@particle-academy/react-fancy 4.13.0 → 4.14.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 +1 -1
- package/dist/index.cjs +129 -38
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +67 -10
- package/dist/index.d.ts +67 -10
- package/dist/index.js +129 -38
- package/dist/index.js.map +1 -1
- package/docs/Editor.md +55 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -162,7 +162,7 @@ npx vite build # Build demo app (verifies imports work)
|
|
|
162
162
|
|-----------|-------------|------|
|
|
163
163
|
| Composer | Chat-style message input composing textarea + actions | [docs](docs/Composer.md) |
|
|
164
164
|
| Chart | SVG-based Bar, Line, Area, Pie, Donut, and Sparkline charts | [docs](docs/Chart.md) |
|
|
165
|
-
| Editor | Toolbar chrome wrapper for contentEditable | [docs](docs/Editor.md) |
|
|
165
|
+
| Editor | Toolbar chrome wrapper for contentEditable, with a Source toggle to edit raw HTML/Markdown | [docs](docs/Editor.md) |
|
|
166
166
|
| Kanban | Drag-and-drop board with columns and cards | [docs](docs/Kanban.md) |
|
|
167
167
|
| Canvas | Interactive node canvas with pan, zoom, and connections | [docs](docs/Canvas.md) |
|
|
168
168
|
| Diagram | Entity-relationship diagram with draggable nodes and relation lines | [docs](docs/Diagram.md) |
|
package/dist/index.cjs
CHANGED
|
@@ -7884,11 +7884,12 @@ function usePopover() {
|
|
|
7884
7884
|
}
|
|
7885
7885
|
return ctx;
|
|
7886
7886
|
}
|
|
7887
|
-
function PopoverTrigger({ children, className }) {
|
|
7887
|
+
function PopoverTrigger({ children, className, ...rest }) {
|
|
7888
7888
|
const { setOpen, open, anchorRef, hover, onHoverEnter, onHoverLeave } = usePopover();
|
|
7889
7889
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
7890
7890
|
"span",
|
|
7891
7891
|
{
|
|
7892
|
+
...rest,
|
|
7892
7893
|
ref: anchorRef,
|
|
7893
7894
|
"data-react-fancy-popover-trigger": "",
|
|
7894
7895
|
className: cn("inline-flex", className),
|
|
@@ -7902,7 +7903,7 @@ function PopoverTrigger({ children, className }) {
|
|
|
7902
7903
|
);
|
|
7903
7904
|
}
|
|
7904
7905
|
PopoverTrigger.displayName = "PopoverTrigger";
|
|
7905
|
-
function PopoverContent({ children, className }) {
|
|
7906
|
+
function PopoverContent({ children, className, style, ...rest }) {
|
|
7906
7907
|
const { open, setOpen, anchorRef, floatingRef, placement, offset, hover, onHoverEnter, onHoverLeave } = usePopover();
|
|
7907
7908
|
const outsideRef = react.useRef(null);
|
|
7908
7909
|
const position = useFloatingPosition(anchorRef, floatingRef, {
|
|
@@ -7918,6 +7919,7 @@ function PopoverContent({ children, className }) {
|
|
|
7918
7919
|
return /* @__PURE__ */ jsxRuntime.jsx(Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
7919
7920
|
"div",
|
|
7920
7921
|
{
|
|
7922
|
+
...rest,
|
|
7921
7923
|
ref: (node) => {
|
|
7922
7924
|
outsideRef.current = node;
|
|
7923
7925
|
floatingRef.current = node;
|
|
@@ -7928,7 +7930,7 @@ function PopoverContent({ children, className }) {
|
|
|
7928
7930
|
positioned ? "fancy-scale-in" : "invisible",
|
|
7929
7931
|
className
|
|
7930
7932
|
),
|
|
7931
|
-
style: { left: position.x, top: position.y },
|
|
7933
|
+
style: { ...style, left: position.x, top: position.y },
|
|
7932
7934
|
onMouseEnter: hover ? onHoverEnter : void 0,
|
|
7933
7935
|
onMouseLeave: hover ? onHoverLeave : void 0,
|
|
7934
7936
|
children
|
|
@@ -11342,6 +11344,31 @@ function useEditor() {
|
|
|
11342
11344
|
}
|
|
11343
11345
|
return ctx;
|
|
11344
11346
|
}
|
|
11347
|
+
function EditorSourceToggle({
|
|
11348
|
+
className,
|
|
11349
|
+
icon = "</>",
|
|
11350
|
+
title = "Source",
|
|
11351
|
+
activeTitle = "Rich text"
|
|
11352
|
+
}) {
|
|
11353
|
+
const { showSource, setShowSource } = useEditor();
|
|
11354
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
11355
|
+
"button",
|
|
11356
|
+
{
|
|
11357
|
+
type: "button",
|
|
11358
|
+
onClick: () => setShowSource(!showSource),
|
|
11359
|
+
title: showSource ? activeTitle : title,
|
|
11360
|
+
"aria-pressed": showSource,
|
|
11361
|
+
"data-react-fancy-editor-source-toggle": "",
|
|
11362
|
+
className: cn(
|
|
11363
|
+
"inline-flex h-8 items-center justify-center rounded-md px-2 font-mono text-xs transition-colors",
|
|
11364
|
+
showSource ? "bg-zinc-200 dark:bg-zinc-700" : "hover:bg-zinc-100 dark:hover:bg-zinc-800",
|
|
11365
|
+
className
|
|
11366
|
+
),
|
|
11367
|
+
children: icon
|
|
11368
|
+
}
|
|
11369
|
+
);
|
|
11370
|
+
}
|
|
11371
|
+
EditorSourceToggle.displayName = "EditorSourceToggle";
|
|
11345
11372
|
var DEFAULT_ACTIONS = [
|
|
11346
11373
|
{ icon: "B", label: "Bold", command: "bold" },
|
|
11347
11374
|
{ icon: "I", label: "Italic", command: "italic" },
|
|
@@ -11352,9 +11379,10 @@ function EditorToolbar({
|
|
|
11352
11379
|
actions = DEFAULT_ACTIONS,
|
|
11353
11380
|
onAction,
|
|
11354
11381
|
children,
|
|
11355
|
-
className
|
|
11382
|
+
className,
|
|
11383
|
+
showSourceToggle = true
|
|
11356
11384
|
}) {
|
|
11357
|
-
const { exec } = useEditor();
|
|
11385
|
+
const { exec, showSource } = useEditor();
|
|
11358
11386
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
11359
11387
|
"div",
|
|
11360
11388
|
{
|
|
@@ -11363,26 +11391,31 @@ function EditorToolbar({
|
|
|
11363
11391
|
"flex items-center gap-0.5 border-b border-zinc-200 px-2 py-1.5 dark:border-zinc-700",
|
|
11364
11392
|
className
|
|
11365
11393
|
),
|
|
11366
|
-
children: children ??
|
|
11367
|
-
|
|
11368
|
-
|
|
11369
|
-
|
|
11370
|
-
|
|
11371
|
-
|
|
11372
|
-
|
|
11373
|
-
|
|
11374
|
-
|
|
11375
|
-
|
|
11394
|
+
children: children ?? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
11395
|
+
actions.map((action) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
11396
|
+
"button",
|
|
11397
|
+
{
|
|
11398
|
+
type: "button",
|
|
11399
|
+
disabled: showSource,
|
|
11400
|
+
onClick: () => {
|
|
11401
|
+
if (onAction) {
|
|
11402
|
+
onAction(action.command);
|
|
11403
|
+
} else {
|
|
11404
|
+
exec(action.command, action.commandArg);
|
|
11405
|
+
}
|
|
11406
|
+
},
|
|
11407
|
+
title: action.label,
|
|
11408
|
+
className: cn(
|
|
11409
|
+
"inline-flex h-8 w-8 items-center justify-center rounded-md text-sm transition-colors",
|
|
11410
|
+
action.active ? "bg-zinc-200 dark:bg-zinc-700" : "hover:bg-zinc-100 dark:hover:bg-zinc-800",
|
|
11411
|
+
showSource && "cursor-not-allowed opacity-40 hover:bg-transparent"
|
|
11412
|
+
),
|
|
11413
|
+
children: action.icon
|
|
11376
11414
|
},
|
|
11377
|
-
|
|
11378
|
-
|
|
11379
|
-
|
|
11380
|
-
|
|
11381
|
-
),
|
|
11382
|
-
children: action.icon
|
|
11383
|
-
},
|
|
11384
|
-
`${action.command}-${action.commandArg ?? ""}`
|
|
11385
|
-
))
|
|
11415
|
+
`${action.command}-${action.commandArg ?? ""}`
|
|
11416
|
+
)),
|
|
11417
|
+
showSourceToggle && /* @__PURE__ */ jsxRuntime.jsx(EditorSourceToggle, { className: "ml-auto" })
|
|
11418
|
+
] })
|
|
11386
11419
|
}
|
|
11387
11420
|
);
|
|
11388
11421
|
}
|
|
@@ -11588,20 +11621,58 @@ function extensionEditorClasses(extensions) {
|
|
|
11588
11621
|
].join(" ");
|
|
11589
11622
|
}).join(" ");
|
|
11590
11623
|
}
|
|
11591
|
-
function EditorContent({ className, maxHeight }) {
|
|
11592
|
-
const {
|
|
11593
|
-
|
|
11624
|
+
function EditorContent({ className, maxHeight, sourceClassName }) {
|
|
11625
|
+
const {
|
|
11626
|
+
contentRef,
|
|
11627
|
+
lineSpacing,
|
|
11628
|
+
placeholder,
|
|
11629
|
+
extensions,
|
|
11630
|
+
outputFormat,
|
|
11631
|
+
showSource,
|
|
11632
|
+
_initialHtml,
|
|
11633
|
+
_onInput,
|
|
11634
|
+
_sourceValue,
|
|
11635
|
+
_onSourceInput
|
|
11636
|
+
} = useEditor();
|
|
11637
|
+
const seededHtml = react.useRef(null);
|
|
11594
11638
|
react.useEffect(() => {
|
|
11639
|
+
if (showSource) {
|
|
11640
|
+
seededHtml.current = null;
|
|
11641
|
+
return;
|
|
11642
|
+
}
|
|
11595
11643
|
const el = contentRef.current;
|
|
11596
|
-
if (el &&
|
|
11644
|
+
if (el && seededHtml.current !== _initialHtml) {
|
|
11597
11645
|
el.innerHTML = _initialHtml;
|
|
11598
|
-
|
|
11646
|
+
seededHtml.current = _initialHtml;
|
|
11599
11647
|
}
|
|
11600
|
-
}, [contentRef, _initialHtml]);
|
|
11648
|
+
}, [contentRef, _initialHtml, showSource]);
|
|
11601
11649
|
const extClasses = react.useMemo(
|
|
11602
11650
|
() => extensionEditorClasses(extensions),
|
|
11603
11651
|
[extensions]
|
|
11604
11652
|
);
|
|
11653
|
+
if (showSource) {
|
|
11654
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
11655
|
+
"textarea",
|
|
11656
|
+
{
|
|
11657
|
+
"data-react-fancy-editor-source": "",
|
|
11658
|
+
value: _sourceValue,
|
|
11659
|
+
onChange: (e) => _onSourceInput(e.target.value),
|
|
11660
|
+
spellCheck: false,
|
|
11661
|
+
placeholder,
|
|
11662
|
+
"aria-label": `${outputFormat === "markdown" ? "Markdown" : "HTML"} source`,
|
|
11663
|
+
style: {
|
|
11664
|
+
maxHeight: maxHeight ? `${maxHeight}px` : void 0
|
|
11665
|
+
},
|
|
11666
|
+
className: cn(
|
|
11667
|
+
"block min-h-[120px] w-full resize-y px-4 py-3 font-mono text-xs leading-relaxed outline-none",
|
|
11668
|
+
"bg-zinc-50 text-zinc-800 focus:outline-none dark:bg-zinc-900 dark:text-zinc-200",
|
|
11669
|
+
"placeholder:text-zinc-400",
|
|
11670
|
+
maxHeight && "overflow-y-auto",
|
|
11671
|
+
sourceClassName
|
|
11672
|
+
)
|
|
11673
|
+
}
|
|
11674
|
+
);
|
|
11675
|
+
}
|
|
11605
11676
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
11606
11677
|
"div",
|
|
11607
11678
|
{
|
|
@@ -11793,20 +11864,29 @@ function EditorRoot({
|
|
|
11793
11864
|
placeholder,
|
|
11794
11865
|
mode: modeProp,
|
|
11795
11866
|
extensions: instanceExtensions,
|
|
11796
|
-
unsafe = false
|
|
11867
|
+
unsafe = false,
|
|
11868
|
+
showSource: showSourceProp,
|
|
11869
|
+
defaultShowSource = false,
|
|
11870
|
+
onShowSourceChange
|
|
11797
11871
|
}) {
|
|
11798
11872
|
const contentRef = react.useRef(null);
|
|
11799
11873
|
const [value, setValue] = useControllableState(controlledValue, defaultValue, onChange);
|
|
11874
|
+
const [showSource, setShowSource] = useControllableState(
|
|
11875
|
+
showSourceProp,
|
|
11876
|
+
defaultShowSource,
|
|
11877
|
+
onShowSourceChange
|
|
11878
|
+
);
|
|
11800
11879
|
const mode = useFieldMode(modeProp);
|
|
11801
11880
|
const isView = mode === "view";
|
|
11802
11881
|
const initialHtml = react.useMemo(
|
|
11803
11882
|
() => toHtml(value, outputFormat, unsafe, valueFormat),
|
|
11804
|
-
// Seed the contentEditable from the LIVE value when (re)entering edit mode
|
|
11805
|
-
//
|
|
11806
|
-
//
|
|
11807
|
-
//
|
|
11883
|
+
// Seed the contentEditable from the LIVE value when (re)entering edit mode —
|
|
11884
|
+
// and when returning from the raw-source view, which may have rewritten the
|
|
11885
|
+
// value out from under the rich-text surface. EditorContent re-seeds when
|
|
11886
|
+
// this recomputes; it does NOT re-run on every keystroke (`value` is not a
|
|
11887
|
+
// dep, so typing in the rich-text surface never re-seeds it).
|
|
11808
11888
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
11809
|
-
[isView, outputFormat, unsafe, valueFormat]
|
|
11889
|
+
[isView, outputFormat, unsafe, valueFormat, showSource]
|
|
11810
11890
|
);
|
|
11811
11891
|
const extensions = react.useMemo(
|
|
11812
11892
|
() => mergeExtensions(instanceExtensions),
|
|
@@ -11843,6 +11923,12 @@ function EditorRoot({
|
|
|
11843
11923
|
},
|
|
11844
11924
|
[handleInput]
|
|
11845
11925
|
);
|
|
11926
|
+
const handleSourceInput = react.useCallback(
|
|
11927
|
+
(next) => {
|
|
11928
|
+
setValue(next);
|
|
11929
|
+
},
|
|
11930
|
+
[setValue]
|
|
11931
|
+
);
|
|
11846
11932
|
const wrapSelection = react.useCallback(
|
|
11847
11933
|
(before, after) => {
|
|
11848
11934
|
const sel = window.getSelection();
|
|
@@ -11866,8 +11952,12 @@ function EditorRoot({
|
|
|
11866
11952
|
lineSpacing,
|
|
11867
11953
|
placeholder,
|
|
11868
11954
|
extensions,
|
|
11955
|
+
showSource,
|
|
11956
|
+
setShowSource,
|
|
11869
11957
|
_initialHtml: initialHtml,
|
|
11870
|
-
_onInput: handleInput
|
|
11958
|
+
_onInput: handleInput,
|
|
11959
|
+
_sourceValue: value,
|
|
11960
|
+
_onSourceInput: handleSourceInput
|
|
11871
11961
|
};
|
|
11872
11962
|
if (isView) {
|
|
11873
11963
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -11911,7 +12001,8 @@ var ToolbarWithSeparator = Object.assign(EditorToolbar, {
|
|
|
11911
12001
|
});
|
|
11912
12002
|
var Editor = Object.assign(EditorRoot, {
|
|
11913
12003
|
Toolbar: ToolbarWithSeparator,
|
|
11914
|
-
Content: EditorContent
|
|
12004
|
+
Content: EditorContent,
|
|
12005
|
+
SourceToggle: EditorSourceToggle
|
|
11915
12006
|
});
|
|
11916
12007
|
var MenuContext = react.createContext(null);
|
|
11917
12008
|
function useMenu() {
|