@delacour/react-native-ui 0.1.0-alpha.20260925053522
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 +113 -0
- package/package.json +125 -0
- package/src/components/accordion/AGENTS.md +193 -0
- package/src/components/accordion/accordion-content.tsx +125 -0
- package/src/components/accordion/accordion-description.tsx +17 -0
- package/src/components/accordion/accordion-indicator.tsx +104 -0
- package/src/components/accordion/accordion-item.tsx +78 -0
- package/src/components/accordion/accordion-title.tsx +18 -0
- package/src/components/accordion/accordion-trigger.tsx +148 -0
- package/src/components/accordion/accordion.context.tsx +153 -0
- package/src/components/accordion/accordion.tsx +253 -0
- package/src/components/accordion/accordion.types.ts +11 -0
- package/src/components/accordion/accordion.variants.test.ts +434 -0
- package/src/components/accordion/accordion.variants.ts +358 -0
- package/src/components/accordion/index.ts +45 -0
- package/src/components/badge/AGENTS.md +83 -0
- package/src/components/badge/badge-close-button.tsx +48 -0
- package/src/components/badge/badge-end-content.tsx +15 -0
- package/src/components/badge/badge-label.tsx +24 -0
- package/src/components/badge/badge-start-content.tsx +16 -0
- package/src/components/badge/badge.context.tsx +64 -0
- package/src/components/badge/badge.tsx +192 -0
- package/src/components/badge/badge.types.ts +10 -0
- package/src/components/badge/badge.variants.test.ts +269 -0
- package/src/components/badge/badge.variants.ts +238 -0
- package/src/components/badge/index.ts +17 -0
- package/src/components/bottom-sheet/AGENTS.md +242 -0
- package/src/components/bottom-sheet/bottom-sheet-background.tsx +35 -0
- package/src/components/bottom-sheet/bottom-sheet-close.tsx +60 -0
- package/src/components/bottom-sheet/bottom-sheet-container.tsx +246 -0
- package/src/components/bottom-sheet/bottom-sheet-content.tsx +66 -0
- package/src/components/bottom-sheet/bottom-sheet-description.tsx +29 -0
- package/src/components/bottom-sheet/bottom-sheet-footer.tsx +150 -0
- package/src/components/bottom-sheet/bottom-sheet-handle.tsx +47 -0
- package/src/components/bottom-sheet/bottom-sheet-overlay.tsx +180 -0
- package/src/components/bottom-sheet/bottom-sheet-portal.tsx +72 -0
- package/src/components/bottom-sheet/bottom-sheet-scroll-view.tsx +105 -0
- package/src/components/bottom-sheet/bottom-sheet-title.tsx +24 -0
- package/src/components/bottom-sheet/bottom-sheet-trigger.tsx +73 -0
- package/src/components/bottom-sheet/bottom-sheet.context.tsx +135 -0
- package/src/components/bottom-sheet/bottom-sheet.tsx +125 -0
- package/src/components/bottom-sheet/bottom-sheet.variants.test.ts +293 -0
- package/src/components/bottom-sheet/bottom-sheet.variants.ts +220 -0
- package/src/components/bottom-sheet/index.ts +40 -0
- package/src/components/bottom-sheet/use-bottom-sheet-input.ts +125 -0
- package/src/components/button/AGENTS.md +217 -0
- package/src/components/button/button-end-content.tsx +15 -0
- package/src/components/button/button-group-separator.tsx +37 -0
- package/src/components/button/button-group-text.tsx +101 -0
- package/src/components/button/button-group.tsx +158 -0
- package/src/components/button/button-label.tsx +24 -0
- package/src/components/button/button-start-content.tsx +15 -0
- package/src/components/button/button.context.tsx +187 -0
- package/src/components/button/button.tsx +273 -0
- package/src/components/button/button.types.ts +10 -0
- package/src/components/button/button.variants.test.ts +811 -0
- package/src/components/button/button.variants.ts +480 -0
- package/src/components/button/index.ts +50 -0
- package/src/components/chart/AGENTS.md +210 -0
- package/src/components/chart/chart-area.tsx +93 -0
- package/src/components/chart/chart-bar.tsx +103 -0
- package/src/components/chart/chart-bars.tsx +75 -0
- package/src/components/chart/chart-candlestick.tsx +60 -0
- package/src/components/chart/chart-grid.tsx +33 -0
- package/src/components/chart/chart-legend.tsx +41 -0
- package/src/components/chart/chart-line.tsx +50 -0
- package/src/components/chart/chart-scatter.tsx +35 -0
- package/src/components/chart/chart-tooltip-dot.tsx +70 -0
- package/src/components/chart/chart-tooltip-x.tsx +121 -0
- package/src/components/chart/chart-tooltip-y.tsx +55 -0
- package/src/components/chart/chart-tooltip.tsx +133 -0
- package/src/components/chart/chart-x-axis.tsx +27 -0
- package/src/components/chart/chart-y-axis.tsx +21 -0
- package/src/components/chart/chart.context.tsx +127 -0
- package/src/components/chart/chart.tsx +448 -0
- package/src/components/chart/chart.types.ts +108 -0
- package/src/components/chart/chart.variants.test.ts +893 -0
- package/src/components/chart/chart.variants.ts +726 -0
- package/src/components/chart/index.ts +103 -0
- package/src/components/chart/pie-chart-center.tsx +40 -0
- package/src/components/chart/pie-chart-label.tsx +56 -0
- package/src/components/chart/pie-chart-slice.tsx +45 -0
- package/src/components/chart/pie-chart-tooltip.tsx +85 -0
- package/src/components/chart/pie-chart.context.tsx +77 -0
- package/src/components/chart/pie-chart.tsx +247 -0
- package/src/components/chart/use-chart-font.ts +25 -0
- package/src/components/chart/use-chart-palette.ts +38 -0
- package/src/components/checkbox/AGENTS.md +185 -0
- package/src/components/checkbox/checkbox-box.tsx +152 -0
- package/src/components/checkbox/checkbox-group.tsx +107 -0
- package/src/components/checkbox/checkbox-label.tsx +34 -0
- package/src/components/checkbox/checkbox.context.tsx +136 -0
- package/src/components/checkbox/checkbox.tsx +258 -0
- package/src/components/checkbox/checkbox.types.ts +14 -0
- package/src/components/checkbox/checkbox.variants.test.ts +634 -0
- package/src/components/checkbox/checkbox.variants.ts +484 -0
- package/src/components/checkbox/index.ts +42 -0
- package/src/components/field/AGENTS.md +112 -0
- package/src/components/field/field-content.tsx +24 -0
- package/src/components/field/field-description.tsx +29 -0
- package/src/components/field/field-error.tsx +61 -0
- package/src/components/field/field-group.tsx +22 -0
- package/src/components/field/field-label.tsx +47 -0
- package/src/components/field/field-legend.tsx +34 -0
- package/src/components/field/field-separator.tsx +81 -0
- package/src/components/field/field-set.tsx +22 -0
- package/src/components/field/field.context.tsx +95 -0
- package/src/components/field/field.tsx +157 -0
- package/src/components/field/field.types.ts +15 -0
- package/src/components/field/field.variants.test.ts +240 -0
- package/src/components/field/field.variants.ts +156 -0
- package/src/components/field/index.ts +22 -0
- package/src/components/icon/AGENTS.md +104 -0
- package/src/components/icon/icon.context.tsx +29 -0
- package/src/components/icon/icon.tsx +92 -0
- package/src/components/icon/icon.variants.test.ts +111 -0
- package/src/components/icon/icon.variants.ts +79 -0
- package/src/components/icon/index.ts +11 -0
- package/src/components/input/AGENTS.md +126 -0
- package/src/components/input/index.ts +27 -0
- package/src/components/input/input-group-decorator.tsx +84 -0
- package/src/components/input/input-group-prefix.tsx +16 -0
- package/src/components/input/input-group-suffix.tsx +15 -0
- package/src/components/input/input-group.tsx +140 -0
- package/src/components/input/input.context.tsx +87 -0
- package/src/components/input/input.tsx +184 -0
- package/src/components/input/input.types.ts +10 -0
- package/src/components/input/input.variants.test.ts +369 -0
- package/src/components/input/input.variants.ts +329 -0
- package/src/components/list-group/AGENTS.md +67 -0
- package/src/components/list-group/index.ts +20 -0
- package/src/components/list-group/list-group-item-content.tsx +10 -0
- package/src/components/list-group/list-group-item-description.tsx +12 -0
- package/src/components/list-group/list-group-item-prefix.tsx +30 -0
- package/src/components/list-group/list-group-item-suffix.tsx +55 -0
- package/src/components/list-group/list-group-item-title.tsx +17 -0
- package/src/components/list-group/list-group-item.tsx +87 -0
- package/src/components/list-group/list-group.context.tsx +65 -0
- package/src/components/list-group/list-group.tsx +116 -0
- package/src/components/list-group/list-group.types.ts +14 -0
- package/src/components/list-group/list-group.variants.test.ts +223 -0
- package/src/components/list-group/list-group.variants.ts +107 -0
- package/src/components/pressable/AGENTS.md +74 -0
- package/src/components/pressable/index.ts +9 -0
- package/src/components/pressable/pressable.tsx +261 -0
- package/src/components/pressable/pressable.variants.test.ts +128 -0
- package/src/components/pressable/pressable.variants.ts +80 -0
- package/src/components/provider/AGENTS.md +90 -0
- package/src/components/provider/index.ts +1 -0
- package/src/components/provider/provider.tsx +105 -0
- package/src/components/radio/AGENTS.md +245 -0
- package/src/components/radio/index.ts +35 -0
- package/src/components/radio/radio-group.tsx +126 -0
- package/src/components/radio/radio-indicator.tsx +91 -0
- package/src/components/radio/radio-label.tsx +39 -0
- package/src/components/radio/radio.context.tsx +132 -0
- package/src/components/radio/radio.tsx +215 -0
- package/src/components/radio/radio.variants.test.ts +580 -0
- package/src/components/radio/radio.variants.ts +271 -0
- package/src/components/screen/AGENTS.md +287 -0
- package/src/components/screen/index.ts +85 -0
- package/src/components/screen/screen-chat-list.tsx +485 -0
- package/src/components/screen/screen-content.tsx +69 -0
- package/src/components/screen/screen-debug.ts +43 -0
- package/src/components/screen/screen-error.tsx +61 -0
- package/src/components/screen/screen-flat-list.tsx +88 -0
- package/src/components/screen/screen-footer-background.tsx +60 -0
- package/src/components/screen/screen-footer.tsx +170 -0
- package/src/components/screen/screen-header.tsx +36 -0
- package/src/components/screen/screen-legend-list.tsx +105 -0
- package/src/components/screen/screen-list-component.tsx +23 -0
- package/src/components/screen/screen-loading.tsx +50 -0
- package/src/components/screen/screen-navbar-back-button.tsx +79 -0
- package/src/components/screen/screen-navbar-background.tsx +45 -0
- package/src/components/screen/screen-navbar-subtitle.tsx +26 -0
- package/src/components/screen/screen-navbar-title.tsx +31 -0
- package/src/components/screen/screen-navbar.tsx +154 -0
- package/src/components/screen/screen-root.tsx +38 -0
- package/src/components/screen/screen-scroll-area.tsx +116 -0
- package/src/components/screen/screen-scroll-shadow.tsx +186 -0
- package/src/components/screen/screen-section-list.tsx +81 -0
- package/src/components/screen/screen-view.tsx +59 -0
- package/src/components/screen/screen.context.tsx +167 -0
- package/src/components/screen/screen.tsx +114 -0
- package/src/components/screen/screen.types.ts +66 -0
- package/src/components/screen/screen.variants.test.ts +602 -0
- package/src/components/screen/screen.variants.ts +475 -0
- package/src/components/screen/use-screen-scroll-insets.ts +218 -0
- package/src/components/separator/AGENTS.md +30 -0
- package/src/components/separator/index.ts +8 -0
- package/src/components/separator/separator.tsx +85 -0
- package/src/components/slider/AGENTS.md +274 -0
- package/src/components/slider/index.ts +50 -0
- package/src/components/slider/slider-fill.tsx +68 -0
- package/src/components/slider/slider-output.tsx +55 -0
- package/src/components/slider/slider-thumb.tsx +193 -0
- package/src/components/slider/slider-track.tsx +233 -0
- package/src/components/slider/slider.context.tsx +118 -0
- package/src/components/slider/slider.tsx +321 -0
- package/src/components/slider/slider.types.ts +26 -0
- package/src/components/slider/slider.variants.test.ts +856 -0
- package/src/components/slider/slider.variants.ts +661 -0
- package/src/components/spinner/AGENTS.md +73 -0
- package/src/components/spinner/index.ts +17 -0
- package/src/components/spinner/spinner-arc.tsx +90 -0
- package/src/components/spinner/spinner-content.tsx +63 -0
- package/src/components/spinner/spinner.context.tsx +52 -0
- package/src/components/spinner/spinner.tsx +128 -0
- package/src/components/spinner/spinner.variants.test.ts +273 -0
- package/src/components/spinner/spinner.variants.ts +187 -0
- package/src/components/switch/AGENTS.md +213 -0
- package/src/components/switch/index.ts +42 -0
- package/src/components/switch/switch-content.tsx +111 -0
- package/src/components/switch/switch-end-content.tsx +18 -0
- package/src/components/switch/switch-start-content.tsx +20 -0
- package/src/components/switch/switch-thumb.tsx +102 -0
- package/src/components/switch/switch.context.tsx +79 -0
- package/src/components/switch/switch.tsx +423 -0
- package/src/components/switch/switch.types.ts +14 -0
- package/src/components/switch/switch.variants.test.ts +570 -0
- package/src/components/switch/switch.variants.ts +511 -0
- package/src/components/tabs/AGENTS.md +287 -0
- package/src/components/tabs/index.ts +69 -0
- package/src/components/tabs/tabs-content.tsx +65 -0
- package/src/components/tabs/tabs-indicator.tsx +97 -0
- package/src/components/tabs/tabs-label.tsx +62 -0
- package/src/components/tabs/tabs-list.tsx +139 -0
- package/src/components/tabs/tabs-pager.tsx +59 -0
- package/src/components/tabs/tabs-scroll-view.tsx +171 -0
- package/src/components/tabs/tabs-separator.tsx +73 -0
- package/src/components/tabs/tabs-trigger.tsx +210 -0
- package/src/components/tabs/tabs.context.tsx +294 -0
- package/src/components/tabs/tabs.tsx +435 -0
- package/src/components/tabs/tabs.types.ts +13 -0
- package/src/components/tabs/tabs.variants.test.ts +1020 -0
- package/src/components/tabs/tabs.variants.ts +670 -0
- package/src/components/text/AGENTS.md +95 -0
- package/src/components/text/index.ts +25 -0
- package/src/components/text/text.context.tsx +60 -0
- package/src/components/text/text.tsx +251 -0
- package/src/components/text/text.variants.test.ts +422 -0
- package/src/components/text/text.variants.ts +282 -0
- package/src/display-name.test.ts +145 -0
- package/src/docs.test.ts +98 -0
- package/src/expo/navigation-theme.tsx +58 -0
- package/src/hooks/use-controllable-state.ts +45 -0
- package/src/hooks/use-keyboard-state-sync.tsx +147 -0
- package/src/hooks/use-navigation-theme.ts +78 -0
- package/src/hooks/use-theme-color.ts +44 -0
- package/src/icons/central.ts +1 -0
- package/src/lib/cn.test.ts +136 -0
- package/src/lib/cn.ts +29 -0
- package/src/lib/color.test.ts +80 -0
- package/src/lib/color.ts +79 -0
- package/src/lib/compose-refs.test.ts +64 -0
- package/src/lib/compose-refs.ts +37 -0
- package/src/lib/keyboard-animation.test.ts +33 -0
- package/src/lib/keyboard-animation.ts +28 -0
- package/src/lib/merge-props.test.ts +82 -0
- package/src/lib/merge-props.ts +47 -0
- package/src/lib/navigation-theme.test.ts +49 -0
- package/src/lib/navigation-theme.ts +53 -0
- package/src/lib/slot.tsx +45 -0
- package/src/lib/tv.ts +20 -0
- package/src/styles/base.css +2 -0
- package/src/styles/geometry.test.ts +117 -0
- package/src/styles/index.css +3 -0
- package/src/styles/theme-tokens.test.ts +252 -0
- package/src/styles/theme.css +447 -0
- package/src/styles/tokens.css +132 -0
- package/src/styles/tokens.test.ts +181 -0
- package/src/styles/tokens.ts +86 -0
- package/src/uniwind-env.d.ts +1 -0
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { Children, isValidElement, type ReactElement, type ReactNode, useCallback, useMemo } from "react";
|
|
2
|
+
import { useFieldContext } from "../field/field.context";
|
|
3
|
+
import { Pressable, type PressableProps } from "../pressable";
|
|
4
|
+
import { TextClassProvider } from "../text/text.context";
|
|
5
|
+
import { resolveTextClass } from "../text/text.variants";
|
|
6
|
+
import { type RadioContextValue, RadioProvider, useRadioGroupContext } from "./radio.context";
|
|
7
|
+
import {
|
|
8
|
+
RADIO_LABEL_TEXT_SIZE,
|
|
9
|
+
type RadioSize,
|
|
10
|
+
type RadioVariant,
|
|
11
|
+
radioVariants,
|
|
12
|
+
resolveIndicatorPlacement,
|
|
13
|
+
resolveRadioState,
|
|
14
|
+
} from "./radio.variants";
|
|
15
|
+
import { RadioGroup } from "./radio-group";
|
|
16
|
+
import { RadioIndicator } from "./radio-indicator";
|
|
17
|
+
import { RadioLabel } from "./radio-label";
|
|
18
|
+
|
|
19
|
+
export type RadioProps = Omit<PressableProps, "asChild" | "busy" | "children" | "disabled"> & {
|
|
20
|
+
/** Identifies this radio to its `Radio.Group`. Required there; inert without one. */
|
|
21
|
+
value?: string;
|
|
22
|
+
/** Selected state for a radio standing on its own. Ignored inside a `Radio.Group`. */
|
|
23
|
+
isSelected?: boolean;
|
|
24
|
+
/** Initial selected state while uncontrolled and ungrouped. */
|
|
25
|
+
defaultSelected?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Called when this radio becomes the selection. Ignored inside a
|
|
28
|
+
* `Radio.Group`, which owns the callback.
|
|
29
|
+
*
|
|
30
|
+
* Only ever called with `true`: a radio does not deselect itself, which is
|
|
31
|
+
* `Switch` and `Checkbox` semantics rather than radio ones.
|
|
32
|
+
*/
|
|
33
|
+
onSelected?: (isSelected: boolean) => void;
|
|
34
|
+
/** Ignored inside a `Radio.Group` — the group owns this axis. */
|
|
35
|
+
size?: RadioSize;
|
|
36
|
+
/** Ignored inside a `Radio.Group` — the group owns this axis. */
|
|
37
|
+
variant?: RadioVariant;
|
|
38
|
+
/** Wins over an enclosing `Field`. A group that names it wins over both. */
|
|
39
|
+
isInvalid?: boolean;
|
|
40
|
+
/** Wins over an enclosing `Field`. A group that names it wins over both. */
|
|
41
|
+
isDisabled?: boolean;
|
|
42
|
+
children?: ReactNode;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
function RadioRoot({
|
|
46
|
+
value,
|
|
47
|
+
isSelected,
|
|
48
|
+
defaultSelected,
|
|
49
|
+
onSelected,
|
|
50
|
+
size,
|
|
51
|
+
variant,
|
|
52
|
+
isInvalid,
|
|
53
|
+
isDisabled,
|
|
54
|
+
feedback = "fade",
|
|
55
|
+
hitSlop = 8,
|
|
56
|
+
className,
|
|
57
|
+
children,
|
|
58
|
+
onPress,
|
|
59
|
+
...props
|
|
60
|
+
}: RadioProps): ReactElement {
|
|
61
|
+
const group = useRadioGroupContext();
|
|
62
|
+
const field = useFieldContext();
|
|
63
|
+
|
|
64
|
+
const state = resolveRadioState({
|
|
65
|
+
field,
|
|
66
|
+
group,
|
|
67
|
+
own: { isDisabled, isInvalid, isSelected, size, value, variant },
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
if (process.env.NODE_ENV !== "production" && group && value === undefined) {
|
|
71
|
+
console.warn("Radio: a <Radio> inside a <Radio.Group> needs a `value`, or it can never be selected.");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const handlePress = useCallback(() => {
|
|
75
|
+
if (group) {
|
|
76
|
+
if (value !== undefined) group.select(value);
|
|
77
|
+
} else if (!state.isSelected) {
|
|
78
|
+
onSelected?.(true);
|
|
79
|
+
}
|
|
80
|
+
onPress?.();
|
|
81
|
+
}, [group, onPress, onSelected, state.isSelected, value]);
|
|
82
|
+
|
|
83
|
+
const context = useMemo<RadioContextValue>(
|
|
84
|
+
() => ({
|
|
85
|
+
isDisabled: state.isDisabled,
|
|
86
|
+
isInvalid: state.isInvalid,
|
|
87
|
+
isSelected: state.isSelected,
|
|
88
|
+
size: state.size,
|
|
89
|
+
variant: state.variant,
|
|
90
|
+
}),
|
|
91
|
+
[state.isDisabled, state.isInvalid, state.isSelected, state.size, state.variant]
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const { content, placement } = useMemo(() => withIndicator(children), [children]);
|
|
95
|
+
|
|
96
|
+
// One treatment covers the whole subtree — a radio row has a label and no
|
|
97
|
+
// description part — which is the condition for publishing into the cascade.
|
|
98
|
+
const labelClass = useMemo(
|
|
99
|
+
() => resolveTextClass({ size: RADIO_LABEL_TEXT_SIZE[state.size], variant: "label" }),
|
|
100
|
+
[state.size]
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<RadioProvider value={context}>
|
|
105
|
+
<Pressable
|
|
106
|
+
accessibilityRole="radio"
|
|
107
|
+
accessibilityState={{ checked: state.isSelected }}
|
|
108
|
+
className={radioVariants({
|
|
109
|
+
isDisabled: state.isDisabled,
|
|
110
|
+
isIndicatorTrailing: placement === "end",
|
|
111
|
+
size: state.size,
|
|
112
|
+
}).root({ className })}
|
|
113
|
+
disabled={state.isDisabled}
|
|
114
|
+
feedback={feedback}
|
|
115
|
+
hitSlop={hitSlop}
|
|
116
|
+
onPress={handlePress}
|
|
117
|
+
{...props}
|
|
118
|
+
>
|
|
119
|
+
<TextClassProvider value={labelClass}>{content}</TextClassProvider>
|
|
120
|
+
</Pressable>
|
|
121
|
+
</RadioProvider>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Wraps bare text in a `Radio.Label` and settles where the indicator sits.
|
|
127
|
+
*
|
|
128
|
+
* React Native cannot render a string outside a `<Text>`, so `<Radio>Yes</Radio>`
|
|
129
|
+
* would otherwise crash. Consecutive strings and numbers collapse into one label
|
|
130
|
+
* rather than one each — `Plan {n}` is a single piece of text, and wrapping the
|
|
131
|
+
* parts separately would space them apart by the row's own gap.
|
|
132
|
+
*
|
|
133
|
+
* An indicator is composed in at the front only when the children hold none,
|
|
134
|
+
* which is what buys a settings row for free: place a `<Radio.Indicator />` last
|
|
135
|
+
* and the row lays out `[label and description] [ring]`, spread to the far edge,
|
|
136
|
+
* with nothing else said. {@link resolveIndicatorPlacement} is that decision and
|
|
137
|
+
* is pure, so `bun test` reaches it.
|
|
138
|
+
*
|
|
139
|
+
* Lives here rather than with the parts: it is the root that wraps its own
|
|
140
|
+
* children, and importing it from a part would close a cycle. See AGENTS.md.
|
|
141
|
+
*/
|
|
142
|
+
function withIndicator(children: ReactNode): { content: ReactNode; placement: "start" | "end" | "none" } {
|
|
143
|
+
const items = Children.toArray(children);
|
|
144
|
+
const output: ReactNode[] = [];
|
|
145
|
+
const isIndicator: boolean[] = [];
|
|
146
|
+
let run: (string | number)[] = [];
|
|
147
|
+
|
|
148
|
+
const flushRun = () => {
|
|
149
|
+
if (run.length === 0) return;
|
|
150
|
+
output.push(<RadioLabel key={`label-${output.length}`}>{run.join("")}</RadioLabel>);
|
|
151
|
+
isIndicator.push(false);
|
|
152
|
+
run = [];
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
for (const child of items) {
|
|
156
|
+
if (typeof child === "string" || typeof child === "number") {
|
|
157
|
+
run.push(child);
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
flushRun();
|
|
161
|
+
output.push(child);
|
|
162
|
+
isIndicator.push(isValidElement(child) && child.type === RadioIndicator);
|
|
163
|
+
}
|
|
164
|
+
flushRun();
|
|
165
|
+
|
|
166
|
+
const placement = resolveIndicatorPlacement(isIndicator);
|
|
167
|
+
if (placement !== "none") return { content: output, placement };
|
|
168
|
+
|
|
169
|
+
return { content: [<RadioIndicator key="indicator" />, ...output], placement: "start" };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* One option in a set where exactly one can be chosen.
|
|
174
|
+
*
|
|
175
|
+
* The whole row is the control: the ring, the label beside it and the space
|
|
176
|
+
* between them are one tap target, so a press anywhere on the row selects. A
|
|
177
|
+
* `Radio.Indicator` is composed in automatically and plain string children are
|
|
178
|
+
* wrapped in a `Radio.Label`, so the shortest thing that works is `<Radio
|
|
179
|
+
* value="pro">Pro</Radio>`.
|
|
180
|
+
*
|
|
181
|
+
* **A radio works with or without a group.** Inside a `Radio.Group` its `value`
|
|
182
|
+
* identifies it and the group owns the selection, the size and the variant.
|
|
183
|
+
* Outside one it is driven by its own `isSelected` and `onSelected`.
|
|
184
|
+
*
|
|
185
|
+
* Nearest wins for state: a group first, this radio's own props next, an
|
|
186
|
+
* enclosing `Field` last — so a radio inside an invalid `Field` turns destructive with
|
|
187
|
+
* nothing said at the call site, while `isInvalid={false}` still opts out.
|
|
188
|
+
*
|
|
189
|
+
* A radio whose row holds no text needs an `accessibilityLabel`, the same rule an
|
|
190
|
+
* icon-sized `Button` follows.
|
|
191
|
+
*
|
|
192
|
+
* @example
|
|
193
|
+
* <Radio.Group onSelected={setPlan} selected={plan ?? null} accessibilityLabel="Plan">
|
|
194
|
+
* <Radio value="free">Free</Radio>
|
|
195
|
+
* <Radio value="pro">Pro</Radio>
|
|
196
|
+
* </Radio.Group>
|
|
197
|
+
*
|
|
198
|
+
* @example
|
|
199
|
+
* <Radio value="express">
|
|
200
|
+
* <Radio.Label>Express</Radio.Label>
|
|
201
|
+
* <Text.Caption>Arrives tomorrow.</Text.Caption>
|
|
202
|
+
* </Radio>
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* <Radio accessibilityLabel="Express delivery" isSelected={isExpress} onSelected={setExpress} />
|
|
206
|
+
*/
|
|
207
|
+
export const Radio = Object.assign(RadioRoot, {
|
|
208
|
+
/** The radio's text, inside its tap target. Takes its size step from the radio's own. */
|
|
209
|
+
Label: RadioLabel,
|
|
210
|
+
/** The ring and its dot. Composed in automatically; write it out to move or replace it. */
|
|
211
|
+
Indicator: RadioIndicator,
|
|
212
|
+
/** Groups radios and owns which one is selected. */
|
|
213
|
+
Group: RadioGroup,
|
|
214
|
+
displayName: "DelacourUI.Radio",
|
|
215
|
+
});
|