@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
@@ -3,10 +3,10 @@ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { en
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
4
  import { t as __commonJSMin } from "./chunk-BNovOVIE.js";
5
5
  import { t as require_compiler_runtime } from "./compiler-runtime-B_OLMU9S.js";
6
- import { l as createLucideIcon } from "./dist-eWNxvDQG.js";
7
- import { g as Logger } from "./button-CxEhg2E1.js";
6
+ import { l as createLucideIcon } from "./dist-D2Rk1j4R.js";
7
+ import { h as Logger } from "./button-qsiIHncQ.js";
8
8
  import { r as KnownQueryParams } from "./constants-CXiS0vdr.js";
9
- import { f as waitFor, p as isIslands, u as store, y as atom } from "./useTheme-5rRsavIK.js";
9
+ import { f as waitFor, p as isIslands, u as store, y as atom } from "./useTheme-BIAKDAh6.js";
10
10
  var CircleQuestionMark = createLucideIcon("circle-question-mark", [
11
11
  ["circle", {
12
12
  cx: "12",
@@ -1,9 +1,9 @@
1
1
  import { s as __toESM } from "./chunk-BNovOVIE.js";
2
2
  import { t as require_react } from "./react-Bs6Z0kvn.js";
3
3
  import { t as require_compiler_runtime } from "./compiler-runtime-B_OLMU9S.js";
4
- import { a as Content, i as Arrow, o as Root2, r as Anchor, s as createPopperScope, t as Root } from "./dist-eWNxvDQG.js";
5
- import { m as useComposedRefs, y as cn } from "./button-CxEhg2E1.js";
6
- import { _t as createContextScope, at as useControllableState, ct as useId, ft as Primitive, ht as createSlottable, it as StyleNamespace, nt as withSmartCollisionBoundary, ot as Presence, st as Portal, tt as withFullScreenAsRoot, ut as DismissableLayer, vt as composeEventHandlers } from "./Combination-BVnmMSzE.js";
4
+ import { a as Content, i as Arrow, o as Root2, r as Anchor, s as createPopperScope, t as Root } from "./dist-D2Rk1j4R.js";
5
+ import { p as useComposedRefs, v as cn } from "./button-qsiIHncQ.js";
6
+ import { _t as createContextScope, at as useControllableState, ct as useId, ft as Primitive, ht as createSlottable, it as StyleNamespace, nt as withSmartCollisionBoundary, ot as Presence, st as Portal, tt as withFullScreenAsRoot, ut as DismissableLayer, vt as composeEventHandlers } from "./Combination-B--d1_LV.js";
7
7
  import { t as require_jsx_runtime } from "./jsx-runtime-9hcJiI23.js";
8
8
  var import_react = /* @__PURE__ */ __toESM(require_react(), 1), import_jsx_runtime = /* @__PURE__ */ __toESM(require_jsx_runtime(), 1), [createTooltipContext, createTooltipScope] = createContextScope("Tooltip", [createPopperScope]), usePopperScope = createPopperScope(), PROVIDER_NAME = "TooltipProvider", DEFAULT_DELAY_DURATION = 700, TOOLTIP_OPEN = "tooltip.open", [TooltipProviderContextProvider, useTooltipProviderContext] = createTooltipContext(PROVIDER_NAME), TooltipProvider$1 = (t) => {
9
9
  let { __scopeTooltip: k, delayDuration: A = DEFAULT_DELAY_DURATION, skipDelayDuration: j = 300, disableHoverableContent: M = false, children: N } = t, P = import_react.useRef(true), F = import_react.useRef(false), I = import_react.useRef(0);
@@ -1,8 +1,8 @@
1
1
  import { s as __toESM, t as __commonJSMin } from "./chunk-BNovOVIE.js";
2
2
  import { t as require_react } from "./react-Bs6Z0kvn.js";
3
3
  import { t as require_compiler_runtime } from "./compiler-runtime-B_OLMU9S.js";
4
- import { l as createLucideIcon } from "./dist-eWNxvDQG.js";
5
- import { t as Button } from "./button-CxEhg2E1.js";
4
+ import { l as createLucideIcon } from "./dist-D2Rk1j4R.js";
5
+ import { t as Button } from "./button-qsiIHncQ.js";
6
6
  import { t as require_jsx_runtime } from "./jsx-runtime-9hcJiI23.js";
7
7
  import { n as Constants } from "./constants-CXiS0vdr.js";
8
8
  var Pencil = createLucideIcon("pencil", [["path", {
@@ -1,7 +1,7 @@
1
1
  import { s as __toESM } from "./chunk-BNovOVIE.js";
2
2
  import { t as require_react } from "./react-Bs6Z0kvn.js";
3
3
  import { t as require_compiler_runtime } from "./compiler-runtime-B_OLMU9S.js";
4
- import { w as useEvent_default } from "./useTheme-5rRsavIK.js";
4
+ import { w as useEvent_default } from "./useTheme-BIAKDAh6.js";
5
5
  import { t as invariant } from "./invariant-CzjtdzpE.js";
6
6
  var import_compiler_runtime = require_compiler_runtime(), import_react = /* @__PURE__ */ __toESM(require_react(), 1), Result = {
7
7
  error(e, s) {
@@ -1,11 +1,11 @@
1
1
  import { s as __toESM } from "./chunk-BNovOVIE.js";
2
2
  import { t as require_react } from "./react-Bs6Z0kvn.js";
3
3
  import { t as require_compiler_runtime } from "./compiler-runtime-B_OLMU9S.js";
4
- import { t as toDate } from "./toDate-BEXAEQ0o.js";
5
- import { a as cva, y as cn } from "./button-CxEhg2E1.js";
4
+ import { t as toDate } from "./toDate-BWaG12Pv.js";
5
+ import { a as cva, v as cn } from "./button-qsiIHncQ.js";
6
6
  import { t as require_jsx_runtime } from "./jsx-runtime-9hcJiI23.js";
7
- import { C as dequal } from "./useTheme-5rRsavIK.js";
8
- import { i as tableFromIPC } from "./loader-hvRcVSq5.js";
7
+ import { C as dequal } from "./useTheme-BIAKDAh6.js";
8
+ import { i as tableFromIPC } from "./loader-DrMJeyDu.js";
9
9
  function isDate(t) {
10
10
  return t instanceof Date || typeof t == "object" && Object.prototype.toString.call(t) === "[object Date]";
11
11
  }
@@ -1,7 +1,7 @@
1
1
  import { s as __toESM } from "./chunk-BNovOVIE.js";
2
2
  import { t as require_react } from "./react-Bs6Z0kvn.js";
3
3
  import { t as require_compiler_runtime } from "./compiler-runtime-B_OLMU9S.js";
4
- import { t as getIframeCapabilities } from "./capabilities-x7AvQTfX.js";
4
+ import { t as getIframeCapabilities } from "./capabilities-BC3mzKnw.js";
5
5
  var import_compiler_runtime = require_compiler_runtime();
6
6
  require_react();
7
7
  function useIframeCapabilities() {
@@ -1,10 +1,10 @@
1
1
  import { s as __toESM } from "./chunk-BNovOVIE.js";
2
2
  import { t as require_react } from "./react-Bs6Z0kvn.js";
3
3
  import { t as require_compiler_runtime } from "./compiler-runtime-B_OLMU9S.js";
4
- import { l as createLucideIcon } from "./dist-eWNxvDQG.js";
5
- import { a as cva, g as Logger, y as cn } from "./button-CxEhg2E1.js";
4
+ import { l as createLucideIcon } from "./dist-D2Rk1j4R.js";
5
+ import { a as cva, h as Logger, v as cn } from "./button-qsiIHncQ.js";
6
6
  import { t as require_jsx_runtime } from "./jsx-runtime-9hcJiI23.js";
7
- import { _ as useSetAtom, y as atom } from "./useTheme-5rRsavIK.js";
7
+ import { _ as useSetAtom, y as atom } from "./useTheme-BIAKDAh6.js";
8
8
  var Calendar = createLucideIcon("calendar", [
9
9
  ["path", {
10
10
  d: "M8 2v4",
@@ -1,8 +1,8 @@
1
1
  import { s as __toESM } from "./chunk-BNovOVIE.js";
2
2
  import { t as require_react } from "./react-Bs6Z0kvn.js";
3
3
  import { t as require_compiler_runtime } from "./compiler-runtime-B_OLMU9S.js";
4
- import { g as Logger, s as OverridingHotkeyProvider, u as resolvePlatform } from "./button-CxEhg2E1.js";
5
- import { B as record, H as string, L as number, O as array, P as looseObject, R as object, W as union, k as boolean, w as _enum } from "./Combination-BVnmMSzE.js";
4
+ import { h as Logger, l as resolvePlatform, s as OverridingHotkeyProvider } from "./button-qsiIHncQ.js";
5
+ import { B as record, H as string, L as number, O as array, P as looseObject, R as object, W as union, k as boolean, w as _enum } from "./Combination-B--d1_LV.js";
6
6
  import { t as merge_default } from "./merge-Byt9w-nO.js";
7
7
  var import_react = /* @__PURE__ */ __toESM(require_react()), useInsertionEffect = typeof window < "u" ? import_react.useInsertionEffect || import_react.useLayoutEffect : () => {
8
8
  };
@@ -1,20 +1,20 @@
1
1
  import { s as __toESM } from "./chunk-BNovOVIE.js";
2
2
  import { t as require_react } from "./react-Bs6Z0kvn.js";
3
3
  import { t as require_compiler_runtime } from "./compiler-runtime-B_OLMU9S.js";
4
- import { c as asRemoteURL, g as CircleQuestionMark } from "./toDate-BEXAEQ0o.js";
5
- import { d as Objects, g as Logger, h as Events, y as cn } from "./button-CxEhg2E1.js";
6
- import "./Combination-BVnmMSzE.js";
4
+ import { c as asRemoteURL, g as CircleQuestionMark } from "./toDate-BWaG12Pv.js";
5
+ import { h as Logger, m as Events, u as Objects, v as cn } from "./button-qsiIHncQ.js";
6
+ import "./Combination-B--d1_LV.js";
7
7
  import { t as require_jsx_runtime } from "./jsx-runtime-9hcJiI23.js";
8
8
  import "./react-dom-BKwCWYPW.js";
9
- import { t as Tooltip } from "./tooltip-CYv8qy5F.js";
9
+ import { t as Tooltip } from "./tooltip-DGHTbHl5.js";
10
10
  import { i as debounce_default } from "./constants-CXiS0vdr.js";
11
- import { n as useTheme, w as useEvent_default } from "./useTheme-5rRsavIK.js";
11
+ import { n as useTheme, w as useEvent_default } from "./useTheme-BIAKDAh6.js";
12
12
  import { c as uniq } from "./arrays--2cd0hte.js";
13
- import { a as AlertTitle, n as arrow, o as isValid, r as Alert, t as useDeepCompareMemoize } from "./useDeepCompareMemoize-C7Ut95sA.js";
14
- import { n as ErrorBanner } from "./error-banner-CW87Aylu.js";
13
+ import { a as AlertTitle, n as arrow, o as isValid, r as Alert, t as useDeepCompareMemoize } from "./useDeepCompareMemoize-D2PKDkrk.js";
14
+ import { n as ErrorBanner } from "./error-banner-DkDzvax3.js";
15
15
  import { n as formats } from "./vega-loader.browser-hMqVC9bf.js";
16
- import { a as getContainerWidth, n as vegaLoadData, s as tooltipHandler } from "./loader-hvRcVSq5.js";
17
- import { t as useAsyncData } from "./useAsyncData-b2IfqSIa.js";
16
+ import { a as getContainerWidth, n as vegaLoadData, s as tooltipHandler } from "./loader-DrMJeyDu.js";
17
+ import { t as useAsyncData } from "./useAsyncData-Dqt2tV1E.js";
18
18
  import { t as j } from "./react-vega-JMnqwKtN.js";
19
19
  import "./defaultLocale-DjFHq3Xk.js";
20
20
  import "./defaultLocale-B_A76Zpk.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marimo-team/islands",
3
- "version": "0.21.2-dev93",
3
+ "version": "0.21.2-dev95",
4
4
  "main": "dist/main.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -2,17 +2,15 @@
2
2
  "use no memo";
3
3
 
4
4
  import type { RowSelectionState, Table } from "@tanstack/react-table";
5
- import {
6
- ChartColumnStacked,
7
- ChartSplineIcon,
8
- PanelRightIcon,
9
- SearchIcon,
10
- } from "lucide-react";
5
+ import { ChartSplineIcon, PanelRightIcon, SearchIcon } from "lucide-react";
11
6
  import React from "react";
12
7
  import { useLocale } from "react-aria";
13
8
  import type { GetRowIds } from "@/plugins/impl/DataTablePlugin";
14
9
  import { cn } from "@/utils/cn";
15
- import type { PanelType } from "../editor/chrome/panels/context-aware-panel/context-aware-panel";
10
+ import {
11
+ PANEL_TYPES,
12
+ type PanelType,
13
+ } from "../editor/chrome/panels/context-aware-panel/context-aware-panel";
16
14
  import { Button } from "../ui/button";
17
15
  import { Tooltip } from "../ui/tooltip";
18
16
  import { toast } from "../ui/use-toast";
@@ -31,15 +29,13 @@ interface TableActionsProps<TData> {
31
29
  onRowSelectionChange?: (value: RowSelectionState) => void;
32
30
  table: Table<TData>;
33
31
  downloadAs?: DownloadActionProps["downloadAs"];
34
- downloadFileName?: string;
35
32
  getRowIds?: GetRowIds;
36
33
  toggleDisplayHeader?: () => void;
37
34
  showChartBuilder?: boolean;
38
- showColumnExplorer?: boolean;
39
- showRowExplorer?: boolean;
35
+ showTableExplorer?: boolean;
40
36
  showPageSizeSelector?: boolean;
41
37
  togglePanel?: (panelType: PanelType) => void;
42
- isPanelOpen?: (panelType: PanelType) => boolean;
38
+ isAnyPanelOpen?: boolean;
43
39
  tableLoading?: boolean;
44
40
  }
45
41
 
@@ -54,15 +50,13 @@ export const TableActions = <TData,>({
54
50
  onRowSelectionChange,
55
51
  table,
56
52
  downloadAs,
57
- downloadFileName,
58
53
  getRowIds,
59
54
  toggleDisplayHeader,
60
55
  showChartBuilder,
61
- showColumnExplorer,
62
- showRowExplorer,
56
+ showTableExplorer,
63
57
  showPageSizeSelector,
64
58
  togglePanel,
65
- isPanelOpen,
59
+ isAnyPanelOpen,
66
60
  tableLoading,
67
61
  }: TableActionsProps<TData>) => {
68
62
  const { locale } = useLocale();
@@ -136,43 +130,22 @@ export const TableActions = <TData,>({
136
130
  </Button>
137
131
  </Tooltip>
138
132
  )}
139
- {togglePanel && isPanelOpen !== undefined && (
140
- <>
141
- {showRowExplorer && (
142
- <Tooltip content="Toggle row viewer">
143
- <Button
144
- variant="text"
145
- size="xs"
146
- onClick={() => togglePanel("row-viewer")}
147
- className="print:hidden"
148
- >
149
- <PanelRightIcon
150
- className={cn(
151
- "w-4 h-4 text-muted-foreground",
152
- isPanelOpen("row-viewer") && "text-primary",
153
- )}
154
- />
155
- </Button>
156
- </Tooltip>
157
- )}
158
- {showColumnExplorer && (
159
- <Tooltip content="Toggle column explorer">
160
- <Button
161
- variant="text"
162
- size="xs"
163
- onClick={() => togglePanel("column-explorer")}
164
- className="print:hidden"
165
- >
166
- <ChartColumnStacked
167
- className={cn(
168
- "w-4 h-4 text-muted-foreground",
169
- isPanelOpen("column-explorer") && "text-primary",
170
- )}
171
- />
172
- </Button>
173
- </Tooltip>
174
- )}
175
- </>
133
+ {showTableExplorer && togglePanel && (
134
+ <Tooltip content="Toggle table explorer">
135
+ <Button
136
+ variant="text"
137
+ size="xs"
138
+ onClick={() => togglePanel(PANEL_TYPES.ROW_VIEWER)}
139
+ className="print:hidden"
140
+ >
141
+ <PanelRightIcon
142
+ className={cn(
143
+ "w-4 h-4 text-muted-foreground",
144
+ isAnyPanelOpen && "text-primary",
145
+ )}
146
+ />
147
+ </Button>
148
+ </Tooltip>
176
149
  )}
177
150
 
178
151
  {pagination ? (
@@ -190,12 +163,7 @@ export const TableActions = <TData,>({
190
163
  </span>
191
164
  )}
192
165
  <div className="ml-auto">
193
- {downloadAs && (
194
- <DownloadAs
195
- downloadAs={downloadAs}
196
- downloadFileName={downloadFileName}
197
- />
198
- )}
166
+ {downloadAs && <DownloadAs downloadAs={downloadAs} />}
199
167
  </div>
200
168
  </div>
201
169
  );
@@ -1,5 +1,6 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
+ import { ChevronDownIcon, ChevronRightIcon } from "lucide-react";
3
4
  import { useState } from "react";
4
5
  import { useLocale } from "react-aria";
5
6
  import {
@@ -88,18 +89,21 @@ export const ColumnExplorerPanel = ({
88
89
  />
89
90
  <CommandList className="max-h-full">
90
91
  <CommandEmpty>No results.</CommandEmpty>
91
- {filteredColumns?.map(([columnName, [dataType, externalType]]) => {
92
- return (
93
- <ColumnItem
94
- // Tables may have the same column names, hence we use tableId to make it unique
95
- key={`${tableId}-${columnName}`}
96
- columnName={columnName}
97
- dataType={dataType}
98
- externalType={externalType}
99
- previewColumn={previewColumn}
100
- />
101
- );
102
- })}
92
+ {filteredColumns?.map(
93
+ ([columnName, [dataType, externalType]], index) => {
94
+ return (
95
+ <ColumnItem
96
+ // Tables may have the same column names, hence we use tableId to make it unique
97
+ key={`${tableId}-${columnName}`}
98
+ columnName={columnName}
99
+ dataType={dataType}
100
+ externalType={externalType}
101
+ previewColumn={previewColumn}
102
+ defaultExpanded={index === 0}
103
+ />
104
+ );
105
+ },
106
+ )}
103
107
  </CommandList>
104
108
  </Command>
105
109
  </div>
@@ -111,13 +115,15 @@ const ColumnItem = ({
111
115
  dataType,
112
116
  externalType,
113
117
  previewColumn,
118
+ defaultExpanded = false,
114
119
  }: {
115
120
  columnName: string;
116
121
  dataType: DataType;
117
122
  externalType: string;
118
123
  previewColumn: PreviewColumn;
124
+ defaultExpanded?: boolean;
119
125
  }) => {
120
- const [isExpanded, setIsExpanded] = useState(false);
126
+ const [isExpanded, setIsExpanded] = useState(defaultExpanded);
121
127
 
122
128
  const columnText = (
123
129
  <span className={isExpanded ? "font-semibold" : ""}>{columnName}</span>
@@ -130,6 +136,11 @@ const ColumnItem = ({
130
136
  onSelect={() => setIsExpanded(!isExpanded)}
131
137
  className="flex flex-row items-center gap-1.5 group w-full cursor-pointer"
132
138
  >
139
+ {isExpanded ? (
140
+ <ChevronDownIcon className="w-3 h-3 shrink-0 text-muted-foreground" />
141
+ ) : (
142
+ <ChevronRightIcon className="w-3 h-3 shrink-0 text-muted-foreground" />
143
+ )}
133
144
  <ColumnName columnName={columnText} dataType={dataType} />
134
145
  <div className="ml-auto">
135
146
  <Tooltip content="Copy column name" delayDuration={400}>
@@ -24,7 +24,10 @@ import { useLocale } from "react-aria";
24
24
  import { Table } from "@/components/ui/table";
25
25
  import type { GetRowIds } from "@/plugins/impl/DataTablePlugin";
26
26
  import { cn } from "@/utils/cn";
27
- import type { PanelType } from "../editor/chrome/panels/context-aware-panel/context-aware-panel";
27
+ import {
28
+ PANEL_TYPES,
29
+ type PanelType,
30
+ } from "../editor/chrome/panels/context-aware-panel/context-aware-panel";
28
31
  import { CellHoverTemplateFeature } from "./cell-hover-template/feature";
29
32
  import { CellHoverTextFeature } from "./cell-hover-text/feature";
30
33
  import { CellSelectionFeature } from "./cell-selection/feature";
@@ -97,10 +100,10 @@ interface DataTableProps<TData> extends Partial<DownloadActionProps> {
97
100
  // Others
98
101
  showChartBuilder?: boolean;
99
102
  showPageSizeSelector?: boolean;
100
- showColumnExplorer?: boolean;
101
- showRowExplorer?: boolean;
103
+ showTableExplorer?: boolean;
102
104
  togglePanel?: (panelType: PanelType) => void;
103
105
  isPanelOpen?: (panelType: PanelType) => boolean;
106
+ isAnyPanelOpen?: boolean;
104
107
  }
105
108
 
106
109
  const DataTableInternal = <TData,>({
@@ -124,7 +127,6 @@ const DataTableInternal = <TData,>({
124
127
  paginationState,
125
128
  setPaginationState,
126
129
  downloadAs,
127
- downloadFileName,
128
130
  manualPagination = false,
129
131
  pagination = false,
130
132
  onRowSelectionChange,
@@ -142,10 +144,10 @@ const DataTableInternal = <TData,>({
142
144
  toggleDisplayHeader,
143
145
  showChartBuilder,
144
146
  showPageSizeSelector,
145
- showColumnExplorer,
146
- showRowExplorer,
147
+ showTableExplorer,
147
148
  togglePanel,
148
149
  isPanelOpen,
150
+ isAnyPanelOpen,
149
151
  viewedRowIdx,
150
152
  onViewedRowChange,
151
153
  }: DataTableProps<TData>) => {
@@ -272,7 +274,7 @@ const DataTableInternal = <TData,>({
272
274
  },
273
275
  });
274
276
 
275
- const rowViewerPanelOpen = isPanelOpen?.("row-viewer") ?? false;
277
+ const rowViewerPanelOpen = isPanelOpen?.(PANEL_TYPES.ROW_VIEWER) ?? false;
276
278
  const virtualize = !pagination && data.length > MIN_ROWS_TO_VIRTUALIZE;
277
279
 
278
280
  const tableRef = useScrollContainerHeight({ maxHeight, virtualize });
@@ -319,15 +321,13 @@ const DataTableInternal = <TData,>({
319
321
  onRowSelectionChange={onRowSelectionChange}
320
322
  table={table}
321
323
  downloadAs={downloadAs}
322
- downloadFileName={downloadFileName}
323
324
  getRowIds={getRowIds}
324
325
  toggleDisplayHeader={toggleDisplayHeader}
325
326
  showChartBuilder={showChartBuilder}
326
327
  showPageSizeSelector={showPageSizeSelector}
327
- showColumnExplorer={showColumnExplorer}
328
- showRowExplorer={showRowExplorer}
328
+ showTableExplorer={showTableExplorer}
329
329
  togglePanel={togglePanel}
330
- isPanelOpen={isPanelOpen}
330
+ isAnyPanelOpen={isAnyPanelOpen}
331
331
  tableLoading={reloading}
332
332
  />
333
333
  </div>
@@ -14,7 +14,6 @@ import { logNever } from "@/utils/assertNever";
14
14
  import { copyToClipboard } from "@/utils/copy";
15
15
  import { downloadByURL } from "@/utils/download";
16
16
  import { prettyError } from "@/utils/errors";
17
- import { Filenames } from "@/utils/filenames";
18
17
  import {
19
18
  jsonParseWithSpecialChar,
20
19
  jsonToMarkdown,
@@ -36,8 +35,9 @@ import { toast } from "../ui/use-toast";
36
35
  type DownloadFormat = "csv" | "json" | "parquet";
37
36
 
38
37
  export interface DownloadActionProps {
39
- downloadAs: (req: { format: DownloadFormat }) => Promise<string>;
40
- downloadFileName?: string;
38
+ downloadAs: (req: {
39
+ format: DownloadFormat;
40
+ }) => Promise<{ url: string; filename: string }>;
41
41
  }
42
42
 
43
43
  const options = [
@@ -99,7 +99,9 @@ export const DownloadAs: React.FC<DownloadActionProps> = (props) => {
99
99
  </Button>
100
100
  );
101
101
 
102
- const getDownloadUrl = (format: DownloadFormat) => {
102
+ const getDownloadResponse = (
103
+ format: DownloadFormat,
104
+ ): Promise<{ url: string; filename: string }> => {
103
105
  return props.downloadAs({ format }).catch((error) => {
104
106
  toast({
105
107
  title: "Failed to download",
@@ -116,26 +118,26 @@ export const DownloadAs: React.FC<DownloadActionProps> = (props) => {
116
118
 
117
119
  switch (format) {
118
120
  case "tsv": {
119
- const downloadUrl = await getDownloadUrl("json");
120
- const json = await fetchJson(downloadUrl);
121
+ const { url } = await getDownloadResponse("json");
122
+ const json = await fetchJson(url);
121
123
  text = jsonToTSV(json, locale);
122
124
  break;
123
125
  }
124
126
  case "json": {
125
- const downloadUrl = await getDownloadUrl("json");
126
- const json = await fetchJson(downloadUrl);
127
+ const { url } = await getDownloadResponse("json");
128
+ const json = await fetchJson(url);
127
129
  text = JSON.stringify(json, null, 2);
128
130
  break;
129
131
  }
130
132
  case "csv": {
131
- const downloadUrl = await getDownloadUrl("csv");
132
- const csv = await fetchText(downloadUrl);
133
+ const { url } = await getDownloadResponse("csv");
134
+ const csv = await fetchText(url);
133
135
  text = csv;
134
136
  break;
135
137
  }
136
138
  case "markdown": {
137
- const downloadUrl = await getDownloadUrl("json");
138
- const json = await fetchJson(downloadUrl);
139
+ const { url } = await getDownloadResponse("json");
140
+ const json = await fetchJson(url);
139
141
  text = jsonToMarkdown(json);
140
142
  break;
141
143
  }
@@ -158,12 +160,10 @@ export const DownloadAs: React.FC<DownloadActionProps> = (props) => {
158
160
  <DropdownMenuItem
159
161
  key={option.label}
160
162
  onSelect={async () => {
161
- const downloadUrl = await getDownloadUrl(option.format);
162
- const ext = option.format;
163
- const rawName = (props.downloadFileName ?? "").trim();
164
- const baseName =
165
- Filenames.withoutExtension(rawName) || "download";
166
- downloadByURL(downloadUrl, `${baseName}.${ext}`);
163
+ const { url, filename } = await getDownloadResponse(
164
+ option.format,
165
+ );
166
+ downloadByURL(url, filename);
167
167
  }}
168
168
  >
169
169
  <option.icon className="mo-dropdown-icon" />
@@ -13,7 +13,10 @@ import { Logger } from "@/utils/Logger";
13
13
 
14
14
  interface PanelOwnershipResult {
15
15
  isPanelOpen: (panelType: PanelType) => boolean;
16
- togglePanel: (panelType: PanelType) => void;
16
+ isAnyPanelOpen: boolean;
17
+ togglePanel: (panelType?: PanelType) => void;
18
+ panelType: PanelType | null;
19
+ setPanelType: (panelType: PanelType) => void;
17
20
  }
18
21
 
19
22
  export function usePanelOwnership(
@@ -55,24 +58,31 @@ export function usePanelOwnership(
55
58
  setPanelOwner(panelId);
56
59
  }
57
60
 
58
- function togglePanel(panelType: PanelType) {
59
- if (isPanelOpen(panelType)) {
61
+ const isAnyPanelOpen = panelOwner === panelId && isContextAwarePanelOpen;
62
+
63
+ function togglePanel(requestedType?: PanelType) {
64
+ if (isAnyPanelOpen) {
60
65
  setPanelOwner(null);
61
66
  setContextAwarePanelOpen(false);
62
67
  } else {
63
68
  setPanelOwner(panelId);
64
- // if cell-aware, we want to focus on this cell when toggled open
65
69
  if (isPanelCellAware && cellId) {
66
70
  focusCell({ cellId });
67
71
  }
68
72
  setContextAwarePanelOpen(true);
69
- setPanelType(panelType);
73
+ // Only set type if explicitly requested and no previous type exists
74
+ if (requestedType && !panelType) {
75
+ setPanelType(requestedType);
76
+ }
70
77
  }
71
78
  }
72
79
 
73
80
  return {
74
81
  isPanelOpen,
82
+ isAnyPanelOpen,
75
83
  togglePanel,
84
+ panelType,
85
+ setPanelType,
76
86
  };
77
87
  }
78
88