setupListeners() {
window.addEventListener('keydown', event => {
- if (event.target.closest('input, select, textarea, .cm-editor')) {
+ if (event.target.closest('input, select, textarea, .cm-editor, .editor-container')) {
return;
}
_: RangeSelection,
restoreSelection = true,
): ListItemNode | ParagraphNode {
+
+ if (this.getTextContent().trim() === '' && this.isLastChild()) {
+ const list = this.getParentOrThrow<ListNode>();
+ if (!$isListItemNode(list.getParent())) {
+ const paragraph = $createParagraphNode();
+ list.insertAfter(paragraph, restoreSelection);
+ this.remove();
+ return paragraph;
+ }
+ }
+
const newElement = $createListItemNode(
this.__checked == null ? undefined : false,
);
+
this.insertAfter(newElement, restoreSelection);
return newElement;
## Main Todo
- Mac: Shortcut support via command.
-- Translations
-- Form closing on submit
- Update toolbar overflows to match existing editor, incl. direction dynamic controls
## Secondary Todo
- Color picker for color controls
- Table caption text support
- Support media src conversions (https://github.com/tinymce/tinymce/blob/release/6.6/modules/tinymce/src/plugins/media/main/ts/core/UrlPatterns.ts)
-- Check translation coverage
+- Deep check of translation coverage
## Bugs
- List selection can get lost on nesting/unnesting
-- Can't escape lists when bottom element
-- Content not properly saving on new pages
-- BookStack UI (non-editor) shortcuts can trigger in editor (`/` for example)
\ No newline at end of file
+- Content not properly saving on new pages
\ No newline at end of file
export function $showLinkForm(link: LinkNode|null, context: EditorUiContext) {
const linkModal = context.manager.createModal('link');
- let formDefaults = {};
if (link) {
- formDefaults = {
+ const formDefaults: Record<string, string> = {
url: link.getURL(),
text: link.getTextContent(),
- title: link.getTitle(),
- target: link.getTarget(),
+ title: link.getTitle() || '',
+ target: link.getTarget() || '',
}
context.editor.update(() => {
selection.add(link.getKey());
$setSelection(selection);
});
- }
- linkModal.show(formDefaults);
+ linkModal.show(formDefaults);
+ } else {
+ context.editor.getEditorState().read(() => {
+ const selection = $getSelection();
+ const text = selection?.getTextContent() || '';
+ const formDefaults = {text};
+ linkModal.show(formDefaults);
+ });
+ }
}
export const link: EditorFormDefinition = {
export class EditorForm extends EditorContainerUiElement {
protected definition: EditorFormDefinition;
protected onCancel: null|(() => void) = null;
+ protected onSuccessfulSubmit: null|(() => void) = null;
constructor(definition: EditorFormDefinition) {
let children: (EditorFormField|EditorUiElement)[] = definition.fields.map(fieldDefinition => {
this.onCancel = callback;
}
+ setOnSuccessfulSubmit(callback: () => void) {
+ this.onSuccessfulSubmit = callback;
+ }
+
protected getFieldByName(name: string): EditorFormField|null {
const search = (children: EditorUiElement[]): EditorFormField|null => {
])
]);
- form.addEventListener('submit', (event) => {
+ form.addEventListener('submit', async (event) => {
event.preventDefault();
const formData = new FormData(form as HTMLFormElement);
- this.definition.action(formData, this.getContext());
+ const result = await this.definition.action(formData, this.getContext());
+ if (result && this.onSuccessfulSubmit) {
+ this.onSuccessfulSubmit();
+ }
});
cancelButton.addEventListener('click', (event) => {
const form = this.getForm();
form.setValues(defaultValues);
form.setOnCancel(this.hide.bind(this));
+ form.setOnSuccessfulSubmit(this.hide.bind(this));
this.getContext().manager.setModalActive(this.key, this);
}