@svgrid/grid 1.2.23 → 1.2.25

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 (141) hide show
  1. package/dist/SvAutoComplete.svelte +23 -34
  2. package/dist/SvCalendar.svelte +74 -334
  3. package/dist/SvCalendar.svelte.d.ts +5 -23
  4. package/dist/SvCheckBox.svelte +11 -10
  5. package/dist/SvColorInput.svelte +31 -58
  6. package/dist/SvComboBox.svelte +32 -54
  7. package/dist/SvCountryInput.svelte +31 -29
  8. package/dist/SvDateTimePicker.svelte +53 -110
  9. package/dist/SvDateTimePicker.svelte.d.ts +4 -3
  10. package/dist/SvDropDownList.svelte +31 -54
  11. package/dist/SvGauge.svelte +21 -23
  12. package/dist/SvGauge.svelte.d.ts +0 -5
  13. package/dist/SvListBox.svelte +25 -58
  14. package/dist/SvListBox.svelte.d.ts +3 -0
  15. package/dist/SvMaskedInput.svelte +0 -0
  16. package/dist/SvNumberInput.svelte +0 -0
  17. package/dist/SvNumberInput.svelte.d.ts +2 -6
  18. package/dist/SvPasswordInput.svelte +18 -28
  19. package/dist/SvPasswordInput.svelte.d.ts +0 -4
  20. package/dist/SvPhoneInput.svelte +0 -0
  21. package/dist/SvRadioGroup.svelte +14 -37
  22. package/dist/SvRadioGroup.svelte.d.ts +6 -9
  23. package/dist/SvRating.svelte +22 -42
  24. package/dist/SvRating.svelte.d.ts +3 -5
  25. package/dist/SvSlider.svelte +30 -57
  26. package/dist/SvSlider.svelte.d.ts +0 -5
  27. package/dist/SvSwitchButton.svelte +10 -17
  28. package/dist/SvSwitchButton.svelte.d.ts +0 -5
  29. package/dist/SvTabs.svelte +26 -39
  30. package/dist/SvTabs.svelte.d.ts +5 -5
  31. package/dist/SvTagsInput.svelte +17 -28
  32. package/dist/SvTagsInput.svelte.d.ts +0 -5
  33. package/dist/SvTimePicker.svelte +52 -210
  34. package/dist/SvTimePicker.svelte.d.ts +11 -8
  35. package/dist/SvToggleButton.svelte +9 -9
  36. package/dist/SvTree.svelte +30 -109
  37. package/dist/SvTree.svelte.d.ts +9 -11
  38. package/dist/cdn/svgrid.js +7488 -5839
  39. package/dist/cdn/svgrid.svelte-external.js +4691 -3028
  40. package/dist/createAutocomplete.svelte.d.ts +77 -0
  41. package/dist/createAutocomplete.svelte.js +100 -0
  42. package/dist/createCalendar.svelte.d.ts +192 -0
  43. package/dist/createCalendar.svelte.js +402 -0
  44. package/dist/createCheckbox.svelte.d.ts +45 -0
  45. package/dist/createCheckbox.svelte.js +26 -0
  46. package/dist/createColorInput.svelte.d.ts +67 -0
  47. package/dist/createColorInput.svelte.js +100 -0
  48. package/dist/createCombobox.svelte.d.ts +102 -0
  49. package/dist/createCombobox.svelte.js +171 -0
  50. package/dist/createCountryInput.svelte.d.ts +86 -0
  51. package/dist/createCountryInput.svelte.js +126 -0
  52. package/dist/createDateTimePicker.svelte.d.ts +83 -0
  53. package/dist/createDateTimePicker.svelte.js +202 -0
  54. package/dist/createDropdownList.svelte.d.ts +78 -0
  55. package/dist/createDropdownList.svelte.js +117 -0
  56. package/dist/createGauge.svelte.d.ts +56 -0
  57. package/dist/createGauge.svelte.js +49 -0
  58. package/dist/createListbox.svelte.d.ts +79 -0
  59. package/dist/createListbox.svelte.js +117 -0
  60. package/dist/createMaskedInput.svelte.d.ts +30 -0
  61. package/dist/createMaskedInput.svelte.js +62 -0
  62. package/dist/createNumberInput.svelte.d.ts +72 -0
  63. package/dist/createNumberInput.svelte.js +167 -0
  64. package/dist/createPasswordInput.svelte.d.ts +60 -0
  65. package/dist/createPasswordInput.svelte.js +72 -0
  66. package/dist/createPhoneInput.svelte.d.ts +46 -0
  67. package/dist/createPhoneInput.svelte.js +105 -0
  68. package/dist/createRadioGroup.svelte.d.ts +64 -0
  69. package/dist/createRadioGroup.svelte.js +73 -0
  70. package/dist/createRating.svelte.d.ts +70 -0
  71. package/dist/createRating.svelte.js +85 -0
  72. package/dist/createSlider.svelte.d.ts +71 -0
  73. package/dist/createSlider.svelte.js +123 -0
  74. package/dist/createSwitch.svelte.d.ts +43 -0
  75. package/dist/createSwitch.svelte.js +41 -0
  76. package/dist/createTabs.svelte.d.ts +78 -0
  77. package/dist/createTabs.svelte.js +92 -0
  78. package/dist/createTagsInput.svelte.d.ts +65 -0
  79. package/dist/createTagsInput.svelte.js +93 -0
  80. package/dist/createTimePicker.svelte.d.ts +106 -0
  81. package/dist/createTimePicker.svelte.js +248 -0
  82. package/dist/createToggle.svelte.d.ts +43 -0
  83. package/dist/createToggle.svelte.js +39 -0
  84. package/dist/createTree.svelte.d.ts +96 -0
  85. package/dist/createTree.svelte.js +194 -0
  86. package/dist/editor-contract.d.ts +48 -0
  87. package/dist/editor-contract.js +26 -0
  88. package/dist/index.d.ts +24 -0
  89. package/dist/index.js +31 -0
  90. package/package.json +1 -1
  91. package/src/SvAutoComplete.svelte +23 -34
  92. package/src/SvCalendar.svelte +74 -334
  93. package/src/SvCheckBox.svelte +11 -10
  94. package/src/SvColorInput.svelte +31 -58
  95. package/src/SvComboBox.svelte +32 -54
  96. package/src/SvCountryInput.svelte +31 -29
  97. package/src/SvDateTimePicker.svelte +53 -110
  98. package/src/SvDropDownList.svelte +31 -54
  99. package/src/SvGauge.svelte +21 -23
  100. package/src/SvListBox.svelte +25 -58
  101. package/src/SvMaskedInput.svelte +0 -0
  102. package/src/SvNumberInput.svelte +0 -0
  103. package/src/SvPasswordInput.svelte +18 -28
  104. package/src/SvPhoneInput.svelte +0 -0
  105. package/src/SvRadioGroup.svelte +14 -37
  106. package/src/SvRating.svelte +22 -42
  107. package/src/SvSlider.svelte +30 -57
  108. package/src/SvSwitchButton.svelte +10 -17
  109. package/src/SvTabs.svelte +26 -39
  110. package/src/SvTagsInput.svelte +17 -28
  111. package/src/SvTimePicker.svelte +52 -210
  112. package/src/SvToggleButton.svelte +9 -9
  113. package/src/SvTree.svelte +30 -109
  114. package/src/createAutocomplete.svelte.ts +104 -0
  115. package/src/createCalendar.svelte.ts +488 -0
  116. package/src/createCheckbox.svelte.ts +74 -0
  117. package/src/createColorInput.svelte.ts +115 -0
  118. package/src/createCombobox.svelte.ts +158 -0
  119. package/src/createCountryInput.svelte.ts +126 -0
  120. package/src/createDateTimePicker.svelte.ts +216 -0
  121. package/src/createDropdownList.svelte.ts +125 -0
  122. package/src/createGauge.svelte.ts +86 -0
  123. package/src/createListbox.svelte.ts +186 -0
  124. package/src/createMaskedInput.svelte.ts +81 -0
  125. package/src/createNumberInput.svelte.ts +193 -0
  126. package/src/createPasswordInput.svelte.ts +87 -0
  127. package/src/createPhoneInput.svelte.ts +119 -0
  128. package/src/createRadioGroup.svelte.ts +132 -0
  129. package/src/createRating.svelte.ts +145 -0
  130. package/src/createSlider.svelte.ts +160 -0
  131. package/src/createSwitch.svelte.ts +78 -0
  132. package/src/createTabs.svelte.ts +129 -0
  133. package/src/createTagsInput.svelte.ts +98 -0
  134. package/src/createTimePicker.svelte.ts +289 -0
  135. package/src/createToggle.svelte.ts +69 -0
  136. package/src/createTree.svelte.ts +216 -0
  137. package/src/editor-contract.test.ts +32 -0
  138. package/src/editor-contract.ts +60 -0
  139. package/src/index.ts +39 -0
  140. package/src/ui-inputs.test.ts +26 -0
  141. package/src/ui-selection.test.ts +13 -0
@@ -1,10 +1,5 @@
1
1
  <script lang="ts" module>
2
- export type TreeNode = {
3
- id: string
4
- label: string
5
- children?: TreeNode[]
6
- disabled?: boolean
7
- }
2
+ export type { TreeNode } from './createTree.svelte'
8
3
  </script>
9
4
 
10
5
  <script lang="ts">
@@ -12,8 +7,12 @@
12
7
  * SvTree - a WAI-ARIA tree view. Expand/collapse, single-select highlight, and
13
8
  * optional cascading tri-state checkboxes. Parity: Smart `smart-tree`. Keyboard:
14
9
  * up/down move, left collapse/parent, right expand/child, Enter/Space select.
15
- * The visible tree is flattened so roving-focus keyboard nav stays simple.
10
+ *
11
+ * The behavior (flattening, cascade math, keyboard, ARIA) lives in the headless
12
+ * `createTree` core; this component is just one styled renderer over it.
16
13
  */
14
+ import { createTree, type TreeNode } from './createTree.svelte'
15
+
17
16
  type Props = {
18
17
  nodes: ReadonlyArray<TreeNode>
19
18
  /** Selected node id (single-select highlight). */
@@ -41,117 +40,39 @@
41
40
  ariaLabel,
42
41
  }: Props = $props()
43
42
 
44
- // Internal expanded set, seeded from the prop once (then self-managed unless
45
- // the prop identity changes).
46
- let expanded = $state<Set<string>>(new Set())
47
- let seededExp = false
48
- $effect(() => {
49
- if (!seededExp) { seededExp = true; if (expandedIds) expanded = new Set(expandedIds) }
43
+ const tree = createTree({
44
+ nodes: () => nodes,
45
+ selected: () => selected,
46
+ onSelect: (id) => onSelect?.(id),
47
+ expandedIds: () => expandedIds,
48
+ onToggle: (id, exp) => onToggle?.(id, exp),
49
+ checkable: () => checkable,
50
+ checked: () => checked,
51
+ onCheck: (ids) => onCheck?.(ids),
52
+ ariaLabel: () => ariaLabel,
50
53
  })
51
54
 
52
- type Flat = { node: TreeNode; depth: number; hasChildren: boolean; open: boolean; parentId: string | null }
53
-
54
- const flat = $derived.by<Flat[]>(() => {
55
- const out: Flat[] = []
56
- const walk = (list: ReadonlyArray<TreeNode>, depth: number, parentId: string | null) => {
57
- for (const node of list) {
58
- const hasChildren = !!node.children?.length
59
- const open = expanded.has(node.id)
60
- out.push({ node, depth, hasChildren, open, parentId })
61
- if (hasChildren && open) walk(node.children!, depth + 1, node.id)
62
- }
55
+ // DOM focus movement is a render concern: follow the core's roving focus row.
56
+ let treeEl: HTMLDivElement | null = null
57
+ let lastFocusTick = 0
58
+ $effect(() => {
59
+ if (tree.focusTick !== lastFocusTick) {
60
+ lastFocusTick = tree.focusTick
61
+ const i = tree.activeIndex
62
+ queueMicrotask(() => treeEl?.querySelector<HTMLElement>(`[data-row="${i}"]`)?.focus())
63
63
  }
64
- walk(nodes, 0, null)
65
- return out
66
64
  })
67
-
68
- let active = $state(0)
69
- $effect(() => { if (active >= flat.length) active = Math.max(0, flat.length - 1) })
70
-
71
- const checkedSet = $derived(new Set(checked))
72
-
73
- function descendantIds(node: TreeNode): string[] {
74
- const ids: string[] = []
75
- const walk = (n: TreeNode) => { for (const c of n.children ?? []) { ids.push(c.id); walk(c) } }
76
- walk(node)
77
- return ids
78
- }
79
- function checkState(node: TreeNode): 'checked' | 'indeterminate' | 'unchecked' {
80
- if (!node.children?.length) return checkedSet.has(node.id) ? 'checked' : 'unchecked'
81
- const desc = descendantIds(node)
82
- const on = desc.filter((id) => checkedSet.has(id)).length
83
- if (on === 0 && !checkedSet.has(node.id)) return 'unchecked'
84
- if (on === desc.length) return 'checked'
85
- return 'indeterminate'
86
- }
87
-
88
- function toggleExpand(node: TreeNode) {
89
- if (!node.children?.length) return
90
- const next = new Set(expanded)
91
- const willOpen = !next.has(node.id)
92
- willOpen ? next.add(node.id) : next.delete(node.id)
93
- expanded = next
94
- onToggle?.(node.id, willOpen)
95
- }
96
- function select(node: TreeNode) {
97
- if (node.disabled) return
98
- onSelect?.(node.id)
99
- }
100
- function toggleCheck(node: TreeNode) {
101
- if (node.disabled) return
102
- const next = new Set(checkedSet)
103
- const ids = [node.id, ...descendantIds(node)]
104
- const currentlyOn = checkState(node) === 'checked'
105
- for (const id of ids) currentlyOn ? next.delete(id) : next.add(id)
106
- onCheck?.([...next])
107
- }
108
-
109
- function onKeydown(e: KeyboardEvent) {
110
- const item = flat[active]
111
- if (!item) return
112
- switch (e.key) {
113
- case 'ArrowDown': e.preventDefault(); active = Math.min(active + 1, flat.length - 1); focusActive(); break
114
- case 'ArrowUp': e.preventDefault(); active = Math.max(active - 1, 0); focusActive(); break
115
- case 'ArrowRight':
116
- e.preventDefault()
117
- if (item.hasChildren && !item.open) toggleExpand(item.node)
118
- else if (item.hasChildren && item.open) { active = Math.min(active + 1, flat.length - 1); focusActive() }
119
- break
120
- case 'ArrowLeft':
121
- e.preventDefault()
122
- if (item.hasChildren && item.open) toggleExpand(item.node)
123
- else if (item.parentId) { const pi = flat.findIndex((f) => f.node.id === item.parentId); if (pi >= 0) { active = pi; focusActive() } }
124
- break
125
- case 'Enter': e.preventDefault(); select(item.node); break
126
- case ' ': e.preventDefault(); checkable ? toggleCheck(item.node) : select(item.node); break
127
- case 'Home': e.preventDefault(); active = 0; focusActive(); break
128
- case 'End': e.preventDefault(); active = flat.length - 1; focusActive(); break
129
- }
130
- }
131
-
132
- let treeEl: HTMLDivElement | null = null
133
- function focusActive() {
134
- queueMicrotask(() => treeEl?.querySelector<HTMLElement>(`[data-row="${active}"]`)?.focus())
135
- }
136
65
  </script>
137
66
 
138
- <div bind:this={treeEl} class="sv-tree" role="tree" aria-label={ariaLabel} aria-multiselectable={checkable}>
139
- {#each flat as item, i (item.node.id)}
140
- {@const cs = checkable ? checkState(item.node) : 'unchecked'}
67
+ <div bind:this={treeEl} class="sv-tree" {...tree.treeProps()}>
68
+ {#each tree.rows as item (item.node.id)}
69
+ {@const cs = checkable ? tree.checkStateOf(item.node) : 'unchecked'}
141
70
  <div
142
71
  class="sv-tree__row"
143
72
  class:is-selected={item.node.id === selected}
144
73
  class:is-disabled={item.node.disabled}
145
- role="treeitem"
146
- aria-level={item.depth + 1}
147
- aria-selected={item.node.id === selected}
148
- aria-expanded={item.hasChildren ? item.open : undefined}
149
- aria-checked={checkable ? cs === 'checked' ? 'true' : cs === 'indeterminate' ? 'mixed' : 'false' : undefined}
150
- data-row={i}
151
- tabindex={i === active ? 0 : -1}
152
74
  style:padding-left={`${item.depth * 18 + 6}px`}
153
- onclick={() => { active = i; select(item.node) }}
154
- onkeydown={onKeydown}
75
+ {...tree.itemProps(item)}
155
76
  >
156
77
  <button
157
78
  type="button"
@@ -160,14 +81,14 @@
160
81
  class:is-leaf={!item.hasChildren}
161
82
  tabindex="-1"
162
83
  aria-hidden={!item.hasChildren}
163
- onclick={(e) => { e.stopPropagation(); toggleExpand(item.node) }}
84
+ onclick={(e) => { e.stopPropagation(); tree.toggleExpand(item.node) }}
164
85
  >
165
86
  {#if item.hasChildren}
166
87
  <svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6" /></svg>
167
88
  {/if}
168
89
  </button>
169
90
  {#if checkable}
170
- <button type="button" class="sv-tree__check is-{cs}" tabindex="-1" aria-hidden="true" onclick={(e) => { e.stopPropagation(); toggleCheck(item.node) }}>
91
+ <button type="button" class="sv-tree__check is-{cs}" tabindex="-1" aria-hidden="true" onclick={(e) => { e.stopPropagation(); tree.toggleCheck(item.node) }}>
171
92
  {#if cs === 'checked'}<svg viewBox="0 0 16 16"><path d="M3.5 8.5l3 3 6-6.5" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" /></svg>
172
93
  {:else if cs === 'indeterminate'}<svg viewBox="0 0 16 16"><path d="M4 8h8" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" /></svg>{/if}
173
94
  </button>
@@ -1,15 +1,13 @@
1
- export type TreeNode = {
2
- id: string;
3
- label: string;
4
- children?: TreeNode[];
5
- disabled?: boolean;
6
- };
1
+ export type { TreeNode } from './createTree.svelte';
7
2
  /**
8
- * SvTree - a WAI-ARIA tree view. Expand/collapse, single-select highlight, and
9
- * optional cascading tri-state checkboxes. Parity: Smart `smart-tree`. Keyboard:
10
- * up/down move, left collapse/parent, right expand/child, Enter/Space select.
11
- * The visible tree is flattened so roving-focus keyboard nav stays simple.
12
- */
3
+ * SvTree - a WAI-ARIA tree view. Expand/collapse, single-select highlight, and
4
+ * optional cascading tri-state checkboxes. Parity: Smart `smart-tree`. Keyboard:
5
+ * up/down move, left collapse/parent, right expand/child, Enter/Space select.
6
+ *
7
+ * The behavior (flattening, cascade math, keyboard, ARIA) lives in the headless
8
+ * `createTree` core; this component is just one styled renderer over it.
9
+ */
10
+ import { type TreeNode } from './createTree.svelte';
13
11
  type Props = {
14
12
  nodes: ReadonlyArray<TreeNode>;
15
13
  /** Selected node id (single-select highlight). */