feat: add text alignment options to toolbar with corresponding styles and commands
All checks were successful
CI - Build and Push / Build and Push Docker Image (push) Successful in 17s

This commit is contained in:
2025-10-22 17:48:42 +08:00
parent 79fe02e307
commit d4713fd69e
4 changed files with 92 additions and 1 deletions

View File

@@ -7,12 +7,16 @@ import {
UNDO_COMMAND,
SELECTION_CHANGE_COMMAND,
FORMAT_TEXT_COMMAND,
FORMAT_ELEMENT_COMMAND,
COMMAND_PRIORITY_CRITICAL,
$getSelection,
$isRangeSelection,
$isRootOrShadowRoot,
$isElementNode,
} from 'lexical';
import type { TextFormatType } from 'lexical';
import type { ElementFormatType, TextFormatType } from 'lexical';
import { $getSelectionStyleValueForProperty, $patchStyleText } from '@lexical/selection';
import { $findMatchingParent } from '@lexical/utils';
import DropdownColorPicker from '../ui/DropdownColorPicker';
import '../styles/toolbar.css';
@@ -30,6 +34,7 @@ export default function ToolbarPlugin() {
const [fontColor, setFontColor] = useState('#000');
const [bgColor, setBgColor] = useState('#fff');
const [fontSize, setFontSize] = useState('15px');
const [elementFormat, setElementFormat] = useState<ElementFormatType>('left');
const updateToolbar = useCallback(() => {
const selection = $getSelection();
@@ -51,6 +56,21 @@ export default function ToolbarPlugin() {
setFontSize(
$getSelectionStyleValueForProperty(selection, 'font-size', '15px')
);
// Update element format (alignment)
const node = selection.anchor.getNode();
const element =
node.getKey() === 'root'
? node
: $findMatchingParent(node, (e) => {
const parent = e.getParent();
return parent !== null && $isRootOrShadowRoot(parent);
});
if (element !== null && $isElementNode(element)) {
const formatType = element.getFormatType();
setElementFormat(formatType || 'left');
}
}
}, [editor]);
@@ -225,6 +245,40 @@ export default function ToolbarPlugin() {
onChange={onBgColorSelect}
title="bg color"
/>
<div className="divider" />
<button
onClick={() => {
editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, 'left');
}}
className={'toolbar-item spaced ' + (elementFormat === 'left' ? 'active' : '')}
aria-label="Left Align">
<i className="format left-align" />
</button>
<button
onClick={() => {
editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, 'center');
}}
className={'toolbar-item spaced ' + (elementFormat === 'center' ? 'active' : '')}
aria-label="Center Align">
<i className="format center-align" />
</button>
<button
onClick={() => {
editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, 'right');
}}
className={'toolbar-item spaced ' + (elementFormat === 'right' ? 'active' : '')}
aria-label="Right Align">
<i className="format right-align" />
</button>
<button
onClick={() => {
editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, 'justify');
}}
className={'toolbar-item spaced ' + (elementFormat === 'justify' ? 'active' : '')}
aria-label="Justify Align">
<i className="format justify-align" />
</button>
</div>
);
}

View File

@@ -334,3 +334,20 @@
left: -6px;
cursor: nw-resize;
}
/* Text Alignment */
.editor-text-left {
text-align: left;
}
.editor-text-center {
text-align: center;
}
.editor-text-right {
text-align: right;
}
.editor-text-justify {
text-align: justify;
}

View File

@@ -128,3 +128,19 @@ i.icon.bg-color {
width: 18px;
background-size: contain;
}
i.format.left-align {
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="15" y2="12"/><line x1="3" y1="18" x2="18" y2="18"/></svg>');
}
i.format.center-align {
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="3" y1="6" x2="21" y2="6"/><line x1="6" y1="12" x2="18" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg>');
}
i.format.right-align {
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="3" y1="6" x2="21" y2="6"/><line x1="9" y1="12" x2="21" y2="12"/><line x1="6" y1="18" x2="21" y2="18"/></svg>');
}
i.format.justify-align {
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg>');
}

View File

@@ -27,6 +27,10 @@ const theme: EditorThemeClasses = {
underline: 'editor-text-underline',
strikethrough: 'editor-text-strikethrough',
code: 'editor-text-code',
left: 'editor-text-left',
center: 'editor-text-center',
right: 'editor-text-right',
justify: 'editor-text-justify',
},
code: 'editor-code',
codeHighlight: {},