@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 CHANGED
@@ -2141,6 +2141,7 @@ function getIconResolutionVersion() {
2141
2141
  return lucideVersion;
2142
2142
  }
2143
2143
  function resolveFromLucide(name) {
2144
+ if (typeof window === "undefined") return null;
2144
2145
  if (!lucideModule) {
2145
2146
  ensureLucideLoaded();
2146
2147
  return null;
@@ -11590,6 +11591,71 @@ function mergeExtensions(instanceExtensions) {
11590
11591
  }
11591
11592
  return merged;
11592
11593
  }
11594
+ function RenderedContent({
11595
+ html,
11596
+ extensions: instanceExtensions,
11597
+ unsafe = false
11598
+ }) {
11599
+ const extensions = react.useMemo(
11600
+ () => mergeExtensions(instanceExtensions),
11601
+ [instanceExtensions]
11602
+ );
11603
+ const segments = react.useMemo(
11604
+ () => parseSegments(html, extensions),
11605
+ [html, extensions]
11606
+ );
11607
+ const renderHtml = (content) => unsafe ? content : sanitizeHtml(content);
11608
+ if (segments.length === 1 && segments[0].type === "html") {
11609
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { dangerouslySetInnerHTML: { __html: renderHtml(segments[0].content) } });
11610
+ }
11611
+ if (segments.length === 0) {
11612
+ return null;
11613
+ }
11614
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: segments.map((segment, i) => {
11615
+ if (segment.type === "html") {
11616
+ return segment.content ? /* @__PURE__ */ jsxRuntime.jsx("div", { dangerouslySetInnerHTML: { __html: renderHtml(segment.content) } }, i) : null;
11617
+ }
11618
+ const ext = extensions.find(
11619
+ (e) => e.tag.toLowerCase() === segment.tag
11620
+ );
11621
+ if (!ext) return null;
11622
+ const Component = ext.component;
11623
+ const isBlock = ext.block !== false;
11624
+ const Wrapper = isBlock ? "div" : "span";
11625
+ return /* @__PURE__ */ jsxRuntime.jsx(Wrapper, { "data-render-extension": segment.tag, children: /* @__PURE__ */ jsxRuntime.jsx(Component, { content: segment.content, attributes: segment.attributes }) }, i);
11626
+ }) });
11627
+ }
11628
+ RenderedContent.displayName = "RenderedContent";
11629
+ function ContentRenderer({
11630
+ value,
11631
+ format = "auto",
11632
+ lineSpacing = 1.6,
11633
+ className,
11634
+ extensions: instanceExtensions,
11635
+ unsafe = false
11636
+ }) {
11637
+ const extensions = react.useMemo(
11638
+ () => mergeExtensions(instanceExtensions),
11639
+ [instanceExtensions]
11640
+ );
11641
+ const html = react.useMemo(() => {
11642
+ const safe = value ?? "";
11643
+ const resolvedFormat = format === "auto" ? detectFormat(safe) : format;
11644
+ const raw = resolvedFormat === "markdown" ? marked.marked.parse(safe, { async: false }) : safe;
11645
+ return unsafe ? raw : sanitizeHtml(raw);
11646
+ }, [value, format, unsafe]);
11647
+ const hasExtensions = extensions.length > 0;
11648
+ return /* @__PURE__ */ jsxRuntime.jsx(
11649
+ "div",
11650
+ {
11651
+ "data-react-fancy-content-renderer": "",
11652
+ style: { lineHeight: lineSpacing },
11653
+ className: cn("text-sm", proseClasses, className),
11654
+ children: hasExtensions ? /* @__PURE__ */ jsxRuntime.jsx(RenderedContent, { html, extensions, unsafe }) : /* @__PURE__ */ jsxRuntime.jsx("div", { dangerouslySetInnerHTML: { __html: html } })
11655
+ }
11656
+ );
11657
+ }
11658
+ ContentRenderer.displayName = "ContentRenderer";
11593
11659
  function toHtml(value, outputFormat, unsafe) {
11594
11660
  if (!value) return "";
11595
11661
  const raw = (() => {
@@ -11609,16 +11675,22 @@ function EditorRoot({
11609
11675
  outputFormat = "html",
11610
11676
  lineSpacing = 1.6,
11611
11677
  placeholder,
11678
+ mode: modeProp,
11612
11679
  extensions: instanceExtensions,
11613
11680
  unsafe = false
11614
11681
  }) {
11615
11682
  const contentRef = react.useRef(null);
11616
- const [, setValue] = useControllableState(controlledValue, defaultValue, onChange);
11683
+ const [value, setValue] = useControllableState(controlledValue, defaultValue, onChange);
11684
+ const mode = useFieldMode(modeProp);
11685
+ const isView = mode === "view";
11617
11686
  const initialHtml = react.useMemo(
11618
- () => toHtml(controlledValue ?? defaultValue, outputFormat, unsafe),
11619
- // Only compute once on mount don't re-run when value changes from user input
11687
+ () => toHtml(value, outputFormat, unsafe),
11688
+ // Seed the contentEditable from the LIVE value when (re)entering edit mode.
11689
+ // EditorContent reads this once on mount, and it only mounts in edit mode —
11690
+ // so this captures the current value on view→edit, not a stale mount snapshot,
11691
+ // and does NOT re-run on every keystroke.
11620
11692
  // eslint-disable-next-line react-hooks/exhaustive-deps
11621
- []
11693
+ [isView, outputFormat, unsafe]
11622
11694
  );
11623
11695
  const extensions = react.useMemo(
11624
11696
  () => mergeExtensions(instanceExtensions),
@@ -11681,10 +11753,35 @@ function EditorRoot({
11681
11753
  _initialHtml: initialHtml,
11682
11754
  _onInput: handleInput
11683
11755
  };
11756
+ if (isView) {
11757
+ return /* @__PURE__ */ jsxRuntime.jsx(
11758
+ "div",
11759
+ {
11760
+ "data-react-fancy-editor": "",
11761
+ "data-mode": "view",
11762
+ className: cn(
11763
+ "overflow-hidden rounded-xl border border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900",
11764
+ className
11765
+ ),
11766
+ children: /* @__PURE__ */ jsxRuntime.jsx(
11767
+ ContentRenderer,
11768
+ {
11769
+ value,
11770
+ format: outputFormat,
11771
+ lineSpacing,
11772
+ extensions: instanceExtensions,
11773
+ unsafe,
11774
+ className: "px-4 py-3"
11775
+ }
11776
+ )
11777
+ }
11778
+ );
11779
+ }
11684
11780
  return /* @__PURE__ */ jsxRuntime.jsx(EditorContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsx(
11685
11781
  "div",
11686
11782
  {
11687
11783
  "data-react-fancy-editor": "",
11784
+ "data-mode": "edit",
11688
11785
  className: cn(
11689
11786
  "overflow-hidden rounded-xl border border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900",
11690
11787
  className
@@ -11700,71 +11797,6 @@ var Editor = Object.assign(EditorRoot, {
11700
11797
  Toolbar: ToolbarWithSeparator,
11701
11798
  Content: EditorContent
11702
11799
  });
11703
- function RenderedContent({
11704
- html,
11705
- extensions: instanceExtensions,
11706
- unsafe = false
11707
- }) {
11708
- const extensions = react.useMemo(
11709
- () => mergeExtensions(instanceExtensions),
11710
- [instanceExtensions]
11711
- );
11712
- const segments = react.useMemo(
11713
- () => parseSegments(html, extensions),
11714
- [html, extensions]
11715
- );
11716
- const renderHtml = (content) => unsafe ? content : sanitizeHtml(content);
11717
- if (segments.length === 1 && segments[0].type === "html") {
11718
- return /* @__PURE__ */ jsxRuntime.jsx("div", { dangerouslySetInnerHTML: { __html: renderHtml(segments[0].content) } });
11719
- }
11720
- if (segments.length === 0) {
11721
- return null;
11722
- }
11723
- return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: segments.map((segment, i) => {
11724
- if (segment.type === "html") {
11725
- return segment.content ? /* @__PURE__ */ jsxRuntime.jsx("div", { dangerouslySetInnerHTML: { __html: renderHtml(segment.content) } }, i) : null;
11726
- }
11727
- const ext = extensions.find(
11728
- (e) => e.tag.toLowerCase() === segment.tag
11729
- );
11730
- if (!ext) return null;
11731
- const Component = ext.component;
11732
- const isBlock = ext.block !== false;
11733
- const Wrapper = isBlock ? "div" : "span";
11734
- return /* @__PURE__ */ jsxRuntime.jsx(Wrapper, { "data-render-extension": segment.tag, children: /* @__PURE__ */ jsxRuntime.jsx(Component, { content: segment.content, attributes: segment.attributes }) }, i);
11735
- }) });
11736
- }
11737
- RenderedContent.displayName = "RenderedContent";
11738
- function ContentRenderer({
11739
- value,
11740
- format = "auto",
11741
- lineSpacing = 1.6,
11742
- className,
11743
- extensions: instanceExtensions,
11744
- unsafe = false
11745
- }) {
11746
- const extensions = react.useMemo(
11747
- () => mergeExtensions(instanceExtensions),
11748
- [instanceExtensions]
11749
- );
11750
- const html = react.useMemo(() => {
11751
- const safe = value ?? "";
11752
- const resolvedFormat = format === "auto" ? detectFormat(safe) : format;
11753
- const raw = resolvedFormat === "markdown" ? marked.marked.parse(safe, { async: false }) : safe;
11754
- return unsafe ? raw : sanitizeHtml(raw);
11755
- }, [value, format, unsafe]);
11756
- const hasExtensions = extensions.length > 0;
11757
- return /* @__PURE__ */ jsxRuntime.jsx(
11758
- "div",
11759
- {
11760
- "data-react-fancy-content-renderer": "",
11761
- style: { lineHeight: lineSpacing },
11762
- className: cn("text-sm", proseClasses, className),
11763
- children: hasExtensions ? /* @__PURE__ */ jsxRuntime.jsx(RenderedContent, { html, extensions, unsafe }) : /* @__PURE__ */ jsxRuntime.jsx("div", { dangerouslySetInnerHTML: { __html: html } })
11764
- }
11765
- );
11766
- }
11767
- ContentRenderer.displayName = "ContentRenderer";
11768
11800
  var MenuContext = react.createContext(null);
11769
11801
  function useMenu() {
11770
11802
  const ctx = react.useContext(MenuContext);