@datawheel/bespoke 0.6.0-rc.15 → 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 -326
- package/dist/server.js +239 -1
- package/package.json +2 -3
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,159 +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 saveAs2(uri, filename) {
|
|
16132
|
-
const link = document.createElement("a");
|
|
16133
|
-
if (typeof link.download === "string") {
|
|
16134
|
-
link.href = uri;
|
|
16135
|
-
link.download = filename;
|
|
16136
|
-
document.body.appendChild(link);
|
|
16137
|
-
link.click();
|
|
16138
|
-
document.body.removeChild(link);
|
|
16139
|
-
} else {
|
|
16140
|
-
window.open(uri);
|
|
16141
|
-
}
|
|
16142
|
-
}
|
|
16143
|
-
function exportSVG(svg) {
|
|
16144
|
-
const serializer = new XMLSerializer();
|
|
16145
|
-
let source = serializer.serializeToString(svg);
|
|
16146
|
-
if (!source.match(/^<svg[^>]+xmlns="http\:\/\/www\.w3\.org\/2000\/svg"/)) {
|
|
16147
|
-
source = source.replace(/^<svg/, '<svg xmlns="http://www.w3.org/2000/svg"');
|
|
16148
|
-
}
|
|
16149
|
-
if (!source.match(/^<svg[^>]+"http\:\/\/www\.w3\.org\/1999\/xlink"/)) {
|
|
16150
|
-
source = source.replace(/^<svg/, '<svg xmlns:xlink="http://www.w3.org/1999/xlink"');
|
|
16151
|
-
}
|
|
16152
|
-
source = '<?xml version="1.0" standalone="no"?>\r\n' + source;
|
|
16153
|
-
const url = "data:image/svg+xml;charset=utf-8," + encodeURIComponent(source);
|
|
16154
|
-
const a2 = document.createElement("a");
|
|
16155
|
-
const e = new MouseEvent("click");
|
|
16156
|
-
a2.download = "image.svg";
|
|
16157
|
-
a2.href = url;
|
|
16158
|
-
a2.dispatchEvent(e);
|
|
16159
|
-
}
|
|
16160
15954
|
function ImageTab(props) {
|
|
16161
15955
|
const { section } = props;
|
|
16162
15956
|
const blocksIds = section.blocks || [];
|
|
16163
|
-
const
|
|
15957
|
+
const { asPath, query, locale } = useRouter();
|
|
15958
|
+
const prefixLocale = useLocalePrefix();
|
|
15959
|
+
const { pathSegment } = useBespoke();
|
|
16164
15960
|
const vizAvailable = useAppSelector((state) => blocksIds.map((blockId) => state.records.entities.block[blockId]).filter((block) => block.type === "visualization" && state.variables.status[block.id].allowed));
|
|
16165
15961
|
const [imageContext, setImageContext] = useState(contextOptions.section);
|
|
16166
15962
|
const [imageFormat, setImageFormat] = useState(formatOptions2.png);
|
|
@@ -16170,13 +15966,62 @@ function ImageTab(props) {
|
|
|
16170
15966
|
const [transparentBackground, setTransparentBackground] = useState(false);
|
|
16171
15967
|
const [loadingPreviews, setLoadingPreviews] = useState(false);
|
|
16172
15968
|
const optionsTranslations = useBespokeTranslations("options");
|
|
16173
|
-
const translations = { ...
|
|
16174
|
-
const search = Object.fromEntries(new URLSearchParams(location.search));
|
|
16175
|
-
const selectors = Object.fromEntries(
|
|
16176
|
-
Object.entries(search).filter(([key]) => key.match(/selector\d+id/))
|
|
16177
|
-
);
|
|
15969
|
+
const translations = { ...DEFAULT_TRANSLATIONS3, ...optionsTranslations["image_tab"] };
|
|
16178
15970
|
const sectionId = slugify_default(section?.settings?.name) || `section-${section.id}`;
|
|
16179
|
-
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
|
+
};
|
|
16180
16025
|
const generatePreviews = async () => {
|
|
16181
16026
|
const { toCanvas } = await import('html-to-image');
|
|
16182
16027
|
Promise.all(
|
|
@@ -16202,33 +16047,6 @@ function ImageTab(props) {
|
|
|
16202
16047
|
const onSelectViz = (vizId) => () => {
|
|
16203
16048
|
setVizSelected(vizId);
|
|
16204
16049
|
};
|
|
16205
|
-
const onDownloadIframe = () => {
|
|
16206
|
-
setImageProcessing(true);
|
|
16207
|
-
const container = document.getElementById("iframe-preview");
|
|
16208
|
-
framer.current = new Framer(container, { src: embedPath });
|
|
16209
|
-
};
|
|
16210
|
-
useEffect(() => {
|
|
16211
|
-
const onIframeLoad = async (e) => {
|
|
16212
|
-
if (e.data === "iframe-loaded") {
|
|
16213
|
-
if (imageFormat === formatOptions2.png) {
|
|
16214
|
-
const iframe = document.querySelector("#iframe-preview > iframe");
|
|
16215
|
-
const doc = iframe.contentDocument;
|
|
16216
|
-
const node = doc?.querySelector(".bespoke-Section-root");
|
|
16217
|
-
html2canvas(node).then(function(canvas) {
|
|
16218
|
-
saveAs2(canvas.toDataURL(), "file-name.png");
|
|
16219
|
-
});
|
|
16220
|
-
} else {
|
|
16221
|
-
const iframe = document.querySelector("#iframe-preview > iframe");
|
|
16222
|
-
const doc = iframe.contentDocument;
|
|
16223
|
-
const svg = doc?.querySelector("svg.d3plus-viz");
|
|
16224
|
-
if (svg)
|
|
16225
|
-
exportSVG(svg);
|
|
16226
|
-
}
|
|
16227
|
-
}
|
|
16228
|
-
};
|
|
16229
|
-
window.addEventListener("message", onIframeLoad);
|
|
16230
|
-
return () => window.removeEventListener("message", onIframeLoad);
|
|
16231
|
-
}, [transparentBackground]);
|
|
16232
16050
|
return /* @__PURE__ */ jsxs(Stack, { className: "cms-section-options-image", children: [
|
|
16233
16051
|
/* @__PURE__ */ jsx(Space, { h: "xs" }),
|
|
16234
16052
|
vizAvailable.length > 0 ? /* @__PURE__ */ jsx(Input.Wrapper, { label: `${translations["choose_area"]}:`, children: /* @__PURE__ */ jsx(
|
|
@@ -16275,7 +16093,28 @@ function ImageTab(props) {
|
|
|
16275
16093
|
]
|
|
16276
16094
|
}
|
|
16277
16095
|
),
|
|
16278
|
-
/* @__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
|
+
] }) }),
|
|
16279
16118
|
imageFormat !== formatOptions2.svg && /* @__PURE__ */ jsx(
|
|
16280
16119
|
Checkbox,
|
|
16281
16120
|
{
|
|
@@ -16290,25 +16129,26 @@ function ImageTab(props) {
|
|
|
16290
16129
|
{
|
|
16291
16130
|
leftIcon: /* @__PURE__ */ jsx(IconDownload, { size: 16 }),
|
|
16292
16131
|
loading: imageProcessing,
|
|
16293
|
-
onClick:
|
|
16132
|
+
onClick: imageContext === contextOptions.section ? onSaveClickSection : onSaveClickViz,
|
|
16294
16133
|
fullWidth: true,
|
|
16295
16134
|
children: /* @__PURE__ */ jsx("span", { children: imageProcessing ? translations["processing"] : translations["download"].replace("{imageFormat}", imageFormat) })
|
|
16296
16135
|
}
|
|
16297
16136
|
)
|
|
16298
16137
|
] });
|
|
16299
16138
|
}
|
|
16300
|
-
var contextOptions, formatOptions2, VizPreview2,
|
|
16139
|
+
var contextOptions, formatOptions2, VizPreview2, DEFAULT_TRANSLATIONS3, getSectionNode, getVizNode;
|
|
16301
16140
|
var init_ImageTab = __esm({
|
|
16302
16141
|
"components/options/tabs/ImageTab.tsx"() {
|
|
16303
16142
|
init_esm_shims();
|
|
16304
16143
|
init_slugify();
|
|
16305
16144
|
init_store2();
|
|
16306
16145
|
init_TranslationsProvider();
|
|
16146
|
+
init_ResourceProvider();
|
|
16147
|
+
init_useLocalePrefix();
|
|
16307
16148
|
init_d3plusPropify();
|
|
16308
16149
|
init_varSwapRecursive();
|
|
16309
16150
|
init_getBlockContent();
|
|
16310
16151
|
init_hooks();
|
|
16311
|
-
init_ShareTab();
|
|
16312
16152
|
contextOptions = {
|
|
16313
16153
|
viz: "viz",
|
|
16314
16154
|
section: "section"
|
|
@@ -16362,7 +16202,7 @@ var init_ImageTab = __esm({
|
|
|
16362
16202
|
)
|
|
16363
16203
|
] });
|
|
16364
16204
|
};
|
|
16365
|
-
|
|
16205
|
+
DEFAULT_TRANSLATIONS3 = {
|
|
16366
16206
|
"wrong_node": "Wrong node in export",
|
|
16367
16207
|
"choose_area": "Choose image area",
|
|
16368
16208
|
"choose_viz": "Choose visualization",
|
|
@@ -16375,6 +16215,7 @@ var init_ImageTab = __esm({
|
|
|
16375
16215
|
"source": "Source: {src}"
|
|
16376
16216
|
};
|
|
16377
16217
|
getSectionNode = (sectionId) => {
|
|
16218
|
+
console.log({ sectionId });
|
|
16378
16219
|
const sectionNode = document.getElementById(sectionId);
|
|
16379
16220
|
return select(sectionNode).select(".bespoke-section-content").node();
|
|
16380
16221
|
};
|
|
@@ -16384,6 +16225,142 @@ var init_ImageTab = __esm({
|
|
|
16384
16225
|
};
|
|
16385
16226
|
}
|
|
16386
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
|
+
});
|
|
16387
16364
|
function OptionsModal(props) {
|
|
16388
16365
|
const {
|
|
16389
16366
|
section,
|
|
@@ -17513,8 +17490,8 @@ var init_Section = __esm({
|
|
|
17513
17490
|
const slugs = Array.isArray(querySlugs) ? querySlugs?.join("/") : [];
|
|
17514
17491
|
const prefixLocale = useLocalePrefix();
|
|
17515
17492
|
let path = prefixLocale ? `/${locale}` : "";
|
|
17516
|
-
path +=
|
|
17517
|
-
const link = new URL(path, window
|
|
17493
|
+
path += `/${profilePrefix}/${slugs}/`;
|
|
17494
|
+
const link = new URL(path, window?.location.href).href;
|
|
17518
17495
|
return /* @__PURE__ */ jsxs(Group, { position: "apart", noWrap: true, sx: { flexGrow: 1 }, children: [
|
|
17519
17496
|
/* @__PURE__ */ jsx(Anchor, { href: link, target: "_blank", children: link }),
|
|
17520
17497
|
/* @__PURE__ */ jsx(
|
|
@@ -17717,9 +17694,6 @@ var LoginButton_default = BespokeLoginBtn;
|
|
|
17717
17694
|
init_esm_shims();
|
|
17718
17695
|
var UserProvider_default = UserProvider;
|
|
17719
17696
|
|
|
17720
|
-
// frontend/index.ts
|
|
17721
|
-
init_hooks2();
|
|
17722
|
-
|
|
17723
17697
|
// frontend/components/auth/withPageRoleAuthRequired.tsx
|
|
17724
17698
|
init_esm_shims();
|
|
17725
17699
|
function withPageRoleAuthRequired(params) {
|
|
@@ -25844,4 +25818,4 @@ function BespokeRenderer({
|
|
|
25844
25818
|
] }) });
|
|
25845
25819
|
}
|
|
25846
25820
|
|
|
25847
|
-
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",
|
|
@@ -94,7 +93,6 @@
|
|
|
94
93
|
"formidable": "^2.1.1",
|
|
95
94
|
"html-react-parser": "^3.0.15",
|
|
96
95
|
"html-to-image": "^1.10.8",
|
|
97
|
-
"html2canvas": "^1.4.1",
|
|
98
96
|
"image-type": "^5.2.0",
|
|
99
97
|
"jszip": "^3.10.1",
|
|
100
98
|
"lunr": "^2.3.9",
|
|
@@ -106,6 +104,7 @@
|
|
|
106
104
|
"normalizr": "^3.6.2",
|
|
107
105
|
"pg": "^8.7.3",
|
|
108
106
|
"pretty-format": "^28.1.1",
|
|
107
|
+
"puppeteer": "^21.5.2",
|
|
109
108
|
"react": "^18.2.0",
|
|
110
109
|
"react-clipboard.js": "^2.0.16",
|
|
111
110
|
"react-dom": "^18.2.0",
|