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:
parent
cd6f1e9f02
commit
2300ce23ac
8 changed files with 179 additions and 40 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.`;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
52
libs/plugin-types/index.d.ts
vendored
52
libs/plugin-types/index.d.ts
vendored
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
9
libs/plugins-runtime/src/lib/global.d.ts
vendored
9
libs/plugins-runtime/src/lib/global.d.ts
vendored
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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';
|
||||
|
|
Loading…
Reference in a new issue