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/src/components/Radio.tsx
CHANGED
|
@@ -7,10 +7,11 @@
|
|
|
7
7
|
* - variant はレイアウト: vertical(標準)/ horizontal(wrap する横並び)/ card-style
|
|
8
8
|
* (各 option を枠付きカードにする。web の grid 2col は RN では縦積みに読み替え)。
|
|
9
9
|
* - RN にネイティブ radio が無いため各 option は Pressable + 描画(circle + 内側 dot)。
|
|
10
|
+
* option 行は minHeight 44pt(RADIO_SPEC.optionMinHeight)で実効タップ標的を確保する。
|
|
10
11
|
* - states(contract → prop の写像): selected/unselected → value との一致、
|
|
11
12
|
* error → error?: string(circle の borderColor 差分 + errorText 表示。selected と併用時は
|
|
12
13
|
* error を優先 = 契約 stateSpecs)、disabled → グループ全体 or option 個別(opacity 0.5 + 非活性)。
|
|
13
|
-
* focus
|
|
14
|
+
* focus は非対応(recipe にも focus 差分なし)。
|
|
14
15
|
* - a11y: グループに accessibilityRole="radiogroup"、各 option に "radio" +
|
|
15
16
|
* accessibilityState={{ checked, disabled }}。
|
|
16
17
|
* - 色・寸法の決定は pure resolver(radio.styles.ts)に分離。
|
|
@@ -19,15 +20,15 @@
|
|
|
19
20
|
|
|
20
21
|
import { useMemo } from "react";
|
|
21
22
|
import { Pressable, View } from "react-native";
|
|
22
|
-
import { Text } from "../primitives/Text";
|
|
23
|
-
import { useTheme } from "../theme";
|
|
24
|
-
import { CONTRACTS } from "../contracts/contract-types";
|
|
23
|
+
import { Text } from "../primitives/Text.js";
|
|
24
|
+
import { useTheme } from "../theme/index.js";
|
|
25
|
+
import { CONTRACTS } from "../contracts/contract-types.js";
|
|
25
26
|
import {
|
|
26
27
|
RADIO_SPEC,
|
|
27
28
|
resolveRadioCircleStyle,
|
|
28
29
|
resolveRadioGroupStyle,
|
|
29
30
|
type RadioVariant,
|
|
30
|
-
} from "./radio.styles";
|
|
31
|
+
} from "./radio.styles.js";
|
|
31
32
|
|
|
32
33
|
export interface RadioOption {
|
|
33
34
|
label: string;
|
|
@@ -99,6 +100,9 @@ export function Radio({
|
|
|
99
100
|
accessibilityState={{ checked: selected, disabled: optionDisabled }}
|
|
100
101
|
accessibilityLabel={option.label}
|
|
101
102
|
style={[
|
|
103
|
+
// 実効タップ標的 44pt(A11Y_MIN_TAP_TARGET_44)。option 行は背景を持たないので
|
|
104
|
+
// minHeight で下げ止める(視覚不変・隣接 option と当たり判定が重ならない)。
|
|
105
|
+
{ minHeight: RADIO_SPEC.optionMinHeight, justifyContent: "center" },
|
|
102
106
|
variant === "card-style"
|
|
103
107
|
? [group.cardStyle, selected ? group.cardSelectedStyle : null]
|
|
104
108
|
: null,
|
|
@@ -150,5 +154,6 @@ export function Radio({
|
|
|
150
154
|
);
|
|
151
155
|
}
|
|
152
156
|
|
|
153
|
-
//
|
|
157
|
+
// conformance test 用の contract メタ(§2 A-3)。scripts/lib/conformance.test.ts が
|
|
158
|
+
// 「この宣言が正しい contract を指しているか」を静的スキャンで照合する。
|
|
154
159
|
Radio.__contract = CONTRACTS.radio;
|
|
@@ -13,22 +13,54 @@
|
|
|
13
13
|
* react-native-safe-area-context 利用者は subpath "melta-app/safe-area" の
|
|
14
14
|
* enableSafeAreaContext() で context hook + View adapter に差し替えられる
|
|
15
15
|
* (rally-nav dogfood 由来、RN 0.85 Fabric/Android の初回 inset flash 対応)。
|
|
16
|
+
* 適用する edge は `edges` prop(省略時は enableSafeAreaContext の既定)。
|
|
16
17
|
*/
|
|
17
18
|
|
|
18
|
-
import { useMemo, type ReactNode } from "react";
|
|
19
|
-
import {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
import { useEffect, useMemo, type ReactNode, type Ref } from "react";
|
|
20
|
+
import {
|
|
21
|
+
ScrollView,
|
|
22
|
+
View,
|
|
23
|
+
type ScrollViewProps,
|
|
24
|
+
type StyleProp,
|
|
25
|
+
type ViewStyle,
|
|
26
|
+
} from "react-native";
|
|
27
|
+
import { resolveSafeAreaView, type SafeAreaEdge } from "./safe-area-registry.js";
|
|
28
|
+
import { isDev } from "../theme/define-theme.js";
|
|
29
|
+
import { useTheme } from "../theme/index.js";
|
|
30
|
+
import { CONTRACTS, type VariantOf } from "../contracts/contract-types.js";
|
|
31
|
+
import { resolveScreenStyle, type ScreenPadding, type ScreenStyle } from "./screen.styles.js";
|
|
32
|
+
|
|
33
|
+
/** dev 警告の重複抑止(inline object の scrollViewProps で毎 render 鳴らさない)。 */
|
|
34
|
+
const warned = new Set<string>();
|
|
24
35
|
|
|
25
36
|
interface ScreenProps {
|
|
26
37
|
/** 画面の骨格(contract variant)。default "scroll"。 */
|
|
27
38
|
variant?: VariantOf<"screen">;
|
|
28
39
|
/** content の padding(spacing token キー限定 | "none")。default "4"。 */
|
|
29
40
|
padding?: ScreenPadding;
|
|
41
|
+
/**
|
|
42
|
+
* safe-area を適用する辺。省略時は enableSafeAreaContext({ edges }) の既定。
|
|
43
|
+
* タブバーが bottom を自前処理する画面は `["top"]` のように絞る。
|
|
44
|
+
*
|
|
45
|
+
* ⚠️ adapter 未登録(RN core SafeAreaView)では**無視される**(core は辺を選べない)。
|
|
46
|
+
*/
|
|
47
|
+
edges?: readonly SafeAreaEdge[];
|
|
30
48
|
/** scroll 外(SafeArea 直下)に固定されるヘッダー slot(Header を差す想定)。 */
|
|
31
49
|
header?: ReactNode;
|
|
50
|
+
/**
|
|
51
|
+
* 内部 ScrollView への passthrough(variant="scroll" のみ)。children は Screen が持つ。
|
|
52
|
+
*
|
|
53
|
+
* - `contentContainerStyle` は DS の padding と**配列合成**される(渡した側が後勝ち。
|
|
54
|
+
* 意図的な非常口 — DS の padding を残したい場合は padding 系を書かないこと)。
|
|
55
|
+
* - `contentInsetAdjustmentBehavior` は**型で遮断**(safe-area は Screen 側が持つので
|
|
56
|
+
* iOS の自動 inset と必ず二重になる。正当な用途が無いため Omit)。
|
|
57
|
+
*/
|
|
58
|
+
scrollViewProps?: Omit<ScrollViewProps, "children" | "contentInsetAdjustmentBehavior">;
|
|
59
|
+
/**
|
|
60
|
+
* 内部 ScrollView の ref(scrollTo / scrollToEnd 用、variant="scroll" のみ)。
|
|
61
|
+
* Screen 自体は `__contract` 静的プロパティを持つため forwardRef 化せず別 prop にしている。
|
|
62
|
+
*/
|
|
63
|
+
scrollViewRef?: Ref<ScrollView>;
|
|
32
64
|
style?: StyleProp<ViewStyle>;
|
|
33
65
|
testID?: string;
|
|
34
66
|
children: ReactNode;
|
|
@@ -37,7 +69,10 @@ interface ScreenProps {
|
|
|
37
69
|
export function Screen({
|
|
38
70
|
variant = "scroll",
|
|
39
71
|
padding = "4",
|
|
72
|
+
edges,
|
|
40
73
|
header,
|
|
74
|
+
scrollViewProps,
|
|
75
|
+
scrollViewRef,
|
|
41
76
|
style,
|
|
42
77
|
testID,
|
|
43
78
|
children,
|
|
@@ -49,13 +84,36 @@ export function Screen({
|
|
|
49
84
|
[theme, mode, variant, padding],
|
|
50
85
|
);
|
|
51
86
|
|
|
52
|
-
|
|
87
|
+
// fixed には内部 ScrollView が無い=渡された prop は黙って捨てられる。dev で気づけるようにする。
|
|
88
|
+
const ignoresScrollProps =
|
|
89
|
+
variant === "fixed" && (scrollViewProps != null || scrollViewRef != null);
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
if (!isDev || !ignoresScrollProps) return;
|
|
92
|
+
const message =
|
|
93
|
+
'melta: Screen variant="fixed" では scrollViewProps / scrollViewRef は適用されない' +
|
|
94
|
+
"(内部 ScrollView が無いため)。variant=\"scroll\" にするか prop を外すこと。";
|
|
95
|
+
if (warned.has(message)) return;
|
|
96
|
+
warned.add(message);
|
|
97
|
+
console.warn(message);
|
|
98
|
+
}, [ignoresScrollProps]);
|
|
99
|
+
|
|
100
|
+
const SafeArea = resolveSafeAreaView(edges);
|
|
53
101
|
|
|
54
102
|
return (
|
|
55
103
|
<SafeArea style={[safeAreaStyle, style]} testID={testID}>
|
|
56
104
|
{header}
|
|
57
105
|
{variant === "scroll" ? (
|
|
58
|
-
<ScrollView
|
|
106
|
+
<ScrollView
|
|
107
|
+
ref={scrollViewRef}
|
|
108
|
+
// iOS の既定 0 は onScroll が 1 ドラッグ 1 発しか来ない(スクロール連動ヘッダが動かない)。
|
|
109
|
+
// spread より前に置いて消費者が上書きできるようにする。
|
|
110
|
+
scrollEventThrottle={16}
|
|
111
|
+
{...scrollViewProps}
|
|
112
|
+
// contentContainerStyle は素の spread だと DS の padding ごと消えるため配列合成(後勝ち)。
|
|
113
|
+
contentContainerStyle={[contentStyle, scrollViewProps?.contentContainerStyle]}
|
|
114
|
+
>
|
|
115
|
+
{children}
|
|
116
|
+
</ScrollView>
|
|
59
117
|
) : (
|
|
60
118
|
<View style={contentStyle}>{children}</View>
|
|
61
119
|
)}
|
|
@@ -22,9 +22,9 @@ import {
|
|
|
22
22
|
type StyleProp,
|
|
23
23
|
type ViewStyle,
|
|
24
24
|
} from "react-native";
|
|
25
|
-
import { Surface } from "./Surface";
|
|
26
|
-
import { useTheme } from "../theme";
|
|
27
|
-
import { CONTRACTS } from "../contracts/contract-types";
|
|
25
|
+
import { Surface } from "./Surface.js";
|
|
26
|
+
import { useTheme } from "../theme/index.js";
|
|
27
|
+
import { CONTRACTS } from "../contracts/contract-types.js";
|
|
28
28
|
import {
|
|
29
29
|
CARD_SURFACE_SPEC,
|
|
30
30
|
PULSE_OPACITY,
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
resolveSkeletonStates,
|
|
33
33
|
resolveSkeletonVariants,
|
|
34
34
|
type SkeletonVariant,
|
|
35
|
-
} from "./skeleton.styles";
|
|
35
|
+
} from "./skeleton.styles.js";
|
|
36
36
|
|
|
37
37
|
interface SkeletonProps {
|
|
38
38
|
variant?: SkeletonVariant;
|
|
@@ -150,5 +150,6 @@ export function Skeleton({
|
|
|
150
150
|
);
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
//
|
|
153
|
+
// conformance test 用の contract メタ(§2 A-3)。scripts/lib/conformance.test.ts が
|
|
154
|
+
// 「この宣言が正しい contract を指しているか」を静的スキャンで照合する。
|
|
154
155
|
Skeleton.__contract = CONTRACTS.skeleton;
|
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
|
|
14
14
|
import type { ReactNode } from "react";
|
|
15
15
|
import { View, type StyleProp, type ViewStyle } from "react-native";
|
|
16
|
-
import { useTheme } from "../theme";
|
|
17
|
-
import { CONTRACTS } from "../contracts/contract-types";
|
|
18
|
-
import { resolveSurfaceStyle, type SurfaceStyleProps } from "./surface.styles";
|
|
16
|
+
import { useTheme } from "../theme/index.js";
|
|
17
|
+
import { CONTRACTS } from "../contracts/contract-types.js";
|
|
18
|
+
import { resolveSurfaceStyle, type SurfaceStyleProps } from "./surface.styles.js";
|
|
19
19
|
|
|
20
20
|
export interface SurfaceProps extends SurfaceStyleProps {
|
|
21
21
|
style?: StyleProp<ViewStyle>;
|
|
@@ -33,5 +33,6 @@ export function Surface({ bg, radius, elevation, padding, style, testID, childre
|
|
|
33
33
|
);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
//
|
|
36
|
+
// conformance test 用の contract メタ(§2 A-3)。scripts/lib/conformance.test.ts が
|
|
37
|
+
// 「この宣言が正しい contract を指しているか」を静的スキャンで照合する。
|
|
37
38
|
Surface.__contract = CONTRACTS.surface;
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
* editable=false + accessibilityState.disabled を併せて適用する(recipe description の規約)。
|
|
7
7
|
* - error 時は errorText を必ずテキストで表示する(FORM_NO_COLOR_ONLY_ERROR = 色だけで伝えない)。
|
|
8
8
|
* RN には aria-describedby 相当が無いため、(a) errorText を input の accessibilityLabel に合成
|
|
9
|
-
*
|
|
9
|
+
* (再フォーカス時に読み上げ。合成文は formatErrorAccessibilityLabel で差し替え可能 = i18n フック。
|
|
10
|
+
* 既定は日本語)、(b) errorText 側に accessibilityLiveRegion="polite"(Android は
|
|
10
11
|
* 出現時に通知。iOS の即時通知が要る画面は submit 時に announceForAccessibility を併用する —
|
|
11
12
|
* docs/patterns.md 参照)。
|
|
12
13
|
* - focus は内部 state(onFocus/onBlur)で recipe states.focus の inputStyle 差分
|
|
@@ -32,16 +33,16 @@ import {
|
|
|
32
33
|
type TextStyle,
|
|
33
34
|
type ViewStyle,
|
|
34
35
|
} from "react-native";
|
|
35
|
-
import { useTheme } from "../theme";
|
|
36
|
-
import { CONTRACTS } from "../contracts/contract-types";
|
|
36
|
+
import { useTheme } from "../theme/index.js";
|
|
37
|
+
import { CONTRACTS } from "../contracts/contract-types.js";
|
|
37
38
|
import {
|
|
38
39
|
resolveTextFieldStyle,
|
|
39
40
|
resolveTextFieldFocusStyle,
|
|
40
41
|
type TextFieldSize,
|
|
41
|
-
} from "./textfield.styles";
|
|
42
|
+
} from "./textfield.styles.js";
|
|
42
43
|
|
|
43
44
|
/**
|
|
44
|
-
* Android の EditText
|
|
45
|
+
* Android の EditText は高さ下限(minHeight)と組み合わさると既定の縦 padding で文字が
|
|
45
46
|
* 上寄り・下端切れになるため、縦 padding を殺して縦センターに揃える。
|
|
46
47
|
* textAlignVertical は Android 専用、paddingVertical: 0 は iOS の見た目を変えない
|
|
47
48
|
* (iOS の単一行入力は元々縦センター)。
|
|
@@ -65,6 +66,15 @@ export function resolveInputVerticalFix(os: typeof Platform.OS): TextStyle {
|
|
|
65
66
|
|
|
66
67
|
const INPUT_VERTICAL_FIX = resolveInputVerticalFix(Platform.OS);
|
|
67
68
|
|
|
69
|
+
/**
|
|
70
|
+
* error 時の accessibilityLabel 合成の既定(日本語)。
|
|
71
|
+
* props で差し替え可能(formatErrorAccessibilityLabel)。既定値を変えると
|
|
72
|
+
* 既存アプリの VoiceOver 読み上げが変わるので、既定は日本語のまま据え置く。
|
|
73
|
+
*/
|
|
74
|
+
function defaultErrorAccessibilityLabel(label: string, errorText: string): string {
|
|
75
|
+
return `${label}。エラー: ${errorText}`;
|
|
76
|
+
}
|
|
77
|
+
|
|
68
78
|
interface TextFieldProps {
|
|
69
79
|
/** ラベル(必須。placeholder だけの入力欄を作らせない = FORM_NO_LABEL_OMIT)。 */
|
|
70
80
|
label: string;
|
|
@@ -80,6 +90,12 @@ interface TextFieldProps {
|
|
|
80
90
|
helperText?: string;
|
|
81
91
|
/** variant="error" 時に表示するエラーメッセージ(色だけでなくテキストで伝える)。 */
|
|
82
92
|
errorText?: string;
|
|
93
|
+
/**
|
|
94
|
+
* error 時に input の accessibilityLabel へ合成する文字列の組み立て(i18n フック)。
|
|
95
|
+
* 既定は日本語の `${label}。エラー: ${errorText}`(既存アプリの読み上げを変えないため据え置き)。
|
|
96
|
+
* 英語アプリなどでは `(label, error) => \`${label}. Error: ${error}\`` を渡す。
|
|
97
|
+
*/
|
|
98
|
+
formatErrorAccessibilityLabel?: (label: string, errorText: string) => string;
|
|
83
99
|
/** キーボード種別(RN TextInput へ透過)。 */
|
|
84
100
|
keyboardType?: KeyboardTypeOptions;
|
|
85
101
|
autoCapitalize?: "none" | "sentences" | "words" | "characters";
|
|
@@ -104,6 +120,7 @@ export function TextField({
|
|
|
104
120
|
size = "medium",
|
|
105
121
|
helperText,
|
|
106
122
|
errorText,
|
|
123
|
+
formatErrorAccessibilityLabel = defaultErrorAccessibilityLabel,
|
|
107
124
|
keyboardType,
|
|
108
125
|
autoCapitalize,
|
|
109
126
|
autoCorrect,
|
|
@@ -143,7 +160,7 @@ export function TextField({
|
|
|
143
160
|
maxLength={maxLength}
|
|
144
161
|
returnKeyType={returnKeyType}
|
|
145
162
|
onSubmitEditing={onSubmitEditing}
|
|
146
|
-
accessibilityLabel={showError ?
|
|
163
|
+
accessibilityLabel={showError ? formatErrorAccessibilityLabel(label, errorText) : label}
|
|
147
164
|
accessibilityState={{ disabled }}
|
|
148
165
|
onFocus={(e) => {
|
|
149
166
|
if (!disabled) setFocused(true);
|
|
@@ -167,5 +184,6 @@ export function TextField({
|
|
|
167
184
|
);
|
|
168
185
|
}
|
|
169
186
|
|
|
170
|
-
//
|
|
187
|
+
// conformance test 用の contract メタ(§2 A-3)。scripts/lib/conformance.test.ts が
|
|
188
|
+
// 「この宣言が正しい contract を指しているか」を静的スキャンで照合する。
|
|
171
189
|
TextField.__contract = CONTRACTS.textfield;
|
package/src/components/Toast.tsx
CHANGED
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
* - presentational component。表示位置・キュー・自動消滅はアプリ層の責務
|
|
6
6
|
* (entering/visible/dismissing のアニメ state は Phase 外、recipe description)。
|
|
7
7
|
* - closeButton は契約 anatomy で必須 → onClose を required prop にして型で強制。
|
|
8
|
-
* Pressable + テキスト ×(accessibilityLabel
|
|
8
|
+
* Pressable + テキスト ×(accessibilityLabel は既定 "閉じる"、closeAccessibilityLabel で差し替え可能)。
|
|
9
|
+
* - action と × は隣接する 2 つの操作要素。当たり判定が重なると押し違いが起きるので、
|
|
10
|
+
* 横 hitSlop を gap の 1/2 に絞る(toast.styles.ts の TOAST_TAP_TARGET)。
|
|
9
11
|
* - a11y(§2 web→RN mapping): web の role=status + aria-live=polite を
|
|
10
12
|
* accessibilityLiveRegion="polite"(Android のみ有効)に落とす。
|
|
11
13
|
* - elevation は theme.elevation.sm(iOS shadow* + Android elevation の複合値)を container に
|
|
@@ -17,10 +19,15 @@
|
|
|
17
19
|
|
|
18
20
|
import { useMemo } from "react";
|
|
19
21
|
import { Pressable, View, type StyleProp, type ViewStyle } from "react-native";
|
|
20
|
-
import { Text } from "../primitives/Text";
|
|
21
|
-
import { useTheme } from "../theme";
|
|
22
|
-
import { CONTRACTS } from "../contracts/contract-types";
|
|
23
|
-
import {
|
|
22
|
+
import { Text } from "../primitives/Text.js";
|
|
23
|
+
import { useTheme } from "../theme/index.js";
|
|
24
|
+
import { CONTRACTS } from "../contracts/contract-types.js";
|
|
25
|
+
import {
|
|
26
|
+
TOAST_SPEC,
|
|
27
|
+
TOAST_TAP_TARGET,
|
|
28
|
+
resolveToastStyles,
|
|
29
|
+
type ToastVariant,
|
|
30
|
+
} from "./toast.styles.js";
|
|
24
31
|
|
|
25
32
|
interface ToastProps {
|
|
26
33
|
/** 通知タイプ(contract variant)。 */
|
|
@@ -34,6 +41,11 @@ interface ToastProps {
|
|
|
34
41
|
/** あれば message の後にアクションテキスト(onAction とセットで有効)。 */
|
|
35
42
|
actionLabel?: string;
|
|
36
43
|
onAction?: () => void;
|
|
44
|
+
/**
|
|
45
|
+
* × ボタンの accessibilityLabel(i18n フック)。既定は日本語 "閉じる"
|
|
46
|
+
* (既存アプリの読み上げを変えないため据え置き)。
|
|
47
|
+
*/
|
|
48
|
+
closeAccessibilityLabel?: string;
|
|
37
49
|
style?: StyleProp<ViewStyle>;
|
|
38
50
|
testID?: string;
|
|
39
51
|
}
|
|
@@ -45,6 +57,7 @@ export function Toast({
|
|
|
45
57
|
onClose,
|
|
46
58
|
actionLabel,
|
|
47
59
|
onAction,
|
|
60
|
+
closeAccessibilityLabel = "閉じる",
|
|
48
61
|
style,
|
|
49
62
|
testID,
|
|
50
63
|
}: ToastProps) {
|
|
@@ -73,7 +86,9 @@ export function Toast({
|
|
|
73
86
|
)}
|
|
74
87
|
</View>
|
|
75
88
|
{actionLabel != null && onAction != null && (
|
|
76
|
-
|
|
89
|
+
// 横 hitSlop は gap/2(= 6)まで。10 のままだと隣の × と当たり判定が 8pt 重なり、
|
|
90
|
+
// 手前に描かれる × が勝って「元に戻す」が押せない(toast.styles.ts の TOAST_TAP_TARGET 参照)。
|
|
91
|
+
<Pressable onPress={onAction} accessibilityRole="button" hitSlop={TOAST_TAP_TARGET.hitSlop}>
|
|
77
92
|
<Text
|
|
78
93
|
variant={TOAST_SPEC.messageFont}
|
|
79
94
|
weight={TOAST_SPEC.messageWeight}
|
|
@@ -83,13 +98,19 @@ export function Toast({
|
|
|
83
98
|
</Text>
|
|
84
99
|
</Pressable>
|
|
85
100
|
)}
|
|
86
|
-
{/* 最小タップターゲット確保:
|
|
101
|
+
{/* 最小タップターゲット確保: 横 hitSlop を gap/2 に絞るぶん箱を 32pt に広げて 32+6×2 = 44pt。
|
|
102
|
+
縦は 24 + 10×2 = 44pt(正典パターン)。 */}
|
|
87
103
|
<Pressable
|
|
88
104
|
onPress={onClose}
|
|
89
105
|
accessibilityRole="button"
|
|
90
|
-
accessibilityLabel=
|
|
91
|
-
hitSlop={
|
|
92
|
-
style={{
|
|
106
|
+
accessibilityLabel={closeAccessibilityLabel}
|
|
107
|
+
hitSlop={TOAST_TAP_TARGET.hitSlop}
|
|
108
|
+
style={{
|
|
109
|
+
minWidth: TOAST_TAP_TARGET.closeMinWidth,
|
|
110
|
+
minHeight: TOAST_TAP_TARGET.closeMinHeight,
|
|
111
|
+
alignItems: "center",
|
|
112
|
+
justifyContent: "center",
|
|
113
|
+
}}
|
|
93
114
|
>
|
|
94
115
|
<Text variant="xs" style={{ color: styles.messageStyle.color }}>
|
|
95
116
|
×
|
|
@@ -99,5 +120,6 @@ export function Toast({
|
|
|
99
120
|
);
|
|
100
121
|
}
|
|
101
122
|
|
|
102
|
-
//
|
|
123
|
+
// conformance test 用の contract メタ(§2 A-3)。scripts/lib/conformance.test.ts が
|
|
124
|
+
// 「この宣言が正しい contract を指しているか」を静的スキャンで照合する。
|
|
103
125
|
Toast.__contract = CONTRACTS.toast;
|
|
@@ -8,22 +8,24 @@
|
|
|
8
8
|
* flex-start / flex-end を切り替える(アニメは Phase 外)。
|
|
9
9
|
* - disabled: recipe states.disabled の opacity + accessibilityState.disabled + onPress 無効。
|
|
10
10
|
* - a11y: accessibilityRole="switch" + accessibilityState.checked。label があれば右に表示し、
|
|
11
|
-
* Pressable 全体がタップ領域 + accessibilityLabel
|
|
11
|
+
* Pressable 全体がタップ領域 + accessibilityLabel になる。実効タップ標的 44pt は
|
|
12
|
+
* 縦 hitSlop で確保する(TOGGLE_VERTICAL_HIT_SLOP、A11Y_MIN_TAP_TARGET_44)。
|
|
12
13
|
* - 色・寸法の決定は pure resolver(toggle.styles.ts)に分離。
|
|
13
14
|
* recipes/app/toggle.recipe.json との機械照合は scripts/lib/toggle-conformance.test.ts が行う。
|
|
14
15
|
*/
|
|
15
16
|
|
|
16
17
|
import { useMemo } from "react";
|
|
17
18
|
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";
|
|
19
|
+
import { Text } from "../primitives/Text.js";
|
|
20
|
+
import { useTheme } from "../theme/index.js";
|
|
21
|
+
import { CONTRACTS } from "../contracts/contract-types.js";
|
|
21
22
|
import {
|
|
22
23
|
resolveToggleStyle,
|
|
23
24
|
TOGGLE_DISABLED_OPACITY,
|
|
25
|
+
TOGGLE_VERTICAL_HIT_SLOP,
|
|
24
26
|
type ToggleSize,
|
|
25
27
|
type ToggleVariant,
|
|
26
|
-
} from "./toggle.styles";
|
|
28
|
+
} from "./toggle.styles.js";
|
|
27
29
|
|
|
28
30
|
interface ToggleProps {
|
|
29
31
|
/** 現在値(true=on / false=off。variant はここから暗黙決定)。 */
|
|
@@ -61,6 +63,12 @@ export function Toggle({
|
|
|
61
63
|
accessibilityRole="switch"
|
|
62
64
|
accessibilityState={{ checked: value, disabled }}
|
|
63
65
|
accessibilityLabel={label}
|
|
66
|
+
// track(24/28pt)も label 付きの行(36pt)も 44pt に届かないので縦 hitSlop で補う。
|
|
67
|
+
// 値の根拠は toggle.styles.ts の TOGGLE_VERTICAL_HIT_SLOP。
|
|
68
|
+
hitSlop={{
|
|
69
|
+
top: TOGGLE_VERTICAL_HIT_SLOP[size],
|
|
70
|
+
bottom: TOGGLE_VERTICAL_HIT_SLOP[size],
|
|
71
|
+
}}
|
|
64
72
|
testID={testID}
|
|
65
73
|
style={[
|
|
66
74
|
{
|
|
@@ -90,5 +98,6 @@ export function Toggle({
|
|
|
90
98
|
);
|
|
91
99
|
}
|
|
92
100
|
|
|
93
|
-
//
|
|
101
|
+
// conformance test 用の contract メタ(§2 A-3)。scripts/lib/conformance.test.ts が
|
|
102
|
+
// 「この宣言が正しい contract を指しているか」を静的スキャンで照合する。
|
|
94
103
|
Toggle.__contract = CONTRACTS.toggle;
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* - sheet の下余白(SafeArea)は実装(ActionSheet.tsx)が SafeAreaView で加算する。
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
import type { FontWeightValue, NativeTheme, ThemeMode } from "../theme";
|
|
16
|
+
import type { FontWeightValue, NativeTheme, ThemeMode } from "../theme/index.js";
|
|
17
17
|
|
|
18
18
|
/** overlay の黒 50%(token 外 literal。modal と同値)。 */
|
|
19
19
|
// eslint-disable-next-line melta/no-raw-color -- overlay の黒 50% は契約上 token 外の literal(recipe description / web の bg-black/50 と同値)
|
|
@@ -7,8 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* - RN では border を持たず bg subtle + text 色で表現する(status token に border 用の中間色が
|
|
9
9
|
* 無いため。COLOR_ONLY_FORBIDDEN は title/message のテキストと icon slot で担保、recipe description)。
|
|
10
|
-
* -
|
|
11
|
-
* (dark token が無い既知の割り切り、web と同じ)。
|
|
10
|
+
* - status 色(mode ごとの subtle / text、info の primary 固定)の解決は status-colors.ts が SSOT。
|
|
12
11
|
*/
|
|
13
12
|
|
|
14
13
|
import type {
|
|
@@ -19,14 +18,23 @@ import type {
|
|
|
19
18
|
RadiusKey,
|
|
20
19
|
SpacingKey,
|
|
21
20
|
ThemeMode,
|
|
22
|
-
} from "../theme";
|
|
21
|
+
} from "../theme/index.js";
|
|
22
|
+
import { resolveStatusVariantColors, type StatusVariant } from "./status-colors.js";
|
|
23
|
+
import { CANONICAL_TAP_TARGET } from "../a11y/tap-target.js";
|
|
23
24
|
|
|
24
|
-
|
|
25
|
+
/** alert.contract の variant 語彙(status 共通語彙と 1:1)。 */
|
|
26
|
+
export type AlertVariant = StatusVariant;
|
|
25
27
|
|
|
26
28
|
/**
|
|
27
29
|
* 構成キー(token キー)。component は Text にこのキーをそのまま渡し、resolver は同じキーから
|
|
28
30
|
* style 値を解決する(component と resolver が別々のキーを持って drift する事故を防ぐ SSOT)。
|
|
29
31
|
*/
|
|
32
|
+
/**
|
|
33
|
+
* × の当たり判定(正典パターン: 視覚 24pt + hitSlop 10 = 実効 44pt)。
|
|
34
|
+
* Alert の × は右端に単独で立つ(Toast と違い隣接する操作要素が無い)ので横も 10 のままでよい。
|
|
35
|
+
*/
|
|
36
|
+
export const ALERT_CLOSE_TAP_TARGET = CANONICAL_TAP_TARGET;
|
|
37
|
+
|
|
30
38
|
export const ALERT_SPEC = {
|
|
31
39
|
padding: "4",
|
|
32
40
|
gap: "3",
|
|
@@ -66,33 +74,13 @@ export interface AlertStyles {
|
|
|
66
74
|
};
|
|
67
75
|
}
|
|
68
76
|
|
|
69
|
-
/**
|
|
70
|
-
* variant → bg / text 色の解決。
|
|
71
|
-
* - info: primary.50 / primary.800 固定(mode 非依存)。
|
|
72
|
-
* - success / warning / error: status token(error は status キー上は danger)。
|
|
73
|
-
* light は subtleLight / textLight、dark は subtleDark / textDark(recipe description の写像)。
|
|
74
|
-
*/
|
|
75
|
-
export function resolveAlertColors(
|
|
76
|
-
theme: NativeTheme,
|
|
77
|
-
mode: ThemeMode,
|
|
78
|
-
variant: AlertVariant,
|
|
79
|
-
): { bg: string; text: string } {
|
|
80
|
-
if (variant === "info") {
|
|
81
|
-
return { bg: theme.color.primary["50"], text: theme.color.primary["800"] };
|
|
82
|
-
}
|
|
83
|
-
const status = theme.color.status[variant === "error" ? "danger" : variant];
|
|
84
|
-
return mode === "dark"
|
|
85
|
-
? { bg: status.subtleDark, text: status.textDark }
|
|
86
|
-
: { bg: status.subtleLight, text: status.textLight };
|
|
87
|
-
}
|
|
88
|
-
|
|
89
77
|
/** variant → 全 slot の style 解決(alert.recipe styleRefs の 1:1 写像)。 */
|
|
90
78
|
export function resolveAlertStyles(
|
|
91
79
|
theme: NativeTheme,
|
|
92
80
|
mode: ThemeMode,
|
|
93
81
|
variant: AlertVariant,
|
|
94
82
|
): AlertStyles {
|
|
95
|
-
const { bg, text } =
|
|
83
|
+
const { bg, text } = resolveStatusVariantColors(theme, mode, variant);
|
|
96
84
|
return {
|
|
97
85
|
containerStyle: {
|
|
98
86
|
backgroundColor: bg,
|
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
* 機械照合は scripts/lib/avatar-conformance.test.ts が行う。
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import type { FontWeightValue, NativeTheme, ThemeMode } from "../theme";
|
|
9
|
+
import type { FontWeightValue, NativeTheme, ThemeMode } from "../theme/index.js";
|
|
10
10
|
|
|
11
11
|
export type AvatarSize = "small" | "medium" | "large";
|
|
12
12
|
export type AvatarStatus = "online" | "away" | "offline";
|
|
13
13
|
|
|
14
14
|
/** サイズ軸(contract sizes の height と 1:1。recipe sizes の width/height/dotSize/fontSize に対応)。 */
|
|
15
|
-
const
|
|
15
|
+
export const AVATAR_SIZE_SPEC = {
|
|
16
16
|
small: { box: 32, dot: 8, fontSize: "xs" },
|
|
17
17
|
medium: { box: 40, dot: 10, fontSize: "sm" },
|
|
18
18
|
large: { box: 48, dot: 12, fontSize: "base" },
|
|
@@ -21,6 +21,28 @@ const SIZE_TABLE = {
|
|
|
21
21
|
/** group の重なり(-spacing.2 相当。負値 token が無いため recipe と同じ literal)。 */
|
|
22
22
|
export const AVATAR_GROUP_OVERLAP = -8;
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* initials の文字サイズ倍率の上限(size 別)。
|
|
26
|
+
*
|
|
27
|
+
* Avatar の器は **width = height の円**(recipe sizes の box)で、fontScale で伸びるのは
|
|
28
|
+
* 中身の文字だけ。器は伸びないので、上限を掛けないと OS の文字サイズ拡大で文字が円から
|
|
29
|
+
* はみ出す(円が楕円に見える / 文字が切れる)。
|
|
30
|
+
*
|
|
31
|
+
* 導出は「box ÷ その size が使う fontSize token の lineHeight」を**小数第1位で切り捨て**(安全側):
|
|
32
|
+
* small 32 ÷ 19(xs の lineHeight) = 1.68… → 1.6
|
|
33
|
+
* medium 40 ÷ 26(sm の lineHeight) = 1.53… → 1.5
|
|
34
|
+
* large 48 ÷ 36(base の lineHeight) = 1.33… → 1.3
|
|
35
|
+
*
|
|
36
|
+
* lineHeight(fontSize ではなく)を分母に採るのは、RN が確保する行ボックスの高さが
|
|
37
|
+
* 溢れの実体だから。theme を差し替えても壊れないよう、この導出そのものは
|
|
38
|
+
* avatar-conformance.test.ts が実 theme の値で再検算する。
|
|
39
|
+
*/
|
|
40
|
+
export const AVATAR_INITIALS_MAX_FONT_SCALE: Record<AvatarSize, number> = {
|
|
41
|
+
small: 1.6,
|
|
42
|
+
medium: 1.5,
|
|
43
|
+
large: 1.3,
|
|
44
|
+
};
|
|
45
|
+
|
|
24
46
|
/**
|
|
25
47
|
* group variant(avatar.recipe の group.style / group.overlapStyle の 1:1 写像)。
|
|
26
48
|
* container は横並び、2枚目以降の子に overlap(負 margin)を適用する。
|
|
@@ -61,7 +83,7 @@ export function resolveAvatarStyle(
|
|
|
61
83
|
variant: "image" | "initials",
|
|
62
84
|
size: AvatarSize,
|
|
63
85
|
): AvatarStyleResult {
|
|
64
|
-
const s =
|
|
86
|
+
const s = AVATAR_SIZE_SPEC[size];
|
|
65
87
|
return {
|
|
66
88
|
container: {
|
|
67
89
|
width: s.box,
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* - sheet の下余白(SafeArea)は実装(BottomSheet.tsx)が SafeAreaView で加算する。
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
import type { FontWeightValue, NativeTheme, ThemeMode } from "../theme";
|
|
16
|
+
import type { FontWeightValue, NativeTheme, ThemeMode } from "../theme/index.js";
|
|
17
17
|
|
|
18
18
|
/** overlay の黒 50%(token 外 literal。modal と同値)。 */
|
|
19
19
|
// eslint-disable-next-line melta/no-raw-color -- overlay の黒 50% は契約上 token 外の literal(recipe description / web の bg-black/50 と同値)
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { ViewStyle } from "react-native";
|
|
10
|
-
import type { NativeTheme, ThemeMode, ElevationKey } from "../theme";
|
|
10
|
+
import type { NativeTheme, ThemeMode, ElevationKey } from "../theme/index.js";
|
|
11
11
|
|
|
12
12
|
export type CardVariant = "basic" | "media" | "action" | "link";
|
|
13
13
|
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { isValidElement } from "react";
|
|
13
|
-
import type { CardVariant } from "./card.styles";
|
|
14
|
-
import { CARD_INTERACTIVE } from "./card.styles";
|
|
13
|
+
import type { CardVariant } from "./card.styles.js";
|
|
14
|
+
import { CARD_INTERACTIVE } from "./card.styles.js";
|
|
15
15
|
|
|
16
16
|
export interface CardPropsForValidation {
|
|
17
17
|
variant: CardVariant;
|
|
@@ -6,18 +6,21 @@
|
|
|
6
6
|
* 機械照合は scripts/lib/checkbox-conformance.test.ts が行う。
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import type { NativeTheme, ThemeMode } from "../theme";
|
|
9
|
+
import type { NativeTheme, ThemeMode } from "../theme/index.js";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* recipe 由来の literal 値テーブル(token 化されていない数値。conformance が recipe と照合する)。
|
|
13
13
|
* - box は 20px(web の 16px より大きいのはタッチ前提、recipe description)
|
|
14
|
-
* -
|
|
14
|
+
* - 実効タップ標的 44pt は行の minHeight で確保(A11Y_MIN_TAP_TARGET_44。規約 10-(b):
|
|
15
|
+
* 背景を持たない行は hitSlop でなく寸法で満たす — 旧実装の四方 hitSlop 12 は行全体
|
|
16
|
+
* Pressable に効くため、縦積みの隣接 Checkbox と当たり判定が最大 24pt 重なっていた
|
|
17
|
+
* (Codex レビュー指摘)。Radio の option 行と同じ手当てに統一)
|
|
15
18
|
*/
|
|
16
19
|
export const CHECKBOX_SPEC = {
|
|
17
20
|
boxSize: 20,
|
|
18
21
|
boxBorderWidth: 2,
|
|
19
22
|
disabledOpacity: 0.5,
|
|
20
|
-
|
|
23
|
+
rowMinHeight: 44,
|
|
21
24
|
} as const;
|
|
22
25
|
|
|
23
26
|
/** 見た目(色)に効く状態入力。disabled は opacity のみで色を変えないため含めない。 */
|