melta-app 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +197 -0
- package/THIRD_PARTY_LICENSES.md +198 -0
- package/lib/module/components/ActionSheet.js +87 -0
- package/lib/module/components/ActionSheet.js.map +1 -0
- package/lib/module/components/Alert.js +93 -0
- package/lib/module/components/Alert.js.map +1 -0
- package/lib/module/components/Avatar.js +93 -0
- package/lib/module/components/Avatar.js.map +1 -0
- package/lib/module/components/BottomSheet.js +69 -0
- package/lib/module/components/BottomSheet.js.map +1 -0
- package/lib/module/components/Card.js +93 -0
- package/lib/module/components/Card.js.map +1 -0
- package/lib/module/components/Checkbox.js +115 -0
- package/lib/module/components/Checkbox.js.map +1 -0
- package/lib/module/components/EmptyState.js +70 -0
- package/lib/module/components/EmptyState.js.map +1 -0
- package/lib/module/components/Header.js +53 -0
- package/lib/module/components/Header.js.map +1 -0
- package/lib/module/components/Image.js +67 -0
- package/lib/module/components/Image.js.map +1 -0
- package/lib/module/components/Modal.js +95 -0
- package/lib/module/components/Modal.js.map +1 -0
- package/lib/module/components/Progress.js +113 -0
- package/lib/module/components/Progress.js.map +1 -0
- package/lib/module/components/Radio.js +134 -0
- package/lib/module/components/Radio.js.map +1 -0
- package/lib/module/components/Screen.js +57 -0
- package/lib/module/components/Screen.js.map +1 -0
- package/lib/module/components/Skeleton.js +169 -0
- package/lib/module/components/Skeleton.js.map +1 -0
- package/lib/module/components/Surface.js +49 -0
- package/lib/module/components/Surface.js.map +1 -0
- package/lib/module/components/TextField.js +84 -0
- package/lib/module/components/TextField.js.map +1 -0
- package/lib/module/components/Toast.js +105 -0
- package/lib/module/components/Toast.js.map +1 -0
- package/lib/module/components/Toggle.js +77 -0
- package/lib/module/components/Toggle.js.map +1 -0
- package/lib/module/components/action-sheet.styles.js +70 -0
- package/lib/module/components/action-sheet.styles.js.map +1 -0
- package/lib/module/components/alert.styles.js +80 -0
- package/lib/module/components/alert.styles.js.map +1 -0
- package/lib/module/components/avatar.styles.js +94 -0
- package/lib/module/components/avatar.styles.js.map +1 -0
- package/lib/module/components/bottom-sheet.styles.js +55 -0
- package/lib/module/components/bottom-sheet.styles.js.map +1 -0
- package/lib/module/components/card.styles.js +50 -0
- package/lib/module/components/card.styles.js.map +1 -0
- package/lib/module/components/checkbox.styles.js +55 -0
- package/lib/module/components/checkbox.styles.js.map +1 -0
- package/lib/module/components/empty-state.styles.js +60 -0
- package/lib/module/components/empty-state.styles.js.map +1 -0
- package/lib/module/components/header.styles.js +33 -0
- package/lib/module/components/header.styles.js.map +1 -0
- package/lib/module/components/image.styles.js +37 -0
- package/lib/module/components/image.styles.js.map +1 -0
- package/lib/module/components/index.js +26 -0
- package/lib/module/components/index.js.map +1 -0
- package/lib/module/components/modal.styles.js +68 -0
- package/lib/module/components/modal.styles.js.map +1 -0
- package/lib/module/components/progress.styles.js +55 -0
- package/lib/module/components/progress.styles.js.map +1 -0
- package/lib/module/components/radio.styles.js +96 -0
- package/lib/module/components/radio.styles.js.map +1 -0
- package/lib/module/components/screen.styles.js +38 -0
- package/lib/module/components/screen.styles.js.map +1 -0
- package/lib/module/components/skeleton.styles.js +114 -0
- package/lib/module/components/skeleton.styles.js.map +1 -0
- package/lib/module/components/surface.styles.js +37 -0
- package/lib/module/components/surface.styles.js.map +1 -0
- package/lib/module/components/textfield.styles.js +121 -0
- package/lib/module/components/textfield.styles.js.map +1 -0
- package/lib/module/components/toast.styles.js +81 -0
- package/lib/module/components/toast.styles.js.map +1 -0
- package/lib/module/components/toggle.styles.js +69 -0
- package/lib/module/components/toggle.styles.js.map +1 -0
- package/lib/module/contracts/contract-types.js +192 -0
- package/lib/module/contracts/contract-types.js.map +1 -0
- package/lib/module/icons/Icon.js +57 -0
- package/lib/module/icons/Icon.js.map +1 -0
- package/lib/module/icons/glyphs.js +257 -0
- package/lib/module/icons/glyphs.js.map +1 -0
- package/lib/module/icons/icon.styles.js +28 -0
- package/lib/module/icons/icon.styles.js.map +1 -0
- package/lib/module/icons/index.js +14 -0
- package/lib/module/icons/index.js.map +1 -0
- package/lib/module/index.js +20 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/primitives/Button.js +105 -0
- package/lib/module/primitives/Button.js.map +1 -0
- package/lib/module/primitives/Metric.js +68 -0
- package/lib/module/primitives/Metric.js.map +1 -0
- package/lib/module/primitives/Row.js +43 -0
- package/lib/module/primitives/Row.js.map +1 -0
- package/lib/module/primitives/Stack.js +39 -0
- package/lib/module/primitives/Stack.js.map +1 -0
- package/lib/module/primitives/Tag.js +160 -0
- package/lib/module/primitives/Tag.js.map +1 -0
- package/lib/module/primitives/Text.js +54 -0
- package/lib/module/primitives/Text.js.map +1 -0
- package/lib/module/primitives/_internal/focus-ring.js +57 -0
- package/lib/module/primitives/_internal/focus-ring.js.map +1 -0
- package/lib/module/primitives/button.styles.js +88 -0
- package/lib/module/primitives/button.styles.js.map +1 -0
- package/lib/module/primitives/index.js +11 -0
- package/lib/module/primitives/index.js.map +1 -0
- package/lib/module/primitives/metric.styles.js +54 -0
- package/lib/module/primitives/metric.styles.js.map +1 -0
- package/lib/module/primitives/row.styles.js +53 -0
- package/lib/module/primitives/row.styles.js.map +1 -0
- package/lib/module/primitives/stack.styles.js +37 -0
- package/lib/module/primitives/stack.styles.js.map +1 -0
- package/lib/module/primitives/tag.styles.js +56 -0
- package/lib/module/primitives/tag.styles.js.map +1 -0
- package/lib/module/primitives/text.styles.js +33 -0
- package/lib/module/primitives/text.styles.js.map +1 -0
- package/lib/module/theme/ThemeProvider.js +41 -0
- package/lib/module/theme/ThemeProvider.js.map +1 -0
- package/lib/module/theme/index.js +19 -0
- package/lib/module/theme/index.js.map +1 -0
- package/lib/module/theme/native-theme.js +201 -0
- package/lib/module/theme/native-theme.js.map +1 -0
- package/lib/module/theme/types.js +4 -0
- package/lib/module/theme/types.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/components/ActionSheet.d.ts +45 -0
- package/lib/typescript/src/components/ActionSheet.d.ts.map +1 -0
- package/lib/typescript/src/components/Alert.d.ts +43 -0
- package/lib/typescript/src/components/Alert.d.ts.map +1 -0
- package/lib/typescript/src/components/Avatar.d.ts +45 -0
- package/lib/typescript/src/components/Avatar.d.ts.map +1 -0
- package/lib/typescript/src/components/BottomSheet.d.ts +36 -0
- package/lib/typescript/src/components/BottomSheet.d.ts.map +1 -0
- package/lib/typescript/src/components/Card.d.ts +46 -0
- package/lib/typescript/src/components/Card.d.ts.map +1 -0
- package/lib/typescript/src/components/Checkbox.d.ts +40 -0
- package/lib/typescript/src/components/Checkbox.d.ts.map +1 -0
- package/lib/typescript/src/components/EmptyState.d.ts +36 -0
- package/lib/typescript/src/components/EmptyState.d.ts.map +1 -0
- package/lib/typescript/src/components/Header.d.ts +34 -0
- package/lib/typescript/src/components/Header.d.ts.map +1 -0
- package/lib/typescript/src/components/Image.d.ts +41 -0
- package/lib/typescript/src/components/Image.d.ts.map +1 -0
- package/lib/typescript/src/components/Modal.d.ts +46 -0
- package/lib/typescript/src/components/Modal.d.ts.map +1 -0
- package/lib/typescript/src/components/Progress.d.ts +39 -0
- package/lib/typescript/src/components/Progress.d.ts.map +1 -0
- package/lib/typescript/src/components/Radio.d.ts +52 -0
- package/lib/typescript/src/components/Radio.d.ts.map +1 -0
- package/lib/typescript/src/components/Screen.d.ts +42 -0
- package/lib/typescript/src/components/Screen.d.ts.map +1 -0
- package/lib/typescript/src/components/Skeleton.d.ts +37 -0
- package/lib/typescript/src/components/Skeleton.d.ts.map +1 -0
- package/lib/typescript/src/components/Surface.d.ts +31 -0
- package/lib/typescript/src/components/Surface.d.ts.map +1 -0
- package/lib/typescript/src/components/TextField.d.ts +45 -0
- package/lib/typescript/src/components/TextField.d.ts.map +1 -0
- package/lib/typescript/src/components/Toast.d.ts +45 -0
- package/lib/typescript/src/components/Toast.d.ts.map +1 -0
- package/lib/typescript/src/components/Toggle.d.ts +39 -0
- package/lib/typescript/src/components/Toggle.d.ts.map +1 -0
- package/lib/typescript/src/components/action-sheet.styles.d.ts +65 -0
- package/lib/typescript/src/components/action-sheet.styles.d.ts.map +1 -0
- package/lib/typescript/src/components/alert.styles.d.ts +61 -0
- package/lib/typescript/src/components/alert.styles.d.ts.map +1 -0
- package/lib/typescript/src/components/avatar.styles.d.ts +60 -0
- package/lib/typescript/src/components/avatar.styles.d.ts.map +1 -0
- package/lib/typescript/src/components/bottom-sheet.styles.d.ts +50 -0
- package/lib/typescript/src/components/bottom-sheet.styles.d.ts.map +1 -0
- package/lib/typescript/src/components/card.styles.d.ts +27 -0
- package/lib/typescript/src/components/card.styles.d.ts.map +1 -0
- package/lib/typescript/src/components/checkbox.styles.d.ts +55 -0
- package/lib/typescript/src/components/checkbox.styles.d.ts.map +1 -0
- package/lib/typescript/src/components/empty-state.styles.d.ts +58 -0
- package/lib/typescript/src/components/empty-state.styles.d.ts.map +1 -0
- package/lib/typescript/src/components/header.styles.d.ts +30 -0
- package/lib/typescript/src/components/header.styles.d.ts.map +1 -0
- package/lib/typescript/src/components/image.styles.d.ts +28 -0
- package/lib/typescript/src/components/image.styles.d.ts.map +1 -0
- package/lib/typescript/src/components/index.d.ts +23 -0
- package/lib/typescript/src/components/index.d.ts.map +1 -0
- package/lib/typescript/src/components/modal.styles.d.ts +66 -0
- package/lib/typescript/src/components/modal.styles.d.ts.map +1 -0
- package/lib/typescript/src/components/progress.styles.d.ts +44 -0
- package/lib/typescript/src/components/progress.styles.d.ts.map +1 -0
- package/lib/typescript/src/components/radio.styles.d.ts +91 -0
- package/lib/typescript/src/components/radio.styles.d.ts.map +1 -0
- package/lib/typescript/src/components/screen.styles.d.ts +34 -0
- package/lib/typescript/src/components/screen.styles.d.ts.map +1 -0
- package/lib/typescript/src/components/skeleton.styles.d.ts +105 -0
- package/lib/typescript/src/components/skeleton.styles.d.ts.map +1 -0
- package/lib/typescript/src/components/surface.styles.d.ts +31 -0
- package/lib/typescript/src/components/surface.styles.d.ts.map +1 -0
- package/lib/typescript/src/components/textfield.styles.d.ts +75 -0
- package/lib/typescript/src/components/textfield.styles.d.ts.map +1 -0
- package/lib/typescript/src/components/toast.styles.d.ts +62 -0
- package/lib/typescript/src/components/toast.styles.d.ts.map +1 -0
- package/lib/typescript/src/components/toggle.styles.d.ts +53 -0
- package/lib/typescript/src/components/toggle.styles.d.ts.map +1 -0
- package/lib/typescript/src/contracts/contract-types.d.ts +193 -0
- package/lib/typescript/src/contracts/contract-types.d.ts.map +1 -0
- package/lib/typescript/src/icons/Icon.d.ts +40 -0
- package/lib/typescript/src/icons/Icon.d.ts.map +1 -0
- package/lib/typescript/src/icons/glyphs.d.ts +259 -0
- package/lib/typescript/src/icons/glyphs.d.ts.map +1 -0
- package/lib/typescript/src/icons/icon.styles.d.ts +20 -0
- package/lib/typescript/src/icons/icon.styles.d.ts.map +1 -0
- package/lib/typescript/src/icons/index.d.ts +12 -0
- package/lib/typescript/src/icons/index.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +12 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/primitives/Button.d.ts +48 -0
- package/lib/typescript/src/primitives/Button.d.ts.map +1 -0
- package/lib/typescript/src/primitives/Metric.d.ts +38 -0
- package/lib/typescript/src/primitives/Metric.d.ts.map +1 -0
- package/lib/typescript/src/primitives/Row.d.ts +37 -0
- package/lib/typescript/src/primitives/Row.d.ts.map +1 -0
- package/lib/typescript/src/primitives/Stack.d.ts +33 -0
- package/lib/typescript/src/primitives/Stack.d.ts.map +1 -0
- package/lib/typescript/src/primitives/Tag.d.ts +43 -0
- package/lib/typescript/src/primitives/Tag.d.ts.map +1 -0
- package/lib/typescript/src/primitives/Text.d.ts +45 -0
- package/lib/typescript/src/primitives/Text.d.ts.map +1 -0
- package/lib/typescript/src/primitives/_internal/focus-ring.d.ts +31 -0
- package/lib/typescript/src/primitives/_internal/focus-ring.d.ts.map +1 -0
- package/lib/typescript/src/primitives/button.styles.d.ts +29 -0
- package/lib/typescript/src/primitives/button.styles.d.ts.map +1 -0
- package/lib/typescript/src/primitives/index.d.ts +8 -0
- package/lib/typescript/src/primitives/index.d.ts.map +1 -0
- package/lib/typescript/src/primitives/metric.styles.d.ts +43 -0
- package/lib/typescript/src/primitives/metric.styles.d.ts.map +1 -0
- package/lib/typescript/src/primitives/row.styles.d.ts +50 -0
- package/lib/typescript/src/primitives/row.styles.d.ts.map +1 -0
- package/lib/typescript/src/primitives/stack.styles.d.ts +34 -0
- package/lib/typescript/src/primitives/stack.styles.d.ts.map +1 -0
- package/lib/typescript/src/primitives/tag.styles.d.ts +32 -0
- package/lib/typescript/src/primitives/tag.styles.d.ts.map +1 -0
- package/lib/typescript/src/primitives/text.styles.d.ts +26 -0
- package/lib/typescript/src/primitives/text.styles.d.ts.map +1 -0
- package/lib/typescript/src/theme/ThemeProvider.d.ts +32 -0
- package/lib/typescript/src/theme/ThemeProvider.d.ts.map +1 -0
- package/lib/typescript/src/theme/index.d.ts +15 -0
- package/lib/typescript/src/theme/index.d.ts.map +1 -0
- package/lib/typescript/src/theme/native-theme.d.ts +3 -0
- package/lib/typescript/src/theme/native-theme.d.ts.map +1 -0
- package/lib/typescript/src/theme/types.d.ts +121 -0
- package/lib/typescript/src/theme/types.d.ts.map +1 -0
- package/package.json +120 -0
- package/src/components/ActionSheet.tsx +112 -0
- package/src/components/Alert.tsx +84 -0
- package/src/components/Avatar.tsx +110 -0
- package/src/components/BottomSheet.tsx +81 -0
- package/src/components/Card.tsx +102 -0
- package/src/components/Checkbox.tsx +107 -0
- package/src/components/EmptyState.tsx +64 -0
- package/src/components/Header.tsx +51 -0
- package/src/components/Image.tsx +89 -0
- package/src/components/Modal.tsx +113 -0
- package/src/components/Progress.tsx +124 -0
- package/src/components/Radio.tsx +151 -0
- package/src/components/Screen.tsx +62 -0
- package/src/components/Skeleton.tsx +154 -0
- package/src/components/Surface.tsx +37 -0
- package/src/components/TextField.tsx +102 -0
- package/src/components/Toast.tsx +103 -0
- package/src/components/Toggle.tsx +94 -0
- package/src/components/action-sheet.styles.ts +102 -0
- package/src/components/alert.styles.ts +115 -0
- package/src/components/avatar.styles.ts +110 -0
- package/src/components/bottom-sheet.styles.ts +78 -0
- package/src/components/card.styles.ts +56 -0
- package/src/components/checkbox.styles.ts +83 -0
- package/src/components/empty-state.styles.ts +98 -0
- package/src/components/header.styles.ts +44 -0
- package/src/components/image.styles.ts +36 -0
- package/src/components/index.ts +23 -0
- package/src/components/modal.styles.ts +106 -0
- package/src/components/progress.styles.ts +74 -0
- package/src/components/radio.styles.ts +132 -0
- package/src/components/screen.styles.ts +44 -0
- package/src/components/skeleton.styles.ts +155 -0
- package/src/components/surface.styles.ts +50 -0
- package/src/components/textfield.styles.ts +135 -0
- package/src/components/toast.styles.ts +115 -0
- package/src/components/toggle.styles.ts +73 -0
- package/src/contracts/contract-types.ts +199 -0
- package/src/icons/Icon.tsx +65 -0
- package/src/icons/glyphs.ts +54 -0
- package/src/icons/icon.styles.ts +37 -0
- package/src/icons/index.ts +12 -0
- package/src/index.ts +24 -0
- package/src/primitives/Button.tsx +114 -0
- package/src/primitives/Metric.tsx +67 -0
- package/src/primitives/Row.tsx +54 -0
- package/src/primitives/Stack.tsx +42 -0
- package/src/primitives/Tag.tsx +126 -0
- package/src/primitives/Text.tsx +71 -0
- package/src/primitives/_internal/focus-ring.tsx +59 -0
- package/src/primitives/button.styles.ts +69 -0
- package/src/primitives/index.ts +8 -0
- package/src/primitives/metric.styles.ts +75 -0
- package/src/primitives/row.styles.ts +66 -0
- package/src/primitives/stack.styles.ts +47 -0
- package/src/primitives/tag.styles.ts +65 -0
- package/src/primitives/text.styles.ts +42 -0
- package/src/theme/ThemeProvider.tsx +60 -0
- package/src/theme/index.ts +18 -0
- package/src/theme/native-theme.ts +215 -0
- package/src/theme/types.ts +137 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NativeTheme — melta の design contracts(melta-contracts の tokens.json)を
|
|
3
|
+
* React Native 向けに正規化した theme の型。
|
|
4
|
+
*
|
|
5
|
+
* 生成は scripts/generate-native-theme.ts(純粋変換は scripts/lib/normalize-tokens.ts)。
|
|
6
|
+
* web 形式 → RN 形式の正規化規則は requirements-melta-app.md §4 を参照。
|
|
7
|
+
*/
|
|
8
|
+
import type { ViewStyle } from "react-native";
|
|
9
|
+
/** primary パレット(50〜950)。値は hex 文字列。 */
|
|
10
|
+
export type PrimaryScale = "50" | "100" | "200" | "300" | "400" | "500" | "600" | "700" | "800" | "900" | "950";
|
|
11
|
+
/** semantic カラー(light / dark で同じキー集合を持つ)。値は色文字列。 */
|
|
12
|
+
export interface SemanticColors {
|
|
13
|
+
"bg-page": string;
|
|
14
|
+
"bg-page-alt": string;
|
|
15
|
+
"bg-surface": string;
|
|
16
|
+
"bg-surface-alt": string;
|
|
17
|
+
"text-heading": string;
|
|
18
|
+
"text-default": string;
|
|
19
|
+
"text-muted": string;
|
|
20
|
+
"border-default": string;
|
|
21
|
+
"border-strong": string;
|
|
22
|
+
"input-bg": string;
|
|
23
|
+
"input-border": string;
|
|
24
|
+
"text-on-accent": string;
|
|
25
|
+
}
|
|
26
|
+
/** status カラー(success / warning / danger)。light/dark の subtle・text を保持。 */
|
|
27
|
+
export interface StatusColors {
|
|
28
|
+
base: string;
|
|
29
|
+
subtleLight: string;
|
|
30
|
+
textLight: string;
|
|
31
|
+
subtleDark: string;
|
|
32
|
+
textDark: string;
|
|
33
|
+
}
|
|
34
|
+
export interface ThemeColor {
|
|
35
|
+
primary: Record<PrimaryScale, string>;
|
|
36
|
+
body: string;
|
|
37
|
+
semantic: {
|
|
38
|
+
light: SemanticColors;
|
|
39
|
+
dark: SemanticColors;
|
|
40
|
+
};
|
|
41
|
+
status: {
|
|
42
|
+
success: StatusColors;
|
|
43
|
+
warning: StatusColors;
|
|
44
|
+
danger: StatusColors;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export type FontSizeKey = "xs" | "sm" | "base" | "lg" | "xl" | "2xl" | "3xl";
|
|
48
|
+
export type FontWeightKey = "normal" | "medium" | "semibold" | "bold";
|
|
49
|
+
/** RN の fontWeight は文字列。tokens の数値(400 等)を文字列化したもの。 */
|
|
50
|
+
export type FontWeightValue = "400" | "500" | "600" | "700";
|
|
51
|
+
export interface FontSizeEntry {
|
|
52
|
+
/** px 数値(tokens.json の px をそのまま採用) */
|
|
53
|
+
fontSize: number;
|
|
54
|
+
/** fontSize × lineHeight比率 を px に丸めた値 */
|
|
55
|
+
lineHeight: number;
|
|
56
|
+
}
|
|
57
|
+
export interface ThemeTypography {
|
|
58
|
+
/**
|
|
59
|
+
* 初期は system default(未指定 = OS デフォルトフォント)。RN の fontFamily は string 一本で
|
|
60
|
+
* web の fallback 配列のようには効かず、Inter 等を焼くと expo-font でのロード必須になり実機で崩れる。
|
|
61
|
+
* Inter / JetBrains Mono の embed は expo-font 導入後に generator へ font マップを渡す Phase 2。
|
|
62
|
+
* それまでは sans/mono とも未指定(= OS の sans / monospace)にする。
|
|
63
|
+
*/
|
|
64
|
+
fontFamily: {
|
|
65
|
+
sans?: string;
|
|
66
|
+
mono?: string;
|
|
67
|
+
};
|
|
68
|
+
fontSize: Record<FontSizeKey, FontSizeEntry>;
|
|
69
|
+
fontWeight: Record<FontWeightKey, FontWeightValue>;
|
|
70
|
+
/**
|
|
71
|
+
* letterSpacing の em ratio を数値で保持する(px ではない)。
|
|
72
|
+
* tokens は em(fontSize 相対)だが RN の letterSpacing は絶対 pt のため token 単体では px 換算できない。
|
|
73
|
+
* 適用側は `resolveLetterSpacing(fontSize, ratio)`(= fontSize × ratio)で pt を算出すること。
|
|
74
|
+
* 名前を `Ratio` 付きにしているのは、ratio をそのまま style.letterSpacing に入れる誤用を防ぐため。
|
|
75
|
+
* (requirements §4 の「px に換算」からの意図的な乖離。理由はこのコメントの通り)
|
|
76
|
+
*/
|
|
77
|
+
letterSpacingRatio: {
|
|
78
|
+
heading: number;
|
|
79
|
+
body: number;
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* elevation 1段。iOS は shadow* を、Android は elevation を読む(互いに無視するので
|
|
84
|
+
* 1つの ViewStyle に同居させて spread すれば両 OS に効く)。
|
|
85
|
+
*/
|
|
86
|
+
export type ElevationStyle = Pick<ViewStyle, "shadowColor" | "shadowOffset" | "shadowOpacity" | "shadowRadius" | "elevation">;
|
|
87
|
+
/** iOS 側の shadow props だけ(Android elevation は別途 mapping table で決める)。 */
|
|
88
|
+
export type ShadowStyle = Omit<ElevationStyle, "elevation">;
|
|
89
|
+
export type ElevationKey = "none" | "sm" | "md" | "overlay";
|
|
90
|
+
export type SpacingKey = "1" | "2" | "3" | "4" | "5" | "6" | "8" | "10" | "12" | "14" | "16";
|
|
91
|
+
export type RadiusKey = "sm" | "md" | "lg" | "full";
|
|
92
|
+
export interface ThemeMotion {
|
|
93
|
+
/** ms 数値 */
|
|
94
|
+
duration: {
|
|
95
|
+
fast: number;
|
|
96
|
+
normal: number;
|
|
97
|
+
slow: number;
|
|
98
|
+
};
|
|
99
|
+
/** cubic-bezier の 4 引数(Easing.bezier(...) に展開して使う) */
|
|
100
|
+
easing: {
|
|
101
|
+
default: [number, number, number, number];
|
|
102
|
+
in: [number, number, number, number];
|
|
103
|
+
out: [number, number, number, number];
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
export interface NativeTheme {
|
|
107
|
+
color: ThemeColor;
|
|
108
|
+
typography: ThemeTypography;
|
|
109
|
+
spacing: Record<SpacingKey, number>;
|
|
110
|
+
radius: Record<RadiusKey, number>;
|
|
111
|
+
elevation: Record<ElevationKey, ElevationStyle>;
|
|
112
|
+
motion: ThemeMotion;
|
|
113
|
+
zIndex: {
|
|
114
|
+
base: number;
|
|
115
|
+
dropdown: number;
|
|
116
|
+
sticky: number;
|
|
117
|
+
overlay: number;
|
|
118
|
+
modal: number;
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/theme/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "melta-app",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "melta for APP — melta デザインシステムの React Native UI kit。melta-contracts(デザイン契約)を single source of truth に RN 実装でその契約を満たす。アプリ本体ではなくライブラリ。",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"homepage": "https://app.melta.tsubotax.com",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/tsubotax/melta-app.git"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"react-native",
|
|
13
|
+
"expo",
|
|
14
|
+
"design-system",
|
|
15
|
+
"ui-kit",
|
|
16
|
+
"melta",
|
|
17
|
+
"ai-ready"
|
|
18
|
+
],
|
|
19
|
+
"main": "./lib/module/index.js",
|
|
20
|
+
"types": "./lib/typescript/src/index.d.ts",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./lib/typescript/src/index.d.ts",
|
|
24
|
+
"react-native": "./lib/module/index.js",
|
|
25
|
+
"import": "./lib/module/index.js",
|
|
26
|
+
"default": "./lib/module/index.js"
|
|
27
|
+
},
|
|
28
|
+
"./icons": {
|
|
29
|
+
"types": "./lib/typescript/src/icons/index.d.ts",
|
|
30
|
+
"react-native": "./lib/module/icons/index.js",
|
|
31
|
+
"import": "./lib/module/icons/index.js",
|
|
32
|
+
"default": "./lib/module/icons/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./package.json": "./package.json"
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"lib",
|
|
38
|
+
"src",
|
|
39
|
+
"!src/**/*.test.*",
|
|
40
|
+
"THIRD_PARTY_LICENSES.md"
|
|
41
|
+
],
|
|
42
|
+
"sideEffects": false,
|
|
43
|
+
"workspaces": [
|
|
44
|
+
"example"
|
|
45
|
+
],
|
|
46
|
+
"peerDependencies": {
|
|
47
|
+
"react": "*",
|
|
48
|
+
"react-native": "*",
|
|
49
|
+
"react-native-svg": ">=15"
|
|
50
|
+
},
|
|
51
|
+
"peerDependenciesMeta": {
|
|
52
|
+
"react-native-svg": {
|
|
53
|
+
"optional": true
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@jest/globals": "^29.7.0",
|
|
58
|
+
"@react-native/babel-preset": "^0.85.3",
|
|
59
|
+
"@react-native/jest-preset": "^0.85.3",
|
|
60
|
+
"@testing-library/react-native": "^14.0.1",
|
|
61
|
+
"@types/node": "^25.9.1",
|
|
62
|
+
"@types/react": "~19.2.2",
|
|
63
|
+
"eslint": "^10.4.1",
|
|
64
|
+
"jest": "^29.7.0",
|
|
65
|
+
"melta-contracts": "^0.4.0",
|
|
66
|
+
"react": "19.2.3",
|
|
67
|
+
"react-native": "0.85.3",
|
|
68
|
+
"react-native-builder-bob": "^0.40.13",
|
|
69
|
+
"react-native-svg": "^15.15.4",
|
|
70
|
+
"tsx": "^4.19.0",
|
|
71
|
+
"typescript": "~6.0.3",
|
|
72
|
+
"typescript-eslint": "^8.60.0"
|
|
73
|
+
},
|
|
74
|
+
"scripts": {
|
|
75
|
+
"start": "npm run start -w example",
|
|
76
|
+
"android": "npm run android -w example",
|
|
77
|
+
"ios": "npm run ios -w example",
|
|
78
|
+
"web": "npm run web -w example",
|
|
79
|
+
"export:web": "npm run export:web -w example",
|
|
80
|
+
"build:showcase": "bash scripts/build-showcase.sh",
|
|
81
|
+
"generate:theme": "tsx scripts/generate-native-theme.ts",
|
|
82
|
+
"generate:contracts": "tsx scripts/generate-contract-types.ts",
|
|
83
|
+
"generate": "npm run generate:theme && npm run generate:contracts",
|
|
84
|
+
"build": "bob build",
|
|
85
|
+
"prepare": "bob build",
|
|
86
|
+
"test": "tsx --test scripts/lib/*.test.ts",
|
|
87
|
+
"test:rn": "jest",
|
|
88
|
+
"typecheck": "tsc --noEmit && tsc --noEmit -p example/tsconfig.json && tsc --noEmit -p scripts/tsconfig.json",
|
|
89
|
+
"lint": "eslint . --max-warnings 0",
|
|
90
|
+
"check:drift": "tsx scripts/check-drift.ts",
|
|
91
|
+
"check:installability": "bash scripts/check-installability.sh"
|
|
92
|
+
},
|
|
93
|
+
"jest": {
|
|
94
|
+
"preset": "@react-native/jest-preset",
|
|
95
|
+
"roots": [
|
|
96
|
+
"<rootDir>/src"
|
|
97
|
+
],
|
|
98
|
+
"testMatch": [
|
|
99
|
+
"<rootDir>/src/**/__tests__/**/*.test.tsx"
|
|
100
|
+
]
|
|
101
|
+
},
|
|
102
|
+
"react-native-builder-bob": {
|
|
103
|
+
"source": "src",
|
|
104
|
+
"output": "lib",
|
|
105
|
+
"targets": [
|
|
106
|
+
[
|
|
107
|
+
"module",
|
|
108
|
+
{
|
|
109
|
+
"esm": true
|
|
110
|
+
}
|
|
111
|
+
],
|
|
112
|
+
[
|
|
113
|
+
"typescript",
|
|
114
|
+
{
|
|
115
|
+
"project": "tsconfig.build.json"
|
|
116
|
+
}
|
|
117
|
+
]
|
|
118
|
+
]
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ActionSheet — 画面下から出るアクション/選択リスト。contract: action-sheet(appFirst、feedback)。
|
|
3
|
+
* RN core Modal(transparent + slide)+ 下端 sheet。web の select / dropdown を APP に
|
|
4
|
+
* adapted 変換する際の受け皿。
|
|
5
|
+
*
|
|
6
|
+
* - actions は縦積み(区切り線 border-default)、cancel は下に分離した独立ブロック
|
|
7
|
+
* (閉じる手段を複数持つ: cancelAction + overlay tap + Android back = contract a11y required)。
|
|
8
|
+
* - destructive アクションは destructiveTextStyle(status.danger.base)で色を差し替える。
|
|
9
|
+
* 色だけでなくラベル文言でも破壊的操作と分かる文言にするのは呼び出し側の責務(contract a11y)。
|
|
10
|
+
* - 先頭 action の borderTop は title が無い場合は消す(title がある時だけ区切りとして意味を持つ)。
|
|
11
|
+
* - action の onPress 後は onClose も呼ぶ(選択 = シートを閉じる)。
|
|
12
|
+
* - sheet 群は SafeAreaView で包み下余白(ホームインジケータ)を確保(recipe description 参照)。
|
|
13
|
+
* - sheet 側は onStartShouldSetResponder で touch を claim し、overlay tap でだけ閉じる。
|
|
14
|
+
* - style の決定は pure resolver(action-sheet.styles.ts)に分離 — recipe との機械照合対象。
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { useMemo } from "react";
|
|
18
|
+
import {
|
|
19
|
+
Modal as RNModal,
|
|
20
|
+
Pressable,
|
|
21
|
+
SafeAreaView,
|
|
22
|
+
Text as RNText,
|
|
23
|
+
View,
|
|
24
|
+
} from "react-native";
|
|
25
|
+
import { useTheme } from "../theme";
|
|
26
|
+
import { CONTRACTS } from "../contracts/contract-types";
|
|
27
|
+
import { resolveActionSheetStyle, type ActionSheetStyle } from "./action-sheet.styles";
|
|
28
|
+
|
|
29
|
+
export interface ActionSheetAction {
|
|
30
|
+
label: string;
|
|
31
|
+
onPress: () => void;
|
|
32
|
+
/** 破壊的操作(色を danger に差し替え。文言でも破壊的と分かるようにすること)。 */
|
|
33
|
+
destructive?: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface ActionSheetProps {
|
|
37
|
+
visible: boolean;
|
|
38
|
+
/** 閉じ要求(cancel / overlay tap / Android back / action 選択後の全経路で呼ばれる)。 */
|
|
39
|
+
onClose: () => void;
|
|
40
|
+
/** シート上部の説明ラベル(任意)。 */
|
|
41
|
+
title?: string;
|
|
42
|
+
/** 縦積みするアクション群。 */
|
|
43
|
+
actions: ActionSheetAction[];
|
|
44
|
+
/** キャンセル行のラベル。default "キャンセル"。 */
|
|
45
|
+
cancelLabel?: string;
|
|
46
|
+
testID?: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function ActionSheet({
|
|
50
|
+
visible,
|
|
51
|
+
onClose,
|
|
52
|
+
title,
|
|
53
|
+
actions,
|
|
54
|
+
cancelLabel = "キャンセル",
|
|
55
|
+
testID,
|
|
56
|
+
}: ActionSheetProps) {
|
|
57
|
+
const { theme, mode } = useTheme();
|
|
58
|
+
|
|
59
|
+
const styles = useMemo<ActionSheetStyle>(
|
|
60
|
+
() => resolveActionSheetStyle(theme, mode),
|
|
61
|
+
[theme, mode],
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<RNModal
|
|
66
|
+
visible={visible}
|
|
67
|
+
transparent
|
|
68
|
+
animationType="slide"
|
|
69
|
+
onRequestClose={onClose}
|
|
70
|
+
statusBarTranslucent
|
|
71
|
+
>
|
|
72
|
+
<Pressable style={styles.overlayStyle} onPress={onClose} accessible={false}>
|
|
73
|
+
{/* sheet 群(sheet + cancel): SafeArea で下余白を確保し、touch を claim して overlay へ流さない */}
|
|
74
|
+
<SafeAreaView
|
|
75
|
+
accessibilityViewIsModal
|
|
76
|
+
onStartShouldSetResponder={() => true}
|
|
77
|
+
testID={testID}
|
|
78
|
+
>
|
|
79
|
+
<View style={styles.sheetStyle}>
|
|
80
|
+
{title != null && <RNText style={styles.titleStyle}>{title}</RNText>}
|
|
81
|
+
{actions.map((action, index) => (
|
|
82
|
+
<Pressable
|
|
83
|
+
key={`${index}-${action.label}`}
|
|
84
|
+
accessibilityRole="button"
|
|
85
|
+
onPress={() => {
|
|
86
|
+
action.onPress();
|
|
87
|
+
onClose();
|
|
88
|
+
}}
|
|
89
|
+
style={[
|
|
90
|
+
styles.actionStyle,
|
|
91
|
+
// 先頭 action の区切り線は title がある時だけ意味を持つ
|
|
92
|
+
index === 0 && title == null ? { borderTopWidth: 0 } : null,
|
|
93
|
+
]}
|
|
94
|
+
>
|
|
95
|
+
<RNText
|
|
96
|
+
style={action.destructive ? styles.destructiveTextStyle : styles.actionTextStyle}
|
|
97
|
+
>
|
|
98
|
+
{action.label}
|
|
99
|
+
</RNText>
|
|
100
|
+
</Pressable>
|
|
101
|
+
))}
|
|
102
|
+
</View>
|
|
103
|
+
<Pressable accessibilityRole="button" onPress={onClose} style={styles.cancelStyle}>
|
|
104
|
+
<RNText style={styles.cancelTextStyle}>{cancelLabel}</RNText>
|
|
105
|
+
</Pressable>
|
|
106
|
+
</SafeAreaView>
|
|
107
|
+
</Pressable>
|
|
108
|
+
</RNModal>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
ActionSheet.__contract = CONTRACTS.actionSheet;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Alert — インラインアラート通知(feedback)。contract: alert。
|
|
3
|
+
* Info / Success / Warning / Error の4タイプ。Text の compose + 任意 icon slot / 閉じるボタン。
|
|
4
|
+
*
|
|
5
|
+
* - RN では border を持たず bg subtle + text 色で表現(status token に border 用の中間色が無い、
|
|
6
|
+
* recipe description)。COLOR_ONLY_FORBIDDEN は title/message のテキストと icon slot で担保する設計。
|
|
7
|
+
* - icon は ReactNode slot(EmptyState 前例。Icon を差すなら melta-app/icons を利用者が opt-in)。
|
|
8
|
+
* - a11y(§2 web→RN mapping): web の role=alert(error/warning) / status(info/success) を、
|
|
9
|
+
* error/warning → accessibilityRole="alert"、info/success → accessibilityLiveRegion="polite"
|
|
10
|
+
* (Android のみ有効。RN に status role が無いための写像)に落とす。
|
|
11
|
+
* - 色・寸法の決定は pure resolver(alert.styles.ts)に分離。dark mode は resolver が
|
|
12
|
+
* status.*.subtleDark / textDark を解決(info のみ primary 固定)。
|
|
13
|
+
* recipes/app/alert.recipe.json との機械照合は scripts/lib/alert-conformance.test.ts が行う。
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { useMemo, type ReactNode } from "react";
|
|
17
|
+
import { Pressable, View, type StyleProp, type ViewStyle } from "react-native";
|
|
18
|
+
import { Text } from "../primitives/Text";
|
|
19
|
+
import { useTheme } from "../theme";
|
|
20
|
+
import { CONTRACTS } from "../contracts/contract-types";
|
|
21
|
+
import { ALERT_SPEC, resolveAlertStyles, type AlertVariant } from "./alert.styles";
|
|
22
|
+
|
|
23
|
+
interface AlertProps {
|
|
24
|
+
/** 通知タイプ(contract variant)。 */
|
|
25
|
+
variant: AlertVariant;
|
|
26
|
+
/** 見出し(任意)。 */
|
|
27
|
+
title?: string;
|
|
28
|
+
/** 本文(必須)。 */
|
|
29
|
+
message: string;
|
|
30
|
+
/** 先頭の icon slot(デフォルト無し)。 */
|
|
31
|
+
icon?: ReactNode;
|
|
32
|
+
/** あれば右端に閉じるボタン(Pressable の ×)を出す。 */
|
|
33
|
+
onClose?: () => void;
|
|
34
|
+
style?: StyleProp<ViewStyle>;
|
|
35
|
+
testID?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function Alert({ variant, title, message, icon, onClose, style, testID }: AlertProps) {
|
|
39
|
+
const { theme, mode } = useTheme();
|
|
40
|
+
const styles = useMemo(() => resolveAlertStyles(theme, mode, variant), [theme, mode, variant]);
|
|
41
|
+
const urgent = variant === "error" || variant === "warning";
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<View
|
|
45
|
+
accessibilityRole={urgent ? "alert" : undefined}
|
|
46
|
+
accessibilityLiveRegion={urgent ? undefined : "polite"}
|
|
47
|
+
testID={testID}
|
|
48
|
+
style={[styles.containerStyle, style]}
|
|
49
|
+
>
|
|
50
|
+
{icon != null && <View>{icon}</View>}
|
|
51
|
+
<View style={{ flex: 1, gap: theme.spacing["1"] }}>
|
|
52
|
+
{title != null && (
|
|
53
|
+
<Text
|
|
54
|
+
variant={ALERT_SPEC.titleFont}
|
|
55
|
+
weight={ALERT_SPEC.titleWeight}
|
|
56
|
+
style={{ color: styles.titleStyle.color }}
|
|
57
|
+
>
|
|
58
|
+
{title}
|
|
59
|
+
</Text>
|
|
60
|
+
)}
|
|
61
|
+
<Text variant={ALERT_SPEC.messageFont} style={{ color: styles.messageStyle.color }}>
|
|
62
|
+
{message}
|
|
63
|
+
</Text>
|
|
64
|
+
</View>
|
|
65
|
+
{onClose != null && (
|
|
66
|
+
// 最小タップターゲット確保: min 24 + hitSlop(Tag removable と同じ手当て)。
|
|
67
|
+
<Pressable
|
|
68
|
+
onPress={onClose}
|
|
69
|
+
accessibilityRole="button"
|
|
70
|
+
accessibilityLabel="閉じる"
|
|
71
|
+
hitSlop={10}
|
|
72
|
+
style={{ minWidth: 24, minHeight: 24, alignItems: "center", justifyContent: "center" }}
|
|
73
|
+
>
|
|
74
|
+
<Text variant="xs" style={{ color: styles.messageStyle.color }}>
|
|
75
|
+
×
|
|
76
|
+
</Text>
|
|
77
|
+
</Pressable>
|
|
78
|
+
)}
|
|
79
|
+
</View>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Phase 2 conformance test 用の contract メタ(§2 A-3)。
|
|
84
|
+
Alert.__contract = CONTRACTS.alert;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Avatar — ユーザーアバター(Image / Initials / Status / Group)。
|
|
3
|
+
* contract: avatar(web 契約 v2.0.0 の app 実装)。dogfood 不足-4 の解消。
|
|
4
|
+
*
|
|
5
|
+
* - variant は props から暗黙決定: source があれば image、無ければ initials(name から生成)。
|
|
6
|
+
* contract の group variant は Avatar.Group(重ね表示コンテナ、-spacing.2 相当のオーバーラップ)。
|
|
7
|
+
* - status(online / away / offline)指定時は右下に statusDot(bg-surface の ring 付き)。
|
|
8
|
+
* - a11y: role img + accessibilityLabel=name(contract required)。
|
|
9
|
+
* - 決定ロジックは pure resolver(avatar.styles.ts)に分離 — recipe との機械照合対象。
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { Children, useMemo, type ReactNode } from "react";
|
|
13
|
+
import {
|
|
14
|
+
Image as RNImage,
|
|
15
|
+
Text as RNText,
|
|
16
|
+
View,
|
|
17
|
+
type ImageSourcePropType,
|
|
18
|
+
type StyleProp,
|
|
19
|
+
type ViewStyle,
|
|
20
|
+
} from "react-native";
|
|
21
|
+
import { useTheme } from "../theme";
|
|
22
|
+
import { CONTRACTS } from "../contracts/contract-types";
|
|
23
|
+
import {
|
|
24
|
+
resolveAvatarGroupStyle,
|
|
25
|
+
resolveAvatarStatusColor,
|
|
26
|
+
resolveAvatarStyle,
|
|
27
|
+
type AvatarSize,
|
|
28
|
+
type AvatarStatus,
|
|
29
|
+
type AvatarStyleResult,
|
|
30
|
+
} from "./avatar.styles";
|
|
31
|
+
|
|
32
|
+
interface AvatarProps {
|
|
33
|
+
/** 表示名(initials の生成元 + accessibilityLabel。contract a11y required)。 */
|
|
34
|
+
name: string;
|
|
35
|
+
/** プロフィール画像。省略時は initials variant にフォールバック。 */
|
|
36
|
+
source?: ImageSourcePropType;
|
|
37
|
+
/** サイズ(contract sizes)。default "medium"。 */
|
|
38
|
+
size?: AvatarSize;
|
|
39
|
+
/** 在席状態(contract states)。指定時のみ statusDot を表示。 */
|
|
40
|
+
status?: AvatarStatus;
|
|
41
|
+
style?: StyleProp<ViewStyle>;
|
|
42
|
+
testID?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** name → initials(先頭2文字。空白区切りなら各語の頭文字を優先: "Taro Tanaka" → "TT")。 */
|
|
46
|
+
function toInitials(name: string): string {
|
|
47
|
+
const words = name.trim().split(/\s+/).filter(Boolean);
|
|
48
|
+
if (words.length >= 2) return (words[0][0] + words[1][0]).toUpperCase();
|
|
49
|
+
return name.trim().slice(0, 2).toUpperCase();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function Avatar({ name, source, size = "medium", status, style, testID }: AvatarProps) {
|
|
53
|
+
const { theme, mode } = useTheme();
|
|
54
|
+
const variant = source ? "image" : "initials";
|
|
55
|
+
|
|
56
|
+
const resolved = useMemo<AvatarStyleResult>(
|
|
57
|
+
() => resolveAvatarStyle(theme, mode, variant, size),
|
|
58
|
+
[theme, mode, variant, size],
|
|
59
|
+
);
|
|
60
|
+
const dotColor = status ? resolveAvatarStatusColor(theme, mode, status) : undefined;
|
|
61
|
+
|
|
62
|
+
// clip(overflow hidden)は内側の円に閉じ、statusDot は外側(クリップされない層)に置く。
|
|
63
|
+
// accessible 明示で「1つの読み上げ単位」にする(非 Touchable の View は暗黙では discoverable でない)
|
|
64
|
+
return (
|
|
65
|
+
<View
|
|
66
|
+
style={[{ width: resolved.container.width, height: resolved.container.height }, style]}
|
|
67
|
+
accessible
|
|
68
|
+
accessibilityRole="image"
|
|
69
|
+
accessibilityLabel={name}
|
|
70
|
+
testID={testID}
|
|
71
|
+
>
|
|
72
|
+
<View style={resolved.container}>
|
|
73
|
+
{source ? (
|
|
74
|
+
<RNImage source={source} style={{ width: "100%", height: "100%" }} />
|
|
75
|
+
) : (
|
|
76
|
+
<RNText style={resolved.text}>{toInitials(name)}</RNText>
|
|
77
|
+
)}
|
|
78
|
+
</View>
|
|
79
|
+
{status ? (
|
|
80
|
+
<View
|
|
81
|
+
style={[
|
|
82
|
+
resolved.dot,
|
|
83
|
+
{ backgroundColor: dotColor, position: "absolute", right: 0, bottom: 0 },
|
|
84
|
+
]}
|
|
85
|
+
/>
|
|
86
|
+
) : null}
|
|
87
|
+
</View>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
interface AvatarGroupProps {
|
|
92
|
+
style?: StyleProp<ViewStyle>;
|
|
93
|
+
testID?: string;
|
|
94
|
+
children: ReactNode;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** 複数アバターの重ね表示(contract の group variant)。2枚目以降を負 margin で重ねる。 */
|
|
98
|
+
function AvatarGroup({ style, testID, children }: AvatarGroupProps) {
|
|
99
|
+
const group = resolveAvatarGroupStyle();
|
|
100
|
+
return (
|
|
101
|
+
<View style={[group.container, style]} testID={testID}>
|
|
102
|
+
{Children.map(children, (child, index) => (
|
|
103
|
+
<View style={index > 0 ? group.overlap : null}>{child}</View>
|
|
104
|
+
))}
|
|
105
|
+
</View>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
Avatar.Group = AvatarGroup;
|
|
110
|
+
Avatar.__contract = CONTRACTS.avatar;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BottomSheet — 画面下から出る汎用シート container(content は自由 slot)。
|
|
3
|
+
* contract: bottom-sheet(appFirst、feedback)。RN core Modal(transparent + slide)+
|
|
4
|
+
* 下端 sheet(上角のみ radius)。ActionSheet がリスト特化なのに対しこちらは容器。
|
|
5
|
+
*
|
|
6
|
+
* - grabber は装飾バー。a11y ツリーから除外する(importantForAccessibility="no" +
|
|
7
|
+
* accessibilityElementsHidden。recipe description 参照)。
|
|
8
|
+
* - スワイプで閉じるジェスチャは実装しない(overlay tap + Android back で閉じる、Phase 外)。
|
|
9
|
+
* - sheet は下端密着(bg-surface)のため、SafeAreaView を sheet の内側に置いて
|
|
10
|
+
* 下余白(ホームインジケータ)まで sheet 背景で覆いつつ content を安全域に収める。
|
|
11
|
+
* - sheet 側は onStartShouldSetResponder で touch を claim し、overlay tap でだけ閉じる。
|
|
12
|
+
* - style の決定は pure resolver(bottom-sheet.styles.ts)に分離 — recipe との機械照合対象。
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { useMemo, type ReactNode } from "react";
|
|
16
|
+
import {
|
|
17
|
+
Modal as RNModal,
|
|
18
|
+
Pressable,
|
|
19
|
+
SafeAreaView,
|
|
20
|
+
Text as RNText,
|
|
21
|
+
View,
|
|
22
|
+
} from "react-native";
|
|
23
|
+
import { useTheme } from "../theme";
|
|
24
|
+
import { CONTRACTS } from "../contracts/contract-types";
|
|
25
|
+
import { resolveBottomSheetStyle, type BottomSheetStyle } from "./bottom-sheet.styles";
|
|
26
|
+
|
|
27
|
+
interface BottomSheetProps {
|
|
28
|
+
visible: boolean;
|
|
29
|
+
/** 閉じ要求(overlay tap / Android back の全経路で呼ばれる)。 */
|
|
30
|
+
onClose: () => void;
|
|
31
|
+
/** シート見出し(任意)。 */
|
|
32
|
+
title?: string;
|
|
33
|
+
testID?: string;
|
|
34
|
+
/** content slot(自由)。 */
|
|
35
|
+
children: ReactNode;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function BottomSheet({ visible, onClose, title, testID, children }: BottomSheetProps) {
|
|
39
|
+
const { theme, mode } = useTheme();
|
|
40
|
+
|
|
41
|
+
const styles = useMemo<BottomSheetStyle>(
|
|
42
|
+
() => resolveBottomSheetStyle(theme, mode),
|
|
43
|
+
[theme, mode],
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<RNModal
|
|
48
|
+
visible={visible}
|
|
49
|
+
transparent
|
|
50
|
+
animationType="slide"
|
|
51
|
+
onRequestClose={onClose}
|
|
52
|
+
statusBarTranslucent
|
|
53
|
+
>
|
|
54
|
+
<Pressable style={styles.overlayStyle} onPress={onClose} accessible={false}>
|
|
55
|
+
<View
|
|
56
|
+
style={styles.sheetStyle}
|
|
57
|
+
accessibilityViewIsModal
|
|
58
|
+
onStartShouldSetResponder={() => true}
|
|
59
|
+
testID={testID}
|
|
60
|
+
>
|
|
61
|
+
<SafeAreaView>
|
|
62
|
+
{/* grabber: 装飾のみ(スワイプ操作は持たないため a11y から隠す) */}
|
|
63
|
+
<View
|
|
64
|
+
style={styles.grabberStyle}
|
|
65
|
+
importantForAccessibility="no"
|
|
66
|
+
accessibilityElementsHidden
|
|
67
|
+
/>
|
|
68
|
+
{title != null && (
|
|
69
|
+
<RNText style={styles.titleStyle} accessibilityRole="header">
|
|
70
|
+
{title}
|
|
71
|
+
</RNText>
|
|
72
|
+
)}
|
|
73
|
+
<View style={styles.contentStyle}>{children}</View>
|
|
74
|
+
</SafeAreaView>
|
|
75
|
+
</View>
|
|
76
|
+
</Pressable>
|
|
77
|
+
</RNModal>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
BottomSheet.__contract = CONTRACTS.bottomSheet;
|