mirror of
https://github.com/penpot/penpot-plugins.git
synced 2025-03-06 04:45:44 -05:00
feat: rename layers plugin
This commit is contained in:
parent
50d11177a6
commit
b54eb57781
21 changed files with 384 additions and 8 deletions
17
README.md
17
README.md
|
@ -64,14 +64,15 @@ Open in your browser: `http://localhost:4210/`
|
|||
|
||||
## Sample plugins
|
||||
|
||||
| Plugin | Description | PORT | Start command | Manifest URL |
|
||||
| --------------------- | ----------------------------------------------------------- | ---- | ------------------------------- | ------------------------------------------ |
|
||||
| poc-state-plugin | Sandbox plugin to test new plugins api functionality | 4301 | npm run start:plugin:poc-state | http://localhost:4301/assets/manifest.json |
|
||||
| contrast-plugin | Sample plugin that gives you color contrast information | 4302 | npm run start:plugin:contrast | http://localhost:4302/manifest.json |
|
||||
| icons-plugin | Tool to add icons from [Feather](https://feathericons.com/) | 4303 | npm run start:plugin:icons | http://localhost:4303/assets/manifest.json |
|
||||
| lorem-ipsum-plugin | Generate Lorem ipsum text | 4304 | npm run start:plugin:loremipsum | http://localhost:4304/assets/manifest.json |
|
||||
| create-palette-plugin | Creates a board with all the palette colors | 4305 | npm run start:plugin:palette | http://localhost:4305/assets/manifest.json |
|
||||
| table-plugin | Create or import table | 4306 | npm run start:table-plugin | http://localhost:4306/assets/manifest.json | | -- |
|
||||
| Plugin | Description | PORT | Start command | Manifest URL |
|
||||
| --------------------- | ----------------------------------------------------------- | ---- | --------------------------------- | ------------------------------------------ |
|
||||
| poc-state-plugin | Sandbox plugin to test new plugins api functionality | 4301 | npm run start:plugin:poc-state | http://localhost:4301/assets/manifest.json |
|
||||
| contrast-plugin | Sample plugin that gives you color contrast information | 4302 | npm run start:plugin:contrast | http://localhost:4302/manifest.json |
|
||||
| icons-plugin | Tool to add icons from [Feather](https://feathericons.com/) | 4303 | npm run start:plugin:icons | http://localhost:4303/assets/manifest.json |
|
||||
| lorem-ipsum-plugin | Generate Lorem ipsum text | 4304 | npm run start:plugin:loremipsum | http://localhost:4304/assets/manifest.json |
|
||||
| create-palette-plugin | Creates a board with all the palette colors | 4305 | npm run start:plugin:palette | http://localhost:4305/assets/manifest.json |
|
||||
| table-plugin | Create or import table | 4306 | npm run start:table-plugin | http://localhost:4306/assets/manifest.json |
|
||||
| rename-layers-plugin | Rename layers in bulk | 4307 | npm run start:plugin:renamelayers | http://localhost:4307/assets/manifest.json |
|
||||
|
||||
## Web Apps
|
||||
|
||||
|
|
43
apps/rename-layers-plugin/eslint.config.js
Normal file
43
apps/rename-layers-plugin/eslint.config.js
Normal file
|
@ -0,0 +1,43 @@
|
|||
import baseConfig from '../../eslint.config.js';
|
||||
import { compat } from '../../eslint.base.config.js';
|
||||
|
||||
export default [
|
||||
...baseConfig,
|
||||
...compat
|
||||
.config({
|
||||
extends: [
|
||||
'plugin:@nx/angular',
|
||||
'plugin:@angular-eslint/template/process-inline-templates',
|
||||
],
|
||||
})
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.ts'],
|
||||
rules: {
|
||||
'@angular-eslint/directive-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'attribute',
|
||||
prefix: 'app',
|
||||
style: 'camelCase',
|
||||
},
|
||||
],
|
||||
'@angular-eslint/component-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'element',
|
||||
prefix: 'app',
|
||||
style: 'kebab-case',
|
||||
},
|
||||
],
|
||||
},
|
||||
})),
|
||||
...compat
|
||||
.config({ extends: ['plugin:@nx/angular-template'] })
|
||||
.map((config) => ({
|
||||
...config,
|
||||
files: ['**/*.html'],
|
||||
rules: {},
|
||||
})),
|
||||
{ ignores: ['**/assets/*.js'] },
|
||||
];
|
92
apps/rename-layers-plugin/project.json
Normal file
92
apps/rename-layers-plugin/project.json
Normal file
|
@ -0,0 +1,92 @@
|
|||
{
|
||||
"name": "rename-layers-plugin",
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"projectType": "application",
|
||||
"prefix": "app",
|
||||
"sourceRoot": "apps/rename-layers-plugin/src",
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@angular-devkit/build-angular:application",
|
||||
"outputs": ["{options.outputPath}"],
|
||||
"options": {
|
||||
"outputPath": "dist/apps/rename-layers-plugin",
|
||||
"index": "apps/rename-layers-plugin/src/index.html",
|
||||
"browser": "apps/rename-layers-plugin/src/main.ts",
|
||||
"polyfills": ["zone.js"],
|
||||
"tsConfig": "apps/rename-layers-plugin/tsconfig.app.json",
|
||||
"assets": [
|
||||
"apps/rename-layers-plugin/src/favicon.ico",
|
||||
"apps/rename-layers-plugin/src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"libs/plugins-styles/src/lib/styles.css",
|
||||
"apps/rename-layers-plugin/src/styles.css"
|
||||
],
|
||||
"optimization": {
|
||||
"scripts": true,
|
||||
"styles": true,
|
||||
"fonts": false
|
||||
},
|
||||
"scripts": []
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "500kb",
|
||||
"maximumError": "1mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "2kb",
|
||||
"maximumError": "4kb"
|
||||
}
|
||||
],
|
||||
"outputHashing": "all"
|
||||
},
|
||||
"development": {
|
||||
"optimization": false,
|
||||
"extractLicenses": false,
|
||||
"sourceMap": true
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "production",
|
||||
"dependsOn": ["buildPlugin"]
|
||||
},
|
||||
"serve": {
|
||||
"executor": "@angular-devkit/build-angular:dev-server",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"buildTarget": "rename-layers-plugin:build:production"
|
||||
},
|
||||
"development": {
|
||||
"buildTarget": "rename-layers-plugin:build:development",
|
||||
"host": "0.0.0.0",
|
||||
"port": 4307
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "development"
|
||||
},
|
||||
"extract-i18n": {
|
||||
"executor": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"buildTarget": "rename-layers-plugin:build"
|
||||
}
|
||||
},
|
||||
"buildPlugin": {
|
||||
"executor": "@nx/esbuild:esbuild",
|
||||
"outputs": ["{options.outputPath}"],
|
||||
"options": {
|
||||
"minify": true,
|
||||
"outputPath": "apps/rename-layers-plugin/src/assets/",
|
||||
"main": "apps/rename-layers-plugin/src/plugin.ts",
|
||||
"tsConfig": "apps/rename-layers-plugin/tsconfig.plugin.json",
|
||||
"generatePackageJson": false,
|
||||
"format": ["esm"],
|
||||
"deleteOutputPath": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
15
apps/rename-layers-plugin/src/app/app.component.css
Normal file
15
apps/rename-layers-plugin/src/app/app.component.css
Normal file
|
@ -0,0 +1,15 @@
|
|||
.explanation {
|
||||
margin-block-end: var(--spacing-8);
|
||||
}
|
||||
.form {
|
||||
padding-block: var(--spacing-12);
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-block-end: var(--spacing-8);
|
||||
}
|
||||
|
||||
.input,
|
||||
button {
|
||||
inline-size: 100%;
|
||||
}
|
26
apps/rename-layers-plugin/src/app/app.component.html
Normal file
26
apps/rename-layers-plugin/src/app/app.component.html
Normal file
|
@ -0,0 +1,26 @@
|
|||
<div class="form">
|
||||
<p class="explanation body-s">Introduce the text to replace</p>
|
||||
<div class="form-group">
|
||||
<label class="input-label-hidden" for="current">Search</label>
|
||||
<input
|
||||
[(ngModel)]="textToReplace.current"
|
||||
class="input"
|
||||
type="text"
|
||||
placeholder="Search"
|
||||
id="current"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="input-label-hidden" for="replace">Replace</label>
|
||||
<input
|
||||
[(ngModel)]="textToReplace.new"
|
||||
class="input"
|
||||
type="text"
|
||||
placeholder="Replace"
|
||||
id="replace"
|
||||
/>
|
||||
</div>
|
||||
<button type="button" data-appearance="primary" (click)="updateText()">
|
||||
Replace all
|
||||
</button>
|
||||
</div>
|
52
apps/rename-layers-plugin/src/app/app.component.ts
Normal file
52
apps/rename-layers-plugin/src/app/app.component.ts
Normal file
|
@ -0,0 +1,52 @@
|
|||
import { Component, inject } from '@angular/core';
|
||||
import { ActivatedRoute, RouterModule } from '@angular/router';
|
||||
import { toSignal } from '@angular/core/rxjs-interop';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import type { PluginMessageEvent, ReplaceText } from '../app/model';
|
||||
import { filter, fromEvent, map, merge, take } from 'rxjs';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
imports: [RouterModule, CommonModule, FormsModule],
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrl: './app.component.css',
|
||||
host: {
|
||||
'[attr.data-theme]': 'theme()',
|
||||
},
|
||||
})
|
||||
export class AppComponent {
|
||||
route = inject(ActivatedRoute);
|
||||
messages$ = fromEvent<MessageEvent<PluginMessageEvent>>(window, 'message');
|
||||
public textToReplace: ReplaceText = {
|
||||
current: '',
|
||||
new: '',
|
||||
};
|
||||
|
||||
initialTheme$ = this.route.queryParamMap.pipe(
|
||||
map((params) => params.get('theme')),
|
||||
filter((theme) => !!theme),
|
||||
take(1)
|
||||
);
|
||||
|
||||
theme = toSignal(
|
||||
merge(
|
||||
this.initialTheme$,
|
||||
this.messages$.pipe(
|
||||
filter((event) => event.data.type === 'theme'),
|
||||
map((event) => {
|
||||
return event.data.content;
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
public updateText() {
|
||||
this.sendMessage({ type: 'replace-text', content: this.textToReplace });
|
||||
}
|
||||
|
||||
private sendMessage(message: PluginMessageEvent): void {
|
||||
parent.postMessage(message, '*');
|
||||
}
|
||||
}
|
7
apps/rename-layers-plugin/src/app/app.config.ts
Normal file
7
apps/rename-layers-plugin/src/app/app.config.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
import { ApplicationConfig } from '@angular/core';
|
||||
import { provideRouter } from '@angular/router';
|
||||
import { appRoutes } from './app.routes';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [provideRouter(appRoutes)],
|
||||
};
|
3
apps/rename-layers-plugin/src/app/app.routes.ts
Normal file
3
apps/rename-layers-plugin/src/app/app.routes.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
import { Route } from '@angular/router';
|
||||
|
||||
export const appRoutes: Route[] = [];
|
25
apps/rename-layers-plugin/src/app/model.ts
Normal file
25
apps/rename-layers-plugin/src/app/model.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
export interface InitPluginEvent {
|
||||
type: 'init';
|
||||
content: {
|
||||
theme: string;
|
||||
};
|
||||
}
|
||||
export interface ThemePluginEvent {
|
||||
type: 'theme';
|
||||
content: string;
|
||||
}
|
||||
|
||||
export interface ReplaceTextPluginEvent {
|
||||
type: 'replace-text';
|
||||
content: ReplaceText;
|
||||
}
|
||||
|
||||
export type PluginMessageEvent =
|
||||
| InitPluginEvent
|
||||
| ThemePluginEvent
|
||||
| ReplaceTextPluginEvent;
|
||||
|
||||
export interface ReplaceText {
|
||||
current: string;
|
||||
new: string;
|
||||
}
|
0
apps/rename-layers-plugin/src/assets/.gitkeep
Normal file
0
apps/rename-layers-plugin/src/assets/.gitkeep
Normal file
7
apps/rename-layers-plugin/src/assets/manifest.json
Normal file
7
apps/rename-layers-plugin/src/assets/manifest.json
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"name": "Rename layers plugin",
|
||||
"host": "http://localhost:4307",
|
||||
"description": "Change the name of one or several layers",
|
||||
"code": "/assets/plugin.js",
|
||||
"permissions": ["page:read", "file:read", "selection:read"]
|
||||
}
|
BIN
apps/rename-layers-plugin/src/favicon.ico
Normal file
BIN
apps/rename-layers-plugin/src/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
13
apps/rename-layers-plugin/src/index.html
Normal file
13
apps/rename-layers-plugin/src/index.html
Normal file
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>rename-layers-plugin</title>
|
||||
<base href="/" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
||||
</head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
</html>
|
7
apps/rename-layers-plugin/src/main.ts
Normal file
7
apps/rename-layers-plugin/src/main.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
import { bootstrapApplication } from '@angular/platform-browser';
|
||||
import { appConfig } from './app/app.config';
|
||||
import { AppComponent } from './app/app.component';
|
||||
|
||||
bootstrapApplication(AppComponent, appConfig).catch((err) =>
|
||||
console.error(err)
|
||||
);
|
25
apps/rename-layers-plugin/src/plugin.ts
Normal file
25
apps/rename-layers-plugin/src/plugin.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
import { PluginMessageEvent } from './app/model';
|
||||
|
||||
penpot.ui.open('Plugin rename layers', `?theme=${penpot.getTheme()}`, {
|
||||
width: 235,
|
||||
height: 200,
|
||||
});
|
||||
|
||||
penpot.on('themechange', (theme) => {
|
||||
penpot.ui.sendMessage({ type: 'theme', content: theme });
|
||||
});
|
||||
|
||||
penpot.ui.onMessage<PluginMessageEvent>((message) => {
|
||||
if (message.type === 'replace-text') {
|
||||
const shapes = penpot.getPage()?.findShapes();
|
||||
const shapesToUpdate = shapes?.filter((shape) =>
|
||||
shape.name.includes(message.content.current)
|
||||
);
|
||||
shapesToUpdate?.forEach((shape) => {
|
||||
shape.name = shape.name.replace(
|
||||
message.content.current,
|
||||
message.content.new
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
1
apps/rename-layers-plugin/src/styles.css
Normal file
1
apps/rename-layers-plugin/src/styles.css
Normal file
|
@ -0,0 +1 @@
|
|||
/* You can add global styles to this file, and also import other style files */
|
10
apps/rename-layers-plugin/tsconfig.app.json
Normal file
10
apps/rename-layers-plugin/tsconfig.app.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"types": []
|
||||
},
|
||||
"files": ["src/main.ts"],
|
||||
"include": ["src/**/*.d.ts"],
|
||||
"exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"]
|
||||
}
|
7
apps/rename-layers-plugin/tsconfig.editor.json
Normal file
7
apps/rename-layers-plugin/tsconfig.editor.json
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"include": ["src/**/*.ts"],
|
||||
"compilerOptions": {
|
||||
"types": []
|
||||
}
|
||||
}
|
33
apps/rename-layers-plugin/tsconfig.json
Normal file
33
apps/rename-layers-plugin/tsconfig.json
Normal file
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2022",
|
||||
"useDefineForClassFields": false,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.editor.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.plugin.json"
|
||||
}
|
||||
],
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"angularCompilerOptions": {
|
||||
"enableI18nLegacyMessageIdFormat": false,
|
||||
"strictInjectionParameters": true,
|
||||
"strictInputAccessModifiers": true,
|
||||
"strictTemplates": true
|
||||
}
|
||||
}
|
8
apps/rename-layers-plugin/tsconfig.plugin.json
Normal file
8
apps/rename-layers-plugin/tsconfig.plugin.json
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"types": []
|
||||
},
|
||||
"files": ["src/plugin.ts"],
|
||||
"include": ["../../libs/plugin-types/index.d.ts"]
|
||||
}
|
|
@ -14,6 +14,7 @@
|
|||
"start:plugin:loremipsum": "npx nx run lorem-ipsum-plugin:init",
|
||||
"start:plugin:palette": "npx nx run create-palette-plugin:build --watch & npx nx run create-palette-plugin:preview",
|
||||
"start:plugin:table": "npx nx run table-plugin:init",
|
||||
"start:plugin:renamelayers": "npx nx run rename-layers-plugin:init",
|
||||
"build": "npx nx build plugins-runtime --emptyOutDir=true",
|
||||
"lint": "nx run-many --all --target=lint --parallel",
|
||||
"lint:affected": "npx nx affected --target=lint",
|
||||
|
|
Loading…
Add table
Reference in a new issue