@datawheel/bespoke 0.5.1 → 0.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Group, Stack, Text, useMantineTheme, List, Modal, Button, Input, MantineProvider, Box, Navbar, ScrollArea, Avatar, AppShell, UnstyledButton, ThemeIcon, LoadingOverlay, Skeleton, Flex, packSx, Menu, Card, Center, Space, Badge, Title, Tabs, TextInput, Code, Loader, Divider, SegmentedControl, Select, ActionIcon, Textarea, Paper, Alert, Container, Grid, Tooltip, createStyles, MultiSelect, Anchor, Checkbox, MediaQuery, Affix, rem, Popover, Radio, Switch, Drawer, Overlay, NumberInput, Autocomplete, Notification, Image, Accordion, Header, px, FileInput, SimpleGrid, Burger, Collapse, HoverCard, CopyButton, Breadcrumbs, Col } from '@mantine/core';
1
+ import { Group, Stack, Text, useMantineTheme, List, Modal, Button, Input, MantineProvider, Box, Navbar, ScrollArea, Avatar, AppShell, UnstyledButton, ThemeIcon, LoadingOverlay, Skeleton, Flex, packSx, Menu, Card, Center, Space, Badge, Title, Tabs, TextInput, Code, Loader, Divider, SegmentedControl, Select, ActionIcon, Textarea, Paper, Alert, Container, Grid, Tooltip, createStyles, MultiSelect, Anchor, Checkbox, MediaQuery, Affix, rem, Popover, Radio, Switch, Drawer, Overlay, NumberInput, Autocomplete, Notification, Image, Accordion, Header, px, FileInput, SimpleGrid, Burger, Collapse, CopyButton, HoverCard, Breadcrumbs, Col } from '@mantine/core';
2
2
  import React, { forwardRef, useState, useCallback, useRef, useEffect, createContext, useContext, useMemo, Fragment as Fragment$1, createElement, memo, useImperativeHandle } from 'react';
3
3
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
4
4
  import axios from 'axios';
@@ -22,7 +22,7 @@ import { Notifications, notifications } from '@mantine/notifications';
22
22
  import { useDispatch, useSelector } from 'react-redux';
23
23
  import Router, { useRouter } from 'next/router';
24
24
  import { useClickOutside, useDisclosure, useDebouncedValue, useSetState, useUncontrolled, useHotkeys, useListState, randomId, getHotkeyHandler, useMediaQuery, useMergedRef, useFullscreen } from '@mantine/hooks';
25
- import { IconDice, IconBoxMultiple, IconEyeOff, IconChevronDown, IconBallpen, IconDatabase, IconMathFunction, IconUsers, IconLogout, IconHeading, IconApi, IconPercentage, IconChartBar, IconAlignLeft, IconSelector, IconPhoto, IconTable, IconUserCircle, IconEdit, IconServer, IconPencil, IconAlertCircle, IconCircleCheck, IconPlayerPlay, IconTrash, IconCircleX, IconPlus, IconFileAnalytics, IconHome, IconSearch, IconX, IconRefresh, IconDownload, IconCircleDashed, IconLanguage, IconSettingsFilled, IconEye, IconWorldUpload, IconBraces, IconClockHour2, IconAugmentedReality, IconGitMerge, IconGripHorizontal, IconChevronLeft, IconChevronRight, IconListCheck, IconPolaroid, IconCircleMinus, IconInfoCircle, IconGripVertical, IconCamera, IconShare, IconQuestionMark, IconCirclePlus, IconLogin, IconWorld, IconLock, IconCopy, IconBinaryTree, IconVariable, IconArrowRightCircle, IconPhotoFilled, IconFileUpload, IconIndentIncrease, IconCodeDots, IconUpload, IconCodePlus, IconLink, IconSparkles, IconClipboardCheck, IconClipboardCopy, IconExternalLink, IconFileTypeCsv, IconFileTypeJs, IconFileTypeXls, IconTemplate, IconCode, IconPalette, IconBold, IconItalic, IconUnderline, IconAlignCenter, IconAlignRight, IconAlignJustified, IconArrowBackUp, IconArrowForwardUp, IconCheck, IconLanguageOff, IconTriangleInvertedFilled, IconDeviceFloppy, IconSettings, IconMinimize, IconMaximize, IconGlobe, IconLinkOff } from '@tabler/icons-react';
25
+ import { IconDice, IconBoxMultiple, IconEyeOff, IconChevronDown, IconBallpen, IconDatabase, IconMathFunction, IconUsers, IconLogout, IconHeading, IconApi, IconPercentage, IconChartBar, IconAlignLeft, IconSelector, IconPhoto, IconTable, IconUserCircle, IconEdit, IconServer, IconPencil, IconAlertCircle, IconCircleCheck, IconPlayerPlay, IconTrash, IconCircleX, IconPlus, IconFileAnalytics, IconHome, IconSearch, IconX, IconRefresh, IconDownload, IconCircleDashed, IconLanguage, IconSettingsFilled, IconEye, IconWorldUpload, IconBraces, IconClockHour2, IconAugmentedReality, IconGitMerge, IconGripHorizontal, IconChevronLeft, IconChevronRight, IconListCheck, IconPolaroid, IconCircleMinus, IconInfoCircle, IconGripVertical, IconCamera, IconShare, IconQuestionMark, IconCirclePlus, IconLogin, IconWorld, IconLock, IconCopy, IconBinaryTree, IconVariable, IconArrowRightCircle, IconPhotoFilled, IconFileUpload, IconIndentIncrease, IconCodeDots, IconUpload, IconCheck, IconCodePlus, IconLink, IconSparkles, IconClipboardCheck, IconClipboardCopy, IconExternalLink, IconFileTypeCsv, IconFileTypeJs, IconFileTypeXls, IconTemplate, IconCode, IconPalette, IconBold, IconItalic, IconUnderline, IconAlignCenter, IconAlignRight, IconAlignJustified, IconArrowBackUp, IconArrowForwardUp, IconLanguageOff, IconTriangleInvertedFilled, IconDeviceFloppy, IconSettings, IconMinimize, IconMaximize, IconGlobe, IconLinkOff } from '@tabler/icons-react';
26
26
  import Link from 'next/link';
27
27
  import parse2, { Element as Element$1, domToReact, Text as Text$1 } from 'html-react-parser';
28
28
  import { UserProvider, withPageAuthRequired, useUser } from '@auth0/nextjs-auth0/client';
@@ -1369,7 +1369,7 @@ function ApiFetchException(message) {
1369
1369
  this.message = message;
1370
1370
  this.name = "ApiFetchException";
1371
1371
  }
1372
- async function apiFetch(url, settings, readMemberFn, locale = "en") {
1372
+ async function apiFetch(url, readMemberFn, locale = "en", block) {
1373
1373
  const start2 = Date.now();
1374
1374
  let finalUrl = url;
1375
1375
  let finalData;
@@ -1379,6 +1379,7 @@ async function apiFetch(url, settings, readMemberFn, locale = "en") {
1379
1379
  const slugRegex = new RegExp(`[?&]bespoke_slugs=${parsedQuery.get("bespoke_slugs")}`, "g");
1380
1380
  finalUrl = finalUrl.replace(slugRegex, "");
1381
1381
  }
1382
+ const settings = block ? block.settings : {};
1382
1383
  const useProxy = settings && settings.useProxy && settings.useProxy === "true" ? true : false;
1383
1384
  if (typeof window === "object" && useProxy) {
1384
1385
  const result = await urlProxy({ url: finalUrl, timeout: apiFetchTimeout });
@@ -1437,7 +1438,12 @@ async function apiFetch(url, settings, readMemberFn, locale = "en") {
1437
1438
  }
1438
1439
  return data && Array.isArray(data) ? data : [];
1439
1440
  }).catch((err) => {
1440
- console.log("Error hydrating slugs", err);
1441
+ console.error(`Error hydrating slugs for Variant: ${variantId}, Key: ${keyId}, Accessor: ${accessor}`);
1442
+ console.error(` Url ${url}`);
1443
+ if (block) {
1444
+ console.error(` From Section ${block.section_id} and Block ${block.id}`);
1445
+ }
1446
+ console.error(err);
1441
1447
  return [];
1442
1448
  });
1443
1449
  if (bespokeMembers) {
@@ -1490,9 +1496,9 @@ async function runSingleBlock(block, formatterFunctions, blockContext, readMembe
1490
1496
  apiPromisesList.push(
1491
1497
  apiFetch(
1492
1498
  apiUrl,
1493
- block.settings,
1494
1499
  readMemberFn,
1495
- locale
1500
+ locale,
1501
+ block
1496
1502
  ).catch(
1497
1503
  (e) => {
1498
1504
  throw new BlockException(` Excecution of ${block.type}-${block.id} in section ${block.section_id} in report ${variables.report_name} (${variables.report_id}) failed.
@@ -1892,6 +1898,7 @@ function previewsToAttributes(previews = []) {
1892
1898
  const i2 = index + 1;
1893
1899
  attributes[`id${i2}`] = preview.id;
1894
1900
  attributes[`name${i2}`] = preview.name;
1901
+ attributes[`slug${i2}`] = preview.slug;
1895
1902
  if ("attributes" in preview) {
1896
1903
  Object.keys(preview.attributes).forEach((key) => {
1897
1904
  attributes[`attr_${key}${i2}`] = preview.attributes[key];
@@ -1899,6 +1906,7 @@ function previewsToAttributes(previews = []) {
1899
1906
  }
1900
1907
  attributes[`variant_id${i2}`] = preview.variant_id;
1901
1908
  attributes[`variant_name${i2}`] = preview.variant_name;
1909
+ attributes[`variant_slug${i2}`] = preview.variant_slug;
1902
1910
  attributes[`dimension_id${i2}`] = preview.dimension_id;
1903
1911
  attributes[`dimension_name${i2}`] = preview.dimension_name;
1904
1912
  attributes["report_id"] = preview.report_id;
@@ -4624,10 +4632,11 @@ function RichText({
4624
4632
  size = "md",
4625
4633
  html,
4626
4634
  style = {},
4627
- append = /* @__PURE__ */ jsx(Fragment, {})
4635
+ append = /* @__PURE__ */ jsx(Fragment, {}),
4636
+ isPreview = false
4628
4637
  }) {
4629
4638
  const parentProps = getParentNodeProps(html);
4630
- const paragraphHTML = replaceTags(html);
4639
+ const paragraphHTML = replaceTags(html, isPreview);
4631
4640
  const textAlign = style && style.align ? String(style.align) : "left";
4632
4641
  let Wrapper = Text;
4633
4642
  let extraProps = {};
@@ -4670,7 +4679,7 @@ var init_RichText = __esm({
4670
4679
  parse2(htmlString, options);
4671
4680
  return parentProps;
4672
4681
  };
4673
- replaceTags = (stringHTML = "-Empty text-") => {
4682
+ replaceTags = (stringHTML = "", isPreview = false) => {
4674
4683
  const options = {
4675
4684
  replace: (domNode) => {
4676
4685
  const { type } = domNode;
@@ -4680,16 +4689,26 @@ var init_RichText = __esm({
4680
4689
  return /* @__PURE__ */ jsx(Fragment, { children: domToReact(children, options) });
4681
4690
  }
4682
4691
  if (name === "a") {
4683
- const { href } = attribs;
4692
+ const { href, target } = attribs;
4684
4693
  const isExternal = href.indexOf("http") === 0 ? true : false;
4685
- return /* @__PURE__ */ jsx(
4694
+ const isBlank = target && target === "_blank" ? true : false;
4695
+ const anchorElement = /* @__PURE__ */ jsx(
4696
+ Anchor,
4697
+ {
4698
+ ...attribs,
4699
+ href: attribs.href,
4700
+ target: "_blank",
4701
+ children: domToReact(children, options)
4702
+ }
4703
+ );
4704
+ return isExternal || isBlank || isPreview ? anchorElement : /* @__PURE__ */ jsx(
4686
4705
  Link,
4687
4706
  {
4688
4707
  ...attribs,
4689
4708
  href: attribs.href,
4690
- target: isExternal ? "_blank" : void 0,
4691
4709
  prefetch: false,
4692
- children: domToReact(children, options)
4710
+ passHref: true,
4711
+ children: anchorElement
4693
4712
  }
4694
4713
  );
4695
4714
  } else if (name === "strong") {
@@ -4716,7 +4735,7 @@ var init_RichText = __esm({
4716
4735
  function InnerTooltip(props) {
4717
4736
  const { content, style = {} } = props;
4718
4737
  const print = usePrint();
4719
- const contentObj = /* @__PURE__ */ jsx(RichText, { component: "span", html: content });
4738
+ const contentObj = /* @__PURE__ */ jsx(RichText, { component: "span", html: content, isPreview: true });
4720
4739
  const element = useMemo(() => {
4721
4740
  return print ? /* @__PURE__ */ jsx("div", { className: "bespoke-inner-tooltip-notification", children: /* @__PURE__ */ jsx(
4722
4741
  Notification,
@@ -5449,7 +5468,7 @@ function Selector(config) {
5449
5468
  setMultiValue(parseMultiValue(value2, defaultValue));
5450
5469
  }
5451
5470
  }
5452
- }, [window.location.search]);
5471
+ }, [window.location.search, defaultValue]);
5453
5472
  return /* @__PURE__ */ jsxs(Fragment, { children: [
5454
5473
  /* @__PURE__ */ jsx(MediaQuery, { query: "print", styles: { display: "none" }, children: /* @__PURE__ */ jsx(Box, { children: {
5455
5474
  [SELECTOR_TYPES.SINGLE]: component === SELECTOR_COMPONENTS.SEGMENTED_CONTROL ? /* @__PURE__ */ jsxs("div", { children: [
@@ -12511,14 +12530,17 @@ function TranslationHeader({ locale, content }) {
12511
12530
  children: locale.toUpperCase()
12512
12531
  }
12513
12532
  ),
12514
- /* @__PURE__ */ jsx(CopyButton, { value: content, timeout: 2e3, children: ({ copied, copy }) => /* @__PURE__ */ jsx(Tooltip, { label: copied ? "Copied" : "Copy", withArrow: true, position: "right", children: /* @__PURE__ */ jsx(ActionIcon, { size: "xs", color: copied ? "teal" : "gray", variant: "subtle", onClick: () => {
12515
- copy();
12516
- const blobInput = new Blob([content], { type: "text/html" });
12517
- const clipboardItemInput = new ClipboardItem({ "text/html": blobInput });
12518
- navigator.clipboard.write([clipboardItemInput]);
12519
- }, children: copied ? /* @__PURE__ */ jsx(IconCheck, { style: { width: rem(16) } }) : /* @__PURE__ */ jsx(IconCopy, { style: { width: rem(16) } }) }) }) })
12533
+ /* @__PURE__ */ jsx(CopyBtn, { content })
12520
12534
  ] });
12521
12535
  }
12536
+ function CopyBtn({ content }) {
12537
+ return /* @__PURE__ */ jsx(CopyButton, { value: content, timeout: 2e3, children: ({ copied, copy }) => /* @__PURE__ */ jsx(Tooltip, { label: copied ? "Copied" : "Copy", withArrow: true, position: "right", children: /* @__PURE__ */ jsx(ActionIcon, { size: "xs", color: copied ? "teal" : "gray", variant: "subtle", onClick: () => {
12538
+ copy();
12539
+ const blobInput = new Blob([content], { type: "text/html" });
12540
+ const clipboardItemInput = new ClipboardItem({ "text/html": blobInput });
12541
+ navigator.clipboard.write([clipboardItemInput]);
12542
+ }, children: copied ? /* @__PURE__ */ jsx(IconCheck, { style: { width: rem(16) } }) : /* @__PURE__ */ jsx(IconCopy, { style: { width: rem(16) } }) }) }) });
12543
+ }
12522
12544
  function ReferenceTranslation({ referenceLocale, referenceContent }) {
12523
12545
  return /* @__PURE__ */ jsx(Stack, { spacing: "xs", children: /* @__PURE__ */ jsx(
12524
12546
  BespokeTextEditor,
@@ -12563,6 +12585,7 @@ function FieldWithTranslation({
12563
12585
  children: titleCase(field)
12564
12586
  }
12565
12587
  ),
12588
+ toolbarRightSection: /* @__PURE__ */ jsx(CopyBtn, { content: value }),
12566
12589
  toolbarOptions: {
12567
12590
  history: true,
12568
12591
  alignment: field !== "tooltip",
@@ -13536,6 +13559,7 @@ function TableUI(props) {
13536
13559
  Object.keys(item).forEach((key) => columns.add(key));
13537
13560
  });
13538
13561
  const stateColumns = {};
13562
+ const variableColumns = [];
13539
13563
  if (simpleState.columns && Object.keys(simpleState.columns)) {
13540
13564
  simpleState.columns.forEach((column) => {
13541
13565
  stateColumns[column.accessorKey] = column.header;
@@ -13553,11 +13577,14 @@ function TableUI(props) {
13553
13577
  key: randomId(),
13554
13578
  label: key
13555
13579
  });
13580
+ variableColumns.push(key);
13556
13581
  });
13557
13582
  customLabelsHandler.setState(keyLabels);
13558
13583
  selectedColumnsHandler.setState(keyList);
13559
13584
  const { initialState: initialState4 = {}, state = {}, mantinePaginationProps = {}, ...props2 } = simpleState.tableProps || {};
13560
- setColumnOrder(state.columnOrder || []);
13585
+ const currentColumnOrder = state.columnOrder.filter((col) => variableColumns.includes(col));
13586
+ currentColumnOrder.push(variableColumns.filter((col) => !state.columnOrder.includes(col)));
13587
+ setColumnOrder(currentColumnOrder.flat() || []);
13561
13588
  setPaginationProps(mantinePaginationProps);
13562
13589
  setPaginationState(initialState4.pagination);
13563
13590
  setTableProps({ ...props2 });
@@ -14054,7 +14081,8 @@ function ParagraphPreview({ paragraph, tooltip, settings }) {
14054
14081
  component: "p",
14055
14082
  html: paragraphHTML,
14056
14083
  style: settings,
14057
- append: tooltipObj
14084
+ append: tooltipObj,
14085
+ isPreview: true
14058
14086
  }
14059
14087
  );
14060
14088
  }
@@ -14096,9 +14124,9 @@ function StatPreview({
14096
14124
  }
14097
14125
  ),
14098
14126
  /* @__PURE__ */ jsxs("div", { className: "bespoke-Stat-wrapper", children: [
14099
- /* @__PURE__ */ jsx(RichText, { component: "div", size: "md", html: titleHTML, style: settings }),
14100
- /* @__PURE__ */ jsx(RichText, { component: "div", size: "xl", html: valueHTML, style: settings }),
14101
- /* @__PURE__ */ jsx(RichText, { component: "div", size: "sm", html: subtitleHTML, style: settings })
14127
+ /* @__PURE__ */ jsx(RichText, { component: "div", size: "md", html: titleHTML, style: settings, isPreview: true }),
14128
+ /* @__PURE__ */ jsx(RichText, { component: "div", size: "xl", html: valueHTML, style: settings, isPreview: true }),
14129
+ /* @__PURE__ */ jsx(RichText, { component: "div", size: "sm", html: subtitleHTML, style: settings, isPreview: true })
14102
14130
  ] }, "stat"),
14103
14131
  tooltip && /* @__PURE__ */ jsx(InnerTooltip, { content: tooltip })
14104
14132
  ] }) });
@@ -14116,7 +14144,7 @@ function SubtitlePreview({ subtitle, tooltip, settings }) {
14116
14144
  const subtitleHTML = sanitizeBlockContent_default(subtitle) || "<span class='cr-block-placeholder'>Small subtitle.</span>";
14117
14145
  const align = settings && settings.align ? settings.align : "left";
14118
14146
  return /* @__PURE__ */ jsxs(Group, { spacing: "sm", position: getAlignForGroup(align), children: [
14119
- /* @__PURE__ */ jsx(RichText, { size: "sm", html: subtitleHTML }),
14147
+ /* @__PURE__ */ jsx(RichText, { size: "sm", html: subtitleHTML, isPreview: true }),
14120
14148
  tooltip && /* @__PURE__ */ jsx(InnerTooltip, { content: tooltip, style: { display: "inline-flex" } })
14121
14149
  ] });
14122
14150
  }
@@ -14165,7 +14193,8 @@ function TitlePreview({
14165
14193
  component: "h",
14166
14194
  order: order2,
14167
14195
  style: settings,
14168
- html: titleHTML
14196
+ html: titleHTML,
14197
+ isPreview: true
14169
14198
  }
14170
14199
  ),
14171
14200
  searchOnClick && /* @__PURE__ */ jsx(Tooltip, { label: `Click will open '${searchType}' report search`, children: /* @__PURE__ */ jsx(ActionIcon, { variant: "light", children: /* @__PURE__ */ jsx(IconSearch, { size: 20 }) }) }),
@@ -14216,7 +14245,7 @@ function SelectorPreview(config) {
14216
14245
  dispatch(setQueryParam(resource, selectorIdentifier, e.join()));
14217
14246
  };
14218
14247
  const { name, settings } = config;
14219
- const label = config.hideLabel === "true" ? /* @__PURE__ */ jsx(Fragment, {}) : /* @__PURE__ */ jsx(RichText, { component: "div", html: name, size: "sm", style: settings });
14248
+ const label = config.hideLabel === "true" ? /* @__PURE__ */ jsx(Fragment, {}) : /* @__PURE__ */ jsx(RichText, { component: "div", html: name, size: "sm", style: settings, isPreview: true });
14220
14249
  return {
14221
14250
  [SELECTOR_TYPES.SINGLE]: config.component === SELECTOR_COMPONENTS.SEGMENTED_CONTROL ? /* @__PURE__ */ jsxs("div", { children: [
14222
14251
  label,
@@ -14524,9 +14553,9 @@ function ImagePreview(props) {
14524
14553
  const align = settings && settings.align ? String(settings.align) : "left";
14525
14554
  if (descText && descText.length || title && title.length) {
14526
14555
  element = /* @__PURE__ */ jsxs(Stack, { className: "bespoke-Image-wrapper", align: getAlignForStack(align), spacing: "sm", children: [
14527
- title && /* @__PURE__ */ jsx(RichText, { component: "p", html: title, style: settings }),
14556
+ title && /* @__PURE__ */ jsx(RichText, { component: "p", html: title, style: settings, isPreview: true }),
14528
14557
  element,
14529
- desc && /* @__PURE__ */ jsx(RichText, { component: "p", size: "sm", html: desc, style: settings })
14558
+ desc && /* @__PURE__ */ jsx(RichText, { component: "p", size: "sm", html: desc, style: settings, isPreview: true })
14530
14559
  ] });
14531
14560
  }
14532
14561
  return srcText && altText ? element : /* @__PURE__ */ jsx(Center, { mih: 100, bg: "white", children: /* @__PURE__ */ jsx(Badge, { color: "gray", variant: "outline", children: "IMAGE" }, "type") });
@@ -14753,7 +14782,7 @@ function TablePreview({ columns = [], id, data = [], tableProps = {} }) {
14753
14782
  ), [columnsKeys, data]);
14754
14783
  const richColumns = useMemo(() => columns.map((column) => ({
14755
14784
  ...column,
14756
- Cell: ({ renderedCellValue }) => /* @__PURE__ */ jsx(RichText, { html: `${renderedCellValue}` }),
14785
+ Cell: ({ renderedCellValue }) => /* @__PURE__ */ jsx(RichText, { html: `${renderedCellValue}`, isPreview: true }),
14757
14786
  filterFn: "contains"
14758
14787
  })), [columns]);
14759
14788
  const locale = useCurrentLocale();
@@ -15245,7 +15274,7 @@ var init_Block = __esm({
15245
15274
  });
15246
15275
 
15247
15276
  // libs/settings/section.tsx
15248
- var sectionSettings, defaultSettings2, defaultSectionSettings;
15277
+ var sectionSettings, defaultSettings2, defaultSectionSettings, getOptionsSettings;
15249
15278
  var init_section2 = __esm({
15250
15279
  "libs/settings/section.tsx"() {
15251
15280
  init_esm_shims();
@@ -15302,8 +15331,16 @@ var init_section2 = __esm({
15302
15331
  defaultSectionSettings = {
15303
15332
  memberImageBg: false,
15304
15333
  optionsMenu: false,
15334
+ optionData: true,
15335
+ optionImage: true,
15336
+ optionShare: true,
15305
15337
  ...defaultSettings2
15306
15338
  };
15339
+ getOptionsSettings = (settings) => ({
15340
+ data: typeof settings.optionData === "boolean" ? settings.optionData : true,
15341
+ image: typeof settings.optionImage === "boolean" ? settings.optionImage : true,
15342
+ share: typeof settings.optionShare === "boolean" ? settings.optionShare : true
15343
+ });
15307
15344
  }
15308
15345
  });
15309
15346
  function SectionBackground({ previews }) {
@@ -15625,7 +15662,7 @@ function DataTab(props) {
15625
15662
  setLoadingPreview(true);
15626
15663
  const dataItem = selectedSource.data[selectedDataset];
15627
15664
  if (typeof dataItem === "string") {
15628
- apiFetch(dataItem, {}, readMemberFn, locale).then((response) => {
15665
+ apiFetch(dataItem, readMemberFn, locale).then((response) => {
15629
15666
  const dataToShare = guessResponse(response);
15630
15667
  setPreviewsSource({
15631
15668
  ...previewsSource,
@@ -16272,7 +16309,9 @@ function OptionsModal(props) {
16272
16309
  open,
16273
16310
  title
16274
16311
  } = props;
16275
- const [selectedTab, setSelectedTab] = useState(initialMode || "data");
16312
+ const settings = getOptionsSettings(section.settings);
16313
+ const availableTabs = useMemo(() => tabList.filter((tId) => settings[tId]), [settings]);
16314
+ const [selectedTab, setSelectedTab] = useState(initialMode || availableTabs[0] || "data");
16276
16315
  const optionsTranslations = useBespokeTranslations("options");
16277
16316
  useEffect(() => {
16278
16317
  if (initialMode !== selectedTab) {
@@ -16280,6 +16319,18 @@ function OptionsModal(props) {
16280
16319
  }
16281
16320
  }, [initialMode]);
16282
16321
  const translations = { ...DEFAULT_TRANSLATIONS5, ...optionsTranslations };
16322
+ const tabs = useMemo(() => {
16323
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
16324
+ /* @__PURE__ */ jsxs(Tabs.List, { children: [
16325
+ settings.data && /* @__PURE__ */ jsx(Tabs.Tab, { value: "data", children: translations.download_title }),
16326
+ settings.image && /* @__PURE__ */ jsx(Tabs.Tab, { value: "image", children: translations.image_title }),
16327
+ settings.share && /* @__PURE__ */ jsx(Tabs.Tab, { value: "share", children: translations.share_title })
16328
+ ] }),
16329
+ settings.data && /* @__PURE__ */ jsx(Tabs.Panel, { value: "data", children: /* @__PURE__ */ jsx(DataTab, { section }) }),
16330
+ settings.image && /* @__PURE__ */ jsx(Tabs.Panel, { value: "image", children: /* @__PURE__ */ jsx(ImageTab, { section }) }),
16331
+ settings.share && /* @__PURE__ */ jsx(Tabs.Panel, { value: "share", children: /* @__PURE__ */ jsx(ShareTab, { section }) })
16332
+ ] });
16333
+ }, [settings, section, translations]);
16283
16334
  return /* @__PURE__ */ jsx("div", { className: "cms-section-options-modal", children: open && /* @__PURE__ */ jsx(
16284
16335
  Modal,
16285
16336
  {
@@ -16288,28 +16339,19 @@ function OptionsModal(props) {
16288
16339
  onClose: () => onEnds(),
16289
16340
  size: "xl",
16290
16341
  title,
16291
- children: /* @__PURE__ */ jsxs(
16342
+ children: /* @__PURE__ */ jsx(
16292
16343
  Tabs,
16293
16344
  {
16294
16345
  onTabChange: setSelectedTab,
16295
16346
  value: selectedTab,
16296
16347
  keepMounted: false,
16297
- children: [
16298
- /* @__PURE__ */ jsxs(Tabs.List, { children: [
16299
- /* @__PURE__ */ jsx(Tabs.Tab, { value: "data", children: translations.download_title }),
16300
- /* @__PURE__ */ jsx(Tabs.Tab, { value: "image", children: translations.image_title }),
16301
- /* @__PURE__ */ jsx(Tabs.Tab, { value: "share", children: translations.share_title })
16302
- ] }),
16303
- /* @__PURE__ */ jsx(Tabs.Panel, { value: "data", children: /* @__PURE__ */ jsx(DataTab, { section }) }),
16304
- /* @__PURE__ */ jsx(Tabs.Panel, { value: "image", children: /* @__PURE__ */ jsx(ImageTab, { section }) }),
16305
- /* @__PURE__ */ jsx(Tabs.Panel, { value: "share", children: /* @__PURE__ */ jsx(ShareTab, { section }) })
16306
- ]
16348
+ children: tabs
16307
16349
  }
16308
16350
  )
16309
16351
  }
16310
16352
  ) });
16311
16353
  }
16312
- var DEFAULT_TRANSLATIONS5;
16354
+ var DEFAULT_TRANSLATIONS5, tabList;
16313
16355
  var init_OptionsModal = __esm({
16314
16356
  "components/options/OptionsModal.tsx"() {
16315
16357
  init_esm_shims();
@@ -16317,11 +16359,13 @@ var init_OptionsModal = __esm({
16317
16359
  init_ImageTab();
16318
16360
  init_ShareTab();
16319
16361
  init_TranslationsProvider();
16362
+ init_section2();
16320
16363
  DEFAULT_TRANSLATIONS5 = {
16321
16364
  download_title: "Download Data",
16322
16365
  image_title: "Save Image",
16323
16366
  share_title: "Share Link"
16324
16367
  };
16368
+ tabList = ["data", "image", "share"];
16325
16369
  }
16326
16370
  });
16327
16371
  function Options(props) {
@@ -16331,6 +16375,7 @@ function Options(props) {
16331
16375
  const sectionRef = useSectionRef(sectionId);
16332
16376
  const optionsTranslations = useBespokeTranslations("options");
16333
16377
  const translations = { ...DEFAULT_TRANSLATIONS5, ...optionsTranslations };
16378
+ const settings = getOptionsSettings(sectionRef.isSuccess ? sectionRef.data.settings : {});
16334
16379
  const onClickOption = (mode2) => {
16335
16380
  setMode(mode2);
16336
16381
  setOpened(true);
@@ -16340,7 +16385,7 @@ function Options(props) {
16340
16385
  };
16341
16386
  return /* @__PURE__ */ jsxs("div", { className: "cms-section-options", children: [
16342
16387
  /* @__PURE__ */ jsxs(Button.Group, { children: [
16343
- /* @__PURE__ */ jsx(
16388
+ settings.data && /* @__PURE__ */ jsx(
16344
16389
  Button,
16345
16390
  {
16346
16391
  variant: "default",
@@ -16352,7 +16397,7 @@ function Options(props) {
16352
16397
  children: /* @__PURE__ */ jsx(IconTable, { size: 16 })
16353
16398
  }
16354
16399
  ),
16355
- /* @__PURE__ */ jsx(
16400
+ settings.image && /* @__PURE__ */ jsx(
16356
16401
  Button,
16357
16402
  {
16358
16403
  variant: "default",
@@ -16364,7 +16409,7 @@ function Options(props) {
16364
16409
  children: /* @__PURE__ */ jsx(IconCamera, { size: 16 })
16365
16410
  }
16366
16411
  ),
16367
- /* @__PURE__ */ jsx(
16412
+ settings.share && /* @__PURE__ */ jsx(
16368
16413
  Button,
16369
16414
  {
16370
16415
  variant: "default",
@@ -16394,6 +16439,7 @@ var init_Options = __esm({
16394
16439
  init_OptionsModal();
16395
16440
  init_store2();
16396
16441
  init_TranslationsProvider();
16442
+ init_section2();
16397
16443
  }
16398
16444
  });
16399
16445
 
@@ -21382,7 +21428,6 @@ function BlockElement({
21382
21428
  !allowed && /* @__PURE__ */ jsx(Tooltip, { withArrow: true, label: allowedMessage, children: /* @__PURE__ */ jsx(ActionIcon, { variant: "transparent", children: /* @__PURE__ */ jsx(IconEyeOff, { size: 20 }) }) }, "allowed"),
21383
21429
  block.type !== "generator" && /* @__PURE__ */ jsx(StyleMenu_default, { id, display: "button" }, "design"),
21384
21430
  /* @__PURE__ */ jsx(ActionIcon, { onClick: () => setOpened(true), children: /* @__PURE__ */ jsx(IconPencil, { size: 20 }) }, "edit"),
21385
- /* @__PURE__ */ jsx(DeleteButton_default, { type: ENTITY_TYPES.BLOCK, id }, "cog"),
21386
21431
  /* @__PURE__ */ jsx(
21387
21432
  ActionIcon,
21388
21433
  {
@@ -21390,7 +21435,8 @@ function BlockElement({
21390
21435
  children: /* @__PURE__ */ jsx(IconCopy, { size: 20 })
21391
21436
  },
21392
21437
  "copy"
21393
- )
21438
+ ),
21439
+ /* @__PURE__ */ jsx(DeleteButton_default, { type: ENTITY_TYPES.BLOCK, id }, "cog")
21394
21440
  ]
21395
21441
  },
21396
21442
  "bc"
@@ -21433,8 +21479,8 @@ function SectionHeader({
21433
21479
  id,
21434
21480
  dragHandleProps,
21435
21481
  hidden,
21436
- optionsMenu,
21437
- onActivate
21482
+ onActivate,
21483
+ sectionSettings: sectionSettings2
21438
21484
  }) {
21439
21485
  const section = useAppSelector((state) => state.records.entities.section[id] || null);
21440
21486
  if (!section)
@@ -21467,7 +21513,7 @@ function SectionHeader({
21467
21513
  )
21468
21514
  ] }) }),
21469
21515
  /* @__PURE__ */ jsxs(Group, { children: [
21470
- optionsMenu && /* @__PURE__ */ jsxs(Flex, { justify: "flex-end", mx: 16, align: "center", gap: "xs", children: [
21516
+ sectionSettings2.optionsMenu && /* @__PURE__ */ jsxs(Flex, { justify: "flex-end", mx: 16, align: "center", gap: "xs", children: [
21471
21517
  /* @__PURE__ */ jsx(SectionResetButton, { id: section.id }),
21472
21518
  /* @__PURE__ */ jsx(Options, { sectionId: section.id, disabled: !active })
21473
21519
  ] }),
@@ -21643,7 +21689,7 @@ function SectionEditor({
21643
21689
  const blocks = useAppSelector((state) => state.records.entities.block);
21644
21690
  const previews = useAppSelector((state) => state.status.previews);
21645
21691
  const cloneState = useCloneStatusSelector("section", section.id);
21646
- const { memberImageBg, optionsMenu, hidden, columnSettings = {} } = section.settings;
21692
+ const { memberImageBg, hidden, columnSettings = {} } = section.settings;
21647
21693
  const isActive = activeEntity.type === "section" && activeEntity.id === section.id;
21648
21694
  const { inputs, consumers } = useMemo(() => {
21649
21695
  if (hoverBlock) {
@@ -21735,7 +21781,7 @@ function SectionEditor({
21735
21781
  setShowGenerators,
21736
21782
  hasGenerators: Boolean(generators.length)
21737
21783
  },
21738
- optionsMenu
21784
+ sectionSettings: section.settings
21739
21785
  }
21740
21786
  ),
21741
21787
  /* @__PURE__ */ jsxs(
@@ -22511,35 +22557,54 @@ function SectionSettingsPanel() {
22511
22557
  const report = useAppSelector((state) => activeSection && state.records.entities.report[section.report_id]);
22512
22558
  const reportSettings = report?.settings;
22513
22559
  const { settings, updateSettings } = useSectionSettings(activeSection);
22560
+ const optionsMenu = useMemo(() => [
22561
+ { key: "optionData", label: "Download data (csv, xls, json)" },
22562
+ { key: "optionImage", label: "Save Image" },
22563
+ { key: "optionShare", label: "Share link" }
22564
+ ], []);
22565
+ const optionsList = useMemo(() => optionsMenu.map((om) => /* @__PURE__ */ jsxs(List.Item, { children: [
22566
+ om.label,
22567
+ "."
22568
+ ] }, `item-${om.key}`)), [optionsMenu]);
22514
22569
  if (activeEntity.type !== "section" || !activeEntity.id)
22515
22570
  return null;
22516
22571
  return /* @__PURE__ */ jsxs("div", { children: [
22517
22572
  /* @__PURE__ */ jsx(NameInput, { activeSection }),
22518
22573
  /* @__PURE__ */ jsx(Divider, { my: "sm" }),
22519
22574
  /* @__PURE__ */ jsxs(Stack, { mt: "sm", children: [
22520
- /* @__PURE__ */ jsx(
22521
- BespokeInfoTooltip,
22522
- {
22523
- target: /* @__PURE__ */ jsx(
22575
+ /* @__PURE__ */ jsxs(Stack, { spacing: "0.4rem", children: [
22576
+ /* @__PURE__ */ jsx(
22577
+ BespokeInfoTooltip,
22578
+ {
22579
+ target: /* @__PURE__ */ jsx(
22580
+ Switch,
22581
+ {
22582
+ radius: "sm",
22583
+ label: "Show export menu.",
22584
+ checked: settings.optionsMenu,
22585
+ onChange: (e) => updateSettings("optionsMenu", e.currentTarget.checked)
22586
+ }
22587
+ ),
22588
+ dropdown: /* @__PURE__ */ jsxs(Box, { children: [
22589
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 700, children: "Export Menu" }),
22590
+ /* @__PURE__ */ jsx(Text, { size: "sm", children: "The Export Menu will allow users to:" }),
22591
+ /* @__PURE__ */ jsx(List, { withPadding: true, size: "sm", children: optionsList })
22592
+ ] })
22593
+ }
22594
+ ),
22595
+ settings.optionsMenu && /* @__PURE__ */ jsx(Stack, { spacing: "0.5rem", pl: "md", children: optionsMenu.map(
22596
+ (optionMenu) => /* @__PURE__ */ jsx(
22524
22597
  Switch,
22525
22598
  {
22526
22599
  radius: "sm",
22527
- label: "Show export menu.",
22528
- checked: settings.optionsMenu,
22529
- onChange: (e) => updateSettings("optionsMenu", e.currentTarget.checked)
22530
- }
22531
- ),
22532
- dropdown: /* @__PURE__ */ jsxs(Box, { children: [
22533
- /* @__PURE__ */ jsx(Text, { size: "sm", fw: 700, children: "Export Menu" }),
22534
- /* @__PURE__ */ jsx(Text, { size: "sm", children: "The Export Menu will allow users to:" }),
22535
- /* @__PURE__ */ jsxs(List, { withPadding: true, size: "sm", children: [
22536
- /* @__PURE__ */ jsx(List.Item, { children: "Download data (csv, xls, json)." }),
22537
- /* @__PURE__ */ jsx(List.Item, { children: "Save Image" }),
22538
- /* @__PURE__ */ jsx(List.Item, { children: "Share Link" })
22539
- ] })
22540
- ] })
22541
- }
22542
- ),
22600
+ label: optionMenu.label,
22601
+ checked: typeof settings[optionMenu.key] === "undefined" ? settings.optionsMenu : settings[optionMenu.key],
22602
+ onChange: (e) => updateSettings(optionMenu.key, e.currentTarget.checked)
22603
+ },
22604
+ optionMenu.key
22605
+ )
22606
+ ) })
22607
+ ] }),
22543
22608
  /* @__PURE__ */ jsx(
22544
22609
  BespokeInfoTooltip,
22545
22610
  {
package/dist/server.js CHANGED
@@ -4957,7 +4957,7 @@ function ApiFetchException(message) {
4957
4957
  this.name = "ApiFetchException";
4958
4958
  }
4959
4959
  var apiFetchTimeout = process.env.NEXT_PUBLIC_REPORTS_API_FETCH_TIMEOUT || 0;
4960
- async function apiFetch(url, settings, readMemberFn, locale = "en") {
4960
+ async function apiFetch(url, readMemberFn, locale = "en", block) {
4961
4961
  const start = Date.now();
4962
4962
  let finalUrl = url;
4963
4963
  let finalData;
@@ -4967,6 +4967,7 @@ async function apiFetch(url, settings, readMemberFn, locale = "en") {
4967
4967
  const slugRegex = new RegExp(`[?&]bespoke_slugs=${parsedQuery.get("bespoke_slugs")}`, "g");
4968
4968
  finalUrl = finalUrl.replace(slugRegex, "");
4969
4969
  }
4970
+ const settings = block ? block.settings : {};
4970
4971
  const useProxy = settings && settings.useProxy && settings.useProxy === "true" ? true : false;
4971
4972
  if (typeof window === "object" && useProxy) {
4972
4973
  const result = await urlProxy({ url: finalUrl, timeout: apiFetchTimeout });
@@ -5025,7 +5026,12 @@ async function apiFetch(url, settings, readMemberFn, locale = "en") {
5025
5026
  }
5026
5027
  return data && Array.isArray(data) ? data : [];
5027
5028
  }).catch((err) => {
5028
- console.log("Error hydrating slugs", err);
5029
+ console.error(`Error hydrating slugs for Variant: ${variantId}, Key: ${keyId}, Accessor: ${accessor}`);
5030
+ console.error(` Url ${url}`);
5031
+ if (block) {
5032
+ console.error(` From Section ${block.section_id} and Block ${block.id}`);
5033
+ }
5034
+ console.error(err);
5029
5035
  return [];
5030
5036
  });
5031
5037
  if (bespokeMembers) {
@@ -5078,9 +5084,9 @@ async function runSingleBlock(block, formatterFunctions, blockContext, readMembe
5078
5084
  apiPromisesList.push(
5079
5085
  apiFetch(
5080
5086
  apiUrl,
5081
- block.settings,
5082
5087
  readMemberFn,
5083
- locale
5088
+ locale,
5089
+ block
5084
5090
  ).catch(
5085
5091
  (e) => {
5086
5092
  throw new BlockException(` Excecution of ${block.type}-${block.id} in section ${block.section_id} in report ${variables.report_name} (${variables.report_id}) failed.
@@ -5422,6 +5428,7 @@ function previewsToAttributes(previews = []) {
5422
5428
  const i2 = index + 1;
5423
5429
  attributes[`id${i2}`] = preview.id;
5424
5430
  attributes[`name${i2}`] = preview.name;
5431
+ attributes[`slug${i2}`] = preview.slug;
5425
5432
  if ("attributes" in preview) {
5426
5433
  Object.keys(preview.attributes).forEach((key) => {
5427
5434
  attributes[`attr_${key}${i2}`] = preview.attributes[key];
@@ -5429,6 +5436,7 @@ function previewsToAttributes(previews = []) {
5429
5436
  }
5430
5437
  attributes[`variant_id${i2}`] = preview.variant_id;
5431
5438
  attributes[`variant_name${i2}`] = preview.variant_name;
5439
+ attributes[`variant_slug${i2}`] = preview.variant_slug;
5432
5440
  attributes[`dimension_id${i2}`] = preview.dimension_id;
5433
5441
  attributes[`dimension_name${i2}`] = preview.dimension_name;
5434
5442
  attributes["report_id"] = preview.report_id;
@@ -7078,6 +7086,9 @@ function BespokeManagerServerSideProps(options) {
7078
7086
  ]);
7079
7087
  }
7080
7088
  }
7089
+ if (page === "metadata") {
7090
+ await dispatch(readEntity("report", { include: "dimensions" }));
7091
+ }
7081
7092
  if (page === "formatters") {
7082
7093
  await dispatch(readEntity("formatter", {}));
7083
7094
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datawheel/bespoke",
3
- "version": "0.5.1",
3
+ "version": "0.5.3",
4
4
  "description": "Content management system for creating automated data reports",
5
5
  "exports": {
6
6
  ".": {
@@ -139,6 +139,6 @@
139
139
  "tsup": "^6.6.0"
140
140
  },
141
141
  "peerDependencies": {
142
- "next": "^13.4.0"
142
+ "next": "^14.2.3"
143
143
  }
144
- }
144
+ }