melta-app 0.8.1 → 0.9.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 +15 -0
- package/lib/module/components/Header.js +28 -16
- package/lib/module/components/Header.js.map +1 -1
- package/lib/module/components/header.styles.js +26 -18
- package/lib/module/components/header.styles.js.map +1 -1
- package/lib/module/contracts/contract-types.js +2 -2
- package/lib/module/contracts/contract-types.js.map +1 -1
- package/lib/typescript/src/components/Header.d.ts +10 -14
- package/lib/typescript/src/components/Header.d.ts.map +1 -1
- package/lib/typescript/src/components/header.styles.d.ts +22 -13
- package/lib/typescript/src/components/header.styles.d.ts.map +1 -1
- package/lib/typescript/src/contracts/contract-types.d.ts +2 -2
- package/llms.txt +1 -1
- package/package.json +4 -3
- package/src/components/Header.tsx +25 -24
- package/src/components/header.styles.ts +29 -28
- package/src/contracts/contract-types.ts +2 -2
package/README.md
CHANGED
|
@@ -668,3 +668,18 @@ telemetry / 使用状況の収集は無い。runtime 依存ゼロ(peerDependen
|
|
|
668
668
|
## License
|
|
669
669
|
|
|
670
670
|
MIT([LICENSE](./LICENSE))。同梱するアイコングリフの帰属表示は [THIRD_PARTY_LICENSES.md](./THIRD_PARTY_LICENSES.md)。
|
|
671
|
+
|
|
672
|
+
|
|
673
|
+
### 操作バーとしてのHeader
|
|
674
|
+
|
|
675
|
+
`<Header variant="actions" title="作業ログを残す" leading={closeButton} trailing={actions} />` は、
|
|
676
|
+
タイトルを画面では隠し、読み上げ可能な見出しとして残します。`title`には空でない画面名を渡します。
|
|
677
|
+
左右の操作は既存のslotで渡し、操作間のgapと各ボタンの有効領域はslot内で確保してください。
|
|
678
|
+
Headerの上下余白と下線は標準recipeが持つため、アプリ側で複製しません。
|
|
679
|
+
中央spacer前後のgapはactionsではspacing.1、defaultでは従来のspacing.3です。
|
|
680
|
+
variant省略時は従来どおり可視タイトルを表示します。
|
|
681
|
+
|
|
682
|
+
カタログをWeb exportしてローカル配信した後、`npm run check:header-layout -- --base <URL>` で
|
|
683
|
+
320/393px幅・大きな子要素の余白と44pt標的を測定できます。Playwrightは検証環境に用意し、
|
|
684
|
+
別のインストールを使う場合は `--playwright-module <playwrightのディレクトリ>` を渡します。
|
|
685
|
+
この実描画検査は手動レーンで、OSの文字拡大・読み上げ確認は別途必要です。
|
|
@@ -1,25 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Header —
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* - title は melta の Text(xl / bold / text-heading / heading role)で描画し、
|
|
9
|
-
* heading として読み上げられる(contract a11y required)。
|
|
10
|
-
* - titleWrapStyle の flex:1 で title が余白を占有し trailing を右端へ押す。
|
|
11
|
-
* - 決定ロジックは pure resolver(header.styles.ts)に分離 — recipe との機械照合対象。
|
|
4
|
+
* Header — 画面名と左右の操作を持つ共通ヘッダー。
|
|
5
|
+
* actionsでは画面名を視覚的に隠すが、独立したa11y見出しとして残す。
|
|
6
|
+
* 余白・下線・見出しの配置はheader recipeとpure resolverが所有する。
|
|
12
7
|
*/
|
|
13
|
-
|
|
14
8
|
import { useMemo } from "react";
|
|
15
9
|
import { View } from "react-native";
|
|
16
10
|
import { useTheme } from "../theme/index.js";
|
|
17
11
|
import { CONTRACTS } from "../contracts/contract-types.js";
|
|
18
12
|
import { Text } from "../primitives/Text.js";
|
|
19
|
-
import { resolveHeaderStyle } from "./header.styles.js";
|
|
13
|
+
import { HEADER_SPEC, resolveHeaderStyle } from "./header.styles.js";
|
|
20
14
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
21
15
|
export function Header({
|
|
22
16
|
title,
|
|
17
|
+
variant = "default",
|
|
23
18
|
leading,
|
|
24
19
|
trailing,
|
|
25
20
|
style,
|
|
@@ -31,17 +26,34 @@ export function Header({
|
|
|
31
26
|
} = useTheme();
|
|
32
27
|
const {
|
|
33
28
|
containerStyle,
|
|
34
|
-
titleWrapStyle
|
|
35
|
-
|
|
29
|
+
titleWrapStyle,
|
|
30
|
+
hiddenTitleStyle
|
|
31
|
+
} = useMemo(() => resolveHeaderStyle(theme, mode, {
|
|
32
|
+
variant
|
|
33
|
+
}), [theme, mode, variant]);
|
|
36
34
|
return /*#__PURE__*/_jsxs(View, {
|
|
37
35
|
style: [containerStyle, style],
|
|
38
36
|
testID: testID,
|
|
39
37
|
children: [leading, /*#__PURE__*/_jsx(View, {
|
|
40
38
|
style: titleWrapStyle,
|
|
41
|
-
children:
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
children: variant === "actions" ?
|
|
40
|
+
/*#__PURE__*/
|
|
41
|
+
// 見出しTextの契約を再利用し、左右の操作を親の読み上げへ畳まない。
|
|
42
|
+
_jsx(View, {
|
|
43
|
+
pointerEvents: "none",
|
|
44
|
+
style: hiddenTitleStyle,
|
|
45
|
+
children: /*#__PURE__*/_jsx(Text, {
|
|
46
|
+
variant: HEADER_SPEC.titleFont,
|
|
47
|
+
weight: HEADER_SPEC.titleWeight,
|
|
48
|
+
color: HEADER_SPEC.titleColor,
|
|
49
|
+
role: "heading",
|
|
50
|
+
numberOfLines: 1,
|
|
51
|
+
children: title
|
|
52
|
+
})
|
|
53
|
+
}) : /*#__PURE__*/_jsx(Text, {
|
|
54
|
+
variant: HEADER_SPEC.titleFont,
|
|
55
|
+
weight: HEADER_SPEC.titleWeight,
|
|
56
|
+
color: HEADER_SPEC.titleColor,
|
|
45
57
|
role: "heading",
|
|
46
58
|
numberOfLines: 1,
|
|
47
59
|
children: title
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["useMemo","View","useTheme","CONTRACTS","Text","resolveHeaderStyle","jsx","_jsx","jsxs","_jsxs","Header","title","leading","trailing","style","testID","theme","mode","containerStyle","titleWrapStyle","children","
|
|
1
|
+
{"version":3,"names":["useMemo","View","useTheme","CONTRACTS","Text","HEADER_SPEC","resolveHeaderStyle","jsx","_jsx","jsxs","_jsxs","Header","title","variant","leading","trailing","style","testID","theme","mode","containerStyle","titleWrapStyle","hiddenTitleStyle","children","pointerEvents","titleFont","weight","titleWeight","color","titleColor","role","numberOfLines","__contract","header"],"sourceRoot":"../../../src","sources":["components/Header.tsx"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA,SAASA,OAAO,QAAwB,OAAO;AAC/C,SAASC,IAAI,QAAwC,cAAc;AACnE,SAASC,QAAQ,QAAQ,mBAAmB;AAC5C,SAASC,SAAS,QAAwB,gCAAgC;AAC1E,SAASC,IAAI,QAAQ,uBAAuB;AAC5C,SAASC,WAAW,EAAEC,kBAAkB,QAAQ,oBAAoB;AAAC,SAAAC,GAAA,IAAAC,IAAA,EAAAC,IAAA,IAAAC,KAAA;AAarE,OAAO,SAASC,MAAMA,CAAC;EAAEC,KAAK;EAAEC,OAAO,GAAG,SAAS;EAAEC,OAAO;EAAEC,QAAQ;EAAEC,KAAK;EAAEC;AAAoB,CAAC,EAAE;EACpG,MAAM;IAAEC,KAAK;IAAEC;EAAK,CAAC,GAAGjB,QAAQ,CAAC,CAAC;EAClC,MAAM;IAAEkB,cAAc;IAAEC,cAAc;IAAEC;EAAiB,CAAC,GAAGtB,OAAO,CAClE,MAAMM,kBAAkB,CAACY,KAAK,EAAEC,IAAI,EAAE;IAAEN;EAAQ,CAAC,CAAC,EAAE,CAACK,KAAK,EAAEC,IAAI,EAAEN,OAAO,CAC3E,CAAC;EACD,oBACEH,KAAA,CAACT,IAAI;IAACe,KAAK,EAAE,CAACI,cAAc,EAAEJ,KAAK,CAAE;IAACC,MAAM,EAAEA,MAAO;IAAAM,QAAA,GAClDT,OAAO,eACRN,IAAA,CAACP,IAAI;MAACe,KAAK,EAAEK,cAAe;MAAAE,QAAA,EACzBV,OAAO,KAAK,SAAS;MAAA;MACpB;MACAL,IAAA,CAACP,IAAI;QAACuB,aAAa,EAAC,MAAM;QAACR,KAAK,EAAEM,gBAAiB;QAAAC,QAAA,eACjDf,IAAA,CAACJ,IAAI;UAACS,OAAO,EAAER,WAAW,CAACoB,SAAU;UAACC,MAAM,EAAErB,WAAW,CAACsB,WAAY;UACpEC,KAAK,EAAEvB,WAAW,CAACwB,UAAW;UAACC,IAAI,EAAC,SAAS;UAACC,aAAa,EAAE,CAAE;UAAAR,QAAA,EAC9DX;QAAK,CACF;MAAC,CACH,CAAC,gBAEPJ,IAAA,CAACJ,IAAI;QAACS,OAAO,EAAER,WAAW,CAACoB,SAAU;QAACC,MAAM,EAAErB,WAAW,CAACsB,WAAY;QACpEC,KAAK,EAAEvB,WAAW,CAACwB,UAAW;QAACC,IAAI,EAAC,SAAS;QAACC,aAAa,EAAE,CAAE;QAAAR,QAAA,EAC9DX;MAAK,CACF;IACP,CACG,CAAC,EACNG,QAAQ;EAAA,CACL,CAAC;AAEX;AACAJ,MAAM,CAACqB,UAAU,GAAG7B,SAAS,CAAC8B,MAAM","ignoreList":[]}
|
|
@@ -1,33 +1,41 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* header.styles — Header の pure style resolver(styleRefs conformance 対応で Header.tsx から分離)。
|
|
5
|
-
*
|
|
6
|
-
* react-native を import しない純粋モジュールにすることで node テスト(tsx --test)から
|
|
7
|
-
* 直接実行できる。recipes/app/header.recipe.json(melta-contracts の styleRefs)との
|
|
8
|
-
* 機械照合は scripts/lib/header-conformance.test.ts が行う。
|
|
9
|
-
*/
|
|
3
|
+
/** Headerの見た目をrecipeと照合できるpure resolverへ集約する。 */
|
|
10
4
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
5
|
+
export const HEADER_SPEC = {
|
|
6
|
+
gap: "3",
|
|
7
|
+
actionsGap: "1",
|
|
8
|
+
paddingX: "4",
|
|
9
|
+
paddingY: "3",
|
|
10
|
+
titleFont: "xl",
|
|
11
|
+
titleWeight: "bold",
|
|
12
|
+
titleColor: "text-heading",
|
|
13
|
+
borderWidth: 1,
|
|
14
|
+
hiddenTitleSize: 1
|
|
15
|
+
};
|
|
16
|
+
export function resolveHeaderStyle(theme, mode, props = {}) {
|
|
17
17
|
return {
|
|
18
18
|
containerStyle: {
|
|
19
19
|
flexDirection: "row",
|
|
20
20
|
alignItems: "center",
|
|
21
|
-
gap: theme.spacing["
|
|
22
|
-
paddingHorizontal: theme.spacing[
|
|
23
|
-
paddingVertical: theme.spacing[
|
|
21
|
+
gap: theme.spacing[props.variant === "actions" ? HEADER_SPEC.actionsGap : HEADER_SPEC.gap],
|
|
22
|
+
paddingHorizontal: theme.spacing[HEADER_SPEC.paddingX],
|
|
23
|
+
paddingVertical: theme.spacing[HEADER_SPEC.paddingY],
|
|
24
24
|
backgroundColor: theme.color.semantic[mode]["bg-page"],
|
|
25
|
-
borderBottomWidth:
|
|
25
|
+
borderBottomWidth: HEADER_SPEC.borderWidth,
|
|
26
26
|
borderBottomColor: theme.color.semantic[mode]["border-default"]
|
|
27
27
|
},
|
|
28
28
|
titleWrapStyle: {
|
|
29
29
|
flex: 1
|
|
30
|
-
}
|
|
30
|
+
},
|
|
31
|
+
...(props.variant === "actions" ? {
|
|
32
|
+
hiddenTitleStyle: {
|
|
33
|
+
position: "absolute",
|
|
34
|
+
width: HEADER_SPEC.hiddenTitleSize,
|
|
35
|
+
height: HEADER_SPEC.hiddenTitleSize,
|
|
36
|
+
overflow: "hidden"
|
|
37
|
+
}
|
|
38
|
+
} : {})
|
|
31
39
|
};
|
|
32
40
|
}
|
|
33
41
|
//# sourceMappingURL=header.styles.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["resolveHeaderStyle","theme","mode","containerStyle","flexDirection","alignItems","
|
|
1
|
+
{"version":3,"names":["HEADER_SPEC","gap","actionsGap","paddingX","paddingY","titleFont","titleWeight","titleColor","borderWidth","hiddenTitleSize","resolveHeaderStyle","theme","mode","props","containerStyle","flexDirection","alignItems","spacing","variant","paddingHorizontal","paddingVertical","backgroundColor","color","semantic","borderBottomWidth","borderBottomColor","titleWrapStyle","flex","hiddenTitleStyle","position","width","height","overflow"],"sourceRoot":"../../../src","sources":["components/header.styles.ts"],"mappings":";;AAAA;;AAIA,OAAO,MAAMA,WAAW,GAAG;EACzBC,GAAG,EAAE,GAAG;EAAEC,UAAU,EAAE,GAAG;EAAEC,QAAQ,EAAE,GAAG;EAAEC,QAAQ,EAAE,GAAG;EACvDC,SAAS,EAAE,IAAI;EAAEC,WAAW,EAAE,MAAM;EAAEC,UAAU,EAAE,cAAc;EAChEC,WAAW,EAAE,CAAC;EAAEC,eAAe,EAAE;AACnC,CAIC;AAYD,OAAO,SAASC,kBAAkBA,CAChCC,KAAkB,EAAEC,IAAe,EAAEC,KAAwC,GAAG,CAAC,CAAC,EACrE;EACb,OAAO;IACLC,cAAc,EAAE;MACdC,aAAa,EAAE,KAAK;MAAEC,UAAU,EAAE,QAAQ;MAAEf,GAAG,EAAEU,KAAK,CAACM,OAAO,CAACJ,KAAK,CAACK,OAAO,KAAK,SAAS,GAAGlB,WAAW,CAACE,UAAU,GAAGF,WAAW,CAACC,GAAG,CAAC;MACtIkB,iBAAiB,EAAER,KAAK,CAACM,OAAO,CAACjB,WAAW,CAACG,QAAQ,CAAC;MACtDiB,eAAe,EAAET,KAAK,CAACM,OAAO,CAACjB,WAAW,CAACI,QAAQ,CAAC;MACpDiB,eAAe,EAAEV,KAAK,CAACW,KAAK,CAACC,QAAQ,CAACX,IAAI,CAAC,CAAC,SAAS,CAAC;MACtDY,iBAAiB,EAAExB,WAAW,CAACQ,WAAW;MAC1CiB,iBAAiB,EAAEd,KAAK,CAACW,KAAK,CAACC,QAAQ,CAACX,IAAI,CAAC,CAAC,gBAAgB;IAChE,CAAC;IACDc,cAAc,EAAE;MAAEC,IAAI,EAAE;IAAE,CAAC;IAC3B,IAAId,KAAK,CAACK,OAAO,KAAK,SAAS,GAAG;MAChCU,gBAAgB,EAAE;QAChBC,QAAQ,EAAE,UAAmB;QAAEC,KAAK,EAAE9B,WAAW,CAACS,eAAe;QACjEsB,MAAM,EAAE/B,WAAW,CAACS,eAAe;QAAEuB,QAAQ,EAAE;MACjD;IACF,CAAC,GAAG,CAAC,CAAC;EACR,CAAC;AACH","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["CONTRACTS","text","id","version","variants","sizes","states","button","tag","card","image","surface","skeleton","emptyState","metric","stack","row","screen","header","icon","avatar","textfield","toggle","checkbox","radio","alert","toast","progress","modal","actionSheet","bottomSheet"],"sourceRoot":"../../../src","sources":["contracts/contract-types.ts"],"mappings":"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
|
|
1
|
+
{"version":3,"names":["CONTRACTS","text","id","version","variants","sizes","states","button","tag","card","image","surface","skeleton","emptyState","metric","stack","row","screen","header","icon","avatar","textfield","toggle","checkbox","radio","alert","toast","progress","modal","actionSheet","bottomSheet"],"sourceRoot":"../../../src","sources":["contracts/contract-types.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,31 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Header —
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* - title は melta の Text(xl / bold / text-heading / heading role)で描画し、
|
|
7
|
-
* heading として読み上げられる(contract a11y required)。
|
|
8
|
-
* - titleWrapStyle の flex:1 で title が余白を占有し trailing を右端へ押す。
|
|
9
|
-
* - 決定ロジックは pure resolver(header.styles.ts)に分離 — recipe との機械照合対象。
|
|
2
|
+
* Header — 画面名と左右の操作を持つ共通ヘッダー。
|
|
3
|
+
* actionsでは画面名を視覚的に隠すが、独立したa11y見出しとして残す。
|
|
4
|
+
* 余白・下線・見出しの配置はheader recipeとpure resolverが所有する。
|
|
10
5
|
*/
|
|
11
6
|
import { type ReactNode } from "react";
|
|
12
7
|
import { type StyleProp, type ViewStyle } from "react-native";
|
|
8
|
+
import { type VariantOf } from "../contracts/contract-types.js";
|
|
13
9
|
interface HeaderProps {
|
|
14
|
-
/**
|
|
10
|
+
/** 空でない画面名。actionsでも読み上げるため必須。 */
|
|
15
11
|
title: string;
|
|
16
|
-
/**
|
|
12
|
+
/** defaultは可視見出し、actionsは可視タイトルのない操作バー。 */
|
|
13
|
+
variant?: VariantOf<"header">;
|
|
17
14
|
leading?: ReactNode;
|
|
18
|
-
/** 右端の slot(アクション等)。 */
|
|
19
15
|
trailing?: ReactNode;
|
|
20
16
|
style?: StyleProp<ViewStyle>;
|
|
21
17
|
testID?: string;
|
|
22
18
|
}
|
|
23
|
-
export declare function Header({ title, leading, trailing, style, testID }: HeaderProps): import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
export declare function Header({ title, variant, leading, trailing, style, testID }: HeaderProps): import("react/jsx-runtime").JSX.Element;
|
|
24
20
|
export declare namespace Header {
|
|
25
21
|
var __contract: {
|
|
26
22
|
readonly id: "header";
|
|
27
|
-
readonly version: "0.
|
|
28
|
-
readonly variants: readonly ["default"];
|
|
23
|
+
readonly version: "0.2.0";
|
|
24
|
+
readonly variants: readonly ["default", "actions"];
|
|
29
25
|
readonly sizes: readonly [];
|
|
30
26
|
readonly states: readonly ["default"];
|
|
31
27
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Header.d.ts","sourceRoot":"","sources":["../../../../src/components/Header.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"Header.d.ts","sourceRoot":"","sources":["../../../../src/components/Header.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAW,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAChD,OAAO,EAAQ,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAEpE,OAAO,EAAa,KAAK,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAI3E,UAAU,WAAW;IACnB,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,2CAA2C;IAC3C,OAAO,CAAC,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAC;IAC9B,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,MAAM,CAAC,EAAE,KAAK,EAAE,OAAmB,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,WAAW,2CA2BnG;yBA3Be,MAAM"}
|
|
@@ -1,11 +1,17 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* header.styles — Header の pure style resolver(styleRefs conformance 対応で Header.tsx から分離)。
|
|
3
|
-
*
|
|
4
|
-
* react-native を import しない純粋モジュールにすることで node テスト(tsx --test)から
|
|
5
|
-
* 直接実行できる。recipes/app/header.recipe.json(melta-contracts の styleRefs)との
|
|
6
|
-
* 機械照合は scripts/lib/header-conformance.test.ts が行う。
|
|
7
|
-
*/
|
|
1
|
+
/** Headerの見た目をrecipeと照合できるpure resolverへ集約する。 */
|
|
8
2
|
import type { NativeTheme, ThemeMode } from "../theme/index.js";
|
|
3
|
+
import type { VariantOf } from "../contracts/contract-types.js";
|
|
4
|
+
export declare const HEADER_SPEC: {
|
|
5
|
+
readonly gap: "3";
|
|
6
|
+
readonly actionsGap: "1";
|
|
7
|
+
readonly paddingX: "4";
|
|
8
|
+
readonly paddingY: "3";
|
|
9
|
+
readonly titleFont: "xl";
|
|
10
|
+
readonly titleWeight: "bold";
|
|
11
|
+
readonly titleColor: "text-heading";
|
|
12
|
+
readonly borderWidth: 1;
|
|
13
|
+
readonly hiddenTitleSize: 1;
|
|
14
|
+
};
|
|
9
15
|
export interface HeaderStyle {
|
|
10
16
|
containerStyle: {
|
|
11
17
|
flexDirection: "row";
|
|
@@ -20,11 +26,14 @@ export interface HeaderStyle {
|
|
|
20
26
|
titleWrapStyle: {
|
|
21
27
|
flex: 1;
|
|
22
28
|
};
|
|
29
|
+
hiddenTitleStyle?: {
|
|
30
|
+
position: "absolute";
|
|
31
|
+
width: 1;
|
|
32
|
+
height: 1;
|
|
33
|
+
overflow: "hidden";
|
|
34
|
+
};
|
|
23
35
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
* (variant="xl" weight="bold" color="text-heading" role="heading")を compose する(text recipe が正)。
|
|
28
|
-
*/
|
|
29
|
-
export declare function resolveHeaderStyle(theme: NativeTheme, mode: ThemeMode): HeaderStyle;
|
|
36
|
+
export declare function resolveHeaderStyle(theme: NativeTheme, mode: ThemeMode, props?: {
|
|
37
|
+
variant?: VariantOf<"header">;
|
|
38
|
+
}): HeaderStyle;
|
|
30
39
|
//# sourceMappingURL=header.styles.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"header.styles.d.ts","sourceRoot":"","sources":["../../../../src/components/header.styles.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"header.styles.d.ts","sourceRoot":"","sources":["../../../../src/components/header.styles.ts"],"names":[],"mappings":"AAAA,iDAAiD;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAA0D,MAAM,mBAAmB,CAAC;AACxH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAEhE,eAAO,MAAM,WAAW;;;;;;;;;;CAQvB,CAAC;AAEF,MAAM,WAAW,WAAW;IAC1B,cAAc,EAAE;QACd,aAAa,EAAE,KAAK,CAAC;QAAC,UAAU,EAAE,QAAQ,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QACxD,iBAAiB,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAC;QACnD,eAAe,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,CAAC,CAAC;QAAC,iBAAiB,EAAE,MAAM,CAAC;KAC1E,CAAC;IACF,cAAc,EAAE;QAAE,IAAI,EAAE,CAAC,CAAA;KAAE,CAAC;IAC5B,gBAAgB,CAAC,EAAE;QAAE,QAAQ,EAAE,UAAU,CAAC;QAAC,KAAK,EAAE,CAAC,CAAC;QAAC,MAAM,EAAE,CAAC,CAAC;QAAC,QAAQ,EAAE,QAAQ,CAAA;KAAE,CAAC;CACtF;AAED,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,GAAE;IAAE,OAAO,CAAC,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAA;CAAO,GACjF,WAAW,CAkBb"}
|
|
@@ -93,8 +93,8 @@ export declare const CONTRACTS: {
|
|
|
93
93
|
};
|
|
94
94
|
readonly header: {
|
|
95
95
|
readonly id: "header";
|
|
96
|
-
readonly version: "0.
|
|
97
|
-
readonly variants: readonly ["default"];
|
|
96
|
+
readonly version: "0.2.0";
|
|
97
|
+
readonly variants: readonly ["default", "actions"];
|
|
98
98
|
readonly sizes: readonly [];
|
|
99
99
|
readonly states: readonly ["default"];
|
|
100
100
|
};
|
package/llms.txt
CHANGED
|
@@ -23,7 +23,7 @@ UI を組む前に docs/patterns.md(組み方の規範)を読むこと。exa
|
|
|
23
23
|
- [card](https://melta.tsubotax.com/design/contracts/components/card.contract.json): ✅ `Card` — コンテンツコンテナ。Basic / Metrics / Media / Action / Link の5バリエーション。
|
|
24
24
|
- [checkbox](https://melta.tsubotax.com/design/contracts/components/checkbox.contract.json): ✅ `Checkbox` — 複数選択フォームコントロール。CSS 18px サイズ。
|
|
25
25
|
- [empty-state](https://melta.tsubotax.com/design/contracts/components/empty-state.contract.json): ✅ `EmptyState` — データ無し時のプレースホルダー(アイコン + タイトル + 説明 + 任意のアクション)。Text + Button の compose。
|
|
26
|
-
- [header](https://melta.tsubotax.com/design/contracts/components/header.contract.json): ✅ `Header` —
|
|
26
|
+
- [header](https://melta.tsubotax.com/design/contracts/components/header.contract.json): ✅ `Header` — 画面ヘッダー。defaultは可視見出し、actionsは見出しを視覚的に隠した操作バー。どちらも意味のあるtitleを必須とし、leading/trailingの操作を個別に読み上げる。
|
|
27
27
|
- [icon](https://melta.tsubotax.com/design/contracts/components/icon.contract.json): ✅ `Icon` — 統一アイコン primitive。name prop でグリフを選択し(グリフ集合は実装側の公開面 = assets/icons の Charcoal セット由来)、色は semantic token キー限定の prop(default
|
|
28
28
|
- [image](https://melta.tsubotax.com/design/contracts/components/image.contract.json): ✅ `Image` — 画像表示 primitive。aspectRatio / radius / contentFit / onError fallback を持つ。Phase1 は RN コア Image、expo-image は Phase2。
|
|
29
29
|
- [metric](https://melta.tsubotax.com/design/contracts/components/metric.contract.json): ✅ `Metric` — 数値 + 単位 + ラベルの指標表示 primitive(距離 / 時間 / 獲得標高 等の走行サマリー表示)。tabular-nums で桁揃え。sizes の height は数値テキストの想定行高(px)で、実 fontSize は実
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "melta-app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "melta for APP — melta デザインシステムの React Native UI kit。melta-contracts(デザイン契約)を single source of truth に RN 実装でその契約を満たす。アプリ本体ではなくライブラリ。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://app.melta.tsubotax.com",
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
"@types/react": "~19.2.2",
|
|
87
87
|
"eslint": "^10.4.1",
|
|
88
88
|
"jest": "^29.7.0",
|
|
89
|
-
"melta-contracts": "^0.
|
|
89
|
+
"melta-contracts": "^0.8.0",
|
|
90
90
|
"react": "19.2.3",
|
|
91
91
|
"react-native": "0.85.3",
|
|
92
92
|
"react-native-builder-bob": "^0.40.13",
|
|
@@ -117,7 +117,8 @@
|
|
|
117
117
|
"check:installability": "bash scripts/check-installability.sh",
|
|
118
118
|
"check:build-fresh": "tsx scripts/check-build-fresh.ts",
|
|
119
119
|
"release": "npm run build && npm run typecheck && npm run lint && npm test && npm run test:rn && npm run check:drift && npm run check:build-fresh && npm run check:installability && npm publish",
|
|
120
|
-
"build:llms": "tsx scripts/build-llms-txt.ts"
|
|
120
|
+
"build:llms": "tsx scripts/build-llms-txt.ts",
|
|
121
|
+
"check:header-layout": "node scripts/check-header-layout.mjs"
|
|
121
122
|
},
|
|
122
123
|
"jest": {
|
|
123
124
|
"preset": "@react-native/jest-preset",
|
|
@@ -1,51 +1,52 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Header —
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* - title は melta の Text(xl / bold / text-heading / heading role)で描画し、
|
|
7
|
-
* heading として読み上げられる(contract a11y required)。
|
|
8
|
-
* - titleWrapStyle の flex:1 で title が余白を占有し trailing を右端へ押す。
|
|
9
|
-
* - 決定ロジックは pure resolver(header.styles.ts)に分離 — recipe との機械照合対象。
|
|
2
|
+
* Header — 画面名と左右の操作を持つ共通ヘッダー。
|
|
3
|
+
* actionsでは画面名を視覚的に隠すが、独立したa11y見出しとして残す。
|
|
4
|
+
* 余白・下線・見出しの配置はheader recipeとpure resolverが所有する。
|
|
10
5
|
*/
|
|
11
|
-
|
|
12
6
|
import { useMemo, type ReactNode } from "react";
|
|
13
7
|
import { View, type StyleProp, type ViewStyle } from "react-native";
|
|
14
8
|
import { useTheme } from "../theme/index.js";
|
|
15
|
-
import { CONTRACTS } from "../contracts/contract-types.js";
|
|
9
|
+
import { CONTRACTS, type VariantOf } from "../contracts/contract-types.js";
|
|
16
10
|
import { Text } from "../primitives/Text.js";
|
|
17
|
-
import {
|
|
11
|
+
import { HEADER_SPEC, resolveHeaderStyle } from "./header.styles.js";
|
|
18
12
|
|
|
19
13
|
interface HeaderProps {
|
|
20
|
-
/**
|
|
14
|
+
/** 空でない画面名。actionsでも読み上げるため必須。 */
|
|
21
15
|
title: string;
|
|
22
|
-
/**
|
|
16
|
+
/** defaultは可視見出し、actionsは可視タイトルのない操作バー。 */
|
|
17
|
+
variant?: VariantOf<"header">;
|
|
23
18
|
leading?: ReactNode;
|
|
24
|
-
/** 右端の slot(アクション等)。 */
|
|
25
19
|
trailing?: ReactNode;
|
|
26
20
|
style?: StyleProp<ViewStyle>;
|
|
27
21
|
testID?: string;
|
|
28
22
|
}
|
|
29
23
|
|
|
30
|
-
export function Header({ title, leading, trailing, style, testID }: HeaderProps) {
|
|
24
|
+
export function Header({ title, variant = "default", leading, trailing, style, testID }: HeaderProps) {
|
|
31
25
|
const { theme, mode } = useTheme();
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
() => resolveHeaderStyle(theme, mode),
|
|
35
|
-
[theme, mode],
|
|
26
|
+
const { containerStyle, titleWrapStyle, hiddenTitleStyle } = useMemo(
|
|
27
|
+
() => resolveHeaderStyle(theme, mode, { variant }), [theme, mode, variant],
|
|
36
28
|
);
|
|
37
|
-
|
|
38
29
|
return (
|
|
39
30
|
<View style={[containerStyle, style]} testID={testID}>
|
|
40
31
|
{leading}
|
|
41
32
|
<View style={titleWrapStyle}>
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
33
|
+
{variant === "actions" ? (
|
|
34
|
+
// 見出しTextの契約を再利用し、左右の操作を親の読み上げへ畳まない。
|
|
35
|
+
<View pointerEvents="none" style={hiddenTitleStyle}>
|
|
36
|
+
<Text variant={HEADER_SPEC.titleFont} weight={HEADER_SPEC.titleWeight}
|
|
37
|
+
color={HEADER_SPEC.titleColor} role="heading" numberOfLines={1}>
|
|
38
|
+
{title}
|
|
39
|
+
</Text>
|
|
40
|
+
</View>
|
|
41
|
+
) : (
|
|
42
|
+
<Text variant={HEADER_SPEC.titleFont} weight={HEADER_SPEC.titleWeight}
|
|
43
|
+
color={HEADER_SPEC.titleColor} role="heading" numberOfLines={1}>
|
|
44
|
+
{title}
|
|
45
|
+
</Text>
|
|
46
|
+
)}
|
|
45
47
|
</View>
|
|
46
48
|
{trailing}
|
|
47
49
|
</View>
|
|
48
50
|
);
|
|
49
51
|
}
|
|
50
|
-
|
|
51
52
|
Header.__contract = CONTRACTS.header;
|
|
@@ -1,44 +1,45 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* react-native を import しない純粋モジュールにすることで node テスト(tsx --test)から
|
|
5
|
-
* 直接実行できる。recipes/app/header.recipe.json(melta-contracts の styleRefs)との
|
|
6
|
-
* 機械照合は scripts/lib/header-conformance.test.ts が行う。
|
|
7
|
-
*/
|
|
1
|
+
/** Headerの見た目をrecipeと照合できるpure resolverへ集約する。 */
|
|
2
|
+
import type { NativeTheme, ThemeMode, SpacingKey, FontSizeKey, FontWeightKey, SemanticColors } from "../theme/index.js";
|
|
3
|
+
import type { VariantOf } from "../contracts/contract-types.js";
|
|
8
4
|
|
|
9
|
-
|
|
5
|
+
export const HEADER_SPEC = {
|
|
6
|
+
gap: "3", actionsGap: "1", paddingX: "4", paddingY: "3",
|
|
7
|
+
titleFont: "xl", titleWeight: "bold", titleColor: "text-heading",
|
|
8
|
+
borderWidth: 1, hiddenTitleSize: 1,
|
|
9
|
+
} as const satisfies {
|
|
10
|
+
gap: SpacingKey; actionsGap: SpacingKey; paddingX: SpacingKey; paddingY: SpacingKey;
|
|
11
|
+
titleFont: FontSizeKey; titleWeight: FontWeightKey; titleColor: keyof SemanticColors;
|
|
12
|
+
borderWidth: 1; hiddenTitleSize: 1;
|
|
13
|
+
};
|
|
10
14
|
|
|
11
15
|
export interface HeaderStyle {
|
|
12
16
|
containerStyle: {
|
|
13
|
-
flexDirection: "row";
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
paddingHorizontal: number;
|
|
17
|
-
paddingVertical: number;
|
|
18
|
-
backgroundColor: string;
|
|
19
|
-
borderBottomWidth: 1;
|
|
20
|
-
borderBottomColor: string;
|
|
17
|
+
flexDirection: "row"; alignItems: "center"; gap: number;
|
|
18
|
+
paddingHorizontal: number; paddingVertical: number;
|
|
19
|
+
backgroundColor: string; borderBottomWidth: 1; borderBottomColor: string;
|
|
21
20
|
};
|
|
22
21
|
titleWrapStyle: { flex: 1 };
|
|
22
|
+
hiddenTitleStyle?: { position: "absolute"; width: 1; height: 1; overflow: "hidden" };
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
* (variant="xl" weight="bold" color="text-heading" role="heading")を compose する(text recipe が正)。
|
|
29
|
-
*/
|
|
30
|
-
export function resolveHeaderStyle(theme: NativeTheme, mode: ThemeMode): HeaderStyle {
|
|
25
|
+
export function resolveHeaderStyle(
|
|
26
|
+
theme: NativeTheme, mode: ThemeMode, props: { variant?: VariantOf<"header"> } = {},
|
|
27
|
+
): HeaderStyle {
|
|
31
28
|
return {
|
|
32
29
|
containerStyle: {
|
|
33
|
-
flexDirection: "row",
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
paddingHorizontal: theme.spacing["4"],
|
|
37
|
-
paddingVertical: theme.spacing["3"],
|
|
30
|
+
flexDirection: "row", alignItems: "center", gap: theme.spacing[props.variant === "actions" ? HEADER_SPEC.actionsGap : HEADER_SPEC.gap],
|
|
31
|
+
paddingHorizontal: theme.spacing[HEADER_SPEC.paddingX],
|
|
32
|
+
paddingVertical: theme.spacing[HEADER_SPEC.paddingY],
|
|
38
33
|
backgroundColor: theme.color.semantic[mode]["bg-page"],
|
|
39
|
-
borderBottomWidth:
|
|
34
|
+
borderBottomWidth: HEADER_SPEC.borderWidth,
|
|
40
35
|
borderBottomColor: theme.color.semantic[mode]["border-default"],
|
|
41
36
|
},
|
|
42
37
|
titleWrapStyle: { flex: 1 },
|
|
38
|
+
...(props.variant === "actions" ? {
|
|
39
|
+
hiddenTitleStyle: {
|
|
40
|
+
position: "absolute" as const, width: HEADER_SPEC.hiddenTitleSize,
|
|
41
|
+
height: HEADER_SPEC.hiddenTitleSize, overflow: "hidden" as const,
|
|
42
|
+
},
|
|
43
|
+
} : {}),
|
|
43
44
|
};
|
|
44
45
|
}
|