@motion-proto/live-tokens 0.3.9 → 0.6.0
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 +47 -4
- package/package.json +18 -12
- package/src/component-editor/BadgeEditor.svelte +24 -22
- package/src/component-editor/CalloutEditor.svelte +3 -3
- package/src/component-editor/CardEditor.svelte +25 -21
- package/src/component-editor/CollapsibleSectionEditor.svelte +27 -25
- package/src/component-editor/CornerBadgeEditor.svelte +37 -35
- package/src/component-editor/DialogEditor.svelte +26 -24
- package/src/component-editor/ImageEditor.svelte +11 -9
- package/src/component-editor/InlineEditActionsEditor.svelte +17 -15
- package/src/component-editor/NotificationEditor.svelte +32 -30
- package/src/component-editor/ProgressBarEditor.svelte +3 -3
- package/src/component-editor/RadioButtonEditor.svelte +31 -29
- package/src/component-editor/SectionDividerEditor.svelte +30 -28
- package/src/component-editor/SegmentedControlEditor.svelte +29 -25
- package/src/component-editor/StandardButtonsEditor.svelte +42 -38
- package/src/component-editor/TabBarEditor.svelte +20 -18
- package/src/component-editor/TableEditor.svelte +4 -4
- package/src/component-editor/TooltipEditor.svelte +11 -9
- package/src/component-editor/registry.ts +2 -2
- package/src/component-editor/scaffolding/AngleDial.svelte +20 -19
- package/src/component-editor/scaffolding/ComponentEditorBase.svelte +44 -20
- package/src/component-editor/scaffolding/ComponentFileManager.svelte +262 -38
- package/src/component-editor/scaffolding/ComponentFileMenu.svelte +41 -29
- package/src/component-editor/scaffolding/ComponentsTab.svelte +7 -3
- package/src/component-editor/scaffolding/CopyFromMenu.svelte +21 -12
- package/src/component-editor/scaffolding/DemoHeader.svelte +13 -4
- package/src/component-editor/scaffolding/DividerEditor.svelte +27 -14
- package/src/component-editor/scaffolding/FieldsetWrapper.svelte +10 -4
- package/src/component-editor/scaffolding/GradientCard.svelte +25 -20
- package/src/component-editor/scaffolding/LinkageChart.svelte +43 -34
- package/src/component-editor/scaffolding/LinkedBlock.svelte +24 -21
- package/src/component-editor/scaffolding/NonStylableConfig.svelte +6 -1
- package/src/component-editor/scaffolding/SaveAsDialog.svelte +39 -35
- package/src/component-editor/scaffolding/ShadowBackdrop.svelte +21 -9
- package/src/component-editor/scaffolding/ShadowBackdropControls.svelte +8 -3
- package/src/component-editor/scaffolding/StateBlock.svelte +30 -13
- package/src/component-editor/scaffolding/TokenLayout.svelte +46 -30
- package/src/component-editor/scaffolding/TypeEditor.svelte +52 -26
- package/src/component-editor/scaffolding/VariantGroup.svelte +81 -48
- package/src/component-editor/scaffolding/componentSectionType.ts +2 -2
- package/src/components/Badge.svelte +45 -26
- package/src/components/Button.svelte +44 -21
- package/src/components/Callout.svelte +17 -12
- package/src/components/Card.svelte +23 -11
- package/src/components/CollapsibleSection.svelte +56 -27
- package/src/components/CornerBadge.svelte +32 -18
- package/src/components/Dialog.svelte +55 -31
- package/src/components/Image.svelte +14 -5
- package/src/components/InlineEditActions.svelte +22 -10
- package/src/components/Notification.svelte +39 -19
- package/src/components/ProgressBar.svelte +27 -17
- package/src/components/RadioButton.svelte +27 -10
- package/src/components/SectionDivider.svelte +34 -26
- package/src/components/SegmentedControl.svelte +23 -9
- package/src/components/TabBar.svelte +23 -10
- package/src/components/Table.svelte +8 -3
- package/src/components/Tooltip.svelte +15 -5
- package/src/lib/ColumnsOverlay.svelte +3 -3
- package/src/lib/LiveEditorOverlay.svelte +57 -36
- package/src/pages/ComponentEditorPage.svelte +25 -14
- package/src/pages/Editor.svelte +8 -2
- package/src/pages/EditorShell.svelte +24 -20
- package/src/styles/site.css +138 -0
- package/src/styles/tokens.css +78 -76
- package/src/styles/ui-form-controls.css +186 -0
- package/src/ui/BezierCurveEditor.svelte +59 -43
- package/src/ui/ColorEditPanel.svelte +71 -44
- package/src/ui/EditorViewSwitcher.svelte +9 -5
- package/src/ui/FontStackEditor.svelte +17 -16
- package/src/ui/GradientEditor.svelte +42 -33
- package/src/ui/GradientStopPicker.svelte +18 -29
- package/src/ui/PaletteEditor.svelte +238 -212
- package/src/ui/PresetFileManager.svelte +20 -18
- package/src/ui/ProjectFontsSection.svelte +34 -34
- package/src/ui/SurfacesTab.svelte +3 -3
- package/src/ui/TextTab.svelte +2 -2
- package/src/ui/ThemeFileManager.svelte +38 -35
- package/src/ui/Toggle.svelte +11 -9
- package/src/ui/UICopyPopover.svelte +19 -15
- package/src/ui/UIDialog.svelte +48 -30
- package/src/ui/UIFontFamilySelector.svelte +104 -78
- package/src/ui/UIFontSizeSelector.svelte +38 -20
- package/src/ui/UIFontWeightSelector.svelte +33 -13
- package/src/ui/UILineHeightSelector.svelte +33 -13
- package/src/ui/UILinkToggle.svelte +7 -6
- package/src/ui/UIOptionItem.svelte +21 -7
- package/src/ui/UIOptionList.svelte +9 -3
- package/src/ui/UIPaddingSelector.svelte +108 -82
- package/src/ui/UIPaletteSelector.svelte +186 -161
- package/src/ui/UIRadio.svelte +23 -8
- package/src/ui/UIRadioGroup.svelte +9 -8
- package/src/ui/UIRelinkConfirmPopover.svelte +26 -16
- package/src/ui/UITokenSelector.svelte +112 -68
- package/src/ui/UIVariantSelector.svelte +79 -57
- package/src/ui/VariablesTab.svelte +15 -15
- package/src/ui/palette/GradientStopEditor.svelte +45 -26
- package/src/ui/palette/OverridesPanel.svelte +85 -49
- package/src/ui/palette/PaletteBase.svelte +60 -32
- package/src/ui/palette/ScaleCurveEditor.svelte +25 -10
- package/src/ui/sections/ColumnsSection.svelte +13 -13
- package/src/ui/sections/GradientsSection.svelte +12 -9
- package/src/ui/sections/OverlaysSection.svelte +50 -47
- package/src/ui/sections/ShadowsSection.svelte +110 -104
- package/src/ui/sections/TokenScaleTable.svelte +38 -22
- package/src/ui/sections/tokenScales.ts +2 -2
- package/src/styles/form-controls.css +0 -188
|
@@ -13,8 +13,12 @@
|
|
|
13
13
|
const fadeDur = reduceMotion ? 0 : 140;
|
|
14
14
|
const slideDur = reduceMotion ? 0 : 200;
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
interface Props {
|
|
17
|
+
component: string;
|
|
18
|
+
linked: LinkedBlockResult;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let { component, linked }: Props = $props();
|
|
18
22
|
|
|
19
23
|
const editorCtx = getEditorContext();
|
|
20
24
|
const focusedVariant = editorCtx?.focusedVariant;
|
|
@@ -25,9 +29,9 @@
|
|
|
25
29
|
doesn't know if its rows are variants (top-level tab strip) or states (per-VariantGroup
|
|
26
30
|
state tabs), so we set both stores; each consumer adopts the value only if it names
|
|
27
31
|
one of its own tabs. */
|
|
28
|
-
function handleChartSelect(
|
|
29
|
-
editorCtx?.focusedVariant.set(
|
|
30
|
-
editorCtx?.focusedState.set(
|
|
32
|
+
function handleChartSelect(label: string) {
|
|
33
|
+
editorCtx?.focusedVariant.set(label);
|
|
34
|
+
editorCtx?.focusedState.set(label);
|
|
31
35
|
}
|
|
32
36
|
|
|
33
37
|
/** Pick the sibling that backs the cell the user is currently focused on, so the row
|
|
@@ -71,35 +75,35 @@
|
|
|
71
75
|
return `${brokenContexts.length} unlinked`;
|
|
72
76
|
}
|
|
73
77
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
78
|
+
let focusedV = $derived((focusedVariant ? $focusedVariant : null) ?? null);
|
|
79
|
+
let focusedS = $derived((focusedState ? $focusedState : null) ?? null);
|
|
80
|
+
let hoveredVar = $derived((hoveredLinkedVariable ? $hoveredLinkedVariable : null) ?? null);
|
|
77
81
|
|
|
78
82
|
function setHover(variable: string | null) {
|
|
79
83
|
hoveredLinkedVariable?.set(variable);
|
|
80
84
|
}
|
|
81
85
|
|
|
82
86
|
/** One card per LinkedGroup. Stable order = editor-declared order. */
|
|
83
|
-
|
|
87
|
+
let cards = $derived(linked.groups.map((g) => ({
|
|
84
88
|
contexts: g.contexts,
|
|
85
89
|
brokenContexts: g.brokenContexts,
|
|
86
90
|
row: { ...g.token, variable: pickFocusedVariable(g, focusedV, focusedS) },
|
|
87
91
|
caption: captionFor(g.contexts),
|
|
88
92
|
unlinkedText: formatUnlinked(g.brokenContexts),
|
|
89
93
|
isBroken: g.brokenContexts.length > 0,
|
|
90
|
-
}));
|
|
94
|
+
})));
|
|
91
95
|
|
|
92
96
|
/** Section-level summary: count of properties with any broken peers. The
|
|
93
97
|
header tells the user *whether to look*; the per-card text tells them
|
|
94
98
|
*what's broken*. Two layers, two granularities. */
|
|
95
|
-
|
|
96
|
-
|
|
99
|
+
let brokenPropertyCount = $derived(cards.filter((c) => c.isBroken).length);
|
|
100
|
+
let hasAnyBroken = $derived(brokenPropertyCount > 0);
|
|
97
101
|
|
|
98
102
|
/** Default closed; the section header's summary count + "in sync / N unlinked"
|
|
99
103
|
text is the at-a-glance signal, so users opt into the matrix only when
|
|
100
104
|
they need it. */
|
|
101
|
-
let sectionToggleOverride: boolean | null = null;
|
|
102
|
-
|
|
105
|
+
let sectionToggleOverride: boolean | null = $state(null);
|
|
106
|
+
let sectionExpanded = $derived(sectionToggleOverride ?? false);
|
|
103
107
|
function toggleSection() {
|
|
104
108
|
sectionToggleOverride = !sectionExpanded;
|
|
105
109
|
}
|
|
@@ -107,7 +111,7 @@
|
|
|
107
111
|
/** Per-card chart expand state, keyed by the card's row variable (which is
|
|
108
112
|
stable per LinkedGroup since pickFocusedVariable runs against the same
|
|
109
113
|
group on every render). */
|
|
110
|
-
let expandedCards: Record<string, boolean> = {};
|
|
114
|
+
let expandedCards: Record<string, boolean> = $state({});
|
|
111
115
|
function toggleCard(variable: string) {
|
|
112
116
|
expandedCards = { ...expandedCards, [variable]: !expandedCards[variable] };
|
|
113
117
|
}
|
|
@@ -120,7 +124,7 @@
|
|
|
120
124
|
class="section-header"
|
|
121
125
|
class:expanded={sectionExpanded}
|
|
122
126
|
aria-expanded={sectionExpanded}
|
|
123
|
-
|
|
127
|
+
onclick={toggleSection}
|
|
124
128
|
>
|
|
125
129
|
<i class="fas fa-chevron-right chevron"></i>
|
|
126
130
|
<span class="section-title">Linked properties</span>
|
|
@@ -144,8 +148,8 @@
|
|
|
144
148
|
class="linked-card"
|
|
145
149
|
class:broken={card.isBroken}
|
|
146
150
|
class:hovered={cardHovered}
|
|
147
|
-
|
|
148
|
-
|
|
151
|
+
onmouseenter={() => setHover(card.row.variable)}
|
|
152
|
+
onmouseleave={() => setHover(null)}
|
|
149
153
|
>
|
|
150
154
|
<h4 class="property-name">{card.row.label}</h4>
|
|
151
155
|
<div class="control-row">
|
|
@@ -154,7 +158,6 @@
|
|
|
154
158
|
{component}
|
|
155
159
|
linkedOrder={linked.linkedOrder}
|
|
156
160
|
isLinkedBlock
|
|
157
|
-
on:change
|
|
158
161
|
/>
|
|
159
162
|
</div>
|
|
160
163
|
<button
|
|
@@ -162,7 +165,7 @@
|
|
|
162
165
|
class="drill-down"
|
|
163
166
|
class:expanded={cardExpanded}
|
|
164
167
|
aria-expanded={cardExpanded}
|
|
165
|
-
|
|
168
|
+
onclick={() => toggleCard(card.row.variable)}
|
|
166
169
|
>
|
|
167
170
|
<i class="fas fa-chevron-right chevron"></i>
|
|
168
171
|
<span class="drill-label">Links</span>
|
|
@@ -181,7 +184,7 @@
|
|
|
181
184
|
caption={card.caption}
|
|
182
185
|
selectedRow={focusedV}
|
|
183
186
|
selectedCol={focusedS}
|
|
184
|
-
|
|
187
|
+
onselect={handleChartSelect}
|
|
185
188
|
/>
|
|
186
189
|
</div>
|
|
187
190
|
{/if}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
interface Props {
|
|
3
|
+
children?: import('svelte').Snippet;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
let { children }: Props = $props();
|
|
2
7
|
/** Section that gathers render-time toggles a component accepts as props
|
|
3
8
|
(e.g. dismissible, action buttons, hover shimmer, show icons). */
|
|
4
9
|
</script>
|
|
@@ -9,7 +14,7 @@
|
|
|
9
14
|
<p class="config-description">Component accepts multiple properties that can change its layout. Use these to preview.</p>
|
|
10
15
|
</header>
|
|
11
16
|
<div class="config-controls">
|
|
12
|
-
|
|
17
|
+
{@render children?.()}
|
|
13
18
|
</div>
|
|
14
19
|
</section>
|
|
15
20
|
|
|
@@ -1,43 +1,26 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import { run } from 'svelte/legacy';
|
|
3
|
+
|
|
3
4
|
import type { ComponentConfigMeta } from '../../lib/themeTypes';
|
|
4
5
|
import { sanitizeFileName } from '../../lib/themeService';
|
|
5
6
|
import UIDialog from '../../ui/UIDialog.svelte';
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
interface Props {
|
|
9
|
+
/** Two-way bound: parent toggles to open/close. */
|
|
10
|
+
show?: boolean;
|
|
11
|
+
/** Display name to seed the input with when the dialog opens. */
|
|
12
|
+
currentDisplayName?: string;
|
|
13
|
+
/** Existing files used by the increment helper to find the next available `_NN` suffix. */
|
|
14
|
+
files?: ComponentConfigMeta[];
|
|
15
|
+
onsave?: (payload: { displayName: string; fileName: string }) => void;
|
|
16
|
+
}
|
|
13
17
|
|
|
14
|
-
|
|
15
|
-
save: { displayName: string; fileName: string };
|
|
16
|
-
}>();
|
|
18
|
+
let { show = $bindable(false), currentDisplayName = '', files = [], onsave }: Props = $props();
|
|
17
19
|
|
|
18
|
-
let saveAsName = '';
|
|
19
|
-
let saveAsInput: HTMLInputElement;
|
|
20
|
+
let saveAsName = $state('');
|
|
21
|
+
let saveAsInput: HTMLInputElement | undefined = $state();
|
|
20
22
|
|
|
21
|
-
// Seed and select the input whenever the dialog opens. setTimeout(..., 0)
|
|
22
|
-
// matches the original parent's behaviour: it runs as a macrotask, after
|
|
23
|
-
// UIDialog's microtask-queued focus on the confirm button — so the input
|
|
24
|
-
// ends up focused-and-selected, not the button.
|
|
25
|
-
$: if (show) {
|
|
26
|
-
saveAsName =
|
|
27
|
-
sanitizeFileName(currentDisplayName) === 'default'
|
|
28
|
-
? nextIncrementName(currentDisplayName).displayName
|
|
29
|
-
: currentDisplayName;
|
|
30
|
-
setTimeout(() => saveAsInput?.select(), 0);
|
|
31
|
-
}
|
|
32
23
|
|
|
33
|
-
$: saveAsError = (() => {
|
|
34
|
-
const trimmed = saveAsName.trim();
|
|
35
|
-
if (!trimmed) return '';
|
|
36
|
-
if (sanitizeFileName(trimmed) === 'default') {
|
|
37
|
-
return 'The name "default" is reserved for the core component definition.';
|
|
38
|
-
}
|
|
39
|
-
return '';
|
|
40
|
-
})();
|
|
41
24
|
|
|
42
25
|
function nextIncrementName(baseDisplay: string): { displayName: string; fileName: string } {
|
|
43
26
|
const baseName = baseDisplay.replace(/_\d+$/, '');
|
|
@@ -67,12 +50,33 @@
|
|
|
67
50
|
if (!displayName || saveAsError) return;
|
|
68
51
|
const fileName = sanitizeFileName(displayName);
|
|
69
52
|
show = false;
|
|
70
|
-
|
|
53
|
+
onsave?.({ displayName, fileName });
|
|
71
54
|
}
|
|
72
55
|
|
|
73
56
|
function handleKeydown(e: KeyboardEvent) {
|
|
74
57
|
if (e.key === 'Enter') confirmSaveAs();
|
|
75
58
|
}
|
|
59
|
+
// Seed and select the input whenever the dialog opens. setTimeout(..., 0)
|
|
60
|
+
// matches the original parent's behaviour: it runs as a macrotask, after
|
|
61
|
+
// UIDialog's microtask-queued focus on the confirm button — so the input
|
|
62
|
+
// ends up focused-and-selected, not the button.
|
|
63
|
+
run(() => {
|
|
64
|
+
if (show) {
|
|
65
|
+
saveAsName =
|
|
66
|
+
sanitizeFileName(currentDisplayName) === 'default'
|
|
67
|
+
? nextIncrementName(currentDisplayName).displayName
|
|
68
|
+
: currentDisplayName;
|
|
69
|
+
setTimeout(() => saveAsInput?.select(), 0);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
let saveAsError = $derived((() => {
|
|
73
|
+
const trimmed = saveAsName.trim();
|
|
74
|
+
if (!trimmed) return '';
|
|
75
|
+
if (sanitizeFileName(trimmed) === 'default') {
|
|
76
|
+
return 'The name "default" is reserved for the core component definition.';
|
|
77
|
+
}
|
|
78
|
+
return '';
|
|
79
|
+
})());
|
|
76
80
|
</script>
|
|
77
81
|
|
|
78
82
|
<UIDialog
|
|
@@ -81,7 +85,7 @@
|
|
|
81
85
|
cancelLabel="Cancel"
|
|
82
86
|
confirmLabel="Save"
|
|
83
87
|
confirmDisabled={!saveAsName.trim() || !!saveAsError}
|
|
84
|
-
|
|
88
|
+
onconfirm={confirmSaveAs}
|
|
85
89
|
width="360px"
|
|
86
90
|
>
|
|
87
91
|
<div class="save-as-dialog">
|
|
@@ -92,13 +96,13 @@
|
|
|
92
96
|
type="text"
|
|
93
97
|
bind:value={saveAsName}
|
|
94
98
|
bind:this={saveAsInput}
|
|
95
|
-
|
|
99
|
+
onkeydown={handleKeydown}
|
|
96
100
|
placeholder="Config name…"
|
|
97
101
|
/>
|
|
98
102
|
<button
|
|
99
103
|
type="button"
|
|
100
104
|
class="save-as-increment"
|
|
101
|
-
|
|
105
|
+
onclick={incrementSaveAsName}
|
|
102
106
|
title="Increment filename"
|
|
103
107
|
>
|
|
104
108
|
<i class="fas fa-plus"></i>
|
|
@@ -1,20 +1,32 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import newspaperBg from '../../assets/newspaper.webp';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
mode?: 'image' | 'color';
|
|
8
|
+
/** CSS var name (set by ShadowBackdropControls) the backdrop reads when in color mode. */
|
|
9
|
+
colorVariable: string;
|
|
10
|
+
/** Padding around the slotted preview. Set to '0' when the slotted component should cover the full backdrop area (e.g. dialog overlay). */
|
|
11
|
+
padding?: string;
|
|
12
|
+
children?: import('svelte').Snippet;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let {
|
|
16
|
+
mode = 'image',
|
|
17
|
+
colorVariable,
|
|
18
|
+
padding = '128px',
|
|
19
|
+
children
|
|
20
|
+
}: Props = $props();
|
|
9
21
|
|
|
10
|
-
|
|
11
|
-
mode === 'image'
|
|
22
|
+
let backgroundStyle =
|
|
23
|
+
$derived(mode === 'image'
|
|
12
24
|
? `padding: ${padding}; background-image: url(${newspaperBg}); background-size: cover; background-position: center; background-repeat: no-repeat;`
|
|
13
|
-
: `padding: ${padding}; background: var(${colorVariable}, #1a1a1a)
|
|
25
|
+
: `padding: ${padding}; background: var(${colorVariable}, #1a1a1a);`);
|
|
14
26
|
</script>
|
|
15
27
|
|
|
16
28
|
<div class="shadow-backdrop" style={backgroundStyle}>
|
|
17
|
-
|
|
29
|
+
{@render children?.()}
|
|
18
30
|
</div>
|
|
19
31
|
|
|
20
32
|
<style>
|
|
@@ -5,9 +5,14 @@
|
|
|
5
5
|
import { setCssVar } from '../../lib/cssVarSync';
|
|
6
6
|
|
|
7
7
|
type Mode = 'image' | 'color';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
mode?: Mode;
|
|
11
|
+
/** Editor-scoped CSS var the picker writes to (must end with `-surface` to allow gradients). */
|
|
12
|
+
colorVariable: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let { mode = $bindable('image'), colorVariable }: Props = $props();
|
|
11
16
|
|
|
12
17
|
const options: ReadonlyArray<{ value: Mode; label: string }> = [
|
|
13
18
|
{ value: 'image', label: 'Image' },
|
|
@@ -12,21 +12,38 @@
|
|
|
12
12
|
import TypeEditor from './TypeEditor.svelte';
|
|
13
13
|
import type { Token, TypeGroupConfig } from './types';
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
interface Props {
|
|
21
|
+
/** Tokens for this state, fed to `<TokenLayout>`. */
|
|
22
|
+
tokens: Token[];
|
|
23
|
+
/** Type groups for this state; rendered as a row of `<TypeEditor>` blocks. */
|
|
24
|
+
typeGroups?: TypeGroupConfig[];
|
|
25
|
+
/** Forwarded to TypeEditor and TokenLayout so writes persist through the editor store. */
|
|
26
|
+
component?: string | undefined;
|
|
27
|
+
/** Per-variable rank passed through to TokenLayout for linked-block alignment. */
|
|
28
|
+
linkedOrder?: Map<string, number> | undefined;
|
|
29
|
+
/** Render the token grid with N visual columns. >1 spreads a long property
|
|
24
30
|
list horizontally; only meaningful for state-blocks without typeGroups
|
|
25
31
|
(the two-col flex layout already partitions screen real estate when
|
|
26
32
|
typeGroups are present). */
|
|
27
|
-
|
|
33
|
+
columns?: number;
|
|
34
|
+
onchange?: () => void;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let {
|
|
38
|
+
tokens,
|
|
39
|
+
typeGroups = [],
|
|
40
|
+
component = undefined,
|
|
41
|
+
linkedOrder = undefined,
|
|
42
|
+
columns = 1,
|
|
43
|
+
onchange,
|
|
44
|
+
}: Props = $props();
|
|
28
45
|
|
|
29
|
-
|
|
46
|
+
let hasTypeGroups = $derived(typeGroups.length > 0);
|
|
30
47
|
</script>
|
|
31
48
|
|
|
32
49
|
<div class="state-controls" class:two-col={hasTypeGroups}>
|
|
@@ -50,7 +67,7 @@
|
|
|
50
67
|
outlineColorVariable={tg.outlineColorVariable}
|
|
51
68
|
outlineColorLabel={tg.outlineColorLabel ?? 'outline color'}
|
|
52
69
|
{component}
|
|
53
|
-
|
|
70
|
+
{onchange}
|
|
54
71
|
/>
|
|
55
72
|
{/each}
|
|
56
73
|
</div>
|
|
@@ -61,7 +78,7 @@
|
|
|
61
78
|
{component}
|
|
62
79
|
{linkedOrder}
|
|
63
80
|
{columns}
|
|
64
|
-
|
|
81
|
+
{onchange}
|
|
65
82
|
/>
|
|
66
83
|
</div>
|
|
67
84
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import type { Component } from 'svelte';
|
|
3
3
|
import UIPaletteSelector from '../../ui/UIPaletteSelector.svelte';
|
|
4
4
|
import UIVariantSelector from '../../ui/UIVariantSelector.svelte';
|
|
5
5
|
import UIFontFamilySelector from '../../ui/UIFontFamilySelector.svelte';
|
|
@@ -40,21 +40,40 @@
|
|
|
40
40
|
|
|
41
41
|
type Entry = { kind: Kind; token: Token };
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
interface Props {
|
|
49
|
+
title?: string;
|
|
50
|
+
tokens: Token[];
|
|
51
|
+
/** Forwarded to each selector; when set, writes persist through the editor store. */
|
|
52
|
+
component?: string | undefined;
|
|
53
|
+
/** Optional context labels per variable (shown below the selector). */
|
|
54
|
+
contexts?: Record<string, string[]>;
|
|
55
|
+
/** Per-variable rank that overrides kind rank when sorting; lets linked tokens align with the top linked row. */
|
|
56
|
+
linkedOrder?: Map<string, number> | undefined;
|
|
57
|
+
/** Set true on the linked-block instance so dimmed variant rows can scroll/flash to the matching anchor. */
|
|
58
|
+
isLinkedBlock?: boolean;
|
|
59
|
+
/** Number of visual columns. >1 switches to a column-major grid (grid-auto-flow: column)
|
|
54
60
|
so the consumer can spread a long property list across the available width. In
|
|
55
61
|
multi-col mode, the linked-first sort + zone divider are dropped — kind-grouped flow
|
|
56
62
|
reads more naturally when columns themselves carry the visual grouping. */
|
|
57
|
-
|
|
63
|
+
columns?: number;
|
|
64
|
+
onchange?: () => void;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
let {
|
|
68
|
+
title = '',
|
|
69
|
+
tokens,
|
|
70
|
+
component = undefined,
|
|
71
|
+
contexts = {},
|
|
72
|
+
linkedOrder = undefined,
|
|
73
|
+
isLinkedBlock = false,
|
|
74
|
+
columns = 1,
|
|
75
|
+
onchange,
|
|
76
|
+
}: Props = $props();
|
|
58
77
|
|
|
59
78
|
/** Suffix/prefix patterns mapped to kinds — single source of truth used by `categorize`.
|
|
60
79
|
Order matters: `text` must run before `border`/`surface` because `--text-*` would
|
|
@@ -134,7 +153,7 @@
|
|
|
134
153
|
/** Selector registry: one entry per kind. `extra` props (e.g. UIPaddingSelector's
|
|
135
154
|
`mode`/`splittable`/`rowLabel`) are forwarded alongside the linked props. */
|
|
136
155
|
type SelectorEntry = {
|
|
137
|
-
component:
|
|
156
|
+
component: Component<any, any, any>;
|
|
138
157
|
extra?: (token: Token) => Record<string, unknown>;
|
|
139
158
|
/** When true, the row is rendered as a self-contained block (spans all grid columns,
|
|
140
159
|
no .token-row wrapper, no contexts strip). Currently only `padding-split`. */
|
|
@@ -235,8 +254,6 @@
|
|
|
235
254
|
return set;
|
|
236
255
|
}
|
|
237
256
|
|
|
238
|
-
const dispatch = createEventDispatcher();
|
|
239
|
-
|
|
240
257
|
/** When a row collapses several groupKey leads into one display, mirror the lead's
|
|
241
258
|
new alias onto each peer (and its siblings) so the merged display stays in sync. */
|
|
242
259
|
function handleRowChange(token: Token) {
|
|
@@ -248,7 +265,7 @@
|
|
|
248
265
|
else clearComponentAliasLinked(component, peer);
|
|
249
266
|
}
|
|
250
267
|
}
|
|
251
|
-
|
|
268
|
+
onchange?.();
|
|
252
269
|
}
|
|
253
270
|
|
|
254
271
|
/** Bidirectional hover cue with the Linked-properties block. The upper grid
|
|
@@ -263,20 +280,20 @@
|
|
|
263
280
|
hoveredLinkedVariable?.set(variable);
|
|
264
281
|
}
|
|
265
282
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
283
|
+
let hoveredVar = $derived(!isLinkedBlock && hoveredLinkedVariable ? $hoveredLinkedVariable : null);
|
|
284
|
+
let isMultiCol = $derived(columns > 1);
|
|
285
|
+
let linkedKinds = $derived(computeLinkedKinds(component, $editorState));
|
|
286
|
+
let entries = $derived(buildEntries(tokens.filter((t) => !t.hidden), linkedOrder, linkedKinds, component, $editorState, isMultiCol));
|
|
270
287
|
/** Index of the first independent (non-linked) entry; -1 when there are no linked entries or no boundary.
|
|
271
288
|
Suppressed in multi-col mode (no divider — column structure is the grouping). */
|
|
272
|
-
|
|
289
|
+
let firstIndependentIdx = $derived((() => {
|
|
273
290
|
if (isMultiCol) return -1;
|
|
274
291
|
const idx = entries.findIndex((e) => !linkedKinds.has(e.kind));
|
|
275
292
|
if (idx <= 0) return -1;
|
|
276
293
|
return idx;
|
|
277
|
-
})();
|
|
294
|
+
})());
|
|
278
295
|
/** Rows per column for column-major flow; ceil so the last column may be short. */
|
|
279
|
-
|
|
296
|
+
let rowsPerCol = $derived(isMultiCol ? Math.max(1, Math.ceil(entries.length / columns)) : entries.length);
|
|
280
297
|
|
|
281
298
|
</script>
|
|
282
299
|
|
|
@@ -320,24 +337,23 @@
|
|
|
320
337
|
-->
|
|
321
338
|
{@const isLinkedRow = linkedKinds.has(entry.kind)}
|
|
322
339
|
{@const isHovered = !isLinkedBlock && isLinkedRow && hoveredVar === token.variable}
|
|
323
|
-
<!-- svelte-ignore
|
|
340
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
324
341
|
<div
|
|
325
342
|
class="token-entry"
|
|
326
343
|
class:token-row={!sel.standalone}
|
|
327
344
|
class:has-contexts={!sel.standalone && !!ctxs?.length}
|
|
328
345
|
class:linked-hovered={isHovered}
|
|
329
346
|
class:non-first-set={isNonFirstSet}
|
|
330
|
-
|
|
331
|
-
|
|
347
|
+
onmouseenter={isLinkedBlock || !isLinkedRow ? undefined : () => setHover(token.variable)}
|
|
348
|
+
onmouseleave={isLinkedBlock || !isLinkedRow ? undefined : () => setHover(null)}
|
|
332
349
|
>
|
|
333
350
|
{#if !sel.standalone}
|
|
334
351
|
<span class="token-label">{token.label}</span>
|
|
335
352
|
{/if}
|
|
336
|
-
<
|
|
337
|
-
this={sel.component}
|
|
353
|
+
<sel.component
|
|
338
354
|
{...sharedProps}
|
|
339
355
|
{...extra}
|
|
340
|
-
|
|
356
|
+
onchange={() => handleRowChange(token)}
|
|
341
357
|
/>
|
|
342
358
|
{#if !sel.standalone && ctxs?.length}
|
|
343
359
|
<div class="token-contexts">
|
|
@@ -8,57 +8,83 @@
|
|
|
8
8
|
import FieldsetWrapper from './FieldsetWrapper.svelte';
|
|
9
9
|
import { BORDER_WIDTH } from '../../ui/variantScales';
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
interface Props {
|
|
15
|
+
colorVariable: string;
|
|
16
|
+
colorLabel?: string;
|
|
17
|
+
familyVariable?: string | undefined;
|
|
18
|
+
familyLabel?: string;
|
|
19
|
+
sizeVariable?: string | undefined;
|
|
20
|
+
sizeLabel?: string;
|
|
21
|
+
weightVariable?: string | undefined;
|
|
22
|
+
weightLabel?: string;
|
|
23
|
+
lineHeightVariable?: string | undefined;
|
|
24
|
+
lineHeightLabel?: string;
|
|
25
|
+
/** Optional outline rows rendered under the typography rows so a text-with-
|
|
22
26
|
stroke group keeps stroke controls visually nested with the type they
|
|
23
27
|
drive (e.g. SectionDivider title outline). */
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
outlineWidthVariable?: string | undefined;
|
|
29
|
+
outlineWidthLabel?: string;
|
|
30
|
+
outlineColorVariable?: string | undefined;
|
|
31
|
+
outlineColorLabel?: string;
|
|
32
|
+
/** When set, writes persist through the editor store under this component. */
|
|
33
|
+
component?: string | undefined;
|
|
34
|
+
/** Legend text for the fieldset. */
|
|
35
|
+
legend?: string;
|
|
36
|
+
onchange?: () => void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let {
|
|
40
|
+
colorVariable,
|
|
41
|
+
colorLabel = 'color',
|
|
42
|
+
familyVariable = undefined,
|
|
43
|
+
familyLabel = 'family',
|
|
44
|
+
sizeVariable = undefined,
|
|
45
|
+
sizeLabel = 'size',
|
|
46
|
+
weightVariable = undefined,
|
|
47
|
+
weightLabel = 'weight',
|
|
48
|
+
lineHeightVariable = undefined,
|
|
49
|
+
lineHeightLabel = 'line-h',
|
|
50
|
+
outlineWidthVariable = undefined,
|
|
51
|
+
outlineWidthLabel = 'outline thickness',
|
|
52
|
+
outlineColorVariable = undefined,
|
|
53
|
+
outlineColorLabel = 'outline color',
|
|
54
|
+
component = undefined,
|
|
55
|
+
legend = 'type',
|
|
56
|
+
onchange,
|
|
57
|
+
}: Props = $props();
|
|
32
58
|
</script>
|
|
33
59
|
|
|
34
60
|
<FieldsetWrapper {legend}>
|
|
35
61
|
<div class="type-grid">
|
|
36
62
|
<span class="row-label">{colorLabel}</span>
|
|
37
|
-
<UIPaletteSelector variable={colorVariable} {component}
|
|
63
|
+
<UIPaletteSelector variable={colorVariable} {component} {onchange} />
|
|
38
64
|
|
|
39
65
|
{#if familyVariable}
|
|
40
66
|
<span class="row-label">{familyLabel}</span>
|
|
41
|
-
<UIFontFamilySelector variable={familyVariable} {component} canBeLinked
|
|
67
|
+
<UIFontFamilySelector variable={familyVariable} {component} canBeLinked {onchange} />
|
|
42
68
|
{/if}
|
|
43
69
|
{#if weightVariable}
|
|
44
70
|
<span class="row-label">{weightLabel}</span>
|
|
45
|
-
<UIFontWeightSelector variable={weightVariable} {component} canBeLinked
|
|
71
|
+
<UIFontWeightSelector variable={weightVariable} {component} canBeLinked {onchange} />
|
|
46
72
|
{/if}
|
|
47
73
|
{#if sizeVariable}
|
|
48
74
|
<span class="row-label">{sizeLabel}</span>
|
|
49
|
-
<UIFontSizeSelector variable={sizeVariable} {component} canBeLinked
|
|
75
|
+
<UIFontSizeSelector variable={sizeVariable} {component} canBeLinked {onchange} />
|
|
50
76
|
{/if}
|
|
51
77
|
{#if lineHeightVariable}
|
|
52
78
|
<span class="row-label">{lineHeightLabel}</span>
|
|
53
|
-
<UILineHeightSelector variable={lineHeightVariable} {component} canBeLinked
|
|
79
|
+
<UILineHeightSelector variable={lineHeightVariable} {component} canBeLinked {onchange} />
|
|
54
80
|
{/if}
|
|
55
81
|
{#if outlineWidthVariable}
|
|
56
82
|
<span class="row-label">{outlineWidthLabel}</span>
|
|
57
|
-
<UIVariantSelector variable={outlineWidthVariable} {component} canBeLinked {...BORDER_WIDTH}
|
|
83
|
+
<UIVariantSelector variable={outlineWidthVariable} {component} canBeLinked {...BORDER_WIDTH} {onchange} />
|
|
58
84
|
{/if}
|
|
59
85
|
{#if outlineColorVariable}
|
|
60
86
|
<span class="row-label">{outlineColorLabel}</span>
|
|
61
|
-
<UIPaletteSelector variable={outlineColorVariable} {component} canBeLinked
|
|
87
|
+
<UIPaletteSelector variable={outlineColorVariable} {component} canBeLinked {onchange} />
|
|
62
88
|
{/if}
|
|
63
89
|
</div>
|
|
64
90
|
</FieldsetWrapper>
|