From 43f33acd10123c2dbc96abb834022aed9b69ff0c Mon Sep 17 00:00:00 2001 From: pisandelli <1986634+pisandelli@users.noreply.github.com> Date: Fri, 3 Jul 2026 19:36:44 +0000 Subject: [PATCH] Extract studio components Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- runtime/components/studio/StudioField.vue | 441 ++++++++++++++ runtime/components/studio/StudioHeader.vue | 42 ++ runtime/components/studio/StudioSidebar.vue | 189 ++++++ runtime/pages/studio.vue | 605 ++------------------ 4 files changed, 706 insertions(+), 571 deletions(-) create mode 100644 runtime/components/studio/StudioField.vue create mode 100644 runtime/components/studio/StudioHeader.vue create mode 100644 runtime/components/studio/StudioSidebar.vue diff --git a/runtime/components/studio/StudioField.vue b/runtime/components/studio/StudioField.vue new file mode 100644 index 0000000..3ec44f9 --- /dev/null +++ b/runtime/components/studio/StudioField.vue @@ -0,0 +1,441 @@ + + + diff --git a/runtime/components/studio/StudioHeader.vue b/runtime/components/studio/StudioHeader.vue new file mode 100644 index 0000000..fd2ddab --- /dev/null +++ b/runtime/components/studio/StudioHeader.vue @@ -0,0 +1,42 @@ + + + diff --git a/runtime/components/studio/StudioSidebar.vue b/runtime/components/studio/StudioSidebar.vue new file mode 100644 index 0000000..6511cce --- /dev/null +++ b/runtime/components/studio/StudioSidebar.vue @@ -0,0 +1,189 @@ + + + diff --git a/runtime/pages/studio.vue b/runtime/pages/studio.vue index 661928a..69bc2df 100644 --- a/runtime/pages/studio.vue +++ b/runtime/pages/studio.vue @@ -9,13 +9,14 @@ import type { StudioTabDefinition } from '../studio/types' +import StudioHeader from '../components/studio/StudioHeader.vue' +import StudioSidebar from '../components/studio/StudioSidebar.vue' + definePageMeta({ layout: false }) const tabs = STUDIO_TABS const activeTabId = ref(tabs[0]?.id ?? 'base') const componentSearch = ref('') -const isComponentPickerOpen = ref(false) -const componentSearchInput = ref(null) const activeTab = computed(() => { return tabs.find((tab) => tab.id === activeTabId.value) ?? tabs[0]! @@ -31,11 +32,6 @@ const componentTabs = computed(() => .sort((a, b) => a.label.localeCompare(b.label)) ) -const activeComponentTab = computed(() => { - if (activeTab.value.navigationKind === 'component') return activeTab.value - return componentTabs.value[0] ?? null -}) - const filteredComponentTabs = computed(() => { const query = componentSearch.value.trim().toLowerCase() if (!query) return componentTabs.value @@ -117,177 +113,8 @@ const groupedFields = computed(() => { })) }) -function eventValue(event: Event): string { - return (event.target as HTMLInputElement | HTMLSelectElement | null)?.value ?? '' -} - -function displayFieldLabel(field: StudioFieldDefinition): string { - return field.label.replace(/\s*\/ Expression$/, '') -} - -function fieldAcceptedInputs(field: StudioFieldDefinition): string[] { - if (field.type === 'select') return ['preset option'] - if (field.type === 'color') return ['color literal', 'reference'] - return ['string', 'reference', 'CSS expression'] -} - -function fieldExampleValues(field: StudioFieldDefinition): string[] { - const examples = new Set() - - if (field.type === 'select') { - for (const option of field.options?.slice(0, 3) ?? []) { - examples.add(option) - } - return [...examples] - } - - if (field.type === 'color') { - examples.add('#ffffff') - examples.add('{color.primary.600}') - } else { - examples.add('1rem') - examples.add('{space.md}') - - if (field.rawDefaultValue?.includes('(')) { - examples.add(field.rawDefaultValue) - } else { - examples.add('contrast-color({button.base-color})') - examples.add('color-mix(in srgb, {color.primary.600} 80%, black)') - } - } - - if (field.rawDefaultValue) examples.add(field.rawDefaultValue) - - return [...examples].slice(0, 4) -} - -function fieldInfoAriaLabel(field: StudioFieldDefinition): string { - return `Show help for ${displayFieldLabel(field)}` -} - -function pathHasAny(path: string, fragments: string[]): boolean { - return fragments.some((fragment) => path.includes(fragment)) -} - -function referencePlaceholder(field: StudioFieldDefinition): string { - if (field.referencePath) return field.referencePath - - const pathParts = field.path.split('.') - const root = pathParts[0] ?? '' - const semanticColor = pathParts.find((part) => - ['primary', 'secondary', 'accent', 'success', 'danger', 'error', 'warning', 'info'].includes(part) - ) - - if (field.type === 'color' || field.path.includes('color')) { - return semanticColor ? `color.${semanticColor}.500` : 'color.primary.500' - } - - switch (root) { - case 'font': - return 'font.base' - case 'font-size': - return 'font-size.md' - case 'font-weight': - return 'font-weight.bold' - case 'line-height': - return 'line-height.normal' - case 'letter-spacing': - return 'letter-spacing.wide' - case 'border-radius': - return 'border-radius.md' - case 'border-width': - return 'border-width.sm' - case 'transition': - return 'transition.base' - case 'shadow': - return 'shadow.md' - case 'space': - return 'space.md' - default: - break - } - - if (pathHasAny(field.path, ['font-size', 'icon-size'])) return 'font-size.md' - if (pathHasAny(field.path, ['font-weight'])) return 'font-weight.bold' - if (pathHasAny(field.path, ['line-height'])) return 'line-height.normal' - if (pathHasAny(field.path, ['letter-spacing'])) return 'letter-spacing.wide' - if (pathHasAny(field.path, ['border-radius'])) return 'border-radius.md' - if (pathHasAny(field.path, ['border-width'])) return 'border-width.sm' - if (pathHasAny(field.path, ['transition'])) return 'transition.base' - if (pathHasAny(field.path, ['shadow'])) return 'shadow.md' - if (pathHasAny(field.path, ['padding', 'gap', 'height', 'size'])) return 'space.md' - - return 'token.path' -} - -function currentReferencePath(field: StudioFieldDefinition): string { - return references.value[field.path] || field.referencePath || '' -} - -function currentReferenceRelation(field: StudioFieldDefinition): string { - const referencePath = currentReferencePath(field) - if (!referencePath) return field.path - return `${field.path} -> ${referencePath}` -} - -function isDetachedFromDefaultReference(field: StudioFieldDefinition): boolean { - return Boolean(field.referencePath) && modes.value[field.path] !== 'reference' -} - -function referenceSummaryLabel(field: StudioFieldDefinition): string { - if (isDetachedFromDefaultReference(field)) return 'Detached from default' - if (modes.value[field.path] === 'reference') return 'Inherits from' - if (field.referencePath) return 'Default reference' - return 'Reference' -} - -function resolvedValueLabel(field: StudioFieldDefinition): string { - if (field.type === 'color') return 'Resolved color' - return 'Resolved value' -} - -function referenceSuggestions(field: StudioFieldDefinition): string[] { - return [...new Set( - [field.referencePath, referencePlaceholder(field)] - .map((value) => value?.trim() ?? '') - .filter(Boolean) - )] -} - -function applyReference(field: StudioFieldDefinition, referencePath: string): void { - setReferencePath(field.path, referencePath) - setMode(field.path, 'reference') -} - -function referenceSuggestionLabel(field: StudioFieldDefinition, suggestion: string): string { - if (field.referencePath === suggestion) return `Use default: ${suggestion}` - return `Use ${suggestion}` -} - -function usesSingleExpressionReferenceInput(field: StudioFieldDefinition): boolean { - return modes.value[field.path] === 'reference' && hasReferenceTemplate(field.path) -} - function selectTab(tab: StudioTabDefinition): void { activeTabId.value = tab.id - isComponentPickerOpen.value = false -} - -function toggleComponentPicker(): void { - isComponentPickerOpen.value = !isComponentPickerOpen.value - if (isComponentPickerOpen.value) { - componentSearch.value = '' - - nextTick(() => { - componentSearchInput.value?.focus() - - const activeOption = document.querySelector('.dde-component-option-active') as HTMLElement | null - activeOption?.scrollIntoView({ - block: 'start', - inline: 'nearest' - }) - }) - } } async function focusField(path: string) { @@ -334,403 +161,39 @@ provide(STUDIO_PREVIEW_CONTEXT_KEY, { v-text="previewCss" /> -
-
- -
- DareDash Studio - Theme Editor -
-
-
- - -
-
+
- +