Squash merge feat/expo-app: app-expo, .cursor, workflows, package.json, .husky; remove app-android, app-ios, react-app
This commit is contained in:
5
app-expo/src/hooks/use-app-settings.ts
Normal file
5
app-expo/src/hooks/use-app-settings.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { useAppSettingsContext } from '@/core/app-settings-context';
|
||||
|
||||
export function useAppSettings() {
|
||||
return useAppSettingsContext();
|
||||
}
|
||||
95
app-expo/src/hooks/use-breakpoint.ts
Normal file
95
app-expo/src/hooks/use-breakpoint.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useWindowDimensions } from 'react-native';
|
||||
|
||||
import { Breakpoints } from '@/constants/layout';
|
||||
|
||||
export type BreakpointName = keyof typeof Breakpoints | 'base';
|
||||
|
||||
type BreakpointMatches = Record<keyof typeof Breakpoints, boolean>;
|
||||
|
||||
const BREAKPOINT_ORDER: (keyof typeof Breakpoints)[] = [
|
||||
'sm',
|
||||
'md',
|
||||
'lg',
|
||||
'xl',
|
||||
'2xl',
|
||||
];
|
||||
|
||||
const ALL_BREAKPOINTS: BreakpointName[] = [
|
||||
'base',
|
||||
'sm',
|
||||
'md',
|
||||
'lg',
|
||||
'xl',
|
||||
'2xl',
|
||||
];
|
||||
|
||||
/**
|
||||
* Returns the current breakpoint based on window width (mobile-first).
|
||||
*
|
||||
* - `base`: width < 390px (compact phones)
|
||||
* - `sm`..`2xl`: width >= that breakpoint
|
||||
*/
|
||||
export function useBreakpoint(): BreakpointName {
|
||||
const { width } = useWindowDimensions();
|
||||
|
||||
for (let i = BREAKPOINT_ORDER.length - 1; i >= 0; i--) {
|
||||
const name = BREAKPOINT_ORDER[i];
|
||||
if (width >= Breakpoints[name]) return name;
|
||||
}
|
||||
return 'base';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns booleans for each breakpoint (mobile-first: true when width >= that breakpoint).
|
||||
* Memoized to avoid unnecessary re-renders.
|
||||
*/
|
||||
export function useBreakpointMatches(): BreakpointMatches {
|
||||
const { width } = useWindowDimensions();
|
||||
|
||||
return useMemo(
|
||||
() => ({
|
||||
sm: width >= Breakpoints.sm,
|
||||
md: width >= Breakpoints.md,
|
||||
lg: width >= Breakpoints.lg,
|
||||
xl: width >= Breakpoints.xl,
|
||||
'2xl': width >= Breakpoints['2xl'],
|
||||
}),
|
||||
[width],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if width >= the given breakpoint (mobile-first).
|
||||
* Use when you need a one-off check without the full matches object.
|
||||
*/
|
||||
export function isBreakpointUp(
|
||||
width: number,
|
||||
breakpoint: keyof typeof Breakpoints,
|
||||
): boolean {
|
||||
return width >= Breakpoints[breakpoint];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the responsive value for the current breakpoint (mobile-first).
|
||||
* Picks the value for the largest breakpoint that matches; falls back to smaller breakpoints if not defined.
|
||||
*
|
||||
* @example
|
||||
* const columns = useBreakpointValue({ base: 1, sm: 2, md: 3 });
|
||||
* // width 350: 1, width 400: 2, width 800: 3
|
||||
*
|
||||
* @example
|
||||
* const padding = useBreakpointValue({ base: 16, md: 24 });
|
||||
*/
|
||||
export function useBreakpointValue<T>(
|
||||
values: Partial<Record<BreakpointName, T>>,
|
||||
): T | undefined {
|
||||
const breakpoint = useBreakpoint();
|
||||
const idx = ALL_BREAKPOINTS.indexOf(breakpoint);
|
||||
|
||||
for (let i = idx; i >= 0; i--) {
|
||||
const v = values[ALL_BREAKPOINTS[i]];
|
||||
if (v !== undefined) return v;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
9
app-expo/src/hooks/use-color-scheme.ts
Normal file
9
app-expo/src/hooks/use-color-scheme.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Re-export NativeWind's useColorScheme for unified light/dark handling.
|
||||
* Uses Appearance API on native and prefers-color-scheme on web.
|
||||
* Supports manual override via setColorScheme() for user toggles.
|
||||
*
|
||||
* @see https://www.nativewind.dev/docs/core-concepts/dark-mode
|
||||
* @see https://www.nativewind.dev/docs/api/use-color-scheme
|
||||
*/
|
||||
export { useColorScheme } from 'nativewind';
|
||||
20
app-expo/src/hooks/use-color-scheme.web.ts
Normal file
20
app-expo/src/hooks/use-color-scheme.web.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useColorScheme as useNativeWindColorScheme } from 'nativewind';
|
||||
|
||||
/**
|
||||
* Web override: avoid hydration mismatch during static rendering.
|
||||
* Returns 'light' until client hydration, then uses NativeWind's value.
|
||||
*/
|
||||
export function useColorScheme() {
|
||||
const [hasHydrated, setHasHydrated] = useState(false);
|
||||
useEffect(() => setHasHydrated(true), []);
|
||||
|
||||
const { colorScheme, setColorScheme, toggleColorScheme } =
|
||||
useNativeWindColorScheme();
|
||||
|
||||
return {
|
||||
colorScheme: hasHydrated ? (colorScheme ?? 'light') : 'light',
|
||||
setColorScheme,
|
||||
toggleColorScheme,
|
||||
};
|
||||
}
|
||||
16
app-expo/src/hooks/use-theme-colors.ts
Normal file
16
app-expo/src/hooks/use-theme-colors.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { useAppSettingsContext } from '@/core/app-settings-context';
|
||||
import { getThemeColors } from '@/constants/theme-bridge';
|
||||
import { useColorScheme } from '@/hooks/use-color-scheme';
|
||||
|
||||
/**
|
||||
* Returns the current theme's resolved colors from design-tokens.
|
||||
* Use this for inline styles when NativeWind className is not suitable.
|
||||
* Resolves themeName (from app settings) + colorScheme (light/dark).
|
||||
* Must be used within AppSettingsProvider.
|
||||
*/
|
||||
export function useThemeColors() {
|
||||
const { themeName } = useAppSettingsContext();
|
||||
const { colorScheme } = useColorScheme();
|
||||
const resolved = colorScheme === 'dark' ? 'dark' : 'light';
|
||||
return getThemeColors(themeName, resolved);
|
||||
}
|
||||
Reference in New Issue
Block a user