@godxjp/ui 27.8.0 → 27.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/dist/components/data-display/range-timeline.d.ts +19 -0
- package/dist/components/data-display/range-timeline.js +2 -0
- package/dist/components/data-display/tree.js +2 -0
- package/dist/components/data-entry/upload.js +17 -15
- package/dist/components/layout/app-shell.d.ts +1 -1
- package/dist/components/layout/app-shell.js +8 -2
- package/dist/components/layout/topbar.d.ts +1 -1
- package/dist/components/layout/topbar.js +2 -0
- package/dist/contracts/measurement.json +1 -1
- package/dist/props/components/data-display.prop.d.ts +11 -0
- package/dist/props/components/data-entry.prop.d.ts +6 -3
- package/dist/props/components/layout.prop.d.ts +40 -0
- package/dist/props/registry.d.ts +20 -4
- package/dist/props/registry.js +32 -3
- package/dist/styles/control.css +6 -7
- package/dist/styles/data-display-layout.css +25 -7
- package/dist/styles/data-entry-layout.css +13 -0
- package/dist/styles/layout.css +8 -0
- package/dist/styles/shell-layout.css +43 -1
- package/dist/tokens/components/control.css +5 -2
- package/dist/tokens/components/data-display.css +10 -1
- package/dist/tokens/components/shell.css +3 -0
- package/dist/tokens/components/tree.css +2 -0
- package/docs/DESIGN-AUTHORITY.md +20 -0
- package/docs/FRAME-COVERAGE-REPORT.md +1 -1
- package/docs/data-display/timeline.tsx +77 -1
- package/docs/data-display/tree.tsx +94 -0
- package/docs/data-entry/calendar.tsx +3 -2
- package/docs/general/icon.tsx +3 -1
- package/docs/layout/app-shell-narrow-bar.tsx +205 -0
- package/docs/layout/app-shell.tsx +10 -0
- package/docs/layout/topbar.tsx +11 -1
- package/package.json +2 -2
- package/scripts/cli.mjs +3 -0
|
@@ -737,6 +737,28 @@
|
|
|
737
737
|
align-items: center;
|
|
738
738
|
}
|
|
739
739
|
|
|
740
|
+
.app-topbar-logo {
|
|
741
|
+
max-inline-size: var(--app-shell-brand-max-inline-size);
|
|
742
|
+
min-inline-size: 0;
|
|
743
|
+
flex: 0 1 auto;
|
|
744
|
+
overflow: clip;
|
|
745
|
+
overflow-clip-margin: var(--focus-ring-clip-margin);
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
.app-topbar-brand {
|
|
749
|
+
display: contents;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
@media (width < 40rem) {
|
|
753
|
+
.app-topbar-logo {
|
|
754
|
+
max-inline-size: var(--app-shell-brand-compact-max-inline-size);
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
.app-topbar-logo [data-slot="logo-wordmark"] {
|
|
758
|
+
display: none;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
|
|
740
762
|
.app-topbar-custom {
|
|
741
763
|
display: flex;
|
|
742
764
|
min-width: 0;
|
|
@@ -1997,12 +2019,32 @@
|
|
|
1997
2019
|
flex: 1 1 0%;
|
|
1998
2020
|
justify-content: center;
|
|
1999
2021
|
}
|
|
2022
|
+
|
|
2000
2023
|
.ui-topbar-end {
|
|
2001
2024
|
max-width: 100%;
|
|
2002
|
-
flex: 0
|
|
2025
|
+
flex: 0 1 auto;
|
|
2003
2026
|
margin-inline-start: auto;
|
|
2004
2027
|
}
|
|
2005
2028
|
|
|
2029
|
+
.ui-topbar[data-overflow="scroll"] {
|
|
2030
|
+
overflow-x: auto;
|
|
2031
|
+
overscroll-behavior-inline: contain;
|
|
2032
|
+
scrollbar-width: none;
|
|
2033
|
+
}
|
|
2034
|
+
|
|
2035
|
+
.ui-topbar[data-overflow="scroll"]::-webkit-scrollbar {
|
|
2036
|
+
display: none;
|
|
2037
|
+
}
|
|
2038
|
+
|
|
2039
|
+
.ui-topbar[data-overflow="scroll"] > .ui-topbar-end {
|
|
2040
|
+
max-inline-size: none;
|
|
2041
|
+
min-inline-size: auto;
|
|
2042
|
+
}
|
|
2043
|
+
|
|
2044
|
+
.ui-topbar[data-overflow="scroll"] > .ui-topbar-start {
|
|
2045
|
+
min-inline-size: var(--topbar-item-min-width);
|
|
2046
|
+
}
|
|
2047
|
+
|
|
2006
2048
|
.ui-topbar-item {
|
|
2007
2049
|
display: inline-flex;
|
|
2008
2050
|
min-width: var(--topbar-item-min-width);
|
|
@@ -281,14 +281,17 @@
|
|
|
281
281
|
--tree-select-empty-space-block: var(--space-6);
|
|
282
282
|
|
|
283
283
|
--calendar-space-inset: var(--space-3);
|
|
284
|
+
|
|
284
285
|
--calendar-month-space-gap: var(--space-4);
|
|
285
286
|
--calendar-caption-space-inline: var(--space-8);
|
|
286
287
|
--calendar-caption-font-size: var(--font-size-sm);
|
|
287
288
|
--calendar-nav-space-block-start: var(--space-3);
|
|
288
|
-
|
|
289
|
+
|
|
290
|
+
--calendar-nav-space-inline: var(--calendar-space-inset);
|
|
289
291
|
|
|
290
292
|
--calendar-nav-rest-alpha: 0.7;
|
|
291
|
-
|
|
293
|
+
|
|
294
|
+
--calendar-grid-space-block-start: var(--space-3);
|
|
292
295
|
--calendar-week-space-block-start: var(--space-2);
|
|
293
296
|
--calendar-weekday-font-size: var(--font-size-xs);
|
|
294
297
|
--calendar-weekday-radius: var(--radius-md);
|
|
@@ -132,9 +132,12 @@
|
|
|
132
132
|
--thumbnail-radius: var(--radius);
|
|
133
133
|
|
|
134
134
|
--timeline-dot-done-background: initial;
|
|
135
|
+
--timeline-dot-done-foreground: initial;
|
|
135
136
|
--timeline-dot-current-background: initial;
|
|
136
137
|
--timeline-line-completed-background: initial;
|
|
137
138
|
|
|
139
|
+
--timeline-dot-current-ring-width: var(--stroke-xl);
|
|
140
|
+
|
|
138
141
|
--qr-code-foreground: 0 0% 0%;
|
|
139
142
|
--qr-code-background: 0 0% 100%;
|
|
140
143
|
--qr-code-size-xs: 6rem;
|
|
@@ -173,9 +176,15 @@
|
|
|
173
176
|
|
|
174
177
|
:root {
|
|
175
178
|
--range-timeline-label-width: var(--app-shell-sidebar-width);
|
|
176
|
-
|
|
179
|
+
|
|
180
|
+
--range-timeline-unit-width: var(--range-timeline-unit-width-default);
|
|
181
|
+
|
|
182
|
+
--range-timeline-unit-width-compact: calc(var(--control-height-sm) * 1.5);
|
|
183
|
+
--range-timeline-unit-width-default: calc(var(--control-height-sm) * 2);
|
|
184
|
+
--range-timeline-unit-width-comfortable: calc(var(--control-height-sm) * 2.5);
|
|
177
185
|
--range-timeline-row-height: var(--control-height-lg);
|
|
178
186
|
--range-timeline-bar-height: var(--control-height-sm);
|
|
187
|
+
|
|
179
188
|
--range-timeline-border-color: var(--border);
|
|
180
189
|
|
|
181
190
|
--range-timeline-grid-color: initial;
|
|
@@ -43,6 +43,9 @@
|
|
|
43
43
|
--app-shell-bar-inset-compact: var(--space-page-compact-x);
|
|
44
44
|
--app-shell-bar-gap: var(--space-3);
|
|
45
45
|
|
|
46
|
+
--app-shell-brand-max-inline-size: none;
|
|
47
|
+
--app-shell-brand-compact-max-inline-size: var(--app-shell-bar-height);
|
|
48
|
+
|
|
46
49
|
--app-shell-sidebar-width: 16rem;
|
|
47
50
|
--app-shell-sidebar-collapsed-width: 4rem;
|
|
48
51
|
|
package/docs/DESIGN-AUTHORITY.md
CHANGED
|
@@ -298,6 +298,26 @@ bottomEnd | none` (`TablePaginationPositionProp`, default `['bottomEnd']`), the
|
|
|
298
298
|
purpose: omitted, `flow="columns"` keeps `start` and `flow="rows"` keeps the grid's stretch, so
|
|
299
299
|
no existing grid moves; antd's `Row` default is `top` in every case.
|
|
300
300
|
|
|
301
|
+
- **`Tree.divided` — a new capability, named from this library, not from antd (gh#732).** antd's
|
|
302
|
+
`Tree` has no horizontal rule between nodes at all (only `showLine`, the VERTICAL parent/child
|
|
303
|
+
rail), so there is no antd spelling to port and nothing to be out of parity with. The name was
|
|
304
|
+
picked from the two the library already uses for the idea, and they are not interchangeable here:
|
|
305
|
+
**`bordered` means a GRID** — `Table` / `DataTable` "outer frame + vertical rules between
|
|
306
|
+
columns", `Descriptions` "bordered table with shaded label cells", `Calendar` "rule the day
|
|
307
|
+
grid", `RangeTimeline` "row rules, header rule, a vertical rule per column" — while the
|
|
308
|
+
horizontal hairline BETWEEN rows of a vertical list is called a **divider** everywhere it exists
|
|
309
|
+
(`--list-row-border` on `ListRow`, `--command-item-divider-color`, `--table-flush-divider-*`,
|
|
310
|
+
`--card-*-divider`). A `Tree` has no columns and no frame, so `bordered` would promise chrome the
|
|
311
|
+
prop does not draw and would collide with a real Tree frame later. `Command.split` is the same
|
|
312
|
+
hairline, but that name is antd `List.split` adopted verbatim (gh#699) and carries a second
|
|
313
|
+
meaning there — zero list padding, rows bled to the panel edge — which `Tree` must not inherit.
|
|
314
|
+
So: `divided`, matching the token `--tree-divider-color`, a positive boolean adjective like
|
|
315
|
+
`striped` (gh#700). It **defaults to `false`**, unlike `RangeTimeline bordered`: a Gantt body is
|
|
316
|
+
unreadable without a grid, whereas `Tree`'s documented chrome default is quiet (cardinal rule
|
|
317
|
+
#44 — `showLine` ships off for the same reason, and the token file states it), and default-on
|
|
318
|
+
would rule every existing tree, including the picker-sized and `size="xs"` outlines where a rule
|
|
319
|
+
every 24px is noise rather than structure. A tree used as page navigation opts in.
|
|
320
|
+
|
|
301
321
|
- **`RangeTimeline` nested rows take `Tree`'s spelling, not antd's (gh#724).** antd has no Gantt;
|
|
302
322
|
the nearest names are `Tree expandedKeys` / `defaultExpandedKeys` / `onExpand` and `Table
|
|
303
323
|
expandable.expandedRowKeys` / `onExpandedRowsChange`. This library already mapped the Tree ones
|
|
@@ -45,7 +45,7 @@ Legend: ✓ covered · N/A reasoned skip · · UNTESTED (blank cell = UNTESTED).
|
|
|
45
45
|
| CardBar | **✗** | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
46
46
|
| TimeRangePicker | ✓ | · | · | · | · | · | · | · | · | · |
|
|
47
47
|
| VisuallyHidden | **✗** | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
48
|
-
| RangeTimeline | **✗** |
|
|
48
|
+
| RangeTimeline | **✗** | · | N/A | N/A | · | · | · | · | · | · |
|
|
49
49
|
| PageContainer | ✓ | ✓ | N/A | N/A | · | · | · | · | · | · |
|
|
50
50
|
| Flex | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
51
51
|
| ResponsiveGrid | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { CSSProperties } from "react";
|
|
2
|
+
|
|
1
3
|
import { useTranslation } from "@godxjp/ui/i18n";
|
|
2
4
|
import {
|
|
3
5
|
Card,
|
|
@@ -142,7 +144,10 @@ export default function Demo() {
|
|
|
142
144
|
<CardTitle level={2}>承認・消費税トラッカー</CardTitle>
|
|
143
145
|
<CardDescription>
|
|
144
146
|
variant="status" は status ごとにグリフを切り替えます(done → チェック、current →
|
|
145
|
-
塗りつぶしドット、pending →
|
|
147
|
+
塗りつぶしドット、pending → 連番)。進捗レールの色は1つだけ:
|
|
148
|
+
done・current・通過済みの線 はすべて --primary で塗られ、current
|
|
149
|
+
はリング(--timeline-dot-current-ring-width)と グリフで区別します。Steps の finish /
|
|
150
|
+
process と同じ約束です。
|
|
146
151
|
</CardDescription>
|
|
147
152
|
</CardHeader>
|
|
148
153
|
<CardContent>
|
|
@@ -163,6 +168,31 @@ export default function Demo() {
|
|
|
163
168
|
</CardContent>
|
|
164
169
|
</Card>
|
|
165
170
|
|
|
171
|
+
<Card>
|
|
172
|
+
<CardHeader>
|
|
173
|
+
<CardTitle level={2}>進捗レールの色をテーマで戻す</CardTitle>
|
|
174
|
+
<CardDescription>
|
|
175
|
+
done の緑と current の紫という 27.8 までの配色に戻したい場合は、塗りとインクを
|
|
176
|
+
対にしてテーマで指定します。塗りだけを戻すと、若竹の緑にほぼ白のグリフが乗って 2.18:1
|
|
177
|
+
になり AA を満たしません(gh#643)。同じ要領で --timeline-dot-current-background と
|
|
178
|
+
--timeline-line-completed-background も再着色できます。
|
|
179
|
+
</CardDescription>
|
|
180
|
+
</CardHeader>
|
|
181
|
+
{/* The knobs are declared on the CARD BODY, not on the Timeline: custom properties
|
|
182
|
+
inherit, so one scope retints every Timeline inside it — which is how a service
|
|
183
|
+
theme sets them (once, globally, or under `[data-tenant]`), not per call site. */}
|
|
184
|
+
<CardContent
|
|
185
|
+
style={
|
|
186
|
+
{
|
|
187
|
+
"--timeline-dot-done-background": "hsl(var(--success))",
|
|
188
|
+
"--timeline-dot-done-foreground": "hsl(var(--success-foreground))",
|
|
189
|
+
} as CSSProperties
|
|
190
|
+
}
|
|
191
|
+
>
|
|
192
|
+
<Timeline variant="status" items={approvalItems} />
|
|
193
|
+
</CardContent>
|
|
194
|
+
</Card>
|
|
195
|
+
|
|
166
196
|
<Card>
|
|
167
197
|
<CardHeader>
|
|
168
198
|
<CardTitle level={2}>RangeTimeline</CardTitle>
|
|
@@ -236,6 +266,52 @@ export default function Demo() {
|
|
|
236
266
|
</CardContent>
|
|
237
267
|
</Card>
|
|
238
268
|
|
|
269
|
+
<Card>
|
|
270
|
+
<CardHeader>
|
|
271
|
+
<CardTitle level={2}>密度(density)— 1日の列幅</CardTitle>
|
|
272
|
+
<CardDescription>
|
|
273
|
+
同じ31日分の軸を density で三段。compact は1日42px、default
|
|
274
|
+
は従来どおり56px、comfortable は70px です。動くのは列幅だけで、行の高さもバーの高さも
|
|
275
|
+
ラベル列の幅も変わりません。画面に入る日数を増やしたいときは compact
|
|
276
|
+
を選びます(app.css でトークンを書く必要はありません)。
|
|
277
|
+
</CardDescription>
|
|
278
|
+
</CardHeader>
|
|
279
|
+
<CardContent>
|
|
280
|
+
<Flex direction="col" gap="md">
|
|
281
|
+
{(["compact", "default", "comfortable"] as const).map((density) => (
|
|
282
|
+
<div key={density}>
|
|
283
|
+
<Text size="xs" tone="muted">
|
|
284
|
+
density="{density}"
|
|
285
|
+
</Text>
|
|
286
|
+
<RangeTimeline
|
|
287
|
+
label={t("rangeTimeline.schedule")}
|
|
288
|
+
density={density}
|
|
289
|
+
today={8}
|
|
290
|
+
bands={[{ label: monthLabel(8), units: 31 }]}
|
|
291
|
+
columns={Array.from({ length: 31 }, (_, index) => {
|
|
292
|
+
const weekday = new Date(Date.UTC(2026, 8, index + 1)).getUTCDay();
|
|
293
|
+
return {
|
|
294
|
+
label: new Intl.NumberFormat(locale).format(index + 1),
|
|
295
|
+
units: 1,
|
|
296
|
+
muted: weekday === 0 || weekday === 6,
|
|
297
|
+
};
|
|
298
|
+
})}
|
|
299
|
+
rows={[
|
|
300
|
+
{ id: "design", label: "要件定義", start: 0, end: 6 },
|
|
301
|
+
{ id: "build", label: "実装", start: 7, end: 22 },
|
|
302
|
+
{ id: "review", label: "レビュー", start: 20, end: 29 },
|
|
303
|
+
].map((row) => ({
|
|
304
|
+
...row,
|
|
305
|
+
startLabel: dayLabel(row.start + 1),
|
|
306
|
+
endLabel: dayLabel(row.end + 1),
|
|
307
|
+
}))}
|
|
308
|
+
/>
|
|
309
|
+
</div>
|
|
310
|
+
))}
|
|
311
|
+
</Flex>
|
|
312
|
+
</CardContent>
|
|
313
|
+
</Card>
|
|
314
|
+
|
|
239
315
|
<Card>
|
|
240
316
|
<CardHeader>
|
|
241
317
|
<CardTitle level={2}>月単位の軸(列ごとに units が異なる)</CardTitle>
|
|
@@ -100,6 +100,48 @@ const fileDetails: Record<string, { size: string; changed: string }> = {
|
|
|
100
100
|
"docs/tree.mdx": { size: "4.1 KB", changed: "2026-09-10" },
|
|
101
101
|
};
|
|
102
102
|
|
|
103
|
+
// ── the page index used as NAVIGATION inside a Card (`divided`, gh#732) ──────────────────────
|
|
104
|
+
const wikiPages: TreeNodeProp[] = [
|
|
105
|
+
{
|
|
106
|
+
value: "handbook",
|
|
107
|
+
label: "社内ハンドブック",
|
|
108
|
+
children: [
|
|
109
|
+
{ value: "handbook/onboarding", label: "入社手続き", isLeaf: true },
|
|
110
|
+
{ value: "handbook/expenses", label: "経費精算", isLeaf: true },
|
|
111
|
+
{
|
|
112
|
+
value: "handbook/security",
|
|
113
|
+
label: "情報セキュリティ",
|
|
114
|
+
children: [
|
|
115
|
+
{ value: "handbook/security/passwords", label: "パスワード規程", isLeaf: true },
|
|
116
|
+
{ value: "handbook/security/incidents", label: "インシデント報告", isLeaf: true },
|
|
117
|
+
],
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
value: "product",
|
|
123
|
+
label: "プロダクト仕様",
|
|
124
|
+
children: [
|
|
125
|
+
{ value: "product/release", label: "リリース手順", isLeaf: true },
|
|
126
|
+
{ value: "product/support", label: "サポート窓口", isLeaf: true },
|
|
127
|
+
],
|
|
128
|
+
},
|
|
129
|
+
{ value: "minutes", label: "議事録", isLeaf: true },
|
|
130
|
+
];
|
|
131
|
+
|
|
132
|
+
const wikiPageDetails: Record<string, { updated: string; author: string }> = {
|
|
133
|
+
handbook: { updated: "2026-09-01", author: "総務部" },
|
|
134
|
+
"handbook/onboarding": { updated: "2026-08-18", author: "人事部" },
|
|
135
|
+
"handbook/expenses": { updated: "2026-07-30", author: "経理部" },
|
|
136
|
+
"handbook/security": { updated: "2026-09-11", author: "情報システム部" },
|
|
137
|
+
"handbook/security/passwords": { updated: "2026-09-11", author: "情報システム部" },
|
|
138
|
+
"handbook/security/incidents": { updated: "2026-06-02", author: "情報システム部" },
|
|
139
|
+
product: { updated: "2026-09-14", author: "プロダクト部" },
|
|
140
|
+
"product/release": { updated: "2026-09-14", author: "プロダクト部" },
|
|
141
|
+
"product/support": { updated: "2026-05-20", author: "CS部" },
|
|
142
|
+
minutes: { updated: "2026-09-16", author: "総務部" },
|
|
143
|
+
};
|
|
144
|
+
|
|
103
145
|
// ── card 3 · async branches (fieldNames + loadData + titleRender) ────────────────────────────
|
|
104
146
|
type Department = { id: string; name: string; leaf?: boolean; units?: Department[] };
|
|
105
147
|
|
|
@@ -130,6 +172,8 @@ export default function Demo() {
|
|
|
130
172
|
const [granted, setGranted] = React.useState<string[]>(["billing.invoice.read"]);
|
|
131
173
|
// Card 2 — selection is controlled and drives the detail pane.
|
|
132
174
|
const [openFile, setOpenFile] = React.useState<string | undefined>("src/components/tree.tsx");
|
|
175
|
+
// The page index — the tree IS the navigation, so selection is what the reading pane follows.
|
|
176
|
+
const [openPage, setOpenPage] = React.useState<string | undefined>("handbook/security/passwords");
|
|
133
177
|
// Card 3 — async children land in state, exactly as a real fetch would.
|
|
134
178
|
const [departments, setDepartments] = React.useState<Department[]>(initialDepartments);
|
|
135
179
|
// Card 5 — independent (strict) checks, and a multi-select tree.
|
|
@@ -239,6 +283,56 @@ export default function Demo() {
|
|
|
239
283
|
</CardContent>
|
|
240
284
|
</Card>
|
|
241
285
|
|
|
286
|
+
<Card>
|
|
287
|
+
<CardHeader>
|
|
288
|
+
<CardTitle level={2}>ページ一覧をナビゲーションに使う(divided)</CardTitle>
|
|
289
|
+
<CardDescription>
|
|
290
|
+
ドキュメント/wiki のページ一覧のように、ツリーそのものが画面のナビゲーションになる
|
|
291
|
+
ときは divided で行間に罫を引きます。罫は各行の block-start に 1
|
|
292
|
+
本ずつ、外側の最初の行にだけ引かれません。 深さに関係なく行の幅いっぱいに走るので
|
|
293
|
+
(インデントは行の padding であって margin ではありません)、 子行が「表の中の表」に
|
|
294
|
+
見えることはありません。色は --tree-divider-color(既定 hsl(var(--border)))。 divided
|
|
295
|
+
は既定オフで、このページの他のカードがその既定の姿です。
|
|
296
|
+
</CardDescription>
|
|
297
|
+
</CardHeader>
|
|
298
|
+
<CardContent>
|
|
299
|
+
<Flex gap="lg" wrap>
|
|
300
|
+
<Card className="w-72">
|
|
301
|
+
<CardHeader>
|
|
302
|
+
<CardTitle level={3}>ページ一覧</CardTitle>
|
|
303
|
+
</CardHeader>
|
|
304
|
+
<CardContent flush>
|
|
305
|
+
<Tree
|
|
306
|
+
aria-label="ページ一覧"
|
|
307
|
+
divided
|
|
308
|
+
treeData={wikiPages}
|
|
309
|
+
defaultExpandAll
|
|
310
|
+
value={openPage}
|
|
311
|
+
onValueChange={(next) => setOpenPage(next as string | undefined)}
|
|
312
|
+
/>
|
|
313
|
+
</CardContent>
|
|
314
|
+
</Card>
|
|
315
|
+
<Flex direction="col" gap="sm">
|
|
316
|
+
{openPage && wikiPageDetails[openPage] ? (
|
|
317
|
+
<Descriptions
|
|
318
|
+
items={[
|
|
319
|
+
{ label: "パス", children: openPage },
|
|
320
|
+
{ label: "最終更新", children: wikiPageDetails[openPage].updated },
|
|
321
|
+
{ label: "更新者", children: wikiPageDetails[openPage].author },
|
|
322
|
+
]}
|
|
323
|
+
/>
|
|
324
|
+
) : (
|
|
325
|
+
<EmptyState
|
|
326
|
+
icon={FolderTree}
|
|
327
|
+
title="ページが選択されていません"
|
|
328
|
+
description="左の一覧からページを選んでください。"
|
|
329
|
+
/>
|
|
330
|
+
)}
|
|
331
|
+
</Flex>
|
|
332
|
+
</Flex>
|
|
333
|
+
</CardContent>
|
|
334
|
+
</Card>
|
|
335
|
+
|
|
242
336
|
<Card>
|
|
243
337
|
<CardHeader>
|
|
244
338
|
<CardTitle level={2}>組織ツリー(loadData · fieldNames · titleRender)</CardTitle>
|
|
@@ -77,8 +77,9 @@ export default function Demo() {
|
|
|
77
77
|
罫線 (bordered) · 既定で表示 / bordered={"{false}"} で解除
|
|
78
78
|
</CardTitle>
|
|
79
79
|
<CardDescription>
|
|
80
|
-
日と日の間に罫線を引くのが既定。線色は --calendar-grid-border-color (既定 --
|
|
81
|
-
|
|
80
|
+
日と日の間に罫線を引くのが既定。線色は --calendar-grid-border-color (既定 --border、
|
|
81
|
+
背景に対し約 1.15:1)。表の行やカードの枠と同じ段なので、罫線が枠より濃くなることは
|
|
82
|
+
ない。選択日・今日・範囲の始点/中間/終点・無効日・前後月の日が
|
|
82
83
|
罫線の中でも判別できる。罫線なしの浮いたボタン表示にしたい場合のみ bordered=
|
|
83
84
|
{"{false}"} を渡す。
|
|
84
85
|
</CardDescription>
|
package/docs/general/icon.tsx
CHANGED
|
@@ -183,7 +183,9 @@ export default function Demo() {
|
|
|
183
183
|
|
|
184
184
|
<Flex direction="col" gap="xs">
|
|
185
185
|
<Heading level={3}>Button の中 · 明示した段が Button の既定に勝つ</Heading>
|
|
186
|
-
|
|
186
|
+
{/* 320px では 2 つのラベル付き Button が 1 行に収まらない。Button はラベル幅より
|
|
187
|
+
縮まないので、行を折り返させるのは並べた側の責任(`wrap`)。 */}
|
|
188
|
+
<Flex direction="row" gap="sm" align="center" wrap>
|
|
187
189
|
<Button size="sm">
|
|
188
190
|
<Icon as={Download} size="sm" />
|
|
189
191
|
書き出す
|
|
@@ -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> と同じグリッド列に載っている ―
|