Skip to content

Commit

Permalink
Figma plugin testing
Browse files Browse the repository at this point in the history
  • Loading branch information
Thunear committed Jun 10, 2024
1 parent 25ffe3f commit 1a3f169
Show file tree
Hide file tree
Showing 10 changed files with 182 additions and 540 deletions.
4 changes: 2 additions & 2 deletions plugins/figma-sync/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"type": "module",
"scripts": {
"dev": "run-s watch",
"watch": "run-p watch:ui watch:plugin",
"watch": "run-p 'watch:*'",
"watch:ui": "npm run build:ui -- --watch --mode=development",
"watch:plugin": "npm run build:plugin -- --watch --mode=development",
"build": "npm run build:plugin && npm run build:ui",
Expand All @@ -18,7 +18,7 @@
"@digdir/designsystemet-css": "workspace:^",
"@digdir/designsystemet-react": "workspace:^",
"@digdir/designsystemet-theme": "workspace:^",
"npm-run-all": "^4.1.5",
"npm-run-all2": "^6.2.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.23.1"
Expand Down
9 changes: 9 additions & 0 deletions plugins/figma-sync/src/common/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export type JsonInput = {
theme: {
accent: object;
neutral: object;
brand1: object;
brand2: object;
brand3: object;
};
};
63 changes: 24 additions & 39 deletions plugins/figma-sync/src/plugin/plugin.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
figma.showUI(__html__, { width: 600, height: 570 });

figma.showUI(__html__, { width: 600, height: 575 });

function hexToRgb(hex: string) {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
Expand All @@ -20,12 +21,14 @@ function UpdateColor(
modeId: string,
variable: Variable,
) {
const number = variable.name.split('/')[2];
const suffix = variable.name.split('/')[2];
if (variable.name.startsWith(`theme/${type}`)) {
for (const [key, value] of Object.entries(obj)) {
if (key === number) {
if (key === suffix) {
const rgb = hexToRgb(value['value'] as string);
variable.setValueForMode(modeId, rgb || { r: 0.2, g: 0.4, b: 0.6 });
if (rgb) {
variable.setValueForMode(modeId, rgb);
}
}
}
}
Expand All @@ -39,50 +42,32 @@ function getModeIndex(mode: string) {
}
}

type OBJ = {
theme: {
accent: object;
neutral: object;
brand1: object;
brand2: object;
brand3: object;
};
};

figma.ui.onmessage = (msg: { type: string; text: string; mode: string }) => {
// One way of distinguishing between different types of messages sent from
// your HTML page is to use an object with a "type" property like this.
if (msg.type === 'update-variables') {
const nodes: SceneNode[] = [];
const obj: OBJ = JSON.parse(msg.text);
const accent = obj['theme']['accent'];
const neutral = obj['theme']['neutral'];
const brand1 = obj['theme']['brand1'];
const brand2 = obj['theme']['brand2'];
const brand3 = obj['theme']['brand3'];
const obj = JSON.parse(msg.text);
const accent = obj['theme']['accent'] as object;
const neutral = obj['theme']['neutral'] as object;
const brand1 = obj['theme']['brand1'] as object;
const brand2 = obj['theme']['brand2'] as object;
const brand3 = obj['theme']['brand3'] as object;

figma.variables
.getVariableCollectionByIdAsync('VariableCollectionId:34811:5472')
.then((collection) => {
if (collection) {
figma.variables.getLocalVariableCollectionsAsync().then((collections) => {
for (const collection of collections) {
if (collection.name === 'Mode') {
figma.variables.getLocalVariablesAsync('COLOR').then((variables) => {
const modeId: string =
collection.modes[getModeIndex(msg.mode)].modeId;

for (let i = 0; i < variables.length; i++) {
UpdateColor('accent', accent, modeId, variables[i]);
UpdateColor('neutral', neutral, modeId, variables[i]);
UpdateColor('brand1', brand1, modeId, variables[i]);
UpdateColor('brand2', brand2, modeId, variables[i]);
UpdateColor('brand3', brand3, modeId, variables[i]);
for (const variable of variables) {
UpdateColor('accent', accent, modeId, variable);
UpdateColor('neutral', neutral, modeId, variable);
UpdateColor('brand1', brand1, modeId, variable);
UpdateColor('brand2', brand2, modeId, variable);
UpdateColor('brand3', brand3, modeId, variable);
}
});
} else {
console.error('Variable collection is null.');
}
});

figma.currentPage.selection = nodes;
figma.viewport.scrollAndZoomIntoView(nodes);
}
});
}
};
5 changes: 5 additions & 0 deletions plugins/figma-sync/src/ui/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ body {
padding: 0 16px;
}

.button {
position: absolute;
bottom: 65px;
}

.link {
height: 52px;
display: flex;
Expand Down
2 changes: 2 additions & 0 deletions plugins/figma-sync/src/ui/components/Footer/Footer.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
.footer {
position: absolute;
z-index: 10;
background-color: white;
bottom: 0;
left: 0;
right: 0;
Expand Down
24 changes: 24 additions & 0 deletions plugins/figma-sync/src/ui/components/Toast/Toast.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
.toast {
position: fixed;
z-index: 5;
bottom: -20px;
border-radius: 4px;
left: 16px;
right: 16px;
background-color: var(--ds-color-accent-surface-default);
box-shadow: var(--ds-shadow-xs);
height: 58px;
display: flex;
align-items: center;
padding: 0 16px;
gap: 12px;
transition: 0.4s all ease-out;
}

.toastActive {
bottom: 65px;
}

.toastClose {
bottom: -20px;
}
50 changes: 50 additions & 0 deletions plugins/figma-sync/src/ui/components/Toast/Toast.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { useEffect, useState } from 'react';
import './Toast.css';
import { Spinner } from '@digdir/designsystemet-react';
import { CheckmarkIcon } from '@navikt/aksel-icons';
import cl from 'clsx/lite';

export const Toast = () => {
const [success, setSuccess] = useState(false);
const [open, setOpen] = useState(false);
const [close, setClose] = useState(false);

useEffect(() => {
setTimeout(() => {
setOpen(true);
}, 10);
setTimeout(() => {
setSuccess(true);
}, 6000);

setTimeout(() => {
setClose(true);
}, 7500);
}, []);

return (
<div className={cl('toast', open && 'toastActive', close && 'toastClose')}>
{success && (
<>
<div className='icon'>
<CheckmarkIcon
title='a11y-title'
fontSize='2rem'
/>
</div>{' '}
Oppdatering vellykket!
</>
)}
{!success && (
<>
<Spinner
color='accent'
title='loading'
size='sm'
/>
Oppdaterer variabler...
</>
)}
</div>
);
};
69 changes: 39 additions & 30 deletions plugins/figma-sync/src/ui/pages/PageOne/PageOne.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,19 @@ import {
Link,
Spinner,
} from '@digdir/designsystemet-react';
import React, { useEffect, useState } from 'react';
import React, { useState } from 'react';

import { Toast } from '@ui/components/Toast/Toast';

import type { JsonInput } from '../../../common/types';

function PageOne() {
const [jsonText, setJsonText] = useState('');
const [mode, setMode] = useState('light');
const [errorText, setErrorText] = useState('');
const [isLoading, setIsLoading] = useState(false);
const [buttonText, setButtonText] = useState('Oppdater variabler');
useEffect(() => {
window.onmessage = (event: {
data: { pluginMessage: { type: string; message: string } };
}) => {
const { type, message } = event.data.pluginMessage;
if (type === 'create-rectangles') {
console.log(`Figma Says: ${message}`);
}
};
}, []);

const isValidJson = (str: string) => {
const isJsonValid = (str: string) => {
try {
JSON.parse(str);
} catch (e) {
Expand All @@ -36,33 +29,54 @@ function PageOne() {
return true;
};

const isJsonCorrect = (str: string) => {
try {
const obj = JSON.parse(str) as JsonInput;
if (
obj.theme.accent &&
obj.theme.neutral &&
obj.theme.brand1 &&
obj.theme.brand2 &&
obj.theme.brand3
) {
return true;
}
} catch (e) {
return false;
}
return false;
};

const onSubmit = () => {
if (isLoading) {
return;
}

if (jsonText === '') {
setErrorText('Dette feltet er påkrevd.');
return;
}

if (!isValidJson(jsonText)) {
if (!isJsonValid(jsonText) || !isJsonCorrect(jsonText)) {
setErrorText('Ugyldig JSON, prøv å kopier og lim inn på nytt.');
setJsonText('');
return;
}

parent.postMessage(
{ pluginMessage: { type: 'update-variables', text: jsonText, mode } },
'*',
);

setButtonText('Oppdaterer...');
setIsLoading(true);
setErrorText('');
setJsonText('');

setTimeout(() => {
parent.postMessage(
{ pluginMessage: { type: 'update-variables', text: jsonText, mode } },
'*',
);
}, 400);

setTimeout(() => {
setIsLoading(false);
setButtonText('Oppdater variabler');
}, 6000);
}, 7900);
};
return (
<div className='content'>
Expand Down Expand Up @@ -115,16 +129,11 @@ function PageOne() {
<Button
onClick={() => onSubmit()}
size='sm'
className='button'
>
{isLoading && (
<Spinner
color='accent'
title='loading'
size='xs'
/>
)}
{buttonText}
Oppdater variabler
</Button>
{isLoading && <Toast />}
</div>
</div>
);
Expand Down
4 changes: 2 additions & 2 deletions plugins/figma-sync/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ES2022",
"moduleResolution": "Node",
"module": "esnext",
"moduleResolution": "bundler",
"useDefineForClassFields": true,
"skipLibCheck": true,
"esModuleInterop": true,
Expand Down
Loading

0 comments on commit 1a3f169

Please sign in to comment.