feat(release): prepare 1.1.0 candidate
This commit is contained in:
parent
5a34f66981
commit
5205dcdfa9
736 changed files with 115667 additions and 12203 deletions
|
|
@ -2,7 +2,8 @@
|
|||
// RN FilterChip — pill-shaped toggle chip for filter bars
|
||||
|
||||
import { Pressable, type ViewStyle, type StyleProp } from 'react-native'
|
||||
import { d3roNativePalette, d3roNativeRadius } from '../theme'
|
||||
import { d3roNativeRadius } from '../theme'
|
||||
import { useNativePalette } from '../theme-context'
|
||||
import { PhosphorText } from './PhosphorText'
|
||||
|
||||
export interface FilterChipProps {
|
||||
|
|
@ -18,14 +19,21 @@ export function FilterChip({
|
|||
onPress,
|
||||
style
|
||||
}: FilterChipProps): React.ReactElement {
|
||||
const palette = useNativePalette()
|
||||
return (
|
||||
<Pressable
|
||||
accessibilityRole="button"
|
||||
accessibilityState={{ selected: active }}
|
||||
accessibilityLabel={label}
|
||||
onPress={onPress}
|
||||
style={[
|
||||
{
|
||||
backgroundColor: active ? d3roNativePalette.accent.main : d3roNativePalette.bg.card,
|
||||
minHeight: 48,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: active ? palette.accent.main : palette.bg.card,
|
||||
borderWidth: 1,
|
||||
borderColor: active ? d3roNativePalette.accent.main : d3roNativePalette.border.default,
|
||||
borderColor: active ? palette.accent.main : palette.border.default,
|
||||
borderRadius: d3roNativeRadius.pill,
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 6
|
||||
|
|
@ -36,7 +44,7 @@ export function FilterChip({
|
|||
<PhosphorText
|
||||
variant="small"
|
||||
color={active ? 'primary' : 'muted'}
|
||||
style={active ? { color: d3roNativePalette.bg.app } : undefined}
|
||||
style={active ? { color: palette.text.onAccent ?? palette.bg.app } : undefined}
|
||||
>
|
||||
{label}
|
||||
</PhosphorText>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
import { View, type ViewStyle, type StyleProp } from 'react-native'
|
||||
import { Led } from './Led'
|
||||
import { PhosphorText } from './PhosphorText'
|
||||
import { d3roNativePalette } from '../theme'
|
||||
import { useNativePalette } from '../theme-context'
|
||||
|
||||
export interface HeaderProps {
|
||||
title: string
|
||||
|
|
@ -22,6 +22,7 @@ export function Header({
|
|||
paddingTop = 0,
|
||||
style
|
||||
}: HeaderProps): React.ReactElement {
|
||||
const palette = useNativePalette()
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
|
|
@ -33,14 +34,18 @@ export function Header({
|
|||
paddingTop: paddingTop + 8,
|
||||
paddingBottom: 12,
|
||||
borderBottomWidth: showBorder ? 1 : 0,
|
||||
borderBottomColor: d3roNativePalette.border.subtle
|
||||
borderBottomColor: palette.border.subtle
|
||||
},
|
||||
style
|
||||
]}
|
||||
>
|
||||
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
|
||||
<Led color="amber" size={8} />
|
||||
<PhosphorText variant="label" color="muted" style={{ letterSpacing: 3 }}>
|
||||
<PhosphorText
|
||||
accessibilityRole="header"
|
||||
variant="label"
|
||||
color="muted"
|
||||
>
|
||||
{title}
|
||||
</PhosphorText>
|
||||
</View>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// RN용 Led — 작은 발광 원
|
||||
|
||||
import { View, type ViewStyle } from 'react-native'
|
||||
import { d3roNativePalette } from '../theme'
|
||||
import { useNativePalette } from '../theme-context'
|
||||
|
||||
export interface LedProps {
|
||||
color?: 'amber' | 'green' | 'red' | 'orange' | 'blue'
|
||||
|
|
@ -10,21 +10,21 @@ export interface LedProps {
|
|||
size?: number
|
||||
}
|
||||
|
||||
const COLOR_MAP: Record<string, string> = {
|
||||
amber: d3roNativePalette.accent.main,
|
||||
green: d3roNativePalette.tag.green,
|
||||
red: d3roNativePalette.tag.red,
|
||||
orange: d3roNativePalette.tag.orange,
|
||||
blue: d3roNativePalette.tag.blue
|
||||
}
|
||||
|
||||
export function Led({ color = 'amber', on = true, size = 8 }: LedProps): React.ReactElement {
|
||||
const c = COLOR_MAP[color] ?? d3roNativePalette.accent.main
|
||||
const palette = useNativePalette()
|
||||
const colorMap: Record<NonNullable<LedProps['color']>, string> = {
|
||||
amber: palette.accent.main,
|
||||
green: palette.tag.green,
|
||||
red: palette.tag.red,
|
||||
orange: palette.tag.orange,
|
||||
blue: palette.tag.blue
|
||||
}
|
||||
const c = colorMap[color]
|
||||
const style: ViewStyle = {
|
||||
width: size,
|
||||
height: size,
|
||||
borderRadius: size / 2,
|
||||
backgroundColor: on ? c : d3roNativePalette.led.off,
|
||||
backgroundColor: on ? c : palette.led.off,
|
||||
shadowColor: c,
|
||||
shadowOffset: { width: 0, height: 0 },
|
||||
shadowOpacity: on ? 0.8 : 0,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
// RN용 MetalCard — View + 섀시 스타일
|
||||
|
||||
import { View, type ViewProps, type ViewStyle, type StyleProp } from 'react-native'
|
||||
import { d3roNativePalette, d3roNativeRadius } from '../theme'
|
||||
import { d3roNativeRadius } from '../theme'
|
||||
import { useNativePalette } from '../theme-context'
|
||||
|
||||
export interface MetalCardProps extends ViewProps {
|
||||
inset?: boolean
|
||||
|
|
@ -10,15 +11,16 @@ export interface MetalCardProps extends ViewProps {
|
|||
}
|
||||
|
||||
export function MetalCard({ children, inset = false, style, ...rest }: MetalCardProps): React.ReactElement {
|
||||
const palette = useNativePalette()
|
||||
const baseStyle: ViewStyle = {
|
||||
backgroundColor: inset ? d3roNativePalette.bg.inset : d3roNativePalette.bg.card,
|
||||
backgroundColor: inset ? palette.bg.inset : palette.bg.card,
|
||||
borderRadius: inset ? d3roNativeRadius.inner : d3roNativeRadius.card,
|
||||
padding: inset ? 6 : 16,
|
||||
borderWidth: inset ? 0 : 1,
|
||||
borderColor: d3roNativePalette.border.subtle,
|
||||
borderColor: palette.border.subtle,
|
||||
overflow: 'hidden',
|
||||
// RN 그림자
|
||||
shadowColor: '#000',
|
||||
shadowColor: palette.shadow ?? '#000',
|
||||
shadowOffset: { width: 0, height: 4 },
|
||||
shadowOpacity: inset ? 0 : 0.25,
|
||||
shadowRadius: inset ? 0 : 8,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
// packages/ui-native/src/components/PhosphorText.tsx
|
||||
// RN용 PhosphorText — Text + 앰버 glow (textShadowColor)
|
||||
// RN용 PhosphorText — v3: 시스템 산세리프 기반 (한글은 Apple SD Gothic Neo /
|
||||
// Noto Sans KR 자동 폴백). 텍스트 글로우는 제거하고 명도로 위계를 만든다.
|
||||
|
||||
import { Text, Platform, type TextProps, type TextStyle, type StyleProp } from 'react-native'
|
||||
import { d3roNativePalette, d3roNativeTypo, type D3roNativeTypoKey } from '../theme'
|
||||
import { Text, type TextProps, type TextStyle, type StyleProp } from 'react-native'
|
||||
import { d3roNativeFonts, d3roNativeTypo, type D3roNativeTypoKey } from '../theme'
|
||||
import { useNativePalette } from '../theme-context'
|
||||
|
||||
export type PhosphorVariant = D3roNativeTypoKey
|
||||
|
||||
|
|
@ -19,29 +21,23 @@ export function PhosphorText({
|
|||
style,
|
||||
...rest
|
||||
}: PhosphorTextProps): React.ReactElement {
|
||||
const palette = useNativePalette()
|
||||
const typo = d3roNativeTypo[variant]
|
||||
const isAmberVariant = variant === 'hero' || variant === 'title' || variant === 'value'
|
||||
|
||||
const resolvedColor: string = (() => {
|
||||
if (color === 'amber') return d3roNativePalette.accent.main
|
||||
if (color === 'primary') return d3roNativePalette.text.primary
|
||||
if (color === 'secondary') return d3roNativePalette.text.secondary
|
||||
if (color === 'label') return d3roNativePalette.text.label
|
||||
if (color === 'muted') return d3roNativePalette.text.muted
|
||||
return isAmberVariant ? d3roNativePalette.accent.main : d3roNativePalette.text.primary
|
||||
if (color === 'amber') return palette.accent.main
|
||||
if (color === 'primary') return palette.text.primary
|
||||
if (color === 'secondary') return palette.text.secondary
|
||||
if (color === 'label') return palette.text.label
|
||||
if (color === 'muted') return palette.text.muted
|
||||
return isAmberVariant ? palette.accent.main : palette.text.primary
|
||||
})()
|
||||
|
||||
const baseStyle: TextStyle = {
|
||||
...typo,
|
||||
color: resolvedColor,
|
||||
fontFamily: Platform.OS === 'ios' ? 'Menlo' : 'monospace',
|
||||
...(isAmberVariant
|
||||
? {
|
||||
textShadowColor: d3roNativePalette.accent.glow,
|
||||
textShadowOffset: { width: 0, height: 0 },
|
||||
textShadowRadius: 6
|
||||
}
|
||||
: {})
|
||||
fontFamily: d3roNativeFonts.sans
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@ import {
|
|||
type TextStyle,
|
||||
type StyleProp
|
||||
} from 'react-native'
|
||||
import { d3roNativePalette, d3roNativeRadius, d3roNativeTypo } from '../theme'
|
||||
import { d3roNativeRadius, d3roNativeTypo } from '../theme'
|
||||
import { useNativePalette } from '../theme-context'
|
||||
|
||||
export interface PhysicalButtonProps extends Omit<PressableProps, 'children' | 'style'> {
|
||||
label: string
|
||||
|
|
@ -27,28 +28,30 @@ export function PhysicalButton({
|
|||
onPress,
|
||||
...rest
|
||||
}: PhysicalButtonProps): React.ReactElement {
|
||||
const palette = useNativePalette()
|
||||
const [pressed, setPressed] = useState(false)
|
||||
|
||||
const bgColor =
|
||||
variant === 'primary'
|
||||
? d3roNativePalette.accent.main
|
||||
? palette.accent.main
|
||||
: variant === 'danger'
|
||||
? d3roNativePalette.tag.red
|
||||
? palette.tag.red
|
||||
: 'transparent'
|
||||
|
||||
const borderColor =
|
||||
variant === 'secondary' ? d3roNativePalette.border.strong : 'transparent'
|
||||
variant === 'secondary' ? palette.border.strong : 'transparent'
|
||||
|
||||
const textColor =
|
||||
variant === 'primary' || variant === 'danger'
|
||||
? d3roNativePalette.text.primary
|
||||
: d3roNativePalette.text.secondary
|
||||
? palette.text.onAccent ?? palette.text.primary
|
||||
: palette.text.secondary
|
||||
|
||||
const containerStyle: ViewStyle = {
|
||||
backgroundColor: bgColor,
|
||||
borderColor,
|
||||
borderWidth: variant === 'secondary' ? 1 : 0,
|
||||
borderRadius: d3roNativeRadius.button,
|
||||
minHeight: 48,
|
||||
paddingVertical: 14,
|
||||
paddingHorizontal: 24,
|
||||
alignItems: 'center',
|
||||
|
|
@ -59,19 +62,22 @@ export function PhysicalButton({
|
|||
|
||||
const textStyle: TextStyle = {
|
||||
...d3roNativeTypo.heading,
|
||||
color: textColor,
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: 1.5
|
||||
color: textColor
|
||||
}
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
{...rest}
|
||||
accessibilityRole={rest.accessibilityRole ?? 'button'}
|
||||
accessibilityState={{
|
||||
...rest.accessibilityState,
|
||||
disabled,
|
||||
}}
|
||||
onPress={onPress}
|
||||
onPressIn={() => setPressed(true)}
|
||||
onPressOut={() => setPressed(false)}
|
||||
disabled={disabled}
|
||||
style={[containerStyle, style]}
|
||||
{...rest}
|
||||
>
|
||||
<Text style={textStyle}>{label}</Text>
|
||||
</Pressable>
|
||||
|
|
|
|||
|
|
@ -2,15 +2,17 @@
|
|||
// RN ScreenPanel — inset bg + dot grid background
|
||||
|
||||
import { View, type ViewProps, type ViewStyle, type StyleProp } from 'react-native'
|
||||
import { d3roNativePalette, d3roNativeRadius } from '../theme'
|
||||
import { d3roNativeRadius } from '../theme'
|
||||
import { useNativePalette } from '../theme-context'
|
||||
|
||||
export interface ScreenPanelProps extends ViewProps {
|
||||
style?: StyleProp<ViewStyle>
|
||||
}
|
||||
|
||||
export function ScreenPanel({ children, style, ...rest }: ScreenPanelProps): React.ReactElement {
|
||||
const palette = useNativePalette()
|
||||
const baseStyle: ViewStyle = {
|
||||
backgroundColor: d3roNativePalette.bg.inset,
|
||||
backgroundColor: palette.bg.inset,
|
||||
borderRadius: d3roNativeRadius.inner,
|
||||
padding: 20,
|
||||
overflow: 'hidden'
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
// RN StatusBar — bottom status strip with LED + model label
|
||||
|
||||
import { View, type ViewStyle, type StyleProp } from 'react-native'
|
||||
import { d3roNativePalette, d3roNativeFonts } from '../theme'
|
||||
import { d3roNativeFonts } from '../theme'
|
||||
import { useNativePalette } from '../theme-context'
|
||||
import { Led } from './Led'
|
||||
import { PhosphorText } from './PhosphorText'
|
||||
|
||||
|
|
@ -17,6 +18,7 @@ export function AppStatusBar({
|
|||
label = 'PRECISION DATA LINK',
|
||||
style
|
||||
}: AppStatusBarProps): React.ReactElement {
|
||||
const palette = useNativePalette()
|
||||
const containerStyle: ViewStyle = {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
|
|
@ -33,15 +35,15 @@ export function AppStatusBar({
|
|||
<PhosphorText
|
||||
variant="label"
|
||||
color="muted"
|
||||
style={{ fontFamily: d3roNativeFonts.mono, fontSize: 8, letterSpacing: 1.5 }}
|
||||
style={{ fontFamily: d3roNativeFonts.mono, fontSize: 10, letterSpacing: 0.6 }}
|
||||
>
|
||||
{model}
|
||||
</PhosphorText>
|
||||
<View style={{ width: 1, height: 8, backgroundColor: d3roNativePalette.border.default }} />
|
||||
<View style={{ width: 1, height: 8, backgroundColor: palette.border.default }} />
|
||||
<PhosphorText
|
||||
variant="label"
|
||||
color="muted"
|
||||
style={{ fontFamily: d3roNativeFonts.mono, fontSize: 8, letterSpacing: 1.5 }}
|
||||
style={{ fontFamily: d3roNativeFonts.mono, fontSize: 10, letterSpacing: 0.6 }}
|
||||
>
|
||||
{label}
|
||||
</PhosphorText>
|
||||
|
|
|
|||
|
|
@ -1,13 +1,21 @@
|
|||
// packages/ui-native/src/components/WaveBars.tsx
|
||||
// RN WaveBars — animated audio level bars using core Animated API
|
||||
|
||||
import { useEffect, useRef } from 'react'
|
||||
import { View, Animated, Easing, type ViewStyle, type StyleProp } from 'react-native'
|
||||
import { d3roNativePalette } from '../theme'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import {
|
||||
AccessibilityInfo,
|
||||
View,
|
||||
Animated,
|
||||
Easing,
|
||||
type ViewStyle,
|
||||
type StyleProp,
|
||||
} from 'react-native'
|
||||
import { useNativePalette } from '../theme-context'
|
||||
|
||||
export interface WaveBarsProps {
|
||||
active?: boolean
|
||||
barCount?: number
|
||||
reduceMotion?: boolean
|
||||
style?: StyleProp<ViewStyle>
|
||||
}
|
||||
|
||||
|
|
@ -15,7 +23,15 @@ const BAR_MIN = 8
|
|||
const BAR_MAX = 32
|
||||
const DURATIONS = [800, 1000, 900, 1200, 1100, 950, 1050, 1300, 850, 1150, 1000]
|
||||
|
||||
function WaveBar({ index, active }: { index: number; active: boolean }): React.ReactElement {
|
||||
function WaveBar({
|
||||
index,
|
||||
active,
|
||||
color,
|
||||
}: {
|
||||
index: number
|
||||
active: boolean
|
||||
color: string
|
||||
}): React.ReactElement {
|
||||
const height = useRef(new Animated.Value(BAR_MIN)).current
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -41,11 +57,8 @@ function WaveBar({ index, active }: { index: number; active: boolean }): React.R
|
|||
animation.start()
|
||||
return () => animation.stop()
|
||||
} else {
|
||||
Animated.timing(height, {
|
||||
toValue: BAR_MIN,
|
||||
duration: 300,
|
||||
useNativeDriver: false
|
||||
}).start()
|
||||
height.stopAnimation()
|
||||
height.setValue(BAR_MIN)
|
||||
}
|
||||
}, [active, height, index])
|
||||
|
||||
|
|
@ -54,7 +67,7 @@ function WaveBar({ index, active }: { index: number; active: boolean }): React.R
|
|||
style={{
|
||||
width: 6,
|
||||
height,
|
||||
backgroundColor: d3roNativePalette.accent.main,
|
||||
backgroundColor: color,
|
||||
borderRadius: 3
|
||||
}}
|
||||
/>
|
||||
|
|
@ -64,23 +77,54 @@ function WaveBar({ index, active }: { index: number; active: boolean }): React.R
|
|||
export function WaveBars({
|
||||
active = false,
|
||||
barCount = 11,
|
||||
reduceMotion,
|
||||
style
|
||||
}: WaveBarsProps): React.ReactElement {
|
||||
const palette = useNativePalette()
|
||||
// Fail closed: do not animate until the asynchronous OS preference is known.
|
||||
const [systemReduceMotion, setSystemReduceMotion] = useState(
|
||||
reduceMotion === undefined,
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (reduceMotion !== undefined) return undefined
|
||||
let mounted = true
|
||||
void AccessibilityInfo.isReduceMotionEnabled()
|
||||
.then((enabled) => {
|
||||
if (mounted) setSystemReduceMotion(enabled)
|
||||
})
|
||||
.catch(() => undefined)
|
||||
const subscription = AccessibilityInfo.addEventListener(
|
||||
'reduceMotionChanged',
|
||||
setSystemReduceMotion,
|
||||
)
|
||||
return () => {
|
||||
mounted = false
|
||||
subscription.remove()
|
||||
}
|
||||
}, [reduceMotion])
|
||||
|
||||
const motionDisabled = reduceMotion ?? systemReduceMotion
|
||||
const containerStyle: ViewStyle = {
|
||||
height: 128,
|
||||
backgroundColor: d3roNativePalette.bg.inset,
|
||||
backgroundColor: palette.bg.inset,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: 6,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: d3roNativePalette.border.subtle
|
||||
borderBottomColor: palette.border.subtle
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={[containerStyle, style]}>
|
||||
{Array.from({ length: barCount }).map((_, i) => (
|
||||
<WaveBar key={i} index={i} active={active} />
|
||||
<WaveBar
|
||||
key={i}
|
||||
index={i}
|
||||
active={active && !motionDisabled}
|
||||
color={palette.accent.main}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue