initial commit
This commit is contained in:
151
packages/react-native/src/components/DTButton.tsx
Normal file
151
packages/react-native/src/components/DTButton.tsx
Normal file
@@ -0,0 +1,151 @@
|
||||
/**
|
||||
* DT Button Component
|
||||
*
|
||||
* A themed button following the Dangerous Things design language
|
||||
* with SVG-based beveled corners.
|
||||
*/
|
||||
|
||||
import {StyleSheet, View, ViewStyle, StyleProp, Pressable} from 'react-native';
|
||||
import {Text} from 'react-native-paper';
|
||||
import Svg, {Path} from 'react-native-svg';
|
||||
import {DTColors} from '../theme/colors';
|
||||
import {type DTVariant, getVariantColor} from '../utils/variantColors';
|
||||
import {buildButtonBevelPath} from '../utils/bevelPaths';
|
||||
import {useComponentLayout} from '../utils/useComponentLayout';
|
||||
import {useState} from 'react';
|
||||
|
||||
interface DTButtonProps {
|
||||
/**
|
||||
* Button content/label
|
||||
*/
|
||||
children: React.ReactNode;
|
||||
/**
|
||||
* Visual variant of the button
|
||||
* @default 'normal'
|
||||
*/
|
||||
variant?: DTVariant;
|
||||
/**
|
||||
* Button display mode
|
||||
* - 'outlined': Border with transparent background (default)
|
||||
* - 'contained': Filled background
|
||||
* @default 'outlined'
|
||||
*/
|
||||
mode?: 'outlined' | 'contained';
|
||||
/**
|
||||
* Custom color (overrides variant)
|
||||
*/
|
||||
color?: string;
|
||||
/**
|
||||
* Press handler
|
||||
*/
|
||||
onPress?: () => void;
|
||||
/**
|
||||
* Whether button is disabled
|
||||
*/
|
||||
disabled?: boolean;
|
||||
/**
|
||||
* Additional styles for container
|
||||
*/
|
||||
style?: StyleProp<ViewStyle>;
|
||||
/**
|
||||
* Border width
|
||||
* @default 2
|
||||
*/
|
||||
borderWidth?: number;
|
||||
/**
|
||||
* Bevel size in pixels
|
||||
* @default 8
|
||||
*/
|
||||
bevelSize?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* DT-styled Button component with SVG beveled corners
|
||||
*
|
||||
* @example
|
||||
* <DTButton variant="normal" onPress={handleScan}>
|
||||
* Scan NFC Tag
|
||||
* </DTButton>
|
||||
*
|
||||
* @example
|
||||
* <DTButton variant="emphasis" mode="contained" onPress={handleAction}>
|
||||
* View Results
|
||||
* </DTButton>
|
||||
*/
|
||||
export function DTButton({
|
||||
children,
|
||||
variant = 'normal',
|
||||
mode = 'outlined',
|
||||
color,
|
||||
onPress,
|
||||
disabled = false,
|
||||
style,
|
||||
borderWidth = 2,
|
||||
bevelSize = 8,
|
||||
}: DTButtonProps) {
|
||||
const {dimensions, onLayout, hasDimensions} = useComponentLayout();
|
||||
const [pressed, setPressed] = useState(false);
|
||||
|
||||
const accentColor = getVariantColor(variant, color);
|
||||
const isContained = mode === 'contained';
|
||||
const bgColor = isContained ? accentColor : 'transparent';
|
||||
const textColor = isContained ? DTColors.dark : accentColor;
|
||||
|
||||
const {width, height} = dimensions;
|
||||
const opacity = disabled ? 0.5 : pressed ? 0.7 : 1;
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
onPress={onPress}
|
||||
disabled={disabled}
|
||||
onPressIn={() => setPressed(true)}
|
||||
onPressOut={() => setPressed(false)}
|
||||
style={[{opacity}, style]}>
|
||||
<View style={styles.container} onLayout={onLayout}>
|
||||
{hasDimensions && (
|
||||
<Svg
|
||||
style={StyleSheet.absoluteFill}
|
||||
width={width}
|
||||
height={height}
|
||||
viewBox={`0 0 ${width} ${height}`}>
|
||||
<Path
|
||||
d={buildButtonBevelPath(width, height, bevelSize, borderWidth)}
|
||||
fill={bgColor}
|
||||
stroke={accentColor}
|
||||
strokeWidth={borderWidth}
|
||||
/>
|
||||
</Svg>
|
||||
)}
|
||||
<View style={styles.content}>
|
||||
{typeof children === 'string' ? (
|
||||
<Text variant="labelLarge" style={[styles.label, {color: textColor}]}>
|
||||
{children}
|
||||
</Text>
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</Pressable>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
position: 'relative',
|
||||
minHeight: 44,
|
||||
},
|
||||
content: {
|
||||
position: 'relative',
|
||||
zIndex: 1,
|
||||
paddingVertical: 12,
|
||||
paddingHorizontal: 24,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
label: {
|
||||
fontWeight: '600',
|
||||
letterSpacing: 1,
|
||||
textTransform: 'uppercase',
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user