@godxjp/ui 19.3.1 → 19.4.1

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 (128) hide show
  1. package/dist/components/data-display/badge.d.ts +9 -1
  2. package/dist/components/data-display/badge.js +3 -2
  3. package/dist/components/data-display/index.d.ts +2 -0
  4. package/dist/components/data-display/index.js +2 -0
  5. package/dist/components/data-display/popover.d.ts +13 -1
  6. package/dist/components/data-display/popover.js +3 -1
  7. package/dist/components/data-display/table.d.ts +19 -2
  8. package/dist/components/data-display/table.js +4 -1
  9. package/dist/components/data-display/timeline-grid.d.ts +18 -0
  10. package/dist/components/data-display/timeline-grid.js +222 -0
  11. package/dist/components/data-entry/cascader.js +4 -6
  12. package/dist/components/data-entry/checkbox.js +1 -1
  13. package/dist/components/data-entry/search-select.js +5 -7
  14. package/dist/components/data-entry/select.js +32 -28
  15. package/dist/components/data-entry/tree-select.js +4 -6
  16. package/dist/components/general/inert-background.d.ts +5 -0
  17. package/dist/components/general/inert-background.js +48 -0
  18. package/dist/components/layout/auth-shell.d.ts +4 -2
  19. package/dist/components/layout/auth-shell.js +7 -1
  20. package/dist/components/layout/centered-shell.d.ts +2 -2
  21. package/dist/components/layout/flex.d.ts +1 -1
  22. package/dist/components/layout/flex.js +2 -1
  23. package/dist/components/layout/index.d.ts +3 -0
  24. package/dist/components/layout/index.js +2 -0
  25. package/dist/components/layout/mobile-shell.d.ts +21 -0
  26. package/dist/components/layout/mobile-shell.js +43 -0
  27. package/dist/components/layout/sidebar-link.d.ts +1 -1
  28. package/dist/components/navigation/context-menu.js +13 -9
  29. package/dist/components/navigation/dropdown-menu.js +17 -13
  30. package/dist/inertia/index.d.ts +1 -1
  31. package/dist/lib/variants.d.ts +3 -3
  32. package/dist/lib/variants.js +1 -0
  33. package/dist/props/components/data-display.prop.d.ts +65 -0
  34. package/dist/props/components/data-entry.prop.d.ts +1 -1
  35. package/dist/props/components/layout.prop.d.ts +93 -6
  36. package/dist/props/registry.d.ts +109 -2
  37. package/dist/props/registry.js +143 -0
  38. package/dist/props/vocabulary/data.prop.d.ts +6 -0
  39. package/dist/props/vocabulary/index.d.ts +3 -3
  40. package/dist/props/vocabulary/layout.prop.d.ts +17 -2
  41. package/dist/props/vocabulary/shared.prop.d.ts +7 -0
  42. package/dist/styles/control.css +18 -1
  43. package/dist/styles/data-display-layout.css +167 -0
  44. package/dist/styles/layout.css +7 -0
  45. package/dist/styles/shell-layout.css +148 -0
  46. package/dist/styles/table-layout.css +23 -4
  47. package/dist/tokens/components/control.css +1 -0
  48. package/dist/tokens/components/data-display.css +20 -0
  49. package/dist/tokens/components/shell.css +22 -0
  50. package/dist/tokens/components/table.css +2 -0
  51. package/docs/FRAME-A11Y-CI.md +99 -17
  52. package/docs/FRAME-COVERAGE-REPORT.md +4 -2
  53. package/docs/SPACING.md +19 -6
  54. package/docs/charts/compact-bar-trend.tsx +5 -6
  55. package/docs/data-display/avatar.tsx +7 -9
  56. package/docs/data-display/badge.tsx +1 -1
  57. package/docs/data-display/card/index.tsx +6 -4
  58. package/docs/data-display/data-table/examples/approval-queue.tsx +1 -1
  59. package/docs/data-display/data-table/index.tsx +1 -1
  60. package/docs/data-display/permission-matrix.tsx +2 -2
  61. package/docs/data-display/popover.tsx +4 -1
  62. package/docs/data-display/scroll-area.tsx +1 -1
  63. package/docs/data-display/table.tsx +1 -1
  64. package/docs/data-display/timeline-grid.tsx +119 -0
  65. package/docs/data-entry/branch-scope-picker.tsx +1 -1
  66. package/docs/data-entry/date-picker.tsx +2 -1
  67. package/docs/data-entry/form-field/index.tsx +2 -2
  68. package/docs/data-entry/input-otp.tsx +1 -1
  69. package/docs/data-entry/select-matrix.tsx +2 -1
  70. package/docs/data-entry/select.tsx +3 -1
  71. package/docs/data-entry/textarea.tsx +1 -1
  72. package/docs/data-entry/toggle-count.tsx +1 -1
  73. package/docs/feedback/alert.tsx +1 -1
  74. package/docs/feedback/banner.tsx +1 -1
  75. package/docs/feedback/dialog.tsx +2 -1
  76. package/docs/feedback/sheet.tsx +2 -1
  77. package/docs/foundation/email-tokens.tsx +1 -1
  78. package/docs/general/activity.tsx +1 -1
  79. package/docs/layout/app-shell.tsx +4 -4
  80. package/docs/layout/auth-account-summary.tsx +1 -1
  81. package/docs/layout/auth-recovery/examples/mobile-390.tsx +1 -1
  82. package/docs/layout/auth-recovery/examples/password-recovery.tsx +0 -2
  83. package/docs/layout/auth-shell-context.tsx +1 -1
  84. package/docs/layout/auth-shell-device.tsx +2 -2
  85. package/docs/layout/auth-shell-registration.tsx +1 -1
  86. package/docs/layout/auth-shell.tsx +1 -1
  87. package/docs/layout/master-detail.tsx +2 -2
  88. package/docs/layout/mobile-shell.tsx +101 -0
  89. package/docs/layout/page-container.tsx +6 -6
  90. package/docs/layout/responsive-grid.tsx +5 -5
  91. package/docs/layout/separator.tsx +2 -2
  92. package/docs/layout/service-role-panel.tsx +1 -1
  93. package/docs/layout/sidebar.tsx +9 -9
  94. package/docs/layout/split-pane.tsx +5 -6
  95. package/docs/layout/topbar.tsx +2 -8
  96. package/docs/navigation/context-menu.tsx +6 -1
  97. package/docs/navigation/dropdown-menu.tsx +2 -1
  98. package/docs/navigation/filter-bar.tsx +2 -2
  99. package/docs/navigation/pagination.tsx +2 -2
  100. package/docs/navigation/steps.tsx +1 -1
  101. package/docs/navigation/tabs.tsx +3 -3
  102. package/docs/showcase/acme-portal.tsx +11 -12
  103. package/docs/showcase/acme-website.tsx +7 -7
  104. package/docs/showcase/case1-warehouse-dashboard.tsx +11 -12
  105. package/docs/showcase/case2-employee-me.tsx +10 -16
  106. package/docs/showcase/case4-login.tsx +144 -156
  107. package/docs/showcase/case5-shift-calendar.tsx +181 -280
  108. package/docs/showcase/case6-agency-handy.tsx +446 -443
  109. package/docs/showcase/futurelastic-web.tsx +9 -8
  110. package/docs/showcase/org-switcher.tsx +18 -11
  111. package/docs/showcase/permission-matrix.tsx +13 -13
  112. package/docs/showcase/public-landing.tsx +1 -1
  113. package/docs/showcase/settings-account-sections.tsx +4 -4
  114. package/docs/showcase/settings-security-mfa.tsx +3 -3
  115. package/docs/showcase/table-approval-queue.tsx +1 -1
  116. package/docs/showcase/table-bulk-actions.tsx +49 -52
  117. package/docs/showcase/table-conditional-format.tsx +1 -1
  118. package/docs/showcase/table-crud-list.tsx +1 -1
  119. package/docs/showcase/table-expandable-rows.tsx +101 -98
  120. package/docs/showcase/table-filter-chips.tsx +78 -66
  121. package/docs/showcase/table-footer-totals.tsx +23 -19
  122. package/docs/showcase/table-grouped-subtotals.tsx +7 -4
  123. package/docs/showcase/table-master-detail.tsx +13 -15
  124. package/docs/showcase/table-states.tsx +34 -29
  125. package/docs/showcase/table-sticky-columns.tsx +3 -2
  126. package/docs/showcase/table-tree-rows.tsx +22 -15
  127. package/docs/showcase/table-view-tabs.tsx +12 -14
  128. package/package.json +7 -6
@@ -14,6 +14,14 @@ declare const badgeVariants: (props?: ({
14
14
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
15
15
  export interface BadgeProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "children">, Omit<VariantProps<typeof badgeVariants>, "variant" | "shape"> {
16
16
  variant?: BadgeVariant | null;
17
+ /**
18
+ * Render element — `div` (default) or `span` when the chip sits in a PHRASING context and a
19
+ * `<div>` would be invalid HTML: inside a `TabsTrigger`/`PopoverTrigger`/`Button` (all render a
20
+ * `<button>`, whose content model is phrasing content only), inside a `<label>` or a `<p>`.
21
+ * It swaps the TAG only — the chip's own `inline-flex` box, icon and label are unchanged, and
22
+ * both the icon `<svg>` and the label `<span>` are already phrasing content.
23
+ */
24
+ as?: "div" | "span";
17
25
  /** Corner shape — `default` (badge radius) · `pill` (fully rounded) · `sharp` (square). */
18
26
  shape?: ShapeProp | null;
19
27
  tone?: BadgeTone | null;
@@ -39,7 +47,7 @@ export interface BadgeProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "
39
47
  status?: string;
40
48
  children?: React.ReactNode;
41
49
  }
42
- export declare function Badge({ className, variant, shape, tone, color, icon, status, style, children, ...props }: BadgeProps): React.JSX.Element;
50
+ export declare function Badge({ as: Element, className, variant, shape, tone, color, icon, status, style, children, ...props }: BadgeProps): React.JSX.Element;
43
51
  /**
44
52
  * Status-aware badge with the shared domain-status-to-tone mapping. `Badge` remains the
45
53
  * general-purpose primitive.
@@ -62,7 +62,7 @@ const badgeVariants = cva(
62
62
  * `BadgeVariant` union: every fill, border and text utility has to stay
63
63
  * off the element so badge-layout.css's `[data-tinted]` rule is
64
64
  * reachable (a utility beats a components-layer rule whatever the
65
- * specificity — gh#260). Selected by the `color` prop, never by hand. */
65
+ * specificity). Selected by the `color` prop, never by hand. */
66
66
  tinted: ""
67
67
  },
68
68
  // Corner shape — default inherits the badge radius token; pill/sharp override via the tokens.
@@ -86,6 +86,7 @@ const badgeToneClass = {
86
86
  neutral: cn("border-transparent", toneNeutralClass)
87
87
  };
88
88
  function Badge({
89
+ as: Element = "div",
89
90
  className,
90
91
  variant,
91
92
  shape,
@@ -104,7 +105,7 @@ function Badge({
104
105
  const resolvedChildren = children ?? (status ? status in STATUS_MAP ? t(`status.${status}`) : status : void 0);
105
106
  const tinted = color != null && color !== "";
106
107
  return /* @__PURE__ */ jsxs(
107
- "div",
108
+ Element,
108
109
  {
109
110
  "data-slot": "badge",
110
111
  "data-tone": tinted ? void 0 : resolvedTone,
@@ -28,6 +28,8 @@ export { TreeList } from "./tree-list.js";
28
28
  export type { TreeListItem, TreeListProps } from "./tree-list.js";
29
29
  export { Timeline } from "./timeline.js";
30
30
  export type { TimelineItem, TimelineProps } from "./timeline.js";
31
+ export { TimelineGrid } from "./timeline-grid.js";
32
+ export type { TimelineGridColumnProp, TimelineGridEventProp, TimelineGridProp, TimelineGridProps, } from "./timeline-grid.js";
31
33
  export { Popover, PopoverAnchor, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger, } from "./popover.js";
32
34
  export { ScrollArea, ScrollBar } from "./scroll-area.js";
33
35
  export { Collapsible, CollapsibleTrigger, CollapsibleContent } from "./collapsible.js";
@@ -29,6 +29,7 @@ import { CodeBlock } from "./code-block.js";
29
29
  import { Prose } from "./prose.js";
30
30
  import { TreeList } from "./tree-list.js";
31
31
  import { Timeline } from "./timeline.js";
32
+ import { TimelineGrid } from "./timeline-grid.js";
32
33
  import {
33
34
  Popover,
34
35
  PopoverAnchor,
@@ -113,6 +114,7 @@ export {
113
114
  TableHeader,
114
115
  TableRow,
115
116
  Timeline,
117
+ TimelineGrid,
116
118
  TreeList,
117
119
  flexRender,
118
120
  useCarousel
@@ -1,9 +1,21 @@
1
1
  import * as React from "react";
2
2
  import * as PopoverPrimitive from "@radix-ui/react-popover";
3
+ import type { FlushProp } from "../../props/vocabulary/index.js";
3
4
  export declare function Popover(props: React.ComponentProps<typeof PopoverPrimitive.Root>): React.JSX.Element;
4
5
  export declare function PopoverTrigger(props: React.ComponentProps<typeof PopoverPrimitive.Trigger>): React.JSX.Element;
5
6
  export declare function PopoverAnchor(props: React.ComponentProps<typeof PopoverPrimitive.Anchor>): React.JSX.Element;
6
- export declare const PopoverContent: React.ForwardRefExoticComponent<Omit<PopoverPrimitive.PopoverContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
7
+ /**
8
+ * The panel's CONTENT owns its inset — a Command list, a menu or a table that must run edge to
9
+ * edge and draw its own separators across the full width. The popover drops its own padding by
10
+ * zeroing `--popover-space-inset` ON THE PANEL, so the inset stays one token (a service that
11
+ * retunes `--popover-space-inset` still owns every padded popover) and no consumer has to reach
12
+ * for a zero-padding utility, which no service theme can reach.
13
+ */
14
+ type PopoverContentFlush = {
15
+ flush?: FlushProp;
16
+ };
17
+ export declare const PopoverContent: React.ForwardRefExoticComponent<Omit<PopoverPrimitive.PopoverContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & PopoverContentFlush & React.RefAttributes<HTMLDivElement>>;
7
18
  export declare const PopoverHeader: ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => React.JSX.Element;
8
19
  export declare const PopoverTitle: ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => React.JSX.Element;
9
20
  export declare const PopoverDescription: ({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>) => React.JSX.Element;
21
+ export {};
@@ -12,13 +12,15 @@ function PopoverTrigger(props) {
12
12
  function PopoverAnchor(props) {
13
13
  return /* @__PURE__ */ jsx(PopoverPrimitive.Anchor, { "data-slot": "popover-anchor", ...props });
14
14
  }
15
- const PopoverContent = React.forwardRef(({ className, align = "center", sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx(
15
+ const PopoverContent = React.forwardRef(({ className, align = "center", sideOffset = 4, flush, style, ...props }, ref) => /* @__PURE__ */ jsx(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx(
16
16
  PopoverPrimitive.Content,
17
17
  {
18
18
  ref,
19
19
  "data-slot": "popover-content",
20
+ "data-flush": flush ? "" : void 0,
20
21
  align,
21
22
  sideOffset,
23
+ style: flush ? { ...style, "--popover-space-inset": "0" } : style,
22
24
  className: cn(
23
25
  "ui-popover-content origin-[var(--radix-popover-content-transform-origin)]",
24
26
  "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
@@ -1,5 +1,5 @@
1
1
  import * as React from "react";
2
- import type { BreakpointProp, TableColumnPriorityProp, TablePresetProp } from "../../props/vocabulary/index.js";
2
+ import type { BreakpointProp, FlushProp, TableCellIndentProp, TableColumnPriorityProp, TablePresetProp } from "../../props/vocabulary/index.js";
3
3
  export type TableProps = React.HTMLAttributes<HTMLTableElement> & {
4
4
  /**
5
5
  * Whether the Table owns its own horizontal-scroll region (default `true`). When `true` a table
@@ -62,5 +62,22 @@ type TableCellLabel = {
62
62
  label?: React.ReactNode;
63
63
  };
64
64
  export declare const TableHead: React.ForwardRefExoticComponent<React.ThHTMLAttributes<HTMLTableCellElement> & TableCellPriority & React.RefAttributes<HTMLTableCellElement>>;
65
- export declare const TableCell: React.ForwardRefExoticComponent<React.TdHTMLAttributes<HTMLTableCellElement> & TableCellPriority & TableCellLabel & React.RefAttributes<HTMLTableCellElement>>;
65
+ /**
66
+ * The cell's CONTENT owns its inset — an expanded detail panel, a nested table, a full-bleed media
67
+ * strip. The cell drops its own padding so the child reaches the cell edges; without it the only
68
+ * route was a zero-padding utility at the call site, which no service theme can reach.
69
+ */
70
+ type TableCellFlush = {
71
+ flush?: FlushProp;
72
+ };
73
+ /**
74
+ * Hierarchy depth. The indent is `--table-cell-space-x + depth × --table-cell-indent-space-step`,
75
+ * computed in table-layout.css off the level this prop publishes as `--table-cell-indent-level`,
76
+ * so a service retunes (or flattens) the step without touching JSX — the route TreeSelect's
77
+ * `--tree-select-depth` already takes. Logical, so an RTL tree indents from the inline start.
78
+ */
79
+ type TableCellIndent = {
80
+ indent?: TableCellIndentProp;
81
+ };
82
+ export declare const TableCell: React.ForwardRefExoticComponent<React.TdHTMLAttributes<HTMLTableCellElement> & TableCellPriority & TableCellLabel & TableCellFlush & TableCellIndent & React.RefAttributes<HTMLTableCellElement>>;
66
83
  export {};
@@ -76,13 +76,16 @@ const TableHead = React.forwardRef(({ className, priority, ...props }, ref) => /
76
76
  }
77
77
  ));
78
78
  TableHead.displayName = "TableHead";
79
- const TableCell = React.forwardRef(({ className, priority, label, children, ...props }, ref) => /* @__PURE__ */ jsxs(
79
+ const TableCell = React.forwardRef(({ className, priority, label, flush, indent, children, style, ...props }, ref) => /* @__PURE__ */ jsxs(
80
80
  "td",
81
81
  {
82
82
  ref,
83
83
  "data-slot": "table-cell",
84
84
  "data-priority": priority,
85
+ "data-flush": flush ? "" : void 0,
86
+ "data-indent": indent === void 0 ? void 0 : indent,
85
87
  className: cn(className),
88
+ style: indent === void 0 ? style : { ...style, "--table-cell-indent-level": indent },
86
89
  ...props,
87
90
  children: [
88
91
  label !== void 0 ? /* @__PURE__ */ jsx("span", { className: "ui-table-stacked-collection-label", "aria-hidden": "true", children: label }) : null,
@@ -0,0 +1,18 @@
1
+ import * as React from "react";
2
+ import type { TimelineGridProp } from "../../props/components/data-display.prop.js";
3
+ export type { TimelineGridColumnProp, TimelineGridEventProp, TimelineGridProp, TimelineGridProp as TimelineGridProps, } from "../../props/components/data-display.prop.js";
4
+ /**
5
+ * TimelineGrid — the time-axis half of the Timeline family: a vertical hour axis, one column per
6
+ * day (or room, or machine) and event blocks placed by start time and duration.
7
+ *
8
+ * WHAT IT IS NOT: a calendar. There is no month view, no navigation, no drag-to-create, no
9
+ * recurrence and no timezone conversion. `start`/`end` are clock times in the column's own day;
10
+ * which day a column stands for is the consumer's business.
11
+ *
12
+ * SEMANTICS: each column is a `ul` named by its own head, and each event is an `li` that PRINTS
13
+ * its time range as text. A screen reader therefore reads "木 14, list, 3 items — 09:00–17:30
14
+ * 早番 田中 …" in time order, which is the same information the sighted reader takes from the
15
+ * block's position. Nothing depends on the pixel offset. The hour rail is decorative
16
+ * (`aria-hidden`) because it repeats what every block already says.
17
+ */
18
+ export declare const TimelineGrid: React.ForwardRefExoticComponent<TimelineGridProp & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,222 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { cn } from "../../lib/utils.js";
5
+ const MINUTES_PER_DAY = 24 * 60;
6
+ const CLOCK = /^(\d{1,2}):(\d{2})$/;
7
+ function toMinutes(value) {
8
+ const match = CLOCK.exec(value.trim());
9
+ if (!match) return null;
10
+ const hours = Number(match[1]);
11
+ const minutes = Number(match[2]);
12
+ if (!Number.isInteger(hours) || !Number.isInteger(minutes)) return null;
13
+ if (hours > 24 || minutes > 59 || hours === 24 && minutes !== 0) return null;
14
+ return hours * 60 + minutes;
15
+ }
16
+ function toClock(minutes) {
17
+ const total = (Math.round(minutes) % MINUTES_PER_DAY + MINUTES_PER_DAY) % MINUTES_PER_DAY;
18
+ const hours = Math.floor(total / 60);
19
+ const rest = total % 60;
20
+ return `${String(hours).padStart(2, "0")}:${String(rest).padStart(2, "0")}`;
21
+ }
22
+ function resolveEvent(event) {
23
+ const startMin = toMinutes(event.start);
24
+ const endMin = toMinutes(event.end);
25
+ if (startMin === null || endMin === null) return null;
26
+ return { event, startMin, endMin: endMin > startMin ? endMin : endMin + MINUTES_PER_DAY };
27
+ }
28
+ function placeColumn(events) {
29
+ const sorted = [...events].sort((a, b) => a.startMin - b.startMin || a.endMin - b.endMin);
30
+ const placed = [];
31
+ let cluster = [];
32
+ let clusterEnd = Number.NEGATIVE_INFINITY;
33
+ let laneEnds = [];
34
+ const closeCluster = () => {
35
+ const lanes = Math.max(laneEnds.length, 1);
36
+ for (const entry of cluster) placed.push({ ...entry, lanes });
37
+ cluster = [];
38
+ laneEnds = [];
39
+ clusterEnd = Number.NEGATIVE_INFINITY;
40
+ };
41
+ for (const entry of sorted) {
42
+ if (entry.startMin >= clusterEnd) closeCluster();
43
+ let lane = laneEnds.findIndex((end) => end <= entry.startMin);
44
+ if (lane === -1) lane = laneEnds.length;
45
+ laneEnds[lane] = entry.endMin;
46
+ clusterEnd = Math.max(clusterEnd, entry.endMin);
47
+ cluster.push({ ...entry, lane, lanes: 1 });
48
+ }
49
+ closeCluster();
50
+ return placed;
51
+ }
52
+ function resolveWindow(start, end, events) {
53
+ const pinnedStart = start === void 0 ? null : toMinutes(start);
54
+ const pinnedEnd = end === void 0 ? null : toMinutes(end);
55
+ let windowStart = pinnedStart;
56
+ let windowEnd = pinnedEnd;
57
+ if (windowStart === null || windowEnd === null) {
58
+ let earliest = Number.POSITIVE_INFINITY;
59
+ let latest = Number.NEGATIVE_INFINITY;
60
+ for (const entry of events) {
61
+ earliest = Math.min(earliest, entry.startMin);
62
+ latest = Math.max(latest, Math.min(entry.endMin, MINUTES_PER_DAY));
63
+ }
64
+ if (Number.isFinite(earliest)) {
65
+ windowStart ??= Math.floor(earliest / 60) * 60;
66
+ windowEnd ??= Math.ceil(latest / 60) * 60;
67
+ } else {
68
+ windowStart ??= 0;
69
+ windowEnd ??= MINUTES_PER_DAY;
70
+ }
71
+ }
72
+ if (windowEnd <= windowStart) windowEnd = windowStart + 60;
73
+ return { startMin: windowStart, endMin: windowEnd };
74
+ }
75
+ const TimelineGrid = React.forwardRef(
76
+ function TimelineGrid2({ label, columns, events, start, end, interval = 1, now, onEventSelect, className, id }, ref) {
77
+ const uid = React.useId();
78
+ const columnIndex = React.useMemo(() => {
79
+ const map = /* @__PURE__ */ new Map();
80
+ columns.forEach((column, index) => map.set(column.id, index));
81
+ return map;
82
+ }, [columns]);
83
+ const resolved = React.useMemo(() => {
84
+ const out = [];
85
+ for (const event of events) {
86
+ const index = columnIndex.get(event.columnId);
87
+ if (index === void 0) continue;
88
+ const parsed = resolveEvent(event);
89
+ if (parsed) out.push({ ...parsed, columnIndex: index });
90
+ }
91
+ return out;
92
+ }, [events, columnIndex]);
93
+ const axis = React.useMemo(() => resolveWindow(start, end, resolved), [start, end, resolved]);
94
+ const span = axis.endMin - axis.startMin;
95
+ const hours = span / 60;
96
+ const step = Number.isFinite(interval) && interval > 0 ? interval : 1;
97
+ const byColumn = React.useMemo(() => {
98
+ const buckets = columns.map(() => []);
99
+ for (const entry of resolved) {
100
+ if (entry.endMin <= axis.startMin || entry.startMin >= axis.endMin) continue;
101
+ buckets[entry.columnIndex].push(entry);
102
+ }
103
+ return buckets.map(
104
+ (bucket) => [...placeColumn(bucket)].sort((a, b) => a.startMin - b.startMin || a.lane - b.lane)
105
+ );
106
+ }, [columns, resolved, axis.startMin, axis.endMin]);
107
+ const ticks = React.useMemo(() => {
108
+ const out = [];
109
+ for (let minutes = axis.startMin; minutes <= axis.endMin; minutes += step * 60) {
110
+ out.push({ minutes, offset: (minutes - axis.startMin) / 60 });
111
+ }
112
+ return out;
113
+ }, [axis.startMin, axis.endMin, step]);
114
+ const nowMin = now === void 0 ? null : toMinutes(now);
115
+ const nowOffset = nowMin !== null && nowMin >= axis.startMin && nowMin <= axis.endMin ? (nowMin - axis.startMin) / 60 : null;
116
+ return /* @__PURE__ */ jsx(
117
+ "div",
118
+ {
119
+ ref,
120
+ id,
121
+ "data-slot": "timeline-grid",
122
+ className: cn("ui-timeline-grid ui-focus-ring", className),
123
+ tabIndex: 0,
124
+ role: "group",
125
+ "aria-label": label,
126
+ style: {
127
+ "--timeline-grid-hours": hours,
128
+ "--timeline-grid-columns": columns.length,
129
+ "--timeline-grid-interval": step
130
+ },
131
+ children: /* @__PURE__ */ jsxs("div", { className: "ui-timeline-grid-frame", children: [
132
+ /* @__PURE__ */ jsx("div", { className: "ui-timeline-grid-corner", "aria-hidden": "true" }),
133
+ columns.map((column, index) => /* @__PURE__ */ jsxs(
134
+ "div",
135
+ {
136
+ id: `${uid}-head-${index}`,
137
+ className: "ui-timeline-grid-head",
138
+ "data-current": column.current ? "true" : void 0,
139
+ children: [
140
+ /* @__PURE__ */ jsx("span", { className: "ui-timeline-grid-head-label", children: column.label }),
141
+ column.description === void 0 ? null : /* @__PURE__ */ jsx("span", { className: "ui-timeline-grid-head-description", children: column.description })
142
+ ]
143
+ },
144
+ column.id
145
+ )),
146
+ /* @__PURE__ */ jsx("div", { className: "ui-timeline-grid-axis", "aria-hidden": "true", children: ticks.map((tick) => /* @__PURE__ */ jsx(
147
+ "span",
148
+ {
149
+ className: "ui-timeline-grid-tick",
150
+ style: { "--timeline-grid-tick-offset": tick.offset },
151
+ children: toClock(tick.minutes)
152
+ },
153
+ tick.minutes
154
+ )) }),
155
+ columns.map((column, index) => /* @__PURE__ */ jsxs(
156
+ "ul",
157
+ {
158
+ className: "ui-timeline-grid-column",
159
+ "aria-labelledby": `${uid}-head-${index}`,
160
+ "data-current": column.current ? "true" : void 0,
161
+ children: [
162
+ byColumn[index].map((entry) => renderEvent(entry, axis, onEventSelect)),
163
+ column.current && nowOffset !== null ? /* @__PURE__ */ jsx(
164
+ "li",
165
+ {
166
+ "aria-hidden": "true",
167
+ className: "ui-timeline-grid-now",
168
+ style: { "--timeline-grid-now-offset": nowOffset }
169
+ }
170
+ ) : null
171
+ ]
172
+ },
173
+ column.id
174
+ ))
175
+ ] })
176
+ }
177
+ );
178
+ }
179
+ );
180
+ TimelineGrid.displayName = "TimelineGrid";
181
+ function renderEvent(entry, axis, onEventSelect) {
182
+ const { event } = entry;
183
+ const from = Math.max(entry.startMin, axis.startMin);
184
+ const to = Math.min(entry.endMin, axis.endMin);
185
+ const clippedStart = entry.startMin < axis.startMin;
186
+ const clippedEnd = entry.endMin > axis.endMin;
187
+ const clipped = clippedStart && clippedEnd ? "both" : clippedStart ? "start" : "end";
188
+ const range = `${toClock(entry.startMin)}\u2013${toClock(entry.endMin)}`;
189
+ const body = /* @__PURE__ */ jsxs(Fragment, { children: [
190
+ /* @__PURE__ */ jsx("span", { className: "ui-timeline-grid-event-time", children: range }),
191
+ /* @__PURE__ */ jsx("span", { className: "ui-timeline-grid-event-title", children: event.title }),
192
+ event.description === void 0 ? null : /* @__PURE__ */ jsx("span", { className: "ui-timeline-grid-event-description", children: event.description })
193
+ ] });
194
+ return /* @__PURE__ */ jsx(
195
+ "li",
196
+ {
197
+ "data-event-id": event.id,
198
+ "data-clipped": clippedStart || clippedEnd ? clipped : void 0,
199
+ className: "ui-timeline-grid-event",
200
+ style: {
201
+ "--timeline-grid-event-offset": (from - axis.startMin) / 60,
202
+ "--timeline-grid-event-span": (to - from) / 60,
203
+ "--timeline-grid-event-lane": entry.lane,
204
+ "--timeline-grid-event-lanes": entry.lanes,
205
+ ...event.color === void 0 || event.color === "" ? null : { "--timeline-grid-event-color": event.color }
206
+ },
207
+ children: onEventSelect ? /* @__PURE__ */ jsx(
208
+ "button",
209
+ {
210
+ type: "button",
211
+ className: "ui-timeline-grid-event-body ui-focus-ring",
212
+ onClick: () => onEventSelect(event),
213
+ children: body
214
+ }
215
+ ) : /* @__PURE__ */ jsx("span", { className: "ui-timeline-grid-event-body", children: body })
216
+ },
217
+ event.id
218
+ );
219
+ }
220
+ export {
221
+ TimelineGrid
222
+ };
@@ -5,8 +5,7 @@ import { Check, ChevronRight, ChevronsUpDown, Minus, X } from "lucide-react";
5
5
  import { useTranslation } from "../../i18n/use-translation.js";
6
6
  import { cn } from "../../lib/utils.js";
7
7
  import { pickFieldA11y, useFieldIdentity } from "../../lib/field-a11y.js";
8
- import { controlOpenRingClass } from "../../lib/control-styles.js";
9
- import { Button } from "../general/button.js";
8
+ import { controlTriggerClass } from "../../lib/control-styles.js";
10
9
  import { Popover, PopoverContent, PopoverTrigger } from "../data-display/popover.js";
11
10
  import { ScrollArea, ScrollBar } from "../data-display/scroll-area.js";
12
11
  import { Command, CommandInput } from "./command.js";
@@ -238,12 +237,11 @@ function Cascader({
238
237
  return /* @__PURE__ */ jsxs("div", { className: cn("relative", className), children: [
239
238
  /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: handleOpenChange, children: [
240
239
  /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
241
- Button,
240
+ "button",
242
241
  {
243
242
  id,
244
243
  "data-field": fieldA11y["data-field"] ?? identity["data-field"],
245
244
  type: "button",
246
- variant: "outline",
247
245
  role: "combobox",
248
246
  "aria-expanded": open,
249
247
  "aria-haspopup": "listbox",
@@ -251,8 +249,8 @@ function Cascader({
251
249
  ...fieldA11y,
252
250
  disabled,
253
251
  className: cn(
254
- "w-full justify-start font-normal",
255
- controlOpenRingClass,
252
+ controlTriggerClass,
253
+ "w-full justify-start",
256
254
  // Reserve trailing room for the single clear-or-chevron overlay rendered below.
257
255
  "ui-control-trigger-affixed",
258
256
  !displayLabel && "text-muted-foreground"
@@ -19,7 +19,7 @@ const CheckboxRoot = React.forwardRef(({ className, ...props }, ref) => {
19
19
  "data-slot": "checkbox",
20
20
  className: cn(
21
21
  // `disabled:cursor-not-allowed disabled:opacity-50` and the checked fill are DELETED, not
22
- // moved (#319): `.ui-checkbox:disabled, .ui-checkbox[data-disabled]` and
22
+ // moved: `.ui-checkbox:disabled, .ui-checkbox[data-disabled]` and
23
23
  // `.ui-checkbox[data-state="checked"]` in styles/control.css already declare both, reading
24
24
  // --disabled-opacity and --checkbox-checked-background. Utilities are layered AFTER
25
25
  // components in Tailwind v4, so these literals were silently OUTRANKING those knobs — a
@@ -5,8 +5,7 @@ import { ChevronsUpDown, Loader2, X } from "lucide-react";
5
5
  import { useTranslation } from "../../i18n/use-translation.js";
6
6
  import { useFieldIdentity, useFieldNameFallback } from "../../lib/field-a11y.js";
7
7
  import { cn } from "../../lib/utils.js";
8
- import { controlOpenRingClass } from "../../lib/control-styles.js";
9
- import { Button } from "../general/button.js";
8
+ import { controlTriggerClass } from "../../lib/control-styles.js";
10
9
  import { Popover, PopoverContent, PopoverTrigger } from "../data-display/popover.js";
11
10
  import { Command, CommandGroup } from "./command.js";
12
11
  import { Input } from "./input.js";
@@ -240,13 +239,12 @@ function SearchSelect({
240
239
  },
241
240
  children: [
242
241
  /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
243
- Button,
242
+ "button",
244
243
  {
245
244
  id,
246
245
  type: "button",
247
- variant: "outline",
248
246
  role: "combobox",
249
- size,
247
+ "data-size": size === "md" ? void 0 : size,
250
248
  "aria-expanded": open,
251
249
  "aria-controls": open ? listId : void 0,
252
250
  "aria-label": triggerAriaLabel,
@@ -261,8 +259,8 @@ function SearchSelect({
261
259
  "data-field": resolvedField,
262
260
  "data-value": value || void 0,
263
261
  className: cn(
264
- "w-full justify-start font-normal",
265
- controlOpenRingClass,
262
+ controlTriggerClass,
263
+ "w-full justify-start",
266
264
  // Reserve trailing room for the single clear-or-chevron overlay rendered below.
267
265
  "ui-control-trigger-affixed"
268
266
  ),
@@ -5,6 +5,7 @@ import * as SelectPrimitive from "@radix-ui/react-select";
5
5
  import { ChevronDown, ChevronUp, X } from "lucide-react";
6
6
  import { cn } from "../../lib/utils.js";
7
7
  import { controlTriggerClass } from "../../lib/control-styles.js";
8
+ import { useInertHiddenBackground } from "../general/inert-background.js";
8
9
  import {
9
10
  mergeAriaIds,
10
11
  useFieldIdentity,
@@ -141,35 +142,38 @@ const SelectScrollDownButton = React.forwardRef(({ className, ...props }, ref) =
141
142
  }
142
143
  ));
143
144
  SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
144
- const SelectContent = React.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
145
- SelectPrimitive.Content,
146
- {
147
- ref,
148
- "data-slot": "select-content",
149
- className: cn(
150
- "ui-select-content data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
151
- position === "popper" && "translate-y-1",
152
- className
153
- ),
154
- position,
155
- ...props,
156
- children: [
157
- /* @__PURE__ */ jsx(SelectScrollUpButton, {}),
158
- /* @__PURE__ */ jsx(
159
- SelectPrimitive.Viewport,
160
- {
161
- "data-slot": "select-viewport",
162
- className: cn(
163
- "ui-select-viewport",
164
- position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
165
- ),
166
- children
167
- }
145
+ const SelectContent = React.forwardRef(({ className, children, position = "popper", ...props }, ref) => {
146
+ useInertHiddenBackground();
147
+ return /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
148
+ SelectPrimitive.Content,
149
+ {
150
+ ref,
151
+ "data-slot": "select-content",
152
+ className: cn(
153
+ "ui-select-content data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
154
+ position === "popper" && "translate-y-1",
155
+ className
168
156
  ),
169
- /* @__PURE__ */ jsx(SelectScrollDownButton, {})
170
- ]
171
- }
172
- ) }));
157
+ position,
158
+ ...props,
159
+ children: [
160
+ /* @__PURE__ */ jsx(SelectScrollUpButton, {}),
161
+ /* @__PURE__ */ jsx(
162
+ SelectPrimitive.Viewport,
163
+ {
164
+ "data-slot": "select-viewport",
165
+ className: cn(
166
+ "ui-select-viewport",
167
+ position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
168
+ ),
169
+ children
170
+ }
171
+ ),
172
+ /* @__PURE__ */ jsx(SelectScrollDownButton, {})
173
+ ]
174
+ }
175
+ ) });
176
+ });
173
177
  SelectContent.displayName = SelectPrimitive.Content.displayName;
174
178
  const SelectLabel = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
175
179
  SelectPrimitive.Label,
@@ -5,8 +5,7 @@ import { ChevronDown, ChevronRight, ChevronsUpDown, X } from "lucide-react";
5
5
  import { useTranslation } from "../../i18n/use-translation.js";
6
6
  import { cn } from "../../lib/utils.js";
7
7
  import { pickFieldA11y, useFieldIdentity } from "../../lib/field-a11y.js";
8
- import { controlOpenRingClass } from "../../lib/control-styles.js";
9
- import { Button } from "../general/button.js";
8
+ import { controlTriggerClass } from "../../lib/control-styles.js";
10
9
  import { Popover, PopoverContent, PopoverTrigger } from "../data-display/popover.js";
11
10
  import { ScrollArea } from "../data-display/scroll-area.js";
12
11
  import { Checkbox } from "./checkbox.js";
@@ -177,12 +176,11 @@ function TreeSelectRoot({
177
176
  return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, children: [
178
177
  /* @__PURE__ */ jsxs("div", { className: "relative", children: [
179
178
  /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
180
- Button,
179
+ "button",
181
180
  {
182
181
  id,
183
182
  "data-field": fieldA11y["data-field"] ?? identity["data-field"],
184
183
  type: "button",
185
- variant: "outline",
186
184
  role: "combobox",
187
185
  "aria-expanded": open,
188
186
  "aria-haspopup": "tree",
@@ -190,9 +188,9 @@ function TreeSelectRoot({
190
188
  ...fieldA11y,
191
189
  disabled,
192
190
  className: cn(
193
- "w-full justify-between font-normal",
191
+ controlTriggerClass,
192
+ "w-full justify-between",
194
193
  allowClear && displayKeys.length > 0 && !disabled && "ui-tree-select-trigger-affixed",
195
- controlOpenRingClass,
196
194
  !displayKeys.length && "text-muted-foreground",
197
195
  className
198
196
  ),
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Call from an overlay's content wrapper (the always-mounted portal wrapper, not the portalled
3
+ * content itself). Idempotent and refcounted: one observer serves every overlay on the page.
4
+ */
5
+ export declare function useInertHiddenBackground(): void;