diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ee04d9f0..0c969b584 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ - Drag with the Text tool to create a fixed-size text box, or click to create auto-width text. - Target a specific open document and page from live CLI and MCP automation, including sessions with multiple documents. - Test OpenAI-compatible provider connections from AI settings with clearer setup errors. -- Build custom property panels with new Vue SDK number fields, bindable values, property sections, segmented controls, property lists, color models, fill controls, and gradient primitives. +- Build custom property panels with new Vue SDK number fields, bindable values, property sections, responsive property grids, segmented controls, property lists, color models, fill controls, and gradient primitives. - Connect local MCP clients through automatically discovered private Unix sockets on macOS and Linux, with localhost TCP fallback. (#338) - Create centered frames from current Figma-style device and asset presets, or resize selected frames from the Design panel while preserving their names. diff --git a/packages/docs/programmable/sdk/api/components/index.md b/packages/docs/programmable/sdk/api/components/index.md index a642c2e36..c2d97d90f 100644 --- a/packages/docs/programmable/sdk/api/components/index.md +++ b/packages/docs/programmable/sdk/api/components/index.md @@ -23,6 +23,7 @@ description: Component reference for headless Vue primitives in @open-pencil/vue + diff --git a/packages/docs/programmable/sdk/api/components/property-grid.data.ts b/packages/docs/programmable/sdk/api/components/property-grid.data.ts new file mode 100644 index 000000000..d242e2890 --- /dev/null +++ b/packages/docs/programmable/sdk/api/components/property-grid.data.ts @@ -0,0 +1,5 @@ +import { defineComponentMetaLoader } from '#docs/sdk/component-meta' + +const sources = ['packages/vue/src/primitives/PropertyGrid/PropertyGridRoot.vue'] + +export default defineComponentMetaLoader(sources) diff --git a/packages/docs/programmable/sdk/api/components/property-grid.md b/packages/docs/programmable/sdk/api/components/property-grid.md new file mode 100644 index 000000000..14374349f --- /dev/null +++ b/packages/docs/programmable/sdk/api/components/property-grid.md @@ -0,0 +1,40 @@ +--- +title: PropertyGrid +description: Headless field-grid and action-rail anatomy for property panels. +--- + + + +# PropertyGrid + +`PropertyGridRoot` separates responsive property fields from optional intrinsic-width actions. It exposes structural data attributes without imposing column widths, gaps, or presentation. + +```vue twoslash + + + +``` + +Themes can target `data-slot="fields"`, `data-slot="actions"`, `data-columns`, and `data-distribution`. The `wide-first` distribution is semantic; consumers choose its exact ratio. + +## Generated API reference + + diff --git a/packages/vue/src/index.ts b/packages/vue/src/index.ts index 2c9187e35..c82e631ec 100644 --- a/packages/vue/src/index.ts +++ b/packages/vue/src/index.ts @@ -246,6 +246,14 @@ export type { PropertyListRootSlotProps, PropertyListRootSlots } from '#vue/primitives/PropertyList' +export { PropertyGridRoot } from '#vue/primitives/PropertyGrid' +export type { + PropertyGridColumns, + PropertyGridDistribution, + PropertyGridRootProps, + PropertyGridRootSlots +} from '#vue/primitives/PropertyGrid' + export { PropertySectionRoot, PropertySectionHeader, diff --git a/packages/vue/src/primitives/PropertyGrid/PropertyGridRoot.vue b/packages/vue/src/primitives/PropertyGrid/PropertyGridRoot.vue new file mode 100644 index 000000000..78454f7eb --- /dev/null +++ b/packages/vue/src/primitives/PropertyGrid/PropertyGridRoot.vue @@ -0,0 +1,27 @@ + + + diff --git a/packages/vue/src/primitives/PropertyGrid/index.ts b/packages/vue/src/primitives/PropertyGrid/index.ts new file mode 100644 index 000000000..cda77b067 --- /dev/null +++ b/packages/vue/src/primitives/PropertyGrid/index.ts @@ -0,0 +1,7 @@ +export { default as PropertyGridRoot } from '#vue/primitives/PropertyGrid/PropertyGridRoot.vue' +export type { + PropertyGridColumns, + PropertyGridDistribution, + PropertyGridRootProps, + PropertyGridRootSlots +} from '#vue/primitives/PropertyGrid/types' diff --git a/packages/vue/src/primitives/PropertyGrid/types.ts b/packages/vue/src/primitives/PropertyGrid/types.ts new file mode 100644 index 000000000..ace20d98c --- /dev/null +++ b/packages/vue/src/primitives/PropertyGrid/types.ts @@ -0,0 +1,18 @@ +import type { VNode } from 'vue' + +export type PropertyGridColumns = 1 | 2 | 3 +export type PropertyGridDistribution = 'equal' | 'wide-first' + +export interface PropertyGridRootProps { + /** Number of field columns. @default 1 */ + columns?: PropertyGridColumns + /** Relative distribution of field columns. @default 'equal' */ + distribution?: PropertyGridDistribution +} + +export interface PropertyGridRootSlots { + /** Property fields arranged by the consumer's visual theme. */ + default(): VNode[] + /** Optional intrinsic-width controls kept separate from the field grid. */ + actions?(): VNode[] +} diff --git a/src/components/color-picker-panel/OkhclChannelSlider.vue b/src/components/color-picker-panel/OkhclChannelSlider.vue index f1d608206..07f0b36cd 100644 --- a/src/components/color-picker-panel/OkhclChannelSlider.vue +++ b/src/components/color-picker-panel/OkhclChannelSlider.vue @@ -77,7 +77,7 @@ const styles = useColorSliderUI( :max="displayMax" :step="displayStep" :suffix="suffix" - :ui="{ leading: 'hidden' }" + :ui="{ leading: 'hidden', field: 'pl-1.5', display: 'pl-1.5' }" @update:model-value="emit('updateDisplay', $event)" /> diff --git a/src/components/color-picker-panel/StandardColorSlider.vue b/src/components/color-picker-panel/StandardColorSlider.vue index 7080df110..590ebb80e 100644 --- a/src/components/color-picker-panel/StandardColorSlider.vue +++ b/src/components/color-picker-panel/StandardColorSlider.vue @@ -72,7 +72,7 @@ const styles = useColorSliderUI( :max="numberMax" :step="numberStep" :suffix="suffix" - :ui="{ leading: 'hidden' }" + :ui="{ leading: 'hidden', field: 'pl-1.5', display: 'pl-1.5' }" @update:model-value="emit('updateNumber', $event)" /> diff --git a/src/components/fill-picker/FillPicker.vue b/src/components/fill-picker/FillPicker.vue index 28c5b42d2..ecc3b78c9 100644 --- a/src/components/fill-picker/FillPicker.vue +++ b/src/components/fill-picker/FillPicker.vue @@ -52,7 +52,7 @@ function cancelFromEscape(event: KeyboardEvent) { type="button" :aria-label="panels.fill" data-test-id="fill-picker-swatch" - class="size-5 shrink-0 cursor-pointer rounded border-0 bg-transparent p-0" + class="size-4 shrink-0 cursor-pointer rounded-sm border-0 bg-transparent p-0" > - + - + - +
- +
- - - - - - + - + -
- + - - + - + - - - + + + + +
diff --git a/src/components/properties/LayoutSection/LayoutGridSection.vue b/src/components/properties/LayoutSection/LayoutGridSection.vue index b7fa841ee..0a56c2ca3 100644 --- a/src/components/properties/LayoutSection/LayoutGridSection.vue +++ b/src/components/properties/LayoutSection/LayoutGridSection.vue @@ -100,7 +100,7 @@ function isGrid(grid: LayoutGrid): boolean { - + diff --git a/src/components/properties/PositionSection.vue b/src/components/properties/PositionSection.vue index 4b6e66c5c..44de639f5 100644 --- a/src/components/properties/PositionSection.vue +++ b/src/components/properties/PositionSection.vue @@ -79,7 +79,7 @@ function handleAlign( - + - + -
+ - - - - - - - - - -
+
+ + + + + + + + + +
+
diff --git a/src/components/properties/StrokeSection.vue b/src/components/properties/StrokeSection.vue index 607e5b9fe..f9eaedc2f 100644 --- a/src/components/properties/StrokeSection.vue +++ b/src/components/properties/StrokeSection.vue @@ -154,7 +154,7 @@ function onToggleSides(activeNode: SceneNode | null) {