@create-markdown/react 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/dist/index.js ADDED
@@ -0,0 +1,525 @@
1
+ // src/block-renderer.tsx
2
+ import { jsxDEV, Fragment } from "react/jsx-dev-runtime";
3
+ function BlockRenderer({
4
+ blocks,
5
+ className,
6
+ customRenderers
7
+ }) {
8
+ return /* @__PURE__ */ jsxDEV("div", {
9
+ className,
10
+ children: blocks.map((block) => /* @__PURE__ */ jsxDEV(BlockElement, {
11
+ block,
12
+ customRenderers
13
+ }, block.id, false, undefined, this))
14
+ }, undefined, false, undefined, this);
15
+ }
16
+ function BlockElement({
17
+ block,
18
+ customRenderers
19
+ }) {
20
+ const CustomRenderer = customRenderers?.[block.type];
21
+ if (CustomRenderer) {
22
+ return /* @__PURE__ */ jsxDEV(CustomRenderer, {
23
+ block
24
+ }, undefined, false, undefined, this);
25
+ }
26
+ switch (block.type) {
27
+ case "paragraph":
28
+ return /* @__PURE__ */ jsxDEV(ParagraphRenderer, {
29
+ block
30
+ }, undefined, false, undefined, this);
31
+ case "heading":
32
+ return /* @__PURE__ */ jsxDEV(HeadingRenderer, {
33
+ block
34
+ }, undefined, false, undefined, this);
35
+ case "bulletList":
36
+ return /* @__PURE__ */ jsxDEV(BulletListRenderer, {
37
+ block,
38
+ customRenderers
39
+ }, undefined, false, undefined, this);
40
+ case "numberedList":
41
+ return /* @__PURE__ */ jsxDEV(NumberedListRenderer, {
42
+ block,
43
+ customRenderers
44
+ }, undefined, false, undefined, this);
45
+ case "checkList":
46
+ return /* @__PURE__ */ jsxDEV(CheckListRenderer, {
47
+ block
48
+ }, undefined, false, undefined, this);
49
+ case "codeBlock":
50
+ return /* @__PURE__ */ jsxDEV(CodeBlockRenderer, {
51
+ block
52
+ }, undefined, false, undefined, this);
53
+ case "blockquote":
54
+ return /* @__PURE__ */ jsxDEV(BlockquoteRenderer, {
55
+ block
56
+ }, undefined, false, undefined, this);
57
+ case "table":
58
+ return /* @__PURE__ */ jsxDEV(TableRenderer, {
59
+ block
60
+ }, undefined, false, undefined, this);
61
+ case "image":
62
+ return /* @__PURE__ */ jsxDEV(ImageRenderer, {
63
+ block
64
+ }, undefined, false, undefined, this);
65
+ case "divider":
66
+ return /* @__PURE__ */ jsxDEV(DividerRenderer, {}, undefined, false, undefined, this);
67
+ case "callout":
68
+ return /* @__PURE__ */ jsxDEV(CalloutRenderer, {
69
+ block
70
+ }, undefined, false, undefined, this);
71
+ default:
72
+ return null;
73
+ }
74
+ }
75
+ function InlineContent({ spans }) {
76
+ return /* @__PURE__ */ jsxDEV(Fragment, {
77
+ children: spans.map((span, index) => /* @__PURE__ */ jsxDEV(SpanElement, {
78
+ span
79
+ }, index, false, undefined, this))
80
+ }, undefined, false, undefined, this);
81
+ }
82
+ function SpanElement({ span }) {
83
+ let content = span.text;
84
+ const styles = span.styles;
85
+ if (styles.code) {
86
+ content = /* @__PURE__ */ jsxDEV("code", {
87
+ children: content
88
+ }, undefined, false, undefined, this);
89
+ }
90
+ if (styles.highlight) {
91
+ content = /* @__PURE__ */ jsxDEV("mark", {
92
+ children: content
93
+ }, undefined, false, undefined, this);
94
+ }
95
+ if (styles.strikethrough) {
96
+ content = /* @__PURE__ */ jsxDEV("del", {
97
+ children: content
98
+ }, undefined, false, undefined, this);
99
+ }
100
+ if (styles.underline) {
101
+ content = /* @__PURE__ */ jsxDEV("u", {
102
+ children: content
103
+ }, undefined, false, undefined, this);
104
+ }
105
+ if (styles.italic) {
106
+ content = /* @__PURE__ */ jsxDEV("em", {
107
+ children: content
108
+ }, undefined, false, undefined, this);
109
+ }
110
+ if (styles.bold) {
111
+ content = /* @__PURE__ */ jsxDEV("strong", {
112
+ children: content
113
+ }, undefined, false, undefined, this);
114
+ }
115
+ if (styles.link) {
116
+ content = /* @__PURE__ */ jsxDEV("a", {
117
+ href: styles.link.url,
118
+ title: styles.link.title,
119
+ children: content
120
+ }, undefined, false, undefined, this);
121
+ }
122
+ return /* @__PURE__ */ jsxDEV(Fragment, {
123
+ children: content
124
+ }, undefined, false, undefined, this);
125
+ }
126
+ function ParagraphRenderer({ block }) {
127
+ return /* @__PURE__ */ jsxDEV("p", {
128
+ children: /* @__PURE__ */ jsxDEV(InlineContent, {
129
+ spans: block.content
130
+ }, undefined, false, undefined, this)
131
+ }, undefined, false, undefined, this);
132
+ }
133
+ function HeadingRenderer({ block }) {
134
+ const Tag = `h${block.props.level}`;
135
+ return /* @__PURE__ */ jsxDEV(Tag, {
136
+ children: /* @__PURE__ */ jsxDEV(InlineContent, {
137
+ spans: block.content
138
+ }, undefined, false, undefined, this)
139
+ }, undefined, false, undefined, this);
140
+ }
141
+ function BulletListRenderer({
142
+ block,
143
+ customRenderers
144
+ }) {
145
+ return /* @__PURE__ */ jsxDEV("ul", {
146
+ children: block.children.map((child) => /* @__PURE__ */ jsxDEV("li", {
147
+ children: [
148
+ /* @__PURE__ */ jsxDEV(InlineContent, {
149
+ spans: child.content
150
+ }, undefined, false, undefined, this),
151
+ child.children.length > 0 && /* @__PURE__ */ jsxDEV(BlockElement, {
152
+ block: child,
153
+ customRenderers
154
+ }, undefined, false, undefined, this)
155
+ ]
156
+ }, child.id, true, undefined, this))
157
+ }, undefined, false, undefined, this);
158
+ }
159
+ function NumberedListRenderer({
160
+ block,
161
+ customRenderers
162
+ }) {
163
+ return /* @__PURE__ */ jsxDEV("ol", {
164
+ children: block.children.map((child) => /* @__PURE__ */ jsxDEV("li", {
165
+ children: [
166
+ /* @__PURE__ */ jsxDEV(InlineContent, {
167
+ spans: child.content
168
+ }, undefined, false, undefined, this),
169
+ child.children.length > 0 && /* @__PURE__ */ jsxDEV(BlockElement, {
170
+ block: child,
171
+ customRenderers
172
+ }, undefined, false, undefined, this)
173
+ ]
174
+ }, child.id, true, undefined, this))
175
+ }, undefined, false, undefined, this);
176
+ }
177
+ function CheckListRenderer({ block }) {
178
+ return /* @__PURE__ */ jsxDEV("div", {
179
+ style: { display: "flex", alignItems: "flex-start", gap: "0.5rem" },
180
+ children: [
181
+ /* @__PURE__ */ jsxDEV("input", {
182
+ type: "checkbox",
183
+ checked: block.props.checked,
184
+ readOnly: true,
185
+ style: { marginTop: "0.25rem" }
186
+ }, undefined, false, undefined, this),
187
+ /* @__PURE__ */ jsxDEV("span", {
188
+ style: { textDecoration: block.props.checked ? "line-through" : "none" },
189
+ children: /* @__PURE__ */ jsxDEV(InlineContent, {
190
+ spans: block.content
191
+ }, undefined, false, undefined, this)
192
+ }, undefined, false, undefined, this)
193
+ ]
194
+ }, undefined, true, undefined, this);
195
+ }
196
+ function CodeBlockRenderer({ block }) {
197
+ const code = block.content.map((span) => span.text).join("");
198
+ const language = block.props.language;
199
+ return /* @__PURE__ */ jsxDEV("pre", {
200
+ children: /* @__PURE__ */ jsxDEV("code", {
201
+ className: language ? `language-${language}` : undefined,
202
+ children: code
203
+ }, undefined, false, undefined, this)
204
+ }, undefined, false, undefined, this);
205
+ }
206
+ function BlockquoteRenderer({ block }) {
207
+ return /* @__PURE__ */ jsxDEV("blockquote", {
208
+ children: /* @__PURE__ */ jsxDEV(InlineContent, {
209
+ spans: block.content
210
+ }, undefined, false, undefined, this)
211
+ }, undefined, false, undefined, this);
212
+ }
213
+ function TableRenderer({ block }) {
214
+ const { headers, rows, alignments } = block.props;
215
+ const getAlignment = (index) => {
216
+ return alignments?.[index] ?? undefined;
217
+ };
218
+ return /* @__PURE__ */ jsxDEV("table", {
219
+ children: [
220
+ headers.length > 0 && /* @__PURE__ */ jsxDEV("thead", {
221
+ children: /* @__PURE__ */ jsxDEV("tr", {
222
+ children: headers.map((header, i) => /* @__PURE__ */ jsxDEV("th", {
223
+ style: { textAlign: getAlignment(i) },
224
+ children: header
225
+ }, i, false, undefined, this))
226
+ }, undefined, false, undefined, this)
227
+ }, undefined, false, undefined, this),
228
+ /* @__PURE__ */ jsxDEV("tbody", {
229
+ children: rows.map((row, rowIndex) => /* @__PURE__ */ jsxDEV("tr", {
230
+ children: row.map((cell, cellIndex) => /* @__PURE__ */ jsxDEV("td", {
231
+ style: { textAlign: getAlignment(cellIndex) },
232
+ children: cell
233
+ }, cellIndex, false, undefined, this))
234
+ }, rowIndex, false, undefined, this))
235
+ }, undefined, false, undefined, this)
236
+ ]
237
+ }, undefined, true, undefined, this);
238
+ }
239
+ function ImageRenderer({ block }) {
240
+ return /* @__PURE__ */ jsxDEV("figure", {
241
+ children: [
242
+ /* @__PURE__ */ jsxDEV("img", {
243
+ src: block.props.url,
244
+ alt: block.props.alt ?? "",
245
+ title: block.props.title,
246
+ width: block.props.width,
247
+ height: block.props.height
248
+ }, undefined, false, undefined, this),
249
+ block.props.alt && /* @__PURE__ */ jsxDEV("figcaption", {
250
+ children: block.props.alt
251
+ }, undefined, false, undefined, this)
252
+ ]
253
+ }, undefined, true, undefined, this);
254
+ }
255
+ function DividerRenderer() {
256
+ return /* @__PURE__ */ jsxDEV("hr", {}, undefined, false, undefined, this);
257
+ }
258
+ function CalloutRenderer({ block }) {
259
+ const calloutType = block.props.type;
260
+ const styles = {
261
+ padding: "1rem",
262
+ borderRadius: "0.25rem",
263
+ borderLeft: "4px solid",
264
+ marginBottom: "1rem"
265
+ };
266
+ const colors = {
267
+ info: { borderColor: "#3b82f6", backgroundColor: "#eff6ff" },
268
+ warning: { borderColor: "#f59e0b", backgroundColor: "#fffbeb" },
269
+ tip: { borderColor: "#10b981", backgroundColor: "#ecfdf5" },
270
+ danger: { borderColor: "#ef4444", backgroundColor: "#fef2f2" },
271
+ note: { borderColor: "#6b7280", backgroundColor: "#f9fafb" }
272
+ };
273
+ const colorStyle = colors[calloutType] ?? colors.note;
274
+ return /* @__PURE__ */ jsxDEV("div", {
275
+ style: {
276
+ ...styles,
277
+ borderLeftColor: colorStyle.borderColor,
278
+ backgroundColor: colorStyle.backgroundColor
279
+ },
280
+ role: "alert",
281
+ children: [
282
+ /* @__PURE__ */ jsxDEV("strong", {
283
+ style: { textTransform: "capitalize", display: "block", marginBottom: "0.5rem" },
284
+ children: calloutType
285
+ }, undefined, false, undefined, this),
286
+ /* @__PURE__ */ jsxDEV(InlineContent, {
287
+ spans: block.content
288
+ }, undefined, false, undefined, this)
289
+ ]
290
+ }, undefined, true, undefined, this);
291
+ }
292
+ // src/hooks.ts
293
+ import { useState, useCallback, useMemo } from "react";
294
+ import {
295
+ createDocument,
296
+ insertBlock,
297
+ appendBlock,
298
+ removeBlock,
299
+ updateBlock,
300
+ moveBlock,
301
+ findBlock,
302
+ getBlockIndex,
303
+ clearBlocks,
304
+ setBlocks,
305
+ updateMeta,
306
+ markdownToBlocks,
307
+ blocksToMarkdown
308
+ } from "@create-markdown/core";
309
+ function useDocument(initialBlocks = [], options) {
310
+ const [document, setDocument] = useState(() => createDocument(initialBlocks, options));
311
+ const insert = useCallback((block, index) => {
312
+ setDocument((doc) => insertBlock(doc, block, index));
313
+ }, []);
314
+ const append = useCallback((block) => {
315
+ setDocument((doc) => appendBlock(doc, block));
316
+ }, []);
317
+ const remove = useCallback((blockId) => {
318
+ setDocument((doc) => removeBlock(doc, blockId));
319
+ }, []);
320
+ const update = useCallback((blockId, updates) => {
321
+ setDocument((doc) => updateBlock(doc, blockId, updates));
322
+ }, []);
323
+ const move = useCallback((blockId, newIndex) => {
324
+ setDocument((doc) => moveBlock(doc, blockId, newIndex));
325
+ }, []);
326
+ const find = useCallback((blockId) => findBlock(document, blockId), [document]);
327
+ const getIndex = useCallback((blockId) => getBlockIndex(document, blockId), [document]);
328
+ const clear = useCallback(() => {
329
+ setDocument((doc) => clearBlocks(doc));
330
+ }, []);
331
+ const set = useCallback((blocks) => {
332
+ setDocument((doc) => setBlocks(doc, blocks));
333
+ }, []);
334
+ const toMd = useCallback(() => blocksToMarkdown(document.blocks), [document]);
335
+ const fromMd = useCallback((markdown) => {
336
+ const blocks = markdownToBlocks(markdown);
337
+ setDocument((doc) => setBlocks(doc, blocks));
338
+ }, []);
339
+ const meta = useCallback((newMeta) => {
340
+ setDocument((doc) => updateMeta(doc, newMeta));
341
+ }, []);
342
+ return {
343
+ document,
344
+ blocks: document.blocks,
345
+ insertBlock: insert,
346
+ appendBlock: append,
347
+ removeBlock: remove,
348
+ updateBlock: update,
349
+ moveBlock: move,
350
+ findBlock: find,
351
+ getBlockIndex: getIndex,
352
+ clearBlocks: clear,
353
+ setBlocks: set,
354
+ setDocument,
355
+ toMarkdown: toMd,
356
+ fromMarkdown: fromMd,
357
+ updateMeta: meta
358
+ };
359
+ }
360
+ function useMarkdown(initialMarkdown = "") {
361
+ const [markdown, setMarkdownState] = useState(initialMarkdown);
362
+ const blocks = useMemo(() => markdownToBlocks(markdown), [markdown]);
363
+ const setMarkdown = useCallback((newMarkdown) => {
364
+ setMarkdownState(newMarkdown);
365
+ }, []);
366
+ const setBlocksFromBlocks = useCallback((newBlocks) => {
367
+ const newMarkdown = blocksToMarkdown(newBlocks);
368
+ setMarkdownState(newMarkdown);
369
+ }, []);
370
+ return {
371
+ markdown,
372
+ blocks,
373
+ setMarkdown,
374
+ setBlocks: setBlocksFromBlocks
375
+ };
376
+ }
377
+ function useBlockEditor(documentHook) {
378
+ const { document, blocks, removeBlock: removeBlock2, updateBlock: updateBlock2, moveBlock: moveBlock2, insertBlock: insertBlock2 } = documentHook;
379
+ const [selectedBlockId, setSelectedBlockId] = useState(null);
380
+ const selectedBlock = useMemo(() => selectedBlockId ? findBlock(document, selectedBlockId) : undefined, [document, selectedBlockId]);
381
+ const selectBlock = useCallback((blockId) => {
382
+ setSelectedBlockId(blockId);
383
+ }, []);
384
+ const selectNext = useCallback(() => {
385
+ if (!selectedBlockId) {
386
+ if (blocks.length > 0) {
387
+ setSelectedBlockId(blocks[0].id);
388
+ }
389
+ return;
390
+ }
391
+ const currentIndex = getBlockIndex(document, selectedBlockId);
392
+ if (currentIndex < blocks.length - 1) {
393
+ setSelectedBlockId(blocks[currentIndex + 1].id);
394
+ }
395
+ }, [document, blocks, selectedBlockId]);
396
+ const selectPrevious = useCallback(() => {
397
+ if (!selectedBlockId) {
398
+ if (blocks.length > 0) {
399
+ setSelectedBlockId(blocks[blocks.length - 1].id);
400
+ }
401
+ return;
402
+ }
403
+ const currentIndex = getBlockIndex(document, selectedBlockId);
404
+ if (currentIndex > 0) {
405
+ setSelectedBlockId(blocks[currentIndex - 1].id);
406
+ }
407
+ }, [document, blocks, selectedBlockId]);
408
+ const deleteSelected = useCallback(() => {
409
+ if (!selectedBlockId)
410
+ return;
411
+ const currentIndex = getBlockIndex(document, selectedBlockId);
412
+ removeBlock2(selectedBlockId);
413
+ if (blocks.length > 1) {
414
+ if (currentIndex < blocks.length - 1) {
415
+ setSelectedBlockId(blocks[currentIndex + 1].id);
416
+ } else if (currentIndex > 0) {
417
+ setSelectedBlockId(blocks[currentIndex - 1].id);
418
+ }
419
+ } else {
420
+ setSelectedBlockId(null);
421
+ }
422
+ }, [document, blocks, selectedBlockId, removeBlock2]);
423
+ const updateSelectedContent = useCallback((content) => {
424
+ if (!selectedBlockId)
425
+ return;
426
+ updateBlock2(selectedBlockId, { content });
427
+ }, [selectedBlockId, updateBlock2]);
428
+ const duplicateSelected = useCallback(() => {
429
+ if (!selectedBlockId || !selectedBlock)
430
+ return;
431
+ const currentIndex = getBlockIndex(document, selectedBlockId);
432
+ const clonedBlock = {
433
+ ...selectedBlock,
434
+ id: Math.random().toString(36).substring(2, 9)
435
+ };
436
+ insertBlock2(clonedBlock, currentIndex + 1);
437
+ setSelectedBlockId(clonedBlock.id);
438
+ }, [document, selectedBlockId, selectedBlock, insertBlock2]);
439
+ const moveSelectedUp = useCallback(() => {
440
+ if (!selectedBlockId)
441
+ return;
442
+ const currentIndex = getBlockIndex(document, selectedBlockId);
443
+ if (currentIndex > 0) {
444
+ moveBlock2(selectedBlockId, currentIndex - 1);
445
+ }
446
+ }, [document, selectedBlockId, moveBlock2]);
447
+ const moveSelectedDown = useCallback(() => {
448
+ if (!selectedBlockId)
449
+ return;
450
+ const currentIndex = getBlockIndex(document, selectedBlockId);
451
+ if (currentIndex < blocks.length - 1) {
452
+ moveBlock2(selectedBlockId, currentIndex + 1);
453
+ }
454
+ }, [document, blocks, selectedBlockId, moveBlock2]);
455
+ return {
456
+ selectedBlockId,
457
+ selectedBlock,
458
+ selectBlock,
459
+ selectNext,
460
+ selectPrevious,
461
+ deleteSelected,
462
+ updateSelectedContent,
463
+ duplicateSelected,
464
+ moveSelectedUp,
465
+ moveSelectedDown
466
+ };
467
+ }
468
+
469
+ // src/index.ts
470
+ import {
471
+ paragraph,
472
+ heading,
473
+ h1,
474
+ h2,
475
+ h3,
476
+ h4,
477
+ h5,
478
+ h6,
479
+ bulletList,
480
+ numberedList,
481
+ checkListItem,
482
+ checkList,
483
+ codeBlock,
484
+ blockquote,
485
+ divider,
486
+ image,
487
+ callout,
488
+ text,
489
+ bold,
490
+ italic,
491
+ code,
492
+ link
493
+ } from "@create-markdown/core";
494
+ var VERSION = "0.1.0";
495
+ export {
496
+ useMarkdown,
497
+ useDocument,
498
+ useBlockEditor,
499
+ text,
500
+ paragraph,
501
+ numberedList,
502
+ link,
503
+ italic,
504
+ image,
505
+ heading,
506
+ h6,
507
+ h5,
508
+ h4,
509
+ h3,
510
+ h2,
511
+ h1,
512
+ divider,
513
+ codeBlock,
514
+ code,
515
+ checkListItem,
516
+ checkList,
517
+ callout,
518
+ bulletList,
519
+ bold,
520
+ blockquote,
521
+ VERSION,
522
+ InlineContent,
523
+ BlockRenderer,
524
+ BlockElement
525
+ };
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "@create-markdown/react",
3
+ "version": "0.1.0",
4
+ "description": "React components and hooks for @create-markdown",
5
+ "author": "Val Alexander <val@viewdue.ai>",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/BunsDev/create-markdown",
10
+ "directory": "packages/react"
11
+ },
12
+ "homepage": "https://github.com/BunsDev/create-markdown/tree/main/packages/react",
13
+ "bugs": {
14
+ "url": "https://github.com/BunsDev/create-markdown/issues"
15
+ },
16
+ "keywords": [
17
+ "markdown",
18
+ "react",
19
+ "blocks",
20
+ "editor",
21
+ "typescript"
22
+ ],
23
+ "type": "module",
24
+ "main": "./dist/index.cjs",
25
+ "module": "./dist/index.js",
26
+ "types": "./dist/index.d.ts",
27
+ "exports": {
28
+ ".": {
29
+ "import": {
30
+ "types": "./dist/index.d.ts",
31
+ "default": "./dist/index.js"
32
+ },
33
+ "require": {
34
+ "types": "./dist/index.d.ts",
35
+ "default": "./dist/index.cjs"
36
+ }
37
+ }
38
+ },
39
+ "files": [
40
+ "dist"
41
+ ],
42
+ "scripts": {
43
+ "build": "bun run build:esm && bun run build:cjs && bun run build:types",
44
+ "build:esm": "bun build ./src/index.ts --outfile ./dist/index.js --format esm --external react --external @create-markdown/core",
45
+ "build:cjs": "bun build ./src/index.ts --outfile ./dist/index.cjs --format cjs --external react --external @create-markdown/core",
46
+ "build:types": "tsc --emitDeclarationOnly --declaration --outDir dist",
47
+ "dev": "bun run --watch src/index.ts",
48
+ "clean": "rm -rf dist",
49
+ "typecheck": "tsc --noEmit",
50
+ "test": "vitest run",
51
+ "test:watch": "vitest"
52
+ },
53
+ "dependencies": {
54
+ "@create-markdown/core": "workspace:*"
55
+ },
56
+ "devDependencies": {
57
+ "@testing-library/react": "^16.0.0",
58
+ "@types/react": "^18.2.0",
59
+ "jsdom": "^25.0.0",
60
+ "react": "^18.2.0",
61
+ "react-dom": "^18.2.0",
62
+ "typescript": "^5.3.0",
63
+ "vitest": "^2.1.0"
64
+ },
65
+ "peerDependencies": {
66
+ "react": ">=18.0.0"
67
+ },
68
+ "engines": {
69
+ "node": ">=20.0.0"
70
+ }
71
+ }