├── .env ├── public ├── evanbacon.jpg ├── evanbacon.avif ├── placeholder-user.avif └── placeholder.svg ├── assets └── images │ ├── icon.png │ ├── favicon.png │ ├── splash.png │ └── adaptive-icon.png ├── app ├── x │ ├── ios+api.ts │ └── src+api.ts ├── (index,orders,products,analytics) │ ├── orders.tsx │ ├── products.tsx │ ├── analytics.tsx │ ├── settings.tsx │ ├── index.tsx │ └── _layout.tsx ├── +html.tsx └── _layout.tsx ├── components ├── mdx │ ├── sci-fi.avif │ ├── mobile-cropper.tsx │ ├── tiptap.css │ ├── syntax.tsx │ ├── tiptap.module.css │ ├── mdx-route.tsx │ ├── story.mdx │ ├── ambient-bulb.tsx │ ├── shaders.tsx │ └── tiptap-wrap.tsx ├── touchable-bounce.tsx ├── touchable-bounce.native.tsx ├── haptic-tab.tsx ├── NoSelect.tsx ├── touchable-impact.tsx ├── shad │ ├── settings.tsx │ ├── shad-layout.tsx │ ├── shad-nav.tsx │ ├── products.tsx │ └── orders.tsx ├── ui │ ├── label.tsx │ ├── textarea.tsx │ ├── separator.tsx │ ├── progress.tsx │ ├── input.tsx │ ├── tooltip.tsx │ ├── badge.tsx │ ├── toggle.tsx │ ├── avatar.tsx │ ├── toggle-group.tsx │ ├── button.tsx │ ├── tabs.tsx │ ├── card.tsx │ ├── breadcrumb.tsx │ ├── pagination.tsx │ ├── table.tsx │ ├── sheet.tsx │ ├── select.tsx │ ├── dropdown-menu.tsx │ └── chart.tsx ├── flow │ ├── flow-edge.tsx │ ├── flow-node.tsx │ ├── flow.css │ └── flow.tsx └── global-button-haptics.tsx ├── postcss.config.js ├── lib ├── utils.ts └── tab-to-top.ts ├── babel.config.js ├── tsconfig.json ├── metro.config.js ├── components.json ├── .gitignore ├── eas.json ├── metro.transformer.js ├── index.ts ├── README.md ├── store.config.json ├── app.json ├── global.css ├── tailwind.config.js └── package.json /.env: -------------------------------------------------------------------------------- 1 | EXPO_USE_FAST_RESOLVER=1 2 | EXPO_UNSTABLE_DEPLOY_SERVER=1 -------------------------------------------------------------------------------- /public/evanbacon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EvanBacon/expo-dom-components-example/HEAD/public/evanbacon.jpg -------------------------------------------------------------------------------- /assets/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EvanBacon/expo-dom-components-example/HEAD/assets/images/icon.png -------------------------------------------------------------------------------- /public/evanbacon.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EvanBacon/expo-dom-components-example/HEAD/public/evanbacon.avif -------------------------------------------------------------------------------- /assets/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EvanBacon/expo-dom-components-example/HEAD/assets/images/favicon.png -------------------------------------------------------------------------------- /assets/images/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EvanBacon/expo-dom-components-example/HEAD/assets/images/splash.png -------------------------------------------------------------------------------- /app/x/ios+api.ts: -------------------------------------------------------------------------------- 1 | export function GET() { 2 | return Response.redirect("https://testflight.apple.com/join/W2r9d14u"); 3 | } 4 | -------------------------------------------------------------------------------- /components/mdx/sci-fi.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EvanBacon/expo-dom-components-example/HEAD/components/mdx/sci-fi.avif -------------------------------------------------------------------------------- /components/touchable-bounce.tsx: -------------------------------------------------------------------------------- 1 | import { TouchableOpacity } from "react-native"; 2 | 3 | export default TouchableOpacity; 4 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /public/placeholder-user.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EvanBacon/expo-dom-components-example/HEAD/public/placeholder-user.avif -------------------------------------------------------------------------------- /assets/images/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EvanBacon/expo-dom-components-example/HEAD/assets/images/adaptive-icon.png -------------------------------------------------------------------------------- /app/x/src+api.ts: -------------------------------------------------------------------------------- 1 | export function GET() { 2 | return Response.redirect( 3 | "https://github.com/EvanBacon/expo-dom-components-example" 4 | ); 5 | } 6 | -------------------------------------------------------------------------------- /components/touchable-bounce.native.tsx: -------------------------------------------------------------------------------- 1 | import TouchableBounce from "react-native/Libraries/Components/Touchable/TouchableBounce"; 2 | 3 | export default TouchableBounce; 4 | -------------------------------------------------------------------------------- /lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx" 2 | import { twMerge } from "tailwind-merge" 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)) 6 | } 7 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function (api) { 2 | api.cache(true); 3 | return { 4 | presets: [ 5 | [ 6 | "babel-preset-expo", 7 | { 8 | unstable_transformImportMeta: true, 9 | }, 10 | ], 11 | ], 12 | }; 13 | }; 14 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "expo/tsconfig.base", 3 | "compilerOptions": { 4 | "strict": true, 5 | "paths": { 6 | "@/*": [ 7 | "./*" 8 | ] 9 | } 10 | }, 11 | "include": [ 12 | "**/*.ts", 13 | "**/*.tsx", 14 | ".expo/types/**/*.ts", 15 | "expo-env.d.ts" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /metro.config.js: -------------------------------------------------------------------------------- 1 | // Learn more https://docs.expo.dev/guides/customizing-metro/ 2 | const { getDefaultConfig } = require("expo/metro-config"); 3 | 4 | const config = getDefaultConfig(__dirname); 5 | 6 | config.resolver.sourceExts.push("md", "mdx"); 7 | 8 | config.transformer.babelTransformerPath = require.resolve( 9 | "./metro.transformer.js" 10 | ); 11 | 12 | module.exports = config; 13 | -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": false, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.js", 8 | "css": "global.css", 9 | "baseColor": "slate", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils" 16 | } 17 | } -------------------------------------------------------------------------------- /components/mdx/mobile-cropper.tsx: -------------------------------------------------------------------------------- 1 | "use dom"; 2 | 3 | import { Cropper } from "react-mobile-cropper"; 4 | import "react-mobile-cropper/dist/style.css"; 5 | 6 | export default function CropPage({ 7 | image, 8 | style, 9 | }: { 10 | image: string; 11 | style?: React.CSSProperties; 12 | dom?: import("expo/dom").DOMProps; 13 | }) { 14 | return ; 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .expo/ 3 | dist/ 4 | npm-debug.* 5 | *.jks 6 | *.p8 7 | *.p12 8 | *.key 9 | *.mobileprovision 10 | *.orig.* 11 | web-build/ 12 | 13 | # macOS 14 | .DS_Store 15 | 16 | # @generated expo-cli sync-2b81b286409207a5da26e14c78851eb30d8ccbdb 17 | # The following patterns were generated by expo-cli 18 | 19 | expo-env.d.ts 20 | # @end expo-cli 21 | 22 | /ios 23 | /android 24 | # Local Netlify folder 25 | .netlify 26 | -------------------------------------------------------------------------------- /eas.json: -------------------------------------------------------------------------------- 1 | { 2 | "cli": { 3 | "version": ">= 10.2.4", 4 | "appVersionSource": "remote" 5 | }, 6 | "build": { 7 | "development": { 8 | "developmentClient": true, 9 | "distribution": "internal" 10 | }, 11 | "preview": { 12 | "distribution": "internal" 13 | }, 14 | "production": { 15 | "resourceClass": "large", 16 | "autoIncrement": true 17 | } 18 | }, 19 | "submit": { 20 | "production": {} 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /metro.transformer.js: -------------------------------------------------------------------------------- 1 | const upstreamTransformer = require('@expo/metro-config/babel-transformer'); 2 | const MdxTransformer = require('@bacons/mdx/metro-transformer'); 3 | 4 | const mdxTransformer = MdxTransformer.createTransformer({}); 5 | 6 | module.exports.transform = async (props) => { 7 | // Then pass it to the upstream transformer. 8 | return upstreamTransformer.transform( 9 | // Transpile MDX first. 10 | await mdxTransformer.transform(props) 11 | ); 12 | }; 13 | -------------------------------------------------------------------------------- /components/haptic-tab.tsx: -------------------------------------------------------------------------------- 1 | import { BottomTabBarButtonProps } from "@react-navigation/bottom-tabs"; 2 | import { PlatformPressable } from "@react-navigation/elements"; 3 | import * as Haptics from "expo-haptics"; 4 | 5 | export function HapticTab(props: BottomTabBarButtonProps) { 6 | return ( 7 | { 10 | if (process.env.EXPO_OS !== "web") { 11 | // Add a soft haptic feedback when pressing down on the tabs. 12 | Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); 13 | } 14 | props.onPressIn?.(ev); 15 | }} 16 | /> 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | import "expo-router/entry"; 2 | 3 | import { LogBox } from "react-native"; 4 | import * as Notifications from "expo-notifications"; 5 | 6 | LogBox.ignoreLogs([ 7 | "Android Push notifications", 8 | "functionality provided by expo-notifications", 9 | "functionality is not fully supported in Expo Go", 10 | // Shows when adding the tab bar during fast refresh. Maybe related to the scrolling system? 11 | "Error evaluating injectedJavaScript:", 12 | ]); 13 | Notifications.setNotificationHandler({ 14 | handleNotification: async () => ({ 15 | shouldShowAlert: true, 16 | shouldPlaySound: false, 17 | shouldSetBadge: false, 18 | }), 19 | }); 20 | -------------------------------------------------------------------------------- /components/NoSelect.tsx: -------------------------------------------------------------------------------- 1 | export function StyleNoSelect() { 2 | if ( 3 | // @ts-expect-error: Added via react-native-webview 4 | typeof ReactNativeWebView === "undefined" 5 | ) 6 | return null; 7 | return ( 8 |