import { defineCommands, defineKeymap, defineNodeAttr, setNodeAttrsBetween, union, type Extension, type PlainExtension, type Union, } from '@prosekit/core' import type { Command } from '@prosekit/pm/state' export interface TextAlignOptions { /** * The names of node to add the attribute to. * * @example * * ["paragraph", "heading"] */ types: NodeName[] /** * The default value for the attribute. * * @default "left" */ default?: string } function defineTextAlignAttr( type: NodeName, defaultValue: string | null, ) { return defineNodeAttr({ type, attr: 'textAlign', default: defaultValue, splittable: true, toDOM: (value: any) => (value ? ['style', `text-align:${value};`] : null), parseDOM: (node: HTMLElement) => { return node.style.getPropertyValue('text-align') || null }, }) } /** * @internal */ export type TextAlignAttrsExtension = Extension<{ Nodes: { [K in NodeName]: { textAlign: string | null } } }> /** * @internal */ function defineTextAlignAttrs( types: NodeName[], defaultValue: string | null, ): TextAlignAttrsExtension { return union(types.map((type) => defineTextAlignAttr(type, defaultValue))) } /** * @internal */ export function setTextAlign({ types, value, }: { types: string[] value: string | null }): Command { return setNodeAttrsBetween({ type: types, attrs: { textAlign: value } }) } /** * @internal */ export type TextAlignCommandsExtension = Extension<{ Commands: { setTextAlign: [value: string | null] } }> /** * @internal */ export function defineTextAlignCommands( types: string[], ): TextAlignCommandsExtension { return defineCommands({ setTextAlign: (value: string | null) => setTextAlign({ types, value }), }) } /** * @internal */ export function defineTextAlignKeymap(types: string[]): PlainExtension { return defineKeymap({ 'Mod-L': setTextAlign({ types, value: 'left' }), 'Mod-E': setTextAlign({ types, value: 'center' }), 'Mod-R': setTextAlign({ types, value: 'right' }), 'Mod-J': setTextAlign({ types, value: 'justify' }), }) } /** * @internal */ export type TextAlignExtension = Union< [TextAlignAttrsExtension, TextAlignCommandsExtension] > /** * Adds a `textAlign` attribute to the specified nodes. This will be rendered as * a CSS `text-align` style. */ export function defineTextAlign( options: TextAlignOptions, ): TextAlignExtension { return union( defineTextAlignAttrs(options.types, options.default || 'left'), defineTextAlignKeymap(options.types), defineTextAlignCommands(options.types), ) }