@nqmcreative/ui 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -15
- package/cli/index.mjs +81 -42
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.js +1 -1
- package/dist/core/toast.svelte.d.ts +3 -0
- package/dist/core/trigger.d.ts +6 -0
- package/dist/core/trigger.js +13 -0
- package/dist/styles/matte/AccordionItem.svelte +5 -0
- package/dist/styles/matte/AccordionItem.svelte.d.ts +2 -0
- package/dist/styles/matte/Avatar.svelte +15 -0
- package/dist/styles/matte/Avatar.svelte.d.ts +3 -0
- package/dist/styles/matte/Badge.svelte +5 -0
- package/dist/styles/matte/Badge.svelte.d.ts +2 -0
- package/dist/styles/matte/Breadcrumb.svelte +11 -4
- package/dist/styles/matte/Breadcrumb.svelte.d.ts +3 -0
- package/dist/styles/matte/Button.svelte +62 -11
- package/dist/styles/matte/Button.svelte.d.ts +14 -2
- package/dist/styles/matte/Card.svelte +9 -4
- package/dist/styles/matte/Card.svelte.d.ts +2 -0
- package/dist/styles/matte/CheckboxGroup.svelte +6 -0
- package/dist/styles/matte/CheckboxGroup.svelte.d.ts +2 -0
- package/dist/styles/matte/Combobox.svelte +7 -2
- package/dist/styles/matte/Combobox.svelte.d.ts +3 -0
- package/dist/styles/matte/CommandPalette.svelte +38 -7
- package/dist/styles/matte/CommandPalette.svelte.d.ts +8 -0
- package/dist/styles/matte/DatePicker.svelte +2 -2
- package/dist/styles/matte/Drawer.svelte +7 -2
- package/dist/styles/matte/Drawer.svelte.d.ts +2 -0
- package/dist/styles/matte/Dropzone.svelte +1 -1
- package/dist/styles/matte/FeatureCard.svelte +1 -1
- package/dist/styles/matte/Footer.svelte +6 -2
- package/dist/styles/matte/Footer.svelte.d.ts +2 -0
- package/dist/styles/matte/Input.svelte +4 -4
- package/dist/styles/matte/Link.svelte +9 -0
- package/dist/styles/matte/Link.svelte.d.ts +4 -0
- package/dist/styles/matte/MenuItem.svelte +3 -2
- package/dist/styles/matte/Modal.svelte +8 -3
- package/dist/styles/matte/Modal.svelte.d.ts +2 -0
- package/dist/styles/matte/MultiSelect.svelte +7 -1
- package/dist/styles/matte/MultiSelect.svelte.d.ts +3 -0
- package/dist/styles/matte/Navbar.svelte +18 -4
- package/dist/styles/matte/Navbar.svelte.d.ts +2 -0
- package/dist/styles/matte/PricingCard.svelte +19 -11
- package/dist/styles/matte/PricingCard.svelte.d.ts +2 -0
- package/dist/styles/matte/RadioGroup.svelte +6 -0
- package/dist/styles/matte/RadioGroup.svelte.d.ts +2 -0
- package/dist/styles/matte/SegmentedControl.svelte +17 -3
- package/dist/styles/matte/SegmentedControl.svelte.d.ts +5 -0
- package/dist/styles/matte/Select.svelte +1 -1
- package/dist/styles/matte/Sidebar.svelte +76 -26
- package/dist/styles/matte/Sidebar.svelte.d.ts +15 -1
- package/dist/styles/matte/Stat.svelte +8 -1
- package/dist/styles/matte/Stat.svelte.d.ts +3 -0
- package/dist/styles/matte/StatsBand.svelte +1 -1
- package/dist/styles/matte/Steps.svelte +7 -0
- package/dist/styles/matte/Steps.svelte.d.ts +3 -0
- package/dist/styles/matte/Table.svelte +154 -98
- package/dist/styles/matte/Table.svelte.d.ts +12 -0
- package/dist/styles/matte/Tabs.svelte +5 -0
- package/dist/styles/matte/Tabs.svelte.d.ts +3 -0
- package/dist/styles/matte/Testimonial.svelte +2 -2
- package/dist/styles/matte/Textarea.svelte +1 -1
- package/dist/styles/matte/Toaster.svelte +3 -0
- package/dist/styles/matte/icon.d.ts +21 -0
- package/dist/styles/matte/icon.js +21 -0
- package/dist/styles/matte/index.d.ts +1 -3
- package/dist/styles/matte/index.js +0 -2
- package/dist/styles/paper/AccordionItem.svelte +5 -0
- package/dist/styles/paper/AccordionItem.svelte.d.ts +2 -0
- package/dist/styles/paper/Avatar.svelte +15 -0
- package/dist/styles/paper/Avatar.svelte.d.ts +3 -0
- package/dist/styles/paper/Badge.svelte +5 -0
- package/dist/styles/paper/Badge.svelte.d.ts +2 -0
- package/dist/styles/paper/Breadcrumb.svelte +8 -4
- package/dist/styles/paper/Breadcrumb.svelte.d.ts +3 -0
- package/dist/styles/paper/Button.svelte +58 -7
- package/dist/styles/paper/Button.svelte.d.ts +14 -2
- package/dist/styles/paper/Card.svelte +6 -1
- package/dist/styles/paper/Card.svelte.d.ts +2 -0
- package/dist/styles/paper/CheckboxGroup.svelte +6 -0
- package/dist/styles/paper/CheckboxGroup.svelte.d.ts +2 -0
- package/dist/styles/paper/Combobox.svelte +5 -0
- package/dist/styles/paper/Combobox.svelte.d.ts +3 -0
- package/dist/styles/paper/CommandPalette.svelte +31 -2
- package/dist/styles/paper/CommandPalette.svelte.d.ts +8 -0
- package/dist/styles/paper/Drawer.svelte +5 -0
- package/dist/styles/paper/Drawer.svelte.d.ts +2 -0
- package/dist/styles/paper/Footer.svelte +6 -2
- package/dist/styles/paper/Footer.svelte.d.ts +2 -0
- package/dist/styles/paper/Link.svelte +9 -0
- package/dist/styles/paper/Link.svelte.d.ts +4 -0
- package/dist/styles/paper/MenuItem.svelte +2 -1
- package/dist/styles/paper/Modal.svelte +14 -1
- package/dist/styles/paper/Modal.svelte.d.ts +2 -0
- package/dist/styles/paper/MultiSelect.svelte +7 -1
- package/dist/styles/paper/MultiSelect.svelte.d.ts +3 -0
- package/dist/styles/paper/Navbar.svelte +18 -4
- package/dist/styles/paper/Navbar.svelte.d.ts +2 -0
- package/dist/styles/paper/PricingCard.svelte +23 -15
- package/dist/styles/paper/PricingCard.svelte.d.ts +2 -0
- package/dist/styles/paper/RadioGroup.svelte +6 -0
- package/dist/styles/paper/RadioGroup.svelte.d.ts +2 -0
- package/dist/styles/paper/SegmentedControl.svelte +16 -2
- package/dist/styles/paper/SegmentedControl.svelte.d.ts +5 -0
- package/dist/styles/paper/Sidebar.svelte +76 -26
- package/dist/styles/paper/Sidebar.svelte.d.ts +15 -1
- package/dist/styles/paper/Stat.svelte +8 -1
- package/dist/styles/paper/Stat.svelte.d.ts +3 -0
- package/dist/styles/paper/Steps.svelte +7 -0
- package/dist/styles/paper/Steps.svelte.d.ts +3 -0
- package/dist/styles/paper/Table.svelte +153 -97
- package/dist/styles/paper/Table.svelte.d.ts +12 -0
- package/dist/styles/paper/Tabs.svelte +5 -0
- package/dist/styles/paper/Tabs.svelte.d.ts +3 -0
- package/dist/styles/paper/Toaster.svelte +3 -0
- package/dist/styles/paper/fonts.css +34 -7
- package/dist/styles/paper/icon.d.ts +21 -0
- package/dist/styles/paper/icon.js +21 -0
- package/dist/styles/paper/index.d.ts +1 -3
- package/dist/styles/paper/index.js +0 -2
- package/dist/styles/paper/theme.css +11 -8
- package/dist/styles/sprout/AccordionItem.svelte +5 -0
- package/dist/styles/sprout/AccordionItem.svelte.d.ts +2 -0
- package/dist/styles/sprout/Avatar.svelte +15 -0
- package/dist/styles/sprout/Avatar.svelte.d.ts +3 -0
- package/dist/styles/sprout/Badge.svelte +5 -0
- package/dist/styles/sprout/Badge.svelte.d.ts +2 -0
- package/dist/styles/sprout/Breadcrumb.svelte +8 -4
- package/dist/styles/sprout/Breadcrumb.svelte.d.ts +3 -0
- package/dist/styles/sprout/Button.svelte +58 -7
- package/dist/styles/sprout/Button.svelte.d.ts +14 -2
- package/dist/styles/sprout/Card.svelte +6 -1
- package/dist/styles/sprout/Card.svelte.d.ts +2 -0
- package/dist/styles/sprout/CheckboxGroup.svelte +6 -0
- package/dist/styles/sprout/CheckboxGroup.svelte.d.ts +2 -0
- package/dist/styles/sprout/Combobox.svelte +5 -0
- package/dist/styles/sprout/Combobox.svelte.d.ts +3 -0
- package/dist/styles/sprout/CommandPalette.svelte +31 -2
- package/dist/styles/sprout/CommandPalette.svelte.d.ts +8 -0
- package/dist/styles/sprout/Drawer.svelte +5 -0
- package/dist/styles/sprout/Drawer.svelte.d.ts +2 -0
- package/dist/styles/sprout/Footer.svelte +6 -2
- package/dist/styles/sprout/Footer.svelte.d.ts +2 -0
- package/dist/styles/sprout/Link.svelte +9 -0
- package/dist/styles/sprout/Link.svelte.d.ts +4 -0
- package/dist/styles/sprout/MenuItem.svelte +2 -1
- package/dist/styles/sprout/Modal.svelte +14 -1
- package/dist/styles/sprout/Modal.svelte.d.ts +2 -0
- package/dist/styles/sprout/MultiSelect.svelte +7 -1
- package/dist/styles/sprout/MultiSelect.svelte.d.ts +3 -0
- package/dist/styles/sprout/Navbar.svelte +18 -4
- package/dist/styles/sprout/Navbar.svelte.d.ts +2 -0
- package/dist/styles/sprout/PricingCard.svelte +23 -15
- package/dist/styles/sprout/PricingCard.svelte.d.ts +2 -0
- package/dist/styles/sprout/RadioGroup.svelte +6 -0
- package/dist/styles/sprout/RadioGroup.svelte.d.ts +2 -0
- package/dist/styles/sprout/SegmentedControl.svelte +16 -2
- package/dist/styles/sprout/SegmentedControl.svelte.d.ts +5 -0
- package/dist/styles/sprout/Sidebar.svelte +76 -26
- package/dist/styles/sprout/Sidebar.svelte.d.ts +15 -1
- package/dist/styles/sprout/Stat.svelte +8 -1
- package/dist/styles/sprout/Stat.svelte.d.ts +3 -0
- package/dist/styles/sprout/Steps.svelte +7 -0
- package/dist/styles/sprout/Steps.svelte.d.ts +3 -0
- package/dist/styles/sprout/Table.svelte +153 -97
- package/dist/styles/sprout/Table.svelte.d.ts +12 -0
- package/dist/styles/sprout/Tabs.svelte +5 -0
- package/dist/styles/sprout/Tabs.svelte.d.ts +3 -0
- package/dist/styles/sprout/Toaster.svelte +3 -0
- package/dist/styles/sprout/fonts.css +7 -8
- package/dist/styles/sprout/icon.d.ts +21 -0
- package/dist/styles/sprout/icon.js +21 -0
- package/dist/styles/sprout/index.d.ts +1 -3
- package/dist/styles/sprout/index.js +0 -2
- package/dist/styles/sprout/theme.css +6 -6
- package/package.json +2 -26
- package/registry.json +3 -36
- package/dist/styles/matte/NumberInput.svelte +0 -96
- package/dist/styles/matte/NumberInput.svelte.d.ts +0 -15
- package/dist/styles/matte/PasswordInput.svelte +0 -95
- package/dist/styles/matte/PasswordInput.svelte.d.ts +0 -12
- package/dist/styles/paper/NumberInput.svelte +0 -96
- package/dist/styles/paper/NumberInput.svelte.d.ts +0 -15
- package/dist/styles/paper/PasswordInput.svelte +0 -100
- package/dist/styles/paper/PasswordInput.svelte.d.ts +0 -12
- package/dist/styles/sprout/NumberInput.svelte +0 -97
- package/dist/styles/sprout/NumberInput.svelte.d.ts +0 -15
- package/dist/styles/sprout/PasswordInput.svelte +0 -101
- package/dist/styles/sprout/PasswordInput.svelte.d.ts +0 -12
|
@@ -32,6 +32,16 @@
|
|
|
32
32
|
/** Header stays put while the body scrolls — pair with a `max-h-*` class. */
|
|
33
33
|
stickyHeader?: boolean;
|
|
34
34
|
|
|
35
|
+
/** Bar above the table — a title, a search field, a filter row. */
|
|
36
|
+
header?: Snippet;
|
|
37
|
+
/** Bar below the table — pagination, a total, a summary line. */
|
|
38
|
+
footer?: Snippet;
|
|
39
|
+
/**
|
|
40
|
+
* Shown in place of `header` while rows are ticked, after the count and the
|
|
41
|
+
* clear button. Passing it is what turns the bulk bar on.
|
|
42
|
+
*/
|
|
43
|
+
bulkActions?: Snippet<[{ selected: string[]; clear: () => void }]>;
|
|
44
|
+
|
|
35
45
|
/** Bindable. Set it to sort; leave `sortable` columns to drive it. */
|
|
36
46
|
sort?: TableSort | null;
|
|
37
47
|
/** Sort the rows here rather than refetching them server-side. Default true. */
|
|
@@ -57,6 +67,9 @@
|
|
|
57
67
|
hoverable = true,
|
|
58
68
|
compact = false,
|
|
59
69
|
stickyHeader = false,
|
|
70
|
+
header,
|
|
71
|
+
footer,
|
|
72
|
+
bulkActions,
|
|
60
73
|
sort = $bindable(null),
|
|
61
74
|
sortLocally = true,
|
|
62
75
|
onsort,
|
|
@@ -106,112 +119,155 @@
|
|
|
106
119
|
selected = toggleKey(selected, keyOf(row));
|
|
107
120
|
onselect?.(selected);
|
|
108
121
|
}
|
|
122
|
+
|
|
123
|
+
function clearSelection() {
|
|
124
|
+
selected = [];
|
|
125
|
+
onselect?.(selected);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** The bulk bar takes the header's place, so only one of the two is up. */
|
|
129
|
+
const bulkBar = $derived(selectable && bulkActions !== undefined && selected.length > 0);
|
|
109
130
|
</script>
|
|
110
131
|
|
|
111
132
|
<div
|
|
112
|
-
class="w-full overflow-
|
|
133
|
+
class="flex w-full flex-col overflow-hidden rounded-lg border border-hairline bg-bg shadow-xs {className}"
|
|
113
134
|
{...rest}
|
|
114
135
|
>
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
136
|
+
{#if bulkBar}
|
|
137
|
+
<div
|
|
138
|
+
class="flex flex-wrap items-center gap-3 border-b border-hairline bg-brand-light px-4 py-3"
|
|
139
|
+
>
|
|
140
|
+
<span class="font-sans text-[13px] font-semibold text-text">
|
|
141
|
+
{selected.length}
|
|
142
|
+
{t.current.selected}
|
|
143
|
+
</span>
|
|
144
|
+
<button
|
|
145
|
+
type="button"
|
|
146
|
+
onclick={clearSelection}
|
|
147
|
+
class="rounded font-sans text-[13px] font-medium text-text-muted transition-colors duration-150 hover:text-text focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-current"
|
|
119
148
|
>
|
|
120
|
-
{
|
|
121
|
-
</
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
149
|
+
{t.current.clear}
|
|
150
|
+
</button>
|
|
151
|
+
<div class="ml-auto flex flex-wrap items-center gap-2">
|
|
152
|
+
{@render bulkActions?.({ selected, clear: clearSelection })}
|
|
153
|
+
</div>
|
|
154
|
+
</div>
|
|
155
|
+
{:else if header}
|
|
156
|
+
<div
|
|
157
|
+
class="flex flex-wrap items-center gap-3 border-b border-hairline px-4 py-3 font-sans text-[13px] text-text-secondary"
|
|
158
|
+
>
|
|
159
|
+
{@render header()}
|
|
160
|
+
</div>
|
|
161
|
+
{/if}
|
|
162
|
+
<div class="min-h-0 w-full flex-1 overflow-auto">
|
|
163
|
+
<table class="w-full border-collapse font-sans text-sm">
|
|
164
|
+
{#if caption}
|
|
165
|
+
<caption
|
|
166
|
+
class="border-b border-hairline px-4 py-3 text-left font-sans text-[13px] text-text-muted"
|
|
167
|
+
>
|
|
168
|
+
{caption}
|
|
169
|
+
</caption>
|
|
170
|
+
{/if}
|
|
171
|
+
<thead class={stickyHeader ? 'sticky top-0 z-10' : ''}>
|
|
172
|
+
<tr class="border-b border-hairline bg-bg-alt">
|
|
173
|
+
{#if selectable}
|
|
174
|
+
<th scope="col" class="w-10 {pad}">
|
|
175
|
+
<Checkbox
|
|
176
|
+
checked={allSelected}
|
|
177
|
+
indeterminate={someSelected}
|
|
178
|
+
{tone}
|
|
179
|
+
aria-label={t.current.selectAllRows}
|
|
180
|
+
onchange={toggleAll}
|
|
181
|
+
/>
|
|
182
|
+
</th>
|
|
183
|
+
{/if}
|
|
184
|
+
{#each columns as column (column.key)}
|
|
185
|
+
<th
|
|
186
|
+
scope="col"
|
|
187
|
+
aria-sort={sort?.key === column.key
|
|
188
|
+
? sort.direction === 'asc'
|
|
189
|
+
? 'ascending'
|
|
190
|
+
: 'descending'
|
|
191
|
+
: undefined}
|
|
192
|
+
class="text-[13px] font-semibold text-text-muted {pad} {aligns[
|
|
193
|
+
column.align ?? 'left'
|
|
194
|
+
]} {column.class ?? ''}"
|
|
195
|
+
>
|
|
196
|
+
{#if column.sortable}
|
|
197
|
+
<button
|
|
198
|
+
type="button"
|
|
199
|
+
onclick={() => toggleSort(column)}
|
|
200
|
+
class="inline-flex items-center gap-1.5 rounded transition-colors duration-150 hover:text-text focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-current
|
|
153
201
|
{sort?.key === column.key ? 'text-text' : ''}"
|
|
154
|
-
|
|
202
|
+
>
|
|
203
|
+
{column.label}
|
|
204
|
+
<span class="text-[9px] leading-none" aria-hidden="true">
|
|
205
|
+
{#if sort?.key === column.key}
|
|
206
|
+
{sort.direction === 'asc' ? '▲' : '▼'}
|
|
207
|
+
{:else}
|
|
208
|
+
⇅
|
|
209
|
+
{/if}
|
|
210
|
+
</span>
|
|
211
|
+
</button>
|
|
212
|
+
{:else}
|
|
155
213
|
{column.label}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
{:else}
|
|
160
|
-
⇅
|
|
161
|
-
{/if}
|
|
162
|
-
</span>
|
|
163
|
-
</button>
|
|
164
|
-
{:else}
|
|
165
|
-
{column.label}
|
|
166
|
-
{/if}
|
|
167
|
-
</th>
|
|
168
|
-
{/each}
|
|
169
|
-
</tr>
|
|
170
|
-
</thead>
|
|
171
|
-
<tbody>
|
|
172
|
-
{#if sorted.length === 0}
|
|
173
|
-
<tr>
|
|
174
|
-
<td
|
|
175
|
-
colspan={columns.length + (selectable ? 1 : 0)}
|
|
176
|
-
class="px-4 py-10 text-center text-sm text-text-muted"
|
|
177
|
-
>
|
|
178
|
-
{#if empty}{@render empty()}{:else}{t.current.noData}{/if}
|
|
179
|
-
</td>
|
|
214
|
+
{/if}
|
|
215
|
+
</th>
|
|
216
|
+
{/each}
|
|
180
217
|
</tr>
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
<tr
|
|
185
|
-
|
|
186
|
-
|
|
218
|
+
</thead>
|
|
219
|
+
<tbody>
|
|
220
|
+
{#if sorted.length === 0}
|
|
221
|
+
<tr>
|
|
222
|
+
<td
|
|
223
|
+
colspan={columns.length + (selectable ? 1 : 0)}
|
|
224
|
+
class="px-4 py-10 text-center text-sm text-text-muted"
|
|
225
|
+
>
|
|
226
|
+
{#if empty}{@render empty()}{:else}{t.current.noData}{/if}
|
|
227
|
+
</td>
|
|
228
|
+
</tr>
|
|
229
|
+
{:else}
|
|
230
|
+
{#each sorted as row, i (selectable ? keyOf(row) : i)}
|
|
231
|
+
{@const isSelected = selectable && selected.includes(keyOf(row))}
|
|
232
|
+
<tr
|
|
233
|
+
aria-selected={selectable ? isSelected : undefined}
|
|
234
|
+
class="border-b border-hairline last:border-b-0
|
|
187
235
|
{isSelected ? 'bg-brand-light' : striped && i % 2 === 1 ? 'bg-bg-alt' : ''}
|
|
188
236
|
{hoverable ? 'transition-colors duration-150 hover:bg-bg-alt' : ''}"
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
237
|
+
>
|
|
238
|
+
{#if selectable}
|
|
239
|
+
<td class={pad}>
|
|
240
|
+
<Checkbox
|
|
241
|
+
checked={isSelected}
|
|
242
|
+
{tone}
|
|
243
|
+
aria-label={t.current.selectRow}
|
|
244
|
+
onchange={() => toggleRow(row)}
|
|
245
|
+
/>
|
|
246
|
+
</td>
|
|
247
|
+
{/if}
|
|
248
|
+
{#each columns as column (column.key)}
|
|
249
|
+
<td
|
|
250
|
+
class="text-text-secondary {pad} {aligns[column.align ?? 'left']} {column.class ??
|
|
251
|
+
''}"
|
|
252
|
+
>
|
|
253
|
+
{#if cell}
|
|
254
|
+
{@render cell({ row, column, value: row[column.key] })}
|
|
255
|
+
{:else}
|
|
256
|
+
{row[column.key] ?? ''}
|
|
257
|
+
{/if}
|
|
258
|
+
</td>
|
|
259
|
+
{/each}
|
|
260
|
+
</tr>
|
|
261
|
+
{/each}
|
|
262
|
+
{/if}
|
|
263
|
+
</tbody>
|
|
264
|
+
</table>
|
|
265
|
+
</div>
|
|
266
|
+
{#if footer}
|
|
267
|
+
<div
|
|
268
|
+
class="flex flex-wrap items-center gap-3 border-t border-hairline px-4 py-3 font-sans text-[13px] text-text-secondary"
|
|
269
|
+
>
|
|
270
|
+
{@render footer()}
|
|
271
|
+
</div>
|
|
272
|
+
{/if}
|
|
217
273
|
</div>
|
|
@@ -29,6 +29,18 @@ declare function $$render<T extends Record<string, unknown>>(): {
|
|
|
29
29
|
compact?: boolean;
|
|
30
30
|
/** Header stays put while the body scrolls — pair with a `max-h-*` class. */
|
|
31
31
|
stickyHeader?: boolean;
|
|
32
|
+
/** Bar above the table — a title, a search field, a filter row. */
|
|
33
|
+
header?: Snippet;
|
|
34
|
+
/** Bar below the table — pagination, a total, a summary line. */
|
|
35
|
+
footer?: Snippet;
|
|
36
|
+
/**
|
|
37
|
+
* Shown in place of `header` while rows are ticked, after the count and the
|
|
38
|
+
* clear button. Passing it is what turns the bulk bar on.
|
|
39
|
+
*/
|
|
40
|
+
bulkActions?: Snippet<[{
|
|
41
|
+
selected: string[];
|
|
42
|
+
clear: () => void;
|
|
43
|
+
}]>;
|
|
32
44
|
/** Bindable. Set it to sort; leave `sortable` columns to drive it. */
|
|
33
45
|
sort?: TableSort | null;
|
|
34
46
|
/** Sort the rows here rather than refetching them server-side. Default true. */
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
2
3
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
4
|
import {
|
|
4
5
|
focusRing,
|
|
@@ -8,10 +9,13 @@
|
|
|
8
9
|
toneText,
|
|
9
10
|
type Tone
|
|
10
11
|
} from '../../core/tones.js';
|
|
12
|
+
import { iconMd } from './icon.js';
|
|
11
13
|
|
|
12
14
|
export interface TabItem {
|
|
13
15
|
value: string;
|
|
14
16
|
label: string;
|
|
17
|
+
/** Leading icon, 16px. */
|
|
18
|
+
icon?: Snippet;
|
|
15
19
|
disabled?: boolean;
|
|
16
20
|
/** Small count/label rendered after the tab title. */
|
|
17
21
|
badge?: string | number;
|
|
@@ -89,6 +93,7 @@
|
|
|
89
93
|
onclick={() => select(item)}
|
|
90
94
|
class={tabClass(value === item.value)}
|
|
91
95
|
>
|
|
96
|
+
{#if item.icon}<span class={iconMd}>{@render item.icon()}</span>{/if}
|
|
92
97
|
{item.label}
|
|
93
98
|
{#if item.badge !== undefined}
|
|
94
99
|
<span
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
1
2
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
2
3
|
import { type Tone } from '../../core/tones.js';
|
|
3
4
|
export interface TabItem {
|
|
4
5
|
value: string;
|
|
5
6
|
label: string;
|
|
7
|
+
/** Leading icon, 16px. */
|
|
8
|
+
icon?: Snippet;
|
|
6
9
|
disabled?: boolean;
|
|
7
10
|
/** Small count/label rendered after the tab title. */
|
|
8
11
|
badge?: string | number;
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { portal } from '../../core/actions/dismissable.js';
|
|
5
5
|
import { toast } from '../../core/toast.svelte.js';
|
|
6
6
|
import { toneText } from '../../core/tones.js';
|
|
7
|
+
import { iconMd } from './icon.js';
|
|
7
8
|
import Spinner from './Spinner.svelte';
|
|
8
9
|
import { useLocale } from '../../core/locale.svelte.js';
|
|
9
10
|
|
|
@@ -61,6 +62,8 @@
|
|
|
61
62
|
<span class="mt-0.5 shrink-0 {toneText[item.tone]}">
|
|
62
63
|
{#if item.loading}
|
|
63
64
|
<Spinner size="sm" label="" />
|
|
65
|
+
{:else if item.icon}
|
|
66
|
+
<span class={iconMd}>{@render item.icon()}</span>
|
|
64
67
|
{:else if item.tone === 'success'}
|
|
65
68
|
<svg class="size-4" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
|
66
69
|
<circle cx="8" cy="8" r="6.5" stroke="currentColor" stroke-width="1.5" />
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Optional:
|
|
3
|
-
* only if your project doesn't
|
|
4
|
-
*
|
|
5
|
-
* harder and colder.
|
|
2
|
+
* Optional: the two faces paper is designed around — Plus Jakarta Sans
|
|
3
|
+
* (headings) and Work Sans (body). Import this only if your project doesn't
|
|
4
|
+
* already self-host or load them another way — without it the tokens fall back
|
|
5
|
+
* to the system stack, which works but reads harder and colder.
|
|
6
6
|
*
|
|
7
7
|
* These are plain `@font-face` rules rather than an `@import` of the Google
|
|
8
8
|
* stylesheet on purpose: `@import` is only valid before every other rule, so a
|
|
@@ -11,13 +11,13 @@
|
|
|
11
11
|
* you put it, and it skips the CSS-to-CSS round trip before the font files
|
|
12
12
|
* start downloading.
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* around 49 kB in total. Add
|
|
14
|
+
* Four rules for two families: both are variable fonts, so one file per subset
|
|
15
|
+
* covers every weight from 400 to 700. Latin and Latin Extended only. Add
|
|
17
16
|
* `<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>` to
|
|
18
17
|
* your document head to open the connection earlier.
|
|
19
18
|
*/
|
|
20
19
|
|
|
20
|
+
/* --- Plus Jakarta Sans --- */
|
|
21
21
|
/* latin */
|
|
22
22
|
@font-face {
|
|
23
23
|
font-family: 'Plus Jakarta Sans';
|
|
@@ -44,3 +44,30 @@
|
|
|
44
44
|
U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
|
|
45
45
|
U+A720-A7FF;
|
|
46
46
|
}
|
|
47
|
+
|
|
48
|
+
/* --- Work Sans --- */
|
|
49
|
+
/* latin */
|
|
50
|
+
@font-face {
|
|
51
|
+
font-family: 'Work Sans';
|
|
52
|
+
font-style: normal;
|
|
53
|
+
font-weight: 400 700;
|
|
54
|
+
font-display: swap;
|
|
55
|
+
src: url(https://fonts.gstatic.com/s/worksans/v24/QGYsz_wNahGAdqQ43Rh_fKDp.woff2) format('woff2');
|
|
56
|
+
unicode-range:
|
|
57
|
+
U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
|
|
58
|
+
U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* latin-ext */
|
|
62
|
+
@font-face {
|
|
63
|
+
font-family: 'Work Sans';
|
|
64
|
+
font-style: normal;
|
|
65
|
+
font-weight: 400 700;
|
|
66
|
+
font-display: swap;
|
|
67
|
+
src: url(https://fonts.gstatic.com/s/worksans/v24/QGYsz_wNahGAdqQ43Rh_cqDpp_k.woff2)
|
|
68
|
+
format('woff2');
|
|
69
|
+
unicode-range:
|
|
70
|
+
U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329,
|
|
71
|
+
U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F,
|
|
72
|
+
U+A720-A7FF;
|
|
73
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The box a consumer-supplied icon renders into.
|
|
3
|
+
*
|
|
4
|
+
* Every icon in the library is a `Snippet` the app writes — the package ships
|
|
5
|
+
* no icon set and no runtime dependency — so the component has to size the
|
|
6
|
+
* glyph rather than trust it. The `*:` variant hits the snippet's own element,
|
|
7
|
+
* which is what keeps a bare `<svg viewBox="0 0 16 16">` from laying out at its
|
|
8
|
+
* intrinsic 300×150.
|
|
9
|
+
*
|
|
10
|
+
* Kept per style rather than in `core/`: an app only `@source`s the style
|
|
11
|
+
* folder it installed, so a class string living outside it would never be
|
|
12
|
+
* generated by Tailwind.
|
|
13
|
+
*/
|
|
14
|
+
/** 14px — menu rows, chips, anything sitting on one line of small text. */
|
|
15
|
+
export declare const iconSm = "grid size-3.5 shrink-0 place-items-center *:size-3.5";
|
|
16
|
+
/** 16px — the default: nav items, tabs, buttons, list rows. */
|
|
17
|
+
export declare const iconMd = "grid size-4 shrink-0 place-items-center *:size-4";
|
|
18
|
+
/** 20px — headers, stats, anything sitting beside a heading. */
|
|
19
|
+
export declare const iconLg = "grid size-5 shrink-0 place-items-center *:size-5";
|
|
20
|
+
/** 24px — dialog and empty-state marks. */
|
|
21
|
+
export declare const iconXl = "grid size-6 shrink-0 place-items-center *:size-6";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The box a consumer-supplied icon renders into.
|
|
3
|
+
*
|
|
4
|
+
* Every icon in the library is a `Snippet` the app writes — the package ships
|
|
5
|
+
* no icon set and no runtime dependency — so the component has to size the
|
|
6
|
+
* glyph rather than trust it. The `*:` variant hits the snippet's own element,
|
|
7
|
+
* which is what keeps a bare `<svg viewBox="0 0 16 16">` from laying out at its
|
|
8
|
+
* intrinsic 300×150.
|
|
9
|
+
*
|
|
10
|
+
* Kept per style rather than in `core/`: an app only `@source`s the style
|
|
11
|
+
* folder it installed, so a class string living outside it would never be
|
|
12
|
+
* generated by Tailwind.
|
|
13
|
+
*/
|
|
14
|
+
/** 14px — menu rows, chips, anything sitting on one line of small text. */
|
|
15
|
+
export const iconSm = 'grid size-3.5 shrink-0 place-items-center *:size-3.5';
|
|
16
|
+
/** 16px — the default: nav items, tabs, buttons, list rows. */
|
|
17
|
+
export const iconMd = 'grid size-4 shrink-0 place-items-center *:size-4';
|
|
18
|
+
/** 20px — headers, stats, anything sitting beside a heading. */
|
|
19
|
+
export const iconLg = 'grid size-5 shrink-0 place-items-center *:size-5';
|
|
20
|
+
/** 24px — dialog and empty-state marks. */
|
|
21
|
+
export const iconXl = 'grid size-6 shrink-0 place-items-center *:size-6';
|
|
@@ -22,8 +22,6 @@ export { default as Switch } from './Switch.svelte';
|
|
|
22
22
|
export type { SwitchSize } from './Switch.svelte';
|
|
23
23
|
export { default as Field } from './Field.svelte';
|
|
24
24
|
export { default as Label } from './Label.svelte';
|
|
25
|
-
export { default as PasswordInput } from './PasswordInput.svelte';
|
|
26
|
-
export { default as NumberInput } from './NumberInput.svelte';
|
|
27
25
|
export { default as Slider } from './Slider.svelte';
|
|
28
26
|
export { default as RadioGroup } from './RadioGroup.svelte';
|
|
29
27
|
export type { RadioOption } from './RadioGroup.svelte';
|
|
@@ -76,7 +74,7 @@ export type { StepItem } from './Steps.svelte';
|
|
|
76
74
|
export { default as Navbar } from './Navbar.svelte';
|
|
77
75
|
export type { NavItem } from './Navbar.svelte';
|
|
78
76
|
export { default as Sidebar } from './Sidebar.svelte';
|
|
79
|
-
export type { SidebarItem, SidebarSection } from './Sidebar.svelte';
|
|
77
|
+
export type { SidebarVariant, SidebarItem, SidebarSection } from './Sidebar.svelte';
|
|
80
78
|
export { default as Footer } from './Footer.svelte';
|
|
81
79
|
export type { FooterLink, FooterColumn } from './Footer.svelte';
|
|
82
80
|
export { default as HeroSection } from './HeroSection.svelte';
|
|
@@ -21,8 +21,6 @@ export { default as Radio } from './Radio.svelte';
|
|
|
21
21
|
export { default as Switch } from './Switch.svelte';
|
|
22
22
|
export { default as Field } from './Field.svelte';
|
|
23
23
|
export { default as Label } from './Label.svelte';
|
|
24
|
-
export { default as PasswordInput } from './PasswordInput.svelte';
|
|
25
|
-
export { default as NumberInput } from './NumberInput.svelte';
|
|
26
24
|
export { default as Slider } from './Slider.svelte';
|
|
27
25
|
export { default as RadioGroup } from './RadioGroup.svelte';
|
|
28
26
|
export { default as CheckboxGroup } from './CheckboxGroup.svelte';
|
|
@@ -78,16 +78,19 @@
|
|
|
78
78
|
|
|
79
79
|
/* --- type --- */
|
|
80
80
|
/*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
81
|
+
* Work Sans sets running text in every style, so a project that swaps the
|
|
82
|
+
* word after `@nqmcreative/ui/` keeps the same reading rhythm and only the
|
|
83
|
+
* headings change voice.
|
|
84
84
|
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
85
|
+
* Plus Jakarta Sans keeps the headings: rounded terminals, open counters,
|
|
86
|
+
* low contrast — softer than a system stack, which lands on Segoe UI for
|
|
87
|
+
* most readers and gives paper's generous spacing nothing warm to sit in.
|
|
88
|
+
*
|
|
89
|
+
* Both are variable files, one request per subset — see `fonts.css`.
|
|
90
|
+
* Everything degrades to the system stack if that file is not imported, so
|
|
91
|
+
* the webfonts stay optional.
|
|
88
92
|
*/
|
|
89
|
-
--font-sans:
|
|
90
|
-
'Plus Jakarta Sans', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
|
|
93
|
+
--font-sans: 'Work Sans', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
|
|
91
94
|
--font-heading:
|
|
92
95
|
'Plus Jakarta Sans', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
|
|
93
96
|
--font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
3
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
4
|
import { toneText, type Tone } from '../../core/tones.js';
|
|
5
|
+
import { iconMd } from './icon.js';
|
|
5
6
|
|
|
6
7
|
interface Props extends HTMLAttributes<HTMLDetailsElement> {
|
|
7
8
|
title: string;
|
|
@@ -9,6 +10,8 @@
|
|
|
9
10
|
tone?: Tone;
|
|
10
11
|
/** Short text on the right of the summary row. */
|
|
11
12
|
meta?: string;
|
|
13
|
+
/** Leading icon on the summary row. */
|
|
14
|
+
icon?: Snippet;
|
|
12
15
|
children: Snippet;
|
|
13
16
|
}
|
|
14
17
|
|
|
@@ -17,6 +20,7 @@
|
|
|
17
20
|
open = $bindable(false),
|
|
18
21
|
tone = 'brand',
|
|
19
22
|
meta,
|
|
23
|
+
icon,
|
|
20
24
|
class: className = '',
|
|
21
25
|
children,
|
|
22
26
|
...rest
|
|
@@ -27,6 +31,7 @@
|
|
|
27
31
|
<summary
|
|
28
32
|
class="flex cursor-pointer list-none items-center gap-4 px-4 py-3.5 font-sans text-sm font-medium text-text transition-colors duration-150 ease-brand-out select-none hover:bg-bg-alt focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-current [&::-webkit-details-marker]:hidden"
|
|
29
33
|
>
|
|
34
|
+
{#if icon}<span class={iconMd}>{@render icon()}</span>{/if}
|
|
30
35
|
<span class="flex-1">{title}</span>
|
|
31
36
|
{#if meta}<span class="text-[13px] font-normal text-text-muted">{meta}</span>{/if}
|
|
32
37
|
<svg
|
|
@@ -7,6 +7,8 @@ interface Props extends HTMLAttributes<HTMLDetailsElement> {
|
|
|
7
7
|
tone?: Tone;
|
|
8
8
|
/** Short text on the right of the summary row. */
|
|
9
9
|
meta?: string;
|
|
10
|
+
/** Leading icon on the summary row. */
|
|
11
|
+
icon?: Snippet;
|
|
10
12
|
children: Snippet;
|
|
11
13
|
}
|
|
12
14
|
declare const AccordionItem: import("svelte").Component<Props, {}, "open">;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
2
3
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
4
|
import { toneSoft, type Tone } from '../../core/tones.js';
|
|
4
5
|
|
|
@@ -13,6 +14,8 @@
|
|
|
13
14
|
tone?: Tone;
|
|
14
15
|
/** Rounded square instead of the default circle. */
|
|
15
16
|
squared?: boolean;
|
|
17
|
+
/** Drawn instead of the initials when there is no `src` — a user mark. */
|
|
18
|
+
fallback?: Snippet;
|
|
16
19
|
}
|
|
17
20
|
|
|
18
21
|
let {
|
|
@@ -22,6 +25,7 @@
|
|
|
22
25
|
size = 'md',
|
|
23
26
|
tone = 'brand',
|
|
24
27
|
squared = false,
|
|
28
|
+
fallback,
|
|
25
29
|
class: className = '',
|
|
26
30
|
...rest
|
|
27
31
|
}: Props = $props();
|
|
@@ -34,6 +38,15 @@
|
|
|
34
38
|
xl: 'size-16 text-lg'
|
|
35
39
|
};
|
|
36
40
|
|
|
41
|
+
// The mark tracks the initials: about half the circle, whatever the size.
|
|
42
|
+
const glyphs: Record<AvatarSize, string> = {
|
|
43
|
+
xs: 'grid place-items-center *:size-3',
|
|
44
|
+
sm: 'grid place-items-center *:size-4',
|
|
45
|
+
md: 'grid place-items-center *:size-5',
|
|
46
|
+
lg: 'grid place-items-center *:size-6',
|
|
47
|
+
xl: 'grid place-items-center *:size-8'
|
|
48
|
+
};
|
|
49
|
+
|
|
37
50
|
const initials = $derived(
|
|
38
51
|
name
|
|
39
52
|
.trim()
|
|
@@ -54,6 +67,8 @@
|
|
|
54
67
|
>
|
|
55
68
|
{#if src}
|
|
56
69
|
<img {src} alt={alt ?? name} class="size-full object-cover" />
|
|
70
|
+
{:else if fallback}
|
|
71
|
+
<span class={glyphs[size]}>{@render fallback()}</span>
|
|
57
72
|
{:else}
|
|
58
73
|
{initials}
|
|
59
74
|
{/if}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
1
2
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
2
3
|
import { type Tone } from '../../core/tones.js';
|
|
3
4
|
export type AvatarSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
@@ -10,6 +11,8 @@ interface Props extends HTMLAttributes<HTMLSpanElement> {
|
|
|
10
11
|
tone?: Tone;
|
|
11
12
|
/** Rounded square instead of the default circle. */
|
|
12
13
|
squared?: boolean;
|
|
14
|
+
/** Drawn instead of the initials when there is no `src` — a user mark. */
|
|
15
|
+
fallback?: Snippet;
|
|
13
16
|
}
|
|
14
17
|
declare const Avatar: import("svelte").Component<Props, {}, "">;
|
|
15
18
|
type Avatar = ReturnType<typeof Avatar>;
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
toneText,
|
|
10
10
|
type Tone
|
|
11
11
|
} from '../../core/tones.js';
|
|
12
|
+
import { iconSm } from './icon.js';
|
|
12
13
|
|
|
13
14
|
export type BadgeVariant = 'soft' | 'solid' | 'outline';
|
|
14
15
|
export type BadgeSize = 'sm' | 'md';
|
|
@@ -19,6 +20,8 @@
|
|
|
19
20
|
size?: BadgeSize;
|
|
20
21
|
/** Small filled circle before the label. */
|
|
21
22
|
dot?: boolean;
|
|
23
|
+
/** Leading icon, 14px — sits where the dot would. */
|
|
24
|
+
icon?: Snippet;
|
|
22
25
|
children: Snippet;
|
|
23
26
|
}
|
|
24
27
|
|
|
@@ -27,6 +30,7 @@
|
|
|
27
30
|
variant = 'soft',
|
|
28
31
|
size = 'md',
|
|
29
32
|
dot = false,
|
|
33
|
+
icon,
|
|
30
34
|
class: className = '',
|
|
31
35
|
children,
|
|
32
36
|
...rest
|
|
@@ -54,5 +58,6 @@
|
|
|
54
58
|
class="size-1.5 shrink-0 rounded-full {variant === 'solid' ? 'bg-current' : toneFill[tone]}"
|
|
55
59
|
></span>
|
|
56
60
|
{/if}
|
|
61
|
+
{#if icon}<span class={iconSm}>{@render icon()}</span>{/if}
|
|
57
62
|
{@render children()}
|
|
58
63
|
</span>
|