Skip to content

Commit

Permalink
fix(dom-export): fix web components default styles
Browse files Browse the repository at this point in the history
  • Loading branch information
riccardoperra committed Jan 1, 2023
1 parent 5687b8e commit b3d7788
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 34 deletions.
10 changes: 6 additions & 4 deletions packages/dom-export/src/lib/cloneNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ async function cloneChildren<T extends HTMLElement>(
function cloneCSSStyle<T extends HTMLElement>(
nativeNode: T,
clonedNode: T,
parentComputedStyles: CSSStyleDeclaration,
parentComputedStyles: CSSStyleDeclaration | null,
) {
const nativeComputedStyle = getComputedStyle(nativeNode);
const sourceStyle = clonedNode.style;
Expand Down Expand Up @@ -153,7 +153,7 @@ function cloneSelectValue<T extends HTMLElement>(nativeNode: T, clonedNode: T) {
async function decorate<T extends HTMLElement>(
nativeNode: T,
clonedNode: T,
parentComputedStyles: CSSStyleDeclaration,
parentComputedStyles: CSSStyleDeclaration | null,
): Promise<T> {
if (!(clonedNode instanceof Element)) {
return clonedNode;
Expand Down Expand Up @@ -218,7 +218,7 @@ export async function cloneNode<T extends HTMLElement>(
node: T,
options: Options,
isRoot?: boolean,
parentComputedStyles?: any,
parentComputedStyles?: CSSStyleDeclaration | null,
): Promise<T | null> {
if (!isRoot && options.filter && !options.filter(node)) {
return null;
Expand All @@ -227,6 +227,8 @@ export async function cloneNode<T extends HTMLElement>(
return Promise.resolve(node)
.then(clonedNode => cloneSingleNode(clonedNode, options) as Promise<T>)
.then(clonedNode => cloneChildren(node, clonedNode, options))
.then(clonedNode => decorate(node, clonedNode, parentComputedStyles))
.then(clonedNode =>
decorate(node, clonedNode, parentComputedStyles ?? null),
)
.then(clonedNode => ensureSVGSymbols(clonedNode, options));
}
59 changes: 29 additions & 30 deletions packages/dom-export/src/lib/cloneStyle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -28,7 +28,7 @@ export function copyFont(

export function copyUserComputedStyleFast<T extends HTMLElement>(
sourceComputedStyles: CSSStyleDeclaration,
parentComputedStyles: CSSStyleDeclaration,
parentComputedStyles: CSSStyleDeclaration | null,
targetElement: T,
) {
const defaultStyle = getDefaultStyle(targetElement);
Expand All @@ -38,14 +38,6 @@ export function copyUserComputedStyleFast<T extends HTMLElement>(
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' ||
Expand Down Expand Up @@ -93,12 +85,6 @@ let removeDefaultStylesTimeoutId: number | null = null;
let sandbox: HTMLIFrameElement | null = null;
let tagNameDefaultStyles: Record<string, Record<string, string>> = {};

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<T extends Element>(element: T) {
const tagName = element.tagName;
if (tagNameDefaultStyles[tagName]) {
Expand All @@ -121,29 +107,42 @@ function getDefaultStyle<T extends Element>(element: T) {
}
if (!sandbox.contentWindow) return {};

const defaultStyle: Record<string, string> = {};

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<string, string> = {};
// 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<string, string>,
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() {
Expand Down
6 changes: 6 additions & 0 deletions packages/dom-export/src/lib/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}`;
}
Expand Down

0 comments on commit b3d7788

Please sign in to comment.