Appearance section: opacity + corner radius + visibility toggle
- Opacity with blend icon, corner radius with radius icon - Eye/eye-off toggle in section header - Layers panel: hidden layers dimmed (opacity-50) with permanent eye-off indicator - Removed duplicate corner radius from LayoutSection
This commit is contained in:
parent
bfe9726035
commit
2af2a280c6
2
components.d.ts
vendored
2
components.d.ts
vendored
|
|
@ -20,11 +20,13 @@ declare module 'vue' {
|
|||
IconLucideAlignLeft: typeof import('~icons/lucide/align-left')['default']
|
||||
IconLucideAlignRight: typeof import('~icons/lucide/align-right')['default']
|
||||
IconLucideBaseline: typeof import('~icons/lucide/baseline')['default']
|
||||
IconLucideBlend: typeof import('~icons/lucide/blend')['default']
|
||||
IconLucideCheck: typeof import('~icons/lucide/check')['default']
|
||||
IconLucideChevronDown: typeof import('~icons/lucide/chevron-down')['default']
|
||||
IconLucideChevronRight: typeof import('~icons/lucide/chevron-right')['default']
|
||||
IconLucideEye: typeof import('~icons/lucide/eye')['default']
|
||||
IconLucideEyeOff: typeof import('~icons/lucide/eye-off')['default']
|
||||
IconLucideRadius: typeof import('~icons/lucide/radius')['default']
|
||||
IconLucideSearch: typeof import('~icons/lucide/search')['default']
|
||||
LayersPanel: typeof import('./src/components/LayersPanel.vue')['default']
|
||||
LayoutSection: typeof import('./src/components/properties/LayoutSection.vue')['default']
|
||||
|
|
|
|||
|
|
@ -218,13 +218,14 @@ function updateDropTarget(ev: PointerEvent) {
|
|||
@select="onSelect"
|
||||
>
|
||||
<button
|
||||
class="flex w-full cursor-pointer items-center gap-1 rounded border-none py-1 text-left text-xs"
|
||||
class="group/row flex w-full cursor-pointer items-center gap-1 rounded border-none py-1 text-left text-xs"
|
||||
:class="[
|
||||
store.state.selectedIds.has(item.value.id)
|
||||
? 'bg-accent text-white'
|
||||
: 'bg-transparent text-surface hover:bg-hover',
|
||||
dragging && dragNodeId === item.value.id ? 'opacity-30' : '',
|
||||
dropIntoId === item.value.id ? 'ring-2 ring-accent ring-inset' : ''
|
||||
dropIntoId === item.value.id ? 'ring-2 ring-accent ring-inset' : '',
|
||||
!item.value.visible ? 'opacity-50' : ''
|
||||
]"
|
||||
:style="{ paddingLeft: `${8 + (item.level - 1) * 16}px` }"
|
||||
@pointerdown.prevent="onPointerDown($event, item.value.id)"
|
||||
|
|
@ -239,7 +240,11 @@ function updateDropTarget(ev: PointerEvent) {
|
|||
</span>
|
||||
<span v-else class="w-4 shrink-0" />
|
||||
<component :is="nodeIcons[item.value.type] ?? IconSquare" class="size-3 shrink-0 opacity-70" />
|
||||
<span class="truncate">{{ item.value.name }}</span>
|
||||
<span class="min-w-0 flex-1 truncate">{{ item.value.name }}</span>
|
||||
<icon-lucide-eye-off
|
||||
v-if="!item.value.visible"
|
||||
class="mr-1 size-3 shrink-0 text-muted"
|
||||
/>
|
||||
</button>
|
||||
</TreeItem>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ const multiCount = computed(() => store.selectedNodes.value.length)
|
|||
<span class="text-[11px] text-muted">Mixed</span>
|
||||
<span class="text-xs font-semibold">{{ multiCount }} layers</span>
|
||||
</div>
|
||||
<AppearanceSection :opacity="store.selectedNodes.value[0]?.opacity ?? 1" />
|
||||
<AppearanceSection v-if="store.selectedNodes.value[0]" :node="store.selectedNodes.value[0]" />
|
||||
</div>
|
||||
|
||||
<!-- Single selection -->
|
||||
|
|
@ -46,7 +46,7 @@ const multiCount = computed(() => store.selectedNodes.value.length)
|
|||
|
||||
<PositionSection :node="node" />
|
||||
<LayoutSection :node="node" />
|
||||
<AppearanceSection :opacity="node.opacity" />
|
||||
<AppearanceSection :node="node" />
|
||||
<TypographySection v-if="node.type === 'TEXT'" :node="node" />
|
||||
<FillSection :node-id="node.id" :fills="node.fills" />
|
||||
<StrokeSection :node-id="node.id" :strokes="node.strokes" />
|
||||
|
|
|
|||
|
|
@ -2,24 +2,54 @@
|
|||
import ScrubInput from '../ScrubInput.vue'
|
||||
import { useNodeProps } from '../../composables/use-node-props'
|
||||
|
||||
const { updateProp, commitProp } = useNodeProps()
|
||||
import type { SceneNode } from '../../engine/scene-graph'
|
||||
|
||||
defineProps<{ opacity: number }>()
|
||||
const { node } = defineProps<{ node: SceneNode }>()
|
||||
const { store, updateProp, commitProp } = useNodeProps()
|
||||
|
||||
function toggleVisibility() {
|
||||
store.updateNodeWithUndo(node.id, { visible: !node.visible }, 'Toggle visibility')
|
||||
store.requestRender()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="border-b border-border px-3 py-2">
|
||||
<label class="mb-1.5 block text-[11px] text-muted">Appearance</label>
|
||||
<div class="mb-1.5 flex items-center justify-between">
|
||||
<label class="text-[11px] text-muted">Appearance</label>
|
||||
<button
|
||||
class="flex cursor-pointer items-center justify-center rounded border-none bg-transparent p-0.5 text-muted hover:bg-hover hover:text-surface"
|
||||
:class="{ 'text-accent': !node.visible }"
|
||||
title="Toggle visibility"
|
||||
@click="toggleVisibility"
|
||||
>
|
||||
<icon-lucide-eye v-if="node.visible" class="size-3.5" />
|
||||
<icon-lucide-eye-off v-else class="size-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex gap-1.5">
|
||||
<ScrubInput
|
||||
icon="⊘"
|
||||
suffix="%"
|
||||
:model-value="Math.round(opacity * 100)"
|
||||
:model-value="Math.round(node.opacity * 100)"
|
||||
:min="0"
|
||||
:max="100"
|
||||
@update:model-value="updateProp('opacity', $event / 100)"
|
||||
@commit="(v: number, p: number) => commitProp('opacity', v / 100, p / 100)"
|
||||
/>
|
||||
>
|
||||
<template #icon>
|
||||
<icon-lucide-blend class="size-3" />
|
||||
</template>
|
||||
</ScrubInput>
|
||||
<ScrubInput
|
||||
:model-value="node.cornerRadius"
|
||||
:min="0"
|
||||
@update:model-value="updateProp('cornerRadius', $event)"
|
||||
@commit="(v: number, p: number) => commitProp('cornerRadius', v, p)"
|
||||
>
|
||||
<template #icon>
|
||||
<icon-lucide-radius class="size-3" />
|
||||
</template>
|
||||
</ScrubInput>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -145,10 +145,6 @@ function setAlignment(primary: LayoutAlign, counter: LayoutCounterAlign) {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Corner radius -->
|
||||
<div class="mt-1.5 flex gap-1.5">
|
||||
<ScrubInput icon="↻" :model-value="node.cornerRadius" :min="0" @update:model-value="updateProp('cornerRadius', $event)" @commit="(v: number, p: number) => commitProp('cornerRadius', v, p)" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Auto Layout -->
|
||||
|
|
|
|||
Loading…
Reference in a new issue