1 import {$isElementNode, BaseSelection} from "lexical";
2 import {EditorButtonDefinition} from "../../framework/buttons";
3 import alignLeftIcon from "@icons/editor/align-left.svg";
4 import {EditorUiContext} from "../../framework/core";
5 import alignCenterIcon from "@icons/editor/align-center.svg";
6 import alignRightIcon from "@icons/editor/align-right.svg";
7 import alignJustifyIcon from "@icons/editor/align-justify.svg";
8 import ltrIcon from "@icons/editor/direction-ltr.svg";
9 import rtlIcon from "@icons/editor/direction-rtl.svg";
11 $getBlockElementNodesInSelection,
12 $selectionContainsAlignment, $selectionContainsDirection, $selectSingleNode, getLastSelection
13 } from "../../../utils/selection";
14 import {CommonBlockAlignment} from "lexical/nodes/common";
15 import {nodeHasAlignment} from "../../../utils/nodes";
18 function setAlignmentForSelection(context: EditorUiContext, alignment: CommonBlockAlignment): void {
19 const selection = getLastSelection(context.editor);
20 const selectionNodes = selection?.getNodes() || [];
22 // Handle inline node selection alignment
23 if (selectionNodes.length === 1 && $isElementNode(selectionNodes[0]) && selectionNodes[0].isInline() && nodeHasAlignment(selectionNodes[0])) {
24 selectionNodes[0].setAlignment(alignment);
25 $selectSingleNode(selectionNodes[0]);
26 context.manager.triggerFutureStateRefresh();
30 // Handle normal block/range alignment
31 const elements = $getBlockElementNodesInSelection(selection);
32 const alignmentNodes = elements.filter(n => nodeHasAlignment(n));
33 const allAlreadyAligned = alignmentNodes.every(n => n.getAlignment() === alignment);
34 const newAlignment = allAlreadyAligned ? '' : alignment;
35 for (const node of alignmentNodes) {
36 node.setAlignment(newAlignment);
39 context.manager.triggerFutureStateRefresh();
42 function setDirectionForSelection(context: EditorUiContext, direction: 'ltr' | 'rtl'): void {
43 const selection = getLastSelection(context.editor);
45 const elements = $getBlockElementNodesInSelection(selection);
46 for (const node of elements) {
47 node.setDirection(direction);
50 context.manager.triggerFutureStateRefresh();
53 export const alignLeft: EditorButtonDefinition = {
56 action(context: EditorUiContext) {
57 context.editor.update(() => setAlignmentForSelection(context, 'left'));
59 isActive(selection: BaseSelection|null) {
60 return $selectionContainsAlignment(selection, 'left');
64 export const alignCenter: EditorButtonDefinition = {
65 label: 'Align center',
66 icon: alignCenterIcon,
67 action(context: EditorUiContext) {
68 context.editor.update(() => setAlignmentForSelection(context, 'center'));
70 isActive(selection: BaseSelection|null) {
71 return $selectionContainsAlignment(selection, 'center');
75 export const alignRight: EditorButtonDefinition = {
78 action(context: EditorUiContext) {
79 context.editor.update(() => setAlignmentForSelection(context, 'right'));
81 isActive(selection: BaseSelection|null) {
82 return $selectionContainsAlignment(selection, 'right');
86 export const alignJustify: EditorButtonDefinition = {
88 icon: alignJustifyIcon,
89 action(context: EditorUiContext) {
90 context.editor.update(() => setAlignmentForSelection(context, 'justify'));
92 isActive(selection: BaseSelection|null) {
93 return $selectionContainsAlignment(selection, 'justify');
97 export const directionLTR: EditorButtonDefinition = {
98 label: 'Left to right',
100 action(context: EditorUiContext) {
101 context.editor.update(() => setDirectionForSelection(context, 'ltr'));
103 isActive(selection: BaseSelection|null) {
104 return $selectionContainsDirection(selection, 'ltr');
108 export const directionRTL: EditorButtonDefinition = {
109 label: 'Right to left',
111 action(context: EditorUiContext) {
112 context.editor.update(() => setDirectionForSelection(context, 'rtl'));
114 isActive(selection: BaseSelection|null) {
115 return $selectionContainsDirection(selection, 'rtl');