@datawheel/bespoke 0.1.30 → 0.1.32

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.
Files changed (3) hide show
  1. package/dist/index.js +409 -219
  2. package/dist/server.js +99 -72
  3. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import axios from 'axios';
2
- import yn3 from 'yn';
2
+ import yn4 from 'yn';
3
3
  import * as d3Array from 'd3-array';
4
4
  import * as d3Collection from 'd3-collection';
5
5
  import * as d3Format from 'd3-format';
@@ -16,11 +16,11 @@ import { Notifications, notifications } from '@mantine/notifications';
16
16
  import React, { forwardRef, useMemo, useState, useCallback, useContext, useEffect, createContext, useRef, Fragment as Fragment$1, createElement } from 'react';
17
17
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
18
18
  import { useDispatch, useSelector } from 'react-redux';
19
- import { Stack, Text, Badge, Group, useMantineTheme, Flex, packSx, Tooltip, ActionIcon, Center, Modal, Button, SegmentedControl, Select, MultiSelect, Title, Box, List, Menu, Anchor, MantineProvider, Divider, Burger, Navbar, ScrollArea, Avatar, AppShell, UnstyledButton, ThemeIcon, LoadingOverlay, Skeleton, Container, TextInput, Loader, Alert, Collapse, Card, Space, Code, Textarea, rem, Paper, Grid, Input, Popover, Checkbox, Radio, Switch, Drawer, Overlay, SimpleGrid, Autocomplete, Tabs, Header, px, Image, FileInput, Accordion, HoverCard, CopyButton, NumberInput, Col } from '@mantine/core';
19
+ import { Stack, Text, Badge, Group, useMantineTheme, Flex, packSx, Tooltip, ActionIcon, Center, Modal, Button, SegmentedControl, Select, MultiSelect, Title, TextInput, Switch, Box, List, Menu, Anchor, MantineProvider, Divider, Burger, Navbar, ScrollArea, Avatar, AppShell, UnstyledButton, ThemeIcon, LoadingOverlay, Skeleton, Container, Loader, Alert, Collapse, Card, Space, Code, Textarea, rem, Paper, Grid, Input, Popover, Checkbox, Radio, Drawer, Overlay, SimpleGrid, Autocomplete, Tabs, Header, px, Image, FileInput, Accordion, HoverCard, CopyButton, NumberInput, Col } from '@mantine/core';
20
20
  import { dataConcat } from 'd3plus-viz';
21
21
  import * as d3plus from 'd3plus-react';
22
22
  import Router, { useRouter } from 'next/router';
23
- import { IconInfoCircle, IconSearch, IconAlignLeft, IconAlignCenter, IconAlignRight, IconBoxMargin, IconTable, IconMathFunction, IconUsers, IconLogout, IconTrash, IconUserCircle, IconEdit, IconDatabase, IconServer, IconPencil, IconAlertCircle, IconCircleCheck, IconPlayerPlay, IconAlarmFilled, IconBox, IconLink, IconCircleX, IconFlag, IconCirclePlus, IconFileAnalytics, IconPlus, IconX, IconChevronDown, IconCamera, IconShare, IconCircleDashed, IconListSearch, IconExternalLink, IconSettings, IconFileOff, IconFilesOff, IconHierarchy3, IconMenu, IconRefresh, IconPolaroid, IconCircleMinus, IconEyeOff, IconChevronLeft, IconChevronRight, IconLogin, IconWorld, IconLock, IconVariable, IconArrowRightCircle, IconDownload, IconTemplate, IconChartBar, IconCode, IconUpload, IconCodePlus, IconClipboardCheck, IconClipboardCopy, IconPalette, IconEye, IconMinimize, IconMaximize, IconRss, IconGlobe } from '@tabler/icons-react';
23
+ import { IconInfoCircle, IconRefresh, IconSearch, IconAlignLeft, IconAlignCenter, IconAlignRight, IconBoxMargin, IconTable, IconMathFunction, IconUsers, IconLogout, IconTrash, IconUserCircle, IconEdit, IconDatabase, IconServer, IconPencil, IconAlertCircle, IconCircleCheck, IconPlayerPlay, IconAlarmFilled, IconBox, IconLink, IconCircleX, IconFlag, IconCirclePlus, IconFileAnalytics, IconPlus, IconX, IconChevronDown, IconCamera, IconShare, IconCircleDashed, IconListSearch, IconExternalLink, IconSettings, IconFileOff, IconFilesOff, IconHierarchy3, IconMenu, IconApi, IconPolaroid, IconCircleMinus, IconEyeOff, IconChevronLeft, IconChevronRight, IconLogin, IconWorld, IconLock, IconVariable, IconArrowRightCircle, IconDownload, IconTemplate, IconChartBar, IconCode, IconUpload, IconCodePlus, IconClipboardCheck, IconClipboardCopy, IconPalette, IconEye, IconMinimize, IconMaximize, IconRss, IconGlobe } from '@tabler/icons-react';
24
24
  import { useMediaQuery, useDisclosure, useDebouncedValue, useHotkeys, useFullscreen, getHotkeyHandler } from '@mantine/hooks';
25
25
  import dynamic from 'next/dynamic';
26
26
  import Link from 'next/link';
@@ -354,8 +354,9 @@ var init_cms = __esm({
354
354
  STAT: "stat",
355
355
  SUBTITLE: "subtitle",
356
356
  TITLE: "title",
357
- NAV: "nav"
357
+ NAV: "nav",
358
358
  // todo1.0, how to put custom blocks in here?
359
+ RESET_BUTTON: "reset_button"
359
360
  };
360
361
  BLOCK_LOGIC_TYPES = {
361
362
  GENERATOR: "generator",
@@ -472,7 +473,7 @@ var getLogging_default;
472
473
  var init_getLogging = __esm({
473
474
  "libs/configs/getLogging.ts"() {
474
475
  init_esm_shims();
475
- getLogging_default = (env = process.env) => yn3(env.REPORTS_LOGGING);
476
+ getLogging_default = (env = process.env) => yn4(env.REPORTS_LOGGING);
476
477
  }
477
478
  });
478
479
 
@@ -740,7 +741,7 @@ var init_mortarEval = __esm({
740
741
  init_esm_shims();
741
742
  init_block();
742
743
  init_libs();
743
- verbose = yn3(process.env.REPORTS_LOGGING);
744
+ verbose = yn4(process.env.REPORTS_LOGGING);
744
745
  mortarEval_default = mortarEval;
745
746
  }
746
747
  });
@@ -969,7 +970,7 @@ function getRootBlocksForSection(sid, blocks) {
969
970
  blockList.filter((block) => block.section_id === sid && // leave blocks belonging to this section
970
971
  // that don't depend on any other blocks OR
971
972
  (block.inputs.length === 0 || // that depend only on blocks from other sections
972
- block.inputs.every((id) => nonNativeBlocks.includes(id)))).map((block) => [block.id, block])
973
+ block.inputs.some((id) => nonNativeBlocks.includes(id)))).map((block) => [block.id, block])
973
974
  // then convert them to entries
974
975
  );
975
976
  }
@@ -1095,14 +1096,7 @@ async function runSingleBlock(block, formatterFunctions, blockContext, readMembe
1095
1096
  return {
1096
1097
  outputVariables: {},
1097
1098
  renderVariables: {},
1098
- status: {
1099
- api: null,
1100
- duration: null,
1101
- resp: null,
1102
- log: [],
1103
- error: null,
1104
- allowed: false
1105
- }
1099
+ status: { allowed: false }
1106
1100
  };
1107
1101
  }
1108
1102
  if (unswappedAPI) {
@@ -1133,9 +1127,6 @@ async function runSingleBlock(block, formatterFunctions, blockContext, readMembe
1133
1127
  outputVariables: selectorQueryToVariable_default(block.id, query, config),
1134
1128
  renderVariables: config,
1135
1129
  status: {
1136
- api: null,
1137
- duration: null,
1138
- resp: null,
1139
1130
  log: log2,
1140
1131
  error: error2,
1141
1132
  allowed
@@ -1148,11 +1139,8 @@ async function runSingleBlock(block, formatterFunctions, blockContext, readMembe
1148
1139
  // todo1.0 this will have to be materialized click-variables
1149
1140
  renderVariables: block.contentByLocale.logic.content,
1150
1141
  status: {
1151
- api: null,
1152
- duration: null,
1153
- resp: null,
1154
1142
  log: [],
1155
- error: null,
1143
+ // todo: see if we can remove this safely
1156
1144
  allowed
1157
1145
  }
1158
1146
  };
@@ -1189,7 +1177,7 @@ async function runSingleBlock(block, formatterFunctions, blockContext, readMembe
1189
1177
  }
1190
1178
  };
1191
1179
  }
1192
- var verbose2, ORIGIN, swapAPI, urlProxy, getDependencies, isBlockAllowed, runConsumersV2, getDurationColor, getSizeColor;
1180
+ var verbose2, debug, ORIGIN, swapAPI, urlProxy, getDependencies, isBlockAllowed, runConsumersV2, getDurationColor, getSizeColor;
1193
1181
  var init_runConsumers = __esm({
1194
1182
  "libs/blocks/runConsumers.ts"() {
1195
1183
  init_esm_shims();
@@ -1205,6 +1193,7 @@ var init_runConsumers = __esm({
1205
1193
  init_getRootBlocksForSection();
1206
1194
  init_arrayUtils();
1207
1195
  verbose2 = getLogging_default();
1196
+ debug = yn4(process.env.NEXT_PUBLIC_REPORTS_DEBUG);
1208
1197
  ORIGIN = process.env.REPORTS_ORIGIN || "";
1209
1198
  axios.interceptors.request.use((config) => ({
1210
1199
  ...config,
@@ -1224,10 +1213,7 @@ var init_runConsumers = __esm({
1224
1213
  getDependencies = (bid, blocks, acc = [], crawlUp = true, crawlDown = true, withinSection = true, visited = []) => {
1225
1214
  if (visited.includes(bid))
1226
1215
  return [];
1227
- if (crawlDown)
1228
- visited.push(bid);
1229
- if (verbose2)
1230
- console.log("%cpassing through block", "color:green", bid);
1216
+ visited.push(bid);
1231
1217
  const rootBlock = blocks[bid];
1232
1218
  if (rootBlock.inputs.length && crawlUp) {
1233
1219
  rootBlock.inputs.forEach((iid) => {
@@ -1244,7 +1230,7 @@ var init_runConsumers = __esm({
1244
1230
  if (!acc.includes(rel) && (blocks[cid].section_id === blocks[bid].section_id || !withinSection)) {
1245
1231
  acc.push(rel);
1246
1232
  }
1247
- rootBlock.consumers.filter((cid2) => blocks[cid2].section_id === blocks[bid].section_id || !withinSection).forEach((cid2) => getDependencies(cid2, blocks, acc, crawlUp, crawlDown, withinSection, visited));
1233
+ rootBlock.consumers.filter((cid2) => blocks[cid2].section_id === blocks[bid].section_id || !withinSection).forEach((cid2) => getDependencies(cid2, blocks, acc, false, crawlDown, withinSection, visited));
1248
1234
  });
1249
1235
  }
1250
1236
  return acc;
@@ -1277,22 +1263,31 @@ var init_runConsumers = __esm({
1277
1263
  const statusById = { ...initialState4.status ?? {} };
1278
1264
  const parsedBlockContext = parseBlockContext(blockContext);
1279
1265
  const attributes = parsedBlockContext.variables;
1280
- const rootBlocks = bid ? { [bid]: blocks[bid] } : sections.reduce((rootBlocks2, { id }) => ({ ...rootBlocks2, ...getRootBlocksForSection_default(id, blocks) }), {});
1281
- if (verbose2)
1282
- console.log("%crunConsumers: running root blocks", "color:blue", Object.keys(rootBlocks));
1266
+ const rootBlocks = bid ? { [bid]: blocks[bid] } : (sections ?? []).reduce((rootBlocks2, { id }) => ({ ...rootBlocks2, ...getRootBlocksForSection_default(id, blocks) }), {});
1267
+ if (debug)
1268
+ console.log("%crunConsumers: running root blocks", "color:purple", Object.keys(rootBlocks));
1283
1269
  const withinSection = mode === "section";
1284
1270
  const blockDeps = Object.keys(rootBlocks).reduce((deps, id) => {
1285
- if (verbose2)
1286
- console.log("%cresolving dependencies for", "color:red", id);
1271
+ const start = /* @__PURE__ */ new Date();
1287
1272
  const dependencies = getDependencies(Number(id), blocks, [], !bid, true, withinSection).map((rel) => rel.split("-"));
1273
+ const end = /* @__PURE__ */ new Date();
1274
+ const elapsedTime = (end.getTime() - start.getTime()) / 1e3;
1275
+ if (debug)
1276
+ console.log(
1277
+ `%cResolved dependencies for ${id} (${elapsedTime.toFixed(2)}s)`,
1278
+ "color:blue"
1279
+ );
1288
1280
  return [...deps, ...dependencies];
1289
1281
  }, []);
1282
+ const startToposort = /* @__PURE__ */ new Date();
1290
1283
  const orderedDAG = Object.keys(rootBlocks).reduce(
1291
1284
  (orderedDAG2, bid2) => orderedDAG2.includes(bid2) ? [...orderedDAG2] : [bid2, ...orderedDAG2],
1292
1285
  toposort(blockDeps)
1293
1286
  );
1294
- if (verbose2)
1295
- console.log("resolved DAG: ", orderedDAG);
1287
+ const endToposort = /* @__PURE__ */ new Date();
1288
+ const toposortTime = (endToposort.getTime() - startToposort.getTime()) / 1e3;
1289
+ if (debug)
1290
+ console.log(`%cResolved DAG (${toposortTime.toFixed(2)}s): `, "color:green", orderedDAG);
1296
1291
  async function runTasksInParallel(executionOrder, blocks2) {
1297
1292
  const runningBlocks = /* @__PURE__ */ new Map();
1298
1293
  for (const bid2 of executionOrder) {
@@ -1300,22 +1295,20 @@ var init_runConsumers = __esm({
1300
1295
  const dependenciesDone = Promise.all(
1301
1296
  block.inputs.filter((iid) => executionOrder.includes(String(iid))).map((iid) => runningBlocks.get(Number(iid)))
1302
1297
  );
1303
- const runningTask = dependenciesDone.then(() => {
1298
+ const runningTask = dependenciesDone.then(async () => {
1304
1299
  const variables = block.inputs.reduce((acc, d) => ({ ...acc, ...variablesById[d] }), attributes);
1305
- const inputNotAllowed = block.inputs.find((iid) => !statusById[iid].allowed || statusById[iid].hiddenByCascade);
1300
+ const inputNotAllowed = block.inputs.find(
1301
+ (iid) => !statusById[iid].allowed || statusById[iid].hiddenByCascade
1302
+ );
1306
1303
  if (inputNotAllowed) {
1307
1304
  statusById[bid2] = {
1308
- api: null,
1309
- duration: null,
1310
- resp: null,
1311
- log: [],
1312
- error: null,
1313
1305
  allowed: false,
1314
1306
  hiddenByCascade: statusById[inputNotAllowed].hiddenByCascade || inputNotAllowed
1315
1307
  };
1316
1308
  return;
1317
1309
  }
1318
- return runSingleBlock(
1310
+ const startBlock = /* @__PURE__ */ new Date();
1311
+ const blockResult = await runSingleBlock(
1319
1312
  block,
1320
1313
  formatterFunctions,
1321
1314
  {
@@ -1324,15 +1317,32 @@ var init_runConsumers = __esm({
1324
1317
  },
1325
1318
  readMemberFn
1326
1319
  ).then(({ outputVariables, status }) => {
1327
- variablesById[bid2] = outputVariables;
1328
- statusById[bid2] = status;
1320
+ if (
1321
+ // store output variables for block that:
1322
+ block.consumers.length > 0 && status.allowed && Object.keys(outputVariables).length > 0
1323
+ )
1324
+ variablesById[bid2] = outputVariables;
1325
+ statusById[bid2] = mode === "report" ? { allowed: status.allowed } : status;
1329
1326
  });
1327
+ const endBlock = /* @__PURE__ */ new Date();
1328
+ const blockTime = (endBlock.getTime() - startBlock.getTime()) / 1e3;
1329
+ if (debug)
1330
+ console.log(
1331
+ `%cBlock ${block.id} resolved in ${blockTime.toFixed(3)}s`,
1332
+ blockTime > 500 ? "color:red" : "color:green"
1333
+ );
1334
+ return blockResult;
1330
1335
  });
1331
1336
  runningBlocks.set(Number(bid2), runningTask);
1332
1337
  }
1333
1338
  await Promise.all(Array.from(runningBlocks.values()));
1334
1339
  }
1340
+ const startVariables = /* @__PURE__ */ new Date();
1335
1341
  await runTasksInParallel(orderedDAG, blocks);
1342
+ const endVariables = /* @__PURE__ */ new Date();
1343
+ const variablesTime = (endVariables.getTime() - startVariables.getTime()) / 1e3;
1344
+ if (debug)
1345
+ console.log(`%cResolved Variables (${variablesTime.toFixed(2)}s): `, "color:green");
1336
1346
  return {
1337
1347
  variables: { ...initialState4.variables ?? {}, ...variablesById },
1338
1348
  status: { ...initialState4.status ?? {}, ...statusById }
@@ -1866,15 +1876,12 @@ var init_recordsSlice = __esm({
1866
1876
  */
1867
1877
  setReadRequest(state, action) {
1868
1878
  const req = action.payload;
1869
- state.requests[req.type][req.key] = req;
1879
+ if (req.type !== "report")
1880
+ state.requests[req.type][req.key] = req;
1870
1881
  if (req.status === "SUCCESS" /* SUCCESS */) {
1871
1882
  const newEntities = normalizeEntity(req.type, req.data);
1872
1883
  const nextEntities = combineRecords(state.entities, newEntities);
1873
1884
  state.entities = nextEntities;
1874
- if (req.type === "report") {
1875
- state.reports = Object.values(nextEntities.report).map((item) => item.id);
1876
- state.tree = req.data;
1877
- }
1878
1885
  }
1879
1886
  },
1880
1887
  /**
@@ -2900,6 +2907,7 @@ function Viz(config) {
2900
2907
  const blockContext = { variables };
2901
2908
  const router = useRouter();
2902
2909
  const { sectionVariables, setSectionVariables, resetSectionVariables } = useSectionVariables(block.section_id);
2910
+ const sectionVariablesStr = JSON.stringify(sectionVariables);
2903
2911
  const vizProps = useMemo(() => {
2904
2912
  if (!content?.logic)
2905
2913
  return { error: "Add a Configuration" };
@@ -2913,7 +2921,7 @@ function Viz(config) {
2913
2921
  }
2914
2922
  };
2915
2923
  return d3plusPropify_default(transpiledLogic, formatterFunctions, variables, locale, block.id, {}, globals);
2916
- }, [block, active, variables, sectionVariables]);
2924
+ }, [block, active, variables, sectionVariablesStr]);
2917
2925
  const { type } = vizProps.config || {};
2918
2926
  const fallbackType = type && vizTypes[type] ? type : "Treemap";
2919
2927
  if (!vizTypes[type])
@@ -2947,7 +2955,7 @@ function Viz(config) {
2947
2955
  linksFormat: vizProps.linksFormat,
2948
2956
  nodesFormat: vizProps.nodesFormat,
2949
2957
  topojsonFormat: vizProps.topojsonFormat,
2950
- config: { ...defaultConfig_default, ...vizConfig }
2958
+ config: { ...defaultConfig_default, ...vizConfig, sectionVariables: sectionVariablesStr }
2951
2959
  },
2952
2960
  "viz-key"
2953
2961
  ) })
@@ -3113,7 +3121,7 @@ function Viz2({
3113
3121
  active,
3114
3122
  locale,
3115
3123
  variables,
3116
- debug,
3124
+ debug: debug2,
3117
3125
  configOverride = {}
3118
3126
  }) {
3119
3127
  const context = useAppContext();
@@ -3483,6 +3491,7 @@ function BespokeExplore({
3483
3491
  const [metadata, setMetadata] = useState();
3484
3492
  const inputRef = useRef();
3485
3493
  useEffect(() => {
3494
+ setLoading(true);
3486
3495
  dispatch(actions_exports.readMetadata({})).then((resp) => {
3487
3496
  setMetadata(resp.data);
3488
3497
  }, (err) => {
@@ -4343,6 +4352,30 @@ function NavView({ headings, settings }) {
4343
4352
  return smallerThanMd ? /* @__PURE__ */ jsx(MobileNav, { contentOutline }) : /* @__PURE__ */ jsx(DesktopNav, { contentOutline });
4344
4353
  }
4345
4354
 
4355
+ // frontend/components/report/blocks/ResetButton.tsx
4356
+ init_esm_shims();
4357
+ init_hooks();
4358
+ init_hooks();
4359
+ function ResetButtonView({ id, label, showIcon, fullWidth, showWhenDisabled }) {
4360
+ const block = useBlockRef(id).data;
4361
+ const { sectionVariables, resetSectionVariables } = useSectionVariables(block?.section_id);
4362
+ const hasVariables = Boolean(Object.keys(sectionVariables).length);
4363
+ if (!hasVariables && !showWhenDisabled)
4364
+ return null;
4365
+ return /* @__PURE__ */ jsx(
4366
+ Button,
4367
+ {
4368
+ onClick: () => resetSectionVariables(),
4369
+ disabled: !hasVariables,
4370
+ fullWidth,
4371
+ children: /* @__PURE__ */ jsxs(Group, { align: "center", children: [
4372
+ label,
4373
+ showIcon && /* @__PURE__ */ jsx(IconRefresh, {})
4374
+ ] })
4375
+ }
4376
+ );
4377
+ }
4378
+
4346
4379
  // frontend/components/report/blocks/index.tsx
4347
4380
  var VizView = dynamic(
4348
4381
  () => Promise.resolve().then(() => (init_Viz(), Viz_exports)),
@@ -4357,7 +4390,8 @@ var TypeRenderers = {
4357
4390
  [BLOCK_TYPES.VIZ]: VizView,
4358
4391
  [BLOCK_TYPES.GENERATOR]: Generator,
4359
4392
  [BLOCK_TYPES.IMAGE]: ImageView,
4360
- [BLOCK_TYPES.NAV]: NavView
4393
+ [BLOCK_TYPES.NAV]: NavView,
4394
+ [BLOCK_TYPES.RESET_BUTTON]: ResetButtonView
4361
4395
  };
4362
4396
  var blocks_default = TypeRenderers;
4363
4397
 
@@ -4789,10 +4823,67 @@ function NavUI() {
4789
4823
  return /* @__PURE__ */ jsx(Title, { order: 4, children: "Nav UI" });
4790
4824
  }
4791
4825
 
4826
+ // components/blocks/types/simpleEditors/ResetButtonUI.tsx
4827
+ init_esm_shims();
4828
+ function ResetButtonUI({ id, locale, executeButton, onChange, simpleState }) {
4829
+ const [label, setLabel] = useState(simpleState?.label ?? "");
4830
+ const [showIcon, setShowIcon] = useState(simpleState?.showIcon ?? true);
4831
+ const [fullWidth, setFullwidth] = useState(simpleState?.fullWidth ?? false);
4832
+ const [showWhenDisabled, setShowWhenDisabled] = useState(simpleState?.showWhenDisabled ?? false);
4833
+ useEffect(() => {
4834
+ const simpleState2 = {
4835
+ label,
4836
+ showIcon,
4837
+ fullWidth,
4838
+ showWhenDisabled
4839
+ };
4840
+ onChange(simpleState2);
4841
+ }, [label, showIcon, fullWidth, showWhenDisabled, onChange]);
4842
+ return /* @__PURE__ */ jsxs(Stack, { children: [
4843
+ /* @__PURE__ */ jsx(
4844
+ TextInput,
4845
+ {
4846
+ placeholder: "Label for reset section state button",
4847
+ value: label,
4848
+ onChange: (e) => setLabel(e.target.value),
4849
+ label: "Label"
4850
+ }
4851
+ ),
4852
+ /* @__PURE__ */ jsxs(Group, { children: [
4853
+ /* @__PURE__ */ jsx(
4854
+ Switch,
4855
+ {
4856
+ checked: showIcon,
4857
+ label: "Show icon",
4858
+ onChange: (e) => setShowIcon(e.currentTarget.checked)
4859
+ }
4860
+ ),
4861
+ /* @__PURE__ */ jsx(
4862
+ Switch,
4863
+ {
4864
+ checked: fullWidth,
4865
+ label: "Full width",
4866
+ onChange: (e) => setFullwidth(e.currentTarget.checked)
4867
+ }
4868
+ ),
4869
+ /* @__PURE__ */ jsx(
4870
+ Switch,
4871
+ {
4872
+ checked: showWhenDisabled,
4873
+ label: "Show when disabled",
4874
+ onChange: (e) => setShowWhenDisabled(e.currentTarget.checked)
4875
+ }
4876
+ )
4877
+ ] }),
4878
+ executeButton
4879
+ ] });
4880
+ }
4881
+
4792
4882
  // components/blocks/types/simpleEditors/index.js
4793
4883
  var simpleEditors_default = {
4794
4884
  [BLOCK_TYPES.SELECTOR]: SelectorUI_default,
4795
- [BLOCK_TYPES.NAV]: NavUI
4885
+ [BLOCK_TYPES.NAV]: NavUI,
4886
+ [BLOCK_TYPES.RESET_BUTTON]: ResetButtonUI
4796
4887
  };
4797
4888
 
4798
4889
  // components/blocks/types/renderers/index.tsx
@@ -5073,11 +5164,16 @@ function InputMenuItem({
5073
5164
  ] }) }) });
5074
5165
  }
5075
5166
  var InputMenuItem_default = InputMenuItem;
5076
- function Generator2({ outputVariables, debug }) {
5077
- return debug ? /* @__PURE__ */ jsxs(Fragment, { children: [
5167
+ function Generator2({ outputVariables, status, hasAPI = false, debug: debug2 }) {
5168
+ const loading = hasAPI && !Object.keys(outputVariables).length && !status.error;
5169
+ const preview = loading ? /* @__PURE__ */ jsxs(Stack, { align: "center", children: [
5170
+ /* @__PURE__ */ jsx(Loader, {}),
5171
+ /* @__PURE__ */ jsx(Text, { size: "sm", children: "Loading Generator" })
5172
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
5078
5173
  /* @__PURE__ */ jsx(Divider, { label: "Output Variables", labelPosition: "center" }),
5079
5174
  /* @__PURE__ */ jsx(InputMenuItem_default, { variables: outputVariables })
5080
- ] }) : /* @__PURE__ */ jsx(Center, { children: /* @__PURE__ */ jsx(Badge, { color: "gray", variant: "outline", children: "GENERATOR" }, "type") });
5175
+ ] });
5176
+ return debug2 ? preview : /* @__PURE__ */ jsx(Center, { children: /* @__PURE__ */ jsx(Badge, { color: "gray", variant: "outline", children: "GENERATOR" }, "type") });
5081
5177
  }
5082
5178
  var Generator_default = Generator2;
5083
5179
 
@@ -5233,6 +5329,31 @@ function NavPreview({ headings, settings }) {
5233
5329
  ] });
5234
5330
  }
5235
5331
 
5332
+ // components/blocks/types/renderers/ResetButton.tsx
5333
+ init_esm_shims();
5334
+ init_hooks();
5335
+ init_hooks();
5336
+ function ResetButtonPreview({ id, label, showIcon, fullWidth, showWhenDisabled }) {
5337
+ const block = useBlockRef(id).data;
5338
+ const { sectionVariables, resetSectionVariables } = useSectionVariables(block?.section_id);
5339
+ const hasVariables = Boolean(Object.keys(sectionVariables).length);
5340
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
5341
+ !hasVariables && !showWhenDisabled && /* @__PURE__ */ jsx(Text, { size: "xs", children: "Reset button will not be shown on its disabled state" }),
5342
+ /* @__PURE__ */ jsx(
5343
+ Button,
5344
+ {
5345
+ onClick: () => resetSectionVariables(),
5346
+ disabled: !hasVariables,
5347
+ fullWidth,
5348
+ children: /* @__PURE__ */ jsxs(Group, { align: "center", children: [
5349
+ label,
5350
+ showIcon && /* @__PURE__ */ jsx(IconRefresh, {})
5351
+ ] })
5352
+ }
5353
+ )
5354
+ ] });
5355
+ }
5356
+
5236
5357
  // components/blocks/types/renderers/index.tsx
5237
5358
  var VizPreview = dynamic(
5238
5359
  () => Promise.resolve().then(() => (init_Viz2(), Viz_exports2)),
@@ -5247,7 +5368,8 @@ var renderersMap = {
5247
5368
  [BLOCK_TYPES.VIZ]: VizPreview,
5248
5369
  [BLOCK_TYPES.GENERATOR]: Generator_default,
5249
5370
  [BLOCK_TYPES.IMAGE]: ImagePreview,
5250
- [BLOCK_TYPES.NAV]: NavPreview
5371
+ [BLOCK_TYPES.NAV]: NavPreview,
5372
+ [BLOCK_TYPES.RESET_BUTTON]: ResetButtonPreview
5251
5373
  };
5252
5374
  var renderers_default = renderersMap;
5253
5375
 
@@ -5311,6 +5433,12 @@ var allBlocks = {
5311
5433
  renderer: renderers_default[BLOCK_TYPES.NAV],
5312
5434
  renderPreviewOnEdit: true,
5313
5435
  evalWhenNonActive: true
5436
+ },
5437
+ [BLOCK_TYPES.RESET_BUTTON]: {
5438
+ type: BLOCK_TYPES.RESET_BUTTON,
5439
+ renderer: renderers_default[BLOCK_TYPES.RESET_BUTTON],
5440
+ renderPreviewOnEdit: true,
5441
+ evalWhenNonActive: true
5314
5442
  }
5315
5443
  };
5316
5444
  var types_default = allBlocks;
@@ -5396,6 +5524,10 @@ function Block({ blockId, active = true }) {
5396
5524
  });
5397
5525
  return;
5398
5526
  }
5527
+ if (block.type === BLOCK_TYPES.RESET_BUTTON) {
5528
+ setContent({ id: block.id, ...blockContent.simple });
5529
+ return;
5530
+ }
5399
5531
  const swappedLogic = varSwap_default(blockContent?.logic, formatterFunctions, blockContext);
5400
5532
  const { vars } = mortarEval_default(
5401
5533
  "variables",
@@ -6445,7 +6577,7 @@ var SectionResetButton = ({ id }) => {
6445
6577
  const hasVariables = Boolean(Object.keys(sectionVariables).length);
6446
6578
  if (!hasVariables)
6447
6579
  return null;
6448
- return /* @__PURE__ */ jsx(ActionIcon, { disabled: !hasVariables, onClick: resetSectionVariables, children: /* @__PURE__ */ jsx(IconRefresh, { size: 20 }) });
6580
+ return /* @__PURE__ */ jsx(ActionIcon, { disabled: !hasVariables, onClick: resetSectionVariables, variant: "filled", size: "xs", children: /* @__PURE__ */ jsx(IconRefresh, { size: 20 }) });
6449
6581
  };
6450
6582
  function SectionMenu({ sectionId }) {
6451
6583
  const theme = useMantineTheme();
@@ -6454,8 +6586,7 @@ function SectionMenu({ sectionId }) {
6454
6586
  borderRadius: theme.radius.sm
6455
6587
  }, children: [
6456
6588
  /* @__PURE__ */ jsx(DesignSectionMenu, { sectionId }),
6457
- /* @__PURE__ */ jsx(EntityDeleteButton, { type: "section", id: sectionId }),
6458
- /* @__PURE__ */ jsx(SectionResetButton, { id: sectionId })
6589
+ /* @__PURE__ */ jsx(EntityDeleteButton, { type: "section", id: sectionId })
6459
6590
  ] });
6460
6591
  }
6461
6592
  var SectionMenu_default = SectionMenu;
@@ -6959,14 +7090,9 @@ function Section({ section }) {
6959
7090
  id: `section-${id}`,
6960
7091
  pos: "relative",
6961
7092
  children: [
6962
- /* @__PURE__ */ jsxs("div", { style: {
6963
- position: "absolute",
6964
- right: 10,
6965
- top: 10,
6966
- zIndex: 3
6967
- }, children: [
6968
- sectionSettings2.optionsMenu && /* @__PURE__ */ jsx(Options, { sectionId: section.id }),
6969
- /* @__PURE__ */ jsx(SectionResetButton, { id: section.id })
7093
+ /* @__PURE__ */ jsxs(Group, { position: "right", mb: "xs", children: [
7094
+ /* @__PURE__ */ jsx(SectionResetButton, { id: section.id }),
7095
+ sectionSettings2.optionsMenu && /* @__PURE__ */ jsx(Options, { sectionId: section.id })
6970
7096
  ] }),
6971
7097
  /* @__PURE__ */ jsx(StyleWrapper, { className: "bespoke-section-content", settings: sectionSettings2, styles: sectionStyles?.content, children: /* @__PURE__ */ jsx(ColumnsWrapper, { children: Object.keys(columns).sort((a, b) => orderSort(a, b, "blockcol")).map((columnIndex) => {
6972
7098
  const column = columns[columnIndex];
@@ -7258,7 +7384,7 @@ function normalizeList(value) {
7258
7384
  var { localeDefault: localeDefault8 } = getLocales_default();
7259
7385
  function parseReadMemberParams(query) {
7260
7386
  const locale = normalizeList(query.locale)[0] || localeDefault8 || "en";
7261
- const all = locale === "all" || yn3(query.all);
7387
+ const all = locale === "all" || yn4(query.all);
7262
7388
  const mode = normalizeList(query.mode)[0];
7263
7389
  const outputParam = normalizeList(query.output)[0] || "full";
7264
7390
  const output = outputParam === "lite" ? "lite" : "full";
@@ -7323,9 +7449,9 @@ function parseSearchMemberParams(query) {
7323
7449
  locale: localeIsAll ? localeDefault8 : locale,
7324
7450
  limit: normalizeList(query.limit).map(parseFiniteNumber)[0] ?? 5,
7325
7451
  format: formatIsNested ? "nested" : "plain",
7326
- includes: yn3(query.includes, { default: true }),
7327
- visible: yn3(query.visible, { default: true }),
7328
- noImage: yn3(query.noImage, { default: false }),
7452
+ includes: yn4(query.includes, { default: true }),
7453
+ visible: yn4(query.visible, { default: true }),
7454
+ noImage: yn4(query.noImage, { default: false }),
7329
7455
  variant,
7330
7456
  dimension,
7331
7457
  report,
@@ -8907,7 +9033,7 @@ function BlockPreview(props) {
8907
9033
  active,
8908
9034
  allowed,
8909
9035
  blockStateContent,
8910
- debug,
9036
+ debug: debug2,
8911
9037
  for: block,
8912
9038
  locale
8913
9039
  } = props;
@@ -8961,7 +9087,8 @@ function BlockPreview(props) {
8961
9087
  return;
8962
9088
  }
8963
9089
  if (block.type === BLOCK_TYPES.GENERATOR) {
8964
- setContent({ outputVariables: generatorVariables });
9090
+ const hasAPI = Boolean(block.contentByLocale?.logic?.content?.api);
9091
+ setContent({ outputVariables: generatorVariables, hasAPI });
8965
9092
  setStatus2(generatorStatus);
8966
9093
  return;
8967
9094
  }
@@ -8976,6 +9103,10 @@ function BlockPreview(props) {
8976
9103
  setContent({ headings: block.inputs, settings: block.settings });
8977
9104
  return;
8978
9105
  }
9106
+ if (block.type === BLOCK_TYPES.RESET_BUTTON) {
9107
+ setContent(blockContent.simple);
9108
+ return;
9109
+ }
8979
9110
  const swappedLogic = varSwap_default(blockContent?.logic, formatterFunctions, blockContext);
8980
9111
  const { vars, error: error2, log: log2 } = mortarEval_default(
8981
9112
  "variables",
@@ -9015,8 +9146,8 @@ function BlockPreview(props) {
9015
9146
  style: { width: "100%", minHeight: block.type === BLOCK_TYPES.VIZ ? 400 : "auto" },
9016
9147
  children: [
9017
9148
  !allowed && allowedOverlay,
9018
- Renderer ? /* @__PURE__ */ jsx(Fragment$1, { children: /* @__PURE__ */ jsx(Renderer, { id: block.id, debug, ...content, settings: block.settings }) }, "renderer") : /* @__PURE__ */ jsx(Center, { style: { minHeight: 100 }, children: /* @__PURE__ */ jsx(Badge, { color: "gray", variant: "outline", children: block.type }, "type") }),
9019
- debug && textLog && /* @__PURE__ */ jsx(
9149
+ Renderer ? /* @__PURE__ */ jsx(Fragment$1, { children: /* @__PURE__ */ jsx(Renderer, { id: block.id, debug: debug2, ...content, settings: block.settings, status }) }, "renderer") : /* @__PURE__ */ jsx(Center, { style: { minHeight: 100 }, children: /* @__PURE__ */ jsx(Badge, { color: "gray", variant: "outline", children: block.type }, "type") }),
9150
+ debug2 && textLog && /* @__PURE__ */ jsx(
9020
9151
  Textarea,
9021
9152
  {
9022
9153
  label: "Console",
@@ -9026,7 +9157,7 @@ function BlockPreview(props) {
9026
9157
  error
9027
9158
  }
9028
9159
  ),
9029
- debug && error && /* @__PURE__ */ jsx(Textarea, { label: "Error", readOnly: true, minRows: 3, value: error }),
9160
+ debug2 && error && /* @__PURE__ */ jsx(Textarea, { label: "Error", readOnly: true, minRows: 3, value: error }),
9030
9161
  block.type === BLOCK_TYPES.GENERATOR && /* @__PURE__ */ jsxs(Fragment, { children: [
9031
9162
  /* @__PURE__ */ jsxs(Group, { spacing: "xs", my: "xs", children: [
9032
9163
  status.duration && /* @__PURE__ */ jsxs(
@@ -10183,6 +10314,28 @@ init_getBlockContent();
10183
10314
 
10184
10315
  // components/sections/SectionHeader.tsx
10185
10316
  init_esm_shims();
10317
+ init_store2();
10318
+ init_cms();
10319
+ init_getBlockContent();
10320
+ function AddGeneratorButton({ section_id, onCreate = () => {
10321
+ } }) {
10322
+ const dispatch = useAppDispatch();
10323
+ const createGenerator = () => {
10324
+ const { locales: locales4 } = getLocaleDerived({ type: BLOCK_TYPES.GENERATOR });
10325
+ dispatch(actions_exports.createEntity("block", {
10326
+ blockrow: void 0,
10327
+ blockcol: void 0,
10328
+ type: BLOCK_TYPES.GENERATOR,
10329
+ section_id,
10330
+ locales: locales4
10331
+ }));
10332
+ onCreate();
10333
+ };
10334
+ return /* @__PURE__ */ jsxs(Group, { spacing: "xs", ml: "sm", children: [
10335
+ /* @__PURE__ */ jsx(ActionIcon, { color: "blue", size: "xs", radius: "lg", variant: "light", onClick: createGenerator, children: /* @__PURE__ */ jsx(IconApi, { size: "0.8rem" }) }),
10336
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", onClick: createGenerator, children: "Add generator" })
10337
+ ] });
10338
+ }
10186
10339
  function SectionHeader({
10187
10340
  active,
10188
10341
  isDragging,
@@ -10204,18 +10357,35 @@ function SectionHeader({
10204
10357
  id || "hello"
10205
10358
  ] }, "s1"),
10206
10359
  /* @__PURE__ */ jsx(ActionIcon, { ...dragHandleProps, children: /* @__PURE__ */ jsx(IconMenu, { size: 16 }) }, "b1"),
10207
- /* @__PURE__ */ jsx(
10360
+ generators.hasGenerators && active && /* @__PURE__ */ jsx(
10208
10361
  Switch,
10209
10362
  {
10210
10363
  size: "xs",
10211
- label: "Show generators",
10364
+ label: /* @__PURE__ */ jsxs(Text, { c: "dimmed", size: "xs", children: [
10365
+ generators.showGenerators ? "Hide" : "Show",
10366
+ " generators"
10367
+ ] }),
10212
10368
  color: "green",
10213
10369
  checked: generators.showGenerators,
10370
+ onLabel: /* @__PURE__ */ jsx(IconApi, { size: "0.8rem" }),
10371
+ offLabel: /* @__PURE__ */ jsx(IconApi, { size: "0.8rem" }),
10214
10372
  onChange: (e) => generators.setShowGenerators(e.currentTarget.checked)
10215
10373
  }
10216
- )
10374
+ ),
10375
+ (!generators.hasGenerators || generators.showGenerators) && active && /* @__PURE__ */ jsx(AddGeneratorButton, { section_id: id, onCreate: () => generators.setShowGenerators(true) })
10217
10376
  ] }),
10218
- hidden && /* @__PURE__ */ jsx(Text, { size: "xs", fw: 600, color: "dark.3", children: "Section will be hidden on the frontend" }),
10377
+ hidden && /* @__PURE__ */ jsx(
10378
+ Text,
10379
+ {
10380
+ size: "xs",
10381
+ fw: 600,
10382
+ pos: "absolute",
10383
+ sx: { transform: "translateX(-50%)" },
10384
+ left: "50%",
10385
+ color: "dark.3",
10386
+ children: "Section will be hidden on the frontend"
10387
+ }
10388
+ ),
10219
10389
  /* @__PURE__ */ jsx(SectionMenu_default, { sectionId: id })
10220
10390
  ]
10221
10391
  }
@@ -10252,7 +10422,7 @@ var CreateBlockButton = ({ columns, columnIndex, section }) => {
10252
10422
  type: "select",
10253
10423
  name: "type",
10254
10424
  label: "Block Type",
10255
- options: blockTypes.map((d) => ({ label: d, value: d }))
10425
+ options: blockTypes.filter((d) => d !== BLOCK_TYPES.GENERATOR).map((d) => ({ label: d, value: d }))
10256
10426
  }],
10257
10427
  onSubmit: (value) => addBlock(value.type, columnIndex),
10258
10428
  target: /* @__PURE__ */ jsx(ActionIcon, { size: "md", radius: "lg", children: /* @__PURE__ */ jsx(IconCirclePlus, { size: 20 }) })
@@ -10309,21 +10479,29 @@ var ClickToEditOverlay = ({ onActivate, section, show = false }) => {
10309
10479
  );
10310
10480
  };
10311
10481
  var GeneratorsPanel = ({ generators, show = false }) => {
10312
- return /* @__PURE__ */ jsxs(Collapse, { in: show, p: "sm", children: [
10313
- /* @__PURE__ */ jsx(Title, { order: 5, align: "center", children: "Generators" }),
10314
- /* @__PURE__ */ jsx(
10315
- SimpleGrid,
10316
- {
10317
- cols: generators.length > 3 ? 3 : generators.length,
10318
- breakpoints: [
10319
- { maxWidth: "md", cols: 3, spacing: "md" },
10320
- { maxWidth: "sm", cols: 2, spacing: "sm" },
10321
- { maxWidth: "xs", cols: 1, spacing: "sm" }
10322
- ],
10323
- children: generators
10324
- }
10325
- )
10326
- ] });
10482
+ return /* @__PURE__ */ jsx(Collapse, { in: show, p: "sm", children: /* @__PURE__ */ jsx(
10483
+ Box,
10484
+ {
10485
+ sx: (theme) => ({
10486
+ backgroundColor: theme.colors.gray[1],
10487
+ padding: theme.spacing.xl,
10488
+ borderRadius: theme.radius.md
10489
+ }),
10490
+ children: /* @__PURE__ */ jsx(
10491
+ SimpleGrid,
10492
+ {
10493
+ cols: generators.length > 3 ? 3 : generators.length,
10494
+ mt: "md",
10495
+ breakpoints: [
10496
+ { maxWidth: "md", cols: 3, spacing: "md" },
10497
+ { maxWidth: "sm", cols: 2, spacing: "sm" },
10498
+ { maxWidth: "xs", cols: 1, spacing: "sm" }
10499
+ ],
10500
+ children: generators
10501
+ }
10502
+ )
10503
+ }
10504
+ ) });
10327
10505
  };
10328
10506
  function SectionEditor({
10329
10507
  for: section,
@@ -10417,131 +10595,143 @@ function SectionEditor({
10417
10595
  dragHandleProps,
10418
10596
  generators: {
10419
10597
  showGenerators,
10420
- setShowGenerators
10598
+ setShowGenerators,
10599
+ hasGenerators: Boolean(generators.length)
10421
10600
  }
10422
10601
  }
10423
10602
  ),
10424
- memberImageBg && /* @__PURE__ */ jsx(SectionBackground, {}),
10425
- optionsMenu && /* @__PURE__ */ jsx(Flex, { justify: "flex-end", mx: 16, children: /* @__PURE__ */ jsx(Options, { sectionId: section.id }) }),
10426
- /* @__PURE__ */ jsx(
10427
- GeneratorsPanel,
10428
- {
10429
- show: showGenerators,
10430
- generators: generators.map((item) => /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(
10431
- BlockElement_default,
10432
- {
10433
- id: Number(item.id),
10434
- active: isActive,
10435
- isInput: inputs.includes(Number(item.id)),
10436
- isConsumer: consumers.includes(Number(item.id)),
10437
- setHoverBlock
10438
- }
10439
- ) }, `block-${item.id}`))
10440
- }
10441
- ),
10442
- /* @__PURE__ */ jsxs(
10443
- ColumnsWrapper,
10444
- {
10445
- ref: columnContainer,
10446
- containerProps: {
10447
- mih: !isActive || !columnsQty ? 120 : "none",
10448
- p: "md"
10449
- },
10450
- children: [
10451
- /* @__PURE__ */ jsxs(DragDropContext, { onDragEnd, onDragStart, children: [
10452
- orderedCols.map((columnIndex) => {
10453
- const orderedBlocks = Object.values(columns[columnIndex]).sort((a, b) => orderSort(a, b, "blockrow"));
10454
- return /* @__PURE__ */ jsx(Droppable, { droppableId: columnIndex, children: (provided, snapshot) => /* @__PURE__ */ jsxs(
10455
- SectionColumn,
10456
- {
10457
- column: columns[columnIndex],
10458
- columnSettings: columnSettings[columnIndex],
10459
- ref: provided.innerRef,
10460
- sx: {
10461
- // extra styles for columns on editor
10462
- flexBasis: `${100 / columnsQty}%`,
10463
- background: snapshot.isDraggingOver ? theme.colors[theme.primaryColor][0] : "inherit",
10464
- border: "1px solid transparent",
10465
- "& .bespoke-resize-col": {
10466
- visibility: "hidden"
10467
- },
10468
- "&:hover .bespoke-resize-col": {
10469
- visibility: "visible"
10470
- },
10471
- "&:hover": {
10472
- border: `1px solid ${theme.colors.teal[1]}`
10473
- }
10474
- },
10475
- ...provided.droppableProps,
10476
- children: [
10477
- /* @__PURE__ */ jsx(
10478
- ResizeColumnInput,
10479
- {
10480
- sectionId: section.id,
10481
- columnIndex
10603
+ /* @__PURE__ */ jsxs(Box, { pos: "relative", children: [
10604
+ memberImageBg && /* @__PURE__ */ jsx(SectionBackground, {}),
10605
+ optionsMenu && /* @__PURE__ */ jsxs(Flex, { justify: "flex-end", mx: 16, align: "center", gap: "xs", children: [
10606
+ /* @__PURE__ */ jsx(SectionResetButton, { id: section.id }),
10607
+ /* @__PURE__ */ jsx(Options, { sectionId: section.id })
10608
+ ] }),
10609
+ /* @__PURE__ */ jsx(
10610
+ GeneratorsPanel,
10611
+ {
10612
+ show: showGenerators && Boolean(generators.length),
10613
+ generators: generators.map((item) => /* @__PURE__ */ jsxs("div", { children: [
10614
+ /* @__PURE__ */ jsxs(Title, { order: 6, children: [
10615
+ "Generator ",
10616
+ item.id
10617
+ ] }),
10618
+ /* @__PURE__ */ jsx(
10619
+ BlockElement_default,
10620
+ {
10621
+ id: Number(item.id),
10622
+ active: isActive,
10623
+ isInput: inputs.includes(Number(item.id)),
10624
+ isConsumer: consumers.includes(Number(item.id)),
10625
+ setHoverBlock
10626
+ }
10627
+ )
10628
+ ] }, `block-${item.id}`))
10629
+ }
10630
+ ),
10631
+ /* @__PURE__ */ jsxs(
10632
+ ColumnsWrapper,
10633
+ {
10634
+ ref: columnContainer,
10635
+ containerProps: {
10636
+ mih: !isActive || !columnsQty ? 120 : "none",
10637
+ p: "md"
10638
+ },
10639
+ children: [
10640
+ /* @__PURE__ */ jsxs(DragDropContext, { onDragEnd, onDragStart, children: [
10641
+ orderedCols.map((columnIndex) => {
10642
+ const orderedBlocks = Object.values(columns[columnIndex]).sort((a, b) => orderSort(a, b, "blockrow"));
10643
+ return /* @__PURE__ */ jsx(Droppable, { droppableId: columnIndex, children: (provided, snapshot) => /* @__PURE__ */ jsxs(
10644
+ SectionColumn,
10645
+ {
10646
+ column: columns[columnIndex],
10647
+ columnSettings: columnSettings[columnIndex],
10648
+ ref: provided.innerRef,
10649
+ sx: {
10650
+ // extra styles for columns on editor
10651
+ flexBasis: `${100 / columnsQty}%`,
10652
+ background: snapshot.isDraggingOver ? theme.colors[theme.primaryColor][0] : "inherit",
10653
+ border: "1px solid transparent",
10654
+ "& .bespoke-resize-col": {
10655
+ visibility: "hidden"
10656
+ },
10657
+ "&:hover .bespoke-resize-col": {
10658
+ visibility: "visible"
10659
+ },
10660
+ "&:hover": {
10661
+ border: `1px solid ${theme.colors.teal[1]}`
10482
10662
  }
10483
- ),
10484
- orderedBlocks.map((item, rowIndex) => {
10485
- if (!blocks[item.id])
10486
- return null;
10487
- const { settings, type } = blocks[item.id];
10488
- const blockWidth = settings.width && !settings.width.stretch && settings.width.unit ? formatters[settings.width.unit](settings.width.value) : settings.display === "inline" ? "auto" : "100%";
10489
- const blockStyles = {
10490
- alignSelf: type === "visualization" ? "stretch" : "flex-start",
10491
- flexGrow: 0,
10492
- margin: "0",
10493
- textAlign: settings.align || blockSettings.align.defaultValue,
10494
- width: blockWidth,
10495
- minWidth: 120
10496
- };
10497
- return /* @__PURE__ */ jsx(
10498
- Draggable,
10663
+ },
10664
+ ...provided.droppableProps,
10665
+ children: [
10666
+ /* @__PURE__ */ jsx(
10667
+ ResizeColumnInput,
10499
10668
  {
10500
- draggableId: String(item.id),
10501
- index: rowIndex,
10502
- children: (provided2, snapshot2) => /* @__PURE__ */ jsx(
10503
- Box,
10504
- {
10505
- ref: provided2.innerRef,
10506
- ...provided2.draggableProps,
10507
- ...provided2.dragHandleProps,
10508
- sx: [
10509
- blockStyles,
10510
- {
10511
- boxShadow: snapshot2.isDragging ? theme.shadows.lg : "none",
10512
- ...provided2.draggableProps.style
10513
- }
10514
- ],
10515
- children: /* @__PURE__ */ jsx(
10516
- BlockElement_default,
10517
- {
10518
- id: Number(item.id),
10519
- active: isActive,
10520
- isInput: inputs.includes(Number(item.id)),
10521
- isConsumer: consumers.includes(Number(item.id)),
10522
- setHoverBlock
10523
- },
10524
- `block-${item.id}`
10525
- )
10526
- }
10527
- )
10528
- },
10529
- item.id
10530
- );
10531
- }),
10532
- isActive && /* @__PURE__ */ jsx(CreateBlockButton, { section, columnIndex, columns }),
10533
- provided.placeholder
10534
- ]
10535
- }
10536
- ) }, columnIndex);
10537
- }),
10538
- /* @__PURE__ */ jsx(CreateColumnArea, { active: draggingBlock && isActive })
10539
- ] }),
10540
- /* @__PURE__ */ jsx(FirstBlockButton, { columns, section, show: isActive && !columnsQty })
10541
- ]
10542
- }
10543
- ),
10544
- /* @__PURE__ */ jsx(ClickToEditOverlay, { onActivate, section, show: !isActive })
10669
+ sectionId: section.id,
10670
+ columnIndex
10671
+ }
10672
+ ),
10673
+ orderedBlocks.map((item, rowIndex) => {
10674
+ if (!blocks[item.id])
10675
+ return null;
10676
+ const { settings, type } = blocks[item.id];
10677
+ const blockWidth = settings.width && !settings.width.stretch && settings.width.unit ? formatters[settings.width.unit](settings.width.value) : settings.display === "inline" ? "auto" : "100%";
10678
+ const blockStyles = {
10679
+ alignSelf: type === "visualization" ? "stretch" : "flex-start",
10680
+ flexGrow: 0,
10681
+ margin: "0",
10682
+ textAlign: settings.align || blockSettings.align.defaultValue,
10683
+ width: blockWidth,
10684
+ minWidth: 120
10685
+ };
10686
+ return /* @__PURE__ */ jsx(
10687
+ Draggable,
10688
+ {
10689
+ draggableId: String(item.id),
10690
+ index: rowIndex,
10691
+ children: (provided2, snapshot2) => /* @__PURE__ */ jsx(
10692
+ Box,
10693
+ {
10694
+ ref: provided2.innerRef,
10695
+ ...provided2.draggableProps,
10696
+ ...provided2.dragHandleProps,
10697
+ sx: [
10698
+ blockStyles,
10699
+ {
10700
+ boxShadow: snapshot2.isDragging ? theme.shadows.lg : "none",
10701
+ ...provided2.draggableProps.style
10702
+ }
10703
+ ],
10704
+ children: /* @__PURE__ */ jsx(
10705
+ BlockElement_default,
10706
+ {
10707
+ id: Number(item.id),
10708
+ active: isActive,
10709
+ isInput: inputs.includes(Number(item.id)),
10710
+ isConsumer: consumers.includes(Number(item.id)),
10711
+ setHoverBlock
10712
+ },
10713
+ `block-${item.id}`
10714
+ )
10715
+ }
10716
+ )
10717
+ },
10718
+ item.id
10719
+ );
10720
+ }),
10721
+ isActive && /* @__PURE__ */ jsx(CreateBlockButton, { section, columnIndex, columns }),
10722
+ provided.placeholder
10723
+ ]
10724
+ }
10725
+ ) }, columnIndex);
10726
+ }),
10727
+ /* @__PURE__ */ jsx(CreateColumnArea, { active: draggingBlock && isActive })
10728
+ ] }),
10729
+ /* @__PURE__ */ jsx(FirstBlockButton, { columns, section, show: isActive && !columnsQty })
10730
+ ]
10731
+ }
10732
+ ),
10733
+ /* @__PURE__ */ jsx(ClickToEditOverlay, { onActivate, section, show: !isActive })
10734
+ ] })
10545
10735
  ]
10546
10736
  }
10547
10737
  );
package/dist/server.js CHANGED
@@ -500,8 +500,9 @@ var BLOCK_CONTENT_TYPES = {
500
500
  STAT: "stat",
501
501
  SUBTITLE: "subtitle",
502
502
  TITLE: "title",
503
- NAV: "nav"
503
+ NAV: "nav",
504
504
  // todo1.0, how to put custom blocks in here?
505
+ RESET_BUTTON: "reset_button"
505
506
  };
506
507
  var BLOCK_LOGIC_TYPES = {
507
508
  GENERATOR: "generator",
@@ -2115,20 +2116,26 @@ var whitelist = [
2115
2116
  "vi",
2116
2117
  "zh"
2117
2118
  ];
2118
- getLogging_default();
2119
+ var verbose3 = getLogging_default();
2119
2120
  var { locales } = getLocales_default();
2120
2121
  var enabledLocales = locales.filter((d) => whitelist.includes(d));
2121
2122
  var initializing = false;
2122
2123
  var getSearchIndexByLocale = async (db, forceRegenerate = false) => {
2123
2124
  if (forceRegenerate) {
2125
+ if (verbose3)
2126
+ console.log("0\uFE0F\u20E3 CLEARING SEARCH INDEX");
2124
2127
  global.lunrsearch = void 0;
2125
2128
  initializing = false;
2126
2129
  }
2127
2130
  if (global.lunrsearch) {
2131
+ if (verbose3)
2132
+ console.log("\u{1F501} RE-UTILIZING SEARCH INDEX");
2128
2133
  return global.lunrsearch;
2129
2134
  }
2130
2135
  try {
2131
2136
  if (!initializing) {
2137
+ if (verbose3)
2138
+ console.log("1\uFE0F\u20E3 INITIALIZING SEARCH INDEX");
2132
2139
  initializing = true;
2133
2140
  global.lunrsearch = await newSearchIndex(db);
2134
2141
  }
@@ -2146,34 +2153,46 @@ async function newSearchIndex(db) {
2146
2153
  include: [{ association: "contentByLocale" }]
2147
2154
  });
2148
2155
  const setups = enabledLocales.map(async (locale) => {
2149
- if (!lunr[locale] && whitelist.includes(locale) && locale !== "en") {
2156
+ if (!lunr[locale] && locale !== "en") {
2150
2157
  const lunrLang = (await import(`lunr-languages/lunr.${locale}`)).default;
2151
2158
  lunrLang(lunr);
2159
+ lunrStemmer(lunr);
2160
+ if (verbose3)
2161
+ console.log(`\u{1F3F3}\uFE0F INITIALIZING SEARCH ${locale} LOCALE`);
2152
2162
  }
2153
- return [locale, lunr(function() {
2154
- if (locale && locale !== "en")
2155
- this.use(lunr[locale]);
2156
- this.ref("content_id");
2157
- this.field("id");
2158
- this.field("name");
2159
- this.field("keywords");
2160
- this.field("attr");
2161
- this.pipeline.reset();
2162
- this.searchPipeline.reset();
2163
- results.forEach((result) => {
2164
- const content = result.contentByLocale.find((d) => d.locale === locale);
2165
- if (content) {
2166
- const payload = {
2167
- id: result.id,
2168
- content_id: result.content_id,
2169
- name: content.name,
2170
- keywords: content.keywords,
2171
- attributes: content.attributes
2172
- };
2173
- this.add(payload, { boost: result.zvalue });
2174
- }
2175
- }, this);
2176
- })];
2163
+ return [
2164
+ locale,
2165
+ {
2166
+ tokenizer: lunr.tokenizer,
2167
+ query: lunr.Query,
2168
+ index: await lunr(
2169
+ function() {
2170
+ if (locale && locale !== "en")
2171
+ this.use(lunr[locale]);
2172
+ this.ref("content_id");
2173
+ this.field("id");
2174
+ this.field("name");
2175
+ this.field("keywords");
2176
+ this.field("attributes");
2177
+ this.pipeline.reset();
2178
+ this.searchPipeline.reset();
2179
+ results.forEach((result) => {
2180
+ const content = result.contentByLocale.find((d) => d.locale === locale);
2181
+ if (content) {
2182
+ const payload = {
2183
+ id: result.id,
2184
+ content_id: result.content_id,
2185
+ name: content.name,
2186
+ keywords: content.keywords,
2187
+ attributes: content.attributes
2188
+ };
2189
+ this.add(payload, { boost: result.zvalue });
2190
+ }
2191
+ }, this);
2192
+ }
2193
+ )
2194
+ }
2195
+ ];
2177
2196
  });
2178
2197
  return Object.fromEntries(
2179
2198
  await Promise.all(setups)
@@ -2205,8 +2224,10 @@ function dbSearchMemberFactory(db) {
2205
2224
  direction = "ASC"
2206
2225
  } = params;
2207
2226
  if (query && query !== "" && searchIndexByLocale[locale]) {
2208
- const terms = query.split(" ").map((d) => `+${d}*`).join(" ");
2209
- const lunrResults = searchIndexByLocale[locale].search(terms);
2227
+ const terms = query.split(" ").filter((d) => d.trim() !== "").map((d) => {
2228
+ return `+${d}~2*`;
2229
+ }).join(" ");
2230
+ const lunrResults = searchIndexByLocale[locale].index.search(terms);
2210
2231
  resultsIds = lunrResults.map((d) => parseInt(d.ref, 10));
2211
2232
  }
2212
2233
  const whereClause = {
@@ -4612,12 +4633,13 @@ function getRootBlocksForSection(sid, blocks) {
4612
4633
  blockList.filter((block) => block.section_id === sid && // leave blocks belonging to this section
4613
4634
  // that don't depend on any other blocks OR
4614
4635
  (block.inputs.length === 0 || // that depend only on blocks from other sections
4615
- block.inputs.every((id) => nonNativeBlocks.includes(id)))).map((block) => [block.id, block])
4636
+ block.inputs.some((id) => nonNativeBlocks.includes(id)))).map((block) => [block.id, block])
4616
4637
  // then convert them to entries
4617
4638
  );
4618
4639
  }
4619
4640
  var getRootBlocksForSection_default = getRootBlocksForSection;
4620
4641
  var verbose7 = getLogging_default();
4642
+ var debug = yn3(process.env.NEXT_PUBLIC_REPORTS_DEBUG);
4621
4643
  var ORIGIN = process.env.REPORTS_ORIGIN || "";
4622
4644
  axios5.interceptors.request.use((config) => ({
4623
4645
  ...config,
@@ -4721,14 +4743,7 @@ async function runSingleBlock(block, formatterFunctions, blockContext, readMembe
4721
4743
  return {
4722
4744
  outputVariables: {},
4723
4745
  renderVariables: {},
4724
- status: {
4725
- api: null,
4726
- duration: null,
4727
- resp: null,
4728
- log: [],
4729
- error: null,
4730
- allowed: false
4731
- }
4746
+ status: { allowed: false }
4732
4747
  };
4733
4748
  }
4734
4749
  if (unswappedAPI) {
@@ -4759,9 +4774,6 @@ async function runSingleBlock(block, formatterFunctions, blockContext, readMembe
4759
4774
  outputVariables: selectorQueryToVariable_default(block.id, query, config),
4760
4775
  renderVariables: config,
4761
4776
  status: {
4762
- api: null,
4763
- duration: null,
4764
- resp: null,
4765
4777
  log: log2,
4766
4778
  error: error2,
4767
4779
  allowed
@@ -4774,11 +4786,8 @@ async function runSingleBlock(block, formatterFunctions, blockContext, readMembe
4774
4786
  // todo1.0 this will have to be materialized click-variables
4775
4787
  renderVariables: block.contentByLocale.logic.content,
4776
4788
  status: {
4777
- api: null,
4778
- duration: null,
4779
- resp: null,
4780
4789
  log: [],
4781
- error: null,
4790
+ // todo: see if we can remove this safely
4782
4791
  allowed
4783
4792
  }
4784
4793
  };
@@ -4818,10 +4827,7 @@ async function runSingleBlock(block, formatterFunctions, blockContext, readMembe
4818
4827
  var getDependencies = (bid, blocks, acc = [], crawlUp = true, crawlDown2 = true, withinSection = true, visited = []) => {
4819
4828
  if (visited.includes(bid))
4820
4829
  return [];
4821
- if (crawlDown2)
4822
- visited.push(bid);
4823
- if (verbose7)
4824
- console.log("%cpassing through block", "color:green", bid);
4830
+ visited.push(bid);
4825
4831
  const rootBlock = blocks[bid];
4826
4832
  if (rootBlock.inputs.length && crawlUp) {
4827
4833
  rootBlock.inputs.forEach((iid) => {
@@ -4838,7 +4844,7 @@ var getDependencies = (bid, blocks, acc = [], crawlUp = true, crawlDown2 = true,
4838
4844
  if (!acc.includes(rel) && (blocks[cid].section_id === blocks[bid].section_id || !withinSection)) {
4839
4845
  acc.push(rel);
4840
4846
  }
4841
- rootBlock.consumers.filter((cid2) => blocks[cid2].section_id === blocks[bid].section_id || !withinSection).forEach((cid2) => getDependencies(cid2, blocks, acc, crawlUp, crawlDown2, withinSection, visited));
4847
+ rootBlock.consumers.filter((cid2) => blocks[cid2].section_id === blocks[bid].section_id || !withinSection).forEach((cid2) => getDependencies(cid2, blocks, acc, false, crawlDown2, withinSection, visited));
4842
4848
  });
4843
4849
  }
4844
4850
  return acc;
@@ -4871,22 +4877,31 @@ var runConsumersV2 = async (blocks, sections, bid, formatterFunctions, blockCont
4871
4877
  const statusById = { ...initialState4.status ?? {} };
4872
4878
  const parsedBlockContext = parseBlockContext(blockContext);
4873
4879
  const attributes = parsedBlockContext.variables;
4874
- const rootBlocks = bid ? { [bid]: blocks[bid] } : sections.reduce((rootBlocks2, { id }) => ({ ...rootBlocks2, ...getRootBlocksForSection_default(id, blocks) }), {});
4875
- if (verbose7)
4876
- console.log("%crunConsumers: running root blocks", "color:blue", Object.keys(rootBlocks));
4880
+ const rootBlocks = bid ? { [bid]: blocks[bid] } : (sections ?? []).reduce((rootBlocks2, { id }) => ({ ...rootBlocks2, ...getRootBlocksForSection_default(id, blocks) }), {});
4881
+ if (debug)
4882
+ console.log("%crunConsumers: running root blocks", "color:purple", Object.keys(rootBlocks));
4877
4883
  const withinSection = mode === "section";
4878
4884
  const blockDeps = Object.keys(rootBlocks).reduce((deps, id) => {
4879
- if (verbose7)
4880
- console.log("%cresolving dependencies for", "color:red", id);
4885
+ const start = /* @__PURE__ */ new Date();
4881
4886
  const dependencies = getDependencies(Number(id), blocks, [], !bid, true, withinSection).map((rel) => rel.split("-"));
4887
+ const end = /* @__PURE__ */ new Date();
4888
+ const elapsedTime = (end.getTime() - start.getTime()) / 1e3;
4889
+ if (debug)
4890
+ console.log(
4891
+ `%cResolved dependencies for ${id} (${elapsedTime.toFixed(2)}s)`,
4892
+ "color:blue"
4893
+ );
4882
4894
  return [...deps, ...dependencies];
4883
4895
  }, []);
4896
+ const startToposort = /* @__PURE__ */ new Date();
4884
4897
  const orderedDAG = Object.keys(rootBlocks).reduce(
4885
4898
  (orderedDAG2, bid2) => orderedDAG2.includes(bid2) ? [...orderedDAG2] : [bid2, ...orderedDAG2],
4886
4899
  toposort(blockDeps)
4887
4900
  );
4888
- if (verbose7)
4889
- console.log("resolved DAG: ", orderedDAG);
4901
+ const endToposort = /* @__PURE__ */ new Date();
4902
+ const toposortTime = (endToposort.getTime() - startToposort.getTime()) / 1e3;
4903
+ if (debug)
4904
+ console.log(`%cResolved DAG (${toposortTime.toFixed(2)}s): `, "color:green", orderedDAG);
4890
4905
  async function runTasksInParallel(executionOrder, blocks2) {
4891
4906
  const runningBlocks = /* @__PURE__ */ new Map();
4892
4907
  for (const bid2 of executionOrder) {
@@ -4894,22 +4909,20 @@ var runConsumersV2 = async (blocks, sections, bid, formatterFunctions, blockCont
4894
4909
  const dependenciesDone = Promise.all(
4895
4910
  block.inputs.filter((iid) => executionOrder.includes(String(iid))).map((iid) => runningBlocks.get(Number(iid)))
4896
4911
  );
4897
- const runningTask = dependenciesDone.then(() => {
4912
+ const runningTask = dependenciesDone.then(async () => {
4898
4913
  const variables = block.inputs.reduce((acc, d) => ({ ...acc, ...variablesById[d] }), attributes);
4899
- const inputNotAllowed = block.inputs.find((iid) => !statusById[iid].allowed || statusById[iid].hiddenByCascade);
4914
+ const inputNotAllowed = block.inputs.find(
4915
+ (iid) => !statusById[iid].allowed || statusById[iid].hiddenByCascade
4916
+ );
4900
4917
  if (inputNotAllowed) {
4901
4918
  statusById[bid2] = {
4902
- api: null,
4903
- duration: null,
4904
- resp: null,
4905
- log: [],
4906
- error: null,
4907
4919
  allowed: false,
4908
4920
  hiddenByCascade: statusById[inputNotAllowed].hiddenByCascade || inputNotAllowed
4909
4921
  };
4910
4922
  return;
4911
4923
  }
4912
- return runSingleBlock(
4924
+ const startBlock = /* @__PURE__ */ new Date();
4925
+ const blockResult = await runSingleBlock(
4913
4926
  block,
4914
4927
  formatterFunctions,
4915
4928
  {
@@ -4918,15 +4931,32 @@ var runConsumersV2 = async (blocks, sections, bid, formatterFunctions, blockCont
4918
4931
  },
4919
4932
  readMemberFn
4920
4933
  ).then(({ outputVariables, status }) => {
4921
- variablesById[bid2] = outputVariables;
4922
- statusById[bid2] = status;
4934
+ if (
4935
+ // store output variables for block that:
4936
+ block.consumers.length > 0 && status.allowed && Object.keys(outputVariables).length > 0
4937
+ )
4938
+ variablesById[bid2] = outputVariables;
4939
+ statusById[bid2] = mode === "report" ? { allowed: status.allowed } : status;
4923
4940
  });
4941
+ const endBlock = /* @__PURE__ */ new Date();
4942
+ const blockTime = (endBlock.getTime() - startBlock.getTime()) / 1e3;
4943
+ if (debug)
4944
+ console.log(
4945
+ `%cBlock ${block.id} resolved in ${blockTime.toFixed(3)}s`,
4946
+ blockTime > 500 ? "color:red" : "color:green"
4947
+ );
4948
+ return blockResult;
4924
4949
  });
4925
4950
  runningBlocks.set(Number(bid2), runningTask);
4926
4951
  }
4927
4952
  await Promise.all(Array.from(runningBlocks.values()));
4928
4953
  }
4954
+ const startVariables = /* @__PURE__ */ new Date();
4929
4955
  await runTasksInParallel(orderedDAG, blocks);
4956
+ const endVariables = /* @__PURE__ */ new Date();
4957
+ const variablesTime = (endVariables.getTime() - startVariables.getTime()) / 1e3;
4958
+ if (debug)
4959
+ console.log(`%cResolved Variables (${variablesTime.toFixed(2)}s): `, "color:green");
4930
4960
  return {
4931
4961
  variables: { ...initialState4.variables ?? {}, ...variablesById },
4932
4962
  status: { ...initialState4.status ?? {}, ...statusById }
@@ -5269,15 +5299,12 @@ var recordsSlice = createSlice({
5269
5299
  */
5270
5300
  setReadRequest(state, action) {
5271
5301
  const req = action.payload;
5272
- state.requests[req.type][req.key] = req;
5302
+ if (req.type !== "report")
5303
+ state.requests[req.type][req.key] = req;
5273
5304
  if (req.status === "SUCCESS" /* SUCCESS */) {
5274
5305
  const newEntities = normalizeEntity(req.type, req.data);
5275
5306
  const nextEntities = combineRecords(state.entities, newEntities);
5276
5307
  state.entities = nextEntities;
5277
- if (req.type === "report") {
5278
- state.reports = Object.values(nextEntities.report).map((item) => item.id);
5279
- state.tree = req.data;
5280
- }
5281
5308
  }
5282
5309
  },
5283
5310
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datawheel/bespoke",
3
- "version": "0.1.30",
3
+ "version": "0.1.32",
4
4
  "description": "Content management system for creating automated data reports",
5
5
  "exports": {
6
6
  ".": {
@@ -103,7 +103,7 @@
103
103
  "slugify": "^1.6.5",
104
104
  "toposort": "^2.0.2",
105
105
  "unsplash-js": "^7.0.15",
106
- "xlsx": "^0.18.5",
106
+ "xlsx": "https://cdn.sheetjs.com/xlsx-0.19.3/xlsx-0.19.3.tgz",
107
107
  "yn": "^5.0.0"
108
108
  },
109
109
  "devDependencies": {