@blocklet/editor 2.4.57 → 2.4.58

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.
@@ -0,0 +1,6 @@
1
+ interface CustomComponentProps {
2
+ component: string;
3
+ properties?: any;
4
+ }
5
+ export declare function CustomComponent({ component, properties }: CustomComponentProps): import("react/jsx-runtime").JSX.Element | null;
6
+ export {};
@@ -0,0 +1,15 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Box } from '@mui/material';
3
+ import { ErrorBoundary } from 'react-error-boundary';
4
+ import components from './components';
5
+ export function CustomComponent({ component, properties }) {
6
+ const Component = components[component];
7
+ if (!Component) {
8
+ return null;
9
+ }
10
+ const fallbackRender = ({ error }) => {
11
+ console.error('CustomComponent error', component, properties, error);
12
+ return null;
13
+ };
14
+ return (_jsx(Box, { sx: { py: 1 }, children: _jsx(ErrorBoundary, { fallbackRender: fallbackRender, children: _jsx(Component, { ...properties }) }) }));
15
+ }
@@ -0,0 +1,29 @@
1
+ import { type DOMConversionMap, type DOMExportOutput, type EditorConfig, type ElementFormatType, type LexicalEditor, type LexicalNode, type NodeKey, type Spread } from 'lexical';
2
+ import { DecoratorBlockNode, SerializedDecoratorBlockNode } from '@lexical/react/LexicalDecoratorBlockNode';
3
+ import type { JSX } from 'react';
4
+ declare const NODE_TYPE = "x-component";
5
+ interface CustomComponentData {
6
+ component: string;
7
+ properties?: Record<string, unknown>;
8
+ }
9
+ export type SerializedCustomComponentNode = Spread<{
10
+ type: typeof NODE_TYPE;
11
+ data: CustomComponentData;
12
+ }, SerializedDecoratorBlockNode>;
13
+ export declare class CustomComponentNode extends DecoratorBlockNode {
14
+ __data: CustomComponentData;
15
+ static getType(): string;
16
+ static clone(node: CustomComponentNode): CustomComponentNode;
17
+ constructor(data: CustomComponentData, format?: ElementFormatType, key?: NodeKey);
18
+ exportDOM(): DOMExportOutput;
19
+ static importDOM(): DOMConversionMap | null;
20
+ static importJSON(serializedNode: SerializedCustomComponentNode): CustomComponentNode;
21
+ exportJSON(): SerializedCustomComponentNode;
22
+ getData(): CustomComponentData;
23
+ setData(data: CustomComponentData): void;
24
+ isInline(): false;
25
+ decorate(editor: LexicalEditor, config: EditorConfig): JSX.Element;
26
+ }
27
+ export declare function $createCustomComponentNode(data: CustomComponentData): CustomComponentNode;
28
+ export declare function $isCustomComponentNode(node: LexicalNode | null | undefined): node is CustomComponentNode;
29
+ export {};
@@ -0,0 +1,111 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { BlockWithAlignableContents } from '@lexical/react/LexicalBlockWithAlignableContents';
3
+ import { DecoratorBlockNode } from '@lexical/react/LexicalDecoratorBlockNode';
4
+ import { CustomComponent } from './CustomComponent';
5
+ const NODE_TYPE = 'x-component';
6
+ function isCustomComponent(domNode) {
7
+ return domNode.tagName.toLowerCase().startsWith('x-');
8
+ }
9
+ function getComponentName(domNode) {
10
+ if (!isCustomComponent(domNode)) {
11
+ throw new Error(`Invalid component name: ${domNode.tagName.toLowerCase()}`);
12
+ }
13
+ return domNode.tagName.toLowerCase().slice(2);
14
+ }
15
+ function domToComponentProperties(domNode) {
16
+ const properties = { component: getComponentName(domNode), ...domNode.dataset };
17
+ const { children } = domNode;
18
+ const hasChildren = Array.from(children).some((child) => isCustomComponent(child));
19
+ if (hasChildren) {
20
+ properties.children = Array.from(children).map((child) => {
21
+ const childProperties = domToComponentProperties(child);
22
+ return { component: childProperties.component, properties: childProperties };
23
+ });
24
+ }
25
+ else {
26
+ properties.body = domNode.textContent?.trim() || '';
27
+ }
28
+ return properties;
29
+ }
30
+ function convertCustomComponentElement(domNode) {
31
+ const component = getComponentName(domNode);
32
+ try {
33
+ if (component) {
34
+ const node = $createCustomComponentNode({ component, properties: domToComponentProperties(domNode) });
35
+ return { node };
36
+ }
37
+ }
38
+ catch (e) {
39
+ console.warn(`Failed to parse: ${component}`, e);
40
+ }
41
+ return null;
42
+ }
43
+ export class CustomComponentNode extends DecoratorBlockNode {
44
+ __data;
45
+ static getType() {
46
+ return NODE_TYPE;
47
+ }
48
+ static clone(node) {
49
+ return new CustomComponentNode(node.__data, node.__format, node.__key);
50
+ }
51
+ constructor(data, format, key) {
52
+ super(format, key);
53
+ this.__data = { ...data };
54
+ }
55
+ exportDOM() {
56
+ const element = document.createElement('div');
57
+ const { body, ...rest } = this.__data.properties || {};
58
+ if (body) {
59
+ element.textContent = body;
60
+ }
61
+ Object.entries(rest).forEach(([key, value]) => {
62
+ element.setAttribute(`data-${key}`, value);
63
+ });
64
+ return { element };
65
+ }
66
+ static importDOM() {
67
+ return {
68
+ 'x-card': () => ({ conversion: convertCustomComponentElement, priority: 1 }),
69
+ 'x-cards': () => ({ conversion: convertCustomComponentElement, priority: 1 }),
70
+ 'x-code-group': () => ({ conversion: convertCustomComponentElement, priority: 1 }),
71
+ 'x-steps': () => ({ conversion: convertCustomComponentElement, priority: 1 }),
72
+ };
73
+ }
74
+ static importJSON(serializedNode) {
75
+ const node = $createCustomComponentNode(serializedNode.data);
76
+ return node;
77
+ }
78
+ exportJSON() {
79
+ return {
80
+ ...super.exportJSON(),
81
+ type: NODE_TYPE,
82
+ data: this.__data,
83
+ version: 1,
84
+ };
85
+ }
86
+ getData() {
87
+ return this.__data;
88
+ }
89
+ setData(data) {
90
+ const writable = this.getWritable();
91
+ writable.__data = data;
92
+ }
93
+ isInline() {
94
+ return false;
95
+ }
96
+ decorate(editor, config) {
97
+ const customComponentTheme = config.theme.customComponent || {};
98
+ const className = {
99
+ base: customComponentTheme.base || '',
100
+ focus: customComponentTheme.focus || '',
101
+ };
102
+ return (_jsx(BlockWithAlignableContents, { className: className, format: this.__format, nodeKey: this.__key, children: _jsx(CustomComponent, { component: this.__data.component, properties: this.__data.properties }) }));
103
+ }
104
+ }
105
+ export function $createCustomComponentNode(data) {
106
+ const customComponentNode = new CustomComponentNode(data);
107
+ return customComponentNode;
108
+ }
109
+ export function $isCustomComponentNode(node) {
110
+ return node instanceof CustomComponentNode;
111
+ }
@@ -0,0 +1,11 @@
1
+ interface CardProps {
2
+ title: string;
3
+ body?: string;
4
+ image?: string;
5
+ icon?: string;
6
+ href?: string;
7
+ horizontal?: boolean;
8
+ cta?: string;
9
+ }
10
+ export default function Card({ title, body, image, icon, href, horizontal, cta }: CardProps): import("react/jsx-runtime").JSX.Element;
11
+ export {};
@@ -0,0 +1,60 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Card as MuiCard, CardContent, Typography, Link } from '@mui/material';
3
+ import { Icon } from '@iconify/react';
4
+ export default function Card({ title, body, image, icon, href, horizontal = false, cta }) {
5
+ const isLink = !!href;
6
+ // 渲染图标
7
+ const renderIcon = () => {
8
+ if (!icon || !!image)
9
+ return null;
10
+ return (_jsx(Box, { component: Icon, icon: icon, sx: {
11
+ display: 'inline-block',
12
+ fontSize: '1.5em',
13
+ color: 'primary.dark',
14
+ } }));
15
+ };
16
+ const cardContent = (_jsxs(MuiCard, { sx: {
17
+ height: '100%',
18
+ display: 'flex',
19
+ flexDirection: horizontal ? 'row' : 'column',
20
+ cursor: isLink ? 'pointer' : 'default',
21
+ transition: 'all 0.2s ease-in-out',
22
+ '&:hover': isLink && !image ? { borderColor: 'primary.main' } : {},
23
+ border: 1,
24
+ borderColor: 'divider',
25
+ borderRadius: 1,
26
+ overflow: 'hidden',
27
+ }, elevation: 0, children: [image && !horizontal && (_jsx(Box, { component: "img", src: image, alt: title, sx: {
28
+ width: '100%',
29
+ height: 200,
30
+ objectFit: 'cover',
31
+ } })), _jsx(CardContent, { sx: {
32
+ display: 'flex',
33
+ flexDirection: horizontal ? 'row' : 'column',
34
+ alignItems: horizontal ? 'center' : 'flex-start',
35
+ flex: 1,
36
+ p: 3,
37
+ }, children: _jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', flex: 1, gap: 1 }, children: [_jsxs(Box, { sx: {
38
+ display: 'flex',
39
+ flexDirection: 'column',
40
+ gap: 1,
41
+ ...(horizontal && { flexDirection: 'row', alignItems: 'center' }),
42
+ }, children: [renderIcon(), _jsx(Typography, { variant: "h5", component: "h3", sx: {
43
+ fontWeight: 600,
44
+ color: 'text.primary',
45
+ }, children: title })] }), body && (_jsx(Typography, { variant: "body2", sx: {
46
+ color: 'text.secondary',
47
+ }, children: body })), isLink && cta && (_jsx(Box, { sx: { display: 'flex', alignItems: 'center', marginTop: 'auto' }, children: _jsxs(Typography, { variant: "body2", sx: {
48
+ color: 'text.primary',
49
+ display: 'flex',
50
+ alignItems: 'center',
51
+ gap: 0.5,
52
+ }, children: [cta || 'Learn more', _jsx(Box, { component: Icon, icon: "lucide:chevron-right", sx: {
53
+ fontSize: '0.875rem',
54
+ transition: 'transform 0.2s ease-in-out',
55
+ } })] }) }))] }) })] }));
56
+ if (isLink) {
57
+ return (_jsx(Box, { component: Link, href: href, sx: { display: 'block', height: '100%', textDecoration: 'none', color: 'inherit' }, children: cardContent }));
58
+ }
59
+ return cardContent;
60
+ }
@@ -0,0 +1,20 @@
1
+ interface Item {
2
+ title: string;
3
+ body?: string;
4
+ image?: string;
5
+ icon?: string;
6
+ color?: string;
7
+ href?: string;
8
+ horizontal?: boolean;
9
+ cta?: string;
10
+ arrow?: boolean;
11
+ }
12
+ interface CardsProps {
13
+ columns?: number;
14
+ children: {
15
+ properties: Item;
16
+ }[];
17
+ gap?: number;
18
+ }
19
+ export default function Cards({ children, columns, gap }: CardsProps): import("react/jsx-runtime").JSX.Element | null;
20
+ export {};
@@ -0,0 +1,38 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Box } from '@mui/material';
3
+ import Card from './Card';
4
+ export default function Cards({ children, columns = 2, gap = 2 }) {
5
+ if (!children || children.length === 0) {
6
+ return null;
7
+ }
8
+ // 响应式列数
9
+ const getResponsiveColumns = (cols) => {
10
+ return {
11
+ xs: 1,
12
+ sm: Math.min(2, cols),
13
+ md: Math.min(3, cols),
14
+ lg: Math.min(4, cols),
15
+ xl: cols,
16
+ };
17
+ };
18
+ const responsiveCols = getResponsiveColumns(columns);
19
+ return (_jsx(Box, { sx: {
20
+ display: 'grid',
21
+ gridTemplateColumns: {
22
+ xs: 'repeat(1, 1fr)',
23
+ sm: `repeat(${responsiveCols.sm}, 1fr)`,
24
+ md: `repeat(${responsiveCols.md}, 1fr)`,
25
+ lg: `repeat(${responsiveCols.lg}, 1fr)`,
26
+ xl: `repeat(${responsiveCols.xl}, 1fr)`,
27
+ },
28
+ gap,
29
+ width: '100%',
30
+ }, children: children.map((x) => {
31
+ const { properties } = x;
32
+ return (_jsx(Box, { sx: {
33
+ display: 'flex',
34
+ flexDirection: 'column',
35
+ height: '100%',
36
+ }, children: _jsx(Card, { ...properties }) }, properties.title));
37
+ }) }));
38
+ }
@@ -0,0 +1,7 @@
1
+ import Card from './Card';
2
+ import Cards from './Cards';
3
+ declare const components: {
4
+ card: typeof Card;
5
+ cards: typeof Cards;
6
+ };
7
+ export default components;
@@ -0,0 +1,7 @@
1
+ import Card from './Card';
2
+ import Cards from './Cards';
3
+ const components = {
4
+ card: Card,
5
+ cards: Cards,
6
+ };
7
+ export default components;
@@ -39,6 +39,7 @@ import { FileNode } from '../../ext/FilePlugin';
39
39
  import { SubpageListingNode } from '../../ext/SubpageListingPlugin/SubpageListingNode';
40
40
  import { PagesKitComponentNode } from '../../ext/PagesKitComponent/PagesKitComponentNode';
41
41
  import { TranslationNode } from '../../ext/InlineTranslationPlugin';
42
+ import { CustomComponentNode } from '../../ext/CustomComponent/CustomComponentNode';
42
43
  const PlaygroundNodes = [
43
44
  HeadingNode,
44
45
  ListNode,
@@ -80,5 +81,6 @@ const PlaygroundNodes = [
80
81
  PagesKitComponentNode,
81
82
  TranslationNode,
82
83
  MermaidNode,
84
+ CustomComponentNode,
83
85
  ];
84
86
  export default PlaygroundNodes;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocklet/editor",
3
- "version": "2.4.57",
3
+ "version": "2.4.58",
4
4
  "main": "lib/index.js",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -63,13 +63,14 @@
63
63
  "medium-zoom": "^1.1.0",
64
64
  "mermaid": "^11.6.0",
65
65
  "path-parser": "^6.1.0",
66
+ "react-error-boundary": "^3.1.4",
66
67
  "react-player": "^2.16.0",
67
68
  "react-popper": "^2.3.0",
68
69
  "split-pane-react": "^0.1.3",
69
70
  "ufo": "^1.5.4",
70
71
  "url-join": "^4.0.1",
71
72
  "zustand": "^4.5.5",
72
- "@blocklet/pdf": "2.4.57"
73
+ "@blocklet/pdf": "2.4.58"
73
74
  },
74
75
  "devDependencies": {
75
76
  "@babel/core": "^7.25.2",