diff --git a/packages/api-page-builder-import-export/src/export/process/exporters/BlockExporter.ts b/packages/api-page-builder-import-export/src/export/process/exporters/BlockExporter.ts index 1ea83f10187..0b670f3a9b0 100644 --- a/packages/api-page-builder-import-export/src/export/process/exporters/BlockExporter.ts +++ b/packages/api-page-builder-import-export/src/export/process/exporters/BlockExporter.ts @@ -4,7 +4,7 @@ import Zipper from "~/export/zipper"; import { extractFilesFromData } from "~/export/utils"; export interface ExportedBlockData { - block: Pick; + block: Pick; category: BlockCategory; files: File[]; } @@ -26,21 +26,12 @@ export class BlockExporter { const [filesData] = await this.fileManager.listFiles({ where: { id_in: fileIds } }); imageFilesData.push(...filesData); } - // Add block preview image file data - if (block.preview.id) { - const previewImage = await this.getPreviewImageFile(block); - if (previewImage) { - imageFilesData.push(previewImage); - block.preview.id = previewImage.id; - } - } // Extract the block data in a json file and upload it to S3 const blockData = { block: { name: block.name, - content: block.content, - preview: block.preview + content: block.content }, category: { name: blockCategory.name, @@ -63,16 +54,4 @@ export class BlockExporter { return zipper.process(); } - - private async getPreviewImageFile(block: PageBlock): Promise { - // For BC, we need to check 2 IDs: the preview `id` and the `id` from the file URL. - const idFromSrc = block.preview.src?.split("/files/")[1].split("/")[0]; - const possibleIds = [block.preview.id, idFromSrc].filter(Boolean); - - const [files] = await this.fileManager.listFiles({ - where: { id_in: possibleIds, meta: { private: true } } - }); - - return files[0]; - } } diff --git a/packages/api-page-builder-import-export/src/export/utils.ts b/packages/api-page-builder-import-export/src/export/utils.ts index bd45b59ece2..1708c56f9cd 100644 --- a/packages/api-page-builder-import-export/src/export/utils.ts +++ b/packages/api-page-builder-import-export/src/export/utils.ts @@ -1,4 +1,165 @@ -import { File } from "@webiny/api-file-manager/types"; +import S3 from "aws-sdk/clients/s3"; +import { BlockCategory, Page, PageBlock, PageTemplate } from "@webiny/api-page-builder/types"; +import { FileManagerContext, File } from "@webiny/api-file-manager/types"; +import get from "lodash/get"; +import Zipper from "./zipper"; + +export const EXPORT_PAGES_FOLDER_KEY = "WEBINY_PB_EXPORT_PAGES"; +export const EXPORT_BLOCKS_FOLDER_KEY = "WEBINY_PB_EXPORT_BLOCK"; +export const EXPORT_TEMPLATES_FOLDER_KEY = "WEBINY_PB_EXPORT_TEMPLATE"; +export const EXPORT_FORMS_FOLDER_KEY = "WEBINY_FB_EXPORT_FORM"; + +export interface ExportedPageData { + page: Pick; + files: File[]; +} + +export async function exportPage( + page: Page, + exportPagesDataKey: string, + fileManager: FileManagerContext["fileManager"] +): Promise { + // Extract all files + const files = extractFilesFromData(page.content || {}); + // Extract images from page settings + const pageSettingsImages = [ + get(page, "settings.general.image") as unknown as File, + get(page, "settings.social.image") as unknown as File + ].filter(image => image && image.src); + + const fileIds = [...files, ...pageSettingsImages].map(imageFile => imageFile.id); + // Get file data for all images + const imageFilesData = []; + if (fileIds.length > 0) { + const [filesData] = await fileManager.listFiles({ where: { id_in: fileIds } }); + imageFilesData.push(...filesData); + } + + // Extract the page data in a json file and upload it to S3 + const pageData = { + page: { + content: page.content, + title: page.title, + path: page.path, + version: page.version, + status: page.status, + settings: page.settings + }, + files: imageFilesData + }; + const pageDataBuffer = Buffer.from(JSON.stringify(pageData)); + + const zipper = new Zipper({ + exportInfo: { + files: imageFilesData, + name: page.title, + dataBuffer: pageDataBuffer + }, + archiveFileKey: exportPagesDataKey + }); + + return zipper.process(); +} + +export interface ExportedBlockData { + block: Pick; + category: BlockCategory; + files: File[]; +} + +export async function exportBlock( + block: PageBlock, + blockCategory: BlockCategory, + exportBlocksDataKey: string, + fileManager: FileManagerContext["fileManager"] +): Promise { + // Extract all files + const files = extractFilesFromData(block.content || {}); + const fileIds = files.map(imageFile => imageFile.id); + // Get file data for all images + const imageFilesData = []; + if (fileIds.length > 0) { + const [filesData] = await fileManager.listFiles({ where: { id_in: fileIds } }); + imageFilesData.push(...filesData); + } + + // Extract the block data in a json file and upload it to S3 + const blockData = { + block: { + name: block.name, + content: block.content + }, + category: { + name: blockCategory.name, + slug: blockCategory.slug, + icon: blockCategory.icon, + description: blockCategory.description + }, + files: imageFilesData + }; + const blockDataBuffer = Buffer.from(JSON.stringify(blockData)); + + const zipper = new Zipper({ + exportInfo: { + files: imageFilesData, + name: block.name, + dataBuffer: blockDataBuffer + }, + archiveFileKey: exportBlocksDataKey + }); + + return zipper.process(); +} + +export interface ExportedTemplateData { + template: Pick< + PageTemplate, + "title" | "slug" | "tags" | "description" | "content" | "layout" | "pageCategory" + >; + files: File[]; +} + +export async function exportTemplate( + template: PageTemplate, + exportTemplatesDataKey: string, + fileManager: FileManagerContext["fileManager"] +): Promise { + // Extract all files + const files = extractFilesFromData(template.content || {}); + const fileIds = files.map(imageFile => imageFile.id); + // Get file data for all images + const imageFilesData = []; + if (fileIds.length > 0) { + const [filesData] = await fileManager.listFiles({ where: { id_in: fileIds } }); + imageFilesData.push(...filesData); + } + + // Extract the template data in a json file and upload it to S3 + const templateData = { + template: { + title: template.title, + slug: template.slug, + tags: template.tags, + description: template.description, + content: template.content, + layout: template.layout, + pageCategory: template.pageCategory + }, + files: imageFilesData + }; + const templateDataBuffer = Buffer.from(JSON.stringify(templateData)); + + const zipper = new Zipper({ + exportInfo: { + files: imageFilesData, + name: template.title, + dataBuffer: templateDataBuffer + }, + archiveFileKey: exportTemplatesDataKey + }); + + return zipper.process(); +} export function extractFilesFromData(data: Record, files: File[] = []) { if (!data || typeof data !== "object") { diff --git a/packages/api-page-builder-import-export/src/import/process/blocks/blocksHandler.ts b/packages/api-page-builder-import-export/src/import/process/blocks/blocksHandler.ts index 30e5e0ce625..d3588994f30 100644 --- a/packages/api-page-builder-import-export/src/import/process/blocks/blocksHandler.ts +++ b/packages/api-page-builder-import-export/src/import/process/blocks/blocksHandler.ts @@ -76,8 +76,7 @@ export const blocksHandler = async ( const pbBlock = await context.pageBuilder.createPageBlock({ name: block.name, blockCategory: block.blockCategory, - content: block.content, - preview: block.preview + content: block.content }); // Update task record in DB diff --git a/packages/api-page-builder-import-export/src/import/process/blocks/importBlock.ts b/packages/api-page-builder-import-export/src/import/process/blocks/importBlock.ts index c83ce2cea7c..c7d976e9136 100644 --- a/packages/api-page-builder-import-export/src/import/process/blocks/importBlock.ts +++ b/packages/api-page-builder-import-export/src/import/process/blocks/importBlock.ts @@ -2,9 +2,8 @@ import path from "path"; import dotProp from "dot-prop-immutable"; import loadJson from "load-json-file"; import { ensureDirSync, createWriteStream } from "fs-extra"; -import { FileInput } from "@webiny/api-file-manager/types"; import { PbImportExportContext } from "~/graphql/types"; -import { File as ImageFile, FileUploadsData } from "~/types"; +import { FileUploadsData } from "~/types"; import { PageBlock } from "@webiny/api-page-builder/types"; import { s3Stream } from "~/export/s3Stream"; import { uploadAssets } from "~/import/utils/uploadAssets"; @@ -21,17 +20,11 @@ interface ImportBlockParams { fileUploadsData: FileUploadsData; } -interface UpdateBlockPreviewImage { - fileIdToNewFileMap: Map; - srcPrefix: string; - file: ImageFile; -} - export async function importBlock({ blockKey, context, fileUploadsData -}: ImportBlockParams): Promise> { +}: ImportBlockParams): Promise> { const log = console.log; // Making Directory for block in which we're going to extract the block data file. @@ -78,15 +71,6 @@ export async function importBlock({ fileIdToNewFileMap, srcPrefix }); - - block.preview = updateBlockPreviewImage({ - /** - * Casting as this is only a type error. - */ - file: (block.preview as ImageFile) || {}, - fileIdToNewFileMap, - srcPrefix - }); } let loadedCategory; @@ -123,22 +107,3 @@ export async function importBlock({ return { ...block, blockCategory: loadedCategory!.slug }; } - -function updateBlockPreviewImage(params: UpdateBlockPreviewImage): ImageFile { - const { file: blockPreview, fileIdToNewFileMap, srcPrefix } = params; - const newFile = fileIdToNewFileMap.get(blockPreview.id || ""); - - if (!newFile) { - console.log("Block preview file not found!"); - return blockPreview; - } - - const srcPrefixWithoutTrailingSlash = srcPrefix.endsWith("/") - ? srcPrefix.slice(0, -1) - : srcPrefix; - - blockPreview.id = newFile.id; - blockPreview.src = `${srcPrefixWithoutTrailingSlash}/${newFile.key}`; - - return blockPreview; -} diff --git a/packages/api-page-builder/__tests__/graphql/blockCategories.test.ts b/packages/api-page-builder/__tests__/graphql/blockCategories.test.ts index 5b8aee6facb..bb18313beb9 100644 --- a/packages/api-page-builder/__tests__/graphql/blockCategories.test.ts +++ b/packages/api-page-builder/__tests__/graphql/blockCategories.test.ts @@ -298,7 +298,6 @@ describe("Block Categories CRUD Test", () => { data: { name: `page-block-one-name`, blockCategory: `delete-block-cat`, - preview: { src: `https://test.com/page-block-one-name/src.jpg` }, content: { some: `page-block-one-content` } } }).then(([res]) => res.data.pageBuilder.createPageBlock.data); @@ -306,7 +305,6 @@ describe("Block Categories CRUD Test", () => { data: { name: `page-block-two-name`, blockCategory: `delete-block-cat`, - preview: { src: `https://test.com/page-block-two-name/src.jpg` }, content: { some: `page-block-two-content` } } }).then(([res]) => res.data.pageBuilder.createPageBlock.data); @@ -314,7 +312,6 @@ describe("Block Categories CRUD Test", () => { data: { name: `page-block-three-name`, blockCategory: `delete-block-cat`, - preview: { src: `https://test.com/page-block-three-name/src.jpg` }, content: { some: `page-block-three-content` } } }).then(([res]) => res.data.pageBuilder.createPageBlock.data); diff --git a/packages/api-page-builder/__tests__/graphql/graphql/pageBlocks.ts b/packages/api-page-builder/__tests__/graphql/graphql/pageBlocks.ts index 846cf3b2cdd..47fa0375751 100644 --- a/packages/api-page-builder/__tests__/graphql/graphql/pageBlocks.ts +++ b/packages/api-page-builder/__tests__/graphql/graphql/pageBlocks.ts @@ -2,7 +2,6 @@ export const DATA_FIELD = /* GraphQL */ ` { id blockCategory - preview name content createdOn diff --git a/packages/api-page-builder/__tests__/graphql/graphql/pageElements.ts b/packages/api-page-builder/__tests__/graphql/graphql/pageElements.ts index 889cec7205e..b9eda2e3475 100644 --- a/packages/api-page-builder/__tests__/graphql/graphql/pageElements.ts +++ b/packages/api-page-builder/__tests__/graphql/graphql/pageElements.ts @@ -2,7 +2,6 @@ export const DATA_FIELD = /* GraphQL */ ` { id category - preview name content type diff --git a/packages/api-page-builder/__tests__/graphql/lifecycleEvents.pageBlocks.test.ts b/packages/api-page-builder/__tests__/graphql/lifecycleEvents.pageBlocks.test.ts index e71609b163b..b6c36ef41ed 100644 --- a/packages/api-page-builder/__tests__/graphql/lifecycleEvents.pageBlocks.test.ts +++ b/packages/api-page-builder/__tests__/graphql/lifecycleEvents.pageBlocks.test.ts @@ -8,7 +8,6 @@ const blockCategory = "block-category-lifecycle-events"; const pageBlockData = { name: "Page Block Lifecycle Events", blockCategory, - preview: { src: "https://test.com/src.jpg" }, content: { some: "page-block-content" } }; diff --git a/packages/api-page-builder/__tests__/graphql/lifecycleEvents.pageElements.test.ts b/packages/api-page-builder/__tests__/graphql/lifecycleEvents.pageElements.test.ts index 0a73e8ebfdf..d785a8b64f5 100644 --- a/packages/api-page-builder/__tests__/graphql/lifecycleEvents.pageElements.test.ts +++ b/packages/api-page-builder/__tests__/graphql/lifecycleEvents.pageElements.test.ts @@ -12,13 +12,6 @@ const updatedContent = { ...content, updated: true }; -const preview = { - previewTest: true -}; -const updatedPreview = { - ...preview, - updated: true -}; describe("Page Element Lifecycle Events", () => { const handler = useGqlHandler({ @@ -37,8 +30,7 @@ describe("Page Element Lifecycle Events", () => { name, type, category, - content, - preview + content } }); expect(response).toMatchObject({ @@ -50,8 +42,7 @@ describe("Page Element Lifecycle Events", () => { name, type, category, - content, - preview + content }, error: null } @@ -73,8 +64,7 @@ describe("Page Element Lifecycle Events", () => { name, type, category, - content, - preview + content } }); @@ -88,8 +78,7 @@ describe("Page Element Lifecycle Events", () => { name, type, category, - content: updatedContent, - preview: updatedPreview + content: updatedContent } }); expect(response).toMatchObject({ @@ -101,8 +90,7 @@ describe("Page Element Lifecycle Events", () => { name, type, category, - content: updatedContent, - preview: updatedPreview + content: updatedContent }, error: null } @@ -124,8 +112,7 @@ describe("Page Element Lifecycle Events", () => { name, type, category, - content, - preview + content } }); @@ -146,8 +133,7 @@ describe("Page Element Lifecycle Events", () => { name, type, category, - content, - preview + content }, error: null } diff --git a/packages/api-page-builder/__tests__/graphql/pageBlocks.test.ts b/packages/api-page-builder/__tests__/graphql/pageBlocks.test.ts index e9e6eb7c262..a2dd600c591 100644 --- a/packages/api-page-builder/__tests__/graphql/pageBlocks.test.ts +++ b/packages/api-page-builder/__tests__/graphql/pageBlocks.test.ts @@ -34,7 +34,6 @@ describe("Page Blocks Test", () => { const data = { name: `${prefix}name`, blockCategory: `block-category`, - preview: { src: `https://test.com/${prefix}name/src.jpg` }, content: { some: `${prefix}content` } }; @@ -71,7 +70,6 @@ describe("Page Blocks Test", () => { const updateData = { name: `${prefix}name-UPDATED`, blockCategory: `block-category`, - preview: { src: `https://test.com/${prefix}name-UPDATED/src.jpg` }, content: { some: `${prefix}content-UPDATED` } }; @@ -110,10 +108,7 @@ describe("Page Blocks Test", () => { createdBy: defaultIdentity, createdOn: /^20/, id: ids[0], - name: "page-block-one-name-UPDATED", - preview: { - src: "https://test.com/page-block-one-name-UPDATED/src.jpg" - } + name: "page-block-one-name-UPDATED" }, { blockCategory: "block-category", @@ -123,10 +118,7 @@ describe("Page Blocks Test", () => { createdBy: defaultIdentity, createdOn: /^20/, id: ids[1], - name: "page-block-two-name-UPDATED", - preview: { - src: "https://test.com/page-block-two-name-UPDATED/src.jpg" - } + name: "page-block-two-name-UPDATED" }, { blockCategory: "block-category", @@ -136,10 +128,7 @@ describe("Page Blocks Test", () => { createdBy: defaultIdentity, createdOn: /^20/, id: ids[2], - name: "page-block-three-name-UPDATED", - preview: { - src: "https://test.com/page-block-three-name-UPDATED/src.jpg" - } + name: "page-block-three-name-UPDATED" } ], error: null @@ -184,7 +173,6 @@ describe("Page Blocks Test", () => { data: { name: "name", blockCategory: "", - preview: { src: "https://test.com/src.jpg" }, content: { some: "content" } } }); @@ -218,7 +206,6 @@ describe("Page Blocks Test", () => { data: { name: "name", blockCategory: "invalid-block-category", - preview: { src: "https://test.com/src.jpg" }, content: { some: "content" } } }); @@ -255,7 +242,6 @@ describe("Page Blocks Test", () => { data: { name: "name", blockCategory: "block-category", - preview: { src: "https://test.com/src.jpg" }, content: { some: "content" } } }); @@ -267,7 +253,6 @@ describe("Page Blocks Test", () => { data: { name: "name", blockCategory: "", - preview: { src: "https://test.com/src.jpg" }, content: { some: "content" } } }); @@ -279,7 +264,6 @@ describe("Page Blocks Test", () => { data: { name: "name", blockCategory: "block-category", - preview: { src: "https://test.com/src.jpg" }, content: { some: "content" } }, error: null @@ -293,7 +277,6 @@ describe("Page Blocks Test", () => { data: { name: "name", blockCategory: "invalid-block-category", - preview: { src: "https://test.com/src.jpg" }, content: { some: "content" } } }); @@ -356,7 +339,6 @@ describe("Page Blocks Test", () => { data: { name: `page-block-one-name`, blockCategory: `block-category-one`, - preview: { src: `https://test.com/page-block-one-name/src.jpg` }, content: { some: `page-block-one-content` } } }); @@ -366,7 +348,6 @@ describe("Page Blocks Test", () => { data: { name: `page-block-two-name`, blockCategory: `block-category-two`, - preview: { src: `https://test.com/page-block-two-name/src.jpg` }, content: { some: `page-block-two-content` } } }); @@ -391,10 +372,7 @@ describe("Page Blocks Test", () => { createdBy: defaultIdentity, createdOn: /^20/, id: pageBlockOneId, - name: "page-block-one-name", - preview: { - src: "https://test.com/page-block-one-name/src.jpg" - } + name: "page-block-one-name" } ], error: null @@ -422,10 +400,7 @@ describe("Page Blocks Test", () => { createdBy: defaultIdentity, createdOn: /^20/, id: pageBlockTwoId, - name: "page-block-two-name", - preview: { - src: "https://test.com/page-block-two-name/src.jpg" - } + name: "page-block-two-name" } ], error: null diff --git a/packages/api-page-builder/__tests__/graphql/pageBlocksSecurity.test.ts b/packages/api-page-builder/__tests__/graphql/pageBlocksSecurity.test.ts index 5f4a06aaf26..f32d810891d 100644 --- a/packages/api-page-builder/__tests__/graphql/pageBlocksSecurity.test.ts +++ b/packages/api-page-builder/__tests__/graphql/pageBlocksSecurity.test.ts @@ -5,13 +5,11 @@ import { SecurityIdentity, SecurityPermission } from "@webiny/api-security/types class Mock { public name: string; public blockCategory: string; - public preview: { src: string }; public content: { some: string }; constructor(prefix = "") { this.name = `${prefix}name`; this.blockCategory = `block-category`; - this.preview = { src: `https://test.com/${prefix}name/src.jpg` }; this.content = { some: `${prefix}content` }; } } @@ -167,9 +165,6 @@ describe("Page blocks Security Test", () => { createdOn: /^20/, name: "list-page-blocks-one-name", blockCategory: "block-category", - preview: { - src: "https://test.com/list-page-blocks-one-name/src.jpg" - }, content: { some: "list-page-blocks-one-content" } }, { @@ -177,9 +172,6 @@ describe("Page blocks Security Test", () => { createdOn: /^20/, name: "list-page-blocks-two-name", blockCategory: "block-category", - preview: { - src: "https://test.com/list-page-blocks-two-name/src.jpg" - }, content: { some: "list-page-blocks-two-content" } }, { @@ -187,9 +179,6 @@ describe("Page blocks Security Test", () => { createdOn: /^20/, name: "list-page-blocks-three-name", blockCategory: "block-category", - preview: { - src: "https://test.com/list-page-blocks-three-name/src.jpg" - }, content: { some: "list-page-blocks-three-content" } }, { @@ -197,9 +186,6 @@ describe("Page blocks Security Test", () => { createdOn: /^20/, name: "list-page-blocks-four-name", blockCategory: "block-category", - preview: { - src: "https://test.com/list-page-blocks-four-name/src.jpg" - }, content: { some: "list-page-blocks-four-content" } } ], @@ -226,9 +212,6 @@ describe("Page blocks Security Test", () => { createdOn: /^20/, name: "list-page-blocks-one-name", blockCategory: "block-category", - preview: { - src: "https://test.com/list-page-blocks-one-name/src.jpg" - }, content: { some: "list-page-blocks-one-content" } }, { @@ -236,9 +219,6 @@ describe("Page blocks Security Test", () => { createdOn: /^20/, name: "list-page-blocks-two-name", blockCategory: "block-category", - preview: { - src: "https://test.com/list-page-blocks-two-name/src.jpg" - }, content: { some: "list-page-blocks-two-content" } } ], @@ -264,9 +244,6 @@ describe("Page blocks Security Test", () => { createdOn: /^20/, name: "list-page-blocks-three-name", blockCategory: "block-category", - preview: { - src: "https://test.com/list-page-blocks-three-name/src.jpg" - }, content: { some: "list-page-blocks-three-content" } }, { @@ -274,9 +251,6 @@ describe("Page blocks Security Test", () => { createdOn: /^20/, name: "list-page-blocks-four-name", blockCategory: "block-category", - preview: { - src: "https://test.com/list-page-blocks-four-name/src.jpg" - }, content: { some: "list-page-blocks-four-content" } } ], diff --git a/packages/api-page-builder/__tests__/graphql/pageElements.test.ts b/packages/api-page-builder/__tests__/graphql/pageElements.test.ts index 70145062774..b18d6b2bb16 100644 --- a/packages/api-page-builder/__tests__/graphql/pageElements.test.ts +++ b/packages/api-page-builder/__tests__/graphql/pageElements.test.ts @@ -21,7 +21,6 @@ describe("PageElements Test", () => { name: `${prefix}name`, type: `element`, category: `${prefix}category`, - preview: { src: `https://test.com/${prefix}/src.jpg` }, content: { some: `${prefix}content` } }; @@ -58,7 +57,6 @@ describe("PageElements Test", () => { const updateData = { name: `${prefix}name-UPDATED`, category: `${prefix}category-UPDATED`, - preview: { src: `https://test.com/${prefix}/src-UPDATED.jpg` }, content: { some: `${prefix}content-UPDATED` } }; @@ -98,9 +96,6 @@ describe("PageElements Test", () => { createdOn: /^20/, id: ids[0], name: "pageElement-0-name-UPDATED", - preview: { - src: "https://test.com/pageElement-0-/src-UPDATED.jpg" - }, type: "element" }, { @@ -112,9 +107,6 @@ describe("PageElements Test", () => { createdOn: /^20/, id: ids[1], name: "pageElement-1-name-UPDATED", - preview: { - src: "https://test.com/pageElement-1-/src-UPDATED.jpg" - }, type: "element" }, { @@ -126,9 +118,6 @@ describe("PageElements Test", () => { createdOn: /^20/, id: ids[2], name: "pageElement-2-name-UPDATED", - preview: { - src: "https://test.com/pageElement-2-/src-UPDATED.jpg" - }, type: "element" } ], diff --git a/packages/api-page-builder/__tests__/graphql/pageTemplates.test.ts b/packages/api-page-builder/__tests__/graphql/pageTemplates.test.ts index 14e5c3547b9..99060fa077a 100644 --- a/packages/api-page-builder/__tests__/graphql/pageTemplates.test.ts +++ b/packages/api-page-builder/__tests__/graphql/pageTemplates.test.ts @@ -165,8 +165,7 @@ describe("Page Templates Test", () => { data: { name: "New block", blockCategory: "block-category", - content: simplePageBlockContent, - preview: {} + content: simplePageBlockContent } }).then(([response]) => response.data.pageBuilder.createPageBlock.data); diff --git a/packages/api-page-builder/__tests__/graphql/pages.test.ts b/packages/api-page-builder/__tests__/graphql/pages.test.ts index f32b589779b..327e116f7bb 100644 --- a/packages/api-page-builder/__tests__/graphql/pages.test.ts +++ b/packages/api-page-builder/__tests__/graphql/pages.test.ts @@ -413,7 +413,6 @@ describe("CRUD Test", () => { data: { name: "block-name", blockCategory: "block-category", - preview: { src: "https://test.com/src.jpg" }, content: { data: {}, elements: [], type: "block" } } }); @@ -425,7 +424,6 @@ describe("CRUD Test", () => { name: "element-name", type: "element", category: "element-category", - preview: { src: "https://test.com/element/src.jpg" }, content: { some: "element-content" } } }); @@ -499,7 +497,6 @@ describe("CRUD Test", () => { { id: pageElementData.id, category: "element-category", - preview: { src: "https://test.com/element/src.jpg" }, name: "element-name", content: { some: "element-content" }, type: "element", diff --git a/packages/api-page-builder/src/graphql/crud/pageBlocks/validation.ts b/packages/api-page-builder/src/graphql/crud/pageBlocks/validation.ts index 26e1053180c..61eda80ac6b 100644 --- a/packages/api-page-builder/src/graphql/crud/pageBlocks/validation.ts +++ b/packages/api-page-builder/src/graphql/crud/pageBlocks/validation.ts @@ -32,14 +32,7 @@ export const createPageBlocksCreateValidation = () => { .string() .min(1) .max(100) - .refine(refineValidation, refineValidationMessage), - preview: zod - .object({ - id: zod.string(), - src: zod.string() - }) - .partial() - .passthrough() + .refine(refineValidation, refineValidationMessage) }); }; @@ -58,15 +51,7 @@ export const createPageBlocksUpdateValidation = () => { return true; } return refineValidation(value); - }, refineValidationMessage), - preview: zod - .object({ - id: zod.string(), - src: zod.string() - }) - .partial() - .passthrough() - .optional() + }, refineValidationMessage) }) .partial(); }; diff --git a/packages/api-page-builder/src/graphql/crud/pageElements/validation.ts b/packages/api-page-builder/src/graphql/crud/pageElements/validation.ts index b67f3eed51f..b60694334b0 100644 --- a/packages/api-page-builder/src/graphql/crud/pageElements/validation.ts +++ b/packages/api-page-builder/src/graphql/crud/pageElements/validation.ts @@ -4,8 +4,7 @@ const baseValidation = zod.object({ name: zod.string().max(100), type: zod.enum(["element", "block"]), category: zod.string().max(100), - content: zod.object({}).partial().passthrough(), - preview: zod.object({}).partial().passthrough() + content: zod.object({}).partial().passthrough() }); export const createPageElementsCreateValidation = () => { diff --git a/packages/api-page-builder/src/graphql/graphql/pageBlocks.gql.ts b/packages/api-page-builder/src/graphql/graphql/pageBlocks.gql.ts index 854e177d0a8..3df9410649f 100644 --- a/packages/api-page-builder/src/graphql/graphql/pageBlocks.gql.ts +++ b/packages/api-page-builder/src/graphql/graphql/pageBlocks.gql.ts @@ -12,21 +12,18 @@ export const createPageBlockGraphQL = new GraphQLSchemaPlugin({ name: String blockCategory: String content: JSON - preview: JSON } input PbCreatePageBlockInput { name: String! blockCategory: String! content: JSON! - preview: JSON! } input PbUpdatePageBlockInput { name: String blockCategory: String content: JSON - preview: JSON } input PbListPageBlocksWhereInput { diff --git a/packages/api-page-builder/src/graphql/graphql/pageElements.gql.ts b/packages/api-page-builder/src/graphql/graphql/pageElements.gql.ts index 51235f490b1..27fc02ce552 100644 --- a/packages/api-page-builder/src/graphql/graphql/pageElements.gql.ts +++ b/packages/api-page-builder/src/graphql/graphql/pageElements.gql.ts @@ -15,7 +15,6 @@ export const createPageElementsGraphQL = (): GraphQLSchemaPlugin => { category: String type: String content: JSON - preview: JSON } input PbCreatePageElementInput { @@ -23,7 +22,6 @@ export const createPageElementsGraphQL = (): GraphQLSchemaPlugin => { type: String! category: String! content: JSON! - preview: JSON! } input PbUpdatePageElementInput { @@ -31,7 +29,6 @@ export const createPageElementsGraphQL = (): GraphQLSchemaPlugin => { type: String category: String content: JSON - preview: JSON } # Response types diff --git a/packages/api-page-builder/src/types.ts b/packages/api-page-builder/src/types.ts index 194b8c56439..c9cfcd6df25 100644 --- a/packages/api-page-builder/src/types.ts +++ b/packages/api-page-builder/src/types.ts @@ -795,7 +795,6 @@ export interface PageBlock { name: string; blockCategory: string; content: any; - preview: Partial; createdOn: string; createdBy: CreatedBy; tenant: string; diff --git a/packages/app-page-builder/package.json b/packages/app-page-builder/package.json index c9c50deb13f..d782de2e651 100644 --- a/packages/app-page-builder/package.json +++ b/packages/app-page-builder/package.json @@ -28,7 +28,6 @@ "@webiny/app": "0.0.0", "@webiny/app-aco": "0.0.0", "@webiny/app-admin": "0.0.0", - "@webiny/app-file-manager": "0.0.0", "@webiny/app-i18n": "0.0.0", "@webiny/app-page-builder-elements": "0.0.0", "@webiny/app-plugin-admin-welcome-screen": "0.0.0", @@ -50,7 +49,6 @@ "apollo-link": "^1.2.14", "apollo-utilities": "^1.3.4", "classnames": "^2.2.6", - "dataurl-to-blob": "^0.0.1", "dot-prop-immutable": "^2.1.0", "emotion": "^10.0.17", "graphql": "^15.7.2", diff --git a/packages/app-page-builder/src/admin/components/EditorPluginsLoader.tsx b/packages/app-page-builder/src/admin/components/EditorPluginsLoader.tsx index 32438367666..b1ce5282431 100644 --- a/packages/app-page-builder/src/admin/components/EditorPluginsLoader.tsx +++ b/packages/app-page-builder/src/admin/components/EditorPluginsLoader.tsx @@ -17,10 +17,12 @@ interface State { render?: boolean; editor?: boolean; } + interface EditorPluginsLoaderProps { location: History.Location; children: React.ReactNode; } + export const EditorPluginsLoader: React.FC = ({ children, location }) => { const [loaded, setLoaded] = useReducer( (state: State, newState: Partial) => ({ ...state, ...newState }), @@ -61,6 +63,17 @@ export const EditorPluginsLoader: React.FC = ({ childr setLoaded({ render: true }); } + // If we are on pages list route, import plugins required to render the page content. + if (location.pathname.startsWith("/page-builder/page-blocks") && !loaded.render) { + const renderPlugins = await loadRenderPlugins(); + + // "skipExisting" will ensure existing plugins (with the same name) are not overridden. + plugins.register(renderPlugins, { skipExisting: true }); + + globalState.render = true; + setLoaded({ render: true }); + } + // If we are on page templates list route, import plugins required to render the template content. if (location.pathname.startsWith("/page-builder/page-templates") && !loaded.render) { const renderPlugins = await loadRenderPlugins(); @@ -100,6 +113,13 @@ export const EditorPluginsLoader: React.FC = ({ childr if (location.pathname.startsWith("/page-builder/pages") && loaded.render) { return children as unknown as React.ReactElement; } + /** + * This condition is for the list of page blocks. + * Blocks can be selected at this point. + */ + if (location.pathname.startsWith("/page-builder/page-blocks") && loaded.render) { + return children as unknown as React.ReactElement; + } /** * This condition is for the list of page templates. * Page template can be selected at this point. diff --git a/packages/app-page-builder/src/admin/components/PreviewBlock.tsx b/packages/app-page-builder/src/admin/components/PreviewBlock.tsx new file mode 100644 index 00000000000..e88b8e21086 --- /dev/null +++ b/packages/app-page-builder/src/admin/components/PreviewBlock.tsx @@ -0,0 +1,16 @@ +import React from "react"; +import { Content as ContentType } from "@webiny/app-page-builder-elements/types"; +import { Content } from "@webiny/app-page-builder-elements/components/Content"; +import { PbPageBlock, PbEditorElement } from "~/types"; +import { addElementId } from "~/editor/helpers"; +import { PageElementsProvider } from "~/contexts/PageBuilder/PageElementsProvider"; + +export const PreviewBlock = ({ element }: { element: PbPageBlock | PbEditorElement }) => { + const elementsWithIds = addElementId(element.content); + + return ( + + + + ); +}; diff --git a/packages/app-page-builder/src/admin/graphql/pages.ts b/packages/app-page-builder/src/admin/graphql/pages.ts index 3916b081ad0..a643ce49b86 100644 --- a/packages/app-page-builder/src/admin/graphql/pages.ts +++ b/packages/app-page-builder/src/admin/graphql/pages.ts @@ -241,7 +241,6 @@ const PAGE_ELEMENT_FIELDS = /*GraphQL*/ ` type category content - preview } `; /** @@ -262,7 +261,6 @@ export interface ListPageElementsQueryResponseData { category: string; type: string; content: PbElement; - preview: ListPageElementsQueryResponseDataPreview; } export interface ListPageElementsQueryResponse { diff --git a/packages/app-page-builder/src/admin/plugins/routes.tsx b/packages/app-page-builder/src/admin/plugins/routes.tsx index 87ba9f5a9aa..c281e8235b5 100644 --- a/packages/app-page-builder/src/admin/plugins/routes.tsx +++ b/packages/app-page-builder/src/admin/plugins/routes.tsx @@ -167,12 +167,14 @@ const plugins: RoutePlugin[] = [ ( + render={({ location }) => ( - - - - + + + + + + )} /> diff --git a/packages/app-page-builder/src/admin/utils/components/graphql.ts b/packages/app-page-builder/src/admin/utils/components/graphql.ts index 568eb03e01a..1878f516629 100644 --- a/packages/app-page-builder/src/admin/utils/components/graphql.ts +++ b/packages/app-page-builder/src/admin/utils/components/graphql.ts @@ -14,7 +14,6 @@ const PAGE_ELEMENT_FIELDS = /*GraphQL*/ ` type category content - preview } `; diff --git a/packages/app-page-builder/src/admin/utils/createBlockPlugin.tsx b/packages/app-page-builder/src/admin/utils/createBlockPlugin.tsx index cd032054016..34580062cef 100644 --- a/packages/app-page-builder/src/admin/utils/createBlockPlugin.tsx +++ b/packages/app-page-builder/src/admin/utils/createBlockPlugin.tsx @@ -1,8 +1,8 @@ import React from "react"; import cloneDeep from "lodash/cloneDeep"; import { plugins } from "@webiny/plugins"; -import { Image } from "@webiny/ui/Image"; import { PbEditorBlockPlugin, PbPageBlock } from "~/types"; +import { PreviewBlock } from "~/admin/components/PreviewBlock"; export default (element: PbPageBlock): void => { const plugin: PbEditorBlockPlugin = { @@ -12,18 +12,11 @@ export default (element: PbPageBlock): void => { title: element.name, blockCategory: element.blockCategory, tags: ["saved"], - image: element.preview, create() { return cloneDeep({ ...element.content, source: element.id }); }, preview() { - return ( - {element.name} - ); + return ; } }; plugins.register(plugin); diff --git a/packages/app-page-builder/src/admin/utils/createElementPlugin.tsx b/packages/app-page-builder/src/admin/utils/createElementPlugin.tsx index ea036b509b0..8b80062ef38 100644 --- a/packages/app-page-builder/src/admin/utils/createElementPlugin.tsx +++ b/packages/app-page-builder/src/admin/utils/createElementPlugin.tsx @@ -2,6 +2,7 @@ import React from "react"; import cloneDeep from "lodash/cloneDeep"; import { plugins } from "@webiny/plugins"; import { OnCreateActions, PbEditorElement, PbEditorPageElementPlugin } from "~/types"; +import { PreviewBlock } from "~/admin/components/PreviewBlock"; import Title from "./components/Title"; /** @@ -34,15 +35,7 @@ export default (el: PbEditorElement): void => { }, group: "pb-editor-element-group-saved", preview() { - return ( - {el.name} - ); + return ; } }, diff --git a/packages/app-page-builder/src/admin/views/PageBlocks/BlocksByCategoriesDataList.tsx b/packages/app-page-builder/src/admin/views/PageBlocks/BlocksByCategoriesDataList.tsx index 99a96cd5848..ba33642439e 100644 --- a/packages/app-page-builder/src/admin/views/PageBlocks/BlocksByCategoriesDataList.tsx +++ b/packages/app-page-builder/src/admin/views/PageBlocks/BlocksByCategoriesDataList.tsx @@ -259,8 +259,7 @@ const BlocksByCategoriesDataList = ({ path: ["sUK8viY2oz", "eM2Z1d9gfH"] } ] - }), - preview: {} + }) } }, refetchQueries: [ diff --git a/packages/app-page-builder/src/admin/views/PageBlocks/PageBlocksDataList.tsx b/packages/app-page-builder/src/admin/views/PageBlocks/PageBlocksDataList.tsx index d255651e162..43a01966f37 100644 --- a/packages/app-page-builder/src/admin/views/PageBlocks/PageBlocksDataList.tsx +++ b/packages/app-page-builder/src/admin/views/PageBlocks/PageBlocksDataList.tsx @@ -15,6 +15,7 @@ import { i18n } from "@webiny/app/i18n"; import { useSnackbar } from "@webiny/app-admin/hooks/useSnackbar"; import { useConfirmationDialog } from "@webiny/app-admin/hooks/useConfirmationDialog"; import useExportBlockDialog from "~/editor/plugins/defaultBar/components/ExportBlockButton/useExportBlockDialog"; +import { addElementId } from "~/editor/helpers"; import { PbPageBlock } from "~/types"; import { @@ -24,7 +25,8 @@ import { DELETE_PAGE_BLOCK } from "./graphql"; import { CreatableItem } from "./PageBlocks"; -import previewFallback from "./assets/preview.png"; +import { Content } from "@webiny/app-page-builder-elements/components/Content"; +import { Content as ContentType } from "@webiny/app-page-builder-elements/types"; const t = i18n.ns("app-page-builder/admin/page-blocks/data-list"); @@ -203,8 +205,7 @@ const PageBlocksDataList = ({ filter, canCreate, canEdit, canDelete }: PageBlock data: { name: `${item.name} (copy)`, blockCategory: item.blockCategory, - content: item.content, - preview: item.preview + content: item.content } } }); @@ -252,10 +253,7 @@ const PageBlocksDataList = ({ filter, canCreate, canEdit, canDelete }: PageBlock {isLoading && } {filteredBlocksData.map(pageBlock => ( - {pageBlock.name} + {pageBlock.name} { - return new Promise(resolve => { - const image = new window.Image(); - image.onload = function () { - resolve({ width: image.width, height: image.height }); - }; - image.src = dataURL; - }); -} - -function takePageScreenshot() { - const node = document.querySelector("pb-document"); - - if (!node) { - return null; - } - - return domToImage.toPng(node, { - width: 2000, - filter: (element: Element) => { - return element.tagName !== "PB-ELEMENT-CONTROLS-OVERLAY"; - } - }); -} - -export async function getPreviewImage( - element: PbElement, - meta: EventActionHandlerMeta, - prevFileId?: string -): Promise { - const editor = document.querySelector(".pb-editor"); - // Hide element highlight while creating the image - editor && editor.classList.add("pb-editor-no-highlight"); - - const dataUrl = await takePageScreenshot(); - - editor && editor.classList.remove("pb-editor-no-highlight"); - - if (!dataUrl) { - return null; - } - - const imageMeta = await getDataURLImageDimensions(dataUrl); - const blob = dataURLtoBlob(dataUrl); - blob.name = "pb-editor-page-element-" + element.id + ".png"; - - const fileUploaderPlugin = plugins.byName("file-uploader"); - - /** - * We break the method because it would break if there is no fileUploaderPlugin. - */ - if (!fileUploaderPlugin) { - return null; - } - - const previewImage = await fileUploaderPlugin.upload(blob, { apolloClient: meta.client }); - - const createFile: FileInput = { - ...previewImage, - tags: [], - meta: { - ...imageMeta, - private: true - } - }; - - const createdImageResponse = await meta.client.mutate({ - mutation: CREATE_FILE, - variables: { - data: createFile - } - }); - - // Delete previous preview image file - if (prevFileId) { - await meta.client.mutate({ - mutation: DELETE_FILE, - variables: { - id: prevFileId - } - }); - } - - return get(createdImageResponse, "data.fileManager.createFile.data", null); -} diff --git a/packages/app-page-builder/src/blockEditor/config/eventActions/saveBlock/saveBlockAction.ts b/packages/app-page-builder/src/blockEditor/config/eventActions/saveBlock/saveBlockAction.ts index 0898789a9dd..004dedc9dc8 100644 --- a/packages/app-page-builder/src/blockEditor/config/eventActions/saveBlock/saveBlockAction.ts +++ b/packages/app-page-builder/src/blockEditor/config/eventActions/saveBlock/saveBlockAction.ts @@ -5,9 +5,8 @@ import { SaveBlockActionArgsType } from "./types"; import { BlockEventActionCallable } from "~/blockEditor/types"; import { BlockWithContent } from "~/blockEditor/state"; import { UPDATE_PAGE_BLOCK } from "~/admin/views/PageBlocks/graphql"; -import { getPreviewImage } from "./getPreviewImage"; import { removeElementId } from "~/editor/helpers"; -import { File, PbElement, PbBlockVariable, PbBlockEditorCreateVariablePlugin } from "~/types"; +import { PbElement, PbBlockVariable, PbBlockEditorCreateVariablePlugin } from "~/types"; export const findElementByVariableId = (elements: PbElement[], variableId: string): any => { for (const element of elements) { @@ -71,14 +70,6 @@ export const saveBlockAction: BlockEventActionCallable // TODO: make sure the API call is not sent if the data was not changed since the last invocation of this event. // See `pageEditor` for an example and feel free to copy that same logic over here. const element = (await state.getElementTree()) as PbElement; - // We need to grab the first block from the "document" element. - const createdImage = await getPreviewImage(element.elements[0], meta, state.block?.preview?.id); - - let preview: Pick | null = null; - if (createdImage) { - const { id, src, meta } = createdImage; - preview = { id, src, meta }; - } const data: BlockType = { name: state.block.name, @@ -98,8 +89,7 @@ export const saveBlockAction: BlockEventActionCallable variables: { id: state.block.id, data: { - ...data, - preview + ...data } } }); diff --git a/packages/app-page-builder/src/editor/plugins/elementSettings/save/SaveAction.tsx b/packages/app-page-builder/src/editor/plugins/elementSettings/save/SaveAction.tsx index 7e9e47e176c..a7663ac79d5 100644 --- a/packages/app-page-builder/src/editor/plugins/elementSettings/save/SaveAction.tsx +++ b/packages/app-page-builder/src/editor/plugins/elementSettings/save/SaveAction.tsx @@ -3,7 +3,6 @@ import React, { useEffect, useCallback, useState } from "react"; * Package dataurl-to-blob does not have types. */ // @ts-ignore -import dataURLtoBlob from "dataurl-to-blob"; import SaveDialog from "./SaveDialog"; import pick from "lodash/pick"; import get from "lodash/get"; @@ -21,8 +20,6 @@ import { LIST_PAGE_BLOCKS_AND_CATEGORIES } from "~/admin/views/PageBlocks/graphql"; import { useRecoilValue } from "recoil"; -import { CREATE_FILE } from "./SaveDialog/graphql"; -import { FileUploaderPlugin } from "@webiny/app/types"; import { PbEditorPageElementPlugin, PbEditorPageElementSaveActionPlugin, @@ -32,24 +29,7 @@ import { } from "~/types"; import { useEventActionHandler } from "~/editor/hooks/useEventActionHandler"; import { removeElementId } from "~/editor/helpers"; -import { FileInput } from "@webiny/app-file-manager/types"; - -interface ImageDimensionsType { - width: number; - height: number; -} -function getDataURLImageDimensions(dataURL: string): Promise { - return new Promise(resolve => { - const image = new window.Image(); - image.onload = function () { - resolve({ width: image.width, height: image.height }); - }; - image.src = dataURL; - }); -} - interface PbDocumentElement extends BasePbEditorElement { - preview: string; overwrite?: boolean; } @@ -82,46 +62,6 @@ const SaveAction: React.FC = ({ children }) => { const pbElement = (await getElementTree({ element })) as PbElement; formData.content = pluginOnSave(removeElementId(pbElement)); - const meta = await getDataURLImageDimensions(formData.preview); - const blob = dataURLtoBlob(formData.preview); - blob.name = "pb-editor-page-element-" + element.id + ".png"; - - const fileUploaderPlugin = plugins.byName("file-uploader"); - /** - * We break the method because it would break if there is no fileUploaderPlugin. - */ - if (!fileUploaderPlugin) { - return; - } - const previewImage = await fileUploaderPlugin.upload(blob, { apolloClient: client }); - - const createFile: FileInput = { - ...previewImage, - tags: [], - meta: { - ...meta, - private: true - } - }; - - const createdImageResponse = await client.mutate({ - mutation: CREATE_FILE, - variables: { - data: createFile - } - }); - - const createdImage = get(createdImageResponse, "data.fileManager.createFile", {}); - if (createdImage.error) { - showSnackbar("Image could not be saved."); - return; - } else if (!createdImage.data.id) { - showSnackbar("Missing saved image id."); - return; - } - - formData.preview = createdImage.data; - if (formData.type === "block") { const query = formData.overwrite ? UPDATE_PAGE_BLOCK : CREATE_PAGE_BLOCK; @@ -130,9 +70,9 @@ const SaveAction: React.FC = ({ children }) => { variables: formData.overwrite ? { id: element.source, - data: pick(formData, ["content", "preview"]) + data: pick(formData, ["content"]) } - : { data: pick(formData, ["name", "blockCategory", "preview", "content"]) }, + : { data: pick(formData, ["name", "blockCategory", "content"]) }, refetchQueries: [{ query: LIST_PAGE_BLOCKS_AND_CATEGORIES }] }); @@ -160,9 +100,9 @@ const SaveAction: React.FC = ({ children }) => { variables: formData.overwrite ? { id: element.source, - data: pick(formData, ["content", "preview"]) + data: pick(formData, ["content"]) } - : { data: pick(formData, ["type", "category", "preview", "name", "content"]) } + : { data: pick(formData, ["type", "category", "name", "content"]) } }); hideDialog(); diff --git a/packages/app-page-builder/src/editor/plugins/elementSettings/save/SaveDialog.tsx b/packages/app-page-builder/src/editor/plugins/elementSettings/save/SaveDialog.tsx index 4d6b1674677..af881b28a32 100644 --- a/packages/app-page-builder/src/editor/plugins/elementSettings/save/SaveDialog.tsx +++ b/packages/app-page-builder/src/editor/plugins/elementSettings/save/SaveDialog.tsx @@ -1,8 +1,9 @@ -import React, { useState } from "react"; +import React, { useState, useEffect } from "react"; import { css } from "emotion"; import { plugins } from "@webiny/plugins"; import ElementPreview from "./SaveDialog/ElementPreview"; import { CircularProgress } from "@webiny/ui/Progress"; +import { PageElementsProvider } from "~/contexts/PageBuilder/PageElementsProvider"; import { Dialog, @@ -20,7 +21,8 @@ import { Grid, Cell } from "@webiny/ui/Grid"; import { Form, FormOnSubmit } from "@webiny/form"; import styled from "@emotion/styled"; import { validation } from "@webiny/validation"; -import { PbEditorBlockCategoryPlugin, PbEditorElement } from "~/types"; +import { PbEditorBlockCategoryPlugin, PbEditorElement, PbElement } from "~/types"; +import { useEventActionHandler } from "~/editor/hooks/useEventActionHandler"; const narrowDialog = css({ ".mdc-dialog__surface": { @@ -57,6 +59,15 @@ const SaveDialog = (props: Props) => { const { element, open, onClose, type } = props; const [loading, setLoading] = useState(false); + const [pbElement, setPbElement] = useState(); + const { getElementTree } = useEventActionHandler(); + + useEffect(() => { + setTimeout(async () => { + setPbElement((await getElementTree({ element })) as PbElement); + }); + }); + const blockCategoriesOptions = plugins .byType("pb-editor-block-category") .map(item => { @@ -74,7 +85,7 @@ const SaveDialog = (props: Props) => { return ( -
+ {({ data, submit, Bind }) => ( Save {type} @@ -123,21 +134,9 @@ const SaveDialog = (props: Props) => { - - {({ value, onChange }) => - value ? ( - {""} - ) : open ? ( - - ) : ( - <> - ) - } - + + + diff --git a/packages/app-page-builder/src/editor/plugins/elementSettings/save/SaveDialog/ElementPreview.tsx b/packages/app-page-builder/src/editor/plugins/elementSettings/save/SaveDialog/ElementPreview.tsx index 9cf0d6c9681..41bc8ed41f2 100644 --- a/packages/app-page-builder/src/editor/plugins/elementSettings/save/SaveDialog/ElementPreview.tsx +++ b/packages/app-page-builder/src/editor/plugins/elementSettings/save/SaveDialog/ElementPreview.tsx @@ -1,32 +1,12 @@ -import React, { useEffect } from "react"; -import domToImage from "./domToImage"; - -const generateImage = async (element: any, onChange: (value: string) => void): Promise => { - const node = document.getElementById(element.id); - if (!node) { - return; - } - - const dataUrl = await domToImage.toPng(node, { - width: 2000, - filter: (element: Element) => { - return element.tagName !== "PB-ELEMENT-CONTROLS-OVERLAY"; - } - }); - - onChange(dataUrl); -}; +import React from "react"; +import { Content } from "@webiny/app-page-builder-elements/components/Content"; type ElementPreviewPropsType = { element: any; - onChange: (value: string) => void; }; -const ElementPreview: React.FC = ({ element, onChange }) => { - useEffect(() => { - generateImage(element, onChange); - }); - return null; +const ElementPreview: React.FC = ({ element }) => { + return ; }; export default ElementPreview; diff --git a/packages/app-page-builder/src/editor/plugins/elementSettings/save/SaveDialog/domToImage.ts b/packages/app-page-builder/src/editor/plugins/elementSettings/save/SaveDialog/domToImage.ts deleted file mode 100644 index 87a71c4789f..00000000000 --- a/packages/app-page-builder/src/editor/plugins/elementSettings/save/SaveDialog/domToImage.ts +++ /dev/null @@ -1,699 +0,0 @@ -// TODO remove -// @ts-nocheck -/* eslint-disable */ - -const util = newUtil(); -const inliner = newInliner(); -const fontFaces = newFontFaces(); -const images = newImages(); - -// Default impl options -const defaultOptions = { - // Default is to fail on error, no placeholder - imagePlaceholder: undefined, - // No caching by default - cacheBust: true -}; - -const domtoimage = { - toPng, - impl: { - fontFaces: fontFaces, - images: images, - util: util, - inliner: inliner, - options: {} - } -}; - -export default domtoimage; - -function toSvg(node, options) { - options = options || {}; - copyOptions(options); - return Promise.resolve(node) - .then(function (node) { - return cloneNode(node, options.filter, true); - }) - .then(embedFonts) - .then(inlineImages) - .then(applyOptions) - .then(function (clone) { - if (typeof options.onDocument === "function") { - clone = options.onDocument(clone); - } - return makeSvgDataUri(clone, util.width(node), util.height(node)); - }); - - function applyOptions(clone) { - if (options.width) clone.style.width = options.width + "px"; - if (options.height) clone.style.height = options.height + "px"; - - return clone; - } -} - -/** - * @param {Node} node - The DOM Node object to render - * @param {Object} options - Rendering options, @see {@link toSvg} - * @return {Promise} - A promise that is fulfilled with a PNG image data URL - * */ -function toPng(node, options) { - return draw(node, options || {}).then(function (canvas) { - return canvas.toDataURL(); - }); -} - -function copyOptions(options) { - // Copy options to impl options for use in impl - if (typeof options.imagePlaceholder === "undefined") { - domtoimage.impl.options.imagePlaceholder = defaultOptions.imagePlaceholder; - } else { - domtoimage.impl.options.imagePlaceholder = options.imagePlaceholder; - } - - if (typeof options.cacheBust === "undefined") { - domtoimage.impl.options.cacheBust = defaultOptions.cacheBust; - } else { - domtoimage.impl.options.cacheBust = options.cacheBust; - } -} - -function draw(domNode, options) { - return toSvg(domNode, options) - .then(util.makeImage) - .then(util.delay(100)) - .then(function (image) { - const canvas = document.createElement("canvas"); - const { width } = options; - const aspect = image.width / image.height; - const height = width / aspect; - - canvas.width = width; - canvas.height = height; - - canvas - .getContext("2d") - .drawImage(image, 0, 0, image.width, image.height, 0, 0, width, height); - - return canvas; - }); -} - -function cloneNode(node, filter, root) { - if (!root && filter && !filter(node)) return Promise.resolve(); - - return Promise.resolve(node) - .then(makeNodeCopy) - .then(function (clone) { - return cloneChildren(node, clone, filter); - }) - .then(function (clone) { - return processClone(node, clone); - }); - - function makeNodeCopy(node) { - if (node instanceof HTMLCanvasElement) return util.makeImage(node.toDataURL()); - return node.cloneNode(false); - } - - function cloneChildren(original, clone, filter) { - var children = original.childNodes; - if (children.length === 0) return Promise.resolve(clone); - - return cloneChildrenInOrder(clone, util.asArray(children), filter).then(function () { - return clone; - }); - - function cloneChildrenInOrder(parent, children, filter) { - var done = Promise.resolve(); - children.forEach(function (child) { - done = done - .then(() => cloneNode(child, filter)) - .then(function (childClone) { - if (childClone) parent.appendChild(childClone); - }); - }); - return done; - } - } - - function processClone(original, clone) { - if (!(clone instanceof Element)) return clone; - - return Promise.resolve() - .then(cloneStyle) - .then(clonePseudoElements) - .then(copyUserInput) - .then(fixSvg) - .then(function () { - return clone; - }); - - function cloneStyle() { - copyStyle(window.getComputedStyle(original), clone.style); - - function copyStyle(source, target) { - if (source.cssText) target.cssText = source.cssText; - else copyProperties(source, target); - - function copyProperties(source, target) { - util.asArray(source).forEach(function (name) { - target.setProperty( - name, - source.getPropertyValue(name), - source.getPropertyPriority(name) - ); - }); - } - } - } - - function clonePseudoElements() { - [":before", ":after"].forEach(function (element) { - clonePseudoElement(element); - }); - - function clonePseudoElement(element) { - var style = window.getComputedStyle(original, element); - var content = style.getPropertyValue("content"); - - if (content === "" || content === "none") return; - - var className = util.uid(); - clone.className = clone.className + " " + className; - var styleElement = document.createElement("style"); - styleElement.appendChild(formatPseudoElementStyle(className, element, style)); - clone.appendChild(styleElement); - - function formatPseudoElementStyle(className, element, style) { - var selector = "." + className + ":" + element; - var cssText = style.cssText ? formatCssText(style) : formatCssProperties(style); - return document.createTextNode(selector + "{" + cssText + "}"); - - function formatCssText(style) { - var content = style.getPropertyValue("content"); - return style.cssText + " content: " + content + ";"; - } - - function formatCssProperties(style) { - return util.asArray(style).map(formatProperty).join("; ") + ";"; - - function formatProperty(name) { - return ( - name + - ": " + - style.getPropertyValue(name) + - (style.getPropertyPriority(name) ? " !important" : "") - ); - } - } - } - } - } - - function copyUserInput() { - if (original instanceof HTMLTextAreaElement) clone.innerHTML = original.value; - if (original instanceof HTMLInputElement) clone.setAttribute("value", original.value); - } - - function fixSvg() { - if (!(clone instanceof SVGElement)) return; - clone.setAttribute("xmlns", "http://www.w3.org/2000/svg"); - - if (!(clone instanceof SVGRectElement)) return; - ["width", "height"].forEach(function (attribute) { - var value = clone.getAttribute(attribute); - if (!value) return; - - clone.style.setProperty(attribute, value); - }); - } - } -} - -function embedFonts(node) { - return fontFaces.resolveAll().then(function (cssText) { - const styleNode = document.createElement("style"); - node.appendChild(styleNode); - styleNode.appendChild(document.createTextNode(cssText)); - return node; - }); -} - -function inlineImages(node) { - return images.inlineAll(node).then(function () { - return node; - }); -} - -function makeSvgDataUri(node, width, height) { - return Promise.resolve(node) - .then(function (node) { - node.setAttribute("xmlns", "http://www.w3.org/1999/xhtml"); - return new XMLSerializer().serializeToString(node); - }) - .then(util.escapeXhtml) - .then(function (xhtml) { - return ( - '' + - encodeURIComponent(xhtml) + - "" - ); - }) - .then(function (foreignObject) { - return ( - '' + - foreignObject + - "" - ); - }) - .then(function (svg) { - return "data:image/svg+xml;charset=utf-8," + svg; - }); -} - -function newUtil() { - return { - escape, - mimeType, - dataAsUrl, - isDataUrl, - resolveUrl, - getAndEncode, - uid: uid(), - delay, - asArray, - escapeXhtml, - makeImage, - width, - height - }; - - function mimes() { - /* - * Only WOFF and EOT mime types for fonts are 'real' - * see http://www.iana.org/assignments/media-types/media-types.xhtml - */ - const WOFF = "application/font-woff"; - const JPEG = "image/jpeg"; - - return { - woff: WOFF, - woff2: WOFF, - ttf: "application/font-truetype", - eot: "application/vnd.ms-fontobject", - png: "image/png", - jpg: JPEG, - jpeg: JPEG, - gif: "image/gif", - tiff: "image/tiff", - svg: "image/svg+xml" - }; - } - - function parseExtension(url) { - const match = /\.([^\.\/]*?)$/g.exec(url); - return match ? match[1] : ""; - } - - function mimeType(url) { - const extension = parseExtension(url).toLowerCase().split("?")[0]; - return mimes()[extension] || ""; - } - - function isDataUrl(url) { - return url.search(/^(data:)/) !== -1; - } - - function toBlob(canvas) { - return new Promise(function (resolve) { - var binaryString = window.atob(canvas.toDataURL().split(",")[1]); - var length = binaryString.length; - var binaryArray = new Uint8Array(length); - - for (var i = 0; i < length; i++) binaryArray[i] = binaryString.charCodeAt(i); - - resolve( - new Blob([binaryArray], { - type: "image/png" - }) - ); - }); - } - - function canvasToBlob(canvas) { - if (canvas.toBlob) - return new Promise(function (resolve) { - canvas.toBlob(resolve); - }); - - return toBlob(canvas); - } - - function resolveUrl(url, baseUrl) { - const doc = document.implementation.createHTMLDocument(); - const base = doc.createElement("base"); - doc.head.appendChild(base); - const a = doc.createElement("a"); - doc.body.appendChild(a); - base.href = baseUrl; - a.href = url; - return a.href; - } - - function uid() { - let index = 0; - - return function () { - return "u" + fourRandomChars() + index++; - - function fourRandomChars() { - /* see http://stackoverflow.com/a/6248722/2519373 */ - return ("0000" + ((Math.random() * Math.pow(36, 4)) << 0).toString(36)).slice(-4); - } - }; - } - - function makeImage(uri) { - return new Promise(function (resolve, reject) { - var image = new Image(); - image.onload = function () { - resolve(image); - }; - image.onerror = reject; - image.src = uri; - }); - } - - function getAndEncode(url) { - var TIMEOUT = 30000; - if (domtoimage.impl.options.cacheBust) { - // Cache bypass so we dont have CORS issues with cached images - // Source: https://developer.mozilla.org/en/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest#Bypassing_the_cache - url += (/\?/.test(url) ? "&" : "?") + new Date().getTime(); - } - - return new Promise(function (resolve) { - var request = new XMLHttpRequest(); - - request.onreadystatechange = done; - request.ontimeout = timeout; - request.responseType = "blob"; - request.timeout = TIMEOUT; - request.open("GET", url, true); - request.send(); - - var placeholder; - if (domtoimage.impl.options.imagePlaceholder) { - var split = domtoimage.impl.options.imagePlaceholder.split(/,/); - if (split && split[1]) { - placeholder = split[1]; - } - } - - function done() { - if (request.readyState !== 4) return; - - if (request.status !== 200) { - if (placeholder) { - resolve(placeholder); - } else { - fail("cannot fetch resource: " + url + ", status: " + request.status); - } - - return; - } - - var encoder = new FileReader(); - encoder.onloadend = function () { - var content = encoder.result.split(/,/)[1]; - resolve(content); - }; - encoder.readAsDataURL(request.response); - } - - function timeout() { - if (placeholder) { - resolve(placeholder); - } else { - fail("timeout of " + TIMEOUT + "ms occured while fetching resource: " + url); - } - } - - function fail(message) { - console.error(message); - resolve(""); - } - }); - } - - function dataAsUrl(content, type) { - return "data:" + type + ";base64," + content; - } - - function escape(string) { - return string.replace(/([.*+?^${}()|\[\]\/\\])/g, "\\$1"); - } - - function delay(ms) { - return function (arg) { - return new Promise(function (resolve) { - setTimeout(function () { - resolve(arg); - }, ms); - }); - }; - } - - function asArray(arrayLike) { - const array = []; - const length = arrayLike.length; - for (var i = 0; i < length; i++) array.push(arrayLike[i]); - return array; - } - - function escapeXhtml(string) { - return string.replace(/#/g, "%23").replace(/\n/g, "%0A"); - } - - function width(node) { - const leftBorder = px(node, "border-left-width"); - const rightBorder = px(node, "border-right-width"); - return node.scrollWidth + leftBorder + rightBorder; - } - - function height(node) { - var topBorder = px(node, "border-top-width"); - var bottomBorder = px(node, "border-bottom-width"); - return node.scrollHeight + topBorder + bottomBorder; - } - - function px(node, styleProperty) { - var value = window.getComputedStyle(node).getPropertyValue(styleProperty); - return parseFloat(value.replace("px", "")); - } -} - -function newInliner() { - const URL_REGEX = /url\(['"]?([^'"]+?)['"]?\)/g; - - return { - inlineAll, - shouldProcess, - impl: { readUrls, inline } - }; - - function shouldProcess(string) { - return string.search(URL_REGEX) !== -1; - } - - function readUrls(string) { - var result = []; - var match; - while ((match = URL_REGEX.exec(string)) !== null) { - result.push(match[1]); - } - return result.filter(function (url) { - return !util.isDataUrl(url); - }); - } - - function inline(string, url, baseUrl, get) { - return Promise.resolve(url) - .then(function (url) { - return baseUrl ? util.resolveUrl(url, baseUrl) : url; - }) - .then(get || util.getAndEncode) - .then(function (data) { - return util.dataAsUrl(data, util.mimeType(url)); - }) - .then(function (dataUrl) { - return string.replace(urlAsRegex(url), "$1" + dataUrl + "$3"); - }); - - function urlAsRegex(url) { - return new RegExp("(url\\(['\"]?)(" + util.escape(url) + ")(['\"]?\\))", "g"); - } - } - - function inlineAll(string, baseUrl, get) { - if (nothingToInline()) return Promise.resolve(string); - - return Promise.resolve(string) - .then(readUrls) - .then(function (urls) { - var done = Promise.resolve(string); - urls.forEach(function (url) { - done = done.then(function (string) { - return inline(string, url, baseUrl, get); - }); - }); - return done; - }); - - function nothingToInline() { - return !shouldProcess(string); - } - } -} - -function newFontFaces() { - return { - resolveAll: resolveAll, - impl: { - readAll: readAll - } - }; - - function resolveAll() { - return readAll(document) - .then(function (webFonts) { - return Promise.all( - webFonts.map(function (webFont) { - return webFont.resolve(); - }) - ); - }) - .then(function (cssStrings) { - return cssStrings.join("\n"); - }); - } - - function readAll() { - return Promise.resolve(util.asArray(document.styleSheets)) - .then(getCssRules) - .then(selectWebFontRules) - .then(function (rules) { - return rules.map(newWebFont); - }); - - function selectWebFontRules(cssRules) { - return cssRules - .filter(function (rule) { - return rule.type === CSSRule.FONT_FACE_RULE; - }) - .filter(function (rule) { - return inliner.shouldProcess(rule.style.getPropertyValue("src")); - }); - } - - function getCssRules(styleSheets) { - var cssRules = []; - styleSheets.forEach(function (sheet) { - if (sheet.hasOwnProperty("cssRules")) { - try { - util.asArray(sheet.cssRules || []).forEach(cssRules.push.bind(cssRules)); - } catch (e) { - console.log( - "Error while reading CSS rules from " + sheet.href, - e.toString() - ); - } - } - }); - return cssRules; - } - - function newWebFont(webFontRule) { - return { - resolve: function resolve() { - var baseUrl = (webFontRule.parentStyleSheet || {}).href; - return inliner.inlineAll(webFontRule.cssText, baseUrl); - }, - src: function () { - return webFontRule.style.getPropertyValue("src"); - } - }; - } - } -} - -function newImages() { - return { - inlineAll: inlineAll, - impl: { newImage } - }; - - function newImage(element) { - return { - inline: inline - }; - - function inline(get) { - if (util.isDataUrl(element.src)) return Promise.resolve(); - - return Promise.resolve(element.src) - .then(get || util.getAndEncode) - .then(function (data) { - return util.dataAsUrl(data, util.mimeType(element.src)); - }) - .then(function (dataUrl) { - element.removeAttribute("srcset"); - - return new Promise(function (resolve, reject) { - element.onload = resolve; - element.onerror = reject; - element.src = dataUrl; - }); - }); - } - } - - function inlineAll(node) { - if (!(node instanceof Element)) return Promise.resolve(node); - - return inlineBackground(node).then(function () { - if (node instanceof HTMLImageElement) return newImage(node).inline(); - else - return Promise.all( - util.asArray(node.childNodes).map(function (child) { - return inlineAll(child); - }) - ); - }); - - function inlineBackground(node) { - var background = node.style.getPropertyValue("background"); - - if (!background) return Promise.resolve(node); - - return inliner - .inlineAll(background) - .then(function (inlined) { - node.style.setProperty( - "background", - inlined, - node.style.getPropertyPriority("background") - ); - }) - .then(function () { - return node; - }); - } - } -} diff --git a/packages/app-page-builder/src/pageEditor/config/blockEditing/BlockPreview.tsx b/packages/app-page-builder/src/pageEditor/config/blockEditing/BlockPreview.tsx index 19f5e4fbbc6..b2fb1b678cd 100644 --- a/packages/app-page-builder/src/pageEditor/config/blockEditing/BlockPreview.tsx +++ b/packages/app-page-builder/src/pageEditor/config/blockEditing/BlockPreview.tsx @@ -12,7 +12,6 @@ import * as Styled from "./StyledComponents"; import kebabCase from "lodash/kebabCase"; import { PbEditorBlockPlugin } from "~/types"; import { useCallback } from "react"; -import previewFallback from "~/admin/views/PageBlocks/assets/preview.png"; interface BlockPreviewProps { plugin: PbEditorBlockPlugin; @@ -46,6 +45,8 @@ const BlockPreview: React.FC = props => { {onDelete && ( } @@ -80,13 +81,7 @@ const BlockPreview: React.FC = props => { )} - - {plugin?.image?.src ? ( - plugin.preview?.() - ) : ( - {plugin.title} - )} - + {plugin.preview()} {plugin.title} diff --git a/packages/app-page-builder/src/pageEditor/config/blockEditing/BlocksList.tsx b/packages/app-page-builder/src/pageEditor/config/blockEditing/BlocksList.tsx index 060a4934e87..8d7c11370b5 100644 --- a/packages/app-page-builder/src/pageEditor/config/blockEditing/BlocksList.tsx +++ b/packages/app-page-builder/src/pageEditor/config/blockEditing/BlocksList.tsx @@ -127,7 +127,10 @@ const BlocksList: React.FC = props => { blocks, addBlock: props.addBlock, onEdit: props.onEdit, - onDelete: props.onDelete + onDelete: props.onDelete, + style: { + position: "static" + } }); }} scrollTop={scrollTop} diff --git a/packages/app-page-builder/src/pageEditor/config/blockEditing/EditBlockDialog.tsx b/packages/app-page-builder/src/pageEditor/config/blockEditing/EditBlockDialog.tsx index 7c295aeaddb..17d5fd6c5c3 100644 --- a/packages/app-page-builder/src/pageEditor/config/blockEditing/EditBlockDialog.tsx +++ b/packages/app-page-builder/src/pageEditor/config/blockEditing/EditBlockDialog.tsx @@ -1,5 +1,4 @@ import React from "react"; -import { css } from "emotion"; import { plugins } from "@webiny/plugins"; import { Dialog, @@ -19,15 +18,17 @@ import { Form, FormOnSubmit } from "@webiny/form"; import styled from "@emotion/styled"; import { PbEditorBlockCategoryPlugin, PbEditorBlockPlugin } from "~/types"; -const narrowDialog = css({ - ".mdc-dialog__surface": { - width: 600, - minWidth: 600 +const StyledDialog = styled(Dialog)` + // We need to have this z-index because without it Edit Block Dialog will be rendered below All Blocks Component. + z-index: 100; + + & .mdc-dialog__surface { + width: 600px; + min-width: 600px; } -}); +`; const PreviewBox = styled("div")({ - width: 500, minHeight: 250, border: "1px solid var(--mdc-theme-on-background)", backgroundColor: "#fff", // this must always be white @@ -60,7 +61,7 @@ const EditBlockDialog: React.FC = props => { })); return ( - + {loading && } {plugin && ( @@ -108,7 +109,7 @@ const EditBlockDialog: React.FC = props => { )} )} - + ); }; diff --git a/packages/app-page-builder/src/pageEditor/config/blockEditing/graphql.ts b/packages/app-page-builder/src/pageEditor/config/blockEditing/graphql.ts index 4fb1e3e54a7..c42bde2ad27 100644 --- a/packages/app-page-builder/src/pageEditor/config/blockEditing/graphql.ts +++ b/packages/app-page-builder/src/pageEditor/config/blockEditing/graphql.ts @@ -7,7 +7,6 @@ const PAGE_ELEMENT_FIELDS = /*GraphQL*/ ` type category content - preview } `; diff --git a/packages/app-page-builder/src/types.ts b/packages/app-page-builder/src/types.ts index 2f5463a6ae3..3b50f50e054 100644 --- a/packages/app-page-builder/src/types.ts +++ b/packages/app-page-builder/src/types.ts @@ -632,8 +632,7 @@ export type PbEditorBlockPlugin = Plugin & { blockCategory: string; tags: string[]; create(): PbEditorElement; - image?: Partial; - preview?(): ReactElement; + preview(): ReactElement; }; export type PbEditorBlockCategoryPlugin = Plugin & { @@ -868,7 +867,6 @@ export interface PbPageBlock { name: string; blockCategory: string; content: any; - preview: File; createdOn: string; createdBy: PbIdentity; } diff --git a/packages/app-page-builder/tsconfig.build.json b/packages/app-page-builder/tsconfig.build.json index 489e4547ba0..8e7a8c5a653 100644 --- a/packages/app-page-builder/tsconfig.build.json +++ b/packages/app-page-builder/tsconfig.build.json @@ -5,7 +5,6 @@ { "path": "../app/tsconfig.build.json" }, { "path": "../app-aco/tsconfig.build.json" }, { "path": "../app-admin/tsconfig.build.json" }, - { "path": "../app-file-manager/tsconfig.build.json" }, { "path": "../app-i18n/tsconfig.build.json" }, { "path": "../app-page-builder-elements/tsconfig.build.json" }, { "path": "../app-plugin-admin-welcome-screen/tsconfig.build.json" }, diff --git a/packages/app-page-builder/tsconfig.json b/packages/app-page-builder/tsconfig.json index 37cedd5c74d..060b37feb68 100644 --- a/packages/app-page-builder/tsconfig.json +++ b/packages/app-page-builder/tsconfig.json @@ -5,7 +5,6 @@ { "path": "../app" }, { "path": "../app-aco" }, { "path": "../app-admin" }, - { "path": "../app-file-manager" }, { "path": "../app-i18n" }, { "path": "../app-page-builder-elements" }, { "path": "../app-plugin-admin-welcome-screen" }, @@ -34,8 +33,6 @@ "@webiny/app-aco": ["../app-aco/src"], "@webiny/app-admin/*": ["../app-admin/src/*"], "@webiny/app-admin": ["../app-admin/src"], - "@webiny/app-file-manager/*": ["../app-file-manager/src/*"], - "@webiny/app-file-manager": ["../app-file-manager/src"], "@webiny/app-i18n/*": ["../app-i18n/src/*"], "@webiny/app-i18n": ["../app-i18n/src"], "@webiny/app-page-builder-elements/*": ["../app-page-builder-elements/src/*"], diff --git a/yarn.lock b/yarn.lock index a65d17e340a..6c2ebdd5995 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15075,7 +15075,6 @@ __metadata: "@webiny/app": 0.0.0 "@webiny/app-aco": 0.0.0 "@webiny/app-admin": 0.0.0 - "@webiny/app-file-manager": 0.0.0 "@webiny/app-i18n": 0.0.0 "@webiny/app-page-builder-elements": 0.0.0 "@webiny/app-plugin-admin-welcome-screen": 0.0.0 @@ -15100,7 +15099,6 @@ __metadata: apollo-utilities: ^1.3.4 babel-plugin-emotion: ^9.2.8 classnames: ^2.2.6 - dataurl-to-blob: ^0.0.1 dot-prop-immutable: ^2.1.0 emotion: ^10.0.17 execa: ^5.0.0