@haklex/rich-static-renderer 0.0.45 → 0.0.46
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/README.md +35 -0
- package/dist/RichRenderer.d.ts +1 -1
- package/dist/RichRenderer.d.ts.map +1 -1
- package/dist/engine/renderBuiltinNode.d.ts +1 -1
- package/dist/engine/renderBuiltinNode.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +37 -24
- package/dist/types.d.ts +3 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -43,7 +43,15 @@ interface RichRendererProps {
|
|
|
43
43
|
as?: keyof React.JSX.IntrinsicElements
|
|
44
44
|
rendererConfig?: RendererConfig
|
|
45
45
|
extraNodes?: Array<Klass<LexicalNode>>
|
|
46
|
+
builtinNodeOverrides?: Record<string, BuiltinNodeRenderer>
|
|
46
47
|
}
|
|
48
|
+
|
|
49
|
+
type BuiltinNodeRenderer = (
|
|
50
|
+
node: any,
|
|
51
|
+
key: string,
|
|
52
|
+
children: ReactNode[] | null,
|
|
53
|
+
defaultRenderer: () => ReactNode,
|
|
54
|
+
) => ReactNode
|
|
47
55
|
```
|
|
48
56
|
|
|
49
57
|
字段说明:
|
|
@@ -52,6 +60,7 @@ interface RichRendererProps {
|
|
|
52
60
|
- `as`:渲染容器标签,默认 `div`
|
|
53
61
|
- `rendererConfig`:覆写某些节点的渲染组件(Image/CodeBlock/...)
|
|
54
62
|
- `extraNodes`:注册扩展节点(如 Tldraw、Embed、Gallery、CodeSnippet)
|
|
63
|
+
- `builtinNodeOverrides`:覆写内置节点的渲染(paragraph/heading/link/...),按 `node.type` 匹配
|
|
55
64
|
|
|
56
65
|
## 与增强渲染器一起用
|
|
57
66
|
|
|
@@ -86,6 +95,32 @@ const extraNodes = [
|
|
|
86
95
|
/>
|
|
87
96
|
```
|
|
88
97
|
|
|
98
|
+
## 覆写内置节点渲染
|
|
99
|
+
|
|
100
|
+
通过 `builtinNodeOverrides` 可按 `node.type` 覆写 paragraph、heading、link 等内置节点的渲染逻辑。回调提供 `defaultRenderer` 用于回退默认渲染。
|
|
101
|
+
|
|
102
|
+
```tsx
|
|
103
|
+
<RichRenderer
|
|
104
|
+
value={value}
|
|
105
|
+
builtinNodeOverrides={{
|
|
106
|
+
link: (node, key, children) => (
|
|
107
|
+
<a key={key} href={node.url} className="custom-link" target="_blank">
|
|
108
|
+
{children}
|
|
109
|
+
</a>
|
|
110
|
+
),
|
|
111
|
+
heading: (node, key, children, defaultRenderer) => {
|
|
112
|
+
// 仅覆写 h1,其余回退默认
|
|
113
|
+
if (node.tag === 'h1') {
|
|
114
|
+
return <h1 key={key} className="custom-h1">{children}</h1>
|
|
115
|
+
}
|
|
116
|
+
return defaultRenderer()
|
|
117
|
+
},
|
|
118
|
+
}}
|
|
119
|
+
/>
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
支持的 type:`root`、`paragraph`、`heading`、`quote`、`list`、`listitem`、`link`、`autolink`、`horizontalrule`、`table`、`tablerow`、`tablecell`、`details`、`spoiler`、`ruby`、`code`、`code-highlight`、`linebreak`、`tab`。
|
|
123
|
+
|
|
89
124
|
## 渲染行为说明
|
|
90
125
|
|
|
91
126
|
- 内部使用 `@lexical/headless` 构建只读 editor,再将节点树转为 React
|
package/dist/RichRenderer.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { RichRendererProps } from './types';
|
|
2
|
-
export declare function RichRenderer({ value, variant, theme, className, style, as: Component, rendererConfig, extraNodes, }: RichRendererProps): import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
export declare function RichRenderer({ value, variant, theme, className, style, as: Component, rendererConfig, extraNodes, builtinNodeOverrides, }: RichRendererProps): import("react/jsx-runtime").JSX.Element;
|
|
3
3
|
//# sourceMappingURL=RichRenderer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RichRenderer.d.ts","sourceRoot":"","sources":["../src/RichRenderer.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"RichRenderer.d.ts","sourceRoot":"","sources":["../src/RichRenderer.tsx"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAuB,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAkOrE,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,OAAmB,EACnB,KAAe,EACf,SAAS,EACT,KAAK,EACL,EAAE,EAAE,SAAiB,EACrB,cAAc,EACd,UAAU,EACV,oBAAoB,GACrB,EAAE,iBAAiB,2CAuCnB"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
|
-
export declare function renderBuiltinNode(node: any, key: string, children: ReactNode[] | null, headingSlugs: Map<string, number
|
|
2
|
+
export declare function renderBuiltinNode(node: any, key: string, children: ReactNode[] | null, headingSlugs: Map<string, number>, textContent?: string): ReactNode;
|
|
3
3
|
//# sourceMappingURL=renderBuiltinNode.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderBuiltinNode.d.ts","sourceRoot":"","sources":["../../src/engine/renderBuiltinNode.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAiB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAmBrD,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,GAAG,EACT,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,SAAS,EAAE,GAAG,IAAI,EAC5B,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"renderBuiltinNode.d.ts","sourceRoot":"","sources":["../../src/engine/renderBuiltinNode.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAiB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAmBrD,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,GAAG,EACT,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,SAAS,EAAE,GAAG,IAAI,EAC5B,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EACjC,WAAW,CAAC,EAAE,MAAM,GACnB,SAAS,CA+LX"}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,YAAY,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,YAAY,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA"}
|
package/dist/index.mjs
CHANGED
|
@@ -3,20 +3,20 @@ import { RendererWrapper, RubyRenderer, LinkFavicon, getVariantClass, allNodes,
|
|
|
3
3
|
import { PortalThemeProvider } from "@haklex/rich-style-token";
|
|
4
4
|
import { createHeadlessEditor } from "@lexical/headless";
|
|
5
5
|
import { $getRoot } from "lexical";
|
|
6
|
-
import { createElement, isValidElement, cloneElement } from "react";
|
|
6
|
+
import { createElement, useMemo, isValidElement, cloneElement } from "react";
|
|
7
7
|
var tableWrapper = "_1v9yxw30";
|
|
8
8
|
var table = "_1v9yxw31";
|
|
9
9
|
var tableHead = "_1v9yxw32";
|
|
10
10
|
var tableCell = "_1v9yxw33";
|
|
11
11
|
function textToSlug(text) {
|
|
12
|
-
return text.toLowerCase().trim().replaceAll(/[^\w\s\
|
|
12
|
+
return text.toLowerCase().trim().replaceAll(/[^\w\s\u3000-\u9fff\uac00-\ud7af\uff00-\uffef-]/g, "").replaceAll(/[\s_]+/g, "-").replaceAll(/^-+|-+$/g, "");
|
|
13
13
|
}
|
|
14
14
|
function extractText(node) {
|
|
15
15
|
if (node.text) return node.text;
|
|
16
16
|
if (node.children) return node.children.map(extractText).join("");
|
|
17
17
|
return "";
|
|
18
18
|
}
|
|
19
|
-
function renderBuiltinNode(node, key, children, headingSlugs) {
|
|
19
|
+
function renderBuiltinNode(node, key, children, headingSlugs, textContent) {
|
|
20
20
|
switch (node.type) {
|
|
21
21
|
case "root":
|
|
22
22
|
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
@@ -26,7 +26,7 @@ function renderBuiltinNode(node, key, children, headingSlugs) {
|
|
|
26
26
|
}
|
|
27
27
|
case "heading": {
|
|
28
28
|
const Tag = node.tag;
|
|
29
|
-
const text = extractText(node);
|
|
29
|
+
const text = textContent || extractText(node);
|
|
30
30
|
const baseSlug = textToSlug(text);
|
|
31
31
|
let slug = baseSlug;
|
|
32
32
|
if (baseSlug) {
|
|
@@ -39,14 +39,7 @@ function renderBuiltinNode(node, key, children, headingSlugs) {
|
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
return /* @__PURE__ */ jsxs(Tag, { id: slug || void 0, className: `rich-heading-${Tag}`, children: [
|
|
42
|
-
slug && /* @__PURE__ */ jsx(
|
|
43
|
-
"a",
|
|
44
|
-
{
|
|
45
|
-
className: "rich-heading-anchor",
|
|
46
|
-
tabIndex: 0,
|
|
47
|
-
href: `#${slug}`
|
|
48
|
-
}
|
|
49
|
-
),
|
|
42
|
+
slug && /* @__PURE__ */ jsx("a", { className: "rich-heading-anchor", tabIndex: 0, href: `#${slug}` }),
|
|
50
43
|
children
|
|
51
44
|
] }, key);
|
|
52
45
|
}
|
|
@@ -300,7 +293,7 @@ function applyBlockId(element, blockId, nodeKey) {
|
|
|
300
293
|
`${nodeKey}-block-anchor`
|
|
301
294
|
);
|
|
302
295
|
}
|
|
303
|
-
function renderTree(node, editor, editorConfig, headingSlugs, key, blockId) {
|
|
296
|
+
function renderTree(node, editor, editorConfig, headingSlugs, key, blockId, builtinNodeOverrides) {
|
|
304
297
|
const nodeKey = node.getKey ? node.getKey() : key;
|
|
305
298
|
if (typeof node.decorate === "function") {
|
|
306
299
|
try {
|
|
@@ -330,18 +323,36 @@ function renderTree(node, editor, editorConfig, headingSlugs, key, blockId) {
|
|
|
330
323
|
editor,
|
|
331
324
|
editorConfig,
|
|
332
325
|
headingSlugs,
|
|
333
|
-
`${nodeKey}-${i}
|
|
326
|
+
`${nodeKey}-${i}`,
|
|
327
|
+
void 0,
|
|
328
|
+
builtinNodeOverrides
|
|
334
329
|
)
|
|
335
330
|
);
|
|
336
331
|
}
|
|
337
332
|
}
|
|
333
|
+
const textContent = node.getTextContent ? node.getTextContent() : void 0;
|
|
334
|
+
const override = builtinNodeOverrides?.[serialized.type];
|
|
335
|
+
if (override) {
|
|
336
|
+
const defaultRenderer = () => renderBuiltinNode(
|
|
337
|
+
serialized,
|
|
338
|
+
nodeKey,
|
|
339
|
+
children,
|
|
340
|
+
headingSlugs,
|
|
341
|
+
textContent
|
|
342
|
+
);
|
|
343
|
+
return applyBlockId(
|
|
344
|
+
override(serialized, nodeKey, children, defaultRenderer),
|
|
345
|
+
blockId,
|
|
346
|
+
nodeKey
|
|
347
|
+
);
|
|
348
|
+
}
|
|
338
349
|
return applyBlockId(
|
|
339
|
-
renderBuiltinNode(serialized, nodeKey, children, headingSlugs),
|
|
350
|
+
renderBuiltinNode(serialized, nodeKey, children, headingSlugs, textContent),
|
|
340
351
|
blockId,
|
|
341
352
|
nodeKey
|
|
342
353
|
);
|
|
343
354
|
}
|
|
344
|
-
function renderEditorToReact(value, nodes) {
|
|
355
|
+
function renderEditorToReact(value, nodes, builtinNodeOverrides) {
|
|
345
356
|
const editor = createHeadlessEditor({
|
|
346
357
|
nodes,
|
|
347
358
|
theme: editorTheme,
|
|
@@ -366,7 +377,8 @@ function renderEditorToReact(value, nodes) {
|
|
|
366
377
|
editorConfig,
|
|
367
378
|
headingSlugs,
|
|
368
379
|
`ssr-${i}`,
|
|
369
|
-
rawRootChildren?.[i]?.$?.blockId
|
|
380
|
+
rawRootChildren?.[i]?.$?.blockId,
|
|
381
|
+
builtinNodeOverrides
|
|
370
382
|
)
|
|
371
383
|
);
|
|
372
384
|
content = /* @__PURE__ */ jsx(Fragment, { children });
|
|
@@ -398,7 +410,8 @@ function renderEditorToReact(value, nodes) {
|
|
|
398
410
|
nestedEditorConfig,
|
|
399
411
|
headingSlugs,
|
|
400
412
|
`nested-${i}`,
|
|
401
|
-
nestedRawChildren?.[i]?.$?.blockId
|
|
413
|
+
nestedRawChildren?.[i]?.$?.blockId,
|
|
414
|
+
builtinNodeOverrides
|
|
402
415
|
)
|
|
403
416
|
);
|
|
404
417
|
nested = /* @__PURE__ */ jsx(Fragment, { children: ch });
|
|
@@ -415,14 +428,14 @@ function RichRenderer({
|
|
|
415
428
|
style,
|
|
416
429
|
as: Component = "div",
|
|
417
430
|
rendererConfig,
|
|
418
|
-
extraNodes
|
|
431
|
+
extraNodes,
|
|
432
|
+
builtinNodeOverrides
|
|
419
433
|
}) {
|
|
420
434
|
const variantClass = getVariantClass(variant);
|
|
421
|
-
const
|
|
422
|
-
|
|
423
|
-
value,
|
|
424
|
-
|
|
425
|
-
);
|
|
435
|
+
const { content, footnoteData, renderNestedContent } = useMemo(() => {
|
|
436
|
+
const nodes = extraNodes ? [...allNodes, ...extraNodes] : allNodes;
|
|
437
|
+
return renderEditorToReact(value, nodes, builtinNodeOverrides);
|
|
438
|
+
}, [builtinNodeOverrides, extraNodes, value]);
|
|
426
439
|
const classes = ["rich-content", variantClass, className].filter(Boolean).join(" ");
|
|
427
440
|
return /* @__PURE__ */ jsx(PortalThemeProvider, { className: variantClass, theme, children: /* @__PURE__ */ jsx(ColorSchemeProvider, { colorScheme: theme, children: /* @__PURE__ */ jsx(
|
|
428
441
|
RendererConfigProvider,
|
package/dist/types.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ColorScheme, RendererConfig, RichEditorVariant } from '@haklex/rich-editor/static';
|
|
2
2
|
import { Klass, LexicalNode, SerializedEditorState } from 'lexical';
|
|
3
|
-
import { CSSProperties } from 'react';
|
|
3
|
+
import { CSSProperties, ReactNode } from 'react';
|
|
4
|
+
export type BuiltinNodeRenderer = (node: any, key: string, children: ReactNode[] | null, defaultRenderer: () => ReactNode) => ReactNode;
|
|
4
5
|
export interface RichRendererProps {
|
|
5
6
|
value: SerializedEditorState;
|
|
6
7
|
variant?: RichEditorVariant;
|
|
@@ -10,5 +11,6 @@ export interface RichRendererProps {
|
|
|
10
11
|
as?: keyof React.JSX.IntrinsicElements;
|
|
11
12
|
rendererConfig?: RendererConfig;
|
|
12
13
|
extraNodes?: Array<Klass<LexicalNode>>;
|
|
14
|
+
builtinNodeOverrides?: Record<string, BuiltinNodeRenderer>;
|
|
13
15
|
}
|
|
14
16
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,cAAc,EACd,iBAAiB,EAClB,MAAM,4BAA4B,CAAA;AACnC,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAA;AACxE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,cAAc,EACd,iBAAiB,EAClB,MAAM,4BAA4B,CAAA;AACnC,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAA;AACxE,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAErD,MAAM,MAAM,mBAAmB,GAAG,CAChC,IAAI,EAAE,GAAG,EACT,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,SAAS,EAAE,GAAG,IAAI,EAC5B,eAAe,EAAE,MAAM,SAAS,KAC7B,SAAS,CAAA;AAEd,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,qBAAqB,CAAA;IAC5B,OAAO,CAAC,EAAE,iBAAiB,CAAA;IAC3B,KAAK,CAAC,EAAE,WAAW,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,aAAa,CAAA;IACrB,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAA;IACtC,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B,UAAU,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAA;IACtC,oBAAoB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAA;CAC3D"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@haklex/rich-static-renderer",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.46",
|
|
4
4
|
"description": "Headless SSR engine for Lexical rich content",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@lexical/headless": "^0.41.0",
|
|
19
|
-
"@haklex/rich-
|
|
20
|
-
"@haklex/rich-
|
|
19
|
+
"@haklex/rich-editor": "0.0.46",
|
|
20
|
+
"@haklex/rich-style-token": "0.0.46"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@lexical/code": "^0.41.0",
|