87 | >(({ className, ...props }, ref) => (
88 | | [role=checkbox]]:translate-y-[2px]",
92 | className
93 | )}
94 | {...props}
95 | />
96 | ))
97 | TableCell.displayName = "TableCell"
98 |
99 | const TableCaption = React.forwardRef<
100 | HTMLTableCaptionElement,
101 | React.HTMLAttributes
102 | >(({ className, ...props }, ref) => (
103 |
108 | ))
109 | TableCaption.displayName = "TableCaption"
110 |
111 | export {
112 | Table,
113 | TableHeader,
114 | TableBody,
115 | TableFooter,
116 | TableHead,
117 | TableRow,
118 | TableCell,
119 | TableCaption,
120 | }
121 |
--------------------------------------------------------------------------------
/components/mdx/story.mdx:
--------------------------------------------------------------------------------
1 | # DOM Markdown
2 |
3 | 
4 |
5 | In the year 2473, humanity had long since spread beyond Earth, colonizing planets and moons in distant star systems. Amidst this interstellar expansion, the **ISV Nomad**, a lone exploratory vessel, drifted through the vast void of space, its mission: to investigate the unknown regions at the edge of the Milky Way.
6 |
7 | ## Shaders
8 |
9 | Shaders make life better.
10 |
11 | import Shader from "@/components/mdx/shaders";
12 |
13 |
14 |
15 |
16 | ---
17 |
18 | ## Bring to the table
19 |
20 | Expo Router brings exciting new features to the table, including:
21 |
22 | | Feature | SDK Release |
23 | | :----------------- | ----------: |
24 | | File-based routing | 48.0.0 |
25 | | CSS Support | 49.0.0 |
26 | | Typed Routes | 49.0.0 |
27 | | API Routes | 50.0.0 |
28 | | Fast Resolver | 51.0.0 |
29 | | DOM Components | 52.0.0 |
30 |
31 | Soon, we'll have React Server Components, Server Actions, and more!
32 |
33 | ## Syntax Highlighting
34 |
35 | Expo Router enables a best-in-class developer experience by carefully automating and innovating in every inch of the stack.
36 |
37 | ```typescript
38 | // Render to DOM
39 | "use dom";
40 |
41 | // Import MDX as React
42 | import Story from "@/components/mdx/story.mdx";
43 |
44 | // Export a linkable route
45 | export default function MDX() {
46 | return ;
47 | }
48 | ```
49 |
50 | - Seamlessly create native views with markdown.
51 | - Switch between DOM and native with a single line.
52 | - Create files to define fully linkable navigation.
53 | - And share across all platforms!
54 |
55 | ## Flow Chart
56 |
57 | Flow charts with `@xyflow/react` (React Flow).
58 |
59 | import Flow from "@/components/flow/flow";
60 |
61 |
62 |
63 | ---
64 |
65 | ## Emoji Picker
66 |
67 | Custom emoji picker with `@emoji-mart/react`.
68 |
69 | import data from "@emoji-mart/data";
70 | import Picker from "@emoji-mart/react";
71 |
72 |
73 |
74 | ```typescript
75 | import data from "@emoji-mart/data";
76 | import Picker from "@emoji-mart/react";
77 |
78 | export default () => ;
79 | ```
80 |
81 | ## 3D Scene
82 |
83 | WebGL and 3D with THREE.js.
84 |
85 | import Three from "@/components/mdx/ambient-bulb";
86 |
87 |
92 |
93 | ---
94 |
95 | ## Rich Text
96 |
97 | Editing complex text with `@tiptap/react`.
98 |
99 | import TipTapWrap from "@/components/mdx/tiptap-wrap";
100 |
101 | Wow, this editor has support for links to the whole world wide web. We tested a lot of URLs and I think you can add *every URL* you want. Isn’t that cool? Let’s try another one! Yep, seems to work.
104 | By default every link will get a rel="noopener noreferrer nofollow" attribute. It’s configurable though.
105 | This is bold.
106 | This is underlined though.
107 | This is italic.
108 | But that’s striked through.
109 | This is code.
110 | `}
111 | />
112 |
113 | ---
114 |
--------------------------------------------------------------------------------
/app/(index,orders,products,analytics)/_layout.tsx:
--------------------------------------------------------------------------------
1 | import { Link, Slot, Stack } from "expo-router";
2 | import { Image } from "react-native";
3 | import { TouchableImpact } from "@/components/touchable-impact";
4 |
5 | import * as AC from "@bacons/apple-colors";
6 | import { ShadLayoutFull } from "@/components/shad/shad-layout";
7 |
8 | export default function RootLayout({ segment }: { segment: string }) {
9 | if (process.env.EXPO_OS === "web") {
10 | return (
11 |
12 |
13 |
14 | );
15 | }
16 |
17 | // TODO: Add header bar
18 | // return ;
19 |
20 | const name = getRouteName(segment);
21 | return (
22 |
40 | ;
48 | },
49 |
50 | //
51 | ...(name !== "index"
52 | ? {
53 | headerLargeTitle: undefined,
54 | headerSearchBarOptions: undefined,
55 | }
56 | : {}),
57 | }}
58 | />
59 |
73 |
74 | );
75 | }
76 |
77 | function ProfileButton({ segment }: { segment: string }) {
78 | return (
79 |
80 |
85 |
94 |
95 |
96 | );
97 | }
98 |
99 | export const unstable_settings = {
100 | anchor: "index",
101 | orders: {
102 | anchor: "orders",
103 | },
104 | products: {
105 | anchor: "products",
106 | },
107 | analytics: {
108 | anchor: "analytics",
109 | },
110 | };
111 |
112 | const titles = {
113 | index: "Dashboard",
114 | orders: "Orders",
115 | products: "Products",
116 | analytics: "Analytics",
117 | };
118 |
119 | function getRouteName(segment: string) {
120 | return segment.replace(/\((.+)\)/, "$1") as keyof typeof titles;
121 | }
122 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mdex",
3 | "main": "./index",
4 | "version": "1.0.0",
5 | "scripts": {
6 | "start": "EXPO_UNSTABLE_ATLAS=1 expo start -d",
7 | "android": "expo run:android",
8 | "ios": "expo run:ios",
9 | "lint": "expo lint",
10 | "predeploy:web": "expo export -p web",
11 | "deploy:web": "eas deploy",
12 | "prepare": "npx patch-package"
13 | },
14 | "dependencies": {
15 | "@bacons/apple-colors": "^0.0.8",
16 | "@bacons/mdx": "^0.3.8",
17 | "@emoji-mart/data": "^1.2.1",
18 | "@emoji-mart/react": "^1.1.1",
19 | "@expo/dom-webview": "^0.0.1-canary-20240912-1059f85",
20 | "@expo/vector-icons": "^14.1.0",
21 | "@radix-ui/react-avatar": "^1.1.0",
22 | "@radix-ui/react-dialog": "^1.1.1",
23 | "@radix-ui/react-dropdown-menu": "^2.1.1",
24 | "@radix-ui/react-icons": "^1.3.0",
25 | "@radix-ui/react-label": "^2.1.0",
26 | "@radix-ui/react-progress": "^1.1.0",
27 | "@radix-ui/react-select": "^2.1.1",
28 | "@radix-ui/react-separator": "^1.1.0",
29 | "@radix-ui/react-slot": "^1.1.0",
30 | "@radix-ui/react-tabs": "^1.1.0",
31 | "@radix-ui/react-toggle": "^1.1.0",
32 | "@radix-ui/react-toggle-group": "^1.1.0",
33 | "@radix-ui/react-tooltip": "^1.1.2",
34 | "@react-three/drei": "9.105.6",
35 | "@react-three/fiber": "8.16.6",
36 | "@tiptap/core": "^2.6.4",
37 | "@tiptap/extension-bold": "^2.6.6",
38 | "@tiptap/extension-code": "^2.6.6",
39 | "@tiptap/extension-document": "^2.6.6",
40 | "@tiptap/extension-history": "^2.6.6",
41 | "@tiptap/extension-italic": "^2.6.6",
42 | "@tiptap/extension-link": "^2.6.6",
43 | "@tiptap/extension-paragraph": "^2.6.6",
44 | "@tiptap/extension-strike": "^2.6.6",
45 | "@tiptap/extension-text": "^2.6.6",
46 | "@tiptap/extension-underline": "^2.6.6",
47 | "@tiptap/react": "^2.6.4",
48 | "@tiptap/starter-kit": "^2.6.4",
49 | "@types/three": "0.164.0",
50 | "@xyflow/react": "^12.1.1",
51 | "babel-plugin-react-compiler": "^19.1.0-rc.1",
52 | "class-variance-authority": "^0.7.0",
53 | "clsx": "^2.1.1",
54 | "emoji-mart": "^5.6.0",
55 | "eruda": "^3.2.3",
56 | "expo": "^53.0.8",
57 | "expo-application": "~6.1.4",
58 | "expo-asset": "~11.1.5",
59 | "expo-blur": "~14.1.4",
60 | "expo-constants": "~17.1.6",
61 | "expo-file-system": "~18.1.9",
62 | "expo-font": "~13.3.1",
63 | "expo-haptics": "~14.1.4",
64 | "expo-keep-awake": "~14.1.4",
65 | "expo-linking": "~7.1.4",
66 | "expo-modules-core": "~2.3.12",
67 | "expo-notifications": "~0.31.1",
68 | "expo-router": "~5.0.6",
69 | "expo-splash-screen": "~0.30.8",
70 | "expo-status-bar": "~2.2.3",
71 | "expo-system-ui": "~5.0.7",
72 | "expo-web-browser": "~14.1.6",
73 | "glslCanvas": "^0.2.6",
74 | "lucide-react": "^0.408.0",
75 | "lucide-react-native": "^0.436.0",
76 | "postcss": "~8.4.32",
77 | "prism-react-renderer": "^2.3.1",
78 | "react": "19.0.0",
79 | "react-advanced-cropper": "^0.20.0",
80 | "react-dom": "19.0.0",
81 | "react-icons": "^5.3.0",
82 | "react-mobile-cropper": "^0.10.0",
83 | "react-native": "0.79.2",
84 | "react-native-gesture-handler": "~2.24.0",
85 | "react-native-maps": "1.20.1",
86 | "react-native-reanimated": "~3.17.4",
87 | "react-native-safe-area-context": "5.4.0",
88 | "react-native-screens": "~4.10.0",
89 | "react-native-svg": "15.11.2",
90 | "react-native-web": "^0.20.0",
91 | "react-native-webview": "13.13.5",
92 | "recharts": "alpha",
93 | "sass": "^1.77.8",
94 | "tailwind-merge": "^2.4.0",
95 | "tailwindcss": "^3.4.10",
96 | "tailwindcss-animate": "^1.0.7",
97 | "three": "0.164.1"
98 | },
99 | "devDependencies": {
100 | "@babel/core": "^7.26.0",
101 | "@types/bun": "^1.1.8",
102 | "@types/react": "~19.0.10",
103 | "autoprefixer": "^10.4.16",
104 | "expo-atlas": "0.3.21",
105 | "typescript": "~5.8.3"
106 | },
107 | "private": true
108 | }
109 |
--------------------------------------------------------------------------------
/components/flow/flow.css:
--------------------------------------------------------------------------------
1 | .react-flow {
2 | --bg-color: #f9f9f9;
3 | --text-color: rgb(17, 17, 17);
4 | --node-border-radius: 10px;
5 | --node-box-shadow: 10px 0 15px rgba(7, 47, 53, 0.127),
6 | -10px 0 15px rgba(32, 34, 33, 0.034);
7 | background-color: var(--bg-color);
8 | color: var(--text-color);
9 | }
10 |
11 | .react-flow__node-custom {
12 | border-radius: var(--node-border-radius);
13 | display: flex;
14 | height: 70px;
15 | min-width: 150px;
16 | font-family: "Fira Mono", Monospace;
17 | font-weight: 500;
18 | letter-spacing: -0.2px;
19 | box-shadow: var(--node-box-shadow);
20 | }
21 |
22 | .react-flow__node-custom .wrapper {
23 | overflow: hidden;
24 | display: flex;
25 | padding: 2px;
26 | position: relative;
27 | border-radius: var(--node-border-radius);
28 | flex-grow: 1;
29 | }
30 |
31 | .gradient:before {
32 | content: "";
33 | position: absolute;
34 | padding-bottom: calc(100% * 1.41421356237);
35 | width: calc(100% * 1.41421356237);
36 | background: conic-gradient(
37 | from -160deg at 50% 50%,
38 | #9394a0 0deg,
39 | #222431 120deg,
40 | #9394a0 240deg,
41 | #000000 360deg
42 | );
43 | left: 50%;
44 | top: 50%;
45 | transform: translate(-50%, -50%);
46 | border-radius: 100%;
47 | }
48 |
49 | .react-flow__node-custom.selected .wrapper.gradient:before {
50 | content: "";
51 | background: conic-gradient(
52 | from -160deg at 50% 50%,
53 | #000000 0deg,
54 | #222431 120deg,
55 | #9394a0 240deg,
56 | rgba(42, 138, 246, 0) 360deg
57 | );
58 | animation: spinner 4s linear infinite;
59 | transform: translate(-50%, -50%) rotate(0deg);
60 | z-index: -1;
61 | }
62 |
63 | @keyframes spinner {
64 | 100% {
65 | transform: translate(-50%, -50%) rotate(-360deg);
66 | }
67 | }
68 |
69 | .react-flow__node-custom .inner {
70 | background: var(--bg-color);
71 | padding: 16px 20px;
72 | border-radius: var(--node-border-radius);
73 | display: flex;
74 | flex-direction: column;
75 | justify-content: center;
76 | flex-grow: 1;
77 | position: relative;
78 | }
79 |
80 | .react-flow__node-custom .icon {
81 | margin-right: 8px;
82 | }
83 |
84 | .react-flow__node-custom .body {
85 | display: flex;
86 | }
87 |
88 | .react-flow__node-custom .title {
89 | font-size: 16px;
90 | margin-bottom: 2px;
91 | line-height: 1;
92 | }
93 |
94 | .react-flow__node-custom .subline {
95 | font-size: 12px;
96 | color: #777;
97 | }
98 |
99 | .react-flow__node-custom .cloud {
100 | border-radius: 100%;
101 | width: 30px;
102 | height: 30px;
103 | right: 0;
104 | position: absolute;
105 | top: 0;
106 | transform: translate(50%, -50%);
107 | display: flex;
108 | transform-origin: center center;
109 | padding: 2px;
110 | overflow: hidden;
111 | box-shadow: var(--node-box-shadow);
112 | z-index: 1;
113 | }
114 |
115 | .react-flow__node-custom .cloud div {
116 | background-color: var(--bg-color);
117 | flex-grow: 1;
118 | border-radius: 100%;
119 | display: flex;
120 | justify-content: center;
121 | align-items: center;
122 | position: relative;
123 | }
124 |
125 | .react-flow__handle {
126 | opacity: 0;
127 | }
128 |
129 | .react-flow__handle.source {
130 | right: -10px;
131 | }
132 |
133 | .react-flow__handle.target {
134 | left: -10px;
135 | }
136 |
137 | .react-flow__node:focus {
138 | outline: none;
139 | }
140 |
141 | .react-flow__edge .react-flow__edge-path {
142 | /* stroke: url(#edge-gradient); */
143 | stroke-width: 2;
144 | stroke-opacity: 0.75;
145 | }
146 |
147 | .react-flow__controls button {
148 | background-color: var(--bg-color);
149 | color: var(--text-color);
150 | border: 1px solid #5939d7;
151 | border-bottom: none;
152 | }
153 |
154 | .react-flow__controls button:hover {
155 | background-color: rgb(37, 37, 37);
156 | }
157 |
158 | .react-flow__controls button:first-child {
159 | border-radius: 5px 5px 0 0;
160 | }
161 |
162 | .react-flow__controls button:last-child {
163 | border-bottom: 1px solid #5939d7;
164 | border-radius: 0 0 5px 5px;
165 | }
166 |
167 | .react-flow__controls button path {
168 | fill: var(--text-color);
169 | }
170 |
171 | .react-flow__attribution {
172 | background: rgba(255, 255, 255, 0.2);
173 | }
174 |
175 | .react-flow__attribution a {
176 | color: #5939d7;
177 | }
178 |
--------------------------------------------------------------------------------
/components/mdx/ambient-bulb.tsx:
--------------------------------------------------------------------------------
1 | "use dom";
2 |
3 | import { useEffect, useRef } from "react";
4 | import * as THREE from "three";
5 |
6 | export default function ThreeThing({
7 | style,
8 | }: {
9 | style?: React.CSSProperties;
10 | dom?: import("expo/dom").DOMProps;
11 | }) {
12 | const ref = useRef(null);
13 |
14 | useEffect(() => {
15 | if (!ref.current) return;
16 |
17 | const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
18 | renderer.setPixelRatio(
19 | window.devicePixelRatio ? window.devicePixelRatio : 1
20 | );
21 | renderer.setSize(ref.current?.clientWidth, ref.current?.clientHeight);
22 | renderer.autoClear = false;
23 | renderer.setClearColor(0x000000, 0.0);
24 | ref.current!.appendChild(renderer.domElement);
25 |
26 | const scene = new THREE.Scene();
27 |
28 | const camera = new THREE.PerspectiveCamera(
29 | 75,
30 | ref.current.clientWidth / ref.current.clientHeight,
31 | 1,
32 | 1000
33 | );
34 | camera.position.z = 400;
35 | scene.add(camera);
36 |
37 | const circle = new THREE.Object3D();
38 | const skelet = new THREE.Object3D();
39 | const particle = new THREE.Object3D();
40 |
41 | scene.add(circle);
42 | scene.add(skelet);
43 | scene.add(particle);
44 |
45 | const geometry = new THREE.TetrahedronGeometry(2, 0);
46 | const geom = new THREE.IcosahedronGeometry(7, 1);
47 | const geom2 = new THREE.IcosahedronGeometry(15, 1);
48 |
49 | const material = new THREE.MeshPhongMaterial({
50 | color: 0xffffff,
51 | flatShading: true,
52 | });
53 |
54 | for (let i = 0; i < 1000; i++) {
55 | const mesh = new THREE.Mesh(geometry, material);
56 | mesh.position
57 | .set(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5)
58 | .normalize();
59 | mesh.position.multiplyScalar(90 + Math.random() * 700);
60 | mesh.rotation.set(
61 | Math.random() * 2,
62 | Math.random() * 2,
63 | Math.random() * 2
64 | );
65 | particle.add(mesh);
66 | }
67 |
68 | const mat = new THREE.MeshPhongMaterial({
69 | color: 0xffffff,
70 | flatShading: true,
71 | });
72 |
73 | const mat2 = new THREE.MeshPhongMaterial({
74 | color: 0xffffff,
75 | wireframe: true,
76 | side: THREE.DoubleSide,
77 | });
78 |
79 | const planet = new THREE.Mesh(geom, mat);
80 | planet.scale.x = planet.scale.y = planet.scale.z = 16;
81 | circle.add(planet);
82 |
83 | const planet2 = new THREE.Mesh(geom2, mat2);
84 | planet2.scale.x = planet2.scale.y = planet2.scale.z = 10;
85 | skelet.add(planet2);
86 |
87 | const ambientLight = new THREE.AmbientLight(0x999999);
88 | scene.add(ambientLight);
89 |
90 | const lights = [];
91 | lights[0] = new THREE.DirectionalLight(0xffffff, 1);
92 | lights[0].position.set(1, 0, 0);
93 | lights[1] = new THREE.DirectionalLight(0x11e8bb, 1);
94 | lights[1].position.set(0.75, 1, 0.5);
95 | lights[2] = new THREE.DirectionalLight(0x8200c9, 1);
96 | lights[2].position.set(-0.75, -1, 0.5);
97 | scene.add(lights[0]);
98 | scene.add(lights[1]);
99 | scene.add(lights[2]);
100 |
101 | window.addEventListener("resize", onWindowResize, false);
102 |
103 | function onWindowResize() {
104 | if (!ref.current) return;
105 | camera.aspect = ref.current.clientWidth / ref.current.clientHeight;
106 | camera.updateProjectionMatrix();
107 |
108 | renderer.setSize(ref.current.clientWidth, ref.current.clientHeight);
109 | }
110 |
111 | function animate() {
112 | requestAnimationFrame(animate);
113 |
114 | particle.rotation.x += 0.0;
115 | particle.rotation.y -= 0.004;
116 | circle.rotation.x -= 0.002;
117 | circle.rotation.y -= 0.003;
118 | skelet.rotation.x -= 0.001;
119 | skelet.rotation.y += 0.002;
120 | renderer.clear();
121 |
122 | renderer.render(scene, camera);
123 | }
124 |
125 | animate();
126 | }, []);
127 |
128 | return (
129 |
142 | );
143 | }
144 |
--------------------------------------------------------------------------------
/components/ui/sheet.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import * as SheetPrimitive from "@radix-ui/react-dialog"
3 | import { Cross2Icon } from "@radix-ui/react-icons"
4 | import { cva, type VariantProps } from "class-variance-authority"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Sheet = SheetPrimitive.Root
9 |
10 | const SheetTrigger = SheetPrimitive.Trigger
11 |
12 | const SheetClose = SheetPrimitive.Close
13 |
14 | const SheetPortal = SheetPrimitive.Portal
15 |
16 | const SheetOverlay = React.forwardRef<
17 | React.ElementRef,
18 | React.ComponentPropsWithoutRef
19 | >(({ className, ...props }, ref) => (
20 |
28 | ))
29 | SheetOverlay.displayName = SheetPrimitive.Overlay.displayName
30 |
31 | const sheetVariants = cva(
32 | "fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500 data-[state=open]:animate-in data-[state=closed]:animate-out",
33 | {
34 | variants: {
35 | side: {
36 | top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
37 | bottom:
38 | "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
39 | left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
40 | right:
41 | "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
42 | },
43 | },
44 | defaultVariants: {
45 | side: "right",
46 | },
47 | }
48 | )
49 |
50 | interface SheetContentProps
51 | extends React.ComponentPropsWithoutRef,
52 | VariantProps {}
53 |
54 | const SheetContent = React.forwardRef<
55 | React.ElementRef,
56 | SheetContentProps
57 | >(({ side = "right", className, children, ...props }, ref) => (
58 |
59 |
60 |
65 |
66 |
67 | Close
68 |
69 | {children}
70 |
71 |
72 | ))
73 | SheetContent.displayName = SheetPrimitive.Content.displayName
74 |
75 | const SheetHeader = ({
76 | className,
77 | ...props
78 | }: React.HTMLAttributes) => (
79 |
86 | )
87 | SheetHeader.displayName = "SheetHeader"
88 |
89 | const SheetFooter = ({
90 | className,
91 | ...props
92 | }: React.HTMLAttributes) => (
93 |
100 | )
101 | SheetFooter.displayName = "SheetFooter"
102 |
103 | const SheetTitle = React.forwardRef<
104 | React.ElementRef,
105 | React.ComponentPropsWithoutRef
106 | >(({ className, ...props }, ref) => (
107 |
112 | ))
113 | SheetTitle.displayName = SheetPrimitive.Title.displayName
114 |
115 | const SheetDescription = React.forwardRef<
116 | React.ElementRef,
117 | React.ComponentPropsWithoutRef
118 | >(({ className, ...props }, ref) => (
119 |
124 | ))
125 | SheetDescription.displayName = SheetPrimitive.Description.displayName
126 |
127 | export {
128 | Sheet,
129 | SheetPortal,
130 | SheetOverlay,
131 | SheetTrigger,
132 | SheetClose,
133 | SheetContent,
134 | SheetHeader,
135 | SheetFooter,
136 | SheetTitle,
137 | SheetDescription,
138 | }
139 |
--------------------------------------------------------------------------------
/lib/tab-to-top.ts:
--------------------------------------------------------------------------------
1 | import {
2 | EventArg,
3 | NavigationProp,
4 | useNavigation,
5 | useRoute,
6 | } from "@react-navigation/core";
7 | import * as React from "react";
8 | import type { ScrollView } from "react-native";
9 | import { WebView } from "react-native-webview";
10 |
11 | type ScrollOptions = { x?: number; y?: number; animated?: boolean };
12 |
13 | type ScrollableView =
14 | | { scrollToTop(): void }
15 | | { scrollTo(options: ScrollOptions): void }
16 | | { scrollToOffset(options: { offset?: number; animated?: boolean }): void }
17 | | { scrollResponderScrollTo(options: ScrollOptions): void };
18 |
19 | type ScrollableWrapper =
20 | | { getScrollResponder(): React.ReactNode | ScrollView }
21 | | { getNode(): ScrollableView }
22 | | ScrollableView;
23 |
24 | function getScrollableNode(
25 | ref: React.RefObject | React.RefObject
26 | ) {
27 | if (ref.current == null) {
28 | return null;
29 | }
30 |
31 | if (
32 | "scrollToTop" in ref.current ||
33 | "scrollTo" in ref.current ||
34 | "scrollToOffset" in ref.current ||
35 | "scrollResponderScrollTo" in ref.current
36 | ) {
37 | // This is already a scrollable node.
38 | return ref.current;
39 | } else if ("getScrollResponder" in ref.current) {
40 | // If the view is a wrapper like FlatList, SectionList etc.
41 | // We need to use `getScrollResponder` to get access to the scroll responder
42 | return ref.current.getScrollResponder();
43 | } else if ("getNode" in ref.current) {
44 | // When a `ScrollView` is wraped in `Animated.createAnimatedComponent`
45 | // we need to use `getNode` to get the ref to the actual scrollview.
46 | // Note that `getNode` is deprecated in newer versions of react-native
47 | // this is why we check if we already have a scrollable node above.
48 | return ref.current.getNode();
49 | } else {
50 | return ref.current;
51 | }
52 | }
53 |
54 | export function useScrollToTop(
55 | ref: React.RefObject | React.RefObject,
56 | offset: number = 0
57 | ) {
58 | const navigation = useNavigation();
59 | const route = useRoute();
60 |
61 | React.useEffect(() => {
62 | let tabNavigations: NavigationProp[] = [];
63 | let currentNavigation = navigation;
64 |
65 | // If the screen is nested inside multiple tab navigators, we should scroll to top for any of them
66 | // So we need to find all the parent tab navigators and add the listeners there
67 | while (currentNavigation) {
68 | if (currentNavigation.getState()?.type === "tab") {
69 | tabNavigations.push(currentNavigation);
70 | }
71 |
72 | currentNavigation = currentNavigation.getParent();
73 | }
74 |
75 | if (tabNavigations.length === 0) {
76 | return;
77 | }
78 |
79 | const unsubscribers = tabNavigations.map((tab) => {
80 | return tab.addListener(
81 | // We don't wanna import tab types here to avoid extra deps
82 | // in addition, there are multiple tab implementations
83 | // @ts-expect-error
84 | "tabPress",
85 | (e: EventArg<"tabPress", true>) => {
86 | // We should scroll to top only when the screen is focused
87 | const isFocused = navigation.isFocused();
88 |
89 | // In a nested stack navigator, tab press resets the stack to first screen
90 | // So we should scroll to top only when we are on first screen
91 | const isFirst =
92 | tabNavigations.includes(navigation) ||
93 | navigation.getState().routes[0].key === route.key;
94 |
95 | // Run the operation in the next frame so we're sure all listeners have been run
96 | // This is necessary to know if preventDefault() has been called
97 | requestAnimationFrame(() => {
98 | const scrollable = getScrollableNode(ref) as
99 | | ScrollableWrapper
100 | | WebView;
101 |
102 | if (isFocused && isFirst && scrollable && !e.defaultPrevented) {
103 | if ("scrollToTop" in scrollable) {
104 | scrollable.scrollToTop();
105 | } else if ("scrollTo" in scrollable) {
106 | scrollable.scrollTo({ y: offset, animated: true });
107 | } else if ("scrollToOffset" in scrollable) {
108 | scrollable.scrollToOffset({ offset: offset, animated: true });
109 | } else if ("scrollResponderScrollTo" in scrollable) {
110 | scrollable.scrollResponderScrollTo({
111 | y: offset,
112 | animated: true,
113 | });
114 | } else if ("injectJavaScript" in scrollable) {
115 | scrollable.injectJavaScript(
116 | `;window.scrollTo({ top: ${offset}, behavior: 'smooth' }); true;`
117 | );
118 | }
119 | }
120 | });
121 | }
122 | );
123 | });
124 |
125 | return () => {
126 | unsubscribers.forEach((unsubscribe) => unsubscribe());
127 | };
128 | }, [navigation, ref, offset, route.key]);
129 | }
130 |
131 | export function useScrollRef() {
132 | const ref = React.useRef(null);
133 | if (process.env.EXPO_OS === "web") {
134 | return ref;
135 | }
136 |
137 | useScrollToTop(ref);
138 |
139 | return ref;
140 | }
141 |
--------------------------------------------------------------------------------
/components/ui/select.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import {
3 | CaretSortIcon,
4 | CheckIcon,
5 | ChevronDownIcon,
6 | ChevronUpIcon,
7 | } from "@radix-ui/react-icons"
8 | import * as SelectPrimitive from "@radix-ui/react-select"
9 |
10 | import { cn } from "@/lib/utils"
11 |
12 | const Select = SelectPrimitive.Root
13 |
14 | const SelectGroup = SelectPrimitive.Group
15 |
16 | const SelectValue = SelectPrimitive.Value
17 |
18 | const SelectTrigger = React.forwardRef<
19 | React.ElementRef,
20 | React.ComponentPropsWithoutRef
21 | >(({ className, children, ...props }, ref) => (
22 | span]:line-clamp-1",
26 | className
27 | )}
28 | {...props}
29 | >
30 | {children}
31 |
32 |
33 |
34 |
35 | ))
36 | SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
37 |
38 | const SelectScrollUpButton = React.forwardRef<
39 | React.ElementRef,
40 | React.ComponentPropsWithoutRef
41 | >(({ className, ...props }, ref) => (
42 |
50 |
51 |
52 | ))
53 | SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
54 |
55 | const SelectScrollDownButton = React.forwardRef<
56 | React.ElementRef,
57 | React.ComponentPropsWithoutRef
58 | >(({ className, ...props }, ref) => (
59 |
67 |
68 |
69 | ))
70 | SelectScrollDownButton.displayName =
71 | SelectPrimitive.ScrollDownButton.displayName
72 |
73 | const SelectContent = React.forwardRef<
74 | React.ElementRef,
75 | React.ComponentPropsWithoutRef
76 | >(({ className, children, position = "popper", ...props }, ref) => (
77 |
78 |
89 |
90 |
97 | {children}
98 |
99 |
100 |
101 |
102 | ))
103 | SelectContent.displayName = SelectPrimitive.Content.displayName
104 |
105 | const SelectLabel = React.forwardRef<
106 | React.ElementRef,
107 | React.ComponentPropsWithoutRef
108 | >(({ className, ...props }, ref) => (
109 |
114 | ))
115 | SelectLabel.displayName = SelectPrimitive.Label.displayName
116 |
117 | const SelectItem = React.forwardRef<
118 | React.ElementRef,
119 | React.ComponentPropsWithoutRef
120 | >(({ className, children, ...props }, ref) => (
121 |
129 |
130 |
131 |
132 |
133 |
134 | {children}
135 |
136 | ))
137 | SelectItem.displayName = SelectPrimitive.Item.displayName
138 |
139 | const SelectSeparator = React.forwardRef<
140 | React.ElementRef,
141 | React.ComponentPropsWithoutRef
142 | >(({ className, ...props }, ref) => (
143 |
148 | ))
149 | SelectSeparator.displayName = SelectPrimitive.Separator.displayName
150 |
151 | export {
152 | Select,
153 | SelectGroup,
154 | SelectValue,
155 | SelectTrigger,
156 | SelectContent,
157 | SelectLabel,
158 | SelectItem,
159 | SelectSeparator,
160 | SelectScrollUpButton,
161 | SelectScrollDownButton,
162 | }
163 |
--------------------------------------------------------------------------------
/components/mdx/shaders.tsx:
--------------------------------------------------------------------------------
1 | "use dom";
2 |
3 | import React from "react";
4 | import GlslCanvas from "glslCanvas";
5 |
6 | interface ShaderCanvasProps {
7 | fragmentShader: string;
8 | }
9 |
10 | function ShaderCanvas({ fragmentShader }: ShaderCanvasProps) {
11 | const canvasRef = React.useRef(null);
12 |
13 | React.useEffect(() => {
14 | const canvas = canvasRef.current;
15 | const glsl = new GlslCanvas(canvas);
16 | const pixelRatio = window.devicePixelRatio || 1;
17 |
18 | try {
19 | glsl.load(fragmentShader);
20 | glsl.setUniform("u_resolution", [canvas.width, canvas.height]);
21 | } catch (e) {
22 | console.error("Shader compilation error:", e);
23 | }
24 |
25 | const resizeCanvas = () => {
26 | // const width = window.innerWidth;
27 | // const height = window.innerHeight;
28 |
29 | // canvas.width = width * pixelRatio;
30 | // canvas.height = height * pixelRatio;
31 | // canvas.style.width = `${width}px`;
32 | // canvas.style.height = `${height}px`;
33 |
34 | glsl.setUniform("u_resolution", [
35 | canvas.width * pixelRatio,
36 | canvas.height * pixelRatio,
37 | ]);
38 | };
39 |
40 | window.addEventListener("resize", resizeCanvas);
41 | resizeCanvas();
42 |
43 | return () => {
44 | window.removeEventListener("resize", resizeCanvas);
45 | glsl.destroy();
46 | };
47 | }, [fragmentShader]);
48 |
49 | return (
50 |
58 | );
59 | }
60 |
61 | import "@bacons/apple-colors";
62 |
63 | // Example usage with ContourShader
64 | const dither = `
65 | #extension GL_OES_standard_derivatives : enable
66 | precision mediump float;
67 | uniform vec2 u_resolution;
68 | uniform float u_time;
69 |
70 | const vec4 COLOR_DARK = vec4(174.0/255.0, 174.0/255.0, 178.0/255.0, 1.0);
71 | // // const vec4 COLOR_DARK = vec4(28.05/255.0, 28.05/255.0, 30.6/255.0, 1.0);
72 | const vec4 COLOR_LIGHT = vec4(242.05/255.0, 242.05/255.0, 247.6/255.0, 1.0);
73 |
74 | const float SPEED = 0.1;
75 | const float PIXEL_SIZE = 1.0;
76 | const float DITHER_SCALE = 1.0;
77 |
78 | const float ROT_SPEED = 0.1;
79 | const float WARP_INIT = 1.0;
80 | const float WARP_ITER = 2.112312321;
81 | const float WARP_AMPL = 3.1123123123;
82 |
83 | float getBayerValue(int index) {
84 | if (index == 0) return 0.0/16.0;
85 | if (index == 1) return 8.0/16.0;
86 | if (index == 2) return 2.0/16.0;
87 | if (index == 3) return 10.0/16.0;
88 | if (index == 4) return 12.0/16.0;
89 | if (index == 5) return 4.0/16.0;
90 | if (index == 6) return 14.0/16.0;
91 | if (index == 7) return 6.0/16.0;
92 | if (index == 8) return 3.0/16.0;
93 | if (index == 9) return 11.0/16.0;
94 | if (index == 10) return 1.0/16.0;
95 | if (index == 11) return 9.0/16.0;
96 | if (index == 12) return 15.0/16.0;
97 | if (index == 13) return 7.0/16.0;
98 | if (index == 14) return 13.0/16.0;
99 | if (index == 15) return 5.0/16.0;
100 | return 0.0;
101 | }
102 |
103 | vec2 transformUV(vec2 uv) {
104 | float angle = u_time * SPEED * ROT_SPEED;
105 | uv = mat2(cos(angle), -sin(angle), sin(angle), cos(angle)) * uv;
106 |
107 | float t = u_time * SPEED;
108 | for (float i = WARP_INIT; i < WARP_ITER; i++) {
109 | float iInv = 1.0 / i;
110 |
111 | vec2 oscillation = vec2(
112 | sin(i * 0.5 + t * 0.25) * cos(t + uv.y * 1.5),
113 | cos(i * 0.7 + t * 0.35) * sin(t + uv.x * 1.2)
114 | );
115 |
116 | vec2 modulation = vec2(
117 | 0.2 * sin(t * 0.6 + uv.y * 3.0),
118 | 0.3 * cos(t * 0.8 + uv.x * 2.5)
119 | );
120 |
121 | uv += WARP_AMPL * iInv * (oscillation + modulation);
122 | }
123 |
124 | return uv;
125 | }
126 |
127 | float getDither(vec2 pos) {
128 | vec2 scaled = pos / PIXEL_SIZE * DITHER_SCALE;
129 | int index = int(mod(scaled.x, 4.0)) + int(mod(scaled.y, 4.0)) * 4;
130 | return getBayerValue(index);
131 | }
132 |
133 | void main() {
134 | vec2 pixelatedCoord =
135 | floor(gl_FragCoord.xy / PIXEL_SIZE) * PIXEL_SIZE + PIXEL_SIZE / 2.0;
136 | vec2 uv = (2.0 * pixelatedCoord - u_resolution.xy) / min(u_resolution.x, u_resolution.y);
137 |
138 | uv = transformUV(uv);
139 |
140 | float intensity = 0.5 + 0.5 * sin(u_time * SPEED - uv.x - uv.y);
141 | float threshold = getDither(gl_FragCoord.xy);
142 |
143 | gl_FragColor = mix(COLOR_DARK, COLOR_LIGHT, step(threshold, intensity));
144 | }
145 | `;
146 |
147 | const contour = `
148 | #extension GL_OES_standard_derivatives : enable
149 | precision mediump float;
150 | uniform vec2 u_resolution;
151 | uniform float u_time;
152 |
153 | // Control variables
154 | const float WAVE_SPEED = 150.0; // Higher = slower
155 | const float WAVE_AMPLITUDE = 2.0; // Wave height multiplier
156 | const vec3 COLOR_PRIMARY = vec3(0.8, 0.8, 0.8); // Primary contour color
157 | const vec3 COLOR_SECONDARY = vec3(0.95, 0.95, 0.95); // Secondary contour color
158 | const float LINE_WIDTH = 0.5; // Width of contour lines
159 | const float LINE_SPACING = 4000.0; // Space between contour lines
160 |
161 | float wave(float x, float y, float k)
162 | {
163 | return sin(10.0*x+10.0*y) / 5.0 +
164 | sin(20.0*x*k+15.0*y*k) / 3.0 +
165 | sin(4.0*x+10.0*y) / -4.0 +
166 | sin(y) / 2.0 +
167 | sin(x*x*y*20.0*k) +
168 | sin(x * 20.0*k + 4.0) / 5.0 +
169 | sin(y * 30.0*k) / 5.0 +
170 | sin(x) / 4.0;
171 | }
172 |
173 | void main() {
174 | vec2 uv = gl_FragCoord.xy / u_resolution.xy;
175 | uv.xy *= 2.;
176 | uv.x += cos(u_time/WAVE_SPEED)/4. - 0.5;
177 | uv.y += sin(u_time/WAVE_SPEED)*2. -0.5;
178 |
179 | float z = wave(uv.x, uv.y, 1./10.) + 2.0;
180 |
181 | z *= WAVE_AMPLITUDE * (sin(u_time/20.)+2.);
182 | float d = fract(z);
183 | if(mod(z, 2.0) > 1.) d = 1.-d;
184 |
185 | vec3 col = vec3(1.0);
186 | for(float i=0.; i<3.; i++){
187 | col -= vec3(step(d/fwidth(z*3.),
188 | LINE_WIDTH+(u_resolution.x+u_resolution.y)/LINE_SPACING - (i+1.)/3.)*((i+1.)/4.));
189 | }
190 |
191 | col *= mix(COLOR_PRIMARY, COLOR_SECONDARY, fwidth(z*4.)+0.3);
192 | gl_FragColor = vec4(col, 1.0);
193 | }
194 | `;
195 | function ContourShader(props: { dither: boolean }) {
196 | return (
197 |
198 |
199 |
200 | );
201 | }
202 |
203 | export default ContourShader;
204 |
--------------------------------------------------------------------------------
/components/mdx/tiptap-wrap.tsx:
--------------------------------------------------------------------------------
1 | "use dom";
2 |
3 | import React, { useCallback } from "react";
4 | import classNames from "classnames";
5 | // => Tiptap packages
6 | import { useEditor, EditorContent, Editor } from "@tiptap/react";
7 | import Document from "@tiptap/extension-document";
8 | import Paragraph from "@tiptap/extension-paragraph";
9 | import Text from "@tiptap/extension-text";
10 | import Link from "@tiptap/extension-link";
11 | import Bold from "@tiptap/extension-bold";
12 | import Underline from "@tiptap/extension-underline";
13 | import Italic from "@tiptap/extension-italic";
14 | import Strike from "@tiptap/extension-strike";
15 | import Code from "@tiptap/extension-code";
16 | import History from "@tiptap/extension-history";
17 |
18 | import "./tiptap.css";
19 | import styles from "./tiptap.module.css";
20 | import clsx from "clsx";
21 |
22 | export default function TipTapWrap({
23 | content,
24 | }: {
25 | content: string;
26 | dom?: import("expo/dom").DOMProps;
27 | }) {
28 | const editor = useEditor({
29 | extensions: [
30 | Document,
31 | History,
32 | Paragraph,
33 | Text,
34 | Link.configure({
35 | openOnClick: false,
36 | }),
37 | Bold,
38 | Underline,
39 | Italic,
40 | Strike,
41 | Code,
42 | ],
43 | content,
44 | }) as Editor;
45 |
46 | const toggleBold = useCallback(() => {
47 | editor.chain().focus().toggleBold().run();
48 | }, [editor]);
49 |
50 | const toggleUnderline = useCallback(() => {
51 | editor.chain().focus().toggleUnderline().run();
52 | }, [editor]);
53 |
54 | const toggleItalic = useCallback(() => {
55 | editor.chain().focus().toggleItalic().run();
56 | }, [editor]);
57 |
58 | const toggleStrike = useCallback(() => {
59 | editor.chain().focus().toggleStrike().run();
60 | }, [editor]);
61 |
62 | const toggleCode = useCallback(() => {
63 | editor.chain().focus().toggleCode().run();
64 | }, [editor]);
65 |
66 | if (!editor) {
67 | return null;
68 | }
69 |
70 | return (
71 |
72 |
73 |
80 |
87 |
88 |
96 |
104 |
112 |
120 |
128 |
129 |
130 |
131 |
132 | );
133 | }
134 |
135 | const IconBold = ({ size = 16, color = "currentColor" }) => (
136 |
150 | );
151 |
152 | const IconUnderline = ({ size = 16, color = "currentColor" }) => (
153 |
167 | );
168 |
169 | const IconItalic = ({ size = 16, color = "currentColor" }) => (
170 |
183 | );
184 |
185 | const IconCode = ({ size = 16, color = "currentColor" }) => (
186 |
200 | );
201 |
202 | const IconStrikethrough = ({ size = 16, color = "currentColor" }) => (
203 |
216 | );
217 |
218 | const IconRotateLeft = ({ size = 16, color = "currentColor" }) => (
219 |
232 | );
233 |
234 | const IconRotateRight = ({ size = 16, color = "currentColor" }) => (
235 |
248 | );
249 |
250 | const IconX = ({ size = 16, color = "currentColor" }) => (
251 |
265 | );
266 |
--------------------------------------------------------------------------------
/components/ui/dropdown-menu.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
3 | import {
4 | CheckIcon,
5 | ChevronRightIcon,
6 | DotFilledIcon,
7 | } from "@radix-ui/react-icons"
8 |
9 | import { cn } from "@/lib/utils"
10 |
11 | const DropdownMenu = DropdownMenuPrimitive.Root
12 |
13 | const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
14 |
15 | const DropdownMenuGroup = DropdownMenuPrimitive.Group
16 |
17 | const DropdownMenuPortal = DropdownMenuPrimitive.Portal
18 |
19 | const DropdownMenuSub = DropdownMenuPrimitive.Sub
20 |
21 | const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
22 |
23 | const DropdownMenuSubTrigger = React.forwardRef<
24 | React.ElementRef,
25 | React.ComponentPropsWithoutRef & {
26 | inset?: boolean
27 | }
28 | >(({ className, inset, children, ...props }, ref) => (
29 |
38 | {children}
39 |
40 |
41 | ))
42 | DropdownMenuSubTrigger.displayName =
43 | DropdownMenuPrimitive.SubTrigger.displayName
44 |
45 | const DropdownMenuSubContent = React.forwardRef<
46 | React.ElementRef,
47 | React.ComponentPropsWithoutRef
48 | >(({ className, ...props }, ref) => (
49 |
57 | ))
58 | DropdownMenuSubContent.displayName =
59 | DropdownMenuPrimitive.SubContent.displayName
60 |
61 | const DropdownMenuContent = React.forwardRef<
62 | React.ElementRef,
63 | React.ComponentPropsWithoutRef
64 | >(({ className, sideOffset = 4, ...props }, ref) => (
65 |
66 |
76 |
77 | ))
78 | DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName
79 |
80 | const DropdownMenuItem = React.forwardRef<
81 | React.ElementRef,
82 | React.ComponentPropsWithoutRef & {
83 | inset?: boolean
84 | }
85 | >(({ className, inset, ...props }, ref) => (
86 |
95 | ))
96 | DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName
97 |
98 | const DropdownMenuCheckboxItem = React.forwardRef<
99 | React.ElementRef,
100 | React.ComponentPropsWithoutRef
101 | >(({ className, children, checked, ...props }, ref) => (
102 |
111 |
112 |
113 |
114 |
115 |
116 | {children}
117 |
118 | ))
119 | DropdownMenuCheckboxItem.displayName =
120 | DropdownMenuPrimitive.CheckboxItem.displayName
121 |
122 | const DropdownMenuRadioItem = React.forwardRef<
123 | React.ElementRef,
124 | React.ComponentPropsWithoutRef
125 | >(({ className, children, ...props }, ref) => (
126 |
134 |
135 |
136 |
137 |
138 |
139 | {children}
140 |
141 | ))
142 | DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName
143 |
144 | const DropdownMenuLabel = React.forwardRef<
145 | React.ElementRef,
146 | React.ComponentPropsWithoutRef & {
147 | inset?: boolean
148 | }
149 | >(({ className, inset, ...props }, ref) => (
150 |
159 | ))
160 | DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName
161 |
162 | const DropdownMenuSeparator = React.forwardRef<
163 | React.ElementRef,
164 | React.ComponentPropsWithoutRef
165 | >(({ className, ...props }, ref) => (
166 |
171 | ))
172 | DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName
173 |
174 | const DropdownMenuShortcut = ({
175 | className,
176 | ...props
177 | }: React.HTMLAttributes) => {
178 | return (
179 |
183 | )
184 | }
185 | DropdownMenuShortcut.displayName = "DropdownMenuShortcut"
186 |
187 | export {
188 | DropdownMenu,
189 | DropdownMenuTrigger,
190 | DropdownMenuContent,
191 | DropdownMenuItem,
192 | DropdownMenuCheckboxItem,
193 | DropdownMenuRadioItem,
194 | DropdownMenuLabel,
195 | DropdownMenuSeparator,
196 | DropdownMenuShortcut,
197 | DropdownMenuGroup,
198 | DropdownMenuPortal,
199 | DropdownMenuSub,
200 | DropdownMenuSubContent,
201 | DropdownMenuSubTrigger,
202 | DropdownMenuRadioGroup,
203 | }
204 |
--------------------------------------------------------------------------------
/components/shad/shad-nav.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Breadcrumb,
3 | BreadcrumbItem,
4 | BreadcrumbLink,
5 | BreadcrumbList,
6 | BreadcrumbPage,
7 | BreadcrumbSeparator,
8 | } from "@/components/ui/breadcrumb";
9 | import { Button } from "@/components/ui/button";
10 | import {
11 | DropdownMenu,
12 | DropdownMenuContent,
13 | DropdownMenuItem,
14 | DropdownMenuLabel,
15 | DropdownMenuSeparator,
16 | DropdownMenuTrigger,
17 | } from "@/components/ui/dropdown-menu";
18 | import { Input } from "@/components/ui/input";
19 | import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet";
20 | import {
21 | Tooltip,
22 | TooltipContent,
23 | TooltipTrigger,
24 | } from "@/components/ui/tooltip";
25 | import clsx from "clsx";
26 | import {
27 | Home,
28 | LineChart as LineChartIcon,
29 | Package,
30 | Package2,
31 | PanelLeft,
32 | Search,
33 | Settings,
34 | ShoppingCart,
35 | } from "lucide-react";
36 | import React from "react";
37 |
38 | import { Link, useSegments } from "expo-router";
39 | import { IS_DOM } from "expo/dom";
40 |
41 | const baseUrl = IS_DOM ? process.env.EXPO_DOM_BASE_URL : "";
42 |
43 | export function SideBarTab({
44 | href,
45 | title,
46 | selected,
47 | icon,
48 | }: {
49 | href: import("expo-router").LinkProps["href"];
50 | title: string;
51 | selected?: boolean;
52 | icon: React.ReactNode;
53 | }) {
54 | return (
55 |
56 |
57 |
64 | {icon}
65 |
66 | {title}
67 |
68 |
69 | {title}
70 |
71 | );
72 | }
73 |
74 | export function SheetTab({
75 | href,
76 | title,
77 | selected,
78 | icon,
79 | }: {
80 | href: import("expo-router").LinkProps["href"];
81 | title: string;
82 | selected?: boolean;
83 | icon: React.ReactNode;
84 | }) {
85 | return (
86 |
93 | {icon}
94 | {title}
95 |
96 | );
97 | }
98 |
99 | export function SideNavigationBar() {
100 | const [, segment] = useSegments();
101 |
102 | return (
103 |
155 | );
156 | }
157 |
158 | function DrawerSheet() {
159 | const [, segment] = useSegments();
160 |
161 | return (
162 |
163 |
164 |
168 |
169 |
170 |
210 |
211 |
212 | );
213 | }
214 |
215 | export function Header() {
216 | return (
217 |
272 | );
273 | }
274 |
--------------------------------------------------------------------------------
/components/ui/chart.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import * as RechartsPrimitive from "recharts"
3 |
4 | import { cn } from "@/lib/utils"
5 |
6 | // Format: { THEME_NAME: CSS_SELECTOR }
7 | const THEMES = { light: "", dark: ".dark" } as const
8 |
9 | export type ChartConfig = {
10 | [k in string]: {
11 | label?: React.ReactNode
12 | icon?: React.ComponentType
13 | } & (
14 | | { color?: string; theme?: never }
15 | | { color?: never; theme: Record }
16 | )
17 | }
18 |
19 | type ChartContextProps = {
20 | config: ChartConfig
21 | }
22 |
23 | const ChartContext = React.createContext(null)
24 |
25 | function useChart() {
26 | const context = React.useContext(ChartContext)
27 |
28 | if (!context) {
29 | throw new Error("useChart must be used within a ")
30 | }
31 |
32 | return context
33 | }
34 |
35 | const ChartContainer = React.forwardRef<
36 | HTMLDivElement,
37 | React.ComponentProps<"div"> & {
38 | config: ChartConfig
39 | children: React.ComponentProps<
40 | typeof RechartsPrimitive.ResponsiveContainer
41 | >["children"]
42 | }
43 | >(({ id, className, children, config, ...props }, ref) => {
44 | const uniqueId = React.useId()
45 | const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`
46 |
47 | return (
48 |
49 |
58 |
59 |
60 | {children}
61 |
62 |
63 |
64 | )
65 | })
66 | ChartContainer.displayName = "Chart"
67 |
68 | const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
69 | const colorConfig = Object.entries(config).filter(
70 | ([_, config]) => config.theme || config.color
71 | )
72 |
73 | if (!colorConfig.length) {
74 | return null
75 | }
76 |
77 | return (
78 | |