diff --git a/.changeset/dull-donkeys-share.md b/.changeset/dull-donkeys-share.md new file mode 100644 index 0000000..d2ec82c --- /dev/null +++ b/.changeset/dull-donkeys-share.md @@ -0,0 +1,5 @@ +--- +"penpot-exporter": minor +--- + +Added support for masked groups diff --git a/plugin-src/transformers/partials/transformChildren.ts b/plugin-src/transformers/partials/transformChildren.ts index 579fb3c..6a94471 100644 --- a/plugin-src/transformers/partials/transformChildren.ts +++ b/plugin-src/transformers/partials/transformChildren.ts @@ -1,16 +1,22 @@ -import { transformSceneNode } from '@plugin/transformers'; +import { translateChildren, translateMaskChildren } from '@plugin/translators'; -import { PenpotNode } from '@ui/lib/types/penpotNode'; import { Children } from '@ui/lib/types/utils/children'; +const nodeActsAsMask = (node: SceneNode): boolean => { + return 'isMask' in node && node.isMask; +}; + export const transformChildren = async ( node: ChildrenMixin, baseX: number = 0, baseY: number = 0 ): Promise => { + const maskIndex = node.children.findIndex(nodeActsAsMask); + const containsMask = maskIndex !== -1; + return { - children: ( - await Promise.all(node.children.map(child => transformSceneNode(child, baseX, baseY))) - ).filter((child): child is PenpotNode => !!child) + children: containsMask + ? await translateMaskChildren(node.children, maskIndex, baseX, baseY) + : await translateChildren(node.children, baseX, baseY) }; }; diff --git a/plugin-src/translators/index.ts b/plugin-src/translators/index.ts index 1bb9a10..465baba 100644 --- a/plugin-src/translators/index.ts +++ b/plugin-src/translators/index.ts @@ -1,5 +1,6 @@ export * from './translateBlendMode'; export * from './translateBlurEffects'; export * from './translateBoolType'; +export * from './translateChildren'; export * from './translateShadowEffects'; export * from './translateStrokes'; diff --git a/plugin-src/translators/translateChildren.ts b/plugin-src/translators/translateChildren.ts new file mode 100644 index 0000000..4d75b0c --- /dev/null +++ b/plugin-src/translators/translateChildren.ts @@ -0,0 +1,41 @@ +import { transformGroupNodeLike, transformSceneNode } from '@plugin/transformers'; + +import { PenpotNode } from '@ui/lib/types/penpotNode'; + +/** + * Translates the children of a node that acts as a mask. + * We need to split the children into two groups: the ones that are masked and the ones that are not. + * + * The masked children will be grouped together in a mask group. + * The unmasked children will be returned as they are. + * + * @maskIndex The index of the mask node in the children array + */ +export const translateMaskChildren = async ( + children: readonly SceneNode[], + maskIndex: number, + baseX: number, + baseY: number +): Promise => { + const maskChild = children[maskIndex]; + const unmaskedChildren = await translateChildren(children.slice(0, maskIndex), baseX, baseY); + const maskedChildren = await translateChildren(children.slice(maskIndex), baseX, baseY); + + const maskGroup = { + ...transformGroupNodeLike(maskChild, baseX, baseY), + children: maskedChildren, + maskedGroup: true + }; + + return [...unmaskedChildren, maskGroup]; +}; + +export const translateChildren = async ( + children: readonly SceneNode[], + baseX: number, + baseY: number +): Promise => { + return (await Promise.all(children.map(child => transformSceneNode(child, baseX, baseY)))).filter( + (child): child is PenpotNode => !!child + ); +};