@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.
- package/dist/autocomplete/createAutocomplete.d.ts +2 -2
- package/dist/autocomplete/createAutocomplete.d.ts.map +1 -1
- package/dist/index.js +233 -234
- package/dist/index.js.map +2 -2
- package/dist/index.ssr.js +233 -234
- package/dist/index.ssr.js.map +2 -2
- package/dist/interactions/PressEvent.d.ts +13 -10
- package/dist/interactions/PressEvent.d.ts.map +1 -1
- package/dist/interactions/createPress.d.ts.map +1 -1
- package/dist/interactions/index.d.ts +1 -1
- package/dist/interactions/index.d.ts.map +1 -1
- package/dist/select/createHiddenSelect.d.ts.map +1 -1
- package/dist/toolbar/createToolbar.d.ts.map +1 -1
- package/dist/tooltip/createTooltipTrigger.d.ts.map +1 -1
- package/package.json +9 -7
- package/src/autocomplete/createAutocomplete.ts +341 -0
- package/src/autocomplete/index.ts +9 -0
- package/src/breadcrumbs/createBreadcrumbs.ts +196 -0
- package/src/breadcrumbs/index.ts +8 -0
- package/src/button/createButton.ts +142 -0
- package/src/button/createToggleButton.ts +101 -0
- package/src/button/index.ts +4 -0
- package/src/button/types.ts +78 -0
- package/src/calendar/createCalendar.ts +138 -0
- package/src/calendar/createCalendarCell.ts +187 -0
- package/src/calendar/createCalendarGrid.ts +140 -0
- package/src/calendar/createRangeCalendar.ts +136 -0
- package/src/calendar/createRangeCalendarCell.ts +186 -0
- package/src/calendar/index.ts +34 -0
- package/src/checkbox/createCheckbox.ts +135 -0
- package/src/checkbox/createCheckboxGroup.ts +137 -0
- package/src/checkbox/createCheckboxGroupItem.ts +117 -0
- package/src/checkbox/createCheckboxGroupState.ts +193 -0
- package/src/checkbox/index.ts +13 -0
- package/src/color/createColorArea.ts +314 -0
- package/src/color/createColorField.ts +137 -0
- package/src/color/createColorSlider.ts +197 -0
- package/src/color/createColorSwatch.ts +40 -0
- package/src/color/createColorWheel.ts +208 -0
- package/src/color/index.ts +24 -0
- package/src/color/types.ts +116 -0
- package/src/combobox/createComboBox.ts +647 -0
- package/src/combobox/index.ts +6 -0
- package/src/combobox/intl/en-US.json +7 -0
- package/src/combobox/intl/es-ES.json +7 -0
- package/src/combobox/intl/index.ts +23 -0
- package/src/datepicker/createDateField.ts +154 -0
- package/src/datepicker/createDatePicker.ts +206 -0
- package/src/datepicker/createDateSegment.ts +229 -0
- package/src/datepicker/createTimeField.ts +154 -0
- package/src/datepicker/index.ts +28 -0
- package/src/dialog/createDialog.ts +120 -0
- package/src/dialog/index.ts +2 -0
- package/src/dialog/types.ts +19 -0
- package/src/disclosure/createDisclosure.ts +131 -0
- package/src/disclosure/createDisclosureGroup.ts +62 -0
- package/src/disclosure/index.ts +11 -0
- package/src/dnd/createDrag.ts +209 -0
- package/src/dnd/createDraggableCollection.ts +63 -0
- package/src/dnd/createDraggableItem.ts +243 -0
- package/src/dnd/createDrop.ts +321 -0
- package/src/dnd/createDroppableCollection.ts +293 -0
- package/src/dnd/createDroppableItem.ts +213 -0
- package/src/dnd/index.ts +47 -0
- package/src/dnd/types.ts +89 -0
- package/src/dnd/utils.ts +294 -0
- package/src/focus/FocusScope.tsx +408 -0
- package/src/focus/createAutoFocus.ts +321 -0
- package/src/focus/createFocusRestore.ts +313 -0
- package/src/focus/createVirtualFocus.ts +396 -0
- package/src/focus/index.ts +35 -0
- package/src/form/createFormReset.ts +51 -0
- package/src/form/createFormValidation.ts +224 -0
- package/src/form/index.ts +11 -0
- package/src/grid/GridKeyboardDelegate.ts +429 -0
- package/src/grid/createGrid.ts +261 -0
- package/src/grid/createGridCell.ts +182 -0
- package/src/grid/createGridRow.ts +153 -0
- package/src/grid/index.ts +18 -0
- package/src/grid/types.ts +133 -0
- package/src/gridlist/createGridList.ts +185 -0
- package/src/gridlist/createGridListItem.ts +180 -0
- package/src/gridlist/createGridListSelectionCheckbox.ts +59 -0
- package/src/gridlist/index.ts +16 -0
- package/src/gridlist/types.ts +81 -0
- package/src/i18n/NumberFormatter.ts +266 -0
- package/src/i18n/createCollator.ts +79 -0
- package/src/i18n/createDateFormatter.ts +83 -0
- package/src/i18n/createFilter.ts +131 -0
- package/src/i18n/createNumberFormatter.ts +52 -0
- package/src/i18n/createStringFormatter.ts +87 -0
- package/src/i18n/index.ts +40 -0
- package/src/i18n/locale.tsx +188 -0
- package/src/i18n/utils.ts +99 -0
- package/src/index.ts +670 -0
- package/src/interactions/FocusableProvider.tsx +44 -0
- package/src/interactions/PressEvent.ts +126 -0
- package/src/interactions/createFocus.ts +163 -0
- package/src/interactions/createFocusRing.ts +89 -0
- package/src/interactions/createFocusWithin.ts +206 -0
- package/src/interactions/createFocusable.ts +168 -0
- package/src/interactions/createHover.ts +254 -0
- package/src/interactions/createInteractionModality.ts +424 -0
- package/src/interactions/createKeyboard.ts +82 -0
- package/src/interactions/createLongPress.ts +174 -0
- package/src/interactions/createMove.ts +289 -0
- package/src/interactions/createPress.ts +834 -0
- package/src/interactions/index.ts +78 -0
- package/src/label/createField.ts +145 -0
- package/src/label/createLabel.ts +117 -0
- package/src/label/createLabels.ts +50 -0
- package/src/label/index.ts +19 -0
- package/src/landmark/createLandmark.ts +377 -0
- package/src/landmark/index.ts +8 -0
- package/src/link/createLink.ts +182 -0
- package/src/link/index.ts +1 -0
- package/src/listbox/createListBox.ts +269 -0
- package/src/listbox/createOption.ts +151 -0
- package/src/listbox/index.ts +12 -0
- package/src/live-announcer/announce.ts +322 -0
- package/src/live-announcer/index.ts +9 -0
- package/src/menu/createMenu.ts +396 -0
- package/src/menu/createMenuItem.ts +149 -0
- package/src/menu/createMenuTrigger.ts +88 -0
- package/src/menu/index.ts +18 -0
- package/src/meter/createMeter.ts +75 -0
- package/src/meter/index.ts +1 -0
- package/src/numberfield/createNumberField.ts +268 -0
- package/src/numberfield/index.ts +5 -0
- package/src/overlays/ariaHideOutside.ts +219 -0
- package/src/overlays/createInteractOutside.ts +149 -0
- package/src/overlays/createModal.tsx +202 -0
- package/src/overlays/createOverlay.ts +155 -0
- package/src/overlays/createOverlayTrigger.ts +85 -0
- package/src/overlays/createPreventScroll.ts +266 -0
- package/src/overlays/index.ts +44 -0
- package/src/popover/calculatePosition.ts +766 -0
- package/src/popover/createOverlayPosition.ts +356 -0
- package/src/popover/createPopover.ts +170 -0
- package/src/popover/index.ts +24 -0
- package/src/progress/createProgressBar.ts +128 -0
- package/src/progress/index.ts +5 -0
- package/src/radio/createRadio.ts +287 -0
- package/src/radio/createRadioGroup.ts +189 -0
- package/src/radio/createRadioGroupState.ts +201 -0
- package/src/radio/index.ts +23 -0
- package/src/searchfield/createSearchField.ts +186 -0
- package/src/searchfield/index.ts +2 -0
- package/src/select/createHiddenSelect.tsx +236 -0
- package/src/select/createSelect.ts +395 -0
- package/src/select/index.ts +14 -0
- package/src/selection/createTypeSelect.ts +201 -0
- package/src/selection/index.ts +6 -0
- package/src/separator/createSeparator.ts +82 -0
- package/src/separator/index.ts +6 -0
- package/src/slider/createSlider.ts +349 -0
- package/src/slider/index.ts +2 -0
- package/src/ssr/index.tsx +370 -0
- package/src/switch/createSwitch.ts +70 -0
- package/src/switch/index.ts +1 -0
- package/src/table/createTable.ts +526 -0
- package/src/table/createTableCell.ts +147 -0
- package/src/table/createTableColumnHeader.ts +115 -0
- package/src/table/createTableHeaderRow.ts +40 -0
- package/src/table/createTableRow.ts +155 -0
- package/src/table/createTableRowGroup.ts +32 -0
- package/src/table/createTableSelectAllCheckbox.ts +73 -0
- package/src/table/createTableSelectionCheckbox.ts +59 -0
- package/src/table/index.ts +30 -0
- package/src/table/types.ts +165 -0
- package/src/tabs/createTabs.ts +472 -0
- package/src/tabs/index.ts +14 -0
- package/src/tag/createTag.ts +194 -0
- package/src/tag/createTagGroup.ts +154 -0
- package/src/tag/index.ts +12 -0
- package/src/textfield/createTextField.ts +198 -0
- package/src/textfield/index.ts +5 -0
- package/src/toast/createToast.ts +118 -0
- package/src/toast/createToastRegion.ts +100 -0
- package/src/toast/index.ts +11 -0
- package/src/toggle/createToggle.ts +223 -0
- package/src/toggle/createToggleState.ts +94 -0
- package/src/toggle/index.ts +7 -0
- package/src/toolbar/createToolbar.ts +369 -0
- package/src/toolbar/index.ts +6 -0
- package/src/tooltip/createTooltip.ts +79 -0
- package/src/tooltip/createTooltipTrigger.ts +222 -0
- package/src/tooltip/index.ts +6 -0
- package/src/tree/createTree.ts +246 -0
- package/src/tree/createTreeItem.ts +233 -0
- package/src/tree/createTreeSelectionCheckbox.ts +68 -0
- package/src/tree/index.ts +16 -0
- package/src/tree/types.ts +87 -0
- package/src/utils/createDescription.ts +137 -0
- package/src/utils/dom.ts +327 -0
- package/src/utils/env.ts +54 -0
- package/src/utils/events.ts +106 -0
- package/src/utils/filterDOMProps.ts +116 -0
- package/src/utils/focus.ts +151 -0
- package/src/utils/geometry.ts +115 -0
- package/src/utils/globalListeners.ts +142 -0
- package/src/utils/index.ts +80 -0
- package/src/utils/mergeProps.ts +52 -0
- package/src/utils/platform.ts +52 -0
- package/src/utils/reactivity.ts +36 -0
- package/src/utils/textSelection.ts +114 -0
- package/src/visually-hidden/createVisuallyHidden.ts +124 -0
- package/src/visually-hidden/index.ts +6 -0
- package/dist/index.jsx +0 -15845
- 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
|
+
}
|