@godxjp/ui 27.8.0 → 27.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/components/data-display/range-timeline.d.ts +19 -0
  2. package/dist/components/data-display/range-timeline.js +2 -0
  3. package/dist/components/data-display/tree.js +2 -0
  4. package/dist/components/data-entry/upload.d.ts +1 -1
  5. package/dist/components/data-entry/upload.js +19 -16
  6. package/dist/components/general/button.d.ts +1 -0
  7. package/dist/components/general/button.js +10 -2
  8. package/dist/components/layout/app-shell.d.ts +1 -1
  9. package/dist/components/layout/app-shell.js +8 -2
  10. package/dist/components/layout/page-container.d.ts +1 -1
  11. package/dist/components/layout/page-container.js +12 -2
  12. package/dist/components/layout/topbar.d.ts +1 -1
  13. package/dist/components/layout/topbar.js +2 -0
  14. package/dist/components/ui/toggle-group.d.ts +4 -0
  15. package/dist/components/ui/toggle-group.js +10 -2
  16. package/dist/components/ui/toggle.d.ts +4 -2
  17. package/dist/components/ui/toggle.js +22 -3
  18. package/dist/contracts/measurement.json +1 -1
  19. package/dist/props/components/data-display.prop.d.ts +11 -0
  20. package/dist/props/components/data-entry.prop.d.ts +22 -3
  21. package/dist/props/components/general.prop.d.ts +12 -0
  22. package/dist/props/components/index.d.ts +1 -1
  23. package/dist/props/components/layout.prop.d.ts +60 -1
  24. package/dist/props/registry.d.ts +27 -6
  25. package/dist/props/registry.js +42 -3
  26. package/dist/styles/control.css +17 -7
  27. package/dist/styles/data-display-layout.css +25 -7
  28. package/dist/styles/data-entry-layout.css +13 -0
  29. package/dist/styles/layout.css +8 -0
  30. package/dist/styles/shell-layout.css +43 -1
  31. package/dist/styles/toggle.css +5 -0
  32. package/dist/tokens/components/control.css +5 -2
  33. package/dist/tokens/components/data-display.css +10 -1
  34. package/dist/tokens/components/shell.css +3 -0
  35. package/dist/tokens/components/toggle.css +7 -0
  36. package/dist/tokens/components/tree.css +2 -0
  37. package/docs/DESIGN-AUTHORITY.md +20 -0
  38. package/docs/FRAME-COVERAGE-REPORT.md +1 -1
  39. package/docs/data-display/timeline.tsx +77 -1
  40. package/docs/data-display/tree.tsx +94 -0
  41. package/docs/data-entry/calendar.tsx +3 -2
  42. package/docs/data-entry/toggle-count.tsx +51 -0
  43. package/docs/data-entry/toggle-group.tsx +40 -2
  44. package/docs/data-entry/toggle.tsx +54 -2
  45. package/docs/data-entry/upload.tsx +44 -0
  46. package/docs/general/button/index.tsx +18 -6
  47. package/docs/general/icon.tsx +3 -1
  48. package/docs/layout/app-shell-narrow-bar.tsx +205 -0
  49. package/docs/layout/app-shell.tsx +10 -0
  50. package/docs/layout/page-container.tsx +67 -1
  51. package/docs/layout/topbar.tsx +11 -1
  52. package/package.json +2 -2
  53. package/scripts/cli.mjs +3 -0
@@ -0,0 +1,205 @@
1
+ import { useState } from "react";
2
+ import { AppShell, Flex, PageContainer, Sidebar, Topbar, TopbarItem } from "@godxjp/ui/layout";
3
+ import type { SidebarSectionProp } from "@godxjp/ui/layout";
4
+ import {
5
+ Avatar,
6
+ AvatarFallback,
7
+ Badge,
8
+ Card,
9
+ CardContent,
10
+ CardDescription,
11
+ CardHeader,
12
+ CardTitle,
13
+ } from "@godxjp/ui/data-display";
14
+ import { Button, Text } from "@godxjp/ui/general";
15
+ import {
16
+ Bell,
17
+ CalendarDays,
18
+ ClipboardList,
19
+ FileText,
20
+ LayoutDashboard,
21
+ MessageSquare,
22
+ Search,
23
+ Settings,
24
+ Users,
25
+ } from "lucide-react";
26
+
27
+ /**
28
+ * AppShell · 390px の細いバー — ブランドの縮退と Topbar の溢れ契約(gh#728)。
29
+ *
30
+ * 再現している形: 6:1 のロゴロックアップ(`--logo-size-sm` で 144x24)+ start 1 セル +
31
+ * end 4 セル + ユーザーセル + AppShell 自身のドロワートリガー。27.8.0 ではこの組み合わせが
32
+ * 390px で破綻していた — brand はどの幅でも 143.7px のまま、`.ui-topbar-start` は width 0、
33
+ * end の最後のセルは x=384.8 w=132.3 で viewport の外に描かれていた。
34
+ *
35
+ * ここで見える契約は 2 つ:
36
+ * 1. `logoCompact` — 狭いバーで `logo` の代わりに描かれる別ノード。別ノードなので `viewBox`
37
+ * ごと差し替えられる(stylesheet では属性を書き換えられない)。渡さなくても brand セル
38
+ * 自体が縮むようになった(`--app-shell-brand-max-inline-size`)。
39
+ * 2. `Topbar overflow="scroll"`(既定)— 足りなくなったらセルを切り落とさず、バー自身が
40
+ * インライン方向にスクロールする。セルは DOM に残るので Tab で到達でき、フォーカスが
41
+ * 当たったセルはブラウザが可視域へ全体を送り込む。
42
+ */
43
+ const SECTIONS: SidebarSectionProp[] = [
44
+ {
45
+ label: "業務",
46
+ items: [
47
+ { id: "dashboard", label: "ダッシュボード", icon: LayoutDashboard },
48
+ { id: "tasks", label: "タスク", icon: ClipboardList },
49
+ { id: "docs", label: "ドキュメント", icon: FileText },
50
+ { id: "members", label: "メンバー", icon: Users },
51
+ ],
52
+ },
53
+ ];
54
+
55
+ /** 6:1 のロックアップ — design が実物の logotype を渡してくる形そのまま(inline `<svg>`)。 */
56
+ function BrandLockup() {
57
+ return (
58
+ <svg
59
+ data-demo-brand="full"
60
+ viewBox="0 0 144 24"
61
+ role="img"
62
+ aria-label="ゴードエックス ワークスペース"
63
+ style={{ blockSize: "var(--logo-size-sm)", inlineSize: "auto" }}
64
+ >
65
+ <rect width="24" height="24" rx="6" fill="currentColor" opacity="0.18" />
66
+ <path d="M7 12h10M12 7v10" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
67
+ <text x="32" y="17" fontSize="14" fontWeight="600" fill="currentColor">
68
+ GoDX Workspace
69
+ </text>
70
+ </svg>
71
+ );
72
+ }
73
+
74
+ /** 同じブランドの「マークだけ」— viewBox が違うので stylesheet では作れないノード。 */
75
+ function BrandMark() {
76
+ return (
77
+ <svg
78
+ data-demo-brand="compact"
79
+ viewBox="0 0 24 24"
80
+ role="img"
81
+ aria-label="ゴードエックス ワークスペース"
82
+ style={{ blockSize: "var(--logo-size-sm)", inlineSize: "auto" }}
83
+ >
84
+ <rect width="24" height="24" rx="6" fill="currentColor" opacity="0.18" />
85
+ <path d="M7 12h10M12 7v10" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
86
+ </svg>
87
+ );
88
+ }
89
+
90
+ export default function Demo() {
91
+ const [activeId, setActiveId] = useState("dashboard");
92
+ const [compactBrand, setCompactBrand] = useState(true);
93
+ const [unread, setUnread] = useState(7);
94
+
95
+ const end = (
96
+ <>
97
+ <TopbarItem aria-label="検索" icon={<Search aria-hidden="true" />} />
98
+ <TopbarItem aria-label="予定" icon={<CalendarDays aria-hidden="true" />} />
99
+ <TopbarItem aria-label="メッセージ" icon={<MessageSquare aria-hidden="true" />} />
100
+ <TopbarItem
101
+ aria-label={`通知 ${String(unread)} 件`}
102
+ badge={unread === 0 ? undefined : unread}
103
+ badgeTone="destructive"
104
+ icon={<Bell aria-hidden="true" />}
105
+ onClick={() => setUnread(0)}
106
+ />
107
+ <TopbarItem aria-label="アカウント">
108
+ <Avatar className="size-7">
109
+ <AvatarFallback>佐藤</AvatarFallback>
110
+ </Avatar>
111
+ </TopbarItem>
112
+ </>
113
+ );
114
+
115
+ return (
116
+ <AppShell
117
+ topbarSpan="full"
118
+ logo={<BrandLockup />}
119
+ logoCompact={compactBrand ? <BrandMark /> : undefined}
120
+ sidebar={<Sidebar activeId={activeId} onSelect={setActiveId} sections={SECTIONS} />}
121
+ topbar={
122
+ <Topbar
123
+ start={
124
+ <TopbarItem
125
+ aria-label="統括"
126
+ icon={<LayoutDashboard aria-hidden="true" />}
127
+ labelHideBelow="md"
128
+ >
129
+ 統括
130
+ </TopbarItem>
131
+ }
132
+ end={end}
133
+ />
134
+ }
135
+ >
136
+ <PageContainer
137
+ title="390px の細いバー"
138
+ subtitle="brand の縮退 · Topbar の溢れ契約(gh#728)"
139
+ breadcrumb={[{ label: "ホーム", to: "/" }, { label: "390px の細いバー" }]}
140
+ >
141
+ <Flex direction="col" gap="lg">
142
+ <Card>
143
+ <CardHeader>
144
+ <CardTitle level={2}>ブランドセルは狭いバーで席を譲る</CardTitle>
145
+ <CardDescription>
146
+ `logoCompact` を渡すと、`logoCompactBelow`(既定 `sm`)の段より下でそのノードが
147
+ `logo` の代わりに描かれる。 渡さない場合でも brand セルは sm 未満で
148
+ `--app-shell-brand-compact-max-inline-size`(=バーの高さ)まで詰められ、 はみ出した
149
+ 分は inline-end 側から落ちる。27.8.0 のように 143.7px
150
+ のまま居座ることはもうない。ボタンで両方を比べられる。
151
+ </CardDescription>
152
+ </CardHeader>
153
+ <CardContent>
154
+ <Flex align="center" gap="md" wrap>
155
+ <Button
156
+ size="sm"
157
+ variant="outline"
158
+ aria-pressed={compactBrand}
159
+ onClick={() => setCompactBrand((value) => !value)}
160
+ >
161
+ <Settings />
162
+ logoCompact: {compactBrand ? "あり" : "なし(既定の縮退のみ)"}
163
+ </Button>
164
+ <Badge tone={compactBrand ? "success" : "info"} icon={null}>
165
+ {compactBrand ? "マークだけの viewBox に差し替え" : "6:1 ロックアップを縮小"}
166
+ </Badge>
167
+ </Flex>
168
+ </CardContent>
169
+ </Card>
170
+
171
+ <Card>
172
+ <CardHeader>
173
+ <CardTitle level={2}>溢れたクラスタは切り落とさずスクロールする</CardTitle>
174
+ <CardDescription>
175
+ `Topbar overflow="scroll"`(既定)。320px ではこのバーはまだ 44px
176
+ 足りない。足りない分は start だけに押し付けられず(end も `flex: 0 1 auto`
177
+ になった)、残りは バー自身が横スクロールで引き受ける — start はセル 1
178
+ つ分の下限を、end はセルの実寸を保つので、 セルが「半分だけ描かれる」ことはない。
179
+ `display: none` にもならないので Tab
180
+ で到達でき、フォーカスの当たったセルはブラウザが可視域へ全体を送り込む。
181
+ `overflow="clip"` で 27.8.0 以前の切り落としに戻せる。
182
+ </CardDescription>
183
+ </CardHeader>
184
+ <CardContent>
185
+ <Flex direction="col" gap="sm" className="text-sm">
186
+ {[
187
+ { prop: "start", desc: "統括セル(md 未満はアイコンのみ · 名前は残る)" },
188
+ { prop: "end", desc: "検索 / 予定 / メッセージ / 通知 + アカウント" },
189
+ { prop: "overflow", desc: '"scroll"(既定)| "clip"' },
190
+ ].map(({ prop, desc }) => (
191
+ <Flex key={prop} align="start" gap="sm">
192
+ <Badge variant="secondary" className="shrink-0">
193
+ {prop}
194
+ </Badge>
195
+ <Text tone="muted">{desc}</Text>
196
+ </Flex>
197
+ ))}
198
+ </Flex>
199
+ </CardContent>
200
+ </Card>
201
+ </Flex>
202
+ </PageContainer>
203
+ </AppShell>
204
+ );
205
+ }
@@ -296,6 +296,16 @@ export default function Demo() {
296
296
  navigation を失わせないため)。空の <code>{"topbar={<></>}"}</code>
297
297
  で代用しないこと ― それは 4 スロットが埋まった状態と同じで、バーの行は残る。
298
298
  </CardDescription>
299
+ <CardDescription>
300
+ ブランドの席も幅で動く(gh#728)。<code>.app-topbar-logo</code> は <code>sm</code>{" "}
301
+ の段より下で <code>--app-shell-brand-compact-max-inline-size</code>
302
+ (=バーの高さ)まで切り詰められ、はみ出した分は inline-end 側から落ちる ―
303
+ どのロックアップもマークが先頭にあるからで、これは package
304
+ が「自分で描いていない絵」にできる唯一のこと。 何を出すかまで選びたいときは{" "}
305
+ <code>logoCompact</code> に別ノードを渡す:
306
+ <code>viewBox</code> は属性なので、どんな stylesheet でも書き換えられない。 実寸は{" "}
307
+ <code>layout/app-shell-narrow-bar</code> のページで確認できる。
308
+ </CardDescription>
299
309
  <CardDescription>
300
310
  横方向のページ余白軸の持ち主は <code>--space-page-x</code> ひとつ。 トップバーは{" "}
301
311
  <code>.app-main</code> と同じグリッド列に載っている ―
@@ -17,7 +17,7 @@ import type { ColumnDef } from "@godxjp/ui/data-display";
17
17
  import { Button, Text } from "@godxjp/ui/general";
18
18
  import { SearchInput } from "@godxjp/ui/data-entry";
19
19
  import { ResponsiveGrid } from "@godxjp/ui/layout";
20
- import { Toolbar, ToolbarGroup } from "@godxjp/ui/navigation";
20
+ import { Pagination, Toolbar, ToolbarGroup } from "@godxjp/ui/navigation";
21
21
  import { Plus, Download, Filter, Search } from "lucide-react";
22
22
 
23
23
  /**
@@ -129,6 +129,9 @@ export default function Demo() {
129
129
  { key: "ghost", label: "ghost" },
130
130
  ] as const;
131
131
 
132
+ /** The record pager that must sit AFTER the header's action cluster (`extra.end`). */
133
+ const [recordPage, setRecordPage] = useState(12);
134
+
132
135
  const densities = [
133
136
  { key: "compact", label: "compact" },
134
137
  { key: "default", label: "default" },
@@ -165,6 +168,69 @@ export default function Demo() {
165
168
  </ResponsiveGrid>
166
169
  </PageContainer>
167
170
 
171
+ {/* ── 1a. `extra` as { start, end } · 本人確認 → 操作 → ページャが最後 ── */}
172
+ {/* `extra` はノード 1 つでも、start / end の 2 スロットでも受ける(Tabs.extra と同じ形)。
173
+ レコード画面の並びは「誰の何か → 操作の束 → ページャ」で、ページャは操作の“あと”に
174
+ 来る。1 スロットしかなかったころは、この最後の一つを本文の中に第二のヘッダ帯として
175
+ 置くしかなく、アプリがヘッダを 2 種類抱えることになっていた。両方とも同じ
176
+ .ui-page-header-extra の直下に並ぶので、DOM 順がそのまま読み上げ順になる。 */}
177
+ <PageContainer
178
+ title="不具合 #1042 · 請求書の消費税区分が誤っている"
179
+ subtitle="Issue detail · 担当 佐藤 · 更新 2026-09-14"
180
+ breadcrumb={[
181
+ { label: "ホーム", to: "/" },
182
+ { label: "不具合", to: "/issues" },
183
+ { label: "#1042" },
184
+ ]}
185
+ breadcrumbLabel="不具合詳細のパンくず"
186
+ extra={{
187
+ start: (
188
+ <Flex gap="sm">
189
+ <Button variant="outline" size="sm">
190
+ <Download />
191
+ エクスポート
192
+ </Button>
193
+ <Button size="sm">
194
+ <Plus />
195
+ コメント
196
+ </Button>
197
+ </Flex>
198
+ ),
199
+ end: (
200
+ <Pagination
201
+ size="sm"
202
+ value={recordPage}
203
+ onValueChange={setRecordPage}
204
+ total={48}
205
+ pageSize={1}
206
+ simple
207
+ ariaLabel="不具合の前後移動"
208
+ />
209
+ ),
210
+ }}
211
+ >
212
+ <Card>
213
+ <CardHeader>
214
+ <CardTitle level={2}>ページャはヘッダの最後に置く</CardTitle>
215
+ <CardDescription>
216
+ extra にノードを 1 つ渡したときは start に入るので、既存のページは 1
217
+ 文字も変わらない。end を足したときだけ、その後ろにもう 1
218
+ スロットが増える。名前は論理方向(start / end)なので dir=&quot;rtl&quot;
219
+ でそのまま入れ替わる。本文の中にもう一段ヘッダ帯を作る必要はない。
220
+ </CardDescription>
221
+ </CardHeader>
222
+ <CardContent>
223
+ <Descriptions
224
+ items={[
225
+ { label: "状態", value: "未対応" },
226
+ { label: "優先度", value: "高" },
227
+ { label: "報告者", value: "鈴木" },
228
+ ]}
229
+ />
230
+ </CardContent>
231
+ </Card>
232
+ </PageContainer>
233
+
168
234
  {/* ── 1b. Canonical page-header contract · status/meta band ── */}
169
235
  {/* PageContainer's embedded header IS the DXS PageHeader: breadcrumbs + title +
170
236
  subtitle + status/meta + actions + responsive overflow on ONE renderer. The
@@ -289,6 +289,10 @@ export default function Demo() {
289
289
  },
290
290
  { prop: "center", desc: "検索トリガー(Button)。コマンドパレットを開く" },
291
291
  { prop: "end", desc: "通知ボタン + ユーザーメニュー(DropdownMenu)" },
292
+ {
293
+ prop: "overflow",
294
+ desc: '"scroll"(既定)| "clip" · 収まらないときバーが横スクロールするか、切り落とすか',
295
+ },
292
296
  { prop: "children", desc: "3 slot を使わず完全カスタムにする場合の逃げ道" },
293
297
  ].map(({ prop, desc }) => (
294
298
  <Flex key={prop} align="start" gap="sm">
@@ -328,7 +332,13 @@ export default function Demo() {
328
332
  <CardTitle level={2}>320px stress · 長いローカライズ内容</CardTitle>
329
333
  <CardDescription>
330
334
  1100px 以下では package-owned contract が center slot を隠し、start の最後の
331
- ラベルを ellipsis で切り詰めて end utilities を保護する。
335
+ ラベルを ellipsis で切り詰めて end utilities を保護する。 それでも足りなくなったら
336
+ バーは<b>切り落とさず横スクロールする</b>(<code>overflow=&quot;scroll&quot;</code>
337
+ が既定 · gh#728): start はセル 1 つ分の下限を、end
338
+ はセルの実寸を保つので、収まらない分は「半分だけ描かれる」のではなく
339
+ 視界の外に出るだけになる。セルは DOM に残るため Tab
340
+ で到達でき、フォーカスが当たるとブラウザが可視域へスクロールする。
341
+ <code>overflow=&quot;clip&quot;</code> で以前の切り落としに戻せる。
332
342
  </CardDescription>
333
343
  </CardHeader>
334
344
  <CardContent>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@godxjp/ui",
3
- "version": "27.8.0",
4
- "godxUiMcp": "27.8.0",
3
+ "version": "27.10.0",
4
+ "godxUiMcp": "27.10.0",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
7
7
  "type": "git",
package/scripts/cli.mjs CHANGED
@@ -36,6 +36,9 @@ const HELP = {
36
36
  It is the postinstall step, run by hand — use it when the app installs with ignore-scripts=true,
37
37
  where postinstall never runs and the rules go stale. Silent no-op in CI and when opted out.
38
38
 
39
+ Restart the session afterwards: rewriting the .mcp.json pin does not relaunch an MCP server that
40
+ is already running, and that process keeps answering from the catalog it started with.
41
+
39
42
  It also READS Claude Code's ~/.claude.json and reports any @godxjp/ui-mcp registered there (user
40
43
  scope, or local scope for this project): key, pin, and the claude mcp command that updates it.
41
44
  That file is outside the project and is never written; env values are never printed.`,