@byline/richtext-lexical 2.3.3 → 2.4.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/lexical-editor.d.ts +10 -1
- package/dist/lexical-editor.js +53 -14
- package/package.json +4 -4
- package/src/lexical-editor.tsx +88 -16
package/dist/lexical-editor.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ import type { EditorConfig } from './field/config/types';
|
|
|
16
16
|
export type LexicalEditorConfigureInput = Omit<EditorConfig, 'extensions'> & {
|
|
17
17
|
extensions: ExtensionsList;
|
|
18
18
|
};
|
|
19
|
+
type ConfigureFn = (config: LexicalEditorConfigureInput) => LexicalEditorConfigureInput;
|
|
19
20
|
/**
|
|
20
21
|
* Returns a `RichTextEditorComponent` with editor settings baked in. Use
|
|
21
22
|
* this at the registration site in your admin config when you want to
|
|
@@ -23,6 +24,13 @@ export type LexicalEditorConfigureInput = Omit<EditorConfig, 'extensions'> & {
|
|
|
23
24
|
* overrides via `RichTextField.editorConfig` continue to take precedence
|
|
24
25
|
* at render time.
|
|
25
26
|
*
|
|
27
|
+
* The returned component is lazy: the editor module graph (RichTextField
|
|
28
|
+
* + every built-in extension + the Lexical core) is dynamically imported
|
|
29
|
+
* on first mount, so callers that merely *reference* `lexicalEditor` at
|
|
30
|
+
* registration time don't drag the editor onto every bundle that touches
|
|
31
|
+
* the registration. The `configure` callback runs once the chunk has
|
|
32
|
+
* loaded, with the same seed it received before.
|
|
33
|
+
*
|
|
26
34
|
* The `configure` callback receives a deep clone of `defaultEditorConfig`
|
|
27
35
|
* with `extensions` populated from `defaultExtensionsList()`. Mutate the
|
|
28
36
|
* clone freely — it's local to this call. Use the chainable
|
|
@@ -49,4 +57,5 @@ export type LexicalEditorConfigureInput = Omit<EditorConfig, 'extensions'> & {
|
|
|
49
57
|
* })
|
|
50
58
|
* ```
|
|
51
59
|
*/
|
|
52
|
-
export declare function lexicalEditor(configure?:
|
|
60
|
+
export declare function lexicalEditor(configure?: ConfigureFn): RichTextEditorComponent;
|
|
61
|
+
export {};
|
package/dist/lexical-editor.js
CHANGED
|
@@ -1,21 +1,60 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
import { Suspense, lazy } from "react";
|
|
3
|
+
import { Shimmer } from "@byline/ui/react";
|
|
4
|
+
let editorBundlePromise = null;
|
|
5
|
+
function loadEditorBundle() {
|
|
6
|
+
if (!editorBundlePromise) editorBundlePromise = (async ()=>{
|
|
7
|
+
const [richtextMod, defaultMod, extensionsMod, lodashMod] = await Promise.all([
|
|
8
|
+
import("./richtext-field.js"),
|
|
9
|
+
import("./field/config/default.js"),
|
|
10
|
+
import("./field/config/default-extensions.js"),
|
|
11
|
+
import("lodash-es")
|
|
12
|
+
]);
|
|
13
|
+
return {
|
|
14
|
+
RichTextField: richtextMod.RichTextField,
|
|
15
|
+
defaultEditorConfig: defaultMod.defaultEditorConfig,
|
|
16
|
+
defaultExtensionsList: extensionsMod.defaultExtensionsList,
|
|
17
|
+
cloneDeep: lodashMod.cloneDeep
|
|
18
|
+
};
|
|
19
|
+
})();
|
|
20
|
+
return editorBundlePromise;
|
|
21
|
+
}
|
|
6
22
|
function lexicalEditor(configure) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
23
|
+
const Lazy = /*#__PURE__*/ lazy(async ()=>{
|
|
24
|
+
const { RichTextField, defaultEditorConfig, defaultExtensionsList, cloneDeep } = await loadEditorBundle();
|
|
25
|
+
let baked;
|
|
26
|
+
if (configure) {
|
|
27
|
+
const seed = {
|
|
28
|
+
...cloneDeep(defaultEditorConfig),
|
|
29
|
+
extensions: defaultExtensionsList()
|
|
30
|
+
};
|
|
31
|
+
baked = configure(seed);
|
|
32
|
+
}
|
|
33
|
+
const Configured = (props)=>/*#__PURE__*/ jsx(RichTextField, {
|
|
34
|
+
...props,
|
|
35
|
+
editorConfig: baked
|
|
36
|
+
});
|
|
37
|
+
return {
|
|
38
|
+
default: Configured
|
|
12
39
|
};
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
40
|
+
});
|
|
41
|
+
const ConfiguredEditor = (props)=>/*#__PURE__*/ jsx(Suspense, {
|
|
42
|
+
fallback: /*#__PURE__*/ jsx(EditorPlaceholder, {}),
|
|
43
|
+
children: /*#__PURE__*/ jsx(Lazy, {
|
|
44
|
+
...props
|
|
45
|
+
})
|
|
18
46
|
});
|
|
19
47
|
return ConfiguredEditor;
|
|
20
48
|
}
|
|
49
|
+
function EditorPlaceholder() {
|
|
50
|
+
return /*#__PURE__*/ jsx("div", {
|
|
51
|
+
className: "byline-field-richtext",
|
|
52
|
+
children: /*#__PURE__*/ jsx("div", {
|
|
53
|
+
className: "byline-field-richtext-body",
|
|
54
|
+
children: /*#__PURE__*/ jsx(Shimmer, {
|
|
55
|
+
height: "35vh"
|
|
56
|
+
})
|
|
57
|
+
})
|
|
58
|
+
});
|
|
59
|
+
}
|
|
21
60
|
export { lexicalEditor };
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"private": false,
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MPL-2.0",
|
|
6
|
-
"version": "2.
|
|
6
|
+
"version": "2.4.0",
|
|
7
7
|
"engines": {
|
|
8
8
|
"node": ">=20.9.0"
|
|
9
9
|
},
|
|
@@ -72,9 +72,9 @@
|
|
|
72
72
|
"npm-run-all": "^4.1.5",
|
|
73
73
|
"prism-react-renderer": "^2.4.1",
|
|
74
74
|
"react-error-boundary": "^6.1.1",
|
|
75
|
-
"@byline/core": "2.
|
|
76
|
-
"@byline/client": "2.
|
|
77
|
-
"@byline/ui": "2.
|
|
75
|
+
"@byline/core": "2.4.0",
|
|
76
|
+
"@byline/client": "2.4.0",
|
|
77
|
+
"@byline/ui": "2.4.0"
|
|
78
78
|
},
|
|
79
79
|
"peerDependencies": {
|
|
80
80
|
"react": "^19.0.0",
|
package/src/lexical-editor.tsx
CHANGED
|
@@ -6,12 +6,11 @@
|
|
|
6
6
|
* Copyright (c) Infonomic Company Limited
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { lazy, Suspense } from 'react'
|
|
10
|
+
|
|
9
11
|
import type { RichTextEditorComponent } from '@byline/core'
|
|
10
|
-
import {
|
|
12
|
+
import { Shimmer } from '@byline/ui/react'
|
|
11
13
|
|
|
12
|
-
import { defaultEditorConfig } from './field/config/default'
|
|
13
|
-
import { defaultExtensionsList } from './field/config/default-extensions'
|
|
14
|
-
import { RichTextField } from './richtext-field'
|
|
15
14
|
import type { ExtensionsList } from './field/config/extensions-list'
|
|
16
15
|
import type { EditorConfig } from './field/config/types'
|
|
17
16
|
|
|
@@ -24,6 +23,46 @@ export type LexicalEditorConfigureInput = Omit<EditorConfig, 'extensions'> & {
|
|
|
24
23
|
extensions: ExtensionsList
|
|
25
24
|
}
|
|
26
25
|
|
|
26
|
+
type ConfigureFn = (config: LexicalEditorConfigureInput) => LexicalEditorConfigureInput
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Bundle of editor internals that are dynamically imported on first
|
|
30
|
+
* mount. Kept narrow so the chunk only carries what the configure step
|
|
31
|
+
* + render need — extension classes the user references directly stay
|
|
32
|
+
* out of this bundle and remain tree-shakeable.
|
|
33
|
+
*/
|
|
34
|
+
interface EditorBundle {
|
|
35
|
+
RichTextField: typeof import('./richtext-field').RichTextField
|
|
36
|
+
defaultEditorConfig: typeof import('./field/config/default').defaultEditorConfig
|
|
37
|
+
defaultExtensionsList: typeof import('./field/config/default-extensions').defaultExtensionsList
|
|
38
|
+
cloneDeep: <T>(value: T) => T
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
let editorBundlePromise: Promise<EditorBundle> | null = null
|
|
42
|
+
|
|
43
|
+
function loadEditorBundle(): Promise<EditorBundle> {
|
|
44
|
+
// Memoize the import so multiple `lexicalEditor()` calls share one chunk
|
|
45
|
+
// load — React.lazy already caches per-wrapper, but consumers that call
|
|
46
|
+
// the factory more than once would otherwise create parallel promises.
|
|
47
|
+
if (!editorBundlePromise) {
|
|
48
|
+
editorBundlePromise = (async () => {
|
|
49
|
+
const [richtextMod, defaultMod, extensionsMod, lodashMod] = await Promise.all([
|
|
50
|
+
import('./richtext-field'),
|
|
51
|
+
import('./field/config/default'),
|
|
52
|
+
import('./field/config/default-extensions'),
|
|
53
|
+
import('lodash-es'),
|
|
54
|
+
])
|
|
55
|
+
return {
|
|
56
|
+
RichTextField: richtextMod.RichTextField,
|
|
57
|
+
defaultEditorConfig: defaultMod.defaultEditorConfig,
|
|
58
|
+
defaultExtensionsList: extensionsMod.defaultExtensionsList,
|
|
59
|
+
cloneDeep: lodashMod.cloneDeep,
|
|
60
|
+
}
|
|
61
|
+
})()
|
|
62
|
+
}
|
|
63
|
+
return editorBundlePromise
|
|
64
|
+
}
|
|
65
|
+
|
|
27
66
|
/**
|
|
28
67
|
* Returns a `RichTextEditorComponent` with editor settings baked in. Use
|
|
29
68
|
* this at the registration site in your admin config when you want to
|
|
@@ -31,6 +70,13 @@ export type LexicalEditorConfigureInput = Omit<EditorConfig, 'extensions'> & {
|
|
|
31
70
|
* overrides via `RichTextField.editorConfig` continue to take precedence
|
|
32
71
|
* at render time.
|
|
33
72
|
*
|
|
73
|
+
* The returned component is lazy: the editor module graph (RichTextField
|
|
74
|
+
* + every built-in extension + the Lexical core) is dynamically imported
|
|
75
|
+
* on first mount, so callers that merely *reference* `lexicalEditor` at
|
|
76
|
+
* registration time don't drag the editor onto every bundle that touches
|
|
77
|
+
* the registration. The `configure` callback runs once the chunk has
|
|
78
|
+
* loaded, with the same seed it received before.
|
|
79
|
+
*
|
|
34
80
|
* The `configure` callback receives a deep clone of `defaultEditorConfig`
|
|
35
81
|
* with `extensions` populated from `defaultExtensionsList()`. Mutate the
|
|
36
82
|
* clone freely — it's local to this call. Use the chainable
|
|
@@ -57,21 +103,47 @@ export type LexicalEditorConfigureInput = Omit<EditorConfig, 'extensions'> & {
|
|
|
57
103
|
* })
|
|
58
104
|
* ```
|
|
59
105
|
*/
|
|
60
|
-
export function lexicalEditor(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
106
|
+
export function lexicalEditor(configure?: ConfigureFn): RichTextEditorComponent {
|
|
107
|
+
const Lazy = lazy(async () => {
|
|
108
|
+
const { RichTextField, defaultEditorConfig, defaultExtensionsList, cloneDeep } =
|
|
109
|
+
await loadEditorBundle()
|
|
110
|
+
|
|
111
|
+
let baked: EditorConfig | undefined
|
|
112
|
+
if (configure) {
|
|
113
|
+
const seed: LexicalEditorConfigureInput = {
|
|
114
|
+
...cloneDeep(defaultEditorConfig),
|
|
115
|
+
extensions: defaultExtensionsList(),
|
|
116
|
+
}
|
|
117
|
+
baked = configure(seed)
|
|
68
118
|
}
|
|
69
|
-
|
|
70
|
-
|
|
119
|
+
|
|
120
|
+
const Configured: RichTextEditorComponent = (props) => (
|
|
121
|
+
<RichTextField {...props} editorConfig={baked} />
|
|
122
|
+
)
|
|
123
|
+
return { default: Configured as React.ComponentType<any> }
|
|
124
|
+
})
|
|
71
125
|
|
|
72
126
|
const ConfiguredEditor: RichTextEditorComponent = (props) => (
|
|
73
|
-
<
|
|
127
|
+
<Suspense fallback={<EditorPlaceholder />}>
|
|
128
|
+
<Lazy {...(props as object)} />
|
|
129
|
+
</Suspense>
|
|
74
130
|
)
|
|
75
|
-
|
|
76
131
|
return ConfiguredEditor
|
|
77
132
|
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Skeleton shown while the editor module graph is loading. Mirrors the
|
|
136
|
+
* `byline-field-richtext` / `byline-field-richtext-body` shell that
|
|
137
|
+
* `RichTextField` renders, and reuses the same `Shimmer` placeholder the
|
|
138
|
+
* inner `EditorField` Suspense uses — so the visible cold-load sequence
|
|
139
|
+
* is just "shimmer → editor" instead of "blank → shimmer → editor".
|
|
140
|
+
*/
|
|
141
|
+
function EditorPlaceholder() {
|
|
142
|
+
return (
|
|
143
|
+
<div className="byline-field-richtext">
|
|
144
|
+
<div className="byline-field-richtext-body">
|
|
145
|
+
<Shimmer height="35vh" />
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
)
|
|
149
|
+
}
|