linked-data-browser 0.0.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 (91) hide show
  1. package/.eslintrc.js +13 -0
  2. package/.ldo/profile.context.ts +459 -0
  3. package/.ldo/profile.schema.ts +751 -0
  4. package/.ldo/profile.shapeTypes.ts +71 -0
  5. package/.ldo/profile.typings.ts +295 -0
  6. package/.prettierignore +6 -0
  7. package/.prettierrc +10 -0
  8. package/.shapes/profile.shex +121 -0
  9. package/README.md +3 -0
  10. package/app/index.tsx +25 -0
  11. package/app.json +37 -0
  12. package/assets/images/adaptive-icon.png +0 -0
  13. package/assets/images/favicon.png +0 -0
  14. package/assets/images/icon.png +0 -0
  15. package/assets/images/splash.png +0 -0
  16. package/babel.config.js +6 -0
  17. package/components/DataBrowser.tsx +57 -0
  18. package/components/ResourceView.tsx +14 -0
  19. package/components/TargetResourceProvider.tsx +128 -0
  20. package/components/ThemeProvider.tsx +123 -0
  21. package/components/nav/DialogProvider.tsx +140 -0
  22. package/components/nav/Layout.tsx +118 -0
  23. package/components/nav/header/AddressBox.tsx +126 -0
  24. package/components/nav/header/AvatarMenu.tsx +62 -0
  25. package/components/nav/header/Header.tsx +28 -0
  26. package/components/nav/header/SignInMenu.tsx +126 -0
  27. package/components/nav/header/ThemeToggleMenu.tsx +34 -0
  28. package/components/nav/header/ViewMenu.tsx +88 -0
  29. package/components/nav/useValidView.tsx +51 -0
  30. package/components/nav/utilityResourceViews/ErrorMessageResourceView.tsx +26 -0
  31. package/components/ui/avatar.tsx +53 -0
  32. package/components/ui/button.tsx +88 -0
  33. package/components/ui/card.tsx +101 -0
  34. package/components/ui/dialog.tsx +159 -0
  35. package/components/ui/dropdown-menu.tsx +275 -0
  36. package/components/ui/input.tsx +25 -0
  37. package/components/ui/label.tsx +34 -0
  38. package/components/ui/navigation-menu.tsx +200 -0
  39. package/components/ui/popover.tsx +45 -0
  40. package/components/ui/progress.tsx +79 -0
  41. package/components/ui/radio-group.tsx +59 -0
  42. package/components/ui/separator.tsx +27 -0
  43. package/components/ui/switch.tsx +105 -0
  44. package/components/ui/text.tsx +30 -0
  45. package/components/ui/tooltip.tsx +46 -0
  46. package/components.json +7 -0
  47. package/global.css +61 -0
  48. package/index.js +12 -0
  49. package/lib/android-navigation-bar.ts +11 -0
  50. package/lib/constants.ts +18 -0
  51. package/lib/icons/ArrowRight.tsx +4 -0
  52. package/lib/icons/Check.tsx +4 -0
  53. package/lib/icons/ChevronDown.tsx +4 -0
  54. package/lib/icons/ChevronRight.tsx +4 -0
  55. package/lib/icons/ChevronUp.tsx +4 -0
  56. package/lib/icons/ChevronsRight.tsx +4 -0
  57. package/lib/icons/CircleSlash.tsx +4 -0
  58. package/lib/icons/CircleX.tsx +4 -0
  59. package/lib/icons/Code.tsx +4 -0
  60. package/lib/icons/EllipsisVertical.tsx +4 -0
  61. package/lib/icons/EyeOff.tsx +4 -0
  62. package/lib/icons/File.tsx +4 -0
  63. package/lib/icons/Folder.tsx +4 -0
  64. package/lib/icons/Folders.tsx +4 -0
  65. package/lib/icons/Info.tsx +4 -0
  66. package/lib/icons/MonitorSmartphone.tsx +4 -0
  67. package/lib/icons/MoonStar.tsx +4 -0
  68. package/lib/icons/OctagonX.tsx +4 -0
  69. package/lib/icons/RefreshCw.tsx +4 -0
  70. package/lib/icons/ShieldX.tsx +4 -0
  71. package/lib/icons/Sun.tsx +4 -0
  72. package/lib/icons/TextCursorInput.tsx +4 -0
  73. package/lib/icons/Trash.tsx +4 -0
  74. package/lib/icons/ViewIcon.tsx +4 -0
  75. package/lib/icons/X.tsx +4 -0
  76. package/lib/icons/iconWithClassName.ts +14 -0
  77. package/lib/utils.ts +6 -0
  78. package/metro.config.js +6 -0
  79. package/nativewind-env.d.ts +1 -0
  80. package/package.json +89 -0
  81. package/resourceViews/Container/ContainerConfig.tsx +13 -0
  82. package/resourceViews/Container/ContainerView.tsx +148 -0
  83. package/resourceViews/RawCode/RawCodeConfig.tsx +11 -0
  84. package/resourceViews/RawCode/RawCodeEditor.tsx +37 -0
  85. package/resourceViews/RawCode/RawCodeView.tsx +67 -0
  86. package/scripts/adjust-server-paths.js +37 -0
  87. package/scripts/adjust-standalone-paths.js +28 -0
  88. package/tailwind.config.js +69 -0
  89. package/test-server/server-config.json +75 -0
  90. package/test-server/solid-css-seed.json +11 -0
  91. package/tsconfig.json +19 -0
@@ -0,0 +1,159 @@
1
+ import * as DialogPrimitive from '@rn-primitives/dialog';
2
+ import * as React from 'react';
3
+ import { Platform, StyleSheet, View, type ViewProps } from 'react-native';
4
+ import Animated, { FadeIn, FadeOut } from 'react-native-reanimated';
5
+ import { X } from '~/lib/icons/X';
6
+ import { cn } from '~/lib/utils';
7
+
8
+ const Dialog = DialogPrimitive.Root;
9
+
10
+ const DialogTrigger = DialogPrimitive.Trigger;
11
+
12
+ const DialogPortal = DialogPrimitive.Portal;
13
+
14
+ const DialogClose = DialogPrimitive.Close;
15
+
16
+ function DialogOverlayWeb({
17
+ className,
18
+ ...props
19
+ }: DialogPrimitive.OverlayProps & {
20
+ ref?: React.RefObject<DialogPrimitive.OverlayRef>;
21
+ }) {
22
+ const { open } = DialogPrimitive.useRootContext();
23
+ return (
24
+ <DialogPrimitive.Overlay
25
+ className={cn(
26
+ 'bg-black/80 flex justify-center items-center p-2 absolute top-0 right-0 bottom-0 left-0',
27
+ open ? 'web:animate-in web:fade-in-0' : 'web:animate-out web:fade-out-0',
28
+ className
29
+ )}
30
+ {...props}
31
+ />
32
+ );
33
+ }
34
+
35
+ function DialogOverlayNative({
36
+ className,
37
+ children,
38
+ ...props
39
+ }: DialogPrimitive.OverlayProps & {
40
+ ref?: React.RefObject<DialogPrimitive.OverlayRef>;
41
+ children?: React.ReactNode;
42
+ }) {
43
+ return (
44
+ <DialogPrimitive.Overlay
45
+ style={StyleSheet.absoluteFill}
46
+ className={cn('flex bg-black/80 justify-center items-center p-2', className)}
47
+ {...props}
48
+ >
49
+ <Animated.View entering={FadeIn.duration(150)} exiting={FadeOut.duration(150)}>
50
+ {children}
51
+ </Animated.View>
52
+ </DialogPrimitive.Overlay>
53
+ );
54
+ }
55
+
56
+ const DialogOverlay = Platform.select({
57
+ web: DialogOverlayWeb,
58
+ default: DialogOverlayNative,
59
+ });
60
+
61
+ function DialogContent({
62
+ className,
63
+ children,
64
+ portalHost,
65
+ ...props
66
+ }: DialogPrimitive.ContentProps & {
67
+ ref?: React.RefObject<DialogPrimitive.ContentRef>;
68
+ className?: string;
69
+ portalHost?: string;
70
+ }) {
71
+ const { open } = DialogPrimitive.useRootContext();
72
+ return (
73
+ <DialogPortal hostName={portalHost}>
74
+ <DialogOverlay>
75
+ <DialogPrimitive.Content
76
+ className={cn(
77
+ 'max-w-lg gap-4 border border-border web:cursor-default bg-background p-6 shadow-lg web:duration-200 rounded-lg',
78
+ open
79
+ ? 'web:animate-in web:fade-in-0 web:zoom-in-95'
80
+ : 'web:animate-out web:fade-out-0 web:zoom-out-95',
81
+ className
82
+ )}
83
+ {...props}
84
+ >
85
+ {children}
86
+ <DialogPrimitive.Close
87
+ className={
88
+ 'absolute right-4 top-4 p-0.5 web:group rounded-sm opacity-70 web:ring-offset-background web:transition-opacity web:hover:opacity-100 web:focus:outline-none web:focus:ring-2 web:focus:ring-ring web:focus:ring-offset-2 web:disabled:pointer-events-none'
89
+ }
90
+ >
91
+ <X
92
+ size={Platform.OS === 'web' ? 16 : 18}
93
+ className={cn('text-muted-foreground', open && 'text-accent-foreground')}
94
+ />
95
+ </DialogPrimitive.Close>
96
+ </DialogPrimitive.Content>
97
+ </DialogOverlay>
98
+ </DialogPortal>
99
+ );
100
+ }
101
+
102
+ function DialogHeader({ className, ...props }: ViewProps) {
103
+ return (
104
+ <View className={cn('flex flex-col gap-1.5 text-center sm:text-left', className)} {...props} />
105
+ );
106
+ }
107
+
108
+ function DialogFooter({ className, ...props }: ViewProps) {
109
+ return (
110
+ <View
111
+ className={cn('flex flex-col-reverse sm:flex-row sm:justify-end gap-2', className)}
112
+ {...props}
113
+ />
114
+ );
115
+ }
116
+
117
+ function DialogTitle({
118
+ className,
119
+ ...props
120
+ }: DialogPrimitive.TitleProps & {
121
+ ref?: React.RefObject<DialogPrimitive.TitleRef>;
122
+ }) {
123
+ return (
124
+ <DialogPrimitive.Title
125
+ className={cn(
126
+ 'text-lg native:text-xl text-foreground font-semibold leading-none tracking-tight',
127
+ className
128
+ )}
129
+ {...props}
130
+ />
131
+ );
132
+ }
133
+
134
+ function DialogDescription({
135
+ className,
136
+ ...props
137
+ }: DialogPrimitive.DescriptionProps & {
138
+ ref?: React.RefObject<DialogPrimitive.DescriptionRef>;
139
+ }) {
140
+ return (
141
+ <DialogPrimitive.Description
142
+ className={cn('text-sm native:text-base text-muted-foreground', className)}
143
+ {...props}
144
+ />
145
+ );
146
+ }
147
+
148
+ export {
149
+ Dialog,
150
+ DialogClose,
151
+ DialogContent,
152
+ DialogDescription,
153
+ DialogFooter,
154
+ DialogHeader,
155
+ DialogOverlay,
156
+ DialogPortal,
157
+ DialogTitle,
158
+ DialogTrigger,
159
+ };
@@ -0,0 +1,275 @@
1
+ import * as DropdownMenuPrimitive from '@rn-primitives/dropdown-menu';
2
+ import * as React from 'react';
3
+ import {
4
+ Platform,
5
+ type StyleProp,
6
+ StyleSheet,
7
+ Text,
8
+ type TextProps,
9
+ View,
10
+ type ViewStyle,
11
+ } from 'react-native';
12
+ import { Check } from '~/lib/icons/Check';
13
+ import { ChevronDown } from '~/lib/icons/ChevronDown';
14
+ import { ChevronRight } from '~/lib/icons/ChevronRight';
15
+ import { ChevronUp } from '~/lib/icons/ChevronUp';
16
+ import { cn } from '~/lib/utils';
17
+ import { TextClassContext } from '~/components/ui/text';
18
+
19
+ const DropdownMenu = DropdownMenuPrimitive.Root;
20
+
21
+ const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
22
+
23
+ const DropdownMenuGroup = DropdownMenuPrimitive.Group;
24
+
25
+ const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
26
+
27
+ const DropdownMenuSub = DropdownMenuPrimitive.Sub;
28
+
29
+ const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
30
+
31
+ function DropdownMenuSubTrigger({
32
+ className,
33
+ inset,
34
+ children,
35
+ ...props
36
+ }: DropdownMenuPrimitive.SubTriggerProps & {
37
+ ref?: React.RefObject<DropdownMenuPrimitive.SubTriggerRef>;
38
+ className?: string;
39
+ inset?: boolean;
40
+ children?: React.ReactNode;
41
+ }) {
42
+ const { open } = DropdownMenuPrimitive.useSubContext();
43
+ const Icon =
44
+ Platform.OS === 'web' ? ChevronRight : open ? ChevronUp : ChevronDown;
45
+ return (
46
+ <TextClassContext.Provider
47
+ value={cn(
48
+ 'select-none text-sm native:text-lg text-primary',
49
+ open && 'native:text-accent-foreground',
50
+ )}
51
+ >
52
+ <DropdownMenuPrimitive.SubTrigger
53
+ className={cn(
54
+ 'flex flex-row web:cursor-default web:select-none gap-2 items-center web:focus:bg-accent web:hover:bg-accent active:bg-accent rounded-sm px-2 py-1.5 native:py-2 web:outline-none',
55
+ open && 'bg-accent',
56
+ inset && 'pl-8',
57
+ className,
58
+ )}
59
+ {...props}
60
+ >
61
+ {children}
62
+ <Icon size={18} className="ml-auto text-foreground" />
63
+ </DropdownMenuPrimitive.SubTrigger>
64
+ </TextClassContext.Provider>
65
+ );
66
+ }
67
+
68
+ function DropdownMenuSubContent({
69
+ className,
70
+ ...props
71
+ }: DropdownMenuPrimitive.SubContentProps & {
72
+ ref?: React.RefObject<DropdownMenuPrimitive.SubContentRef>;
73
+ }) {
74
+ const { open } = DropdownMenuPrimitive.useSubContext();
75
+ return (
76
+ <DropdownMenuPrimitive.SubContent
77
+ className={cn(
78
+ 'z-50 min-w-[8rem] overflow-hidden rounded-md border border-border mt-1 bg-popover p-1 shadow-md shadow-foreground/5 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
79
+ open
80
+ ? 'web:animate-in web:fade-in-0 web:zoom-in-95'
81
+ : 'web:animate-out web:fade-out-0 web:zoom-out',
82
+ className,
83
+ )}
84
+ {...props}
85
+ />
86
+ );
87
+ }
88
+
89
+ function DropdownMenuContent({
90
+ className,
91
+ overlayClassName,
92
+ overlayStyle,
93
+ portalHost,
94
+ ...props
95
+ }: DropdownMenuPrimitive.ContentProps & {
96
+ ref?: React.RefObject<DropdownMenuPrimitive.ContentRef>;
97
+ overlayStyle?: StyleProp<ViewStyle>;
98
+ overlayClassName?: string;
99
+ portalHost?: string;
100
+ }) {
101
+ const { open } = DropdownMenuPrimitive.useRootContext();
102
+ return (
103
+ <DropdownMenuPrimitive.Portal hostName={portalHost}>
104
+ <DropdownMenuPrimitive.Overlay
105
+ style={
106
+ overlayStyle
107
+ ? StyleSheet.flatten([
108
+ Platform.OS !== 'web' ? StyleSheet.absoluteFill : undefined,
109
+ overlayStyle as typeof StyleSheet.absoluteFill,
110
+ ])
111
+ : Platform.OS !== 'web'
112
+ ? StyleSheet.absoluteFill
113
+ : undefined
114
+ }
115
+ className={overlayClassName}
116
+ >
117
+ <DropdownMenuPrimitive.Content
118
+ className={cn(
119
+ 'z-50 min-w-[8rem] overflow-hidden rounded-md border border-border bg-popover p-1 shadow-md shadow-foreground/5 web:data-[side=bottom]:slide-in-from-top-2 web:data-[side=left]:slide-in-from-right-2 web:data-[side=right]:slide-in-from-left-2 web:data-[side=top]:slide-in-from-bottom-2',
120
+ open
121
+ ? 'web:animate-in web:fade-in-0 web:zoom-in-95'
122
+ : 'web:animate-out web:fade-out-0 web:zoom-out-95',
123
+ className,
124
+ )}
125
+ {...props}
126
+ />
127
+ </DropdownMenuPrimitive.Overlay>
128
+ </DropdownMenuPrimitive.Portal>
129
+ );
130
+ }
131
+
132
+ function DropdownMenuItem({
133
+ className,
134
+ inset,
135
+ ...props
136
+ }: DropdownMenuPrimitive.ItemProps & {
137
+ ref?: React.RefObject<DropdownMenuPrimitive.ItemRef>;
138
+ className?: string;
139
+ inset?: boolean;
140
+ }) {
141
+ return (
142
+ <TextClassContext.Provider value="select-none text-sm native:text-lg text-popover-foreground web:group-focus:text-accent-foreground">
143
+ <DropdownMenuPrimitive.Item
144
+ className={cn(
145
+ 'relative flex flex-row web:cursor-default gap-2 items-center rounded-sm px-2 py-1.5 native:py-2 web:outline-none web:focus:bg-accent active:bg-accent web:hover:bg-accent group',
146
+ inset && 'pl-8',
147
+ props.disabled && 'opacity-50 web:pointer-events-none',
148
+ className,
149
+ )}
150
+ {...props}
151
+ />
152
+ </TextClassContext.Provider>
153
+ );
154
+ }
155
+
156
+ function DropdownMenuCheckboxItem({
157
+ className,
158
+ children,
159
+ checked,
160
+ ...props
161
+ }: DropdownMenuPrimitive.CheckboxItemProps & {
162
+ ref?: React.RefObject<DropdownMenuPrimitive.CheckboxItemRef>;
163
+ children?: React.ReactNode;
164
+ }) {
165
+ return (
166
+ <DropdownMenuPrimitive.CheckboxItem
167
+ className={cn(
168
+ 'relative flex flex-row web:cursor-default items-center web:group rounded-sm py-1.5 native:py-2 pl-8 pr-2 web:outline-none web:focus:bg-accent active:bg-accent',
169
+ props.disabled && 'web:pointer-events-none opacity-50',
170
+ className,
171
+ )}
172
+ checked={checked}
173
+ {...props}
174
+ >
175
+ <View className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
176
+ <DropdownMenuPrimitive.ItemIndicator>
177
+ <Check size={14} strokeWidth={3} className="text-foreground" />
178
+ </DropdownMenuPrimitive.ItemIndicator>
179
+ </View>
180
+ {children}
181
+ </DropdownMenuPrimitive.CheckboxItem>
182
+ );
183
+ }
184
+
185
+ function DropdownMenuRadioItem({
186
+ className,
187
+ children,
188
+ ...props
189
+ }: DropdownMenuPrimitive.RadioItemProps & {
190
+ ref?: React.RefObject<DropdownMenuPrimitive.RadioItemRef>;
191
+ children?: React.ReactNode;
192
+ }) {
193
+ return (
194
+ <DropdownMenuPrimitive.RadioItem
195
+ className={cn(
196
+ 'relative flex flex-row web:cursor-default web:group items-center rounded-sm py-1.5 native:py-2 pl-8 pr-2 web:outline-none web:focus:bg-accent active:bg-accent',
197
+ props.disabled && 'web:pointer-events-none opacity-50',
198
+ className,
199
+ )}
200
+ {...props}
201
+ >
202
+ <View className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
203
+ <DropdownMenuPrimitive.ItemIndicator>
204
+ <View className="bg-foreground h-2 w-2 rounded-full" />
205
+ </DropdownMenuPrimitive.ItemIndicator>
206
+ </View>
207
+ {children}
208
+ </DropdownMenuPrimitive.RadioItem>
209
+ );
210
+ }
211
+
212
+ function DropdownMenuLabel({
213
+ className,
214
+ inset,
215
+ ...props
216
+ }: DropdownMenuPrimitive.LabelProps & {
217
+ ref?: React.RefObject<DropdownMenuPrimitive.LabelRef>;
218
+ className?: string;
219
+ inset?: boolean;
220
+ }) {
221
+ return (
222
+ <DropdownMenuPrimitive.Label
223
+ className={cn(
224
+ 'px-2 py-1.5 text-sm native:text-base font-semibold text-foreground web:cursor-default',
225
+ inset && 'pl-8',
226
+ className,
227
+ )}
228
+ {...props}
229
+ />
230
+ );
231
+ }
232
+
233
+ function DropdownMenuSeparator({
234
+ className,
235
+ ...props
236
+ }: DropdownMenuPrimitive.SeparatorProps & {
237
+ ref?: React.RefObject<DropdownMenuPrimitive.SeparatorRef>;
238
+ }) {
239
+ return (
240
+ <DropdownMenuPrimitive.Separator
241
+ className={cn('-mx-1 my-1 h-px bg-border', className)}
242
+ {...props}
243
+ />
244
+ );
245
+ }
246
+
247
+ function DropdownMenuShortcut({ className, ...props }: TextProps) {
248
+ return (
249
+ <Text
250
+ className={cn(
251
+ 'ml-auto text-xs native:text-sm tracking-widest text-muted-foreground',
252
+ className,
253
+ )}
254
+ {...props}
255
+ />
256
+ );
257
+ }
258
+
259
+ export {
260
+ DropdownMenu,
261
+ DropdownMenuCheckboxItem,
262
+ DropdownMenuContent,
263
+ DropdownMenuGroup,
264
+ DropdownMenuItem,
265
+ DropdownMenuLabel,
266
+ DropdownMenuPortal,
267
+ DropdownMenuRadioGroup,
268
+ DropdownMenuRadioItem,
269
+ DropdownMenuSeparator,
270
+ DropdownMenuShortcut,
271
+ DropdownMenuSub,
272
+ DropdownMenuSubContent,
273
+ DropdownMenuSubTrigger,
274
+ DropdownMenuTrigger,
275
+ };
@@ -0,0 +1,25 @@
1
+ import * as React from 'react';
2
+ import { TextInput, type TextInputProps } from 'react-native';
3
+ import { cn } from '~/lib/utils';
4
+
5
+ function Input({
6
+ className,
7
+ placeholderClassName,
8
+ ...props
9
+ }: TextInputProps & {
10
+ ref?: React.RefObject<TextInput>;
11
+ }) {
12
+ return (
13
+ <TextInput
14
+ className={cn(
15
+ 'web:flex h-10 native:h-12 web:w-full rounded-md border border-input bg-background px-3 web:py-2 text-base lg:text-sm native:text-lg native:leading-[1.25] text-foreground placeholder:text-muted-foreground web:ring-offset-background file:border-0 file:bg-transparent file:font-medium web:focus-visible:outline-none web:focus-visible:ring-2 web:focus-visible:ring-ring web:focus-visible:ring-offset-2',
16
+ props.editable === false && 'opacity-50 web:cursor-not-allowed',
17
+ className,
18
+ )}
19
+ placeholderClassName={cn('text-muted-foreground', placeholderClassName)}
20
+ {...props}
21
+ />
22
+ );
23
+ }
24
+
25
+ export { Input };
@@ -0,0 +1,34 @@
1
+ import * as LabelPrimitive from '@rn-primitives/label';
2
+ import * as React from 'react';
3
+ import { cn } from '~/lib/utils';
4
+
5
+ function Label({
6
+ className,
7
+ onPress,
8
+ onLongPress,
9
+ onPressIn,
10
+ onPressOut,
11
+ ...props
12
+ }: LabelPrimitive.TextProps & {
13
+ ref?: React.RefObject<LabelPrimitive.TextRef>;
14
+ }) {
15
+ return (
16
+ <LabelPrimitive.Root
17
+ className="web:cursor-default"
18
+ onPress={onPress}
19
+ onLongPress={onLongPress}
20
+ onPressIn={onPressIn}
21
+ onPressOut={onPressOut}
22
+ >
23
+ <LabelPrimitive.Text
24
+ className={cn(
25
+ 'text-sm text-foreground native:text-base font-medium leading-none web:peer-disabled:cursor-not-allowed web:peer-disabled:opacity-70',
26
+ className,
27
+ )}
28
+ {...props}
29
+ />
30
+ </LabelPrimitive.Root>
31
+ );
32
+ }
33
+
34
+ export { Label };
@@ -0,0 +1,200 @@
1
+ import * as NavigationMenuPrimitive from '@rn-primitives/navigation-menu';
2
+ import { cva } from 'class-variance-authority';
3
+ import * as React from 'react';
4
+ import { Platform, View } from 'react-native';
5
+ import Animated, {
6
+ Extrapolation,
7
+ FadeInLeft,
8
+ FadeOutLeft,
9
+ interpolate,
10
+ useAnimatedStyle,
11
+ useDerivedValue,
12
+ withTiming,
13
+ } from 'react-native-reanimated';
14
+ import { ChevronDown } from '~/lib/icons/ChevronDown';
15
+ import { cn } from '~/lib/utils';
16
+
17
+ function NavigationMenu({
18
+ className,
19
+ children,
20
+ ...props
21
+ }: NavigationMenuPrimitive.RootProps & {
22
+ ref?: React.RefObject<NavigationMenuPrimitive.RootRef>;
23
+ }) {
24
+ return (
25
+ <NavigationMenuPrimitive.Root
26
+ className={cn(
27
+ 'relative z-10 flex flex-row max-w-max items-center justify-center',
28
+ className,
29
+ )}
30
+ {...props}
31
+ >
32
+ {children}
33
+ {Platform.OS === 'web' && <NavigationMenuViewport />}
34
+ </NavigationMenuPrimitive.Root>
35
+ );
36
+ }
37
+
38
+ function NavigationMenuList({
39
+ className,
40
+ ...props
41
+ }: NavigationMenuPrimitive.ListProps & {
42
+ ref?: React.RefObject<NavigationMenuPrimitive.ListRef>;
43
+ }) {
44
+ return (
45
+ <NavigationMenuPrimitive.List
46
+ className={cn(
47
+ 'web:group flex flex-1 flex-row web:list-none items-center justify-center gap-1',
48
+ className,
49
+ )}
50
+ {...props}
51
+ />
52
+ );
53
+ }
54
+
55
+ const NavigationMenuItem = NavigationMenuPrimitive.Item;
56
+
57
+ const navigationMenuTriggerStyle = cva(
58
+ 'web:group web:inline-flex flex-row h-10 native:h-12 native:px-3 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium web:transition-colors web:hover:bg-accent active:bg-accent web:hover:text-accent-foreground web:focus:bg-accent web:focus:text-accent-foreground web:focus:outline-none web:disabled:pointer-events-none disabled:opacity-50 web:data-[active]:bg-accent/50 web:data-[state=open]:bg-accent/50',
59
+ );
60
+
61
+ function NavigationMenuTrigger({
62
+ className,
63
+ children,
64
+ ...props
65
+ }: Omit<NavigationMenuPrimitive.TriggerProps, 'children'> & {
66
+ children?: React.ReactNode;
67
+ ref?: React.RefObject<NavigationMenuPrimitive.TriggerRef>;
68
+ }) {
69
+ const { value } = NavigationMenuPrimitive.useRootContext();
70
+ const { value: itemValue } = NavigationMenuPrimitive.useItemContext();
71
+
72
+ const progress = useDerivedValue(() =>
73
+ value === itemValue
74
+ ? withTiming(1, { duration: 250 })
75
+ : withTiming(0, { duration: 200 }),
76
+ );
77
+ const chevronStyle = useAnimatedStyle(() => ({
78
+ transform: [{ rotate: `${progress.value * 180}deg` }],
79
+ opacity: interpolate(progress.value, [0, 1], [1, 0.8], Extrapolation.CLAMP),
80
+ }));
81
+
82
+ return (
83
+ <NavigationMenuPrimitive.Trigger
84
+ className={cn(
85
+ navigationMenuTriggerStyle(),
86
+ 'web:group gap-1.5',
87
+ value === itemValue && 'bg-accent',
88
+ className,
89
+ )}
90
+ {...props}
91
+ >
92
+ {children}
93
+ <Animated.View style={chevronStyle}>
94
+ <ChevronDown
95
+ size={12}
96
+ className={cn(
97
+ 'relative text-foreground h-3 w-3 web:transition web:duration-200',
98
+ )}
99
+ aria-hidden={true}
100
+ />
101
+ </Animated.View>
102
+ </NavigationMenuPrimitive.Trigger>
103
+ );
104
+ }
105
+
106
+ function NavigationMenuContent({
107
+ className,
108
+ children,
109
+ portalHost,
110
+ ...props
111
+ }: NavigationMenuPrimitive.ContentProps & {
112
+ portalHost?: string;
113
+ ref?: React.RefObject<NavigationMenuPrimitive.ContentRef>;
114
+ }) {
115
+ const { value } = NavigationMenuPrimitive.useRootContext();
116
+ const { value: itemValue } = NavigationMenuPrimitive.useItemContext();
117
+ return (
118
+ <NavigationMenuPrimitive.Portal hostName={portalHost}>
119
+ <NavigationMenuPrimitive.Content
120
+ className={cn(
121
+ 'w-full native:border native:border-border native:rounded-lg native:shadow-lg native:bg-popover native:text-popover-foreground native:overflow-hidden',
122
+ value === itemValue
123
+ ? 'web:animate-in web:fade-in web:slide-in-from-right-20'
124
+ : 'web:animate-out web:fade-out web:slide-out-to-left-20',
125
+ className,
126
+ )}
127
+ {...props}
128
+ >
129
+ <Animated.View
130
+ entering={Platform.OS !== 'web' ? FadeInLeft : undefined}
131
+ exiting={Platform.OS !== 'web' ? FadeOutLeft : undefined}
132
+ >
133
+ {children}
134
+ </Animated.View>
135
+ </NavigationMenuPrimitive.Content>
136
+ </NavigationMenuPrimitive.Portal>
137
+ );
138
+ }
139
+
140
+ const NavigationMenuLink = NavigationMenuPrimitive.Link;
141
+
142
+ function NavigationMenuViewport({
143
+ className,
144
+ ...props
145
+ }: NavigationMenuPrimitive.ViewportProps & {
146
+ ref?: React.RefObject<NavigationMenuPrimitive.ViewportRef>;
147
+ }) {
148
+ return (
149
+ <View className={cn('fixed right-0 top-12 mr-2 ml-2 flex justify-center')}>
150
+ <View
151
+ className={cn(
152
+ 'web:origin-top-center relative mt-1.5 web:h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border border-border bg-popover text-popover-foreground shadow-lg web:animate-in web:zoom-in-90',
153
+ className,
154
+ )}
155
+ {...props}
156
+ >
157
+ <NavigationMenuPrimitive.Viewport />
158
+ </View>
159
+ </View>
160
+ );
161
+ }
162
+
163
+ function NavigationMenuIndicator({
164
+ ref,
165
+ className,
166
+ ...props
167
+ }: NavigationMenuPrimitive.IndicatorProps & {
168
+ ref?: React.RefObject<NavigationMenuPrimitive.IndicatorRef>;
169
+ }) {
170
+ const { value } = NavigationMenuPrimitive.useRootContext();
171
+ const { value: itemValue } = NavigationMenuPrimitive.useItemContext();
172
+
173
+ return (
174
+ <NavigationMenuPrimitive.Indicator
175
+ ref={ref}
176
+ className={cn(
177
+ 'top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden',
178
+ value === itemValue
179
+ ? 'web:animate-in web:fade-in'
180
+ : 'web:animate-out web:fade-out',
181
+ className,
182
+ )}
183
+ {...props}
184
+ >
185
+ <View className="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md shadow-foreground/5" />
186
+ </NavigationMenuPrimitive.Indicator>
187
+ );
188
+ }
189
+
190
+ export {
191
+ NavigationMenu,
192
+ NavigationMenuContent,
193
+ NavigationMenuIndicator,
194
+ NavigationMenuItem,
195
+ NavigationMenuLink,
196
+ NavigationMenuList,
197
+ NavigationMenuTrigger,
198
+ navigationMenuTriggerStyle,
199
+ NavigationMenuViewport,
200
+ };