diff --git a/apps/poc-state-plugin/src/app/app.component.ts b/apps/poc-state-plugin/src/app/app.component.ts index 94aa922..eb8d82c 100644 --- a/apps/poc-state-plugin/src/app/app.component.ts +++ b/apps/poc-state-plugin/src/app/app.component.ts @@ -59,6 +59,13 @@ import type { PenpotShape } from '@penpot/plugin-types'; > + Grid +

@@ -163,6 +170,10 @@ export class AppComponent { this.#sendMessage({ content: 'create-grid' }); } + createPalette() { + this.#sendMessage({ content: 'create-colors' }); + } + #sendMessage(message: unknown) { parent.postMessage(message, '*'); } diff --git a/apps/poc-state-plugin/src/plugin.ts b/apps/poc-state-plugin/src/plugin.ts index bb2f23d..82638b0 100644 --- a/apps/poc-state-plugin/src/plugin.ts +++ b/apps/poc-state-plugin/src/plugin.ts @@ -158,6 +158,83 @@ Phasellus fringilla tortor elit, ac dictum tellus posuere sodales. Ut eget imper grid.appendChild(text, row + 1, col + 1); } } + } else if (message.content === 'create-colors') { + const frame = penpot.createFrame(); + frame.name = 'Palette'; + + const viewport = penpot.viewport; + frame.x = viewport.center.x - 150; + frame.y = viewport.center.y - 200; + + const colors = penpot.library.local.colors.sort((a, b) => + a.name.toLowerCase() > b.name.toLowerCase() + ? 1 + : a.name.toLowerCase() < b.name.toLowerCase() + ? -1 + : 0 + ); + + if (colors.length === 0) { + // NO colors return + return; + } + + const cols = 3; + const rows = Math.ceil(colors.length / 3); + + const width = cols * 150 + Math.max(0, cols - 1) * 10 + 20; + const height = rows * 100 + Math.max(0, rows - 1) * 10 + 20; + + frame.resize(width, height); + + // create grid + const grid = frame.addGridLayout(); + + for (let i = 0; i < rows; i++) { + grid.addRow('auto'); + } + + for (let i = 0; i < cols; i++) { + grid.addColumn('auto'); + } + + grid.alignItems = 'center'; + grid.justifyItems = 'start'; + grid.justifyContent = 'stretch'; + grid.alignContent = 'stretch'; + grid.rowGap = 10; + grid.columnGap = 10; + grid.verticalPadding = 10; + grid.horizontalPadding = 10; + + // create text + for (let row = 0; row < rows; row++) { + for (let col = 0; col < cols; col++) { + const i = row * cols + col; + const color = colors[i]; + + if (i >= colors.length) { + return; + } + + const board = penpot.createFrame(); + grid.appendChild(board, row + 1, col + 1); + board.fills = [color.asFill()]; + + if (board.layoutChild) { + board.layoutChild.horizontalSizing = 'fill'; + board.layoutChild.verticalSizing = 'fill'; + } + + const flex = board.addFlexLayout(); + flex.alignItems = 'center'; + flex.justifyContent = 'center'; + + const text = penpot.createText(color.name); + text.growType = 'auto-width'; + board.appendChild(text); + } + } } }); diff --git a/libs/plugin-types/index.d.ts b/libs/plugin-types/index.d.ts index 76d5a58..0de06f9 100644 --- a/libs/plugin-types/index.d.ts +++ b/libs/plugin-types/index.d.ts @@ -137,11 +137,7 @@ export interface PenpotTrack { value: number | null; } -export interface PenpotGridLayout { - dir: 'column' | 'row'; - readonly rows: PenpotTrack[]; - readonly columns: PenpotTrack[]; - +export interface PenpotCommonLayout { alignItems?: 'start' | 'end' | 'center' | 'stretch'; alignContent?: | 'start' @@ -172,6 +168,17 @@ export interface PenpotGridLayout { bottomPadding: number; leftPadding: number; + horizontalSizing: 'fit-content' | 'fill' | 'auto'; + verticalSizing: 'fit-content' | 'fill' | 'auto'; + + remove(): void; +} + +export interface PenpotGridLayout extends PenpotCommonLayout { + dir: 'column' | 'row'; + readonly rows: PenpotTrack[]; + readonly columns: PenpotTrack[]; + addRow(type: PenpotTrackType, value?: number): void; addRowAtIndex(index: number, type: PenpotTrackType, value?: number): void; addColumn(type: PenpotTrackType, value?: number): void; @@ -182,7 +189,13 @@ export interface PenpotGridLayout { setRow(index: number, type: PenpotTrackType, value?: number): void; appendChild(child: PenpotShape, row: number, column: number): void; - remove(): void; +} + +export interface PenpotFlexLayout extends PenpotCommonLayout { + dir: 'row' | 'row-reverse' | 'column' | 'column-reverse'; + wrap?: 'wrap' | 'nowrap'; + + appendChild(child: PenpotShape): void; } export interface PenpotShapeBase { @@ -239,6 +252,38 @@ export interface PenpotShapeBase { fills: PenpotFill[]; strokes: PenpotStroke[]; + readonly layoutChild?: { + absolute: boolean; + zIndex: number; + + horizontalSizing: 'auto' | 'fill' | 'fix'; + verticalSizing: 'auto' | 'fill' | 'fix'; + + alignSelf: 'auto' | 'start' | 'center' | 'end' | 'stretch'; + + horizontalMargin: number; + verticalMargin: number; + + topMargin: number; + rightMargin: number; + bottomMargin: number; + leftMargin: number; + + maxWidth: number | null; + maxHeight: number | null; + minWidth: number | null; + minHeight: number | null; + }; + + readonly layoutCell?: { + row?: number; + rowSpan?: number; + column?: number; + columnSpan?: number; + areaName?: string; + position?: 'auto' | 'manual' | 'area'; + }; + resize(width: number, height: number): void; clone(): PenpotShape; remove(): void; @@ -246,21 +291,39 @@ export interface PenpotShapeBase { export interface PenpotFrame extends PenpotShapeBase { readonly type: 'frame'; - readonly children: PenpotShape[]; readonly grid?: PenpotGridLayout; + readonly flex?: PenpotFlexLayout; guides: PenpotFrameGuide; + horizontalSizing?: 'auto' | 'fix'; + verticalSizing?: 'auto' | 'fix'; + + // Container Properties + readonly children: PenpotShape[]; + appendChild(child: PenpotShape): void; + insertChild(index: number, child: PenpotShape): void; + + // Grid layout + addFlexLayout(): PenpotFlexLayout; addGridLayout(): PenpotGridLayout; } export interface PenpotGroup extends PenpotShapeBase { readonly type: 'group'; + + // Container Properties readonly children: PenpotShape[]; + appendChild(child: PenpotShape): void; + insertChild(index: number, child: PenpotShape): void; } export interface PenpotBool extends PenpotShapeBase { readonly type: 'bool'; + + // Container Properties readonly children: PenpotShape[]; + appendChild(child: PenpotShape): void; + insertChild(index: number, child: PenpotShape): void; } export interface PenpotRectangle extends PenpotShapeBase { @@ -328,11 +391,39 @@ export interface EventsMap { export type PenpotTheme = 'light' | 'dark'; +export type PenpotLibraryColor = { + name: string; + color?: string; + opacity?: number; + asFill(): PenpotFill; + asStroke(): PenpotStroke; +}; + +export type PenpotLibraryTypography = { + name: string; +}; + +export type PenpotLibraryComponent = { + name: string; +}; + +export type PenpotLibrary = { + colors: PenpotLibraryColor[]; + typographies: PenpotLibraryTypography[]; + components: PenpotLibraryComponent[]; +}; + +export type PenpotLibraryContext = { + local: PenpotLibrary; + connected: PenpotLibrary[]; +}; + export interface PenpotContext { root: PenpotShape; currentPage: PenpotPage; selection: PenpotShape[]; viewport: PenpotViewport; + library: PenpotLibraryContext; getFile(): PenpotFile | null; getPage(): PenpotPage | null; diff --git a/libs/plugins-runtime/src/lib/api/index.ts b/libs/plugins-runtime/src/lib/api/index.ts index e39ca71..bd268d5 100644 --- a/libs/plugins-runtime/src/lib/api/index.ts +++ b/libs/plugins-runtime/src/lib/api/index.ts @@ -11,6 +11,7 @@ import type { PenpotText, PenpotFile, PenpotTheme, + PenpotLibraryContext, } from '@penpot/plugin-types'; import { Manifest, Permissions } from '../models/manifest.model.js'; @@ -178,10 +179,15 @@ export function createApi(context: PenpotContext, manifest: Manifest): Penpot { }, get viewport(): PenpotViewport { - checkPermission('selection:read'); + // checkPermission('viewport:read'); return context.viewport; }, + get library(): PenpotLibraryContext { + // checkPermission('library:read'); + return context.library; + }, + getFile(): PenpotFile | null { checkPermission('file:read'); return context.getFile();