gridsmith-ui 0.17.3 → 0.18.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/README.md +19 -13
- package/dist/index.d.ts +9 -2
- package/dist/index.js +148 -60
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# gridsmith-ui
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
103 React components with a CSS custom property theming engine, 13 chart components, and 5 icon packs.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
|
-
gridsmith-ui needs React 19. On a React 18 project, upgrade `react`, `react-dom`, `@types/react` and `@types/react-dom` to 19 first; installing with `--force` or `--legacy-peer-deps` gives an app that cannot run.
|
|
7
|
+
gridsmith-ui needs React 19. On a React 18 project, upgrade `react`, `react-dom`, `@types/react` and `@types/react-dom` to 19 first; installing with `--force` or `--legacy-peer-deps` gives an app that cannot run. On React 18, `AppShell` and `PageContainer` stop with an error that says so.
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
10
|
npm install gridsmith-ui
|
|
@@ -18,7 +18,7 @@ npm install recharts
|
|
|
18
18
|
|
|
19
19
|
### Heavy components (optional)
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
Seven components depend on large third-party libraries. They live in six entry points of their own so the main bundle stays small — install the library only if you use the component:
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
24
|
npm install @tiptap/react @tiptap/pm @tiptap/starter-kit lowlight \
|
|
@@ -28,18 +28,19 @@ npm install @tiptap/react @tiptap/pm @tiptap/starter-kit lowlight \
|
|
|
28
28
|
@tiptap/extension-table @tiptap/extension-table-cell @tiptap/extension-table-header \
|
|
29
29
|
@tiptap/extension-table-row @tiptap/extension-task-item @tiptap/extension-task-list \
|
|
30
30
|
@tiptap/extension-text-align @tiptap/extension-text-style @tiptap/extension-underline # gridsmith-ui/editor
|
|
31
|
+
npm install react-markdown remark-gfm # gridsmith-ui/chat
|
|
31
32
|
npm install @dnd-kit/core @dnd-kit/sortable @dnd-kit/utilities # gridsmith-ui/kanban
|
|
32
33
|
npm install @tanstack/react-table # gridsmith-ui/data-grid
|
|
33
34
|
npm install @tanstack/react-virtual # gridsmith-ui/virtualized
|
|
34
35
|
npm install react-resizable-panels # gridsmith-ui/resizable
|
|
35
36
|
```
|
|
36
37
|
|
|
37
|
-
### Icon packs
|
|
38
|
+
### Icon packs
|
|
38
39
|
|
|
39
|
-
The `Icon` component uses `lucide-react` by default. Other packs are loaded on demand — install only what you need:
|
|
40
|
+
The `Icon` component uses `lucide-react` by default, and every other pack falls back to it for a missing name, so always install it. Other packs are loaded on demand — install only what you need:
|
|
40
41
|
|
|
41
42
|
```bash
|
|
42
|
-
# Default (
|
|
43
|
+
# Default and fallback (required)
|
|
43
44
|
npm install lucide-react
|
|
44
45
|
|
|
45
46
|
# Alternative packs
|
|
@@ -58,11 +59,13 @@ Import the design tokens and Tailwind bindings in your CSS entry point:
|
|
|
58
59
|
@import "gridsmith-ui/roots.css";
|
|
59
60
|
@import "gridsmith-ui/tokens.css";
|
|
60
61
|
@import "gridsmith-ui/tailwind.css";
|
|
62
|
+
@source "../node_modules/gridsmith-ui/dist/*.js";
|
|
61
63
|
```
|
|
62
64
|
|
|
63
65
|
- **roots.css** — the CSS custom property engine (L1 roots, L2 derived scales, L3 semantics)
|
|
64
|
-
- **tokens.css** — default Precision Minimal preset values (
|
|
66
|
+
- **tokens.css** — default Precision Minimal preset values (a Gridsmith project imports the `tokens_css` from `get_tokens` called with `css: true`, saved as `gridsmith-tokens.css`, instead)
|
|
65
67
|
- **tailwind.css** — maps `--ds-*` tokens to Tailwind utilities (`p-ds-4`, `text-2xs`, `opacity-disabled`)
|
|
68
|
+
- **@source** — tells Tailwind to scan the compiled components; without it they render unstyled. It must come after every `@import`.
|
|
66
69
|
|
|
67
70
|
For the fastest icon rendering on cold loads, call `preloadPack` in your entry point before `createRoot`:
|
|
68
71
|
|
|
@@ -75,13 +78,14 @@ preloadPack("lucide");
|
|
|
75
78
|
|
|
76
79
|
| Import path | What it provides |
|
|
77
80
|
|---|---|
|
|
78
|
-
| `gridsmith-ui` |
|
|
79
|
-
| `gridsmith-ui/charts` |
|
|
81
|
+
| `gridsmith-ui` | 96 components (actions, forms, data display, navigation, overlays, feedback, layout) |
|
|
82
|
+
| `gridsmith-ui/charts` | 13 chart components (Bar, Line, Area, Pie, Composite, Scatter, Radar, RadialBar, Funnel, Treemap, Sankey, Sparkline, ChartContainer) + `useChartColors` hook + `categoryKind()` |
|
|
80
83
|
| `gridsmith-ui/editor` | `RichTextEditor` (requires the tiptap packages + `lowlight`) |
|
|
84
|
+
| `gridsmith-ui/chat` | `MarkdownContent` for chat replies; pass it to `ChatMessageList`'s `renderContent` (requires `react-markdown` + `remark-gfm`) |
|
|
81
85
|
| `gridsmith-ui/kanban` | `KanbanBoard` (requires `@dnd-kit/*`) |
|
|
82
86
|
| `gridsmith-ui/data-grid` | `DataGrid` (requires `@tanstack/react-table`) |
|
|
83
87
|
| `gridsmith-ui/virtualized` | `VirtualizedList` (requires `@tanstack/react-virtual`) |
|
|
84
|
-
| `gridsmith-ui/resizable` | `ResizablePanelGroup`, `ResizablePanel`, `ResizableHandle` (requires `react-resizable-panels`) |
|
|
88
|
+
| `gridsmith-ui/resizable` | `ResizablePanelGroup`, `ResizablePanel`, `ResizableHandle`, `SplitView` (requires `react-resizable-panels`) |
|
|
85
89
|
| `gridsmith-ui/icons` | `Icon`, `IconProvider`, `preloadPack` — 5 icon packs with two-phase loading |
|
|
86
90
|
| `gridsmith-ui/utils` | `cn()` — className merge utility (clsx + tailwind-merge) |
|
|
87
91
|
| `gridsmith-ui/roots.css` | CSS custom property engine |
|
|
@@ -94,10 +98,11 @@ preloadPack("lucide");
|
|
|
94
98
|
import { Button, Card, Input, Table } from "gridsmith-ui";
|
|
95
99
|
import { BarChart, LineChart } from "gridsmith-ui/charts";
|
|
96
100
|
import { RichTextEditor } from "gridsmith-ui/editor";
|
|
101
|
+
import { MarkdownContent } from "gridsmith-ui/chat";
|
|
97
102
|
import { KanbanBoard } from "gridsmith-ui/kanban";
|
|
98
103
|
import { DataGrid } from "gridsmith-ui/data-grid";
|
|
99
104
|
import { VirtualizedList } from "gridsmith-ui/virtualized";
|
|
100
|
-
import { ResizablePanelGroup, ResizablePanel, ResizableHandle } from "gridsmith-ui/resizable";
|
|
105
|
+
import { ResizablePanelGroup, ResizablePanel, ResizableHandle, SplitView } from "gridsmith-ui/resizable";
|
|
101
106
|
import { Icon, IconProvider } from "gridsmith-ui/icons";
|
|
102
107
|
import { cn } from "gridsmith-ui/utils";
|
|
103
108
|
```
|
|
@@ -134,10 +139,11 @@ Wrap your app in `<IconProvider>` to select a pack:
|
|
|
134
139
|
|
|
135
140
|
| Package | Required |
|
|
136
141
|
|---------|----------|
|
|
137
|
-
| `react` / `react-dom` | Yes (^
|
|
138
|
-
| `lucide-react` |
|
|
142
|
+
| `react` / `react-dom` | Yes (^19) |
|
|
143
|
+
| `lucide-react` | Yes (the default icon pack and every pack's fallback) |
|
|
139
144
|
| `recharts` | Optional (required for `gridsmith-ui/charts`) |
|
|
140
145
|
| `@tiptap/*`, `lowlight` | Optional (required for `gridsmith-ui/editor`) |
|
|
146
|
+
| `react-markdown`, `remark-gfm` | Optional (required for `gridsmith-ui/chat`) |
|
|
141
147
|
| `@dnd-kit/*` | Optional (required for `gridsmith-ui/kanban`) |
|
|
142
148
|
| `@tanstack/react-table` | Optional (required for `gridsmith-ui/data-grid`) |
|
|
143
149
|
| `@tanstack/react-virtual` | Optional (required for `gridsmith-ui/virtualized`) |
|
package/dist/index.d.ts
CHANGED
|
@@ -682,16 +682,23 @@ interface TableProps {
|
|
|
682
682
|
* The table's title inside its frame (Sprint 25.9, A1): a heading in CardTitle's style at CardHeader's inset, in a band
|
|
683
683
|
* above the scroll area that never scrolls sideways, and the `<table>`'s name. Title a table this way or with a
|
|
684
684
|
* SectionHeading above it — the same way as the blocks beside it (page_composition#45). In card mode, and while it
|
|
685
|
-
* loads or is empty there, the same heading sits above the cards. Without a title the
|
|
685
|
+
* loads or is empty there, the same heading sits above the cards. Without a title the frame holds the scroll area
|
|
686
|
+
* alone.
|
|
686
687
|
*/
|
|
687
688
|
title?: string;
|
|
688
689
|
/** A line under the title, which also describes the `<table>` (aria-describedby). Needs `title`. */
|
|
689
690
|
description?: string;
|
|
690
691
|
/** The table's buttons, at the end of the title band as in a CardHeader. Needs `title`. */
|
|
691
692
|
actions?: ReactNode;
|
|
693
|
+
/**
|
|
694
|
+
* The table's name for assistive tech when it has no title (a title names a titled one): the `<table>`'s label, and
|
|
695
|
+
* its scroll region's while it scrolls, which is otherwise "Table". Two untitled tables on one page need a name each,
|
|
696
|
+
* or their regions share one (Sprint 25.10 review).
|
|
697
|
+
*/
|
|
698
|
+
"aria-label"?: string;
|
|
692
699
|
ref?: Ref<HTMLDivElement>;
|
|
693
700
|
}
|
|
694
|
-
declare function Table({ columns, data, className, sortable, sortKey: controlledSortKey, sortDir: controlledSortDir, onSortChange, selectable, selectedRows, onSelectionChange, expandable, renderExpanded, density, stickyHeader, striped, loading, emptyState, onRowClick, mobileBreakpoint: mobileBreakpointProp, title, description, actions, ref, }: TableProps): react_jsx_runtime.JSX.Element;
|
|
701
|
+
declare function Table({ columns, data, className, sortable, sortKey: controlledSortKey, sortDir: controlledSortDir, onSortChange, selectable, selectedRows, onSelectionChange, expandable, renderExpanded, density, stickyHeader, striped, loading, emptyState, onRowClick, mobileBreakpoint: mobileBreakpointProp, title, description, actions, "aria-label": ariaLabel, ref, }: TableProps): react_jsx_runtime.JSX.Element;
|
|
695
702
|
|
|
696
703
|
interface CalendarEvent {
|
|
697
704
|
id: string;
|
package/dist/index.js
CHANGED
|
@@ -19,7 +19,7 @@ import { mergeRefs, Slot } from './chunk-7Q46JXSK.js';
|
|
|
19
19
|
import { useLatest } from './chunk-WH2FUFAC.js';
|
|
20
20
|
import { Icon } from './chunk-4LDYR7QP.js';
|
|
21
21
|
import { cn, GLASS_SURFACE, ACTIVE_RING, GLASS_BACKDROP, GLASS_DROPDOWN, lockBodyScroll, unlockBodyScroll, GLASS_OVERLAY, ERROR_ACTIVE_RING, ERROR_FOCUS_RING, FOCUS_RING, ERROR_HAS_FOCUS_RING, HAS_FOCUS_RING, ERROR_FOCUS_WITHIN_RING, FOCUS_WITHIN_RING } from './chunk-4KVFH5KC.js';
|
|
22
|
-
import { createContext, forwardRef, useState, useRef, useCallback, useLayoutEffect, useMemo, useContext, useId, memo, useEffect, useImperativeHandle,
|
|
22
|
+
import { createContext, forwardRef, useState, useRef, useCallback, useLayoutEffect, useMemo, useContext, Children, isValidElement, useId, memo, useEffect, useImperativeHandle, Fragment as Fragment$1, cloneElement, useSyncExternalStore } from 'react';
|
|
23
23
|
import { createPortal, flushSync } from 'react-dom';
|
|
24
24
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
25
25
|
|
|
@@ -4907,6 +4907,17 @@ function MentionInput({
|
|
|
4907
4907
|
)
|
|
4908
4908
|
] });
|
|
4909
4909
|
}
|
|
4910
|
+
var HEADER_LAYOUT = {
|
|
4911
|
+
row: "flex flex-wrap items-start justify-between gap-ds-4",
|
|
4912
|
+
block: "flex flex-col space-y-ds-1.5 grow basis-1/2 [min-width:min-content] break-words",
|
|
4913
|
+
titleLeads: "grow basis-1/2 max-w-max [min-width:min-content] break-words",
|
|
4914
|
+
actions: "flex items-center gap-ds-2 shrink-0 ml-auto",
|
|
4915
|
+
below: "mt-ds-1.5"
|
|
4916
|
+
};
|
|
4917
|
+
function actionsAreBadges(actions) {
|
|
4918
|
+
const items = Children.toArray(actions);
|
|
4919
|
+
return items.length > 0 && items.every((item) => isValidElement(item) && (item.type === Fragment$1 ? actionsAreBadges(item.props.children) : item.type === Badge));
|
|
4920
|
+
}
|
|
4910
4921
|
var CardCtx = createContext(false);
|
|
4911
4922
|
var CardActionCtx = createContext(null);
|
|
4912
4923
|
var CARD_ACTION_ATTR = "data-card-action";
|
|
@@ -5149,7 +5160,7 @@ var CardHeader = forwardRef(function CardHeader2({ className, children, actions,
|
|
|
5149
5160
|
const isCheckable = useContext(CardCtx);
|
|
5150
5161
|
const hasActions = actions || menuItems;
|
|
5151
5162
|
const pl = isCheckable ? "pl-ds-10" : "pl-ds-5";
|
|
5152
|
-
const actionsBlock = hasActions ? /* @__PURE__ */ jsxs("div", { className:
|
|
5163
|
+
const actionsBlock = hasActions ? /* @__PURE__ */ jsxs("div", { className: HEADER_LAYOUT.actions, children: [
|
|
5153
5164
|
actions,
|
|
5154
5165
|
menuItems && /* @__PURE__ */ jsx(
|
|
5155
5166
|
DropdownMenu,
|
|
@@ -5174,8 +5185,20 @@ var CardHeader = forwardRef(function CardHeader2({ className, children, actions,
|
|
|
5174
5185
|
] }) : null;
|
|
5175
5186
|
const iconBlock = icon ? /* @__PURE__ */ jsx("span", { className: "shrink-0 h-10 w-10 rounded-[var(--ds-radius-md)] bg-[var(--ds-bg-muted)] flex items-center justify-center mb-ds-1", children: /* @__PURE__ */ jsx(Icon, { name: icon, size: 20, className: "text-[var(--ds-text-secondary)]" }) }) : null;
|
|
5176
5187
|
if (hasActions) {
|
|
5177
|
-
|
|
5178
|
-
|
|
5188
|
+
const parts = Children.toArray(children);
|
|
5189
|
+
const titleLeads = !iconBlock && !menuItems && actionsAreBadges(actions) && parts.length > 0 && isValidElement(parts[0]) && parts[0].type === CardTitle;
|
|
5190
|
+
if (titleLeads) {
|
|
5191
|
+
const below = parts.slice(1);
|
|
5192
|
+
return /* @__PURE__ */ jsxs("div", { ref, className: cn(pl, "pr-ds-5 pt-ds-4 last:pb-ds-4", className), ...props, children: [
|
|
5193
|
+
/* @__PURE__ */ jsxs("div", { className: HEADER_LAYOUT.row, children: [
|
|
5194
|
+
/* @__PURE__ */ jsx("div", { "data-card-title-row": "", className: HEADER_LAYOUT.titleLeads, children: parts[0] }),
|
|
5195
|
+
actionsBlock
|
|
5196
|
+
] }),
|
|
5197
|
+
below.length > 0 && /* @__PURE__ */ jsx("div", { className: cn("flex flex-col space-y-ds-1.5 break-words", HEADER_LAYOUT.below), children: below })
|
|
5198
|
+
] });
|
|
5199
|
+
}
|
|
5200
|
+
return /* @__PURE__ */ jsx("div", { ref, className: cn(pl, "pr-ds-5 pt-ds-4 last:pb-ds-4", className), ...props, children: /* @__PURE__ */ jsxs("div", { className: HEADER_LAYOUT.row, children: [
|
|
5201
|
+
/* @__PURE__ */ jsxs("div", { className: HEADER_LAYOUT.block, children: [
|
|
5179
5202
|
iconBlock,
|
|
5180
5203
|
children
|
|
5181
5204
|
] }),
|
|
@@ -5599,6 +5622,69 @@ function Accordion({ items, type = "single", defaultOpen = [], open: controlledO
|
|
|
5599
5622
|
] }, i);
|
|
5600
5623
|
}) });
|
|
5601
5624
|
}
|
|
5625
|
+
var EDGE_FADE = 24;
|
|
5626
|
+
var NONE = { left: false, right: false, vertical: false, gutterRight: 0, gutterBottom: 0 };
|
|
5627
|
+
var same = (a, b) => a.left === b.left && a.right === b.right && a.vertical === b.vertical && a.gutterRight === b.gutterRight && a.gutterBottom === b.gutterBottom;
|
|
5628
|
+
function useScrollEdges(options = {}) {
|
|
5629
|
+
const { enabled = true, fade = EDGE_FADE } = options;
|
|
5630
|
+
const elRef = useRef(null);
|
|
5631
|
+
const observer = useRef(null);
|
|
5632
|
+
const borders = useRef({ x: 0, y: 0 });
|
|
5633
|
+
const [state, setState] = useState(NONE);
|
|
5634
|
+
const measure = useCallback(() => {
|
|
5635
|
+
const el = elRef.current;
|
|
5636
|
+
if (!el) return;
|
|
5637
|
+
const next = {
|
|
5638
|
+
left: el.scrollLeft > 1,
|
|
5639
|
+
right: el.scrollLeft + el.clientWidth < el.scrollWidth - 1,
|
|
5640
|
+
vertical: el.scrollHeight > el.clientHeight + 1,
|
|
5641
|
+
gutterRight: Math.max(0, el.offsetWidth - el.clientWidth - borders.current.x),
|
|
5642
|
+
gutterBottom: Math.max(0, el.offsetHeight - el.clientHeight - borders.current.y)
|
|
5643
|
+
};
|
|
5644
|
+
setState((prev) => same(prev, next) ? prev : next);
|
|
5645
|
+
}, []);
|
|
5646
|
+
const ref = useCallback((el) => {
|
|
5647
|
+
observer.current?.disconnect();
|
|
5648
|
+
observer.current = null;
|
|
5649
|
+
elRef.current = el;
|
|
5650
|
+
if (!el || !enabled) return;
|
|
5651
|
+
const style = getComputedStyle(el);
|
|
5652
|
+
const px = (v) => parseFloat(v) || 0;
|
|
5653
|
+
borders.current = { x: px(style.borderLeftWidth) + px(style.borderRightWidth), y: px(style.borderTopWidth) + px(style.borderBottomWidth) };
|
|
5654
|
+
measure();
|
|
5655
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
5656
|
+
const o = new ResizeObserver(measure);
|
|
5657
|
+
o.observe(el);
|
|
5658
|
+
for (const child of Array.from(el.children)) o.observe(child);
|
|
5659
|
+
observer.current = o;
|
|
5660
|
+
}, [enabled, measure]);
|
|
5661
|
+
const shown = enabled && (state.left || state.right);
|
|
5662
|
+
const { gutterRight: gr, gutterBottom: gb } = state;
|
|
5663
|
+
const fadeLayer = `linear-gradient(to right, ${state.left ? `transparent 0, black ${fade}px` : "black 0"}, ${state.right ? `black calc(100% - ${gr + fade}px), transparent calc(100% - ${gr}px)` : "black 100%"})`;
|
|
5664
|
+
const gutters = [gb > 0 && { size: `100% ${gb}px`, position: "0 100%" }, gr > 0 && { size: `${gr}px 100%`, position: "100% 0" }].filter(Boolean);
|
|
5665
|
+
const mask = shown ? {
|
|
5666
|
+
image: [fadeLayer, ...gutters.map(() => "linear-gradient(black, black)")].join(", "),
|
|
5667
|
+
size: ["100% 100%", ...gutters.map((g) => g.size)].join(", "),
|
|
5668
|
+
position: ["0 0", ...gutters.map((g) => g.position)].join(", ")
|
|
5669
|
+
} : void 0;
|
|
5670
|
+
return {
|
|
5671
|
+
/** The scroller's ref (a callback). */
|
|
5672
|
+
ref,
|
|
5673
|
+
/** The scroller itself, for code that moves it. */
|
|
5674
|
+
elRef,
|
|
5675
|
+
measure,
|
|
5676
|
+
/** Content hides past a side edge: the scroller moves sideways, and its edges fade. */
|
|
5677
|
+
overflowing: shown,
|
|
5678
|
+
/** It scrolls either way, sideways or up and down: a keyboard needs to reach it. */
|
|
5679
|
+
scrollable: enabled && (state.left || state.right || state.vertical),
|
|
5680
|
+
edges: { left: state.left, right: state.right },
|
|
5681
|
+
/** For the scroller: `data-fade` names the faded edges ("left", "right" or "left right"), the style masks them. */
|
|
5682
|
+
fadeProps: {
|
|
5683
|
+
"data-fade": shown ? [state.left && "left", state.right && "right"].filter(Boolean).join(" ") : void 0,
|
|
5684
|
+
style: mask ? { maskImage: mask.image, WebkitMaskImage: mask.image, maskSize: mask.size, WebkitMaskSize: mask.size, maskPosition: mask.position, WebkitMaskPosition: mask.position, maskRepeat: "no-repeat", WebkitMaskRepeat: "no-repeat" } : void 0
|
|
5685
|
+
}
|
|
5686
|
+
};
|
|
5687
|
+
}
|
|
5602
5688
|
function TruncCell({ children, lines }) {
|
|
5603
5689
|
const { ref, isTruncated } = useTextOverflow({ lines });
|
|
5604
5690
|
const clampClass = lines === 1 ? "truncate" : `line-clamp-${lines}`;
|
|
@@ -5614,13 +5700,22 @@ var FRAME_CLASSES = cn(
|
|
|
5614
5700
|
"bg-[var(--ds-bg-surface)]"
|
|
5615
5701
|
);
|
|
5616
5702
|
function TableHeading({ title, description, actions, titleId, descriptionId, inset }) {
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5703
|
+
const heading = /* @__PURE__ */ jsx("h3", { id: titleId, className: TITLE_CLASSES, children: title });
|
|
5704
|
+
const actionsBlock = actions ? /* @__PURE__ */ jsx("div", { className: HEADER_LAYOUT.actions, children: actions }) : null;
|
|
5705
|
+
const descriptionText = (className) => description ? /* @__PURE__ */ jsx("p", { id: descriptionId, className: cn(className, "text-sm text-[var(--ds-text-muted)] break-words"), children: description }) : null;
|
|
5706
|
+
return /* @__PURE__ */ jsx("div", { "data-table-heading": "", className: cn(inset ? "px-ds-5 py-ds-4" : "pb-ds-4"), children: actions && !actionsAreBadges(actions) ? /* @__PURE__ */ jsxs("div", { className: HEADER_LAYOUT.row, children: [
|
|
5707
|
+
/* @__PURE__ */ jsxs("div", { className: HEADER_LAYOUT.block, children: [
|
|
5708
|
+
heading,
|
|
5709
|
+
descriptionText()
|
|
5621
5710
|
] }),
|
|
5622
|
-
|
|
5623
|
-
] })
|
|
5711
|
+
actionsBlock
|
|
5712
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5713
|
+
/* @__PURE__ */ jsxs("div", { className: HEADER_LAYOUT.row, children: [
|
|
5714
|
+
/* @__PURE__ */ jsx("div", { className: HEADER_LAYOUT.titleLeads, children: heading }),
|
|
5715
|
+
actionsBlock
|
|
5716
|
+
] }),
|
|
5717
|
+
descriptionText(HEADER_LAYOUT.below)
|
|
5718
|
+
] }) });
|
|
5624
5719
|
}
|
|
5625
5720
|
function Table({
|
|
5626
5721
|
columns,
|
|
@@ -5645,6 +5740,7 @@ function Table({
|
|
|
5645
5740
|
title,
|
|
5646
5741
|
description,
|
|
5647
5742
|
actions,
|
|
5743
|
+
"aria-label": ariaLabel,
|
|
5648
5744
|
ref
|
|
5649
5745
|
}) {
|
|
5650
5746
|
const headingId = useId();
|
|
@@ -5716,6 +5812,8 @@ function Table({
|
|
|
5716
5812
|
const preferences = useShellPreferences();
|
|
5717
5813
|
const mobileBreakpoint = mobileBreakpointProp ?? preferences.tableToCardsBreakpoint ?? "md";
|
|
5718
5814
|
const isMobile = useBreakpoint(mobileBreakpoint);
|
|
5815
|
+
const { ref: scrollRef, measure: measureScroll, scrollable, fadeProps } = useScrollEdges({ enabled: !isMobile });
|
|
5816
|
+
const [regionFocused, setRegionFocused] = useState(false);
|
|
5719
5817
|
const cardHeading = title ? /* @__PURE__ */ jsx(TableHeading, { title, description, actions, titleId, descriptionId, inset: false }) : null;
|
|
5720
5818
|
const titledCards = (body) => /* @__PURE__ */ jsxs("div", { ref, "data-ds-list": "", "data-gs": "table", className: cn("flex flex-col", className), children: [
|
|
5721
5819
|
cardHeading,
|
|
@@ -5766,7 +5864,7 @@ function Table({
|
|
|
5766
5864
|
}) }) });
|
|
5767
5865
|
return title ? titledCards(cards) : cards;
|
|
5768
5866
|
}
|
|
5769
|
-
const table = /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", ...title ? { "aria-labelledby": titleId, "aria-describedby": description ? descriptionId : void 0 } : {}, children: [
|
|
5867
|
+
const table = /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", ...title ? { "aria-labelledby": titleId, "aria-describedby": description ? descriptionId : void 0 } : ariaLabel ? { "aria-label": ariaLabel } : {}, children: [
|
|
5770
5868
|
/* @__PURE__ */ jsx("thead", { className: cn("bg-[var(--ds-bg-muted)]", stickyHeader && "sticky top-0 z-10"), children: /* @__PURE__ */ jsxs("tr", { className: "border-b-[length:var(--ds-divider-width)] border-[var(--ds-border-subtle)]", children: [
|
|
5771
5869
|
selectable && /* @__PURE__ */ jsx("th", { className: cn("w-10", density === "compact" ? "px-ds-2 py-ds-2" : "px-ds-3 py-ds-3"), children: /* @__PURE__ */ jsx(
|
|
5772
5870
|
Checkbox,
|
|
@@ -5917,26 +6015,37 @@ function Table({
|
|
|
5917
6015
|
] }, i);
|
|
5918
6016
|
}) })
|
|
5919
6017
|
] });
|
|
5920
|
-
|
|
5921
|
-
|
|
5922
|
-
|
|
5923
|
-
|
|
5924
|
-
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
|
|
6018
|
+
const region = scrollable || regionFocused ? { tabIndex: 0, role: "region", ...title ? { "aria-labelledby": titleId } : { "aria-label": ariaLabel ?? "Table" } } : {};
|
|
6019
|
+
return /* @__PURE__ */ jsxs(
|
|
6020
|
+
"div",
|
|
6021
|
+
{
|
|
6022
|
+
ref,
|
|
6023
|
+
"data-ds-list": "",
|
|
6024
|
+
"data-gs": "table",
|
|
6025
|
+
className: cn("w-full min-w-0 flex flex-col", FRAME_CLASSES, "has-[>[data-table-scroll]:focus-visible]:shadow-[var(--ds-shadow-focus)]", className),
|
|
6026
|
+
children: [
|
|
6027
|
+
title && /* @__PURE__ */ jsx(TableHeading, { title, description, actions, titleId, descriptionId, inset: true }),
|
|
6028
|
+
/* @__PURE__ */ jsx(
|
|
6029
|
+
"div",
|
|
6030
|
+
{
|
|
6031
|
+
ref: scrollRef,
|
|
6032
|
+
"data-table-scroll": "",
|
|
6033
|
+
onScroll: measureScroll,
|
|
6034
|
+
onFocus: (e) => {
|
|
6035
|
+
if (e.target === e.currentTarget) setRegionFocused(true);
|
|
6036
|
+
},
|
|
6037
|
+
onBlur: (e) => {
|
|
6038
|
+
if (e.target === e.currentTarget) setRegionFocused(false);
|
|
6039
|
+
},
|
|
6040
|
+
...fadeProps,
|
|
6041
|
+
...region,
|
|
6042
|
+
className: cn("min-h-0 overflow-x-auto focus-visible:outline-none", title ? "rounded-b-[inherit]" : "rounded-[inherit]"),
|
|
6043
|
+
children: table
|
|
6044
|
+
}
|
|
6045
|
+
)
|
|
6046
|
+
]
|
|
6047
|
+
}
|
|
6048
|
+
);
|
|
5940
6049
|
}
|
|
5941
6050
|
function optionsKeyForFit(options) {
|
|
5942
6051
|
return options.map((o) => `${o.value}:${o.label}`).join("|");
|
|
@@ -11121,40 +11230,22 @@ function trackClasses(orientation) {
|
|
|
11121
11230
|
"border-b-[length:var(--ds-nav-h-track-border)] border-[var(--ds-border-default)]"
|
|
11122
11231
|
);
|
|
11123
11232
|
}
|
|
11124
|
-
var FADE = 24;
|
|
11125
11233
|
function useScrollTrack(activeTab, enabled) {
|
|
11126
|
-
const ref =
|
|
11127
|
-
const [edges, setEdges] = useState({ left: false, right: false });
|
|
11128
|
-
const measure = useCallback(() => {
|
|
11129
|
-
const el = ref.current;
|
|
11130
|
-
if (!el) return;
|
|
11131
|
-
const left = el.scrollLeft > 1;
|
|
11132
|
-
const right = el.scrollLeft + el.clientWidth < el.scrollWidth - 1;
|
|
11133
|
-
setEdges((prev) => prev.left === left && prev.right === right ? prev : { left, right });
|
|
11134
|
-
}, []);
|
|
11135
|
-
useLayoutEffect(() => {
|
|
11136
|
-
if (!enabled) return;
|
|
11137
|
-
measure();
|
|
11138
|
-
const el = ref.current;
|
|
11139
|
-
if (!el || typeof ResizeObserver === "undefined") return;
|
|
11140
|
-
const observer = new ResizeObserver(measure);
|
|
11141
|
-
observer.observe(el);
|
|
11142
|
-
return () => observer.disconnect();
|
|
11143
|
-
}, [enabled, measure]);
|
|
11234
|
+
const { ref, elRef, measure, fadeProps } = useScrollEdges({ enabled });
|
|
11144
11235
|
useLayoutEffect(() => {
|
|
11145
11236
|
if (!enabled) return;
|
|
11146
|
-
const el =
|
|
11237
|
+
const el = elRef.current;
|
|
11147
11238
|
const tab = el?.querySelector('[role="tab"][aria-selected="true"]');
|
|
11148
11239
|
if (!el || !tab) return;
|
|
11149
11240
|
const start = tab.offsetLeft;
|
|
11150
11241
|
const end = start + tab.offsetWidth;
|
|
11151
|
-
if (start < el.scrollLeft +
|
|
11152
|
-
else if (end > el.scrollLeft + el.clientWidth -
|
|
11242
|
+
if (start < el.scrollLeft + EDGE_FADE) el.scrollLeft = Math.max(0, start - EDGE_FADE);
|
|
11243
|
+
else if (end > el.scrollLeft + el.clientWidth - EDGE_FADE) el.scrollLeft = end - el.clientWidth + EDGE_FADE;
|
|
11153
11244
|
measure();
|
|
11154
|
-
}, [activeTab, enabled, measure]);
|
|
11245
|
+
}, [activeTab, enabled, measure, elRef]);
|
|
11155
11246
|
useEffect(() => {
|
|
11156
11247
|
if (!enabled) return;
|
|
11157
|
-
const el =
|
|
11248
|
+
const el = elRef.current;
|
|
11158
11249
|
if (!el) return;
|
|
11159
11250
|
const onWheel = (e) => {
|
|
11160
11251
|
if (el.scrollWidth <= el.clientWidth || Math.abs(e.deltaY) <= Math.abs(e.deltaX)) return;
|
|
@@ -11204,14 +11295,11 @@ function useScrollTrack(activeTab, enabled) {
|
|
|
11204
11295
|
el.removeEventListener("pointercancel", onUp);
|
|
11205
11296
|
el.removeEventListener("click", onClick, true);
|
|
11206
11297
|
};
|
|
11207
|
-
}, [enabled]);
|
|
11208
|
-
const fade = enabled ? [edges.left && "left", edges.right && "right"].filter(Boolean).join(" ") : "";
|
|
11209
|
-
const mask = fade ? `linear-gradient(to right, ${edges.left ? `transparent 0, black ${FADE}px` : "black 0"}, ${edges.right ? `black calc(100% - ${FADE}px), transparent 100%` : "black 100%"})` : void 0;
|
|
11298
|
+
}, [enabled, elRef]);
|
|
11210
11299
|
return {
|
|
11211
11300
|
ref,
|
|
11212
11301
|
onScroll: measure,
|
|
11213
|
-
|
|
11214
|
-
style: mask ? { maskImage: mask, WebkitMaskImage: mask } : void 0
|
|
11302
|
+
...fadeProps
|
|
11215
11303
|
};
|
|
11216
11304
|
}
|
|
11217
11305
|
var SCROLL_TRACK = "overflow-x-auto overflow-y-hidden [scrollbar-width:none] [&::-webkit-scrollbar]:hidden max-w-full";
|