mirror of
https://github.com/penpot/penpot-exporter-figma-plugin.git
synced 2024-12-21 21:23:06 -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",
|
||||
"romans": "^2.0",
|
||||
"slugify": "^1.6",
|
||||
"svg-path-parser": "^1.1"
|
||||
"svg-path-parser": "^1.1",
|
||||
"use-resize-observer": "^9.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@changesets/changelog-github": "^0.5",
|
||||
"@changesets/cli": "^2.27",
|
||||
"@figma/eslint-plugin-figma-plugins": "^0.15",
|
||||
"@figma/plugin-typings": "^1.92",
|
||||
"@figma/plugin-typings": "^1.93",
|
||||
"@preact/preset-vite": "^2.8",
|
||||
"@trivago/prettier-plugin-sort-imports": "^4.3",
|
||||
"@types/svg-path-parser": "^1.1",
|
||||
"@typescript-eslint/eslint-plugin": "^7.8",
|
||||
"@typescript-eslint/parser": "^7.8",
|
||||
"@vitejs/plugin-react-swc": "^3.6",
|
||||
"concurrently": "^8.2",
|
||||
"esbuild": "^0.20",
|
||||
"esbuild": "^0.21",
|
||||
"eslint": "^8.57",
|
||||
"eslint-config-prettier": "^9.1",
|
||||
"eslint-plugin-prettier": "^5.1",
|
||||
"eslint-plugin-react": "^7.34",
|
||||
"prettier": "^3.2",
|
||||
"stylelint": "^16.4",
|
||||
"stylelint": "^16.5",
|
||||
"stylelint-config-standard": "^36.0",
|
||||
"typescript": "^5.4",
|
||||
"vite": "^5.2",
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import { findAllTextNodes } from './findAllTextnodes';
|
||||
import { handleExportMessage } from './handleExportMessage';
|
||||
import { BASE_WIDTH, LOADING_HEIGHT } from './pluginSizes';
|
||||
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 => {
|
||||
if (message.type === 'ready') {
|
||||
|
@ -21,6 +23,10 @@ figma.ui.onmessage = message => {
|
|||
if (message.type === 'reload') {
|
||||
findAllTextNodes();
|
||||
}
|
||||
|
||||
if (message.type === 'resize') {
|
||||
figma.ui.resize(BASE_WIDTH, message.height);
|
||||
}
|
||||
};
|
||||
|
||||
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 { isGoogleFont } from './translators/text/font/gfonts';
|
||||
import { isLocalFont } from './translators/text/font/local';
|
||||
|
@ -34,10 +28,5 @@ export const findAllTextNodes = async () => {
|
|||
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);
|
||||
};
|
||||
|
|
|
@ -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 = () => {
|
||||
figma.ui.postMessage({ type: 'CHANGES_DETECTED' });
|
||||
figma.ui.resize(BASE_WIDTH, NORMAL_HEIGHT + NEEDS_RELOAD_TEXT_HEIGHT);
|
||||
};
|
||||
|
|
|
@ -1,21 +1,39 @@
|
|||
import { useEffect } from 'react';
|
||||
import useResizeObserver from 'use-resize-observer';
|
||||
|
||||
import Penpot from '@ui/assets/penpot.svg?react';
|
||||
import { PenpotExporter } from '@ui/components/PenpotExporter';
|
||||
import { Stack } from '@ui/components/Stack';
|
||||
|
||||
import { Wrapper } from './components/Wrapper/Wrapper';
|
||||
|
||||
export const App = () => (
|
||||
<Wrapper>
|
||||
<Stack space="medium">
|
||||
<Penpot
|
||||
style={{
|
||||
alignSelf: 'center',
|
||||
height: 'auto',
|
||||
width: '8.125rem',
|
||||
fill: 'var(--figma-color-icon)'
|
||||
}}
|
||||
/>
|
||||
<PenpotExporter />
|
||||
</Stack>
|
||||
</Wrapper>
|
||||
);
|
||||
// Safe default value to avoid overflowing from the screen
|
||||
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">
|
||||
<Penpot
|
||||
style={{
|
||||
alignSelf: 'center',
|
||||
height: 'auto',
|
||||
width: '8.125rem',
|
||||
fill: 'var(--figma-color-icon)'
|
||||
}}
|
||||
/>
|
||||
<PenpotExporter />
|
||||
</Stack>
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -6,12 +6,12 @@
|
|||
flex-direction: var(--direction);
|
||||
gap: var(--spacing);
|
||||
|
||||
& > * {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.stack-row {
|
||||
--direction: row;
|
||||
|
||||
& > * {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.stack-small {
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
.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';
|
||||
|
||||
type WrapperProps = PropsWithChildren & {
|
||||
style?: CSSProperties;
|
||||
overflowing?: boolean;
|
||||
};
|
||||
|
||||
export const Wrapper = ({ style, children }: WrapperProps) => {
|
||||
return (
|
||||
<div className={styles.wrapper} style={style}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
const Wrapper = forwardRef<HTMLDivElement, WrapperProps>(
|
||||
({ style, overflowing = false, children }: WrapperProps, ref) => {
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={classNames({ [styles.wrapper]: true, [styles.wrapperOverflow]: overflowing })}
|
||||
style={style}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
Wrapper.displayName = 'Wrapper';
|
||||
|
||||
export { Wrapper };
|
||||
|
|
|
@ -1,40 +1,3 @@
|
|||
*,
|
||||
*::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;
|
||||
body :not:has(.wrapper-overflow) {
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import { createRoot } from 'react-dom/client';
|
|||
|
||||
import { App } from './App';
|
||||
import './main.css';
|
||||
import './reset.css';
|
||||
|
||||
createRoot(document.getElementById('root') as HTMLElement).render(
|
||||
<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