@marimo-team/islands 0.23.10-dev1 → 0.23.10-dev12
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/{any-language-editor-DfdpyDv_.js → any-language-editor-CiES2a2h.js} +2 -2
- package/dist/{chat-ui-ChD4VvCo.js → chat-ui-K84W5ecY.js} +5 -5
- package/dist/{code-visibility-B3oOX_TK.js → code-visibility-DK0V-P30.js} +1036 -882
- package/dist/{copy-BuQpJEzp.js → copy-5jQ_kGE1.js} +32 -32
- package/dist/{esm-BfhQmZjp.js → esm-CCuYCd3R.js} +1 -1
- package/dist/{extends-BgdxCfYu.js → extends-CkydH1Q5.js} +1 -1
- package/dist/{glide-data-editor-BOmK9ETQ.js → glide-data-editor-CgIxTzAP.js} +1 -1
- package/dist/{html-to-image-BHv7CEU_.js → html-to-image-B93KtAVY.js} +2135 -2118
- package/dist/main.js +10 -10
- package/dist/{process-output-BvySRgli.js → process-output-BOXi9fnS.js} +1 -1
- package/dist/{reveal-component-Dj4x14QX.js → reveal-component-DLdvi2qq.js} +2 -2
- package/package.json +1 -1
- package/src/components/data-table/__tests__/data-table.test.tsx +154 -12
- package/src/components/data-table/hover-tooltip/__tests__/content.test.ts +60 -0
- package/src/components/data-table/hover-tooltip/content.ts +44 -0
- package/src/components/data-table/hover-tooltip/hover-tooltip.tsx +55 -0
- package/src/components/data-table/hover-tooltip/use-table-hover-tooltip.ts +159 -0
- package/src/components/data-table/renderers.tsx +27 -43
- package/src/components/editor/cell/cell-context-menu.tsx +15 -2
- package/src/components/ui/__tests__/use-toast.test.ts +75 -0
- package/src/components/ui/use-toast.ts +33 -13
- package/src/core/cells/__tests__/__snapshots__/cells.test.ts.snap +0 -28
- package/src/core/cells/__tests__/cell.test.ts +29 -2
- package/src/core/cells/__tests__/document-changes.test.ts +201 -2
- package/src/core/cells/cell.ts +5 -1
- package/src/core/cells/document-changes.ts +61 -3
- package/src/core/codemirror/go-to-definition/__tests__/utils.test.ts +37 -0
- package/src/core/codemirror/go-to-definition/commands.ts +17 -9
- package/src/core/codemirror/go-to-definition/utils.ts +1 -0
- package/src/core/network/__tests__/requests-static.test.ts +30 -0
- package/src/core/network/requests-static.ts +14 -10
- package/src/plugins/layout/DownloadPlugin.tsx +1 -1
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-
|
|
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-
|
|
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-
|
|
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-DK0V-P30.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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
}));
|
|
@@ -26689,7 +26689,7 @@ ${c}
|
|
|
26689
26689
|
l(true);
|
|
26690
26690
|
try {
|
|
26691
26691
|
let c3 = await r({});
|
|
26692
|
-
downloadByURL(c3.data, c3.filename ?? e.filename ?? void 0);
|
|
26692
|
+
downloadByURL(c3.data || e.data, c3.filename ?? e.filename ?? void 0);
|
|
26693
26693
|
} catch (e2) {
|
|
26694
26694
|
toast({
|
|
26695
26695
|
title: "Failed to download",
|
|
@@ -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-
|
|
36119
|
+
var LazySlidesComponent = import_react.lazy(() => import("./reveal-component-DLdvi2qq.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-
|
|
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-
|
|
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-
|
|
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-DK0V-P30.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
|
@@ -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("
|
|
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={
|
|
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
|
-
//
|
|
100
|
-
expect(rows[1]).toHaveAttribute("title"
|
|
101
|
-
|
|
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
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
import {
|
|
3
|
+
TooltipContent,
|
|
4
|
+
TooltipPortal,
|
|
5
|
+
TooltipRoot,
|
|
6
|
+
TooltipTrigger,
|
|
7
|
+
} from "@/components/ui/tooltip";
|
|
8
|
+
import type { HoverTooltipState } from "./use-table-hover-tooltip";
|
|
9
|
+
|
|
10
|
+
interface HoverTooltipProps {
|
|
11
|
+
state: HoverTooltipState | null;
|
|
12
|
+
contentId: string;
|
|
13
|
+
onClose: () => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* A single radix tooltip whose anchor is repositioned to the hovered cell.
|
|
18
|
+
* Rendering one instance per table (instead of one per cell) keeps the cost
|
|
19
|
+
* constant regardless of how many cells are on screen.
|
|
20
|
+
*/
|
|
21
|
+
export const HoverTooltip = ({
|
|
22
|
+
state,
|
|
23
|
+
contentId,
|
|
24
|
+
onClose,
|
|
25
|
+
}: HoverTooltipProps) => {
|
|
26
|
+
return (
|
|
27
|
+
<TooltipRoot
|
|
28
|
+
open={state != null}
|
|
29
|
+
onOpenChange={(open) => {
|
|
30
|
+
if (!open) {
|
|
31
|
+
onClose();
|
|
32
|
+
}
|
|
33
|
+
}}
|
|
34
|
+
delayDuration={0}
|
|
35
|
+
disableHoverableContent={true}
|
|
36
|
+
>
|
|
37
|
+
<TooltipTrigger asChild={true}>
|
|
38
|
+
<div
|
|
39
|
+
aria-hidden={true}
|
|
40
|
+
style={{
|
|
41
|
+
position: "fixed",
|
|
42
|
+
top: state?.rect.top ?? 0,
|
|
43
|
+
left: state?.rect.left ?? 0,
|
|
44
|
+
width: state?.rect.width ?? 0,
|
|
45
|
+
height: state?.rect.height ?? 0,
|
|
46
|
+
pointerEvents: "none",
|
|
47
|
+
}}
|
|
48
|
+
/>
|
|
49
|
+
</TooltipTrigger>
|
|
50
|
+
<TooltipPortal>
|
|
51
|
+
<TooltipContent id={contentId}>{state?.content}</TooltipContent>
|
|
52
|
+
</TooltipPortal>
|
|
53
|
+
</TooltipRoot>
|
|
54
|
+
);
|
|
55
|
+
};
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
import type { Cell, RowData, Table } from "@tanstack/react-table";
|
|
3
|
+
import {
|
|
4
|
+
type ReactNode,
|
|
5
|
+
useEffect,
|
|
6
|
+
useId,
|
|
7
|
+
useLayoutEffect,
|
|
8
|
+
useRef,
|
|
9
|
+
useState,
|
|
10
|
+
} from "react";
|
|
11
|
+
import useEvent from "react-use-event-hook";
|
|
12
|
+
import { computeCellTooltipContent } from "./content";
|
|
13
|
+
|
|
14
|
+
// Matches the default TooltipProvider delay (MarimoApp.tsx) for visual parity
|
|
15
|
+
// with the rest of the app's tooltips.
|
|
16
|
+
const TOOLTIP_DELAY_MS = 400;
|
|
17
|
+
|
|
18
|
+
export interface HoverTooltipState {
|
|
19
|
+
rect: { top: number; left: number; width: number; height: number };
|
|
20
|
+
content: ReactNode;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function useTableHoverTooltip<TData extends RowData>({
|
|
24
|
+
table,
|
|
25
|
+
}: {
|
|
26
|
+
table: Table<TData>;
|
|
27
|
+
}) {
|
|
28
|
+
const hoverTemplate = table.getState().cellHoverTemplate || null;
|
|
29
|
+
const [tooltipState, setTooltipState] = useState<HoverTooltipState | null>(
|
|
30
|
+
null,
|
|
31
|
+
);
|
|
32
|
+
const timer = useRef<number | null>(null);
|
|
33
|
+
|
|
34
|
+
// Stable id linking the focused/hovered cell to the tooltip content for
|
|
35
|
+
// assistive tech (the radix trigger is an aria-hidden phantom anchor).
|
|
36
|
+
const tooltipContentId = useId();
|
|
37
|
+
const anchorCell = useRef<HTMLElement | null>(null);
|
|
38
|
+
// Focus fires for pointer interactions too; track pointer state so
|
|
39
|
+
// click/drag-select focus doesn't show a tooltip (keyboard focus still does).
|
|
40
|
+
const pointerDown = useRef(false);
|
|
41
|
+
|
|
42
|
+
const clearTimer = () => {
|
|
43
|
+
if (timer.current != null) {
|
|
44
|
+
clearTimeout(timer.current);
|
|
45
|
+
timer.current = null;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const hideTooltip = useEvent(() => {
|
|
50
|
+
clearTimer();
|
|
51
|
+
setTooltipState(null);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const showFor = (target: HTMLElement, content: ReactNode) => {
|
|
55
|
+
anchorCell.current = target;
|
|
56
|
+
const r = target.getBoundingClientRect();
|
|
57
|
+
setTooltipState({
|
|
58
|
+
rect: { top: r.top, left: r.left, width: r.width, height: r.height },
|
|
59
|
+
content,
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Point the real cell at the tooltip content while it is shown. Done in a
|
|
64
|
+
// layout effect (after commit) so React's re-render from `setTooltipState`
|
|
65
|
+
// can't clobber an imperatively set attribute; cleanup unlinks the previous
|
|
66
|
+
// cell.
|
|
67
|
+
useLayoutEffect(() => {
|
|
68
|
+
if (!tooltipState) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const cell = anchorCell.current;
|
|
72
|
+
cell?.setAttribute("aria-describedby", tooltipContentId);
|
|
73
|
+
return () => cell?.removeAttribute("aria-describedby");
|
|
74
|
+
}, [tooltipState, tooltipContentId]);
|
|
75
|
+
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
const onDown = () => {
|
|
78
|
+
pointerDown.current = true;
|
|
79
|
+
};
|
|
80
|
+
const onUp = () => {
|
|
81
|
+
pointerDown.current = false;
|
|
82
|
+
};
|
|
83
|
+
window.addEventListener("mousedown", onDown, { capture: true });
|
|
84
|
+
window.addEventListener("mouseup", onUp, { capture: true });
|
|
85
|
+
return () => {
|
|
86
|
+
window.removeEventListener("mousedown", onDown, { capture: true });
|
|
87
|
+
window.removeEventListener("mouseup", onUp, { capture: true });
|
|
88
|
+
};
|
|
89
|
+
}, []);
|
|
90
|
+
|
|
91
|
+
const handleCellMouseOver = useEvent(
|
|
92
|
+
(e: React.MouseEvent, cell: Cell<TData, unknown>) => {
|
|
93
|
+
// Suppress while a mouse button is held (range-select drag).
|
|
94
|
+
if (e.buttons !== 0) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const target = e.currentTarget as HTMLElement;
|
|
98
|
+
const content = computeCellTooltipContent(cell, hoverTemplate);
|
|
99
|
+
if (content == null || content === "") {
|
|
100
|
+
hideTooltip();
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
clearTimer();
|
|
104
|
+
timer.current = window.setTimeout(
|
|
105
|
+
() => showFor(target, content),
|
|
106
|
+
TOOLTIP_DELAY_MS,
|
|
107
|
+
);
|
|
108
|
+
},
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
const handleCellMouseLeave = useEvent(() => hideTooltip());
|
|
112
|
+
|
|
113
|
+
// Keyboard parity: cells are tabIndex=0, native `title` showed on focus too.
|
|
114
|
+
const handleCellFocus = useEvent(
|
|
115
|
+
(e: React.FocusEvent, cell: Cell<TData, unknown>) => {
|
|
116
|
+
// Cancel any pending hover-show so a stale timer can't overwrite the
|
|
117
|
+
// focus-triggered tooltip after the delay.
|
|
118
|
+
clearTimer();
|
|
119
|
+
// Focus also fires for click/drag-select; only keyboard focus (no pointer
|
|
120
|
+
// held) should show the tooltip, mirroring the hover drag suppression.
|
|
121
|
+
if (pointerDown.current) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const content = computeCellTooltipContent(cell, hoverTemplate);
|
|
125
|
+
if (content == null || content === "") {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
showFor(e.currentTarget as HTMLElement, content);
|
|
129
|
+
},
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
const handleCellBlur = useEvent(() => hideTooltip());
|
|
133
|
+
|
|
134
|
+
// The anchor rect is captured at hover time, so any scroll or resize leaves
|
|
135
|
+
// it stale; hide instead of tracking. Capture catches scrolls inside the
|
|
136
|
+
// table's own container too (scroll events don't bubble but do fire in
|
|
137
|
+
// capture).
|
|
138
|
+
useEffect(() => {
|
|
139
|
+
const opts = { passive: true, capture: true } as const;
|
|
140
|
+
window.addEventListener("scroll", hideTooltip, opts);
|
|
141
|
+
window.addEventListener("resize", hideTooltip);
|
|
142
|
+
return () => {
|
|
143
|
+
window.removeEventListener("scroll", hideTooltip, { capture: true });
|
|
144
|
+
window.removeEventListener("resize", hideTooltip);
|
|
145
|
+
};
|
|
146
|
+
}, [hideTooltip]);
|
|
147
|
+
|
|
148
|
+
useEffect(() => clearTimer, []);
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
tooltipState,
|
|
152
|
+
tooltipContentId,
|
|
153
|
+
hideTooltip,
|
|
154
|
+
handleCellMouseOver,
|
|
155
|
+
handleCellMouseLeave,
|
|
156
|
+
handleCellFocus,
|
|
157
|
+
handleCellBlur,
|
|
158
|
+
};
|
|
159
|
+
}
|