@motion-proto/live-tokens 0.1.1 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +168 -21
- package/dist-plugin/index.cjs +823 -336
- package/dist-plugin/index.d.cts +9 -7
- package/dist-plugin/index.d.ts +9 -7
- package/dist-plugin/index.js +822 -335
- package/package.json +46 -20
- package/src/assets/newspaper.webp +0 -0
- package/src/assets/offering.webp +0 -0
- package/src/component-editor/BadgeEditor.svelte +170 -0
- package/src/component-editor/CalloutEditor.svelte +103 -0
- package/src/component-editor/CardEditor.svelte +184 -0
- package/src/component-editor/CollapsibleSectionEditor.svelte +167 -0
- package/src/component-editor/CornerBadgeEditor.svelte +207 -0
- package/src/component-editor/DialogEditor.svelte +172 -0
- package/src/component-editor/ImageEditor.svelte +72 -0
- package/src/component-editor/InlineEditActionsEditor.svelte +83 -0
- package/src/component-editor/NotificationEditor.svelte +160 -0
- package/src/component-editor/ProgressBarEditor.svelte +124 -0
- package/src/component-editor/RadioButtonEditor.svelte +140 -0
- package/src/component-editor/SectionDividerEditor.svelte +263 -0
- package/src/component-editor/SegmentedControlEditor.svelte +154 -0
- package/src/component-editor/StandardButtonsEditor.svelte +178 -0
- package/src/component-editor/TabBarEditor.svelte +137 -0
- package/src/component-editor/TableEditor.svelte +128 -0
- package/src/component-editor/TooltipEditor.svelte +122 -0
- package/src/component-editor/editorTokens.test.ts +93 -0
- package/src/component-editor/groupKeySlots.test.ts +67 -0
- package/src/component-editor/groupKeySnapshot.test.ts +52 -0
- package/src/component-editor/index.ts +5 -0
- package/src/component-editor/registry.ts +246 -0
- package/src/component-editor/scaffolding/AngleDial.svelte +185 -0
- package/src/component-editor/scaffolding/ComponentEditorBase.svelte +96 -0
- package/src/component-editor/scaffolding/ComponentFileManager.svelte +682 -0
- package/src/component-editor/scaffolding/ComponentFileMenu.svelte +312 -0
- package/src/component-editor/scaffolding/ComponentsTab.svelte +69 -0
- package/src/component-editor/scaffolding/CopyFromMenu.svelte +246 -0
- package/src/component-editor/scaffolding/DemoHeader.svelte +21 -0
- package/src/component-editor/scaffolding/DividerEditor.svelte +81 -0
- package/src/component-editor/scaffolding/FieldsetWrapper.svelte +46 -0
- package/src/component-editor/scaffolding/GradientCard.svelte +291 -0
- package/src/component-editor/scaffolding/LinkageChart.svelte +297 -0
- package/src/component-editor/scaffolding/LinkedBlock.svelte +418 -0
- package/src/component-editor/scaffolding/NonStylableConfig.svelte +57 -0
- package/src/component-editor/scaffolding/SaveAsDialog.svelte +177 -0
- package/src/component-editor/scaffolding/ShadowBackdrop.svelte +25 -0
- package/src/component-editor/scaffolding/ShadowBackdropControls.svelte +56 -0
- package/src/component-editor/scaffolding/StateBlock.svelte +115 -0
- package/src/component-editor/scaffolding/TokenLayout.svelte +511 -0
- package/src/component-editor/scaffolding/TypeEditor.svelte +82 -0
- package/src/component-editor/scaffolding/VariantGroup.svelte +277 -0
- package/src/component-editor/scaffolding/buildTypeGroupTokens.ts +97 -0
- package/src/component-editor/scaffolding/componentSectionType.ts +8 -0
- package/src/component-editor/scaffolding/componentSources.ts +9 -0
- package/src/component-editor/scaffolding/defaultSections.ts +16 -0
- package/src/component-editor/scaffolding/editorContext.ts +44 -0
- package/src/component-editor/scaffolding/linkedBlock.ts +226 -0
- package/src/component-editor/scaffolding/siblings.ts +33 -0
- package/src/component-editor/scaffolding/types.ts +39 -0
- package/src/components/Badge.svelte +231 -42
- package/src/components/Button.svelte +324 -124
- package/src/components/Callout.svelte +145 -0
- package/src/components/Card.svelte +123 -25
- package/src/components/CollapsibleSection.svelte +213 -35
- package/src/components/CornerBadge.svelte +224 -0
- package/src/components/Dialog.svelte +137 -114
- package/src/components/Image.svelte +43 -0
- package/src/components/InlineEditActions.svelte +74 -14
- package/src/components/Notification.svelte +184 -163
- package/src/components/ProgressBar.svelte +216 -22
- package/src/components/RadioButton.svelte +110 -40
- package/src/components/SectionDivider.svelte +428 -74
- package/src/components/SegmentedControl.svelte +203 -0
- package/src/components/TabBar.svelte +146 -21
- package/src/components/Table.svelte +102 -0
- package/src/components/Tooltip.svelte +45 -19
- package/src/components/types.ts +51 -0
- package/src/data/google-fonts.json +75 -0
- package/src/lib/ColumnsOverlay.svelte +20 -7
- package/src/lib/LiveEditorOverlay.svelte +257 -78
- package/src/lib/columnsOverlay.ts +21 -17
- package/src/lib/componentConfig.test.ts +204 -0
- package/src/lib/componentConfigKeys.ts +19 -0
- package/src/lib/componentConfigService.ts +88 -0
- package/src/lib/copyPopover.ts +30 -0
- package/src/lib/cssVarSync.ts +59 -7
- package/src/lib/editorConfigStore.ts +0 -10
- package/src/lib/editorCore.ts +402 -0
- package/src/lib/editorKeybindings.ts +52 -0
- package/src/lib/editorPersistence.ts +106 -0
- package/src/lib/editorRenderer.ts +74 -0
- package/src/lib/editorStore.test.ts +328 -0
- package/src/lib/editorStore.ts +412 -0
- package/src/lib/editorTypes.ts +100 -0
- package/src/lib/editorViewStore.ts +55 -0
- package/src/lib/files/versionedFileResource.ts +140 -0
- package/src/lib/fontLoader.ts +130 -0
- package/src/lib/fontMigration.ts +140 -0
- package/src/lib/fontParse.ts +168 -0
- package/src/lib/index.ts +48 -30
- package/src/lib/lazyConfig.test.ts +54 -0
- package/src/lib/migrations/2026-04-24-component-prefix-and-suffix-renames.ts +64 -0
- package/src/lib/migrations/2026-04-24-legacy-keys-and-bg-to-canvas.ts +71 -0
- package/src/lib/migrations/2026-04-27-segmentedcontrol-disabled-flatten.ts +43 -0
- package/src/lib/migrations/2026-05-08-collapsiblesection-frame-and-cleanup.ts +68 -0
- package/src/lib/migrations/2026-05-08-collapsiblesection-variant-namespace.ts +35 -0
- package/src/lib/migrations/2026-05-10-sectiondivider-gradient-stops.ts +50 -0
- package/src/lib/migrations/2026-05-13-primary-to-brand.ts +90 -0
- package/src/lib/migrations/index.ts +93 -0
- package/src/lib/migrations/migrations.test.ts +341 -0
- package/src/lib/navLinkTypes.ts +1 -0
- package/src/lib/overlayState.ts +3 -0
- package/src/lib/paletteDerivation.ts +300 -0
- package/src/lib/parentRouteStore.ts +42 -0
- package/src/lib/parsers/globalRootBlock.ts +32 -0
- package/src/lib/presetService.ts +94 -0
- package/src/lib/router.ts +42 -10
- package/src/lib/scrollSection.ts +45 -0
- package/src/lib/slices/columns.ts +59 -0
- package/src/lib/slices/components.ts +362 -0
- package/src/lib/slices/domainVars.ts +15 -0
- package/src/lib/slices/fonts.ts +30 -0
- package/src/lib/slices/gradients.ts +153 -0
- package/src/lib/slices/overlays.ts +132 -0
- package/src/lib/slices/palettes.ts +26 -0
- package/src/lib/slices/shadows.ts +123 -0
- package/src/lib/storage.ts +88 -0
- package/src/lib/themeInit.ts +74 -0
- package/src/lib/themeService.ts +101 -0
- package/src/lib/themeTypes.ts +146 -0
- package/src/lib/tokenRegistry.ts +148 -0
- package/src/pages/ComponentEditorPage.svelte +384 -0
- package/src/pages/ComponentEditorPage.svelte.d.ts +2 -0
- package/src/pages/Editor.svelte +98 -0
- package/src/pages/Editor.svelte.d.ts +2 -0
- package/src/pages/EditorShell.svelte +348 -0
- package/src/styles/_padding.scss +34 -0
- package/src/styles/fonts/Fraunces/Fraunces-italic-latin-ext.woff2 +0 -0
- package/src/styles/fonts/Fraunces/Fraunces-italic-latin.woff2 +0 -0
- package/src/styles/fonts/Fraunces/Fraunces-roman-latin-ext.woff2 +0 -0
- package/src/styles/fonts/Fraunces/Fraunces-roman-latin.woff2 +0 -0
- package/src/styles/fonts/Manrope/Manrope-latin-ext.woff2 +0 -0
- package/src/styles/fonts/Manrope/Manrope-latin.woff2 +0 -0
- package/src/styles/fonts.css +22 -10
- package/src/styles/form-controls.css +14 -16
- package/src/styles/tokens.css +1322 -0
- package/src/styles/ui-editor.css +126 -0
- package/src/{showcase → ui}/BezierCurveEditor.svelte +14 -14
- package/src/{showcase → ui}/ColorEditPanel.svelte +42 -36
- package/src/ui/EditorViewSwitcher.svelte +180 -0
- package/src/ui/FontStackEditor.svelte +360 -0
- package/src/ui/GradientEditor.svelte +461 -0
- package/src/ui/GradientStopPicker.svelte +74 -0
- package/src/ui/PaletteEditor.svelte +1590 -0
- package/src/ui/PaletteEditor.test.ts +108 -0
- package/src/ui/PresetFileManager.svelte +567 -0
- package/src/ui/ProjectFontsSection.svelte +645 -0
- package/src/{showcase → ui}/SurfacesTab.svelte +39 -39
- package/src/{showcase → ui}/TextTab.svelte +27 -27
- package/src/{showcase/TokenFileManager.svelte → ui/ThemeFileManager.svelte} +196 -112
- package/src/ui/Toggle.svelte +108 -0
- package/src/ui/UICopyPopover.svelte +78 -0
- package/src/{showcase/EditorDialog.svelte → ui/UIDialog.svelte} +66 -25
- package/src/ui/UIFontFamilySelector.svelte +309 -0
- package/src/ui/UIFontSizeSelector.svelte +165 -0
- package/src/ui/UIFontWeightSelector.svelte +52 -0
- package/src/ui/UILineHeightSelector.svelte +47 -0
- package/src/ui/UILinkToggle.svelte +60 -0
- package/src/ui/UIOptionItem.svelte +74 -0
- package/src/ui/UIOptionList.svelte +27 -0
- package/src/ui/UIPaddingSelector.svelte +661 -0
- package/src/ui/UIPaletteSelector.svelte +1084 -0
- package/src/ui/UIRadio.svelte +72 -0
- package/src/ui/UIRadioGroup.svelte +59 -0
- package/src/ui/UIRelinkConfirmPopover.svelte +235 -0
- package/src/ui/UITokenSelector.svelte +509 -0
- package/src/ui/UIVariantSelector.svelte +145 -0
- package/src/ui/VariablesTab.svelte +252 -0
- package/src/ui/index.ts +31 -0
- package/src/ui/keepInViewport.ts +84 -0
- package/src/ui/palette/GradientStopEditor.svelte +482 -0
- package/src/ui/palette/OverridesPanel.svelte +526 -0
- package/src/ui/palette/PaletteBase.svelte +165 -0
- package/src/ui/palette/ScaleCurveEditor.svelte +38 -0
- package/src/ui/palette/paletteEditorState.ts +89 -0
- package/src/ui/sections/ColumnsSection.svelte +273 -0
- package/src/ui/sections/GradientsSection.svelte +147 -0
- package/src/ui/sections/OverlaysSection.svelte +670 -0
- package/src/ui/sections/ShadowsSection.svelte +1250 -0
- package/src/ui/sections/TokenScaleTable.svelte +332 -0
- package/src/ui/sections/tokenScales.ts +81 -0
- package/src/ui/variantScales.ts +108 -0
- package/src/components/DetailNav.svelte +0 -78
- package/src/components/Toggle.svelte +0 -86
- package/src/lib/tokenInit.ts +0 -29
- package/src/lib/tokenService.ts +0 -144
- package/src/lib/tokenTypes.ts +0 -45
- package/src/pages/Admin.svelte +0 -100
- package/src/pages/ShowcasePage.svelte +0 -144
- package/src/showcase/BackupBrowser.svelte +0 -617
- package/src/showcase/ComponentsTab.svelte +0 -105
- package/src/showcase/PaletteEditor.svelte +0 -2579
- package/src/showcase/PaletteSelector.svelte +0 -627
- package/src/showcase/TokenMap.svelte +0 -54
- package/src/showcase/VariablesTab.svelte +0 -2655
- package/src/showcase/VisualsTab.svelte +0 -231
- package/src/showcase/demos/BadgeDemo.svelte +0 -56
- package/src/showcase/demos/CardDemo.svelte +0 -50
- package/src/showcase/demos/ChoiceButtonsDemo.svelte +0 -192
- package/src/showcase/demos/CollapsibleSectionDemo.svelte +0 -54
- package/src/showcase/demos/DialogDemo.svelte +0 -42
- package/src/showcase/demos/InlineEditActionsDemo.svelte +0 -25
- package/src/showcase/demos/NotificationDemo.svelte +0 -147
- package/src/showcase/demos/ProgressBarDemo.svelte +0 -54
- package/src/showcase/demos/RadioButtonDemo.svelte +0 -56
- package/src/showcase/demos/SectionDividerDemo.svelte +0 -77
- package/src/showcase/demos/StandardButtonsDemo.svelte +0 -455
- package/src/showcase/demos/TabBarDemo.svelte +0 -58
- package/src/showcase/demos/TooltipDemo.svelte +0 -52
- package/src/showcase/editor.css +0 -93
- package/src/showcase/index.ts +0 -17
- package/src/styles/fonts/Domine/Domine-VariableFont_wght.ttf +0 -0
- package/src/styles/fonts/Domine/OFL.txt +0 -97
- package/src/styles/fonts/Domine/README.txt +0 -66
- /package/src/{showcase → ui}/curveEngine.ts +0 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import UIPaletteSelector from '../../ui/UIPaletteSelector.svelte';
|
|
3
|
+
import UIVariantSelector from '../../ui/UIVariantSelector.svelte';
|
|
4
|
+
import UIFontFamilySelector from '../../ui/UIFontFamilySelector.svelte';
|
|
5
|
+
import UIFontSizeSelector from '../../ui/UIFontSizeSelector.svelte';
|
|
6
|
+
import UIFontWeightSelector from '../../ui/UIFontWeightSelector.svelte';
|
|
7
|
+
import UILineHeightSelector from '../../ui/UILineHeightSelector.svelte';
|
|
8
|
+
import FieldsetWrapper from './FieldsetWrapper.svelte';
|
|
9
|
+
import { BORDER_WIDTH } from '../../ui/variantScales';
|
|
10
|
+
|
|
11
|
+
export let colorVariable: string;
|
|
12
|
+
export let colorLabel: string = 'color';
|
|
13
|
+
export let familyVariable: string | undefined = undefined;
|
|
14
|
+
export let familyLabel: string = 'family';
|
|
15
|
+
export let sizeVariable: string | undefined = undefined;
|
|
16
|
+
export let sizeLabel: string = 'size';
|
|
17
|
+
export let weightVariable: string | undefined = undefined;
|
|
18
|
+
export let weightLabel: string = 'weight';
|
|
19
|
+
export let lineHeightVariable: string | undefined = undefined;
|
|
20
|
+
export let lineHeightLabel: string = 'line-h';
|
|
21
|
+
/** Optional outline rows rendered under the typography rows so a text-with-
|
|
22
|
+
stroke group keeps stroke controls visually nested with the type they
|
|
23
|
+
drive (e.g. SectionDivider title outline). */
|
|
24
|
+
export let outlineWidthVariable: string | undefined = undefined;
|
|
25
|
+
export let outlineWidthLabel: string = 'outline thickness';
|
|
26
|
+
export let outlineColorVariable: string | undefined = undefined;
|
|
27
|
+
export let outlineColorLabel: string = 'outline color';
|
|
28
|
+
/** When set, writes persist through the editor store under this component. */
|
|
29
|
+
export let component: string | undefined = undefined;
|
|
30
|
+
/** Legend text for the fieldset. */
|
|
31
|
+
export let legend: string = 'type';
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<FieldsetWrapper {legend}>
|
|
35
|
+
<div class="type-grid">
|
|
36
|
+
<span class="row-label">{colorLabel}</span>
|
|
37
|
+
<UIPaletteSelector variable={colorVariable} {component} on:change />
|
|
38
|
+
|
|
39
|
+
{#if familyVariable}
|
|
40
|
+
<span class="row-label">{familyLabel}</span>
|
|
41
|
+
<UIFontFamilySelector variable={familyVariable} {component} canBeLinked on:change />
|
|
42
|
+
{/if}
|
|
43
|
+
{#if weightVariable}
|
|
44
|
+
<span class="row-label">{weightLabel}</span>
|
|
45
|
+
<UIFontWeightSelector variable={weightVariable} {component} canBeLinked on:change />
|
|
46
|
+
{/if}
|
|
47
|
+
{#if sizeVariable}
|
|
48
|
+
<span class="row-label">{sizeLabel}</span>
|
|
49
|
+
<UIFontSizeSelector variable={sizeVariable} {component} canBeLinked on:change />
|
|
50
|
+
{/if}
|
|
51
|
+
{#if lineHeightVariable}
|
|
52
|
+
<span class="row-label">{lineHeightLabel}</span>
|
|
53
|
+
<UILineHeightSelector variable={lineHeightVariable} {component} canBeLinked on:change />
|
|
54
|
+
{/if}
|
|
55
|
+
{#if outlineWidthVariable}
|
|
56
|
+
<span class="row-label">{outlineWidthLabel}</span>
|
|
57
|
+
<UIVariantSelector variable={outlineWidthVariable} {component} canBeLinked {...BORDER_WIDTH} on:change />
|
|
58
|
+
{/if}
|
|
59
|
+
{#if outlineColorVariable}
|
|
60
|
+
<span class="row-label">{outlineColorLabel}</span>
|
|
61
|
+
<UIPaletteSelector variable={outlineColorVariable} {component} canBeLinked on:change />
|
|
62
|
+
{/if}
|
|
63
|
+
</div>
|
|
64
|
+
</FieldsetWrapper>
|
|
65
|
+
|
|
66
|
+
<style>
|
|
67
|
+
.type-grid {
|
|
68
|
+
display: grid;
|
|
69
|
+
grid-template-columns: max-content 8rem 1fr;
|
|
70
|
+
column-gap: var(--ui-space-10);
|
|
71
|
+
row-gap: var(--ui-space-6);
|
|
72
|
+
align-items: center;
|
|
73
|
+
padding: var(--ui-space-4) var(--ui-space-12);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.row-label {
|
|
77
|
+
font-size: var(--ui-font-size-sm);
|
|
78
|
+
color: var(--ui-text-secondary);
|
|
79
|
+
text-align: left;
|
|
80
|
+
line-height: 1;
|
|
81
|
+
}
|
|
82
|
+
</style>
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { writable } from 'svelte/store';
|
|
3
|
+
import TokenLayout from './TokenLayout.svelte';
|
|
4
|
+
import StateBlock from './StateBlock.svelte';
|
|
5
|
+
import CopyFromMenu from './CopyFromMenu.svelte';
|
|
6
|
+
import { mutate } from '../../lib/editorStore';
|
|
7
|
+
import { getEditorContext } from './editorContext';
|
|
8
|
+
import type { Token, TypeGroupConfig } from './types';
|
|
9
|
+
import type { Sibling } from './siblings';
|
|
10
|
+
|
|
11
|
+
export let name: string;
|
|
12
|
+
export let title: string;
|
|
13
|
+
export let tokens: Token[] = [];
|
|
14
|
+
export let states: Record<string, Token[]> | null = null;
|
|
15
|
+
/** Per-state type groups; rendered as TypeEditor blocks alongside the state's TokenLayout. */
|
|
16
|
+
export let typeGroups: Record<string, TypeGroupConfig[]> = {};
|
|
17
|
+
/** When set, overrides are read from and cleared through the editor store. */
|
|
18
|
+
export let component: string | undefined = undefined;
|
|
19
|
+
/** Sibling variants of this component (excludes self). When non-empty,
|
|
20
|
+
a "Copy from" menu is rendered that lets the user pull token values from
|
|
21
|
+
a sibling's same-state into the current state. */
|
|
22
|
+
export let siblings: Sibling[] = [];
|
|
23
|
+
/** Forwarded to StateBlock → TokenLayout. >1 lays out the property grid
|
|
24
|
+
across multiple visual columns (column-major flow). Useful for
|
|
25
|
+
single-text components like Button whose 8-10 properties stretch the
|
|
26
|
+
panel vertically when stacked single-column. */
|
|
27
|
+
export let columns: number = 1;
|
|
28
|
+
/** Label rendered above the state-tab selector strip. Defaults to "Element"
|
|
29
|
+
because most strips mix structural parts (e.g. bar, frame) with
|
|
30
|
+
component states; "States" would mislabel the parts. Editors can override
|
|
31
|
+
when every tab on the strip really is a state. */
|
|
32
|
+
export let selectorLabel: string = 'Element';
|
|
33
|
+
|
|
34
|
+
const editorCtx = getEditorContext();
|
|
35
|
+
const linkedOrderStore = editorCtx?.linkedOrder ?? writable<Map<string, number> | null>(null);
|
|
36
|
+
const focusedVariantStore = editorCtx?.focusedVariant ?? writable<string | null>(null);
|
|
37
|
+
const focusedStateStore = editorCtx?.focusedState ?? writable<string | null>(null);
|
|
38
|
+
$: linkedOrder = $linkedOrderStore ?? undefined;
|
|
39
|
+
|
|
40
|
+
let activeTab: string = '';
|
|
41
|
+
|
|
42
|
+
const TYPE_PROPS = ['colorVariable', 'familyVariable', 'sizeVariable', 'weightVariable', 'lineHeightVariable', 'outlineWidthVariable', 'outlineColorVariable'] as const;
|
|
43
|
+
|
|
44
|
+
function pickCopySource(toState: string, fromVariant: string, fromState: string) {
|
|
45
|
+
if (!component || !states) return;
|
|
46
|
+
const isSelfVariant = fromVariant === name;
|
|
47
|
+
const sibling = isSelfVariant ? null : siblings.find((s) => s.name === fromVariant);
|
|
48
|
+
if (!isSelfVariant && !sibling) return;
|
|
49
|
+
const srcTokens = (isSelfVariant ? states[fromState] : sibling!.states[fromState]) ?? [];
|
|
50
|
+
const dstTokens = states[toState] ?? [];
|
|
51
|
+
const srcTypeGroups = (isSelfVariant ? typeGroups[fromState] : sibling!.typeGroups?.[fromState]) ?? [];
|
|
52
|
+
const dstTypeGroups = typeGroups[toState] ?? [];
|
|
53
|
+
|
|
54
|
+
mutate(`copy ${fromVariant}/${fromState} → ${name}/${toState}`, (s) => {
|
|
55
|
+
const slice = s.components[component!] ?? (s.components[component!] = { activeFile: 'default', aliases: {}, config: {} });
|
|
56
|
+
const apply = (srcVar: string, dstVar: string) => {
|
|
57
|
+
if (srcVar === dstVar) return;
|
|
58
|
+
if (srcVar in slice.aliases) slice.aliases[dstVar] = slice.aliases[srcVar];
|
|
59
|
+
else delete slice.aliases[dstVar];
|
|
60
|
+
};
|
|
61
|
+
const minLen = Math.min(srcTokens.length, dstTokens.length);
|
|
62
|
+
for (let i = 0; i < minLen; i++) apply(srcTokens[i].variable, dstTokens[i].variable);
|
|
63
|
+
const minTypeGroups = Math.min(srcTypeGroups.length, dstTypeGroups.length);
|
|
64
|
+
for (let g = 0; g < minTypeGroups; g++) {
|
|
65
|
+
const srcType = srcTypeGroups[g];
|
|
66
|
+
const dstType = dstTypeGroups[g];
|
|
67
|
+
for (const prop of TYPE_PROPS) {
|
|
68
|
+
const srcVar = srcType[prop];
|
|
69
|
+
const dstVar = dstType[prop];
|
|
70
|
+
if (srcVar && dstVar) apply(srcVar, dstVar);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
$: copySources = (() => {
|
|
77
|
+
const fromSiblings = siblings.map((s) => ({
|
|
78
|
+
name: s.name,
|
|
79
|
+
label: s.label,
|
|
80
|
+
states: Object.keys(s.states),
|
|
81
|
+
}));
|
|
82
|
+
const ownStates = states ? Object.keys(states) : [];
|
|
83
|
+
if (ownStates.length >= 2) {
|
|
84
|
+
return [{ name, label: title, states: ownStates }, ...fromSiblings];
|
|
85
|
+
}
|
|
86
|
+
return fromSiblings;
|
|
87
|
+
})();
|
|
88
|
+
|
|
89
|
+
$: stateNames = states ? Object.keys(states) : [];
|
|
90
|
+
$: tabsStripVisible = stateNames.length >= 2;
|
|
91
|
+
$: if (stateNames.length > 0 && !stateNames.includes(activeTab)) {
|
|
92
|
+
activeTab = stateNames[0];
|
|
93
|
+
}
|
|
94
|
+
// Cross-group hint from chart row clicks: adopt it if it names one of our states.
|
|
95
|
+
$: if ($focusedStateStore && stateNames.includes($focusedStateStore) && activeTab !== $focusedStateStore) {
|
|
96
|
+
activeTab = $focusedStateStore;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
$: inFocusMode = siblings.length > 0;
|
|
100
|
+
$: amIFocused = $focusedVariantStore === name;
|
|
101
|
+
$: shouldRender = !inFocusMode || amIFocused;
|
|
102
|
+
// Mirror this group's active state back to the shared store when this is the
|
|
103
|
+
// focused variant, so the linked-block row + chart selection track the user's
|
|
104
|
+
// state-tab clicks (not just chart-row clicks).
|
|
105
|
+
$: if (amIFocused && activeTab && stateNames.includes(activeTab) && $focusedStateStore !== activeTab) {
|
|
106
|
+
focusedStateStore.set(activeTab);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
</script>
|
|
110
|
+
|
|
111
|
+
{#if shouldRender}
|
|
112
|
+
<div class="demo-section variant-group">
|
|
113
|
+
{#if !inFocusMode}
|
|
114
|
+
<div class="variant-header">
|
|
115
|
+
<h3 class="demo-subtitle">{title}</h3>
|
|
116
|
+
</div>
|
|
117
|
+
{/if}
|
|
118
|
+
|
|
119
|
+
{#if states}
|
|
120
|
+
<!-- Preview at top, then state tabs + Copy from, then properties for active tab. -->
|
|
121
|
+
<div class="tabs-preview">
|
|
122
|
+
<span class="section-label">Preview</span>
|
|
123
|
+
<slot activeState={activeTab} />
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
{#if tabsStripVisible || (copySources.length > 0 && activeTab)}
|
|
127
|
+
<div class="tabs-states-block">
|
|
128
|
+
{#if tabsStripVisible}
|
|
129
|
+
<span class="section-label">{selectorLabel}</span>
|
|
130
|
+
{/if}
|
|
131
|
+
<div class="tabs-selectors">
|
|
132
|
+
{#if tabsStripVisible}
|
|
133
|
+
<div class="state-tabs" role="tablist">
|
|
134
|
+
{#each stateNames as s}
|
|
135
|
+
<button
|
|
136
|
+
type="button"
|
|
137
|
+
class="state-tab-btn"
|
|
138
|
+
class:active={activeTab === s}
|
|
139
|
+
role="tab"
|
|
140
|
+
aria-selected={activeTab === s}
|
|
141
|
+
on:click={() => { activeTab = s; focusedStateStore.set(s); }}
|
|
142
|
+
>{s}</button>
|
|
143
|
+
{/each}
|
|
144
|
+
</div>
|
|
145
|
+
{/if}
|
|
146
|
+
{#if activeTab}
|
|
147
|
+
<slot name="state-actions" stateName={activeTab} />
|
|
148
|
+
{/if}
|
|
149
|
+
{#if copySources.length > 0 && activeTab}
|
|
150
|
+
<CopyFromMenu
|
|
151
|
+
toState={activeTab}
|
|
152
|
+
variantName={name}
|
|
153
|
+
{copySources}
|
|
154
|
+
on:select={(e) => pickCopySource(activeTab, e.detail.fromVariant, e.detail.fromState)}
|
|
155
|
+
/>
|
|
156
|
+
{/if}
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
{/if}
|
|
160
|
+
|
|
161
|
+
{#if activeTab && states[activeTab]}
|
|
162
|
+
{@const stateName = activeTab}
|
|
163
|
+
<slot name="composite-controls" {stateName} />
|
|
164
|
+
<span class="section-label">Properties</span>
|
|
165
|
+
<StateBlock
|
|
166
|
+
tokens={states[stateName]}
|
|
167
|
+
typeGroups={typeGroups[stateName] ?? []}
|
|
168
|
+
{component}
|
|
169
|
+
{linkedOrder}
|
|
170
|
+
{columns}
|
|
171
|
+
on:change
|
|
172
|
+
/>
|
|
173
|
+
{/if}
|
|
174
|
+
{:else}
|
|
175
|
+
<slot activeState="" />
|
|
176
|
+
<TokenLayout
|
|
177
|
+
title={name}
|
|
178
|
+
tokens={tokens}
|
|
179
|
+
{component}
|
|
180
|
+
{linkedOrder}
|
|
181
|
+
on:change
|
|
182
|
+
/>
|
|
183
|
+
{/if}
|
|
184
|
+
|
|
185
|
+
</div>
|
|
186
|
+
{/if}
|
|
187
|
+
|
|
188
|
+
<style>
|
|
189
|
+
.variant-group {
|
|
190
|
+
padding: var(--ui-space-16);
|
|
191
|
+
background: var(--ui-surface-low);
|
|
192
|
+
border: 1px solid var(--ui-border-faint);
|
|
193
|
+
border-radius: var(--ui-radius-md);
|
|
194
|
+
gap: var(--ui-space-12);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.variant-header {
|
|
198
|
+
display: flex;
|
|
199
|
+
align-items: center;
|
|
200
|
+
justify-content: flex-start;
|
|
201
|
+
gap: var(--ui-space-12);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.variant-header .demo-subtitle {
|
|
205
|
+
margin: 0;
|
|
206
|
+
font-size: var(--ui-font-size-2xl);
|
|
207
|
+
font-weight: var(--ui-font-weight-semibold);
|
|
208
|
+
color: var(--ui-text-primary);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.tabs-preview {
|
|
212
|
+
display: flex;
|
|
213
|
+
flex-direction: column;
|
|
214
|
+
gap: var(--ui-space-8);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.tabs-states-block {
|
|
218
|
+
display: flex;
|
|
219
|
+
flex-direction: column;
|
|
220
|
+
gap: var(--ui-space-8);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.tabs-selectors {
|
|
224
|
+
display: flex;
|
|
225
|
+
align-items: center;
|
|
226
|
+
gap: var(--ui-space-12);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.state-tabs {
|
|
230
|
+
display: inline-flex;
|
|
231
|
+
flex-wrap: wrap;
|
|
232
|
+
gap: var(--ui-space-4);
|
|
233
|
+
padding: var(--ui-space-4);
|
|
234
|
+
background: var(--ui-surface-lowest);
|
|
235
|
+
border: 1px solid var(--ui-border-faint);
|
|
236
|
+
border-radius: var(--ui-radius-md);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.state-tab-btn {
|
|
240
|
+
padding: var(--ui-space-6) var(--ui-space-12);
|
|
241
|
+
background: none;
|
|
242
|
+
border: none;
|
|
243
|
+
border-radius: var(--ui-radius-sm);
|
|
244
|
+
color: var(--ui-text-secondary);
|
|
245
|
+
font-size: var(--ui-font-size-sm);
|
|
246
|
+
font-weight: var(--ui-font-weight-medium);
|
|
247
|
+
text-transform: capitalize;
|
|
248
|
+
cursor: pointer;
|
|
249
|
+
transition: color var(--ui-transition-fast), background var(--ui-transition-fast);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.state-tab-btn:hover:not(.active) {
|
|
253
|
+
color: var(--ui-text-primary);
|
|
254
|
+
background: var(--ui-hover);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.state-tab-btn.active {
|
|
258
|
+
color: var(--ui-text-primary);
|
|
259
|
+
background: var(--ui-surface-high);
|
|
260
|
+
box-shadow: 0 0 0 1px var(--ui-border-default);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.section-label {
|
|
264
|
+
display: block;
|
|
265
|
+
margin: 0;
|
|
266
|
+
font-size: var(--ui-font-size-md);
|
|
267
|
+
font-weight: 500;
|
|
268
|
+
color: var(--ui-text-primary);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/* The Properties label is a direct child of .variant-group (which doesn't
|
|
272
|
+
apply a flex gap), so space it from the tabs strip above. */
|
|
273
|
+
.variant-group > .section-label {
|
|
274
|
+
margin-top: var(--ui-space-8);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
</style>
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { Token, TypeGroupConfig } from './types';
|
|
2
|
+
|
|
3
|
+
/** The four font-shape properties on a TypeGroupConfig that share-block sharing
|
|
4
|
+
targets. Order is the canonical render order and the groupKey order. */
|
|
5
|
+
export const TYPE_FONT_PROPS = [
|
|
6
|
+
{ key: 'familyVariable', label: 'font family', defaultGroupKey: 'font-family' },
|
|
7
|
+
{ key: 'sizeVariable', label: 'font size', defaultGroupKey: 'font-size' },
|
|
8
|
+
{ key: 'weightVariable', label: 'font weight', defaultGroupKey: 'font-weight' },
|
|
9
|
+
{ key: 'lineHeightVariable', label: 'line height', defaultGroupKey: 'line-height' },
|
|
10
|
+
] as const satisfies ReadonlyArray<{
|
|
11
|
+
key: keyof TypeGroupConfig;
|
|
12
|
+
label: string;
|
|
13
|
+
defaultGroupKey: string;
|
|
14
|
+
}>;
|
|
15
|
+
|
|
16
|
+
export type TypeFontProp = typeof TYPE_FONT_PROPS[number];
|
|
17
|
+
|
|
18
|
+
export type BuildTypeGroupTokensOptions = {
|
|
19
|
+
/** Override the default groupKey per property. Receives the prop descriptor and the
|
|
20
|
+
type-group config it came from. Return a stable string — siblings sharing the same
|
|
21
|
+
groupKey are linked in the linked block. */
|
|
22
|
+
groupKeyFor?: (prop: TypeFontProp, group: TypeGroupConfig) => string;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/** Derive the Token[] schema entries for every TypeGroupConfig in `typeGroups`. Each
|
|
26
|
+
group emits one `colorVariable` token plus up to 4 font-shape tokens (family/size/
|
|
27
|
+
weight/line-height) for whichever of those are declared on the group. Font-shape
|
|
28
|
+
tokens carry `canBeLinked: true` and a stable `groupKey` so the linked-block linkage
|
|
29
|
+
sees them; the color token is emitted plain (no groupKey, not shareable) so it stays
|
|
30
|
+
out of the linked block while still appearing in the editor's full token surface
|
|
31
|
+
(used by the reset-button and the design-token resolution test).
|
|
32
|
+
|
|
33
|
+
Mirrors the `flatMap`/loop pattern in StandardButtonsEditor and RadioButtonEditor so
|
|
34
|
+
editors don't have to hand-list 16+ near-identical Token entries. */
|
|
35
|
+
export function buildTypeGroupTokens(
|
|
36
|
+
typeGroups: Record<string, TypeGroupConfig[]>,
|
|
37
|
+
options: BuildTypeGroupTokensOptions = {},
|
|
38
|
+
): Token[] {
|
|
39
|
+
const { groupKeyFor } = options;
|
|
40
|
+
const tokens: Token[] = [];
|
|
41
|
+
for (const groups of Object.values(typeGroups)) {
|
|
42
|
+
for (const group of groups) {
|
|
43
|
+
tokens.push({ label: group.colorLabel ?? 'color', variable: group.colorVariable });
|
|
44
|
+
for (const prop of TYPE_FONT_PROPS) {
|
|
45
|
+
const variable = group[prop.key];
|
|
46
|
+
if (!variable) continue;
|
|
47
|
+
const groupKey = groupKeyFor ? groupKeyFor(prop, group) : prop.defaultGroupKey;
|
|
48
|
+
tokens.push({ label: prop.label, canBeLinked: true, groupKey, variable });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return tokens;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Color-only counterpart for editors that hand-roll their font-shape tokens (because
|
|
56
|
+
they use a custom groupKey scheme) but still want their `colorVariable`s in
|
|
57
|
+
`allTokens` — needed so the reset-button and the design-token resolution test see
|
|
58
|
+
them. Accepts either the full `Record` shape or a flat group array, so it slots
|
|
59
|
+
cleanly into both `Object.values(typeGroups).flat()` chains and per-variant
|
|
60
|
+
`flatMap` constructions.
|
|
61
|
+
|
|
62
|
+
Each color token gets a groupKey derived from the colorVariable's last-dash
|
|
63
|
+
suffix (e.g. `--badge-primary-text` → `text`) so that all variants/states of
|
|
64
|
+
the same slot are siblings and can be linked in the editor. They start out
|
|
65
|
+
divergent (one value per variant) and the user can opt in to a single shared
|
|
66
|
+
value via the link UI. */
|
|
67
|
+
export function buildTypeGroupColorTokens(
|
|
68
|
+
typeGroups: Record<string, TypeGroupConfig[]> | TypeGroupConfig[],
|
|
69
|
+
): Token[] {
|
|
70
|
+
const groups: TypeGroupConfig[] = Array.isArray(typeGroups)
|
|
71
|
+
? typeGroups
|
|
72
|
+
: Object.values(typeGroups).flat();
|
|
73
|
+
return groups.map((g) => {
|
|
74
|
+
const lastDash = g.colorVariable.lastIndexOf('-');
|
|
75
|
+
const groupKey = lastDash >= 0 ? g.colorVariable.slice(lastDash + 1) : g.colorVariable;
|
|
76
|
+
return { label: g.colorLabel ?? 'color', variable: g.colorVariable, groupKey };
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Companion helper: derive a `linkableContexts` map mapping every typography variable in
|
|
81
|
+
`typeGroups` to its state name. Use to build the linked-block context map without
|
|
82
|
+
spelling out 16+ entries; merge with non-typography entries via spread. */
|
|
83
|
+
export function buildTypeGroupShareableContexts(
|
|
84
|
+
typeGroups: Record<string, TypeGroupConfig[]>,
|
|
85
|
+
): Array<readonly [string, string]> {
|
|
86
|
+
const entries: Array<readonly [string, string]> = [];
|
|
87
|
+
for (const [stateName, groups] of Object.entries(typeGroups)) {
|
|
88
|
+
for (const group of groups) {
|
|
89
|
+
for (const prop of TYPE_FONT_PROPS) {
|
|
90
|
+
const variable = group[prop.key];
|
|
91
|
+
if (!variable) continue;
|
|
92
|
+
entries.push([variable, stateName] as const);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return entries;
|
|
97
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { componentRegistry } from '../registry';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Resolve a component id to its runtime source file path. Reads from the
|
|
5
|
+
* single component registry — no parallel mapping to maintain.
|
|
6
|
+
*/
|
|
7
|
+
export function componentSourceFile(component: string): string {
|
|
8
|
+
return componentRegistry[component as keyof typeof componentRegistry]?.sourceFile ?? '';
|
|
9
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ComponentSection } from './componentSectionType';
|
|
2
|
+
import { componentRegistryEntries } from '../registry';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Default editor sections — derived from the single component registry. Each
|
|
6
|
+
* section's `id` is the canonical lowercase component id (matches the runtime
|
|
7
|
+
* filename, server scan, and `setComponentAlias` key); `label` is the
|
|
8
|
+
* display string; `component` is the editor Svelte component.
|
|
9
|
+
*
|
|
10
|
+
* To add or reorder sections, edit `src/component-editor/registry.ts`.
|
|
11
|
+
*/
|
|
12
|
+
export const defaultSections: ComponentSection[] = componentRegistryEntries.map((entry) => ({
|
|
13
|
+
id: entry.id,
|
|
14
|
+
label: entry.label,
|
|
15
|
+
component: entry.editorComponent,
|
|
16
|
+
}));
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { getContext, setContext } from 'svelte';
|
|
2
|
+
import { writable, type Writable, type Readable } from 'svelte/store';
|
|
3
|
+
|
|
4
|
+
const KEY = Symbol('editor-context');
|
|
5
|
+
|
|
6
|
+
export type EditorContext = {
|
|
7
|
+
/** Per-variable rank used by TokenLayout to align linked rows; null when no linked block. */
|
|
8
|
+
linkedOrder: Readable<Map<string, number> | null>;
|
|
9
|
+
/** Variant currently focused in the preview when multiple sibling variants exist. */
|
|
10
|
+
focusedVariant: Writable<string | null>;
|
|
11
|
+
/** Cross-group hint for which state tab to activate. VariantGroups whose `stateNames`
|
|
12
|
+
contain the value adopt it as their `activeTab`; others ignore it. Used to forward
|
|
13
|
+
LinkageChart row clicks to the state tab strip when the chart spans states. */
|
|
14
|
+
focusedState: Writable<string | null>;
|
|
15
|
+
/** Variable currently hovered in either the per-state Properties grid or the
|
|
16
|
+
Linked-properties block. Bidirectional cue: a hover in one surface lights up
|
|
17
|
+
the matching row in the other so the user can see the linkage at a glance. */
|
|
18
|
+
hoveredLinkedVariable: Writable<string | null>;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/** Internal mutable handle used by ComponentEditorBase. */
|
|
22
|
+
export type EditorContextInternal = EditorContext & {
|
|
23
|
+
_linkedOrder: Writable<Map<string, number> | null>;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export function createEditorContext(): EditorContextInternal {
|
|
27
|
+
const _linkedOrder = writable<Map<string, number> | null>(null);
|
|
28
|
+
const focusedVariant = writable<string | null>(null);
|
|
29
|
+
const focusedState = writable<string | null>(null);
|
|
30
|
+
const hoveredLinkedVariable = writable<string | null>(null);
|
|
31
|
+
const ctx: EditorContextInternal = {
|
|
32
|
+
linkedOrder: _linkedOrder,
|
|
33
|
+
focusedVariant,
|
|
34
|
+
focusedState,
|
|
35
|
+
hoveredLinkedVariable,
|
|
36
|
+
_linkedOrder,
|
|
37
|
+
};
|
|
38
|
+
setContext(KEY, ctx);
|
|
39
|
+
return ctx;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function getEditorContext(): EditorContext | undefined {
|
|
43
|
+
return getContext(KEY);
|
|
44
|
+
}
|