flexdesk 0.1.0
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/LICENSE +21 -0
- package/README.md +195 -0
- package/THIRD_PARTY_LICENSES.md +53 -0
- package/css/base.css +3439 -0
- package/css/flexdesk.css +5797 -0
- package/css/overrides.css +2349 -0
- package/css/reset.css +46 -0
- package/css/tokens.css +137 -0
- package/dist/charts.js +780 -0
- package/dist/charts.js.map +7 -0
- package/dist/chunk-3PHCPZHT.js +222 -0
- package/dist/chunk-3PHCPZHT.js.map +7 -0
- package/dist/chunk-CT4YXXLP.js +1770 -0
- package/dist/chunk-CT4YXXLP.js.map +7 -0
- package/dist/chunk-DRYCDMEG.js +2217 -0
- package/dist/chunk-DRYCDMEG.js.map +7 -0
- package/dist/chunk-DVU44T77.js +855 -0
- package/dist/chunk-DVU44T77.js.map +7 -0
- package/dist/chunk-FL5KFNQH.js +640 -0
- package/dist/chunk-FL5KFNQH.js.map +7 -0
- package/dist/chunk-FOOS3T5L.js +117 -0
- package/dist/chunk-FOOS3T5L.js.map +7 -0
- package/dist/chunk-JYWURG5T.js +32 -0
- package/dist/chunk-JYWURG5T.js.map +7 -0
- package/dist/chunk-TLZUUFOE.js +276 -0
- package/dist/chunk-TLZUUFOE.js.map +7 -0
- package/dist/chunk-UCJ2WD4D.js +625 -0
- package/dist/chunk-UCJ2WD4D.js.map +7 -0
- package/dist/chunk-WVFGV5FT.js +205 -0
- package/dist/chunk-WVFGV5FT.js.map +7 -0
- package/dist/core.js +1098 -0
- package/dist/core.js.map +7 -0
- package/dist/editor.js +1661 -0
- package/dist/editor.js.map +7 -0
- package/dist/flexdesk.css +5797 -0
- package/dist/host.js +132 -0
- package/dist/host.js.map +7 -0
- package/dist/reset.css +46 -0
- package/dist/tile_registry-6WZPTUZV.js +28 -0
- package/dist/tile_registry-6WZPTUZV.js.map +7 -0
- package/dist/tiles.js +2068 -0
- package/dist/tiles.js.map +7 -0
- package/dist/tokens.css +137 -0
- package/dist/widgets.js +5438 -0
- package/dist/widgets.js.map +7 -0
- package/dist/wm.js +4830 -0
- package/dist/wm.js.map +7 -0
- package/package.json +80 -0
- package/src/charts/chart_types.js +699 -0
- package/src/charts/data_series_plot_window.js +761 -0
- package/src/charts/downsample.js +119 -0
- package/src/charts/plot_config.js +176 -0
- package/src/charts/plot_popout_window.js +1560 -0
- package/src/charts/plotly_wrapper.js +432 -0
- package/src/core/event_bus.js +230 -0
- package/src/core/logging.js +300 -0
- package/src/core/settings.js +813 -0
- package/src/core/state_guard.js +382 -0
- package/src/core/state_guard_config.js +309 -0
- package/src/core/state_machine.js +258 -0
- package/src/editor/editor_pane.js +264 -0
- package/src/editor/monaco_editor_factory.js +449 -0
- package/src/editor/monaco_loader.js +74 -0
- package/src/editor/notebook_search.js +460 -0
- package/src/editor/notebook_tab_bar.js +456 -0
- package/src/editor/notebook_undo_manager.js +108 -0
- package/src/editor/split_pane_container.js +197 -0
- package/src/help/help_modal.js +409 -0
- package/src/help/help_registry.js +65 -0
- package/src/host/host.js +69 -0
- package/src/host/pywebview_host.js +133 -0
- package/src/tiles/config_schema.js +256 -0
- package/src/tiles/layout_persistence.js +662 -0
- package/src/tiles/tile_base.js +785 -0
- package/src/tiles/tile_grid.js +905 -0
- package/src/tiles/tile_registry.js +136 -0
- package/src/tiling/command_palette.js +328 -0
- package/src/tiling/content_registry.js +110 -0
- package/src/tiling/desktops.js +133 -0
- package/src/tiling/entity_sources.js +179 -0
- package/src/tiling/keymap.js +156 -0
- package/src/tiling/kind_taxonomy.js +172 -0
- package/src/tiling/landing_table.js +717 -0
- package/src/tiling/loading_overlay.js +33 -0
- package/src/tiling/nav_panel.js +142 -0
- package/src/tiling/page_factory.js +215 -0
- package/src/tiling/panel_keys.js +145 -0
- package/src/tiling/shell.js +437 -0
- package/src/tiling/tile_breadcrumb.js +222 -0
- package/src/tiling/tile_renderer.js +459 -0
- package/src/tiling/tile_tab_menu.js +340 -0
- package/src/tiling/tile_tree.js +880 -0
- package/src/tiling/wm.js +1352 -0
- package/src/ui/base/component_base.js +30 -0
- package/src/ui/base/controller_base.js +48 -0
- package/src/ui/base/page_base.js +102 -0
- package/src/ui/components/about_dialog.js +224 -0
- package/src/ui/components/action_dropdown.js +389 -0
- package/src/ui/components/attribute_list_editor.js +652 -0
- package/src/ui/components/autocomplete_field.js +732 -0
- package/src/ui/components/computing_status_window.js +81 -0
- package/src/ui/components/confirm_dialog.js +307 -0
- package/src/ui/components/context_menu.js +87 -0
- package/src/ui/components/data_table.js +2228 -0
- package/src/ui/components/detail_header.js +177 -0
- package/src/ui/components/drag_reorder.js +0 -0
- package/src/ui/components/gallery_picker.js +147 -0
- package/src/ui/components/inline_renamer.js +129 -0
- package/src/ui/components/managed_window.js +804 -0
- package/src/ui/components/modal.js +512 -0
- package/src/ui/components/notification_history.js +264 -0
- package/src/ui/components/slide_out_panel.js +185 -0
- package/src/ui/components/slider_field.js +122 -0
- package/src/ui/components/sortable_list.js +309 -0
- package/src/ui/components/table_state_store.js +81 -0
- package/src/ui/components/toast.js +59 -0
- package/src/ui/components/tree_view.js +460 -0
- package/src/ui/components/window_taskbar.js +143 -0
- package/src/ui/controllers/panel_state_machine.js +634 -0
- package/src/ui/controllers/window_chrome_controller.js +659 -0
- package/src/ui/notification_center.js +576 -0
- package/src/ui/utils/overlay_scrollbar.js +492 -0
- package/src/ui/utils/raf_resize_observer.js +31 -0
- package/src/ui/utils/tooltip_service.js +406 -0
package/dist/charts.js
ADDED
|
@@ -0,0 +1,780 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ALL_CHART_TYPES,
|
|
3
|
+
CHART_TYPES_2D,
|
|
4
|
+
CHART_TYPES_3D,
|
|
5
|
+
COLOR_PALETTE,
|
|
6
|
+
DARK_THEME_LAYOUT,
|
|
7
|
+
DEFAULT_CONFIG,
|
|
8
|
+
PLOT_LAYOUTS,
|
|
9
|
+
PlotPopoutWindow,
|
|
10
|
+
applyNanHandling,
|
|
11
|
+
build3DSceneLayout,
|
|
12
|
+
buildBoxTrace,
|
|
13
|
+
buildFanBandTraces,
|
|
14
|
+
buildHeatmapTrace,
|
|
15
|
+
buildHistogramTrace,
|
|
16
|
+
buildLegendLayout,
|
|
17
|
+
buildOverlayShapes,
|
|
18
|
+
buildTrace,
|
|
19
|
+
buildXAxisLayout,
|
|
20
|
+
buildYAxisLayout,
|
|
21
|
+
createChart,
|
|
22
|
+
createChartDeferred,
|
|
23
|
+
ensurePlotly,
|
|
24
|
+
extendTraces,
|
|
25
|
+
getSeriesColor,
|
|
26
|
+
hasPlot,
|
|
27
|
+
hexToRgba,
|
|
28
|
+
is3DChart,
|
|
29
|
+
makeDefaultSubplot,
|
|
30
|
+
migrateData,
|
|
31
|
+
migrateSingleChart,
|
|
32
|
+
openPlotPopoutWindow,
|
|
33
|
+
openRawTracesWindow,
|
|
34
|
+
purge,
|
|
35
|
+
relayout,
|
|
36
|
+
resize,
|
|
37
|
+
restyle,
|
|
38
|
+
setPlotlySource,
|
|
39
|
+
uid,
|
|
40
|
+
updateChart
|
|
41
|
+
} from "./chunk-DRYCDMEG.js";
|
|
42
|
+
import {
|
|
43
|
+
createRafResizeObserver
|
|
44
|
+
} from "./chunk-CT4YXXLP.js";
|
|
45
|
+
import {
|
|
46
|
+
ManagedWindow
|
|
47
|
+
} from "./chunk-UCJ2WD4D.js";
|
|
48
|
+
import "./chunk-FL5KFNQH.js";
|
|
49
|
+
import "./chunk-JYWURG5T.js";
|
|
50
|
+
|
|
51
|
+
// src/charts/data_series_plot_window.js
|
|
52
|
+
var defaultClassifyHeader = (header) => {
|
|
53
|
+
const lower = String(header ?? "").trim().toLowerCase();
|
|
54
|
+
return lower === "t" || lower === "time" || lower === "step" || lower === "period" ? "time" : "indicator";
|
|
55
|
+
};
|
|
56
|
+
var defaultLabelHeader = (header) => header;
|
|
57
|
+
var INTERPOLATION_MODES = ["linear", "step", "cubic"];
|
|
58
|
+
var CHART_TYPES = ["line", "scatter", "bar", "area", "area-stacked"];
|
|
59
|
+
var LEGEND_POSITIONS = ["top", "bottom", "right", "hidden"];
|
|
60
|
+
var MAX_AUTO_COLUMNS = 3;
|
|
61
|
+
var DataSeriesPlotWindow = class _DataSeriesPlotWindow {
|
|
62
|
+
/**
|
|
63
|
+
* @param {Object} options
|
|
64
|
+
* @param {string} options.seriesId
|
|
65
|
+
* @param {string} options.seriesName
|
|
66
|
+
* @param {string} options.datasetName
|
|
67
|
+
* @param {string[]} options.headers - Column headers from the series
|
|
68
|
+
* @param {Object} options.dataHub - DataHub service for fetching data
|
|
69
|
+
* @param {Object} options.services - { eventBus, logger }
|
|
70
|
+
* @param {Object} [options.host] - A Host (ui/js/host/host.js). Its `dialogs`
|
|
71
|
+
* capability backs Export PNG/CSV; absent => Blob download fallback.
|
|
72
|
+
* @param {Function} options.getPlotConfig - (seriesId) => config | null
|
|
73
|
+
* @param {Function} options.savePlotConfig - (seriesId, config) => void
|
|
74
|
+
*/
|
|
75
|
+
constructor({
|
|
76
|
+
seriesId,
|
|
77
|
+
seriesName,
|
|
78
|
+
datasetName,
|
|
79
|
+
headers,
|
|
80
|
+
dataHub,
|
|
81
|
+
services,
|
|
82
|
+
host,
|
|
83
|
+
getPlotConfig,
|
|
84
|
+
savePlotConfig,
|
|
85
|
+
classifyHeader = defaultClassifyHeader,
|
|
86
|
+
labelHeader = defaultLabelHeader
|
|
87
|
+
}) {
|
|
88
|
+
this.classifyHeader = classifyHeader;
|
|
89
|
+
this.labelHeader = labelHeader;
|
|
90
|
+
this.seriesId = seriesId;
|
|
91
|
+
this.seriesName = seriesName;
|
|
92
|
+
this.datasetName = datasetName;
|
|
93
|
+
this.headers = headers;
|
|
94
|
+
this.dataHub = dataHub;
|
|
95
|
+
this.services = services;
|
|
96
|
+
this._host = host || null;
|
|
97
|
+
this.getPlotConfig = getPlotConfig;
|
|
98
|
+
this.savePlotConfig = savePlotConfig;
|
|
99
|
+
this.window = null;
|
|
100
|
+
this.contentEl = null;
|
|
101
|
+
this.plotDiv = null;
|
|
102
|
+
this._resizeObserver = null;
|
|
103
|
+
this._chartCreated = false;
|
|
104
|
+
this._config = null;
|
|
105
|
+
this._fullData = null;
|
|
106
|
+
this._numericColumns = null;
|
|
107
|
+
this._loading = false;
|
|
108
|
+
}
|
|
109
|
+
async show() {
|
|
110
|
+
if (this.window?.isVisible) {
|
|
111
|
+
this.window.bringToFront();
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
await ensurePlotly();
|
|
115
|
+
await this.#fetchData();
|
|
116
|
+
this._numericColumns = new Set(this.headers.filter((h) => this.#isNumericColumn(h)));
|
|
117
|
+
const saved = this.getPlotConfig?.(this.seriesId);
|
|
118
|
+
if (saved) {
|
|
119
|
+
this._config = { ...saved };
|
|
120
|
+
this._config.yColumns = (saved.yColumns || []).filter((c) => this._numericColumns.has(c.header));
|
|
121
|
+
} else {
|
|
122
|
+
this._config = _DataSeriesPlotWindow.createDefaultConfig(this.headers, this._numericColumns);
|
|
123
|
+
}
|
|
124
|
+
this.#createWindow();
|
|
125
|
+
this.#setupResizeObserver();
|
|
126
|
+
await this.#renderPlot();
|
|
127
|
+
}
|
|
128
|
+
close() {
|
|
129
|
+
if (this.window?.isVisible) {
|
|
130
|
+
this.window.close();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// ── Window creation ──────────────────────────────────────────────────
|
|
134
|
+
#createWindow() {
|
|
135
|
+
this.contentEl = this.#buildContent();
|
|
136
|
+
const title = this.datasetName ? `${this.datasetName} \xB7 ${this.seriesName}` : this.seriesName || "Series Plot";
|
|
137
|
+
this.window = new ManagedWindow({
|
|
138
|
+
id: `data-series-plot-${this.seriesId}`,
|
|
139
|
+
title,
|
|
140
|
+
icon: "monitoring",
|
|
141
|
+
content: this.contentEl,
|
|
142
|
+
minWidth: 600,
|
|
143
|
+
minHeight: 400,
|
|
144
|
+
defaultWidth: 900,
|
|
145
|
+
defaultHeight: 550,
|
|
146
|
+
canMinimize: true,
|
|
147
|
+
canMaximize: true,
|
|
148
|
+
canResize: true,
|
|
149
|
+
canDrag: true,
|
|
150
|
+
onClose: () => this.#dispose()
|
|
151
|
+
});
|
|
152
|
+
this.window.show();
|
|
153
|
+
}
|
|
154
|
+
#buildContent() {
|
|
155
|
+
const container = document.createElement("div");
|
|
156
|
+
container.className = "twm-data-series-plot-container";
|
|
157
|
+
const actionsBar = document.createElement("div");
|
|
158
|
+
actionsBar.className = "twm-data-series-plot-actions";
|
|
159
|
+
const pngBtn = document.createElement("button");
|
|
160
|
+
pngBtn.className = "twm-btn-icon twm-has-tooltip";
|
|
161
|
+
pngBtn.type = "button";
|
|
162
|
+
pngBtn.setAttribute("data-tooltip", "Download plot as PNG");
|
|
163
|
+
pngBtn.innerHTML = '<span class="material-symbols-outlined">image</span>';
|
|
164
|
+
pngBtn.addEventListener("click", () => this.#downloadPNG());
|
|
165
|
+
const csvBtn = document.createElement("button");
|
|
166
|
+
csvBtn.className = "twm-btn-icon twm-has-tooltip";
|
|
167
|
+
csvBtn.type = "button";
|
|
168
|
+
csvBtn.setAttribute("data-tooltip", "Download data as CSV");
|
|
169
|
+
csvBtn.innerHTML = '<span class="material-symbols-outlined">download</span>';
|
|
170
|
+
csvBtn.addEventListener("click", () => this.#downloadCSV());
|
|
171
|
+
actionsBar.appendChild(pngBtn);
|
|
172
|
+
actionsBar.appendChild(csvBtn);
|
|
173
|
+
const chartWrap = document.createElement("div");
|
|
174
|
+
chartWrap.className = "twm-data-series-plot-body";
|
|
175
|
+
this.plotDiv = document.createElement("div");
|
|
176
|
+
this.plotDiv.className = "twm-data-series-plot-chart";
|
|
177
|
+
chartWrap.appendChild(this.plotDiv);
|
|
178
|
+
const configPanel = this.#buildConfigPanel();
|
|
179
|
+
chartWrap.appendChild(configPanel);
|
|
180
|
+
container.appendChild(actionsBar);
|
|
181
|
+
container.appendChild(chartWrap);
|
|
182
|
+
return container;
|
|
183
|
+
}
|
|
184
|
+
// ── Config panel ─────────────────────────────────────────────────────
|
|
185
|
+
#buildConfigPanel() {
|
|
186
|
+
const panel = document.createElement("div");
|
|
187
|
+
panel.className = "twm-data-series-plot-config";
|
|
188
|
+
panel.appendChild(this.#buildSection("X Axis", this.#buildXColumnSelect()));
|
|
189
|
+
this._yColumnsList = document.createElement("div");
|
|
190
|
+
this._yColumnsList.className = "twm-data-series-plot-config__columns";
|
|
191
|
+
this.#populateYColumns();
|
|
192
|
+
panel.appendChild(this.#buildSection("Y Axis", this._yColumnsList));
|
|
193
|
+
panel.appendChild(this.#buildSection("Chart Type", this.#buildSelect(
|
|
194
|
+
CHART_TYPES,
|
|
195
|
+
this._config.chartType,
|
|
196
|
+
(val) => {
|
|
197
|
+
this._config.chartType = val;
|
|
198
|
+
this.#onConfigChanged();
|
|
199
|
+
}
|
|
200
|
+
)));
|
|
201
|
+
panel.appendChild(this.#buildSection("Interpolation", this.#buildSelect(
|
|
202
|
+
INTERPOLATION_MODES,
|
|
203
|
+
this._config.interpolation,
|
|
204
|
+
(val) => {
|
|
205
|
+
this._config.interpolation = val;
|
|
206
|
+
this.#onConfigChanged();
|
|
207
|
+
}
|
|
208
|
+
)));
|
|
209
|
+
panel.appendChild(this.#buildSection("Legend", this.#buildSelect(
|
|
210
|
+
LEGEND_POSITIONS,
|
|
211
|
+
this._config.legendPosition,
|
|
212
|
+
(val) => {
|
|
213
|
+
this._config.legendPosition = val;
|
|
214
|
+
this.#onConfigChanged();
|
|
215
|
+
}
|
|
216
|
+
)));
|
|
217
|
+
panel.appendChild(this.#buildSection("", this.#buildCheckbox(
|
|
218
|
+
"Show data points",
|
|
219
|
+
this._config.showDataPoints,
|
|
220
|
+
(checked) => {
|
|
221
|
+
this._config.showDataPoints = checked;
|
|
222
|
+
this.#onConfigChanged();
|
|
223
|
+
}
|
|
224
|
+
)));
|
|
225
|
+
return panel;
|
|
226
|
+
}
|
|
227
|
+
#buildSection(label, content) {
|
|
228
|
+
const section = document.createElement("div");
|
|
229
|
+
section.className = "twm-data-series-plot-config__section";
|
|
230
|
+
if (label) {
|
|
231
|
+
const labelEl = document.createElement("div");
|
|
232
|
+
labelEl.className = "twm-data-series-plot-config__label";
|
|
233
|
+
labelEl.textContent = label;
|
|
234
|
+
section.appendChild(labelEl);
|
|
235
|
+
}
|
|
236
|
+
if (content instanceof HTMLElement) {
|
|
237
|
+
section.appendChild(content);
|
|
238
|
+
}
|
|
239
|
+
return section;
|
|
240
|
+
}
|
|
241
|
+
#buildXColumnSelect() {
|
|
242
|
+
const select = document.createElement("select");
|
|
243
|
+
for (const header of this.headers) {
|
|
244
|
+
if (!this._numericColumns.has(header)) continue;
|
|
245
|
+
const opt = document.createElement("option");
|
|
246
|
+
opt.value = header;
|
|
247
|
+
opt.textContent = this.labelHeader(header);
|
|
248
|
+
if (header === this._config.xColumn) opt.selected = true;
|
|
249
|
+
select.appendChild(opt);
|
|
250
|
+
}
|
|
251
|
+
select.addEventListener("change", () => {
|
|
252
|
+
this._config.xColumn = select.value;
|
|
253
|
+
this.#onConfigChanged();
|
|
254
|
+
});
|
|
255
|
+
return select;
|
|
256
|
+
}
|
|
257
|
+
#populateYColumns() {
|
|
258
|
+
const list = this._yColumnsList;
|
|
259
|
+
list.innerHTML = "";
|
|
260
|
+
const selectedSet = new Set(this._config.yColumns.map((c) => c.header));
|
|
261
|
+
for (const header of this.headers) {
|
|
262
|
+
if (header === this._config.xColumn) continue;
|
|
263
|
+
if (!this._numericColumns.has(header)) continue;
|
|
264
|
+
const item = document.createElement("label");
|
|
265
|
+
item.className = "twm-data-series-plot-config__column-item";
|
|
266
|
+
const cb = document.createElement("input");
|
|
267
|
+
cb.type = "checkbox";
|
|
268
|
+
cb.checked = selectedSet.has(header);
|
|
269
|
+
cb.addEventListener("change", () => this.#toggleYColumn(header, cb.checked));
|
|
270
|
+
const colorSwatch = document.createElement("input");
|
|
271
|
+
colorSwatch.type = "color";
|
|
272
|
+
colorSwatch.className = "twm-data-series-plot-config__color-swatch";
|
|
273
|
+
const existing = this._config.yColumns.find((c) => c.header === header);
|
|
274
|
+
colorSwatch.value = existing?.color || this.#nextColor(this.headers.indexOf(header));
|
|
275
|
+
colorSwatch.addEventListener("input", () => this.#updateColumnColor(header, colorSwatch.value));
|
|
276
|
+
const label = document.createElement("span");
|
|
277
|
+
label.textContent = this.labelHeader(header);
|
|
278
|
+
label.title = header;
|
|
279
|
+
item.appendChild(cb);
|
|
280
|
+
item.appendChild(colorSwatch);
|
|
281
|
+
item.appendChild(label);
|
|
282
|
+
list.appendChild(item);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
#toggleYColumn(header, checked) {
|
|
286
|
+
if (checked) {
|
|
287
|
+
if (!this._config.yColumns.find((c) => c.header === header)) {
|
|
288
|
+
const colorSwatch = this._yColumnsList.querySelector(
|
|
289
|
+
`label:has(span[title="${header}"]) input[type="color"]`
|
|
290
|
+
);
|
|
291
|
+
this._config.yColumns.push({
|
|
292
|
+
header,
|
|
293
|
+
color: colorSwatch?.value || this.#nextColor(this.headers.indexOf(header))
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
} else {
|
|
297
|
+
this._config.yColumns = this._config.yColumns.filter((c) => c.header !== header);
|
|
298
|
+
}
|
|
299
|
+
this.#onConfigChanged();
|
|
300
|
+
}
|
|
301
|
+
#updateColumnColor(header, color) {
|
|
302
|
+
const col = this._config.yColumns.find((c) => c.header === header);
|
|
303
|
+
if (col) {
|
|
304
|
+
col.color = color;
|
|
305
|
+
this.#onConfigChanged();
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
#nextColor(index) {
|
|
309
|
+
return getSeriesColor(index);
|
|
310
|
+
}
|
|
311
|
+
#buildSelect(options, value, onChange) {
|
|
312
|
+
const select = document.createElement("select");
|
|
313
|
+
for (const opt of options) {
|
|
314
|
+
const el = document.createElement("option");
|
|
315
|
+
el.value = opt;
|
|
316
|
+
el.textContent = opt.charAt(0).toUpperCase() + opt.slice(1);
|
|
317
|
+
if (opt === value) el.selected = true;
|
|
318
|
+
select.appendChild(el);
|
|
319
|
+
}
|
|
320
|
+
select.addEventListener("change", () => onChange(select.value));
|
|
321
|
+
return select;
|
|
322
|
+
}
|
|
323
|
+
#buildCheckbox(label, checked, onChange) {
|
|
324
|
+
const wrapper = document.createElement("label");
|
|
325
|
+
wrapper.className = "twm-data-series-plot-config__column-item";
|
|
326
|
+
const cb = document.createElement("input");
|
|
327
|
+
cb.type = "checkbox";
|
|
328
|
+
cb.checked = checked;
|
|
329
|
+
cb.addEventListener("change", () => onChange(cb.checked));
|
|
330
|
+
const span = document.createElement("span");
|
|
331
|
+
span.textContent = label;
|
|
332
|
+
wrapper.appendChild(cb);
|
|
333
|
+
wrapper.appendChild(span);
|
|
334
|
+
return wrapper;
|
|
335
|
+
}
|
|
336
|
+
// ── Data fetching ────────────────────────────────────────────────────
|
|
337
|
+
async #fetchData() {
|
|
338
|
+
if (this._loading) return;
|
|
339
|
+
this._loading = true;
|
|
340
|
+
try {
|
|
341
|
+
const response = await this.dataHub.fetchSeriesById(this.seriesId, { limit: null, offset: 0 });
|
|
342
|
+
if (response?.ok) {
|
|
343
|
+
this._fullData = {
|
|
344
|
+
headers: response.headers || [],
|
|
345
|
+
rows: response.rows || []
|
|
346
|
+
};
|
|
347
|
+
} else {
|
|
348
|
+
this.#notify("Plot", response?.error || "Failed to load series data", "error");
|
|
349
|
+
}
|
|
350
|
+
} catch (err) {
|
|
351
|
+
this.services.logger?.warn?.("[DataSeriesPlotWindow] Fetch failed", err);
|
|
352
|
+
this.#notify("Plot", "Failed to load data", "error");
|
|
353
|
+
} finally {
|
|
354
|
+
this._loading = false;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
#isNumericColumn(header) {
|
|
358
|
+
if (!this._fullData) return false;
|
|
359
|
+
const data = this.#extractColumnData(header);
|
|
360
|
+
let numericCount = 0;
|
|
361
|
+
let sampleCount = 0;
|
|
362
|
+
for (const v of data) {
|
|
363
|
+
if (v == null || v === "") continue;
|
|
364
|
+
sampleCount++;
|
|
365
|
+
if (Number.isFinite(Number(v))) numericCount++;
|
|
366
|
+
if (sampleCount >= 20) break;
|
|
367
|
+
}
|
|
368
|
+
return sampleCount > 0 && numericCount / sampleCount >= 0.8;
|
|
369
|
+
}
|
|
370
|
+
// ── Chart rendering ──────────────────────────────────────────────────
|
|
371
|
+
#extractColumnData(columnHeader) {
|
|
372
|
+
if (!this._fullData) return [];
|
|
373
|
+
const { headers, rows } = this._fullData;
|
|
374
|
+
const idx = headers.indexOf(columnHeader);
|
|
375
|
+
if (idx < 0) return [];
|
|
376
|
+
const TIME_NAMES = /* @__PURE__ */ new Set(["time", "tDisp", "year", "date", "timestamp"]);
|
|
377
|
+
const firstIsTime = TIME_NAMES.has(headers[0]);
|
|
378
|
+
const colOffset = firstIsTime ? 1 : 0;
|
|
379
|
+
return rows.map((row) => {
|
|
380
|
+
if (Array.isArray(row)) return row[idx];
|
|
381
|
+
if (row && typeof row === "object") {
|
|
382
|
+
if (TIME_NAMES.has(columnHeader) && row.tDisp !== void 0) return row.tDisp;
|
|
383
|
+
if (Array.isArray(row.cols)) return row.cols[idx - colOffset] ?? "";
|
|
384
|
+
return row[columnHeader] ?? "";
|
|
385
|
+
}
|
|
386
|
+
return "";
|
|
387
|
+
}).map((v) => {
|
|
388
|
+
const n = Number(v);
|
|
389
|
+
return Number.isFinite(n) ? n : v;
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
async #renderPlot() {
|
|
393
|
+
if (!this.plotDiv || !window.Plotly || !this._fullData) return;
|
|
394
|
+
const cfg = this._config;
|
|
395
|
+
const xData = this.#extractColumnData(cfg.xColumn);
|
|
396
|
+
if (xData.length === 0 || cfg.yColumns.length === 0) {
|
|
397
|
+
if (this._chartCreated) {
|
|
398
|
+
try {
|
|
399
|
+
purge(this.plotDiv);
|
|
400
|
+
} catch (e) {
|
|
401
|
+
}
|
|
402
|
+
this._chartCreated = false;
|
|
403
|
+
}
|
|
404
|
+
this.plotDiv.innerHTML = '<div style="display:flex;align-items:center;justify-content:center;height:100%;color:#888;font-size:13px;">Select columns to plot</div>';
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
if (!this._chartCreated) {
|
|
408
|
+
this.plotDiv.innerHTML = "";
|
|
409
|
+
}
|
|
410
|
+
const traces = [];
|
|
411
|
+
for (let i = 0; i < cfg.yColumns.length; i++) {
|
|
412
|
+
const col = cfg.yColumns[i];
|
|
413
|
+
const yData = this.#extractColumnData(col.header);
|
|
414
|
+
traces.push(buildTrace({
|
|
415
|
+
chartType: cfg.chartType,
|
|
416
|
+
x: xData,
|
|
417
|
+
y: yData,
|
|
418
|
+
name: this.labelHeader(col.header),
|
|
419
|
+
color: col.color,
|
|
420
|
+
lineWidth: 2,
|
|
421
|
+
lineStyle: "solid",
|
|
422
|
+
interpolation: cfg.interpolation,
|
|
423
|
+
yAxisId: "y",
|
|
424
|
+
showMarkers: cfg.showDataPoints,
|
|
425
|
+
seriesIndex: i
|
|
426
|
+
}));
|
|
427
|
+
}
|
|
428
|
+
const legendAtTop = cfg.legendPosition === "top";
|
|
429
|
+
const legendAtBottom = cfg.legendPosition === "bottom";
|
|
430
|
+
const layout = {
|
|
431
|
+
...DARK_THEME_LAYOUT,
|
|
432
|
+
autosize: true,
|
|
433
|
+
margin: {
|
|
434
|
+
l: 60,
|
|
435
|
+
r: 40,
|
|
436
|
+
b: legendAtBottom ? 80 : 50,
|
|
437
|
+
t: legendAtTop ? 60 : 30
|
|
438
|
+
}
|
|
439
|
+
};
|
|
440
|
+
delete layout.scene;
|
|
441
|
+
layout.xaxis = {
|
|
442
|
+
...buildXAxisLayout({ title: this.labelHeader(cfg.xColumn) })
|
|
443
|
+
};
|
|
444
|
+
layout.hovermode = "x unified";
|
|
445
|
+
layout.xaxis.showspikes = true;
|
|
446
|
+
layout.xaxis.spikemode = "across";
|
|
447
|
+
layout.xaxis.spikethickness = 1;
|
|
448
|
+
layout.xaxis.spikecolor = "#666666";
|
|
449
|
+
layout.xaxis.spikedash = "dot";
|
|
450
|
+
layout.yaxis = buildYAxisLayout({
|
|
451
|
+
axisId: "yaxis",
|
|
452
|
+
title: "",
|
|
453
|
+
position: "left",
|
|
454
|
+
scaleType: "linear",
|
|
455
|
+
showGrid: true
|
|
456
|
+
});
|
|
457
|
+
const legendConfig = buildLegendLayout(cfg.legendPosition);
|
|
458
|
+
if (legendConfig.legend) {
|
|
459
|
+
layout.legend = legendConfig.legend;
|
|
460
|
+
}
|
|
461
|
+
layout.showlegend = legendConfig.showlegend !== false;
|
|
462
|
+
const plotConfig = {
|
|
463
|
+
...DEFAULT_CONFIG,
|
|
464
|
+
responsive: true,
|
|
465
|
+
displayModeBar: true,
|
|
466
|
+
modeBarButtonsToRemove: ["lasso2d", "select2d", "toImage"]
|
|
467
|
+
};
|
|
468
|
+
try {
|
|
469
|
+
await window.Plotly.newPlot(this.plotDiv, traces, layout, plotConfig);
|
|
470
|
+
this._chartCreated = true;
|
|
471
|
+
requestAnimationFrame(() => {
|
|
472
|
+
try {
|
|
473
|
+
window.Plotly.Plots.resize(this.plotDiv);
|
|
474
|
+
} catch (e) {
|
|
475
|
+
}
|
|
476
|
+
});
|
|
477
|
+
} catch (e) {
|
|
478
|
+
console.error("[DataSeriesPlotWindow] Chart render error:", e);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
// ── Config change handler ────────────────────────────────────────────
|
|
482
|
+
#onConfigChanged() {
|
|
483
|
+
this.#renderPlot();
|
|
484
|
+
this.savePlotConfig?.(this.seriesId, { ...this._config, yColumns: [...this._config.yColumns] });
|
|
485
|
+
}
|
|
486
|
+
// ── Resize observer ──────────────────────────────────────────────────
|
|
487
|
+
#setupResizeObserver() {
|
|
488
|
+
if (!this.plotDiv) return;
|
|
489
|
+
this._resizeObserver = createRafResizeObserver(() => {
|
|
490
|
+
if (this.plotDiv && window.Plotly && this._chartCreated) {
|
|
491
|
+
try {
|
|
492
|
+
window.Plotly.Plots.resize(this.plotDiv);
|
|
493
|
+
} catch (e) {
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
});
|
|
497
|
+
this._resizeObserver.observe(this.plotDiv);
|
|
498
|
+
}
|
|
499
|
+
// ── Downloads ────────────────────────────────────────────────────────
|
|
500
|
+
async #downloadPNG() {
|
|
501
|
+
if (!this.plotDiv || !window.Plotly || !this._chartCreated) {
|
|
502
|
+
this.#notify("Download", "Plot not ready", "warn");
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
const filename = `${(this.seriesName || "series").replace(/[^a-z0-9]/gi, "_")}.png`;
|
|
506
|
+
try {
|
|
507
|
+
const pngDataUrl = await window.Plotly.toImage(this.plotDiv, {
|
|
508
|
+
format: "png",
|
|
509
|
+
width: 1920,
|
|
510
|
+
height: 1080,
|
|
511
|
+
scale: 2
|
|
512
|
+
});
|
|
513
|
+
const dialogs = this._host?.dialogs;
|
|
514
|
+
const result = dialogs ? await dialogs.saveFile({ data: pngDataUrl.split(",")[1], filename, kind: "png" }) : null;
|
|
515
|
+
if (result) {
|
|
516
|
+
if (result.ok) {
|
|
517
|
+
this.#notify("Download", `Plot saved to ${result.path}`, "success");
|
|
518
|
+
} else if (!result.cancelled) {
|
|
519
|
+
this.#notify("Download", result.error || "Failed to save image", "error");
|
|
520
|
+
}
|
|
521
|
+
} else {
|
|
522
|
+
const link = document.createElement("a");
|
|
523
|
+
link.href = pngDataUrl;
|
|
524
|
+
link.download = filename;
|
|
525
|
+
link.click();
|
|
526
|
+
}
|
|
527
|
+
} catch (err) {
|
|
528
|
+
this.services.logger?.warn?.("[DataSeriesPlotWindow] PNG download failed", err);
|
|
529
|
+
this.#notify("Download", "Failed to generate PNG", "error");
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
async #downloadCSV() {
|
|
533
|
+
if (!this._fullData || !this._fullData.rows.length) {
|
|
534
|
+
this.#notify("Download", "No data available", "warn");
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
const cfg = this._config;
|
|
538
|
+
const columns = [cfg.xColumn, ...cfg.yColumns.map((c) => c.header)];
|
|
539
|
+
const headers = columns.map((h) => this.labelHeader(h));
|
|
540
|
+
const columnData = columns.map((col) => this.#extractColumnData(col));
|
|
541
|
+
const rowCount = this._fullData.rows.length;
|
|
542
|
+
const lines = [headers.join(",")];
|
|
543
|
+
for (let r = 0; r < rowCount; r++) {
|
|
544
|
+
const vals = columnData.map((data) => this.#csvEscape(data[r]));
|
|
545
|
+
lines.push(vals.join(","));
|
|
546
|
+
}
|
|
547
|
+
const csv = lines.join("\n");
|
|
548
|
+
const filename = `${(this.seriesName || "series").replace(/[^a-z0-9]/gi, "_")}_data.csv`;
|
|
549
|
+
const dialogs = this._host?.dialogs;
|
|
550
|
+
let result = null;
|
|
551
|
+
if (dialogs) {
|
|
552
|
+
try {
|
|
553
|
+
result = await dialogs.saveFile({ data: csv, filename, kind: "csv" });
|
|
554
|
+
} catch (err) {
|
|
555
|
+
this.#notify("Download", "Failed to save file", "error");
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
if (result) {
|
|
560
|
+
if (result.ok) {
|
|
561
|
+
this.#notify("Download", `Saved to ${result.path}`, "success");
|
|
562
|
+
} else if (!result.cancelled) {
|
|
563
|
+
this.#notify("Download", result.error || "Failed to save file", "error");
|
|
564
|
+
}
|
|
565
|
+
} else {
|
|
566
|
+
const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
|
|
567
|
+
const url = URL.createObjectURL(blob);
|
|
568
|
+
const link = document.createElement("a");
|
|
569
|
+
link.href = url;
|
|
570
|
+
link.download = filename;
|
|
571
|
+
link.click();
|
|
572
|
+
URL.revokeObjectURL(url);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
#csvEscape(value) {
|
|
576
|
+
if (value == null) return "";
|
|
577
|
+
const str = String(value);
|
|
578
|
+
if (/[",\n]/.test(str)) return `"${str.replace(/"/g, '""')}"`;
|
|
579
|
+
return str;
|
|
580
|
+
}
|
|
581
|
+
// ── Notifications ────────────────────────────────────────────────────
|
|
582
|
+
#notify(title, message, severity = "info") {
|
|
583
|
+
this.services.eventBus?.emit?.("toast:show", { title, message, type: severity });
|
|
584
|
+
}
|
|
585
|
+
// ── Disposal ─────────────────────────────────────────────────────────
|
|
586
|
+
#dispose() {
|
|
587
|
+
if (this._resizeObserver) {
|
|
588
|
+
this._resizeObserver.disconnect();
|
|
589
|
+
this._resizeObserver = null;
|
|
590
|
+
}
|
|
591
|
+
if (this.plotDiv && window.Plotly) {
|
|
592
|
+
try {
|
|
593
|
+
purge(this.plotDiv);
|
|
594
|
+
} catch (e) {
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
_openPlotWindows.delete(this.seriesId);
|
|
598
|
+
this.window = null;
|
|
599
|
+
this.contentEl = null;
|
|
600
|
+
this.plotDiv = null;
|
|
601
|
+
this._yColumnsList = null;
|
|
602
|
+
this._chartCreated = false;
|
|
603
|
+
this._fullData = null;
|
|
604
|
+
this._numericColumns = null;
|
|
605
|
+
this._config = null;
|
|
606
|
+
}
|
|
607
|
+
// ── Default config factory ───────────────────────────────────────────
|
|
608
|
+
static createDefaultConfig(headers, numericColumns) {
|
|
609
|
+
const isNumeric = numericColumns instanceof Set ? (h) => numericColumns.has(h) : () => true;
|
|
610
|
+
let xColumn = "";
|
|
611
|
+
for (const h of headers) {
|
|
612
|
+
if (isNumeric(h) && this.classifyHeader(h) === "time") {
|
|
613
|
+
xColumn = h;
|
|
614
|
+
break;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
if (!xColumn) {
|
|
618
|
+
for (const h of headers) {
|
|
619
|
+
if (isNumeric(h)) {
|
|
620
|
+
xColumn = h;
|
|
621
|
+
break;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
const yColumns = [];
|
|
626
|
+
for (const h of headers) {
|
|
627
|
+
if (h === xColumn) continue;
|
|
628
|
+
if (!isNumeric(h)) continue;
|
|
629
|
+
if (yColumns.length >= MAX_AUTO_COLUMNS) break;
|
|
630
|
+
yColumns.push({ header: h, color: getSeriesColor(yColumns.length) });
|
|
631
|
+
}
|
|
632
|
+
return {
|
|
633
|
+
xColumn,
|
|
634
|
+
yColumns,
|
|
635
|
+
chartType: "line",
|
|
636
|
+
showDataPoints: false,
|
|
637
|
+
interpolation: "linear",
|
|
638
|
+
legendPosition: "top"
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
};
|
|
642
|
+
var _openPlotWindows = /* @__PURE__ */ new Map();
|
|
643
|
+
async function openDataSeriesPlotWindow(options) {
|
|
644
|
+
const { seriesId } = options;
|
|
645
|
+
let win = _openPlotWindows.get(seriesId);
|
|
646
|
+
if (win?.window?.isVisible) {
|
|
647
|
+
win.window.bringToFront();
|
|
648
|
+
return win;
|
|
649
|
+
}
|
|
650
|
+
win = new DataSeriesPlotWindow(options);
|
|
651
|
+
_openPlotWindows.set(seriesId, win);
|
|
652
|
+
await win.show();
|
|
653
|
+
return win;
|
|
654
|
+
}
|
|
655
|
+
function closeDataSeriesPlotWindow(seriesId) {
|
|
656
|
+
const win = _openPlotWindows.get(seriesId);
|
|
657
|
+
if (win) {
|
|
658
|
+
win.close();
|
|
659
|
+
_openPlotWindows.delete(seriesId);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// src/charts/downsample.js
|
|
664
|
+
function strideIndices(n, targetPoints) {
|
|
665
|
+
if (!Number.isFinite(n) || n <= 0) return null;
|
|
666
|
+
const target = Math.max(2, Math.floor(targetPoints) || 0);
|
|
667
|
+
if (n <= target) return null;
|
|
668
|
+
const step = (n - 1) / (target - 1);
|
|
669
|
+
const idx = new Array(target);
|
|
670
|
+
for (let i = 0; i < target; i++) idx[i] = Math.round(i * step);
|
|
671
|
+
idx[target - 1] = n - 1;
|
|
672
|
+
let w = 0;
|
|
673
|
+
for (let r = 0; r < idx.length; r++) {
|
|
674
|
+
if (w === 0 || idx[r] > idx[w - 1]) idx[w++] = idx[r];
|
|
675
|
+
}
|
|
676
|
+
idx.length = w;
|
|
677
|
+
return idx;
|
|
678
|
+
}
|
|
679
|
+
function strideDownsampleShared(time, series, targetPoints) {
|
|
680
|
+
const idx = strideIndices(time?.length ?? 0, targetPoints);
|
|
681
|
+
if (!idx) return { time, series };
|
|
682
|
+
const dsTime = idx.map((i) => time[i]);
|
|
683
|
+
const dsSeries = {};
|
|
684
|
+
for (const [name, arr] of Object.entries(series || {})) {
|
|
685
|
+
if (!Array.isArray(arr)) {
|
|
686
|
+
dsSeries[name] = arr;
|
|
687
|
+
continue;
|
|
688
|
+
}
|
|
689
|
+
dsSeries[name] = idx.map((i) => i < arr.length ? arr[i] : null);
|
|
690
|
+
}
|
|
691
|
+
return { time: dsTime, series: dsSeries };
|
|
692
|
+
}
|
|
693
|
+
function lttb(x, y, threshold) {
|
|
694
|
+
const n = Math.min(x?.length ?? 0, y?.length ?? 0);
|
|
695
|
+
const target = Math.floor(threshold) || 0;
|
|
696
|
+
if (target <= 0 || target >= n) return { x: x.slice(0, n), y: y.slice(0, n) };
|
|
697
|
+
if (target < 3) return { x: [x[0], x[n - 1]], y: [y[0], y[n - 1]] };
|
|
698
|
+
const num = (v) => Number.isFinite(v) ? v : 0;
|
|
699
|
+
const outX = [x[0]];
|
|
700
|
+
const outY = [y[0]];
|
|
701
|
+
const bucketSize = (n - 2) / (target - 2);
|
|
702
|
+
let a = 0;
|
|
703
|
+
for (let i = 0; i < target - 2; i++) {
|
|
704
|
+
let nextStart = Math.floor((i + 1) * bucketSize) + 1;
|
|
705
|
+
let nextEnd = Math.min(Math.floor((i + 2) * bucketSize) + 1, n);
|
|
706
|
+
if (nextEnd <= nextStart) nextEnd = Math.min(nextStart + 1, n);
|
|
707
|
+
let avgX = 0, avgY = 0;
|
|
708
|
+
const count = nextEnd - nextStart;
|
|
709
|
+
for (let j = nextStart; j < nextEnd; j++) {
|
|
710
|
+
avgX += x[j];
|
|
711
|
+
avgY += num(y[j]);
|
|
712
|
+
}
|
|
713
|
+
avgX /= count;
|
|
714
|
+
avgY /= count;
|
|
715
|
+
const curStart = Math.floor(i * bucketSize) + 1;
|
|
716
|
+
const curEnd = Math.min(Math.floor((i + 1) * bucketSize) + 1, n);
|
|
717
|
+
const ax = x[a], ay = num(y[a]);
|
|
718
|
+
let maxArea = -1, chosen = curStart;
|
|
719
|
+
for (let j = curStart; j < curEnd; j++) {
|
|
720
|
+
const area = Math.abs((ax - avgX) * (num(y[j]) - ay) - (ax - x[j]) * (avgY - ay));
|
|
721
|
+
if (area > maxArea) {
|
|
722
|
+
maxArea = area;
|
|
723
|
+
chosen = j;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
outX.push(x[chosen]);
|
|
727
|
+
outY.push(y[chosen]);
|
|
728
|
+
a = chosen;
|
|
729
|
+
}
|
|
730
|
+
outX.push(x[n - 1]);
|
|
731
|
+
outY.push(y[n - 1]);
|
|
732
|
+
return { x: outX, y: outY };
|
|
733
|
+
}
|
|
734
|
+
export {
|
|
735
|
+
ALL_CHART_TYPES,
|
|
736
|
+
CHART_TYPES_2D,
|
|
737
|
+
CHART_TYPES_3D,
|
|
738
|
+
COLOR_PALETTE,
|
|
739
|
+
DARK_THEME_LAYOUT,
|
|
740
|
+
DEFAULT_CONFIG,
|
|
741
|
+
PLOT_LAYOUTS,
|
|
742
|
+
PlotPopoutWindow,
|
|
743
|
+
applyNanHandling,
|
|
744
|
+
build3DSceneLayout,
|
|
745
|
+
buildBoxTrace,
|
|
746
|
+
buildFanBandTraces,
|
|
747
|
+
buildHeatmapTrace,
|
|
748
|
+
buildHistogramTrace,
|
|
749
|
+
buildLegendLayout,
|
|
750
|
+
buildOverlayShapes,
|
|
751
|
+
buildTrace,
|
|
752
|
+
buildXAxisLayout,
|
|
753
|
+
buildYAxisLayout,
|
|
754
|
+
closeDataSeriesPlotWindow,
|
|
755
|
+
createChart,
|
|
756
|
+
createChartDeferred,
|
|
757
|
+
ensurePlotly,
|
|
758
|
+
extendTraces,
|
|
759
|
+
getSeriesColor,
|
|
760
|
+
hasPlot,
|
|
761
|
+
hexToRgba,
|
|
762
|
+
is3DChart,
|
|
763
|
+
lttb,
|
|
764
|
+
makeDefaultSubplot,
|
|
765
|
+
migrateData,
|
|
766
|
+
migrateSingleChart,
|
|
767
|
+
openDataSeriesPlotWindow,
|
|
768
|
+
openPlotPopoutWindow,
|
|
769
|
+
openRawTracesWindow,
|
|
770
|
+
purge,
|
|
771
|
+
relayout,
|
|
772
|
+
resize,
|
|
773
|
+
restyle,
|
|
774
|
+
setPlotlySource,
|
|
775
|
+
strideDownsampleShared,
|
|
776
|
+
strideIndices,
|
|
777
|
+
uid,
|
|
778
|
+
updateChart
|
|
779
|
+
};
|
|
780
|
+
//# sourceMappingURL=charts.js.map
|