fix: 浮动工具栏开关按钮无效 — 新增 toggleFloatingToolbar/isFloatingToolbar API
This commit is contained in:
+2
-9
@@ -229,15 +229,8 @@ bar.addEventListener('click', function(e){
|
|||||||
else if (cmd === 'outline') { toggleOutline(btn); }
|
else if (cmd === 'outline') { toggleOutline(btn); }
|
||||||
else if (cmd === 'wrap') { editor.toggleWordWrap(); btn.classList.toggle('on', !editor.isWordWrap()); }
|
else if (cmd === 'wrap') { editor.toggleWordWrap(); btn.classList.toggle('on', !editor.isWordWrap()); }
|
||||||
else if (cmd === 'float') {
|
else if (cmd === 'float') {
|
||||||
var hasFloat = editor._floatingToolbar && editor._floatingToolbar.parentNode;
|
editor.toggleFloatingToolbar();
|
||||||
if (hasFloat) {
|
btn.classList.toggle('on', editor.isFloatingToolbar());
|
||||||
if (editor._floatingToolbar.parentNode) editor._floatingToolbar.parentNode.removeChild(editor._floatingToolbar);
|
|
||||||
editor._floatingToolbar = null;
|
|
||||||
} else {
|
|
||||||
editor.config.floatingToolbar = true;
|
|
||||||
editor._initFloatingToolbar();
|
|
||||||
}
|
|
||||||
btn.classList.toggle('on', !!editor._floatingToolbar);
|
|
||||||
}
|
}
|
||||||
else if (cmd === 'toast') {
|
else if (cmd === 'toast') {
|
||||||
var types = ['success','error','warning','info'];
|
var types = ['success','error','warning','info'];
|
||||||
|
|||||||
+39
-16
@@ -84,6 +84,7 @@ export class MarkdownEditor {
|
|||||||
_i18nCtx!: InstanceI18n;
|
_i18nCtx!: InstanceI18n;
|
||||||
_floatingToolbar!: HTMLElement | null;
|
_floatingToolbar!: HTMLElement | null;
|
||||||
_selectionTimer!: ReturnType<typeof setInterval> | null;
|
_selectionTimer!: ReturnType<typeof setInterval> | null;
|
||||||
|
_floatingEnabled!: boolean;
|
||||||
|
|
||||||
constructor(container: string | HTMLElement, options: EditorOptions = {}) {
|
constructor(container: string | HTMLElement, options: EditorOptions = {}) {
|
||||||
if (!isBrowser()) {
|
if (!isBrowser()) {
|
||||||
@@ -108,6 +109,7 @@ export class MarkdownEditor {
|
|||||||
this._outlineTimer = null; this._zenMode = false; this._wordWrap = true; this._syncing = false;
|
this._outlineTimer = null; this._zenMode = false; this._wordWrap = true; this._syncing = false;
|
||||||
this._zenMouseHandler = null;
|
this._zenMouseHandler = null;
|
||||||
this._floatingToolbar = null; this._selectionTimer = null;
|
this._floatingToolbar = null; this._selectionTimer = null;
|
||||||
|
this._floatingEnabled = this.config.floatingToolbar !== false;
|
||||||
|
|
||||||
this._renderFn = (typeof this.config.render === 'function') ? this.config.render : parseMarkdown;
|
this._renderFn = (typeof this.config.render === 'function') ? this.config.render : parseMarkdown;
|
||||||
this._highlightFn = (typeof this.config.highlight === 'function') ? this.config.highlight : null;
|
this._highlightFn = (typeof this.config.highlight === 'function') ? this.config.highlight : null;
|
||||||
@@ -716,13 +718,15 @@ export class MarkdownEditor {
|
|||||||
document.head.appendChild(style);
|
document.head.appendChild(style);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this._floatingEnabled) return;
|
||||||
|
|
||||||
const ta = this.textarea;
|
const ta = this.textarea;
|
||||||
const show = () => {
|
const show = () => {
|
||||||
if (this._destroyed || this.config.readOnly) return;
|
if (this._destroyed || this.config.readOnly || !this._floatingEnabled) return;
|
||||||
const start = ta.selectionStart; const end = ta.selectionEnd;
|
const start = ta.selectionStart; const end = ta.selectionEnd;
|
||||||
if (start === end) { this._hideFloatingToolbar(); return; }
|
if (start === end) { this._hideFloatingToolbar(); return; }
|
||||||
|
|
||||||
if (!this._floatingToolbar) this._buildFloatingToolbar();
|
if (!this._floatingToolbar || !this._floatingToolbar.parentNode) this._buildFloatingToolbar();
|
||||||
|
|
||||||
// Position the toolbar near the selection
|
// Position the toolbar near the selection
|
||||||
const rect = ta.getBoundingClientRect();
|
const rect = ta.getBoundingClientRect();
|
||||||
@@ -731,10 +735,9 @@ export class MarkdownEditor {
|
|||||||
const currentLine = lines.length - 1;
|
const currentLine = lines.length - 1;
|
||||||
const lineHeight = parseInt(getComputedStyle(ta).lineHeight, 10) || 22;
|
const lineHeight = parseInt(getComputedStyle(ta).lineHeight, 10) || 22;
|
||||||
|
|
||||||
let top = rect.top + currentLine * lineHeight - 38; // above the selection
|
let top = rect.top + currentLine * lineHeight - 38;
|
||||||
let left = rect.left + (ta.selectionDirection !== 'backward' ? (ta.selectionEnd - ta.selectionStart) / 2 : 0) * 8 + 10;
|
let left = rect.left + (ta.selectionDirection !== 'backward' ? (ta.selectionEnd - ta.selectionStart) / 2 : 0) * 8 + 10;
|
||||||
|
|
||||||
// Clamp to stay within the editor pane
|
|
||||||
if (top < rect.top) top = rect.top + currentLine * lineHeight + lineHeight + 4;
|
if (top < rect.top) top = rect.top + currentLine * lineHeight + lineHeight + 4;
|
||||||
if (left < rect.left) left = rect.left + 4;
|
if (left < rect.left) left = rect.left + 4;
|
||||||
const maxLeft = rect.right - 200;
|
const maxLeft = rect.right - 200;
|
||||||
@@ -747,35 +750,55 @@ export class MarkdownEditor {
|
|||||||
|
|
||||||
const hide = () => { this._hideFloatingToolbar(); };
|
const hide = () => { this._hideFloatingToolbar(); };
|
||||||
|
|
||||||
// Detect selection changes via events + throttled polling
|
|
||||||
let lastSelStart = ta.selectionStart;
|
let lastSelStart = ta.selectionStart;
|
||||||
let lastSelEnd = ta.selectionEnd;
|
let lastSelEnd = ta.selectionEnd;
|
||||||
const check = () => {
|
this._selectionTimer = setInterval(() => {
|
||||||
if (this._destroyed || !document.body.contains(ta)) return;
|
if (this._destroyed || !document.body.contains(ta) || !this._floatingEnabled) return;
|
||||||
const s = ta.selectionStart; const e = ta.selectionEnd;
|
const s = ta.selectionStart; const e = ta.selectionEnd;
|
||||||
if (s !== lastSelStart || e !== lastSelEnd) {
|
if (s !== lastSelStart || e !== lastSelEnd) {
|
||||||
lastSelStart = s; lastSelEnd = e;
|
lastSelStart = s; lastSelEnd = e;
|
||||||
const text = this._value.slice(s, e);
|
const text = this._value.slice(s, e);
|
||||||
this._emit('selectionChange', { start: s, end: e, text });
|
this._emit('selectionChange', { start: s, end: e, text });
|
||||||
}
|
}
|
||||||
};
|
}, 500) as any;
|
||||||
// Use longer interval for polling (won't block event loop as much)
|
|
||||||
this._selectionTimer = setInterval(check, 500) as any;
|
|
||||||
this._cleanups.push(() => { if (this._selectionTimer) { clearInterval(this._selectionTimer); this._selectionTimer = null; } });
|
this._cleanups.push(() => { if (this._selectionTimer) { clearInterval(this._selectionTimer); this._selectionTimer = null; } });
|
||||||
|
|
||||||
ta.addEventListener('mouseup', () => setTimeout(show, 0));
|
ta.addEventListener('mouseup', () => { if (this._floatingEnabled) setTimeout(show, 0); });
|
||||||
ta.addEventListener('keyup', () => {
|
ta.addEventListener('keyup', () => {
|
||||||
const s = ta.selectionStart; const e = ta.selectionEnd;
|
if (!this._floatingEnabled) return;
|
||||||
if (s !== e) setTimeout(show, 0);
|
if (ta.selectionStart !== ta.selectionEnd) setTimeout(show, 0);
|
||||||
else setTimeout(hide, 0);
|
else setTimeout(hide, 0);
|
||||||
this._emit('cursorMove', this.getCursorPosition());
|
this._emit('cursorMove', this.getCursorPosition());
|
||||||
});
|
});
|
||||||
ta.addEventListener('blur', () => setTimeout(hide, 300));
|
ta.addEventListener('blur', () => setTimeout(hide, 300));
|
||||||
ta.addEventListener('click', () => setTimeout(() => {
|
ta.addEventListener('click', () => { if (ta.selectionStart === ta.selectionEnd) setTimeout(hide, 0); });
|
||||||
if (ta.selectionStart === ta.selectionEnd) hide();
|
|
||||||
}, 0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggleFloatingToolbar(): this {
|
||||||
|
this._floatingEnabled = !this._floatingEnabled;
|
||||||
|
if (this._floatingEnabled) {
|
||||||
|
// Re-enable: restart the interval
|
||||||
|
if (!this._selectionTimer) {
|
||||||
|
const ta = this.textarea;
|
||||||
|
this._selectionTimer = setInterval(() => {
|
||||||
|
if (this._destroyed || !this._floatingEnabled) return;
|
||||||
|
const s = ta.selectionStart; const e = ta.selectionEnd;
|
||||||
|
if (s !== e) { /* will show via keyup/mouseup */ }
|
||||||
|
}, 500) as any;
|
||||||
|
this._cleanups.push(() => { if (this._selectionTimer) { clearInterval(this._selectionTimer); this._selectionTimer = null; } });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Disable: clear interval and hide toolbar
|
||||||
|
if (this._selectionTimer) { clearInterval(this._selectionTimer); this._selectionTimer = null; }
|
||||||
|
if (this._floatingToolbar) {
|
||||||
|
if (this._floatingToolbar.parentNode) this._floatingToolbar.parentNode.removeChild(this._floatingToolbar);
|
||||||
|
this._floatingToolbar = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
isFloatingToolbar(): boolean { return this._floatingEnabled; }
|
||||||
|
|
||||||
_buildFloatingToolbar(): void {
|
_buildFloatingToolbar(): void {
|
||||||
const bar = document.createElement('div');
|
const bar = document.createElement('div');
|
||||||
bar.className = 'me-float-toolbar';
|
bar.className = 'me-float-toolbar';
|
||||||
|
|||||||
Reference in New Issue
Block a user