@particle-academy/react-fancy 4.7.0 → 4.8.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.cjs +101 -69
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +101 -69
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -2064,6 +2064,14 @@ interface EditorProps {
|
|
|
2064
2064
|
outputFormat?: "html" | "markdown";
|
|
2065
2065
|
lineSpacing?: number;
|
|
2066
2066
|
placeholder?: string;
|
|
2067
|
+
/**
|
|
2068
|
+
* View/edit mode — like the react-fancy inputs. Resolution: this prop →
|
|
2069
|
+
* nearest `<Form>` / `<FormProvider>` context → `"edit"`. In `"view"` mode the
|
|
2070
|
+
* editor renders its value read-only through `<ContentRenderer>` (matching
|
|
2071
|
+
* `outputFormat` — markdown/html) instead of the editable toolbar + content.
|
|
2072
|
+
* @default "edit"
|
|
2073
|
+
*/
|
|
2074
|
+
mode?: FieldMode;
|
|
2067
2075
|
/** Per-instance render extensions. Merged with any globally-registered extensions. */
|
|
2068
2076
|
extensions?: RenderExtension[];
|
|
2069
2077
|
/**
|
|
@@ -2091,7 +2099,7 @@ declare namespace EditorContent {
|
|
|
2091
2099
|
var displayName: string;
|
|
2092
2100
|
}
|
|
2093
2101
|
|
|
2094
|
-
declare function EditorRoot({ children, className, value: controlledValue, defaultValue, onChange, outputFormat, lineSpacing, placeholder, extensions: instanceExtensions, unsafe, }: EditorProps): react.JSX.Element;
|
|
2102
|
+
declare function EditorRoot({ children, className, value: controlledValue, defaultValue, onChange, outputFormat, lineSpacing, placeholder, mode: modeProp, extensions: instanceExtensions, unsafe, }: EditorProps): react.JSX.Element;
|
|
2095
2103
|
declare const Editor: typeof EditorRoot & {
|
|
2096
2104
|
Toolbar: typeof EditorToolbar & {
|
|
2097
2105
|
Separator: typeof EditorToolbarSeparator;
|
package/dist/index.d.ts
CHANGED
|
@@ -2064,6 +2064,14 @@ interface EditorProps {
|
|
|
2064
2064
|
outputFormat?: "html" | "markdown";
|
|
2065
2065
|
lineSpacing?: number;
|
|
2066
2066
|
placeholder?: string;
|
|
2067
|
+
/**
|
|
2068
|
+
* View/edit mode — like the react-fancy inputs. Resolution: this prop →
|
|
2069
|
+
* nearest `<Form>` / `<FormProvider>` context → `"edit"`. In `"view"` mode the
|
|
2070
|
+
* editor renders its value read-only through `<ContentRenderer>` (matching
|
|
2071
|
+
* `outputFormat` — markdown/html) instead of the editable toolbar + content.
|
|
2072
|
+
* @default "edit"
|
|
2073
|
+
*/
|
|
2074
|
+
mode?: FieldMode;
|
|
2067
2075
|
/** Per-instance render extensions. Merged with any globally-registered extensions. */
|
|
2068
2076
|
extensions?: RenderExtension[];
|
|
2069
2077
|
/**
|
|
@@ -2091,7 +2099,7 @@ declare namespace EditorContent {
|
|
|
2091
2099
|
var displayName: string;
|
|
2092
2100
|
}
|
|
2093
2101
|
|
|
2094
|
-
declare function EditorRoot({ children, className, value: controlledValue, defaultValue, onChange, outputFormat, lineSpacing, placeholder, extensions: instanceExtensions, unsafe, }: EditorProps): react.JSX.Element;
|
|
2102
|
+
declare function EditorRoot({ children, className, value: controlledValue, defaultValue, onChange, outputFormat, lineSpacing, placeholder, mode: modeProp, extensions: instanceExtensions, unsafe, }: EditorProps): react.JSX.Element;
|
|
2095
2103
|
declare const Editor: typeof EditorRoot & {
|
|
2096
2104
|
Toolbar: typeof EditorToolbar & {
|
|
2097
2105
|
Separator: typeof EditorToolbarSeparator;
|
package/dist/index.js
CHANGED
|
@@ -2139,6 +2139,7 @@ function getIconResolutionVersion() {
|
|
|
2139
2139
|
return lucideVersion;
|
|
2140
2140
|
}
|
|
2141
2141
|
function resolveFromLucide(name) {
|
|
2142
|
+
if (typeof window === "undefined") return null;
|
|
2142
2143
|
if (!lucideModule) {
|
|
2143
2144
|
ensureLucideLoaded();
|
|
2144
2145
|
return null;
|
|
@@ -11588,6 +11589,71 @@ function mergeExtensions(instanceExtensions) {
|
|
|
11588
11589
|
}
|
|
11589
11590
|
return merged;
|
|
11590
11591
|
}
|
|
11592
|
+
function RenderedContent({
|
|
11593
|
+
html,
|
|
11594
|
+
extensions: instanceExtensions,
|
|
11595
|
+
unsafe = false
|
|
11596
|
+
}) {
|
|
11597
|
+
const extensions = useMemo(
|
|
11598
|
+
() => mergeExtensions(instanceExtensions),
|
|
11599
|
+
[instanceExtensions]
|
|
11600
|
+
);
|
|
11601
|
+
const segments = useMemo(
|
|
11602
|
+
() => parseSegments(html, extensions),
|
|
11603
|
+
[html, extensions]
|
|
11604
|
+
);
|
|
11605
|
+
const renderHtml = (content) => unsafe ? content : sanitizeHtml(content);
|
|
11606
|
+
if (segments.length === 1 && segments[0].type === "html") {
|
|
11607
|
+
return /* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: renderHtml(segments[0].content) } });
|
|
11608
|
+
}
|
|
11609
|
+
if (segments.length === 0) {
|
|
11610
|
+
return null;
|
|
11611
|
+
}
|
|
11612
|
+
return /* @__PURE__ */ jsx(Fragment, { children: segments.map((segment, i) => {
|
|
11613
|
+
if (segment.type === "html") {
|
|
11614
|
+
return segment.content ? /* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: renderHtml(segment.content) } }, i) : null;
|
|
11615
|
+
}
|
|
11616
|
+
const ext = extensions.find(
|
|
11617
|
+
(e) => e.tag.toLowerCase() === segment.tag
|
|
11618
|
+
);
|
|
11619
|
+
if (!ext) return null;
|
|
11620
|
+
const Component = ext.component;
|
|
11621
|
+
const isBlock = ext.block !== false;
|
|
11622
|
+
const Wrapper = isBlock ? "div" : "span";
|
|
11623
|
+
return /* @__PURE__ */ jsx(Wrapper, { "data-render-extension": segment.tag, children: /* @__PURE__ */ jsx(Component, { content: segment.content, attributes: segment.attributes }) }, i);
|
|
11624
|
+
}) });
|
|
11625
|
+
}
|
|
11626
|
+
RenderedContent.displayName = "RenderedContent";
|
|
11627
|
+
function ContentRenderer({
|
|
11628
|
+
value,
|
|
11629
|
+
format = "auto",
|
|
11630
|
+
lineSpacing = 1.6,
|
|
11631
|
+
className,
|
|
11632
|
+
extensions: instanceExtensions,
|
|
11633
|
+
unsafe = false
|
|
11634
|
+
}) {
|
|
11635
|
+
const extensions = useMemo(
|
|
11636
|
+
() => mergeExtensions(instanceExtensions),
|
|
11637
|
+
[instanceExtensions]
|
|
11638
|
+
);
|
|
11639
|
+
const html = useMemo(() => {
|
|
11640
|
+
const safe = value ?? "";
|
|
11641
|
+
const resolvedFormat = format === "auto" ? detectFormat(safe) : format;
|
|
11642
|
+
const raw = resolvedFormat === "markdown" ? marked.parse(safe, { async: false }) : safe;
|
|
11643
|
+
return unsafe ? raw : sanitizeHtml(raw);
|
|
11644
|
+
}, [value, format, unsafe]);
|
|
11645
|
+
const hasExtensions = extensions.length > 0;
|
|
11646
|
+
return /* @__PURE__ */ jsx(
|
|
11647
|
+
"div",
|
|
11648
|
+
{
|
|
11649
|
+
"data-react-fancy-content-renderer": "",
|
|
11650
|
+
style: { lineHeight: lineSpacing },
|
|
11651
|
+
className: cn("text-sm", proseClasses, className),
|
|
11652
|
+
children: hasExtensions ? /* @__PURE__ */ jsx(RenderedContent, { html, extensions, unsafe }) : /* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: html } })
|
|
11653
|
+
}
|
|
11654
|
+
);
|
|
11655
|
+
}
|
|
11656
|
+
ContentRenderer.displayName = "ContentRenderer";
|
|
11591
11657
|
function toHtml(value, outputFormat, unsafe) {
|
|
11592
11658
|
if (!value) return "";
|
|
11593
11659
|
const raw = (() => {
|
|
@@ -11607,16 +11673,22 @@ function EditorRoot({
|
|
|
11607
11673
|
outputFormat = "html",
|
|
11608
11674
|
lineSpacing = 1.6,
|
|
11609
11675
|
placeholder,
|
|
11676
|
+
mode: modeProp,
|
|
11610
11677
|
extensions: instanceExtensions,
|
|
11611
11678
|
unsafe = false
|
|
11612
11679
|
}) {
|
|
11613
11680
|
const contentRef = useRef(null);
|
|
11614
|
-
const [, setValue] = useControllableState(controlledValue, defaultValue, onChange);
|
|
11681
|
+
const [value, setValue] = useControllableState(controlledValue, defaultValue, onChange);
|
|
11682
|
+
const mode = useFieldMode(modeProp);
|
|
11683
|
+
const isView = mode === "view";
|
|
11615
11684
|
const initialHtml = useMemo(
|
|
11616
|
-
() => toHtml(
|
|
11617
|
-
//
|
|
11685
|
+
() => toHtml(value, outputFormat, unsafe),
|
|
11686
|
+
// Seed the contentEditable from the LIVE value when (re)entering edit mode.
|
|
11687
|
+
// EditorContent reads this once on mount, and it only mounts in edit mode —
|
|
11688
|
+
// so this captures the current value on view→edit, not a stale mount snapshot,
|
|
11689
|
+
// and does NOT re-run on every keystroke.
|
|
11618
11690
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
11619
|
-
[]
|
|
11691
|
+
[isView, outputFormat, unsafe]
|
|
11620
11692
|
);
|
|
11621
11693
|
const extensions = useMemo(
|
|
11622
11694
|
() => mergeExtensions(instanceExtensions),
|
|
@@ -11679,10 +11751,35 @@ function EditorRoot({
|
|
|
11679
11751
|
_initialHtml: initialHtml,
|
|
11680
11752
|
_onInput: handleInput
|
|
11681
11753
|
};
|
|
11754
|
+
if (isView) {
|
|
11755
|
+
return /* @__PURE__ */ jsx(
|
|
11756
|
+
"div",
|
|
11757
|
+
{
|
|
11758
|
+
"data-react-fancy-editor": "",
|
|
11759
|
+
"data-mode": "view",
|
|
11760
|
+
className: cn(
|
|
11761
|
+
"overflow-hidden rounded-xl border border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900",
|
|
11762
|
+
className
|
|
11763
|
+
),
|
|
11764
|
+
children: /* @__PURE__ */ jsx(
|
|
11765
|
+
ContentRenderer,
|
|
11766
|
+
{
|
|
11767
|
+
value,
|
|
11768
|
+
format: outputFormat,
|
|
11769
|
+
lineSpacing,
|
|
11770
|
+
extensions: instanceExtensions,
|
|
11771
|
+
unsafe,
|
|
11772
|
+
className: "px-4 py-3"
|
|
11773
|
+
}
|
|
11774
|
+
)
|
|
11775
|
+
}
|
|
11776
|
+
);
|
|
11777
|
+
}
|
|
11682
11778
|
return /* @__PURE__ */ jsx(EditorContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(
|
|
11683
11779
|
"div",
|
|
11684
11780
|
{
|
|
11685
11781
|
"data-react-fancy-editor": "",
|
|
11782
|
+
"data-mode": "edit",
|
|
11686
11783
|
className: cn(
|
|
11687
11784
|
"overflow-hidden rounded-xl border border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900",
|
|
11688
11785
|
className
|
|
@@ -11698,71 +11795,6 @@ var Editor = Object.assign(EditorRoot, {
|
|
|
11698
11795
|
Toolbar: ToolbarWithSeparator,
|
|
11699
11796
|
Content: EditorContent
|
|
11700
11797
|
});
|
|
11701
|
-
function RenderedContent({
|
|
11702
|
-
html,
|
|
11703
|
-
extensions: instanceExtensions,
|
|
11704
|
-
unsafe = false
|
|
11705
|
-
}) {
|
|
11706
|
-
const extensions = useMemo(
|
|
11707
|
-
() => mergeExtensions(instanceExtensions),
|
|
11708
|
-
[instanceExtensions]
|
|
11709
|
-
);
|
|
11710
|
-
const segments = useMemo(
|
|
11711
|
-
() => parseSegments(html, extensions),
|
|
11712
|
-
[html, extensions]
|
|
11713
|
-
);
|
|
11714
|
-
const renderHtml = (content) => unsafe ? content : sanitizeHtml(content);
|
|
11715
|
-
if (segments.length === 1 && segments[0].type === "html") {
|
|
11716
|
-
return /* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: renderHtml(segments[0].content) } });
|
|
11717
|
-
}
|
|
11718
|
-
if (segments.length === 0) {
|
|
11719
|
-
return null;
|
|
11720
|
-
}
|
|
11721
|
-
return /* @__PURE__ */ jsx(Fragment, { children: segments.map((segment, i) => {
|
|
11722
|
-
if (segment.type === "html") {
|
|
11723
|
-
return segment.content ? /* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: renderHtml(segment.content) } }, i) : null;
|
|
11724
|
-
}
|
|
11725
|
-
const ext = extensions.find(
|
|
11726
|
-
(e) => e.tag.toLowerCase() === segment.tag
|
|
11727
|
-
);
|
|
11728
|
-
if (!ext) return null;
|
|
11729
|
-
const Component = ext.component;
|
|
11730
|
-
const isBlock = ext.block !== false;
|
|
11731
|
-
const Wrapper = isBlock ? "div" : "span";
|
|
11732
|
-
return /* @__PURE__ */ jsx(Wrapper, { "data-render-extension": segment.tag, children: /* @__PURE__ */ jsx(Component, { content: segment.content, attributes: segment.attributes }) }, i);
|
|
11733
|
-
}) });
|
|
11734
|
-
}
|
|
11735
|
-
RenderedContent.displayName = "RenderedContent";
|
|
11736
|
-
function ContentRenderer({
|
|
11737
|
-
value,
|
|
11738
|
-
format = "auto",
|
|
11739
|
-
lineSpacing = 1.6,
|
|
11740
|
-
className,
|
|
11741
|
-
extensions: instanceExtensions,
|
|
11742
|
-
unsafe = false
|
|
11743
|
-
}) {
|
|
11744
|
-
const extensions = useMemo(
|
|
11745
|
-
() => mergeExtensions(instanceExtensions),
|
|
11746
|
-
[instanceExtensions]
|
|
11747
|
-
);
|
|
11748
|
-
const html = useMemo(() => {
|
|
11749
|
-
const safe = value ?? "";
|
|
11750
|
-
const resolvedFormat = format === "auto" ? detectFormat(safe) : format;
|
|
11751
|
-
const raw = resolvedFormat === "markdown" ? marked.parse(safe, { async: false }) : safe;
|
|
11752
|
-
return unsafe ? raw : sanitizeHtml(raw);
|
|
11753
|
-
}, [value, format, unsafe]);
|
|
11754
|
-
const hasExtensions = extensions.length > 0;
|
|
11755
|
-
return /* @__PURE__ */ jsx(
|
|
11756
|
-
"div",
|
|
11757
|
-
{
|
|
11758
|
-
"data-react-fancy-content-renderer": "",
|
|
11759
|
-
style: { lineHeight: lineSpacing },
|
|
11760
|
-
className: cn("text-sm", proseClasses, className),
|
|
11761
|
-
children: hasExtensions ? /* @__PURE__ */ jsx(RenderedContent, { html, extensions, unsafe }) : /* @__PURE__ */ jsx("div", { dangerouslySetInnerHTML: { __html: html } })
|
|
11762
|
-
}
|
|
11763
|
-
);
|
|
11764
|
-
}
|
|
11765
|
-
ContentRenderer.displayName = "ContentRenderer";
|
|
11766
11798
|
var MenuContext = createContext(null);
|
|
11767
11799
|
function useMenu() {
|
|
11768
11800
|
const ctx = useContext(MenuContext);
|