diff --git a/libs/plugins-runtime/src/index.ts b/libs/plugins-runtime/src/index.ts index 35006bc..45bbe96 100644 --- a/libs/plugins-runtime/src/index.ts +++ b/libs/plugins-runtime/src/index.ts @@ -1,6 +1,5 @@ import 'ses'; import './lib/plugin-modal'; -import { initInstaller } from './lib/installer.js'; import { ɵloadPlugin, setContext } from './lib/load-plugin.js'; import * as api from './lib/api/index.js'; @@ -22,7 +21,6 @@ globalThis.initPluginsRuntime = (context: PenpotContext) => { /* eslint-disable */ globalThis.ɵcontext = context; globalThis.ɵloadPlugin = ɵloadPlugin; - initInstaller(); setContext(context); diff --git a/libs/plugins-runtime/src/lib/installer.ts b/libs/plugins-runtime/src/lib/installer.ts deleted file mode 100644 index 792d039..0000000 --- a/libs/plugins-runtime/src/lib/installer.ts +++ /dev/null @@ -1,336 +0,0 @@ -import { loadManifest } from './parse-manifest'; -import { ɵloadPlugin } from './load-plugin'; - -const closeSvg = ` -`; - -const pasteEventHandler = (event: ClipboardEvent) => { - const tagName = (event.target as HTMLElement).tagName; - - if (tagName === 'INSTALLER-MODAL') { - event.stopImmediatePropagation(); - } -}; - -export class InstallerElement extends HTMLElement { - constructor() { - super(); - this.attachShadow({ mode: 'open' }); - } - - dialog: HTMLDialogElement | null = null; - - createPlugin(pluginName: string, pluginUrl: string) { - const plugin = document.createElement('li'); - plugin.classList.add('plugin'); - plugin.textContent = pluginName; - - const actions = document.createElement('div'); - actions.classList.add('actions'); - - const openButton = document.createElement('button'); - openButton.classList.add('button'); - openButton.textContent = 'Open'; - openButton.type = 'button'; - openButton.addEventListener('click', () => { - this.closeModal(); - - void ɵloadPlugin({ - manifest: pluginUrl, - }); - }); - - actions.appendChild(openButton); - - const removeButton = document.createElement('button'); - removeButton.classList.add('button', 'remove'); - removeButton.textContent = 'Remove'; - removeButton.type = 'button'; - removeButton.addEventListener('click', () => { - plugin.remove(); - - const plugins = this.getPlugins(); - - const newPlugins = plugins.filter((p) => p.url !== pluginUrl); - - this.savePlugins(newPlugins); - }); - - actions.appendChild(removeButton); - - plugin.appendChild(actions); - - this.dialog?.querySelector('.plugins-list')?.prepend(plugin); - } - - loadPluginList() { - const plugins = this.getPlugins(); - - for (const plugin of plugins) { - this.createPlugin(plugin.name, plugin.url); - } - } - - getPlugins() { - const pluginsStorage = localStorage.getItem('plugins'); - - if (!pluginsStorage) { - return []; - } - - return JSON.parse(pluginsStorage) as { - name: string; - url: string; - }[]; - } - - savePlugins(plugins: { name: string; url: string }[]) { - localStorage.setItem('plugins', JSON.stringify(plugins)); - } - - submitNewPlugin(event: Event) { - event.preventDefault(); - - const form = event.target as HTMLFormElement; - const input = form.querySelector('input') as HTMLInputElement; - - if (!input) { - return; - } - - const url = input.value; - - input.value = ''; - - void loadManifest(url) - .then((manifest) => { - this.createPlugin(manifest.name, url); - - const pluginsStorage = localStorage.getItem('plugins'); - - if (!pluginsStorage) { - localStorage.setItem( - 'plugins', - JSON.stringify([{ name: manifest.name, url }]) - ); - } else { - const plugins = this.getPlugins(); - plugins.push({ name: manifest.name, url }); - - this.savePlugins(plugins); - } - - this.error(false); - }) - .catch((error) => { - console.error(error); - this.error(true); - }); - } - - error(show: boolean) { - this.dialog?.querySelector('.error')?.classList.toggle('show', show); - } - - connectedCallback() { - if (!this.shadowRoot) { - throw new Error('Error creating shadow root'); - } - - this.dialog = document.createElement('dialog'); - - this.dialog.innerHTML = ` -
-

Plugins

- -
-
- - -
-
- Error instaling plugin -
- - - `; - - this.dialog.querySelector('.close')?.addEventListener('click', () => { - this.closeModal(); - }); - - this.shadowRoot.appendChild(this.dialog); - - this.dialog.addEventListener('submit', (event: Event) => { - this.submitNewPlugin(event); - }); - - this.loadPluginList(); - - const style = document.createElement('style'); - style.textContent = ` - * { - font-family worksans, sans-serif - } - - ::backdrop { - background-color: rgba(0, 0, 0, 0.8); - } - - dialog { - border: 0; - width: 700px; - height: 500px; - padding: 20px; - background-color: white; - border-radius: 10px; - flex-direction: column; - display: none; - } - - dialog[open] { - display: flex; - } - - .header { - display: flex; - justify-content: space-between; - } - - h1 { - margin: 0; - margin-block-end: 10px; - } - - ul { - padding: 0; - } - - li { - list-style: none; - } - - .input { - display: flex; - border: 1px solid; - border-radius: calc( 0.25rem * 2); - font-size: 12px; - font-weight: 400; - line-height: 1.4; - outline: none; - padding-block: calc( 0.25rem * 2); - padding-inline: calc( 0.25rem * 2); - background-color: #f3f4f6; - border-color: #f3f4f6; - color: #000; - - &:hover { - background-color: #eef0f2; - border-color: #eef0f2; - } - - &:focus { - background-color: #ffffff - border-color: ##6911d4; - } - } - - button { - background: transparent; - border: 0; - cursor: pointer; - } - - .button { - border: 1px solid transparent; - font-weight: 500; - font-size: 12px; - border-radius: 8px; - line-height: 1.2; - padding: 8px 24px 8px 24px; - text-transform: uppercase; - background-color: #7EFFF5; - border: 1px solid 7EFFF5; - outline: 2px solid transparent; - - &:hover:not(:disabled) { - cursor: pointer; - } - - &:focus-visible { - outline: none; - } - } - - .remove { - background-color: #ff3277; - border: 1px solid #ff3277; - outline: 2px solid transparent; - } - - form { - display: flex; - gap: 10px; - margin-block-end: 20px; - } - - .url-input { - inline-size: 400px; - } - - .plugins-list { - display: flex; - flex-direction: column; - gap: 10px; - } - - .plugin { - display: flex; - justify-content: space-between; - } - - .actions { - display: flex; - gap: 10px; - } - - .error { - display: none; - color: red; - - &.show { - display: block; - } - } - `; - - this.shadowRoot.appendChild(style); - } - - closeModal() { - this.shadowRoot?.querySelector('dialog')?.close(); - - window.removeEventListener('paste', pasteEventHandler, true); - } - - openModal() { - this.shadowRoot?.querySelector('dialog')?.showModal(); - - // prevent paste event in penpot workspace - window.addEventListener('paste', pasteEventHandler, true); - } -} - -export function initInstaller() { - customElements.define('installer-modal', InstallerElement); - - const modal = document.createElement('installer-modal'); - - document.body.appendChild(modal); - - document.addEventListener('keydown', (event: KeyboardEvent) => { - if (event.key.toUpperCase() === 'I' && event.ctrlKey) { - document.querySelector('installer-modal')?.openModal(); - } - }); -}