@marimo-team/frontend 0.23.9-dev3 → 0.23.9-dev4
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/{ConnectedDataExplorerComponent-D-boDGAP.js → ConnectedDataExplorerComponent-B-cElX-s.js} +1 -1
- package/dist/assets/{ImperativeModal-DEC1mXgV.js → ImperativeModal-BeQmePpG.js} +1 -1
- package/dist/assets/JsonOutput-BbkJ4c02.js +53 -0
- package/dist/assets/{MarimoErrorOutput-XWqnhvJ6.js → MarimoErrorOutput-ciRhjJM2.js} +1 -1
- package/dist/assets/{RSPContexts-Bk1r00gJ.js → RSPContexts-Clr6RnG2.js} +1 -1
- package/dist/assets/{RunButton-Dbak5hfa.js → RunButton-DySeQPd2.js} +1 -1
- package/dist/assets/{add-cell-with-ai-BbZkMqv2.js → add-cell-with-ai-DKUeTe86.js} +1 -1
- package/dist/assets/{add-connection-dialog-CzxRpS5F.js → add-connection-dialog-DwDtRq10.js} +1 -1
- package/dist/assets/{agent-panel-zPhlhkYL.js → agent-panel-C8Oqvdr5.js} +1 -1
- package/dist/assets/{ai-model-dropdown-CjhUqXgj.js → ai-model-dropdown-Cv4r0gqY.js} +1 -1
- package/dist/assets/{app-config-button-CCs8Jepz.js → app-config-button-rS9K4BOr.js} +1 -1
- package/dist/assets/{cache-panel-VL13fWgF.js → cache-panel-BSlEbX4w.js} +1 -1
- package/dist/assets/{cell-editor-ODyJXDT8.js → cell-editor-DnLtTvEM.js} +3 -3
- package/dist/assets/{cell-link-PQYiMZw1.js → cell-link-f3ZU0MzW.js} +1 -1
- package/dist/assets/{chat-display-DetTBnqK.js → chat-display-B7QE6C2q.js} +1 -1
- package/dist/assets/{chat-panel-CEgw_vg0.js → chat-panel-D11B2Q4F.js} +1 -1
- package/dist/assets/{chat-ui-D-Y7p_cT.js → chat-ui-BwVhfsq2.js} +1 -1
- package/dist/assets/{chunk-5FQGJX7Z-BSzccEgu.js → chunk-5FQGJX7Z-CbuGydc8.js} +3 -3
- package/dist/assets/{code-block-37QAKDTI-U2R1jyOo.js → code-block-37QAKDTI-BwYRrSJW.js} +1 -1
- package/dist/assets/{column-preview-BLIWbdOX.js → column-preview-8cXBINr1.js} +1 -1
- package/dist/assets/command-DUeag2QH.js +1 -0
- package/dist/assets/{command-palette-CeDe63_W.js → command-palette-BlX5QV4R.js} +1 -1
- package/dist/assets/{common-BaBE_ygg.js → common-DAjN54-N.js} +1 -1
- package/dist/assets/dates-DS_7IZoI.js +1 -0
- package/dist/assets/{dependency-graph-panel-ClI5byUa.js → dependency-graph-panel-C7UdKk5P.js} +1 -1
- package/dist/assets/{dist-CW3rweKM.js → dist-DFHp_ZJR.js} +1 -1
- package/dist/assets/{download-B1QFVDP-.js → download-eMMt69Hd.js} +1 -1
- package/dist/assets/{edit-page-ZFpn8-WM.js → edit-page-DoGaZtlD.js} +6 -6
- package/dist/assets/{error-panel-iXznkJZ1.js → error-panel-CkKXrRna.js} +1 -1
- package/dist/assets/{field-DNlzfMKW.js → field-B4CdIHa9.js} +1 -1
- package/dist/assets/{file-explorer-panel-BVBKF1SH.js → file-explorer-panel-BA0aI_q7.js} +3 -3
- package/dist/assets/{file-name-input-g2H2sY2h.js → file-name-input-BpDnZMOs.js} +1 -1
- package/dist/assets/{form-BjUJP6PJ.js → form-z4S7B_rP.js} +1 -1
- package/dist/assets/{gallery-page-MrZHjySE.js → gallery-page-BXZHpNqZ.js} +1 -1
- package/dist/assets/{glide-data-editor-4Wql6uq7.js → glide-data-editor-C5d_9QYv.js} +1 -1
- package/dist/assets/{home-page-De1W6q6f.js → home-page-Cv2KTtQj.js} +1 -1
- package/dist/assets/{hooks-jWLD3t7P.js → hooks-DeuLZEyD.js} +1 -1
- package/dist/assets/{index-ZA7t2ThT.js → index-B5z1LmJ1.js} +19 -19
- package/dist/assets/index-Cn0RBoFD.css +2 -0
- package/dist/assets/{input-CVE-gIjt.js → input-TSilD7AA.js} +1 -1
- package/dist/assets/{layout-DEU6lX-9.js → layout-Dpf6l72t.js} +3 -3
- package/dist/assets/{logs-panel-BMAfoMJg.js → logs-panel-D6VqVLlw.js} +1 -1
- package/dist/assets/{markdown-renderer-BQ-BQLiJ.js → markdown-renderer-l3qZKrTC.js} +3 -3
- package/dist/assets/mermaid-4DMBBIKO-BlSTFoRU.js +1 -0
- package/dist/assets/{mermaid-CZhfODkT.js → mermaid-BpEU2haB.js} +1 -1
- package/dist/assets/{name-cell-input-bwfAyC0i.js → name-cell-input-BmJb1jcI.js} +1 -1
- package/dist/assets/{packages-panel-B3dRYuRM.js → packages-panel-BQJiodre.js} +1 -1
- package/dist/assets/{panels-DWhhEgv4.js → panels-EIGgrBT7.js} +1 -1
- package/dist/assets/{radio-group-rsi1ibXY.js → radio-group-BE0Xe9G9.js} +1 -1
- package/dist/assets/{readonly-python-code-BKYj8PNf.js → readonly-python-code-D3NHaVFs.js} +1 -1
- package/dist/assets/{reveal-component-DNpBzX6F.js → reveal-component-Cy-Nz7JR.js} +1 -1
- package/dist/assets/{run-page-CO2X6wso.js → run-page-IrSzOAx3.js} +1 -1
- package/dist/assets/{scratchpad-panel-CWfddArs.js → scratchpad-panel-DVSB-2ZU.js} +1 -1
- package/dist/assets/{secrets-panel-DqHGq3V8.js → secrets-panel-EMyfZ0xi.js} +1 -1
- package/dist/assets/{session-panel-BP0QxaoM.js → session-panel-BSP3VdpL.js} +1 -1
- package/dist/assets/{snippets-panel-DFJd1ui5.js → snippets-panel-WZ7ZOs2t.js} +1 -1
- package/dist/assets/{state-dx303w7J.js → state-CcAGAozT.js} +1 -1
- package/dist/assets/{state-BXNNuw9g.js → state-KI8ENp1g.js} +1 -1
- package/dist/assets/{tracing-BQU8fBDM.js → tracing-CXaM8i7_.js} +1 -1
- package/dist/assets/{tracing-panel-DEVpyGX3.js → tracing-panel-DcR9ni3B.js} +2 -2
- package/dist/assets/{useCellActionButton-QaDO24oW.js → useCellActionButton-p6Ij9yyu.js} +1 -1
- package/dist/assets/{useDependencyPanelTab-BB_XeSAg.js → useDependencyPanelTab-KDzMTAUI.js} +1 -1
- package/dist/assets/{useNotebookActions-CJEicFed.js → useNotebookActions-COhd0Ld9.js} +1 -1
- package/dist/assets/{vega-component-C9fDGx86.js → vega-component-BGC-ewWV.js} +1 -1
- package/dist/assets/{write-secret-modal-Liv_9MXS.js → write-secret-modal-DcmyNRRr.js} +1 -1
- package/dist/index.html +27 -27
- package/package.json +1 -1
- package/src/components/data-table/__tests__/column-header.test.tsx +110 -277
- package/src/components/data-table/__tests__/date-filter-inputs.test.tsx +33 -0
- package/src/components/data-table/__tests__/filter-pill-editor.test.tsx +75 -38
- package/src/components/data-table/__tests__/filter-pills.test.tsx +287 -0
- package/src/components/data-table/__tests__/filter-test-utils.ts +47 -0
- package/src/components/data-table/__tests__/filters.test.ts +5 -5
- package/src/components/data-table/add-filter-button.tsx +85 -0
- package/src/components/data-table/column-header.tsx +92 -691
- package/src/components/data-table/context-menu.tsx +26 -12
- package/src/components/data-table/data-table.tsx +89 -57
- package/src/components/data-table/date-filter-inputs.tsx +13 -10
- package/src/components/data-table/filter-by-values-picker.tsx +13 -19
- package/src/components/data-table/filter-editor-context.tsx +34 -0
- package/src/components/data-table/filter-pill-editor.tsx +152 -175
- package/src/components/data-table/filter-pills.tsx +190 -153
- package/src/components/data-table/filters/builders.ts +102 -0
- package/src/components/data-table/filters/defaults.ts +31 -0
- package/src/components/data-table/filters/format.ts +131 -0
- package/src/components/data-table/filters/guards.ts +51 -0
- package/src/components/data-table/filters/index.ts +7 -0
- package/src/components/data-table/filters/operators.ts +76 -0
- package/src/components/data-table/filters/serialize.ts +186 -0
- package/src/components/data-table/filters/types.ts +33 -0
- package/src/components/data-table/header-items.tsx +6 -83
- package/src/components/data-table/value-chips.tsx +52 -0
- package/src/components/ui/number-field.tsx +13 -1
- package/src/utils/dates.ts +39 -0
- package/dist/assets/JsonOutput-05-R3eil.js +0 -53
- package/dist/assets/command-2NPJCYDa.js +0 -1
- package/dist/assets/dates-DI1TvEEK.js +0 -1
- package/dist/assets/index-B30qjBZM.css +0 -2
- package/dist/assets/mermaid-4DMBBIKO-C0OyyVdo.js +0 -1
- package/src/components/data-table/__tests__/column-header.test.ts +0 -65
- package/src/components/data-table/filters.ts +0 -386
- /package/dist/assets/{focus-BaOnnMs-.js → focus-BLb-92ed.js} +0 -0
- /package/dist/assets/{formats-BRq458WH.js → formats-DP_z0P-n.js} +0 -0
- /package/dist/assets/{html-to-image-D6SgvARi.js → html-to-image-Ctd6Wpty.js} +0 -0
- /package/dist/assets/{micromark-factory-space-BUQpMdx2.js → micromark-factory-space-bqhKsQDn.js} +0 -0
- /package/dist/assets/{react-resizable-panels.browser.esm-Ce2ksurd.js → react-resizable-panels.browser.esm-BdtIs0E-.js} +0 -0
- /package/dist/assets/{table-DQE9hQzM.js → table-Bgc-inJs.js} +0 -0
- /package/dist/assets/{useAsyncData-C5i0IRVM.js → useAsyncData-Dg8E_bPh.js} +0 -0
|
@@ -16,7 +16,11 @@ import {
|
|
|
16
16
|
ContextMenuTrigger,
|
|
17
17
|
} from "../ui/context-menu";
|
|
18
18
|
import { DATA_CELL_ID } from "./cell-utils";
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
Filter,
|
|
21
|
+
isMembershipFilterType,
|
|
22
|
+
type MembershipFilterType,
|
|
23
|
+
} from "./filters";
|
|
20
24
|
import { selectedCellsAtom } from "./range-focus/atoms";
|
|
21
25
|
import { getClipboardContent, getRawValue } from "./utils";
|
|
22
26
|
|
|
@@ -97,15 +101,21 @@ export const CellContextMenu = <TData,>({
|
|
|
97
101
|
};
|
|
98
102
|
|
|
99
103
|
const column = cell.column;
|
|
100
|
-
const
|
|
104
|
+
const filterType = column.columnDef.meta?.filterType;
|
|
105
|
+
const membershipFilterType: MembershipFilterType | undefined =
|
|
106
|
+
column.getCanFilter() && filterType && isMembershipFilterType(filterType)
|
|
107
|
+
? filterType
|
|
108
|
+
: undefined;
|
|
101
109
|
|
|
102
|
-
const handleFilterCell = (
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
110
|
+
const handleFilterCell = (
|
|
111
|
+
type: MembershipFilterType,
|
|
112
|
+
operator: "in" | "not_in",
|
|
113
|
+
) => {
|
|
114
|
+
const filter =
|
|
115
|
+
type === "number"
|
|
116
|
+
? Filter.number({ operator, values: [rawValue] })
|
|
117
|
+
: Filter.text({ operator, values: [rawValue] });
|
|
118
|
+
column.setFilterValue(filter);
|
|
109
119
|
};
|
|
110
120
|
|
|
111
121
|
return (
|
|
@@ -120,14 +130,18 @@ export const CellContextMenu = <TData,>({
|
|
|
120
130
|
Copy selected cells
|
|
121
131
|
</ContextMenuItem>
|
|
122
132
|
)}
|
|
123
|
-
{
|
|
133
|
+
{membershipFilterType && (
|
|
124
134
|
<>
|
|
125
135
|
<ContextMenuSeparator />
|
|
126
|
-
<ContextMenuItem
|
|
136
|
+
<ContextMenuItem
|
|
137
|
+
onClick={() => handleFilterCell(membershipFilterType, "in")}
|
|
138
|
+
>
|
|
127
139
|
<FilterIcon className="mo-dropdown-icon h-3 w-3" />
|
|
128
140
|
Filter by this value
|
|
129
141
|
</ContextMenuItem>
|
|
130
|
-
<ContextMenuItem
|
|
142
|
+
<ContextMenuItem
|
|
143
|
+
onClick={() => handleFilterCell(membershipFilterType, "not_in")}
|
|
144
|
+
>
|
|
131
145
|
<FilterIcon className="mo-dropdown-icon h-3 w-3" />
|
|
132
146
|
Remove rows with this value
|
|
133
147
|
</ContextMenuItem>
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
// https://github.com/TanStack/table/issues/5567
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
|
+
type Column,
|
|
8
9
|
type ColumnDef,
|
|
9
10
|
type ColumnFiltersState,
|
|
10
11
|
ColumnPinning,
|
|
@@ -41,7 +42,13 @@ import { ColumnFormattingFeature } from "./column-formatting/feature";
|
|
|
41
42
|
import { ColumnWrappingFeature } from "./column-wrapping/feature";
|
|
42
43
|
import { CopyColumnFeature } from "./copy-column/feature";
|
|
43
44
|
import type { ExportActionProps } from "./export-actions";
|
|
45
|
+
import {
|
|
46
|
+
type AddFilterRequest,
|
|
47
|
+
FilterEditorProvider,
|
|
48
|
+
} from "./filter-editor-context";
|
|
49
|
+
import { buildEditorSnapshot } from "./filter-pill-editor";
|
|
44
50
|
import { FilterPills } from "./filter-pills";
|
|
51
|
+
import type { Snapshot } from "./filters";
|
|
45
52
|
import { FocusRowFeature } from "./focus-row/feature";
|
|
46
53
|
import { useColumnPinning } from "./hooks/use-column-pinning";
|
|
47
54
|
import { useScrollContainerHeight } from "./hooks/use-scroll-container-height";
|
|
@@ -289,67 +296,92 @@ const DataTableInternal = <TData,>({
|
|
|
289
296
|
|
|
290
297
|
const tableRef = useScrollContainerHeight({ maxHeight, virtualize });
|
|
291
298
|
|
|
299
|
+
const [addFilterSnapshot, setAddFilterSnapshot] =
|
|
300
|
+
React.useState<Snapshot | null>(null);
|
|
301
|
+
|
|
302
|
+
// useMemo instead of useCallback because need to pass it as object
|
|
303
|
+
const filterEditor = React.useMemo(
|
|
304
|
+
() => ({
|
|
305
|
+
requestAddFilter: (request: AddFilterRequest) => {
|
|
306
|
+
const column = table.getColumn(request.columnId);
|
|
307
|
+
if (!column) {
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
setAddFilterSnapshot(
|
|
311
|
+
buildEditorSnapshot(column as Column<unknown, unknown>, {
|
|
312
|
+
operator: request.operator,
|
|
313
|
+
}),
|
|
314
|
+
);
|
|
315
|
+
},
|
|
316
|
+
}),
|
|
317
|
+
[table],
|
|
318
|
+
);
|
|
319
|
+
|
|
292
320
|
return (
|
|
293
|
-
<
|
|
294
|
-
<
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
searchQuery={searchQuery}
|
|
307
|
-
onSearchQueryChange={onSearchQueryChange}
|
|
308
|
-
reloading={reloading}
|
|
309
|
-
showChartBuilder={showChartBuilder}
|
|
310
|
-
isChartBuilderOpen={isChartBuilderOpen}
|
|
311
|
-
toggleDisplayHeader={toggleDisplayHeader}
|
|
312
|
-
showTableExplorer={showTableExplorer}
|
|
313
|
-
togglePanel={togglePanel}
|
|
314
|
-
isAnyPanelOpen={isAnyPanelOpen}
|
|
315
|
-
downloadAs={downloadAs}
|
|
316
|
-
sizeBytes={sizeBytes}
|
|
317
|
-
/>
|
|
318
|
-
<Table
|
|
319
|
-
className={cn(
|
|
320
|
-
"relative",
|
|
321
|
-
columns.length <= AUTO_WIDTH_MAX_COLUMNS ? "w-auto" : "w-full",
|
|
322
|
-
)}
|
|
323
|
-
ref={tableRef}
|
|
321
|
+
<FilterEditorProvider value={filterEditor}>
|
|
322
|
+
<div className={cn(wrapperClassName, "flex flex-col space-y-1")}>
|
|
323
|
+
<FilterPills
|
|
324
|
+
filters={filters}
|
|
325
|
+
table={table}
|
|
326
|
+
calculateTopKRows={calculateTopKRows}
|
|
327
|
+
addFilterSnapshot={addFilterSnapshot}
|
|
328
|
+
onAddFilterSnapshotChange={setAddFilterSnapshot}
|
|
329
|
+
/>
|
|
330
|
+
<CellSelectionProvider>
|
|
331
|
+
<div
|
|
332
|
+
part="table-wrapper"
|
|
333
|
+
className={cn(className || "rounded-md border overflow-hidden")}
|
|
324
334
|
>
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
335
|
+
<TableTopBar
|
|
336
|
+
enableSearch={enableSearch}
|
|
337
|
+
searchQuery={searchQuery}
|
|
338
|
+
onSearchQueryChange={onSearchQueryChange}
|
|
339
|
+
reloading={reloading}
|
|
340
|
+
showChartBuilder={showChartBuilder}
|
|
341
|
+
isChartBuilderOpen={isChartBuilderOpen}
|
|
342
|
+
toggleDisplayHeader={toggleDisplayHeader}
|
|
343
|
+
showTableExplorer={showTableExplorer}
|
|
344
|
+
togglePanel={togglePanel}
|
|
345
|
+
isAnyPanelOpen={isAnyPanelOpen}
|
|
346
|
+
downloadAs={downloadAs}
|
|
347
|
+
sizeBytes={sizeBytes}
|
|
348
|
+
/>
|
|
349
|
+
<Table
|
|
350
|
+
className={cn(
|
|
351
|
+
"relative",
|
|
352
|
+
columns.length <= AUTO_WIDTH_MAX_COLUMNS ? "w-auto" : "w-full",
|
|
353
|
+
)}
|
|
354
|
+
ref={tableRef}
|
|
355
|
+
>
|
|
356
|
+
{showLoadingBar && (
|
|
357
|
+
<thead className="absolute top-0 left-0 h-[3px] w-1/2 bg-primary animate-slide" />
|
|
358
|
+
)}
|
|
359
|
+
{renderTableHeader(table, virtualize || Boolean(maxHeight))}
|
|
360
|
+
<DataTableBody
|
|
361
|
+
table={table}
|
|
362
|
+
columns={columns}
|
|
363
|
+
rowViewerPanelOpen={rowViewerPanelOpen}
|
|
364
|
+
getRowIndex={getPaginatedRowIndex}
|
|
365
|
+
viewedRowIdx={viewedRowIdx}
|
|
366
|
+
virtualize={virtualize}
|
|
367
|
+
/>
|
|
368
|
+
</Table>
|
|
369
|
+
<TableBottomBar
|
|
370
|
+
part="table-footer"
|
|
371
|
+
className="pt-1.5 pb-0.5 border-t border-border"
|
|
372
|
+
totalColumns={totalColumns}
|
|
373
|
+
pagination={pagination}
|
|
374
|
+
selection={selection}
|
|
375
|
+
onRowSelectionChange={onRowSelectionChange}
|
|
330
376
|
table={table}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
viewedRowIdx={viewedRowIdx}
|
|
335
|
-
virtualize={virtualize}
|
|
377
|
+
getRowIds={getRowIds}
|
|
378
|
+
showPageSizeSelector={showPageSizeSelector}
|
|
379
|
+
tableLoading={reloading}
|
|
336
380
|
/>
|
|
337
|
-
</
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
totalColumns={totalColumns}
|
|
342
|
-
pagination={pagination}
|
|
343
|
-
selection={selection}
|
|
344
|
-
onRowSelectionChange={onRowSelectionChange}
|
|
345
|
-
table={table}
|
|
346
|
-
getRowIds={getRowIds}
|
|
347
|
-
showPageSizeSelector={showPageSizeSelector}
|
|
348
|
-
tableLoading={reloading}
|
|
349
|
-
/>
|
|
350
|
-
</div>
|
|
351
|
-
</CellSelectionProvider>
|
|
352
|
-
</div>
|
|
381
|
+
</div>
|
|
382
|
+
</CellSelectionProvider>
|
|
383
|
+
</div>
|
|
384
|
+
</FilterEditorProvider>
|
|
353
385
|
);
|
|
354
386
|
};
|
|
355
387
|
|
|
@@ -11,11 +11,11 @@ import type { DateValue, TimeValue } from "react-aria-components";
|
|
|
11
11
|
import { TimeField } from "@/components/ui/date-input";
|
|
12
12
|
import { DatePicker, DateRangePicker } from "@/components/ui/date-picker";
|
|
13
13
|
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
} from "./filters";
|
|
14
|
+
dateToLocalISODate,
|
|
15
|
+
dateToLocalISODateTime,
|
|
16
|
+
dateToLocalISOTime,
|
|
17
|
+
} from "@/utils/dates";
|
|
18
|
+
import type { FilterType } from "./filters";
|
|
19
19
|
|
|
20
20
|
export type DateLikeFilterType = Extract<
|
|
21
21
|
FilterType,
|
|
@@ -28,11 +28,11 @@ function dateToAria(
|
|
|
28
28
|
): DateValue | TimeValue {
|
|
29
29
|
switch (filterType) {
|
|
30
30
|
case "date":
|
|
31
|
-
return parseDate(
|
|
31
|
+
return parseDate(dateToLocalISODate(d));
|
|
32
32
|
case "datetime":
|
|
33
|
-
return parseDateTime(
|
|
33
|
+
return parseDateTime(dateToLocalISODateTime(d));
|
|
34
34
|
case "time":
|
|
35
|
-
return parseTime(
|
|
35
|
+
return parseTime(dateToLocalISOTime(d));
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
|
|
@@ -128,11 +128,11 @@ export function parsePastedDate(
|
|
|
128
128
|
return parsed;
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
-
function parsePastedRange(
|
|
131
|
+
export function parsePastedRange(
|
|
132
132
|
filterType: DateLikeFilterType,
|
|
133
133
|
text: string,
|
|
134
134
|
): { min: Date; max: Date } | undefined {
|
|
135
|
-
const parts = text.split(/\s+(?:-|–|—|to)\s+/i);
|
|
135
|
+
const parts = text.split(/\s+(?:-|–|—|to|and)\s+/i);
|
|
136
136
|
if (parts.length === 2) {
|
|
137
137
|
const min = parsePastedDate(filterType, parts[0]);
|
|
138
138
|
const max = parsePastedDate(filterType, parts[1]);
|
|
@@ -194,6 +194,7 @@ export const DateLikeInput = ({
|
|
|
194
194
|
key={seedKey}
|
|
195
195
|
aria-label={ariaLabel}
|
|
196
196
|
defaultValue={seedValue as Time | undefined}
|
|
197
|
+
hourCycle={24}
|
|
197
198
|
onChange={handleChange}
|
|
198
199
|
className={className}
|
|
199
200
|
/>
|
|
@@ -211,6 +212,7 @@ export const DateLikeInput = ({
|
|
|
211
212
|
aria-label={ariaLabel}
|
|
212
213
|
defaultValue={seedValue as CalendarDateTime | undefined}
|
|
213
214
|
granularity="second"
|
|
215
|
+
hourCycle={24}
|
|
214
216
|
onChange={handleChange}
|
|
215
217
|
className={className}
|
|
216
218
|
/>
|
|
@@ -316,6 +318,7 @@ export const DateLikeRangeInput = ({
|
|
|
316
318
|
| undefined
|
|
317
319
|
}
|
|
318
320
|
granularity="second"
|
|
321
|
+
hourCycle={24}
|
|
319
322
|
onChange={handleChange}
|
|
320
323
|
className={className}
|
|
321
324
|
/>
|
|
@@ -7,7 +7,6 @@ import { useMemo, useState } from "react";
|
|
|
7
7
|
import { useAsyncData } from "@/hooks/useAsyncData";
|
|
8
8
|
import { ErrorBanner } from "@/plugins/impl/common/error-banner";
|
|
9
9
|
import type { CalculateTopKRows } from "@/plugins/impl/DataTablePlugin";
|
|
10
|
-
import { cn } from "@/utils/cn";
|
|
11
10
|
import { Logger } from "@/utils/Logger";
|
|
12
11
|
import { Sets } from "@/utils/sets";
|
|
13
12
|
import { smartMatch } from "@/utils/smartMatch";
|
|
@@ -24,6 +23,7 @@ import {
|
|
|
24
23
|
import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover";
|
|
25
24
|
import { SentinelCell } from "./sentinel-cell";
|
|
26
25
|
import { detectSentinel, stringifyUnknownValue } from "./utils";
|
|
26
|
+
import { CompactChipRow } from "./value-chips";
|
|
27
27
|
|
|
28
28
|
const TOP_K_ROWS = 30;
|
|
29
29
|
|
|
@@ -42,19 +42,14 @@ export const FilterByValuesPicker = <TData, TValue>({
|
|
|
42
42
|
onChange,
|
|
43
43
|
creatable = false,
|
|
44
44
|
}: Props<TData, TValue>) => {
|
|
45
|
-
const [open, setOpen] = useState(
|
|
45
|
+
const [open, setOpen] = useState(chosenValues.length === 0);
|
|
46
46
|
|
|
47
47
|
const chosenValuesSet = useMemo(() => new Set(chosenValues), [chosenValues]);
|
|
48
48
|
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const items = [...chosenValuesSet].map((v) =>
|
|
54
|
-
stringifyUnknownValue({ value: v }),
|
|
55
|
-
);
|
|
56
|
-
return `[${items.join(", ")}]`;
|
|
57
|
-
}, [chosenValuesSet]);
|
|
49
|
+
const displayItems = useMemo(
|
|
50
|
+
() => [...chosenValuesSet].map((v) => stringifyUnknownValue({ value: v })),
|
|
51
|
+
[chosenValuesSet],
|
|
52
|
+
);
|
|
58
53
|
|
|
59
54
|
return (
|
|
60
55
|
<Popover open={open} onOpenChange={setOpen}>
|
|
@@ -65,14 +60,13 @@ export const FilterByValuesPicker = <TData, TValue>({
|
|
|
65
60
|
size="xs"
|
|
66
61
|
className="h-6 mb-1 w-full justify-between font-normal"
|
|
67
62
|
>
|
|
68
|
-
|
|
69
|
-
className=
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
</span>
|
|
63
|
+
{displayItems.length === 0 ? (
|
|
64
|
+
<span className="truncate text-muted-foreground">
|
|
65
|
+
Select values…
|
|
66
|
+
</span>
|
|
67
|
+
) : (
|
|
68
|
+
<CompactChipRow items={displayItems} max={3} />
|
|
69
|
+
)}
|
|
76
70
|
<ChevronDownIcon className="h-4 w-4 opacity-50 shrink-0" />
|
|
77
71
|
</Button>
|
|
78
72
|
</PopoverTrigger>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Lets descendants of `DataTable` (column headers, etc.) ask the table to open
|
|
5
|
+
* the filter pill editor for a given column. The table owns the pending
|
|
6
|
+
* snapshot state and renders the editor anchored under the pills strip's
|
|
7
|
+
* `+` button; consumers only fire intent via `requestAddFilter`.
|
|
8
|
+
*
|
|
9
|
+
* `useFilterEditor()` returns `null` outside a provider, which callers treat as
|
|
10
|
+
* "filter editor not available" and hide the corresponding menu items.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { createContext, useContext } from "react";
|
|
14
|
+
import type { OperatorType } from "@/plugins/impl/data-frames/utils/operators";
|
|
15
|
+
|
|
16
|
+
export interface AddFilterRequest {
|
|
17
|
+
columnId: string;
|
|
18
|
+
/** Pre-select an operator (e.g. `"in"` for "Filter by values"); defaults to the column dtype's default. */
|
|
19
|
+
operator?: OperatorType;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface FilterEditorContextValue {
|
|
23
|
+
requestAddFilter: (request: AddFilterRequest) => void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const FilterEditorContext = createContext<FilterEditorContextValue | null>(
|
|
27
|
+
null,
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
export const FilterEditorProvider = FilterEditorContext.Provider;
|
|
31
|
+
|
|
32
|
+
export function useFilterEditor(): FilterEditorContextValue | null {
|
|
33
|
+
return useContext(FilterEditorContext);
|
|
34
|
+
}
|