2026-03-01 07:55:52 +00:00
|
|
|
<script setup lang="ts">
|
|
|
|
|
import { computed, ref } from 'vue'
|
|
|
|
|
|
Phase 7: Migrate 10 components from useEditorStore to useEditor
Components now using useEditor() from SDK instead of app's useEditorStore():
- CodePanel, DesignPanel, ImageFillPicker, LayerTree, PagesPanel,
PropertiesPanel, VariablesDialog, FillSection, PageSection,
VariablesSection
7 components remain on useEditorStore for app-specific methods:
- AppMenu, Toolbar, MobileHud (saveFigFile, exportSelection, mobile*)
- CanvasMenu (renderExportImage)
- EditorCanvas (cursorCanvasX/Y app state)
- MobileDrawer (mobileDrawerSnap app state)
- ExportSection (exportSelection, renderExportImage)
2026-03-17 07:38:40 +00:00
|
|
|
import { useEditor } from '@open-pencil/vue'
|
2026-03-01 07:55:52 +00:00
|
|
|
|
|
|
|
|
import VariablesDialog from './VariablesDialog.vue'
|
|
|
|
|
import AppearanceSection from './properties/AppearanceSection.vue'
|
|
|
|
|
import EffectsSection from './properties/EffectsSection.vue'
|
|
|
|
|
import ExportSection from './properties/ExportSection.vue'
|
|
|
|
|
import FillSection from './properties/FillSection.vue'
|
|
|
|
|
import LayoutSection from './properties/LayoutSection.vue'
|
|
|
|
|
import PageSection from './properties/PageSection.vue'
|
|
|
|
|
import PositionSection from './properties/PositionSection.vue'
|
|
|
|
|
import StrokeSection from './properties/StrokeSection.vue'
|
|
|
|
|
import TypographySection from './properties/TypographySection.vue'
|
|
|
|
|
import VariablesSection from './properties/VariablesSection.vue'
|
|
|
|
|
|
Phase 7: Migrate 10 components from useEditorStore to useEditor
Components now using useEditor() from SDK instead of app's useEditorStore():
- CodePanel, DesignPanel, ImageFillPicker, LayerTree, PagesPanel,
PropertiesPanel, VariablesDialog, FillSection, PageSection,
VariablesSection
7 components remain on useEditorStore for app-specific methods:
- AppMenu, Toolbar, MobileHud (saveFigFile, exportSelection, mobile*)
- CanvasMenu (renderExportImage)
- EditorCanvas (cursorCanvasX/Y app state)
- MobileDrawer (mobileDrawerSnap app state)
- ExportSection (exportSelection, renderExportImage)
2026-03-17 07:38:40 +00:00
|
|
|
const store = useEditor()
|
2026-03-01 07:55:52 +00:00
|
|
|
const variablesOpen = ref(false)
|
|
|
|
|
|
Phase 7: Migrate 10 components from useEditorStore to useEditor
Components now using useEditor() from SDK instead of app's useEditorStore():
- CodePanel, DesignPanel, ImageFillPicker, LayerTree, PagesPanel,
PropertiesPanel, VariablesDialog, FillSection, PageSection,
VariablesSection
7 components remain on useEditorStore for app-specific methods:
- AppMenu, Toolbar, MobileHud (saveFigFile, exportSelection, mobile*)
- CanvasMenu (renderExportImage)
- EditorCanvas (cursorCanvasX/Y app state)
- MobileDrawer (mobileDrawerSnap app state)
- ExportSection (exportSelection, renderExportImage)
2026-03-17 07:38:40 +00:00
|
|
|
const node = computed(() => store.getSelectedNode())
|
|
|
|
|
const multiCount = computed(() => store.getSelectedNodes().length)
|
2026-03-01 07:55:52 +00:00
|
|
|
const isComponentType = computed(() => {
|
|
|
|
|
const t = node.value?.type
|
|
|
|
|
return t === 'COMPONENT' || t === 'COMPONENT_SET' || t === 'INSTANCE'
|
|
|
|
|
})
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
<!-- Multi-select summary -->
|
2026-03-04 14:18:26 +00:00
|
|
|
<div
|
|
|
|
|
v-if="multiCount > 1"
|
|
|
|
|
data-test-id="design-panel-multi"
|
2026-03-09 12:46:20 +00:00
|
|
|
class="scrollbar-thin flex-1 overflow-x-hidden overflow-y-auto pb-4"
|
2026-03-04 14:18:26 +00:00
|
|
|
>
|
|
|
|
|
<div
|
|
|
|
|
data-test-id="design-multi-header"
|
|
|
|
|
class="flex items-center gap-1.5 border-b border-border px-3 py-2"
|
|
|
|
|
>
|
2026-03-01 07:55:52 +00:00
|
|
|
<span class="text-[11px] text-muted">Mixed</span>
|
|
|
|
|
<span class="text-xs font-semibold">{{ multiCount }} layers</span>
|
|
|
|
|
</div>
|
Multi-selection properties panel with tests
Show position, appearance, fill, stroke, and effects sections
when multiple nodes are selected. Shared values display normally,
differing values show 'Mixed'. Editing applies to all selected.
- Shared composable (use-multi-props) for isMulti, active, activeNode
- ScrubInput supports MIXED symbol as model-value
- Per-node previous values for correct undo on multi-edit
- Fix flip transform encoding (scale first column only)
- Fix npm publish auth for MCP package
- 22 new tests: flip, alignment, multi-node merging, kiwi roundtrip
2026-03-03 17:35:51 +00:00
|
|
|
<PositionSection />
|
2026-03-01 07:55:52 +00:00
|
|
|
<AppearanceSection />
|
Multi-selection properties panel with tests
Show position, appearance, fill, stroke, and effects sections
when multiple nodes are selected. Shared values display normally,
differing values show 'Mixed'. Editing applies to all selected.
- Shared composable (use-multi-props) for isMulti, active, activeNode
- ScrubInput supports MIXED symbol as model-value
- Per-node previous values for correct undo on multi-edit
- Fix flip transform encoding (scale first column only)
- Fix npm publish auth for MCP package
- 22 new tests: flip, alignment, multi-node merging, kiwi roundtrip
2026-03-03 17:35:51 +00:00
|
|
|
<FillSection />
|
|
|
|
|
<StrokeSection />
|
|
|
|
|
<EffectsSection />
|
2026-03-01 07:55:52 +00:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Single selection -->
|
2026-03-04 14:18:26 +00:00
|
|
|
<div
|
|
|
|
|
v-else-if="node"
|
|
|
|
|
data-test-id="design-panel-single"
|
2026-03-09 12:46:20 +00:00
|
|
|
class="scrollbar-thin flex-1 overflow-x-hidden overflow-y-auto pb-4"
|
2026-03-04 14:18:26 +00:00
|
|
|
>
|
|
|
|
|
<div
|
|
|
|
|
data-test-id="design-node-header"
|
|
|
|
|
class="flex items-center gap-1.5 border-b border-border px-3 py-2"
|
|
|
|
|
>
|
2026-03-16 11:13:36 +00:00
|
|
|
<span class="text-[11px]" :class="isComponentType ? 'text-component' : 'text-muted'">{{
|
2026-03-01 07:55:52 +00:00
|
|
|
node.type
|
|
|
|
|
}}</span>
|
|
|
|
|
<span class="text-xs font-semibold">{{ node.name }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Component actions -->
|
|
|
|
|
<div
|
|
|
|
|
v-if="node.type === 'INSTANCE'"
|
|
|
|
|
class="flex flex-col gap-1 border-b border-border px-3 py-2"
|
|
|
|
|
>
|
|
|
|
|
<button
|
2026-03-04 14:18:26 +00:00
|
|
|
data-test-id="design-go-to-component"
|
2026-03-16 11:13:36 +00:00
|
|
|
class="rounded bg-component/10 px-2 py-1 text-left text-[11px] text-component hover:bg-component/20"
|
2026-03-01 07:55:52 +00:00
|
|
|
@click="store.goToMainComponent()"
|
|
|
|
|
>
|
|
|
|
|
Go to Main Component
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
2026-03-04 14:18:26 +00:00
|
|
|
data-test-id="design-detach-instance"
|
2026-03-01 07:55:52 +00:00
|
|
|
class="rounded px-2 py-1 text-left text-[11px] text-muted hover:bg-hover"
|
|
|
|
|
@click="store.detachInstance()"
|
|
|
|
|
>
|
|
|
|
|
Detach Instance
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<PositionSection />
|
|
|
|
|
<LayoutSection />
|
|
|
|
|
<AppearanceSection />
|
|
|
|
|
<TypographySection v-if="node.type === 'TEXT'" />
|
|
|
|
|
<FillSection />
|
|
|
|
|
<StrokeSection />
|
|
|
|
|
<EffectsSection />
|
|
|
|
|
|
|
|
|
|
<ExportSection />
|
|
|
|
|
</div>
|
|
|
|
|
|
2026-03-04 14:18:26 +00:00
|
|
|
<div
|
|
|
|
|
v-else
|
|
|
|
|
data-test-id="design-panel-empty"
|
2026-03-09 12:46:20 +00:00
|
|
|
class="scrollbar-thin flex-1 overflow-x-hidden overflow-y-auto pb-4"
|
2026-03-04 14:18:26 +00:00
|
|
|
>
|
2026-03-01 07:55:52 +00:00
|
|
|
<PageSection />
|
|
|
|
|
<VariablesSection @open-dialog="variablesOpen = true" />
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<VariablesDialog v-model:open="variablesOpen" />
|
|
|
|
|
</template>
|