@proyecto-viviana/solidaria 0.2.2 → 0.2.4

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 (210) hide show
  1. package/dist/autocomplete/createAutocomplete.d.ts +2 -2
  2. package/dist/autocomplete/createAutocomplete.d.ts.map +1 -1
  3. package/dist/index.js +233 -234
  4. package/dist/index.js.map +2 -2
  5. package/dist/index.ssr.js +233 -234
  6. package/dist/index.ssr.js.map +2 -2
  7. package/dist/interactions/PressEvent.d.ts +13 -10
  8. package/dist/interactions/PressEvent.d.ts.map +1 -1
  9. package/dist/interactions/createPress.d.ts.map +1 -1
  10. package/dist/interactions/index.d.ts +1 -1
  11. package/dist/interactions/index.d.ts.map +1 -1
  12. package/dist/select/createHiddenSelect.d.ts.map +1 -1
  13. package/dist/toolbar/createToolbar.d.ts.map +1 -1
  14. package/dist/tooltip/createTooltipTrigger.d.ts.map +1 -1
  15. package/package.json +9 -7
  16. package/src/autocomplete/createAutocomplete.ts +341 -0
  17. package/src/autocomplete/index.ts +9 -0
  18. package/src/breadcrumbs/createBreadcrumbs.ts +196 -0
  19. package/src/breadcrumbs/index.ts +8 -0
  20. package/src/button/createButton.ts +142 -0
  21. package/src/button/createToggleButton.ts +101 -0
  22. package/src/button/index.ts +4 -0
  23. package/src/button/types.ts +78 -0
  24. package/src/calendar/createCalendar.ts +138 -0
  25. package/src/calendar/createCalendarCell.ts +187 -0
  26. package/src/calendar/createCalendarGrid.ts +140 -0
  27. package/src/calendar/createRangeCalendar.ts +136 -0
  28. package/src/calendar/createRangeCalendarCell.ts +186 -0
  29. package/src/calendar/index.ts +34 -0
  30. package/src/checkbox/createCheckbox.ts +135 -0
  31. package/src/checkbox/createCheckboxGroup.ts +137 -0
  32. package/src/checkbox/createCheckboxGroupItem.ts +117 -0
  33. package/src/checkbox/createCheckboxGroupState.ts +193 -0
  34. package/src/checkbox/index.ts +13 -0
  35. package/src/color/createColorArea.ts +314 -0
  36. package/src/color/createColorField.ts +137 -0
  37. package/src/color/createColorSlider.ts +197 -0
  38. package/src/color/createColorSwatch.ts +40 -0
  39. package/src/color/createColorWheel.ts +208 -0
  40. package/src/color/index.ts +24 -0
  41. package/src/color/types.ts +116 -0
  42. package/src/combobox/createComboBox.ts +647 -0
  43. package/src/combobox/index.ts +6 -0
  44. package/src/combobox/intl/en-US.json +7 -0
  45. package/src/combobox/intl/es-ES.json +7 -0
  46. package/src/combobox/intl/index.ts +23 -0
  47. package/src/datepicker/createDateField.ts +154 -0
  48. package/src/datepicker/createDatePicker.ts +206 -0
  49. package/src/datepicker/createDateSegment.ts +229 -0
  50. package/src/datepicker/createTimeField.ts +154 -0
  51. package/src/datepicker/index.ts +28 -0
  52. package/src/dialog/createDialog.ts +120 -0
  53. package/src/dialog/index.ts +2 -0
  54. package/src/dialog/types.ts +19 -0
  55. package/src/disclosure/createDisclosure.ts +131 -0
  56. package/src/disclosure/createDisclosureGroup.ts +62 -0
  57. package/src/disclosure/index.ts +11 -0
  58. package/src/dnd/createDrag.ts +209 -0
  59. package/src/dnd/createDraggableCollection.ts +63 -0
  60. package/src/dnd/createDraggableItem.ts +243 -0
  61. package/src/dnd/createDrop.ts +321 -0
  62. package/src/dnd/createDroppableCollection.ts +293 -0
  63. package/src/dnd/createDroppableItem.ts +213 -0
  64. package/src/dnd/index.ts +47 -0
  65. package/src/dnd/types.ts +89 -0
  66. package/src/dnd/utils.ts +294 -0
  67. package/src/focus/FocusScope.tsx +408 -0
  68. package/src/focus/createAutoFocus.ts +321 -0
  69. package/src/focus/createFocusRestore.ts +313 -0
  70. package/src/focus/createVirtualFocus.ts +396 -0
  71. package/src/focus/index.ts +35 -0
  72. package/src/form/createFormReset.ts +51 -0
  73. package/src/form/createFormValidation.ts +224 -0
  74. package/src/form/index.ts +11 -0
  75. package/src/grid/GridKeyboardDelegate.ts +429 -0
  76. package/src/grid/createGrid.ts +261 -0
  77. package/src/grid/createGridCell.ts +182 -0
  78. package/src/grid/createGridRow.ts +153 -0
  79. package/src/grid/index.ts +18 -0
  80. package/src/grid/types.ts +133 -0
  81. package/src/gridlist/createGridList.ts +185 -0
  82. package/src/gridlist/createGridListItem.ts +180 -0
  83. package/src/gridlist/createGridListSelectionCheckbox.ts +59 -0
  84. package/src/gridlist/index.ts +16 -0
  85. package/src/gridlist/types.ts +81 -0
  86. package/src/i18n/NumberFormatter.ts +266 -0
  87. package/src/i18n/createCollator.ts +79 -0
  88. package/src/i18n/createDateFormatter.ts +83 -0
  89. package/src/i18n/createFilter.ts +131 -0
  90. package/src/i18n/createNumberFormatter.ts +52 -0
  91. package/src/i18n/createStringFormatter.ts +87 -0
  92. package/src/i18n/index.ts +40 -0
  93. package/src/i18n/locale.tsx +188 -0
  94. package/src/i18n/utils.ts +99 -0
  95. package/src/index.ts +670 -0
  96. package/src/interactions/FocusableProvider.tsx +44 -0
  97. package/src/interactions/PressEvent.ts +126 -0
  98. package/src/interactions/createFocus.ts +163 -0
  99. package/src/interactions/createFocusRing.ts +89 -0
  100. package/src/interactions/createFocusWithin.ts +206 -0
  101. package/src/interactions/createFocusable.ts +168 -0
  102. package/src/interactions/createHover.ts +254 -0
  103. package/src/interactions/createInteractionModality.ts +424 -0
  104. package/src/interactions/createKeyboard.ts +82 -0
  105. package/src/interactions/createLongPress.ts +174 -0
  106. package/src/interactions/createMove.ts +289 -0
  107. package/src/interactions/createPress.ts +834 -0
  108. package/src/interactions/index.ts +78 -0
  109. package/src/label/createField.ts +145 -0
  110. package/src/label/createLabel.ts +117 -0
  111. package/src/label/createLabels.ts +50 -0
  112. package/src/label/index.ts +19 -0
  113. package/src/landmark/createLandmark.ts +377 -0
  114. package/src/landmark/index.ts +8 -0
  115. package/src/link/createLink.ts +182 -0
  116. package/src/link/index.ts +1 -0
  117. package/src/listbox/createListBox.ts +269 -0
  118. package/src/listbox/createOption.ts +151 -0
  119. package/src/listbox/index.ts +12 -0
  120. package/src/live-announcer/announce.ts +322 -0
  121. package/src/live-announcer/index.ts +9 -0
  122. package/src/menu/createMenu.ts +396 -0
  123. package/src/menu/createMenuItem.ts +149 -0
  124. package/src/menu/createMenuTrigger.ts +88 -0
  125. package/src/menu/index.ts +18 -0
  126. package/src/meter/createMeter.ts +75 -0
  127. package/src/meter/index.ts +1 -0
  128. package/src/numberfield/createNumberField.ts +268 -0
  129. package/src/numberfield/index.ts +5 -0
  130. package/src/overlays/ariaHideOutside.ts +219 -0
  131. package/src/overlays/createInteractOutside.ts +149 -0
  132. package/src/overlays/createModal.tsx +202 -0
  133. package/src/overlays/createOverlay.ts +155 -0
  134. package/src/overlays/createOverlayTrigger.ts +85 -0
  135. package/src/overlays/createPreventScroll.ts +266 -0
  136. package/src/overlays/index.ts +44 -0
  137. package/src/popover/calculatePosition.ts +766 -0
  138. package/src/popover/createOverlayPosition.ts +356 -0
  139. package/src/popover/createPopover.ts +170 -0
  140. package/src/popover/index.ts +24 -0
  141. package/src/progress/createProgressBar.ts +128 -0
  142. package/src/progress/index.ts +5 -0
  143. package/src/radio/createRadio.ts +287 -0
  144. package/src/radio/createRadioGroup.ts +189 -0
  145. package/src/radio/createRadioGroupState.ts +201 -0
  146. package/src/radio/index.ts +23 -0
  147. package/src/searchfield/createSearchField.ts +186 -0
  148. package/src/searchfield/index.ts +2 -0
  149. package/src/select/createHiddenSelect.tsx +236 -0
  150. package/src/select/createSelect.ts +395 -0
  151. package/src/select/index.ts +14 -0
  152. package/src/selection/createTypeSelect.ts +201 -0
  153. package/src/selection/index.ts +6 -0
  154. package/src/separator/createSeparator.ts +82 -0
  155. package/src/separator/index.ts +6 -0
  156. package/src/slider/createSlider.ts +349 -0
  157. package/src/slider/index.ts +2 -0
  158. package/src/ssr/index.tsx +370 -0
  159. package/src/switch/createSwitch.ts +70 -0
  160. package/src/switch/index.ts +1 -0
  161. package/src/table/createTable.ts +526 -0
  162. package/src/table/createTableCell.ts +147 -0
  163. package/src/table/createTableColumnHeader.ts +115 -0
  164. package/src/table/createTableHeaderRow.ts +40 -0
  165. package/src/table/createTableRow.ts +155 -0
  166. package/src/table/createTableRowGroup.ts +32 -0
  167. package/src/table/createTableSelectAllCheckbox.ts +73 -0
  168. package/src/table/createTableSelectionCheckbox.ts +59 -0
  169. package/src/table/index.ts +30 -0
  170. package/src/table/types.ts +165 -0
  171. package/src/tabs/createTabs.ts +472 -0
  172. package/src/tabs/index.ts +14 -0
  173. package/src/tag/createTag.ts +194 -0
  174. package/src/tag/createTagGroup.ts +154 -0
  175. package/src/tag/index.ts +12 -0
  176. package/src/textfield/createTextField.ts +198 -0
  177. package/src/textfield/index.ts +5 -0
  178. package/src/toast/createToast.ts +118 -0
  179. package/src/toast/createToastRegion.ts +100 -0
  180. package/src/toast/index.ts +11 -0
  181. package/src/toggle/createToggle.ts +223 -0
  182. package/src/toggle/createToggleState.ts +94 -0
  183. package/src/toggle/index.ts +7 -0
  184. package/src/toolbar/createToolbar.ts +369 -0
  185. package/src/toolbar/index.ts +6 -0
  186. package/src/tooltip/createTooltip.ts +79 -0
  187. package/src/tooltip/createTooltipTrigger.ts +222 -0
  188. package/src/tooltip/index.ts +6 -0
  189. package/src/tree/createTree.ts +246 -0
  190. package/src/tree/createTreeItem.ts +233 -0
  191. package/src/tree/createTreeSelectionCheckbox.ts +68 -0
  192. package/src/tree/index.ts +16 -0
  193. package/src/tree/types.ts +87 -0
  194. package/src/utils/createDescription.ts +137 -0
  195. package/src/utils/dom.ts +327 -0
  196. package/src/utils/env.ts +54 -0
  197. package/src/utils/events.ts +106 -0
  198. package/src/utils/filterDOMProps.ts +116 -0
  199. package/src/utils/focus.ts +151 -0
  200. package/src/utils/geometry.ts +115 -0
  201. package/src/utils/globalListeners.ts +142 -0
  202. package/src/utils/index.ts +80 -0
  203. package/src/utils/mergeProps.ts +52 -0
  204. package/src/utils/platform.ts +52 -0
  205. package/src/utils/reactivity.ts +36 -0
  206. package/src/utils/textSelection.ts +114 -0
  207. package/src/visually-hidden/createVisuallyHidden.ts +124 -0
  208. package/src/visually-hidden/index.ts +6 -0
  209. package/dist/index.jsx +0 -15845
  210. package/dist/index.jsx.map +0 -7
@@ -0,0 +1,314 @@
1
+ /**
2
+ * createColorArea hook.
3
+ *
4
+ * Provides ARIA attributes and keyboard/pointer handling for a 2D color area.
5
+ */
6
+
7
+ import { createMemo, type Accessor } from 'solid-js';
8
+ import type { ColorAreaState } from '@proyecto-viviana/solid-stately';
9
+ import { createId } from '../ssr';
10
+ import type { AriaColorAreaOptions, ColorAreaAria } from './types';
11
+
12
+ /**
13
+ * Creates ARIA props for a color area.
14
+ */
15
+ export function createColorArea(
16
+ props: Accessor<AriaColorAreaOptions>,
17
+ state: Accessor<ColorAreaState>,
18
+ areaRef: Accessor<HTMLDivElement | null>
19
+ ): ColorAreaAria {
20
+ const getProps = () => props();
21
+ const getState = () => state();
22
+
23
+ // Generate IDs
24
+ const xInputId = createId();
25
+ const yInputId = createId();
26
+
27
+ // Calculate position from pointer event
28
+ const getPositionFromEvent = (e: MouseEvent | PointerEvent) => {
29
+ const area = areaRef();
30
+ if (!area) return null;
31
+
32
+ const rect = area.getBoundingClientRect();
33
+ const x = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
34
+ const y = Math.max(0, Math.min(1, (e.clientY - rect.top) / rect.height));
35
+
36
+ return { x, y };
37
+ };
38
+
39
+ // Handle pointer down
40
+ const onPointerDown = (e: PointerEvent) => {
41
+ if (getProps().isDisabled || getState().isDisabled) return;
42
+
43
+ const pos = getPositionFromEvent(e);
44
+ if (!pos) return;
45
+
46
+ getState().setColorFromPoint(pos.x, pos.y);
47
+ getState().setDragging(true);
48
+
49
+ // Capture pointer for dragging
50
+ (e.target as HTMLElement).setPointerCapture?.(e.pointerId);
51
+ };
52
+
53
+ // Handle pointer move
54
+ const onPointerMove = (e: PointerEvent) => {
55
+ if (!getState().isDragging) return;
56
+
57
+ const pos = getPositionFromEvent(e);
58
+ if (!pos) return;
59
+
60
+ getState().setColorFromPoint(pos.x, pos.y);
61
+ };
62
+
63
+ // Handle pointer up
64
+ const onPointerUp = (e: PointerEvent) => {
65
+ if (getState().isDragging) {
66
+ getState().setDragging(false);
67
+ (e.target as HTMLElement).releasePointerCapture?.(e.pointerId);
68
+ }
69
+ };
70
+
71
+ // Handle keyboard for X axis
72
+ const onKeyDownX = (e: KeyboardEvent) => {
73
+ if (getProps().isDisabled || getState().isDisabled) return;
74
+
75
+ const s = getState();
76
+ let handled = true;
77
+
78
+ switch (e.key) {
79
+ case 'ArrowRight':
80
+ s.incrementX();
81
+ break;
82
+ case 'ArrowLeft':
83
+ s.decrementX();
84
+ break;
85
+ case 'ArrowUp':
86
+ s.incrementY();
87
+ break;
88
+ case 'ArrowDown':
89
+ s.decrementY();
90
+ break;
91
+ case 'PageUp':
92
+ s.incrementY(s.yChannelPageStep);
93
+ break;
94
+ case 'PageDown':
95
+ s.decrementY(s.yChannelPageStep);
96
+ break;
97
+ case 'Home':
98
+ if (e.ctrlKey) {
99
+ s.setXValue(0);
100
+ s.setYValue(100);
101
+ } else {
102
+ s.setXValue(0);
103
+ }
104
+ break;
105
+ case 'End':
106
+ if (e.ctrlKey) {
107
+ s.setXValue(100);
108
+ s.setYValue(0);
109
+ } else {
110
+ s.setXValue(100);
111
+ }
112
+ break;
113
+ default:
114
+ handled = false;
115
+ }
116
+
117
+ if (handled) {
118
+ e.preventDefault();
119
+ e.stopPropagation();
120
+ }
121
+ };
122
+
123
+ // Handle keyboard for Y axis
124
+ const onKeyDownY = (e: KeyboardEvent) => {
125
+ if (getProps().isDisabled || getState().isDisabled) return;
126
+
127
+ const s = getState();
128
+ let handled = true;
129
+
130
+ switch (e.key) {
131
+ case 'ArrowUp':
132
+ s.incrementY();
133
+ break;
134
+ case 'ArrowDown':
135
+ s.decrementY();
136
+ break;
137
+ case 'ArrowRight':
138
+ s.incrementX();
139
+ break;
140
+ case 'ArrowLeft':
141
+ s.decrementX();
142
+ break;
143
+ case 'PageUp':
144
+ s.incrementY(s.yChannelPageStep);
145
+ break;
146
+ case 'PageDown':
147
+ s.decrementY(s.yChannelPageStep);
148
+ break;
149
+ case 'Home':
150
+ s.setYValue(100);
151
+ break;
152
+ case 'End':
153
+ s.setYValue(0);
154
+ break;
155
+ default:
156
+ handled = false;
157
+ }
158
+
159
+ if (handled) {
160
+ e.preventDefault();
161
+ e.stopPropagation();
162
+ }
163
+ };
164
+
165
+ // Color area props
166
+ const colorAreaProps = createMemo(() => {
167
+ const s = getState();
168
+ const p = getProps();
169
+
170
+ return {
171
+ role: 'group' as const,
172
+ 'aria-label': p['aria-label'],
173
+ 'aria-labelledby': p['aria-labelledby'],
174
+ 'aria-describedby': p['aria-describedby'],
175
+ 'aria-disabled': s.isDisabled || p.isDisabled || undefined,
176
+ onPointerDown,
177
+ onPointerMove,
178
+ onPointerUp,
179
+ style: {
180
+ position: 'relative' as const,
181
+ 'touch-action': 'none',
182
+ },
183
+ 'data-disabled': s.isDisabled || p.isDisabled || undefined,
184
+ };
185
+ });
186
+
187
+ // Gradient props (the visual area)
188
+ const gradientProps = createMemo(() => {
189
+ return {
190
+ role: 'presentation' as const,
191
+ style: {
192
+ width: '100%',
193
+ height: '100%',
194
+ },
195
+ };
196
+ });
197
+
198
+ // Thumb props
199
+ const thumbProps = createMemo(() => {
200
+ const s = getState();
201
+ const p = getProps();
202
+ const pos = s.getThumbPosition();
203
+
204
+ return {
205
+ role: 'presentation' as const,
206
+ style: {
207
+ position: 'absolute' as const,
208
+ left: `${pos.x * 100}%`,
209
+ top: `${pos.y * 100}%`,
210
+ transform: 'translate(-50%, -50%)',
211
+ 'touch-action': 'none',
212
+ },
213
+ 'data-dragging': s.isDragging || undefined,
214
+ 'data-disabled': s.isDisabled || p.isDisabled || undefined,
215
+ };
216
+ });
217
+
218
+ // X input props (hidden, for accessibility)
219
+ const xInputProps = createMemo(() => {
220
+ const s = getState();
221
+ const p = getProps();
222
+ const xRange = s.value.getChannelRange(s.xChannel);
223
+ const xName = s.value.getChannelName(s.xChannel, 'en-US');
224
+
225
+ return {
226
+ type: 'range',
227
+ id: xInputId,
228
+ 'aria-label': `${xName}`,
229
+ 'aria-valuetext': `${xName}: ${s.getXValue()}`,
230
+ min: xRange.minValue,
231
+ max: xRange.maxValue,
232
+ step: xRange.step,
233
+ value: s.getXValue(),
234
+ disabled: s.isDisabled || p.isDisabled,
235
+ onKeyDown: onKeyDownX,
236
+ onChange: (e: Event) => {
237
+ const target = e.target as HTMLInputElement;
238
+ s.setXValue(parseFloat(target.value));
239
+ },
240
+ onBlur: () => {
241
+ if (s.isDragging) {
242
+ s.setDragging(false);
243
+ }
244
+ },
245
+ tabIndex: 0,
246
+ style: {
247
+ position: 'absolute' as const,
248
+ width: '1px',
249
+ height: '1px',
250
+ padding: '0',
251
+ margin: '-1px',
252
+ overflow: 'hidden',
253
+ clip: 'rect(0, 0, 0, 0)',
254
+ 'white-space': 'nowrap',
255
+ border: '0',
256
+ },
257
+ };
258
+ });
259
+
260
+ // Y input props (hidden, for accessibility)
261
+ const yInputProps = createMemo(() => {
262
+ const s = getState();
263
+ const p = getProps();
264
+ const yRange = s.value.getChannelRange(s.yChannel);
265
+ const yName = s.value.getChannelName(s.yChannel, 'en-US');
266
+
267
+ return {
268
+ type: 'range',
269
+ id: yInputId,
270
+ 'aria-label': `${yName}`,
271
+ 'aria-valuetext': `${yName}: ${s.getYValue()}`,
272
+ min: yRange.minValue,
273
+ max: yRange.maxValue,
274
+ step: yRange.step,
275
+ value: s.getYValue(),
276
+ disabled: s.isDisabled || p.isDisabled,
277
+ onKeyDown: onKeyDownY,
278
+ onChange: (e: Event) => {
279
+ const target = e.target as HTMLInputElement;
280
+ s.setYValue(parseFloat(target.value));
281
+ },
282
+ tabIndex: -1, // Only first input is in tab order
283
+ style: {
284
+ position: 'absolute' as const,
285
+ width: '1px',
286
+ height: '1px',
287
+ padding: '0',
288
+ margin: '-1px',
289
+ overflow: 'hidden',
290
+ clip: 'rect(0, 0, 0, 0)',
291
+ 'white-space': 'nowrap',
292
+ border: '0',
293
+ },
294
+ };
295
+ });
296
+
297
+ return {
298
+ get colorAreaProps() {
299
+ return colorAreaProps();
300
+ },
301
+ get gradientProps() {
302
+ return gradientProps();
303
+ },
304
+ get thumbProps() {
305
+ return thumbProps();
306
+ },
307
+ get xInputProps() {
308
+ return xInputProps();
309
+ },
310
+ get yInputProps() {
311
+ return yInputProps();
312
+ },
313
+ };
314
+ }
@@ -0,0 +1,137 @@
1
+ /**
2
+ * createColorField hook.
3
+ *
4
+ * Provides ARIA attributes and keyboard handling for a color input field.
5
+ */
6
+
7
+ import { createMemo, type Accessor } from 'solid-js';
8
+ import type { ColorFieldState } from '@proyecto-viviana/solid-stately';
9
+ import { createId } from '../ssr';
10
+ import type { AriaColorFieldOptions, ColorFieldAria } from './types';
11
+
12
+ /**
13
+ * Creates ARIA props for a color field.
14
+ */
15
+ export function createColorField(
16
+ props: Accessor<AriaColorFieldOptions>,
17
+ state: Accessor<ColorFieldState>,
18
+ _inputRef: Accessor<HTMLInputElement | null>
19
+ ): ColorFieldAria {
20
+ const getProps = () => props();
21
+ const getState = () => state();
22
+
23
+ // Generate IDs
24
+ const inputId = createId();
25
+ const labelId = createId();
26
+
27
+ // Handle keyboard
28
+ const onKeyDown = (e: KeyboardEvent) => {
29
+ const s = getState();
30
+ const p = getProps();
31
+
32
+ if (p.isDisabled || s.isDisabled || p.isReadOnly || s.isReadOnly) return;
33
+
34
+ // Only handle special keys for channel mode
35
+ if (!s.channel) return;
36
+
37
+ let handled = true;
38
+
39
+ switch (e.key) {
40
+ case 'ArrowUp':
41
+ s.increment();
42
+ break;
43
+ case 'ArrowDown':
44
+ s.decrement();
45
+ break;
46
+ case 'PageUp':
47
+ s.incrementToMax();
48
+ break;
49
+ case 'PageDown':
50
+ s.decrementToMin();
51
+ break;
52
+ case 'Home':
53
+ if (e.ctrlKey) {
54
+ s.decrementToMin();
55
+ } else {
56
+ handled = false;
57
+ }
58
+ break;
59
+ case 'End':
60
+ if (e.ctrlKey) {
61
+ s.incrementToMax();
62
+ } else {
63
+ handled = false;
64
+ }
65
+ break;
66
+ default:
67
+ handled = false;
68
+ }
69
+
70
+ if (handled) {
71
+ e.preventDefault();
72
+ }
73
+ };
74
+
75
+ // Label props
76
+ const labelProps = createMemo(() => {
77
+ return {
78
+ id: labelId,
79
+ for: inputId,
80
+ };
81
+ });
82
+
83
+ // Input props
84
+ const inputProps = createMemo(() => {
85
+ const s = getState();
86
+ const p = getProps();
87
+
88
+ // Get channel name if in channel mode
89
+ const channelLabel = s.channel && s.value
90
+ ? s.value.getChannelName(s.channel, 'en-US')
91
+ : undefined;
92
+
93
+ return {
94
+ id: inputId,
95
+ type: 'text',
96
+ value: s.inputValue,
97
+ disabled: p.isDisabled || s.isDisabled,
98
+ readOnly: p.isReadOnly || s.isReadOnly,
99
+ 'aria-label': p['aria-label'] ?? channelLabel,
100
+ 'aria-labelledby': p['aria-labelledby'],
101
+ 'aria-describedby': p['aria-describedby'],
102
+ 'aria-invalid': s.isInvalid || undefined,
103
+ // For spinbutton role in channel mode
104
+ role: s.channel ? ('spinbutton' as const) : undefined,
105
+ 'aria-valuenow': s.channel && s.value
106
+ ? s.value.getChannelValue(s.channel)
107
+ : undefined,
108
+ 'aria-valuemin': s.channel && s.value
109
+ ? s.value.getChannelRange(s.channel).minValue
110
+ : undefined,
111
+ 'aria-valuemax': s.channel && s.value
112
+ ? s.value.getChannelRange(s.channel).maxValue
113
+ : undefined,
114
+ onInput: (e: InputEvent) => {
115
+ const target = e.target as HTMLInputElement;
116
+ s.setInputValue(target.value);
117
+ },
118
+ onChange: () => {
119
+ // onChange fires on blur or enter
120
+ s.commit();
121
+ },
122
+ onBlur: () => {
123
+ s.commit();
124
+ },
125
+ onKeyDown,
126
+ };
127
+ });
128
+
129
+ return {
130
+ get labelProps() {
131
+ return labelProps();
132
+ },
133
+ get inputProps() {
134
+ return inputProps();
135
+ },
136
+ };
137
+ }
@@ -0,0 +1,197 @@
1
+ /**
2
+ * createColorSlider hook.
3
+ *
4
+ * Provides ARIA attributes and keyboard handling for a color slider.
5
+ */
6
+
7
+ import { createMemo, type Accessor } from 'solid-js';
8
+ import type { ColorSliderState } from '@proyecto-viviana/solid-stately';
9
+ import { createId } from '../ssr';
10
+ import type { AriaColorSliderOptions, ColorSliderAria } from './types';
11
+
12
+ /**
13
+ * Creates ARIA props for a color slider.
14
+ */
15
+ export function createColorSlider(
16
+ props: Accessor<AriaColorSliderOptions>,
17
+ state: Accessor<ColorSliderState>,
18
+ trackRef: Accessor<HTMLDivElement | null>
19
+ ): ColorSliderAria {
20
+ const getProps = () => props();
21
+ const getState = () => state();
22
+
23
+ // Generate IDs
24
+ const inputId = createId();
25
+ const labelId = createId();
26
+
27
+ // Get channel name for ARIA label
28
+ const channelName = createMemo(() => {
29
+ const p = getProps();
30
+ if (p.channelName) return p.channelName;
31
+ const s = getState();
32
+ return s.value.getChannelName(s.channel, 'en-US');
33
+ });
34
+
35
+ // Handle track click
36
+ const onTrackMouseDown = (e: MouseEvent) => {
37
+ if (getProps().isDisabled || getState().isDisabled) return;
38
+
39
+ const track = trackRef();
40
+ if (!track) return;
41
+
42
+ const rect = track.getBoundingClientRect();
43
+ const percent = (e.clientX - rect.left) / rect.width;
44
+ getState().setThumbPercent(Math.max(0, Math.min(1, percent)));
45
+ getState().setDragging(true);
46
+ };
47
+
48
+ // Handle keyboard
49
+ const onKeyDown = (e: KeyboardEvent) => {
50
+ if (getProps().isDisabled || getState().isDisabled) return;
51
+
52
+ const s = getState();
53
+ let handled = true;
54
+
55
+ switch (e.key) {
56
+ case 'ArrowRight':
57
+ case 'ArrowUp':
58
+ s.incrementThumb();
59
+ break;
60
+ case 'ArrowLeft':
61
+ case 'ArrowDown':
62
+ s.decrementThumb();
63
+ break;
64
+ case 'PageUp':
65
+ s.incrementThumb(s.pageSize);
66
+ break;
67
+ case 'PageDown':
68
+ s.decrementThumb(s.pageSize);
69
+ break;
70
+ case 'Home':
71
+ s.setThumbValue(s.minValue);
72
+ break;
73
+ case 'End':
74
+ s.setThumbValue(s.maxValue);
75
+ break;
76
+ default:
77
+ handled = false;
78
+ }
79
+
80
+ if (handled) {
81
+ e.preventDefault();
82
+ e.stopPropagation();
83
+ }
84
+ };
85
+
86
+ // Track props
87
+ const trackProps = createMemo(() => {
88
+ const s = getState();
89
+ return {
90
+ role: 'presentation' as const,
91
+ onMouseDown: onTrackMouseDown,
92
+ style: {
93
+ position: 'relative' as const,
94
+ 'touch-action': 'none',
95
+ },
96
+ 'data-disabled': s.isDisabled || undefined,
97
+ };
98
+ });
99
+
100
+ // Thumb props
101
+ const thumbProps = createMemo(() => {
102
+ const s = getState();
103
+ const p = getProps();
104
+ const percent = s.getThumbPercent();
105
+
106
+ return {
107
+ role: 'presentation' as const,
108
+ style: {
109
+ position: 'absolute' as const,
110
+ left: `${percent * 100}%`,
111
+ top: '50%',
112
+ transform: 'translate(-50%, -50%)',
113
+ 'touch-action': 'none',
114
+ },
115
+ 'data-dragging': s.isDragging || undefined,
116
+ 'data-disabled': s.isDisabled || p.isDisabled || undefined,
117
+ };
118
+ });
119
+
120
+ // Input props (hidden, for accessibility)
121
+ const inputProps = createMemo(() => {
122
+ const s = getState();
123
+ const p = getProps();
124
+
125
+ return {
126
+ type: 'range',
127
+ id: inputId,
128
+ min: s.minValue,
129
+ max: s.maxValue,
130
+ step: s.step,
131
+ value: s.getThumbValue(),
132
+ disabled: s.isDisabled || p.isDisabled,
133
+ 'aria-label': p['aria-label'] ?? channelName(),
134
+ 'aria-labelledby': p['aria-labelledby'],
135
+ 'aria-describedby': p['aria-describedby'],
136
+ 'aria-valuetext': s.getThumbValueLabel(),
137
+ onKeyDown,
138
+ onChange: (e: Event) => {
139
+ const target = e.target as HTMLInputElement;
140
+ s.setThumbValue(parseFloat(target.value));
141
+ },
142
+ onFocus: () => {
143
+ // Focus handling
144
+ },
145
+ onBlur: () => {
146
+ if (s.isDragging) {
147
+ s.setDragging(false);
148
+ }
149
+ },
150
+ style: {
151
+ position: 'absolute' as const,
152
+ width: '1px',
153
+ height: '1px',
154
+ padding: '0',
155
+ margin: '-1px',
156
+ overflow: 'hidden',
157
+ clip: 'rect(0, 0, 0, 0)',
158
+ 'white-space': 'nowrap',
159
+ border: '0',
160
+ },
161
+ };
162
+ });
163
+
164
+ // Output props
165
+ const outputProps = createMemo(() => {
166
+ return {
167
+ 'aria-live': 'off' as const,
168
+ for: inputId,
169
+ };
170
+ });
171
+
172
+ // Label props
173
+ const labelProps = createMemo(() => {
174
+ return {
175
+ id: labelId,
176
+ for: inputId,
177
+ };
178
+ });
179
+
180
+ return {
181
+ get trackProps() {
182
+ return trackProps();
183
+ },
184
+ get thumbProps() {
185
+ return thumbProps();
186
+ },
187
+ get inputProps() {
188
+ return inputProps();
189
+ },
190
+ get outputProps() {
191
+ return outputProps();
192
+ },
193
+ get labelProps() {
194
+ return labelProps();
195
+ },
196
+ };
197
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * createColorSwatch hook.
3
+ *
4
+ * Provides ARIA attributes for a color swatch display.
5
+ */
6
+
7
+ import { createMemo, type Accessor } from 'solid-js';
8
+ import { normalizeColor } from '@proyecto-viviana/solid-stately';
9
+ import type { AriaColorSwatchOptions, ColorSwatchAria } from './types';
10
+
11
+ /**
12
+ * Creates ARIA props for a color swatch.
13
+ */
14
+ export function createColorSwatch(
15
+ props: Accessor<AriaColorSwatchOptions>
16
+ ): ColorSwatchAria {
17
+ const getProps = () => props();
18
+
19
+ // Swatch props
20
+ const swatchProps = createMemo(() => {
21
+ const p = getProps();
22
+ const color = normalizeColor(p.color);
23
+ const colorName = color.getColorName('en-US');
24
+
25
+ return {
26
+ role: 'img' as const,
27
+ 'aria-label': p['aria-label'] ?? colorName,
28
+ 'aria-roledescription': 'color swatch',
29
+ style: {
30
+ 'background-color': color.toString('css'),
31
+ },
32
+ };
33
+ });
34
+
35
+ return {
36
+ get swatchProps() {
37
+ return swatchProps();
38
+ },
39
+ };
40
+ }