@byline/richtext-lexical 3.0.2 → 3.1.1
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/config.d.ts +45 -0
- package/dist/config.js +4 -0
- package/dist/field/config/built-in-extension-names.d.ts +51 -0
- package/dist/field/config/built-in-extension-names.js +17 -0
- package/dist/field/config/extensions-list.d.ts +17 -7
- package/dist/field/config/extensions-list.js +6 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +11 -5
- package/src/config.ts +65 -0
- package/src/field/config/built-in-extension-names.test.node.ts +44 -0
- package/src/field/config/built-in-extension-names.ts +53 -0
- package/src/field/config/extensions-list.ts +28 -10
- package/src/index.ts +4 -0
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Config-only surface for `@byline/richtext-lexical`.
|
|
10
|
+
*
|
|
11
|
+
* This entry is intentionally **light**: it carries the `lexicalEditor`
|
|
12
|
+
* registration factory (which dynamic-imports the editor runtime on first
|
|
13
|
+
* mount), the config types, the built-in extension **names**, and the
|
|
14
|
+
* light toolbar-authoring primitives — but NOT `RichTextField` /
|
|
15
|
+
* `EditorField` / `Nodes` or the heavy *content* extension classes
|
|
16
|
+
* (Table, InlineImage, Admonition, …), all of which statically pull React
|
|
17
|
+
* plugins, nodes, and the Lexical core. Import from here at registration
|
|
18
|
+
* sites (e.g. an admin/client config that you want to evaluate eagerly)
|
|
19
|
+
* so referencing the editor doesn't drag the editor module graph into the
|
|
20
|
+
* importing bundle.
|
|
21
|
+
*
|
|
22
|
+
* To toggle a built-in extension without importing its heavy class, use
|
|
23
|
+
* the name-based form of `extensions.remove/has/replace` with
|
|
24
|
+
* `builtInExtensions.*`:
|
|
25
|
+
*
|
|
26
|
+
* ```ts
|
|
27
|
+
* import { builtInExtensions, lexicalEditor } from '@byline/richtext-lexical/config'
|
|
28
|
+
*
|
|
29
|
+
* lexicalEditor((c) => {
|
|
30
|
+
* c.extensions.remove(builtInExtensions.FloatingTextFormat)
|
|
31
|
+
* return c
|
|
32
|
+
* })
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* Need an actual extension class (to `.add(...)` it, or pass it to
|
|
36
|
+
* `configExtension`)? Import it from the `.` barrel instead — and accept
|
|
37
|
+
* that doing so pulls that extension's runtime into the bundle.
|
|
38
|
+
*/
|
|
39
|
+
export { type BuiltInExtensionName, builtInExtensions, } from './field/config/built-in-extension-names';
|
|
40
|
+
export { type BylineToolbarConfig, BylineToolbarExtension, type BylineToolbarItem, type BylineToolbarPlacement, selectToolbarItems, } from './field/extensions/byline-toolbar';
|
|
41
|
+
export { ToolbarActiveEditorProvider, useToolbarActiveEditor, } from './field/plugins/toolbar-plugin/toolbar-active-editor';
|
|
42
|
+
export { lexicalEditor } from './lexical-editor';
|
|
43
|
+
export type { ExtensionsList } from './field/config/extensions-list';
|
|
44
|
+
export type { EditorConfig, EditorSettings, EditorSettingsOverride } from './field/config/types';
|
|
45
|
+
export type { LexicalEditorConfigureInput } from './lexical-editor';
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { builtInExtensions } from "./field/config/built-in-extension-names.js";
|
|
2
|
+
export { BylineToolbarExtension, selectToolbarItems } from "./field/extensions/byline-toolbar/index.js";
|
|
3
|
+
export { ToolbarActiveEditorProvider, useToolbarActiveEditor } from "./field/plugins/toolbar-plugin/toolbar-active-editor.js";
|
|
4
|
+
export { lexicalEditor } from "./lexical-editor.js";
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* The `name` of every built-in extension, as a plain string map.
|
|
10
|
+
*
|
|
11
|
+
* This module is **React-free and runtime-free** — it carries only the
|
|
12
|
+
* identifying strings, never the extension classes (which statically
|
|
13
|
+
* import their React plugins / nodes). That lets config-only code paths
|
|
14
|
+
* (the `@byline/richtext-lexical/config` subpath) reference a built-in
|
|
15
|
+
* for `extensions.remove(...)` / `.has(...)` / `.replace(...)` without
|
|
16
|
+
* dragging the editor runtime into the importing bundle.
|
|
17
|
+
*
|
|
18
|
+
* ```ts
|
|
19
|
+
* import { builtInExtensions, lexicalEditor } from '@byline/richtext-lexical/config'
|
|
20
|
+
*
|
|
21
|
+
* lexicalEditor((c) => {
|
|
22
|
+
* c.extensions.remove(builtInExtensions.FloatingTextFormat)
|
|
23
|
+
* return c
|
|
24
|
+
* })
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* Each value MUST stay in sync with the corresponding extension's
|
|
28
|
+
* `defineExtension({ name })` — they share the `@byline/richtext-lexical/*`
|
|
29
|
+
* convention (suffix === map key). `built-in-extension-names.test.node.ts`
|
|
30
|
+
* guards this module's own integrity (convention, uniqueness); the mirror
|
|
31
|
+
* against the live extension `name`s is exercised by the editor's
|
|
32
|
+
* jsdom/integration coverage.
|
|
33
|
+
*/
|
|
34
|
+
export declare const builtInExtensions: {
|
|
35
|
+
readonly Admonition: "@byline/richtext-lexical/Admonition";
|
|
36
|
+
readonly AutoEmbed: "@byline/richtext-lexical/AutoEmbed";
|
|
37
|
+
readonly AutoLink: "@byline/richtext-lexical/AutoLink";
|
|
38
|
+
readonly CodeHighlight: "@byline/richtext-lexical/CodeHighlight";
|
|
39
|
+
readonly FloatingTextFormat: "@byline/richtext-lexical/FloatingTextFormat";
|
|
40
|
+
readonly FloatingUI: "@byline/richtext-lexical/FloatingUI";
|
|
41
|
+
readonly HorizontalRule: "@byline/richtext-lexical/HorizontalRule";
|
|
42
|
+
readonly InlineImage: "@byline/richtext-lexical/InlineImage";
|
|
43
|
+
readonly Layout: "@byline/richtext-lexical/Layout";
|
|
44
|
+
readonly Link: "@byline/richtext-lexical/Link";
|
|
45
|
+
readonly Table: "@byline/richtext-lexical/Table";
|
|
46
|
+
readonly Toolbar: "@byline/richtext-lexical/Toolbar";
|
|
47
|
+
readonly Vimeo: "@byline/richtext-lexical/Vimeo";
|
|
48
|
+
readonly YouTube: "@byline/richtext-lexical/YouTube";
|
|
49
|
+
};
|
|
50
|
+
/** Union of the built-in extension `name` strings. */
|
|
51
|
+
export type BuiltInExtensionName = (typeof builtInExtensions)[keyof typeof builtInExtensions];
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const builtInExtensions = {
|
|
2
|
+
Admonition: '@byline/richtext-lexical/Admonition',
|
|
3
|
+
AutoEmbed: '@byline/richtext-lexical/AutoEmbed',
|
|
4
|
+
AutoLink: '@byline/richtext-lexical/AutoLink',
|
|
5
|
+
CodeHighlight: '@byline/richtext-lexical/CodeHighlight',
|
|
6
|
+
FloatingTextFormat: '@byline/richtext-lexical/FloatingTextFormat',
|
|
7
|
+
FloatingUI: '@byline/richtext-lexical/FloatingUI',
|
|
8
|
+
HorizontalRule: '@byline/richtext-lexical/HorizontalRule',
|
|
9
|
+
InlineImage: '@byline/richtext-lexical/InlineImage',
|
|
10
|
+
Layout: '@byline/richtext-lexical/Layout',
|
|
11
|
+
Link: '@byline/richtext-lexical/Link',
|
|
12
|
+
Table: '@byline/richtext-lexical/Table',
|
|
13
|
+
Toolbar: '@byline/richtext-lexical/Toolbar',
|
|
14
|
+
Vimeo: '@byline/richtext-lexical/Vimeo',
|
|
15
|
+
YouTube: '@byline/richtext-lexical/YouTube'
|
|
16
|
+
};
|
|
17
|
+
export { builtInExtensions };
|
|
@@ -22,12 +22,19 @@ export declare class ExtensionsList {
|
|
|
22
22
|
/** Append `extension` to the end of the list. */
|
|
23
23
|
add(extension: AnyLexicalExtensionArgument): this;
|
|
24
24
|
/**
|
|
25
|
-
* Remove every entry whose name matches
|
|
26
|
-
* the
|
|
25
|
+
* Remove every entry whose name matches the target. Accepts either an
|
|
26
|
+
* extension object (matched by `.name`) or the name string directly —
|
|
27
|
+
* the string form lets config-only code remove a built-in via
|
|
28
|
+
* `builtInExtensions.*` without importing the heavy extension class.
|
|
29
|
+
* No-op when the extension isn't present.
|
|
27
30
|
*/
|
|
28
|
-
remove(extension: AnyLexicalExtension): this;
|
|
29
|
-
/**
|
|
30
|
-
|
|
31
|
+
remove(extension: AnyLexicalExtension | string): this;
|
|
32
|
+
/**
|
|
33
|
+
* Replace `oldExtension` with `newExtension`, preserving position.
|
|
34
|
+
* `oldExtension` may be an extension object or its name string; the
|
|
35
|
+
* replacement must be a real extension argument.
|
|
36
|
+
*/
|
|
37
|
+
replace(oldExtension: AnyLexicalExtension | string, newExtension: AnyLexicalExtensionArgument): this;
|
|
31
38
|
/**
|
|
32
39
|
* Re-wrap an existing extension entry with a fresh `configExtension`
|
|
33
40
|
* binding the supplied config. If the extension isn't present, it is
|
|
@@ -35,8 +42,11 @@ export declare class ExtensionsList {
|
|
|
35
42
|
* is replaced rather than nested.
|
|
36
43
|
*/
|
|
37
44
|
configure<Extension extends AnyLexicalExtension>(extension: Extension, config: Partial<LexicalExtensionConfig<Extension>>): this;
|
|
38
|
-
/**
|
|
39
|
-
|
|
45
|
+
/**
|
|
46
|
+
* True when an entry with the same `name` is present. Accepts an
|
|
47
|
+
* extension object or its name string.
|
|
48
|
+
*/
|
|
49
|
+
has(extension: AnyLexicalExtension | string): boolean;
|
|
40
50
|
/** Independent copy — safe to hand to a configure callback. */
|
|
41
51
|
clone(): ExtensionsList;
|
|
42
52
|
/** The list as a plain array, ready for `defineExtension({ dependencies })`. */
|
|
@@ -5,12 +5,12 @@ class ExtensionsList {
|
|
|
5
5
|
return this;
|
|
6
6
|
}
|
|
7
7
|
remove(extension) {
|
|
8
|
-
const targetName = extension
|
|
8
|
+
const targetName = resolveTargetName(extension);
|
|
9
9
|
this.items = this.items.filter((item)=>extensionName(item) !== targetName);
|
|
10
10
|
return this;
|
|
11
11
|
}
|
|
12
12
|
replace(oldExtension, newExtension) {
|
|
13
|
-
const targetName = oldExtension
|
|
13
|
+
const targetName = resolveTargetName(oldExtension);
|
|
14
14
|
let replaced = false;
|
|
15
15
|
this.items = this.items.map((item)=>{
|
|
16
16
|
if (!replaced && extensionName(item) === targetName) {
|
|
@@ -27,7 +27,7 @@ class ExtensionsList {
|
|
|
27
27
|
return this.replace(extension, wrapped);
|
|
28
28
|
}
|
|
29
29
|
has(extension) {
|
|
30
|
-
const targetName = extension
|
|
30
|
+
const targetName = resolveTargetName(extension);
|
|
31
31
|
return this.items.some((item)=>extensionName(item) === targetName);
|
|
32
32
|
}
|
|
33
33
|
clone() {
|
|
@@ -44,6 +44,9 @@ class ExtensionsList {
|
|
|
44
44
|
];
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
|
+
function resolveTargetName(target) {
|
|
48
|
+
return 'string' == typeof target ? target : target.name;
|
|
49
|
+
}
|
|
47
50
|
function extensionName(item) {
|
|
48
51
|
if (Array.isArray(item)) {
|
|
49
52
|
const head = item[0];
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export type { SerializedEditor, SerializedEditorState, SerializedElementNode, SerializedLexicalNode, SerializedRootNode, SerializedTextNode, } from 'lexical';
|
|
2
|
+
export { type BuiltInExtensionName, builtInExtensions, } from './field/config/built-in-extension-names';
|
|
2
3
|
export { defaultEditorConfig } from './field/config/default';
|
|
3
4
|
export { defaultClientEditorConfig, defaultExtensionsArray, defaultExtensionsList, } from './field/config/default-extensions';
|
|
4
5
|
export { ExtensionsList } from './field/config/extensions-list';
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { builtInExtensions } from "./field/config/built-in-extension-names.js";
|
|
1
2
|
export { defaultEditorConfig } from "./field/config/default.js";
|
|
2
3
|
export { defaultClientEditorConfig, defaultExtensionsArray, defaultExtensionsList } from "./field/config/default-extensions.js";
|
|
3
4
|
export { ExtensionsList } from "./field/config/extensions-list.js";
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"private": false,
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MPL-2.0",
|
|
6
|
-
"version": "3.
|
|
6
|
+
"version": "3.1.1",
|
|
7
7
|
"engines": {
|
|
8
8
|
"node": ">=20.9.0"
|
|
9
9
|
},
|
|
@@ -42,6 +42,12 @@
|
|
|
42
42
|
"main": "./dist/index.js",
|
|
43
43
|
"default": "./dist/index.js"
|
|
44
44
|
},
|
|
45
|
+
"./config": {
|
|
46
|
+
"types": "./dist/config.d.ts",
|
|
47
|
+
"import": "./dist/config.js",
|
|
48
|
+
"main": "./dist/config.js",
|
|
49
|
+
"default": "./dist/config.js"
|
|
50
|
+
},
|
|
45
51
|
"./server": {
|
|
46
52
|
"types": "./dist/server.d.ts",
|
|
47
53
|
"import": "./dist/server.js",
|
|
@@ -72,10 +78,10 @@
|
|
|
72
78
|
"npm-run-all": "^4.1.5",
|
|
73
79
|
"prism-react-renderer": "^2.4.1",
|
|
74
80
|
"react-error-boundary": "^6.1.1",
|
|
75
|
-
"@byline/
|
|
76
|
-
"@byline/
|
|
77
|
-
"@byline/
|
|
78
|
-
"@byline/
|
|
81
|
+
"@byline/client": "3.1.1",
|
|
82
|
+
"@byline/ui": "3.1.1",
|
|
83
|
+
"@byline/core": "3.1.1",
|
|
84
|
+
"@byline/admin": "3.1.1"
|
|
79
85
|
},
|
|
80
86
|
"peerDependencies": {
|
|
81
87
|
"react": "^19.0.0",
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Config-only surface for `@byline/richtext-lexical`.
|
|
11
|
+
*
|
|
12
|
+
* This entry is intentionally **light**: it carries the `lexicalEditor`
|
|
13
|
+
* registration factory (which dynamic-imports the editor runtime on first
|
|
14
|
+
* mount), the config types, the built-in extension **names**, and the
|
|
15
|
+
* light toolbar-authoring primitives — but NOT `RichTextField` /
|
|
16
|
+
* `EditorField` / `Nodes` or the heavy *content* extension classes
|
|
17
|
+
* (Table, InlineImage, Admonition, …), all of which statically pull React
|
|
18
|
+
* plugins, nodes, and the Lexical core. Import from here at registration
|
|
19
|
+
* sites (e.g. an admin/client config that you want to evaluate eagerly)
|
|
20
|
+
* so referencing the editor doesn't drag the editor module graph into the
|
|
21
|
+
* importing bundle.
|
|
22
|
+
*
|
|
23
|
+
* To toggle a built-in extension without importing its heavy class, use
|
|
24
|
+
* the name-based form of `extensions.remove/has/replace` with
|
|
25
|
+
* `builtInExtensions.*`:
|
|
26
|
+
*
|
|
27
|
+
* ```ts
|
|
28
|
+
* import { builtInExtensions, lexicalEditor } from '@byline/richtext-lexical/config'
|
|
29
|
+
*
|
|
30
|
+
* lexicalEditor((c) => {
|
|
31
|
+
* c.extensions.remove(builtInExtensions.FloatingTextFormat)
|
|
32
|
+
* return c
|
|
33
|
+
* })
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* Need an actual extension class (to `.add(...)` it, or pass it to
|
|
37
|
+
* `configExtension`)? Import it from the `.` barrel instead — and accept
|
|
38
|
+
* that doing so pulls that extension's runtime into the bundle.
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
export {
|
|
42
|
+
type BuiltInExtensionName,
|
|
43
|
+
builtInExtensions,
|
|
44
|
+
} from './field/config/built-in-extension-names'
|
|
45
|
+
// Light extension-authoring primitives — the toolbar coordination
|
|
46
|
+
// extension, its selectors/types, and the active-editor hook. These carry
|
|
47
|
+
// no React plugin/node runtime (only `lexical` + a React context), so a
|
|
48
|
+
// third-party extension (e.g. `@byline/ai`'s Lexical extension) can
|
|
49
|
+
// declare a toolbar contribution and dispatch commands without pulling
|
|
50
|
+
// the editor's content runtime into its bundle.
|
|
51
|
+
export {
|
|
52
|
+
type BylineToolbarConfig,
|
|
53
|
+
BylineToolbarExtension,
|
|
54
|
+
type BylineToolbarItem,
|
|
55
|
+
type BylineToolbarPlacement,
|
|
56
|
+
selectToolbarItems,
|
|
57
|
+
} from './field/extensions/byline-toolbar'
|
|
58
|
+
export {
|
|
59
|
+
ToolbarActiveEditorProvider,
|
|
60
|
+
useToolbarActiveEditor,
|
|
61
|
+
} from './field/plugins/toolbar-plugin/toolbar-active-editor'
|
|
62
|
+
export { lexicalEditor } from './lexical-editor'
|
|
63
|
+
export type { ExtensionsList } from './field/config/extensions-list'
|
|
64
|
+
export type { EditorConfig, EditorSettings, EditorSettingsOverride } from './field/config/types'
|
|
65
|
+
export type { LexicalEditorConfigureInput } from './lexical-editor'
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { describe, expect, it } from 'vitest'
|
|
10
|
+
|
|
11
|
+
import { builtInExtensions } from './built-in-extension-names'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Node-safe self-consistency checks for the `builtInExtensions` name map.
|
|
15
|
+
*
|
|
16
|
+
* The map deliberately carries strings only (no extension imports), so
|
|
17
|
+
* this test cannot diff against the live `defineExtension({ name })`
|
|
18
|
+
* values without dragging the React-heavy editor graph into a node test.
|
|
19
|
+
* Instead it guards the map's own integrity — the `@byline/richtext-lexical/*`
|
|
20
|
+
* convention, uniqueness, and that each key matches the suffix of its
|
|
21
|
+
* value. Each value must still mirror the corresponding extension's
|
|
22
|
+
* `name`; that mirror is asserted by the editor's jsdom/integration
|
|
23
|
+
* coverage, not here.
|
|
24
|
+
*/
|
|
25
|
+
describe('builtInExtensions name map', () => {
|
|
26
|
+
const entries = Object.entries(builtInExtensions)
|
|
27
|
+
|
|
28
|
+
it('namespaces every value under @byline/richtext-lexical/', () => {
|
|
29
|
+
for (const [, value] of entries) {
|
|
30
|
+
expect(value).toMatch(/^@byline\/richtext-lexical\/[A-Za-z]+$/)
|
|
31
|
+
}
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('uses each value exactly once', () => {
|
|
35
|
+
const values = entries.map(([, value]) => value)
|
|
36
|
+
expect(new Set(values).size).toBe(values.length)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('names each key after the suffix of its value', () => {
|
|
40
|
+
for (const [key, value] of entries) {
|
|
41
|
+
expect(value).toBe(`@byline/richtext-lexical/${key}`)
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
})
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This Source Code is subject to the terms of the Mozilla Public
|
|
3
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
4
|
+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
5
|
+
*
|
|
6
|
+
* Copyright (c) Infonomic Company Limited
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The `name` of every built-in extension, as a plain string map.
|
|
11
|
+
*
|
|
12
|
+
* This module is **React-free and runtime-free** — it carries only the
|
|
13
|
+
* identifying strings, never the extension classes (which statically
|
|
14
|
+
* import their React plugins / nodes). That lets config-only code paths
|
|
15
|
+
* (the `@byline/richtext-lexical/config` subpath) reference a built-in
|
|
16
|
+
* for `extensions.remove(...)` / `.has(...)` / `.replace(...)` without
|
|
17
|
+
* dragging the editor runtime into the importing bundle.
|
|
18
|
+
*
|
|
19
|
+
* ```ts
|
|
20
|
+
* import { builtInExtensions, lexicalEditor } from '@byline/richtext-lexical/config'
|
|
21
|
+
*
|
|
22
|
+
* lexicalEditor((c) => {
|
|
23
|
+
* c.extensions.remove(builtInExtensions.FloatingTextFormat)
|
|
24
|
+
* return c
|
|
25
|
+
* })
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* Each value MUST stay in sync with the corresponding extension's
|
|
29
|
+
* `defineExtension({ name })` — they share the `@byline/richtext-lexical/*`
|
|
30
|
+
* convention (suffix === map key). `built-in-extension-names.test.node.ts`
|
|
31
|
+
* guards this module's own integrity (convention, uniqueness); the mirror
|
|
32
|
+
* against the live extension `name`s is exercised by the editor's
|
|
33
|
+
* jsdom/integration coverage.
|
|
34
|
+
*/
|
|
35
|
+
export const builtInExtensions = {
|
|
36
|
+
Admonition: '@byline/richtext-lexical/Admonition',
|
|
37
|
+
AutoEmbed: '@byline/richtext-lexical/AutoEmbed',
|
|
38
|
+
AutoLink: '@byline/richtext-lexical/AutoLink',
|
|
39
|
+
CodeHighlight: '@byline/richtext-lexical/CodeHighlight',
|
|
40
|
+
FloatingTextFormat: '@byline/richtext-lexical/FloatingTextFormat',
|
|
41
|
+
FloatingUI: '@byline/richtext-lexical/FloatingUI',
|
|
42
|
+
HorizontalRule: '@byline/richtext-lexical/HorizontalRule',
|
|
43
|
+
InlineImage: '@byline/richtext-lexical/InlineImage',
|
|
44
|
+
Layout: '@byline/richtext-lexical/Layout',
|
|
45
|
+
Link: '@byline/richtext-lexical/Link',
|
|
46
|
+
Table: '@byline/richtext-lexical/Table',
|
|
47
|
+
Toolbar: '@byline/richtext-lexical/Toolbar',
|
|
48
|
+
Vimeo: '@byline/richtext-lexical/Vimeo',
|
|
49
|
+
YouTube: '@byline/richtext-lexical/YouTube',
|
|
50
|
+
} as const
|
|
51
|
+
|
|
52
|
+
/** Union of the built-in extension `name` strings. */
|
|
53
|
+
export type BuiltInExtensionName = (typeof builtInExtensions)[keyof typeof builtInExtensions]
|
|
@@ -37,18 +37,28 @@ export class ExtensionsList {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
|
-
* Remove every entry whose name matches
|
|
41
|
-
* the
|
|
40
|
+
* Remove every entry whose name matches the target. Accepts either an
|
|
41
|
+
* extension object (matched by `.name`) or the name string directly —
|
|
42
|
+
* the string form lets config-only code remove a built-in via
|
|
43
|
+
* `builtInExtensions.*` without importing the heavy extension class.
|
|
44
|
+
* No-op when the extension isn't present.
|
|
42
45
|
*/
|
|
43
|
-
remove(extension: AnyLexicalExtension): this {
|
|
44
|
-
const targetName = extension
|
|
46
|
+
remove(extension: AnyLexicalExtension | string): this {
|
|
47
|
+
const targetName = resolveTargetName(extension)
|
|
45
48
|
this.items = this.items.filter((item) => extensionName(item) !== targetName)
|
|
46
49
|
return this
|
|
47
50
|
}
|
|
48
51
|
|
|
49
|
-
/**
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Replace `oldExtension` with `newExtension`, preserving position.
|
|
54
|
+
* `oldExtension` may be an extension object or its name string; the
|
|
55
|
+
* replacement must be a real extension argument.
|
|
56
|
+
*/
|
|
57
|
+
replace(
|
|
58
|
+
oldExtension: AnyLexicalExtension | string,
|
|
59
|
+
newExtension: AnyLexicalExtensionArgument
|
|
60
|
+
): this {
|
|
61
|
+
const targetName = resolveTargetName(oldExtension)
|
|
52
62
|
let replaced = false
|
|
53
63
|
this.items = this.items.map((item) => {
|
|
54
64
|
if (!replaced && extensionName(item) === targetName) {
|
|
@@ -75,9 +85,12 @@ export class ExtensionsList {
|
|
|
75
85
|
return this.replace(extension, wrapped)
|
|
76
86
|
}
|
|
77
87
|
|
|
78
|
-
/**
|
|
79
|
-
|
|
80
|
-
|
|
88
|
+
/**
|
|
89
|
+
* True when an entry with the same `name` is present. Accepts an
|
|
90
|
+
* extension object or its name string.
|
|
91
|
+
*/
|
|
92
|
+
has(extension: AnyLexicalExtension | string): boolean {
|
|
93
|
+
const targetName = resolveTargetName(extension)
|
|
81
94
|
return this.items.some((item) => extensionName(item) === targetName)
|
|
82
95
|
}
|
|
83
96
|
|
|
@@ -92,6 +105,11 @@ export class ExtensionsList {
|
|
|
92
105
|
}
|
|
93
106
|
}
|
|
94
107
|
|
|
108
|
+
/** Resolve a remove/has/replace target to its comparison name. */
|
|
109
|
+
function resolveTargetName(target: AnyLexicalExtension | string): string {
|
|
110
|
+
return typeof target === 'string' ? target : target.name
|
|
111
|
+
}
|
|
112
|
+
|
|
95
113
|
function extensionName(item: AnyLexicalExtensionArgument): string | undefined {
|
|
96
114
|
if (Array.isArray(item)) {
|
|
97
115
|
const head = item[0]
|
package/src/index.ts
CHANGED
|
@@ -10,6 +10,10 @@ export type {
|
|
|
10
10
|
SerializedTextNode,
|
|
11
11
|
} from 'lexical'
|
|
12
12
|
|
|
13
|
+
export {
|
|
14
|
+
type BuiltInExtensionName,
|
|
15
|
+
builtInExtensions,
|
|
16
|
+
} from './field/config/built-in-extension-names'
|
|
13
17
|
export { defaultEditorConfig } from './field/config/default'
|
|
14
18
|
export {
|
|
15
19
|
defaultClientEditorConfig,
|