BookStackApp/BookStack

View on GitHub
resources/js/wysiwyg/ui/defaults/buttons/alignments.ts

Summary

Maintainability
D
2 days
Test Coverage
import {$isElementNode, BaseSelection} from "lexical";
import {EditorButtonDefinition} from "../../framework/buttons";
import alignLeftIcon from "@icons/editor/align-left.svg";
import {EditorUiContext} from "../../framework/core";
import alignCenterIcon from "@icons/editor/align-center.svg";
import alignRightIcon from "@icons/editor/align-right.svg";
import alignJustifyIcon from "@icons/editor/align-justify.svg";
import ltrIcon from "@icons/editor/direction-ltr.svg";
import rtlIcon from "@icons/editor/direction-rtl.svg";
import {
    $getBlockElementNodesInSelection,
    $selectionContainsAlignment, $selectionContainsDirection, $selectSingleNode, $toggleSelection, getLastSelection
} from "../../../utils/selection";
import {CommonBlockAlignment} from "../../../nodes/_common";
import {nodeHasAlignment} from "../../../utils/nodes";


function setAlignmentForSelection(context: EditorUiContext, alignment: CommonBlockAlignment): void {
    const selection = getLastSelection(context.editor);
    const selectionNodes = selection?.getNodes() || [];

    // Handle inline node selection alignment
    if (selectionNodes.length === 1 && $isElementNode(selectionNodes[0]) && selectionNodes[0].isInline() && nodeHasAlignment(selectionNodes[0])) {
        selectionNodes[0].setAlignment(alignment);
        $selectSingleNode(selectionNodes[0]);
        context.manager.triggerFutureStateRefresh();
        return;
    }

    // Handle normal block/range alignment
    const elements = $getBlockElementNodesInSelection(selection);
    const alignmentNodes = elements.filter(n => nodeHasAlignment(n));
    const allAlreadyAligned = alignmentNodes.every(n => n.getAlignment() === alignment);
    const newAlignment = allAlreadyAligned ? '' : alignment;
    for (const node of alignmentNodes) {
        node.setAlignment(newAlignment);
    }

    context.manager.triggerFutureStateRefresh();
}

function setDirectionForSelection(context: EditorUiContext, direction: 'ltr' | 'rtl'): void {
    const selection = getLastSelection(context.editor);

    const elements = $getBlockElementNodesInSelection(selection);
    for (const node of elements) {
        node.setDirection(direction);
    }

    context.manager.triggerFutureStateRefresh();
}

export const alignLeft: EditorButtonDefinition = {
    label: 'Align left',
    icon: alignLeftIcon,
    action(context: EditorUiContext) {
        context.editor.update(() => setAlignmentForSelection(context, 'left'));
    },
    isActive(selection: BaseSelection|null) {
        return $selectionContainsAlignment(selection, 'left');
    }
};

export const alignCenter: EditorButtonDefinition = {
    label: 'Align center',
    icon: alignCenterIcon,
    action(context: EditorUiContext) {
        context.editor.update(() => setAlignmentForSelection(context, 'center'));
    },
    isActive(selection: BaseSelection|null) {
        return $selectionContainsAlignment(selection, 'center');
    }
};

export const alignRight: EditorButtonDefinition = {
    label: 'Align right',
    icon: alignRightIcon,
    action(context: EditorUiContext) {
        context.editor.update(() => setAlignmentForSelection(context, 'right'));
    },
    isActive(selection: BaseSelection|null) {
        return $selectionContainsAlignment(selection, 'right');
    }
};

export const alignJustify: EditorButtonDefinition = {
    label: 'Justify',
    icon: alignJustifyIcon,
    action(context: EditorUiContext) {
        context.editor.update(() => setAlignmentForSelection(context, 'justify'));
    },
    isActive(selection: BaseSelection|null) {
        return $selectionContainsAlignment(selection, 'justify');
    }
};

export const directionLTR: EditorButtonDefinition = {
    label: 'Left to right',
    icon: ltrIcon,
    action(context: EditorUiContext) {
        context.editor.update(() => setDirectionForSelection(context, 'ltr'));
    },
    isActive(selection: BaseSelection|null) {
        return $selectionContainsDirection(selection, 'ltr');
    }
};

export const directionRTL: EditorButtonDefinition = {
    label: 'Right to left',
    icon: rtlIcon,
    action(context: EditorUiContext) {
        context.editor.update(() => setDirectionForSelection(context, 'rtl'));
    },
    isActive(selection: BaseSelection|null) {
        return $selectionContainsDirection(selection, 'rtl');
    }
};