@zylem/ui 0.8.8 → 0.8.11
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/dist/chunk-V47CGFWX.js +1 -0
- package/dist/index.css +1 -1
- package/dist/index.d.ts +15 -0
- package/dist/index.js +1 -1
- package/dist/styles.css +1 -1
- package/dist/styles.js +1 -1
- package/package.json +22 -11
- package/src/components/Accordion/Accordion.tsx +10 -1
- package/src/components/AddTile/AddTile.css.ts +95 -0
- package/src/components/AddTile/AddTile.tsx +45 -0
- package/src/components/BuildLabel/BuildLabel.css.ts +31 -0
- package/src/components/BuildLabel/BuildLabel.tsx +37 -0
- package/src/components/BuildLabel/mount-build-label.ts +77 -0
- package/src/components/BuildLabel/resolve-build-version.ts +47 -0
- package/src/components/Callout/Callout.css.ts +45 -0
- package/src/components/Callout/Callout.tsx +32 -0
- package/src/components/Card/Card.css.ts +18 -0
- package/src/components/Card/Card.tsx +25 -2
- package/src/components/ColorPalette/ColorPalette.css.ts +51 -0
- package/src/components/ColorPalette/ColorPalette.tsx +78 -0
- package/src/components/Dialog/Dialog.css.ts +69 -2
- package/src/components/Dialog/Dialog.tsx +35 -7
- package/src/components/DropdownMenu/DropdownMenu.css.ts +22 -0
- package/src/components/DropdownMenu/DropdownMenu.tsx +67 -20
- package/src/components/EmptyState/EmptyState.css.ts +9 -0
- package/src/components/EmptyState/EmptyState.tsx +3 -1
- package/src/components/FrameStrip/FrameStrip.css.ts +138 -0
- package/src/components/FrameStrip/FrameStrip.tsx +146 -0
- package/src/components/Menubar/Menubar.css.ts +52 -0
- package/src/components/Menubar/Menubar.tsx +77 -0
- package/src/components/Mixer/Knob.tsx +188 -0
- package/src/components/Mixer/Mixer.css.ts +246 -0
- package/src/components/Mixer/MixerStrip.tsx +150 -0
- package/src/components/NumberField/NumberField.tsx +5 -1
- package/src/components/Panel/Panel.tsx +6 -5
- package/src/components/PianoRoll/PianoRoll.css.ts +224 -0
- package/src/components/PianoRoll/PianoRoll.tsx +353 -0
- package/src/components/PianoRoll/piano-roll-math.ts +89 -0
- package/src/components/PixelGrid/PixelGrid.css.ts +30 -0
- package/src/components/PixelGrid/PixelGrid.tsx +48 -0
- package/src/components/PlaybackControls/PlaybackControls.css.ts +53 -0
- package/src/components/PlaybackControls/PlaybackControls.tsx +80 -0
- package/src/components/Property/Property.css.ts +1 -1
- package/src/components/Property/Property.tsx +3 -0
- package/src/components/Sidebar/Sidebar.css.ts +8 -0
- package/src/components/Sidebar/Sidebar.tsx +10 -2
- package/src/components/Slider/Slider.tsx +6 -0
- package/src/components/SpriteSizePicker/SpriteSizePicker.css.ts +103 -0
- package/src/components/SpriteSizePicker/SpriteSizePicker.tsx +172 -0
- package/src/components/SpriteSizePicker/snap-size.ts +28 -0
- package/src/components/Tabs/Tabs.css.ts +48 -0
- package/src/components/Tabs/Tabs.tsx +41 -3
- package/src/components/TextField/TextField.tsx +49 -5
- package/src/components/TimelineLanes/TimelineLanes.css.ts +365 -0
- package/src/components/TimelineLanes/TimelineLanes.tsx +509 -0
- package/src/components/TimelineLanes/lanes-math.ts +182 -0
- package/src/components/TimelineTrack/TimelineTrack.css.ts +163 -0
- package/src/components/TimelineTrack/TimelineTrack.tsx +90 -0
- package/src/components/TimelineTrack/timeline-math.ts +43 -0
- package/src/components/Toast/Toast.css.ts +48 -0
- package/src/components/Toast/Toast.tsx +37 -0
- package/src/components/ToolButton/ToolButton.tsx +92 -0
- package/src/components/ToolGroup/ToolGroup.css.ts +35 -0
- package/src/components/ToolGroup/ToolGroup.tsx +29 -0
- package/src/components/ToolboxButton/ToolboxButton.css.ts +131 -0
- package/src/components/ToolboxButton/ToolboxButton.tsx +88 -0
- package/src/components/TrackHeader/TrackHeader.css.ts +162 -0
- package/src/components/TrackHeader/TrackHeader.tsx +145 -0
- package/src/components/TransportBar/TransportBar.css.ts +65 -0
- package/src/components/TransportBar/TransportBar.tsx +154 -0
- package/src/components/Waveform/Waveform.css.ts +77 -0
- package/src/components/Waveform/Waveform.tsx +195 -0
- package/src/components/Waveform/peaks.ts +49 -0
- package/src/components/index.ts +122 -0
- package/src/global/accordion.css.ts +23 -0
- package/src/global/hyperglass-base.css.ts +9 -6
- package/src/styles.ts +23 -0
- package/src/theme.css.ts +15 -0
- package/vite/collect-zylem-versions.ts +82 -0
- package/vite/index.ts +30 -0
- package/dist/chunk-3TP2SNNW.js +0 -1
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { For, Show } from 'solid-js';
|
|
2
|
+
import { AddTile } from '../AddTile/AddTile';
|
|
3
|
+
|
|
4
|
+
/** Sentinel colour for the eraser / transparent swatch. */
|
|
5
|
+
export const TRANSPARENT = 'transparent';
|
|
6
|
+
|
|
7
|
+
export interface SwatchProps {
|
|
8
|
+
/** Any CSS colour, or `'transparent'` for the checkered swatch. */
|
|
9
|
+
color: string;
|
|
10
|
+
selected?: boolean;
|
|
11
|
+
onClick?: (event: MouseEvent) => void;
|
|
12
|
+
/** Accessible name; defaults to the colour string. */
|
|
13
|
+
label?: string;
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
class?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** One palette colour: a small glass-edged square, ringed when selected. */
|
|
19
|
+
export function Swatch(props: SwatchProps) {
|
|
20
|
+
const transparent = () => props.color === TRANSPARENT;
|
|
21
|
+
return (
|
|
22
|
+
<button
|
|
23
|
+
type="button"
|
|
24
|
+
class={props.class ? `zylem-swatch ${props.class}` : 'zylem-swatch'}
|
|
25
|
+
style={transparent() ? undefined : { '--zylem-swatch-color': props.color }}
|
|
26
|
+
data-transparent={transparent() ? '' : undefined}
|
|
27
|
+
data-selected={props.selected ? '' : undefined}
|
|
28
|
+
aria-pressed={props.selected ? 'true' : 'false'}
|
|
29
|
+
aria-label={props.label ?? (transparent() ? 'Transparent' : props.color)}
|
|
30
|
+
title={props.label ?? (transparent() ? 'Transparent' : props.color)}
|
|
31
|
+
disabled={props.disabled}
|
|
32
|
+
onClick={props.onClick}
|
|
33
|
+
/>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface ColorPaletteProps {
|
|
38
|
+
colors: readonly string[];
|
|
39
|
+
/** Currently selected colour (compare with `colors` entries or `'transparent'`). */
|
|
40
|
+
value?: string;
|
|
41
|
+
onSelect?: (color: string) => void;
|
|
42
|
+
/** When provided, an add tile follows the swatches. */
|
|
43
|
+
onAdd?: () => void;
|
|
44
|
+
/** Lead with the transparent swatch. Defaults to true. */
|
|
45
|
+
includeTransparent?: boolean;
|
|
46
|
+
class?: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Row of swatches with an optional transparent lead and add tile. */
|
|
50
|
+
export function ColorPalette(props: ColorPaletteProps) {
|
|
51
|
+
return (
|
|
52
|
+
<div
|
|
53
|
+
role="listbox"
|
|
54
|
+
aria-label="Palette"
|
|
55
|
+
class={props.class ? `zylem-palette ${props.class}` : 'zylem-palette'}
|
|
56
|
+
>
|
|
57
|
+
<Show when={props.includeTransparent ?? true}>
|
|
58
|
+
<Swatch
|
|
59
|
+
color={TRANSPARENT}
|
|
60
|
+
selected={props.value === TRANSPARENT}
|
|
61
|
+
onClick={() => props.onSelect?.(TRANSPARENT)}
|
|
62
|
+
/>
|
|
63
|
+
</Show>
|
|
64
|
+
<For each={props.colors}>
|
|
65
|
+
{(color) => (
|
|
66
|
+
<Swatch
|
|
67
|
+
color={color}
|
|
68
|
+
selected={props.value === color}
|
|
69
|
+
onClick={() => props.onSelect?.(color)}
|
|
70
|
+
/>
|
|
71
|
+
)}
|
|
72
|
+
</For>
|
|
73
|
+
<Show when={props.onAdd}>
|
|
74
|
+
<AddTile label="Add color" onClick={() => props.onAdd?.()} />
|
|
75
|
+
</Show>
|
|
76
|
+
</div>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
@@ -32,8 +32,12 @@ globalStyle('.zylem-dialog-positioner', {
|
|
|
32
32
|
});
|
|
33
33
|
|
|
34
34
|
globalStyle('.zylem-dialog-content', {
|
|
35
|
+
display: 'flex',
|
|
36
|
+
flexDirection: 'column',
|
|
35
37
|
width: '100%',
|
|
36
38
|
maxWidth: '440px',
|
|
39
|
+
maxHeight: '100%',
|
|
40
|
+
minHeight: 0,
|
|
37
41
|
borderRadius: vars.radii.window,
|
|
38
42
|
border: '1px solid rgba(146, 205, 255, 0.54)',
|
|
39
43
|
background: vars.material.glassPanelDark,
|
|
@@ -48,19 +52,41 @@ globalStyle('.zylem-dialog-content', {
|
|
|
48
52
|
},
|
|
49
53
|
});
|
|
50
54
|
|
|
55
|
+
globalStyle('.zylem-dialog-content[data-size="lg"]', {
|
|
56
|
+
maxWidth: '760px',
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Fullscreen fills the positioner (viewport minus its padding). The body takes
|
|
60
|
+
// whatever height the header and footer leave, so browser-style content can
|
|
61
|
+
// hand scrolling to its own columns.
|
|
62
|
+
globalStyle('.zylem-dialog-content[data-size="fullscreen"]', {
|
|
63
|
+
maxWidth: 'none',
|
|
64
|
+
height: '100%',
|
|
65
|
+
borderRadius: vars.radii.card,
|
|
66
|
+
});
|
|
67
|
+
|
|
51
68
|
globalStyle('.zylem-dialog-header', {
|
|
69
|
+
flex: 'none',
|
|
52
70
|
minHeight: '40px',
|
|
53
71
|
display: 'flex',
|
|
54
72
|
alignItems: 'center',
|
|
55
73
|
justifyContent: 'space-between',
|
|
56
|
-
gap: vars.spacing.
|
|
57
|
-
padding:
|
|
74
|
+
gap: vars.spacing.md,
|
|
75
|
+
padding: `${vars.spacing.xs} ${vars.spacing.md}`,
|
|
58
76
|
background: vars.material.glossyBar,
|
|
59
77
|
borderBottom: '1px solid rgba(5, 18, 32, 0.85)',
|
|
60
78
|
boxShadow:
|
|
61
79
|
'inset 0 1px 0 rgba(255,255,255,0.62), inset 0 -1px 0 rgba(255,255,255,0.08)',
|
|
62
80
|
});
|
|
63
81
|
|
|
82
|
+
globalStyle('.zylem-dialog-titles', {
|
|
83
|
+
display: 'flex',
|
|
84
|
+
flexDirection: 'column',
|
|
85
|
+
gap: '2px',
|
|
86
|
+
flex: '1 1 auto',
|
|
87
|
+
minWidth: 0,
|
|
88
|
+
});
|
|
89
|
+
|
|
64
90
|
globalStyle('.zylem-dialog-title', {
|
|
65
91
|
margin: 0,
|
|
66
92
|
fontSize: '13px',
|
|
@@ -71,7 +97,26 @@ globalStyle('.zylem-dialog-title', {
|
|
|
71
97
|
textShadow: '0 1px 1px rgba(0,0,0,0.65)',
|
|
72
98
|
});
|
|
73
99
|
|
|
100
|
+
globalStyle('.zylem-dialog-subtitle', {
|
|
101
|
+
margin: 0,
|
|
102
|
+
fontSize: `calc(${vars.typography.fontSize} - 2px)`,
|
|
103
|
+
fontWeight: 500,
|
|
104
|
+
color: 'rgba(232, 244, 255, 0.78)',
|
|
105
|
+
fontFamily: vars.typography.fontFamily,
|
|
106
|
+
textShadow: '0 1px 1px rgba(0,0,0,0.5)',
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
globalStyle('.zylem-dialog-header-extra', {
|
|
110
|
+
display: 'flex',
|
|
111
|
+
alignItems: 'center',
|
|
112
|
+
gap: vars.spacing.sm,
|
|
113
|
+
flex: 'none',
|
|
114
|
+
});
|
|
115
|
+
|
|
74
116
|
globalStyle('.zylem-dialog-body', {
|
|
117
|
+
flex: '1 1 auto',
|
|
118
|
+
minHeight: 0,
|
|
119
|
+
overflow: 'auto',
|
|
75
120
|
padding: vars.spacing.lg,
|
|
76
121
|
color: vars.colors.text,
|
|
77
122
|
fontFamily: vars.typography.fontFamily,
|
|
@@ -79,9 +124,31 @@ globalStyle('.zylem-dialog-body', {
|
|
|
79
124
|
lineHeight: 1.45,
|
|
80
125
|
});
|
|
81
126
|
|
|
127
|
+
// In fullscreen the body and its layer scope become flex columns so content
|
|
128
|
+
// can lay out against the dialog's height (and own its scrolling).
|
|
129
|
+
globalStyle('.zylem-dialog-content[data-size="fullscreen"] .zylem-dialog-body', {
|
|
130
|
+
display: 'flex',
|
|
131
|
+
flexDirection: 'column',
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
globalStyle('.zylem-dialog-content[data-size="fullscreen"] .zylem-dialog-scope', {
|
|
135
|
+
display: 'flex',
|
|
136
|
+
flexDirection: 'column',
|
|
137
|
+
flex: '1 1 auto',
|
|
138
|
+
minHeight: 0,
|
|
139
|
+
});
|
|
140
|
+
|
|
82
141
|
globalStyle('.zylem-dialog-footer', {
|
|
142
|
+
flex: 'none',
|
|
83
143
|
display: 'flex',
|
|
144
|
+
alignItems: 'center',
|
|
84
145
|
justifyContent: 'flex-end',
|
|
85
146
|
gap: vars.spacing.sm,
|
|
86
147
|
padding: `0 ${vars.spacing.lg} ${vars.spacing.lg}`,
|
|
87
148
|
});
|
|
149
|
+
|
|
150
|
+
globalStyle('.zylem-dialog-content[data-size="fullscreen"] .zylem-dialog-footer', {
|
|
151
|
+
padding: `${vars.spacing.sm} ${vars.spacing.lg}`,
|
|
152
|
+
borderTop: '1px solid rgba(97, 166, 232, 0.28)',
|
|
153
|
+
background: 'rgba(4, 12, 20, 0.6)',
|
|
154
|
+
});
|
|
@@ -5,13 +5,29 @@ import { createDismissGuard, LayerScope, useLayer } from '../../layers';
|
|
|
5
5
|
import { Button } from '../Button/Button';
|
|
6
6
|
import { WindowControls } from '../WindowControls/WindowControls';
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Card width. `md` is the default 440px card, `lg` a 760px card for forms
|
|
10
|
+
* with two columns, and `fullscreen` fills the viewport (minus a margin) with
|
|
11
|
+
* the body taking the remaining height — the shell for browser-style pickers.
|
|
12
|
+
*/
|
|
13
|
+
export type DialogSize = 'md' | 'lg' | 'fullscreen';
|
|
14
|
+
|
|
8
15
|
export interface DialogProps {
|
|
9
16
|
open?: boolean;
|
|
10
17
|
onOpenChange?: (open: boolean) => void;
|
|
11
18
|
title?: JSX.Element;
|
|
19
|
+
/** Secondary line under the title. */
|
|
20
|
+
subtitle?: JSX.Element;
|
|
21
|
+
/** Content between the titles and the close control, e.g. a search field. */
|
|
22
|
+
headerExtra?: JSX.Element;
|
|
23
|
+
size?: DialogSize;
|
|
12
24
|
children: JSX.Element;
|
|
13
25
|
/** Optional footer content (e.g. action buttons). */
|
|
14
26
|
footer?: JSX.Element;
|
|
27
|
+
/** Extra class on the card. */
|
|
28
|
+
class?: string;
|
|
29
|
+
/** Extra class on the body, e.g. to hand scrolling to the content's columns. */
|
|
30
|
+
bodyClass?: string;
|
|
15
31
|
}
|
|
16
32
|
|
|
17
33
|
/**
|
|
@@ -43,19 +59,31 @@ export function Dialog(props: DialogProps) {
|
|
|
43
59
|
<div class="zylem-dialog-positioner">
|
|
44
60
|
<KDialog.Content
|
|
45
61
|
ref={dismiss.ref}
|
|
46
|
-
class=
|
|
62
|
+
class={props.class ? `zylem-dialog-content ${props.class}` : 'zylem-dialog-content'}
|
|
63
|
+
data-size={props.size}
|
|
47
64
|
onInteractOutside={dismiss.onInteractOutside}
|
|
48
65
|
>
|
|
49
66
|
<div class="zylem-dialog-header">
|
|
50
|
-
<Show when={props.title}>
|
|
51
|
-
<
|
|
52
|
-
{props.title}
|
|
53
|
-
|
|
67
|
+
<Show when={props.title || props.subtitle}>
|
|
68
|
+
<div class="zylem-dialog-titles">
|
|
69
|
+
<Show when={props.title}>
|
|
70
|
+
<KDialog.Title class="zylem-dialog-title">{props.title}</KDialog.Title>
|
|
71
|
+
</Show>
|
|
72
|
+
<Show when={props.subtitle}>
|
|
73
|
+
<p class="zylem-dialog-subtitle">{props.subtitle}</p>
|
|
74
|
+
</Show>
|
|
75
|
+
</div>
|
|
76
|
+
</Show>
|
|
77
|
+
<Show when={props.headerExtra}>
|
|
78
|
+
<div class="zylem-dialog-header-extra">{props.headerExtra}</div>
|
|
54
79
|
</Show>
|
|
55
80
|
<WindowControls onClose={() => props.onOpenChange?.(false)} />
|
|
56
81
|
</div>
|
|
57
|
-
<KDialog.Description
|
|
58
|
-
|
|
82
|
+
<KDialog.Description
|
|
83
|
+
as="div"
|
|
84
|
+
class={props.bodyClass ? `zylem-dialog-body ${props.bodyClass}` : 'zylem-dialog-body'}
|
|
85
|
+
>
|
|
86
|
+
<LayerScope class="zylem-dialog-scope">{props.children}</LayerScope>
|
|
59
87
|
</KDialog.Description>
|
|
60
88
|
<Show when={props.footer}>
|
|
61
89
|
<div class="zylem-dialog-footer">{props.footer}</div>
|
|
@@ -61,6 +61,28 @@ globalStyle('.zylem-dropdown-item--danger[data-highlighted]', {
|
|
|
61
61
|
color: '#FFD9D3',
|
|
62
62
|
});
|
|
63
63
|
|
|
64
|
+
globalStyle('.zylem-dropdown-label', {
|
|
65
|
+
flex: '1 1 auto',
|
|
66
|
+
minWidth: 0,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// Check column, reserved on every row of a menu that has a checkable item.
|
|
70
|
+
globalStyle('.zylem-dropdown-check', {
|
|
71
|
+
display: 'inline-flex',
|
|
72
|
+
alignItems: 'center',
|
|
73
|
+
justifyContent: 'center',
|
|
74
|
+
flex: 'none',
|
|
75
|
+
width: '14px',
|
|
76
|
+
height: '14px',
|
|
77
|
+
marginRight: `calc(${vars.spacing.xs} * -1)`,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
globalStyle('.zylem-dropdown-check svg', {
|
|
81
|
+
width: '13px',
|
|
82
|
+
height: '13px',
|
|
83
|
+
stroke: 'currentColor',
|
|
84
|
+
});
|
|
85
|
+
|
|
64
86
|
globalStyle('.zylem-dropdown-shortcut', {
|
|
65
87
|
fontSize: `calc(${vars.typography.fontSize} - 3px)`,
|
|
66
88
|
color: vars.colors.textSecondary,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DropdownMenu as KDropdownMenu } from '@kobalte/core';
|
|
2
|
+
import Check from 'lucide-solid/icons/check';
|
|
2
3
|
import { For, Show } from 'solid-js';
|
|
3
4
|
import type { JSX } from 'solid-js';
|
|
4
5
|
import { createDismissGuard, useLayer } from '../../layers';
|
|
@@ -10,6 +11,12 @@ export interface DropdownMenuItemConfig {
|
|
|
10
11
|
shortcut?: string;
|
|
11
12
|
disabled?: boolean;
|
|
12
13
|
danger?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Render as a checkable item showing this state. Selecting it still calls
|
|
16
|
+
* `onSelect`; the owner decides what the new state is (a plain toggle, or
|
|
17
|
+
* one-of-many the way a stage picker works).
|
|
18
|
+
*/
|
|
19
|
+
checked?: boolean;
|
|
13
20
|
/** Render a separator line above this item. */
|
|
14
21
|
separatorAbove?: boolean;
|
|
15
22
|
}
|
|
@@ -24,40 +31,80 @@ export interface DropdownMenuProps {
|
|
|
24
31
|
}
|
|
25
32
|
|
|
26
33
|
/**
|
|
27
|
-
*
|
|
34
|
+
* The rows of a menu. Shared by `DropdownMenu` and `Menubar`, whose Kobalte
|
|
35
|
+
* parts are the same underlying menu primitives, so it may be rendered inside
|
|
36
|
+
* either one's content.
|
|
28
37
|
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
38
|
+
* A menu with any checkable row reserves the check column on every row so
|
|
39
|
+
* labels line up.
|
|
31
40
|
*/
|
|
32
|
-
export function
|
|
33
|
-
const
|
|
34
|
-
const dismiss = createDismissGuard();
|
|
41
|
+
export function MenuItems(props: { items: DropdownMenuItemConfig[] }) {
|
|
42
|
+
const hasChecks = () => props.items.some((item) => item.checked !== undefined);
|
|
35
43
|
|
|
36
|
-
const
|
|
44
|
+
const body = (item: DropdownMenuItemConfig) => (
|
|
45
|
+
<>
|
|
46
|
+
<Show when={hasChecks()}>
|
|
47
|
+
<span class="zylem-dropdown-check" aria-hidden="true">
|
|
48
|
+
<Show when={item.checked !== undefined}>
|
|
49
|
+
<KDropdownMenu.ItemIndicator>
|
|
50
|
+
<Check />
|
|
51
|
+
</KDropdownMenu.ItemIndicator>
|
|
52
|
+
</Show>
|
|
53
|
+
</span>
|
|
54
|
+
</Show>
|
|
55
|
+
<span class="zylem-dropdown-label">{item.label}</span>
|
|
56
|
+
<Show when={item.shortcut}>
|
|
57
|
+
<span class="zylem-dropdown-shortcut">{item.shortcut}</span>
|
|
58
|
+
</Show>
|
|
59
|
+
</>
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const itemClass = (item: DropdownMenuItemConfig) =>
|
|
63
|
+
item.danger ? 'zylem-dropdown-item zylem-dropdown-item--danger' : 'zylem-dropdown-item';
|
|
64
|
+
|
|
65
|
+
return (
|
|
37
66
|
<For each={props.items}>
|
|
38
67
|
{(item) => (
|
|
39
68
|
<>
|
|
40
69
|
<Show when={item.separatorAbove}>
|
|
41
70
|
<KDropdownMenu.Separator class="zylem-dropdown-separator" />
|
|
42
71
|
</Show>
|
|
43
|
-
<
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
72
|
+
<Show
|
|
73
|
+
when={item.checked !== undefined}
|
|
74
|
+
fallback={
|
|
75
|
+
<KDropdownMenu.Item
|
|
76
|
+
class={itemClass(item)}
|
|
77
|
+
disabled={item.disabled}
|
|
78
|
+
onSelect={item.onSelect}
|
|
79
|
+
>
|
|
80
|
+
{body(item)}
|
|
81
|
+
</KDropdownMenu.Item>
|
|
48
82
|
}
|
|
49
|
-
disabled={item.disabled}
|
|
50
|
-
onSelect={item.onSelect}
|
|
51
83
|
>
|
|
52
|
-
<
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
84
|
+
<KDropdownMenu.CheckboxItem
|
|
85
|
+
class={itemClass(item)}
|
|
86
|
+
checked={item.checked}
|
|
87
|
+
disabled={item.disabled}
|
|
88
|
+
onSelect={item.onSelect}
|
|
89
|
+
>
|
|
90
|
+
{body(item)}
|
|
91
|
+
</KDropdownMenu.CheckboxItem>
|
|
92
|
+
</Show>
|
|
57
93
|
</>
|
|
58
94
|
)}
|
|
59
95
|
</For>
|
|
60
96
|
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* HyperGlass dropdown menu: floating glass sheet with highlight rows.
|
|
101
|
+
*
|
|
102
|
+
* Portals into the layer container for its tree at the `menu` tier, so it opens
|
|
103
|
+
* over panels and dialogs and stays styled inside a shadow root.
|
|
104
|
+
*/
|
|
105
|
+
export function DropdownMenu(props: DropdownMenuProps) {
|
|
106
|
+
const layer = useLayer('menu');
|
|
107
|
+
const dismiss = createDismissGuard();
|
|
61
108
|
|
|
62
109
|
return (
|
|
63
110
|
<KDropdownMenu.Root placement={props.placement ?? 'bottom-start'}>
|
|
@@ -72,7 +119,7 @@ export function DropdownMenu(props: DropdownMenuProps) {
|
|
|
72
119
|
class="zylem-dropdown-content"
|
|
73
120
|
onInteractOutside={dismiss.onInteractOutside}
|
|
74
121
|
>
|
|
75
|
-
{items
|
|
122
|
+
<MenuItems items={props.items} />
|
|
76
123
|
</KDropdownMenu.Content>
|
|
77
124
|
</KDropdownMenu.Portal>
|
|
78
125
|
)}
|
|
@@ -27,3 +27,12 @@ globalStyle('.zylem-empty-state-message', {
|
|
|
27
27
|
lineHeight: 1.45,
|
|
28
28
|
maxWidth: '360px',
|
|
29
29
|
});
|
|
30
|
+
|
|
31
|
+
// One or several actions side by side, wrapping on narrow hosts.
|
|
32
|
+
globalStyle('.zylem-empty-state-actions', {
|
|
33
|
+
display: 'flex',
|
|
34
|
+
flexWrap: 'wrap',
|
|
35
|
+
justifyContent: 'center',
|
|
36
|
+
gap: vars.spacing.sm,
|
|
37
|
+
marginTop: vars.spacing.xs,
|
|
38
|
+
});
|
|
@@ -26,7 +26,9 @@ export function EmptyState(props: EmptyStateProps) {
|
|
|
26
26
|
<Show when={props.children}>
|
|
27
27
|
<div class="zylem-empty-state-message">{props.children}</div>
|
|
28
28
|
</Show>
|
|
29
|
-
{props.action}
|
|
29
|
+
<Show when={props.action}>
|
|
30
|
+
<div class="zylem-empty-state-actions">{props.action}</div>
|
|
31
|
+
</Show>
|
|
30
32
|
</div>
|
|
31
33
|
);
|
|
32
34
|
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { globalStyle } from '@vanilla-extract/css';
|
|
2
|
+
import { vars } from '../../theme.css';
|
|
3
|
+
import { FRAME_SIZE } from '../AddTile/AddTile.css';
|
|
4
|
+
import { checkerBackground } from '../PixelGrid/PixelGrid.css';
|
|
5
|
+
import { artCaption } from '../ToolGroup/ToolGroup.css';
|
|
6
|
+
|
|
7
|
+
globalStyle('.zylem-frame-strip', {
|
|
8
|
+
display: 'flex',
|
|
9
|
+
flexDirection: 'column',
|
|
10
|
+
gap: vars.spacing.sm,
|
|
11
|
+
minWidth: 0,
|
|
12
|
+
fontFamily: vars.typography.fontFamily,
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
globalStyle('.zylem-frame-strip-header', {
|
|
16
|
+
display: 'flex',
|
|
17
|
+
alignItems: 'baseline',
|
|
18
|
+
gap: vars.spacing.sm,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
globalStyle('.zylem-frame-strip-label', artCaption);
|
|
22
|
+
|
|
23
|
+
globalStyle('.zylem-frame-strip-title', {
|
|
24
|
+
fontSize: `calc(${vars.typography.fontSize} - 1px)`,
|
|
25
|
+
fontWeight: 600,
|
|
26
|
+
color: vars.colors.text,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
globalStyle('.zylem-frame-strip-row', {
|
|
30
|
+
display: 'flex',
|
|
31
|
+
alignItems: 'flex-start',
|
|
32
|
+
gap: vars.spacing.sm,
|
|
33
|
+
padding: `${vars.spacing.xs} ${vars.spacing.xs} ${vars.spacing.sm}`,
|
|
34
|
+
overflowX: 'auto',
|
|
35
|
+
overflowY: 'hidden',
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Thumb: checker surface with the frame number in a glass badge beneath.
|
|
39
|
+
globalStyle('.zylem-frame-thumb', {
|
|
40
|
+
boxSizing: 'border-box',
|
|
41
|
+
position: 'relative',
|
|
42
|
+
display: 'flex',
|
|
43
|
+
flexDirection: 'column',
|
|
44
|
+
flex: 'none',
|
|
45
|
+
width: FRAME_SIZE,
|
|
46
|
+
height: `calc(${FRAME_SIZE} + 18px)`,
|
|
47
|
+
padding: 0,
|
|
48
|
+
margin: 0,
|
|
49
|
+
cursor: 'pointer',
|
|
50
|
+
overflow: 'hidden',
|
|
51
|
+
borderRadius: vars.radii.control,
|
|
52
|
+
border: '1px solid rgba(150, 210, 255, 0.4)',
|
|
53
|
+
background: vars.material.fieldGlass,
|
|
54
|
+
boxShadow: vars.effects.shadowInset,
|
|
55
|
+
color: '#E8F4FF',
|
|
56
|
+
fontFamily: vars.typography.fontFamily,
|
|
57
|
+
transition: `border-color ${vars.motion.fast} ${vars.motion.easeOut}, box-shadow ${vars.motion.fast} ${vars.motion.easeOut}, transform ${vars.motion.fast} ${vars.motion.easeOut}`,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
globalStyle('.zylem-frame-thumb:hover', {
|
|
61
|
+
borderColor: 'rgba(178, 219, 255, 0.8)',
|
|
62
|
+
transform: 'translateY(-1px)',
|
|
63
|
+
boxShadow: `${vars.effects.shadowInset}, ${vars.effects.glowPrimary}`,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
globalStyle('.zylem-frame-thumb:focus-visible', {
|
|
67
|
+
outline: 'none',
|
|
68
|
+
boxShadow: `${vars.effects.shadowInset}, ${vars.effects.focusRing}`,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
globalStyle('.zylem-frame-thumb[data-selected]', {
|
|
72
|
+
borderColor: 'rgba(178, 219, 255, 0.95)',
|
|
73
|
+
boxShadow: `${vars.effects.shadowInset}, 0 0 0 1px rgba(178, 219, 255, 0.6), ${vars.effects.glowPrimary}`,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
globalStyle('.zylem-frame-thumb-surface', {
|
|
77
|
+
position: 'relative',
|
|
78
|
+
display: 'block',
|
|
79
|
+
flex: 'none',
|
|
80
|
+
width: '100%',
|
|
81
|
+
aspectRatio: '1 / 1',
|
|
82
|
+
vars: { '--zylem-pixel-grid-cell': '6px' },
|
|
83
|
+
background: checkerBackground,
|
|
84
|
+
imageRendering: 'pixelated',
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
globalStyle(
|
|
88
|
+
'.zylem-frame-thumb-surface > canvas, .zylem-frame-thumb-surface > img, .zylem-frame-thumb-surface > svg',
|
|
89
|
+
{
|
|
90
|
+
position: 'absolute',
|
|
91
|
+
inset: 0,
|
|
92
|
+
width: '100%',
|
|
93
|
+
height: '100%',
|
|
94
|
+
display: 'block',
|
|
95
|
+
imageRendering: 'pixelated',
|
|
96
|
+
}
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
globalStyle('.zylem-frame-thumb-index', {
|
|
100
|
+
display: 'flex',
|
|
101
|
+
alignItems: 'center',
|
|
102
|
+
justifyContent: 'center',
|
|
103
|
+
flex: '1 1 auto',
|
|
104
|
+
fontSize: '11px',
|
|
105
|
+
fontWeight: 600,
|
|
106
|
+
fontVariantNumeric: 'tabular-nums',
|
|
107
|
+
lineHeight: 1,
|
|
108
|
+
background: vars.material.panelHeaderGloss,
|
|
109
|
+
borderTop: '1px solid rgba(150, 210, 255, 0.25)',
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Actions row: commands left, step arrows right, all vertically centered.
|
|
113
|
+
globalStyle('.zylem-frame-actions', {
|
|
114
|
+
display: 'flex',
|
|
115
|
+
alignItems: 'center',
|
|
116
|
+
gap: vars.spacing.sm,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
globalStyle('.zylem-frame-actions .zylem-button .zylem-icon', {
|
|
120
|
+
width: '14px',
|
|
121
|
+
height: '14px',
|
|
122
|
+
stroke: 'currentColor',
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
globalStyle('.zylem-frame-actions-spacer', {
|
|
126
|
+
flex: '1 1 auto',
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
globalStyle('.zylem-frame-actions-group', {
|
|
130
|
+
display: 'inline-flex',
|
|
131
|
+
alignItems: 'center',
|
|
132
|
+
gap: vars.spacing.xs,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
globalStyle('.zylem-frame-actions-step', {
|
|
136
|
+
width: '34px',
|
|
137
|
+
padding: 0,
|
|
138
|
+
});
|