mirror of
https://github.com/penpot/penpot-plugins.git
synced 2025-01-21 06:02:34 -05:00
feat: update design system css
This commit is contained in:
parent
e3252387bd
commit
385c5bfa09
7 changed files with 151 additions and 57 deletions
|
@ -1,4 +1,4 @@
|
|||
import { Component, signal } from '@angular/core';
|
||||
import { Component, effect, signal } from '@angular/core';
|
||||
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||
import type { PenpotShape } from '@penpot/plugin-types';
|
||||
|
||||
|
@ -7,7 +7,7 @@ import type { PenpotShape } from '@penpot/plugin-types';
|
|||
selector: 'app-root',
|
||||
imports: [ReactiveFormsModule],
|
||||
template: `
|
||||
<div class="wrapper" [attr.data-theme]="theme()">
|
||||
<div class="wrapper">
|
||||
<h1>Test area!</h1>
|
||||
|
||||
<p>
|
||||
|
@ -17,20 +17,48 @@ import type { PenpotShape } from '@penpot/plugin-types';
|
|||
<form [formGroup]="form" (ngSubmit)="updateName()">
|
||||
<div class="name-wrap">
|
||||
<label>Selected Shape: </label>
|
||||
<input type="text" formControlName="name" />
|
||||
<input class="input" type="text" formControlName="name" />
|
||||
<button type="submit">Update</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="actions-wrap">
|
||||
<button type="button" (click)="createRect()">+Rect</button>
|
||||
<button type="button" (click)="moveX()">Move X</button>
|
||||
<button type="button" (click)="moveY()">Move Y</button>
|
||||
<button type="button" (click)="resizeW()">Resize W</button>
|
||||
<button type="button" (click)="resizeH()">Resize H</button>
|
||||
<button type="button" (click)="loremIpsum()">Lorem Ipsum</button>
|
||||
<button type="button" (click)="addIcon()">+ Icon</button>
|
||||
<button type="button" (click)="createGrid()">+ Grid</button>
|
||||
<button
|
||||
type="button"
|
||||
data-appearance="secondary"
|
||||
(click)="createRect()"
|
||||
>
|
||||
+Rect
|
||||
</button>
|
||||
<button type="button" data-appearance="secondary" (click)="moveX()">
|
||||
Move X
|
||||
</button>
|
||||
<button type="button" data-appearance="secondary" (click)="moveY()">
|
||||
Move Y
|
||||
</button>
|
||||
<button type="button" data-appearance="secondary" (click)="resizeW()">
|
||||
Resize W
|
||||
</button>
|
||||
<button type="button" data-appearance="secondary" (click)="resizeH()">
|
||||
Resize H
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
data-appearance="secondary"
|
||||
(click)="loremIpsum()"
|
||||
>
|
||||
Lorem Ipsum
|
||||
</button>
|
||||
<button type="button" data-appearance="secondary" (click)="addIcon()">
|
||||
+ Icon
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
data-appearance="secondary"
|
||||
(click)="createGrid()"
|
||||
>
|
||||
+ Grid
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
|
@ -83,6 +111,10 @@ export class AppComponent {
|
|||
});
|
||||
|
||||
this.#sendMessage({ content: 'ready' });
|
||||
|
||||
effect(() => {
|
||||
document.body.setAttribute('data-theme', this.theme());
|
||||
});
|
||||
}
|
||||
|
||||
close() {
|
||||
|
|
|
@ -15,9 +15,9 @@ import type {
|
|||
|
||||
import { Manifest, Permissions } from '../models/manifest.model.js';
|
||||
import { OpenUIOptions } from '../models/open-ui-options.model.js';
|
||||
import { setModalTheme } from '../create-modal.js';
|
||||
import openUIApi from './openUI.api.js';
|
||||
import z from 'zod';
|
||||
import type { PluginModalElement } from '../plugin-modal.js';
|
||||
|
||||
type Callback<T> = (message: T) => void;
|
||||
|
||||
|
@ -30,7 +30,7 @@ export const validEvents = [
|
|||
|
||||
export let uiMessagesCallbacks: Callback<unknown>[] = [];
|
||||
|
||||
let modal: HTMLElement | null = null;
|
||||
let modal: PluginModalElement | null = null;
|
||||
|
||||
const eventListeners: Map<string, Callback<unknown>[]> = new Map();
|
||||
|
||||
|
@ -45,7 +45,7 @@ export function triggerEvent(
|
|||
message: EventsMap[keyof EventsMap]
|
||||
) {
|
||||
if (type === 'themechange' && modal) {
|
||||
setModalTheme(modal, message);
|
||||
modal.setTheme(message);
|
||||
}
|
||||
const listeners = eventListeners.get(type) || [];
|
||||
listeners.forEach((listener) => listener(message));
|
||||
|
@ -72,7 +72,7 @@ export function createApi(context: PenpotContext, manifest: Manifest): Penpot {
|
|||
open: (name: string, url: string, options: OpenUIOptions) => {
|
||||
const theme = context.getTheme() as 'light' | 'dark';
|
||||
modal = openUIApi(name, url, theme, options);
|
||||
setModalTheme(modal, theme);
|
||||
modal.setTheme(theme);
|
||||
|
||||
modal.addEventListener('close', closePlugin, {
|
||||
once: true,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import z from 'zod';
|
||||
import { openUISchema } from '../models/open-ui-options.schema';
|
||||
import { createModal } from '../create-modal';
|
||||
import { openUISchema } from '../models/open-ui-options.schema.js';
|
||||
import { createModal } from '../create-modal.js';
|
||||
|
||||
export default z
|
||||
.function()
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
import { expect, describe, vi } from 'vitest';
|
||||
import {
|
||||
createApi,
|
||||
triggerEvent,
|
||||
uiMessagesCallbacks,
|
||||
} from './index.js';
|
||||
import { createApi, triggerEvent, uiMessagesCallbacks } from './index.js';
|
||||
import openUIApi from './openUI.api.js';
|
||||
import { FileState } from '@penpot/plugin-types';
|
||||
|
||||
|
@ -15,6 +11,7 @@ vi.mock('./openUI.api', () => {
|
|||
removeEventListener: vi.fn(),
|
||||
remove: vi.fn(),
|
||||
setAttribute: vi.fn(),
|
||||
setTheme: vi.fn(),
|
||||
})),
|
||||
};
|
||||
});
|
||||
|
@ -256,9 +253,11 @@ describe('Plugin api', () => {
|
|||
api.ui.open(name, url, options);
|
||||
|
||||
const modalMock = openUIApiMock.mock.results[0].value;
|
||||
expect(modalMock.setAttribute).toHaveBeenCalledWith('data-theme', 'light');
|
||||
expect(modalMock.setAttribute).toHaveBeenCalledTimes(1);
|
||||
expect(modalMock.setTheme).toHaveBeenCalledWith('light');
|
||||
expect(api.getTheme()).toBe('light');
|
||||
|
||||
triggerEvent('themechange', 'dark' as any);
|
||||
expect(modalMock.setTheme).toHaveBeenCalledWith('dark');
|
||||
});
|
||||
|
||||
it('close puglin', () => {
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
import { OpenUIOptions } from './models/open-ui-options.model';
|
||||
|
||||
import type { OpenUIOptions } from './models/open-ui-options.model.js';
|
||||
import type { PenpotTheme } from '@penpot/plugin-types';
|
||||
|
||||
export function setModalTheme(modal: HTMLElement, theme: PenpotTheme) {
|
||||
modal.setAttribute('data-theme', theme);
|
||||
}
|
||||
import type { PluginModalElement } from './plugin-modal.js';
|
||||
|
||||
export function createModal(
|
||||
name: string,
|
||||
|
@ -12,14 +8,14 @@ export function createModal(
|
|||
theme: PenpotTheme,
|
||||
options: OpenUIOptions
|
||||
) {
|
||||
const modal = document.createElement('plugin-modal');
|
||||
const modal = document.createElement('plugin-modal') as PluginModalElement;
|
||||
|
||||
setModalTheme(modal, theme);
|
||||
modal.setTheme(theme);
|
||||
|
||||
modal.setAttribute('title', name);
|
||||
modal.setAttribute('iframe-src', url);
|
||||
modal.setAttribute('width', String(options.width || 300));
|
||||
modal.setAttribute('height', String(options.height || 400));
|
||||
modal.setAttribute('width', String(options.width || 285));
|
||||
modal.setAttribute('height', String(options.height || 540));
|
||||
|
||||
document.body.appendChild(modal);
|
||||
|
||||
|
|
|
@ -1,12 +1,22 @@
|
|||
const closeSvg = `
|
||||
<svg width="16" height="16"xmlns="http://www.w3.org/2000/svg" fill="none"><g class="fills"><rect rx="0" ry="0" width="16" height="16" class="frame-background"/></g><g class="frame-children"><path d="M11.997 3.997 8 8l-3.997 4.003m-.006-8L8 8l4.003 3.997" class="fills"/><g class="strokes"><path d="M11.997 3.997 8 8l-3.997 4.003m-.006-8L8 8l4.003 3.997" style="fill: none; stroke-width: 1; stroke: rgb(143, 157, 163); stroke-opacity: 1; stroke-linecap: round;" class="stroke-shape"/></g></g></svg>`;
|
||||
|
||||
import type { PenpotTheme } from '@penpot/plugin-types';
|
||||
|
||||
export class PluginModalElement extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.attachShadow({ mode: 'open' });
|
||||
}
|
||||
|
||||
#wrapper: HTMLElement | null = null;
|
||||
|
||||
setTheme(theme: PenpotTheme) {
|
||||
if (this.#wrapper) {
|
||||
this.#wrapper.setAttribute('data-theme', theme);
|
||||
}
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
const title = this.getAttribute('title');
|
||||
const iframeSrc = this.getAttribute('iframe-src');
|
||||
|
@ -21,6 +31,9 @@ export class PluginModalElement extends HTMLElement {
|
|||
throw new Error('Error creating shadow root');
|
||||
}
|
||||
|
||||
this.#wrapper = document.createElement('div');
|
||||
this.#wrapper.classList.add('wrapper');
|
||||
|
||||
const header = document.createElement('div');
|
||||
header.classList.add('header');
|
||||
|
||||
|
@ -67,52 +80,75 @@ export class PluginModalElement extends HTMLElement {
|
|||
iframe.contentWindow.postMessage((e as CustomEvent).detail, '*');
|
||||
});
|
||||
|
||||
this.shadowRoot.appendChild(header);
|
||||
this.shadowRoot.appendChild(iframe);
|
||||
this.shadowRoot.appendChild(this.#wrapper);
|
||||
|
||||
this.#wrapper.appendChild(header);
|
||||
this.#wrapper.appendChild(iframe);
|
||||
|
||||
const style = document.createElement('style');
|
||||
style.textContent = `
|
||||
:host {
|
||||
--spacing-4: 0.25rem;
|
||||
--spacing-8: calc(var(--spacing-4) * 2);
|
||||
--spacing-12: calc(var(--spacing-4) * 3);
|
||||
--spacing-16: calc(var(--spacing-4) * 4);
|
||||
--spacing-20: calc(var(--spacing-4) * 5);
|
||||
--spacing-24: calc(var(--spacing-4) * 6);
|
||||
--spacing-28: calc(var(--spacing-4) * 7);
|
||||
--spacing-32: calc(var(--spacing-4) * 8);
|
||||
--spacing-36: calc(var(--spacing-4) * 9);
|
||||
--spacing-40: calc(var(--spacing-4) * 10);
|
||||
|
||||
--font-weight-regular: 400;
|
||||
--font-weight-bold: 500;
|
||||
--font-line-height-s: 1.2;
|
||||
--font-line-height-m: 1.4;
|
||||
--font-line-height-l: 1.5;
|
||||
--font-size-s: 12px;
|
||||
--font-size-m: 14px;
|
||||
--font-size-l: 16px;
|
||||
}
|
||||
|
||||
[data-theme] {
|
||||
background-color: var(--color-background-primary);
|
||||
color: var(--color-foreground-secondary);
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: fixed;
|
||||
inset-block-end: 10px;
|
||||
inset-inline-start: 10px;
|
||||
z-index: 1000;
|
||||
padding: 20px;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
||||
padding: 25px;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.3);
|
||||
inline-size: ${width}px;
|
||||
block-size: ${height}px;
|
||||
}
|
||||
|
||||
:host([data-theme="dark"]) {
|
||||
background: #2e3434;
|
||||
border: 1px solid #2e3434;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
:host([data-theme="light"]) {
|
||||
background: #ffffff;
|
||||
border: 1px solid #eef0f2;
|
||||
color: #18181a;
|
||||
}
|
||||
|
||||
.header {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
border-block-end: 2px solid var(--color-background-quaternary);
|
||||
padding-block-end: var(--spacing-4);
|
||||
margin-block-end: var(--spacing-20);
|
||||
}
|
||||
|
||||
button {
|
||||
background: transparent;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: var(--font-size-s);
|
||||
font-weight: var(--font-weight-bold);
|
||||
margin: 0;
|
||||
margin-block-end: 10px;
|
||||
margin-inline-end: var(--spacing-4);
|
||||
}
|
||||
|
||||
iframe {
|
||||
|
|
|
@ -33,11 +33,42 @@ ul {
|
|||
}
|
||||
|
||||
[data-theme='dark'] {
|
||||
background-color: var(--db-quaternary);
|
||||
color: var(--lb-primary);
|
||||
color-scheme: dark;
|
||||
|
||||
--background-primary: var(--db-primary);
|
||||
--background-secondary: var(--db-secondary);
|
||||
--background-tertiary: var(--db-tertiary);
|
||||
--background-quaternary: var(--db-quaternary);
|
||||
|
||||
--foreground-primary: var(--df-primary);
|
||||
--foreground-secondary: var(--df-secondary);
|
||||
|
||||
--accent-primary: var(--da-primary);
|
||||
--accent-primary-muted: var(--da-primary-muted);
|
||||
--accent-secondary: var(--da-secondary);
|
||||
--accent-tertiary: var(--da-tertiary);
|
||||
--accent-quaternary: var(--da-quaternary);
|
||||
}
|
||||
|
||||
[data-theme='light'] {
|
||||
background-color: var(--lb-primary);
|
||||
color: var(--db-primary);
|
||||
color-scheme: light;
|
||||
|
||||
--background-primary: var(--lb-primary);
|
||||
--background-secondary: var(--lb-secondary);
|
||||
--background-tertiary: var(--lb-tertiary);
|
||||
--background-quaternary: var(--lb-quaternary);
|
||||
|
||||
--foreground-primary: var(--lf-primary);
|
||||
--foreground-secondary: var(--lf-secondary);
|
||||
|
||||
--accent-primary: var(--la-primary);
|
||||
--accent-primary-muted: var(--la-primary-muted);
|
||||
--accent-secondary: var(--la-secondary);
|
||||
--accent-tertiary: var(--la-tertiary);
|
||||
--accent-quaternary: var(--la-quaternary);
|
||||
}
|
||||
|
||||
[data-theme] {
|
||||
background-color: var(--background-primary);
|
||||
color: var(--foreground-secondary);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue