@particle-academy/react-fancy 5.59.0 → 5.60.1
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/CHANGELOG.md +74 -0
- package/dist/{chunk-ZASX5GO4.js → chunk-2OVDWUQ7.js} +3 -3
- package/dist/{chunk-ZASX5GO4.js.map → chunk-2OVDWUQ7.js.map} +1 -1
- package/dist/{chunk-5IWKZ2WK.cjs → chunk-34GALZRX.cjs} +212 -2
- package/dist/chunk-34GALZRX.cjs.map +1 -0
- package/dist/{chunk-2GFM5WD4.cjs → chunk-AGO4AA3K.cjs} +57 -9
- package/dist/chunk-AGO4AA3K.cjs.map +1 -0
- package/dist/{chunk-ICI33QMY.cjs → chunk-BX7ZJ3HG.cjs} +9 -9
- package/dist/{chunk-ICI33QMY.cjs.map → chunk-BX7ZJ3HG.cjs.map} +1 -1
- package/dist/chunk-CEFK54WP.cjs +186 -0
- package/dist/chunk-CEFK54WP.cjs.map +1 -0
- package/dist/chunk-GDF6XTHC.js +184 -0
- package/dist/chunk-GDF6XTHC.js.map +1 -0
- package/dist/{chunk-ZAX72LXL.js → chunk-JEHV7PYP.js} +58 -10
- package/dist/chunk-JEHV7PYP.js.map +1 -0
- package/dist/{chunk-6XX6OHAP.cjs → chunk-M77BB2QI.cjs} +3 -3
- package/dist/{chunk-6XX6OHAP.cjs.map → chunk-M77BB2QI.cjs.map} +1 -1
- package/dist/{chunk-JXXUTPDH.js → chunk-PAFTHGQD.js} +212 -2
- package/dist/chunk-PAFTHGQD.js.map +1 -0
- package/dist/{chunk-YBRQTZKV.js → chunk-ZGQEKDG6.js} +3 -3
- package/dist/{chunk-YBRQTZKV.js.map → chunk-ZGQEKDG6.js.map} +1 -1
- package/dist/content-renderer.cjs +4 -4
- package/dist/content-renderer.js +1 -1
- package/dist/editor.cjs +4 -4
- package/dist/editor.js +2 -2
- package/dist/file-upload.cjs +3 -3
- package/dist/file-upload.d.cts +7 -1
- package/dist/file-upload.d.ts +7 -1
- package/dist/file-upload.js +1 -1
- package/dist/index.cjs +50 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +10 -9
- package/dist/index.js.map +1 -1
- package/dist/inputs.cjs +7 -7
- package/dist/inputs.js +3 -3
- package/dist/json-editor.cjs +13 -13
- package/dist/json-editor.js +2 -2
- package/dist/resizable-panels.cjs +13 -0
- package/dist/resizable-panels.cjs.map +1 -0
- package/dist/resizable-panels.d.cts +68 -0
- package/dist/resizable-panels.d.ts +68 -0
- package/dist/resizable-panels.js +4 -0
- package/dist/resizable-panels.js.map +1 -0
- package/docs/FileUpload.md +3 -0
- package/docs/ResizablePanels.md +81 -0
- package/package.json +11 -1
- package/dist/chunk-2GFM5WD4.cjs.map +0 -1
- package/dist/chunk-5IWKZ2WK.cjs.map +0 -1
- package/dist/chunk-JXXUTPDH.js.map +0 -1
- package/dist/chunk-ZAX72LXL.js.map +0 -1
|
@@ -15,13 +15,19 @@ function useFileUpload() {
|
|
|
15
15
|
}
|
|
16
16
|
return ctx;
|
|
17
17
|
}
|
|
18
|
+
var DEFAULT_BROWSE_LABEL = "Browse files";
|
|
19
|
+
var DEFAULT_COPY = "Drop files here or click to browse";
|
|
18
20
|
function FileUploadDropzone({
|
|
19
21
|
children,
|
|
20
|
-
className
|
|
22
|
+
className,
|
|
23
|
+
browseLabel
|
|
21
24
|
}) {
|
|
22
25
|
const { addFiles, disabled, accept, multiple } = useFileUpload();
|
|
23
26
|
const [dragOver, setDragOver] = react.useState(false);
|
|
24
27
|
const inputRef = react.useRef(null);
|
|
28
|
+
const inputId = react.useId();
|
|
29
|
+
const custom = children !== void 0 && children !== null;
|
|
30
|
+
const inputLabel = browseLabel ?? (custom ? DEFAULT_BROWSE_LABEL : DEFAULT_COPY);
|
|
25
31
|
const handleDrop = react.useCallback(
|
|
26
32
|
(e) => {
|
|
27
33
|
e.preventDefault();
|
|
@@ -36,16 +42,31 @@ function FileUploadDropzone({
|
|
|
36
42
|
setDragOver(true);
|
|
37
43
|
}, []);
|
|
38
44
|
const handleDragLeave = react.useCallback(() => setDragOver(false), []);
|
|
45
|
+
const handleClick = react.useCallback(
|
|
46
|
+
(e) => {
|
|
47
|
+
if (disabled) return;
|
|
48
|
+
if (e.target === inputRef.current) return;
|
|
49
|
+
inputRef.current?.click();
|
|
50
|
+
},
|
|
51
|
+
[disabled]
|
|
52
|
+
);
|
|
39
53
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
40
54
|
"div",
|
|
41
55
|
{
|
|
42
56
|
"data-react-fancy-file-upload-dropzone": "",
|
|
57
|
+
"data-drag-over": dragOver ? "" : void 0,
|
|
58
|
+
"data-disabled": disabled ? "" : void 0,
|
|
59
|
+
"aria-disabled": disabled || void 0,
|
|
43
60
|
onDrop: handleDrop,
|
|
44
61
|
onDragOver: handleDragOver,
|
|
45
62
|
onDragLeave: handleDragLeave,
|
|
46
|
-
onClick:
|
|
63
|
+
onClick: handleClick,
|
|
47
64
|
className: chunkZU5Q77OT_cjs.cn(
|
|
48
|
-
"flex cursor-pointer flex-col items-center justify-center rounded-xl border-2 border-dashed p-8 text-center transition-
|
|
65
|
+
"flex cursor-pointer flex-col items-center justify-center rounded-xl border-2 border-dashed p-8 text-center transition-[color,background-color,border-color,box-shadow]",
|
|
66
|
+
// Full opacity on purpose: a translucent 2px ring over a dashed border
|
|
67
|
+
// falls under WCAG 2.2's 3:1 for a focus indicator. The offset gap is
|
|
68
|
+
// painted with the surface token so it matches the page in dark mode.
|
|
69
|
+
"has-focus-visible:ring-2 has-focus-visible:ring-offset-2 has-focus-visible:ring-[color:var(--fancy-focus,var(--color-blue-500))] has-focus-visible:ring-offset-[color:var(--fancy-surface,var(--color-white))] dark:has-focus-visible:ring-[color:var(--fancy-focus-dark,var(--color-blue-400))] dark:has-focus-visible:ring-offset-[color:var(--fancy-surface-dark,var(--color-zinc-900))]",
|
|
49
70
|
dragOver ? "border-blue-400 bg-blue-50 dark:border-blue-500 dark:bg-blue-950" : "border-[color:var(--fancy-border-strong,var(--color-zinc-300))] hover:border-zinc-400 dark:border-[color:var(--fancy-border-strong-dark,var(--color-zinc-700))] dark:hover:border-zinc-500",
|
|
50
71
|
disabled && "cursor-not-allowed opacity-50",
|
|
51
72
|
className
|
|
@@ -55,8 +76,11 @@ function FileUploadDropzone({
|
|
|
55
76
|
"input",
|
|
56
77
|
{
|
|
57
78
|
ref: inputRef,
|
|
79
|
+
id: inputId,
|
|
58
80
|
type: "file",
|
|
59
81
|
className: "hidden",
|
|
82
|
+
tabIndex: -1,
|
|
83
|
+
"aria-label": inputLabel,
|
|
60
84
|
onChange: (e) => {
|
|
61
85
|
if (e.target.files) addFiles(e.target.files);
|
|
62
86
|
e.target.value = "";
|
|
@@ -66,10 +90,34 @@ function FileUploadDropzone({
|
|
|
66
90
|
disabled
|
|
67
91
|
}
|
|
68
92
|
),
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
72
|
-
|
|
93
|
+
custom ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
94
|
+
children,
|
|
95
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
96
|
+
"button",
|
|
97
|
+
{
|
|
98
|
+
type: "button",
|
|
99
|
+
"data-react-fancy-file-upload-browse": "",
|
|
100
|
+
"aria-controls": inputId,
|
|
101
|
+
disabled,
|
|
102
|
+
className: "sr-only",
|
|
103
|
+
children: browseLabel ?? DEFAULT_BROWSE_LABEL
|
|
104
|
+
}
|
|
105
|
+
)
|
|
106
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(
|
|
107
|
+
"button",
|
|
108
|
+
{
|
|
109
|
+
type: "button",
|
|
110
|
+
"data-react-fancy-file-upload-browse": "",
|
|
111
|
+
"aria-controls": inputId,
|
|
112
|
+
"aria-label": browseLabel,
|
|
113
|
+
disabled,
|
|
114
|
+
className: "flex cursor-pointer flex-col items-center rounded-md outline-none disabled:cursor-not-allowed",
|
|
115
|
+
children: [
|
|
116
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Upload, { size: 32, strokeWidth: 1.5, "aria-hidden": "true", className: "mb-2 text-zinc-400" }),
|
|
117
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-zinc-500", children: DEFAULT_COPY })
|
|
118
|
+
]
|
|
119
|
+
}
|
|
120
|
+
)
|
|
73
121
|
]
|
|
74
122
|
}
|
|
75
123
|
);
|
|
@@ -212,5 +260,5 @@ var FileUpload = Object.assign(FileUploadRoot, {
|
|
|
212
260
|
|
|
213
261
|
exports.FileUpload = FileUpload;
|
|
214
262
|
exports.useFileUpload = useFileUpload;
|
|
215
|
-
//# sourceMappingURL=chunk-
|
|
216
|
-
//# sourceMappingURL=chunk-
|
|
263
|
+
//# sourceMappingURL=chunk-AGO4AA3K.cjs.map
|
|
264
|
+
//# sourceMappingURL=chunk-AGO4AA3K.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/FileUpload/FileUpload.context.ts","../src/components/FileUpload/FileUploadDropzone.tsx","../src/components/FileUpload/FileUploadList.tsx","../src/components/FileUpload/matchesAccept.ts","../src/components/FileUpload/FileUpload.tsx"],"names":["createContext","useContext","useState","useRef","useId","useCallback","jsxs","cn","jsx","Fragment","Upload","useMemo","useEffect","File","X"],"mappings":"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","file":"chunk-AGO4AA3K.cjs","sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { FileUploadContextValue } from \"./FileUpload.types\";\n\nexport const FileUploadContext =\n createContext<FileUploadContextValue | null>(null);\n\nexport function useFileUpload(): FileUploadContextValue {\n const ctx = useContext(FileUploadContext);\n if (!ctx) {\n throw new Error(\n \"FileUpload compound components must be used within <FileUpload>\",\n );\n }\n return ctx;\n}\n","import { useCallback, useId, useRef, useState } from \"react\";\nimport { Upload } from \"lucide-react\";\nimport { cn } from \"../../utils/cn\";\nimport { useFileUpload } from \"./FileUpload.context\";\nimport type { FileUploadDropzoneProps } from \"./FileUpload.types\";\n\n/** Accessible name of the browse trigger when the zone has custom children. */\nconst DEFAULT_BROWSE_LABEL = \"Browse files\";\n\n/** The default zone copy; it doubles as the trigger's accessible name. */\nconst DEFAULT_COPY = \"Drop files here or click to browse\";\n\n/*\n * Keyboard access (after 5.59.0). Until then the zone was a clickable div around a\n * display:none input: no tab stop, no role, no key handler, so a keyboard or\n * switch user could not open the picker at all.\n *\n * The trigger is a REAL <button type=\"button\">, not a div with role=\"button\":\n * focus, Enter and Space (Space on keyup, cancelled by moving off) then come\n * from the platform instead of a key handler that has to get those right.\n *\n * - Default content: the button wraps the icon and the copy, so the visible\n * text IS its accessible name and the look is unchanged.\n * - Custom children: they may contain their own interactive content, which a\n * button must not wrap, so the button is visually hidden beside them and the\n * ZONE draws the focus ring (has-focus-visible) — the focus indicator is\n * never invisible.\n *\n * A click anywhere on the zone still opens the picker, as before; a click on\n * the button bubbles to the zone and is handled there once.\n */\nexport function FileUploadDropzone({\n children,\n className,\n browseLabel,\n}: FileUploadDropzoneProps) {\n const { addFiles, disabled, accept, multiple } = useFileUpload();\n const [dragOver, setDragOver] = useState(false);\n const inputRef = useRef<HTMLInputElement>(null);\n const inputId = useId();\n const custom = children !== undefined && children !== null;\n const inputLabel = browseLabel ?? (custom ? DEFAULT_BROWSE_LABEL : DEFAULT_COPY);\n\n const handleDrop = useCallback(\n (e: React.DragEvent) => {\n e.preventDefault();\n setDragOver(false);\n if (disabled) return;\n addFiles(e.dataTransfer.files);\n },\n [addFiles, disabled],\n );\n\n const handleDragOver = useCallback((e: React.DragEvent) => {\n e.preventDefault();\n setDragOver(true);\n }, []);\n\n const handleDragLeave = useCallback(() => setDragOver(false), []);\n\n const handleClick = useCallback(\n (e: React.MouseEvent) => {\n if (disabled) return;\n // The input's own click bubbles back up here; re-entering would ask for a\n // second picker.\n if (e.target === inputRef.current) return;\n inputRef.current?.click();\n },\n [disabled],\n );\n\n return (\n <div\n data-react-fancy-file-upload-dropzone=\"\"\n data-drag-over={dragOver ? \"\" : undefined}\n data-disabled={disabled ? \"\" : undefined}\n aria-disabled={disabled || undefined}\n onDrop={handleDrop}\n onDragOver={handleDragOver}\n onDragLeave={handleDragLeave}\n onClick={handleClick}\n className={cn(\n \"flex cursor-pointer flex-col items-center justify-center rounded-xl border-2 border-dashed p-8 text-center transition-[color,background-color,border-color,box-shadow]\",\n // Full opacity on purpose: a translucent 2px ring over a dashed border\n // falls under WCAG 2.2's 3:1 for a focus indicator. The offset gap is\n // painted with the surface token so it matches the page in dark mode.\n \"has-focus-visible:ring-2 has-focus-visible:ring-offset-2 has-focus-visible:ring-[color:var(--fancy-focus,var(--color-blue-500))] has-focus-visible:ring-offset-[color:var(--fancy-surface,var(--color-white))] dark:has-focus-visible:ring-[color:var(--fancy-focus-dark,var(--color-blue-400))] dark:has-focus-visible:ring-offset-[color:var(--fancy-surface-dark,var(--color-zinc-900))]\",\n dragOver\n ? \"border-blue-400 bg-blue-50 dark:border-blue-500 dark:bg-blue-950\"\n : \"border-[color:var(--fancy-border-strong,var(--color-zinc-300))] hover:border-zinc-400 dark:border-[color:var(--fancy-border-strong-dark,var(--color-zinc-700))] dark:hover:border-zinc-500\",\n disabled && \"cursor-not-allowed opacity-50\",\n className,\n )}\n >\n <input\n ref={inputRef}\n id={inputId}\n type=\"file\"\n className=\"hidden\"\n tabIndex={-1}\n aria-label={inputLabel}\n onChange={(e) => {\n if (e.target.files) addFiles(e.target.files);\n\n // Reset, or choosing the SAME file again fires no `change` event at\n // all and the gesture is simply dead — \"remove it, then re-add it\"\n // and \"retry the import that just failed\" both stop working. The\n // component never sees those files to reject them; it is not asked.\n e.target.value = \"\";\n }}\n accept={accept}\n multiple={multiple}\n disabled={disabled}\n />\n {custom ? (\n <>\n {children}\n <button\n type=\"button\"\n data-react-fancy-file-upload-browse=\"\"\n aria-controls={inputId}\n disabled={disabled}\n className=\"sr-only\"\n >\n {browseLabel ?? DEFAULT_BROWSE_LABEL}\n </button>\n </>\n ) : (\n <button\n type=\"button\"\n data-react-fancy-file-upload-browse=\"\"\n aria-controls={inputId}\n aria-label={browseLabel}\n disabled={disabled}\n className=\"flex cursor-pointer flex-col items-center rounded-md outline-none disabled:cursor-not-allowed\"\n >\n <Upload size={32} strokeWidth={1.5} aria-hidden=\"true\" className=\"mb-2 text-zinc-400\" />\n <span className=\"text-sm text-zinc-500\">{DEFAULT_COPY}</span>\n </button>\n )}\n </div>\n );\n}\n\nFileUploadDropzone.displayName = \"FileUploadDropzone\";\n","import { useEffect, useMemo } from \"react\";\nimport { File, X } from \"lucide-react\";\nimport { cn } from \"../../utils/cn\";\nimport { useFileUpload } from \"./FileUpload.context\";\nimport type { FileUploadListProps } from \"./FileUpload.types\";\n\nfunction formatSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n}\n\nfunction getExtension(name: string): string {\n const parts = name.split(\".\");\n return parts.length > 1 ? parts.pop()!.toUpperCase() : \"\";\n}\n\nfunction ThumbnailItem({ file, index, onRemove }: { file: File; index: number; onRemove: (i: number) => void }) {\n const isImage = file.type.startsWith(\"image/\");\n const objectUrl = useMemo(() => (isImage ? URL.createObjectURL(file) : null), [file, isImage]);\n\n useEffect(() => {\n return () => {\n if (objectUrl) URL.revokeObjectURL(objectUrl);\n };\n }, [objectUrl]);\n\n return (\n <div className=\"group relative flex w-20 flex-col items-center gap-1\">\n <div className=\"relative h-20 w-20 overflow-hidden rounded-lg border border-[color:var(--fancy-border,var(--color-zinc-200))] bg-zinc-50 dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))] dark:bg-zinc-800\">\n {isImage && objectUrl ? (\n <img src={objectUrl} alt={file.name} className=\"h-full w-full object-cover\" />\n ) : (\n <div className=\"flex h-full w-full flex-col items-center justify-center gap-1\">\n <File size={20} className=\"text-zinc-400\" />\n <span className=\"text-[10px] font-medium text-zinc-500\">{getExtension(file.name)}</span>\n </div>\n )}\n <button\n type=\"button\"\n onClick={() => onRemove(index)}\n className=\"absolute top-1 right-1 flex h-5 w-5 items-center justify-center rounded-full bg-black/60 text-white opacity-0 transition-opacity hover:bg-black/80 group-hover:opacity-100\"\n aria-label={`Remove ${file.name}`}\n >\n <X size={10} />\n </button>\n </div>\n <span className=\"w-full truncate text-center text-xs text-zinc-500\">{file.name}</span>\n </div>\n );\n}\n\nexport function FileUploadList({ thumbnail, className }: FileUploadListProps) {\n const { files, removeFile } = useFileUpload();\n\n if (files.length === 0) return null;\n\n if (thumbnail) {\n return (\n <div data-react-fancy-file-upload-list=\"\" className={cn(\"mt-3 flex flex-wrap gap-3\", className)}>\n {files.map((file, i) => (\n <ThumbnailItem key={`${file.name}-${i}`} file={file} index={i} onRemove={removeFile} />\n ))}\n </div>\n );\n }\n\n return (\n <ul data-react-fancy-file-upload-list=\"\" className={cn(\"mt-3 space-y-2\", className)}>\n {files.map((file, i) => (\n <li\n key={`${file.name}-${i}`}\n className=\"flex items-center gap-3 rounded-lg border border-[color:var(--fancy-border,var(--color-zinc-200))] px-3 py-2 text-sm dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))]\"\n >\n <File size={16} className=\"shrink-0 text-zinc-400\" />\n <span className=\"flex-1 truncate\">{file.name}</span>\n <span className=\"shrink-0 text-xs text-zinc-400\">\n {formatSize(file.size)}\n </span>\n <button\n type=\"button\"\n onClick={() => removeFile(i)}\n className=\"shrink-0 text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300\"\n aria-label={`Remove ${file.name}`}\n >\n <X size={14} />\n </button>\n </li>\n ))}\n </ul>\n );\n}\n\nFileUploadList.displayName = \"FileUploadList\";\n","/**\n * Does a file satisfy an `accept` string?\n *\n * The browser enforces `accept` in the FILE PICKER only. A drag-and-drop never\n * opens a picker, so on the drop path this is the only thing standing between an\n * image-only dropzone and a dropped executable. Forwarding the attribute to the\n * hidden input is half the job; this is the other half.\n *\n * Follows the HTML `accept` grammar: a comma-separated list of extensions\n * (`.csv`), exact MIME types (`text/plain`) and type wildcards (`image/*`).\n * Matching is case-insensitive, because a drop reports whatever the OS says and\n * `TEXT/PLAIN` is a real thing to receive.\n *\n * A dropped file often carries an EMPTY `type` — the OS may not know it and\n * `File.type` is a hint, never a guarantee. That is why extension rules are\n * checked against the name and not the type: an `accept=\".csv\"` zone has to work\n * for a .csv whose type is `\"\"`, which is the common case rather than the edge.\n */\nexport function matchesAccept(file: File, accept: string): boolean {\n const rules = accept\n .split(\",\")\n .map((rule) => rule.trim().toLowerCase())\n .filter(Boolean);\n\n // An accept string that lists nothing constrains nothing. Failing closed here\n // would turn `accept=\"\"` into a dropzone that silently swallows every file.\n if (rules.length === 0) return true;\n\n const type = file.type.toLowerCase();\n const name = file.name.toLowerCase();\n\n return rules.some((rule) => {\n if (rule.startsWith(\".\")) return name.endsWith(rule);\n if (rule.endsWith(\"/*\")) return type.startsWith(rule.slice(0, -1));\n return type === rule;\n });\n}\n","import { useCallback, useMemo, useState } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { FileUploadContext } from \"./FileUpload.context\";\nimport { FileUploadDropzone } from \"./FileUploadDropzone\";\nimport { FileUploadList } from \"./FileUploadList\";\nimport { matchesAccept } from \"./matchesAccept\";\nimport type { FileUploadProps } from \"./FileUpload.types\";\n\nfunction FileUploadRoot({\n children,\n value,\n onChange,\n accept,\n multiple = true,\n maxFiles,\n maxSize,\n disabled = false,\n className,\n}: FileUploadProps) {\n const [internalFiles, setInternalFiles] = useState<File[]>([]);\n const files = value ?? internalFiles;\n\n const addFiles = useCallback(\n (newFiles: FileList | File[]) => {\n let toAdd = Array.from(newFiles);\n\n // `accept` is enforced HERE, not only on the input: the input's attribute\n // governs the picker, and a drop never opens one.\n if (accept) {\n toAdd = toAdd.filter((f) => matchesAccept(f, accept));\n }\n\n if (maxSize) {\n toAdd = toAdd.filter((f) => f.size <= maxSize);\n }\n\n /*\n * `multiple={false}` REPLACES; `maxFiles` refuses the overflow.\n *\n * These used to be the same rule — a cap of one, first-wins — on the\n * reasoning that the two props must not disagree about what a single-file\n * upload means. But they are different statements:\n *\n * - `maxFiles={n}` is a QUOTA. Dropping five files into a three-file\n * field should keep three, not the last three.\n * - `multiple={false}` is a FIELD SHAPE: this input holds one file. Its\n * analogue is a radio group, where choosing again MOVES the selection.\n *\n * Under the old rule a single-file field could never change its mind: the\n * second choice computed `[old, new].slice(0, 1)` and discarded the new\n * file in silence. The YouGene estate worked around it by remounting the\n * component with a `key`, which is the shape of a bug rather than a\n * preference.\n */\n if (multiple === false) {\n /*\n * The FIRST of the incoming batch, not the last.\n *\n * Two separate questions hide here, and only one of them was the bug:\n *\n * - ACROSS gestures — choose a file, then choose another. The second\n * must win, or the field cannot change its mind. That is the fix.\n * - WITHIN one gesture — drop two files at once onto a single-file\n * field. Which one wins is arbitrary, and taking the first is what\n * has always shipped; there is no replacement intent in a single\n * ambiguous drop.\n *\n * Taking `slice(-1)` would have answered both with \"the last\", quietly\n * changing the second. The existing drop test caught it.\n */\n const replacement = toAdd.slice(0, 1);\n // Nothing survived the accept/size filters — leave the field alone\n // rather than clearing a file the user never asked to remove.\n const next = replacement.length > 0 ? replacement : files;\n\n if (value === undefined) setInternalFiles(next);\n onChange?.(next);\n\n return;\n }\n\n const updated = [...files, ...toAdd];\n const limited = maxFiles ? updated.slice(0, maxFiles) : updated;\n\n if (value === undefined) setInternalFiles(limited);\n onChange?.(limited);\n },\n [accept, files, maxFiles, maxSize, multiple, onChange, value],\n );\n\n const removeFile = useCallback(\n (index: number) => {\n const updated = files.filter((_, i) => i !== index);\n if (value === undefined) setInternalFiles(updated);\n onChange?.(updated);\n },\n [files, onChange, value],\n );\n\n const ctx = useMemo(\n () => ({ files, addFiles, removeFile, disabled, accept, multiple }),\n [files, addFiles, removeFile, disabled, accept, multiple],\n );\n\n return (\n <FileUploadContext.Provider value={ctx}>\n <div data-react-fancy-file-upload=\"\" className={cn(className)}>{children}</div>\n </FileUploadContext.Provider>\n );\n}\n\nexport const FileUpload = Object.assign(FileUploadRoot, {\n Dropzone: FileUploadDropzone,\n List: FileUploadList,\n});\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunk34GALZRX_cjs = require('./chunk-34GALZRX.cjs');
|
|
4
4
|
var chunkG7227RTY_cjs = require('./chunk-G7227RTY.cjs');
|
|
5
5
|
var chunkL3V3BYVS_cjs = require('./chunk-L3V3BYVS.cjs');
|
|
6
6
|
var chunkX2FC7ODX_cjs = require('./chunk-X2FC7ODX.cjs');
|
|
@@ -130,7 +130,7 @@ function EditorContent({ className, maxHeight, sourceClassName }) {
|
|
|
130
130
|
}
|
|
131
131
|
}, [contentRef, _initialHtml, showSource]);
|
|
132
132
|
const extClasses = react.useMemo(
|
|
133
|
-
() =>
|
|
133
|
+
() => chunk34GALZRX_cjs.extensionEditorClasses(extensions),
|
|
134
134
|
[extensions]
|
|
135
135
|
);
|
|
136
136
|
if (showSource) {
|
|
@@ -162,7 +162,7 @@ function EditorContent({ className, maxHeight, sourceClassName }) {
|
|
|
162
162
|
className: chunkZU5Q77OT_cjs.cn(
|
|
163
163
|
"min-h-[120px] flex-auto overflow-y-auto px-4 py-3 text-sm outline-none",
|
|
164
164
|
"focus:outline-none",
|
|
165
|
-
|
|
165
|
+
chunk34GALZRX_cjs.proseClasses,
|
|
166
166
|
extClasses,
|
|
167
167
|
"empty:before:content-[attr(data-placeholder)] empty:before:text-zinc-400 empty:before:pointer-events-none",
|
|
168
168
|
className
|
|
@@ -174,7 +174,7 @@ EditorContent.displayName = "EditorContent";
|
|
|
174
174
|
function toHtml(value, outputFormat, unsafe, valueFormat = "auto") {
|
|
175
175
|
if (!value) return "";
|
|
176
176
|
const raw = (() => {
|
|
177
|
-
const format = valueFormat !== "auto" ? valueFormat : outputFormat === "html" ? "html" :
|
|
177
|
+
const format = valueFormat !== "auto" ? valueFormat : outputFormat === "html" ? "html" : chunk34GALZRX_cjs.detectFormat(value);
|
|
178
178
|
if (format === "html") return value;
|
|
179
179
|
return marked.marked.parse(value, { async: false }).trim();
|
|
180
180
|
})();
|
|
@@ -222,12 +222,12 @@ function EditorRoot({
|
|
|
222
222
|
[isView, outputFormat, unsafe, valueFormat, showSource, externalSeed]
|
|
223
223
|
);
|
|
224
224
|
const extensions = react.useMemo(
|
|
225
|
-
() =>
|
|
225
|
+
() => chunk34GALZRX_cjs.mergeExtensions(instanceExtensions),
|
|
226
226
|
[instanceExtensions]
|
|
227
227
|
);
|
|
228
228
|
const getOutputValue = react.useCallback(
|
|
229
229
|
(html) => {
|
|
230
|
-
return outputFormat === "markdown" ?
|
|
230
|
+
return outputFormat === "markdown" ? chunk34GALZRX_cjs.htmlToMarkdown(html) : html;
|
|
231
231
|
},
|
|
232
232
|
[outputFormat]
|
|
233
233
|
);
|
|
@@ -306,7 +306,7 @@ function EditorRoot({
|
|
|
306
306
|
className
|
|
307
307
|
),
|
|
308
308
|
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
309
|
-
|
|
309
|
+
chunk34GALZRX_cjs.ContentRenderer,
|
|
310
310
|
{
|
|
311
311
|
value,
|
|
312
312
|
format: valueFormat !== "auto" ? valueFormat : outputFormat,
|
|
@@ -343,5 +343,5 @@ var Editor = Object.assign(EditorRoot, {
|
|
|
343
343
|
|
|
344
344
|
exports.Editor = Editor;
|
|
345
345
|
exports.useEditor = useEditor;
|
|
346
|
-
//# sourceMappingURL=chunk-
|
|
347
|
-
//# sourceMappingURL=chunk-
|
|
346
|
+
//# sourceMappingURL=chunk-BX7ZJ3HG.cjs.map
|
|
347
|
+
//# sourceMappingURL=chunk-BX7ZJ3HG.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/Editor/Editor.context.ts","../src/components/Editor/EditorSourceToggle.tsx","../src/components/Editor/EditorToolbar.tsx","../src/components/Editor/EditorToolbarSeparator.tsx","../src/components/Editor/EditorContent.tsx","../src/components/Editor/Editor.tsx"],"names":["createContext","useContext","jsx","cn","jsxs","Fragment","useRef","useEffect","useMemo","extensionEditorClasses","CodeView","proseClasses","detectFormat","marked","sanitizeHtml","useControllableState","useFieldMode","useState","mergeExtensions","useCallback","htmlToMarkdown","ContentRenderer"],"mappings":"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","file":"chunk-ICI33QMY.cjs","sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { RefObject } from \"react\";\nimport type { RenderExtension } from \"../ContentRenderer/extensions\";\n\nexport interface EditorContextValue {\n contentRef: RefObject<HTMLDivElement | null>;\n exec: (command: string, arg?: string) => void;\n insertText: (text: string) => void;\n wrapSelection: (before: string, after: string) => void;\n outputFormat: \"html\" | \"markdown\";\n lineSpacing: number;\n placeholder?: string;\n /** Merged render extensions (global + instance). */\n extensions: RenderExtension[];\n /** Whether the raw-source view (textarea) is showing instead of the rich-text surface. */\n showSource: boolean;\n /** Toggle/set the raw-source view. */\n setShowSource: (showSource: boolean) => void;\n /** Initial HTML content to load into the editor on mount */\n _initialHtml: string;\n /** @internal Called by EditorContent on input events */\n _onInput: () => void;\n /** @internal Raw source value (`value` in `outputFormat`), for the source textarea. */\n _sourceValue: string;\n /** @internal Commit an edit made directly in the source textarea. */\n _onSourceInput: (value: string) => void;\n}\n\nexport const EditorContext = createContext<EditorContextValue | null>(null);\n\nexport function useEditor(): EditorContextValue {\n const ctx = useContext(EditorContext);\n if (!ctx) {\n throw new Error(\"useEditor must be used within an <Editor> component\");\n }\n return ctx;\n}\n","import { cn } from \"../../utils/cn\";\nimport { useEditor } from \"./Editor.context\";\nimport type { EditorSourceToggleProps } from \"./Editor.types\";\n\n/**\n * Toolbar button that reveals the raw HTML/Markdown behind the editor. Reads\n * `showSource` / `setShowSource` from context, so it works in the default\n * toolbar and in any custom toolbar composed with `useEditor()`.\n */\nexport function EditorSourceToggle({\n className,\n icon = \"</>\",\n title = \"Source\",\n activeTitle = \"Rich text\",\n}: EditorSourceToggleProps) {\n const { showSource, setShowSource } = useEditor();\n\n return (\n <button\n type=\"button\"\n onClick={() => setShowSource(!showSource)}\n title={showSource ? activeTitle : title}\n aria-pressed={showSource}\n data-react-fancy-editor-source-toggle=\"\"\n className={cn(\n \"inline-flex h-8 items-center justify-center rounded-md px-2 font-mono text-xs transition-colors\",\n showSource\n ? \"bg-[color:var(--fancy-border,var(--color-zinc-200))] dark:bg-[color:var(--fancy-border-dark,var(--color-zinc-700))]\"\n : \"hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))]\",\n className,\n )}\n >\n {icon}\n </button>\n );\n}\n\nEditorSourceToggle.displayName = \"EditorSourceToggle\";\n","import { cn } from \"../../utils/cn\";\nimport { useEditor } from \"./Editor.context\";\nimport { EditorSourceToggle } from \"./EditorSourceToggle\";\nimport type { EditorToolbarProps } from \"./Editor.types\";\n\nconst DEFAULT_ACTIONS = [\n { icon: \"B\", label: \"Bold\", command: \"bold\" },\n { icon: \"I\", label: \"Italic\", command: \"italic\" },\n { icon: \"U\", label: \"Underline\", command: \"underline\" },\n { icon: \"S\", label: \"Strikethrough\", command: \"strikeThrough\" },\n];\n\nexport function EditorToolbar({\n actions = DEFAULT_ACTIONS,\n onAction,\n children,\n className,\n showSourceToggle = true,\n}: EditorToolbarProps) {\n const { exec, showSource } = useEditor();\n\n return (\n <div\n data-react-fancy-editor-toolbar=\"\"\n className={cn(\n \"flex shrink-0 items-center gap-0.5 border-b border-[color:var(--fancy-border,var(--color-zinc-200))] px-2 py-1.5 dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))]\",\n className,\n )}\n >\n {children ?? (\n <>\n {actions.map((action) => (\n <button\n key={`${action.command}-${action.commandArg ?? \"\"}`}\n type=\"button\"\n // Rich-text commands don't apply to the raw-source textarea.\n disabled={showSource}\n onClick={() => {\n if (onAction) {\n onAction(action.command);\n } else {\n exec(action.command, action.commandArg);\n }\n }}\n title={action.label}\n className={cn(\n \"inline-flex h-8 w-8 items-center justify-center rounded-md text-sm transition-colors\",\n action.active\n ? \"bg-[color:var(--fancy-border,var(--color-zinc-200))] dark:bg-[color:var(--fancy-border-dark,var(--color-zinc-700))]\"\n : \"hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))]\",\n showSource && \"cursor-not-allowed opacity-40 hover:bg-transparent\",\n )}\n >\n {action.icon}\n </button>\n ))}\n {showSourceToggle && <EditorSourceToggle className=\"ml-auto\" />}\n </>\n )}\n </div>\n );\n}\n\nEditorToolbar.displayName = \"EditorToolbar\";\n","export function EditorToolbarSeparator() {\n return (\n <span\n data-react-fancy-editor-toolbar-separator=\"\"\n className=\"mx-1 h-5 w-px bg-[color:var(--fancy-border,var(--color-zinc-200))] dark:bg-[color:var(--fancy-border-dark,var(--color-zinc-700))]\"\n />\n );\n}\n\nEditorToolbarSeparator.displayName = \"EditorToolbarSeparator\";\n","import { useEffect, useMemo, useRef } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { CodeView } from \"../CodeView\";\nimport { useEditor } from \"./Editor.context\";\nimport { proseClasses, extensionEditorClasses } from \"./editor.utils\";\nimport type { EditorContentProps } from \"./Editor.types\";\n\nexport function EditorContent({ className, maxHeight, sourceClassName }: EditorContentProps) {\n const {\n contentRef,\n lineSpacing,\n placeholder,\n extensions,\n outputFormat,\n showSource,\n _initialHtml,\n _onInput,\n _sourceValue,\n _onSourceInput,\n } = useEditor();\n // Tracks which seed HTML is currently in the contentEditable. Seeding on the\n // seed VALUE (not a one-shot boolean) re-seeds when the source view rewrites\n // `value`, while still never re-seeding on plain keystrokes — typing leaves\n // `_initialHtml` untouched, so `seededHtml.current === _initialHtml` holds.\n const seededHtml = useRef<string | null>(null);\n\n useEffect(() => {\n // Source view replaces the contentEditable in the tree, so it unmounts.\n // Forget what we seeded — the div that returns is a fresh, empty one and\n // must always be re-seeded, even if `value` was left untouched.\n if (showSource) {\n seededHtml.current = null;\n return;\n }\n const el = contentRef.current;\n if (el && seededHtml.current !== _initialHtml) {\n el.innerHTML = _initialHtml;\n seededHtml.current = _initialHtml;\n }\n }, [contentRef, _initialHtml, showSource]);\n\n const extClasses = useMemo(\n () => extensionEditorClasses(extensions),\n [extensions],\n );\n\n if (showSource) {\n // Raw-source view — a syntax-highlighted code surface (HTML highlighted;\n // markdown/plain un-highlighted), built on the shared fancy-file-commons\n // primitives. It's a DIRECT child of the flex-column editor root (no\n // wrapper — a nested flex box collapses the fill), so `flex-auto` makes it\n // fill the editor body when height-constrained and grow with content\n // (≥ minHeight) when not. `maxHeight` caps + scrolls.\n return (\n <CodeView\n value={_sourceValue}\n onChange={_onSourceInput}\n language={outputFormat === \"html\" ? \"html\" : \"plaintext\"}\n placeholder={placeholder}\n maxHeight={maxHeight}\n className={cn(\"min-h-0 flex-auto\", sourceClassName)}\n />\n );\n }\n\n return (\n <div\n ref={contentRef}\n contentEditable\n suppressContentEditableWarning\n data-react-fancy-editor-content=\"\"\n data-placeholder={placeholder}\n onInput={_onInput}\n style={{\n lineHeight: lineSpacing,\n maxHeight: maxHeight ? `${maxHeight}px` : undefined,\n }}\n className={cn(\n \"min-h-[120px] flex-auto overflow-y-auto px-4 py-3 text-sm outline-none\",\n \"focus:outline-none\",\n proseClasses,\n extClasses,\n \"empty:before:content-[attr(data-placeholder)] empty:before:text-zinc-400 empty:before:pointer-events-none\",\n className,\n )}\n />\n );\n}\n\nEditorContent.displayName = \"EditorContent\";\n","import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { marked } from \"marked\";\nimport { cn } from \"../../utils/cn\";\nimport { sanitizeHtml } from \"../../utils/sanitize\";\nimport { useControllableState } from \"../../hooks\";\nimport { EditorContext } from \"./Editor.context\";\nimport { EditorToolbar } from \"./EditorToolbar\";\nimport { EditorToolbarSeparator } from \"./EditorToolbarSeparator\";\nimport { EditorSourceToggle } from \"./EditorSourceToggle\";\nimport { EditorContent } from \"./EditorContent\";\nimport { htmlToMarkdown, detectFormat } from \"./editor.utils\";\nimport { mergeExtensions } from \"../ContentRenderer/extensions\";\nimport { ContentRenderer } from \"../ContentRenderer/ContentRenderer\";\nimport { useFieldMode } from \"../inputs/mode/FieldMode.context\";\nimport type { EditorProps } from \"./Editor.types\";\n\nfunction toHtml(\n value: string,\n outputFormat: \"html\" | \"markdown\",\n unsafe: boolean,\n valueFormat: \"markdown\" | \"html\" | \"auto\" = \"auto\",\n): string {\n if (!value) return \"\";\n const raw = (() => {\n // An explicit valueFormat is authoritative — the sniff misclassifies\n // markdown that merely MENTIONS HTML-ish snippets (`<code>`, `<table`),\n // collapsing it into an innerHTML wall of text (#10).\n const format =\n valueFormat !== \"auto\"\n ? valueFormat\n : outputFormat === \"html\"\n ? \"html\"\n : detectFormat(value);\n if (format === \"html\") return value;\n return (marked.parse(value, { async: false }) as string).trim();\n })();\n return unsafe ? raw : sanitizeHtml(raw);\n}\n\nfunction EditorRoot({\n children,\n className,\n value: controlledValue,\n defaultValue = \"\",\n onChange,\n outputFormat = \"html\",\n valueFormat = \"auto\",\n lineSpacing = 1.6,\n placeholder,\n mode: modeProp,\n extensions: instanceExtensions,\n unsafe = false,\n showSource: showSourceProp,\n defaultShowSource = false,\n onShowSourceChange,\n}: EditorProps) {\n const contentRef = useRef<HTMLDivElement | null>(null);\n const [value, setValue] = useControllableState(controlledValue, defaultValue, onChange);\n const [showSource, setShowSource] = useControllableState(\n showSourceProp,\n defaultShowSource,\n onShowSourceChange,\n );\n // View/edit mode — same resolution as the inputs: prop → <Form> context → \"edit\".\n const mode = useFieldMode(modeProp);\n const isView = mode === \"view\";\n\n // The value this editor last emitted through `onChange`. An incoming `value`\n // equal to it is our own keystroke echoing back; anything else came from\n // OUTSIDE — an async load, a host reset, a form repopulating.\n const lastEmitted = useRef<string | null>(null);\n\n // Bumped only on an external `value` change, so the seed memo below recomputes\n // for those and stays put while the user types. Without this, `value` was\n // excluded from the deps to avoid re-seeding on keystrokes, which also meant a\n // value arriving after mount was silently dropped and the editor stayed blank\n // forever (#15).\n const [externalSeed, setExternalSeed] = useState(0);\n useEffect(() => {\n if (value !== lastEmitted.current) setExternalSeed((n) => n + 1);\n }, [value]);\n\n const initialHtml = useMemo(\n () => toHtml(value, outputFormat, unsafe, valueFormat),\n // Seed the contentEditable from the LIVE value when (re)entering edit mode —\n // and when returning from the raw-source view, which may have rewritten the\n // value out from under the rich-text surface. `value` itself stays out of\n // the deps so typing never re-seeds (which would fight the caret);\n // `externalSeed` covers the externally-driven case instead.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [isView, outputFormat, unsafe, valueFormat, showSource, externalSeed],\n );\n\n const extensions = useMemo(\n () => mergeExtensions(instanceExtensions),\n [instanceExtensions],\n );\n\n const getOutputValue = useCallback(\n (html: string): string => {\n return outputFormat === \"markdown\" ? htmlToMarkdown(html) : html;\n },\n [outputFormat],\n );\n\n const handleInput = useCallback(() => {\n const el = contentRef.current;\n if (!el) return;\n const next = getOutputValue(el.innerHTML);\n // Record what we emitted BEFORE publishing it, so the value coming back in\n // is recognised as our own and does not trigger a re-seed mid-keystroke.\n lastEmitted.current = next;\n setValue(next);\n }, [setValue, getOutputValue]);\n\n const exec = useCallback(\n (command: string, arg?: string) => {\n const el = contentRef.current;\n if (!el) return;\n el.focus();\n document.execCommand(command, false, arg);\n handleInput();\n },\n [handleInput],\n );\n\n const insertText = useCallback(\n (text: string) => {\n const el = contentRef.current;\n if (!el) return;\n el.focus();\n document.execCommand(\"insertText\", false, text);\n handleInput();\n },\n [handleInput],\n );\n\n // The source textarea edits `value` directly — it is already the canonical\n // source in `outputFormat` (html or markdown), so no conversion round-trip.\n const handleSourceInput = useCallback(\n (next: string) => {\n // Also an internal emission — record it so typing in the source view is\n // not mistaken for an external change on every keystroke.\n lastEmitted.current = next;\n setValue(next);\n },\n [setValue],\n );\n\n const wrapSelection = useCallback(\n (before: string, after: string) => {\n const sel = window.getSelection();\n if (!sel || sel.rangeCount === 0) return;\n const range = sel.getRangeAt(0);\n const selectedText = range.toString();\n const el = contentRef.current;\n if (!el) return;\n el.focus();\n document.execCommand(\"insertText\", false, `${before}${selectedText}${after}`);\n handleInput();\n },\n [handleInput],\n );\n\n const contextValue = {\n contentRef,\n exec,\n insertText,\n wrapSelection,\n outputFormat,\n lineSpacing,\n placeholder,\n extensions,\n showSource,\n setShowSource,\n _initialHtml: initialHtml,\n _onInput: handleInput,\n _sourceValue: value,\n _onSourceInput: handleSourceInput,\n };\n\n // View mode — render the value read-only through ContentRenderer, matching the\n // editor's own format (markdown/html), instead of the editable toolbar+content\n // tree. Driven by `mode` / a `<Form mode=\"view\">`, exactly like the inputs.\n if (isView) {\n return (\n <div\n data-react-fancy-editor=\"\"\n data-mode=\"view\"\n className={cn(\n \"overflow-hidden rounded-xl border border-[color:var(--fancy-border,var(--color-zinc-200))] bg-[color:var(--fancy-surface,var(--color-white))] dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))] dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]\",\n className,\n )}\n >\n <ContentRenderer\n value={value}\n format={valueFormat !== \"auto\" ? valueFormat : outputFormat}\n lineSpacing={lineSpacing}\n extensions={instanceExtensions}\n unsafe={unsafe}\n className=\"px-4 py-3\"\n />\n </div>\n );\n }\n\n return (\n <EditorContext.Provider value={contextValue}>\n <div\n data-react-fancy-editor=\"\"\n data-mode=\"edit\"\n className={cn(\n \"flex flex-col overflow-hidden rounded-xl border border-[color:var(--fancy-border,var(--color-zinc-200))] bg-[color:var(--fancy-surface,var(--color-white))] dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))] dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]\",\n className,\n )}\n >\n {children}\n </div>\n </EditorContext.Provider>\n );\n}\n\nconst ToolbarWithSeparator = Object.assign(EditorToolbar, {\n Separator: EditorToolbarSeparator,\n});\n\nexport const Editor = Object.assign(EditorRoot, {\n Toolbar: ToolbarWithSeparator,\n Content: EditorContent,\n SourceToggle: EditorSourceToggle,\n});\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/components/Editor/Editor.context.ts","../src/components/Editor/EditorSourceToggle.tsx","../src/components/Editor/EditorToolbar.tsx","../src/components/Editor/EditorToolbarSeparator.tsx","../src/components/Editor/EditorContent.tsx","../src/components/Editor/Editor.tsx"],"names":["createContext","useContext","jsx","cn","jsxs","Fragment","useRef","useEffect","useMemo","extensionEditorClasses","CodeView","proseClasses","detectFormat","marked","sanitizeHtml","useControllableState","useFieldMode","useState","mergeExtensions","useCallback","htmlToMarkdown","ContentRenderer"],"mappings":"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","file":"chunk-BX7ZJ3HG.cjs","sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { RefObject } from \"react\";\nimport type { RenderExtension } from \"../ContentRenderer/extensions\";\n\nexport interface EditorContextValue {\n contentRef: RefObject<HTMLDivElement | null>;\n exec: (command: string, arg?: string) => void;\n insertText: (text: string) => void;\n wrapSelection: (before: string, after: string) => void;\n outputFormat: \"html\" | \"markdown\";\n lineSpacing: number;\n placeholder?: string;\n /** Merged render extensions (global + instance). */\n extensions: RenderExtension[];\n /** Whether the raw-source view (textarea) is showing instead of the rich-text surface. */\n showSource: boolean;\n /** Toggle/set the raw-source view. */\n setShowSource: (showSource: boolean) => void;\n /** Initial HTML content to load into the editor on mount */\n _initialHtml: string;\n /** @internal Called by EditorContent on input events */\n _onInput: () => void;\n /** @internal Raw source value (`value` in `outputFormat`), for the source textarea. */\n _sourceValue: string;\n /** @internal Commit an edit made directly in the source textarea. */\n _onSourceInput: (value: string) => void;\n}\n\nexport const EditorContext = createContext<EditorContextValue | null>(null);\n\nexport function useEditor(): EditorContextValue {\n const ctx = useContext(EditorContext);\n if (!ctx) {\n throw new Error(\"useEditor must be used within an <Editor> component\");\n }\n return ctx;\n}\n","import { cn } from \"../../utils/cn\";\nimport { useEditor } from \"./Editor.context\";\nimport type { EditorSourceToggleProps } from \"./Editor.types\";\n\n/**\n * Toolbar button that reveals the raw HTML/Markdown behind the editor. Reads\n * `showSource` / `setShowSource` from context, so it works in the default\n * toolbar and in any custom toolbar composed with `useEditor()`.\n */\nexport function EditorSourceToggle({\n className,\n icon = \"</>\",\n title = \"Source\",\n activeTitle = \"Rich text\",\n}: EditorSourceToggleProps) {\n const { showSource, setShowSource } = useEditor();\n\n return (\n <button\n type=\"button\"\n onClick={() => setShowSource(!showSource)}\n title={showSource ? activeTitle : title}\n aria-pressed={showSource}\n data-react-fancy-editor-source-toggle=\"\"\n className={cn(\n \"inline-flex h-8 items-center justify-center rounded-md px-2 font-mono text-xs transition-colors\",\n showSource\n ? \"bg-[color:var(--fancy-border,var(--color-zinc-200))] dark:bg-[color:var(--fancy-border-dark,var(--color-zinc-700))]\"\n : \"hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))]\",\n className,\n )}\n >\n {icon}\n </button>\n );\n}\n\nEditorSourceToggle.displayName = \"EditorSourceToggle\";\n","import { cn } from \"../../utils/cn\";\nimport { useEditor } from \"./Editor.context\";\nimport { EditorSourceToggle } from \"./EditorSourceToggle\";\nimport type { EditorToolbarProps } from \"./Editor.types\";\n\nconst DEFAULT_ACTIONS = [\n { icon: \"B\", label: \"Bold\", command: \"bold\" },\n { icon: \"I\", label: \"Italic\", command: \"italic\" },\n { icon: \"U\", label: \"Underline\", command: \"underline\" },\n { icon: \"S\", label: \"Strikethrough\", command: \"strikeThrough\" },\n];\n\nexport function EditorToolbar({\n actions = DEFAULT_ACTIONS,\n onAction,\n children,\n className,\n showSourceToggle = true,\n}: EditorToolbarProps) {\n const { exec, showSource } = useEditor();\n\n return (\n <div\n data-react-fancy-editor-toolbar=\"\"\n className={cn(\n \"flex shrink-0 items-center gap-0.5 border-b border-[color:var(--fancy-border,var(--color-zinc-200))] px-2 py-1.5 dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))]\",\n className,\n )}\n >\n {children ?? (\n <>\n {actions.map((action) => (\n <button\n key={`${action.command}-${action.commandArg ?? \"\"}`}\n type=\"button\"\n // Rich-text commands don't apply to the raw-source textarea.\n disabled={showSource}\n onClick={() => {\n if (onAction) {\n onAction(action.command);\n } else {\n exec(action.command, action.commandArg);\n }\n }}\n title={action.label}\n className={cn(\n \"inline-flex h-8 w-8 items-center justify-center rounded-md text-sm transition-colors\",\n action.active\n ? \"bg-[color:var(--fancy-border,var(--color-zinc-200))] dark:bg-[color:var(--fancy-border-dark,var(--color-zinc-700))]\"\n : \"hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))]\",\n showSource && \"cursor-not-allowed opacity-40 hover:bg-transparent\",\n )}\n >\n {action.icon}\n </button>\n ))}\n {showSourceToggle && <EditorSourceToggle className=\"ml-auto\" />}\n </>\n )}\n </div>\n );\n}\n\nEditorToolbar.displayName = \"EditorToolbar\";\n","export function EditorToolbarSeparator() {\n return (\n <span\n data-react-fancy-editor-toolbar-separator=\"\"\n className=\"mx-1 h-5 w-px bg-[color:var(--fancy-border,var(--color-zinc-200))] dark:bg-[color:var(--fancy-border-dark,var(--color-zinc-700))]\"\n />\n );\n}\n\nEditorToolbarSeparator.displayName = \"EditorToolbarSeparator\";\n","import { useEffect, useMemo, useRef } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { CodeView } from \"../CodeView\";\nimport { useEditor } from \"./Editor.context\";\nimport { proseClasses, extensionEditorClasses } from \"./editor.utils\";\nimport type { EditorContentProps } from \"./Editor.types\";\n\nexport function EditorContent({ className, maxHeight, sourceClassName }: EditorContentProps) {\n const {\n contentRef,\n lineSpacing,\n placeholder,\n extensions,\n outputFormat,\n showSource,\n _initialHtml,\n _onInput,\n _sourceValue,\n _onSourceInput,\n } = useEditor();\n // Tracks which seed HTML is currently in the contentEditable. Seeding on the\n // seed VALUE (not a one-shot boolean) re-seeds when the source view rewrites\n // `value`, while still never re-seeding on plain keystrokes — typing leaves\n // `_initialHtml` untouched, so `seededHtml.current === _initialHtml` holds.\n const seededHtml = useRef<string | null>(null);\n\n useEffect(() => {\n // Source view replaces the contentEditable in the tree, so it unmounts.\n // Forget what we seeded — the div that returns is a fresh, empty one and\n // must always be re-seeded, even if `value` was left untouched.\n if (showSource) {\n seededHtml.current = null;\n return;\n }\n const el = contentRef.current;\n if (el && seededHtml.current !== _initialHtml) {\n el.innerHTML = _initialHtml;\n seededHtml.current = _initialHtml;\n }\n }, [contentRef, _initialHtml, showSource]);\n\n const extClasses = useMemo(\n () => extensionEditorClasses(extensions),\n [extensions],\n );\n\n if (showSource) {\n // Raw-source view — a syntax-highlighted code surface (HTML highlighted;\n // markdown/plain un-highlighted), built on the shared fancy-file-commons\n // primitives. It's a DIRECT child of the flex-column editor root (no\n // wrapper — a nested flex box collapses the fill), so `flex-auto` makes it\n // fill the editor body when height-constrained and grow with content\n // (≥ minHeight) when not. `maxHeight` caps + scrolls.\n return (\n <CodeView\n value={_sourceValue}\n onChange={_onSourceInput}\n language={outputFormat === \"html\" ? \"html\" : \"plaintext\"}\n placeholder={placeholder}\n maxHeight={maxHeight}\n className={cn(\"min-h-0 flex-auto\", sourceClassName)}\n />\n );\n }\n\n return (\n <div\n ref={contentRef}\n contentEditable\n suppressContentEditableWarning\n data-react-fancy-editor-content=\"\"\n data-placeholder={placeholder}\n onInput={_onInput}\n style={{\n lineHeight: lineSpacing,\n maxHeight: maxHeight ? `${maxHeight}px` : undefined,\n }}\n className={cn(\n \"min-h-[120px] flex-auto overflow-y-auto px-4 py-3 text-sm outline-none\",\n \"focus:outline-none\",\n proseClasses,\n extClasses,\n \"empty:before:content-[attr(data-placeholder)] empty:before:text-zinc-400 empty:before:pointer-events-none\",\n className,\n )}\n />\n );\n}\n\nEditorContent.displayName = \"EditorContent\";\n","import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { marked } from \"marked\";\nimport { cn } from \"../../utils/cn\";\nimport { sanitizeHtml } from \"../../utils/sanitize\";\nimport { useControllableState } from \"../../hooks\";\nimport { EditorContext } from \"./Editor.context\";\nimport { EditorToolbar } from \"./EditorToolbar\";\nimport { EditorToolbarSeparator } from \"./EditorToolbarSeparator\";\nimport { EditorSourceToggle } from \"./EditorSourceToggle\";\nimport { EditorContent } from \"./EditorContent\";\nimport { htmlToMarkdown, detectFormat } from \"./editor.utils\";\nimport { mergeExtensions } from \"../ContentRenderer/extensions\";\nimport { ContentRenderer } from \"../ContentRenderer/ContentRenderer\";\nimport { useFieldMode } from \"../inputs/mode/FieldMode.context\";\nimport type { EditorProps } from \"./Editor.types\";\n\nfunction toHtml(\n value: string,\n outputFormat: \"html\" | \"markdown\",\n unsafe: boolean,\n valueFormat: \"markdown\" | \"html\" | \"auto\" = \"auto\",\n): string {\n if (!value) return \"\";\n const raw = (() => {\n // An explicit valueFormat is authoritative — the sniff misclassifies\n // markdown that merely MENTIONS HTML-ish snippets (`<code>`, `<table`),\n // collapsing it into an innerHTML wall of text (#10).\n const format =\n valueFormat !== \"auto\"\n ? valueFormat\n : outputFormat === \"html\"\n ? \"html\"\n : detectFormat(value);\n if (format === \"html\") return value;\n return (marked.parse(value, { async: false }) as string).trim();\n })();\n return unsafe ? raw : sanitizeHtml(raw);\n}\n\nfunction EditorRoot({\n children,\n className,\n value: controlledValue,\n defaultValue = \"\",\n onChange,\n outputFormat = \"html\",\n valueFormat = \"auto\",\n lineSpacing = 1.6,\n placeholder,\n mode: modeProp,\n extensions: instanceExtensions,\n unsafe = false,\n showSource: showSourceProp,\n defaultShowSource = false,\n onShowSourceChange,\n}: EditorProps) {\n const contentRef = useRef<HTMLDivElement | null>(null);\n const [value, setValue] = useControllableState(controlledValue, defaultValue, onChange);\n const [showSource, setShowSource] = useControllableState(\n showSourceProp,\n defaultShowSource,\n onShowSourceChange,\n );\n // View/edit mode — same resolution as the inputs: prop → <Form> context → \"edit\".\n const mode = useFieldMode(modeProp);\n const isView = mode === \"view\";\n\n // The value this editor last emitted through `onChange`. An incoming `value`\n // equal to it is our own keystroke echoing back; anything else came from\n // OUTSIDE — an async load, a host reset, a form repopulating.\n const lastEmitted = useRef<string | null>(null);\n\n // Bumped only on an external `value` change, so the seed memo below recomputes\n // for those and stays put while the user types. Without this, `value` was\n // excluded from the deps to avoid re-seeding on keystrokes, which also meant a\n // value arriving after mount was silently dropped and the editor stayed blank\n // forever (#15).\n const [externalSeed, setExternalSeed] = useState(0);\n useEffect(() => {\n if (value !== lastEmitted.current) setExternalSeed((n) => n + 1);\n }, [value]);\n\n const initialHtml = useMemo(\n () => toHtml(value, outputFormat, unsafe, valueFormat),\n // Seed the contentEditable from the LIVE value when (re)entering edit mode —\n // and when returning from the raw-source view, which may have rewritten the\n // value out from under the rich-text surface. `value` itself stays out of\n // the deps so typing never re-seeds (which would fight the caret);\n // `externalSeed` covers the externally-driven case instead.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [isView, outputFormat, unsafe, valueFormat, showSource, externalSeed],\n );\n\n const extensions = useMemo(\n () => mergeExtensions(instanceExtensions),\n [instanceExtensions],\n );\n\n const getOutputValue = useCallback(\n (html: string): string => {\n return outputFormat === \"markdown\" ? htmlToMarkdown(html) : html;\n },\n [outputFormat],\n );\n\n const handleInput = useCallback(() => {\n const el = contentRef.current;\n if (!el) return;\n const next = getOutputValue(el.innerHTML);\n // Record what we emitted BEFORE publishing it, so the value coming back in\n // is recognised as our own and does not trigger a re-seed mid-keystroke.\n lastEmitted.current = next;\n setValue(next);\n }, [setValue, getOutputValue]);\n\n const exec = useCallback(\n (command: string, arg?: string) => {\n const el = contentRef.current;\n if (!el) return;\n el.focus();\n document.execCommand(command, false, arg);\n handleInput();\n },\n [handleInput],\n );\n\n const insertText = useCallback(\n (text: string) => {\n const el = contentRef.current;\n if (!el) return;\n el.focus();\n document.execCommand(\"insertText\", false, text);\n handleInput();\n },\n [handleInput],\n );\n\n // The source textarea edits `value` directly — it is already the canonical\n // source in `outputFormat` (html or markdown), so no conversion round-trip.\n const handleSourceInput = useCallback(\n (next: string) => {\n // Also an internal emission — record it so typing in the source view is\n // not mistaken for an external change on every keystroke.\n lastEmitted.current = next;\n setValue(next);\n },\n [setValue],\n );\n\n const wrapSelection = useCallback(\n (before: string, after: string) => {\n const sel = window.getSelection();\n if (!sel || sel.rangeCount === 0) return;\n const range = sel.getRangeAt(0);\n const selectedText = range.toString();\n const el = contentRef.current;\n if (!el) return;\n el.focus();\n document.execCommand(\"insertText\", false, `${before}${selectedText}${after}`);\n handleInput();\n },\n [handleInput],\n );\n\n const contextValue = {\n contentRef,\n exec,\n insertText,\n wrapSelection,\n outputFormat,\n lineSpacing,\n placeholder,\n extensions,\n showSource,\n setShowSource,\n _initialHtml: initialHtml,\n _onInput: handleInput,\n _sourceValue: value,\n _onSourceInput: handleSourceInput,\n };\n\n // View mode — render the value read-only through ContentRenderer, matching the\n // editor's own format (markdown/html), instead of the editable toolbar+content\n // tree. Driven by `mode` / a `<Form mode=\"view\">`, exactly like the inputs.\n if (isView) {\n return (\n <div\n data-react-fancy-editor=\"\"\n data-mode=\"view\"\n className={cn(\n \"overflow-hidden rounded-xl border border-[color:var(--fancy-border,var(--color-zinc-200))] bg-[color:var(--fancy-surface,var(--color-white))] dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))] dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]\",\n className,\n )}\n >\n <ContentRenderer\n value={value}\n format={valueFormat !== \"auto\" ? valueFormat : outputFormat}\n lineSpacing={lineSpacing}\n extensions={instanceExtensions}\n unsafe={unsafe}\n className=\"px-4 py-3\"\n />\n </div>\n );\n }\n\n return (\n <EditorContext.Provider value={contextValue}>\n <div\n data-react-fancy-editor=\"\"\n data-mode=\"edit\"\n className={cn(\n \"flex flex-col overflow-hidden rounded-xl border border-[color:var(--fancy-border,var(--color-zinc-200))] bg-[color:var(--fancy-surface,var(--color-white))] dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))] dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]\",\n className,\n )}\n >\n {children}\n </div>\n </EditorContext.Provider>\n );\n}\n\nconst ToolbarWithSeparator = Object.assign(EditorToolbar, {\n Separator: EditorToolbarSeparator,\n});\n\nexport const Editor = Object.assign(EditorRoot, {\n Toolbar: ToolbarWithSeparator,\n Content: EditorContent,\n SourceToggle: EditorSourceToggle,\n});\n"]}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkZU5Q77OT_cjs = require('./chunk-ZU5Q77OT.cjs');
|
|
4
|
+
var react = require('react');
|
|
5
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
6
|
+
|
|
7
|
+
function clamp(n, lo, hi) {
|
|
8
|
+
return Math.min(hi, Math.max(lo, n));
|
|
9
|
+
}
|
|
10
|
+
function tidy(n) {
|
|
11
|
+
return Math.round(n * 100) / 100;
|
|
12
|
+
}
|
|
13
|
+
var SEPARATOR_BASE = "relative shrink-0 touch-none select-none outline-none transition-colors bg-[color:var(--fancy-border,var(--color-zinc-200))] dark:bg-[color:var(--fancy-border-dark,var(--color-zinc-700))] focus-visible:bg-[color:var(--fancy-focus,var(--color-blue-500))] dark:focus-visible:bg-[color:var(--fancy-focus-dark,var(--color-blue-400))] focus-visible:ring-2 focus-visible:ring-[color:var(--fancy-focus,var(--color-blue-500))]/40 dark:focus-visible:ring-[color:var(--fancy-focus-dark,var(--color-blue-400))]/40";
|
|
14
|
+
var SEPARATOR_ACTIVE = "hover:bg-[color:var(--fancy-focus,var(--color-blue-500))] dark:hover:bg-[color:var(--fancy-focus-dark,var(--color-blue-400))]";
|
|
15
|
+
var ResizablePanels = react.forwardRef(
|
|
16
|
+
({
|
|
17
|
+
children,
|
|
18
|
+
value,
|
|
19
|
+
defaultValue = 50,
|
|
20
|
+
onValueChange,
|
|
21
|
+
onValueCommit,
|
|
22
|
+
min = 10,
|
|
23
|
+
max = 90,
|
|
24
|
+
orientation = "horizontal",
|
|
25
|
+
step = 5,
|
|
26
|
+
largeStep = 20,
|
|
27
|
+
label = "Resize panels",
|
|
28
|
+
disabled = false,
|
|
29
|
+
className,
|
|
30
|
+
...props
|
|
31
|
+
}, forwardedRef) => {
|
|
32
|
+
const lo = clamp(min, 0, 100);
|
|
33
|
+
const hi = Math.max(lo, clamp(max, 0, 100));
|
|
34
|
+
const [internal, setInternal] = react.useState(defaultValue);
|
|
35
|
+
const controlled = value !== void 0;
|
|
36
|
+
const current = tidy(clamp(controlled ? value : internal, lo, hi));
|
|
37
|
+
const latest = react.useRef(current);
|
|
38
|
+
latest.current = current;
|
|
39
|
+
const rootRef = react.useRef(null);
|
|
40
|
+
const setRefs = react.useCallback(
|
|
41
|
+
(node) => {
|
|
42
|
+
rootRef.current = node;
|
|
43
|
+
if (typeof forwardedRef === "function") forwardedRef(node);
|
|
44
|
+
else if (forwardedRef) forwardedRef.current = node;
|
|
45
|
+
},
|
|
46
|
+
[forwardedRef]
|
|
47
|
+
);
|
|
48
|
+
const firstId = react.useId();
|
|
49
|
+
const horizontal = orientation === "horizontal";
|
|
50
|
+
const request = react.useCallback(
|
|
51
|
+
(next) => {
|
|
52
|
+
const v = tidy(clamp(next, lo, hi));
|
|
53
|
+
if (v === latest.current) return;
|
|
54
|
+
latest.current = v;
|
|
55
|
+
if (!controlled) setInternal(v);
|
|
56
|
+
onValueChange?.(v);
|
|
57
|
+
},
|
|
58
|
+
[controlled, hi, lo, onValueChange]
|
|
59
|
+
);
|
|
60
|
+
const keyChanged = react.useRef(false);
|
|
61
|
+
const onKeyDown = (e) => {
|
|
62
|
+
if (disabled) return;
|
|
63
|
+
const dec = horizontal ? "ArrowLeft" : "ArrowUp";
|
|
64
|
+
const inc = horizontal ? "ArrowRight" : "ArrowDown";
|
|
65
|
+
const by = e.shiftKey ? largeStep : step;
|
|
66
|
+
let next;
|
|
67
|
+
if (e.key === dec) next = latest.current - by;
|
|
68
|
+
else if (e.key === inc) next = latest.current + by;
|
|
69
|
+
else if (e.key === "Home") next = lo;
|
|
70
|
+
else if (e.key === "End") next = hi;
|
|
71
|
+
else return;
|
|
72
|
+
e.preventDefault();
|
|
73
|
+
const before = latest.current;
|
|
74
|
+
request(next);
|
|
75
|
+
if (latest.current !== before) keyChanged.current = true;
|
|
76
|
+
};
|
|
77
|
+
const onKeyUp = () => {
|
|
78
|
+
if (!keyChanged.current) return;
|
|
79
|
+
keyChanged.current = false;
|
|
80
|
+
onValueCommit?.(latest.current);
|
|
81
|
+
};
|
|
82
|
+
const drag = react.useRef(null);
|
|
83
|
+
const [dragging, setDragging] = react.useState(false);
|
|
84
|
+
const valueAt = (clientX, clientY) => {
|
|
85
|
+
const rect = rootRef.current?.getBoundingClientRect();
|
|
86
|
+
if (!rect) return null;
|
|
87
|
+
const size = horizontal ? rect.width : rect.height;
|
|
88
|
+
if (!size) return null;
|
|
89
|
+
const offset = horizontal ? clientX - rect.left : clientY - rect.top;
|
|
90
|
+
return offset / size * 100;
|
|
91
|
+
};
|
|
92
|
+
const onPointerDown = (e) => {
|
|
93
|
+
if (disabled || e.button !== 0) return;
|
|
94
|
+
e.preventDefault();
|
|
95
|
+
e.currentTarget.setPointerCapture?.(e.pointerId);
|
|
96
|
+
drag.current = { pointerId: e.pointerId, start: latest.current };
|
|
97
|
+
setDragging(true);
|
|
98
|
+
};
|
|
99
|
+
const onPointerMove = (e) => {
|
|
100
|
+
if (!drag.current || drag.current.pointerId !== e.pointerId) return;
|
|
101
|
+
const v = valueAt(e.clientX, e.clientY);
|
|
102
|
+
if (v !== null) request(v);
|
|
103
|
+
};
|
|
104
|
+
const endDrag = (e) => {
|
|
105
|
+
const d = drag.current;
|
|
106
|
+
if (!d || d.pointerId !== e.pointerId) return;
|
|
107
|
+
drag.current = null;
|
|
108
|
+
setDragging(false);
|
|
109
|
+
if (e.currentTarget.hasPointerCapture?.(e.pointerId)) {
|
|
110
|
+
e.currentTarget.releasePointerCapture?.(e.pointerId);
|
|
111
|
+
}
|
|
112
|
+
if (latest.current !== d.start) onValueCommit?.(latest.current);
|
|
113
|
+
};
|
|
114
|
+
const items = react.Children.toArray(children);
|
|
115
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
116
|
+
"div",
|
|
117
|
+
{
|
|
118
|
+
ref: setRefs,
|
|
119
|
+
...props,
|
|
120
|
+
"data-react-fancy-resizable-panels": "",
|
|
121
|
+
"data-orientation": orientation,
|
|
122
|
+
"data-dragging": dragging ? "" : void 0,
|
|
123
|
+
"data-disabled": disabled ? "" : void 0,
|
|
124
|
+
className: chunkZU5Q77OT_cjs.cn(
|
|
125
|
+
"flex h-full w-full min-h-0 min-w-0 overflow-hidden",
|
|
126
|
+
horizontal ? "flex-row" : "flex-col",
|
|
127
|
+
dragging && (horizontal ? "cursor-col-resize select-none" : "cursor-row-resize select-none"),
|
|
128
|
+
className
|
|
129
|
+
),
|
|
130
|
+
children: [
|
|
131
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
132
|
+
"div",
|
|
133
|
+
{
|
|
134
|
+
id: firstId,
|
|
135
|
+
"data-react-fancy-resizable-panels-panel": "first",
|
|
136
|
+
className: "min-h-0 min-w-0 overflow-auto",
|
|
137
|
+
style: { flexBasis: `${current}%`, flexGrow: 0, flexShrink: 0 },
|
|
138
|
+
children: items[0]
|
|
139
|
+
}
|
|
140
|
+
),
|
|
141
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
142
|
+
"div",
|
|
143
|
+
{
|
|
144
|
+
role: "separator",
|
|
145
|
+
"data-react-fancy-resizable-panels-separator": "",
|
|
146
|
+
"aria-orientation": horizontal ? "vertical" : "horizontal",
|
|
147
|
+
"aria-valuenow": current,
|
|
148
|
+
"aria-valuemin": lo,
|
|
149
|
+
"aria-valuemax": hi,
|
|
150
|
+
"aria-controls": firstId,
|
|
151
|
+
"aria-label": label,
|
|
152
|
+
"aria-disabled": disabled || void 0,
|
|
153
|
+
tabIndex: disabled ? -1 : 0,
|
|
154
|
+
onKeyDown,
|
|
155
|
+
onKeyUp,
|
|
156
|
+
onPointerDown,
|
|
157
|
+
onPointerMove,
|
|
158
|
+
onPointerUp: endDrag,
|
|
159
|
+
onPointerCancel: endDrag,
|
|
160
|
+
onLostPointerCapture: endDrag,
|
|
161
|
+
className: chunkZU5Q77OT_cjs.cn(
|
|
162
|
+
SEPARATOR_BASE,
|
|
163
|
+
// A 1px line with a 12px hit area, so it is easy to grab by touch.
|
|
164
|
+
horizontal ? "w-px before:absolute before:inset-y-0 before:-left-1.5 before:-right-1.5" : "h-px before:absolute before:inset-x-0 before:-top-1.5 before:-bottom-1.5",
|
|
165
|
+
disabled ? "cursor-default opacity-50" : chunkZU5Q77OT_cjs.cn(SEPARATOR_ACTIVE, horizontal ? "cursor-col-resize" : "cursor-row-resize")
|
|
166
|
+
)
|
|
167
|
+
}
|
|
168
|
+
),
|
|
169
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
170
|
+
"div",
|
|
171
|
+
{
|
|
172
|
+
"data-react-fancy-resizable-panels-panel": "second",
|
|
173
|
+
className: "min-h-0 min-w-0 flex-1 overflow-auto",
|
|
174
|
+
children: items[1]
|
|
175
|
+
}
|
|
176
|
+
)
|
|
177
|
+
]
|
|
178
|
+
}
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
);
|
|
182
|
+
ResizablePanels.displayName = "ResizablePanels";
|
|
183
|
+
|
|
184
|
+
exports.ResizablePanels = ResizablePanels;
|
|
185
|
+
//# sourceMappingURL=chunk-CEFK54WP.cjs.map
|
|
186
|
+
//# sourceMappingURL=chunk-CEFK54WP.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/ResizablePanels/ResizablePanels.tsx"],"names":["forwardRef","useState","useRef","useCallback","useId","Children","jsxs","cn","jsx"],"mappings":"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","file":"chunk-CEFK54WP.cjs","sourcesContent":["import { Children, forwardRef, useCallback, useId, useRef, useState } from \"react\";\nimport type { KeyboardEvent, PointerEvent } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport type { ResizablePanelsProps } from \"./ResizablePanels.types\";\n\nfunction clamp(n: number, lo: number, hi: number): number {\n return Math.min(hi, Math.max(lo, n));\n}\n\n/** Two decimals: precise enough for a pixel on a 4K row, tidy in JSON. */\nfunction tidy(n: number): number {\n return Math.round(n * 100) / 100;\n}\n\nconst SEPARATOR_BASE =\n \"relative shrink-0 touch-none select-none outline-none transition-colors \" +\n \"bg-[color:var(--fancy-border,var(--color-zinc-200))] dark:bg-[color:var(--fancy-border-dark,var(--color-zinc-700))] \" +\n \"focus-visible:bg-[color:var(--fancy-focus,var(--color-blue-500))] dark:focus-visible:bg-[color:var(--fancy-focus-dark,var(--color-blue-400))] \" +\n \"focus-visible:ring-2 focus-visible:ring-[color:var(--fancy-focus,var(--color-blue-500))]/40 dark:focus-visible:ring-[color:var(--fancy-focus-dark,var(--color-blue-400))]/40\";\n\nconst SEPARATOR_ACTIVE =\n \"hover:bg-[color:var(--fancy-focus,var(--color-blue-500))] dark:hover:bg-[color:var(--fancy-focus-dark,var(--color-blue-400))]\";\n\n/**\n * Two panels and a draggable, keyboard-operable separator between them — the\n * W3C window-splitter pattern\n * (https://www.w3.org/WAI/ARIA/apg/patterns/windowsplitter/).\n *\n * `value` is the PRIMARY (first) panel's share of the container, as a\n * percentage. The separator is `role=\"separator\"` with `aria-valuenow/min/max`,\n * `aria-controls` pointing at the primary panel, and a tab stop. Arrow keys\n * along the drag axis move it by `step`, Shift + Arrow by `largeStep`, Home /\n * End jump to `min` / `max`. Pointer and touch drags use pointer capture, so a\n * fast drag that leaves the bar keeps resizing.\n *\n * `onValueChange` fires continuously; `onValueCommit` fires once per gesture\n * (pointer release, key release) so a host can persist a per-user size without\n * writing on every frame. The host owns persistence.\n */\nexport const ResizablePanels = forwardRef<HTMLDivElement, ResizablePanelsProps>(\n (\n {\n children,\n value,\n defaultValue = 50,\n onValueChange,\n onValueCommit,\n min = 10,\n max = 90,\n orientation = \"horizontal\",\n step = 5,\n largeStep = 20,\n label = \"Resize panels\",\n disabled = false,\n className,\n ...props\n },\n forwardedRef,\n ) => {\n // Never past the container, whatever min/max say.\n const lo = clamp(min, 0, 100);\n const hi = Math.max(lo, clamp(max, 0, 100));\n\n const [internal, setInternal] = useState(defaultValue);\n const controlled = value !== undefined;\n const current = tidy(clamp(controlled ? value : internal, lo, hi));\n\n /*\n * The value most recently ASKED FOR, re-synced to what is rendered on every\n * render. Several key repeats can land before React re-renders; reading the\n * render-time value would make each of them compute from the same start.\n */\n const latest = useRef(current);\n latest.current = current;\n\n const rootRef = useRef<HTMLDivElement | null>(null);\n const setRefs = useCallback(\n (node: HTMLDivElement | null) => {\n rootRef.current = node;\n if (typeof forwardedRef === \"function\") forwardedRef(node);\n else if (forwardedRef) forwardedRef.current = node;\n },\n [forwardedRef],\n );\n\n const firstId = useId();\n const horizontal = orientation === \"horizontal\";\n\n const request = useCallback(\n (next: number) => {\n const v = tidy(clamp(next, lo, hi));\n if (v === latest.current) return;\n latest.current = v;\n if (!controlled) setInternal(v);\n onValueChange?.(v);\n },\n [controlled, hi, lo, onValueChange],\n );\n\n // --- keyboard -----------------------------------------------------------\n\n const keyChanged = useRef(false);\n\n const onKeyDown = (e: KeyboardEvent<HTMLDivElement>) => {\n if (disabled) return;\n const dec = horizontal ? \"ArrowLeft\" : \"ArrowUp\";\n const inc = horizontal ? \"ArrowRight\" : \"ArrowDown\";\n const by = e.shiftKey ? largeStep : step;\n let next: number;\n if (e.key === dec) next = latest.current - by;\n else if (e.key === inc) next = latest.current + by;\n else if (e.key === \"Home\") next = lo;\n else if (e.key === \"End\") next = hi;\n else return;\n e.preventDefault();\n const before = latest.current;\n request(next);\n if (latest.current !== before) keyChanged.current = true;\n };\n\n const onKeyUp = () => {\n if (!keyChanged.current) return;\n keyChanged.current = false;\n onValueCommit?.(latest.current);\n };\n\n // --- pointer ------------------------------------------------------------\n\n const drag = useRef<{ pointerId: number; start: number } | null>(null);\n const [dragging, setDragging] = useState(false);\n\n const valueAt = (clientX: number, clientY: number): number | null => {\n const rect = rootRef.current?.getBoundingClientRect();\n if (!rect) return null;\n const size = horizontal ? rect.width : rect.height;\n if (!size) return null;\n const offset = horizontal ? clientX - rect.left : clientY - rect.top;\n return (offset / size) * 100;\n };\n\n const onPointerDown = (e: PointerEvent<HTMLDivElement>) => {\n if (disabled || e.button !== 0) return;\n e.preventDefault();\n e.currentTarget.setPointerCapture?.(e.pointerId);\n drag.current = { pointerId: e.pointerId, start: latest.current };\n setDragging(true);\n };\n\n const onPointerMove = (e: PointerEvent<HTMLDivElement>) => {\n if (!drag.current || drag.current.pointerId !== e.pointerId) return;\n const v = valueAt(e.clientX, e.clientY);\n if (v !== null) request(v);\n };\n\n const endDrag = (e: PointerEvent<HTMLDivElement>) => {\n const d = drag.current;\n if (!d || d.pointerId !== e.pointerId) return;\n drag.current = null;\n setDragging(false);\n if (e.currentTarget.hasPointerCapture?.(e.pointerId)) {\n e.currentTarget.releasePointerCapture?.(e.pointerId);\n }\n if (latest.current !== d.start) onValueCommit?.(latest.current);\n };\n\n const items = Children.toArray(children);\n\n return (\n <div\n ref={setRefs}\n {...props}\n data-react-fancy-resizable-panels=\"\"\n data-orientation={orientation}\n data-dragging={dragging ? \"\" : undefined}\n data-disabled={disabled ? \"\" : undefined}\n className={cn(\n \"flex h-full w-full min-h-0 min-w-0 overflow-hidden\",\n horizontal ? \"flex-row\" : \"flex-col\",\n dragging && (horizontal ? \"cursor-col-resize select-none\" : \"cursor-row-resize select-none\"),\n className,\n )}\n >\n <div\n id={firstId}\n data-react-fancy-resizable-panels-panel=\"first\"\n className=\"min-h-0 min-w-0 overflow-auto\"\n style={{ flexBasis: `${current}%`, flexGrow: 0, flexShrink: 0 }}\n >\n {items[0]}\n </div>\n <div\n role=\"separator\"\n data-react-fancy-resizable-panels-separator=\"\"\n aria-orientation={horizontal ? \"vertical\" : \"horizontal\"}\n aria-valuenow={current}\n aria-valuemin={lo}\n aria-valuemax={hi}\n aria-controls={firstId}\n aria-label={label}\n aria-disabled={disabled || undefined}\n tabIndex={disabled ? -1 : 0}\n onKeyDown={onKeyDown}\n onKeyUp={onKeyUp}\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={endDrag}\n onPointerCancel={endDrag}\n onLostPointerCapture={endDrag}\n className={cn(\n SEPARATOR_BASE,\n // A 1px line with a 12px hit area, so it is easy to grab by touch.\n horizontal\n ? \"w-px before:absolute before:inset-y-0 before:-left-1.5 before:-right-1.5\"\n : \"h-px before:absolute before:inset-x-0 before:-top-1.5 before:-bottom-1.5\",\n disabled\n ? \"cursor-default opacity-50\"\n : cn(SEPARATOR_ACTIVE, horizontal ? \"cursor-col-resize\" : \"cursor-row-resize\"),\n )}\n />\n <div\n data-react-fancy-resizable-panels-panel=\"second\"\n className=\"min-h-0 min-w-0 flex-1 overflow-auto\"\n >\n {items[1]}\n </div>\n </div>\n );\n },\n);\n\nResizablePanels.displayName = \"ResizablePanels\";\n"]}
|