@marimo-team/islands 0.15.5 → 0.16.1
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-CBeIYi8p.js → ConnectedDataExplorerComponent-DyqLQGPc.js} +1567 -1544
- package/dist/{ImageComparisonComponent-Bk0a0xBq.js → ImageComparisonComponent-CQDGJfUA.js} +1 -1
- package/dist/{_baseUniq-utU5_Vu-.js → _baseUniq-B2Nna6Kt.js} +1 -1
- package/dist/{any-language-editor-PrUUh2lr.js → any-language-editor-D-wq0tOG.js} +1 -1
- package/dist/{architectureDiagram-W76B3OCA-D-vOp0UU.js → architectureDiagram-W76B3OCA-C6tdnMBf.js} +4 -4
- package/dist/assets/{worker-BcG8m3h5.js → worker-B0C57BK8.js} +40 -38
- package/dist/{blockDiagram-QIGZ2CNN-IG-z8q8A.js → blockDiagram-QIGZ2CNN-IagL8LCN.js} +5 -5
- package/dist/{c4Diagram-FPNF74CW-5AEXIX3t.js → c4Diagram-FPNF74CW-D3_lIWUP.js} +2 -2
- package/dist/{channel-ECVsTGGL.js → channel-DCJI_DKk.js} +1 -1
- package/dist/{chunk-4BX2VUAB-DfJcd9e-.js → chunk-4BX2VUAB-B2DrODwN.js} +1 -1
- package/dist/{chunk-55IACEB6-BwT8MejR.js → chunk-55IACEB6-BUWDsQ-t.js} +1 -1
- package/dist/{chunk-FMBD7UC4-DW7uxNR6.js → chunk-FMBD7UC4-BExPNFv1.js} +1 -1
- package/dist/{chunk-K7UQS3LO-BGn2ZPDQ.js → chunk-K7UQS3LO-Cixi-Yko.js} +4 -4
- package/dist/{chunk-QN33PNHL-BcIbOumv.js → chunk-QN33PNHL-B83MtvER.js} +1 -1
- package/dist/{chunk-QZHKN3VN-CMSnhk6x.js → chunk-QZHKN3VN-CXvbu85X.js} +1 -1
- package/dist/{chunk-TVAH2DTR-CZF2JRya.js → chunk-TVAH2DTR-CpiumCHg.js} +3 -3
- package/dist/{chunk-TZMSLE5B-BHzN_BY6.js → chunk-TZMSLE5B-DIzaZjcI.js} +1 -1
- package/dist/{classDiagram-v2-RKCZMP56-2H7MseyB.js → classDiagram-KNZD7YFC-DyN5HPdk.js} +2 -2
- package/dist/{classDiagram-KNZD7YFC-2H7MseyB.js → classDiagram-v2-RKCZMP56-DyN5HPdk.js} +2 -2
- package/dist/{clone-DKQcSK7N.js → clone-DrJYap2i.js} +1 -1
- package/dist/{cose-bilkent-S5V4N54A-CgvKFxTr.js → cose-bilkent-S5V4N54A-D39b4WrQ.js} +2 -2
- package/dist/{dagre-5GWH7T2D-VNFIipzt.js → dagre-5GWH7T2D-BLjRxDpS.js} +6 -6
- package/dist/{data-grid-overlay-editor-XdqkKCVx.js → data-grid-overlay-editor-DTALqerV.js} +2 -2
- package/dist/{diagram-N5W7TBWH-D1s8h-eH.js → diagram-N5W7TBWH-MM8AIKGR.js} +5 -5
- package/dist/{diagram-QEK2KX5R-DOa-AstT.js → diagram-QEK2KX5R-BZGarWuJ.js} +3 -3
- package/dist/{diagram-S2PKOQOG-CFZ-Y2zi.js → diagram-S2PKOQOG-CnPinN9Q.js} +3 -3
- package/dist/{dockerfile-zE-2DWBS.js → dockerfile-U8DnCJ4X.js} +1 -1
- package/dist/{erDiagram-AWTI2OKA-WxUYJfbS.js → erDiagram-AWTI2OKA-CvDVbxOO.js} +4 -4
- package/dist/{flowDiagram-PVAE7QVJ-dDZH2O1W.js → flowDiagram-PVAE7QVJ-C2uuBTZS.js} +5 -5
- package/dist/{ganttDiagram-OWAHRB6G-D3CCqPQq.js → ganttDiagram-OWAHRB6G-BEff10RF.js} +4 -4
- package/dist/{gitGraphDiagram-NY62KEGX-BHFylEwc.js → gitGraphDiagram-NY62KEGX-wggu0kb2.js} +4 -4
- package/dist/{glide-data-editor-D0aJSGV_.js → glide-data-editor-Bqh5_dzJ.js} +3 -3
- package/dist/{graph-BPGEu6c8.js → graph-DKpp_wzf.js} +3 -3
- package/dist/{index-HtOEKQ3O.js → index-4XruEJkp.js} +1 -1
- package/dist/{index-eDB61tLS.js → index-DW0BCGJE.js} +1 -1
- package/dist/{index-DotQhzoN.js → index-DdfF_cLK.js} +1 -1
- package/dist/{index-Bx2b23rX.js → index-DzJ_YPCG.js} +3 -3
- package/dist/{infoDiagram-STP46IZ2-DWhhqGPi.js → infoDiagram-STP46IZ2-DF7KW-Op.js} +2 -2
- package/dist/{journeyDiagram-BIP6EPQ6-CU8FpryL.js → journeyDiagram-BIP6EPQ6-B_jmhmqd.js} +3 -3
- package/dist/{kanban-definition-6OIFK2YF-CWhF_a4g.js → kanban-definition-6OIFK2YF-B-M9FTyw.js} +2 -2
- package/dist/{layout-DGonEvAZ.js → layout-C4oVYZZD.js} +4 -4
- package/dist/{linear-Cww2a6nQ.js → linear-C-HCGr0T.js} +1 -1
- package/dist/{main-Bc0LY9fB.js → main-B9x2-9f2.js} +93798 -93495
- package/dist/main.js +1 -1
- package/dist/{mermaid-DpJuOhRr.js → mermaid-BE4cM3Qs.js} +30 -30
- package/dist/{min-CFQjsG4L.js → min-DTpHJ698.js} +2 -2
- package/dist/{mindmap-definition-Q6HEUPPD-K513Ef1t.js → mindmap-definition-Q6HEUPPD-Cpd-hO1E.js} +3 -3
- package/dist/{number-overlay-editor-DuSchUfE.js → number-overlay-editor-CvURA2Ud.js} +2 -2
- package/dist/{pieDiagram-ADFJNKIX-DAIIUJJO.js → pieDiagram-ADFJNKIX-D9f_f6fn.js} +3 -3
- package/dist/{quadrantDiagram-LMRXKWRM-yuf-j7Os.js → quadrantDiagram-LMRXKWRM-DgllE7xw.js} +2 -2
- package/dist/{react-plotly-B378DZ9U.js → react-plotly-BU-JRJSi.js} +1 -1
- package/dist/{requirementDiagram-4UW4RH46-BBWvEl6q.js → requirementDiagram-4UW4RH46-Dk_G8eUb.js} +3 -3
- package/dist/{sankeyDiagram-GR3RE2ED-B_TwV-dS.js → sankeyDiagram-GR3RE2ED-BhLIhDc1.js} +1 -1
- package/dist/{sequenceDiagram-C3RYC4MD-BVC6lltp.js → sequenceDiagram-C3RYC4MD-DHoZdMFJ.js} +3 -3
- package/dist/{slides-component-CPX3S0Y9.js → slides-component-DXAgdf7K.js} +2 -2
- package/dist/{stateDiagram-KXAO66HF-BCU1tYTD.js → stateDiagram-KXAO66HF-C1Ie-7Xf.js} +4 -4
- package/dist/{stateDiagram-v2-UMBNRL4Z-BdvN6wTu.js → stateDiagram-v2-UMBNRL4Z--CRuIHtM.js} +2 -2
- package/dist/style.css +1 -1
- package/dist/{time-CSIip6fV.js → time-yQjlGPwa.js} +2 -2
- package/dist/{timeline-definition-XQNQX7LJ-CCxCPNQI.js → timeline-definition-XQNQX7LJ-D_PjxB1B.js} +1 -1
- package/dist/{treemap-75Q7IDZK-Du6v0BzD.js → treemap-75Q7IDZK--NYqQjUZ.js} +134 -134
- package/dist/{vega-component-Da93sTnp.js → vega-component-CCUOMM5K.js} +2 -2
- package/dist/{xychartDiagram-6GGTOJPD-Oq6xaZKR.js → xychartDiagram-6GGTOJPD-WLKsEnzs.js} +2 -2
- package/package.json +10 -5
- package/src/__tests__/mocks.ts +43 -0
- package/src/components/app-config/user-config-form.tsx +78 -1
- package/src/components/chat/acp/__tests__/__snapshots__/prompt.test.ts.snap +116 -65
- package/src/components/chat/acp/__tests__/atoms.test.ts +1 -1
- package/src/components/chat/acp/__tests__/context-utils.test.ts +222 -0
- package/src/components/chat/acp/__tests__/prompt.test.ts +1 -1
- package/src/components/chat/acp/__tests__/state.test.ts +38 -42
- package/src/components/chat/acp/agent-docs.tsx +33 -6
- package/src/components/chat/acp/agent-panel.css +0 -18
- package/src/components/chat/acp/agent-panel.tsx +394 -72
- package/src/components/chat/acp/agent-selector.tsx +7 -1
- package/src/components/chat/acp/blocks.tsx +40 -10
- package/src/components/chat/acp/common.tsx +10 -2
- package/src/components/chat/acp/context-utils.ts +127 -0
- package/src/components/chat/acp/prompt.ts +96 -53
- package/src/components/chat/acp/state.ts +1 -1
- package/src/components/chat/acp/types.ts +8 -0
- package/src/components/chat/chat-panel.tsx +28 -89
- package/src/components/chat/chat-utils.ts +127 -1
- package/src/components/chat/markdown-renderer.css +39 -0
- package/src/components/chat/markdown-renderer.tsx +12 -47
- package/src/components/chat/tool-call-accordion.tsx +148 -26
- package/src/components/data-table/SearchBar.tsx +8 -7
- package/src/components/data-table/__tests__/column_formatting.test.ts +50 -35
- package/src/components/data-table/__tests__/data-table.test.tsx +39 -1
- package/src/components/data-table/cell-hover-template/feature.ts +14 -0
- package/src/components/data-table/cell-hover-template/types.ts +11 -0
- package/src/components/data-table/charts/components/form-fields.tsx +41 -37
- package/src/components/data-table/charts/forms/common-chart.tsx +2 -2
- package/src/components/data-table/column-explorer-panel/column-explorer.tsx +5 -2
- package/src/components/data-table/column-formatting/feature.ts +62 -29
- package/src/components/data-table/column-formatting/types.ts +1 -0
- package/src/components/data-table/column-header.tsx +3 -1
- package/src/components/data-table/column-summary/chart-spec-model.tsx +24 -7
- package/src/components/data-table/column-summary/column-summary.tsx +18 -9
- package/src/components/data-table/columns.tsx +42 -18
- package/src/components/data-table/data-table.tsx +10 -2
- package/src/components/data-table/date-popover.tsx +85 -75
- package/src/components/data-table/filter-pills.tsx +14 -9
- package/src/components/data-table/header-items.tsx +5 -1
- package/src/components/data-table/pagination.tsx +20 -13
- package/src/components/data-table/renderers.tsx +28 -0
- package/src/components/data-table/row-viewer-panel/row-viewer.tsx +10 -8
- package/src/components/datasources/column-preview.tsx +6 -2
- package/src/components/datasources/datasources.tsx +8 -12
- package/src/components/editor/Cell.tsx +6 -0
- package/src/components/editor/actions/name-cell-input.tsx +6 -1
- package/src/components/editor/actions/useCellActionButton.tsx +3 -1
- package/src/components/editor/ai/__tests__/completion-utils.test.ts +178 -1
- package/src/components/editor/ai/add-cell-with-ai.tsx +68 -66
- package/src/components/editor/ai/ai-completion-editor.tsx +29 -26
- package/src/components/editor/ai/completion-handlers.tsx +44 -6
- package/src/components/editor/ai/completion-utils.ts +92 -0
- package/src/components/editor/ai/transport/chat-transport.tsx +39 -0
- package/src/components/editor/cell/CellStatus.tsx +23 -20
- package/src/components/editor/cell/CreateCellButton.tsx +3 -4
- package/src/components/editor/cell/StagedAICell.tsx +51 -0
- package/src/components/editor/cell/cell-actions.tsx +2 -1
- package/src/components/editor/cell/code/language-toggle.tsx +3 -4
- package/src/components/editor/chrome/wrapper/footer-items/machine-stats.tsx +39 -28
- package/src/components/editor/controls/notebook-menu-dropdown.tsx +4 -2
- package/src/components/editor/file-tree/requesting-tree.tsx +14 -8
- package/src/components/editor/renderers/CellArray.tsx +3 -4
- package/src/components/editor/renderers/slides-layout/slides-layout.tsx +3 -3
- package/src/components/editor/renderers/slides-layout/types.ts +1 -0
- package/src/components/pages/home-page.tsx +4 -1
- package/src/components/slides/slides-component.tsx +1 -1
- package/src/components/slides/slides.css +6 -0
- package/src/components/terminal/__tests__/state.test.ts +207 -0
- package/src/components/terminal/hooks.ts +41 -0
- package/src/components/terminal/state.ts +75 -0
- package/src/components/terminal/terminal.tsx +334 -13
- package/src/components/terminal/theme.tsx +57 -0
- package/src/components/tracing/tracing-spec.ts +5 -4
- package/src/components/ui/range-slider.tsx +4 -2
- package/src/components/ui/slider.tsx +3 -1
- package/src/components/variables/variables-table.tsx +3 -0
- package/src/core/MarimoApp.tsx +9 -6
- package/src/core/ai/__tests__/staged-cells.test.ts +356 -0
- package/src/core/ai/context/__tests__/registry.test.ts +6 -4
- package/src/core/ai/context/providers/cell-output.ts +3 -2
- package/src/core/ai/context/providers/error.ts +3 -1
- package/src/core/ai/context/providers/file.ts +7 -2
- package/src/core/ai/context/providers/tables.ts +3 -2
- package/src/core/ai/context/providers/variable.ts +6 -4
- package/src/core/ai/staged-cells.ts +241 -0
- package/src/core/cells/__tests__/add-missing-import.test.ts +67 -22
- package/src/core/cells/add-missing-import.ts +24 -7
- package/src/core/cells/cells.ts +27 -28
- package/src/core/cells/logs.ts +1 -1
- package/src/core/codemirror/find-replace/search-highlight.ts +3 -1
- package/src/core/codemirror/language/LanguageAdapters.ts +9 -3
- package/src/core/codemirror/lsp/federated-lsp.ts +1 -1
- package/src/core/codemirror/lsp/notebook-lsp.ts +8 -2
- package/src/core/codemirror/readonly/__tests__/extension.test.ts +1 -1
- package/src/core/codemirror/rtc/loro/awareness.ts +52 -17
- package/src/core/codemirror/rtc/loro/sync.ts +12 -4
- package/src/core/config/config-schema.ts +1 -0
- package/src/core/config/config.ts +4 -0
- package/src/core/hotkeys/hotkeys.ts +8 -4
- package/src/core/i18n/__tests__/locale-provider.test.tsx +176 -0
- package/src/core/i18n/locale-provider.tsx +35 -0
- package/src/core/i18n/with-locale.tsx +12 -0
- package/src/core/islands/components/web-components.tsx +13 -10
- package/src/core/islands/main.ts +2 -2
- package/src/core/kernel/RuntimeState.ts +4 -1
- package/src/core/kernel/messages.ts +8 -12
- package/src/core/network/DeferredRequestRegistry.ts +16 -4
- package/src/core/runtime/runtime.ts +5 -4
- package/src/core/saving/__tests__/filename.test.ts +37 -0
- package/src/core/static/__tests__/download-html.test.ts +43 -1
- package/src/core/wasm/bridge.ts +5 -1
- package/src/core/wasm/store.ts +4 -1
- package/src/core/wasm/worker/message-buffer.ts +3 -2
- package/src/core/websocket/types.ts +22 -16
- package/src/core/websocket/useMarimoWebSocket.tsx +2 -2
- package/src/css/app/Cell.css +11 -0
- package/src/hooks/useFormatting.ts +97 -0
- package/src/hooks/useTimer.ts +8 -5
- package/src/plugins/core/RenderHTML.tsx +36 -2
- package/src/plugins/core/__test__/RenderHTML.test.ts +72 -0
- package/src/plugins/core/registerReactComponent.tsx +44 -10
- package/src/plugins/impl/DataTablePlugin.tsx +4 -0
- package/src/plugins/impl/FileBrowserPlugin.tsx +8 -2
- package/src/plugins/impl/RangeSliderPlugin.tsx +5 -3
- package/src/plugins/impl/SliderPlugin.tsx +3 -1
- package/src/plugins/impl/anywidget/model.ts +16 -5
- package/src/plugins/impl/data-editor/types.ts +7 -5
- package/src/plugins/impl/data-explorer/components/column-summary.tsx +20 -13
- package/src/plugins/impl/panel/utils.ts +6 -4
- package/src/plugins/layout/OutlinePlugin.tsx +69 -0
- package/src/plugins/layout/StatPlugin.tsx +4 -1
- package/src/plugins/plugins.ts +2 -0
- package/src/stories/cell.stories.tsx +1 -1
- package/src/stories/layout/vertical/one-column.stories.tsx +1 -1
- package/src/utils/__tests__/cell-urls.test.ts +29 -0
- package/src/utils/__tests__/dates.test.ts +45 -24
- package/src/utils/__tests__/filenames.test.ts +18 -0
- package/src/utils/__tests__/numbers.test.ts +42 -30
- package/src/utils/__tests__/once.test.ts +187 -0
- package/src/utils/__tests__/path.test.ts +38 -0
- package/src/utils/__tests__/urls.test.ts +56 -1
- package/src/utils/dates.ts +15 -10
- package/src/utils/edit-distance.ts +8 -6
- package/src/utils/errors.ts +9 -0
- package/src/utils/id-tree.tsx +21 -10
- package/src/utils/localStorage.ts +13 -4
- package/src/utils/numbers.ts +11 -11
- package/src/utils/once.ts +32 -0
- package/src/utils/paths.ts +4 -1
- package/src/utils/pluralize.ts +12 -5
- package/src/utils/python-poet/poet.ts +30 -15
- package/src/utils/time.ts +5 -1
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/* Copyright 2024 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { useCallback } from "react";
|
|
4
|
+
import { useLocale } from "react-aria";
|
|
5
|
+
import { getShortTimeZone, prettyDate, timeAgo } from "@/utils/dates";
|
|
6
|
+
import {
|
|
7
|
+
prettyEngineeringNumber,
|
|
8
|
+
prettyNumber,
|
|
9
|
+
prettyScientificNumber,
|
|
10
|
+
} from "@/utils/numbers";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Hook that provides locale-aware number formatting using the prettyNumber utility
|
|
14
|
+
*/
|
|
15
|
+
export function usePrettyNumber() {
|
|
16
|
+
const { locale } = useLocale();
|
|
17
|
+
|
|
18
|
+
return useCallback(
|
|
19
|
+
(value: unknown): string => {
|
|
20
|
+
return prettyNumber(value, locale);
|
|
21
|
+
},
|
|
22
|
+
[locale],
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Hook that provides locale-aware scientific number formatting
|
|
28
|
+
*/
|
|
29
|
+
export function usePrettyScientificNumber() {
|
|
30
|
+
const { locale } = useLocale();
|
|
31
|
+
|
|
32
|
+
return useCallback(
|
|
33
|
+
(value: number, opts: { shouldRound?: boolean } = {}): string => {
|
|
34
|
+
return prettyScientificNumber(value, { ...opts, locale });
|
|
35
|
+
},
|
|
36
|
+
[locale],
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Hook that provides locale-aware engineering number formatting
|
|
42
|
+
*/
|
|
43
|
+
export function usePrettyEngineeringNumber() {
|
|
44
|
+
const { locale } = useLocale();
|
|
45
|
+
|
|
46
|
+
return useCallback(
|
|
47
|
+
(value: number): string => {
|
|
48
|
+
return prettyEngineeringNumber(value, locale);
|
|
49
|
+
},
|
|
50
|
+
[locale],
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Hook that provides locale-aware date formatting
|
|
56
|
+
*/
|
|
57
|
+
export function usePrettyDate() {
|
|
58
|
+
const { locale } = useLocale();
|
|
59
|
+
|
|
60
|
+
return useCallback(
|
|
61
|
+
(
|
|
62
|
+
value: string | number | null | undefined,
|
|
63
|
+
type: "date" | "datetime",
|
|
64
|
+
): string => {
|
|
65
|
+
return prettyDate(value, type, locale);
|
|
66
|
+
},
|
|
67
|
+
[locale],
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Hook that provides locale-aware relative time formatting
|
|
73
|
+
*/
|
|
74
|
+
export function useTimeAgo() {
|
|
75
|
+
const { locale } = useLocale();
|
|
76
|
+
|
|
77
|
+
return useCallback(
|
|
78
|
+
(value: string | number | null | undefined): string => {
|
|
79
|
+
return timeAgo(value, locale);
|
|
80
|
+
},
|
|
81
|
+
[locale],
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Hook that provides locale-aware timezone abbreviation
|
|
87
|
+
*/
|
|
88
|
+
export function useShortTimeZone() {
|
|
89
|
+
const { locale } = useLocale();
|
|
90
|
+
|
|
91
|
+
return useCallback(
|
|
92
|
+
(timezone: string): string => {
|
|
93
|
+
return getShortTimeZone(timezone, locale);
|
|
94
|
+
},
|
|
95
|
+
[locale],
|
|
96
|
+
);
|
|
97
|
+
}
|
package/src/hooks/useTimer.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* Copyright 2024 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
3
|
import { useEffect, useRef, useState } from "react";
|
|
4
|
+
import { useNumberFormatter } from "react-aria";
|
|
4
5
|
import useEvent from "react-use-event-hook";
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -11,6 +12,12 @@ export function useTimer() {
|
|
|
11
12
|
const [time, setTime] = useState(0);
|
|
12
13
|
const interval = useRef<number>(undefined);
|
|
13
14
|
|
|
15
|
+
// one decimal place, exactly
|
|
16
|
+
const numberFormatter = useNumberFormatter({
|
|
17
|
+
minimumFractionDigits: 1,
|
|
18
|
+
maximumFractionDigits: 1,
|
|
19
|
+
});
|
|
20
|
+
|
|
14
21
|
const start = useEvent(() => {
|
|
15
22
|
interval.current = window.setInterval(() => {
|
|
16
23
|
setTime((time) => time + 0.1);
|
|
@@ -36,11 +43,7 @@ export function useTimer() {
|
|
|
36
43
|
}, []);
|
|
37
44
|
|
|
38
45
|
return {
|
|
39
|
-
|
|
40
|
-
time: new Intl.NumberFormat("en-US", {
|
|
41
|
-
minimumFractionDigits: 1,
|
|
42
|
-
maximumFractionDigits: 1,
|
|
43
|
-
}).format(time),
|
|
46
|
+
time: numberFormatter.format(time),
|
|
44
47
|
start,
|
|
45
48
|
stop,
|
|
46
49
|
clear,
|
|
@@ -5,7 +5,7 @@ import parse, {
|
|
|
5
5
|
Element,
|
|
6
6
|
type HTMLReactParserOptions,
|
|
7
7
|
} from "html-react-parser";
|
|
8
|
-
import React, { type JSX, type ReactNode, useId } from "react";
|
|
8
|
+
import React, { isValidElement, type JSX, type ReactNode, useId } from "react";
|
|
9
9
|
import { CopyClipboardIcon } from "@/components/icons/copy-icon";
|
|
10
10
|
|
|
11
11
|
type ReplacementFn = NonNullable<HTMLReactParserOptions["replace"]>;
|
|
@@ -23,6 +23,36 @@ const replaceValidTags = (domNode: DOMNode) => {
|
|
|
23
23
|
}
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
+
const removeWrappingBodyTags: TransformFn = (
|
|
27
|
+
reactNode: ReactNode,
|
|
28
|
+
domNode: DOMNode,
|
|
29
|
+
) => {
|
|
30
|
+
// Remove body tags and just render their children
|
|
31
|
+
if (domNode instanceof Element && domNode.name === "body") {
|
|
32
|
+
if (isValidElement(reactNode) && "props" in reactNode) {
|
|
33
|
+
const props = reactNode.props as { children?: ReactNode };
|
|
34
|
+
const children = props.children;
|
|
35
|
+
return <>{children}</>; // eslint-disable-line react/jsx-no-useless-fragment
|
|
36
|
+
}
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const removeWrappingHtmlTags: TransformFn = (
|
|
42
|
+
reactNode: ReactNode,
|
|
43
|
+
domNode: DOMNode,
|
|
44
|
+
) => {
|
|
45
|
+
// Remove html tags and just render their children
|
|
46
|
+
if (domNode instanceof Element && domNode.name === "html") {
|
|
47
|
+
if (isValidElement(reactNode) && "props" in reactNode) {
|
|
48
|
+
const props = reactNode.props as { children?: ReactNode };
|
|
49
|
+
const children = props.children;
|
|
50
|
+
return <>{children}</>; // eslint-disable-line react/jsx-no-useless-fragment
|
|
51
|
+
}
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
26
56
|
const replaceValidIframes = (domNode: DOMNode) => {
|
|
27
57
|
// For iframe, we just want to use dangerouslySetInnerHTML so:
|
|
28
58
|
// 1) we can remount the iframe when the src changes
|
|
@@ -110,7 +140,11 @@ export const renderHTML = ({ html, additionalReplacements = [] }: Options) => {
|
|
|
110
140
|
...additionalReplacements,
|
|
111
141
|
];
|
|
112
142
|
|
|
113
|
-
const transformFunctions: TransformFn[] = [
|
|
143
|
+
const transformFunctions: TransformFn[] = [
|
|
144
|
+
addCopyButtonToCodehilite,
|
|
145
|
+
removeWrappingBodyTags,
|
|
146
|
+
removeWrappingHtmlTags,
|
|
147
|
+
];
|
|
114
148
|
|
|
115
149
|
return parse(html, {
|
|
116
150
|
replace: (domNode: DOMNode, index: number) => {
|
|
@@ -121,6 +121,78 @@ describe("RenderHTML", () => {
|
|
|
121
121
|
</p>
|
|
122
122
|
`);
|
|
123
123
|
});
|
|
124
|
+
|
|
125
|
+
test("removes body tags but preserves children", () => {
|
|
126
|
+
const html = "<body><h1>Hello</h1><p>World</p></body>";
|
|
127
|
+
expect(renderHTML({ html })).toMatchInlineSnapshot(`
|
|
128
|
+
<React.Fragment>
|
|
129
|
+
<h1>
|
|
130
|
+
Hello
|
|
131
|
+
</h1>
|
|
132
|
+
<p>
|
|
133
|
+
World
|
|
134
|
+
</p>
|
|
135
|
+
</React.Fragment>
|
|
136
|
+
`);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test("removes nested body tags", () => {
|
|
140
|
+
const html = "<div><body><span>Content</span></body></div>";
|
|
141
|
+
expect(renderHTML({ html })).toMatchInlineSnapshot(`
|
|
142
|
+
<div>
|
|
143
|
+
<React.Fragment>
|
|
144
|
+
<span>
|
|
145
|
+
Content
|
|
146
|
+
</span>
|
|
147
|
+
</React.Fragment>
|
|
148
|
+
</div>
|
|
149
|
+
`);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test("removes html tags but preserves children", () => {
|
|
153
|
+
const html =
|
|
154
|
+
"<html><head><title>Test</title></head><body><p>Content</p></body></html>";
|
|
155
|
+
expect(renderHTML({ html })).toMatchInlineSnapshot(`
|
|
156
|
+
<React.Fragment>
|
|
157
|
+
<head>
|
|
158
|
+
<title>
|
|
159
|
+
Test
|
|
160
|
+
</title>
|
|
161
|
+
</head>
|
|
162
|
+
<React.Fragment>
|
|
163
|
+
<p>
|
|
164
|
+
Content
|
|
165
|
+
</p>
|
|
166
|
+
</React.Fragment>
|
|
167
|
+
</React.Fragment>
|
|
168
|
+
`);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test("removes nested html tags", () => {
|
|
172
|
+
const html = "<div><html><span>Content</span></html></div>";
|
|
173
|
+
expect(renderHTML({ html })).toMatchInlineSnapshot(`
|
|
174
|
+
<div>
|
|
175
|
+
<React.Fragment>
|
|
176
|
+
<span>
|
|
177
|
+
Content
|
|
178
|
+
</span>
|
|
179
|
+
</React.Fragment>
|
|
180
|
+
</div>
|
|
181
|
+
`);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
test("remove nested body in html", () => {
|
|
185
|
+
const html = "<html><body><span>Content</span></body></html>";
|
|
186
|
+
expect(renderHTML({ html })).toMatchInlineSnapshot(`
|
|
187
|
+
<React.Fragment>
|
|
188
|
+
<React.Fragment>
|
|
189
|
+
<span>
|
|
190
|
+
Content
|
|
191
|
+
</span>
|
|
192
|
+
</React.Fragment>
|
|
193
|
+
</React.Fragment>
|
|
194
|
+
`);
|
|
195
|
+
});
|
|
124
196
|
});
|
|
125
197
|
|
|
126
198
|
describe("RenderHTML with < nad >", () => {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/* Copyright 2024 Marimo. All rights reserved. */
|
|
2
|
+
|
|
2
3
|
/* eslint-disable unicorn/prefer-spread */
|
|
3
4
|
/**
|
|
4
5
|
* WebComponent Factory for React Components
|
|
@@ -7,6 +8,7 @@
|
|
|
7
8
|
* component. The factory handles the logic of communicating UI element values
|
|
8
9
|
* to and from the rest of marimo.
|
|
9
10
|
*/
|
|
11
|
+
import { Provider } from "jotai";
|
|
10
12
|
import React, {
|
|
11
13
|
createRef,
|
|
12
14
|
type JSX,
|
|
@@ -21,16 +23,22 @@ import React, {
|
|
|
21
23
|
import ReactDOM, { type Root } from "react-dom/client";
|
|
22
24
|
import useEvent from "react-use-event-hook";
|
|
23
25
|
import type { ZodSchema } from "zod";
|
|
26
|
+
import { notebookAtom } from "@/core/cells/cells.ts";
|
|
27
|
+
import { findCellId } from "@/core/cells/ids.ts";
|
|
28
|
+
import { isUninstantiated } from "@/core/cells/utils";
|
|
24
29
|
import { createInputEvent, MarimoValueUpdateEvent } from "@/core/dom/events";
|
|
25
30
|
import { getUIElementObjectId } from "@/core/dom/ui-element";
|
|
26
31
|
import { UIElementRegistry } from "@/core/dom/uiregistry";
|
|
27
32
|
import { FUNCTIONS_REGISTRY } from "@/core/functions/FunctionRegistry";
|
|
33
|
+
import { LocaleProvider } from "@/core/i18n/locale-provider";
|
|
34
|
+
import { store } from "@/core/state/jotai";
|
|
28
35
|
import {
|
|
29
36
|
type HTMLElementNotDerivedFromRef,
|
|
30
37
|
useEventListener,
|
|
31
38
|
} from "@/hooks/useEventListener";
|
|
32
39
|
import { StyleNamespace } from "@/theme/namespace";
|
|
33
40
|
import { useTheme } from "@/theme/useTheme";
|
|
41
|
+
import { CellNotInitializedError } from "@/utils/errors.ts";
|
|
34
42
|
import { Functions } from "@/utils/functions";
|
|
35
43
|
import { shallowCompare } from "@/utils/shallow-compare";
|
|
36
44
|
import { defineCustomElement } from "../../core/dom/defineCustomElement";
|
|
@@ -78,6 +86,7 @@ interface PluginSlotProps<T> {
|
|
|
78
86
|
}
|
|
79
87
|
|
|
80
88
|
/* Handles synchronization of value on behalf of the component */
|
|
89
|
+
|
|
81
90
|
// eslint-disable-next-line react/function-component-definition
|
|
82
91
|
function PluginSlotInternal<T>(
|
|
83
92
|
{ hostElement, plugin, children, getInitialValue }: PluginSlotProps<T>,
|
|
@@ -172,6 +181,27 @@ function PluginSlotInternal<T>(
|
|
|
172
181
|
);
|
|
173
182
|
const objectId = getUIElementObjectId(hostElement);
|
|
174
183
|
invariant(objectId, "Object ID should exist");
|
|
184
|
+
|
|
185
|
+
const cellId = findCellId(hostElement);
|
|
186
|
+
invariant(cellId, "Cell ID should exist");
|
|
187
|
+
|
|
188
|
+
const notebookState = store.get(notebookAtom);
|
|
189
|
+
const cellRuntime = notebookState.cellRuntime[cellId];
|
|
190
|
+
const cellData = notebookState.cellData[cellId];
|
|
191
|
+
|
|
192
|
+
const cellNotInitialized = isUninstantiated({
|
|
193
|
+
executionTime:
|
|
194
|
+
cellRuntime.runElapsedTimeMs ?? cellData.lastExecutionTime,
|
|
195
|
+
status: cellRuntime.status,
|
|
196
|
+
errored: cellRuntime.errored,
|
|
197
|
+
interrupted: cellRuntime.interrupted,
|
|
198
|
+
stopped: cellRuntime.stopped,
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
if (cellNotInitialized) {
|
|
202
|
+
throw new CellNotInitializedError();
|
|
203
|
+
}
|
|
204
|
+
|
|
175
205
|
const response = await FUNCTIONS_REGISTRY.request({
|
|
176
206
|
args: prettyParse(input, args[0]),
|
|
177
207
|
functionName: key,
|
|
@@ -335,16 +365,20 @@ export function registerReactComponent<T>(plugin: IPlugin<T, unknown>): void {
|
|
|
335
365
|
|
|
336
366
|
invariant(this.root, "Root must be defined");
|
|
337
367
|
this.root.render(
|
|
338
|
-
<
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
368
|
+
<Provider store={store}>
|
|
369
|
+
<LocaleProvider>
|
|
370
|
+
<PluginSlot
|
|
371
|
+
hostElement={this}
|
|
372
|
+
plugin={plugin}
|
|
373
|
+
ref={this.pluginRef}
|
|
374
|
+
getInitialValue={() => {
|
|
375
|
+
return parseInitialValue(this, UIElementRegistry.INSTANCE);
|
|
376
|
+
}}
|
|
377
|
+
>
|
|
378
|
+
{this.getChildren()}
|
|
379
|
+
</PluginSlot>
|
|
380
|
+
</LocaleProvider>
|
|
381
|
+
</Provider>,
|
|
348
382
|
);
|
|
349
383
|
}
|
|
350
384
|
|
|
@@ -254,6 +254,7 @@ export const DataTablePlugin = createPlugin<S>("marimo-table")
|
|
|
254
254
|
maxColumns: z.union([z.number(), z.literal("all")]).default("all"),
|
|
255
255
|
hasStableRowId: z.boolean().default(false),
|
|
256
256
|
cellStyles: z.record(z.record(z.object({}).passthrough())).optional(),
|
|
257
|
+
hoverTemplate: z.string().optional(),
|
|
257
258
|
// Whether to load the data lazily.
|
|
258
259
|
lazy: z.boolean().default(false),
|
|
259
260
|
// If lazy, this will preload the first page of data
|
|
@@ -385,6 +386,7 @@ interface DataTableProps<T> extends Data<T>, DataTableFunctions {
|
|
|
385
386
|
// Filters
|
|
386
387
|
enableFilters?: boolean;
|
|
387
388
|
cellStyles?: CellStyleState | null;
|
|
389
|
+
hoverTemplate?: string | null;
|
|
388
390
|
toggleDisplayHeader?: () => void;
|
|
389
391
|
host: HTMLElement;
|
|
390
392
|
cellId?: CellId | null;
|
|
@@ -707,6 +709,7 @@ const DataTableComponent = ({
|
|
|
707
709
|
totalColumns,
|
|
708
710
|
get_row_ids,
|
|
709
711
|
cellStyles,
|
|
712
|
+
hoverTemplate,
|
|
710
713
|
toggleDisplayHeader,
|
|
711
714
|
calculate_top_k_rows,
|
|
712
715
|
preview_column,
|
|
@@ -904,6 +907,7 @@ const DataTableComponent = ({
|
|
|
904
907
|
rowSelection={rowSelection}
|
|
905
908
|
cellSelection={cellSelection}
|
|
906
909
|
cellStyling={cellStyles}
|
|
910
|
+
hoverTemplate={hoverTemplate}
|
|
907
911
|
downloadAs={showDownload ? downloadAs : undefined}
|
|
908
912
|
enableSearch={enableSearch}
|
|
909
913
|
searchQuery={searchQuery}
|
|
@@ -12,7 +12,7 @@ import { Button } from "@/components/ui/button";
|
|
|
12
12
|
import { Checkbox } from "@/components/ui/checkbox";
|
|
13
13
|
import { Label } from "@/components/ui/label";
|
|
14
14
|
import { NativeSelect } from "@/components/ui/native-select";
|
|
15
|
-
import { Table, TableCell, TableRow } from "@/components/ui/table";
|
|
15
|
+
import { Table, TableBody, TableCell, TableRow } from "@/components/ui/table";
|
|
16
16
|
import { toast } from "@/components/ui/use-toast";
|
|
17
17
|
import { useAsyncData } from "@/hooks/useAsyncData";
|
|
18
18
|
import { useInternalStateWithSync } from "@/hooks/useInternalStateWithSync";
|
|
@@ -158,6 +158,10 @@ export const FileBrowser = ({
|
|
|
158
158
|
return null;
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
+
if (!data && error) {
|
|
162
|
+
return <Banner kind="danger">{error.message}</Banner>;
|
|
163
|
+
}
|
|
164
|
+
|
|
161
165
|
let { files } = data || {};
|
|
162
166
|
if (files === undefined) {
|
|
163
167
|
files = [];
|
|
@@ -458,7 +462,9 @@ export const FileBrowser = ({
|
|
|
458
462
|
className="mt-3 overflow-y-auto w-full border"
|
|
459
463
|
style={{ height: "14rem" }}
|
|
460
464
|
>
|
|
461
|
-
<Table className="cursor-pointer table-fixed">
|
|
465
|
+
<Table className="cursor-pointer table-fixed">
|
|
466
|
+
<TableBody>{fileRows}</TableBody>
|
|
467
|
+
</Table>
|
|
462
468
|
</div>
|
|
463
469
|
<div className="mt-4">
|
|
464
470
|
{value.length > 0 && (
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { isEqual } from "lodash-es";
|
|
4
4
|
import { type JSX, useEffect, useId, useState } from "react";
|
|
5
|
+
import { useLocale } from "react-aria";
|
|
5
6
|
import { z } from "zod";
|
|
6
7
|
import { cn } from "@/utils/cn";
|
|
7
8
|
import { prettyScientificNumber } from "@/utils/numbers";
|
|
@@ -82,6 +83,7 @@ const RangeSliderComponent = ({
|
|
|
82
83
|
valueMap,
|
|
83
84
|
}: RangeSliderProps): JSX.Element => {
|
|
84
85
|
const id = useId();
|
|
86
|
+
const { locale } = useLocale();
|
|
85
87
|
|
|
86
88
|
// Hold internal value
|
|
87
89
|
const [internalValue, setInternalValue] = useState(value);
|
|
@@ -150,9 +152,9 @@ const RangeSliderComponent = ({
|
|
|
150
152
|
/>
|
|
151
153
|
{showValue && (
|
|
152
154
|
<div className="text-xs text-muted-foreground min-w-[16px]">
|
|
153
|
-
{`${prettyScientificNumber(
|
|
154
|
-
|
|
155
|
-
)}, ${prettyScientificNumber(valueMap(internalValue[1]))}`}
|
|
155
|
+
{`${prettyScientificNumber(valueMap(internalValue[0]), {
|
|
156
|
+
locale,
|
|
157
|
+
})}, ${prettyScientificNumber(valueMap(internalValue[1]), { locale })}`}
|
|
156
158
|
</div>
|
|
157
159
|
)}
|
|
158
160
|
</div>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* Copyright 2024 Marimo. All rights reserved. */
|
|
2
2
|
import { type JSX, useEffect, useId, useState } from "react";
|
|
3
|
+
import { useLocale } from "react-aria";
|
|
3
4
|
import { z } from "zod";
|
|
4
5
|
import { NumberField } from "@/components/ui/number-field";
|
|
5
6
|
import { cn } from "@/utils/cn";
|
|
@@ -85,6 +86,7 @@ const SliderComponent = ({
|
|
|
85
86
|
disabled,
|
|
86
87
|
}: SliderProps): JSX.Element => {
|
|
87
88
|
const id = useId();
|
|
89
|
+
const { locale } = useLocale();
|
|
88
90
|
|
|
89
91
|
// Hold internal value
|
|
90
92
|
const [internalValue, setInternalValue] = useState(value);
|
|
@@ -138,7 +140,7 @@ const SliderComponent = ({
|
|
|
138
140
|
/>
|
|
139
141
|
{showValue && (
|
|
140
142
|
<div className="text-xs text-muted-foreground min-w-[16px]">
|
|
141
|
-
{prettyScientificNumber(valueMap(internalValue))}
|
|
143
|
+
{prettyScientificNumber(valueMap(internalValue), { locale })}
|
|
142
144
|
</div>
|
|
143
145
|
)}
|
|
144
146
|
{includeInput && (
|
|
@@ -17,8 +17,10 @@ export type EventHandler = (...args: any[]) => void;
|
|
|
17
17
|
|
|
18
18
|
class ModelManager {
|
|
19
19
|
private models = new Map<string, Deferred<Model<any>>>();
|
|
20
|
-
|
|
21
|
-
constructor(
|
|
20
|
+
private timeout: number;
|
|
21
|
+
constructor(timeout = 10_000) {
|
|
22
|
+
this.timeout = timeout;
|
|
23
|
+
}
|
|
22
24
|
|
|
23
25
|
get(key: string): Promise<Model<any>> {
|
|
24
26
|
let deferred = this.models.get(key);
|
|
@@ -64,16 +66,25 @@ export class Model<T extends Record<string, any>> implements AnyModel<T> {
|
|
|
64
66
|
private ANY_CHANGE_EVENT = "change";
|
|
65
67
|
private dirtyFields;
|
|
66
68
|
public static _modelManager: ModelManager = MODEL_MANAGER;
|
|
69
|
+
private data: T;
|
|
70
|
+
private onChange: (value: Partial<T>) => void;
|
|
71
|
+
private sendToWidget: (req: {
|
|
72
|
+
content?: any;
|
|
73
|
+
buffers?: ArrayBuffer[] | ArrayBufferView[];
|
|
74
|
+
}) => Promise<null | undefined>;
|
|
67
75
|
|
|
68
76
|
constructor(
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
77
|
+
data: T,
|
|
78
|
+
onChange: (value: Partial<T>) => void,
|
|
79
|
+
sendToWidget: (req: {
|
|
72
80
|
content?: any;
|
|
73
81
|
buffers?: ArrayBuffer[] | ArrayBufferView[];
|
|
74
82
|
}) => Promise<null | undefined>,
|
|
75
83
|
initialDirtyFields: Set<keyof T>,
|
|
76
84
|
) {
|
|
85
|
+
this.data = data;
|
|
86
|
+
this.onChange = onChange;
|
|
87
|
+
this.sendToWidget = sendToWidget;
|
|
77
88
|
this.dirtyFields = new Set(initialDirtyFields);
|
|
78
89
|
}
|
|
79
90
|
|
|
@@ -10,11 +10,13 @@ export interface PositionalEdit {
|
|
|
10
10
|
value: unknown;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export
|
|
14
|
-
Insert
|
|
15
|
-
Remove
|
|
16
|
-
Rename
|
|
17
|
-
}
|
|
13
|
+
export const BulkEdit = {
|
|
14
|
+
Insert: "insert",
|
|
15
|
+
Remove: "remove",
|
|
16
|
+
Rename: "rename",
|
|
17
|
+
} as const;
|
|
18
|
+
|
|
19
|
+
type BulkEdit = (typeof BulkEdit)[keyof typeof BulkEdit];
|
|
18
20
|
|
|
19
21
|
export interface RowEdit {
|
|
20
22
|
rowIdx: number;
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import type { Schema } from "compassql/build/src/schema";
|
|
4
4
|
import { BarChartBigIcon } from "lucide-react";
|
|
5
5
|
import React, { useState } from "react";
|
|
6
|
+
import { useDateFormatter, useNumberFormatter } from "react-aria";
|
|
6
7
|
import { Button } from "@/components/ui/button";
|
|
7
8
|
import {
|
|
8
9
|
Select,
|
|
@@ -131,7 +132,9 @@ export const ColumnSummary: React.FC<Props> = ({ schema }) => {
|
|
|
131
132
|
{STAT_KEYS.map((key) => (
|
|
132
133
|
<div key={key} className="flex flex-row gap-2 min-w-[100px]">
|
|
133
134
|
<span className="font-semibold">{key}</span>
|
|
134
|
-
<span>
|
|
135
|
+
<span>
|
|
136
|
+
<FormatStat value={stats?.[key]} />
|
|
137
|
+
</span>
|
|
135
138
|
</div>
|
|
136
139
|
))}
|
|
137
140
|
</div>
|
|
@@ -151,24 +154,28 @@ const STAT_KEYS = [
|
|
|
151
154
|
"stdev",
|
|
152
155
|
];
|
|
153
156
|
|
|
154
|
-
|
|
157
|
+
const FormatStat = ({ value }: { value: unknown }) => {
|
|
158
|
+
// Decimal .2
|
|
159
|
+
const numberFormatter = useNumberFormatter({
|
|
160
|
+
maximumFractionDigits: 2,
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// Just day, month, year
|
|
164
|
+
const dateFormatter = useDateFormatter({
|
|
165
|
+
year: "numeric",
|
|
166
|
+
month: "short",
|
|
167
|
+
day: "numeric",
|
|
168
|
+
});
|
|
169
|
+
|
|
155
170
|
if (typeof value === "number") {
|
|
156
|
-
|
|
157
|
-
return new Intl.NumberFormat("en-US", {
|
|
158
|
-
maximumFractionDigits: 2,
|
|
159
|
-
}).format(value);
|
|
171
|
+
return numberFormatter.format(value);
|
|
160
172
|
}
|
|
161
173
|
if (typeof value === "string") {
|
|
162
174
|
return value;
|
|
163
175
|
}
|
|
164
176
|
if (typeof value === "object" && value instanceof Date) {
|
|
165
|
-
|
|
166
|
-
return new Intl.DateTimeFormat("en-US", {
|
|
167
|
-
year: "numeric",
|
|
168
|
-
month: "short",
|
|
169
|
-
day: "numeric",
|
|
170
|
-
}).format(value);
|
|
177
|
+
return dateFormatter.format(value);
|
|
171
178
|
}
|
|
172
179
|
|
|
173
180
|
return String(value);
|
|
174
|
-
}
|
|
181
|
+
};
|
|
@@ -57,11 +57,13 @@ export class EventBuffer<T> {
|
|
|
57
57
|
private buffer: T[] = [];
|
|
58
58
|
private isBlocked = false;
|
|
59
59
|
private timeout: number | null = null;
|
|
60
|
+
private processEvents: () => void;
|
|
61
|
+
private blockDuration: number;
|
|
60
62
|
|
|
61
|
-
constructor(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
constructor(processEvents: () => void, blockDuration = 200) {
|
|
64
|
+
this.processEvents = processEvents;
|
|
65
|
+
this.blockDuration = blockDuration;
|
|
66
|
+
}
|
|
65
67
|
|
|
66
68
|
add(event: T) {
|
|
67
69
|
this.buffer.push(event);
|