@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
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* captures coherent edits. The store's subscriber fans values to :root
|
|
13
13
|
* via cssVarSync.
|
|
14
14
|
*/
|
|
15
|
-
import { onMount
|
|
15
|
+
import { onMount } from 'svelte';
|
|
16
16
|
import {
|
|
17
17
|
editorState, mutate, beginScope, commitScope, beginSliderGesture,
|
|
18
18
|
seedShadowsFromDom, shadowTokenCss, computeShadowXY,
|
|
@@ -21,10 +21,14 @@
|
|
|
21
21
|
} from '../../lib/editorStore';
|
|
22
22
|
import type { ShadowToken, ShadowOverrideFlags, EditorState } from '../../lib/editorTypes';
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
interface Props {
|
|
25
|
+
copiedVar?: string | null;
|
|
26
|
+
oncopy?: (variable: string) => void;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let { copiedVar = null, oncopy }: Props = $props();
|
|
25
30
|
|
|
26
|
-
|
|
27
|
-
function copy(v: string) { dispatch('copy', v); }
|
|
31
|
+
function copy(v: string) { oncopy?.(v); }
|
|
28
32
|
|
|
29
33
|
function getShadowOverride(s: EditorState, variable: string): ShadowOverrideFlags {
|
|
30
34
|
let ov = s.shadows.overrides[variable];
|
|
@@ -221,19 +225,19 @@
|
|
|
221
225
|
}
|
|
222
226
|
|
|
223
227
|
// HSL gradient helpers (read from globals for their background-color cues).
|
|
224
|
-
|
|
225
|
-
|
|
228
|
+
let sg = $derived($editorState.shadows.globals);
|
|
229
|
+
let shadowHueGrad = $derived(`linear-gradient(to right, ${
|
|
226
230
|
[0, 60, 120, 180, 240, 300, 360].map(h => `hsl(${h},${sg.saturation}%,${sg.lightness}%)`).join(',')
|
|
227
|
-
})
|
|
228
|
-
|
|
229
|
-
|
|
231
|
+
})`);
|
|
232
|
+
let shadowSatGrad = $derived(`linear-gradient(to right, hsl(${sg.hue},0%,${sg.lightness}%), hsl(${sg.hue},100%,${sg.lightness}%))`);
|
|
233
|
+
let shadowLightGrad = $derived(`linear-gradient(to right, hsl(${sg.hue},${sg.saturation}%,0%), hsl(${sg.hue},${sg.saturation}%,50%), hsl(${sg.hue},${sg.saturation}%,100%))`);
|
|
230
234
|
|
|
231
|
-
let editingShadow: string | null = null;
|
|
235
|
+
let editingShadow: string | null = $state(null);
|
|
232
236
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
+
let shadowTokens = $derived($editorState.shadows.tokens);
|
|
238
|
+
let editingToken = $derived(editingShadow ? shadowTokens.find(t => t.variable === editingShadow) ?? null : null);
|
|
239
|
+
let editingIdx = $derived(editingToken ? shadowTokens.indexOf(editingToken) : -1);
|
|
240
|
+
let editingIsScale = $derived(editingToken ? SCALE_SHADOW_VARIABLES.has(editingToken.variable) : false);
|
|
237
241
|
|
|
238
242
|
function angleFromPointer(event: PointerEvent, dialEl: SVGSVGElement): number {
|
|
239
243
|
const rect = dialEl.getBoundingClientRect();
|
|
@@ -383,9 +387,9 @@
|
|
|
383
387
|
},
|
|
384
388
|
];
|
|
385
389
|
|
|
386
|
-
let shadowBg = 'var(--ui-surface-highest)';
|
|
387
|
-
let bgPickerOpen = false;
|
|
388
|
-
let expandedGroup: string | null = null;
|
|
390
|
+
let shadowBg = $state('var(--ui-surface-highest)');
|
|
391
|
+
let bgPickerOpen = $state(false);
|
|
392
|
+
let expandedGroup: string | null = $state(null);
|
|
389
393
|
|
|
390
394
|
function pickBg(value: string) {
|
|
391
395
|
shadowBg = value;
|
|
@@ -415,10 +419,10 @@
|
|
|
415
419
|
<div class="shadow-item" class:active={editingShadow === token.variable}>
|
|
416
420
|
<div class="shadow-box" style="box-shadow: {shadowTokenCss(token)};"></div>
|
|
417
421
|
<div class="token-info">
|
|
418
|
-
<button class="token-variable copyable" class:copied={copiedVar === token.variable}
|
|
422
|
+
<button class="token-variable copyable" class:copied={copiedVar === token.variable} onclick={() => copy(token.variable)}>{copiedVar === token.variable ? 'copied!' : token.variable}</button>
|
|
419
423
|
<span class="token-value">{shadowTokenValueLabel(token)}</span>
|
|
420
424
|
</div>
|
|
421
|
-
<button class="shadow-edit-btn"
|
|
425
|
+
<button class="shadow-edit-btn" onclick={() => editingShadow = editingShadow === token.variable ? null : token.variable}>
|
|
422
426
|
{editingShadow === token.variable ? 'Close' : 'Edit'}
|
|
423
427
|
</button>
|
|
424
428
|
</div>
|
|
@@ -430,10 +434,10 @@
|
|
|
430
434
|
<div class="shadow-item" class:active={editingShadow === token.variable}>
|
|
431
435
|
<div class="shadow-box" style="box-shadow: {shadowTokenCss(token)};"></div>
|
|
432
436
|
<div class="token-info">
|
|
433
|
-
<button class="token-variable copyable" class:copied={copiedVar === token.variable}
|
|
437
|
+
<button class="token-variable copyable" class:copied={copiedVar === token.variable} onclick={() => copy(token.variable)}>{copiedVar === token.variable ? 'copied!' : token.variable}</button>
|
|
434
438
|
<span class="token-value">{shadowTokenValueLabel(token)}</span>
|
|
435
439
|
</div>
|
|
436
|
-
<button class="shadow-edit-btn"
|
|
440
|
+
<button class="shadow-edit-btn" onclick={() => editingShadow = editingShadow === token.variable ? null : token.variable}>
|
|
437
441
|
{editingShadow === token.variable ? 'Close' : 'Edit'}
|
|
438
442
|
</button>
|
|
439
443
|
</div>
|
|
@@ -447,17 +451,18 @@
|
|
|
447
451
|
{#if editingToken}
|
|
448
452
|
<div class="editor-header">
|
|
449
453
|
<h4 class="global-shadow-title">{editingToken.variable}</h4>
|
|
450
|
-
<button class="shadow-edit-btn"
|
|
454
|
+
<button class="shadow-edit-btn" onclick={() => editingShadow = null}>Close</button>
|
|
451
455
|
</div>
|
|
452
456
|
{#if editingIsScale}
|
|
453
|
-
<button class="reset-btn"
|
|
457
|
+
<button class="reset-btn" onclick={resetToGlobal}>Reset to Global</button>
|
|
454
458
|
{/if}
|
|
455
459
|
<div class="global-shadow-row">
|
|
456
460
|
<span class="shadow-slider-label" title="Direction the light source is coming from — controls which side the shadow falls on">Angle</span>
|
|
461
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
457
462
|
<svg class="angle-dial" viewBox="0 0 48 48" width="48" height="48"
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
463
|
+
onpointerdown={(e) => handleDialDown(e, editingIdx)}
|
|
464
|
+
onpointermove={(e) => handleDialMove(e, editingIdx)}
|
|
465
|
+
onpointerup={handleDialUp}
|
|
461
466
|
>
|
|
462
467
|
<circle cx="24" cy="24" r="20" class="dial-ring" />
|
|
463
468
|
<line x1="24" y1="24"
|
|
@@ -471,47 +476,47 @@
|
|
|
471
476
|
</svg>
|
|
472
477
|
<input class="shadow-slider-input" type="number" min="0" max="360"
|
|
473
478
|
value={editingToken.angle}
|
|
474
|
-
|
|
479
|
+
onchange={(e) => setTokenField(editingIdx, 'angle', +e.currentTarget.value)} />
|
|
475
480
|
<span class="shadow-slider-unit">°</span>
|
|
476
481
|
</div>
|
|
477
482
|
<div class="global-shadow-row">
|
|
478
483
|
<span class="shadow-slider-label" title="How far the shadow is cast from the element — simulates height off the surface">Dist</span>
|
|
479
484
|
<input type="range" min="0" max="60" value={editingToken.distance}
|
|
480
|
-
|
|
481
|
-
|
|
485
|
+
onpointerdown={() => beginSliderGesture('edit shadow distance')}
|
|
486
|
+
oninput={(e) => setTokenField(editingIdx, 'distance', +e.currentTarget.value)} />
|
|
482
487
|
<input class="shadow-slider-input" type="number" min="0" max="100"
|
|
483
488
|
value={editingToken.distance}
|
|
484
|
-
|
|
489
|
+
onchange={(e) => setTokenField(editingIdx, 'distance', +e.currentTarget.value)} />
|
|
485
490
|
<span class="shadow-slider-unit">px</span>
|
|
486
491
|
</div>
|
|
487
492
|
<div class="global-shadow-row">
|
|
488
493
|
<span class="shadow-slider-label" title="Grows or shrinks the shadow before blurring — positive makes it larger than the element, negative makes it smaller">Spread</span>
|
|
489
494
|
<input type="range" min="-50" max="50" value={editingToken.spread}
|
|
490
|
-
|
|
491
|
-
|
|
495
|
+
onpointerdown={() => beginSliderGesture('edit shadow spread')}
|
|
496
|
+
oninput={(e) => setTokenField(editingIdx, 'spread', +e.currentTarget.value)} />
|
|
492
497
|
<input class="shadow-slider-input" type="number" min="-50" max="50"
|
|
493
498
|
value={editingToken.spread}
|
|
494
|
-
|
|
499
|
+
onchange={(e) => setTokenField(editingIdx, 'spread', +e.currentTarget.value)} />
|
|
495
500
|
<span class="shadow-slider-unit">px</span>
|
|
496
501
|
</div>
|
|
497
502
|
<div class="global-shadow-row">
|
|
498
503
|
<span class="shadow-slider-label" title="How soft the shadow edge is — higher values make a wider, more diffused shadow">Blur</span>
|
|
499
504
|
<input type="range" min="0" max="100" value={editingToken.blur}
|
|
500
|
-
|
|
501
|
-
|
|
505
|
+
onpointerdown={() => beginSliderGesture('edit shadow blur')}
|
|
506
|
+
oninput={(e) => setTokenField(editingIdx, 'blur', +e.currentTarget.value)} />
|
|
502
507
|
<input class="shadow-slider-input" type="number" min="0" max="100"
|
|
503
508
|
value={editingToken.blur}
|
|
504
|
-
|
|
509
|
+
onchange={(e) => setTokenField(editingIdx, 'blur', +e.currentTarget.value)} />
|
|
505
510
|
<span class="shadow-slider-unit">px</span>
|
|
506
511
|
</div>
|
|
507
512
|
<div class="global-shadow-row">
|
|
508
513
|
<span class="shadow-slider-label" title="How visible the shadow is — 0% is invisible, 100% is fully opaque">Op.</span>
|
|
509
514
|
<input type="range" min="0" max="100" value={Math.round(editingToken.opacity * 100)}
|
|
510
|
-
|
|
511
|
-
|
|
515
|
+
onpointerdown={() => beginSliderGesture('edit shadow opacity')}
|
|
516
|
+
oninput={(e) => setTokenField(editingIdx, 'opacity', +e.currentTarget.value / 100)} />
|
|
512
517
|
<input class="shadow-slider-input" type="number" min="0" max="100"
|
|
513
518
|
value={Math.round(editingToken.opacity * 100)}
|
|
514
|
-
|
|
519
|
+
onchange={(e) => setTokenField(editingIdx, 'opacity', +e.currentTarget.value / 100)} />
|
|
515
520
|
<span class="shadow-slider-unit">%</span>
|
|
516
521
|
</div>
|
|
517
522
|
<div class="global-color-group">
|
|
@@ -521,43 +526,43 @@
|
|
|
521
526
|
<span class="shadow-slider-label" title="Hue — the base color of the shadow (0°=red, 120°=green, 240°=blue)">H</span>
|
|
522
527
|
<div class="slider-track" style="background: {shadowHueGrad}">
|
|
523
528
|
<input type="range" min="0" max="360" value={editingToken.hue}
|
|
524
|
-
|
|
525
|
-
|
|
529
|
+
onpointerdown={() => beginSliderGesture('edit shadow hue')}
|
|
530
|
+
oninput={(e) => setTokenColor(editingIdx, 'hue', +e.currentTarget.value)} />
|
|
526
531
|
</div>
|
|
527
532
|
<input class="shadow-slider-input" type="number" min="0" max="360"
|
|
528
533
|
value={editingToken.hue}
|
|
529
|
-
|
|
534
|
+
onchange={(e) => setTokenColor(editingIdx, 'hue', +e.currentTarget.value)} />
|
|
530
535
|
<span class="shadow-slider-unit">°</span>
|
|
531
536
|
</div>
|
|
532
537
|
<div class="global-shadow-row">
|
|
533
538
|
<span class="shadow-slider-label" title="Saturation — 0% is gray, 100% is full color intensity">S</span>
|
|
534
539
|
<div class="slider-track" style="background: linear-gradient(to right, hsl({editingToken.hue},0%,{editingToken.lightness}%), hsl({editingToken.hue},100%,{editingToken.lightness}%))">
|
|
535
540
|
<input type="range" min="0" max="100" value={editingToken.saturation}
|
|
536
|
-
|
|
537
|
-
|
|
541
|
+
onpointerdown={() => beginSliderGesture('edit shadow saturation')}
|
|
542
|
+
oninput={(e) => setTokenColor(editingIdx, 'saturation', +e.currentTarget.value)} />
|
|
538
543
|
</div>
|
|
539
544
|
<input class="shadow-slider-input" type="number" min="0" max="100"
|
|
540
545
|
value={editingToken.saturation}
|
|
541
|
-
|
|
546
|
+
onchange={(e) => setTokenColor(editingIdx, 'saturation', +e.currentTarget.value)} />
|
|
542
547
|
<span class="shadow-slider-unit">%</span>
|
|
543
548
|
</div>
|
|
544
549
|
<div class="global-shadow-row">
|
|
545
550
|
<span class="shadow-slider-label" title="Lightness — 0% is black, 100% is white">L</span>
|
|
546
551
|
<div class="slider-track" style="background: linear-gradient(to right, hsl({editingToken.hue},{editingToken.saturation}%,0%), hsl({editingToken.hue},{editingToken.saturation}%,50%), hsl({editingToken.hue},{editingToken.saturation}%,100%))">
|
|
547
552
|
<input type="range" min="0" max="100" value={editingToken.lightness}
|
|
548
|
-
|
|
549
|
-
|
|
553
|
+
onpointerdown={() => beginSliderGesture('edit shadow lightness')}
|
|
554
|
+
oninput={(e) => setTokenColor(editingIdx, 'lightness', +e.currentTarget.value)} />
|
|
550
555
|
</div>
|
|
551
556
|
<input class="shadow-slider-input" type="number" min="0" max="100"
|
|
552
557
|
value={editingToken.lightness}
|
|
553
|
-
|
|
558
|
+
onchange={(e) => setTokenColor(editingIdx, 'lightness', +e.currentTarget.value)} />
|
|
554
559
|
<span class="shadow-slider-unit">%</span>
|
|
555
560
|
</div>
|
|
556
561
|
</div>
|
|
557
562
|
</div>
|
|
558
563
|
<div class="shadow-css-output">
|
|
559
564
|
<code>{shadowTokenCss(editingToken)}</code>
|
|
560
|
-
<button class="shadow-copy-btn"
|
|
565
|
+
<button class="shadow-copy-btn" onclick={() => copy(shadowTokenCss(editingToken))}>
|
|
561
566
|
{copiedVar === shadowTokenCss(editingToken) ? 'Copied!' : 'Copy CSS'}
|
|
562
567
|
</button>
|
|
563
568
|
</div>
|
|
@@ -565,10 +570,11 @@
|
|
|
565
570
|
<h4 class="global-shadow-title">Global Light</h4>
|
|
566
571
|
<div class="global-shadow-row">
|
|
567
572
|
<span class="shadow-slider-label" title="Direction the light source is coming from — controls which side the shadow falls on">Angle</span>
|
|
573
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
568
574
|
<svg class="angle-dial" viewBox="0 0 48 48" width="48" height="48"
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
575
|
+
onpointerdown={handleGlobalDialDown}
|
|
576
|
+
onpointermove={handleGlobalDialMove}
|
|
577
|
+
onpointerup={handleGlobalDialUp}
|
|
572
578
|
>
|
|
573
579
|
<circle cx="24" cy="24" r="20" class="dial-ring" />
|
|
574
580
|
<line x1="24" y1="24"
|
|
@@ -582,40 +588,40 @@
|
|
|
582
588
|
</svg>
|
|
583
589
|
<input class="shadow-slider-input" type="number" min="0" max="360"
|
|
584
590
|
value={sg.angle}
|
|
585
|
-
|
|
591
|
+
onchange={(e) => setGlobalAngle(+e.currentTarget.value)} />
|
|
586
592
|
<span class="shadow-slider-unit">°</span>
|
|
587
593
|
</div>
|
|
588
594
|
<div class="global-shadow-row">
|
|
589
595
|
<span class="shadow-slider-label" title="How far the shadow is cast — simulates height off the surface">Dist Min</span>
|
|
590
596
|
<input type="range" min="0" max="60" value={sg.distanceMin}
|
|
591
|
-
|
|
592
|
-
|
|
597
|
+
onpointerdown={() => beginSliderGesture('drag global dist min')}
|
|
598
|
+
oninput={(e) => setGlobalDistance('distanceMin', +e.currentTarget.value)} />
|
|
593
599
|
<input class="shadow-slider-input" type="number" min="0" max="100"
|
|
594
600
|
value={sg.distanceMin}
|
|
595
|
-
|
|
601
|
+
onchange={(e) => setGlobalDistance('distanceMin', +e.currentTarget.value)} />
|
|
596
602
|
<span class="shadow-slider-unit">px</span>
|
|
597
603
|
</div>
|
|
598
604
|
<div class="global-shadow-row">
|
|
599
605
|
<span class="shadow-slider-label" title="How far the shadow is cast — simulates height off the surface">Dist Max</span>
|
|
600
606
|
<input type="range" min="0" max="60" value={sg.distanceMax}
|
|
601
|
-
|
|
602
|
-
|
|
607
|
+
onpointerdown={() => beginSliderGesture('drag global dist max')}
|
|
608
|
+
oninput={(e) => setGlobalDistance('distanceMax', +e.currentTarget.value)} />
|
|
603
609
|
<input class="shadow-slider-input" type="number" min="0" max="100"
|
|
604
610
|
value={sg.distanceMax}
|
|
605
|
-
|
|
611
|
+
onchange={(e) => setGlobalDistance('distanceMax', +e.currentTarget.value)} />
|
|
606
612
|
<span class="shadow-slider-unit">px</span>
|
|
607
613
|
</div>
|
|
608
614
|
{#if sg.sizeLocked}
|
|
609
615
|
<div class="global-shadow-row">
|
|
610
616
|
<span class="shadow-slider-label" title="Grows or shrinks the shadow before blurring — positive makes it larger than the element, negative makes it smaller">Spread</span>
|
|
611
617
|
<input type="range" min="-50" max="50" value={sg.sizeMin}
|
|
612
|
-
|
|
613
|
-
|
|
618
|
+
onpointerdown={() => beginSliderGesture('drag global spread')}
|
|
619
|
+
oninput={(e) => setGlobalSize('sizeMin', +e.currentTarget.value)} />
|
|
614
620
|
<input class="shadow-slider-input" type="number" min="-50" max="50"
|
|
615
621
|
value={sg.sizeMin}
|
|
616
|
-
|
|
622
|
+
onchange={(e) => setGlobalSize('sizeMin', +e.currentTarget.value)} />
|
|
617
623
|
<span class="shadow-slider-unit">px</span>
|
|
618
|
-
<button class="lock-btn" title="Unlock min/max"
|
|
624
|
+
<button class="lock-btn" title="Unlock min/max" onclick={() => toggleLock('sizeLocked')}>
|
|
619
625
|
<svg viewBox="0 0 16 16" width="14" height="14"><path d="M4 7V5a4 4 0 118 0v2h1a1 1 0 011 1v5a1 1 0 01-1 1H3a1 1 0 01-1-1V8a1 1 0 011-1h1zm2 0h4V5a2 2 0 10-4 0v2z" fill="currentColor"/></svg>
|
|
620
626
|
</button>
|
|
621
627
|
</div>
|
|
@@ -623,24 +629,24 @@
|
|
|
623
629
|
<div class="global-shadow-row">
|
|
624
630
|
<span class="shadow-slider-label" title="Grows or shrinks the shadow before blurring — positive makes it larger than the element, negative makes it smaller">Spread Min</span>
|
|
625
631
|
<input type="range" min="-50" max="50" value={sg.sizeMin}
|
|
626
|
-
|
|
627
|
-
|
|
632
|
+
onpointerdown={() => beginSliderGesture('drag global spread min')}
|
|
633
|
+
oninput={(e) => setGlobalSize('sizeMin', +e.currentTarget.value)} />
|
|
628
634
|
<input class="shadow-slider-input" type="number" min="-50" max="50"
|
|
629
635
|
value={sg.sizeMin}
|
|
630
|
-
|
|
636
|
+
onchange={(e) => setGlobalSize('sizeMin', +e.currentTarget.value)} />
|
|
631
637
|
<span class="shadow-slider-unit">px</span>
|
|
632
|
-
<button class="lock-btn unlocked" title="Lock to single value"
|
|
638
|
+
<button class="lock-btn unlocked" title="Lock to single value" onclick={() => toggleLock('sizeLocked')}>
|
|
633
639
|
<svg viewBox="0 0 16 16" width="14" height="14"><path d="M10 7V5a2 2 0 10-4 0v.5H4V5a4 4 0 118 0v2h1a1 1 0 011 1v5a1 1 0 01-1 1H3a1 1 0 01-1-1V8a1 1 0 011-1h7z" fill="currentColor"/></svg>
|
|
634
640
|
</button>
|
|
635
641
|
</div>
|
|
636
642
|
<div class="global-shadow-row">
|
|
637
643
|
<span class="shadow-slider-label" title="Grows or shrinks the shadow before blurring — positive makes it larger than the element, negative makes it smaller">Spread Max</span>
|
|
638
644
|
<input type="range" min="-50" max="50" value={sg.sizeMax}
|
|
639
|
-
|
|
640
|
-
|
|
645
|
+
onpointerdown={() => beginSliderGesture('drag global spread max')}
|
|
646
|
+
oninput={(e) => setGlobalSize('sizeMax', +e.currentTarget.value)} />
|
|
641
647
|
<input class="shadow-slider-input" type="number" min="-50" max="50"
|
|
642
648
|
value={sg.sizeMax}
|
|
643
|
-
|
|
649
|
+
onchange={(e) => setGlobalSize('sizeMax', +e.currentTarget.value)} />
|
|
644
650
|
<span class="shadow-slider-unit">px</span>
|
|
645
651
|
</div>
|
|
646
652
|
{/if}
|
|
@@ -648,13 +654,13 @@
|
|
|
648
654
|
<div class="global-shadow-row">
|
|
649
655
|
<span class="shadow-slider-label" title="How soft the shadow edge is — higher values make a wider, more diffused shadow">Blur</span>
|
|
650
656
|
<input type="range" min="0" max="100" value={sg.blurMin}
|
|
651
|
-
|
|
652
|
-
|
|
657
|
+
onpointerdown={() => beginSliderGesture('drag global blur')}
|
|
658
|
+
oninput={(e) => setGlobalBlur('blurMin', +e.currentTarget.value)} />
|
|
653
659
|
<input class="shadow-slider-input" type="number" min="0" max="100"
|
|
654
660
|
value={sg.blurMin}
|
|
655
|
-
|
|
661
|
+
onchange={(e) => setGlobalBlur('blurMin', +e.currentTarget.value)} />
|
|
656
662
|
<span class="shadow-slider-unit">px</span>
|
|
657
|
-
<button class="lock-btn" title="Unlock min/max"
|
|
663
|
+
<button class="lock-btn" title="Unlock min/max" onclick={() => toggleLock('blurLocked')}>
|
|
658
664
|
<svg viewBox="0 0 16 16" width="14" height="14"><path d="M4 7V5a4 4 0 118 0v2h1a1 1 0 011 1v5a1 1 0 01-1 1H3a1 1 0 01-1-1V8a1 1 0 011-1h1zm2 0h4V5a2 2 0 10-4 0v2z" fill="currentColor"/></svg>
|
|
659
665
|
</button>
|
|
660
666
|
</div>
|
|
@@ -662,24 +668,24 @@
|
|
|
662
668
|
<div class="global-shadow-row">
|
|
663
669
|
<span class="shadow-slider-label" title="How soft the shadow edge is — higher values make a wider, more diffused shadow">Blur Min</span>
|
|
664
670
|
<input type="range" min="0" max="100" value={sg.blurMin}
|
|
665
|
-
|
|
666
|
-
|
|
671
|
+
onpointerdown={() => beginSliderGesture('drag global blur min')}
|
|
672
|
+
oninput={(e) => setGlobalBlur('blurMin', +e.currentTarget.value)} />
|
|
667
673
|
<input class="shadow-slider-input" type="number" min="0" max="100"
|
|
668
674
|
value={sg.blurMin}
|
|
669
|
-
|
|
675
|
+
onchange={(e) => setGlobalBlur('blurMin', +e.currentTarget.value)} />
|
|
670
676
|
<span class="shadow-slider-unit">px</span>
|
|
671
|
-
<button class="lock-btn unlocked" title="Lock to single value"
|
|
677
|
+
<button class="lock-btn unlocked" title="Lock to single value" onclick={() => toggleLock('blurLocked')}>
|
|
672
678
|
<svg viewBox="0 0 16 16" width="14" height="14"><path d="M10 7V5a2 2 0 10-4 0v.5H4V5a4 4 0 118 0v2h1a1 1 0 011 1v5a1 1 0 01-1 1H3a1 1 0 01-1-1V8a1 1 0 011-1h7z" fill="currentColor"/></svg>
|
|
673
679
|
</button>
|
|
674
680
|
</div>
|
|
675
681
|
<div class="global-shadow-row">
|
|
676
682
|
<span class="shadow-slider-label" title="How soft the shadow edge is — higher values make a wider, more diffused shadow">Blur Max</span>
|
|
677
683
|
<input type="range" min="0" max="100" value={sg.blurMax}
|
|
678
|
-
|
|
679
|
-
|
|
684
|
+
onpointerdown={() => beginSliderGesture('drag global blur max')}
|
|
685
|
+
oninput={(e) => setGlobalBlur('blurMax', +e.currentTarget.value)} />
|
|
680
686
|
<input class="shadow-slider-input" type="number" min="0" max="100"
|
|
681
687
|
value={sg.blurMax}
|
|
682
|
-
|
|
688
|
+
onchange={(e) => setGlobalBlur('blurMax', +e.currentTarget.value)} />
|
|
683
689
|
<span class="shadow-slider-unit">px</span>
|
|
684
690
|
</div>
|
|
685
691
|
{/if}
|
|
@@ -687,13 +693,13 @@
|
|
|
687
693
|
<div class="global-shadow-row">
|
|
688
694
|
<span class="shadow-slider-label" title="How visible the shadow is — 0% is invisible, 100% is fully opaque">Op.</span>
|
|
689
695
|
<input type="range" min="0" max="100" value={Math.round(sg.opacityMin * 100)}
|
|
690
|
-
|
|
691
|
-
|
|
696
|
+
onpointerdown={() => beginSliderGesture('drag global opacity')}
|
|
697
|
+
oninput={(e) => setGlobalOpacity('opacityMin', +e.currentTarget.value / 100)} />
|
|
692
698
|
<input class="shadow-slider-input" type="number" min="0" max="100"
|
|
693
699
|
value={Math.round(sg.opacityMin * 100)}
|
|
694
|
-
|
|
700
|
+
onchange={(e) => setGlobalOpacity('opacityMin', +e.currentTarget.value / 100)} />
|
|
695
701
|
<span class="shadow-slider-unit">%</span>
|
|
696
|
-
<button class="lock-btn" title="Unlock min/max"
|
|
702
|
+
<button class="lock-btn" title="Unlock min/max" onclick={() => toggleLock('opacityLocked')}>
|
|
697
703
|
<svg viewBox="0 0 16 16" width="14" height="14"><path d="M4 7V5a4 4 0 118 0v2h1a1 1 0 011 1v5a1 1 0 01-1 1H3a1 1 0 01-1-1V8a1 1 0 011-1h1zm2 0h4V5a2 2 0 10-4 0v2z" fill="currentColor"/></svg>
|
|
698
704
|
</button>
|
|
699
705
|
</div>
|
|
@@ -701,24 +707,24 @@
|
|
|
701
707
|
<div class="global-shadow-row">
|
|
702
708
|
<span class="shadow-slider-label" title="How visible the shadow is — 0% is invisible, 100% is fully opaque">Op. Min</span>
|
|
703
709
|
<input type="range" min="0" max="100" value={Math.round(sg.opacityMin * 100)}
|
|
704
|
-
|
|
705
|
-
|
|
710
|
+
onpointerdown={() => beginSliderGesture('drag global opacity min')}
|
|
711
|
+
oninput={(e) => setGlobalOpacity('opacityMin', +e.currentTarget.value / 100)} />
|
|
706
712
|
<input class="shadow-slider-input" type="number" min="0" max="100"
|
|
707
713
|
value={Math.round(sg.opacityMin * 100)}
|
|
708
|
-
|
|
714
|
+
onchange={(e) => setGlobalOpacity('opacityMin', +e.currentTarget.value / 100)} />
|
|
709
715
|
<span class="shadow-slider-unit">%</span>
|
|
710
|
-
<button class="lock-btn unlocked" title="Lock to single value"
|
|
716
|
+
<button class="lock-btn unlocked" title="Lock to single value" onclick={() => toggleLock('opacityLocked')}>
|
|
711
717
|
<svg viewBox="0 0 16 16" width="14" height="14"><path d="M10 7V5a2 2 0 10-4 0v.5H4V5a4 4 0 118 0v2h1a1 1 0 011 1v5a1 1 0 01-1 1H3a1 1 0 01-1-1V8a1 1 0 011-1h7z" fill="currentColor"/></svg>
|
|
712
718
|
</button>
|
|
713
719
|
</div>
|
|
714
720
|
<div class="global-shadow-row">
|
|
715
721
|
<span class="shadow-slider-label" title="How visible the shadow is — 0% is invisible, 100% is fully opaque">Op. Max</span>
|
|
716
722
|
<input type="range" min="0" max="100" value={Math.round(sg.opacityMax * 100)}
|
|
717
|
-
|
|
718
|
-
|
|
723
|
+
onpointerdown={() => beginSliderGesture('drag global opacity max')}
|
|
724
|
+
oninput={(e) => setGlobalOpacity('opacityMax', +e.currentTarget.value / 100)} />
|
|
719
725
|
<input class="shadow-slider-input" type="number" min="0" max="100"
|
|
720
726
|
value={Math.round(sg.opacityMax * 100)}
|
|
721
|
-
|
|
727
|
+
onchange={(e) => setGlobalOpacity('opacityMax', +e.currentTarget.value / 100)} />
|
|
722
728
|
<span class="shadow-slider-unit">%</span>
|
|
723
729
|
</div>
|
|
724
730
|
{/if}
|
|
@@ -729,52 +735,52 @@
|
|
|
729
735
|
<span class="shadow-slider-label" title="Hue — the base color of the shadow (0°=red, 120°=green, 240°=blue)">H</span>
|
|
730
736
|
<div class="slider-track" style="background: {shadowHueGrad}">
|
|
731
737
|
<input type="range" min="0" max="360" value={sg.hue}
|
|
732
|
-
|
|
733
|
-
|
|
738
|
+
onpointerdown={() => beginSliderGesture('drag global hue')}
|
|
739
|
+
oninput={(e) => setGlobalColor('hue', +e.currentTarget.value)} />
|
|
734
740
|
</div>
|
|
735
741
|
<input class="shadow-slider-input" type="number" min="0" max="360"
|
|
736
742
|
value={sg.hue}
|
|
737
|
-
|
|
743
|
+
onchange={(e) => setGlobalColor('hue', +e.currentTarget.value)} />
|
|
738
744
|
<span class="shadow-slider-unit">°</span>
|
|
739
745
|
</div>
|
|
740
746
|
<div class="global-shadow-row">
|
|
741
747
|
<span class="shadow-slider-label" title="Saturation — 0% is gray, 100% is full color intensity">S</span>
|
|
742
748
|
<div class="slider-track" style="background: {shadowSatGrad}">
|
|
743
749
|
<input type="range" min="0" max="100" value={sg.saturation}
|
|
744
|
-
|
|
745
|
-
|
|
750
|
+
onpointerdown={() => beginSliderGesture('drag global saturation')}
|
|
751
|
+
oninput={(e) => setGlobalColor('saturation', +e.currentTarget.value)} />
|
|
746
752
|
</div>
|
|
747
753
|
<input class="shadow-slider-input" type="number" min="0" max="100"
|
|
748
754
|
value={sg.saturation}
|
|
749
|
-
|
|
755
|
+
onchange={(e) => setGlobalColor('saturation', +e.currentTarget.value)} />
|
|
750
756
|
<span class="shadow-slider-unit">%</span>
|
|
751
757
|
</div>
|
|
752
758
|
<div class="global-shadow-row">
|
|
753
759
|
<span class="shadow-slider-label" title="Lightness — 0% is black, 100% is white">L</span>
|
|
754
760
|
<div class="slider-track" style="background: {shadowLightGrad}">
|
|
755
761
|
<input type="range" min="0" max="100" value={sg.lightness}
|
|
756
|
-
|
|
757
|
-
|
|
762
|
+
onpointerdown={() => beginSliderGesture('drag global lightness')}
|
|
763
|
+
oninput={(e) => setGlobalColor('lightness', +e.currentTarget.value)} />
|
|
758
764
|
</div>
|
|
759
765
|
<input class="shadow-slider-input" type="number" min="0" max="100"
|
|
760
766
|
value={sg.lightness}
|
|
761
|
-
|
|
767
|
+
onchange={(e) => setGlobalColor('lightness', +e.currentTarget.value)} />
|
|
762
768
|
<span class="shadow-slider-unit">%</span>
|
|
763
769
|
</div>
|
|
764
770
|
</div>
|
|
765
771
|
</div>
|
|
766
|
-
<button class="bg-picker-btn"
|
|
772
|
+
<button class="bg-picker-btn" onclick={toggleBgPicker}>BG</button>
|
|
767
773
|
{#if bgPickerOpen}
|
|
768
774
|
<div class="bg-picker-menu">
|
|
769
775
|
{#each bgColorGroups as group}
|
|
770
|
-
<button class="bg-group-header"
|
|
776
|
+
<button class="bg-group-header" onclick={() => expandedGroup = expandedGroup === group.label ? null : group.label}>
|
|
771
777
|
<span>{group.label}</span>
|
|
772
778
|
<span class="bg-group-arrow">{expandedGroup === group.label ? '▴' : '▾'}</span>
|
|
773
779
|
</button>
|
|
774
780
|
{#if expandedGroup === group.label}
|
|
775
781
|
<div class="bg-group-colors">
|
|
776
782
|
{#each group.colors as color}
|
|
777
|
-
<button class="bg-color-option"
|
|
783
|
+
<button class="bg-color-option" onclick={() => pickBg(color.value)}>
|
|
778
784
|
<span class="bg-color-swatch" style="background: {color.value};"></span>
|
|
779
785
|
<span>{color.label}</span>
|
|
780
786
|
</button>
|