Skip to content

Commit

Permalink
Lexical: Added details toolbar
Browse files Browse the repository at this point in the history
Includes unwrap and toggle open actions.
  • Loading branch information
ssddanbrown committed Dec 15, 2024
1 parent 3f86937 commit 5887322
Show file tree
Hide file tree
Showing 7 changed files with 144 additions and 6 deletions.
1 change: 1 addition & 0 deletions resources/icons/editor/details-toggle.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 32 additions & 1 deletion resources/js/wysiwyg/lexical/rich-text/LexicalDetailsNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export type SerializedDetailsNode = Spread<{
export class DetailsNode extends ElementNode {
__id: string = '';
__summary: string = '';
__open: boolean = false;

static getType() {
return 'details';
Expand All @@ -43,11 +44,22 @@ export class DetailsNode extends ElementNode {
return self.__summary;
}

setOpen(open: boolean) {
const self = this.getWritable();
self.__open = open;
}

getOpen(): boolean {
const self = this.getLatest();
return self.__open;
}

static clone(node: DetailsNode): DetailsNode {
const newNode = new DetailsNode(node.__key);
newNode.__id = node.__id;
newNode.__dir = node.__dir;
newNode.__summary = node.__summary;
newNode.__open = node.__open;
return newNode;
}

Expand All @@ -61,17 +73,34 @@ export class DetailsNode extends ElementNode {
el.setAttribute('dir', this.__dir);
}

if (this.__open) {
el.setAttribute('open', 'true');
}

const summary = document.createElement('summary');
summary.textContent = this.__summary;
summary.setAttribute('contenteditable', 'false');
summary.addEventListener('click', event => {
event.preventDefault();
_editor.update(() => {
this.select();
})
});

el.append(summary);

return el;
}

updateDOM(prevNode: DetailsNode, dom: HTMLElement) {

if (prevNode.__open !== this.__open) {
dom.toggleAttribute('open', this.__open);
}

return prevNode.__id !== this.__id
|| prevNode.__dir !== this.__dir;
|| prevNode.__dir !== this.__dir
|| prevNode.__summary !== this.__summary;
}

static importDOM(): DOMConversionMap|null {
Expand Down Expand Up @@ -114,6 +143,8 @@ export class DetailsNode extends ElementNode {
elem.removeAttribute('contenteditable');
}

element.removeAttribute('open');

return {element};
}

Expand Down
59 changes: 58 additions & 1 deletion resources/js/wysiwyg/ui/defaults/buttons/objects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ import editIcon from "@icons/edit.svg";
import diagramIcon from "@icons/editor/diagram.svg";
import {$createDiagramNode, DiagramNode} from "@lexical/rich-text/LexicalDiagramNode";
import detailsIcon from "@icons/editor/details.svg";
import detailsToggleIcon from "@icons/editor/details-toggle.svg";
import tableDeleteIcon from "@icons/editor/table-delete.svg";
import tagIcon from "@icons/tag.svg";
import mediaIcon from "@icons/editor/media.svg";
import {$createDetailsNode, $isDetailsNode} from "@lexical/rich-text/LexicalDetailsNode";
import {$isMediaNode, MediaNode} from "@lexical/rich-text/LexicalMediaNode";
Expand All @@ -29,7 +32,7 @@ import {
} from "../../../utils/selection";
import {$isDiagramNode, $openDrawingEditorForNode, showDiagramManagerForInsert} from "../../../utils/diagrams";
import {$createLinkedImageNodeFromImageData, showImageManager} from "../../../utils/images";
import {$showImageForm, $showLinkForm} from "../forms/objects";
import {$showDetailsForm, $showImageForm, $showLinkForm} from "../forms/objects";
import {formatCodeBlock} from "../../../utils/formats";

export const link: EditorButtonDefinition = {
Expand Down Expand Up @@ -216,4 +219,58 @@ export const details: EditorButtonDefinition = {
isActive(selection: BaseSelection | null): boolean {
return $selectionContainsNodeType(selection, $isDetailsNode);
}
}

export const detailsEditLabel: EditorButtonDefinition = {
label: 'Edit label',
icon: tagIcon,
action(context: EditorUiContext) {
context.editor.getEditorState().read(() => {
const details = $getNodeFromSelection($getSelection(), $isDetailsNode);
if ($isDetailsNode(details)) {
$showDetailsForm(details, context);
}
})
},
isActive(selection: BaseSelection | null): boolean {
return false;
}
}

export const detailsToggle: EditorButtonDefinition = {
label: 'Toggle open/closed',
icon: detailsToggleIcon,
action(context: EditorUiContext) {
context.editor.update(() => {
const details = $getNodeFromSelection($getSelection(), $isDetailsNode);
if ($isDetailsNode(details)) {
details.setOpen(!details.getOpen());
context.manager.triggerLayoutUpdate();
}
})
},
isActive(selection: BaseSelection | null): boolean {
return false;
}
}

export const detailsUnwrap: EditorButtonDefinition = {
label: 'Unwrap',
icon: tableDeleteIcon,
action(context: EditorUiContext) {
context.editor.update(() => {
const details = $getNodeFromSelection($getSelection(), $isDetailsNode);
if ($isDetailsNode(details)) {
const children = details.getChildren();
for (const child of children) {
details.insertBefore(child);
}
details.remove();
context.manager.triggerLayoutUpdate();
}
})
},
isActive(selection: BaseSelection | null): boolean {
return false;
}
}
34 changes: 34 additions & 0 deletions resources/js/wysiwyg/ui/defaults/forms/objects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import searchIcon from "@icons/search.svg";
import {showLinkSelector} from "../../../utils/links";
import {LinkField} from "../../framework/blocks/link-field";
import {insertOrUpdateLink} from "../../../utils/formats";
import {$isDetailsNode, DetailsNode} from "@lexical/rich-text/LexicalDetailsNode";

export function $showImageForm(image: ImageNode, context: EditorUiContext) {
const imageModal: EditorFormModal = context.manager.createModal('image');
Expand Down Expand Up @@ -262,4 +263,37 @@ export const media: EditorFormDefinition = {
}
},
],
};

export function $showDetailsForm(details: DetailsNode|null, context: EditorUiContext) {
const linkModal = context.manager.createModal('details');
if (!details) {
return;
}

linkModal.show({
summary: details.getSummary()
});
}

export const details: EditorFormDefinition = {
submitText: 'Save',
async action(formData, context: EditorUiContext) {
context.editor.update(() => {
const node = $getNodeFromSelection($getSelection(), $isDetailsNode);
const summary = (formData.get('summary') || '').toString().trim();
if ($isDetailsNode(node)) {
node.setSummary(summary);
}
});

return true;
},
fields: [
{
label: 'Toggle label',
name: 'summary',
type: 'text',
},
],
};
6 changes: 5 additions & 1 deletion resources/js/wysiwyg/ui/defaults/modals.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {EditorFormModalDefinition} from "../framework/modals";
import {image, link, media} from "./forms/objects";
import {details, image, link, media} from "./forms/objects";
import {source} from "./forms/controls";
import {cellProperties, rowProperties, tableProperties} from "./forms/tables";

Expand Down Expand Up @@ -32,4 +32,8 @@ export const modals: Record<string, EditorFormModalDefinition> = {
title: 'Table Properties',
form: tableProperties,
},
details: {
title: 'Edit collapsible block',
form: details,
}
};
7 changes: 5 additions & 2 deletions resources/js/wysiwyg/ui/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {LexicalEditor} from "lexical";
import {
getCodeToolbarContent,
getCodeToolbarContent, getDetailsToolbarContent,
getImageToolbarContent,
getLinkToolbarContent,
getMainEditorFullToolbar, getTableToolbarContent
Expand Down Expand Up @@ -56,14 +56,17 @@ export function buildEditorUI(container: HTMLElement, element: HTMLElement, scro
selector: '.editor-code-block-wrap',
content: getCodeToolbarContent(),
});

manager.registerContextToolbar('table', {
selector: 'td,th',
content: getTableToolbarContent(),
displayTargetLocator(originalTarget: HTMLElement): HTMLElement {
return originalTarget.closest('table') as HTMLTableElement;
}
});
manager.registerContextToolbar('details', {
selector: 'details',
content: getDetailsToolbarContent(),
});

// Register image decorator listener
manager.registerDecoratorType('code', CodeBlockDecorator);
Expand Down
10 changes: 9 additions & 1 deletion resources/js/wysiwyg/ui/toolbars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ import {
} from "./defaults/buttons/lists";
import {
codeBlock,
details,
details, detailsEditLabel, detailsToggle, detailsUnwrap,
diagram, diagramManager,
editCodeBlock,
horizontalRule,
Expand Down Expand Up @@ -253,4 +253,12 @@ export function getTableToolbarContent(): EditorUiElement[] {
new EditorButton(deleteColumn),
]),
];
}

export function getDetailsToolbarContent(): EditorUiElement[] {
return [
new EditorButton(detailsEditLabel),
new EditorButton(detailsToggle),
new EditorButton(detailsUnwrap),
];
}

0 comments on commit 5887322

Please sign in to comment.