@nqmcreative/ui 0.2.1 → 0.2.3
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 +7 -5
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.js +1 -1
- package/dist/core/trigger.d.ts +6 -0
- package/dist/core/trigger.js +13 -0
- package/dist/styles/matte/Button.svelte +4 -4
- package/dist/styles/matte/Card.svelte +3 -3
- package/dist/styles/matte/Combobox.svelte +2 -2
- package/dist/styles/matte/CommandPalette.svelte +32 -7
- package/dist/styles/matte/CommandPalette.svelte.d.ts +5 -0
- package/dist/styles/matte/DatePicker.svelte +2 -2
- package/dist/styles/matte/Drawer.svelte +2 -2
- package/dist/styles/matte/Dropzone.svelte +1 -1
- package/dist/styles/matte/FeatureCard.svelte +1 -1
- package/dist/styles/matte/Input.svelte +4 -4
- package/dist/styles/matte/MenuItem.svelte +1 -1
- package/dist/styles/matte/Modal.svelte +3 -3
- package/dist/styles/matte/NumberInput.svelte +2 -2
- package/dist/styles/matte/PasswordInput.svelte +2 -2
- package/dist/styles/matte/PricingCard.svelte +3 -3
- package/dist/styles/matte/SegmentedControl.svelte +1 -1
- package/dist/styles/matte/Select.svelte +1 -1
- package/dist/styles/matte/Sidebar.svelte +75 -26
- package/dist/styles/matte/Sidebar.svelte.d.ts +15 -1
- package/dist/styles/matte/StatsBand.svelte +1 -1
- package/dist/styles/matte/Table.svelte +3 -3
- package/dist/styles/matte/Testimonial.svelte +2 -2
- package/dist/styles/matte/Textarea.svelte +1 -1
- package/dist/styles/matte/index.d.ts +1 -1
- package/dist/styles/paper/CommandPalette.svelte +25 -2
- package/dist/styles/paper/CommandPalette.svelte.d.ts +5 -0
- package/dist/styles/paper/Modal.svelte +9 -1
- package/dist/styles/paper/Sidebar.svelte +75 -26
- package/dist/styles/paper/Sidebar.svelte.d.ts +15 -1
- package/dist/styles/paper/fonts.css +34 -7
- package/dist/styles/paper/index.d.ts +1 -1
- package/dist/styles/paper/theme.css +11 -8
- package/dist/styles/sprout/Accordion.svelte +17 -0
- package/dist/styles/sprout/Accordion.svelte.d.ts +8 -0
- package/dist/styles/sprout/AccordionItem.svelte +46 -0
- package/dist/styles/sprout/AccordionItem.svelte.d.ts +14 -0
- package/dist/styles/sprout/Alert.svelte +100 -0
- package/dist/styles/sprout/Alert.svelte.d.ts +18 -0
- package/dist/styles/sprout/Avatar.svelte +60 -0
- package/dist/styles/sprout/Avatar.svelte.d.ts +16 -0
- package/dist/styles/sprout/AvatarGroup.svelte +17 -0
- package/dist/styles/sprout/AvatarGroup.svelte.d.ts +8 -0
- package/dist/styles/sprout/Badge.svelte +58 -0
- package/dist/styles/sprout/Badge.svelte.d.ts +16 -0
- package/dist/styles/sprout/Breadcrumb.svelte +49 -0
- package/dist/styles/sprout/Breadcrumb.svelte.d.ts +14 -0
- package/dist/styles/sprout/Button.svelte +105 -0
- package/dist/styles/sprout/Button.svelte.d.ts +21 -0
- package/dist/styles/sprout/CTASection.svelte +77 -0
- package/dist/styles/sprout/CTASection.svelte.d.ts +18 -0
- package/dist/styles/sprout/Calendar.svelte +202 -0
- package/dist/styles/sprout/Calendar.svelte.d.ts +22 -0
- package/dist/styles/sprout/Card.svelte +82 -0
- package/dist/styles/sprout/Card.svelte.d.ts +21 -0
- package/dist/styles/sprout/Checkbox.svelte +76 -0
- package/dist/styles/sprout/Checkbox.svelte.d.ts +15 -0
- package/dist/styles/sprout/CheckboxGroup.svelte +112 -0
- package/dist/styles/sprout/CheckboxGroup.svelte.d.ts +30 -0
- package/dist/styles/sprout/Combobox.svelte +267 -0
- package/dist/styles/sprout/Combobox.svelte.d.ts +29 -0
- package/dist/styles/sprout/CommandPalette.svelte +233 -0
- package/dist/styles/sprout/CommandPalette.svelte.d.ts +37 -0
- package/dist/styles/sprout/ConfirmDialog.svelte +97 -0
- package/dist/styles/sprout/ConfirmDialog.svelte.d.ts +22 -0
- package/dist/styles/sprout/ContextMenu.svelte +89 -0
- package/dist/styles/sprout/ContextMenu.svelte.d.ts +15 -0
- package/dist/styles/sprout/DatePicker.svelte +193 -0
- package/dist/styles/sprout/DatePicker.svelte.d.ts +23 -0
- package/dist/styles/sprout/Divider.svelte +30 -0
- package/dist/styles/sprout/Divider.svelte.d.ts +10 -0
- package/dist/styles/sprout/Drawer.svelte +166 -0
- package/dist/styles/sprout/Drawer.svelte.d.ts +20 -0
- package/dist/styles/sprout/Dropdown.svelte +95 -0
- package/dist/styles/sprout/Dropdown.svelte.d.ts +23 -0
- package/dist/styles/sprout/Dropzone.svelte +211 -0
- package/dist/styles/sprout/Dropzone.svelte.d.ts +29 -0
- package/dist/styles/sprout/EmptyState.svelte +71 -0
- package/dist/styles/sprout/EmptyState.svelte.d.ts +18 -0
- package/dist/styles/sprout/FeatureCard.svelte +61 -0
- package/dist/styles/sprout/FeatureCard.svelte.d.ts +16 -0
- package/dist/styles/sprout/FeatureGrid.svelte +62 -0
- package/dist/styles/sprout/FeatureGrid.svelte.d.ts +16 -0
- package/dist/styles/sprout/Field.svelte +42 -0
- package/dist/styles/sprout/Field.svelte.d.ts +16 -0
- package/dist/styles/sprout/Footer.svelte +91 -0
- package/dist/styles/sprout/Footer.svelte.d.ts +25 -0
- package/dist/styles/sprout/HeroSection.svelte +86 -0
- package/dist/styles/sprout/HeroSection.svelte.d.ts +21 -0
- package/dist/styles/sprout/Input.svelte +61 -0
- package/dist/styles/sprout/Input.svelte.d.ts +18 -0
- package/dist/styles/sprout/InputGroup.svelte +20 -0
- package/dist/styles/sprout/InputGroup.svelte.d.ts +11 -0
- package/dist/styles/sprout/Kbd.svelte +18 -0
- package/dist/styles/sprout/Kbd.svelte.d.ts +8 -0
- package/dist/styles/sprout/Label.svelte +16 -0
- package/dist/styles/sprout/Label.svelte.d.ts +9 -0
- package/dist/styles/sprout/Link.svelte +44 -0
- package/dist/styles/sprout/Link.svelte.d.ts +16 -0
- package/dist/styles/sprout/LocaleProvider.svelte +24 -0
- package/dist/styles/sprout/LocaleProvider.svelte.d.ts +10 -0
- package/dist/styles/sprout/LogoCloud.svelte +61 -0
- package/dist/styles/sprout/LogoCloud.svelte.d.ts +19 -0
- package/dist/styles/sprout/MenuItem.svelte +64 -0
- package/dist/styles/sprout/MenuItem.svelte.d.ts +18 -0
- package/dist/styles/sprout/MenuSeparator.svelte +22 -0
- package/dist/styles/sprout/MenuSeparator.svelte.d.ts +8 -0
- package/dist/styles/sprout/Modal.svelte +127 -0
- package/dist/styles/sprout/Modal.svelte.d.ts +21 -0
- package/dist/styles/sprout/MultiSelect.svelte +288 -0
- package/dist/styles/sprout/MultiSelect.svelte.d.ts +31 -0
- package/dist/styles/sprout/Navbar.svelte +201 -0
- package/dist/styles/sprout/Navbar.svelte.d.ts +33 -0
- package/dist/styles/sprout/NumberInput.svelte +97 -0
- package/dist/styles/sprout/NumberInput.svelte.d.ts +15 -0
- package/dist/styles/sprout/Pagination.svelte +84 -0
- package/dist/styles/sprout/Pagination.svelte.d.ts +15 -0
- package/dist/styles/sprout/PasswordInput.svelte +101 -0
- package/dist/styles/sprout/PasswordInput.svelte.d.ts +12 -0
- package/dist/styles/sprout/Popover.svelte +90 -0
- package/dist/styles/sprout/Popover.svelte.d.ts +19 -0
- package/dist/styles/sprout/PricingCard.svelte +109 -0
- package/dist/styles/sprout/PricingCard.svelte.d.ts +25 -0
- package/dist/styles/sprout/Progress.svelte +59 -0
- package/dist/styles/sprout/Progress.svelte.d.ts +17 -0
- package/dist/styles/sprout/Radio.svelte +56 -0
- package/dist/styles/sprout/Radio.svelte.d.ts +15 -0
- package/dist/styles/sprout/RadioGroup.svelte +96 -0
- package/dist/styles/sprout/RadioGroup.svelte.d.ts +27 -0
- package/dist/styles/sprout/SegmentedControl.svelte +89 -0
- package/dist/styles/sprout/SegmentedControl.svelte.d.ts +25 -0
- package/dist/styles/sprout/Select.svelte +62 -0
- package/dist/styles/sprout/Select.svelte.d.ts +20 -0
- package/dist/styles/sprout/Sidebar.svelte +255 -0
- package/dist/styles/sprout/Sidebar.svelte.d.ts +50 -0
- package/dist/styles/sprout/Skeleton.svelte +46 -0
- package/dist/styles/sprout/Skeleton.svelte.d.ts +13 -0
- package/dist/styles/sprout/Slider.svelte +80 -0
- package/dist/styles/sprout/Slider.svelte.d.ts +19 -0
- package/dist/styles/sprout/Spinner.svelte +34 -0
- package/dist/styles/sprout/Spinner.svelte.d.ts +13 -0
- package/dist/styles/sprout/Stat.svelte +51 -0
- package/dist/styles/sprout/Stat.svelte.d.ts +16 -0
- package/dist/styles/sprout/StatsBand.svelte +64 -0
- package/dist/styles/sprout/StatsBand.svelte.d.ts +20 -0
- package/dist/styles/sprout/Steps.svelte +143 -0
- package/dist/styles/sprout/Steps.svelte.d.ts +24 -0
- package/dist/styles/sprout/Switch.svelte +65 -0
- package/dist/styles/sprout/Switch.svelte.d.ts +14 -0
- package/dist/styles/sprout/Table.svelte +218 -0
- package/dist/styles/sprout/Table.svelte.d.ts +66 -0
- package/dist/styles/sprout/Tabs.svelte +103 -0
- package/dist/styles/sprout/Tabs.svelte.d.ts +23 -0
- package/dist/styles/sprout/Testimonial.svelte +65 -0
- package/dist/styles/sprout/Testimonial.svelte.d.ts +19 -0
- package/dist/styles/sprout/Textarea.svelte +55 -0
- package/dist/styles/sprout/Textarea.svelte.d.ts +12 -0
- package/dist/styles/sprout/ThemeToggle.svelte +131 -0
- package/dist/styles/sprout/ThemeToggle.svelte.d.ts +16 -0
- package/dist/styles/sprout/Toaster.svelte +133 -0
- package/dist/styles/sprout/Toaster.svelte.d.ts +8 -0
- package/dist/styles/sprout/Tooltip.svelte +34 -0
- package/dist/styles/sprout/Tooltip.svelte.d.ts +12 -0
- package/dist/styles/sprout/fonts.css +77 -0
- package/dist/styles/sprout/index.d.ts +109 -0
- package/dist/styles/sprout/index.js +83 -0
- package/dist/styles/sprout/lift.d.ts +40 -0
- package/dist/styles/sprout/lift.js +36 -0
- package/dist/styles/sprout/theme.css +255 -0
- package/package.json +269 -3
- package/registry.json +139 -66
package/README.md
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
# @nqmcreative/ui
|
|
2
2
|
|
|
3
|
-
Svelte 5 (runes) + Tailwind CSS v4 component library. One package, **
|
|
3
|
+
Svelte 5 (runes) + Tailwind CSS v4 component library. One package, **three
|
|
4
4
|
styles**, 65 components each — the same props, the same behaviour, a different
|
|
5
5
|
look. Eight tones, light and dark, no runtime dependencies.
|
|
6
6
|
|
|
7
|
-
| style
|
|
8
|
-
|
|
|
9
|
-
| `matte`
|
|
10
|
-
| `paper`
|
|
7
|
+
| style | import | character |
|
|
8
|
+
| -------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------ |
|
|
9
|
+
| `matte` | `@nqmcreative/ui/matte` | Flat and sharp-cornered — no radius, no shadow, no gloss. Hairline rules and mono micro-labels do the work borders usually do. |
|
|
10
|
+
| `paper` | `@nqmcreative/ui/paper` | The neutral one. Start here if unsure: soft corners, light shadows, generous spacing, and nothing that fights the content. |
|
|
11
|
+
| `sprout` | `@nqmcreative/ui/sprout` | Warm and fully rounded — leaf green on cream, pill buttons, and a round display face. For school, community and family sites. |
|
|
11
12
|
|
|
12
13
|
The docs site lives in this repo — every component with a live demo, plus
|
|
13
14
|
theming, locale and contributing guides. `bun run dev` to read it.
|
|
@@ -132,6 +133,7 @@ Under SSR use `<LocaleProvider>` instead, so the locale is scoped to a request.
|
|
|
132
133
|
src/lib/core/ behaviour, a11y, types — no markup, no classes
|
|
133
134
|
src/lib/styles/matte/ 65 components
|
|
134
135
|
src/lib/styles/paper/ 65 components
|
|
136
|
+
src/lib/styles/sprout/ 65 components
|
|
135
137
|
scripts/catalogue.mjs the list every style must implement
|
|
136
138
|
```
|
|
137
139
|
|
package/dist/core/index.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export type { Placement, AnchorOptions } from './actions/anchor.js';
|
|
|
15
15
|
export { clickOutside, focusTrap, focusable, navigateList, portal } from './actions/dismissable.js';
|
|
16
16
|
export type { ClickOutsideOptions, FocusTrapOptions } from './actions/dismissable.js';
|
|
17
17
|
export { syncDialog, isBackdropClick } from './dialog.svelte.js';
|
|
18
|
-
export { popupTrigger, describeTrigger, menuItems, HOVER_GRACE } from './trigger.js';
|
|
18
|
+
export { popupTrigger, describeTrigger, menuItems, isTypingTarget, HOVER_GRACE } from './trigger.js';
|
|
19
19
|
export type { PopupTriggerOptions } from './trigger.js';
|
|
20
20
|
export { ListCursor, groupItems, matchQuery, revealIndex } from './list.svelte.js';
|
|
21
21
|
export type { ListItem } from './list.svelte.js';
|
package/dist/core/index.js
CHANGED
|
@@ -15,7 +15,7 @@ export { anchored } from './actions/anchor.js';
|
|
|
15
15
|
export { clickOutside, focusTrap, focusable, navigateList, portal } from './actions/dismissable.js';
|
|
16
16
|
/* --- overlays --- */
|
|
17
17
|
export { syncDialog, isBackdropClick } from './dialog.svelte.js';
|
|
18
|
-
export { popupTrigger, describeTrigger, menuItems, HOVER_GRACE } from './trigger.js';
|
|
18
|
+
export { popupTrigger, describeTrigger, menuItems, isTypingTarget, HOVER_GRACE } from './trigger.js';
|
|
19
19
|
/* --- lists --- */
|
|
20
20
|
export { ListCursor, groupItems, matchQuery, revealIndex } from './list.svelte.js';
|
|
21
21
|
/* --- data --- */
|
package/dist/core/trigger.d.ts
CHANGED
|
@@ -33,3 +33,9 @@ export declare function popupTrigger(node: HTMLElement, options: PopupTriggerOpt
|
|
|
33
33
|
export declare function describeTrigger(wrapper: HTMLElement | null, kind: 'menu' | 'dialog' | 'listbox', open: boolean): void;
|
|
34
34
|
/** The menu items a roving-focus menu should walk, in DOM order. */
|
|
35
35
|
export declare function menuItems(menu: HTMLElement | null): HTMLElement[];
|
|
36
|
+
/**
|
|
37
|
+
* Whether a keystroke landed in something the reader is typing into. A
|
|
38
|
+
* bare-key shortcut — `/` for search — has to stand down for those, or it
|
|
39
|
+
* eats the character instead of opening anything.
|
|
40
|
+
*/
|
|
41
|
+
export declare function isTypingTarget(target: EventTarget | null): boolean;
|
package/dist/core/trigger.js
CHANGED
|
@@ -75,3 +75,16 @@ export function describeTrigger(wrapper, kind, open) {
|
|
|
75
75
|
export function menuItems(menu) {
|
|
76
76
|
return menu ? [...menu.querySelectorAll('[role="menuitem"]:not([disabled])')] : [];
|
|
77
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* Whether a keystroke landed in something the reader is typing into. A
|
|
80
|
+
* bare-key shortcut — `/` for search — has to stand down for those, or it
|
|
81
|
+
* eats the character instead of opening anything.
|
|
82
|
+
*/
|
|
83
|
+
export function isTypingTarget(target) {
|
|
84
|
+
if (!(target instanceof HTMLElement))
|
|
85
|
+
return false;
|
|
86
|
+
return (target.isContentEditable ||
|
|
87
|
+
target.tagName === 'INPUT' ||
|
|
88
|
+
target.tagName === 'TEXTAREA' ||
|
|
89
|
+
target.tagName === 'SELECT');
|
|
90
|
+
}
|
|
@@ -49,10 +49,10 @@
|
|
|
49
49
|
'inline-flex items-center justify-center gap-2 border font-sans font-medium whitespace-nowrap transition-all duration-150 ease-brand-out disabled:opacity-50 disabled:pointer-events-none aria-disabled:opacity-50 aria-disabled:pointer-events-none';
|
|
50
50
|
|
|
51
51
|
const sizes: Record<ButtonSize, string> = {
|
|
52
|
-
sm: 'h-
|
|
53
|
-
md: 'h-
|
|
54
|
-
lg: 'h-
|
|
55
|
-
xl: 'h-
|
|
52
|
+
sm: 'h-8 px-3.5 text-[13px]',
|
|
53
|
+
md: 'h-10 px-5 text-[15px]',
|
|
54
|
+
lg: 'h-11 px-6 text-base',
|
|
55
|
+
xl: 'h-12 px-8 text-base'
|
|
56
56
|
};
|
|
57
57
|
|
|
58
58
|
const variantClass = $derived.by(() => {
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
{...rest}
|
|
51
51
|
>
|
|
52
52
|
{#if header || title || eyebrow}
|
|
53
|
-
<div class="flex flex-col gap-1 {padded ? 'px-
|
|
53
|
+
<div class="flex flex-col gap-1 {padded ? 'px-5 pt-5' : ''}">
|
|
54
54
|
{#if header}
|
|
55
55
|
{@render header()}
|
|
56
56
|
{:else}
|
|
@@ -64,12 +64,12 @@
|
|
|
64
64
|
</div>
|
|
65
65
|
{/if}
|
|
66
66
|
|
|
67
|
-
<div class="flex-1 {padded ? (header || title || eyebrow ? 'px-
|
|
67
|
+
<div class="flex-1 {padded ? (header || title || eyebrow ? 'px-5 pt-3.5 pb-5' : 'p-5') : ''}">
|
|
68
68
|
{@render children()}
|
|
69
69
|
</div>
|
|
70
70
|
|
|
71
71
|
{#if footer}
|
|
72
|
-
<div class="border-t border-hairline {padded ? 'px-
|
|
72
|
+
<div class="border-t border-hairline {padded ? 'px-5 py-3.5' : ''}">
|
|
73
73
|
{@render footer()}
|
|
74
74
|
</div>
|
|
75
75
|
{/if}
|
|
@@ -142,7 +142,7 @@
|
|
|
142
142
|
|
|
143
143
|
<div
|
|
144
144
|
bind:this={wrapper}
|
|
145
|
-
class="relative inline-flex w-full items-center gap-2 border bg-bg pr-2 pl-
|
|
145
|
+
class="relative inline-flex w-full items-center gap-2 border bg-bg pr-2 pl-3.5 transition-colors duration-150 ease-brand-out
|
|
146
146
|
{invalid ? 'border-danger' : `border-hairline ${toneFocusWithinBorder[tone]}`}
|
|
147
147
|
{disabled ? 'pointer-events-none opacity-50' : ''} {className}"
|
|
148
148
|
>
|
|
@@ -166,7 +166,7 @@
|
|
|
166
166
|
}}
|
|
167
167
|
onfocus={openList}
|
|
168
168
|
onkeydown={onKeydown}
|
|
169
|
-
class="h-
|
|
169
|
+
class="h-10 w-full min-w-0 bg-transparent font-sans text-[15px] text-text placeholder:text-text-muted focus:outline-none"
|
|
170
170
|
/>
|
|
171
171
|
|
|
172
172
|
{#if name}<input type="hidden" {name} {value} />{/if}
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
import { ListCursor, groupItems, matchQuery, revealIndex } from '../../core/list.svelte.js';
|
|
22
22
|
import { useLocale } from '../../core/locale.svelte.js';
|
|
23
23
|
import { toneSoft, type Tone } from '../../core/tones.js';
|
|
24
|
+
import { isTypingTarget } from '../../core/trigger.js';
|
|
24
25
|
import Kbd from './Kbd.svelte';
|
|
25
26
|
|
|
26
27
|
interface Props {
|
|
@@ -34,6 +35,11 @@
|
|
|
34
35
|
* Pass `null` to wire your own trigger only.
|
|
35
36
|
*/
|
|
36
37
|
hotkey?: string | null;
|
|
38
|
+
/**
|
|
39
|
+
* Key that opens the palette on its own, pressed anywhere the reader
|
|
40
|
+
* isn't typing. Default `'/'`. Pass `null` to turn it off.
|
|
41
|
+
*/
|
|
42
|
+
quickKey?: string | null;
|
|
37
43
|
onselect?: (item: CommandItem) => void;
|
|
38
44
|
class?: string;
|
|
39
45
|
}
|
|
@@ -45,6 +51,7 @@
|
|
|
45
51
|
emptyText,
|
|
46
52
|
tone = 'brand',
|
|
47
53
|
hotkey = 'k',
|
|
54
|
+
quickKey = '/',
|
|
48
55
|
onselect,
|
|
49
56
|
class: className = ''
|
|
50
57
|
}: Props = $props();
|
|
@@ -82,11 +89,27 @@
|
|
|
82
89
|
});
|
|
83
90
|
|
|
84
91
|
$effect(() => {
|
|
85
|
-
if (!hotkey) return;
|
|
92
|
+
if (!hotkey && !quickKey) return;
|
|
86
93
|
const onKeydown = (event: KeyboardEvent) => {
|
|
87
|
-
|
|
94
|
+
const key = event.key.toLowerCase();
|
|
95
|
+
|
|
96
|
+
if ((event.metaKey || event.ctrlKey) && key === hotkey?.toLowerCase()) {
|
|
88
97
|
event.preventDefault();
|
|
89
98
|
open = !open;
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// A bare key can only open the palette, never close it: while it is
|
|
103
|
+
// open the keystroke belongs to the query field.
|
|
104
|
+
if (
|
|
105
|
+
key === quickKey?.toLowerCase() &&
|
|
106
|
+
!event.metaKey &&
|
|
107
|
+
!event.ctrlKey &&
|
|
108
|
+
!event.altKey &&
|
|
109
|
+
!isTypingTarget(event.target)
|
|
110
|
+
) {
|
|
111
|
+
event.preventDefault();
|
|
112
|
+
open = true;
|
|
90
113
|
}
|
|
91
114
|
};
|
|
92
115
|
window.addEventListener('keydown', onKeydown);
|
|
@@ -138,7 +161,7 @@
|
|
|
138
161
|
class="mx-auto mt-[12vh] mb-auto w-[min(36rem,calc(100vw-2rem))] max-w-none border border-hairline bg-bg p-0 text-text backdrop:bg-text/40 {className}"
|
|
139
162
|
>
|
|
140
163
|
<div use:focusTrap={{ autofocus: false }} onkeydown={onKeydown} role="none" class="flex flex-col">
|
|
141
|
-
<div class="flex items-center gap-3 border-b border-hairline px-
|
|
164
|
+
<div class="flex items-center gap-3 border-b border-hairline px-3.5">
|
|
142
165
|
<svg
|
|
143
166
|
class="size-4 shrink-0 text-text-muted"
|
|
144
167
|
viewBox="0 0 16 16"
|
|
@@ -156,7 +179,7 @@
|
|
|
156
179
|
autocomplete="off"
|
|
157
180
|
aria-label={placeholder ?? t.current.commandPlaceholder}
|
|
158
181
|
placeholder={placeholder ?? t.current.commandPlaceholder}
|
|
159
|
-
class="h-
|
|
182
|
+
class="h-11 w-full min-w-0 bg-transparent font-sans text-[15px] text-text placeholder:text-text-muted focus:outline-none"
|
|
160
183
|
/>
|
|
161
184
|
<Kbd>esc</Kbd>
|
|
162
185
|
</div>
|
|
@@ -168,13 +191,15 @@
|
|
|
168
191
|
class="max-h-80 overflow-y-auto py-1"
|
|
169
192
|
>
|
|
170
193
|
{#if flat.length === 0}
|
|
171
|
-
<p class="px-
|
|
194
|
+
<p class="px-3.5 py-10 text-center font-sans text-sm text-text-muted">
|
|
172
195
|
{emptyText ?? t.current.noResults}
|
|
173
196
|
</p>
|
|
174
197
|
{:else}
|
|
175
198
|
{#each groups as [group, groupItems] (group)}
|
|
176
199
|
{#if group}
|
|
177
|
-
<p
|
|
200
|
+
<p
|
|
201
|
+
class="px-3.5 pt-3 pb-1 font-mono text-[10px] tracking-wide text-text-muted uppercase"
|
|
202
|
+
>
|
|
178
203
|
{group}
|
|
179
204
|
</p>
|
|
180
205
|
{/if}
|
|
@@ -189,7 +214,7 @@
|
|
|
189
214
|
aria-disabled={item.disabled}
|
|
190
215
|
onclick={() => run(item)}
|
|
191
216
|
onpointermove={() => (cursor.index = index)}
|
|
192
|
-
class="flex cursor-pointer items-center gap-3 px-
|
|
217
|
+
class="flex cursor-pointer items-center gap-3 px-3.5 py-2 font-sans text-[14px] transition-colors duration-100
|
|
193
218
|
{item.disabled ? 'pointer-events-none opacity-40' : ''}
|
|
194
219
|
{index === active ? toneSoft[tone] : 'text-text-secondary'}"
|
|
195
220
|
>
|
|
@@ -24,6 +24,11 @@ interface Props {
|
|
|
24
24
|
* Pass `null` to wire your own trigger only.
|
|
25
25
|
*/
|
|
26
26
|
hotkey?: string | null;
|
|
27
|
+
/**
|
|
28
|
+
* Key that opens the palette on its own, pressed anywhere the reader
|
|
29
|
+
* isn't typing. Default `'/'`. Pass `null` to turn it off.
|
|
30
|
+
*/
|
|
31
|
+
quickKey?: string | null;
|
|
27
32
|
onselect?: (item: CommandItem) => void;
|
|
28
33
|
class?: string;
|
|
29
34
|
}
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
|
|
95
95
|
<div
|
|
96
96
|
bind:this={wrapper}
|
|
97
|
-
class="inline-flex w-full items-center gap-1 border bg-bg pr-1 pl-
|
|
97
|
+
class="inline-flex w-full items-center gap-1 border bg-bg pr-1 pl-3.5 transition-colors duration-150 ease-brand-out
|
|
98
98
|
{invalid ? 'border-danger' : `border-hairline ${toneFocusWithinBorder[tone]}`}
|
|
99
99
|
{disabled ? 'pointer-events-none opacity-50' : ''} {className}"
|
|
100
100
|
>
|
|
@@ -118,7 +118,7 @@
|
|
|
118
118
|
}}
|
|
119
119
|
onblur={commit}
|
|
120
120
|
onkeydown={onKeydown}
|
|
121
|
-
class="h-
|
|
121
|
+
class="h-10 w-full min-w-0 bg-transparent font-sans text-[15px] text-text tabular-nums placeholder:text-text-muted focus:outline-none"
|
|
122
122
|
/>
|
|
123
123
|
|
|
124
124
|
{#if name}<input type="hidden" {name} {value} />{/if}
|
|
@@ -91,7 +91,7 @@
|
|
|
91
91
|
>
|
|
92
92
|
<div class="flex h-full flex-col">
|
|
93
93
|
{#if title || dismissible}
|
|
94
|
-
<div class="flex items-start gap-4 border-b border-hairline px-
|
|
94
|
+
<div class="flex items-start gap-4 border-b border-hairline px-5 py-4">
|
|
95
95
|
<div class="flex min-w-0 flex-1 flex-col gap-1">
|
|
96
96
|
{#if title}
|
|
97
97
|
<h2 class="font-heading text-lg font-medium tracking-tight">{title}</h2>
|
|
@@ -116,7 +116,7 @@
|
|
|
116
116
|
{/if}
|
|
117
117
|
|
|
118
118
|
<div
|
|
119
|
-
class="flex-1 overflow-y-auto px-
|
|
119
|
+
class="flex-1 overflow-y-auto px-5 py-4 font-sans text-[15px] leading-relaxed text-text-secondary"
|
|
120
120
|
>
|
|
121
121
|
{@render children()}
|
|
122
122
|
</div>
|
|
@@ -173,7 +173,7 @@
|
|
|
173
173
|
<ul class="flex flex-col border border-hairline">
|
|
174
174
|
{#each files as file, i (file.name + file.size + i)}
|
|
175
175
|
<li
|
|
176
|
-
class="flex items-center gap-3 border-b border-hairline px-
|
|
176
|
+
class="flex items-center gap-3 border-b border-hairline px-3.5 py-2.5 font-sans last:border-b-0"
|
|
177
177
|
>
|
|
178
178
|
<span class="min-w-0 flex-1 truncate text-sm text-text">{file.name}</span>
|
|
179
179
|
<span class="shrink-0 font-mono text-xs text-text-muted">{formatSize(file.size)}</span>
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
<svelte:element
|
|
30
30
|
this={href ? 'a' : 'div'}
|
|
31
31
|
{href}
|
|
32
|
-
class="group flex flex-col gap-3 bg-bg p-
|
|
32
|
+
class="group flex flex-col gap-3 bg-bg p-5 {href
|
|
33
33
|
? `transition-colors duration-150 ease-brand-out ${focusRing} ${toneRing[tone]}`
|
|
34
34
|
: ''} {className}"
|
|
35
35
|
{...rest}
|
|
@@ -30,14 +30,14 @@
|
|
|
30
30
|
}: Props = $props();
|
|
31
31
|
|
|
32
32
|
const sizes: Record<InputSize, string> = {
|
|
33
|
-
sm: 'h-
|
|
34
|
-
md: 'h-
|
|
35
|
-
lg: 'h-
|
|
33
|
+
sm: 'h-8 text-[13px]',
|
|
34
|
+
md: 'h-10 text-[15px]',
|
|
35
|
+
lg: 'h-11 text-base'
|
|
36
36
|
};
|
|
37
37
|
</script>
|
|
38
38
|
|
|
39
39
|
<span
|
|
40
|
-
class="inline-flex w-full items-center gap-2 border bg-bg px-
|
|
40
|
+
class="inline-flex w-full items-center gap-2 border bg-bg px-3.5 transition-colors duration-150 ease-brand-out
|
|
41
41
|
{sizes[size]}
|
|
42
42
|
{invalid
|
|
43
43
|
? 'border-danger focus-within:border-danger'
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
disabled={href ? undefined : disabled}
|
|
38
38
|
aria-disabled={disabled ? 'true' : undefined}
|
|
39
39
|
tabindex={disabled ? undefined : -1}
|
|
40
|
-
class="flex w-full items-center gap-2.5 px-3 py-
|
|
40
|
+
class="flex w-full items-center gap-2.5 px-3 py-1.5 text-left text-[14px] transition-colors duration-100 ease-brand-out focus:outline-none disabled:pointer-events-none disabled:opacity-40
|
|
41
41
|
{tone === 'neutral'
|
|
42
42
|
? 'text-text-secondary hover:bg-bg-inset focus-visible:bg-bg-inset'
|
|
43
43
|
: `${toneText[tone]} ${toneSoftHover[tone]}`}
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
>
|
|
75
75
|
<div class="flex flex-col">
|
|
76
76
|
{#if title || closable}
|
|
77
|
-
<div class="flex items-start gap-4 border-b border-hairline px-
|
|
77
|
+
<div class="flex items-start gap-4 border-b border-hairline px-5 py-4">
|
|
78
78
|
<div class="flex min-w-0 flex-1 flex-col gap-1">
|
|
79
79
|
{#if title}
|
|
80
80
|
<h2 class="font-heading text-lg font-medium tracking-tight">{title}</h2>
|
|
@@ -98,12 +98,12 @@
|
|
|
98
98
|
</div>
|
|
99
99
|
{/if}
|
|
100
100
|
|
|
101
|
-
<div class="px-
|
|
101
|
+
<div class="px-5 py-4 font-sans text-[15px] leading-relaxed text-text-secondary">
|
|
102
102
|
{@render children()}
|
|
103
103
|
</div>
|
|
104
104
|
|
|
105
105
|
{#if footer}
|
|
106
|
-
<div class="flex items-center justify-end gap-3 border-t border-hairline px-
|
|
106
|
+
<div class="flex items-center justify-end gap-3 border-t border-hairline px-5 py-3.5">
|
|
107
107
|
{@render footer()}
|
|
108
108
|
</div>
|
|
109
109
|
{/if}
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
</script>
|
|
52
52
|
|
|
53
53
|
<span
|
|
54
|
-
class="inline-flex w-full items-center border bg-bg pr-1 pl-
|
|
54
|
+
class="inline-flex w-full items-center border bg-bg pr-1 pl-3.5 transition-colors duration-150 ease-brand-out
|
|
55
55
|
{invalid ? 'border-danger' : `border-hairline ${toneFocusWithinBorder[tone]}`}
|
|
56
56
|
{disabled ? 'pointer-events-none opacity-50' : ''} {className}"
|
|
57
57
|
>
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
oninput={(event) => (value = clamp(event.currentTarget.valueAsNumber))}
|
|
66
66
|
onkeydown={onKeydown}
|
|
67
67
|
aria-invalid={invalid ? 'true' : undefined}
|
|
68
|
-
class="h-
|
|
68
|
+
class="h-10 w-full min-w-0 [appearance:textfield] bg-transparent font-sans text-[15px] text-text tabular-nums placeholder:text-text-muted focus:outline-none [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none"
|
|
69
69
|
{...rest}
|
|
70
70
|
/>
|
|
71
71
|
{#if unit}
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
|
|
35
35
|
<div class="flex w-full flex-col gap-2">
|
|
36
36
|
<span
|
|
37
|
-
class="inline-flex w-full items-center gap-2 border bg-bg pr-2 pl-
|
|
37
|
+
class="inline-flex w-full items-center gap-2 border bg-bg pr-2 pl-3.5 transition-colors duration-150 ease-brand-out
|
|
38
38
|
{invalid ? 'border-danger' : `border-hairline ${toneFocusWithinBorder[tone]}`}
|
|
39
39
|
{disabled ? 'pointer-events-none opacity-50' : ''} {className}"
|
|
40
40
|
>
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
{disabled}
|
|
44
44
|
type={visible ? 'text' : 'password'}
|
|
45
45
|
aria-invalid={invalid ? 'true' : undefined}
|
|
46
|
-
class="h-
|
|
46
|
+
class="h-10 w-full min-w-0 bg-transparent font-sans text-[15px] text-text placeholder:text-text-muted focus:outline-none"
|
|
47
47
|
{...rest}
|
|
48
48
|
/>
|
|
49
49
|
<button
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
: 'border border-hairline'} {className}"
|
|
46
46
|
{...rest}
|
|
47
47
|
>
|
|
48
|
-
<div class="flex flex-col gap-4 p-
|
|
48
|
+
<div class="flex flex-col gap-4 p-5 {featured ? toneSoft[tone] : ''}">
|
|
49
49
|
<div class="flex items-start justify-between gap-3">
|
|
50
50
|
<h3 class="font-heading text-lg font-medium tracking-tight {featured ? '' : 'text-text'}">
|
|
51
51
|
{name}
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
</div>
|
|
81
81
|
|
|
82
82
|
{#if features.length}
|
|
83
|
-
<ul class="flex flex-1 flex-col gap-3 border-t border-hairline p-
|
|
83
|
+
<ul class="flex flex-1 flex-col gap-3 border-t border-hairline p-5">
|
|
84
84
|
{#each features as feature (feature)}
|
|
85
85
|
<li class="flex items-start gap-3 font-sans text-sm text-text-secondary">
|
|
86
86
|
<svg
|
|
@@ -102,6 +102,6 @@
|
|
|
102
102
|
{/if}
|
|
103
103
|
|
|
104
104
|
{#if action}
|
|
105
|
-
<div class="p-
|
|
105
|
+
<div class="p-5 pt-2">{@render action()}</div>
|
|
106
106
|
{/if}
|
|
107
107
|
</div>
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
<select
|
|
36
36
|
bind:value
|
|
37
37
|
aria-invalid={invalid ? 'true' : undefined}
|
|
38
|
-
class="h-
|
|
38
|
+
class="h-10 w-full appearance-none border bg-bg py-0 pr-10 pl-3.5 font-sans text-[15px] text-text transition-colors duration-150 ease-brand-out focus:outline-none disabled:pointer-events-none disabled:opacity-50
|
|
39
39
|
{invalid ? 'border-danger' : `border-hairline ${toneFocusBorder[tone]}`} {className}"
|
|
40
40
|
{...rest}
|
|
41
41
|
>
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
<script module lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
export type SidebarVariant = 'plain' | 'filled' | 'floating';
|
|
5
|
+
|
|
2
6
|
export interface SidebarItem {
|
|
3
7
|
/** Stable id used for the active state. Falls back to `href`. */
|
|
4
8
|
id?: string;
|
|
@@ -6,10 +10,21 @@
|
|
|
6
10
|
href?: string;
|
|
7
11
|
disabled?: boolean;
|
|
8
12
|
badge?: string | number;
|
|
13
|
+
/**
|
|
14
|
+
* Leading glyph, 16px. The one thing a collapsed rail still shows, so
|
|
15
|
+
* an item that has to survive `collapsed` needs one.
|
|
16
|
+
*/
|
|
17
|
+
icon?: Snippet;
|
|
9
18
|
/** Nested links — the parent becomes a collapsible group. */
|
|
10
19
|
items?: SidebarItem[];
|
|
11
20
|
/** Open the group on first render. */
|
|
12
21
|
open?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* `false` pins a group open: the parent stops being a button and the
|
|
24
|
+
* children are always on show. For navigation a reader should not be
|
|
25
|
+
* able to lose.
|
|
26
|
+
*/
|
|
27
|
+
collapsible?: boolean;
|
|
13
28
|
}
|
|
14
29
|
|
|
15
30
|
export interface SidebarSection {
|
|
@@ -20,7 +35,6 @@
|
|
|
20
35
|
</script>
|
|
21
36
|
|
|
22
37
|
<script lang="ts">
|
|
23
|
-
import type { Snippet } from 'svelte';
|
|
24
38
|
import { useLocale } from '../../core/locale.svelte.js';
|
|
25
39
|
import { focusRing, toneRing, toneSoft, type Tone } from '../../core/tones.js';
|
|
26
40
|
|
|
@@ -32,6 +46,8 @@
|
|
|
32
46
|
collapsed?: boolean;
|
|
33
47
|
/** Show the collapse button. */
|
|
34
48
|
collapsible?: boolean;
|
|
49
|
+
/** Surface treatment: bare, the framed default, or a detached card. */
|
|
50
|
+
variant?: SidebarVariant;
|
|
35
51
|
tone?: Tone;
|
|
36
52
|
header?: Snippet;
|
|
37
53
|
footer?: Snippet;
|
|
@@ -44,6 +60,7 @@
|
|
|
44
60
|
value = $bindable(''),
|
|
45
61
|
collapsed = $bindable(false),
|
|
46
62
|
collapsible = true,
|
|
63
|
+
variant = 'filled',
|
|
47
64
|
tone = 'brand',
|
|
48
65
|
header,
|
|
49
66
|
footer,
|
|
@@ -59,7 +76,8 @@
|
|
|
59
76
|
let opened = $state<Record<string, boolean>>({});
|
|
60
77
|
|
|
61
78
|
const isOpen = (item: SidebarItem) =>
|
|
62
|
-
|
|
79
|
+
item.collapsible === false ||
|
|
80
|
+
(opened[keyOf(item)] ?? (item.open || item.items!.some((child) => keyOf(child) === value)));
|
|
63
81
|
|
|
64
82
|
function toggle(item: SidebarItem) {
|
|
65
83
|
opened = { ...opened, [keyOf(item)]: !isOpen(item) };
|
|
@@ -71,13 +89,50 @@
|
|
|
71
89
|
onnavigate?.(item);
|
|
72
90
|
}
|
|
73
91
|
|
|
92
|
+
const surfaces: Record<SidebarVariant, string> = {
|
|
93
|
+
plain: 'h-full bg-transparent',
|
|
94
|
+
filled: 'h-full border-r border-hairline bg-bg',
|
|
95
|
+
floating: 'm-3 h-[calc(100%-1.5rem)] border border-hairline bg-bg'
|
|
96
|
+
};
|
|
97
|
+
|
|
74
98
|
const row =
|
|
75
99
|
'flex w-full items-center gap-3 px-3 py-2 text-left font-sans text-sm transition-colors duration-150 ease-brand-out disabled:pointer-events-none disabled:opacity-40';
|
|
76
100
|
</script>
|
|
77
101
|
|
|
102
|
+
<!-- Nested links. One copy, rendered by both kinds of group. -->
|
|
103
|
+
{#snippet subitems(item: SidebarItem)}
|
|
104
|
+
<div class="ml-5 flex flex-col gap-0.5 border-l border-hairline pl-1">
|
|
105
|
+
{#each item.items as child (keyOf(child))}
|
|
106
|
+
<a
|
|
107
|
+
href={child.href}
|
|
108
|
+
onclick={() => go(child)}
|
|
109
|
+
aria-current={value === keyOf(child) ? 'page' : undefined}
|
|
110
|
+
class="{row} {focusRing} {toneRing[tone]} {value === keyOf(child)
|
|
111
|
+
? `${toneSoft[tone]} font-medium`
|
|
112
|
+
: 'text-text-muted hover:bg-bg-inset hover:text-text'} {child.disabled
|
|
113
|
+
? 'pointer-events-none opacity-40'
|
|
114
|
+
: ''}"
|
|
115
|
+
>
|
|
116
|
+
{@render glyph(child)}
|
|
117
|
+
<span class="min-w-0 flex-1 truncate">{child.label}</span>
|
|
118
|
+
{#if child.badge !== undefined}
|
|
119
|
+
<span class="shrink-0 font-mono text-[11px]">{child.badge}</span>
|
|
120
|
+
{/if}
|
|
121
|
+
</a>
|
|
122
|
+
{/each}
|
|
123
|
+
</div>
|
|
124
|
+
{/snippet}
|
|
125
|
+
|
|
126
|
+
<!-- One box for every glyph, so rows line up whether or not an item has one. -->
|
|
127
|
+
{#snippet glyph(item: SidebarItem)}
|
|
128
|
+
{#if item.icon}
|
|
129
|
+
<span class="grid size-4 shrink-0 place-items-center *:size-4">{@render item.icon()}</span>
|
|
130
|
+
{/if}
|
|
131
|
+
{/snippet}
|
|
132
|
+
|
|
78
133
|
<aside
|
|
79
|
-
class="flex
|
|
80
|
-
{collapsed ? 'w-16' : 'w-60'} {className}"
|
|
134
|
+
class="flex shrink-0 flex-col transition-[width] duration-200 ease-brand-out
|
|
135
|
+
{surfaces[variant]} {collapsed ? 'w-16' : 'w-60'} {className}"
|
|
81
136
|
>
|
|
82
137
|
{#if header}
|
|
83
138
|
<div class="flex h-16 shrink-0 items-center gap-3 border-b border-hairline px-3">
|
|
@@ -97,16 +152,25 @@
|
|
|
97
152
|
{/if}
|
|
98
153
|
|
|
99
154
|
{#each section.items as item (keyOf(item))}
|
|
100
|
-
{#if item.items?.length}
|
|
155
|
+
{#if item.items?.length && item.collapsible === false}
|
|
156
|
+
<div class="{row} {collapsed ? 'justify-center' : ''} font-medium text-text-muted">
|
|
157
|
+
{@render glyph(item)}
|
|
158
|
+
<span class="min-w-0 flex-1 truncate {collapsed ? 'sr-only' : ''}">{item.label}</span>
|
|
159
|
+
</div>
|
|
160
|
+
{#if isOpen(item) && !collapsed}
|
|
161
|
+
{@render subitems(item)}
|
|
162
|
+
{/if}
|
|
163
|
+
{:else if item.items?.length}
|
|
101
164
|
<button
|
|
102
165
|
type="button"
|
|
103
166
|
onclick={() => toggle(item)}
|
|
104
167
|
aria-expanded={isOpen(item)}
|
|
105
168
|
title={collapsed ? item.label : undefined}
|
|
106
|
-
class="{row} {focusRing} {toneRing[
|
|
169
|
+
class="{row} {collapsed ? 'justify-center' : ''} {focusRing} {toneRing[
|
|
107
170
|
tone
|
|
108
171
|
]} text-text-secondary hover:bg-bg-inset hover:text-text"
|
|
109
172
|
>
|
|
173
|
+
{@render glyph(item)}
|
|
110
174
|
<span class="min-w-0 flex-1 truncate {collapsed ? 'sr-only' : ''}">{item.label}</span>
|
|
111
175
|
{#if !collapsed}
|
|
112
176
|
<svg
|
|
@@ -128,25 +192,7 @@
|
|
|
128
192
|
</button>
|
|
129
193
|
|
|
130
194
|
{#if isOpen(item) && !collapsed}
|
|
131
|
-
|
|
132
|
-
{#each item.items as child (keyOf(child))}
|
|
133
|
-
<a
|
|
134
|
-
href={child.href}
|
|
135
|
-
onclick={() => go(child)}
|
|
136
|
-
aria-current={value === keyOf(child) ? 'page' : undefined}
|
|
137
|
-
class="{row} {focusRing} {toneRing[tone]} {value === keyOf(child)
|
|
138
|
-
? `${toneSoft[tone]} font-medium`
|
|
139
|
-
: 'text-text-muted hover:bg-bg-inset hover:text-text'} {child.disabled
|
|
140
|
-
? 'pointer-events-none opacity-40'
|
|
141
|
-
: ''}"
|
|
142
|
-
>
|
|
143
|
-
<span class="min-w-0 flex-1 truncate">{child.label}</span>
|
|
144
|
-
{#if child.badge !== undefined}
|
|
145
|
-
<span class="shrink-0 font-mono text-[11px]">{child.badge}</span>
|
|
146
|
-
{/if}
|
|
147
|
-
</a>
|
|
148
|
-
{/each}
|
|
149
|
-
</div>
|
|
195
|
+
{@render subitems(item)}
|
|
150
196
|
{/if}
|
|
151
197
|
{:else}
|
|
152
198
|
<a
|
|
@@ -154,12 +200,15 @@
|
|
|
154
200
|
onclick={() => go(item)}
|
|
155
201
|
aria-current={value === keyOf(item) ? 'page' : undefined}
|
|
156
202
|
title={collapsed ? item.label : undefined}
|
|
157
|
-
class="{row} {focusRing} {toneRing[
|
|
203
|
+
class="{row} {collapsed ? 'justify-center' : ''} {focusRing} {toneRing[
|
|
204
|
+
tone
|
|
205
|
+
]} {value === keyOf(item)
|
|
158
206
|
? `${toneSoft[tone]} font-medium`
|
|
159
207
|
: 'text-text-secondary hover:bg-bg-inset hover:text-text'} {item.disabled
|
|
160
208
|
? 'pointer-events-none opacity-40'
|
|
161
209
|
: ''}"
|
|
162
210
|
>
|
|
211
|
+
{@render glyph(item)}
|
|
163
212
|
<span class="min-w-0 flex-1 truncate {collapsed ? 'sr-only' : ''}">{item.label}</span>
|
|
164
213
|
{#if item.badge !== undefined && !collapsed}
|
|
165
214
|
<span class="shrink-0 font-mono text-[11px] text-text-muted">{item.badge}</span>
|