@proyecto-viviana/solidaria 0.0.1 → 0.0.2
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/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
- package/src/button/createButton.ts +135 -0
- package/src/button/createToggleButton.ts +101 -0
- package/src/button/index.ts +4 -0
- package/src/button/types.ts +67 -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/index.ts +128 -0
- package/src/interactions/FocusableProvider.tsx +44 -0
- package/src/interactions/PressEvent.ts +112 -0
- package/src/interactions/createFocus.ts +157 -0
- package/src/interactions/createFocusRing.ts +142 -0
- package/src/interactions/createFocusWithin.ts +141 -0
- package/src/interactions/createFocusable.ts +168 -0
- package/src/interactions/createHover.ts +214 -0
- package/src/interactions/createKeyboard.ts +82 -0
- package/src/interactions/createPress.ts +758 -0
- package/src/interactions/index.ts +45 -0
- package/src/label/createField.ts +145 -0
- package/src/label/createLabel.ts +116 -0
- package/src/label/createLabels.ts +50 -0
- package/src/label/index.ts +19 -0
- package/src/link/createLink.ts +176 -0
- package/src/link/index.ts +1 -0
- package/src/progress/createProgressBar.ts +128 -0
- package/src/progress/index.ts +5 -0
- package/src/radio/createRadio.ts +286 -0
- package/src/radio/createRadioGroup.ts +189 -0
- package/src/radio/createRadioGroupState.ts +201 -0
- package/src/radio/index.ts +23 -0
- package/src/separator/createSeparator.ts +82 -0
- package/src/separator/index.ts +6 -0
- package/src/ssr/index.ts +36 -0
- package/src/switch/createSwitch.ts +70 -0
- package/src/switch/index.ts +1 -0
- package/src/textfield/createTextField.ts +198 -0
- package/src/textfield/index.ts +5 -0
- package/src/toggle/createToggle.ts +222 -0
- package/src/toggle/createToggleState.ts +94 -0
- package/src/toggle/index.ts +7 -0
- package/src/utils/dom.ts +244 -0
- package/src/utils/events.ts +119 -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 +66 -0
- package/src/utils/mergeProps.ts +49 -0
- package/src/utils/platform.ts +52 -0
- package/src/utils/reactivity.ts +36 -0
- package/src/utils/textSelection.ts +114 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checkbox group item hook for Solidaria
|
|
3
|
+
*
|
|
4
|
+
* Provides the behavior and accessibility implementation for a checkbox component
|
|
5
|
+
* contained within a checkbox group.
|
|
6
|
+
*
|
|
7
|
+
* This is a 1:1 port of @react-aria/checkbox's useCheckboxGroupItem hook.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { JSX } from 'solid-js';
|
|
11
|
+
import { createCheckbox, type AriaCheckboxProps, type CheckboxAria } from './createCheckbox';
|
|
12
|
+
import { type ToggleState, type CheckboxGroupState } from '@proyecto-viviana/solid-stately';
|
|
13
|
+
import { checkboxGroupData } from './createCheckboxGroup';
|
|
14
|
+
import { type MaybeAccessor, access } from '../utils/reactivity';
|
|
15
|
+
|
|
16
|
+
// ============================================
|
|
17
|
+
// TYPES
|
|
18
|
+
// ============================================
|
|
19
|
+
|
|
20
|
+
export interface AriaCheckboxGroupItemProps extends Omit<AriaCheckboxProps, 'isSelected' | 'defaultSelected'> {
|
|
21
|
+
/** The value of the checkbox. */
|
|
22
|
+
value: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// ============================================
|
|
26
|
+
// IMPLEMENTATION
|
|
27
|
+
// ============================================
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Provides the behavior and accessibility implementation for a checkbox component
|
|
31
|
+
* contained within a checkbox group.
|
|
32
|
+
*
|
|
33
|
+
* @param props - Props for the checkbox.
|
|
34
|
+
* @param state - State for the checkbox group, as returned by `createCheckboxGroupState`.
|
|
35
|
+
* @param inputRef - A ref accessor for the HTML input element.
|
|
36
|
+
*/
|
|
37
|
+
export function createCheckboxGroupItem(
|
|
38
|
+
props: MaybeAccessor<AriaCheckboxGroupItemProps>,
|
|
39
|
+
state: CheckboxGroupState,
|
|
40
|
+
inputRef: () => HTMLInputElement | null
|
|
41
|
+
): CheckboxAria {
|
|
42
|
+
const getProps = () => access(props);
|
|
43
|
+
|
|
44
|
+
// Create toggle state that syncs with the group state
|
|
45
|
+
const toggleState: ToggleState = {
|
|
46
|
+
isSelected: () => state.isSelected(getProps().value),
|
|
47
|
+
defaultSelected: state.defaultValue.includes(getProps().value),
|
|
48
|
+
setSelected(isSelected: boolean) {
|
|
49
|
+
const value = getProps().value;
|
|
50
|
+
if (isSelected) {
|
|
51
|
+
state.addValue(value);
|
|
52
|
+
} else {
|
|
53
|
+
state.removeValue(value);
|
|
54
|
+
}
|
|
55
|
+
getProps().onChange?.(isSelected);
|
|
56
|
+
},
|
|
57
|
+
toggle() {
|
|
58
|
+
state.toggleValue(getProps().value);
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// Get group data
|
|
63
|
+
const getGroupData = () => checkboxGroupData.get(state);
|
|
64
|
+
|
|
65
|
+
// Build checkbox props
|
|
66
|
+
const checkboxProps = (): AriaCheckboxProps => {
|
|
67
|
+
const p = getProps();
|
|
68
|
+
const groupData = getGroupData();
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
...p,
|
|
72
|
+
isReadOnly: p.isReadOnly ?? state.isReadOnly,
|
|
73
|
+
isDisabled: p.isDisabled ?? state.isDisabled,
|
|
74
|
+
name: p.name ?? groupData?.name,
|
|
75
|
+
form: p.form ?? groupData?.form,
|
|
76
|
+
isRequired: p.isRequired ?? state.isRequired(),
|
|
77
|
+
validationBehavior: p.validationBehavior ?? groupData?.validationBehavior ?? 'aria',
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// Use the checkbox hook
|
|
82
|
+
const result = createCheckbox(checkboxProps, toggleState, inputRef);
|
|
83
|
+
|
|
84
|
+
// Add group-level aria-describedby
|
|
85
|
+
return {
|
|
86
|
+
...result,
|
|
87
|
+
get inputProps() {
|
|
88
|
+
const baseInputProps = result.inputProps;
|
|
89
|
+
const groupData = getGroupData();
|
|
90
|
+
|
|
91
|
+
const describedByIds: string[] = [];
|
|
92
|
+
|
|
93
|
+
// Add props aria-describedby
|
|
94
|
+
const propsDescribedBy = getProps()['aria-describedby'];
|
|
95
|
+
if (propsDescribedBy) {
|
|
96
|
+
describedByIds.push(propsDescribedBy);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Add error message ID if group is invalid
|
|
100
|
+
if (state.isInvalid && groupData?.errorMessageId) {
|
|
101
|
+
describedByIds.push(groupData.errorMessageId);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Add description ID
|
|
105
|
+
if (groupData?.descriptionId) {
|
|
106
|
+
describedByIds.push(groupData.descriptionId);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const ariaDescribedBy = describedByIds.length > 0 ? describedByIds.join(' ') : undefined;
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
...baseInputProps,
|
|
113
|
+
'aria-describedby': ariaDescribedBy,
|
|
114
|
+
} as JSX.InputHTMLAttributes<HTMLInputElement>;
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checkbox group state for Solidaria
|
|
3
|
+
*
|
|
4
|
+
* Provides state management for a checkbox group component.
|
|
5
|
+
* Provides a name for the group, and manages selection and focus state.
|
|
6
|
+
*
|
|
7
|
+
* This is a 1:1 port of @react-stately/checkbox's useCheckboxGroupState.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { createSignal, Accessor } from 'solid-js';
|
|
11
|
+
import { type MaybeAccessor, access } from '../utils/reactivity';
|
|
12
|
+
|
|
13
|
+
// ============================================
|
|
14
|
+
// TYPES
|
|
15
|
+
// ============================================
|
|
16
|
+
|
|
17
|
+
export interface CheckboxGroupProps {
|
|
18
|
+
/** The current selected values (controlled). */
|
|
19
|
+
value?: string[];
|
|
20
|
+
/** The default selected values (uncontrolled). */
|
|
21
|
+
defaultValue?: string[];
|
|
22
|
+
/** Handler that is called when the value changes. */
|
|
23
|
+
onChange?: (value: string[]) => void;
|
|
24
|
+
/** Whether the checkbox group is disabled. */
|
|
25
|
+
isDisabled?: boolean;
|
|
26
|
+
/** Whether the checkbox group is read only. */
|
|
27
|
+
isReadOnly?: boolean;
|
|
28
|
+
/** Whether the checkbox group is required. */
|
|
29
|
+
isRequired?: boolean;
|
|
30
|
+
/** Whether the checkbox group is invalid. */
|
|
31
|
+
isInvalid?: boolean;
|
|
32
|
+
/** The name of the checkbox group, used when submitting an HTML form. */
|
|
33
|
+
name?: string;
|
|
34
|
+
/** The form to associate the checkbox group with. */
|
|
35
|
+
form?: string;
|
|
36
|
+
/** The label for the checkbox group. */
|
|
37
|
+
label?: string;
|
|
38
|
+
/** Handler that is called when the checkbox group receives focus. */
|
|
39
|
+
onFocus?: (e: FocusEvent) => void;
|
|
40
|
+
/** Handler that is called when the checkbox group loses focus. */
|
|
41
|
+
onBlur?: (e: FocusEvent) => void;
|
|
42
|
+
/** Handler that is called when the checkbox group's focus status changes. */
|
|
43
|
+
onFocusChange?: (isFocused: boolean) => void;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface CheckboxGroupState {
|
|
47
|
+
/** Current selected values. */
|
|
48
|
+
readonly value: Accessor<readonly string[]>;
|
|
49
|
+
/** Default selected values. */
|
|
50
|
+
readonly defaultValue: readonly string[];
|
|
51
|
+
/** Whether the checkbox group is disabled. */
|
|
52
|
+
readonly isDisabled: boolean;
|
|
53
|
+
/** Whether the checkbox group is read only. */
|
|
54
|
+
readonly isReadOnly: boolean;
|
|
55
|
+
/** Whether the checkbox group is invalid. */
|
|
56
|
+
readonly isInvalid: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Whether the checkboxes in the group are required.
|
|
59
|
+
* This changes to false once at least one item is selected.
|
|
60
|
+
*/
|
|
61
|
+
readonly isRequired: Accessor<boolean>;
|
|
62
|
+
/** Returns whether the given value is selected. */
|
|
63
|
+
isSelected(value: string): boolean;
|
|
64
|
+
/** Sets the selected values. */
|
|
65
|
+
setValue(value: string[]): void;
|
|
66
|
+
/** Adds a value to the set of selected values. */
|
|
67
|
+
addValue(value: string): void;
|
|
68
|
+
/** Removes a value from the set of selected values. */
|
|
69
|
+
removeValue(value: string): void;
|
|
70
|
+
/** Toggles a value in the set of selected values. */
|
|
71
|
+
toggleValue(value: string): void;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ============================================
|
|
75
|
+
// IMPLEMENTATION
|
|
76
|
+
// ============================================
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Provides state management for a checkbox group component.
|
|
80
|
+
* Provides a name for the group, and manages selection and focus state.
|
|
81
|
+
*/
|
|
82
|
+
export function createCheckboxGroupState(
|
|
83
|
+
props: MaybeAccessor<CheckboxGroupProps> = {}
|
|
84
|
+
): CheckboxGroupState {
|
|
85
|
+
const getProps = () => access(props);
|
|
86
|
+
|
|
87
|
+
// Get initial values
|
|
88
|
+
const initialProps = getProps();
|
|
89
|
+
const initialValue = initialProps.value ?? initialProps.defaultValue ?? [];
|
|
90
|
+
|
|
91
|
+
// Create internal signal for uncontrolled mode
|
|
92
|
+
const [internalValue, setInternalValue] = createSignal<readonly string[]>(initialValue);
|
|
93
|
+
|
|
94
|
+
// Determine if controlled
|
|
95
|
+
const isControlled = () => getProps().value !== undefined;
|
|
96
|
+
|
|
97
|
+
// Get current value
|
|
98
|
+
const value: Accessor<readonly string[]> = () => {
|
|
99
|
+
const p = getProps();
|
|
100
|
+
return isControlled() ? (p.value ?? []) : internalValue();
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
// Check if required (true if isRequired and no items selected)
|
|
104
|
+
const isRequired: Accessor<boolean> = () => {
|
|
105
|
+
const p = getProps();
|
|
106
|
+
return !!p.isRequired && value().length === 0;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// Check if invalid
|
|
110
|
+
const isInvalid = () => {
|
|
111
|
+
return getProps().isInvalid ?? false;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// Set value
|
|
115
|
+
function setValue(newValue: string[]): void {
|
|
116
|
+
const p = getProps();
|
|
117
|
+
if (p.isReadOnly || p.isDisabled) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (!isControlled()) {
|
|
122
|
+
setInternalValue(newValue);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
p.onChange?.(newValue);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Check if value is selected
|
|
129
|
+
function isSelected(checkValue: string): boolean {
|
|
130
|
+
return value().includes(checkValue);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Add value
|
|
134
|
+
function addValue(addVal: string): void {
|
|
135
|
+
const p = getProps();
|
|
136
|
+
if (p.isReadOnly || p.isDisabled) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const current = value();
|
|
141
|
+
if (!current.includes(addVal)) {
|
|
142
|
+
setValue([...current, addVal]);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Remove value
|
|
147
|
+
function removeValue(removeVal: string): void {
|
|
148
|
+
const p = getProps();
|
|
149
|
+
if (p.isReadOnly || p.isDisabled) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const current = value();
|
|
154
|
+
if (current.includes(removeVal)) {
|
|
155
|
+
setValue(current.filter((v) => v !== removeVal));
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Toggle value
|
|
160
|
+
function toggleValue(toggleVal: string): void {
|
|
161
|
+
const p = getProps();
|
|
162
|
+
if (p.isReadOnly || p.isDisabled) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const current = value();
|
|
167
|
+
if (current.includes(toggleVal)) {
|
|
168
|
+
setValue(current.filter((v) => v !== toggleVal));
|
|
169
|
+
} else {
|
|
170
|
+
setValue([...current, toggleVal]);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return {
|
|
175
|
+
value,
|
|
176
|
+
defaultValue: initialProps.defaultValue ?? initialValue,
|
|
177
|
+
get isDisabled() {
|
|
178
|
+
return getProps().isDisabled ?? false;
|
|
179
|
+
},
|
|
180
|
+
get isReadOnly() {
|
|
181
|
+
return getProps().isReadOnly ?? false;
|
|
182
|
+
},
|
|
183
|
+
get isInvalid() {
|
|
184
|
+
return isInvalid();
|
|
185
|
+
},
|
|
186
|
+
isRequired,
|
|
187
|
+
isSelected,
|
|
188
|
+
setValue,
|
|
189
|
+
addValue,
|
|
190
|
+
removeValue,
|
|
191
|
+
toggleValue,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { createCheckbox } from './createCheckbox';
|
|
2
|
+
export type { AriaCheckboxProps, CheckboxAria } from './createCheckbox';
|
|
3
|
+
|
|
4
|
+
// Re-export state from solid-stately
|
|
5
|
+
export { createCheckboxGroupState } from '@proyecto-viviana/solid-stately';
|
|
6
|
+
export type { CheckboxGroupProps, CheckboxGroupState } from '@proyecto-viviana/solid-stately';
|
|
7
|
+
|
|
8
|
+
// ARIA hooks (solidaria-specific)
|
|
9
|
+
export { createCheckboxGroup, checkboxGroupData } from './createCheckboxGroup';
|
|
10
|
+
export type { AriaCheckboxGroupProps, CheckboxGroupAria } from './createCheckboxGroup';
|
|
11
|
+
|
|
12
|
+
export { createCheckboxGroupItem } from './createCheckboxGroupItem';
|
|
13
|
+
export type { AriaCheckboxGroupItemProps } from './createCheckboxGroupItem';
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
// Button
|
|
2
|
+
export {
|
|
3
|
+
createButton,
|
|
4
|
+
createToggleButton,
|
|
5
|
+
type AriaButtonProps,
|
|
6
|
+
type ButtonAria,
|
|
7
|
+
type AriaToggleButtonProps,
|
|
8
|
+
type ToggleButtonAria,
|
|
9
|
+
} from './button';
|
|
10
|
+
|
|
11
|
+
// Checkbox
|
|
12
|
+
export {
|
|
13
|
+
createCheckbox,
|
|
14
|
+
createCheckboxGroup,
|
|
15
|
+
createCheckboxGroupItem,
|
|
16
|
+
createCheckboxGroupState,
|
|
17
|
+
checkboxGroupData,
|
|
18
|
+
type AriaCheckboxProps,
|
|
19
|
+
type CheckboxAria,
|
|
20
|
+
type AriaCheckboxGroupProps,
|
|
21
|
+
type CheckboxGroupAria,
|
|
22
|
+
type AriaCheckboxGroupItemProps,
|
|
23
|
+
type CheckboxGroupProps,
|
|
24
|
+
type CheckboxGroupState,
|
|
25
|
+
} from './checkbox';
|
|
26
|
+
|
|
27
|
+
// Radio
|
|
28
|
+
export {
|
|
29
|
+
createRadio,
|
|
30
|
+
createRadioGroup,
|
|
31
|
+
createRadioGroupState,
|
|
32
|
+
radioGroupData,
|
|
33
|
+
type AriaRadioProps,
|
|
34
|
+
type RadioAria,
|
|
35
|
+
type AriaRadioGroupProps,
|
|
36
|
+
type RadioGroupAria,
|
|
37
|
+
type RadioGroupProps,
|
|
38
|
+
type RadioGroupState,
|
|
39
|
+
} from './radio';
|
|
40
|
+
|
|
41
|
+
// Interactions
|
|
42
|
+
export {
|
|
43
|
+
createPress,
|
|
44
|
+
createFocusable,
|
|
45
|
+
createFocusRing,
|
|
46
|
+
createHover,
|
|
47
|
+
type CreatePressProps,
|
|
48
|
+
type PressResult,
|
|
49
|
+
type PressEvent,
|
|
50
|
+
type CreateFocusableProps,
|
|
51
|
+
type FocusableResult,
|
|
52
|
+
type FocusRingProps,
|
|
53
|
+
type FocusRingResult,
|
|
54
|
+
type CreateHoverProps,
|
|
55
|
+
type HoverResult,
|
|
56
|
+
type HoverEvent,
|
|
57
|
+
type HoverEvents,
|
|
58
|
+
} from './interactions';
|
|
59
|
+
|
|
60
|
+
// Label
|
|
61
|
+
export {
|
|
62
|
+
createLabel,
|
|
63
|
+
createField,
|
|
64
|
+
createLabels,
|
|
65
|
+
type LabelAriaProps,
|
|
66
|
+
type LabelAria,
|
|
67
|
+
type AriaLabelingProps,
|
|
68
|
+
type LabelableProps,
|
|
69
|
+
type DOMProps,
|
|
70
|
+
type AriaFieldProps,
|
|
71
|
+
type FieldAria,
|
|
72
|
+
type HelpTextProps,
|
|
73
|
+
type ValidationResult,
|
|
74
|
+
type Validation,
|
|
75
|
+
} from './label';
|
|
76
|
+
|
|
77
|
+
// SSR
|
|
78
|
+
export { createIsSSR, createId, canUseDOM } from './ssr';
|
|
79
|
+
|
|
80
|
+
// Toggle
|
|
81
|
+
export {
|
|
82
|
+
createToggle,
|
|
83
|
+
createToggleState,
|
|
84
|
+
type AriaToggleProps,
|
|
85
|
+
type ToggleAria,
|
|
86
|
+
type ToggleStateOptions,
|
|
87
|
+
type ToggleState,
|
|
88
|
+
} from './toggle';
|
|
89
|
+
|
|
90
|
+
// Switch
|
|
91
|
+
export {
|
|
92
|
+
createSwitch,
|
|
93
|
+
type AriaSwitchProps,
|
|
94
|
+
type SwitchAria,
|
|
95
|
+
} from './switch';
|
|
96
|
+
|
|
97
|
+
// Link
|
|
98
|
+
export {
|
|
99
|
+
createLink,
|
|
100
|
+
type AriaLinkProps,
|
|
101
|
+
type LinkAria,
|
|
102
|
+
} from './link';
|
|
103
|
+
|
|
104
|
+
// TextField
|
|
105
|
+
export {
|
|
106
|
+
createTextField,
|
|
107
|
+
type AriaTextFieldProps,
|
|
108
|
+
type TextFieldAria,
|
|
109
|
+
} from './textfield';
|
|
110
|
+
|
|
111
|
+
// ProgressBar
|
|
112
|
+
export {
|
|
113
|
+
createProgressBar,
|
|
114
|
+
type AriaProgressBarProps,
|
|
115
|
+
type ProgressBarAria,
|
|
116
|
+
} from './progress';
|
|
117
|
+
|
|
118
|
+
// Separator
|
|
119
|
+
export {
|
|
120
|
+
createSeparator,
|
|
121
|
+
type AriaSeparatorProps,
|
|
122
|
+
type SeparatorAria,
|
|
123
|
+
type Orientation,
|
|
124
|
+
} from './separator';
|
|
125
|
+
|
|
126
|
+
// Utils
|
|
127
|
+
export { mergeProps, filterDOMProps, type FilterDOMPropsOptions } from './utils';
|
|
128
|
+
export { access, isAccessor, type MaybeAccessor, type MaybeAccessorValue } from './utils';
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FocusableProvider - Provides DOM props to the nearest focusable child.
|
|
3
|
+
*
|
|
4
|
+
* This is a 1-1 port of React-Aria's FocusableProvider adapted for SolidJS.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { JSX, ParentComponent } from 'solid-js';
|
|
8
|
+
import { FocusableContext, FocusableContextValue, FocusableProviderProps } from './createFocusable';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Provides DOM props to the nearest focusable child.
|
|
12
|
+
* Used to pass focus-related props through component boundaries.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```tsx
|
|
16
|
+
* import { FocusableProvider } from 'solidaria';
|
|
17
|
+
*
|
|
18
|
+
* function MyComponent() {
|
|
19
|
+
* return (
|
|
20
|
+
* <FocusableProvider onFocus={() => console.log('focused!')}>
|
|
21
|
+
* <NestedFocusableComponent />
|
|
22
|
+
* </FocusableProvider>
|
|
23
|
+
* );
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export const FocusableProvider: ParentComponent<
|
|
28
|
+
FocusableProviderProps & JSX.HTMLAttributes<HTMLElement>
|
|
29
|
+
> = (props) => {
|
|
30
|
+
const { children, ...otherProps } = props;
|
|
31
|
+
|
|
32
|
+
const context: FocusableContextValue = {
|
|
33
|
+
...otherProps,
|
|
34
|
+
ref: (_el: HTMLElement) => {
|
|
35
|
+
// Store ref if needed by parent
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<FocusableContext.Provider value={context}>
|
|
41
|
+
{children}
|
|
42
|
+
</FocusableContext.Provider>
|
|
43
|
+
);
|
|
44
|
+
};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PressEvent class that matches React-Aria's PressEvent interface.
|
|
3
|
+
* Wraps native events with press-specific data.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type PointerType = 'mouse' | 'pen' | 'touch' | 'keyboard' | 'virtual';
|
|
7
|
+
export type PressEventType = 'pressstart' | 'pressend' | 'pressup' | 'press';
|
|
8
|
+
|
|
9
|
+
export interface IPressEvent {
|
|
10
|
+
/** The type of press event being fired. */
|
|
11
|
+
type: PressEventType;
|
|
12
|
+
/** The pointer type that triggered the press event. */
|
|
13
|
+
pointerType: PointerType;
|
|
14
|
+
/** The target element of the press event. */
|
|
15
|
+
target: Element;
|
|
16
|
+
/** Whether the shift keyboard modifier was held during the press event. */
|
|
17
|
+
shiftKey: boolean;
|
|
18
|
+
/** Whether the ctrl keyboard modifier was held during the press event. */
|
|
19
|
+
ctrlKey: boolean;
|
|
20
|
+
/** Whether the meta keyboard modifier was held during the press event. */
|
|
21
|
+
metaKey: boolean;
|
|
22
|
+
/** Whether the alt keyboard modifier was held during the press event. */
|
|
23
|
+
altKey: boolean;
|
|
24
|
+
/** X position of the press relative to the target element. */
|
|
25
|
+
x: number;
|
|
26
|
+
/** Y position of the press relative to the target element. */
|
|
27
|
+
y: number;
|
|
28
|
+
/**
|
|
29
|
+
* By default, press events stop propagation to parent elements.
|
|
30
|
+
* Call continuePropagation() to allow the event to bubble up.
|
|
31
|
+
*/
|
|
32
|
+
continuePropagation(): void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* PressEvent class that provides all press event data.
|
|
37
|
+
* Based on React-Aria's PressEvent implementation.
|
|
38
|
+
*/
|
|
39
|
+
export class PressEvent implements IPressEvent {
|
|
40
|
+
type: PressEventType;
|
|
41
|
+
pointerType: PointerType;
|
|
42
|
+
target: Element;
|
|
43
|
+
shiftKey: boolean;
|
|
44
|
+
ctrlKey: boolean;
|
|
45
|
+
metaKey: boolean;
|
|
46
|
+
altKey: boolean;
|
|
47
|
+
x: number;
|
|
48
|
+
y: number;
|
|
49
|
+
|
|
50
|
+
#shouldStopPropagation = true;
|
|
51
|
+
|
|
52
|
+
constructor(
|
|
53
|
+
type: PressEventType,
|
|
54
|
+
pointerType: PointerType,
|
|
55
|
+
originalEvent: Event | null,
|
|
56
|
+
target: Element
|
|
57
|
+
) {
|
|
58
|
+
this.type = type;
|
|
59
|
+
this.pointerType = pointerType;
|
|
60
|
+
this.target = target;
|
|
61
|
+
|
|
62
|
+
// Extract modifier keys from the original event
|
|
63
|
+
const e = originalEvent as MouseEvent | KeyboardEvent | null;
|
|
64
|
+
this.shiftKey = e?.shiftKey ?? false;
|
|
65
|
+
this.ctrlKey = e?.ctrlKey ?? false;
|
|
66
|
+
this.metaKey = e?.metaKey ?? false;
|
|
67
|
+
this.altKey = e?.altKey ?? false;
|
|
68
|
+
|
|
69
|
+
// Calculate position relative to target
|
|
70
|
+
this.x = 0;
|
|
71
|
+
this.y = 0;
|
|
72
|
+
|
|
73
|
+
if (originalEvent && 'clientX' in originalEvent && target) {
|
|
74
|
+
const rect = target.getBoundingClientRect();
|
|
75
|
+
this.x = (originalEvent as MouseEvent).clientX - rect.left;
|
|
76
|
+
this.y = (originalEvent as MouseEvent).clientY - rect.top;
|
|
77
|
+
} else if (target) {
|
|
78
|
+
// For keyboard events, use center of element
|
|
79
|
+
const rect = target.getBoundingClientRect();
|
|
80
|
+
this.x = rect.width / 2;
|
|
81
|
+
this.y = rect.height / 2;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Call this to allow the press event to propagate to parent elements.
|
|
87
|
+
* By default, press events stop propagation.
|
|
88
|
+
*/
|
|
89
|
+
continuePropagation(): void {
|
|
90
|
+
this.#shouldStopPropagation = false;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Whether the event should stop propagation.
|
|
95
|
+
* Used internally by the press handler.
|
|
96
|
+
*/
|
|
97
|
+
get shouldStopPropagation(): boolean {
|
|
98
|
+
return this.#shouldStopPropagation;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Creates a PressEvent from a native event.
|
|
104
|
+
*/
|
|
105
|
+
export function createPressEvent(
|
|
106
|
+
type: PressEventType,
|
|
107
|
+
pointerType: PointerType,
|
|
108
|
+
originalEvent: Event | null,
|
|
109
|
+
target: Element
|
|
110
|
+
): PressEvent {
|
|
111
|
+
return new PressEvent(type, pointerType, originalEvent, target);
|
|
112
|
+
}
|