@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.
Files changed (308) hide show
  1. package/README.md +151 -439
  2. package/cli/index.mjs +67 -30
  3. package/dist/core/calendar.d.ts +3 -0
  4. package/dist/core/calendar.js +36 -0
  5. package/dist/core/dialog.svelte.d.ts +14 -0
  6. package/dist/core/dialog.svelte.js +23 -0
  7. package/dist/core/files.d.ts +35 -0
  8. package/dist/core/files.js +62 -0
  9. package/dist/core/index.d.ts +37 -0
  10. package/dist/core/index.js +35 -0
  11. package/dist/core/list.svelte.d.ts +41 -0
  12. package/dist/core/list.svelte.js +77 -0
  13. package/dist/core/number.d.ts +7 -0
  14. package/dist/core/number.js +17 -0
  15. package/dist/core/pagination.d.ts +6 -0
  16. package/dist/core/pagination.js +21 -0
  17. package/dist/core/password.d.ts +8 -0
  18. package/dist/core/password.js +23 -0
  19. package/dist/core/table.d.ts +22 -0
  20. package/dist/core/table.js +41 -0
  21. package/dist/core/theme.svelte.d.ts +16 -0
  22. package/dist/core/theme.svelte.js +34 -0
  23. package/dist/core/trigger.d.ts +35 -0
  24. package/dist/core/trigger.js +77 -0
  25. package/dist/index.d.ts +11 -103
  26. package/dist/index.js +11 -75
  27. package/dist/{components → styles/matte}/AccordionItem.svelte +1 -1
  28. package/dist/{components → styles/matte}/AccordionItem.svelte.d.ts +1 -1
  29. package/dist/{components → styles/matte}/Alert.svelte +8 -2
  30. package/dist/{components → styles/matte}/Alert.svelte.d.ts +1 -1
  31. package/dist/{components → styles/matte}/Avatar.svelte +1 -1
  32. package/dist/{components → styles/matte}/Avatar.svelte.d.ts +1 -1
  33. package/dist/{components → styles/matte}/Badge.svelte +8 -3
  34. package/dist/{components → styles/matte}/Badge.svelte.d.ts +1 -3
  35. package/dist/{components → styles/matte}/Breadcrumb.svelte +2 -2
  36. package/dist/{components → styles/matte}/Breadcrumb.svelte.d.ts +1 -1
  37. package/dist/{components → styles/matte}/Button.svelte +1 -1
  38. package/dist/{components → styles/matte}/Button.svelte.d.ts +1 -1
  39. package/dist/styles/matte/CTASection.svelte +76 -0
  40. package/dist/styles/matte/CTASection.svelte.d.ts +18 -0
  41. package/dist/{components → styles/matte}/Calendar.svelte +7 -17
  42. package/dist/{components → styles/matte}/Calendar.svelte.d.ts +1 -1
  43. package/dist/{components → styles/matte}/Card.svelte +1 -1
  44. package/dist/{components → styles/matte}/Card.svelte.d.ts +1 -1
  45. package/dist/{components → styles/matte}/Checkbox.svelte +1 -1
  46. package/dist/{components → styles/matte}/Checkbox.svelte.d.ts +1 -1
  47. package/dist/{components → styles/matte}/CheckboxGroup.svelte +1 -1
  48. package/dist/{components → styles/matte}/CheckboxGroup.svelte.d.ts +1 -1
  49. package/dist/{components → styles/matte}/Combobox.svelte +31 -42
  50. package/dist/{components → styles/matte}/Combobox.svelte.d.ts +1 -1
  51. package/dist/{components → styles/matte}/CommandPalette.svelte +26 -41
  52. package/dist/{components → styles/matte}/CommandPalette.svelte.d.ts +1 -1
  53. package/dist/{components → styles/matte}/ConfirmDialog.svelte +2 -2
  54. package/dist/{components → styles/matte}/ConfirmDialog.svelte.d.ts +2 -2
  55. package/dist/{components → styles/matte}/ContextMenu.svelte +5 -7
  56. package/dist/{components → styles/matte}/DatePicker.svelte +7 -11
  57. package/dist/{components → styles/matte}/DatePicker.svelte.d.ts +1 -1
  58. package/dist/{components → styles/matte}/Drawer.svelte +4 -7
  59. package/dist/{components → styles/matte}/Dropdown.svelte +18 -36
  60. package/dist/{components → styles/matte}/Dropdown.svelte.d.ts +1 -1
  61. package/dist/{components → styles/matte}/Dropzone.svelte +11 -43
  62. package/dist/{components → styles/matte}/Dropzone.svelte.d.ts +2 -5
  63. package/dist/{components → styles/matte}/EmptyState.svelte +1 -1
  64. package/dist/{components → styles/matte}/EmptyState.svelte.d.ts +1 -1
  65. package/dist/styles/matte/FeatureCard.svelte +58 -0
  66. package/dist/styles/matte/FeatureCard.svelte.d.ts +16 -0
  67. package/dist/styles/matte/FeatureGrid.svelte +62 -0
  68. package/dist/styles/matte/FeatureGrid.svelte.d.ts +16 -0
  69. package/dist/{components → styles/matte}/Footer.svelte +1 -1
  70. package/dist/{components → styles/matte}/Footer.svelte.d.ts +1 -1
  71. package/dist/styles/matte/HeroSection.svelte +80 -0
  72. package/dist/styles/matte/HeroSection.svelte.d.ts +21 -0
  73. package/dist/{components → styles/matte}/Input.svelte +1 -1
  74. package/dist/{components → styles/matte}/Input.svelte.d.ts +1 -1
  75. package/dist/{components → styles/matte}/Link.svelte +1 -1
  76. package/dist/{components → styles/matte}/Link.svelte.d.ts +1 -1
  77. package/dist/{components → styles/matte}/LocaleProvider.svelte +1 -1
  78. package/dist/{components → styles/matte}/LocaleProvider.svelte.d.ts +1 -1
  79. package/dist/styles/matte/LogoCloud.svelte +61 -0
  80. package/dist/styles/matte/LogoCloud.svelte.d.ts +19 -0
  81. package/dist/{components → styles/matte}/MenuItem.svelte +1 -1
  82. package/dist/{components → styles/matte}/MenuItem.svelte.d.ts +1 -1
  83. package/dist/{components → styles/matte}/Modal.svelte +4 -7
  84. package/dist/{components → styles/matte}/MultiSelect.svelte +18 -37
  85. package/dist/{components → styles/matte}/MultiSelect.svelte.d.ts +1 -1
  86. package/dist/{components → styles/matte}/Navbar.svelte +2 -2
  87. package/dist/{components → styles/matte}/Navbar.svelte.d.ts +1 -1
  88. package/dist/{components → styles/matte}/NumberInput.svelte +4 -8
  89. package/dist/{components → styles/matte}/NumberInput.svelte.d.ts +1 -1
  90. package/dist/{components → styles/matte}/Pagination.svelte +4 -15
  91. package/dist/{components → styles/matte}/Pagination.svelte.d.ts +1 -1
  92. package/dist/{components → styles/matte}/PasswordInput.svelte +4 -13
  93. package/dist/{components → styles/matte}/PasswordInput.svelte.d.ts +1 -1
  94. package/dist/{components → styles/matte}/Popover.svelte +16 -41
  95. package/dist/{components → styles/matte}/Popover.svelte.d.ts +1 -1
  96. package/dist/styles/matte/PricingCard.svelte +107 -0
  97. package/dist/styles/matte/PricingCard.svelte.d.ts +25 -0
  98. package/dist/{components → styles/matte}/Progress.svelte +3 -2
  99. package/dist/{components → styles/matte}/Progress.svelte.d.ts +1 -1
  100. package/dist/{components → styles/matte}/Radio.svelte +1 -1
  101. package/dist/{components → styles/matte}/Radio.svelte.d.ts +1 -1
  102. package/dist/{components → styles/matte}/RadioGroup.svelte +1 -1
  103. package/dist/{components → styles/matte}/RadioGroup.svelte.d.ts +1 -1
  104. package/dist/{components → styles/matte}/SegmentedControl.svelte +1 -1
  105. package/dist/{components → styles/matte}/SegmentedControl.svelte.d.ts +1 -1
  106. package/dist/{components → styles/matte}/Select.svelte +1 -1
  107. package/dist/{components → styles/matte}/Select.svelte.d.ts +1 -1
  108. package/dist/{components → styles/matte}/Sidebar.svelte +2 -2
  109. package/dist/{components → styles/matte}/Sidebar.svelte.d.ts +1 -1
  110. package/dist/{components → styles/matte}/Slider.svelte +3 -2
  111. package/dist/{components → styles/matte}/Slider.svelte.d.ts +1 -1
  112. package/dist/{components → styles/matte}/Spinner.svelte +2 -2
  113. package/dist/{components → styles/matte}/Spinner.svelte.d.ts +1 -1
  114. package/dist/{components → styles/matte}/Stat.svelte +1 -1
  115. package/dist/{components → styles/matte}/Stat.svelte.d.ts +1 -1
  116. package/dist/styles/matte/StatsBand.svelte +57 -0
  117. package/dist/styles/matte/StatsBand.svelte.d.ts +20 -0
  118. package/dist/{components → styles/matte}/Steps.svelte +1 -1
  119. package/dist/{components → styles/matte}/Steps.svelte.d.ts +1 -1
  120. package/dist/{components → styles/matte}/Switch.svelte +1 -1
  121. package/dist/{components → styles/matte}/Switch.svelte.d.ts +1 -1
  122. package/dist/{components → styles/matte}/Table.svelte +6 -28
  123. package/dist/{components → styles/matte}/Table.svelte.d.ts +2 -6
  124. package/dist/{components → styles/matte}/Tabs.svelte +8 -1
  125. package/dist/{components → styles/matte}/Tabs.svelte.d.ts +1 -1
  126. package/dist/styles/matte/Testimonial.svelte +67 -0
  127. package/dist/styles/matte/Testimonial.svelte.d.ts +19 -0
  128. package/dist/{components → styles/matte}/Textarea.svelte +1 -1
  129. package/dist/{components → styles/matte}/Textarea.svelte.d.ts +1 -1
  130. package/dist/{components → styles/matte}/ThemeToggle.svelte +14 -27
  131. package/dist/{components → styles/matte}/ThemeToggle.svelte.d.ts +2 -6
  132. package/dist/{components → styles/matte}/Toaster.svelte +4 -4
  133. package/dist/{fonts.css → styles/matte/fonts.css} +4 -3
  134. package/dist/styles/matte/index.d.ts +109 -0
  135. package/dist/styles/matte/index.js +83 -0
  136. package/dist/{theme.css → styles/matte/theme.css} +0 -1
  137. package/dist/styles/paper/Accordion.svelte +17 -0
  138. package/dist/styles/paper/Accordion.svelte.d.ts +8 -0
  139. package/dist/styles/paper/AccordionItem.svelte +46 -0
  140. package/dist/styles/paper/AccordionItem.svelte.d.ts +14 -0
  141. package/dist/styles/paper/Alert.svelte +99 -0
  142. package/dist/styles/paper/Alert.svelte.d.ts +18 -0
  143. package/dist/styles/paper/Avatar.svelte +60 -0
  144. package/dist/styles/paper/Avatar.svelte.d.ts +16 -0
  145. package/dist/styles/paper/AvatarGroup.svelte +17 -0
  146. package/dist/styles/paper/AvatarGroup.svelte.d.ts +8 -0
  147. package/dist/styles/paper/Badge.svelte +58 -0
  148. package/dist/styles/paper/Badge.svelte.d.ts +16 -0
  149. package/dist/styles/paper/Breadcrumb.svelte +49 -0
  150. package/dist/styles/paper/Breadcrumb.svelte.d.ts +14 -0
  151. package/dist/styles/paper/Button.svelte +96 -0
  152. package/dist/styles/paper/Button.svelte.d.ts +21 -0
  153. package/dist/styles/paper/CTASection.svelte +76 -0
  154. package/dist/styles/paper/CTASection.svelte.d.ts +18 -0
  155. package/dist/styles/paper/Calendar.svelte +202 -0
  156. package/dist/styles/paper/Calendar.svelte.d.ts +22 -0
  157. package/dist/styles/paper/Card.svelte +78 -0
  158. package/dist/styles/paper/Card.svelte.d.ts +21 -0
  159. package/dist/styles/paper/Checkbox.svelte +75 -0
  160. package/dist/styles/paper/Checkbox.svelte.d.ts +15 -0
  161. package/dist/styles/paper/CheckboxGroup.svelte +111 -0
  162. package/dist/styles/paper/CheckboxGroup.svelte.d.ts +30 -0
  163. package/dist/styles/paper/Combobox.svelte +266 -0
  164. package/dist/styles/paper/Combobox.svelte.d.ts +29 -0
  165. package/dist/styles/paper/CommandPalette.svelte +209 -0
  166. package/dist/styles/paper/CommandPalette.svelte.d.ts +32 -0
  167. package/dist/styles/paper/ConfirmDialog.svelte +97 -0
  168. package/dist/styles/paper/ConfirmDialog.svelte.d.ts +22 -0
  169. package/dist/styles/paper/ContextMenu.svelte +88 -0
  170. package/dist/styles/paper/ContextMenu.svelte.d.ts +15 -0
  171. package/dist/styles/paper/DatePicker.svelte +192 -0
  172. package/dist/styles/paper/DatePicker.svelte.d.ts +23 -0
  173. package/dist/styles/paper/Divider.svelte +30 -0
  174. package/dist/styles/paper/Divider.svelte.d.ts +10 -0
  175. package/dist/styles/paper/Drawer.svelte +165 -0
  176. package/dist/styles/paper/Drawer.svelte.d.ts +20 -0
  177. package/dist/styles/paper/Dropdown.svelte +94 -0
  178. package/dist/styles/paper/Dropdown.svelte.d.ts +23 -0
  179. package/dist/styles/paper/Dropzone.svelte +210 -0
  180. package/dist/styles/paper/Dropzone.svelte.d.ts +29 -0
  181. package/dist/styles/paper/EmptyState.svelte +71 -0
  182. package/dist/styles/paper/EmptyState.svelte.d.ts +18 -0
  183. package/dist/styles/paper/FeatureCard.svelte +58 -0
  184. package/dist/styles/paper/FeatureCard.svelte.d.ts +16 -0
  185. package/dist/styles/paper/FeatureGrid.svelte +62 -0
  186. package/dist/styles/paper/FeatureGrid.svelte.d.ts +16 -0
  187. package/dist/styles/paper/Field.svelte +42 -0
  188. package/dist/styles/paper/Field.svelte.d.ts +16 -0
  189. package/dist/styles/paper/Footer.svelte +91 -0
  190. package/dist/styles/paper/Footer.svelte.d.ts +25 -0
  191. package/dist/styles/paper/HeroSection.svelte +86 -0
  192. package/dist/styles/paper/HeroSection.svelte.d.ts +21 -0
  193. package/dist/styles/paper/Input.svelte +60 -0
  194. package/dist/styles/paper/Input.svelte.d.ts +18 -0
  195. package/dist/styles/paper/InputGroup.svelte +20 -0
  196. package/dist/styles/paper/InputGroup.svelte.d.ts +11 -0
  197. package/dist/styles/paper/Kbd.svelte +17 -0
  198. package/dist/styles/paper/Kbd.svelte.d.ts +8 -0
  199. package/dist/styles/paper/Label.svelte +16 -0
  200. package/dist/styles/paper/Label.svelte.d.ts +9 -0
  201. package/dist/styles/paper/Link.svelte +44 -0
  202. package/dist/styles/paper/Link.svelte.d.ts +16 -0
  203. package/dist/styles/paper/LocaleProvider.svelte +24 -0
  204. package/dist/styles/paper/LocaleProvider.svelte.d.ts +10 -0
  205. package/dist/styles/paper/LogoCloud.svelte +61 -0
  206. package/dist/styles/paper/LogoCloud.svelte.d.ts +19 -0
  207. package/dist/styles/paper/MenuItem.svelte +64 -0
  208. package/dist/styles/paper/MenuItem.svelte.d.ts +18 -0
  209. package/dist/styles/paper/MenuSeparator.svelte +22 -0
  210. package/dist/styles/paper/MenuSeparator.svelte.d.ts +8 -0
  211. package/dist/styles/paper/Modal.svelte +118 -0
  212. package/dist/styles/paper/Modal.svelte.d.ts +21 -0
  213. package/dist/styles/paper/MultiSelect.svelte +287 -0
  214. package/dist/styles/paper/MultiSelect.svelte.d.ts +31 -0
  215. package/dist/styles/paper/Navbar.svelte +201 -0
  216. package/dist/styles/paper/Navbar.svelte.d.ts +33 -0
  217. package/dist/styles/paper/NumberInput.svelte +96 -0
  218. package/dist/styles/paper/NumberInput.svelte.d.ts +15 -0
  219. package/dist/styles/paper/Pagination.svelte +83 -0
  220. package/dist/styles/paper/Pagination.svelte.d.ts +15 -0
  221. package/dist/styles/paper/PasswordInput.svelte +100 -0
  222. package/dist/styles/paper/PasswordInput.svelte.d.ts +12 -0
  223. package/dist/styles/paper/Popover.svelte +89 -0
  224. package/dist/styles/paper/Popover.svelte.d.ts +19 -0
  225. package/dist/styles/paper/PricingCard.svelte +108 -0
  226. package/dist/styles/paper/PricingCard.svelte.d.ts +25 -0
  227. package/dist/styles/paper/Progress.svelte +59 -0
  228. package/dist/styles/paper/Progress.svelte.d.ts +17 -0
  229. package/dist/styles/paper/Radio.svelte +55 -0
  230. package/dist/styles/paper/Radio.svelte.d.ts +15 -0
  231. package/dist/styles/paper/RadioGroup.svelte +95 -0
  232. package/dist/styles/paper/RadioGroup.svelte.d.ts +27 -0
  233. package/dist/styles/paper/SegmentedControl.svelte +88 -0
  234. package/dist/styles/paper/SegmentedControl.svelte.d.ts +25 -0
  235. package/dist/styles/paper/Select.svelte +61 -0
  236. package/dist/styles/paper/Select.svelte.d.ts +20 -0
  237. package/dist/styles/paper/Sidebar.svelte +206 -0
  238. package/dist/styles/paper/Sidebar.svelte.d.ts +36 -0
  239. package/dist/styles/paper/Skeleton.svelte +46 -0
  240. package/dist/styles/paper/Skeleton.svelte.d.ts +13 -0
  241. package/dist/styles/paper/Slider.svelte +80 -0
  242. package/dist/styles/paper/Slider.svelte.d.ts +19 -0
  243. package/dist/styles/paper/Spinner.svelte +34 -0
  244. package/dist/styles/paper/Spinner.svelte.d.ts +13 -0
  245. package/dist/styles/paper/Stat.svelte +51 -0
  246. package/dist/styles/paper/Stat.svelte.d.ts +16 -0
  247. package/dist/styles/paper/StatsBand.svelte +63 -0
  248. package/dist/styles/paper/StatsBand.svelte.d.ts +20 -0
  249. package/dist/styles/paper/Steps.svelte +141 -0
  250. package/dist/styles/paper/Steps.svelte.d.ts +24 -0
  251. package/dist/styles/paper/Switch.svelte +64 -0
  252. package/dist/styles/paper/Switch.svelte.d.ts +14 -0
  253. package/dist/styles/paper/Table.svelte +217 -0
  254. package/dist/styles/paper/Table.svelte.d.ts +66 -0
  255. package/dist/styles/paper/Tabs.svelte +102 -0
  256. package/dist/styles/paper/Tabs.svelte.d.ts +23 -0
  257. package/dist/styles/paper/Testimonial.svelte +64 -0
  258. package/dist/styles/paper/Testimonial.svelte.d.ts +19 -0
  259. package/dist/styles/paper/Textarea.svelte +54 -0
  260. package/dist/styles/paper/Textarea.svelte.d.ts +12 -0
  261. package/dist/styles/paper/ThemeToggle.svelte +130 -0
  262. package/dist/styles/paper/ThemeToggle.svelte.d.ts +16 -0
  263. package/dist/styles/paper/Toaster.svelte +132 -0
  264. package/dist/styles/paper/Toaster.svelte.d.ts +8 -0
  265. package/dist/styles/paper/Tooltip.svelte +33 -0
  266. package/dist/styles/paper/Tooltip.svelte.d.ts +12 -0
  267. package/dist/styles/paper/fonts.css +46 -0
  268. package/dist/styles/paper/index.d.ts +109 -0
  269. package/dist/styles/paper/index.js +83 -0
  270. package/dist/styles/paper/theme.css +222 -0
  271. package/package.json +531 -201
  272. package/registry.json +658 -339
  273. /package/dist/{actions → core/actions}/anchor.d.ts +0 -0
  274. /package/dist/{actions → core/actions}/anchor.js +0 -0
  275. /package/dist/{actions → core/actions}/dismissable.d.ts +0 -0
  276. /package/dist/{actions → core/actions}/dismissable.js +0 -0
  277. /package/dist/{date.d.ts → core/date.d.ts} +0 -0
  278. /package/dist/{date.js → core/date.js} +0 -0
  279. /package/dist/{locale.svelte.d.ts → core/locale.svelte.d.ts} +0 -0
  280. /package/dist/{locale.svelte.js → core/locale.svelte.js} +0 -0
  281. /package/dist/{toast.svelte.d.ts → core/toast.svelte.d.ts} +0 -0
  282. /package/dist/{toast.svelte.js → core/toast.svelte.js} +0 -0
  283. /package/dist/{tones.d.ts → core/tones.d.ts} +0 -0
  284. /package/dist/{tones.js → core/tones.js} +0 -0
  285. /package/dist/{components → styles/matte}/Accordion.svelte +0 -0
  286. /package/dist/{components → styles/matte}/Accordion.svelte.d.ts +0 -0
  287. /package/dist/{components → styles/matte}/AvatarGroup.svelte +0 -0
  288. /package/dist/{components → styles/matte}/AvatarGroup.svelte.d.ts +0 -0
  289. /package/dist/{components → styles/matte}/ContextMenu.svelte.d.ts +0 -0
  290. /package/dist/{components → styles/matte}/Divider.svelte +0 -0
  291. /package/dist/{components → styles/matte}/Divider.svelte.d.ts +0 -0
  292. /package/dist/{components → styles/matte}/Drawer.svelte.d.ts +0 -0
  293. /package/dist/{components → styles/matte}/Field.svelte +0 -0
  294. /package/dist/{components → styles/matte}/Field.svelte.d.ts +0 -0
  295. /package/dist/{components → styles/matte}/InputGroup.svelte +0 -0
  296. /package/dist/{components → styles/matte}/InputGroup.svelte.d.ts +0 -0
  297. /package/dist/{components → styles/matte}/Kbd.svelte +0 -0
  298. /package/dist/{components → styles/matte}/Kbd.svelte.d.ts +0 -0
  299. /package/dist/{components → styles/matte}/Label.svelte +0 -0
  300. /package/dist/{components → styles/matte}/Label.svelte.d.ts +0 -0
  301. /package/dist/{components → styles/matte}/MenuSeparator.svelte +0 -0
  302. /package/dist/{components → styles/matte}/MenuSeparator.svelte.d.ts +0 -0
  303. /package/dist/{components → styles/matte}/Modal.svelte.d.ts +0 -0
  304. /package/dist/{components → styles/matte}/Skeleton.svelte +0 -0
  305. /package/dist/{components → styles/matte}/Skeleton.svelte.d.ts +0 -0
  306. /package/dist/{components → styles/matte}/Toaster.svelte.d.ts +0 -0
  307. /package/dist/{components → styles/matte}/Tooltip.svelte +0 -0
  308. /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 the full subpath. */
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
- (item) =>
74
- item.name.toLowerCase() === needle ||
75
- item.subpath.endsWith(`/${needle}`) ||
76
- item.subpath === query
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(' @nqmcreative/ui'));
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 the package's dist so`,
158
- ` the class names used inside the components are generated. */`,
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
- if (css.includes('@nqmcreative/ui/theme.css')) {
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('bunx @nqmcreative/ui add button')}`);
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.subpath.split('/').pop());
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.subpath}';`);
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.subpath.split('/').pop().padEnd(20)} ${c.dim(item.subpath)}`);
387
+ log(` ${item.slug.padEnd(20)} ${c.dim(item.description)}`);
359
388
  }
360
389
  log();
361
390
  }
362
- log(c.dim(` ${shown} of ${count} components`));
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
- log(` ${c.cyan(`import ${item.name} from '${item.subpath}';`)}`);
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(` uses ${item.modules.join(', ')}`);
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')} wire the design system into this project
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 and its subpath
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,3 @@
1
+ export declare function calendarKeyMove(key: string, focused: Date, weekStart: 0 | 1, shiftKey?: boolean): Date | null;
2
+ /** The placeholder a text date field shows for a locale's part order. */
3
+ export declare function dateHint(format: 'dmy' | 'mdy' | 'ymd'): string;
@@ -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
+ }