@marimo-team/frontend 0.22.5-dev13 → 0.22.5-dev14
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/assets/JsonOutput-Wyxa3w5g.js +49 -0
- package/dist/assets/{add-connection-dialog-Bu5E77IS.js → add-connection-dialog-D_pURG16.js} +1 -1
- package/dist/assets/{agent-panel-HMju_soU.js → agent-panel-DWJX2Qht.js} +1 -1
- package/dist/assets/{cell-editor-CEok7I_G.js → cell-editor-CBrBKs4-.js} +1 -1
- package/dist/assets/{column-preview-BLzfoQuq.js → column-preview-DH0JBNY2.js} +1 -1
- package/dist/assets/{command-palette-DNvYHUpi.js → command-palette-BtgML0wI.js} +1 -1
- package/dist/assets/{edit-page-DJprVtJ6.js → edit-page-CDmLtIpY.js} +3 -3
- package/dist/assets/{file-explorer-panel-DiNhLdAc.js → file-explorer-panel-BweLqkU2.js} +1 -1
- package/dist/assets/{form-BV-yji2Y.js → form-COrD4PHo.js} +1 -1
- package/dist/assets/{hooks-SmuOPKfj.js → hooks-DG20O34i.js} +1 -1
- package/dist/assets/index-DBs2il8a.css +2 -0
- package/dist/assets/{index-B5beKa_i.js → index-GnShyw0h.js} +4 -4
- package/dist/assets/{layout-BxUONa-J.js → layout-KyEkcuqM.js} +1 -1
- package/dist/assets/{panels-DuR2pNy9.js → panels-DzpbDG4q.js} +1 -1
- package/dist/assets/{run-page-BDV1C8Oi.js → run-page-v7YWM_1t.js} +1 -1
- package/dist/assets/{scratchpad-panel-CWU7CyZh.js → scratchpad-panel-CQj-oV3w.js} +1 -1
- package/dist/assets/{session-panel-B8t0Xymv.js → session-panel-YYyV3RG-.js} +1 -1
- package/dist/assets/{state-WTTs5oP6.js → state-DZeUz07_.js} +1 -1
- package/dist/assets/{useNotebookActions-D2fp_HNm.js → useNotebookActions-CyWR4QZi.js} +1 -1
- package/dist/index.html +7 -6
- package/package.json +1 -1
- package/src/components/data-table/__tests__/columns.test.tsx +92 -13
- package/src/components/data-table/column-header.tsx +81 -56
- package/src/components/data-table/columns.tsx +25 -32
- package/src/components/data-table/data-table.tsx +8 -1
- package/src/components/data-table/renderers.tsx +19 -6
- package/src/components/data-table/types.ts +4 -0
- package/dist/assets/JsonOutput-Dl2dfhmz.js +0 -49
- package/dist/assets/index-BNN_F0CC.css +0 -2
|
@@ -2,7 +2,13 @@
|
|
|
2
2
|
"use no memo";
|
|
3
3
|
|
|
4
4
|
import type { Column, Table } from "@tanstack/react-table";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
EllipsisIcon,
|
|
7
|
+
FilterIcon,
|
|
8
|
+
MinusIcon,
|
|
9
|
+
TextIcon,
|
|
10
|
+
XIcon,
|
|
11
|
+
} from "lucide-react";
|
|
6
12
|
import { useMemo, useRef, useState } from "react";
|
|
7
13
|
import { useLocale } from "react-aria";
|
|
8
14
|
import {
|
|
@@ -69,7 +75,7 @@ interface DataTableColumnHeaderProps<
|
|
|
69
75
|
> extends React.HTMLAttributes<HTMLDivElement> {
|
|
70
76
|
column: Column<TData, TValue>;
|
|
71
77
|
header: React.ReactNode;
|
|
72
|
-
|
|
78
|
+
subheader?: React.ReactNode;
|
|
73
79
|
calculateTopKRows?: CalculateTopKRows;
|
|
74
80
|
table?: Table<TData>;
|
|
75
81
|
}
|
|
@@ -77,7 +83,7 @@ interface DataTableColumnHeaderProps<
|
|
|
77
83
|
export const DataTableColumnHeader = <TData, TValue>({
|
|
78
84
|
column,
|
|
79
85
|
header,
|
|
80
|
-
|
|
86
|
+
subheader,
|
|
81
87
|
className,
|
|
82
88
|
calculateTopKRows,
|
|
83
89
|
table,
|
|
@@ -92,49 +98,51 @@ export const DataTableColumnHeader = <TData, TValue>({
|
|
|
92
98
|
|
|
93
99
|
// No sorting or filtering
|
|
94
100
|
if (!column.getCanSort() && !column.getCanFilter()) {
|
|
95
|
-
return
|
|
101
|
+
return (
|
|
102
|
+
<div className={cn(className)}>
|
|
103
|
+
{header}
|
|
104
|
+
{subheader}
|
|
105
|
+
</div>
|
|
106
|
+
);
|
|
96
107
|
}
|
|
97
108
|
|
|
98
109
|
const hasFilter = column.getFilterValue() !== undefined;
|
|
99
|
-
const hideIcon = !column.getIsSorted() && !hasFilter;
|
|
100
110
|
|
|
101
111
|
return (
|
|
102
112
|
<>
|
|
103
|
-
<
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
>
|
|
121
|
-
{
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
</DropdownMenuContent>
|
|
137
|
-
</DropdownMenu>
|
|
113
|
+
<div
|
|
114
|
+
className={cn("group flex flex-col my-1 w-full select-none", className)}
|
|
115
|
+
>
|
|
116
|
+
<div className="flex items-center gap-1">
|
|
117
|
+
<span>{header}</span>
|
|
118
|
+
{column.getCanSort() && <SortButton column={column} />}
|
|
119
|
+
<DropdownMenu modal={false}>
|
|
120
|
+
<DropdownMenuTrigger asChild={true}>
|
|
121
|
+
<button
|
|
122
|
+
type="button"
|
|
123
|
+
className="inline-flex items-center justify-center h-5 w-5 rounded hover:bg-(--slate-4) text-muted-foreground opacity-0 group-hover:opacity-100 focus:opacity-100 group-focus-within:opacity-100 data-[state=open]:opacity-100 data-[state=open]:text-accent-foreground"
|
|
124
|
+
aria-label="Column options"
|
|
125
|
+
data-testid="data-table-column-menu-button"
|
|
126
|
+
>
|
|
127
|
+
<EllipsisIcon className="h-3.5 w-3.5" />
|
|
128
|
+
</button>
|
|
129
|
+
</DropdownMenuTrigger>
|
|
130
|
+
<DropdownMenuContent align="start">
|
|
131
|
+
{renderDataType(column)}
|
|
132
|
+
{renderSorts(column, table)}
|
|
133
|
+
{renderCopyColumn(column)}
|
|
134
|
+
{renderColumnPinning(column)}
|
|
135
|
+
{renderColumnWrapping(column)}
|
|
136
|
+
{renderFormatOptions(column, locale)}
|
|
137
|
+
<DropdownMenuSeparator />
|
|
138
|
+
{renderMenuItemFilter(column)}
|
|
139
|
+
{renderFilterByValues(column, setIsFilterValueOpen)}
|
|
140
|
+
{hasFilter && <ClearFilterMenuItem column={column} />}
|
|
141
|
+
</DropdownMenuContent>
|
|
142
|
+
</DropdownMenu>
|
|
143
|
+
</div>
|
|
144
|
+
{subheader}
|
|
145
|
+
</div>
|
|
138
146
|
{isFilterValueOpen && (
|
|
139
147
|
<PopoverFilterByValues
|
|
140
148
|
setIsFilterValueOpen={setIsFilterValueOpen}
|
|
@@ -146,28 +154,45 @@ export const DataTableColumnHeader = <TData, TValue>({
|
|
|
146
154
|
);
|
|
147
155
|
};
|
|
148
156
|
|
|
149
|
-
|
|
157
|
+
const SortButton = <TData, TValue>({
|
|
150
158
|
column,
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
className,
|
|
154
|
-
}: DataTableColumnHeaderProps<TData, TValue> & {
|
|
155
|
-
summary: React.ReactNode;
|
|
159
|
+
}: {
|
|
160
|
+
column: Column<TData, TValue>;
|
|
156
161
|
}) => {
|
|
162
|
+
const sortDirection = column.getIsSorted();
|
|
163
|
+
|
|
164
|
+
const handleClick = (e: React.MouseEvent) => {
|
|
165
|
+
e.stopPropagation();
|
|
166
|
+
if (!sortDirection) {
|
|
167
|
+
column.toggleSorting(false, true); // asc
|
|
168
|
+
} else if (sortDirection === "asc") {
|
|
169
|
+
column.toggleSorting(true, true); // desc
|
|
170
|
+
} else {
|
|
171
|
+
column.clearSorting();
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
|
|
157
175
|
return (
|
|
158
|
-
<
|
|
176
|
+
<button
|
|
177
|
+
type="button"
|
|
178
|
+
onClick={handleClick}
|
|
159
179
|
className={cn(
|
|
160
|
-
"flex
|
|
161
|
-
|
|
180
|
+
"inline-flex items-center justify-center h-5 w-5 rounded hover:bg-(--slate-4)",
|
|
181
|
+
sortDirection
|
|
182
|
+
? "text-accent-foreground"
|
|
183
|
+
: "text-muted-foreground opacity-0 group-hover:opacity-100 focus:opacity-100 group-focus-within:opacity-100",
|
|
162
184
|
)}
|
|
185
|
+
aria-label={
|
|
186
|
+
sortDirection === "asc"
|
|
187
|
+
? "Sorted ascending, click to sort descending"
|
|
188
|
+
: sortDirection === "desc"
|
|
189
|
+
? "Sorted descending, click to clear sort"
|
|
190
|
+
: "Sort column ascending"
|
|
191
|
+
}
|
|
192
|
+
data-testid="data-table-sort-button"
|
|
163
193
|
>
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
header={header}
|
|
167
|
-
className={className}
|
|
168
|
-
/>
|
|
169
|
-
{summary}
|
|
170
|
-
</div>
|
|
194
|
+
{renderSortFilterIcon(column)}
|
|
195
|
+
</button>
|
|
171
196
|
);
|
|
172
197
|
};
|
|
173
198
|
|
|
@@ -206,41 +206,30 @@ export function generateColumns<T>({
|
|
|
206
206
|
</div>
|
|
207
207
|
) : null;
|
|
208
208
|
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
const headerWithType = (
|
|
212
|
-
<div
|
|
209
|
+
const headerName = (
|
|
210
|
+
<span
|
|
213
211
|
className={cn(
|
|
214
|
-
"
|
|
215
|
-
|
|
216
|
-
justify === "right" && "items-end",
|
|
212
|
+
"font-bold",
|
|
213
|
+
headerTitle && "underline decoration-dotted",
|
|
217
214
|
)}
|
|
218
215
|
>
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
"font-bold",
|
|
222
|
-
headerTitle && "underline decoration-dotted",
|
|
223
|
-
)}
|
|
224
|
-
>
|
|
225
|
-
{key === "" ? " " : key}
|
|
226
|
-
</span>
|
|
227
|
-
{dtypeHeader}
|
|
228
|
-
</div>
|
|
216
|
+
{key === "" ? " " : key}
|
|
217
|
+
</span>
|
|
229
218
|
);
|
|
230
219
|
|
|
231
220
|
const headerWithTooltip = headerTitle ? (
|
|
232
221
|
<Tooltip content={headerTitle} delayDuration={300}>
|
|
233
|
-
{
|
|
222
|
+
{headerName}
|
|
234
223
|
</Tooltip>
|
|
235
224
|
) : (
|
|
236
|
-
|
|
225
|
+
headerName
|
|
237
226
|
);
|
|
238
227
|
|
|
239
228
|
const dataTableColumnHeader = (
|
|
240
229
|
<DataTableColumnHeader
|
|
241
230
|
header={headerWithTooltip}
|
|
231
|
+
subheader={dtypeHeader}
|
|
242
232
|
column={column}
|
|
243
|
-
justify={justify}
|
|
244
233
|
calculateTopKRows={calculateTopKRows}
|
|
245
234
|
table={table}
|
|
246
235
|
/>
|
|
@@ -255,8 +244,6 @@ export function generateColumns<T>({
|
|
|
255
244
|
<div
|
|
256
245
|
className={cn(
|
|
257
246
|
"flex flex-col h-full pt-0.5 pb-3 justify-between items-start",
|
|
258
|
-
justify === "center" && "items-center",
|
|
259
|
-
justify === "right" && "items-end",
|
|
260
247
|
)}
|
|
261
248
|
>
|
|
262
249
|
{dataTableColumnHeader}
|
|
@@ -283,13 +270,13 @@ export function generateColumns<T>({
|
|
|
283
270
|
|
|
284
271
|
const dataType = column.columnDef.meta?.dataType;
|
|
285
272
|
const isNumeric = dataType === "number" || dataType === "integer";
|
|
286
|
-
const cellStyles = getCellStyleClass(
|
|
273
|
+
const cellStyles = getCellStyleClass({
|
|
287
274
|
justify,
|
|
288
275
|
wrapped,
|
|
289
276
|
canSelectCell,
|
|
290
|
-
isCellSelected,
|
|
277
|
+
isSelected: isCellSelected,
|
|
291
278
|
isNumeric,
|
|
292
|
-
);
|
|
279
|
+
});
|
|
293
280
|
|
|
294
281
|
const renderedCell = renderCellValue({
|
|
295
282
|
column,
|
|
@@ -448,13 +435,19 @@ function getFilterTypeForFieldType(
|
|
|
448
435
|
}
|
|
449
436
|
}
|
|
450
437
|
|
|
451
|
-
function getCellStyleClass(
|
|
452
|
-
justify
|
|
453
|
-
wrapped
|
|
454
|
-
canSelectCell
|
|
455
|
-
isSelected
|
|
456
|
-
isNumeric
|
|
457
|
-
|
|
438
|
+
function getCellStyleClass({
|
|
439
|
+
justify = "left",
|
|
440
|
+
wrapped,
|
|
441
|
+
canSelectCell,
|
|
442
|
+
isSelected,
|
|
443
|
+
isNumeric = false,
|
|
444
|
+
}: {
|
|
445
|
+
justify: "left" | "center" | "right" | undefined;
|
|
446
|
+
wrapped: boolean | undefined;
|
|
447
|
+
canSelectCell: boolean;
|
|
448
|
+
isSelected: boolean;
|
|
449
|
+
isNumeric?: boolean;
|
|
450
|
+
}): string {
|
|
458
451
|
return cn(
|
|
459
452
|
canSelectCell && "cursor-pointer",
|
|
460
453
|
isSelected &&
|
|
@@ -47,6 +47,7 @@ import { DataTableBody, renderTableHeader } from "./renderers";
|
|
|
47
47
|
import { TableBottomBar } from "./TableBottomBar";
|
|
48
48
|
import { TableTopBar } from "./TableTopBar";
|
|
49
49
|
import {
|
|
50
|
+
AUTO_WIDTH_MAX_COLUMNS,
|
|
50
51
|
type DataTableSelection,
|
|
51
52
|
MIN_ROWS_TO_VIRTUALIZE,
|
|
52
53
|
type TooManyRows,
|
|
@@ -300,7 +301,13 @@ const DataTableInternal = <TData,>({
|
|
|
300
301
|
isAnyPanelOpen={isAnyPanelOpen}
|
|
301
302
|
downloadAs={downloadAs}
|
|
302
303
|
/>
|
|
303
|
-
<Table
|
|
304
|
+
<Table
|
|
305
|
+
className={cn(
|
|
306
|
+
"relative",
|
|
307
|
+
columns.length <= AUTO_WIDTH_MAX_COLUMNS ? "w-auto" : "w-full",
|
|
308
|
+
)}
|
|
309
|
+
ref={tableRef}
|
|
310
|
+
>
|
|
304
311
|
{showLoadingBar && (
|
|
305
312
|
<thead className="absolute top-0 left-0 h-[3px] w-1/2 bg-primary animate-slide" />
|
|
306
313
|
)}
|
|
@@ -27,7 +27,7 @@ import { DataTableContextMenu } from "./context-menu";
|
|
|
27
27
|
import { CellRangeSelectionIndicator } from "./range-focus/cell-selection-indicator";
|
|
28
28
|
import { useCellRangeSelection } from "./range-focus/use-cell-range-selection";
|
|
29
29
|
import { useScrollIntoViewOnFocus } from "./range-focus/use-scroll-into-view";
|
|
30
|
-
import { TABLE_ROW_HEIGHT_PX } from "./types";
|
|
30
|
+
import { AUTO_WIDTH_MAX_COLUMNS, TABLE_ROW_HEIGHT_PX } from "./types";
|
|
31
31
|
import { stringifyUnknownValue } from "./utils";
|
|
32
32
|
|
|
33
33
|
export function renderTableHeader<TData>(
|
|
@@ -46,7 +46,7 @@ export function renderTableHeader<TData>(
|
|
|
46
46
|
<TableHead
|
|
47
47
|
key={header.id}
|
|
48
48
|
className={cn(
|
|
49
|
-
"h-auto min-h-10 whitespace-pre align-top",
|
|
49
|
+
"h-auto min-h-10 whitespace-pre align-top border-r border-r-border/75",
|
|
50
50
|
className,
|
|
51
51
|
)}
|
|
52
52
|
style={style}
|
|
@@ -69,6 +69,13 @@ export function renderTableHeader<TData>(
|
|
|
69
69
|
{renderHeaderGroup(table.getLeftHeaderGroups())}
|
|
70
70
|
{renderHeaderGroup(table.getCenterHeaderGroups())}
|
|
71
71
|
{renderHeaderGroup(table.getRightHeaderGroups())}
|
|
72
|
+
{table.getAllColumns().length <= AUTO_WIDTH_MAX_COLUMNS && (
|
|
73
|
+
<th
|
|
74
|
+
className="w-full border-0"
|
|
75
|
+
aria-hidden="true"
|
|
76
|
+
role="presentation"
|
|
77
|
+
/>
|
|
78
|
+
)}
|
|
72
79
|
</TableRow>
|
|
73
80
|
</TableHeader>
|
|
74
81
|
);
|
|
@@ -163,7 +170,7 @@ export const DataTableBody = <TData,>({
|
|
|
163
170
|
{...getCellDomProps(cell.id)}
|
|
164
171
|
key={cell.id}
|
|
165
172
|
className={cn(
|
|
166
|
-
"whitespace-pre truncate max-w-[300px] outline-hidden",
|
|
173
|
+
"whitespace-pre truncate max-w-[300px] outline-hidden border-r border-r-border/75",
|
|
167
174
|
cell.column.getColumnWrapping &&
|
|
168
175
|
cell.column.getColumnWrapping?.() === "wrap" &&
|
|
169
176
|
COLUMN_WRAPPING_STYLES,
|
|
@@ -230,15 +237,21 @@ export const DataTableBody = <TData,>({
|
|
|
230
237
|
{renderCells(row.getLeftVisibleCells())}
|
|
231
238
|
{renderCells(row.getCenterVisibleCells())}
|
|
232
239
|
{renderCells(row.getRightVisibleCells())}
|
|
240
|
+
{columns.length <= AUTO_WIDTH_MAX_COLUMNS && (
|
|
241
|
+
<td className="border-0" aria-hidden="true" role="presentation" />
|
|
242
|
+
)}
|
|
233
243
|
</TableRow>
|
|
234
244
|
);
|
|
235
245
|
};
|
|
236
246
|
|
|
247
|
+
const hasFillerColumn = columns.length <= AUTO_WIDTH_MAX_COLUMNS;
|
|
248
|
+
const totalColSpan = columns.length + (hasFillerColumn ? 1 : 0);
|
|
249
|
+
|
|
237
250
|
const renderRows = () => {
|
|
238
251
|
if (rows.length === 0) {
|
|
239
252
|
return (
|
|
240
253
|
<TableRow>
|
|
241
|
-
<TableCell colSpan={
|
|
254
|
+
<TableCell colSpan={totalColSpan} className="h-24 text-center">
|
|
242
255
|
No results.
|
|
243
256
|
</TableCell>
|
|
244
257
|
</TableRow>
|
|
@@ -255,7 +268,7 @@ export const DataTableBody = <TData,>({
|
|
|
255
268
|
data-virtual-spacer=""
|
|
256
269
|
style={{ height: virtualItems[0].start }}
|
|
257
270
|
>
|
|
258
|
-
<td colSpan={
|
|
271
|
+
<td colSpan={totalColSpan} />
|
|
259
272
|
</tr>
|
|
260
273
|
)}
|
|
261
274
|
{virtualItems.map((vItem) => renderRow(rows[vItem.index]))}
|
|
@@ -266,7 +279,7 @@ export const DataTableBody = <TData,>({
|
|
|
266
279
|
height: totalSize - (virtualItems.at(-1)?.end ?? totalSize),
|
|
267
280
|
}}
|
|
268
281
|
>
|
|
269
|
-
<td colSpan={
|
|
282
|
+
<td colSpan={totalColSpan} />
|
|
270
283
|
</tr>
|
|
271
284
|
)}
|
|
272
285
|
</>
|
|
@@ -16,6 +16,10 @@ declare module "@tanstack/react-table" {
|
|
|
16
16
|
export const TABLE_ROW_HEIGHT_PX = 24;
|
|
17
17
|
export const TABLE_HEADER_HEIGHT_PX = 40;
|
|
18
18
|
|
|
19
|
+
// Below this column count, the table uses w-auto with a filler column
|
|
20
|
+
// to prevent columns from stretching unnecessarily
|
|
21
|
+
export const AUTO_WIDTH_MAX_COLUMNS = 4;
|
|
22
|
+
|
|
19
23
|
// Default number of visible rows when virtualizing without an explicit maxHeight.
|
|
20
24
|
export const DEFAULT_VIRTUAL_ROWS = 15;
|
|
21
25
|
|