@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,279 @@
1
+ import * as React from "react";
2
+ import { BackHandler, Pressable, View, type GestureResponderEvent, type LayoutChangeEvent, type LayoutRectangle } from "react-native";
3
+
4
+ import { useAugmentedRef, useRelativePosition, type LayoutPosition } from "../hooks";
5
+ import { Portal as RNPPortal } from "../portal";
6
+ import * as Slot from "../slot";
7
+
8
+ import type {
9
+ CloseProps,
10
+ CloseRef,
11
+ ContentProps,
12
+ ContentRef,
13
+ BackdropProps,
14
+ BackdropRef,
15
+ PortalProps,
16
+ RootProps,
17
+ RootRef,
18
+ TriggerProps,
19
+ TriggerRef,
20
+ } from "./types";
21
+
22
+ interface RootContextInterface {
23
+ open: boolean;
24
+ onOpenChange: (open: boolean) => void;
25
+ triggerPosition: LayoutPosition | null;
26
+ setTriggerPosition: (triggerPosition: LayoutPosition | null) => void;
27
+ contentLayout: LayoutRectangle | null;
28
+ setContentLayout: (contentLayout: LayoutRectangle | null) => void;
29
+ nativeID: string;
30
+ }
31
+
32
+ const RootContext = React.createContext<RootContextInterface | null>(null);
33
+
34
+ const Root = React.forwardRef<RootRef, RootProps>(({ onOpenChange: onOpenChangeProp, ...viewProps }, ref) => {
35
+ const nativeID = React.useId();
36
+ const [triggerPosition, setTriggerPosition] = React.useState<LayoutPosition | null>(null);
37
+ const [contentLayout, setContentLayout] = React.useState<LayoutRectangle | null>(null);
38
+ const [open, setOpen] = React.useState(false);
39
+
40
+ function onOpenChange(value: boolean) {
41
+ setOpen(value);
42
+ onOpenChangeProp?.(value);
43
+ }
44
+
45
+ return (
46
+ <RootContext.Provider
47
+ value={{
48
+ open,
49
+ onOpenChange,
50
+ contentLayout,
51
+ nativeID,
52
+ setContentLayout,
53
+ setTriggerPosition,
54
+ triggerPosition,
55
+ }}
56
+ >
57
+ <Component
58
+ ref={ref}
59
+ {...viewProps}
60
+ />
61
+ </RootContext.Provider>
62
+ );
63
+ });
64
+
65
+ Root.displayName = "RootNativePopover";
66
+
67
+ function useRootContext() {
68
+ const context = React.useContext(RootContext);
69
+ if (!context) {
70
+ throw new Error("Popover compound components cannot be rendered outside the Popover component");
71
+ }
72
+ return context;
73
+ }
74
+
75
+ const Trigger = React.forwardRef<TriggerRef, TriggerProps>(({ onPress: onPressProp, disabled = false, ...props }, ref) => {
76
+ const { onOpenChange, open, setTriggerPosition } = useRootContext();
77
+
78
+ const augmentedRef = useAugmentedRef({
79
+ ref,
80
+ methods: {
81
+ open: () => {
82
+ onOpenChange(true);
83
+ augmentedRef.current?.measure((_x, _y, width, height, pageX, pageY) => {
84
+ setTriggerPosition({ width, pageX, pageY: pageY, height });
85
+ });
86
+ },
87
+ close: () => {
88
+ setTriggerPosition(null);
89
+ onOpenChange(false);
90
+ },
91
+ },
92
+ });
93
+
94
+ function onPress(ev: GestureResponderEvent) {
95
+ if (disabled) return;
96
+ augmentedRef.current?.measure((_x, _y, width, height, pageX, pageY) => {
97
+ setTriggerPosition({ width, pageX, pageY: pageY, height });
98
+ });
99
+ onOpenChange(!open);
100
+ onPressProp?.(ev);
101
+ }
102
+
103
+ return (
104
+ <Component
105
+ ref={augmentedRef}
106
+ aria-disabled={disabled ?? undefined}
107
+ role="button"
108
+ onPress={onPress}
109
+ disabled={disabled ?? undefined}
110
+ {...props}
111
+ />
112
+ );
113
+ });
114
+
115
+ Trigger.displayName = "TriggerNativePopover";
116
+
117
+ /**
118
+ * @warning when using a custom `<PortalHost />`, you might have to adjust the Content's sideOffset to account for nav elements like headers.
119
+ */
120
+ function Portal({ keepMounted, hostName, children }: PortalProps) {
121
+ const value = useRootContext();
122
+
123
+ if (!value.triggerPosition) {
124
+ return null;
125
+ }
126
+
127
+ if (!keepMounted) {
128
+ if (!value.open) {
129
+ return null;
130
+ }
131
+ }
132
+
133
+ return (
134
+ <RNPPortal
135
+ hostName={hostName}
136
+ name={`${value.nativeID}_portal`}
137
+ >
138
+ <RootContext.Provider value={value}>{children}</RootContext.Provider>
139
+ </RNPPortal>
140
+ );
141
+ }
142
+
143
+ const Backdrop = React.forwardRef<BackdropRef, BackdropProps>(({ keepMounted, onPress: OnPressProp, closeOnPress = true, ...props }, ref) => {
144
+ const { open, onOpenChange, setTriggerPosition, setContentLayout } = useRootContext();
145
+
146
+ function onPress(ev: GestureResponderEvent) {
147
+ if (closeOnPress) {
148
+ setTriggerPosition(null);
149
+ setContentLayout(null);
150
+ onOpenChange(false);
151
+ }
152
+ OnPressProp?.(ev);
153
+ }
154
+
155
+ if (!keepMounted) {
156
+ if (!open) {
157
+ return null;
158
+ }
159
+ }
160
+
161
+ return (
162
+ <Component
163
+ ref={ref}
164
+ onPress={onPress}
165
+ {...props}
166
+ />
167
+ );
168
+ });
169
+
170
+ Backdrop.displayName = "BackdropNativePopover";
171
+
172
+ /**
173
+ * @info `position`, `top`, `left`, and `maxWidth` style properties are controlled internally. Opt out of this behavior by setting `disablePositioningStyle` to `true`.
174
+ */
175
+ const Content = React.forwardRef<ContentRef, ContentProps>(
176
+ (
177
+ {
178
+ render = false,
179
+ keepMounted,
180
+ align = "start",
181
+ side = "bottom",
182
+ sideOffset = 0,
183
+ alignOffset = 0,
184
+ avoidCollisions = true,
185
+ onLayout: onLayoutProp,
186
+ insets,
187
+ style,
188
+ disablePositioningStyle,
189
+ onOpenAutoFocus: _onOpenAutoFocus,
190
+ ...props
191
+ },
192
+ ref,
193
+ ) => {
194
+ const { open, onOpenChange, contentLayout, nativeID, setContentLayout, setTriggerPosition, triggerPosition } = useRootContext();
195
+
196
+ React.useEffect(() => {
197
+ const backHandler = BackHandler.addEventListener("hardwareBackPress", () => {
198
+ setTriggerPosition(null);
199
+ setContentLayout(null);
200
+ onOpenChange(false);
201
+ return true;
202
+ });
203
+
204
+ return () => {
205
+ setContentLayout(null);
206
+ backHandler.remove();
207
+ };
208
+ }, []);
209
+
210
+ const positionStyle = useRelativePosition({
211
+ align,
212
+ avoidCollisions,
213
+ triggerPosition,
214
+ contentLayout,
215
+ alignOffset,
216
+ insets,
217
+ sideOffset,
218
+ side,
219
+ disablePositioningStyle,
220
+ });
221
+
222
+ function onLayout(event: LayoutChangeEvent) {
223
+ setContentLayout(event.nativeEvent.layout);
224
+ onLayoutProp?.(event);
225
+ }
226
+
227
+ if (!keepMounted) {
228
+ if (!open) {
229
+ return null;
230
+ }
231
+ }
232
+
233
+ return (
234
+ <Component
235
+ ref={ref}
236
+ role="dialog"
237
+ nativeID={nativeID}
238
+ aria-modal={true}
239
+ style={[positionStyle, style]}
240
+ onLayout={onLayout}
241
+ onStartShouldSetResponder={onStartShouldSetResponder}
242
+ {...props}
243
+ />
244
+ );
245
+ },
246
+ );
247
+
248
+ Content.displayName = "ContentNativePopover";
249
+
250
+ const Close = React.forwardRef<CloseRef, CloseProps>(({ onPress: onPressProp, disabled = false, ...props }, ref) => {
251
+ const { onOpenChange, setContentLayout, setTriggerPosition } = useRootContext();
252
+
253
+ function onPress(ev: GestureResponderEvent) {
254
+ if (disabled) return;
255
+ setTriggerPosition(null);
256
+ setContentLayout(null);
257
+ onOpenChange(false);
258
+ onPressProp?.(ev);
259
+ }
260
+
261
+ return (
262
+ <Component
263
+ ref={ref}
264
+ aria-disabled={disabled ?? undefined}
265
+ role="button"
266
+ onPress={onPress}
267
+ disabled={disabled ?? undefined}
268
+ {...props}
269
+ />
270
+ );
271
+ });
272
+
273
+ Close.displayName = "CloseNativePopover";
274
+
275
+ export { Close, Content, Backdrop, Portal, Root, Trigger, useRootContext };
276
+
277
+ function onStartShouldSetResponder() {
278
+ return true;
279
+ }
@@ -0,0 +1,217 @@
1
+ // import * as Popover from "@radix-ui/react-popover";
2
+ // import * as React from "react";
3
+ // import { Pressable, View, type GestureResponderEvent } from "react-native";
4
+
5
+ // import { useAugmentedRef, useIsomorphicLayoutEffect } from "../hooks";
6
+ // import * as Slot from "../slot";
7
+
8
+ // import type {
9
+ // CloseProps,
10
+ // CloseRef,
11
+ // ContentProps,
12
+ // ContentRef,
13
+ // BackdropProps,
14
+ // BackdropRef,
15
+ // PortalProps,
16
+ // RootProps,
17
+ // RootRef,
18
+ // TriggerProps,
19
+ // TriggerRef,
20
+ // } from "./types";
21
+
22
+ // const RootContext = React.createContext<{
23
+ // open: boolean;
24
+ // onOpenChange: (open: boolean) => void;
25
+ // } | null>(null);
26
+
27
+ // const Root = React.forwardRef<RootRef, RootProps & { onOpenChange?: (open: boolean) => void }>(
28
+ // ({ render, onOpenChange: onOpenChangeProp, ...viewProps }, ref) => {
29
+ // const [open, setOpen] = React.useState(false);
30
+
31
+ // function onOpenChange(value: boolean) {
32
+ // setOpen(value);
33
+ // onOpenChangeProp?.(value);
34
+ // }
35
+ //
36
+ // return (
37
+ // <RootContext.Provider value={{ open, onOpenChange }}>
38
+ // <Popover.Root
39
+ // open={open}
40
+ // onOpenChange={onOpenChange}
41
+ // >
42
+ // <Component
43
+ // ref={ref}
44
+ // {...viewProps}
45
+ // />
46
+ // </Popover.Root>
47
+ // </RootContext.Provider>
48
+ // );
49
+ // },
50
+ // );
51
+
52
+ // Root.displayName = "RootWebPopover";
53
+
54
+ // function useRootContext() {
55
+ // const context = React.useContext(RootContext);
56
+ // if (!context) {
57
+ // throw new Error("Popover compound components cannot be rendered outside the Popover component");
58
+ // }
59
+ // return context;
60
+ // }
61
+
62
+ // const Trigger = React.forwardRef<TriggerRef, TriggerProps>(({ onPress: onPressProp, role: _role, disabled, ...props }, ref) => {
63
+ // const { onOpenChange, open } = useRootContext();
64
+ // const augmentedRef = useAugmentedRef({
65
+ // ref,
66
+ // methods: {
67
+ // open() {
68
+ // onOpenChange(true);
69
+ // },
70
+ // close() {
71
+ // onOpenChange(false);
72
+ // },
73
+ // },
74
+ // });
75
+ // function onPress(ev: GestureResponderEvent) {
76
+ // if (onPressProp) {
77
+ // onPressProp(ev);
78
+ // }
79
+ // onOpenChange(!open);
80
+ // }
81
+
82
+ // useIsomorphicLayoutEffect(() => {
83
+ // if (augmentedRef.current) {
84
+ // const augRef = augmentedRef.current as unknown as HTMLButtonElement;
85
+ // augRef.dataset.state = open ? "open" : "closed";
86
+ // augRef.type = "button";
87
+ // }
88
+ // }, [open]);
89
+
90
+ //
91
+ // return (
92
+ // <Popover.Trigger
93
+ // disabled={disabled ?? undefined}
94
+ // render
95
+ // >
96
+ // <Component
97
+ // ref={augmentedRef}
98
+ // onPress={onPress}
99
+ // role="button"
100
+ // disabled={disabled}
101
+ // {...props}
102
+ // />
103
+ // </Popover.Trigger>
104
+ // );
105
+ // });
106
+
107
+ // Trigger.displayName = "TriggerWebPopover";
108
+
109
+ // function Portal({ keepMounted, container, children }: PortalProps) {
110
+ // return (
111
+ // <Popover.Portal
112
+ // keepMounted={keepMounted}
113
+ // children={children}
114
+ // container={container}
115
+ // />
116
+ // );
117
+ // }
118
+
119
+ // const Backdrop = React.forwardRef<BackdropRef, BackdropProps>(({ keepMounted, ...props }, ref) => {
120
+ //
121
+ // return (
122
+ // <Component
123
+ // ref={ref}
124
+ // {...props}
125
+ // />
126
+ // );
127
+ // });
128
+
129
+ // Backdrop.displayName = "BackdropWebPopover";
130
+
131
+ // const Content = React.forwardRef<ContentRef, ContentProps>(
132
+ // (
133
+ // {
134
+ // render = false,
135
+ // keepMounted,
136
+ // align = "start",
137
+ // side = "bottom",
138
+ // sideOffset = 0,
139
+ // alignOffset = 0,
140
+ // avoidCollisions = true,
141
+ // insets: _insets,
142
+ // disablePositioningStyle: _disablePositioningStyle,
143
+ // onCloseAutoFocus,
144
+ // onEscapeKeyDown,
145
+ // onInteractOutside,
146
+ // onPointerDownOutside,
147
+ // onOpenAutoFocus,
148
+ // ...props
149
+ // },
150
+ // ref,
151
+ // ) => {
152
+ //
153
+ // return (
154
+ // <Popover.Content
155
+ // onCloseAutoFocus={onCloseAutoFocus}
156
+ // onEscapeKeyDown={onEscapeKeyDown}
157
+ // onInteractOutside={onInteractOutside}
158
+ // onPointerDownOutside={onPointerDownOutside}
159
+ // keepMounted={keepMounted}
160
+ // align={align}
161
+ // side={side}
162
+ // sideOffset={sideOffset}
163
+ // alignOffset={alignOffset}
164
+ // avoidCollisions={avoidCollisions}
165
+ // onOpenAutoFocus={onOpenAutoFocus}
166
+ // >
167
+ // <Component
168
+ // ref={ref}
169
+ // {...props}
170
+ // />
171
+ // </Popover.Content>
172
+ // );
173
+ // },
174
+ // );
175
+
176
+ // Content.displayName = "ContentWebPopover";
177
+
178
+ // const Close = React.forwardRef<CloseRef, CloseProps>(({ onPress: onPressProp, disabled, ...props }, ref) => {
179
+ // const augmentedRef = useAugmentedRef({ ref });
180
+ // const { onOpenChange, open } = useRootContext();
181
+
182
+ // function onPress(ev: GestureResponderEvent) {
183
+ // if (onPressProp) {
184
+ // onPressProp(ev);
185
+ // }
186
+ // onOpenChange(!open);
187
+ // }
188
+
189
+ // useIsomorphicLayoutEffect(() => {
190
+ // if (augmentedRef.current) {
191
+ // const augRef = augmentedRef.current as unknown as HTMLButtonElement;
192
+ // augRef.type = "button";
193
+ // }
194
+ // }, []);
195
+
196
+ //
197
+ // return (
198
+ // <>
199
+ // <Popover.Close
200
+ // disabled={disabled ?? undefined}
201
+ // render
202
+ // >
203
+ // <Component
204
+ // ref={augmentedRef}
205
+ // onPress={onPress}
206
+ // role="button"
207
+ // disabled={disabled}
208
+ // {...props}
209
+ // />
210
+ // </Popover.Close>
211
+ // </>
212
+ // );
213
+ // });
214
+
215
+ // Close.displayName = "CloseWebPopover";
216
+
217
+ // export { Close, Content, Backdrop, Portal, Root, Trigger, useRootContext };
@@ -0,0 +1,44 @@
1
+ import type { KeepMountable, PositionedContentProps, PressableRef, RenderPressableProps, RenderViewProps, ViewRef } from "../types";
2
+
3
+ type RootProps = RenderViewProps & {
4
+ onOpenChange?: (open: boolean) => void;
5
+ };
6
+ interface PortalProps extends KeepMountable {
7
+ children: React.ReactNode;
8
+
9
+ /**
10
+ * @platform: NATIVE ONLY
11
+ */
12
+ hostName?: string;
13
+
14
+ /**
15
+ * @platform: WEB ONLY
16
+ */
17
+ container?: HTMLElement | null | undefined;
18
+ }
19
+
20
+ type BackdropProps = KeepMountable &
21
+ RenderPressableProps & {
22
+ closeOnPress?: boolean;
23
+ };
24
+
25
+ type TriggerProps = RenderPressableProps;
26
+ type ContentProps = RenderViewProps &
27
+ PositionedContentProps & {
28
+ /**
29
+ * @platform: WEB ONLY
30
+ */
31
+ onOpenAutoFocus?: (event: Event) => void;
32
+ };
33
+ type CloseProps = RenderPressableProps;
34
+
35
+ type CloseRef = PressableRef;
36
+ type ContentRef = ViewRef;
37
+ type BackdropRef = PressableRef;
38
+ type RootRef = ViewRef;
39
+ type TriggerRef = PressableRef & {
40
+ open: () => void;
41
+ close: () => void;
42
+ };
43
+
44
+ export type { CloseProps, CloseRef, ContentProps, ContentRef, BackdropProps, BackdropRef, PortalProps, RootProps, RootRef, TriggerProps, TriggerRef };
@@ -0,0 +1 @@
1
+ export * from "./portal";
@@ -0,0 +1,56 @@
1
+ import { useEffect } from "react";
2
+ import { create } from "zustand"; // TODO: drop zustand and implement own store functionaly
3
+
4
+ const DEFAULT_PORTAL_HOST_NAME = "__INTERNAL_PRIMITIVE_DEFAULT_HOST_NAME";
5
+
6
+ type PortalMap = Map<string, React.ReactNode>;
7
+
8
+ const startingState = {
9
+ map: new Map<string, PortalMap>(),
10
+ };
11
+
12
+ startingState.map.set(DEFAULT_PORTAL_HOST_NAME, new Map<string, React.ReactNode>());
13
+
14
+ const usePortal = create(() => startingState);
15
+
16
+ const updatePortal = (hostName: string, name: string, children: React.ReactNode) => {
17
+ usePortal.setState((prev) => {
18
+ const next = new Map(prev.map);
19
+ const portal = next.get(hostName) ?? new Map<string, React.ReactNode>();
20
+ portal.set(name, children);
21
+ next.set(hostName, portal);
22
+ return { map: next };
23
+ });
24
+ };
25
+
26
+ const removePortal = (hostName: string, name: string) => {
27
+ usePortal.setState((prev) => {
28
+ const next = new Map(prev.map);
29
+ const portal = next.get(hostName) ?? new Map<string, React.ReactNode>();
30
+ portal.delete(name);
31
+ next.set(hostName, portal);
32
+ return { map: next };
33
+ });
34
+ };
35
+
36
+ export function PortalHost({ name = DEFAULT_PORTAL_HOST_NAME }: { name?: string }) {
37
+ const portalMap = usePortal((state) => state.map).get(name);
38
+ if (!portalMap || portalMap.size === 0) {
39
+ return null;
40
+ }
41
+ return <>{Array.from(portalMap.values())}</>;
42
+ }
43
+
44
+ export function Portal({ name, hostName = DEFAULT_PORTAL_HOST_NAME, children }: { name: string; hostName?: string; children: React.ReactNode }) {
45
+ useEffect(() => {
46
+ updatePortal(hostName, name, children);
47
+ }, [hostName, name, children]);
48
+
49
+ useEffect(() => {
50
+ return () => {
51
+ removePortal(hostName, name);
52
+ };
53
+ }, [hostName, name]);
54
+
55
+ return null;
56
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./progress";
2
+ export * from "./types";
@@ -0,0 +1,59 @@
1
+ import * as React from "react";
2
+ import { View } from "react-native";
3
+
4
+ import * as Slot from "../slot";
5
+
6
+ import type { IndicatorProps, IndicatorRef, RootProps, RootRef } from "./types";
7
+
8
+ // This project uses code from WorkOS/Radix Primitives.
9
+ // The code is licensed under the MIT License.
10
+ // https://github.com/radix-ui/primitives/tree/main
11
+
12
+ const DEFAULT_MAX = 100;
13
+
14
+ const Root = React.forwardRef<RootRef, RootProps>(({ value: valueProp, max: maxProp, getValueLabel = defaultGetValueLabel, ...props }, ref) => {
15
+ const max = maxProp ?? DEFAULT_MAX;
16
+ const value = isValidValueNumber(valueProp, max) ? valueProp : 0;
17
+
18
+ return (
19
+ <Component
20
+ role="progressbar"
21
+ ref={ref}
22
+ aria-valuemax={max}
23
+ aria-valuemin={0}
24
+ aria-valuenow={value}
25
+ aria-valuetext={getValueLabel(value, max)}
26
+ accessibilityValue={{
27
+ min: 0,
28
+ max,
29
+ now: value,
30
+ text: getValueLabel(value, max),
31
+ }}
32
+ {...props}
33
+ />
34
+ );
35
+ });
36
+
37
+ Root.displayName = "RootProgress";
38
+
39
+ const Indicator = React.forwardRef<IndicatorRef, IndicatorProps>(({ ...props }, ref) => {
40
+ return (
41
+ <Component
42
+ ref={ref}
43
+ role="presentation"
44
+ {...props}
45
+ />
46
+ );
47
+ });
48
+
49
+ Indicator.displayName = "IndicatorProgress";
50
+
51
+ export { Indicator, Root };
52
+
53
+ function defaultGetValueLabel(value: number, max: number) {
54
+ return `${Math.round((value / max) * 100)}%`;
55
+ }
56
+
57
+ function isValidValueNumber(value: any, max: number): value is number {
58
+ return typeof value === "number" && !isNaN(value) && value <= max && value >= 0;
59
+ }
@@ -0,0 +1,46 @@
1
+ // import * as Progress from "@radix-ui/react-progress";
2
+ // import * as React from "react";
3
+ // import { View } from "react-native";
4
+
5
+ // import * as Slot from "../slot";
6
+
7
+ // import type { IndicatorProps, IndicatorRef, RootProps, RootRef } from "./types";
8
+
9
+ // const ProgressContext = React.createContext<RootProps | null>(null);
10
+
11
+ // const Root = React.forwardRef<RootRef, RootProps>(({ value, max, getValueLabel, ...props }, ref) => {
12
+ //
13
+ // return (
14
+ // <ProgressContext.Provider value={{ value, max }}>
15
+ // <Progress.Root
16
+ // value={value}
17
+ // max={max}
18
+ // getValueLabel={getValueLabel}
19
+ // render
20
+ // >
21
+ // <Component
22
+ // ref={ref}
23
+ // {...props}
24
+ // />
25
+ // </Progress.Root>
26
+ // </ProgressContext.Provider>
27
+ // );
28
+ // });
29
+
30
+ // Root.displayName = "RootProgress";
31
+
32
+ // const Indicator = React.forwardRef<IndicatorRef, IndicatorProps>(({ ...props }, ref) => {
33
+ //
34
+ // return (
35
+ // <Progress.Indicator render>
36
+ // <Component
37
+ // ref={ref}
38
+ // {...props}
39
+ // />
40
+ // </Progress.Indicator>
41
+ // );
42
+ // });
43
+
44
+ // Indicator.displayName = "IndicatorProgress";
45
+
46
+ // export { Indicator, Root };