Files
simple-react-app-kit/packages/lexical/src/nodes/date-node.ts
T

165 lines
3.8 KiB
TypeScript
Raw Normal View History

/* eslint-disable no-underscore-dangle -- Lexical nodes use protected __ fields by convention. */
import {
$applyNodeReplacement,
$getSelection,
$isRangeSelection,
TextNode,
} from "lexical"
import type {
DOMConversionMap,
DOMExportOutput,
EditorConfig,
LexicalEditor,
LexicalNode,
NodeKey,
SerializedTextNode,
Spread,
} from "lexical"
import { isISODate } from "../date-value"
export type SerializedDateNode = Spread<
{
date: string
},
SerializedTextNode
>
export class DateNode extends TextNode {
__date: string
static getType(): string {
return "date"
}
static clone(node: DateNode): DateNode {
return new DateNode(node.__date, node.__text, node.__key)
}
static importJSON(node: SerializedDateNode): DateNode {
return $createDateNode(node.date, node.text).updateFromJSON(node)
}
static importDOM(): DOMConversionMap | null {
return {
time: (element) => {
const date =
element.getAttribute("datetime") || element.dataset.lexicalDate
if (!date || !isISODate(date)) return null
return {
conversion: () => ({
node: $createDateNode(date, element.textContent || date),
forChild: () => null,
}),
priority: 4,
}
},
}
}
constructor(date: string, text?: string, key?: NodeKey) {
super(text ?? date, key)
this.__date = date
}
createDOM(config: EditorConfig, editor?: LexicalEditor): HTMLElement {
const element = super.createDOM(config, editor)
element.dataset.lexicalDate = this.__date
return element
}
updateDOM(
previousNode: this,
element: HTMLElement,
config: EditorConfig
): boolean {
const didUpdate = super.updateDOM(previousNode, element, config)
if (previousNode.__date !== this.__date) {
element.dataset.lexicalDate = this.__date
}
return didUpdate
}
exportDOM(): DOMExportOutput {
const element = document.createElement("time")
element.dateTime = this.__date
element.dataset.lexicalDate = this.__date
element.textContent = this.getTextContent()
return { element }
}
exportJSON(): SerializedDateNode {
return {
...super.exportJSON(),
date: this.__date,
type: "date",
version: 1,
}
}
getDate(): string {
return this.getLatest().__date
}
setDate(date: string, text: string): this {
const writable = this.getWritable()
writable.__date = date
writable.__text = text
return writable
}
isTextEntity(): true {
return true
}
}
export function $createDateNode(date: string, text?: string): DateNode {
return $applyNodeReplacement(new DateNode(date, text))
.setMode("token")
.setDetail("unmergable")
}
export function $isDateNode(
node: LexicalNode | null | undefined
): node is DateNode {
return node instanceof DateNode
}
export function $getSelectedDateNode(): DateNode | null {
const selection = $getSelection()
if (!$isRangeSelection(selection)) return null
const anchorNode = selection.anchor.getNode()
const focusNode = selection.focus.getNode()
if (anchorNode === focusNode && $isDateNode(anchorNode)) return anchorNode
const selectedNodes = selection.getNodes()
return selectedNodes.length === 1 && $isDateNode(selectedNodes[0])
? selectedNodes[0]
: null
}
export function $insertOrUpdateDate(
date: string,
text: string
): DateNode | null {
const selectedDateNode = $getSelectedDateNode()
if (selectedDateNode) {
selectedDateNode.setDate(date, text).select(0, text.length)
return selectedDateNode
}
const selection = $getSelection()
if (!$isRangeSelection(selection)) return null
const dateNode = $createDateNode(date, text)
selection.insertNodes([dateNode])
dateNode.select(0, text.length)
return dateNode
}
export { isISODate } from "../date-value"