@marimo-team/islands 0.21.2-dev93 → 0.21.2-dev95

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 (44) hide show
  1. package/dist/{Combination-BVnmMSzE.js → Combination-B--d1_LV.js} +8 -8
  2. package/dist/{ConnectedDataExplorerComponent-BzRetIpL.js → ConnectedDataExplorerComponent-CTfvzyMi.js} +19 -19
  3. package/dist/{any-language-editor-BgjMQ0JO.js → any-language-editor-Bj-3432h.js} +4 -4
  4. package/dist/{button-CxEhg2E1.js → button-qsiIHncQ.js} +10 -11
  5. package/dist/{capabilities-x7AvQTfX.js → capabilities-BC3mzKnw.js} +1 -1
  6. package/dist/{chat-ui-Blhe8Kd8.js → chat-ui-BvK3aDSR.js} +13 -13
  7. package/dist/{check-Cfbm1K98.js → check-D_YwHEgY.js} +1 -1
  8. package/dist/{copy-Bb-tO2YP.js → copy-CBo9JcJW.js} +2 -2
  9. package/dist/{dist-eWNxvDQG.js → dist-D2Rk1j4R.js} +2 -2
  10. package/dist/{error-banner-CW87Aylu.js → error-banner-DkDzvax3.js} +25 -25
  11. package/dist/{esm-BjPSZ7Qf.js → esm-C9_jY_wu.js} +4 -4
  12. package/dist/{glide-data-editor-CSemfq4D.js → glide-data-editor-D0IYL4_F.js} +7 -7
  13. package/dist/{input-EtKyRcb-.js → input-C5uUN4xL.js} +9 -9
  14. package/dist/{label-mQKeDCJ8.js → label-DwSVaniz.js} +4 -4
  15. package/dist/{loader-hvRcVSq5.js → loader-DrMJeyDu.js} +15 -15
  16. package/dist/main.js +17411 -17369
  17. package/dist/{mermaid-CjhFZReU.js → mermaid-Bwy7OYzI.js} +5 -5
  18. package/dist/{process-output-Cy0KEXJh.js → process-output-CT8hHGp6.js} +15 -15
  19. package/dist/{slides-component-DKg-ohoF.js → slides-component-BsaaAy66.js} +2 -2
  20. package/dist/{spec-DLNqef4y.js → spec-BLAZSydG.js} +3 -3
  21. package/dist/style.css +1 -1
  22. package/dist/{toDate-BEXAEQ0o.js → toDate-BWaG12Pv.js} +3 -3
  23. package/dist/{tooltip-CYv8qy5F.js → tooltip-DGHTbHl5.js} +3 -3
  24. package/dist/{types-B7Om6oxp.js → types-Dqw69fPc.js} +2 -2
  25. package/dist/{useAsyncData-b2IfqSIa.js → useAsyncData-Dqt2tV1E.js} +1 -1
  26. package/dist/{useDeepCompareMemoize-C7Ut95sA.js → useDeepCompareMemoize-D2PKDkrk.js} +4 -4
  27. package/dist/{useIframeCapabilities-DHGxPMu5.js → useIframeCapabilities-DlwLttZw.js} +1 -1
  28. package/dist/{useLifecycle-Q7CzdfPd.js → useLifecycle-CJ_5Z4Mk.js} +3 -3
  29. package/dist/{useTheme-5rRsavIK.js → useTheme-BIAKDAh6.js} +2 -2
  30. package/dist/{vega-component-B0Ik4707.js → vega-component-CTOT0vRO.js} +9 -9
  31. package/package.json +1 -1
  32. package/src/components/data-table/TableActions.tsx +26 -58
  33. package/src/components/data-table/column-explorer-panel/column-explorer.tsx +24 -13
  34. package/src/components/data-table/data-table.tsx +11 -11
  35. package/src/components/data-table/download-actions.tsx +18 -18
  36. package/src/components/data-table/hooks/use-panel-ownership.ts +15 -5
  37. package/src/components/data-table/row-viewer-panel/row-viewer.tsx +53 -44
  38. package/src/components/data-table/schemas.ts +7 -2
  39. package/src/components/data-table/table-explorer-panel/table-explorer-panel.tsx +161 -0
  40. package/src/components/editor/chrome/panels/context-aware-panel/context-aware-panel.tsx +16 -9
  41. package/src/core/slots/slots.ts +1 -0
  42. package/src/plugins/impl/DataTablePlugin.tsx +19 -26
  43. package/src/plugins/impl/data-frames/DataFramePlugin.tsx +0 -1
  44. package/src/stories/dataframe.stories.tsx +1 -1
@@ -12,16 +12,16 @@ import {
12
12
  ChevronsLeft,
13
13
  ChevronsRight,
14
14
  Info,
15
- SearchIcon,
16
15
  } from "lucide-react";
17
- import { useRef, useState } from "react";
16
+ import { type KeyboardEvent, useId, useRef, useState } from "react";
18
17
  import { useLocale } from "react-aria";
19
18
  import { ColumnName } from "@/components/datasources/components";
20
19
  import { CopyClipboardIcon } from "@/components/icons/copy-icon";
21
20
  import { Spinner } from "@/components/icons/spinner";
22
21
  import { KeyboardHotkeys } from "@/components/shortcuts/renderShortcut";
23
22
  import { Button } from "@/components/ui/button";
24
- import { Input } from "@/components/ui/input";
23
+ import { Checkbox } from "@/components/ui/checkbox";
24
+ import { Command, CommandInput } from "@/components/ui/command";
25
25
  import {
26
26
  Table,
27
27
  TableBody,
@@ -32,7 +32,6 @@ import {
32
32
  } from "@/components/ui/table";
33
33
  import { DelayMount } from "@/components/utils/delay-mount";
34
34
  import { useAsyncData } from "@/hooks/useAsyncData";
35
- import { useKeydownOnElement } from "@/hooks/useHotkey";
36
35
  import { Banner, ErrorBanner } from "@/plugins/impl/common/error-banner";
37
36
  import type { GetRowResult } from "@/plugins/impl/DataTablePlugin";
38
37
  import { NAMELESS_COLUMN_PREFIX, renderCellValue } from "../columns";
@@ -68,7 +67,7 @@ export const RowViewerPanel: React.FC<RowViewerPanelProps> = ({
68
67
  }: RowViewerPanelProps) => {
69
68
  const [searchQuery, setSearchQuery] = useState("");
70
69
  const panelRef = useRef<HTMLDivElement>(null);
71
- const searchInputRef = useRef<HTMLInputElement>(null);
70
+ const checkboxId = useId();
72
71
  const { locale } = useLocale();
73
72
 
74
73
  const tooManyRows = totalRows === TOO_MANY_ROWS;
@@ -102,26 +101,29 @@ export const RowViewerPanel: React.FC<RowViewerPanelProps> = ({
102
101
  setRow(totalRows - 1);
103
102
  }
104
103
 
105
- useKeydownOnElement(panelRef, {
106
- ArrowLeft: (e) => {
107
- if (e?.target === searchInputRef.current) {
108
- return false;
109
- }
110
- setRow(rowIdx - 1);
111
- },
112
- ArrowRight: (e) => {
113
- if (e?.target === searchInputRef.current) {
114
- return false;
115
- }
116
- setRow(rowIdx + 1);
117
- },
118
- Space: (e) => {
119
- if (e?.target === searchInputRef.current) {
120
- return false;
121
- }
122
- toggleRowSelection();
123
- },
124
- });
104
+ const handleKeyDown = (e: KeyboardEvent) => {
105
+ // Don't intercept keys when typing in an input
106
+ if (e.target instanceof HTMLInputElement) {
107
+ return;
108
+ }
109
+ switch (e.key) {
110
+ case "ArrowLeft":
111
+ setRow(rowIdx - 1);
112
+
113
+ break;
114
+
115
+ case "ArrowRight":
116
+ setRow(rowIdx + 1);
117
+
118
+ break;
119
+
120
+ case " ":
121
+ e.preventDefault();
122
+ toggleRowSelection();
123
+
124
+ break;
125
+ }
126
+ };
125
127
 
126
128
  const buttonStyles = "h-6 w-6 p-0.5";
127
129
 
@@ -248,19 +250,29 @@ export const RowViewerPanel: React.FC<RowViewerPanelProps> = ({
248
250
  className="flex flex-col gap-3 mt-4 focus:outline-hidden"
249
251
  ref={panelRef}
250
252
  tabIndex={-1}
253
+ onKeyDown={handleKeyDown}
251
254
  >
252
- <div className="flex flex-row gap-2 items-center mr-2">
255
+ <div className="flex flex-row gap-2 items-center mr-2 px-2">
253
256
  {isSelectable && (
254
- <div className="flex flex-row gap-1 items-center">
255
- <Button
256
- variant="link"
257
- size="xs"
258
- className="pr-0"
259
- onClick={toggleRowSelection}
260
- >
261
- {isRowSelected ? "Deselect row" : "Select row"}
262
- </Button>
263
- <KeyboardHotkeys shortcut="Space" />
257
+ <div
258
+ className="flex items-center"
259
+ title="Select/unselect the current row"
260
+ >
261
+ <div className="flex items-center gap-1.5">
262
+ <Checkbox
263
+ id={checkboxId}
264
+ checked={isRowSelected}
265
+ onCheckedChange={toggleRowSelection}
266
+ className="h-3.5 w-3.5"
267
+ />
268
+ <label
269
+ htmlFor={checkboxId}
270
+ className="text-xs text-muted-foreground cursor-pointer"
271
+ >
272
+ Select
273
+ </label>
274
+ </div>
275
+ <KeyboardHotkeys shortcut="Space" className="scale-75" />
264
276
  </div>
265
277
  )}
266
278
 
@@ -315,17 +327,14 @@ export const RowViewerPanel: React.FC<RowViewerPanelProps> = ({
315
327
  </Button>
316
328
  </div>
317
329
 
318
- <div className="mx-2 -mb-1">
319
- <Input
320
- ref={searchInputRef}
321
- type="text"
330
+ <Command className="bg-background" shouldFilter={false}>
331
+ <CommandInput
322
332
  placeholder="Search"
323
- onChange={(e) => setSearchQuery(e.target.value)}
324
- icon={<SearchIcon className="w-4 h-4" />}
325
- className="mb-0 border-border"
333
+ value={searchQuery}
334
+ onValueChange={setSearchQuery}
326
335
  data-testid="selection-panel-search-input"
327
336
  />
328
- </div>
337
+ </Command>
329
338
  {renderTable()}
330
339
  </div>
331
340
  );
@@ -5,7 +5,7 @@ import { rpc } from "@/plugins/core/rpc";
5
5
 
6
6
  export type DownloadAsArgs = (req: {
7
7
  format: "csv" | "json" | "parquet";
8
- }) => Promise<string>;
8
+ }) => Promise<{ url: string; filename: string }>;
9
9
 
10
10
  export const DownloadAsSchema = rpc
11
11
  .input(
@@ -13,4 +13,9 @@ export const DownloadAsSchema = rpc
13
13
  format: z.enum(["csv", "json", "parquet"]),
14
14
  }),
15
15
  )
16
- .output(z.string());
16
+ .output(
17
+ z.object({
18
+ url: z.string(),
19
+ filename: z.string(),
20
+ }),
21
+ );
@@ -0,0 +1,161 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { Fill } from "@marimo-team/react-slotz";
4
+ import type { OnChangeFn, RowSelectionState } from "@tanstack/react-table";
5
+ import type React from "react";
6
+ import { Button } from "@/components/ui/button";
7
+ import { Tabs, TabsContent } from "@/components/ui/tabs";
8
+ import { SlotNames } from "@/core/slots/slots";
9
+ import type {
10
+ GetRowResult,
11
+ PreviewColumn,
12
+ } from "@/plugins/impl/DataTablePlugin";
13
+ import { cn } from "@/utils/cn";
14
+ import {
15
+ PANEL_TYPES,
16
+ type PanelType,
17
+ } from "../../editor/chrome/panels/context-aware-panel/context-aware-panel";
18
+ import { ColumnExplorerPanel } from "../column-explorer-panel/column-explorer";
19
+ import { RowViewerPanel } from "../row-viewer-panel/row-viewer";
20
+ import type { FieldTypesWithExternalType, TooManyRows } from "../types";
21
+
22
+ export interface TableExplorerPanelProps {
23
+ // Row viewer props
24
+ rowIdx: number;
25
+ setRowIdx: (rowIdx: number) => void;
26
+ totalRows: number | TooManyRows;
27
+ fieldTypes: FieldTypesWithExternalType | undefined | null;
28
+ getRow: (rowIdx: number) => Promise<GetRowResult>;
29
+ isSelectable: boolean;
30
+ isRowSelected: boolean;
31
+ handleRowSelectionChange?: OnChangeFn<RowSelectionState>;
32
+ // Column explorer props
33
+ previewColumn?: PreviewColumn;
34
+ totalColumns: number;
35
+ tableId: string;
36
+ // Visibility flags
37
+ showRowExplorer: boolean;
38
+ showColumnExplorer: boolean;
39
+ // Tab state (driven by contextAwarePanelType atom)
40
+ activeTab: PanelType | null;
41
+ onTabChange: (tab: PanelType) => void;
42
+ }
43
+
44
+ const tabTriggerClassName =
45
+ "text-xs uppercase tracking-wide font-semibold cursor-pointer transition-colors";
46
+ const activeClassName = "text-primary";
47
+ const inactiveClassName = "hover:text-foreground";
48
+
49
+ export const TableExplorerPanel: React.FC<TableExplorerPanelProps> = ({
50
+ // Row viewer
51
+ rowIdx,
52
+ setRowIdx,
53
+ totalRows,
54
+ fieldTypes,
55
+ getRow,
56
+ isSelectable,
57
+ isRowSelected,
58
+ handleRowSelectionChange,
59
+ // Column explorer
60
+ previewColumn,
61
+ totalColumns,
62
+ tableId,
63
+ // Visibility
64
+ showRowExplorer,
65
+ showColumnExplorer,
66
+ // Tab state
67
+ activeTab,
68
+ onTabChange,
69
+ }) => {
70
+ const showTabs = showRowExplorer && showColumnExplorer;
71
+
72
+ const rowViewer = (
73
+ <RowViewerPanel
74
+ rowIdx={rowIdx}
75
+ setRowIdx={setRowIdx}
76
+ totalRows={totalRows}
77
+ fieldTypes={fieldTypes}
78
+ getRow={getRow}
79
+ isSelectable={isSelectable}
80
+ isRowSelected={isRowSelected}
81
+ handleRowSelectionChange={handleRowSelectionChange}
82
+ />
83
+ );
84
+
85
+ const columnExplorer = previewColumn && (
86
+ <ColumnExplorerPanel
87
+ previewColumn={previewColumn}
88
+ fieldTypes={fieldTypes}
89
+ totalRows={totalRows}
90
+ totalColumns={totalColumns}
91
+ tableId={tableId}
92
+ />
93
+ );
94
+
95
+ // If only one panel is visible, don't show tabs
96
+ if (!showTabs) {
97
+ if (showRowExplorer) {
98
+ return rowViewer;
99
+ }
100
+ if (showColumnExplorer) {
101
+ return columnExplorer;
102
+ }
103
+ return null;
104
+ }
105
+
106
+ // Resolve active tab — fall back to first available
107
+ const resolvedTab = activeTab ?? PANEL_TYPES.ROW_VIEWER;
108
+
109
+ return (
110
+ <Tabs
111
+ value={resolvedTab}
112
+ onValueChange={(value) => onTabChange(value as PanelType)}
113
+ className="h-full flex flex-col min-w-[350px]"
114
+ >
115
+ <Fill name={SlotNames.CONTEXT_AWARE_PANEL_HEADER}>
116
+ <div className="flex items-center gap-1">
117
+ <Button
118
+ variant="text"
119
+ size="xs"
120
+ onClick={() => onTabChange(PANEL_TYPES.ROW_VIEWER)}
121
+ className={cn(
122
+ tabTriggerClassName,
123
+ resolvedTab === PANEL_TYPES.ROW_VIEWER
124
+ ? activeClassName
125
+ : inactiveClassName,
126
+ )}
127
+ >
128
+ Rows
129
+ </Button>
130
+ <span className="text-muted-foreground text-xs">|</span>
131
+ <Button
132
+ variant="text"
133
+ size="xs"
134
+ onClick={() => onTabChange(PANEL_TYPES.COLUMN_EXPLORER)}
135
+ className={cn(
136
+ tabTriggerClassName,
137
+ resolvedTab === PANEL_TYPES.COLUMN_EXPLORER
138
+ ? activeClassName
139
+ : inactiveClassName,
140
+ )}
141
+ >
142
+ Explorer
143
+ </Button>
144
+ </div>
145
+ </Fill>
146
+
147
+ <TabsContent
148
+ value={PANEL_TYPES.ROW_VIEWER}
149
+ className="flex-1 overflow-auto"
150
+ >
151
+ {rowViewer}
152
+ </TabsContent>
153
+ <TabsContent
154
+ value={PANEL_TYPES.COLUMN_EXPLORER}
155
+ className="flex-1 overflow-auto"
156
+ >
157
+ {columnExplorer}
158
+ </TabsContent>
159
+ </Tabs>
160
+ );
161
+ };
@@ -21,7 +21,12 @@ import {
21
21
  isPinnedAtom,
22
22
  } from "./atoms";
23
23
 
24
- export type PanelType = "row-viewer" | "column-explorer";
24
+ export const PANEL_TYPES = {
25
+ ROW_VIEWER: "row-viewer",
26
+ COLUMN_EXPLORER: "column-explorer",
27
+ } as const;
28
+
29
+ export type PanelType = (typeof PANEL_TYPES)[keyof typeof PANEL_TYPES];
25
30
 
26
31
  export const ContextAwarePanel: React.FC = () => {
27
32
  const [owner, setOwner] = useAtom(contextAwarePanelOwner);
@@ -51,7 +56,7 @@ export const ContextAwarePanel: React.FC = () => {
51
56
  aria-label={isPinned ? "Unpin panel" : "Pin panel"}
52
57
  >
53
58
  {isPinned ? (
54
- <PinIcon className="w-4 h-4" />
59
+ <PinIcon className="w-4 h-4 text-primary" />
55
60
  ) : (
56
61
  <PinOffIcon className="w-4 h-4" />
57
62
  )}
@@ -95,16 +100,18 @@ export const ContextAwarePanel: React.FC = () => {
95
100
 
96
101
  const renderBody = () => {
97
102
  return (
98
- <div className="mt-2 pb-7 mb-4 h-full overflow-auto">
99
- <div className="flex flex-row justify-between items-center mx-2">
103
+ <div className="pb-7 mb-4 h-full overflow-auto">
104
+ <div className="p-3 border-b flex justify-between items-center">
100
105
  {renderModeToggle()}
106
+ <Slot name={SlotNames.CONTEXT_AWARE_PANEL_HEADER} />
101
107
  <Button
102
- variant="linkDestructive"
103
- size="icon"
108
+ variant="text"
109
+ size="xs"
110
+ className="m-0"
104
111
  onClick={closePanel}
105
112
  aria-label="Close selection panel"
106
113
  >
107
- <XIcon className="w-4 h-4" />
114
+ <XIcon className="w-4 h-4 hover:text-destructive" />
108
115
  </Button>
109
116
  </div>
110
117
 
@@ -126,7 +133,7 @@ export const ContextAwarePanel: React.FC = () => {
126
133
  onDragging={handleDragging}
127
134
  className="resize-handle border-border z-20 print:hidden border-l"
128
135
  />
129
- <Panel defaultSize={20} minSize={15} maxSize={80}>
136
+ <Panel defaultSize={25} minSize={25} maxSize={80}>
130
137
  {renderBody()}
131
138
  </Panel>
132
139
  </>
@@ -150,7 +157,7 @@ interface ResizableComponentProps {
150
157
  const ResizableComponent = ({ children }: ResizableComponentProps) => {
151
158
  const { resizableDivRef, handleRefs, style } = useResizeHandle({
152
159
  startingWidth: 400,
153
- minWidth: 300,
160
+ minWidth: 400,
154
161
  maxWidth: 1500,
155
162
  onResize: () => {
156
163
  raf2(() => {
@@ -6,4 +6,5 @@ export const slotsController = new SlotzController();
6
6
  export const SlotNames = {
7
7
  SIDEBAR: "sidebar",
8
8
  CONTEXT_AWARE_PANEL: "context-aware-panel",
9
+ CONTEXT_AWARE_PANEL_HEADER: "context-aware-panel-header",
9
10
  };
@@ -26,7 +26,6 @@ import type { CellSelectionState } from "@/components/data-table/cell-selection/
26
26
  import type { CellStyleState } from "@/components/data-table/cell-styling/types";
27
27
  import { TablePanel } from "@/components/data-table/charts/charts";
28
28
  import { hasChart } from "@/components/data-table/charts/storage";
29
- import { ColumnExplorerPanel } from "@/components/data-table/column-explorer-panel/column-explorer";
30
29
  import { ColumnChartSpecModel } from "@/components/data-table/column-summary/chart-spec-model";
31
30
  import { ColumnChartContext } from "@/components/data-table/column-summary/column-summary";
32
31
  import {
@@ -35,11 +34,11 @@ import {
35
34
  } from "@/components/data-table/filters";
36
35
  import { usePanelOwnership } from "@/components/data-table/hooks/use-panel-ownership";
37
36
  import { LoadingTable } from "@/components/data-table/loading-table";
38
- import { RowViewerPanel } from "@/components/data-table/row-viewer-panel/row-viewer";
39
37
  import {
40
38
  type DownloadAsArgs,
41
39
  DownloadAsSchema,
42
40
  } from "@/components/data-table/schemas";
41
+ import { TableExplorerPanel } from "@/components/data-table/table-explorer-panel/table-explorer-panel";
43
42
  import {
44
43
  type BinValues,
45
44
  type ColumnHeaderStats,
@@ -201,7 +200,6 @@ interface Data<T> {
201
200
  hasStableRowId: boolean;
202
201
  lazy: boolean;
203
202
  cellHoverTexts?: Record<string, Record<string, string | null>> | null;
204
- downloadFileName?: string;
205
203
  }
206
204
 
207
205
  // eslint-disable-next-line @typescript-eslint/consistent-type-definitions
@@ -287,7 +285,6 @@ export const DataTablePlugin = createPlugin<S>("marimo-table")
287
285
  // If lazy, this will preload the first page of data
288
286
  // without user confirmation.
289
287
  preload: z.boolean().default(false),
290
- downloadFileName: z.string().optional(),
291
288
  }),
292
289
  )
293
290
  .withFunctions<DataTableFunctions>({
@@ -823,7 +820,6 @@ const DataTableComponent = ({
823
820
  cellStyles,
824
821
  hoverTemplate,
825
822
  cellHoverTexts,
826
- downloadFileName,
827
823
  toggleDisplayHeader,
828
824
  calculate_top_k_rows,
829
825
  preview_column,
@@ -839,7 +835,8 @@ const DataTableComponent = ({
839
835
  }): JSX.Element => {
840
836
  const id = useId();
841
837
  const [viewedRowIdx, setViewedRowIdx] = useState(0);
842
- const { isPanelOpen, togglePanel } = usePanelOwnership(id, cellId);
838
+ const { isPanelOpen, isAnyPanelOpen, togglePanel, panelType, setPanelType } =
839
+ usePanelOwnership(id, cellId);
843
840
 
844
841
  const chartSpecModel = useMemo(() => {
845
842
  if (!columnSummaries) {
@@ -1002,8 +999,7 @@ const DataTableComponent = ({
1002
999
  );
1003
1000
 
1004
1001
  const isSelectable = selection === "multi" || selection === "single";
1005
- const showColExplorer =
1006
- showColumnExplorer && preview_column && isPanelOpen("column-explorer");
1002
+ const canShowColumnExplorer = showColumnExplorer && !!preview_column;
1007
1003
 
1008
1004
  const isInVscode = isInVscodeExtension();
1009
1005
 
@@ -1032,28 +1028,24 @@ const DataTableComponent = ({
1032
1028
  </Banner>
1033
1029
  )}
1034
1030
 
1035
- {isPanelOpen("row-viewer") && (
1031
+ {isAnyPanelOpen && (showRowExplorer || canShowColumnExplorer) && (
1036
1032
  <ContextAwarePanelItem>
1037
- <RowViewerPanel
1038
- getRow={getRow}
1039
- fieldTypes={memoizedUnclampedFieldTypes}
1040
- totalRows={totalRows}
1033
+ <TableExplorerPanel
1041
1034
  rowIdx={viewedRowIdx}
1042
1035
  setRowIdx={setViewedRow}
1036
+ totalRows={totalRows}
1037
+ fieldTypes={memoizedUnclampedFieldTypes}
1038
+ getRow={getRow}
1043
1039
  isSelectable={isSelectable}
1044
- isRowSelected={rowSelection[viewedRowIdx]}
1040
+ isRowSelected={Boolean(rowSelection[viewedRowIdx])}
1045
1041
  handleRowSelectionChange={handleRowSelectionChange}
1046
- />
1047
- </ContextAwarePanelItem>
1048
- )}
1049
- {showColExplorer && (
1050
- <ContextAwarePanelItem>
1051
- <ColumnExplorerPanel
1052
1042
  previewColumn={preview_column}
1053
- fieldTypes={memoizedUnclampedFieldTypes}
1054
- totalRows={totalRows}
1055
1043
  totalColumns={totalColumns}
1056
1044
  tableId={id}
1045
+ showRowExplorer={showRowExplorer && !isInVscode}
1046
+ showColumnExplorer={canShowColumnExplorer && !isInVscode}
1047
+ activeTab={panelType}
1048
+ onTabChange={setPanelType}
1057
1049
  />
1058
1050
  </ContextAwarePanelItem>
1059
1051
  )}
@@ -1082,7 +1074,6 @@ const DataTableComponent = ({
1082
1074
  hoverTemplate={hoverTemplate}
1083
1075
  cellHoverTexts={cellHoverTexts}
1084
1076
  downloadAs={showDownload ? downloadAs : undefined}
1085
- downloadFileName={downloadFileName}
1086
1077
  enableSearch={enableSearch}
1087
1078
  searchQuery={searchQuery}
1088
1079
  onSearchQueryChange={setSearchQuery}
@@ -1099,11 +1090,13 @@ const DataTableComponent = ({
1099
1090
  showChartBuilder={showChartBuilder}
1100
1091
  showPageSizeSelector={showPageSizeSelector}
1101
1092
  // Hidden in VSCode (for now) because we don't have a panel to show
1102
- // the column/row explorer.
1103
- showColumnExplorer={showColumnExplorer && !isInVscode}
1104
- showRowExplorer={showRowExplorer && !isInVscode}
1093
+ // the table explorer.
1094
+ showTableExplorer={
1095
+ (showRowExplorer || canShowColumnExplorer) && !isInVscode
1096
+ }
1105
1097
  togglePanel={togglePanel}
1106
1098
  isPanelOpen={isPanelOpen}
1099
+ isAnyPanelOpen={isAnyPanelOpen}
1107
1100
  viewedRowIdx={viewedRowIdx}
1108
1101
  onViewedRowChange={(rowIdx) => setViewedRowIdx(rowIdx)}
1109
1102
  />
@@ -329,7 +329,6 @@ export const DataFrameComponent = memo(
329
329
  fieldTypes={field_types}
330
330
  rowHeaders={row_headers || Arrays.EMPTY}
331
331
  showDownload={showDownload}
332
- downloadFileName={dataframeName}
333
332
  download_as={download_as}
334
333
  enableSearch={false}
335
334
  showFilters={false}
@@ -41,7 +41,7 @@ export const DataFrame: StoryObj = {
41
41
  search={Functions.THROW}
42
42
  host={document.body}
43
43
  showDownload={false}
44
- download_as={async () => ""}
44
+ download_as={async () => ({ url: "", filename: "" })}
45
45
  lazy={false}
46
46
  />
47
47
  );