import {EditorFormModal, EditorFormModalDefinition} from "./modals";
import {EditorContainerUiElement, EditorUiContext, EditorUiElement, EditorUiStateUpdate} from "./core";
import {EditorDecorator, EditorDecoratorAdapter} from "./decorator";
-import {$getSelection, BaseSelection, COMMAND_PRIORITY_LOW, LexicalEditor, SELECTION_CHANGE_COMMAND} from "lexical";
+import {$getSelection, BaseSelection, LexicalEditor} from "lexical";
import {DecoratorListener} from "lexical/LexicalEditor";
import type {NodeKey} from "lexical/LexicalNode";
import {EditorContextToolbar, EditorContextToolbarDefinition} from "./toolbars";
+import {getLastSelection, setLastSelection} from "../../utils/selection";
+import {DropDownManager} from "./helpers/dropdowns";
export type SelectionChangeHandler = (selection: BaseSelection|null) => void;
protected activeContextToolbars: EditorContextToolbar[] = [];
protected selectionChangeHandlers: Set<SelectionChangeHandler> = new Set();
+ public dropdowns: DropDownManager = new DropDownManager();
+
setContext(context: EditorUiContext) {
this.context = context;
this.setupEventListeners(context);
this.contextToolbarDefinitionsByKey[key] = definition;
}
- protected triggerStateUpdate(update: EditorUiStateUpdate): void {
- const context = this.getContext();
- context.lastSelection = update.selection;
+ triggerStateUpdate(update: EditorUiStateUpdate): void {
+ setLastSelection(update.editor, update.selection);
this.toolbar?.updateState(update);
this.updateContextToolbars(update);
for (const toolbar of this.activeContextToolbars) {
}
triggerStateRefresh(): void {
- this.triggerStateUpdate({
- editor: this.getContext().editor,
- selection: this.getContext().lastSelection,
+ const editor = this.getContext().editor;
+ const update = {
+ editor,
+ selection: getLastSelection(editor),
+ };
+
+ this.triggerStateUpdate(update);
+ this.updateContextToolbars(update);
+ }
+
+ triggerFutureStateRefresh(): void {
+ requestAnimationFrame(() => {
+ this.getContext().editor.getEditorState().read(() => {
+ this.triggerStateRefresh();
+ });
});
}
this.selectionChangeHandlers.delete(handler);
}
+ triggerLayoutUpdate(): void {
+ window.requestAnimationFrame(() => {
+ for (const toolbar of this.activeContextToolbars) {
+ toolbar.updatePosition();
+ }
+ });
+ }
+
+ getDefaultDirection(): 'rtl' | 'ltr' {
+ return this.getContext().options.textDirection === 'rtl' ? 'rtl' : 'ltr';
+ }
+
protected updateContextToolbars(update: EditorUiStateUpdate): void {
for (let i = this.activeContextToolbars.length - 1; i >= 0; i--) {
const toolbar = this.activeContextToolbars[i];
}
protected setupEditor(editor: LexicalEditor) {
- // Update button states on editor selection change
- editor.registerCommand(SELECTION_CHANGE_COMMAND, () => {
- this.triggerStateUpdate({
- editor: editor,
- selection: $getSelection(),
- });
- return false;
- }, COMMAND_PRIORITY_LOW);
-
// Register our DOM decorate listener with the editor
const domDecorateListener: DecoratorListener<EditorDecoratorAdapter> = (decorators: Record<NodeKey, EditorDecoratorAdapter>) => {
editor.getEditorState().read(() => {
});
}
editor.registerDecoratorListener(domDecorateListener);
- }
- protected setupEventListeners(context: EditorUiContext) {
- const updateToolbars = (event: Event) => {
- for (const toolbar of this.activeContextToolbars) {
- toolbar.updatePosition();
+ // Watch for changes to update local state
+ editor.registerUpdateListener(({editorState, prevEditorState}) => {
+ // Watch for selection changes to update the UI on change
+ // Used to be done via SELECTION_CHANGE_COMMAND but this would not always emit
+ // for all selection changes, so this proved more reliable.
+ const selectionChange = !(prevEditorState._selection?.is(editorState._selection) || false);
+ if (selectionChange) {
+ editor.update(() => {
+ const selection = $getSelection();
+ // console.log('manager::selection', selection);
+ this.triggerStateUpdate({
+ editor, selection,
+ });
+ });
}
- };
+ });
+ }
- window.addEventListener('scroll', updateToolbars, {capture: true, passive: true});
- window.addEventListener('resize', updateToolbars, {passive: true});
+ protected setupEventListeners(context: EditorUiContext) {
+ const layoutUpdate = this.triggerLayoutUpdate.bind(this);
+ window.addEventListener('scroll', layoutUpdate, {capture: true, passive: true});
+ window.addEventListener('resize', layoutUpdate, {passive: true});
}
}
\ No newline at end of file