@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
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @nqmcreative/ui
|
|
2
2
|
|
|
3
3
|
Svelte 5 (runes) + Tailwind CSS v4 component library. One package, **three
|
|
4
|
-
styles**,
|
|
4
|
+
styles**, 63 components each — the same props, the same behaviour, a different
|
|
5
5
|
look. Eight tones, light and dark, no runtime dependencies.
|
|
6
6
|
|
|
7
7
|
| style | import | character |
|
|
@@ -131,9 +131,9 @@ Under SSR use `<LocaleProvider>` instead, so the locale is scoped to a request.
|
|
|
131
131
|
|
|
132
132
|
```
|
|
133
133
|
src/lib/core/ behaviour, a11y, types — no markup, no classes
|
|
134
|
-
src/lib/styles/matte/
|
|
135
|
-
src/lib/styles/paper/
|
|
136
|
-
src/lib/styles/sprout/
|
|
134
|
+
src/lib/styles/matte/ 63 components
|
|
135
|
+
src/lib/styles/paper/ 63 components
|
|
136
|
+
src/lib/styles/sprout/ 63 components
|
|
137
137
|
scripts/catalogue.mjs the list every style must implement
|
|
138
138
|
```
|
|
139
139
|
|
|
@@ -150,17 +150,17 @@ same.
|
|
|
150
150
|
|
|
151
151
|
## Components
|
|
152
152
|
|
|
153
|
-
| group | components
|
|
154
|
-
| ------------ |
|
|
155
|
-
| Actions | `Button`, `Link`
|
|
156
|
-
| Forms | `Input`, `Textarea`, `Select`, `Combobox`, `MultiSelect`, `DatePicker`, `Calendar`, `Checkbox`, `CheckboxGroup`, `Radio`, `RadioGroup`, `SegmentedControl`, `Switch`, `Slider`, `
|
|
157
|
-
| Data display | `Badge`, `Avatar`, `AvatarGroup`, `Card`, `Stat`, `Table`, `Kbd`
|
|
158
|
-
| Feedback | `Alert`, `Toaster` + `toast`, `Progress`, `Spinner`, `Skeleton`, `EmptyState`
|
|
159
|
-
| Layout & nav | `Divider`, `Tabs`, `Accordion`, `AccordionItem`, `Breadcrumb`, `Pagination`, `Steps`
|
|
160
|
-
| App shell | `Navbar`, `Sidebar`, `Footer`
|
|
161
|
-
| Overlay | `Modal`, `ConfirmDialog`, `Drawer`, `Dropdown` + `MenuItem` + `MenuSeparator`, `ContextMenu`, `CommandPalette`, `Popover`, `Tooltip`
|
|
162
|
-
| Marketing | `HeroSection`, `FeatureGrid`, `FeatureCard`, `PricingCard`, `Testimonial`, `LogoCloud`, `CTASection`, `StatsBand`
|
|
163
|
-
| System | `ThemeToggle`, `LocaleProvider`
|
|
153
|
+
| group | components |
|
|
154
|
+
| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
155
|
+
| Actions | `Button`, `Link` |
|
|
156
|
+
| Forms | `Input`, `Textarea`, `Select`, `Combobox`, `MultiSelect`, `DatePicker`, `Calendar`, `Checkbox`, `CheckboxGroup`, `Radio`, `RadioGroup`, `SegmentedControl`, `Switch`, `Slider`, `InputGroup`, `Dropzone`, `Field`, `Label` |
|
|
157
|
+
| Data display | `Badge`, `Avatar`, `AvatarGroup`, `Card`, `Stat`, `Table`, `Kbd` |
|
|
158
|
+
| Feedback | `Alert`, `Toaster` + `toast`, `Progress`, `Spinner`, `Skeleton`, `EmptyState` |
|
|
159
|
+
| Layout & nav | `Divider`, `Tabs`, `Accordion`, `AccordionItem`, `Breadcrumb`, `Pagination`, `Steps` |
|
|
160
|
+
| App shell | `Navbar`, `Sidebar`, `Footer` |
|
|
161
|
+
| Overlay | `Modal`, `ConfirmDialog`, `Drawer`, `Dropdown` + `MenuItem` + `MenuSeparator`, `ContextMenu`, `CommandPalette`, `Popover`, `Tooltip` |
|
|
162
|
+
| Marketing | `HeroSection`, `FeatureGrid`, `FeatureCard`, `PricingCard`, `Testimonial`, `LogoCloud`, `CTASection`, `StatsBand` |
|
|
163
|
+
| System | `ThemeToggle`, `LocaleProvider` |
|
|
164
164
|
|
|
165
165
|
Overlays are built on the native top layer and three in-house actions
|
|
166
166
|
(`portal`, `focusTrap`, `anchored`) rather than a positioning dependency.
|
package/cli/index.mjs
CHANGED
|
@@ -3,16 +3,19 @@
|
|
|
3
3
|
* @nqmcreative/ui CLI
|
|
4
4
|
*
|
|
5
5
|
* nqm-ui init wire the design system into this project
|
|
6
|
+
* nqm-ui update rewrite that wiring after upgrading the package
|
|
6
7
|
* nqm-ui add <name...> print (or insert) the import for a component
|
|
7
8
|
* nqm-ui list [category] every component and its subpath
|
|
8
9
|
* nqm-ui info <name> what one component pulls in
|
|
9
10
|
*
|
|
10
11
|
* Node built-ins only, so `bunx @nqmcreative/ui init` needs nothing installed.
|
|
11
|
-
* Every write is idempotent:
|
|
12
|
-
* time
|
|
12
|
+
* Every write is idempotent: the lines this CLI owns are rewritten from
|
|
13
|
+
* scratch on every run, so running it twice changes nothing the second time —
|
|
14
|
+
* and running it after an upgrade picks up whatever those lines became.
|
|
13
15
|
*/
|
|
14
16
|
import { readFile, writeFile, mkdir } from 'node:fs/promises';
|
|
15
17
|
import { existsSync } from 'node:fs';
|
|
18
|
+
import { createInterface } from 'node:readline/promises';
|
|
16
19
|
import { dirname, join, relative, resolve } from 'node:path';
|
|
17
20
|
import { fileURLToPath } from 'node:url';
|
|
18
21
|
|
|
@@ -76,27 +79,56 @@ function findComponent(list, query) {
|
|
|
76
79
|
* The style to work in. There is no default on purpose: a component belongs to
|
|
77
80
|
* a style, and picking one by accident is the mistake this package exists to
|
|
78
81
|
* prevent. Asking once beats shipping the wrong look.
|
|
82
|
+
*
|
|
83
|
+
* `--style` answers it up front. Without it a terminal gets a numbered prompt;
|
|
84
|
+
* a pipe or a CI job gets the list and a non-zero exit, because there is
|
|
85
|
+
* nobody there to answer.
|
|
79
86
|
*/
|
|
80
|
-
function pickStyle(styles) {
|
|
87
|
+
async function pickStyle(styles) {
|
|
81
88
|
const index = argv.indexOf('--style');
|
|
82
89
|
const wanted = index !== -1 ? argv[index + 1] : null;
|
|
83
90
|
|
|
84
|
-
if (
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
if (wanted) {
|
|
92
|
+
const named = styles.find((s) => s.name === wanted);
|
|
93
|
+
if (!named) fail(`unknown style "${wanted}" — try ${styles.map((s) => s.name).join(' or ')}`);
|
|
94
|
+
return named;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
log();
|
|
98
|
+
log(' which style?');
|
|
99
|
+
log();
|
|
100
|
+
styles.forEach((style, i) => {
|
|
101
|
+
log(` ${c.cyan(`${i + 1})`)} ${c.bold(style.name.padEnd(8))} ${c.dim(style.description)}`);
|
|
102
|
+
});
|
|
103
|
+
log();
|
|
104
|
+
|
|
105
|
+
if (!process.stdin.isTTY) {
|
|
106
|
+
log(` ${c.dim(`not a terminal — pass it: ${command} --style ${styles[0].name}`)}`);
|
|
93
107
|
log();
|
|
94
108
|
process.exit(1);
|
|
95
109
|
}
|
|
96
110
|
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
111
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
112
|
+
try {
|
|
113
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
114
|
+
const answer = (await rl.question(` 1-${styles.length}, or a name [1]: `)).trim();
|
|
115
|
+
if (!answer) return styles[0];
|
|
116
|
+
|
|
117
|
+
const number = Number(answer);
|
|
118
|
+
if (Number.isInteger(number) && number >= 1 && number <= styles.length) {
|
|
119
|
+
return styles[number - 1];
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const named = styles.find((s) => s.name === answer.toLowerCase());
|
|
123
|
+
if (named) return named;
|
|
124
|
+
|
|
125
|
+
warn(`no style called "${answer}"`);
|
|
126
|
+
}
|
|
127
|
+
} finally {
|
|
128
|
+
rl.close();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
fail(`try ${command} --style ${styles[0].name}`);
|
|
100
132
|
}
|
|
101
133
|
|
|
102
134
|
/* ------------------------------------------------------------ file edits -- */
|
|
@@ -151,7 +183,7 @@ function sourcePath(cssRel) {
|
|
|
151
183
|
|
|
152
184
|
async function init() {
|
|
153
185
|
const { styles } = await registry();
|
|
154
|
-
const style = pickStyle(styles);
|
|
186
|
+
const style = await pickStyle(styles);
|
|
155
187
|
const project = await detectProject();
|
|
156
188
|
|
|
157
189
|
log();
|
|
@@ -185,28 +217,31 @@ async function init() {
|
|
|
185
217
|
|
|
186
218
|
const css = await readOr(project.css);
|
|
187
219
|
|
|
188
|
-
//
|
|
189
|
-
//
|
|
190
|
-
//
|
|
191
|
-
|
|
192
|
-
|
|
220
|
+
// The block above belongs to this package, so it is rewritten on every run
|
|
221
|
+
// rather than left alone once it is there. That is what makes an upgrade
|
|
222
|
+
// land: when a release changes which files a style imports, or where
|
|
223
|
+
// `@source` has to point, the old lines would otherwise sit here and the
|
|
224
|
+
// new components would come up unstyled.
|
|
225
|
+
//
|
|
226
|
+
// Only those lines. Everything below is the project's own CSS and is
|
|
227
|
+
// carried over untouched — including an `@import` of another package, which
|
|
228
|
+
// is why the match is anchored to this one.
|
|
229
|
+
const stale =
|
|
230
|
+
/^@import ['"]@nqmcreative\/ui\/[^'"]+\.css['"];$|^@source ['"][^'"]*@nqmcreative\/ui[^'"]*['"];$/;
|
|
231
|
+
|
|
232
|
+
const kept = css
|
|
233
|
+
.split('\n')
|
|
234
|
+
.filter((line) => !stale.test(line.trim()))
|
|
235
|
+
.join('\n')
|
|
236
|
+
.replace(/@import ['"]tailwindcss['"];\n?/, '')
|
|
237
|
+
.replace(/\/\* Tailwind v4 skips node_modules[\s\S]*?\*\/\n?/, '')
|
|
238
|
+
.replace(/\n{3,}/g, '\n\n')
|
|
239
|
+
.trim();
|
|
240
|
+
|
|
241
|
+
const body = kept ? `${lines.join('\n')}\n\n${kept}\n` : `${lines.join('\n')}\n`;
|
|
242
|
+
if (body === css) {
|
|
243
|
+
skip(`${project.cssRel} already up to date`);
|
|
193
244
|
} else {
|
|
194
|
-
// Drop whatever this package left here before — an older layout's
|
|
195
|
-
// `@nqmcreative/ui/theme.css`, another style's, and the `@source` that
|
|
196
|
-
// went with them. Keeping those would either fight the new tokens or fail
|
|
197
|
-
// to resolve, so a rewrite is cleaner than an append.
|
|
198
|
-
const stale =
|
|
199
|
-
/^@import ['"]@nqmcreative\/ui\/[^'"]+\.css['"];$|^@source ['"][^'"]*@nqmcreative\/ui[^'"]*['"];$/;
|
|
200
|
-
|
|
201
|
-
const kept = css
|
|
202
|
-
.split('\n')
|
|
203
|
-
.filter((line) => !stale.test(line.trim()))
|
|
204
|
-
.join('\n')
|
|
205
|
-
.replace(/@import ['"]tailwindcss['"];\n?/, '')
|
|
206
|
-
.replace(/\n{3,}/g, '\n\n')
|
|
207
|
-
.trim();
|
|
208
|
-
|
|
209
|
-
const body = kept ? `${lines.join('\n')}\n\n${kept}\n` : `${lines.join('\n')}\n`;
|
|
210
245
|
await write(project.css, body, project.cssRel);
|
|
211
246
|
}
|
|
212
247
|
|
|
@@ -321,7 +356,7 @@ async function init() {
|
|
|
321
356
|
|
|
322
357
|
async function add() {
|
|
323
358
|
const { components, styles } = await registry();
|
|
324
|
-
const style = pickStyle(styles);
|
|
359
|
+
const style = await pickStyle(styles);
|
|
325
360
|
const names = args.slice(1);
|
|
326
361
|
if (names.length === 0) fail('which component? e.g. `nqm-ui add button dialog`');
|
|
327
362
|
|
|
@@ -435,22 +470,26 @@ function help() {
|
|
|
435
470
|
log(`
|
|
436
471
|
${c.bold('@nqmcreative/ui')}
|
|
437
472
|
|
|
438
|
-
${c.cyan('init
|
|
473
|
+
${c.cyan('init')} wire one style into this project
|
|
474
|
+
${c.cyan('update')} rewrite that wiring after upgrading the package
|
|
439
475
|
${c.cyan('add <name...>')} print the import for a component
|
|
440
|
-
${c.dim('--style <s>')} which style to import from (required)
|
|
441
476
|
${c.dim('--to <file>')} …and insert it into that file instead
|
|
442
477
|
${c.cyan('list [category]')} every component in the catalogue
|
|
443
478
|
${c.cyan('info <name>')} what one component pulls in, in every style
|
|
444
479
|
|
|
480
|
+
${c.dim('--style <s>')} skip the picker: matte, paper or sprout
|
|
445
481
|
${c.dim('--dry-run')} show the writes without making them
|
|
446
482
|
|
|
447
|
-
${c.dim('Every style implements every component. Run init once per project
|
|
483
|
+
${c.dim('Every style implements every component. Run init once per project,')}
|
|
484
|
+
${c.dim('then update after each upgrade — it overwrites the lines it owns.')}
|
|
448
485
|
`);
|
|
449
486
|
}
|
|
450
487
|
|
|
451
488
|
/* ------------------------------------------------------------------ main -- */
|
|
452
489
|
|
|
453
|
-
|
|
490
|
+
// `update` is `init` under the name people reach for after an upgrade: both
|
|
491
|
+
// rewrite the lines this CLI owns and leave the rest of the file alone.
|
|
492
|
+
const commands = { init, update: init, add, list, info };
|
|
454
493
|
if (!command || flags.has('--help') || command === 'help') {
|
|
455
494
|
help();
|
|
456
495
|
} else if (commands[command]) {
|
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 --- */
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* const id = toast.loading('Rendering…');
|
|
9
9
|
* toast.dismiss(id);
|
|
10
10
|
*/
|
|
11
|
+
import type { Snippet } from 'svelte';
|
|
11
12
|
import type { Tone } from './tones.js';
|
|
12
13
|
export interface ToastAction {
|
|
13
14
|
label: string;
|
|
@@ -19,6 +20,8 @@ export interface ToastOptions {
|
|
|
19
20
|
/** Milliseconds on screen. `0` keeps it until dismissed. Default 5000. */
|
|
20
21
|
duration?: number;
|
|
21
22
|
action?: ToastAction;
|
|
23
|
+
/** Replaces the tone's own glyph. */
|
|
24
|
+
icon?: Snippet;
|
|
22
25
|
/** Swaps the glyph for a spinner and defaults `duration` to 0. */
|
|
23
26
|
loading?: boolean;
|
|
24
27
|
}
|
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
|
+
}
|
|
@@ -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 py-4 font-sans text-[15px] text-text transition-colors duration-150 ease-brand-out select-none hover:text-text-secondary focus-visible:outline-2 focus-visible:outline-offset-2 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 font-medium">{title}</span>
|
|
31
36
|
{#if meta}<span class="font-mono text-xs 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
|
/** 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()
|
|
@@ -52,6 +65,8 @@
|
|
|
52
65
|
>
|
|
53
66
|
{#if src}
|
|
54
67
|
<img {src} alt={alt ?? name} class="size-full object-cover" />
|
|
68
|
+
{:else if fallback}
|
|
69
|
+
<span class={glyphs[size]}>{@render fallback()}</span>
|
|
55
70
|
{:else}
|
|
56
71
|
{initials}
|
|
57
72
|
{/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
|
/** 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>
|
|
@@ -9,6 +9,8 @@ interface Props extends HTMLAttributes<HTMLSpanElement> {
|
|
|
9
9
|
size?: BadgeSize;
|
|
10
10
|
/** Small filled circle before the label. */
|
|
11
11
|
dot?: boolean;
|
|
12
|
+
/** Leading icon, 14px — sits where the dot would. */
|
|
13
|
+
icon?: Snippet;
|
|
12
14
|
children: Snippet;
|
|
13
15
|
}
|
|
14
16
|
declare const Badge: import("svelte").Component<Props, {}, "">;
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
2
3
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
4
|
import { toneHoverText, type Tone } from '../../core/tones.js';
|
|
4
5
|
import { useLocale } from '../../core/locale.svelte.js';
|
|
6
|
+
import { iconMd } from './icon.js';
|
|
5
7
|
|
|
6
8
|
export interface BreadcrumbItem {
|
|
7
9
|
label: string;
|
|
8
10
|
href?: string;
|
|
11
|
+
/** Leading icon — a home mark on the first crumb, say. */
|
|
12
|
+
icon?: Snippet;
|
|
9
13
|
}
|
|
10
14
|
|
|
11
15
|
interface Props extends HTMLAttributes<HTMLElement> {
|
|
@@ -29,15 +33,18 @@
|
|
|
29
33
|
{#if item.href && i < items.length - 1}
|
|
30
34
|
<a
|
|
31
35
|
href={item.href}
|
|
32
|
-
class="text-text-secondary transition-colors duration-150 ease-brand-out {toneHoverText[
|
|
36
|
+
class="inline-flex items-center gap-1.5 text-text-secondary transition-colors duration-150 ease-brand-out {toneHoverText[
|
|
33
37
|
tone
|
|
34
38
|
]}"
|
|
35
39
|
>
|
|
36
|
-
{item.label}
|
|
40
|
+
{#if item.icon}<span class={iconMd}>{@render item.icon()}</span>{/if}{item.label}
|
|
37
41
|
</a>
|
|
38
42
|
{:else}
|
|
39
|
-
<span
|
|
40
|
-
|
|
43
|
+
<span
|
|
44
|
+
class="inline-flex items-center gap-1.5 text-text"
|
|
45
|
+
aria-current={i === items.length - 1 ? 'page' : undefined}
|
|
46
|
+
>
|
|
47
|
+
{#if item.icon}<span class={iconMd}>{@render item.icon()}</span>{/if}{item.label}
|
|
41
48
|
</span>
|
|
42
49
|
{/if}
|
|
43
50
|
</li>
|
|
@@ -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 BreadcrumbItem {
|
|
4
5
|
label: string;
|
|
5
6
|
href?: string;
|
|
7
|
+
/** Leading icon — a home mark on the first crumb, say. */
|
|
8
|
+
icon?: Snippet;
|
|
6
9
|
}
|
|
7
10
|
interface Props extends HTMLAttributes<HTMLElement> {
|
|
8
11
|
items: BreadcrumbItem[];
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
3
|
import type { HTMLButtonAttributes } from 'svelte/elements';
|
|
4
4
|
import Spinner from './Spinner.svelte';
|
|
5
|
+
import { iconMd, iconSm } from './icon.js';
|
|
5
6
|
import {
|
|
6
7
|
focusRing,
|
|
7
8
|
toneBorder,
|
|
@@ -17,7 +18,7 @@
|
|
|
17
18
|
export type ButtonVariant = 'solid' | 'soft' | 'outline' | 'ghost' | 'link' | 'primary';
|
|
18
19
|
export type ButtonSize = 'sm' | 'md' | 'lg' | 'xl';
|
|
19
20
|
|
|
20
|
-
interface Props extends HTMLButtonAttributes {
|
|
21
|
+
interface Props extends Omit<HTMLButtonAttributes, 'onclick'> {
|
|
21
22
|
variant?: ButtonVariant;
|
|
22
23
|
tone?: Tone;
|
|
23
24
|
size?: ButtonSize;
|
|
@@ -28,7 +29,19 @@
|
|
|
28
29
|
loading?: boolean;
|
|
29
30
|
/** Stretch to the container's full width. */
|
|
30
31
|
block?: boolean;
|
|
31
|
-
|
|
32
|
+
/** Leading icon. The spinner takes its place while the button is busy. */
|
|
33
|
+
icon?: Snippet;
|
|
34
|
+
/** Trailing icon. */
|
|
35
|
+
iconEnd?: Snippet;
|
|
36
|
+
/** Square button with no label — pass an `aria-label`. */
|
|
37
|
+
iconOnly?: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Return a promise — a save, a submit, a fetch — and the button spins and
|
|
40
|
+
* locks itself until it settles, without any state in the parent. It
|
|
41
|
+
* unlocks on a rejection too, so catch failures inside the handler.
|
|
42
|
+
*/
|
|
43
|
+
onclick?: (event: MouseEvent) => unknown;
|
|
44
|
+
children?: Snippet;
|
|
32
45
|
}
|
|
33
46
|
|
|
34
47
|
let {
|
|
@@ -38,6 +51,10 @@
|
|
|
38
51
|
href,
|
|
39
52
|
loading = false,
|
|
40
53
|
block = false,
|
|
54
|
+
icon,
|
|
55
|
+
iconEnd,
|
|
56
|
+
iconOnly = false,
|
|
57
|
+
onclick,
|
|
41
58
|
disabled = false,
|
|
42
59
|
type = 'button',
|
|
43
60
|
class: className = '',
|
|
@@ -49,10 +66,17 @@
|
|
|
49
66
|
'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
67
|
|
|
51
68
|
const sizes: Record<ButtonSize, string> = {
|
|
52
|
-
sm: 'h-
|
|
53
|
-
md: 'h-
|
|
54
|
-
lg: 'h-
|
|
55
|
-
xl: 'h-
|
|
69
|
+
sm: 'h-8 px-3.5 text-[13px]',
|
|
70
|
+
md: 'h-10 px-5 text-[15px]',
|
|
71
|
+
lg: 'h-11 px-6 text-base',
|
|
72
|
+
xl: 'h-12 px-8 text-base'
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const squares: Record<ButtonSize, string> = {
|
|
76
|
+
sm: 'size-8',
|
|
77
|
+
md: 'size-10',
|
|
78
|
+
lg: 'size-11',
|
|
79
|
+
xl: 'size-12'
|
|
56
80
|
};
|
|
57
81
|
|
|
58
82
|
const variantClass = $derived.by(() => {
|
|
@@ -72,7 +96,24 @@
|
|
|
72
96
|
});
|
|
73
97
|
|
|
74
98
|
const isLink = $derived(variant === 'link');
|
|
75
|
-
|
|
99
|
+
|
|
100
|
+
/** Set by an `onclick` that returned a promise, on top of the `loading` prop. */
|
|
101
|
+
let pending = $state(false);
|
|
102
|
+
const busy = $derived(loading || pending);
|
|
103
|
+
const inert = $derived(disabled || busy);
|
|
104
|
+
|
|
105
|
+
const iconClass = $derived(size === 'sm' ? iconSm : iconMd);
|
|
106
|
+
|
|
107
|
+
async function handleClick(event: MouseEvent) {
|
|
108
|
+
const result = onclick?.(event);
|
|
109
|
+
// Anything else is a plain handler: nothing to wait for, nothing to lock.
|
|
110
|
+
if (!(result instanceof Promise)) return;
|
|
111
|
+
pending = true;
|
|
112
|
+
// The handler owns its own errors — catch them there to show a message.
|
|
113
|
+
// All the button needs to know is that the work has settled.
|
|
114
|
+
await result.catch(() => {});
|
|
115
|
+
pending = false;
|
|
116
|
+
}
|
|
76
117
|
</script>
|
|
77
118
|
|
|
78
119
|
<svelte:element
|
|
@@ -81,14 +122,24 @@
|
|
|
81
122
|
type={href ? undefined : type}
|
|
82
123
|
disabled={href ? undefined : inert}
|
|
83
124
|
aria-disabled={href && inert ? 'true' : undefined}
|
|
84
|
-
aria-busy={
|
|
125
|
+
aria-busy={busy ? 'true' : undefined}
|
|
85
126
|
role={href ? 'button' : undefined}
|
|
86
|
-
class="{base} {focusRing} {toneRing[tone]} {variantClass} {isLink
|
|
127
|
+
class="{base} {focusRing} {toneRing[tone]} {variantClass} {isLink
|
|
128
|
+
? ''
|
|
129
|
+
: iconOnly
|
|
130
|
+
? squares[size]
|
|
131
|
+
: sizes[size]}
|
|
87
132
|
{block ? 'w-full' : ''} {className}"
|
|
88
133
|
{...rest}
|
|
134
|
+
onclick={handleClick}
|
|
89
135
|
>
|
|
90
|
-
{#if
|
|
136
|
+
{#if busy}
|
|
91
137
|
<Spinner size={size === 'sm' ? 'xs' : 'sm'} label="" />
|
|
138
|
+
{:else if icon}
|
|
139
|
+
<span class={iconClass} aria-hidden="true">{@render icon()}</span>
|
|
140
|
+
{/if}
|
|
141
|
+
{@render children?.()}
|
|
142
|
+
{#if iconEnd && !iconOnly}
|
|
143
|
+
<span class={iconClass} aria-hidden="true">{@render iconEnd()}</span>
|
|
92
144
|
{/if}
|
|
93
|
-
{@render children()}
|
|
94
145
|
</svelte:element>
|