@devtable/dashboard 1.6.0 → 1.9.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.
@@ -29,13 +29,17 @@ var __objRest = (source, exclude) => {
29
29
  }
30
30
  return target;
31
31
  };
32
+ var __publicField = (obj, key, value) => {
33
+ __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
34
+ return value;
35
+ };
32
36
  import React from "react";
33
37
  import _ from "lodash";
34
38
  import { WidthProvider, Responsive } from "react-grid-layout";
35
- import { Popover, Tooltip, Group, Text, ActionIcon, TextInput, Box, LoadingOverlay, Table, Select, Button, useMantineTheme, ColorSwatch, JsonInput, Anchor, Switch, Slider, Modal, AppShell, Tabs, Menu, Divider, Container, SegmentedControl, Textarea } from "@mantine/core";
39
+ import { Popover, Tooltip, Group, Text, ActionIcon, TextInput, Box, LoadingOverlay, Table, Select, Button, useMantineTheme, ColorSwatch, JsonInput, Anchor, Slider, ColorInput, Accordion, Switch, Modal, AppShell, Tabs, Menu, Divider, Container, SegmentedControl, Textarea } from "@mantine/core";
36
40
  import { useRequest } from "ahooks";
37
41
  import axios from "axios";
38
- import { InfoCircle, DeviceFloppy, Refresh, Trash, Settings, Paint, PlayerPlay, PlaylistAdd, ClipboardText, Database, Recycle, Share } from "tabler-icons-react";
42
+ import { InfoCircle, DeviceFloppy, Refresh, Trash, PlaylistAdd, Settings, Resize, Paint, PlayerPlay, ClipboardText, Database, Recycle, Share } from "tabler-icons-react";
39
43
  import RichTextEditor, { RichTextEditor as RichTextEditor$1 } from "@mantine/rte";
40
44
  import { useInputState, useElementSize, randomId } from "@mantine/hooks";
41
45
  import ReactEChartsCore from "echarts-for-react/lib/core";
@@ -50,6 +54,7 @@ import { formList, useForm as useForm$1 } from "@mantine/form";
50
54
  import { Prism } from "@mantine/prism";
51
55
  var DashboardMode = /* @__PURE__ */ ((DashboardMode2) => {
52
56
  DashboardMode2["Use"] = "use";
57
+ DashboardMode2["Layout"] = "layout";
53
58
  DashboardMode2["Edit"] = "edit";
54
59
  return DashboardMode2;
55
60
  })(DashboardMode || {});
@@ -58,7 +63,9 @@ const initialContext$3 = {
58
63
  freezeLayout: () => {
59
64
  },
60
65
  mode: DashboardMode.Edit,
61
- inEditMode: false
66
+ inEditMode: false,
67
+ inLayoutMode: false,
68
+ inUseMode: true
62
69
  };
63
70
  const LayoutStateContext = React.createContext(initialContext$3);
64
71
  const getRequest = (method) => {
@@ -216,7 +223,7 @@ function DescriptionPopover({
216
223
  React.useEffect(() => {
217
224
  freezeLayout(opened);
218
225
  }, [opened]);
219
- if (!description) {
226
+ if (!description || description === "<p><br></p>") {
220
227
  return null;
221
228
  }
222
229
  const target = trigger === "click" ? /* @__PURE__ */ jsx(Tooltip, {
@@ -463,7 +470,8 @@ function DataPreview({
463
470
  });
464
471
  if (loading) {
465
472
  return /* @__PURE__ */ jsx(LoadingOverlay, {
466
- visible: loading
473
+ visible: loading,
474
+ exitTransitionDuration: 0
467
475
  });
468
476
  }
469
477
  if (data.length === 0) {
@@ -845,7 +853,7 @@ function CellValue({
845
853
  }
846
854
  function VizTable({
847
855
  conf,
848
- data,
856
+ data = [],
849
857
  width,
850
858
  height
851
859
  }) {
@@ -886,7 +894,7 @@ function VizTable({
886
894
  }, label))
887
895
  })
888
896
  }), /* @__PURE__ */ jsx("tbody", {
889
- children: data.map((row, index2) => /* @__PURE__ */ jsx("tr", {
897
+ children: data.slice(0, 30).map((row, index2) => /* @__PURE__ */ jsx("tr", {
890
898
  children: finalColumns.map(({
891
899
  value_field,
892
900
  value_type
@@ -894,7 +902,8 @@ function VizTable({
894
902
  children: /* @__PURE__ */ jsx(Group, {
895
903
  sx: {
896
904
  "&, .mantine-Text-root": {
897
- fontFamily: "monospace"
905
+ fontFamily: "monospace",
906
+ fontSize: rest.fontSize
898
907
  }
899
908
  },
900
909
  children: /* @__PURE__ */ jsx(CellValue, {
@@ -904,12 +913,26 @@ function VizTable({
904
913
  })
905
914
  }, row[value_field]))
906
915
  }, id_field ? row[id_field] : `row-${index2}`))
916
+ }), data.length > 100 && /* @__PURE__ */ jsx("tfoot", {
917
+ children: /* @__PURE__ */ jsx("tr", {
918
+ children: /* @__PURE__ */ jsx("td", {
919
+ colSpan: labels.length,
920
+ children: /* @__PURE__ */ jsx(Text, {
921
+ color: "red",
922
+ size: "sm",
923
+ children: "Showing only the first 30 rows to avoid causing slow performance"
924
+ })
925
+ })
926
+ })
907
927
  })]
908
928
  }));
909
929
  }
910
- function interpolateString(template, params = {}) {
911
- const names = Object.keys(params);
912
- const vals = Object.values(params);
930
+ function interpolateString$1(template, params = {}) {
931
+ const extendedParams = __spreadProps(__spreadValues({}, params), {
932
+ numbro
933
+ });
934
+ const names = Object.keys(extendedParams);
935
+ const vals = Object.values(extendedParams);
913
936
  try {
914
937
  return new Function(...names, `return \`${template}\`;`)(...vals);
915
938
  } catch (error) {
@@ -935,7 +958,7 @@ function VizText({
935
958
  sx: {
936
959
  fontSize: size
937
960
  },
938
- children: interpolateString(template, data[0])
961
+ children: interpolateString$1(template, data[0])
939
962
  }), `${template}---${index2}`);
940
963
  })
941
964
  });
@@ -1094,6 +1117,420 @@ function VizPie({
1094
1117
  }
1095
1118
  });
1096
1119
  }
1120
+ var invariant = function() {
1121
+ };
1122
+ const clamp$1 = (min, max, v) => Math.min(Math.max(v, min), max);
1123
+ const progress = (from, to, value) => {
1124
+ const toFromDifference = to - from;
1125
+ return toFromDifference === 0 ? 1 : (value - from) / toFromDifference;
1126
+ };
1127
+ const mix = (from, to, progress2) => -progress2 * from + progress2 * to + from;
1128
+ const clamp = (min, max) => (v) => Math.max(Math.min(v, max), min);
1129
+ const sanitize = (v) => v % 1 ? Number(v.toFixed(5)) : v;
1130
+ const floatRegex = /(-)?([\d]*\.?[\d])+/g;
1131
+ 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;
1132
+ const singleColorRegex = /^(#[0-9a-f]{3}|#(?:[0-9a-f]{2}){2,4}|(rgb|hsl)a?\((-?[\d\.]+%?[,\s]+){2,3}\s*\/*\s*[\d\.]+%?\))$/i;
1133
+ function isString(v) {
1134
+ return typeof v === "string";
1135
+ }
1136
+ const number = {
1137
+ test: (v) => typeof v === "number",
1138
+ parse: parseFloat,
1139
+ transform: (v) => v
1140
+ };
1141
+ const alpha = Object.assign(Object.assign({}, number), { transform: clamp(0, 1) });
1142
+ Object.assign(Object.assign({}, number), { default: 1 });
1143
+ const createUnitType = (unit) => ({
1144
+ test: (v) => isString(v) && v.endsWith(unit) && v.split(" ").length === 1,
1145
+ parse: parseFloat,
1146
+ transform: (v) => `${v}${unit}`
1147
+ });
1148
+ const percent = createUnitType("%");
1149
+ Object.assign(Object.assign({}, percent), { parse: (v) => percent.parse(v) / 100, transform: (v) => percent.transform(v * 100) });
1150
+ const isColorString = (type, testProp) => (v) => {
1151
+ return Boolean(isString(v) && singleColorRegex.test(v) && v.startsWith(type) || testProp && Object.prototype.hasOwnProperty.call(v, testProp));
1152
+ };
1153
+ const splitColor = (aName, bName, cName) => (v) => {
1154
+ if (!isString(v))
1155
+ return v;
1156
+ const [a, b, c, alpha2] = v.match(floatRegex);
1157
+ return {
1158
+ [aName]: parseFloat(a),
1159
+ [bName]: parseFloat(b),
1160
+ [cName]: parseFloat(c),
1161
+ alpha: alpha2 !== void 0 ? parseFloat(alpha2) : 1
1162
+ };
1163
+ };
1164
+ const hsla = {
1165
+ test: isColorString("hsl", "hue"),
1166
+ parse: splitColor("hue", "saturation", "lightness"),
1167
+ transform: ({ hue, saturation, lightness, alpha: alpha$1 = 1 }) => {
1168
+ return "hsla(" + Math.round(hue) + ", " + percent.transform(sanitize(saturation)) + ", " + percent.transform(sanitize(lightness)) + ", " + sanitize(alpha.transform(alpha$1)) + ")";
1169
+ }
1170
+ };
1171
+ const clampRgbUnit = clamp(0, 255);
1172
+ const rgbUnit = Object.assign(Object.assign({}, number), { transform: (v) => Math.round(clampRgbUnit(v)) });
1173
+ const rgba = {
1174
+ test: isColorString("rgb", "red"),
1175
+ parse: splitColor("red", "green", "blue"),
1176
+ transform: ({ red, green, blue, alpha: alpha$1 = 1 }) => "rgba(" + rgbUnit.transform(red) + ", " + rgbUnit.transform(green) + ", " + rgbUnit.transform(blue) + ", " + sanitize(alpha.transform(alpha$1)) + ")"
1177
+ };
1178
+ function parseHex(v) {
1179
+ let r = "";
1180
+ let g = "";
1181
+ let b = "";
1182
+ let a = "";
1183
+ if (v.length > 5) {
1184
+ r = v.substr(1, 2);
1185
+ g = v.substr(3, 2);
1186
+ b = v.substr(5, 2);
1187
+ a = v.substr(7, 2);
1188
+ } else {
1189
+ r = v.substr(1, 1);
1190
+ g = v.substr(2, 1);
1191
+ b = v.substr(3, 1);
1192
+ a = v.substr(4, 1);
1193
+ r += r;
1194
+ g += g;
1195
+ b += b;
1196
+ a += a;
1197
+ }
1198
+ return {
1199
+ red: parseInt(r, 16),
1200
+ green: parseInt(g, 16),
1201
+ blue: parseInt(b, 16),
1202
+ alpha: a ? parseInt(a, 16) / 255 : 1
1203
+ };
1204
+ }
1205
+ const hex = {
1206
+ test: isColorString("#"),
1207
+ parse: parseHex,
1208
+ transform: rgba.transform
1209
+ };
1210
+ const color = {
1211
+ test: (v) => rgba.test(v) || hex.test(v) || hsla.test(v),
1212
+ parse: (v) => {
1213
+ if (rgba.test(v)) {
1214
+ return rgba.parse(v);
1215
+ } else if (hsla.test(v)) {
1216
+ return hsla.parse(v);
1217
+ } else {
1218
+ return hex.parse(v);
1219
+ }
1220
+ },
1221
+ transform: (v) => {
1222
+ return isString(v) ? v : v.hasOwnProperty("red") ? rgba.transform(v) : hsla.transform(v);
1223
+ }
1224
+ };
1225
+ const colorToken = "${c}";
1226
+ const numberToken = "${n}";
1227
+ function test(v) {
1228
+ var _a, _b, _c, _d;
1229
+ 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;
1230
+ }
1231
+ function analyse$1(v) {
1232
+ if (typeof v === "number")
1233
+ v = `${v}`;
1234
+ const values = [];
1235
+ let numColors = 0;
1236
+ const colors = v.match(colorRegex);
1237
+ if (colors) {
1238
+ numColors = colors.length;
1239
+ v = v.replace(colorRegex, colorToken);
1240
+ values.push(...colors.map(color.parse));
1241
+ }
1242
+ const numbers = v.match(floatRegex);
1243
+ if (numbers) {
1244
+ v = v.replace(floatRegex, numberToken);
1245
+ values.push(...numbers.map(number.parse));
1246
+ }
1247
+ return { values, numColors, tokenised: v };
1248
+ }
1249
+ function parse(v) {
1250
+ return analyse$1(v).values;
1251
+ }
1252
+ function createTransformer(v) {
1253
+ const { values, numColors, tokenised } = analyse$1(v);
1254
+ const numValues = values.length;
1255
+ return (v2) => {
1256
+ let output = tokenised;
1257
+ for (let i = 0; i < numValues; i++) {
1258
+ output = output.replace(i < numColors ? colorToken : numberToken, i < numColors ? color.transform(v2[i]) : sanitize(v2[i]));
1259
+ }
1260
+ return output;
1261
+ };
1262
+ }
1263
+ const convertNumbersToZero = (v) => typeof v === "number" ? 0 : v;
1264
+ function getAnimatableNone(v) {
1265
+ const parsed = parse(v);
1266
+ const transformer = createTransformer(v);
1267
+ return transformer(parsed.map(convertNumbersToZero));
1268
+ }
1269
+ const complex = { test, parse, createTransformer, getAnimatableNone };
1270
+ function hueToRgb(p2, q2, t) {
1271
+ if (t < 0)
1272
+ t += 1;
1273
+ if (t > 1)
1274
+ t -= 1;
1275
+ if (t < 1 / 6)
1276
+ return p2 + (q2 - p2) * 6 * t;
1277
+ if (t < 1 / 2)
1278
+ return q2;
1279
+ if (t < 2 / 3)
1280
+ return p2 + (q2 - p2) * (2 / 3 - t) * 6;
1281
+ return p2;
1282
+ }
1283
+ function hslaToRgba({ hue, saturation, lightness, alpha: alpha2 }) {
1284
+ hue /= 360;
1285
+ saturation /= 100;
1286
+ lightness /= 100;
1287
+ let red = 0;
1288
+ let green = 0;
1289
+ let blue = 0;
1290
+ if (!saturation) {
1291
+ red = green = blue = lightness;
1292
+ } else {
1293
+ const q2 = lightness < 0.5 ? lightness * (1 + saturation) : lightness + saturation - lightness * saturation;
1294
+ const p2 = 2 * lightness - q2;
1295
+ red = hueToRgb(p2, q2, hue + 1 / 3);
1296
+ green = hueToRgb(p2, q2, hue);
1297
+ blue = hueToRgb(p2, q2, hue - 1 / 3);
1298
+ }
1299
+ return {
1300
+ red: Math.round(red * 255),
1301
+ green: Math.round(green * 255),
1302
+ blue: Math.round(blue * 255),
1303
+ alpha: alpha2
1304
+ };
1305
+ }
1306
+ const mixLinearColor = (from, to, v) => {
1307
+ const fromExpo = from * from;
1308
+ const toExpo = to * to;
1309
+ return Math.sqrt(Math.max(0, v * (toExpo - fromExpo) + fromExpo));
1310
+ };
1311
+ const colorTypes = [hex, rgba, hsla];
1312
+ const getColorType = (v) => colorTypes.find((type) => type.test(v));
1313
+ const mixColor = (from, to) => {
1314
+ let fromColorType = getColorType(from);
1315
+ let toColorType = getColorType(to);
1316
+ let fromColor = fromColorType.parse(from);
1317
+ let toColor = toColorType.parse(to);
1318
+ if (fromColorType === hsla) {
1319
+ fromColor = hslaToRgba(fromColor);
1320
+ fromColorType = rgba;
1321
+ }
1322
+ if (toColorType === hsla) {
1323
+ toColor = hslaToRgba(toColor);
1324
+ toColorType = rgba;
1325
+ }
1326
+ const blended = Object.assign({}, fromColor);
1327
+ return (v) => {
1328
+ for (const key in blended) {
1329
+ if (key !== "alpha") {
1330
+ blended[key] = mixLinearColor(fromColor[key], toColor[key], v);
1331
+ }
1332
+ }
1333
+ blended.alpha = mix(fromColor.alpha, toColor.alpha, v);
1334
+ return fromColorType.transform(blended);
1335
+ };
1336
+ };
1337
+ const isNum = (v) => typeof v === "number";
1338
+ const combineFunctions = (a, b) => (v) => b(a(v));
1339
+ const pipe = (...transformers) => transformers.reduce(combineFunctions);
1340
+ function getMixer(origin, target) {
1341
+ if (isNum(origin)) {
1342
+ return (v) => mix(origin, target, v);
1343
+ } else if (color.test(origin)) {
1344
+ return mixColor(origin, target);
1345
+ } else {
1346
+ return mixComplex(origin, target);
1347
+ }
1348
+ }
1349
+ const mixArray = (from, to) => {
1350
+ const output = [...from];
1351
+ const numValues = output.length;
1352
+ const blendValue = from.map((fromThis, i) => getMixer(fromThis, to[i]));
1353
+ return (v) => {
1354
+ for (let i = 0; i < numValues; i++) {
1355
+ output[i] = blendValue[i](v);
1356
+ }
1357
+ return output;
1358
+ };
1359
+ };
1360
+ const mixObject = (origin, target) => {
1361
+ const output = Object.assign(Object.assign({}, origin), target);
1362
+ const blendValue = {};
1363
+ for (const key in output) {
1364
+ if (origin[key] !== void 0 && target[key] !== void 0) {
1365
+ blendValue[key] = getMixer(origin[key], target[key]);
1366
+ }
1367
+ }
1368
+ return (v) => {
1369
+ for (const key in blendValue) {
1370
+ output[key] = blendValue[key](v);
1371
+ }
1372
+ return output;
1373
+ };
1374
+ };
1375
+ function analyse(value) {
1376
+ const parsed = complex.parse(value);
1377
+ const numValues = parsed.length;
1378
+ let numNumbers = 0;
1379
+ let numRGB = 0;
1380
+ let numHSL = 0;
1381
+ for (let i = 0; i < numValues; i++) {
1382
+ if (numNumbers || typeof parsed[i] === "number") {
1383
+ numNumbers++;
1384
+ } else {
1385
+ if (parsed[i].hue !== void 0) {
1386
+ numHSL++;
1387
+ } else {
1388
+ numRGB++;
1389
+ }
1390
+ }
1391
+ }
1392
+ return { parsed, numNumbers, numRGB, numHSL };
1393
+ }
1394
+ const mixComplex = (origin, target) => {
1395
+ const template = complex.createTransformer(target);
1396
+ const originStats = analyse(origin);
1397
+ const targetStats = analyse(target);
1398
+ const canInterpolate = originStats.numHSL === targetStats.numHSL && originStats.numRGB === targetStats.numRGB && originStats.numNumbers >= targetStats.numNumbers;
1399
+ if (canInterpolate) {
1400
+ return pipe(mixArray(originStats.parsed, targetStats.parsed), template);
1401
+ } else {
1402
+ return (p2) => `${p2 > 0 ? target : origin}`;
1403
+ }
1404
+ };
1405
+ const mixNumber = (from, to) => (p2) => mix(from, to, p2);
1406
+ function detectMixerFactory(v) {
1407
+ if (typeof v === "number") {
1408
+ return mixNumber;
1409
+ } else if (typeof v === "string") {
1410
+ if (color.test(v)) {
1411
+ return mixColor;
1412
+ } else {
1413
+ return mixComplex;
1414
+ }
1415
+ } else if (Array.isArray(v)) {
1416
+ return mixArray;
1417
+ } else if (typeof v === "object") {
1418
+ return mixObject;
1419
+ }
1420
+ }
1421
+ function createMixers(output, ease, customMixer) {
1422
+ const mixers = [];
1423
+ const mixerFactory = customMixer || detectMixerFactory(output[0]);
1424
+ const numMixers = output.length - 1;
1425
+ for (let i = 0; i < numMixers; i++) {
1426
+ let mixer = mixerFactory(output[i], output[i + 1]);
1427
+ if (ease) {
1428
+ const easingFunction = Array.isArray(ease) ? ease[i] : ease;
1429
+ mixer = pipe(easingFunction, mixer);
1430
+ }
1431
+ mixers.push(mixer);
1432
+ }
1433
+ return mixers;
1434
+ }
1435
+ function fastInterpolate([from, to], [mixer]) {
1436
+ return (v) => mixer(progress(from, to, v));
1437
+ }
1438
+ function slowInterpolate(input, mixers) {
1439
+ const inputLength = input.length;
1440
+ const lastInputIndex = inputLength - 1;
1441
+ return (v) => {
1442
+ let mixerIndex = 0;
1443
+ let foundMixerIndex = false;
1444
+ if (v <= input[0]) {
1445
+ foundMixerIndex = true;
1446
+ } else if (v >= input[lastInputIndex]) {
1447
+ mixerIndex = lastInputIndex - 1;
1448
+ foundMixerIndex = true;
1449
+ }
1450
+ if (!foundMixerIndex) {
1451
+ let i = 1;
1452
+ for (; i < inputLength; i++) {
1453
+ if (input[i] > v || i === lastInputIndex) {
1454
+ break;
1455
+ }
1456
+ }
1457
+ mixerIndex = i - 1;
1458
+ }
1459
+ const progressInRange = progress(input[mixerIndex], input[mixerIndex + 1], v);
1460
+ return mixers[mixerIndex](progressInRange);
1461
+ };
1462
+ }
1463
+ function interpolate(input, output, { clamp: isClamp = true, ease, mixer } = {}) {
1464
+ const inputLength = input.length;
1465
+ invariant(inputLength === output.length);
1466
+ invariant(!ease || !Array.isArray(ease) || ease.length === inputLength - 1);
1467
+ if (input[0] > input[inputLength - 1]) {
1468
+ input = [].concat(input);
1469
+ output = [].concat(output);
1470
+ input.reverse();
1471
+ output.reverse();
1472
+ }
1473
+ const mixers = createMixers(output, ease, mixer);
1474
+ const interpolator = inputLength === 2 ? fastInterpolate(input, mixers) : slowInterpolate(input, mixers);
1475
+ return isClamp ? (v) => interpolator(clamp$1(input[0], input[inputLength - 1], v)) : interpolator;
1476
+ }
1477
+ class InterpolateColor {
1478
+ constructor({ valueRange, colorRange }) {
1479
+ __publicField(this, "mapper");
1480
+ this.mapper = interpolate(valueRange, colorRange);
1481
+ }
1482
+ getColor(value) {
1483
+ return this.mapper(value);
1484
+ }
1485
+ }
1486
+ function interpolateString(template, params = {}) {
1487
+ const extendedParams = __spreadProps(__spreadValues({}, params), {
1488
+ numbro
1489
+ });
1490
+ const names = Object.keys(extendedParams);
1491
+ const vals = Object.values(extendedParams);
1492
+ try {
1493
+ return new Function(...names, `return \`${template}\`;`)(...vals);
1494
+ } catch (error) {
1495
+ return error.message;
1496
+ }
1497
+ }
1498
+ function getColorByColorConf(conf, dataRow) {
1499
+ if (conf.type === "static") {
1500
+ return conf.staticColor;
1501
+ }
1502
+ if (conf.type === "continuous") {
1503
+ const mapper = new InterpolateColor(conf);
1504
+ const value = dataRow[conf.valueField];
1505
+ return mapper.getColor(value);
1506
+ }
1507
+ return "black";
1508
+ }
1509
+ function VizStats(_a) {
1510
+ var _b = _a, {
1511
+ conf: _c
1512
+ } = _b, _d = _c, {
1513
+ template,
1514
+ size,
1515
+ color: color2
1516
+ } = _d, rest = __objRest(_d, [
1517
+ "template",
1518
+ "size",
1519
+ "color"
1520
+ ]), {
1521
+ data
1522
+ } = _b;
1523
+ const finalColor = React.useMemo(() => {
1524
+ return getColorByColorConf(color2, data[0]);
1525
+ }, [color2, data]);
1526
+ return /* @__PURE__ */ jsx(Text, __spreadProps(__spreadValues({}, rest), {
1527
+ color: finalColor,
1528
+ sx: {
1529
+ fontSize: size
1530
+ },
1531
+ children: interpolateString(template, data[0])
1532
+ }));
1533
+ }
1097
1534
  function renderViz(width, height, data, viz) {
1098
1535
  const props = {
1099
1536
  width,
@@ -1110,6 +1547,8 @@ function renderViz(width, height, data, viz) {
1110
1547
  return /* @__PURE__ */ jsx(VizTable, __spreadValues({}, props));
1111
1548
  case "text":
1112
1549
  return /* @__PURE__ */ jsx(VizText, __spreadValues({}, props));
1550
+ case "stats":
1551
+ return /* @__PURE__ */ jsx(VizStats, __spreadValues({}, props));
1113
1552
  case "bar-3d":
1114
1553
  return /* @__PURE__ */ jsx(VizBar3D, __spreadValues({}, props));
1115
1554
  case "pie":
@@ -1134,7 +1573,8 @@ function Viz({
1134
1573
  className: "viz-root",
1135
1574
  ref,
1136
1575
  children: /* @__PURE__ */ jsx(LoadingOverlay, {
1137
- visible: loading
1576
+ visible: loading,
1577
+ exitTransitionDuration: 0
1138
1578
  })
1139
1579
  });
1140
1580
  }
@@ -1332,8 +1772,8 @@ function MantineColorSelector({
1332
1772
  }) {
1333
1773
  const theme = useMantineTheme();
1334
1774
  const themeColors = React.useMemo(() => {
1335
- return Object.entries(theme.colors).map(([color, profile]) => ({
1336
- label: color,
1775
+ return Object.entries(theme.colors).map(([color2, profile]) => ({
1776
+ label: color2,
1337
1777
  value: profile[6]
1338
1778
  }));
1339
1779
  }, [theme]);
@@ -1431,7 +1871,7 @@ function withDefaults(series) {
1431
1871
  y_axis_data_formatter = "",
1432
1872
  label_position = "top",
1433
1873
  stack = "1",
1434
- color = "black"
1874
+ color: color2 = "black"
1435
1875
  }) {
1436
1876
  return {
1437
1877
  type,
@@ -1441,7 +1881,7 @@ function withDefaults(series) {
1441
1881
  y_axis_data_formatter,
1442
1882
  label_position,
1443
1883
  stack,
1444
- color
1884
+ color: color2
1445
1885
  };
1446
1886
  }
1447
1887
  return series.map(setDefaults);
@@ -1684,6 +2124,374 @@ function VizPiePanel({
1684
2124
  })
1685
2125
  });
1686
2126
  }
2127
+ const marks = [{
2128
+ label: "initial",
2129
+ value: 0
2130
+ }, {
2131
+ label: "500",
2132
+ value: 25
2133
+ }, {
2134
+ label: "700",
2135
+ value: 50
2136
+ }, {
2137
+ label: "semibold",
2138
+ value: 75
2139
+ }, {
2140
+ label: "bold",
2141
+ value: 100
2142
+ }];
2143
+ function MantineFontWeightSlider({
2144
+ label,
2145
+ value,
2146
+ onChange
2147
+ }) {
2148
+ var _a, _b;
2149
+ const [mark, setMark] = React.useState((_b = (_a = marks.find((m2) => m2.label === value)) == null ? void 0 : _a.value) != null ? _b : marks[0].value);
2150
+ React.useEffect(() => {
2151
+ const match = marks.find((s) => s.value === mark);
2152
+ if (match) {
2153
+ onChange(match.label);
2154
+ }
2155
+ }, [mark]);
2156
+ return /* @__PURE__ */ jsxs(Group, {
2157
+ direction: "column",
2158
+ grow: true,
2159
+ spacing: "xs",
2160
+ mb: "lg",
2161
+ children: [/* @__PURE__ */ jsx(Text, {
2162
+ children: label
2163
+ }), /* @__PURE__ */ jsx(Slider, {
2164
+ label: null,
2165
+ marks,
2166
+ value: mark,
2167
+ onChange: setMark,
2168
+ step: 25,
2169
+ placeholder: "Pick a font size"
2170
+ })]
2171
+ });
2172
+ }
2173
+ function TextArrayInput({
2174
+ label,
2175
+ value,
2176
+ onChange
2177
+ }) {
2178
+ const [values, setValues] = React.useState(Array.isArray(value) ? [...value] : []);
2179
+ const add = React.useCallback(() => {
2180
+ setValues((s) => [...s, ""]);
2181
+ }, [setValues]);
2182
+ const del = React.useCallback((index2) => {
2183
+ setValues((s) => {
2184
+ s.splice(index2, 1);
2185
+ return [...s];
2186
+ });
2187
+ }, [setValues]);
2188
+ const changed = React.useMemo(() => {
2189
+ return !_.isEqual(values, value);
2190
+ }, [values, value]);
2191
+ const submit = () => {
2192
+ onChange(values.map((s) => s.toString()));
2193
+ };
2194
+ return /* @__PURE__ */ jsxs(Fragment, {
2195
+ children: [/* @__PURE__ */ jsxs(Group, {
2196
+ position: "left",
2197
+ children: [/* @__PURE__ */ jsx(Text, {
2198
+ children: label
2199
+ }), /* @__PURE__ */ jsx(ActionIcon, {
2200
+ mr: 5,
2201
+ variant: "filled",
2202
+ color: "blue",
2203
+ disabled: !changed,
2204
+ onClick: submit,
2205
+ children: /* @__PURE__ */ jsx(DeviceFloppy, {
2206
+ size: 20
2207
+ })
2208
+ })]
2209
+ }), /* @__PURE__ */ jsxs(Group, {
2210
+ children: [values.map((v, i) => /* @__PURE__ */ jsx(TextInput, {
2211
+ value: v,
2212
+ onChange: (event) => {
2213
+ const newValue = event.currentTarget.value;
2214
+ setValues((s) => {
2215
+ s.splice(i, 1, newValue);
2216
+ return [...s];
2217
+ });
2218
+ },
2219
+ rightSection: /* @__PURE__ */ jsx(ActionIcon, {
2220
+ onClick: () => del(i),
2221
+ color: "red",
2222
+ children: /* @__PURE__ */ jsx(Trash, {
2223
+ size: 14
2224
+ })
2225
+ }),
2226
+ sx: {
2227
+ width: "45%"
2228
+ }
2229
+ })), /* @__PURE__ */ jsx(ActionIcon, {
2230
+ onClick: add,
2231
+ color: "blue",
2232
+ variant: "outline",
2233
+ children: /* @__PURE__ */ jsx(PlaylistAdd, {
2234
+ size: 20
2235
+ })
2236
+ })]
2237
+ })]
2238
+ });
2239
+ }
2240
+ function ColorArrayInput({
2241
+ label,
2242
+ value,
2243
+ onChange
2244
+ }) {
2245
+ const [values, setValues] = React.useState(Array.isArray(value) ? [...value] : []);
2246
+ const add = React.useCallback(() => {
2247
+ setValues((s) => [...s, ""]);
2248
+ }, [setValues]);
2249
+ const del = React.useCallback((index2) => {
2250
+ setValues((s) => {
2251
+ s.splice(index2, 1);
2252
+ return [...s];
2253
+ });
2254
+ }, [setValues]);
2255
+ const changed = React.useMemo(() => {
2256
+ return !_.isEqual(values, value);
2257
+ }, [values, value]);
2258
+ const submit = () => {
2259
+ onChange(values.map((s) => s.toString()));
2260
+ };
2261
+ const theme = useMantineTheme();
2262
+ const swatches = React.useMemo(() => {
2263
+ return Object.entries(theme.colors).map(([_color, profile]) => profile[6]);
2264
+ }, [theme]);
2265
+ return /* @__PURE__ */ jsxs(Fragment, {
2266
+ children: [/* @__PURE__ */ jsxs(Group, {
2267
+ position: "left",
2268
+ children: [/* @__PURE__ */ jsx(Text, {
2269
+ children: label
2270
+ }), /* @__PURE__ */ jsx(ActionIcon, {
2271
+ mr: 5,
2272
+ variant: "filled",
2273
+ color: "blue",
2274
+ disabled: !changed,
2275
+ onClick: submit,
2276
+ children: /* @__PURE__ */ jsx(DeviceFloppy, {
2277
+ size: 20
2278
+ })
2279
+ })]
2280
+ }), /* @__PURE__ */ jsxs(Group, {
2281
+ children: [values.map((v, i) => /* @__PURE__ */ jsx(ColorInput, {
2282
+ value: v,
2283
+ onChange: (color2) => {
2284
+ setValues((s) => {
2285
+ s.splice(i, 1, color2);
2286
+ return [...s];
2287
+ });
2288
+ },
2289
+ swatches,
2290
+ rightSection: /* @__PURE__ */ jsx(ActionIcon, {
2291
+ onClick: () => del(i),
2292
+ color: "red",
2293
+ children: /* @__PURE__ */ jsx(Trash, {
2294
+ size: 14
2295
+ })
2296
+ }),
2297
+ sx: {
2298
+ width: "45%"
2299
+ }
2300
+ })), /* @__PURE__ */ jsx(ActionIcon, {
2301
+ onClick: add,
2302
+ color: "blue",
2303
+ variant: "outline",
2304
+ children: /* @__PURE__ */ jsx(PlaylistAdd, {
2305
+ size: 20
2306
+ })
2307
+ })]
2308
+ })]
2309
+ });
2310
+ }
2311
+ function VizStatsPanel({
2312
+ conf,
2313
+ setConf
2314
+ }) {
2315
+ const defaultValues = _.merge({}, {
2316
+ align: "center",
2317
+ size: "100px",
2318
+ template: "",
2319
+ weight: "bold",
2320
+ color: {
2321
+ type: "static",
2322
+ staticColor: "red"
2323
+ }
2324
+ }, conf);
2325
+ const {
2326
+ control,
2327
+ handleSubmit,
2328
+ watch,
2329
+ formState: {
2330
+ isDirty
2331
+ }
2332
+ } = useForm({
2333
+ defaultValues
2334
+ });
2335
+ const colorType = watch("color.type");
2336
+ watch("color.valueField");
2337
+ return /* @__PURE__ */ jsx(Group, {
2338
+ direction: "column",
2339
+ mt: "md",
2340
+ spacing: "xs",
2341
+ grow: true,
2342
+ noWrap: true,
2343
+ children: /* @__PURE__ */ jsxs("form", {
2344
+ onSubmit: handleSubmit(setConf),
2345
+ children: [/* @__PURE__ */ jsxs(Group, {
2346
+ position: "left",
2347
+ py: "md",
2348
+ pl: "md",
2349
+ sx: {
2350
+ borderBottom: "1px solid #eee",
2351
+ background: "#efefef"
2352
+ },
2353
+ children: [/* @__PURE__ */ jsx(Text, {
2354
+ weight: 500,
2355
+ children: "Stats Configurations"
2356
+ }), /* @__PURE__ */ jsx(ActionIcon, {
2357
+ type: "submit",
2358
+ mr: 5,
2359
+ variant: "filled",
2360
+ color: "blue",
2361
+ disabled: !isDirty,
2362
+ children: /* @__PURE__ */ jsx(DeviceFloppy, {
2363
+ size: 20
2364
+ })
2365
+ })]
2366
+ }), /* @__PURE__ */ jsxs(Accordion, {
2367
+ offsetIcon: false,
2368
+ multiple: true,
2369
+ initialState: {
2370
+ 0: true,
2371
+ 2: true
2372
+ },
2373
+ children: [/* @__PURE__ */ jsx(Accordion.Item, {
2374
+ label: "Content",
2375
+ children: /* @__PURE__ */ jsx(Group, {
2376
+ direction: "column",
2377
+ grow: true,
2378
+ children: /* @__PURE__ */ jsx(Controller, {
2379
+ name: "template",
2380
+ control,
2381
+ render: ({
2382
+ field
2383
+ }) => /* @__PURE__ */ jsx(TextInput, __spreadValues({
2384
+ placeholder: "Time: ${new Date().toISOString()}",
2385
+ label: "Content Template",
2386
+ required: true,
2387
+ sx: {
2388
+ flex: 1
2389
+ }
2390
+ }, field))
2391
+ })
2392
+ })
2393
+ }), /* @__PURE__ */ jsxs(Accordion.Item, {
2394
+ label: "Font",
2395
+ children: [/* @__PURE__ */ jsx(Group, {
2396
+ direction: "column",
2397
+ grow: true,
2398
+ children: /* @__PURE__ */ jsx(Controller, {
2399
+ name: "size",
2400
+ control,
2401
+ render: ({
2402
+ field
2403
+ }) => /* @__PURE__ */ jsx(TextInput, __spreadValues({
2404
+ label: "Font Size",
2405
+ placeholder: "10px, 1em, 1rem, 100%...",
2406
+ sx: {
2407
+ flex: 1
2408
+ }
2409
+ }, field))
2410
+ })
2411
+ }), /* @__PURE__ */ jsx(Group, {
2412
+ position: "apart",
2413
+ grow: true,
2414
+ sx: {
2415
+ "> *": {
2416
+ flexGrow: 1,
2417
+ maxWidth: "100%"
2418
+ }
2419
+ },
2420
+ children: /* @__PURE__ */ jsx(Controller, {
2421
+ name: "weight",
2422
+ control,
2423
+ render: ({
2424
+ field
2425
+ }) => /* @__PURE__ */ jsx(MantineFontWeightSlider, __spreadValues({
2426
+ label: "Font Weight"
2427
+ }, field))
2428
+ })
2429
+ })]
2430
+ }), /* @__PURE__ */ jsx(Accordion.Item, {
2431
+ label: "Color",
2432
+ children: /* @__PURE__ */ jsxs(Group, {
2433
+ direction: "column",
2434
+ grow: true,
2435
+ children: [/* @__PURE__ */ jsx(Controller, {
2436
+ name: "color.type",
2437
+ control,
2438
+ render: ({
2439
+ field
2440
+ }) => /* @__PURE__ */ jsx(Select, __spreadValues({
2441
+ label: "Color Type",
2442
+ data: [{
2443
+ label: "Static Color",
2444
+ value: "static"
2445
+ }, {
2446
+ label: "Continuous Color",
2447
+ value: "continuous"
2448
+ }]
2449
+ }, field))
2450
+ }), colorType === "static" && /* @__PURE__ */ jsx(Controller, {
2451
+ name: "color.staticColor",
2452
+ control,
2453
+ render: ({
2454
+ field
2455
+ }) => /* @__PURE__ */ jsx(MantineColorSelector, __spreadValues({}, field))
2456
+ }), colorType === "continuous" && /* @__PURE__ */ jsxs(Fragment, {
2457
+ children: [/* @__PURE__ */ jsx(Controller, {
2458
+ name: "color.valueField",
2459
+ control,
2460
+ defaultValue: "",
2461
+ render: ({
2462
+ field
2463
+ }) => /* @__PURE__ */ jsx(TextInput, __spreadValues({
2464
+ placeholder: "Calculate color with this field",
2465
+ label: "Value Field",
2466
+ required: true,
2467
+ sx: {
2468
+ flex: 1
2469
+ }
2470
+ }, field))
2471
+ }), /* @__PURE__ */ jsx(Controller, {
2472
+ name: "color.valueRange",
2473
+ control,
2474
+ render: ({
2475
+ field
2476
+ }) => /* @__PURE__ */ jsx(TextArrayInput, __spreadValues({
2477
+ label: "Value Range"
2478
+ }, field))
2479
+ }), /* @__PURE__ */ jsx(Controller, {
2480
+ name: "color.colorRange",
2481
+ control,
2482
+ render: ({
2483
+ field
2484
+ }) => /* @__PURE__ */ jsx(ColorArrayInput, __spreadValues({
2485
+ label: "Color Range"
2486
+ }, field))
2487
+ })]
2488
+ })]
2489
+ })
2490
+ })]
2491
+ })]
2492
+ })
2493
+ });
2494
+ }
1687
2495
  function SunburstPanel({
1688
2496
  conf: {
1689
2497
  label_field,
@@ -1768,22 +2576,22 @@ function ValueTypeSelector({
1768
2576
  sx
1769
2577
  });
1770
2578
  }
1771
- function VizTablePanel(_a) {
1772
- var _b = _a, {
1773
- conf: _c
1774
- } = _b, _d = _c, {
2579
+ function VizTablePanel(_e) {
2580
+ var _f = _e, {
2581
+ conf: _g
2582
+ } = _f, _h = _g, {
1775
2583
  columns
1776
- } = _d, restConf = __objRest(_d, [
2584
+ } = _h, restConf = __objRest(_h, [
1777
2585
  "columns"
1778
2586
  ]), {
1779
2587
  setConf
1780
- } = _b;
2588
+ } = _f;
1781
2589
  const form = useForm$1({
1782
2590
  initialValues: __spreadValues({
1783
2591
  id_field: "id",
1784
2592
  use_raw_columns: true,
1785
2593
  columns: formList(columns != null ? columns : []),
1786
- size: "sm",
2594
+ fontSize: "sm",
1787
2595
  horizontalSpacing: "sm",
1788
2596
  verticalSpacing: "sm",
1789
2597
  striped: false,
@@ -1874,7 +2682,7 @@ function VizTablePanel(_a) {
1874
2682
  sx: {
1875
2683
  flex: 1
1876
2684
  }
1877
- }, form.getInputProps("size")))
2685
+ }, form.getInputProps("fontSize")))
1878
2686
  }), /* @__PURE__ */ jsxs(Group, {
1879
2687
  direction: "column",
1880
2688
  grow: true,
@@ -1989,52 +2797,6 @@ function VizTablePanel(_a) {
1989
2797
  })
1990
2798
  });
1991
2799
  }
1992
- const marks = [{
1993
- label: "initial",
1994
- value: 0
1995
- }, {
1996
- label: "500",
1997
- value: 25
1998
- }, {
1999
- label: "700",
2000
- value: 50
2001
- }, {
2002
- label: "semibold",
2003
- value: 75
2004
- }, {
2005
- label: "bold",
2006
- value: 100
2007
- }];
2008
- function MantineFontWeightSlider({
2009
- label,
2010
- value,
2011
- onChange
2012
- }) {
2013
- var _a, _b;
2014
- const [mark, setMark] = React.useState((_b = (_a = marks.find((m2) => m2.label === value)) == null ? void 0 : _a.value) != null ? _b : marks[0].value);
2015
- React.useEffect(() => {
2016
- const match = marks.find((s) => s.value === mark);
2017
- if (match) {
2018
- onChange(match.label);
2019
- }
2020
- }, [mark]);
2021
- return /* @__PURE__ */ jsxs(Group, {
2022
- direction: "column",
2023
- grow: true,
2024
- spacing: "xs",
2025
- mb: "lg",
2026
- children: [/* @__PURE__ */ jsx(Text, {
2027
- children: label
2028
- }), /* @__PURE__ */ jsx(Slider, {
2029
- label: null,
2030
- marks,
2031
- value: mark,
2032
- onChange: setMark,
2033
- step: 25,
2034
- placeholder: "Pick a font size"
2035
- })]
2036
- });
2037
- }
2038
2800
  const sampleParagraphs = [{
2039
2801
  align: "center",
2040
2802
  size: "xl",
@@ -2174,6 +2936,10 @@ const types = [{
2174
2936
  value: "text",
2175
2937
  label: "Text",
2176
2938
  Panel: VizTextPanel
2939
+ }, {
2940
+ value: "stats",
2941
+ label: "Stats",
2942
+ Panel: VizStatsPanel
2177
2943
  }, {
2178
2944
  value: "table",
2179
2945
  label: "Table",
@@ -2323,7 +3089,8 @@ function PanelSettingsModal({
2323
3089
  children: [/* @__PURE__ */ jsxs(Tabs.Tab, {
2324
3090
  label: "Data Source",
2325
3091
  children: [/* @__PURE__ */ jsx(LoadingOverlay, {
2326
- visible: loading
3092
+ visible: loading,
3093
+ exitTransitionDuration: 0
2327
3094
  }), /* @__PURE__ */ jsx(PickDataSource, {})]
2328
3095
  }), /* @__PURE__ */ jsx(Tabs.Tab, {
2329
3096
  label: "Panel",
@@ -2561,10 +3328,15 @@ function ModeToggler({
2561
3328
  size: 20
2562
3329
  }), "Use"),
2563
3330
  value: DashboardMode.Use
3331
+ }, {
3332
+ label: renderLabel(/* @__PURE__ */ jsx(Resize, {
3333
+ size: 20
3334
+ }), "Layout"),
3335
+ value: DashboardMode.Layout
2564
3336
  }, {
2565
3337
  label: renderLabel(/* @__PURE__ */ jsx(Paint, {
2566
3338
  size: 20
2567
- }), "Edit"),
3339
+ }), "Content"),
2568
3340
  value: DashboardMode.Edit
2569
3341
  }]
2570
3342
  });
@@ -2773,7 +3545,8 @@ function DataSourceForm({
2773
3545
  });
2774
3546
  }
2775
3547
  function DataSourceEditor({
2776
- id
3548
+ id,
3549
+ setID
2777
3550
  }) {
2778
3551
  const {
2779
3552
  dataSources,
@@ -2784,7 +3557,7 @@ function DataSourceEditor({
2784
3557
  }, [dataSources, id]);
2785
3558
  const update = React.useCallback((value) => {
2786
3559
  const index2 = dataSources.findIndex((d) => d.id === id);
2787
- if (!index2) {
3560
+ if (index2 === -1) {
2788
3561
  console.error(new Error("Invalid data source id when updating by id"));
2789
3562
  return;
2790
3563
  }
@@ -2793,7 +3566,11 @@ function DataSourceEditor({
2793
3566
  prevs.splice(index22, 1, value);
2794
3567
  return [...prevs];
2795
3568
  });
2796
- }, [id, setDataSources]);
3569
+ setID(value.id);
3570
+ }, [id, dataSources, setDataSources, setID]);
3571
+ if (!id) {
3572
+ return null;
3573
+ }
2797
3574
  if (!dataSource) {
2798
3575
  return /* @__PURE__ */ jsx("span", {
2799
3576
  children: "Invalid Data Source ID"
@@ -2922,7 +3699,8 @@ function EditDataSourcesModal({
2922
3699
  setID
2923
3700
  }),
2924
3701
  children: [/* @__PURE__ */ jsx(DataSourceEditor, {
2925
- id
3702
+ id,
3703
+ setID
2926
3704
  }), /* @__PURE__ */ jsx(DataPreview, {
2927
3705
  id
2928
3706
  })]
@@ -3015,46 +3793,48 @@ WHERE \${author_time_condition}`;
3015
3793
  }) => {
3016
3794
  setSQLSnippets(snippets);
3017
3795
  };
3018
- return /* @__PURE__ */ jsxs(Group, {
3796
+ return /* @__PURE__ */ jsx(Group, {
3019
3797
  direction: "column",
3020
3798
  grow: true,
3021
3799
  sx: {
3022
3800
  border: "1px solid #eee"
3023
3801
  },
3024
- children: [/* @__PURE__ */ jsxs(Group, {
3025
- position: "left",
3026
- pl: "md",
3027
- py: "md",
3028
- sx: {
3029
- borderBottom: "1px solid #eee",
3030
- background: "#efefef",
3031
- flexGrow: 0
3032
- },
3033
- children: [/* @__PURE__ */ jsx(Text, {
3034
- weight: 500,
3035
- children: "SQL Snippets"
3036
- }), /* @__PURE__ */ jsx(ActionIcon, {
3037
- type: "submit",
3038
- mr: 5,
3039
- variant: "filled",
3040
- color: "blue",
3041
- disabled: !changed,
3042
- children: /* @__PURE__ */ jsx(DeviceFloppy, {
3043
- size: 20
3044
- })
3045
- })]
3046
- }), /* @__PURE__ */ jsxs(Group, {
3047
- px: "md",
3048
- pb: "md",
3049
- children: [/* @__PURE__ */ jsx(Prism, {
3050
- language: "sql",
3802
+ children: /* @__PURE__ */ jsxs("form", {
3803
+ onSubmit: form.onSubmit(submit),
3804
+ children: [/* @__PURE__ */ jsxs(Group, {
3805
+ position: "left",
3806
+ pl: "md",
3807
+ py: "md",
3051
3808
  sx: {
3052
- width: "100%"
3809
+ borderBottom: "1px solid #eee",
3810
+ background: "#efefef",
3811
+ flexGrow: 0
3053
3812
  },
3054
- noCopy: true,
3055
- trim: false,
3056
- colorScheme: "dark",
3057
- children: `-- You may refer context data *by name*
3813
+ children: [/* @__PURE__ */ jsx(Text, {
3814
+ weight: 500,
3815
+ children: "SQL Snippets"
3816
+ }), /* @__PURE__ */ jsx(ActionIcon, {
3817
+ type: "submit",
3818
+ mr: 5,
3819
+ variant: "filled",
3820
+ color: "blue",
3821
+ disabled: !changed,
3822
+ children: /* @__PURE__ */ jsx(DeviceFloppy, {
3823
+ size: 20
3824
+ })
3825
+ })]
3826
+ }), /* @__PURE__ */ jsxs(Group, {
3827
+ px: "md",
3828
+ pb: "md",
3829
+ children: [/* @__PURE__ */ jsx(Prism, {
3830
+ language: "sql",
3831
+ sx: {
3832
+ width: "100%"
3833
+ },
3834
+ noCopy: true,
3835
+ trim: false,
3836
+ colorScheme: "dark",
3837
+ children: `-- You may refer context data *by name*
3058
3838
  -- in SQL or VizConfig.
3059
3839
 
3060
3840
  ${sampleSQL}
@@ -3062,15 +3842,13 @@ ${sampleSQL}
3062
3842
  -- where author_time_condition is:
3063
3843
  author_time BETWEEN '\${timeRange?.[0].toISOString()}' AND '\${timeRange?.[1].toISOString()}'
3064
3844
  `
3065
- }), /* @__PURE__ */ jsx(Group, {
3066
- direction: "column",
3067
- sx: {
3068
- width: "100%",
3069
- position: "relative"
3070
- },
3071
- grow: true,
3072
- children: /* @__PURE__ */ jsxs("form", {
3073
- onSubmit: form.onSubmit(submit),
3845
+ }), /* @__PURE__ */ jsxs(Group, {
3846
+ direction: "column",
3847
+ sx: {
3848
+ width: "100%",
3849
+ position: "relative"
3850
+ },
3851
+ grow: true,
3074
3852
  children: [form.values.snippets.map((_item, index2) => /* @__PURE__ */ jsxs(Group, {
3075
3853
  direction: "column",
3076
3854
  grow: true,
@@ -3115,9 +3893,9 @@ author_time BETWEEN '\${timeRange?.[0].toISOString()}' AND '\${timeRange?.[1].to
3115
3893
  children: "Add a snippet"
3116
3894
  })
3117
3895
  })]
3118
- })
3896
+ })]
3119
3897
  })]
3120
- })]
3898
+ })
3121
3899
  });
3122
3900
  }
3123
3901
  function EditSQLSnippetsModal({
@@ -3173,13 +3951,17 @@ function DashboardActions({
3173
3951
  addPanel,
3174
3952
  saveChanges
3175
3953
  }) {
3954
+ const {
3955
+ inLayoutMode,
3956
+ inEditMode,
3957
+ inUseMode
3958
+ } = React.useContext(LayoutStateContext);
3176
3959
  const [dataSourcesOpened, setDataSourcesOpened] = React.useState(false);
3177
3960
  const openDataSources = () => setDataSourcesOpened(true);
3178
3961
  const closeDataSources = () => setDataSourcesOpened(false);
3179
3962
  const [sqlSnippetsOpened, setSQLSnippetsOpened] = React.useState(false);
3180
3963
  const openSQLSnippets = () => setSQLSnippetsOpened(true);
3181
3964
  const closeSQLSnippets = () => setSQLSnippetsOpened(false);
3182
- const inEditMode = mode === DashboardMode.Edit;
3183
3965
  return /* @__PURE__ */ jsxs(Group, {
3184
3966
  position: "apart",
3185
3967
  pt: "sm",
@@ -3190,59 +3972,57 @@ function DashboardActions({
3190
3972
  mode,
3191
3973
  setMode
3192
3974
  })
3193
- }), inEditMode && /* @__PURE__ */ jsxs(Fragment, {
3194
- children: [/* @__PURE__ */ jsxs(Group, {
3195
- position: "right",
3196
- children: [/* @__PURE__ */ jsx(Button, {
3197
- variant: "default",
3198
- size: "sm",
3199
- onClick: addPanel,
3200
- leftIcon: /* @__PURE__ */ jsx(PlaylistAdd, {
3201
- size: 20
3202
- }),
3203
- children: "Add a Panel"
3204
- }), /* @__PURE__ */ jsx(Button, {
3205
- variant: "default",
3206
- size: "sm",
3207
- onClick: openSQLSnippets,
3208
- leftIcon: /* @__PURE__ */ jsx(ClipboardText, {
3209
- size: 20
3210
- }),
3211
- children: "SQL Snippets"
3212
- }), /* @__PURE__ */ jsx(Button, {
3213
- variant: "default",
3214
- size: "sm",
3215
- onClick: openDataSources,
3216
- leftIcon: /* @__PURE__ */ jsx(Database, {
3217
- size: 20
3218
- }),
3219
- children: "Data Sources"
3220
- }), /* @__PURE__ */ jsx(Button, {
3221
- variant: "default",
3222
- size: "sm",
3223
- onClick: saveChanges,
3224
- disabled: !hasChanges,
3225
- leftIcon: /* @__PURE__ */ jsx(DeviceFloppy, {
3226
- size: 20
3227
- }),
3228
- children: "Save Changes"
3229
- }), /* @__PURE__ */ jsx(Button, {
3230
- color: "red",
3231
- size: "sm",
3232
- disabled: !hasChanges,
3233
- leftIcon: /* @__PURE__ */ jsx(Recycle, {
3234
- size: 20
3235
- }),
3236
- children: "Revert Changes"
3237
- })]
3238
- }), /* @__PURE__ */ jsx(EditDataSourcesModal, {
3239
- opened: dataSourcesOpened,
3240
- close: closeDataSources
3241
- }), /* @__PURE__ */ jsx(EditSQLSnippetsModal, {
3242
- opened: sqlSnippetsOpened,
3243
- close: closeSQLSnippets
3975
+ }), /* @__PURE__ */ jsxs(Group, {
3976
+ position: "right",
3977
+ children: [inLayoutMode && /* @__PURE__ */ jsx(Button, {
3978
+ variant: "default",
3979
+ size: "sm",
3980
+ onClick: addPanel,
3981
+ leftIcon: /* @__PURE__ */ jsx(PlaylistAdd, {
3982
+ size: 20
3983
+ }),
3984
+ children: "Add a Panel"
3985
+ }), inEditMode && /* @__PURE__ */ jsx(Button, {
3986
+ variant: "default",
3987
+ size: "sm",
3988
+ onClick: openSQLSnippets,
3989
+ leftIcon: /* @__PURE__ */ jsx(ClipboardText, {
3990
+ size: 20
3991
+ }),
3992
+ children: "SQL Snippets"
3993
+ }), inEditMode && /* @__PURE__ */ jsx(Button, {
3994
+ variant: "default",
3995
+ size: "sm",
3996
+ onClick: openDataSources,
3997
+ leftIcon: /* @__PURE__ */ jsx(Database, {
3998
+ size: 20
3999
+ }),
4000
+ children: "Data Sources"
4001
+ }), !inUseMode && /* @__PURE__ */ jsx(Button, {
4002
+ variant: "default",
4003
+ size: "sm",
4004
+ onClick: saveChanges,
4005
+ disabled: !hasChanges,
4006
+ leftIcon: /* @__PURE__ */ jsx(DeviceFloppy, {
4007
+ size: 20
4008
+ }),
4009
+ children: "Save Changes"
4010
+ }), !inUseMode && /* @__PURE__ */ jsx(Button, {
4011
+ color: "red",
4012
+ size: "sm",
4013
+ disabled: !hasChanges,
4014
+ leftIcon: /* @__PURE__ */ jsx(Recycle, {
4015
+ size: 20
4016
+ }),
4017
+ children: "Revert Changes"
3244
4018
  })]
3245
- }), !inEditMode && /* @__PURE__ */ jsx(Button, {
4019
+ }), /* @__PURE__ */ jsx(EditDataSourcesModal, {
4020
+ opened: dataSourcesOpened,
4021
+ close: closeDataSources
4022
+ }), /* @__PURE__ */ jsx(EditSQLSnippetsModal, {
4023
+ opened: sqlSnippetsOpened,
4024
+ close: closeSQLSnippets
4025
+ }), inUseMode && /* @__PURE__ */ jsx(Button, {
3246
4026
  variant: "default",
3247
4027
  size: "sm",
3248
4028
  disabled: true,
@@ -3316,6 +4096,8 @@ function Dashboard({
3316
4096
  });
3317
4097
  };
3318
4098
  const inEditMode = mode === DashboardMode.Edit;
4099
+ const inLayoutMode = mode === DashboardMode.Layout;
4100
+ const inUseMode = mode === DashboardMode.Use;
3319
4101
  const definitions = React.useMemo(() => ({
3320
4102
  sqlSnippets,
3321
4103
  setSQLSnippets,
@@ -3333,7 +4115,9 @@ function Dashboard({
3333
4115
  layoutFrozen,
3334
4116
  freezeLayout,
3335
4117
  mode,
3336
- inEditMode
4118
+ inEditMode,
4119
+ inLayoutMode,
4120
+ inUseMode
3337
4121
  },
3338
4122
  children: [/* @__PURE__ */ jsx(DashboardActions, {
3339
4123
  mode,
@@ -3344,8 +4128,8 @@ function Dashboard({
3344
4128
  }), /* @__PURE__ */ jsx(DashboardLayout, {
3345
4129
  panels,
3346
4130
  setPanels,
3347
- isDraggable: inEditMode && !layoutFrozen,
3348
- isResizable: inEditMode && !layoutFrozen,
4131
+ isDraggable: inLayoutMode,
4132
+ isResizable: inLayoutMode,
3349
4133
  onRemoveItem: removePanelByID,
3350
4134
  setLocalCols,
3351
4135
  setBreakpoint
@@ -3412,7 +4196,9 @@ function ReadOnlyDashboard({
3412
4196
  freezeLayout: () => {
3413
4197
  },
3414
4198
  mode: DashboardMode.Use,
3415
- inEditMode: false
4199
+ inEditMode: false,
4200
+ inLayoutMode: false,
4201
+ inUseMode: true
3416
4202
  },
3417
4203
  children: /* @__PURE__ */ jsx(ReadOnlyDashboardLayout, {
3418
4204
  panels: dashboard.panels