@cdx-ui/primitives 0.0.1-beta.6 → 0.0.1-beta.60
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/README.md +51 -25
- package/lib/commonjs/CLAUDE.md +32 -0
- package/lib/commonjs/avatar/createAvatarText.js +9 -1
- package/lib/commonjs/avatar/createAvatarText.js.map +1 -1
- package/lib/commonjs/button/createButtonText.js +1 -0
- package/lib/commonjs/button/createButtonText.js.map +1 -1
- package/lib/commonjs/checkbox/createCheckboxRoot.web.js +8 -3
- package/lib/commonjs/checkbox/createCheckboxRoot.web.js.map +1 -1
- package/lib/commonjs/chip/createChipRoot.js +132 -0
- package/lib/commonjs/chip/createChipRoot.js.map +1 -0
- package/lib/commonjs/chip/index.js +16 -0
- package/lib/commonjs/chip/index.js.map +1 -0
- package/lib/commonjs/chip/types.js +6 -0
- package/lib/commonjs/chip/types.js.map +1 -0
- package/lib/commonjs/dialog/createDialogContent.js +7 -127
- package/lib/commonjs/dialog/createDialogContent.js.map +1 -1
- package/lib/commonjs/dialog/createDialogPopup.js +157 -0
- package/lib/commonjs/dialog/createDialogPopup.js.map +1 -0
- package/lib/commonjs/dialog/index.js +8 -8
- package/lib/commonjs/dialog/index.js.map +1 -1
- package/lib/commonjs/field/createFieldLabel.js.map +1 -1
- package/lib/commonjs/index.js +45 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/input/createInputField.js +5 -0
- package/lib/commonjs/input/createInputField.js.map +1 -1
- package/lib/commonjs/link/context.js +11 -0
- package/lib/commonjs/link/context.js.map +1 -0
- package/lib/commonjs/link/createLink.js +28 -14
- package/lib/commonjs/link/createLink.js.map +1 -1
- package/lib/commonjs/link/index.js +13 -0
- package/lib/commonjs/link/index.js.map +1 -1
- package/lib/commonjs/list-item/createListItemRoot.js +64 -37
- package/lib/commonjs/list-item/createListItemRoot.js.map +1 -1
- package/lib/commonjs/list-item/index.js.map +1 -1
- package/lib/commonjs/radio/context.js +14 -0
- package/lib/commonjs/radio/context.js.map +1 -0
- package/lib/commonjs/radio/createRadioGroup.js +66 -0
- package/lib/commonjs/radio/createRadioGroup.js.map +1 -0
- package/lib/commonjs/radio/createRadioIndicator.js +43 -0
- package/lib/commonjs/radio/createRadioIndicator.js.map +1 -0
- package/lib/commonjs/radio/createRadioLabel.js +38 -0
- package/lib/commonjs/radio/createRadioLabel.js.map +1 -0
- package/lib/commonjs/radio/createRadioRoot.js +95 -0
- package/lib/commonjs/radio/createRadioRoot.js.map +1 -0
- package/lib/commonjs/radio/createRadioRoot.web.js +87 -0
- package/lib/commonjs/radio/createRadioRoot.web.js.map +1 -0
- package/lib/commonjs/radio/index.js +26 -0
- package/lib/commonjs/radio/index.js.map +1 -0
- package/lib/commonjs/radio/types.js +6 -0
- package/lib/commonjs/radio/types.js.map +1 -0
- package/lib/commonjs/radio/useRadioRoot.js +64 -0
- package/lib/commonjs/radio/useRadioRoot.js.map +1 -0
- package/lib/commonjs/select/createSelectIcon.js +3 -1
- package/lib/commonjs/select/createSelectIcon.js.map +1 -1
- package/lib/commonjs/select/createSelectTrigger.js +17 -4
- package/lib/commonjs/select/createSelectTrigger.js.map +1 -1
- package/lib/commonjs/tile/context.js +30 -0
- package/lib/commonjs/tile/context.js.map +1 -0
- package/lib/commonjs/tile/createTileContent.js +30 -0
- package/lib/commonjs/tile/createTileContent.js.map +1 -0
- package/lib/commonjs/tile/createTileDescription.js +28 -0
- package/lib/commonjs/tile/createTileDescription.js.map +1 -0
- package/lib/commonjs/tile/createTileGroup.js +112 -0
- package/lib/commonjs/tile/createTileGroup.js.map +1 -0
- package/lib/commonjs/tile/createTileIndicator.js +46 -0
- package/lib/commonjs/tile/createTileIndicator.js.map +1 -0
- package/lib/commonjs/tile/createTileLeadingSlot.js +34 -0
- package/lib/commonjs/tile/createTileLeadingSlot.js.map +1 -0
- package/lib/commonjs/tile/createTileRoot.js +133 -0
- package/lib/commonjs/tile/createTileRoot.js.map +1 -0
- package/lib/commonjs/{dialog/createDialogBody.js → tile/createTileTitle.js} +18 -11
- package/lib/commonjs/tile/createTileTitle.js.map +1 -0
- package/lib/commonjs/tile/createTileTrailingSlot.js +35 -0
- package/lib/commonjs/tile/createTileTrailingSlot.js.map +1 -0
- package/lib/commonjs/tile/index.js +55 -0
- package/lib/commonjs/tile/index.js.map +1 -0
- package/lib/commonjs/tile/types.js +6 -0
- package/lib/commonjs/tile/types.js.map +1 -0
- package/lib/commonjs/utils/dataAttributes.js +6 -25
- package/lib/commonjs/utils/dataAttributes.js.map +1 -1
- package/lib/commonjs/utils/domDataAttributes.js +34 -0
- package/lib/commonjs/utils/domDataAttributes.js.map +1 -0
- package/lib/module/CLAUDE.md +32 -0
- package/lib/module/avatar/createAvatarText.js +9 -1
- package/lib/module/avatar/createAvatarText.js.map +1 -1
- package/lib/module/button/createButtonText.js +1 -0
- package/lib/module/button/createButtonText.js.map +1 -1
- package/lib/module/checkbox/createCheckboxRoot.web.js +8 -2
- package/lib/module/checkbox/createCheckboxRoot.web.js.map +1 -1
- package/lib/module/chip/createChipRoot.js +126 -0
- package/lib/module/chip/createChipRoot.js.map +1 -0
- package/lib/module/chip/index.js +12 -0
- package/lib/module/chip/index.js.map +1 -0
- package/lib/module/chip/types.js +4 -0
- package/lib/module/chip/types.js.map +1 -0
- package/lib/module/dialog/createDialogContent.js +7 -128
- package/lib/module/dialog/createDialogContent.js.map +1 -1
- package/lib/module/dialog/createDialogPopup.js +152 -0
- package/lib/module/dialog/createDialogPopup.js.map +1 -0
- package/lib/module/dialog/index.js +8 -8
- package/lib/module/dialog/index.js.map +1 -1
- package/lib/module/field/createFieldLabel.js.map +1 -1
- package/lib/module/index.js +4 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/input/createInputField.js +5 -0
- package/lib/module/input/createInputField.js.map +1 -1
- package/lib/module/link/context.js +5 -0
- package/lib/module/link/context.js.map +1 -0
- package/lib/module/link/createLink.js +29 -15
- package/lib/module/link/createLink.js.map +1 -1
- package/lib/module/link/index.js +1 -0
- package/lib/module/link/index.js.map +1 -1
- package/lib/module/list-item/createListItemRoot.js +64 -37
- package/lib/module/list-item/createListItemRoot.js.map +1 -1
- package/lib/module/list-item/index.js.map +1 -1
- package/lib/module/radio/context.js +7 -0
- package/lib/module/radio/context.js.map +1 -0
- package/lib/module/radio/createRadioGroup.js +61 -0
- package/lib/module/radio/createRadioGroup.js.map +1 -0
- package/lib/module/radio/createRadioIndicator.js +38 -0
- package/lib/module/radio/createRadioIndicator.js.map +1 -0
- package/lib/module/radio/createRadioLabel.js +33 -0
- package/lib/module/radio/createRadioLabel.js.map +1 -0
- package/lib/module/radio/createRadioRoot.js +90 -0
- package/lib/module/radio/createRadioRoot.js.map +1 -0
- package/lib/module/radio/createRadioRoot.web.js +82 -0
- package/lib/module/radio/createRadioRoot.web.js.map +1 -0
- package/lib/module/radio/index.js +22 -0
- package/lib/module/radio/index.js.map +1 -0
- package/lib/module/radio/types.js +4 -0
- package/lib/module/radio/types.js.map +1 -0
- package/lib/module/radio/useRadioRoot.js +60 -0
- package/lib/module/radio/useRadioRoot.js.map +1 -0
- package/lib/module/select/createSelectIcon.js +3 -1
- package/lib/module/select/createSelectIcon.js.map +1 -1
- package/lib/module/select/createSelectTrigger.js +19 -6
- package/lib/module/select/createSelectTrigger.js.map +1 -1
- package/lib/module/tile/context.js +21 -0
- package/lib/module/tile/context.js.map +1 -0
- package/lib/module/tile/createTileContent.js +24 -0
- package/lib/module/tile/createTileContent.js.map +1 -0
- package/lib/module/tile/createTileDescription.js +22 -0
- package/lib/module/tile/createTileDescription.js.map +1 -0
- package/lib/module/tile/createTileGroup.js +106 -0
- package/lib/module/tile/createTileGroup.js.map +1 -0
- package/lib/module/tile/createTileIndicator.js +40 -0
- package/lib/module/tile/createTileIndicator.js.map +1 -0
- package/lib/module/tile/createTileLeadingSlot.js +28 -0
- package/lib/module/tile/createTileLeadingSlot.js.map +1 -0
- package/lib/module/tile/createTileRoot.js +127 -0
- package/lib/module/tile/createTileRoot.js.map +1 -0
- package/lib/module/tile/createTileTitle.js +22 -0
- package/lib/module/tile/createTileTitle.js.map +1 -0
- package/lib/module/tile/createTileTrailingSlot.js +29 -0
- package/lib/module/tile/createTileTrailingSlot.js.map +1 -0
- package/lib/module/tile/index.js +39 -0
- package/lib/module/tile/index.js.map +1 -0
- package/lib/module/tile/types.js +4 -0
- package/lib/module/tile/types.js.map +1 -0
- package/lib/module/utils/dataAttributes.js +4 -22
- package/lib/module/utils/dataAttributes.js.map +1 -1
- package/lib/module/utils/domDataAttributes.js +30 -0
- package/lib/module/utils/domDataAttributes.js.map +1 -0
- package/lib/typescript/avatar/createAvatarText.d.ts.map +1 -1
- package/lib/typescript/button/createButtonText.d.ts.map +1 -1
- package/lib/typescript/checkbox/createCheckboxRoot.web.d.ts +4 -0
- package/lib/typescript/checkbox/createCheckboxRoot.web.d.ts.map +1 -1
- package/lib/typescript/checkbox/useCheckboxRoot.d.ts +2 -0
- package/lib/typescript/checkbox/useCheckboxRoot.d.ts.map +1 -1
- package/lib/typescript/chip/createChipRoot.d.ts +4 -0
- package/lib/typescript/chip/createChipRoot.d.ts.map +1 -0
- package/lib/typescript/chip/index.d.ts +9 -0
- package/lib/typescript/chip/index.d.ts.map +1 -0
- package/lib/typescript/chip/types.d.ts +17 -0
- package/lib/typescript/chip/types.d.ts.map +1 -0
- package/lib/typescript/dialog/createDialogContent.d.ts +2 -2
- package/lib/typescript/dialog/createDialogContent.d.ts.map +1 -1
- package/lib/typescript/dialog/createDialogPopup.d.ts +4 -0
- package/lib/typescript/dialog/createDialogPopup.d.ts.map +1 -0
- package/lib/typescript/dialog/index.d.ts +5 -5
- package/lib/typescript/dialog/index.d.ts.map +1 -1
- package/lib/typescript/dialog/types.d.ts +16 -5
- package/lib/typescript/dialog/types.d.ts.map +1 -1
- package/lib/typescript/field/createFieldLabel.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +4 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/input/createInputField.d.ts.map +1 -1
- package/lib/typescript/link/context.d.ts +10 -0
- package/lib/typescript/link/context.d.ts.map +1 -0
- package/lib/typescript/link/createLink.d.ts.map +1 -1
- package/lib/typescript/link/index.d.ts +2 -0
- package/lib/typescript/link/index.d.ts.map +1 -1
- package/lib/typescript/link/types.d.ts +1 -0
- package/lib/typescript/link/types.d.ts.map +1 -1
- package/lib/typescript/list-item/createListItemRoot.d.ts.map +1 -1
- package/lib/typescript/list-item/index.d.ts +1 -1
- package/lib/typescript/list-item/index.d.ts.map +1 -1
- package/lib/typescript/list-item/types.d.ts +1 -7
- package/lib/typescript/list-item/types.d.ts.map +1 -1
- package/lib/typescript/radio/context.d.ts +21 -0
- package/lib/typescript/radio/context.d.ts.map +1 -0
- package/lib/typescript/radio/createRadioGroup.d.ts +3 -0
- package/lib/typescript/radio/createRadioGroup.d.ts.map +1 -0
- package/lib/typescript/radio/createRadioIndicator.d.ts +5 -0
- package/lib/typescript/radio/createRadioIndicator.d.ts.map +1 -0
- package/lib/typescript/radio/createRadioLabel.d.ts +5 -0
- package/lib/typescript/radio/createRadioLabel.d.ts.map +1 -0
- package/lib/typescript/radio/createRadioRoot.d.ts +3 -0
- package/lib/typescript/radio/createRadioRoot.d.ts.map +1 -0
- package/lib/typescript/radio/createRadioRoot.web.d.ts +3 -0
- package/lib/typescript/radio/createRadioRoot.web.d.ts.map +1 -0
- package/lib/typescript/radio/index.d.ts +10 -0
- package/lib/typescript/radio/index.d.ts.map +1 -0
- package/lib/typescript/radio/types.d.ts +54 -0
- package/lib/typescript/radio/types.d.ts.map +1 -0
- package/lib/typescript/radio/useRadioRoot.d.ts +151 -0
- package/lib/typescript/radio/useRadioRoot.d.ts.map +1 -0
- package/lib/typescript/select/createSelectIcon.d.ts.map +1 -1
- package/lib/typescript/select/createSelectTrigger.d.ts.map +1 -1
- package/lib/typescript/tile/context.d.ts +9 -0
- package/lib/typescript/tile/context.d.ts.map +1 -0
- package/lib/typescript/tile/createTileContent.d.ts +3 -0
- package/lib/typescript/tile/createTileContent.d.ts.map +1 -0
- package/lib/typescript/tile/createTileDescription.d.ts +3 -0
- package/lib/typescript/tile/createTileDescription.d.ts.map +1 -0
- package/lib/typescript/tile/createTileGroup.d.ts +4 -0
- package/lib/typescript/tile/createTileGroup.d.ts.map +1 -0
- package/lib/typescript/tile/createTileIndicator.d.ts +4 -0
- package/lib/typescript/tile/createTileIndicator.d.ts.map +1 -0
- package/lib/typescript/tile/createTileLeadingSlot.d.ts +4 -0
- package/lib/typescript/tile/createTileLeadingSlot.d.ts.map +1 -0
- package/lib/typescript/tile/createTileRoot.d.ts +4 -0
- package/lib/typescript/tile/createTileRoot.d.ts.map +1 -0
- package/lib/typescript/tile/createTileTitle.d.ts +3 -0
- package/lib/typescript/tile/createTileTitle.d.ts.map +1 -0
- package/lib/typescript/tile/createTileTrailingSlot.d.ts +9 -0
- package/lib/typescript/tile/createTileTrailingSlot.d.ts.map +1 -0
- package/lib/typescript/tile/index.d.ts +15 -0
- package/lib/typescript/tile/index.d.ts.map +1 -0
- package/lib/typescript/tile/types.d.ts +119 -0
- package/lib/typescript/tile/types.d.ts.map +1 -0
- package/lib/typescript/utils/dataAttributes.d.ts +4 -10
- package/lib/typescript/utils/dataAttributes.d.ts.map +1 -1
- package/lib/typescript/utils/domDataAttributes.d.ts +15 -0
- package/lib/typescript/utils/domDataAttributes.d.ts.map +1 -0
- package/package.json +5 -2
- package/src/CLAUDE.md +32 -0
- package/src/avatar/createAvatarText.tsx +10 -1
- package/src/button/createButtonText.tsx +1 -0
- package/src/checkbox/createCheckboxRoot.web.tsx +6 -2
- package/src/chip/createChipRoot.tsx +144 -0
- package/src/chip/index.ts +18 -0
- package/src/chip/types.ts +23 -0
- package/src/dialog/createDialogContent.tsx +6 -117
- package/src/dialog/createDialogPopup.tsx +146 -0
- package/src/dialog/index.tsx +16 -16
- package/src/dialog/types.ts +20 -9
- package/src/field/createFieldLabel.tsx +4 -1
- package/src/index.ts +4 -0
- package/src/input/createInputField.tsx +6 -0
- package/src/link/context.tsx +10 -0
- package/src/link/createLink.tsx +41 -23
- package/src/link/index.tsx +2 -0
- package/src/link/types.ts +1 -0
- package/src/list-item/createListItemRoot.tsx +68 -36
- package/src/list-item/index.ts +0 -1
- package/src/list-item/types.ts +1 -8
- package/src/radio/context.tsx +21 -0
- package/src/radio/createRadioGroup.tsx +67 -0
- package/src/radio/createRadioIndicator.tsx +32 -0
- package/src/radio/createRadioLabel.tsx +28 -0
- package/src/radio/createRadioRoot.tsx +100 -0
- package/src/radio/createRadioRoot.web.tsx +81 -0
- package/src/radio/index.ts +37 -0
- package/src/radio/types.ts +67 -0
- package/src/radio/useRadioRoot.ts +69 -0
- package/src/select/createSelectIcon.tsx +2 -1
- package/src/select/createSelectTrigger.tsx +26 -3
- package/src/tile/context.tsx +23 -0
- package/src/tile/createTileContent.tsx +23 -0
- package/src/tile/createTileDescription.tsx +19 -0
- package/src/tile/createTileGroup.tsx +134 -0
- package/src/tile/createTileIndicator.tsx +38 -0
- package/src/tile/createTileLeadingSlot.tsx +30 -0
- package/src/tile/createTileRoot.tsx +124 -0
- package/src/tile/createTileTitle.tsx +19 -0
- package/src/tile/createTileTrailingSlot.tsx +25 -0
- package/src/tile/index.ts +88 -0
- package/src/tile/types.ts +153 -0
- package/src/utils/dataAttributes.ts +4 -25
- package/src/utils/domDataAttributes.ts +32 -0
- package/lib/commonjs/dialog/createDialogBody.js.map +0 -1
- package/lib/module/dialog/createDialogBody.js +0 -15
- package/lib/module/dialog/createDialogBody.js.map +0 -1
- package/lib/typescript/dialog/createDialogBody.d.ts +0 -4
- package/lib/typescript/dialog/createDialogBody.d.ts.map +0 -1
- package/src/dialog/createDialogBody.tsx +0 -11
package/src/CLAUDE.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Primitive Authoring
|
|
2
|
+
|
|
3
|
+
Auto-loaded when working in `packages/primitives/src/**`.
|
|
4
|
+
|
|
5
|
+
Primitives in `@cdx-ui/primitives` are unstyled behavioral components that handle accessibility and interactions. Read the full guides before making changes:
|
|
6
|
+
|
|
7
|
+
- `docs/internal/practices/composition.md` — compound component structure
|
|
8
|
+
- `docs/internal/practices/types.md` — `I*Props` convention, extending RN base types
|
|
9
|
+
- `docs/internal/practices/accessibility.md` — ARIA roles, keyboard nav, screen reader support
|
|
10
|
+
- `docs/internal/practices/as-child.md` — `asChild` prop pattern
|
|
11
|
+
|
|
12
|
+
## Pattern summary
|
|
13
|
+
|
|
14
|
+
Each primitive follows the factory function pattern (see `createButtonRoot.tsx` as the canonical example):
|
|
15
|
+
|
|
16
|
+
1. **Factory function** — `export const createComponentPart = <T,>(BaseComponent: ComponentType<T>) => forwardRef(...)` — accepts a host element type, returns a wrapped component with behavior
|
|
17
|
+
2. **Interaction hooks** — `useFocus`, `useFocusRing`, `useHover`, `usePress` from `@react-native-aria/*`
|
|
18
|
+
3. **Data attributes** — `dataAttributes({ hover, focus, active, disabled, focusVisible })` emitted on the rendered element for Uniwind `data-[state]` selectors
|
|
19
|
+
4. **Context provider** — interaction state exposed via context so child primitives can react to parent state
|
|
20
|
+
5. **Barrel export** — `index.tsx` re-exports the factory, context, and types
|
|
21
|
+
|
|
22
|
+
## Key rules
|
|
23
|
+
|
|
24
|
+
- **Unstyled** — no visual styling (no `className`, no color/spacing classes). Only behavioral and a11y concerns.
|
|
25
|
+
- **`I*Props` convention** — public prop types are aliased with `I` prefix (e.g., `IButtonProps`, `ICheckboxProps`), exported from `types.ts`
|
|
26
|
+
- **`Interface*Props` pattern** — the full interface (e.g., `InterfaceButtonProps`, `InterfaceCheckbox`) extends the appropriate RN base type (`PressableProps`, `ViewProps`, etc.) with interaction state booleans (`isHovered`, `isActive`, `isFocused`, `isFocusVisible`, `isDisabled`); re-exported as `I*Props` (e.g., `export type IButtonProps = InterfaceButtonProps`)
|
|
27
|
+
- **`dataAttributes()` helper** — always use the helper from `../utils/dataAttributes` to emit `data-*` props; it handles platform differences (native vs web)
|
|
28
|
+
- **`composeEventHandlers`** from `@cdx-ui/utils` — merge consumer handlers with internal handlers; never silently override
|
|
29
|
+
- **`forwardRef`** on every component — refs forwarded to the underlying host element
|
|
30
|
+
- **ARIA roles** — set `role` with a sensible default (e.g., `role={props?.role || 'button'}`)
|
|
31
|
+
- **Render children as function** — support `children` as `(state: InteractionState) => ReactNode` for state-driven rendering
|
|
32
|
+
- **Consumable independently** of `@cdx-ui/components` — no imports from the styled layer
|
|
@@ -4,7 +4,16 @@ import { useAvatarContext } from './context';
|
|
|
4
4
|
import type { IAvatarTextProps } from './types';
|
|
5
5
|
|
|
6
6
|
function getInitials(name: string): string {
|
|
7
|
-
const
|
|
7
|
+
const trimmed = name.trim();
|
|
8
|
+
const words = trimmed.split(/\s+/);
|
|
9
|
+
|
|
10
|
+
// Pre-composed initials (e.g. "JD", "AB") — single token, at most 2 chars.
|
|
11
|
+
// Figma Code Connect surfaces the raw text property which is already initials;
|
|
12
|
+
// passing them through a derivation step would truncate to the first letter.
|
|
13
|
+
if (words.length === 1 && trimmed.length <= 2) {
|
|
14
|
+
return trimmed.toUpperCase();
|
|
15
|
+
}
|
|
16
|
+
|
|
8
17
|
let result = '';
|
|
9
18
|
for (const word of words) {
|
|
10
19
|
if (word.length > 0) {
|
|
@@ -5,6 +5,7 @@ import { useButtonContext } from './context';
|
|
|
5
5
|
import type { IButtonTextProps } from './types';
|
|
6
6
|
|
|
7
7
|
// TODO: Rename to ButtonLabel
|
|
8
|
+
// TODO: Can we remove the dataAttributes here?
|
|
8
9
|
|
|
9
10
|
export const createButtonText = <T,>(BaseButtonText: React.ComponentType<T>) =>
|
|
10
11
|
forwardRef<unknown, IButtonTextProps>(({ children, ...props }, ref) => {
|
|
@@ -6,8 +6,10 @@ import { CheckboxProvider } from './context';
|
|
|
6
6
|
import type { ICheckboxProps } from './types';
|
|
7
7
|
import { useCheckboxRoot } from './useCheckboxRoot';
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Web uses a native checkbox input (VisuallyHidden) for ARIA + focus management. The outer
|
|
11
|
+
* `Pressable` must not be in the tab order or each control consumes two Tab stops (wrapper + input).
|
|
12
|
+
*/
|
|
11
13
|
export const createCheckboxRoot = <T,>(BaseCheckbox: React.ComponentType<T>) =>
|
|
12
14
|
forwardRef(({ children, ...props }: ICheckboxProps, ref?: React.Ref<T>) => {
|
|
13
15
|
const {
|
|
@@ -41,6 +43,8 @@ export const createCheckboxRoot = <T,>(BaseCheckbox: React.ComponentType<T>) =>
|
|
|
41
43
|
<BaseCheckbox
|
|
42
44
|
{...(combinedProps as T)}
|
|
43
45
|
ref={mergedRef}
|
|
46
|
+
// Native input is the only tab stop; Pressable is still clickable (mouse / screen reader).
|
|
47
|
+
tabIndex={-1}
|
|
44
48
|
role="label"
|
|
45
49
|
// eslint-disable-next-line react-native-a11y/has-valid-accessibility-role
|
|
46
50
|
accessibilityRole="label"
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import React, { forwardRef } from 'react';
|
|
2
|
+
import type { PressableProps } from 'react-native';
|
|
3
|
+
import { composeEventHandlers, mergeRefs } from '@cdx-ui/utils';
|
|
4
|
+
import { useFocus, useFocusRing } from '@react-native-aria/focus';
|
|
5
|
+
import { useHover, usePress } from '@react-native-aria/interactions';
|
|
6
|
+
import { dataAttributes } from '../utils/dataAttributes';
|
|
7
|
+
import type { IChipPressablePassthrough, IChipProps } from './types';
|
|
8
|
+
|
|
9
|
+
const rowStyle = {
|
|
10
|
+
flexDirection: 'row' as const,
|
|
11
|
+
alignItems: 'center' as const,
|
|
12
|
+
alignSelf: 'flex-start' as const,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const chipSlotAttrs = dataAttributes({ slot: 'chip' });
|
|
16
|
+
|
|
17
|
+
export const createChipRoot = <V, P>(
|
|
18
|
+
BaseView: React.ComponentType<V>,
|
|
19
|
+
BasePressable: React.ComponentType<P>,
|
|
20
|
+
) =>
|
|
21
|
+
forwardRef(
|
|
22
|
+
(
|
|
23
|
+
{
|
|
24
|
+
asChild = false,
|
|
25
|
+
children,
|
|
26
|
+
onPress,
|
|
27
|
+
disabled = false,
|
|
28
|
+
accessibilityRole,
|
|
29
|
+
style,
|
|
30
|
+
...restProps
|
|
31
|
+
}: IChipProps,
|
|
32
|
+
ref: React.Ref<unknown>,
|
|
33
|
+
) => {
|
|
34
|
+
const childOnPress = React.isValidElement(children)
|
|
35
|
+
? ((children.props as { onPress?: unknown }).onPress as IChipProps['onPress'] | undefined)
|
|
36
|
+
: undefined;
|
|
37
|
+
|
|
38
|
+
const asChildInteractive = asChild && React.isValidElement(children);
|
|
39
|
+
const isPressableRoot = !!onPress && !asChildInteractive;
|
|
40
|
+
|
|
41
|
+
const pressState = usePress({
|
|
42
|
+
isDisabled: !isPressableRoot || disabled,
|
|
43
|
+
});
|
|
44
|
+
const isPressed = pressState.isPressed;
|
|
45
|
+
const pressProps = pressState.pressProps as Pick<PressableProps, 'onPressIn' | 'onPressOut'>;
|
|
46
|
+
|
|
47
|
+
const { hoverProps, isHovered } = useHover();
|
|
48
|
+
const { isFocused, focusProps } = useFocus();
|
|
49
|
+
const { isFocusVisible, focusProps: focusRingProps } = useFocusRing() as {
|
|
50
|
+
isFocusVisible: boolean;
|
|
51
|
+
focusProps: typeof focusProps;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
if (asChildInteractive) {
|
|
55
|
+
const child = children as React.ReactElement<Record<string, unknown>>;
|
|
56
|
+
|
|
57
|
+
const childDisabled = !!(child.props as { disabled?: boolean }).disabled;
|
|
58
|
+
const resolvedDisabled = disabled || childDisabled;
|
|
59
|
+
|
|
60
|
+
const cloneProps: Record<string, unknown> = {
|
|
61
|
+
...restProps,
|
|
62
|
+
...chipSlotAttrs,
|
|
63
|
+
...dataAttributes({
|
|
64
|
+
active: false,
|
|
65
|
+
hover: false,
|
|
66
|
+
disabled: resolvedDisabled,
|
|
67
|
+
focused: false,
|
|
68
|
+
focusVisible: false,
|
|
69
|
+
}),
|
|
70
|
+
...(resolvedDisabled && { accessibilityState: { disabled: true } }),
|
|
71
|
+
disabled: resolvedDisabled,
|
|
72
|
+
ref: mergeRefs(ref, child.props.ref as React.Ref<unknown>),
|
|
73
|
+
style: [rowStyle, style, child.props.style],
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
if (resolvedDisabled) {
|
|
77
|
+
cloneProps.onPress = undefined;
|
|
78
|
+
} else if (onPress) {
|
|
79
|
+
cloneProps.onPress = composeEventHandlers(childOnPress, onPress);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return React.cloneElement(child, cloneProps);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (isPressableRoot) {
|
|
86
|
+
const interactionAttrs = dataAttributes({
|
|
87
|
+
active: isPressed,
|
|
88
|
+
hover: isHovered,
|
|
89
|
+
disabled,
|
|
90
|
+
focused: isFocused,
|
|
91
|
+
focusVisible: isFocusVisible,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const {
|
|
95
|
+
onPressIn: onPressInProp,
|
|
96
|
+
onPressOut: onPressOutProp,
|
|
97
|
+
onHoverIn: onHoverInProp,
|
|
98
|
+
onHoverOut: onHoverOutProp,
|
|
99
|
+
onFocus: onFocusProp,
|
|
100
|
+
onBlur: onBlurProp,
|
|
101
|
+
} = restProps as IChipPressablePassthrough & Pick<PressableProps, 'onFocus' | 'onBlur'>;
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<BasePressable
|
|
105
|
+
{...(restProps as P)}
|
|
106
|
+
{...chipSlotAttrs}
|
|
107
|
+
{...interactionAttrs}
|
|
108
|
+
accessibilityRole={accessibilityRole ?? 'button'}
|
|
109
|
+
{...(disabled && { accessibilityState: { disabled: true } })}
|
|
110
|
+
disabled={disabled}
|
|
111
|
+
ref={ref as React.Ref<P>}
|
|
112
|
+
style={[rowStyle, style]}
|
|
113
|
+
onPress={disabled ? undefined : onPress}
|
|
114
|
+
onPressIn={composeEventHandlers(onPressInProp, pressProps.onPressIn)}
|
|
115
|
+
onPressOut={composeEventHandlers(onPressOutProp, pressProps.onPressOut)}
|
|
116
|
+
onHoverIn={composeEventHandlers(onHoverInProp, hoverProps.onHoverIn)}
|
|
117
|
+
onHoverOut={composeEventHandlers(onHoverOutProp, hoverProps.onHoverOut)}
|
|
118
|
+
onFocus={composeEventHandlers(
|
|
119
|
+
composeEventHandlers(onFocusProp, focusProps.onFocus),
|
|
120
|
+
focusRingProps.onFocus,
|
|
121
|
+
)}
|
|
122
|
+
onBlur={composeEventHandlers(
|
|
123
|
+
composeEventHandlers(onBlurProp, focusProps.onBlur),
|
|
124
|
+
focusRingProps.onBlur,
|
|
125
|
+
)}
|
|
126
|
+
>
|
|
127
|
+
{children}
|
|
128
|
+
</BasePressable>
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
<BaseView
|
|
134
|
+
{...(restProps as V)}
|
|
135
|
+
{...chipSlotAttrs}
|
|
136
|
+
{...(disabled ? dataAttributes({ disabled: true }) : undefined)}
|
|
137
|
+
ref={ref as React.Ref<V>}
|
|
138
|
+
style={[rowStyle, style]}
|
|
139
|
+
>
|
|
140
|
+
{children}
|
|
141
|
+
</BaseView>
|
|
142
|
+
);
|
|
143
|
+
},
|
|
144
|
+
);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import type { PressableProps, ViewProps } from 'react-native';
|
|
3
|
+
import { createChipRoot } from './createChipRoot';
|
|
4
|
+
import type { IChipComponentType } from './types';
|
|
5
|
+
|
|
6
|
+
export type { IChipComponentType, IChipPressablePassthrough, IChipProps } from './types';
|
|
7
|
+
|
|
8
|
+
export function createChip({
|
|
9
|
+
View,
|
|
10
|
+
Pressable,
|
|
11
|
+
}: {
|
|
12
|
+
View: React.ComponentType<ViewProps>;
|
|
13
|
+
Pressable: React.ComponentType<PressableProps>;
|
|
14
|
+
}) {
|
|
15
|
+
const Chip = createChipRoot(View, Pressable);
|
|
16
|
+
Chip.displayName = 'ChipPrimitive';
|
|
17
|
+
return Chip as IChipComponentType<ViewProps | PressableProps>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ForwardRefExoticComponent, RefAttributes } from 'react';
|
|
2
|
+
import type { PressableProps, ViewProps } from 'react-native';
|
|
3
|
+
|
|
4
|
+
export type IChipPressablePassthrough = Partial<
|
|
5
|
+
Pick<PressableProps, 'onPressIn' | 'onPressOut' | 'onHoverIn' | 'onHoverOut'>
|
|
6
|
+
>;
|
|
7
|
+
|
|
8
|
+
export interface IChipProps extends ViewProps, IChipPressablePassthrough {
|
|
9
|
+
/**
|
|
10
|
+
* When set (and `asChild` is not used with a valid element), root renders as pressable.
|
|
11
|
+
*/
|
|
12
|
+
onPress?: PressableProps['onPress'];
|
|
13
|
+
/** Disables press handling when the root is pressable. */
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Merge slot + interaction props onto a single child instead of rendering the default pressable root.
|
|
17
|
+
*/
|
|
18
|
+
asChild?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type IChipComponentType<RootRef> = ForwardRefExoticComponent<
|
|
22
|
+
RefAttributes<RootRef> & IChipProps
|
|
23
|
+
>;
|
|
@@ -1,122 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { forwardRef, useCallback } from 'react';
|
|
3
|
-
import { Modal, Platform, StyleSheet, View } from 'react-native';
|
|
4
|
-
import { FocusScope } from '@react-native-aria/focus';
|
|
5
|
-
import { useEscapeKey } from '../overlay';
|
|
6
|
-
import { createPortalFn } from '../utils/createPortal';
|
|
7
|
-
import { dataAttributes } from '../utils/dataAttributes';
|
|
8
|
-
import { useDialog } from './context';
|
|
1
|
+
import React, { forwardRef } from 'react';
|
|
9
2
|
import type { IDialogContentProps } from './types';
|
|
10
3
|
|
|
11
|
-
export const createDialogContent = <
|
|
12
|
-
|
|
13
|
-
BaseOverlay: React.ComponentType<OverlayProps>,
|
|
14
|
-
) =>
|
|
15
|
-
forwardRef(({ children, forceMount = false, ...props }: IDialogContentProps, ref?: any) => {
|
|
16
|
-
const { open, onOpenChange, closeOnBackdropPress, closeOnEscKey, nativeID, role } = useDialog();
|
|
17
|
-
|
|
18
|
-
const handleClose = useCallback(() => {
|
|
19
|
-
onOpenChange(false);
|
|
20
|
-
}, [onOpenChange]);
|
|
21
|
-
|
|
22
|
-
useEscapeKey(open && closeOnEscKey, handleClose);
|
|
23
|
-
|
|
24
|
-
if (!(open || forceMount)) {
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const overlayProps = {
|
|
29
|
-
...(closeOnBackdropPress ? { onPress: handleClose } : {}),
|
|
30
|
-
accessible: false,
|
|
31
|
-
importantForAccessibility: 'no-hide-descendants' as const,
|
|
32
|
-
...dataAttributes({ slot: 'dialog-overlay' }),
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const contentSlotAttrs = dataAttributes({
|
|
36
|
-
slot: 'dialog-content',
|
|
37
|
-
state: open ? 'open' : 'closed',
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
const ariaProps = {
|
|
41
|
-
role,
|
|
42
|
-
'aria-modal': true,
|
|
43
|
-
'aria-labelledby': `${nativeID}-title`,
|
|
44
|
-
'aria-describedby': `${nativeID}-description`,
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
if (Platform.OS === 'web' && createPortalFn) {
|
|
48
|
-
return createPortalFn(
|
|
49
|
-
<FocusScope contain={open} restoreFocus autoFocus={open}>
|
|
50
|
-
<View
|
|
51
|
-
style={{
|
|
52
|
-
position: 'fixed' as any,
|
|
53
|
-
inset: 0 as any,
|
|
54
|
-
zIndex: 50,
|
|
55
|
-
display: 'flex' as any,
|
|
56
|
-
alignItems: 'center',
|
|
57
|
-
justifyContent: 'center',
|
|
58
|
-
}}
|
|
59
|
-
>
|
|
60
|
-
<BaseOverlay {...(overlayProps as any)} />
|
|
61
|
-
<BaseContent
|
|
62
|
-
ref={ref}
|
|
63
|
-
{...contentSlotAttrs}
|
|
64
|
-
{...(props as ContentProps)}
|
|
65
|
-
{...ariaProps}
|
|
66
|
-
>
|
|
67
|
-
{children}
|
|
68
|
-
</BaseContent>
|
|
69
|
-
</View>
|
|
70
|
-
</FocusScope>,
|
|
71
|
-
document.body,
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Native layout uses two layers to support both backdrop dismiss and
|
|
76
|
-
// scrollable body content. The overlay Pressable fills the Modal via
|
|
77
|
-
// absolute positioning and handles backdrop taps. A second absolute-fill
|
|
78
|
-
// View with pointerEvents="box-none" centers the content panel — "box-none"
|
|
79
|
-
// lets taps outside the panel pass through to the overlay while allowing
|
|
80
|
-
// the panel and its children to receive touches normally.
|
|
81
|
-
//
|
|
82
|
-
// Critically, the content panel must NOT claim the JS touch responder
|
|
83
|
-
// (e.g. via onStartShouldSetResponder). Doing so triggers React Native's
|
|
84
|
-
// blockNativeResponder, which suppresses the native UIScrollView /
|
|
85
|
-
// ScrollView pan gesture recognizer and breaks any ScrollView inside
|
|
86
|
-
// Dialog.Body.
|
|
87
|
-
//
|
|
88
|
-
// Static StyleSheet used here because these are structural layout concerns
|
|
89
|
-
// of the primitive's modal chrome — not themeable styles. They must remain
|
|
90
|
-
// stable regardless of the styled layer's className.
|
|
4
|
+
export const createDialogContent = <T,>(BaseContent: React.ComponentType<T>) =>
|
|
5
|
+
forwardRef<unknown, IDialogContentProps>(({ children, ...props }, ref) => {
|
|
91
6
|
return (
|
|
92
|
-
<
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
<BaseOverlay {...(overlayProps as any)} />
|
|
96
|
-
<View style={styles.center} pointerEvents="box-none">
|
|
97
|
-
<BaseContent
|
|
98
|
-
ref={ref}
|
|
99
|
-
{...contentSlotAttrs}
|
|
100
|
-
{...(props as ContentProps)}
|
|
101
|
-
{...ariaProps}
|
|
102
|
-
>
|
|
103
|
-
{children}
|
|
104
|
-
</BaseContent>
|
|
105
|
-
</View>
|
|
106
|
-
</View>
|
|
107
|
-
</FocusScope>
|
|
108
|
-
</Modal>
|
|
7
|
+
<BaseContent ref={ref} {...(props as T)}>
|
|
8
|
+
{children}
|
|
9
|
+
</BaseContent>
|
|
109
10
|
);
|
|
110
11
|
});
|
|
111
|
-
|
|
112
|
-
// Structural layout for the native Modal chrome. These are not themeable —
|
|
113
|
-
// they position the overlay and centering layers that the primitive manages
|
|
114
|
-
// internally. Matches the pattern in OverlayContainer.tsx.
|
|
115
|
-
const styles = StyleSheet.create({
|
|
116
|
-
fill: { flex: 1 },
|
|
117
|
-
center: {
|
|
118
|
-
...StyleSheet.absoluteFillObject,
|
|
119
|
-
alignItems: 'center',
|
|
120
|
-
justifyContent: 'center',
|
|
121
|
-
},
|
|
122
|
-
});
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import { forwardRef, useCallback } from 'react';
|
|
3
|
+
import { KeyboardAvoidingView, Modal, Platform, StyleSheet, View } from 'react-native';
|
|
4
|
+
import { FocusScope } from '@react-native-aria/focus';
|
|
5
|
+
import { useEscapeKey } from '../overlay';
|
|
6
|
+
import { createPortalFn } from '../utils/createPortal';
|
|
7
|
+
import { dataAttributes } from '../utils/dataAttributes';
|
|
8
|
+
import { useDialog } from './context';
|
|
9
|
+
import type { IDialogPopupProps } from './types';
|
|
10
|
+
|
|
11
|
+
export const createDialogPopup = <PopupProps, OverlayProps>(
|
|
12
|
+
BasePopup: React.ComponentType<PopupProps>,
|
|
13
|
+
BaseOverlay: React.ComponentType<OverlayProps>,
|
|
14
|
+
) =>
|
|
15
|
+
forwardRef(
|
|
16
|
+
(
|
|
17
|
+
{
|
|
18
|
+
children,
|
|
19
|
+
forceMount = false,
|
|
20
|
+
avoidKeyboard = true,
|
|
21
|
+
keyboardVerticalOffset = 0,
|
|
22
|
+
...props
|
|
23
|
+
}: IDialogPopupProps,
|
|
24
|
+
ref?: any,
|
|
25
|
+
) => {
|
|
26
|
+
const { open, onOpenChange, closeOnBackdropPress, closeOnEscKey, nativeID, role } =
|
|
27
|
+
useDialog();
|
|
28
|
+
|
|
29
|
+
const handleClose = useCallback(() => {
|
|
30
|
+
onOpenChange(false);
|
|
31
|
+
}, [onOpenChange]);
|
|
32
|
+
|
|
33
|
+
useEscapeKey(open && closeOnEscKey, handleClose);
|
|
34
|
+
|
|
35
|
+
if (!(open || forceMount)) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const overlayProps = {
|
|
40
|
+
...(closeOnBackdropPress ? { onPress: handleClose } : {}),
|
|
41
|
+
accessible: false,
|
|
42
|
+
importantForAccessibility: 'no-hide-descendants' as const,
|
|
43
|
+
...dataAttributes({ slot: 'dialog-overlay' }),
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const contentSlotAttrs = dataAttributes({
|
|
47
|
+
slot: 'dialog-popup',
|
|
48
|
+
state: open ? 'open' : 'closed',
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const ariaProps = {
|
|
52
|
+
role,
|
|
53
|
+
'aria-modal': true,
|
|
54
|
+
'aria-labelledby': `${nativeID}-title`,
|
|
55
|
+
'aria-describedby': `${nativeID}-description`,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
if (Platform.OS === 'web' && createPortalFn) {
|
|
59
|
+
return createPortalFn(
|
|
60
|
+
<FocusScope contain={open} restoreFocus autoFocus={open}>
|
|
61
|
+
<View
|
|
62
|
+
style={{
|
|
63
|
+
position: 'fixed' as any,
|
|
64
|
+
inset: 0 as any,
|
|
65
|
+
zIndex: 50,
|
|
66
|
+
display: 'flex' as any,
|
|
67
|
+
alignItems: 'center',
|
|
68
|
+
justifyContent: 'center',
|
|
69
|
+
}}
|
|
70
|
+
>
|
|
71
|
+
<BaseOverlay {...(overlayProps as any)} />
|
|
72
|
+
<BasePopup ref={ref} {...contentSlotAttrs} {...(props as PopupProps)} {...ariaProps}>
|
|
73
|
+
{children}
|
|
74
|
+
</BasePopup>
|
|
75
|
+
</View>
|
|
76
|
+
</FocusScope>,
|
|
77
|
+
document.body,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Native layout uses two layers to support both backdrop dismiss and
|
|
82
|
+
// scrollable body content. The overlay Pressable fills the Modal via
|
|
83
|
+
// absolute positioning and handles backdrop taps. A second flex layer with
|
|
84
|
+
// pointerEvents="box-none" centers the content panel — "box-none" lets taps
|
|
85
|
+
// outside the panel pass through to the overlay while allowing the panel and
|
|
86
|
+
// its children to receive touches normally.
|
|
87
|
+
//
|
|
88
|
+
// That centering layer is a KeyboardAvoidingView so the panel (including its
|
|
89
|
+
// footer/actions, which sit at the bottom of a centered, max-h-capped panel)
|
|
90
|
+
// shifts up to clear the on-screen keyboard. `behavior` is platform-specific
|
|
91
|
+
// and the layer is a no-op while no input is focused. The RN Modal renders in
|
|
92
|
+
// its own window, so this MUST live here — a KeyboardAvoidingView wrapping the
|
|
93
|
+
// <Dialog> at the usage level cannot reach this subtree. Web is unaffected
|
|
94
|
+
// (the browser handles focus scrolling; there is no occluding keyboard).
|
|
95
|
+
//
|
|
96
|
+
// Critically, the content panel must NOT claim the JS touch responder
|
|
97
|
+
// (e.g. via onStartShouldSetResponder). Doing so triggers React Native's
|
|
98
|
+
// blockNativeResponder, which suppresses the native UIScrollView /
|
|
99
|
+
// ScrollView pan gesture recognizer and breaks any ScrollView inside
|
|
100
|
+
// Dialog.Content.
|
|
101
|
+
//
|
|
102
|
+
// Static StyleSheet used here because these are structural layout concerns
|
|
103
|
+
// of the primitive's modal chrome — not themeable styles. They must remain
|
|
104
|
+
// stable regardless of the styled layer's className.
|
|
105
|
+
const keyboardBehavior = avoidKeyboard
|
|
106
|
+
? Platform.select<'padding' | 'height'>({ ios: 'padding', default: 'height' })
|
|
107
|
+
: undefined;
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<Modal visible={open} transparent statusBarTranslucent onRequestClose={handleClose}>
|
|
111
|
+
<FocusScope contain={open} restoreFocus autoFocus={open}>
|
|
112
|
+
<View style={styles.fill}>
|
|
113
|
+
<BaseOverlay {...(overlayProps as any)} />
|
|
114
|
+
<KeyboardAvoidingView
|
|
115
|
+
style={styles.center}
|
|
116
|
+
pointerEvents="box-none"
|
|
117
|
+
behavior={keyboardBehavior}
|
|
118
|
+
keyboardVerticalOffset={keyboardVerticalOffset}
|
|
119
|
+
>
|
|
120
|
+
<BasePopup
|
|
121
|
+
ref={ref}
|
|
122
|
+
{...contentSlotAttrs}
|
|
123
|
+
{...(props as PopupProps)}
|
|
124
|
+
{...ariaProps}
|
|
125
|
+
>
|
|
126
|
+
{children}
|
|
127
|
+
</BasePopup>
|
|
128
|
+
</KeyboardAvoidingView>
|
|
129
|
+
</View>
|
|
130
|
+
</FocusScope>
|
|
131
|
+
</Modal>
|
|
132
|
+
);
|
|
133
|
+
},
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
// Structural layout for the native Modal chrome. These are not themeable —
|
|
137
|
+
// they position the overlay and centering layers that the primitive manages
|
|
138
|
+
// internally. Matches the pattern in OverlayContainer.tsx.
|
|
139
|
+
const styles = StyleSheet.create({
|
|
140
|
+
fill: { flex: 1 },
|
|
141
|
+
center: {
|
|
142
|
+
flex: 1,
|
|
143
|
+
alignItems: 'center',
|
|
144
|
+
justifyContent: 'center',
|
|
145
|
+
},
|
|
146
|
+
});
|
package/src/dialog/index.tsx
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type React from 'react';
|
|
2
2
|
import { createDialogRoot } from './createDialogRoot';
|
|
3
3
|
import { createDialogTrigger } from './createDialogTrigger';
|
|
4
|
-
import {
|
|
4
|
+
import { createDialogPopup } from './createDialogPopup';
|
|
5
5
|
import { createDialogHeader } from './createDialogHeader';
|
|
6
6
|
import { createDialogTitle } from './createDialogTitle';
|
|
7
7
|
import { createDialogDescription } from './createDialogDescription';
|
|
8
|
-
import {
|
|
8
|
+
import { createDialogContent } from './createDialogContent';
|
|
9
9
|
import { createDialogFooter } from './createDialogFooter';
|
|
10
10
|
import { createDialogClose } from './createDialogClose';
|
|
11
11
|
import { DialogProvider, useDialog } from './context';
|
|
@@ -16,12 +16,12 @@ export type {
|
|
|
16
16
|
IDialogContextType,
|
|
17
17
|
IDialogRootProps,
|
|
18
18
|
IDialogTriggerProps,
|
|
19
|
-
|
|
19
|
+
IDialogPopupProps,
|
|
20
20
|
IDialogOverlayProps,
|
|
21
21
|
IDialogHeaderProps,
|
|
22
22
|
IDialogTitleProps,
|
|
23
23
|
IDialogDescriptionProps,
|
|
24
|
-
|
|
24
|
+
IDialogContentProps,
|
|
25
25
|
IDialogFooterProps,
|
|
26
26
|
IDialogCloseProps,
|
|
27
27
|
} from './types';
|
|
@@ -29,64 +29,64 @@ export type {
|
|
|
29
29
|
export function createDialog<
|
|
30
30
|
RootProps,
|
|
31
31
|
TriggerProps,
|
|
32
|
-
|
|
32
|
+
PopupProps,
|
|
33
33
|
OverlayProps,
|
|
34
34
|
HeaderProps,
|
|
35
35
|
TitleProps,
|
|
36
36
|
DescriptionProps,
|
|
37
|
-
|
|
37
|
+
ContentProps,
|
|
38
38
|
FooterProps,
|
|
39
39
|
CloseProps,
|
|
40
40
|
>(BaseComponents: {
|
|
41
41
|
Root: React.ComponentType<RootProps>;
|
|
42
42
|
Trigger: React.ComponentType<TriggerProps>;
|
|
43
|
-
|
|
43
|
+
Popup: React.ComponentType<PopupProps>;
|
|
44
44
|
Overlay: React.ComponentType<OverlayProps>;
|
|
45
45
|
Header: React.ComponentType<HeaderProps>;
|
|
46
46
|
Title: React.ComponentType<TitleProps>;
|
|
47
47
|
Description: React.ComponentType<DescriptionProps>;
|
|
48
|
-
|
|
48
|
+
Content: React.ComponentType<ContentProps>;
|
|
49
49
|
Footer: React.ComponentType<FooterProps>;
|
|
50
50
|
Close: React.ComponentType<CloseProps>;
|
|
51
51
|
}) {
|
|
52
52
|
const Root = createDialogRoot(BaseComponents.Root);
|
|
53
53
|
const Trigger = createDialogTrigger(BaseComponents.Trigger);
|
|
54
|
-
const
|
|
54
|
+
const Popup = createDialogPopup(BaseComponents.Popup, BaseComponents.Overlay);
|
|
55
55
|
const Header = createDialogHeader(BaseComponents.Header);
|
|
56
56
|
const Title = createDialogTitle(BaseComponents.Title);
|
|
57
57
|
const Description = createDialogDescription(BaseComponents.Description);
|
|
58
|
-
const
|
|
58
|
+
const Content = createDialogContent(BaseComponents.Content);
|
|
59
59
|
const Footer = createDialogFooter(BaseComponents.Footer);
|
|
60
60
|
const Close = createDialogClose(BaseComponents.Close);
|
|
61
61
|
|
|
62
62
|
Root.displayName = 'DialogPrimitive.Root';
|
|
63
63
|
Trigger.displayName = 'DialogPrimitive.Trigger';
|
|
64
|
-
|
|
64
|
+
Popup.displayName = 'DialogPrimitive.Popup';
|
|
65
65
|
Header.displayName = 'DialogPrimitive.Header';
|
|
66
66
|
Title.displayName = 'DialogPrimitive.Title';
|
|
67
67
|
Description.displayName = 'DialogPrimitive.Description';
|
|
68
|
-
|
|
68
|
+
Content.displayName = 'DialogPrimitive.Content';
|
|
69
69
|
Footer.displayName = 'DialogPrimitive.Footer';
|
|
70
70
|
Close.displayName = 'DialogPrimitive.Close';
|
|
71
71
|
|
|
72
72
|
return Object.assign(Root, {
|
|
73
73
|
Root,
|
|
74
74
|
Trigger,
|
|
75
|
-
|
|
75
|
+
Popup,
|
|
76
76
|
Header,
|
|
77
77
|
Title,
|
|
78
78
|
Description,
|
|
79
|
-
|
|
79
|
+
Content,
|
|
80
80
|
Footer,
|
|
81
81
|
Close,
|
|
82
82
|
}) as IDialogComponentType<
|
|
83
83
|
RootProps,
|
|
84
84
|
TriggerProps,
|
|
85
|
-
|
|
85
|
+
PopupProps,
|
|
86
86
|
HeaderProps,
|
|
87
87
|
TitleProps,
|
|
88
88
|
DescriptionProps,
|
|
89
|
-
|
|
89
|
+
ContentProps,
|
|
90
90
|
FooterProps,
|
|
91
91
|
CloseProps
|
|
92
92
|
>;
|