|
|
@@ -0,0 +1,163 @@
|
|
|
+import { TextNode } from 'lexical'
|
|
|
+import type { EditorConfig, SerializedTextNode } from 'lexical'
|
|
|
+
|
|
|
+export type SerializedVarLabelNode = SerializedTextNode & {
|
|
|
+ type: 'var-label'
|
|
|
+ className: string
|
|
|
+ key?: string
|
|
|
+}
|
|
|
+
|
|
|
+type LabelInfo = {
|
|
|
+ type: 'env' | 'sys' | 'node' | 'custom'
|
|
|
+ value: string
|
|
|
+ nodeName?: string
|
|
|
+}
|
|
|
+
|
|
|
+function parseLabelInfo(rawText: string, className: string): LabelInfo {
|
|
|
+ if (rawText.startsWith('#{') && rawText.endsWith('}')) {
|
|
|
+ const expression = rawText.slice(2, -1)
|
|
|
+ const [prefix, ...rest] = expression.split('.')
|
|
|
+ const value = rest.join('.')
|
|
|
+
|
|
|
+ if (prefix === 'env' && value) {
|
|
|
+ return { type: 'env', value }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (prefix === 'sys' && value) {
|
|
|
+ return { type: 'sys', value }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (prefix && value) {
|
|
|
+ return {
|
|
|
+ type: 'node',
|
|
|
+ nodeName: prefix,
|
|
|
+ value
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (className.includes('env-label')) {
|
|
|
+ return { type: 'env', value: rawText }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (className.includes('sys-label')) {
|
|
|
+ return { type: 'sys', value: rawText }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (className.includes('node-label')) {
|
|
|
+ return { type: 'node', nodeName: 'node', value: rawText }
|
|
|
+ }
|
|
|
+
|
|
|
+ return { type: 'custom', value: rawText }
|
|
|
+}
|
|
|
+
|
|
|
+function createSpan(className: string, text: string) {
|
|
|
+ const span = document.createElement('span')
|
|
|
+ span.className = className
|
|
|
+ span.textContent = text
|
|
|
+ return span
|
|
|
+}
|
|
|
+
|
|
|
+function renderLabel(dom: HTMLElement, className: string, rawText: string) {
|
|
|
+ const info = parseLabelInfo(rawText, className)
|
|
|
+
|
|
|
+ dom.className = `${className} var-label-token`
|
|
|
+ dom.contentEditable = 'false'
|
|
|
+ dom.title =
|
|
|
+ info.type === 'node' && info.nodeName ? `${info.nodeName} / ${info.value}` : info.value || rawText
|
|
|
+
|
|
|
+ const container = document.createElement('span')
|
|
|
+ container.className = 'var-label-content'
|
|
|
+
|
|
|
+ if (info.type === 'env' || info.type === 'sys') {
|
|
|
+ const prefix = document.createElement('span')
|
|
|
+ prefix.className = `var-select__item-prefix ${info.type} text-6px`
|
|
|
+ prefix.appendChild(createSpan('', info.type.toUpperCase()))
|
|
|
+ container.appendChild(prefix)
|
|
|
+ container.appendChild(createSpan('var-label-value text-gray-600', info.value))
|
|
|
+ dom.replaceChildren(container)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if (info.type === 'node') {
|
|
|
+ const prefix = createSpan('var-select__item-prefix node text-10px', 'VAR')
|
|
|
+ container.appendChild(prefix)
|
|
|
+ container.appendChild(createSpan('var-label-node-name', info.nodeName || 'node'))
|
|
|
+ container.appendChild(createSpan('var-label-separator mx-2px text-gray-400', '/'))
|
|
|
+ container.appendChild(createSpan('var-label-value text-gray-600', info.value))
|
|
|
+ dom.replaceChildren(container)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ container.appendChild(createSpan('var-label-value text-gray-600', info.value))
|
|
|
+ dom.replaceChildren(container)
|
|
|
+}
|
|
|
+
|
|
|
+export class VarLabelNode extends TextNode {
|
|
|
+ __className: string
|
|
|
+
|
|
|
+ static getType() {
|
|
|
+ return 'var-label'
|
|
|
+ }
|
|
|
+
|
|
|
+ static clone(node: VarLabelNode) {
|
|
|
+ return new VarLabelNode(node.__className, node.__text, node.__key)
|
|
|
+ }
|
|
|
+
|
|
|
+ static importJSON(serializedNode: SerializedTextNode): VarLabelNode {
|
|
|
+ const { className, text, key } = serializedNode as SerializedVarLabelNode
|
|
|
+ const node = new VarLabelNode(className, text, key)
|
|
|
+ node.setMode('token')
|
|
|
+ return node
|
|
|
+ }
|
|
|
+
|
|
|
+ constructor(className: string, text: string, key?: string) {
|
|
|
+ super(text, key)
|
|
|
+ this.__className = className
|
|
|
+ }
|
|
|
+
|
|
|
+ isTextEntity() {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+
|
|
|
+ canInsertTextBefore() {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ canInsertTextAfter() {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ createDOM(_config: EditorConfig) {
|
|
|
+ const dom = document.createElement('span')
|
|
|
+ renderLabel(dom, this.__className, this.__text)
|
|
|
+ return dom
|
|
|
+ }
|
|
|
+
|
|
|
+ updateDOM(prevNode: VarLabelNode, dom: ChildNode, _config: EditorConfig) {
|
|
|
+ if (!(dom instanceof HTMLElement)) return true
|
|
|
+
|
|
|
+ if (prevNode.__className !== this.__className || prevNode.__text !== this.__text) {
|
|
|
+ renderLabel(dom, this.__className, this.__text)
|
|
|
+ }
|
|
|
+
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ exportJSON(): SerializedVarLabelNode {
|
|
|
+ return {
|
|
|
+ ...super.exportJSON(),
|
|
|
+ type: 'var-label',
|
|
|
+ className: this.__className,
|
|
|
+ version: 1
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+export function $isVarLabelNode(node: unknown): node is VarLabelNode {
|
|
|
+ return node instanceof VarLabelNode
|
|
|
+}
|
|
|
+
|
|
|
+export function $createVarLabelNode(className: string, text: string) {
|
|
|
+ return new VarLabelNode(className, text).setMode('token')
|
|
|
+}
|