@motion-proto/live-tokens 0.3.7 → 0.5.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 +1 -1
- package/package.json +11 -9
- 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 +260 -37
- 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 +73 -36
- package/src/pages/ComponentEditorPage.svelte +17 -13
- package/src/pages/EditorShell.svelte +24 -20
- package/src/styles/form-controls.css +2 -2
- package/src/styles/tokens.css +59 -81
- 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 +16 -15
- 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 +30 -30
- 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
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import {
|
|
2
|
+
import { run, createBubbler, stopPropagation } from 'svelte/legacy';
|
|
3
|
+
|
|
4
|
+
const bubble = createBubbler();
|
|
5
|
+
import { onMount, onDestroy } from 'svelte';
|
|
3
6
|
import UIRadio from './UIRadio.svelte';
|
|
4
7
|
import { keepInViewport } from './keepInViewport';
|
|
5
8
|
|
|
6
9
|
type Candidate = { variable: string; alias: string };
|
|
7
10
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
interface Props {
|
|
12
|
+
candidates: Candidate[];
|
|
13
|
+
/** Variable whose lock was clicked — its alias is the default selection. */
|
|
14
|
+
initialVariable: string;
|
|
15
|
+
/** Component prefix to strip when rendering source labels. */
|
|
16
|
+
prefixToStrip?: string;
|
|
17
|
+
onconfirm?: (payload: { alias: string }) => void;
|
|
18
|
+
oncancel?: () => void;
|
|
19
|
+
}
|
|
13
20
|
|
|
14
|
-
|
|
21
|
+
let { candidates, initialVariable, prefixToStrip = '', onconfirm, oncancel }: Props = $props();
|
|
15
22
|
|
|
16
23
|
type Option = { alias: string; sources: string[] };
|
|
17
24
|
|
|
@@ -31,17 +38,19 @@
|
|
|
31
38
|
return [...seen.values()];
|
|
32
39
|
}
|
|
33
40
|
|
|
34
|
-
|
|
35
|
-
let selected = '';
|
|
36
|
-
|
|
41
|
+
let options = $derived(distinctOptions(candidates, initialVariable));
|
|
42
|
+
let selected = $state('');
|
|
43
|
+
run(() => {
|
|
44
|
+
if (selected === '' && options.length > 0) selected = options[0].alias;
|
|
45
|
+
});
|
|
37
46
|
|
|
38
47
|
function handleConfirm() {
|
|
39
48
|
if (!selected) return;
|
|
40
|
-
|
|
49
|
+
onconfirm?.({ alias: selected });
|
|
41
50
|
}
|
|
42
51
|
|
|
43
52
|
function handleCancel() {
|
|
44
|
-
|
|
53
|
+
oncancel?.();
|
|
45
54
|
}
|
|
46
55
|
|
|
47
56
|
function handleKeydown(e: KeyboardEvent) {
|
|
@@ -55,7 +64,7 @@
|
|
|
55
64
|
}
|
|
56
65
|
}
|
|
57
66
|
|
|
58
|
-
let popoverEl: HTMLDivElement;
|
|
67
|
+
let popoverEl: HTMLDivElement | undefined = $state();
|
|
59
68
|
|
|
60
69
|
onMount(() => {
|
|
61
70
|
popoverEl?.focus();
|
|
@@ -67,6 +76,7 @@
|
|
|
67
76
|
});
|
|
68
77
|
</script>
|
|
69
78
|
|
|
79
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
70
80
|
<div
|
|
71
81
|
class="ui-relink-popover"
|
|
72
82
|
role="dialog"
|
|
@@ -74,7 +84,7 @@
|
|
|
74
84
|
tabindex="-1"
|
|
75
85
|
bind:this={popoverEl}
|
|
76
86
|
use:keepInViewport
|
|
77
|
-
|
|
87
|
+
onclick={stopPropagation(bubble('click'))}
|
|
78
88
|
>
|
|
79
89
|
<div class="ui-relink-header">
|
|
80
90
|
{#if options.length > 1}
|
|
@@ -99,8 +109,8 @@
|
|
|
99
109
|
</div>
|
|
100
110
|
|
|
101
111
|
<div class="ui-relink-footer">
|
|
102
|
-
<button type="button" class="ui-relink-btn ui-relink-btn-cancel"
|
|
103
|
-
<button type="button" class="ui-relink-btn ui-relink-btn-confirm"
|
|
112
|
+
<button type="button" class="ui-relink-btn ui-relink-btn-cancel" onclick={handleCancel}>Cancel</button>
|
|
113
|
+
<button type="button" class="ui-relink-btn ui-relink-btn-confirm" onclick={handleConfirm}>Link</button>
|
|
104
114
|
</div>
|
|
105
115
|
</div>
|
|
106
116
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { onMount, onDestroy
|
|
2
|
+
import { onMount, onDestroy } from 'svelte';
|
|
3
|
+
import type { Snippet } from 'svelte';
|
|
3
4
|
import { setCssVar, removeCssVar, CSS_VAR_CHANGE_EVENT } from '../lib/cssVarSync';
|
|
4
5
|
import type { CssVarRef } from '../lib/editorTypes';
|
|
5
6
|
import {
|
|
@@ -17,47 +18,85 @@
|
|
|
17
18
|
import UIRelinkConfirmPopover from './UIRelinkConfirmPopover.svelte';
|
|
18
19
|
import { keepInViewport } from './keepInViewport';
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
21
|
+
type DropdownContext = { close: () => void; handleReset: () => void };
|
|
22
|
+
|
|
23
|
+
interface Props {
|
|
24
|
+
variable: string;
|
|
25
|
+
/** When set, writes persist through the editor store under this component. */
|
|
26
|
+
component?: string | undefined;
|
|
27
|
+
/** When true, render a link toggle that lets the user share this value across all sibling variants. */
|
|
28
|
+
canBeLinked?: boolean;
|
|
29
|
+
/** Minimum width of the dropdown panel. */
|
|
30
|
+
dropdownMinWidth?: string;
|
|
31
|
+
/** Max width of the dropdown panel (useful for grids). */
|
|
32
|
+
dropdownMaxWidth?: string;
|
|
33
|
+
/** When true, the default dropdown header (variable name + reset) is omitted. */
|
|
34
|
+
hideDefaultHeader?: boolean;
|
|
35
|
+
/** When true, the trigger becomes non-interactive and visually dimmed. */
|
|
36
|
+
disabled?: boolean;
|
|
37
|
+
/** When true, the trigger opens normally but the dropdown's selection area is
|
|
38
|
+
* dimmed and non-interactive. The lock toggle in the header stays active so
|
|
39
|
+
* the user can re-engage editing by re-linking. Used by the linked block to
|
|
40
|
+
* make the row openable even when currently unshared. */
|
|
41
|
+
selectionsLocked?: boolean;
|
|
42
|
+
/** Optional preview rendered before the trigger text (e.g. swatch). Renamed from `slot="trigger-preview"` in 0.5.0. */
|
|
43
|
+
triggerPreview?: Snippet;
|
|
44
|
+
/** Optional full trigger text replacement. Falls back to `triggerTitle`. Renamed from `slot="trigger-text"` in 0.5.0. */
|
|
45
|
+
triggerText?: Snippet;
|
|
46
|
+
/** Optional trigger title text. Renamed from `slot="trigger-title"` in 0.5.0. */
|
|
47
|
+
triggerTitle?: Snippet;
|
|
48
|
+
/** Optional meta text rendered after the trigger. Renamed from `slot="trigger-meta"` in 0.5.0. */
|
|
49
|
+
triggerMeta?: Snippet;
|
|
50
|
+
/** Optional dropdown header replacement. */
|
|
51
|
+
header?: Snippet;
|
|
52
|
+
/** Optional dropdown content above the selections. */
|
|
53
|
+
subheader?: Snippet;
|
|
54
|
+
/** Dropdown body content; receives `{ close, handleReset }`. */
|
|
55
|
+
children?: Snippet<[DropdownContext]>;
|
|
56
|
+
onchange?: () => void;
|
|
57
|
+
onreset?: () => void;
|
|
58
|
+
onopen?: () => void;
|
|
59
|
+
onclose?: () => void;
|
|
60
|
+
onvarChange?: () => void;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
let {
|
|
64
|
+
variable,
|
|
65
|
+
component = undefined,
|
|
66
|
+
canBeLinked = false,
|
|
67
|
+
dropdownMinWidth = '14rem',
|
|
68
|
+
dropdownMaxWidth = '',
|
|
69
|
+
hideDefaultHeader = false,
|
|
70
|
+
disabled = false,
|
|
71
|
+
selectionsLocked = false,
|
|
72
|
+
triggerPreview,
|
|
73
|
+
triggerText,
|
|
74
|
+
triggerTitle,
|
|
75
|
+
triggerMeta,
|
|
76
|
+
header,
|
|
77
|
+
subheader,
|
|
78
|
+
children,
|
|
79
|
+
onchange,
|
|
80
|
+
onreset,
|
|
81
|
+
onopen,
|
|
82
|
+
onclose,
|
|
83
|
+
onvarChange,
|
|
84
|
+
}: Props = $props();
|
|
85
|
+
|
|
86
|
+
let open = $state(false);
|
|
48
87
|
let container: HTMLElement;
|
|
49
|
-
let relinkOpen = false;
|
|
50
|
-
let relinkCandidates: { variable: string; alias: string }[] = [];
|
|
88
|
+
let relinkOpen = $state(false);
|
|
89
|
+
let relinkCandidates: { variable: string; alias: string }[] = $state([]);
|
|
51
90
|
|
|
52
|
-
|
|
91
|
+
let isLinkedFromData = $derived(canBeLinked && component && $editorState
|
|
53
92
|
? isComponentPropertyLinked(component, variable)
|
|
54
|
-
: false;
|
|
55
|
-
|
|
56
|
-
|
|
93
|
+
: false);
|
|
94
|
+
let isLinkedDisplay = $derived(canBeLinked && !!component && isLinkedFromData);
|
|
95
|
+
let peerCount = $derived(canBeLinked && component && $editorState
|
|
57
96
|
? getComponentPropertySiblings(component, variable).length
|
|
58
|
-
: 0;
|
|
59
|
-
|
|
60
|
-
|
|
97
|
+
: 0);
|
|
98
|
+
let hasSiblings = $derived(peerCount >= 2);
|
|
99
|
+
let showLinkToggle = $derived(canBeLinked && !!component && hasSiblings);
|
|
61
100
|
|
|
62
101
|
/** Persist a semantic CSS-var reference (or clear it when null). */
|
|
63
102
|
export function writeOverride(semanticName: string | null): void {
|
|
@@ -90,22 +129,25 @@
|
|
|
90
129
|
export function close() {
|
|
91
130
|
if (!open) return;
|
|
92
131
|
open = false;
|
|
93
|
-
|
|
132
|
+
onclose?.();
|
|
94
133
|
}
|
|
95
134
|
|
|
96
135
|
function toggle() {
|
|
97
136
|
if (disabled) return;
|
|
98
137
|
open = !open;
|
|
99
|
-
|
|
138
|
+
if (open) onopen?.();
|
|
139
|
+
else onclose?.();
|
|
100
140
|
}
|
|
101
141
|
|
|
102
|
-
|
|
142
|
+
$effect(() => {
|
|
143
|
+
if (disabled && open) close();
|
|
144
|
+
});
|
|
103
145
|
|
|
104
146
|
function toggleLinked() {
|
|
105
147
|
if (!canBeLinked || !component) return;
|
|
106
148
|
if (isLinkedDisplay) {
|
|
107
149
|
unlinkComponentProperty(component, variable);
|
|
108
|
-
|
|
150
|
+
onchange?.();
|
|
109
151
|
return;
|
|
110
152
|
}
|
|
111
153
|
const slice = $editorState.components[component];
|
|
@@ -126,7 +168,7 @@
|
|
|
126
168
|
const currentValue = slice.aliases[variable];
|
|
127
169
|
if (currentValue) {
|
|
128
170
|
setComponentAliasLinked(component, variable, currentValue);
|
|
129
|
-
|
|
171
|
+
onchange?.();
|
|
130
172
|
}
|
|
131
173
|
return;
|
|
132
174
|
}
|
|
@@ -150,7 +192,7 @@
|
|
|
150
192
|
: slice.aliases[variable];
|
|
151
193
|
if (adoptRef) setComponentAliasLinked(component, variable, adoptRef);
|
|
152
194
|
else relinkComponentProperty(component, variable);
|
|
153
|
-
|
|
195
|
+
onchange?.();
|
|
154
196
|
return;
|
|
155
197
|
}
|
|
156
198
|
|
|
@@ -158,10 +200,10 @@
|
|
|
158
200
|
relinkOpen = true;
|
|
159
201
|
}
|
|
160
202
|
|
|
161
|
-
function handleRelinkConfirm(
|
|
203
|
+
function handleRelinkConfirm(payload: { alias: string }) {
|
|
162
204
|
if (!component) return;
|
|
163
|
-
setComponentAliasLinked(component, variable, { kind: 'token', name:
|
|
164
|
-
|
|
205
|
+
setComponentAliasLinked(component, variable, { kind: 'token', name: payload.alias });
|
|
206
|
+
onchange?.();
|
|
165
207
|
relinkOpen = false;
|
|
166
208
|
}
|
|
167
209
|
|
|
@@ -182,10 +224,10 @@
|
|
|
182
224
|
// Per-peer resets while preserving the link are impossible by definition
|
|
183
225
|
// (linked = peers share one value); a "reset just this one" intent is
|
|
184
226
|
// really "unlink, then reset," which the user does in two visible steps.
|
|
185
|
-
|
|
227
|
+
onreset?.();
|
|
186
228
|
writeOverride(null);
|
|
187
229
|
close();
|
|
188
|
-
|
|
230
|
+
onchange?.();
|
|
189
231
|
}
|
|
190
232
|
|
|
191
233
|
function handleClickOutside(e: MouseEvent) {
|
|
@@ -197,7 +239,7 @@
|
|
|
197
239
|
|
|
198
240
|
function handleVarChange(e: Event) {
|
|
199
241
|
const detail = (e as CustomEvent<{ name: string }>).detail;
|
|
200
|
-
if (detail?.name === variable)
|
|
242
|
+
if (detail?.name === variable) onvarChange?.();
|
|
201
243
|
}
|
|
202
244
|
|
|
203
245
|
onMount(() => {
|
|
@@ -217,21 +259,21 @@
|
|
|
217
259
|
class="ui-ts-trigger"
|
|
218
260
|
class:linked={isLinkedDisplay}
|
|
219
261
|
class:unlinked={showLinkToggle && !isLinkedDisplay}
|
|
220
|
-
|
|
262
|
+
onclick={toggle}
|
|
221
263
|
{disabled}
|
|
222
264
|
>
|
|
223
265
|
<div class="ui-ts-content">
|
|
224
|
-
{#if
|
|
266
|
+
{#if triggerPreview}
|
|
225
267
|
<div class="ui-ts-preview">
|
|
226
|
-
|
|
268
|
+
{@render triggerPreview()}
|
|
227
269
|
</div>
|
|
228
270
|
{/if}
|
|
229
271
|
<div class="ui-ts-text">
|
|
230
|
-
|
|
231
|
-
{
|
|
232
|
-
|
|
233
|
-
{
|
|
234
|
-
|
|
272
|
+
{#if triggerText}
|
|
273
|
+
{@render triggerText()}
|
|
274
|
+
{:else if triggerTitle}
|
|
275
|
+
<span class="ui-ts-category">{@render triggerTitle()}</span>
|
|
276
|
+
{/if}
|
|
235
277
|
</div>
|
|
236
278
|
</div>
|
|
237
279
|
<i class="fas fa-chevron-down ui-ts-chevron" class:open></i>
|
|
@@ -242,8 +284,8 @@
|
|
|
242
284
|
candidates={relinkCandidates}
|
|
243
285
|
initialVariable={variable}
|
|
244
286
|
prefixToStrip={`--${component}-`}
|
|
245
|
-
|
|
246
|
-
|
|
287
|
+
onconfirm={handleRelinkConfirm}
|
|
288
|
+
oncancel={handleRelinkCancel}
|
|
247
289
|
/>
|
|
248
290
|
{/if}
|
|
249
291
|
|
|
@@ -254,15 +296,17 @@
|
|
|
254
296
|
use:keepInViewport
|
|
255
297
|
>
|
|
256
298
|
{#if !hideDefaultHeader}
|
|
257
|
-
|
|
299
|
+
{#if header}
|
|
300
|
+
{@render header()}
|
|
301
|
+
{:else}
|
|
258
302
|
<div class="ui-ts-header">
|
|
259
303
|
{#if showLinkToggle}
|
|
260
|
-
<UILinkToggle linked={isLinkedDisplay}
|
|
304
|
+
<UILinkToggle linked={isLinkedDisplay} ontoggle={toggleLinked} />
|
|
261
305
|
{/if}
|
|
262
306
|
<button
|
|
263
307
|
type="button"
|
|
264
308
|
class="ui-ts-reset"
|
|
265
|
-
|
|
309
|
+
onclick={handleReset}
|
|
266
310
|
disabled={selectionsLocked}
|
|
267
311
|
title={selectionsLocked ? 'Unlock to reset' : 'Reset to default'}
|
|
268
312
|
>
|
|
@@ -270,18 +314,18 @@
|
|
|
270
314
|
<span>Reset</span>
|
|
271
315
|
</button>
|
|
272
316
|
</div>
|
|
273
|
-
|
|
317
|
+
{/if}
|
|
274
318
|
{/if}
|
|
275
319
|
<div class="ui-ts-selections" class:locked={selectionsLocked}>
|
|
276
|
-
|
|
277
|
-
|
|
320
|
+
{@render subheader?.()}
|
|
321
|
+
{@render children?.({ close, handleReset })}
|
|
278
322
|
</div>
|
|
279
323
|
</div>
|
|
280
324
|
{/if}
|
|
281
325
|
</div>
|
|
282
326
|
|
|
283
|
-
{#if
|
|
284
|
-
<span class="ui-ts-meta-text"
|
|
327
|
+
{#if triggerMeta}
|
|
328
|
+
<span class="ui-ts-meta-text">{@render triggerMeta()}</span>
|
|
285
329
|
{/if}
|
|
286
330
|
</div>
|
|
287
331
|
|
|
@@ -1,37 +1,62 @@
|
|
|
1
1
|
<script lang="ts" generics="T extends { key: string; label?: string; value?: string }">
|
|
2
|
-
import {
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
3
|
import { resolveAliasChain } from '../lib/tokenRegistry';
|
|
4
4
|
import UITokenSelector from './UITokenSelector.svelte';
|
|
5
5
|
import UIOptionList from './UIOptionList.svelte';
|
|
6
6
|
import UIOptionItem from './UIOptionItem.svelte';
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
interface Props<O extends { key: string; label?: string; value?: string }> {
|
|
9
|
+
variable: string;
|
|
10
|
+
component?: string | undefined;
|
|
11
|
+
canBeLinked?: boolean;
|
|
12
|
+
disabled?: boolean;
|
|
13
|
+
selectionsLocked?: boolean;
|
|
14
|
+
dropdownMinWidth?: string;
|
|
15
|
+
dropdownMaxWidth?: string;
|
|
16
|
+
/** Forwarded to UIOptionList — when set, options render in a linked-column grid. */
|
|
17
|
+
dropdownGridColumns?: string;
|
|
18
|
+
/** CSS var prefix that, joined with an option `key`, forms the target var (e.g. `--font-weight-`). */
|
|
19
|
+
varPrefix: string;
|
|
20
|
+
/** Selectable options. Each must have a unique `key`. */
|
|
21
|
+
options: ReadonlyArray<O>;
|
|
22
|
+
/** Trigger title slot. Renamed from `slot="trigger-title"` in 0.5.0. */
|
|
23
|
+
triggerTitle?: Snippet<[{ activeOption: O | null }]>;
|
|
24
|
+
/** Trigger meta slot. Renamed from `slot="trigger-meta"` in 0.5.0. */
|
|
25
|
+
triggerMeta?: Snippet<[{ currentValue: string; activeOption: O | null }]>;
|
|
26
|
+
/** Per-option custom render. */
|
|
27
|
+
option?: Snippet<[{ opt: O; active: boolean; select: () => void }]>;
|
|
28
|
+
/** Trailing dropdown content (e.g. action buttons). */
|
|
29
|
+
extras?: Snippet<[{ close: () => void }]>;
|
|
30
|
+
onchange?: () => void;
|
|
31
|
+
}
|
|
11
32
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
33
|
+
let {
|
|
34
|
+
variable,
|
|
35
|
+
component = undefined,
|
|
36
|
+
canBeLinked = false,
|
|
37
|
+
disabled = false,
|
|
38
|
+
selectionsLocked = false,
|
|
39
|
+
dropdownMinWidth = '12rem',
|
|
40
|
+
dropdownMaxWidth = '',
|
|
41
|
+
dropdownGridColumns = '',
|
|
42
|
+
varPrefix,
|
|
43
|
+
options,
|
|
44
|
+
triggerTitle: callerTriggerTitle,
|
|
45
|
+
triggerMeta: callerTriggerMeta,
|
|
46
|
+
option: callerOption,
|
|
47
|
+
extras: callerExtras,
|
|
48
|
+
onchange,
|
|
49
|
+
}: Props<T> = $props();
|
|
25
50
|
|
|
26
51
|
let selector: UITokenSelector;
|
|
27
|
-
let chosenKey: string | null = null;
|
|
28
|
-
let currentValue: string = '';
|
|
52
|
+
let chosenKey: string | null = $state(null);
|
|
53
|
+
let currentValue: string = $state('');
|
|
29
54
|
|
|
30
|
-
|
|
31
|
-
|
|
55
|
+
let validKeys = $derived(new Set(options.map((o) => o.key)));
|
|
56
|
+
let refMatcher = $derived.by(() => {
|
|
32
57
|
const escaped = varPrefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
33
58
|
return new RegExp(`var\\((${escaped}[^)\\s]+)\\)`);
|
|
34
|
-
})
|
|
59
|
+
});
|
|
35
60
|
|
|
36
61
|
function parseRef(value: string): string | null {
|
|
37
62
|
const m = value.match(refMatcher);
|
|
@@ -70,7 +95,7 @@
|
|
|
70
95
|
function handleReset() {
|
|
71
96
|
chosenKey = null;
|
|
72
97
|
readResolved();
|
|
73
|
-
|
|
98
|
+
onchange?.();
|
|
74
99
|
}
|
|
75
100
|
|
|
76
101
|
function selectKey(key: string, close: () => void) {
|
|
@@ -84,19 +109,21 @@
|
|
|
84
109
|
}
|
|
85
110
|
readResolved();
|
|
86
111
|
close();
|
|
87
|
-
|
|
112
|
+
onchange?.();
|
|
88
113
|
}
|
|
89
114
|
|
|
90
115
|
// Re-derive `chosenKey` when the bound `variable` changes (e.g. when a
|
|
91
116
|
// VariantGroup tabs view reuses the same selector across states). Without
|
|
92
117
|
// this, prop swaps leave the trigger label stale.
|
|
93
118
|
let lastSeenVariable: string | null = null;
|
|
94
|
-
|
|
95
|
-
lastSeenVariable
|
|
96
|
-
|
|
97
|
-
|
|
119
|
+
$effect(() => {
|
|
120
|
+
if (variable !== lastSeenVariable) {
|
|
121
|
+
lastSeenVariable = variable;
|
|
122
|
+
initFromCurrent();
|
|
123
|
+
}
|
|
124
|
+
});
|
|
98
125
|
|
|
99
|
-
|
|
126
|
+
let activeOption = $derived((options.find((o) => o.key === chosenKey) ?? null) as T | null);
|
|
100
127
|
</script>
|
|
101
128
|
|
|
102
129
|
<UITokenSelector
|
|
@@ -108,38 +135,33 @@
|
|
|
108
135
|
{selectionsLocked}
|
|
109
136
|
{dropdownMinWidth}
|
|
110
137
|
{dropdownMaxWidth}
|
|
111
|
-
|
|
112
|
-
|
|
138
|
+
onreset={handleReset}
|
|
139
|
+
onvarChange={initFromCurrent}
|
|
113
140
|
>
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
141
|
+
{#snippet triggerTitle()}
|
|
142
|
+
{#if callerTriggerTitle}{@render callerTriggerTitle({ activeOption })}{:else}{activeOption?.label ?? ''}{/if}
|
|
143
|
+
{/snippet}
|
|
144
|
+
{#snippet triggerMeta()}
|
|
145
|
+
{#if callerTriggerMeta}{@render callerTriggerMeta({ currentValue, activeOption })}{:else}{currentValue || '—'}{/if}
|
|
146
|
+
{/snippet}
|
|
120
147
|
|
|
121
|
-
|
|
148
|
+
{#snippet children({ close })}
|
|
122
149
|
<UIOptionList gridColumns={dropdownGridColumns}>
|
|
123
150
|
{#each options as opt (opt.key)}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
active={chosenKey === opt.key}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
<UIOptionItem active={chosenKey === opt.key} on:click={() => selectKey(opt.key, close)}>
|
|
137
|
-
<svelte:fragment slot="label">{opt.label ?? ''}</svelte:fragment>
|
|
138
|
-
</UIOptionItem>
|
|
139
|
-
{/if}
|
|
140
|
-
</slot>
|
|
151
|
+
{#if callerOption}
|
|
152
|
+
{@render callerOption({ opt, active: chosenKey === opt.key, select: () => selectKey(opt.key, close) })}
|
|
153
|
+
{:else if opt.value !== undefined}
|
|
154
|
+
<UIOptionItem active={chosenKey === opt.key} onclick={() => selectKey(opt.key, close)}>
|
|
155
|
+
{#snippet label()}{opt.label ?? ''}{/snippet}
|
|
156
|
+
{#snippet meta()}{opt.value}{/snippet}
|
|
157
|
+
</UIOptionItem>
|
|
158
|
+
{:else}
|
|
159
|
+
<UIOptionItem active={chosenKey === opt.key} onclick={() => selectKey(opt.key, close)}>
|
|
160
|
+
{#snippet label()}{opt.label ?? ''}{/snippet}
|
|
161
|
+
</UIOptionItem>
|
|
162
|
+
{/if}
|
|
141
163
|
{/each}
|
|
142
|
-
|
|
164
|
+
{@render callerExtras?.({ close })}
|
|
143
165
|
</UIOptionList>
|
|
144
|
-
|
|
166
|
+
{/snippet}
|
|
145
167
|
</UITokenSelector>
|