@nqmcreative/ui 0.1.0 → 0.2.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 +151 -439
- package/cli/index.mjs +67 -30
- package/dist/core/calendar.d.ts +3 -0
- package/dist/core/calendar.js +36 -0
- package/dist/core/dialog.svelte.d.ts +14 -0
- package/dist/core/dialog.svelte.js +23 -0
- package/dist/core/files.d.ts +35 -0
- package/dist/core/files.js +62 -0
- package/dist/core/index.d.ts +37 -0
- package/dist/core/index.js +35 -0
- package/dist/core/list.svelte.d.ts +41 -0
- package/dist/core/list.svelte.js +77 -0
- package/dist/core/number.d.ts +7 -0
- package/dist/core/number.js +17 -0
- package/dist/core/pagination.d.ts +6 -0
- package/dist/core/pagination.js +21 -0
- package/dist/core/password.d.ts +8 -0
- package/dist/core/password.js +23 -0
- package/dist/core/table.d.ts +22 -0
- package/dist/core/table.js +41 -0
- package/dist/core/theme.svelte.d.ts +16 -0
- package/dist/core/theme.svelte.js +34 -0
- package/dist/core/trigger.d.ts +35 -0
- package/dist/core/trigger.js +77 -0
- package/dist/index.d.ts +11 -103
- package/dist/index.js +11 -75
- package/dist/{components → styles/matte}/AccordionItem.svelte +1 -1
- package/dist/{components → styles/matte}/AccordionItem.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Alert.svelte +8 -2
- package/dist/{components → styles/matte}/Alert.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Avatar.svelte +1 -1
- package/dist/{components → styles/matte}/Avatar.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Badge.svelte +8 -3
- package/dist/{components → styles/matte}/Badge.svelte.d.ts +1 -3
- package/dist/{components → styles/matte}/Breadcrumb.svelte +2 -2
- package/dist/{components → styles/matte}/Breadcrumb.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Button.svelte +1 -1
- package/dist/{components → styles/matte}/Button.svelte.d.ts +1 -1
- package/dist/styles/matte/CTASection.svelte +76 -0
- package/dist/styles/matte/CTASection.svelte.d.ts +18 -0
- package/dist/{components → styles/matte}/Calendar.svelte +7 -17
- package/dist/{components → styles/matte}/Calendar.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Card.svelte +1 -1
- package/dist/{components → styles/matte}/Card.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Checkbox.svelte +1 -1
- package/dist/{components → styles/matte}/Checkbox.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/CheckboxGroup.svelte +1 -1
- package/dist/{components → styles/matte}/CheckboxGroup.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Combobox.svelte +31 -42
- package/dist/{components → styles/matte}/Combobox.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/CommandPalette.svelte +26 -41
- package/dist/{components → styles/matte}/CommandPalette.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/ConfirmDialog.svelte +2 -2
- package/dist/{components → styles/matte}/ConfirmDialog.svelte.d.ts +2 -2
- package/dist/{components → styles/matte}/ContextMenu.svelte +5 -7
- package/dist/{components → styles/matte}/DatePicker.svelte +7 -11
- package/dist/{components → styles/matte}/DatePicker.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Drawer.svelte +4 -7
- package/dist/{components → styles/matte}/Dropdown.svelte +18 -36
- package/dist/{components → styles/matte}/Dropdown.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Dropzone.svelte +11 -43
- package/dist/{components → styles/matte}/Dropzone.svelte.d.ts +2 -5
- package/dist/{components → styles/matte}/EmptyState.svelte +1 -1
- package/dist/{components → styles/matte}/EmptyState.svelte.d.ts +1 -1
- package/dist/styles/matte/FeatureCard.svelte +58 -0
- package/dist/styles/matte/FeatureCard.svelte.d.ts +16 -0
- package/dist/styles/matte/FeatureGrid.svelte +62 -0
- package/dist/styles/matte/FeatureGrid.svelte.d.ts +16 -0
- package/dist/{components → styles/matte}/Footer.svelte +1 -1
- package/dist/{components → styles/matte}/Footer.svelte.d.ts +1 -1
- package/dist/styles/matte/HeroSection.svelte +80 -0
- package/dist/styles/matte/HeroSection.svelte.d.ts +21 -0
- package/dist/{components → styles/matte}/Input.svelte +1 -1
- package/dist/{components → styles/matte}/Input.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Link.svelte +1 -1
- package/dist/{components → styles/matte}/Link.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/LocaleProvider.svelte +1 -1
- package/dist/{components → styles/matte}/LocaleProvider.svelte.d.ts +1 -1
- package/dist/styles/matte/LogoCloud.svelte +61 -0
- package/dist/styles/matte/LogoCloud.svelte.d.ts +19 -0
- package/dist/{components → styles/matte}/MenuItem.svelte +1 -1
- package/dist/{components → styles/matte}/MenuItem.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Modal.svelte +4 -7
- package/dist/{components → styles/matte}/MultiSelect.svelte +18 -37
- package/dist/{components → styles/matte}/MultiSelect.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Navbar.svelte +2 -2
- package/dist/{components → styles/matte}/Navbar.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/NumberInput.svelte +4 -8
- package/dist/{components → styles/matte}/NumberInput.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Pagination.svelte +4 -15
- package/dist/{components → styles/matte}/Pagination.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/PasswordInput.svelte +4 -13
- package/dist/{components → styles/matte}/PasswordInput.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Popover.svelte +16 -41
- package/dist/{components → styles/matte}/Popover.svelte.d.ts +1 -1
- package/dist/styles/matte/PricingCard.svelte +107 -0
- package/dist/styles/matte/PricingCard.svelte.d.ts +25 -0
- package/dist/{components → styles/matte}/Progress.svelte +3 -2
- package/dist/{components → styles/matte}/Progress.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Radio.svelte +1 -1
- package/dist/{components → styles/matte}/Radio.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/RadioGroup.svelte +1 -1
- package/dist/{components → styles/matte}/RadioGroup.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/SegmentedControl.svelte +1 -1
- package/dist/{components → styles/matte}/SegmentedControl.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Select.svelte +1 -1
- package/dist/{components → styles/matte}/Select.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Sidebar.svelte +2 -2
- package/dist/{components → styles/matte}/Sidebar.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Slider.svelte +3 -2
- package/dist/{components → styles/matte}/Slider.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Spinner.svelte +2 -2
- package/dist/{components → styles/matte}/Spinner.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Stat.svelte +1 -1
- package/dist/{components → styles/matte}/Stat.svelte.d.ts +1 -1
- package/dist/styles/matte/StatsBand.svelte +57 -0
- package/dist/styles/matte/StatsBand.svelte.d.ts +20 -0
- package/dist/{components → styles/matte}/Steps.svelte +1 -1
- package/dist/{components → styles/matte}/Steps.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Switch.svelte +1 -1
- package/dist/{components → styles/matte}/Switch.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/Table.svelte +6 -28
- package/dist/{components → styles/matte}/Table.svelte.d.ts +2 -6
- package/dist/{components → styles/matte}/Tabs.svelte +8 -1
- package/dist/{components → styles/matte}/Tabs.svelte.d.ts +1 -1
- package/dist/styles/matte/Testimonial.svelte +67 -0
- package/dist/styles/matte/Testimonial.svelte.d.ts +19 -0
- package/dist/{components → styles/matte}/Textarea.svelte +1 -1
- package/dist/{components → styles/matte}/Textarea.svelte.d.ts +1 -1
- package/dist/{components → styles/matte}/ThemeToggle.svelte +14 -27
- package/dist/{components → styles/matte}/ThemeToggle.svelte.d.ts +2 -6
- package/dist/{components → styles/matte}/Toaster.svelte +4 -4
- package/dist/{fonts.css → styles/matte/fonts.css} +4 -3
- package/dist/styles/matte/index.d.ts +109 -0
- package/dist/styles/matte/index.js +83 -0
- package/dist/{theme.css → styles/matte/theme.css} +0 -1
- package/dist/styles/paper/Accordion.svelte +17 -0
- package/dist/styles/paper/Accordion.svelte.d.ts +8 -0
- package/dist/styles/paper/AccordionItem.svelte +46 -0
- package/dist/styles/paper/AccordionItem.svelte.d.ts +14 -0
- package/dist/styles/paper/Alert.svelte +99 -0
- package/dist/styles/paper/Alert.svelte.d.ts +18 -0
- package/dist/styles/paper/Avatar.svelte +60 -0
- package/dist/styles/paper/Avatar.svelte.d.ts +16 -0
- package/dist/styles/paper/AvatarGroup.svelte +17 -0
- package/dist/styles/paper/AvatarGroup.svelte.d.ts +8 -0
- package/dist/styles/paper/Badge.svelte +58 -0
- package/dist/styles/paper/Badge.svelte.d.ts +16 -0
- package/dist/styles/paper/Breadcrumb.svelte +49 -0
- package/dist/styles/paper/Breadcrumb.svelte.d.ts +14 -0
- package/dist/styles/paper/Button.svelte +96 -0
- package/dist/styles/paper/Button.svelte.d.ts +21 -0
- package/dist/styles/paper/CTASection.svelte +76 -0
- package/dist/styles/paper/CTASection.svelte.d.ts +18 -0
- package/dist/styles/paper/Calendar.svelte +202 -0
- package/dist/styles/paper/Calendar.svelte.d.ts +22 -0
- package/dist/styles/paper/Card.svelte +78 -0
- package/dist/styles/paper/Card.svelte.d.ts +21 -0
- package/dist/styles/paper/Checkbox.svelte +75 -0
- package/dist/styles/paper/Checkbox.svelte.d.ts +15 -0
- package/dist/styles/paper/CheckboxGroup.svelte +111 -0
- package/dist/styles/paper/CheckboxGroup.svelte.d.ts +30 -0
- package/dist/styles/paper/Combobox.svelte +266 -0
- package/dist/styles/paper/Combobox.svelte.d.ts +29 -0
- package/dist/styles/paper/CommandPalette.svelte +209 -0
- package/dist/styles/paper/CommandPalette.svelte.d.ts +32 -0
- package/dist/styles/paper/ConfirmDialog.svelte +97 -0
- package/dist/styles/paper/ConfirmDialog.svelte.d.ts +22 -0
- package/dist/styles/paper/ContextMenu.svelte +88 -0
- package/dist/styles/paper/ContextMenu.svelte.d.ts +15 -0
- package/dist/styles/paper/DatePicker.svelte +192 -0
- package/dist/styles/paper/DatePicker.svelte.d.ts +23 -0
- package/dist/styles/paper/Divider.svelte +30 -0
- package/dist/styles/paper/Divider.svelte.d.ts +10 -0
- package/dist/styles/paper/Drawer.svelte +165 -0
- package/dist/styles/paper/Drawer.svelte.d.ts +20 -0
- package/dist/styles/paper/Dropdown.svelte +94 -0
- package/dist/styles/paper/Dropdown.svelte.d.ts +23 -0
- package/dist/styles/paper/Dropzone.svelte +210 -0
- package/dist/styles/paper/Dropzone.svelte.d.ts +29 -0
- package/dist/styles/paper/EmptyState.svelte +71 -0
- package/dist/styles/paper/EmptyState.svelte.d.ts +18 -0
- package/dist/styles/paper/FeatureCard.svelte +58 -0
- package/dist/styles/paper/FeatureCard.svelte.d.ts +16 -0
- package/dist/styles/paper/FeatureGrid.svelte +62 -0
- package/dist/styles/paper/FeatureGrid.svelte.d.ts +16 -0
- package/dist/styles/paper/Field.svelte +42 -0
- package/dist/styles/paper/Field.svelte.d.ts +16 -0
- package/dist/styles/paper/Footer.svelte +91 -0
- package/dist/styles/paper/Footer.svelte.d.ts +25 -0
- package/dist/styles/paper/HeroSection.svelte +86 -0
- package/dist/styles/paper/HeroSection.svelte.d.ts +21 -0
- package/dist/styles/paper/Input.svelte +60 -0
- package/dist/styles/paper/Input.svelte.d.ts +18 -0
- package/dist/styles/paper/InputGroup.svelte +20 -0
- package/dist/styles/paper/InputGroup.svelte.d.ts +11 -0
- package/dist/styles/paper/Kbd.svelte +17 -0
- package/dist/styles/paper/Kbd.svelte.d.ts +8 -0
- package/dist/styles/paper/Label.svelte +16 -0
- package/dist/styles/paper/Label.svelte.d.ts +9 -0
- package/dist/styles/paper/Link.svelte +44 -0
- package/dist/styles/paper/Link.svelte.d.ts +16 -0
- package/dist/styles/paper/LocaleProvider.svelte +24 -0
- package/dist/styles/paper/LocaleProvider.svelte.d.ts +10 -0
- package/dist/styles/paper/LogoCloud.svelte +61 -0
- package/dist/styles/paper/LogoCloud.svelte.d.ts +19 -0
- package/dist/styles/paper/MenuItem.svelte +64 -0
- package/dist/styles/paper/MenuItem.svelte.d.ts +18 -0
- package/dist/styles/paper/MenuSeparator.svelte +22 -0
- package/dist/styles/paper/MenuSeparator.svelte.d.ts +8 -0
- package/dist/styles/paper/Modal.svelte +118 -0
- package/dist/styles/paper/Modal.svelte.d.ts +21 -0
- package/dist/styles/paper/MultiSelect.svelte +287 -0
- package/dist/styles/paper/MultiSelect.svelte.d.ts +31 -0
- package/dist/styles/paper/Navbar.svelte +201 -0
- package/dist/styles/paper/Navbar.svelte.d.ts +33 -0
- package/dist/styles/paper/NumberInput.svelte +96 -0
- package/dist/styles/paper/NumberInput.svelte.d.ts +15 -0
- package/dist/styles/paper/Pagination.svelte +83 -0
- package/dist/styles/paper/Pagination.svelte.d.ts +15 -0
- package/dist/styles/paper/PasswordInput.svelte +100 -0
- package/dist/styles/paper/PasswordInput.svelte.d.ts +12 -0
- package/dist/styles/paper/Popover.svelte +89 -0
- package/dist/styles/paper/Popover.svelte.d.ts +19 -0
- package/dist/styles/paper/PricingCard.svelte +108 -0
- package/dist/styles/paper/PricingCard.svelte.d.ts +25 -0
- package/dist/styles/paper/Progress.svelte +59 -0
- package/dist/styles/paper/Progress.svelte.d.ts +17 -0
- package/dist/styles/paper/Radio.svelte +55 -0
- package/dist/styles/paper/Radio.svelte.d.ts +15 -0
- package/dist/styles/paper/RadioGroup.svelte +95 -0
- package/dist/styles/paper/RadioGroup.svelte.d.ts +27 -0
- package/dist/styles/paper/SegmentedControl.svelte +88 -0
- package/dist/styles/paper/SegmentedControl.svelte.d.ts +25 -0
- package/dist/styles/paper/Select.svelte +61 -0
- package/dist/styles/paper/Select.svelte.d.ts +20 -0
- package/dist/styles/paper/Sidebar.svelte +206 -0
- package/dist/styles/paper/Sidebar.svelte.d.ts +36 -0
- package/dist/styles/paper/Skeleton.svelte +46 -0
- package/dist/styles/paper/Skeleton.svelte.d.ts +13 -0
- package/dist/styles/paper/Slider.svelte +80 -0
- package/dist/styles/paper/Slider.svelte.d.ts +19 -0
- package/dist/styles/paper/Spinner.svelte +34 -0
- package/dist/styles/paper/Spinner.svelte.d.ts +13 -0
- package/dist/styles/paper/Stat.svelte +51 -0
- package/dist/styles/paper/Stat.svelte.d.ts +16 -0
- package/dist/styles/paper/StatsBand.svelte +63 -0
- package/dist/styles/paper/StatsBand.svelte.d.ts +20 -0
- package/dist/styles/paper/Steps.svelte +141 -0
- package/dist/styles/paper/Steps.svelte.d.ts +24 -0
- package/dist/styles/paper/Switch.svelte +64 -0
- package/dist/styles/paper/Switch.svelte.d.ts +14 -0
- package/dist/styles/paper/Table.svelte +217 -0
- package/dist/styles/paper/Table.svelte.d.ts +66 -0
- package/dist/styles/paper/Tabs.svelte +102 -0
- package/dist/styles/paper/Tabs.svelte.d.ts +23 -0
- package/dist/styles/paper/Testimonial.svelte +64 -0
- package/dist/styles/paper/Testimonial.svelte.d.ts +19 -0
- package/dist/styles/paper/Textarea.svelte +54 -0
- package/dist/styles/paper/Textarea.svelte.d.ts +12 -0
- package/dist/styles/paper/ThemeToggle.svelte +130 -0
- package/dist/styles/paper/ThemeToggle.svelte.d.ts +16 -0
- package/dist/styles/paper/Toaster.svelte +132 -0
- package/dist/styles/paper/Toaster.svelte.d.ts +8 -0
- package/dist/styles/paper/Tooltip.svelte +33 -0
- package/dist/styles/paper/Tooltip.svelte.d.ts +12 -0
- package/dist/styles/paper/fonts.css +46 -0
- package/dist/styles/paper/index.d.ts +109 -0
- package/dist/styles/paper/index.js +83 -0
- package/dist/styles/paper/theme.css +222 -0
- package/package.json +531 -201
- package/registry.json +658 -339
- /package/dist/{actions → core/actions}/anchor.d.ts +0 -0
- /package/dist/{actions → core/actions}/anchor.js +0 -0
- /package/dist/{actions → core/actions}/dismissable.d.ts +0 -0
- /package/dist/{actions → core/actions}/dismissable.js +0 -0
- /package/dist/{date.d.ts → core/date.d.ts} +0 -0
- /package/dist/{date.js → core/date.js} +0 -0
- /package/dist/{locale.svelte.d.ts → core/locale.svelte.d.ts} +0 -0
- /package/dist/{locale.svelte.js → core/locale.svelte.js} +0 -0
- /package/dist/{toast.svelte.d.ts → core/toast.svelte.d.ts} +0 -0
- /package/dist/{toast.svelte.js → core/toast.svelte.js} +0 -0
- /package/dist/{tones.d.ts → core/tones.d.ts} +0 -0
- /package/dist/{tones.js → core/tones.js} +0 -0
- /package/dist/{components → styles/matte}/Accordion.svelte +0 -0
- /package/dist/{components → styles/matte}/Accordion.svelte.d.ts +0 -0
- /package/dist/{components → styles/matte}/AvatarGroup.svelte +0 -0
- /package/dist/{components → styles/matte}/AvatarGroup.svelte.d.ts +0 -0
- /package/dist/{components → styles/matte}/ContextMenu.svelte.d.ts +0 -0
- /package/dist/{components → styles/matte}/Divider.svelte +0 -0
- /package/dist/{components → styles/matte}/Divider.svelte.d.ts +0 -0
- /package/dist/{components → styles/matte}/Drawer.svelte.d.ts +0 -0
- /package/dist/{components → styles/matte}/Field.svelte +0 -0
- /package/dist/{components → styles/matte}/Field.svelte.d.ts +0 -0
- /package/dist/{components → styles/matte}/InputGroup.svelte +0 -0
- /package/dist/{components → styles/matte}/InputGroup.svelte.d.ts +0 -0
- /package/dist/{components → styles/matte}/Kbd.svelte +0 -0
- /package/dist/{components → styles/matte}/Kbd.svelte.d.ts +0 -0
- /package/dist/{components → styles/matte}/Label.svelte +0 -0
- /package/dist/{components → styles/matte}/Label.svelte.d.ts +0 -0
- /package/dist/{components → styles/matte}/MenuSeparator.svelte +0 -0
- /package/dist/{components → styles/matte}/MenuSeparator.svelte.d.ts +0 -0
- /package/dist/{components → styles/matte}/Modal.svelte.d.ts +0 -0
- /package/dist/{components → styles/matte}/Skeleton.svelte +0 -0
- /package/dist/{components → styles/matte}/Skeleton.svelte.d.ts +0 -0
- /package/dist/{components → styles/matte}/Toaster.svelte.d.ts +0 -0
- /package/dist/{components → styles/matte}/Tooltip.svelte +0 -0
- /package/dist/{components → styles/matte}/Tooltip.svelte.d.ts +0 -0
package/cli/index.mjs
CHANGED
|
@@ -23,7 +23,7 @@ const argv = process.argv.slice(2);
|
|
|
23
23
|
const flags = new Set(argv.filter((a) => a.startsWith('--')));
|
|
24
24
|
|
|
25
25
|
/** Flags that take a value, so the value is not mistaken for a positional. */
|
|
26
|
-
const valueFlags = new Set(['--to']);
|
|
26
|
+
const valueFlags = new Set(['--to', '--style']);
|
|
27
27
|
const args = [];
|
|
28
28
|
for (let i = 0; i < argv.length; i++) {
|
|
29
29
|
if (argv[i].startsWith('--')) {
|
|
@@ -66,15 +66,37 @@ async function registry() {
|
|
|
66
66
|
fail('registry.json not found — is the package installed correctly?');
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
/** Accepts `Button`, `button`, `date-picker` or
|
|
69
|
+
/** Accepts `Button`, `button`, `date-picker` or any style's full subpath. */
|
|
70
70
|
function findComponent(list, query) {
|
|
71
|
-
const needle = query.toLowerCase().replace(/^@nqmcreative\/ui
|
|
72
|
-
return list.find(
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
71
|
+
const needle = query.toLowerCase().replace(/^@nqmcreative\/ui\/[a-z-]+\//, '');
|
|
72
|
+
return list.find((item) => item.name.toLowerCase() === needle || item.slug === needle);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* The style to work in. There is no default on purpose: a component belongs to
|
|
77
|
+
* a style, and picking one by accident is the mistake this package exists to
|
|
78
|
+
* prevent. Asking once beats shipping the wrong look.
|
|
79
|
+
*/
|
|
80
|
+
function pickStyle(styles) {
|
|
81
|
+
const index = argv.indexOf('--style');
|
|
82
|
+
const wanted = index !== -1 ? argv[index + 1] : null;
|
|
83
|
+
|
|
84
|
+
if (!wanted) {
|
|
85
|
+
log();
|
|
86
|
+
log(' which style?');
|
|
87
|
+
log();
|
|
88
|
+
for (const style of styles) {
|
|
89
|
+
log(` ${c.cyan(style.name.padEnd(8))} ${c.dim(style.description)}`);
|
|
90
|
+
}
|
|
91
|
+
log();
|
|
92
|
+
log(` ${c.dim(`e.g. ${command} --style ${styles[0].name}`)}`);
|
|
93
|
+
log();
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const style = styles.find((s) => s.name === wanted);
|
|
98
|
+
if (!style) fail(`unknown style "${wanted}" — try ${styles.map((s) => s.name).join(' or ')}`);
|
|
99
|
+
return style;
|
|
78
100
|
}
|
|
79
101
|
|
|
80
102
|
/* ------------------------------------------------------------ file edits -- */
|
|
@@ -128,10 +150,12 @@ function sourcePath(cssRel) {
|
|
|
128
150
|
/* ------------------------------------------------------------------ init -- */
|
|
129
151
|
|
|
130
152
|
async function init() {
|
|
153
|
+
const { styles } = await registry();
|
|
154
|
+
const style = pickStyle(styles);
|
|
131
155
|
const project = await detectProject();
|
|
132
156
|
|
|
133
157
|
log();
|
|
134
|
-
log(c.bold(
|
|
158
|
+
log(c.bold(` @nqmcreative/ui — ${style.title}`));
|
|
135
159
|
log(c.dim(` ${project.isKit ? 'SvelteKit' : 'Svelte + Vite'} project detected`));
|
|
136
160
|
log();
|
|
137
161
|
|
|
@@ -151,16 +175,20 @@ async function init() {
|
|
|
151
175
|
const source = sourcePath(project.cssRel);
|
|
152
176
|
const lines = [
|
|
153
177
|
`@import 'tailwindcss';`,
|
|
154
|
-
`@import '@nqmcreative/ui/theme.css';`,
|
|
155
|
-
`@import '@nqmcreative/ui/fonts.css'
|
|
178
|
+
`@import '@nqmcreative/ui/${style.name}/theme.css';`,
|
|
179
|
+
...(style.fonts ? [`@import '@nqmcreative/ui/${style.name}/fonts.css';`] : []),
|
|
156
180
|
``,
|
|
157
|
-
`/* Tailwind v4 skips node_modules — point it at
|
|
158
|
-
` the class names used inside
|
|
159
|
-
`@source '${source}';`
|
|
181
|
+
`/* Tailwind v4 skips node_modules — point it at this style's folder so`,
|
|
182
|
+
` the class names used inside its components are generated. */`,
|
|
183
|
+
`@source '${source}/styles/${style.name}';`
|
|
160
184
|
];
|
|
161
185
|
|
|
162
186
|
const css = await readOr(project.css);
|
|
163
|
-
|
|
187
|
+
|
|
188
|
+
// Matched against *this* style's theme, not any of them. A file left over
|
|
189
|
+
// from an older layout — or wired to a different style — has to be reported,
|
|
190
|
+
// not skipped as already done.
|
|
191
|
+
if (css.includes(`@nqmcreative/ui/${style.name}/theme.css`)) {
|
|
164
192
|
skip(`${project.cssRel} already imports the theme`);
|
|
165
193
|
} else {
|
|
166
194
|
const body = css.trim()
|
|
@@ -197,7 +225,7 @@ async function init() {
|
|
|
197
225
|
// Unindented — `%sveltekit.head%` already sits at its own indentation,
|
|
198
226
|
// so the join below supplies it and the first line does not double up.
|
|
199
227
|
const additions = [];
|
|
200
|
-
if (!html.includes('fonts.gstatic.com')) {
|
|
228
|
+
if (style.fonts && !html.includes('fonts.gstatic.com')) {
|
|
201
229
|
additions.push(`<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />`);
|
|
202
230
|
}
|
|
203
231
|
if (!html.includes('nqm-theme')) {
|
|
@@ -272,14 +300,15 @@ async function init() {
|
|
|
272
300
|
}
|
|
273
301
|
|
|
274
302
|
log();
|
|
275
|
-
log(` Next: ${c.cyan(
|
|
303
|
+
log(` Next: ${c.cyan(`bunx @nqmcreative/ui add button --style ${style.name}`)}`);
|
|
276
304
|
log();
|
|
277
305
|
}
|
|
278
306
|
|
|
279
307
|
/* ------------------------------------------------------------------- add -- */
|
|
280
308
|
|
|
281
309
|
async function add() {
|
|
282
|
-
const { components } = await registry();
|
|
310
|
+
const { components, styles } = await registry();
|
|
311
|
+
const style = pickStyle(styles);
|
|
283
312
|
const names = args.slice(1);
|
|
284
313
|
if (names.length === 0) fail('which component? e.g. `nqm-ui add button dialog`');
|
|
285
314
|
|
|
@@ -290,7 +319,7 @@ async function add() {
|
|
|
290
319
|
const close = components
|
|
291
320
|
.filter((c) => c.name.toLowerCase().startsWith(name.slice(0, 3).toLowerCase()))
|
|
292
321
|
.slice(0, 3)
|
|
293
|
-
.map((c) => c.
|
|
322
|
+
.map((c) => c.slug);
|
|
294
323
|
fail(
|
|
295
324
|
`unknown component "${name}"${close.length ? ` — did you mean ${close.join(', ')}?` : ''}`
|
|
296
325
|
);
|
|
@@ -298,7 +327,7 @@ async function add() {
|
|
|
298
327
|
found.push(item);
|
|
299
328
|
}
|
|
300
329
|
|
|
301
|
-
const imports = found.map((item) => `import ${item.name} from '${item.
|
|
330
|
+
const imports = found.map((item) => `import ${item.name} from '${item.subpaths[style.name]}';`);
|
|
302
331
|
|
|
303
332
|
// `--to <file>` inserts the imports into an existing Svelte file.
|
|
304
333
|
const toIndex = argv.indexOf('--to');
|
|
@@ -339,7 +368,7 @@ async function add() {
|
|
|
339
368
|
/* ------------------------------------------------------------------ list -- */
|
|
340
369
|
|
|
341
370
|
async function list() {
|
|
342
|
-
const { components, count } = await registry();
|
|
371
|
+
const { components, count, styles } = await registry();
|
|
343
372
|
const filter = args[1]?.toLowerCase();
|
|
344
373
|
|
|
345
374
|
const groups = {};
|
|
@@ -355,11 +384,15 @@ async function list() {
|
|
|
355
384
|
for (const [category, items] of Object.entries(groups)) {
|
|
356
385
|
log(` ${c.bold(category)}`);
|
|
357
386
|
for (const item of items) {
|
|
358
|
-
log(` ${item.
|
|
387
|
+
log(` ${item.slug.padEnd(20)} ${c.dim(item.description)}`);
|
|
359
388
|
}
|
|
360
389
|
log();
|
|
361
390
|
}
|
|
362
|
-
log(
|
|
391
|
+
log(
|
|
392
|
+
c.dim(
|
|
393
|
+
` ${shown} of ${count} components, in every style: ${styles.map((s) => s.name).join(', ')}`
|
|
394
|
+
)
|
|
395
|
+
);
|
|
363
396
|
log();
|
|
364
397
|
}
|
|
365
398
|
|
|
@@ -374,11 +407,12 @@ async function info() {
|
|
|
374
407
|
log(` ${c.bold(item.name)} ${c.dim(item.category)}`);
|
|
375
408
|
if (item.description) log(` ${item.description}`);
|
|
376
409
|
log();
|
|
377
|
-
|
|
410
|
+
for (const [style, subpath] of Object.entries(item.subpaths)) {
|
|
411
|
+
log(` ${c.dim(style.padEnd(8))} ${c.cyan(`import ${item.name} from '${subpath}';`)}`);
|
|
412
|
+
}
|
|
378
413
|
log();
|
|
379
414
|
if (item.uses.length) log(` renders ${item.uses.join(', ')}`);
|
|
380
|
-
if (item.modules.length) log(`
|
|
381
|
-
log(` source ${item.file}`);
|
|
415
|
+
if (item.modules.length) log(` core ${item.modules.join(', ')}`);
|
|
382
416
|
log();
|
|
383
417
|
}
|
|
384
418
|
|
|
@@ -388,13 +422,16 @@ function help() {
|
|
|
388
422
|
log(`
|
|
389
423
|
${c.bold('@nqmcreative/ui')}
|
|
390
424
|
|
|
391
|
-
${c.cyan('init')}
|
|
425
|
+
${c.cyan('init --style <s>')} wire one style into this project
|
|
392
426
|
${c.cyan('add <name...>')} print the import for a component
|
|
427
|
+
${c.dim('--style <s>')} which style to import from (required)
|
|
393
428
|
${c.dim('--to <file>')} …and insert it into that file instead
|
|
394
|
-
${c.cyan('list [category]')} every component
|
|
395
|
-
${c.cyan('info <name>')} what one component pulls in
|
|
429
|
+
${c.cyan('list [category]')} every component in the catalogue
|
|
430
|
+
${c.cyan('info <name>')} what one component pulls in, in every style
|
|
396
431
|
|
|
397
432
|
${c.dim('--dry-run')} show the writes without making them
|
|
433
|
+
|
|
434
|
+
${c.dim('Every style implements every component. Run init once per project.')}
|
|
398
435
|
`);
|
|
399
436
|
}
|
|
400
437
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keyboard movement inside a month grid.
|
|
3
|
+
*
|
|
4
|
+
* The arrow keys are obvious; Home/End are not — they go to the start and end
|
|
5
|
+
* of the *displayed* week, which depends on whether the grid starts on Monday
|
|
6
|
+
* or Sunday. Returns the day focus should move to, or `null` for a key the
|
|
7
|
+
* grid does not handle.
|
|
8
|
+
*/
|
|
9
|
+
import { addDays, addMonths } from './date.js';
|
|
10
|
+
export function calendarKeyMove(key, focused, weekStart, shiftKey = false) {
|
|
11
|
+
const intoWeek = (focused.getDay() - weekStart + 7) % 7;
|
|
12
|
+
switch (key) {
|
|
13
|
+
case 'ArrowLeft':
|
|
14
|
+
return addDays(focused, -1);
|
|
15
|
+
case 'ArrowRight':
|
|
16
|
+
return addDays(focused, 1);
|
|
17
|
+
case 'ArrowUp':
|
|
18
|
+
return addDays(focused, -7);
|
|
19
|
+
case 'ArrowDown':
|
|
20
|
+
return addDays(focused, 7);
|
|
21
|
+
case 'Home':
|
|
22
|
+
return addDays(focused, -intoWeek);
|
|
23
|
+
case 'End':
|
|
24
|
+
return addDays(focused, 6 - intoWeek);
|
|
25
|
+
case 'PageUp':
|
|
26
|
+
return addMonths(focused, shiftKey ? -12 : -1);
|
|
27
|
+
case 'PageDown':
|
|
28
|
+
return addMonths(focused, shiftKey ? 12 : 1);
|
|
29
|
+
default:
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/** The placeholder a text date field shows for a locale's part order. */
|
|
34
|
+
export function dateHint(format) {
|
|
35
|
+
return { dmy: 'DD/MM/YYYY', mdy: 'MM/DD/YYYY', ymd: 'YYYY/MM/DD' }[format];
|
|
36
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keeps a bound `open` prop and a native `<dialog>` in step.
|
|
3
|
+
*
|
|
4
|
+
* `showModal()` throws if the dialog is already open and `close()` fires an
|
|
5
|
+
* event of its own, so both directions need the guard below. Every overlay
|
|
6
|
+
* built on `<dialog>` — Modal, Drawer, CommandPalette — calls this from an
|
|
7
|
+
* effect rather than repeating the two `if`s.
|
|
8
|
+
*/
|
|
9
|
+
export declare function syncDialog(dialog: HTMLDialogElement | null, open: boolean): void;
|
|
10
|
+
/**
|
|
11
|
+
* True when a click landed on the backdrop rather than inside the panel.
|
|
12
|
+
* The backdrop is not a separate element — it is the dialog's own box.
|
|
13
|
+
*/
|
|
14
|
+
export declare function isBackdropClick(event: MouseEvent, dialog: HTMLDialogElement | null): boolean;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keeps a bound `open` prop and a native `<dialog>` in step.
|
|
3
|
+
*
|
|
4
|
+
* `showModal()` throws if the dialog is already open and `close()` fires an
|
|
5
|
+
* event of its own, so both directions need the guard below. Every overlay
|
|
6
|
+
* built on `<dialog>` — Modal, Drawer, CommandPalette — calls this from an
|
|
7
|
+
* effect rather than repeating the two `if`s.
|
|
8
|
+
*/
|
|
9
|
+
export function syncDialog(dialog, open) {
|
|
10
|
+
if (!dialog)
|
|
11
|
+
return;
|
|
12
|
+
if (open && !dialog.open)
|
|
13
|
+
dialog.showModal();
|
|
14
|
+
else if (!open && dialog.open)
|
|
15
|
+
dialog.close();
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* True when a click landed on the backdrop rather than inside the panel.
|
|
19
|
+
* The backdrop is not a separate element — it is the dialog's own box.
|
|
20
|
+
*/
|
|
21
|
+
export function isBackdropClick(event, dialog) {
|
|
22
|
+
return !!dialog && event.target === dialog;
|
|
23
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File validation for the drop zone: which files a set of rules lets through,
|
|
3
|
+
* why the rest were turned away, and how to print a byte count.
|
|
4
|
+
*/
|
|
5
|
+
export interface RejectedFile {
|
|
6
|
+
file: File;
|
|
7
|
+
reason: 'type' | 'size' | 'count';
|
|
8
|
+
}
|
|
9
|
+
export interface FileRules {
|
|
10
|
+
/** Same syntax as the input's `accept` — `'image/*,.pdf'`. */
|
|
11
|
+
accept?: string;
|
|
12
|
+
/** Per-file ceiling in bytes. `0` means no limit. */
|
|
13
|
+
maxSize?: number;
|
|
14
|
+
/** Cap on how many files may be held at once. `0` means no limit. */
|
|
15
|
+
maxFiles?: number;
|
|
16
|
+
multiple?: boolean;
|
|
17
|
+
/** How many are already held, counted against `maxFiles`. */
|
|
18
|
+
held?: number;
|
|
19
|
+
}
|
|
20
|
+
/** `image/*`, `.pdf` and `application/pdf` all mean what you would expect. */
|
|
21
|
+
export declare function matchesAccept(file: File, accept?: string): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Sorts one drop into keepers and rejects. Order matters: a file is judged on
|
|
24
|
+
* type first, then size, then whether there is room left — so the reason a
|
|
25
|
+
* user sees is the first rule it actually broke.
|
|
26
|
+
*/
|
|
27
|
+
export declare function sortFiles(incoming: FileList | File[] | null, rules?: FileRules): {
|
|
28
|
+
accepted: File[];
|
|
29
|
+
rejected: RejectedFile[];
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* `900 B`, `2 MB`, `1.5 GB`. Units come from the locale so the number and its
|
|
33
|
+
* suffix can be translated together.
|
|
34
|
+
*/
|
|
35
|
+
export declare function formatSize(bytes: number, units: readonly string[]): string;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File validation for the drop zone: which files a set of rules lets through,
|
|
3
|
+
* why the rest were turned away, and how to print a byte count.
|
|
4
|
+
*/
|
|
5
|
+
/** `image/*`, `.pdf` and `application/pdf` all mean what you would expect. */
|
|
6
|
+
export function matchesAccept(file, accept) {
|
|
7
|
+
if (!accept)
|
|
8
|
+
return true;
|
|
9
|
+
return accept.split(',').some((raw) => {
|
|
10
|
+
const rule = raw.trim().toLowerCase();
|
|
11
|
+
if (!rule)
|
|
12
|
+
return true;
|
|
13
|
+
if (rule.startsWith('.'))
|
|
14
|
+
return file.name.toLowerCase().endsWith(rule);
|
|
15
|
+
if (rule.endsWith('/*'))
|
|
16
|
+
return file.type.startsWith(rule.slice(0, -1));
|
|
17
|
+
return file.type.toLowerCase() === rule;
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Sorts one drop into keepers and rejects. Order matters: a file is judged on
|
|
22
|
+
* type first, then size, then whether there is room left — so the reason a
|
|
23
|
+
* user sees is the first rule it actually broke.
|
|
24
|
+
*/
|
|
25
|
+
export function sortFiles(incoming, rules = {}) {
|
|
26
|
+
const accepted = [];
|
|
27
|
+
const rejected = [];
|
|
28
|
+
if (!incoming)
|
|
29
|
+
return { accepted, rejected };
|
|
30
|
+
const { accept, maxSize = 0, maxFiles = 0, multiple = true, held = 0 } = rules;
|
|
31
|
+
let room = maxFiles ? maxFiles - (multiple ? held : 0) : Infinity;
|
|
32
|
+
for (const file of Array.from(incoming)) {
|
|
33
|
+
if (!matchesAccept(file, accept))
|
|
34
|
+
rejected.push({ file, reason: 'type' });
|
|
35
|
+
else if (maxSize && file.size > maxSize)
|
|
36
|
+
rejected.push({ file, reason: 'size' });
|
|
37
|
+
else if (room <= 0)
|
|
38
|
+
rejected.push({ file, reason: 'count' });
|
|
39
|
+
else {
|
|
40
|
+
accepted.push(file);
|
|
41
|
+
room--;
|
|
42
|
+
}
|
|
43
|
+
if (!multiple && accepted.length === 1)
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
return { accepted, rejected };
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* `900 B`, `2 MB`, `1.5 GB`. Units come from the locale so the number and its
|
|
50
|
+
* suffix can be translated together.
|
|
51
|
+
*/
|
|
52
|
+
export function formatSize(bytes, units) {
|
|
53
|
+
if (bytes < 1024)
|
|
54
|
+
return `${bytes} ${units[0]}`;
|
|
55
|
+
let value = bytes / 1024;
|
|
56
|
+
let i = 1;
|
|
57
|
+
while (value >= 1024 && i < units.length - 1) {
|
|
58
|
+
value /= 1024;
|
|
59
|
+
i++;
|
|
60
|
+
}
|
|
61
|
+
return `${value.toFixed(Number.isInteger(value) || value >= 10 ? 0 : 1)} ${units[i]}`;
|
|
62
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @nqmcreative/ui/core — everything the styles share.
|
|
3
|
+
*
|
|
4
|
+
* Behaviour, not looks: focus containment, keyboard navigation, date maths,
|
|
5
|
+
* file validation, the toast queue, the locale strings. Nothing in here
|
|
6
|
+
* renders anything, and nothing in here knows which style is being used.
|
|
7
|
+
*
|
|
8
|
+
* Styles are free to ignore any of it — but there is no `matte` version of a
|
|
9
|
+
* focus trap, so none of them should have to write one.
|
|
10
|
+
*/
|
|
11
|
+
export { TONES, focusRing, peerFocusRing, toneBorder, toneBorderLeft, toneBorderSoft, toneFill, toneFocusBorder, toneFocusWithinBorder, toneHoverBorder, toneHoverText, tonePeerChecked, tonePeerFocus, toneRing, toneSoft, toneSoftHover, toneSolid, toneSolidHover, toneSurface, toneText } from './tones.js';
|
|
12
|
+
export type { Tone } from './tones.js';
|
|
13
|
+
export { anchored } from './actions/anchor.js';
|
|
14
|
+
export type { Placement, AnchorOptions } from './actions/anchor.js';
|
|
15
|
+
export { clickOutside, focusTrap, focusable, navigateList, portal } from './actions/dismissable.js';
|
|
16
|
+
export type { ClickOutsideOptions, FocusTrapOptions } from './actions/dismissable.js';
|
|
17
|
+
export { syncDialog, isBackdropClick } from './dialog.svelte.js';
|
|
18
|
+
export { popupTrigger, describeTrigger, menuItems, HOVER_GRACE } from './trigger.js';
|
|
19
|
+
export type { PopupTriggerOptions } from './trigger.js';
|
|
20
|
+
export { ListCursor, groupItems, matchQuery, revealIndex } from './list.svelte.js';
|
|
21
|
+
export type { ListItem } from './list.svelte.js';
|
|
22
|
+
export { nextSort, sortRows, toggleKey } from './table.js';
|
|
23
|
+
export type { TableSort, SortDirection } from './table.js';
|
|
24
|
+
export { pageRange } from './pagination.js';
|
|
25
|
+
export { sortFiles, matchesAccept, formatSize } from './files.js';
|
|
26
|
+
export type { RejectedFile, FileRules } from './files.js';
|
|
27
|
+
export { passwordScore } from './password.js';
|
|
28
|
+
export { clampToStep, percentOf, stepDecimals } from './number.js';
|
|
29
|
+
export { toISO, fromISO, today, isSameDay, addDays, addMonths, monthGrid, weekdayNames, monthLabel, isOutOfRange, formatISO, parseFormatted } from './date.js';
|
|
30
|
+
export type { DateFormat } from './date.js';
|
|
31
|
+
export { calendarKeyMove, dateHint } from './calendar.js';
|
|
32
|
+
export { toast } from './toast.svelte.js';
|
|
33
|
+
export type { Toast, ToastOptions, ToastAction } from './toast.svelte.js';
|
|
34
|
+
export { applyTheme, storedTheme, persistTheme, nextTheme } from './theme.svelte.js';
|
|
35
|
+
export type { Theme } from './theme.svelte.js';
|
|
36
|
+
export { locale, setLocale, provideLocale, useLocale, enUS, idID, LocaleStore } from './locale.svelte.js';
|
|
37
|
+
export type { Locale } from './locale.svelte.js';
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @nqmcreative/ui/core — everything the styles share.
|
|
3
|
+
*
|
|
4
|
+
* Behaviour, not looks: focus containment, keyboard navigation, date maths,
|
|
5
|
+
* file validation, the toast queue, the locale strings. Nothing in here
|
|
6
|
+
* renders anything, and nothing in here knows which style is being used.
|
|
7
|
+
*
|
|
8
|
+
* Styles are free to ignore any of it — but there is no `matte` version of a
|
|
9
|
+
* focus trap, so none of them should have to write one.
|
|
10
|
+
*/
|
|
11
|
+
/* --- tones --- */
|
|
12
|
+
export { TONES, focusRing, peerFocusRing, toneBorder, toneBorderLeft, toneBorderSoft, toneFill, toneFocusBorder, toneFocusWithinBorder, toneHoverBorder, toneHoverText, tonePeerChecked, tonePeerFocus, toneRing, toneSoft, toneSoftHover, toneSolid, toneSolidHover, toneSurface, toneText } from './tones.js';
|
|
13
|
+
/* --- actions --- */
|
|
14
|
+
export { anchored } from './actions/anchor.js';
|
|
15
|
+
export { clickOutside, focusTrap, focusable, navigateList, portal } from './actions/dismissable.js';
|
|
16
|
+
/* --- overlays --- */
|
|
17
|
+
export { syncDialog, isBackdropClick } from './dialog.svelte.js';
|
|
18
|
+
export { popupTrigger, describeTrigger, menuItems, HOVER_GRACE } from './trigger.js';
|
|
19
|
+
/* --- lists --- */
|
|
20
|
+
export { ListCursor, groupItems, matchQuery, revealIndex } from './list.svelte.js';
|
|
21
|
+
/* --- data --- */
|
|
22
|
+
export { nextSort, sortRows, toggleKey } from './table.js';
|
|
23
|
+
export { pageRange } from './pagination.js';
|
|
24
|
+
export { sortFiles, matchesAccept, formatSize } from './files.js';
|
|
25
|
+
export { passwordScore } from './password.js';
|
|
26
|
+
export { clampToStep, percentOf, stepDecimals } from './number.js';
|
|
27
|
+
/* --- dates --- */
|
|
28
|
+
export { toISO, fromISO, today, isSameDay, addDays, addMonths, monthGrid, weekdayNames, monthLabel, isOutOfRange, formatISO, parseFormatted } from './date.js';
|
|
29
|
+
export { calendarKeyMove, dateHint } from './calendar.js';
|
|
30
|
+
/* --- toast --- */
|
|
31
|
+
export { toast } from './toast.svelte.js';
|
|
32
|
+
/* --- theme --- */
|
|
33
|
+
export { applyTheme, storedTheme, persistTheme, nextTheme } from './theme.svelte.js';
|
|
34
|
+
/* --- locale --- */
|
|
35
|
+
export { locale, setLocale, provideLocale, useLocale, enUS, idID, LocaleStore } from './locale.svelte.js';
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The list machinery behind every "type to filter, arrow to choose" control —
|
|
3
|
+
* Combobox, MultiSelect, CommandPalette.
|
|
4
|
+
*
|
|
5
|
+
* All three want the same four things: match a query, bundle the matches under
|
|
6
|
+
* their group headings, flatten them back into one indexable run, and move an
|
|
7
|
+
* active cursor over that run while skipping disabled entries. None of it has
|
|
8
|
+
* an opinion about how a row looks, so all of it lives here.
|
|
9
|
+
*/
|
|
10
|
+
export interface ListItem {
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
/** Items sharing a group are rendered under one heading. */
|
|
13
|
+
group?: string;
|
|
14
|
+
}
|
|
15
|
+
/** Case-insensitive substring match over whichever fields a control cares about. */
|
|
16
|
+
export declare function matchQuery<T>(items: T[], query: string, haystack: (item: T) => string): T[];
|
|
17
|
+
/**
|
|
18
|
+
* Buckets items by `group`, keeping first-seen order for both the groups and
|
|
19
|
+
* the items inside them. Ungrouped items land under `''`.
|
|
20
|
+
*/
|
|
21
|
+
export declare function groupItems<T extends ListItem>(items: T[]): [string, T[]][];
|
|
22
|
+
/**
|
|
23
|
+
* An active-item cursor over a list that changes as the user types.
|
|
24
|
+
*
|
|
25
|
+
* `items` is a getter rather than an array so the cursor always reads the
|
|
26
|
+
* current matches — the caller keeps ownership of the filtering.
|
|
27
|
+
*/
|
|
28
|
+
export declare class ListCursor<T extends ListItem> {
|
|
29
|
+
#private;
|
|
30
|
+
/** Index into the flattened list. */
|
|
31
|
+
index: number;
|
|
32
|
+
constructor(items: () => T[]);
|
|
33
|
+
get items(): T[];
|
|
34
|
+
get current(): T | undefined;
|
|
35
|
+
/** Jumps to the first item matching `predicate`, or to the top. */
|
|
36
|
+
reset(predicate?: (item: T) => boolean): void;
|
|
37
|
+
/** Wraps around, skipping disabled entries. */
|
|
38
|
+
move(step: number): void;
|
|
39
|
+
}
|
|
40
|
+
/** Brings the row carrying `data-index` into view without scrolling the page. */
|
|
41
|
+
export declare function revealIndex(container: HTMLElement | null, index: number): void;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The list machinery behind every "type to filter, arrow to choose" control —
|
|
3
|
+
* Combobox, MultiSelect, CommandPalette.
|
|
4
|
+
*
|
|
5
|
+
* All three want the same four things: match a query, bundle the matches under
|
|
6
|
+
* their group headings, flatten them back into one indexable run, and move an
|
|
7
|
+
* active cursor over that run while skipping disabled entries. None of it has
|
|
8
|
+
* an opinion about how a row looks, so all of it lives here.
|
|
9
|
+
*/
|
|
10
|
+
/** Case-insensitive substring match over whichever fields a control cares about. */
|
|
11
|
+
export function matchQuery(items, query, haystack) {
|
|
12
|
+
const needle = query.trim().toLowerCase();
|
|
13
|
+
if (!needle)
|
|
14
|
+
return items;
|
|
15
|
+
return items.filter((item) => haystack(item).toLowerCase().includes(needle));
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Buckets items by `group`, keeping first-seen order for both the groups and
|
|
19
|
+
* the items inside them. Ungrouped items land under `''`.
|
|
20
|
+
*/
|
|
21
|
+
export function groupItems(items) {
|
|
22
|
+
const out = [];
|
|
23
|
+
for (const item of items) {
|
|
24
|
+
const key = item.group ?? '';
|
|
25
|
+
const bucket = out.find(([name]) => name === key);
|
|
26
|
+
if (bucket)
|
|
27
|
+
bucket[1].push(item);
|
|
28
|
+
else
|
|
29
|
+
out.push([key, [item]]);
|
|
30
|
+
}
|
|
31
|
+
return out;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* An active-item cursor over a list that changes as the user types.
|
|
35
|
+
*
|
|
36
|
+
* `items` is a getter rather than an array so the cursor always reads the
|
|
37
|
+
* current matches — the caller keeps ownership of the filtering.
|
|
38
|
+
*/
|
|
39
|
+
export class ListCursor {
|
|
40
|
+
#items;
|
|
41
|
+
/** Index into the flattened list. */
|
|
42
|
+
index = $state(0);
|
|
43
|
+
constructor(items) {
|
|
44
|
+
this.#items = items;
|
|
45
|
+
}
|
|
46
|
+
get items() {
|
|
47
|
+
return this.#items();
|
|
48
|
+
}
|
|
49
|
+
get current() {
|
|
50
|
+
return this.items[this.index];
|
|
51
|
+
}
|
|
52
|
+
/** Jumps to the first item matching `predicate`, or to the top. */
|
|
53
|
+
reset(predicate) {
|
|
54
|
+
if (!predicate) {
|
|
55
|
+
this.index = 0;
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
this.index = Math.max(0, this.items.findIndex(predicate));
|
|
59
|
+
}
|
|
60
|
+
/** Wraps around, skipping disabled entries. */
|
|
61
|
+
move(step) {
|
|
62
|
+
const items = this.items;
|
|
63
|
+
if (items.length === 0)
|
|
64
|
+
return;
|
|
65
|
+
let next = this.index;
|
|
66
|
+
for (let i = 0; i < items.length; i++) {
|
|
67
|
+
next = (next + step + items.length) % items.length;
|
|
68
|
+
if (!items[next].disabled)
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
this.index = next;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/** Brings the row carrying `data-index` into view without scrolling the page. */
|
|
75
|
+
export function revealIndex(container, index) {
|
|
76
|
+
container?.querySelector(`[data-index="${index}"]`)?.scrollIntoView({ block: 'nearest' });
|
|
77
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Numeric helpers shared by the stepper, the slider and the progress bar. */
|
|
2
|
+
/** Decimal places implied by a step, so `0.25` never produces `0.7500000001`. */
|
|
3
|
+
export declare function stepDecimals(step: number): number;
|
|
4
|
+
/** Clamps into range and rounds to the step's precision. */
|
|
5
|
+
export declare function clampToStep(value: number, min: number, max: number, step: number): number;
|
|
6
|
+
/** Position of `value` within `min…max`, as 0–100. */
|
|
7
|
+
export declare function percentOf(value: number, min: number, max: number): number;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Numeric helpers shared by the stepper, the slider and the progress bar. */
|
|
2
|
+
/** Decimal places implied by a step, so `0.25` never produces `0.7500000001`. */
|
|
3
|
+
export function stepDecimals(step) {
|
|
4
|
+
return (String(step).split('.')[1] ?? '').length;
|
|
5
|
+
}
|
|
6
|
+
/** Clamps into range and rounds to the step's precision. */
|
|
7
|
+
export function clampToStep(value, min, max, step) {
|
|
8
|
+
if (Number.isNaN(value))
|
|
9
|
+
return min === -Infinity ? 0 : min;
|
|
10
|
+
return Number(Math.min(max, Math.max(min, value)).toFixed(stepDecimals(step)));
|
|
11
|
+
}
|
|
12
|
+
/** Position of `value` within `min…max`, as 0–100. */
|
|
13
|
+
export function percentOf(value, min, max) {
|
|
14
|
+
if (max === min)
|
|
15
|
+
return 0;
|
|
16
|
+
return Math.min(100, Math.max(0, ((value - min) / (max - min)) * 100));
|
|
17
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The page numbers a pager should show. Collapses long runs to `…` while
|
|
3
|
+
* always keeping the first page, the last page, and `siblings` either side of
|
|
4
|
+
* the current one — so the control never changes width as you walk through it.
|
|
5
|
+
*/
|
|
6
|
+
export declare function pageRange(page: number, total: number, siblings?: number): (number | '…')[];
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The page numbers a pager should show. Collapses long runs to `…` while
|
|
3
|
+
* always keeping the first page, the last page, and `siblings` either side of
|
|
4
|
+
* the current one — so the control never changes width as you walk through it.
|
|
5
|
+
*/
|
|
6
|
+
export function pageRange(page, total, siblings = 1) {
|
|
7
|
+
if (total <= 5 + siblings * 2) {
|
|
8
|
+
return Array.from({ length: total }, (_, i) => i + 1);
|
|
9
|
+
}
|
|
10
|
+
const start = Math.max(2, page - siblings);
|
|
11
|
+
const end = Math.min(total - 1, page + siblings);
|
|
12
|
+
const out = [1];
|
|
13
|
+
if (start > 2)
|
|
14
|
+
out.push('…');
|
|
15
|
+
for (let i = start; i <= end; i++)
|
|
16
|
+
out.push(i);
|
|
17
|
+
if (end < total - 1)
|
|
18
|
+
out.push('…');
|
|
19
|
+
out.push(total);
|
|
20
|
+
return out;
|
|
21
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Password strength, 0–4, matching the five labels in the locale.
|
|
3
|
+
*
|
|
4
|
+
* Deliberately simple — length plus character variety. It is a nudge in the
|
|
5
|
+
* form, not a security control, and anything cleverer would need a dictionary
|
|
6
|
+
* this package has no business shipping.
|
|
7
|
+
*/
|
|
8
|
+
export declare function passwordScore(value: string): number;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Password strength, 0–4, matching the five labels in the locale.
|
|
3
|
+
*
|
|
4
|
+
* Deliberately simple — length plus character variety. It is a nudge in the
|
|
5
|
+
* form, not a security control, and anything cleverer would need a dictionary
|
|
6
|
+
* this package has no business shipping.
|
|
7
|
+
*/
|
|
8
|
+
export function passwordScore(value) {
|
|
9
|
+
if (!value)
|
|
10
|
+
return 0;
|
|
11
|
+
let points = 0;
|
|
12
|
+
if (value.length >= 8)
|
|
13
|
+
points++;
|
|
14
|
+
if (value.length >= 12)
|
|
15
|
+
points++;
|
|
16
|
+
if (/[a-z]/.test(value) && /[A-Z]/.test(value))
|
|
17
|
+
points++;
|
|
18
|
+
if (/\d/.test(value))
|
|
19
|
+
points++;
|
|
20
|
+
if (/[^\w\s]/.test(value))
|
|
21
|
+
points++;
|
|
22
|
+
return Math.min(4, points);
|
|
23
|
+
}
|