@marimo-team/islands 0.25.3-dev6 → 0.25.3-dev60
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/{ConnectedDataExplorerComponent-CNMliVzw.js → ConnectedDataExplorerComponent-Da5OoEUL.js} +14 -14
- package/dist/{ErrorBoundary-Ca-Sa492.js → ErrorBoundary-BmHVnL-A.js} +8 -8
- package/dist/{abnfDiagram-VCTEODGH-D-Ez91gV.js → abnfDiagram-VCTEODGH-Bz3mrprX.js} +2 -2
- package/dist/any-language-editor-k2B8YI3Y.js +75 -0
- package/dist/{architectureDiagram-5GKGNRK7-CTpcNtO8.js → architectureDiagram-5GKGNRK7-Cs5ujb9K.js} +1 -1
- package/dist/{arrays-CN1djC4k.js → arrays-R6JsImiT.js} +8 -0
- package/dist/{button-CvjkwdSk.js → button-BxB3RDjN.js} +1 -1
- package/dist/{capabilities-CQ5IjhzL.js → capabilities-BgR0osfN.js} +2 -2
- package/dist/{chat-ui-0lKdOoQ3.js → chat-ui-C0_lCzql.js} +1139 -1140
- package/dist/{check-EDwSwP89.js → check-P35dc2dH.js} +1 -1
- package/dist/{chunk-IMKFNOWR-DGxi-zeo.js → chunk-IMKFNOWR-CcTSaxZt.js} +1 -1
- package/dist/{chunk-SHT3W25Y-CV9J3rAB.js → chunk-SHT3W25Y-DcJcjOaO.js} +1 -1
- package/dist/{chunk-SVP7TREG-BL-9Fqgg.js → chunk-SVP7TREG-BVtDTjaF.js} +1 -1
- package/dist/{chunk-TICWLB2K-Cu_XMjDM.js → chunk-TICWLB2K-BkDFkh8q.js} +1 -1
- package/dist/{chunk-YOKDWASO-DPZk8vhS.js → chunk-YOKDWASO-DKplTrBY.js} +20 -20
- package/dist/{classDiagram-ZZMXUADV-BXXV13Hq.js → classDiagram-ZZMXUADV-CfAt7HT5.js} +2 -2
- package/dist/{classDiagram-v2-VYDZK3BY-B7FXJ1Cp.js → classDiagram-v2-VYDZK3BY-CGEjNYa2.js} +2 -2
- package/dist/{code-visibility-NGRjhbtA.js → code-visibility-BWERlt58.js} +6 -6
- package/dist/{common-B7_ZSg0P.js → common-E2zdIyjs.js} +4960 -4835
- package/dist/{copy-icon-Ft7t9bqz.js → copy-icon-CPoxUG7G.js} +3 -3
- package/dist/{cynefinDiagram-5FMLGOSQ-CH96rwba.js → cynefinDiagram-5FMLGOSQ-BW6NmfJi.js} +1 -1
- package/dist/{dagre-GXQ25YYZ-BD3PmpFY.js → dagre-GXQ25YYZ-CWg3CTuF.js} +1 -1
- package/dist/{data-grid-overlay-editor-B_cmrkBm.js → data-grid-overlay-editor-CHUwPg7P.js} +5 -5
- package/dist/{data-grid-types-BH_r7g7l.js → data-grid-types-0PJ-9N5n.js} +1 -1
- package/dist/{diagram-S7CK7UJ4-BsSJJldB.js → diagram-S7CK7UJ4-BPxtoS-z.js} +1 -1
- package/dist/{diagram-UQ7AKVKN-muQtNZc4.js → diagram-UQ7AKVKN-DA-vJdFL.js} +1 -1
- package/dist/{diagram-VX7I27RA-CdhuZAqR.js → diagram-VX7I27RA-D6bkR84S.js} +1 -1
- package/dist/{diagram-Z3DM3KII-BD8mXjOj.js → diagram-Z3DM3KII-m60g4cAT.js} +1 -1
- package/dist/{dialog-CEBFZNyc.js → dialog-C1PcpCqX.js} +3 -3
- package/dist/{dist-DpBpA_n1.js → dist-Lm-D-Vr6.js} +3 -3
- package/dist/{dropdown-menu-CP_qCYHC.js → dropdown-menu-B3eUiVJB.js} +177 -169
- package/dist/{ebnfDiagram-PWID7BFC-BIN1sweE.js → ebnfDiagram-PWID7BFC-CK99sho2.js} +2 -2
- package/dist/{erDiagram-RLTQ6QDP-Ctp5cfoN.js → erDiagram-RLTQ6QDP-Bryq0tdE.js} +1 -1
- package/dist/{error-banner-D-WCIiMt.js → error-banner-Sl2FlSJ4.js} +2 -2
- package/dist/{error-entries-DSvFWT_M.js → error-entries-CMNtA8NK.js} +959 -959
- package/dist/{feedback-button-CTF2Nj9s.js → feedback-button-DwOUm3jt.js} +29 -29
- package/dist/{flowDiagram-HODETNUW-3EfLrlFb.js → flowDiagram-HODETNUW-D-3aRpfS.js} +2 -2
- package/dist/{formats-CeOmU1MU.js → formats-6pLWWrY2.js} +3 -3
- package/dist/{ganttDiagram-EL5Y4UJY-BQT16JSc.js → ganttDiagram-EL5Y4UJY-Dh_4cBJZ.js} +2 -2
- package/dist/{glide-data-editor-BQHF9puC.js → glide-data-editor-C9WGlA66.js} +370 -368
- package/dist/{glide-utils-DJyqh0EM.js → glide-utils-Bt8_pBa2.js} +16 -16
- package/dist/{globals-Cj8iqMEp.js → globals-BwFL--QV.js} +3 -3
- package/dist/{highlighted-body-KPVGNVTW-zwjq21tf.js → highlighted-body-KPVGNVTW-3Ziwfz-V.js} +2 -2
- package/dist/{html-to-image-B8s5SgGm.js → html-to-image-6xcoTKhC.js} +29 -29
- package/dist/{infoDiagram-27XIBGKW-DipQoHxl.js → infoDiagram-27XIBGKW-s1C1kgfU.js} +1 -1
- package/dist/{ishikawaDiagram-5VMMS53U-DgFMjSGz.js → ishikawaDiagram-5VMMS53U-CCsRgolY.js} +1 -1
- package/dist/{kanban-definition-UXKFOSKX-w1ACVjgl.js → kanban-definition-UXKFOSKX-BrmxDlcc.js} +1 -1
- package/dist/{label-BDKF3Afc.js → label-BRnUC0NX.js} +2 -2
- package/dist/{linear-Cnaq3Tfb.js → linear-DWERh7eI.js} +121 -73
- package/dist/{loader-BQOfjTIJ.js → loader-Bm8cy3e8.js} +12 -12
- package/dist/main.js +3177 -2980
- package/dist/{mermaid-CHo49Yv-.js → mermaid-DKI0869I.js} +38 -38
- package/dist/mermaid-HWGCJPDP-Du3-HPQA.js +2 -0
- package/dist/{mindmap-definition-YA3MSWOX-CrT0KSfX.js → mindmap-definition-YA3MSWOX-641xJysF.js} +1 -1
- package/dist/{number-overlay-editor-DBcnm0SY.js → number-overlay-editor-Ci9h4-h1.js} +3 -3
- package/dist/{objects-CzUgoMIF.js → objects-DCmMvZtn.js} +1 -3
- package/dist/{once-BtE-wKRL.js → once-eG8Vmvjd.js} +1 -1
- package/dist/{pegDiagram-XKGWAZYB-BqvywNJA.js → pegDiagram-XKGWAZYB-keVUAUeS.js} +2 -2
- package/dist/{pieDiagram-E7YTZNPT-BfxJ_XCc.js → pieDiagram-E7YTZNPT-DHktXPO_.js} +1 -1
- package/dist/precisionRound-DhAzeRcm.js +49 -0
- package/dist/{process-output-CKRXyszb.js → process-output-BJRJFobf.js} +4 -4
- package/dist/{quadrantDiagram-AXDQQJYC-Ccw0xMjN.js → quadrantDiagram-AXDQQJYC-Blrm7V7_.js} +1 -1
- package/dist/{railroadDiagram-O6MQD6OU-8vFLVTPu.js → railroadDiagram-O6MQD6OU-BVEF4OV_.js} +2 -2
- package/dist/{react-vega-BED0DjiB.js → react-vega-C4T9Rb_w.js} +12284 -12270
- package/dist/{requirementDiagram-BXWQKSXE-a_MxGvxe.js → requirementDiagram-BXWQKSXE-CWo-UAKV.js} +1 -1
- package/dist/{reveal-component-DTBSuGkC.js → reveal-component-Ay-npdvI.js} +19 -19
- package/dist/{select-BButFi-d.js → select-B0LRDH0g.js} +4 -4
- package/dist/{sequenceDiagram-WJ2MYXX4-CXFMnxGh.js → sequenceDiagram-WJ2MYXX4-CHQvLnq4.js} +1 -1
- package/dist/{spec-BwarBpUJ.js → spec-C-LRKGtT.js} +4 -4
- package/dist/{stateDiagram-D77RDMKH-DQeA_CwI.js → stateDiagram-D77RDMKH-BOJZgbUJ.js} +2 -2
- package/dist/{stateDiagram-v2-MP3YSRHH-BxxQucm1.js → stateDiagram-v2-MP3YSRHH-C53XXdYH.js} +2 -2
- package/dist/style.css +1 -1
- package/dist/{swimlanes-42K2YHIH-Do6L-cYg.js → swimlanes-42K2YHIH-D2o1gOtw.js} +1 -1
- package/dist/{swimlanesDiagram-VR7AAH4N-DUzPvSVU.js → swimlanesDiagram-VR7AAH4N-BHFzN1Wk.js} +2 -2
- package/dist/{swiper-component-DKntyn8p.js → swiper-component-DfhRvAFi.js} +2 -2
- package/dist/{time-BieKwdYS.js → time-BaMbJs4z.js} +18 -18
- package/dist/{timeline-definition-24CTP7MA-D3c46WM5.js → timeline-definition-24CTP7MA-rv7QUa46.js} +1 -1
- package/dist/{toDate-vIXljnsr.js → toDate-CjPuEPou.js} +21 -20
- package/dist/{toast-B9UMEi8u.js → toast-CFWpihJU.js} +4 -4
- package/dist/{tooltip-BO1REbqs.js → tooltip-CKO1pHJo.js} +2 -2
- package/dist/{use-restore-focus-BEM7sFWl.js → use-restore-focus-DBCITLeF.js} +1 -1
- package/dist/{useAsyncData-Deh5kszT.js → useAsyncData-ChgNtdmE.js} +15 -14
- package/dist/{useDateFormatter-D8XruNGG.js → useDateFormatter-c_yJtkAe.js} +2 -2
- package/dist/{useDeepCompareMemoize-DXwzRINI.js → useDeepCompareMemoize-Bet7kSsI.js} +1 -1
- package/dist/{useIframeCapabilities-DBETvvPl.js → useIframeCapabilities-BkdeVdXC.js} +1 -1
- package/dist/{useLifecycle-j7D9uD1o.js → useLifecycle-VVmz_24o.js} +3 -3
- package/dist/{useTheme-CRnjkku0.js → useTheme-DympCsQg.js} +5 -5
- package/dist/{vega-component-DTG6zxaY.js → vega-component-DnfQeoyt.js} +147 -147
- package/dist/{vega-embed-container-BNHE18G9.js → vega-embed-container--LYZqguO.js} +2 -2
- package/dist/{vega-loader.browser-DNST6uVf.js → vega-loader.browser-B9Pz0EGP.js} +298 -507
- package/dist/{vennDiagram-4TSXK5OY-BSZD8JcZ.js → vennDiagram-4TSXK5OY-D36Zk7t-.js} +1 -1
- package/dist/{wardleyDiagram-VM6X3IG4-48cUWkfA.js → wardleyDiagram-VM6X3IG4-BLBolvyQ.js} +1 -1
- package/dist/{xychartDiagram-S5SC5T6Z-CswYgYeu.js → xychartDiagram-S5SC5T6Z-CfH57l0v.js} +2 -2
- package/package.json +4 -4
- package/src/__tests__/host-imports.test.ts +1 -0
- package/src/components/app-config/ai-config.tsx +6 -2
- package/src/components/chat/__tests__/message-queue.test.tsx +17 -0
- package/src/components/chat/chat-panel.tsx +3 -7
- package/src/components/chat/chat-utils.ts +4 -8
- package/src/components/data-table/__tests__/context-menu-interaction.test.tsx +130 -0
- package/src/components/data-table/__tests__/date-popover.test.tsx +72 -0
- package/src/components/data-table/__tests__/export-actions.test.tsx +434 -6
- package/src/components/data-table/charts/charts.tsx +14 -19
- package/src/components/data-table/column-visibility-dropdown.tsx +8 -13
- package/src/components/data-table/columns.tsx +2 -1
- package/src/components/data-table/context-menu.tsx +36 -20
- package/src/components/data-table/date-popover.tsx +6 -4
- package/src/components/data-table/export-actions.tsx +267 -163
- package/src/components/data-table/row-viewer-panel/row-viewer.tsx +2 -2
- package/src/components/data-table/schemas.ts +14 -36
- package/src/components/editor/__tests__/dynamic-favicon.test.tsx +111 -18
- package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +24 -0
- package/src/components/editor/actions/pair-with-agent-modal.tsx +18 -15
- package/src/components/editor/ai/add-cell-with-ai.tsx +3 -3
- package/src/components/editor/cell/CreateCellButton.tsx +8 -10
- package/src/components/editor/cell/__tests__/useShouldShowInterrupt.test.ts +39 -0
- package/src/components/editor/cell/code/__tests__/language-toggle.test.tsx +54 -0
- package/src/components/editor/cell/code/cell-editor.tsx +1 -1
- package/src/components/editor/cell/code/language-toggle.tsx +8 -7
- package/src/components/editor/cell/useShouldShowInterrupt.ts +2 -12
- package/src/components/editor/chrome/panels/__tests__/packages-panel.test.tsx +30 -1
- package/src/components/editor/chrome/panels/packages-panel.tsx +4 -7
- package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +12 -18
- package/src/components/editor/connections/components.tsx +1 -1
- package/src/components/editor/controls/__tests__/command-palette.test.tsx +53 -0
- package/src/components/editor/controls/command-palette.tsx +22 -10
- package/src/components/editor/dynamic-favicon.tsx +21 -40
- package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +12 -23
- package/src/components/editor/file-tree/__tests__/file-explorer-interactions.test.tsx +6 -0
- package/src/components/editor/file-tree/__tests__/use-file-search.test.tsx +91 -0
- package/src/components/editor/file-tree/file-explorer.tsx +10 -5
- package/src/components/editor/file-tree/file-viewer.tsx +4 -2
- package/src/components/editor/file-tree/use-file-search.ts +35 -21
- package/src/components/editor/output/MarimoTracebackOutput.tsx +1 -1
- package/src/components/editor/output/console/ConsoleOutput.tsx +8 -18
- package/src/components/icons/__tests__/large-spinner.test.tsx +39 -0
- package/src/components/icons/large-spinner.tsx +3 -14
- package/src/components/pages/home-page.tsx +4 -4
- package/src/components/slides/slide-cell-view.tsx +1 -1
- package/src/components/tracing/tracing.tsx +2 -2
- package/src/core/attachments/state.ts +5 -8
- package/src/core/cells/__tests__/notebook-outline.test.ts +113 -0
- package/src/core/cells/cells.ts +2 -1
- package/src/core/cells/pending-delete-service.ts +2 -6
- package/src/core/datasets/__tests__/data-source-discovery.test.ts +4 -4
- package/src/core/datasets/data-source-discovery.ts +4 -6
- package/src/core/edit-app.tsx +2 -2
- package/src/core/host/AGENTS.md +11 -2
- package/src/core/host/__tests__/attachments.test.ts +56 -0
- package/src/core/host/__tests__/document.test.ts +126 -0
- package/src/core/host/__tests__/events.test.ts +49 -0
- package/src/core/host/__tests__/fixtures.ts +41 -0
- package/src/core/host/__tests__/notebook-client.test.ts +496 -0
- package/src/core/host/connection.ts +81 -0
- package/src/core/host/events.ts +68 -0
- package/src/core/host/notebook-client.ts +316 -0
- package/src/core/host/resources/attachments.ts +19 -0
- package/src/core/host/resources/document.ts +101 -0
- package/src/core/hotkeys/actions.ts +2 -4
- package/src/core/islands/__tests__/bootstrap.test.ts +0 -26
- package/src/core/islands/bootstrap.ts +0 -1
- package/src/core/network/connection.ts +4 -2
- package/src/core/network/useConnectionNotice.ts +8 -2
- package/src/core/run-app.tsx +2 -2
- package/src/core/runtime/__tests__/runtime.test.ts +5 -12
- package/src/core/runtime/runtime.ts +5 -5
- package/src/core/static/files.ts +2 -0
- package/src/core/websocket/__tests__/{useMarimoKernelConnection.test.ts → connection-state.test.ts} +15 -17
- package/src/core/websocket/__tests__/{useWebSocket.test.ts → createConnectionTransport.test.ts} +1 -1
- package/src/core/websocket/__tests__/useNotebookClient.hook.test.tsx +149 -0
- package/src/core/websocket/__tests__/{useMarimoKernelConnection.hook.test.tsx → useNotebookSession.hook.test.tsx} +168 -253
- package/src/core/websocket/connection-state.ts +70 -0
- package/src/core/websocket/state.ts +7 -0
- package/src/core/websocket/transports/__tests__/sse.test.ts +18 -4
- package/src/core/websocket/transports/create.ts +31 -0
- package/src/core/websocket/transports/sse.ts +14 -2
- package/src/core/websocket/transports/transport.ts +12 -23
- package/src/core/websocket/types.ts +9 -39
- package/src/core/websocket/useNotebookClient.ts +78 -0
- package/src/core/websocket/{useMarimoKernelConnection.tsx → useNotebookSession.tsx} +53 -233
- package/src/hooks/__tests__/useAsyncData.test.tsx +148 -0
- package/src/hooks/__tests__/useDebounce.test.tsx +51 -0
- package/src/hooks/__tests__/useDeepCompareMemoize.test.tsx +39 -0
- package/src/hooks/__tests__/useDelayElapsed.test.tsx +28 -0
- package/src/hooks/__tests__/useInternalStateWithSync.test.tsx +20 -0
- package/src/hooks/__tests__/useScript.test.tsx +77 -0
- package/src/hooks/__tests__/useStateForKey.test.tsx +257 -0
- package/src/hooks/useAsyncData.ts +3 -3
- package/src/hooks/useDataSourceDiscovery.ts +6 -4
- package/src/hooks/useDebounce.ts +5 -8
- package/src/hooks/useDeepCompareMemoize.ts +1 -1
- package/src/hooks/useDelayElapsed.ts +14 -10
- package/src/hooks/useElapsedTime.ts +3 -3
- package/src/hooks/useInputHistory.ts +4 -7
- package/src/hooks/useInternalStateWithSync.ts +6 -6
- package/src/hooks/useResizeObserver.ts +4 -7
- package/src/hooks/useScript.ts +67 -70
- package/src/hooks/useStateForKey.ts +93 -0
- package/src/plugins/core/registerReactComponent.tsx +1 -1
- package/src/plugins/impl/DataTablePlugin.tsx +14 -15
- package/src/plugins/impl/DropdownPlugin.tsx +2 -6
- package/src/plugins/impl/FileBrowserPlugin.tsx +4 -16
- package/src/plugins/impl/FormPlugin.tsx +1 -1
- package/src/plugins/impl/SliderPlugin.tsx +6 -8
- package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +201 -1
- package/src/plugins/impl/__tests__/FileBrowserPlugin.test.tsx +45 -0
- package/src/plugins/impl/__tests__/SliderPlugin.test.tsx +24 -3
- package/src/plugins/impl/anywidget/__tests__/registry.test.ts +2 -0
- package/src/plugins/impl/anywidget/__tests__/widget-binding.test.ts +241 -2
- package/src/plugins/impl/anywidget/runtime.ts +21 -9
- package/src/plugins/impl/anywidget/widget-binding.ts +160 -82
- package/src/plugins/impl/chat/__tests__/chat-prompts.test.tsx +93 -0
- package/src/plugins/impl/chat/chat-ui.tsx +10 -9
- package/src/plugins/impl/code/any-language-editor.tsx +2 -2
- package/src/plugins/impl/data-editor/glide-data-editor.tsx +8 -5
- package/src/plugins/impl/data-editor/glide-utils.ts +3 -1
- package/src/plugins/impl/mpl-interactive/MplInteractivePlugin.tsx +5 -3
- package/src/plugins/impl/panel/PanelPlugin.tsx +15 -16
- package/src/plugins/impl/panel/__tests__/utils.test.ts +39 -1
- package/src/plugins/impl/panel/utils.ts +17 -0
- package/src/plugins/impl/vega/make-selectable.ts +3 -6
- package/src/stories/__tests__/editor-story.test.tsx +33 -0
- package/src/stories/editor.stories.tsx +24 -17
- package/src/utils/__tests__/arrays.test.ts +35 -0
- package/src/utils/__tests__/numbers.test.ts +48 -1
- package/src/utils/__tests__/objects.test.ts +21 -0
- package/src/utils/arrays.ts +10 -0
- package/src/utils/numbers.ts +28 -17
- package/src/utils/objects.ts +3 -7
- package/src/utils/once.ts +19 -19
- package/dist/any-language-editor-DZ_YTVus.js +0 -76
- package/dist/mermaid-HWGCJPDP-ByCsZnYF.js +0 -2
- package/dist/precisionRound-DMIVQrt2.js +0 -97
- package/src/core/host/__tests__/protocol.test.ts +0 -214
- package/src/core/host/protocol.ts +0 -1943
- package/src/core/websocket/useWebSocket.tsx +0 -88
package/src/hooks/useDebounce.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { debounce } from "lodash-es";
|
|
3
3
|
import { useEffect, useMemo, useState } from "react";
|
|
4
4
|
import useEvent from "react-use-event-hook";
|
|
5
|
+
import { useInternalStateWithSync } from "./useInternalStateWithSync";
|
|
5
6
|
|
|
6
7
|
export function useDebounce<T>(value: T, delay: number): T {
|
|
7
8
|
const [debouncedValue, setDebouncedValue] = useState<T>(value);
|
|
@@ -41,18 +42,14 @@ export function useDebounceControlledState<T>(opts: {
|
|
|
41
42
|
disabled?: boolean;
|
|
42
43
|
}) {
|
|
43
44
|
const { initialValue, onChange, delay, disabled } = opts;
|
|
44
|
-
const [internalValue, setInternalValue] =
|
|
45
|
+
const [internalValue, setInternalValue] = useInternalStateWithSync(
|
|
46
|
+
initialValue,
|
|
47
|
+
Object.is,
|
|
48
|
+
);
|
|
45
49
|
const debouncedValue = useDebounce(internalValue, delay || 200);
|
|
46
50
|
|
|
47
51
|
const onUpdate = useEvent(onChange);
|
|
48
52
|
|
|
49
|
-
// Handle external changes:
|
|
50
|
-
useEffect(() => {
|
|
51
|
-
// TODO: Replace this synchronization with a controlled-state primitive.
|
|
52
|
-
// oxlint-disable-next-line react/no-deriving-state-in-effects -- Synchronize the debounced draft when the controlled value changes.
|
|
53
|
-
setInternalValue(initialValue);
|
|
54
|
-
}, [initialValue]);
|
|
55
|
-
|
|
56
53
|
// Handle debounced updates for user input
|
|
57
54
|
useEffect(() => {
|
|
58
55
|
if (disabled) {
|
|
@@ -12,7 +12,7 @@ import React from "react";
|
|
|
12
12
|
* Useful when the re-render if more expensive than the deep compare.
|
|
13
13
|
*/
|
|
14
14
|
export function useDeepCompareMemoize<T>(value: T) {
|
|
15
|
-
const ref = React.useRef
|
|
15
|
+
const ref = React.useRef(value);
|
|
16
16
|
|
|
17
17
|
if (!dequal(value, ref.current)) {
|
|
18
18
|
ref.current = value;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Returns `false` until `delayMs` has elapsed since mount (or since `delayMs`
|
|
@@ -8,20 +8,24 @@ import { useLayoutEffect, useState } from "react";
|
|
|
8
8
|
* `delayMs <= 0`. Useful for suppressing spinner/fallback flashes.
|
|
9
9
|
*/
|
|
10
10
|
export function useDelayElapsed(delayMs: number): boolean {
|
|
11
|
-
const [
|
|
11
|
+
const [timer, setTimer] = useState({ delayMs, elapsed: delayMs <= 0 });
|
|
12
12
|
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
// Reset during render so a changed delay never commits a stale `elapsed`
|
|
14
|
+
// (e.g. a gate re-closing shows no fallback flash before paint)
|
|
15
|
+
if (!Object.is(timer.delayMs, delayMs)) {
|
|
16
|
+
setTimer({ delayMs, elapsed: delayMs <= 0 });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
17
20
|
if (delayMs <= 0) {
|
|
18
|
-
setElapsed(true);
|
|
19
21
|
return;
|
|
20
22
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
const timeout = setTimeout(
|
|
24
|
+
() => setTimer({ delayMs, elapsed: true }),
|
|
25
|
+
delayMs,
|
|
26
|
+
);
|
|
23
27
|
return () => clearTimeout(timeout);
|
|
24
28
|
}, [delayMs]);
|
|
25
29
|
|
|
26
|
-
return elapsed;
|
|
30
|
+
return timer.elapsed;
|
|
27
31
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
|
-
import { useEffect,
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
4
|
import { type Milliseconds, Time } from "@/utils/time";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Returns the elapsed time since mount, in milliseconds.
|
|
8
8
|
*/
|
|
9
9
|
export function useElapsedTime(initialStartTimeMs: Milliseconds) {
|
|
10
|
-
const startTime =
|
|
10
|
+
const [startTime] = useState(initialStartTimeMs);
|
|
11
11
|
const [endTime, setEndTime] = useState(initialStartTimeMs);
|
|
12
12
|
|
|
13
13
|
useEffect(() => {
|
|
@@ -21,5 +21,5 @@ export function useElapsedTime(initialStartTimeMs: Milliseconds) {
|
|
|
21
21
|
return () => clearInterval(interval);
|
|
22
22
|
}, []);
|
|
23
23
|
|
|
24
|
-
return endTime - startTime
|
|
24
|
+
return endTime - startTime;
|
|
25
25
|
}
|
|
@@ -37,12 +37,9 @@ export function useInputHistory({
|
|
|
37
37
|
const historyIndexRef = React.useRef<number>(-1);
|
|
38
38
|
// Store the current input when navigating history
|
|
39
39
|
const pendingInputRef = React.useRef<string>("");
|
|
40
|
-
// Keep a ref to history for use in callbacks without causing re-renders
|
|
41
|
-
const historyRef = React.useRef(history);
|
|
42
|
-
historyRef.current = history;
|
|
43
40
|
|
|
44
41
|
const navigateUp = React.useCallback(() => {
|
|
45
|
-
const currentHistory =
|
|
42
|
+
const currentHistory = history;
|
|
46
43
|
if (currentHistory.length === 0) {
|
|
47
44
|
return;
|
|
48
45
|
}
|
|
@@ -59,10 +56,10 @@ export function useInputHistory({
|
|
|
59
56
|
historyIndexRef.current = newIndex;
|
|
60
57
|
setValue(currentHistory[currentHistory.length - 1 - newIndex]);
|
|
61
58
|
}
|
|
62
|
-
}, [value, setValue]);
|
|
59
|
+
}, [history, value, setValue]);
|
|
63
60
|
|
|
64
61
|
const navigateDown = React.useCallback(() => {
|
|
65
|
-
const currentHistory =
|
|
62
|
+
const currentHistory = history;
|
|
66
63
|
if (currentHistory.length === 0) {
|
|
67
64
|
return;
|
|
68
65
|
}
|
|
@@ -79,7 +76,7 @@ export function useInputHistory({
|
|
|
79
76
|
historyIndexRef.current = -1;
|
|
80
77
|
setValue(pendingInputRef.current);
|
|
81
78
|
}
|
|
82
|
-
}, [setValue]);
|
|
79
|
+
}, [history, setValue]);
|
|
83
80
|
|
|
84
81
|
const addToHistory = React.useCallback((command: string) => {
|
|
85
82
|
setHistory((prev) => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
-
import {
|
|
2
|
+
import { useState } from "react";
|
|
3
3
|
|
|
4
4
|
type EqualityFn<T> = (a: T, b: T) => boolean;
|
|
5
5
|
|
|
@@ -13,13 +13,13 @@ export function useInternalStateWithSync<T>(
|
|
|
13
13
|
initial: T,
|
|
14
14
|
equalityFn: EqualityFn<T> = defaultEqualityFn,
|
|
15
15
|
) {
|
|
16
|
-
const [state, setState] = useState(initial);
|
|
17
|
-
const
|
|
16
|
+
const [state, setState] = useState(() => initial);
|
|
17
|
+
const [previousInitial, setPreviousInitial] = useState(() => initial);
|
|
18
18
|
|
|
19
19
|
// Whenever the outside state changes, update the internal state
|
|
20
|
-
if (!equalityFn(
|
|
21
|
-
setState(initial);
|
|
22
|
-
|
|
20
|
+
if (!equalityFn(previousInitial, initial)) {
|
|
21
|
+
setState(() => initial);
|
|
22
|
+
setPreviousInitial(() => initial);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
return [state, setState] as const;
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import type { RefObject } from "react";
|
|
8
8
|
import { useEffect, useRef } from "react";
|
|
9
|
+
import useEvent from "react-use-event-hook";
|
|
9
10
|
|
|
10
11
|
interface Size {
|
|
11
12
|
width: number | undefined;
|
|
@@ -30,9 +31,7 @@ export function useResizeObserver<T extends HTMLElement = HTMLElement>(
|
|
|
30
31
|
const { ref, box = "content-box", skip } = options;
|
|
31
32
|
// oxlint-disable-next-line react/hook-use-state
|
|
32
33
|
const previousSize = useRef<Size>({ ...initialSize });
|
|
33
|
-
const onResize =
|
|
34
|
-
// oxlint-disable-next-line react-hooks/react-compiler
|
|
35
|
-
onResize.current = options.onResize;
|
|
34
|
+
const onResize = useEvent((size: Size) => options.onResize?.(size));
|
|
36
35
|
|
|
37
36
|
useEffect(() => {
|
|
38
37
|
if (!ref.current || skip) {
|
|
@@ -63,9 +62,7 @@ export function useResizeObserver<T extends HTMLElement = HTMLElement>(
|
|
|
63
62
|
previousSize.current.width = newWidth;
|
|
64
63
|
previousSize.current.height = newHeight;
|
|
65
64
|
|
|
66
|
-
|
|
67
|
-
onResize.current(newSize);
|
|
68
|
-
}
|
|
65
|
+
onResize(newSize);
|
|
69
66
|
}
|
|
70
67
|
});
|
|
71
68
|
|
|
@@ -74,7 +71,7 @@ export function useResizeObserver<T extends HTMLElement = HTMLElement>(
|
|
|
74
71
|
return () => {
|
|
75
72
|
observer.disconnect();
|
|
76
73
|
};
|
|
77
|
-
}, [box, ref, skip]);
|
|
74
|
+
}, [box, ref, skip, onResize]);
|
|
78
75
|
}
|
|
79
76
|
|
|
80
77
|
type BoxSizesKey = keyof Pick<
|
package/src/hooks/useScript.ts
CHANGED
|
@@ -1,90 +1,87 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
-
import
|
|
2
|
+
import { useCallback, useSyncExternalStore } from "react";
|
|
3
3
|
|
|
4
4
|
type ScriptStatus = "loading" | "ready" | "error" | "unknown";
|
|
5
5
|
interface ScriptOptions {
|
|
6
6
|
removeOnUnmount?: boolean;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
interface ScriptEntry {
|
|
10
|
+
element: HTMLScriptElement;
|
|
11
|
+
listeners: Set<() => void>;
|
|
12
|
+
/** Whether useScript created the element, and so may remove it. */
|
|
13
|
+
owned: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
9
16
|
const defaultOptions = { removeOnUnmount: false };
|
|
10
17
|
|
|
18
|
+
// One entry per src, so reading the status on render is a map lookup
|
|
19
|
+
const scripts = new Map<string, ScriptEntry>();
|
|
20
|
+
|
|
21
|
+
function findScript(src: string): HTMLScriptElement | undefined {
|
|
22
|
+
// Compare attributes rather than building a selector, which `src` could break
|
|
23
|
+
return [...document.scripts].find(
|
|
24
|
+
(script) => script.getAttribute("src") === src,
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function acquireScript(src: string): ScriptEntry {
|
|
29
|
+
const cached = scripts.get(src);
|
|
30
|
+
if (cached?.element.isConnected) {
|
|
31
|
+
return cached;
|
|
32
|
+
}
|
|
33
|
+
const existing = findScript(src);
|
|
34
|
+
const element = existing ?? document.createElement("script");
|
|
35
|
+
const entry: ScriptEntry = {
|
|
36
|
+
element,
|
|
37
|
+
listeners: new Set(),
|
|
38
|
+
owned: !existing,
|
|
39
|
+
};
|
|
40
|
+
const setStatus = (status: ScriptStatus) => {
|
|
41
|
+
element.dataset.status = status;
|
|
42
|
+
entry.listeners.forEach((listener) => listener());
|
|
43
|
+
};
|
|
44
|
+
// Listen for the script's lifetime rather than per subscriber, so the
|
|
45
|
+
// status still settles if the first subscriber unmounts mid-load.
|
|
46
|
+
element.addEventListener("load", () => setStatus("ready"), { once: true });
|
|
47
|
+
element.addEventListener("error", () => setStatus("error"), { once: true });
|
|
48
|
+
if (!existing) {
|
|
49
|
+
element.src = src;
|
|
50
|
+
element.async = true;
|
|
51
|
+
element.dataset.status = "loading";
|
|
52
|
+
document.body.append(element);
|
|
53
|
+
}
|
|
54
|
+
scripts.set(src, entry);
|
|
55
|
+
return entry;
|
|
56
|
+
}
|
|
57
|
+
|
|
11
58
|
export function useScript(
|
|
12
59
|
src: string,
|
|
13
60
|
options: ScriptOptions = defaultOptions,
|
|
14
61
|
) {
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
);
|
|
19
|
-
return (
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
React.useEffect(() => {
|
|
23
|
-
let script = document.querySelector<HTMLScriptElement>(
|
|
24
|
-
`script[src="${src}"]`,
|
|
25
|
-
);
|
|
26
|
-
|
|
27
|
-
const updateStatus = (newStatus: ScriptStatus) => {
|
|
28
|
-
if (script) {
|
|
29
|
-
script.dataset.status = newStatus;
|
|
30
|
-
setStatus(newStatus);
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
const handleScriptLoad = () => updateStatus("ready");
|
|
35
|
-
const handleScriptError = () => updateStatus("error");
|
|
36
|
-
|
|
37
|
-
// observes DOM changes and updates the status
|
|
38
|
-
const observeScript = (script: HTMLScriptElement) => {
|
|
39
|
-
const observer = new MutationObserver(() => {
|
|
40
|
-
const newStatus = script.dataset.status;
|
|
41
|
-
if (newStatus) {
|
|
42
|
-
setStatus(newStatus as ScriptStatus);
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
observer.observe(script, {
|
|
46
|
-
attributes: true,
|
|
47
|
-
attributeFilter: ["data-status"],
|
|
48
|
-
});
|
|
49
|
-
return observer;
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
if (!script) {
|
|
53
|
-
script = document.createElement("script");
|
|
54
|
-
script.src = src;
|
|
55
|
-
script.async = true;
|
|
56
|
-
updateStatus("loading");
|
|
57
|
-
document.body.append(script);
|
|
58
|
-
|
|
59
|
-
script.addEventListener("load", handleScriptLoad);
|
|
60
|
-
script.addEventListener("error", handleScriptError);
|
|
61
|
-
|
|
62
|
-
const observer = observeScript(script);
|
|
62
|
+
const { removeOnUnmount } = options;
|
|
63
|
+
const getSnapshot = useCallback((): ScriptStatus => {
|
|
64
|
+
const cached = scripts.get(src)?.element;
|
|
65
|
+
const element = cached?.isConnected ? cached : findScript(src);
|
|
66
|
+
return (element?.dataset.status as ScriptStatus) || "unknown";
|
|
67
|
+
}, [src]);
|
|
63
68
|
|
|
69
|
+
const subscribe = useCallback(
|
|
70
|
+
(onChange: () => void) => {
|
|
71
|
+
const entry = acquireScript(src);
|
|
72
|
+
entry.listeners.add(onChange);
|
|
64
73
|
return () => {
|
|
65
|
-
|
|
66
|
-
if (
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (options.removeOnUnmount) {
|
|
71
|
-
script.remove();
|
|
74
|
+
entry.listeners.delete(onChange);
|
|
75
|
+
if (removeOnUnmount && entry.owned && entry.listeners.size === 0) {
|
|
76
|
+
entry.element.remove();
|
|
77
|
+
if (scripts.get(src) === entry) {
|
|
78
|
+
scripts.delete(src);
|
|
72
79
|
}
|
|
73
80
|
}
|
|
74
81
|
};
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if (domStatus) {
|
|
79
|
-
if (domStatus === "ready" || domStatus === "error") {
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
const observer = observeScript(script);
|
|
83
|
-
return () => observer.disconnect();
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
setStatus("unknown");
|
|
87
|
-
}, [src, options.removeOnUnmount]);
|
|
82
|
+
},
|
|
83
|
+
[src, removeOnUnmount],
|
|
84
|
+
);
|
|
88
85
|
|
|
89
|
-
return
|
|
86
|
+
return useSyncExternalStore(subscribe, getSnapshot);
|
|
90
87
|
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
import { type SetStateAction, useCallback, useReducer } from "react";
|
|
3
|
+
|
|
4
|
+
interface KeyedState<T> {
|
|
5
|
+
key: unknown;
|
|
6
|
+
value: T;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
type KeyedAction<T> =
|
|
10
|
+
| { type: "reset"; state: KeyedState<T> }
|
|
11
|
+
| { type: "update"; key: unknown; update: SetStateAction<T> };
|
|
12
|
+
|
|
13
|
+
function reduceKeyedState<T>(
|
|
14
|
+
previous: KeyedState<T>,
|
|
15
|
+
action: KeyedAction<T>,
|
|
16
|
+
): KeyedState<T> {
|
|
17
|
+
if (action.type === "reset") {
|
|
18
|
+
return action.state;
|
|
19
|
+
}
|
|
20
|
+
if (!Object.is(previous.key, action.key)) {
|
|
21
|
+
return previous;
|
|
22
|
+
}
|
|
23
|
+
const value =
|
|
24
|
+
typeof action.update === "function"
|
|
25
|
+
? (action.update as (previous: T) => T)(previous.value)
|
|
26
|
+
: action.update;
|
|
27
|
+
return Object.is(previous.value, value)
|
|
28
|
+
? previous
|
|
29
|
+
: { key: action.key, value };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Local state that resets when its owner key changes.
|
|
34
|
+
*
|
|
35
|
+
* The key uses Object.is. NaN matches NaN, 0 differs from -0, and objects match
|
|
36
|
+
* by reference. Keep the key stable between resets. An inline object such as
|
|
37
|
+
* `{ id }` creates a new key on each render and causes a render loop.
|
|
38
|
+
*
|
|
39
|
+
* On a key change, this hook calls initialize() and schedules a state update
|
|
40
|
+
* during render. React retries the calling component before rendering its
|
|
41
|
+
* children. Children and effects receive the reset value without an intervening
|
|
42
|
+
* commit of the old value. This resets only this hook's state. The component
|
|
43
|
+
* stays mounted, and its other hooks keep their state.
|
|
44
|
+
*
|
|
45
|
+
* initialize must be pure. React can call it more than once in Strict Mode or
|
|
46
|
+
* when retrying an interrupted render. A new initializer alone does not reset
|
|
47
|
+
* state. The next key change uses the initializer from that render. After
|
|
48
|
+
* committed key changes A -> B -> A, A starts fresh instead of restoring its
|
|
49
|
+
* earlier value. If React abandons a pending render for B, the visible state
|
|
50
|
+
* for A remains intact, including edits made while B was pending.
|
|
51
|
+
*
|
|
52
|
+
* The setter accepts values or pure updater functions, like useState. To store
|
|
53
|
+
* a function, use setValue(() => callback). Its identity is stable for the same
|
|
54
|
+
* key and changes with the key. Updates from a different key are ignored,
|
|
55
|
+
* including their updater functions. After A -> B -> A, an old setter for A
|
|
56
|
+
* can update A again. This hook does not cancel requests. For request results,
|
|
57
|
+
* use a distinct key for each lifetime so late results stay invalid.
|
|
58
|
+
*
|
|
59
|
+
* React can still call the component for ignored or same-value updates.
|
|
60
|
+
* Render counts do not indicate how many changes React commits to the screen.
|
|
61
|
+
*
|
|
62
|
+
* Use useInternalStateWithSync when the external value itself supplies the
|
|
63
|
+
* reset value. Use this hook when a separate key controls the reset.
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* const [expanded, setExpanded] = useStateForKey(tree, () => new Set<string>());
|
|
67
|
+
*
|
|
68
|
+
* @see https://react.dev/reference/react/useState#storing-information-from-previous-renders
|
|
69
|
+
*/
|
|
70
|
+
export function useStateForKey<T>(key: unknown, initialize: () => T) {
|
|
71
|
+
// useState can eagerly evaluate an updater against a suspended render's
|
|
72
|
+
// state. A reducer checks the key when React processes each render's queue,
|
|
73
|
+
// so the still-visible owner can accept edits during a transition.
|
|
74
|
+
const [stored, dispatch] = useReducer(reduceKeyedState<T>, key, (key) => ({
|
|
75
|
+
key,
|
|
76
|
+
value: initialize(),
|
|
77
|
+
}));
|
|
78
|
+
const current = Object.is(stored.key, key)
|
|
79
|
+
? stored
|
|
80
|
+
: { key, value: initialize() };
|
|
81
|
+
if (current !== stored) {
|
|
82
|
+
dispatch({ type: "reset", state: current });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const setValue = useCallback(
|
|
86
|
+
(update: SetStateAction<T>) => {
|
|
87
|
+
dispatch({ type: "update", key, update });
|
|
88
|
+
},
|
|
89
|
+
[key],
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
return [current.value, setValue] as const;
|
|
93
|
+
}
|
|
@@ -104,7 +104,7 @@ function PluginSlotInternal<T>(
|
|
|
104
104
|
ref: React.Ref<PluginSlotHandle>,
|
|
105
105
|
): JSX.Element {
|
|
106
106
|
const [childNodes, setChildNodes] = useState<ReactNode>(children);
|
|
107
|
-
const [value, setValue] = useState<T>(getInitialValue
|
|
107
|
+
const [value, setValue] = useState<T>(getInitialValue);
|
|
108
108
|
const { theme } = useTheme();
|
|
109
109
|
|
|
110
110
|
const [parsedResult, setParsedResult] = useState(() => {
|
|
@@ -546,10 +546,12 @@ export const LoadingDataTableComponent = memo(
|
|
|
546
546
|
}
|
|
547
547
|
}, [setValue, filters, searchQuery, sorting, props.hasStableRowId]);
|
|
548
548
|
|
|
549
|
-
|
|
550
|
-
|
|
549
|
+
const [previousPageSize, setPreviousPageSize] = useState(props.pageSize);
|
|
550
|
+
// Reconcile before requesting data for the updated table configuration.
|
|
551
|
+
if (previousPageSize !== props.pageSize) {
|
|
552
|
+
setPreviousPageSize(props.pageSize);
|
|
551
553
|
setPaginationState({ pageIndex: 0, pageSize: props.pageSize });
|
|
552
|
-
}
|
|
554
|
+
}
|
|
553
555
|
|
|
554
556
|
// Data loading
|
|
555
557
|
const { data, error, isPending, isFetching } = useAsyncData<{
|
|
@@ -697,12 +699,13 @@ export const LoadingDataTableComponent = memo(
|
|
|
697
699
|
[search, sorting, filters, searchQuery],
|
|
698
700
|
);
|
|
699
701
|
|
|
700
|
-
|
|
701
|
-
|
|
702
|
+
const [previousTotalRows, setPreviousTotalRows] = useState(data?.totalRows);
|
|
703
|
+
if (previousTotalRows !== data?.totalRows) {
|
|
704
|
+
setPreviousTotalRows(data?.totalRows);
|
|
702
705
|
setPaginationState((state) =>
|
|
703
706
|
state.pageIndex === 0 ? state : { ...state, pageIndex: 0 },
|
|
704
707
|
);
|
|
705
|
-
}
|
|
708
|
+
}
|
|
706
709
|
|
|
707
710
|
// Column summaries
|
|
708
711
|
const { data: columnSummaries, error: columnSummariesError } = useAsyncData<
|
|
@@ -835,7 +838,7 @@ const DataTableComponent = ({
|
|
|
835
838
|
sizeBytesIsLoading,
|
|
836
839
|
maxColumns,
|
|
837
840
|
pagination,
|
|
838
|
-
selection,
|
|
841
|
+
selection: requestedSelection,
|
|
839
842
|
value,
|
|
840
843
|
showFilters,
|
|
841
844
|
showDownload,
|
|
@@ -843,7 +846,7 @@ const DataTableComponent = ({
|
|
|
843
846
|
showColumnExplorer,
|
|
844
847
|
showRowExplorer,
|
|
845
848
|
showChartBuilder,
|
|
846
|
-
showDataTypes,
|
|
849
|
+
showDataTypes: requestedShowDataTypes,
|
|
847
850
|
rowHeaders,
|
|
848
851
|
fieldTypes,
|
|
849
852
|
paginationState,
|
|
@@ -975,15 +978,11 @@ const DataTableComponent = ({
|
|
|
975
978
|
const fractionDigitsByColumn = useDeepCompareMemoize(computedFractionDigits);
|
|
976
979
|
const shownColumns = memoizedClampedFieldTypes.length;
|
|
977
980
|
|
|
978
|
-
//
|
|
979
|
-
|
|
980
|
-
showDataTypes = false;
|
|
981
|
-
}
|
|
981
|
+
// Only show data types when they are explicitly supplied.
|
|
982
|
+
const showDataTypes = Boolean(fieldTypes && requestedShowDataTypes);
|
|
982
983
|
|
|
983
984
|
// Row/cell selection writes back to the kernel, absent in static exports.
|
|
984
|
-
|
|
985
|
-
selection = null;
|
|
986
|
-
}
|
|
985
|
+
const selection = isStatic ? null : requestedSelection;
|
|
987
986
|
|
|
988
987
|
const columns = useMemo(
|
|
989
988
|
() =>
|
|
@@ -97,13 +97,9 @@ const Dropdown = (props: DropdownProps): JSX.Element => {
|
|
|
97
97
|
id={id}
|
|
98
98
|
disabled={disabled}
|
|
99
99
|
>
|
|
100
|
-
{allowSelectNone ?
|
|
101
|
-
<option value={EMPTY_VALUE} selected={value.length === 0}>
|
|
102
|
-
--
|
|
103
|
-
</option>
|
|
104
|
-
) : null}
|
|
100
|
+
{allowSelectNone ? <option value={EMPTY_VALUE}>--</option> : null}
|
|
105
101
|
{options.map((option) => (
|
|
106
|
-
<option value={option}
|
|
102
|
+
<option value={option} key={option}>
|
|
107
103
|
{option}
|
|
108
104
|
</option>
|
|
109
105
|
))}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
3
|
import { type LucideIcon, ChevronRightIcon, CornerLeftUp } from "lucide-react";
|
|
4
|
-
import { type JSX,
|
|
4
|
+
import { type JSX, useLayoutEffect, useRef, useState } from "react";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
import {
|
|
7
7
|
FILE_ICON as FILE_TYPE_ICONS,
|
|
@@ -16,6 +16,7 @@ import { Table, TableBody, TableCell, TableRow } from "@/components/ui/table";
|
|
|
16
16
|
import { toast } from "@/components/ui/use-toast";
|
|
17
17
|
import { RANDOM_ID_ATTR } from "@/core/dom/ui-element-constants";
|
|
18
18
|
import { useAsyncData } from "@/hooks/useAsyncData";
|
|
19
|
+
import { useDelayElapsed } from "@/hooks/useDelayElapsed";
|
|
19
20
|
import { useInternalStateWithSync } from "@/hooks/useInternalStateWithSync";
|
|
20
21
|
import { cn } from "@/utils/cn";
|
|
21
22
|
import { type FilePath, PathBuilder, Paths } from "@/utils/paths";
|
|
@@ -200,7 +201,6 @@ export const FileBrowser = ({
|
|
|
200
201
|
}: FileBrowserProps): JSX.Element | null => {
|
|
201
202
|
const [path, setPath] = useInternalStateWithSync(initialPath);
|
|
202
203
|
const [isUpdatingPath, setIsUpdatingPath] = useState(false);
|
|
203
|
-
const [showLoadingOverlay, setShowLoadingOverlay] = useState(false);
|
|
204
204
|
const [activeIndex, setActiveIndex] = useState(0);
|
|
205
205
|
const rowRefs = useRef<(HTMLTableRowElement | null)[]>([]);
|
|
206
206
|
const gridRef = useRef<HTMLTableElement | null>(null);
|
|
@@ -219,20 +219,8 @@ export const FileBrowser = ({
|
|
|
219
219
|
return list_directory({ path: path });
|
|
220
220
|
}, [path, randomId, list_directory]);
|
|
221
221
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
setShowLoadingOverlay(false);
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
const timeout = window.setTimeout(() => {
|
|
229
|
-
setShowLoadingOverlay(true);
|
|
230
|
-
}, 200);
|
|
231
|
-
|
|
232
|
-
return () => {
|
|
233
|
-
window.clearTimeout(timeout);
|
|
234
|
-
};
|
|
235
|
-
}, [isPending]);
|
|
222
|
+
const loadingDelayElapsed = useDelayElapsed(isPending ? 200 : 0);
|
|
223
|
+
const showLoadingOverlay = isPending && loadingDelayElapsed;
|
|
236
224
|
|
|
237
225
|
// Reset the roving tabindex whenever the listing reloads (a new path or a
|
|
238
226
|
// same-path refresh) so activeIndex never points past the current rows.
|
|
@@ -212,7 +212,7 @@ const Form = ({
|
|
|
212
212
|
// wrapped plugin; this buffered value is in contrast to the actual
|
|
213
213
|
// value of the plugin, which is the value of the wrapped plugin when
|
|
214
214
|
// the submit button was last clicked/activated.
|
|
215
|
-
const [internalValue, setInternalValue] = useState<T>(
|
|
215
|
+
const [internalValue, setInternalValue] = useState<T>(() =>
|
|
216
216
|
UI_ELEMENT_REGISTRY.lookupValue(elementId),
|
|
217
217
|
);
|
|
218
218
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
-
import { type JSX,
|
|
2
|
+
import { type JSX, useId, useMemo } from "react";
|
|
3
3
|
import { useLocale } from "react-aria";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { NumberField } from "@/components/ui/number-field";
|
|
6
|
+
import { useInternalStateWithSync } from "@/hooks/useInternalStateWithSync";
|
|
6
7
|
import { cn } from "@/utils/cn";
|
|
7
8
|
import { clamp } from "@/utils/math";
|
|
8
9
|
import {
|
|
@@ -156,13 +157,10 @@ const SliderComponent = ({
|
|
|
156
157
|
const { locale } = useLocale();
|
|
157
158
|
|
|
158
159
|
// Hold internal value
|
|
159
|
-
const [internalValue, setInternalValue] =
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
// oxlint-disable-next-line react/no-deriving-state-in-effects -- Synchronize the editable buffer when the controlled value changes.
|
|
164
|
-
setInternalValue(value);
|
|
165
|
-
}, [value]);
|
|
160
|
+
const [internalValue, setInternalValue] = useInternalStateWithSync(
|
|
161
|
+
value,
|
|
162
|
+
Object.is,
|
|
163
|
+
);
|
|
166
164
|
|
|
167
165
|
const stepsConfig = useMemo(() => computeStepsConfig(steps), [steps]);
|
|
168
166
|
|