melta-app 0.6.0 → 0.7.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/README.md +220 -12
- package/docs/architecture.md +11 -4
- package/eslint-rules/melta.d.mts +44 -0
- package/lib/module/a11y/tap-target.js +41 -0
- package/lib/module/a11y/tap-target.js.map +1 -0
- package/lib/module/components/ActionSheet.js +20 -6
- package/lib/module/components/ActionSheet.js.map +1 -1
- package/lib/module/components/Alert.js +11 -7
- package/lib/module/components/Alert.js.map +1 -1
- package/lib/module/components/Avatar.js +7 -2
- package/lib/module/components/Avatar.js.map +1 -1
- package/lib/module/components/BottomSheet.js +11 -3
- package/lib/module/components/BottomSheet.js.map +1 -1
- package/lib/module/components/Card.js +4 -3
- package/lib/module/components/Card.js.map +1 -1
- package/lib/module/components/Checkbox.js +8 -5
- package/lib/module/components/Checkbox.js.map +1 -1
- package/lib/module/components/EmptyState.js +2 -1
- package/lib/module/components/EmptyState.js.map +1 -1
- package/lib/module/components/Header.js.map +1 -1
- package/lib/module/components/Image.js +5 -3
- package/lib/module/components/Image.js.map +1 -1
- package/lib/module/components/Modal.js +12 -4
- package/lib/module/components/Modal.js.map +1 -1
- package/lib/module/components/Progress.js +2 -1
- package/lib/module/components/Progress.js.map +1 -1
- package/lib/module/components/Radio.js +11 -3
- package/lib/module/components/Radio.js.map +1 -1
- package/lib/module/components/Screen.js +28 -3
- package/lib/module/components/Screen.js.map +1 -1
- package/lib/module/components/Skeleton.js +2 -1
- package/lib/module/components/Skeleton.js.map +1 -1
- package/lib/module/components/Surface.js +2 -1
- package/lib/module/components/Surface.js.map +1 -1
- package/lib/module/components/TextField.js +16 -4
- package/lib/module/components/TextField.js.map +1 -1
- package/lib/module/components/Toast.js +17 -9
- package/lib/module/components/Toast.js.map +1 -1
- package/lib/module/components/Toggle.js +13 -4
- package/lib/module/components/Toggle.js.map +1 -1
- package/lib/module/components/alert.styles.js +12 -26
- package/lib/module/components/alert.styles.js.map +1 -1
- package/lib/module/components/avatar.styles.js +24 -2
- package/lib/module/components/avatar.styles.js.map +1 -1
- package/lib/module/components/card.validate.js.map +1 -1
- package/lib/module/components/checkbox.styles.js +5 -2
- package/lib/module/components/checkbox.styles.js.map +1 -1
- package/lib/module/components/index.js.map +1 -1
- package/lib/module/components/modal.styles.js +9 -0
- package/lib/module/components/modal.styles.js.map +1 -1
- package/lib/module/components/radio.styles.js +11 -2
- package/lib/module/components/radio.styles.js.map +1 -1
- package/lib/module/components/safe-area-registry.js +64 -13
- package/lib/module/components/safe-area-registry.js.map +1 -1
- package/lib/module/components/status-colors.js +53 -0
- package/lib/module/components/status-colors.js.map +1 -0
- package/lib/module/components/textfield.styles.js +15 -8
- package/lib/module/components/textfield.styles.js.map +1 -1
- package/lib/module/components/toast.styles.js +36 -25
- package/lib/module/components/toast.styles.js.map +1 -1
- package/lib/module/components/toggle.styles.js +20 -0
- package/lib/module/components/toggle.styles.js.map +1 -1
- package/lib/module/icons/Icon.js +4 -1
- package/lib/module/icons/Icon.js.map +1 -1
- package/lib/module/icons/icon.styles.js +32 -1
- package/lib/module/icons/icon.styles.js.map +1 -1
- package/lib/module/icons/index.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/primitives/Button.js +23 -11
- package/lib/module/primitives/Button.js.map +1 -1
- package/lib/module/primitives/Metric.js +2 -1
- package/lib/module/primitives/Metric.js.map +1 -1
- package/lib/module/primitives/Row.js.map +1 -1
- package/lib/module/primitives/Stack.js.map +1 -1
- package/lib/module/primitives/Tag.js +15 -7
- package/lib/module/primitives/Tag.js.map +1 -1
- package/lib/module/primitives/Text.js +13 -5
- package/lib/module/primitives/Text.js.map +1 -1
- package/lib/module/primitives/_internal/focus-ring.js.map +1 -1
- package/lib/module/primitives/button.styles.js +58 -8
- package/lib/module/primitives/button.styles.js.map +1 -1
- package/lib/module/primitives/index.js.map +1 -1
- package/lib/module/primitives/metric.styles.js +2 -2
- package/lib/module/primitives/metric.styles.js.map +1 -1
- package/lib/module/primitives/tag.styles.js +42 -3
- package/lib/module/primitives/tag.styles.js.map +1 -1
- package/lib/module/primitives/text.styles.js +6 -7
- package/lib/module/primitives/text.styles.js.map +1 -1
- package/lib/module/safe-area/index.js +11 -5
- package/lib/module/safe-area/index.js.map +1 -1
- package/lib/module/theme/ThemeProvider.js +5 -2
- package/lib/module/theme/ThemeProvider.js.map +1 -1
- package/lib/module/theme/define-theme.js +14 -1
- package/lib/module/theme/define-theme.js.map +1 -1
- package/lib/module/theme/index.js +6 -8
- package/lib/module/theme/index.js.map +1 -1
- package/lib/module/theme/letter-spacing.js +20 -0
- package/lib/module/theme/letter-spacing.js.map +1 -0
- package/lib/module/theme/line-height.js +9 -0
- package/lib/module/theme/line-height.js.map +1 -1
- package/lib/module/theme/native-theme.js +1 -1
- package/lib/typescript/src/a11y/tap-target.d.ts +34 -0
- package/lib/typescript/src/a11y/tap-target.d.ts.map +1 -0
- package/lib/typescript/src/components/ActionSheet.d.ts +8 -1
- package/lib/typescript/src/components/ActionSheet.d.ts.map +1 -1
- package/lib/typescript/src/components/Alert.d.ts +9 -2
- package/lib/typescript/src/components/Alert.d.ts.map +1 -1
- package/lib/typescript/src/components/Avatar.d.ts +1 -1
- package/lib/typescript/src/components/Avatar.d.ts.map +1 -1
- package/lib/typescript/src/components/BottomSheet.d.ts +4 -1
- package/lib/typescript/src/components/BottomSheet.d.ts.map +1 -1
- package/lib/typescript/src/components/Card.d.ts +2 -2
- package/lib/typescript/src/components/Checkbox.d.ts +3 -3
- package/lib/typescript/src/components/Checkbox.d.ts.map +1 -1
- package/lib/typescript/src/components/Image.d.ts +4 -3
- package/lib/typescript/src/components/Image.d.ts.map +1 -1
- package/lib/typescript/src/components/Modal.d.ts +9 -3
- package/lib/typescript/src/components/Modal.d.ts.map +1 -1
- package/lib/typescript/src/components/Progress.d.ts +1 -1
- package/lib/typescript/src/components/Progress.d.ts.map +1 -1
- package/lib/typescript/src/components/Radio.d.ts +3 -2
- package/lib/typescript/src/components/Radio.d.ts.map +1 -1
- package/lib/typescript/src/components/Screen.d.ts +28 -5
- package/lib/typescript/src/components/Screen.d.ts.map +1 -1
- package/lib/typescript/src/components/Skeleton.d.ts +1 -1
- package/lib/typescript/src/components/Skeleton.d.ts.map +1 -1
- package/lib/typescript/src/components/Surface.d.ts +1 -1
- package/lib/typescript/src/components/Surface.d.ts.map +1 -1
- package/lib/typescript/src/components/TextField.d.ts +11 -4
- package/lib/typescript/src/components/TextField.d.ts.map +1 -1
- package/lib/typescript/src/components/Toast.d.ts +10 -3
- package/lib/typescript/src/components/Toast.d.ts.map +1 -1
- package/lib/typescript/src/components/Toggle.d.ts +3 -2
- package/lib/typescript/src/components/Toggle.d.ts.map +1 -1
- package/lib/typescript/src/components/action-sheet.styles.d.ts +1 -1
- package/lib/typescript/src/components/action-sheet.styles.d.ts.map +1 -1
- package/lib/typescript/src/components/alert.styles.d.ts +14 -14
- package/lib/typescript/src/components/alert.styles.d.ts.map +1 -1
- package/lib/typescript/src/components/avatar.styles.d.ts +36 -1
- package/lib/typescript/src/components/avatar.styles.d.ts.map +1 -1
- package/lib/typescript/src/components/bottom-sheet.styles.d.ts +1 -1
- package/lib/typescript/src/components/bottom-sheet.styles.d.ts.map +1 -1
- package/lib/typescript/src/components/card.styles.d.ts +1 -1
- package/lib/typescript/src/components/card.styles.d.ts.map +1 -1
- package/lib/typescript/src/components/card.validate.d.ts +1 -1
- package/lib/typescript/src/components/card.validate.d.ts.map +1 -1
- package/lib/typescript/src/components/checkbox.styles.d.ts +6 -3
- package/lib/typescript/src/components/checkbox.styles.d.ts.map +1 -1
- package/lib/typescript/src/components/empty-state.styles.d.ts +1 -1
- package/lib/typescript/src/components/empty-state.styles.d.ts.map +1 -1
- package/lib/typescript/src/components/header.styles.d.ts +1 -1
- package/lib/typescript/src/components/header.styles.d.ts.map +1 -1
- package/lib/typescript/src/components/image.styles.d.ts +1 -1
- package/lib/typescript/src/components/image.styles.d.ts.map +1 -1
- package/lib/typescript/src/components/index.d.ts +18 -18
- package/lib/typescript/src/components/index.d.ts.map +1 -1
- package/lib/typescript/src/components/modal.styles.d.ts +13 -2
- package/lib/typescript/src/components/modal.styles.d.ts.map +1 -1
- package/lib/typescript/src/components/progress.styles.d.ts +1 -1
- package/lib/typescript/src/components/progress.styles.d.ts.map +1 -1
- package/lib/typescript/src/components/radio.styles.d.ts +10 -2
- package/lib/typescript/src/components/radio.styles.d.ts.map +1 -1
- package/lib/typescript/src/components/safe-area-registry.d.ts +47 -11
- package/lib/typescript/src/components/safe-area-registry.d.ts.map +1 -1
- package/lib/typescript/src/components/screen.styles.d.ts +1 -1
- package/lib/typescript/src/components/screen.styles.d.ts.map +1 -1
- package/lib/typescript/src/components/skeleton.styles.d.ts +1 -1
- package/lib/typescript/src/components/skeleton.styles.d.ts.map +1 -1
- package/lib/typescript/src/components/status-colors.d.ts +33 -0
- package/lib/typescript/src/components/status-colors.d.ts.map +1 -0
- package/lib/typescript/src/components/surface.styles.d.ts +1 -1
- package/lib/typescript/src/components/surface.styles.d.ts.map +1 -1
- package/lib/typescript/src/components/textfield.styles.d.ts +9 -5
- package/lib/typescript/src/components/textfield.styles.d.ts.map +1 -1
- package/lib/typescript/src/components/toast.styles.d.ts +34 -14
- package/lib/typescript/src/components/toast.styles.d.ts.map +1 -1
- package/lib/typescript/src/components/toggle.styles.d.ts +17 -1
- package/lib/typescript/src/components/toggle.styles.d.ts.map +1 -1
- package/lib/typescript/src/icons/Icon.d.ts +12 -6
- package/lib/typescript/src/icons/Icon.d.ts.map +1 -1
- package/lib/typescript/src/icons/icon.styles.d.ts +14 -2
- package/lib/typescript/src/icons/icon.styles.d.ts.map +1 -1
- package/lib/typescript/src/icons/index.d.ts +3 -3
- package/lib/typescript/src/icons/index.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +4 -4
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/primitives/Button.d.ts +8 -4
- package/lib/typescript/src/primitives/Button.d.ts.map +1 -1
- package/lib/typescript/src/primitives/Metric.d.ts +1 -1
- package/lib/typescript/src/primitives/Metric.d.ts.map +1 -1
- package/lib/typescript/src/primitives/Row.d.ts +2 -2
- package/lib/typescript/src/primitives/Row.d.ts.map +1 -1
- package/lib/typescript/src/primitives/Stack.d.ts +2 -2
- package/lib/typescript/src/primitives/Stack.d.ts.map +1 -1
- package/lib/typescript/src/primitives/Tag.d.ts +2 -2
- package/lib/typescript/src/primitives/Tag.d.ts.map +1 -1
- package/lib/typescript/src/primitives/Text.d.ts +20 -6
- package/lib/typescript/src/primitives/Text.d.ts.map +1 -1
- package/lib/typescript/src/primitives/button.styles.d.ts +44 -7
- package/lib/typescript/src/primitives/button.styles.d.ts.map +1 -1
- package/lib/typescript/src/primitives/index.d.ts +6 -6
- package/lib/typescript/src/primitives/index.d.ts.map +1 -1
- package/lib/typescript/src/primitives/metric.styles.d.ts +1 -1
- package/lib/typescript/src/primitives/metric.styles.d.ts.map +1 -1
- package/lib/typescript/src/primitives/row.styles.d.ts +1 -1
- package/lib/typescript/src/primitives/row.styles.d.ts.map +1 -1
- package/lib/typescript/src/primitives/stack.styles.d.ts +1 -1
- package/lib/typescript/src/primitives/stack.styles.d.ts.map +1 -1
- package/lib/typescript/src/primitives/tag.styles.d.ts +34 -1
- package/lib/typescript/src/primitives/tag.styles.d.ts.map +1 -1
- package/lib/typescript/src/primitives/text.styles.d.ts +2 -4
- package/lib/typescript/src/primitives/text.styles.d.ts.map +1 -1
- package/lib/typescript/src/safe-area/index.d.ts +19 -5
- package/lib/typescript/src/safe-area/index.d.ts.map +1 -1
- package/lib/typescript/src/theme/ThemeProvider.d.ts +2 -2
- package/lib/typescript/src/theme/ThemeProvider.d.ts.map +1 -1
- package/lib/typescript/src/theme/define-theme.d.ts +15 -2
- package/lib/typescript/src/theme/define-theme.d.ts.map +1 -1
- package/lib/typescript/src/theme/index.d.ts +14 -14
- package/lib/typescript/src/theme/index.d.ts.map +1 -1
- package/lib/typescript/src/theme/letter-spacing.d.ts +15 -0
- package/lib/typescript/src/theme/letter-spacing.d.ts.map +1 -0
- package/lib/typescript/src/theme/line-height.d.ts +10 -0
- package/lib/typescript/src/theme/line-height.d.ts.map +1 -1
- package/lib/typescript/src/theme/native-theme.d.ts +1 -1
- package/lib/typescript/src/theme/native-theme.d.ts.map +1 -1
- package/lib/typescript/src/theme/types.d.ts +1 -1
- package/package.json +22 -7
- package/src/a11y/tap-target.ts +38 -0
- package/src/components/ActionSheet.tsx +25 -15
- package/src/components/Alert.tsx +37 -10
- package/src/components/Avatar.tsx +12 -4
- package/src/components/BottomSheet.tsx +16 -13
- package/src/components/Card.tsx +9 -8
- package/src/components/Checkbox.tsx +12 -9
- package/src/components/EmptyState.tsx +7 -6
- package/src/components/Header.tsx +4 -4
- package/src/components/Image.tsx +9 -7
- package/src/components/Modal.tsx +22 -6
- package/src/components/Progress.tsx +5 -4
- package/src/components/Radio.tsx +11 -6
- package/src/components/Screen.tsx +66 -8
- package/src/components/Skeleton.tsx +6 -5
- package/src/components/Surface.tsx +5 -4
- package/src/components/TextField.tsx +25 -7
- package/src/components/Toast.tsx +33 -11
- package/src/components/Toggle.tsx +15 -6
- package/src/components/action-sheet.styles.ts +1 -1
- package/src/components/alert.styles.ts +13 -25
- package/src/components/avatar.styles.ts +25 -3
- package/src/components/bottom-sheet.styles.ts +1 -1
- package/src/components/card.styles.ts +1 -1
- package/src/components/card.validate.ts +2 -2
- package/src/components/checkbox.styles.ts +6 -3
- package/src/components/empty-state.styles.ts +1 -1
- package/src/components/header.styles.ts +1 -1
- package/src/components/image.styles.ts +1 -1
- package/src/components/index.ts +18 -18
- package/src/components/modal.styles.ts +11 -2
- package/src/components/progress.styles.ts +1 -1
- package/src/components/radio.styles.ts +11 -2
- package/src/components/safe-area-registry.ts +81 -14
- package/src/components/screen.styles.ts +1 -1
- package/src/components/skeleton.styles.ts +1 -1
- package/src/components/status-colors.ts +59 -0
- package/src/components/surface.styles.ts +1 -1
- package/src/components/textfield.styles.ts +18 -15
- package/src/components/toast.styles.ts +32 -25
- package/src/components/toggle.styles.ts +21 -1
- package/src/icons/Icon.tsx +13 -8
- package/src/icons/icon.styles.ts +38 -3
- package/src/icons/index.ts +3 -3
- package/src/index.ts +4 -4
- package/src/primitives/Button.tsx +28 -14
- package/src/primitives/Metric.tsx +5 -4
- package/src/primitives/Row.tsx +4 -4
- package/src/primitives/Stack.tsx +4 -4
- package/src/primitives/Tag.tsx +29 -11
- package/src/primitives/Text.tsx +28 -11
- package/src/primitives/_internal/focus-ring.tsx +1 -1
- package/src/primitives/button.styles.ts +63 -10
- package/src/primitives/index.ts +6 -6
- package/src/primitives/metric.styles.ts +3 -3
- package/src/primitives/row.styles.ts +1 -1
- package/src/primitives/stack.styles.ts +1 -1
- package/src/primitives/tag.styles.ts +43 -4
- package/src/primitives/text.styles.ts +7 -12
- package/src/safe-area/index.ts +21 -7
- package/src/theme/ThemeProvider.tsx +8 -5
- package/src/theme/define-theme.ts +15 -2
- package/src/theme/index.ts +14 -17
- package/src/theme/letter-spacing.ts +17 -0
- package/src/theme/line-height.ts +9 -0
- package/src/theme/native-theme.ts +2 -2
- package/src/theme/types.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "melta-app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "melta for APP — melta デザインシステムの React Native UI kit。melta-contracts(デザイン契約)を single source of truth に RN 実装でその契約を満たす。アプリ本体ではなくライブラリ。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://app.melta.tsubotax.com",
|
|
@@ -37,8 +37,16 @@
|
|
|
37
37
|
"import": "./lib/module/safe-area/index.js",
|
|
38
38
|
"default": "./lib/module/safe-area/index.js"
|
|
39
39
|
},
|
|
40
|
-
"./eslint-plugin":
|
|
41
|
-
|
|
40
|
+
"./eslint-plugin": {
|
|
41
|
+
"types": "./eslint-rules/melta.d.mts",
|
|
42
|
+
"import": "./eslint-rules/melta.mjs",
|
|
43
|
+
"default": "./eslint-rules/melta.mjs"
|
|
44
|
+
},
|
|
45
|
+
"./eslint-rules/melta.mjs": {
|
|
46
|
+
"types": "./eslint-rules/melta.d.mts",
|
|
47
|
+
"import": "./eslint-rules/melta.mjs",
|
|
48
|
+
"default": "./eslint-rules/melta.mjs"
|
|
49
|
+
},
|
|
42
50
|
"./package.json": "./package.json"
|
|
43
51
|
},
|
|
44
52
|
"files": [
|
|
@@ -55,8 +63,8 @@
|
|
|
55
63
|
"example"
|
|
56
64
|
],
|
|
57
65
|
"peerDependencies": {
|
|
58
|
-
"react": "
|
|
59
|
-
"react-native": "
|
|
66
|
+
"react": ">=18.2.0",
|
|
67
|
+
"react-native": ">=0.71.0",
|
|
60
68
|
"react-native-safe-area-context": ">=4",
|
|
61
69
|
"react-native-svg": ">=15"
|
|
62
70
|
},
|
|
@@ -69,6 +77,7 @@
|
|
|
69
77
|
}
|
|
70
78
|
},
|
|
71
79
|
"devDependencies": {
|
|
80
|
+
"@arethetypeswrong/cli": "0.18.5",
|
|
72
81
|
"@jest/globals": "^29.7.0",
|
|
73
82
|
"@react-native/babel-preset": "^0.85.3",
|
|
74
83
|
"@react-native/jest-preset": "^0.85.3",
|
|
@@ -77,7 +86,7 @@
|
|
|
77
86
|
"@types/react": "~19.2.2",
|
|
78
87
|
"eslint": "^10.4.1",
|
|
79
88
|
"jest": "^29.7.0",
|
|
80
|
-
"melta-contracts": "^0.
|
|
89
|
+
"melta-contracts": "^0.7.0",
|
|
81
90
|
"react": "19.2.3",
|
|
82
91
|
"react-native": "0.85.3",
|
|
83
92
|
"react-native-builder-bob": "^0.40.13",
|
|
@@ -117,7 +126,10 @@
|
|
|
117
126
|
],
|
|
118
127
|
"testMatch": [
|
|
119
128
|
"<rootDir>/src/**/__tests__/**/*.test.tsx"
|
|
120
|
-
]
|
|
129
|
+
],
|
|
130
|
+
"moduleNameMapper": {
|
|
131
|
+
"^(\\.{1,2}/.*)\\.js$": "$1"
|
|
132
|
+
}
|
|
121
133
|
},
|
|
122
134
|
"react-native-builder-bob": {
|
|
123
135
|
"source": "src",
|
|
@@ -136,5 +148,8 @@
|
|
|
136
148
|
}
|
|
137
149
|
]
|
|
138
150
|
]
|
|
151
|
+
},
|
|
152
|
+
"engines": {
|
|
153
|
+
"node": ">=22"
|
|
139
154
|
}
|
|
140
155
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tap-target — 実効タップ標的 44pt(横断方針)の数値 SSOT。
|
|
3
|
+
*
|
|
4
|
+
* 正本は melta-contracts の `A11Y_MIN_TAP_TARGET_44`(rules.json):
|
|
5
|
+
* 「すべての操作要素は実効タップ標的 44pt を下回らない。視覚寸法は契約どおり据え置き、
|
|
6
|
+
* 当たり判定だけを広げる。app は視覚 24pt + hitSlop 10 の正典パターンか minHeight で下限を保証する
|
|
7
|
+
* (height 固定は fontScale でクリップするので使わない)」。
|
|
8
|
+
*
|
|
9
|
+
* ここは「44」という数と正典パターンの寸法を**一箇所**に置くための pure module。
|
|
10
|
+
* 各コンポーネントの hitSlop 値そのものは **literal で各 `*.styles.ts` に置く**(ここから
|
|
11
|
+
* 自動導出しない)。導出にすると視覚寸法が変わったとき hitSlop も一緒に動いてしまい、
|
|
12
|
+
* conformance テスト(scripts/lib/tap-target-conformance.test.ts)が構造的に fail-open になるため。
|
|
13
|
+
*
|
|
14
|
+
* react-native を import しない純粋モジュール(`*.styles.ts` と同じ規約)。
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/** 実効タップ標的の下限(pt)。WCAG 2.2 SC 2.5.5(AAA)水準を melta の横断方針として採る。 */
|
|
18
|
+
export const MIN_TAP_TARGET = 44;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 正典パターン: 視覚 24pt の箱 + hitSlop 10 → 実効 44pt。
|
|
22
|
+
* 背景を持たない小さな操作要素(Tag removable の × / Toast の × / Alert の × / Modal の ×)はこれ。
|
|
23
|
+
*/
|
|
24
|
+
export const CANONICAL_TAP_TARGET = {
|
|
25
|
+
minWidth: 24,
|
|
26
|
+
minHeight: 24,
|
|
27
|
+
hitSlop: 10,
|
|
28
|
+
} as const;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 視覚寸法 `visual` を実効 44pt に届かせるのに必要な**片側** hitSlop(切り上げ = 安全側)。
|
|
32
|
+
*
|
|
33
|
+
* ⚠️ 実装の hitSlop 値をこの関数で計算しないこと(上のコメント参照)。
|
|
34
|
+
* 使うのは (a) 値を決めるときの検算、(b) conformance テストの期待値算出。
|
|
35
|
+
*/
|
|
36
|
+
export function requiredHitSlop(visual: number): number {
|
|
37
|
+
return Math.max(0, Math.ceil((MIN_TAP_TARGET - visual) / 2));
|
|
38
|
+
}
|
|
@@ -9,22 +9,27 @@
|
|
|
9
9
|
* 色だけでなくラベル文言でも破壊的操作と分かる文言にするのは呼び出し側の責務(contract a11y)。
|
|
10
10
|
* - 先頭 action の borderTop は title が無い場合は消す(title がある時だけ区切りとして意味を持つ)。
|
|
11
11
|
* - action の onPress 後は onClose も呼ぶ(選択 = シートを閉じる)。
|
|
12
|
-
* -
|
|
12
|
+
* - 下余白(ホームインジケータ)は SafeArea で確保する(recipe description 参照)。位置は
|
|
13
|
+
* **最下部の面である cancel ブロックの内側**(BottomSheet と同じ「内側」方針に統一)。
|
|
14
|
+
* 0.5.x までは sheet 群の外側に置いていたため、iOS では inset 帯に overlay の黒が見えていた。
|
|
15
|
+
* 内側に移したことで inset 帯まで bg-surface で塗られる代わり、cancel ブロックは
|
|
16
|
+
* inset のぶん背が高くなる(ラベルは上寄りに見える)=意図した見た目の変更。
|
|
17
|
+
* edge は bottom + 左右のみ(top は overlay 側で、シートは上端に接しない)。
|
|
18
|
+
* SafeArea 実装は registry 経由(safe-area-registry.ts)— "melta-app/safe-area" を
|
|
19
|
+
* 有効化したアプリでは Android でも inset が入る(RN core の SafeAreaView は Android で no-op)。
|
|
13
20
|
* - sheet 側は onStartShouldSetResponder で touch を claim し、overlay tap でだけ閉じる。
|
|
14
21
|
* - style の決定は pure resolver(action-sheet.styles.ts)に分離 — recipe との機械照合対象。
|
|
15
22
|
*/
|
|
16
23
|
|
|
17
24
|
import { useMemo } from "react";
|
|
18
|
-
import {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
import { CONTRACTS } from "../contracts/contract-types";
|
|
27
|
-
import { resolveActionSheetStyle, type ActionSheetStyle } from "./action-sheet.styles";
|
|
25
|
+
import { Modal as RNModal, Pressable, Text as RNText, View } from "react-native";
|
|
26
|
+
import { useTheme } from "../theme/index.js";
|
|
27
|
+
import { CONTRACTS } from "../contracts/contract-types.js";
|
|
28
|
+
import { resolveActionSheetStyle, type ActionSheetStyle } from "./action-sheet.styles.js";
|
|
29
|
+
import { resolveSafeAreaView, type SafeAreaEdge } from "./safe-area-registry.js";
|
|
30
|
+
|
|
31
|
+
/** シートは画面下端に密着するので top inset は不要。 */
|
|
32
|
+
const SHEET_EDGES: readonly SafeAreaEdge[] = ["bottom", "left", "right"];
|
|
28
33
|
|
|
29
34
|
export interface ActionSheetAction {
|
|
30
35
|
label: string;
|
|
@@ -61,6 +66,8 @@ export function ActionSheet({
|
|
|
61
66
|
[theme, mode],
|
|
62
67
|
);
|
|
63
68
|
|
|
69
|
+
const SafeArea = resolveSafeAreaView(SHEET_EDGES);
|
|
70
|
+
|
|
64
71
|
return (
|
|
65
72
|
<RNModal
|
|
66
73
|
visible={visible}
|
|
@@ -70,8 +77,8 @@ export function ActionSheet({
|
|
|
70
77
|
statusBarTranslucent
|
|
71
78
|
>
|
|
72
79
|
<Pressable style={styles.overlayStyle} onPress={onClose} accessible={false}>
|
|
73
|
-
{/* sheet 群(sheet + cancel):
|
|
74
|
-
<
|
|
80
|
+
{/* sheet 群(sheet + cancel): touch を claim して overlay へ流さない */}
|
|
81
|
+
<View
|
|
75
82
|
accessibilityViewIsModal
|
|
76
83
|
onStartShouldSetResponder={() => true}
|
|
77
84
|
testID={testID}
|
|
@@ -101,9 +108,12 @@ export function ActionSheet({
|
|
|
101
108
|
))}
|
|
102
109
|
</View>
|
|
103
110
|
<Pressable accessibilityRole="button" onPress={onClose} style={styles.cancelStyle}>
|
|
104
|
-
|
|
111
|
+
{/* SafeArea は最下部の面(cancel)の内側 = inset 帯を overlay の黒でなく bg-surface で塗る */}
|
|
112
|
+
<SafeArea>
|
|
113
|
+
<RNText style={styles.cancelTextStyle}>{cancelLabel}</RNText>
|
|
114
|
+
</SafeArea>
|
|
105
115
|
</Pressable>
|
|
106
|
-
</
|
|
116
|
+
</View>
|
|
107
117
|
</Pressable>
|
|
108
118
|
</RNModal>
|
|
109
119
|
);
|
package/src/components/Alert.tsx
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* - RN では border を持たず bg subtle + text 色で表現(status token に border 用の中間色が無い、
|
|
6
6
|
* recipe description)。COLOR_ONLY_FORBIDDEN は title/message のテキストと icon slot で担保する設計。
|
|
7
7
|
* - icon は ReactNode slot(EmptyState 前例。Icon を差すなら melta-app/icons を利用者が opt-in)。
|
|
8
|
+
* - × の accessibilityLabel は既定 "閉じる"(closeAccessibilityLabel で差し替え可能)。
|
|
9
|
+
* 当たり判定は正典パターン(24 + hitSlop 10 = 44pt)。
|
|
8
10
|
* - a11y(§2 web→RN mapping): web の role=alert(error/warning) / status(info/success) を、
|
|
9
11
|
* error/warning → accessibilityRole="alert"、info/success → accessibilityLiveRegion="polite"
|
|
10
12
|
* (Android のみ有効。RN に status role が無いための写像)に落とす。
|
|
@@ -15,10 +17,15 @@
|
|
|
15
17
|
|
|
16
18
|
import { useMemo, type ReactNode } from "react";
|
|
17
19
|
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 {
|
|
20
|
+
import { Text } from "../primitives/Text.js";
|
|
21
|
+
import { useTheme } from "../theme/index.js";
|
|
22
|
+
import { CONTRACTS } from "../contracts/contract-types.js";
|
|
23
|
+
import {
|
|
24
|
+
ALERT_CLOSE_TAP_TARGET,
|
|
25
|
+
ALERT_SPEC,
|
|
26
|
+
resolveAlertStyles,
|
|
27
|
+
type AlertVariant,
|
|
28
|
+
} from "./alert.styles.js";
|
|
22
29
|
|
|
23
30
|
interface AlertProps {
|
|
24
31
|
/** 通知タイプ(contract variant)。 */
|
|
@@ -31,11 +38,25 @@ interface AlertProps {
|
|
|
31
38
|
icon?: ReactNode;
|
|
32
39
|
/** あれば右端に閉じるボタン(Pressable の ×)を出す。 */
|
|
33
40
|
onClose?: () => void;
|
|
41
|
+
/**
|
|
42
|
+
* × ボタンの accessibilityLabel(i18n フック)。既定は日本語 "閉じる"
|
|
43
|
+
* (既存アプリの読み上げを変えないため据え置き)。
|
|
44
|
+
*/
|
|
45
|
+
closeAccessibilityLabel?: string;
|
|
34
46
|
style?: StyleProp<ViewStyle>;
|
|
35
47
|
testID?: string;
|
|
36
48
|
}
|
|
37
49
|
|
|
38
|
-
export function Alert({
|
|
50
|
+
export function Alert({
|
|
51
|
+
variant,
|
|
52
|
+
title,
|
|
53
|
+
message,
|
|
54
|
+
icon,
|
|
55
|
+
onClose,
|
|
56
|
+
closeAccessibilityLabel = "閉じる",
|
|
57
|
+
style,
|
|
58
|
+
testID,
|
|
59
|
+
}: AlertProps) {
|
|
39
60
|
const { theme, mode } = useTheme();
|
|
40
61
|
const styles = useMemo(() => resolveAlertStyles(theme, mode, variant), [theme, mode, variant]);
|
|
41
62
|
const urgent = variant === "error" || variant === "warning";
|
|
@@ -63,13 +84,18 @@ export function Alert({ variant, title, message, icon, onClose, style, testID }:
|
|
|
63
84
|
</Text>
|
|
64
85
|
</View>
|
|
65
86
|
{onClose != null && (
|
|
66
|
-
// 最小タップターゲット確保:
|
|
87
|
+
// 最小タップターゲット確保: 正典パターン 24 + hitSlop 10 = 実効 44pt(Tag removable と同型)。
|
|
67
88
|
<Pressable
|
|
68
89
|
onPress={onClose}
|
|
69
90
|
accessibilityRole="button"
|
|
70
|
-
accessibilityLabel=
|
|
71
|
-
hitSlop={
|
|
72
|
-
style={{
|
|
91
|
+
accessibilityLabel={closeAccessibilityLabel}
|
|
92
|
+
hitSlop={ALERT_CLOSE_TAP_TARGET.hitSlop}
|
|
93
|
+
style={{
|
|
94
|
+
minWidth: ALERT_CLOSE_TAP_TARGET.minWidth,
|
|
95
|
+
minHeight: ALERT_CLOSE_TAP_TARGET.minHeight,
|
|
96
|
+
alignItems: "center",
|
|
97
|
+
justifyContent: "center",
|
|
98
|
+
}}
|
|
73
99
|
>
|
|
74
100
|
<Text variant="xs" style={{ color: styles.messageStyle.color }}>
|
|
75
101
|
×
|
|
@@ -80,5 +106,6 @@ export function Alert({ variant, title, message, icon, onClose, style, testID }:
|
|
|
80
106
|
);
|
|
81
107
|
}
|
|
82
108
|
|
|
83
|
-
//
|
|
109
|
+
// conformance test 用の contract メタ(§2 A-3)。scripts/lib/conformance.test.ts が
|
|
110
|
+
// 「この宣言が正しい contract を指しているか」を静的スキャンで照合する。
|
|
84
111
|
Alert.__contract = CONTRACTS.alert;
|
|
@@ -18,16 +18,17 @@ import {
|
|
|
18
18
|
type StyleProp,
|
|
19
19
|
type ViewStyle,
|
|
20
20
|
} from "react-native";
|
|
21
|
-
import { useTheme } from "../theme";
|
|
22
|
-
import { CONTRACTS } from "../contracts/contract-types";
|
|
21
|
+
import { useTheme } from "../theme/index.js";
|
|
22
|
+
import { CONTRACTS } from "../contracts/contract-types.js";
|
|
23
23
|
import {
|
|
24
|
+
AVATAR_INITIALS_MAX_FONT_SCALE,
|
|
24
25
|
resolveAvatarGroupStyle,
|
|
25
26
|
resolveAvatarStatusColor,
|
|
26
27
|
resolveAvatarStyle,
|
|
27
28
|
type AvatarSize,
|
|
28
29
|
type AvatarStatus,
|
|
29
30
|
type AvatarStyleResult,
|
|
30
|
-
} from "./avatar.styles";
|
|
31
|
+
} from "./avatar.styles.js";
|
|
31
32
|
|
|
32
33
|
interface AvatarProps {
|
|
33
34
|
/** 表示名(initials の生成元 + accessibilityLabel。contract a11y required)。 */
|
|
@@ -73,7 +74,14 @@ export function Avatar({ name, source, size = "medium", status, style, testID }:
|
|
|
73
74
|
{source ? (
|
|
74
75
|
<RNImage source={source} style={{ width: "100%", height: "100%" }} />
|
|
75
76
|
) : (
|
|
76
|
-
|
|
77
|
+
// 器(円)は fontScale で伸びないので、文字側に size 別の上限を掛けて溢れを防ぐ
|
|
78
|
+
// (根拠と導出は avatar.styles.ts の AVATAR_INITIALS_MAX_FONT_SCALE)。
|
|
79
|
+
<RNText
|
|
80
|
+
style={resolved.text}
|
|
81
|
+
maxFontSizeMultiplier={AVATAR_INITIALS_MAX_FONT_SCALE[size]}
|
|
82
|
+
>
|
|
83
|
+
{toInitials(name)}
|
|
84
|
+
</RNText>
|
|
77
85
|
)}
|
|
78
86
|
</View>
|
|
79
87
|
{status ? (
|
|
@@ -6,23 +6,24 @@
|
|
|
6
6
|
* - grabber は装飾バー。a11y ツリーから除外する(importantForAccessibility="no" +
|
|
7
7
|
* accessibilityElementsHidden。recipe description 参照)。
|
|
8
8
|
* - スワイプで閉じるジェスチャは実装しない(overlay tap + Android back で閉じる、Phase 外)。
|
|
9
|
-
* - sheet は下端密着(bg-surface)のため、
|
|
9
|
+
* - sheet は下端密着(bg-surface)のため、SafeArea を sheet の内側に置いて
|
|
10
10
|
* 下余白(ホームインジケータ)まで sheet 背景で覆いつつ content を安全域に収める。
|
|
11
|
+
* edge は bottom + 左右のみ(top は overlay 側で、シートは上端に接しない)。
|
|
12
|
+
* SafeArea 実装は registry 経由(safe-area-registry.ts)— "melta-app/safe-area" を
|
|
13
|
+
* 有効化したアプリでは Android でも inset が入る(RN core の SafeAreaView は Android で no-op)。
|
|
11
14
|
* - sheet 側は onStartShouldSetResponder で touch を claim し、overlay tap でだけ閉じる。
|
|
12
15
|
* - style の決定は pure resolver(bottom-sheet.styles.ts)に分離 — recipe との機械照合対象。
|
|
13
16
|
*/
|
|
14
17
|
|
|
15
18
|
import { useMemo, type ReactNode } from "react";
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
import { CONTRACTS } from "../contracts/contract-types";
|
|
25
|
-
import { resolveBottomSheetStyle, type BottomSheetStyle } from "./bottom-sheet.styles";
|
|
19
|
+
import { Modal as RNModal, Pressable, Text as RNText, View } from "react-native";
|
|
20
|
+
import { useTheme } from "../theme/index.js";
|
|
21
|
+
import { CONTRACTS } from "../contracts/contract-types.js";
|
|
22
|
+
import { resolveBottomSheetStyle, type BottomSheetStyle } from "./bottom-sheet.styles.js";
|
|
23
|
+
import { resolveSafeAreaView, type SafeAreaEdge } from "./safe-area-registry.js";
|
|
24
|
+
|
|
25
|
+
/** シートは画面下端に密着するので top inset は不要。 */
|
|
26
|
+
const SHEET_EDGES: readonly SafeAreaEdge[] = ["bottom", "left", "right"];
|
|
26
27
|
|
|
27
28
|
interface BottomSheetProps {
|
|
28
29
|
visible: boolean;
|
|
@@ -43,6 +44,8 @@ export function BottomSheet({ visible, onClose, title, testID, children }: Botto
|
|
|
43
44
|
[theme, mode],
|
|
44
45
|
);
|
|
45
46
|
|
|
47
|
+
const SafeArea = resolveSafeAreaView(SHEET_EDGES);
|
|
48
|
+
|
|
46
49
|
return (
|
|
47
50
|
<RNModal
|
|
48
51
|
visible={visible}
|
|
@@ -58,7 +61,7 @@ export function BottomSheet({ visible, onClose, title, testID, children }: Botto
|
|
|
58
61
|
onStartShouldSetResponder={() => true}
|
|
59
62
|
testID={testID}
|
|
60
63
|
>
|
|
61
|
-
<
|
|
64
|
+
<SafeArea>
|
|
62
65
|
{/* grabber: 装飾のみ(スワイプ操作は持たないため a11y から隠す) */}
|
|
63
66
|
<View
|
|
64
67
|
style={styles.grabberStyle}
|
|
@@ -71,7 +74,7 @@ export function BottomSheet({ visible, onClose, title, testID, children }: Botto
|
|
|
71
74
|
</RNText>
|
|
72
75
|
)}
|
|
73
76
|
<View style={styles.contentStyle}>{children}</View>
|
|
74
|
-
</
|
|
77
|
+
</SafeArea>
|
|
75
78
|
</View>
|
|
76
79
|
</Pressable>
|
|
77
80
|
</RNModal>
|
package/src/components/Card.tsx
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* scripts/lib/card-conformance.test.ts が行う。
|
|
8
8
|
* - variant: basic/media は非インタラクティブ、action/link は Pressable。
|
|
9
9
|
* action/link は pressed 時 elevation.sm→md(§2 hover→pressed mapping)。
|
|
10
|
-
* focus-within は RN
|
|
10
|
+
* focus-within は RN 非サポートとして明示 drop(§2)。
|
|
11
11
|
* - 型で action/link は onPress 必須を強制(§4 の DU 精神。無言で非対話に落ちる事故を防ぐ)。
|
|
12
12
|
* - **カード面自体は操作要素にしない**(contract 2.1.0 の a11y.required)。面の Pressable は
|
|
13
13
|
* `accessibilityRole` を名乗らず `accessible={false}`。理由は2つ:
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* react-native-web では実際に `<article>` 要素になる)。accessibilityRole 系とは別系統。
|
|
22
22
|
* - **focus ring は Card では描かない**。フォーカスを受けるのは `primaryAction` 側で、
|
|
23
23
|
* melta の Button は自前の ring を持つ。カードにも出すと1回のフォーカスで ring が二重に描かれる。
|
|
24
|
-
* contract の focus-within は RN
|
|
24
|
+
* contract の focus-within は RN 非サポートとして明示 drop 済み(app recipe に記録あり)で、
|
|
25
25
|
* web だけ動く実装を残すとプラットフォーム非対称になるため、実装ごと持たない。
|
|
26
26
|
* - `accessibilityLabel` は 0.5.0 で削除した。面が `accessible={false}` なので native では
|
|
27
27
|
* 原理的に読まれず、web だけ `aria-label` として効く非対称な prop だった。
|
|
@@ -33,11 +33,11 @@
|
|
|
33
33
|
|
|
34
34
|
import { useEffect, type ReactElement, type ReactNode } from "react";
|
|
35
35
|
import { Pressable, View, type StyleProp, type ViewStyle } from "react-native";
|
|
36
|
-
import { useTheme } from "../theme";
|
|
37
|
-
import { CONTRACTS } from "../contracts/contract-types";
|
|
38
|
-
import { isDev } from "../theme/define-theme";
|
|
39
|
-
import { CARD_INTERACTIVE, resolveCardShape, resolveCardBodyStyle } from "./card.styles";
|
|
40
|
-
import { validateCardProps } from "./card.validate";
|
|
36
|
+
import { useTheme } from "../theme/index.js";
|
|
37
|
+
import { CONTRACTS } from "../contracts/contract-types.js";
|
|
38
|
+
import { isDev } from "../theme/define-theme.js";
|
|
39
|
+
import { CARD_INTERACTIVE, resolveCardShape, resolveCardBodyStyle } from "./card.styles.js";
|
|
40
|
+
import { validateCardProps } from "./card.validate.js";
|
|
41
41
|
|
|
42
42
|
/**
|
|
43
43
|
* 同じ問題を毎レンダー報告しないためのラッチ。
|
|
@@ -144,5 +144,6 @@ export function Card({
|
|
|
144
144
|
);
|
|
145
145
|
}
|
|
146
146
|
|
|
147
|
-
//
|
|
147
|
+
// conformance test 用の contract メタ(§2 A-3)。scripts/lib/conformance.test.ts が
|
|
148
|
+
// 「この宣言が正しい contract を指しているか」を静的スキャンで照合する。
|
|
148
149
|
Card.__contract = CONTRACTS.card;
|
|
@@ -4,24 +4,24 @@
|
|
|
4
4
|
* - RN にネイティブ checkbox が無いため Pressable(行全体タップ)+ 描画で実装する。
|
|
5
5
|
* checkmark は rotate した View の L 字 border、indeterminate は横棒
|
|
6
6
|
* (react-native-svg 非依存 = 本体エントリの依存ゼロ維持。recipe description 参照)。
|
|
7
|
-
* - box は 20px(web の 16px
|
|
8
|
-
*
|
|
7
|
+
* - box は 20px(web の 16px より大きいのはタッチ前提)。実効タップ標的は行の
|
|
8
|
+
* minHeight 44 で確保する(規約 10-(b): 背景を持たない行は hitSlop でなく寸法)。
|
|
9
9
|
* - label 必須(FORM_NO_LABEL_OMIT を props 型で強制)。label は Text primitive
|
|
10
10
|
* (base/text-default = recipe labelStyle と同値)。
|
|
11
11
|
* - states(contract → prop の写像): checked/unchecked → checked:boolean、
|
|
12
12
|
* indeterminate → indeterminate:boolean(variant "indeterminate"。a11y は "mixed")、
|
|
13
13
|
* error → error:boolean(borderColor 差分、checked より優先)、disabled → prop
|
|
14
|
-
* (opacity 0.5 + 非活性)。focus
|
|
14
|
+
* (opacity 0.5 + 非活性)。focus は非対応(recipe にも focus 差分なし)。
|
|
15
15
|
* - 色・寸法の決定は pure resolver(checkbox.styles.ts)に分離。
|
|
16
16
|
* recipes/app/checkbox.recipe.json との機械照合は scripts/lib/checkbox-conformance.test.ts が行う。
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import { useMemo } from "react";
|
|
20
20
|
import { Pressable, View } from "react-native";
|
|
21
|
-
import { Text } from "../primitives/Text";
|
|
22
|
-
import { useTheme } from "../theme";
|
|
23
|
-
import { CONTRACTS } from "../contracts/contract-types";
|
|
24
|
-
import { CHECKBOX_SPEC, resolveCheckboxStyle } from "./checkbox.styles";
|
|
21
|
+
import { Text } from "../primitives/Text.js";
|
|
22
|
+
import { useTheme } from "../theme/index.js";
|
|
23
|
+
import { CONTRACTS } from "../contracts/contract-types.js";
|
|
24
|
+
import { CHECKBOX_SPEC, resolveCheckboxStyle } from "./checkbox.styles.js";
|
|
25
25
|
|
|
26
26
|
/** checkmark / 横棒の幾何値(recipe に token 化されていない描画トリック側の定数)。 */
|
|
27
27
|
const CHECKMARK = { width: 10, height: 6, borderWidth: 2, offsetY: -1 } as const;
|
|
@@ -59,7 +59,6 @@ export function Checkbox({
|
|
|
59
59
|
<Pressable
|
|
60
60
|
onPress={() => onChange(!checked)}
|
|
61
61
|
disabled={disabled}
|
|
62
|
-
hitSlop={CHECKBOX_SPEC.hitSlop}
|
|
63
62
|
accessibilityRole="checkbox"
|
|
64
63
|
accessibilityState={{ checked: indeterminate ? "mixed" : checked, disabled }}
|
|
65
64
|
accessibilityLabel={label}
|
|
@@ -68,6 +67,9 @@ export function Checkbox({
|
|
|
68
67
|
flexDirection: "row",
|
|
69
68
|
alignItems: "center",
|
|
70
69
|
gap: resolved.gap,
|
|
70
|
+
// 実効タップ標的 44pt。背景を持たない行なので hitSlop でなく minHeight(規約 10-(b)、
|
|
71
|
+
// 縦積みの隣接行と当たり判定が重ならない)
|
|
72
|
+
minHeight: CHECKBOX_SPEC.rowMinHeight,
|
|
71
73
|
opacity: disabled ? CHECKBOX_SPEC.disabledOpacity : 1,
|
|
72
74
|
}}
|
|
73
75
|
>
|
|
@@ -103,5 +105,6 @@ export function Checkbox({
|
|
|
103
105
|
);
|
|
104
106
|
}
|
|
105
107
|
|
|
106
|
-
//
|
|
108
|
+
// conformance test 用の contract メタ(§2 A-3)。scripts/lib/conformance.test.ts が
|
|
109
|
+
// 「この宣言が正しい contract を指しているか」を静的スキャンで照合する。
|
|
107
110
|
Checkbox.__contract = CONTRACTS.checkbox;
|
|
@@ -12,11 +12,11 @@
|
|
|
12
12
|
|
|
13
13
|
import { useMemo, type ReactNode } from "react";
|
|
14
14
|
import { View, type StyleProp, type ViewStyle } from "react-native";
|
|
15
|
-
import { Text } from "../primitives/Text";
|
|
16
|
-
import { Button } from "../primitives/Button";
|
|
17
|
-
import { useTheme } from "../theme";
|
|
18
|
-
import { CONTRACTS } from "../contracts/contract-types";
|
|
19
|
-
import { EMPTY_STATE_SPEC, resolveEmptyStateStyles } from "./empty-state.styles";
|
|
15
|
+
import { Text } from "../primitives/Text.js";
|
|
16
|
+
import { Button } from "../primitives/Button.js";
|
|
17
|
+
import { useTheme } from "../theme/index.js";
|
|
18
|
+
import { CONTRACTS } from "../contracts/contract-types.js";
|
|
19
|
+
import { EMPTY_STATE_SPEC, resolveEmptyStateStyles } from "./empty-state.styles.js";
|
|
20
20
|
|
|
21
21
|
interface EmptyStateProps {
|
|
22
22
|
icon?: ReactNode;
|
|
@@ -60,5 +60,6 @@ export function EmptyState({ icon, title, description, action, style, testID }:
|
|
|
60
60
|
);
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
//
|
|
63
|
+
// conformance test 用の contract メタ(§2 A-3)。scripts/lib/conformance.test.ts が
|
|
64
|
+
// 「この宣言が正しい contract を指しているか」を静的スキャンで照合する。
|
|
64
65
|
EmptyState.__contract = CONTRACTS.emptyState;
|
|
@@ -11,10 +11,10 @@
|
|
|
11
11
|
|
|
12
12
|
import { useMemo, type ReactNode } from "react";
|
|
13
13
|
import { View, type StyleProp, type ViewStyle } from "react-native";
|
|
14
|
-
import { useTheme } from "../theme";
|
|
15
|
-
import { CONTRACTS } from "../contracts/contract-types";
|
|
16
|
-
import { Text } from "../primitives/Text";
|
|
17
|
-
import { resolveHeaderStyle, type HeaderStyle } from "./header.styles";
|
|
14
|
+
import { useTheme } from "../theme/index.js";
|
|
15
|
+
import { CONTRACTS } from "../contracts/contract-types.js";
|
|
16
|
+
import { Text } from "../primitives/Text.js";
|
|
17
|
+
import { resolveHeaderStyle, type HeaderStyle } from "./header.styles.js";
|
|
18
18
|
|
|
19
19
|
interface HeaderProps {
|
|
20
20
|
/** 画面タイトル(heading として読み上げ)。 */
|
package/src/components/Image.tsx
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Image — 画像表示 component(設計書 §1)。contract: image(app 先行定義)。
|
|
3
3
|
*
|
|
4
|
-
* -
|
|
5
|
-
*
|
|
4
|
+
* - 実装は RN コア Image(依存ゼロ維持)。expo-image への差し替えは blurhash / 高度 cache が
|
|
5
|
+
* 要件化してから(未着手)。差し替えコスト最小化のためここで包む(呼び出し側は melta-app の
|
|
6
|
+
* Image だけ見る)。
|
|
6
7
|
* - contentFit("cover"|"contain") → RN の resizeMode に変換。
|
|
7
8
|
* - onError 時に fallback(ReactNode)を表示。fallback 未指定なら何も出さない。
|
|
8
9
|
* - radius は token キーのみ(生数値不可、§5)。shape の解決は pure resolver
|
|
@@ -19,10 +20,10 @@ import {
|
|
|
19
20
|
type StyleProp,
|
|
20
21
|
type ViewStyle,
|
|
21
22
|
} from "react-native";
|
|
22
|
-
import { useTheme } from "../theme";
|
|
23
|
-
import type { RadiusKey } from "../theme";
|
|
24
|
-
import { CONTRACTS } from "../contracts/contract-types";
|
|
25
|
-
import { resolveImageShape } from "./image.styles";
|
|
23
|
+
import { useTheme } from "../theme/index.js";
|
|
24
|
+
import type { RadiusKey } from "../theme/index.js";
|
|
25
|
+
import { CONTRACTS } from "../contracts/contract-types.js";
|
|
26
|
+
import { resolveImageShape } from "./image.styles.js";
|
|
26
27
|
|
|
27
28
|
type ContentFit = "cover" | "contain";
|
|
28
29
|
|
|
@@ -85,5 +86,6 @@ export function Image({
|
|
|
85
86
|
);
|
|
86
87
|
}
|
|
87
88
|
|
|
88
|
-
//
|
|
89
|
+
// conformance test 用の contract メタ(§2 A-3)。scripts/lib/conformance.test.ts が
|
|
90
|
+
// 「この宣言が正しい contract を指しているか」を静的スキャンで照合する。
|
|
89
91
|
Image.__contract = CONTRACTS.image;
|
package/src/components/Modal.tsx
CHANGED
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
* Escape → Android back(onRequestClose)、open/closing state → RN Modal の visible + animationType。
|
|
9
9
|
* - overlay tap で閉じる(MODAL_OVERLAY_REQUIRED)。panel 側は onStartShouldSetResponder で
|
|
10
10
|
* touch を claim し、overlay への伝播を止める(panel 内 tap では閉じない)。
|
|
11
|
-
* - title 行の右端に閉じるボタン「×」(MODAL_CLOSE_REQUIRED、accessibilityLabel
|
|
11
|
+
* - title 行の右端に閉じるボタン「×」(MODAL_CLOSE_REQUIRED、accessibilityLabel は既定 "閉じる"、
|
|
12
|
+
* closeAccessibilityLabel で差し替え可能)。当たり判定は正典パターン(24 + hitSlop 10 = 44pt)。
|
|
12
13
|
* title は melta の Text ではなく titleStyle 直(recipe が typography を持つ)。
|
|
13
14
|
* - style の決定は pure resolver(modal.styles.ts)に分離 — recipe との機械照合対象。
|
|
14
15
|
* title 行の row layout と × の見た目は recipe 外の構造 glue(token 経由で解決)。
|
|
@@ -21,14 +22,15 @@ import {
|
|
|
21
22
|
Text as RNText,
|
|
22
23
|
View,
|
|
23
24
|
} from "react-native";
|
|
24
|
-
import { useTheme } from "../theme";
|
|
25
|
-
import { CONTRACTS } from "../contracts/contract-types";
|
|
25
|
+
import { useTheme } from "../theme/index.js";
|
|
26
|
+
import { CONTRACTS } from "../contracts/contract-types.js";
|
|
26
27
|
import {
|
|
28
|
+
MODAL_CLOSE_TAP_TARGET,
|
|
27
29
|
resolveModalStyle,
|
|
28
30
|
type ModalSize,
|
|
29
31
|
type ModalStyle,
|
|
30
32
|
type ModalVariant,
|
|
31
|
-
} from "./modal.styles";
|
|
33
|
+
} from "./modal.styles.js";
|
|
32
34
|
|
|
33
35
|
interface ModalProps {
|
|
34
36
|
/** 表示制御(契約の open state に対応。閉じるアニメは RN Modal に委譲)。 */
|
|
@@ -43,6 +45,11 @@ interface ModalProps {
|
|
|
43
45
|
size?: ModalSize;
|
|
44
46
|
/** アクション群(任意)。footerStyle の row に載る。 */
|
|
45
47
|
footer?: ReactNode;
|
|
48
|
+
/**
|
|
49
|
+
* × ボタンの accessibilityLabel(i18n フック)。既定は日本語 "閉じる"
|
|
50
|
+
* (既存アプリの読み上げを変えないため据え置き)。
|
|
51
|
+
*/
|
|
52
|
+
closeAccessibilityLabel?: string;
|
|
46
53
|
testID?: string;
|
|
47
54
|
/** body。 */
|
|
48
55
|
children: ReactNode;
|
|
@@ -55,6 +62,7 @@ export function Modal({
|
|
|
55
62
|
variant = "confirmation",
|
|
56
63
|
size = "medium",
|
|
57
64
|
footer,
|
|
65
|
+
closeAccessibilityLabel = "閉じる",
|
|
58
66
|
testID,
|
|
59
67
|
children,
|
|
60
68
|
}: ModalProps) {
|
|
@@ -86,11 +94,19 @@ export function Modal({
|
|
|
86
94
|
<RNText style={[styles.titleStyle, { flex: 1 }]} accessibilityRole="header">
|
|
87
95
|
{title}
|
|
88
96
|
</RNText>
|
|
97
|
+
{/* 最小タップターゲット確保: 正典パターン(視覚 24 + hitSlop 10 = 実効 44)。
|
|
98
|
+
× のグリフは幅 13pt 前後しかないので、箱の下限を持たせないと 44 に届かない。 */}
|
|
89
99
|
<Pressable
|
|
90
100
|
onPress={onClose}
|
|
91
101
|
accessibilityRole="button"
|
|
92
|
-
accessibilityLabel=
|
|
93
|
-
hitSlop={
|
|
102
|
+
accessibilityLabel={closeAccessibilityLabel}
|
|
103
|
+
hitSlop={MODAL_CLOSE_TAP_TARGET.hitSlop}
|
|
104
|
+
style={{
|
|
105
|
+
minWidth: MODAL_CLOSE_TAP_TARGET.minWidth,
|
|
106
|
+
minHeight: MODAL_CLOSE_TAP_TARGET.minHeight,
|
|
107
|
+
alignItems: "center",
|
|
108
|
+
justifyContent: "center",
|
|
109
|
+
}}
|
|
94
110
|
>
|
|
95
111
|
<RNText
|
|
96
112
|
style={{
|
|
@@ -23,14 +23,14 @@ import {
|
|
|
23
23
|
type StyleProp,
|
|
24
24
|
type ViewStyle,
|
|
25
25
|
} from "react-native";
|
|
26
|
-
import { useTheme } from "../theme";
|
|
27
|
-
import { CONTRACTS } from "../contracts/contract-types";
|
|
26
|
+
import { useTheme } from "../theme/index.js";
|
|
27
|
+
import { CONTRACTS } from "../contracts/contract-types.js";
|
|
28
28
|
import {
|
|
29
29
|
PROGRESS_INDETERMINATE_FILL_RATIO,
|
|
30
30
|
clampProgressValue,
|
|
31
31
|
resolveProgressStyles,
|
|
32
32
|
type ProgressVariant,
|
|
33
|
-
} from "./progress.styles";
|
|
33
|
+
} from "./progress.styles.js";
|
|
34
34
|
|
|
35
35
|
interface ProgressProps {
|
|
36
36
|
/** 進捗タイプ(contract variant)。default "primary"。 */
|
|
@@ -120,5 +120,6 @@ export function Progress({ variant = "primary", value, label, style, testID }: P
|
|
|
120
120
|
);
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
//
|
|
123
|
+
// conformance test 用の contract メタ(§2 A-3)。scripts/lib/conformance.test.ts が
|
|
124
|
+
// 「この宣言が正しい contract を指しているか」を静的スキャンで照合する。
|
|
124
125
|
Progress.__contract = CONTRACTS.progress;
|