@godxjp/ui 19.2.0 → 19.4.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/badge.d.ts +26 -2
- package/dist/components/data-display/badge.js +20 -5
- package/dist/components/data-display/index.d.ts +2 -0
- package/dist/components/data-display/index.js +2 -0
- package/dist/components/data-display/popover.d.ts +13 -1
- package/dist/components/data-display/popover.js +3 -1
- package/dist/components/data-display/table.d.ts +19 -2
- package/dist/components/data-display/table.js +4 -1
- package/dist/components/data-display/timeline-grid.d.ts +18 -0
- package/dist/components/data-display/timeline-grid.js +222 -0
- package/dist/components/data-entry/cascader.js +4 -6
- package/dist/components/data-entry/checkbox.js +1 -1
- package/dist/components/data-entry/search-select.js +5 -7
- package/dist/components/data-entry/select.js +32 -28
- package/dist/components/data-entry/tree-select.js +4 -6
- package/dist/components/general/inert-background.d.ts +5 -0
- package/dist/components/general/inert-background.js +48 -0
- package/dist/components/layout/auth-shell.d.ts +4 -2
- package/dist/components/layout/auth-shell.js +7 -1
- package/dist/components/layout/centered-shell.d.ts +2 -2
- package/dist/components/layout/flex.d.ts +1 -1
- package/dist/components/layout/flex.js +2 -1
- package/dist/components/layout/index.d.ts +3 -0
- package/dist/components/layout/index.js +2 -0
- package/dist/components/layout/mobile-shell.d.ts +21 -0
- package/dist/components/layout/mobile-shell.js +43 -0
- package/dist/components/layout/sidebar-link.d.ts +1 -1
- package/dist/components/navigation/context-menu.js +13 -9
- package/dist/components/navigation/dropdown-menu.js +17 -13
- package/dist/inertia/index.d.ts +1 -1
- package/dist/lib/variants.d.ts +3 -3
- package/dist/lib/variants.js +1 -0
- package/dist/props/components/data-display.prop.d.ts +72 -0
- package/dist/props/components/data-entry.prop.d.ts +1 -1
- package/dist/props/components/layout.prop.d.ts +93 -6
- package/dist/props/registry.d.ts +113 -2
- package/dist/props/registry.js +155 -1
- package/dist/props/vocabulary/data.prop.d.ts +6 -0
- package/dist/props/vocabulary/index.d.ts +3 -3
- package/dist/props/vocabulary/layout.prop.d.ts +17 -2
- package/dist/props/vocabulary/shared.prop.d.ts +7 -0
- package/dist/styles/badge-layout.css +15 -0
- package/dist/styles/control.css +18 -1
- package/dist/styles/data-display-layout.css +167 -0
- package/dist/styles/layout.css +7 -0
- package/dist/styles/shell-layout.css +148 -0
- package/dist/styles/table-layout.css +23 -4
- package/dist/tokens/components/badge.css +8 -0
- package/dist/tokens/components/control.css +1 -0
- package/dist/tokens/components/data-display.css +20 -0
- package/dist/tokens/components/shell.css +22 -0
- package/dist/tokens/components/table.css +2 -0
- package/docs/SPACING.md +19 -6
- package/docs/charts/compact-bar-trend.tsx +5 -6
- package/docs/data-display/avatar.tsx +7 -9
- package/docs/data-display/badge.tsx +37 -1
- package/docs/data-display/card/index.tsx +6 -4
- package/docs/data-display/carousel.tsx +8 -4
- package/docs/data-display/data-table/examples/approval-queue.tsx +1 -1
- package/docs/data-display/data-table/examples/grid-features.tsx +1 -1
- package/docs/data-display/data-table/index.tsx +1 -1
- package/docs/data-display/permission-matrix.tsx +15 -11
- package/docs/data-display/popover.tsx +2 -1
- package/docs/data-display/scroll-area.tsx +108 -76
- package/docs/data-display/table.tsx +2 -2
- package/docs/data-display/timeline-grid.tsx +119 -0
- package/docs/data-entry/branch-scope-picker.tsx +1 -1
- package/docs/data-entry/calendar.tsx +1 -1
- package/docs/data-entry/checkbox.tsx +1 -1
- package/docs/data-entry/country-picker-recipe.tsx +11 -6
- package/docs/data-entry/form-field/index.tsx +33 -29
- package/docs/data-entry/form-root.tsx +1 -1
- package/docs/data-entry/input-otp.tsx +1 -1
- package/docs/data-entry/password-input.tsx +1 -5
- package/docs/data-entry/select.tsx +7 -7
- package/docs/data-entry/textarea.tsx +3 -3
- package/docs/data-entry/toggle-count.tsx +1 -1
- package/docs/feedback/alert.tsx +1 -1
- package/docs/feedback/banner.tsx +1 -1
- package/docs/feedback/sheet.tsx +112 -102
- package/docs/feedback/skeleton.tsx +2 -2
- package/docs/foundation/colors.tsx +8 -6
- package/docs/foundation/email-tokens.tsx +83 -66
- package/docs/foundation/radius-shadow.tsx +13 -11
- package/docs/general/activity.tsx +1 -1
- package/docs/layout/app-shell.tsx +2 -2
- package/docs/layout/aspect-ratio.tsx +30 -14
- package/docs/layout/auth-account-summary.tsx +1 -1
- package/docs/layout/auth-recovery/examples/mobile-390.tsx +1 -1
- package/docs/layout/auth-recovery/examples/password-recovery.tsx +0 -2
- package/docs/layout/auth-shell-context.tsx +1 -1
- package/docs/layout/auth-shell-device.tsx +2 -2
- package/docs/layout/auth-shell-registration.tsx +1 -1
- package/docs/layout/auth-shell.tsx +1 -1
- package/docs/layout/flex.tsx +6 -2
- package/docs/layout/master-detail.tsx +2 -2
- package/docs/layout/mobile-shell.tsx +101 -0
- package/docs/layout/page-container.tsx +15 -7
- package/docs/layout/responsive-grid.tsx +12 -4
- package/docs/layout/separator.tsx +2 -2
- package/docs/layout/service-role-panel.tsx +1 -1
- package/docs/layout/sidebar.tsx +175 -152
- package/docs/layout/split-pane.tsx +45 -28
- package/docs/layout/topbar.tsx +19 -23
- package/docs/navigation/app-setting-picker.tsx +5 -4
- package/docs/navigation/breadcrumb.tsx +12 -5
- package/docs/navigation/context-menu.tsx +10 -4
- package/docs/navigation/filter-bar.tsx +2 -2
- package/docs/navigation/navigation-menu.tsx +89 -96
- package/docs/navigation/pagination.tsx +2 -2
- package/docs/navigation/steps.tsx +13 -11
- package/docs/navigation/tabs.tsx +27 -21
- package/docs/navigation/toolbar.tsx +15 -15
- package/docs/providers/format-date.tsx +2 -2
- package/docs/showcase/acme-portal.tsx +41 -28
- package/docs/showcase/acme-website.tsx +67 -61
- package/docs/showcase/case1-warehouse-dashboard.tsx +46 -31
- package/docs/showcase/case2-employee-me.tsx +33 -36
- package/docs/showcase/case3-approval-workflow.tsx +34 -32
- package/docs/showcase/case4-login.tsx +95 -101
- package/docs/showcase/case5-shift-calendar.tsx +182 -281
- package/docs/showcase/case6-agency-handy.tsx +514 -470
- package/docs/showcase/futurelastic-web.tsx +32 -29
- package/docs/showcase/org-switcher.tsx +20 -13
- package/docs/showcase/permission-matrix.tsx +20 -17
- package/docs/showcase/public-landing.tsx +1 -1
- package/docs/showcase/settings-account-sections.tsx +2 -2
- package/docs/showcase/settings-security-mfa.tsx +3 -3
- package/docs/showcase/table-approval-queue.tsx +1 -1
- package/docs/showcase/table-bulk-actions.tsx +49 -52
- package/docs/showcase/table-conditional-format.tsx +9 -9
- package/docs/showcase/table-crud-list.tsx +4 -4
- package/docs/showcase/table-density.tsx +49 -42
- package/docs/showcase/table-expandable-rows.tsx +112 -100
- package/docs/showcase/table-filter-chips.tsx +78 -71
- package/docs/showcase/table-footer-totals.tsx +30 -23
- package/docs/showcase/table-grouped-subtotals.tsx +31 -25
- package/docs/showcase/table-master-detail.tsx +27 -24
- package/docs/showcase/table-pagination.tsx +34 -27
- package/docs/showcase/table-states.tsx +42 -34
- package/docs/showcase/table-sticky-columns.tsx +11 -7
- package/docs/showcase/table-tree-rows.tsx +22 -15
- package/docs/showcase/table-view-tabs.tsx +12 -14
- package/package.json +7 -7
|
@@ -83,6 +83,7 @@ const THEME = `
|
|
|
83
83
|
[data-tenant="futurelastic"] .fl-lead { font-size: 1.25rem; line-height: 1.65; color: hsl(var(--muted-foreground)); max-width: 620px; }
|
|
84
84
|
[data-tenant="futurelastic"] .fl-note { font-size: 0.9375rem; color: hsl(var(--muted-foreground)); margin-top: 2.5rem; }
|
|
85
85
|
[data-tenant="futurelastic"] .fl-head { max-width: 640px; margin-inline: auto; text-align: center; }
|
|
86
|
+
[data-tenant="futurelastic"] .fl-hero-actions { margin-top: 1.75rem; }
|
|
86
87
|
[data-tenant="futurelastic"] .fl-cta-inner { position: relative; max-width: 680px; margin-inline: auto; }
|
|
87
88
|
[data-tenant="futurelastic"] .fl-footer-tagline { max-width: 260px; }
|
|
88
89
|
[data-tenant="futurelastic"] .fl-logocloud { display: flex; flex-wrap: wrap; justify-content: center; gap: 1rem 2.5rem; }
|
|
@@ -121,7 +122,7 @@ function Navbar() {
|
|
|
121
122
|
<span className="fl-brand">
|
|
122
123
|
futur<span className="fl-gold">elastic</span>
|
|
123
124
|
</span>
|
|
124
|
-
<Flex direction="row" gap="xs" align="center"
|
|
125
|
+
<Flex direction="row" gap="xs" align="center">
|
|
125
126
|
{links.map((l, i) => (
|
|
126
127
|
<Button key={l} variant="ghost" size="sm" aria-current={i === 0 ? "page" : undefined}>
|
|
127
128
|
{l}
|
|
@@ -154,7 +155,7 @@ function Hero() {
|
|
|
154
155
|
Futurelastic là pháp nhân holding của nhóm công ty công nghệ & AI vận hành xuyên Nhật
|
|
155
156
|
Bản và Việt Nam, từ logistics, fintech đến F&B và thương mại.
|
|
156
157
|
</p>
|
|
157
|
-
<Flex direction="row" gap="md" className="
|
|
158
|
+
<Flex direction="row" gap="md" className="fl-hero-actions">
|
|
158
159
|
<Button size="lg">Khám phá hệ sinh thái</Button>
|
|
159
160
|
<Button variant="outline" size="lg">
|
|
160
161
|
Liên hệ hợp tác
|
|
@@ -176,8 +177,8 @@ function LogoCloud() {
|
|
|
176
177
|
];
|
|
177
178
|
return (
|
|
178
179
|
<section className="fl-section-tint">
|
|
179
|
-
<
|
|
180
|
-
<Text as="p" size="xs" tone="muted" className="
|
|
180
|
+
<Flex direction="col" gap="lg" className={SHELL}>
|
|
181
|
+
<Text as="p" size="xs" tone="muted" className="text-center tracking-widest uppercase">
|
|
181
182
|
Các pháp nhân vận hành dưới Futurelastic
|
|
182
183
|
</Text>
|
|
183
184
|
<div className="fl-logocloud">
|
|
@@ -187,7 +188,7 @@ function LogoCloud() {
|
|
|
187
188
|
</span>
|
|
188
189
|
))}
|
|
189
190
|
</div>
|
|
190
|
-
</
|
|
191
|
+
</Flex>
|
|
191
192
|
</section>
|
|
192
193
|
);
|
|
193
194
|
}
|
|
@@ -235,8 +236,8 @@ function Bento() {
|
|
|
235
236
|
];
|
|
236
237
|
return (
|
|
237
238
|
<section className="fl-section" id="ventures">
|
|
238
|
-
<
|
|
239
|
-
<div className="fl-head
|
|
239
|
+
<Flex direction="col" gap="xl" className={SHELL}>
|
|
240
|
+
<div className="fl-head">
|
|
240
241
|
<div className="fl-eyebrow">Hệ sinh thái</div>
|
|
241
242
|
<h2 className="fl-h2">Một mái nhà, nhiều mũi nhọn</h2>
|
|
242
243
|
<p className="fl-lead mx-auto">
|
|
@@ -247,18 +248,20 @@ function Bento() {
|
|
|
247
248
|
{cells.map((c) => (
|
|
248
249
|
<Card key={c.title} className={`fl-cell ${c.span ?? ""}`}>
|
|
249
250
|
<CardContent>
|
|
250
|
-
<
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
251
|
+
<Flex direction="col" gap="lg">
|
|
252
|
+
<Medallion icon={c.icon} />
|
|
253
|
+
<Text as="div" weight="bold" size="lg">
|
|
254
|
+
{c.title}
|
|
255
|
+
</Text>
|
|
256
|
+
<Text as="p" size="sm" tone="muted">
|
|
257
|
+
{c.text}
|
|
258
|
+
</Text>
|
|
259
|
+
</Flex>
|
|
257
260
|
</CardContent>
|
|
258
261
|
</Card>
|
|
259
262
|
))}
|
|
260
263
|
</div>
|
|
261
|
-
</
|
|
264
|
+
</Flex>
|
|
262
265
|
</section>
|
|
263
266
|
);
|
|
264
267
|
}
|
|
@@ -275,12 +278,12 @@ function Stats() {
|
|
|
275
278
|
<div className={SHELL}>
|
|
276
279
|
<div className="fl-stats">
|
|
277
280
|
{stats.map(([n, l]) => (
|
|
278
|
-
<
|
|
281
|
+
<Flex key={l} direction="col" gap="sm" className="text-center">
|
|
279
282
|
<div className="fl-stat-num">{n}</div>
|
|
280
|
-
<Text as="div" size="sm" tone="muted"
|
|
283
|
+
<Text as="div" size="sm" tone="muted">
|
|
281
284
|
{l}
|
|
282
285
|
</Text>
|
|
283
|
-
</
|
|
286
|
+
</Flex>
|
|
284
287
|
))}
|
|
285
288
|
</div>
|
|
286
289
|
</div>
|
|
@@ -294,20 +297,20 @@ function Cta() {
|
|
|
294
297
|
<div className={SHELL}>
|
|
295
298
|
<div className="fl-cta">
|
|
296
299
|
<div className="fl-cta-glow" />
|
|
297
|
-
<
|
|
300
|
+
<Flex direction="col" gap="xl" className="fl-cta-inner">
|
|
298
301
|
<h2 className="fl-h2">Cùng xây điều mới?</h2>
|
|
299
|
-
<p className="fl-lead mx-auto
|
|
302
|
+
<p className="fl-lead mx-auto">
|
|
300
303
|
Chúng tôi tìm đối tác, nhân tài và cơ hội M&A trong công nghệ, logistics và
|
|
301
304
|
F&B tại Nhật Bản & Việt Nam.
|
|
302
305
|
</p>
|
|
303
|
-
<Flex direction="row" gap="md" className="
|
|
306
|
+
<Flex direction="row" gap="md" className="justify-center">
|
|
304
307
|
<Button size="lg">Đặt lịch trao đổi</Button>
|
|
305
308
|
<Button variant="ghost" size="lg">
|
|
306
309
|
Đọc blog
|
|
307
310
|
<ArrowRight aria-hidden="true" />
|
|
308
311
|
</Button>
|
|
309
312
|
</Flex>
|
|
310
|
-
</
|
|
313
|
+
</Flex>
|
|
311
314
|
</div>
|
|
312
315
|
</div>
|
|
313
316
|
</section>
|
|
@@ -323,26 +326,26 @@ function Footer() {
|
|
|
323
326
|
return (
|
|
324
327
|
<footer className="fl-footer">
|
|
325
328
|
<div className={`${SHELL} fl-footer-grid`}>
|
|
326
|
-
<
|
|
329
|
+
<Flex direction="col" gap="md">
|
|
327
330
|
<span className="fl-brand">
|
|
328
331
|
futur<span className="fl-gold">elastic</span>
|
|
329
332
|
</span>
|
|
330
|
-
<Text as="p" size="sm" tone="muted" className="fl-footer-tagline
|
|
333
|
+
<Text as="p" size="sm" tone="muted" className="fl-footer-tagline">
|
|
331
334
|
Tech · AI · Holdings · building the elastic infrastructure of the future across Japan
|
|
332
335
|
& Vietnam.
|
|
333
336
|
</Text>
|
|
334
|
-
</
|
|
337
|
+
</Flex>
|
|
335
338
|
{cols.map(([h, items]) => (
|
|
336
|
-
<
|
|
339
|
+
<Flex direction="col" gap="md" key={h}>
|
|
337
340
|
<Text
|
|
338
341
|
as="div"
|
|
339
342
|
size="2xs"
|
|
340
343
|
weight="bold"
|
|
341
|
-
className="text-foreground
|
|
344
|
+
className="text-foreground tracking-widest uppercase"
|
|
342
345
|
>
|
|
343
346
|
{h}
|
|
344
347
|
</Text>
|
|
345
|
-
<Flex direction="col" gap="xs"
|
|
348
|
+
<Flex direction="col" gap="xs">
|
|
346
349
|
{items.map((i) => (
|
|
347
350
|
<Button
|
|
348
351
|
key={i}
|
|
@@ -354,7 +357,7 @@ function Footer() {
|
|
|
354
357
|
</Button>
|
|
355
358
|
))}
|
|
356
359
|
</Flex>
|
|
357
|
-
</
|
|
360
|
+
</Flex>
|
|
358
361
|
))}
|
|
359
362
|
</div>
|
|
360
363
|
<div className={`${SHELL} fl-footer-bottom`}>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Showcase · OrgSwitcher — sidebar organization/tenant switcher (Slack/Linear pattern)
|
|
3
3
|
*
|
|
4
|
-
* Requested by dxs-platform
|
|
4
|
+
* Requested by dxs-platform as a "first-class OrgSwitcher component".
|
|
5
5
|
* GATE 0 — Framework-Component Test verdict: **COMPOSITION PATTERN**, not a framework
|
|
6
6
|
* component. It owns NO new behaviour — search/keyboard/filtering come from `Command`
|
|
7
7
|
* (cmdk), open/focus-trap/dismiss from `Popover`, the trigger from `Button` + `Avatar`.
|
|
@@ -44,6 +44,7 @@ import {
|
|
|
44
44
|
AppShell,
|
|
45
45
|
Flex,
|
|
46
46
|
PageContainer,
|
|
47
|
+
Separator,
|
|
47
48
|
Sidebar,
|
|
48
49
|
type SidebarSectionProp,
|
|
49
50
|
Topbar,
|
|
@@ -116,25 +117,29 @@ function OrgSwitcher({ organizations, currentId, onSelect, onCreate, onJoin }: O
|
|
|
116
117
|
type="button"
|
|
117
118
|
variant="ghost"
|
|
118
119
|
aria-label={`Tổ chức hiện tại: ${current.name}. Nhấn để đổi tổ chức`}
|
|
119
|
-
className="w-full justify-between
|
|
120
|
+
className="w-full justify-between"
|
|
120
121
|
>
|
|
121
|
-
<
|
|
122
|
+
{/* Inside a Button, i.e. inside a <button>, whose content model is phrasing content
|
|
123
|
+
only — so every Flex on this branch renders as a <span> (gh#354). */}
|
|
124
|
+
<Flex as="span" align="center" gap="sm" className="min-w-0">
|
|
122
125
|
<Avatar className="size-7 rounded-md">
|
|
123
126
|
<AvatarFallback>{monogram(current.name)}</AvatarFallback>
|
|
124
127
|
</Avatar>
|
|
125
|
-
<span className="
|
|
128
|
+
<Flex as="span" direction="col" gap="none" className="min-w-0 text-start">
|
|
126
129
|
<Text as="span" size="sm" weight="medium" truncate>
|
|
127
130
|
{current.name}
|
|
128
131
|
</Text>
|
|
129
132
|
<Text as="span" size="xs" tone="muted" truncate>
|
|
130
133
|
{current.role}
|
|
131
134
|
</Text>
|
|
132
|
-
</
|
|
135
|
+
</Flex>
|
|
133
136
|
</Flex>
|
|
134
137
|
<ChevronsUpDown aria-hidden className="size-4 shrink-0 opacity-60" />
|
|
135
138
|
</Button>
|
|
136
139
|
</PopoverTrigger>
|
|
137
|
-
|
|
140
|
+
{/* `flush` — the Command list owns its own inset, so its rows and separators run edge to
|
|
141
|
+
edge inside the popover instead of being indented by the panel padding. */}
|
|
142
|
+
<PopoverContent flush align="start" sideOffset={6} className="w-64">
|
|
138
143
|
<Command label="Chọn tổ chức">
|
|
139
144
|
<CommandInput
|
|
140
145
|
placeholder="Tìm tổ chức…"
|
|
@@ -154,18 +159,18 @@ function OrgSwitcher({ organizations, currentId, onSelect, onCreate, onJoin }: O
|
|
|
154
159
|
keywords={[org.id]}
|
|
155
160
|
onSelect={() => choose(org.id)}
|
|
156
161
|
>
|
|
157
|
-
<Flex align="center" gap="sm" className="w-full min-w-0">
|
|
162
|
+
<Flex as="span" align="center" gap="sm" className="w-full min-w-0">
|
|
158
163
|
<Avatar className="size-6 rounded">
|
|
159
164
|
<AvatarFallback>{monogram(org.name)}</AvatarFallback>
|
|
160
165
|
</Avatar>
|
|
161
|
-
<span className="
|
|
166
|
+
<Flex as="span" direction="col" gap="none" className="min-w-0 text-start">
|
|
162
167
|
<Text as="span" size="sm" truncate>
|
|
163
168
|
{org.name}
|
|
164
169
|
</Text>
|
|
165
170
|
<Text as="span" size="xs" tone="muted" truncate>
|
|
166
171
|
{org.role}
|
|
167
172
|
</Text>
|
|
168
|
-
</
|
|
173
|
+
</Flex>
|
|
169
174
|
{active ? (
|
|
170
175
|
<Check aria-hidden className="text-primary ms-auto size-4 shrink-0" />
|
|
171
176
|
) : null}
|
|
@@ -176,12 +181,14 @@ function OrgSwitcher({ organizations, currentId, onSelect, onCreate, onJoin }: O
|
|
|
176
181
|
})}
|
|
177
182
|
</CommandGroup>
|
|
178
183
|
</CommandList>
|
|
179
|
-
<
|
|
184
|
+
<Separator />
|
|
185
|
+
{/* Menu-style footer: the two actions read as one list, so no seam between them. */}
|
|
186
|
+
<Flex direction="col" gap="none">
|
|
180
187
|
<Button
|
|
181
188
|
type="button"
|
|
182
189
|
variant="ghost"
|
|
183
190
|
size="sm"
|
|
184
|
-
className="w-full justify-start
|
|
191
|
+
className="w-full justify-start"
|
|
185
192
|
onClick={() => {
|
|
186
193
|
onCreate();
|
|
187
194
|
setOpen(false);
|
|
@@ -194,7 +201,7 @@ function OrgSwitcher({ organizations, currentId, onSelect, onCreate, onJoin }: O
|
|
|
194
201
|
type="button"
|
|
195
202
|
variant="ghost"
|
|
196
203
|
size="sm"
|
|
197
|
-
className="w-full justify-start
|
|
204
|
+
className="w-full justify-start"
|
|
198
205
|
onClick={() => {
|
|
199
206
|
onJoin();
|
|
200
207
|
setOpen(false);
|
|
@@ -203,7 +210,7 @@ function OrgSwitcher({ organizations, currentId, onSelect, onCreate, onJoin }: O
|
|
|
203
210
|
<TicketPlus aria-hidden className="size-4" />
|
|
204
211
|
Tham gia bằng mã mời
|
|
205
212
|
</Button>
|
|
206
|
-
</
|
|
213
|
+
</Flex>
|
|
207
214
|
</Command>
|
|
208
215
|
</PopoverContent>
|
|
209
216
|
</Popover>
|
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
* A role × permission matrix with a STICKY first column (権限), ✓/— cells, a
|
|
5
5
|
* two-role COMPARE mode, and a 差分のみ (differences-only) filter — the pattern
|
|
6
6
|
* DXS role tabs (SCR-203) and the org role editor (SCR-111b) each hand-rolled
|
|
7
|
-
|
|
7
|
+
*. Read-only by default.
|
|
8
8
|
*
|
|
9
|
-
* SINCE
|
|
9
|
+
* SINCE the grid itself IS a package export — `PermissionMatrix` from
|
|
10
10
|
* `@godxjp/ui/data-display` (see docs/data-display/permission-matrix.tsx). The
|
|
11
11
|
* original Gate-0 "composition pattern" verdict was OUTCOME-corrected the same
|
|
12
|
-
* way ErrorSurface was
|
|
12
|
+
* way ErrorSurface was: a consumer cannot import a docs page, so the
|
|
13
13
|
* canonical grid needed an importable home. Prefer the export in app code.
|
|
14
14
|
*
|
|
15
15
|
* This showcase remains as the COMPOSED form of the same screen — the full
|
|
@@ -34,6 +34,7 @@ import { Check, Minus } from "lucide-react";
|
|
|
34
34
|
import {
|
|
35
35
|
Badge,
|
|
36
36
|
Card,
|
|
37
|
+
CardAction,
|
|
37
38
|
CardContent,
|
|
38
39
|
CardHeader,
|
|
39
40
|
CardTitle,
|
|
@@ -128,10 +129,10 @@ function GrantCell({ granted }: { granted: boolean }) {
|
|
|
128
129
|
<span className="sr-only">許可</span>
|
|
129
130
|
</Badge>
|
|
130
131
|
) : (
|
|
131
|
-
<
|
|
132
|
+
<Flex align="center" justify="center" gap="none" className="text-muted-foreground">
|
|
132
133
|
<Minus aria-hidden="true" className="size-4" />
|
|
133
134
|
<span className="sr-only">不許可</span>
|
|
134
|
-
</
|
|
135
|
+
</Flex>
|
|
135
136
|
);
|
|
136
137
|
}
|
|
137
138
|
|
|
@@ -217,11 +218,13 @@ export default function Demo() {
|
|
|
217
218
|
|
|
218
219
|
{/* ── マトリクス本体 ── */}
|
|
219
220
|
<Card>
|
|
220
|
-
<CardHeader
|
|
221
|
+
<CardHeader>
|
|
221
222
|
<CardTitle level={2}>ロール × 権限</CardTitle>
|
|
222
|
-
<
|
|
223
|
-
|
|
224
|
-
|
|
223
|
+
<CardAction>
|
|
224
|
+
<Text size="xs" tone="muted" tabular>
|
|
225
|
+
権限 {rows.length} / {PERMISSIONS.length} 件 · ロール {ROLES.length}
|
|
226
|
+
</Text>
|
|
227
|
+
</CardAction>
|
|
225
228
|
</CardHeader>
|
|
226
229
|
<CardContent flush>
|
|
227
230
|
<div className="overflow-x-auto">
|
|
@@ -237,19 +240,19 @@ export default function Demo() {
|
|
|
237
240
|
className={cn("text-center", isCompared && "bg-primary/[0.06]")}
|
|
238
241
|
aria-label={isCompared ? `${role.name}(比較対象)` : role.name}
|
|
239
242
|
>
|
|
240
|
-
<
|
|
241
|
-
<
|
|
243
|
+
<Flex direction="col" align="center" gap="none" className="leading-tight">
|
|
244
|
+
<Flex align="center" gap="xs">
|
|
242
245
|
<Text weight="medium">{role.name}</Text>
|
|
243
246
|
{isCompared && (
|
|
244
247
|
<Badge tone="primary" variant="outline">
|
|
245
248
|
{role.id === compareA ? "A" : "B"}
|
|
246
249
|
</Badge>
|
|
247
250
|
)}
|
|
248
|
-
</
|
|
251
|
+
</Flex>
|
|
249
252
|
<Text size="2xs" tone="muted">
|
|
250
253
|
{role.hint} · {countGrants(PERMISSIONS, GRANTS, role.id)}件
|
|
251
254
|
</Text>
|
|
252
|
-
</
|
|
255
|
+
</Flex>
|
|
253
256
|
</TableHead>
|
|
254
257
|
);
|
|
255
258
|
})}
|
|
@@ -267,14 +270,14 @@ export default function Demo() {
|
|
|
267
270
|
>
|
|
268
271
|
<TableCell className={cn(PIN_START, "w-64 min-w-64 align-middle")}>
|
|
269
272
|
<Flex direction="col" className="leading-tight">
|
|
270
|
-
<
|
|
273
|
+
<Flex align="center" gap="xs">
|
|
271
274
|
<Text weight="medium">{perm.name}</Text>
|
|
272
275
|
{isDiffRow && (
|
|
273
276
|
<Badge tone="warning" variant="outline">
|
|
274
277
|
差分
|
|
275
278
|
</Badge>
|
|
276
279
|
)}
|
|
277
|
-
</
|
|
280
|
+
</Flex>
|
|
278
281
|
<Text size="2xs" tone="muted">
|
|
279
282
|
{perm.category} · {perm.description}
|
|
280
283
|
</Text>
|
|
@@ -319,9 +322,9 @@ export default function Demo() {
|
|
|
319
322
|
</Text>
|
|
320
323
|
</Flex>
|
|
321
324
|
<Flex direction="row" align="center" gap="xs">
|
|
322
|
-
<
|
|
325
|
+
<Flex align="center" gap="none" className="text-muted-foreground">
|
|
323
326
|
<Minus aria-hidden="true" className="size-4" />
|
|
324
|
-
</
|
|
327
|
+
</Flex>
|
|
325
328
|
<Text size="xs" tone="muted">
|
|
326
329
|
不許可
|
|
327
330
|
</Text>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Showcase · Public landing
|
|
2
|
+
* Showcase · Public landing — the PUBLIC marketing/product landing page composed from
|
|
3
3
|
* real @godxjp/ui primitives with **zero page-local CSS**.
|
|
4
4
|
*
|
|
5
5
|
* Gate 0 (docs/COMPOSITION-VS-COMPONENT.md): a landing header / hero / section grid / legal footer
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Showcase · settings-account-sections — the canonical SETTINGS SECTION / ROW composition
|
|
2
|
+
* Showcase · settings-account-sections — the canonical SETTINGS SECTION / ROW composition
|
|
3
3
|
*
|
|
4
4
|
* ── Gate 0 verdict: COMPOSITION PATTERN, not a framework component ────────────────────────────
|
|
5
5
|
* A "SettingsSection" / "SettingsRow" / "DangerZone" fails the Framework-Component Test
|
|
@@ -477,7 +477,7 @@ export default function SettingsAccountSectionsShowcase() {
|
|
|
477
477
|
</Descriptions>
|
|
478
478
|
</CardContent>
|
|
479
479
|
<CardFooter separated>
|
|
480
|
-
<Flex direction="col" gap="
|
|
480
|
+
<Flex direction="col" gap="md" className="sm:flex-row sm:items-center">
|
|
481
481
|
<Button variant="outline" asChild>
|
|
482
482
|
<a href="https://billing.example.com/portal" target="_blank" rel="noreferrer">
|
|
483
483
|
<ExternalLink aria-hidden="true" />
|
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
* ✗ wrong (hand-rolled) ✓ here (rule)
|
|
8
8
|
* ─────────────────────────────────────────────────────────────────────────────────────────
|
|
9
9
|
* factor row = a centered <div> with the ListRow — leading icon · title/description ·
|
|
10
|
-
* title duplicated as its own description
|
|
10
|
+
* title duplicated as its own description trailing action (no redundant copy)
|
|
11
11
|
* "Xoá" rendered as plain black text destructive-toned ghost button — delete is a
|
|
12
12
|
* destructive action, color carries intent
|
|
13
13
|
* every action a full-width stacked bar right-aligned, auto-width actions; ONE primary
|
|
14
14
|
* (rule 217 explicitly forbids this) per card, secondary = outline (rule 217)
|
|
15
15
|
* 📱 / ✉️ emoji as "icons" lucide icons (no emoji in product UI) (anti-tell)
|
|
16
|
-
* brand glyph = a styled <span> (size/radius
|
|
16
|
+
* brand glyph = a styled <span> (size/radius Avatar primitive — real brand mark
|
|
17
17
|
* baked as literals)
|
|
18
18
|
*
|
|
19
19
|
* Built ENTIRELY from real @godxjp/ui primitives. reference-design DNA: small headings, 14/1.7 body,
|
|
@@ -130,7 +130,7 @@ export default function SecurityMfaShowcase() {
|
|
|
130
130
|
</Text>
|
|
131
131
|
</Flex>
|
|
132
132
|
</CardContent>
|
|
133
|
-
<CardFooter separated className="justify-end
|
|
133
|
+
<CardFooter separated className="justify-end">
|
|
134
134
|
<Button variant="ghost" size="sm">
|
|
135
135
|
Tải xuống
|
|
136
136
|
</Button>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Showcase · アクセス承認キュー / Access approval queue
|
|
2
|
+
* Showcase · アクセス承認キュー / Access approval queue — the canonical five-column
|
|
3
3
|
* approval collection at 1440 · 1024 · 390 using ONLY public API.
|
|
4
4
|
*
|
|
5
5
|
* The whole responsive contract is `Table preset="action-collection"` plus a `priority` on each
|
|
@@ -202,61 +202,58 @@ function BulkActionBar({
|
|
|
202
202
|
}) {
|
|
203
203
|
const effective = spanAll ? total : count;
|
|
204
204
|
return (
|
|
205
|
-
<
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
>
|
|
213
|
-
{/* left: count + cross-page select-all escalation + safe batch actions */}
|
|
214
|
-
<Flex direction="row" align="center" gap="sm" className="min-w-0 flex-1">
|
|
215
|
-
<Text tone="primary" weight="medium" tabular className="whitespace-nowrap">
|
|
216
|
-
{effective}件を選択中
|
|
217
|
-
</Text>
|
|
218
|
-
|
|
219
|
-
{/* cross-page select-all: only offered once the whole page is selected */}
|
|
220
|
-
{pageAllSelected &&
|
|
221
|
-
(spanAll ? (
|
|
222
|
-
<Text size="xs" tone="muted">
|
|
223
|
-
フィルタ条件の全{" "}
|
|
224
|
-
<Text as="span" tabular>
|
|
225
|
-
{total}
|
|
226
|
-
</Text>{" "}
|
|
227
|
-
件を選択しました
|
|
205
|
+
<Card className="bg-primary/5 border-primary/30">
|
|
206
|
+
<CardContent solo>
|
|
207
|
+
<Flex role="region" aria-label="一括操作" wrap align="center" gap="md">
|
|
208
|
+
{/* left: count + cross-page select-all escalation + safe batch actions */}
|
|
209
|
+
<Flex direction="row" align="center" gap="sm" className="min-w-0 flex-1">
|
|
210
|
+
<Text tone="primary" weight="medium" tabular className="whitespace-nowrap">
|
|
211
|
+
{effective}件を選択中
|
|
228
212
|
</Text>
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
213
|
+
|
|
214
|
+
{/* cross-page select-all: only offered once the whole page is selected */}
|
|
215
|
+
{pageAllSelected &&
|
|
216
|
+
(spanAll ? (
|
|
217
|
+
<Text size="xs" tone="muted">
|
|
218
|
+
フィルタ条件の全{" "}
|
|
219
|
+
<Text as="span" tabular>
|
|
220
|
+
{total}
|
|
221
|
+
</Text>{" "}
|
|
222
|
+
件を選択しました
|
|
223
|
+
</Text>
|
|
224
|
+
) : (
|
|
225
|
+
<Button variant="link" size="sm" onClick={onSelectSpanAll}>
|
|
226
|
+
全 <span className="tabular-nums">{total}</span> 件を選択
|
|
227
|
+
</Button>
|
|
228
|
+
))}
|
|
229
|
+
|
|
230
|
+
<span className="bg-border h-4 w-px" aria-hidden="true" />
|
|
231
|
+
|
|
232
|
+
<Button variant="outline" size="sm" onClick={onApprove}>
|
|
233
|
+
<CheckCheck aria-hidden="true" />
|
|
234
|
+
一括承認
|
|
232
235
|
</Button>
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
<CheckCheck aria-hidden="true" />
|
|
239
|
-
一括承認
|
|
240
|
-
</Button>
|
|
241
|
-
<Button variant="outline" size="sm" onClick={onExport}>
|
|
242
|
-
<Download aria-hidden="true" />
|
|
243
|
-
CSV出力
|
|
244
|
-
</Button>
|
|
245
|
-
</Flex>
|
|
236
|
+
<Button variant="outline" size="sm" onClick={onExport}>
|
|
237
|
+
<Download aria-hidden="true" />
|
|
238
|
+
CSV出力
|
|
239
|
+
</Button>
|
|
240
|
+
</Flex>
|
|
246
241
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
242
|
+
{/* right: ISOLATED destructive action, kept apart from the safe ones */}
|
|
243
|
+
<Flex direction="row" align="center" gap="sm">
|
|
244
|
+
<Button variant="destructive" size="sm" onClick={onDelete}>
|
|
245
|
+
<Trash2 aria-hidden="true" />
|
|
246
|
+
一括削除
|
|
247
|
+
</Button>
|
|
248
|
+
<span className="bg-border h-4 w-px" aria-hidden="true" />
|
|
249
|
+
<Button variant="ghost" size="sm" onClick={onClear} aria-label="選択を解除">
|
|
250
|
+
<X aria-hidden="true" />
|
|
251
|
+
解除
|
|
252
|
+
</Button>
|
|
253
|
+
</Flex>
|
|
254
|
+
</Flex>
|
|
255
|
+
</CardContent>
|
|
256
|
+
</Card>
|
|
260
257
|
);
|
|
261
258
|
}
|
|
262
259
|
|
|
@@ -205,11 +205,11 @@ export default function Demo() {
|
|
|
205
205
|
sortable: true,
|
|
206
206
|
render: (r) =>
|
|
207
207
|
isEarlyCell(r) ? (
|
|
208
|
-
<
|
|
208
|
+
<Badge tone="warning" shape="sharp">
|
|
209
209
|
<Text as="span" weight="medium" tabular style={{ color: "inherit" }}>
|
|
210
210
|
{hours(r.earlyLeaveHours)}
|
|
211
211
|
</Text>
|
|
212
|
-
</
|
|
212
|
+
</Badge>
|
|
213
213
|
) : (
|
|
214
214
|
<Text tabular>{hours(r.earlyLeaveHours)}</Text>
|
|
215
215
|
),
|
|
@@ -221,11 +221,11 @@ export default function Demo() {
|
|
|
221
221
|
sortable: true,
|
|
222
222
|
render: (r) =>
|
|
223
223
|
isOvertimeCell(r) ? (
|
|
224
|
-
<
|
|
224
|
+
<Badge tone="warning" shape="sharp">
|
|
225
225
|
<Text as="span" weight="medium" tabular style={{ color: "inherit" }}>
|
|
226
226
|
{hours(r.overtime)}
|
|
227
227
|
</Text>
|
|
228
|
-
</
|
|
228
|
+
</Badge>
|
|
229
229
|
) : (
|
|
230
230
|
<Text tabular>{hours(r.overtime)}</Text>
|
|
231
231
|
),
|
|
@@ -247,7 +247,7 @@ export default function Demo() {
|
|
|
247
247
|
<Flex direction="col" gap="lg">
|
|
248
248
|
{/* ── 行レベル + セルレベルの条件付き書式(実 Table primitive で合成) ── */}
|
|
249
249
|
<Card>
|
|
250
|
-
<CardHeader
|
|
250
|
+
<CardHeader>
|
|
251
251
|
<Flex direction="row" wrap align="center" justify="between" gap="sm">
|
|
252
252
|
<CardTitle level={2}>勤怠サマリ · 製造部(当月締め)</CardTitle>
|
|
253
253
|
<Text size="xs" tone="muted" tabular>
|
|
@@ -312,11 +312,11 @@ export default function Demo() {
|
|
|
312
312
|
{/* 早退セル: > 2.0h は attention(朱) で背景+前景強調 */}
|
|
313
313
|
<TableCell className="text-end">
|
|
314
314
|
{isEarlyCell(r) ? (
|
|
315
|
-
<
|
|
315
|
+
<Badge tone="warning" shape="sharp">
|
|
316
316
|
<Text as="span" weight="medium" tabular style={{ color: "inherit" }}>
|
|
317
317
|
{hours(r.earlyLeaveHours)}
|
|
318
318
|
</Text>
|
|
319
|
-
</
|
|
319
|
+
</Badge>
|
|
320
320
|
) : (
|
|
321
321
|
<Text tone="muted" tabular>
|
|
322
322
|
{hours(r.earlyLeaveHours)}
|
|
@@ -326,11 +326,11 @@ export default function Demo() {
|
|
|
326
326
|
{/* 残業セル: ≥ 45h は warning(山吹) で注意 */}
|
|
327
327
|
<TableCell className="text-end">
|
|
328
328
|
{isOvertimeCell(r) ? (
|
|
329
|
-
<
|
|
329
|
+
<Badge tone="warning" shape="sharp">
|
|
330
330
|
<Text as="span" weight="medium" tabular style={{ color: "inherit" }}>
|
|
331
331
|
{hours(r.overtime)}
|
|
332
332
|
</Text>
|
|
333
|
-
</
|
|
333
|
+
</Badge>
|
|
334
334
|
) : (
|
|
335
335
|
<Text tabular>{hours(r.overtime)}</Text>
|
|
336
336
|
)}
|