@buildingbite/blocks 0.1.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/README.md +110 -0
- package/dist/index.d.mts +191 -0
- package/dist/index.d.ts +191 -0
- package/dist/index.js +2514 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2492 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +48 -0
package/README.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# @buildingbite/blocks
|
|
2
|
+
|
|
3
|
+
블로그용 블록 기반 리치 텍스트 에디터
|
|
4
|
+
|
|
5
|
+
## 설치
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @buildingbite/blocks
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 사용법
|
|
12
|
+
|
|
13
|
+
### 1. EditorServices 구현
|
|
14
|
+
|
|
15
|
+
`EditorServices` 인터페이스를 구현하여 이미지 업로드, OG 데이터 조회 등을 처리합니다.
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { EditorServices } from '@buildingbite/blocks';
|
|
19
|
+
|
|
20
|
+
export const editorServices: EditorServices = {
|
|
21
|
+
// 이미지 업로드
|
|
22
|
+
uploadImage: async (file: File, category: string): Promise<string> => {
|
|
23
|
+
// Firebase Storage 등에 업로드 후 URL 반환
|
|
24
|
+
const url = await uploadToStorage(file, category);
|
|
25
|
+
return url;
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
// OG 데이터 조회
|
|
29
|
+
fetchOGData: async (url: string) => {
|
|
30
|
+
const response = await fetch(`/api/og?url=${encodeURIComponent(url)}`);
|
|
31
|
+
return response.json();
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
// 캐릭터 라이브러리 (선택적)
|
|
35
|
+
getCharacters: async () => {
|
|
36
|
+
return await fetchCharactersFromDB();
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
// 이미지 컴포넌트 (선택적, Next.js의 경우)
|
|
40
|
+
ImageComponent: NextImage,
|
|
41
|
+
};
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 2. EditorProvider로 감싸기
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
import { EditorProvider, BlockEditor } from '@buildingbite/blocks';
|
|
48
|
+
import { editorServices } from '@/lib/editorServices';
|
|
49
|
+
|
|
50
|
+
function MyEditor() {
|
|
51
|
+
const [blocks, setBlocks] = useState([]);
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<EditorProvider services={editorServices}>
|
|
55
|
+
<BlockEditor
|
|
56
|
+
initialBlocks={blocks}
|
|
57
|
+
onChange={setBlocks}
|
|
58
|
+
onCharCountChange={(count) => console.log(`글자 수: ${count}`)}
|
|
59
|
+
/>
|
|
60
|
+
</EditorProvider>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## 블록 타입
|
|
66
|
+
|
|
67
|
+
- `paragraph` - 일반 텍스트
|
|
68
|
+
- `heading` - 제목 (H1, H2, H3)
|
|
69
|
+
- `image` - 단일 이미지
|
|
70
|
+
- `image-gallery` - 이미지 갤러리
|
|
71
|
+
- `link-card` - OG 미리보기 카드
|
|
72
|
+
- `link-embed` - YouTube/Twitter 임베드
|
|
73
|
+
- `dialogue` - 대사 (채팅/희극 스타일)
|
|
74
|
+
- `divider` - 구분선
|
|
75
|
+
- `quote` - 인용문
|
|
76
|
+
- `list` - 목록
|
|
77
|
+
|
|
78
|
+
## 헬퍼 함수
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
import {
|
|
82
|
+
createEmptyParagraphBlock,
|
|
83
|
+
generateBlockId,
|
|
84
|
+
textNodesToPlainText,
|
|
85
|
+
generatePreviewFromBlocks,
|
|
86
|
+
countBlocksCharacters,
|
|
87
|
+
} from '@buildingbite/blocks';
|
|
88
|
+
|
|
89
|
+
// 빈 문단 블록 생성
|
|
90
|
+
const newBlock = createEmptyParagraphBlock();
|
|
91
|
+
|
|
92
|
+
// 블록 ID 생성
|
|
93
|
+
const id = generateBlockId();
|
|
94
|
+
|
|
95
|
+
// 블록에서 미리보기 텍스트 생성
|
|
96
|
+
const preview = generatePreviewFromBlocks(blocks, 150);
|
|
97
|
+
|
|
98
|
+
// 총 글자 수 계산
|
|
99
|
+
const charCount = countBlocksCharacters(blocks);
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## 의존성
|
|
103
|
+
|
|
104
|
+
- React 18+
|
|
105
|
+
- @dnd-kit/core (드래그 앤 드롭)
|
|
106
|
+
- lucide-react (아이콘)
|
|
107
|
+
|
|
108
|
+
## 라이선스
|
|
109
|
+
|
|
110
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import React, { ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
type BlockType = 'paragraph' | 'heading' | 'image' | 'image-gallery' | 'link-card' | 'link-embed' | 'dialogue' | 'divider' | 'quote' | 'list';
|
|
5
|
+
interface TextNode {
|
|
6
|
+
text: string;
|
|
7
|
+
bold?: boolean;
|
|
8
|
+
italic?: boolean;
|
|
9
|
+
underline?: boolean;
|
|
10
|
+
strikethrough?: boolean;
|
|
11
|
+
link?: string;
|
|
12
|
+
}
|
|
13
|
+
interface BaseBlock {
|
|
14
|
+
id: string;
|
|
15
|
+
type: BlockType;
|
|
16
|
+
}
|
|
17
|
+
interface ParagraphBlock extends BaseBlock {
|
|
18
|
+
type: 'paragraph';
|
|
19
|
+
content: TextNode[];
|
|
20
|
+
}
|
|
21
|
+
interface HeadingBlock extends BaseBlock {
|
|
22
|
+
type: 'heading';
|
|
23
|
+
level: 1 | 2 | 3;
|
|
24
|
+
content: TextNode[];
|
|
25
|
+
}
|
|
26
|
+
interface ImageData {
|
|
27
|
+
url: string;
|
|
28
|
+
alt?: string;
|
|
29
|
+
caption?: string;
|
|
30
|
+
width?: number;
|
|
31
|
+
height?: number;
|
|
32
|
+
}
|
|
33
|
+
interface ImageBlock extends BaseBlock {
|
|
34
|
+
type: 'image';
|
|
35
|
+
image: ImageData;
|
|
36
|
+
alignment: 'left' | 'center' | 'right' | 'full-width';
|
|
37
|
+
}
|
|
38
|
+
interface ImageGalleryBlock extends BaseBlock {
|
|
39
|
+
type: 'image-gallery';
|
|
40
|
+
images: ImageData[];
|
|
41
|
+
layout: 'grid' | 'horizontal-scroll' | 'masonry';
|
|
42
|
+
columns?: 2 | 3 | 4;
|
|
43
|
+
}
|
|
44
|
+
interface OGData {
|
|
45
|
+
title?: string;
|
|
46
|
+
description?: string;
|
|
47
|
+
image?: string;
|
|
48
|
+
siteName?: string;
|
|
49
|
+
}
|
|
50
|
+
interface LinkCardBlock extends BaseBlock {
|
|
51
|
+
type: 'link-card';
|
|
52
|
+
url: string;
|
|
53
|
+
ogData?: OGData;
|
|
54
|
+
}
|
|
55
|
+
interface LinkEmbedBlock extends BaseBlock {
|
|
56
|
+
type: 'link-embed';
|
|
57
|
+
url: string;
|
|
58
|
+
embedType: 'youtube' | 'twitter' | 'generic';
|
|
59
|
+
videoId?: string;
|
|
60
|
+
}
|
|
61
|
+
interface DialogueLine {
|
|
62
|
+
speaker: string;
|
|
63
|
+
content: TextNode[];
|
|
64
|
+
speakerColor?: string;
|
|
65
|
+
profileImage?: string;
|
|
66
|
+
alignment?: 'left' | 'right';
|
|
67
|
+
}
|
|
68
|
+
interface DialogueBlock extends BaseBlock {
|
|
69
|
+
type: 'dialogue';
|
|
70
|
+
lines: DialogueLine[];
|
|
71
|
+
style?: 'chat' | 'play';
|
|
72
|
+
}
|
|
73
|
+
interface DividerBlock extends BaseBlock {
|
|
74
|
+
type: 'divider';
|
|
75
|
+
variant: 'line' | 'short-line' | 'dots' | 'custom-image';
|
|
76
|
+
customImage?: string;
|
|
77
|
+
alignment?: 'left' | 'center' | 'right';
|
|
78
|
+
}
|
|
79
|
+
interface QuoteBlock extends BaseBlock {
|
|
80
|
+
type: 'quote';
|
|
81
|
+
content: TextNode[];
|
|
82
|
+
citation?: string;
|
|
83
|
+
}
|
|
84
|
+
interface ListBlock extends BaseBlock {
|
|
85
|
+
type: 'list';
|
|
86
|
+
listType: 'ordered' | 'unordered';
|
|
87
|
+
content: TextNode[];
|
|
88
|
+
}
|
|
89
|
+
type Block = ParagraphBlock | HeadingBlock | ImageBlock | ImageGalleryBlock | LinkCardBlock | LinkEmbedBlock | DialogueBlock | DividerBlock | QuoteBlock | ListBlock;
|
|
90
|
+
interface BlockContent {
|
|
91
|
+
version: 2;
|
|
92
|
+
blocks: Block[];
|
|
93
|
+
}
|
|
94
|
+
interface Character {
|
|
95
|
+
id: string;
|
|
96
|
+
name: string;
|
|
97
|
+
profileImage: string | null;
|
|
98
|
+
defaultColor: string;
|
|
99
|
+
description?: string | null;
|
|
100
|
+
}
|
|
101
|
+
declare function createEmptyParagraphBlock(): ParagraphBlock;
|
|
102
|
+
declare function generateBlockId(): string;
|
|
103
|
+
declare function textNodesToPlainText(nodes: TextNode[]): string;
|
|
104
|
+
declare function generatePreviewFromBlocks(blocks: Block[], maxLength?: number): string;
|
|
105
|
+
declare function countBlocksCharacters(blocks: Block[]): number;
|
|
106
|
+
|
|
107
|
+
interface BlockEditorProps {
|
|
108
|
+
initialBlocks?: Block[];
|
|
109
|
+
onChange: (blocks: Block[]) => void;
|
|
110
|
+
onCharCountChange?: (count: number) => void;
|
|
111
|
+
}
|
|
112
|
+
declare function BlockEditor({ initialBlocks, onChange, onCharCountChange, }: BlockEditorProps): react_jsx_runtime.JSX.Element;
|
|
113
|
+
|
|
114
|
+
interface BlockMenuProps {
|
|
115
|
+
position: {
|
|
116
|
+
x: number;
|
|
117
|
+
y: number;
|
|
118
|
+
};
|
|
119
|
+
onClose: () => void;
|
|
120
|
+
onSelect: (type: Block['type']) => void;
|
|
121
|
+
}
|
|
122
|
+
declare function BlockMenu({ position, onClose, onSelect }: BlockMenuProps): react_jsx_runtime.JSX.Element;
|
|
123
|
+
|
|
124
|
+
interface TextBlockEditorProps {
|
|
125
|
+
block: ParagraphBlock | HeadingBlock | QuoteBlock | ListBlock | ImageBlock;
|
|
126
|
+
onUpdate: (updates: Partial<Block>) => void;
|
|
127
|
+
onMergeWithPrevious?: () => void;
|
|
128
|
+
onSplitBlock?: (beforeText: TextNode[], afterText: TextNode[]) => void;
|
|
129
|
+
onPasteBlocks?: (beforeText: TextNode[], pastedLines: string[], afterText: TextNode[]) => void;
|
|
130
|
+
onDeleteEmptyBlock?: () => void;
|
|
131
|
+
onFocusPrevious?: () => void;
|
|
132
|
+
onFocusNext?: () => void;
|
|
133
|
+
isOnlyBlock?: boolean;
|
|
134
|
+
}
|
|
135
|
+
declare function TextBlockEditor({ block, onUpdate, onMergeWithPrevious, onSplitBlock, onPasteBlocks, onDeleteEmptyBlock, onFocusPrevious, onFocusNext, isOnlyBlock }: TextBlockEditorProps): react_jsx_runtime.JSX.Element;
|
|
136
|
+
|
|
137
|
+
interface ImageGalleryEditorProps {
|
|
138
|
+
block: ImageGalleryBlock;
|
|
139
|
+
onUpdate: (updates: Partial<ImageGalleryBlock>) => void;
|
|
140
|
+
}
|
|
141
|
+
declare function ImageGalleryEditor({ block, onUpdate }: ImageGalleryEditorProps): react_jsx_runtime.JSX.Element;
|
|
142
|
+
|
|
143
|
+
interface LinkCardEditorProps {
|
|
144
|
+
block: LinkCardBlock;
|
|
145
|
+
onUpdate: (updates: Partial<LinkCardBlock>) => void;
|
|
146
|
+
}
|
|
147
|
+
declare function LinkCardEditor({ block, onUpdate }: LinkCardEditorProps): react_jsx_runtime.JSX.Element;
|
|
148
|
+
|
|
149
|
+
interface LinkEmbedEditorProps {
|
|
150
|
+
block: LinkEmbedBlock;
|
|
151
|
+
onUpdate: (updates: Partial<LinkEmbedBlock>) => void;
|
|
152
|
+
}
|
|
153
|
+
declare function LinkEmbedEditor({ block, onUpdate }: LinkEmbedEditorProps): react_jsx_runtime.JSX.Element;
|
|
154
|
+
|
|
155
|
+
interface DialogueEditorProps {
|
|
156
|
+
block: DialogueBlock;
|
|
157
|
+
onUpdate: (updates: Partial<DialogueBlock>) => void;
|
|
158
|
+
}
|
|
159
|
+
declare function DialogueEditor({ block, onUpdate }: DialogueEditorProps): react_jsx_runtime.JSX.Element;
|
|
160
|
+
|
|
161
|
+
interface DividerEditorProps {
|
|
162
|
+
block: DividerBlock;
|
|
163
|
+
onUpdate: (updates: Partial<DividerBlock>) => void;
|
|
164
|
+
}
|
|
165
|
+
declare function DividerEditor({ block, onUpdate }: DividerEditorProps): react_jsx_runtime.JSX.Element;
|
|
166
|
+
|
|
167
|
+
interface ImageComponentProps {
|
|
168
|
+
src: string;
|
|
169
|
+
alt: string;
|
|
170
|
+
fill?: boolean;
|
|
171
|
+
width?: number;
|
|
172
|
+
height?: number;
|
|
173
|
+
className?: string;
|
|
174
|
+
style?: React.CSSProperties;
|
|
175
|
+
}
|
|
176
|
+
interface EditorServices {
|
|
177
|
+
uploadImage: (file: File, category: string) => Promise<string>;
|
|
178
|
+
fetchOGData: (url: string) => Promise<OGData>;
|
|
179
|
+
getCharacters?: () => Promise<Character[]>;
|
|
180
|
+
ImageComponent?: React.ComponentType<ImageComponentProps>;
|
|
181
|
+
}
|
|
182
|
+
interface EditorProviderProps {
|
|
183
|
+
services: EditorServices;
|
|
184
|
+
children: ReactNode;
|
|
185
|
+
}
|
|
186
|
+
declare function EditorProvider({ services, children }: EditorProviderProps): react_jsx_runtime.JSX.Element;
|
|
187
|
+
declare function useEditorServices(): EditorServices;
|
|
188
|
+
declare function useOptionalEditorServices(): EditorServices | null;
|
|
189
|
+
declare function DefaultImage({ src, alt, fill, width, height, className, style, }: ImageComponentProps): react_jsx_runtime.JSX.Element;
|
|
190
|
+
|
|
191
|
+
export { type Block, type BlockContent, BlockEditor, BlockMenu, type BlockType, type Character, DefaultImage, type DialogueBlock, DialogueEditor, type DialogueLine, type DividerBlock, DividerEditor, EditorProvider, type EditorServices, type HeadingBlock, type ImageBlock, type ImageComponentProps, type ImageData, type ImageGalleryBlock, ImageGalleryEditor, type LinkCardBlock, LinkCardEditor, type LinkEmbedBlock, LinkEmbedEditor, type ListBlock, type OGData, type ParagraphBlock, type QuoteBlock, TextBlockEditor, type TextNode, countBlocksCharacters, createEmptyParagraphBlock, generateBlockId, generatePreviewFromBlocks, textNodesToPlainText, useEditorServices, useOptionalEditorServices };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import React, { ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
type BlockType = 'paragraph' | 'heading' | 'image' | 'image-gallery' | 'link-card' | 'link-embed' | 'dialogue' | 'divider' | 'quote' | 'list';
|
|
5
|
+
interface TextNode {
|
|
6
|
+
text: string;
|
|
7
|
+
bold?: boolean;
|
|
8
|
+
italic?: boolean;
|
|
9
|
+
underline?: boolean;
|
|
10
|
+
strikethrough?: boolean;
|
|
11
|
+
link?: string;
|
|
12
|
+
}
|
|
13
|
+
interface BaseBlock {
|
|
14
|
+
id: string;
|
|
15
|
+
type: BlockType;
|
|
16
|
+
}
|
|
17
|
+
interface ParagraphBlock extends BaseBlock {
|
|
18
|
+
type: 'paragraph';
|
|
19
|
+
content: TextNode[];
|
|
20
|
+
}
|
|
21
|
+
interface HeadingBlock extends BaseBlock {
|
|
22
|
+
type: 'heading';
|
|
23
|
+
level: 1 | 2 | 3;
|
|
24
|
+
content: TextNode[];
|
|
25
|
+
}
|
|
26
|
+
interface ImageData {
|
|
27
|
+
url: string;
|
|
28
|
+
alt?: string;
|
|
29
|
+
caption?: string;
|
|
30
|
+
width?: number;
|
|
31
|
+
height?: number;
|
|
32
|
+
}
|
|
33
|
+
interface ImageBlock extends BaseBlock {
|
|
34
|
+
type: 'image';
|
|
35
|
+
image: ImageData;
|
|
36
|
+
alignment: 'left' | 'center' | 'right' | 'full-width';
|
|
37
|
+
}
|
|
38
|
+
interface ImageGalleryBlock extends BaseBlock {
|
|
39
|
+
type: 'image-gallery';
|
|
40
|
+
images: ImageData[];
|
|
41
|
+
layout: 'grid' | 'horizontal-scroll' | 'masonry';
|
|
42
|
+
columns?: 2 | 3 | 4;
|
|
43
|
+
}
|
|
44
|
+
interface OGData {
|
|
45
|
+
title?: string;
|
|
46
|
+
description?: string;
|
|
47
|
+
image?: string;
|
|
48
|
+
siteName?: string;
|
|
49
|
+
}
|
|
50
|
+
interface LinkCardBlock extends BaseBlock {
|
|
51
|
+
type: 'link-card';
|
|
52
|
+
url: string;
|
|
53
|
+
ogData?: OGData;
|
|
54
|
+
}
|
|
55
|
+
interface LinkEmbedBlock extends BaseBlock {
|
|
56
|
+
type: 'link-embed';
|
|
57
|
+
url: string;
|
|
58
|
+
embedType: 'youtube' | 'twitter' | 'generic';
|
|
59
|
+
videoId?: string;
|
|
60
|
+
}
|
|
61
|
+
interface DialogueLine {
|
|
62
|
+
speaker: string;
|
|
63
|
+
content: TextNode[];
|
|
64
|
+
speakerColor?: string;
|
|
65
|
+
profileImage?: string;
|
|
66
|
+
alignment?: 'left' | 'right';
|
|
67
|
+
}
|
|
68
|
+
interface DialogueBlock extends BaseBlock {
|
|
69
|
+
type: 'dialogue';
|
|
70
|
+
lines: DialogueLine[];
|
|
71
|
+
style?: 'chat' | 'play';
|
|
72
|
+
}
|
|
73
|
+
interface DividerBlock extends BaseBlock {
|
|
74
|
+
type: 'divider';
|
|
75
|
+
variant: 'line' | 'short-line' | 'dots' | 'custom-image';
|
|
76
|
+
customImage?: string;
|
|
77
|
+
alignment?: 'left' | 'center' | 'right';
|
|
78
|
+
}
|
|
79
|
+
interface QuoteBlock extends BaseBlock {
|
|
80
|
+
type: 'quote';
|
|
81
|
+
content: TextNode[];
|
|
82
|
+
citation?: string;
|
|
83
|
+
}
|
|
84
|
+
interface ListBlock extends BaseBlock {
|
|
85
|
+
type: 'list';
|
|
86
|
+
listType: 'ordered' | 'unordered';
|
|
87
|
+
content: TextNode[];
|
|
88
|
+
}
|
|
89
|
+
type Block = ParagraphBlock | HeadingBlock | ImageBlock | ImageGalleryBlock | LinkCardBlock | LinkEmbedBlock | DialogueBlock | DividerBlock | QuoteBlock | ListBlock;
|
|
90
|
+
interface BlockContent {
|
|
91
|
+
version: 2;
|
|
92
|
+
blocks: Block[];
|
|
93
|
+
}
|
|
94
|
+
interface Character {
|
|
95
|
+
id: string;
|
|
96
|
+
name: string;
|
|
97
|
+
profileImage: string | null;
|
|
98
|
+
defaultColor: string;
|
|
99
|
+
description?: string | null;
|
|
100
|
+
}
|
|
101
|
+
declare function createEmptyParagraphBlock(): ParagraphBlock;
|
|
102
|
+
declare function generateBlockId(): string;
|
|
103
|
+
declare function textNodesToPlainText(nodes: TextNode[]): string;
|
|
104
|
+
declare function generatePreviewFromBlocks(blocks: Block[], maxLength?: number): string;
|
|
105
|
+
declare function countBlocksCharacters(blocks: Block[]): number;
|
|
106
|
+
|
|
107
|
+
interface BlockEditorProps {
|
|
108
|
+
initialBlocks?: Block[];
|
|
109
|
+
onChange: (blocks: Block[]) => void;
|
|
110
|
+
onCharCountChange?: (count: number) => void;
|
|
111
|
+
}
|
|
112
|
+
declare function BlockEditor({ initialBlocks, onChange, onCharCountChange, }: BlockEditorProps): react_jsx_runtime.JSX.Element;
|
|
113
|
+
|
|
114
|
+
interface BlockMenuProps {
|
|
115
|
+
position: {
|
|
116
|
+
x: number;
|
|
117
|
+
y: number;
|
|
118
|
+
};
|
|
119
|
+
onClose: () => void;
|
|
120
|
+
onSelect: (type: Block['type']) => void;
|
|
121
|
+
}
|
|
122
|
+
declare function BlockMenu({ position, onClose, onSelect }: BlockMenuProps): react_jsx_runtime.JSX.Element;
|
|
123
|
+
|
|
124
|
+
interface TextBlockEditorProps {
|
|
125
|
+
block: ParagraphBlock | HeadingBlock | QuoteBlock | ListBlock | ImageBlock;
|
|
126
|
+
onUpdate: (updates: Partial<Block>) => void;
|
|
127
|
+
onMergeWithPrevious?: () => void;
|
|
128
|
+
onSplitBlock?: (beforeText: TextNode[], afterText: TextNode[]) => void;
|
|
129
|
+
onPasteBlocks?: (beforeText: TextNode[], pastedLines: string[], afterText: TextNode[]) => void;
|
|
130
|
+
onDeleteEmptyBlock?: () => void;
|
|
131
|
+
onFocusPrevious?: () => void;
|
|
132
|
+
onFocusNext?: () => void;
|
|
133
|
+
isOnlyBlock?: boolean;
|
|
134
|
+
}
|
|
135
|
+
declare function TextBlockEditor({ block, onUpdate, onMergeWithPrevious, onSplitBlock, onPasteBlocks, onDeleteEmptyBlock, onFocusPrevious, onFocusNext, isOnlyBlock }: TextBlockEditorProps): react_jsx_runtime.JSX.Element;
|
|
136
|
+
|
|
137
|
+
interface ImageGalleryEditorProps {
|
|
138
|
+
block: ImageGalleryBlock;
|
|
139
|
+
onUpdate: (updates: Partial<ImageGalleryBlock>) => void;
|
|
140
|
+
}
|
|
141
|
+
declare function ImageGalleryEditor({ block, onUpdate }: ImageGalleryEditorProps): react_jsx_runtime.JSX.Element;
|
|
142
|
+
|
|
143
|
+
interface LinkCardEditorProps {
|
|
144
|
+
block: LinkCardBlock;
|
|
145
|
+
onUpdate: (updates: Partial<LinkCardBlock>) => void;
|
|
146
|
+
}
|
|
147
|
+
declare function LinkCardEditor({ block, onUpdate }: LinkCardEditorProps): react_jsx_runtime.JSX.Element;
|
|
148
|
+
|
|
149
|
+
interface LinkEmbedEditorProps {
|
|
150
|
+
block: LinkEmbedBlock;
|
|
151
|
+
onUpdate: (updates: Partial<LinkEmbedBlock>) => void;
|
|
152
|
+
}
|
|
153
|
+
declare function LinkEmbedEditor({ block, onUpdate }: LinkEmbedEditorProps): react_jsx_runtime.JSX.Element;
|
|
154
|
+
|
|
155
|
+
interface DialogueEditorProps {
|
|
156
|
+
block: DialogueBlock;
|
|
157
|
+
onUpdate: (updates: Partial<DialogueBlock>) => void;
|
|
158
|
+
}
|
|
159
|
+
declare function DialogueEditor({ block, onUpdate }: DialogueEditorProps): react_jsx_runtime.JSX.Element;
|
|
160
|
+
|
|
161
|
+
interface DividerEditorProps {
|
|
162
|
+
block: DividerBlock;
|
|
163
|
+
onUpdate: (updates: Partial<DividerBlock>) => void;
|
|
164
|
+
}
|
|
165
|
+
declare function DividerEditor({ block, onUpdate }: DividerEditorProps): react_jsx_runtime.JSX.Element;
|
|
166
|
+
|
|
167
|
+
interface ImageComponentProps {
|
|
168
|
+
src: string;
|
|
169
|
+
alt: string;
|
|
170
|
+
fill?: boolean;
|
|
171
|
+
width?: number;
|
|
172
|
+
height?: number;
|
|
173
|
+
className?: string;
|
|
174
|
+
style?: React.CSSProperties;
|
|
175
|
+
}
|
|
176
|
+
interface EditorServices {
|
|
177
|
+
uploadImage: (file: File, category: string) => Promise<string>;
|
|
178
|
+
fetchOGData: (url: string) => Promise<OGData>;
|
|
179
|
+
getCharacters?: () => Promise<Character[]>;
|
|
180
|
+
ImageComponent?: React.ComponentType<ImageComponentProps>;
|
|
181
|
+
}
|
|
182
|
+
interface EditorProviderProps {
|
|
183
|
+
services: EditorServices;
|
|
184
|
+
children: ReactNode;
|
|
185
|
+
}
|
|
186
|
+
declare function EditorProvider({ services, children }: EditorProviderProps): react_jsx_runtime.JSX.Element;
|
|
187
|
+
declare function useEditorServices(): EditorServices;
|
|
188
|
+
declare function useOptionalEditorServices(): EditorServices | null;
|
|
189
|
+
declare function DefaultImage({ src, alt, fill, width, height, className, style, }: ImageComponentProps): react_jsx_runtime.JSX.Element;
|
|
190
|
+
|
|
191
|
+
export { type Block, type BlockContent, BlockEditor, BlockMenu, type BlockType, type Character, DefaultImage, type DialogueBlock, DialogueEditor, type DialogueLine, type DividerBlock, DividerEditor, EditorProvider, type EditorServices, type HeadingBlock, type ImageBlock, type ImageComponentProps, type ImageData, type ImageGalleryBlock, ImageGalleryEditor, type LinkCardBlock, LinkCardEditor, type LinkEmbedBlock, LinkEmbedEditor, type ListBlock, type OGData, type ParagraphBlock, type QuoteBlock, TextBlockEditor, type TextNode, countBlocksCharacters, createEmptyParagraphBlock, generateBlockId, generatePreviewFromBlocks, textNodesToPlainText, useEditorServices, useOptionalEditorServices };
|