@brand-map/primitives 0.0.0-broken.0

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.
Files changed (121) hide show
  1. package/.changeset/README.md +8 -0
  2. package/.changeset/config.json +11 -0
  3. package/.oxfmtrc.json +35 -0
  4. package/.oxlintrc.json +166 -0
  5. package/README.md +78 -0
  6. package/bun.lock +904 -0
  7. package/mise.toml +3 -0
  8. package/package.json +61 -0
  9. package/src/accordion/accordion.tsx +189 -0
  10. package/src/accordion/accordion.web.tsx +282 -0
  11. package/src/accordion/index.ts +2 -0
  12. package/src/accordion/types.ts +44 -0
  13. package/src/alert-dialog/alert-dialog.tsx +238 -0
  14. package/src/alert-dialog/alert-dialog.web.tsx +260 -0
  15. package/src/alert-dialog/index.ts +2 -0
  16. package/src/alert-dialog/types.ts +81 -0
  17. package/src/aspect-ratio/aspect-ratio.tsx +27 -0
  18. package/src/aspect-ratio/index.ts +1 -0
  19. package/src/avatar/avatar.tsx +122 -0
  20. package/src/avatar/index.ts +2 -0
  21. package/src/avatar/types.ts +20 -0
  22. package/src/checkbox/checkbox.tsx +95 -0
  23. package/src/checkbox/checkbox.web.tsx +111 -0
  24. package/src/checkbox/index.ts +2 -0
  25. package/src/checkbox/types.ts +14 -0
  26. package/src/collapsible/collapsible.tsx +98 -0
  27. package/src/collapsible/collapsible.web.tsx +149 -0
  28. package/src/collapsible/index.ts +2 -0
  29. package/src/collapsible/types.ts +23 -0
  30. package/src/context-menu/context-menu.tsx +616 -0
  31. package/src/context-menu/context-menu.web.tsx +560 -0
  32. package/src/context-menu/index.ts +2 -0
  33. package/src/context-menu/types.ts +136 -0
  34. package/src/dialog/dialog.tsx +286 -0
  35. package/src/dialog/dialog.web.tsx +215 -0
  36. package/src/dialog/index.ts +2 -0
  37. package/src/dialog/types.ts +92 -0
  38. package/src/dropdown-menu/dropdown-menu.tsx +575 -0
  39. package/src/dropdown-menu/dropdown-menu.web.tsx +565 -0
  40. package/src/dropdown-menu/index.ts +2 -0
  41. package/src/dropdown-menu/types.ts +121 -0
  42. package/src/hooks/index.ts +4 -0
  43. package/src/hooks/use-Isomorphic-layout-effect.tsx +12 -0
  44. package/src/hooks/use-augmented-ref.tsx +25 -0
  45. package/src/hooks/use-controllable-state.tsx +70 -0
  46. package/src/hooks/use-relative-position.tsx +175 -0
  47. package/src/hover-card/hover-card.tsx +255 -0
  48. package/src/hover-card/hover-card.web.tsx +161 -0
  49. package/src/hover-card/index.ts +2 -0
  50. package/src/hover-card/types.ts +56 -0
  51. package/src/label/index.ts +2 -0
  52. package/src/label/label.tsx +36 -0
  53. package/src/label/label.web.tsx +38 -0
  54. package/src/label/types.ts +24 -0
  55. package/src/menubar/index.ts +2 -0
  56. package/src/menubar/menubar.tsx +602 -0
  57. package/src/menubar/menubar.web.tsx +575 -0
  58. package/src/menubar/types.ts +126 -0
  59. package/src/navigation-menu/index.ts +2 -0
  60. package/src/navigation-menu/navigation-menu.tsx +302 -0
  61. package/src/navigation-menu/navigation-menu.web.tsx +259 -0
  62. package/src/navigation-menu/types.ts +85 -0
  63. package/src/popover/index.ts +2 -0
  64. package/src/popover/popover.tsx +279 -0
  65. package/src/popover/popover.web.tsx +217 -0
  66. package/src/popover/types.ts +44 -0
  67. package/src/portal/index.ts +1 -0
  68. package/src/portal/portal.tsx +56 -0
  69. package/src/progress/index.ts +2 -0
  70. package/src/progress/progress.tsx +59 -0
  71. package/src/progress/progress.web.tsx +46 -0
  72. package/src/progress/types.ts +14 -0
  73. package/src/radio-group/index.ts +2 -0
  74. package/src/radio-group/radio-group.tsx +106 -0
  75. package/src/radio-group/radio-group.web.tsx +85 -0
  76. package/src/radio-group/types.ts +24 -0
  77. package/src/select/index.ts +2 -0
  78. package/src/select/select.tsx +447 -0
  79. package/src/select/select.web.tsx +368 -0
  80. package/src/select/types.ts +145 -0
  81. package/src/separator/index.ts +2 -0
  82. package/src/separator/separator.tsx +21 -0
  83. package/src/separator/types.ts +10 -0
  84. package/src/slider/index.ts +2 -0
  85. package/src/slider/slider.tsx +77 -0
  86. package/src/slider/slider.web.tsx +75 -0
  87. package/src/slider/types.ts +39 -0
  88. package/src/slot/index.ts +1 -0
  89. package/src/slot/slot.tsx +224 -0
  90. package/src/switch/index.ts +2 -0
  91. package/src/switch/switch.tsx +49 -0
  92. package/src/switch/switch.web.tsx +60 -0
  93. package/src/switch/types.ts +19 -0
  94. package/src/table/index.ts +1 -0
  95. package/src/table/table.tsx +121 -0
  96. package/src/tabs/index.ts +2 -0
  97. package/src/tabs/tabs.tsx +120 -0
  98. package/src/tabs/tabs.web.tsx +106 -0
  99. package/src/tabs/types.ts +37 -0
  100. package/src/toast/index.ts +2 -0
  101. package/src/toast/toast.tsx +124 -0
  102. package/src/toast/types.ts +20 -0
  103. package/src/toggle/index.ts +2 -0
  104. package/src/toggle/toggle.tsx +35 -0
  105. package/src/toggle/toggle.web.tsx +36 -0
  106. package/src/toggle/types.ts +11 -0
  107. package/src/toggle-group/index.ts +2 -0
  108. package/src/toggle-group/toggle-group.tsx +100 -0
  109. package/src/toggle-group/toggle-group.web.tsx +103 -0
  110. package/src/toggle-group/types.ts +46 -0
  111. package/src/toolbar/index.ts +2 -0
  112. package/src/toolbar/toolbar.tsx +141 -0
  113. package/src/toolbar/toolbar.web.tsx +158 -0
  114. package/src/toolbar/types.ts +64 -0
  115. package/src/tooltip/index.ts +2 -0
  116. package/src/tooltip/tooltip.tsx +261 -0
  117. package/src/tooltip/tooltip.web.tsx +175 -0
  118. package/src/tooltip/types.ts +61 -0
  119. package/src/types/index.ts +141 -0
  120. package/src/utils/index.ts +69 -0
  121. package/tsconfig.json +23 -0
@@ -0,0 +1,39 @@
1
+ import type { RenderViewProps, ViewRef } from "../types";
2
+
3
+ type RootProps = RenderViewProps & {
4
+ value: number;
5
+ disabled?: boolean;
6
+ min?: number;
7
+ max?: number;
8
+
9
+ /**
10
+ * @platform: WEB ONLY
11
+ */
12
+ dir?: "ltr" | "rtl";
13
+
14
+ /**
15
+ * @platform: WEB ONLY
16
+ */
17
+ inverted?: boolean;
18
+
19
+ /**
20
+ * @platform: WEB ONLY
21
+ */
22
+ step?: number;
23
+
24
+ /**
25
+ * @platform: WEB ONLY
26
+ */
27
+ onValueChange?: (value: number[]) => void;
28
+ };
29
+
30
+ type TrackProps = RenderViewProps;
31
+ type RangeProps = RenderViewProps;
32
+ type ThumbProps = RenderViewProps;
33
+
34
+ type RootRef = ViewRef;
35
+ type TrackRef = ViewRef;
36
+ type RangeRef = ViewRef;
37
+ type ThumbRef = ViewRef;
38
+
39
+ export type { RangeProps, RangeRef, RootProps, RootRef, ThumbProps, ThumbRef, TrackProps, TrackRef };
@@ -0,0 +1 @@
1
+ export * from "./slot";
@@ -0,0 +1,224 @@
1
+ import * as React from "react";
2
+ import { cloneElement, isValidElement } from "react";
3
+ import {
4
+ Image as ReactNativeImage,
5
+ Pressable as ReactNativePressable,
6
+ Text as ReactNativeText,
7
+ View as ReactNativeView,
8
+ StyleSheet,
9
+ type PressableStateCallbackType,
10
+ type ImageStyle as ReactNativeImageStyle,
11
+ type PressableProps as ReactNativePressableProps,
12
+ type StyleProp,
13
+ } from "react-native";
14
+
15
+ import { BrandMapUIComponentProps } from "../types";
16
+
17
+ // TODO: handle children as callback
18
+
19
+ const Pressable = (props: BrandMapUIComponentProps<typeof ReactNativePressable>): React.JSX.Element => {
20
+ const { children, render, ...rest } = props;
21
+
22
+ if (!render) {
23
+ return <ReactNativePressable {...rest}>{children}</ReactNativePressable>;
24
+ }
25
+
26
+ if (!isValidElement(render)) {
27
+ console.log("Slot.Pressable - Invalid render prop", render);
28
+ return <></>;
29
+ }
30
+
31
+ return cloneElement<React.ComponentPropsWithRef<typeof ReactNativePressable>>(
32
+ isTextChildren(render) ? <></> : render,
33
+
34
+ {
35
+ // @ts-expect-error
36
+ ...mergeProps(rest, render.props),
37
+
38
+ // @ts-expect-error
39
+ ref: props.ref ? composeRefs(props.ref, render.ref) : render.ref,
40
+ children,
41
+ },
42
+ );
43
+ };
44
+ // Pressable.displayName = "SlotPressable";
45
+
46
+ const View = (props: BrandMapUIComponentProps<typeof ReactNativeView>): React.JSX.Element => {
47
+ const { children, render, ...rest } = props;
48
+
49
+ if (!render) {
50
+ return <ReactNativeView {...rest}>{children}</ReactNativeView>;
51
+ }
52
+
53
+ if (!isValidElement(render)) {
54
+ console.log("Slot.View - Invalid render prop", render);
55
+ return <></>;
56
+ }
57
+
58
+ return cloneElement<React.ComponentPropsWithRef<typeof ReactNativeView>>(
59
+ isTextChildren(render) ? <></> : render,
60
+
61
+ {
62
+ // @ts-expect-error
63
+ ...mergeProps(rest, render.props),
64
+
65
+ // @ts-expect-error
66
+ ref: props.ref ? composeRefs(props.ref, render.ref) : render.ref,
67
+ children,
68
+ },
69
+ );
70
+ };
71
+ // View.displayName = "SlotView";
72
+
73
+ const Text = (props: BrandMapUIComponentProps<typeof ReactNativeText>): React.JSX.Element => {
74
+ const { children, render, ...rest } = props;
75
+
76
+ if (!render) {
77
+ return <ReactNativeText {...rest}>{children}</ReactNativeText>;
78
+ }
79
+
80
+ if (!isValidElement(render)) {
81
+ console.log("Slot.Text - Invalid render prop", render);
82
+ return <></>;
83
+ }
84
+
85
+ return cloneElement<React.ComponentPropsWithRef<typeof ReactNativeText>>(
86
+ isTextChildren(render) ? <></> : render,
87
+
88
+ {
89
+ // @ts-expect-error
90
+ ...mergeProps(rest, render.props),
91
+
92
+ // @ts-expect-error
93
+ ref: props.ref ? composeRefs(props.ref, render.ref) : render.ref,
94
+ children,
95
+ },
96
+ );
97
+ };
98
+ // Text.displayName = "SlotText";
99
+
100
+ const Image = (props: BrandMapUIComponentProps<typeof ReactNativeImage> /** & { children?: React.ReactNode } */): React.JSX.Element => {
101
+ const { render, ...rest } = props;
102
+
103
+ if (!render) {
104
+ return <ReactNativeImage {...rest} />;
105
+ }
106
+
107
+ if (!isValidElement(render)) {
108
+ console.log("Slot.Image - Invalid render prop", render);
109
+ return <></>;
110
+ }
111
+
112
+ return cloneElement<React.ComponentPropsWithRef<typeof ReactNativeImage>>(isTextChildren(render) ? <></> : render, {
113
+ // @ts-expect-error
114
+ ...mergeProps(rest, render.props),
115
+
116
+ // @ts-expect-error
117
+ ref: props.ref ? composeRefs(props.ref, render.ref) : render.ref,
118
+ // children,
119
+ });
120
+ };
121
+ // Image.displayName = "SlotImage";
122
+
123
+ export { Image, Pressable, Text, View };
124
+
125
+ // This project uses code from WorkOS/Radix Primitives.
126
+ // The code is licensed under the MIT License.
127
+ // https://github.com/radix-ui/primitives/tree/main
128
+
129
+ function composeRefs<T>(...refs: (React.Ref<T> | undefined)[]) {
130
+ return (node: T) => {
131
+ for (const ref of refs) {
132
+ switch (true) {
133
+ case !ref:
134
+ continue;
135
+
136
+ case typeof ref === "function":
137
+ ref(node);
138
+ continue;
139
+
140
+ case !!ref:
141
+ ref.current = node;
142
+ continue;
143
+
144
+ default:
145
+ throw new Error(`Must never reach. Invalid ref provided: ${ref}`);
146
+ }
147
+ }
148
+ };
149
+ }
150
+
151
+ type AnyProps = Record<string, any>;
152
+
153
+ function mergeProps(slotProps: AnyProps, childProps: AnyProps) {
154
+ // all child props should override
155
+ const overrideProps = { ...childProps };
156
+
157
+ for (const propName in childProps) {
158
+ const slotPropValue = slotProps[propName];
159
+ const childPropValue = childProps[propName];
160
+
161
+ if (propName.startsWith("on")) {
162
+ if (!childPropValue && !slotPropValue) {
163
+ continue;
164
+ }
165
+
166
+ // if the handler exists on both, we compose them
167
+ if (slotPropValue && childPropValue) {
168
+ overrideProps[propName] = (...args: any[]) => {
169
+ childPropValue(...args);
170
+ slotPropValue(...args);
171
+ };
172
+ }
173
+
174
+ // but if it exists only on the slot, we use only this one
175
+ if (slotPropValue) {
176
+ overrideProps[propName] = slotPropValue;
177
+ }
178
+
179
+ continue;
180
+ }
181
+
182
+ // if it's `style`, we merge them
183
+ if (propName === "style") {
184
+ overrideProps[propName] = combineStyles(slotPropValue, childPropValue);
185
+ }
186
+
187
+ // if it's `className`, we merge them
188
+ if (propName === "className") {
189
+ overrideProps[propName] = [slotPropValue, childPropValue].filter(Boolean).join(" ");
190
+ }
191
+ }
192
+
193
+ return { ...slotProps, ...overrideProps };
194
+ }
195
+
196
+ type PressableStyle = ReactNativePressableProps["style"];
197
+ type ImageStyle = StyleProp<ReactNativeImageStyle>;
198
+ type Style = PressableStyle | ImageStyle;
199
+
200
+ function combineStyles(slotStyle?: Style, childValue?: Style) {
201
+ if (typeof slotStyle === "function" && typeof childValue === "function") {
202
+ return (state: PressableStateCallbackType) => {
203
+ return StyleSheet.flatten([slotStyle(state), childValue(state)]);
204
+ };
205
+ }
206
+
207
+ if (typeof slotStyle === "function") {
208
+ return (state: PressableStateCallbackType) => {
209
+ return childValue ? StyleSheet.flatten([slotStyle(state), childValue]) : slotStyle(state);
210
+ };
211
+ }
212
+
213
+ if (typeof childValue === "function") {
214
+ return (state: PressableStateCallbackType) => {
215
+ return slotStyle ? StyleSheet.flatten([slotStyle, childValue(state)]) : childValue(state);
216
+ };
217
+ }
218
+
219
+ return StyleSheet.flatten([slotStyle, childValue].filter(Boolean));
220
+ }
221
+
222
+ export function isTextChildren(children: React.ReactNode | ((state: PressableStateCallbackType) => React.ReactNode)): children is string | string[] {
223
+ return Array.isArray(children) ? children.every((child) => typeof child === "string") : typeof children === "string";
224
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./switch";
2
+ export * from "./types";
@@ -0,0 +1,49 @@
1
+ import * as React from "react";
2
+ import { Pressable, View, type GestureResponderEvent } from "react-native";
3
+
4
+ import * as Slot from "../slot";
5
+
6
+ import type { RootProps, RootRef, ThumbProps, ThumbRef } from "./types";
7
+
8
+ const Root = React.forwardRef<RootRef, RootProps>(
9
+ ({ render, checked, onCheckedChange, disabled, onPress: onPressProp, "aria-valuetext": ariaValueText, ...props }, ref) => {
10
+ function onPress(ev: GestureResponderEvent) {
11
+ if (disabled) return;
12
+ onCheckedChange(!checked);
13
+ onPressProp?.(ev);
14
+ }
15
+
16
+ return (
17
+ <Component
18
+ ref={ref}
19
+ aria-disabled={disabled}
20
+ role="switch"
21
+ aria-checked={checked}
22
+ aria-valuetext={(ariaValueText ?? checked) ? "on" : "off"}
23
+ onPress={onPress}
24
+ accessibilityState={{
25
+ checked,
26
+ disabled,
27
+ }}
28
+ disabled={disabled}
29
+ {...props}
30
+ />
31
+ );
32
+ },
33
+ );
34
+
35
+ Root.displayName = "RootNativeSwitch";
36
+
37
+ const Thumb = React.forwardRef<ThumbRef, ThumbProps>(({ ...props }, ref) => {
38
+ return (
39
+ <Component
40
+ ref={ref}
41
+ role="presentation"
42
+ {...props}
43
+ />
44
+ );
45
+ });
46
+
47
+ Thumb.displayName = "ThumbNativeSwitch";
48
+
49
+ export { Root, Thumb };
@@ -0,0 +1,60 @@
1
+ // import * as Switch from "@radix-ui/react-switch";
2
+ // import * as React from "react";
3
+ // import { Pressable, View, type GestureResponderEvent } from "react-native";
4
+
5
+ // import * as Slot from "../slot";
6
+
7
+ // import type { RootProps, RootRef, ThumbProps, ThumbRef } from "./types";
8
+
9
+ // const Root = React.forwardRef<RootRef, RootProps>(
10
+ // ({ render, checked, onCheckedChange, disabled, onPress: onPressProp, onKeyDown: onKeyDownProp, ...props }, ref) => {
11
+ // function onPress(ev: GestureResponderEvent) {
12
+ // onCheckedChange(!checked);
13
+ // onPressProp?.(ev);
14
+ // }
15
+
16
+ // function onKeyDown(ev: React.KeyboardEvent) {
17
+ // onKeyDownProp?.(ev);
18
+ // if (ev.key === " ") {
19
+ // onCheckedChange(!checked);
20
+ // }
21
+ // }
22
+
23
+ //
24
+ // return (
25
+ // <Switch.Root
26
+ // checked={checked}
27
+ // onCheckedChange={onCheckedChange}
28
+ // disabled={disabled}
29
+ // render
30
+ // >
31
+ // <Component
32
+ // ref={ref}
33
+ // disabled={disabled}
34
+ // onPress={onPress}
35
+ // // @ts-expect-error Web only
36
+ // onKeyDown={onKeyDown}
37
+ // {...props}
38
+ // />
39
+ // </Switch.Root>
40
+ // );
41
+ // },
42
+ // );
43
+
44
+ // Root.displayName = "RootWebSwitch";
45
+
46
+ // const Thumb = React.forwardRef<ThumbRef, ThumbProps>(({ ...props }, ref) => {
47
+ //
48
+ // return (
49
+ // <Switch.Thumb render>
50
+ // <Component
51
+ // ref={ref}
52
+ // {...props}
53
+ // />
54
+ // </Switch.Thumb>
55
+ // );
56
+ // });
57
+
58
+ // Thumb.displayName = "ThumbWebSwitch";
59
+
60
+ // export { Root, Thumb };
@@ -0,0 +1,19 @@
1
+ import type { PressableRef, RenderPressableProps, RenderViewProps, ViewRef } from "../types";
2
+
3
+ type RootProps = RenderPressableProps & {
4
+ checked: boolean;
5
+ onCheckedChange: (checked: boolean) => void;
6
+ disabled?: boolean;
7
+
8
+ /**
9
+ * @platform: WEB ONLY
10
+ */
11
+ onKeyDown?: (ev: React.KeyboardEvent) => void;
12
+ };
13
+
14
+ type ThumbProps = RenderViewProps;
15
+
16
+ type RootRef = PressableRef;
17
+ type ThumbRef = ViewRef;
18
+
19
+ export type { RootProps, RootRef, ThumbProps, ThumbRef };
@@ -0,0 +1 @@
1
+ export * from "./table";
@@ -0,0 +1,121 @@
1
+ import * as React from "react";
2
+ import { Pressable, View } from "react-native";
3
+
4
+ import * as Slot from "../slot";
5
+ import type { PressableRef, RenderPressableProps, RenderViewProps, ViewRef } from "../types";
6
+
7
+ type RootProps = RenderViewProps;
8
+ type RootRef = ViewRef;
9
+
10
+ const Root = React.forwardRef<RootRef, RootProps>(({ ...props }, ref) => {
11
+ return (
12
+ <Component
13
+ role="table"
14
+ ref={ref}
15
+ {...props}
16
+ />
17
+ );
18
+ });
19
+ Root.displayName = "RootTable";
20
+
21
+ type HeaderProps = RenderViewProps;
22
+ type HeaderRef = ViewRef;
23
+
24
+ const Header = React.forwardRef<HeaderRef, HeaderProps>(({ ...props }, ref) => {
25
+ return (
26
+ <Component
27
+ role="rowheader"
28
+ ref={ref}
29
+ {...props}
30
+ />
31
+ );
32
+ });
33
+ Header.displayName = "HeaderTable";
34
+
35
+ type RowProps = RenderPressableProps;
36
+ type RowRef = PressableRef;
37
+
38
+ const Row = React.forwardRef<RowRef, RowProps>(({ ...props }, ref) => {
39
+ return (
40
+ <Component
41
+ ref={ref}
42
+ role="row"
43
+ {...props}
44
+ />
45
+ );
46
+ });
47
+ Row.displayName = "RowTable";
48
+
49
+ type HeadProps = RenderViewProps;
50
+ type HeadRef = ViewRef;
51
+
52
+ const Head = React.forwardRef<HeadRef, HeadProps>(({ ...props }, ref) => {
53
+ return (
54
+ <Component
55
+ ref={ref}
56
+ role="columnheader"
57
+ {...props}
58
+ />
59
+ );
60
+ });
61
+ Head.displayName = "HeadTable";
62
+
63
+ type BodyProps = RenderViewProps;
64
+ type BodyRef = ViewRef;
65
+
66
+ const Body = React.forwardRef<BodyRef, BodyProps>(({ ...props }, ref) => {
67
+ return (
68
+ <Component
69
+ ref={ref}
70
+ role="rowgroup"
71
+ {...props}
72
+ />
73
+ );
74
+ });
75
+ Body.displayName = "BodyTable";
76
+
77
+ type CellProps = RenderViewProps;
78
+ type CellRef = ViewRef;
79
+
80
+ const Cell = React.forwardRef<CellRef, CellProps>(({ ...props }, ref) => {
81
+ return (
82
+ <Component
83
+ ref={ref}
84
+ role="cell"
85
+ {...props}
86
+ />
87
+ );
88
+ });
89
+ Cell.displayName = "CellTable";
90
+
91
+ type FooterProps = RenderViewProps;
92
+ type FooterRef = ViewRef;
93
+
94
+ const Footer = React.forwardRef<FooterRef, FooterProps>(({ ...props }, ref) => {
95
+ return (
96
+ <Component
97
+ ref={ref}
98
+ role="rowgroup"
99
+ {...props}
100
+ />
101
+ );
102
+ });
103
+ Footer.displayName = "FooterTable";
104
+
105
+ export { Body, Cell, Footer, Head, Header, Root, Row };
106
+ export type {
107
+ BodyProps,
108
+ BodyRef,
109
+ CellProps,
110
+ CellRef,
111
+ FooterProps,
112
+ FooterRef,
113
+ HeaderProps,
114
+ HeaderRef,
115
+ HeadProps,
116
+ HeadRef,
117
+ RootProps,
118
+ RootRef,
119
+ RowProps,
120
+ RowRef,
121
+ };
@@ -0,0 +1,2 @@
1
+ export * from "./tabs";
2
+ export * from "./types";
@@ -0,0 +1,120 @@
1
+ import * as React from "react";
2
+ import { Pressable, View, type GestureResponderEvent } from "react-native";
3
+
4
+ import * as Slot from "../slot";
5
+
6
+ import type { ContentProps, ContentRef, ListProps, ListRef, RootProps, RootRef, TriggerProps, TriggerRef } from "./types";
7
+
8
+ interface RootContext extends RootProps {
9
+ nativeID: string;
10
+ }
11
+
12
+ const TabsContext = React.createContext<RootContext | null>(null);
13
+
14
+ const Root = React.forwardRef<RootRef, RootProps>(
15
+ ({ render, value, onValueChange, orientation: _orientation, dir: _dir, activationMode: _activationMode, ...viewProps }, ref) => {
16
+ const nativeID = React.useId();
17
+
18
+ return (
19
+ <TabsContext.Provider
20
+ value={{
21
+ value,
22
+ onValueChange,
23
+ nativeID,
24
+ }}
25
+ >
26
+ <Component
27
+ ref={ref}
28
+ {...viewProps}
29
+ />
30
+ </TabsContext.Provider>
31
+ );
32
+ },
33
+ );
34
+
35
+ Root.displayName = "RootNativeTabs";
36
+
37
+ function useRootContext() {
38
+ const context = React.useContext(TabsContext);
39
+ if (!context) {
40
+ throw new Error("Tabs compound components cannot be rendered outside the Tabs component");
41
+ }
42
+ return context;
43
+ }
44
+
45
+ const List = React.forwardRef<ListRef, ListProps>(({ ...props }, ref) => {
46
+ return (
47
+ <Component
48
+ ref={ref}
49
+ role="tablist"
50
+ {...props}
51
+ />
52
+ );
53
+ });
54
+
55
+ List.displayName = "ListNativeTabs";
56
+
57
+ const TriggerContext = React.createContext<{ value: string } | null>(null);
58
+
59
+ const Trigger = React.forwardRef<TriggerRef, TriggerProps>(({ onPress: onPressProp, disabled, value: tabValue, ...props }, ref) => {
60
+ const { onValueChange, value: rootValue, nativeID } = useRootContext();
61
+
62
+ function onPress(ev: GestureResponderEvent) {
63
+ if (disabled) return;
64
+ onValueChange(tabValue);
65
+ onPressProp?.(ev);
66
+ }
67
+
68
+ return (
69
+ <TriggerContext.Provider value={{ value: tabValue }}>
70
+ <Component
71
+ ref={ref}
72
+ nativeID={`${nativeID}-tab-${tabValue}`}
73
+ aria-disabled={!!disabled}
74
+ aria-selected={rootValue === tabValue}
75
+ role="tab"
76
+ onPress={onPress}
77
+ accessibilityState={{
78
+ selected: rootValue === tabValue,
79
+ disabled: !!disabled,
80
+ }}
81
+ disabled={!!disabled}
82
+ {...props}
83
+ />
84
+ </TriggerContext.Provider>
85
+ );
86
+ });
87
+
88
+ Trigger.displayName = "TriggerNativeTabs";
89
+
90
+ function useTriggerContext() {
91
+ const context = React.useContext(TriggerContext);
92
+ if (!context) {
93
+ throw new Error("Tabs.Trigger compound components cannot be rendered outside the Tabs.Trigger component");
94
+ }
95
+ return context;
96
+ }
97
+
98
+ const Content = React.forwardRef<ContentRef, ContentProps>(({ keepMounted, value: tabValue, ...props }, ref) => {
99
+ const { value: rootValue, nativeID } = useRootContext();
100
+
101
+ if (!keepMounted) {
102
+ if (rootValue !== tabValue) {
103
+ return null;
104
+ }
105
+ }
106
+
107
+ return (
108
+ <Component
109
+ ref={ref}
110
+ aria-hidden={!(keepMounted || rootValue === tabValue)}
111
+ aria-labelledby={`${nativeID}-tab-${tabValue}`}
112
+ role="tabpanel"
113
+ {...props}
114
+ />
115
+ );
116
+ });
117
+
118
+ Content.displayName = "ContentNativeTabs";
119
+
120
+ export { Content, List, Root, Trigger, useRootContext, useTriggerContext };