@meowdown/react 0.37.1 → 0.39.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.d.ts +40 -2
- package/dist/index.js +131 -52
- package/package.json +3 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ReactElement, ReactNode, Ref } from "react";
|
|
2
|
-
import { AcceptPendingReplacementOptions, ExitBoundaryHandler, FileClickHandler, FileLinkResolver, FilePasteOptions, FileViewOptions, ImageClickHandler, ImageOptions, LinkClickHandler, LinkCopyHandler, MarkMode, PendingReplacement, PendingReplacementOutcome, PlaceholderOptions, StartPendingReplacementOptions, TagClickHandler, TypedEditor, WikilinkClickHandler } from "@meowdown/core";
|
|
2
|
+
import { AcceptPendingReplacementOptions, ExitBoundaryHandler, FileClickHandler, FileLinkResolver, FilePasteOptions, FileViewOptions, ImageClickHandler, ImageOptions, LinkClickHandler, LinkCopyHandler, ListMarker, MarkMode, PendingReplacement, PendingReplacementOutcome, PlaceholderOptions, StartPendingReplacementOptions, TagClickHandler, TypedEditor, WikilinkClickHandler } from "@meowdown/core";
|
|
3
3
|
import { SelectionJSON, SelectionJSON as SelectionJSON$1 } from "@prosekit/core";
|
|
4
4
|
import { useEditor, useExtension, useKeymap } from "@prosekit/react";
|
|
5
5
|
|
|
@@ -318,6 +318,11 @@ interface EditorProps {
|
|
|
318
318
|
* the embed back into the raw link. On by default.
|
|
319
319
|
*/
|
|
320
320
|
embedPaste?: boolean;
|
|
321
|
+
/**
|
|
322
|
+
* Pasting a URL over selected text wraps the selection as a Markdown link
|
|
323
|
+
* `[selected text](url)`; one undo restores the plain text. On by default.
|
|
324
|
+
*/
|
|
325
|
+
linkPaste?: boolean;
|
|
321
326
|
/**
|
|
322
327
|
* Pressing Enter at the end of the document's first heading (the title line)
|
|
323
328
|
* starts a fresh empty bullet on the next line instead of a plain paragraph.
|
|
@@ -384,6 +389,7 @@ declare function MeowdownEditor({
|
|
|
384
389
|
onFileSaveError,
|
|
385
390
|
onImageClick,
|
|
386
391
|
embedPaste,
|
|
392
|
+
linkPaste,
|
|
387
393
|
bulletAfterHeading,
|
|
388
394
|
frontmatter,
|
|
389
395
|
blockHandle,
|
|
@@ -398,6 +404,35 @@ declare function MeowdownEditor({
|
|
|
398
404
|
}: EditorProps): import("react").JSX.Element;
|
|
399
405
|
//#endregion
|
|
400
406
|
//#region src/components/markdown-view.d.ts
|
|
407
|
+
/** Payload for {@link TaskClickHandler}. */
|
|
408
|
+
interface TaskClickPayload {
|
|
409
|
+
/**
|
|
410
|
+
* Zero-based position of the clicked checkbox among all the checkboxes this
|
|
411
|
+
* view renders, in document order. Stable for a given `markdown`, so a host
|
|
412
|
+
* can map it back to the corresponding task item in its own parse of the
|
|
413
|
+
* same source.
|
|
414
|
+
*/
|
|
415
|
+
index: number;
|
|
416
|
+
/** The checkbox's rendered state. The view never flips it — see the handler doc. */
|
|
417
|
+
checked: boolean;
|
|
418
|
+
/** The item's list marker as written (`+` renders a circle checkbox, `-`/`*` a square). */
|
|
419
|
+
marker: ListMarker;
|
|
420
|
+
/**
|
|
421
|
+
* First line of the item's own inline content, exactly as it appears in the
|
|
422
|
+
* source after the `[ ]`/`[x]` marker and one space. A host locating the task
|
|
423
|
+
* by {@link index} can cross-check this against its own parse and refuse a
|
|
424
|
+
* mismatch instead of toggling the wrong item.
|
|
425
|
+
*/
|
|
426
|
+
text: string;
|
|
427
|
+
/** The originating click. Read modifier keys or position a popover from it. */
|
|
428
|
+
event: globalThis.MouseEvent;
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Called when a rendered task checkbox is clicked. The view is a pure render
|
|
432
|
+
* of `markdown` and never flips the box itself: apply the toggle to the
|
|
433
|
+
* source and re-render, exactly like the other click handlers.
|
|
434
|
+
*/
|
|
435
|
+
type TaskClickHandler = (payload: TaskClickPayload) => void;
|
|
401
436
|
interface MarkdownViewProps {
|
|
402
437
|
/** The Markdown to render. Live: changing it re-renders the content. */
|
|
403
438
|
markdown: string;
|
|
@@ -413,6 +448,8 @@ interface MarkdownViewProps {
|
|
|
413
448
|
onLinkClick?: LinkClickHandler;
|
|
414
449
|
/** Called when a rendered image is clicked. Pass a stable function. */
|
|
415
450
|
onImageClick?: ImageClickHandler;
|
|
451
|
+
/** Called when a rendered task checkbox is clicked. Pass a stable function. */
|
|
452
|
+
onTaskClick?: TaskClickHandler;
|
|
416
453
|
/** Extra class on the content root (alongside `ProseMirror meowdown-content`). */
|
|
417
454
|
className?: string;
|
|
418
455
|
}
|
|
@@ -435,7 +472,8 @@ declare function MarkdownView({
|
|
|
435
472
|
onWikilinkClick,
|
|
436
473
|
onLinkClick,
|
|
437
474
|
onImageClick,
|
|
475
|
+
onTaskClick,
|
|
438
476
|
className
|
|
439
477
|
}: MarkdownViewProps): ReactElement;
|
|
440
478
|
//#endregion
|
|
441
|
-
export { type EditorHandle, type EditorMode, type EditorProps, type EditorStateSnapshot, MarkdownView, type MarkdownViewProps, MeowdownEditor, type PendingReplacementResolveHandler, type SelectionHint, type SelectionJSON, type SelectionMenuContext, type SelectionMenuItem, type SelectionMenuSearchHandler, type SlashMenuItem, type SlashMenuSearchHandler, type TagItem, type TagSearchHandler, type TimeFormat, type WikilinkItem, type WikilinkSearchHandler, useEditor, useExtension, useKeymap };
|
|
479
|
+
export { type EditorHandle, type EditorMode, type EditorProps, type EditorStateSnapshot, MarkdownView, type MarkdownViewProps, MeowdownEditor, type PendingReplacementResolveHandler, type SelectionHint, type SelectionJSON, type SelectionMenuContext, type SelectionMenuItem, type SelectionMenuSearchHandler, type SlashMenuItem, type SlashMenuSearchHandler, type TagItem, type TagSearchHandler, type TaskClickHandler, type TaskClickPayload, type TimeFormat, type WikilinkItem, type WikilinkSearchHandler, useEditor, useExtension, useKeymap };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { clsx } from "clsx/lite";
|
|
2
|
-
import { Fragment, createElement, useCallback, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
3
|
-
import { codeBlockLanguages, defaultResolveImageUrl, defineBulletAfterHeading, defineCodeBlockPreviewPlugin, defineEditorExtension, defineEmbedPaste, defineExitBoundaryHandler, defineFileClickHandler, defineFilePaste, defineFileView, defineFollowLinkHandler, defineHTMLPaste, defineImage, defineImageClickHandler, defineLinkClickHandler, defineLinkEditKeymap, defineLinkHoverHandler, defineMarkdownCopy, definePendingReplacementHandler, definePlaceholder, defineReadonly, defineSpellCheckPlugin, defineTagClickHandler, defineWikilinkClickHandler, defineWikilinkTrigger, docToMarkdown, getCodeTokens, getMarkBuilders, getPendingReplacement, getSelectedText, getTableColumnAlign, getVirtualElementFromRange, inlineTextToMarkChunks, isCodeBlockPreviewHiddenDecoration, isSelectionInTableCell, listenForTweetHeight, loadKaTeX, markdownToDoc, matchEmbed, renderMathInto } from "@meowdown/core";
|
|
2
|
+
import { Fragment, cloneElement, createElement, useCallback, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { codeBlockLanguages, defaultResolveImageUrl, defineBulletAfterHeading, defineCodeBlockPreviewPlugin, defineEditorExtension, defineEmbedPaste, defineExitBoundaryHandler, defineFileClickHandler, defineFilePaste, defineFileView, defineFollowLinkHandler, defineHTMLPaste, defineImage, defineImageClickHandler, defineLinkClickHandler, defineLinkEditKeymap, defineLinkHoverHandler, defineLinkPaste, defineMarkdownCopy, definePendingReplacementHandler, definePlaceholder, defineReadonly, defineSpellCheckPlugin, defineTagClickHandler, defineWikilinkClickHandler, defineWikilinkTrigger, docToMarkdown, getCodeTokens, getMarkBuilders, getPendingReplacement, getSelectedText, getTableColumnAlign, getVirtualElementFromRange, inlineTextToMarkChunks, isCodeBlockPreviewHiddenDecoration, isSelectionInTableCell, listenForTweetHeight, loadKaTeX, markdownToDoc, matchEmbed, renderMathInto } from "@meowdown/core";
|
|
4
4
|
import { clamp } from "@ocavue/utils";
|
|
5
5
|
import { canUseRegexLookbehind, createEditor, defineDocChangeHandler, defineUpdateHandler, isTextSelection, union } from "@prosekit/core";
|
|
6
6
|
import { Selection, TextSelection } from "@prosekit/pm/state";
|
|
@@ -15,6 +15,7 @@ import { AutocompleteEmpty, AutocompleteItem, AutocompletePopup, AutocompletePos
|
|
|
15
15
|
import { MenuItem, MenuPopup, MenuPositioner } from "@prosekit/react/menu";
|
|
16
16
|
import { TableHandleColumnMenuRoot, TableHandleColumnMenuTrigger, TableHandleColumnPopup, TableHandleColumnPositioner, TableHandleDragPreview, TableHandleDropIndicator, TableHandleRoot, TableHandleRowMenuRoot, TableHandleRowMenuTrigger, TableHandleRowPopup, TableHandleRowPositioner } from "@prosekit/react/table-handle";
|
|
17
17
|
import { Mark } from "@prosekit/pm/model";
|
|
18
|
+
import { BOOLEAN, OVERLOADED_BOOLEAN, getPropertyInfo, possibleStandardNames } from "react-property";
|
|
18
19
|
|
|
19
20
|
//#region src/hooks/use-katex.ts
|
|
20
21
|
/**
|
|
@@ -293,7 +294,7 @@ function DropIndicator$1() {
|
|
|
293
294
|
|
|
294
295
|
//#endregion
|
|
295
296
|
//#region src/components/editor-extensions.tsx
|
|
296
|
-
function EditorExtensions({ markMode, onDocChange, onWikilinkClick, onLinkClick, onTagClick, onExitBoundary, resolveImageUrl, resolveFileInfo, onFileClick, onFilePaste, onFileSaveError, onImageClick, embedPaste, bulletAfterHeading, placeholder, readOnly, wikilinkEnabled, spellCheck }) {
|
|
297
|
+
function EditorExtensions({ markMode, onDocChange, onWikilinkClick, onLinkClick, onTagClick, onExitBoundary, resolveImageUrl, resolveFileInfo, onFileClick, onFilePaste, onFileSaveError, onImageClick, embedPaste, linkPaste, bulletAfterHeading, placeholder, readOnly, wikilinkEnabled, spellCheck }) {
|
|
297
298
|
const editor = useEditor$1();
|
|
298
299
|
useEffect(() => {
|
|
299
300
|
editor.commands.setMarkMode(markMode);
|
|
@@ -350,6 +351,9 @@ function EditorExtensions({ markMode, onDocChange, onWikilinkClick, onLinkClick,
|
|
|
350
351
|
useExtension$1(useMemo(() => {
|
|
351
352
|
return embedPaste ? defineEmbedPaste() : null;
|
|
352
353
|
}, [embedPaste]));
|
|
354
|
+
useExtension$1(useMemo(() => {
|
|
355
|
+
return linkPaste ? defineLinkPaste() : null;
|
|
356
|
+
}, [linkPaste]));
|
|
353
357
|
useExtension$1(useMemo(() => {
|
|
354
358
|
return defineHTMLPaste();
|
|
355
359
|
}, []));
|
|
@@ -1486,7 +1490,7 @@ function resolveSelection(doc, selection) {
|
|
|
1486
1490
|
return TextSelection.between(doc.resolve(anchor), doc.resolve(head));
|
|
1487
1491
|
}
|
|
1488
1492
|
}
|
|
1489
|
-
function ProseKitEditor({ markMode = "focus", initialMarkdown, onDocChange, onSlashMenuSearch, onTagSearch, onWikilinkSearch, onSelectionMenuSearch, selectionMenuAffordance = true, pendingReplacementActions, onPendingReplacementResolve, onWikilinkClick, onLinkClick, onLinkCopy, onTagClick, onExitBoundary, resolveImageUrl, resolveFileLink, resolveFileInfo, onFileClick, onFilePaste, onFileSaveError, onImageClick, embedPaste, bulletAfterHeading, frontmatter = false, blockHandle = true, placeholder, readOnly, spellCheck, timeFormat, editorClassName, ref, children }) {
|
|
1493
|
+
function ProseKitEditor({ markMode = "focus", initialMarkdown, onDocChange, onSlashMenuSearch, onTagSearch, onWikilinkSearch, onSelectionMenuSearch, selectionMenuAffordance = true, pendingReplacementActions, onPendingReplacementResolve, onWikilinkClick, onLinkClick, onLinkCopy, onTagClick, onExitBoundary, resolveImageUrl, resolveFileLink, resolveFileInfo, onFileClick, onFilePaste, onFileSaveError, onImageClick, embedPaste, linkPaste, bulletAfterHeading, frontmatter = false, blockHandle = true, placeholder, readOnly, spellCheck, timeFormat, editorClassName, ref, children }) {
|
|
1490
1494
|
const [editor] = useState(() => {
|
|
1491
1495
|
const editor = createEditor({ extension: union(defineEditorExtension({
|
|
1492
1496
|
resolveFileLink,
|
|
@@ -1628,6 +1632,7 @@ function ProseKitEditor({ markMode = "focus", initialMarkdown, onDocChange, onSl
|
|
|
1628
1632
|
onFileSaveError,
|
|
1629
1633
|
onImageClick,
|
|
1630
1634
|
embedPaste,
|
|
1635
|
+
linkPaste,
|
|
1631
1636
|
bulletAfterHeading,
|
|
1632
1637
|
placeholder,
|
|
1633
1638
|
readOnly,
|
|
@@ -1665,7 +1670,7 @@ function ProseKitEditor({ markMode = "focus", initialMarkdown, onDocChange, onSl
|
|
|
1665
1670
|
|
|
1666
1671
|
//#endregion
|
|
1667
1672
|
//#region src/components/editor.tsx
|
|
1668
|
-
function MeowdownEditor({ mode = "focus", initialMarkdown, onDocChange, onSlashMenuSearch, onTagSearch, onWikilinkSearch, onSelectionMenuSearch, selectionMenuAffordance = true, pendingReplacementActions, onPendingReplacementResolve, onWikilinkClick, onLinkClick, onLinkCopy, onTagClick, onExitBoundary, resolveImageUrl, resolveFileLink, resolveFileInfo, onFileClick, onFilePaste, onFileSaveError, onImageClick, embedPaste = true, bulletAfterHeading = false, frontmatter = false, blockHandle = true, placeholder, readOnly, spellCheck, timeFormat, editorClassName, wrapperClassName, handleRef, children }) {
|
|
1673
|
+
function MeowdownEditor({ mode = "focus", initialMarkdown, onDocChange, onSlashMenuSearch, onTagSearch, onWikilinkSearch, onSelectionMenuSearch, selectionMenuAffordance = true, pendingReplacementActions, onPendingReplacementResolve, onWikilinkClick, onLinkClick, onLinkCopy, onTagClick, onExitBoundary, resolveImageUrl, resolveFileLink, resolveFileInfo, onFileClick, onFilePaste, onFileSaveError, onImageClick, embedPaste = true, linkPaste = true, bulletAfterHeading = false, frontmatter = false, blockHandle = true, placeholder, readOnly, spellCheck, timeFormat, editorClassName, wrapperClassName, handleRef, children }) {
|
|
1669
1674
|
const childRef = useRef(null);
|
|
1670
1675
|
useImperativeHandle(handleRef, () => {
|
|
1671
1676
|
function getMarkdown() {
|
|
@@ -1769,6 +1774,7 @@ function MeowdownEditor({ mode = "focus", initialMarkdown, onDocChange, onSlashM
|
|
|
1769
1774
|
onFileSaveError,
|
|
1770
1775
|
onImageClick,
|
|
1771
1776
|
embedPaste,
|
|
1777
|
+
linkPaste,
|
|
1772
1778
|
bulletAfterHeading,
|
|
1773
1779
|
frontmatter,
|
|
1774
1780
|
blockHandle,
|
|
@@ -1783,51 +1789,102 @@ function MeowdownEditor({ mode = "focus", initialMarkdown, onDocChange, onSlashM
|
|
|
1783
1789
|
}
|
|
1784
1790
|
|
|
1785
1791
|
//#endregion
|
|
1786
|
-
//#region src/components/
|
|
1787
|
-
const
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1792
|
+
//#region src/components/attributes-to-props.ts
|
|
1793
|
+
const UNCONTROLLED_COMPONENT_ATTRIBUTES = ["checked", "value"];
|
|
1794
|
+
const UNCONTROLLED_COMPONENT_NAMES = [
|
|
1795
|
+
"input",
|
|
1796
|
+
"select",
|
|
1797
|
+
"textarea"
|
|
1798
|
+
];
|
|
1799
|
+
/**
|
|
1800
|
+
* Converts HTML/SVG DOM attributes to React props.
|
|
1801
|
+
*
|
|
1802
|
+
* @param attributes - HTML/SVG DOM attributes.
|
|
1803
|
+
* @param nodeName - DOM node name.
|
|
1804
|
+
* @returns - React props.
|
|
1805
|
+
*/
|
|
1806
|
+
function attributesToProps(attributes = {}, nodeName) {
|
|
1807
|
+
const props = {};
|
|
1808
|
+
const isInputValueOnly = nodeName === "input" || !!attributes["reset"] || !!attributes["submit"];
|
|
1809
|
+
for (const [attributeName, attributeValue] of Object.entries(attributes)) {
|
|
1810
|
+
if (attributeValue === void 0) continue;
|
|
1811
|
+
const attributeNameLowerCased = attributeName.toLowerCase();
|
|
1812
|
+
if (attributeNameLowerCased === "style") continue;
|
|
1813
|
+
if (attributeNameLowerCased === "contenteditable") continue;
|
|
1814
|
+
if (attributeNameLowerCased.startsWith("aria-") || attributeNameLowerCased.startsWith("data-")) {
|
|
1815
|
+
props[attributeName] = attributeValue;
|
|
1816
|
+
continue;
|
|
1817
|
+
}
|
|
1818
|
+
let propName = getPropName(attributeNameLowerCased);
|
|
1819
|
+
if (propName) {
|
|
1820
|
+
const propertyInfo = getPropertyInfo(propName);
|
|
1821
|
+
if (UNCONTROLLED_COMPONENT_ATTRIBUTES.includes(propName) && UNCONTROLLED_COMPONENT_NAMES.includes(nodeName) && !isInputValueOnly) propName = getPropName("default" + attributeNameLowerCased);
|
|
1822
|
+
props[propName] = attributeValue;
|
|
1823
|
+
switch (propertyInfo?.type) {
|
|
1824
|
+
case BOOLEAN:
|
|
1825
|
+
props[propName] = true;
|
|
1826
|
+
break;
|
|
1827
|
+
case OVERLOADED_BOOLEAN:
|
|
1828
|
+
if (attributeValue === "") props[propName] = true;
|
|
1829
|
+
break;
|
|
1830
|
+
}
|
|
1831
|
+
continue;
|
|
1832
|
+
}
|
|
1833
|
+
props[attributeName] = attributeValue;
|
|
1802
1834
|
}
|
|
1803
1835
|
return props;
|
|
1804
1836
|
}
|
|
1805
1837
|
/**
|
|
1806
|
-
*
|
|
1807
|
-
*
|
|
1808
|
-
*
|
|
1809
|
-
*
|
|
1838
|
+
* Gets prop name from lowercased attribute name.
|
|
1839
|
+
*
|
|
1840
|
+
* @param attributeName - Lowercased attribute name.
|
|
1841
|
+
* @returns - Prop name.
|
|
1810
1842
|
*/
|
|
1811
|
-
function
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1843
|
+
function getPropName(attributeName) {
|
|
1844
|
+
return possibleStandardNames[attributeName];
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
//#endregion
|
|
1848
|
+
//#region src/components/dom-output-spec.tsx
|
|
1849
|
+
function normalizeDOMOutputSpec(domSpec) {
|
|
1850
|
+
const spec = domSpec;
|
|
1851
|
+
if (!spec || !Array.isArray(spec)) return;
|
|
1852
|
+
const tag = spec[0];
|
|
1816
1853
|
let childStart = 1;
|
|
1817
1854
|
let attrs;
|
|
1818
|
-
const second =
|
|
1855
|
+
const second = spec[1];
|
|
1819
1856
|
if (second != null && second !== 0 && typeof second === "object" && !Array.isArray(second)) {
|
|
1820
1857
|
attrs = second;
|
|
1821
1858
|
childStart = 2;
|
|
1822
1859
|
}
|
|
1823
|
-
const
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1860
|
+
const rest = spec.slice(childStart);
|
|
1861
|
+
return [
|
|
1862
|
+
tag,
|
|
1863
|
+
attrs,
|
|
1864
|
+
rest
|
|
1865
|
+
];
|
|
1827
1866
|
}
|
|
1828
1867
|
|
|
1829
1868
|
//#endregion
|
|
1830
1869
|
//#region src/components/markdown-view.tsx
|
|
1870
|
+
/**
|
|
1871
|
+
* Convert a ProseMirror `DOMOutputSpec` into a React node, substituting `content`
|
|
1872
|
+
* for the spec's content hole (`0`). Reused for every node/mark spec the static
|
|
1873
|
+
* walker does not special-case, so blocks and plain marks render off their real
|
|
1874
|
+
* `toDOM`, exactly as the editor serializes them.
|
|
1875
|
+
*/
|
|
1876
|
+
function outputSpecToReact(spec, content, context) {
|
|
1877
|
+
const key = context.keyCounter.value++;
|
|
1878
|
+
if (typeof spec === "string") return spec;
|
|
1879
|
+
if (spec === 0) return /* @__PURE__ */ jsx(Fragment, { children: content }, key);
|
|
1880
|
+
const normalized = normalizeDOMOutputSpec(spec);
|
|
1881
|
+
if (!normalized) return null;
|
|
1882
|
+
const [tag, attrs, rest] = normalized;
|
|
1883
|
+
const reactProps = { ...attributesToProps(attrs, tag) };
|
|
1884
|
+
reactProps.key = `${key} ${JSON.stringify(attrs)}`;
|
|
1885
|
+
if (tag === "input" && attrs?.["type"] === "checkbox") reactProps.readOnly = true;
|
|
1886
|
+
return createElement(tag, reactProps, ...rest.map((child) => outputSpecToReact(child, content, context)));
|
|
1887
|
+
}
|
|
1831
1888
|
function WikilinkChip(props) {
|
|
1832
1889
|
const { target, display, onWikilinkClick, children } = props;
|
|
1833
1890
|
return /* @__PURE__ */ jsxs("span", {
|
|
@@ -2039,7 +2096,7 @@ function wrapMark(mark, children, context) {
|
|
|
2039
2096
|
default: {
|
|
2040
2097
|
const toDOM = mark.type.spec.toDOM;
|
|
2041
2098
|
if (!toDOM) return children;
|
|
2042
|
-
return outputSpecToReact(toDOM(mark, true), children);
|
|
2099
|
+
return outputSpecToReact(toDOM(mark, true), children, context);
|
|
2043
2100
|
}
|
|
2044
2101
|
}
|
|
2045
2102
|
}
|
|
@@ -2077,8 +2134,31 @@ function renderInline(node, context) {
|
|
|
2077
2134
|
marks: Mark.setFrom(marks)
|
|
2078
2135
|
})), 0, context);
|
|
2079
2136
|
}
|
|
2080
|
-
function renderBlock(node,
|
|
2081
|
-
|
|
2137
|
+
function renderBlock(node, context) {
|
|
2138
|
+
const key = context.keyCounter.value++;
|
|
2139
|
+
const typeName = node.type.name;
|
|
2140
|
+
let handleTaskClick;
|
|
2141
|
+
if (typeName === "list") {
|
|
2142
|
+
const attrs = node.attrs;
|
|
2143
|
+
const { onTaskClick } = context;
|
|
2144
|
+
if (attrs.kind === "task" && onTaskClick) {
|
|
2145
|
+
const index = context.taskCounter.value++;
|
|
2146
|
+
const checked = attrs.checked === true;
|
|
2147
|
+
const marker = attrs.marker ?? null;
|
|
2148
|
+
const text = node.firstChild?.isTextblock ? node.firstChild.textContent.split("\n", 1)[0] ?? "" : "";
|
|
2149
|
+
handleTaskClick = (event) => {
|
|
2150
|
+
event.preventDefault();
|
|
2151
|
+
onTaskClick({
|
|
2152
|
+
index,
|
|
2153
|
+
checked,
|
|
2154
|
+
marker,
|
|
2155
|
+
text,
|
|
2156
|
+
event: event.nativeEvent
|
|
2157
|
+
});
|
|
2158
|
+
};
|
|
2159
|
+
}
|
|
2160
|
+
}
|
|
2161
|
+
if (typeName === "codeBlock") {
|
|
2082
2162
|
const attrs = node.attrs;
|
|
2083
2163
|
const language = typeof attrs.language === "string" ? attrs.language : "";
|
|
2084
2164
|
if (language === "math") return /* @__PURE__ */ jsx(MathCodeBlock, { code: node.textContent }, key);
|
|
@@ -2090,13 +2170,12 @@ function renderBlock(node, key, context) {
|
|
|
2090
2170
|
const toDOM = node.type.spec.toDOM;
|
|
2091
2171
|
if (node.isTextblock) {
|
|
2092
2172
|
const inline = renderInline(node, context);
|
|
2093
|
-
return toDOM ? outputSpecToReact(toDOM(node), inline,
|
|
2173
|
+
return toDOM ? outputSpecToReact(toDOM(node), inline, context) : /* @__PURE__ */ jsx(Fragment, { children: inline }, key);
|
|
2094
2174
|
}
|
|
2095
|
-
const children =
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
return toDOM ? outputSpecToReact(toDOM(node), children, key) : /* @__PURE__ */ jsx(Fragment, { children }, key);
|
|
2175
|
+
const children = node.content.content.map((child) => renderBlock(child, context));
|
|
2176
|
+
const reactNode = toDOM ? outputSpecToReact(toDOM(node), children, context) : /* @__PURE__ */ jsx(Fragment, { children }, key);
|
|
2177
|
+
if (typeName === "list" && handleTaskClick && typeof reactNode !== "string" && reactNode != null) return cloneElement(reactNode, { onClick: handleTaskClick });
|
|
2178
|
+
return reactNode;
|
|
2100
2179
|
}
|
|
2101
2180
|
/**
|
|
2102
2181
|
* Render Markdown to a read-only React tree that looks exactly like the editor
|
|
@@ -2109,27 +2188,27 @@ function renderBlock(node, key, context) {
|
|
|
2109
2188
|
* Callbacks (`onWikilinkClick`, etc.) should be stable; pass them via
|
|
2110
2189
|
* `useCallback` to avoid re-rendering the whole tree.
|
|
2111
2190
|
*/
|
|
2112
|
-
function MarkdownView({ markdown, markMode = "hide", frontmatter = false, resolveImageUrl, onWikilinkClick, onLinkClick, onImageClick, className }) {
|
|
2191
|
+
function MarkdownView({ markdown, markMode = "hide", frontmatter = false, resolveImageUrl, onWikilinkClick, onLinkClick, onImageClick, onTaskClick, className }) {
|
|
2113
2192
|
const content = useMemo(() => {
|
|
2114
2193
|
const doc = markdownToDoc(markdown, { frontmatter });
|
|
2115
2194
|
const context = {
|
|
2116
2195
|
resolveImageUrl,
|
|
2117
2196
|
onWikilinkClick,
|
|
2118
2197
|
onLinkClick,
|
|
2119
|
-
onImageClick
|
|
2198
|
+
onImageClick,
|
|
2199
|
+
onTaskClick,
|
|
2200
|
+
taskCounter: { value: 0 },
|
|
2201
|
+
keyCounter: { value: 0 }
|
|
2120
2202
|
};
|
|
2121
|
-
|
|
2122
|
-
doc.forEach((node, _offset, index) => {
|
|
2123
|
-
blocks.push(renderBlock(node, index, context));
|
|
2124
|
-
});
|
|
2125
|
-
return blocks;
|
|
2203
|
+
return doc.content.content.map((node) => renderBlock(node, context));
|
|
2126
2204
|
}, [
|
|
2127
2205
|
markdown,
|
|
2128
2206
|
frontmatter,
|
|
2129
2207
|
resolveImageUrl,
|
|
2130
2208
|
onWikilinkClick,
|
|
2131
2209
|
onLinkClick,
|
|
2132
|
-
onImageClick
|
|
2210
|
+
onImageClick,
|
|
2211
|
+
onTaskClick
|
|
2133
2212
|
]);
|
|
2134
2213
|
return /* @__PURE__ */ jsx("div", {
|
|
2135
2214
|
className: clsx("ProseMirror", "meowdown-content", className),
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meowdown/react",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.39.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
"@prosekit/react": "^0.8.0-beta.19",
|
|
26
26
|
"clsx": "^2.1.1",
|
|
27
27
|
"lucide-react": "^1.21.0",
|
|
28
|
-
"
|
|
28
|
+
"react-property": "^2.0.2",
|
|
29
|
+
"@meowdown/core": "0.39.0"
|
|
29
30
|
},
|
|
30
31
|
"peerDependencies": {
|
|
31
32
|
"react": "^19.0.0",
|