From 385c5bfa09387e6e33120e86ac4f368c1cf1a5fc Mon Sep 17 00:00:00 2001 From: Juanfran Date: Thu, 9 May 2024 14:45:15 +0200 Subject: [PATCH] feat: update design system css --- .../poc-state-plugin/src/app/app.component.ts | 54 +++++++++++--- libs/plugins-runtime/src/lib/api/index.ts | 8 +- .../plugins-runtime/src/lib/api/openUI.api.ts | 4 +- .../src/lib/api/plugin-api.spec.ts | 13 ++-- libs/plugins-runtime/src/lib/create-modal.ts | 16 ++-- libs/plugins-runtime/src/lib/plugin-modal.ts | 74 ++++++++++++++----- libs/plugins-styles/src/lib/core/generic.css | 39 +++++++++- 7 files changed, 151 insertions(+), 57 deletions(-) diff --git a/apps/poc-state-plugin/src/app/app.component.ts b/apps/poc-state-plugin/src/app/app.component.ts index f5bda0c..94aa922 100644 --- a/apps/poc-state-plugin/src/app/app.component.ts +++ b/apps/poc-state-plugin/src/app/app.component.ts @@ -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: ` -
+

Test area!

@@ -17,20 +17,48 @@ import type { PenpotShape } from '@penpot/plugin-types';

- +
- - - - - - - - + + + + + + + +

@@ -83,6 +111,10 @@ export class AppComponent { }); this.#sendMessage({ content: 'ready' }); + + effect(() => { + document.body.setAttribute('data-theme', this.theme()); + }); } close() { diff --git a/libs/plugins-runtime/src/lib/api/index.ts b/libs/plugins-runtime/src/lib/api/index.ts index 6625149..0b55b62 100644 --- a/libs/plugins-runtime/src/lib/api/index.ts +++ b/libs/plugins-runtime/src/lib/api/index.ts @@ -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 = (message: T) => void; @@ -30,7 +30,7 @@ export const validEvents = [ export let uiMessagesCallbacks: Callback[] = []; -let modal: HTMLElement | null = null; +let modal: PluginModalElement | null = null; const eventListeners: Map[]> = 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, diff --git a/libs/plugins-runtime/src/lib/api/openUI.api.ts b/libs/plugins-runtime/src/lib/api/openUI.api.ts index d170acb..35a28f1 100644 --- a/libs/plugins-runtime/src/lib/api/openUI.api.ts +++ b/libs/plugins-runtime/src/lib/api/openUI.api.ts @@ -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() diff --git a/libs/plugins-runtime/src/lib/api/plugin-api.spec.ts b/libs/plugins-runtime/src/lib/api/plugin-api.spec.ts index b01a3e9..39fa139 100644 --- a/libs/plugins-runtime/src/lib/api/plugin-api.spec.ts +++ b/libs/plugins-runtime/src/lib/api/plugin-api.spec.ts @@ -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', () => { diff --git a/libs/plugins-runtime/src/lib/create-modal.ts b/libs/plugins-runtime/src/lib/create-modal.ts index a4396e0..84eac04 100644 --- a/libs/plugins-runtime/src/lib/create-modal.ts +++ b/libs/plugins-runtime/src/lib/create-modal.ts @@ -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); diff --git a/libs/plugins-runtime/src/lib/plugin-modal.ts b/libs/plugins-runtime/src/lib/plugin-modal.ts index b764bc8..17e4a3f 100644 --- a/libs/plugins-runtime/src/lib/plugin-modal.ts +++ b/libs/plugins-runtime/src/lib/plugin-modal.ts @@ -1,12 +1,22 @@ const closeSvg = ` `; +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 { diff --git a/libs/plugins-styles/src/lib/core/generic.css b/libs/plugins-styles/src/lib/core/generic.css index 9d885ee..5aca3e4 100644 --- a/libs/plugins-styles/src/lib/core/generic.css +++ b/libs/plugins-styles/src/lib/core/generic.css @@ -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); }