From b3d7788bff595812de410e771c9fafaa8a487568 Mon Sep 17 00:00:00 2001 From: riccardoperra Date: Sun, 1 Jan 2023 12:22:01 +0100 Subject: [PATCH] fix(dom-export): fix web components default styles --- packages/dom-export/src/lib/cloneNode.ts | 10 ++-- packages/dom-export/src/lib/cloneStyle.ts | 59 +++++++++++------------ packages/dom-export/src/lib/util.ts | 6 +++ 3 files changed, 41 insertions(+), 34 deletions(-) diff --git a/packages/dom-export/src/lib/cloneNode.ts b/packages/dom-export/src/lib/cloneNode.ts index 2643f1339..d9882914f 100644 --- a/packages/dom-export/src/lib/cloneNode.ts +++ b/packages/dom-export/src/lib/cloneNode.ts @@ -73,7 +73,7 @@ async function cloneChildren( function cloneCSSStyle( nativeNode: T, clonedNode: T, - parentComputedStyles: CSSStyleDeclaration, + parentComputedStyles: CSSStyleDeclaration | null, ) { const nativeComputedStyle = getComputedStyle(nativeNode); const sourceStyle = clonedNode.style; @@ -153,7 +153,7 @@ function cloneSelectValue(nativeNode: T, clonedNode: T) { async function decorate( nativeNode: T, clonedNode: T, - parentComputedStyles: CSSStyleDeclaration, + parentComputedStyles: CSSStyleDeclaration | null, ): Promise { if (!(clonedNode instanceof Element)) { return clonedNode; @@ -218,7 +218,7 @@ export async function cloneNode( node: T, options: Options, isRoot?: boolean, - parentComputedStyles?: any, + parentComputedStyles?: CSSStyleDeclaration | null, ): Promise { if (!isRoot && options.filter && !options.filter(node)) { return null; @@ -227,6 +227,8 @@ export async function cloneNode( return Promise.resolve(node) .then(clonedNode => cloneSingleNode(clonedNode, options) as Promise) .then(clonedNode => cloneChildren(node, clonedNode, options)) - .then(clonedNode => decorate(node, clonedNode, parentComputedStyles)) + .then(clonedNode => + decorate(node, clonedNode, parentComputedStyles ?? null), + ) .then(clonedNode => ensureSVGSymbols(clonedNode, options)); } diff --git a/packages/dom-export/src/lib/cloneStyle.ts b/packages/dom-export/src/lib/cloneStyle.ts index c0ca6f53c..f2feb683d 100644 --- a/packages/dom-export/src/lib/cloneStyle.ts +++ b/packages/dom-export/src/lib/cloneStyle.ts @@ -4,7 +4,7 @@ *: * https://github.com/1904labs/dom-to-image-more */ -import {toArray} from './util'; +import {isCustomElement, isSlotElement, toArray} from './util'; export function copyFont( source: CSSStyleDeclaration, @@ -28,7 +28,7 @@ export function copyFont( export function copyUserComputedStyleFast( sourceComputedStyles: CSSStyleDeclaration, - parentComputedStyles: CSSStyleDeclaration, + parentComputedStyles: CSSStyleDeclaration | null, targetElement: T, ) { const defaultStyle = getDefaultStyle(targetElement); @@ -38,14 +38,6 @@ export function copyUserComputedStyleFast( const typedName = name as string; let sourceValue = sourceComputedStyles.getPropertyValue(typedName); - if (name === 'font-size' && sourceValue.endsWith('px')) { - const reducedFont = - Math.floor( - parseFloat(sourceValue.substring(0, sourceValue.length - 2)), - ) - 0.1; - sourceValue = `${reducedFont}px`; - } - // This is needed to be able to "treeshake" web fonts during embedding if ( (name === 'font-family' || @@ -93,12 +85,6 @@ let removeDefaultStylesTimeoutId: number | null = null; let sandbox: HTMLIFrameElement | null = null; let tagNameDefaultStyles: Record> = {}; -export const isSlotElement = (node: Node): node is HTMLSlotElement => - (node as Element).tagName === 'SLOT'; - -export const isCustomElement = (node: Element): boolean => - node.tagName.indexOf('-') > 0; - function getDefaultStyle(element: T) { const tagName = element.tagName; if (tagNameDefaultStyles[tagName]) { @@ -121,29 +107,42 @@ function getDefaultStyle(element: T) { } if (!sandbox.contentWindow) return {}; + const defaultStyle: Record = {}; + if (!isSlotElement(element) && !isCustomElement(element)) { const defaultElement = document.createElement(tagName); sandbox.contentWindow.document.body.appendChild(defaultElement); // Ensure that there is some content, so that properties like margin are applied. defaultElement.textContent = '.'; - const defaultComputedStyle = - sandbox.contentWindow.getComputedStyle(defaultElement); - const defaultStyle: Record = {}; - // Copy styles to an object, making sure that 'width' and 'height' are given the default value of 'auto', since - // their initial value is always 'auto' despite that the default computed value is sometimes an absolute length. - toArray(defaultComputedStyle).forEach(name => { - const typedName = name as string; - defaultStyle[typedName] = - name === 'width' || name === 'height' - ? 'auto' - : defaultComputedStyle.getPropertyValue(typedName); - }); + + copyCSSStyleDeclaration( + defaultStyle, + sandbox.contentWindow.getComputedStyle(defaultElement), + ); + sandbox.contentWindow.document.body.removeChild(defaultElement); - tagNameDefaultStyles[tagName] = defaultStyle; return defaultStyle; + } else { + copyCSSStyleDeclaration(defaultStyle, window.getComputedStyle(element)); } - return {}; + return defaultStyle; +} + +function copyCSSStyleDeclaration( + targetDefaultStyle: Record, + declaration: CSSStyleDeclaration, +) { + // Copy styles to an object, making sure that 'width' and 'height' are given the default value of 'auto', since + // their initial value is always 'auto' despite that the default computed value is sometimes an absolute length. + toArray(declaration).forEach(name => { + const typedName = name as string; + targetDefaultStyle[typedName] = + name === 'width' || name === 'height' + ? 'auto' + : declaration.getPropertyValue(typedName); + }); + return targetDefaultStyle; } export function removeSandbox() { diff --git a/packages/dom-export/src/lib/util.ts b/packages/dom-export/src/lib/util.ts index 6d6472c84..b7c55b3c2 100644 --- a/packages/dom-export/src/lib/util.ts +++ b/packages/dom-export/src/lib/util.ts @@ -64,6 +64,12 @@ export function isDataUrl(url: string) { return url.search(/^(data:)/) !== -1; } +export const isSlotElement = (node: Node): node is HTMLSlotElement => + (node as Element).tagName === 'SLOT'; + +export const isCustomElement = (node: Element): boolean => + node.tagName.indexOf('-') > 0; + export function makeDataUrl(content: string, mimeType: string) { return `data:${mimeType};base64,${content}`; }