@alexgorbatchev/shadcn 0.0.16 → 0.0.17
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
CHANGED
|
@@ -52,6 +52,7 @@ Import shared controls and providers from `@alexgorbatchev/shadcn`. Import the f
|
|
|
52
52
|
| `/fonts.css` | JetBrains Mono font stylesheet | `bun add @fontsource-variable/jetbrains-mono@^5.3.0` |
|
|
53
53
|
| `/sonner` | Sonner, SonnerToaster, Toaster, toast, useSonner | No additional install; patched implementation is bundled |
|
|
54
54
|
| `/input-otp` | InputOTP components | No additional install; patched implementation is bundled |
|
|
55
|
+
| `/virtual-table` | VirtualTable and row measurement props | `@tanstack/react-virtual@3.14.13` |
|
|
55
56
|
| `/message-scroller` | MessageScroller components, hooks, and types | No additional install; patched implementation is bundled |
|
|
56
57
|
| `/questionnaire` | Questionnaire components and types | No additional install; patched implementation is bundled |
|
|
57
58
|
|
|
@@ -268,6 +269,8 @@ Compose `QuestionnaireItem`, `QuestionnaireTitle`, and answer controls inside `Q
|
|
|
268
269
|
|
|
269
270
|
`InlineEdit` mounts an edit session with a controlled `value`, `onValueChange`, `onCommit`, and `onCancel`. Mount it in place of the displayed label. Its input focuses and selects its text by default; `shouldSelectOnMount={false}` preserves the initial caret. Enter or blur commits once, Escape cancels without committing, and asynchronous saves disable the input until completion. Rejected saves display an error and allow retry.
|
|
270
271
|
|
|
272
|
+
`VirtualTable` renders a bounded, dynamically measured set of native table rows using TanStack Virtual. Supply `items`, stable `getItemKey`, `header`, `columnCount`, `label`, and `renderRow`; spread the supplied measurement props onto each native row. Optional `columns` accepts a colgroup. `maxHeight` defaults to `65vh`, `estimateRowHeight` to 80 pixels, and `overscan` to five rows. `retainedKeys` keeps editing rows mounted; the focused row is retained automatically. Filtering and selection belong to the consumer.
|
|
273
|
+
|
|
271
274
|
`StarRating` takes a controlled numeric `value` from 0 to 5, `onValueChange`, and an accessible `label`. Its native radio buttons support keyboard selection and an Unrated option to clear the rating. Set `disabled` while saving. `Avatar shape="square"` displays square artwork with matching image, fallback, and border corners.
|
|
272
275
|
|
|
273
276
|
`DialogContent size="wide"` uses a desktop maximum width of 576px; `size="narrow"` uses 192px, and the default uses 384px. Dialogs remain bounded by the viewport. `FieldGroup columns={2}` arranges fields in two columns on desktop and one on mobile.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type ComponentProps, type Key, type ReactElement, type ReactNode } from "react";
|
|
2
|
+
import { Table } from "../Table.js";
|
|
3
|
+
export interface IVirtualTableRowProps {
|
|
4
|
+
ref: (element: HTMLTableRowElement | null) => void;
|
|
5
|
+
"data-index": number;
|
|
6
|
+
"aria-rowindex": number;
|
|
7
|
+
}
|
|
8
|
+
interface IVirtualTableProps<T> {
|
|
9
|
+
items: readonly T[];
|
|
10
|
+
getItemKey: (item: T) => Key;
|
|
11
|
+
renderRow: (item: T, props: IVirtualTableRowProps) => ReactNode;
|
|
12
|
+
header: ReactNode;
|
|
13
|
+
columns?: ReactNode;
|
|
14
|
+
columnCount: number;
|
|
15
|
+
label: string;
|
|
16
|
+
maxHeight?: number | string;
|
|
17
|
+
estimateRowHeight?: number;
|
|
18
|
+
overscan?: number;
|
|
19
|
+
retainedKeys?: readonly Key[];
|
|
20
|
+
layout?: ComponentProps<typeof Table>["layout"];
|
|
21
|
+
}
|
|
22
|
+
export declare function VirtualTable<T>({ items, getItemKey, renderRow, header, columns, columnCount, label, maxHeight, estimateRowHeight, overscan, retainedKeys, layout, }: IVirtualTableProps<T>): ReactElement;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import"../../chunk-cp2m7y7v.js";
|
|
2
|
+
import {
|
|
3
|
+
Table2,
|
|
4
|
+
TableHeader2,
|
|
5
|
+
TableBody2
|
|
6
|
+
} from "../../chunk-0y0kqaqj.js";
|
|
7
|
+
|
|
8
|
+
// src/components/virtual-table/VirtualTable.tsx
|
|
9
|
+
import {
|
|
10
|
+
Fragment,
|
|
11
|
+
useCallback,
|
|
12
|
+
useMemo,
|
|
13
|
+
useRef,
|
|
14
|
+
useState
|
|
15
|
+
} from "react";
|
|
16
|
+
import { defaultRangeExtractor, useVirtualizer } from "@tanstack/react-virtual";
|
|
17
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
18
|
+
function VirtualTable({
|
|
19
|
+
items,
|
|
20
|
+
getItemKey,
|
|
21
|
+
renderRow,
|
|
22
|
+
header,
|
|
23
|
+
columns,
|
|
24
|
+
columnCount,
|
|
25
|
+
label,
|
|
26
|
+
maxHeight = "65vh",
|
|
27
|
+
estimateRowHeight = 80,
|
|
28
|
+
overscan = 5,
|
|
29
|
+
retainedKeys,
|
|
30
|
+
layout = "fixed"
|
|
31
|
+
}) {
|
|
32
|
+
const viewport = useRef(null);
|
|
33
|
+
const [focusedKey, setFocusedKey] = useState();
|
|
34
|
+
const retained = useMemo(() => items.flatMap((item, index) => retainedKeys?.includes(getItemKey(item)) || getItemKey(item) === focusedKey ? [index] : []), [items, retainedKeys, getItemKey, focusedKey]);
|
|
35
|
+
const rangeExtractor = useCallback((range) => [...new Set([...defaultRangeExtractor(range), ...retained])].sort((a, b) => a - b), [retained]);
|
|
36
|
+
const itemKey = useCallback((index) => getItemKey(items[index]), [items, getItemKey]);
|
|
37
|
+
const virtualizer = useVirtualizer({
|
|
38
|
+
count: items.length,
|
|
39
|
+
getScrollElement: () => viewport.current,
|
|
40
|
+
getItemKey: itemKey,
|
|
41
|
+
estimateSize: () => estimateRowHeight,
|
|
42
|
+
overscan,
|
|
43
|
+
rangeExtractor
|
|
44
|
+
});
|
|
45
|
+
const rows = virtualizer.getVirtualItems();
|
|
46
|
+
const last = rows.at(-1);
|
|
47
|
+
return /* @__PURE__ */ jsx("div", {
|
|
48
|
+
ref: viewport,
|
|
49
|
+
"data-testid": "VirtualTable",
|
|
50
|
+
role: "region",
|
|
51
|
+
"aria-label": label,
|
|
52
|
+
tabIndex: 0,
|
|
53
|
+
className: "min-w-0 overflow-auto overscroll-contain",
|
|
54
|
+
style: { maxHeight },
|
|
55
|
+
onFocusCapture: (event) => {
|
|
56
|
+
const row = event.target.closest("tr[data-index]");
|
|
57
|
+
const item = items[Number(row?.dataset.index)];
|
|
58
|
+
if (row && item)
|
|
59
|
+
setFocusedKey(getItemKey(item));
|
|
60
|
+
},
|
|
61
|
+
children: /* @__PURE__ */ jsxs(Table2, {
|
|
62
|
+
layout,
|
|
63
|
+
"aria-label": label,
|
|
64
|
+
"aria-rowcount": items.length + 1,
|
|
65
|
+
children: [
|
|
66
|
+
columns,
|
|
67
|
+
/* @__PURE__ */ jsx(TableHeader2, {
|
|
68
|
+
children: header
|
|
69
|
+
}),
|
|
70
|
+
/* @__PURE__ */ jsxs(TableBody2, {
|
|
71
|
+
children: [
|
|
72
|
+
rows.map((row, index) => /* @__PURE__ */ jsxs(Fragment, {
|
|
73
|
+
children: [
|
|
74
|
+
row.start > (rows[index - 1]?.end ?? 0) && /* @__PURE__ */ jsx("tr", {
|
|
75
|
+
"aria-hidden": "true",
|
|
76
|
+
children: /* @__PURE__ */ jsx("td", {
|
|
77
|
+
colSpan: columnCount,
|
|
78
|
+
style: { height: row.start - (rows[index - 1]?.end ?? 0), padding: 0, border: 0 }
|
|
79
|
+
})
|
|
80
|
+
}),
|
|
81
|
+
renderRow(items[row.index], {
|
|
82
|
+
ref: virtualizer.measureElement,
|
|
83
|
+
"data-index": row.index,
|
|
84
|
+
"aria-rowindex": row.index + 2
|
|
85
|
+
})
|
|
86
|
+
]
|
|
87
|
+
}, row.key)),
|
|
88
|
+
last && virtualizer.getTotalSize() > last.end && /* @__PURE__ */ jsx("tr", {
|
|
89
|
+
"aria-hidden": "true",
|
|
90
|
+
children: /* @__PURE__ */ jsx("td", {
|
|
91
|
+
colSpan: columnCount,
|
|
92
|
+
style: { height: virtualizer.getTotalSize() - last.end, padding: 0, border: 0 }
|
|
93
|
+
})
|
|
94
|
+
})
|
|
95
|
+
]
|
|
96
|
+
})
|
|
97
|
+
]
|
|
98
|
+
})
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
export {
|
|
102
|
+
VirtualTable
|
|
103
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alexgorbatchev/shadcn",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.17",
|
|
4
4
|
"description": "Sharp, monospace React components with compact and comfortable densities.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -988,6 +988,10 @@
|
|
|
988
988
|
"./assistant-ui/native/task-tray": {
|
|
989
989
|
"types": "./dist/components/assistant-ui/native/TaskTray.d.ts",
|
|
990
990
|
"import": "./dist/components/assistant-ui/native/TaskTray.js"
|
|
991
|
+
},
|
|
992
|
+
"./virtual-table": {
|
|
993
|
+
"types": "./dist/components/virtual-table/index.d.ts",
|
|
994
|
+
"import": "./dist/components/virtual-table/index.js"
|
|
991
995
|
}
|
|
992
996
|
},
|
|
993
997
|
"publishConfig": {
|
|
@@ -1105,6 +1109,7 @@
|
|
|
1105
1109
|
"@daypicker/persian": "^10.0.2",
|
|
1106
1110
|
"@daypicker/react": "^10.0.2",
|
|
1107
1111
|
"@fontsource-variable/jetbrains-mono": "^5.3.0",
|
|
1112
|
+
"@tanstack/react-virtual": "^3.14.13",
|
|
1108
1113
|
"anser": "2.3.5",
|
|
1109
1114
|
"beautiful-mermaid": "^1.1.3",
|
|
1110
1115
|
"cmdk": "^1.1.1",
|
|
@@ -1218,6 +1223,9 @@
|
|
|
1218
1223
|
},
|
|
1219
1224
|
"colorjs.io": {
|
|
1220
1225
|
"optional": true
|
|
1226
|
+
},
|
|
1227
|
+
"@tanstack/react-virtual": {
|
|
1228
|
+
"optional": true
|
|
1221
1229
|
}
|
|
1222
1230
|
},
|
|
1223
1231
|
"packageManager": "bun@1.4.2"
|