From 2300ce23ac74155bac89a7b4031439f0068a43b0 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Mon, 22 Apr 2024 16:02:06 +0200 Subject: [PATCH] feat: create rectangles and modify text --- .../src/app/app.element.css | 8 ++ .../src/app/app.element.ts | 81 +++++++++++++++---- apps/poc-state-read-plugin/src/plugin.ts | 31 +++++++ libs/plugin-types/index.d.ts | 52 ++++++++++-- libs/plugins-runtime/src/index.ts | 7 +- libs/plugins-runtime/src/lib/api/index.ts | 29 +++++-- libs/plugins-runtime/src/lib/global.d.ts | 9 --- libs/plugins-runtime/src/lib/load-plugin.ts | 2 + 8 files changed, 179 insertions(+), 40 deletions(-) diff --git a/apps/poc-state-read-plugin/src/app/app.element.css b/apps/poc-state-read-plugin/src/app/app.element.css index f1149c8..eb53ba9 100644 --- a/apps/poc-state-read-plugin/src/app/app.element.css +++ b/apps/poc-state-read-plugin/src/app/app.element.css @@ -77,3 +77,11 @@ h1 { .name-wrap input { flex: 1; } + +.actions-wrap { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + gap: 1rem; + margin-bottom: 1rem; +} diff --git a/apps/poc-state-read-plugin/src/app/app.element.ts b/apps/poc-state-read-plugin/src/app/app.element.ts index 41e77f3..3c9d2c8 100644 --- a/apps/poc-state-read-plugin/src/app/app.element.ts +++ b/apps/poc-state-read-plugin/src/app/app.element.ts @@ -11,6 +11,12 @@ export class AppElement extends HTMLElement { #nameSubmit = null; #nameInput = null; + #createRectBtn = null; + #moveXBtn = null; + #moveYBtn = null; + #resizeWBtn = null; + #resizeHBtn = null; + #loremIpsumBtn = null; refreshPage(pageId: string, name: string) { const projectName = document.getElementById('project-name'); @@ -32,25 +38,43 @@ export class AppElement extends HTMLElement { connectedCallback() { this.innerHTML = ` -
-

Test area!

+
+

Test area!

-

Current project name: Unknown

+

Current project name: Unknown

-
- - - -
-

- -

-
- `; +
+ + + +
+ +
+ + + + + + +
+ +

+ +

+
+ `; this.#nameSubmit = document.getElementById('name-submit'); this.#nameInput = document.getElementById('name-input'); + this.#createRectBtn = document.getElementById('create-rect'); + this.#moveXBtn = document.getElementById('move-x'); + this.#moveYBtn = document.getElementById('move-y'); + this.#resizeWBtn = document.getElementById('resize-w'); + this.#resizeHBtn = document.getElementById('resize-h'); + this.#loremIpsumBtn = document.getElementById('lorem-ipsum'); + console.log(this.#loremIpsumBtn); + window.addEventListener('message', (event) => { if (event.data.type === 'file') { this.#fileId = event.data.content.id; @@ -77,12 +101,41 @@ export class AppElement extends HTMLElement { parent.postMessage({ content: 'ready' }, '*'); - console.log(this.#nameSubmit); this.#nameSubmit?.addEventListener('click', (e) => { const id = this.#selection[0].id; const name = this.#nameInput.value; parent.postMessage({ content: 'change-name', data: { id, name } }, '*'); }); + + this.#createRectBtn?.addEventListener('click', (e) => { + parent.postMessage({ content: 'create-rect' }, '*'); + }); + + this.#moveXBtn?.addEventListener('click', (e) => { + const id = this.#selection[0].id; + parent.postMessage({ content: 'move-x', data: { id } }, '*'); + }); + + this.#moveYBtn?.addEventListener('click', (e) => { + const id = this.#selection[0].id; + parent.postMessage({ content: 'move-y', data: { id } }, '*'); + }); + + this.#resizeWBtn?.addEventListener('click', (e) => { + const id = this.#selection[0].id; + parent.postMessage({ content: 'resize-w', data: { id } }, '*'); + }); + + this.#resizeHBtn?.addEventListener('click', (e) => { + const id = this.#selection[0].id; + parent.postMessage({ content: 'resize-h', data: { id } }, '*'); + }); + + this.#loremIpsumBtn?.addEventListener('click', (e) => { + console.log(">>"); + parent.postMessage({ content: 'lorem-ipsum' }, '*'); + }); + } } customElements.define('app-root', AppElement); diff --git a/apps/poc-state-read-plugin/src/plugin.ts b/apps/poc-state-read-plugin/src/plugin.ts index b8d6247..d542c65 100644 --- a/apps/poc-state-read-plugin/src/plugin.ts +++ b/apps/poc-state-read-plugin/src/plugin.ts @@ -30,6 +30,37 @@ penpot.ui.onMessage<{ content: string; data: unknown }>((message) => { if (shape) { shape.name = (message.data as {name: string}).name; } + } else if (message.content === 'create-rect') { + const shape = penpot.createRectangle(); + penpot.log(shape); + } else if (message.content === 'move-x') { + const shape = penpot.getPage()?.getShapeById('' + (message.data as {id: string}).id); + if (shape) { + shape.x += 100; + } + } else if (message.content === 'move-y') { + const shape = penpot.getPage()?.getShapeById('' + (message.data as {id: string}).id); + if (shape) { + shape.y += 100; + } + } else if (message.content === 'resize-w') { + const shape = penpot.getPage()?.getShapeById('' + (message.data as {id: string}).id); + if (shape) { + shape.resize(shape.width * 2, shape.height); + } + } else if (message.content === 'resize-h') { + const shape = penpot.getPage()?.getShapeById('' + (message.data as {id: string}).id); + if (shape) { + shape.resize(shape.width, shape.height * 2); + } + } else if (message.content === 'lorem-ipsum') { + const selection = penpot.selection; + + for (const shape of selection) { + shape.characters = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam id mauris ut felis finibus congue. Ut odio ipsum, condimentum id tellus sit amet, dapibus sagittis ligula. Pellentesque hendrerit, nulla sit amet aliquet scelerisque, orci nunc commodo tellus, quis hendrerit nisl massa non tellus. + +Phasellus fringilla tortor elit, ac dictum tellus posuere sodales. Ut eget imperdiet ante. Nunc eros magna, tincidunt non finibus in, tempor elementum nunc. Sed commodo magna in arcu aliquam efficitur.`; + } } }); diff --git a/libs/plugin-types/index.d.ts b/libs/plugin-types/index.d.ts index ec9364d..f9ee353 100644 --- a/libs/plugin-types/index.d.ts +++ b/libs/plugin-types/index.d.ts @@ -19,7 +19,20 @@ export interface PenpotFill { export interface PenpotShape { id: string; name: string; - fills: PenpotFill[] + type: 'frame' | 'group' | 'bool' | 'rect' | 'path' | 'text' | 'circle' | 'svg-raw' | 'image'; + x: number; + y: number; + width: number; + height: number; + children: PenpotShape[]; + fills: PenpotFill[]; + strokes: PenpotStroke[]; + resize(width: number, height: number); +} + +export interface PenpotText extends PenpotShape { + type: 'text'; + characters: string; } export interface EventsMap { @@ -31,6 +44,23 @@ export interface EventsMap { export type PenpotTheme = 'light' | 'dark'; +export interface PenpotContext { + root: PenpotShape; + currentPage: PenpotPage; + selection: PenpotShape[]; + + // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents + getFile(): PenpotFile | null; + + // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents + getPage(): PenpotPage | null; + getSelected(): string[]; + getSelectedShapes(): PenpotShape[]; + getTheme(): PenpotTheme; + + createRectangle(): PenpotShape; +} + export interface Penpot { ui: { open: ( @@ -52,13 +82,21 @@ export interface Penpot { type: T, callback: (event: EventsMap[T]) => void ) => void; - getFile: () => PenpotFile | null; - getPage: () => PenpotPage | null; - getCurrentPage: () => PenpotPage | null; - getSelected: () => string[]; - getSelectedShapes(): PenpotShape[]; - getTheme: () => PenpotTheme; + fetch: typeof fetch; + + // Exposes Penpot Context + root: PenpotShape; + currentPage: PenpotPage; + selection: PenpotShape[]; + + getFile(): PenpotFile | null; + getPage(): PenpotPage | null; + getSelected(): string[]; + getSelectedShapes(): PenpotShape[]; + getTheme(): PenpotTheme; + + createRectangle(): PenpotShape; } declare global { diff --git a/libs/plugins-runtime/src/index.ts b/libs/plugins-runtime/src/index.ts index ad2dc63..cc5b6fd 100644 --- a/libs/plugins-runtime/src/index.ts +++ b/libs/plugins-runtime/src/index.ts @@ -5,23 +5,22 @@ import { initInstaller } from './lib/installer'; import { ɵloadPlugin, setContext } from './lib/load-plugin'; import * as api from './lib/api'; -console.log('Loading plugin system'); +console.log('%c[PLUGINS] Loading plugin system', 'color: #008d7c'); repairIntrinsics({ evalTaming: 'unsafeEval', consoleTaming: import.meta.env.MODE === 'development' ? 'unsafe' : 'safe', }); -// eslint-disable-next-line @typescript-eslint/no-explicit-any globalThis.initPluginsRuntime = (context: PenpotContext) => { if (context) { - console.log('Initialize context'); + console.log('%c[PLUGINS] Initialize context', 'color: #008d7c'); + /* eslint-disable */ globalThis.ɵcontext = context; globalThis.ɵloadPlugin = ɵloadPlugin; initInstaller(); - /* eslint-disable */ setContext(context); for (const event of api.validEvents) { diff --git a/libs/plugins-runtime/src/lib/api/index.ts b/libs/plugins-runtime/src/lib/api/index.ts index b5e0f2a..b77c3f6 100644 --- a/libs/plugins-runtime/src/lib/api/index.ts +++ b/libs/plugins-runtime/src/lib/api/index.ts @@ -1,4 +1,4 @@ -import type { Penpot, EventsMap } from '@penpot/plugin-types'; +import type { PenpotContext, Penpot, EventsMap, PenpotPage, PenpotShape } from '@penpot/plugin-types'; import { Manifest, Permissions } from '../models/manifest.model'; import { OpenUIOptions } from '../models/open-ui-options.model'; @@ -128,16 +128,27 @@ export function createApi(context: PenpotContext, manifest: Manifest) { }, // Penpot State API + + get root(): PenpotShape { + checkPermission('page:read'); + return context.root; + }, + + get currentPage(): PenpotPage { + checkPermission('page:read'); + return context.currentPage; + }, + + get selection(): PenpotShape[] { + checkPermission('selection:read'); + return context.selection; + }, + getFile(): PenpotFile { checkPermission('file:read'); return context.getFile(); }, - getCurrentPage(): PenpotPage { - checkPermission('page:read'); - return context.getCurrentPage(); - }, - getPage(): PenpotPage { checkPermission('page:read'); return context.getPage(); @@ -157,8 +168,14 @@ export function createApi(context: PenpotContext, manifest: Manifest) { return context.getTheme(); }, + createRectangle(): PenpotShape { + // checkPermission('page:write'); + return context.createRectangle(); + }, + fetch, }; return penpot; } + diff --git a/libs/plugins-runtime/src/lib/global.d.ts b/libs/plugins-runtime/src/lib/global.d.ts index b6970d2..ae9a748 100644 --- a/libs/plugins-runtime/src/lib/global.d.ts +++ b/libs/plugins-runtime/src/lib/global.d.ts @@ -2,13 +2,4 @@ export declare global { declare namespace globalThis { function ɵloadPlugin(cofig: PluginConfig): Promise; } - - interface PenpotContext { - getFile(): PenpotFile; - getCurrentPage(): PenpotPage; - getPage(): PenpotPage; - getSelected(): string[]; - getSelectedShapes(): PenpotShape[]; - getTheme(): PenpotTheme; - } } diff --git a/libs/plugins-runtime/src/lib/load-plugin.ts b/libs/plugins-runtime/src/lib/load-plugin.ts index b9670bb..307cb9f 100644 --- a/libs/plugins-runtime/src/lib/load-plugin.ts +++ b/libs/plugins-runtime/src/lib/load-plugin.ts @@ -1,3 +1,5 @@ +import type { PenpotContext } from '@penpot/plugin-types'; + import { PluginConfig } from './models/plugin-config.model'; import { createApi } from './api'; import { parseManifest } from './parse-manifest';