@syware/jlib 0.0.0-stage → 0.1.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 (331) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +116 -2
  3. package/dist/all.d.ts +1 -0
  4. package/dist/all.js +75 -0
  5. package/dist/atoms/jl-avatar.css_raw.js +3 -0
  6. package/dist/atoms/jl-avatar.d.ts +52 -0
  7. package/dist/atoms/jl-avatar.js +167 -0
  8. package/dist/atoms/jl-badge.css_raw.js +3 -0
  9. package/dist/atoms/jl-badge.d.ts +32 -0
  10. package/dist/atoms/jl-badge.js +60 -0
  11. package/dist/atoms/jl-button.css_raw.js +3 -0
  12. package/dist/atoms/jl-button.d.ts +70 -0
  13. package/dist/atoms/jl-button.js +146 -0
  14. package/dist/atoms/jl-checkbox.css_raw.js +3 -0
  15. package/dist/atoms/jl-checkbox.d.ts +61 -0
  16. package/dist/atoms/jl-checkbox.js +149 -0
  17. package/dist/atoms/jl-chip.css_raw.js +3 -0
  18. package/dist/atoms/jl-chip.d.ts +59 -0
  19. package/dist/atoms/jl-chip.js +164 -0
  20. package/dist/atoms/jl-divider.css_raw.js +3 -0
  21. package/dist/atoms/jl-divider.d.ts +26 -0
  22. package/dist/atoms/jl-divider.js +59 -0
  23. package/dist/atoms/jl-icon.css_raw.js +3 -0
  24. package/dist/atoms/jl-icon.d.ts +25 -0
  25. package/dist/atoms/jl-icon.js +41 -0
  26. package/dist/atoms/jl-input.css_raw.js +3 -0
  27. package/dist/atoms/jl-input.d.ts +112 -0
  28. package/dist/atoms/jl-input.js +202 -0
  29. package/dist/atoms/jl-kbd.css_raw.js +3 -0
  30. package/dist/atoms/jl-kbd.d.ts +17 -0
  31. package/dist/atoms/jl-kbd.js +62 -0
  32. package/dist/atoms/jl-link.css_raw.js +3 -0
  33. package/dist/atoms/jl-link.d.ts +37 -0
  34. package/dist/atoms/jl-link.js +86 -0
  35. package/dist/atoms/jl-progress-ring.css_raw.js +3 -0
  36. package/dist/atoms/jl-progress-ring.d.ts +49 -0
  37. package/dist/atoms/jl-progress-ring.js +132 -0
  38. package/dist/atoms/jl-progress.css_raw.js +3 -0
  39. package/dist/atoms/jl-progress.d.ts +43 -0
  40. package/dist/atoms/jl-progress.js +109 -0
  41. package/dist/atoms/jl-rating.css_raw.js +3 -0
  42. package/dist/atoms/jl-rating.d.ts +73 -0
  43. package/dist/atoms/jl-rating.js +184 -0
  44. package/dist/atoms/jl-select.css_raw.js +3 -0
  45. package/dist/atoms/jl-select.d.ts +70 -0
  46. package/dist/atoms/jl-select.js +146 -0
  47. package/dist/atoms/jl-skeleton.css_raw.js +3 -0
  48. package/dist/atoms/jl-skeleton.d.ts +34 -0
  49. package/dist/atoms/jl-skeleton.js +63 -0
  50. package/dist/atoms/jl-slider.css_raw.js +3 -0
  51. package/dist/atoms/jl-slider.d.ts +65 -0
  52. package/dist/atoms/jl-slider.js +143 -0
  53. package/dist/atoms/jl-spinner.css_raw.js +3 -0
  54. package/dist/atoms/jl-spinner.d.ts +16 -0
  55. package/dist/atoms/jl-spinner.js +26 -0
  56. package/dist/atoms/jl-switch.css_raw.js +3 -0
  57. package/dist/atoms/jl-switch.d.ts +57 -0
  58. package/dist/atoms/jl-switch.js +125 -0
  59. package/dist/atoms/jl-textarea.css_raw.js +3 -0
  60. package/dist/atoms/jl-textarea.d.ts +81 -0
  61. package/dist/atoms/jl-textarea.js +148 -0
  62. package/dist/base/jl-element.d.ts +16 -0
  63. package/dist/base/jl-element.js +35 -0
  64. package/dist/base/jl-form-control.d.ts +29 -0
  65. package/dist/base/jl-form-control.js +44 -0
  66. package/dist/index.d.ts +119 -0
  67. package/dist/index.js +72 -0
  68. package/dist/lib/arrow.d.ts +6 -0
  69. package/dist/lib/arrow.js +15 -0
  70. package/dist/lib/attributes.d.ts +9 -0
  71. package/dist/lib/attributes.js +22 -0
  72. package/dist/lib/avatar.d.ts +11 -0
  73. package/dist/lib/avatar.js +37 -0
  74. package/dist/lib/calendar.d.ts +48 -0
  75. package/dist/lib/calendar.js +166 -0
  76. package/dist/lib/css-length.d.ts +2 -0
  77. package/dist/lib/css-length.js +8 -0
  78. package/dist/lib/define.d.ts +9 -0
  79. package/dist/lib/define.js +9 -0
  80. package/dist/lib/dom.d.ts +4 -0
  81. package/dist/lib/dom.js +16 -0
  82. package/dist/lib/files.d.ts +18 -0
  83. package/dist/lib/files.js +89 -0
  84. package/dist/lib/floating.d.ts +13 -0
  85. package/dist/lib/floating.js +127 -0
  86. package/dist/lib/fuzzy.d.ts +26 -0
  87. package/dist/lib/fuzzy.js +149 -0
  88. package/dist/lib/highlight.d.ts +5 -0
  89. package/dist/lib/highlight.js +267 -0
  90. package/dist/lib/hotkey.d.ts +9 -0
  91. package/dist/lib/hotkey.js +101 -0
  92. package/dist/lib/icon-set.d.ts +95 -0
  93. package/dist/lib/icon-set.js +97 -0
  94. package/dist/lib/icons.d.ts +12 -0
  95. package/dist/lib/icons.js +39 -0
  96. package/dist/lib/indicator.d.ts +6 -0
  97. package/dist/lib/indicator.js +18 -0
  98. package/dist/lib/keep-in-view.d.ts +5 -0
  99. package/dist/lib/keep-in-view.js +15 -0
  100. package/dist/lib/key-shortcut.d.ts +20 -0
  101. package/dist/lib/key-shortcut.js +100 -0
  102. package/dist/lib/keycaps.d.ts +8 -0
  103. package/dist/lib/keycaps.js +162 -0
  104. package/dist/lib/locale.d.ts +4 -0
  105. package/dist/lib/locale.js +13 -0
  106. package/dist/lib/nav-tree.d.ts +9 -0
  107. package/dist/lib/nav-tree.js +30 -0
  108. package/dist/lib/numeric.d.ts +20 -0
  109. package/dist/lib/numeric.js +50 -0
  110. package/dist/lib/option-filter.d.ts +29 -0
  111. package/dist/lib/option-filter.js +108 -0
  112. package/dist/lib/option-groups.d.ts +6 -0
  113. package/dist/lib/option-groups.js +26 -0
  114. package/dist/lib/options.d.ts +5 -0
  115. package/dist/lib/options.js +24 -0
  116. package/dist/lib/pagination.d.ts +9 -0
  117. package/dist/lib/pagination.js +52 -0
  118. package/dist/lib/palette.d.ts +8 -0
  119. package/dist/lib/palette.js +46 -0
  120. package/dist/lib/progress.d.ts +20 -0
  121. package/dist/lib/progress.js +33 -0
  122. package/dist/lib/rating.d.ts +7 -0
  123. package/dist/lib/rating.js +26 -0
  124. package/dist/lib/roving-index.d.ts +10 -0
  125. package/dist/lib/roving-index.js +32 -0
  126. package/dist/lib/roving.d.ts +11 -0
  127. package/dist/lib/roving.js +21 -0
  128. package/dist/lib/slots.d.ts +2 -0
  129. package/dist/lib/slots.js +6 -0
  130. package/dist/lib/sparkline.d.ts +11 -0
  131. package/dist/lib/sparkline.js +42 -0
  132. package/dist/lib/split.d.ts +12 -0
  133. package/dist/lib/split.js +37 -0
  134. package/dist/lib/surface.d.ts +7 -0
  135. package/dist/lib/surface.js +28 -0
  136. package/dist/lib/table.d.ts +30 -0
  137. package/dist/lib/table.js +110 -0
  138. package/dist/lib/tags.d.ts +14 -0
  139. package/dist/lib/tags.js +29 -0
  140. package/dist/lib/text.d.ts +5 -0
  141. package/dist/lib/text.js +13 -0
  142. package/dist/lib/tokens.d.ts +5 -0
  143. package/dist/lib/tokens.js +15 -0
  144. package/dist/lib/tree.d.ts +16 -0
  145. package/dist/lib/tree.js +89 -0
  146. package/dist/lib/trend.d.ts +3 -0
  147. package/dist/lib/trend.js +15 -0
  148. package/dist/lib/trigger.d.ts +11 -0
  149. package/dist/lib/trigger.js +34 -0
  150. package/dist/molecules/jl-accordion-item.css_raw.js +3 -0
  151. package/dist/molecules/jl-accordion-item.d.ts +34 -0
  152. package/dist/molecules/jl-accordion-item.js +82 -0
  153. package/dist/molecules/jl-accordion.css_raw.js +3 -0
  154. package/dist/molecules/jl-accordion.d.ts +27 -0
  155. package/dist/molecules/jl-accordion.js +61 -0
  156. package/dist/molecules/jl-alert.css_raw.js +3 -0
  157. package/dist/molecules/jl-alert.d.ts +38 -0
  158. package/dist/molecules/jl-alert.js +131 -0
  159. package/dist/molecules/jl-avatar-group.css_raw.js +3 -0
  160. package/dist/molecules/jl-avatar-group.d.ts +28 -0
  161. package/dist/molecules/jl-avatar-group.js +69 -0
  162. package/dist/molecules/jl-breadcrumb.css_raw.js +3 -0
  163. package/dist/molecules/jl-breadcrumb.d.ts +35 -0
  164. package/dist/molecules/jl-breadcrumb.js +126 -0
  165. package/dist/molecules/jl-calendar.css_raw.js +3 -0
  166. package/dist/molecules/jl-calendar.d.ts +101 -0
  167. package/dist/molecules/jl-calendar.js +314 -0
  168. package/dist/molecules/jl-card.css_raw.js +3 -0
  169. package/dist/molecules/jl-card.d.ts +46 -0
  170. package/dist/molecules/jl-card.js +172 -0
  171. package/dist/molecules/jl-code-block.css_raw.js +3 -0
  172. package/dist/molecules/jl-code-block.d.ts +44 -0
  173. package/dist/molecules/jl-code-block.js +106 -0
  174. package/dist/molecules/jl-combobox.css_raw.js +3 -0
  175. package/dist/molecules/jl-combobox.d.ts +120 -0
  176. package/dist/molecules/jl-combobox.js +450 -0
  177. package/dist/molecules/jl-copy-button.css_raw.js +3 -0
  178. package/dist/molecules/jl-copy-button.d.ts +31 -0
  179. package/dist/molecules/jl-copy-button.js +69 -0
  180. package/dist/molecules/jl-date-picker.css_raw.js +3 -0
  181. package/dist/molecules/jl-date-picker.d.ts +109 -0
  182. package/dist/molecules/jl-date-picker.js +322 -0
  183. package/dist/molecules/jl-empty.css_raw.js +3 -0
  184. package/dist/molecules/jl-empty.d.ts +32 -0
  185. package/dist/molecules/jl-empty.js +94 -0
  186. package/dist/molecules/jl-field.css_raw.js +3 -0
  187. package/dist/molecules/jl-field.d.ts +49 -0
  188. package/dist/molecules/jl-field.js +147 -0
  189. package/dist/molecules/jl-file-drop.css_raw.js +3 -0
  190. package/dist/molecules/jl-file-drop.d.ts +93 -0
  191. package/dist/molecules/jl-file-drop.js +366 -0
  192. package/dist/molecules/jl-list-item.css_raw.js +3 -0
  193. package/dist/molecules/jl-list-item.d.ts +56 -0
  194. package/dist/molecules/jl-list-item.js +176 -0
  195. package/dist/molecules/jl-menu.css_raw.js +3 -0
  196. package/dist/molecules/jl-menu.d.ts +62 -0
  197. package/dist/molecules/jl-menu.js +288 -0
  198. package/dist/molecules/jl-number-input.css_raw.js +3 -0
  199. package/dist/molecules/jl-number-input.d.ts +81 -0
  200. package/dist/molecules/jl-number-input.js +220 -0
  201. package/dist/molecules/jl-pagination.css_raw.js +3 -0
  202. package/dist/molecules/jl-pagination.d.ts +59 -0
  203. package/dist/molecules/jl-pagination.js +172 -0
  204. package/dist/molecules/jl-popover.css_raw.js +3 -0
  205. package/dist/molecules/jl-popover.d.ts +49 -0
  206. package/dist/molecules/jl-popover.js +152 -0
  207. package/dist/molecules/jl-radio-group.css_raw.js +3 -0
  208. package/dist/molecules/jl-radio-group.d.ts +71 -0
  209. package/dist/molecules/jl-radio-group.js +202 -0
  210. package/dist/molecules/jl-search.css_raw.js +3 -0
  211. package/dist/molecules/jl-search.d.ts +71 -0
  212. package/dist/molecules/jl-search.js +210 -0
  213. package/dist/molecules/jl-segmented.css_raw.js +3 -0
  214. package/dist/molecules/jl-segmented.d.ts +65 -0
  215. package/dist/molecules/jl-segmented.js +187 -0
  216. package/dist/molecules/jl-stat.css_raw.js +3 -0
  217. package/dist/molecules/jl-stat.d.ts +55 -0
  218. package/dist/molecules/jl-stat.js +169 -0
  219. package/dist/molecules/jl-steps.css_raw.js +3 -0
  220. package/dist/molecules/jl-steps.d.ts +44 -0
  221. package/dist/molecules/jl-steps.js +118 -0
  222. package/dist/molecules/jl-tab-panel.css_raw.js +3 -0
  223. package/dist/molecules/jl-tab-panel.d.ts +35 -0
  224. package/dist/molecules/jl-tab-panel.js +35 -0
  225. package/dist/molecules/jl-tabs.css_raw.js +3 -0
  226. package/dist/molecules/jl-tabs.d.ts +51 -0
  227. package/dist/molecules/jl-tabs.js +198 -0
  228. package/dist/molecules/jl-tag-input.css_raw.js +3 -0
  229. package/dist/molecules/jl-tag-input.d.ts +103 -0
  230. package/dist/molecules/jl-tag-input.js +429 -0
  231. package/dist/molecules/jl-timeline.css_raw.js +3 -0
  232. package/dist/molecules/jl-timeline.d.ts +28 -0
  233. package/dist/molecules/jl-timeline.js +81 -0
  234. package/dist/molecules/jl-toast.css_raw.js +3 -0
  235. package/dist/molecules/jl-toast.d.ts +75 -0
  236. package/dist/molecules/jl-toast.js +274 -0
  237. package/dist/molecules/jl-tooltip.css_raw.js +3 -0
  238. package/dist/molecules/jl-tooltip.d.ts +44 -0
  239. package/dist/molecules/jl-tooltip.js +160 -0
  240. package/dist/organisms/jl-command-palette.css_raw.js +3 -0
  241. package/dist/organisms/jl-command-palette.d.ts +60 -0
  242. package/dist/organisms/jl-command-palette.js +285 -0
  243. package/dist/organisms/jl-confirm.css_raw.js +3 -0
  244. package/dist/organisms/jl-confirm.d.ts +56 -0
  245. package/dist/organisms/jl-confirm.js +132 -0
  246. package/dist/organisms/jl-dialog.css_raw.js +3 -0
  247. package/dist/organisms/jl-dialog.d.ts +43 -0
  248. package/dist/organisms/jl-dialog.js +120 -0
  249. package/dist/organisms/jl-drawer.css_raw.js +3 -0
  250. package/dist/organisms/jl-drawer.d.ts +50 -0
  251. package/dist/organisms/jl-drawer.js +141 -0
  252. package/dist/organisms/jl-navbar.css_raw.js +3 -0
  253. package/dist/organisms/jl-navbar.d.ts +32 -0
  254. package/dist/organisms/jl-navbar.js +121 -0
  255. package/dist/organisms/jl-page-header.css_raw.js +3 -0
  256. package/dist/organisms/jl-page-header.d.ts +26 -0
  257. package/dist/organisms/jl-page-header.js +85 -0
  258. package/dist/organisms/jl-side-nav.css_raw.js +3 -0
  259. package/dist/organisms/jl-side-nav.d.ts +60 -0
  260. package/dist/organisms/jl-side-nav.js +269 -0
  261. package/dist/organisms/jl-table.css_raw.js +3 -0
  262. package/dist/organisms/jl-table.d.ts +109 -0
  263. package/dist/organisms/jl-table.js +318 -0
  264. package/dist/organisms/jl-toaster.css_raw.js +3 -0
  265. package/dist/organisms/jl-toaster.d.ts +48 -0
  266. package/dist/organisms/jl-toaster.js +142 -0
  267. package/dist/organisms/jl-tree.css_raw.js +3 -0
  268. package/dist/organisms/jl-tree.d.ts +59 -0
  269. package/dist/organisms/jl-tree.js +198 -0
  270. package/dist/styles/base.css_raw.js +3 -0
  271. package/dist/styles/control.css_raw.js +3 -0
  272. package/dist/styles/floating.css_raw.js +3 -0
  273. package/dist/styles/shared.d.ts +8 -0
  274. package/dist/styles/shared.js +14 -0
  275. package/dist/styles/tokens.css_raw.js +3 -0
  276. package/dist/templates/jl-app-shell.css_raw.js +3 -0
  277. package/dist/templates/jl-app-shell.d.ts +37 -0
  278. package/dist/templates/jl-app-shell.js +150 -0
  279. package/dist/templates/jl-split-view.css_raw.js +3 -0
  280. package/dist/templates/jl-split-view.d.ts +49 -0
  281. package/dist/templates/jl-split-view.js +149 -0
  282. package/dist/tokens.css +188 -0
  283. package/dist/types/calendar.types.d.ts +11 -0
  284. package/dist/types/files.types.d.ts +23 -0
  285. package/dist/types/floating.types.d.ts +13 -0
  286. package/dist/types/fuzzy.types.d.ts +10 -0
  287. package/dist/types/highlight.types.d.ts +6 -0
  288. package/dist/types/hotkey.types.d.ts +19 -0
  289. package/dist/types/jl-accordion.types.d.ts +2 -0
  290. package/dist/types/jl-avatar.types.d.ts +10 -0
  291. package/dist/types/jl-breadcrumb.types.d.ts +6 -0
  292. package/dist/types/jl-button.types.d.ts +3 -0
  293. package/dist/types/jl-card.types.d.ts +2 -0
  294. package/dist/types/jl-chip.types.d.ts +1 -0
  295. package/dist/types/jl-combobox.types.d.ts +8 -0
  296. package/dist/types/jl-command-palette.types.d.ts +22 -0
  297. package/dist/types/jl-confirm.types.d.ts +10 -0
  298. package/dist/types/jl-dialog.types.d.ts +1 -0
  299. package/dist/types/jl-divider.types.d.ts +2 -0
  300. package/dist/types/jl-drawer.types.d.ts +4 -0
  301. package/dist/types/jl-empty.types.d.ts +2 -0
  302. package/dist/types/jl-field.types.d.ts +2 -0
  303. package/dist/types/jl-input.types.d.ts +1 -0
  304. package/dist/types/jl-link.types.d.ts +2 -0
  305. package/dist/types/jl-menu.types.d.ts +22 -0
  306. package/dist/types/jl-navbar.types.d.ts +6 -0
  307. package/dist/types/jl-progress.types.d.ts +3 -0
  308. package/dist/types/jl-radio-group.types.d.ts +10 -0
  309. package/dist/types/jl-segmented.types.d.ts +7 -0
  310. package/dist/types/jl-select.types.d.ts +12 -0
  311. package/dist/types/jl-side-nav.types.d.ts +17 -0
  312. package/dist/types/jl-skeleton.types.d.ts +2 -0
  313. package/dist/types/jl-slider.types.d.ts +5 -0
  314. package/dist/types/jl-split-view.types.d.ts +2 -0
  315. package/dist/types/jl-stat.types.d.ts +2 -0
  316. package/dist/types/jl-steps.types.d.ts +8 -0
  317. package/dist/types/jl-switch.types.d.ts +1 -0
  318. package/dist/types/jl-table.types.d.ts +30 -0
  319. package/dist/types/jl-tabs.types.d.ts +2 -0
  320. package/dist/types/jl-timeline.types.d.ts +10 -0
  321. package/dist/types/jl-toast.types.d.ts +2 -0
  322. package/dist/types/jl-toaster.types.d.ts +23 -0
  323. package/dist/types/jl-tree.types.d.ts +26 -0
  324. package/dist/types/key-shortcut.types.d.ts +9 -0
  325. package/dist/types/keycaps.types.d.ts +7 -0
  326. package/dist/types/option-filter.types.d.ts +10 -0
  327. package/dist/types/options.types.d.ts +4 -0
  328. package/dist/types/pagination.types.d.ts +8 -0
  329. package/dist/types/roving-index.types.d.ts +2 -0
  330. package/dist/types/sparkline.types.d.ts +11 -0
  331. package/package.json +96 -4
@@ -0,0 +1,50 @@
1
+ // src/lib/numeric.ts
2
+ var NO_DECIMALS = 0;
3
+ /** A number written in a field or an attribute; empty or unreadable text gives null. */
4
+ function parseNumber(text) {
5
+ if (text === null || text.trim() === "") return null;
6
+ const parsed = Number(text.trim());
7
+ return Number.isFinite(parsed) ? parsed : null;
8
+ }
9
+ /** A number kept within bounds; a null bound does not limit that side. */
10
+ function clamp(value, min, max) {
11
+ const floored = min === null ? value : Math.max(min, value);
12
+ return max === null ? floored : Math.min(max, floored);
13
+ }
14
+ /** How many decimals a number has as written: 0.25 has 2, so sums of steps print without float noise. */
15
+ function decimalsOf(value) {
16
+ if (!Number.isFinite(value) || Number.isInteger(value)) return NO_DECIMALS;
17
+ const [mantissa = "", exponent = "0"] = String(value).split("e");
18
+ const decimals = mantissa.split(".")[1]?.length ?? NO_DECIMALS;
19
+ return Math.max(NO_DECIMALS, decimals - Number(exponent));
20
+ }
21
+ function roundTo(value, decimals) {
22
+ return Number(value.toFixed(decimals));
23
+ }
24
+ /**
25
+ * The value one step up or down, within bounds, rounded to the decimals of the step and the value. An empty value
26
+ * counts as zero.
27
+ */
28
+ function stepValue(value, step, direction, min, max) {
29
+ const start = value ?? 0;
30
+ const decimals = Math.max(decimalsOf(step), decimalsOf(start));
31
+ return clamp(roundTo(start + step * direction, decimals), min, max);
32
+ }
33
+ /** Where a value sits between min and max, from 0 to 1; an empty range gives 0. */
34
+ function fractionOf(value, min, max) {
35
+ if (max <= min) return 0;
36
+ return clamp((value - min) / (max - min), 0, 1);
37
+ }
38
+ var isMarkObject = (mark) => typeof mark === "object" && mark !== null && typeof mark.value === "number";
39
+ /**
40
+ * The marks of a slider from what a page wrote: numbers (`[0, 50, 100]`) or objects with a label
41
+ * (`[{ "value": 0, "label": "Off" }]`). Marks outside the range and unreadable entries are left out.
42
+ */
43
+ function marksOf(raw, min, max) {
44
+ if (!Array.isArray(raw)) return [];
45
+ return raw.map((mark) => {
46
+ if (typeof mark === "number") return { value: mark };
47
+ return isMarkObject(mark) ? mark : null;
48
+ }).filter((mark) => mark !== null && mark.value >= min && mark.value <= max);
49
+ }
50
+ export { clamp, decimalsOf, fractionOf, marksOf, parseNumber, roundTo, stepValue };
@@ -0,0 +1,29 @@
1
+ import type { TextMatch, TextMatchPart } from '../types/option-filter.types';
2
+ /** A text as matching sees it: "Crème Brûlée" gives "creme brulee". */
3
+ export declare const foldText: (text: string) => string;
4
+ /**
5
+ * Where a search first occurs in a text, ignoring case and accents, in the text's own indexes; null when it does
6
+ * not, or when the search is empty.
7
+ */
8
+ export declare function findMatch(text: string, query: string): TextMatch | null;
9
+ /** A text cut around its match, so the matched piece can be drawn highlighted. */
10
+ export declare function matchParts(text: string, query: string): TextMatchPart[];
11
+ /**
12
+ * The options whose label contains the search, ignoring case and accents: those that start with it first, then
13
+ * those with a word that starts with it, then the rest, each group in its original order. An empty search keeps
14
+ * every option.
15
+ */
16
+ export declare function filterOptions<Option extends {
17
+ label: string;
18
+ }>(options: readonly Option[], query: string): Option[];
19
+ /**
20
+ * The index of the next option that can be chosen, going down (1) or up (-1) from the current one and wrapping
21
+ * around the ends; from -1 (none active) it starts at the first or the last. -1 when none can be chosen.
22
+ */
23
+ export declare function nextEnabledIndex(options: readonly {
24
+ disabled?: boolean;
25
+ }[], current: number, direction: 1 | -1): number;
26
+ /** The first (1) or last (-1) option that can be chosen; -1 when none can. */
27
+ export declare const edgeEnabledIndex: (options: readonly {
28
+ disabled?: boolean;
29
+ }[], from: 1 | -1) => number;
@@ -0,0 +1,108 @@
1
+ // src/lib/option-filter.ts
2
+ /** How well a label matches: at its start, at the start of one of its words, or anywhere inside. */
3
+ var RANK = {
4
+ anywhere: 2,
5
+ labelStart: 0,
6
+ wordStart: 1
7
+ };
8
+ var DIACRITICS = /\p{Diacritic}/gu;
9
+ var WORD_CHARACTER = /[\p{L}\p{N}]/u;
10
+ /** One character as matching sees it: lower case, without its accents ("É" gives "e"). */
11
+ var foldCharacter = (character) => character.normalize("NFD").replace(DIACRITICS, "").toLowerCase();
12
+ /** A text as matching sees it: "Crème Brûlée" gives "creme brulee". */
13
+ var foldText = (text) => Array.from(text, foldCharacter).join("");
14
+ function foldPieces(text) {
15
+ const pieces = [];
16
+ const offsets = {
17
+ folded: 0,
18
+ original: 0
19
+ };
20
+ for (const character of text) {
21
+ const folded = foldCharacter(character);
22
+ pieces.push({
23
+ end: offsets.original + character.length,
24
+ foldedEnd: offsets.folded + folded.length,
25
+ foldedStart: offsets.folded,
26
+ start: offsets.original
27
+ });
28
+ offsets.original += character.length;
29
+ offsets.folded += folded.length;
30
+ }
31
+ return pieces;
32
+ }
33
+ /**
34
+ * Where a search first occurs in a text, ignoring case and accents, in the text's own indexes; null when it does
35
+ * not, or when the search is empty.
36
+ */
37
+ function findMatch(text, query) {
38
+ const needle = foldText(query.trim());
39
+ if (needle === "") return null;
40
+ const at = foldText(text).indexOf(needle);
41
+ if (at < 0) return null;
42
+ const end = at + needle.length;
43
+ const pieces = foldPieces(text);
44
+ const first = pieces.find((piece) => piece.foldedEnd > at);
45
+ const last = pieces.findLast((piece) => piece.foldedStart < end || piece.foldedEnd === end);
46
+ if (first === void 0 || last === void 0) return null;
47
+ return {
48
+ end: last.end,
49
+ start: first.start
50
+ };
51
+ }
52
+ /** A text cut around its match, so the matched piece can be drawn highlighted. */
53
+ function matchParts(text, query) {
54
+ const match = findMatch(text, query);
55
+ if (match === null) return text === "" ? [] : [{
56
+ matched: false,
57
+ text
58
+ }];
59
+ return [
60
+ {
61
+ matched: false,
62
+ text: text.slice(0, match.start)
63
+ },
64
+ {
65
+ matched: true,
66
+ text: text.slice(match.start, match.end)
67
+ },
68
+ {
69
+ matched: false,
70
+ text: text.slice(match.end)
71
+ }
72
+ ].filter((part) => part.text !== "");
73
+ }
74
+ var startsAWord = (folded, index) => index === 0 || !WORD_CHARACTER.test(folded.charAt(index - 1));
75
+ /** How well a label matches a folded search, or null when it does not contain it. */
76
+ function rankOf(label, needle) {
77
+ const folded = foldText(label);
78
+ const first = folded.indexOf(needle);
79
+ if (first < 0) return null;
80
+ if (first === 0) return RANK.labelStart;
81
+ return Array.from(folded, (_, index) => index).filter((index) => folded.startsWith(needle, index)).some((index) => startsAWord(folded, index)) ? RANK.wordStart : RANK.anywhere;
82
+ }
83
+ /**
84
+ * The options whose label contains the search, ignoring case and accents: those that start with it first, then
85
+ * those with a word that starts with it, then the rest, each group in its original order. An empty search keeps
86
+ * every option.
87
+ */
88
+ function filterOptions(options, query) {
89
+ const needle = foldText(query.trim());
90
+ if (needle === "") return [...options];
91
+ return options.map((option, index) => ({
92
+ index,
93
+ option,
94
+ rank: rankOf(option.label, needle)
95
+ })).filter((entry) => entry.rank !== null).sort((a, b) => a.rank - b.rank || a.index - b.index).map((entry) => entry.option);
96
+ }
97
+ /**
98
+ * The index of the next option that can be chosen, going down (1) or up (-1) from the current one and wrapping
99
+ * around the ends; from -1 (none active) it starts at the first or the last. -1 when none can be chosen.
100
+ */
101
+ function nextEnabledIndex(options, current, direction) {
102
+ const count = options.length;
103
+ const start = current < 0 && direction < 0 ? count : current;
104
+ return Array.from({ length: count }, (_, step) => ((start + direction * (step + 1)) % count + count) % count).find((index) => options[index]?.disabled !== true) ?? -1;
105
+ }
106
+ /** The first (1) or last (-1) option that can be chosen; -1 when none can. */
107
+ var edgeEnabledIndex = (options, from) => nextEnabledIndex(options, -1, from);
108
+ export { edgeEnabledIndex, filterOptions, findMatch, foldText, matchParts, nextEnabledIndex };
@@ -0,0 +1,6 @@
1
+ import type { SelectOption, SelectOptionGroup } from '../types/jl-select.types';
2
+ /**
3
+ * Options laid out as a select draws them: the options of a group gather under it, where the group first appears;
4
+ * options without a group stay in place, between the groups.
5
+ */
6
+ export declare function groupOptions(options: readonly SelectOption[]): SelectOptionGroup[];
@@ -0,0 +1,26 @@
1
+ // src/lib/option-groups.ts
2
+ /**
3
+ * Options laid out as a select draws them: the options of a group gather under it, where the group first appears;
4
+ * options without a group stay in place, between the groups.
5
+ */
6
+ function groupOptions(options) {
7
+ const groups = [];
8
+ for (const option of options) {
9
+ const label = option.group ?? null;
10
+ const target = label === null ? lastIfUngrouped(groups) : groups.find((group) => group.label === label);
11
+ if (target === void 0) {
12
+ groups.push({
13
+ label,
14
+ options: [option]
15
+ });
16
+ continue;
17
+ }
18
+ target.options.push(option);
19
+ }
20
+ return groups;
21
+ }
22
+ var lastIfUngrouped = (groups) => {
23
+ const last = groups.at(-1);
24
+ return last?.label === null ? last : void 0;
25
+ };
26
+ export { groupOptions };
@@ -0,0 +1,5 @@
1
+ import type { ControlSize, Tone } from '../types/options.types';
2
+ export declare const TONES: readonly Tone[];
3
+ export declare const CONTROL_SIZES: readonly ControlSize[];
4
+ /** The icon that says each tone at a glance: alerts, toasts and callouts use it. */
5
+ export declare const TONE_ICONS: Record<Tone, string>;
@@ -0,0 +1,24 @@
1
+ // src/lib/options.ts
2
+ var TONES = [
3
+ "neutral",
4
+ "accent",
5
+ "ok",
6
+ "warn",
7
+ "danger",
8
+ "info"
9
+ ];
10
+ var CONTROL_SIZES = [
11
+ "md",
12
+ "sm",
13
+ "lg"
14
+ ];
15
+ /** The icon that says each tone at a glance: alerts, toasts and callouts use it. */
16
+ var TONE_ICONS = {
17
+ accent: "sparkles",
18
+ danger: "circle-x",
19
+ info: "info",
20
+ neutral: "info",
21
+ ok: "circle-check",
22
+ warn: "triangle-alert"
23
+ };
24
+ export { CONTROL_SIZES, TONES, TONE_ICONS };
@@ -0,0 +1,9 @@
1
+ import type { PageItem } from '../types/pagination.types';
2
+ /** A page number kept within 1 and the last page; a missing or unreadable one is the first page. */
3
+ export declare function clampPage(page: number, pages: number): number;
4
+ /**
5
+ * The entries of a pagination: the first and the last `boundaries` pages, the current page with `siblings` pages
6
+ * on each side, and gaps for the pages between. The list has the same length on every page, so the buttons do not
7
+ * move under the pointer while people step through.
8
+ */
9
+ export declare function pageItems(page: number, pages: number, siblings?: number, boundaries?: number): PageItem[];
@@ -0,0 +1,52 @@
1
+ // src/lib/pagination.ts
2
+ var FIRST_PAGE = 1;
3
+ var range = (start, end) => end < start ? [] : Array.from({ length: end - start + 1 }, (_, index) => start + index);
4
+ var pageItem = (page) => ({
5
+ page,
6
+ type: "page"
7
+ });
8
+ /** A page number kept within 1 and the last page; a missing or unreadable one is the first page. */
9
+ function clampPage(page, pages) {
10
+ if (Number.isNaN(page) || pages < FIRST_PAGE) return FIRST_PAGE;
11
+ return Math.min(Math.max(Math.round(page), FIRST_PAGE), pages);
12
+ }
13
+ /** Between the first pages and the siblings: a gap, the one page a gap would hide, or nothing. */
14
+ function startJoint(siblingsStart, boundaries, pages) {
15
+ if (siblingsStart > boundaries + 2) return [{
16
+ side: "start",
17
+ type: "gap"
18
+ }];
19
+ if (boundaries + 1 < pages - boundaries) return [pageItem(boundaries + 1)];
20
+ return [];
21
+ }
22
+ /** Between the siblings and the last pages: a gap, the one page a gap would hide, or nothing. */
23
+ function endJoint(siblingsEnd, boundaries, pages) {
24
+ if (siblingsEnd < pages - boundaries - 1) return [{
25
+ side: "end",
26
+ type: "gap"
27
+ }];
28
+ if (pages - boundaries > boundaries) return [pageItem(pages - boundaries)];
29
+ return [];
30
+ }
31
+ /**
32
+ * The entries of a pagination: the first and the last `boundaries` pages, the current page with `siblings` pages
33
+ * on each side, and gaps for the pages between. The list has the same length on every page, so the buttons do not
34
+ * move under the pointer while people step through.
35
+ */
36
+ function pageItems(page, pages, siblings = 1, boundaries = 1) {
37
+ if (pages < FIRST_PAGE) return [];
38
+ const current = clampPage(page, pages);
39
+ const startPages = range(FIRST_PAGE, Math.min(boundaries, pages));
40
+ const endPages = range(Math.max(pages - boundaries + 1, boundaries + 1), pages);
41
+ const siblingsStart = Math.max(Math.min(current - siblings, pages - boundaries - siblings * 2 - 1), boundaries + 2);
42
+ const lastBeforeEnd = endPages.length > 0 ? (endPages[0] ?? pages) - 2 : pages - 1;
43
+ const siblingsEnd = Math.min(Math.max(current + siblings, boundaries + siblings * 2 + 2), lastBeforeEnd);
44
+ return [
45
+ ...startPages.map(pageItem),
46
+ ...startJoint(siblingsStart, boundaries, pages),
47
+ ...range(siblingsStart, siblingsEnd).map(pageItem),
48
+ ...endJoint(siblingsEnd, boundaries, pages),
49
+ ...endPages.map(pageItem)
50
+ ];
51
+ }
52
+ export { clampPage, pageItems };
@@ -0,0 +1,8 @@
1
+ import type { PaletteCommand, PaletteGroup, PaletteMatch } from '../types/jl-command-palette.types';
2
+ /**
3
+ * The commands that match the search, best first; an empty search keeps them all in their order. The label's
4
+ * matched letters come with each, for highlighting; a command found by a keyword only has none.
5
+ */
6
+ export declare function matchCommands(commands: readonly PaletteCommand[], query: string): PaletteMatch[];
7
+ /** The matches under their group headings, groups in the order their first command comes. */
8
+ export declare function groupMatches(matches: readonly PaletteMatch[]): PaletteGroup[];
@@ -0,0 +1,46 @@
1
+ import { fuzzyMatch } from "./fuzzy.js";
2
+ // src/lib/palette.ts
3
+ /** A match on a keyword counts less than one on the label people read. */
4
+ var KEYWORD_WEIGHT = .5;
5
+ var NO_GROUP = "";
6
+ function keywordScore(query, keywords) {
7
+ const scores = keywords.map((keyword) => fuzzyMatch(query, keyword)?.score).filter((score) => score !== void 0);
8
+ return scores.length === 0 ? null : Math.max(...scores) * KEYWORD_WEIGHT;
9
+ }
10
+ function matchCommand(query, command) {
11
+ const label = fuzzyMatch(query, command.label);
12
+ const keyword = keywordScore(query, command.keywords ?? []);
13
+ if (label === null && keyword === null) return null;
14
+ if (label === null) return {
15
+ command,
16
+ positions: [],
17
+ score: keyword ?? 0
18
+ };
19
+ return {
20
+ command,
21
+ positions: label.positions,
22
+ score: Math.max(label.score, keyword ?? label.score)
23
+ };
24
+ }
25
+ /**
26
+ * The commands that match the search, best first; an empty search keeps them all in their order. The label's
27
+ * matched letters come with each, for highlighting; a command found by a keyword only has none.
28
+ */
29
+ function matchCommands(commands, query) {
30
+ const matches = commands.map((command) => matchCommand(query, command)).filter((match) => match !== null);
31
+ if (query.trim() === "") return matches;
32
+ return matches.sort((a, b) => b.score - a.score);
33
+ }
34
+ /** The matches under their group headings, groups in the order their first command comes. */
35
+ function groupMatches(matches) {
36
+ const groups = /* @__PURE__ */ new Map();
37
+ for (const match of matches) {
38
+ const label = match.command.group ?? NO_GROUP;
39
+ groups.set(label, [...groups.get(label) ?? [], match]);
40
+ }
41
+ return [...groups].map(([label, grouped]) => ({
42
+ label,
43
+ matches: grouped
44
+ }));
45
+ }
46
+ export { groupMatches, matchCommands };
@@ -0,0 +1,20 @@
1
+ /**
2
+ * A number attribute that may be absent: `null` when it is missing or unreadable. A progress without a value is
3
+ * indeterminate.
4
+ */
5
+ export declare const optionalNumber: (value: string | null) => number | null;
6
+ /** How far along a value is, from 0 to 1; a maximum of 0 or less counts as no progress. */
7
+ export declare function progressRatio(value: number, max: number): number;
8
+ /** A ratio as the percentage people read: 0.456 gives "46%". */
9
+ export declare const percentLabel: (ratio: number) => string;
10
+ /** A ratio in percent, for a path drawn with `pathLength="100"`: 0.25 gives 25. */
11
+ export declare const percentOf: (ratio: number) => number;
12
+ /**
13
+ * The circle of a progress ring of `size` pixels, its stroke `thickness` wide: the stroke stays inside the box,
14
+ * and is at most half the size.
15
+ */
16
+ export declare function ringGeometry(size: number, thickness: number): {
17
+ center: number;
18
+ radius: number;
19
+ stroke: number;
20
+ };
@@ -0,0 +1,33 @@
1
+ // src/lib/progress.ts
2
+ var PERCENT = 100;
3
+ /**
4
+ * A number attribute that may be absent: `null` when it is missing or unreadable. A progress without a value is
5
+ * indeterminate.
6
+ */
7
+ var optionalNumber = (value) => {
8
+ const parsed = value === null || value.trim() === "" ? NaN : Number(value);
9
+ return Number.isFinite(parsed) ? parsed : null;
10
+ };
11
+ /** How far along a value is, from 0 to 1; a maximum of 0 or less counts as no progress. */
12
+ function progressRatio(value, max) {
13
+ if (!(max > 0) || !Number.isFinite(value)) return 0;
14
+ return Math.min(1, Math.max(0, value / max));
15
+ }
16
+ /** A ratio as the percentage people read: 0.456 gives "46%". */
17
+ var percentLabel = (ratio) => `${Math.round(ratio * PERCENT)}%`;
18
+ /** A ratio in percent, for a path drawn with `pathLength="100"`: 0.25 gives 25. */
19
+ var percentOf = (ratio) => ratio * PERCENT;
20
+ /**
21
+ * The circle of a progress ring of `size` pixels, its stroke `thickness` wide: the stroke stays inside the box,
22
+ * and is at most half the size.
23
+ */
24
+ function ringGeometry(size, thickness) {
25
+ const box = Math.max(0, size);
26
+ const stroke = Math.min(Math.max(0, thickness), box / 2);
27
+ return {
28
+ center: box / 2,
29
+ radius: (box - stroke) / 2,
30
+ stroke
31
+ };
32
+ }
33
+ export { optionalNumber, percentLabel, percentOf, progressRatio, ringGeometry };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * The rating a key gives, as a slider reads keys: right and up add one, left and down take one, Home empties it,
3
+ * End fills it, a digit sets that many (when it fits). Null for any other key.
4
+ */
5
+ export declare function ratingAfterKey(value: number, key: string, max: number): number | null;
6
+ /** Choosing the rating already given clears it: a second press on the third star leaves none. */
7
+ export declare const ratingAfterChoice: (value: number, chosen: number) => number;
@@ -0,0 +1,26 @@
1
+ // src/lib/rating.ts
2
+ var DIGIT = /^\d$/;
3
+ /** A digit key sets that many, when the scale has room for it. */
4
+ function ratingOfDigit(key, max) {
5
+ const rating = Number(key);
6
+ return rating <= max ? rating : null;
7
+ }
8
+ /**
9
+ * The rating a key gives, as a slider reads keys: right and up add one, left and down take one, Home empties it,
10
+ * End fills it, a digit sets that many (when it fits). Null for any other key.
11
+ */
12
+ function ratingAfterKey(value, key, max) {
13
+ if (DIGIT.test(key)) return ratingOfDigit(key, max);
14
+ const next = (/* @__PURE__ */ new Map([
15
+ ["ArrowDown", value - 1],
16
+ ["ArrowLeft", value - 1],
17
+ ["ArrowRight", value + 1],
18
+ ["ArrowUp", value + 1],
19
+ ["End", max],
20
+ ["Home", 0]
21
+ ])).get(key);
22
+ return next === void 0 ? null : Math.min(max, Math.max(0, next));
23
+ }
24
+ /** Choosing the rating already given clears it: a second press on the third star leaves none. */
25
+ var ratingAfterChoice = (value, chosen) => chosen === value ? 0 : chosen;
26
+ export { ratingAfterChoice, ratingAfterKey };
@@ -0,0 +1,10 @@
1
+ import type { RovingMove } from '../types/roving-index.types';
2
+ /** The first item that is not disabled, or -1 when every item is. */
3
+ export declare function firstEnabledIndex(disabled: readonly boolean[]): number;
4
+ /** The last item that is not disabled, or -1 when every item is. */
5
+ export declare function lastEnabledIndex(disabled: readonly boolean[]): number;
6
+ /**
7
+ * Where a move goes from the item at `from`, skipping disabled items as native radios do. From nowhere (-1), next
8
+ * starts at the first item and previous at the last.
9
+ */
10
+ export declare function rovingIndex(disabled: readonly boolean[], from: number, move: RovingMove): number;
@@ -0,0 +1,32 @@
1
+ // src/lib/roving-index.ts
2
+ var NONE = -1;
3
+ /** The first item that is not disabled, or -1 when every item is. */
4
+ function firstEnabledIndex(disabled) {
5
+ return disabled.indexOf(false);
6
+ }
7
+ /** The last item that is not disabled, or -1 when every item is. */
8
+ function lastEnabledIndex(disabled) {
9
+ return disabled.lastIndexOf(false);
10
+ }
11
+ /** The next enabled item after `from` in a direction, wrapping around the ends; -1 when none is enabled. */
12
+ function steppedIndex(disabled, from, step) {
13
+ const count = disabled.length;
14
+ for (let offset = 1; offset <= count; offset += 1) {
15
+ const index = ((from + step * offset) % count + count) % count;
16
+ if (disabled[index] === false) return index;
17
+ }
18
+ return NONE;
19
+ }
20
+ /**
21
+ * Where a move goes from the item at `from`, skipping disabled items as native radios do. From nowhere (-1), next
22
+ * starts at the first item and previous at the last.
23
+ */
24
+ function rovingIndex(disabled, from, move) {
25
+ return {
26
+ first: () => firstEnabledIndex(disabled),
27
+ last: () => lastEnabledIndex(disabled),
28
+ next: () => steppedIndex(disabled, from, 1),
29
+ previous: () => steppedIndex(disabled, from === NONE ? disabled.length : from, -1)
30
+ }[move]();
31
+ }
32
+ export { firstEnabledIndex, lastEnabledIndex, rovingIndex };
@@ -0,0 +1,11 @@
1
+ /** The first entry that can be chosen. */
2
+ export declare const firstIndex: (enabled: readonly boolean[]) => number;
3
+ /** The last entry that can be chosen. */
4
+ export declare const lastIndex: (enabled: readonly boolean[]) => number;
5
+ /** The entry `step` places away from `from` among those that can be chosen, wrapping around the ends. */
6
+ export declare function stepIndex(enabled: readonly boolean[], from: number, step: 1 | -1): number;
7
+ /**
8
+ * Typeahead: the next entry after `from` whose label starts with the letter typed, wrapping around; the search
9
+ * ignores case and the spaces before the label.
10
+ */
11
+ export declare function letterIndex(labels: readonly string[], enabled: readonly boolean[], from: number, letter: string): number;
@@ -0,0 +1,21 @@
1
+ // src/lib/roving.ts
2
+ var NONE = -1;
3
+ /** The first entry that can be chosen. */
4
+ var firstIndex = (enabled) => enabled.indexOf(true);
5
+ /** The last entry that can be chosen. */
6
+ var lastIndex = (enabled) => enabled.lastIndexOf(true);
7
+ var wrap = (index, count) => (index % count + count) % count;
8
+ /** The entry `step` places away from `from` among those that can be chosen, wrapping around the ends. */
9
+ function stepIndex(enabled, from, step) {
10
+ const count = enabled.length;
11
+ return Array.from({ length: count }, (_, offset) => wrap(from + step * (offset + 1), count)).find((index) => enabled[index] === true) ?? NONE;
12
+ }
13
+ /**
14
+ * Typeahead: the next entry after `from` whose label starts with the letter typed, wrapping around; the search
15
+ * ignores case and the spaces before the label.
16
+ */
17
+ function letterIndex(labels, enabled, from, letter) {
18
+ const wanted = letter.toLocaleLowerCase();
19
+ return stepIndex(labels.map((label, index) => enabled[index] === true && label.trim().toLocaleLowerCase().startsWith(wanted)), from, 1);
20
+ }
21
+ export { firstIndex, lastIndex, letterIndex, stepIndex };
@@ -0,0 +1,2 @@
1
+ /** Whether a slot holds something to show: an element, or text that is not only white space. */
2
+ export declare function hasSlotted(slot: HTMLSlotElement): boolean;
@@ -0,0 +1,6 @@
1
+ // src/lib/slots.ts
2
+ /** Whether a slot holds something to show: an element, or text that is not only white space. */
3
+ function hasSlotted(slot) {
4
+ return slot.assignedNodes({ flatten: true }).some((node) => node instanceof Element || (node.textContent ?? "").trim() !== "");
5
+ }
6
+ export { hasSlotted };
@@ -0,0 +1,11 @@
1
+ import type { SparkBox, SparkPoint } from '../types/sparkline.types';
2
+ /**
3
+ * The points of a sparkline in a box: evenly spaced from left to right, the highest value at the top and the
4
+ * lowest at the bottom, `inset` away from the edges. One value draws a flat line across; equal values sit in the
5
+ * middle. Values that are not finite numbers are left out.
6
+ */
7
+ export declare function sparklinePoints(values: readonly number[], box: SparkBox): SparkPoint[];
8
+ /** Points as an SVG `points` attribute: "0,10 25,4 50,8". */
9
+ export declare const pointsAttribute: (points: readonly SparkPoint[]) => string;
10
+ /** The area under the line, closed along the bottom of the box, as an SVG `points` attribute. */
11
+ export declare function areaAttribute(points: readonly SparkPoint[], box: SparkBox): string;
@@ -0,0 +1,42 @@
1
+ // src/lib/sparkline.ts
2
+ var PRECISION = 100;
3
+ var round = (value) => Math.round(value * PRECISION) / PRECISION;
4
+ /**
5
+ * The points of a sparkline in a box: evenly spaced from left to right, the highest value at the top and the
6
+ * lowest at the bottom, `inset` away from the edges. One value draws a flat line across; equal values sit in the
7
+ * middle. Values that are not finite numbers are left out.
8
+ */
9
+ function sparklinePoints(values, box) {
10
+ const finite = values.filter((value) => Number.isFinite(value));
11
+ if (finite.length === 0) return [];
12
+ const series = finite.length === 1 ? [finite[0] ?? 0, finite[0] ?? 0] : finite;
13
+ const min = Math.min(...series);
14
+ const range = Math.max(...series) - min;
15
+ const innerWidth = box.width - box.inset * 2;
16
+ const innerHeight = box.height - box.inset * 2;
17
+ const step = innerWidth / (series.length - 1);
18
+ return series.map((value, index) => ({
19
+ x: round(box.inset + index * step),
20
+ y: round(range === 0 ? box.height / 2 : box.inset + (1 - (value - min) / range) * innerHeight)
21
+ }));
22
+ }
23
+ /** Points as an SVG `points` attribute: "0,10 25,4 50,8". */
24
+ var pointsAttribute = (points) => points.map((point) => `${point.x},${point.y}`).join(" ");
25
+ /** The area under the line, closed along the bottom of the box, as an SVG `points` attribute. */
26
+ function areaAttribute(points, box) {
27
+ const first = points[0];
28
+ const last = points.at(-1);
29
+ if (first === void 0 || last === void 0) return "";
30
+ return pointsAttribute([
31
+ ...points,
32
+ {
33
+ x: last.x,
34
+ y: box.height
35
+ },
36
+ {
37
+ x: first.x,
38
+ y: box.height
39
+ }
40
+ ]);
41
+ }
42
+ export { areaAttribute, pointsAttribute, sparklinePoints };
@@ -0,0 +1,12 @@
1
+ import type { SplitOrientation } from '../types/jl-split-view.types';
2
+ /** The smallest share each pane keeps, in percent: never under 0 nor over half. */
3
+ export declare const clampMin: (min: number) => number;
4
+ /** A divider position kept so that each pane keeps at least `min` percent. */
5
+ export declare const clampSplit: (position: number, min: number) => number;
6
+ /** Where a pointer sits along the split, in percent of its length from its start; the middle when it has none. */
7
+ export declare const positionAt: (pointer: number, start: number, length: number) => number;
8
+ /**
9
+ * Where a key moves the divider: arrows along the split by a step (a large one with Shift), Home and End to the
10
+ * ends; null for any other key.
11
+ */
12
+ export declare function positionAfterKey(position: number, key: string, orientation: SplitOrientation, min: number, large?: boolean): number | null;