melta-app 0.5.3 → 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.
Files changed (298) hide show
  1. package/README.md +493 -186
  2. package/docs/architecture.md +99 -0
  3. package/eslint-rules/melta.d.mts +44 -0
  4. package/lib/module/a11y/tap-target.js +41 -0
  5. package/lib/module/a11y/tap-target.js.map +1 -0
  6. package/lib/module/components/ActionSheet.js +20 -6
  7. package/lib/module/components/ActionSheet.js.map +1 -1
  8. package/lib/module/components/Alert.js +11 -7
  9. package/lib/module/components/Alert.js.map +1 -1
  10. package/lib/module/components/Avatar.js +7 -2
  11. package/lib/module/components/Avatar.js.map +1 -1
  12. package/lib/module/components/BottomSheet.js +11 -3
  13. package/lib/module/components/BottomSheet.js.map +1 -1
  14. package/lib/module/components/Card.js +4 -3
  15. package/lib/module/components/Card.js.map +1 -1
  16. package/lib/module/components/Checkbox.js +8 -5
  17. package/lib/module/components/Checkbox.js.map +1 -1
  18. package/lib/module/components/EmptyState.js +2 -1
  19. package/lib/module/components/EmptyState.js.map +1 -1
  20. package/lib/module/components/Header.js.map +1 -1
  21. package/lib/module/components/Image.js +5 -3
  22. package/lib/module/components/Image.js.map +1 -1
  23. package/lib/module/components/Modal.js +12 -4
  24. package/lib/module/components/Modal.js.map +1 -1
  25. package/lib/module/components/Progress.js +2 -1
  26. package/lib/module/components/Progress.js.map +1 -1
  27. package/lib/module/components/Radio.js +11 -3
  28. package/lib/module/components/Radio.js.map +1 -1
  29. package/lib/module/components/Screen.js +28 -3
  30. package/lib/module/components/Screen.js.map +1 -1
  31. package/lib/module/components/Skeleton.js +2 -1
  32. package/lib/module/components/Skeleton.js.map +1 -1
  33. package/lib/module/components/Surface.js +2 -1
  34. package/lib/module/components/Surface.js.map +1 -1
  35. package/lib/module/components/TextField.js +16 -4
  36. package/lib/module/components/TextField.js.map +1 -1
  37. package/lib/module/components/Toast.js +17 -9
  38. package/lib/module/components/Toast.js.map +1 -1
  39. package/lib/module/components/Toggle.js +13 -4
  40. package/lib/module/components/Toggle.js.map +1 -1
  41. package/lib/module/components/alert.styles.js +12 -26
  42. package/lib/module/components/alert.styles.js.map +1 -1
  43. package/lib/module/components/avatar.styles.js +24 -2
  44. package/lib/module/components/avatar.styles.js.map +1 -1
  45. package/lib/module/components/card.validate.js.map +1 -1
  46. package/lib/module/components/checkbox.styles.js +5 -2
  47. package/lib/module/components/checkbox.styles.js.map +1 -1
  48. package/lib/module/components/index.js.map +1 -1
  49. package/lib/module/components/modal.styles.js +9 -0
  50. package/lib/module/components/modal.styles.js.map +1 -1
  51. package/lib/module/components/radio.styles.js +11 -2
  52. package/lib/module/components/radio.styles.js.map +1 -1
  53. package/lib/module/components/safe-area-registry.js +64 -13
  54. package/lib/module/components/safe-area-registry.js.map +1 -1
  55. package/lib/module/components/status-colors.js +53 -0
  56. package/lib/module/components/status-colors.js.map +1 -0
  57. package/lib/module/components/textfield.styles.js +15 -8
  58. package/lib/module/components/textfield.styles.js.map +1 -1
  59. package/lib/module/components/toast.styles.js +36 -25
  60. package/lib/module/components/toast.styles.js.map +1 -1
  61. package/lib/module/components/toggle.styles.js +20 -0
  62. package/lib/module/components/toggle.styles.js.map +1 -1
  63. package/lib/module/icons/Icon.js +4 -1
  64. package/lib/module/icons/Icon.js.map +1 -1
  65. package/lib/module/icons/icon.styles.js +32 -1
  66. package/lib/module/icons/icon.styles.js.map +1 -1
  67. package/lib/module/icons/index.js.map +1 -1
  68. package/lib/module/index.js.map +1 -1
  69. package/lib/module/primitives/Button.js +23 -11
  70. package/lib/module/primitives/Button.js.map +1 -1
  71. package/lib/module/primitives/Metric.js +2 -1
  72. package/lib/module/primitives/Metric.js.map +1 -1
  73. package/lib/module/primitives/Row.js.map +1 -1
  74. package/lib/module/primitives/Stack.js.map +1 -1
  75. package/lib/module/primitives/Tag.js +15 -7
  76. package/lib/module/primitives/Tag.js.map +1 -1
  77. package/lib/module/primitives/Text.js +28 -7
  78. package/lib/module/primitives/Text.js.map +1 -1
  79. package/lib/module/primitives/_internal/focus-ring.js.map +1 -1
  80. package/lib/module/primitives/button.styles.js +58 -8
  81. package/lib/module/primitives/button.styles.js.map +1 -1
  82. package/lib/module/primitives/index.js.map +1 -1
  83. package/lib/module/primitives/metric.styles.js +4 -1
  84. package/lib/module/primitives/metric.styles.js.map +1 -1
  85. package/lib/module/primitives/tag.styles.js +42 -3
  86. package/lib/module/primitives/tag.styles.js.map +1 -1
  87. package/lib/module/primitives/text.styles.js +10 -5
  88. package/lib/module/primitives/text.styles.js.map +1 -1
  89. package/lib/module/safe-area/index.js +11 -5
  90. package/lib/module/safe-area/index.js.map +1 -1
  91. package/lib/module/theme/ThemeProvider.js +5 -2
  92. package/lib/module/theme/ThemeProvider.js.map +1 -1
  93. package/lib/module/theme/define-theme.js +27 -1
  94. package/lib/module/theme/define-theme.js.map +1 -1
  95. package/lib/module/theme/index.js +7 -8
  96. package/lib/module/theme/index.js.map +1 -1
  97. package/lib/module/theme/letter-spacing.js +20 -0
  98. package/lib/module/theme/letter-spacing.js.map +1 -0
  99. package/lib/module/theme/line-height.js +52 -0
  100. package/lib/module/theme/line-height.js.map +1 -0
  101. package/lib/module/theme/native-theme.js +8 -7
  102. package/lib/module/theme/native-theme.js.map +1 -1
  103. package/lib/typescript/src/a11y/tap-target.d.ts +34 -0
  104. package/lib/typescript/src/a11y/tap-target.d.ts.map +1 -0
  105. package/lib/typescript/src/components/ActionSheet.d.ts +8 -1
  106. package/lib/typescript/src/components/ActionSheet.d.ts.map +1 -1
  107. package/lib/typescript/src/components/Alert.d.ts +9 -2
  108. package/lib/typescript/src/components/Alert.d.ts.map +1 -1
  109. package/lib/typescript/src/components/Avatar.d.ts +1 -1
  110. package/lib/typescript/src/components/Avatar.d.ts.map +1 -1
  111. package/lib/typescript/src/components/BottomSheet.d.ts +4 -1
  112. package/lib/typescript/src/components/BottomSheet.d.ts.map +1 -1
  113. package/lib/typescript/src/components/Card.d.ts +2 -2
  114. package/lib/typescript/src/components/Checkbox.d.ts +3 -3
  115. package/lib/typescript/src/components/Checkbox.d.ts.map +1 -1
  116. package/lib/typescript/src/components/Image.d.ts +4 -3
  117. package/lib/typescript/src/components/Image.d.ts.map +1 -1
  118. package/lib/typescript/src/components/Modal.d.ts +9 -3
  119. package/lib/typescript/src/components/Modal.d.ts.map +1 -1
  120. package/lib/typescript/src/components/Progress.d.ts +1 -1
  121. package/lib/typescript/src/components/Progress.d.ts.map +1 -1
  122. package/lib/typescript/src/components/Radio.d.ts +3 -2
  123. package/lib/typescript/src/components/Radio.d.ts.map +1 -1
  124. package/lib/typescript/src/components/Screen.d.ts +28 -5
  125. package/lib/typescript/src/components/Screen.d.ts.map +1 -1
  126. package/lib/typescript/src/components/Skeleton.d.ts +1 -1
  127. package/lib/typescript/src/components/Skeleton.d.ts.map +1 -1
  128. package/lib/typescript/src/components/Surface.d.ts +1 -1
  129. package/lib/typescript/src/components/Surface.d.ts.map +1 -1
  130. package/lib/typescript/src/components/TextField.d.ts +11 -4
  131. package/lib/typescript/src/components/TextField.d.ts.map +1 -1
  132. package/lib/typescript/src/components/Toast.d.ts +10 -3
  133. package/lib/typescript/src/components/Toast.d.ts.map +1 -1
  134. package/lib/typescript/src/components/Toggle.d.ts +3 -2
  135. package/lib/typescript/src/components/Toggle.d.ts.map +1 -1
  136. package/lib/typescript/src/components/action-sheet.styles.d.ts +1 -1
  137. package/lib/typescript/src/components/action-sheet.styles.d.ts.map +1 -1
  138. package/lib/typescript/src/components/alert.styles.d.ts +14 -14
  139. package/lib/typescript/src/components/alert.styles.d.ts.map +1 -1
  140. package/lib/typescript/src/components/avatar.styles.d.ts +36 -1
  141. package/lib/typescript/src/components/avatar.styles.d.ts.map +1 -1
  142. package/lib/typescript/src/components/bottom-sheet.styles.d.ts +1 -1
  143. package/lib/typescript/src/components/bottom-sheet.styles.d.ts.map +1 -1
  144. package/lib/typescript/src/components/card.styles.d.ts +1 -1
  145. package/lib/typescript/src/components/card.styles.d.ts.map +1 -1
  146. package/lib/typescript/src/components/card.validate.d.ts +1 -1
  147. package/lib/typescript/src/components/card.validate.d.ts.map +1 -1
  148. package/lib/typescript/src/components/checkbox.styles.d.ts +6 -3
  149. package/lib/typescript/src/components/checkbox.styles.d.ts.map +1 -1
  150. package/lib/typescript/src/components/empty-state.styles.d.ts +1 -1
  151. package/lib/typescript/src/components/empty-state.styles.d.ts.map +1 -1
  152. package/lib/typescript/src/components/header.styles.d.ts +1 -1
  153. package/lib/typescript/src/components/header.styles.d.ts.map +1 -1
  154. package/lib/typescript/src/components/image.styles.d.ts +1 -1
  155. package/lib/typescript/src/components/image.styles.d.ts.map +1 -1
  156. package/lib/typescript/src/components/index.d.ts +18 -18
  157. package/lib/typescript/src/components/index.d.ts.map +1 -1
  158. package/lib/typescript/src/components/modal.styles.d.ts +13 -2
  159. package/lib/typescript/src/components/modal.styles.d.ts.map +1 -1
  160. package/lib/typescript/src/components/progress.styles.d.ts +1 -1
  161. package/lib/typescript/src/components/progress.styles.d.ts.map +1 -1
  162. package/lib/typescript/src/components/radio.styles.d.ts +10 -2
  163. package/lib/typescript/src/components/radio.styles.d.ts.map +1 -1
  164. package/lib/typescript/src/components/safe-area-registry.d.ts +47 -11
  165. package/lib/typescript/src/components/safe-area-registry.d.ts.map +1 -1
  166. package/lib/typescript/src/components/screen.styles.d.ts +1 -1
  167. package/lib/typescript/src/components/screen.styles.d.ts.map +1 -1
  168. package/lib/typescript/src/components/skeleton.styles.d.ts +1 -1
  169. package/lib/typescript/src/components/skeleton.styles.d.ts.map +1 -1
  170. package/lib/typescript/src/components/status-colors.d.ts +33 -0
  171. package/lib/typescript/src/components/status-colors.d.ts.map +1 -0
  172. package/lib/typescript/src/components/surface.styles.d.ts +1 -1
  173. package/lib/typescript/src/components/surface.styles.d.ts.map +1 -1
  174. package/lib/typescript/src/components/textfield.styles.d.ts +9 -5
  175. package/lib/typescript/src/components/textfield.styles.d.ts.map +1 -1
  176. package/lib/typescript/src/components/toast.styles.d.ts +34 -14
  177. package/lib/typescript/src/components/toast.styles.d.ts.map +1 -1
  178. package/lib/typescript/src/components/toggle.styles.d.ts +17 -1
  179. package/lib/typescript/src/components/toggle.styles.d.ts.map +1 -1
  180. package/lib/typescript/src/icons/Icon.d.ts +12 -6
  181. package/lib/typescript/src/icons/Icon.d.ts.map +1 -1
  182. package/lib/typescript/src/icons/icon.styles.d.ts +14 -2
  183. package/lib/typescript/src/icons/icon.styles.d.ts.map +1 -1
  184. package/lib/typescript/src/icons/index.d.ts +3 -3
  185. package/lib/typescript/src/icons/index.d.ts.map +1 -1
  186. package/lib/typescript/src/index.d.ts +4 -4
  187. package/lib/typescript/src/index.d.ts.map +1 -1
  188. package/lib/typescript/src/primitives/Button.d.ts +8 -4
  189. package/lib/typescript/src/primitives/Button.d.ts.map +1 -1
  190. package/lib/typescript/src/primitives/Metric.d.ts +1 -1
  191. package/lib/typescript/src/primitives/Metric.d.ts.map +1 -1
  192. package/lib/typescript/src/primitives/Row.d.ts +2 -2
  193. package/lib/typescript/src/primitives/Row.d.ts.map +1 -1
  194. package/lib/typescript/src/primitives/Stack.d.ts +2 -2
  195. package/lib/typescript/src/primitives/Stack.d.ts.map +1 -1
  196. package/lib/typescript/src/primitives/Tag.d.ts +2 -2
  197. package/lib/typescript/src/primitives/Tag.d.ts.map +1 -1
  198. package/lib/typescript/src/primitives/Text.d.ts +20 -6
  199. package/lib/typescript/src/primitives/Text.d.ts.map +1 -1
  200. package/lib/typescript/src/primitives/button.styles.d.ts +44 -7
  201. package/lib/typescript/src/primitives/button.styles.d.ts.map +1 -1
  202. package/lib/typescript/src/primitives/index.d.ts +6 -6
  203. package/lib/typescript/src/primitives/index.d.ts.map +1 -1
  204. package/lib/typescript/src/primitives/metric.styles.d.ts +1 -1
  205. package/lib/typescript/src/primitives/metric.styles.d.ts.map +1 -1
  206. package/lib/typescript/src/primitives/row.styles.d.ts +1 -1
  207. package/lib/typescript/src/primitives/row.styles.d.ts.map +1 -1
  208. package/lib/typescript/src/primitives/stack.styles.d.ts +1 -1
  209. package/lib/typescript/src/primitives/stack.styles.d.ts.map +1 -1
  210. package/lib/typescript/src/primitives/tag.styles.d.ts +34 -1
  211. package/lib/typescript/src/primitives/tag.styles.d.ts.map +1 -1
  212. package/lib/typescript/src/primitives/text.styles.d.ts +2 -4
  213. package/lib/typescript/src/primitives/text.styles.d.ts.map +1 -1
  214. package/lib/typescript/src/safe-area/index.d.ts +19 -5
  215. package/lib/typescript/src/safe-area/index.d.ts.map +1 -1
  216. package/lib/typescript/src/theme/ThemeProvider.d.ts +2 -2
  217. package/lib/typescript/src/theme/ThemeProvider.d.ts.map +1 -1
  218. package/lib/typescript/src/theme/define-theme.d.ts +15 -2
  219. package/lib/typescript/src/theme/define-theme.d.ts.map +1 -1
  220. package/lib/typescript/src/theme/index.d.ts +14 -13
  221. package/lib/typescript/src/theme/index.d.ts.map +1 -1
  222. package/lib/typescript/src/theme/letter-spacing.d.ts +15 -0
  223. package/lib/typescript/src/theme/letter-spacing.d.ts.map +1 -0
  224. package/lib/typescript/src/theme/line-height.d.ts +44 -0
  225. package/lib/typescript/src/theme/line-height.d.ts.map +1 -0
  226. package/lib/typescript/src/theme/native-theme.d.ts +1 -1
  227. package/lib/typescript/src/theme/native-theme.d.ts.map +1 -1
  228. package/lib/typescript/src/theme/types.d.ts +10 -1
  229. package/lib/typescript/src/theme/types.d.ts.map +1 -1
  230. package/llms.txt +2 -1
  231. package/package.json +22 -7
  232. package/src/a11y/tap-target.ts +38 -0
  233. package/src/components/ActionSheet.tsx +25 -15
  234. package/src/components/Alert.tsx +37 -10
  235. package/src/components/Avatar.tsx +12 -4
  236. package/src/components/BottomSheet.tsx +16 -13
  237. package/src/components/Card.tsx +9 -8
  238. package/src/components/Checkbox.tsx +12 -9
  239. package/src/components/EmptyState.tsx +7 -6
  240. package/src/components/Header.tsx +4 -4
  241. package/src/components/Image.tsx +9 -7
  242. package/src/components/Modal.tsx +22 -6
  243. package/src/components/Progress.tsx +5 -4
  244. package/src/components/Radio.tsx +11 -6
  245. package/src/components/Screen.tsx +66 -8
  246. package/src/components/Skeleton.tsx +6 -5
  247. package/src/components/Surface.tsx +5 -4
  248. package/src/components/TextField.tsx +25 -7
  249. package/src/components/Toast.tsx +33 -11
  250. package/src/components/Toggle.tsx +15 -6
  251. package/src/components/action-sheet.styles.ts +1 -1
  252. package/src/components/alert.styles.ts +13 -25
  253. package/src/components/avatar.styles.ts +25 -3
  254. package/src/components/bottom-sheet.styles.ts +1 -1
  255. package/src/components/card.styles.ts +1 -1
  256. package/src/components/card.validate.ts +2 -2
  257. package/src/components/checkbox.styles.ts +6 -3
  258. package/src/components/empty-state.styles.ts +1 -1
  259. package/src/components/header.styles.ts +1 -1
  260. package/src/components/image.styles.ts +1 -1
  261. package/src/components/index.ts +18 -18
  262. package/src/components/modal.styles.ts +11 -2
  263. package/src/components/progress.styles.ts +1 -1
  264. package/src/components/radio.styles.ts +11 -2
  265. package/src/components/safe-area-registry.ts +81 -14
  266. package/src/components/screen.styles.ts +1 -1
  267. package/src/components/skeleton.styles.ts +1 -1
  268. package/src/components/status-colors.ts +59 -0
  269. package/src/components/surface.styles.ts +1 -1
  270. package/src/components/textfield.styles.ts +18 -15
  271. package/src/components/toast.styles.ts +32 -25
  272. package/src/components/toggle.styles.ts +21 -1
  273. package/src/icons/Icon.tsx +13 -8
  274. package/src/icons/icon.styles.ts +38 -3
  275. package/src/icons/index.ts +3 -3
  276. package/src/index.ts +4 -4
  277. package/src/primitives/Button.tsx +28 -14
  278. package/src/primitives/Metric.tsx +5 -4
  279. package/src/primitives/Row.tsx +4 -4
  280. package/src/primitives/Stack.tsx +4 -4
  281. package/src/primitives/Tag.tsx +29 -11
  282. package/src/primitives/Text.tsx +42 -9
  283. package/src/primitives/_internal/focus-ring.tsx +1 -1
  284. package/src/primitives/button.styles.ts +63 -10
  285. package/src/primitives/index.ts +6 -6
  286. package/src/primitives/metric.styles.ts +9 -2
  287. package/src/primitives/row.styles.ts +1 -1
  288. package/src/primitives/stack.styles.ts +1 -1
  289. package/src/primitives/tag.styles.ts +43 -4
  290. package/src/primitives/text.styles.ts +10 -6
  291. package/src/safe-area/index.ts +21 -7
  292. package/src/theme/ThemeProvider.tsx +8 -5
  293. package/src/theme/define-theme.ts +33 -2
  294. package/src/theme/index.ts +18 -16
  295. package/src/theme/letter-spacing.ts +17 -0
  296. package/src/theme/line-height.ts +53 -0
  297. package/src/theme/native-theme.ts +9 -8
  298. package/src/theme/types.ts +10 -1
@@ -12,9 +12,9 @@
12
12
 
13
13
  import { useMemo } from "react";
14
14
  import { Text as RNText, View, type StyleProp, type ViewStyle } from "react-native";
15
- import { useTheme } from "../theme";
16
- import { CONTRACTS, type SizeOf } from "../contracts/contract-types";
17
- import { resolveMetricStyles } from "./metric.styles";
15
+ import { useTheme } from "../theme/index.js";
16
+ import { CONTRACTS, type SizeOf } from "../contracts/contract-types.js";
17
+ import { resolveMetricStyles } from "./metric.styles.js";
18
18
 
19
19
  type MetricSize = SizeOf<"metric">; // "sm" | "md" | "lg"
20
20
 
@@ -63,5 +63,6 @@ export function Metric({
63
63
  );
64
64
  }
65
65
 
66
- // Phase 2 conformance test 用の contract メタ(§2 A-3)。
66
+ // conformance test 用の contract メタ(§2 A-3)。scripts/lib/conformance.test.ts が
67
+ // 「この宣言が正しい contract を指しているか」を静的スキャンで照合する。
67
68
  Metric.__contract = CONTRACTS.metric;
@@ -9,10 +9,10 @@
9
9
 
10
10
  import { useMemo, type ReactNode } from "react";
11
11
  import { View, type StyleProp, type ViewStyle } from "react-native";
12
- import { useTheme } from "../theme";
13
- import type { SpacingKey } from "../theme";
14
- import { CONTRACTS } from "../contracts/contract-types";
15
- import { resolveRowStyle, type RowAlign, type RowJustify, type RowStyle } from "./row.styles";
12
+ import { useTheme } from "../theme/index.js";
13
+ import type { SpacingKey } from "../theme/index.js";
14
+ import { CONTRACTS } from "../contracts/contract-types.js";
15
+ import { resolveRowStyle, type RowAlign, type RowJustify, type RowStyle } from "./row.styles.js";
16
16
 
17
17
  interface RowProps {
18
18
  /** 子要素間の gap(spacing token キー限定)。省略時 gap なし。 */
@@ -9,10 +9,10 @@
9
9
 
10
10
  import { useMemo, type ReactNode } from "react";
11
11
  import { View, type StyleProp, type ViewStyle } from "react-native";
12
- import { useTheme } from "../theme";
13
- import type { SpacingKey } from "../theme";
14
- import { CONTRACTS } from "../contracts/contract-types";
15
- import { resolveStackStyle, type StackAlign, type StackStyle } from "./stack.styles";
12
+ import { useTheme } from "../theme/index.js";
13
+ import type { SpacingKey } from "../theme/index.js";
14
+ import { CONTRACTS } from "../contracts/contract-types.js";
15
+ import { resolveStackStyle, type StackAlign, type StackStyle } from "./stack.styles.js";
16
16
 
17
17
  interface StackProps {
18
18
  /** 子要素間の gap(spacing token キー限定)。省略時 gap なし。 */
@@ -9,16 +9,21 @@
9
9
  * 色・寸法の決定は pure resolver(tag.styles.ts)に分離、recipe との機械照合は
10
10
  * scripts/lib/tag-conformance.test.ts が行う。
11
11
  * - label は Text primitive。filter-chip active の文字色だけ primary.700(SemanticColors 外なので style 上書き)。
12
- * - focus outline は Button(Step8) で Pressable focus handling を共通化してから Tag にも適用する
13
- * (§1 は Phase1 scope だが acceptance 外。今は accessibilityState のみ。Button 実装時に回収)。
12
+ * - focus outline は Button と共通の Pressable focus handling(_internal/focus-ring)を使う
13
+ * (removable / filter-chip の押せる要素に FocusRing を描く)。
14
14
  */
15
15
 
16
16
  import { Pressable, View, type StyleProp, type ViewStyle } from "react-native";
17
- import { Text } from "./Text";
18
- import { useTheme } from "../theme";
19
- import { useFocusRing, FocusRing } from "./_internal/focus-ring";
20
- import { CONTRACTS } from "../contracts/contract-types";
21
- import { resolveTagBase, resolveTagVariant } from "./tag.styles";
17
+ import { Text } from "./Text.js";
18
+ import { useTheme } from "../theme/index.js";
19
+ import { useFocusRing, FocusRing } from "./_internal/focus-ring.js";
20
+ import { CONTRACTS } from "../contracts/contract-types.js";
21
+ import {
22
+ resolveTagBase,
23
+ resolveTagVariant,
24
+ TAG_FILTER_CHIP_VERTICAL_HIT_SLOP,
25
+ TAG_REMOVE_TAP_TARGET,
26
+ } from "./tag.styles.js";
22
27
 
23
28
  interface TagBase {
24
29
  label: string;
@@ -53,6 +58,12 @@ function FilterChipTag(props: TagBase & { selected: boolean; onToggle: () => voi
53
58
  onPress={onToggle}
54
59
  accessibilityRole="button"
55
60
  accessibilityState={{ selected }}
61
+ // 視覚高さ 34pt(padding 駆動)に縦 hitSlop を足して実効 44pt(A11Y_MIN_TAP_TARGET_44)。
62
+ // 背景と枠を持つので minHeight で伸ばすと見た目が変わる → 当たり判定だけ広げる。
63
+ hitSlop={{
64
+ top: TAG_FILTER_CHIP_VERTICAL_HIT_SLOP,
65
+ bottom: TAG_FILTER_CHIP_VERTICAL_HIT_SLOP,
66
+ }}
56
67
  testID={testID}
57
68
  style={[
58
69
  base,
@@ -86,14 +97,20 @@ function RemovableTag(
86
97
  <Text variant={v.font} color="text-default">
87
98
  {label}
88
99
  </Text>
89
- {/* 最小タップターゲット確保: 親 Tag の高さを超えられないので remove 自身に min 24 + hitSlop(Codex M-2)。 */}
100
+ {/* 最小タップターゲット確保: 親 Tag の高さを超えられないので remove 自身に正典パターン
101
+ (視覚 24 + hitSlop 10 = 実効 44)を持たせる(Codex M-2 / A11Y_MIN_TAP_TARGET_44)。 */}
90
102
  <Pressable
91
103
  {...focusHandlers}
92
104
  onPress={onRemove}
93
105
  accessibilityRole="button"
94
106
  accessibilityLabel={removeAccessibilityLabel}
95
- hitSlop={10}
96
- style={{ minWidth: 24, minHeight: 24, alignItems: "center", justifyContent: "center" }}
107
+ hitSlop={TAG_REMOVE_TAP_TARGET.hitSlop}
108
+ style={{
109
+ minWidth: TAG_REMOVE_TAP_TARGET.minWidth,
110
+ minHeight: TAG_REMOVE_TAP_TARGET.minHeight,
111
+ alignItems: "center",
112
+ justifyContent: "center",
113
+ }}
97
114
  >
98
115
  <Text variant="xs" color="text-muted">
99
116
  ×
@@ -122,5 +139,6 @@ export function Tag(props: TagProps) {
122
139
  );
123
140
  }
124
141
 
125
- // Phase 2 conformance test 用の contract メタ(§2 A-3)。
142
+ // conformance test 用の contract メタ(§2 A-3)。scripts/lib/conformance.test.ts が
143
+ // 「この宣言が正しい contract を指しているか」を静的スキャンで照合する。
126
144
  Tag.__contract = CONTRACTS.tag;
@@ -6,18 +6,21 @@
6
6
  * 構造一致している前提で theme.typography.fontSize[variant] を引く。乖離したら型エラーで検知
7
7
  * される(= A-3 conformance の型レベル最低ライン、§2)。
8
8
  * - 色は token キーのみ(生 hex 不可、§5 lint の意味を保つ)。
9
+ * - fontScale 制御(allowFontScaling / maxFontSizeMultiplier)は RN Text へ素通しする。
10
+ * 既定は未指定 = RN 既定(OS の文字サイズ設定に追随)。器が固定寸法の箇所だけ opt-in で絞る。
9
11
  * - letterSpacing は role(heading/body) の ratio を fontSize から pt 換算(text.styles.ts)。
10
12
  * - 形状(fontSize/lineHeight/letterSpacing/fontWeight)は variant/role/weight 依存なので useMemo で
11
- * 分離(B-3 の「形状/色 分離 + 参照安定性」を Phase1 は useMemo で満たす。StyleSheet 事前生成の
12
- * 最適化は Card で実証予定)。色のみ render 時に colors から取る。
13
+ * 分離(B-3 の「形状/色 分離 + 参照安定性」は useMemo で満たす。StyleSheet 事前生成による
14
+ * 最適化はどのコンポーネントでも採っていない)。色のみ render 時に colors から取る。
13
15
  */
14
16
 
15
17
  import { useMemo, type ReactNode } from "react";
16
- import { Text as RNText, type StyleProp, type TextStyle } from "react-native";
17
- import { useTheme } from "../theme";
18
- import type { FontWeightKey, SemanticColors } from "../theme";
19
- import { CONTRACTS, type VariantOf } from "../contracts/contract-types";
20
- import { resolveTextShape, type TextRole } from "./text.styles";
18
+ import { StyleSheet, Text as RNText, type StyleProp, type TextStyle } from "react-native";
19
+ import { useTheme } from "../theme/index.js";
20
+ import { minLineHeightFor, minRatioOf } from "../theme/line-height.js";
21
+ import type { FontWeightKey, SemanticColors } from "../theme/index.js";
22
+ import { CONTRACTS, type VariantOf } from "../contracts/contract-types.js";
23
+ import { resolveTextShape, type TextRole } from "./text.styles.js";
21
24
 
22
25
  type TextVariant = VariantOf<"text">;
23
26
 
@@ -31,6 +34,18 @@ interface TextProps {
31
34
  /** letterSpacing の切替軸。default "body"。 */
32
35
  role?: TextRole;
33
36
  numberOfLines?: number;
37
+ /**
38
+ * OS の文字サイズ設定に追随するか(RN Text へそのまま透過)。既定は未指定 = RN 既定(true)。
39
+ * **原則は既定のまま**(false にすると視覚障害者の拡大設定を無視することになる)。
40
+ * 固定寸法の図版・チャートのラベルなど、拡大するとレイアウトが壊れる箇所だけ false にする。
41
+ */
42
+ allowFontScaling?: boolean;
43
+ /**
44
+ * 文字サイズ倍率の上限(RN Text へそのまま透過)。既定は未指定 = RN 既定(上限なし)。
45
+ * 拡大は許しつつ器からの溢れだけ抑えたいとき(円形の Avatar initials 等)に使う。
46
+ * `0` は「上限なし」、`1` は「拡大しない」という RN の規約に従う。
47
+ */
48
+ maxFontSizeMultiplier?: number;
34
49
  style?: StyleProp<TextStyle>;
35
50
  testID?: string;
36
51
  children: ReactNode;
@@ -42,6 +57,8 @@ export function Text({
42
57
  color = "text-default",
43
58
  role = "body",
44
59
  numberOfLines,
60
+ allowFontScaling,
61
+ maxFontSizeMultiplier,
45
62
  style,
46
63
  testID,
47
64
  children,
@@ -55,10 +72,25 @@ export function Text({
55
72
  [theme, variant, role, weight],
56
73
  );
57
74
 
75
+ // 消費者 style は合成順で最後=上書きが勝つため、resolver 内のクランプだけでは
76
+ // `style={{lineHeight: 16}}` を止められない。flatten した**最終値**に行間の安全下限を掛ける
77
+ // (fontSize の上書きにも追随させるため、下限は最終 fontSize から算出。機序は theme/line-height.ts)。
78
+ const flat: TextStyle = StyleSheet.flatten([shape, { color: colors[color] }, style]);
79
+ // `fontSize: undefined` の明示上書き(flatten は undefined でも上書きする)は、RN 既定サイズで
80
+ // 描画されるのにクランプ基準だけ variant 値になる齟齬を生む。variant の fontSize を復元して
81
+ // 「Text の文字サイズは variant が決める」契約とクランプ基準を一致させる。
82
+ if (typeof flat.fontSize !== "number") flat.fontSize = shape.fontSize;
83
+ if (flat.lineHeight !== undefined) {
84
+ const floor = minLineHeightFor(flat.fontSize ?? 0, minRatioOf(theme));
85
+ if (flat.lineHeight < floor) flat.lineHeight = floor;
86
+ }
87
+
58
88
  return (
59
89
  <RNText
60
- style={[shape, { color: colors[color] }, style]}
90
+ style={flat}
61
91
  numberOfLines={numberOfLines}
92
+ allowFontScaling={allowFontScaling}
93
+ maxFontSizeMultiplier={maxFontSizeMultiplier}
62
94
  accessibilityRole={role === "heading" ? "header" : undefined}
63
95
  testID={testID}
64
96
  >
@@ -67,5 +99,6 @@ export function Text({
67
99
  );
68
100
  }
69
101
 
70
- // Phase 2 conformance test 用の contract メタ(§2 A-3)。
102
+ // conformance test 用の contract メタ(§2 A-3)。scripts/lib/conformance.test.ts が
103
+ // 「この宣言が正しい contract を指しているか」を静的スキャンで照合する。
71
104
  Text.__contract = CONTRACTS.text;
@@ -17,7 +17,7 @@
17
17
 
18
18
  import { useState } from "react";
19
19
  import { View } from "react-native";
20
- import { useTheme } from "../../theme";
20
+ import { useTheme } from "../../theme/index.js";
21
21
 
22
22
  export function useFocusRing() {
23
23
  const [focused, setFocused] = useState(false);
@@ -1,12 +1,16 @@
1
1
  /**
2
- * button.styles — Button の pure style resolver(P4 conformance 対応で Button.tsx から分離)。
2
+ * button.styles — Button の pure style resolver(styleRefs conformance 対応で Button.tsx から分離)。
3
3
  *
4
4
  * react-native を import しない純粋モジュールにすることで node テスト(tsx --test)から
5
5
  * 直接実行できる。recipes/app/button.recipe.json(melta-contracts の styleRefs)との
6
- * 機械照合は scripts/lib/recipe-conformance.test.ts が行う。
6
+ * 機械照合は scripts/lib/recipe-conformance.test.ts が行う(層B)。
7
+ *
8
+ * ⚠️ このファイルは resolver 機構の**最初の実装**で、戻り値が slot 単位に揃っていない
9
+ * (色だけを返し、寸法は component 側で組む)。現行の書き方は AGENTS.md「style resolver の規約」
10
+ * を参照すること — 新規 / 改修は alert.styles.ts・textfield.styles.ts の形に寄せる。
7
11
  */
8
12
 
9
- import type { NativeTheme, ThemeMode, FontSizeKey, SpacingKey } from "../theme";
13
+ import type { NativeTheme, ThemeMode, FontSizeKey, SpacingKey } from "../theme/index.js";
10
14
 
11
15
  export type ButtonVariant =
12
16
  | "contained"
@@ -18,22 +22,71 @@ export type ButtonVariant =
18
22
  | "subtle";
19
23
  export type ButtonSize = "small" | "medium" | "large";
20
24
 
21
- /** size → height / 横 padding / label fontSize / icon box(contract sizes/iconButton と整合)。 */
22
- export const SIZE_SPEC: Record<
25
+ /**
26
+ * size → 最小高さ / 横 padding / label fontSize / icon box(contract sizes/iconButton と整合)。
27
+ *
28
+ * `minHeight`(`height` ではない)なのは fontScale 対策。label は Text primitive の
29
+ * lineHeight(base = 36pt)で伸びるため、height 固定だと fontScale 1.12x あたりから
30
+ * medium(40pt)で文字がクリップする。recipe 側も 0.7.0 で `minHeight` に変わっている。
31
+ * iconOnly だけは正方形を保つため width/height 固定のまま(recipe の iconOnlyWidth/iconOnlyHeight)。
32
+ */
33
+ export const BUTTON_SIZE_SPEC: Record<
23
34
  ButtonSize,
24
- { height: number; px: SpacingKey; font: FontSizeKey; iconBox: number }
35
+ { minHeight: number; px: SpacingKey; font: FontSizeKey; iconBox: number }
25
36
  > = {
26
- small: { height: 32, px: "3", font: "sm", iconBox: 32 },
27
- medium: { height: 40, px: "4", font: "base", iconBox: 40 },
28
- large: { height: 48, px: "6", font: "base", iconBox: 48 },
37
+ small: { minHeight: 32, px: "3", font: "sm", iconBox: 32 },
38
+ medium: { minHeight: 40, px: "4", font: "base", iconBox: 40 },
39
+ large: { minHeight: 48, px: "6", font: "base", iconBox: 48 },
29
40
  };
30
41
 
42
+ /**
43
+ * size → 縦方向の片側 hitSlop(実効タップ標的 44pt 確保。A11Y_MIN_TAP_TARGET_44)。
44
+ *
45
+ * 視覚寸法は据え置き、当たり判定だけを広げる:
46
+ * small 32 + 6*2 = 44 / medium 40 + 2*2 = 44 / large 48(既に 44 以上なので 0)
47
+ *
48
+ * ⚠️ literal で持つ(BUTTON_SIZE_SPEC から自動導出しない)。導出にすると視覚寸法を変えたときに
49
+ * hitSlop が黙って追随してしまい、conformance が構造的に fail-open になる。
50
+ */
51
+ export const BUTTON_VERTICAL_HIT_SLOP: Record<ButtonSize, number> = {
52
+ small: 6,
53
+ medium: 2,
54
+ large: 0,
55
+ };
56
+
57
+ /** Pressable の hitSlop(RN の Insets)。 */
58
+ export interface ButtonHitSlop {
59
+ top: number;
60
+ bottom: number;
61
+ left?: number;
62
+ right?: number;
63
+ }
64
+
65
+ /**
66
+ * size / iconOnly → Pressable に渡す hitSlop。0 のときは undefined(prop を付けない)。
67
+ *
68
+ * - labeled: **縦だけ**。横は付けない — Row の gap 0 で隣接した Button 同士の当たり判定が
69
+ * 重なって押し違いが起きるため(横方向は padding で既に広い)。
70
+ * - iconOnly: 幅も 32/40 と狭い正方形なので**横にも同値**を付ける(左右の隣接は
71
+ * iconOnly ボタンを gap 0 で並べない前提。並べる場合は消費者側で gap を取る)。
72
+ */
73
+ export function resolveButtonHitSlop(
74
+ size: ButtonSize,
75
+ iconOnly: boolean,
76
+ ): ButtonHitSlop | undefined {
77
+ const slop = BUTTON_VERTICAL_HIT_SLOP[size];
78
+ if (slop === 0) return undefined;
79
+ return iconOnly
80
+ ? { top: slop, bottom: slop, left: slop, right: slop }
81
+ : { top: slop, bottom: slop };
82
+ }
83
+
31
84
  /**
32
85
  * variant → 色解決(button.contract tokenRefs の 1:1 写像)。
33
86
  * 文字色は variant により semantic 外(primary.500 等)もあるため hex 文字列で返し、
34
87
  * Text の style で color 上書きする(Text の color prop は SemanticColors キー限定のため)。
35
88
  */
36
- export function resolveVariant(
89
+ export function resolveButtonColors(
37
90
  theme: NativeTheme,
38
91
  mode: ThemeMode,
39
92
  variant: ButtonVariant,
@@ -1,8 +1,8 @@
1
1
  /** melta-app primitives の公開エントリ。実装ごとにここへ追記する(設計書 §1)。 */
2
2
 
3
- export { Text } from "./Text";
4
- export { Metric } from "./Metric";
5
- export { Tag } from "./Tag";
6
- export { Button } from "./Button";
7
- export { Stack } from "./Stack";
8
- export { Row } from "./Row";
3
+ export { Text } from "./Text.js";
4
+ export { Metric } from "./Metric.js";
5
+ export { Tag } from "./Tag.js";
6
+ export { Button } from "./Button.js";
7
+ export { Stack } from "./Stack.js";
8
+ export { Row } from "./Row.js";
@@ -7,7 +7,9 @@
7
7
  */
8
8
 
9
9
  import type { FontVariant } from "react-native";
10
- import type { NativeTheme, ThemeMode, FontSizeKey, FontWeightValue } from "../theme";
10
+ import type { NativeTheme, ThemeMode, FontSizeKey, FontWeightValue } from "../theme/index.js";
11
+ // theme index からの runtime import は ThemeProvider → react-native を引くため pure module を直接参照
12
+ import { clampLineHeight, minRatioOf } from "../theme/line-height.js";
11
13
 
12
14
  export type MetricSize = "sm" | "md" | "lg";
13
15
 
@@ -56,7 +58,12 @@ export function resolveMetricStyles(
56
58
  return {
57
59
  valueStyle: {
58
60
  fontSize: fontSize[font.value].fontSize,
59
- lineHeight: fontSize[font.value].lineHeight,
61
+ // カスタム theme の未クランプ値への防波堤(text.styles.ts と同じ。根拠は theme/line-height.ts)
62
+ lineHeight: clampLineHeight(
63
+ fontSize[font.value].fontSize,
64
+ fontSize[font.value].lineHeight,
65
+ minRatioOf(theme),
66
+ ),
60
67
  fontWeight: theme.typography.fontWeight.bold,
61
68
  color: sem["text-heading"],
62
69
  fontVariant: ["tabular-nums"],
@@ -6,7 +6,7 @@
6
6
  * 機械照合は scripts/lib/row-conformance.test.ts が行う。
7
7
  */
8
8
 
9
- import type { NativeTheme, SpacingKey } from "../theme";
9
+ import type { NativeTheme, SpacingKey } from "../theme/index.js";
10
10
 
11
11
  /** 交差軸の配置(RN alignItems への写像)。default "center"(recipe default に焼いてある)。 */
12
12
  export type RowAlign = "start" | "center" | "end" | "baseline" | "stretch";
@@ -6,7 +6,7 @@
6
6
  * 機械照合は scripts/lib/stack-conformance.test.ts が行う。
7
7
  */
8
8
 
9
- import type { NativeTheme, SpacingKey } from "../theme";
9
+ import type { NativeTheme, SpacingKey } from "../theme/index.js";
10
10
 
11
11
  /** 交差軸の配置(RN alignItems への写像。default stretch = RN デフォルトなので出力しない)。 */
12
12
  export type StackAlign = "start" | "center" | "end" | "stretch";
@@ -6,10 +6,49 @@
6
6
  * 機械照合は scripts/lib/tag-conformance.test.ts が行う。
7
7
  */
8
8
 
9
- import type { NativeTheme, ThemeMode, FontSizeKey } from "../theme";
9
+ import type { NativeTheme, ThemeMode, FontSizeKey } from "../theme/index.js";
10
+ import { CANONICAL_TAP_TARGET } from "../a11y/tap-target.js";
11
+ import { clampLineHeight, minRatioOf } from "../theme/line-height.js";
10
12
 
11
13
  export type TagVariant = "basic" | "removable" | "filter-chip";
12
14
 
15
+ /** label の fontSize token キー(component / resolver / 検算が同じキーを読む SSOT)。 */
16
+ export const TAG_SPEC = {
17
+ /** basic / removable の label。 */
18
+ labelFont: "xs",
19
+ /** filter-chip の label(押せる要素なので一段大きい)。 */
20
+ chipLabelFont: "sm",
21
+ } as const satisfies { labelFont: FontSizeKey; chipLabelFont: FontSizeKey };
22
+
23
+ /**
24
+ * removable の × の当たり判定(正典パターン: 視覚 24pt + hitSlop 10 = 実効 44pt)。
25
+ * 親 Tag の高さ(padding 駆動で 34pt 前後)を超えられないので × 自身に持たせる。
26
+ */
27
+ export const TAG_REMOVE_TAP_TARGET = CANONICAL_TAP_TARGET;
28
+
29
+ /**
30
+ * filter-chip(押せる chip 本体)の縦 hitSlop。
31
+ *
32
+ * chip は背景と枠を持つので minHeight で伸ばすと**見た目が変わる**(他 variant と高さが揃わない)。
33
+ * よって視覚寸法は据え置き、当たり判定だけ広げる:
34
+ * 視覚高さ 34pt(paddingVertical 4 × 2 + label sm の lineHeight 26)+ 5 × 2 = 44pt。
35
+ *
36
+ * padding 駆動なので fontScale で視覚高さ自体が伸びる = 拡大時に過剰な当たり判定にならない。
37
+ * ⚠️ literal で持つ(resolveTagFilterChipVisualHeight から導出しない)。導出にすると
38
+ * 寸法変更に hitSlop が黙って追随し、conformance が fail-open になる。
39
+ */
40
+ export const TAG_FILTER_CHIP_VERTICAL_HIT_SLOP = 5;
41
+
42
+ /**
43
+ * filter-chip の視覚高さ(padding 駆動)。conformance が 44pt 到達を検算するための実測値。
44
+ * = paddingVertical × 2 + label(sm)の lineHeight。
45
+ */
46
+ export function resolveTagFilterChipVisualHeight(theme: NativeTheme): number {
47
+ const font = theme.typography.fontSize[TAG_SPEC.chipLabelFont];
48
+ const lineHeight = clampLineHeight(font.fontSize, font.lineHeight, minRatioOf(theme));
49
+ return resolveTagBase(theme).paddingVertical * 2 + lineHeight;
50
+ }
51
+
13
52
  /** 共通形状(px-3 py-1 gap-1 rounded-full。tag.recipe の全 variant style と共通の寸法キー)。 */
14
53
  export function resolveTagBase(theme: NativeTheme): {
15
54
  gap: number;
@@ -44,7 +83,7 @@ export function resolveTagVariant(
44
83
  switch (variant) {
45
84
  case "basic":
46
85
  case "removable":
47
- return { bg: sem["bg-page-alt"], textColor: sem["text-default"], font: "xs" };
86
+ return { bg: sem["bg-page-alt"], textColor: sem["text-default"], font: TAG_SPEC.labelFont };
48
87
  case "filter-chip":
49
88
  return selected
50
89
  ? {
@@ -52,14 +91,14 @@ export function resolveTagVariant(
52
91
  borderWidth: 1,
53
92
  border: c.primary["200"],
54
93
  textColor: c.primary["700"],
55
- font: "sm",
94
+ font: TAG_SPEC.chipLabelFont,
56
95
  }
57
96
  : {
58
97
  bg: sem["bg-surface"],
59
98
  borderWidth: 1,
60
99
  border: sem["border-default"],
61
100
  textColor: sem["text-default"],
62
- font: "sm",
101
+ font: TAG_SPEC.chipLabelFont,
63
102
  };
64
103
  }
65
104
  }
@@ -6,7 +6,11 @@
6
6
  * 機械照合は scripts/lib/text-conformance.test.ts が行う。
7
7
  */
8
8
 
9
- import type { NativeTheme, FontSizeKey, FontWeightKey, FontWeightValue } from "../theme";
9
+ import type { NativeTheme, FontSizeKey, FontWeightKey, FontWeightValue } from "../theme/index.js";
10
+ // theme index からの runtime import は ThemeProvider → react-native を引くため、
11
+ // pure module(line-height.ts / letter-spacing.ts)を直接参照する。
12
+ import { clampLineHeight, minRatioOf } from "../theme/line-height.js";
13
+ import { resolveLetterSpacing } from "../theme/letter-spacing.js";
10
14
 
11
15
  /** letterSpacing の切替軸(text.recipe description の role prop に対応)。 */
12
16
  export type TextRole = "heading" | "body";
@@ -22,9 +26,7 @@ export interface TextShape {
22
26
  /**
23
27
  * variant / role / weight → 形状解決(text.recipe styleRefs の 1:1 写像)。
24
28
  * - fontSize token は複合値(px + lineHeight 比率)。theme 生成時に lineHeight は px 化済み。
25
- * - letterSpacing は em 比率 token のため fontSize × ratio で pt 化する。
26
- * theme の resolveLetterSpacing と同一式だが、theme index 経由の runtime import は
27
- * ThemeProvider → react-native を引くため、pure に保つ目的でここでは式を直書きする。
29
+ * - letterSpacing は em 比率 token のため fontSize × ratio で pt 化する(resolveLetterSpacing)。
28
30
  */
29
31
  export function resolveTextShape(
30
32
  theme: NativeTheme,
@@ -35,8 +37,10 @@ export function resolveTextShape(
35
37
  const fs = theme.typography.fontSize[variant];
36
38
  return {
37
39
  fontSize: fs.fontSize,
38
- lineHeight: fs.lineHeight,
39
- letterSpacing: fs.fontSize * theme.typography.letterSpacingRatio[role],
40
+ // 既定 theme は codegen 時点でクランプ済みだが、defineTheme で注入されたカスタム theme の
41
+ // 値はここが最初の防波堤(機序と下限の根拠は theme/line-height.ts)。
42
+ lineHeight: clampLineHeight(fs.fontSize, fs.lineHeight, minRatioOf(theme)),
43
+ letterSpacing: resolveLetterSpacing(fs.fontSize, theme.typography.letterSpacingRatio[role]),
40
44
  ...(weight ? { fontWeight: theme.typography.fontWeight[weight] } : null),
41
45
  };
42
46
  }
@@ -11,9 +11,10 @@
11
11
  * import { enableSafeAreaContext } from "melta-app/safe-area";
12
12
  * enableSafeAreaContext();
13
13
  *
14
- * これで Screen の SafeArea が useSafeAreaInsets() + View の adapter に切り替わる。
15
- * Provider の context 値を render 中に同期参照して padding を付けるため、native
16
- * SafeAreaView の layout/state 更新を待たず、初回 render から inset が反映される。
14
+ * これで Screen / ActionSheet / BottomSheet の SafeArea が useSafeAreaInsets() + View の
15
+ * adapter に切り替わる。Provider の context 値を render 中に同期参照して padding を付けるため、
16
+ * native SafeAreaView の layout/state 更新を待たず、初回 render から inset が反映される。
17
+ * Android でも効く(RN core の SafeAreaView は Android では完全 no-op)。
17
18
  *
18
19
  * ⚠️ 前提: アプリの root に SafeAreaProvider が必要(無いと useSafeAreaInsets が throw
19
20
  * する)。初回 render から正しい inset を使うには Provider に initialMetrics も渡す。
@@ -35,13 +36,21 @@ import {
35
36
  setSafeAreaView,
36
37
  type SafeAreaViewLike,
37
38
  type SafeAreaViewLikeProps,
38
- } from "../components/safe-area-registry";
39
+ } from "../components/safe-area-registry.js";
39
40
 
40
41
  const ALL_EDGES = ["top", "right", "bottom", "left"] as const satisfies readonly Edge[];
41
42
 
42
43
  /** enableSafeAreaContext() の設定。edges 省略時は従来相当の全 edge。 */
43
44
  export interface EnableSafeAreaContextOptions {
44
- /** Screen に適用する edge。tab bar 等が担当する edge は除外する。 */
45
+ /**
46
+ * **Screen の既定 edge**(`edges` prop を渡さなかった Screen に適用される)。
47
+ * tab bar 等が担当する edge は除外する。
48
+ *
49
+ * ⚠️ 0.5.x までは「アプリ全体で 1 個のグローバル」だったが、edge は消費者ごとに
50
+ * 決まるべきものなので既定値の意味に変わった。ActionSheet / BottomSheet は画面下端に
51
+ * 出る都合上、ここの指定に関わらず自前の edge(bottom + 左右)を使う。
52
+ * 画面ごとに変えたい場合は `<Screen edges={["top"]} />` のように Screen 側で指定する。
53
+ */
45
54
  edges?: readonly Edge[];
46
55
  }
47
56
 
@@ -100,7 +109,12 @@ function createContextSafeAreaView(selectedEdges: readonly Edge[]): SafeAreaView
100
109
  return ContextSafeAreaView;
101
110
  }
102
111
 
103
- /** Screen の SafeArea を context 同期参照の View adapter に切り替える。 */
112
+ /**
113
+ * melta の SafeArea を context 同期参照の View adapter に切り替える。
114
+ *
115
+ * registry には**ファクトリ**を渡す(消費者ごとに edges が違うため)。同じ edges 集合には
116
+ * 同じコンポーネント参照が返るよう memo するのは registry 側の責務。
117
+ */
104
118
  export function enableSafeAreaContext(options: EnableSafeAreaContextOptions = {}): void {
105
- setSafeAreaView(createContextSafeAreaView(options.edges ?? ALL_EDGES));
119
+ setSafeAreaView(createContextSafeAreaView, options.edges ?? ALL_EDGES);
106
120
  }
@@ -14,9 +14,9 @@
14
14
 
15
15
  import { createContext, useContext, useMemo, type ReactNode } from "react";
16
16
  import { useColorScheme } from "react-native";
17
- import { nativeTheme } from "./native-theme";
18
- import { defineTheme, isDev, resolveMode, type ResolvedCapabilities, type ResolvedNativeTheme, type ThemeModeViolation } from "./define-theme";
19
- import type { NativeTheme, SemanticColors, ThemeMode } from "./types";
17
+ import { nativeTheme } from "./native-theme.js";
18
+ import { defineTheme, isDev, resolveMode, type ResolvedCapabilities, type ResolvedNativeTheme, type ThemeModeViolation } from "./define-theme.js";
19
+ import type { NativeTheme, SemanticColors, ThemeMode } from "./types.js";
20
20
 
21
21
  export type { ThemeMode };
22
22
 
@@ -41,8 +41,11 @@ const ThemeContext = createContext<ThemeContextValue | null>(null);
41
41
 
42
42
  /**
43
43
  * 既定 theme。melta-contracts 由来の codegen 結果を注入経路と同じ形に通す(自分でも dogfood する)。
44
- * 副作用として dev では `nativeTheme` の入れ子オブジェクト(トークン群)も freeze される
45
- * — 生成物のトークンを実行時に書き換える経路は元々無いので、意図した副作用として許容する。
44
+ *
45
+ * dev で freeze されるのは **`defineTheme()` の戻り値**(= `useTheme().theme` として配られる
46
+ * 解決済み theme)で、入れ子のトークン群まで再帰的に凍る。公開 export の `nativeTheme` 自体は
47
+ * `defineTheme` 内の `cloneTokenTree` で参照が切り離されているので**凍らない**
48
+ * (0.4.3 で意図的にこう変えた: 凍らせると `{ ...nativeTheme }` からの正当な派生が dev で壊れるため)。
46
49
  */
47
50
  const DEFAULT_THEME: ResolvedNativeTheme = defineTheme({ ...nativeTheme, id: "melta" });
48
51