@marimo-team/frontend 0.19.10-dev9 → 0.19.10

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 (170) hide show
  1. package/dist/assets/{CellStatus-BYjJCgaE.js → CellStatus-BAeLnQ68.js} +1 -1
  2. package/dist/assets/JsonOutput-EJizmc0Z.js +46 -0
  3. package/dist/assets/{LazyAnyLanguageCodeMirror-DgZ8iknE.js → LazyAnyLanguageCodeMirror-BTO7DS3k.js} +2 -2
  4. package/dist/assets/{MarimoErrorOutput-JyWZzx2m.js → MarimoErrorOutput-C90djx1V.js} +1 -1
  5. package/dist/assets/Plot-DolV1EVq.js +3789 -0
  6. package/dist/assets/{RenderHTML-CfzZdtCa.js → RenderHTML-ByTXWSgj.js} +1 -1
  7. package/dist/assets/{add-cell-with-ai-kak40OU9.js → add-cell-with-ai-CnyhGSdf.js} +2 -2
  8. package/dist/assets/{add-database-form-CFtfw8CL.js → add-database-form-DqVwhh_K.js} +1 -1
  9. package/dist/assets/{agent-panel-v3TnlrgF.js → agent-panel-Bh2AfKzA.js} +1 -1
  10. package/dist/assets/{ai-model-dropdown-BWLcucX9.js → ai-model-dropdown-C_pphOGv.js} +1 -1
  11. package/dist/assets/{any-language-editor-BODEG_5g.js → any-language-editor-BdrnE_3i.js} +1 -1
  12. package/dist/assets/{app-config-button-BZ_itkIb.js → app-config-button-BariZTN8.js} +1 -1
  13. package/dist/assets/{cell-editor-QlfCJVx8.js → cell-editor-DMwqDwss.js} +12 -12
  14. package/dist/assets/{cell-link-CrgPSA5u.js → cell-link-D46k36Xe.js} +1 -1
  15. package/dist/assets/{cells-B9Mw1aiY.js → cells-DG7rjkOQ.js} +14 -14
  16. package/dist/assets/{chat-display-dPv3uSbY.js → chat-display-hQ2Dy5fa.js} +1 -1
  17. package/dist/assets/{chat-panel-BH2bCr1s.js → chat-panel-D4Bcz2Sv.js} +1 -1
  18. package/dist/assets/{column-preview-JRDUZ_bG.js → column-preview-DZULCraY.js} +1 -1
  19. package/dist/assets/{command-Ch5r6r1Z.js → command-Djb6VJ8T.js} +1 -1
  20. package/dist/assets/{command-palette-BtxKt_L8.js → command-palette-CXYdoElD.js} +1 -1
  21. package/dist/assets/{common-Bpmeqynd.js → common-B9j0Q2gP.js} +1 -1
  22. package/dist/assets/{datasource-C-tSv_Wf.js → datasource-w1NuMzfo.js} +1 -1
  23. package/dist/assets/dependency-graph-panel-DED_3Q6I.js +4 -0
  24. package/dist/assets/dist--lWkNwLa.js +1 -0
  25. package/dist/assets/{dist-CRjEDsfC.js → dist-8qtC33as.js} +1 -1
  26. package/dist/assets/{dist-CsRJPnA9.js → dist-B0T008FI.js} +1 -1
  27. package/dist/assets/dist-B1Jd_IvQ.js +1 -0
  28. package/dist/assets/dist-B5vqrkGM.js +1 -0
  29. package/dist/assets/dist-BDTS_4tQ.js +1 -0
  30. package/dist/assets/{dist-C5H5qIvq.js → dist-BHxWJlYy.js} +1 -1
  31. package/dist/assets/{dist-sMh6mJ2d.js → dist-BNkRH34W.js} +2 -2
  32. package/dist/assets/{dist-tLOz534J.js → dist-BP9zs-JA.js} +1 -1
  33. package/dist/assets/dist-BeHHM5ER.js +1 -0
  34. package/dist/assets/dist-BkTLZYtq.js +1 -0
  35. package/dist/assets/dist-Bl-MdZlw.js +1 -0
  36. package/dist/assets/dist-Bouhdq2b.js +1 -0
  37. package/dist/assets/{dist-CtrmRz20.js → dist-BqBWjk9M.js} +3 -3
  38. package/dist/assets/{dist-Gqv0jSNr.js → dist-Bt3KRZho.js} +1 -1
  39. package/dist/assets/dist-BtnFSOCN.js +1 -0
  40. package/dist/assets/{dist-B62Xo7-b.js → dist-C0vFollF.js} +1 -1
  41. package/dist/assets/dist-CBgJfRVh.js +1 -0
  42. package/dist/assets/{dist-CEaOyZOW.js → dist-CCX32maQ.js} +1 -1
  43. package/dist/assets/dist-CDTDwVaL.js +1 -0
  44. package/dist/assets/dist-CF9gSfGe.js +2 -0
  45. package/dist/assets/{dist-BpuNldXk.js → dist-CShMY7yu.js} +1 -1
  46. package/dist/assets/{dist-vTtrExIU.js → dist-CUeuuHG_.js} +1 -1
  47. package/dist/assets/dist-CY47RP0T.js +1 -0
  48. package/dist/assets/dist-D1HV0xoM.js +1 -0
  49. package/dist/assets/{dist-BZWmfQbq.js → dist-DDhEwFtR.js} +1 -1
  50. package/dist/assets/{dist-BXnpRw3d.js → dist-DDoTyiJg.js} +1 -1
  51. package/dist/assets/{dist-DLgWirXg.js → dist-DKmfcej2.js} +1 -1
  52. package/dist/assets/{dist-Dv0MupEh.js → dist-DPQdWUrU.js} +1 -1
  53. package/dist/assets/dist-DYjR2ilN.js +1 -0
  54. package/dist/assets/{dist-8kKeYgOg.js → dist-D_DbFqxl.js} +1 -1
  55. package/dist/assets/dist-DdDowPeI.js +1 -0
  56. package/dist/assets/{dist-B83wRp_v.js → dist-Dt1by2kD.js} +1 -1
  57. package/dist/assets/dist-Dt_gLA9L.js +1 -0
  58. package/dist/assets/dist-DxWb3aMV.js +13 -0
  59. package/dist/assets/dist-Dz922FNY.js +1 -0
  60. package/dist/assets/dist-GRPM_OuL.js +1 -0
  61. package/dist/assets/{dist-CF4gkF4y.js → dist-K8bI26Ke.js} +1 -1
  62. package/dist/assets/{dist-Btv5Rh1v.js → dist-LUpffRIq.js} +1 -1
  63. package/dist/assets/{dist-bBwmhqty.js → dist-MyTWYTLd.js} +4 -4
  64. package/dist/assets/dist-NOntBqny.js +1 -0
  65. package/dist/assets/{dist-Dcqqg9UU.js → dist-PzrizfuL.js} +1 -1
  66. package/dist/assets/{dist-CNW1zLeq.js → dist-dnoBqBf0.js} +1 -1
  67. package/dist/assets/{dist-CLc5WXWw.js → dist-kjrKkhgz.js} +1 -1
  68. package/dist/assets/{dist-CoCQUAeM.js → dist-maX8rbyb.js} +1 -1
  69. package/dist/assets/{documentation-panel-DRZKFCxe.js → documentation-panel-CG2t9UyE.js} +1 -1
  70. package/dist/assets/{download-DcO8ZTeW.js → download-B6duieQs.js} +1 -1
  71. package/dist/assets/{edit-page-DFTXtpjP.js → edit-page-ab-pH9y6.js} +7 -7
  72. package/dist/assets/{error-panel-MncM7IO8.js → error-panel-CDGOPmKx.js} +1 -1
  73. package/dist/assets/{esm-D82gQH1f.js → esm-BeuExXY6.js} +1 -1
  74. package/dist/assets/{esm-Bmu2DhPy.js → esm-BqiVbELQ.js} +1 -1
  75. package/dist/assets/{file-explorer-panel-CIhpYw6d.js → file-explorer-panel-OefSIlIn.js} +1 -1
  76. package/dist/assets/{floating-outline-D7MmuvBX.js → floating-outline--UenxIj3.js} +1 -1
  77. package/dist/assets/{focus-Cfq7Qx9L.js → focus-n0WPxeOV.js} +1 -1
  78. package/dist/assets/{form-D6wq0yzP.js → form-C59_eE2a.js} +1 -1
  79. package/dist/assets/{globals-D08tPmbM.js → globals-CP-h_Os3.js} +1 -1
  80. package/dist/assets/{home-page-QeTbaVnU.js → home-page-DnqxPw6c.js} +1 -1
  81. package/dist/assets/{hooks-cYyUDg3L.js → hooks-cl29HCFx.js} +1 -1
  82. package/dist/assets/{html-to-image-DTn-asee.js → html-to-image-BXhYNOMC.js} +1 -1
  83. package/dist/assets/index-B5Sirmey.js +38 -0
  84. package/dist/assets/{kiosk-mode-DjIsTNjl.js → kiosk-mode-CPN0mq4M.js} +1 -1
  85. package/dist/assets/{layout-C7QMwOg5.js → layout-ZZ7iNZSi.js} +3 -3
  86. package/dist/assets/{logs-panel-DSlZEaRL.js → logs-panel-CZIVXROt.js} +1 -1
  87. package/dist/assets/{markdown-renderer-bMEFR0Vj.js → markdown-renderer-CnImn_qm.js} +1 -1
  88. package/dist/assets/{mode-DhrLOXrN.js → mode-a9XOBfse.js} +1 -1
  89. package/dist/assets/{name-cell-input-to4l2uJD.js → name-cell-input-BhJdGpGA.js} +1 -1
  90. package/dist/assets/{outline-panel-DKBT99sm.js → outline-panel-BGroTTXd.js} +1 -1
  91. package/dist/assets/{packages-panel-SNt0KIcg.js → packages-panel-CQGOcCdz.js} +1 -1
  92. package/dist/assets/{panels-CkoN1Cq9.js → panels-B3g436fI.js} +1 -1
  93. package/dist/assets/{process-output-CdFLcVbk.js → process-output-C4GYMI00.js} +1 -1
  94. package/dist/assets/{readonly-python-code-LF0diK_w.js → readonly-python-code-BvJmyMxd.js} +1 -1
  95. package/dist/assets/{run-page-B7sRqeMN.js → run-page-BGmNoipL.js} +1 -1
  96. package/dist/assets/{scratchpad-panel-SPOKVrq6.js → scratchpad-panel-BH-ZA_0Y.js} +1 -1
  97. package/dist/assets/{session-panel-DotS45sN.js → session-panel-Co_o0ALo.js} +1 -1
  98. package/dist/assets/{snippets-panel-tTRfQnC-.js → snippets-panel-Dg7V8q_w.js} +1 -1
  99. package/dist/assets/{state-zfxByMml.js → state-C-B637hX.js} +1 -1
  100. package/dist/assets/{switch-CT0cCQMS.js → switch-B-UXYPJj.js} +1 -1
  101. package/dist/assets/{textarea-e5AWinyy.js → textarea-gBSp2Bx0.js} +1 -1
  102. package/dist/assets/{tracing-BviqDXIW.js → tracing-Dy8UdLvI.js} +1 -1
  103. package/dist/assets/{tracing-panel-c7K0vhZ-.js → tracing-panel-BKDVrccB.js} +2 -2
  104. package/dist/assets/{types-BtZ02GBj.js → types-Cggdh96K.js} +1 -1
  105. package/dist/assets/{useAddCell-CGoO3J0l.js → useAddCell-CkxiWxI4.js} +1 -1
  106. package/dist/assets/{useCellActionButton-fszU9atc.js → useCellActionButton-BCYKogBW.js} +1 -1
  107. package/dist/assets/{useDeleteCell-Du1ML1VC.js → useDeleteCell-CU4wVnMY.js} +1 -1
  108. package/dist/assets/{useDependencyPanelTab-CU_8jikX.js → useDependencyPanelTab-Dc4i3G5R.js} +1 -1
  109. package/dist/assets/{useNotebookActions-DTWJT8eH.js → useNotebookActions-CeCCuvJT.js} +1 -1
  110. package/dist/assets/{useRunCells-CZG2blin.js → useRunCells-DnyQs7_N.js} +1 -1
  111. package/dist/assets/{useSplitCell-_kUqEKtd.js → useSplitCell-CSr3as14.js} +1 -1
  112. package/dist/assets/utilities.esm-CW5Mhtfu.js +3 -0
  113. package/dist/index.html +41 -41
  114. package/package.json +11 -11
  115. package/src/components/data-table/TableActions.tsx +8 -1
  116. package/src/components/data-table/data-table.tsx +2 -0
  117. package/src/components/data-table/download-actions.tsx +6 -1
  118. package/src/components/dependency-graph/dependency-graph-tree.tsx +10 -1
  119. package/src/components/dependency-graph/dependency-graph.tsx +1 -0
  120. package/src/components/dependency-graph/elements.ts +20 -9
  121. package/src/components/dependency-graph/panels.tsx +27 -11
  122. package/src/components/dependency-graph/types.ts +1 -0
  123. package/src/components/editor/chrome/wrapper/app-chrome.tsx +3 -0
  124. package/src/components/editor/package-alert.tsx +4 -4
  125. package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +3 -5
  126. package/src/core/codemirror/misc/__tests__/paste.test.ts +18 -0
  127. package/src/core/codemirror/misc/paste.ts +14 -10
  128. package/src/core/kernel/messages.ts +1 -0
  129. package/src/core/static/static-state.ts +5 -0
  130. package/src/core/static/types.ts +2 -0
  131. package/src/core/wasm/__tests__/store.test.ts +33 -0
  132. package/src/core/wasm/bridge.ts +2 -1
  133. package/src/core/wasm/store.ts +13 -1
  134. package/src/mount.tsx +23 -1
  135. package/src/plugins/impl/DataTablePlugin.tsx +4 -0
  136. package/src/plugins/impl/anywidget/AnyWidgetPlugin.tsx +7 -5
  137. package/src/plugins/impl/anywidget/__tests__/model.test.ts +53 -0
  138. package/src/plugins/impl/anywidget/model.ts +13 -10
  139. package/src/plugins/impl/chat/ChatPlugin.tsx +2 -0
  140. package/src/plugins/impl/chat/chat-ui.tsx +10 -1
  141. package/src/plugins/impl/data-frames/DataFramePlugin.tsx +4 -0
  142. package/src/plugins/impl/plotly/Plot.tsx +2 -0
  143. package/src/plugins/impl/plotly/PlotlyPlugin.tsx +36 -0
  144. package/src/theme/ThemeProvider.tsx +2 -0
  145. package/dist/assets/JsonOutput-CWXeFX63.js +0 -46
  146. package/dist/assets/Plot-CN9AM0tY.js +0 -4030
  147. package/dist/assets/dependency-graph-panel-BDtbA3R5.js +0 -4
  148. package/dist/assets/dist-4mAhUzty.js +0 -1
  149. package/dist/assets/dist-5CXgzdUa.js +0 -1
  150. package/dist/assets/dist-B27MCO52.js +0 -1
  151. package/dist/assets/dist-Bc7uxGRW.js +0 -1
  152. package/dist/assets/dist-BtJZmWkg.js +0 -1
  153. package/dist/assets/dist-ByjGU_ag.js +0 -1
  154. package/dist/assets/dist-C-V6lvxH.js +0 -1
  155. package/dist/assets/dist-C6SivM7z.js +0 -1
  156. package/dist/assets/dist-C9k2RMmO.js +0 -1
  157. package/dist/assets/dist-ChS0Dc_R.js +0 -1
  158. package/dist/assets/dist-CtsanegT.js +0 -2
  159. package/dist/assets/dist-Cx8mOJOB.js +0 -1
  160. package/dist/assets/dist-DBwNzi3C.js +0 -13
  161. package/dist/assets/dist-DOZ8nmkC.js +0 -1
  162. package/dist/assets/dist-DUlOLsKi.js +0 -1
  163. package/dist/assets/dist-OlCHPNfN.js +0 -1
  164. package/dist/assets/dist-Z4EybR_c.js +0 -1
  165. package/dist/assets/dist-fO1a06Tp.js +0 -1
  166. package/dist/assets/dist-iXB2pOUD.js +0 -1
  167. package/dist/assets/dist-lTwzYaMX.js +0 -1
  168. package/dist/assets/dist-wS1s8MYb.js +0 -1
  169. package/dist/assets/index-jlWJTe49.js +0 -38
  170. package/dist/assets/utilities.esm-JjvzgPvo.js +0 -3
@@ -31,6 +31,12 @@ vi.mock("@/core/network/requests", () => ({
31
31
  }),
32
32
  }));
33
33
 
34
+ // Mock isStaticNotebook — default to false (normal mode)
35
+ const mockIsStatic = vi.fn().mockReturnValue(false);
36
+ vi.mock("@/core/static/static-state", () => ({
37
+ isStaticNotebook: () => mockIsStatic(),
38
+ }));
39
+
34
40
  // Helper to create a mock MarimoComm
35
41
  function createMockComm<T>() {
36
42
  return {
@@ -393,4 +399,51 @@ describe("ModelManager", () => {
393
399
 
394
400
  expect(BINDING_MANAGER.has(testId)).toBe(false);
395
401
  });
402
+
403
+ describe("static mode", () => {
404
+ beforeEach(() => {
405
+ mockIsStatic.mockReturnValue(true);
406
+ });
407
+
408
+ afterAll(() => {
409
+ mockIsStatic.mockReturnValue(false);
410
+ });
411
+
412
+ it("should create model with no-op comm in static mode", async () => {
413
+ await handleWidgetMessage(modelManager, {
414
+ model_id: testId,
415
+ message: {
416
+ method: "open",
417
+ state: { count: 42 },
418
+ buffer_paths: [],
419
+ buffers: [],
420
+ },
421
+ });
422
+
423
+ const model = await modelManager.get(testId);
424
+ expect(model.get("count")).toBe(42);
425
+
426
+ // save_changes should not call the real request client
427
+ model.set("count", 100);
428
+ model.save_changes();
429
+ expect(mockSendModelValue).not.toHaveBeenCalled();
430
+ });
431
+
432
+ it("should not throw on send in static mode", async () => {
433
+ await handleWidgetMessage(modelManager, {
434
+ model_id: testId,
435
+ message: {
436
+ method: "open",
437
+ state: { count: 0 },
438
+ buffer_paths: [],
439
+ buffers: [],
440
+ },
441
+ });
442
+
443
+ const model = await modelManager.get(testId);
444
+ // send() should silently no-op
445
+ await expect(model.send({ test: true })).resolves.toBeUndefined();
446
+ expect(mockSendModelValue).not.toHaveBeenCalled();
447
+ });
448
+ });
396
449
  });
@@ -5,6 +5,7 @@ import type { AnyModel } from "@anywidget/types";
5
5
  import { debounce } from "lodash-es";
6
6
  import type { NotificationMessageData } from "@/core/kernel/messages";
7
7
  import { getRequestClient } from "@/core/network/requests";
8
+ import { isStaticNotebook } from "@/core/static/static-state";
8
9
  import {
9
10
  decodeFromWire,
10
11
  serializeBuffersToBase64,
@@ -343,12 +344,14 @@ export async function handleWidgetMessage(
343
344
  return;
344
345
  }
345
346
 
346
- modelManager.create(
347
- modelId,
348
- (signal) =>
349
- new Model(
350
- stateWithBuffers,
351
- {
347
+ modelManager.create(modelId, (signal) => {
348
+ // In static exports there is no kernel, so comm calls are no-ops.
349
+ const comm: MarimoComm<ModelState> = isStaticNotebook()
350
+ ? {
351
+ sendUpdate: async () => undefined,
352
+ sendCustomMessage: async () => undefined,
353
+ }
354
+ : {
352
355
  async sendUpdate(changeData) {
353
356
  if (signal.aborted) {
354
357
  Logger.debug(
@@ -377,10 +380,10 @@ export async function handleWidgetMessage(
377
380
  buffers: buffers.map(dataViewToBase64),
378
381
  });
379
382
  },
380
- },
381
- signal,
382
- ),
383
- );
383
+ };
384
+
385
+ return new Model(stateWithBuffers, comm, signal);
386
+ });
384
387
  return;
385
388
  }
386
389
 
@@ -47,6 +47,7 @@ export const ChatPlugin = createPlugin<{ messages: UIMessage[] }>(
47
47
  maxHeight: z.number().optional(),
48
48
  config: configSchema,
49
49
  allowAttachments: z.union([z.boolean(), z.string().array()]),
50
+ disabled: z.boolean().default(false),
50
51
  }),
51
52
  )
52
53
  .withFunctions<PluginFunctions>({
@@ -76,6 +77,7 @@ export const ChatPlugin = createPlugin<{ messages: UIMessage[] }>(
76
77
  showConfigurationControls={props.data.showConfigurationControls}
77
78
  maxHeight={props.data.maxHeight}
78
79
  allowAttachments={props.data.allowAttachments}
80
+ disabled={props.data.disabled}
79
81
  config={props.data.config}
80
82
  get_chat_history={props.functions.get_chat_history}
81
83
  delete_chat_history={props.functions.delete_chat_history}
@@ -67,6 +67,7 @@ interface Props extends PluginFunctions {
67
67
  showConfigurationControls: boolean;
68
68
  maxHeight: number | undefined;
69
69
  allowAttachments: boolean | string[];
70
+ disabled: boolean;
70
71
  value: UIMessage[];
71
72
  setValue: (messages: UIMessage[]) => void;
72
73
  host: HTMLElement;
@@ -450,6 +451,9 @@ export const Chatbot: React.FC<Props> = (props) => {
450
451
  <form
451
452
  onSubmit={async (evt) => {
452
453
  evt.preventDefault();
454
+ if (props.disabled) {
455
+ return;
456
+ }
453
457
 
454
458
  const fileParts = files
455
459
  ? await convertToFileUIPart(files)
@@ -462,7 +466,12 @@ export const Chatbot: React.FC<Props> = (props) => {
462
466
  resetInput();
463
467
  }}
464
468
  ref={formRef}
465
- className="flex w-full border-t border-(--slate-6) px-2 py-1 items-center"
469
+ // biome-ignore lint/a11y/useSemanticElements: inert is used to disable the entire form
470
+ inert={props.disabled || undefined}
471
+ className={cn(
472
+ "flex w-full border-t border-(--slate-6) px-2 py-1 items-center",
473
+ props.disabled && "opacity-50 cursor-not-allowed",
474
+ )}
466
475
  >
467
476
  {props.showConfigurationControls && (
468
477
  <ConfigPopup config={config} onChange={setConfig} />
@@ -48,6 +48,7 @@ type TableData<T> = T[] | CsvURL;
48
48
  interface Data {
49
49
  label?: string | null;
50
50
  columns: ColumnDataTypes;
51
+ dataframeName?: string;
51
52
  pageSize: number;
52
53
  showDownload: boolean;
53
54
  lazy: boolean;
@@ -93,6 +94,7 @@ export const DataFramePlugin = createPlugin<S>("marimo-dataframe")
93
94
  label: z.string().nullish(),
94
95
  pageSize: z.number().default(5),
95
96
  showDownload: z.boolean().default(true),
97
+ dataframeName: z.string().optional(),
96
98
  columns: z
97
99
  .array(z.tuple([z.string().or(z.number()), z.string(), z.string()]))
98
100
  .transform((value) => {
@@ -176,6 +178,7 @@ const EMPTY: Transformations = {
176
178
  export const DataFrameComponent = memo(
177
179
  ({
178
180
  columns,
181
+ dataframeName,
179
182
  pageSize,
180
183
  showDownload,
181
184
  lazy,
@@ -326,6 +329,7 @@ export const DataFrameComponent = memo(
326
329
  fieldTypes={field_types}
327
330
  rowHeaders={row_headers || Arrays.EMPTY}
328
331
  showDownload={showDownload}
332
+ downloadFileName={dataframeName}
329
333
  download_as={download_as}
330
334
  enableSearch={false}
331
335
  showFilters={false}
@@ -35,6 +35,7 @@ export interface PlotProps {
35
35
  onRelayouting?: (event: PlotlyTypes.PlotRelayoutEvent) => void;
36
36
  onSelected?: (event: PlotlyTypes.PlotSelectionEvent) => void;
37
37
  onDeselect?: () => void;
38
+ onClick?: (event: PlotlyTypes.PlotMouseEvent) => void;
38
39
  onSunburstClick?: (event: PlotlyTypes.PlotMouseEvent) => void;
39
40
  onTreemapClick?: (event: PlotlyTypes.PlotMouseEvent) => void;
40
41
  onError?: (err: Error) => void;
@@ -48,6 +49,7 @@ const EVENT_NAMES = [
48
49
  "Relayouting",
49
50
  "Selected",
50
51
  "Deselect",
52
+ "Click",
51
53
  "SunburstClick",
52
54
  "TreemapClick",
53
55
  ] as const;
@@ -193,6 +193,24 @@ export const PlotlyComponent = memo(
193
193
  }));
194
194
  })}
195
195
  config={plotlyConfig}
196
+ onClick={useEvent((evt: Readonly<Plotly.PlotMouseEvent>) => {
197
+ if (!evt) {
198
+ return;
199
+ }
200
+ // Only handle clicks for chart types where box/lasso selection
201
+ // (onSelected) doesn't work, such as heatmaps.
202
+ const isHeatmap = evt.points.some(
203
+ (point) => point.data?.type === "heatmap",
204
+ );
205
+ if (!isHeatmap) {
206
+ return;
207
+ }
208
+ setValue((prev) => ({
209
+ ...prev,
210
+ points: extractPoints(evt.points),
211
+ indices: evt.points.map((point) => point.pointIndex),
212
+ }));
213
+ })}
196
214
  onSelected={useEvent((evt: Readonly<Plotly.PlotSelectionEvent>) => {
197
215
  if (!evt) {
198
216
  return;
@@ -224,6 +242,17 @@ PlotlyComponent.displayName = "PlotlyComponent";
224
242
  * instead of the ones that Plotly uses internally,
225
243
  * by using the hovertemplate.
226
244
  */
245
+ const STANDARD_POINT_KEYS: string[] = [
246
+ "x",
247
+ "y",
248
+ "z",
249
+ "lat",
250
+ "lon",
251
+ "curveNumber",
252
+ "pointNumber",
253
+ "pointIndex",
254
+ ];
255
+
227
256
  function extractPoints(
228
257
  points: Plotly.PlotDatum[],
229
258
  ): Record<AxisName, AxisDatum>[] {
@@ -238,6 +267,13 @@ function extractPoints(
238
267
  const hovertemplate = Array.isArray(point.data.hovertemplate)
239
268
  ? point.data.hovertemplate[0]
240
269
  : point.data.hovertemplate;
270
+
271
+ // For chart types with standard point keys (e.g. heatmaps),
272
+ // or when there's no hovertemplate, pick keys directly from the point.
273
+ if (!hovertemplate || point.data?.type === "heatmap") {
274
+ return pick(point, STANDARD_POINT_KEYS);
275
+ }
276
+
241
277
  // Update or create a parser
242
278
  parser = parser
243
279
  ? parser.update(hovertemplate)
@@ -10,8 +10,10 @@ export const ThemeProvider: React.FC<PropsWithChildren> = memo(
10
10
  const { theme } = useTheme();
11
11
  useLayoutEffect(() => {
12
12
  document.body.classList.add(theme, `${theme}-theme`);
13
+ document.body.dataset.theme = theme;
13
14
  return () => {
14
15
  document.body.classList.remove(theme, `${theme}-theme`);
16
+ delete document.body.dataset.theme;
15
17
  };
16
18
  }, [theme]);
17
19