@motion-proto/live-tokens 0.85.0 → 0.86.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/CHANGELOG.md +44 -0
- package/README.md +1 -1
- package/package.json +1 -1
- package/src/editor/component-editor/SlidePagerEditor.svelte +118 -0
- package/src/editor/component-editor/VideoFrameEditor.svelte +73 -0
- package/src/editor/component-editor/VideoLightboxEditor.svelte +95 -0
- package/src/editor/component-editor/registry.ts +40 -1
- package/src/editor/core/sketch/sketchLayer.ts +7 -0
- package/src/editor/overlay/LiveEditorOverlay.svelte +45 -52
- package/src/editor/pages/ComponentEditorPage.svelte +1 -1
- package/src/editor/ui/ThemePanel.svelte +9 -53
- package/src/live-tokens/data/themes/autumn.json +86 -0
- package/src/live-tokens/data/themes/halloween.json +86 -0
- package/src/live-tokens/data/themes/midnight-study.json +86 -0
- package/src/live-tokens/data/themes/ocean.json +86 -0
- package/src/live-tokens/data/themes/royal-velvet.json +86 -0
- package/src/live-tokens/data/themes/sketchy.json +86 -0
- package/src/live-tokens/data/themes/spring-meadow.json +86 -0
- package/src/live-tokens/data/themes/sunset.json +86 -0
- package/src/system/assets/slide-04.webp +0 -0
- package/src/system/assets/slide-05.webp +0 -0
- package/src/system/assets/slide-06.webp +0 -0
- package/src/system/assets/watch-navigation-poster.webp +0 -0
- package/src/system/assets/watch-navigation.mp4 +0 -0
- package/src/system/components/ImageLightbox.svelte +20 -21
- package/src/system/components/SlidePager.svelte +516 -0
- package/src/system/components/VideoFrame.svelte +284 -0
- package/src/system/components/VideoLightbox.svelte +381 -0
- package/src/testing-js/{chunk-RDHCBQ6I.js → chunk-OHU5BHGZ.js} +344 -2
- package/src/testing-js/chunk-OHU5BHGZ.js.map +1 -0
- package/src/testing-js/{chunk-XV5CYADO.js → chunk-Y6R7Z7VK.js} +10 -6
- package/src/testing-js/chunk-Y6R7Z7VK.js.map +1 -0
- package/src/testing-js/component-behavior.contract.js +1 -1
- package/src/testing-js/component-editor.contract.js +1 -1
- package/src/testing-js/component-render.contract.js +1 -1
- package/src/testing-js/index.d.ts +2 -2
- package/src/testing-js/index.js +2 -2
- package/src/testing-js/page-compliance.contract.js +1 -1
- package/src/testing-js/{vitest-BMLIbDs2.d.ts → vitest-5vWtqwWe.d.ts} +2 -2
- package/src/testing-js/vitest.d.ts +1 -1
- package/src/testing-js/vitest.js +2 -2
- package/src/testing-js/chunk-RDHCBQ6I.js.map +0 -1
- package/src/testing-js/chunk-XV5CYADO.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,49 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.86.0 — Video and slides ship as components
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **VideoLightbox.** A still that fades the page away and plays its clip in
|
|
8
|
+
the middle of the screen. A click anywhere, the close button, or Escape
|
|
9
|
+
closes it. Tile, scrim, and close-button chrome are editable, with a hover
|
|
10
|
+
state for the close button. Every shipped theme carries it, derived from
|
|
11
|
+
that theme's ImageLightbox so the two match.
|
|
12
|
+
|
|
13
|
+
- **VideoFrame.** It plays a clip in place from a poster frame, with a play
|
|
14
|
+
badge or no chrome at all, and falls back to the poster when the clip is
|
|
15
|
+
not served.
|
|
16
|
+
|
|
17
|
+
- **SlidePager.** It flips a deck of slides in place. Its full-screen view
|
|
18
|
+
pages through every slide with the same bar and the arrow keys, and has a
|
|
19
|
+
close button and Tab stops on its controls. Its chevrons are inline SVG, so
|
|
20
|
+
it needs no icon font.
|
|
21
|
+
|
|
22
|
+
Every shipped theme carries VideoFrame and SlidePager, with frame corners
|
|
23
|
+
and borders taken from that theme's ImageLightbox and Card. The catalogue
|
|
24
|
+
entries of the four media components point to each other.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- **Every ImageLightbox shows a close button.** A plain lightbox closed only
|
|
29
|
+
on a click, and nothing on screen said so. The close button now renders for
|
|
30
|
+
every lightbox and holds focus when the modal opens. An extended lightbox
|
|
31
|
+
also closes on a click to its unzoomed image; zoomed, a click still pans.
|
|
32
|
+
|
|
33
|
+
- **The editor overlay picks the active page from a dropdown.** The theme
|
|
34
|
+
panel's Components and Sketchstyle rows lose their Open buttons, since the
|
|
35
|
+
view switcher at the top of the panel already opens both. The font pairing
|
|
36
|
+
moves into the Colors & Type info box.
|
|
37
|
+
|
|
38
|
+
### Fixed
|
|
39
|
+
|
|
40
|
+
- **`check-component --tests` collects only the installed package's
|
|
41
|
+
contracts.** `createVitestConfig` globbed the whole project, so a nested
|
|
42
|
+
project with its own copy of the package in `node_modules` added that
|
|
43
|
+
copy's contracts to the run, and they failed against this project's
|
|
44
|
+
registry. The default now scans the contract files' own directory, the way
|
|
45
|
+
`createPlaywrightConfig` already did.
|
|
46
|
+
|
|
3
47
|
## 0.85.0 — A theme's scrims and tints are a colour and three strengths
|
|
4
48
|
|
|
5
49
|
### Changed
|
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ The editor is dev-only. Production builds get plain CSS variables and the compon
|
|
|
11
11
|
## Features
|
|
12
12
|
|
|
13
13
|
- **Live token editing.** Colors, typography, spacing, radii, shadows, motion, palettes, and gradients. Every input writes a CSS variable, so the page repaints with no reload and no build step.
|
|
14
|
-
- **Live component editing.**
|
|
14
|
+
- **Live component editing.** 29 shipped Svelte components (Button, IconButton, Input, Card, Dialog, Badge, Callout, Table, Tooltip, Toggle, TabBar, SegmentedControl, RadioButton, MenuSelect, ProgressBar, CornerBadge, SectionDivider, CollapsibleSection, Notification, Image, ImageLightbox, VideoLightbox, VideoFrame, SlidePager, CodeSnippet, SideNavigation, Panel, InlineEditActions, Slider) declare their design-token aliases in a `:global(:root)` block. Rewire an alias from the component's editor and it updates everywhere that component is used, on your real pages.
|
|
15
15
|
- **Four dev-only routes.** `/live-tokens/editor` for tokens, `/live-tokens/colors` for palettes, `/live-tokens/components` for per-component aliases, `/live-tokens/docs` for the user guide.
|
|
16
16
|
- **Editor overlay.** Pins to the top right of every dev page and opens the editor in a side panel or floating window, so you edit on the page you are styling. Its "Page Source" button opens the current page's `.svelte` file in VS Code.
|
|
17
17
|
- **Themes.** A theme is a whole look in one file: colors and type plus a config for every component, stored by value. Loading one changes a single pointer file, and nothing your site ships changes until you Adopt. Export a theme and import it into another project to restore the look in one step.
|
package/package.json
CHANGED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import type { Token } from './scaffolding/types';
|
|
3
|
+
|
|
4
|
+
export const component = 'slidepager';
|
|
5
|
+
|
|
6
|
+
// Single variant: the default carries the frame, the bar, the counter and
|
|
7
|
+
// the control's resting look. Hover and disabled layer on the control alone.
|
|
8
|
+
const states: Record<string, Token[]> = {
|
|
9
|
+
default: [
|
|
10
|
+
{ label: 'surface color', element: 'frame', variable: '--slidepager-frame-surface' },
|
|
11
|
+
{ label: 'border color', element: 'frame', variable: '--slidepager-frame-border' },
|
|
12
|
+
{ label: 'border width', canBeLinked: true, element: 'frame', variable: '--slidepager-frame-border-width' },
|
|
13
|
+
{ label: 'corner radius', canBeLinked: true, element: 'frame', variable: '--slidepager-frame-radius' },
|
|
14
|
+
{ label: 'shadow', element: 'frame', variable: '--slidepager-frame-shadow' },
|
|
15
|
+
{ label: 'gap', canBeLinked: true, element: 'bar', variable: '--slidepager-bar-gap' },
|
|
16
|
+
{ label: 'padding', canBeLinked: true, element: 'bar', variable: '--slidepager-bar-padding' },
|
|
17
|
+
{ label: 'surface color', element: 'control', variable: '--slidepager-control-surface' },
|
|
18
|
+
{ label: 'border color', element: 'control', variable: '--slidepager-control-border' },
|
|
19
|
+
{ label: 'border width', canBeLinked: true, element: 'control', variable: '--slidepager-control-border-width' },
|
|
20
|
+
{ label: 'corner radius', canBeLinked: true, element: 'control', variable: '--slidepager-control-radius' },
|
|
21
|
+
{ label: 'size', canBeLinked: true, element: 'control', variable: '--slidepager-control-size' },
|
|
22
|
+
{ label: 'icon color', element: 'control', variable: '--slidepager-control-icon' },
|
|
23
|
+
{ label: 'icon size', canBeLinked: true, element: 'control', variable: '--slidepager-control-icon-size' },
|
|
24
|
+
{ label: 'text color', element: 'counter', variable: '--slidepager-counter-text' },
|
|
25
|
+
{ label: 'font family', element: 'counter', variable: '--slidepager-counter-font-family' },
|
|
26
|
+
{ label: 'font size', element: 'counter', variable: '--slidepager-counter-font-size' },
|
|
27
|
+
{ label: 'font weight', element: 'counter', variable: '--slidepager-counter-font-weight' },
|
|
28
|
+
{ label: 'line height', element: 'counter', variable: '--slidepager-counter-line-height' },
|
|
29
|
+
{ label: 'letter spacing', element: 'counter', variable: '--slidepager-counter-letter-spacing' },
|
|
30
|
+
{ label: 'scrim color', element: 'overlay', variable: '--slidepager-scrim-surface' },
|
|
31
|
+
{ label: 'padding', canBeLinked: true, element: 'overlay', variable: '--slidepager-overlay-padding' },
|
|
32
|
+
{ label: 'close button margin', canBeLinked: true, element: 'overlay', variable: '--slidepager-close-margin' },
|
|
33
|
+
{ label: 'fade duration', element: 'overlay', variable: '--slidepager-overlay-duration' },
|
|
34
|
+
{ label: 'fade easing', element: 'overlay', variable: '--slidepager-overlay-easing' }
|
|
35
|
+
],
|
|
36
|
+
hover: [
|
|
37
|
+
{ label: 'surface color', element: 'control', variable: '--slidepager-control-hover-surface' },
|
|
38
|
+
{ label: 'border color', element: 'control', variable: '--slidepager-control-hover-border' },
|
|
39
|
+
{ label: 'icon color', element: 'control', variable: '--slidepager-control-hover-icon' }
|
|
40
|
+
],
|
|
41
|
+
disabled: [
|
|
42
|
+
{ label: 'surface color', element: 'control', variable: '--slidepager-control-disabled-surface' },
|
|
43
|
+
{ label: 'border color', element: 'control', variable: '--slidepager-control-disabled-border' },
|
|
44
|
+
{ label: 'icon color', element: 'control', variable: '--slidepager-control-disabled-icon' }
|
|
45
|
+
]
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const allTokens: Token[] = Object.values(states).flat();
|
|
49
|
+
</script>
|
|
50
|
+
|
|
51
|
+
<script lang="ts">
|
|
52
|
+
import SlidePager, { type SlidePage } from '../../system/components/SlidePager.svelte';
|
|
53
|
+
import VariantGroup from './scaffolding/VariantGroup.svelte';
|
|
54
|
+
import ComponentEditorBase from './scaffolding/ComponentEditorBase.svelte';
|
|
55
|
+
import page04 from '../../system/assets/slide-04.webp';
|
|
56
|
+
import page05 from '../../system/assets/slide-05.webp';
|
|
57
|
+
import page06 from '../../system/assets/slide-06.webp';
|
|
58
|
+
|
|
59
|
+
const demoSlides: SlidePage[] = [
|
|
60
|
+
{ src: page04, alt: 'Types of disabilities: visual, auditory, motor, cognitive', width: 960, height: 540 },
|
|
61
|
+
{ src: page05, alt: 'Permanent, temporary, or situational', width: 960, height: 540 },
|
|
62
|
+
{ src: page06, alt: 'Non-apparent disabilities', width: 960, height: 540 }
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
// The control's two states are forced on both chevrons at once, so the strip
|
|
66
|
+
// shows each one without a pointer and without landing on an end slide.
|
|
67
|
+
function forceClass(activeState: string) {
|
|
68
|
+
if (activeState === 'hover') return 'force-hover';
|
|
69
|
+
if (activeState === 'disabled') return 'force-disabled';
|
|
70
|
+
return '';
|
|
71
|
+
}
|
|
72
|
+
</script>
|
|
73
|
+
|
|
74
|
+
<ComponentEditorBase
|
|
75
|
+
{component}
|
|
76
|
+
title="Slide Pager"
|
|
77
|
+
description="A deck of slides the reader flips through in place, one at a time. Clicking the frame opens a full-screen read that pages through every slide, with the same bar and a close button. The open preview shows it in place."
|
|
78
|
+
tokens={allTokens}
|
|
79
|
+
>
|
|
80
|
+
<VariantGroup
|
|
81
|
+
name="slidepager"
|
|
82
|
+
title="Slide Pager"
|
|
83
|
+
{states}
|
|
84
|
+
{component}
|
|
85
|
+
elementOrder={['frame', 'bar', 'control', 'counter', 'overlay']}
|
|
86
|
+
>
|
|
87
|
+
{#snippet children({ activeState }: { activeState: string })}
|
|
88
|
+
<div class="slidepager-demo">
|
|
89
|
+
<div class="slidepager-demo-inline">
|
|
90
|
+
<SlidePager slides={demoSlides} index={1} class={forceClass(activeState)} />
|
|
91
|
+
</div>
|
|
92
|
+
<div class="slidepager-demo-open">
|
|
93
|
+
<SlidePager slides={demoSlides} index={1} inline class={forceClass(activeState)} />
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
{/snippet}
|
|
97
|
+
</VariantGroup>
|
|
98
|
+
</ComponentEditorBase>
|
|
99
|
+
|
|
100
|
+
<style>
|
|
101
|
+
.slidepager-demo {
|
|
102
|
+
display: flex;
|
|
103
|
+
flex-wrap: wrap;
|
|
104
|
+
align-items: flex-start;
|
|
105
|
+
gap: var(--ui-space-32);
|
|
106
|
+
width: 100%;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.slidepager-demo-inline {
|
|
110
|
+
flex: 1 1 22rem;
|
|
111
|
+
max-width: 32rem;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.slidepager-demo-open {
|
|
115
|
+
flex: 1 1 16rem;
|
|
116
|
+
max-width: 22rem;
|
|
117
|
+
}
|
|
118
|
+
</style>
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import type { Token } from './scaffolding/types';
|
|
3
|
+
|
|
4
|
+
export const component = 'videoframe';
|
|
5
|
+
|
|
6
|
+
const states: Record<string, Token[]> = {
|
|
7
|
+
default: [
|
|
8
|
+
{ label: 'surface color', element: 'frame', variable: '--videoframe-frame-surface' },
|
|
9
|
+
{ label: 'clip surface color', element: 'frame', variable: '--videoframe-clip-surface' },
|
|
10
|
+
{ label: 'border color', element: 'frame', variable: '--videoframe-frame-border' },
|
|
11
|
+
{ label: 'border width', canBeLinked: true, element: 'frame', variable: '--videoframe-frame-border-width' },
|
|
12
|
+
{ label: 'corner radius', canBeLinked: true, element: 'frame', variable: '--videoframe-frame-radius' },
|
|
13
|
+
{ label: 'surface color', element: 'badge', variable: '--videoframe-badge-default-surface' },
|
|
14
|
+
{ label: 'border color', element: 'badge', variable: '--videoframe-badge-default-border' },
|
|
15
|
+
{ label: 'border width', canBeLinked: true, element: 'badge', variable: '--videoframe-badge-default-border-width' },
|
|
16
|
+
{ label: 'size', canBeLinked: true, element: 'badge', variable: '--videoframe-badge-default-size' },
|
|
17
|
+
{ label: 'icon color', element: 'badge', variable: '--videoframe-badge-default-icon' },
|
|
18
|
+
{ label: 'icon size', canBeLinked: true, element: 'badge', variable: '--videoframe-badge-default-icon-size' },
|
|
19
|
+
{ label: 'hover duration', element: 'badge', variable: '--videoframe-badge-duration' },
|
|
20
|
+
{ label: 'hover easing', element: 'badge', variable: '--videoframe-badge-easing' }
|
|
21
|
+
],
|
|
22
|
+
hover: [
|
|
23
|
+
{ label: 'surface color', element: 'badge', variable: '--videoframe-badge-hover-surface' },
|
|
24
|
+
{ label: 'border color', element: 'badge', variable: '--videoframe-badge-hover-border' },
|
|
25
|
+
{ label: 'icon color', element: 'badge', variable: '--videoframe-badge-hover-icon' }
|
|
26
|
+
]
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const allTokens: Token[] = Object.values(states).flat();
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<script lang="ts">
|
|
33
|
+
import VideoFrame from '../../system/components/VideoFrame.svelte';
|
|
34
|
+
import VariantGroup from './scaffolding/VariantGroup.svelte';
|
|
35
|
+
import ComponentEditorBase from './scaffolding/ComponentEditorBase.svelte';
|
|
36
|
+
import clipUrl from '../../system/assets/watch-navigation.mp4';
|
|
37
|
+
import posterUrl from '../../system/assets/watch-navigation-poster.webp';
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<ComponentEditorBase
|
|
41
|
+
{component}
|
|
42
|
+
title="Video Frame"
|
|
43
|
+
description="A poster frame that plays its clip in place when clicked."
|
|
44
|
+
tokens={allTokens}
|
|
45
|
+
>
|
|
46
|
+
<VariantGroup
|
|
47
|
+
name="videoframe"
|
|
48
|
+
title="Video Frame"
|
|
49
|
+
{states}
|
|
50
|
+
{component}
|
|
51
|
+
elementOrder={['frame', 'badge']}
|
|
52
|
+
>
|
|
53
|
+
{#snippet children({ activeState }: { activeState: string })}
|
|
54
|
+
<div class="videoframe-demo">
|
|
55
|
+
<VideoFrame
|
|
56
|
+
src={clipUrl}
|
|
57
|
+
poster={posterUrl}
|
|
58
|
+
alt="Navigation running on a smartwatch"
|
|
59
|
+
width={480}
|
|
60
|
+
height={552}
|
|
61
|
+
class={activeState === 'hover' ? 'force-hover' : ''}
|
|
62
|
+
/>
|
|
63
|
+
</div>
|
|
64
|
+
{/snippet}
|
|
65
|
+
</VariantGroup>
|
|
66
|
+
</ComponentEditorBase>
|
|
67
|
+
|
|
68
|
+
<style>
|
|
69
|
+
.videoframe-demo {
|
|
70
|
+
width: 100%;
|
|
71
|
+
max-width: 14rem;
|
|
72
|
+
}
|
|
73
|
+
</style>
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import type { Token } from './scaffolding/types';
|
|
3
|
+
|
|
4
|
+
export const component = 'videolightbox';
|
|
5
|
+
|
|
6
|
+
const states: Record<string, Token[]> = {
|
|
7
|
+
default: [
|
|
8
|
+
{ label: 'surface color', element: 'tile', variable: '--videolightbox-tile-surface' },
|
|
9
|
+
{ label: 'border color', element: 'tile', variable: '--videolightbox-tile-border' },
|
|
10
|
+
{ label: 'border width', canBeLinked: true, element: 'tile', variable: '--videolightbox-tile-border-width' },
|
|
11
|
+
{ label: 'corner radius', canBeLinked: true, element: 'tile', variable: '--videolightbox-tile-radius' },
|
|
12
|
+
{ label: 'shadow', element: 'tile', variable: '--videolightbox-tile-shadow' },
|
|
13
|
+
{ label: 'scrim color', element: 'overlay', variable: '--videolightbox-scrim-surface' },
|
|
14
|
+
{ label: 'padding', canBeLinked: true, element: 'overlay', variable: '--videolightbox-overlay-padding' },
|
|
15
|
+
{ label: 'fade duration', element: 'overlay', variable: '--videolightbox-overlay-duration' },
|
|
16
|
+
{ label: 'fade easing', element: 'overlay', variable: '--videolightbox-overlay-easing' },
|
|
17
|
+
{ label: 'surface color', element: 'close', variable: '--videolightbox-chrome-surface' },
|
|
18
|
+
{ label: 'border color', element: 'close', variable: '--videolightbox-chrome-border' },
|
|
19
|
+
{ label: 'border width', canBeLinked: true, element: 'close', variable: '--videolightbox-chrome-border-width' },
|
|
20
|
+
{ label: 'corner radius', canBeLinked: true, element: 'close', variable: '--videolightbox-chrome-radius' },
|
|
21
|
+
{ label: 'icon color', element: 'close', variable: '--videolightbox-chrome-icon' },
|
|
22
|
+
],
|
|
23
|
+
hover: [
|
|
24
|
+
{ label: 'surface color', element: 'close', variable: '--videolightbox-chrome-hover-surface' },
|
|
25
|
+
],
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const allTokens: Token[] = Object.values(states).flat();
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<script lang="ts">
|
|
32
|
+
import VideoLightbox from '../../system/components/VideoLightbox.svelte';
|
|
33
|
+
import VariantGroup from './scaffolding/VariantGroup.svelte';
|
|
34
|
+
import ComponentEditorBase from './scaffolding/ComponentEditorBase.svelte';
|
|
35
|
+
import clipUrl from '../../system/assets/watch-navigation.mp4';
|
|
36
|
+
import posterUrl from '../../system/assets/watch-navigation-poster.webp';
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<ComponentEditorBase
|
|
40
|
+
{component}
|
|
41
|
+
title="Video Lightbox"
|
|
42
|
+
description="A small still that fades the page away and plays its clip in the middle of the screen. Click anywhere, the close button, or Escape to close it. The open preview beside the tile shows the overlay and close button in place."
|
|
43
|
+
tokens={allTokens}
|
|
44
|
+
>
|
|
45
|
+
<VariantGroup
|
|
46
|
+
name="videolightbox"
|
|
47
|
+
title="Video Lightbox"
|
|
48
|
+
{states}
|
|
49
|
+
{component}
|
|
50
|
+
elementOrder={['tile', 'overlay', 'close']}
|
|
51
|
+
>
|
|
52
|
+
{#snippet children({ activeState })}
|
|
53
|
+
<div class="videolightbox-demo">
|
|
54
|
+
<div class="videolightbox-demo-tile">
|
|
55
|
+
<VideoLightbox
|
|
56
|
+
src={clipUrl}
|
|
57
|
+
poster={posterUrl}
|
|
58
|
+
alt="Navigation running on a smartwatch"
|
|
59
|
+
width={480}
|
|
60
|
+
height={552}
|
|
61
|
+
/>
|
|
62
|
+
</div>
|
|
63
|
+
<div class="videolightbox-demo-open">
|
|
64
|
+
<VideoLightbox
|
|
65
|
+
src={clipUrl}
|
|
66
|
+
poster={posterUrl}
|
|
67
|
+
width={480}
|
|
68
|
+
height={552}
|
|
69
|
+
inline
|
|
70
|
+
class={activeState === 'hover' ? 'force-hover' : ''}
|
|
71
|
+
/>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
{/snippet}
|
|
75
|
+
</VariantGroup>
|
|
76
|
+
</ComponentEditorBase>
|
|
77
|
+
|
|
78
|
+
<style>
|
|
79
|
+
.videolightbox-demo {
|
|
80
|
+
display: flex;
|
|
81
|
+
flex-wrap: wrap;
|
|
82
|
+
align-items: center;
|
|
83
|
+
gap: var(--ui-space-32);
|
|
84
|
+
width: 100%;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.videolightbox-demo-tile {
|
|
88
|
+
width: 8rem;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.videolightbox-demo-open {
|
|
92
|
+
flex: 1 1 18rem;
|
|
93
|
+
max-width: 28rem;
|
|
94
|
+
}
|
|
95
|
+
</style>
|
|
@@ -24,6 +24,12 @@ import ImageEditor, { allTokens as imageTokens, intrinsics as imageIntrinsics }
|
|
|
24
24
|
import { catalogue as imageCatalogue } from '../../system/components/Image.svelte';
|
|
25
25
|
import ImageLightboxEditor, { allTokens as imageLightboxTokens } from './ImageLightboxEditor.svelte';
|
|
26
26
|
import { catalogue as imageLightboxCatalogue } from '../../system/components/ImageLightbox.svelte';
|
|
27
|
+
import VideoLightboxEditor, { allTokens as videoLightboxTokens } from './VideoLightboxEditor.svelte';
|
|
28
|
+
import VideoFrameEditor, { allTokens as videoFrameTokens } from './VideoFrameEditor.svelte';
|
|
29
|
+
import { catalogue as videoFrameCatalogue } from '../../system/components/VideoFrame.svelte';
|
|
30
|
+
import SlidePagerEditor, { allTokens as slidePagerTokens } from './SlidePagerEditor.svelte';
|
|
31
|
+
import { catalogue as slidePagerCatalogue } from '../../system/components/SlidePager.svelte';
|
|
32
|
+
import { catalogue as videoLightboxCatalogue } from '../../system/components/VideoLightbox.svelte';
|
|
27
33
|
import InlineEditActionsEditor, { allTokens as inlineEditActionsTokens } from './InlineEditActionsEditor.svelte';
|
|
28
34
|
import { catalogue as inlineEditActionsCatalogue } from '../../system/components/InlineEditActions.svelte';
|
|
29
35
|
import InputEditor, { allTokens as inputTokens } from './InputEditor.svelte';
|
|
@@ -81,7 +87,10 @@ type BuiltInComponentId =
|
|
|
81
87
|
| 'tabbar'
|
|
82
88
|
| 'toggle'
|
|
83
89
|
| 'tooltip'
|
|
84
|
-
| 'progressbar'
|
|
90
|
+
| 'progressbar'
|
|
91
|
+
| 'videolightbox'
|
|
92
|
+
| 'videoframe'
|
|
93
|
+
| 'slidepager';
|
|
85
94
|
|
|
86
95
|
/**
|
|
87
96
|
* Public component id type. Widened to `string` because consumers can register
|
|
@@ -255,6 +264,36 @@ const builtInRegistry: Readonly<Record<BuiltInComponentId, RegistryEntry>> = Obj
|
|
|
255
264
|
catalogue: imageLightboxCatalogue,
|
|
256
265
|
origin: 'system',
|
|
257
266
|
},
|
|
267
|
+
videolightbox: {
|
|
268
|
+
id: 'videolightbox',
|
|
269
|
+
label: 'Video Lightbox',
|
|
270
|
+
icon: 'fas fa-circle-play',
|
|
271
|
+
sourceFile: 'src/system/components/VideoLightbox.svelte',
|
|
272
|
+
editorComponent: VideoLightboxEditor,
|
|
273
|
+
schema: videoLightboxTokens,
|
|
274
|
+
catalogue: videoLightboxCatalogue,
|
|
275
|
+
origin: 'system',
|
|
276
|
+
},
|
|
277
|
+
videoframe: {
|
|
278
|
+
id: 'videoframe',
|
|
279
|
+
label: 'Video Frame',
|
|
280
|
+
icon: 'fas fa-film',
|
|
281
|
+
sourceFile: 'src/system/components/VideoFrame.svelte',
|
|
282
|
+
editorComponent: VideoFrameEditor,
|
|
283
|
+
schema: videoFrameTokens,
|
|
284
|
+
catalogue: videoFrameCatalogue,
|
|
285
|
+
origin: 'system',
|
|
286
|
+
},
|
|
287
|
+
slidepager: {
|
|
288
|
+
id: 'slidepager',
|
|
289
|
+
label: 'Slide Pager',
|
|
290
|
+
icon: 'fas fa-images',
|
|
291
|
+
sourceFile: 'src/system/components/SlidePager.svelte',
|
|
292
|
+
editorComponent: SlidePagerEditor,
|
|
293
|
+
schema: slidePagerTokens,
|
|
294
|
+
catalogue: slidePagerCatalogue,
|
|
295
|
+
origin: 'system',
|
|
296
|
+
},
|
|
258
297
|
inlineeditactions: {
|
|
259
298
|
id: 'inlineeditactions',
|
|
260
299
|
label: 'Inline Edit Actions',
|
|
@@ -192,6 +192,13 @@ const PART_SPECS: readonly PartSpec[] = [
|
|
|
192
192
|
// Close and the two nav chevrons all carry this class; each is fixed and
|
|
193
193
|
// floats over the photo rather than a scrim of its own.
|
|
194
194
|
{ sel: '.image-lightbox-chrome', stem: 'imagelightbox-chrome', positioned: true, unmasked: true },
|
|
195
|
+
// The trigger inside clips the still, so the tile itself stays unclipped.
|
|
196
|
+
{ sel: '.videolightbox', stem: 'videolightbox-tile' },
|
|
197
|
+
{
|
|
198
|
+
sel: '.videolightbox-modal', fill: 'var(--videolightbox-scrim-surface)',
|
|
199
|
+
stroke: 'transparent', positioned: true, unmasked: true,
|
|
200
|
+
},
|
|
201
|
+
{ sel: '.videolightbox-close', stem: 'videolightbox-chrome', positioned: true, unmasked: true },
|
|
195
202
|
|
|
196
203
|
// Status blocks
|
|
197
204
|
...STATUS_VARIANTS.map((v) => ({ sel: `.callout-${v}`, stem: `callout-${v}` })),
|
|
@@ -47,6 +47,8 @@
|
|
|
47
47
|
projectRoot = INJECTED_PROJECT_ROOT
|
|
48
48
|
}: Props = $props();
|
|
49
49
|
|
|
50
|
+
let activeLink = $derived(navLinks.find((l) => l.path === $route));
|
|
51
|
+
|
|
50
52
|
// Dev-only; skip inside iframe (editor route embeds this app).
|
|
51
53
|
const isDev = import.meta.env.DEV;
|
|
52
54
|
const isInIframe = typeof window !== 'undefined' && window.parent !== window;
|
|
@@ -677,22 +679,19 @@
|
|
|
677
679
|
|
|
678
680
|
{#if open && navLinks.length > 0}
|
|
679
681
|
<div class="seg-group" transition:fade={BTN_FADE}>
|
|
680
|
-
<
|
|
681
|
-
<div class="
|
|
682
|
-
{#
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
disabled={link.disabled}
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
<span>{link.label}</span>
|
|
694
|
-
</button>
|
|
695
|
-
{/each}
|
|
682
|
+
<label class="seg-label" for="lt-active-page">Active Page:</label>
|
|
683
|
+
<div class="page-select">
|
|
684
|
+
{#if activeLink?.icon}<i class="fas {activeLink.icon} page-icon"></i>{/if}
|
|
685
|
+
<select
|
|
686
|
+
id="lt-active-page"
|
|
687
|
+
value={$route}
|
|
688
|
+
onchange={(e) => navigate(e.currentTarget.value)}
|
|
689
|
+
>
|
|
690
|
+
{#each navLinks as link (link.path)}
|
|
691
|
+
<option value={link.path} disabled={link.disabled}>{link.label}</option>
|
|
692
|
+
{/each}
|
|
693
|
+
</select>
|
|
694
|
+
<i class="fas fa-chevron-down page-caret"></i>
|
|
696
695
|
</div>
|
|
697
696
|
</div>
|
|
698
697
|
{/if}
|
|
@@ -1116,61 +1115,55 @@
|
|
|
1116
1115
|
color: var(--ui-text-primary, #fff);
|
|
1117
1116
|
}
|
|
1118
1117
|
|
|
1119
|
-
.
|
|
1118
|
+
.page-select {
|
|
1119
|
+
position: relative;
|
|
1120
1120
|
display: inline-flex;
|
|
1121
1121
|
align-items: center;
|
|
1122
|
-
|
|
1123
|
-
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
.page-select select {
|
|
1125
|
+
appearance: none;
|
|
1126
|
+
color-scheme: dark;
|
|
1127
|
+
padding: var(--ui-space-4, 4px) calc(var(--ui-space-8, 8px) + 20px) var(--ui-space-4, 4px) var(--ui-space-8, 8px);
|
|
1124
1128
|
background: rgba(0, 0, 0, 0.55);
|
|
1125
1129
|
border: 1px solid rgba(255, 255, 255, 0.28);
|
|
1126
1130
|
border-radius: var(--ui-radius-lg, 6px);
|
|
1127
|
-
|
|
1128
|
-
inset 0 1px 0 rgba(0, 0, 0, 0.5),
|
|
1129
|
-
0 0 0 1px rgba(0, 0, 0, 0.4);
|
|
1130
|
-
}
|
|
1131
|
-
|
|
1132
|
-
.seg-pill {
|
|
1133
|
-
display: inline-flex;
|
|
1134
|
-
align-items: center;
|
|
1135
|
-
gap: var(--ui-space-4, 4px);
|
|
1136
|
-
padding: var(--ui-space-4, 4px) var(--ui-space-8, 8px);
|
|
1137
|
-
background: transparent;
|
|
1138
|
-
border: 1px solid transparent;
|
|
1139
|
-
border-radius: 3px;
|
|
1140
|
-
color: rgba(255, 255, 255, 0.6);
|
|
1131
|
+
color: var(--ui-text-primary, #fff);
|
|
1141
1132
|
font-family: inherit;
|
|
1142
1133
|
font-size: var(--ui-font-size-md, 16px);
|
|
1143
1134
|
font-weight: var(--ui-font-weight-medium, 500);
|
|
1144
1135
|
cursor: pointer;
|
|
1145
|
-
transition:
|
|
1146
|
-
background var(--ui-transition-fast, 120ms ease),
|
|
1147
|
-
color var(--ui-transition-fast, 120ms ease),
|
|
1148
|
-
border-color var(--ui-transition-fast, 120ms ease);
|
|
1136
|
+
transition: border-color var(--ui-transition-fast, 120ms ease);
|
|
1149
1137
|
}
|
|
1150
1138
|
|
|
1151
|
-
.
|
|
1152
|
-
|
|
1153
|
-
opacity: 0.85;
|
|
1139
|
+
.page-icon ~ select {
|
|
1140
|
+
padding-left: calc(var(--ui-space-8, 8px) + 24px);
|
|
1154
1141
|
}
|
|
1155
1142
|
|
|
1156
|
-
.
|
|
1157
|
-
color: rgba(255, 255, 255, 0.
|
|
1143
|
+
.page-select select:hover {
|
|
1144
|
+
border-color: rgba(255, 255, 255, 0.5);
|
|
1158
1145
|
}
|
|
1159
1146
|
|
|
1160
|
-
.
|
|
1161
|
-
|
|
1162
|
-
|
|
1147
|
+
.page-select select:focus-visible {
|
|
1148
|
+
outline: 2px solid rgba(255, 255, 255, 0.5);
|
|
1149
|
+
outline-offset: 2px;
|
|
1163
1150
|
}
|
|
1164
1151
|
|
|
1165
|
-
.
|
|
1166
|
-
|
|
1152
|
+
.page-icon,
|
|
1153
|
+
.page-caret {
|
|
1154
|
+
position: absolute;
|
|
1155
|
+
pointer-events: none;
|
|
1156
|
+
color: rgba(255, 255, 255, 0.6);
|
|
1167
1157
|
}
|
|
1168
1158
|
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1159
|
+
.page-icon {
|
|
1160
|
+
left: var(--ui-space-8, 8px);
|
|
1161
|
+
font-size: var(--ui-font-size-md, 16px);
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
.page-caret {
|
|
1165
|
+
right: var(--ui-space-8, 8px);
|
|
1166
|
+
font-size: var(--ui-font-size-xs, 12px);
|
|
1174
1167
|
}
|
|
1175
1168
|
|
|
1176
1169
|
.frame-wrap {
|