Make RN components fully theme-driven for multi-brand support
Refactor all 18 React Native components from static DTColors imports to dynamic useDTTheme() context, enabling proper visual differentiation when switching between DT and Classic brands. Key changes: - Replace static DTColors with theme context in all components - Add shape tokens (bevel/radius) to DTExtendedTheme interface - Conditionally render SVG bevels (DT) vs borderRadius (Classic) - Configure fonts per brand (Tektur for DT, system sans-serif for Classic) - Update buildThemeFromBrand to parse shape and typography tokens - Fix Metro config for monorepo singleton resolution Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -20,7 +20,7 @@ import {
|
|||||||
Animated,
|
Animated,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import {Text} from 'react-native-paper';
|
import {Text} from 'react-native-paper';
|
||||||
import {DTColors} from '../theme/colors';
|
import {useDTTheme} from '../theme/DTThemeProvider';
|
||||||
import {type DTVariant, getVariantColor} from '../utils/variantColors';
|
import {type DTVariant, getVariantColor} from '../utils/variantColors';
|
||||||
|
|
||||||
export interface DTAccordionSection {
|
export interface DTAccordionSection {
|
||||||
@@ -93,6 +93,7 @@ export function DTAccordion({
|
|||||||
onSectionToggle,
|
onSectionToggle,
|
||||||
style,
|
style,
|
||||||
}: DTAccordionProps) {
|
}: DTAccordionProps) {
|
||||||
|
const theme = useDTTheme();
|
||||||
const [openKeys, setOpenKeys] = useState<Set<string>>(
|
const [openKeys, setOpenKeys] = useState<Set<string>>(
|
||||||
new Set(initialOpenKeys),
|
new Set(initialOpenKeys),
|
||||||
);
|
);
|
||||||
@@ -114,8 +115,8 @@ export function DTAccordion({
|
|||||||
[allowMultiple, onSectionToggle],
|
[allowMultiple, onSectionToggle],
|
||||||
);
|
);
|
||||||
|
|
||||||
const inactiveColor = getVariantColor(variant);
|
const inactiveColor = getVariantColor(theme, variant);
|
||||||
const activeColor = getVariantColor(activeVariant);
|
const activeColor = getVariantColor(theme, activeVariant);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.container, style]}>
|
<View style={[styles.container, style]}>
|
||||||
@@ -146,6 +147,7 @@ function AccordionSection({
|
|||||||
inactiveColor: string;
|
inactiveColor: string;
|
||||||
activeColor: string;
|
activeColor: string;
|
||||||
}) {
|
}) {
|
||||||
|
const theme = useDTTheme();
|
||||||
const heightAnim = useRef(new Animated.Value(isOpen ? 1 : 0)).current;
|
const heightAnim = useRef(new Animated.Value(isOpen ? 1 : 0)).current;
|
||||||
const chevronAnim = useRef(new Animated.Value(isOpen ? 1 : 0)).current;
|
const chevronAnim = useRef(new Animated.Value(isOpen ? 1 : 0)).current;
|
||||||
const [contentHeight, setContentHeight] = useState(0);
|
const [contentHeight, setContentHeight] = useState(0);
|
||||||
@@ -187,6 +189,7 @@ function AccordionSection({
|
|||||||
{
|
{
|
||||||
borderColor: sectionColor,
|
borderColor: sectionColor,
|
||||||
borderTopColor: sectionColor,
|
borderTopColor: sectionColor,
|
||||||
|
backgroundColor: theme.colors.background,
|
||||||
opacity: pressed ? 0.7 : 1,
|
opacity: pressed ? 0.7 : 1,
|
||||||
},
|
},
|
||||||
]}>
|
]}>
|
||||||
@@ -214,7 +217,7 @@ function AccordionSection({
|
|||||||
},
|
},
|
||||||
]}>
|
]}>
|
||||||
<View
|
<View
|
||||||
style={styles.content}
|
style={[styles.content, {backgroundColor: theme.colors.background}]}
|
||||||
onLayout={e => {
|
onLayout={e => {
|
||||||
const h = e.nativeEvent.layout.height;
|
const h = e.nativeEvent.layout.height;
|
||||||
if (h > 0 && !measured) {
|
if (h > 0 && !measured) {
|
||||||
@@ -240,7 +243,6 @@ const styles = StyleSheet.create({
|
|||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
backgroundColor: DTColors.dark,
|
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
borderTopWidth: 5,
|
borderTopWidth: 5,
|
||||||
paddingVertical: 12,
|
paddingVertical: 12,
|
||||||
@@ -256,7 +258,6 @@ const styles = StyleSheet.create({
|
|||||||
content: {
|
content: {
|
||||||
paddingHorizontal: 16,
|
paddingHorizontal: 16,
|
||||||
paddingVertical: 12,
|
paddingVertical: 12,
|
||||||
backgroundColor: DTColors.dark,
|
|
||||||
},
|
},
|
||||||
chevron: {
|
chevron: {
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
import {StyleSheet, View, ViewStyle, StyleProp, Pressable} from 'react-native';
|
import {StyleSheet, View, ViewStyle, StyleProp, Pressable} from 'react-native';
|
||||||
import {Text} from 'react-native-paper';
|
import {Text} from 'react-native-paper';
|
||||||
import Svg, {Path} from 'react-native-svg';
|
import Svg, {Path} from 'react-native-svg';
|
||||||
import {DTColors} from '../theme/colors';
|
import {useDTTheme} from '../theme/DTThemeProvider';
|
||||||
import {type DTVariant, getVariantColor} from '../utils/variantColors';
|
import {type DTVariant, getVariantColor} from '../utils/variantColors';
|
||||||
import {buildButtonBevelPath} from '../utils/bevelPaths';
|
import {buildButtonBevelPath} from '../utils/bevelPaths';
|
||||||
import {useComponentLayout} from '../utils/useComponentLayout';
|
import {useComponentLayout} from '../utils/useComponentLayout';
|
||||||
@@ -83,16 +83,18 @@ export function DTButton({
|
|||||||
borderWidth = 2,
|
borderWidth = 2,
|
||||||
bevelSize = 8,
|
bevelSize = 8,
|
||||||
}: DTButtonProps) {
|
}: DTButtonProps) {
|
||||||
|
const theme = useDTTheme();
|
||||||
const {dimensions, onLayout, hasDimensions} = useComponentLayout();
|
const {dimensions, onLayout, hasDimensions} = useComponentLayout();
|
||||||
const [pressed, setPressed] = useState(false);
|
const [pressed, setPressed] = useState(false);
|
||||||
|
|
||||||
const accentColor = getVariantColor(variant, color);
|
const accentColor = getVariantColor(theme, variant, color);
|
||||||
const isContained = mode === 'contained';
|
const isContained = mode === 'contained';
|
||||||
const bgColor = isContained ? accentColor : 'transparent';
|
const bgColor = isContained ? accentColor : 'transparent';
|
||||||
const textColor = isContained ? DTColors.dark : accentColor;
|
const textColor = isContained ? theme.colors.onPrimary : accentColor;
|
||||||
|
|
||||||
const {width, height} = dimensions;
|
const {width, height} = dimensions;
|
||||||
const opacity = disabled ? 0.5 : pressed ? 0.7 : 1;
|
const opacity = disabled ? 0.5 : pressed ? 0.7 : 1;
|
||||||
|
const useBevels = theme.custom.bevelMd > 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Pressable
|
<Pressable
|
||||||
@@ -101,8 +103,18 @@ export function DTButton({
|
|||||||
onPressIn={() => setPressed(true)}
|
onPressIn={() => setPressed(true)}
|
||||||
onPressOut={() => setPressed(false)}
|
onPressOut={() => setPressed(false)}
|
||||||
style={[{opacity}, style]}>
|
style={[{opacity}, style]}>
|
||||||
<View style={styles.container} onLayout={onLayout}>
|
<View
|
||||||
{hasDimensions && (
|
style={[
|
||||||
|
styles.container,
|
||||||
|
!useBevels && {
|
||||||
|
borderWidth,
|
||||||
|
borderColor: accentColor,
|
||||||
|
borderRadius: theme.custom.radiusSm,
|
||||||
|
backgroundColor: bgColor,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
onLayout={onLayout}>
|
||||||
|
{useBevels && hasDimensions && (
|
||||||
<Svg
|
<Svg
|
||||||
style={StyleSheet.absoluteFill}
|
style={StyleSheet.absoluteFill}
|
||||||
width={width}
|
width={width}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import {ReactNode} from 'react';
|
|||||||
import {StyleSheet, View, ViewStyle, StyleProp, Pressable} from 'react-native';
|
import {StyleSheet, View, ViewStyle, StyleProp, Pressable} from 'react-native';
|
||||||
import {Text} from 'react-native-paper';
|
import {Text} from 'react-native-paper';
|
||||||
import Svg, {Path} from 'react-native-svg';
|
import Svg, {Path} from 'react-native-svg';
|
||||||
import {DTColors} from '../theme/colors';
|
import {useDTTheme} from '../theme/DTThemeProvider';
|
||||||
import {type DTVariant, getVariantColor} from '../utils/variantColors';
|
import {type DTVariant, getVariantColor} from '../utils/variantColors';
|
||||||
import {buildCardBevelPath} from '../utils/bevelPaths';
|
import {buildCardBevelPath} from '../utils/bevelPaths';
|
||||||
import {useComponentLayout} from '../utils/useComponentLayout';
|
import {useComponentLayout} from '../utils/useComponentLayout';
|
||||||
@@ -68,8 +68,7 @@ interface DTCardProps {
|
|||||||
*/
|
*/
|
||||||
bevelSizeSmall?: number;
|
bevelSizeSmall?: number;
|
||||||
/**
|
/**
|
||||||
* Background color
|
* Background color (defaults to theme background)
|
||||||
* @default '#000000'
|
|
||||||
*/
|
*/
|
||||||
backgroundColor?: string;
|
backgroundColor?: string;
|
||||||
/**
|
/**
|
||||||
@@ -103,34 +102,49 @@ export function DTCard({
|
|||||||
borderWidth = 3,
|
borderWidth = 3,
|
||||||
bevelSize = 32,
|
bevelSize = 32,
|
||||||
bevelSizeSmall = 16,
|
bevelSizeSmall = 16,
|
||||||
backgroundColor = '#000000',
|
backgroundColor,
|
||||||
onPress,
|
onPress,
|
||||||
}: DTCardProps) {
|
}: DTCardProps) {
|
||||||
|
const theme = useDTTheme();
|
||||||
const {dimensions, onLayout, hasDimensions} = useComponentLayout();
|
const {dimensions, onLayout, hasDimensions} = useComponentLayout();
|
||||||
const accentColor = getVariantColor(mode, borderColor);
|
const accentColor = getVariantColor(theme, mode, borderColor);
|
||||||
const shouldShowHeader = showHeader ?? !!title;
|
const shouldShowHeader = showHeader ?? !!title;
|
||||||
|
const bgColor = backgroundColor ?? theme.colors.background;
|
||||||
|
|
||||||
const {width, height} = dimensions;
|
const {width, height} = dimensions;
|
||||||
|
const useBevels = theme.custom.bevelMd > 0;
|
||||||
|
|
||||||
// Outer bevel path (full card shape)
|
// Outer bevel path (full card shape)
|
||||||
const outerPath = hasDimensions
|
const outerPath = useBevels && hasDimensions
|
||||||
? buildCardBevelPath(width, height, bevelSize, bevelSizeSmall, 0)
|
? buildCardBevelPath(width, height, bevelSize, bevelSizeSmall, 0)
|
||||||
: '';
|
: '';
|
||||||
// Inner bevel path at border inset (for background + frame cutout)
|
// Inner bevel path at border inset (for background + frame cutout)
|
||||||
const innerPath = hasDimensions
|
const innerPath = useBevels && hasDimensions
|
||||||
? buildCardBevelPath(width, height, bevelSize - borderWidth, bevelSizeSmall - borderWidth, borderWidth)
|
? buildCardBevelPath(width, height, bevelSize - borderWidth, bevelSizeSmall - borderWidth, borderWidth)
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
const content = (
|
const content = (
|
||||||
<View style={[styles.container, style]} onLayout={onLayout}>
|
<View
|
||||||
{/* Background fill (behind content) */}
|
style={[
|
||||||
{hasDimensions && (
|
styles.container,
|
||||||
|
!useBevels && {
|
||||||
|
borderWidth,
|
||||||
|
borderColor: accentColor,
|
||||||
|
borderRadius: theme.custom.radius,
|
||||||
|
backgroundColor: bgColor,
|
||||||
|
overflow: 'hidden',
|
||||||
|
},
|
||||||
|
style,
|
||||||
|
]}
|
||||||
|
onLayout={onLayout}>
|
||||||
|
{/* Background fill (behind content) — beveled mode only */}
|
||||||
|
{useBevels && hasDimensions && (
|
||||||
<Svg
|
<Svg
|
||||||
style={StyleSheet.absoluteFill}
|
style={StyleSheet.absoluteFill}
|
||||||
width={width}
|
width={width}
|
||||||
height={height}
|
height={height}
|
||||||
viewBox={`0 0 ${width} ${height}`}>
|
viewBox={`0 0 ${width} ${height}`}>
|
||||||
<Path d={innerPath} fill={backgroundColor} />
|
<Path d={innerPath} fill={bgColor} />
|
||||||
</Svg>
|
</Svg>
|
||||||
)}
|
)}
|
||||||
<View style={styles.innerContainer}>
|
<View style={styles.innerContainer}>
|
||||||
@@ -139,7 +153,7 @@ export function DTCard({
|
|||||||
{title && (
|
{title && (
|
||||||
<Text
|
<Text
|
||||||
variant="titleMedium"
|
variant="titleMedium"
|
||||||
style={[styles.headerText, {color: DTColors.dark}]}>
|
style={[styles.headerText, {color: theme.colors.onPrimary}]}>
|
||||||
{title}
|
{title}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
@@ -147,8 +161,8 @@ export function DTCard({
|
|||||||
)}
|
)}
|
||||||
<View style={[styles.content, {padding}, contentStyle]}>{children}</View>
|
<View style={[styles.content, {padding}, contentStyle]}>{children}</View>
|
||||||
</View>
|
</View>
|
||||||
{/* Frame overlay (above content) — clips content at beveled border */}
|
{/* Frame overlay (above content) — beveled mode only */}
|
||||||
{hasDimensions && (
|
{useBevels && hasDimensions && (
|
||||||
<Svg
|
<Svg
|
||||||
style={[StyleSheet.absoluteFill, styles.frameOverlay]}
|
style={[StyleSheet.absoluteFill, styles.frameOverlay]}
|
||||||
width={width}
|
width={width}
|
||||||
|
|||||||
@@ -8,10 +8,10 @@
|
|||||||
* Uses diagonal-symmetry bevels (top-left + bottom-right) on the indicator.
|
* Uses diagonal-symmetry bevels (top-left + bottom-right) on the indicator.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {StyleSheet, ViewStyle, TextStyle, StyleProp, Pressable} from 'react-native';
|
import {StyleSheet, View, ViewStyle, TextStyle, StyleProp, Pressable} from 'react-native';
|
||||||
import {Text} from 'react-native-paper';
|
import {Text} from 'react-native-paper';
|
||||||
import Svg, {Path} from 'react-native-svg';
|
import Svg, {Path} from 'react-native-svg';
|
||||||
import {DTColors} from '../theme/colors';
|
import {useDTTheme} from '../theme/DTThemeProvider';
|
||||||
import {type DTVariant, getVariantColor} from '../utils/variantColors';
|
import {type DTVariant, getVariantColor} from '../utils/variantColors';
|
||||||
import {buildBeveledRectPath} from '../utils/bevelPaths';
|
import {buildBeveledRectPath} from '../utils/bevelPaths';
|
||||||
|
|
||||||
@@ -76,16 +76,20 @@ export function DTCheckbox({
|
|||||||
style,
|
style,
|
||||||
labelStyle,
|
labelStyle,
|
||||||
}: DTCheckboxProps) {
|
}: DTCheckboxProps) {
|
||||||
const accentColor = getVariantColor(variant, color);
|
const theme = useDTTheme();
|
||||||
|
const accentColor = getVariantColor(theme, variant, color);
|
||||||
const opacity = disabled ? 0.5 : 1;
|
const opacity = disabled ? 0.5 : 1;
|
||||||
const borderWidth = 2;
|
const borderWidth = 2;
|
||||||
|
const useBevels = theme.custom.bevelMd > 0;
|
||||||
const bevelSize = Math.round(size * 0.3);
|
const bevelSize = Math.round(size * 0.3);
|
||||||
|
|
||||||
// Outer beveled path (border)
|
// Outer beveled path (border)
|
||||||
const outerPath = buildBeveledRectPath(size, size, {
|
const outerPath = useBevels
|
||||||
corners: {topLeft: bevelSize, bottomRight: bevelSize},
|
? buildBeveledRectPath(size, size, {
|
||||||
strokeWidth: borderWidth,
|
corners: {topLeft: bevelSize, bottomRight: bevelSize},
|
||||||
});
|
strokeWidth: borderWidth,
|
||||||
|
})
|
||||||
|
: '';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Pressable
|
<Pressable
|
||||||
@@ -96,28 +100,44 @@ export function DTCheckbox({
|
|||||||
{opacity: pressed ? 0.7 : opacity},
|
{opacity: pressed ? 0.7 : opacity},
|
||||||
style,
|
style,
|
||||||
]}>
|
]}>
|
||||||
<Svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>
|
{useBevels ? (
|
||||||
{/* Beveled border */}
|
<Svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>
|
||||||
<Path
|
|
||||||
d={outerPath}
|
|
||||||
fill={checked ? accentColor : 'transparent'}
|
|
||||||
stroke={accentColor}
|
|
||||||
strokeWidth={borderWidth}
|
|
||||||
/>
|
|
||||||
{/* Checkmark path (only when checked) */}
|
|
||||||
{checked && (
|
|
||||||
<Path
|
<Path
|
||||||
d={`M ${size * 0.2} ${size * 0.5} L ${size * 0.4} ${size * 0.7} L ${size * 0.8} ${size * 0.25}`}
|
d={outerPath}
|
||||||
fill="none"
|
fill={checked ? accentColor : 'transparent'}
|
||||||
stroke={DTColors.dark}
|
stroke={accentColor}
|
||||||
strokeWidth={borderWidth + 0.5}
|
strokeWidth={borderWidth}
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
/>
|
/>
|
||||||
)}
|
{checked && (
|
||||||
</Svg>
|
<Path
|
||||||
|
d={`M ${size * 0.2} ${size * 0.5} L ${size * 0.4} ${size * 0.7} L ${size * 0.8} ${size * 0.25}`}
|
||||||
|
fill="none"
|
||||||
|
stroke={theme.colors.onPrimary}
|
||||||
|
strokeWidth={borderWidth + 0.5}
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Svg>
|
||||||
|
) : (
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
|
borderWidth,
|
||||||
|
borderColor: accentColor,
|
||||||
|
borderRadius: theme.custom.radiusSm,
|
||||||
|
backgroundColor: checked ? accentColor : 'transparent',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
}}>
|
||||||
|
{checked && (
|
||||||
|
<Text style={{color: theme.colors.onPrimary, fontSize: size * 0.6, lineHeight: size * 0.7}}>✓</Text>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
{label && (
|
{label && (
|
||||||
<Text style={[styles.label, {color: DTColors.light}, labelStyle]}>
|
<Text style={[styles.label, {color: theme.colors.onSurface}, labelStyle]}>
|
||||||
{label}
|
{label}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -8,16 +8,15 @@
|
|||||||
// React import not needed with new JSX transform
|
// React import not needed with new JSX transform
|
||||||
import { StyleSheet, ViewStyle, StyleProp } from 'react-native';
|
import { StyleSheet, ViewStyle, StyleProp } from 'react-native';
|
||||||
import { Chip, ChipProps } from 'react-native-paper';
|
import { Chip, ChipProps } from 'react-native-paper';
|
||||||
import { DTColors } from '../theme/colors';
|
import { useDTTheme } from '../theme/DTThemeProvider';
|
||||||
|
import { type DTVariant, getVariantColor } from '../utils/variantColors';
|
||||||
type DTChipVariant = 'normal' | 'emphasis' | 'warning' | 'success' | 'other';
|
|
||||||
|
|
||||||
interface DTChipProps extends Omit<ChipProps, 'mode'> {
|
interface DTChipProps extends Omit<ChipProps, 'mode'> {
|
||||||
/**
|
/**
|
||||||
* Visual variant of the chip
|
* Visual variant of the chip
|
||||||
* @default 'normal'
|
* @default 'normal'
|
||||||
*/
|
*/
|
||||||
variant?: DTChipVariant;
|
variant?: DTVariant;
|
||||||
/**
|
/**
|
||||||
* Whether the chip is in selected state
|
* Whether the chip is in selected state
|
||||||
* @default false
|
* @default false
|
||||||
@@ -29,14 +28,6 @@ interface DTChipProps extends Omit<ChipProps, 'mode'> {
|
|||||||
style?: StyleProp<ViewStyle>;
|
style?: StyleProp<ViewStyle>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const variantColors: Record<DTChipVariant, string> = {
|
|
||||||
normal: DTColors.modeNormal,
|
|
||||||
emphasis: DTColors.modeEmphasis,
|
|
||||||
warning: DTColors.modeWarning,
|
|
||||||
success: DTColors.modeSuccess,
|
|
||||||
other: DTColors.modeOther,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DT-styled Chip component
|
* DT-styled Chip component
|
||||||
*
|
*
|
||||||
@@ -56,7 +47,8 @@ export function DTChip({
|
|||||||
children,
|
children,
|
||||||
...props
|
...props
|
||||||
}: DTChipProps) {
|
}: DTChipProps) {
|
||||||
const color = variantColors[variant];
|
const theme = useDTTheme();
|
||||||
|
const color = getVariantColor(theme, variant);
|
||||||
|
|
||||||
const chipStyle: ViewStyle = {
|
const chipStyle: ViewStyle = {
|
||||||
backgroundColor: selected ? color : 'transparent',
|
backgroundColor: selected ? color : 'transparent',
|
||||||
@@ -71,7 +63,7 @@ export function DTChip({
|
|||||||
mode="outlined"
|
mode="outlined"
|
||||||
textStyle={[
|
textStyle={[
|
||||||
styles.text,
|
styles.text,
|
||||||
{ color: selected ? DTColors.dark : color },
|
{ color: selected ? theme.colors.onPrimary : color },
|
||||||
]}
|
]}
|
||||||
style={[styles.chip, chipStyle, style]}
|
style={[styles.chip, chipStyle, style]}
|
||||||
selectedColor={color}
|
selectedColor={color}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ import {
|
|||||||
import {Portal, Text} from 'react-native-paper';
|
import {Portal, Text} from 'react-native-paper';
|
||||||
import Svg, {Path} from 'react-native-svg';
|
import Svg, {Path} from 'react-native-svg';
|
||||||
import {useSafeAreaInsets} from 'react-native-safe-area-context';
|
import {useSafeAreaInsets} from 'react-native-safe-area-context';
|
||||||
import {DTColors} from '../theme/colors';
|
import {useDTTheme} from '../theme/DTThemeProvider';
|
||||||
import {type DTVariant, getVariantColor} from '../utils/variantColors';
|
import {type DTVariant, getVariantColor} from '../utils/variantColors';
|
||||||
import {buildDrawerBevelPath} from '../utils/bevelPaths';
|
import {buildDrawerBevelPath} from '../utils/bevelPaths';
|
||||||
|
|
||||||
@@ -108,9 +108,11 @@ export function DTDrawer({
|
|||||||
children,
|
children,
|
||||||
style,
|
style,
|
||||||
}: DTDrawerProps) {
|
}: DTDrawerProps) {
|
||||||
|
const theme = useDTTheme();
|
||||||
|
const useBevels = theme.custom.bevelMd > 0;
|
||||||
const slideAnim = useRef(new Animated.Value(0)).current;
|
const slideAnim = useRef(new Animated.Value(0)).current;
|
||||||
const overlayAnim = useRef(new Animated.Value(0)).current;
|
const overlayAnim = useRef(new Animated.Value(0)).current;
|
||||||
const headerColor = getVariantColor(headingVariant);
|
const headerColor = getVariantColor(theme, headingVariant);
|
||||||
const {width: screenWidth, height: screenHeight} = useWindowDimensions();
|
const {width: screenWidth, height: screenHeight} = useWindowDimensions();
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
const effectiveWidth = Math.min(drawerWidth, screenWidth);
|
const effectiveWidth = Math.min(drawerWidth, screenWidth);
|
||||||
@@ -172,7 +174,7 @@ export function DTDrawer({
|
|||||||
{/* Overlay backdrop */}
|
{/* Overlay backdrop */}
|
||||||
<Animated.View style={[StyleSheet.absoluteFill, {opacity: overlayAnim}]}>
|
<Animated.View style={[StyleSheet.absoluteFill, {opacity: overlayAnim}]}>
|
||||||
<Pressable
|
<Pressable
|
||||||
style={[StyleSheet.absoluteFill, styles.overlay]}
|
style={[StyleSheet.absoluteFill, {backgroundColor: theme.colors.backdrop}]}
|
||||||
onPress={onDismiss}
|
onPress={onDismiss}
|
||||||
/>
|
/>
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
@@ -183,42 +185,49 @@ export function DTDrawer({
|
|||||||
styles.panel,
|
styles.panel,
|
||||||
position === 'right' ? styles.panelRight : styles.panelLeft,
|
position === 'right' ? styles.panelRight : styles.panelLeft,
|
||||||
{width: effectiveWidth, transform: [{translateX}]},
|
{width: effectiveWidth, transform: [{translateX}]},
|
||||||
|
!useBevels && {
|
||||||
|
borderWidth,
|
||||||
|
borderColor: headerColor,
|
||||||
|
borderRadius: theme.custom.radius,
|
||||||
|
backgroundColor: theme.colors.background,
|
||||||
|
overflow: 'hidden',
|
||||||
|
},
|
||||||
style,
|
style,
|
||||||
]}>
|
]}>
|
||||||
{/* Dual-SVG-path technique: outer border + inner dark fill */}
|
{/* Dual-SVG-path technique: outer border + inner dark fill — beveled mode only */}
|
||||||
<View style={StyleSheet.absoluteFill}>
|
{useBevels && (
|
||||||
<Svg width={effectiveWidth} height={screenHeight}>
|
<View style={StyleSheet.absoluteFill}>
|
||||||
{/* Outer path: accent border color */}
|
<Svg width={effectiveWidth} height={screenHeight}>
|
||||||
<Path
|
<Path
|
||||||
d={buildDrawerBevelPath(
|
d={buildDrawerBevelPath(
|
||||||
effectiveWidth,
|
effectiveWidth,
|
||||||
screenHeight,
|
screenHeight,
|
||||||
bevelSize,
|
bevelSize,
|
||||||
position,
|
position,
|
||||||
)}
|
)}
|
||||||
fill={headerColor}
|
fill={headerColor}
|
||||||
/>
|
/>
|
||||||
{/* Inner path: dark background (inset by borderWidth) */}
|
<Path
|
||||||
<Path
|
d={buildDrawerBevelPath(
|
||||||
d={buildDrawerBevelPath(
|
effectiveWidth - borderWidth * 2,
|
||||||
effectiveWidth - borderWidth * 2,
|
screenHeight - borderWidth * 2,
|
||||||
screenHeight - borderWidth * 2,
|
bevelSize - borderWidth,
|
||||||
bevelSize - borderWidth,
|
position,
|
||||||
position,
|
)}
|
||||||
)}
|
fill={theme.colors.background}
|
||||||
fill={DTColors.dark}
|
transform={`translate(${borderWidth}, ${borderWidth})`}
|
||||||
transform={`translate(${borderWidth}, ${borderWidth})`}
|
/>
|
||||||
/>
|
</Svg>
|
||||||
</Svg>
|
</View>
|
||||||
</View>
|
)}
|
||||||
|
|
||||||
{/* Header bar */}
|
{/* Header bar */}
|
||||||
<View style={[styles.header, {backgroundColor: headerColor, paddingTop: insets.top + 16}]}>
|
<View style={[styles.header, {backgroundColor: headerColor, paddingTop: insets.top + 16}]}>
|
||||||
<Text style={styles.headerText}>{heading}</Text>
|
<Text style={[styles.headerText, {color: theme.colors.onPrimary}]}>{heading}</Text>
|
||||||
<Pressable
|
<Pressable
|
||||||
onPress={onDismiss}
|
onPress={onDismiss}
|
||||||
style={({pressed}) => ({opacity: pressed ? 0.7 : 1})}>
|
style={({pressed}) => ({opacity: pressed ? 0.7 : 1})}>
|
||||||
<Text style={styles.closeButton}>✕</Text>
|
<Text style={[styles.closeButton, {color: theme.colors.onPrimary}]}>✕</Text>
|
||||||
</Pressable>
|
</Pressable>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
@@ -235,9 +244,6 @@ export function DTDrawer({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
overlay: {
|
|
||||||
backgroundColor: DTColors.overlay,
|
|
||||||
},
|
|
||||||
panel: {
|
panel: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
top: 0,
|
top: 0,
|
||||||
@@ -258,13 +264,11 @@ const styles = StyleSheet.create({
|
|||||||
zIndex: 1,
|
zIndex: 1,
|
||||||
},
|
},
|
||||||
headerText: {
|
headerText: {
|
||||||
color: DTColors.dark,
|
|
||||||
fontWeight: '700',
|
fontWeight: '700',
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
letterSpacing: 0.5,
|
letterSpacing: 0.5,
|
||||||
},
|
},
|
||||||
closeButton: {
|
closeButton: {
|
||||||
color: DTColors.dark,
|
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
fontWeight: '700',
|
fontWeight: '700',
|
||||||
paddingHorizontal: 8,
|
paddingHorizontal: 8,
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import {
|
|||||||
Platform,
|
Platform,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import Svg, {Path} from 'react-native-svg';
|
import Svg, {Path} from 'react-native-svg';
|
||||||
import {DTColors} from '../theme/colors';
|
import {useDTTheme} from '../theme/DTThemeProvider';
|
||||||
import {type DTVariant, getVariantColor} from '../utils/variantColors';
|
import {type DTVariant, getVariantColor} from '../utils/variantColors';
|
||||||
import {buildButtonBevelPath, buildMediaFrameBevelPath} from '../utils/bevelPaths';
|
import {buildButtonBevelPath, buildMediaFrameBevelPath} from '../utils/bevelPaths';
|
||||||
import {useComponentLayout} from '../utils/useComponentLayout';
|
import {useComponentLayout} from '../utils/useComponentLayout';
|
||||||
@@ -110,9 +110,10 @@ export function DTGallery({
|
|||||||
borderWidth = 3,
|
borderWidth = 3,
|
||||||
style,
|
style,
|
||||||
}: DTGalleryProps) {
|
}: DTGalleryProps) {
|
||||||
|
const theme = useDTTheme();
|
||||||
const flatListRef = useRef<FlatList>(null);
|
const flatListRef = useRef<FlatList>(null);
|
||||||
const {dimensions, onLayout, hasDimensions} = useComponentLayout();
|
const {dimensions, onLayout, hasDimensions} = useComponentLayout();
|
||||||
const accentColor = getVariantColor(variant);
|
const accentColor = getVariantColor(theme, variant);
|
||||||
const atStart = activeIndex <= 0;
|
const atStart = activeIndex <= 0;
|
||||||
const atEnd = activeIndex >= items.length - 1;
|
const atEnd = activeIndex >= items.length - 1;
|
||||||
|
|
||||||
@@ -171,22 +172,45 @@ export function DTGallery({
|
|||||||
style={({pressed}) => ({
|
style={({pressed}) => ({
|
||||||
opacity: isDisabled ? 0.3 : pressed ? 0.7 : 1,
|
opacity: isDisabled ? 0.3 : pressed ? 0.7 : 1,
|
||||||
})}>
|
})}>
|
||||||
<Svg width={ARROW_SIZE} height={ARROW_SIZE} viewBox={`0 0 ${ARROW_SIZE} ${ARROW_SIZE}`}>
|
{useBevels ? (
|
||||||
<Path
|
<Svg width={ARROW_SIZE} height={ARROW_SIZE} viewBox={`0 0 ${ARROW_SIZE} ${ARROW_SIZE}`}>
|
||||||
d={buildButtonBevelPath(ARROW_SIZE, ARROW_SIZE, ARROW_BEVEL, ARROW_BORDER)}
|
<Path
|
||||||
fill="transparent"
|
d={buildButtonBevelPath(ARROW_SIZE, ARROW_SIZE, ARROW_BEVEL, ARROW_BORDER)}
|
||||||
stroke={accentColor}
|
fill="transparent"
|
||||||
strokeWidth={ARROW_BORDER}
|
stroke={accentColor}
|
||||||
/>
|
strokeWidth={ARROW_BORDER}
|
||||||
<Path
|
/>
|
||||||
d={iconPath}
|
<Path
|
||||||
fill="none"
|
d={iconPath}
|
||||||
stroke={accentColor}
|
fill="none"
|
||||||
strokeWidth={2}
|
stroke={accentColor}
|
||||||
strokeLinecap="round"
|
strokeWidth={2}
|
||||||
strokeLinejoin="round"
|
strokeLinecap="round"
|
||||||
/>
|
strokeLinejoin="round"
|
||||||
</Svg>
|
/>
|
||||||
|
</Svg>
|
||||||
|
) : (
|
||||||
|
<View style={{
|
||||||
|
width: ARROW_SIZE,
|
||||||
|
height: ARROW_SIZE,
|
||||||
|
borderWidth: ARROW_BORDER,
|
||||||
|
borderColor: accentColor,
|
||||||
|
borderRadius: theme.custom.radiusSm,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
}}>
|
||||||
|
<Svg width={ARROW_SIZE * 0.6} height={ARROW_SIZE * 0.6} viewBox={`${ARROW_SIZE * 0.2} ${ARROW_SIZE * 0.2} ${ARROW_SIZE * 0.6} ${ARROW_SIZE * 0.6}`}>
|
||||||
|
<Path
|
||||||
|
d={iconPath}
|
||||||
|
fill="none"
|
||||||
|
stroke={accentColor}
|
||||||
|
strokeWidth={2}
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</Svg>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
</Pressable>
|
</Pressable>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -204,6 +228,7 @@ export function DTGallery({
|
|||||||
width: thumbnailSize,
|
width: thumbnailSize,
|
||||||
height: thumbnailSize,
|
height: thumbnailSize,
|
||||||
borderColor: isActive ? accentColor : 'transparent',
|
borderColor: isActive ? accentColor : 'transparent',
|
||||||
|
borderRadius: useBevels ? 0 : theme.custom.radiusSm,
|
||||||
},
|
},
|
||||||
isActive && styles.thumbnailActive,
|
isActive && styles.thumbnailActive,
|
||||||
]}>
|
]}>
|
||||||
@@ -235,30 +260,41 @@ export function DTGallery({
|
|||||||
if (items.length === 0) return null;
|
if (items.length === 0) return null;
|
||||||
|
|
||||||
const {width, height} = dimensions;
|
const {width, height} = dimensions;
|
||||||
|
const useBevels = theme.custom.bevelMd > 0;
|
||||||
|
|
||||||
// Outer and inner bevel paths for frame overlay
|
// Outer and inner bevel paths for frame overlay
|
||||||
const outerPath = hasDimensions
|
const outerPath = useBevels && hasDimensions
|
||||||
? buildMediaFrameBevelPath(width, height, bevelSize)
|
? buildMediaFrameBevelPath(width, height, bevelSize)
|
||||||
: '';
|
: '';
|
||||||
const innerPath = hasDimensions
|
const innerPath = useBevels && hasDimensions
|
||||||
? buildMediaFrameBevelPath(width, height, bevelSize - borderWidth, borderWidth)
|
? buildMediaFrameBevelPath(width, height, bevelSize - borderWidth, borderWidth)
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.container, style]}>
|
<View style={[styles.container, style]}>
|
||||||
{/* Main image with beveled frame */}
|
{/* Main image with beveled frame */}
|
||||||
<View style={styles.mainImageContainer} onLayout={onLayout}>
|
<View
|
||||||
{/* Background fill */}
|
style={[
|
||||||
{hasDimensions && (
|
styles.mainImageContainer,
|
||||||
|
!useBevels && {
|
||||||
|
borderWidth,
|
||||||
|
borderColor: accentColor,
|
||||||
|
borderRadius: theme.custom.radius,
|
||||||
|
overflow: 'hidden',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
onLayout={onLayout}>
|
||||||
|
{/* Background fill — beveled mode only */}
|
||||||
|
{useBevels && hasDimensions && (
|
||||||
<Svg
|
<Svg
|
||||||
style={StyleSheet.absoluteFill}
|
style={StyleSheet.absoluteFill}
|
||||||
width={width}
|
width={width}
|
||||||
height={height}
|
height={height}
|
||||||
viewBox={`0 0 ${width} ${height}`}>
|
viewBox={`0 0 ${width} ${height}`}>
|
||||||
<Path d={innerPath} fill={DTColors.surfaceVariant} />
|
<Path d={innerPath} fill={theme.colors.surfaceVariant} />
|
||||||
</Svg>
|
</Svg>
|
||||||
)}
|
)}
|
||||||
<View style={[styles.mainImageContent, {padding: borderWidth}]}>
|
<View style={[styles.mainImageContent, {padding: useBevels ? borderWidth : 0}]}>
|
||||||
<Image
|
<Image
|
||||||
source={{uri: items[activeIndex]?.uri}}
|
source={{uri: items[activeIndex]?.uri}}
|
||||||
style={styles.mainImage}
|
style={styles.mainImage}
|
||||||
@@ -266,21 +302,19 @@ export function DTGallery({
|
|||||||
resizeMode="contain"
|
resizeMode="contain"
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
{/* Frame overlay (above content) — clips image at beveled border */}
|
{/* Frame overlay — beveled mode only */}
|
||||||
{hasDimensions && (
|
{useBevels && hasDimensions && (
|
||||||
<Svg
|
<Svg
|
||||||
style={[StyleSheet.absoluteFill, styles.frameOverlay]}
|
style={[StyleSheet.absoluteFill, styles.frameOverlay]}
|
||||||
width={width}
|
width={width}
|
||||||
height={height}
|
height={height}
|
||||||
viewBox={`0 0 ${width} ${height}`}
|
viewBox={`0 0 ${width} ${height}`}
|
||||||
pointerEvents="none">
|
pointerEvents="none">
|
||||||
{/* Corner mask: fill area outside outer bevel with dark to hide overflow */}
|
|
||||||
<Path
|
<Path
|
||||||
d={`M 0 0 L ${width} 0 L ${width} ${height} L 0 ${height} Z ` + outerPath}
|
d={`M 0 0 L ${width} 0 L ${width} ${height} L 0 ${height} Z ` + outerPath}
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
fill={DTColors.dark}
|
fill={theme.colors.background}
|
||||||
/>
|
/>
|
||||||
{/* Colored border between outer and inner bevel paths */}
|
|
||||||
<Path
|
<Path
|
||||||
d={outerPath + ' ' + innerPath}
|
d={outerPath + ' ' + innerPath}
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
import {useRef, useEffect} from 'react';
|
import {useRef, useEffect} from 'react';
|
||||||
import {StyleSheet, View, ViewStyle, StyleProp, Animated} from 'react-native';
|
import {StyleSheet, View, ViewStyle, StyleProp, Animated} from 'react-native';
|
||||||
import Svg, {Polygon} from 'react-native-svg';
|
import Svg, {Polygon} from 'react-native-svg';
|
||||||
|
import {useDTTheme} from '../theme/DTThemeProvider';
|
||||||
import {type DTVariant, getVariantColor} from '../utils/variantColors';
|
import {type DTVariant, getVariantColor} from '../utils/variantColors';
|
||||||
|
|
||||||
interface DTHexagonProps {
|
interface DTHexagonProps {
|
||||||
@@ -112,7 +113,8 @@ export function DTHexagon({
|
|||||||
style,
|
style,
|
||||||
children,
|
children,
|
||||||
}: DTHexagonProps) {
|
}: DTHexagonProps) {
|
||||||
const accentColor = getVariantColor(variant, color);
|
const theme = useDTTheme();
|
||||||
|
const accentColor = getVariantColor(theme, variant, color);
|
||||||
const rotateAnim = useRef(new Animated.Value(0)).current;
|
const rotateAnim = useRef(new Animated.Value(0)).current;
|
||||||
const pulseAnim = useRef(new Animated.Value(1)).current;
|
const pulseAnim = useRef(new Animated.Value(1)).current;
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import {ReactNode, useEffect, useRef} from 'react';
|
|||||||
import {StyleSheet, View, ViewStyle, StyleProp, Animated} from 'react-native';
|
import {StyleSheet, View, ViewStyle, StyleProp, Animated} from 'react-native';
|
||||||
import {Text} from 'react-native-paper';
|
import {Text} from 'react-native-paper';
|
||||||
import Svg, {Path} from 'react-native-svg';
|
import Svg, {Path} from 'react-native-svg';
|
||||||
import {DTColors} from '../theme/colors';
|
import {useDTTheme} from '../theme/DTThemeProvider';
|
||||||
import {type DTVariant, getVariantColor} from '../utils/variantColors';
|
import {type DTVariant, getVariantColor} from '../utils/variantColors';
|
||||||
import {buildLabelBevelPath} from '../utils/bevelPaths';
|
import {buildLabelBevelPath} from '../utils/bevelPaths';
|
||||||
import {useComponentLayout} from '../utils/useComponentLayout';
|
import {useComponentLayout} from '../utils/useComponentLayout';
|
||||||
@@ -150,12 +150,14 @@ export function DTLabel({
|
|||||||
bevelSize,
|
bevelSize,
|
||||||
style,
|
style,
|
||||||
}: DTLabelProps) {
|
}: DTLabelProps) {
|
||||||
|
const theme = useDTTheme();
|
||||||
const {dimensions, onLayout, hasDimensions} = useComponentLayout();
|
const {dimensions, onLayout, hasDimensions} = useComponentLayout();
|
||||||
const scaleAnim = useRef(new Animated.Value(animated ? 0 : 1)).current;
|
const scaleAnim = useRef(new Animated.Value(animated ? 0 : 1)).current;
|
||||||
const pingAnim = useRef(new Animated.Value(1)).current;
|
const pingAnim = useRef(new Animated.Value(1)).current;
|
||||||
const backgroundColor = getVariantColor(mode);
|
const backgroundColor = getVariantColor(theme, mode);
|
||||||
const sizeConfig = sizeConfigs[size];
|
const sizeConfig = sizeConfigs[size];
|
||||||
const effectiveBevelSize = bevelSize ?? sizeConfig.bevel;
|
const effectiveBevelSize = bevelSize ?? sizeConfig.bevel;
|
||||||
|
const onPrimaryColor = theme.colors.onPrimary;
|
||||||
|
|
||||||
// Mount animation
|
// Mount animation
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -196,6 +198,7 @@ export function DTLabel({
|
|||||||
}, [showIndicator, pingAnim]);
|
}, [showIndicator, pingAnim]);
|
||||||
|
|
||||||
const {width, height} = dimensions;
|
const {width, height} = dimensions;
|
||||||
|
const useBevels = theme.custom.bevelMd > 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Animated.View
|
<Animated.View
|
||||||
@@ -203,10 +206,14 @@ export function DTLabel({
|
|||||||
styles.container,
|
styles.container,
|
||||||
fullWidth && styles.fullWidth,
|
fullWidth && styles.fullWidth,
|
||||||
{transform: [{scale: scaleAnim}]},
|
{transform: [{scale: scaleAnim}]},
|
||||||
|
!useBevels && {
|
||||||
|
backgroundColor,
|
||||||
|
borderRadius: theme.custom.radiusSm,
|
||||||
|
},
|
||||||
style,
|
style,
|
||||||
]}
|
]}
|
||||||
onLayout={onLayout}>
|
onLayout={onLayout}>
|
||||||
{hasDimensions && (
|
{useBevels && hasDimensions && (
|
||||||
<Svg
|
<Svg
|
||||||
style={StyleSheet.absoluteFill}
|
style={StyleSheet.absoluteFill}
|
||||||
width={width}
|
width={width}
|
||||||
@@ -228,7 +235,7 @@ export function DTLabel({
|
|||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
styles.primaryText,
|
styles.primaryText,
|
||||||
{fontSize: sizeConfig.fontSize},
|
{fontSize: sizeConfig.fontSize, color: onPrimaryColor},
|
||||||
]}>
|
]}>
|
||||||
{primaryText}
|
{primaryText}
|
||||||
</Text>
|
</Text>
|
||||||
@@ -236,7 +243,7 @@ export function DTLabel({
|
|||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
styles.secondaryText,
|
styles.secondaryText,
|
||||||
{fontSize: sizeConfig.fontSizeSecondary},
|
{fontSize: sizeConfig.fontSizeSecondary, color: onPrimaryColor},
|
||||||
]}>
|
]}>
|
||||||
{secondaryText}
|
{secondaryText}
|
||||||
</Text>
|
</Text>
|
||||||
@@ -251,7 +258,7 @@ export function DTLabel({
|
|||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
styles.indicatorLine,
|
styles.indicatorLine,
|
||||||
{borderColor: DTColors.dark, width: sizeConfig.indicatorWidth},
|
{borderColor: onPrimaryColor, width: sizeConfig.indicatorWidth},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
@@ -299,11 +306,9 @@ const styles = StyleSheet.create({
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
},
|
},
|
||||||
primaryText: {
|
primaryText: {
|
||||||
color: DTColors.dark,
|
|
||||||
fontWeight: '500',
|
fontWeight: '500',
|
||||||
},
|
},
|
||||||
secondaryText: {
|
secondaryText: {
|
||||||
color: DTColors.dark,
|
|
||||||
fontWeight: '800',
|
fontWeight: '800',
|
||||||
},
|
},
|
||||||
indicator: {},
|
indicator: {},
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
import {ReactNode, useRef, useEffect} from 'react';
|
import {ReactNode, useRef, useEffect} from 'react';
|
||||||
import {StyleSheet, View, ViewStyle, StyleProp, Animated} from 'react-native';
|
import {StyleSheet, View, ViewStyle, StyleProp, Animated} from 'react-native';
|
||||||
import Svg, {Path, Defs, ClipPath} from 'react-native-svg';
|
import Svg, {Path, Defs, ClipPath} from 'react-native-svg';
|
||||||
import {DTColors} from '../theme/colors';
|
import {useDTTheme} from '../theme/DTThemeProvider';
|
||||||
import {type DTVariant, getVariantColor} from '../utils/variantColors';
|
import {type DTVariant, getVariantColor} from '../utils/variantColors';
|
||||||
import {buildMediaFrameBevelPath} from '../utils/bevelPaths';
|
import {buildMediaFrameBevelPath} from '../utils/bevelPaths';
|
||||||
import {useComponentLayout} from '../utils/useComponentLayout';
|
import {useComponentLayout} from '../utils/useComponentLayout';
|
||||||
@@ -91,9 +91,10 @@ export function DTMediaFrame({
|
|||||||
style,
|
style,
|
||||||
contentStyle,
|
contentStyle,
|
||||||
}: DTMediaFrameProps) {
|
}: DTMediaFrameProps) {
|
||||||
|
const theme = useDTTheme();
|
||||||
const {dimensions, onLayout, hasDimensions} = useComponentLayout();
|
const {dimensions, onLayout, hasDimensions} = useComponentLayout();
|
||||||
const scaleAnim = useRef(new Animated.Value(animated ? 0 : 1)).current;
|
const scaleAnim = useRef(new Animated.Value(animated ? 0 : 1)).current;
|
||||||
const accentColor = getVariantColor(variant, color);
|
const accentColor = getVariantColor(theme, variant, color);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (animated) {
|
if (animated) {
|
||||||
@@ -107,13 +108,14 @@ export function DTMediaFrame({
|
|||||||
}, [animated, animationDelay, scaleAnim]);
|
}, [animated, animationDelay, scaleAnim]);
|
||||||
|
|
||||||
const {width, height} = dimensions;
|
const {width, height} = dimensions;
|
||||||
|
const useBevels = theme.custom.bevelMd > 0;
|
||||||
|
|
||||||
// Outer bevel path (full frame shape)
|
// Outer bevel path (full frame shape)
|
||||||
const outerPath = hasDimensions
|
const outerPath = useBevels && hasDimensions
|
||||||
? buildMediaFrameBevelPath(width, height, bevelSize)
|
? buildMediaFrameBevelPath(width, height, bevelSize)
|
||||||
: '';
|
: '';
|
||||||
// Inner bevel path at border inset
|
// Inner bevel path at border inset
|
||||||
const innerPath = hasDimensions
|
const innerPath = useBevels && hasDimensions
|
||||||
? buildMediaFrameBevelPath(width, height, bevelSize - borderWidth, borderWidth)
|
? buildMediaFrameBevelPath(width, height, bevelSize - borderWidth, borderWidth)
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
@@ -123,21 +125,27 @@ export function DTMediaFrame({
|
|||||||
styles.container,
|
styles.container,
|
||||||
aspectRatio ? {aspectRatio} : undefined,
|
aspectRatio ? {aspectRatio} : undefined,
|
||||||
{transform: [{scale: scaleAnim}]},
|
{transform: [{scale: scaleAnim}]},
|
||||||
|
!useBevels && {
|
||||||
|
borderWidth,
|
||||||
|
borderColor: accentColor,
|
||||||
|
borderRadius: theme.custom.radius,
|
||||||
|
overflow: 'hidden',
|
||||||
|
},
|
||||||
style,
|
style,
|
||||||
]}
|
]}
|
||||||
onLayout={onLayout}>
|
onLayout={onLayout}>
|
||||||
{/* Background fill (behind content) */}
|
{/* Background fill (behind content) — beveled mode only */}
|
||||||
{hasDimensions && (
|
{useBevels && hasDimensions && (
|
||||||
<Svg
|
<Svg
|
||||||
style={StyleSheet.absoluteFill}
|
style={StyleSheet.absoluteFill}
|
||||||
width={width}
|
width={width}
|
||||||
height={height}
|
height={height}
|
||||||
viewBox={`0 0 ${width} ${height}`}>
|
viewBox={`0 0 ${width} ${height}`}>
|
||||||
<Path d={innerPath} fill={DTColors.dark} />
|
<Path d={innerPath} fill={theme.colors.background} />
|
||||||
</Svg>
|
</Svg>
|
||||||
)}
|
)}
|
||||||
{/* Content clipped to the inner bevel shape */}
|
{/* Content */}
|
||||||
{hasDimensions ? (
|
{useBevels && hasDimensions ? (
|
||||||
<View style={[styles.content, contentStyle]}>
|
<View style={[styles.content, contentStyle]}>
|
||||||
<Svg
|
<Svg
|
||||||
width={width}
|
width={width}
|
||||||
@@ -155,7 +163,6 @@ export function DTMediaFrame({
|
|||||||
StyleSheet.absoluteFill,
|
StyleSheet.absoluteFill,
|
||||||
{
|
{
|
||||||
margin: borderWidth,
|
margin: borderWidth,
|
||||||
// Use borderRadius 0 to keep angular, overflow hidden clips rectangular part
|
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
},
|
},
|
||||||
]}>
|
]}>
|
||||||
@@ -163,30 +170,27 @@ export function DTMediaFrame({
|
|||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
) : (
|
) : (
|
||||||
<View style={[styles.content, {padding: borderWidth}, contentStyle]}>
|
<View style={[styles.content, !useBevels ? undefined : {padding: borderWidth}, contentStyle]}>
|
||||||
{children}
|
{children}
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
{/* Frame overlay (above content) — masks content at beveled corners */}
|
{/* Frame overlay (above content) — beveled mode only */}
|
||||||
{hasDimensions && (
|
{useBevels && hasDimensions && (
|
||||||
<Svg
|
<Svg
|
||||||
style={[StyleSheet.absoluteFill, styles.frameOverlay]}
|
style={[StyleSheet.absoluteFill, styles.frameOverlay]}
|
||||||
width={width}
|
width={width}
|
||||||
height={height}
|
height={height}
|
||||||
viewBox={`0 0 ${width} ${height}`}
|
viewBox={`0 0 ${width} ${height}`}
|
||||||
pointerEvents="none">
|
pointerEvents="none">
|
||||||
{/* Outer frame border */}
|
|
||||||
<Path
|
<Path
|
||||||
d={outerPath + ' ' + innerPath}
|
d={outerPath + ' ' + innerPath}
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
fill={accentColor}
|
fill={accentColor}
|
||||||
/>
|
/>
|
||||||
{/* Corner masks: fill the rectangular area outside the bevel shape with black
|
|
||||||
to hide content that overflows past the beveled corners */}
|
|
||||||
<Path
|
<Path
|
||||||
d={`M 0 0 L ${width} 0 L ${width} ${height} L 0 ${height} Z ` + outerPath}
|
d={`M 0 0 L ${width} 0 L ${width} ${height} L 0 ${height} Z ` + outerPath}
|
||||||
fillRule="evenodd"
|
fillRule="evenodd"
|
||||||
fill={DTColors.dark}
|
fill={theme.colors.background}
|
||||||
/>
|
/>
|
||||||
</Svg>
|
</Svg>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import {
|
|||||||
Animated,
|
Animated,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import {Menu, Text} from 'react-native-paper';
|
import {Menu, Text} from 'react-native-paper';
|
||||||
import {DTColors} from '../theme/colors';
|
import {useDTTheme} from '../theme/DTThemeProvider';
|
||||||
import {type DTVariant, getVariantColor} from '../utils/variantColors';
|
import {type DTVariant, getVariantColor} from '../utils/variantColors';
|
||||||
|
|
||||||
export interface DTMenuItem {
|
export interface DTMenuItem {
|
||||||
@@ -163,7 +163,8 @@ export function DTMenuDropdown({
|
|||||||
onDismiss,
|
onDismiss,
|
||||||
style,
|
style,
|
||||||
}: DTMenuDropdownProps) {
|
}: DTMenuDropdownProps) {
|
||||||
const accentColor = getVariantColor(variant);
|
const theme = useDTTheme();
|
||||||
|
const accentColor = getVariantColor(theme, variant);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Menu
|
<Menu
|
||||||
@@ -173,12 +174,12 @@ export function DTMenuDropdown({
|
|||||||
anchorPosition="bottom"
|
anchorPosition="bottom"
|
||||||
contentStyle={[
|
contentStyle={[
|
||||||
styles.dropdownContent,
|
styles.dropdownContent,
|
||||||
{borderColor: accentColor},
|
{borderColor: accentColor, backgroundColor: theme.colors.background},
|
||||||
style,
|
style,
|
||||||
]}
|
]}
|
||||||
theme={{
|
theme={{
|
||||||
colors: {
|
colors: {
|
||||||
elevation: {level2: DTColors.dark},
|
elevation: {level2: theme.colors.background},
|
||||||
},
|
},
|
||||||
}}>
|
}}>
|
||||||
{items.map(item => (
|
{items.map(item => (
|
||||||
@@ -189,7 +190,7 @@ export function DTMenuDropdown({
|
|||||||
item.onPress?.();
|
item.onPress?.();
|
||||||
onDismiss();
|
onDismiss();
|
||||||
}}
|
}}
|
||||||
titleStyle={[styles.dropdownItemTitle, {color: DTColors.light}]}
|
titleStyle={[styles.dropdownItemTitle, {color: theme.colors.onSurface}]}
|
||||||
style={styles.dropdownItem}
|
style={styles.dropdownItem}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
@@ -210,10 +211,11 @@ function DTMenuItemRow({
|
|||||||
activeVariant: DTVariant;
|
activeVariant: DTVariant;
|
||||||
level: number;
|
level: number;
|
||||||
}) {
|
}) {
|
||||||
|
const theme = useDTTheme();
|
||||||
const [expanded, setExpanded] = useState(false);
|
const [expanded, setExpanded] = useState(false);
|
||||||
const hasChildren = item.items && item.items.length > 0;
|
const hasChildren = item.items && item.items.length > 0;
|
||||||
const isActive = item.isActive || expanded;
|
const isActive = item.isActive || expanded;
|
||||||
const itemColor = getVariantColor(isActive ? activeVariant : variant);
|
const itemColor = getVariantColor(theme, isActive ? activeVariant : variant);
|
||||||
|
|
||||||
const heightAnim = useRef(new Animated.Value(0)).current;
|
const heightAnim = useRef(new Animated.Value(0)).current;
|
||||||
const chevronAnim = useRef(new Animated.Value(0)).current;
|
const chevronAnim = useRef(new Animated.Value(0)).current;
|
||||||
@@ -265,7 +267,7 @@ function DTMenuItemRow({
|
|||||||
borderTopColor: itemColor,
|
borderTopColor: itemColor,
|
||||||
backgroundColor: isActive
|
backgroundColor: isActive
|
||||||
? `${itemColor}20`
|
? `${itemColor}20`
|
||||||
: DTColors.dark,
|
: theme.colors.background,
|
||||||
paddingLeft: 16 + level * 16,
|
paddingLeft: 16 + level * 16,
|
||||||
opacity: pressed ? 0.7 : 1,
|
opacity: pressed ? 0.7 : 1,
|
||||||
},
|
},
|
||||||
@@ -360,7 +362,6 @@ const styles = StyleSheet.create({
|
|||||||
dropdownContent: {
|
dropdownContent: {
|
||||||
borderWidth: 2,
|
borderWidth: 2,
|
||||||
borderRadius: 0,
|
borderRadius: 0,
|
||||||
backgroundColor: DTColors.dark,
|
|
||||||
},
|
},
|
||||||
dropdownItem: {
|
dropdownItem: {
|
||||||
borderRadius: 0,
|
borderRadius: 0,
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
import {StyleSheet, ViewStyle, StyleProp, KeyboardAvoidingView, Platform} from 'react-native';
|
import {StyleSheet, ViewStyle, StyleProp, KeyboardAvoidingView, Platform} from 'react-native';
|
||||||
import {Portal, Modal} from 'react-native-paper';
|
import {Portal, Modal} from 'react-native-paper';
|
||||||
import {DTColors} from '../theme/colors';
|
import {useDTTheme} from '../theme/DTThemeProvider';
|
||||||
import {type DTVariant} from '../utils/variantColors';
|
import {type DTVariant} from '../utils/variantColors';
|
||||||
import {DTCard} from './DTCard';
|
import {DTCard} from './DTCard';
|
||||||
|
|
||||||
@@ -75,6 +75,8 @@ export function DTModal({
|
|||||||
contentStyle,
|
contentStyle,
|
||||||
style,
|
style,
|
||||||
}: DTModalProps) {
|
}: DTModalProps) {
|
||||||
|
const theme = useDTTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Portal>
|
<Portal>
|
||||||
<Modal
|
<Modal
|
||||||
@@ -85,7 +87,7 @@ export function DTModal({
|
|||||||
style={styles.modal}
|
style={styles.modal}
|
||||||
theme={{
|
theme={{
|
||||||
colors: {
|
colors: {
|
||||||
backdrop: DTColors.overlay,
|
backdrop: theme.colors.backdrop,
|
||||||
},
|
},
|
||||||
}}>
|
}}>
|
||||||
<KeyboardAvoidingView
|
<KeyboardAvoidingView
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
import {StyleSheet, View, ViewStyle, StyleProp} from 'react-native';
|
import {StyleSheet, View, ViewStyle, StyleProp} from 'react-native';
|
||||||
import {ProgressBar, Text} from 'react-native-paper';
|
import {ProgressBar, Text} from 'react-native-paper';
|
||||||
import {DTColors} from '../theme/colors';
|
import {useDTTheme} from '../theme/DTThemeProvider';
|
||||||
import {type DTVariant, getVariantColor} from '../utils/variantColors';
|
import {type DTVariant, getVariantColor} from '../utils/variantColors';
|
||||||
import {useComponentLayout} from '../utils/useComponentLayout';
|
import {useComponentLayout} from '../utils/useComponentLayout';
|
||||||
|
|
||||||
@@ -77,7 +77,8 @@ export function DTProgressBar({
|
|||||||
color,
|
color,
|
||||||
style,
|
style,
|
||||||
}: DTProgressBarProps) {
|
}: DTProgressBarProps) {
|
||||||
const accentColor = getVariantColor(variant, color);
|
const theme = useDTTheme();
|
||||||
|
const accentColor = getVariantColor(theme, variant, color);
|
||||||
const clampedValue = Math.max(0, Math.min(1, value));
|
const clampedValue = Math.max(0, Math.min(1, value));
|
||||||
|
|
||||||
if (orientation === 'vertical') {
|
if (orientation === 'vertical') {
|
||||||
@@ -85,6 +86,7 @@ export function DTProgressBar({
|
|||||||
<VerticalProgressBar
|
<VerticalProgressBar
|
||||||
value={clampedValue}
|
value={clampedValue}
|
||||||
color={accentColor}
|
color={accentColor}
|
||||||
|
trackColor={theme.colors.surfaceDisabled}
|
||||||
width={height}
|
width={height}
|
||||||
showLabel={showLabel}
|
showLabel={showLabel}
|
||||||
style={style}
|
style={style}
|
||||||
@@ -100,7 +102,7 @@ export function DTProgressBar({
|
|||||||
style={[styles.bar, {height}]}
|
style={[styles.bar, {height}]}
|
||||||
theme={{
|
theme={{
|
||||||
colors: {
|
colors: {
|
||||||
surfaceVariant: DTColors.disabledBackground,
|
surfaceVariant: theme.colors.surfaceDisabled,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -120,12 +122,14 @@ export function DTProgressBar({
|
|||||||
function VerticalProgressBar({
|
function VerticalProgressBar({
|
||||||
value,
|
value,
|
||||||
color,
|
color,
|
||||||
|
trackColor,
|
||||||
width,
|
width,
|
||||||
showLabel,
|
showLabel,
|
||||||
style,
|
style,
|
||||||
}: {
|
}: {
|
||||||
value: number;
|
value: number;
|
||||||
color: string;
|
color: string;
|
||||||
|
trackColor: string;
|
||||||
width: number;
|
width: number;
|
||||||
showLabel: boolean;
|
showLabel: boolean;
|
||||||
style?: StyleProp<ViewStyle>;
|
style?: StyleProp<ViewStyle>;
|
||||||
@@ -135,7 +139,7 @@ function VerticalProgressBar({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.verticalContainer, {width}, style]}>
|
<View style={[styles.verticalContainer, {width}, style]}>
|
||||||
<View style={styles.verticalTrack} onLayout={onLayout}>
|
<View style={[styles.verticalTrack, {backgroundColor: trackColor}]} onLayout={onLayout}>
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
styles.verticalFill,
|
styles.verticalFill,
|
||||||
@@ -169,7 +173,6 @@ const styles = StyleSheet.create({
|
|||||||
verticalTrack: {
|
verticalTrack: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
width: '100%',
|
width: '100%',
|
||||||
backgroundColor: DTColors.disabledBackground,
|
|
||||||
justifyContent: 'flex-end',
|
justifyContent: 'flex-end',
|
||||||
},
|
},
|
||||||
verticalFill: {
|
verticalFill: {
|
||||||
|
|||||||
@@ -9,10 +9,10 @@
|
|||||||
* - Quantity display between buttons
|
* - Quantity display between buttons
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {StyleSheet, View, ViewStyle, StyleProp, Pressable} from 'react-native';
|
import {StyleSheet, View, ViewStyle, StyleProp, Pressable, Text as RNText} from 'react-native';
|
||||||
import {Text} from 'react-native-paper';
|
import {Text} from 'react-native-paper';
|
||||||
import Svg, {Path} from 'react-native-svg';
|
import Svg, {Path} from 'react-native-svg';
|
||||||
import {DTColors} from '../theme/colors';
|
import {useDTTheme} from '../theme/DTThemeProvider';
|
||||||
import {type DTVariant, getVariantColor} from '../utils/variantColors';
|
import {type DTVariant, getVariantColor} from '../utils/variantColors';
|
||||||
import {buildButtonBevelPath} from '../utils/bevelPaths';
|
import {buildButtonBevelPath} from '../utils/bevelPaths';
|
||||||
|
|
||||||
@@ -124,7 +124,9 @@ export function DTQuantityStepper({
|
|||||||
color,
|
color,
|
||||||
style,
|
style,
|
||||||
}: DTQuantityStepperProps) {
|
}: DTQuantityStepperProps) {
|
||||||
const accentColor = getVariantColor(variant, color);
|
const theme = useDTTheme();
|
||||||
|
const useBevels = theme.custom.bevelMd > 0;
|
||||||
|
const accentColor = getVariantColor(theme, variant, color);
|
||||||
const config = sizeConfigs[size];
|
const config = sizeConfigs[size];
|
||||||
const atMin = value <= min;
|
const atMin = value <= min;
|
||||||
const atMax = value >= max;
|
const atMax = value >= max;
|
||||||
@@ -168,21 +170,37 @@ export function DTQuantityStepper({
|
|||||||
style={({pressed}) => ({
|
style={({pressed}) => ({
|
||||||
opacity: disabled || isDisabled ? 0.3 : pressed ? 0.7 : 1,
|
opacity: disabled || isDisabled ? 0.3 : pressed ? 0.7 : 1,
|
||||||
})}>
|
})}>
|
||||||
<Svg width={s} height={s} viewBox={`0 0 ${s} ${s}`}>
|
{useBevels ? (
|
||||||
<Path
|
<Svg width={s} height={s} viewBox={`0 0 ${s} ${s}`}>
|
||||||
d={buildButtonBevelPath(s, s, config.bevelSize, borderWidth)}
|
<Path
|
||||||
fill="transparent"
|
d={buildButtonBevelPath(s, s, config.bevelSize, borderWidth)}
|
||||||
stroke={accentColor}
|
fill="transparent"
|
||||||
strokeWidth={borderWidth}
|
stroke={accentColor}
|
||||||
/>
|
strokeWidth={borderWidth}
|
||||||
<Path
|
/>
|
||||||
d={iconPath}
|
<Path
|
||||||
fill="none"
|
d={iconPath}
|
||||||
stroke={accentColor}
|
fill="none"
|
||||||
strokeWidth={sw}
|
stroke={accentColor}
|
||||||
strokeLinecap="round"
|
strokeWidth={sw}
|
||||||
/>
|
strokeLinecap="round"
|
||||||
</Svg>
|
/>
|
||||||
|
</Svg>
|
||||||
|
) : (
|
||||||
|
<View style={{
|
||||||
|
width: s,
|
||||||
|
height: s,
|
||||||
|
borderWidth,
|
||||||
|
borderColor: accentColor,
|
||||||
|
borderRadius: theme.custom.radiusSm,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
}}>
|
||||||
|
<RNText style={{color: accentColor, fontSize: s * 0.5, lineHeight: s * 0.6}}>
|
||||||
|
{type === 'minus' ? '−' : '+'}
|
||||||
|
</RNText>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
</Pressable>
|
</Pressable>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -190,7 +208,7 @@ export function DTQuantityStepper({
|
|||||||
return (
|
return (
|
||||||
<View style={[styles.wrapper, style]}>
|
<View style={[styles.wrapper, style]}>
|
||||||
{label && (
|
{label && (
|
||||||
<Text style={[styles.label, {color: DTColors.light}]}>{label}</Text>
|
<Text style={[styles.label, {color: theme.colors.onSurface}]}>{label}</Text>
|
||||||
)}
|
)}
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
{renderStepButton('minus', handleDecrement, atMin)}
|
{renderStepButton('minus', handleDecrement, atMin)}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import {createContext, useContext} from 'react';
|
|||||||
import {StyleSheet, View, ViewStyle, StyleProp, Pressable} from 'react-native';
|
import {StyleSheet, View, ViewStyle, StyleProp, Pressable} from 'react-native';
|
||||||
import {Text} from 'react-native-paper';
|
import {Text} from 'react-native-paper';
|
||||||
import Svg, {Polygon} from 'react-native-svg';
|
import Svg, {Polygon} from 'react-native-svg';
|
||||||
import {DTColors, getColor} from '../theme/colors';
|
import {useDTTheme} from '../theme/DTThemeProvider';
|
||||||
import {type DTVariant, getVariantColor} from '../utils/variantColors';
|
import {type DTVariant, getVariantColor} from '../utils/variantColors';
|
||||||
|
|
||||||
// --- Context ---
|
// --- Context ---
|
||||||
@@ -115,6 +115,15 @@ function hexPoints(cx: number, cy: number, r: number): string {
|
|||||||
return pts.join(' ');
|
return pts.join(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Convert hex color to rgba with given alpha */
|
||||||
|
function hexToRgba(hex: string, alpha: number): string {
|
||||||
|
const clean = hex.replace('#', '');
|
||||||
|
const r = parseInt(clean.substring(0, 2), 16);
|
||||||
|
const g = parseInt(clean.substring(2, 4), 16);
|
||||||
|
const b = parseInt(clean.substring(4, 6), 16);
|
||||||
|
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DT-styled RadioOption with hexagonal indicator
|
* DT-styled RadioOption with hexagonal indicator
|
||||||
*
|
*
|
||||||
@@ -130,6 +139,7 @@ export function DTRadioOption({
|
|||||||
disabled = false,
|
disabled = false,
|
||||||
style,
|
style,
|
||||||
}: DTRadioOptionProps) {
|
}: DTRadioOptionProps) {
|
||||||
|
const theme = useDTTheme();
|
||||||
const context = useContext(RadioContext);
|
const context = useContext(RadioContext);
|
||||||
if (!context) {
|
if (!context) {
|
||||||
throw new Error('DTRadioOption must be used inside DTRadioGroup');
|
throw new Error('DTRadioOption must be used inside DTRadioGroup');
|
||||||
@@ -137,14 +147,9 @@ export function DTRadioOption({
|
|||||||
|
|
||||||
const {value: selectedValue, onValueChange, variant} = context;
|
const {value: selectedValue, onValueChange, variant} = context;
|
||||||
const isSelected = selectedValue === value;
|
const isSelected = selectedValue === value;
|
||||||
const accentColor = getVariantColor(variant);
|
const accentColor = getVariantColor(theme, variant);
|
||||||
const selectedBgColor = getColor(
|
const selectedBgColor = hexToRgba(accentColor, 0.7);
|
||||||
variant === 'normal'
|
const unselectedBorderColor = hexToRgba(theme.custom.modeNormal, 0.3);
|
||||||
? 'modeNormalSelected'
|
|
||||||
: variant === 'emphasis'
|
|
||||||
? 'modeEmphasisSelected'
|
|
||||||
: 'modeNormal',
|
|
||||||
);
|
|
||||||
const opacity = disabled ? 0.5 : 1;
|
const opacity = disabled ? 0.5 : 1;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -153,7 +158,7 @@ export function DTRadioOption({
|
|||||||
style={({pressed}) => [
|
style={({pressed}) => [
|
||||||
styles.option,
|
styles.option,
|
||||||
{
|
{
|
||||||
borderColor: isSelected ? accentColor : getColor('modeNormal', 0.3),
|
borderColor: isSelected ? accentColor : unselectedBorderColor,
|
||||||
backgroundColor: isSelected ? selectedBgColor : 'transparent',
|
backgroundColor: isSelected ? selectedBgColor : 'transparent',
|
||||||
opacity: pressed ? 0.7 : opacity,
|
opacity: pressed ? 0.7 : opacity,
|
||||||
},
|
},
|
||||||
@@ -183,7 +188,7 @@ export function DTRadioOption({
|
|||||||
<Text
|
<Text
|
||||||
style={[
|
style={[
|
||||||
styles.optionLabel,
|
styles.optionLabel,
|
||||||
{color: isSelected ? DTColors.dark : DTColors.light},
|
{color: isSelected ? theme.colors.onPrimary : theme.colors.onSurface},
|
||||||
]}>
|
]}>
|
||||||
{label}
|
{label}
|
||||||
</Text>
|
</Text>
|
||||||
@@ -192,7 +197,7 @@ export function DTRadioOption({
|
|||||||
variant="bodySmall"
|
variant="bodySmall"
|
||||||
style={[
|
style={[
|
||||||
styles.optionDescription,
|
styles.optionDescription,
|
||||||
{color: isSelected ? DTColors.dark : DTColors.disabled},
|
{color: isSelected ? theme.colors.onPrimary : theme.colors.onSurfaceDisabled},
|
||||||
]}>
|
]}>
|
||||||
{description}
|
{description}
|
||||||
</Text>
|
</Text>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
import {useState, useRef, useEffect} from 'react';
|
import {useState, useRef, useEffect} from 'react';
|
||||||
import {StyleSheet, View, ViewStyle, TextStyle, StyleProp, Animated} from 'react-native';
|
import {StyleSheet, View, ViewStyle, TextStyle, StyleProp, Animated} from 'react-native';
|
||||||
import {Searchbar, ActivityIndicator} from 'react-native-paper';
|
import {Searchbar, ActivityIndicator} from 'react-native-paper';
|
||||||
import {DTColors} from '../theme/colors';
|
import {useDTTheme} from '../theme/DTThemeProvider';
|
||||||
import {type DTVariant, getVariantColor} from '../utils/variantColors';
|
import {type DTVariant, getVariantColor} from '../utils/variantColors';
|
||||||
|
|
||||||
interface DTSearchInputProps {
|
interface DTSearchInputProps {
|
||||||
@@ -86,7 +86,8 @@ export function DTSearchInput({
|
|||||||
style,
|
style,
|
||||||
inputStyle,
|
inputStyle,
|
||||||
}: DTSearchInputProps) {
|
}: DTSearchInputProps) {
|
||||||
const accentColor = getVariantColor(variant, color);
|
const theme = useDTTheme();
|
||||||
|
const accentColor = getVariantColor(theme, variant, color);
|
||||||
const [focused, setFocused] = useState(false);
|
const [focused, setFocused] = useState(false);
|
||||||
const focusAnim = useRef(new Animated.Value(0)).current;
|
const focusAnim = useRef(new Animated.Value(0)).current;
|
||||||
|
|
||||||
@@ -110,7 +111,7 @@ export function DTSearchInput({
|
|||||||
onBlur={() => setFocused(false)}
|
onBlur={() => setFocused(false)}
|
||||||
returnKeyType="search"
|
returnKeyType="search"
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
placeholderTextColor={DTColors.disabled}
|
placeholderTextColor={theme.colors.onSurfaceDisabled}
|
||||||
editable={!disabled}
|
editable={!disabled}
|
||||||
autoFocus={autoFocus}
|
autoFocus={autoFocus}
|
||||||
icon={
|
icon={
|
||||||
@@ -119,13 +120,13 @@ export function DTSearchInput({
|
|||||||
: 'magnify'
|
: 'magnify'
|
||||||
}
|
}
|
||||||
iconColor={accentColor}
|
iconColor={accentColor}
|
||||||
inputStyle={[styles.input, {color: DTColors.light}, inputStyle]}
|
inputStyle={[styles.input, {color: theme.colors.onSurface}, inputStyle]}
|
||||||
style={[styles.searchbar, {borderColor: accentColor}]}
|
style={[styles.searchbar, {borderColor: accentColor, backgroundColor: theme.colors.background}]}
|
||||||
rippleColor={accentColor}
|
rippleColor={accentColor}
|
||||||
theme={{
|
theme={{
|
||||||
colors: {
|
colors: {
|
||||||
elevation: {level3: DTColors.dark},
|
elevation: {level3: theme.colors.background},
|
||||||
onSurface: DTColors.light,
|
onSurface: theme.colors.onSurface,
|
||||||
onSurfaceVariant: accentColor,
|
onSurfaceVariant: accentColor,
|
||||||
},
|
},
|
||||||
roundness: 0,
|
roundness: 0,
|
||||||
@@ -149,7 +150,6 @@ const styles = StyleSheet.create({
|
|||||||
container: {},
|
container: {},
|
||||||
searchbar: {
|
searchbar: {
|
||||||
borderWidth: 2,
|
borderWidth: 2,
|
||||||
backgroundColor: DTColors.dark,
|
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
},
|
},
|
||||||
input: {
|
input: {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
Animated,
|
Animated,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import {Text} from 'react-native-paper';
|
import {Text} from 'react-native-paper';
|
||||||
import {DTColors} from '../theme/colors';
|
import {useDTTheme} from '../theme/DTThemeProvider';
|
||||||
import {type DTVariant, getVariantColor} from '../utils/variantColors';
|
import {type DTVariant, getVariantColor} from '../utils/variantColors';
|
||||||
|
|
||||||
const TRACK_WIDTH = 48;
|
const TRACK_WIDTH = 48;
|
||||||
@@ -73,7 +73,8 @@ export function DTSwitch({
|
|||||||
color,
|
color,
|
||||||
style,
|
style,
|
||||||
}: DTSwitchProps) {
|
}: DTSwitchProps) {
|
||||||
const accentColor = getVariantColor(variant, color);
|
const theme = useDTTheme();
|
||||||
|
const accentColor = getVariantColor(theme, variant, color);
|
||||||
const thumbAnim = useRef(new Animated.Value(value ? 1 : 0)).current;
|
const thumbAnim = useRef(new Animated.Value(value ? 1 : 0)).current;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -91,10 +92,9 @@ export function DTSwitch({
|
|||||||
|
|
||||||
const trackColor = thumbAnim.interpolate({
|
const trackColor = thumbAnim.interpolate({
|
||||||
inputRange: [0, 1],
|
inputRange: [0, 1],
|
||||||
outputRange: [DTColors.disabledBackground, accentColor],
|
outputRange: [theme.colors.surfaceDisabled, accentColor],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Pressable
|
<Pressable
|
||||||
onPress={() => !disabled && onValueChange(!value)}
|
onPress={() => !disabled && onValueChange(!value)}
|
||||||
@@ -104,7 +104,7 @@ export function DTSwitch({
|
|||||||
style,
|
style,
|
||||||
]}>
|
]}>
|
||||||
{label && (
|
{label && (
|
||||||
<Text style={[styles.label, {color: DTColors.light}]}>{label}</Text>
|
<Text style={[styles.label, {color: theme.colors.onSurface}]}>{label}</Text>
|
||||||
)}
|
)}
|
||||||
<Animated.View
|
<Animated.View
|
||||||
style={[
|
style={[
|
||||||
@@ -118,7 +118,7 @@ export function DTSwitch({
|
|||||||
style={[
|
style={[
|
||||||
styles.thumb,
|
styles.thumb,
|
||||||
{
|
{
|
||||||
backgroundColor: value ? DTColors.dark : DTColors.light,
|
backgroundColor: value ? theme.colors.onPrimary : theme.colors.onSurface,
|
||||||
transform: [{translateX: thumbTranslateX}],
|
transform: [{translateX: thumbTranslateX}],
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import {
|
|||||||
Animated,
|
Animated,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import {TextInput, TextInputProps} from 'react-native-paper';
|
import {TextInput, TextInputProps} from 'react-native-paper';
|
||||||
import {DTColors} from '../theme/colors';
|
import {useDTTheme} from '../theme/DTThemeProvider';
|
||||||
import {type DTVariant, getVariantColor} from '../utils/variantColors';
|
import {type DTVariant, getVariantColor} from '../utils/variantColors';
|
||||||
|
|
||||||
interface DTTextInputProps
|
interface DTTextInputProps
|
||||||
@@ -79,12 +79,13 @@ export function DTTextInput({
|
|||||||
style,
|
style,
|
||||||
...props
|
...props
|
||||||
}: DTTextInputProps) {
|
}: DTTextInputProps) {
|
||||||
|
const theme = useDTTheme();
|
||||||
const [focused, setFocused] = useState(false);
|
const [focused, setFocused] = useState(false);
|
||||||
const focusAnim = useRef(new Animated.Value(0)).current;
|
const focusAnim = useRef(new Animated.Value(0)).current;
|
||||||
|
|
||||||
const accentColor = error
|
const accentColor = error
|
||||||
? DTColors.modeWarning
|
? theme.custom.modeWarning
|
||||||
: getVariantColor(variant, color);
|
: getVariantColor(theme, variant, color);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const anim = Animated.timing(focusAnim, {
|
const anim = Animated.timing(focusAnim, {
|
||||||
@@ -101,7 +102,7 @@ export function DTTextInput({
|
|||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
styles.inputWrapper,
|
styles.inputWrapper,
|
||||||
{borderColor: accentColor, borderWidth},
|
{borderColor: accentColor, borderWidth, backgroundColor: theme.colors.background},
|
||||||
]}>
|
]}>
|
||||||
<TextInput
|
<TextInput
|
||||||
{...props}
|
{...props}
|
||||||
@@ -116,8 +117,8 @@ export function DTTextInput({
|
|||||||
onBlur?.(e);
|
onBlur?.(e);
|
||||||
}}
|
}}
|
||||||
style={[styles.input, inputStyle, style]}
|
style={[styles.input, inputStyle, style]}
|
||||||
textColor={DTColors.light}
|
textColor={theme.colors.onSurface}
|
||||||
placeholderTextColor={DTColors.disabled}
|
placeholderTextColor={theme.colors.onSurfaceDisabled}
|
||||||
activeUnderlineColor="transparent"
|
activeUnderlineColor="transparent"
|
||||||
underlineColor="transparent"
|
underlineColor="transparent"
|
||||||
theme={{
|
theme={{
|
||||||
@@ -141,7 +142,7 @@ export function DTTextInput({
|
|||||||
/>
|
/>
|
||||||
{error && errorMessage && (
|
{error && errorMessage && (
|
||||||
<View style={styles.errorContainer}>
|
<View style={styles.errorContainer}>
|
||||||
<Animated.Text style={[styles.errorText, {color: DTColors.modeWarning}]}>
|
<Animated.Text style={[styles.errorText, {color: theme.custom.modeWarning}]}>
|
||||||
{errorMessage}
|
{errorMessage}
|
||||||
</Animated.Text>
|
</Animated.Text>
|
||||||
</View>
|
</View>
|
||||||
@@ -151,9 +152,7 @@ export function DTTextInput({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
inputWrapper: {
|
inputWrapper: {},
|
||||||
backgroundColor: DTColors.dark,
|
|
||||||
},
|
|
||||||
input: {
|
input: {
|
||||||
backgroundColor: 'transparent',
|
backgroundColor: 'transparent',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export { DTThemeProvider, useDTTheme } from './theme/DTThemeProvider';
|
|||||||
|
|
||||||
// Shared utilities
|
// Shared utilities
|
||||||
export type { DTVariant } from './utils/variantColors';
|
export type { DTVariant } from './utils/variantColors';
|
||||||
export { variantColorMap, getVariantColor } from './utils/variantColors';
|
export { getVariantColor } from './utils/variantColors';
|
||||||
export { buildBeveledRectPath } from './utils/bevelPaths';
|
export { buildBeveledRectPath } from './utils/bevelPaths';
|
||||||
export { useComponentLayout } from './utils/useComponentLayout';
|
export { useComponentLayout } from './utils/useComponentLayout';
|
||||||
|
|
||||||
|
|||||||
@@ -124,6 +124,14 @@ export interface DTExtendedTheme extends MD3Theme {
|
|||||||
modeOther: string;
|
modeOther: string;
|
||||||
border: string;
|
border: string;
|
||||||
borderEmphasis: string;
|
borderEmphasis: string;
|
||||||
|
/** Bevel sizes in pixels (0 = no bevels, use borderRadius instead) */
|
||||||
|
bevelSm: number;
|
||||||
|
bevelMd: number;
|
||||||
|
bevelLg: number;
|
||||||
|
/** Border radius in pixels (0 = angular/beveled) */
|
||||||
|
radiusSm: number;
|
||||||
|
radius: number;
|
||||||
|
radiusLg: number;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,6 +147,12 @@ export const DTExtendedDarkTheme: DTExtendedTheme = {
|
|||||||
modeOther: DTColors.modeOther,
|
modeOther: DTColors.modeOther,
|
||||||
border: DTColors.border,
|
border: DTColors.border,
|
||||||
borderEmphasis: DTColors.borderEmphasis,
|
borderEmphasis: DTColors.borderEmphasis,
|
||||||
|
bevelSm: 16,
|
||||||
|
bevelMd: 32,
|
||||||
|
bevelLg: 40,
|
||||||
|
radiusSm: 0,
|
||||||
|
radius: 0,
|
||||||
|
radiusLg: 0,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,26 +2,31 @@
|
|||||||
* Centralized variant type and color mapping
|
* Centralized variant type and color mapping
|
||||||
*
|
*
|
||||||
* Used by all DT components for consistent mode/variant color resolution.
|
* Used by all DT components for consistent mode/variant color resolution.
|
||||||
|
* Reads colors from the active DTExtendedTheme rather than static defaults.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {DTColors} from '../theme/colors';
|
import type {DTExtendedTheme} from '../theme/paperTheme';
|
||||||
|
|
||||||
export type DTVariant = 'normal' | 'emphasis' | 'warning' | 'success' | 'other';
|
export type DTVariant = 'normal' | 'emphasis' | 'warning' | 'success' | 'other';
|
||||||
|
|
||||||
export const variantColorMap: Record<DTVariant, string> = {
|
/** String-typed keys in DTExtendedTheme['custom'] that map to mode colors */
|
||||||
normal: DTColors.modeNormal,
|
type ModeColorKey = 'modeNormal' | 'modeEmphasis' | 'modeWarning' | 'modeSuccess' | 'modeOther';
|
||||||
emphasis: DTColors.modeEmphasis,
|
|
||||||
warning: DTColors.modeWarning,
|
const variantToThemeKey: Record<DTVariant, ModeColorKey> = {
|
||||||
success: DTColors.modeSuccess,
|
normal: 'modeNormal',
|
||||||
other: DTColors.modeOther,
|
emphasis: 'modeEmphasis',
|
||||||
|
warning: 'modeWarning',
|
||||||
|
success: 'modeSuccess',
|
||||||
|
other: 'modeOther',
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve variant to color, with optional custom color override.
|
* Resolve variant to color from the active theme, with optional custom color override.
|
||||||
*/
|
*/
|
||||||
export function getVariantColor(
|
export function getVariantColor(
|
||||||
|
theme: DTExtendedTheme,
|
||||||
variant: DTVariant,
|
variant: DTVariant,
|
||||||
customColor?: string,
|
customColor?: string,
|
||||||
): string {
|
): string {
|
||||||
return customColor ?? variantColorMap[variant];
|
return customColor ?? theme.custom[variantToThemeKey[variant]];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,34 @@ config.resolver.nodeModulesPaths = [
|
|||||||
path.resolve(monorepoRoot, 'node_modules'),
|
path.resolve(monorepoRoot, 'node_modules'),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Force singleton packages to always resolve from the mobile project.
|
||||||
|
// In monorepos, Metro can bundle two copies of react (one from root, one from
|
||||||
|
// the project) causing "Cannot read property 'use' of null" crashes.
|
||||||
|
const singletons = ['react', 'react-native', 'react-native-safe-area-context'];
|
||||||
|
|
||||||
|
config.resolver.resolveRequest = (context, moduleName, platform) => {
|
||||||
|
// Check if this is a singleton or a deep import of one (e.g. react/jsx-runtime)
|
||||||
|
const isSingleton = singletons.some(
|
||||||
|
(s) => moduleName === s || moduleName.startsWith(s + '/')
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isSingleton) {
|
||||||
|
// Resolve as if the import originated from the mobile project root
|
||||||
|
const mobileContext = {
|
||||||
|
...context,
|
||||||
|
resolveRequest: undefined,
|
||||||
|
originModulePath: path.join(projectRoot, 'package.json'),
|
||||||
|
};
|
||||||
|
return context.resolveRequest(mobileContext, moduleName, platform);
|
||||||
|
}
|
||||||
|
|
||||||
|
return context.resolveRequest(
|
||||||
|
{ ...context, resolveRequest: undefined },
|
||||||
|
moduleName,
|
||||||
|
platform,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
// Block sibling packages' node_modules to prevent duplicate native modules
|
// Block sibling packages' node_modules to prevent duplicate native modules
|
||||||
const packagesDir = path.join(monorepoRoot, 'packages');
|
const packagesDir = path.join(monorepoRoot, 'packages');
|
||||||
const escapedPath = packagesDir.replace(/[/\\]/g, '[/\\\\]');
|
const escapedPath = packagesDir.replace(/[/\\]/g, '[/\\\\]');
|
||||||
|
|||||||
@@ -1,7 +1,35 @@
|
|||||||
import { MD3DarkTheme } from 'react-native-paper';
|
import { Platform } from 'react-native';
|
||||||
|
import { MD3DarkTheme, configureFonts } from 'react-native-paper';
|
||||||
import type { BrandTokens } from '@dangerousthings/tokens';
|
import type { BrandTokens } from '@dangerousthings/tokens';
|
||||||
import type { DTExtendedTheme } from '@dangerousthings/react-native';
|
import type { DTExtendedTheme } from '@dangerousthings/react-native';
|
||||||
|
|
||||||
|
/** Parse CSS size token ("1rem", "0.25rem", "0px", "0") to pixels (1rem = 16px) */
|
||||||
|
function parseSize(value: string): number {
|
||||||
|
if (value === '0' || value === '0px') return 0;
|
||||||
|
const num = parseFloat(value);
|
||||||
|
if (value.endsWith('rem')) return num * 16;
|
||||||
|
if (value.endsWith('px')) return num;
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Build RNP font config from brand typography tokens */
|
||||||
|
function buildFonts(brand: BrandTokens) {
|
||||||
|
const usesTektur = brand.typography.heading.includes('Tektur');
|
||||||
|
if (!usesTektur) {
|
||||||
|
// System sans-serif — use RNP defaults
|
||||||
|
return MD3DarkTheme.fonts;
|
||||||
|
}
|
||||||
|
// Tektur custom font
|
||||||
|
const fontFamily = Platform.select({
|
||||||
|
ios: 'Tektur',
|
||||||
|
android: 'Tektur-Regular',
|
||||||
|
default: 'Tektur',
|
||||||
|
})!;
|
||||||
|
return configureFonts({
|
||||||
|
config: { fontFamily },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function buildThemeFromBrand(brand: BrandTokens): DTExtendedTheme {
|
export function buildThemeFromBrand(brand: BrandTokens): DTExtendedTheme {
|
||||||
const colors = brand.dark;
|
const colors = brand.dark;
|
||||||
|
|
||||||
@@ -9,7 +37,7 @@ export function buildThemeFromBrand(brand: BrandTokens): DTExtendedTheme {
|
|||||||
...MD3DarkTheme,
|
...MD3DarkTheme,
|
||||||
dark: true,
|
dark: true,
|
||||||
roundness: brand.shape.radiusSm === '0' || brand.shape.radiusSm === '0px' ? 0 : 4,
|
roundness: brand.shape.radiusSm === '0' || brand.shape.radiusSm === '0px' ? 0 : 4,
|
||||||
fonts: MD3DarkTheme.fonts,
|
fonts: buildFonts(brand),
|
||||||
colors: {
|
colors: {
|
||||||
...MD3DarkTheme.colors,
|
...MD3DarkTheme.colors,
|
||||||
primary: colors.primary,
|
primary: colors.primary,
|
||||||
@@ -63,6 +91,12 @@ export function buildThemeFromBrand(brand: BrandTokens): DTExtendedTheme {
|
|||||||
modeOther: colors.other,
|
modeOther: colors.other,
|
||||||
border: colors.border,
|
border: colors.border,
|
||||||
borderEmphasis: colors.secondary,
|
borderEmphasis: colors.secondary,
|
||||||
|
bevelSm: parseSize(brand.shape.bevelSm),
|
||||||
|
bevelMd: parseSize(brand.shape.bevelMd),
|
||||||
|
bevelLg: parseSize(brand.shape.bevelLg),
|
||||||
|
radiusSm: parseSize(brand.shape.radiusSm),
|
||||||
|
radius: parseSize(brand.shape.radius),
|
||||||
|
radiusLg: parseSize(brand.shape.radiusLg),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user