@byline/richtext-lexical 3.4.0 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/field/extensions/admonition/admonition-commands.d.ts +25 -0
- package/dist/field/extensions/admonition/admonition-commands.js +4 -0
- package/dist/field/extensions/admonition/admonition-extension.d.ts +2 -3
- package/dist/field/extensions/admonition/admonition-extension.js +138 -24
- package/dist/field/extensions/admonition/admonition-node.css +112 -0
- package/dist/field/extensions/admonition/admonition-node.d.ts +26 -14
- package/dist/field/extensions/admonition/admonition-node.js +121 -72
- package/dist/field/extensions/admonition/index.js +1 -0
- package/dist/field/extensions/admonition/node-types.d.ts +10 -4
- package/dist/field/extensions/floating-text-format/index.d.ts +8 -1
- package/dist/field/extensions/floating-text-format/index.js +6 -4
- package/dist/field/markdown/transformers.js +15 -15
- package/package.json +5 -5
- package/src/field/extensions/admonition/admonition-commands.ts +31 -0
- package/src/field/extensions/admonition/admonition-extension.tsx +248 -37
- package/src/field/extensions/admonition/admonition-node.css +113 -0
- package/src/field/extensions/admonition/admonition-node.tsx +172 -93
- package/src/field/extensions/admonition/index.ts +4 -8
- package/src/field/extensions/admonition/node-types.ts +10 -10
- package/src/field/extensions/floating-text-format/index.tsx +19 -3
- package/src/field/markdown/admonition-roundtrip.test.node.ts +110 -0
- package/src/field/markdown/transformers.ts +51 -25
- package/dist/field/extensions/admonition/admonition-node-component.css +0 -119
- package/dist/field/extensions/admonition/admonition-node-component.d.ts +0 -17
- package/dist/field/extensions/admonition/admonition-node-component.js +0 -196
- package/dist/field/extensions/admonition/icons/danger-icon.d.ts +0 -7
- package/dist/field/extensions/admonition/icons/index.d.ts +0 -4
- package/dist/field/extensions/admonition/icons/note-icon.d.ts +0 -7
- package/dist/field/extensions/admonition/icons/tip-icon.d.ts +0 -7
- package/dist/field/extensions/admonition/icons/warning-icon.d.ts +0 -7
- package/src/field/extensions/admonition/admonition-node-component.css +0 -115
- package/src/field/extensions/admonition/admonition-node-component.tsx +0 -257
|
@@ -1,257 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* This Source Code is subject to the terms of the Mozilla Public
|
|
5
|
-
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
6
|
-
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
7
|
-
*
|
|
8
|
-
* Copyright (c) Infonomic Company Limited
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import type * as React from 'react'
|
|
12
|
-
import { Suspense, useCallback, useEffect, useRef, useState } from 'react'
|
|
13
|
-
|
|
14
|
-
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
|
|
15
|
-
import { LexicalErrorBoundary } from '@lexical/react/LexicalErrorBoundary'
|
|
16
|
-
import { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin'
|
|
17
|
-
import { LexicalNestedComposer } from '@lexical/react/LexicalNestedComposer'
|
|
18
|
-
import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin'
|
|
19
|
-
import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin'
|
|
20
|
-
import { useLexicalNodeSelection } from '@lexical/react/useLexicalNodeSelection'
|
|
21
|
-
import { mergeRegister } from '@lexical/utils'
|
|
22
|
-
import cx from 'classnames'
|
|
23
|
-
import type { BaseSelection, LexicalEditor, NodeKey, NodeSelection, RangeSelection } from 'lexical'
|
|
24
|
-
import {
|
|
25
|
-
$getNodeByKey,
|
|
26
|
-
$getSelection,
|
|
27
|
-
$isNodeSelection,
|
|
28
|
-
$setSelection,
|
|
29
|
-
COMMAND_PRIORITY_LOW,
|
|
30
|
-
KEY_BACKSPACE_COMMAND,
|
|
31
|
-
KEY_DELETE_COMMAND,
|
|
32
|
-
KEY_ENTER_COMMAND,
|
|
33
|
-
KEY_ESCAPE_COMMAND,
|
|
34
|
-
SELECTION_CHANGE_COMMAND,
|
|
35
|
-
} from 'lexical'
|
|
36
|
-
|
|
37
|
-
import { useEditorConfig } from '../../config/editor-config-context'
|
|
38
|
-
import { ContentEditable } from '../../content-editable'
|
|
39
|
-
import { useSharedHistoryContext } from '../../context/shared-history-context'
|
|
40
|
-
import { useSharedOnChange } from '../../context/shared-on-change-context'
|
|
41
|
-
import { Placeholder } from '../../ui/placeholder'
|
|
42
|
-
import { FloatingTextFormatToolbarPlugin } from '../floating-text-format'
|
|
43
|
-
import { FloatingLinkEditorPlugin } from '../link/floating-link-editor'
|
|
44
|
-
import { LinkPlugin } from '../link/link-extension'
|
|
45
|
-
import { AdmonitionModal } from './admonition-modal'
|
|
46
|
-
import { $isAdmonitionNode } from './admonition-node'
|
|
47
|
-
import { DangerIcon, NoteIcon, TipIcon, WarningIcon } from './icons'
|
|
48
|
-
import type { AdmonitionNode } from './admonition-node'
|
|
49
|
-
import type { AdmonitionAttributes, AdmonitionType } from './node-types'
|
|
50
|
-
import type { AdmonitionData } from './types'
|
|
51
|
-
|
|
52
|
-
import './admonition-node-component.css'
|
|
53
|
-
|
|
54
|
-
const icons = {
|
|
55
|
-
note: NoteIcon,
|
|
56
|
-
tip: TipIcon,
|
|
57
|
-
warning: WarningIcon,
|
|
58
|
-
danger: DangerIcon,
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export default function AdmonitionNodeComponent({
|
|
62
|
-
admonitionType,
|
|
63
|
-
title,
|
|
64
|
-
content,
|
|
65
|
-
nodeKey,
|
|
66
|
-
}: {
|
|
67
|
-
admonitionType: AdmonitionType
|
|
68
|
-
title: string
|
|
69
|
-
content: LexicalEditor
|
|
70
|
-
nodeKey: NodeKey
|
|
71
|
-
}): React.JSX.Element {
|
|
72
|
-
const [open, setOpen] = useState(false)
|
|
73
|
-
const [editor] = useLexicalComposerContext()
|
|
74
|
-
const buttonRef = useRef<HTMLButtonElement | null>(null)
|
|
75
|
-
const { historyState } = useSharedHistoryContext()
|
|
76
|
-
const [isSelected, setSelected, _clearSelection] = useLexicalNodeSelection(nodeKey)
|
|
77
|
-
const [selection, setSelection] = useState<RangeSelection | NodeSelection | BaseSelection | null>(
|
|
78
|
-
null
|
|
79
|
-
)
|
|
80
|
-
const { uuid } = useEditorConfig()
|
|
81
|
-
const { onChange } = useSharedOnChange()
|
|
82
|
-
const editorState = editor.getEditorState()
|
|
83
|
-
const activeEditorRef = useRef<LexicalEditor | null>(null)
|
|
84
|
-
const node = editorState.read(() => $getNodeByKey(nodeKey) as AdmonitionNode)
|
|
85
|
-
|
|
86
|
-
const onDelete = useCallback(
|
|
87
|
-
(payload: KeyboardEvent) => {
|
|
88
|
-
if (isSelected && $isNodeSelection($getSelection())) {
|
|
89
|
-
const event: KeyboardEvent = payload
|
|
90
|
-
event.preventDefault()
|
|
91
|
-
const node = $getNodeByKey(nodeKey)
|
|
92
|
-
if ($isAdmonitionNode(node)) {
|
|
93
|
-
node?.remove()
|
|
94
|
-
}
|
|
95
|
-
setSelected(false)
|
|
96
|
-
}
|
|
97
|
-
return false
|
|
98
|
-
},
|
|
99
|
-
[isSelected, nodeKey, setSelected]
|
|
100
|
-
)
|
|
101
|
-
|
|
102
|
-
const onEnter = useCallback(
|
|
103
|
-
(event: KeyboardEvent) => {
|
|
104
|
-
const latestSelection = $getSelection()
|
|
105
|
-
if (
|
|
106
|
-
isSelected &&
|
|
107
|
-
$isNodeSelection(latestSelection) &&
|
|
108
|
-
latestSelection.getNodes().length === 1
|
|
109
|
-
) {
|
|
110
|
-
$setSelection(null)
|
|
111
|
-
event.preventDefault()
|
|
112
|
-
content.focus()
|
|
113
|
-
return true
|
|
114
|
-
}
|
|
115
|
-
return false
|
|
116
|
-
},
|
|
117
|
-
[content, isSelected]
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
const onEscape = useCallback(
|
|
121
|
-
(event: KeyboardEvent) => {
|
|
122
|
-
if (activeEditorRef.current === content || buttonRef.current === event.target) {
|
|
123
|
-
$setSelection(null)
|
|
124
|
-
editor.update(() => {
|
|
125
|
-
setSelected(true)
|
|
126
|
-
const parentRootElement = editor.getRootElement()
|
|
127
|
-
if (parentRootElement !== null) {
|
|
128
|
-
parentRootElement.focus()
|
|
129
|
-
}
|
|
130
|
-
})
|
|
131
|
-
return true
|
|
132
|
-
}
|
|
133
|
-
return false
|
|
134
|
-
},
|
|
135
|
-
[content, editor, setSelected]
|
|
136
|
-
)
|
|
137
|
-
|
|
138
|
-
useEffect(() => {
|
|
139
|
-
let isMounted = true
|
|
140
|
-
const unregister = mergeRegister(
|
|
141
|
-
editor.registerUpdateListener(({ editorState }) => {
|
|
142
|
-
if (isMounted) {
|
|
143
|
-
setSelection(editorState.read(() => $getSelection()))
|
|
144
|
-
}
|
|
145
|
-
}),
|
|
146
|
-
editor.registerCommand(
|
|
147
|
-
SELECTION_CHANGE_COMMAND,
|
|
148
|
-
(_, activeEditor) => {
|
|
149
|
-
activeEditorRef.current = activeEditor
|
|
150
|
-
return false
|
|
151
|
-
},
|
|
152
|
-
COMMAND_PRIORITY_LOW
|
|
153
|
-
),
|
|
154
|
-
editor.registerCommand(KEY_DELETE_COMMAND, onDelete, COMMAND_PRIORITY_LOW),
|
|
155
|
-
editor.registerCommand(KEY_BACKSPACE_COMMAND, onDelete, COMMAND_PRIORITY_LOW),
|
|
156
|
-
editor.registerCommand(KEY_ENTER_COMMAND, onEnter, COMMAND_PRIORITY_LOW),
|
|
157
|
-
editor.registerCommand(KEY_ESCAPE_COMMAND, onEscape, COMMAND_PRIORITY_LOW)
|
|
158
|
-
)
|
|
159
|
-
return () => {
|
|
160
|
-
isMounted = false
|
|
161
|
-
unregister()
|
|
162
|
-
}
|
|
163
|
-
}, [editor, onDelete, onEnter, onEscape])
|
|
164
|
-
|
|
165
|
-
const draggable = isSelected && $isNodeSelection(selection)
|
|
166
|
-
const isFocused = isSelected
|
|
167
|
-
|
|
168
|
-
const handleToggleModal = (): void => {
|
|
169
|
-
if (uuid != null) {
|
|
170
|
-
setOpen(!open)
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
const handleUpdateAdmonition = ({ admonitionType, title }: AdmonitionData): void => {
|
|
175
|
-
setOpen(false)
|
|
176
|
-
if (title != null && admonitionType != null) {
|
|
177
|
-
const admonitionPayload: AdmonitionAttributes = {
|
|
178
|
-
admonitionType,
|
|
179
|
-
title,
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
editor.update(() => {
|
|
183
|
-
node.update(admonitionPayload)
|
|
184
|
-
})
|
|
185
|
-
} else {
|
|
186
|
-
console.error('Error: unable to find image source from document.')
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
const classNames = cx(
|
|
191
|
-
'Admonition__container',
|
|
192
|
-
{ focused: isFocused },
|
|
193
|
-
{ draggable: $isNodeSelection(selection) }
|
|
194
|
-
)
|
|
195
|
-
|
|
196
|
-
const Icon = icons[admonitionType]
|
|
197
|
-
|
|
198
|
-
return (
|
|
199
|
-
<Suspense fallback={null}>
|
|
200
|
-
<div draggable={draggable} className={classNames}>
|
|
201
|
-
<button
|
|
202
|
-
type="button"
|
|
203
|
-
className="admonition-edit-button"
|
|
204
|
-
ref={buttonRef}
|
|
205
|
-
onClick={handleToggleModal}
|
|
206
|
-
>
|
|
207
|
-
Edit
|
|
208
|
-
</button>
|
|
209
|
-
<div className="AdmonitionNode__header">
|
|
210
|
-
<Icon />
|
|
211
|
-
<div>{title}</div>
|
|
212
|
-
</div>
|
|
213
|
-
<div className="AdmonitionNode__content">
|
|
214
|
-
<LexicalNestedComposer initialEditor={content}>
|
|
215
|
-
<OnChangePlugin
|
|
216
|
-
ignoreSelectionChange={true}
|
|
217
|
-
onChange={(_nestedEditorState, _nestedEditor, nestedTags) => {
|
|
218
|
-
// Note: Shared 'onChange' context provider so that
|
|
219
|
-
// caption change events can be registered with the parent
|
|
220
|
-
// editor - in turn triggering the parent editor onChange
|
|
221
|
-
// event, and therefore updating editorState and the field
|
|
222
|
-
// value in Payload (Save Draft and Publish Changes will then
|
|
223
|
-
// become 'enabled' from the caption as well as the parent
|
|
224
|
-
// editor content.)
|
|
225
|
-
|
|
226
|
-
// Parent editor state - not the LexicalNestedComposer in this case
|
|
227
|
-
// although there are other ways that this could be used.
|
|
228
|
-
const editorState = editor.getEditorState()
|
|
229
|
-
if (onChange != null) onChange(editorState, editor, nestedTags)
|
|
230
|
-
}}
|
|
231
|
-
/>
|
|
232
|
-
<LinkPlugin />
|
|
233
|
-
<FloatingLinkEditorPlugin />
|
|
234
|
-
<FloatingTextFormatToolbarPlugin />
|
|
235
|
-
<HistoryPlugin externalHistoryState={historyState} />
|
|
236
|
-
<RichTextPlugin
|
|
237
|
-
contentEditable={<ContentEditable className="AdmonitionNode__contentEditable" />}
|
|
238
|
-
placeholder={
|
|
239
|
-
<Placeholder className="Admonition__placeholder">Enter some text...</Placeholder>
|
|
240
|
-
}
|
|
241
|
-
ErrorBoundary={LexicalErrorBoundary}
|
|
242
|
-
/>
|
|
243
|
-
</LexicalNestedComposer>
|
|
244
|
-
</div>
|
|
245
|
-
</div>
|
|
246
|
-
|
|
247
|
-
{uuid != null && uuid.length > 0 && (
|
|
248
|
-
<AdmonitionModal
|
|
249
|
-
open={open}
|
|
250
|
-
onClose={handleToggleModal}
|
|
251
|
-
onSubmit={handleUpdateAdmonition}
|
|
252
|
-
data={{ title, admonitionType }}
|
|
253
|
-
/>
|
|
254
|
-
)}
|
|
255
|
-
</Suspense>
|
|
256
|
-
)
|
|
257
|
-
}
|