From 4ea22025cadaf7c84deed5a4a0300155cda70d33 Mon Sep 17 00:00:00 2001 From: pk Date: Mon, 6 Jul 2026 12:30:51 +0200 Subject: [PATCH] Allow custom dialogs, better styling support. Fix registration bug with tp-form. --- package.json | 2 +- tp-rich-text-box.js | 2 + tp-rtb-base-extension.js | 26 ++++--- tp-rtb-link-dialog.js | 146 +++++++++++++++++++++++++++++++++++++++ tp-rtb-link.js | 37 ++++------ 5 files changed, 179 insertions(+), 34 deletions(-) create mode 100644 tp-rtb-link-dialog.js diff --git a/package.json b/package.json index 6858fd8..4cfa1f3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@tp/tp-rich-text-box", - "version": "0.2.1", + "version": "0.2.2", "description": "", "main": "tp-rich-text-box.js", "scripts": { diff --git a/tp-rich-text-box.js b/tp-rich-text-box.js index 687fe28..de6b002 100644 --- a/tp-rich-text-box.js +++ b/tp-rich-text-box.js @@ -127,6 +127,8 @@ class TpRichTextBox extends FormElement(LitElement) { } firstUpdated() { + super.firstUpdated(); + // Get initial extensions from slot this._processChildExtensions(); diff --git a/tp-rtb-base-extension.js b/tp-rtb-base-extension.js index 5ff9dd1..e1430ab 100644 --- a/tp-rtb-base-extension.js +++ b/tp-rtb-base-extension.js @@ -6,6 +6,14 @@ export class TpRtbBaseExtension extends LitElement { :host { display: inline-block; } + + slot::slotted(*) { + cursor: pointer; + } + + :host([active]) slot::slotted(*) { + --tp-icon-color: var(--tp-rtb-action-active-color, currentColor); + } button { margin: 0 2px; @@ -26,6 +34,16 @@ export class TpRtbBaseExtension extends LitElement { `; } + render() { + return html` + + + + `; + } + static get properties() { return { label: { type: String }, @@ -39,14 +57,6 @@ export class TpRtbBaseExtension extends LitElement { this.active = false; } - render() { - return html` - - `; - } - connectedCallback() { super.connectedCallback(); this._findParentEditor(); diff --git a/tp-rtb-link-dialog.js b/tp-rtb-link-dialog.js new file mode 100644 index 0000000..1dffe15 --- /dev/null +++ b/tp-rtb-link-dialog.js @@ -0,0 +1,146 @@ +/** +@license +Copyright (c) 2026 trading_peter +This program is available under Apache License Version 2.0 +*/ + +import '@tp/tp-dialog/tp-dialog.js'; +import '@tp/tp-form/tp-form.js'; +import '@tp/tp-input/tp-input.js'; +import '@tp/tp-button/tp-button.js'; +import { LitElement, html, css } from 'lit'; + +export class TpRtbLinkDialog extends LitElement { + static get styles() { + return css` + :host { + display: contents; + } + + tp-dialog::part(dialog) { + width: min(420px, calc(100vw - 32px)); + } + + .wrap { + min-width: 300px; + } + + h3 { + margin: 0 0 18px 0; + padding-right: 20px; + } + + tp-form { + display: flex; + flex-direction: column; + gap: 12px; + } + + label { + display: block; + margin: 0 0 4px 0; + color: var(--tp-rtb-link-dialog-label-color, #666666); + } + + .buttons-justified { + display: flex; + flex-direction: row; + justify-content: space-between; + margin-top: 20px; + } + `; + } + + render() { + return html` + +
+

${this.dialogTitle}

+ + +
+ + + + +
+ +
+ + + + +
+ +
+ ${this.cancelLabel} + ${this.saveLabel} +
+
+
+
+ `; + } + + static get properties() { + return { + url: { type: String }, + text: { type: String }, + dialogTitle: { type: String }, + urlLabel: { type: String }, + textLabel: { type: String }, + urlPlaceholder: { type: String }, + textPlaceholder: { type: String }, + requiredMessage: { type: String }, + cancelLabel: { type: String }, + saveLabel: { type: String } + }; + } + + constructor() { + super(); + + this.url = ''; + this.text = ''; + this.dialogTitle = 'Add Link'; + this.urlLabel = 'URL'; + this.textLabel = 'Text'; + this.urlPlaceholder = 'https://example.com'; + this.textPlaceholder = ''; + this.requiredMessage = 'Required'; + this.cancelLabel = 'Cancel'; + this.saveLabel = 'Save'; + } + + async show({ url = '', text = '' } = {}) { + this.url = url; + this.text = text; + + await this.updateComplete; + + return this._dialog.show(); + } + + close() { + this._dialog.close(); + } + + get _dialog() { + return this.shadowRoot.querySelector('tp-dialog'); + } + + _submit(e) { + const event = new CustomEvent('link-save', { + detail: e.detail, + bubbles: true, + composed: true, + cancelable: true + }); + + if (this.dispatchEvent(event)) { + this.close(); + } + } +} + +customElements.define('tp-rtb-link-dialog', TpRtbLinkDialog); \ No newline at end of file diff --git a/tp-rtb-link.js b/tp-rtb-link.js index 8c6e173..c3b5c5c 100644 --- a/tp-rtb-link.js +++ b/tp-rtb-link.js @@ -1,7 +1,4 @@ -import '@tp/tp-dialog/tp-dialog.js'; -import '@tp/tp-form/tp-form.js'; -import '@tp/tp-input/tp-input.js'; -import '@tp/tp-button/tp-button.js'; +import './tp-rtb-link-dialog.js'; import { html } from 'lit'; import { TpRtbBaseExtension } from './tp-rtb-base-extension.js'; import Link from '@tiptap/extension-link'; @@ -16,21 +13,9 @@ class TpRtbLink extends TpRtbBaseExtension { return html` ${super.render()} - -

Add Link

- - - - - - - -
- Cancel - Save -
-
-
+ + + `; } @@ -57,8 +42,10 @@ class TpRtbLink extends TpRtbBaseExtension { this.text = editor.state.doc.textBetween(from, to, ' '); this.url = editor.getAttributes('link').href || ''; - const dialog = this.shadowRoot.querySelector('tp-dialog'); - dialog.show(); + this._dialog.show({ + url: this.url, + text: this.text + }); } } @@ -66,19 +53,19 @@ class TpRtbLink extends TpRtbBaseExtension { const { url, text } = e.detail; const { editor } = this.parentEditor; - console.log(e.detail); if (url) { - console.log(url, editor); editor.chain().focus().setLink({ href: url }).run(); - // If text is provided and different from current selection, update it + if (text && editor.state.selection.empty) { editor.chain().focus().insertContent(text).run(); } } else { editor.chain().focus().unsetLink().run(); } + } - this.shadowRoot.querySelector('tp-dialog').close(); + get _dialog() { + return this.querySelector('[slot="dialog"]') || this.shadowRoot.querySelector('tp-rtb-link-dialog'); } _setupEditorListeners() {