@devtable/dashboard 1.22.0 → 1.26.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.
@@ -36,7 +36,7 @@ var __publicField = (obj, key, value) => {
36
36
  import React 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, JsonInput, Modal, AppShell, Tabs, Menu, Divider, Container, Textarea } from "@mantine/core";
39
+ import { Popover, Tooltip, Group, Text, ActionIcon, Box, Button, TextInput, LoadingOverlay, Table, Select, useMantineTheme, ColorSwatch, Switch, Slider, SegmentedControl, NumberInput, ColorInput, Divider, Accordion, JsonInput, Modal, AppShell, Tabs, Menu, Container, Textarea } from "@mantine/core";
40
40
  import { useRequest } from "ahooks";
41
41
  import axios from "axios";
42
42
  import { InfoCircle, DeviceFloppy, Refresh, Trash, PlaylistAdd, Settings, Resize, Paint, PlayerPlay, Database, Recycle, Share } from "tabler-icons-react";
@@ -109,20 +109,20 @@ function explainSQL(sql, context, definitions) {
109
109
  const APIClient = {
110
110
  baseURL: "http://localhost:31200",
111
111
  getRequest(method) {
112
- return (url, data, options = {}) => {
112
+ return (url, data, options2 = {}) => {
113
113
  const headers = __spreadValues({
114
114
  "X-Requested-With": "XMLHttpRequest",
115
- "Content-Type": options.string ? "application/x-www-form-urlencoded" : "application/json"
116
- }, options.headers);
115
+ "Content-Type": options2.string ? "application/x-www-form-urlencoded" : "application/json"
116
+ }, options2.headers);
117
117
  const conf = {
118
118
  baseURL: this.baseURL,
119
119
  method,
120
120
  url,
121
- params: method === "GET" ? data : options.params,
121
+ params: method === "GET" ? data : options2.params,
122
122
  headers
123
123
  };
124
124
  if (method === "POST") {
125
- conf.data = options.string ? JSON.stringify(data) : data;
125
+ conf.data = options2.string ? JSON.stringify(data) : data;
126
126
  }
127
127
  return axios(conf).then((res) => {
128
128
  return res.data;
@@ -591,7 +591,7 @@ function PickQuery({}) {
591
591
  data,
592
592
  loading
593
593
  } = React.useContext(PanelContext);
594
- const options = React.useMemo(() => {
594
+ const options2 = React.useMemo(() => {
595
595
  return queries.map((d) => ({
596
596
  value: d.id,
597
597
  label: d.id
@@ -610,7 +610,7 @@ function PickQuery({}) {
610
610
  children: [/* @__PURE__ */ jsx(Text, {
611
611
  children: "Select a Query"
612
612
  }), /* @__PURE__ */ jsx(Select, {
613
- data: options,
613
+ data: options2,
614
614
  value: queryID,
615
615
  onChange: setQueryID,
616
616
  allowDeselect: false,
@@ -1567,10 +1567,10 @@ var ecStat = { exports: {} };
1567
1567
  var __WEBPACK_AMD_DEFINE_RESULT__;
1568
1568
  !(__WEBPACK_AMD_DEFINE_RESULT__ = function(require2) {
1569
1569
  var quantile = __webpack_require__(13);
1570
- function median(data) {
1570
+ function median2(data) {
1571
1571
  return quantile(data, 0.5);
1572
1572
  }
1573
- return median;
1573
+ return median2;
1574
1574
  }.call(exports2, __webpack_require__, exports2, module2), __WEBPACK_AMD_DEFINE_RESULT__ !== void 0 && (module2.exports = __WEBPACK_AMD_DEFINE_RESULT__));
1575
1575
  },
1576
1576
  function(module2, exports2, __webpack_require__) {
@@ -2008,7 +2008,7 @@ const defaultOption$1 = {
2008
2008
  }
2009
2009
  },
2010
2010
  grid: {
2011
- top: 30,
2011
+ top: 0,
2012
2012
  left: 15,
2013
2013
  right: 15,
2014
2014
  bottom: 30,
@@ -2102,792 +2102,929 @@ function getOption(conf, data) {
2102
2102
  };
2103
2103
  return _.merge({}, defaultOption$1, customOptions);
2104
2104
  }
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;
2105
+ function median(numbers) {
2106
+ const sorted = Array.from(numbers).sort((a, b) => a - b);
2107
+ const middle = Math.floor(sorted.length / 2);
2108
+ if (sorted.length % 2 === 0) {
2109
+ return (sorted[middle - 1] + sorted[middle]) / 2;
2118
2110
  }
2119
- return /* @__PURE__ */ jsx(ReactEChartsCore, {
2120
- echarts,
2121
- option,
2122
- style: {
2123
- width,
2124
- height
2125
- }
2126
- });
2111
+ return sorted[middle];
2127
2112
  }
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
- });
2113
+ function aggregateValue(data, data_field, aggregation) {
2114
+ var _a, _b, _c;
2115
+ const numbers = data.map((d) => d[data_field]);
2116
+ switch (aggregation) {
2117
+ case "sum":
2118
+ return _.sum(numbers);
2119
+ case "mean":
2120
+ return _.mean(numbers);
2121
+ case "median":
2122
+ return median(numbers);
2123
+ case "max":
2124
+ return (_a = _.max(numbers)) != null ? _a : 0;
2125
+ case "min":
2126
+ return (_b = _.min(numbers)) != null ? _b : 0;
2127
+ default:
2128
+ return (_c = data[0]) == null ? void 0 : _c[data_field];
2129
+ }
2142
2130
  }
2143
- function ELOCCell({
2144
- value
2145
- }) {
2146
- return /* @__PURE__ */ jsx(Text, {
2147
- component: "span",
2148
- children: value
2149
- });
2131
+ var invariant = function() {
2132
+ };
2133
+ const clamp$1 = (min, max, v) => Math.min(Math.max(v, min), max);
2134
+ const progress = (from, to, value) => {
2135
+ const toFromDifference = to - from;
2136
+ return toFromDifference === 0 ? 1 : (value - from) / toFromDifference;
2137
+ };
2138
+ const mix = (from, to, progress2) => -progress2 * from + progress2 * to + from;
2139
+ const clamp = (min, max) => (v) => Math.max(Math.min(v, max), min);
2140
+ const sanitize = (v) => v % 1 ? Number(v.toFixed(5)) : v;
2141
+ const floatRegex = /(-)?([\d]*\.?[\d])+/g;
2142
+ 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;
2143
+ const singleColorRegex = /^(#[0-9a-f]{3}|#(?:[0-9a-f]{2}){2,4}|(rgb|hsl)a?\((-?[\d\.]+%?[,\s]+){2,3}\s*\/*\s*[\d\.]+%?\))$/i;
2144
+ function isString(v) {
2145
+ return typeof v === "string";
2150
2146
  }
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
- });
2147
+ const number = {
2148
+ test: (v) => typeof v === "number",
2149
+ parse: parseFloat,
2150
+ transform: (v) => v
2151
+ };
2152
+ const alpha = Object.assign(Object.assign({}, number), { transform: clamp(0, 1) });
2153
+ Object.assign(Object.assign({}, number), { default: 1 });
2154
+ const createUnitType = (unit) => ({
2155
+ test: (v) => isString(v) && v.endsWith(unit) && v.split(" ").length === 1,
2156
+ parse: parseFloat,
2157
+ transform: (v) => `${v}${unit}`
2158
+ });
2159
+ const percent = createUnitType("%");
2160
+ Object.assign(Object.assign({}, percent), { parse: (v) => percent.parse(v) / 100, transform: (v) => percent.transform(v * 100) });
2161
+ const isColorString = (type, testProp) => (v) => {
2162
+ return Boolean(isString(v) && singleColorRegex.test(v) && v.startsWith(type) || testProp && Object.prototype.hasOwnProperty.call(v, testProp));
2163
+ };
2164
+ const splitColor = (aName, bName, cName) => (v) => {
2165
+ if (!isString(v))
2166
+ return v;
2167
+ const [a, b, c, alpha2] = v.match(floatRegex);
2168
+ return {
2169
+ [aName]: parseFloat(a),
2170
+ [bName]: parseFloat(b),
2171
+ [cName]: parseFloat(c),
2172
+ alpha: alpha2 !== void 0 ? parseFloat(alpha2) : 1
2173
+ };
2174
+ };
2175
+ const hsla = {
2176
+ test: isColorString("hsl", "hue"),
2177
+ parse: splitColor("hue", "saturation", "lightness"),
2178
+ transform: ({ hue, saturation, lightness, alpha: alpha$1 = 1 }) => {
2179
+ return "hsla(" + Math.round(hue) + ", " + percent.transform(sanitize(saturation)) + ", " + percent.transform(sanitize(lightness)) + ", " + sanitize(alpha.transform(alpha$1)) + ")";
2180
+ }
2181
+ };
2182
+ const clampRgbUnit = clamp(0, 255);
2183
+ const rgbUnit = Object.assign(Object.assign({}, number), { transform: (v) => Math.round(clampRgbUnit(v)) });
2184
+ const rgba = {
2185
+ test: isColorString("rgb", "red"),
2186
+ parse: splitColor("red", "green", "blue"),
2187
+ transform: ({ red, green, blue, alpha: alpha$1 = 1 }) => "rgba(" + rgbUnit.transform(red) + ", " + rgbUnit.transform(green) + ", " + rgbUnit.transform(blue) + ", " + sanitize(alpha.transform(alpha$1)) + ")"
2188
+ };
2189
+ function parseHex(v) {
2190
+ let r = "";
2191
+ let g = "";
2192
+ let b = "";
2193
+ let a = "";
2194
+ if (v.length > 5) {
2195
+ r = v.substr(1, 2);
2196
+ g = v.substr(3, 2);
2197
+ b = v.substr(5, 2);
2198
+ a = v.substr(7, 2);
2199
+ } else {
2200
+ r = v.substr(1, 1);
2201
+ g = v.substr(2, 1);
2202
+ b = v.substr(3, 1);
2203
+ a = v.substr(4, 1);
2204
+ r += r;
2205
+ g += g;
2206
+ b += b;
2207
+ a += a;
2208
+ }
2209
+ return {
2210
+ red: parseInt(r, 16),
2211
+ green: parseInt(g, 16),
2212
+ blue: parseInt(b, 16),
2213
+ alpha: a ? parseInt(a, 16) / 255 : 1
2214
+ };
2161
2215
  }
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
- });
2216
+ const hex = {
2217
+ test: isColorString("#"),
2218
+ parse: parseHex,
2219
+ transform: rgba.transform
2220
+ };
2221
+ const color = {
2222
+ test: (v) => rgba.test(v) || hex.test(v) || hsla.test(v),
2223
+ parse: (v) => {
2224
+ if (rgba.test(v)) {
2225
+ return rgba.parse(v);
2226
+ } else if (hsla.test(v)) {
2227
+ return hsla.parse(v);
2228
+ } else {
2229
+ return hex.parse(v);
2230
+ }
2231
+ },
2232
+ transform: (v) => {
2233
+ return isString(v) ? v : v.hasOwnProperty("red") ? rgba.transform(v) : hsla.transform(v);
2234
+ }
2235
+ };
2236
+ const colorToken = "${c}";
2237
+ const numberToken = "${n}";
2238
+ function test(v) {
2239
+ var _a, _b, _c, _d;
2240
+ return isNaN(v) && isString(v) && ((_b = (_a = v.match(floatRegex)) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) + ((_d = (_c = v.match(colorRegex)) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : 0) > 0;
2173
2241
  }
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
- });
2242
+ function analyse$1(v) {
2243
+ if (typeof v === "number")
2244
+ v = `${v}`;
2245
+ const values = [];
2246
+ let numColors = 0;
2247
+ const colors = v.match(colorRegex);
2248
+ if (colors) {
2249
+ numColors = colors.length;
2250
+ v = v.replace(colorRegex, colorToken);
2251
+ values.push(...colors.map(color.parse));
2252
+ }
2253
+ const numbers = v.match(floatRegex);
2254
+ if (numbers) {
2255
+ v = v.replace(floatRegex, numberToken);
2256
+ values.push(...numbers.map(number.parse));
2195
2257
  }
2258
+ return { values, numColors, tokenised: v };
2196
2259
  }
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
2260
+ function parse(v) {
2261
+ return analyse$1(v).values;
2262
+ }
2263
+ function createTransformer(v) {
2264
+ const { values, numColors, tokenised } = analyse$1(v);
2265
+ const numValues = values.length;
2266
+ return (v2) => {
2267
+ let output = tokenised;
2268
+ for (let i = 0; i < numValues; i++) {
2269
+ output = output.replace(i < numColors ? colorToken : numberToken, i < numColors ? color.transform(v2[i]) : sanitize(v2[i]));
2231
2270
  }
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
- }
2271
+ return output;
2272
+ };
2284
2273
  }
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
- });
2274
+ const convertNumbersToZero = (v) => typeof v === "number" ? 0 : v;
2275
+ function getAnimatableNone(v) {
2276
+ const parsed = parse(v);
2277
+ const transformer = createTransformer(v);
2278
+ return transformer(parsed.map(convertNumbersToZero));
2308
2279
  }
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
- });
2280
+ const complex = { test, parse, createTransformer, getAnimatableNone };
2281
+ function hueToRgb(p2, q2, t) {
2282
+ if (t < 0)
2283
+ t += 1;
2284
+ if (t > 1)
2285
+ t -= 1;
2286
+ if (t < 1 / 6)
2287
+ return p2 + (q2 - p2) * 6 * t;
2288
+ if (t < 1 / 2)
2289
+ return q2;
2290
+ if (t < 2 / 3)
2291
+ return p2 + (q2 - p2) * (2 / 3 - t) * 6;
2292
+ return p2;
2380
2293
  }
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
2294
+ function hslaToRgba({ hue, saturation, lightness, alpha: alpha2 }) {
2295
+ hue /= 360;
2296
+ saturation /= 100;
2297
+ lightness /= 100;
2298
+ let red = 0;
2299
+ let green = 0;
2300
+ let blue = 0;
2301
+ if (!saturation) {
2302
+ red = green = blue = lightness;
2303
+ } else {
2304
+ const q2 = lightness < 0.5 ? lightness * (1 + saturation) : lightness + saturation - lightness * saturation;
2305
+ const p2 = 2 * lightness - q2;
2306
+ red = hueToRgb(p2, q2, hue + 1 / 3);
2307
+ green = hueToRgb(p2, q2, hue);
2308
+ blue = hueToRgb(p2, q2, hue - 1 / 3);
2414
2309
  }
2310
+ return {
2311
+ red: Math.round(red * 255),
2312
+ green: Math.round(green * 255),
2313
+ blue: Math.round(blue * 255),
2314
+ alpha: alpha2
2315
+ };
2316
+ }
2317
+ const mixLinearColor = (from, to, v) => {
2318
+ const fromExpo = from * from;
2319
+ const toExpo = to * to;
2320
+ return Math.sqrt(Math.max(0, v * (toExpo - fromExpo) + fromExpo));
2415
2321
  };
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
- }
2322
+ const colorTypes = [hex, rgba, hsla];
2323
+ const getColorType = (v) => colorTypes.find((type) => type.test(v));
2324
+ const mixColor = (from, to) => {
2325
+ let fromColorType = getColorType(from);
2326
+ let toColorType = getColorType(to);
2327
+ let fromColor = fromColorType.parse(from);
2328
+ let toColor = toColorType.parse(to);
2329
+ if (fromColorType === hsla) {
2330
+ fromColor = hslaToRgba(fromColor);
2331
+ fromColorType = rgba;
2332
+ }
2333
+ if (toColorType === hsla) {
2334
+ toColor = hslaToRgba(toColor);
2335
+ toColorType = rgba;
2336
+ }
2337
+ const blended = Object.assign({}, fromColor);
2338
+ return (v) => {
2339
+ for (const key in blended) {
2340
+ if (key !== "alpha") {
2341
+ blended[key] = mixLinearColor(fromColor[key], toColor[key], v);
2446
2342
  }
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
2343
  }
2461
- });
2462
- }
2463
- var invariant = function() {
2464
- };
2465
- const clamp$1 = (min, max, v) => Math.min(Math.max(v, min), max);
2466
- const progress = (from, to, value) => {
2467
- const toFromDifference = to - from;
2468
- return toFromDifference === 0 ? 1 : (value - from) / toFromDifference;
2344
+ blended.alpha = mix(fromColor.alpha, toColor.alpha, v);
2345
+ return fromColorType.transform(blended);
2346
+ };
2469
2347
  };
2470
- const mix = (from, to, progress2) => -progress2 * from + progress2 * to + from;
2471
- const clamp = (min, max) => (v) => Math.max(Math.min(v, max), min);
2472
- const sanitize = (v) => v % 1 ? Number(v.toFixed(5)) : v;
2473
- const floatRegex = /(-)?([\d]*\.?[\d])+/g;
2474
- 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;
2475
- const singleColorRegex = /^(#[0-9a-f]{3}|#(?:[0-9a-f]{2}){2,4}|(rgb|hsl)a?\((-?[\d\.]+%?[,\s]+){2,3}\s*\/*\s*[\d\.]+%?\))$/i;
2476
- function isString(v) {
2477
- return typeof v === "string";
2348
+ const isNum = (v) => typeof v === "number";
2349
+ const combineFunctions = (a, b) => (v) => b(a(v));
2350
+ const pipe = (...transformers) => transformers.reduce(combineFunctions);
2351
+ function getMixer(origin, target) {
2352
+ if (isNum(origin)) {
2353
+ return (v) => mix(origin, target, v);
2354
+ } else if (color.test(origin)) {
2355
+ return mixColor(origin, target);
2356
+ } else {
2357
+ return mixComplex(origin, target);
2358
+ }
2478
2359
  }
2479
- const number = {
2480
- test: (v) => typeof v === "number",
2481
- parse: parseFloat,
2482
- transform: (v) => v
2483
- };
2484
- const alpha = Object.assign(Object.assign({}, number), { transform: clamp(0, 1) });
2485
- Object.assign(Object.assign({}, number), { default: 1 });
2486
- const createUnitType = (unit) => ({
2487
- test: (v) => isString(v) && v.endsWith(unit) && v.split(" ").length === 1,
2488
- parse: parseFloat,
2489
- transform: (v) => `${v}${unit}`
2490
- });
2491
- const percent = createUnitType("%");
2492
- Object.assign(Object.assign({}, percent), { parse: (v) => percent.parse(v) / 100, transform: (v) => percent.transform(v * 100) });
2493
- const isColorString = (type, testProp) => (v) => {
2494
- return Boolean(isString(v) && singleColorRegex.test(v) && v.startsWith(type) || testProp && Object.prototype.hasOwnProperty.call(v, testProp));
2495
- };
2496
- const splitColor = (aName, bName, cName) => (v) => {
2497
- if (!isString(v))
2498
- return v;
2499
- const [a, b, c, alpha2] = v.match(floatRegex);
2500
- return {
2501
- [aName]: parseFloat(a),
2502
- [bName]: parseFloat(b),
2503
- [cName]: parseFloat(c),
2504
- alpha: alpha2 !== void 0 ? parseFloat(alpha2) : 1
2360
+ const mixArray = (from, to) => {
2361
+ const output = [...from];
2362
+ const numValues = output.length;
2363
+ const blendValue = from.map((fromThis, i) => getMixer(fromThis, to[i]));
2364
+ return (v) => {
2365
+ for (let i = 0; i < numValues; i++) {
2366
+ output[i] = blendValue[i](v);
2367
+ }
2368
+ return output;
2505
2369
  };
2506
2370
  };
2507
- const hsla = {
2508
- test: isColorString("hsl", "hue"),
2509
- parse: splitColor("hue", "saturation", "lightness"),
2510
- transform: ({ hue, saturation, lightness, alpha: alpha$1 = 1 }) => {
2511
- return "hsla(" + Math.round(hue) + ", " + percent.transform(sanitize(saturation)) + ", " + percent.transform(sanitize(lightness)) + ", " + sanitize(alpha.transform(alpha$1)) + ")";
2371
+ const mixObject = (origin, target) => {
2372
+ const output = Object.assign(Object.assign({}, origin), target);
2373
+ const blendValue = {};
2374
+ for (const key in output) {
2375
+ if (origin[key] !== void 0 && target[key] !== void 0) {
2376
+ blendValue[key] = getMixer(origin[key], target[key]);
2377
+ }
2512
2378
  }
2379
+ return (v) => {
2380
+ for (const key in blendValue) {
2381
+ output[key] = blendValue[key](v);
2382
+ }
2383
+ return output;
2384
+ };
2513
2385
  };
2514
- const clampRgbUnit = clamp(0, 255);
2515
- const rgbUnit = Object.assign(Object.assign({}, number), { transform: (v) => Math.round(clampRgbUnit(v)) });
2516
- const rgba = {
2517
- test: isColorString("rgb", "red"),
2518
- parse: splitColor("red", "green", "blue"),
2519
- transform: ({ red, green, blue, alpha: alpha$1 = 1 }) => "rgba(" + rgbUnit.transform(red) + ", " + rgbUnit.transform(green) + ", " + rgbUnit.transform(blue) + ", " + sanitize(alpha.transform(alpha$1)) + ")"
2520
- };
2521
- function parseHex(v) {
2522
- let r = "";
2523
- let g = "";
2524
- let b = "";
2525
- let a = "";
2526
- if (v.length > 5) {
2527
- r = v.substr(1, 2);
2528
- g = v.substr(3, 2);
2529
- b = v.substr(5, 2);
2530
- a = v.substr(7, 2);
2531
- } else {
2532
- r = v.substr(1, 1);
2533
- g = v.substr(2, 1);
2534
- b = v.substr(3, 1);
2535
- a = v.substr(4, 1);
2536
- r += r;
2537
- g += g;
2538
- b += b;
2539
- a += a;
2386
+ function analyse(value) {
2387
+ const parsed = complex.parse(value);
2388
+ const numValues = parsed.length;
2389
+ let numNumbers = 0;
2390
+ let numRGB = 0;
2391
+ let numHSL = 0;
2392
+ for (let i = 0; i < numValues; i++) {
2393
+ if (numNumbers || typeof parsed[i] === "number") {
2394
+ numNumbers++;
2395
+ } else {
2396
+ if (parsed[i].hue !== void 0) {
2397
+ numHSL++;
2398
+ } else {
2399
+ numRGB++;
2400
+ }
2401
+ }
2540
2402
  }
2541
- return {
2542
- red: parseInt(r, 16),
2543
- green: parseInt(g, 16),
2544
- blue: parseInt(b, 16),
2545
- alpha: a ? parseInt(a, 16) / 255 : 1
2546
- };
2403
+ return { parsed, numNumbers, numRGB, numHSL };
2547
2404
  }
2548
- const hex = {
2549
- test: isColorString("#"),
2550
- parse: parseHex,
2551
- transform: rgba.transform
2405
+ const mixComplex = (origin, target) => {
2406
+ const template = complex.createTransformer(target);
2407
+ const originStats = analyse(origin);
2408
+ const targetStats = analyse(target);
2409
+ const canInterpolate = originStats.numHSL === targetStats.numHSL && originStats.numRGB === targetStats.numRGB && originStats.numNumbers >= targetStats.numNumbers;
2410
+ if (canInterpolate) {
2411
+ return pipe(mixArray(originStats.parsed, targetStats.parsed), template);
2412
+ } else {
2413
+ return (p2) => `${p2 > 0 ? target : origin}`;
2414
+ }
2552
2415
  };
2553
- const color = {
2554
- test: (v) => rgba.test(v) || hex.test(v) || hsla.test(v),
2555
- parse: (v) => {
2556
- if (rgba.test(v)) {
2557
- return rgba.parse(v);
2558
- } else if (hsla.test(v)) {
2559
- return hsla.parse(v);
2416
+ const mixNumber = (from, to) => (p2) => mix(from, to, p2);
2417
+ function detectMixerFactory(v) {
2418
+ if (typeof v === "number") {
2419
+ return mixNumber;
2420
+ } else if (typeof v === "string") {
2421
+ if (color.test(v)) {
2422
+ return mixColor;
2560
2423
  } else {
2561
- return hex.parse(v);
2424
+ return mixComplex;
2562
2425
  }
2563
- },
2564
- transform: (v) => {
2565
- return isString(v) ? v : v.hasOwnProperty("red") ? rgba.transform(v) : hsla.transform(v);
2426
+ } else if (Array.isArray(v)) {
2427
+ return mixArray;
2428
+ } else if (typeof v === "object") {
2429
+ return mixObject;
2430
+ }
2431
+ }
2432
+ function createMixers(output, ease, customMixer) {
2433
+ const mixers = [];
2434
+ const mixerFactory = customMixer || detectMixerFactory(output[0]);
2435
+ const numMixers = output.length - 1;
2436
+ for (let i = 0; i < numMixers; i++) {
2437
+ let mixer = mixerFactory(output[i], output[i + 1]);
2438
+ if (ease) {
2439
+ const easingFunction = Array.isArray(ease) ? ease[i] : ease;
2440
+ mixer = pipe(easingFunction, mixer);
2441
+ }
2442
+ mixers.push(mixer);
2443
+ }
2444
+ return mixers;
2445
+ }
2446
+ function fastInterpolate([from, to], [mixer]) {
2447
+ return (v) => mixer(progress(from, to, v));
2448
+ }
2449
+ function slowInterpolate(input, mixers) {
2450
+ const inputLength = input.length;
2451
+ const lastInputIndex = inputLength - 1;
2452
+ return (v) => {
2453
+ let mixerIndex = 0;
2454
+ let foundMixerIndex = false;
2455
+ if (v <= input[0]) {
2456
+ foundMixerIndex = true;
2457
+ } else if (v >= input[lastInputIndex]) {
2458
+ mixerIndex = lastInputIndex - 1;
2459
+ foundMixerIndex = true;
2460
+ }
2461
+ if (!foundMixerIndex) {
2462
+ let i = 1;
2463
+ for (; i < inputLength; i++) {
2464
+ if (input[i] > v || i === lastInputIndex) {
2465
+ break;
2466
+ }
2467
+ }
2468
+ mixerIndex = i - 1;
2469
+ }
2470
+ const progressInRange = progress(input[mixerIndex], input[mixerIndex + 1], v);
2471
+ return mixers[mixerIndex](progressInRange);
2472
+ };
2473
+ }
2474
+ function interpolate(input, output, { clamp: isClamp = true, ease, mixer } = {}) {
2475
+ const inputLength = input.length;
2476
+ invariant(inputLength === output.length);
2477
+ invariant(!ease || !Array.isArray(ease) || ease.length === inputLength - 1);
2478
+ if (input[0] > input[inputLength - 1]) {
2479
+ input = [].concat(input);
2480
+ output = [].concat(output);
2481
+ input.reverse();
2482
+ output.reverse();
2483
+ }
2484
+ const mixers = createMixers(output, ease, mixer);
2485
+ const interpolator = inputLength === 2 ? fastInterpolate(input, mixers) : slowInterpolate(input, mixers);
2486
+ return isClamp ? (v) => interpolator(clamp$1(input[0], input[inputLength - 1], v)) : interpolator;
2487
+ }
2488
+ class InterpolateColor {
2489
+ constructor({ valueRange, colorRange }) {
2490
+ __publicField(this, "mapper");
2491
+ this.mapper = interpolate(valueRange, colorRange);
2492
+ }
2493
+ getColor(value) {
2494
+ return this.mapper(value);
2495
+ }
2496
+ }
2497
+ function getColorByColorConf(conf, value) {
2498
+ if (conf.type === "static") {
2499
+ return conf.staticColor;
2566
2500
  }
2567
- };
2568
- const colorToken = "${c}";
2569
- const numberToken = "${n}";
2570
- function test(v) {
2571
- var _a, _b, _c, _d;
2572
- return isNaN(v) && isString(v) && ((_b = (_a = v.match(floatRegex)) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) + ((_d = (_c = v.match(colorRegex)) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : 0) > 0;
2501
+ if (conf.type === "continuous") {
2502
+ return new InterpolateColor(conf).getColor(value);
2503
+ }
2504
+ return "black";
2573
2505
  }
2574
- function analyse$1(v) {
2575
- if (typeof v === "number")
2576
- v = `${v}`;
2577
- const values = [];
2578
- let numColors = 0;
2579
- const colors = v.match(colorRegex);
2580
- if (colors) {
2581
- numColors = colors.length;
2582
- v = v.replace(colorRegex, colorToken);
2583
- values.push(...colors.map(color.parse));
2506
+ function getNonStatsDataText(data) {
2507
+ if (data === null) {
2508
+ return "null";
2584
2509
  }
2585
- const numbers = v.match(floatRegex);
2586
- if (numbers) {
2587
- v = v.replace(floatRegex, numberToken);
2588
- values.push(...numbers.map(number.parse));
2510
+ if (data === void 0) {
2511
+ return "undefined";
2589
2512
  }
2590
- return { values, numColors, tokenised: v };
2513
+ if (Array.isArray(data)) {
2514
+ return `Array(${data.length})`;
2515
+ }
2516
+ return data.toString();
2591
2517
  }
2592
- function parse(v) {
2593
- return analyse$1(v).values;
2518
+ function variablesToElements(variables, data) {
2519
+ const ret = {};
2520
+ variables.forEach(({
2521
+ name,
2522
+ color: color2,
2523
+ data_field,
2524
+ aggregation,
2525
+ size,
2526
+ weight,
2527
+ formatter
2528
+ }) => {
2529
+ const value = aggregateValue(data, data_field, aggregation);
2530
+ let valueContent = "";
2531
+ if (!["string", "number"].includes(typeof value)) {
2532
+ valueContent = getNonStatsDataText(value);
2533
+ } else {
2534
+ valueContent = numbro(value).format(formatter);
2535
+ }
2536
+ ret[name] = /* @__PURE__ */ jsx(Text, {
2537
+ sx: {
2538
+ fontSize: size,
2539
+ display: "inline"
2540
+ },
2541
+ color: getColorByColorConf(color2, value),
2542
+ weight,
2543
+ children: valueContent
2544
+ });
2545
+ });
2546
+ return ret;
2594
2547
  }
2595
- function createTransformer(v) {
2596
- const { values, numColors, tokenised } = analyse$1(v);
2597
- const numValues = values.length;
2598
- return (v2) => {
2599
- let output = tokenised;
2600
- for (let i = 0; i < numValues; i++) {
2601
- output = output.replace(i < numColors ? colorToken : numberToken, i < numColors ? color.transform(v2[i]) : sanitize(v2[i]));
2548
+ function preserveWhiteSpaces(text) {
2549
+ return text.split(" ").map((s) => /* @__PURE__ */ jsxs(Fragment, {
2550
+ children: [s, "\xA0"]
2551
+ }));
2552
+ }
2553
+ function withLineBreaks(text) {
2554
+ const normalized = text.replaceAll("<br />", "<br/>").replaceAll("\n", "<br/>");
2555
+ const splitted = normalized.split("<br/>");
2556
+ const ret = splitted.map((t, i) => {
2557
+ const arr = [preserveWhiteSpaces(t)];
2558
+ if (i !== splitted.length - 1) {
2559
+ arr.push(/* @__PURE__ */ jsx("br", {}));
2602
2560
  }
2603
- return output;
2604
- };
2561
+ return arr;
2562
+ }).flat().filter((t) => t !== void 0);
2563
+ return ret;
2605
2564
  }
2606
- const convertNumbersToZero = (v) => typeof v === "number" ? 0 : v;
2607
- function getAnimatableNone(v) {
2608
- const parsed = parse(v);
2609
- const transformer = createTransformer(v);
2610
- return transformer(parsed.map(convertNumbersToZero));
2565
+ function textToJSX(text) {
2566
+ return withLineBreaks(text);
2611
2567
  }
2612
- const complex = { test, parse, createTransformer, getAnimatableNone };
2613
- function hueToRgb(p2, q2, t) {
2614
- if (t < 0)
2615
- t += 1;
2616
- if (t > 1)
2617
- t -= 1;
2618
- if (t < 1 / 6)
2619
- return p2 + (q2 - p2) * 6 * t;
2620
- if (t < 1 / 2)
2621
- return q2;
2622
- if (t < 2 / 3)
2623
- return p2 + (q2 - p2) * (2 / 3 - t) * 6;
2624
- return p2;
2568
+ function templateToJSX(template, variables, data) {
2569
+ const variableElements = variablesToElements(variables, data);
2570
+ const regx = /^\{(.+)\}(.*)$/;
2571
+ return template.split("$").map((text) => {
2572
+ var _a;
2573
+ const match = regx.exec(text);
2574
+ if (!match) {
2575
+ return textToJSX(text);
2576
+ }
2577
+ const element = variableElements[match[1]];
2578
+ if (!element) {
2579
+ return textToJSX(text);
2580
+ }
2581
+ const rest = (_a = match[2]) != null ? _a : "";
2582
+ return /* @__PURE__ */ jsxs(Fragment, {
2583
+ children: [element, textToJSX(rest)]
2584
+ });
2585
+ });
2625
2586
  }
2626
- function hslaToRgba({ hue, saturation, lightness, alpha: alpha2 }) {
2627
- hue /= 360;
2628
- saturation /= 100;
2629
- lightness /= 100;
2630
- let red = 0;
2631
- let green = 0;
2632
- let blue = 0;
2633
- if (!saturation) {
2634
- red = green = blue = lightness;
2635
- } else {
2636
- const q2 = lightness < 0.5 ? lightness * (1 + saturation) : lightness + saturation - lightness * saturation;
2637
- const p2 = 2 * lightness - q2;
2638
- red = hueToRgb(p2, q2, hue + 1 / 3);
2639
- green = hueToRgb(p2, q2, hue);
2640
- blue = hueToRgb(p2, q2, hue - 1 / 3);
2641
- }
2642
- return {
2643
- red: Math.round(red * 255),
2644
- green: Math.round(green * 255),
2645
- blue: Math.round(blue * 255),
2646
- alpha: alpha2
2647
- };
2587
+ echarts.use([BarChart, LineChart, ScatterChart, GridComponent, LegendComponent, TooltipComponent, CanvasRenderer]);
2588
+ echarts.registerTransform(echartsStat.transform.regression);
2589
+ function templateNotEmpty(str) {
2590
+ return str.trim().length > 0;
2648
2591
  }
2649
- const mixLinearColor = (from, to, v) => {
2650
- const fromExpo = from * from;
2651
- const toExpo = to * to;
2652
- return Math.sqrt(Math.max(0, v * (toExpo - fromExpo) + fromExpo));
2653
- };
2654
- const colorTypes = [hex, rgba, hsla];
2655
- const getColorType = (v) => colorTypes.find((type) => type.test(v));
2656
- const mixColor = (from, to) => {
2657
- let fromColorType = getColorType(from);
2658
- let toColorType = getColorType(to);
2659
- let fromColor = fromColorType.parse(from);
2660
- let toColor = toColorType.parse(to);
2661
- if (fromColorType === hsla) {
2662
- fromColor = hslaToRgba(fromColor);
2663
- fromColorType = rgba;
2664
- }
2665
- if (toColorType === hsla) {
2666
- toColor = hslaToRgba(toColor);
2667
- toColorType = rgba;
2592
+ function Chart({
2593
+ conf,
2594
+ data,
2595
+ width,
2596
+ height
2597
+ }) {
2598
+ const option = React.useMemo(() => {
2599
+ return getOption(conf, data);
2600
+ }, [conf, data]);
2601
+ if (!width || !height) {
2602
+ return null;
2668
2603
  }
2669
- const blended = Object.assign({}, fromColor);
2670
- return (v) => {
2671
- for (const key in blended) {
2672
- if (key !== "alpha") {
2673
- blended[key] = mixLinearColor(fromColor[key], toColor[key], v);
2674
- }
2604
+ return /* @__PURE__ */ jsx(ReactEChartsCore, {
2605
+ echarts,
2606
+ option,
2607
+ style: {
2608
+ width,
2609
+ height
2675
2610
  }
2676
- blended.alpha = mix(fromColor.alpha, toColor.alpha, v);
2677
- return fromColorType.transform(blended);
2678
- };
2679
- };
2680
- const isNum = (v) => typeof v === "number";
2681
- const combineFunctions = (a, b) => (v) => b(a(v));
2682
- const pipe = (...transformers) => transformers.reduce(combineFunctions);
2683
- function getMixer(origin, target) {
2684
- if (isNum(origin)) {
2685
- return (v) => mix(origin, target, v);
2686
- } else if (color.test(origin)) {
2687
- return mixColor(origin, target);
2688
- } else {
2689
- return mixComplex(origin, target);
2690
- }
2611
+ });
2691
2612
  }
2692
- const mixArray = (from, to) => {
2693
- const output = [...from];
2694
- const numValues = output.length;
2695
- const blendValue = from.map((fromThis, i) => getMixer(fromThis, to[i]));
2696
- return (v) => {
2697
- for (let i = 0; i < numValues; i++) {
2698
- output[i] = blendValue[i](v);
2699
- }
2700
- return output;
2701
- };
2702
- };
2703
- const mixObject = (origin, target) => {
2704
- const output = Object.assign(Object.assign({}, origin), target);
2705
- const blendValue = {};
2706
- for (const key in output) {
2707
- if (origin[key] !== void 0 && target[key] !== void 0) {
2708
- blendValue[key] = getMixer(origin[key], target[key]);
2709
- }
2613
+ function VizCartesianChart({
2614
+ conf,
2615
+ data,
2616
+ width,
2617
+ height
2618
+ }) {
2619
+ const {
2620
+ ref: topStatsRef,
2621
+ height: topStatsHeight
2622
+ } = useElementSize();
2623
+ const {
2624
+ ref: bottomStatsRef,
2625
+ height: bottomStatsHeight
2626
+ } = useElementSize();
2627
+ const templates = React.useMemo(() => {
2628
+ const {
2629
+ stats: {
2630
+ templates: templates2,
2631
+ variables
2632
+ }
2633
+ } = conf;
2634
+ return {
2635
+ top: templateToJSX(templates2.top, variables, data),
2636
+ bottom: templateToJSX(templates2.bottom, variables, data)
2637
+ };
2638
+ }, [conf, data]);
2639
+ const finalHeight = Math.max(0, height - topStatsHeight - bottomStatsHeight);
2640
+ return /* @__PURE__ */ jsxs(Box, {
2641
+ children: [templateNotEmpty(conf.stats.templates.top) && /* @__PURE__ */ jsx(Text, {
2642
+ ref: topStatsRef,
2643
+ align: "left",
2644
+ size: "xs",
2645
+ pl: "sm",
2646
+ children: Object.values(templates.top).map((c) => c)
2647
+ }), /* @__PURE__ */ jsx(Chart, {
2648
+ width,
2649
+ height: finalHeight,
2650
+ data,
2651
+ conf
2652
+ }), templateNotEmpty(conf.stats.templates.bottom) && /* @__PURE__ */ jsx(Text, {
2653
+ ref: bottomStatsRef,
2654
+ align: "left",
2655
+ size: "xs",
2656
+ pl: "sm",
2657
+ children: Object.values(templates.bottom).map((c) => c)
2658
+ })]
2659
+ });
2660
+ }
2661
+ var ValueType = /* @__PURE__ */ ((ValueType2) => {
2662
+ ValueType2["string"] = "string";
2663
+ ValueType2["number"] = "number";
2664
+ ValueType2["eloc"] = "eloc";
2665
+ ValueType2["percentage"] = "percentage";
2666
+ return ValueType2;
2667
+ })(ValueType || {});
2668
+ function StringCell({
2669
+ value
2670
+ }) {
2671
+ return /* @__PURE__ */ jsx(Text, {
2672
+ component: "span",
2673
+ children: value
2674
+ });
2675
+ }
2676
+ function ELOCCell({
2677
+ value
2678
+ }) {
2679
+ return /* @__PURE__ */ jsx(Text, {
2680
+ component: "span",
2681
+ children: value
2682
+ });
2683
+ }
2684
+ function NumberCell({
2685
+ value
2686
+ }) {
2687
+ const num = numbro(value).format({
2688
+ thousandSeparated: true
2689
+ });
2690
+ return /* @__PURE__ */ jsx(Text, {
2691
+ component: "span",
2692
+ children: num
2693
+ });
2694
+ }
2695
+ function PercentageCell({
2696
+ value
2697
+ }) {
2698
+ const num = numbro(value).format({
2699
+ output: "percent",
2700
+ mantissa: 3
2701
+ });
2702
+ return /* @__PURE__ */ jsx(Text, {
2703
+ component: "span",
2704
+ children: num
2705
+ });
2706
+ }
2707
+ function CellValue({
2708
+ value,
2709
+ type
2710
+ }) {
2711
+ switch (type) {
2712
+ case ValueType.string:
2713
+ return /* @__PURE__ */ jsx(StringCell, {
2714
+ value
2715
+ });
2716
+ case ValueType.eloc:
2717
+ return /* @__PURE__ */ jsx(ELOCCell, {
2718
+ value
2719
+ });
2720
+ case ValueType.number:
2721
+ return /* @__PURE__ */ jsx(NumberCell, {
2722
+ value
2723
+ });
2724
+ case ValueType.percentage:
2725
+ return /* @__PURE__ */ jsx(PercentageCell, {
2726
+ value
2727
+ });
2710
2728
  }
2711
- return (v) => {
2712
- for (const key in blendValue) {
2713
- output[key] = blendValue[key](v);
2729
+ }
2730
+ function VizTable({
2731
+ conf,
2732
+ data = [],
2733
+ width,
2734
+ height
2735
+ }) {
2736
+ const _a = conf, {
2737
+ id_field,
2738
+ use_raw_columns,
2739
+ columns
2740
+ } = _a, rest = __objRest(_a, [
2741
+ "id_field",
2742
+ "use_raw_columns",
2743
+ "columns"
2744
+ ]);
2745
+ const labels = React.useMemo(() => {
2746
+ if (use_raw_columns) {
2747
+ return Object.keys(data == null ? void 0 : data[0]);
2714
2748
  }
2715
- return output;
2716
- };
2717
- };
2718
- function analyse(value) {
2719
- const parsed = complex.parse(value);
2720
- const numValues = parsed.length;
2721
- let numNumbers = 0;
2722
- let numRGB = 0;
2723
- let numHSL = 0;
2724
- for (let i = 0; i < numValues; i++) {
2725
- if (numNumbers || typeof parsed[i] === "number") {
2726
- numNumbers++;
2727
- } else {
2728
- if (parsed[i].hue !== void 0) {
2729
- numHSL++;
2730
- } else {
2731
- numRGB++;
2732
- }
2749
+ return columns.map((c) => c.label);
2750
+ }, [use_raw_columns, columns, data]);
2751
+ const finalColumns = React.useMemo(() => {
2752
+ if (use_raw_columns) {
2753
+ return Object.keys(data == null ? void 0 : data[0]).map((k2) => ({
2754
+ label: k2,
2755
+ value_field: k2,
2756
+ value_type: ValueType.string
2757
+ }));
2733
2758
  }
2734
- }
2735
- return { parsed, numNumbers, numRGB, numHSL };
2736
- }
2737
- const mixComplex = (origin, target) => {
2738
- const template = complex.createTransformer(target);
2739
- const originStats = analyse(origin);
2740
- const targetStats = analyse(target);
2741
- const canInterpolate = originStats.numHSL === targetStats.numHSL && originStats.numRGB === targetStats.numRGB && originStats.numNumbers >= targetStats.numNumbers;
2742
- if (canInterpolate) {
2743
- return pipe(mixArray(originStats.parsed, targetStats.parsed), template);
2744
- } else {
2745
- return (p2) => `${p2 > 0 ? target : origin}`;
2746
- }
2747
- };
2748
- const mixNumber = (from, to) => (p2) => mix(from, to, p2);
2749
- function detectMixerFactory(v) {
2750
- if (typeof v === "number") {
2751
- return mixNumber;
2752
- } else if (typeof v === "string") {
2753
- if (color.test(v)) {
2754
- return mixColor;
2755
- } else {
2756
- return mixComplex;
2759
+ return columns;
2760
+ }, [use_raw_columns, columns, data]);
2761
+ return /* @__PURE__ */ jsxs(Table, __spreadProps(__spreadValues({
2762
+ sx: {
2763
+ maxHeight: height
2757
2764
  }
2758
- } else if (Array.isArray(v)) {
2759
- return mixArray;
2760
- } else if (typeof v === "object") {
2761
- return mixObject;
2762
- }
2765
+ }, rest), {
2766
+ children: [/* @__PURE__ */ jsx("thead", {
2767
+ children: /* @__PURE__ */ jsx("tr", {
2768
+ children: labels.map((label) => /* @__PURE__ */ jsx("th", {
2769
+ children: label
2770
+ }, label))
2771
+ })
2772
+ }), /* @__PURE__ */ jsx("tbody", {
2773
+ children: data.slice(0, 30).map((row, index2) => /* @__PURE__ */ jsx("tr", {
2774
+ children: finalColumns.map(({
2775
+ value_field,
2776
+ value_type
2777
+ }) => /* @__PURE__ */ jsx("td", {
2778
+ children: /* @__PURE__ */ jsx(Group, {
2779
+ sx: {
2780
+ "&, .mantine-Text-root": {
2781
+ fontFamily: "monospace",
2782
+ fontSize: rest.fontSize
2783
+ }
2784
+ },
2785
+ children: /* @__PURE__ */ jsx(CellValue, {
2786
+ value: row[value_field],
2787
+ type: value_type
2788
+ })
2789
+ })
2790
+ }, `${value_field}--${row[value_field]}`))
2791
+ }, id_field ? row[id_field] : `row-${index2}`))
2792
+ }), data.length > 100 && /* @__PURE__ */ jsx("tfoot", {
2793
+ children: /* @__PURE__ */ jsx("tr", {
2794
+ children: /* @__PURE__ */ jsx("td", {
2795
+ colSpan: labels.length,
2796
+ children: /* @__PURE__ */ jsx(Text, {
2797
+ color: "red",
2798
+ size: "sm",
2799
+ children: "Showing only the first 30 rows to avoid causing slow performance"
2800
+ })
2801
+ })
2802
+ })
2803
+ })]
2804
+ }));
2763
2805
  }
2764
- function createMixers(output, ease, customMixer) {
2765
- const mixers = [];
2766
- const mixerFactory = customMixer || detectMixerFactory(output[0]);
2767
- const numMixers = output.length - 1;
2768
- for (let i = 0; i < numMixers; i++) {
2769
- let mixer = mixerFactory(output[i], output[i + 1]);
2770
- if (ease) {
2771
- const easingFunction = Array.isArray(ease) ? ease[i] : ease;
2772
- mixer = pipe(easingFunction, mixer);
2773
- }
2774
- mixers.push(mixer);
2806
+ function interpolateString(template, params = {}) {
2807
+ const extendedParams = __spreadProps(__spreadValues({}, params), {
2808
+ numbro
2809
+ });
2810
+ const names = Object.keys(extendedParams);
2811
+ const vals = Object.values(extendedParams);
2812
+ try {
2813
+ return new Function(...names, `return \`${template}\`;`)(...vals);
2814
+ } catch (error) {
2815
+ return error.message;
2775
2816
  }
2776
- return mixers;
2777
2817
  }
2778
- function fastInterpolate([from, to], [mixer]) {
2779
- return (v) => mixer(progress(from, to, v));
2818
+ function VizText({
2819
+ conf: {
2820
+ paragraphs
2821
+ },
2822
+ data
2823
+ }) {
2824
+ return /* @__PURE__ */ jsx(Fragment, {
2825
+ children: paragraphs.map((_a, index2) => {
2826
+ var _b = _a, {
2827
+ template,
2828
+ size
2829
+ } = _b, rest = __objRest(_b, [
2830
+ "template",
2831
+ "size"
2832
+ ]);
2833
+ return /* @__PURE__ */ jsx(Text, __spreadProps(__spreadValues({}, rest), {
2834
+ sx: {
2835
+ fontSize: size
2836
+ },
2837
+ children: interpolateString(template, data[0])
2838
+ }), `${template}---${index2}`);
2839
+ })
2840
+ });
2780
2841
  }
2781
- function slowInterpolate(input, mixers) {
2782
- const inputLength = input.length;
2783
- const lastInputIndex = inputLength - 1;
2784
- return (v) => {
2785
- let mixerIndex = 0;
2786
- let foundMixerIndex = false;
2787
- if (v <= input[0]) {
2788
- foundMixerIndex = true;
2789
- } else if (v >= input[lastInputIndex]) {
2790
- mixerIndex = lastInputIndex - 1;
2791
- foundMixerIndex = true;
2842
+ echarts.use([GridComponent, VisualMapComponent, LegendComponent, TooltipComponent, CanvasRenderer]);
2843
+ function VizBar3D({
2844
+ conf,
2845
+ data,
2846
+ width,
2847
+ height
2848
+ }) {
2849
+ const _a = conf, {
2850
+ x_axis_data_key,
2851
+ y_axis_data_key,
2852
+ z_axis_data_key
2853
+ } = _a, restConf = __objRest(_a, [
2854
+ "x_axis_data_key",
2855
+ "y_axis_data_key",
2856
+ "z_axis_data_key"
2857
+ ]);
2858
+ const min = React.useMemo(() => {
2859
+ return _.minBy(data, (d) => d[z_axis_data_key])[z_axis_data_key];
2860
+ }, [data, z_axis_data_key]);
2861
+ const max = React.useMemo(() => {
2862
+ return _.maxBy(data, (d) => d[z_axis_data_key])[z_axis_data_key];
2863
+ }, [data, z_axis_data_key]);
2864
+ const option = __spreadProps(__spreadValues({
2865
+ tooltip: {},
2866
+ backgroundColor: "#fff",
2867
+ visualMap: {
2868
+ show: true,
2869
+ dimension: 2,
2870
+ min,
2871
+ max,
2872
+ inRange: {
2873
+ color: ["#313695", "#4575b4", "#74add1", "#abd9e9", "#e0f3f8", "#ffffbf", "#fee090", "#fdae61", "#f46d43", "#d73027", "#a50026"]
2874
+ }
2875
+ },
2876
+ xAxis3D: {
2877
+ type: "value"
2878
+ },
2879
+ yAxis3D: {
2880
+ type: "value"
2881
+ },
2882
+ zAxis3D: {
2883
+ type: "value"
2884
+ },
2885
+ grid3D: {
2886
+ viewControl: {
2887
+ projection: "orthographic",
2888
+ autoRotate: false
2889
+ },
2890
+ light: {
2891
+ main: {
2892
+ shadow: true,
2893
+ quality: "ultra",
2894
+ intensity: 1.5
2895
+ }
2896
+ }
2897
+ }
2898
+ }, restConf), {
2899
+ series: [{
2900
+ type: "bar3D",
2901
+ wireframe: {},
2902
+ data: data.map((d) => [d[x_axis_data_key], d[y_axis_data_key], d[z_axis_data_key]])
2903
+ }]
2904
+ });
2905
+ return /* @__PURE__ */ jsx(ReactEChartsCore, {
2906
+ echarts,
2907
+ option,
2908
+ style: {
2909
+ width,
2910
+ height
2792
2911
  }
2793
- if (!foundMixerIndex) {
2794
- let i = 1;
2795
- for (; i < inputLength; i++) {
2796
- if (input[i] > v || i === lastInputIndex) {
2797
- break;
2912
+ });
2913
+ }
2914
+ var index$2 = "";
2915
+ echarts.use([PieChart, CanvasRenderer]);
2916
+ const defaultOption = {
2917
+ tooltip: {
2918
+ show: true
2919
+ },
2920
+ series: {
2921
+ type: "pie",
2922
+ radius: ["50%", "80%"],
2923
+ label: {
2924
+ position: "outer",
2925
+ alignTo: "edge",
2926
+ formatter: "{name|{b}}\n{percentage|{d}%}",
2927
+ minMargin: 5,
2928
+ edgeDistance: 10,
2929
+ lineHeight: 15,
2930
+ rich: {
2931
+ percentage: {
2932
+ color: "#999"
2933
+ }
2934
+ },
2935
+ margin: 20
2936
+ },
2937
+ labelLine: {
2938
+ length: 15,
2939
+ length2: 0,
2940
+ maxSurfaceAngle: 80,
2941
+ showAbove: true
2942
+ },
2943
+ top: 10,
2944
+ bottom: 10,
2945
+ left: 10,
2946
+ right: 10
2947
+ }
2948
+ };
2949
+ function VizPie({
2950
+ conf,
2951
+ data,
2952
+ width,
2953
+ height
2954
+ }) {
2955
+ const _a = conf, {
2956
+ label_field = "name",
2957
+ value_field = "value"
2958
+ } = _a, restConf = __objRest(_a, [
2959
+ "label_field",
2960
+ "value_field"
2961
+ ]);
2962
+ const chartData = React.useMemo(() => {
2963
+ return data.map((d) => ({
2964
+ name: d[label_field],
2965
+ value: Number(d[value_field])
2966
+ }));
2967
+ }, [data, label_field, value_field]);
2968
+ const labelOptions = React.useMemo(() => {
2969
+ return {
2970
+ series: {
2971
+ labelLayout: function(params) {
2972
+ const isLeft = params.labelRect.x < width / 2;
2973
+ const points = params.labelLinePoints;
2974
+ points[2][0] = isLeft ? params.labelRect.x : params.labelRect.x + params.labelRect.width;
2975
+ return {
2976
+ labelLinePoints: points
2977
+ };
2798
2978
  }
2799
2979
  }
2800
- mixerIndex = i - 1;
2980
+ };
2981
+ }, [width]);
2982
+ const option = _.merge({}, defaultOption, labelOptions, restConf, {
2983
+ series: {
2984
+ data: chartData
2801
2985
  }
2802
- const progressInRange = progress(input[mixerIndex], input[mixerIndex + 1], v);
2803
- return mixers[mixerIndex](progressInRange);
2804
- };
2805
- }
2806
- function interpolate(input, output, { clamp: isClamp = true, ease, mixer } = {}) {
2807
- const inputLength = input.length;
2808
- invariant(inputLength === output.length);
2809
- invariant(!ease || !Array.isArray(ease) || ease.length === inputLength - 1);
2810
- if (input[0] > input[inputLength - 1]) {
2811
- input = [].concat(input);
2812
- output = [].concat(output);
2813
- input.reverse();
2814
- output.reverse();
2815
- }
2816
- const mixers = createMixers(output, ease, mixer);
2817
- const interpolator = inputLength === 2 ? fastInterpolate(input, mixers) : slowInterpolate(input, mixers);
2818
- return isClamp ? (v) => interpolator(clamp$1(input[0], input[inputLength - 1], v)) : interpolator;
2819
- }
2820
- class InterpolateColor {
2821
- constructor({ valueRange, colorRange }) {
2822
- __publicField(this, "mapper");
2823
- this.mapper = interpolate(valueRange, colorRange);
2824
- }
2825
- getColor(value) {
2826
- return this.mapper(value);
2827
- }
2986
+ });
2987
+ return /* @__PURE__ */ jsx(ReactEChartsCore, {
2988
+ echarts,
2989
+ option,
2990
+ style: {
2991
+ width,
2992
+ height
2993
+ }
2994
+ });
2828
2995
  }
2829
- function getColorByColorConf(conf, dataRow) {
2830
- if (conf.type === "static") {
2831
- return conf.staticColor;
2832
- }
2833
- if (conf.type === "continuous") {
2834
- const mapper = new InterpolateColor(conf);
2835
- const value = dataRow[conf.valueField];
2836
- return mapper.getColor(value);
2837
- }
2838
- return "black";
2996
+ function VizStats({
2997
+ conf: {
2998
+ template,
2999
+ variables,
3000
+ align
3001
+ },
3002
+ data
3003
+ }) {
3004
+ const contents = React.useMemo(() => {
3005
+ return templateToJSX(template, variables, data);
3006
+ }, [template, variables, data]);
3007
+ return /* @__PURE__ */ jsx(Text, {
3008
+ align,
3009
+ children: Object.values(contents).map((c) => c)
3010
+ });
2839
3011
  }
2840
- function getNonStatsDataText(data) {
2841
- if (data === null) {
2842
- return "null";
2843
- }
2844
- if (data === void 0) {
2845
- return "undefined";
2846
- }
2847
- if (Array.isArray(data)) {
2848
- return `Array(${data.length})`;
3012
+ function VizRichText({
3013
+ conf,
3014
+ width,
3015
+ height
3016
+ }) {
3017
+ if (!width || !height) {
3018
+ return null;
2849
3019
  }
2850
- return data.toString();
2851
- }
2852
- function VizStats(_a) {
2853
- var _b = _a, {
2854
- conf: _c
2855
- } = _b, _d = _c, {
2856
- content,
2857
- size,
2858
- color: color2
2859
- } = _d, rest = __objRest(_d, [
2860
- "content",
2861
- "size",
2862
- "color"
2863
- ]), {
2864
- data
2865
- } = _b;
2866
- const finalColor = React.useMemo(() => {
2867
- return getColorByColorConf(color2, data[0]);
2868
- }, [color2, data]);
2869
- const finalContent = React.useMemo(() => {
2870
- var _a2;
2871
- const {
2872
- prefix,
2873
- postfix,
2874
- data_field,
2875
- formatter
2876
- } = content;
2877
- const contentData = (_a2 = data == null ? void 0 : data[0]) == null ? void 0 : _a2[data_field];
2878
- if (!["string", "number"].includes(typeof contentData)) {
2879
- return getNonStatsDataText(contentData);
2880
- }
2881
- const contents = [prefix, numbro(contentData).format(formatter), postfix];
2882
- return contents.join(" ");
2883
- }, [content, data]);
2884
- return /* @__PURE__ */ jsx(Text, __spreadProps(__spreadValues({}, rest), {
2885
- color: finalColor,
3020
+ return /* @__PURE__ */ jsx(RichTextEditor, {
3021
+ readOnly: true,
3022
+ value: conf.content,
3023
+ onChange: _.noop,
2886
3024
  sx: {
2887
- fontSize: size
2888
- },
2889
- children: finalContent
2890
- }));
3025
+ border: "none"
3026
+ }
3027
+ });
2891
3028
  }
2892
3029
  function renderViz(width, height, data, viz) {
2893
3030
  const props = {
@@ -2907,6 +3044,8 @@ function renderViz(width, height, data, viz) {
2907
3044
  return /* @__PURE__ */ jsx(VizText, __spreadValues({}, props));
2908
3045
  case "stats":
2909
3046
  return /* @__PURE__ */ jsx(VizStats, __spreadValues({}, props));
3047
+ case "rich-text":
3048
+ return /* @__PURE__ */ jsx(VizRichText, __spreadValues({}, props));
2910
3049
  case "bar-3d":
2911
3050
  return /* @__PURE__ */ jsx(VizBar3D, __spreadValues({}, props));
2912
3051
  case "pie":
@@ -2915,6 +3054,7 @@ function renderViz(width, height, data, viz) {
2915
3054
  return null;
2916
3055
  }
2917
3056
  }
3057
+ const typesDontNeedData = ["rich-text"];
2918
3058
  function Viz({
2919
3059
  viz,
2920
3060
  data,
@@ -2925,6 +3065,16 @@ function Viz({
2925
3065
  width,
2926
3066
  height
2927
3067
  } = useElementSize();
3068
+ const needData = !typesDontNeedData.includes(viz.type);
3069
+ if (!needData) {
3070
+ return /* @__PURE__ */ jsx("div", {
3071
+ className: "viz-root",
3072
+ ref,
3073
+ children: /* @__PURE__ */ jsx(ErrorBoundary, {
3074
+ children: renderViz(width, height, data, viz)
3075
+ })
3076
+ });
3077
+ }
2928
3078
  const empty = React.useMemo(() => !Array.isArray(data) || data.length === 0, [data]);
2929
3079
  if (loading) {
2930
3080
  return /* @__PURE__ */ jsx("div", {
@@ -2968,7 +3118,7 @@ function _DataFieldSelector({
2968
3118
  data,
2969
3119
  sx
2970
3120
  }, ref) {
2971
- const options = React.useMemo(() => {
3121
+ const options2 = React.useMemo(() => {
2972
3122
  if (!Array.isArray(data) || data.length === 0) {
2973
3123
  return [];
2974
3124
  }
@@ -2981,7 +3131,7 @@ function _DataFieldSelector({
2981
3131
  return /* @__PURE__ */ jsx(Select, {
2982
3132
  ref,
2983
3133
  label,
2984
- data: options,
3134
+ data: options2,
2985
3135
  value,
2986
3136
  onChange,
2987
3137
  required,
@@ -3164,7 +3314,7 @@ function _MantineColorSelector({
3164
3314
  }, [value, themeColors]);
3165
3315
  return /* @__PURE__ */ jsxs(Group, {
3166
3316
  position: "apart",
3167
- spacing: "xs",
3317
+ spacing: 4,
3168
3318
  children: [/* @__PURE__ */ jsx(TextInput, {
3169
3319
  placeholder: "Set any color",
3170
3320
  value: !isThemeColor ? value : "",
@@ -3175,7 +3325,7 @@ function _MantineColorSelector({
3175
3325
  }),
3176
3326
  variant: !isThemeColor ? "default" : "filled",
3177
3327
  sx: {
3178
- maxWidth: "100%",
3328
+ maxWidth: "46%",
3179
3329
  flexGrow: 1
3180
3330
  }
3181
3331
  }), /* @__PURE__ */ jsx(Text, {
@@ -3194,7 +3344,7 @@ function _MantineColorSelector({
3194
3344
  radius: 4
3195
3345
  }),
3196
3346
  sx: {
3197
- maxWidth: "100%",
3347
+ maxWidth: "46%",
3198
3348
  flexGrow: 1
3199
3349
  }
3200
3350
  })]
@@ -3651,12 +3801,125 @@ function _NumbroFormatSelector({
3651
3801
  })
3652
3802
  });
3653
3803
  }
3654
- const NumbroFormatSelector = React.forwardRef(_NumbroFormatSelector);
3655
- function YAxisField({
3804
+ const NumbroFormatSelector = React.forwardRef(_NumbroFormatSelector);
3805
+ function YAxisField({
3806
+ control,
3807
+ index: index2,
3808
+ remove
3809
+ }) {
3810
+ return /* @__PURE__ */ jsxs(Group, {
3811
+ direction: "column",
3812
+ grow: true,
3813
+ my: 0,
3814
+ p: "md",
3815
+ pr: 40,
3816
+ sx: {
3817
+ border: "1px solid #eee",
3818
+ position: "relative"
3819
+ },
3820
+ children: [/* @__PURE__ */ jsx(Group, {
3821
+ direction: "row",
3822
+ grow: true,
3823
+ noWrap: true,
3824
+ children: /* @__PURE__ */ jsx(Controller, {
3825
+ name: `y_axes.${index2}.name`,
3826
+ control,
3827
+ render: ({
3828
+ field
3829
+ }) => /* @__PURE__ */ jsx(TextInput, __spreadValues({
3830
+ label: "Name",
3831
+ required: true,
3832
+ sx: {
3833
+ flex: 1
3834
+ }
3835
+ }, field))
3836
+ })
3837
+ }), /* @__PURE__ */ jsx(Group, {
3838
+ direction: "column",
3839
+ grow: true,
3840
+ noWrap: true,
3841
+ children: /* @__PURE__ */ jsx(Controller, {
3842
+ name: `y_axes.${index2}.label_formatter`,
3843
+ control,
3844
+ render: ({
3845
+ field
3846
+ }) => /* @__PURE__ */ jsx(NumbroFormatSelector, __spreadValues({}, field))
3847
+ })
3848
+ }), /* @__PURE__ */ jsx(ActionIcon, {
3849
+ color: "red",
3850
+ variant: "hover",
3851
+ onClick: () => remove(index2),
3852
+ sx: {
3853
+ position: "absolute",
3854
+ top: 15,
3855
+ right: 5
3856
+ },
3857
+ disabled: index2 === 0,
3858
+ children: /* @__PURE__ */ jsx(Trash, {
3859
+ size: 16
3860
+ })
3861
+ })]
3862
+ }, index2);
3863
+ }
3864
+ function YAxesField({
3865
+ control,
3866
+ watch
3867
+ }) {
3868
+ const {
3869
+ fields,
3870
+ append,
3871
+ remove
3872
+ } = useFieldArray({
3873
+ control,
3874
+ name: "y_axes"
3875
+ });
3876
+ const watchFieldArray = watch("y_axes");
3877
+ const controlledFields = fields.map((field, index2) => {
3878
+ return __spreadValues(__spreadValues({}, field), watchFieldArray[index2]);
3879
+ });
3880
+ const addYAxis = () => append({
3881
+ name: "",
3882
+ label_formatter: defaultNumbroFormat
3883
+ });
3884
+ return /* @__PURE__ */ jsxs(Group, {
3885
+ direction: "column",
3886
+ grow: true,
3887
+ children: [controlledFields.map((field, index2) => /* @__PURE__ */ jsx(YAxisField, {
3888
+ control,
3889
+ index: index2,
3890
+ remove
3891
+ })), /* @__PURE__ */ jsx(Group, {
3892
+ position: "center",
3893
+ mt: "xs",
3894
+ children: /* @__PURE__ */ jsx(Button, {
3895
+ onClick: addYAxis,
3896
+ children: "Add a Y Axis"
3897
+ })
3898
+ })]
3899
+ });
3900
+ }
3901
+ const regressionOptions = [{
3902
+ label: "Linear",
3903
+ value: "linear"
3904
+ }, {
3905
+ label: "Exponential",
3906
+ value: "exponential"
3907
+ }, {
3908
+ label: "Logarithmic",
3909
+ value: "logarithmic"
3910
+ }, {
3911
+ label: "Polynomial",
3912
+ value: "polynomial"
3913
+ }];
3914
+ function RegressionField({
3656
3915
  control,
3916
+ regressionItem,
3657
3917
  index: index2,
3658
- remove
3918
+ remove,
3919
+ yAxisOptions,
3920
+ data
3659
3921
  }) {
3922
+ const method = regressionItem.transform.config.method;
3660
3923
  return /* @__PURE__ */ jsxs(Group, {
3661
3924
  direction: "column",
3662
3925
  grow: true,
@@ -3667,34 +3930,110 @@ function YAxisField({
3667
3930
  border: "1px solid #eee",
3668
3931
  position: "relative"
3669
3932
  },
3670
- children: [/* @__PURE__ */ jsx(Group, {
3933
+ children: [/* @__PURE__ */ jsx(Controller, {
3934
+ name: `regressions.${index2}.name`,
3935
+ control,
3936
+ render: ({
3937
+ field
3938
+ }) => /* @__PURE__ */ jsx(TextInput, __spreadValues({
3939
+ label: "Name",
3940
+ required: true,
3941
+ sx: {
3942
+ flex: 1
3943
+ }
3944
+ }, field))
3945
+ }), /* @__PURE__ */ jsxs(Group, {
3671
3946
  direction: "row",
3672
3947
  grow: true,
3673
3948
  noWrap: true,
3674
- children: /* @__PURE__ */ jsx(Controller, {
3675
- name: `y_axes.${index2}.name`,
3949
+ children: [/* @__PURE__ */ jsx(Controller, {
3950
+ name: `regressions.${index2}.y_axis_data_key`,
3676
3951
  control,
3677
3952
  render: ({
3678
3953
  field
3679
- }) => /* @__PURE__ */ jsx(TextInput, __spreadValues({
3680
- label: "Name",
3954
+ }) => /* @__PURE__ */ jsx(DataFieldSelector, __spreadValues({
3955
+ label: "Value Field",
3681
3956
  required: true,
3957
+ data,
3682
3958
  sx: {
3683
3959
  flex: 1
3684
3960
  }
3685
3961
  }, field))
3686
- })
3687
- }), /* @__PURE__ */ jsx(Group, {
3688
- direction: "column",
3962
+ }), /* @__PURE__ */ jsx(Controller, {
3963
+ name: `regressions.${index2}.plot.yAxisIndex`,
3964
+ control,
3965
+ render: (_a) => {
3966
+ var {
3967
+ field: _b
3968
+ } = _a, _c = _b, {
3969
+ value,
3970
+ onChange
3971
+ } = _c, rest = __objRest(_c, [
3972
+ "value",
3973
+ "onChange"
3974
+ ]);
3975
+ var _a2;
3976
+ return /* @__PURE__ */ jsx(Select, __spreadProps(__spreadValues({
3977
+ label: "Y Axis",
3978
+ data: yAxisOptions,
3979
+ disabled: yAxisOptions.length === 0
3980
+ }, rest), {
3981
+ value: (_a2 = value == null ? void 0 : value.toString()) != null ? _a2 : "",
3982
+ onChange: (value2) => {
3983
+ if (!value2) {
3984
+ onChange(0);
3985
+ return;
3986
+ }
3987
+ onChange(Number(value2));
3988
+ },
3989
+ sx: {
3990
+ flex: 1
3991
+ }
3992
+ }));
3993
+ }
3994
+ })]
3995
+ }), /* @__PURE__ */ jsxs(Group, {
3996
+ direction: "row",
3689
3997
  grow: true,
3690
3998
  noWrap: true,
3691
- children: /* @__PURE__ */ jsx(Controller, {
3692
- name: `y_axes.${index2}.label_formatter`,
3999
+ children: [/* @__PURE__ */ jsx(Controller, {
4000
+ name: `regressions.${index2}.transform.config.method`,
3693
4001
  control,
3694
4002
  render: ({
3695
4003
  field
3696
- }) => /* @__PURE__ */ jsx(NumbroFormatSelector, __spreadValues({}, field))
3697
- })
4004
+ }) => /* @__PURE__ */ jsx(Select, __spreadValues({
4005
+ label: "Method",
4006
+ data: regressionOptions,
4007
+ sx: {
4008
+ flex: 1
4009
+ }
4010
+ }, field))
4011
+ }), method === "polynomial" && /* @__PURE__ */ jsx(Controller, {
4012
+ name: `regressions.${index2}.transform.config.order`,
4013
+ control,
4014
+ render: ({
4015
+ field
4016
+ }) => /* @__PURE__ */ jsx(NumberInput, __spreadValues({
4017
+ label: "Order",
4018
+ sx: {
4019
+ flex: 1
4020
+ }
4021
+ }, field))
4022
+ })]
4023
+ }), /* @__PURE__ */ jsxs(Group, {
4024
+ direction: "column",
4025
+ grow: true,
4026
+ spacing: 4,
4027
+ children: [/* @__PURE__ */ jsx(Text, {
4028
+ size: "sm",
4029
+ children: "Color"
4030
+ }), /* @__PURE__ */ jsx(Controller, {
4031
+ name: `regressions.${index2}.plot.color`,
4032
+ control,
4033
+ render: ({
4034
+ field
4035
+ }) => /* @__PURE__ */ jsx(MantineColorSelector, __spreadValues({}, field))
4036
+ })]
3698
4037
  }), /* @__PURE__ */ jsx(ActionIcon, {
3699
4038
  color: "red",
3700
4039
  variant: "hover",
@@ -3704,16 +4043,17 @@ function YAxisField({
3704
4043
  top: 15,
3705
4044
  right: 5
3706
4045
  },
3707
- disabled: index2 === 0,
3708
4046
  children: /* @__PURE__ */ jsx(Trash, {
3709
4047
  size: 16
3710
4048
  })
3711
4049
  })]
3712
4050
  }, index2);
3713
4051
  }
3714
- function YAxesField({
4052
+ function RegressionsField({
3715
4053
  control,
3716
- watch
4054
+ watch,
4055
+ getValues,
4056
+ data
3717
4057
  }) {
3718
4058
  const {
3719
4059
  fields,
@@ -3721,169 +4061,448 @@ function YAxesField({
3721
4061
  remove
3722
4062
  } = useFieldArray({
3723
4063
  control,
3724
- name: "y_axes"
4064
+ name: "regressions"
3725
4065
  });
3726
- const watchFieldArray = watch("y_axes");
4066
+ const watchFieldArray = watch("regressions");
3727
4067
  const controlledFields = fields.map((field, index2) => {
3728
4068
  return __spreadValues(__spreadValues({}, field), watchFieldArray[index2]);
3729
4069
  });
3730
- const addYAxis = () => append({
4070
+ const yAxisOptions = React.useMemo(() => {
4071
+ return getValues().y_axes.map(({
4072
+ name
4073
+ }, index2) => ({
4074
+ label: name,
4075
+ value: index2.toString()
4076
+ }));
4077
+ }, [getValues]);
4078
+ const add = () => append({
4079
+ transform: {
4080
+ type: "ecStat:regression",
4081
+ config: {
4082
+ method: "linear",
4083
+ order: 1,
4084
+ formulaOn: "end"
4085
+ }
4086
+ },
3731
4087
  name: "",
3732
- label_formatter: defaultNumbroFormat
4088
+ y_axis_data_key: "",
4089
+ plot: {
4090
+ type: "line",
4091
+ yAxisIndex: 0,
4092
+ color: "#666666"
4093
+ }
3733
4094
  });
3734
4095
  return /* @__PURE__ */ jsxs(Group, {
3735
4096
  direction: "column",
3736
4097
  grow: true,
3737
- children: [controlledFields.map((field, index2) => /* @__PURE__ */ jsx(YAxisField, {
4098
+ children: [controlledFields.map((regressionItem, index2) => /* @__PURE__ */ jsx(RegressionField, {
4099
+ regressionItem,
3738
4100
  control,
3739
4101
  index: index2,
3740
- remove
4102
+ remove,
4103
+ yAxisOptions,
4104
+ data
3741
4105
  })), /* @__PURE__ */ jsx(Group, {
3742
4106
  position: "center",
3743
4107
  mt: "xs",
3744
4108
  children: /* @__PURE__ */ jsx(Button, {
3745
- onClick: addYAxis,
3746
- children: "Add a Y Axis"
4109
+ onClick: add,
4110
+ children: "Add a Regression Line"
3747
4111
  })
3748
4112
  })]
3749
4113
  });
3750
4114
  }
3751
- const regressionOptions = [{
3752
- label: "Linear",
3753
- value: "linear"
4115
+ const options = [{
4116
+ label: "None",
4117
+ value: "none"
4118
+ }, {
4119
+ label: "Sum",
4120
+ value: "sum"
4121
+ }, {
4122
+ label: "Mean",
4123
+ value: "mean"
4124
+ }, {
4125
+ label: "Median",
4126
+ value: "median"
4127
+ }, {
4128
+ label: "Max",
4129
+ value: "max"
4130
+ }, {
4131
+ label: "Min",
4132
+ value: "min"
4133
+ }];
4134
+ function _AggregationSelector({
4135
+ label,
4136
+ value,
4137
+ onChange
4138
+ }, ref) {
4139
+ return /* @__PURE__ */ jsx(Select, {
4140
+ ref,
4141
+ label,
4142
+ data: options,
4143
+ value,
4144
+ onChange
4145
+ });
4146
+ }
4147
+ const AggregationSelector = React.forwardRef(_AggregationSelector);
4148
+ function _ColorArrayInput({
4149
+ label,
4150
+ value,
4151
+ onChange
4152
+ }, ref) {
4153
+ const [values, setValues] = React.useState(Array.isArray(value) ? [...value] : []);
4154
+ const add = React.useCallback(() => {
4155
+ setValues((s) => [...s, ""]);
4156
+ }, [setValues]);
4157
+ const del = React.useCallback((index2) => {
4158
+ setValues((s) => {
4159
+ s.splice(index2, 1);
4160
+ return [...s];
4161
+ });
4162
+ }, [setValues]);
4163
+ const changed = React.useMemo(() => {
4164
+ return !_.isEqual(values, value);
4165
+ }, [values, value]);
4166
+ const submit = () => {
4167
+ onChange(values.map((s) => s.toString()));
4168
+ };
4169
+ const theme = useMantineTheme();
4170
+ const swatches = React.useMemo(() => {
4171
+ return Object.entries(theme.colors).map(([_color, profile]) => profile[6]);
4172
+ }, [theme]);
4173
+ return /* @__PURE__ */ jsxs(Fragment, {
4174
+ children: [/* @__PURE__ */ jsxs(Group, {
4175
+ position: "left",
4176
+ ref,
4177
+ children: [/* @__PURE__ */ jsx(Text, {
4178
+ children: label
4179
+ }), /* @__PURE__ */ jsx(ActionIcon, {
4180
+ mr: 5,
4181
+ variant: "filled",
4182
+ color: "blue",
4183
+ disabled: !changed,
4184
+ onClick: submit,
4185
+ children: /* @__PURE__ */ jsx(DeviceFloppy, {
4186
+ size: 20
4187
+ })
4188
+ })]
4189
+ }), /* @__PURE__ */ jsxs(Group, {
4190
+ children: [values.map((v, i) => /* @__PURE__ */ jsx(ColorInput, {
4191
+ value: v,
4192
+ onChange: (color2) => {
4193
+ setValues((s) => {
4194
+ s.splice(i, 1, color2);
4195
+ return [...s];
4196
+ });
4197
+ },
4198
+ swatches,
4199
+ rightSection: /* @__PURE__ */ jsx(ActionIcon, {
4200
+ onClick: () => del(i),
4201
+ color: "red",
4202
+ children: /* @__PURE__ */ jsx(Trash, {
4203
+ size: 14
4204
+ })
4205
+ }),
4206
+ sx: {
4207
+ width: "45%"
4208
+ }
4209
+ })), /* @__PURE__ */ jsx(ActionIcon, {
4210
+ onClick: add,
4211
+ color: "blue",
4212
+ variant: "outline",
4213
+ children: /* @__PURE__ */ jsx(PlaylistAdd, {
4214
+ size: 20
4215
+ })
4216
+ })]
4217
+ })]
4218
+ });
4219
+ }
4220
+ const ColorArrayInput = React.forwardRef(_ColorArrayInput);
4221
+ const marks = [{
4222
+ label: "initial",
4223
+ value: 0
3754
4224
  }, {
3755
- label: "Exponential",
3756
- value: "exponential"
4225
+ label: "500",
4226
+ value: 25
3757
4227
  }, {
3758
- label: "Logarithmic",
3759
- value: "logarithmic"
4228
+ label: "700",
4229
+ value: 50
3760
4230
  }, {
3761
- label: "Polynomial",
3762
- value: "polynomial"
4231
+ label: "semibold",
4232
+ value: 75
4233
+ }, {
4234
+ label: "bold",
4235
+ value: 100
3763
4236
  }];
3764
- function RegressionField({
3765
- control,
3766
- regressionItem,
3767
- index: index2,
3768
- remove,
3769
- yAxisOptions,
3770
- data
3771
- }) {
3772
- const method = regressionItem.transform.config.method;
4237
+ function _MantineFontWeightSlider({
4238
+ label,
4239
+ value,
4240
+ onChange
4241
+ }, ref) {
4242
+ var _a, _b;
4243
+ const [mark, setMark] = React.useState((_b = (_a = marks.find((m2) => m2.label === value)) == null ? void 0 : _a.value) != null ? _b : marks[0].value);
4244
+ React.useEffect(() => {
4245
+ const match = marks.find((s) => s.value === mark);
4246
+ if (match) {
4247
+ onChange(match.label);
4248
+ }
4249
+ }, [mark]);
3773
4250
  return /* @__PURE__ */ jsxs(Group, {
3774
4251
  direction: "column",
3775
4252
  grow: true,
3776
- my: 0,
3777
- p: "md",
3778
- pr: 40,
3779
- sx: {
3780
- border: "1px solid #eee",
3781
- position: "relative"
3782
- },
3783
- children: [/* @__PURE__ */ jsx(Controller, {
3784
- name: `regressions.${index2}.name`,
3785
- control,
3786
- render: ({
3787
- field
3788
- }) => /* @__PURE__ */ jsx(TextInput, __spreadValues({
3789
- label: "Name",
3790
- required: true,
3791
- sx: {
3792
- flex: 1
3793
- }
3794
- }, field))
4253
+ spacing: 0,
4254
+ mt: "sm",
4255
+ mb: "lg",
4256
+ children: [/* @__PURE__ */ jsx(Text, {
4257
+ size: "sm",
4258
+ children: label
4259
+ }), /* @__PURE__ */ jsx(Slider, {
4260
+ label: null,
4261
+ marks,
4262
+ value: mark,
4263
+ onChange: setMark,
4264
+ step: 25,
4265
+ placeholder: "Pick a font size",
4266
+ ref
4267
+ })]
4268
+ });
4269
+ }
4270
+ const MantineFontWeightSlider = React.forwardRef(_MantineFontWeightSlider);
4271
+ function _TextArrayInput({
4272
+ label,
4273
+ value,
4274
+ onChange
4275
+ }, ref) {
4276
+ const [values, setValues] = React.useState(Array.isArray(value) ? [...value] : []);
4277
+ const add = React.useCallback(() => {
4278
+ setValues((s) => [...s, ""]);
4279
+ }, [setValues]);
4280
+ const del = React.useCallback((index2) => {
4281
+ setValues((s) => {
4282
+ s.splice(index2, 1);
4283
+ return [...s];
4284
+ });
4285
+ }, [setValues]);
4286
+ const changed = React.useMemo(() => {
4287
+ return !_.isEqual(values, value);
4288
+ }, [values, value]);
4289
+ const submit = () => {
4290
+ onChange(values.map((s) => s.toString()));
4291
+ };
4292
+ return /* @__PURE__ */ jsxs(Fragment, {
4293
+ children: [/* @__PURE__ */ jsxs(Group, {
4294
+ position: "left",
4295
+ ref,
4296
+ children: [/* @__PURE__ */ jsx(Text, {
4297
+ children: label
4298
+ }), /* @__PURE__ */ jsx(ActionIcon, {
4299
+ mr: 5,
4300
+ variant: "filled",
4301
+ color: "blue",
4302
+ disabled: !changed,
4303
+ onClick: submit,
4304
+ children: /* @__PURE__ */ jsx(DeviceFloppy, {
4305
+ size: 20
4306
+ })
4307
+ })]
3795
4308
  }), /* @__PURE__ */ jsxs(Group, {
3796
- direction: "row",
3797
- grow: true,
3798
- noWrap: true,
3799
- children: [/* @__PURE__ */ jsx(Controller, {
3800
- name: `regressions.${index2}.y_axis_data_key`,
3801
- control,
3802
- render: ({
3803
- field
3804
- }) => /* @__PURE__ */ jsx(DataFieldSelector, __spreadValues({
3805
- label: "Value Field",
3806
- required: true,
3807
- data,
3808
- sx: {
3809
- flex: 1
3810
- }
3811
- }, field))
3812
- }), /* @__PURE__ */ jsx(Controller, {
3813
- name: `regressions.${index2}.plot.yAxisIndex`,
3814
- control,
3815
- render: (_a) => {
3816
- var {
3817
- field: _b
3818
- } = _a, _c = _b, {
3819
- value,
3820
- onChange
3821
- } = _c, rest = __objRest(_c, [
3822
- "value",
3823
- "onChange"
3824
- ]);
3825
- var _a2;
3826
- return /* @__PURE__ */ jsx(Select, __spreadProps(__spreadValues({
3827
- label: "Y Axis",
3828
- data: yAxisOptions,
3829
- disabled: yAxisOptions.length === 0
3830
- }, rest), {
3831
- value: (_a2 = value == null ? void 0 : value.toString()) != null ? _a2 : "",
3832
- onChange: (value2) => {
3833
- if (!value2) {
3834
- onChange(0);
3835
- return;
3836
- }
3837
- onChange(Number(value2));
3838
- },
3839
- sx: {
3840
- flex: 1
3841
- }
3842
- }));
4309
+ children: [values.map((v, i) => /* @__PURE__ */ jsx(TextInput, {
4310
+ value: v,
4311
+ onChange: (event) => {
4312
+ const newValue = event.currentTarget.value;
4313
+ setValues((s) => {
4314
+ s.splice(i, 1, newValue);
4315
+ return [...s];
4316
+ });
4317
+ },
4318
+ rightSection: /* @__PURE__ */ jsx(ActionIcon, {
4319
+ onClick: () => del(i),
4320
+ color: "red",
4321
+ children: /* @__PURE__ */ jsx(Trash, {
4322
+ size: 14
4323
+ })
4324
+ }),
4325
+ sx: {
4326
+ width: "45%"
3843
4327
  }
4328
+ })), /* @__PURE__ */ jsx(ActionIcon, {
4329
+ onClick: add,
4330
+ color: "blue",
4331
+ variant: "outline",
4332
+ children: /* @__PURE__ */ jsx(PlaylistAdd, {
4333
+ size: 20
4334
+ })
3844
4335
  })]
4336
+ })]
4337
+ });
4338
+ }
4339
+ const TextArrayInput = React.forwardRef(_TextArrayInput);
4340
+ function getANewVariable() {
4341
+ return {
4342
+ name: randomId(),
4343
+ size: "20px",
4344
+ weight: "bold",
4345
+ color: {
4346
+ type: "static",
4347
+ staticColor: "blue"
4348
+ },
4349
+ data_field: "",
4350
+ aggregation: "none",
4351
+ formatter: {
4352
+ output: "number",
4353
+ mantissa: 0
4354
+ }
4355
+ };
4356
+ }
4357
+ const TemplateInput = React.forwardRef(function TemplateInput2(_a, ref) {
4358
+ var _b = _a, {
4359
+ value,
4360
+ onChange
4361
+ } = _b, rest = __objRest(_b, [
4362
+ "value",
4363
+ "onChange"
4364
+ ]);
4365
+ return /* @__PURE__ */ jsx(TextInput, __spreadValues({
4366
+ ref,
4367
+ value,
4368
+ onChange
4369
+ }, rest));
4370
+ });
4371
+ function TemplateVariableField({
4372
+ value,
4373
+ onChange,
4374
+ data
4375
+ }) {
4376
+ const colorType = value.color.type;
4377
+ const handleChange = (path, newValue) => {
4378
+ const v = _.cloneDeep(value);
4379
+ _.set(v, path, newValue);
4380
+ onChange(v);
4381
+ };
4382
+ return /* @__PURE__ */ jsxs(Box, {
4383
+ px: "sm",
4384
+ py: "md",
4385
+ children: [/* @__PURE__ */ jsx(Text, {
4386
+ weight: "bold",
4387
+ pb: 0,
4388
+ children: value.name
4389
+ }), /* @__PURE__ */ jsx(Divider, {
4390
+ my: "xs",
4391
+ label: "Data",
4392
+ labelPosition: "center"
3845
4393
  }), /* @__PURE__ */ jsxs(Group, {
3846
4394
  direction: "row",
3847
4395
  grow: true,
3848
4396
  noWrap: true,
3849
- children: [/* @__PURE__ */ jsx(Controller, {
3850
- name: `regressions.${index2}.transform.config.method`,
3851
- control,
3852
- render: ({
3853
- field
3854
- }) => /* @__PURE__ */ jsx(Select, __spreadValues({
3855
- label: "Method",
3856
- data: regressionOptions,
3857
- sx: {
3858
- flex: 1
3859
- }
3860
- }, field))
3861
- }), method === "polynomial" && /* @__PURE__ */ jsx(Controller, {
3862
- name: `regressions.${index2}.transform.config.order`,
3863
- control,
3864
- render: ({
3865
- field
3866
- }) => /* @__PURE__ */ jsx(NumberInput, __spreadValues({
3867
- label: "Order",
3868
- sx: {
3869
- flex: 1
3870
- }
3871
- }, field))
4397
+ children: [/* @__PURE__ */ jsx(TextInput, {
4398
+ label: "Name",
4399
+ required: true,
4400
+ value: value.name,
4401
+ onChange: (e) => handleChange("name", e.currentTarget.value)
4402
+ }), /* @__PURE__ */ jsx(DataFieldSelector, {
4403
+ label: "Data Field",
4404
+ required: true,
4405
+ data,
4406
+ value: value.data_field,
4407
+ onChange: (v) => handleChange("data_field", v)
4408
+ }), /* @__PURE__ */ jsx(AggregationSelector, {
4409
+ label: "Aggregation",
4410
+ value: value.aggregation,
4411
+ onChange: (v) => handleChange("aggregation", v)
3872
4412
  })]
4413
+ }), /* @__PURE__ */ jsx(NumbroFormatSelector, {
4414
+ value: value.formatter,
4415
+ onChange: (v) => handleChange("formatter", v)
4416
+ }), /* @__PURE__ */ jsx(Divider, {
4417
+ my: "xs",
4418
+ label: "Typography",
4419
+ labelPosition: "center"
4420
+ }), /* @__PURE__ */ jsx(Group, {
4421
+ direction: "column",
4422
+ grow: true,
4423
+ children: /* @__PURE__ */ jsx(TextInput, {
4424
+ label: "Font Size",
4425
+ placeholder: "10px, 1em, 1rem, 100%...",
4426
+ sx: {
4427
+ flex: 1
4428
+ },
4429
+ value: value.size,
4430
+ onChange: (e) => handleChange("size", e.currentTarget.value)
4431
+ })
4432
+ }), /* @__PURE__ */ jsx(Group, {
4433
+ position: "apart",
4434
+ grow: true,
4435
+ sx: {
4436
+ "> *": {
4437
+ flexGrow: 1,
4438
+ maxWidth: "100%"
4439
+ }
4440
+ },
4441
+ children: /* @__PURE__ */ jsx(MantineFontWeightSlider, {
4442
+ label: "Font Weight",
4443
+ value: value.weight,
4444
+ onChange: (v) => handleChange("weight", v)
4445
+ })
4446
+ }), /* @__PURE__ */ jsx(Divider, {
4447
+ my: "xs",
4448
+ label: "Style",
4449
+ labelPosition: "center"
3873
4450
  }), /* @__PURE__ */ jsxs(Group, {
3874
4451
  direction: "column",
3875
4452
  grow: true,
3876
- spacing: 4,
3877
- children: [/* @__PURE__ */ jsx(Text, {
3878
- size: "sm",
3879
- children: "Color"
3880
- }), /* @__PURE__ */ jsx(Controller, {
3881
- name: `regressions.${index2}.plot.color`,
3882
- control,
3883
- render: ({
3884
- field
3885
- }) => /* @__PURE__ */ jsx(MantineColorSelector, __spreadValues({}, field))
4453
+ children: [/* @__PURE__ */ jsx(Select, {
4454
+ label: "Color Type",
4455
+ data: [{
4456
+ label: "Static Color",
4457
+ value: "static"
4458
+ }, {
4459
+ label: "Continuous Color",
4460
+ value: "continuous"
4461
+ }],
4462
+ value: value.color.type,
4463
+ onChange: (v) => handleChange("color.type", v)
4464
+ }), colorType === "static" && /* @__PURE__ */ jsx(MantineColorSelector, {
4465
+ value: value.color.staticColor,
4466
+ onChange: (v) => handleChange("color.staticColor", v)
4467
+ }), colorType === "continuous" && /* @__PURE__ */ jsxs(Fragment, {
4468
+ children: [/* @__PURE__ */ jsx(TextArrayInput, {
4469
+ label: "Value Range",
4470
+ value: value.color.valueRange,
4471
+ onChange: (v) => handleChange("color.valueRange", v)
4472
+ }), /* @__PURE__ */ jsx(ColorArrayInput, {
4473
+ label: "Color Range",
4474
+ value: value.color.colorRange,
4475
+ onChange: (v) => handleChange("color.colorRange", v)
4476
+ })]
3886
4477
  })]
4478
+ })]
4479
+ });
4480
+ }
4481
+ function VariableField$1({
4482
+ control,
4483
+ index: index2,
4484
+ remove,
4485
+ data
4486
+ }) {
4487
+ return /* @__PURE__ */ jsxs(Group, {
4488
+ direction: "column",
4489
+ grow: true,
4490
+ my: "sm",
4491
+ p: 0,
4492
+ sx: {
4493
+ border: "1px solid #eee",
4494
+ borderTopColor: "#333",
4495
+ borderTopWidth: 2,
4496
+ position: "relative"
4497
+ },
4498
+ children: [/* @__PURE__ */ jsx(Controller, {
4499
+ name: `stats.variables.${index2}`,
4500
+ control,
4501
+ render: ({
4502
+ field
4503
+ }) => /* @__PURE__ */ jsx(TemplateVariableField, __spreadValues({
4504
+ data
4505
+ }, field))
3887
4506
  }), /* @__PURE__ */ jsx(ActionIcon, {
3888
4507
  color: "red",
3889
4508
  variant: "hover",
@@ -3899,10 +4518,9 @@ function RegressionField({
3899
4518
  })]
3900
4519
  }, index2);
3901
4520
  }
3902
- function RegressionsField({
4521
+ function StatsField({
3903
4522
  control,
3904
4523
  watch,
3905
- getValues,
3906
4524
  data
3907
4525
  }) {
3908
4526
  const {
@@ -3911,53 +4529,58 @@ function RegressionsField({
3911
4529
  remove
3912
4530
  } = useFieldArray({
3913
4531
  control,
3914
- name: "regressions"
4532
+ name: "stats.variables"
3915
4533
  });
3916
- const watchFieldArray = watch("regressions");
4534
+ watch("stats.templates");
4535
+ const watchFieldArray = watch("stats.variables");
3917
4536
  const controlledFields = fields.map((field, index2) => {
3918
4537
  return __spreadValues(__spreadValues({}, field), watchFieldArray[index2]);
3919
4538
  });
3920
- const yAxisOptions = React.useMemo(() => {
3921
- return getValues().y_axes.map(({
3922
- name
3923
- }, index2) => ({
3924
- label: name,
3925
- value: index2.toString()
3926
- }));
3927
- }, [getValues]);
3928
- const add = () => append({
3929
- transform: {
3930
- type: "ecStat:regression",
3931
- config: {
3932
- method: "linear",
3933
- order: 1,
3934
- formulaOn: "end"
3935
- }
3936
- },
3937
- name: "",
3938
- y_axis_data_key: "",
3939
- plot: {
3940
- type: "line",
3941
- yAxisIndex: 0,
3942
- color: "#666666"
3943
- }
3944
- });
4539
+ const add = () => append(getANewVariable());
3945
4540
  return /* @__PURE__ */ jsxs(Group, {
3946
4541
  direction: "column",
3947
4542
  grow: true,
3948
- children: [controlledFields.map((regressionItem, index2) => /* @__PURE__ */ jsx(RegressionField, {
3949
- regressionItem,
4543
+ children: [/* @__PURE__ */ jsxs(Group, {
4544
+ direction: "column",
4545
+ grow: true,
4546
+ noWrap: true,
4547
+ spacing: 0,
4548
+ children: [/* @__PURE__ */ jsx(Controller, {
4549
+ name: "stats.templates.top",
4550
+ control,
4551
+ render: ({
4552
+ field
4553
+ }) => /* @__PURE__ */ jsx(TemplateInput, __spreadValues({
4554
+ label: "Template for stats above the chart",
4555
+ py: "md",
4556
+ sx: {
4557
+ flexGrow: 1
4558
+ }
4559
+ }, field))
4560
+ }), /* @__PURE__ */ jsx(Controller, {
4561
+ name: "stats.templates.bottom",
4562
+ control,
4563
+ render: ({
4564
+ field
4565
+ }) => /* @__PURE__ */ jsx(TemplateInput, __spreadValues({
4566
+ label: "Template for stats under the chart",
4567
+ py: "md",
4568
+ sx: {
4569
+ flexGrow: 1
4570
+ }
4571
+ }, field))
4572
+ })]
4573
+ }), controlledFields.map((_variableItem, index2) => /* @__PURE__ */ jsx(VariableField$1, {
3950
4574
  control,
3951
4575
  index: index2,
3952
4576
  remove,
3953
- yAxisOptions,
3954
4577
  data
3955
4578
  })), /* @__PURE__ */ jsx(Group, {
3956
4579
  position: "center",
3957
4580
  mt: "xs",
3958
4581
  children: /* @__PURE__ */ jsx(Button, {
3959
4582
  onClick: add,
3960
- children: "Add a Regression Line"
4583
+ children: "Add a Variable"
3961
4584
  })
3962
4585
  })]
3963
4586
  });
@@ -3994,6 +4617,18 @@ function withDefaults(series) {
3994
4617
  }
3995
4618
  return series.map(setDefaults);
3996
4619
  }
4620
+ function normalizeStats(stats) {
4621
+ if (!stats) {
4622
+ return {
4623
+ templates: {
4624
+ top: "",
4625
+ bottom: ""
4626
+ },
4627
+ variables: []
4628
+ };
4629
+ }
4630
+ return stats;
4631
+ }
3997
4632
  function VizCartesianChartPanel({
3998
4633
  conf,
3999
4634
  setConf,
@@ -4008,9 +4643,11 @@ function VizCartesianChartPanel({
4008
4643
  ]);
4009
4644
  const defaultValues = React.useMemo(() => {
4010
4645
  const _a2 = restConf, {
4011
- x_axis_name = ""
4646
+ x_axis_name = "",
4647
+ stats
4012
4648
  } = _a2, rest = __objRest(_a2, [
4013
- "x_axis_name"
4649
+ "x_axis_name",
4650
+ "stats"
4014
4651
  ]);
4015
4652
  return __spreadValues({
4016
4653
  series: withDefaults(series != null ? series : []),
@@ -4018,7 +4655,8 @@ function VizCartesianChartPanel({
4018
4655
  y_axes: y_axes != null ? y_axes : [{
4019
4656
  name: "Y Axis",
4020
4657
  label_formatter: defaultNumbroFormat
4021
- }]
4658
+ }],
4659
+ stats: normalizeStats(stats)
4022
4660
  }, rest);
4023
4661
  }, [series, restConf]);
4024
4662
  React.useEffect(() => {
@@ -4128,6 +4766,13 @@ function VizCartesianChartPanel({
4128
4766
  getValues,
4129
4767
  data
4130
4768
  })
4769
+ }), /* @__PURE__ */ jsx(Accordion.Item, {
4770
+ label: "Stats",
4771
+ children: /* @__PURE__ */ jsx(StatsField, {
4772
+ control,
4773
+ watch,
4774
+ data
4775
+ })
4131
4776
  })]
4132
4777
  })]
4133
4778
  })
@@ -4195,231 +4840,249 @@ function VizPiePanel({
4195
4840
  })
4196
4841
  });
4197
4842
  }
4198
- const marks = [{
4199
- label: "initial",
4200
- value: 0
4201
- }, {
4202
- label: "500",
4203
- value: 25
4204
- }, {
4205
- label: "700",
4206
- value: 50
4207
- }, {
4208
- label: "semibold",
4209
- value: 75
4210
- }, {
4211
- label: "bold",
4212
- value: 100
4213
- }];
4214
- function _MantineFontWeightSlider({
4215
- label,
4216
- value,
4217
- onChange
4218
- }, ref) {
4219
- var _a, _b;
4220
- const [mark, setMark] = React.useState((_b = (_a = marks.find((m2) => m2.label === value)) == null ? void 0 : _a.value) != null ? _b : marks[0].value);
4843
+ function VizRichTextPanel({
4844
+ conf,
4845
+ setConf
4846
+ }) {
4847
+ const defaultValues = React.useMemo(() => {
4848
+ const {
4849
+ content = ""
4850
+ } = conf;
4851
+ return {
4852
+ content
4853
+ };
4854
+ }, [conf]);
4221
4855
  React.useEffect(() => {
4222
- const match = marks.find((s) => s.value === mark);
4223
- if (match) {
4224
- onChange(match.label);
4856
+ const configMalformed = !_.isEqual(conf, defaultValues);
4857
+ if (configMalformed) {
4858
+ setConf(defaultValues);
4225
4859
  }
4226
- }, [mark]);
4227
- return /* @__PURE__ */ jsxs(Group, {
4228
- direction: "column",
4229
- grow: true,
4230
- spacing: "xs",
4231
- mb: "lg",
4232
- children: [/* @__PURE__ */ jsx(Text, {
4233
- children: label
4234
- }), /* @__PURE__ */ jsx(Slider, {
4235
- label: null,
4236
- marks,
4237
- value: mark,
4238
- onChange: setMark,
4239
- step: 25,
4240
- placeholder: "Pick a font size",
4241
- ref
4242
- })]
4860
+ }, [conf, defaultValues]);
4861
+ const {
4862
+ control,
4863
+ handleSubmit,
4864
+ watch,
4865
+ getValues
4866
+ } = useForm({
4867
+ defaultValues
4243
4868
  });
4244
- }
4245
- const MantineFontWeightSlider = React.forwardRef(_MantineFontWeightSlider);
4246
- function _TextArrayInput({
4247
- label,
4248
- value,
4249
- onChange
4250
- }, ref) {
4251
- const [values, setValues] = React.useState(Array.isArray(value) ? [...value] : []);
4252
- const add = React.useCallback(() => {
4253
- setValues((s) => [...s, ""]);
4254
- }, [setValues]);
4255
- const del = React.useCallback((index2) => {
4256
- setValues((s) => {
4257
- s.splice(index2, 1);
4258
- return [...s];
4259
- });
4260
- }, [setValues]);
4869
+ watch("content");
4870
+ const values = getValues();
4261
4871
  const changed = React.useMemo(() => {
4262
- return !_.isEqual(values, value);
4263
- }, [values, value]);
4264
- const submit = () => {
4265
- onChange(values.map((s) => s.toString()));
4266
- };
4267
- return /* @__PURE__ */ jsxs(Fragment, {
4268
- children: [/* @__PURE__ */ jsxs(Group, {
4269
- position: "left",
4270
- ref,
4271
- children: [/* @__PURE__ */ jsx(Text, {
4272
- children: label
4273
- }), /* @__PURE__ */ jsx(ActionIcon, {
4274
- mr: 5,
4275
- variant: "filled",
4276
- color: "blue",
4277
- disabled: !changed,
4278
- onClick: submit,
4279
- children: /* @__PURE__ */ jsx(DeviceFloppy, {
4280
- size: 20
4281
- })
4282
- })]
4283
- }), /* @__PURE__ */ jsxs(Group, {
4284
- children: [values.map((v, i) => /* @__PURE__ */ jsx(TextInput, {
4285
- value: v,
4286
- onChange: (event) => {
4287
- const newValue = event.currentTarget.value;
4288
- setValues((s) => {
4289
- s.splice(i, 1, newValue);
4290
- return [...s];
4291
- });
4872
+ return !_.isEqual(values, conf);
4873
+ }, [values, conf]);
4874
+ return /* @__PURE__ */ jsx(Group, {
4875
+ direction: "column",
4876
+ mt: "md",
4877
+ spacing: "xs",
4878
+ grow: true,
4879
+ children: /* @__PURE__ */ jsxs("form", {
4880
+ onSubmit: handleSubmit(setConf),
4881
+ children: [/* @__PURE__ */ jsxs(Group, {
4882
+ position: "left",
4883
+ py: "md",
4884
+ pl: "md",
4885
+ sx: {
4886
+ borderBottom: "1px solid #eee",
4887
+ background: "#efefef"
4292
4888
  },
4293
- rightSection: /* @__PURE__ */ jsx(ActionIcon, {
4294
- onClick: () => del(i),
4295
- color: "red",
4296
- children: /* @__PURE__ */ jsx(Trash, {
4297
- size: 14
4889
+ children: [/* @__PURE__ */ jsx(Text, {
4890
+ children: "Content"
4891
+ }), /* @__PURE__ */ jsx(ActionIcon, {
4892
+ type: "submit",
4893
+ mr: 5,
4894
+ variant: "filled",
4895
+ color: "blue",
4896
+ disabled: !changed,
4897
+ children: /* @__PURE__ */ jsx(DeviceFloppy, {
4898
+ size: 20
4298
4899
  })
4299
- }),
4300
- sx: {
4301
- width: "45%"
4302
- }
4303
- })), /* @__PURE__ */ jsx(ActionIcon, {
4304
- onClick: add,
4305
- color: "blue",
4306
- variant: "outline",
4307
- children: /* @__PURE__ */ jsx(PlaylistAdd, {
4308
- size: 20
4309
- })
4900
+ })]
4901
+ }), /* @__PURE__ */ jsx(Controller, {
4902
+ name: "content",
4903
+ control,
4904
+ render: ({
4905
+ field
4906
+ }) => /* @__PURE__ */ jsx(RichTextEditor, __spreadValues({
4907
+ sx: {
4908
+ flex: 1
4909
+ }
4910
+ }, field))
4310
4911
  })]
4311
- })]
4912
+ })
4312
4913
  });
4313
4914
  }
4314
- const TextArrayInput = React.forwardRef(_TextArrayInput);
4315
- function _ColorArrayInput({
4316
- label,
4317
- value,
4318
- onChange
4319
- }, ref) {
4320
- const [values, setValues] = React.useState(Array.isArray(value) ? [...value] : []);
4321
- const add = React.useCallback(() => {
4322
- setValues((s) => [...s, ""]);
4323
- }, [setValues]);
4324
- const del = React.useCallback((index2) => {
4325
- setValues((s) => {
4326
- s.splice(index2, 1);
4327
- return [...s];
4328
- });
4329
- }, [setValues]);
4330
- const changed = React.useMemo(() => {
4331
- return !_.isEqual(values, value);
4332
- }, [values, value]);
4333
- const submit = () => {
4334
- onChange(values.map((s) => s.toString()));
4915
+ function updateSchema(legacyConf) {
4916
+ if ("variables" in legacyConf) {
4917
+ return legacyConf;
4918
+ }
4919
+ const {
4920
+ align,
4921
+ size,
4922
+ weight,
4923
+ color: color2,
4924
+ content: {
4925
+ prefix = "",
4926
+ data_field = "value",
4927
+ formatter = {
4928
+ output: "number",
4929
+ mantissa: 0
4930
+ },
4931
+ postfix = ""
4932
+ } = {}
4933
+ } = legacyConf;
4934
+ return {
4935
+ align,
4936
+ template: `${prefix} \${value} ${postfix}`,
4937
+ variables: [
4938
+ {
4939
+ name: "value",
4940
+ data_field,
4941
+ aggregation: "none",
4942
+ formatter,
4943
+ color: color2,
4944
+ weight,
4945
+ size
4946
+ }
4947
+ ]
4335
4948
  };
4336
- const theme = useMantineTheme();
4337
- const swatches = React.useMemo(() => {
4338
- return Object.entries(theme.colors).map(([_color, profile]) => profile[6]);
4339
- }, [theme]);
4340
- return /* @__PURE__ */ jsxs(Fragment, {
4341
- children: [/* @__PURE__ */ jsxs(Group, {
4342
- position: "left",
4343
- ref,
4344
- children: [/* @__PURE__ */ jsx(Text, {
4345
- children: label
4346
- }), /* @__PURE__ */ jsx(ActionIcon, {
4347
- mr: 5,
4348
- variant: "filled",
4349
- color: "blue",
4350
- disabled: !changed,
4351
- onClick: submit,
4352
- children: /* @__PURE__ */ jsx(DeviceFloppy, {
4353
- size: 20
4354
- })
4355
- })]
4356
- }), /* @__PURE__ */ jsxs(Group, {
4357
- children: [values.map((v, i) => /* @__PURE__ */ jsx(ColorInput, {
4358
- value: v,
4359
- onChange: (color2) => {
4360
- setValues((s) => {
4361
- s.splice(i, 1, color2);
4362
- return [...s];
4363
- });
4364
- },
4365
- swatches,
4366
- rightSection: /* @__PURE__ */ jsx(ActionIcon, {
4367
- onClick: () => del(i),
4368
- color: "red",
4369
- children: /* @__PURE__ */ jsx(Trash, {
4370
- size: 14
4371
- })
4372
- }),
4373
- sx: {
4374
- width: "45%"
4375
- }
4376
- })), /* @__PURE__ */ jsx(ActionIcon, {
4377
- onClick: add,
4378
- color: "blue",
4379
- variant: "outline",
4380
- children: /* @__PURE__ */ jsx(PlaylistAdd, {
4381
- size: 20
4382
- })
4383
- })]
4949
+ }
4950
+ function VariableField({
4951
+ control,
4952
+ index: index2,
4953
+ remove,
4954
+ data
4955
+ }) {
4956
+ return /* @__PURE__ */ jsxs(Group, {
4957
+ direction: "column",
4958
+ grow: true,
4959
+ my: "sm",
4960
+ p: 0,
4961
+ sx: {
4962
+ border: "1px solid #eee",
4963
+ borderTopColor: "#333",
4964
+ borderTopWidth: 2,
4965
+ position: "relative"
4966
+ },
4967
+ children: [/* @__PURE__ */ jsx(Controller, {
4968
+ name: `variables.${index2}`,
4969
+ control,
4970
+ render: ({
4971
+ field
4972
+ }) => /* @__PURE__ */ jsx(TemplateVariableField, __spreadValues({
4973
+ data
4974
+ }, field))
4975
+ }), /* @__PURE__ */ jsx(ActionIcon, {
4976
+ color: "red",
4977
+ variant: "hover",
4978
+ onClick: () => remove(index2),
4979
+ sx: {
4980
+ position: "absolute",
4981
+ top: 15,
4982
+ right: 5
4983
+ },
4984
+ children: /* @__PURE__ */ jsx(Trash, {
4985
+ size: 16
4986
+ })
4384
4987
  })]
4385
- });
4988
+ }, index2);
4386
4989
  }
4387
- const ColorArrayInput = React.forwardRef(_ColorArrayInput);
4388
- function VizStatsPanel({
4389
- conf,
4390
- setConf,
4990
+ function VariablesField({
4991
+ control,
4992
+ watch,
4391
4993
  data
4392
4994
  }) {
4393
- const defaultValues = _.merge({}, {
4995
+ const {
4996
+ fields,
4997
+ append,
4998
+ remove
4999
+ } = useFieldArray({
5000
+ control,
5001
+ name: "variables"
5002
+ });
5003
+ const watchFieldArray = watch("variables");
5004
+ const controlledFields = fields.map((field, index2) => {
5005
+ return __spreadValues(__spreadValues({}, field), watchFieldArray[index2]);
5006
+ });
5007
+ const add = () => append(getANewVariable());
5008
+ return /* @__PURE__ */ jsxs(Group, {
5009
+ direction: "column",
5010
+ grow: true,
5011
+ children: [controlledFields.map((_variableItem, index2) => /* @__PURE__ */ jsx(VariableField, {
5012
+ control,
5013
+ index: index2,
5014
+ remove,
5015
+ data
5016
+ })), /* @__PURE__ */ jsx(Group, {
5017
+ position: "center",
5018
+ mt: "xs",
5019
+ children: /* @__PURE__ */ jsx(Button, {
5020
+ onClick: add,
5021
+ children: "Add a Variable"
5022
+ })
5023
+ })]
5024
+ });
5025
+ }
5026
+ function getInitialConf() {
5027
+ return {
4394
5028
  align: "center",
4395
- size: "100px",
4396
- weight: "bold",
4397
- color: {
4398
- type: "static",
4399
- staticColor: "red"
4400
- },
4401
- content: {
4402
- prefix: "",
5029
+ template: "The variable ${value} is defined in Variables section",
5030
+ variables: [{
5031
+ name: "value",
5032
+ size: "20px",
5033
+ weight: "bold",
5034
+ color: {
5035
+ type: "static",
5036
+ staticColor: "blue"
5037
+ },
4403
5038
  data_field: "",
5039
+ aggregation: "none",
4404
5040
  formatter: {
4405
5041
  output: "number",
4406
5042
  mantissa: 0
4407
- },
4408
- postfix: ""
5043
+ }
5044
+ }]
5045
+ };
5046
+ }
5047
+ function VizStatsPanel({
5048
+ conf,
5049
+ setConf,
5050
+ data
5051
+ }) {
5052
+ const defaultValues = React.useMemo(() => {
5053
+ const {
5054
+ align,
5055
+ template = "",
5056
+ variables = []
5057
+ } = updateSchema(conf);
5058
+ if (!align) {
5059
+ return getInitialConf();
4409
5060
  }
4410
- }, conf);
5061
+ return {
5062
+ variables,
5063
+ template,
5064
+ align
5065
+ };
5066
+ }, [conf]);
5067
+ React.useEffect(() => {
5068
+ const configMalformed = !_.isEqual(conf, defaultValues);
5069
+ if (configMalformed) {
5070
+ setConf(defaultValues);
5071
+ }
5072
+ }, [conf, defaultValues]);
4411
5073
  const {
4412
5074
  control,
4413
5075
  handleSubmit,
4414
5076
  watch,
4415
- formState: {
4416
- isDirty
4417
- }
5077
+ getValues
4418
5078
  } = useForm({
4419
5079
  defaultValues
4420
5080
  });
4421
- const colorType = watch("color.type");
4422
- watch("color.valueField");
5081
+ watch(["variables", "template"]);
5082
+ const values = getValues();
5083
+ const changed = React.useMemo(() => {
5084
+ return !_.isEqual(values, conf);
5085
+ }, [values, conf]);
4423
5086
  return /* @__PURE__ */ jsx(Group, {
4424
5087
  direction: "column",
4425
5088
  mt: "md",
@@ -4444,166 +5107,32 @@ function VizStatsPanel({
4444
5107
  mr: 5,
4445
5108
  variant: "filled",
4446
5109
  color: "blue",
4447
- disabled: !isDirty,
5110
+ disabled: !changed,
4448
5111
  children: /* @__PURE__ */ jsx(DeviceFloppy, {
4449
5112
  size: 20
4450
5113
  })
4451
5114
  })]
4452
- }), /* @__PURE__ */ jsxs(Accordion, {
4453
- offsetIcon: false,
4454
- multiple: true,
4455
- initialState: {
4456
- 0: true,
4457
- 2: true
4458
- },
4459
- children: [/* @__PURE__ */ jsx(Accordion.Item, {
4460
- label: "Content",
4461
- children: /* @__PURE__ */ jsxs(Group, {
4462
- direction: "column",
4463
- grow: true,
4464
- children: [/* @__PURE__ */ jsxs(Group, {
4465
- direction: "row",
4466
- grow: true,
4467
- noWrap: true,
4468
- children: [/* @__PURE__ */ jsx(Controller, {
4469
- name: "content.prefix",
4470
- control,
4471
- render: ({
4472
- field
4473
- }) => /* @__PURE__ */ jsx(TextInput, __spreadValues({
4474
- label: "Prefix",
4475
- sx: {
4476
- flexGrow: 1
4477
- }
4478
- }, field))
4479
- }), /* @__PURE__ */ jsx(Controller, {
4480
- name: "content.data_field",
4481
- control,
4482
- render: ({
4483
- field
4484
- }) => /* @__PURE__ */ jsx(DataFieldSelector, __spreadValues({
4485
- label: "Data Field",
4486
- required: true,
4487
- data
4488
- }, field))
4489
- }), /* @__PURE__ */ jsx(Controller, {
4490
- name: "content.postfix",
4491
- control,
4492
- render: ({
4493
- field
4494
- }) => /* @__PURE__ */ jsx(TextInput, __spreadValues({
4495
- label: "Postfix",
4496
- sx: {
4497
- flexGrow: 1
4498
- }
4499
- }, field))
4500
- })]
4501
- }), /* @__PURE__ */ jsx(Controller, {
4502
- name: "content.formatter",
4503
- control,
4504
- render: ({
4505
- field
4506
- }) => /* @__PURE__ */ jsx(NumbroFormatSelector, __spreadValues({}, field))
4507
- })]
4508
- })
4509
- }), /* @__PURE__ */ jsxs(Accordion.Item, {
4510
- label: "Font",
4511
- children: [/* @__PURE__ */ jsx(Group, {
4512
- direction: "column",
4513
- grow: true,
4514
- children: /* @__PURE__ */ jsx(Controller, {
4515
- name: "size",
4516
- control,
4517
- render: ({
4518
- field
4519
- }) => /* @__PURE__ */ jsx(TextInput, __spreadValues({
4520
- label: "Font Size",
4521
- placeholder: "10px, 1em, 1rem, 100%...",
4522
- sx: {
4523
- flex: 1
4524
- }
4525
- }, field))
4526
- })
4527
- }), /* @__PURE__ */ jsx(Group, {
4528
- position: "apart",
4529
- grow: true,
4530
- sx: {
4531
- "> *": {
4532
- flexGrow: 1,
4533
- maxWidth: "100%"
4534
- }
4535
- },
4536
- children: /* @__PURE__ */ jsx(Controller, {
4537
- name: "weight",
4538
- control,
4539
- render: ({
4540
- field
4541
- }) => /* @__PURE__ */ jsx(MantineFontWeightSlider, __spreadValues({
4542
- label: "Font Weight"
4543
- }, field))
4544
- })
4545
- })]
4546
- }), /* @__PURE__ */ jsx(Accordion.Item, {
4547
- label: "Color",
4548
- children: /* @__PURE__ */ jsxs(Group, {
4549
- direction: "column",
4550
- grow: true,
4551
- children: [/* @__PURE__ */ jsx(Controller, {
4552
- name: "color.type",
4553
- control,
4554
- render: ({
4555
- field
4556
- }) => /* @__PURE__ */ jsx(Select, __spreadValues({
4557
- label: "Color Type",
4558
- data: [{
4559
- label: "Static Color",
4560
- value: "static"
4561
- }, {
4562
- label: "Continuous Color",
4563
- value: "continuous"
4564
- }]
4565
- }, field))
4566
- }), colorType === "static" && /* @__PURE__ */ jsx(Controller, {
4567
- name: "color.staticColor",
4568
- control,
4569
- render: ({
4570
- field
4571
- }) => /* @__PURE__ */ jsx(MantineColorSelector, __spreadValues({}, field))
4572
- }), colorType === "continuous" && /* @__PURE__ */ jsxs(Fragment, {
4573
- children: [/* @__PURE__ */ jsx(Controller, {
4574
- name: "color.valueField",
4575
- control,
4576
- defaultValue: "",
4577
- render: ({
4578
- field
4579
- }) => /* @__PURE__ */ jsx(TextInput, __spreadValues({
4580
- placeholder: "Calculate color with this field",
4581
- label: "Value Field",
4582
- required: true,
4583
- sx: {
4584
- flex: 1
4585
- }
4586
- }, field))
4587
- }), /* @__PURE__ */ jsx(Controller, {
4588
- name: "color.valueRange",
4589
- control,
4590
- render: ({
4591
- field
4592
- }) => /* @__PURE__ */ jsx(TextArrayInput, __spreadValues({
4593
- label: "Value Range"
4594
- }, field))
4595
- }), /* @__PURE__ */ jsx(Controller, {
4596
- name: "color.colorRange",
4597
- control,
4598
- render: ({
4599
- field
4600
- }) => /* @__PURE__ */ jsx(ColorArrayInput, __spreadValues({
4601
- label: "Color Range"
4602
- }, field))
4603
- })]
4604
- })]
4605
- })
4606
- })]
5115
+ }), /* @__PURE__ */ jsx(Controller, {
5116
+ name: "template",
5117
+ control,
5118
+ render: ({
5119
+ field
5120
+ }) => /* @__PURE__ */ jsx(TemplateInput, __spreadValues({
5121
+ label: "Template",
5122
+ py: "md",
5123
+ sx: {
5124
+ flexGrow: 1
5125
+ }
5126
+ }, field))
5127
+ }), /* @__PURE__ */ jsx(Text, {
5128
+ pb: "sm",
5129
+ pt: "md",
5130
+ size: "sm",
5131
+ children: "Variables"
5132
+ }), /* @__PURE__ */ jsx(VariablesField, {
5133
+ control,
5134
+ watch,
5135
+ data
4607
5136
  })]
4608
5137
  })
4609
5138
  });
@@ -4688,17 +5217,17 @@ function ValueTypeSelector({
4688
5217
  sx
4689
5218
  });
4690
5219
  }
4691
- function VizTablePanel(_e) {
4692
- var _f = _e, {
4693
- conf: _g
4694
- } = _f, _h = _g, {
5220
+ function VizTablePanel(_c) {
5221
+ var _d = _c, {
5222
+ conf: _e
5223
+ } = _d, _f = _e, {
4695
5224
  columns
4696
- } = _h, restConf = __objRest(_h, [
5225
+ } = _f, restConf = __objRest(_f, [
4697
5226
  "columns"
4698
5227
  ]), {
4699
5228
  setConf,
4700
5229
  data
4701
- } = _f;
5230
+ } = _d;
4702
5231
  const form = useForm$1({
4703
5232
  initialValues: __spreadValues({
4704
5233
  id_field: "id",
@@ -5044,6 +5573,10 @@ const types = [{
5044
5573
  value: "stats",
5045
5574
  label: "Stats",
5046
5575
  Panel: VizStatsPanel
5576
+ }, {
5577
+ value: "rich-text",
5578
+ label: "Rich Text",
5579
+ Panel: VizRichTextPanel
5047
5580
  }, {
5048
5581
  value: "table",
5049
5582
  label: "Table",
@@ -5722,7 +6255,7 @@ function SelectOrAddQuery({
5722
6255
  chooseDefault();
5723
6256
  }
5724
6257
  }, [id, queries, chooseDefault]);
5725
- const options = React.useMemo(() => {
6258
+ const options2 = React.useMemo(() => {
5726
6259
  return queries.map((d) => ({
5727
6260
  value: d.id,
5728
6261
  label: d.id
@@ -5749,7 +6282,7 @@ function SelectOrAddQuery({
5749
6282
  children: [/* @__PURE__ */ jsx(Text, {
5750
6283
  children: "Select a Query"
5751
6284
  }), /* @__PURE__ */ jsx(Select, {
5752
- data: options,
6285
+ data: options2,
5753
6286
  value: id,
5754
6287
  onChange: setID,
5755
6288
  allowDeselect: false,