@cdx-ui/primitives 0.0.1-alpha.29 → 0.0.1-alpha.30
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/lib/commonjs/form-control/createFormError.js +41 -0
- package/lib/commonjs/form-control/createFormError.js.map +1 -0
- package/lib/commonjs/form-control/createFormErrorIcon.js +18 -0
- package/lib/commonjs/form-control/createFormErrorIcon.js.map +1 -0
- package/lib/commonjs/form-control/createFormErrorText.js +18 -0
- package/lib/commonjs/form-control/createFormErrorText.js.map +1 -0
- package/lib/commonjs/form-control/createFormField.js +35 -0
- package/lib/commonjs/form-control/createFormField.js.map +1 -0
- package/lib/commonjs/form-control/createFormHelper.js +41 -0
- package/lib/commonjs/form-control/createFormHelper.js.map +1 -0
- package/lib/commonjs/form-control/createFormHelperText.js +18 -0
- package/lib/commonjs/form-control/createFormHelperText.js.map +1 -0
- package/lib/commonjs/form-control/createFormLabel.js +38 -0
- package/lib/commonjs/form-control/createFormLabel.js.map +1 -0
- package/lib/commonjs/form-control/createFormRoot.js +21 -0
- package/lib/commonjs/form-control/createFormRoot.js.map +1 -0
- package/lib/commonjs/form-control/index.js +53 -0
- package/lib/commonjs/form-control/index.js.map +1 -0
- package/lib/commonjs/form-control/types.js +6 -0
- package/lib/commonjs/form-control/types.js.map +1 -0
- package/lib/commonjs/index.js +12 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/input/createInputField.js +3 -1
- package/lib/commonjs/input/createInputField.js.map +1 -1
- package/lib/commonjs/select/createSelectRoot.js +8 -5
- package/lib/commonjs/select/createSelectRoot.js.map +1 -1
- package/lib/commonjs/select/createSelectTrigger.js +50 -3
- package/lib/commonjs/select/createSelectTrigger.js.map +1 -1
- package/lib/module/form-control/createFormError.js +35 -0
- package/lib/module/form-control/createFormError.js.map +1 -0
- package/lib/module/form-control/createFormErrorIcon.js +13 -0
- package/lib/module/form-control/createFormErrorIcon.js.map +1 -0
- package/lib/module/form-control/createFormErrorText.js +13 -0
- package/lib/module/form-control/createFormErrorText.js.map +1 -0
- package/lib/module/form-control/createFormField.js +29 -0
- package/lib/module/form-control/createFormField.js.map +1 -0
- package/lib/module/form-control/createFormHelper.js +35 -0
- package/lib/module/form-control/createFormHelper.js.map +1 -0
- package/lib/module/form-control/createFormHelperText.js +13 -0
- package/lib/module/form-control/createFormHelperText.js.map +1 -0
- package/lib/module/form-control/createFormLabel.js +33 -0
- package/lib/module/form-control/createFormLabel.js.map +1 -0
- package/lib/module/form-control/createFormRoot.js +15 -0
- package/lib/module/form-control/createFormRoot.js.map +1 -0
- package/lib/module/form-control/index.js +49 -0
- package/lib/module/form-control/index.js.map +1 -0
- package/lib/module/form-control/types.js +4 -0
- package/lib/module/form-control/types.js.map +1 -0
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/input/createInputField.js +4 -2
- package/lib/module/input/createInputField.js.map +1 -1
- package/lib/module/select/createSelectRoot.js +8 -5
- package/lib/module/select/createSelectRoot.js.map +1 -1
- package/lib/module/select/createSelectTrigger.js +52 -5
- package/lib/module/select/createSelectTrigger.js.map +1 -1
- package/lib/typescript/checkbox/useCheckboxRoot.d.ts +5 -0
- package/lib/typescript/checkbox/useCheckboxRoot.d.ts.map +1 -1
- package/lib/typescript/form-control/createFormError.d.ts +5 -0
- package/lib/typescript/form-control/createFormError.d.ts.map +1 -0
- package/lib/typescript/form-control/createFormErrorIcon.d.ts +5 -0
- package/lib/typescript/form-control/createFormErrorIcon.d.ts.map +1 -0
- package/lib/typescript/form-control/createFormErrorText.d.ts +5 -0
- package/lib/typescript/form-control/createFormErrorText.d.ts.map +1 -0
- package/lib/typescript/form-control/createFormField.d.ts +6 -0
- package/lib/typescript/form-control/createFormField.d.ts.map +1 -0
- package/lib/typescript/form-control/createFormHelper.d.ts +5 -0
- package/lib/typescript/form-control/createFormHelper.d.ts.map +1 -0
- package/lib/typescript/form-control/createFormHelperText.d.ts +5 -0
- package/lib/typescript/form-control/createFormHelperText.d.ts.map +1 -0
- package/lib/typescript/form-control/createFormLabel.d.ts +8 -0
- package/lib/typescript/form-control/createFormLabel.d.ts.map +1 -0
- package/lib/typescript/form-control/createFormRoot.d.ts +6 -0
- package/lib/typescript/form-control/createFormRoot.d.ts.map +1 -0
- package/lib/typescript/form-control/index.d.ts +14 -0
- package/lib/typescript/form-control/index.d.ts.map +1 -0
- package/lib/typescript/form-control/types.d.ts +73 -0
- package/lib/typescript/form-control/types.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +1 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/input/createInputField.d.ts.map +1 -1
- package/lib/typescript/select/createSelectRoot.d.ts.map +1 -1
- package/lib/typescript/select/createSelectTrigger.d.ts.map +1 -1
- package/lib/typescript/select/types.d.ts +4 -0
- package/lib/typescript/select/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/form-control/createFormError.tsx +32 -0
- package/src/form-control/createFormErrorIcon.tsx +9 -0
- package/src/form-control/createFormErrorText.tsx +9 -0
- package/src/form-control/createFormField.tsx +27 -0
- package/src/form-control/createFormHelper.tsx +28 -0
- package/src/form-control/createFormHelperText.tsx +9 -0
- package/src/form-control/createFormLabel.tsx +30 -0
- package/src/form-control/createFormRoot.tsx +13 -0
- package/src/form-control/index.tsx +71 -0
- package/src/form-control/types.tsx +92 -0
- package/src/index.ts +1 -0
- package/src/input/createInputField.tsx +8 -2
- package/src/select/createSelectRoot.tsx +7 -3
- package/src/select/createSelectTrigger.tsx +55 -4
- package/src/select/types.ts +4 -0
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
Platform,
|
|
7
7
|
type TextInputKeyPressEvent,
|
|
8
8
|
} from 'react-native';
|
|
9
|
-
import { mergeRefs, useFormControl } from '@cdx-ui/utils';
|
|
9
|
+
import { mergeRefs, useFormControl, useFormControlContext } from '@cdx-ui/utils';
|
|
10
10
|
import { dataAttributes } from '../utils/dataAttributes';
|
|
11
11
|
import { useInputContext } from './context';
|
|
12
12
|
import type { IInputProps } from './types';
|
|
@@ -47,12 +47,17 @@ export const createInputField = <T,>(BaseInputField: React.ComponentType<T>) =>
|
|
|
47
47
|
id: props.id,
|
|
48
48
|
});
|
|
49
49
|
|
|
50
|
+
const field = useFormControlContext();
|
|
51
|
+
|
|
50
52
|
const handleFocus = (focusState: boolean, callback: any) => {
|
|
51
53
|
setIsFocused(focusState);
|
|
52
54
|
callback();
|
|
53
55
|
};
|
|
54
56
|
|
|
55
|
-
const mergedRef =
|
|
57
|
+
const mergedRef =
|
|
58
|
+
Platform.OS === 'web'
|
|
59
|
+
? mergeRefs(ref, inputFieldRef)
|
|
60
|
+
: mergeRefs(ref, inputFieldRef, field.inputRef);
|
|
56
61
|
|
|
57
62
|
const isEffectivelyDisabled = isDisabled || inputProps.disabled;
|
|
58
63
|
|
|
@@ -67,6 +72,7 @@ export const createInputField = <T,>(BaseInputField: React.ComponentType<T>) =>
|
|
|
67
72
|
|
|
68
73
|
return (
|
|
69
74
|
<BaseInputField
|
|
75
|
+
{...inputProps}
|
|
70
76
|
{...(props as T)}
|
|
71
77
|
type={type}
|
|
72
78
|
{...dataAttributes({
|
|
@@ -68,9 +68,11 @@ export const createSelectRoot = <T,>(BaseRoot: React.ComponentType<T>) =>
|
|
|
68
68
|
const [activeValue, setActiveValue] = useState<string | undefined>(undefined);
|
|
69
69
|
|
|
70
70
|
const triggerRef = useRef<any>(null);
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
const
|
|
71
|
+
const reactId = useId();
|
|
72
|
+
/** Align with `useFormControl` default id (`${field.id}-input`) when inside `Form.Field`. */
|
|
73
|
+
const triggerId = formControlProps.id ?? `select-trigger-${reactId}`;
|
|
74
|
+
const contentId = `select-content-${reactId}`;
|
|
75
|
+
const ariaDescribedBy = formControlProps['aria-describedby'];
|
|
74
76
|
|
|
75
77
|
const contextValue = useMemo(
|
|
76
78
|
() => ({
|
|
@@ -90,6 +92,7 @@ export const createSelectRoot = <T,>(BaseRoot: React.ComponentType<T>) =>
|
|
|
90
92
|
activeValue,
|
|
91
93
|
setActiveValue,
|
|
92
94
|
accessibilityLabel,
|
|
95
|
+
ariaDescribedBy,
|
|
93
96
|
}),
|
|
94
97
|
[
|
|
95
98
|
open,
|
|
@@ -106,6 +109,7 @@ export const createSelectRoot = <T,>(BaseRoot: React.ComponentType<T>) =>
|
|
|
106
109
|
triggerId,
|
|
107
110
|
activeValue,
|
|
108
111
|
accessibilityLabel,
|
|
112
|
+
ariaDescribedBy,
|
|
109
113
|
],
|
|
110
114
|
);
|
|
111
115
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type React from 'react';
|
|
2
|
-
import { forwardRef, useCallback, useMemo } from 'react';
|
|
2
|
+
import { forwardRef, useCallback, useLayoutEffect, useMemo, useRef } from 'react';
|
|
3
3
|
import { type GestureResponderEvent, Platform } from 'react-native';
|
|
4
|
-
import { composeEventHandlers, mergeRefs } from '@cdx-ui/utils';
|
|
4
|
+
import { composeEventHandlers, mergeRefs, useFormControlContext } from '@cdx-ui/utils';
|
|
5
5
|
import { useFocus, useFocusRing } from '@react-native-aria/focus';
|
|
6
6
|
import { useHover, usePress } from '@react-native-aria/interactions';
|
|
7
7
|
import type { InteractionState } from '../types';
|
|
@@ -19,6 +19,7 @@ export const createSelectTrigger = <T,>(BaseTrigger: React.ComponentType<T>) =>
|
|
|
19
19
|
isFocused: isFocusedProp,
|
|
20
20
|
isFocusVisible: isFocusVisibleProp,
|
|
21
21
|
isDisabled: isDisabledProp,
|
|
22
|
+
'aria-describedby': ariaDescribedByProp,
|
|
22
23
|
...props
|
|
23
24
|
}: Omit<ISelectTriggerProps, 'children'> & {
|
|
24
25
|
children?: ((state: InteractionState) => React.ReactNode) | React.ReactNode;
|
|
@@ -37,8 +38,11 @@ export const createSelectTrigger = <T,>(BaseTrigger: React.ComponentType<T>) =>
|
|
|
37
38
|
triggerRef,
|
|
38
39
|
activeValue,
|
|
39
40
|
accessibilityLabel,
|
|
41
|
+
ariaDescribedBy: ariaDescribedByFromField,
|
|
40
42
|
} = useSelectContext();
|
|
41
43
|
|
|
44
|
+
const field = useFormControlContext();
|
|
45
|
+
|
|
42
46
|
const disabled = contextDisabled || !!isDisabledProp;
|
|
43
47
|
|
|
44
48
|
const { isFocusVisible, focusProps: focusRingProps }: any = useFocusRing();
|
|
@@ -101,7 +105,53 @@ export const createSelectTrigger = <T,>(BaseTrigger: React.ComponentType<T>) =>
|
|
|
101
105
|
],
|
|
102
106
|
);
|
|
103
107
|
|
|
104
|
-
|
|
108
|
+
/**
|
|
109
|
+
* Native: `Pressable` has no usable programmatic `.focus()` (unlike `TextInput`).
|
|
110
|
+
* `Form.Label` calls `focusInput()` → we register a bridge on `field.inputRef` that tries
|
|
111
|
+
* native focus, then opens the list (expected “tap label” behavior for a select).
|
|
112
|
+
*/
|
|
113
|
+
const labelFocusGateRef = useRef({ disabled: false, readOnly: false });
|
|
114
|
+
labelFocusGateRef.current = {
|
|
115
|
+
disabled: !!disabled,
|
|
116
|
+
readOnly: !!contextReadOnly,
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const labelFocusBridge = useMemo(
|
|
120
|
+
() => ({
|
|
121
|
+
focus() {
|
|
122
|
+
const { disabled: isOff, readOnly: isRO } = labelFocusGateRef.current;
|
|
123
|
+
if (isOff || isRO) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const node = triggerRef.current as { focus?: () => void } | null;
|
|
127
|
+
if (node != null && typeof node.focus === 'function') {
|
|
128
|
+
node.focus();
|
|
129
|
+
}
|
|
130
|
+
setOpen(true);
|
|
131
|
+
},
|
|
132
|
+
}),
|
|
133
|
+
[setOpen],
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
useLayoutEffect(() => {
|
|
137
|
+
if (Platform.OS === 'web' || !field.inputRef) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const r = field.inputRef;
|
|
141
|
+
r.current = labelFocusBridge as unknown as typeof r.current;
|
|
142
|
+
return () => {
|
|
143
|
+
if (r.current === labelFocusBridge) {
|
|
144
|
+
r.current = null;
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
}, [field.inputRef, labelFocusBridge]);
|
|
148
|
+
|
|
149
|
+
const mergedRef =
|
|
150
|
+
Platform.OS === 'web' ? mergeRefs(ref, triggerRef) : mergeRefs(ref, triggerRef);
|
|
151
|
+
|
|
152
|
+
const ariaDescribedBy = [ariaDescribedByFromField, ariaDescribedByProp]
|
|
153
|
+
.filter(Boolean)
|
|
154
|
+
.join(' ');
|
|
105
155
|
|
|
106
156
|
const webKeyboardProps =
|
|
107
157
|
Platform.OS === 'web'
|
|
@@ -123,7 +173,7 @@ export const createSelectTrigger = <T,>(BaseTrigger: React.ComponentType<T>) =>
|
|
|
123
173
|
aria-required={contextRequired || undefined}
|
|
124
174
|
aria-invalid={contextInvalid || undefined}
|
|
125
175
|
aria-readonly={contextReadOnly || undefined}
|
|
126
|
-
|
|
176
|
+
aria-describedby={ariaDescribedBy || undefined}
|
|
127
177
|
{...dataAttributes({
|
|
128
178
|
hover: interactionState.hover,
|
|
129
179
|
focus: interactionState.focus,
|
|
@@ -138,6 +188,7 @@ export const createSelectTrigger = <T,>(BaseTrigger: React.ComponentType<T>) =>
|
|
|
138
188
|
})}
|
|
139
189
|
disabled={disabled}
|
|
140
190
|
{...(props as T)}
|
|
191
|
+
id={triggerId}
|
|
141
192
|
onPress={composeEventHandlers(props?.onPress, handlePress)}
|
|
142
193
|
onPressIn={composeEventHandlers(props?.onPressIn, pressProps.onPressIn)}
|
|
143
194
|
onPressOut={composeEventHandlers(props?.onPressOut, pressProps.onPressOut)}
|
package/src/select/types.ts
CHANGED
|
@@ -31,6 +31,8 @@ export interface ISelectTriggerProps extends PressableProps {
|
|
|
31
31
|
isFocused?: boolean;
|
|
32
32
|
isFocusVisible?: boolean;
|
|
33
33
|
isDisabled?: boolean;
|
|
34
|
+
/** @platform web — merged with form field helper/error ids */
|
|
35
|
+
'aria-describedby'?: string;
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
export interface ISelectValueProps {
|
|
@@ -88,6 +90,8 @@ export interface SelectContextValue {
|
|
|
88
90
|
activeValue: string | undefined;
|
|
89
91
|
setActiveValue: (value: string | undefined) => void;
|
|
90
92
|
accessibilityLabel: string | undefined;
|
|
93
|
+
/** From `Form.Field` via `useFormControl` on the select root (helper/error `aria-describedby`). */
|
|
94
|
+
ariaDescribedBy?: string;
|
|
91
95
|
}
|
|
92
96
|
|
|
93
97
|
export type ISelectComponentType<
|