feat(components): add variant inspector UI
Show variant property dropdowns in the design panel when an instance of a component set is selected. Switching a dropdown value finds the matching variant component and updates the instance's componentId. - VariantSection.vue with property dropdowns using AppSelect - Bridge variant actions to the editor return object - i18n for en, ru, zh-CN Ref #239
This commit is contained in:
parent
fee44ebdb7
commit
c79c9106fb
|
|
@ -28,6 +28,13 @@ export function createComponentBridge(
|
|||
createInstanceFromComponent: components.createInstanceFromComponent,
|
||||
detachInstance: () => components.detachInstance(selection.getSelectedNode()),
|
||||
goToMainComponent: () =>
|
||||
components.goToMainComponent(selection.getSelectedNode(), pages.switchPage)
|
||||
components.goToMainComponent(selection.getSelectedNode(), pages.switchPage),
|
||||
getComponentSetPropertyDefs: components.getComponentSetPropertyDefs,
|
||||
addPropertyDefinition: components.addPropertyDefinition,
|
||||
removePropertyDefinition: components.removePropertyDefinition,
|
||||
renamePropertyDefinition: components.renamePropertyDefinition,
|
||||
collectVariantOptions: components.collectVariantOptions,
|
||||
findVariantByValues: components.findVariantByValues,
|
||||
switchInstanceVariant: components.switchInstanceVariant
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,6 +130,7 @@ export const panelMessages = i18n('panels', {
|
|||
export: 'Export',
|
||||
typography: 'Typography',
|
||||
variables: 'Variables',
|
||||
variants: 'Variants',
|
||||
constraints: 'Constraints',
|
||||
|
||||
addFill: 'Add fill',
|
||||
|
|
|
|||
|
|
@ -175,7 +175,8 @@
|
|||
"create": "Создать",
|
||||
"createNumberVariable": "Создать числовую переменную из {value}",
|
||||
"strokeDash": "Пунктирная обводка",
|
||||
"add": "Добавить"
|
||||
"add": "Добавить",
|
||||
"variants": "Варианты"
|
||||
},
|
||||
"pages": {
|
||||
"newPage": "Новая страница",
|
||||
|
|
|
|||
|
|
@ -175,7 +175,8 @@
|
|||
"create": "创建",
|
||||
"createNumberVariable": "从 {value} 创建数字变量",
|
||||
"strokeDash": "虚线描边",
|
||||
"add": "添加"
|
||||
"add": "添加",
|
||||
"variants": "变体"
|
||||
},
|
||||
"pages": {
|
||||
"newPage": "新建页面",
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import PositionSection from './properties/PositionSection.vue'
|
|||
import StrokeSection from './properties/StrokeSection.vue'
|
||||
import TypographySection from './properties/TypographySection.vue'
|
||||
import VariablesSection from './properties/VariablesSection.vue'
|
||||
import VariantSection from './properties/VariantSection.vue'
|
||||
|
||||
const variablesOpen = ref(false)
|
||||
const { selectedNode: node, selectedCount: multiCount } = useSelectionState()
|
||||
|
|
@ -87,6 +88,8 @@ const { panels } = useI18n()
|
|||
</button>
|
||||
</div>
|
||||
|
||||
<VariantSection v-if="node.type === 'INSTANCE'" />
|
||||
|
||||
<PositionSection />
|
||||
<LayoutSection />
|
||||
<AppearanceSection />
|
||||
|
|
|
|||
63
src/components/properties/VariantSection.vue
Normal file
63
src/components/properties/VariantSection.vue
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
|
||||
import { useI18n, useSelectionState } from '@open-pencil/vue'
|
||||
|
||||
import AppSelect from '@/components/ui/AppSelect.vue'
|
||||
import { useSectionUI } from '@/components/ui/section'
|
||||
import { useEditorStore } from '@/app/editor/active-store'
|
||||
|
||||
const editor = useEditorStore()
|
||||
const { selectedNode: node } = useSelectionState()
|
||||
const sectionCls = useSectionUI()
|
||||
const { panels } = useI18n()
|
||||
|
||||
const instanceComponent = computed(() => {
|
||||
if (!node.value || node.value.type !== 'INSTANCE' || !node.value.componentId) return null
|
||||
return editor.graph.getNode(node.value.componentId) ?? null
|
||||
})
|
||||
|
||||
const componentSetId = computed(() => {
|
||||
const comp = instanceComponent.value
|
||||
if (!comp) return null
|
||||
const parent = comp.parentId ? editor.graph.getNode(comp.parentId) : null
|
||||
return parent?.type === 'COMPONENT_SET' ? parent.id : null
|
||||
})
|
||||
|
||||
const variantOptions = computed(() => {
|
||||
const csId = componentSetId.value
|
||||
if (!csId) return new Map<string, Set<string>>()
|
||||
return editor.collectVariantOptions(csId)
|
||||
})
|
||||
|
||||
const currentValues = computed(() => {
|
||||
return instanceComponent.value?.componentPropertyValues ?? {}
|
||||
})
|
||||
|
||||
const hasVariants = computed(() => variantOptions.value.size > 0)
|
||||
|
||||
function switchVariant(propertyName: string, newValue: string) {
|
||||
if (!node.value) return
|
||||
editor.switchInstanceVariant(node.value.id, propertyName, newValue)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="hasVariants" data-test-id="variant-section" :class="sectionCls.wrapper">
|
||||
<label class="mb-1.5 block text-[11px] font-medium text-component">{{ panels.variants }}</label>
|
||||
<div class="flex flex-col gap-1.5">
|
||||
<div
|
||||
v-for="[propName, options] in variantOptions"
|
||||
:key="propName"
|
||||
class="flex flex-col gap-0.5"
|
||||
>
|
||||
<label class="text-[10px] text-muted">{{ propName }}</label>
|
||||
<AppSelect
|
||||
:model-value="currentValues[propName] ?? ''"
|
||||
:options="[...options].map((v) => ({ value: v, label: v }))"
|
||||
@update:model-value="switchVariant(propName, $event)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
Loading…
Reference in a new issue