mirror of
https://github.com/penpot/penpot-exporter-figma-plugin.git
synced 2024-12-22 05:33:02 -05:00
Dynamic plugin size calculation, and cap the height to a safe value (#98)
* dynamic size * fixes * fixes * fix dynamic size * add changelog --------- Co-authored-by: Jordi Sala Morales <jordism91@gmail.com>
This commit is contained in:
parent
7cae18460c
commit
8d5c5c15eb
14 changed files with 700 additions and 453 deletions
5
.changeset/shaggy-dryers-guess.md
Normal file
5
.changeset/shaggy-dryers-guess.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
"penpot-exporter": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Recalculate better the plugin height and limit it to a safer value so it does not get out of the screen
|
934
package-lock.json
generated
934
package-lock.json
generated
File diff suppressed because it is too large
Load diff
10
package.json
10
package.json
|
@ -29,27 +29,27 @@
|
||||||
"react-hook-form": "^7.51",
|
"react-hook-form": "^7.51",
|
||||||
"romans": "^2.0",
|
"romans": "^2.0",
|
||||||
"slugify": "^1.6",
|
"slugify": "^1.6",
|
||||||
"svg-path-parser": "^1.1"
|
"svg-path-parser": "^1.1",
|
||||||
|
"use-resize-observer": "^9.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@changesets/changelog-github": "^0.5",
|
"@changesets/changelog-github": "^0.5",
|
||||||
"@changesets/cli": "^2.27",
|
"@changesets/cli": "^2.27",
|
||||||
"@figma/eslint-plugin-figma-plugins": "^0.15",
|
"@figma/eslint-plugin-figma-plugins": "^0.15",
|
||||||
"@figma/plugin-typings": "^1.92",
|
"@figma/plugin-typings": "^1.93",
|
||||||
"@preact/preset-vite": "^2.8",
|
"@preact/preset-vite": "^2.8",
|
||||||
"@trivago/prettier-plugin-sort-imports": "^4.3",
|
"@trivago/prettier-plugin-sort-imports": "^4.3",
|
||||||
"@types/svg-path-parser": "^1.1",
|
"@types/svg-path-parser": "^1.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.8",
|
"@typescript-eslint/eslint-plugin": "^7.8",
|
||||||
"@typescript-eslint/parser": "^7.8",
|
"@typescript-eslint/parser": "^7.8",
|
||||||
"@vitejs/plugin-react-swc": "^3.6",
|
|
||||||
"concurrently": "^8.2",
|
"concurrently": "^8.2",
|
||||||
"esbuild": "^0.20",
|
"esbuild": "^0.21",
|
||||||
"eslint": "^8.57",
|
"eslint": "^8.57",
|
||||||
"eslint-config-prettier": "^9.1",
|
"eslint-config-prettier": "^9.1",
|
||||||
"eslint-plugin-prettier": "^5.1",
|
"eslint-plugin-prettier": "^5.1",
|
||||||
"eslint-plugin-react": "^7.34",
|
"eslint-plugin-react": "^7.34",
|
||||||
"prettier": "^3.2",
|
"prettier": "^3.2",
|
||||||
"stylelint": "^16.4",
|
"stylelint": "^16.5",
|
||||||
"stylelint-config-standard": "^36.0",
|
"stylelint-config-standard": "^36.0",
|
||||||
"typescript": "^5.4",
|
"typescript": "^5.4",
|
||||||
"vite": "^5.2",
|
"vite": "^5.2",
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import { findAllTextNodes } from './findAllTextnodes';
|
import { findAllTextNodes } from './findAllTextnodes';
|
||||||
import { handleExportMessage } from './handleExportMessage';
|
import { handleExportMessage } from './handleExportMessage';
|
||||||
import { BASE_WIDTH, LOADING_HEIGHT } from './pluginSizes';
|
|
||||||
import { registerChange } from './registerChange';
|
import { registerChange } from './registerChange';
|
||||||
|
|
||||||
figma.showUI(__html__, { themeColors: true, width: BASE_WIDTH, height: LOADING_HEIGHT });
|
const BASE_HEIGHT = 135;
|
||||||
|
const BASE_WIDTH = 290;
|
||||||
|
|
||||||
|
figma.showUI(__html__, { themeColors: true, width: BASE_WIDTH, height: BASE_HEIGHT });
|
||||||
|
|
||||||
figma.ui.onmessage = message => {
|
figma.ui.onmessage = message => {
|
||||||
if (message.type === 'ready') {
|
if (message.type === 'ready') {
|
||||||
|
@ -21,6 +23,10 @@ figma.ui.onmessage = message => {
|
||||||
if (message.type === 'reload') {
|
if (message.type === 'reload') {
|
||||||
findAllTextNodes();
|
findAllTextNodes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (message.type === 'resize') {
|
||||||
|
figma.ui.resize(BASE_WIDTH, message.height);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
figma.on('currentpagechange', () => {
|
figma.on('currentpagechange', () => {
|
||||||
|
|
|
@ -1,9 +1,3 @@
|
||||||
import {
|
|
||||||
BASE_WIDTH,
|
|
||||||
MISSING_FONTS_TEXT_HEIGHT,
|
|
||||||
MISSING_SINGLE_FONT_HEIGHT,
|
|
||||||
NORMAL_HEIGHT
|
|
||||||
} from './pluginSizes';
|
|
||||||
import { registerChange } from './registerChange';
|
import { registerChange } from './registerChange';
|
||||||
import { isGoogleFont } from './translators/text/font/gfonts';
|
import { isGoogleFont } from './translators/text/font/gfonts';
|
||||||
import { isLocalFont } from './translators/text/font/local';
|
import { isLocalFont } from './translators/text/font/local';
|
||||||
|
@ -34,10 +28,5 @@ export const findAllTextNodes = async () => {
|
||||||
data: Array.from(fonts)
|
data: Array.from(fonts)
|
||||||
});
|
});
|
||||||
|
|
||||||
const newHeight =
|
|
||||||
NORMAL_HEIGHT +
|
|
||||||
(fonts.size > 0 ? MISSING_FONTS_TEXT_HEIGHT + fonts.size * MISSING_SINGLE_FONT_HEIGHT : 0);
|
|
||||||
|
|
||||||
figma.ui.resize(BASE_WIDTH, newHeight);
|
|
||||||
figma.currentPage.once('nodechange', registerChange);
|
figma.currentPage.once('nodechange', registerChange);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
export const LOADING_HEIGHT = 130;
|
|
||||||
export const NORMAL_HEIGHT = LOADING_HEIGHT + 5;
|
|
||||||
export const BASE_WIDTH = 290;
|
|
||||||
|
|
||||||
export const MISSING_FONTS_TEXT_HEIGHT = 195;
|
|
||||||
export const MISSING_SINGLE_FONT_HEIGHT = 65;
|
|
||||||
|
|
||||||
export const NEEDS_RELOAD_TEXT_HEIGHT = 90;
|
|
|
@ -1,6 +1,3 @@
|
||||||
import { BASE_WIDTH, NEEDS_RELOAD_TEXT_HEIGHT, NORMAL_HEIGHT } from './pluginSizes';
|
|
||||||
|
|
||||||
export const registerChange = () => {
|
export const registerChange = () => {
|
||||||
figma.ui.postMessage({ type: 'CHANGES_DETECTED' });
|
figma.ui.postMessage({ type: 'CHANGES_DETECTED' });
|
||||||
figma.ui.resize(BASE_WIDTH, NORMAL_HEIGHT + NEEDS_RELOAD_TEXT_HEIGHT);
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,11 +1,28 @@
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
import useResizeObserver from 'use-resize-observer';
|
||||||
|
|
||||||
import Penpot from '@ui/assets/penpot.svg?react';
|
import Penpot from '@ui/assets/penpot.svg?react';
|
||||||
import { PenpotExporter } from '@ui/components/PenpotExporter';
|
import { PenpotExporter } from '@ui/components/PenpotExporter';
|
||||||
import { Stack } from '@ui/components/Stack';
|
import { Stack } from '@ui/components/Stack';
|
||||||
|
|
||||||
import { Wrapper } from './components/Wrapper/Wrapper';
|
import { Wrapper } from './components/Wrapper/Wrapper';
|
||||||
|
|
||||||
export const App = () => (
|
// Safe default value to avoid overflowing from the screen
|
||||||
<Wrapper>
|
const MAX_HEIGHT = 800;
|
||||||
|
|
||||||
|
export const App = () => {
|
||||||
|
const { ref, height } = useResizeObserver<HTMLDivElement>({ box: 'border-box' });
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (height === undefined) return;
|
||||||
|
|
||||||
|
const capHeight = Math.min(height, MAX_HEIGHT);
|
||||||
|
|
||||||
|
parent.postMessage({ pluginMessage: { type: 'resize', height: capHeight } }, '*');
|
||||||
|
}, [height]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Wrapper ref={ref} overflowing={(height ?? 0) > MAX_HEIGHT}>
|
||||||
<Stack space="medium">
|
<Stack space="medium">
|
||||||
<Penpot
|
<Penpot
|
||||||
style={{
|
style={{
|
||||||
|
@ -19,3 +36,4 @@ export const App = () => (
|
||||||
</Stack>
|
</Stack>
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
|
@ -6,12 +6,12 @@
|
||||||
flex-direction: var(--direction);
|
flex-direction: var(--direction);
|
||||||
gap: var(--spacing);
|
gap: var(--spacing);
|
||||||
|
|
||||||
|
.stack-row {
|
||||||
|
--direction: row;
|
||||||
|
|
||||||
& > * {
|
& > * {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stack-row {
|
|
||||||
--direction: row;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.stack-small {
|
.stack-small {
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
.wrapper {
|
.wrapper {
|
||||||
margin: 1.5rem 1rem 0;
|
padding: 1.5rem 1rem 1rem;
|
||||||
|
|
||||||
|
.wrapper-overflow {
|
||||||
|
/* Empty class used to detect overflow on the body */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,27 @@
|
||||||
import { CSSProperties, PropsWithChildren } from 'react';
|
import classNames from 'classnames';
|
||||||
|
import { CSSProperties, PropsWithChildren, forwardRef } from 'react';
|
||||||
|
|
||||||
import styles from './Wrapper.module.css';
|
import styles from './Wrapper.module.css';
|
||||||
|
|
||||||
type WrapperProps = PropsWithChildren & {
|
type WrapperProps = PropsWithChildren & {
|
||||||
style?: CSSProperties;
|
style?: CSSProperties;
|
||||||
|
overflowing?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Wrapper = ({ style, children }: WrapperProps) => {
|
const Wrapper = forwardRef<HTMLDivElement, WrapperProps>(
|
||||||
|
({ style, overflowing = false, children }: WrapperProps, ref) => {
|
||||||
return (
|
return (
|
||||||
<div className={styles.wrapper} style={style}>
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className={classNames({ [styles.wrapper]: true, [styles.wrapperOverflow]: overflowing })}
|
||||||
|
style={style}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Wrapper.displayName = 'Wrapper';
|
||||||
|
|
||||||
|
export { Wrapper };
|
||||||
|
|
|
@ -1,40 +1,3 @@
|
||||||
*,
|
body :not:has(.wrapper-overflow) {
|
||||||
*::before,
|
overflow-y: hidden;
|
||||||
*::after {
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
line-height: 1.5;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
}
|
|
||||||
|
|
||||||
img,
|
|
||||||
picture,
|
|
||||||
video,
|
|
||||||
canvas,
|
|
||||||
svg {
|
|
||||||
display: block;
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
input,
|
|
||||||
button,
|
|
||||||
textarea,
|
|
||||||
select {
|
|
||||||
font: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
p,
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
h3,
|
|
||||||
h4,
|
|
||||||
h5,
|
|
||||||
h6 {
|
|
||||||
overflow-wrap: break-word;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { createRoot } from 'react-dom/client';
|
||||||
|
|
||||||
import { App } from './App';
|
import { App } from './App';
|
||||||
import './main.css';
|
import './main.css';
|
||||||
|
import './reset.css';
|
||||||
|
|
||||||
createRoot(document.getElementById('root') as HTMLElement).render(
|
createRoot(document.getElementById('root') as HTMLElement).render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
|
|
40
ui-src/reset.css
Normal file
40
ui-src/reset.css
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
line-height: 1.5;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
|
||||||
|
img,
|
||||||
|
picture,
|
||||||
|
video,
|
||||||
|
canvas,
|
||||||
|
svg {
|
||||||
|
display: block;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
|
button,
|
||||||
|
textarea,
|
||||||
|
select {
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
p,
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
}
|
Loading…
Reference in a new issue