@datawheel/bespoke 0.5.6 → 0.6.0-rc.1

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 +400 -298
  2. package/dist/server.js +206 -12
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -51,7 +51,6 @@ import slugifyFn from 'slugify';
51
51
  import JSZip from 'jszip';
52
52
  import { saveAs } from 'file-saver';
53
53
  import { select } from 'd3-selection';
54
- import { saveElement } from 'd3plus-export';
55
54
  import { FacebookShareButton, FacebookIcon, TwitterShareButton, TwitterIcon, TelegramShareButton, TelegramIcon, WhatsappShareButton, WhatsappIcon, LinkedinShareButton, LinkedinIcon, RedditShareButton, RedditIcon, EmailShareButton, EmailIcon } from 'react-share';
56
55
  import Head from 'next/head';
57
56
  import Editor, { useMonaco } from '@monaco-editor/react';
@@ -264,8 +263,8 @@ var init_ExploreFilters = __esm({
264
263
  });
265
264
 
266
265
  // api/http/lib.ts
267
- function http(axios10, config) {
268
- return axios10.request(config).then((response) => {
266
+ function http(axios11, config) {
267
+ return axios11.request(config).then((response) => {
269
268
  const { status, data } = response;
270
269
  return "error" in data ? { ok: false, status, error: data.error } : { ok: true, status, data: data.data };
271
270
  }, (err) => {
@@ -276,26 +275,26 @@ function http(axios10, config) {
276
275
  return { ok: false, status: 500, error: err.message };
277
276
  });
278
277
  }
279
- function httpGET(axios10, request, transformParams) {
278
+ function httpGET(axios11, request, transformParams) {
280
279
  const config = typeof request === "string" ? { url: request } : request;
281
- return (params) => http(axios10, {
280
+ return (params) => http(axios11, {
282
281
  ...config,
283
282
  method: "GET",
284
283
  params: transformParams ? transformParams(params) : params
285
284
  });
286
285
  }
287
- function httpPOST(axios10, request, transformPayload) {
286
+ function httpPOST(axios11, request, transformPayload) {
288
287
  const config = typeof request === "string" ? { url: request } : request;
289
- return (payload) => http(axios10, {
288
+ return (payload) => http(axios11, {
290
289
  ...config,
291
290
  method: "POST",
292
291
  data: transformPayload ? transformPayload(payload) : payload
293
292
  });
294
293
  }
295
- function httpDELETE(axios10, request, transformPayload) {
294
+ function httpDELETE(axios11, request, transformPayload) {
296
295
  const config = typeof request === "string" ? { url: request } : request;
297
296
  return (payload) => {
298
- return http(axios10, {
297
+ return http(axios11, {
299
298
  ...config,
300
299
  method: "DELETE",
301
300
  params: transformPayload ? transformPayload(payload) : payload
@@ -309,8 +308,8 @@ var init_lib = __esm({
309
308
  });
310
309
 
311
310
  // api/http/image/imageSave.ts
312
- function httpImageSaveFactory(axios10, provider) {
313
- return (params) => http(axios10, {
311
+ function httpImageSaveFactory(axios11, provider) {
312
+ return (params) => http(axios11, {
314
313
  method: "POST",
315
314
  url: `images/save/${provider}`,
316
315
  params: { prompt: params.prompt, provider }
@@ -324,8 +323,8 @@ var init_imageSave = __esm({
324
323
  });
325
324
 
326
325
  // api/http/image/imageSearch.ts
327
- function httpImageSearchFactory(axios10, provider) {
328
- return (params) => http(axios10, {
326
+ function httpImageSearchFactory(axios11, provider) {
327
+ return (params) => http(axios11, {
329
328
  method: "GET",
330
329
  url: `images/search/${provider}`,
331
330
  params: { prompt: params.prompt, page: params.page, provider }
@@ -339,8 +338,8 @@ var init_imageSearch = __esm({
339
338
  });
340
339
 
341
340
  // api/http/icon/listIcons.ts
342
- function httpListIconsFactory(axios10, provider) {
343
- return () => http(axios10, {
341
+ function httpListIconsFactory(axios11, provider) {
342
+ return () => http(axios11, {
344
343
  method: "GET",
345
344
  url: `list/icons/${provider}`,
346
345
  params: { provider }
@@ -354,8 +353,8 @@ var init_listIcons = __esm({
354
353
  });
355
354
 
356
355
  // api/http/icon/readIcon.ts
357
- function httpReadIconFactory(axios10, provider) {
358
- return (params) => http(axios10, {
356
+ function httpReadIconFactory(axios11, provider) {
357
+ return (params) => http(axios11, {
359
358
  method: "GET",
360
359
  url: `read/icons/${provider}/icon.svg`,
361
360
  params: { name: params.name }
@@ -368,65 +367,65 @@ var init_readIcon = __esm({
368
367
  }
369
368
  });
370
369
  function apiFactory(baseURL) {
371
- const axios10 = axios.create({ baseURL });
370
+ const axios11 = axios.create({ baseURL });
372
371
  return {
373
- createBulkBlock: httpPOST(axios10, "create/bulk/block"),
374
- createBlock: httpPOST(axios10, "create/block"),
375
- createDimension: httpPOST(axios10, "create/dimension"),
376
- createFormatter: httpPOST(axios10, "create/formatter"),
377
- createReport: httpPOST(axios10, "create/report"),
378
- createSection: httpPOST(axios10, "create/section"),
379
- createVariant: httpPOST(axios10, "create/variant"),
380
- deleteBlock: httpDELETE(axios10, "delete/block", transformDeletePayload),
381
- deleteDimension: httpDELETE(axios10, "delete/dimension", transformDeletePayload),
382
- deleteFormatter: httpDELETE(axios10, "delete/formatter", transformDeletePayload),
383
- deleteReport: httpDELETE(axios10, "delete/report", transformDeletePayload),
384
- deleteSection: httpDELETE(axios10, "delete/section", transformDeletePayload),
385
- deleteVariant: httpDELETE(axios10, "delete/variant", transformDeletePayload),
386
- readBlock: httpGET(axios10, "read/block"),
387
- readDimension: httpGET(axios10, "read/dimension"),
388
- readFormatter: httpGET(axios10, "read/formatter"),
389
- readReport: httpGET(axios10, "read/report"),
390
- readSection: httpGET(axios10, "read/section"),
391
- readVariant: httpGET(axios10, "read/variant"),
392
- updateBulkBlock: httpPOST(axios10, "update/bulk/block"),
393
- updateBlock: httpPOST(axios10, "update/block"),
394
- updateDimension: httpPOST(axios10, "update/dimension"),
395
- updateFormatter: httpPOST(axios10, "update/formatter"),
396
- updateReport: httpPOST(axios10, "update/report"),
397
- updateSection: httpPOST(axios10, "update/section"),
398
- updateVariant: httpPOST(axios10, "update/variant"),
399
- searchReport: httpGET(axios10, "search/reports"),
400
- validateVariantSlug: httpGET(axios10, "validate/variant"),
401
- readMember: httpGET(axios10, "read/members", transformReadMembers),
402
- readMemberImage: httpGET(axios10, {
372
+ createBulkBlock: httpPOST(axios11, "create/bulk/block"),
373
+ createBlock: httpPOST(axios11, "create/block"),
374
+ createDimension: httpPOST(axios11, "create/dimension"),
375
+ createFormatter: httpPOST(axios11, "create/formatter"),
376
+ createReport: httpPOST(axios11, "create/report"),
377
+ createSection: httpPOST(axios11, "create/section"),
378
+ createVariant: httpPOST(axios11, "create/variant"),
379
+ deleteBlock: httpDELETE(axios11, "delete/block", transformDeletePayload),
380
+ deleteDimension: httpDELETE(axios11, "delete/dimension", transformDeletePayload),
381
+ deleteFormatter: httpDELETE(axios11, "delete/formatter", transformDeletePayload),
382
+ deleteReport: httpDELETE(axios11, "delete/report", transformDeletePayload),
383
+ deleteSection: httpDELETE(axios11, "delete/section", transformDeletePayload),
384
+ deleteVariant: httpDELETE(axios11, "delete/variant", transformDeletePayload),
385
+ readBlock: httpGET(axios11, "read/block"),
386
+ readDimension: httpGET(axios11, "read/dimension"),
387
+ readFormatter: httpGET(axios11, "read/formatter"),
388
+ readReport: httpGET(axios11, "read/report"),
389
+ readSection: httpGET(axios11, "read/section"),
390
+ readVariant: httpGET(axios11, "read/variant"),
391
+ updateBulkBlock: httpPOST(axios11, "update/bulk/block"),
392
+ updateBlock: httpPOST(axios11, "update/block"),
393
+ updateDimension: httpPOST(axios11, "update/dimension"),
394
+ updateFormatter: httpPOST(axios11, "update/formatter"),
395
+ updateReport: httpPOST(axios11, "update/report"),
396
+ updateSection: httpPOST(axios11, "update/section"),
397
+ updateVariant: httpPOST(axios11, "update/variant"),
398
+ searchReport: httpGET(axios11, "search/reports"),
399
+ validateVariantSlug: httpGET(axios11, "validate/variant"),
400
+ readMember: httpGET(axios11, "read/members", transformReadMembers),
401
+ readMemberImage: httpGET(axios11, {
403
402
  url: "member/image",
404
403
  responseType: "blob"
405
404
  }),
406
- searchMember: httpGET(axios10, "search/members"),
407
- updateMember: httpGET(axios10, "update/members"),
408
- imageLocalSearch: httpImageSearchFactory(axios10, "local"),
409
- imageLocalSave: httpImageSaveFactory(axios10, "local"),
410
- imageFlickrSearch: httpImageSearchFactory(axios10, "flickr"),
411
- imageFlickrSave: httpImageSaveFactory(axios10, "flickr"),
412
- imageUnsplashSearch: httpImageSearchFactory(axios10, "unsplash"),
413
- imageUnsplashSave: httpImageSaveFactory(axios10, "unsplash"),
414
- imageUploadSave: httpImageSaveFactory(axios10, "upload"),
415
- imageAdobeSearch: httpImageSearchFactory(axios10, "adobe"),
416
- imageAdobeSave: httpImageSaveFactory(axios10, "adobe"),
417
- readMetadata: httpGET(axios10, "read/metadata"),
418
- regenerateSearch: httpPOST(axios10, "search/regenerate"),
419
- urlProxy: httpGET(axios10, "url/proxy"),
420
- searchRole: httpGET(axios10, "auth/search/roles"),
421
- searchUser: httpGET(axios10, "auth/search/users"),
422
- readUser: httpGET(axios10, "auth/read/user"),
423
- updateUser: httpPOST(axios10, "auth/update/user"),
424
- updateMyData: httpPOST(axios10, "auth/update/me"),
425
- revalidateReport: httpGET(axios10, "revalidate/report"),
426
- revalidateUrl: httpGET(axios10, "revalidate/url"),
427
- readPrivateBlocks: httpPOST(axios10, "read/blocks/private"),
428
- listTablerIcons: httpListIconsFactory(axios10, "tabler"),
429
- readTablerIcon: httpReadIconFactory(axios10, "tabler")
405
+ searchMember: httpGET(axios11, "search/members"),
406
+ updateMember: httpGET(axios11, "update/members"),
407
+ imageLocalSearch: httpImageSearchFactory(axios11, "local"),
408
+ imageLocalSave: httpImageSaveFactory(axios11, "local"),
409
+ imageFlickrSearch: httpImageSearchFactory(axios11, "flickr"),
410
+ imageFlickrSave: httpImageSaveFactory(axios11, "flickr"),
411
+ imageUnsplashSearch: httpImageSearchFactory(axios11, "unsplash"),
412
+ imageUnsplashSave: httpImageSaveFactory(axios11, "unsplash"),
413
+ imageUploadSave: httpImageSaveFactory(axios11, "upload"),
414
+ imageAdobeSearch: httpImageSearchFactory(axios11, "adobe"),
415
+ imageAdobeSave: httpImageSaveFactory(axios11, "adobe"),
416
+ readMetadata: httpGET(axios11, "read/metadata"),
417
+ regenerateSearch: httpPOST(axios11, "search/regenerate"),
418
+ urlProxy: httpGET(axios11, "url/proxy"),
419
+ searchRole: httpGET(axios11, "auth/search/roles"),
420
+ searchUser: httpGET(axios11, "auth/search/users"),
421
+ readUser: httpGET(axios11, "auth/read/user"),
422
+ updateUser: httpPOST(axios11, "auth/update/user"),
423
+ updateMyData: httpPOST(axios11, "auth/update/me"),
424
+ revalidateReport: httpGET(axios11, "revalidate/report"),
425
+ revalidateUrl: httpGET(axios11, "revalidate/url"),
426
+ readPrivateBlocks: httpPOST(axios11, "read/blocks/private"),
427
+ listTablerIcons: httpListIconsFactory(axios11, "tabler"),
428
+ readTablerIcon: httpReadIconFactory(axios11, "tabler")
430
429
  };
431
430
  }
432
431
  var transformDeletePayload, transformReadMembers;
@@ -2570,6 +2569,26 @@ var init_recordsSlice = __esm({
2570
2569
  }
2571
2570
  }
2572
2571
  };
2572
+ },
2573
+ normalizeSection: (state, action) => {
2574
+ const sectionId = action.payload.section;
2575
+ const section = state.entities.section[sectionId];
2576
+ return {
2577
+ ...state,
2578
+ entities: {
2579
+ ...state.entities,
2580
+ section: {
2581
+ // ...state.entities.section,
2582
+ [sectionId]: {
2583
+ ...section,
2584
+ settings: {
2585
+ ...section.settings,
2586
+ width: "full"
2587
+ }
2588
+ }
2589
+ }
2590
+ }
2591
+ };
2573
2592
  }
2574
2593
  },
2575
2594
  extraReducers: {
@@ -3501,6 +3520,7 @@ __export(actions_exports, {
3501
3520
  createEntity: () => createEntity,
3502
3521
  deleteEntity: () => deleteEntity,
3503
3522
  deleteQueryParam: () => deleteQueryParam,
3523
+ normalizeSectionLayout: () => normalizeSectionLayout,
3504
3524
  readEntity: () => readEntity,
3505
3525
  readMember: () => readMember,
3506
3526
  readMetadata: () => readMetadata,
@@ -3697,6 +3717,12 @@ function removeBlocksFromState(privateBlockIds) {
3697
3717
  await dispatch(removeBlocks(privateBlockIds));
3698
3718
  };
3699
3719
  }
3720
+ function normalizeSectionLayout(sectionId) {
3721
+ const { normalizeSection } = recordsSlice.actions;
3722
+ return async (dispatch) => {
3723
+ await dispatch(normalizeSection(sectionId));
3724
+ };
3725
+ }
3700
3726
  function addBlockToState(newBlocks) {
3701
3727
  const { addBlocks } = recordsSlice.actions;
3702
3728
  return async (dispatch) => {
@@ -4736,10 +4762,10 @@ var init_RichText = __esm({
4736
4762
  });
4737
4763
  function InnerTooltip(props) {
4738
4764
  const { content, style = {} } = props;
4739
- const print = usePrint();
4765
+ const { isPrint } = useMode();
4740
4766
  const contentObj = /* @__PURE__ */ jsx(RichText, { component: "span", html: content, isPreview: true });
4741
4767
  const element = useMemo(() => {
4742
- return print ? /* @__PURE__ */ jsx("div", { className: "bespoke-inner-tooltip-notification", children: /* @__PURE__ */ jsx(
4768
+ return isPrint ? /* @__PURE__ */ jsx("div", { className: "bespoke-inner-tooltip-notification", children: /* @__PURE__ */ jsx(
4743
4769
  Notification,
4744
4770
  {
4745
4771
  icon: /* @__PURE__ */ jsx(IconInfoCircle, { size: "1.1rem" }),
@@ -5281,7 +5307,7 @@ function TitleView({
5281
5307
  _iconPadding,
5282
5308
  asComparison = false
5283
5309
  }) {
5284
- const print = usePrint();
5310
+ const { isPrint } = useMode();
5285
5311
  const titleHTML = sanitizeBlockContent_default(title) || "<span class='cr-block-placeholder'>Title</span>";
5286
5312
  const intOrder = settings && settings.order ? parseInt(settings.order, 10) : 1;
5287
5313
  const order2 = intOrder <= 6 ? intOrder : 1;
@@ -5324,7 +5350,7 @@ function TitleView({
5324
5350
  position: getAlignForGroup(align),
5325
5351
  children: [
5326
5352
  /* @__PURE__ */ jsxs("div", { children: [
5327
- print ? titleElement : finalElement,
5353
+ isPrint ? titleElement : finalElement,
5328
5354
  displayName && /* @__PURE__ */ jsx(Text, { className: "bespoke-comparison-title", children: memberName })
5329
5355
  ] }),
5330
5356
  tooltip && /* @__PURE__ */ jsx(InnerTooltip, { content: tooltip })
@@ -5445,7 +5471,7 @@ var init_useOnChangeSelector = __esm({
5445
5471
  });
5446
5472
  function Selector(config) {
5447
5473
  const { name, settings, options, id, defaultValue, section, component, asComparison, hideLabel } = config;
5448
- const print = usePrint();
5474
+ const { isPrint } = useMode();
5449
5475
  const selectorIdentifier = getSelectorIdentifier(id);
5450
5476
  const callback = useOnChangeSelector(id, section, selectorIdentifier, asComparison);
5451
5477
  const selectorIdentifierParam = asComparison ? `${selectorIdentifier}Compare` : selectorIdentifier;
@@ -5456,9 +5482,9 @@ function Selector(config) {
5456
5482
  (d2) => ({ value: d2.id, label: d2.label })
5457
5483
  ), [optDependency]);
5458
5484
  const printValue = useMemo(() => {
5459
- const optionsString = print ? options.filter((o2) => multiValue.includes(o2.id)).map((o2) => o2.label).join(", ") : "";
5485
+ const optionsString = isPrint ? options.filter((o2) => multiValue.includes(o2.id)).map((o2) => o2.label).join(", ") : "";
5460
5486
  return optionsString;
5461
- }, [print]);
5487
+ }, [isPrint]);
5462
5488
  const label = config.hideLabel === "true" ? /* @__PURE__ */ jsx(Fragment, {}) : /* @__PURE__ */ jsx(RichText, { component: "div", html: name, size: "sm", style: settings });
5463
5489
  useEffect(() => {
5464
5490
  const searchParams = new URLSearchParams(window.location.search);
@@ -5513,7 +5539,7 @@ function Selector(config) {
5513
5539
  }
5514
5540
  )
5515
5541
  }[config.type] }) }),
5516
- print && /* @__PURE__ */ jsxs(Text, { children: [
5542
+ isPrint && /* @__PURE__ */ jsxs(Text, { children: [
5517
5543
  label,
5518
5544
  ": ",
5519
5545
  printValue
@@ -5569,7 +5595,7 @@ var init_Image = __esm({
5569
5595
  init_block2();
5570
5596
  }
5571
5597
  });
5572
- function useInitialState(pathSegmentsKey) {
5598
+ function useInitialState(pathSegmentsKey, mode) {
5573
5599
  const { asPath, locale, defaultLocale } = useRouter();
5574
5600
  const [loading, setLoading] = useState(true);
5575
5601
  const { user, isLoading } = useUser();
@@ -5612,39 +5638,41 @@ function useInitialState(pathSegmentsKey) {
5612
5638
  }
5613
5639
  }, [user, isLoading]);
5614
5640
  useEffect(() => {
5615
- const hasParams = Object.keys(query).some((key) => key !== pathSegmentsKey);
5616
- if ((hasParams || privateBlocks.blocks.length > 0) && privateBlocks.resolved) {
5617
- const selectorIds = Object.keys(query).map((d2) => Number(d2.match(/\d+/)));
5618
- const revalidateBlocks = selectorIds.filter((id) => id !== 0).concat(privateBlocks.blocks);
5619
- Promise.all(
5620
- revalidateBlocks.map((bid) => {
5621
- const block = blockRecords[bid];
5622
- const inputVariablesFlat = block.consumers.reduce((inputVars, cid) => ({ ...inputVars, ...variables[cid] }), {});
5623
- const blockContext = {
5624
- locale: localeDefault4,
5625
- query,
5626
- variables: {
5627
- ...inputVariablesFlat,
5628
- ...attributes
5629
- }
5630
- };
5631
- return runConsumersV2(
5632
- blockRecords,
5633
- void 0,
5634
- bid,
5635
- formatters2,
5636
- blockContext,
5637
- { variables, status },
5638
- readMemberFn,
5639
- "report"
5640
- );
5641
- })
5642
- ).then((d2) => {
5643
- setLoading(false);
5644
- d2.map((data) => dispatch(variablesActions.setVariableChange({ ...data, attributes })));
5645
- });
5641
+ if (mode !== "embed") {
5642
+ const hasParams = Object.keys(query).some((key) => key !== pathSegmentsKey);
5643
+ if ((hasParams || privateBlocks.blocks.length > 0) && privateBlocks.resolved) {
5644
+ const selectorIds = Object.keys(query).map((d2) => Number(d2.match(/\d+/)));
5645
+ const revalidateBlocks = selectorIds.filter((id) => id !== 0).concat(privateBlocks.blocks);
5646
+ Promise.all(
5647
+ revalidateBlocks.map((bid) => {
5648
+ const block = blockRecords[bid];
5649
+ const inputVariablesFlat = block.consumers.reduce((inputVars, cid) => ({ ...inputVars, ...variables[cid] }), {});
5650
+ const blockContext = {
5651
+ locale: localeDefault4,
5652
+ query,
5653
+ variables: {
5654
+ ...inputVariablesFlat,
5655
+ ...attributes
5656
+ }
5657
+ };
5658
+ return runConsumersV2(
5659
+ blockRecords,
5660
+ void 0,
5661
+ bid,
5662
+ formatters2,
5663
+ blockContext,
5664
+ { variables, status },
5665
+ readMemberFn,
5666
+ "report"
5667
+ );
5668
+ })
5669
+ ).then((d2) => {
5670
+ setLoading(false);
5671
+ d2.map((data) => dispatch(variablesActions.setVariableChange({ ...data, attributes })));
5672
+ });
5673
+ }
5646
5674
  }
5647
- }, [privateBlocks.resolved]);
5675
+ }, [privateBlocks.resolved, mode]);
5648
5676
  return loading || !privateBlocks.resolved;
5649
5677
  }
5650
5678
  var init_useInitialState = __esm({
@@ -6237,7 +6265,7 @@ __export(Viz_exports, {
6237
6265
  default: () => Viz
6238
6266
  });
6239
6267
  function Viz(config) {
6240
- const print = usePrint();
6268
+ const { isPrint, isEmbed } = useMode();
6241
6269
  const { block, active, locale, variables, configOverride = {} } = config;
6242
6270
  const content = getBlockContent(block);
6243
6271
  const formatterFunctions = useFormatterFunctionsForLocale(locale);
@@ -6266,8 +6294,8 @@ function Viz(config) {
6266
6294
  const Visualization = vizTypes[fallbackType];
6267
6295
  const vizPropsConfig = useMemo(() => {
6268
6296
  const { _data, ...vizPropsInnerConfig } = vizProps.config;
6269
- return printOverrides(vizPropsInnerConfig, print);
6270
- }, [vizProps, print]);
6297
+ return printOverrides(vizPropsInnerConfig, isPrint || isEmbed);
6298
+ }, [vizProps, isPrint, isEmbed]);
6271
6299
  const vizConfig = { locale, variables, ...vizPropsConfig, ...configOverride };
6272
6300
  return /* @__PURE__ */ jsx("div", { className: "bespoke-Viz", children: /* @__PURE__ */ jsx(
6273
6301
  "div",
@@ -15913,161 +15941,112 @@ var init_DataTab = __esm({
15913
15941
  };
15914
15942
  }
15915
15943
  });
15944
+ function useLocalePrefix() {
15945
+ const { locales: locales4 } = useRouter();
15946
+ const locale = location && location.pathname.split("/").filter((segment) => segment.length > 0)[0];
15947
+ return locales4?.includes(locale);
15948
+ }
15949
+ var init_useLocalePrefix = __esm({
15950
+ "frontend/hooks/useLocalePrefix.ts"() {
15951
+ init_esm_shims();
15952
+ }
15953
+ });
15916
15954
  function ImageTab(props) {
15917
15955
  const { section } = props;
15918
15956
  const blocksIds = section.blocks || [];
15957
+ const { asPath, query, locale } = useRouter();
15958
+ const prefixLocale = useLocalePrefix();
15959
+ const { pathSegment } = useBespoke();
15919
15960
  const vizAvailable = useAppSelector((state) => blocksIds.map((blockId) => state.records.entities.block[blockId]).filter((block) => block.type === "visualization" && state.variables.status[block.id].allowed));
15920
15961
  const [imageContext, setImageContext] = useState(contextOptions.section);
15921
15962
  const [imageFormat, setImageFormat] = useState(formatOptions2.png);
15922
15963
  const [imageProcessing, setImageProcessing] = useState(false);
15923
- const [svgAvailable, setSvgAvailable] = useState(false);
15924
15964
  const [vizPreviews, setVizPreviews] = useState([]);
15925
15965
  const [vizSelected, setVizSelected] = useState(vizAvailable.length > 0 ? vizAvailable[0].id : null);
15926
- const [transparentBackground, setTransparentBackground] = useState(true);
15966
+ const [transparentBackground, setTransparentBackground] = useState(false);
15927
15967
  const [loadingPreviews, setLoadingPreviews] = useState(false);
15928
15968
  const optionsTranslations = useBespokeTranslations("options");
15929
15969
  const translations = { ...DEFAULT_TRANSLATIONS3, ...optionsTranslations["image_tab"] };
15930
- const { siteProps } = useBespoke();
15931
- const includeLogo = siteProps?.logoSrc && typeof siteProps.logoSrc === "string";
15932
- const sectionId = section?.settings?.name || `section-${section.id}`;
15933
- const sectionHash = `#${sectionId}`;
15934
- const theme = useMantineTheme();
15935
- const vizSelectedHasSVG = () => {
15936
- const vizNode = getVizNode(vizSelected);
15937
- const svgCount = select(vizNode).select("svg").size();
15938
- return svgCount === 1;
15939
- };
15940
- const getFileName = () => `${slugify_default(sectionId)}`;
15941
- const getSectionNode = () => {
15942
- const sectionNode = document.getElementById(sectionId);
15943
- return select(sectionNode).select(".bespoke-section-content").node();
15944
- };
15945
- const getVizNode = (vizId) => {
15946
- const sectionNode = getSectionNode();
15947
- return select(sectionNode).select(`#bespoke-visualization-${vizId}.bespoke-block-area`).node();
15948
- };
15949
- const getSelectedNode = () => new Promise((resolve) => {
15950
- let node;
15951
- if (imageContext === contextOptions.section) {
15952
- node = getSectionNode();
15953
- } else if (imageContext === contextOptions.viz) {
15954
- node = getVizNode(vizSelected);
15955
- }
15956
- const sourceContainer = document.createElement("div");
15957
- sourceContainer.style.display = "flex";
15958
- sourceContainer.style.justifyContent = "space-between";
15959
- sourceContainer.classList.add("bespoke-Viz-source");
15960
- sourceContainer.style.padding = theme.spacing.xs;
15961
- sourceContainer.style.width = "100%";
15962
- const sourceNode = document.createElement("div");
15963
- sourceNode.innerText = translations["source"].replace("{src}", `${window.location}${sectionHash}`);
15964
- sourceNode.style.fontSize = theme.fontSizes.xs;
15965
- sourceContainer.appendChild(sourceNode);
15966
- if (includeLogo) {
15967
- const sourceImage = document.createElement("img");
15968
- sourceImage.src = siteProps.logoSrc;
15969
- sourceImage.style.height = "30px";
15970
- sourceImage.onload = () => resolve(node);
15971
- sourceImage.onerror = () => {
15972
- sourceImage.remove();
15973
- resolve(node);
15974
- };
15975
- sourceContainer.appendChild(sourceImage);
15976
- node.appendChild(sourceContainer);
15977
- } else {
15978
- node.appendChild(sourceContainer);
15979
- resolve(node);
15980
- }
15981
- });
15982
- const getBackground = (elem) => {
15983
- if (transparentBackground)
15984
- return "transparent";
15985
- const color = select(elem).style("background-color");
15986
- if (color !== "rgba(0, 0, 0, 0)" && color !== "transparent")
15987
- return color;
15988
- if (elem === document.body)
15989
- return "white";
15990
- return getBackground(elem.parentNode);
15991
- };
15992
- const exportPng = async (node, config) => {
15993
- const { toPng } = await import('html-to-image');
15994
- toPng(
15995
- node,
15996
- {
15997
- backgroundColor: config.background,
15998
- cacheBust: true,
15999
- filter: (innerNode) => {
16000
- if (innerNode && innerNode.classList) {
16001
- return !config.exclusionClasses.some((classname) => innerNode.classList.contains(classname));
16002
- }
16003
- return true;
16004
- }
16005
- }
16006
- ).then((dataUrl) => {
15970
+ const sectionId = slugify_default(section?.settings?.name) || `section-${section.id}`;
15971
+ const onSaveClickSection = async () => {
15972
+ const search = Object.fromEntries(new URLSearchParams(location.search));
15973
+ const selectors = Object.fromEntries(
15974
+ Object.entries(search).filter(([key]) => key.match(/selector\d+id/))
15975
+ );
15976
+ const body = {
15977
+ path: asPath,
15978
+ slugs: JSON.stringify(query[pathSegment]),
15979
+ section: section.id,
15980
+ query: selectors,
15981
+ locale,
15982
+ prefixLocale,
15983
+ transparent: transparentBackground
15984
+ };
15985
+ setImageProcessing(true);
15986
+ axios.post("/api/cms/img", body, { responseType: "blob" }).then((res) => {
15987
+ const url = window.URL.createObjectURL(new Blob([res.data]));
16007
15988
  const link = document.createElement("a");
16008
- link.download = `${config.filename}.png`;
16009
- link.href = dataUrl;
15989
+ link.href = url;
15990
+ link.setAttribute("download", "image.png");
15991
+ document.body.appendChild(link);
16010
15992
  link.click();
16011
- cleanDOM();
15993
+ document.body.removeChild(link);
16012
15994
  setImageProcessing(false);
16013
- }).catch((err) => {
16014
- console.log(err);
16015
15995
  });
16016
15996
  };
16017
- const exportSvg = (node, config) => {
16018
- saveElement(
16019
- node,
16020
- { filename: config.filename, type: "svg", callback: () => setImageProcessing(false) },
16021
- { background: config.background }
15997
+ const onSaveClickViz = async () => {
15998
+ const search = Object.fromEntries(new URLSearchParams(location.search));
15999
+ const selectors = Object.fromEntries(
16000
+ Object.entries(search).filter(([key]) => key.match(/selector\d+id/))
16022
16001
  );
16023
- };
16024
- const onSaveClick = async () => {
16025
- setImageProcessing(true);
16026
- const node = await getSelectedNode();
16027
- const exportConfig = {
16028
- background: getBackground(node),
16029
- exclusionClasses: ["export-hidden"],
16030
- filename: getFileName()
16002
+ const body = {
16003
+ path: asPath,
16004
+ slugs: JSON.stringify(query[pathSegment]),
16005
+ section: section.id,
16006
+ viz: vizSelected,
16007
+ query: selectors,
16008
+ locale,
16009
+ prefixLocale,
16010
+ transparent: transparentBackground,
16011
+ svg: imageFormat === formatOptions2.svg
16031
16012
  };
16032
- const exportFunction = imageFormat === formatOptions2.png ? exportPng : exportSvg;
16033
- try {
16034
- if (node) {
16035
- exportFunction(node, exportConfig);
16036
- } else {
16037
- throw new Error(translations["wrong_node"]);
16038
- }
16039
- } catch (error) {
16013
+ setImageProcessing(true);
16014
+ axios.post("/api/cms/img", body, { responseType: "blob" }).then((res) => {
16015
+ const url = window.URL.createObjectURL(new Blob([res.data]));
16016
+ const link = document.createElement("a");
16017
+ link.href = url;
16018
+ link.setAttribute("download", `image.${imageFormat.toLowerCase()}`);
16019
+ document.body.appendChild(link);
16020
+ link.click();
16021
+ document.body.removeChild(link);
16040
16022
  setImageProcessing(false);
16041
- }
16023
+ });
16042
16024
  };
16043
16025
  const generatePreviews = async () => {
16044
16026
  const { toCanvas } = await import('html-to-image');
16045
16027
  Promise.all(
16046
- vizAvailable.map((viz) => toCanvas(getVizNode(viz.id)))
16028
+ vizAvailable.map((viz) => toCanvas(getVizNode(viz.id, sectionId)))
16047
16029
  ).then((canvases) => {
16048
16030
  setVizPreviews(canvases.map((canvas) => canvas.toDataURL()));
16049
16031
  setLoadingPreviews(false);
16050
16032
  });
16051
16033
  };
16052
- useEffect(() => {
16053
- if (imageContext === contextOptions.viz && vizAvailable.length > 0) {
16034
+ const handleContextChange = (newValue) => {
16035
+ setImageContext(newValue);
16036
+ if (newValue === contextOptions.section) {
16037
+ setImageFormat(formatOptions2.png);
16038
+ }
16039
+ if (newValue === contextOptions.viz && vizAvailable.length > 0) {
16054
16040
  setVizSelected(vizAvailable[0].id);
16055
16041
  setLoadingPreviews(true);
16056
16042
  generatePreviews();
16057
- } else if (imageContext === contextOptions.section) {
16043
+ } else if (newValue === contextOptions.section) {
16058
16044
  setVizPreviews([]);
16059
16045
  }
16060
- }, [imageContext]);
16061
- useEffect(() => {
16062
- if (vizSelected) {
16063
- if (vizSelectedHasSVG()) {
16064
- setSvgAvailable(true);
16065
- } else {
16066
- setSvgAvailable(false);
16067
- setImageFormat(formatOptions2.png);
16068
- }
16069
- }
16070
- }, [vizSelected]);
16046
+ };
16047
+ const onSelectViz = (vizId) => () => {
16048
+ setVizSelected(vizId);
16049
+ };
16071
16050
  return /* @__PURE__ */ jsxs(Stack, { className: "cms-section-options-image", children: [
16072
16051
  /* @__PURE__ */ jsx(Space, { h: "xs" }),
16073
16052
  vizAvailable.length > 0 ? /* @__PURE__ */ jsx(Input.Wrapper, { label: `${translations["choose_area"]}:`, children: /* @__PURE__ */ jsx(
@@ -16091,12 +16070,7 @@ function ImageTab(props) {
16091
16070
  ] })
16092
16071
  }
16093
16072
  ],
16094
- onChange: (newValue) => {
16095
- setImageContext(newValue);
16096
- if (newValue === contextOptions.section) {
16097
- setImageFormat(formatOptions2.png);
16098
- }
16099
- }
16073
+ onChange: handleContextChange
16100
16074
  }
16101
16075
  ) }) : /* @__PURE__ */ jsx(Text, { fw: "700", color: "gray.7", children: `${translations["entire_section"]}:` }),
16102
16076
  imageContext === contextOptions.viz && vizAvailable.length > 0 && /* @__PURE__ */ jsxs(
@@ -16106,27 +16080,20 @@ function ImageTab(props) {
16106
16080
  style: { minHeight: "161px", position: "relative" },
16107
16081
  children: [
16108
16082
  !loadingPreviews && vizPreviews.length > 0 && /* @__PURE__ */ jsx(ScrollArea, { style: { height: 250 }, children: /* @__PURE__ */ jsx(SimpleGrid, { cols: 3, children: vizAvailable.map((vizOption, ix) => /* @__PURE__ */ jsx(
16109
- Image,
16083
+ VizPreview2,
16110
16084
  {
16111
- alt: `Viz ${vizOption.id}`,
16112
- onClick: () => {
16113
- setVizSelected(vizOption.id);
16114
- },
16115
- style: {
16116
- cursor: "pointer",
16117
- border: vizSelected === vizOption.id ? "4px solid #228be6" : "4px solid #eee"
16118
- },
16119
- radius: 0,
16120
- src: vizPreviews[ix],
16121
- withPlaceholder: true
16085
+ vizId: vizOption.id,
16086
+ isSelected: vizOption.id === vizSelected,
16087
+ onClick: () => onSelectViz(vizAvailable[ix].id),
16088
+ src: vizPreviews[ix]
16122
16089
  },
16123
- `viz-option-${vizOption.id}`
16090
+ vizOption.id
16124
16091
  )) }) }),
16125
16092
  /* @__PURE__ */ jsx(LoadingOverlay, { visible: loadingPreviews })
16126
16093
  ]
16127
16094
  }
16128
16095
  ),
16129
- svgAvailable && imageContext !== contextOptions.section && /* @__PURE__ */ jsx(Input.Wrapper, { label: `${translations["choose_format"]}:`, children: /* @__PURE__ */ jsxs(Button.Group, { children: [
16096
+ imageContext !== contextOptions.section && /* @__PURE__ */ jsx(Input.Wrapper, { label: `${translations["choose_format"]}:`, children: /* @__PURE__ */ jsxs(Button.Group, { children: [
16130
16097
  /* @__PURE__ */ jsx(
16131
16098
  Button,
16132
16099
  {
@@ -16148,7 +16115,7 @@ function ImageTab(props) {
16148
16115
  }
16149
16116
  )
16150
16117
  ] }) }),
16151
- /* @__PURE__ */ jsx(
16118
+ imageFormat !== formatOptions2.svg && /* @__PURE__ */ jsx(
16152
16119
  Checkbox,
16153
16120
  {
16154
16121
  checked: transparentBackground,
@@ -16162,14 +16129,14 @@ function ImageTab(props) {
16162
16129
  {
16163
16130
  leftIcon: /* @__PURE__ */ jsx(IconDownload, { size: 16 }),
16164
16131
  loading: imageProcessing,
16165
- onClick: onSaveClick,
16132
+ onClick: imageContext === contextOptions.section ? onSaveClickSection : onSaveClickViz,
16166
16133
  fullWidth: true,
16167
16134
  children: /* @__PURE__ */ jsx("span", { children: imageProcessing ? translations["processing"] : translations["download"].replace("{imageFormat}", imageFormat) })
16168
16135
  }
16169
16136
  )
16170
16137
  ] });
16171
16138
  }
16172
- var contextOptions, formatOptions2, cleanDOM, DEFAULT_TRANSLATIONS3;
16139
+ var contextOptions, formatOptions2, VizPreview2, DEFAULT_TRANSLATIONS3, getSectionNode, getVizNode;
16173
16140
  var init_ImageTab = __esm({
16174
16141
  "components/options/tabs/ImageTab.tsx"() {
16175
16142
  init_esm_shims();
@@ -16177,6 +16144,11 @@ var init_ImageTab = __esm({
16177
16144
  init_store2();
16178
16145
  init_TranslationsProvider();
16179
16146
  init_ResourceProvider();
16147
+ init_useLocalePrefix();
16148
+ init_d3plusPropify();
16149
+ init_varSwapRecursive();
16150
+ init_getBlockContent();
16151
+ init_hooks();
16180
16152
  contextOptions = {
16181
16153
  viz: "viz",
16182
16154
  section: "section"
@@ -16185,9 +16157,50 @@ var init_ImageTab = __esm({
16185
16157
  png: "PNG",
16186
16158
  svg: "SVG"
16187
16159
  };
16188
- cleanDOM = () => {
16189
- const sourceNodes = document.getElementsByClassName("bespoke-Viz-source");
16190
- Array.from(sourceNodes).forEach((node) => node.remove());
16160
+ VizPreview2 = ({ vizId, isSelected = false, onClick = () => void 0, src = void 0 }) => {
16161
+ const router = useRouter();
16162
+ const { locale } = router;
16163
+ const variables = useInputVariablesFlat(vizId);
16164
+ const block = useBlockRef(vizId).data;
16165
+ const content = getBlockContent(block);
16166
+ const formatterFunctions = useFormatterFunctionsForLocale(locale);
16167
+ const blockContext = { variables };
16168
+ const transpiledLogic = varSwapRecursive_default({ logic: content.logic }, formatterFunctions, blockContext).logic;
16169
+ const globals = {
16170
+ router
16171
+ };
16172
+ const props = d3plusPropify_default(transpiledLogic, formatterFunctions, variables, locale, vizId, {}, globals);
16173
+ const { title } = props.config;
16174
+ return /* @__PURE__ */ jsxs(Stack, { align: "center", spacing: "xs", children: [
16175
+ /* @__PURE__ */ jsx(
16176
+ Image,
16177
+ {
16178
+ alt: `Viz ${vizId}`,
16179
+ onClick,
16180
+ style: {
16181
+ cursor: "pointer",
16182
+ border: isSelected ? "4px solid #228be6" : "4px solid #eee"
16183
+ },
16184
+ radius: 0,
16185
+ src,
16186
+ withPlaceholder: true
16187
+ },
16188
+ `viz-option-${vizId}`
16189
+ ),
16190
+ title && /* @__PURE__ */ jsx(
16191
+ Text,
16192
+ {
16193
+ fz: "0.8rem",
16194
+ fw: 700,
16195
+ bg: isSelected ? "rgba(0, 0, 0, 0.2)" : "transparent",
16196
+ w: "fit-content",
16197
+ px: "xs",
16198
+ sx: { borderRadius: "0.3rem" },
16199
+ ta: "center",
16200
+ children: title
16201
+ }
16202
+ )
16203
+ ] });
16191
16204
  };
16192
16205
  DEFAULT_TRANSLATIONS3 = {
16193
16206
  "wrong_node": "Wrong node in export",
@@ -16201,6 +16214,15 @@ var init_ImageTab = __esm({
16201
16214
  "viz_only": "Visualization only",
16202
16215
  "source": "Source: {src}"
16203
16216
  };
16217
+ getSectionNode = (sectionId) => {
16218
+ console.log({ sectionId });
16219
+ const sectionNode = document.getElementById(sectionId);
16220
+ return select(sectionNode).select(".bespoke-section-content").node();
16221
+ };
16222
+ getVizNode = (vizId, sectionId) => {
16223
+ const sectionNode = getSectionNode(sectionId);
16224
+ return select(sectionNode).select(`#bespoke-visualization-${vizId}.bespoke-block-area`).node();
16225
+ };
16204
16226
  }
16205
16227
  });
16206
16228
  function CopyInput(props) {
@@ -16222,12 +16244,31 @@ var init_CopyInput = __esm({
16222
16244
  init_esm_shims();
16223
16245
  }
16224
16246
  });
16247
+ function useEmbedPath(sectionId) {
16248
+ const { query, locale } = useRouter();
16249
+ const prefixLocale = useLocalePrefix();
16250
+ const { pathSegment } = useBespoke();
16251
+ const slugs = query[pathSegment];
16252
+ if (slugs && Array.isArray(slugs)) {
16253
+ const search = new URLSearchParams({ section: sectionId });
16254
+ const embed = `/embed/${slugs.join("/")}`;
16255
+ const url = new URL(
16256
+ prefixLocale ? `/${locale}${embed}` : `${embed}`,
16257
+ location.href
16258
+ );
16259
+ url.search = search.toString();
16260
+ return url.href;
16261
+ }
16262
+ return void 0;
16263
+ }
16225
16264
  function ShareTab(props) {
16226
16265
  const { section } = props;
16227
16266
  const [shareUrl, setShareUrl] = useState("");
16228
16267
  const [title, setTitle] = useState("");
16229
16268
  const [includeSection, setIncludeSection] = useState(true);
16230
16269
  const optionsTranslations = useBespokeTranslations("options");
16270
+ const embedPath = useEmbedPath(section.id);
16271
+ const iframeStr = `<iframe width="100%" height="500px" frameborder="0" src="${embedPath}"></iframe>`;
16231
16272
  const translations = { ...DEFAULT_TRANSLATIONS4, ...optionsTranslations["share_tab"] };
16232
16273
  useEffect(() => {
16233
16274
  setTitle(document.title);
@@ -16248,6 +16289,7 @@ function ShareTab(props) {
16248
16289
  radius: "xl"
16249
16290
  }
16250
16291
  ),
16292
+ /* @__PURE__ */ jsx(CopyInput, { title: translations["embed"], url: iframeStr }),
16251
16293
  /* @__PURE__ */ jsxs(Group, { position: "center", children: [
16252
16294
  /* @__PURE__ */ jsx(
16253
16295
  FacebookShareButton,
@@ -16310,9 +16352,12 @@ var init_ShareTab = __esm({
16310
16352
  init_esm_shims();
16311
16353
  init_CopyInput();
16312
16354
  init_TranslationsProvider();
16355
+ init_useLocalePrefix();
16356
+ init_ResourceProvider();
16313
16357
  DEFAULT_TRANSLATIONS4 = {
16314
16358
  "title": "Title",
16315
- "include_section": "Include Section"
16359
+ "include_section": "Include Section",
16360
+ "embed": "Embed"
16316
16361
  };
16317
16362
  }
16318
16363
  });
@@ -17112,7 +17157,7 @@ var init_site = __esm({
17112
17157
  }
17113
17158
  });
17114
17159
  function ImageCredits({ previews, translations }) {
17115
- const print = usePrint();
17160
+ const { isPrint } = useMode();
17116
17161
  const credits = useMemo(() => previews.map(
17117
17162
  ({ image, name }, idx) => (image?.author || image?.url) && /* @__PURE__ */ jsxs(Stack, { spacing: 0, children: [
17118
17163
  image.author && /* @__PURE__ */ jsxs(Text, { size: "xs", children: [
@@ -17137,7 +17182,7 @@ function ImageCredits({ previews, translations }) {
17137
17182
  idx + 1 < previews.length && /* @__PURE__ */ jsx(Divider, {})
17138
17183
  ] }, image.id)
17139
17184
  ), [previews]);
17140
- return /* @__PURE__ */ jsx(Fragment, { children: print ? /* @__PURE__ */ jsx(Fragment, { children: credits }) : /* @__PURE__ */ jsxs(Popover, { position: "right", children: [
17185
+ return /* @__PURE__ */ jsx(Fragment, { children: isPrint ? /* @__PURE__ */ jsx(Fragment, { children: credits }) : /* @__PURE__ */ jsxs(Popover, { position: "right", children: [
17141
17186
  /* @__PURE__ */ jsx(Popover.Target, { children: /* @__PURE__ */ jsx(ActionIcon, { variant: "light", radius: "xl", children: /* @__PURE__ */ jsx(IconPhoto, { size: 20 }) }) }),
17142
17187
  /* @__PURE__ */ jsx(Popover.Dropdown, { children: credits })
17143
17188
  ] }) });
@@ -17157,7 +17202,7 @@ function SectionWrapper({
17157
17202
  previews,
17158
17203
  translations
17159
17204
  }) {
17160
- const print = usePrint();
17205
+ const { isPrint, isEmbed } = useMode();
17161
17206
  const showCredits = sectionSettings2.memberImageBg && previews.some(({ image }) => image && (image.url || image.author));
17162
17207
  return /* @__PURE__ */ jsxs(
17163
17208
  PositionWrapper,
@@ -17173,12 +17218,12 @@ function SectionWrapper({
17173
17218
  className: `bespoke-Section-${id} bespoke-Section-container `,
17174
17219
  settings: sectionSettings2,
17175
17220
  styles: sectionStyles?.container,
17176
- id: name || `section-${id}`,
17221
+ id: slugify_default(name) || `section-${id}`,
17177
17222
  pos: "relative",
17178
17223
  children: [
17179
- !print && /* @__PURE__ */ jsxs(Group, { position: "right", mb: "xs", children: [
17224
+ !isPrint && /* @__PURE__ */ jsxs(Group, { position: "right", mb: "xs", children: [
17180
17225
  /* @__PURE__ */ jsx(SectionResetButton, { id }),
17181
- sectionSettings2.optionsMenu && /* @__PURE__ */ jsx(Options, { sectionId: id, disabled: false })
17226
+ sectionSettings2.optionsMenu && !isEmbed && /* @__PURE__ */ jsx(Options, { sectionId: id, disabled: false })
17182
17227
  ] }),
17183
17228
  /* @__PURE__ */ jsxs(
17184
17229
  StyleWrapper,
@@ -17205,14 +17250,21 @@ function Section({ section }) {
17205
17250
  ...defaultSectionSettings,
17206
17251
  ...settings
17207
17252
  };
17253
+ const { query } = useRouter();
17208
17254
  const comparison = useComparison();
17255
+ const { isEmbed } = useMode();
17209
17256
  const state = useAppSelector((state2) => state2);
17210
17257
  const status = useAppSelector((state2) => state2.variables.status);
17211
17258
  const previews = useAppSelector((state2) => state2.status.previews);
17212
17259
  const comparisonPreviews = comparison.active && comparison.status ? previews.concat(comparison.status?.previews || []) : previews;
17213
17260
  const sectionStyles = useBespokeStyles()["Section"];
17214
17261
  const blockRecords = selectBlockRecords(state);
17215
- const sectionBlocks = Object.values(blockRecords || {}).filter((d2) => d2.section_id === id);
17262
+ let sectionBlocks = [];
17263
+ if (isEmbed && query.viz) {
17264
+ sectionBlocks = Object.values(blockRecords || {}).filter((d2) => d2.section_id === id && d2.id === Number(query.viz));
17265
+ } else {
17266
+ sectionBlocks = Object.values(blockRecords || {}).filter((d2) => d2.section_id === id);
17267
+ }
17216
17268
  const sectionTranslations = useBespokeTranslations("sections");
17217
17269
  const translations = { ...DEFAULT_TRANSLATIONS6, ...sectionTranslations };
17218
17270
  const allowedSection = sectionBlocks.some((b2) => {
@@ -17320,7 +17372,7 @@ function Section({ section }) {
17320
17372
  );
17321
17373
  });
17322
17374
  }
17323
- return /* @__PURE__ */ jsx(
17375
+ return /* @__PURE__ */ jsxs(
17324
17376
  SectionWrapper,
17325
17377
  {
17326
17378
  id,
@@ -17329,21 +17381,24 @@ function Section({ section }) {
17329
17381
  sectionSettings: sectionSettings2,
17330
17382
  sectionStyles,
17331
17383
  translations,
17332
- children: /* @__PURE__ */ jsx(
17333
- ColumnsWrapper,
17334
- {
17335
- containerProps: {
17336
- gap: sectionSettings2.columnGutter || "md",
17337
- wrap: comparison.active ? "nowrap" : "wrap",
17338
- sx: { zIndex: 20 }
17339
- },
17340
- children: sectionColumns
17341
- }
17342
- )
17384
+ children: [
17385
+ /* @__PURE__ */ jsx(
17386
+ ColumnsWrapper,
17387
+ {
17388
+ containerProps: {
17389
+ gap: sectionSettings2.columnGutter || "md",
17390
+ wrap: comparison.active ? "nowrap" : "wrap",
17391
+ sx: { zIndex: 20 }
17392
+ },
17393
+ children: sectionColumns
17394
+ }
17395
+ ),
17396
+ isEmbed && /* @__PURE__ */ jsx(EmbedSectionSrc, {})
17397
+ ]
17343
17398
  }
17344
17399
  );
17345
17400
  }
17346
- var DEFAULT_TRANSLATIONS6, getStyles, PositionWrapper, WidthWrapper, StyleWrapper, Section_default;
17401
+ var DEFAULT_TRANSLATIONS6, getStyles, PositionWrapper, WidthWrapper, StyleWrapper, EmbedSectionSrc, Section_default;
17347
17402
  var init_Section = __esm({
17348
17403
  "frontend/components/report/Section.tsx"() {
17349
17404
  init_esm_shims();
@@ -17364,6 +17419,9 @@ var init_Section = __esm({
17364
17419
  init_TranslationsProvider();
17365
17420
  init_ImageCredits();
17366
17421
  init_ComparisonProvider();
17422
+ init_ResourceProvider();
17423
+ init_useLocalePrefix();
17424
+ init_slugify();
17367
17425
  DEFAULT_TRANSLATIONS6 = {
17368
17426
  "image_by": "image by"
17369
17427
  };
@@ -17424,18 +17482,56 @@ var init_Section = __esm({
17424
17482
  const sx = [defaultStyles[variant], getStyles(styles, settings)];
17425
17483
  return /* @__PURE__ */ jsx(Paper, { sx, ...defaultProps2[variant], ...props, children });
17426
17484
  };
17485
+ EmbedSectionSrc = () => {
17486
+ const router = useRouter();
17487
+ const { query, locale } = router;
17488
+ const { siteProps, profilePrefix, pathSegment } = useBespoke();
17489
+ const querySlugs = query[pathSegment];
17490
+ const slugs = Array.isArray(querySlugs) ? querySlugs?.join("/") : [];
17491
+ const prefixLocale = useLocalePrefix();
17492
+ let path = prefixLocale ? `/${locale}` : "";
17493
+ path += `/${profilePrefix}/${slugs}/`;
17494
+ const link = new URL(path, window?.location.href).href;
17495
+ return /* @__PURE__ */ jsxs(Group, { position: "apart", noWrap: true, sx: { flexGrow: 1 }, children: [
17496
+ /* @__PURE__ */ jsx(Anchor, { href: link, target: "_blank", children: link }),
17497
+ /* @__PURE__ */ jsx(
17498
+ "img",
17499
+ {
17500
+ src: siteProps.logoSrc,
17501
+ width: "auto",
17502
+ height: 40,
17503
+ alt: "Site Logo"
17504
+ }
17505
+ )
17506
+ ] });
17507
+ };
17427
17508
  Section_default = Section;
17428
17509
  }
17429
17510
  });
17430
- function usePrint() {
17431
- return useContext(PrintContext);
17511
+ function useMode() {
17512
+ const mode = useContext(ModeContext);
17513
+ return {
17514
+ mode,
17515
+ isPrint: mode === MODES.print,
17516
+ isEmbed: mode === MODES.embed,
17517
+ isReport: mode === MODES.report
17518
+ };
17432
17519
  }
17433
- function Report({ pathSegmentsKey = "bespoke" }) {
17434
- const { asPath, query } = useRouter();
17520
+ function Report({ pathSegmentsKey = "bespoke", mode = "report" }) {
17521
+ const router = useRouter();
17522
+ const { asPath, query } = router;
17435
17523
  const sectionList = useSectionList();
17436
17524
  const comparison = useComparison();
17437
- const print = yn4(query.print) ?? false;
17438
- const printBtn = yn4(query.printBtn) ?? false;
17525
+ const [reportMode, setReportMode] = useState(mode);
17526
+ useEffect(() => {
17527
+ if (router.isReady) {
17528
+ const { mode: mode2 } = router.query;
17529
+ if (mode2 && typeof mode2 === "string" && Object.values(MODES).includes(mode2)) {
17530
+ setReportMode(mode2);
17531
+ }
17532
+ }
17533
+ }, [router.isReady, router.query]);
17534
+ const printBtn = yn4(router.query.printBtn) ?? false;
17439
17535
  const [downloadLoading, setDownloadLoading] = useState(false);
17440
17536
  const printReport = () => {
17441
17537
  setDownloadLoading(true);
@@ -17450,8 +17546,8 @@ function Report({ pathSegmentsKey = "bespoke" }) {
17450
17546
  });
17451
17547
  };
17452
17548
  useScrollToAnchor();
17453
- return /* @__PURE__ */ jsxs(PrintContext.Provider, { value: print, children: [
17454
- print && /* @__PURE__ */ jsx(MediaQuery, { query: "print", styles: { display: "block" }, children: /* @__PURE__ */ jsx(Title, { display: "none", children: "This is the report header" }) }),
17549
+ return /* @__PURE__ */ jsxs(ModeContext.Provider, { value: reportMode, children: [
17550
+ mode === MODES.print && /* @__PURE__ */ jsx(MediaQuery, { query: "print", styles: { display: "block" }, children: /* @__PURE__ */ jsx(Title, { display: "none", children: "This is the report header" }) }),
17455
17551
  /* @__PURE__ */ jsxs(Container, { id: "bespoke-report", className: comparison.comparisonLoaded ? "compare" : "", p: 0, pos: "relative", fluid: true, children: [
17456
17552
  sectionList.isSuccess && sectionList.data.sort(orderSort).map((section) => /* @__PURE__ */ jsx(Section_default, { section }, section.id)),
17457
17553
  /*pdf.active*/
@@ -17459,7 +17555,7 @@ function Report({ pathSegmentsKey = "bespoke" }) {
17459
17555
  ] })
17460
17556
  ] });
17461
17557
  }
17462
- var PrintContext, Report_default;
17558
+ var MODES, ModeContext, Report_default;
17463
17559
  var init_Report = __esm({
17464
17560
  "frontend/components/report/Report.tsx"() {
17465
17561
  init_esm_shims();
@@ -17468,7 +17564,12 @@ var init_Report = __esm({
17468
17564
  init_Section();
17469
17565
  init_ComparisonProvider();
17470
17566
  init_useScrollToAnchor();
17471
- PrintContext = createContext(false);
17567
+ MODES = {
17568
+ report: "report",
17569
+ print: "print",
17570
+ embed: "embed"
17571
+ };
17572
+ ModeContext = createContext("report");
17472
17573
  Report_default = Report;
17473
17574
  }
17474
17575
  });
@@ -25705,14 +25806,15 @@ function BespokeRenderer({
25705
25806
  siteProps,
25706
25807
  profilePrefix = "/defaultPath",
25707
25808
  loader = /* @__PURE__ */ jsx(LoadingOverlay, { visible: true }),
25809
+ mode = "report",
25708
25810
  mantineProviderProps = {}
25709
25811
  }) {
25710
- const loading = useInitialState(pathSegmentsKey);
25812
+ const loading = useInitialState(pathSegmentsKey, mode);
25711
25813
  if (loading)
25712
25814
  return /* @__PURE__ */ jsx(Fragment, { children: loader });
25713
- return /* @__PURE__ */ jsx(MantineProvider, { ...mantineProviderProps, theme: { other: { bespokeStyles } }, inherit: true, children: /* @__PURE__ */ jsxs(ResourceProvider, { pathSegment: "bespoke", profilePrefix, siteProps, children: [
25714
- /* @__PURE__ */ jsx(TranslationsProvider, { translations, children: /* @__PURE__ */ jsx(ComparisonProvider, { children: /* @__PURE__ */ jsx(PdfProvider, { children: /* @__PURE__ */ jsx(Report_default, {}) }) }) }),
25715
- /* @__PURE__ */ jsx("small", { className: "bespoke-timestamp", children: buildTime })
25815
+ return /* @__PURE__ */ jsx(MantineProvider, { ...mantineProviderProps, theme: { other: { bespokeStyles } }, inherit: true, children: /* @__PURE__ */ jsxs(ResourceProvider, { pathSegment: pathSegmentsKey, profilePrefix, siteProps, children: [
25816
+ /* @__PURE__ */ jsx(TranslationsProvider, { translations, children: /* @__PURE__ */ jsx(ComparisonProvider, { children: /* @__PURE__ */ jsx(PdfProvider, { children: /* @__PURE__ */ jsx(Report_default, { pathSegmentsKey, mode }) }) }) }),
25817
+ /* @__PURE__ */ jsx("small", { className: "bespoke-timestamp", hidden: true, children: buildTime })
25716
25818
  ] }) });
25717
25819
  }
25718
25820