bak
This commit is contained in:
1 parent
fcefd258eb
commit
6b6157d786
23 files changed
+1140
-237
No files matched your search
@@ -16,6 +16,8 @@ import {
|
||||
import { useSceneStore } from '@/stores/scene'
|
||||
import { isSvgFile, loadImage, parseSvgDimensions, readAsDataUrl } from '@/lib/fileLoader'
|
||||
import type { Asset } from '@/types/scene'
|
||||
import { confirmDialog, promptDialog } from '@/components/ui'
|
||||
import { t } from '@/i18n'
|
||||
|
||||
const store = useSceneStore()
|
||||
|
||||
@@ -54,10 +56,15 @@ function addSampleCircle() {
|
||||
store.addLayer(asset.id)
|
||||
}
|
||||
|
||||
function addSampleText() {
|
||||
const input = prompt('Text?', 'Hallo')
|
||||
async function addSampleText() {
|
||||
const input = await promptDialog({
|
||||
title: t('sidebar.newTextDialog.title'),
|
||||
message: t('sidebar.newTextDialog.message'),
|
||||
defaultValue: t('sidebar.newTextDialog.default'),
|
||||
confirmLabel: t('sidebar.newTextDialog.confirm'),
|
||||
})
|
||||
if (input === null) return
|
||||
const text = input.trim() || 'Text'
|
||||
const text = input.trim() || t('sidebar.newTextDialog.fallback')
|
||||
const asset = store.addAsset({
|
||||
kind: 'text',
|
||||
name: text.length > 24 ? text.slice(0, 24) + '…' : text,
|
||||
@@ -70,20 +77,35 @@ function addSampleText() {
|
||||
|
||||
const clips = computed(() => store.scene.clips ?? [])
|
||||
|
||||
function addClip() {
|
||||
const input = prompt('Name des Clips?', 'Clip')
|
||||
async function addClip() {
|
||||
const input = await promptDialog({
|
||||
title: t('sidebar.newClipDialog.title'),
|
||||
message: t('sidebar.newClipDialog.message'),
|
||||
defaultValue: t('sidebar.newClipDialog.default'),
|
||||
confirmLabel: t('sidebar.newClipDialog.confirm'),
|
||||
})
|
||||
if (input === null) return
|
||||
store.addClip(input)
|
||||
}
|
||||
|
||||
function renameClipPrompt(clipId: string, current: string) {
|
||||
const input = prompt('Neuer Name?', current)
|
||||
async function renameClipPrompt(clipId: string, current: string) {
|
||||
const input = await promptDialog({
|
||||
title: t('sidebar.renameClipDialog.title'),
|
||||
defaultValue: current,
|
||||
confirmLabel: t('sidebar.renameClipDialog.confirm'),
|
||||
})
|
||||
if (input === null) return
|
||||
store.renameClip(clipId, input)
|
||||
}
|
||||
|
||||
function deleteClip(clipId: string) {
|
||||
if (!confirm('Clip und alle Instanzen wirklich löschen?')) return
|
||||
async function deleteClip(clipId: string) {
|
||||
const ok = await confirmDialog({
|
||||
title: t('sidebar.deleteClipDialog.title'),
|
||||
message: t('sidebar.deleteClipDialog.message'),
|
||||
confirmLabel: t('sidebar.deleteClipDialog.confirm'),
|
||||
variant: 'danger',
|
||||
})
|
||||
if (!ok) return
|
||||
store.removeClip(clipId)
|
||||
}
|
||||
|
||||
@@ -149,34 +171,17 @@ const assetIconMap: Record<Asset['kind'], typeof PhSquare> = {
|
||||
text: PhTextT,
|
||||
}
|
||||
|
||||
const assetsWithUsage = computed(() =>
|
||||
store.scene.assets.map((a) => ({
|
||||
asset: a,
|
||||
inUse: store.scene.layers.some((l) => l.assetId === a.id),
|
||||
})),
|
||||
const imageAssets = computed(() =>
|
||||
store.scene.assets.filter((a): a is Extract<Asset, { kind: 'image' }> => a.kind === 'image'),
|
||||
)
|
||||
|
||||
const THUMB_SIZE = 40
|
||||
|
||||
function rectPreviewStyle(asset: Extract<Asset, { kind: 'rect' }>) {
|
||||
const scale = Math.min(THUMB_SIZE / asset.width, THUMB_SIZE / asset.height) * 0.9
|
||||
return {
|
||||
width: `${Math.max(6, asset.width * scale)}px`,
|
||||
height: `${Math.max(6, asset.height * scale)}px`,
|
||||
background: asset.fill,
|
||||
borderRadius: '2px',
|
||||
} as const
|
||||
function onAssetDragStart(e: DragEvent, assetId: string) {
|
||||
if (!e.dataTransfer) return
|
||||
e.dataTransfer.effectAllowed = 'copy'
|
||||
e.dataTransfer.setData('application/x-animatr-asset', assetId)
|
||||
e.dataTransfer.setData('text/plain', assetId)
|
||||
}
|
||||
|
||||
function circlePreviewStyle(asset: Extract<Asset, { kind: 'circle' }>) {
|
||||
const d = Math.min(THUMB_SIZE, asset.radius * 2) * 0.9
|
||||
return {
|
||||
width: `${d}px`,
|
||||
height: `${d}px`,
|
||||
background: asset.fill,
|
||||
borderRadius: '50%',
|
||||
} as const
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -185,21 +190,21 @@ function circlePreviewStyle(asset: Extract<Asset, { kind: 'circle' }>) {
|
||||
|
||||
<button class="upload-btn" @click="triggerUpload">
|
||||
<PhUploadSimple :size="16" />
|
||||
<span>SVG / Bild hinzufügen</span>
|
||||
<span>{{ t('sidebar.addFile') }}</span>
|
||||
</button>
|
||||
|
||||
<div class="actions">
|
||||
<button class="shape-btn" @click="addSampleRect">
|
||||
<PhSquare :size="14" />
|
||||
Rect
|
||||
{{ t('sidebar.shape.rect') }}
|
||||
</button>
|
||||
<button class="shape-btn" @click="addSampleCircle">
|
||||
<PhCircle :size="14" />
|
||||
Circle
|
||||
{{ t('sidebar.shape.circle') }}
|
||||
</button>
|
||||
<button class="shape-btn" @click="addSampleText">
|
||||
<PhTextT :size="14" />
|
||||
Text
|
||||
{{ t('sidebar.shape.text') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -214,10 +219,10 @@ function circlePreviewStyle(asset: Extract<Asset, { kind: 'circle' }>) {
|
||||
<p v-if="uploadError" class="error">{{ uploadError }}</p>
|
||||
|
||||
<div class="lib-header">
|
||||
<h4>Bibliothek</h4>
|
||||
<h4>{{ t('sidebar.library') }}</h4>
|
||||
<button
|
||||
class="thumb-toggle"
|
||||
:title="showThumbs ? 'Thumbnails ausblenden' : 'Thumbnails anzeigen'"
|
||||
:title="showThumbs ? t('sidebar.thumbnailsHide') : t('sidebar.thumbnailsShow')"
|
||||
@click="showThumbs = !showThumbs"
|
||||
>
|
||||
<PhImageSquare v-if="showThumbs" :size="13" />
|
||||
@@ -225,54 +230,38 @@ function circlePreviewStyle(asset: Extract<Asset, { kind: 'circle' }>) {
|
||||
</button>
|
||||
</div>
|
||||
<ul class="assets" :class="{ compact: !showThumbs }">
|
||||
<li v-for="item in assetsWithUsage" :key="item.asset.id" :class="{ unused: !item.inUse }">
|
||||
<li
|
||||
v-for="asset in imageAssets"
|
||||
:key="asset.id"
|
||||
draggable="true"
|
||||
@dragstart="onAssetDragStart($event, asset.id)"
|
||||
>
|
||||
<template v-if="showThumbs">
|
||||
<div class="thumb">
|
||||
<img
|
||||
v-if="item.asset.kind === 'image'"
|
||||
:src="item.asset.src"
|
||||
:alt="item.asset.name"
|
||||
draggable="false"
|
||||
/>
|
||||
<div
|
||||
v-else-if="item.asset.kind === 'rect'"
|
||||
class="shape-preview"
|
||||
:style="rectPreviewStyle(item.asset)"
|
||||
/>
|
||||
<div
|
||||
v-else-if="item.asset.kind === 'circle'"
|
||||
class="shape-preview"
|
||||
:style="circlePreviewStyle(item.asset)"
|
||||
/>
|
||||
<span
|
||||
v-else-if="item.asset.kind === 'text'"
|
||||
class="text-preview"
|
||||
:style="{ color: item.asset.fill }"
|
||||
>Aa</span>
|
||||
<img :src="asset.src" :alt="asset.name" draggable="false" />
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<component :is="assetIconMap[item.asset.kind]" :size="14" class="asset-icon" />
|
||||
<component :is="assetIconMap[asset.kind]" :size="14" class="asset-icon" />
|
||||
</template>
|
||||
<div class="meta">
|
||||
<span class="name" :title="item.asset.name">{{ item.asset.name }}</span>
|
||||
<span v-if="showThumbs" class="sub">{{ item.asset.kind }}</span>
|
||||
<span class="name" :title="asset.name">{{ asset.name }}</span>
|
||||
<span v-if="showThumbs" class="sub">{{ t('sidebar.imageLabel') }}</span>
|
||||
</div>
|
||||
<button
|
||||
v-if="!item.inUse"
|
||||
class="add-layer"
|
||||
title="Als Layer einfügen"
|
||||
@click="store.addLayer(item.asset.id)"
|
||||
:title="t('sidebar.addLayerTooltip')"
|
||||
@click="store.addLayer(asset.id)"
|
||||
>
|
||||
<PhPlus :size="12" weight="bold" />
|
||||
</button>
|
||||
</li>
|
||||
<li v-if="!store.scene.assets.length" class="empty">keine Assets</li>
|
||||
<li v-if="!imageAssets.length" class="empty">{{ t('sidebar.noImages') }}</li>
|
||||
</ul>
|
||||
|
||||
<div class="lib-header clips-header">
|
||||
<h4>Clips</h4>
|
||||
<button class="thumb-toggle" title="Neuen Clip anlegen" @click="addClip">
|
||||
<h4>{{ t('sidebar.clips') }}</h4>
|
||||
<button class="thumb-toggle" :title="t('sidebar.createClipTooltip')" @click="addClip">
|
||||
<PhPlus :size="12" weight="bold" />
|
||||
</button>
|
||||
</div>
|
||||
@@ -292,23 +281,23 @@ function circlePreviewStyle(asset: Extract<Asset, { kind: 'circle' }>) {
|
||||
<div class="clip-meta">
|
||||
<span class="clip-name" :title="clip.name">{{ clip.name }}</span>
|
||||
<span class="clip-sub">
|
||||
{{ clip.layers.length }} Layer · {{ clip.duration.toFixed(1) }}s
|
||||
{{ t('sidebar.clipLayerCount', { n: clip.layers.length }) }} · {{ clip.duration.toFixed(1) }}s
|
||||
</span>
|
||||
</div>
|
||||
<div class="clip-actions">
|
||||
<button
|
||||
class="clip-btn"
|
||||
title="Umbenennen"
|
||||
:title="t('sidebar.renameTooltip')"
|
||||
@click.stop="renameClipPrompt(clip.id, clip.name)"
|
||||
>
|
||||
<PhPencilSimple :size="11" />
|
||||
</button>
|
||||
<button class="clip-btn danger" title="Löschen" @click.stop="deleteClip(clip.id)">
|
||||
<button class="clip-btn danger" :title="t('sidebar.deleteTooltip')" @click.stop="deleteClip(clip.id)">
|
||||
<PhTrash :size="11" />
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
<li v-if="!clips.length" class="empty">keine Clips</li>
|
||||
<li v-if="!clips.length" class="empty">{{ t('sidebar.noClips') }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
Reference in new issue
Block a user