melta-app 0.2.0 → 0.2.2
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/lib/module/components/Screen.js +2 -2
- package/lib/module/components/TextField.js +29 -2
- package/lib/module/components/TextField.js.map +1 -1
- package/lib/module/components/safe-area-registry.js +3 -2
- package/lib/module/components/safe-area-registry.js.map +1 -1
- package/lib/module/safe-area/index.js +56 -6
- package/lib/module/safe-area/index.js.map +1 -1
- package/lib/typescript/src/components/Screen.d.ts +2 -2
- package/lib/typescript/src/components/TextField.d.ts +15 -1
- package/lib/typescript/src/components/TextField.d.ts.map +1 -1
- package/lib/typescript/src/components/safe-area-registry.d.ts +3 -2
- package/lib/typescript/src/components/safe-area-registry.d.ts.map +1 -1
- package/lib/typescript/src/safe-area/index.d.ts +15 -4
- package/lib/typescript/src/safe-area/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/Screen.tsx +2 -2
- package/src/components/TextField.tsx +28 -1
- package/src/components/safe-area-registry.ts +3 -2
- package/src/safe-area/index.ts +69 -10
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* SafeArea は registry(safe-area-registry.ts)で解決する。default は RN core の
|
|
14
14
|
* SafeAreaView(deprecated / iOS のみの最小対応)で依存ゼロを維持しつつ、
|
|
15
15
|
* react-native-safe-area-context 利用者は subpath "melta-app/safe-area" の
|
|
16
|
-
* enableSafeAreaContext()
|
|
17
|
-
*
|
|
16
|
+
* enableSafeAreaContext() で context hook + View adapter に差し替えられる
|
|
17
|
+
* (rally-nav dogfood 由来、RN 0.85 Fabric/Android の初回 inset flash 対応)。
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
import { useMemo } from "react";
|
|
@@ -15,11 +15,38 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import { useMemo, useState } from "react";
|
|
18
|
-
import { TextInput, View, Text as RNText } from "react-native";
|
|
18
|
+
import { Platform, TextInput, View, Text as RNText } from "react-native";
|
|
19
19
|
import { useTheme } from "../theme/index.js";
|
|
20
20
|
import { CONTRACTS } from "../contracts/contract-types.js";
|
|
21
21
|
import { resolveTextFieldStyle, resolveTextFieldFocusStyle } from "./textfield.styles.js";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Android の EditText は固定 height と組み合わさると既定の縦 padding で文字が
|
|
25
|
+
* 上寄り・下端切れになるため、縦 padding を殺して縦センターに揃える。
|
|
26
|
+
* textAlignVertical は Android 専用、paddingVertical: 0 は iOS の見た目を変えない
|
|
27
|
+
* (iOS の単一行入力は元々縦センター)。
|
|
28
|
+
* さらに Android では Noto CJK のフォントメトリクス由来で文字インクが
|
|
29
|
+
* 1dp 強下寄りになる(Pixel 実機ピクセル実測 +2.7px/density2.5。
|
|
30
|
+
* includeFontPadding: false は gravity center 下では無効なことも実測済み)ため、
|
|
31
|
+
* paddingBottom の光学ナッジで打ち消す(gravity center は残り高さの中央に置くので
|
|
32
|
+
* paddingBottom p で p/2 上がる)。
|
|
33
|
+
* recipe(styleRefs)の写像ではなく RN 実装の補正なので resolver には置かない。
|
|
34
|
+
*/
|
|
35
|
+
/* eslint-disable melta/no-raw-spacing -- spacing の選択ではなく EditText 既定 padding の打ち消しと CJK 光学ナッジ(実測由来の固定値) */
|
|
36
|
+
/** OS 毎の補正値を返す純関数(jest は Platform=ios 固定のため、Android 分岐はこれを直接検証する)。 */
|
|
22
37
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
38
|
+
export function resolveInputVerticalFix(os) {
|
|
39
|
+
return {
|
|
40
|
+
paddingVertical: 0,
|
|
41
|
+
...(os === "android" ? {
|
|
42
|
+
paddingBottom: 2
|
|
43
|
+
} : null),
|
|
44
|
+
textAlignVertical: "center"
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/* eslint-enable melta/no-raw-spacing */
|
|
48
|
+
|
|
49
|
+
const INPUT_VERTICAL_FIX = resolveInputVerticalFix(Platform.OS);
|
|
23
50
|
export function TextField({
|
|
24
51
|
label,
|
|
25
52
|
value,
|
|
@@ -68,7 +95,7 @@ export function TextField({
|
|
|
68
95
|
if (!disabled) setFocused(true);
|
|
69
96
|
},
|
|
70
97
|
onBlur: () => setFocused(false),
|
|
71
|
-
style: [resolved.input, focused && !disabled ? focusStyle : null]
|
|
98
|
+
style: [resolved.input, INPUT_VERTICAL_FIX, focused && !disabled ? focusStyle : null]
|
|
72
99
|
}), helperText != null && !showError && /*#__PURE__*/_jsx(RNText, {
|
|
73
100
|
style: resolved.helperText,
|
|
74
101
|
children: helperText
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["useMemo","useState","TextInput","View","Text","RNText","useTheme","CONTRACTS","resolveTextFieldStyle","resolveTextFieldFocusStyle","jsx","_jsx","jsxs","_jsxs","TextField","label","value","onChangeText","placeholder","variant","disabled","size","helperText","errorText","style","testID","theme","mode","colors","focused","setFocused","effectiveVariant","resolved","focusStyle","showError","children","placeholderTextColor","editable","accessibilityLabel","accessibilityState","onFocus","onBlur","input","__contract","textfield"],"sourceRoot":"../../../src","sources":["components/TextField.tsx"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,SAASA,OAAO,EAAEC,QAAQ,QAAQ,OAAO;AACzC,SACEC,SAAS,EACTC,IAAI,EACJC,IAAI,IAAIC,MAAM,
|
|
1
|
+
{"version":3,"names":["useMemo","useState","Platform","TextInput","View","Text","RNText","useTheme","CONTRACTS","resolveTextFieldStyle","resolveTextFieldFocusStyle","jsx","_jsx","jsxs","_jsxs","resolveInputVerticalFix","os","paddingVertical","paddingBottom","textAlignVertical","INPUT_VERTICAL_FIX","OS","TextField","label","value","onChangeText","placeholder","variant","disabled","size","helperText","errorText","style","testID","theme","mode","colors","focused","setFocused","effectiveVariant","resolved","focusStyle","showError","children","placeholderTextColor","editable","accessibilityLabel","accessibilityState","onFocus","onBlur","input","__contract","textfield"],"sourceRoot":"../../../src","sources":["components/TextField.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
* 本体エントリは依存ゼロ方針(P1)のため、default は RN core の SafeAreaView
|
|
7
7
|
* (deprecated / iOS のみの最小対応)。react-native-safe-area-context を使う利用者は
|
|
8
8
|
* subpath "melta-app/safe-area" の enableSafeAreaContext() をアプリ起動時に一度呼ぶと
|
|
9
|
-
* Screen の SafeArea
|
|
10
|
-
*
|
|
9
|
+
* Screen の SafeArea が context hook + View adapter に差し替わる(optional peer の前例 =
|
|
10
|
+
* Icon × react-native-svg。ただし Screen は本体エントリ所属のため subpath 分離ではなく
|
|
11
|
+
* registry で切り替える)。
|
|
11
12
|
*
|
|
12
13
|
* ⚠️ RN 0.85 は react-native の SafeAreaView プロパティへの getter アクセス時点で
|
|
13
14
|
* deprecation 警告を出す。adapter 登録済みの利用者に警告を出さないため、core への
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["ReactNative","registered","setSafeAreaView","component","resolveSafeAreaView","SafeAreaView"],"sourceRoot":"../../../src","sources":["components/safe-area-registry.ts"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAIA,OAAO,KAAKA,WAAW,MAAM,cAAc;;AAE3C;;AASA,IAAIC,UAAmC,GAAG,IAAI;;AAE9C;AACA,OAAO,SAASC,eAAeA,CAACC,SAA2B,EAAQ;EACjEF,UAAU,GAAGE,SAAS;AACxB;;AAEA;AACA,OAAO,SAASC,mBAAmBA,CAAA,EAAqB;EACtD,OAAOH,UAAU,IAAKD,WAAW,CAACK,YAAiC;AACrE","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":["ReactNative","registered","setSafeAreaView","component","resolveSafeAreaView","SafeAreaView"],"sourceRoot":"../../../src","sources":["components/safe-area-registry.ts"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAIA,OAAO,KAAKA,WAAW,MAAM,cAAc;;AAE3C;;AASA,IAAIC,UAAmC,GAAG,IAAI;;AAE9C;AACA,OAAO,SAASC,eAAeA,CAACC,SAA2B,EAAQ;EACjEF,UAAU,GAAGE,SAAS;AACxB;;AAEA;AACA,OAAO,SAASC,mBAAmBA,CAAA,EAAqB;EACtD,OAAOH,UAAU,IAAKD,WAAW,CAACK,YAAiC;AACrE","ignoreList":[]}
|
|
@@ -13,15 +13,65 @@
|
|
|
13
13
|
* import { enableSafeAreaContext } from "melta-app/safe-area";
|
|
14
14
|
* enableSafeAreaContext();
|
|
15
15
|
*
|
|
16
|
-
* これで Screen の SafeArea が
|
|
17
|
-
*
|
|
16
|
+
* これで Screen の SafeArea が useSafeAreaInsets() + View の adapter に切り替わる。
|
|
17
|
+
* Provider の context 値を render 中に同期参照して padding を付けるため、native
|
|
18
|
+
* SafeAreaView の layout/state 更新を待たず、初回 render から inset が反映される。
|
|
19
|
+
*
|
|
20
|
+
* ⚠️ 前提: アプリの root に SafeAreaProvider が必要。初回 render から正しい inset を
|
|
21
|
+
* 使うには Provider に initialMetrics も渡す。React Navigation / Expo Router 利用時は
|
|
22
|
+
* Provider が設置済みのことが多い。
|
|
18
23
|
*/
|
|
19
24
|
|
|
20
|
-
import {
|
|
25
|
+
import { createElement, useMemo } from "react";
|
|
26
|
+
import { StyleSheet, View } from "react-native";
|
|
27
|
+
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
21
28
|
import { setSafeAreaView } from "../components/safe-area-registry.js";
|
|
29
|
+
const ALL_EDGES = ["top", "right", "bottom", "left"];
|
|
30
|
+
|
|
31
|
+
/** enableSafeAreaContext() の設定。edges 省略時は従来相当の全 edge。 */
|
|
32
|
+
|
|
33
|
+
function additivePadding(style, insets, edges) {
|
|
34
|
+
const flat = StyleSheet.flatten(style) ?? {};
|
|
35
|
+
const base = edge => {
|
|
36
|
+
const edgeValue = flat[`padding${edge[0].toUpperCase()}${edge.slice(1)}`];
|
|
37
|
+
const axisValue = edge === "top" || edge === "bottom" ? flat.paddingVertical : flat.paddingHorizontal;
|
|
38
|
+
const value = edgeValue ?? axisValue ?? flat.padding;
|
|
39
|
+
return typeof value === "number" ? value : 0;
|
|
40
|
+
};
|
|
41
|
+
return {
|
|
42
|
+
...(edges.has("top") ? {
|
|
43
|
+
paddingTop: base("top") + insets.top
|
|
44
|
+
} : null),
|
|
45
|
+
...(edges.has("right") ? {
|
|
46
|
+
paddingRight: base("right") + insets.right
|
|
47
|
+
} : null),
|
|
48
|
+
...(edges.has("bottom") ? {
|
|
49
|
+
paddingBottom: base("bottom") + insets.bottom
|
|
50
|
+
} : null),
|
|
51
|
+
...(edges.has("left") ? {
|
|
52
|
+
paddingLeft: base("left") + insets.left
|
|
53
|
+
} : null)
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function createContextSafeAreaView(selectedEdges) {
|
|
57
|
+
const edges = new Set(selectedEdges);
|
|
58
|
+
function ContextSafeAreaView({
|
|
59
|
+
style,
|
|
60
|
+
...props
|
|
61
|
+
}) {
|
|
62
|
+
const insets = useSafeAreaInsets();
|
|
63
|
+
const paddingStyle = useMemo(() => additivePadding(style, insets, edges), [style, insets.top, insets.right, insets.bottom, insets.left]);
|
|
64
|
+
return /*#__PURE__*/createElement(View, {
|
|
65
|
+
...props,
|
|
66
|
+
style: [style, paddingStyle]
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
ContextSafeAreaView.displayName = "MeltaContextSafeAreaView";
|
|
70
|
+
return ContextSafeAreaView;
|
|
71
|
+
}
|
|
22
72
|
|
|
23
|
-
/** Screen の SafeArea を
|
|
24
|
-
export function enableSafeAreaContext() {
|
|
25
|
-
setSafeAreaView(
|
|
73
|
+
/** Screen の SafeArea を context 同期参照の View adapter に切り替える。 */
|
|
74
|
+
export function enableSafeAreaContext(options = {}) {
|
|
75
|
+
setSafeAreaView(createContextSafeAreaView(options.edges ?? ALL_EDGES));
|
|
26
76
|
}
|
|
27
77
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["
|
|
1
|
+
{"version":3,"names":["createElement","useMemo","StyleSheet","View","useSafeAreaInsets","setSafeAreaView","ALL_EDGES","additivePadding","style","insets","edges","flat","flatten","base","edge","edgeValue","toUpperCase","slice","axisValue","paddingVertical","paddingHorizontal","value","padding","has","paddingTop","top","paddingRight","right","paddingBottom","bottom","paddingLeft","left","createContextSafeAreaView","selectedEdges","Set","ContextSafeAreaView","props","paddingStyle","displayName","enableSafeAreaContext","options"],"sourceRoot":"../../../src","sources":["safe-area/index.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
* SafeArea は registry(safe-area-registry.ts)で解決する。default は RN core の
|
|
12
12
|
* SafeAreaView(deprecated / iOS のみの最小対応)で依存ゼロを維持しつつ、
|
|
13
13
|
* react-native-safe-area-context 利用者は subpath "melta-app/safe-area" の
|
|
14
|
-
* enableSafeAreaContext()
|
|
15
|
-
*
|
|
14
|
+
* enableSafeAreaContext() で context hook + View adapter に差し替えられる
|
|
15
|
+
* (rally-nav dogfood 由来、RN 0.85 Fabric/Android の初回 inset flash 対応)。
|
|
16
16
|
*/
|
|
17
17
|
import { type ReactNode } from "react";
|
|
18
18
|
import { type StyleProp, type ViewStyle } from "react-native";
|
|
@@ -11,8 +11,22 @@
|
|
|
11
11
|
* - 色・寸法の決定は pure resolver(textfield.styles.ts)に分離。
|
|
12
12
|
* recipes/app/textfield.recipe.json との機械照合は scripts/lib/textfield-conformance.test.ts が行う。
|
|
13
13
|
*/
|
|
14
|
-
import { type StyleProp, type ViewStyle } from "react-native";
|
|
14
|
+
import { Platform, type StyleProp, type TextStyle, type ViewStyle } from "react-native";
|
|
15
15
|
import { type TextFieldSize } from "./textfield.styles";
|
|
16
|
+
/**
|
|
17
|
+
* Android の EditText は固定 height と組み合わさると既定の縦 padding で文字が
|
|
18
|
+
* 上寄り・下端切れになるため、縦 padding を殺して縦センターに揃える。
|
|
19
|
+
* textAlignVertical は Android 専用、paddingVertical: 0 は iOS の見た目を変えない
|
|
20
|
+
* (iOS の単一行入力は元々縦センター)。
|
|
21
|
+
* さらに Android では Noto CJK のフォントメトリクス由来で文字インクが
|
|
22
|
+
* 1dp 強下寄りになる(Pixel 実機ピクセル実測 +2.7px/density2.5。
|
|
23
|
+
* includeFontPadding: false は gravity center 下では無効なことも実測済み)ため、
|
|
24
|
+
* paddingBottom の光学ナッジで打ち消す(gravity center は残り高さの中央に置くので
|
|
25
|
+
* paddingBottom p で p/2 上がる)。
|
|
26
|
+
* recipe(styleRefs)の写像ではなく RN 実装の補正なので resolver には置かない。
|
|
27
|
+
*/
|
|
28
|
+
/** OS 毎の補正値を返す純関数(jest は Platform=ios 固定のため、Android 分岐はこれを直接検証する)。 */
|
|
29
|
+
export declare function resolveInputVerticalFix(os: typeof Platform.OS): TextStyle;
|
|
16
30
|
interface TextFieldProps {
|
|
17
31
|
/** ラベル(必須。placeholder だけの入力欄を作らせない = FORM_NO_LABEL_OMIT)。 */
|
|
18
32
|
label: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TextField.d.ts","sourceRoot":"","sources":["../../../../src/components/TextField.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,
|
|
1
|
+
{"version":3,"file":"TextField.d.ts","sourceRoot":"","sources":["../../../../src/components/TextField.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EACL,QAAQ,EAIR,KAAK,SAAS,EACd,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAGtB,OAAO,EAGL,KAAK,aAAa,EACnB,MAAM,oBAAoB,CAAC;AAE5B;;;;;;;;;;;GAWG;AAEH,sEAAsE;AACtE,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,OAAO,QAAQ,CAAC,EAAE,GAAG,SAAS,CAMzE;AAKD,UAAU,cAAc;IACtB,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+DAA+D;IAC/D,OAAO,CAAC,EAAE,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;IAC1C,uFAAuF;IACvF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,SAAS,CAAC,EACxB,KAAK,EACL,KAAK,EACL,YAAY,EACZ,WAAW,EACX,OAAmB,EACnB,QAAgB,EAChB,IAAe,EACf,UAAU,EACV,SAAS,EACT,KAAK,EACL,MAAM,GACP,EAAE,cAAc,2CAqChB;yBAjDe,SAAS"}
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
* 本体エントリは依存ゼロ方針(P1)のため、default は RN core の SafeAreaView
|
|
5
5
|
* (deprecated / iOS のみの最小対応)。react-native-safe-area-context を使う利用者は
|
|
6
6
|
* subpath "melta-app/safe-area" の enableSafeAreaContext() をアプリ起動時に一度呼ぶと
|
|
7
|
-
* Screen の SafeArea
|
|
8
|
-
*
|
|
7
|
+
* Screen の SafeArea が context hook + View adapter に差し替わる(optional peer の前例 =
|
|
8
|
+
* Icon × react-native-svg。ただし Screen は本体エントリ所属のため subpath 分離ではなく
|
|
9
|
+
* registry で切り替える)。
|
|
9
10
|
*
|
|
10
11
|
* ⚠️ RN 0.85 は react-native の SafeAreaView プロパティへの getter アクセス時点で
|
|
11
12
|
* deprecation 警告を出す。adapter 登録済みの利用者に警告を出さないため、core への
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"safe-area-registry.d.ts","sourceRoot":"","sources":["../../../../src/components/safe-area-registry.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"safe-area-registry.d.ts","sourceRoot":"","sources":["../../../../src/components/safe-area-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGzD,uEAAuE;AACvE,MAAM,WAAW,qBAAqB;IACpC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED,MAAM,MAAM,gBAAgB,GAAG,aAAa,CAAC,qBAAqB,CAAC,CAAC;AAIpE,kDAAkD;AAClD,wBAAgB,eAAe,CAAC,SAAS,EAAE,gBAAgB,GAAG,IAAI,CAEjE;AAED,4DAA4D;AAC5D,wBAAgB,mBAAmB,IAAI,gBAAgB,CAEtD"}
|
|
@@ -11,9 +11,20 @@
|
|
|
11
11
|
* import { enableSafeAreaContext } from "melta-app/safe-area";
|
|
12
12
|
* enableSafeAreaContext();
|
|
13
13
|
*
|
|
14
|
-
* これで Screen の SafeArea が
|
|
15
|
-
*
|
|
14
|
+
* これで Screen の SafeArea が useSafeAreaInsets() + View の adapter に切り替わる。
|
|
15
|
+
* Provider の context 値を render 中に同期参照して padding を付けるため、native
|
|
16
|
+
* SafeAreaView の layout/state 更新を待たず、初回 render から inset が反映される。
|
|
17
|
+
*
|
|
18
|
+
* ⚠️ 前提: アプリの root に SafeAreaProvider が必要。初回 render から正しい inset を
|
|
19
|
+
* 使うには Provider に initialMetrics も渡す。React Navigation / Expo Router 利用時は
|
|
20
|
+
* Provider が設置済みのことが多い。
|
|
16
21
|
*/
|
|
17
|
-
|
|
18
|
-
|
|
22
|
+
import { type Edge } from "react-native-safe-area-context";
|
|
23
|
+
/** enableSafeAreaContext() の設定。edges 省略時は従来相当の全 edge。 */
|
|
24
|
+
export interface EnableSafeAreaContextOptions {
|
|
25
|
+
/** Screen に適用する edge。tab bar 等が担当する edge は除外する。 */
|
|
26
|
+
edges?: readonly Edge[];
|
|
27
|
+
}
|
|
28
|
+
/** Screen の SafeArea を context 同期参照の View adapter に切り替える。 */
|
|
29
|
+
export declare function enableSafeAreaContext(options?: EnableSafeAreaContextOptions): void;
|
|
19
30
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/safe-area/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/safe-area/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,OAAO,EAEL,KAAK,IAAI,EAEV,MAAM,gCAAgC,CAAC;AASxC,yDAAyD;AACzD,MAAM,WAAW,4BAA4B;IAC3C,mDAAmD;IACnD,KAAK,CAAC,EAAE,SAAS,IAAI,EAAE,CAAC;CACzB;AA0CD,6DAA6D;AAC7D,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,4BAAiC,GAAG,IAAI,CAEtF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "melta-app",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
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",
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
* SafeArea は registry(safe-area-registry.ts)で解決する。default は RN core の
|
|
12
12
|
* SafeAreaView(deprecated / iOS のみの最小対応)で依存ゼロを維持しつつ、
|
|
13
13
|
* react-native-safe-area-context 利用者は subpath "melta-app/safe-area" の
|
|
14
|
-
* enableSafeAreaContext()
|
|
15
|
-
*
|
|
14
|
+
* enableSafeAreaContext() で context hook + View adapter に差し替えられる
|
|
15
|
+
* (rally-nav dogfood 由来、RN 0.85 Fabric/Android の初回 inset flash 対応)。
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import { useMemo, type ReactNode } from "react";
|
|
@@ -14,10 +14,12 @@
|
|
|
14
14
|
|
|
15
15
|
import { useMemo, useState } from "react";
|
|
16
16
|
import {
|
|
17
|
+
Platform,
|
|
17
18
|
TextInput,
|
|
18
19
|
View,
|
|
19
20
|
Text as RNText,
|
|
20
21
|
type StyleProp,
|
|
22
|
+
type TextStyle,
|
|
21
23
|
type ViewStyle,
|
|
22
24
|
} from "react-native";
|
|
23
25
|
import { useTheme } from "../theme";
|
|
@@ -28,6 +30,31 @@ import {
|
|
|
28
30
|
type TextFieldSize,
|
|
29
31
|
} from "./textfield.styles";
|
|
30
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Android の EditText は固定 height と組み合わさると既定の縦 padding で文字が
|
|
35
|
+
* 上寄り・下端切れになるため、縦 padding を殺して縦センターに揃える。
|
|
36
|
+
* textAlignVertical は Android 専用、paddingVertical: 0 は iOS の見た目を変えない
|
|
37
|
+
* (iOS の単一行入力は元々縦センター)。
|
|
38
|
+
* さらに Android では Noto CJK のフォントメトリクス由来で文字インクが
|
|
39
|
+
* 1dp 強下寄りになる(Pixel 実機ピクセル実測 +2.7px/density2.5。
|
|
40
|
+
* includeFontPadding: false は gravity center 下では無効なことも実測済み)ため、
|
|
41
|
+
* paddingBottom の光学ナッジで打ち消す(gravity center は残り高さの中央に置くので
|
|
42
|
+
* paddingBottom p で p/2 上がる)。
|
|
43
|
+
* recipe(styleRefs)の写像ではなく RN 実装の補正なので resolver には置かない。
|
|
44
|
+
*/
|
|
45
|
+
/* eslint-disable melta/no-raw-spacing -- spacing の選択ではなく EditText 既定 padding の打ち消しと CJK 光学ナッジ(実測由来の固定値) */
|
|
46
|
+
/** OS 毎の補正値を返す純関数(jest は Platform=ios 固定のため、Android 分岐はこれを直接検証する)。 */
|
|
47
|
+
export function resolveInputVerticalFix(os: typeof Platform.OS): TextStyle {
|
|
48
|
+
return {
|
|
49
|
+
paddingVertical: 0,
|
|
50
|
+
...(os === "android" ? { paddingBottom: 2 } : null),
|
|
51
|
+
textAlignVertical: "center",
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/* eslint-enable melta/no-raw-spacing */
|
|
55
|
+
|
|
56
|
+
const INPUT_VERTICAL_FIX = resolveInputVerticalFix(Platform.OS);
|
|
57
|
+
|
|
31
58
|
interface TextFieldProps {
|
|
32
59
|
/** ラベル(必須。placeholder だけの入力欄を作らせない = FORM_NO_LABEL_OMIT)。 */
|
|
33
60
|
label: string;
|
|
@@ -88,7 +115,7 @@ export function TextField({
|
|
|
88
115
|
if (!disabled) setFocused(true);
|
|
89
116
|
}}
|
|
90
117
|
onBlur={() => setFocused(false)}
|
|
91
|
-
style={[resolved.input, focused && !disabled ? focusStyle : null]}
|
|
118
|
+
style={[resolved.input, INPUT_VERTICAL_FIX, focused && !disabled ? focusStyle : null]}
|
|
92
119
|
/>
|
|
93
120
|
{helperText != null && !showError && (
|
|
94
121
|
<RNText style={resolved.helperText}>{helperText}</RNText>
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
* 本体エントリは依存ゼロ方針(P1)のため、default は RN core の SafeAreaView
|
|
5
5
|
* (deprecated / iOS のみの最小対応)。react-native-safe-area-context を使う利用者は
|
|
6
6
|
* subpath "melta-app/safe-area" の enableSafeAreaContext() をアプリ起動時に一度呼ぶと
|
|
7
|
-
* Screen の SafeArea
|
|
8
|
-
*
|
|
7
|
+
* Screen の SafeArea が context hook + View adapter に差し替わる(optional peer の前例 =
|
|
8
|
+
* Icon × react-native-svg。ただし Screen は本体エントリ所属のため subpath 分離ではなく
|
|
9
|
+
* registry で切り替える)。
|
|
9
10
|
*
|
|
10
11
|
* ⚠️ RN 0.85 は react-native の SafeAreaView プロパティへの getter アクセス時点で
|
|
11
12
|
* deprecation 警告を出す。adapter 登録済みの利用者に警告を出さないため、core への
|
package/src/safe-area/index.ts
CHANGED
|
@@ -11,18 +11,77 @@
|
|
|
11
11
|
* import { enableSafeAreaContext } from "melta-app/safe-area";
|
|
12
12
|
* enableSafeAreaContext();
|
|
13
13
|
*
|
|
14
|
-
* これで Screen の SafeArea が
|
|
15
|
-
*
|
|
14
|
+
* これで Screen の SafeArea が useSafeAreaInsets() + View の adapter に切り替わる。
|
|
15
|
+
* Provider の context 値を render 中に同期参照して padding を付けるため、native
|
|
16
|
+
* SafeAreaView の layout/state 更新を待たず、初回 render から inset が反映される。
|
|
16
17
|
*
|
|
17
|
-
* ⚠️ 前提: アプリの root に SafeAreaProvider
|
|
18
|
-
*
|
|
19
|
-
*
|
|
18
|
+
* ⚠️ 前提: アプリの root に SafeAreaProvider が必要。初回 render から正しい inset を
|
|
19
|
+
* 使うには Provider に initialMetrics も渡す。React Navigation / Expo Router 利用時は
|
|
20
|
+
* Provider が設置済みのことが多い。
|
|
20
21
|
*/
|
|
21
22
|
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
23
|
+
import { createElement, useMemo } from "react";
|
|
24
|
+
import { StyleSheet, View, type ViewStyle } from "react-native";
|
|
25
|
+
import {
|
|
26
|
+
useSafeAreaInsets,
|
|
27
|
+
type Edge,
|
|
28
|
+
type EdgeInsets,
|
|
29
|
+
} from "react-native-safe-area-context";
|
|
30
|
+
import {
|
|
31
|
+
setSafeAreaView,
|
|
32
|
+
type SafeAreaViewLike,
|
|
33
|
+
type SafeAreaViewLikeProps,
|
|
34
|
+
} from "../components/safe-area-registry";
|
|
24
35
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
36
|
+
const ALL_EDGES = ["top", "right", "bottom", "left"] as const satisfies readonly Edge[];
|
|
37
|
+
|
|
38
|
+
/** enableSafeAreaContext() の設定。edges 省略時は従来相当の全 edge。 */
|
|
39
|
+
export interface EnableSafeAreaContextOptions {
|
|
40
|
+
/** Screen に適用する edge。tab bar 等が担当する edge は除外する。 */
|
|
41
|
+
edges?: readonly Edge[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function additivePadding(
|
|
45
|
+
style: SafeAreaViewLikeProps["style"],
|
|
46
|
+
insets: EdgeInsets,
|
|
47
|
+
edges: ReadonlySet<Edge>,
|
|
48
|
+
): ViewStyle {
|
|
49
|
+
const flat = StyleSheet.flatten(style) ?? {};
|
|
50
|
+
|
|
51
|
+
const base = (edge: Edge): number => {
|
|
52
|
+
const edgeValue = flat[`padding${edge[0].toUpperCase()}${edge.slice(1)}` as keyof ViewStyle];
|
|
53
|
+
const axisValue =
|
|
54
|
+
edge === "top" || edge === "bottom" ? flat.paddingVertical : flat.paddingHorizontal;
|
|
55
|
+
const value = edgeValue ?? axisValue ?? flat.padding;
|
|
56
|
+
return typeof value === "number" ? value : 0;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
...(edges.has("top") ? { paddingTop: base("top") + insets.top } : null),
|
|
61
|
+
...(edges.has("right") ? { paddingRight: base("right") + insets.right } : null),
|
|
62
|
+
...(edges.has("bottom") ? { paddingBottom: base("bottom") + insets.bottom } : null),
|
|
63
|
+
...(edges.has("left") ? { paddingLeft: base("left") + insets.left } : null),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function createContextSafeAreaView(selectedEdges: readonly Edge[]): SafeAreaViewLike {
|
|
68
|
+
const edges = new Set(selectedEdges);
|
|
69
|
+
|
|
70
|
+
function ContextSafeAreaView({ style, ...props }: SafeAreaViewLikeProps) {
|
|
71
|
+
const insets = useSafeAreaInsets();
|
|
72
|
+
const paddingStyle = useMemo(
|
|
73
|
+
() => additivePadding(style, insets, edges),
|
|
74
|
+
[style, insets.top, insets.right, insets.bottom, insets.left],
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
return createElement(View, { ...props, style: [style, paddingStyle] });
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
ContextSafeAreaView.displayName = "MeltaContextSafeAreaView";
|
|
81
|
+
return ContextSafeAreaView;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Screen の SafeArea を context 同期参照の View adapter に切り替える。 */
|
|
85
|
+
export function enableSafeAreaContext(options: EnableSafeAreaContextOptions = {}): void {
|
|
86
|
+
setSafeAreaView(createContextSafeAreaView(options.edges ?? ALL_EDGES));
|
|
28
87
|
}
|