@devtable/dashboard 1.24.0 → 1.27.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.
@@ -33,15 +33,15 @@ var __publicField = (obj, key, value) => {
33
33
  __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
34
34
  return value;
35
35
  };
36
- import React from "react";
36
+ import React, { createContext, useContext, useReducer } from "react";
37
37
  import _ from "lodash";
38
38
  import RGL, { WidthProvider } from "react-grid-layout";
39
- import { Popover, Tooltip, Group, Text, ActionIcon, Box, Button, TextInput, LoadingOverlay, Table, Select, useMantineTheme, ColorSwatch, Switch, Slider, SegmentedControl, NumberInput, Accordion, ColorInput, Divider, JsonInput, Modal, AppShell, Tabs, Menu, Container, Textarea } from "@mantine/core";
39
+ import { Box, Group, Button, Modal, Popover, Tooltip, Text, ActionIcon, TextInput, LoadingOverlay, Table, Select, useMantineTheme, ColorSwatch, Switch, Slider, SegmentedControl, NumberInput, ColorInput, Divider, Accordion, JsonInput, AppShell, Tabs, Menu, Container, Textarea } from "@mantine/core";
40
40
  import { useRequest } from "ahooks";
41
41
  import axios from "axios";
42
- import { InfoCircle, DeviceFloppy, Refresh, Trash, PlaylistAdd, Settings, Resize, Paint, PlayerPlay, Database, Recycle, Share } from "tabler-icons-react";
42
+ import { InfoCircle, DeviceFloppy, Refresh, Trash, PlaylistAdd, Settings, Copy, Resize, Paint, PlayerPlay, Database, Recycle, Share } from "tabler-icons-react";
43
43
  import RichTextEditor, { RichTextEditor as RichTextEditor$1 } from "@mantine/rte";
44
- import { useInputState, useElementSize, randomId } from "@mantine/hooks";
44
+ import { randomId, useInputState, useElementSize } from "@mantine/hooks";
45
45
  import ReactEChartsCore from "echarts-for-react/lib/core";
46
46
  import * as echarts from "echarts/core";
47
47
  import { SunburstChart, BarChart, LineChart, ScatterChart, PieChart } from "echarts/charts";
@@ -58,7 +58,7 @@ var DashboardMode = /* @__PURE__ */ ((DashboardMode2) => {
58
58
  DashboardMode2["Edit"] = "edit";
59
59
  return DashboardMode2;
60
60
  })(DashboardMode || {});
61
- const initialContext$3 = {
61
+ const initialContext$4 = {
62
62
  layoutFrozen: false,
63
63
  freezeLayout: () => {
64
64
  },
@@ -67,7 +67,7 @@ const initialContext$3 = {
67
67
  inLayoutMode: false,
68
68
  inUseMode: true
69
69
  };
70
- const LayoutStateContext = React.createContext(initialContext$3);
70
+ const LayoutStateContext = React.createContext(initialContext$4);
71
71
  function explainSQLSnippet(snippet, context) {
72
72
  const names = Object.keys(context);
73
73
  const vals = Object.values(context);
@@ -162,10 +162,11 @@ async function getQuerySources() {
162
162
  return {};
163
163
  }
164
164
  }
165
- const initialContext$2 = {};
166
- const initialContextInfoContext = initialContext$2;
167
- const ContextInfoContext = React.createContext(initialContext$2);
168
- const initialContext$1 = {
165
+ const initialContext$3 = {};
166
+ const initialContextInfoContext = initialContext$3;
167
+ const ContextInfoContext = React.createContext(initialContext$3);
168
+ const initialContext$2 = {
169
+ id: "",
169
170
  data: [],
170
171
  loading: false,
171
172
  title: "",
@@ -186,16 +187,96 @@ const initialContext$1 = {
186
187
  refreshData: () => {
187
188
  }
188
189
  };
189
- const PanelContext = React.createContext(initialContext$1);
190
- const initialContext = {
191
- sqlSnippets: [],
192
- setSQLSnippets: () => {
193
- },
194
- queries: [],
195
- setQueries: () => {
190
+ const PanelContext = React.createContext(initialContext$2);
191
+ const ModalsContext = createContext(null);
192
+ ModalsContext.displayName = "@mantine/modals/ModalsContext";
193
+ function useModals() {
194
+ const ctx = useContext(ModalsContext);
195
+ if (!ctx) {
196
+ throw new Error("[@mantine/modals] useModals hook was called outside of context, wrap your app with ModalsProvider component");
196
197
  }
198
+ return ctx;
199
+ }
200
+ var __defProp$1 = Object.defineProperty;
201
+ var __defProps$1 = Object.defineProperties;
202
+ var __getOwnPropDescs$1 = Object.getOwnPropertyDescriptors;
203
+ var __getOwnPropSymbols$1 = Object.getOwnPropertySymbols;
204
+ var __hasOwnProp$1 = Object.prototype.hasOwnProperty;
205
+ var __propIsEnum$1 = Object.prototype.propertyIsEnumerable;
206
+ var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
207
+ var __spreadValues$1 = (a, b) => {
208
+ for (var prop in b || (b = {}))
209
+ if (__hasOwnProp$1.call(b, prop))
210
+ __defNormalProp$1(a, prop, b[prop]);
211
+ if (__getOwnPropSymbols$1)
212
+ for (var prop of __getOwnPropSymbols$1(b)) {
213
+ if (__propIsEnum$1.call(b, prop))
214
+ __defNormalProp$1(a, prop, b[prop]);
215
+ }
216
+ return a;
197
217
  };
198
- const DefinitionContext = React.createContext(initialContext);
218
+ var __spreadProps$1 = (a, b) => __defProps$1(a, __getOwnPropDescs$1(b));
219
+ function ConfirmModal({
220
+ id,
221
+ cancelProps,
222
+ confirmProps,
223
+ labels = { cancel: "", confirm: "" },
224
+ closeOnConfirm = true,
225
+ closeOnCancel = true,
226
+ groupProps,
227
+ onCancel,
228
+ onConfirm,
229
+ children
230
+ }) {
231
+ const { cancel: cancelLabel, confirm: confirmLabel } = labels;
232
+ const ctx = useModals();
233
+ const handleCancel = (event) => {
234
+ typeof (cancelProps == null ? void 0 : cancelProps.onClick) === "function" && (cancelProps == null ? void 0 : cancelProps.onClick(event));
235
+ typeof onCancel === "function" && onCancel();
236
+ closeOnCancel && ctx.closeModal(id);
237
+ };
238
+ const handleConfirm = (event) => {
239
+ typeof (confirmProps == null ? void 0 : confirmProps.onClick) === "function" && (confirmProps == null ? void 0 : confirmProps.onClick(event));
240
+ typeof onConfirm === "function" && onConfirm();
241
+ closeOnConfirm && ctx.closeModal(id);
242
+ };
243
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, children && /* @__PURE__ */ React.createElement(Box, {
244
+ mb: "md"
245
+ }, children), /* @__PURE__ */ React.createElement(Group, __spreadValues$1({
246
+ position: "right"
247
+ }, groupProps), /* @__PURE__ */ React.createElement(Button, __spreadProps$1(__spreadValues$1({
248
+ variant: "default"
249
+ }, cancelProps), {
250
+ onClick: handleCancel
251
+ }), (cancelProps == null ? void 0 : cancelProps.children) || cancelLabel), /* @__PURE__ */ React.createElement(Button, __spreadProps$1(__spreadValues$1({}, confirmProps), {
252
+ onClick: handleConfirm
253
+ }), (confirmProps == null ? void 0 : confirmProps.children) || confirmLabel)));
254
+ }
255
+ function modalsReducer(state, action) {
256
+ switch (action.type) {
257
+ case "OPEN": {
258
+ return {
259
+ current: action.payload,
260
+ modals: [...state.modals, action.payload]
261
+ };
262
+ }
263
+ case "CLOSE": {
264
+ return {
265
+ current: state.modals[state.modals.length - 2] || null,
266
+ modals: state.modals.filter((m2) => m2.id !== action.payload)
267
+ };
268
+ }
269
+ case "CLOSE_ALL": {
270
+ return {
271
+ current: state.current,
272
+ modals: []
273
+ };
274
+ }
275
+ default: {
276
+ return state;
277
+ }
278
+ }
279
+ }
199
280
  var commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {};
200
281
  var jsxRuntime = { exports: {} };
201
282
  var reactJsxRuntime_production_min = {};
@@ -230,6 +311,240 @@ reactJsxRuntime_production_min.jsxs = q;
230
311
  const jsx = jsxRuntime.exports.jsx;
231
312
  const jsxs = jsxRuntime.exports.jsxs;
232
313
  const Fragment = jsxRuntime.exports.Fragment;
314
+ var __defProp2 = Object.defineProperty;
315
+ var __defProps2 = Object.defineProperties;
316
+ var __getOwnPropDescs2 = Object.getOwnPropertyDescriptors;
317
+ var __getOwnPropSymbols2 = Object.getOwnPropertySymbols;
318
+ var __hasOwnProp2 = Object.prototype.hasOwnProperty;
319
+ var __propIsEnum2 = Object.prototype.propertyIsEnumerable;
320
+ var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, {
321
+ enumerable: true,
322
+ configurable: true,
323
+ writable: true,
324
+ value
325
+ }) : obj[key] = value;
326
+ var __spreadValues2 = (a, b) => {
327
+ for (var prop in b || (b = {}))
328
+ if (__hasOwnProp2.call(b, prop))
329
+ __defNormalProp2(a, prop, b[prop]);
330
+ if (__getOwnPropSymbols2)
331
+ for (var prop of __getOwnPropSymbols2(b)) {
332
+ if (__propIsEnum2.call(b, prop))
333
+ __defNormalProp2(a, prop, b[prop]);
334
+ }
335
+ return a;
336
+ };
337
+ var __spreadProps2 = (a, b) => __defProps2(a, __getOwnPropDescs2(b));
338
+ var __objRest2 = (source, exclude) => {
339
+ var target = {};
340
+ for (var prop in source)
341
+ if (__hasOwnProp2.call(source, prop) && exclude.indexOf(prop) < 0)
342
+ target[prop] = source[prop];
343
+ if (source != null && __getOwnPropSymbols2)
344
+ for (var prop of __getOwnPropSymbols2(source)) {
345
+ if (exclude.indexOf(prop) < 0 && __propIsEnum2.call(source, prop))
346
+ target[prop] = source[prop];
347
+ }
348
+ return target;
349
+ };
350
+ function separateConfirmModalProps(props) {
351
+ if (!props) {
352
+ return {
353
+ confirmProps: {},
354
+ modalProps: {}
355
+ };
356
+ }
357
+ const _a = props, {
358
+ id,
359
+ children,
360
+ onCancel,
361
+ onConfirm,
362
+ closeOnConfirm,
363
+ closeOnCancel,
364
+ cancelProps,
365
+ confirmProps,
366
+ groupProps,
367
+ labels
368
+ } = _a, others = __objRest2(_a, ["id", "children", "onCancel", "onConfirm", "closeOnConfirm", "closeOnCancel", "cancelProps", "confirmProps", "groupProps", "labels"]);
369
+ return {
370
+ confirmProps: {
371
+ id,
372
+ children,
373
+ onCancel,
374
+ onConfirm,
375
+ closeOnConfirm,
376
+ closeOnCancel,
377
+ cancelProps,
378
+ confirmProps,
379
+ groupProps,
380
+ labels
381
+ },
382
+ modalProps: __spreadValues2({
383
+ id
384
+ }, others)
385
+ };
386
+ }
387
+ function ModalsProvider({
388
+ children,
389
+ modalProps,
390
+ labels,
391
+ modals
392
+ }) {
393
+ const [state, dispatch] = useReducer(modalsReducer, {
394
+ modals: [],
395
+ current: null
396
+ });
397
+ const closeAll = (canceled) => {
398
+ state.modals.forEach((modal) => {
399
+ var _a, _b, _c, _d;
400
+ if (modal.type === "confirm" && canceled) {
401
+ (_b = (_a = modal.props) == null ? void 0 : _a.onCancel) == null ? void 0 : _b.call(_a);
402
+ }
403
+ (_d = (_c = modal.props) == null ? void 0 : _c.onClose) == null ? void 0 : _d.call(_c);
404
+ });
405
+ dispatch({
406
+ type: "CLOSE_ALL"
407
+ });
408
+ };
409
+ const openModal = (props) => {
410
+ const id = props.id || randomId();
411
+ dispatch({
412
+ type: "OPEN",
413
+ payload: {
414
+ id,
415
+ type: "content",
416
+ props
417
+ }
418
+ });
419
+ return id;
420
+ };
421
+ const openConfirmModal = (props) => {
422
+ const id = props.id || randomId();
423
+ dispatch({
424
+ type: "OPEN",
425
+ payload: {
426
+ id,
427
+ type: "confirm",
428
+ props
429
+ }
430
+ });
431
+ return id;
432
+ };
433
+ const openContextModal = (modal, props) => {
434
+ const id = props.id || randomId();
435
+ dispatch({
436
+ type: "OPEN",
437
+ payload: {
438
+ id,
439
+ type: "context",
440
+ props,
441
+ ctx: modal
442
+ }
443
+ });
444
+ return id;
445
+ };
446
+ const closeModal = (id, canceled) => {
447
+ var _a, _b, _c, _d;
448
+ if (state.modals.length <= 1) {
449
+ closeAll(canceled);
450
+ return;
451
+ }
452
+ const modal = state.modals.find((item) => item.id === id);
453
+ if ((modal == null ? void 0 : modal.type) === "confirm" && canceled) {
454
+ (_b = (_a = modal.props) == null ? void 0 : _a.onCancel) == null ? void 0 : _b.call(_a);
455
+ }
456
+ (_d = (_c = modal == null ? void 0 : modal.props) == null ? void 0 : _c.onClose) == null ? void 0 : _d.call(_c);
457
+ dispatch({
458
+ type: "CLOSE",
459
+ payload: modal.id
460
+ });
461
+ };
462
+ const ctx = {
463
+ modals: state.modals,
464
+ openModal,
465
+ openConfirmModal,
466
+ openContextModal,
467
+ closeModal,
468
+ closeAll
469
+ };
470
+ const getCurrentModal = () => {
471
+ var _a;
472
+ switch ((_a = state.current) == null ? void 0 : _a.type) {
473
+ case "context": {
474
+ const _b = state.current.props, {
475
+ innerProps
476
+ } = _b, rest = __objRest2(_b, ["innerProps"]);
477
+ const ContextModal = modals[state.current.ctx];
478
+ return {
479
+ modalProps: rest,
480
+ content: /* @__PURE__ */ jsx(ContextModal, {
481
+ innerProps,
482
+ context: ctx,
483
+ id: state.current.id
484
+ })
485
+ };
486
+ }
487
+ case "confirm": {
488
+ const {
489
+ modalProps: separatedModalProps,
490
+ confirmProps: separatedConfirmProps
491
+ } = separateConfirmModalProps(state.current.props);
492
+ return {
493
+ modalProps: separatedModalProps,
494
+ content: /* @__PURE__ */ jsx(ConfirmModal, __spreadValues({}, __spreadProps2(__spreadValues2({}, separatedConfirmProps), {
495
+ id: state.current.id,
496
+ labels: state.current.props.labels || labels
497
+ })))
498
+ };
499
+ }
500
+ case "content": {
501
+ const _c = state.current.props, {
502
+ children: currentModalChildren
503
+ } = _c, rest = __objRest2(_c, ["children"]);
504
+ return {
505
+ modalProps: rest,
506
+ content: /* @__PURE__ */ jsx(Fragment, {
507
+ children: currentModalChildren
508
+ })
509
+ };
510
+ }
511
+ default: {
512
+ return {
513
+ modalProps: {},
514
+ content: null
515
+ };
516
+ }
517
+ }
518
+ };
519
+ const {
520
+ modalProps: currentModalProps,
521
+ content
522
+ } = getCurrentModal();
523
+ return /* @__PURE__ */ jsxs(ModalsContext.Provider, {
524
+ value: ctx,
525
+ children: [/* @__PURE__ */ jsx(Modal, __spreadProps(__spreadValues({}, __spreadProps2(__spreadValues2(__spreadValues2({}, modalProps), currentModalProps), {
526
+ opened: state.modals.length > 0,
527
+ onClose: () => closeModal(state.current.id)
528
+ })), {
529
+ children: content
530
+ })), children]
531
+ });
532
+ }
533
+ const initialContext$1 = {
534
+ addPanel: _.noop,
535
+ duplidatePanel: _.noop,
536
+ removePanelByID: _.noop
537
+ };
538
+ const DashboardActionContext = React.createContext(initialContext$1);
539
+ const initialContext = {
540
+ sqlSnippets: [],
541
+ setSQLSnippets: () => {
542
+ },
543
+ queries: [],
544
+ setQueries: () => {
545
+ }
546
+ };
547
+ const DefinitionContext = React.createContext(initialContext);
233
548
  function DescriptionPopover({
234
549
  position = "bottom",
235
550
  trigger = "hover"
@@ -2008,7 +2323,7 @@ const defaultOption$1 = {
2008
2323
  }
2009
2324
  },
2010
2325
  grid: {
2011
- top: 30,
2326
+ top: 0,
2012
2327
  left: 15,
2013
2328
  right: 15,
2014
2329
  bottom: 30,
@@ -2102,439 +2417,81 @@ function getOption(conf, data) {
2102
2417
  };
2103
2418
  return _.merge({}, defaultOption$1, customOptions);
2104
2419
  }
2105
- echarts.use([BarChart, LineChart, ScatterChart, GridComponent, LegendComponent, TooltipComponent, CanvasRenderer]);
2106
- echarts.registerTransform(echartsStat.transform.regression);
2107
- function VizCartesianChart({
2108
- conf,
2109
- data,
2110
- width,
2111
- height
2112
- }) {
2113
- const option = React.useMemo(() => {
2114
- return getOption(conf, data);
2115
- }, [conf, data]);
2116
- if (!width || !height) {
2117
- return null;
2420
+ function median(numbers) {
2421
+ const sorted = Array.from(numbers).sort((a, b) => a - b);
2422
+ const middle = Math.floor(sorted.length / 2);
2423
+ if (sorted.length % 2 === 0) {
2424
+ return (sorted[middle - 1] + sorted[middle]) / 2;
2118
2425
  }
2119
- return /* @__PURE__ */ jsx(ReactEChartsCore, {
2120
- echarts,
2121
- option,
2122
- style: {
2123
- width,
2124
- height
2125
- }
2126
- });
2127
- }
2128
- var ValueType = /* @__PURE__ */ ((ValueType2) => {
2129
- ValueType2["string"] = "string";
2130
- ValueType2["number"] = "number";
2131
- ValueType2["eloc"] = "eloc";
2132
- ValueType2["percentage"] = "percentage";
2133
- return ValueType2;
2134
- })(ValueType || {});
2135
- function StringCell({
2136
- value
2137
- }) {
2138
- return /* @__PURE__ */ jsx(Text, {
2139
- component: "span",
2140
- children: value
2141
- });
2142
- }
2143
- function ELOCCell({
2144
- value
2145
- }) {
2146
- return /* @__PURE__ */ jsx(Text, {
2147
- component: "span",
2148
- children: value
2149
- });
2426
+ return sorted[middle];
2150
2427
  }
2151
- function NumberCell({
2152
- value
2153
- }) {
2154
- const num = numbro(value).format({
2155
- thousandSeparated: true
2156
- });
2157
- return /* @__PURE__ */ jsx(Text, {
2158
- component: "span",
2159
- children: num
2160
- });
2428
+ function aggregateValue(data, data_field, aggregation) {
2429
+ var _a, _b, _c;
2430
+ const numbers = data.map((d) => d[data_field]);
2431
+ switch (aggregation) {
2432
+ case "sum":
2433
+ return _.sum(numbers);
2434
+ case "mean":
2435
+ return _.mean(numbers);
2436
+ case "median":
2437
+ return median(numbers);
2438
+ case "max":
2439
+ return (_a = _.max(numbers)) != null ? _a : 0;
2440
+ case "min":
2441
+ return (_b = _.min(numbers)) != null ? _b : 0;
2442
+ default:
2443
+ return (_c = data[0]) == null ? void 0 : _c[data_field];
2444
+ }
2161
2445
  }
2162
- function PercentageCell({
2163
- value
2164
- }) {
2165
- const num = numbro(value).format({
2166
- output: "percent",
2167
- mantissa: 3
2168
- });
2169
- return /* @__PURE__ */ jsx(Text, {
2170
- component: "span",
2171
- children: num
2172
- });
2446
+ var invariant = function() {
2447
+ };
2448
+ const clamp$1 = (min, max, v) => Math.min(Math.max(v, min), max);
2449
+ const progress = (from, to, value) => {
2450
+ const toFromDifference = to - from;
2451
+ return toFromDifference === 0 ? 1 : (value - from) / toFromDifference;
2452
+ };
2453
+ const mix = (from, to, progress2) => -progress2 * from + progress2 * to + from;
2454
+ const clamp = (min, max) => (v) => Math.max(Math.min(v, max), min);
2455
+ const sanitize = (v) => v % 1 ? Number(v.toFixed(5)) : v;
2456
+ const floatRegex = /(-)?([\d]*\.?[\d])+/g;
2457
+ const colorRegex = /(#[0-9a-f]{6}|#[0-9a-f]{3}|#(?:[0-9a-f]{2}){2,4}|(rgb|hsl)a?\((-?[\d\.]+%?[,\s]+){2,3}\s*\/*\s*[\d\.]+%?\))/gi;
2458
+ const singleColorRegex = /^(#[0-9a-f]{3}|#(?:[0-9a-f]{2}){2,4}|(rgb|hsl)a?\((-?[\d\.]+%?[,\s]+){2,3}\s*\/*\s*[\d\.]+%?\))$/i;
2459
+ function isString(v) {
2460
+ return typeof v === "string";
2173
2461
  }
2174
- function CellValue({
2175
- value,
2176
- type
2177
- }) {
2178
- switch (type) {
2179
- case ValueType.string:
2180
- return /* @__PURE__ */ jsx(StringCell, {
2181
- value
2182
- });
2183
- case ValueType.eloc:
2184
- return /* @__PURE__ */ jsx(ELOCCell, {
2185
- value
2186
- });
2187
- case ValueType.number:
2188
- return /* @__PURE__ */ jsx(NumberCell, {
2189
- value
2190
- });
2191
- case ValueType.percentage:
2192
- return /* @__PURE__ */ jsx(PercentageCell, {
2193
- value
2194
- });
2195
- }
2196
- }
2197
- function VizTable({
2198
- conf,
2199
- data = [],
2200
- width,
2201
- height
2202
- }) {
2203
- const _a = conf, {
2204
- id_field,
2205
- use_raw_columns,
2206
- columns
2207
- } = _a, rest = __objRest(_a, [
2208
- "id_field",
2209
- "use_raw_columns",
2210
- "columns"
2211
- ]);
2212
- const labels = React.useMemo(() => {
2213
- if (use_raw_columns) {
2214
- return Object.keys(data == null ? void 0 : data[0]);
2215
- }
2216
- return columns.map((c) => c.label);
2217
- }, [use_raw_columns, columns, data]);
2218
- const finalColumns = React.useMemo(() => {
2219
- if (use_raw_columns) {
2220
- return Object.keys(data == null ? void 0 : data[0]).map((k2) => ({
2221
- label: k2,
2222
- value_field: k2,
2223
- value_type: ValueType.string
2224
- }));
2225
- }
2226
- return columns;
2227
- }, [use_raw_columns, columns, data]);
2228
- return /* @__PURE__ */ jsxs(Table, __spreadProps(__spreadValues({
2229
- sx: {
2230
- maxHeight: height
2231
- }
2232
- }, rest), {
2233
- children: [/* @__PURE__ */ jsx("thead", {
2234
- children: /* @__PURE__ */ jsx("tr", {
2235
- children: labels.map((label) => /* @__PURE__ */ jsx("th", {
2236
- children: label
2237
- }, label))
2238
- })
2239
- }), /* @__PURE__ */ jsx("tbody", {
2240
- children: data.slice(0, 30).map((row, index2) => /* @__PURE__ */ jsx("tr", {
2241
- children: finalColumns.map(({
2242
- value_field,
2243
- value_type
2244
- }) => /* @__PURE__ */ jsx("td", {
2245
- children: /* @__PURE__ */ jsx(Group, {
2246
- sx: {
2247
- "&, .mantine-Text-root": {
2248
- fontFamily: "monospace",
2249
- fontSize: rest.fontSize
2250
- }
2251
- },
2252
- children: /* @__PURE__ */ jsx(CellValue, {
2253
- value: row[value_field],
2254
- type: value_type
2255
- })
2256
- })
2257
- }, `${value_field}--${row[value_field]}`))
2258
- }, id_field ? row[id_field] : `row-${index2}`))
2259
- }), data.length > 100 && /* @__PURE__ */ jsx("tfoot", {
2260
- children: /* @__PURE__ */ jsx("tr", {
2261
- children: /* @__PURE__ */ jsx("td", {
2262
- colSpan: labels.length,
2263
- children: /* @__PURE__ */ jsx(Text, {
2264
- color: "red",
2265
- size: "sm",
2266
- children: "Showing only the first 30 rows to avoid causing slow performance"
2267
- })
2268
- })
2269
- })
2270
- })]
2271
- }));
2272
- }
2273
- function interpolateString(template, params = {}) {
2274
- const extendedParams = __spreadProps(__spreadValues({}, params), {
2275
- numbro
2276
- });
2277
- const names = Object.keys(extendedParams);
2278
- const vals = Object.values(extendedParams);
2279
- try {
2280
- return new Function(...names, `return \`${template}\`;`)(...vals);
2281
- } catch (error) {
2282
- return error.message;
2283
- }
2284
- }
2285
- function VizText({
2286
- conf: {
2287
- paragraphs
2288
- },
2289
- data
2290
- }) {
2291
- return /* @__PURE__ */ jsx(Fragment, {
2292
- children: paragraphs.map((_a, index2) => {
2293
- var _b = _a, {
2294
- template,
2295
- size
2296
- } = _b, rest = __objRest(_b, [
2297
- "template",
2298
- "size"
2299
- ]);
2300
- return /* @__PURE__ */ jsx(Text, __spreadProps(__spreadValues({}, rest), {
2301
- sx: {
2302
- fontSize: size
2303
- },
2304
- children: interpolateString(template, data[0])
2305
- }), `${template}---${index2}`);
2306
- })
2307
- });
2308
- }
2309
- echarts.use([GridComponent, VisualMapComponent, LegendComponent, TooltipComponent, CanvasRenderer]);
2310
- function VizBar3D({
2311
- conf,
2312
- data,
2313
- width,
2314
- height
2315
- }) {
2316
- const _a = conf, {
2317
- x_axis_data_key,
2318
- y_axis_data_key,
2319
- z_axis_data_key
2320
- } = _a, restConf = __objRest(_a, [
2321
- "x_axis_data_key",
2322
- "y_axis_data_key",
2323
- "z_axis_data_key"
2324
- ]);
2325
- const min = React.useMemo(() => {
2326
- return _.minBy(data, (d) => d[z_axis_data_key])[z_axis_data_key];
2327
- }, [data, z_axis_data_key]);
2328
- const max = React.useMemo(() => {
2329
- return _.maxBy(data, (d) => d[z_axis_data_key])[z_axis_data_key];
2330
- }, [data, z_axis_data_key]);
2331
- const option = __spreadProps(__spreadValues({
2332
- tooltip: {},
2333
- backgroundColor: "#fff",
2334
- visualMap: {
2335
- show: true,
2336
- dimension: 2,
2337
- min,
2338
- max,
2339
- inRange: {
2340
- color: ["#313695", "#4575b4", "#74add1", "#abd9e9", "#e0f3f8", "#ffffbf", "#fee090", "#fdae61", "#f46d43", "#d73027", "#a50026"]
2341
- }
2342
- },
2343
- xAxis3D: {
2344
- type: "value"
2345
- },
2346
- yAxis3D: {
2347
- type: "value"
2348
- },
2349
- zAxis3D: {
2350
- type: "value"
2351
- },
2352
- grid3D: {
2353
- viewControl: {
2354
- projection: "orthographic",
2355
- autoRotate: false
2356
- },
2357
- light: {
2358
- main: {
2359
- shadow: true,
2360
- quality: "ultra",
2361
- intensity: 1.5
2362
- }
2363
- }
2364
- }
2365
- }, restConf), {
2366
- series: [{
2367
- type: "bar3D",
2368
- wireframe: {},
2369
- data: data.map((d) => [d[x_axis_data_key], d[y_axis_data_key], d[z_axis_data_key]])
2370
- }]
2371
- });
2372
- return /* @__PURE__ */ jsx(ReactEChartsCore, {
2373
- echarts,
2374
- option,
2375
- style: {
2376
- width,
2377
- height
2378
- }
2379
- });
2380
- }
2381
- var index$2 = "";
2382
- echarts.use([PieChart, CanvasRenderer]);
2383
- const defaultOption = {
2384
- tooltip: {
2385
- show: true
2386
- },
2387
- series: {
2388
- type: "pie",
2389
- radius: ["50%", "80%"],
2390
- label: {
2391
- position: "outer",
2392
- alignTo: "edge",
2393
- formatter: "{name|{b}}\n{percentage|{d}%}",
2394
- minMargin: 5,
2395
- edgeDistance: 10,
2396
- lineHeight: 15,
2397
- rich: {
2398
- percentage: {
2399
- color: "#999"
2400
- }
2401
- },
2402
- margin: 20
2403
- },
2404
- labelLine: {
2405
- length: 15,
2406
- length2: 0,
2407
- maxSurfaceAngle: 80,
2408
- showAbove: true
2409
- },
2410
- top: 10,
2411
- bottom: 10,
2412
- left: 10,
2413
- right: 10
2414
- }
2415
- };
2416
- function VizPie({
2417
- conf,
2418
- data,
2419
- width,
2420
- height
2421
- }) {
2422
- const _a = conf, {
2423
- label_field = "name",
2424
- value_field = "value"
2425
- } = _a, restConf = __objRest(_a, [
2426
- "label_field",
2427
- "value_field"
2428
- ]);
2429
- const chartData = React.useMemo(() => {
2430
- return data.map((d) => ({
2431
- name: d[label_field],
2432
- value: Number(d[value_field])
2433
- }));
2434
- }, [data, label_field, value_field]);
2435
- const labelOptions = React.useMemo(() => {
2436
- return {
2437
- series: {
2438
- labelLayout: function(params) {
2439
- const isLeft = params.labelRect.x < width / 2;
2440
- const points = params.labelLinePoints;
2441
- points[2][0] = isLeft ? params.labelRect.x : params.labelRect.x + params.labelRect.width;
2442
- return {
2443
- labelLinePoints: points
2444
- };
2445
- }
2446
- }
2447
- };
2448
- }, [width]);
2449
- const option = _.merge({}, defaultOption, labelOptions, restConf, {
2450
- series: {
2451
- data: chartData
2452
- }
2453
- });
2454
- return /* @__PURE__ */ jsx(ReactEChartsCore, {
2455
- echarts,
2456
- option,
2457
- style: {
2458
- width,
2459
- height
2460
- }
2461
- });
2462
- }
2463
- function median(numbers) {
2464
- const sorted = Array.from(numbers).sort((a, b) => a - b);
2465
- const middle = Math.floor(sorted.length / 2);
2466
- if (sorted.length % 2 === 0) {
2467
- return (sorted[middle - 1] + sorted[middle]) / 2;
2468
- }
2469
- return sorted[middle];
2470
- }
2471
- function aggregateValue(data, data_field, aggregation) {
2472
- var _a, _b, _c;
2473
- const numbers = data.map((d) => d[data_field]);
2474
- switch (aggregation) {
2475
- case "sum":
2476
- return _.sum(numbers);
2477
- case "mean":
2478
- return _.mean(numbers);
2479
- case "median":
2480
- return median(numbers);
2481
- case "max":
2482
- return (_a = _.max(numbers)) != null ? _a : 0;
2483
- case "min":
2484
- return (_b = _.min(numbers)) != null ? _b : 0;
2485
- default:
2486
- return (_c = data[0]) == null ? void 0 : _c[data_field];
2487
- }
2488
- }
2489
- var invariant = function() {
2490
- };
2491
- const clamp$1 = (min, max, v) => Math.min(Math.max(v, min), max);
2492
- const progress = (from, to, value) => {
2493
- const toFromDifference = to - from;
2494
- return toFromDifference === 0 ? 1 : (value - from) / toFromDifference;
2495
- };
2496
- const mix = (from, to, progress2) => -progress2 * from + progress2 * to + from;
2497
- const clamp = (min, max) => (v) => Math.max(Math.min(v, max), min);
2498
- const sanitize = (v) => v % 1 ? Number(v.toFixed(5)) : v;
2499
- const floatRegex = /(-)?([\d]*\.?[\d])+/g;
2500
- const colorRegex = /(#[0-9a-f]{6}|#[0-9a-f]{3}|#(?:[0-9a-f]{2}){2,4}|(rgb|hsl)a?\((-?[\d\.]+%?[,\s]+){2,3}\s*\/*\s*[\d\.]+%?\))/gi;
2501
- const singleColorRegex = /^(#[0-9a-f]{3}|#(?:[0-9a-f]{2}){2,4}|(rgb|hsl)a?\((-?[\d\.]+%?[,\s]+){2,3}\s*\/*\s*[\d\.]+%?\))$/i;
2502
- function isString(v) {
2503
- return typeof v === "string";
2504
- }
2505
- const number = {
2506
- test: (v) => typeof v === "number",
2507
- parse: parseFloat,
2508
- transform: (v) => v
2509
- };
2510
- const alpha = Object.assign(Object.assign({}, number), { transform: clamp(0, 1) });
2511
- Object.assign(Object.assign({}, number), { default: 1 });
2512
- const createUnitType = (unit) => ({
2513
- test: (v) => isString(v) && v.endsWith(unit) && v.split(" ").length === 1,
2514
- parse: parseFloat,
2515
- transform: (v) => `${v}${unit}`
2516
- });
2517
- const percent = createUnitType("%");
2518
- Object.assign(Object.assign({}, percent), { parse: (v) => percent.parse(v) / 100, transform: (v) => percent.transform(v * 100) });
2519
- const isColorString = (type, testProp) => (v) => {
2520
- return Boolean(isString(v) && singleColorRegex.test(v) && v.startsWith(type) || testProp && Object.prototype.hasOwnProperty.call(v, testProp));
2521
- };
2522
- const splitColor = (aName, bName, cName) => (v) => {
2523
- if (!isString(v))
2524
- return v;
2525
- const [a, b, c, alpha2] = v.match(floatRegex);
2526
- return {
2527
- [aName]: parseFloat(a),
2528
- [bName]: parseFloat(b),
2529
- [cName]: parseFloat(c),
2530
- alpha: alpha2 !== void 0 ? parseFloat(alpha2) : 1
2531
- };
2532
- };
2533
- const hsla = {
2534
- test: isColorString("hsl", "hue"),
2535
- parse: splitColor("hue", "saturation", "lightness"),
2536
- transform: ({ hue, saturation, lightness, alpha: alpha$1 = 1 }) => {
2537
- return "hsla(" + Math.round(hue) + ", " + percent.transform(sanitize(saturation)) + ", " + percent.transform(sanitize(lightness)) + ", " + sanitize(alpha.transform(alpha$1)) + ")";
2462
+ const number = {
2463
+ test: (v) => typeof v === "number",
2464
+ parse: parseFloat,
2465
+ transform: (v) => v
2466
+ };
2467
+ const alpha = Object.assign(Object.assign({}, number), { transform: clamp(0, 1) });
2468
+ Object.assign(Object.assign({}, number), { default: 1 });
2469
+ const createUnitType = (unit) => ({
2470
+ test: (v) => isString(v) && v.endsWith(unit) && v.split(" ").length === 1,
2471
+ parse: parseFloat,
2472
+ transform: (v) => `${v}${unit}`
2473
+ });
2474
+ const percent = createUnitType("%");
2475
+ Object.assign(Object.assign({}, percent), { parse: (v) => percent.parse(v) / 100, transform: (v) => percent.transform(v * 100) });
2476
+ const isColorString = (type, testProp) => (v) => {
2477
+ return Boolean(isString(v) && singleColorRegex.test(v) && v.startsWith(type) || testProp && Object.prototype.hasOwnProperty.call(v, testProp));
2478
+ };
2479
+ const splitColor = (aName, bName, cName) => (v) => {
2480
+ if (!isString(v))
2481
+ return v;
2482
+ const [a, b, c, alpha2] = v.match(floatRegex);
2483
+ return {
2484
+ [aName]: parseFloat(a),
2485
+ [bName]: parseFloat(b),
2486
+ [cName]: parseFloat(c),
2487
+ alpha: alpha2 !== void 0 ? parseFloat(alpha2) : 1
2488
+ };
2489
+ };
2490
+ const hsla = {
2491
+ test: isColorString("hsl", "hue"),
2492
+ parse: splitColor("hue", "saturation", "lightness"),
2493
+ transform: ({ hue, saturation, lightness, alpha: alpha$1 = 1 }) => {
2494
+ return "hsla(" + Math.round(hue) + ", " + percent.transform(sanitize(saturation)) + ", " + percent.transform(sanitize(lightness)) + ", " + sanitize(alpha.transform(alpha$1)) + ")";
2538
2495
  }
2539
2496
  };
2540
2497
  const clampRgbUnit = clamp(0, 255);
@@ -2861,85 +2818,494 @@ function getColorByColorConf(conf, value) {
2861
2818
  }
2862
2819
  return "black";
2863
2820
  }
2864
- function getNonStatsDataText(data) {
2865
- if (data === null) {
2866
- return "null";
2867
- }
2868
- if (data === void 0) {
2869
- return "undefined";
2870
- }
2871
- if (Array.isArray(data)) {
2872
- return `Array(${data.length})`;
2821
+ function getNonStatsDataText(data) {
2822
+ if (data === null) {
2823
+ return "null";
2824
+ }
2825
+ if (data === void 0) {
2826
+ return "undefined";
2827
+ }
2828
+ if (Array.isArray(data)) {
2829
+ return `Array(${data.length})`;
2830
+ }
2831
+ return data.toString();
2832
+ }
2833
+ function variablesToElements(variables, data) {
2834
+ const ret = {};
2835
+ variables.forEach(({
2836
+ name,
2837
+ color: color2,
2838
+ data_field,
2839
+ aggregation,
2840
+ size,
2841
+ weight,
2842
+ formatter
2843
+ }) => {
2844
+ const value = aggregateValue(data, data_field, aggregation);
2845
+ let valueContent = "";
2846
+ if (!["string", "number"].includes(typeof value)) {
2847
+ valueContent = getNonStatsDataText(value);
2848
+ } else {
2849
+ valueContent = numbro(value).format(formatter);
2850
+ }
2851
+ ret[name] = /* @__PURE__ */ jsx(Text, {
2852
+ sx: {
2853
+ fontSize: size,
2854
+ display: "inline"
2855
+ },
2856
+ color: getColorByColorConf(color2, value),
2857
+ weight,
2858
+ children: valueContent
2859
+ });
2860
+ });
2861
+ return ret;
2862
+ }
2863
+ function preserveWhiteSpaces(text) {
2864
+ return text.split(" ").map((s) => /* @__PURE__ */ jsxs(Fragment, {
2865
+ children: [s, "\xA0"]
2866
+ }));
2867
+ }
2868
+ function withLineBreaks(text) {
2869
+ const normalized = text.replaceAll("<br />", "<br/>").replaceAll("\n", "<br/>");
2870
+ const splitted = normalized.split("<br/>");
2871
+ const ret = splitted.map((t, i) => {
2872
+ const arr = [preserveWhiteSpaces(t)];
2873
+ if (i !== splitted.length - 1) {
2874
+ arr.push(/* @__PURE__ */ jsx("br", {}));
2875
+ }
2876
+ return arr;
2877
+ }).flat().filter((t) => t !== void 0);
2878
+ return ret;
2879
+ }
2880
+ function textToJSX(text) {
2881
+ return withLineBreaks(text);
2882
+ }
2883
+ function templateToJSX(template, variables, data) {
2884
+ const variableElements = variablesToElements(variables, data);
2885
+ const regx = /^\{(.+)\}(.*)$/;
2886
+ return template.split("$").map((text) => {
2887
+ var _a;
2888
+ const match = regx.exec(text);
2889
+ if (!match) {
2890
+ return textToJSX(text);
2891
+ }
2892
+ const element = variableElements[match[1]];
2893
+ if (!element) {
2894
+ return textToJSX(text);
2895
+ }
2896
+ const rest = (_a = match[2]) != null ? _a : "";
2897
+ return /* @__PURE__ */ jsxs(Fragment, {
2898
+ children: [element, textToJSX(rest)]
2899
+ });
2900
+ });
2901
+ }
2902
+ echarts.use([BarChart, LineChart, ScatterChart, GridComponent, LegendComponent, TooltipComponent, CanvasRenderer]);
2903
+ echarts.registerTransform(echartsStat.transform.regression);
2904
+ function templateNotEmpty(str) {
2905
+ return str.trim().length > 0;
2906
+ }
2907
+ function Chart({
2908
+ conf,
2909
+ data,
2910
+ width,
2911
+ height
2912
+ }) {
2913
+ const option = React.useMemo(() => {
2914
+ return getOption(conf, data);
2915
+ }, [conf, data]);
2916
+ if (!width || !height) {
2917
+ return null;
2918
+ }
2919
+ return /* @__PURE__ */ jsx(ReactEChartsCore, {
2920
+ echarts,
2921
+ option,
2922
+ style: {
2923
+ width,
2924
+ height
2925
+ }
2926
+ });
2927
+ }
2928
+ function VizCartesianChart({
2929
+ conf,
2930
+ data,
2931
+ width,
2932
+ height
2933
+ }) {
2934
+ const {
2935
+ ref: topStatsRef,
2936
+ height: topStatsHeight
2937
+ } = useElementSize();
2938
+ const {
2939
+ ref: bottomStatsRef,
2940
+ height: bottomStatsHeight
2941
+ } = useElementSize();
2942
+ const templates = React.useMemo(() => {
2943
+ const {
2944
+ stats: {
2945
+ templates: templates2,
2946
+ variables
2947
+ }
2948
+ } = conf;
2949
+ return {
2950
+ top: templateToJSX(templates2.top, variables, data),
2951
+ bottom: templateToJSX(templates2.bottom, variables, data)
2952
+ };
2953
+ }, [conf, data]);
2954
+ const finalHeight = Math.max(0, height - topStatsHeight - bottomStatsHeight);
2955
+ return /* @__PURE__ */ jsxs(Box, {
2956
+ children: [templateNotEmpty(conf.stats.templates.top) && /* @__PURE__ */ jsx(Text, {
2957
+ ref: topStatsRef,
2958
+ align: "left",
2959
+ size: "xs",
2960
+ pl: "sm",
2961
+ children: Object.values(templates.top).map((c) => c)
2962
+ }), /* @__PURE__ */ jsx(Chart, {
2963
+ width,
2964
+ height: finalHeight,
2965
+ data,
2966
+ conf
2967
+ }), templateNotEmpty(conf.stats.templates.bottom) && /* @__PURE__ */ jsx(Text, {
2968
+ ref: bottomStatsRef,
2969
+ align: "left",
2970
+ size: "xs",
2971
+ pl: "sm",
2972
+ children: Object.values(templates.bottom).map((c) => c)
2973
+ })]
2974
+ });
2975
+ }
2976
+ var ValueType = /* @__PURE__ */ ((ValueType2) => {
2977
+ ValueType2["string"] = "string";
2978
+ ValueType2["number"] = "number";
2979
+ ValueType2["eloc"] = "eloc";
2980
+ ValueType2["percentage"] = "percentage";
2981
+ return ValueType2;
2982
+ })(ValueType || {});
2983
+ function StringCell({
2984
+ value
2985
+ }) {
2986
+ return /* @__PURE__ */ jsx(Text, {
2987
+ component: "span",
2988
+ children: value
2989
+ });
2990
+ }
2991
+ function ELOCCell({
2992
+ value
2993
+ }) {
2994
+ return /* @__PURE__ */ jsx(Text, {
2995
+ component: "span",
2996
+ children: value
2997
+ });
2998
+ }
2999
+ function NumberCell({
3000
+ value
3001
+ }) {
3002
+ const num = numbro(value).format({
3003
+ thousandSeparated: true
3004
+ });
3005
+ return /* @__PURE__ */ jsx(Text, {
3006
+ component: "span",
3007
+ children: num
3008
+ });
3009
+ }
3010
+ function PercentageCell({
3011
+ value
3012
+ }) {
3013
+ const num = numbro(value).format({
3014
+ output: "percent",
3015
+ mantissa: 3
3016
+ });
3017
+ return /* @__PURE__ */ jsx(Text, {
3018
+ component: "span",
3019
+ children: num
3020
+ });
3021
+ }
3022
+ function CellValue({
3023
+ value,
3024
+ type
3025
+ }) {
3026
+ switch (type) {
3027
+ case ValueType.string:
3028
+ return /* @__PURE__ */ jsx(StringCell, {
3029
+ value
3030
+ });
3031
+ case ValueType.eloc:
3032
+ return /* @__PURE__ */ jsx(ELOCCell, {
3033
+ value
3034
+ });
3035
+ case ValueType.number:
3036
+ return /* @__PURE__ */ jsx(NumberCell, {
3037
+ value
3038
+ });
3039
+ case ValueType.percentage:
3040
+ return /* @__PURE__ */ jsx(PercentageCell, {
3041
+ value
3042
+ });
2873
3043
  }
2874
- return data.toString();
2875
3044
  }
2876
- function variablesToElements(variables, data) {
2877
- const ret = {};
2878
- variables.forEach(({
2879
- name,
2880
- color: color2,
2881
- data_field,
2882
- aggregation,
2883
- size,
2884
- weight,
2885
- formatter
2886
- }) => {
2887
- const value = aggregateValue(data, data_field, aggregation);
2888
- let valueContent = "";
2889
- if (!["string", "number"].includes(typeof value)) {
2890
- valueContent = getNonStatsDataText(value);
2891
- } else {
2892
- valueContent = numbro(value).format(formatter);
3045
+ function VizTable({
3046
+ conf,
3047
+ data = [],
3048
+ width,
3049
+ height
3050
+ }) {
3051
+ const _a = conf, {
3052
+ id_field,
3053
+ use_raw_columns,
3054
+ columns
3055
+ } = _a, rest = __objRest(_a, [
3056
+ "id_field",
3057
+ "use_raw_columns",
3058
+ "columns"
3059
+ ]);
3060
+ const labels = React.useMemo(() => {
3061
+ if (use_raw_columns) {
3062
+ return Object.keys(data == null ? void 0 : data[0]);
2893
3063
  }
2894
- ret[name] = /* @__PURE__ */ jsx(Text, {
2895
- sx: {
2896
- fontSize: size,
2897
- display: "inline"
2898
- },
2899
- color: getColorByColorConf(color2, value),
2900
- weight,
2901
- children: valueContent
2902
- });
3064
+ return columns.map((c) => c.label);
3065
+ }, [use_raw_columns, columns, data]);
3066
+ const finalColumns = React.useMemo(() => {
3067
+ if (use_raw_columns) {
3068
+ return Object.keys(data == null ? void 0 : data[0]).map((k2) => ({
3069
+ label: k2,
3070
+ value_field: k2,
3071
+ value_type: ValueType.string
3072
+ }));
3073
+ }
3074
+ return columns;
3075
+ }, [use_raw_columns, columns, data]);
3076
+ return /* @__PURE__ */ jsxs(Table, __spreadProps(__spreadValues({
3077
+ sx: {
3078
+ maxHeight: height
3079
+ }
3080
+ }, rest), {
3081
+ children: [/* @__PURE__ */ jsx("thead", {
3082
+ children: /* @__PURE__ */ jsx("tr", {
3083
+ children: labels.map((label) => /* @__PURE__ */ jsx("th", {
3084
+ children: label
3085
+ }, label))
3086
+ })
3087
+ }), /* @__PURE__ */ jsx("tbody", {
3088
+ children: data.slice(0, 30).map((row, index2) => /* @__PURE__ */ jsx("tr", {
3089
+ children: finalColumns.map(({
3090
+ value_field,
3091
+ value_type
3092
+ }) => /* @__PURE__ */ jsx("td", {
3093
+ children: /* @__PURE__ */ jsx(Group, {
3094
+ sx: {
3095
+ "&, .mantine-Text-root": {
3096
+ fontFamily: "monospace",
3097
+ fontSize: rest.fontSize
3098
+ }
3099
+ },
3100
+ children: /* @__PURE__ */ jsx(CellValue, {
3101
+ value: row[value_field],
3102
+ type: value_type
3103
+ })
3104
+ })
3105
+ }, `${value_field}--${row[value_field]}`))
3106
+ }, id_field ? row[id_field] : `row-${index2}`))
3107
+ }), data.length > 100 && /* @__PURE__ */ jsx("tfoot", {
3108
+ children: /* @__PURE__ */ jsx("tr", {
3109
+ children: /* @__PURE__ */ jsx("td", {
3110
+ colSpan: labels.length,
3111
+ children: /* @__PURE__ */ jsx(Text, {
3112
+ color: "red",
3113
+ size: "sm",
3114
+ children: "Showing only the first 30 rows to avoid causing slow performance"
3115
+ })
3116
+ })
3117
+ })
3118
+ })]
3119
+ }));
3120
+ }
3121
+ function interpolateString(template, params = {}) {
3122
+ const extendedParams = __spreadProps(__spreadValues({}, params), {
3123
+ numbro
2903
3124
  });
2904
- return ret;
3125
+ const names = Object.keys(extendedParams);
3126
+ const vals = Object.values(extendedParams);
3127
+ try {
3128
+ return new Function(...names, `return \`${template}\`;`)(...vals);
3129
+ } catch (error) {
3130
+ return error.message;
3131
+ }
2905
3132
  }
2906
- function preserveWhiteSpaces(text) {
2907
- return text.split(" ").map((s) => /* @__PURE__ */ jsxs(Fragment, {
2908
- children: [s, "\xA0"]
2909
- }));
3133
+ function VizText({
3134
+ conf: {
3135
+ paragraphs
3136
+ },
3137
+ data
3138
+ }) {
3139
+ return /* @__PURE__ */ jsx(Fragment, {
3140
+ children: paragraphs.map((_a, index2) => {
3141
+ var _b = _a, {
3142
+ template,
3143
+ size
3144
+ } = _b, rest = __objRest(_b, [
3145
+ "template",
3146
+ "size"
3147
+ ]);
3148
+ return /* @__PURE__ */ jsx(Text, __spreadProps(__spreadValues({}, rest), {
3149
+ sx: {
3150
+ fontSize: size
3151
+ },
3152
+ children: interpolateString(template, data[0])
3153
+ }), `${template}---${index2}`);
3154
+ })
3155
+ });
2910
3156
  }
2911
- function withLineBreaks(text) {
2912
- const normalized = text.replaceAll("<br />", "<br/>").replaceAll("\n", "<br/>");
2913
- const splitted = normalized.split("<br/>");
2914
- const ret = splitted.map((t, i) => {
2915
- const arr = [preserveWhiteSpaces(t)];
2916
- if (i !== splitted.length - 1) {
2917
- arr.push(/* @__PURE__ */ jsx("br", {}));
3157
+ echarts.use([GridComponent, VisualMapComponent, LegendComponent, TooltipComponent, CanvasRenderer]);
3158
+ function VizBar3D({
3159
+ conf,
3160
+ data,
3161
+ width,
3162
+ height
3163
+ }) {
3164
+ const _a = conf, {
3165
+ x_axis_data_key,
3166
+ y_axis_data_key,
3167
+ z_axis_data_key
3168
+ } = _a, restConf = __objRest(_a, [
3169
+ "x_axis_data_key",
3170
+ "y_axis_data_key",
3171
+ "z_axis_data_key"
3172
+ ]);
3173
+ const min = React.useMemo(() => {
3174
+ return _.minBy(data, (d) => d[z_axis_data_key])[z_axis_data_key];
3175
+ }, [data, z_axis_data_key]);
3176
+ const max = React.useMemo(() => {
3177
+ return _.maxBy(data, (d) => d[z_axis_data_key])[z_axis_data_key];
3178
+ }, [data, z_axis_data_key]);
3179
+ const option = __spreadProps(__spreadValues({
3180
+ tooltip: {},
3181
+ backgroundColor: "#fff",
3182
+ visualMap: {
3183
+ show: true,
3184
+ dimension: 2,
3185
+ min,
3186
+ max,
3187
+ inRange: {
3188
+ color: ["#313695", "#4575b4", "#74add1", "#abd9e9", "#e0f3f8", "#ffffbf", "#fee090", "#fdae61", "#f46d43", "#d73027", "#a50026"]
3189
+ }
3190
+ },
3191
+ xAxis3D: {
3192
+ type: "value"
3193
+ },
3194
+ yAxis3D: {
3195
+ type: "value"
3196
+ },
3197
+ zAxis3D: {
3198
+ type: "value"
3199
+ },
3200
+ grid3D: {
3201
+ viewControl: {
3202
+ projection: "orthographic",
3203
+ autoRotate: false
3204
+ },
3205
+ light: {
3206
+ main: {
3207
+ shadow: true,
3208
+ quality: "ultra",
3209
+ intensity: 1.5
3210
+ }
3211
+ }
2918
3212
  }
2919
- return arr;
2920
- }).flat().filter((t) => t !== void 0);
2921
- return ret;
2922
- }
2923
- function textToJSX(text) {
2924
- return withLineBreaks(text);
3213
+ }, restConf), {
3214
+ series: [{
3215
+ type: "bar3D",
3216
+ wireframe: {},
3217
+ data: data.map((d) => [d[x_axis_data_key], d[y_axis_data_key], d[z_axis_data_key]])
3218
+ }]
3219
+ });
3220
+ return /* @__PURE__ */ jsx(ReactEChartsCore, {
3221
+ echarts,
3222
+ option,
3223
+ style: {
3224
+ width,
3225
+ height
3226
+ }
3227
+ });
2925
3228
  }
2926
- function templateToJSX(template, variables, data) {
2927
- const variableElements = variablesToElements(variables, data);
2928
- const regx = /^\{(.+)\}(.*)$/;
2929
- return template.split("$").map((text) => {
2930
- var _a;
2931
- const match = regx.exec(text);
2932
- if (!match) {
2933
- return textToJSX(text);
3229
+ var index$2 = "";
3230
+ echarts.use([PieChart, CanvasRenderer]);
3231
+ const defaultOption = {
3232
+ tooltip: {
3233
+ show: true
3234
+ },
3235
+ series: {
3236
+ type: "pie",
3237
+ radius: ["50%", "80%"],
3238
+ label: {
3239
+ position: "outer",
3240
+ alignTo: "edge",
3241
+ formatter: "{name|{b}}\n{percentage|{d}%}",
3242
+ minMargin: 5,
3243
+ edgeDistance: 10,
3244
+ lineHeight: 15,
3245
+ rich: {
3246
+ percentage: {
3247
+ color: "#999"
3248
+ }
3249
+ },
3250
+ margin: 20
3251
+ },
3252
+ labelLine: {
3253
+ length: 15,
3254
+ length2: 0,
3255
+ maxSurfaceAngle: 80,
3256
+ showAbove: true
3257
+ },
3258
+ top: 10,
3259
+ bottom: 10,
3260
+ left: 10,
3261
+ right: 10
3262
+ }
3263
+ };
3264
+ function VizPie({
3265
+ conf,
3266
+ data,
3267
+ width,
3268
+ height
3269
+ }) {
3270
+ const _a = conf, {
3271
+ label_field = "name",
3272
+ value_field = "value"
3273
+ } = _a, restConf = __objRest(_a, [
3274
+ "label_field",
3275
+ "value_field"
3276
+ ]);
3277
+ const chartData = React.useMemo(() => {
3278
+ return data.map((d) => ({
3279
+ name: d[label_field],
3280
+ value: Number(d[value_field])
3281
+ }));
3282
+ }, [data, label_field, value_field]);
3283
+ const labelOptions = React.useMemo(() => {
3284
+ return {
3285
+ series: {
3286
+ labelLayout: function(params) {
3287
+ const isLeft = params.labelRect.x < width / 2;
3288
+ const points = params.labelLinePoints;
3289
+ points[2][0] = isLeft ? params.labelRect.x : params.labelRect.x + params.labelRect.width;
3290
+ return {
3291
+ labelLinePoints: points
3292
+ };
3293
+ }
3294
+ }
3295
+ };
3296
+ }, [width]);
3297
+ const option = _.merge({}, defaultOption, labelOptions, restConf, {
3298
+ series: {
3299
+ data: chartData
2934
3300
  }
2935
- const element = variableElements[match[1]];
2936
- if (!element) {
2937
- return textToJSX(text);
3301
+ });
3302
+ return /* @__PURE__ */ jsx(ReactEChartsCore, {
3303
+ echarts,
3304
+ option,
3305
+ style: {
3306
+ width,
3307
+ height
2938
3308
  }
2939
- const rest = (_a = match[2]) != null ? _a : "";
2940
- return /* @__PURE__ */ jsxs(Fragment, {
2941
- children: [element, textToJSX(rest)]
2942
- });
2943
3309
  });
2944
3310
  }
2945
3311
  function VizStats({
@@ -4056,700 +4422,846 @@ function RegressionsField({
4056
4422
  mt: "xs",
4057
4423
  children: /* @__PURE__ */ jsx(Button, {
4058
4424
  onClick: add,
4059
- children: "Add a Regression Line"
4060
- })
4425
+ children: "Add a Regression Line"
4426
+ })
4427
+ })]
4428
+ });
4429
+ }
4430
+ const options = [{
4431
+ label: "None",
4432
+ value: "none"
4433
+ }, {
4434
+ label: "Sum",
4435
+ value: "sum"
4436
+ }, {
4437
+ label: "Mean",
4438
+ value: "mean"
4439
+ }, {
4440
+ label: "Median",
4441
+ value: "median"
4442
+ }, {
4443
+ label: "Max",
4444
+ value: "max"
4445
+ }, {
4446
+ label: "Min",
4447
+ value: "min"
4448
+ }];
4449
+ function _AggregationSelector({
4450
+ label,
4451
+ value,
4452
+ onChange
4453
+ }, ref) {
4454
+ return /* @__PURE__ */ jsx(Select, {
4455
+ ref,
4456
+ label,
4457
+ data: options,
4458
+ value,
4459
+ onChange
4460
+ });
4461
+ }
4462
+ const AggregationSelector = React.forwardRef(_AggregationSelector);
4463
+ function _ColorArrayInput({
4464
+ label,
4465
+ value,
4466
+ onChange
4467
+ }, ref) {
4468
+ const [values, setValues] = React.useState(Array.isArray(value) ? [...value] : []);
4469
+ const add = React.useCallback(() => {
4470
+ setValues((s) => [...s, ""]);
4471
+ }, [setValues]);
4472
+ const del = React.useCallback((index2) => {
4473
+ setValues((s) => {
4474
+ s.splice(index2, 1);
4475
+ return [...s];
4476
+ });
4477
+ }, [setValues]);
4478
+ const changed = React.useMemo(() => {
4479
+ return !_.isEqual(values, value);
4480
+ }, [values, value]);
4481
+ const submit = () => {
4482
+ onChange(values.map((s) => s.toString()));
4483
+ };
4484
+ const theme = useMantineTheme();
4485
+ const swatches = React.useMemo(() => {
4486
+ return Object.entries(theme.colors).map(([_color, profile]) => profile[6]);
4487
+ }, [theme]);
4488
+ return /* @__PURE__ */ jsxs(Fragment, {
4489
+ children: [/* @__PURE__ */ jsxs(Group, {
4490
+ position: "left",
4491
+ ref,
4492
+ children: [/* @__PURE__ */ jsx(Text, {
4493
+ children: label
4494
+ }), /* @__PURE__ */ jsx(ActionIcon, {
4495
+ mr: 5,
4496
+ variant: "filled",
4497
+ color: "blue",
4498
+ disabled: !changed,
4499
+ onClick: submit,
4500
+ children: /* @__PURE__ */ jsx(DeviceFloppy, {
4501
+ size: 20
4502
+ })
4503
+ })]
4504
+ }), /* @__PURE__ */ jsxs(Group, {
4505
+ children: [values.map((v, i) => /* @__PURE__ */ jsx(ColorInput, {
4506
+ value: v,
4507
+ onChange: (color2) => {
4508
+ setValues((s) => {
4509
+ s.splice(i, 1, color2);
4510
+ return [...s];
4511
+ });
4512
+ },
4513
+ swatches,
4514
+ rightSection: /* @__PURE__ */ jsx(ActionIcon, {
4515
+ onClick: () => del(i),
4516
+ color: "red",
4517
+ children: /* @__PURE__ */ jsx(Trash, {
4518
+ size: 14
4519
+ })
4520
+ }),
4521
+ sx: {
4522
+ width: "45%"
4523
+ }
4524
+ })), /* @__PURE__ */ jsx(ActionIcon, {
4525
+ onClick: add,
4526
+ color: "blue",
4527
+ variant: "outline",
4528
+ children: /* @__PURE__ */ jsx(PlaylistAdd, {
4529
+ size: 20
4530
+ })
4531
+ })]
4532
+ })]
4533
+ });
4534
+ }
4535
+ const ColorArrayInput = React.forwardRef(_ColorArrayInput);
4536
+ const marks = [{
4537
+ label: "initial",
4538
+ value: 0
4539
+ }, {
4540
+ label: "500",
4541
+ value: 25
4542
+ }, {
4543
+ label: "700",
4544
+ value: 50
4545
+ }, {
4546
+ label: "semibold",
4547
+ value: 75
4548
+ }, {
4549
+ label: "bold",
4550
+ value: 100
4551
+ }];
4552
+ function _MantineFontWeightSlider({
4553
+ label,
4554
+ value,
4555
+ onChange
4556
+ }, ref) {
4557
+ var _a, _b;
4558
+ const [mark, setMark] = React.useState((_b = (_a = marks.find((m2) => m2.label === value)) == null ? void 0 : _a.value) != null ? _b : marks[0].value);
4559
+ React.useEffect(() => {
4560
+ const match = marks.find((s) => s.value === mark);
4561
+ if (match) {
4562
+ onChange(match.label);
4563
+ }
4564
+ }, [mark]);
4565
+ return /* @__PURE__ */ jsxs(Group, {
4566
+ direction: "column",
4567
+ grow: true,
4568
+ spacing: 0,
4569
+ mt: "sm",
4570
+ mb: "lg",
4571
+ children: [/* @__PURE__ */ jsx(Text, {
4572
+ size: "sm",
4573
+ children: label
4574
+ }), /* @__PURE__ */ jsx(Slider, {
4575
+ label: null,
4576
+ marks,
4577
+ value: mark,
4578
+ onChange: setMark,
4579
+ step: 25,
4580
+ placeholder: "Pick a font size",
4581
+ ref
4582
+ })]
4583
+ });
4584
+ }
4585
+ const MantineFontWeightSlider = React.forwardRef(_MantineFontWeightSlider);
4586
+ function _TextArrayInput({
4587
+ label,
4588
+ value,
4589
+ onChange
4590
+ }, ref) {
4591
+ const [values, setValues] = React.useState(Array.isArray(value) ? [...value] : []);
4592
+ const add = React.useCallback(() => {
4593
+ setValues((s) => [...s, ""]);
4594
+ }, [setValues]);
4595
+ const del = React.useCallback((index2) => {
4596
+ setValues((s) => {
4597
+ s.splice(index2, 1);
4598
+ return [...s];
4599
+ });
4600
+ }, [setValues]);
4601
+ const changed = React.useMemo(() => {
4602
+ return !_.isEqual(values, value);
4603
+ }, [values, value]);
4604
+ const submit = () => {
4605
+ onChange(values.map((s) => s.toString()));
4606
+ };
4607
+ return /* @__PURE__ */ jsxs(Fragment, {
4608
+ children: [/* @__PURE__ */ jsxs(Group, {
4609
+ position: "left",
4610
+ ref,
4611
+ children: [/* @__PURE__ */ jsx(Text, {
4612
+ children: label
4613
+ }), /* @__PURE__ */ jsx(ActionIcon, {
4614
+ mr: 5,
4615
+ variant: "filled",
4616
+ color: "blue",
4617
+ disabled: !changed,
4618
+ onClick: submit,
4619
+ children: /* @__PURE__ */ jsx(DeviceFloppy, {
4620
+ size: 20
4621
+ })
4622
+ })]
4623
+ }), /* @__PURE__ */ jsxs(Group, {
4624
+ children: [values.map((v, i) => /* @__PURE__ */ jsx(TextInput, {
4625
+ value: v,
4626
+ onChange: (event) => {
4627
+ const newValue = event.currentTarget.value;
4628
+ setValues((s) => {
4629
+ s.splice(i, 1, newValue);
4630
+ return [...s];
4631
+ });
4632
+ },
4633
+ rightSection: /* @__PURE__ */ jsx(ActionIcon, {
4634
+ onClick: () => del(i),
4635
+ color: "red",
4636
+ children: /* @__PURE__ */ jsx(Trash, {
4637
+ size: 14
4638
+ })
4639
+ }),
4640
+ sx: {
4641
+ width: "45%"
4642
+ }
4643
+ })), /* @__PURE__ */ jsx(ActionIcon, {
4644
+ onClick: add,
4645
+ color: "blue",
4646
+ variant: "outline",
4647
+ children: /* @__PURE__ */ jsx(PlaylistAdd, {
4648
+ size: 20
4649
+ })
4650
+ })]
4061
4651
  })]
4062
4652
  });
4063
4653
  }
4064
- function withDefaults(series) {
4065
- function setDefaults({
4066
- type,
4067
- name,
4068
- showSymbol,
4069
- symbolSize = 5,
4070
- y_axis_data_key = "value",
4071
- yAxisIndex = 0,
4072
- label_position = "top",
4073
- stack = "1",
4074
- color: color2 = "black",
4075
- barWidth = "30",
4076
- smooth = false,
4077
- step = false
4078
- }) {
4079
- return {
4080
- type,
4081
- name,
4082
- showSymbol,
4083
- symbolSize,
4084
- y_axis_data_key,
4085
- yAxisIndex,
4086
- label_position,
4087
- stack,
4088
- color: color2,
4089
- barWidth,
4090
- smooth,
4091
- step
4092
- };
4093
- }
4094
- return series.map(setDefaults);
4654
+ const TextArrayInput = React.forwardRef(_TextArrayInput);
4655
+ function getANewVariable() {
4656
+ return {
4657
+ name: randomId(),
4658
+ size: "20px",
4659
+ weight: "bold",
4660
+ color: {
4661
+ type: "static",
4662
+ staticColor: "blue"
4663
+ },
4664
+ data_field: "",
4665
+ aggregation: "none",
4666
+ formatter: {
4667
+ output: "number",
4668
+ mantissa: 0
4669
+ }
4670
+ };
4095
4671
  }
4096
- function VizCartesianChartPanel({
4097
- conf,
4098
- setConf,
4672
+ const TemplateInput = React.forwardRef(function TemplateInput2(_a, ref) {
4673
+ var _b = _a, {
4674
+ value,
4675
+ onChange
4676
+ } = _b, rest = __objRest(_b, [
4677
+ "value",
4678
+ "onChange"
4679
+ ]);
4680
+ return /* @__PURE__ */ jsx(TextInput, __spreadValues({
4681
+ ref,
4682
+ value,
4683
+ onChange
4684
+ }, rest));
4685
+ });
4686
+ function TemplateVariableField({
4687
+ value,
4688
+ onChange,
4099
4689
  data
4100
4690
  }) {
4101
- const _a = conf, {
4102
- series,
4103
- y_axes
4104
- } = _a, restConf = __objRest(_a, [
4105
- "series",
4106
- "y_axes"
4107
- ]);
4108
- const defaultValues = React.useMemo(() => {
4109
- const _a2 = restConf, {
4110
- x_axis_name = ""
4111
- } = _a2, rest = __objRest(_a2, [
4112
- "x_axis_name"
4113
- ]);
4114
- return __spreadValues({
4115
- series: withDefaults(series != null ? series : []),
4116
- x_axis_name,
4117
- y_axes: y_axes != null ? y_axes : [{
4118
- name: "Y Axis",
4119
- label_formatter: defaultNumbroFormat
4120
- }]
4121
- }, rest);
4122
- }, [series, restConf]);
4123
- React.useEffect(() => {
4124
- const configMalformed = !_.isEqual(conf, defaultValues);
4125
- if (configMalformed) {
4126
- setConf(defaultValues);
4127
- }
4128
- }, [conf, defaultValues]);
4129
- const {
4130
- control,
4131
- handleSubmit,
4132
- watch,
4133
- getValues
4134
- } = useForm({
4135
- defaultValues
4136
- });
4137
- watch(["x_axis_data_key", "x_axis_name"]);
4138
- const values = getValues();
4139
- const changed = React.useMemo(() => {
4140
- return !_.isEqual(values, conf);
4141
- }, [values, conf]);
4142
- return /* @__PURE__ */ jsx(Group, {
4143
- direction: "column",
4144
- mt: "md",
4145
- spacing: "xs",
4146
- grow: true,
4147
- children: /* @__PURE__ */ jsxs("form", {
4148
- onSubmit: handleSubmit(setConf),
4149
- children: [/* @__PURE__ */ jsxs(Group, {
4150
- position: "left",
4151
- py: "md",
4152
- pl: "md",
4153
- sx: {
4154
- borderBottom: "1px solid #eee",
4155
- background: "#efefef"
4156
- },
4157
- children: [/* @__PURE__ */ jsx(Text, {
4158
- children: "Chart Config"
4159
- }), /* @__PURE__ */ jsx(ActionIcon, {
4160
- type: "submit",
4161
- mr: 5,
4162
- variant: "filled",
4163
- color: "blue",
4164
- disabled: !changed,
4165
- children: /* @__PURE__ */ jsx(DeviceFloppy, {
4166
- size: 20
4167
- })
4168
- })]
4169
- }), /* @__PURE__ */ jsxs(Accordion, {
4170
- offsetIcon: false,
4171
- multiple: true,
4172
- initialState: {
4173
- 0: true,
4174
- 1: true
4691
+ const colorType = value.color.type;
4692
+ const handleChange = (path, newValue) => {
4693
+ const v = _.cloneDeep(value);
4694
+ _.set(v, path, newValue);
4695
+ onChange(v);
4696
+ };
4697
+ return /* @__PURE__ */ jsxs(Box, {
4698
+ px: "sm",
4699
+ py: "md",
4700
+ children: [/* @__PURE__ */ jsx(Text, {
4701
+ weight: "bold",
4702
+ pb: 0,
4703
+ children: value.name
4704
+ }), /* @__PURE__ */ jsx(Divider, {
4705
+ my: "xs",
4706
+ label: "Data",
4707
+ labelPosition: "center"
4708
+ }), /* @__PURE__ */ jsxs(Group, {
4709
+ direction: "row",
4710
+ grow: true,
4711
+ noWrap: true,
4712
+ children: [/* @__PURE__ */ jsx(TextInput, {
4713
+ label: "Name",
4714
+ required: true,
4715
+ value: value.name,
4716
+ onChange: (e) => handleChange("name", e.currentTarget.value)
4717
+ }), /* @__PURE__ */ jsx(DataFieldSelector, {
4718
+ label: "Data Field",
4719
+ required: true,
4720
+ data,
4721
+ value: value.data_field,
4722
+ onChange: (v) => handleChange("data_field", v)
4723
+ }), /* @__PURE__ */ jsx(AggregationSelector, {
4724
+ label: "Aggregation",
4725
+ value: value.aggregation,
4726
+ onChange: (v) => handleChange("aggregation", v)
4727
+ })]
4728
+ }), /* @__PURE__ */ jsx(NumbroFormatSelector, {
4729
+ value: value.formatter,
4730
+ onChange: (v) => handleChange("formatter", v)
4731
+ }), /* @__PURE__ */ jsx(Divider, {
4732
+ my: "xs",
4733
+ label: "Typography",
4734
+ labelPosition: "center"
4735
+ }), /* @__PURE__ */ jsx(Group, {
4736
+ direction: "column",
4737
+ grow: true,
4738
+ children: /* @__PURE__ */ jsx(TextInput, {
4739
+ label: "Font Size",
4740
+ placeholder: "10px, 1em, 1rem, 100%...",
4741
+ sx: {
4742
+ flex: 1
4175
4743
  },
4176
- children: [/* @__PURE__ */ jsx(Accordion.Item, {
4177
- label: "X Axis",
4178
- children: /* @__PURE__ */ jsxs(Group, {
4179
- direction: "row",
4180
- grow: true,
4181
- noWrap: true,
4182
- children: [/* @__PURE__ */ jsx(Controller, {
4183
- name: "x_axis_data_key",
4184
- control,
4185
- render: ({
4186
- field
4187
- }) => /* @__PURE__ */ jsx(DataFieldSelector, __spreadValues({
4188
- label: "X Axis Data Field",
4189
- required: true,
4190
- data,
4191
- sx: {
4192
- flex: 1
4193
- }
4194
- }, field))
4195
- }), /* @__PURE__ */ jsx(Controller, {
4196
- name: "x_axis_name",
4197
- control,
4198
- render: ({
4199
- field
4200
- }) => /* @__PURE__ */ jsx(TextInput, __spreadValues({
4201
- label: "X Axis Name",
4202
- sx: {
4203
- flex: 1
4204
- }
4205
- }, field))
4206
- })]
4207
- })
4208
- }), /* @__PURE__ */ jsx(Accordion.Item, {
4209
- label: "Y Axes",
4210
- children: /* @__PURE__ */ jsx(YAxesField, {
4211
- control,
4212
- watch
4213
- })
4214
- }), /* @__PURE__ */ jsx(Accordion.Item, {
4215
- label: "Series",
4216
- children: /* @__PURE__ */ jsx(SeriesField, {
4217
- control,
4218
- watch,
4219
- getValues,
4220
- data
4221
- })
4222
- }), /* @__PURE__ */ jsx(Accordion.Item, {
4223
- label: "Regression Lines",
4224
- children: /* @__PURE__ */ jsx(RegressionsField, {
4225
- control,
4226
- watch,
4227
- getValues,
4228
- data
4229
- })
4744
+ value: value.size,
4745
+ onChange: (e) => handleChange("size", e.currentTarget.value)
4746
+ })
4747
+ }), /* @__PURE__ */ jsx(Group, {
4748
+ position: "apart",
4749
+ grow: true,
4750
+ sx: {
4751
+ "> *": {
4752
+ flexGrow: 1,
4753
+ maxWidth: "100%"
4754
+ }
4755
+ },
4756
+ children: /* @__PURE__ */ jsx(MantineFontWeightSlider, {
4757
+ label: "Font Weight",
4758
+ value: value.weight,
4759
+ onChange: (v) => handleChange("weight", v)
4760
+ })
4761
+ }), /* @__PURE__ */ jsx(Divider, {
4762
+ my: "xs",
4763
+ label: "Style",
4764
+ labelPosition: "center"
4765
+ }), /* @__PURE__ */ jsxs(Group, {
4766
+ direction: "column",
4767
+ grow: true,
4768
+ children: [/* @__PURE__ */ jsx(Select, {
4769
+ label: "Color Type",
4770
+ data: [{
4771
+ label: "Static Color",
4772
+ value: "static"
4773
+ }, {
4774
+ label: "Continuous Color",
4775
+ value: "continuous"
4776
+ }],
4777
+ value: value.color.type,
4778
+ onChange: (v) => handleChange("color.type", v)
4779
+ }), colorType === "static" && /* @__PURE__ */ jsx(MantineColorSelector, {
4780
+ value: value.color.staticColor,
4781
+ onChange: (v) => handleChange("color.staticColor", v)
4782
+ }), colorType === "continuous" && /* @__PURE__ */ jsxs(Fragment, {
4783
+ children: [/* @__PURE__ */ jsx(TextArrayInput, {
4784
+ label: "Value Range",
4785
+ value: value.color.valueRange,
4786
+ onChange: (v) => handleChange("color.valueRange", v)
4787
+ }), /* @__PURE__ */ jsx(ColorArrayInput, {
4788
+ label: "Color Range",
4789
+ value: value.color.colorRange,
4790
+ onChange: (v) => handleChange("color.colorRange", v)
4230
4791
  })]
4231
4792
  })]
4232
- })
4793
+ })]
4233
4794
  });
4234
4795
  }
4235
- function VizPiePanel({
4236
- conf: {
4237
- label_field,
4238
- value_field
4239
- },
4240
- setConf,
4796
+ function VariableField$1({
4797
+ control,
4798
+ index: index2,
4799
+ remove,
4241
4800
  data
4242
4801
  }) {
4243
- const form = useForm$1({
4244
- initialValues: {
4245
- label_field,
4246
- value_field
4247
- }
4248
- });
4249
- return /* @__PURE__ */ jsx(Group, {
4802
+ return /* @__PURE__ */ jsxs(Group, {
4250
4803
  direction: "column",
4251
- mt: "md",
4252
- spacing: "xs",
4253
4804
  grow: true,
4254
- children: /* @__PURE__ */ jsxs("form", {
4255
- onSubmit: form.onSubmit(setConf),
4256
- children: [/* @__PURE__ */ jsxs(Group, {
4257
- position: "apart",
4258
- mb: "lg",
4259
- sx: {
4260
- position: "relative"
4261
- },
4262
- children: [/* @__PURE__ */ jsx(Text, {
4263
- children: "Pie Config"
4264
- }), /* @__PURE__ */ jsx(ActionIcon, {
4265
- type: "submit",
4266
- mr: 5,
4267
- variant: "filled",
4268
- color: "blue",
4269
- children: /* @__PURE__ */ jsx(DeviceFloppy, {
4270
- size: 20
4271
- })
4272
- })]
4273
- }), /* @__PURE__ */ jsxs(Group, {
4274
- direction: "column",
4275
- mt: "md",
4276
- spacing: "xs",
4277
- grow: true,
4278
- p: "md",
4279
- mb: "sm",
4280
- sx: {
4281
- border: "1px solid #eee",
4282
- borderRadius: "5px"
4283
- },
4284
- children: [/* @__PURE__ */ jsx(DataFieldSelector, __spreadValues({
4285
- label: "Label Field",
4286
- required: true,
4287
- data
4288
- }, form.getInputProps("label_field"))), /* @__PURE__ */ jsx(DataFieldSelector, __spreadValues({
4289
- label: "Value Field",
4290
- required: true,
4291
- data
4292
- }, form.getInputProps("value_field")))]
4293
- })]
4294
- })
4295
- });
4805
+ my: "sm",
4806
+ p: 0,
4807
+ sx: {
4808
+ border: "1px solid #eee",
4809
+ borderTopColor: "#333",
4810
+ borderTopWidth: 2,
4811
+ position: "relative"
4812
+ },
4813
+ children: [/* @__PURE__ */ jsx(Controller, {
4814
+ name: `stats.variables.${index2}`,
4815
+ control,
4816
+ render: ({
4817
+ field
4818
+ }) => /* @__PURE__ */ jsx(TemplateVariableField, __spreadValues({
4819
+ data
4820
+ }, field))
4821
+ }), /* @__PURE__ */ jsx(ActionIcon, {
4822
+ color: "red",
4823
+ variant: "hover",
4824
+ onClick: () => remove(index2),
4825
+ sx: {
4826
+ position: "absolute",
4827
+ top: 15,
4828
+ right: 5
4829
+ },
4830
+ children: /* @__PURE__ */ jsx(Trash, {
4831
+ size: 16
4832
+ })
4833
+ })]
4834
+ }, index2);
4296
4835
  }
4297
- function VizRichTextPanel({
4298
- conf,
4299
- setConf
4836
+ function StatsField({
4837
+ control,
4838
+ watch,
4839
+ data
4300
4840
  }) {
4301
- const defaultValues = React.useMemo(() => {
4302
- const {
4303
- content = ""
4304
- } = conf;
4305
- return {
4306
- content
4307
- };
4308
- }, [conf]);
4309
- React.useEffect(() => {
4310
- const configMalformed = !_.isEqual(conf, defaultValues);
4311
- if (configMalformed) {
4312
- setConf(defaultValues);
4313
- }
4314
- }, [conf, defaultValues]);
4315
4841
  const {
4842
+ fields,
4843
+ append,
4844
+ remove
4845
+ } = useFieldArray({
4316
4846
  control,
4317
- handleSubmit,
4318
- watch,
4319
- getValues
4320
- } = useForm({
4321
- defaultValues
4847
+ name: "stats.variables"
4322
4848
  });
4323
- watch("content");
4324
- const values = getValues();
4325
- const changed = React.useMemo(() => {
4326
- return !_.isEqual(values, conf);
4327
- }, [values, conf]);
4328
- return /* @__PURE__ */ jsx(Group, {
4329
- direction: "column",
4330
- mt: "md",
4331
- spacing: "xs",
4332
- grow: true,
4333
- children: /* @__PURE__ */ jsxs("form", {
4334
- onSubmit: handleSubmit(setConf),
4335
- children: [/* @__PURE__ */ jsxs(Group, {
4336
- position: "left",
4337
- py: "md",
4338
- pl: "md",
4339
- sx: {
4340
- borderBottom: "1px solid #eee",
4341
- background: "#efefef"
4342
- },
4343
- children: [/* @__PURE__ */ jsx(Text, {
4344
- children: "Content"
4345
- }), /* @__PURE__ */ jsx(ActionIcon, {
4346
- type: "submit",
4347
- mr: 5,
4348
- variant: "filled",
4349
- color: "blue",
4350
- disabled: !changed,
4351
- children: /* @__PURE__ */ jsx(DeviceFloppy, {
4352
- size: 20
4353
- })
4354
- })]
4849
+ watch("stats.templates");
4850
+ const watchFieldArray = watch("stats.variables");
4851
+ const controlledFields = fields.map((field, index2) => {
4852
+ return __spreadValues(__spreadValues({}, field), watchFieldArray[index2]);
4853
+ });
4854
+ const add = () => append(getANewVariable());
4855
+ return /* @__PURE__ */ jsxs(Group, {
4856
+ direction: "column",
4857
+ grow: true,
4858
+ children: [/* @__PURE__ */ jsxs(Group, {
4859
+ direction: "column",
4860
+ grow: true,
4861
+ noWrap: true,
4862
+ spacing: 0,
4863
+ children: [/* @__PURE__ */ jsx(Controller, {
4864
+ name: "stats.templates.top",
4865
+ control,
4866
+ render: ({
4867
+ field
4868
+ }) => /* @__PURE__ */ jsx(TemplateInput, __spreadValues({
4869
+ label: "Template for stats above the chart",
4870
+ py: "md",
4871
+ sx: {
4872
+ flexGrow: 1
4873
+ }
4874
+ }, field))
4355
4875
  }), /* @__PURE__ */ jsx(Controller, {
4356
- name: "content",
4876
+ name: "stats.templates.bottom",
4357
4877
  control,
4358
4878
  render: ({
4359
4879
  field
4360
- }) => /* @__PURE__ */ jsx(RichTextEditor, __spreadValues({
4880
+ }) => /* @__PURE__ */ jsx(TemplateInput, __spreadValues({
4881
+ label: "Template for stats under the chart",
4882
+ py: "md",
4361
4883
  sx: {
4362
- flex: 1
4884
+ flexGrow: 1
4363
4885
  }
4364
4886
  }, field))
4365
4887
  })]
4366
- })
4888
+ }), controlledFields.map((_variableItem, index2) => /* @__PURE__ */ jsx(VariableField$1, {
4889
+ control,
4890
+ index: index2,
4891
+ remove,
4892
+ data
4893
+ })), /* @__PURE__ */ jsx(Group, {
4894
+ position: "center",
4895
+ mt: "xs",
4896
+ children: /* @__PURE__ */ jsx(Button, {
4897
+ onClick: add,
4898
+ children: "Add a Variable"
4899
+ })
4900
+ })]
4367
4901
  });
4368
4902
  }
4369
- function updateSchema(legacyConf) {
4370
- if ("variables" in legacyConf) {
4371
- return legacyConf;
4903
+ function withDefaults(series) {
4904
+ function setDefaults({
4905
+ type,
4906
+ name,
4907
+ showSymbol,
4908
+ symbolSize = 5,
4909
+ y_axis_data_key = "value",
4910
+ yAxisIndex = 0,
4911
+ label_position = "top",
4912
+ stack = "1",
4913
+ color: color2 = "black",
4914
+ barWidth = "30",
4915
+ smooth = false,
4916
+ step = false
4917
+ }) {
4918
+ return {
4919
+ type,
4920
+ name,
4921
+ showSymbol,
4922
+ symbolSize,
4923
+ y_axis_data_key,
4924
+ yAxisIndex,
4925
+ label_position,
4926
+ stack,
4927
+ color: color2,
4928
+ barWidth,
4929
+ smooth,
4930
+ step
4931
+ };
4372
4932
  }
4373
- const {
4374
- align,
4375
- size,
4376
- weight,
4377
- color: color2,
4378
- content: {
4379
- prefix = "",
4380
- data_field = "value",
4381
- formatter = {
4382
- output: "number",
4383
- mantissa: 0
4384
- },
4385
- postfix = ""
4386
- } = {}
4387
- } = legacyConf;
4388
- return {
4389
- align,
4390
- template: `${prefix} \${value} ${postfix}`,
4391
- variables: [
4392
- {
4393
- name: "value",
4394
- data_field,
4395
- aggregation: "none",
4396
- formatter,
4397
- color: color2,
4398
- weight,
4399
- size
4400
- }
4401
- ]
4402
- };
4403
- }
4404
- const options = [{
4405
- label: "None",
4406
- value: "none"
4407
- }, {
4408
- label: "Sum",
4409
- value: "sum"
4410
- }, {
4411
- label: "Mean",
4412
- value: "mean"
4413
- }, {
4414
- label: "Median",
4415
- value: "median"
4416
- }, {
4417
- label: "Max",
4418
- value: "max"
4419
- }, {
4420
- label: "Min",
4421
- value: "min"
4422
- }];
4423
- function _AggregationSelector({
4424
- label,
4425
- value,
4426
- onChange
4427
- }, ref) {
4428
- return /* @__PURE__ */ jsx(Select, {
4429
- ref,
4430
- label,
4431
- data: options,
4432
- value,
4433
- onChange
4434
- });
4933
+ return series.map(setDefaults);
4435
4934
  }
4436
- const AggregationSelector = React.forwardRef(_AggregationSelector);
4437
- function _ColorArrayInput({
4438
- label,
4439
- value,
4440
- onChange
4441
- }, ref) {
4442
- const [values, setValues] = React.useState(Array.isArray(value) ? [...value] : []);
4443
- const add = React.useCallback(() => {
4444
- setValues((s) => [...s, ""]);
4445
- }, [setValues]);
4446
- const del = React.useCallback((index2) => {
4447
- setValues((s) => {
4448
- s.splice(index2, 1);
4449
- return [...s];
4450
- });
4451
- }, [setValues]);
4452
- const changed = React.useMemo(() => {
4453
- return !_.isEqual(values, value);
4454
- }, [values, value]);
4455
- const submit = () => {
4456
- onChange(values.map((s) => s.toString()));
4457
- };
4458
- const theme = useMantineTheme();
4459
- const swatches = React.useMemo(() => {
4460
- return Object.entries(theme.colors).map(([_color, profile]) => profile[6]);
4461
- }, [theme]);
4462
- return /* @__PURE__ */ jsxs(Fragment, {
4463
- children: [/* @__PURE__ */ jsxs(Group, {
4464
- position: "left",
4465
- ref,
4466
- children: [/* @__PURE__ */ jsx(Text, {
4467
- children: label
4468
- }), /* @__PURE__ */ jsx(ActionIcon, {
4469
- mr: 5,
4470
- variant: "filled",
4471
- color: "blue",
4472
- disabled: !changed,
4473
- onClick: submit,
4474
- children: /* @__PURE__ */ jsx(DeviceFloppy, {
4475
- size: 20
4476
- })
4477
- })]
4478
- }), /* @__PURE__ */ jsxs(Group, {
4479
- children: [values.map((v, i) => /* @__PURE__ */ jsx(ColorInput, {
4480
- value: v,
4481
- onChange: (color2) => {
4482
- setValues((s) => {
4483
- s.splice(i, 1, color2);
4484
- return [...s];
4485
- });
4486
- },
4487
- swatches,
4488
- rightSection: /* @__PURE__ */ jsx(ActionIcon, {
4489
- onClick: () => del(i),
4490
- color: "red",
4491
- children: /* @__PURE__ */ jsx(Trash, {
4492
- size: 14
4493
- })
4494
- }),
4495
- sx: {
4496
- width: "45%"
4497
- }
4498
- })), /* @__PURE__ */ jsx(ActionIcon, {
4499
- onClick: add,
4500
- color: "blue",
4501
- variant: "outline",
4502
- children: /* @__PURE__ */ jsx(PlaylistAdd, {
4503
- size: 20
4504
- })
4505
- })]
4506
- })]
4507
- });
4935
+ function normalizeStats(stats) {
4936
+ if (!stats) {
4937
+ return {
4938
+ templates: {
4939
+ top: "",
4940
+ bottom: ""
4941
+ },
4942
+ variables: []
4943
+ };
4944
+ }
4945
+ return stats;
4508
4946
  }
4509
- const ColorArrayInput = React.forwardRef(_ColorArrayInput);
4510
- const marks = [{
4511
- label: "initial",
4512
- value: 0
4513
- }, {
4514
- label: "500",
4515
- value: 25
4516
- }, {
4517
- label: "700",
4518
- value: 50
4519
- }, {
4520
- label: "semibold",
4521
- value: 75
4522
- }, {
4523
- label: "bold",
4524
- value: 100
4525
- }];
4526
- function _MantineFontWeightSlider({
4527
- label,
4528
- value,
4529
- onChange
4530
- }, ref) {
4531
- var _a, _b;
4532
- const [mark, setMark] = React.useState((_b = (_a = marks.find((m2) => m2.label === value)) == null ? void 0 : _a.value) != null ? _b : marks[0].value);
4947
+ function VizCartesianChartPanel({
4948
+ conf,
4949
+ setConf,
4950
+ data
4951
+ }) {
4952
+ const _a = conf, {
4953
+ series,
4954
+ y_axes
4955
+ } = _a, restConf = __objRest(_a, [
4956
+ "series",
4957
+ "y_axes"
4958
+ ]);
4959
+ const defaultValues = React.useMemo(() => {
4960
+ const _a2 = restConf, {
4961
+ x_axis_name = "",
4962
+ stats
4963
+ } = _a2, rest = __objRest(_a2, [
4964
+ "x_axis_name",
4965
+ "stats"
4966
+ ]);
4967
+ return __spreadValues({
4968
+ series: withDefaults(series != null ? series : []),
4969
+ x_axis_name,
4970
+ y_axes: y_axes != null ? y_axes : [{
4971
+ name: "Y Axis",
4972
+ label_formatter: defaultNumbroFormat
4973
+ }],
4974
+ stats: normalizeStats(stats)
4975
+ }, rest);
4976
+ }, [series, restConf]);
4533
4977
  React.useEffect(() => {
4534
- const match = marks.find((s) => s.value === mark);
4535
- if (match) {
4536
- onChange(match.label);
4978
+ const configMalformed = !_.isEqual(conf, defaultValues);
4979
+ if (configMalformed) {
4980
+ setConf(defaultValues);
4537
4981
  }
4538
- }, [mark]);
4539
- return /* @__PURE__ */ jsxs(Group, {
4540
- direction: "column",
4541
- grow: true,
4542
- spacing: 0,
4543
- mt: "sm",
4544
- mb: "lg",
4545
- children: [/* @__PURE__ */ jsx(Text, {
4546
- size: "sm",
4547
- children: label
4548
- }), /* @__PURE__ */ jsx(Slider, {
4549
- label: null,
4550
- marks,
4551
- value: mark,
4552
- onChange: setMark,
4553
- step: 25,
4554
- placeholder: "Pick a font size",
4555
- ref
4556
- })]
4982
+ }, [conf, defaultValues]);
4983
+ const {
4984
+ control,
4985
+ handleSubmit,
4986
+ watch,
4987
+ getValues
4988
+ } = useForm({
4989
+ defaultValues
4557
4990
  });
4558
- }
4559
- const MantineFontWeightSlider = React.forwardRef(_MantineFontWeightSlider);
4560
- function _TextArrayInput({
4561
- label,
4562
- value,
4563
- onChange
4564
- }, ref) {
4565
- const [values, setValues] = React.useState(Array.isArray(value) ? [...value] : []);
4566
- const add = React.useCallback(() => {
4567
- setValues((s) => [...s, ""]);
4568
- }, [setValues]);
4569
- const del = React.useCallback((index2) => {
4570
- setValues((s) => {
4571
- s.splice(index2, 1);
4572
- return [...s];
4573
- });
4574
- }, [setValues]);
4991
+ watch(["x_axis_data_key", "x_axis_name"]);
4992
+ const values = getValues();
4575
4993
  const changed = React.useMemo(() => {
4576
- return !_.isEqual(values, value);
4577
- }, [values, value]);
4578
- const submit = () => {
4579
- onChange(values.map((s) => s.toString()));
4580
- };
4581
- return /* @__PURE__ */ jsxs(Fragment, {
4582
- children: [/* @__PURE__ */ jsxs(Group, {
4583
- position: "left",
4584
- ref,
4585
- children: [/* @__PURE__ */ jsx(Text, {
4586
- children: label
4587
- }), /* @__PURE__ */ jsx(ActionIcon, {
4588
- mr: 5,
4589
- variant: "filled",
4590
- color: "blue",
4591
- disabled: !changed,
4592
- onClick: submit,
4593
- children: /* @__PURE__ */ jsx(DeviceFloppy, {
4594
- size: 20
4595
- })
4596
- })]
4597
- }), /* @__PURE__ */ jsxs(Group, {
4598
- children: [values.map((v, i) => /* @__PURE__ */ jsx(TextInput, {
4599
- value: v,
4600
- onChange: (event) => {
4601
- const newValue = event.currentTarget.value;
4602
- setValues((s) => {
4603
- s.splice(i, 1, newValue);
4604
- return [...s];
4605
- });
4994
+ return !_.isEqual(values, conf);
4995
+ }, [values, conf]);
4996
+ return /* @__PURE__ */ jsx(Group, {
4997
+ direction: "column",
4998
+ mt: "md",
4999
+ spacing: "xs",
5000
+ grow: true,
5001
+ children: /* @__PURE__ */ jsxs("form", {
5002
+ onSubmit: handleSubmit(setConf),
5003
+ children: [/* @__PURE__ */ jsxs(Group, {
5004
+ position: "left",
5005
+ py: "md",
5006
+ pl: "md",
5007
+ sx: {
5008
+ borderBottom: "1px solid #eee",
5009
+ background: "#efefef"
4606
5010
  },
4607
- rightSection: /* @__PURE__ */ jsx(ActionIcon, {
4608
- onClick: () => del(i),
4609
- color: "red",
4610
- children: /* @__PURE__ */ jsx(Trash, {
4611
- size: 14
5011
+ children: [/* @__PURE__ */ jsx(Text, {
5012
+ children: "Chart Config"
5013
+ }), /* @__PURE__ */ jsx(ActionIcon, {
5014
+ type: "submit",
5015
+ mr: 5,
5016
+ variant: "filled",
5017
+ color: "blue",
5018
+ disabled: !changed,
5019
+ children: /* @__PURE__ */ jsx(DeviceFloppy, {
5020
+ size: 20
4612
5021
  })
4613
- }),
4614
- sx: {
4615
- width: "45%"
4616
- }
4617
- })), /* @__PURE__ */ jsx(ActionIcon, {
4618
- onClick: add,
4619
- color: "blue",
4620
- variant: "outline",
4621
- children: /* @__PURE__ */ jsx(PlaylistAdd, {
4622
- size: 20
4623
- })
5022
+ })]
5023
+ }), /* @__PURE__ */ jsxs(Accordion, {
5024
+ offsetIcon: false,
5025
+ multiple: true,
5026
+ initialState: {
5027
+ 0: true,
5028
+ 1: true
5029
+ },
5030
+ children: [/* @__PURE__ */ jsx(Accordion.Item, {
5031
+ label: "X Axis",
5032
+ children: /* @__PURE__ */ jsxs(Group, {
5033
+ direction: "row",
5034
+ grow: true,
5035
+ noWrap: true,
5036
+ children: [/* @__PURE__ */ jsx(Controller, {
5037
+ name: "x_axis_data_key",
5038
+ control,
5039
+ render: ({
5040
+ field
5041
+ }) => /* @__PURE__ */ jsx(DataFieldSelector, __spreadValues({
5042
+ label: "X Axis Data Field",
5043
+ required: true,
5044
+ data,
5045
+ sx: {
5046
+ flex: 1
5047
+ }
5048
+ }, field))
5049
+ }), /* @__PURE__ */ jsx(Controller, {
5050
+ name: "x_axis_name",
5051
+ control,
5052
+ render: ({
5053
+ field
5054
+ }) => /* @__PURE__ */ jsx(TextInput, __spreadValues({
5055
+ label: "X Axis Name",
5056
+ sx: {
5057
+ flex: 1
5058
+ }
5059
+ }, field))
5060
+ })]
5061
+ })
5062
+ }), /* @__PURE__ */ jsx(Accordion.Item, {
5063
+ label: "Y Axes",
5064
+ children: /* @__PURE__ */ jsx(YAxesField, {
5065
+ control,
5066
+ watch
5067
+ })
5068
+ }), /* @__PURE__ */ jsx(Accordion.Item, {
5069
+ label: "Series",
5070
+ children: /* @__PURE__ */ jsx(SeriesField, {
5071
+ control,
5072
+ watch,
5073
+ getValues,
5074
+ data
5075
+ })
5076
+ }), /* @__PURE__ */ jsx(Accordion.Item, {
5077
+ label: "Regression Lines",
5078
+ children: /* @__PURE__ */ jsx(RegressionsField, {
5079
+ control,
5080
+ watch,
5081
+ getValues,
5082
+ data
5083
+ })
5084
+ }), /* @__PURE__ */ jsx(Accordion.Item, {
5085
+ label: "Stats",
5086
+ children: /* @__PURE__ */ jsx(StatsField, {
5087
+ control,
5088
+ watch,
5089
+ data
5090
+ })
5091
+ })]
4624
5092
  })]
4625
- })]
5093
+ })
4626
5094
  });
4627
5095
  }
4628
- const TextArrayInput = React.forwardRef(_TextArrayInput);
4629
- const TemplateInput = React.forwardRef(function TemplateInput2(_a, ref) {
4630
- var _b = _a, {
4631
- value,
4632
- onChange
4633
- } = _b, rest = __objRest(_b, [
4634
- "value",
4635
- "onChange"
4636
- ]);
4637
- return /* @__PURE__ */ jsx(TextInput, __spreadValues({
4638
- ref,
4639
- value,
4640
- onChange
4641
- }, rest));
4642
- });
4643
- function TemplateVariableField({
4644
- value,
4645
- onChange,
5096
+ function VizPiePanel({
5097
+ conf: {
5098
+ label_field,
5099
+ value_field
5100
+ },
5101
+ setConf,
4646
5102
  data
4647
5103
  }) {
4648
- const colorType = value.color.type;
4649
- const handleChange = (path, newValue) => {
4650
- const v = _.cloneDeep(value);
4651
- _.set(v, path, newValue);
4652
- onChange(v);
4653
- };
4654
- return /* @__PURE__ */ jsxs(Box, {
4655
- px: "sm",
4656
- py: "md",
4657
- children: [/* @__PURE__ */ jsx(Text, {
4658
- weight: "bold",
4659
- pb: 0,
4660
- children: value.name
4661
- }), /* @__PURE__ */ jsx(Divider, {
4662
- my: "xs",
4663
- label: "Data",
4664
- labelPosition: "center"
4665
- }), /* @__PURE__ */ jsxs(Group, {
4666
- direction: "row",
4667
- grow: true,
4668
- noWrap: true,
4669
- children: [/* @__PURE__ */ jsx(TextInput, {
4670
- label: "Name",
4671
- required: true,
4672
- value: value.name,
4673
- onChange: (e) => handleChange("name", e.currentTarget.value)
4674
- }), /* @__PURE__ */ jsx(DataFieldSelector, {
4675
- label: "Data Field",
4676
- required: true,
4677
- data,
4678
- value: value.data_field,
4679
- onChange: (v) => handleChange("data_field", v)
4680
- }), /* @__PURE__ */ jsx(AggregationSelector, {
4681
- label: "Aggregation",
4682
- value: value.aggregation,
4683
- onChange: (v) => handleChange("aggregation", v)
5104
+ const form = useForm$1({
5105
+ initialValues: {
5106
+ label_field,
5107
+ value_field
5108
+ }
5109
+ });
5110
+ return /* @__PURE__ */ jsx(Group, {
5111
+ direction: "column",
5112
+ mt: "md",
5113
+ spacing: "xs",
5114
+ grow: true,
5115
+ children: /* @__PURE__ */ jsxs("form", {
5116
+ onSubmit: form.onSubmit(setConf),
5117
+ children: [/* @__PURE__ */ jsxs(Group, {
5118
+ position: "apart",
5119
+ mb: "lg",
5120
+ sx: {
5121
+ position: "relative"
5122
+ },
5123
+ children: [/* @__PURE__ */ jsx(Text, {
5124
+ children: "Pie Config"
5125
+ }), /* @__PURE__ */ jsx(ActionIcon, {
5126
+ type: "submit",
5127
+ mr: 5,
5128
+ variant: "filled",
5129
+ color: "blue",
5130
+ children: /* @__PURE__ */ jsx(DeviceFloppy, {
5131
+ size: 20
5132
+ })
5133
+ })]
5134
+ }), /* @__PURE__ */ jsxs(Group, {
5135
+ direction: "column",
5136
+ mt: "md",
5137
+ spacing: "xs",
5138
+ grow: true,
5139
+ p: "md",
5140
+ mb: "sm",
5141
+ sx: {
5142
+ border: "1px solid #eee",
5143
+ borderRadius: "5px"
5144
+ },
5145
+ children: [/* @__PURE__ */ jsx(DataFieldSelector, __spreadValues({
5146
+ label: "Label Field",
5147
+ required: true,
5148
+ data
5149
+ }, form.getInputProps("label_field"))), /* @__PURE__ */ jsx(DataFieldSelector, __spreadValues({
5150
+ label: "Value Field",
5151
+ required: true,
5152
+ data
5153
+ }, form.getInputProps("value_field")))]
4684
5154
  })]
4685
- }), /* @__PURE__ */ jsx(NumbroFormatSelector, {
4686
- value: value.formatter,
4687
- onChange: (v) => handleChange("formatter", v)
4688
- }), /* @__PURE__ */ jsx(Divider, {
4689
- my: "xs",
4690
- label: "Typography",
4691
- labelPosition: "center"
4692
- }), /* @__PURE__ */ jsx(Group, {
4693
- direction: "column",
4694
- grow: true,
4695
- children: /* @__PURE__ */ jsx(TextInput, {
4696
- label: "Font Size",
4697
- placeholder: "10px, 1em, 1rem, 100%...",
5155
+ })
5156
+ });
5157
+ }
5158
+ function VizRichTextPanel({
5159
+ conf,
5160
+ setConf
5161
+ }) {
5162
+ const defaultValues = React.useMemo(() => {
5163
+ const {
5164
+ content = ""
5165
+ } = conf;
5166
+ return {
5167
+ content
5168
+ };
5169
+ }, [conf]);
5170
+ React.useEffect(() => {
5171
+ const configMalformed = !_.isEqual(conf, defaultValues);
5172
+ if (configMalformed) {
5173
+ setConf(defaultValues);
5174
+ }
5175
+ }, [conf, defaultValues]);
5176
+ const {
5177
+ control,
5178
+ handleSubmit,
5179
+ watch,
5180
+ getValues
5181
+ } = useForm({
5182
+ defaultValues
5183
+ });
5184
+ watch("content");
5185
+ const values = getValues();
5186
+ const changed = React.useMemo(() => {
5187
+ return !_.isEqual(values, conf);
5188
+ }, [values, conf]);
5189
+ return /* @__PURE__ */ jsx(Group, {
5190
+ direction: "column",
5191
+ mt: "md",
5192
+ spacing: "xs",
5193
+ grow: true,
5194
+ children: /* @__PURE__ */ jsxs("form", {
5195
+ onSubmit: handleSubmit(setConf),
5196
+ children: [/* @__PURE__ */ jsxs(Group, {
5197
+ position: "left",
5198
+ py: "md",
5199
+ pl: "md",
4698
5200
  sx: {
4699
- flex: 1
5201
+ borderBottom: "1px solid #eee",
5202
+ background: "#efefef"
4700
5203
  },
4701
- value: value.size,
4702
- onChange: (e) => handleChange("size", e.currentTarget.value)
4703
- })
4704
- }), /* @__PURE__ */ jsx(Group, {
4705
- position: "apart",
4706
- grow: true,
4707
- sx: {
4708
- "> *": {
4709
- flexGrow: 1,
4710
- maxWidth: "100%"
4711
- }
4712
- },
4713
- children: /* @__PURE__ */ jsx(MantineFontWeightSlider, {
4714
- label: "Font Weight",
4715
- value: value.weight,
4716
- onChange: (v) => handleChange("weight", v)
4717
- })
4718
- }), /* @__PURE__ */ jsx(Divider, {
4719
- my: "xs",
4720
- label: "Style",
4721
- labelPosition: "center"
4722
- }), /* @__PURE__ */ jsxs(Group, {
4723
- direction: "column",
4724
- grow: true,
4725
- children: [/* @__PURE__ */ jsx(Select, {
4726
- label: "Color Type",
4727
- data: [{
4728
- label: "Static Color",
4729
- value: "static"
4730
- }, {
4731
- label: "Continuous Color",
4732
- value: "continuous"
4733
- }],
4734
- value: value.color.type,
4735
- onChange: (v) => handleChange("color.type", v)
4736
- }), colorType === "static" && /* @__PURE__ */ jsx(MantineColorSelector, {
4737
- value: value.color.staticColor,
4738
- onChange: (v) => handleChange("color.staticColor", v)
4739
- }), colorType === "continuous" && /* @__PURE__ */ jsxs(Fragment, {
4740
- children: [/* @__PURE__ */ jsx(TextArrayInput, {
4741
- label: "Value Range",
4742
- value: value.color.valueRange,
4743
- onChange: (v) => handleChange("color.valueRange", v)
4744
- }), /* @__PURE__ */ jsx(ColorArrayInput, {
4745
- label: "Color Range",
4746
- value: value.color.colorRange,
4747
- onChange: (v) => handleChange("color.colorRange", v)
5204
+ children: [/* @__PURE__ */ jsx(Text, {
5205
+ children: "Content"
5206
+ }), /* @__PURE__ */ jsx(ActionIcon, {
5207
+ type: "submit",
5208
+ mr: 5,
5209
+ variant: "filled",
5210
+ color: "blue",
5211
+ disabled: !changed,
5212
+ children: /* @__PURE__ */ jsx(DeviceFloppy, {
5213
+ size: 20
5214
+ })
4748
5215
  })]
5216
+ }), /* @__PURE__ */ jsx(Controller, {
5217
+ name: "content",
5218
+ control,
5219
+ render: ({
5220
+ field
5221
+ }) => /* @__PURE__ */ jsx(RichTextEditor, __spreadValues({
5222
+ sx: {
5223
+ flex: 1
5224
+ }
5225
+ }, field))
4749
5226
  })]
4750
- })]
5227
+ })
4751
5228
  });
4752
5229
  }
5230
+ function updateSchema(legacyConf) {
5231
+ if ("variables" in legacyConf) {
5232
+ return legacyConf;
5233
+ }
5234
+ const {
5235
+ align,
5236
+ size,
5237
+ weight,
5238
+ color: color2,
5239
+ content: {
5240
+ prefix = "",
5241
+ data_field = "value",
5242
+ formatter = {
5243
+ output: "number",
5244
+ mantissa: 0
5245
+ },
5246
+ postfix = ""
5247
+ } = {}
5248
+ } = legacyConf;
5249
+ return {
5250
+ align,
5251
+ template: `${prefix} \${value} ${postfix}`,
5252
+ variables: [
5253
+ {
5254
+ name: "value",
5255
+ data_field,
5256
+ aggregation: "none",
5257
+ formatter,
5258
+ color: color2,
5259
+ weight,
5260
+ size
5261
+ }
5262
+ ]
5263
+ };
5264
+ }
4753
5265
  function VariableField({
4754
5266
  control,
4755
5267
  index: index2,
@@ -4807,21 +5319,7 @@ function VariablesField({
4807
5319
  const controlledFields = fields.map((field, index2) => {
4808
5320
  return __spreadValues(__spreadValues({}, field), watchFieldArray[index2]);
4809
5321
  });
4810
- const add = () => append({
4811
- name: randomId(),
4812
- size: "20px",
4813
- weight: "bold",
4814
- color: {
4815
- type: "static",
4816
- staticColor: "blue"
4817
- },
4818
- data_field: "",
4819
- aggregation: "none",
4820
- formatter: {
4821
- output: "number",
4822
- mantissa: 0
4823
- }
4824
- });
5322
+ const add = () => append(getANewVariable());
4825
5323
  return /* @__PURE__ */ jsxs(Group, {
4826
5324
  direction: "column",
4827
5325
  grow: true,
@@ -5561,16 +6059,34 @@ function PanelSettingsModal({
5561
6059
  }
5562
6060
  var titleBar = "";
5563
6061
  function PanelTitleBar({}) {
6062
+ const modals = useModals();
5564
6063
  const [opened, setOpened] = React.useState(false);
5565
6064
  const open = () => setOpened(true);
5566
6065
  const close = () => setOpened(false);
5567
6066
  const {
6067
+ id,
5568
6068
  title,
5569
6069
  refreshData
5570
6070
  } = React.useContext(PanelContext);
5571
6071
  const {
5572
6072
  inEditMode
5573
6073
  } = React.useContext(LayoutStateContext);
6074
+ const {
6075
+ duplidatePanel,
6076
+ removePanelByID
6077
+ } = React.useContext(DashboardActionContext);
6078
+ const duplicate = React.useCallback(() => {
6079
+ duplidatePanel(id);
6080
+ }, [duplidatePanel, id]);
6081
+ const remove = () => modals.openConfirmModal({
6082
+ title: "Delete this panel?",
6083
+ labels: {
6084
+ confirm: "Confirm",
6085
+ cancel: "Cancel"
6086
+ },
6087
+ onCancel: () => console.log("Cancel"),
6088
+ onConfirm: () => removePanelByID(id)
6089
+ });
5574
6090
  return /* @__PURE__ */ jsxs(Box, {
5575
6091
  sx: {
5576
6092
  position: "relative"
@@ -5611,8 +6127,14 @@ function PanelTitleBar({}) {
5611
6127
  }),
5612
6128
  children: "Settings"
5613
6129
  }), /* @__PURE__ */ jsx(Divider, {}), /* @__PURE__ */ jsx(Menu.Item, {
6130
+ onClick: duplicate,
6131
+ icon: /* @__PURE__ */ jsx(Copy, {
6132
+ size: 14
6133
+ }),
6134
+ children: "Duplicate"
6135
+ }), /* @__PURE__ */ jsx(Menu.Item, {
5614
6136
  color: "red",
5615
- disabled: true,
6137
+ onClick: remove,
5616
6138
  icon: /* @__PURE__ */ jsx(Trash, {
5617
6139
  size: 14
5618
6140
  }),
@@ -5672,6 +6194,7 @@ function Panel({
5672
6194
  const refreshData = refresh;
5673
6195
  return /* @__PURE__ */ jsx(PanelContext.Provider, {
5674
6196
  value: {
6197
+ id,
5675
6198
  data,
5676
6199
  loading,
5677
6200
  title,
@@ -5701,7 +6224,6 @@ function DashboardLayout({
5701
6224
  setPanels,
5702
6225
  className = "layout",
5703
6226
  rowHeight = 10,
5704
- onRemoveItem,
5705
6227
  isDraggable,
5706
6228
  isResizable
5707
6229
  }) {
@@ -5737,7 +6259,6 @@ function DashboardLayout({
5737
6259
  children: /* @__PURE__ */ jsx(Panel, __spreadProps(__spreadValues({
5738
6260
  id
5739
6261
  }, rest), {
5740
- destroy: () => onRemoveItem(id),
5741
6262
  update: (panel) => {
5742
6263
  setPanels((prevs) => {
5743
6264
  prevs.splice(index2, 1, panel);
@@ -6481,9 +7002,12 @@ function DashboardActions({
6481
7002
  mode,
6482
7003
  setMode,
6483
7004
  hasChanges,
6484
- addPanel,
6485
- saveChanges
7005
+ saveChanges,
7006
+ revertChanges
6486
7007
  }) {
7008
+ const {
7009
+ addPanel
7010
+ } = React.useContext(DashboardActionContext);
6487
7011
  const {
6488
7012
  inLayoutMode,
6489
7013
  inEditMode,
@@ -6533,6 +7057,7 @@ function DashboardActions({
6533
7057
  color: "red",
6534
7058
  size: "sm",
6535
7059
  disabled: !hasChanges,
7060
+ onClick: revertChanges,
6536
7061
  leftIcon: /* @__PURE__ */ jsx(Recycle, {
6537
7062
  size: 20
6538
7063
  }),
@@ -6588,6 +7113,11 @@ function Dashboard({
6588
7113
  });
6589
7114
  await update(d);
6590
7115
  };
7116
+ const revertDashboardChanges = () => {
7117
+ setPanels(dashboard.panels);
7118
+ setSQLSnippets(dashboard.definition.sqlSnippets);
7119
+ setQueries(dashboard.definition.queries);
7120
+ };
6591
7121
  const addPanel = () => {
6592
7122
  const id = randomId();
6593
7123
  const newItem = {
@@ -6608,6 +7138,24 @@ function Dashboard({
6608
7138
  };
6609
7139
  setPanels((prevs) => [...prevs, newItem]);
6610
7140
  };
7141
+ const duplidatePanel = (id) => {
7142
+ try {
7143
+ const panel = panels.find((p2) => p2.id === id);
7144
+ if (!panel) {
7145
+ throw new Error(`[duplicate panel] Can't find a panel by id[${id}]`);
7146
+ }
7147
+ const newPanel = __spreadProps(__spreadValues({}, panel), {
7148
+ id: randomId(),
7149
+ layout: __spreadProps(__spreadValues({}, panel.layout), {
7150
+ x: 0,
7151
+ y: Infinity
7152
+ })
7153
+ });
7154
+ setPanels((prevs) => [...prevs, newPanel]);
7155
+ } catch (error) {
7156
+ console.error(error);
7157
+ }
7158
+ };
6611
7159
  const removePanelByID = (id) => {
6612
7160
  const index2 = panels.findIndex((p2) => p2.id === id);
6613
7161
  setPanels((prevs) => {
@@ -6624,34 +7172,42 @@ function Dashboard({
6624
7172
  queries,
6625
7173
  setQueries
6626
7174
  }), [sqlSnippets, setSQLSnippets, queries, setQueries]);
6627
- return /* @__PURE__ */ jsx(ContextInfoContext.Provider, {
6628
- value: context,
6629
- children: /* @__PURE__ */ jsx("div", {
6630
- className,
6631
- children: /* @__PURE__ */ jsx(DefinitionContext.Provider, {
6632
- value: definitions,
6633
- children: /* @__PURE__ */ jsxs(LayoutStateContext.Provider, {
6634
- value: {
6635
- layoutFrozen,
6636
- freezeLayout,
6637
- mode,
6638
- inEditMode,
6639
- inLayoutMode,
6640
- inUseMode
6641
- },
6642
- children: [/* @__PURE__ */ jsx(DashboardActions, {
6643
- mode,
6644
- setMode,
6645
- hasChanges,
6646
- addPanel,
6647
- saveChanges: saveDashboardChanges
6648
- }), /* @__PURE__ */ jsx(DashboardLayout, {
6649
- panels,
6650
- setPanels,
6651
- isDraggable: inLayoutMode,
6652
- isResizable: inLayoutMode,
6653
- onRemoveItem: removePanelByID
6654
- })]
7175
+ return /* @__PURE__ */ jsx(ModalsProvider, {
7176
+ children: /* @__PURE__ */ jsx(ContextInfoContext.Provider, {
7177
+ value: context,
7178
+ children: /* @__PURE__ */ jsx(DashboardActionContext.Provider, {
7179
+ value: {
7180
+ addPanel,
7181
+ duplidatePanel,
7182
+ removePanelByID
7183
+ },
7184
+ children: /* @__PURE__ */ jsx(DefinitionContext.Provider, {
7185
+ value: definitions,
7186
+ children: /* @__PURE__ */ jsx(LayoutStateContext.Provider, {
7187
+ value: {
7188
+ layoutFrozen,
7189
+ freezeLayout,
7190
+ mode,
7191
+ inEditMode,
7192
+ inLayoutMode,
7193
+ inUseMode
7194
+ },
7195
+ children: /* @__PURE__ */ jsxs("div", {
7196
+ className,
7197
+ children: [/* @__PURE__ */ jsx(DashboardActions, {
7198
+ mode,
7199
+ setMode,
7200
+ hasChanges,
7201
+ saveChanges: saveDashboardChanges,
7202
+ revertChanges: revertDashboardChanges
7203
+ }), /* @__PURE__ */ jsx(DashboardLayout, {
7204
+ panels,
7205
+ setPanels,
7206
+ isDraggable: inLayoutMode,
7207
+ isResizable: inLayoutMode
7208
+ })]
7209
+ })
7210
+ })
6655
7211
  })
6656
7212
  })
6657
7213
  })
@@ -6698,24 +7254,26 @@ function ReadOnlyDashboard({
6698
7254
  setQueries: () => {
6699
7255
  }
6700
7256
  }), [dashboard]);
6701
- return /* @__PURE__ */ jsx(ContextInfoContext.Provider, {
6702
- value: context,
6703
- children: /* @__PURE__ */ jsx("div", {
6704
- className,
6705
- children: /* @__PURE__ */ jsx(DefinitionContext.Provider, {
6706
- value: definition,
6707
- children: /* @__PURE__ */ jsx(LayoutStateContext.Provider, {
6708
- value: {
6709
- layoutFrozen: true,
6710
- freezeLayout: () => {
7257
+ return /* @__PURE__ */ jsx(ModalsProvider, {
7258
+ children: /* @__PURE__ */ jsx(ContextInfoContext.Provider, {
7259
+ value: context,
7260
+ children: /* @__PURE__ */ jsx("div", {
7261
+ className,
7262
+ children: /* @__PURE__ */ jsx(DefinitionContext.Provider, {
7263
+ value: definition,
7264
+ children: /* @__PURE__ */ jsx(LayoutStateContext.Provider, {
7265
+ value: {
7266
+ layoutFrozen: true,
7267
+ freezeLayout: () => {
7268
+ },
7269
+ mode: DashboardMode.Use,
7270
+ inEditMode: false,
7271
+ inLayoutMode: false,
7272
+ inUseMode: true
6711
7273
  },
6712
- mode: DashboardMode.Use,
6713
- inEditMode: false,
6714
- inLayoutMode: false,
6715
- inUseMode: true
6716
- },
6717
- children: /* @__PURE__ */ jsx(ReadOnlyDashboardLayout, {
6718
- panels: dashboard.panels
7274
+ children: /* @__PURE__ */ jsx(ReadOnlyDashboardLayout, {
7275
+ panels: dashboard.panels
7276
+ })
6719
7277
  })
6720
7278
  })
6721
7279
  })