@nqmcreative/ui 0.2.2 → 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/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/CommandPalette.svelte +25 -2
- package/dist/styles/sprout/CommandPalette.svelte.d.ts +5 -0
- package/dist/styles/sprout/Modal.svelte +9 -1
- package/dist/styles/sprout/Sidebar.svelte +75 -26
- package/dist/styles/sprout/Sidebar.svelte.d.ts +15 -1
- package/dist/styles/sprout/fonts.css +7 -8
- package/dist/styles/sprout/index.d.ts +1 -1
- package/dist/styles/sprout/theme.css +6 -6
- package/package.json +1 -1
- package/registry.json +2 -1
|
@@ -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-alt',
|
|
95
|
+
floating: 'm-3 h-[calc(100%-1.5rem)] rounded-2xl border border-hairline bg-bg-alt shadow-sm'
|
|
96
|
+
};
|
|
97
|
+
|
|
74
98
|
const row =
|
|
75
99
|
'flex w-full items-center gap-3 rounded-xl px-2.5 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-4 flex flex-col gap-0.5 border-l border-hairline pl-2">
|
|
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 text-[11px] tabular-nums">{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 text-[11px] tabular-nums">{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 text-[11px] text-text-muted tabular-nums">{item.badge}</span>
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
export type SidebarVariant = 'plain' | 'filled' | 'floating';
|
|
1
3
|
export interface SidebarItem {
|
|
2
4
|
/** Stable id used for the active state. Falls back to `href`. */
|
|
3
5
|
id?: string;
|
|
@@ -5,17 +7,27 @@ export interface SidebarItem {
|
|
|
5
7
|
href?: string;
|
|
6
8
|
disabled?: boolean;
|
|
7
9
|
badge?: string | number;
|
|
10
|
+
/**
|
|
11
|
+
* Leading glyph, 16px. The one thing a collapsed rail still shows, so
|
|
12
|
+
* an item that has to survive `collapsed` needs one.
|
|
13
|
+
*/
|
|
14
|
+
icon?: Snippet;
|
|
8
15
|
/** Nested links — the parent becomes a collapsible group. */
|
|
9
16
|
items?: SidebarItem[];
|
|
10
17
|
/** Open the group on first render. */
|
|
11
18
|
open?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* `false` pins a group open: the parent stops being a button and the
|
|
21
|
+
* children are always on show. For navigation a reader should not be
|
|
22
|
+
* able to lose.
|
|
23
|
+
*/
|
|
24
|
+
collapsible?: boolean;
|
|
12
25
|
}
|
|
13
26
|
export interface SidebarSection {
|
|
14
27
|
/** Small heading above a run of items. */
|
|
15
28
|
label?: string;
|
|
16
29
|
items: SidebarItem[];
|
|
17
30
|
}
|
|
18
|
-
import type { Snippet } from 'svelte';
|
|
19
31
|
import { type Tone } from '../../core/tones.js';
|
|
20
32
|
interface Props {
|
|
21
33
|
sections: SidebarSection[];
|
|
@@ -25,6 +37,8 @@ interface Props {
|
|
|
25
37
|
collapsed?: boolean;
|
|
26
38
|
/** Show the collapse button. */
|
|
27
39
|
collapsible?: boolean;
|
|
40
|
+
/** Surface treatment: bare, the framed default, or a detached card. */
|
|
41
|
+
variant?: SidebarVariant;
|
|
28
42
|
tone?: Tone;
|
|
29
43
|
header?: Snippet;
|
|
30
44
|
footer?: Snippet;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* Optional: the two faces sprout is designed around — Fredoka (headings) and
|
|
3
|
-
*
|
|
3
|
+
* Work Sans (body). Import this only if your project doesn't already
|
|
4
4
|
* self-host or load them another way — without it the tokens fall back to the
|
|
5
5
|
* system stack, which works but loses the round, hand-drawn warmth the style is
|
|
6
6
|
* built on.
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
*
|
|
15
15
|
* Four rules for two families: both are variable fonts, so one file per subset
|
|
16
16
|
* covers every weight the style uses — 400 to 600 for Fredoka, 400 to 700 for
|
|
17
|
-
*
|
|
17
|
+
* Work Sans. Latin and Latin Extended only. Add
|
|
18
18
|
* `<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>` to
|
|
19
19
|
* your document head to open the connection earlier.
|
|
20
20
|
*/
|
|
@@ -49,15 +49,14 @@
|
|
|
49
49
|
U+A720-A7FF;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
/* ---
|
|
52
|
+
/* --- Work Sans --- */
|
|
53
53
|
/* latin */
|
|
54
54
|
@font-face {
|
|
55
|
-
font-family: '
|
|
55
|
+
font-family: 'Work Sans';
|
|
56
56
|
font-style: normal;
|
|
57
57
|
font-weight: 400 700;
|
|
58
58
|
font-display: swap;
|
|
59
|
-
src: url(https://fonts.gstatic.com/s/
|
|
60
|
-
format('woff2');
|
|
59
|
+
src: url(https://fonts.gstatic.com/s/worksans/v24/QGYsz_wNahGAdqQ43Rh_fKDp.woff2) format('woff2');
|
|
61
60
|
unicode-range:
|
|
62
61
|
U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
|
|
63
62
|
U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
|
@@ -65,11 +64,11 @@
|
|
|
65
64
|
|
|
66
65
|
/* latin-ext */
|
|
67
66
|
@font-face {
|
|
68
|
-
font-family: '
|
|
67
|
+
font-family: 'Work Sans';
|
|
69
68
|
font-style: normal;
|
|
70
69
|
font-weight: 400 700;
|
|
71
70
|
font-display: swap;
|
|
72
|
-
src: url(https://fonts.gstatic.com/s/
|
|
71
|
+
src: url(https://fonts.gstatic.com/s/worksans/v24/QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2)
|
|
73
72
|
format('woff2');
|
|
74
73
|
unicode-range:
|
|
75
74
|
U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329,
|
|
@@ -76,7 +76,7 @@ export type { StepItem } from './Steps.svelte';
|
|
|
76
76
|
export { default as Navbar } from './Navbar.svelte';
|
|
77
77
|
export type { NavItem } from './Navbar.svelte';
|
|
78
78
|
export { default as Sidebar } from './Sidebar.svelte';
|
|
79
|
-
export type { SidebarItem, SidebarSection } from './Sidebar.svelte';
|
|
79
|
+
export type { SidebarVariant, SidebarItem, SidebarSection } from './Sidebar.svelte';
|
|
80
80
|
export { default as Footer } from './Footer.svelte';
|
|
81
81
|
export type { FooterLink, FooterColumn } from './Footer.svelte';
|
|
82
82
|
export { default as HeroSection } from './HeroSection.svelte';
|
|
@@ -102,18 +102,18 @@
|
|
|
102
102
|
/* --- type --- */
|
|
103
103
|
/*
|
|
104
104
|
* Two faces, and the split is the point. Fredoka sets headings: geometric,
|
|
105
|
-
* round-terminalled, drawn wide — the same warmth the shapes have.
|
|
106
|
-
*
|
|
107
|
-
*
|
|
105
|
+
* round-terminalled, drawn wide — the same warmth the shapes have. Work
|
|
106
|
+
* Sans sets everything a reader has to get through, because Fredoka at 15px
|
|
107
|
+
* in a paragraph is charm at the cost of the sentence — and it is the body
|
|
108
|
+
* face in every style, so only the headings change voice when you swap.
|
|
108
109
|
*
|
|
109
110
|
* Both are variable files, one request per subset — see `fonts.css`.
|
|
110
111
|
* Everything degrades to the system stack if that file is not imported, so
|
|
111
112
|
* the webfonts stay optional.
|
|
112
113
|
*/
|
|
113
|
-
--font-sans:
|
|
114
|
-
'Plus Jakarta Sans', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
|
|
114
|
+
--font-sans: 'Work Sans', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
|
|
115
115
|
--font-heading:
|
|
116
|
-
'Fredoka', '
|
|
116
|
+
'Fredoka', 'Work Sans', ui-rounded, ui-sans-serif, system-ui, -apple-system, sans-serif;
|
|
117
117
|
--font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace;
|
|
118
118
|
|
|
119
119
|
/* --- motion --- */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nqmcreative/ui",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Svelte 5 + Tailwind CSS v4 component library — three styles (matte, paper, sprout), 65 components each, one shared behaviour core. No runtime dependencies.",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "vite dev",
|