0
Fork 0
mirror of https://github.com/penpot/penpot-plugins.git synced 2025-01-06 14:50:21 -05:00

feat: create rectangles and modify text

This commit is contained in:
alonso.torres 2024-04-22 16:02:06 +02:00 committed by Alonso Torres
parent cd6f1e9f02
commit 2300ce23ac
8 changed files with 179 additions and 40 deletions

View file

@ -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;
}

View file

@ -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 = `
<div class="wrapper">
<h1>Test area!</h1>
<div class="wrapper">
<h1>Test area!</h1>
<p>Current project name: <span id="project-name">Unknown</span></p>
<p>Current project name: <span id="project-name">Unknown</span></p>
<div class="name-wrap">
<label>Selected Shape: </label>
<input id="name-input" type="text"></input>
<button id="name-submit" type="button">Update</button>
</div>
<p>
<button type="button" data-appearance="primary" data-variant="destructive" class="act-close-plugin">Close plugin</button>
</p>
</div>
`;
<div class="name-wrap">
<label>Selected Shape: </label>
<input id="name-input" type="text"></input>
<button id="name-submit" type="button">Update</button>
</div>
<div class="actions-wrap">
<button id="create-rect" type="button">+Rect</button>
<button id="move-x" type="button">Move X</button>
<button id="move-y" type="button">Move Y</button>
<button id="resize-w" type="button">Resize W</button>
<button id="resize-h" type="button">Resize H</button>
<button id="lorem-ipsum" type="button">Lorem Ipsum</button>
</div>
<p>
<button type="button" data-appearance="primary" data-variant="destructive" class="act-close-plugin">Close plugin</button>
</p>
</div>
`;
this.#nameSubmit = document.getElementById<HTMLElement>('name-submit');
this.#nameInput = document.getElementById<HTMLElement>('name-input');
this.#createRectBtn = document.getElementById<HTMLElement>('create-rect');
this.#moveXBtn = document.getElementById<HTMLElement>('move-x');
this.#moveYBtn = document.getElementById<HTMLElement>('move-y');
this.#resizeWBtn = document.getElementById<HTMLElement>('resize-w');
this.#resizeHBtn = document.getElementById<HTMLElement>('resize-h');
this.#loremIpsumBtn = document.getElementById<HTMLElement>('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);

View file

@ -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.`;
}
}
});

View file

@ -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 {

View file

@ -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) {

View file

@ -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;
}

View file

@ -2,13 +2,4 @@ export declare global {
declare namespace globalThis {
function ɵloadPlugin(cofig: PluginConfig): Promise<void>;
}
interface PenpotContext {
getFile(): PenpotFile;
getCurrentPage(): PenpotPage;
getPage(): PenpotPage;
getSelected(): string[];
getSelectedShapes(): PenpotShape[];
getTheme(): PenpotTheme;
}
}

View file

@ -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';