Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions apps/expo/app/ExpoRootLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
import { Stack } from 'expo-router'
import UniversalAppProviders from '@app/core/screens/UniversalAppProviders'
import UniversalRootLayout from '@app/core/screens/UniversalRootLayout'
import { NativeWindStyleSheet } from 'nativewind'

// -i- Expo Router's layout setup is much simpler than Next.js's layout setup
// -i- Since Expo doesn't require a custom document setup or server component root layout
// -i- Use this file to apply your Expo specific layout setup:
// -i- like rendering our Universal Layout and App Providers

/* --- Settings -------------------------------------------------------------------------------- */

NativeWindStyleSheet.setOutput({
default: 'native',
})

/* --- <ExpoRootLayout> ------------------------------------------------------------------------ */

export default function ExpoRootLayout() {
return (
<UniversalAppProviders>
Expand Down
3 changes: 2 additions & 1 deletion apps/expo/babel.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
module.exports = function (api) {
api.cache(true)
return {
presets: ["babel-preset-expo"],
presets: ['babel-preset-expo'],
plugins: ['nativewind/babel'],
}
}
14 changes: 14 additions & 0 deletions apps/expo/tailwind.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const { universalTheme } = require('@app/core/tailwind.theme')

/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'../../apps/**/*.tsx',
'../../features/**/*.tsx',
'../../packages/**/*.tsx',
],
plugins: [],
theme: {
...universalTheme,
},
}
3 changes: 2 additions & 1 deletion apps/next/babel.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
module.exports = function (api) {
api.cache(true)
return {
presets: ["babel-preset-expo"],
presets: ['babel-preset-expo'],
plugins: ['nativewind/babel', { mode: 'transformOnly' }],
}
}
5 changes: 5 additions & 0 deletions apps/next/global.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@

@tailwind base;
@tailwind components;
@tailwind utilities;

/* -i- Upgrade from the CSS reset that came with Expo's default Next.js setup */
/* Follows the setup for react-native-web: */
/* https://necolas.github.io/react-native-web/docs/setup/#root-element */
Expand Down
1 change: 1 addition & 0 deletions apps/next/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const nextConfig = withExpo({
"react-native",
"react-native-web",
"expo",
"nativewind",
// Add more React Native / Expo packages here...
],
typescript: {
Expand Down
5 changes: 4 additions & 1 deletion apps/next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
"dependencies": {
"next": "~14.0.4"
},
"devDependencies": {},
"devDependencies": {
"autoprefixer": "^10.4.19",
"postcss": "8.4.23"
},
"scripts": {
"dev": "next dev",
"build": "next build",
Expand Down
6 changes: 6 additions & 0 deletions apps/next/postcss.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
15 changes: 15 additions & 0 deletions apps/next/tailwind.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const { universalTheme } = require('@app/core/tailwind.theme')

/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'../../apps/**/*.tsx',
'../../features/**/*.tsx',
'../../packages/**/*.tsx',
],
plugins: [require('nativewind/tailwind/css')],
important: 'html',
theme: {
...universalTheme,
},
}
8 changes: 6 additions & 2 deletions features/app-core/components/Image.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Image as ExpoImage } from 'expo-image'
import { Platform } from 'react-native'
import { UniversalImageProps, UniversalImageMethods } from './Image.types'
import { parseNativeWindStyles } from '../utils/parseNativeWindStyles'

/* --- <Image/> -------------------------------------------------------------------------------- */

Expand Down Expand Up @@ -39,10 +40,13 @@ const Image = (props: UniversalImageProps): JSX.Element => {
responsivePolicy,
} = props

// -- Nativewind --

const { nativeWindStyles, restStyle } = parseNativeWindStyles(style)
const finalStyle = { width, height, ...nativeWindStyles, ...restStyle }

// -- Overrides --

// @ts-ignore
const finalStyle = { width, height, ...style }
if (fill) finalStyle.height = '100%'
if (fill) finalStyle.width = '100%'

Expand Down
4 changes: 4 additions & 0 deletions features/app-core/components/Image.types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ export type UniversalImageProps = {
* - Remember that the required width and height props can interact with your styling. If you use styling to modify an image's width, you should also style its height to auto to preserve its intrinsic aspect ratio, or your image will be distorted. */
style?: ExpoImageProps['style']

/** Universal, will affect both Expo & Next.js
* - Remember that the required width and height props can interact with your styling. If you use styling to modify an image's width, you should also style its height to auto to preserve its intrinsic aspect ratio, or your image will be distorted. */
className?: string

/** Universal, will affect both Expo & Next.js - Called on an image fetching error. */
onError?: ExpoImageProps['onError']

Expand Down
12 changes: 9 additions & 3 deletions features/app-core/components/Image.web.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import NextImage from 'next/image'
import { UniversalImageProps, UniversalImageMethods } from './Image.types'
import { parseNativeWindStyles } from '../utils/parseNativeWindStyles'

/* --- <Image/> -------------------------------------------------------------------------------- */

Expand All @@ -11,6 +12,7 @@ const Image = (props: UniversalImageProps): JSX.Element => {
alt,
width,
height,
className,
style = {},
priority = 'normal',
onError,
Expand All @@ -31,10 +33,13 @@ const Image = (props: UniversalImageProps): JSX.Element => {
contentFit,
} = props

// -- Nativewind --

const { nativeWindStyles, nativeWindClassName, restStyle } = parseNativeWindStyles(style)
const finalStyle = { width, height, ...nativeWindStyles, ...restStyle } as React.CSSProperties

// -- Overrides --

// @ts-ignore
const finalStyle = { width, height, ...style }
if (fill) finalStyle.height = '100%'
if (fill) finalStyle.width = '100%'
if (fill) finalStyle.objectFit = contentFit || 'cover'
Expand All @@ -47,7 +52,8 @@ const Image = (props: UniversalImageProps): JSX.Element => {
src={src as any}
alt={alt || accessibilityLabel}
width={width}
height={height} // @ts-ignore
height={height}
className={[className, nativeWindClassName].filter(Boolean).join(' ')}
style={finalStyle}
priority={priority === 'high'}
onError={onError as any}
Expand Down
33 changes: 33 additions & 0 deletions features/app-core/components/styled.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { styled } from 'nativewind'
import { Text as RNText, View as RNView } from 'react-native'
import { Link as UniversalLink } from '../navigation/Link'
import { Image as UniversalImage } from './Image'

/* --- Primitives ------------------------------------------------------------------------------ */

export const View = styled(RNView, '')
export const Text = styled(RNText, '')
export const Image = styled(UniversalImage, '')

/* --- Typography ------------------------------------------------------------------------------ */

export const H1 = styled(RNText, 'font-bold text-2xl text-primary-500')
export const H2 = styled(RNText, 'font-bold text-xl text-primary-500')
export const H3 = styled(RNText, 'font-bold text-lg text-primary-500')

export const P = styled(RNText, 'text-base')

/* --- Fix for Next Link ----------------------------------------------------------------------- */

export const Link = styled(UniversalLink, 'text-blue-500 underline')
export const LinkText = styled(RNText, 'text-blue-500 underline')
export const TextLink = (props: Omit<React.ComponentProps<typeof UniversalLink>, 'className'> & { className?: string }) => {
const { className, style, children, ...universalLinkProps } = props
return (
<LinkText className={className} style={style}>
<UniversalLink {...universalLinkProps}>
{children}
</UniversalLink>
</LinkText>
)
}
8 changes: 7 additions & 1 deletion features/app-core/navigation/Link.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Link as ExpoLink } from 'expo-router'
import type { UniversalLinkProps } from './Link.types'
import { parseNativeWindStyles } from '../utils/parseNativeWindStyles'

/* --- <Link/> --------------------------------------------------------------------------------- */

Expand All @@ -21,12 +22,17 @@ export const Link = (props: UniversalLinkProps) => {
maxFontSizeMultiplier
} = props

// -- Nativewind --

const { nativeWindStyles, restStyle } = parseNativeWindStyles(style)
const finalStyle = { ...nativeWindStyles, ...restStyle }

// -- Render --

return (
<ExpoLink
href={href}
style={style}
style={finalStyle}
onPress={onPress}
target={target}
asChild={asChild}
Expand Down
3 changes: 3 additions & 0 deletions features/app-core/navigation/Link.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ export type UniversalLinkProps = {
/** Universal - Style prop: https://reactnative.dev/docs/text#style */
style?: ExpoLinkProps['style'];

/** -!- Nativewind classNames should be applied to either the parent or children of Link. Ideally, create or use a TextLink component instead */
className?: string; // never;

/** Universal - Should replace the current route without adding to the history - Default: false. */
replace?: boolean;

Expand Down
10 changes: 9 additions & 1 deletion features/app-core/navigation/Link.web.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import NextLink from 'next/link'
import type { ComponentProps } from 'react'
import type { UniversalLinkProps } from './Link.types'
import { parseNativeWindStyles } from '../utils/parseNativeWindStyles'

/* --- <Link/> --------------------------------------------------------------------------------- */

Expand All @@ -9,6 +10,7 @@ export const Link = (props: UniversalLinkProps) => {
const {
children,
href,
className,
style,
replace,
onPress,
Expand All @@ -21,12 +23,18 @@ export const Link = (props: UniversalLinkProps) => {
as,
} = props

// -- Nativewind --

const { nativeWindStyles, nativeWindClassName, restStyle } = parseNativeWindStyles(style)
const finalStyle = { ...nativeWindStyles, ...restStyle } as React.CSSProperties

// -- Render --

return (
<NextLink
href={href}
style={style as unknown as ComponentProps<typeof NextLink>['style']}
className={[className, nativeWindClassName].filter(Boolean).join(' ')}
style={finalStyle as unknown as ComponentProps<typeof NextLink>['style']}
onClick={onPress}
target={target}
replace={replace}
Expand Down
5 changes: 4 additions & 1 deletion features/app-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
"name": "@app/core",
"version": "1.0.0",
"private": true,
"dependencies": {},
"dependencies": {
"nativewind": "^2.0.11"
},
"devDependencies": {
"tailwindcss": "3.3.2",
"typescript": "5.3.3"
},
"scripts": {}
Expand Down
51 changes: 14 additions & 37 deletions features/app-core/screens/HomeScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,50 +1,27 @@
import React from 'react'
import { StyleSheet, Text, View } from 'react-native'
import { Link } from '../navigation/Link'
import { Image } from '../components/Image'
import { View, Image, H3, P, Link } from '../components/styled'

/* --- <HomeScreen/> --------------------------------------------------------------------------- */

const HomeScreen = () => {
return (
<View style={styles.container}>
<Image src={require('../assets/aetherspaceLogo.png')} width={60} height={60} style={{ marginBottom: 12 }} />
<Text style={styles.title}>Expo + Next.js app routing 🚀</Text>
<Text style={styles.subtitle}>Open HomeScreen.tsx in features/app-core/screens to start working on your app</Text>
<Link href="/subpages/aetherspace" style={styles.link}>Test navigation</Link>
<Link href="/images" style={styles.link}>Test images</Link>
<Link href="https://universal-base-starter-docs.vercel.app/" target="_blank" style={styles.link}>Docs</Link>
<View className="flex flex-1 justify-center items-center">
<Image src={require('../assets/aetherspaceLogo.png')} width={60} height={60} className="mb-3" />
<H3 className="text-center">Expo + Next.js app routing 🚀</H3>
<P className="mt-2 text-center">Open HomeScreen.tsx in features/app-core/screens to start working on your app</P>
<Link className="mt-4 text-center" href="/subpages/aetherspace">
Test navigation
</Link>
<Link className="mt-4 text-center" href="/images">
Test images
</Link>
<Link className="mt-4 text-center" href="https://universal-base-starter-docs.vercel.app/" target="_blank">
Docs
</Link>
</View>
)
}

/* --- Styles ---------------------------------------------------------------------------------- */

const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
title: {
fontWeight: 'bold',
fontSize: 16,
textAlign: 'center',
},
subtitle: {
marginTop: 8,
fontSize: 16,
textAlign: 'center',
},
link: {
marginTop: 16,
fontSize: 16,
color: 'blue',
textAlign: 'center',
textDecorationLine: 'underline',
},
})

/* --- Exports --------------------------------------------------------------------------------- */

export default HomeScreen
Loading