flexdesk 0.16.1 → 0.17.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/CHANGELOG.md +112 -0
- package/LICENSE +0 -0
- package/README.md +2435 -42
- package/THIRD_PARTY_LICENSES.md +0 -0
- package/canvas.js +18 -0
- package/charts.js +21 -0
- package/core.js +14 -0
- package/css/base.css +1834 -75
- package/css/flexdesk.css +3165 -159
- package/css/overrides.css +16 -0
- package/css/reset.css +0 -0
- package/css/tokens.css +0 -0
- package/dist/canvas.js +6 -239
- package/dist/canvas.js.map +3 -3
- package/dist/charts.js +707 -18
- package/dist/charts.js.map +4 -4
- package/dist/chunk-3PHCPZHT.js +0 -0
- package/dist/chunk-3PHCPZHT.js.map +0 -0
- package/dist/chunk-4A7SJC3T.js +503 -0
- package/dist/chunk-4A7SJC3T.js.map +7 -0
- package/dist/{chunk-6JSOVNID.js → chunk-CVMCZZX2.js} +635 -10
- package/dist/chunk-CVMCZZX2.js.map +7 -0
- package/dist/chunk-D3COLZZ2.js +1311 -0
- package/dist/chunk-D3COLZZ2.js.map +7 -0
- package/dist/chunk-FOOS3T5L.js +0 -0
- package/dist/chunk-FOOS3T5L.js.map +0 -0
- package/dist/{chunk-SN4LSOAR.js → chunk-GOQLR5WR.js} +452 -2
- package/dist/chunk-GOQLR5WR.js.map +7 -0
- package/dist/{chunk-FL5KFNQH.js → chunk-HNU6MM2B.js} +52 -17
- package/dist/chunk-HNU6MM2B.js.map +7 -0
- package/dist/chunk-HYJYYEPK.js +375 -0
- package/dist/chunk-HYJYYEPK.js.map +7 -0
- package/dist/chunk-JKZLNKG6.js +2041 -0
- package/dist/chunk-JKZLNKG6.js.map +7 -0
- package/dist/chunk-JYWURG5T.js +0 -0
- package/dist/chunk-JYWURG5T.js.map +0 -0
- package/dist/{chunk-FBGP5KN5.js → chunk-LWV23YVE.js} +5073 -532
- package/dist/chunk-LWV23YVE.js.map +7 -0
- package/dist/chunk-N7HC3ADV.js +30 -0
- package/dist/chunk-N7HC3ADV.js.map +7 -0
- package/dist/{chunk-RQM5Z2MU.js → chunk-RMTRWWEO.js} +633 -311
- package/dist/chunk-RMTRWWEO.js.map +7 -0
- package/dist/{chunk-NLTOY57M.js → chunk-UURZ37SD.js} +28 -48
- package/dist/chunk-UURZ37SD.js.map +7 -0
- package/dist/{chunk-B5Q53LAJ.js → chunk-UWO43BZU.js} +2 -2
- package/dist/chunk-UWO43BZU.js.map +7 -0
- package/dist/chunk-WVFGV5FT.js +0 -0
- package/dist/chunk-WVFGV5FT.js.map +0 -0
- package/dist/core.js +170 -118
- package/dist/core.js.map +2 -2
- package/dist/editor.js +156 -70
- package/dist/editor.js.map +2 -2
- package/dist/flexdesk.css +3165 -159
- package/dist/flow.js +2789 -306
- package/dist/flow.js.map +4 -4
- package/dist/host.js +109 -3
- package/dist/host.js.map +3 -3
- package/dist/reset.css +0 -0
- package/dist/tile_registry-6WZPTUZV.js +0 -0
- package/dist/tile_registry-6WZPTUZV.js.map +0 -0
- package/dist/tiles.js +657 -234
- package/dist/tiles.js.map +4 -4
- package/dist/tokens.css +0 -0
- package/dist/widgets.js +3290 -2124
- package/dist/widgets.js.map +4 -4
- package/dist/wm.js +1227 -347
- package/dist/wm.js.map +4 -4
- package/editor.js +20 -0
- package/flow.js +21 -0
- package/host.js +14 -0
- package/package.json +12 -1
- package/src/canvas/canvas_adapter.js +5 -0
- package/src/canvas/edge_router.js +0 -0
- package/src/canvas/graph/connector_router.js +0 -0
- package/src/canvas/graph/history_service.js +0 -0
- package/src/canvas/graph/node_platform.js +0 -0
- package/src/canvas/index.js +1 -0
- package/src/canvas/layered_layout.js +244 -0
- package/src/charts/chart_types.js +0 -0
- package/src/charts/data_series_plot_window.js +0 -0
- package/src/charts/downsample.js +2 -2
- package/src/charts/plot_config.js +53 -29
- package/src/charts/plot_config_editor.js +627 -0
- package/src/charts/plot_grid.js +449 -0
- package/src/charts/plot_popout_window.js +103 -317
- package/src/charts/plotly_wrapper.js +22 -2
- package/src/charts/series_transforms.js +206 -0
- package/src/core/event_bus.js +0 -0
- package/src/core/logging.js +1 -1
- package/src/core/settings.js +105 -19
- package/src/core/state_guard.js +93 -51
- package/src/core/state_guard_config.js +85 -74
- package/src/core/state_machine.js +0 -0
- package/src/editor/editor_pane.js +3 -3
- package/src/editor/monaco_editor_factory.js +124 -60
- package/src/editor/monaco_loader.js +103 -28
- package/src/editor/notebook_search.js +0 -0
- package/src/editor/notebook_tab_bar.js +38 -52
- package/src/editor/notebook_undo_manager.js +0 -0
- package/src/editor/split_pane_container.js +6 -3
- package/src/flow/canvas_editor/connections.js +90 -0
- package/src/flow/canvas_editor/editor.js +2351 -153
- package/src/flow/canvas_editor/index.js +2 -1
- package/src/flow/canvas_editor/layout.js +119 -12
- package/src/flow/canvas_editor/strings.js +80 -0
- package/src/flow/kit/always_before.js +0 -0
- package/src/flow/kit/catalogue.js +97 -3
- package/src/flow/kit/chip_input.js +0 -0
- package/src/flow/kit/dom.js +0 -0
- package/src/flow/kit/findings.js +0 -0
- package/src/flow/kit/history.js +0 -0
- package/src/flow/kit/index.js +3 -3
- package/src/flow/kit/keys.js +7 -1
- package/src/flow/kit/logic_graph.js +97 -15
- package/src/flow/kit/references.js +0 -0
- package/src/flow/kit/settings_panel.js +0 -0
- package/src/flow/kit/settings_schema.js +2 -1
- package/src/flow/kit/step_picker.js +0 -0
- package/src/flow/kit/strings.js +3 -1
- package/src/flow/kit/value_picker.js +0 -0
- package/src/flow/kit/widgets.js +86 -109
- package/src/flow/lanes/editor.js +136 -14
- package/src/flow/lanes/index.js +3 -1
- package/src/flow/lanes/layout.js +0 -0
- package/src/flow/lanes/pipeline.js +44 -2
- package/src/flow/lanes/preview.js +0 -0
- package/src/flow/lanes/strings.js +1 -0
- package/src/flow/outline/editor.js +0 -0
- package/src/flow/outline/index.js +0 -0
- package/src/flow/outline/mapping.js +0 -0
- package/src/flow/outline/menu.js +0 -0
- package/src/flow/outline/operations.js +0 -0
- package/src/flow/outline/recognise.js +0 -0
- package/src/flow/outline/strings.js +0 -0
- package/src/help/help_modal.js +168 -42
- package/src/help/help_registry.js +12 -1
- package/src/host/external_links.js +0 -0
- package/src/host/file_picker.js +117 -0
- package/src/host/host.js +10 -2
- package/src/host/photino_host.js +9 -0
- package/src/host/photino_host.md +21 -3
- package/src/host/pywebview_host.js +10 -0
- package/src/tiles/chart_tiles.js +53 -245
- package/src/tiles/config_schema.js +8 -0
- package/src/tiles/figure_tile.js +322 -0
- package/src/tiles/layout_persistence.js +3 -3
- package/src/tiles/list_tiles.js +7 -7
- package/src/tiles/scope_picker.js +0 -0
- package/src/tiles/tile_base.js +1 -1
- package/src/tiles/tile_config_form.js +442 -0
- package/src/tiles/tile_grid.js +2 -0
- package/src/tiles/tile_registry.js +0 -0
- package/src/tiling/bottom_panel_tabs.js +67 -99
- package/src/tiling/command_palette.js +64 -19
- package/src/tiling/content_registry.js +4 -1
- package/src/tiling/desktops.js +11 -1
- package/src/tiling/entity_sources.js +0 -0
- package/src/tiling/keymap.js +403 -128
- package/src/tiling/kind_taxonomy.js +78 -0
- package/src/tiling/landing_table.js +0 -0
- package/src/tiling/loading_overlay.js +0 -0
- package/src/tiling/nav_panel.js +59 -10
- package/src/tiling/page_factory.js +5 -5
- package/src/tiling/panel_availability.js +49 -0
- package/src/tiling/panel_keys.js +0 -0
- package/src/tiling/shell.js +114 -12
- package/src/tiling/tab_strip.js +0 -0
- package/src/tiling/tile_breadcrumb.js +0 -0
- package/src/tiling/tile_renderer.js +105 -9
- package/src/tiling/tile_tab_menu.js +0 -0
- package/src/tiling/tile_tree.js +0 -0
- package/src/tiling/wm.js +562 -30
- package/src/tiling/zoom.js +0 -0
- package/src/ui/base/component_base.js +0 -0
- package/src/ui/base/controller_base.js +0 -0
- package/src/ui/base/page_base.js +0 -0
- package/src/ui/components/about_dialog.js +0 -0
- package/src/ui/components/action_dropdown.js +30 -2
- package/src/ui/components/alert_log.js +0 -0
- package/src/ui/components/alerts_pane.js +0 -0
- package/src/ui/components/attribute_list_editor.js +0 -0
- package/src/ui/components/autocomplete_field.js +0 -0
- package/src/ui/components/cell_editor_host.js +293 -0
- package/src/ui/components/cell_editors.js +480 -0
- package/src/ui/components/cell_grid.js +761 -0
- package/src/ui/components/cell_paste.js +243 -0
- package/src/ui/components/cell_selection.js +220 -0
- package/src/ui/components/choice_controls.js +351 -0
- package/src/ui/components/color_field.js +211 -0
- package/src/ui/components/color_list_field.js +210 -0
- package/src/ui/components/column_fit.js +0 -0
- package/src/ui/components/column_groups.js +314 -0
- package/src/ui/components/column_order.js +142 -0
- package/src/ui/components/computing_status_window.js +98 -25
- package/src/ui/components/confirm_dialog.js +81 -38
- package/src/ui/components/context_menu.js +2 -2
- package/src/ui/components/conversation_view.js +440 -0
- package/src/ui/components/crosstab.js +289 -0
- package/src/ui/components/data_table.js +696 -71
- package/src/ui/components/detail_header.js +22 -11
- package/src/ui/components/document_outline.js +397 -0
- package/src/ui/components/drag_reorder.js +0 -0
- package/src/ui/components/fact_table.js +0 -0
- package/src/ui/components/fill_handle.js +322 -0
- package/src/ui/components/gallery_picker.js +19 -8
- package/src/ui/components/header_actions.js +55 -0
- package/src/ui/components/inline_renamer.js +0 -0
- package/src/ui/components/job_progress.js +51 -0
- package/src/ui/components/managed_window.js +27 -4
- package/src/ui/components/menu_bar.js +745 -0
- package/src/ui/components/modal.js +449 -75
- package/src/ui/components/notification_history.js +3 -3
- package/src/ui/components/progress.js +483 -0
- package/src/ui/components/record_list.js +0 -0
- package/src/ui/components/row_activation.js +0 -0
- package/src/ui/components/section_stack.js +298 -0
- package/src/ui/components/series.js +173 -0
- package/src/ui/components/settings_view.js +634 -0
- package/src/ui/components/slide_out_panel.js +43 -6
- package/src/ui/components/slider_field.js +275 -18
- package/src/ui/components/sortable_list.js +0 -0
- package/src/ui/components/staged_edits.js +361 -0
- package/src/ui/components/staged_history.js +172 -0
- package/src/ui/components/table_filter_chips.js +0 -0
- package/src/ui/components/table_state_store.js +0 -0
- package/src/ui/components/table_structure.js +182 -0
- package/src/ui/components/table_values.js +0 -0
- package/src/ui/components/tabs.js +497 -0
- package/src/ui/components/tag_input.js +215 -0
- package/src/ui/components/toast.js +25 -8
- package/src/ui/components/toggle_field.js +175 -0
- package/src/ui/components/tree_view.js +55 -34
- package/src/ui/components/tsv.js +74 -0
- package/src/ui/components/window_taskbar.js +0 -0
- package/src/ui/components/wizard.js +563 -0
- package/src/ui/controllers/window_chrome_controller.js +2 -2
- package/src/ui/notification_center.js +375 -119
- package/src/ui/utils/auto_scrollbars.js +0 -0
- package/src/ui/utils/markdown.js +709 -0
- package/src/ui/utils/native_context_menu.js +0 -0
- package/src/ui/utils/overlay_scrollbar.js +16 -12
- package/src/ui/utils/raf_resize_observer.js +0 -0
- package/src/ui/utils/scroll_lock.js +0 -0
- package/src/ui/utils/strip_scroll.js +55 -0
- package/src/ui/utils/tooltip_service.js +0 -8
- package/tiles.js +27 -0
- package/widgets.js +69 -0
- package/wm.js +28 -0
- package/dist/chunk-6JSOVNID.js.map +0 -7
- package/dist/chunk-B5Q53LAJ.js.map +0 -7
- package/dist/chunk-FBGP5KN5.js.map +0 -7
- package/dist/chunk-FL5KFNQH.js.map +0 -7
- package/dist/chunk-NLTOY57M.js.map +0 -7
- package/dist/chunk-NNFTETK3.js +0 -633
- package/dist/chunk-NNFTETK3.js.map +0 -7
- package/dist/chunk-O5OHMWBB.js +0 -284
- package/dist/chunk-O5OHMWBB.js.map +0 -7
- package/dist/chunk-RQM5Z2MU.js.map +0 -7
- package/dist/chunk-SN4LSOAR.js.map +0 -7
- package/dist/chunk-W6FBJLPW.js +0 -367
- package/dist/chunk-W6FBJLPW.js.map +0 -7
- package/src/ui/controllers/panel_state_machine.js +0 -634
package/dist/charts.js
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createTabs
|
|
3
|
+
} from "./chunk-HYJYYEPK.js";
|
|
1
4
|
import {
|
|
2
5
|
ALL_CHART_TYPES,
|
|
3
6
|
CHART_TYPES_2D,
|
|
4
7
|
CHART_TYPES_3D,
|
|
5
8
|
COLOR_PALETTE,
|
|
6
9
|
DARK_THEME_LAYOUT,
|
|
10
|
+
DEFAULT_CHART_EXPORT_FILENAME,
|
|
7
11
|
DEFAULT_CONFIG,
|
|
12
|
+
PLOT_GRID_THEME,
|
|
8
13
|
PLOT_LAYOUTS,
|
|
9
14
|
PlotPopoutWindow,
|
|
10
15
|
applyNanHandling,
|
|
@@ -21,35 +26,49 @@ import {
|
|
|
21
26
|
buildYAxisLayout,
|
|
22
27
|
createChart,
|
|
23
28
|
createChartDeferred,
|
|
29
|
+
deriveSeriesTraces,
|
|
24
30
|
ensurePlotly,
|
|
25
31
|
extendTraces,
|
|
32
|
+
getChartExportFilename,
|
|
26
33
|
getSeriesColor,
|
|
27
34
|
hasPlot,
|
|
28
35
|
hexToRgba,
|
|
29
36
|
is3DChart,
|
|
30
37
|
lttb,
|
|
38
|
+
makeDefaultSeries,
|
|
31
39
|
makeDefaultSubplot,
|
|
40
|
+
makeDefaultYAxis,
|
|
32
41
|
migrateData,
|
|
33
42
|
migrateSingleChart,
|
|
34
43
|
openPlotPopoutWindow,
|
|
35
44
|
openRawTracesWindow,
|
|
36
45
|
purge,
|
|
37
46
|
relayout,
|
|
47
|
+
renderPlotGrid,
|
|
48
|
+
renderSeriesTransformControls,
|
|
38
49
|
resize,
|
|
50
|
+
resolveSeriesData,
|
|
39
51
|
restyle,
|
|
52
|
+
setChartExportFilename,
|
|
40
53
|
setPlotlySource,
|
|
41
54
|
strideDownsampleShared,
|
|
42
55
|
strideIndices,
|
|
43
56
|
uid,
|
|
44
57
|
updateChart
|
|
45
|
-
} from "./chunk-
|
|
58
|
+
} from "./chunk-RMTRWWEO.js";
|
|
46
59
|
import {
|
|
60
|
+
AutocompleteField,
|
|
47
61
|
createRafResizeObserver
|
|
48
|
-
} from "./chunk-
|
|
62
|
+
} from "./chunk-LWV23YVE.js";
|
|
49
63
|
import {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
64
|
+
ColorField,
|
|
65
|
+
ManagedWindow,
|
|
66
|
+
ToggleField,
|
|
67
|
+
selectBox
|
|
68
|
+
} from "./chunk-CVMCZZX2.js";
|
|
69
|
+
import "./chunk-HNU6MM2B.js";
|
|
70
|
+
import "./chunk-N7HC3ADV.js";
|
|
71
|
+
import "./chunk-WVFGV5FT.js";
|
|
53
72
|
import "./chunk-JYWURG5T.js";
|
|
54
73
|
|
|
55
74
|
// src/charts/data_series_plot_window.js
|
|
@@ -243,20 +262,20 @@ var DataSeriesPlotWindow = class _DataSeriesPlotWindow {
|
|
|
243
262
|
return section;
|
|
244
263
|
}
|
|
245
264
|
#buildXColumnSelect() {
|
|
246
|
-
const
|
|
265
|
+
const select2 = document.createElement("select");
|
|
247
266
|
for (const header of this.headers) {
|
|
248
267
|
if (!this._numericColumns.has(header)) continue;
|
|
249
268
|
const opt = document.createElement("option");
|
|
250
269
|
opt.value = header;
|
|
251
270
|
opt.textContent = this.labelHeader(header);
|
|
252
271
|
if (header === this._config.xColumn) opt.selected = true;
|
|
253
|
-
|
|
272
|
+
select2.appendChild(opt);
|
|
254
273
|
}
|
|
255
|
-
|
|
256
|
-
this._config.xColumn =
|
|
274
|
+
select2.addEventListener("change", () => {
|
|
275
|
+
this._config.xColumn = select2.value;
|
|
257
276
|
this.#onConfigChanged();
|
|
258
277
|
});
|
|
259
|
-
return
|
|
278
|
+
return select2;
|
|
260
279
|
}
|
|
261
280
|
#populateYColumns() {
|
|
262
281
|
const list = this._yColumnsList;
|
|
@@ -313,16 +332,16 @@ var DataSeriesPlotWindow = class _DataSeriesPlotWindow {
|
|
|
313
332
|
return getSeriesColor(index);
|
|
314
333
|
}
|
|
315
334
|
#buildSelect(options, value, onChange) {
|
|
316
|
-
const
|
|
335
|
+
const select2 = document.createElement("select");
|
|
317
336
|
for (const opt of options) {
|
|
318
|
-
const
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
if (opt === value)
|
|
322
|
-
|
|
337
|
+
const el2 = document.createElement("option");
|
|
338
|
+
el2.value = opt;
|
|
339
|
+
el2.textContent = opt.charAt(0).toUpperCase() + opt.slice(1);
|
|
340
|
+
if (opt === value) el2.selected = true;
|
|
341
|
+
select2.appendChild(el2);
|
|
323
342
|
}
|
|
324
|
-
|
|
325
|
-
return
|
|
343
|
+
select2.addEventListener("change", () => onChange(select2.value));
|
|
344
|
+
return select2;
|
|
326
345
|
}
|
|
327
346
|
#buildCheckbox(label, checked, onChange) {
|
|
328
347
|
const wrapper = document.createElement("label");
|
|
@@ -663,13 +682,674 @@ function closeDataSeriesPlotWindow(seriesId) {
|
|
|
663
682
|
_openPlotWindows.delete(seriesId);
|
|
664
683
|
}
|
|
665
684
|
}
|
|
685
|
+
|
|
686
|
+
// src/charts/plot_config_editor.js
|
|
687
|
+
var CHART_TYPE_OPTIONS = Object.freeze([
|
|
688
|
+
{ group: "2D", options: CHART_TYPES_2D },
|
|
689
|
+
{ group: "3D", options: CHART_TYPES_3D }
|
|
690
|
+
]);
|
|
691
|
+
var SCALES = Object.freeze([{ value: "linear", label: "Linear" }, { value: "log", label: "Log" }]);
|
|
692
|
+
var POSITIONS = Object.freeze([{ value: "left", label: "Left" }, { value: "right", label: "Right" }]);
|
|
693
|
+
var DASHES = Object.freeze([
|
|
694
|
+
{ value: "solid", label: "Solid" },
|
|
695
|
+
{ value: "dashed", label: "Dashed" },
|
|
696
|
+
{ value: "dotted", label: "Dotted" }
|
|
697
|
+
]);
|
|
698
|
+
var LEGEND_POSITIONS2 = Object.freeze([
|
|
699
|
+
{ value: "hidden", label: "Hidden" },
|
|
700
|
+
{ value: "top", label: "Top" },
|
|
701
|
+
{ value: "bottom", label: "Bottom" },
|
|
702
|
+
{ value: "left", label: "Left" },
|
|
703
|
+
{ value: "right", label: "Right" }
|
|
704
|
+
]);
|
|
705
|
+
var INTERPOLATIONS = Object.freeze([
|
|
706
|
+
{ value: "linear", label: "Linear" },
|
|
707
|
+
{ value: "step", label: "Step" },
|
|
708
|
+
{ value: "cubic", label: "Cubic spline" }
|
|
709
|
+
]);
|
|
710
|
+
var NAN_STRATEGIES = Object.freeze([
|
|
711
|
+
{ value: "gap", label: "Gap" },
|
|
712
|
+
{ value: "zero", label: "Zero" },
|
|
713
|
+
{ value: "hold", label: "Hold last value" },
|
|
714
|
+
{ value: "interpolate", label: "Interpolate" }
|
|
715
|
+
]);
|
|
716
|
+
var X_SOURCES = Object.freeze([{ value: "time", label: "Time" }, { value: "series", label: "Series" }]);
|
|
717
|
+
var OVERLAY_MODES = Object.freeze([
|
|
718
|
+
{ value: "markers+lines", label: "Markers and lines" },
|
|
719
|
+
{ value: "markers", label: "Markers" },
|
|
720
|
+
{ value: "lines", label: "Lines" }
|
|
721
|
+
]);
|
|
722
|
+
function el(tag, className, text) {
|
|
723
|
+
const node = document.createElement(tag);
|
|
724
|
+
if (className) node.className = className;
|
|
725
|
+
if (text != null) node.textContent = String(text);
|
|
726
|
+
return node;
|
|
727
|
+
}
|
|
728
|
+
function button(text, onClick, { title = null, className = "twm-btn twm-plot-config__add" } = {}) {
|
|
729
|
+
const b = el("button", className, text);
|
|
730
|
+
b.type = "button";
|
|
731
|
+
if (title) b.title = title;
|
|
732
|
+
b.addEventListener("click", () => {
|
|
733
|
+
if (!b.disabled) onClick();
|
|
734
|
+
});
|
|
735
|
+
return b;
|
|
736
|
+
}
|
|
737
|
+
function removeButton(title, onClick) {
|
|
738
|
+
const b = button("", onClick, { title, className: "twm-btn-icon twm-plot-config__remove" });
|
|
739
|
+
b.setAttribute("aria-label", title);
|
|
740
|
+
b.appendChild(el("span", "material-symbols-outlined", "close"));
|
|
741
|
+
return b;
|
|
742
|
+
}
|
|
743
|
+
function field(label, control) {
|
|
744
|
+
const single = control.matches("input, select, textarea");
|
|
745
|
+
const row = el(single ? "label" : "div", "twm-plot-config__field");
|
|
746
|
+
row.append(el("span", "twm-plot-config__label", label), control);
|
|
747
|
+
return row;
|
|
748
|
+
}
|
|
749
|
+
function textInput(value, onSet, { placeholder = "", label = null } = {}) {
|
|
750
|
+
const input = el("input", "twm-plot-config__input");
|
|
751
|
+
input.type = "text";
|
|
752
|
+
input.value = value ?? "";
|
|
753
|
+
input.placeholder = placeholder;
|
|
754
|
+
if (label) input.setAttribute("aria-label", label);
|
|
755
|
+
input.addEventListener("input", () => onSet(input.value));
|
|
756
|
+
return input;
|
|
757
|
+
}
|
|
758
|
+
function numberInput(value, onSet, { placeholder = "Auto", label = null, min = null, max = null, step = "any" } = {}) {
|
|
759
|
+
const input = el("input", "twm-plot-config__input twm-plot-config__input--number");
|
|
760
|
+
input.type = "number";
|
|
761
|
+
input.step = String(step);
|
|
762
|
+
if (min != null) input.min = String(min);
|
|
763
|
+
if (max != null) input.max = String(max);
|
|
764
|
+
input.value = value == null ? "" : String(value);
|
|
765
|
+
input.placeholder = placeholder;
|
|
766
|
+
if (label) input.setAttribute("aria-label", label);
|
|
767
|
+
input.addEventListener("change", () => {
|
|
768
|
+
const n = parseFloat(input.value);
|
|
769
|
+
onSet(Number.isFinite(n) ? n : null);
|
|
770
|
+
});
|
|
771
|
+
return input;
|
|
772
|
+
}
|
|
773
|
+
function select(options, value, onPick, { label = null, empty = null } = {}) {
|
|
774
|
+
const s = selectBox(options, value, {
|
|
775
|
+
label: label ?? "",
|
|
776
|
+
empty,
|
|
777
|
+
className: "twm-plot-config__input twm-plot-config__input--select"
|
|
778
|
+
});
|
|
779
|
+
s.box.addEventListener("change", () => onPick(s.valueOf()));
|
|
780
|
+
return s.box;
|
|
781
|
+
}
|
|
782
|
+
function makeZAxis() {
|
|
783
|
+
return { label: "", scale: "linear", min: null, max: null, step: null, variable: "" };
|
|
784
|
+
}
|
|
785
|
+
var editorCount = 0;
|
|
786
|
+
function renderPlotConfigEditor(container, config, options = {}) {
|
|
787
|
+
const opts = {
|
|
788
|
+
onChange: null,
|
|
789
|
+
seriesProvider: null,
|
|
790
|
+
overlaySources: null,
|
|
791
|
+
seriesTransforms: null,
|
|
792
|
+
extraSeriesFields: null,
|
|
793
|
+
logger: null,
|
|
794
|
+
...options
|
|
795
|
+
};
|
|
796
|
+
const editorId = `twm-plot-config-${++editorCount}`;
|
|
797
|
+
let fieldCount = 0;
|
|
798
|
+
let active = 0;
|
|
799
|
+
let disposed = false;
|
|
800
|
+
let bodyDisposers = [];
|
|
801
|
+
let generation = 0;
|
|
802
|
+
const notify = () => {
|
|
803
|
+
if (!disposed) opts.onChange?.(config);
|
|
804
|
+
};
|
|
805
|
+
const track = (fn) => {
|
|
806
|
+
bodyDisposers.push(fn);
|
|
807
|
+
};
|
|
808
|
+
const template = () => PLOT_LAYOUTS[config.layout] || PLOT_LAYOUTS["1x1"];
|
|
809
|
+
const slots = () => template().slots;
|
|
810
|
+
function ensureSubplots() {
|
|
811
|
+
if (!Array.isArray(config.subplots)) config.subplots = [];
|
|
812
|
+
while (config.subplots.length < slots()) {
|
|
813
|
+
config.subplots.push(makeDefaultSubplot(config.subplots.length));
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
const root = el("div", "twm-plot-config");
|
|
817
|
+
const layoutButtons = /* @__PURE__ */ new Map();
|
|
818
|
+
const picker = el("div", "twm-plot-config__layouts");
|
|
819
|
+
picker.setAttribute("role", "group");
|
|
820
|
+
picker.setAttribute("aria-label", "Layout");
|
|
821
|
+
for (const [key, tmpl] of Object.entries(PLOT_LAYOUTS)) {
|
|
822
|
+
const b = button(tmpl.label, () => setLayout(key), { className: "twm-btn twm-plot-config__layout" });
|
|
823
|
+
layoutButtons.set(key, b);
|
|
824
|
+
picker.appendChild(b);
|
|
825
|
+
}
|
|
826
|
+
const syncLayoutButtons = () => {
|
|
827
|
+
for (const [key, b] of layoutButtons) {
|
|
828
|
+
const on = key === config.layout;
|
|
829
|
+
b.setAttribute("aria-pressed", on ? "true" : "false");
|
|
830
|
+
b.classList.toggle("twm-plot-config__layout--on", on);
|
|
831
|
+
}
|
|
832
|
+
};
|
|
833
|
+
root.appendChild(section("Layout", picker));
|
|
834
|
+
const tabsHost = el("div", "twm-plot-config__tabs");
|
|
835
|
+
const body = el("div", "twm-plot-config__body");
|
|
836
|
+
root.append(tabsHost, body);
|
|
837
|
+
container.appendChild(root);
|
|
838
|
+
ensureSubplots();
|
|
839
|
+
const tabItems = () => Array.from({ length: slots() }, (_, i) => ({ id: i, label: `Subplot ${i + 1}` }));
|
|
840
|
+
const tabs = createTabs(tabsHost, {
|
|
841
|
+
items: tabItems(),
|
|
842
|
+
active: 0,
|
|
843
|
+
compact: true,
|
|
844
|
+
label: "Subplots",
|
|
845
|
+
onSelect: (id) => {
|
|
846
|
+
if (id == null || id === active) return;
|
|
847
|
+
active = id;
|
|
848
|
+
renderBody();
|
|
849
|
+
}
|
|
850
|
+
});
|
|
851
|
+
const syncChrome = () => {
|
|
852
|
+
syncLayoutButtons();
|
|
853
|
+
tabs.update(tabItems(), active);
|
|
854
|
+
tabsHost.hidden = slots() < 2;
|
|
855
|
+
};
|
|
856
|
+
function setLayout(key) {
|
|
857
|
+
if (disposed || key === config.layout) return;
|
|
858
|
+
config.layout = key;
|
|
859
|
+
ensureSubplots();
|
|
860
|
+
active = Math.min(active, slots() - 1);
|
|
861
|
+
syncChrome();
|
|
862
|
+
renderBody();
|
|
863
|
+
notify();
|
|
864
|
+
}
|
|
865
|
+
function section(title, ...children) {
|
|
866
|
+
const s = el("section", "twm-plot-config__section");
|
|
867
|
+
s.appendChild(el("h4", "twm-plot-config__heading", title));
|
|
868
|
+
for (const child of children) if (child) s.appendChild(child);
|
|
869
|
+
return s;
|
|
870
|
+
}
|
|
871
|
+
function disposeBody() {
|
|
872
|
+
generation += 1;
|
|
873
|
+
const disposers = bodyDisposers;
|
|
874
|
+
bodyDisposers = [];
|
|
875
|
+
for (const fn of disposers) {
|
|
876
|
+
try {
|
|
877
|
+
fn();
|
|
878
|
+
} catch (error) {
|
|
879
|
+
opts.logger?.warn?.("[plot config editor] a field failed to dispose", error);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
function seriesPicker(target, key, role, sp, placeholder) {
|
|
884
|
+
let alive = true;
|
|
885
|
+
const ac = new AutocompleteField({
|
|
886
|
+
fieldId: `${editorId}-field-${++fieldCount}`,
|
|
887
|
+
field: { placeholder, value: target[key] ?? "" },
|
|
888
|
+
skipNamespacePrefix: true,
|
|
889
|
+
logger: opts.logger,
|
|
890
|
+
provider: ({ value }) => opts.seriesProvider ? opts.seriesProvider({ value, role, subplot: sp }) : [],
|
|
891
|
+
onChange: (value) => {
|
|
892
|
+
if (!alive || (target[key] ?? "") === value) return;
|
|
893
|
+
target[key] = value;
|
|
894
|
+
notify();
|
|
895
|
+
}
|
|
896
|
+
});
|
|
897
|
+
track(() => {
|
|
898
|
+
alive = false;
|
|
899
|
+
ac.dispose();
|
|
900
|
+
});
|
|
901
|
+
const node = ac.render();
|
|
902
|
+
node.classList.add("twm-plot-config__picker");
|
|
903
|
+
return node;
|
|
904
|
+
}
|
|
905
|
+
function colorField(value, onSet, label) {
|
|
906
|
+
const f = new ColorField({
|
|
907
|
+
value: value ?? "",
|
|
908
|
+
label,
|
|
909
|
+
onChange: (v) => {
|
|
910
|
+
if (v !== null) {
|
|
911
|
+
onSet(v || null);
|
|
912
|
+
notify();
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
});
|
|
916
|
+
track(() => f.dispose());
|
|
917
|
+
return f.render();
|
|
918
|
+
}
|
|
919
|
+
function toggle(label, checked, onSet) {
|
|
920
|
+
const t = new ToggleField({ label, checked, compact: true, onChange: (on) => {
|
|
921
|
+
onSet(on);
|
|
922
|
+
notify();
|
|
923
|
+
} });
|
|
924
|
+
track(() => t.dispose());
|
|
925
|
+
return t.render();
|
|
926
|
+
}
|
|
927
|
+
function listEditor(sp, key, { addLabel, makeItem, fields }) {
|
|
928
|
+
if (!Array.isArray(sp[key])) sp[key] = [];
|
|
929
|
+
const items = sp[key];
|
|
930
|
+
const wrap = el("div", "twm-plot-config__list");
|
|
931
|
+
items.forEach((item, i) => {
|
|
932
|
+
const row = el("div", "twm-plot-config__row");
|
|
933
|
+
row.append(...fields(item), removeButton("Remove", () => {
|
|
934
|
+
items.splice(i, 1);
|
|
935
|
+
renderBody();
|
|
936
|
+
notify();
|
|
937
|
+
}));
|
|
938
|
+
wrap.appendChild(row);
|
|
939
|
+
});
|
|
940
|
+
wrap.appendChild(button(addLabel, () => {
|
|
941
|
+
items.push(makeItem());
|
|
942
|
+
renderBody();
|
|
943
|
+
notify();
|
|
944
|
+
}));
|
|
945
|
+
return wrap;
|
|
946
|
+
}
|
|
947
|
+
function normalise(sp) {
|
|
948
|
+
if (!sp.xAxis) sp.xAxis = { useTime: true, variable: "" };
|
|
949
|
+
if (!Array.isArray(sp.yAxes) || !sp.yAxes.length) sp.yAxes = [makeDefaultYAxis(0)];
|
|
950
|
+
for (const axis of sp.yAxes) if (!Array.isArray(axis.series)) axis.series = [];
|
|
951
|
+
if (is3DChart(sp.chartType) && !sp.zAxis) sp.zAxis = makeZAxis();
|
|
952
|
+
}
|
|
953
|
+
function renderBody() {
|
|
954
|
+
if (disposed) return;
|
|
955
|
+
disposeBody();
|
|
956
|
+
body.replaceChildren();
|
|
957
|
+
const sp = config.subplots[active];
|
|
958
|
+
if (!sp) return;
|
|
959
|
+
normalise(sp);
|
|
960
|
+
const is3D = is3DChart(sp.chartType);
|
|
961
|
+
let sources = null;
|
|
962
|
+
const overlaySources = () => sources ??= Promise.resolve().then(() => opts.overlaySources());
|
|
963
|
+
body.append(
|
|
964
|
+
section("Subplot", field("Title", textInput(
|
|
965
|
+
sp.displayName,
|
|
966
|
+
(v) => {
|
|
967
|
+
sp.displayName = v;
|
|
968
|
+
notify();
|
|
969
|
+
},
|
|
970
|
+
{ placeholder: "Untitled" }
|
|
971
|
+
))),
|
|
972
|
+
section("Display", ...displayFields(sp)),
|
|
973
|
+
section("X axis", ...xAxisFields(sp)),
|
|
974
|
+
section("Y axes", yAxesEditor(sp, is3D, overlaySources)),
|
|
975
|
+
is3D ? section("Z axis", ...zAxisFields(sp)) : null,
|
|
976
|
+
section("Reference lines", referenceLinesEditor(sp)),
|
|
977
|
+
section("Shaded bands", shadedBandsEditor(sp)),
|
|
978
|
+
transformsSection(sp)
|
|
979
|
+
);
|
|
980
|
+
}
|
|
981
|
+
function displayFields(sp) {
|
|
982
|
+
return [
|
|
983
|
+
field("Chart type", select(CHART_TYPE_OPTIONS, sp.chartType ?? "line", (v) => {
|
|
984
|
+
const was3D = is3DChart(sp.chartType);
|
|
985
|
+
sp.chartType = v;
|
|
986
|
+
const now3D = is3DChart(v);
|
|
987
|
+
if (now3D) sp.zAxis ??= makeZAxis();
|
|
988
|
+
else sp.zAxis = null;
|
|
989
|
+
if (was3D !== now3D) renderBody();
|
|
990
|
+
notify();
|
|
991
|
+
})),
|
|
992
|
+
field("Legend", select(
|
|
993
|
+
LEGEND_POSITIONS2,
|
|
994
|
+
sp.legendPosition ?? "hidden",
|
|
995
|
+
(v) => {
|
|
996
|
+
sp.legendPosition = v;
|
|
997
|
+
notify();
|
|
998
|
+
}
|
|
999
|
+
)),
|
|
1000
|
+
field("Interpolation", select(
|
|
1001
|
+
INTERPOLATIONS,
|
|
1002
|
+
sp.interpolation ?? "linear",
|
|
1003
|
+
(v) => {
|
|
1004
|
+
sp.interpolation = v;
|
|
1005
|
+
notify();
|
|
1006
|
+
}
|
|
1007
|
+
)),
|
|
1008
|
+
field("Missing values", select(
|
|
1009
|
+
NAN_STRATEGIES,
|
|
1010
|
+
sp.nanHandling ?? "gap",
|
|
1011
|
+
(v) => {
|
|
1012
|
+
sp.nanHandling = v;
|
|
1013
|
+
notify();
|
|
1014
|
+
}
|
|
1015
|
+
)),
|
|
1016
|
+
toggle("Data points", sp.showDataPoints === true, (on) => {
|
|
1017
|
+
sp.showDataPoints = on;
|
|
1018
|
+
})
|
|
1019
|
+
];
|
|
1020
|
+
}
|
|
1021
|
+
function xAxisFields(sp) {
|
|
1022
|
+
const useTime = sp.xAxis.useTime !== false;
|
|
1023
|
+
const out = [field("Against", select(X_SOURCES, useTime ? "time" : "series", (v) => {
|
|
1024
|
+
sp.xAxis.useTime = v === "time";
|
|
1025
|
+
renderBody();
|
|
1026
|
+
notify();
|
|
1027
|
+
}))];
|
|
1028
|
+
if (!useTime) {
|
|
1029
|
+
out.push(
|
|
1030
|
+
field("Series", seriesPicker(sp.xAxis, "variable", "x", sp, "X series")),
|
|
1031
|
+
field("Label", textInput(
|
|
1032
|
+
sp.xAxis.label,
|
|
1033
|
+
(v) => {
|
|
1034
|
+
sp.xAxis.label = v;
|
|
1035
|
+
notify();
|
|
1036
|
+
},
|
|
1037
|
+
{ placeholder: sp.xAxis.variable || "Axis title" }
|
|
1038
|
+
))
|
|
1039
|
+
);
|
|
1040
|
+
}
|
|
1041
|
+
return out;
|
|
1042
|
+
}
|
|
1043
|
+
function zAxisFields(sp) {
|
|
1044
|
+
const z = sp.zAxis;
|
|
1045
|
+
return [
|
|
1046
|
+
field("Series", seriesPicker(z, "variable", "z", sp, "Z series")),
|
|
1047
|
+
field("Label", textInput(z.label, (v) => {
|
|
1048
|
+
z.label = v;
|
|
1049
|
+
notify();
|
|
1050
|
+
}, { placeholder: "Axis title" })),
|
|
1051
|
+
field("Scale", select(SCALES, z.scale ?? "linear", (v) => {
|
|
1052
|
+
z.scale = v;
|
|
1053
|
+
notify();
|
|
1054
|
+
})),
|
|
1055
|
+
field("Min", numberInput(z.min, (v) => {
|
|
1056
|
+
z.min = v;
|
|
1057
|
+
notify();
|
|
1058
|
+
})),
|
|
1059
|
+
field("Max", numberInput(z.max, (v) => {
|
|
1060
|
+
z.max = v;
|
|
1061
|
+
notify();
|
|
1062
|
+
})),
|
|
1063
|
+
field("Step", numberInput(z.step, (v) => {
|
|
1064
|
+
z.step = v;
|
|
1065
|
+
notify();
|
|
1066
|
+
}))
|
|
1067
|
+
];
|
|
1068
|
+
}
|
|
1069
|
+
function yAxesEditor(sp, is3D, overlaySources) {
|
|
1070
|
+
const wrap = el("div", "twm-plot-config__axes");
|
|
1071
|
+
sp.yAxes.forEach((axis, i) => wrap.appendChild(yAxisEditor(sp, axis, i, is3D, overlaySources)));
|
|
1072
|
+
const add = button("Add Y axis", () => {
|
|
1073
|
+
sp.yAxes.push(makeDefaultYAxis(sp.yAxes.length));
|
|
1074
|
+
renderBody();
|
|
1075
|
+
notify();
|
|
1076
|
+
}, { title: is3D ? "A 3D chart has one Y axis" : null });
|
|
1077
|
+
add.disabled = is3D;
|
|
1078
|
+
wrap.appendChild(add);
|
|
1079
|
+
return wrap;
|
|
1080
|
+
}
|
|
1081
|
+
function yAxisEditor(sp, axis, index, is3D, overlaySources) {
|
|
1082
|
+
const group = el("fieldset", "twm-plot-config__axis");
|
|
1083
|
+
const legend = el("legend", "twm-plot-config__axis-title", `Y axis ${index + 1}`);
|
|
1084
|
+
group.appendChild(legend);
|
|
1085
|
+
if (sp.yAxes.length > 1) {
|
|
1086
|
+
legend.appendChild(removeButton("Remove Y axis", () => {
|
|
1087
|
+
sp.yAxes.splice(index, 1);
|
|
1088
|
+
renderBody();
|
|
1089
|
+
notify();
|
|
1090
|
+
}));
|
|
1091
|
+
}
|
|
1092
|
+
if (is3D && index > 0) {
|
|
1093
|
+
group.disabled = true;
|
|
1094
|
+
group.classList.add("twm-plot-config__axis--disabled");
|
|
1095
|
+
group.title = "A 3D chart uses the first Y axis only";
|
|
1096
|
+
}
|
|
1097
|
+
const fields = el("div", "twm-plot-config__axis-fields");
|
|
1098
|
+
fields.append(
|
|
1099
|
+
field("Label", textInput(axis.label, (v) => {
|
|
1100
|
+
axis.label = v;
|
|
1101
|
+
notify();
|
|
1102
|
+
}, { placeholder: "Axis title" })),
|
|
1103
|
+
field("Scale", select(SCALES, axis.scale ?? "linear", (v) => {
|
|
1104
|
+
axis.scale = v;
|
|
1105
|
+
notify();
|
|
1106
|
+
})),
|
|
1107
|
+
field("Position", select(POSITIONS, axis.position ?? "left", (v) => {
|
|
1108
|
+
axis.position = v;
|
|
1109
|
+
notify();
|
|
1110
|
+
})),
|
|
1111
|
+
field("Min", numberInput(axis.min, (v) => {
|
|
1112
|
+
axis.min = v;
|
|
1113
|
+
notify();
|
|
1114
|
+
})),
|
|
1115
|
+
field("Max", numberInput(axis.max, (v) => {
|
|
1116
|
+
axis.max = v;
|
|
1117
|
+
notify();
|
|
1118
|
+
})),
|
|
1119
|
+
field("Step", numberInput(axis.step, (v) => {
|
|
1120
|
+
axis.step = v;
|
|
1121
|
+
notify();
|
|
1122
|
+
}))
|
|
1123
|
+
);
|
|
1124
|
+
group.appendChild(fields);
|
|
1125
|
+
const list = el("div", "twm-plot-config__series-list");
|
|
1126
|
+
if (!axis.series.length) list.appendChild(el("div", "twm-plot-config__empty", "No series."));
|
|
1127
|
+
for (const s of axis.series) list.appendChild(seriesRow(sp, axis, s, overlaySources));
|
|
1128
|
+
group.appendChild(list);
|
|
1129
|
+
group.appendChild(button("Add series", () => {
|
|
1130
|
+
const count = sp.yAxes.reduce((n, a) => n + (a.series?.length ?? 0), 0);
|
|
1131
|
+
axis.series.push(makeDefaultSeries(count));
|
|
1132
|
+
renderBody();
|
|
1133
|
+
notify();
|
|
1134
|
+
}));
|
|
1135
|
+
return group;
|
|
1136
|
+
}
|
|
1137
|
+
function seriesRow(sp, axis, s, overlaySources) {
|
|
1138
|
+
const row = el("div", "twm-plot-config__series");
|
|
1139
|
+
const main = el("div", "twm-plot-config__series-main");
|
|
1140
|
+
main.append(
|
|
1141
|
+
seriesPicker(s, "variable", "series", sp, "Series"),
|
|
1142
|
+
textInput(s.label, (v) => {
|
|
1143
|
+
s.label = v;
|
|
1144
|
+
notify();
|
|
1145
|
+
}, { placeholder: "Legend label", label: "Legend label" }),
|
|
1146
|
+
colorField(s.color, (v) => {
|
|
1147
|
+
if (v) s.color = v;
|
|
1148
|
+
else delete s.color;
|
|
1149
|
+
}, "Colour"),
|
|
1150
|
+
numberInput(
|
|
1151
|
+
s.lineWidth ?? 2,
|
|
1152
|
+
(v) => {
|
|
1153
|
+
s.lineWidth = v ?? 2;
|
|
1154
|
+
notify();
|
|
1155
|
+
},
|
|
1156
|
+
{ placeholder: "2", label: "Line width", min: 1, max: 10, step: 1 }
|
|
1157
|
+
),
|
|
1158
|
+
select(DASHES, s.lineStyle ?? "solid", (v) => {
|
|
1159
|
+
s.lineStyle = v;
|
|
1160
|
+
notify();
|
|
1161
|
+
}, { label: "Dash" }),
|
|
1162
|
+
textInput(
|
|
1163
|
+
s.stackGroup,
|
|
1164
|
+
(v) => {
|
|
1165
|
+
s.stackGroup = v || null;
|
|
1166
|
+
notify();
|
|
1167
|
+
},
|
|
1168
|
+
{ placeholder: "Stack group", label: "Stack group" }
|
|
1169
|
+
),
|
|
1170
|
+
removeButton("Remove series", () => {
|
|
1171
|
+
const i = axis.series.indexOf(s);
|
|
1172
|
+
if (i >= 0) axis.series.splice(i, 1);
|
|
1173
|
+
renderBody();
|
|
1174
|
+
notify();
|
|
1175
|
+
})
|
|
1176
|
+
);
|
|
1177
|
+
row.appendChild(main);
|
|
1178
|
+
if (opts.extraSeriesFields) {
|
|
1179
|
+
const extra = el("div", "twm-plot-config__series-extra");
|
|
1180
|
+
row.appendChild(extra);
|
|
1181
|
+
const handle = opts.extraSeriesFields(extra, s, { subplot: sp, axis, onChange: notify });
|
|
1182
|
+
if (typeof handle?.dispose === "function") track(() => handle.dispose());
|
|
1183
|
+
}
|
|
1184
|
+
if (opts.overlaySources) row.appendChild(overlayEditor(s, overlaySources));
|
|
1185
|
+
return row;
|
|
1186
|
+
}
|
|
1187
|
+
function overlayEditor(s, overlaySources) {
|
|
1188
|
+
const wrap = el("div", "twm-plot-config__overlay");
|
|
1189
|
+
const head = el("div", "twm-plot-config__overlay-source", "Loading overlay sources\u2026");
|
|
1190
|
+
const details = el("div", "twm-plot-config__overlay-fields");
|
|
1191
|
+
wrap.append(head, details);
|
|
1192
|
+
const drawDetails = () => {
|
|
1193
|
+
details.replaceChildren();
|
|
1194
|
+
const ov = s.overlay;
|
|
1195
|
+
if (!ov) return;
|
|
1196
|
+
details.append(
|
|
1197
|
+
field("Label", textInput(
|
|
1198
|
+
ov.label,
|
|
1199
|
+
(v) => {
|
|
1200
|
+
ov.label = v;
|
|
1201
|
+
notify();
|
|
1202
|
+
},
|
|
1203
|
+
{ placeholder: `${s.label || s.variable || "Series"} (actual)` }
|
|
1204
|
+
)),
|
|
1205
|
+
field("Colour", colorField(ov.color, (v) => {
|
|
1206
|
+
if (v) ov.color = v;
|
|
1207
|
+
else delete ov.color;
|
|
1208
|
+
}, "Overlay colour")),
|
|
1209
|
+
field("Dash", select(DASHES, ov.lineStyle ?? "dashed", (v) => {
|
|
1210
|
+
ov.lineStyle = v;
|
|
1211
|
+
notify();
|
|
1212
|
+
})),
|
|
1213
|
+
field("Width", numberInput(
|
|
1214
|
+
ov.lineWidth ?? 2,
|
|
1215
|
+
(v) => {
|
|
1216
|
+
ov.lineWidth = v ?? 2;
|
|
1217
|
+
notify();
|
|
1218
|
+
},
|
|
1219
|
+
{ placeholder: "2", min: 1, max: 10, step: 1 }
|
|
1220
|
+
)),
|
|
1221
|
+
field("Mode", select(OVERLAY_MODES, ov.mode ?? "markers+lines", (v) => {
|
|
1222
|
+
ov.mode = v;
|
|
1223
|
+
notify();
|
|
1224
|
+
}))
|
|
1225
|
+
);
|
|
1226
|
+
};
|
|
1227
|
+
const gen = generation;
|
|
1228
|
+
overlaySources().then((list) => {
|
|
1229
|
+
if (disposed || gen !== generation) return;
|
|
1230
|
+
head.replaceChildren(field("Overlay", select(Array.isArray(list) ? list : [], s.overlay?.source, (v) => {
|
|
1231
|
+
if (v === void 0) delete s.overlay;
|
|
1232
|
+
else s.overlay = { ...s.overlay ?? {}, source: v };
|
|
1233
|
+
drawDetails();
|
|
1234
|
+
notify();
|
|
1235
|
+
}, { empty: "None" })));
|
|
1236
|
+
drawDetails();
|
|
1237
|
+
}, () => {
|
|
1238
|
+
if (disposed || gen !== generation) return;
|
|
1239
|
+
head.textContent = "Could not load overlay sources.";
|
|
1240
|
+
head.classList.add("twm-plot-config__overlay-source--error");
|
|
1241
|
+
});
|
|
1242
|
+
return wrap;
|
|
1243
|
+
}
|
|
1244
|
+
function referenceLinesEditor(sp) {
|
|
1245
|
+
const axisOptions = sp.yAxes.map((a, i) => ({ value: a.id, label: a.label || `Y axis ${i + 1}` }));
|
|
1246
|
+
return listEditor(sp, "referenceLines", {
|
|
1247
|
+
addLabel: "Add reference line",
|
|
1248
|
+
makeItem: () => ({ yAxisId: sp.yAxes[0].id, value: 0, label: "", lineStyle: "dashed" }),
|
|
1249
|
+
fields: (line) => [
|
|
1250
|
+
select(
|
|
1251
|
+
axisOptions,
|
|
1252
|
+
line.yAxisId ?? sp.yAxes[0].id,
|
|
1253
|
+
(v) => {
|
|
1254
|
+
line.yAxisId = v;
|
|
1255
|
+
notify();
|
|
1256
|
+
},
|
|
1257
|
+
{ label: "Axis" }
|
|
1258
|
+
),
|
|
1259
|
+
numberInput(line.value, (v) => {
|
|
1260
|
+
line.value = v;
|
|
1261
|
+
notify();
|
|
1262
|
+
}, { placeholder: "Value", label: "Value" }),
|
|
1263
|
+
textInput(line.label, (v) => {
|
|
1264
|
+
line.label = v;
|
|
1265
|
+
notify();
|
|
1266
|
+
}, { placeholder: "Label", label: "Label" }),
|
|
1267
|
+
colorField(line.color, (v) => {
|
|
1268
|
+
if (v) line.color = v;
|
|
1269
|
+
else delete line.color;
|
|
1270
|
+
}, "Colour"),
|
|
1271
|
+
select(DASHES, line.lineStyle ?? "dashed", (v) => {
|
|
1272
|
+
line.lineStyle = v;
|
|
1273
|
+
notify();
|
|
1274
|
+
}, { label: "Dash" })
|
|
1275
|
+
]
|
|
1276
|
+
});
|
|
1277
|
+
}
|
|
1278
|
+
function shadedBandsEditor(sp) {
|
|
1279
|
+
return listEditor(sp, "shadedBands", {
|
|
1280
|
+
addLabel: "Add shaded band",
|
|
1281
|
+
makeItem: () => ({ x0: null, x1: null, label: "" }),
|
|
1282
|
+
fields: (band) => [
|
|
1283
|
+
numberInput(band.x0, (v) => {
|
|
1284
|
+
band.x0 = v;
|
|
1285
|
+
notify();
|
|
1286
|
+
}, { placeholder: "From", label: "From" }),
|
|
1287
|
+
numberInput(band.x1, (v) => {
|
|
1288
|
+
band.x1 = v;
|
|
1289
|
+
notify();
|
|
1290
|
+
}, { placeholder: "To", label: "To" }),
|
|
1291
|
+
textInput(band.label, (v) => {
|
|
1292
|
+
band.label = v;
|
|
1293
|
+
notify();
|
|
1294
|
+
}, { placeholder: "Label", label: "Label" }),
|
|
1295
|
+
colorField(band.color, (v) => {
|
|
1296
|
+
if (v) band.color = v;
|
|
1297
|
+
else delete band.color;
|
|
1298
|
+
}, "Colour"),
|
|
1299
|
+
numberInput(band.opacity, (v) => {
|
|
1300
|
+
if (v == null) delete band.opacity;
|
|
1301
|
+
else band.opacity = v;
|
|
1302
|
+
notify();
|
|
1303
|
+
}, { placeholder: "Opacity", label: "Opacity", min: 0, max: 1, step: 0.05 })
|
|
1304
|
+
]
|
|
1305
|
+
});
|
|
1306
|
+
}
|
|
1307
|
+
function transformsSection(sp) {
|
|
1308
|
+
if (!Object.keys(opts.seriesTransforms ?? {}).length) return null;
|
|
1309
|
+
const s = section("Derived series");
|
|
1310
|
+
const controls = renderSeriesTransformControls(s, sp, opts.seriesTransforms, { onChange: notify });
|
|
1311
|
+
track(() => controls.dispose());
|
|
1312
|
+
return s;
|
|
1313
|
+
}
|
|
1314
|
+
syncChrome();
|
|
1315
|
+
renderBody();
|
|
1316
|
+
return {
|
|
1317
|
+
/** Dispose every field it created and remove the editor. Later calls do nothing. */
|
|
1318
|
+
dispose() {
|
|
1319
|
+
if (disposed) return;
|
|
1320
|
+
disposed = true;
|
|
1321
|
+
disposeBody();
|
|
1322
|
+
tabs.destroy();
|
|
1323
|
+
root.remove();
|
|
1324
|
+
},
|
|
1325
|
+
/** Redraw from the config, after it was changed from outside. */
|
|
1326
|
+
refresh() {
|
|
1327
|
+
if (disposed) return;
|
|
1328
|
+
ensureSubplots();
|
|
1329
|
+
active = Math.min(active, slots() - 1);
|
|
1330
|
+
syncChrome();
|
|
1331
|
+
renderBody();
|
|
1332
|
+
},
|
|
1333
|
+
/** Bring subplot `index` to the front. */
|
|
1334
|
+
selectSubplot(index) {
|
|
1335
|
+
if (disposed || !Number.isInteger(index) || index < 0 || index >= slots()) return;
|
|
1336
|
+
tabs.select(index);
|
|
1337
|
+
},
|
|
1338
|
+
/** The index of the subplot in front. */
|
|
1339
|
+
get activeSubplot() {
|
|
1340
|
+
return active;
|
|
1341
|
+
}
|
|
1342
|
+
};
|
|
1343
|
+
}
|
|
666
1344
|
export {
|
|
667
1345
|
ALL_CHART_TYPES,
|
|
668
1346
|
CHART_TYPES_2D,
|
|
669
1347
|
CHART_TYPES_3D,
|
|
670
1348
|
COLOR_PALETTE,
|
|
671
1349
|
DARK_THEME_LAYOUT,
|
|
1350
|
+
DEFAULT_CHART_EXPORT_FILENAME,
|
|
672
1351
|
DEFAULT_CONFIG,
|
|
1352
|
+
PLOT_GRID_THEME,
|
|
673
1353
|
PLOT_LAYOUTS,
|
|
674
1354
|
PlotPopoutWindow,
|
|
675
1355
|
applyNanHandling,
|
|
@@ -687,14 +1367,18 @@ export {
|
|
|
687
1367
|
closeDataSeriesPlotWindow,
|
|
688
1368
|
createChart,
|
|
689
1369
|
createChartDeferred,
|
|
1370
|
+
deriveSeriesTraces,
|
|
690
1371
|
ensurePlotly,
|
|
691
1372
|
extendTraces,
|
|
1373
|
+
getChartExportFilename,
|
|
692
1374
|
getSeriesColor,
|
|
693
1375
|
hasPlot,
|
|
694
1376
|
hexToRgba,
|
|
695
1377
|
is3DChart,
|
|
696
1378
|
lttb,
|
|
1379
|
+
makeDefaultSeries,
|
|
697
1380
|
makeDefaultSubplot,
|
|
1381
|
+
makeDefaultYAxis,
|
|
698
1382
|
migrateData,
|
|
699
1383
|
migrateSingleChart,
|
|
700
1384
|
openDataSeriesPlotWindow,
|
|
@@ -702,8 +1386,13 @@ export {
|
|
|
702
1386
|
openRawTracesWindow,
|
|
703
1387
|
purge,
|
|
704
1388
|
relayout,
|
|
1389
|
+
renderPlotConfigEditor,
|
|
1390
|
+
renderPlotGrid,
|
|
1391
|
+
renderSeriesTransformControls,
|
|
705
1392
|
resize,
|
|
1393
|
+
resolveSeriesData,
|
|
706
1394
|
restyle,
|
|
1395
|
+
setChartExportFilename,
|
|
707
1396
|
setPlotlySource,
|
|
708
1397
|
strideDownsampleShared,
|
|
709
1398
|
strideIndices,
|