Add placeholder and hide floating menu if no actions are exposed.
This commit is contained in:
+30
-6
@@ -11,7 +11,7 @@ import Document from '@tiptap/extension-document';
|
||||
import Paragraph from '@tiptap/extension-paragraph';
|
||||
import Text from '@tiptap/extension-text';
|
||||
import HardBreak from '@tiptap/extension-hard-break';
|
||||
import { UndoRedo } from '@tiptap/extensions';
|
||||
import { UndoRedo, Placeholder } from '@tiptap/extensions';
|
||||
import Mention from '@tiptap/extension-mention';
|
||||
import { MenuPositioner } from './menu-positioner.js';
|
||||
import { FormElement } from '@tp/helpers/form-element.js';
|
||||
@@ -23,7 +23,6 @@ class TpRichTextBox extends FormElement(LitElement) {
|
||||
:host {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border: 1px solid #ccc;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@@ -92,7 +91,7 @@ class TpRichTextBox extends FormElement(LitElement) {
|
||||
<!-- Selection-based menu with all controls -->
|
||||
<div
|
||||
class="selection-menu"
|
||||
?hidden=${this.menuMode !== 'selection' && this.menuPosition !== 'static'}
|
||||
?hidden=${!this._menuControlCount || (this.menuMode !== 'selection' && this.menuPosition !== 'static')}
|
||||
?data-disabled=${this.menuPosition === 'static' && this.menuMode !== 'selection'}
|
||||
>
|
||||
<slot @slotchange=${this._handleSlotChange}></slot>
|
||||
@@ -113,7 +112,9 @@ class TpRichTextBox extends FormElement(LitElement) {
|
||||
extensions: { type: Array },
|
||||
menuMode: { type: String, reflect: true },
|
||||
activeSuggestionType: { type: String },
|
||||
menuPosition: { type: String, reflect: true }
|
||||
menuPosition: { type: String, reflect: true },
|
||||
placeholder: { type: String },
|
||||
_menuControlCount: { state: true }
|
||||
};
|
||||
}
|
||||
|
||||
@@ -122,6 +123,8 @@ class TpRichTextBox extends FormElement(LitElement) {
|
||||
this.extensions = [];
|
||||
this.menuMode = 'hidden'; // 'hidden', 'selection', 'suggestion'
|
||||
this.activeSuggestionType = null; // 'emoji', 'user', null
|
||||
this.placeholder = '';
|
||||
this._menuControlCount = 0;
|
||||
this.menuPosition = 'floating'; // 'floating', 'static'
|
||||
this._slotObserver = new MutationObserver(this._processSlotChanges.bind(this));
|
||||
}
|
||||
@@ -188,16 +191,19 @@ class TpRichTextBox extends FormElement(LitElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
// No formatting controls slotted — no menu, ever. Consumers without
|
||||
// tp-rtb-* buttons (chat composer, agents composer) otherwise got an
|
||||
// EMPTY floating box popping up on every text selection.
|
||||
if (!this._menuControlCount) return;
|
||||
|
||||
// In floating mode, hide menu if selection is empty or a caret
|
||||
if (!hasSelection) {
|
||||
this.hideMenu();
|
||||
console.log('Selection is empty or a caret. Hiding menu.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Show selection menu for text selection
|
||||
this.showSelectionMenu();
|
||||
console.log('Text selected. Showing selection menu.');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -253,6 +259,7 @@ class TpRichTextBox extends FormElement(LitElement) {
|
||||
}
|
||||
|
||||
_handleSlotChange(e) {
|
||||
this._menuControlCount = e.target.assignedElements().length;
|
||||
this._processChildExtensions();
|
||||
}
|
||||
|
||||
@@ -298,6 +305,10 @@ class TpRichTextBox extends FormElement(LitElement) {
|
||||
Text,
|
||||
HardBreak,
|
||||
UndoRedo,
|
||||
// Empty-state placeholder text, rendered by the consumer's CSS
|
||||
// (.tiptap p.is-editor-empty:first-child::before) from the
|
||||
// data-placeholder attribute the extension decorates onto the node.
|
||||
Placeholder.configure({ placeholder: this.placeholder }),
|
||||
];
|
||||
|
||||
// Collect suggestion configurations from child components
|
||||
@@ -417,6 +428,19 @@ class TpRichTextBox extends FormElement(LitElement) {
|
||||
}
|
||||
}
|
||||
|
||||
updated(changes) {
|
||||
super.updated(changes);
|
||||
|
||||
// The placeholder is baked into the editor's extension config —
|
||||
// re-create the editor (content preserved) when it changes. Consumers
|
||||
// typically set it once as a static attribute.
|
||||
if (changes.has('placeholder') && this.editor) {
|
||||
const html = this.editor.getHTML();
|
||||
this._initEditor();
|
||||
if (html) this.editor.commands.setContent(html);
|
||||
}
|
||||
}
|
||||
|
||||
get value() {
|
||||
if (this.editor) {
|
||||
return { html: this.editor.getHTML(), json: this.editor.getJSON() };
|
||||
|
||||
Reference in New Issue
Block a user