@marimo-team/islands 0.23.10-dev5 → 0.23.10-dev7

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.
@@ -8,61 +8,61 @@ var TOAST_LIMIT = 1, TOAST_REMOVE_DELAY = 1e4, count = 0;
8
8
  function genId() {
9
9
  return count = (count + 1) % Number.MAX_VALUE, count.toString();
10
10
  }
11
- var toastTimeouts = /* @__PURE__ */ new Map(), addToRemoveQueue = (e) => {
11
+ var toastTimeouts = /* @__PURE__ */ new Map(), shownOnceKeys = /* @__PURE__ */ new Set(), addToRemoveQueue = (e) => {
12
12
  if (toastTimeouts.has(e)) return;
13
- let p = setTimeout(() => {
13
+ let d = setTimeout(() => {
14
14
  toastTimeouts.delete(e), dispatch({
15
15
  type: "REMOVE_TOAST",
16
16
  toastId: e
17
17
  });
18
18
  }, TOAST_REMOVE_DELAY);
19
- toastTimeouts.set(e, p);
19
+ toastTimeouts.set(e, d);
20
20
  };
21
- const reducer = (e, p) => {
22
- switch (p.type) {
21
+ const reducer = (e, d) => {
22
+ switch (d.type) {
23
23
  case "ADD_TOAST":
24
24
  return {
25
25
  ...e,
26
- toasts: [p.toast, ...e.toasts].slice(0, TOAST_LIMIT)
26
+ toasts: [d.toast, ...e.toasts].slice(0, TOAST_LIMIT)
27
27
  };
28
28
  case "UPDATE_TOAST":
29
29
  return {
30
30
  ...e,
31
- toasts: e.toasts.map((e2) => e2.id === p.toast.id ? {
31
+ toasts: e.toasts.map((e2) => e2.id === d.toast.id ? {
32
32
  ...e2,
33
- ...p.toast
33
+ ...d.toast
34
34
  } : e2)
35
35
  };
36
36
  case "DISMISS_TOAST": {
37
- let { toastId: m } = p;
38
- return m ? addToRemoveQueue(m) : e.toasts.forEach((e2) => {
37
+ let { toastId: f } = d;
38
+ return f ? addToRemoveQueue(f) : e.toasts.forEach((e2) => {
39
39
  addToRemoveQueue(e2.id);
40
40
  }), {
41
41
  ...e,
42
- toasts: e.toasts.map((e2) => e2.id === m || m === void 0 ? {
42
+ toasts: e.toasts.map((e2) => e2.id === f || f === void 0 ? {
43
43
  ...e2,
44
44
  open: false
45
45
  } : e2)
46
46
  };
47
47
  }
48
48
  case "REMOVE_TOAST":
49
- return p.toastId === void 0 ? {
49
+ return d.toastId === void 0 ? {
50
50
  ...e,
51
51
  toasts: []
52
52
  } : {
53
53
  ...e,
54
- toasts: e.toasts.filter((e2) => e2.id !== p.toastId)
54
+ toasts: e.toasts.filter((e2) => e2.id !== d.toastId)
55
55
  };
56
56
  case "UPSERT_TOAST":
57
- return e.toasts.findIndex((e2) => e2.id === p.toast.id) > -1 ? {
57
+ return e.toasts.findIndex((e2) => e2.id === d.toast.id) > -1 ? {
58
58
  ...e,
59
- toasts: e.toasts.map((e2) => e2.id === p.toast.id ? {
59
+ toasts: e.toasts.map((e2) => e2.id === d.toast.id ? {
60
60
  ...e2,
61
- ...p.toast
61
+ ...d.toast
62
62
  } : e2)
63
63
  } : {
64
64
  ...e,
65
- toasts: [p.toast, ...e.toasts].slice(0, TOAST_LIMIT)
65
+ toasts: [d.toast, ...e.toasts].slice(0, TOAST_LIMIT)
66
66
  };
67
67
  }
68
68
  };
@@ -72,39 +72,39 @@ function dispatch(e) {
72
72
  e2(memoryState);
73
73
  });
74
74
  }
75
- function toast({ id: e, ...p }) {
76
- let m = e || genId(), h = (e2) => dispatch({
75
+ function toast({ id: e, once: d, ...f }) {
76
+ let p = e || genId(), m = d === true && e !== void 0, h = (e2) => dispatch({
77
77
  type: "UPDATE_TOAST",
78
78
  toast: {
79
79
  ...e2,
80
- id: m
80
+ id: p
81
81
  }
82
82
  }), g = () => dispatch({
83
83
  type: "DISMISS_TOAST",
84
- toastId: m
84
+ toastId: p
85
85
  }), _ = (e2) => dispatch({
86
86
  type: "UPSERT_TOAST",
87
87
  toast: {
88
88
  ...e2,
89
- id: m,
89
+ id: p,
90
90
  open: true,
91
91
  onOpenChange: (e3) => {
92
92
  e3 || g();
93
93
  }
94
94
  }
95
- });
96
- return dispatch({
95
+ }), v = m && shownOnceKeys.has(p);
96
+ return m && shownOnceKeys.add(p), v || dispatch({
97
97
  type: "ADD_TOAST",
98
98
  toast: {
99
- ...p,
100
- id: m,
99
+ ...f,
100
+ id: p,
101
101
  open: true,
102
102
  onOpenChange: (e2) => {
103
103
  e2 || g();
104
104
  }
105
105
  }
106
106
  }), {
107
- id: m,
107
+ id: p,
108
108
  dismiss: g,
109
109
  update: h,
110
110
  upsert: _
@@ -122,17 +122,17 @@ var Copy = createLucideIcon("copy", [["rect", {
122
122
  d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",
123
123
  key: "zix9uf"
124
124
  }]]);
125
- async function copyToClipboard(e, m) {
125
+ async function copyToClipboard(e, f) {
126
126
  if (navigator.clipboard === void 0) {
127
127
  Logger.warn("navigator.clipboard is not supported"), window.prompt("Copy to clipboard: Ctrl+C, Enter", e);
128
128
  return;
129
129
  }
130
- if (m && navigator.clipboard.write) try {
131
- let p = new ClipboardItem({
132
- "text/html": new Blob([m], { type: "text/html" }),
130
+ if (f && navigator.clipboard.write) try {
131
+ let d = new ClipboardItem({
132
+ "text/html": new Blob([f], { type: "text/html" }),
133
133
  "text/plain": new Blob([e], { type: "text/plain" })
134
134
  });
135
- await navigator.clipboard.write([p]);
135
+ await navigator.clipboard.write([d]);
136
136
  return;
137
137
  } catch {
138
138
  Logger.warn("Failed to write rich text, falling back to plain text");
@@ -1,7 +1,7 @@
1
1
  import { s as __toESM } from "./chunk-BNovOVIE.js";
2
2
  import { t as require_react } from "./react-DA-nE2FX.js";
3
3
  import { t as require_jsx_runtime } from "./jsx-runtime-DebpN0FN.js";
4
- import { l as historyKeymap, o as defaultKeymap, r as lintKeymap, s as history, t as _extends, u as indentWithTab } from "./extends-BgdxCfYu.js";
4
+ import { l as historyKeymap, o as defaultKeymap, r as lintKeymap, s as history, t as _extends, u as indentWithTab } from "./extends-CkydH1Q5.js";
5
5
  import { $ as ViewPlugin, At as Prec, B as tags, Bt as combineConfig, C as foldKeymap, Dt as EditorState, Et as EditorSelection, F as syntaxHighlighting, Ft as StateField, Ht as findClusterBreak, Mt as RangeSetBuilder, Ot as Facet, Pt as StateEffect, Rt as codePointAt, Ut as fromCodePoint, Y as Decoration, Z as EditorView, _t as showDialog, a as HighlightStyle, bt as crelt, ct as highlightActiveLineGutter, dt as keymap, ft as lineNumbers, g as defaultHighlightStyle, gt as runScopeHandlers, ht as rectangularSelection, it as getPanel, j as indentUnit, k as indentOnInput, lt as highlightSpecialChars, mt as placeholder, nt as drawSelection, p as bracketMatching, rt as dropCursor, st as highlightActiveLine, tt as crosshairCursor, vt as showPanel, wt as CharCategory, x as foldGutter, xt as Annotation, zt as codePointSize } from "./dist-DNdhYsgW.js";
6
6
  import { a as closeBracketsKeymap, c as completionKeymap, i as closeBrackets, r as autocompletion } from "./dist-DhHh0jLg.js";
7
7
  var basicNormalize = typeof String.prototype.normalize == "function" ? (e2) => e2.normalize("NFKD") : (e2) => e2, SearchCursor = class {
@@ -2,7 +2,7 @@ import { s as __toESM } from "./chunk-BNovOVIE.js";
2
2
  import { g as cn, h as Events } from "./button-C5K9fIPF.js";
3
3
  import { t as require_react } from "./react-DA-nE2FX.js";
4
4
  import { t as require_compiler_runtime } from "./compiler-runtime-CEbnTgxf.js";
5
- import { n as Copy, r as toast, t as copyToClipboard } from "./copy-BuQpJEzp.js";
5
+ import { n as Copy, r as toast, t as copyToClipboard } from "./copy-5jQ_kGE1.js";
6
6
  import { t as Check } from "./check-DTbrK0zt.js";
7
7
  import { t as require_jsx_runtime } from "./jsx-runtime-DebpN0FN.js";
8
8
  import { t as Tooltip } from "./tooltip-C5FYOpQc.js";
@@ -5,7 +5,7 @@ import { s as __toESM, t as __commonJSMin } from "./chunk-BNovOVIE.js";
5
5
  import { _ as Logger, h as Events, t as Button } from "./button-C5K9fIPF.js";
6
6
  import { t as require_react } from "./react-DA-nE2FX.js";
7
7
  import { t as require_compiler_runtime } from "./compiler-runtime-CEbnTgxf.js";
8
- import { n as Copy, r as toast, t as copyToClipboard } from "./copy-BuQpJEzp.js";
8
+ import { n as Copy, r as toast, t as copyToClipboard } from "./copy-5jQ_kGE1.js";
9
9
  import { S as logNever, i as SelectContent, l as SelectTrigger, n as capitalize, o as SelectItem, r as Select, u as SelectValue } from "./strings-Bu3vlb6W.js";
10
10
  import { G as marked, W as useNonce, _ as DropdownMenuSub, d as DropdownMenuContent, g as DropdownMenuSeparator, h as DropdownMenuPortal, p as DropdownMenuItem, r as Input, u as DropdownMenu, v as DropdownMenuSubContent, y as DropdownMenuSubTrigger } from "./input-_2sjvfne.js";
11
11
  import { n as Trash, r as Pencil, t as BulkEdit } from "./types-CVvp1fKr.js";
@@ -5,7 +5,7 @@ import { a as __toCommonJS, n as __esmMin, o as __toDynamicImportESM, r as __exp
5
5
  import { _ as Logger, c as Objects, d as createSlot, g as cn, m as useComposedRefs, o as isPlatformMac, r as cva, t as Button } from "./button-C5K9fIPF.js";
6
6
  import { t as require_react } from "./react-DA-nE2FX.js";
7
7
  import { t as require_compiler_runtime } from "./compiler-runtime-CEbnTgxf.js";
8
- import { r as toast, t as copyToClipboard } from "./copy-BuQpJEzp.js";
8
+ import { r as toast, t as copyToClipboard } from "./copy-5jQ_kGE1.js";
9
9
  import { o as xn, s as require_cjs, __tla as __tla_0 } from "./chunk-5FQGJX7Z-BNjes6Yx.js";
10
10
  import { a as createPopperScope, i as Root2$3, l as VisuallyHidden, n as Arrow, o as useSize, r as Content$1, t as Anchor, u as createLucideIcon } from "./dist-C1BYNeCR.js";
11
11
  import { a as Type, c as Calendar, i as createReducerAndAtoms, o as ToggleLeft, r as Badge, s as Hash, t as useOnMount } from "./useLifecycle-BBO9PIph.js";
@@ -17,7 +17,7 @@ import { t as require_react_dom } from "./react-dom-BTJzcVJ9.js";
17
17
  import { t as require_jsx_runtime } from "./jsx-runtime-DebpN0FN.js";
18
18
  import { $ as StyleNamespace, B as union, N as number, P as object, R as string, T as boolean, X as withFullScreenAsRoot, Y as MAX_HEIGHT_OFFSET, Z as withSmartCollisionBoundary, _t as Primitive, at as FocusScope, ct as Root$1, dt as Presence, et as hideOthers, ft as useControllableState, gt as createContextScope, it as Portal, k as literal, lt as useCallbackRef, mt as composeEventHandlers, ot as Branch, pt as useLayoutEffect2, rt as useFocusGuards, st as DismissableLayer, tt as Combination_default, ut as useId, vt as dispatchDiscreteCustomEvent, w as array } from "./zod-CoBiJ5v4.js";
19
19
  import { i as TooltipProvider, t as Tooltip } from "./tooltip-C5FYOpQc.js";
20
- import { a as setDiagnostics, c as historyField, d as insertTab, f as CopyClipboardIcon, i as linter, n as forEachDiagnostic, s as history } from "./extends-BgdxCfYu.js";
20
+ import { a as setDiagnostics, c as historyField, d as insertTab, f as CopyClipboardIcon, i as linter, n as forEachDiagnostic, s as history } from "./extends-CkydH1Q5.js";
21
21
  import { i as debounce_default } from "./constants-T20xxyNf.js";
22
22
  import { _ as useAtomValue, a as getResolvedMarimoConfig, b as atom, d as store, g as useAtom, h as Provider, i as autoInstantiateAtom, m as isIslands, n as useTheme, t as resolvedThemeAtom, u as createDeepEqualAtom, w as dequal } from "./useTheme-DHIrRQOe.js";
23
23
  import { $ as ViewPlugin, At as Prec, B as tags, Dt as EditorState, E as getIndentUnit, Et as EditorSelection, Ft as StateField, I as syntaxTree, It as Text, J as parseMixed, L as unfoldAll, Lt as Transaction, Ot as Facet, Pt as StateEffect, Q as GutterMarker, S as foldInside, Tt as Compartment, Y as Decoration, Z as EditorView, b as foldAll, dt as keymap, f as StreamLanguage, jt as RangeSet, l as LanguageDescription, mt as placeholder, ot as gutter, u as LanguageSupport, ut as hoverTooltip, vt as showPanel, w as foldNodeProp, xt as Annotation, yt as showTooltip } from "./dist-DNdhYsgW.js";
@@ -27989,7 +27989,7 @@ ${n.sqlString}
27989
27989
  hasConsoleOutput: (o == null ? void 0 : o.consoleOutputs) != null
27990
27990
  };
27991
27991
  }
27992
- LazyAnyLanguageCodeMirror = (0, import_react.lazy)(() => import("./any-language-editor-DfdpyDv_.js"));
27992
+ LazyAnyLanguageCodeMirror = (0, import_react.lazy)(() => import("./any-language-editor-CiES2a2h.js"));
27993
27993
  var import_compiler_runtime$1 = require_compiler_runtime(), extensions = [
27994
27994
  EditorView.lineWrapping
27995
27995
  ];
package/dist/main.js CHANGED
@@ -21,18 +21,18 @@ import { a as __toCommonJS, n as __esmMin, r as __export, s as __toESM, t as __c
21
21
  import { _ as Logger, c as Objects, g as cn, h as Events, i as NOT_SET, l as useEventListener, m as useComposedRefs, n as buttonVariants, o as isPlatformMac, p as composeRefs, r as cva, t as Button, u as Slot, v as Functions, y as throwNotImplemented } from "./button-C5K9fIPF.js";
22
22
  import { t as require_react } from "./react-DA-nE2FX.js";
23
23
  import { t as require_compiler_runtime } from "./compiler-runtime-CEbnTgxf.js";
24
- import { n as Copy, r as toast, t as copyToClipboard } from "./copy-BuQpJEzp.js";
25
- import { $ as useCellActions, An as LoaderCircle, At as DeferredRequestRegistry, B as safeExtractSetUIElementMessageBuffers, Bn as Braces, Bt as getDataTypeColor, C as AccordionContent, Cn as Trigger2, Ct as customPythonLanguageSupport, Dn as PaintRoller, Dt as Paths, E as BorderAllIcon, En as Table2, Et as PathBuilder, F as base64ToDataView, Ft as jotaiJsonStorage, Gt as convertStatsName, H as getMarimoExportContext, In as Database, J as getCellNames, Jt as useRequestClient, K as createActions, Kt as getRequestClient, L as dataViewToBase64, Ln as Columns2, Mn as Info, Nn as FileText, Nt as repl, Pn as Eye, Q as reducer, Rt as PluralWords, S as Accordion, Sn as Root2$1, St as Checkbox, T as AccordionTrigger, Tn as Trash2, U as hasTrustedExportContext, V as renderHTML, Vt as require_client, W as hasRunAnyCellAtom, X as notebookOutline, Y as notebookAtom, Yt as isUninstantiated, Z as numColumnsAtom, _n as selectAtom, a as useCellFocusActions, an as parseInitialValue, bn as Content2, bt as isInternalCellName, ct as kioskModeAtom, dn as OBJECT_ID_ATTR, dt as outputIsLoading, en as NotebookScopedLocalStorage, et as useCellIds, f as isOutputEmpty, fn as RANDOM_ID_ATTR, ft as outputIsStale, gn as atomWithStorage, hn as atomWithReducer, i as LazyAnyLanguageCodeMirror, in as parseDataset, jn as Layers, jt as generateUUID, k as ChevronDownIcon, ln as UIElementId, mt as headingToIdentifier, n as Spinner, nt as createCell, o as useLastFocusedCellId, ot as getInitialAppMode, p as useExpandedConsoleOutput, pn as jsonParseWithSpecialChar, pt as isErrorMime, qt as requestClientAtom, rn as parseAttrValue, s as maybeAddAltairImport, sn as HTMLCellId, st as initialModeAtom, un as findCellId, w as AccordionItem, wt as MarkdownLanguageAdapter, xn as Item$1, xt as normalizeName, yt as getValidName, zn as CircleAlert, zt as DATA_TYPE_ICON, __tla as __tla_0 } from "./html-to-image-CnBdHeLh.js";
24
+ import { n as Copy, r as toast, t as copyToClipboard } from "./copy-5jQ_kGE1.js";
25
+ import { $ as useCellActions, An as LoaderCircle, At as DeferredRequestRegistry, B as safeExtractSetUIElementMessageBuffers, Bn as Braces, Bt as getDataTypeColor, C as AccordionContent, Cn as Trigger2, Ct as customPythonLanguageSupport, Dn as PaintRoller, Dt as Paths, E as BorderAllIcon, En as Table2, Et as PathBuilder, F as base64ToDataView, Ft as jotaiJsonStorage, Gt as convertStatsName, H as getMarimoExportContext, In as Database, J as getCellNames, Jt as useRequestClient, K as createActions, Kt as getRequestClient, L as dataViewToBase64, Ln as Columns2, Mn as Info, Nn as FileText, Nt as repl, Pn as Eye, Q as reducer, Rt as PluralWords, S as Accordion, Sn as Root2$1, St as Checkbox, T as AccordionTrigger, Tn as Trash2, U as hasTrustedExportContext, V as renderHTML, Vt as require_client, W as hasRunAnyCellAtom, X as notebookOutline, Y as notebookAtom, Yt as isUninstantiated, Z as numColumnsAtom, _n as selectAtom, a as useCellFocusActions, an as parseInitialValue, bn as Content2, bt as isInternalCellName, ct as kioskModeAtom, dn as OBJECT_ID_ATTR, dt as outputIsLoading, en as NotebookScopedLocalStorage, et as useCellIds, f as isOutputEmpty, fn as RANDOM_ID_ATTR, ft as outputIsStale, gn as atomWithStorage, hn as atomWithReducer, i as LazyAnyLanguageCodeMirror, in as parseDataset, jn as Layers, jt as generateUUID, k as ChevronDownIcon, ln as UIElementId, mt as headingToIdentifier, n as Spinner, nt as createCell, o as useLastFocusedCellId, ot as getInitialAppMode, p as useExpandedConsoleOutput, pn as jsonParseWithSpecialChar, pt as isErrorMime, qt as requestClientAtom, rn as parseAttrValue, s as maybeAddAltairImport, sn as HTMLCellId, st as initialModeAtom, un as findCellId, w as AccordionItem, wt as MarkdownLanguageAdapter, xn as Item$1, xt as normalizeName, yt as getValidName, zn as CircleAlert, zt as DATA_TYPE_ICON, __tla as __tla_0 } from "./html-to-image-B93KtAVY.js";
26
26
  import { __tla as __tla_1 } from "./chunk-5FQGJX7Z-BNjes6Yx.js";
27
27
  import { o as useSize, s as Root$2, u as createLucideIcon } from "./dist-C1BYNeCR.js";
28
28
  import { A as SquareFunction, C as DEFAULT_COLOR_SCHEME, D as SCALE_TYPE_DESCRIPTIONS, E as EMPTY_VALUE$1, O as TIME_UNIT_DESCRIPTIONS, S as DEFAULT_AGGREGATION, T as DEFAULT_TIME_UNIT, _ as AGGREGATION_TYPE_DESCRIPTIONS, a as AGGREGATION_FNS$1, b as COLOR_SCHEMES, c as COLOR_BY_FIELDS, d as NONE_VALUE, f as SELECTABLE_DATA_TYPES, g as TIME_UNITS, h as STRING_AGGREGATION_FNS, i as convertDataTypeToSelectable, j as ChartColumn, k as escapeFieldName, l as COMBINED_TIME_UNITS, m as SORT_TYPES, n as createSpecWithoutData, o as BIN_AGGREGATION, p as SINGLE_TIME_UNITS, r as isFieldSet, s as CHART_TYPES, t as augmentSpecWithData, u as ChartType, v as AGGREGATION_TYPE_ICON, w as DEFAULT_MAX_BINS_FACET, x as COUNT_FIELD, y as CHART_TYPE_ICON } from "./spec-B96zNUEA.js";
29
- import { $ as TableBody, $t as ChevronLeft, A as ComboboxItem, At as ChartErrorState, B as contextAwarePanelOpen, Bt as $fae977aafc393c5c$export$6b862160d295c8e, C as prettifyRowColumnCount, Ct as dateToLocalISODate, D as DatePicker, Dt as TabsContent, E as useInternalStateWithSync, Et as Tabs, F as CommandList, Ft as RenderTextWithLinks, G as slotsController, H as contextAwarePanelType, Ht as GripHorizontal, I as CommandSeparator, It as Kbd, Jt as Code, K as Toggle, Kt as Ellipsis, L as smartMatch, Lt as HtmlOutput, M as CommandEmpty, Mt as ChartLoadingState, N as CommandInput, Nt as LazyVegaEmbed, O as DateRangePicker, Ot as TabsList, P as CommandItem, Pt as useOverflowDetection, Q as Table, Qt as ChevronsDownUp, R as ContextAwarePanelItem, Rt as EmotionCacheProvider, S as downloadSizeLimitAtom, St as Maps, T as getColumnCountForDisplay, Tt as dateToLocalISOTime, U as isCellAwareAtom, Ut as Funnel, V as contextAwarePanelOwner, Vt as TextWrap, W as SlotNames, Wt as EyeOff, X as Fill, Xt as ChevronsRight, Yt as ChevronsUpDown, Z as Provider$1, Zt as ChevronsLeft, _ as downloadBlob, _t as SELECT_COLUMN_ID, at as generateColumns, b as Progress, bt as getMimeValues, c as Slide, ct as ColumnChartContext, d as JsonOutput, dt as useIntersectionObserver, en as ArrowDownWideNarrow, et as TableCell, f as OutputArea, ft as usePrevious$1, g as ADD_PRINTING_CLASS, gt as INDEX_COLUMN_NAME, h as InstallPackageButton, ht as loadTableData, it as NAMELESS_COLUMN_PREFIX, j as Command, jt as ChartInfoState, k as Combobox, kt as TabsTrigger, l as RadioGroup, lt as ColumnChartSpecModel, m as DataTable, mt as loadTableAndRawData, n as marimoVersionAtom, nt as TableHeader, o as SLIDE_TYPE_OPTIONS_BY_VALUE, ot as inferFieldTypes, p as OutputRenderer, pt as getPageIndexForRow, qt as Download, r as showCodeInRunModeAtom, rt as TableRow, st as renderCellValue, t as useNotebookCodeAvailable, tt as TableHead, u as RadioGroupItem, ut as DelayMount, v as downloadByURL, vt as TOO_MANY_ROWS, w as prettifyRowCount, wt as dateToLocalISODateTime, x as Filenames, xt as isNullishFilter, y as downloadHTMLAsImage, yt as toFieldTypes, z as PANEL_TYPES, zt as $fae977aafc393c5c$export$588937bcd60ade55, __tla as __tla_2 } from "./code-visibility-Dek9JNl9.js";
29
+ import { $ as TableBody, $t as ChevronLeft, A as ComboboxItem, At as ChartErrorState, B as contextAwarePanelOpen, Bt as $fae977aafc393c5c$export$6b862160d295c8e, C as prettifyRowColumnCount, Ct as dateToLocalISODate, D as DatePicker, Dt as TabsContent, E as useInternalStateWithSync, Et as Tabs, F as CommandList, Ft as RenderTextWithLinks, G as slotsController, H as contextAwarePanelType, Ht as GripHorizontal, I as CommandSeparator, It as Kbd, Jt as Code, K as Toggle, Kt as Ellipsis, L as smartMatch, Lt as HtmlOutput, M as CommandEmpty, Mt as ChartLoadingState, N as CommandInput, Nt as LazyVegaEmbed, O as DateRangePicker, Ot as TabsList, P as CommandItem, Pt as useOverflowDetection, Q as Table, Qt as ChevronsDownUp, R as ContextAwarePanelItem, Rt as EmotionCacheProvider, S as downloadSizeLimitAtom, St as Maps, T as getColumnCountForDisplay, Tt as dateToLocalISOTime, U as isCellAwareAtom, Ut as Funnel, V as contextAwarePanelOwner, Vt as TextWrap, W as SlotNames, Wt as EyeOff, X as Fill, Xt as ChevronsRight, Yt as ChevronsUpDown, Z as Provider$1, Zt as ChevronsLeft, _ as downloadBlob, _t as SELECT_COLUMN_ID, at as generateColumns, b as Progress, bt as getMimeValues, c as Slide, ct as ColumnChartContext, d as JsonOutput, dt as useIntersectionObserver, en as ArrowDownWideNarrow, et as TableCell, f as OutputArea, ft as usePrevious$1, g as ADD_PRINTING_CLASS, gt as INDEX_COLUMN_NAME, h as InstallPackageButton, ht as loadTableData, it as NAMELESS_COLUMN_PREFIX, j as Command, jt as ChartInfoState, k as Combobox, kt as TabsTrigger, l as RadioGroup, lt as ColumnChartSpecModel, m as DataTable, mt as loadTableAndRawData, n as marimoVersionAtom, nt as TableHeader, o as SLIDE_TYPE_OPTIONS_BY_VALUE, ot as inferFieldTypes, p as OutputRenderer, pt as getPageIndexForRow, qt as Download, r as showCodeInRunModeAtom, rt as TableRow, st as renderCellValue, t as useNotebookCodeAvailable, tt as TableHead, u as RadioGroupItem, ut as DelayMount, v as downloadByURL, vt as TOO_MANY_ROWS, w as prettifyRowCount, wt as dateToLocalISODateTime, x as Filenames, xt as isNullishFilter, y as downloadHTMLAsImage, yt as toFieldTypes, z as PANEL_TYPES, zt as $fae977aafc393c5c$export$588937bcd60ade55, __tla as __tla_2 } from "./code-visibility-WES7G1ix.js";
30
30
  import { c as Calendar, i as createReducerAndAtoms, n as useOnUnmount, o as ToggleLeft, t as useOnMount } from "./useLifecycle-BBO9PIph.js";
31
31
  import { t as Check } from "./check-DTbrK0zt.js";
32
32
  import { A as Trigger$1, C as $a916eb452884faea$export$b7a616150fdb9f44, E as $18f2051aff69b9bf$export$a54013f0d02a8f82, F as X, L as ChevronDown, M as usePrevious$2, N as useDirection, P as createCollection, S as logNever, T as $18f2051aff69b9bf$export$43bb16f9c6d9e3f7, a as SelectGroup, c as SelectSeparator, d as NativeSelect, i as SelectContent, j as clamp$2, k as Icon, l as SelectTrigger, n as capitalize, o as SelectItem, r as Select, s as SelectLabel, t as Strings, u as SelectValue, x as assertNever } from "./strings-Bu3vlb6W.js";
33
33
  import { I as $64fa3d84918910a7$export$29f1550f4b0d4415, K as useDebounceControlledState, L as $64fa3d84918910a7$export$4d86445c2cf5e3, Mt as $65484d02dcb7eb3e$export$457c3d6518dd4c6f, Nt as $3ef42575df84b30b$export$9d1611c77c2fe928, V as $64fa3d84918910a7$export$df3a06d6289f983e, Vt as $ff5963eb1fccf552$export$e08e3b67e392101e, a as NumberField, b as DropdownMenuTrigger, c as prettyNumber, d as DropdownMenuContent, f as DropdownMenuGroup, fn as Circle, g as DropdownMenuSeparator, i as OnBlurredInput, it as $701a24aa0da5b062$export$ea18c227d4417cc3, l as prettyScientificNumber, m as DropdownMenuLabel, n as DebouncedNumberInput, p as DropdownMenuItem, pn as ChevronRight, q as useDebouncedCallback, r as Input, rt as $f7dceffc5ad7768b$export$4e328f61c538687f, t as DebouncedInput, u as DropdownMenu, ut as $6179b936705e76d3$export$ae780daf29e6d456, vt as $458b0a5536c1a7cf$export$40bfa8c7b0832715 } from "./input-_2sjvfne.js";
34
34
  import { _ as isWasm, c as asRemoteURL, d as isStaticNotebook, f as appendQueryParams, g as Deferred, m as require_cuid2, u as getStaticVirtualFiles, v as CircleQuestionMark } from "./toDate-x-WRDCH7.js";
35
- import { a as MarimoIncomingMessageEvent, c as MarimoValueUpdateEvent, d as Square, f as File, i as PythonIcon, l as createInputEvent, n as blobToString, o as MarimoValueInputEvent, r as filesToBase64, s as MarimoValueReadyEvent, t as processOutput, u as deserializeBlob } from "./process-output-vFgqkyUT.js";
35
+ import { a as MarimoIncomingMessageEvent, c as MarimoValueUpdateEvent, d as Square, f as File, i as PythonIcon, l as createInputEvent, n as blobToString, o as MarimoValueInputEvent, r as filesToBase64, s as MarimoValueReadyEvent, t as processOutput, u as deserializeBlob } from "./process-output-BOXi9fnS.js";
36
36
  import { n as Trash, r as Pencil, t as BulkEdit } from "./types-CVvp1fKr.js";
37
37
  import { n as require_prop_types, r as Plus, t as ErrorBoundary } from "./ErrorBoundary-rULOrC_p.js";
38
38
  import { t as require_react_dom } from "./react-dom-BTJzcVJ9.js";
@@ -41,7 +41,7 @@ import { $ as StyleNamespace, A as looseObject, B as union, C as any, F as optio
41
41
  import { i as prettyError, n as ErrorBanner, r as CellNotInitializedError, t as Banner } from "./error-banner-5bz0L9hS.js";
42
42
  import { t as Label } from "./label-LWtdw5i8.js";
43
43
  import { a as TooltipRoot, i as TooltipProvider, n as TooltipContent, o as TooltipTrigger, r as TooltipPortal, t as Tooltip } from "./tooltip-C5FYOpQc.js";
44
- import { f as CopyClipboardIcon } from "./extends-BgdxCfYu.js";
44
+ import { f as CopyClipboardIcon } from "./extends-CkydH1Q5.js";
45
45
  import { a as get_default } from "./hasIn-Deg7jl_j.js";
46
46
  import { n as _baseSet_default, t as pick_default } from "./pick-D1Qo8s2C.js";
47
47
  import { i as debounce_default, r as KnownQueryParams, t as CSSClasses } from "./constants-T20xxyNf.js";
@@ -57,7 +57,7 @@ import "./dist-CcXxepx6.js";
57
57
  import "./dist-DxvORzUR.js";
58
58
  import "./dist-DqAWR3CS.js";
59
59
  import { r as python } from "./dist-BotSqB48.js";
60
- import { n as minimalSetup, t as esm_default } from "./esm-BfhQmZjp.js";
60
+ import { n as minimalSetup, t as esm_default } from "./esm-CCuYCd3R.js";
61
61
  import "./purify.es-H92eMd9-.js";
62
62
  import { i as AlertTitle, n as Alert, r as AlertDescription } from "./formats-DHxc-FdY.js";
63
63
  import "./vega-loader.browser-CZ-J8Py3.js";
@@ -5590,7 +5590,7 @@ let __tla = Promise.all([
5590
5590
  };
5591
5591
  }
5592
5592
  };
5593
- var LazyChatbot = import_react.lazy(() => import("./chat-ui-1rLrACXD.js").then((e) => ({
5593
+ var LazyChatbot = import_react.lazy(() => import("./chat-ui-K84W5ecY.js").then((e) => ({
5594
5594
  default: e.Chatbot
5595
5595
  }))), messageSchema = array(object({
5596
5596
  id: string(),
@@ -5739,7 +5739,7 @@ let __tla = Promise.all([
5739
5739
  "time",
5740
5740
  "unknown"
5741
5741
  ];
5742
- var import_compiler_runtime$78 = require_compiler_runtime(), LazyDataEditor = import_react.lazy(() => import("./glide-data-editor-BOmK9ETQ.js").then(async (m) => {
5742
+ var import_compiler_runtime$78 = require_compiler_runtime(), LazyDataEditor = import_react.lazy(() => import("./glide-data-editor-CgIxTzAP.js").then(async (m) => {
5743
5743
  await m.__tla;
5744
5744
  return m;
5745
5745
  }));
@@ -36116,7 +36116,7 @@ ${c}
36116
36116
  if (l && l !== "slide") return l;
36117
36117
  if (c == null ? void 0 : c.has(e)) return "skip";
36118
36118
  }
36119
- var LazySlidesComponent = import_react.lazy(() => import("./reveal-component-C8AmnVfj.js"));
36119
+ var LazySlidesComponent = import_react.lazy(() => import("./reveal-component-DRTYmMER.js"));
36120
36120
  const SlidesLayoutRenderer = ({ layout: e, setLayout: r, cells: c, mode: l }) => {
36121
36121
  var _a3;
36122
36122
  let u = useAtomValue(kioskModeAtom), d = l === "read" || u, f = useAtomValue(numColumnsAtom) > 1, [p, m] = (0, import_react.useState)(null), { slideCells: h, skippedIds: g, noOutputIds: _, slideTypes: v, startCellIndex: y } = (0, import_react.useMemo)(() => computeSlideCellsInfo(c, e), [
@@ -1,6 +1,6 @@
1
1
  import { s as __toESM } from "./chunk-BNovOVIE.js";
2
2
  import { t as require_compiler_runtime } from "./compiler-runtime-CEbnTgxf.js";
3
- import { it as parseHtmlContent, rt as ansiToPlainText } from "./html-to-image-CnBdHeLh.js";
3
+ import { it as parseHtmlContent, rt as ansiToPlainText } from "./html-to-image-B93KtAVY.js";
4
4
  import { u as createLucideIcon } from "./dist-C1BYNeCR.js";
5
5
  import { t as Strings } from "./strings-Bu3vlb6W.js";
6
6
  import { t as require_jsx_runtime } from "./jsx-runtime-DebpN0FN.js";
@@ -6,10 +6,10 @@ import { s as __toESM } from "./chunk-BNovOVIE.js";
6
6
  import { _ as Logger, g as cn, h as Events, l as useEventListener, t as Button } from "./button-C5K9fIPF.js";
7
7
  import { t as require_react } from "./react-DA-nE2FX.js";
8
8
  import { t as require_compiler_runtime } from "./compiler-runtime-CEbnTgxf.js";
9
- import { ct as kioskModeAtom } from "./html-to-image-CnBdHeLh.js";
9
+ import { ct as kioskModeAtom } from "./html-to-image-B93KtAVY.js";
10
10
  import "./chunk-5FQGJX7Z-BNjes6Yx.js";
11
11
  import { u as createLucideIcon } from "./dist-C1BYNeCR.js";
12
- import { Gt as Expand, J as PanelGroup, Jt as Code, Wt as EyeOff, Y as PanelResizeHandle, a as DEFAULT_SLIDE_TYPE, c as Slide, i as DEFAULT_DECK_TRANSITION, q as Panel, s as SlideSidebar, t as useNotebookCodeAvailable } from "./code-visibility-Dek9JNl9.js";
12
+ import { Gt as Expand, J as PanelGroup, Jt as Code, Wt as EyeOff, Y as PanelResizeHandle, a as DEFAULT_SLIDE_TYPE, c as Slide, i as DEFAULT_DECK_TRANSITION, q as Panel, s as SlideSidebar, t as useNotebookCodeAvailable } from "./code-visibility-WES7G1ix.js";
13
13
  import { q as useDebouncedCallback } from "./input-_2sjvfne.js";
14
14
  import "./toDate-x-WRDCH7.js";
15
15
  import "./react-dom-BTJzcVJ9.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marimo-team/islands",
3
- "version": "0.23.10-dev5",
3
+ "version": "0.23.10-dev7",
4
4
  "main": "dist/main.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -5,8 +5,8 @@ import type {
5
5
  RowSelectionState,
6
6
  SortingState,
7
7
  } from "@tanstack/react-table";
8
- import { fireEvent, render, screen, within } from "@testing-library/react";
9
- import { describe, expect, it, vi } from "vitest";
8
+ import { act, fireEvent, render, screen, within } from "@testing-library/react";
9
+ import { afterEach, describe, expect, it, vi } from "vitest";
10
10
  import { TooltipProvider } from "@/components/ui/tooltip";
11
11
  import { DataTable } from "../data-table";
12
12
 
@@ -16,6 +16,12 @@ interface TestData {
16
16
  }
17
17
 
18
18
  describe("DataTable", () => {
19
+ // Restore real timers unconditionally so a failed assertion in a
20
+ // fake-timer test can't leak fake timers into later tests.
21
+ afterEach(() => {
22
+ vi.useRealTimers();
23
+ });
24
+
19
25
  it("should maintain selection state when remounted", () => {
20
26
  const mockOnRowSelectionChange = vi.fn();
21
27
  const testData: TestData[] = [
@@ -63,17 +69,15 @@ describe("DataTable", () => {
63
69
  expect(commonProps.rowSelection).toEqual(initialRowSelection);
64
70
  });
65
71
 
66
- it("applies hoverTemplate to the row title using row values", () => {
72
+ it("shows the hoverTemplate text as a styled tooltip on hover", async () => {
73
+ vi.useFakeTimers();
67
74
  interface RowData {
68
75
  id: number;
69
76
  first: string;
70
77
  last: string;
71
78
  }
72
79
 
73
- const testData: RowData[] = [
74
- { id: 1, first: "Michael", last: "Scott" },
75
- { id: 2, first: "Jim", last: "Halpert" },
76
- ];
80
+ const testData: RowData[] = [{ id: 1, first: "Michael", last: "Scott" }];
77
81
 
78
82
  const columns: ColumnDef<RowData>[] = [
79
83
  { accessorKey: "first", header: "First" },
@@ -86,7 +90,7 @@ describe("DataTable", () => {
86
90
  data={testData}
87
91
  columns={columns}
88
92
  selection={null}
89
- totalRows={2}
93
+ totalRows={1}
90
94
  totalColumns={2}
91
95
  pagination={false}
92
96
  hoverTemplate={"{{first}} {{last}}"}
@@ -94,11 +98,149 @@ describe("DataTable", () => {
94
98
  </TooltipProvider>,
95
99
  );
96
100
 
97
- // Grab all rows and assert title attribute computed from template
98
101
  const rows = screen.getAllByRole("row");
99
- // The first row is header; subsequent rows correspond to data
100
- expect(rows[1]).toHaveAttribute("title", "Michael Scott");
101
- expect(rows[2]).toHaveAttribute("title", "Jim Halpert");
102
+ // Native title is gone; hover text now comes from the styled tooltip.
103
+ expect(rows[1]).not.toHaveAttribute("title");
104
+
105
+ const cell = within(rows[1]).getAllByRole("cell")[0];
106
+ fireEvent.mouseOver(cell, { buttons: 0 });
107
+ act(() => {
108
+ vi.advanceTimersByTime(400);
109
+ });
110
+
111
+ // Radix renders the content twice (visible + a11y-hidden), so match all.
112
+ expect(screen.getAllByText("Michael Scott").length).toBeGreaterThan(0);
113
+ });
114
+
115
+ it("shows per-cell hover text from cellHoverTexts on hover", () => {
116
+ vi.useFakeTimers();
117
+ const testData: TestData[] = [{ id: 1, name: "Test 1" }];
118
+ const columns: ColumnDef<TestData>[] = [
119
+ { id: "name", accessorKey: "name", header: "Name" },
120
+ ];
121
+
122
+ render(
123
+ <TooltipProvider>
124
+ <DataTable
125
+ data={testData}
126
+ columns={columns}
127
+ selection={null}
128
+ totalRows={1}
129
+ totalColumns={1}
130
+ pagination={false}
131
+ cellHoverTexts={{ "0": { name: "per-cell tip" } }}
132
+ />
133
+ </TooltipProvider>,
134
+ );
135
+
136
+ const cell = within(screen.getAllByRole("row")[1]).getByRole("cell");
137
+ fireEvent.mouseOver(cell, { buttons: 0 });
138
+ act(() => {
139
+ vi.advanceTimersByTime(400);
140
+ });
141
+
142
+ expect(screen.getAllByText("per-cell tip").length).toBeGreaterThan(0);
143
+ });
144
+
145
+ it("links the focused cell to the tooltip content for assistive tech", () => {
146
+ const testData: TestData[] = [{ id: 1, name: "Test 1" }];
147
+ const columns: ColumnDef<TestData>[] = [
148
+ { id: "name", accessorKey: "name", header: "Name" },
149
+ ];
150
+
151
+ render(
152
+ <TooltipProvider>
153
+ <DataTable
154
+ data={testData}
155
+ columns={columns}
156
+ selection={null}
157
+ totalRows={1}
158
+ totalColumns={1}
159
+ pagination={false}
160
+ cellHoverTexts={{ "0": { name: "focus tip" } }}
161
+ />
162
+ </TooltipProvider>,
163
+ );
164
+
165
+ const cell = within(screen.getAllByRole("row")[1]).getByRole("cell");
166
+ fireEvent.focus(cell);
167
+
168
+ const describedBy = cell.getAttribute("aria-describedby");
169
+ expect(describedBy).toBeTruthy();
170
+ expect(document.getElementById(describedBy as string)).toHaveTextContent(
171
+ "focus tip",
172
+ );
173
+
174
+ fireEvent.blur(cell);
175
+ expect(cell).not.toHaveAttribute("aria-describedby");
176
+ });
177
+
178
+ it("does not show a tooltip on pointer-induced focus", () => {
179
+ const testData: TestData[] = [{ id: 1, name: "Test 1" }];
180
+ const columns: ColumnDef<TestData>[] = [
181
+ { id: "name", accessorKey: "name", header: "Name" },
182
+ ];
183
+
184
+ render(
185
+ <TooltipProvider>
186
+ <DataTable
187
+ data={testData}
188
+ columns={columns}
189
+ selection={null}
190
+ totalRows={1}
191
+ totalColumns={1}
192
+ pagination={false}
193
+ cellHoverTexts={{ "0": { name: "click tip" } }}
194
+ />
195
+ </TooltipProvider>,
196
+ );
197
+
198
+ const cell = within(screen.getAllByRole("row")[1]).getByRole("cell");
199
+ // A click focuses the cell; the resulting focus must not show a tooltip.
200
+ fireEvent.mouseDown(cell);
201
+ fireEvent.focus(cell);
202
+
203
+ expect(cell).not.toHaveAttribute("aria-describedby");
204
+ expect(screen.queryByText("click tip")).toBeNull();
205
+ });
206
+
207
+ it("does not let a pending hover timer overwrite a focus tooltip", () => {
208
+ vi.useFakeTimers();
209
+ interface RowData {
210
+ id: number;
211
+ a: string;
212
+ b: string;
213
+ }
214
+ const testData: RowData[] = [{ id: 1, a: "a", b: "b" }];
215
+ const columns: ColumnDef<RowData>[] = [
216
+ { id: "a", accessorKey: "a", header: "A" },
217
+ { id: "b", accessorKey: "b", header: "B" },
218
+ ];
219
+
220
+ render(
221
+ <TooltipProvider>
222
+ <DataTable
223
+ data={testData}
224
+ columns={columns}
225
+ selection={null}
226
+ totalRows={1}
227
+ totalColumns={2}
228
+ pagination={false}
229
+ cellHoverTexts={{ "0": { a: "hover A", b: "focus B" } }}
230
+ />
231
+ </TooltipProvider>,
232
+ );
233
+
234
+ const cells = within(screen.getAllByRole("row")[1]).getAllByRole("cell");
235
+ // Start a pending hover-show on cell A, then focus cell B before it fires.
236
+ fireEvent.mouseOver(cells[0], { buttons: 0 });
237
+ fireEvent.focus(cells[1]);
238
+ act(() => {
239
+ vi.advanceTimersByTime(400);
240
+ });
241
+
242
+ expect(screen.getAllByText("focus B").length).toBeGreaterThan(0);
243
+ expect(screen.queryByText("hover A")).toBeNull();
102
244
  });
103
245
 
104
246
  it("does not virtualize small datasets without pagination", () => {
@@ -0,0 +1,60 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+ import type { Cell } from "@tanstack/react-table";
3
+ import { describe, expect, it } from "vitest";
4
+ import { applyHoverTemplate, computeCellTooltipContent } from "../content";
5
+
6
+ function fakeCell(columnId: string, value: unknown, hoverTitle?: string) {
7
+ return {
8
+ column: { id: columnId },
9
+ getValue: () => value,
10
+ getHoverTitle: () => hoverTitle,
11
+ } as unknown as Cell<unknown, unknown>;
12
+ }
13
+
14
+ describe("applyHoverTemplate", () => {
15
+ it("substitutes column placeholders", () => {
16
+ const cells = [fakeCell("first", "Michael"), fakeCell("last", "Scott")];
17
+ expect(applyHoverTemplate("{{first}} {{last}}", cells)).toBe(
18
+ "Michael Scott",
19
+ );
20
+ });
21
+
22
+ it("renders nulls as empty strings", () => {
23
+ const cells = [fakeCell("a", null)];
24
+ expect(applyHoverTemplate("[{{a}}]", cells)).toBe("[]");
25
+ });
26
+
27
+ it("leaves unknown placeholders intact", () => {
28
+ expect(applyHoverTemplate("{{missing}}", [fakeCell("a", 1)])).toBe(
29
+ "{{missing}}",
30
+ );
31
+ });
32
+ });
33
+
34
+ describe("computeCellTooltipContent", () => {
35
+ it("prefers cell-level hover title", () => {
36
+ const cell = fakeCell("a", 1, "cell text");
37
+ expect(computeCellTooltipContent(cell, "{{a}}")).toBe("cell text");
38
+ });
39
+
40
+ it("falls back to row template when no cell title", () => {
41
+ const cell = {
42
+ column: { id: "first" },
43
+ getValue: () => "X",
44
+ getHoverTitle: () => undefined,
45
+ row: {
46
+ getVisibleCells: () => [
47
+ fakeCell("first", "Jim"),
48
+ fakeCell("last", "Halpert"),
49
+ ],
50
+ },
51
+ } as unknown as Cell<unknown, unknown>;
52
+ expect(computeCellTooltipContent(cell, "{{first}} {{last}}")).toBe(
53
+ "Jim Halpert",
54
+ );
55
+ });
56
+
57
+ it("returns undefined with no title and no template", () => {
58
+ expect(computeCellTooltipContent(fakeCell("a", 1), null)).toBeUndefined();
59
+ });
60
+ });
@@ -0,0 +1,44 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+ import type { Cell, RowData } from "@tanstack/react-table";
3
+ import type { ReactNode } from "react";
4
+ import { stringifyUnknownValue } from "../utils";
5
+
6
+ export function applyHoverTemplate<TData extends RowData>(
7
+ template: string,
8
+ cells: Cell<TData, unknown>[],
9
+ ): string {
10
+ const variableRegex = /{{(\w+)}}/g;
11
+ const idToValue = new Map<string, string>();
12
+ for (const c of cells) {
13
+ const s = stringifyUnknownValue({
14
+ value: c.getValue(),
15
+ nullAsEmptyString: true,
16
+ });
17
+ idToValue.set(c.column.id, s);
18
+ }
19
+ return template.replaceAll(variableRegex, (_substr, varName: string) => {
20
+ const val = idToValue.get(varName);
21
+ return val === undefined ? `{{${varName}}}` : val;
22
+ });
23
+ }
24
+
25
+ /**
26
+ * Resolve the tooltip content for a hovered cell.
27
+ *
28
+ * Cell-level (callable `hover_template`) takes precedence; otherwise the
29
+ * row-level string template is rendered against the row's visible cells.
30
+ * Returns `undefined` when there is nothing to show.
31
+ */
32
+ export function computeCellTooltipContent<TData extends RowData>(
33
+ cell: Cell<TData, unknown>,
34
+ hoverTemplate: string | null,
35
+ ): ReactNode {
36
+ const cellTitle = cell.getHoverTitle?.();
37
+ if (cellTitle != null && cellTitle !== "") {
38
+ return cellTitle;
39
+ }
40
+ if (hoverTemplate) {
41
+ return applyHoverTemplate(hoverTemplate, cell.row.getVisibleCells());
42
+ }
43
+ return undefined;
44
+ }