@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
|
@@ -10,18 +10,17 @@
|
|
|
10
10
|
* page chrome ............. AppShell + Sidebar + Topbar
|
|
11
11
|
* page header + nav ....... PageContainer (extra slot) + Button + ToggleGroup (月/週/日)
|
|
12
12
|
* month jump-to ........... Calendar (date-picker) inside a Popover
|
|
13
|
-
* month event grid ........ Card +
|
|
14
|
-
* week time-axis + now-line Card +
|
|
13
|
+
* month event grid ........ Card + Table bordered (one <tr> per week, one <td> per day)
|
|
14
|
+
* week time-axis + now-line Card + TimelineGrid (hour axis, day columns, `now` marker)
|
|
15
15
|
* day staff×time .......... Card + Timeline (per-staff lane) + EmptyState (understaffed)
|
|
16
16
|
* shift legend ............ Card + the wa-iro decorative palette swatches
|
|
17
17
|
* date detail (mobile) .... Sheet (master-detail collapses to a drawer < lg)
|
|
18
18
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* used for its genuine job (jumping to a month). See `gapNotes`.
|
|
19
|
+
* The two grids each take the primitive that fits their shape. A MONTH is a day matrix, so it is
|
|
20
|
+
* a real `Table`. A WEEK is a continuous time axis, so it is `TimelineGrid` — which also places
|
|
21
|
+
* the 木曜 早番/遅番 pair side by side instead of stacking one on top of the other, and clips the
|
|
22
|
+
* 夜勤 22:00–06:00 at the 24:00 edge while the block still prints its real range. `Calendar` keeps
|
|
23
|
+
* its genuine job: a single-date PICKER for jumping to a month.
|
|
25
24
|
*
|
|
26
25
|
* DNA applied: compact density, small headings (20/18/14/13), fixed color signaling
|
|
27
26
|
* (success 若竹 / warning 山吹 / info 群青 / attention 朱 / danger 茜), tabular-nums,
|
|
@@ -52,10 +51,20 @@ import {
|
|
|
52
51
|
CardHeader,
|
|
53
52
|
CardTitle,
|
|
54
53
|
EmptyState,
|
|
54
|
+
ListRow,
|
|
55
55
|
Popover,
|
|
56
56
|
PopoverContent,
|
|
57
57
|
PopoverTrigger,
|
|
58
|
+
Table,
|
|
59
|
+
TableBody,
|
|
60
|
+
TableCell,
|
|
61
|
+
TableHead,
|
|
62
|
+
TableHeader,
|
|
63
|
+
TableRow,
|
|
58
64
|
Timeline,
|
|
65
|
+
TimelineGrid,
|
|
66
|
+
type TimelineGridColumnProp,
|
|
67
|
+
type TimelineGridEventProp,
|
|
59
68
|
type TimelineItem,
|
|
60
69
|
} from "@godxjp/ui/data-display";
|
|
61
70
|
import { Calendar, ToggleGroup, ToggleGroupItem } from "@godxjp/ui/data-entry";
|
|
@@ -107,17 +116,14 @@ function ShiftPill({
|
|
|
107
116
|
}) {
|
|
108
117
|
const meta = SHIFT_META[kind];
|
|
109
118
|
return (
|
|
110
|
-
<
|
|
119
|
+
<Badge
|
|
111
120
|
data-shift={kind}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
background: `color-mix(in oklch, var(${meta.cssVar}) 12%, transparent)`,
|
|
116
|
-
color: meta.muted ? "var(--muted-foreground)" : "var(--foreground)",
|
|
117
|
-
}}
|
|
121
|
+
color={`var(${meta.cssVar})`}
|
|
122
|
+
shape="sharp"
|
|
123
|
+
className={`truncate leading-tight ${className ?? ""}`}
|
|
118
124
|
title={`${meta.label} ${meta.time}${staff ? ` · ${staff}` : ""}`}
|
|
119
125
|
>
|
|
120
|
-
<Text as="span" size="2xs" weight="medium"
|
|
126
|
+
<Text as="span" size="2xs" weight="medium">
|
|
121
127
|
{meta.label}
|
|
122
128
|
</Text>
|
|
123
129
|
{staff ? (
|
|
@@ -125,7 +131,7 @@ function ShiftPill({
|
|
|
125
131
|
{staff}
|
|
126
132
|
</Text>
|
|
127
133
|
) : null}
|
|
128
|
-
</
|
|
134
|
+
</Badge>
|
|
129
135
|
);
|
|
130
136
|
}
|
|
131
137
|
|
|
@@ -218,44 +224,61 @@ function buildMonth(): DayCell[] {
|
|
|
218
224
|
|
|
219
225
|
const MONTH_CELLS = buildMonth();
|
|
220
226
|
const WEEKDAY_HEAD = ["日", "月", "火", "水", "木", "金", "土"];
|
|
227
|
+
/** 42 day cells as six calendar rows — the month grid is a real <table>, one <tr> per week. */
|
|
228
|
+
const MONTH_WEEKS = Array.from({ length: MONTH_CELLS.length / 7 }, (_, w) =>
|
|
229
|
+
MONTH_CELLS.slice(w * 7, w * 7 + 7),
|
|
230
|
+
);
|
|
221
231
|
|
|
222
|
-
// ── Week view (2026-05-11 〜 05-17), time axis 06:00–
|
|
223
|
-
|
|
232
|
+
// ── Week view (2026-05-11 〜 05-17), time axis 06:00–24:00 ─────────────────────
|
|
233
|
+
// The week axis is a real `TimelineGrid`: columns are days, blocks are placed by start time and
|
|
234
|
+
// duration, and two shifts that overlap are laid out side by side by the primitive.
|
|
235
|
+
const WEEK_COLUMNS: TimelineGridColumnProp[] = [
|
|
224
236
|
{ date: 11, weekday: 1 },
|
|
225
237
|
{ date: 12, weekday: 2 },
|
|
226
238
|
{ date: 13, weekday: 3 },
|
|
227
|
-
{ date: 14, weekday: 4,
|
|
239
|
+
{ date: 14, weekday: 4, current: true },
|
|
228
240
|
{ date: 15, weekday: 5 },
|
|
229
241
|
{ date: 16, weekday: 6 },
|
|
230
242
|
{ date: 17, weekday: 0 },
|
|
231
|
-
]
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
const NOW_HOUR = 14 + 35 / 60; // 14:35 now-line
|
|
243
|
+
].map(({ date, weekday, current }) => ({
|
|
244
|
+
id: `05-${date}`,
|
|
245
|
+
label: (
|
|
246
|
+
<Text
|
|
247
|
+
as="span"
|
|
248
|
+
size="2xs"
|
|
249
|
+
weight="medium"
|
|
250
|
+
tabular
|
|
251
|
+
style={{
|
|
252
|
+
color:
|
|
253
|
+
weekday === 0
|
|
254
|
+
? "var(--destructive)"
|
|
255
|
+
: weekday === 6
|
|
256
|
+
? "var(--info)"
|
|
257
|
+
: "var(--muted-foreground)",
|
|
258
|
+
}}
|
|
259
|
+
>
|
|
260
|
+
{WEEKDAY_HEAD[weekday]} {date}
|
|
261
|
+
</Text>
|
|
262
|
+
),
|
|
263
|
+
current,
|
|
264
|
+
}));
|
|
254
265
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
}
|
|
266
|
+
// 夜勤 22:00→06:00: an end at or before the start means the shift runs into the next day, so the
|
|
267
|
+
// grid clips it at the 24:00 edge and still prints the real range on the block.
|
|
268
|
+
const WEEK_SHIFTS: TimelineGridEventProp[] = [
|
|
269
|
+
{ id: "w1", columnId: "05-14", start: "09:00", end: "17:30", kind: "early", staff: "田中" },
|
|
270
|
+
{ id: "w2", columnId: "05-14", start: "13:00", end: "22:00", kind: "late", staff: "高橋" },
|
|
271
|
+
{ id: "w3", columnId: "05-14", start: "22:00", end: "06:00", kind: "night", staff: "伊藤" },
|
|
272
|
+
{ id: "w4", columnId: "05-11", start: "09:00", end: "22:00", kind: "tsushi", staff: "佐藤" },
|
|
273
|
+
{ id: "w5", columnId: "05-15", start: "09:00", end: "17:30", kind: "early", staff: "佐藤" },
|
|
274
|
+
{ id: "w6", columnId: "05-15", start: "18:00", end: "21:00", kind: "ot", staff: "鈴木" },
|
|
275
|
+
{ id: "w7", columnId: "05-12", start: "06:00", end: "22:00", kind: "leave", staff: "鈴木" },
|
|
276
|
+
].map(({ kind, staff, ...event }) => ({
|
|
277
|
+
...event,
|
|
278
|
+
title: SHIFT_META[kind as ShiftKind].label,
|
|
279
|
+
description: staff,
|
|
280
|
+
color: `var(${SHIFT_META[kind as ShiftKind].cssVar})`,
|
|
281
|
+
}));
|
|
259
282
|
|
|
260
283
|
// ── Day view (2026-05-14) — per-staff lanes via Timeline + understaffed gap ───
|
|
261
284
|
const DAY_LANES: Array<{ staff: string; role: string; items: TimelineItem[] }> = [
|
|
@@ -354,7 +377,7 @@ export default function ShiftCalendarShowcase() {
|
|
|
354
377
|
2026年5月
|
|
355
378
|
</Button>
|
|
356
379
|
</PopoverTrigger>
|
|
357
|
-
<PopoverContent className="w-auto
|
|
380
|
+
<PopoverContent className="w-auto" style={{ padding: 0 }} align="start">
|
|
358
381
|
<Calendar
|
|
359
382
|
mode="single"
|
|
360
383
|
selected={jumpMonth}
|
|
@@ -406,22 +429,14 @@ export default function ShiftCalendarShowcase() {
|
|
|
406
429
|
<CardContent>
|
|
407
430
|
<Flex direction="row" gap="sm" wrap>
|
|
408
431
|
{(Object.keys(SHIFT_META) as ShiftKind[]).map((k) => (
|
|
409
|
-
<
|
|
410
|
-
key={k}
|
|
411
|
-
className="inline-flex items-center gap-2 rounded-sm border px-2 py-1 text-xs"
|
|
412
|
-
>
|
|
413
|
-
<span
|
|
414
|
-
aria-hidden="true"
|
|
415
|
-
className="size-3 shrink-0 rounded-sm"
|
|
416
|
-
style={{ background: `var(${SHIFT_META[k].cssVar})` }}
|
|
417
|
-
/>
|
|
432
|
+
<Badge key={k} color={`var(${SHIFT_META[k].cssVar})`} shape="sharp">
|
|
418
433
|
<Text size="xs" weight="medium" className="whitespace-nowrap">
|
|
419
434
|
{SHIFT_META[k].label}
|
|
420
435
|
</Text>
|
|
421
436
|
<Text size="xs" tone="muted" tabular className="whitespace-nowrap">
|
|
422
437
|
{SHIFT_META[k].time}
|
|
423
438
|
</Text>
|
|
424
|
-
</
|
|
439
|
+
</Badge>
|
|
425
440
|
))}
|
|
426
441
|
</Flex>
|
|
427
442
|
</CardContent>
|
|
@@ -454,18 +469,16 @@ export default function ShiftCalendarShowcase() {
|
|
|
454
469
|
{detailDate && detailDate.shifts.length > 0 ? (
|
|
455
470
|
<Flex direction="col" gap="sm">
|
|
456
471
|
{detailDate.shifts.map((s, i) => (
|
|
457
|
-
<
|
|
472
|
+
<ListRow
|
|
458
473
|
key={`${s.kind}-${i}`}
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
</Text>
|
|
468
|
-
</Flex>
|
|
474
|
+
density="compact"
|
|
475
|
+
title={<ShiftPill kind={s.kind} staff={s.staff} />}
|
|
476
|
+
trailing={
|
|
477
|
+
<Text size="xs" tone="muted" tabular className="whitespace-nowrap">
|
|
478
|
+
{SHIFT_META[s.kind].time}
|
|
479
|
+
</Text>
|
|
480
|
+
}
|
|
481
|
+
/>
|
|
469
482
|
))}
|
|
470
483
|
</Flex>
|
|
471
484
|
) : (
|
|
@@ -482,7 +495,7 @@ export default function ShiftCalendarShowcase() {
|
|
|
482
495
|
);
|
|
483
496
|
}
|
|
484
497
|
|
|
485
|
-
// ── Month grid —
|
|
498
|
+
// ── Month grid — a real Table: one <tr> per week, one <td> per day ────────────
|
|
486
499
|
function MonthGrid({ onPick }: { onPick: (d: DayCell) => void }) {
|
|
487
500
|
return (
|
|
488
501
|
<Card>
|
|
@@ -495,18 +508,14 @@ function MonthGrid({ onPick }: { onPick: (d: DayCell) => void }) {
|
|
|
495
508
|
</CardAction>
|
|
496
509
|
</CardHeader>
|
|
497
510
|
<CardContent flush>
|
|
498
|
-
<
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
<
|
|
511
|
+
{/* Weekday head — Sun→danger, Sat→info; 42 day cells as six <tr> weeks */}
|
|
512
|
+
<Table bordered className="min-w-[720px]">
|
|
513
|
+
<TableHeader className="bg-secondary">
|
|
514
|
+
<TableRow>
|
|
502
515
|
{WEEKDAY_HEAD.map((w, i) => (
|
|
503
|
-
<
|
|
504
|
-
as="div"
|
|
516
|
+
<TableHead
|
|
505
517
|
key={w}
|
|
506
|
-
|
|
507
|
-
weight="medium"
|
|
508
|
-
align="center"
|
|
509
|
-
className="px-2 py-1.5"
|
|
518
|
+
className="text-center"
|
|
510
519
|
style={{
|
|
511
520
|
color:
|
|
512
521
|
i === 0
|
|
@@ -517,84 +526,96 @@ function MonthGrid({ onPick }: { onPick: (d: DayCell) => void }) {
|
|
|
517
526
|
}}
|
|
518
527
|
>
|
|
519
528
|
{w}
|
|
520
|
-
</
|
|
529
|
+
</TableHead>
|
|
521
530
|
))}
|
|
522
|
-
</
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
weight="medium"
|
|
552
|
-
tabular
|
|
553
|
-
style={{
|
|
554
|
-
color: cell.holiday
|
|
555
|
-
? "var(--destructive)"
|
|
556
|
-
: isSun
|
|
557
|
-
? "var(--destructive)"
|
|
558
|
-
: isSat
|
|
559
|
-
? "var(--info)"
|
|
560
|
-
: "var(--foreground)",
|
|
561
|
-
}}
|
|
531
|
+
</TableRow>
|
|
532
|
+
</TableHeader>
|
|
533
|
+
<TableBody>
|
|
534
|
+
{MONTH_WEEKS.map((week, r) => (
|
|
535
|
+
<TableRow key={r}>
|
|
536
|
+
{week.map((cell, c) => {
|
|
537
|
+
const isSun = cell.weekday === 0;
|
|
538
|
+
const isSat = cell.weekday === 6;
|
|
539
|
+
const shown = cell.shifts.slice(0, 3);
|
|
540
|
+
const overflow = cell.shifts.length - shown.length;
|
|
541
|
+
return (
|
|
542
|
+
<TableCell
|
|
543
|
+
key={c}
|
|
544
|
+
className="align-top"
|
|
545
|
+
style={{
|
|
546
|
+
background: cell.today
|
|
547
|
+
? "color-mix(in oklch, var(--primary) 5%, transparent)"
|
|
548
|
+
: cell.dim
|
|
549
|
+
? "color-mix(in oklch, var(--secondary) 40%, transparent)"
|
|
550
|
+
: undefined,
|
|
551
|
+
outline: cell.today ? "2px solid var(--primary)" : undefined,
|
|
552
|
+
outlineOffset: cell.today ? "-2px" : undefined,
|
|
553
|
+
opacity: cell.dim ? 0.5 : 1,
|
|
554
|
+
}}
|
|
555
|
+
>
|
|
556
|
+
<button
|
|
557
|
+
type="button"
|
|
558
|
+
onClick={() => onPick(cell)}
|
|
559
|
+
className="hover:bg-accent focus-visible:ring-ring min-h-[96px] w-full text-start transition-colors focus:outline-none focus-visible:ring-2"
|
|
562
560
|
>
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
561
|
+
<Flex direction="col" gap="xs">
|
|
562
|
+
<Flex align="center" justify="between">
|
|
563
|
+
<Text
|
|
564
|
+
as="span"
|
|
565
|
+
size="sm"
|
|
566
|
+
weight="medium"
|
|
567
|
+
tabular
|
|
568
|
+
style={{
|
|
569
|
+
color: cell.holiday
|
|
570
|
+
? "var(--destructive)"
|
|
571
|
+
: isSun
|
|
572
|
+
? "var(--destructive)"
|
|
573
|
+
: isSat
|
|
574
|
+
? "var(--info)"
|
|
575
|
+
: "var(--foreground)",
|
|
576
|
+
}}
|
|
577
|
+
>
|
|
578
|
+
{cell.date}
|
|
579
|
+
</Text>
|
|
580
|
+
{cell.holiday ? (
|
|
581
|
+
<Badge
|
|
582
|
+
tone="destructive"
|
|
583
|
+
variant="outline"
|
|
584
|
+
shape="sharp"
|
|
585
|
+
className="text-[var(--font-size-2xs)]"
|
|
586
|
+
>
|
|
587
|
+
祝
|
|
588
|
+
</Badge>
|
|
589
|
+
) : null}
|
|
590
|
+
</Flex>
|
|
591
|
+
<Flex direction="col" gap="xs">
|
|
592
|
+
{cell.holiday ? (
|
|
593
|
+
<ShiftPill kind="holiday" staff={cell.holiday} />
|
|
594
|
+
) : null}
|
|
595
|
+
{shown.map((sh, i) => (
|
|
596
|
+
<ShiftPill key={`${sh.kind}-${i}`} kind={sh.kind} staff={sh.staff} />
|
|
597
|
+
))}
|
|
598
|
+
{overflow > 0 ? (
|
|
599
|
+
<Text as="span" size="2xs" tone="muted" tabular>
|
|
600
|
+
+{overflow} 件
|
|
601
|
+
</Text>
|
|
602
|
+
) : null}
|
|
603
|
+
</Flex>
|
|
604
|
+
</Flex>
|
|
605
|
+
</button>
|
|
606
|
+
</TableCell>
|
|
607
|
+
);
|
|
608
|
+
})}
|
|
609
|
+
</TableRow>
|
|
610
|
+
))}
|
|
611
|
+
</TableBody>
|
|
612
|
+
</Table>
|
|
592
613
|
</CardContent>
|
|
593
614
|
</Card>
|
|
594
615
|
);
|
|
595
616
|
}
|
|
596
617
|
|
|
597
|
-
// ── Week time-axis
|
|
618
|
+
// ── Week time-axis — the TimelineGrid primitive (#354 item 7 closed) ──────────
|
|
598
619
|
function WeekTimeline() {
|
|
599
620
|
return (
|
|
600
621
|
<Card>
|
|
@@ -602,140 +623,20 @@ function WeekTimeline() {
|
|
|
602
623
|
<CardTitle level={2}>週</CardTitle>
|
|
603
624
|
<CardAction>
|
|
604
625
|
<Text size="xs" tone="muted" tabular className="whitespace-nowrap">
|
|
605
|
-
5月11日〜17日 · 06:00–
|
|
626
|
+
5月11日〜17日 · 06:00–24:00
|
|
606
627
|
</Text>
|
|
607
628
|
</CardAction>
|
|
608
629
|
</CardHeader>
|
|
609
630
|
<CardContent flush>
|
|
610
|
-
<
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
<Text
|
|
620
|
-
as="div"
|
|
621
|
-
key={d.date}
|
|
622
|
-
size="2xs"
|
|
623
|
-
weight="medium"
|
|
624
|
-
tabular
|
|
625
|
-
align="center"
|
|
626
|
-
className="px-2 py-1.5"
|
|
627
|
-
style={{
|
|
628
|
-
color:
|
|
629
|
-
d.weekday === 0
|
|
630
|
-
? "var(--destructive)"
|
|
631
|
-
: d.weekday === 6
|
|
632
|
-
? "var(--info)"
|
|
633
|
-
: "var(--muted-foreground)",
|
|
634
|
-
background:
|
|
635
|
-
"today" in d && d.today
|
|
636
|
-
? "color-mix(in oklch, var(--primary) 6%, transparent)"
|
|
637
|
-
: undefined,
|
|
638
|
-
}}
|
|
639
|
-
>
|
|
640
|
-
{WEEKDAY_HEAD[d.weekday]} {d.date}
|
|
641
|
-
</Text>
|
|
642
|
-
))}
|
|
643
|
-
</ResponsiveGrid>
|
|
644
|
-
{/* Time grid body */}
|
|
645
|
-
<ResponsiveGrid
|
|
646
|
-
columns={{ sm: 1, md: 1, lg: 1 }}
|
|
647
|
-
className="relative grid-cols-[48px_repeat(7,1fr)]"
|
|
648
|
-
>
|
|
649
|
-
{/* Hour axis labels */}
|
|
650
|
-
<div className="relative" style={{ height: `${(AXIS_END - AXIS_START) * 22}px` }}>
|
|
651
|
-
{HOURS.map((h) => (
|
|
652
|
-
<Text
|
|
653
|
-
as="div"
|
|
654
|
-
key={h}
|
|
655
|
-
size="2xs"
|
|
656
|
-
tone="muted"
|
|
657
|
-
tabular
|
|
658
|
-
className="absolute end-1.5 -translate-y-1/2"
|
|
659
|
-
style={{ top: `${hourToPct(h)}%` }}
|
|
660
|
-
>
|
|
661
|
-
{String(h).padStart(2, "0")}:00
|
|
662
|
-
</Text>
|
|
663
|
-
))}
|
|
664
|
-
</div>
|
|
665
|
-
{/* 7 day columns */}
|
|
666
|
-
{WEEK_DATES.map((d, col) => (
|
|
667
|
-
<div
|
|
668
|
-
key={d.date}
|
|
669
|
-
className="relative border-s"
|
|
670
|
-
style={{ height: `${(AXIS_END - AXIS_START) * 22}px` }}
|
|
671
|
-
>
|
|
672
|
-
{/* hour gridlines */}
|
|
673
|
-
{HOURS.map((h) => (
|
|
674
|
-
<div
|
|
675
|
-
key={h}
|
|
676
|
-
aria-hidden="true"
|
|
677
|
-
className="border-border/60 absolute inset-x-0 border-t"
|
|
678
|
-
style={{ top: `${hourToPct(h)}%` }}
|
|
679
|
-
/>
|
|
680
|
-
))}
|
|
681
|
-
{/* shift blocks for this column */}
|
|
682
|
-
{WEEK_BLOCKS.filter((b) => b.col === col).map((b, i) => {
|
|
683
|
-
const top = hourToPct(b.startHour);
|
|
684
|
-
const bottom = hourToPct(b.endHour);
|
|
685
|
-
const meta = SHIFT_META[b.kind];
|
|
686
|
-
const spills = b.endHour > AXIS_END;
|
|
687
|
-
return (
|
|
688
|
-
<div
|
|
689
|
-
key={`${b.kind}-${i}`}
|
|
690
|
-
className="absolute inset-x-0.5 overflow-hidden rounded-sm border-s-2 px-1 py-0.5 leading-tight"
|
|
691
|
-
style={{
|
|
692
|
-
top: `${top}%`,
|
|
693
|
-
height: `${Math.max(bottom - top, 4)}%`,
|
|
694
|
-
borderLeftColor: `var(${meta.cssVar})`,
|
|
695
|
-
background: `color-mix(in oklch, var(${meta.cssVar}) 14%, transparent)`,
|
|
696
|
-
color: meta.muted ? "var(--muted-foreground)" : "var(--foreground)",
|
|
697
|
-
}}
|
|
698
|
-
title={`${meta.label} ${meta.time} · ${b.staff}`}
|
|
699
|
-
>
|
|
700
|
-
<Text
|
|
701
|
-
as="div"
|
|
702
|
-
size="2xs"
|
|
703
|
-
weight="medium"
|
|
704
|
-
truncate
|
|
705
|
-
style={{ color: "inherit" }}
|
|
706
|
-
>
|
|
707
|
-
{meta.label}
|
|
708
|
-
</Text>
|
|
709
|
-
<Text as="div" size="2xs" tone="muted" truncate>
|
|
710
|
-
{b.staff}
|
|
711
|
-
</Text>
|
|
712
|
-
{spills ? (
|
|
713
|
-
<Text as="div" size="2xs" tone="muted" tabular>
|
|
714
|
-
翌日へ ↓
|
|
715
|
-
</Text>
|
|
716
|
-
) : null}
|
|
717
|
-
</div>
|
|
718
|
-
);
|
|
719
|
-
})}
|
|
720
|
-
{/* now-line — only on "today" (col 3), 14:35 */}
|
|
721
|
-
{"today" in d && d.today ? (
|
|
722
|
-
<div
|
|
723
|
-
aria-label="現在時刻 14:35"
|
|
724
|
-
className="pointer-events-none absolute inset-x-0 z-10"
|
|
725
|
-
style={{ top: `${hourToPct(NOW_HOUR)}%` }}
|
|
726
|
-
>
|
|
727
|
-
<div className="h-px" style={{ background: "var(--destructive)" }} />
|
|
728
|
-
<div
|
|
729
|
-
className="absolute -start-1 -top-1 size-2 rounded-full"
|
|
730
|
-
style={{ background: "var(--destructive)" }}
|
|
731
|
-
/>
|
|
732
|
-
</div>
|
|
733
|
-
) : null}
|
|
734
|
-
</div>
|
|
735
|
-
))}
|
|
736
|
-
</ResponsiveGrid>
|
|
737
|
-
</div>
|
|
738
|
-
</div>
|
|
631
|
+
<TimelineGrid
|
|
632
|
+
label="週シフト 2026年5月11日〜17日"
|
|
633
|
+
columns={WEEK_COLUMNS}
|
|
634
|
+
events={WEEK_SHIFTS}
|
|
635
|
+
start="06:00"
|
|
636
|
+
end="24:00"
|
|
637
|
+
interval={2}
|
|
638
|
+
now="14:35"
|
|
639
|
+
/>
|
|
739
640
|
</CardContent>
|
|
740
641
|
</Card>
|
|
741
642
|
);
|