@cakejp/design-system 0.12.0 → 0.14.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/CHANGELOG.md +77 -0
- package/CLAUDE.md +12 -5
- package/MIGRATION.md +45 -0
- package/UPDATING.md +13 -0
- package/bin/check-duplicates.mjs +133 -9
- package/bin/exports-parser.mjs +54 -0
- package/bin/init.js +3 -0
- package/contracts/components.json +504 -209
- package/contracts/composition.json +11 -8
- package/contracts/rules.json +354 -113
- package/contracts/screen-patterns.json +1 -1
- package/dist/index.css +1 -1
- package/dist/index.js +4285 -3422
- package/dist/types/components/ec/admin-shell.d.ts +30 -2
- package/dist/types/components/ec/product-detail-sidebar.d.ts +1 -1
- package/dist/types/components/foundations/card.d.ts +27 -3
- package/dist/types/components/foundations/field.d.ts +26 -3
- package/dist/types/components/foundations/input.d.ts +3 -1
- package/dist/types/components/foundations/social-icon.d.ts +33 -0
- package/dist/types/components/foundations/textarea.d.ts +3 -1
- package/dist/types/components/primitives/chat-composer.d.ts +87 -0
- package/dist/types/components/primitives/combobox.d.ts +24 -0
- package/dist/types/components/primitives/content-carousel.d.ts +7 -1
- package/dist/types/components/primitives/date-picker.d.ts +15 -3
- package/dist/types/components/primitives/funnel-shell.d.ts +2 -0
- package/dist/types/components/primitives/image-input.d.ts +119 -0
- package/dist/types/components/primitives/multi-select.d.ts +24 -0
- package/dist/types/components/primitives/notification-badge.d.ts +1 -1
- package/dist/types/components/primitives/number-input.d.ts +84 -0
- package/dist/types/components/primitives/section-header.d.ts +9 -2
- package/dist/types/components/primitives/section.d.ts +35 -2
- package/dist/types/components/primitives/social-login-button.d.ts +3 -1
- package/dist/types/components/primitives/timeline.d.ts +23 -0
- package/dist/types/components/primitives/workspace-shell.d.ts +2 -0
- package/dist/types/index.d.ts +25 -7
- package/dist/types/lib/pagination-range.d.ts +16 -0
- package/dist/types/lib/use-value-length.d.ts +31 -0
- package/eslint/deprecated.js +44 -0
- package/package.json +8 -3
- package/scripts/codemod/README.md +14 -0
- package/scripts/codemod/template.mjs +62 -0
- package/src/components/COMPONENT_LOOKUP.md +166 -0
- package/src/preset-standalone.css +16 -0
- package/src/preset.css +17 -0
- package/src/styles/typography.css +7 -0
- package/templates/AGENTS.md +4 -0
- package/templates/CLAUDE.md +4 -0
- package/tokens.json +933 -87
- package/dist/types/components/primitives/quantity-selector.d.ts +0 -50
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,83 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [0.14.0] - 2026-07-25
|
|
11
|
+
|
|
12
|
+
姉妹 DS(ksk-design-system v1.44.0)との相互棚卸しで見つかった 12 課題(#168〜#179 → PR #180)の統合と、そのレビュー・統合過程で見つかった修正を収録する回。**0.13.0 からは追加・改名(旧名 alias 維持)のみで、コード上の破壊的変更なし。** 視覚変更は 2 点(AlertDialog の幅・NumberInput sm の左右余白)。
|
|
13
|
+
|
|
14
|
+
### ✨ Added
|
|
15
|
+
|
|
16
|
+
- **`NumberInput` — `QuantitySelector` を機能名に改名し、汎用数値入力へ拡張**(旧名は deprecated alias で維持・**すぐの対応不要**): `QuantitySelector` は「カート数量」という**用途名**だったため、管理画面の価格・在庫入力を探した側が同じ部品を見つけられず、意匠の違う数値ステッパーの二重実装が生まれかけた(PR #180 → #184 で統合)。意匠は従来どおり Figma DS「Adjust」準拠(`sm`=コンパクト型 / `md`=サークル型)のまま、機能名 `NumberInput` に改名し、汎用の数値入力に必要だった `step` / `formatOptions`(桁区切り・通貨表示。Intl の全角 `¥` は DS 規約の半角 `¥` に正規化)/ `defaultValue`(非制御。min/max にクランプ)/ `label`(読み上げラベル。既定「数量」— **価格・在庫に使うときは必ず指定**)/ `inputId`(`FormField` 接続)を追加した。`aria-label` が「数量」固定で価格・在庫の読み上げが誤っていた問題も解消。既定の `min=1` / `max=99` はカート数量向けのため、汎用用途では明示すること。`data-slot` は `quantity-selector` → `number-input` に変更([#173](https://github.com/flashpark/cake-design-system/issues/173) / PR #184)。命名規約「**コンポーネント名は用途ではなく機能で付ける**」を `contracts/components.json` の `meta.naming` に、探し方のゲート(名前で出なければ機能で探す / 姉妹 DS の在庫は根拠にならない)を `CLAUDE.md` に明文化し、contracts 上のステッパーが 1 つだけであることをテストで固定した
|
|
17
|
+
- **`SocialIcon`(foundations)を新設 — ブランドロゴ SVG の一元管理**: LINE / X / Facebook / Amazon / Google / Apple のロゴが `ShareButtons` と `SocialLoginButton` に**別々に手組み**されており drift リスクがあった(LINE は両方に存在)。`platform` × `tone`(`brand`=公式色 / `mono`=currentColor)の 1 部品に集約し、両コンポーネントを載せ替えた。**ブランド色の地(LINE の緑・Apple の黒)に載せるときは `tone="mono"` を指定する**(brand のままだと地と同色でアイコンが消える)。`size` は inline style で当たるため `Button` 内の `[&_svg]:size-4` に負けない。地色との同化・サブパス欠落・サイズ打ち消しは型でもビルドでも落ちないため、SSR 出力を固定するテストを新設した([#174](https://github.com/flashpark/cake-design-system/issues/174))
|
|
18
|
+
- **`Combobox` / `MultiSelect`(primitives)を新設 — 選択肢が多い場面の受け皿**: `Combobox`=検索付き単一選択(`Popover` + `Command`)、`MultiSelect`=複数選択(選択済みは removable な `Chip` で表示)。いずれも制御コンポーネントで、`id` / `aria-invalid` / `aria-describedby` を trigger に透過して `FormField` / `FieldError` と接続できる。使い分け(少数候補は `Select` / `RadioGroup` / `CheckboxGroup`)を contracts に明記([#175](https://github.com/flashpark/cake-design-system/issues/175))
|
|
19
|
+
- **`eslint/deprecated.js` を配布 — consumer 側で旧 export の残存を検出**: `contracts/components.json` の `deprecatedAliases`(`AdminLayout` / `WorkspaceLayout` / `EcPageShell` / `QuantitySelector`)をデータ源に、ESLint 9 flat config のプラグインとして `@cakejp/design-system/eslint/deprecated` を export。破壊的変更後の残骸チェックを CI・エディタで継続的に回せる([#172](https://github.com/flashpark/cake-design-system/issues/172))
|
|
20
|
+
- **`tokens.json` に semantic トークンを全量収録(light 78 / dark 64)**: `Object-*` / `Hover-*` / `Active-*` / `Overlay-*` / `Focus-*` / `Third-Party-*` と **dark 値全体**が未収録で、AI ツール・Figma 連携から辿れなかった。`semantic.css` との双方向網羅を `__tests__/semantic-token-contract.test.ts` で固定([#176](https://github.com/flashpark/cake-design-system/issues/176))
|
|
21
|
+
- **`rules.json` に `aiPatterns` セクションを新設**: `CLAUDE.md` が参照していた `aiPatterns` キーが実在せず、AI 特有の誤りパターン 7 件(AI1〜AI7)が `prohibited` に混在していた。独立セクションに分離し、MCP の `check_rule` が禁止パターンと AI パターンを区別して返すようにした([#170](https://github.com/flashpark/cake-design-system/issues/170))
|
|
22
|
+
- **Storybook に hostile-context モード(Safe / Ink / Loud)を追加**: 全ストーリーを意図的に敵対的な親(濃色文字・派手な背景)でラップし、親の色を継承して壊れる context-independence 違反を目視で炙り出す。0.13.0 の #150(グレー地でカードが沈む)と同系統のバグの検出用([#179](https://github.com/flashpark/cake-design-system/issues/179)。ksk 由来)
|
|
23
|
+
- **`COMPONENT_LOOKUP.md` を npm 配布物に追加**: 全コンポーネントの import パス・variants・ストーリー名の一覧表。contracts の JSON をパースせず 1 枚で全量を掴める軽量な AI 参照導線([#178](https://github.com/flashpark/cake-design-system/issues/178))
|
|
24
|
+
- **`scripts/codemod/template.mjs` を新設**: 次の破壊的変更用の codemod 雛形(RENAMES / PROP_RENAMES / WARNINGS を埋めるだけ・`--dry` 付き)。※ `npx cakejp-ds codemod` の dispatch は既存名決め打ちのままで、雛形から作った codemod は node 直接実行([#182](https://github.com/flashpark/cake-design-system/issues/182) で対応予定)([#177](https://github.com/flashpark/cake-design-system/issues/177))
|
|
25
|
+
|
|
26
|
+
### 🐛 Fixed
|
|
27
|
+
|
|
28
|
+
- **AlertDialog の幅上限を 480px に下げ、画面端の余白を 40px に広げた**(**視覚変更あり**): `default` サイズは shadcn 標準の `max-w-lg`(512px)を初期構築から引き継いだままで、テキスト確認(見出し+本文+ボタン2つ)では本文が横に間延びしていた。幅上限を **480px**(`sm`=320px は不変)、画面端の余白を左右 16px → **上下左右 40px** に変更。SP では端末幅 −80px に追従する(実測: 375px 幅で左右 40px)。余白はコンポーネントが持つので consumer 側で margin を足さないことを contracts に明記(PR #186)
|
|
29
|
+
- **NumberInput `sm` のホバー円の余白を上下左右 4px に統一**(**視覚変更あり**): コンテナ 36px に対しボタン 28px で上下の余白は 4px だが、左右だけ `px-2.5`(10px)でホバー時の円が左右に寄って見えていた。`px-1` で均等化(PR #185)
|
|
30
|
+
- **MCP サーバーが contracts の古い手動コピーを読んでいたのを直読みに変更**: `mcp-server/data/*.json` は正典 `contracts/*.json` の手動コピー(自動同期なし)で、components **37/121**・rules **40/68** までドリフトしており、0.10.0 以降の追加部品が MCP 経由の AI から一切見えていなかった。`contracts/*.json` の直読みに変更し `data/` を削除([#168](https://github.com/flashpark/cake-design-system/issues/168))
|
|
31
|
+
- **contracts の `meta.version` を実バージョンに同期し、`check-drift.sh` で機械検査**: `components.json` が `0.1.0` のまま 12 マイナー分ズレていた。全 contracts ファイルの `meta.version` と `package.json` の一致を `npm run check` で強制する([#169](https://github.com/flashpark/cake-design-system/issues/169))
|
|
32
|
+
- **参照実装ページの生 `<input type="number">` 8 箇所を `NumberInput` に置換**: 正解形が無く手組みが横展開していた。`contracts/rules.json` に R56(生 number input 禁止)を追加し、`check-story-reuse.sh` の検査対象にも追加([#173](https://github.com/flashpark/cake-design-system/issues/173))
|
|
33
|
+
|
|
34
|
+
### 🛠 Tooling
|
|
35
|
+
|
|
36
|
+
- **コントラスト AA 自動検査(`scripts/check-contrast.mjs`)を `npm run check` に配線**: 主要な「文字色 × 背景色」ペアを WCAG 相対輝度式で計算し、既知の未達 4 ペア(`accent-primary` 4.06 / `success` 3.45 / `warning` 4.00 / 白文字×オレンジ地 4.06)は**理由と現状比の下限つき許容リスト**で回帰だけを止める。パレット調整は別 issue。※ 現状 light 専用(dark は [#181](https://github.com/flashpark/cake-design-system/issues/181))([#171](https://github.com/flashpark/cake-design-system/issues/171))
|
|
37
|
+
- **RHF 統合テストを拡張**: 新設 3 部品(NumberInput / Combobox / MultiSelect)の Controller 接続を、初期表示だけでなく **`reset()` 追従と実操作**まで固定(#151 と同型の退行の再発防止)
|
|
38
|
+
|
|
39
|
+
### 📝 Docs
|
|
40
|
+
|
|
41
|
+
- `MIGRATION.md` に `QuantitySelector` → `NumberInput` の移行ガイドを追加(すぐの対応は不要。`data-slot` を CSS フックにしている場合のみ要変更)
|
|
42
|
+
- `CLAUDE.md` の「DS に無いと思ったときのゲート」を強化: 名前で出なければ**機能で探す**(`props` / `rules` / `features` / `COMPONENT_LOOKUP.md`)、`check-duplicates` は名前違いの二重実装を検出できない、姉妹 DS に同名部品があることは cake に無い根拠にならない
|
|
43
|
+
|
|
44
|
+
## [0.13.0] - 2026-07-23
|
|
45
|
+
|
|
46
|
+
consumer(cake-api / cake-distributor / cake-prototypes)からのフィードバック対応。実装の誤用を誘発していた contracts の記述修正と、任意項目・他ブランド採用で足りなかった口の追加。
|
|
47
|
+
|
|
48
|
+
### ✨ Added
|
|
49
|
+
|
|
50
|
+
- **Surface-Secondary 地のシェル(`AdminShell` / `WorkspaceShell` / `FunnelShell`)も配下 `Card` へサーフェス文脈を伝播**(**視覚変更あり**): #150 で `Section` に入れた仕組みをシェルにも広げた。これらのシェルは地が `Surface-Secondary`(#FBF9F8)で、`Card` の既定 `bg-card` も同じ `Surface-Secondary` のため、**カードが地と完全に同色で罫線だけの区切りになっていた**(実測: 管理ダッシュボードの地 `rgb(251,249,248)` に対しカードも `rgb(251,249,248)`、商品編集ワークスペースはカード5枚すべて同色)。各シェルのルート要素に `--card-surface: var(--Surface-Primary)`(dark は `Surface-Secondary` に据え置き)を宣言し、**配下の `Card` は `surface` 未指定のまま白で地から浮く**ようにした。`MypageShell` は本文が白い `main` の中に入る構造なので**あえて宣言していない**(宣言すると白地に白カードで階層差が消える)。`Section` と同じく伝播は CSS カスタムプロパティのみで React context は使わず、`surface="primary"` の明示指定・`className="bg-card"` での上書きも従来どおり。**影響**: light で上記3シェルを使う画面の `surface` 未指定 `Card` が Surface-Secondary → Surface-Primary(白)に変わる(DS 同梱の管理画面ストーリー多数が該当)。dark は変化なし。API 変更なし([#150](https://github.com/flashpark/cake-design-system/issues/150))
|
|
51
|
+
- **`Timeline`(primitives)を新設**: 応募フロー・オンボーディング・注文履歴向けの本文付き縦タイムライン。連番または任意 marker、見出し、ReactNode 本文を受け、コネクター線を DS 側で管理するため consumer の `::before` 手組みを不要にした([#157](https://github.com/flashpark/cake-design-system/issues/157))
|
|
52
|
+
- **`getPaginationRange()` を追加**: 現在ページ・総ページ数・前後表示数から、先頭/末尾を維持したページ番号と `"ellipsis"` の窓を返す。既存 `Pagination*` primitives の描画 API は変更せず、全ページ列挙を避ける判断ロジックだけを共通化した([#156](https://github.com/flashpark/cake-design-system/issues/156))
|
|
53
|
+
- **`ContentCarousel itemsPerView` を追加**: PC で2〜4枚の ReactNode カードを同時表示できるようにし、`ProductCarousel` に依存せず求人・記事等の多列カルーセルを組めるようにした。SP は従来どおり1枚表示([#160](https://github.com/flashpark/cake-design-system/issues/160))
|
|
54
|
+
- **`Section backgroundImage / scrim` を追加**: `dark` / `light` / `bottom-dark` の画像上オーバーレイを `Surface-Overlay-*` トークンで DS 管理し、consumer ごとの gradient 手組みを不要にした([#159](https://github.com/flashpark/cake-design-system/issues/159))
|
|
55
|
+
- **`.tap-target` CSS utility を preset / preset-standalone に追加**: 見た目を変えず透明な操作領域を44×44px以上へ広げ、小型ボタン・Chip 等の SP 操作性を consumer 側の個別疑似要素なしで確保できるようにした([#158](https://github.com/flashpark/cake-design-system/issues/158))
|
|
56
|
+
- **React Hook Form の `register()` / `reset()` 統合 story を追加**: consumer の主要な非制御 ref 経路を Storybook 上で再現し、Input / Textarea の初期値・外部リセット・文字数表示を目視できるようにした([#152](https://github.com/flashpark/cake-design-system/issues/152))
|
|
57
|
+
- **`Section` から配下 `Card` へサーフェス文脈を伝播 — 色地の上のカードが `surface` 未指定でも白になる**(**視覚変更あり**): 0.12.0 で入れた `Card surface="primary"`([#127](https://github.com/flashpark/cake-design-system/issues/127))は「グレー地でカードが沈む」を prop で解決できたが、保証が**カード1枚ごとの明示指定**になったため、consumer(cake-jobboard)は移行で3ファイル7箇所を個別に触る必要があり、さらに**この先カードを足したときの付け忘れが型でもレビューでも検知できない**(症状が地味なコントラスト不足なので見逃される)という指摘があった。`Section background="subtle"|"accent-subtle"` が配下に `--card-surface: var(--Surface-Primary)` を流し、`Card` の既定サーフェスを `bg-[var(--card-surface,var(--card))]` に変更することで、**色地 Section の中の Card は `surface` 未指定のまま自動で白**になる(=移行前の `.section-gray [data-slot="card"] { background: ... }` と同じ「構造による保証」を DS 側で持つ)。伝播は **CSS カスタムプロパティのカスケードのみで React context は使わない** — Card / Section は Server Component のまま使え、DS の `Card` 以外(consumer 自前の `article` 等)には一切影響しない。DS の `Section` を使わない自前の色地でも、親要素に `className="[--card-surface:var(--Surface-Primary)]"` を宣言すれば同じ文脈を作れる。`Card surface="primary"` の明示指定は従来どおり有効で文脈より優先。例外的に1枚だけ地に馴染ませたいときは `className="bg-card"` で上書きする(tailwind-merge で後勝ち)。`background="none"` は文脈をリセットしない(色地 Section に入れ子にした `none` は背景が透過して外側の色地が見えるため、配下 Card も白のままが正しい)。**dark では伝播値を `Surface-Secondary` に据え置く**(dark の Surface-Secondary は半透明白なので色地に重なって一段明るく浮き、そもそも沈まない。ここで Surface-Primary(ほぼ不透明の黒)を強制すると地より暗い「穴」になり elevation が反転する。※ `surface="primary"` の明示指定は light/dark とも Surface-Primary で固定=従来どおり)。**影響**: light では `Section background="subtle"|"accent-subtle"` の中で `surface` 未指定の `Card` を使っている画面のカード背景が Surface-Secondary(#FBF9F8)から Surface-Primary(#FFFFFF)に変わる(= contracts が元々求めていた見た目に揃う)。dark は変化なし。`Card` は `data-surface` 属性も出すようになった([#150](https://github.com/flashpark/cake-design-system/issues/150))
|
|
58
|
+
- **`typo-heading-5xl`(40px)とLPヒーロー向けレスポンシブ見出しを追加**: LP・マーケティング系ヒーローのPC見出し用に `typo-heading-5xl` を追加し、`SectionHeader` でも `size="3xl"` + `desktopSize="4xl"` / `"5xl"` としてSP 28pxからPC 32px/40pxへ型安全に拡大できるようにした。用途境界は「3xl=通常ページの主見出しにも使用、4xl/5xl=LP・マーケティング系ヒーローのPC限定」に統一。MCPのタイポグラフィ一覧は `tokens.json` から生成し、今後のスケール追加が外部AIツールから欠落しないようにした([#144](https://github.com/flashpark/cake-design-system/issues/144))
|
|
59
|
+
- **`ImageInput`(primitives)を新設 — 画像1枚の入力(プレビュー + 形式/サイズ検証 + 値のバインド)**: 既存の `ImageUploader` は名前に反して**複数枚の表示グリッド**で、`images` を並べるだけ・ファイル選択は `onAdd` で consumer に丸投げという実体だった。そのため cake-api(管理画面のカテゴリLP編集・5箇所)は**同名で責務の違う「1枚の画像入力」を 387 行ローカル実装**して保持し続けており、`npx cakejp-ds check-duplicates` が恒常的に名前衝突を検出していた。「N枚の管理グリッド」と「1枚の入力+検証」は props がほぼ交わらないため `ImageUploader` に mode を足す拡張は採らず、**責務ごとに別部品**として `ImageInput` を新設した(`FileInput`=汎用添付 / `ImageInput`=画像1枚の入力 / `ImageUploader`=複数枚のグリッド管理、の三者の住み分けを contracts・rules・各 docstring に明記)。値は `value: File | string | null`(`File`=新規選択 / `string`=保存済み画像の URL)+ `onChange(file)` の制御コンポーネントで、`id` / `name` / `onBlur` / `error` を持ち `FormField` や React Hook Form に直結できる。ファイル選択の口は既存 `FileInput` を内包して再利用し(生の `<input type="file">` は書かない)、`accept`(既定 `image/png,image/jpeg`)の MIME 検査と `maxSizeMb`(既定 2)の上限検査を行い、**エラーは日本語でインライン表示**(`FieldError` = `role="alert"`、内包 input に `aria-invalid` / `aria-describedby` を接続)。モーダルは入力の流れを止めフィールドと紐付かないため既定にせず、Dialog やトーストで出したい consumer 向けに `onValidationError` を用意した。プレビューは `File` の `URL.createObjectURL` を **値の差し替え時・アンマウント時に必ず `revokeObjectURL`**(メモリリーク防止)、`string` はそのまま描画するため SSR 初回から表示される。表示サイズは `aspect`(`square` / `landscape`=1200:630)× `size`(`sm`=156px / `md`=380px / `lg`=568px)と `objectFit`(`cover` / `contain`)の CVA バリアント。クリアボタンは `clearable={false}` で抑制でき、`disabled` にも対応。非同期検証(画像寸法チェック等)は DS には持たせず、`onChange` 後に consumer 側で検証して `error` で戻す方針を明記した。**新規追加のみで `ImageUploader` / `FileInput` の API・挙動は不変**([#138](https://github.com/flashpark/cake-design-system/issues/138))
|
|
60
|
+
- **`ChatComposer`(primitives)を新設 — メッセージ入力 + 添付 + 送信の一体型コンポーザ**: DS にはチャット/メッセージング系の入力 primitive が無く、consumer(cake-jobboard の応募者⇔店舗チャット)は `Textarea` + `Button` + 生の `<input type="file">` で**コンポーザを丸ごと自前実装**していた(実装コメントに「汎用 UI として DS への昇格候補」と明記済み)。送信キー(Enter か ⌘Enter か)・添付の見せ方・送信中の扱いが consumer ごとにドリフトし、とくに**IME 変換確定の Enter で誤送信**する事故は各実装で個別に手当てするしかなかった。`value` / `onChange` / `onSubmit(value, files)` を持つ制御コンポーネントとして追加し、中身は既存部品の組み合わせ(`Textarea` / `FileInput` / `Chip` / `Button`)で新規 DOM を最小に抑えている。送信キーは Enter 送信・Shift+Enter 改行が既定で、長文が主な用途は `submitKey="mod-enter"`(⌘/Ctrl+Enter 送信・Enter は改行)に切替可。**どちらのモードでも IME 変換中の Enter では送信しない**(`isComposing` と `compositionstart`/`compositionend` で二重ガード)。添付は内蔵の `FileInput`(`accept` / `maxFiles` / `maxSizeMb`、超過は `onAttachmentError`)で選択し、選択済みは押せる・消せる `Chip` の removable で表示(表示専用の `Tag` は使わない)。`disabled` / `isSubmitting`(送信ボタン無効化で**二重送信防止**)/ `placeholder` / `maxLength`(`Textarea` の `showCount` と連動して文字数カウンタ表示)に対応。`<form>` を内包しないため consumer のフォーム内に置いても入れ子にならない。**新規追加のみで既存 API の変更なし**([#122](https://github.com/flashpark/cake-design-system/issues/122))
|
|
61
|
+
- **`DatePicker` / `DateTimePicker` に `clearable` を追加 — 選択済みの値を明示的に消せるように**: これまでクリア手段は「カレンダーを開いて選択済みの日付を再クリックする」(react-day-picker の標準トグル)だけで、trigger 上に導線が無く**画面から発見できなかった**。cake-api の「広告掲載開始日時/終了日時」は任意項目(`nullable`)で「一度設定した期間を空に戻す」操作が実運用で必要だが辿れず、0.12.0 移行時にローカル実装が持っていた × ボタンが失われる退行が発生していた。`clearable?: boolean`(既定 `false`=後方互換)を追加し、`value` があり `disabled` でないとき trigger 右端に × を描画、押下で `onChange(undefined)` を呼ぶ。`aria-label` は `clearLabel`(`DatePicker` 既定「日付をクリア」/ `DateTimePicker` 既定「日時をクリア」)。**クリアボタンは trigger の兄弟として絶対配置**しており(`Chip` removable と同じ解決)、button の入れ子にならない。※ これに伴い `className` の付与先が trigger から外側ラッパーへ変わる(幅・レイアウト指定はそのまま効く)([#136](https://github.com/flashpark/cake-design-system/issues/136))
|
|
62
|
+
- **`AdminShell` に `brand` / `sidebarHeader` を追加 — 他ブランドの管理画面で骨格を再利用できるように**: ブランド表示が `shopName`(文字列)から `{shopName}管理` を合成する形に固定されており、バッジ+見出し+説明のようなブランドブロックを渡す口が無かった(cake-distributor がローカル AdminShell を保持していた理由の一つ)。`brand?: ReactNode`(ヘッダー左側。既定=`{shopName}管理`)と `sidebarHeader?: ReactNode`(SP ドロワー上部見出し。既定=`shopName`)を追加。内部の `AdminHeader.brand` / `AdminSideNav.drawerTitle` は元々 ReactNode を受けられたため、シェルが塞いでいた口を開けた形。既定は現行のままで**後方互換**。※ **PC の左サイドナビは 72px のアイコンレール**でブランドブロックを置く領域が無く、幅広サイドバー前提の画面はこの骨格では置き換えられない旨を docstring / contracts に明記した([#135](https://github.com/flashpark/cake-design-system/issues/135))
|
|
63
|
+
|
|
64
|
+
### 🐛 Fixed
|
|
65
|
+
|
|
66
|
+
- **`StatusCountTabs` がマウント後の `activeIndex` 変更へ追従しない不具合を修正**: URL クエリ復元・外部フィルタ・非同期データ確定など、クリック以外の経路で親が `activeIndex` を変更しても内部 state が初期値のまま残っていた。`activeIndex` の変更を内部選択へ同期し、従来の非制御クリック操作も維持した。外部変更 story と回帰テストを追加([#165](https://github.com/flashpark/cake-design-system/issues/165))
|
|
67
|
+
- **`Input` / `Textarea` の `showCount` が非制御(react-hook-form の `register()`)で初期値を数えず `0/n` のままだった不具合を修正**: カウントの実装が非制御時に内部 state(`useState` + `onChange` でのみ更新)だけを見ていたため、`const count = isControlled ? String(value ?? "").length : uncontrolledValue.length` の非制御側が常に 0 起点だった。react-hook-form の `register()` は `name` / `onChange` / `onBlur` / `ref` のみを渡す非制御パターンで、初期値は `defaultValues` / `reset()` により **`ref` 経由で DOM の `value` に直接書き込まれる**(`onChange` は発火しない)。そのため入力欄に文字が見えているのにカウンタは `0/40` のまま、ユーザーが1文字打つと正しくなる、という挙動になっていた(cake-api のカテゴリLP編集画面で発見)。非制御時は内部 state ではなく**実際の DOM の `value` を毎 render 後の layout effect で読み直す**方式に変更し、`ref` 経由の初期値・`reset()` / `setValue()` による後からの外部書き換えにも追従するようにした。consumer が渡す `ref` は内部 ref と合成して従来どおり届く。制御(`value` 指定)・`defaultValue` のみの非制御・`showCount` 未指定時の挙動と DOM は不変で**後方互換**
|
|
68
|
+
- **`AdminSideNav` の5文字超ラベルが不自然に1文字だけ折り返される問題を修正**: PC レールのラベルを `typo-label-xs`・中央揃え・`max-w-12` に統一し、consumer の `data-slot` 内部上書きを不要にした([#155](https://github.com/flashpark/cake-design-system/issues/155))
|
|
69
|
+
- **フォーム入力コントロールと `Button` の高さを 44px に統一し、`SelectTrigger` の高さを `className` で上書きできるようにした**(**視覚変更あり**): 0.12.0 では `Input`(`h-12`=48px)と `SelectTrigger`(`data-[size=default]:h-10`=40px)の高さが揃わず、検索 + 絞り込みセレクトのような**並置が定番の組み合わせで 8px ずれて**いた。さらに `SelectTrigger` の高さが **data 属性セレクタ**(`data-[size=default]:h-10`)で指定されていたため詳細度で勝てず、`className="h-12"` を渡しても効かない(consumer は DS の内部記法を知って `data-[size=default]:h-12` と書く必要があった)。①`SelectTrigger` の高さ指定を**素のクラス**(`size==="sm" ? "h-9" : "h-11"`)に変更して `className` で素直に上書きできるようにし、②入力コントロールの既定を **44px(`h-11`)に統一**した。44px は `tokens.json` / `contracts/rules.json` の touchTarget『Text input』の **min = 44px を満たす最小サイズ**で、管理画面の情報密度とアクセシビリティのバランスが最良(iOS HIG のタッチターゲット 44pt とも一致)。48px は管理画面のような密度の高い画面で間延びするため採らなかった(48px まで上げたい画面は consumer が `className="h-12"` で上書きできる)。③あわせて **`Button` の `size="default"` も 40px → 44px** に変更した。入力コントロールだけを 48px に揃えても `Button` の既定が 40px のままではフォーム内で **48 vs 40 のズレが残る**ため、今回は**入力行に並ぶ部品を Button 込みで本当に全部 44px に揃えた**。`Button` の他サイズ(`xs`=24px / `sm`=32px / `lg`=48px / `xl`=56px / `hero`=min 56px)と `SelectTrigger size="sm"`(36px)は変更なし。**0.12.0 からの差分**: `Input` 48px → **44px**(低くなる) / `SelectTrigger` default 40px → **44px** / `DatePicker`・`DateTimePicker` trigger 40px → **44px** / `Button` default 40px → **44px**。`Input` を使っている画面は行の高さが 4px 詰まり、`Select` / `DatePicker` / `Button`(default)を使っている画面は 4px 高くなる。「入力コントロールと Button default は 44px」を `contracts/components.json` の Input / Select / Button に明記([#142](https://github.com/flashpark/cake-design-system/issues/142)。0.13.0 は未 publish のため、48px 統一(commit `6b21352`)を受け取った consumer は存在しない)
|
|
70
|
+
- **`DateTimePicker` で日付を変えると設定済みの時刻が 00:00 に落ちる不具合を修正**: カレンダーが返す `Date` は「その日の 00:00:00」だが、`DateTimePicker` がそれをそのまま `onChange` へ流しており、時刻(例 10:30)を設定済みの状態で日付だけ変更すると時刻が失われていた。日付部分のみ差し替え、時/分/秒/ミリ秒は直前の値から引き継ぐよう修正(クリア=`undefined` はそのまま透過)。cake-api では adapter 側で引き継ぎを実装して回避していた([#136](https://github.com/flashpark/cake-design-system/issues/136))
|
|
71
|
+
- **`check-duplicates` が「DS に無い名前」を二重実装として誤検出していたのを修正 — 判定を実 export に限定**: 0.12.0 で新設した `npx cakejp-ds check-duplicates` は `contracts/components.json` の名前とだけ照合していたが、contracts には `CheckboxCard` のような**実装ファイル名・グループ名**(それ自体は export されず、実 export は `CheckboxCardGroup` / `CheckboxCardItem`)が含まれるため、DS から import できない名前でも「DS の import に置き換え検討」と出ていた(cake-api の実行結果 5 件のうち `Field` がこれに該当していた。`Field` 自体は #139 で実 export になった)。判定対象を package 同梱の `dist/types/index.d.ts`(再 export だけの静的テキスト。peer 依存の解決もモジュール評価も不要)から抽出した**実 export 名**との積集合に変更。dist が無い環境(DS リポ自体での実行等)では contracts にフォールバックし、その旨を出力に明示する([#140](https://github.com/flashpark/cake-design-system/issues/140))
|
|
72
|
+
- **`CheckboxGroup` / `CheckboxGroupItem` / `PopoverAnchor` がバレルから export されておらず npm 経由で使えなかったのを修正**: いずれも実装・ストーリー・contracts には存在するのに `src/index.ts` に載っておらず、consumer は `import { CheckboxGroup } from "@cakejp/design-system"` でエラーになっていた。export を追加(追加のみで既存 API の変更なし)([#140](https://github.com/flashpark/cake-design-system/issues/140))
|
|
73
|
+
|
|
74
|
+
### 🛠 Tooling
|
|
75
|
+
|
|
76
|
+
- **react-hook-form の実利用パターンを DS 側で検証できるようにした(テスト・story・contracts・受け入れ条件)**: 0.12.0 の `showCount` 不具合([#151](https://github.com/flashpark/cake-design-system/issues/151))は、CHANGELOG に「制御/非制御どちらでも追従する」と書きながら、**RHF の `register()` / `reset()` 経路がテストにも story にも一度も登場していなかった**ため、consumer(cake-api 管理画面の 13 ファイル / 22 コールサイト)の全カウンタが `0/n` になるまで誰も気付けなかった。同じ穴を塞ぐため、①`__tests__/react-hook-form-integration.test.tsx` を新設し、全フォーム部品について **`defaultValues` での初期表示 / `reset()` への追従 / 操作での値更新**を固定(24 ケース)、②`contracts/components.json` に **`formBinding`**(`register` = `{...register()}` を直接 spread できる / `controller` = `Controller` が必要 / `layout` / `none`)を機械可読で追加し、consumer と AI が繋ぎ方で迷わないようにした、③`__tests__/form-binding-contract.test.ts` が「フォーム部品なら `formBinding` 宣言必須」「`register`/`controller` なら RHF テストに登場必須」を強制する、④`form.stories.tsx` に **`defaultValues` に実データが入った状態**の RHF story(register 経路 / Controller 経路)を追加して視覚回帰にも載せた、⑤`CLAUDE_DEV.md` / `AGENTS.md` に「フォーム系コンポーネントの受け入れ条件」を明記(利用パターンのサポートを CHANGELOG に書くならテストを同 PR に含める)。**実装の変更は無し**([#152](https://github.com/flashpark/cake-design-system/issues/152))
|
|
77
|
+
|
|
78
|
+
- **`check-duplicates` に意図的残置の抑制機構を追加(理由必須)**: 対象 export の直前のコメント行(連続コメントブロック内のどこでも可)または行末コメントに `// cakejp-ds-allow-duplicate: <理由>` を書くと、その 1 件だけ検出から外れる。**理由が空なら抑制しない**(形骸化防止)。抑制した件数と理由はサマリに出力し、理由が空のマーカーは警告する。これにより consumer は `--strict` を CI に常設したまま「新規の意図しない二重実装だけを弾く」運用ができる。書式は `npx cakejp-ds check-duplicates --help` と consumer 向けテンプレ(`templates/CLAUDE.md` / `templates/AGENTS.md`)に明記([#140](https://github.com/flashpark/cake-design-system/issues/140))
|
|
79
|
+
|
|
80
|
+
### 📝 Docs
|
|
81
|
+
|
|
82
|
+
- **contracts と実 export の drift を解消し、対応関係を機械可読にした**: `Sonner`→`Toaster` / `PaymentMethod`→`PaymentMethodDisplay` へエントリ名を実 export に合わせ、`CheckboxCard` のように name がグループ名のものは `"exported": false` + 実 export を `subcomponents` に列挙、`FormField`(primitives)のように別名で export されるものは `"exportedAs": "CakeFormField"` を付与。contracts から辿れなかった実体(`CheckboxCardGroup` / `CheckboxCardItem` / `CheckboxGroupItem` / `RadioGroupItem` / `CreditCardIcon` / `SparkleIcon` / `ReviewItem` / `OptionQuantity` / `OptionSelect` / `OptionText` / `OptionImageUpload` / `MissingComponent` / `Form*`)を `subcomponents` に追加し、旧名 export(`AdminLayout` / `WorkspaceLayout` / `EcPageShell`)は `deprecatedAliases` として記録した(新規利用は不可のまま)。規約は `meta.exportNaming` に明記。再発防止として `__tests__/contracts-export-integrity.test.ts` を新設し、contracts 上の「import できるはずの名前」がすべて `src/index.ts` から export されていること(および `exported:false` の name が export されて**いない**こと)をビルド不要で固定した([#140](https://github.com/flashpark/cake-design-system/issues/140))
|
|
83
|
+
- **Button の `secondary` / `tertiary` の使い分けを contracts から読み取れるように修正**: `composition.json` の `ctaHierarchy.priority` が variant を**重要度の序数だけ**で定義していたため(`secondary`="Secondary action")、「主 CTA の隣=2番目」を素直に読むと「戻る」に `secondary` が選ばれていた。実際は `secondary`=ブランドアクセント(薄オレンジ)/ `tertiary`=ニュートラルグレーで、Material・Bootstrap・shadcn の慣習(secondary=グレー)と**意匠が逆**なため、LLM 実装では特に誤用が繰り返される。各項目に **`appearance`(実際の色)** を追加し、「序列ではなく役割で選ぶ」旨を `description` に明記。「戻る/キャンセル/閉じる/スキップ/あとで=`tertiary`」「`secondary` は別方向の“前進”副 CTA」を `composition.json` / `components.json` 双方の `rules` に追加。意匠・API は不変([#133](https://github.com/flashpark/cake-design-system/issues/133))
|
|
84
|
+
- **`RadioGroup` の記述が枠の二重化を誘発していたのを修正**: `accessibility` の「各RadioGroupItemにLabel必須」が「`<Label>` を併置しろ」と読め、consumer が `RadioGroupItem` を独自の枠 div で囲み `<Label htmlFor>` を兄弟に置いた結果、**選択肢が二重の枠で描画される**崩れが起きていた。実際は `children` がラベルとして描画され内部で `<label>` 相当の関連付けまで行う。文言を「ラベルは children として内側に書く。外側に `<Label htmlFor>` を併置しない」に修正し、「タイルの枠は `RadioGroupItem` 自身が描くので consumer 側で囲まない」を `rules` に追加。`.d.ts` を開かないと分からなかった `description` / `expandedContent` prop も `props` として明記。実装は不変([#134](https://github.com/flashpark/cake-design-system/issues/134))
|
|
85
|
+
- `CLAUDE.md` の「AI 生成コードの典型的な誤り」に上記2件(Button の後退アクション=tertiary、RadioGroupItem を div で囲まない・Label 併置しない)を追加(常時ロードされる導線のため)
|
|
86
|
+
|
|
10
87
|
## [0.12.0] - 2026-07-23
|
|
11
88
|
|
|
12
89
|
consumer(cake-distributor 等)の DS 採用時に、内部 DOM を `[&>div]` 等で上書きして凌いでいた3点の粗の解消と、手組みカルーセル排除のための新コンポーネントを収録する回。
|
package/CLAUDE.md
CHANGED
|
@@ -54,9 +54,14 @@ PdM や社長が AI(Claude 等)で HTML モックを作る際、このパッ
|
|
|
54
54
|
部品が DS に無いと思っても、**手元の台帳・TODO リストで「昇格候補」を管理してはいけない**。台帳は必ず drift する(実例: cake-api の昇格候補台帳が DS 0.10.0 で追加された ImageUploader / ResponsiveDialog / DataTable 等を反映せず、既に DS にある部品を「無い」と誤認してローカル実装を保持し続けた)。次の順で確認する:
|
|
55
55
|
|
|
56
56
|
1. `contracts/components.json` を名前で検索する(正典はここ 1 箇所)。
|
|
57
|
-
2.
|
|
58
|
-
3.
|
|
59
|
-
4.
|
|
57
|
+
2. **名前で出なければ「何をする部品か」で探す。** DS には用途名の部品があり、同じ機能を別の文脈で探すと名前が一致しない(実例: カート数量の `QuantitySelector` が実体は汎用の数値ステッパーだったため、管理画面の価格入力を探した側が「無い」と判断して二重実装を作った)。`props` / `rules` / `features` の中身も含めて検索し、迷ったら `src/components/COMPONENT_LOOKUP.md` の一覧を通しで見る。
|
|
58
|
+
3. `npx cakejp-ds check-duplicates` を実行し、ローカルに DS 同名の実装が無いか機械検出する(`--strict` で検出時 exit 1、consumer CI に組み込める)。**名前が違う二重実装はこれでは検出できない** — 2 を飛ばさないこと。
|
|
59
|
+
4. それでも無ければ、手元に台帳を作らず **flashpark/cake-design-system に issue を立てる**。
|
|
60
|
+
5. やむを得ずローカル実装する場合は、「DS に X が追加されたら削除」の条件コメントを必ず添える。
|
|
61
|
+
|
|
62
|
+
**姉妹 DS(ksk 等)に同名の部品があることは、cake に無い根拠にならない。** 判断は必ず cake の contracts を見て行う。
|
|
63
|
+
|
|
64
|
+
**意図的にローカル実装を残すとき(check-duplicates の抑制)**: 対象 export の直前のコメント行(または行末コメント)に `// cakejp-ds-allow-duplicate: <理由>` を書くと、その 1 件だけ検出から外れる。**理由が空なら抑制されない**(形骸化防止)。抑制した件数と理由はサマリに出る。
|
|
60
65
|
|
|
61
66
|
---
|
|
62
67
|
|
|
@@ -95,9 +100,11 @@ PdM や社長が AI(Claude 等)で HTML モックを作る際、このパッ
|
|
|
95
100
|
- **aria-label**: 日本語で記述 — 英語 aria-label 禁止
|
|
96
101
|
- **input type="date"**: 使用禁止 — 実装済みの正解形 `DatePicker` / `DateTimePicker`(primitives)を使う(内部で Popover と Calendar を DS 意匠で組み済み)。特殊要件のときのみ Popover と Calendar を手組みする
|
|
97
102
|
- **input type="file"**: 使用禁止 — 汎用の添付(PDF/CSV/画像混在)は `FileInput`(foundations)、プレビュー付きの画像管理(並び替え・複数サムネイル)は `ImageUploader`(primitives)を使う
|
|
103
|
+
- **Button の「戻る・キャンセル・閉じる」**: `variant="secondary"` は誤り — **`variant="tertiary"`(グレー)を使う**。Cake DS の `secondary` は Material/Bootstrap/shadcn と違い**ブランドアクセント(薄オレンジ地+オレンジ文字)**。「主 CTA の隣=2番目だから secondary」と序列で選ばず、役割(前進か後退か)で選ぶ。secondary は別方向の“前進”副 CTA 用(例: 主「購入する」/ 副「サンプルを見る」)
|
|
104
|
+
- **RadioGroupItem を div で囲む / Label を併置する**: 禁止 — **ラベルは `children` として内側に書く**(`<RadioGroupItem value="x">ラベル</RadioGroupItem>`)。タイルの枠は `RadioGroupItem` 自身が描くので外側で囲むと**枠が二重**になる。`<Label htmlFor>` を兄弟に置くのもラベル重複で誤り。補足説明は `description`、展開内容は `expandedContent` prop を使う
|
|
98
105
|
- **Tag に onClick**: 禁止 — 押せるものは Chip を使う
|
|
99
106
|
- **選択チップ(単一選択)**: `Chip` に onClick で排他選択を自作しない — 単一選択は `RadioGroup variant="chip"` を使う(Radix のキーボード操作・a11y を維持したまま Chip 意匠で選ばせる)
|
|
100
|
-
-
|
|
101
|
-
-
|
|
107
|
+
- **グレー地の上の Card**: `Section background="subtle"`/`"accent-subtle"` と、地が Surface-Secondary のシェル(`AdminShell` / `WorkspaceShell` / `FunnelShell`)の配下は `--card-surface` が伝播するので、**Card は `surface` 未指定のままで自動的に白**になる(付け忘れ事故が起きない)。DS の `Section` を使わない自前の色地では、親要素に `className="[--card-surface:var(--Surface-Primary)]"` を宣言する(dark にも配慮するなら `dark:[--card-surface:var(--Surface-Secondary)]` を併記)。`Card surface="primary"` の明示指定も従来どおり有効・優先
|
|
108
|
+
- **大見出し**: 通常ページの主見出しは `typo-heading-3xl`(28px)を上限にする。`typo-heading-4xl`(32px)/ `typo-heading-5xl`(40px)は**LP・マーケティング系ヒーローの PC 表示限定**で、SP は `typo-heading-3xl` から段階を上げる。`SectionHeader` では `size="3xl"` に `desktopSize="4xl"` または `desktopSize="5xl"` を加える。手書きの `text-[32px]` / `text-[40px]` 等は禁止
|
|
102
109
|
- **画像・メディア上の操作ボタン**: `rgba(...)` の半透過下地を手組みしない — `--Surface-Overlay-Light`/`--Surface-Overlay-Dark` + `Button variant="overlay"` を使う
|
|
103
110
|
- **SectionHeader が担う「パターン」の手書き**: 禁止 — 次の2パターンは `SectionHeader` を使う。①「タイトル + もっと見る/すべて見るアクション」の並び、②中央寄せの大見出し(ランディング/ガイド系。`align="center"`、eyebrow に Badge 等)。**通常の文書見出し(`<h1>`〜`<h6>` 相当の素の semantic heading)まで SectionHeader に置き換える意味ではない** — 見出し単体なら `typo-heading-*` を付けた素の heading 要素でよい。正典は `contracts/components.json` の SectionHeader `rules`
|
package/MIGRATION.md
CHANGED
|
@@ -2,6 +2,51 @@
|
|
|
2
2
|
|
|
3
3
|
@cakejp/design-system のバージョン間移行ガイド。
|
|
4
4
|
|
|
5
|
+
破壊的変更では `scripts/codemod/template.mjs` から移行スクリプトを作り、
|
|
6
|
+
適用後に `@cakejp/design-system/eslint/deprecated` を有効化して旧exportの残件を確認します。
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 次期リリース — `QuantitySelector` → `NumberInput`
|
|
11
|
+
|
|
12
|
+
### 概要
|
|
13
|
+
|
|
14
|
+
数値ステッパーの名前を**用途名から機能名へ**変更しました。意匠(Figma DS「Adjust」準拠の
|
|
15
|
+
sm=コンパクト型 / md=サークル型)と既存 API は**変更ありません**。
|
|
16
|
+
|
|
17
|
+
`QuantitySelector` は deprecated alias として引き続き動くため、**すぐの対応は不要**です。
|
|
18
|
+
|
|
19
|
+
| 旧 | 新 |
|
|
20
|
+
|---|---|
|
|
21
|
+
| `QuantitySelector` | `NumberInput` |
|
|
22
|
+
| `QuantitySelectorProps` | `NumberInputProps` |
|
|
23
|
+
| `@cakejp/design-system` からの import パス | 変更なし |
|
|
24
|
+
| `data-slot="quantity-selector"` | `data-slot="number-input"` |
|
|
25
|
+
|
|
26
|
+
### なぜ改名したか
|
|
27
|
+
|
|
28
|
+
「カート数量を選ぶもの」という用途名だったため、管理画面の価格・在庫入力を探した側が
|
|
29
|
+
**同じ部品を見つけられず、意匠の違う数値入力を別に作ってしまった**(PR #180)。
|
|
30
|
+
機能名に変えて 1 つに統合しています。今後、数値入力はカート数量・価格・在庫すべて
|
|
31
|
+
`NumberInput` を使ってください。
|
|
32
|
+
|
|
33
|
+
### 追加された機能
|
|
34
|
+
|
|
35
|
+
汎用の数値入力に必要だったものを既存実装に足しています(いずれも任意 prop)。
|
|
36
|
+
|
|
37
|
+
- `step` — 増減幅(既定 1)
|
|
38
|
+
- `formatOptions` / `locale` — 桁区切り・通貨表示(編集中は素の数値)
|
|
39
|
+
- `defaultValue` — 非制御での初期値
|
|
40
|
+
- `label` — 読み上げラベル(既定「数量」。**価格・在庫に使うときは必ず指定**)
|
|
41
|
+
- `inputId` — `FormField` / `FieldLabel` の `htmlFor` と接続
|
|
42
|
+
|
|
43
|
+
### 注意点
|
|
44
|
+
|
|
45
|
+
- 既定の `min=1` / `max=99` は**カート数量向け**のままです。価格・在庫など上限が違う
|
|
46
|
+
用途では `min` / `max` を必ず明示してください
|
|
47
|
+
- `data-slot` を CSS フックにしていた場合は `number-input` に変更が必要です
|
|
48
|
+
(DS の内部スロットを consumer から上書きする運用は元々非推奨です)
|
|
49
|
+
|
|
5
50
|
---
|
|
6
51
|
|
|
7
52
|
## v0.2.x → v0.3.0
|
package/UPDATING.md
CHANGED
|
@@ -107,6 +107,19 @@ DS のリリースサイクル([RELEASE.md](./RELEASE.md))が前提:
|
|
|
107
107
|
|
|
108
108
|
## どうやってアップデートするか(手順)
|
|
109
109
|
|
|
110
|
+
### 0. 非推奨APIの継続検出
|
|
111
|
+
|
|
112
|
+
```js
|
|
113
|
+
import cakeDeprecated from "@cakejp/design-system/eslint/deprecated"
|
|
114
|
+
|
|
115
|
+
export default [{
|
|
116
|
+
plugins: { "cakejp-deprecated": cakeDeprecated },
|
|
117
|
+
rules: { "cakejp-deprecated/no-deprecated": "warn" },
|
|
118
|
+
}]
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
このルールは `contracts/components.json` の `deprecatedAliases` から直接生成されます。
|
|
122
|
+
|
|
110
123
|
### 1. 新バージョンと変更内容を確認
|
|
111
124
|
|
|
112
125
|
```bash
|
package/bin/check-duplicates.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Duplicate-component checker(consumer の二重実装検出)
|
|
4
4
|
*
|
|
5
|
-
* consumer プロジェクトの中に、DS(@cakejp/design-system
|
|
5
|
+
* consumer プロジェクトの中に、DS(@cakejp/design-system)から**実際に import できる**
|
|
6
6
|
* コンポーネントと同名のローカル実装が無いかを走査して知らせる。
|
|
7
7
|
*
|
|
8
8
|
* 背景: consumer が DS 正典(contracts/components.json)とは別に「昇格候補台帳」
|
|
@@ -12,9 +12,25 @@
|
|
|
12
12
|
* このツールは DS 同梱の contracts/components.json を唯一の正典として、
|
|
13
13
|
* consumer のローカル export と**名前の完全一致**で機械検出する。
|
|
14
14
|
*
|
|
15
|
+
* ただし contracts の name には「実装ファイル名・グループ名」(例: CheckboxCard)が
|
|
16
|
+
* 含まれ、それ自体は export されていない。名前だけで照合すると **import できない名前でも
|
|
17
|
+
* 検出される**(issue #140)ため、判定対象は `dist/types/index.d.ts` の実 export と
|
|
18
|
+
* 突き合わせて絞り込む。dist が無い環境(DS リポ自体など)では contracts のみで判定し、
|
|
19
|
+
* その旨を出力に明示する。
|
|
20
|
+
*
|
|
15
21
|
* 使い方:
|
|
16
22
|
* npx cakejp-ds check-duplicates [DIR] # 既定 DIR=./src、助言モード(常に exit 0)
|
|
17
23
|
* npx cakejp-ds check-duplicates ./src --strict # 検出時 exit 1(consumer の CI 組込み用)
|
|
24
|
+
* npx cakejp-ds check-duplicates --help
|
|
25
|
+
*
|
|
26
|
+
* 意図的にローカル実装を残す場合(抑制):
|
|
27
|
+
* 対象 export の**直前のコメント行**(連続コメントブロック内のどこでも可)か
|
|
28
|
+
* **同じ行の行末コメント**に、理由付きで次を書く。
|
|
29
|
+
*
|
|
30
|
+
* // cakejp-ds-allow-duplicate: DS 版は用途が違うため独自実装を残す
|
|
31
|
+
* export function ImageUploader() { ... }
|
|
32
|
+
*
|
|
33
|
+
* 理由が空(`// cakejp-ds-allow-duplicate:` だけ)の場合は抑制しない。
|
|
18
34
|
*
|
|
19
35
|
* 依存追加なし(node 標準 API のみ)。
|
|
20
36
|
*/
|
|
@@ -22,6 +38,7 @@
|
|
|
22
38
|
import fs from "node:fs"
|
|
23
39
|
import path from "node:path"
|
|
24
40
|
import { fileURLToPath } from "node:url"
|
|
41
|
+
import { parseValueExportNames } from "./exports-parser.mjs"
|
|
25
42
|
|
|
26
43
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
27
44
|
const pkgRoot = path.resolve(__dirname, "..")
|
|
@@ -30,6 +47,28 @@ const args = process.argv.slice(2)
|
|
|
30
47
|
const strict = args.includes("--strict")
|
|
31
48
|
const targetDir = args.find((a) => !a.startsWith("-")) || "./src"
|
|
32
49
|
|
|
50
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
51
|
+
console.log(`cakejp-ds check-duplicates — DS に既にある部品の二重実装を検出
|
|
52
|
+
|
|
53
|
+
使い方:
|
|
54
|
+
npx cakejp-ds check-duplicates [DIR] 既定 DIR=./src。助言モード(常に exit 0)
|
|
55
|
+
npx cakejp-ds check-duplicates ./src --strict 検出時 exit 1(CI 組込み用)
|
|
56
|
+
|
|
57
|
+
判定対象:
|
|
58
|
+
contracts/components.json の名前のうち、DS のバレル(dist/types/index.d.ts)から
|
|
59
|
+
実際に import できるものだけ。import できない名前(実装ファイル名・グループ名)は検出しない。
|
|
60
|
+
|
|
61
|
+
意図的なローカル実装の抑制(理由必須):
|
|
62
|
+
対象 export の直前のコメント行、または同じ行の行末コメントに次を書く。
|
|
63
|
+
|
|
64
|
+
// cakejp-ds-allow-duplicate: <残す理由>
|
|
65
|
+
export function ImageUploader() { ... }
|
|
66
|
+
|
|
67
|
+
理由が空の場合は抑制されない(形骸化防止)。抑制した件数はサマリに出る。
|
|
68
|
+
`)
|
|
69
|
+
process.exit(0)
|
|
70
|
+
}
|
|
71
|
+
|
|
33
72
|
// ── DS 正典(contracts/components.json)から全コンポーネント名を収集 ──────────
|
|
34
73
|
const contractsPath = path.join(pkgRoot, "contracts/components.json")
|
|
35
74
|
if (!fs.existsSync(contractsPath)) {
|
|
@@ -43,8 +82,10 @@ const dsComponents = new Map()
|
|
|
43
82
|
const contracts = JSON.parse(fs.readFileSync(contractsPath, "utf-8"))
|
|
44
83
|
for (const tier of ["foundations", "primitives", "ec"]) {
|
|
45
84
|
for (const entry of contracts[tier] || []) {
|
|
46
|
-
|
|
47
|
-
|
|
85
|
+
// exported: false = contracts 上のグループ名(実装ファイル名)で、import できない
|
|
86
|
+
const exportedName = entry.exportedAs || entry.name
|
|
87
|
+
if (entry.exported !== false && exportedName && !dsComponents.has(exportedName)) {
|
|
88
|
+
dsComponents.set(exportedName, { tier, path: entry.path })
|
|
48
89
|
}
|
|
49
90
|
for (const sub of entry.subcomponents || []) {
|
|
50
91
|
if (!dsComponents.has(sub)) {
|
|
@@ -55,6 +96,21 @@ const dsComponents = new Map()
|
|
|
55
96
|
}
|
|
56
97
|
}
|
|
57
98
|
|
|
99
|
+
// ── 実 export(dist/types/index.d.ts)で絞り込む ────────────────────────
|
|
100
|
+
// dist/index.js の動的 import は peer 依存の解決とモジュール評価が必要で consumer 環境で壊れやすい。
|
|
101
|
+
// .d.ts は再 export だけの静的テキストなので、依存も副作用も無く確実に読める。
|
|
102
|
+
const dtsPath = path.join(pkgRoot, "dist/types/index.d.ts")
|
|
103
|
+
let exportSource = null
|
|
104
|
+
if (fs.existsSync(dtsPath)) {
|
|
105
|
+
const realExports = parseValueExportNames(fs.readFileSync(dtsPath, "utf-8"))
|
|
106
|
+
if (realExports.size > 0) {
|
|
107
|
+
for (const name of [...dsComponents.keys()]) {
|
|
108
|
+
if (!realExports.has(name)) dsComponents.delete(name)
|
|
109
|
+
}
|
|
110
|
+
exportSource = "dist/types/index.d.ts"
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
58
114
|
// ── 対象 DIR の走査 ──────────────────────────────────────────────
|
|
59
115
|
const IGNORED_DIRS = new Set(["node_modules", "dist", ".next", "build"])
|
|
60
116
|
|
|
@@ -81,11 +137,51 @@ function findFiles(dir) {
|
|
|
81
137
|
const FN_RE = /export\s+function\s+([A-Z][A-Za-z0-9]*)/
|
|
82
138
|
const CONST_RE = /export\s+const\s+([A-Z][A-Za-z0-9]*)\s*(?::[^=]+)?=/
|
|
83
139
|
|
|
140
|
+
// 意図的残置の抑制マーカー(理由必須)
|
|
141
|
+
const ALLOW_RE = /cakejp-ds-allow-duplicate\s*:(.*)$/
|
|
142
|
+
const COMMENT_LINE_RE = /^\s*(\/\/|\/\*|\*)/
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* 宣言行の直前コメントブロック(または同じ行の行末コメント)から抑制理由を探す。
|
|
146
|
+
* @returns {{ reason: string } | { reason: "" } | null} マーカー無し=null / 理由空=reason:""
|
|
147
|
+
*/
|
|
148
|
+
function findAllowMarker(lines, declIndex) {
|
|
149
|
+
const scan = (line) => {
|
|
150
|
+
const m = line.match(ALLOW_RE)
|
|
151
|
+
if (!m) return null
|
|
152
|
+
const reason = m[1].replace(/\*\/\s*$/, "").trim()
|
|
153
|
+
return { reason }
|
|
154
|
+
}
|
|
155
|
+
// 同じ行の行末コメント
|
|
156
|
+
const own = scan(lines[declIndex])
|
|
157
|
+
if (own) return own
|
|
158
|
+
// 直前の連続コメントブロック
|
|
159
|
+
for (let j = declIndex - 1; j >= 0; j--) {
|
|
160
|
+
const line = lines[j]
|
|
161
|
+
if (!COMMENT_LINE_RE.test(line)) break
|
|
162
|
+
const hit = scan(line)
|
|
163
|
+
if (hit) return hit
|
|
164
|
+
}
|
|
165
|
+
return null
|
|
166
|
+
}
|
|
167
|
+
|
|
84
168
|
console.log(`\n🔍 二重実装チェック`)
|
|
85
169
|
console.log(` 対象: ${path.resolve(targetDir)}`)
|
|
86
|
-
|
|
170
|
+
if (exportSource) {
|
|
171
|
+
console.log(
|
|
172
|
+
` DS 正典: contracts/components.json × 実 export(${exportSource})で照合(${dsComponents.size} 名)\n`
|
|
173
|
+
)
|
|
174
|
+
} else {
|
|
175
|
+
console.log(` DS 正典: contracts/components.json(${dsComponents.size} 名)`)
|
|
176
|
+
console.log(
|
|
177
|
+
` ⚠ dist/types/index.d.ts が無いため実 export と照合できません` +
|
|
178
|
+
`(contracts のみで判定 — import できない名前を誤検出する可能性があります)\n`
|
|
179
|
+
)
|
|
180
|
+
}
|
|
87
181
|
|
|
88
182
|
const findings = []
|
|
183
|
+
const suppressed = []
|
|
184
|
+
const missingReason = []
|
|
89
185
|
|
|
90
186
|
for (const file of findFiles(targetDir)) {
|
|
91
187
|
const lines = fs.readFileSync(file, "utf-8").split(/\r?\n/)
|
|
@@ -95,13 +191,40 @@ for (const file of findFiles(targetDir)) {
|
|
|
95
191
|
if (!m) continue
|
|
96
192
|
const name = m[1]
|
|
97
193
|
const ds = dsComponents.get(name)
|
|
98
|
-
if (ds)
|
|
99
|
-
|
|
194
|
+
if (!ds) continue
|
|
195
|
+
const allow = findAllowMarker(lines, i)
|
|
196
|
+
if (allow && allow.reason) {
|
|
197
|
+
suppressed.push({ name, file, line: i + 1, reason: allow.reason })
|
|
198
|
+
continue
|
|
100
199
|
}
|
|
200
|
+
if (allow) missingReason.push({ name, file, line: i + 1 })
|
|
201
|
+
findings.push({ name, file, line: i + 1, ds })
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const rel = (f) => path.relative(process.cwd(), f)
|
|
206
|
+
|
|
207
|
+
function printSuppressionSummary() {
|
|
208
|
+
if (suppressed.length > 0) {
|
|
209
|
+
console.log(`ℹ ${suppressed.length} 件を抑制(理由コメントあり)`)
|
|
210
|
+
for (const s of suppressed) {
|
|
211
|
+
console.log(` ・${s.name}(${rel(s.file)}:${s.line})— ${s.reason}`)
|
|
212
|
+
}
|
|
213
|
+
console.log("")
|
|
214
|
+
}
|
|
215
|
+
if (missingReason.length > 0) {
|
|
216
|
+
console.log(
|
|
217
|
+
`⚠ 理由が空の抑制コメントが ${missingReason.length} 件あります(理由を書かないと抑制されません)`
|
|
218
|
+
)
|
|
219
|
+
for (const s of missingReason) {
|
|
220
|
+
console.log(` ・${s.name}(${rel(s.file)}:${s.line})`)
|
|
221
|
+
}
|
|
222
|
+
console.log("")
|
|
101
223
|
}
|
|
102
224
|
}
|
|
103
225
|
|
|
104
226
|
if (findings.length === 0) {
|
|
227
|
+
printSuppressionSummary()
|
|
105
228
|
console.log("✓ DS と同名のローカル実装は見つかりませんでした。二重実装なし 🎉\n")
|
|
106
229
|
process.exit(0)
|
|
107
230
|
}
|
|
@@ -109,20 +232,21 @@ if (findings.length === 0) {
|
|
|
109
232
|
console.log(`⚠ DS に既に存在する名前のローカル実装: ${findings.length} 件\n`)
|
|
110
233
|
|
|
111
234
|
for (const f of findings) {
|
|
112
|
-
const rel = path.relative(process.cwd(), f.file)
|
|
113
235
|
const tierLabel = f.ds.sub
|
|
114
236
|
? `${f.ds.tier}(${f.ds.sub} のサブコンポーネント)`
|
|
115
237
|
: f.ds.tier
|
|
116
238
|
console.log(` ● ${f.name}`)
|
|
117
|
-
console.log(` ローカル: ${rel}:${f.line}`)
|
|
239
|
+
console.log(` ローカル: ${rel(f.file)}:${f.line}`)
|
|
118
240
|
console.log(` DS 側: ${tierLabel} / ${f.ds.path}`)
|
|
119
241
|
console.log(
|
|
120
242
|
` → DS の import(@cakejp/design-system)に置き換え検討。` +
|
|
121
|
-
|
|
243
|
+
`意図的なローカル実装なら直前行に「// cakejp-ds-allow-duplicate: 理由」を書くこと。`
|
|
122
244
|
)
|
|
123
245
|
console.log("")
|
|
124
246
|
}
|
|
125
247
|
|
|
248
|
+
printSuppressionSummary()
|
|
249
|
+
|
|
126
250
|
console.log(
|
|
127
251
|
`💡 DS に部品があるか迷ったら、まず contracts/components.json を検索し、` +
|
|
128
252
|
`無ければ手元の台帳ではなく flashpark/cake-design-system に issue を立てること` +
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* バレル(`src/index.ts` / `dist/types/index.d.ts`)から
|
|
3
|
+
* 「値として import できる export 名」を抽出する小さなパーサ。
|
|
4
|
+
*
|
|
5
|
+
* check-duplicates(bin/check-duplicates.mjs)と
|
|
6
|
+
* contracts 整合テスト(__tests__/contracts-export-integrity.test.ts)で共用する。
|
|
7
|
+
*
|
|
8
|
+
* 実 export の取得に**型定義バレルのテキスト解析**を使う理由:
|
|
9
|
+
* - `dist/index.js` の動的 import は react / radix-ui / sonner 等の peer 依存が
|
|
10
|
+
* DS のパス基準で解決できる必要があり、モジュール評価も走るため consumer 環境で壊れやすい
|
|
11
|
+
* - `.d.ts` は再 export だけの静的テキストで、依存も副作用も無い
|
|
12
|
+
*
|
|
13
|
+
* 抽出対象:
|
|
14
|
+
* - `export { A, B as C } from "..."` / `export { A }`(`as` があれば公開名 = 別名側)
|
|
15
|
+
* - `export declare const|function|class X`(バレル直書き宣言)
|
|
16
|
+
* 除外:
|
|
17
|
+
* - `export type { ... }` と specifier 単位の `type X`(値ではない)
|
|
18
|
+
* - `import ...`(`import "./index.css"` 等)
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/** ソースからコメントを除去(文字列中の // は barrel には出てこない前提の簡易版) */
|
|
22
|
+
function stripComments(source) {
|
|
23
|
+
return source.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|\s)\/\/[^\n]*/g, "$1")
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @param {string} source バレルのソース文字列
|
|
28
|
+
* @returns {Set<string>} 値として import できる export 名
|
|
29
|
+
*/
|
|
30
|
+
export function parseValueExportNames(source) {
|
|
31
|
+
const text = stripComments(source)
|
|
32
|
+
const names = new Set()
|
|
33
|
+
|
|
34
|
+
// export { ... } / export type { ... }(複数行可)
|
|
35
|
+
const braceRe = /export\s+(type\s+)?\{([^}]*)\}/g
|
|
36
|
+
let m
|
|
37
|
+
while ((m = braceRe.exec(text)) !== null) {
|
|
38
|
+
if (m[1]) continue // export type { ... } は値ではない
|
|
39
|
+
for (const raw of m[2].split(",")) {
|
|
40
|
+
const spec = raw.trim()
|
|
41
|
+
if (!spec) continue
|
|
42
|
+
if (/^type\s/.test(spec)) continue // { type Foo } は値ではない
|
|
43
|
+
const parts = spec.split(/\s+as\s+/)
|
|
44
|
+
const exported = (parts[1] || parts[0]).trim()
|
|
45
|
+
if (exported && exported !== "default") names.add(exported)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// export (declare) const|function|class X
|
|
50
|
+
const declRe = /export\s+(?:declare\s+)?(?:const|let|var|function|class)\s+([A-Za-z_$][\w$]*)/g
|
|
51
|
+
while ((m = declRe.exec(text)) !== null) names.add(m[1])
|
|
52
|
+
|
|
53
|
+
return names
|
|
54
|
+
}
|
package/bin/init.js
CHANGED
|
@@ -31,6 +31,9 @@ if (cmd === "help" || cmd === "--help" || cmd === "-h") {
|
|
|
31
31
|
npx cakejp-ds codemod v0.2-to-v0.3 [DIR] 旧 API を新 API に自動移行
|
|
32
32
|
npx cakejp-ds check-migration [DIR] 旧 API 利用箇所を検出
|
|
33
33
|
npx cakejp-ds check-duplicates [DIR] DS に既にある部品の二重実装を検出(--strict で検出時 exit 1)
|
|
34
|
+
意図的に残すローカル実装は、対象 export の直前行に
|
|
35
|
+
「// cakejp-ds-allow-duplicate: <理由>」を書くと抑制できる(理由必須)
|
|
36
|
+
詳細: npx cakejp-ds check-duplicates --help
|
|
34
37
|
`)
|
|
35
38
|
process.exit(0)
|
|
36
39
|
}
|