@chenzy-design/core 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 (310) hide show
  1. package/LICENSE +21 -0
  2. package/dist/back-top.d.ts +16 -0
  3. package/dist/back-top.d.ts.map +1 -0
  4. package/dist/back-top.js +29 -0
  5. package/dist/back-top.js.map +1 -0
  6. package/dist/back-top.test.d.ts +2 -0
  7. package/dist/back-top.test.d.ts.map +1 -0
  8. package/dist/back-top.test.js +44 -0
  9. package/dist/back-top.test.js.map +1 -0
  10. package/dist/banner.d.ts +19 -0
  11. package/dist/banner.d.ts.map +1 -0
  12. package/dist/banner.js +20 -0
  13. package/dist/banner.js.map +1 -0
  14. package/dist/banner.test.d.ts +2 -0
  15. package/dist/banner.test.d.ts.map +1 -0
  16. package/dist/banner.test.js +29 -0
  17. package/dist/banner.test.js.map +1 -0
  18. package/dist/breakpoints.d.ts +30 -0
  19. package/dist/breakpoints.d.ts.map +1 -0
  20. package/dist/breakpoints.js +45 -0
  21. package/dist/breakpoints.js.map +1 -0
  22. package/dist/breakpoints.test.d.ts +2 -0
  23. package/dist/breakpoints.test.d.ts.map +1 -0
  24. package/dist/breakpoints.test.js +46 -0
  25. package/dist/breakpoints.test.js.map +1 -0
  26. package/dist/calendar.d.ts +63 -0
  27. package/dist/calendar.d.ts.map +1 -0
  28. package/dist/calendar.js +94 -0
  29. package/dist/calendar.js.map +1 -0
  30. package/dist/calendar.test.d.ts +2 -0
  31. package/dist/calendar.test.d.ts.map +1 -0
  32. package/dist/calendar.test.js +88 -0
  33. package/dist/calendar.test.js.map +1 -0
  34. package/dist/cascader.d.ts +64 -0
  35. package/dist/cascader.d.ts.map +1 -0
  36. package/dist/cascader.js +79 -0
  37. package/dist/cascader.js.map +1 -0
  38. package/dist/cascader.test.d.ts +2 -0
  39. package/dist/cascader.test.d.ts.map +1 -0
  40. package/dist/cascader.test.js +107 -0
  41. package/dist/cascader.test.js.map +1 -0
  42. package/dist/color.d.ts +70 -0
  43. package/dist/color.d.ts.map +1 -0
  44. package/dist/color.js +217 -0
  45. package/dist/color.js.map +1 -0
  46. package/dist/color.test.d.ts +2 -0
  47. package/dist/color.test.d.ts.map +1 -0
  48. package/dist/color.test.js +161 -0
  49. package/dist/color.test.js.map +1 -0
  50. package/dist/config-provider.d.ts +57 -0
  51. package/dist/config-provider.d.ts.map +1 -0
  52. package/dist/config-provider.js +49 -0
  53. package/dist/config-provider.js.map +1 -0
  54. package/dist/config-provider.test.d.ts +2 -0
  55. package/dist/config-provider.test.d.ts.map +1 -0
  56. package/dist/config-provider.test.js +76 -0
  57. package/dist/config-provider.test.js.map +1 -0
  58. package/dist/copyable.d.ts +43 -0
  59. package/dist/copyable.d.ts.map +1 -0
  60. package/dist/copyable.js +102 -0
  61. package/dist/copyable.js.map +1 -0
  62. package/dist/copyable.test.d.ts +2 -0
  63. package/dist/copyable.test.d.ts.map +1 -0
  64. package/dist/copyable.test.js +97 -0
  65. package/dist/copyable.test.js.map +1 -0
  66. package/dist/date.d.ts +86 -0
  67. package/dist/date.d.ts.map +1 -0
  68. package/dist/date.js +226 -0
  69. package/dist/date.js.map +1 -0
  70. package/dist/date.test.d.ts +2 -0
  71. package/dist/date.test.d.ts.map +1 -0
  72. package/dist/date.test.js +171 -0
  73. package/dist/date.test.js.map +1 -0
  74. package/dist/dismiss.d.ts +28 -0
  75. package/dist/dismiss.d.ts.map +1 -0
  76. package/dist/dismiss.js +42 -0
  77. package/dist/dismiss.js.map +1 -0
  78. package/dist/editable.d.ts +59 -0
  79. package/dist/editable.d.ts.map +1 -0
  80. package/dist/editable.js +85 -0
  81. package/dist/editable.js.map +1 -0
  82. package/dist/editable.test.d.ts +2 -0
  83. package/dist/editable.test.d.ts.map +1 -0
  84. package/dist/editable.test.js +111 -0
  85. package/dist/editable.test.js.map +1 -0
  86. package/dist/ellipsis.d.ts +76 -0
  87. package/dist/ellipsis.d.ts.map +1 -0
  88. package/dist/ellipsis.js +99 -0
  89. package/dist/ellipsis.js.map +1 -0
  90. package/dist/ellipsis.test.d.ts +2 -0
  91. package/dist/ellipsis.test.d.ts.map +1 -0
  92. package/dist/ellipsis.test.js +94 -0
  93. package/dist/ellipsis.test.js.map +1 -0
  94. package/dist/floating.d.ts +78 -0
  95. package/dist/floating.d.ts.map +1 -0
  96. package/dist/floating.js +169 -0
  97. package/dist/floating.js.map +1 -0
  98. package/dist/floating.test.d.ts +2 -0
  99. package/dist/floating.test.d.ts.map +1 -0
  100. package/dist/floating.test.js +169 -0
  101. package/dist/floating.test.js.map +1 -0
  102. package/dist/focus-trap.d.ts +24 -0
  103. package/dist/focus-trap.d.ts.map +1 -0
  104. package/dist/focus-trap.js +64 -0
  105. package/dist/focus-trap.js.map +1 -0
  106. package/dist/form.d.ts +131 -0
  107. package/dist/form.d.ts.map +1 -0
  108. package/dist/form.js +257 -0
  109. package/dist/form.js.map +1 -0
  110. package/dist/form.test.d.ts +2 -0
  111. package/dist/form.test.d.ts.map +1 -0
  112. package/dist/form.test.js +323 -0
  113. package/dist/form.test.js.map +1 -0
  114. package/dist/highlight.d.ts +57 -0
  115. package/dist/highlight.d.ts.map +1 -0
  116. package/dist/highlight.js +124 -0
  117. package/dist/highlight.js.map +1 -0
  118. package/dist/highlight.test.d.ts +2 -0
  119. package/dist/highlight.test.d.ts.map +1 -0
  120. package/dist/highlight.test.js +155 -0
  121. package/dist/highlight.test.js.map +1 -0
  122. package/dist/id.d.ts +4 -0
  123. package/dist/id.d.ts.map +1 -0
  124. package/dist/id.js +14 -0
  125. package/dist/id.js.map +1 -0
  126. package/dist/id.test.d.ts +2 -0
  127. package/dist/id.test.d.ts.map +1 -0
  128. package/dist/id.test.js +13 -0
  129. package/dist/id.test.js.map +1 -0
  130. package/dist/index.d.ts +46 -0
  131. package/dist/index.d.ts.map +1 -0
  132. package/dist/index.js +45 -0
  133. package/dist/index.js.map +1 -0
  134. package/dist/inert-background.d.ts +27 -0
  135. package/dist/inert-background.d.ts.map +1 -0
  136. package/dist/inert-background.js +92 -0
  137. package/dist/inert-background.js.map +1 -0
  138. package/dist/inert-background.test.d.ts +2 -0
  139. package/dist/inert-background.test.d.ts.map +1 -0
  140. package/dist/inert-background.test.js +62 -0
  141. package/dist/inert-background.test.js.map +1 -0
  142. package/dist/input-number.d.ts +34 -0
  143. package/dist/input-number.d.ts.map +1 -0
  144. package/dist/input-number.js +69 -0
  145. package/dist/input-number.js.map +1 -0
  146. package/dist/input-number.test.d.ts +2 -0
  147. package/dist/input-number.test.d.ts.map +1 -0
  148. package/dist/input-number.test.js +74 -0
  149. package/dist/input-number.test.js.map +1 -0
  150. package/dist/list-selection.d.ts +38 -0
  151. package/dist/list-selection.d.ts.map +1 -0
  152. package/dist/list-selection.js +61 -0
  153. package/dist/list-selection.js.map +1 -0
  154. package/dist/list-selection.test.d.ts +2 -0
  155. package/dist/list-selection.test.d.ts.map +1 -0
  156. package/dist/list-selection.test.js +61 -0
  157. package/dist/list-selection.test.js.map +1 -0
  158. package/dist/live-announcer.d.ts +38 -0
  159. package/dist/live-announcer.d.ts.map +1 -0
  160. package/dist/live-announcer.js +89 -0
  161. package/dist/live-announcer.js.map +1 -0
  162. package/dist/live-announcer.test.d.ts +2 -0
  163. package/dist/live-announcer.test.d.ts.map +1 -0
  164. package/dist/live-announcer.test.js +70 -0
  165. package/dist/live-announcer.test.js.map +1 -0
  166. package/dist/lottie-icon.d.ts +88 -0
  167. package/dist/lottie-icon.d.ts.map +1 -0
  168. package/dist/lottie-icon.js +84 -0
  169. package/dist/lottie-icon.js.map +1 -0
  170. package/dist/lottie-icon.test.d.ts +2 -0
  171. package/dist/lottie-icon.test.d.ts.map +1 -0
  172. package/dist/lottie-icon.test.js +88 -0
  173. package/dist/lottie-icon.test.js.map +1 -0
  174. package/dist/notification.d.ts +61 -0
  175. package/dist/notification.d.ts.map +1 -0
  176. package/dist/notification.js +133 -0
  177. package/dist/notification.js.map +1 -0
  178. package/dist/notification.test.d.ts +2 -0
  179. package/dist/notification.test.d.ts.map +1 -0
  180. package/dist/notification.test.js +103 -0
  181. package/dist/notification.test.js.map +1 -0
  182. package/dist/overflow-list.d.ts +102 -0
  183. package/dist/overflow-list.d.ts.map +1 -0
  184. package/dist/overflow-list.js +159 -0
  185. package/dist/overflow-list.js.map +1 -0
  186. package/dist/overflow-list.test.d.ts +2 -0
  187. package/dist/overflow-list.test.d.ts.map +1 -0
  188. package/dist/overflow-list.test.js +223 -0
  189. package/dist/overflow-list.test.js.map +1 -0
  190. package/dist/pagination.d.ts +51 -0
  191. package/dist/pagination.d.ts.map +1 -0
  192. package/dist/pagination.js +127 -0
  193. package/dist/pagination.js.map +1 -0
  194. package/dist/pagination.test.d.ts +2 -0
  195. package/dist/pagination.test.d.ts.map +1 -0
  196. package/dist/pagination.test.js +147 -0
  197. package/dist/pagination.test.js.map +1 -0
  198. package/dist/progress.d.ts +64 -0
  199. package/dist/progress.d.ts.map +1 -0
  200. package/dist/progress.js +92 -0
  201. package/dist/progress.js.map +1 -0
  202. package/dist/progress.test.d.ts +2 -0
  203. package/dist/progress.test.d.ts.map +1 -0
  204. package/dist/progress.test.js +79 -0
  205. package/dist/progress.test.js.map +1 -0
  206. package/dist/resize-observer.d.ts +133 -0
  207. package/dist/resize-observer.d.ts.map +1 -0
  208. package/dist/resize-observer.js +358 -0
  209. package/dist/resize-observer.js.map +1 -0
  210. package/dist/resize-observer.test.d.ts +2 -0
  211. package/dist/resize-observer.test.d.ts.map +1 -0
  212. package/dist/resize-observer.test.js +377 -0
  213. package/dist/resize-observer.test.js.map +1 -0
  214. package/dist/roving.d.ts +26 -0
  215. package/dist/roving.d.ts.map +1 -0
  216. package/dist/roving.js +61 -0
  217. package/dist/roving.js.map +1 -0
  218. package/dist/roving.test.d.ts +2 -0
  219. package/dist/roving.test.d.ts.map +1 -0
  220. package/dist/roving.test.js +48 -0
  221. package/dist/roving.test.js.map +1 -0
  222. package/dist/scroll-list.d.ts +81 -0
  223. package/dist/scroll-list.d.ts.map +1 -0
  224. package/dist/scroll-list.js +175 -0
  225. package/dist/scroll-list.js.map +1 -0
  226. package/dist/scroll-list.test.d.ts +2 -0
  227. package/dist/scroll-list.test.d.ts.map +1 -0
  228. package/dist/scroll-list.test.js +135 -0
  229. package/dist/scroll-list.test.js.map +1 -0
  230. package/dist/scroll-lock.d.ts +11 -0
  231. package/dist/scroll-lock.d.ts.map +1 -0
  232. package/dist/scroll-lock.js +45 -0
  233. package/dist/scroll-lock.js.map +1 -0
  234. package/dist/scroll-lock.test.d.ts +2 -0
  235. package/dist/scroll-lock.test.d.ts.map +1 -0
  236. package/dist/scroll-lock.test.js +55 -0
  237. package/dist/scroll-lock.test.js.map +1 -0
  238. package/dist/sider.d.ts +26 -0
  239. package/dist/sider.d.ts.map +1 -0
  240. package/dist/sider.js +55 -0
  241. package/dist/sider.js.map +1 -0
  242. package/dist/sider.test.d.ts +2 -0
  243. package/dist/sider.test.d.ts.map +1 -0
  244. package/dist/sider.test.js +29 -0
  245. package/dist/sider.test.js.map +1 -0
  246. package/dist/spin.d.ts +25 -0
  247. package/dist/spin.d.ts.map +1 -0
  248. package/dist/spin.js +96 -0
  249. package/dist/spin.js.map +1 -0
  250. package/dist/spin.test.d.ts +2 -0
  251. package/dist/spin.test.d.ts.map +1 -0
  252. package/dist/spin.test.js +80 -0
  253. package/dist/spin.test.js.map +1 -0
  254. package/dist/table.d.ts +101 -0
  255. package/dist/table.d.ts.map +1 -0
  256. package/dist/table.js +244 -0
  257. package/dist/table.js.map +1 -0
  258. package/dist/table.test.d.ts +2 -0
  259. package/dist/table.test.d.ts.map +1 -0
  260. package/dist/table.test.js +184 -0
  261. package/dist/table.test.js.map +1 -0
  262. package/dist/textarea.d.ts +48 -0
  263. package/dist/textarea.d.ts.map +1 -0
  264. package/dist/textarea.js +76 -0
  265. package/dist/textarea.js.map +1 -0
  266. package/dist/textarea.test.d.ts +2 -0
  267. package/dist/textarea.test.d.ts.map +1 -0
  268. package/dist/textarea.test.js +77 -0
  269. package/dist/textarea.test.js.map +1 -0
  270. package/dist/time.d.ts +93 -0
  271. package/dist/time.d.ts.map +1 -0
  272. package/dist/time.js +181 -0
  273. package/dist/time.js.map +1 -0
  274. package/dist/time.test.d.ts +2 -0
  275. package/dist/time.test.d.ts.map +1 -0
  276. package/dist/time.test.js +159 -0
  277. package/dist/time.test.js.map +1 -0
  278. package/dist/toast.d.ts +60 -0
  279. package/dist/toast.d.ts.map +1 -0
  280. package/dist/toast.js +135 -0
  281. package/dist/toast.js.map +1 -0
  282. package/dist/toast.test.d.ts +2 -0
  283. package/dist/toast.test.d.ts.map +1 -0
  284. package/dist/toast.test.js +127 -0
  285. package/dist/toast.test.js.map +1 -0
  286. package/dist/tree.d.ts +129 -0
  287. package/dist/tree.d.ts.map +1 -0
  288. package/dist/tree.js +351 -0
  289. package/dist/tree.js.map +1 -0
  290. package/dist/tree.test.d.ts +2 -0
  291. package/dist/tree.test.d.ts.map +1 -0
  292. package/dist/tree.test.js +291 -0
  293. package/dist/tree.test.js.map +1 -0
  294. package/dist/upload.d.ts +62 -0
  295. package/dist/upload.d.ts.map +1 -0
  296. package/dist/upload.js +85 -0
  297. package/dist/upload.js.map +1 -0
  298. package/dist/upload.test.d.ts +2 -0
  299. package/dist/upload.test.d.ts.map +1 -0
  300. package/dist/upload.test.js +169 -0
  301. package/dist/upload.test.js.map +1 -0
  302. package/dist/virtual-list.d.ts +83 -0
  303. package/dist/virtual-list.d.ts.map +1 -0
  304. package/dist/virtual-list.js +143 -0
  305. package/dist/virtual-list.js.map +1 -0
  306. package/dist/virtual-list.test.d.ts +2 -0
  307. package/dist/virtual-list.test.d.ts.map +1 -0
  308. package/dist/virtual-list.test.js +148 -0
  309. package/dist/virtual-list.test.js.map +1 -0
  310. package/package.json +22 -0
@@ -0,0 +1,184 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { nextSortOrder, toggleSort, applySort, paginate, pageCount, selectAllState, toggleSelectAll, toggleRow, flattenTreeRows, conductRows, normalizeRowsToLeaves, toggleRowCheck, } from './table.js';
3
+ describe('sort cycling', () => {
4
+ it('cycles none β†’ ascend β†’ descend β†’ none', () => {
5
+ expect(nextSortOrder(null)).toBe('ascend');
6
+ expect(nextSortOrder('ascend')).toBe('descend');
7
+ expect(nextSortOrder('descend')).toBe(null);
8
+ });
9
+ it('toggleSort starts a new column at ascend', () => {
10
+ const s = { key: 'a', order: 'descend' };
11
+ expect(toggleSort(s, 'b')).toEqual({ key: 'b', order: 'ascend' });
12
+ });
13
+ it('toggleSort cycles the same column and clears key at none', () => {
14
+ expect(toggleSort({ key: 'a', order: null }, 'a')).toEqual({ key: 'a', order: 'ascend' });
15
+ expect(toggleSort({ key: 'a', order: 'ascend' }, 'a')).toEqual({ key: 'a', order: 'descend' });
16
+ expect(toggleSort({ key: 'a', order: 'descend' }, 'a')).toEqual({ key: null, order: null });
17
+ });
18
+ });
19
+ describe('applySort', () => {
20
+ const data = [
21
+ { id: 1, n: 3 },
22
+ { id: 2, n: 1 },
23
+ { id: 3, n: 1 },
24
+ { id: 4, n: 2 },
25
+ ];
26
+ const cmp = (a, b) => a.n - b.n;
27
+ it('returns a copy unchanged when order is null', () => {
28
+ const out = applySort(data, null, cmp);
29
+ expect(out).toEqual(data);
30
+ expect(out).not.toBe(data);
31
+ });
32
+ it('sorts ascending', () => {
33
+ expect(applySort(data, 'ascend', cmp).map((d) => d.id)).toEqual([2, 3, 4, 1]);
34
+ });
35
+ it('sorts descending', () => {
36
+ expect(applySort(data, 'descend', cmp).map((d) => d.id)).toEqual([1, 4, 2, 3]);
37
+ });
38
+ it('is stable for equal keys (ascend keeps original order)', () => {
39
+ // ids 2 and 3 both have n=1 β†’ stay in original relative order
40
+ expect(applySort(data, 'ascend', cmp).slice(0, 2).map((d) => d.id)).toEqual([2, 3]);
41
+ });
42
+ });
43
+ describe('pagination', () => {
44
+ const data = Array.from({ length: 23 }, (_, i) => i + 1);
45
+ it('slices the right page', () => {
46
+ expect(paginate(data, 1, 10)).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
47
+ expect(paginate(data, 3, 10)).toEqual([21, 22, 23]);
48
+ });
49
+ it('computes page count', () => {
50
+ expect(pageCount(23, 10)).toBe(3);
51
+ expect(pageCount(0, 10)).toBe(1);
52
+ expect(pageCount(20, 10)).toBe(2);
53
+ });
54
+ });
55
+ describe('selection', () => {
56
+ const keys = ['a', 'b', 'c', 'd'];
57
+ it('select-all state: none / some / all', () => {
58
+ expect(selectAllState(keys, new Set())).toEqual({ checked: false, indeterminate: false });
59
+ expect(selectAllState(keys, new Set(['a']))).toEqual({ checked: false, indeterminate: true });
60
+ expect(selectAllState(keys, new Set(keys))).toEqual({ checked: true, indeterminate: false });
61
+ });
62
+ it('excludes disabled rows from the all-computation', () => {
63
+ // d disabled; selecting a,b,c counts as "all selectable"
64
+ const st = selectAllState(keys, new Set(['a', 'b', 'c']), new Set(['d']));
65
+ expect(st).toEqual({ checked: true, indeterminate: false });
66
+ });
67
+ it('toggleSelectAll selects all selectable then clears', () => {
68
+ const on = toggleSelectAll(keys, new Set(), new Set(['d']));
69
+ expect([...on].sort()).toEqual(['a', 'b', 'c']); // d never auto-selected
70
+ const off = toggleSelectAll(keys, on, new Set(['d']));
71
+ expect(off.size).toBe(0);
72
+ });
73
+ it('toggleSelectAll preserves a pre-selected disabled row', () => {
74
+ const off = toggleSelectAll(keys, new Set(['a', 'b', 'c', 'd']), new Set(['d']));
75
+ expect([...off]).toEqual(['d']); // selectable cleared, disabled d kept
76
+ });
77
+ it('toggleRow flips a single row', () => {
78
+ expect([...toggleRow(new Set(), 'a')]).toEqual(['a']);
79
+ expect([...toggleRow(new Set(['a']), 'a')]).toEqual([]);
80
+ });
81
+ });
82
+ describe('flattenTreeRows', () => {
83
+ const data = [
84
+ {
85
+ id: 1,
86
+ children: [{ id: 11 }, { id: 12, children: [{ id: 121 }] }],
87
+ },
88
+ { id: 2 },
89
+ { id: 3, children: [{ id: 31 }] },
90
+ ];
91
+ const getKey = (r) => r.id;
92
+ const getChildren = (r) => r.children;
93
+ it('shows only top-level rows when nothing is expanded', () => {
94
+ const flat = flattenTreeRows(data, new Set(), getKey, getChildren);
95
+ expect(flat.map((r) => r.key)).toEqual([1, 2, 3]);
96
+ expect(flat.every((r) => r.level === 0)).toBe(true);
97
+ expect(flat.map((r) => r.hasChildren)).toEqual([true, false, true]);
98
+ expect(flat.map((r) => r.topIndex)).toEqual([0, 1, 2]);
99
+ });
100
+ it('reveals children of expanded rows with incremented level', () => {
101
+ const flat = flattenTreeRows(data, new Set([1]), getKey, getChildren);
102
+ expect(flat.map((r) => r.key)).toEqual([1, 11, 12, 2, 3]);
103
+ const r12 = flat.find((r) => r.key === 12);
104
+ expect(r12.level).toBe(1);
105
+ expect(r12.parentKey).toBe(1);
106
+ expect(r12.hasChildren).toBe(true);
107
+ });
108
+ it('recurses into nested expanded rows and carries topIndex', () => {
109
+ const flat = flattenTreeRows(data, new Set([1, 12]), getKey, getChildren);
110
+ expect(flat.map((r) => r.key)).toEqual([1, 11, 12, 121, 2, 3]);
111
+ const deep = flat.find((r) => r.key === 121);
112
+ expect(deep.level).toBe(2);
113
+ expect(deep.parentKey).toBe(12);
114
+ expect(deep.topIndex).toBe(0);
115
+ });
116
+ it('does not expand a key with no children', () => {
117
+ const flat = flattenTreeRows(data, new Set([2]), getKey, getChildren);
118
+ expect(flat.map((r) => r.key)).toEqual([1, 2, 3]);
119
+ });
120
+ });
121
+ describe('tree row selection conduction', () => {
122
+ // 1 ─ 11
123
+ // β”” 12 ─ 121
124
+ // β”” 122
125
+ // 2
126
+ const data = [
127
+ {
128
+ id: 1,
129
+ children: [
130
+ { id: 11 },
131
+ { id: 12, children: [{ id: 121 }, { id: 122 }] },
132
+ ],
133
+ },
134
+ { id: 2 },
135
+ ];
136
+ const getKey = (r) => r.id;
137
+ const getChildren = (r) => r.children;
138
+ const isDisabled = (r) => !!r.disabled;
139
+ it('conductRows: checking a parent fully checks descendants', () => {
140
+ const { checked, half } = conductRows(data, new Set([1]), getKey, getChildren);
141
+ expect([...checked].sort((a, b) => Number(a) - Number(b))).toEqual([1, 11, 12, 121, 122]);
142
+ expect(half.size).toBe(0);
143
+ });
144
+ it('conductRows: partial children make ancestors half', () => {
145
+ const { checked, half } = conductRows(data, new Set([121]), getKey, getChildren);
146
+ expect([...checked]).toEqual([121]);
147
+ expect([...half].sort((a, b) => Number(a) - Number(b))).toEqual([1, 12]);
148
+ });
149
+ it('conductRows: all leaves of a parent β†’ parent checked, not half', () => {
150
+ const { checked, half } = conductRows(data, new Set([121, 122]), getKey, getChildren);
151
+ expect(checked.has(12)).toBe(true);
152
+ expect(half.has(12)).toBe(false); // 12 fully checked, not half
153
+ expect(half.has(1)).toBe(true); // 1 still partial (11 unchecked)
154
+ });
155
+ it('conductRows: disabled leaf excluded from parent computation', () => {
156
+ const withDisabled = [
157
+ { id: 1, children: [{ id: 11 }, { id: 12, disabled: true }] },
158
+ ];
159
+ // check only 11; 12 is disabled so 1 counts only the enabled leaf β†’ checked
160
+ const { checked, half } = conductRows(withDisabled, new Set([11]), getKey, getChildren, isDisabled);
161
+ expect(checked.has(1)).toBe(true);
162
+ expect(half.has(1)).toBe(false);
163
+ });
164
+ it('toggleRowCheck: toggling a parent adds/removes all leaves', () => {
165
+ const on = toggleRowCheck(data, new Set(), 1, getKey, getChildren);
166
+ expect([...on].sort((a, b) => Number(a) - Number(b))).toEqual([11, 121, 122]); // leaf-level base
167
+ const off = toggleRowCheck(data, on, 1, getKey, getChildren);
168
+ expect(off.size).toBe(0);
169
+ });
170
+ it('toggleRowCheck: round-trips conductRows output (parent keys)', () => {
171
+ // feed conduct output (contains parent keys) back through toggle
172
+ const { checked } = conductRows(data, new Set([1]), getKey, getChildren);
173
+ const off = toggleRowCheck(data, checked, 12, getKey, getChildren);
174
+ // toggling 12 off should remove its leaves only, keep 11
175
+ expect(off.has(11)).toBe(true);
176
+ expect(off.has(121)).toBe(false);
177
+ expect(off.has(122)).toBe(false);
178
+ });
179
+ it('normalizeRowsToLeaves: expands parents to leaves, drops unknown keys', () => {
180
+ const out = normalizeRowsToLeaves(data, new Set([1, 999]), getKey, getChildren);
181
+ expect([...out].sort((a, b) => Number(a) - Number(b))).toEqual([11, 121, 122]);
182
+ });
183
+ });
184
+ //# sourceMappingURL=table.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table.test.js","sourceRoot":"","sources":["../src/table.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * textarea β€” framework-agnostic autosize height math for <textarea>.
3
+ * Pure: given the measured scrollHeight (with box-sizing: border-box, i.e. the
4
+ * content height the browser reports) plus the row metrics, returns the height
5
+ * to apply and whether vertical scrolling is needed (maxRows reached). No DOM.
6
+ * The render layer measures scrollHeight and applies the result.
7
+ * See specs/components/input/Input.spec.md (TextArea autosize).
8
+ */
9
+ export interface AutosizeInput {
10
+ /** measured content height (px), e.g. textarea.scrollHeight with border-box */
11
+ scrollHeight: number;
12
+ /** single line height (px), from computed line-height */
13
+ lineHeight: number;
14
+ /** total vertical padding (top + bottom, px) */
15
+ verticalPadding: number;
16
+ /** total vertical border (top + bottom, px) */
17
+ verticalBorder: number;
18
+ /** minimum visible rows (default 1) */
19
+ minRows?: number;
20
+ /** maximum visible rows before scrolling (default Infinity) */
21
+ maxRows?: number;
22
+ }
23
+ export interface AutosizeResult {
24
+ /** height (px) to apply to the textarea (border-box) */
25
+ height: number;
26
+ /** whether content exceeds maxRows and vertical scroll is needed */
27
+ overflow: boolean;
28
+ }
29
+ /**
30
+ * Compute the autosize height. Clamps the content-driven height into
31
+ * [minRows, maxRows]; sets overflow=true only when the natural content exceeds
32
+ * the maxRows cap (so the caller can switch overflow-y to auto).
33
+ */
34
+ export declare function computeAutosizeHeight(input: AutosizeInput): AutosizeResult;
35
+ /**
36
+ * Count the visible characters of a string. Pure (no DOM).
37
+ *
38
+ * - `graphemes: false` (default) β†’ spread-based code-point count (`[...s].length`),
39
+ * matching the existing behaviour; zero extra cost.
40
+ * - `graphemes: true` β†’ counts user-perceived characters via `Intl.Segmenter`
41
+ * (correctly handles emoji, ZWJ sequences, combining marks, flags). Falls back
42
+ * to the code-point count when `Intl.Segmenter` is unavailable.
43
+ */
44
+ export declare function countCharacters(value: string, options?: {
45
+ graphemes?: boolean;
46
+ locale?: string;
47
+ }): number;
48
+ //# sourceMappingURL=textarea.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"textarea.d.ts","sourceRoot":"","sources":["../src/textarea.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,aAAa;IAC5B,+EAA+E;IAC/E,YAAY,EAAE,MAAM,CAAC;IACrB,yDAAyD;IACzD,UAAU,EAAE,MAAM,CAAC;IACnB,gDAAgD;IAChD,eAAe,EAAE,MAAM,CAAC;IACxB,+CAA+C;IAC/C,cAAc,EAAE,MAAM,CAAC;IACvB,uCAAuC;IACvC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,wDAAwD;IACxD,MAAM,EAAE,MAAM,CAAC;IACf,oEAAoE;IACpE,QAAQ,EAAE,OAAO,CAAC;CACnB;AAYD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,aAAa,GAAG,cAAc,CAiB1E;AAuBD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GACjD,MAAM,CAUR"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * textarea β€” framework-agnostic autosize height math for <textarea>.
3
+ * Pure: given the measured scrollHeight (with box-sizing: border-box, i.e. the
4
+ * content height the browser reports) plus the row metrics, returns the height
5
+ * to apply and whether vertical scrolling is needed (maxRows reached). No DOM.
6
+ * The render layer measures scrollHeight and applies the result.
7
+ * See specs/components/input/Input.spec.md (TextArea autosize).
8
+ */
9
+ /** border-box height for a given number of text rows. */
10
+ function heightForRows(rows, lineHeight, verticalPadding, verticalBorder) {
11
+ return rows * lineHeight + verticalPadding + verticalBorder;
12
+ }
13
+ /**
14
+ * Compute the autosize height. Clamps the content-driven height into
15
+ * [minRows, maxRows]; sets overflow=true only when the natural content exceeds
16
+ * the maxRows cap (so the caller can switch overflow-y to auto).
17
+ */
18
+ export function computeAutosizeHeight(input) {
19
+ const { scrollHeight, lineHeight, verticalPadding, verticalBorder } = input;
20
+ const minRows = input.minRows ?? 1;
21
+ const maxRows = input.maxRows ?? Number.POSITIVE_INFINITY;
22
+ const minHeight = heightForRows(minRows, lineHeight, verticalPadding, verticalBorder);
23
+ const maxHeight = Number.isFinite(maxRows)
24
+ ? heightForRows(maxRows, lineHeight, verticalPadding, verticalBorder)
25
+ : Number.POSITIVE_INFINITY;
26
+ // scrollHeight already includes padding (border-box reporting); it is the
27
+ // natural content height. Clamp it into [minHeight, maxHeight].
28
+ const natural = scrollHeight;
29
+ const overflow = natural > maxHeight;
30
+ const height = Math.min(Math.max(natural, minHeight), maxHeight);
31
+ return { height, overflow };
32
+ }
33
+ // --- character counting -----------------------------------------------------
34
+ /**
35
+ * Lazily-built, locale-keyed cache of `Intl.Segmenter` instances. The Segmenter
36
+ * is only constructed when grapheme counting is actually requested (perf budget:
37
+ * default length counting has zero extra cost). One instance per locale is reused.
38
+ */
39
+ const segmenterCache = new Map();
40
+ function getGraphemeSegmenter(locale) {
41
+ const key = locale ?? '';
42
+ if (segmenterCache.has(key))
43
+ return segmenterCache.get(key) ?? null;
44
+ let seg = null;
45
+ // Intl.Segmenter is widely supported but guard for older runtimes/SSR.
46
+ if (typeof Intl !== 'undefined' && typeof Intl.Segmenter === 'function') {
47
+ seg = new Intl.Segmenter(locale, { granularity: 'grapheme' });
48
+ }
49
+ segmenterCache.set(key, seg);
50
+ return seg;
51
+ }
52
+ /**
53
+ * Count the visible characters of a string. Pure (no DOM).
54
+ *
55
+ * - `graphemes: false` (default) β†’ spread-based code-point count (`[...s].length`),
56
+ * matching the existing behaviour; zero extra cost.
57
+ * - `graphemes: true` β†’ counts user-perceived characters via `Intl.Segmenter`
58
+ * (correctly handles emoji, ZWJ sequences, combining marks, flags). Falls back
59
+ * to the code-point count when `Intl.Segmenter` is unavailable.
60
+ */
61
+ export function countCharacters(value, options) {
62
+ if (!value)
63
+ return 0;
64
+ if (!options?.graphemes)
65
+ return [...value].length;
66
+ const seg = getGraphemeSegmenter(options.locale);
67
+ if (!seg)
68
+ return [...value].length;
69
+ let n = 0;
70
+ // Iterate segments; the iterator yields one entry per grapheme cluster.
71
+ const iter = seg.segment(value)[Symbol.iterator]();
72
+ while (!iter.next().done)
73
+ n++;
74
+ return n;
75
+ }
76
+ //# sourceMappingURL=textarea.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"textarea.js","sourceRoot":"","sources":["../src/textarea.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAwBH,yDAAyD;AACzD,SAAS,aAAa,CACpB,IAAY,EACZ,UAAkB,EAClB,eAAuB,EACvB,cAAsB;IAEtB,OAAO,IAAI,GAAG,UAAU,GAAG,eAAe,GAAG,cAAc,CAAC;AAC9D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAAoB;IACxD,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,cAAc,EAAE,GAAG,KAAK,CAAC;IAC5E,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC;IACnC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,MAAM,CAAC,iBAAiB,CAAC;IAE1D,MAAM,SAAS,GAAG,aAAa,CAAC,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,cAAc,CAAC,CAAC;IACtF,MAAM,SAAS,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC;QACxC,CAAC,CAAC,aAAa,CAAC,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,cAAc,CAAC;QACrE,CAAC,CAAC,MAAM,CAAC,iBAAiB,CAAC;IAE7B,0EAA0E;IAC1E,gEAAgE;IAChE,MAAM,OAAO,GAAG,YAAY,CAAC;IAC7B,MAAM,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;IACrC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,SAAS,CAAC,CAAC;IAEjE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;AAC9B,CAAC;AAED,+EAA+E;AAE/E;;;;GAIG;AACH,MAAM,cAAc,GAAG,IAAI,GAAG,EAAiC,CAAC;AAEhE,SAAS,oBAAoB,CAAC,MAAe;IAC3C,MAAM,GAAG,GAAG,MAAM,IAAI,EAAE,CAAC;IACzB,IAAI,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAO,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;IACpE,IAAI,GAAG,GAA0B,IAAI,CAAC;IACtC,uEAAuE;IACvE,IAAI,OAAO,IAAI,KAAK,WAAW,IAAI,OAAO,IAAI,CAAC,SAAS,KAAK,UAAU,EAAE,CAAC;QACxE,GAAG,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC,CAAC;IAChE,CAAC;IACD,cAAc,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IAC7B,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,eAAe,CAC7B,KAAa,EACb,OAAkD;IAElD,IAAI,CAAC,KAAK;QAAE,OAAO,CAAC,CAAC;IACrB,IAAI,CAAC,OAAO,EAAE,SAAS;QAAE,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC;IAClD,MAAM,GAAG,GAAG,oBAAoB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACjD,IAAI,CAAC,GAAG;QAAE,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC;IACnC,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,wEAAwE;IACxE,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;IACnD,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,IAAI;QAAE,CAAC,EAAE,CAAC;IAC9B,OAAO,CAAC,CAAC;AACX,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=textarea.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"textarea.test.d.ts","sourceRoot":"","sources":["../src/textarea.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,77 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { computeAutosizeHeight, countCharacters } from './textarea.js';
3
+ // row metrics: line 20px, padding 8px (4+4), border 2px (1+1).
4
+ // 1 row = 20 + 8 + 2 = 30; 3 rows = 60 + 10 = 70; 5 rows = 100 + 10 = 110.
5
+ const metrics = { lineHeight: 20, verticalPadding: 8, verticalBorder: 2 };
6
+ describe('computeAutosizeHeight', () => {
7
+ it('grows to fit content between min and max', () => {
8
+ const r = computeAutosizeHeight({ ...metrics, scrollHeight: 70, minRows: 1, maxRows: 5 });
9
+ expect(r.height).toBe(70); // 3 rows worth of content
10
+ expect(r.overflow).toBe(false);
11
+ });
12
+ it('clamps up to minRows when content is shorter', () => {
13
+ // content 30px (1 row) but minRows 2 β†’ 2 rows = 40 + 10 = 50
14
+ const r = computeAutosizeHeight({ ...metrics, scrollHeight: 30, minRows: 2, maxRows: 6 });
15
+ expect(r.height).toBe(50);
16
+ expect(r.overflow).toBe(false);
17
+ });
18
+ it('caps at maxRows and flags overflow when content exceeds it', () => {
19
+ // content 200px but maxRows 5 β†’ cap 110, overflow true
20
+ const r = computeAutosizeHeight({ ...metrics, scrollHeight: 200, minRows: 1, maxRows: 5 });
21
+ expect(r.height).toBe(110);
22
+ expect(r.overflow).toBe(true);
23
+ });
24
+ it('does not overflow when content exactly fills maxRows', () => {
25
+ // exactly 5 rows = 110
26
+ const r = computeAutosizeHeight({ ...metrics, scrollHeight: 110, minRows: 1, maxRows: 5 });
27
+ expect(r.height).toBe(110);
28
+ expect(r.overflow).toBe(false);
29
+ });
30
+ it('defaults minRows=1 and maxRows=Infinity (no cap, never overflow)', () => {
31
+ const r = computeAutosizeHeight({ ...metrics, scrollHeight: 1000 });
32
+ expect(r.height).toBe(1000);
33
+ expect(r.overflow).toBe(false);
34
+ });
35
+ it('respects minRows even with default (Infinity) maxRows', () => {
36
+ const r = computeAutosizeHeight({ ...metrics, scrollHeight: 20, minRows: 3 });
37
+ expect(r.height).toBe(70); // 3 rows
38
+ });
39
+ });
40
+ describe('countCharacters', () => {
41
+ it('returns 0 for empty string', () => {
42
+ expect(countCharacters('')).toBe(0);
43
+ expect(countCharacters('', { graphemes: true })).toBe(0);
44
+ });
45
+ it('counts ASCII identically in both modes', () => {
46
+ expect(countCharacters('hello')).toBe(5);
47
+ expect(countCharacters('hello', { graphemes: true })).toBe(5);
48
+ });
49
+ it('counts CJK by code point in default mode', () => {
50
+ expect(countCharacters('δ½ ε₯½δΈ–η•Œ')).toBe(4);
51
+ });
52
+ it('default mode counts a single emoji by code point (spread)', () => {
53
+ // πŸ˜€ is one code point β†’ spread length 1
54
+ expect(countCharacters('πŸ˜€')).toBe(1);
55
+ });
56
+ it('default mode splits ZWJ emoji into multiple code points', () => {
57
+ // πŸ‘¨β€πŸ‘©β€πŸ‘§ = man + ZWJ + woman + ZWJ + girl β†’ 5 code points via spread
58
+ expect(countCharacters('πŸ‘¨β€πŸ‘©β€πŸ‘§')).toBe(5);
59
+ });
60
+ it('grapheme mode counts a ZWJ family emoji as one character', () => {
61
+ expect(countCharacters('πŸ‘¨β€πŸ‘©β€πŸ‘§', { graphemes: true })).toBe(1);
62
+ });
63
+ it('grapheme mode counts a flag emoji as one character', () => {
64
+ // πŸ‡¨πŸ‡³ is two regional-indicator code points β†’ 1 grapheme
65
+ expect(countCharacters('πŸ‡¨πŸ‡³', { graphemes: true })).toBe(1);
66
+ });
67
+ it('grapheme mode counts combining marks as one character', () => {
68
+ // 'e' + combining acute accent β†’ 1 grapheme, 2 code points
69
+ expect(countCharacters('é')).toBe(2);
70
+ expect(countCharacters('é', { graphemes: true })).toBe(1);
71
+ });
72
+ it('grapheme mode counts mixed text correctly', () => {
73
+ // a + family emoji + b β†’ 3 graphemes
74
+ expect(countCharacters('aπŸ‘¨β€πŸ‘©β€πŸ‘§b', { graphemes: true })).toBe(3);
75
+ });
76
+ });
77
+ //# sourceMappingURL=textarea.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"textarea.test.js","sourceRoot":"","sources":["../src/textarea.test.ts"],"names":[],"mappings":"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"}
package/dist/time.d.ts ADDED
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Framework-agnostic time-column utilities for TimePicker.
3
+ * No DOM, no framework deps. See specs/components/input/TimePicker.spec.md Β§3/Β§4.
4
+ * Pure column math only β€” display formatting is the render layer's job (Intl).
5
+ */
6
+ /** AM/PM meridiem in 12-hour mode. */
7
+ export type Meridiem = 'am' | 'pm';
8
+ /** A single selectable option in a time column. */
9
+ export interface TimeOption {
10
+ /** the underlying numeric value (hour 0-23 / 1-12, minute/second 0-59) */
11
+ value: number;
12
+ /** true when the value is disabled (placed by disabledXxx) */
13
+ disabled: boolean;
14
+ }
15
+ /** Disabled-providers passed straight through from the component props. */
16
+ export interface DisabledTime {
17
+ disabledHours?: () => number[];
18
+ disabledMinutes?: (hour: number) => number[];
19
+ disabledSeconds?: (hour: number, minute: number) => number[];
20
+ }
21
+ /**
22
+ * Build a stepped numeric range `[0, max)`.
23
+ * step ≀ 0 is treated as 1.
24
+ */
25
+ export declare function buildRange(max: number, step: number): number[];
26
+ /** Convert a 24-hour hour (0-23) to its 12-hour display hour (1-12). */
27
+ export declare function to12Hour(hour24: number): number;
28
+ /** Derive the meridiem ('am' | 'pm') of a 24-hour hour. */
29
+ export declare function meridiemOf(hour24: number): Meridiem;
30
+ /**
31
+ * Convert a 12-hour display hour (1-12) + meridiem back to a 24-hour hour (0-23).
32
+ * Boundary rules: 12 AM β†’ 0, 12 PM β†’ 12.
33
+ */
34
+ export declare function from12Hour(hour12: number, meridiem: Meridiem): number;
35
+ /**
36
+ * Build the hour column options.
37
+ * 24h mode: stepped values 0-23.
38
+ * 12h mode: stepped values 1-12 (uses `to12Hour`/`from12Hour` semantics; value is the display hour).
39
+ * `disabledHours` returns 24-hour numbers; in 12h mode it is mapped via the active meridiem.
40
+ */
41
+ export declare function buildHourOptions(step: number, use12Hours: boolean, meridiem: Meridiem, disabledHours?: () => number[]): TimeOption[];
42
+ /** Build the minute column options for a given (24-hour) hour. */
43
+ export declare function buildMinuteOptions(step: number, hour24: number, disabledMinutes?: (hour: number) => number[]): TimeOption[];
44
+ /** Build the second column options for a given (24-hour) hour + minute. */
45
+ export declare function buildSecondOptions(step: number, hour24: number, minute: number, disabledSeconds?: (hour: number, minute: number) => number[]): TimeOption[];
46
+ /**
47
+ * Drop disabled options when `hideDisabledOptions` is on; otherwise keep them all.
48
+ * Pure: returns a new array.
49
+ */
50
+ export declare function applyHideDisabled(options: TimeOption[], hideDisabledOptions: boolean): TimeOption[];
51
+ /** true if the given 24-hour time is (partially) disabled by any provider. */
52
+ export declare function isTimeDisabled(hour24: number, minute: number, second: number, d: DisabledTime): boolean;
53
+ /**
54
+ * A simple time triple in 24-hour internal representation.
55
+ * Shared by the format string parse/serialize helpers below.
56
+ */
57
+ export interface TimeParts {
58
+ hour: number;
59
+ minute: number;
60
+ second: number;
61
+ }
62
+ /**
63
+ * Which columns a `format` string drives, plus whether AM/PM (12h) is requested.
64
+ * Derived purely from the token string β€” the render layer uses this to decide
65
+ * which columns to show instead of the boolean `showSecond` / `use12Hours` props.
66
+ */
67
+ export interface FormatSpec {
68
+ showHour: boolean;
69
+ showMinute: boolean;
70
+ showSecond: boolean;
71
+ use12Hours: boolean;
72
+ }
73
+ /**
74
+ * Parse a format string (e.g. 'HH:mm', 'hh:mm A', 'HH:mm:ss') into a column spec.
75
+ * Recognized tokens: H/HH (24h hour), h/hh (12h hour), m/mm (minute), s/ss (second),
76
+ * A/a (AM/PM meridiem). Lowercase `h` or an `A`/`a` token implies 12-hour mode.
77
+ * Tokens inside any other characters (':', ' ', etc.) are treated as literals.
78
+ */
79
+ export declare function parseFormatSpec(format: string): FormatSpec;
80
+ /**
81
+ * Serialize a time triple to a string per the given format.
82
+ * Token semantics: HH/H 24h hour (HH zero-padded), hh/h 12h hour, mm/m minute,
83
+ * ss/s second, A β†’ 'AM'/'PM', a β†’ 'am'/'pm'. Unrecognized characters pass through.
84
+ */
85
+ export declare function formatTime(parts: TimeParts, format: string): string;
86
+ /**
87
+ * Parse a time string into a 24-hour {hour,minute,second} triple, or null when
88
+ * unparseable. Tolerant: accepts 'HH:mm', 'HH:mm:ss', optional leading zeros, and
89
+ * a trailing AM/PM marker (case-insensitive, e.g. '12:30 pm', '1:05:09AM').
90
+ * When a meridiem is present the leading number is treated as a 12-hour hour.
91
+ */
92
+ export declare function parseTimeString(input: string): TimeParts | null;
93
+ //# sourceMappingURL=time.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time.d.ts","sourceRoot":"","sources":["../src/time.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,sCAAsC;AACtC,MAAM,MAAM,QAAQ,GAAG,IAAI,GAAG,IAAI,CAAC;AAEnC,mDAAmD;AACnD,MAAM,WAAW,UAAU;IACzB,0EAA0E;IAC1E,KAAK,EAAE,MAAM,CAAC;IACd,8DAA8D;IAC9D,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,2EAA2E;AAC3E,MAAM,WAAW,YAAY;IAC3B,aAAa,CAAC,EAAE,MAAM,MAAM,EAAE,CAAC;IAC/B,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;IAC7C,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;CAC9D;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAK9D;AAED,wEAAwE;AACxE,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAI/C;AAED,2DAA2D;AAC3D,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAGnD;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAIrE;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,OAAO,EACnB,QAAQ,EAAE,QAAQ,EAClB,aAAa,CAAC,EAAE,MAAM,MAAM,EAAE,GAC7B,UAAU,EAAE,CAYd;AAED,kEAAkE;AAClE,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,EAAE,GAC3C,UAAU,EAAE,CAGd;AAED,2EAA2E;AAC3E,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,MAAM,EAAE,GAC3D,UAAU,EAAE,CAGd;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,UAAU,EAAE,EAAE,mBAAmB,EAAE,OAAO,GAAG,UAAU,EAAE,CAEnG;AAED,8EAA8E;AAC9E,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,YAAY,GAAG,OAAO,CAKvG;AAED;;;GAGG;AACH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CA8B1D;AAMD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAwBnE;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAgB/D"}