@byline/richtext-lexical 3.4.1 → 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.
Files changed (32) hide show
  1. package/dist/field/extensions/admonition/admonition-commands.d.ts +25 -0
  2. package/dist/field/extensions/admonition/admonition-commands.js +4 -0
  3. package/dist/field/extensions/admonition/admonition-extension.d.ts +2 -3
  4. package/dist/field/extensions/admonition/admonition-extension.js +138 -24
  5. package/dist/field/extensions/admonition/admonition-node.css +112 -0
  6. package/dist/field/extensions/admonition/admonition-node.d.ts +26 -14
  7. package/dist/field/extensions/admonition/admonition-node.js +121 -72
  8. package/dist/field/extensions/admonition/index.js +1 -0
  9. package/dist/field/extensions/admonition/node-types.d.ts +10 -4
  10. package/dist/field/extensions/floating-text-format/index.d.ts +8 -1
  11. package/dist/field/extensions/floating-text-format/index.js +6 -4
  12. package/dist/field/markdown/transformers.js +11 -14
  13. package/package.json +5 -5
  14. package/src/field/extensions/admonition/admonition-commands.ts +31 -0
  15. package/src/field/extensions/admonition/admonition-extension.tsx +248 -37
  16. package/src/field/extensions/admonition/admonition-node.css +113 -0
  17. package/src/field/extensions/admonition/admonition-node.tsx +172 -93
  18. package/src/field/extensions/admonition/index.ts +4 -8
  19. package/src/field/extensions/admonition/node-types.ts +10 -10
  20. package/src/field/extensions/floating-text-format/index.tsx +19 -3
  21. package/src/field/markdown/admonition-roundtrip.test.node.ts +110 -0
  22. package/src/field/markdown/transformers.ts +45 -24
  23. package/dist/field/extensions/admonition/admonition-node-component.css +0 -119
  24. package/dist/field/extensions/admonition/admonition-node-component.d.ts +0 -17
  25. package/dist/field/extensions/admonition/admonition-node-component.js +0 -196
  26. package/dist/field/extensions/admonition/icons/danger-icon.d.ts +0 -7
  27. package/dist/field/extensions/admonition/icons/index.d.ts +0 -4
  28. package/dist/field/extensions/admonition/icons/note-icon.d.ts +0 -7
  29. package/dist/field/extensions/admonition/icons/tip-icon.d.ts +0 -7
  30. package/dist/field/extensions/admonition/icons/warning-icon.d.ts +0 -7
  31. package/src/field/extensions/admonition/admonition-node-component.css +0 -115
  32. 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
- }