@datawheel/bespoke 0.6.0-rc.14 → 0.6.0-rc.2
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 +300 -335
- package/dist/server.js +239 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -22,11 +22,10 @@ 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, IconCheck, IconCodePlus, IconLink, IconSparkles, IconClipboardCheck, IconClipboardCopy, IconExternalLink, IconFileTypeCsv, IconFileTypeJs, IconFileTypeXls, IconTemplate, IconPalette, IconBold, IconItalic, IconUnderline, IconAlignCenter, IconAlignRight, IconAlignJustified, IconArrowBackUp, IconArrowForwardUp, IconLanguageOff, IconTriangleInvertedFilled,
|
|
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';
|
|
29
|
-
import { initFrameAndPoll, Framer } from '@newswire/frames';
|
|
30
29
|
import { MantineReactTable } from 'mantine-react-table';
|
|
31
30
|
import { dataConcat, dataLoad } from 'd3plus-viz';
|
|
32
31
|
import * as d3plus from 'd3plus-react';
|
|
@@ -51,9 +50,8 @@ import { Prism } from '@mantine/prism';
|
|
|
51
50
|
import slugifyFn from 'slugify';
|
|
52
51
|
import JSZip from 'jszip';
|
|
53
52
|
import { saveAs } from 'file-saver';
|
|
54
|
-
import { FacebookShareButton, FacebookIcon, TwitterShareButton, TwitterIcon, TelegramShareButton, TelegramIcon, WhatsappShareButton, WhatsappIcon, LinkedinShareButton, LinkedinIcon, RedditShareButton, RedditIcon, EmailShareButton, EmailIcon } from 'react-share';
|
|
55
53
|
import { select } from 'd3-selection';
|
|
56
|
-
import {
|
|
54
|
+
import { FacebookShareButton, FacebookIcon, TwitterShareButton, TwitterIcon, TelegramShareButton, TelegramIcon, WhatsappShareButton, WhatsappIcon, LinkedinShareButton, LinkedinIcon, RedditShareButton, RedditIcon, EmailShareButton, EmailIcon } from 'react-share';
|
|
57
55
|
import Head from 'next/head';
|
|
58
56
|
import Editor, { useMonaco } from '@monaco-editor/react';
|
|
59
57
|
import { format } from 'pretty-format';
|
|
@@ -265,8 +263,8 @@ var init_ExploreFilters = __esm({
|
|
|
265
263
|
});
|
|
266
264
|
|
|
267
265
|
// api/http/lib.ts
|
|
268
|
-
function http(
|
|
269
|
-
return
|
|
266
|
+
function http(axios11, config) {
|
|
267
|
+
return axios11.request(config).then((response) => {
|
|
270
268
|
const { status, data } = response;
|
|
271
269
|
return "error" in data ? { ok: false, status, error: data.error } : { ok: true, status, data: data.data };
|
|
272
270
|
}, (err) => {
|
|
@@ -277,26 +275,26 @@ function http(axios10, config) {
|
|
|
277
275
|
return { ok: false, status: 500, error: err.message };
|
|
278
276
|
});
|
|
279
277
|
}
|
|
280
|
-
function httpGET(
|
|
278
|
+
function httpGET(axios11, request, transformParams) {
|
|
281
279
|
const config = typeof request === "string" ? { url: request } : request;
|
|
282
|
-
return (params) => http(
|
|
280
|
+
return (params) => http(axios11, {
|
|
283
281
|
...config,
|
|
284
282
|
method: "GET",
|
|
285
283
|
params: transformParams ? transformParams(params) : params
|
|
286
284
|
});
|
|
287
285
|
}
|
|
288
|
-
function httpPOST(
|
|
286
|
+
function httpPOST(axios11, request, transformPayload) {
|
|
289
287
|
const config = typeof request === "string" ? { url: request } : request;
|
|
290
|
-
return (payload) => http(
|
|
288
|
+
return (payload) => http(axios11, {
|
|
291
289
|
...config,
|
|
292
290
|
method: "POST",
|
|
293
291
|
data: transformPayload ? transformPayload(payload) : payload
|
|
294
292
|
});
|
|
295
293
|
}
|
|
296
|
-
function httpDELETE(
|
|
294
|
+
function httpDELETE(axios11, request, transformPayload) {
|
|
297
295
|
const config = typeof request === "string" ? { url: request } : request;
|
|
298
296
|
return (payload) => {
|
|
299
|
-
return http(
|
|
297
|
+
return http(axios11, {
|
|
300
298
|
...config,
|
|
301
299
|
method: "DELETE",
|
|
302
300
|
params: transformPayload ? transformPayload(payload) : payload
|
|
@@ -310,8 +308,8 @@ var init_lib = __esm({
|
|
|
310
308
|
});
|
|
311
309
|
|
|
312
310
|
// api/http/image/imageSave.ts
|
|
313
|
-
function httpImageSaveFactory(
|
|
314
|
-
return (params) => http(
|
|
311
|
+
function httpImageSaveFactory(axios11, provider) {
|
|
312
|
+
return (params) => http(axios11, {
|
|
315
313
|
method: "POST",
|
|
316
314
|
url: `images/save/${provider}`,
|
|
317
315
|
params: { prompt: params.prompt, provider }
|
|
@@ -325,8 +323,8 @@ var init_imageSave = __esm({
|
|
|
325
323
|
});
|
|
326
324
|
|
|
327
325
|
// api/http/image/imageSearch.ts
|
|
328
|
-
function httpImageSearchFactory(
|
|
329
|
-
return (params) => http(
|
|
326
|
+
function httpImageSearchFactory(axios11, provider) {
|
|
327
|
+
return (params) => http(axios11, {
|
|
330
328
|
method: "GET",
|
|
331
329
|
url: `images/search/${provider}`,
|
|
332
330
|
params: { prompt: params.prompt, page: params.page, provider }
|
|
@@ -340,8 +338,8 @@ var init_imageSearch = __esm({
|
|
|
340
338
|
});
|
|
341
339
|
|
|
342
340
|
// api/http/icon/listIcons.ts
|
|
343
|
-
function httpListIconsFactory(
|
|
344
|
-
return () => http(
|
|
341
|
+
function httpListIconsFactory(axios11, provider) {
|
|
342
|
+
return () => http(axios11, {
|
|
345
343
|
method: "GET",
|
|
346
344
|
url: `list/icons/${provider}`,
|
|
347
345
|
params: { provider }
|
|
@@ -355,8 +353,8 @@ var init_listIcons = __esm({
|
|
|
355
353
|
});
|
|
356
354
|
|
|
357
355
|
// api/http/icon/readIcon.ts
|
|
358
|
-
function httpReadIconFactory(
|
|
359
|
-
return (params) => http(
|
|
356
|
+
function httpReadIconFactory(axios11, provider) {
|
|
357
|
+
return (params) => http(axios11, {
|
|
360
358
|
method: "GET",
|
|
361
359
|
url: `read/icons/${provider}/icon.svg`,
|
|
362
360
|
params: { name: params.name }
|
|
@@ -369,65 +367,65 @@ var init_readIcon = __esm({
|
|
|
369
367
|
}
|
|
370
368
|
});
|
|
371
369
|
function apiFactory(baseURL) {
|
|
372
|
-
const
|
|
370
|
+
const axios11 = axios.create({ baseURL });
|
|
373
371
|
return {
|
|
374
|
-
createBulkBlock: httpPOST(
|
|
375
|
-
createBlock: httpPOST(
|
|
376
|
-
createDimension: httpPOST(
|
|
377
|
-
createFormatter: httpPOST(
|
|
378
|
-
createReport: httpPOST(
|
|
379
|
-
createSection: httpPOST(
|
|
380
|
-
createVariant: httpPOST(
|
|
381
|
-
deleteBlock: httpDELETE(
|
|
382
|
-
deleteDimension: httpDELETE(
|
|
383
|
-
deleteFormatter: httpDELETE(
|
|
384
|
-
deleteReport: httpDELETE(
|
|
385
|
-
deleteSection: httpDELETE(
|
|
386
|
-
deleteVariant: httpDELETE(
|
|
387
|
-
readBlock: httpGET(
|
|
388
|
-
readDimension: httpGET(
|
|
389
|
-
readFormatter: httpGET(
|
|
390
|
-
readReport: httpGET(
|
|
391
|
-
readSection: httpGET(
|
|
392
|
-
readVariant: httpGET(
|
|
393
|
-
updateBulkBlock: httpPOST(
|
|
394
|
-
updateBlock: httpPOST(
|
|
395
|
-
updateDimension: httpPOST(
|
|
396
|
-
updateFormatter: httpPOST(
|
|
397
|
-
updateReport: httpPOST(
|
|
398
|
-
updateSection: httpPOST(
|
|
399
|
-
updateVariant: httpPOST(
|
|
400
|
-
searchReport: httpGET(
|
|
401
|
-
validateVariantSlug: httpGET(
|
|
402
|
-
readMember: httpGET(
|
|
403
|
-
readMemberImage: httpGET(
|
|
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, {
|
|
404
402
|
url: "member/image",
|
|
405
403
|
responseType: "blob"
|
|
406
404
|
}),
|
|
407
|
-
searchMember: httpGET(
|
|
408
|
-
updateMember: httpGET(
|
|
409
|
-
imageLocalSearch: httpImageSearchFactory(
|
|
410
|
-
imageLocalSave: httpImageSaveFactory(
|
|
411
|
-
imageFlickrSearch: httpImageSearchFactory(
|
|
412
|
-
imageFlickrSave: httpImageSaveFactory(
|
|
413
|
-
imageUnsplashSearch: httpImageSearchFactory(
|
|
414
|
-
imageUnsplashSave: httpImageSaveFactory(
|
|
415
|
-
imageUploadSave: httpImageSaveFactory(
|
|
416
|
-
imageAdobeSearch: httpImageSearchFactory(
|
|
417
|
-
imageAdobeSave: httpImageSaveFactory(
|
|
418
|
-
readMetadata: httpGET(
|
|
419
|
-
regenerateSearch: httpPOST(
|
|
420
|
-
urlProxy: httpGET(
|
|
421
|
-
searchRole: httpGET(
|
|
422
|
-
searchUser: httpGET(
|
|
423
|
-
readUser: httpGET(
|
|
424
|
-
updateUser: httpPOST(
|
|
425
|
-
updateMyData: httpPOST(
|
|
426
|
-
revalidateReport: httpGET(
|
|
427
|
-
revalidateUrl: httpGET(
|
|
428
|
-
readPrivateBlocks: httpPOST(
|
|
429
|
-
listTablerIcons: httpListIconsFactory(
|
|
430
|
-
readTablerIcon: httpReadIconFactory(
|
|
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")
|
|
431
429
|
};
|
|
432
430
|
}
|
|
433
431
|
var transformDeletePayload, transformReadMembers;
|
|
@@ -4095,7 +4093,7 @@ var init_store = __esm({
|
|
|
4095
4093
|
storeWrapper = createWrapper(storeFactory);
|
|
4096
4094
|
}
|
|
4097
4095
|
});
|
|
4098
|
-
function withFetcher(Component,
|
|
4096
|
+
function withFetcher(Component, useRef14) {
|
|
4099
4097
|
const ComponentWithFetcher = (props) => {
|
|
4100
4098
|
const {
|
|
4101
4099
|
id,
|
|
@@ -4103,7 +4101,7 @@ function withFetcher(Component, useRef16) {
|
|
|
4103
4101
|
skelWidth,
|
|
4104
4102
|
...otherProps
|
|
4105
4103
|
} = props;
|
|
4106
|
-
const ref =
|
|
4104
|
+
const ref = useRef14(id);
|
|
4107
4105
|
if (ref.isUninitialized || ref.isFetching) {
|
|
4108
4106
|
return /* @__PURE__ */ jsx(Skeleton, { width: skelWidth, height: skelHeight });
|
|
4109
4107
|
}
|
|
@@ -5727,41 +5725,6 @@ var init_useScrollToAnchor = __esm({
|
|
|
5727
5725
|
init_esm_shims();
|
|
5728
5726
|
}
|
|
5729
5727
|
});
|
|
5730
|
-
function useEmbed() {
|
|
5731
|
-
const loaded = useRef(false);
|
|
5732
|
-
useEffect(() => {
|
|
5733
|
-
let prevRequests = 0;
|
|
5734
|
-
let thresholdCount = 0;
|
|
5735
|
-
const interval = setInterval(() => {
|
|
5736
|
-
const resources = performance.getEntriesByType("resource");
|
|
5737
|
-
const allRequestsComplete = resources.every((resource) => resource.responseEnd > 0);
|
|
5738
|
-
if (prevRequests === resources.length && allRequestsComplete && thresholdCount > 3) {
|
|
5739
|
-
clearInterval(interval);
|
|
5740
|
-
if (!loaded.current) {
|
|
5741
|
-
setTimeout(() => {
|
|
5742
|
-
window.parent.postMessage("iframe-loaded", "*");
|
|
5743
|
-
}, 500);
|
|
5744
|
-
loaded.current = true;
|
|
5745
|
-
}
|
|
5746
|
-
} else {
|
|
5747
|
-
if (resources.length > prevRequests) {
|
|
5748
|
-
thresholdCount = 0;
|
|
5749
|
-
} else {
|
|
5750
|
-
thresholdCount++;
|
|
5751
|
-
}
|
|
5752
|
-
prevRequests = resources.length;
|
|
5753
|
-
}
|
|
5754
|
-
}, 300);
|
|
5755
|
-
if (typeof document !== "undefined") {
|
|
5756
|
-
initFrameAndPoll();
|
|
5757
|
-
}
|
|
5758
|
-
}, []);
|
|
5759
|
-
}
|
|
5760
|
-
var init_useEmbed = __esm({
|
|
5761
|
-
"frontend/hooks/useEmbed.ts"() {
|
|
5762
|
-
init_esm_shims();
|
|
5763
|
-
}
|
|
5764
|
-
});
|
|
5765
5728
|
function createOutline(elements, variables) {
|
|
5766
5729
|
const roots = [];
|
|
5767
5730
|
const nodes = {};
|
|
@@ -5879,7 +5842,6 @@ var init_hooks2 = __esm({
|
|
|
5879
5842
|
init_useInitialState();
|
|
5880
5843
|
init_useOnChangeSelector();
|
|
5881
5844
|
init_useScrollToAnchor();
|
|
5882
|
-
init_useEmbed();
|
|
5883
5845
|
init_useContentOutline();
|
|
5884
5846
|
}
|
|
5885
5847
|
});
|
|
@@ -15979,25 +15941,6 @@ var init_DataTab = __esm({
|
|
|
15979
15941
|
};
|
|
15980
15942
|
}
|
|
15981
15943
|
});
|
|
15982
|
-
function CopyInput(props) {
|
|
15983
|
-
const { title, url, disabled = false } = props;
|
|
15984
|
-
const Icon = disabled ? IconCode : IconLink;
|
|
15985
|
-
return /* @__PURE__ */ jsx(Input.Wrapper, { label: title, children: /* @__PURE__ */ jsx(
|
|
15986
|
-
Input,
|
|
15987
|
-
{
|
|
15988
|
-
icon: /* @__PURE__ */ jsx(Icon, {}),
|
|
15989
|
-
value: url,
|
|
15990
|
-
disabled,
|
|
15991
|
-
readOnly: true,
|
|
15992
|
-
rightSection: /* @__PURE__ */ jsx(CopyButton, { value: url, timeout: 2e3, children: ({ copied, copy }) => /* @__PURE__ */ jsx(Tooltip, { label: copied ? "Copied" : "Copy", withArrow: true, position: "right", children: /* @__PURE__ */ jsx(ActionIcon, { variant: copied ? "filled" : "subtle", onClick: copy, disabled, children: copied ? /* @__PURE__ */ jsx(IconClipboardCheck, { size: 16 }) : /* @__PURE__ */ jsx(IconClipboardCopy, { size: 16 }) }) }) })
|
|
15993
|
-
}
|
|
15994
|
-
) });
|
|
15995
|
-
}
|
|
15996
|
-
var init_CopyInput = __esm({
|
|
15997
|
-
"components/options/CopyInput.tsx"() {
|
|
15998
|
-
init_esm_shims();
|
|
15999
|
-
}
|
|
16000
|
-
});
|
|
16001
15944
|
function useLocalePrefix() {
|
|
16002
15945
|
const { locales: locales4 } = useRouter();
|
|
16003
15946
|
const locale = location && location.pathname.split("/").filter((segment) => segment.length > 0)[0];
|
|
@@ -16008,147 +15951,12 @@ var init_useLocalePrefix = __esm({
|
|
|
16008
15951
|
init_esm_shims();
|
|
16009
15952
|
}
|
|
16010
15953
|
});
|
|
16011
|
-
function useEmbedPath(sectionId, vizId = void 0, queryParams = {}) {
|
|
16012
|
-
const { query, locale } = useRouter();
|
|
16013
|
-
const prefixLocale = useLocalePrefix();
|
|
16014
|
-
const { pathSegment } = useBespoke();
|
|
16015
|
-
const slugs = query[pathSegment];
|
|
16016
|
-
if (slugs && Array.isArray(slugs)) {
|
|
16017
|
-
const search = new URLSearchParams({ section: sectionId, ...queryParams });
|
|
16018
|
-
if (vizId) {
|
|
16019
|
-
search.set("viz", vizId);
|
|
16020
|
-
}
|
|
16021
|
-
const embed = `/embed/${slugs.join("/")}`;
|
|
16022
|
-
const url = new URL(
|
|
16023
|
-
prefixLocale ? `/${locale}${embed}` : `${embed}`,
|
|
16024
|
-
location.href
|
|
16025
|
-
);
|
|
16026
|
-
url.search = search.toString();
|
|
16027
|
-
return url.href;
|
|
16028
|
-
}
|
|
16029
|
-
return void 0;
|
|
16030
|
-
}
|
|
16031
|
-
function ShareTab(props) {
|
|
16032
|
-
const { section } = props;
|
|
16033
|
-
const [shareUrl, setShareUrl] = useState("");
|
|
16034
|
-
const [title, setTitle] = useState("");
|
|
16035
|
-
const [includeSection, setIncludeSection] = useState(true);
|
|
16036
|
-
const optionsTranslations = useBespokeTranslations("options");
|
|
16037
|
-
const embedPath = useEmbedPath(section.id);
|
|
16038
|
-
const iframeStr = `<iframe width="100%" height="500px" frameborder="0" src="${embedPath}"></iframe>`;
|
|
16039
|
-
const translations = { ...DEFAULT_TRANSLATIONS3, ...optionsTranslations["share_tab"] };
|
|
16040
|
-
useEffect(() => {
|
|
16041
|
-
setTitle(document.title);
|
|
16042
|
-
const { origin, pathname } = window.location;
|
|
16043
|
-
const url = `${origin}${pathname}`;
|
|
16044
|
-
const sectionAnchor = includeSection ? `#section-${section.id}` : "";
|
|
16045
|
-
setShareUrl(`${url}${sectionAnchor}`);
|
|
16046
|
-
}, [includeSection]);
|
|
16047
|
-
return /* @__PURE__ */ jsxs(Stack, { className: "cms-section-options-share", children: [
|
|
16048
|
-
/* @__PURE__ */ jsx(Space, { h: "xs" }),
|
|
16049
|
-
/* @__PURE__ */ jsx(CopyInput, { title: `${translations["title"]}: ${title}`, url: shareUrl }),
|
|
16050
|
-
/* @__PURE__ */ jsx(
|
|
16051
|
-
Checkbox,
|
|
16052
|
-
{
|
|
16053
|
-
checked: includeSection,
|
|
16054
|
-
label: translations["include_section"],
|
|
16055
|
-
onChange: () => setIncludeSection(!includeSection),
|
|
16056
|
-
radius: "xl"
|
|
16057
|
-
}
|
|
16058
|
-
),
|
|
16059
|
-
/* @__PURE__ */ jsx(CopyInput, { title: translations["embed"], url: iframeStr }),
|
|
16060
|
-
/* @__PURE__ */ jsxs(Group, { position: "center", children: [
|
|
16061
|
-
/* @__PURE__ */ jsx(
|
|
16062
|
-
FacebookShareButton,
|
|
16063
|
-
{
|
|
16064
|
-
url: shareUrl,
|
|
16065
|
-
quote: title,
|
|
16066
|
-
children: /* @__PURE__ */ jsx(FacebookIcon, { size: 32, round: true })
|
|
16067
|
-
}
|
|
16068
|
-
),
|
|
16069
|
-
/* @__PURE__ */ jsx(
|
|
16070
|
-
TwitterShareButton,
|
|
16071
|
-
{
|
|
16072
|
-
url: shareUrl,
|
|
16073
|
-
title,
|
|
16074
|
-
children: /* @__PURE__ */ jsx(TwitterIcon, { size: 32, round: true })
|
|
16075
|
-
}
|
|
16076
|
-
),
|
|
16077
|
-
/* @__PURE__ */ jsx(
|
|
16078
|
-
TelegramShareButton,
|
|
16079
|
-
{
|
|
16080
|
-
url: shareUrl,
|
|
16081
|
-
title,
|
|
16082
|
-
children: /* @__PURE__ */ jsx(TelegramIcon, { size: 32, round: true })
|
|
16083
|
-
}
|
|
16084
|
-
),
|
|
16085
|
-
/* @__PURE__ */ jsx(
|
|
16086
|
-
WhatsappShareButton,
|
|
16087
|
-
{
|
|
16088
|
-
url: shareUrl,
|
|
16089
|
-
title,
|
|
16090
|
-
separator: ":: ",
|
|
16091
|
-
children: /* @__PURE__ */ jsx(WhatsappIcon, { size: 32, round: true })
|
|
16092
|
-
}
|
|
16093
|
-
),
|
|
16094
|
-
/* @__PURE__ */ jsx(LinkedinShareButton, { url: shareUrl, children: /* @__PURE__ */ jsx(LinkedinIcon, { size: 32, round: true }) }),
|
|
16095
|
-
/* @__PURE__ */ jsx(
|
|
16096
|
-
RedditShareButton,
|
|
16097
|
-
{
|
|
16098
|
-
url: shareUrl,
|
|
16099
|
-
title,
|
|
16100
|
-
windowWidth: 660,
|
|
16101
|
-
windowHeight: 460,
|
|
16102
|
-
children: /* @__PURE__ */ jsx(RedditIcon, { size: 32, round: true })
|
|
16103
|
-
}
|
|
16104
|
-
),
|
|
16105
|
-
/* @__PURE__ */ jsx(
|
|
16106
|
-
EmailShareButton,
|
|
16107
|
-
{
|
|
16108
|
-
url: shareUrl,
|
|
16109
|
-
subject: title,
|
|
16110
|
-
children: /* @__PURE__ */ jsx(EmailIcon, { size: 32, round: true })
|
|
16111
|
-
}
|
|
16112
|
-
)
|
|
16113
|
-
] })
|
|
16114
|
-
] });
|
|
16115
|
-
}
|
|
16116
|
-
var DEFAULT_TRANSLATIONS3;
|
|
16117
|
-
var init_ShareTab = __esm({
|
|
16118
|
-
"components/options/tabs/ShareTab.tsx"() {
|
|
16119
|
-
init_esm_shims();
|
|
16120
|
-
init_CopyInput();
|
|
16121
|
-
init_TranslationsProvider();
|
|
16122
|
-
init_useLocalePrefix();
|
|
16123
|
-
init_ResourceProvider();
|
|
16124
|
-
DEFAULT_TRANSLATIONS3 = {
|
|
16125
|
-
"title": "Title",
|
|
16126
|
-
"include_section": "Include Section",
|
|
16127
|
-
"embed": "Embed"
|
|
16128
|
-
};
|
|
16129
|
-
}
|
|
16130
|
-
});
|
|
16131
|
-
function exportSVG(svg) {
|
|
16132
|
-
const serializer = new XMLSerializer();
|
|
16133
|
-
let source = serializer.serializeToString(svg);
|
|
16134
|
-
if (!source.match(/^<svg[^>]+xmlns="http\:\/\/www\.w3\.org\/2000\/svg"/)) {
|
|
16135
|
-
source = source.replace(/^<svg/, '<svg xmlns="http://www.w3.org/2000/svg"');
|
|
16136
|
-
}
|
|
16137
|
-
if (!source.match(/^<svg[^>]+"http\:\/\/www\.w3\.org\/1999\/xlink"/)) {
|
|
16138
|
-
source = source.replace(/^<svg/, '<svg xmlns:xlink="http://www.w3.org/1999/xlink"');
|
|
16139
|
-
}
|
|
16140
|
-
source = '<?xml version="1.0" standalone="no"?>\r\n' + source;
|
|
16141
|
-
const url = "data:image/svg+xml;charset=utf-8," + encodeURIComponent(source);
|
|
16142
|
-
const a2 = document.createElement("a");
|
|
16143
|
-
const e = new MouseEvent("click");
|
|
16144
|
-
a2.download = "image.svg";
|
|
16145
|
-
a2.href = url;
|
|
16146
|
-
a2.dispatchEvent(e);
|
|
16147
|
-
}
|
|
16148
15954
|
function ImageTab(props) {
|
|
16149
15955
|
const { section } = props;
|
|
16150
15956
|
const blocksIds = section.blocks || [];
|
|
16151
|
-
const
|
|
15957
|
+
const { asPath, query, locale } = useRouter();
|
|
15958
|
+
const prefixLocale = useLocalePrefix();
|
|
15959
|
+
const { pathSegment } = useBespoke();
|
|
16152
15960
|
const vizAvailable = useAppSelector((state) => blocksIds.map((blockId) => state.records.entities.block[blockId]).filter((block) => block.type === "visualization" && state.variables.status[block.id].allowed));
|
|
16153
15961
|
const [imageContext, setImageContext] = useState(contextOptions.section);
|
|
16154
15962
|
const [imageFormat, setImageFormat] = useState(formatOptions2.png);
|
|
@@ -16158,13 +15966,62 @@ function ImageTab(props) {
|
|
|
16158
15966
|
const [transparentBackground, setTransparentBackground] = useState(false);
|
|
16159
15967
|
const [loadingPreviews, setLoadingPreviews] = useState(false);
|
|
16160
15968
|
const optionsTranslations = useBespokeTranslations("options");
|
|
16161
|
-
const translations = { ...
|
|
16162
|
-
const search = Object.fromEntries(new URLSearchParams(location.search));
|
|
16163
|
-
const selectors = Object.fromEntries(
|
|
16164
|
-
Object.entries(search).filter(([key]) => key.match(/selector\d+id/))
|
|
16165
|
-
);
|
|
15969
|
+
const translations = { ...DEFAULT_TRANSLATIONS3, ...optionsTranslations["image_tab"] };
|
|
16166
15970
|
const sectionId = slugify_default(section?.settings?.name) || `section-${section.id}`;
|
|
16167
|
-
const
|
|
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]));
|
|
15988
|
+
const link = document.createElement("a");
|
|
15989
|
+
link.href = url;
|
|
15990
|
+
link.setAttribute("download", "image.png");
|
|
15991
|
+
document.body.appendChild(link);
|
|
15992
|
+
link.click();
|
|
15993
|
+
document.body.removeChild(link);
|
|
15994
|
+
setImageProcessing(false);
|
|
15995
|
+
});
|
|
15996
|
+
};
|
|
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/))
|
|
16001
|
+
);
|
|
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
|
|
16012
|
+
};
|
|
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);
|
|
16022
|
+
setImageProcessing(false);
|
|
16023
|
+
});
|
|
16024
|
+
};
|
|
16168
16025
|
const generatePreviews = async () => {
|
|
16169
16026
|
const { toCanvas } = await import('html-to-image');
|
|
16170
16027
|
Promise.all(
|
|
@@ -16190,54 +16047,6 @@ function ImageTab(props) {
|
|
|
16190
16047
|
const onSelectViz = (vizId) => () => {
|
|
16191
16048
|
setVizSelected(vizId);
|
|
16192
16049
|
};
|
|
16193
|
-
const onDownloadIframe = () => {
|
|
16194
|
-
setImageProcessing(true);
|
|
16195
|
-
const container = document.getElementById("iframe-preview");
|
|
16196
|
-
framer.current = new Framer(container, { src: embedPath });
|
|
16197
|
-
};
|
|
16198
|
-
useEffect(() => {
|
|
16199
|
-
const onIframeLoad = async (e) => {
|
|
16200
|
-
if (e.data === "iframe-loaded") {
|
|
16201
|
-
if (imageFormat === formatOptions2.png) {
|
|
16202
|
-
const iframe = document.querySelector("#iframe-preview > iframe");
|
|
16203
|
-
const doc = iframe.contentDocument;
|
|
16204
|
-
const node = doc?.querySelector(".bespoke-Section-root");
|
|
16205
|
-
const fontEmbedCSS = await getFontEmbedCSS(node);
|
|
16206
|
-
toPng(
|
|
16207
|
-
node,
|
|
16208
|
-
{
|
|
16209
|
-
backgroundColor: transparentBackground ? "transparent" : "white",
|
|
16210
|
-
cacheBust: true,
|
|
16211
|
-
skipFonts: true,
|
|
16212
|
-
fontEmbedCSS
|
|
16213
|
-
}
|
|
16214
|
-
).then(function(dataUrl) {
|
|
16215
|
-
const link = document.createElement("a");
|
|
16216
|
-
link.href = dataUrl;
|
|
16217
|
-
link.setAttribute("download", `${sectionId}.png`);
|
|
16218
|
-
document.body.appendChild(link);
|
|
16219
|
-
link.click();
|
|
16220
|
-
document.body.removeChild(link);
|
|
16221
|
-
setImageProcessing(false);
|
|
16222
|
-
if (typeof framer.current?.remove === "function") {
|
|
16223
|
-
framer.current.remove();
|
|
16224
|
-
}
|
|
16225
|
-
}).catch(function(error) {
|
|
16226
|
-
console.error("oops, something went wrong!", error);
|
|
16227
|
-
setImageProcessing(false);
|
|
16228
|
-
});
|
|
16229
|
-
} else {
|
|
16230
|
-
const iframe = document.querySelector("#iframe-preview > iframe");
|
|
16231
|
-
const doc = iframe.contentDocument;
|
|
16232
|
-
const svg = doc?.querySelector("svg.d3plus-viz");
|
|
16233
|
-
if (svg)
|
|
16234
|
-
exportSVG(svg);
|
|
16235
|
-
}
|
|
16236
|
-
}
|
|
16237
|
-
};
|
|
16238
|
-
window.addEventListener("message", onIframeLoad);
|
|
16239
|
-
return () => window.removeEventListener("message", onIframeLoad);
|
|
16240
|
-
}, [transparentBackground]);
|
|
16241
16050
|
return /* @__PURE__ */ jsxs(Stack, { className: "cms-section-options-image", children: [
|
|
16242
16051
|
/* @__PURE__ */ jsx(Space, { h: "xs" }),
|
|
16243
16052
|
vizAvailable.length > 0 ? /* @__PURE__ */ jsx(Input.Wrapper, { label: `${translations["choose_area"]}:`, children: /* @__PURE__ */ jsx(
|
|
@@ -16284,7 +16093,28 @@ function ImageTab(props) {
|
|
|
16284
16093
|
]
|
|
16285
16094
|
}
|
|
16286
16095
|
),
|
|
16287
|
-
/* @__PURE__ */ jsx(
|
|
16096
|
+
imageContext !== contextOptions.section && /* @__PURE__ */ jsx(Input.Wrapper, { label: `${translations["choose_format"]}:`, children: /* @__PURE__ */ jsxs(Button.Group, { children: [
|
|
16097
|
+
/* @__PURE__ */ jsx(
|
|
16098
|
+
Button,
|
|
16099
|
+
{
|
|
16100
|
+
leftIcon: /* @__PURE__ */ jsx(IconPolaroid, { size: 16 }),
|
|
16101
|
+
onClick: () => setImageFormat(formatOptions2.png),
|
|
16102
|
+
variant: imageFormat === formatOptions2.png ? "filled" : "default",
|
|
16103
|
+
fullWidth: true,
|
|
16104
|
+
children: formatOptions2.png
|
|
16105
|
+
}
|
|
16106
|
+
),
|
|
16107
|
+
/* @__PURE__ */ jsx(
|
|
16108
|
+
Button,
|
|
16109
|
+
{
|
|
16110
|
+
leftIcon: /* @__PURE__ */ jsx(IconCode, { size: 16 }),
|
|
16111
|
+
onClick: () => setImageFormat(formatOptions2.svg),
|
|
16112
|
+
variant: imageFormat === formatOptions2.svg ? "filled" : "default",
|
|
16113
|
+
fullWidth: true,
|
|
16114
|
+
children: formatOptions2.svg
|
|
16115
|
+
}
|
|
16116
|
+
)
|
|
16117
|
+
] }) }),
|
|
16288
16118
|
imageFormat !== formatOptions2.svg && /* @__PURE__ */ jsx(
|
|
16289
16119
|
Checkbox,
|
|
16290
16120
|
{
|
|
@@ -16299,25 +16129,26 @@ function ImageTab(props) {
|
|
|
16299
16129
|
{
|
|
16300
16130
|
leftIcon: /* @__PURE__ */ jsx(IconDownload, { size: 16 }),
|
|
16301
16131
|
loading: imageProcessing,
|
|
16302
|
-
onClick:
|
|
16132
|
+
onClick: imageContext === contextOptions.section ? onSaveClickSection : onSaveClickViz,
|
|
16303
16133
|
fullWidth: true,
|
|
16304
16134
|
children: /* @__PURE__ */ jsx("span", { children: imageProcessing ? translations["processing"] : translations["download"].replace("{imageFormat}", imageFormat) })
|
|
16305
16135
|
}
|
|
16306
16136
|
)
|
|
16307
16137
|
] });
|
|
16308
16138
|
}
|
|
16309
|
-
var contextOptions, formatOptions2, VizPreview2,
|
|
16139
|
+
var contextOptions, formatOptions2, VizPreview2, DEFAULT_TRANSLATIONS3, getSectionNode, getVizNode;
|
|
16310
16140
|
var init_ImageTab = __esm({
|
|
16311
16141
|
"components/options/tabs/ImageTab.tsx"() {
|
|
16312
16142
|
init_esm_shims();
|
|
16313
16143
|
init_slugify();
|
|
16314
16144
|
init_store2();
|
|
16315
16145
|
init_TranslationsProvider();
|
|
16146
|
+
init_ResourceProvider();
|
|
16147
|
+
init_useLocalePrefix();
|
|
16316
16148
|
init_d3plusPropify();
|
|
16317
16149
|
init_varSwapRecursive();
|
|
16318
16150
|
init_getBlockContent();
|
|
16319
16151
|
init_hooks();
|
|
16320
|
-
init_ShareTab();
|
|
16321
16152
|
contextOptions = {
|
|
16322
16153
|
viz: "viz",
|
|
16323
16154
|
section: "section"
|
|
@@ -16371,7 +16202,7 @@ var init_ImageTab = __esm({
|
|
|
16371
16202
|
)
|
|
16372
16203
|
] });
|
|
16373
16204
|
};
|
|
16374
|
-
|
|
16205
|
+
DEFAULT_TRANSLATIONS3 = {
|
|
16375
16206
|
"wrong_node": "Wrong node in export",
|
|
16376
16207
|
"choose_area": "Choose image area",
|
|
16377
16208
|
"choose_viz": "Choose visualization",
|
|
@@ -16384,6 +16215,7 @@ var init_ImageTab = __esm({
|
|
|
16384
16215
|
"source": "Source: {src}"
|
|
16385
16216
|
};
|
|
16386
16217
|
getSectionNode = (sectionId) => {
|
|
16218
|
+
console.log({ sectionId });
|
|
16387
16219
|
const sectionNode = document.getElementById(sectionId);
|
|
16388
16220
|
return select(sectionNode).select(".bespoke-section-content").node();
|
|
16389
16221
|
};
|
|
@@ -16393,6 +16225,142 @@ var init_ImageTab = __esm({
|
|
|
16393
16225
|
};
|
|
16394
16226
|
}
|
|
16395
16227
|
});
|
|
16228
|
+
function CopyInput(props) {
|
|
16229
|
+
const { title, url, disabled = false } = props;
|
|
16230
|
+
const Icon = disabled ? IconCode : IconLink;
|
|
16231
|
+
return /* @__PURE__ */ jsx(Input.Wrapper, { label: title, children: /* @__PURE__ */ jsx(
|
|
16232
|
+
Input,
|
|
16233
|
+
{
|
|
16234
|
+
icon: /* @__PURE__ */ jsx(Icon, {}),
|
|
16235
|
+
value: url,
|
|
16236
|
+
disabled,
|
|
16237
|
+
readOnly: true,
|
|
16238
|
+
rightSection: /* @__PURE__ */ jsx(CopyButton, { value: url, timeout: 2e3, children: ({ copied, copy }) => /* @__PURE__ */ jsx(Tooltip, { label: copied ? "Copied" : "Copy", withArrow: true, position: "right", children: /* @__PURE__ */ jsx(ActionIcon, { variant: copied ? "filled" : "subtle", onClick: copy, disabled, children: copied ? /* @__PURE__ */ jsx(IconClipboardCheck, { size: 16 }) : /* @__PURE__ */ jsx(IconClipboardCopy, { size: 16 }) }) }) })
|
|
16239
|
+
}
|
|
16240
|
+
) });
|
|
16241
|
+
}
|
|
16242
|
+
var init_CopyInput = __esm({
|
|
16243
|
+
"components/options/CopyInput.tsx"() {
|
|
16244
|
+
init_esm_shims();
|
|
16245
|
+
}
|
|
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
|
+
}
|
|
16264
|
+
function ShareTab(props) {
|
|
16265
|
+
const { section } = props;
|
|
16266
|
+
const [shareUrl, setShareUrl] = useState("");
|
|
16267
|
+
const [title, setTitle] = useState("");
|
|
16268
|
+
const [includeSection, setIncludeSection] = useState(true);
|
|
16269
|
+
const optionsTranslations = useBespokeTranslations("options");
|
|
16270
|
+
const embedPath = useEmbedPath(section.id);
|
|
16271
|
+
const iframeStr = `<iframe width="100%" height="500px" frameborder="0" src="${embedPath}"></iframe>`;
|
|
16272
|
+
const translations = { ...DEFAULT_TRANSLATIONS4, ...optionsTranslations["share_tab"] };
|
|
16273
|
+
useEffect(() => {
|
|
16274
|
+
setTitle(document.title);
|
|
16275
|
+
const { origin, pathname } = window.location;
|
|
16276
|
+
const url = `${origin}${pathname}`;
|
|
16277
|
+
const sectionAnchor = includeSection ? `#section-${section.id}` : "";
|
|
16278
|
+
setShareUrl(`${url}${sectionAnchor}`);
|
|
16279
|
+
}, [includeSection]);
|
|
16280
|
+
return /* @__PURE__ */ jsxs(Stack, { className: "cms-section-options-share", children: [
|
|
16281
|
+
/* @__PURE__ */ jsx(Space, { h: "xs" }),
|
|
16282
|
+
/* @__PURE__ */ jsx(CopyInput, { title: `${translations["title"]}: ${title}`, url: shareUrl }),
|
|
16283
|
+
/* @__PURE__ */ jsx(
|
|
16284
|
+
Checkbox,
|
|
16285
|
+
{
|
|
16286
|
+
checked: includeSection,
|
|
16287
|
+
label: translations["include_section"],
|
|
16288
|
+
onChange: () => setIncludeSection(!includeSection),
|
|
16289
|
+
radius: "xl"
|
|
16290
|
+
}
|
|
16291
|
+
),
|
|
16292
|
+
/* @__PURE__ */ jsx(CopyInput, { title: translations["embed"], url: iframeStr }),
|
|
16293
|
+
/* @__PURE__ */ jsxs(Group, { position: "center", children: [
|
|
16294
|
+
/* @__PURE__ */ jsx(
|
|
16295
|
+
FacebookShareButton,
|
|
16296
|
+
{
|
|
16297
|
+
url: shareUrl,
|
|
16298
|
+
quote: title,
|
|
16299
|
+
children: /* @__PURE__ */ jsx(FacebookIcon, { size: 32, round: true })
|
|
16300
|
+
}
|
|
16301
|
+
),
|
|
16302
|
+
/* @__PURE__ */ jsx(
|
|
16303
|
+
TwitterShareButton,
|
|
16304
|
+
{
|
|
16305
|
+
url: shareUrl,
|
|
16306
|
+
title,
|
|
16307
|
+
children: /* @__PURE__ */ jsx(TwitterIcon, { size: 32, round: true })
|
|
16308
|
+
}
|
|
16309
|
+
),
|
|
16310
|
+
/* @__PURE__ */ jsx(
|
|
16311
|
+
TelegramShareButton,
|
|
16312
|
+
{
|
|
16313
|
+
url: shareUrl,
|
|
16314
|
+
title,
|
|
16315
|
+
children: /* @__PURE__ */ jsx(TelegramIcon, { size: 32, round: true })
|
|
16316
|
+
}
|
|
16317
|
+
),
|
|
16318
|
+
/* @__PURE__ */ jsx(
|
|
16319
|
+
WhatsappShareButton,
|
|
16320
|
+
{
|
|
16321
|
+
url: shareUrl,
|
|
16322
|
+
title,
|
|
16323
|
+
separator: ":: ",
|
|
16324
|
+
children: /* @__PURE__ */ jsx(WhatsappIcon, { size: 32, round: true })
|
|
16325
|
+
}
|
|
16326
|
+
),
|
|
16327
|
+
/* @__PURE__ */ jsx(LinkedinShareButton, { url: shareUrl, children: /* @__PURE__ */ jsx(LinkedinIcon, { size: 32, round: true }) }),
|
|
16328
|
+
/* @__PURE__ */ jsx(
|
|
16329
|
+
RedditShareButton,
|
|
16330
|
+
{
|
|
16331
|
+
url: shareUrl,
|
|
16332
|
+
title,
|
|
16333
|
+
windowWidth: 660,
|
|
16334
|
+
windowHeight: 460,
|
|
16335
|
+
children: /* @__PURE__ */ jsx(RedditIcon, { size: 32, round: true })
|
|
16336
|
+
}
|
|
16337
|
+
),
|
|
16338
|
+
/* @__PURE__ */ jsx(
|
|
16339
|
+
EmailShareButton,
|
|
16340
|
+
{
|
|
16341
|
+
url: shareUrl,
|
|
16342
|
+
subject: title,
|
|
16343
|
+
children: /* @__PURE__ */ jsx(EmailIcon, { size: 32, round: true })
|
|
16344
|
+
}
|
|
16345
|
+
)
|
|
16346
|
+
] })
|
|
16347
|
+
] });
|
|
16348
|
+
}
|
|
16349
|
+
var DEFAULT_TRANSLATIONS4;
|
|
16350
|
+
var init_ShareTab = __esm({
|
|
16351
|
+
"components/options/tabs/ShareTab.tsx"() {
|
|
16352
|
+
init_esm_shims();
|
|
16353
|
+
init_CopyInput();
|
|
16354
|
+
init_TranslationsProvider();
|
|
16355
|
+
init_useLocalePrefix();
|
|
16356
|
+
init_ResourceProvider();
|
|
16357
|
+
DEFAULT_TRANSLATIONS4 = {
|
|
16358
|
+
"title": "Title",
|
|
16359
|
+
"include_section": "Include Section",
|
|
16360
|
+
"embed": "Embed"
|
|
16361
|
+
};
|
|
16362
|
+
}
|
|
16363
|
+
});
|
|
16396
16364
|
function OptionsModal(props) {
|
|
16397
16365
|
const {
|
|
16398
16366
|
section,
|
|
@@ -17522,8 +17490,8 @@ var init_Section = __esm({
|
|
|
17522
17490
|
const slugs = Array.isArray(querySlugs) ? querySlugs?.join("/") : [];
|
|
17523
17491
|
const prefixLocale = useLocalePrefix();
|
|
17524
17492
|
let path = prefixLocale ? `/${locale}` : "";
|
|
17525
|
-
path +=
|
|
17526
|
-
const link = new URL(path, window
|
|
17493
|
+
path += `/${profilePrefix}/${slugs}/`;
|
|
17494
|
+
const link = new URL(path, window?.location.href).href;
|
|
17527
17495
|
return /* @__PURE__ */ jsxs(Group, { position: "apart", noWrap: true, sx: { flexGrow: 1 }, children: [
|
|
17528
17496
|
/* @__PURE__ */ jsx(Anchor, { href: link, target: "_blank", children: link }),
|
|
17529
17497
|
/* @__PURE__ */ jsx(
|
|
@@ -17726,9 +17694,6 @@ var LoginButton_default = BespokeLoginBtn;
|
|
|
17726
17694
|
init_esm_shims();
|
|
17727
17695
|
var UserProvider_default = UserProvider;
|
|
17728
17696
|
|
|
17729
|
-
// frontend/index.ts
|
|
17730
|
-
init_hooks2();
|
|
17731
|
-
|
|
17732
17697
|
// frontend/components/auth/withPageRoleAuthRequired.tsx
|
|
17733
17698
|
init_esm_shims();
|
|
17734
17699
|
function withPageRoleAuthRequired(params) {
|
|
@@ -25853,4 +25818,4 @@ function BespokeRenderer({
|
|
|
25853
25818
|
] }) });
|
|
25854
25819
|
}
|
|
25855
25820
|
|
|
25856
|
-
export { Explore_default as BespokeExplore, ExploreModal_default as BespokeExploreModal, LoginButton_default as BespokeLoginBtn, BespokeManager, BespokeRenderer, Report_default as BespokeReport, Search_default as BespokeSearch, UserProvider_default as BespokeUserProvider, withPageRoleAuthRequired as BespokeWithPageRoleAuthRequired, CMS_ROLES, DialogProvider, actions_exports as actions, storeWrapper, useAppDispatch as useBespokeDispatch, useAppSelector as useBespokeSelector, useUser_default as useBespokeUser, useDialog
|
|
25821
|
+
export { Explore_default as BespokeExplore, ExploreModal_default as BespokeExploreModal, LoginButton_default as BespokeLoginBtn, BespokeManager, BespokeRenderer, Report_default as BespokeReport, Search_default as BespokeSearch, UserProvider_default as BespokeUserProvider, withPageRoleAuthRequired as BespokeWithPageRoleAuthRequired, CMS_ROLES, DialogProvider, actions_exports as actions, storeWrapper, useAppDispatch as useBespokeDispatch, useAppSelector as useBespokeSelector, useUser_default as useBespokeUser, useDialog };
|
package/dist/server.js
CHANGED
|
@@ -26,6 +26,7 @@ import * as allIcons from '@tabler/icons-react';
|
|
|
26
26
|
import NextCors from 'nextjs-cors';
|
|
27
27
|
import imageType from 'image-type';
|
|
28
28
|
import formidable from 'formidable';
|
|
29
|
+
import puppeteer from 'puppeteer';
|
|
29
30
|
import DomParser from 'dom-parser';
|
|
30
31
|
import path from 'path';
|
|
31
32
|
import axiosRetry from 'axios-retry';
|
|
@@ -4219,6 +4220,242 @@ function endpointUpdateMyDataFactory(operations) {
|
|
|
4219
4220
|
}
|
|
4220
4221
|
}, []);
|
|
4221
4222
|
}
|
|
4223
|
+
async function generatePDF(path2) {
|
|
4224
|
+
const width = 1920;
|
|
4225
|
+
const height = 1080;
|
|
4226
|
+
const maxTimeoutPerPage = 0;
|
|
4227
|
+
try {
|
|
4228
|
+
const browser = await puppeteer.launch({
|
|
4229
|
+
args: [
|
|
4230
|
+
`--window-size=${width},${height}`,
|
|
4231
|
+
"--start-fullscreen"
|
|
4232
|
+
],
|
|
4233
|
+
defaultViewport: { width, height },
|
|
4234
|
+
headless: "new",
|
|
4235
|
+
ignoreHTTPSErrors: true
|
|
4236
|
+
});
|
|
4237
|
+
const url = new URL(path2);
|
|
4238
|
+
const searchParams = new URLSearchParams(url.search);
|
|
4239
|
+
searchParams.set("print", "true");
|
|
4240
|
+
url.search = searchParams.toString();
|
|
4241
|
+
const page = await browser.newPage();
|
|
4242
|
+
await page.goto(url.toString(), { waitUntil: "networkidle0", timeout: maxTimeoutPerPage });
|
|
4243
|
+
await page.waitForSelector("#bespoke-report", { timeout: maxTimeoutPerPage });
|
|
4244
|
+
await page.evaluate(async () => {
|
|
4245
|
+
await new Promise((resolve) => {
|
|
4246
|
+
const scrollInterval = setInterval(() => {
|
|
4247
|
+
window.scrollBy(0, window.innerHeight);
|
|
4248
|
+
}, 100);
|
|
4249
|
+
const checkNetworkActivity = () => {
|
|
4250
|
+
const activeRequests = performance.getEntriesByType("resource").filter((entry) => entry.duration === 0);
|
|
4251
|
+
return activeRequests.length === 0;
|
|
4252
|
+
};
|
|
4253
|
+
const checkInterval = setInterval(() => {
|
|
4254
|
+
if (checkNetworkActivity()) {
|
|
4255
|
+
clearInterval(scrollInterval);
|
|
4256
|
+
clearInterval(checkInterval);
|
|
4257
|
+
resolve();
|
|
4258
|
+
}
|
|
4259
|
+
}, 1e3);
|
|
4260
|
+
});
|
|
4261
|
+
});
|
|
4262
|
+
const pdf = await page.pdf({
|
|
4263
|
+
displayHeaderFooter: true,
|
|
4264
|
+
footerTemplate: `
|
|
4265
|
+
<div
|
|
4266
|
+
style="
|
|
4267
|
+
color: lightgray; border-top: solid lightgray 1px; font-size: 10px;
|
|
4268
|
+
padding-top: 5px; text-align: center; width: 100%;
|
|
4269
|
+
"
|
|
4270
|
+
>
|
|
4271
|
+
<p>Page <span class="pageNumber" /> of <span class="totalPages" /></p>
|
|
4272
|
+
</div>`,
|
|
4273
|
+
format: "A4",
|
|
4274
|
+
// Letter,Legal,Tabloid,Ledger,A0,A1,A2,A3,A4,A5,A6
|
|
4275
|
+
headerTemplate: `
|
|
4276
|
+
<div
|
|
4277
|
+
style="
|
|
4278
|
+
color: lightgray; border-bottom: solid lightgray 1px; font-size: 10px;
|
|
4279
|
+
padding-bottom: 5px; text-align: center; width: 100%;
|
|
4280
|
+
"
|
|
4281
|
+
>
|
|
4282
|
+
<p class="url" />
|
|
4283
|
+
</div>`,
|
|
4284
|
+
// height: `${height}px`,
|
|
4285
|
+
landscape: false,
|
|
4286
|
+
margin: {
|
|
4287
|
+
bottom: 70,
|
|
4288
|
+
// minimum required for footer msg to display
|
|
4289
|
+
left: 20,
|
|
4290
|
+
right: 20,
|
|
4291
|
+
top: 70
|
|
4292
|
+
// minimum required for header msg to display
|
|
4293
|
+
},
|
|
4294
|
+
omitBackground: true,
|
|
4295
|
+
pageRanges: "",
|
|
4296
|
+
// All
|
|
4297
|
+
// path: "./test.pdf" // Path to save the file
|
|
4298
|
+
preferCSSPageSize: false,
|
|
4299
|
+
// Give any CSS @page size declared in the page priority over what is declared in the width or height or format option.
|
|
4300
|
+
printBackground: true,
|
|
4301
|
+
scale: 1,
|
|
4302
|
+
// From 0.1 to 2
|
|
4303
|
+
tagged: false,
|
|
4304
|
+
// Generate tagged (accessible) PDF.
|
|
4305
|
+
timeout: 0
|
|
4306
|
+
// width: `${width}px`,
|
|
4307
|
+
});
|
|
4308
|
+
await browser.close();
|
|
4309
|
+
return pdf.toString("base64");
|
|
4310
|
+
} catch (error) {
|
|
4311
|
+
console.error("Error generating PDF:", error);
|
|
4312
|
+
throw error;
|
|
4313
|
+
}
|
|
4314
|
+
}
|
|
4315
|
+
function endpointDownloadReport() {
|
|
4316
|
+
return endpoint("POST", "pdf", async (req, res) => {
|
|
4317
|
+
const { slugs, path: path2 } = req.body;
|
|
4318
|
+
if (!slugs || !path2) {
|
|
4319
|
+
throw new BackendError(400, "Missing path parameter in request.");
|
|
4320
|
+
} else {
|
|
4321
|
+
const url = new URL(path2, req.headers["origin"]);
|
|
4322
|
+
const pdf = await generatePDF(url);
|
|
4323
|
+
return {
|
|
4324
|
+
ok: true,
|
|
4325
|
+
status: 200,
|
|
4326
|
+
data: pdf
|
|
4327
|
+
};
|
|
4328
|
+
}
|
|
4329
|
+
});
|
|
4330
|
+
}
|
|
4331
|
+
var minimal_args = [
|
|
4332
|
+
"--autoplay-policy=user-gesture-required",
|
|
4333
|
+
"--disable-background-networking",
|
|
4334
|
+
"--disable-background-timer-throttling",
|
|
4335
|
+
"--disable-backgrounding-occluded-windows",
|
|
4336
|
+
"--disable-breakpad",
|
|
4337
|
+
"--disable-client-side-phishing-detection",
|
|
4338
|
+
"--disable-component-update",
|
|
4339
|
+
"--disable-default-apps",
|
|
4340
|
+
"--disable-dev-shm-usage",
|
|
4341
|
+
"--disable-domain-reliability",
|
|
4342
|
+
"--disable-extensions",
|
|
4343
|
+
"--disable-features=AudioServiceOutOfProcess",
|
|
4344
|
+
"--disable-hang-monitor",
|
|
4345
|
+
"--disable-ipc-flooding-protection",
|
|
4346
|
+
"--disable-notifications",
|
|
4347
|
+
"--disable-offer-store-unmasked-wallet-cards",
|
|
4348
|
+
"--disable-popup-blocking",
|
|
4349
|
+
"--disable-print-preview",
|
|
4350
|
+
"--disable-prompt-on-repost",
|
|
4351
|
+
"--disable-renderer-backgrounding",
|
|
4352
|
+
"--disable-setuid-sandbox",
|
|
4353
|
+
"--disable-speech-api",
|
|
4354
|
+
"--disable-sync",
|
|
4355
|
+
"--hide-scrollbars",
|
|
4356
|
+
"--ignore-gpu-blacklist",
|
|
4357
|
+
"--metrics-recording-only",
|
|
4358
|
+
"--mute-audio",
|
|
4359
|
+
"--no-default-browser-check",
|
|
4360
|
+
"--no-first-run",
|
|
4361
|
+
"--no-pings",
|
|
4362
|
+
"--no-zygote",
|
|
4363
|
+
"--password-store=basic",
|
|
4364
|
+
"--use-gl=swiftshader",
|
|
4365
|
+
"--use-mock-keychain",
|
|
4366
|
+
"--start-fullscreen",
|
|
4367
|
+
"--disable-dev-shm-usage",
|
|
4368
|
+
"--no-sandbox"
|
|
4369
|
+
];
|
|
4370
|
+
var blocked_domains = [
|
|
4371
|
+
"google-analytics.com",
|
|
4372
|
+
"googletagmanager.com"
|
|
4373
|
+
];
|
|
4374
|
+
async function generateImage(path2, section, format = "png", transparent = false) {
|
|
4375
|
+
const width = 1366;
|
|
4376
|
+
const height = 768;
|
|
4377
|
+
const maxTimeoutPerPage = 0;
|
|
4378
|
+
try {
|
|
4379
|
+
const browser = await puppeteer.launch({
|
|
4380
|
+
args: [
|
|
4381
|
+
`--window-size=${width},${height}`,
|
|
4382
|
+
...minimal_args
|
|
4383
|
+
],
|
|
4384
|
+
defaultViewport: { width, height, deviceScaleFactor: 2 },
|
|
4385
|
+
headless: "new",
|
|
4386
|
+
ignoreHTTPSErrors: true
|
|
4387
|
+
});
|
|
4388
|
+
const url = new URL(path2);
|
|
4389
|
+
const searchParams = new URLSearchParams(url.search);
|
|
4390
|
+
url.search = searchParams.toString();
|
|
4391
|
+
const page = await browser.newPage();
|
|
4392
|
+
await page.addStyleTag({ content: ".bespoke-Section-container{ padding: 1rem !important }" });
|
|
4393
|
+
await page.setRequestInterception(true);
|
|
4394
|
+
page.on("request", (request) => {
|
|
4395
|
+
const url2 = request.url();
|
|
4396
|
+
if (blocked_domains.some((domain) => url2.includes(domain))) {
|
|
4397
|
+
request.abort();
|
|
4398
|
+
} else {
|
|
4399
|
+
request.continue();
|
|
4400
|
+
}
|
|
4401
|
+
});
|
|
4402
|
+
await page.goto(url.toString(), { waitUntil: "networkidle0", timeout: maxTimeoutPerPage });
|
|
4403
|
+
await page.waitForSelector("#bespoke-report", { timeout: maxTimeoutPerPage });
|
|
4404
|
+
if (transparent) {
|
|
4405
|
+
await page.$eval(`.bespoke-Section-${section}`, (el) => el["style"].background = "transparent");
|
|
4406
|
+
await page.evaluate(() => document.body.style.background = "transparent");
|
|
4407
|
+
}
|
|
4408
|
+
if (format === "svg") {
|
|
4409
|
+
const element = await page.$(".d3plus-viz");
|
|
4410
|
+
const svg = await element?.evaluate((el) => el.outerHTML);
|
|
4411
|
+
return svg?.replace(/ {4}|[\t\n\r]/gm, "");
|
|
4412
|
+
} else {
|
|
4413
|
+
const element = await page.$(".bespoke-Section-container");
|
|
4414
|
+
const screenshot = await element?.screenshot({
|
|
4415
|
+
type: "png",
|
|
4416
|
+
omitBackground: true
|
|
4417
|
+
});
|
|
4418
|
+
await browser.close();
|
|
4419
|
+
return screenshot;
|
|
4420
|
+
}
|
|
4421
|
+
} catch (error) {
|
|
4422
|
+
console.error("Error generating Image:", error);
|
|
4423
|
+
throw error;
|
|
4424
|
+
}
|
|
4425
|
+
}
|
|
4426
|
+
function endpointExportImage() {
|
|
4427
|
+
return endpoint("POST", "img", async (req, res) => {
|
|
4428
|
+
const { slugs, path: path2, section, viz, query: selectors, locale, prefixLocale, transparent = false, svg = false } = req.body;
|
|
4429
|
+
if (!slugs || !path2 || !section) {
|
|
4430
|
+
throw new BackendError(400, "Missing parameter in request.");
|
|
4431
|
+
} else {
|
|
4432
|
+
try {
|
|
4433
|
+
let path3 = prefixLocale ? `/${locale}/` : "/";
|
|
4434
|
+
path3 += `embed/${JSON.parse(slugs).join("/")}`;
|
|
4435
|
+
const url = new URL(path3, req.headers["origin"]);
|
|
4436
|
+
const query = new URLSearchParams(Object.entries(selectors || {}));
|
|
4437
|
+
query.set("section", section);
|
|
4438
|
+
if (viz) {
|
|
4439
|
+
query.set("viz", viz);
|
|
4440
|
+
}
|
|
4441
|
+
url.search = query.toString();
|
|
4442
|
+
const format = yn2(svg) ? "svg" : "png";
|
|
4443
|
+
const img = await generateImage(url, section, format, yn2(transparent));
|
|
4444
|
+
return {
|
|
4445
|
+
ok: true,
|
|
4446
|
+
status: 200,
|
|
4447
|
+
data: img
|
|
4448
|
+
};
|
|
4449
|
+
} catch (e) {
|
|
4450
|
+
console.log(e);
|
|
4451
|
+
return {
|
|
4452
|
+
ok: false,
|
|
4453
|
+
status: 500
|
|
4454
|
+
};
|
|
4455
|
+
}
|
|
4456
|
+
}
|
|
4457
|
+
});
|
|
4458
|
+
}
|
|
4222
4459
|
|
|
4223
4460
|
// api/endpoints/icon/listIcons.ts
|
|
4224
4461
|
function endpointListIconsFactory(operations, provider) {
|
|
@@ -4308,7 +4545,8 @@ function getEndpointMap(db) {
|
|
|
4308
4545
|
endpointRevalidateReportFactory(api),
|
|
4309
4546
|
endpointRevalidateUrlFactory(),
|
|
4310
4547
|
endpointReadPrivateBlocksFactory(api),
|
|
4311
|
-
|
|
4548
|
+
endpointDownloadReport(),
|
|
4549
|
+
endpointExportImage(),
|
|
4312
4550
|
endpointListIconsFactory(api, "tabler"),
|
|
4313
4551
|
endpointReadIconFactory(api, "tabler"),
|
|
4314
4552
|
endpointReportVariables()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datawheel/bespoke",
|
|
3
|
-
"version": "0.6.0-rc.
|
|
3
|
+
"version": "0.6.0-rc.2",
|
|
4
4
|
"description": "Content management system for creating automated data reports",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -51,7 +51,6 @@
|
|
|
51
51
|
"@mantine/notifications": "^6.0.19",
|
|
52
52
|
"@mantine/prism": "^6.0.19",
|
|
53
53
|
"@monaco-editor/react": "^4.4.5",
|
|
54
|
-
"@newswire/frames": "^1.0.0",
|
|
55
54
|
"@reduxjs/toolkit": "^1.8.4",
|
|
56
55
|
"@tabler/icons-react": "^2.15.0",
|
|
57
56
|
"@tiptap/extension-hard-break": "^2.2.2",
|
|
@@ -105,6 +104,7 @@
|
|
|
105
104
|
"normalizr": "^3.6.2",
|
|
106
105
|
"pg": "^8.7.3",
|
|
107
106
|
"pretty-format": "^28.1.1",
|
|
107
|
+
"puppeteer": "^21.5.2",
|
|
108
108
|
"react": "^18.2.0",
|
|
109
109
|
"react-clipboard.js": "^2.0.16",
|
|
110
110
|
"react-dom": "^18.2.0",
|