@harryy/react-file-browser 1.4.0 → 1.5.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 +85 -26
- package/dist/chunks/file-types-DZ2qAUnj.js +331 -0
- package/dist/chunks/file-types-DZ2qAUnj.js.map +1 -0
- package/dist/chunks/list-BwYAbqhl.js +31 -0
- package/dist/chunks/list-BwYAbqhl.js.map +1 -0
- package/dist/chunks/spreadsheet-GqF9DCoj.js +118 -0
- package/dist/chunks/spreadsheet-GqF9DCoj.js.map +1 -0
- package/dist/components/file-browser.d.ts +11 -3
- package/dist/components/file-browser.d.ts.map +1 -1
- package/dist/components/file-plugins.d.ts +64 -0
- package/dist/components/file-plugins.d.ts.map +1 -0
- package/dist/components/file-types.d.ts +12 -0
- package/dist/components/file-types.d.ts.map +1 -0
- package/dist/components/file-view.d.ts +25 -0
- package/dist/components/file-view.d.ts.map +1 -0
- package/dist/core/use-file-browser.d.ts +0 -28
- package/dist/core/use-file-browser.d.ts.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2196 -2130
- package/dist/index.js.map +1 -1
- package/dist/plugins/code/index.d.ts +26 -0
- package/dist/plugins/code/index.d.ts.map +1 -0
- package/dist/plugins/code/index.js +215 -0
- package/dist/plugins/code/index.js.map +1 -0
- package/dist/plugins/csv/index.d.ts +6 -0
- package/dist/plugins/csv/index.d.ts.map +1 -0
- package/dist/plugins/csv/index.js +48 -0
- package/dist/plugins/csv/index.js.map +1 -0
- package/dist/plugins/docx/index.d.ts +4 -0
- package/dist/plugins/docx/index.d.ts.map +1 -0
- package/dist/plugins/docx/index.js +40 -0
- package/dist/plugins/docx/index.js.map +1 -0
- package/dist/plugins/markdown/index.d.ts +13 -0
- package/dist/plugins/markdown/index.d.ts.map +1 -0
- package/dist/plugins/markdown/index.js +292 -0
- package/dist/plugins/markdown/index.js.map +1 -0
- package/dist/plugins/rich-text.d.ts +10 -0
- package/dist/plugins/rich-text.d.ts.map +1 -0
- package/dist/plugins/spreadsheet.d.ts +20 -0
- package/dist/plugins/spreadsheet.d.ts.map +1 -0
- package/dist/plugins/xlsx/index.d.ts +6 -0
- package/dist/plugins/xlsx/index.d.ts.map +1 -0
- package/dist/plugins/xlsx/index.js +84 -0
- package/dist/plugins/xlsx/index.js.map +1 -0
- package/package.json +86 -3
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import { i as e, l as t } from "../../chunks/file-types-DZ2qAUnj.js";
|
|
2
|
+
import { t as n } from "../../chunks/list-BwYAbqhl.js";
|
|
3
|
+
import { useEffect as r, useRef as i, useState as a } from "react";
|
|
4
|
+
import { jsx as o, jsxs as s } from "react/jsx-runtime";
|
|
5
|
+
import c from "react-markdown";
|
|
6
|
+
import { marked as l } from "marked";
|
|
7
|
+
import u from "turndown";
|
|
8
|
+
import d from "dompurify";
|
|
9
|
+
import f from "squire-rte";
|
|
10
|
+
var p = t("bold", [["path", {
|
|
11
|
+
d: "M6 12h9a4 4 0 0 1 0 8H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h7a4 4 0 0 1 0 8",
|
|
12
|
+
key: "mg9rjx"
|
|
13
|
+
}]]), m = t("code", [["path", {
|
|
14
|
+
d: "m16 18 6-6-6-6",
|
|
15
|
+
key: "eg8j8"
|
|
16
|
+
}], ["path", {
|
|
17
|
+
d: "m8 6-6 6 6 6",
|
|
18
|
+
key: "ppft3o"
|
|
19
|
+
}]]), h = t("heading-2", [
|
|
20
|
+
["path", {
|
|
21
|
+
d: "M4 12h8",
|
|
22
|
+
key: "17cfdx"
|
|
23
|
+
}],
|
|
24
|
+
["path", {
|
|
25
|
+
d: "M4 18V6",
|
|
26
|
+
key: "1rz3zl"
|
|
27
|
+
}],
|
|
28
|
+
["path", {
|
|
29
|
+
d: "M12 18V6",
|
|
30
|
+
key: "zqpxq5"
|
|
31
|
+
}],
|
|
32
|
+
["path", {
|
|
33
|
+
d: "M21 18h-4c0-4 4-3 4-6 0-1.5-2-2.5-4-1",
|
|
34
|
+
key: "9jr5yi"
|
|
35
|
+
}]
|
|
36
|
+
]), g = t("italic", [
|
|
37
|
+
["line", {
|
|
38
|
+
x1: "19",
|
|
39
|
+
x2: "10",
|
|
40
|
+
y1: "4",
|
|
41
|
+
y2: "4",
|
|
42
|
+
key: "15jd3p"
|
|
43
|
+
}],
|
|
44
|
+
["line", {
|
|
45
|
+
x1: "14",
|
|
46
|
+
x2: "5",
|
|
47
|
+
y1: "20",
|
|
48
|
+
y2: "20",
|
|
49
|
+
key: "bu0au3"
|
|
50
|
+
}],
|
|
51
|
+
["line", {
|
|
52
|
+
x1: "15",
|
|
53
|
+
x2: "9",
|
|
54
|
+
y1: "4",
|
|
55
|
+
y2: "20",
|
|
56
|
+
key: "uljnxc"
|
|
57
|
+
}]
|
|
58
|
+
]), _ = t("link", [["path", {
|
|
59
|
+
d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71",
|
|
60
|
+
key: "1cjeqo"
|
|
61
|
+
}], ["path", {
|
|
62
|
+
d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71",
|
|
63
|
+
key: "19qd67"
|
|
64
|
+
}]]), v = t("list-ordered", [
|
|
65
|
+
["path", {
|
|
66
|
+
d: "M11 5h10",
|
|
67
|
+
key: "1cz7ny"
|
|
68
|
+
}],
|
|
69
|
+
["path", {
|
|
70
|
+
d: "M11 12h10",
|
|
71
|
+
key: "1438ji"
|
|
72
|
+
}],
|
|
73
|
+
["path", {
|
|
74
|
+
d: "M11 19h10",
|
|
75
|
+
key: "11t30w"
|
|
76
|
+
}],
|
|
77
|
+
["path", {
|
|
78
|
+
d: "M4 4h1v5",
|
|
79
|
+
key: "10yrso"
|
|
80
|
+
}],
|
|
81
|
+
["path", {
|
|
82
|
+
d: "M4 9h2",
|
|
83
|
+
key: "r1h2o0"
|
|
84
|
+
}],
|
|
85
|
+
["path", {
|
|
86
|
+
d: "M6.5 20H3.4c0-1 2.6-1.925 2.6-3.5a1.5 1.5 0 0 0-2.6-1.02",
|
|
87
|
+
key: "xtkcd5"
|
|
88
|
+
}]
|
|
89
|
+
]), y = [
|
|
90
|
+
{
|
|
91
|
+
id: "bold",
|
|
92
|
+
label: "Bold",
|
|
93
|
+
icon: p,
|
|
94
|
+
tag: "B",
|
|
95
|
+
run: (e) => b(e, "B", "bold", "removeBold")
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
id: "italic",
|
|
99
|
+
label: "Italic",
|
|
100
|
+
icon: g,
|
|
101
|
+
tag: "I",
|
|
102
|
+
run: (e) => b(e, "I", "italic", "removeItalic")
|
|
103
|
+
},
|
|
104
|
+
"|",
|
|
105
|
+
{
|
|
106
|
+
id: "heading",
|
|
107
|
+
label: "Heading",
|
|
108
|
+
icon: h,
|
|
109
|
+
tag: "H2",
|
|
110
|
+
run: x
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
id: "bullets",
|
|
114
|
+
label: "Bulleted list",
|
|
115
|
+
icon: n,
|
|
116
|
+
tag: "UL",
|
|
117
|
+
run: (e) => e.hasFormat("UL") ? e.removeList() : e.makeUnorderedList()
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
id: "numbers",
|
|
121
|
+
label: "Numbered list",
|
|
122
|
+
icon: v,
|
|
123
|
+
tag: "OL",
|
|
124
|
+
run: (e) => e.hasFormat("OL") ? e.removeList() : e.makeOrderedList()
|
|
125
|
+
},
|
|
126
|
+
"|",
|
|
127
|
+
{
|
|
128
|
+
id: "link",
|
|
129
|
+
label: "Link",
|
|
130
|
+
icon: _,
|
|
131
|
+
tag: "A",
|
|
132
|
+
run: () => void 0
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
id: "code",
|
|
136
|
+
label: "Code",
|
|
137
|
+
icon: m,
|
|
138
|
+
tag: "CODE",
|
|
139
|
+
run: (e) => e.toggleCode()
|
|
140
|
+
}
|
|
141
|
+
];
|
|
142
|
+
function b(e, t, n, r) {
|
|
143
|
+
e.hasFormat(t) ? e[r]() : e[n]();
|
|
144
|
+
}
|
|
145
|
+
function x(e) {
|
|
146
|
+
let t = !e.hasFormat("H2");
|
|
147
|
+
e.modifyBlocks((e) => {
|
|
148
|
+
for (let n of Array.from(e.children)) {
|
|
149
|
+
let e = document.createElement(t ? "h2" : "p");
|
|
150
|
+
e.append(...Array.from(n.childNodes)), n.replaceWith(e);
|
|
151
|
+
}
|
|
152
|
+
return e;
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
var S = "\n[data-fb-rich-text] .fb-rt-body {\n min-height: min(480px, 60dvh); max-height: min(640px, 65dvh); overflow: auto; padding: 16px 20px; outline: none;\n color: var(--fb-text); font: inherit; font-size: var(--fb-font); line-height: 1.6; overflow-wrap: anywhere;\n}\n[data-fb-rich-text] .fb-rt-body > * + * { margin-top: 10px; }\n[data-fb-rich-text] .fb-rt-body :is(p, div, ul, ol, pre, blockquote) { margin-bottom: 0; }\n[data-fb-rich-text] .fb-rt-body p { margin-top: 0; }\n[data-fb-rich-text] .fb-rt-body h1 { font-size: 22px; font-weight: 700; margin: 0; }\n[data-fb-rich-text] .fb-rt-body h2 { font-size: 18px; font-weight: 700; margin: 0; }\n[data-fb-rich-text] .fb-rt-body h3 { font-size: 16px; font-weight: 700; margin: 0; }\n[data-fb-rich-text] .fb-rt-body ul { list-style: disc; padding-left: 22px; }\n[data-fb-rich-text] .fb-rt-body ol { list-style: decimal; padding-left: 22px; }\n[data-fb-rich-text] .fb-rt-body a { color: var(--fb-accent); text-decoration: underline; }\n[data-fb-rich-text] .fb-rt-body code {\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.92em;\n background: var(--fb-surface-2); border-radius: 4px; padding: 1px 4px;\n}\n[data-fb-rich-text] .fb-rt-body pre {\n background: var(--fb-surface-2); border-radius: calc(var(--fb-radius) - 2px); padding: 10px 12px; overflow: auto;\n}\n[data-fb-rich-text] .fb-rt-body pre code { background: transparent; padding: 0; }\n[data-fb-rich-text] .fb-rt-body blockquote { border-left: 3px solid var(--fb-border-strong); padding-left: 10px; color: var(--fb-muted); margin-left: 0; }\n[data-fb-rich-text] .fb-rt-body img { max-width: 100%; }\n", C = "grid size-[30px] shrink-0 place-items-center rounded-[calc(var(--fb-radius)-2px)] text-[var(--fb-muted)] outline-none hover:bg-[var(--fb-surface)] hover:text-[var(--fb-text)] focus-visible:ring-[3px] focus-visible:ring-[color-mix(in_oklch,var(--fb-accent)_22%,transparent)] [@media(pointer:coarse)]:size-[calc(var(--fb-gap)*11)]";
|
|
156
|
+
function w({ html: e, label: t, onChange: n }) {
|
|
157
|
+
let c = i(null), l = i(null), u = i(n);
|
|
158
|
+
u.current = n;
|
|
159
|
+
let [p, m] = a(""), [h, g] = a(!1), [_, v] = a("https://");
|
|
160
|
+
r(() => {
|
|
161
|
+
let n = c.current;
|
|
162
|
+
if (!n) return;
|
|
163
|
+
let r = document.createElement("div");
|
|
164
|
+
r.className = "fb-rt-body", r.setAttribute("role", "textbox"), r.setAttribute("aria-multiline", "true"), r.setAttribute("aria-label", t), n.append(r);
|
|
165
|
+
let i = new f(r, {
|
|
166
|
+
blockTag: "P",
|
|
167
|
+
sanitizeToDOMFragment: (e) => d.sanitize(e, { RETURN_DOM_FRAGMENT: !0 })
|
|
168
|
+
});
|
|
169
|
+
return i.setHTML(e), l.current = i, i.addEventListener("input", () => u.current(i.getHTML())), i.addEventListener("pathChange", () => m(i.getPath())), () => {
|
|
170
|
+
i.destroy(), r.remove(), l.current = null;
|
|
171
|
+
};
|
|
172
|
+
}, []);
|
|
173
|
+
let b = (e) => {
|
|
174
|
+
let t = l.current;
|
|
175
|
+
if (t) {
|
|
176
|
+
if (e.id === "link") {
|
|
177
|
+
t.hasFormat("A") ? (t.removeLink(), u.current(t.getHTML())) : g(!0);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
e.run(t), t.focus(), u.current(t.getHTML());
|
|
181
|
+
}
|
|
182
|
+
}, x = () => {
|
|
183
|
+
let e = l.current, t = _.trim();
|
|
184
|
+
g(!1), !(!e || !/^(https?:|mailto:|\/|#)/i.test(t)) && (e.makeLink(t), e.focus(), u.current(e.getHTML()));
|
|
185
|
+
}, w = new Set(p.split(">").map((e) => e.split(/[.#[]/)[0].toUpperCase()));
|
|
186
|
+
return /* @__PURE__ */ s("div", {
|
|
187
|
+
className: "flex w-full min-w-0 flex-col overflow-hidden rounded-[var(--fb-radius)] border border-[var(--fb-border)] bg-[var(--fb-surface)] text-left",
|
|
188
|
+
"data-fb-rich-text": "",
|
|
189
|
+
ref: c,
|
|
190
|
+
children: [/* @__PURE__ */ o("style", { children: S }), /* @__PURE__ */ s("div", {
|
|
191
|
+
"aria-label": "Formatting",
|
|
192
|
+
className: "flex min-w-0 items-center gap-0.5 overflow-x-auto border-b border-[var(--fb-border)] bg-[var(--fb-surface-2)] p-1.5",
|
|
193
|
+
role: "toolbar",
|
|
194
|
+
children: [y.map((e, t) => e === "|" ? /* @__PURE__ */ o("span", {
|
|
195
|
+
"aria-hidden": "true",
|
|
196
|
+
className: "mx-1.5 h-[18px] w-px bg-[var(--fb-border)]"
|
|
197
|
+
}, `divider-${t}`) : /* @__PURE__ */ o("button", {
|
|
198
|
+
"aria-label": e.label,
|
|
199
|
+
"aria-pressed": w.has(e.tag),
|
|
200
|
+
className: `${C} ${w.has(e.tag) ? "bg-[var(--fb-accent-soft)] text-[var(--fb-text)]" : ""}`,
|
|
201
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
202
|
+
onClick: () => b(e),
|
|
203
|
+
title: e.label,
|
|
204
|
+
type: "button",
|
|
205
|
+
children: /* @__PURE__ */ o(e.icon, {
|
|
206
|
+
"aria-hidden": "true",
|
|
207
|
+
className: "size-4",
|
|
208
|
+
strokeWidth: 2
|
|
209
|
+
})
|
|
210
|
+
}, e.id)), h ? /* @__PURE__ */ s("form", {
|
|
211
|
+
className: "ml-2 flex min-w-0 items-center gap-1.5",
|
|
212
|
+
onSubmit: (e) => {
|
|
213
|
+
e.preventDefault(), x();
|
|
214
|
+
},
|
|
215
|
+
children: [/* @__PURE__ */ o("input", {
|
|
216
|
+
"aria-label": "Link address",
|
|
217
|
+
autoFocus: !0,
|
|
218
|
+
className: "h-[30px] w-[220px] min-w-0 rounded-[calc(var(--fb-radius)-2px)] border border-[var(--fb-border)] bg-[var(--fb-surface)] px-2 text-[calc(var(--fb-font)-1px)] text-[var(--fb-text)] outline-none focus:border-[var(--fb-accent)]",
|
|
219
|
+
onChange: (e) => v(e.target.value),
|
|
220
|
+
onKeyDown: (e) => {
|
|
221
|
+
e.key === "Escape" && (e.preventDefault(), e.stopPropagation(), g(!1));
|
|
222
|
+
},
|
|
223
|
+
value: _
|
|
224
|
+
}), /* @__PURE__ */ o("button", {
|
|
225
|
+
className: "h-[30px] rounded-[calc(var(--fb-radius)-2px)] bg-[var(--fb-accent)] px-3 text-[calc(var(--fb-font)-1px)] font-semibold text-[var(--fb-surface)]",
|
|
226
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
227
|
+
type: "submit",
|
|
228
|
+
children: "Add link"
|
|
229
|
+
})]
|
|
230
|
+
}) : null]
|
|
231
|
+
})]
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
//#endregion
|
|
235
|
+
//#region src/plugins/markdown/index.tsx
|
|
236
|
+
var T = (t) => [
|
|
237
|
+
"md",
|
|
238
|
+
"markdown",
|
|
239
|
+
"mdx"
|
|
240
|
+
].includes(e(t.name)) || t.mimeType === "text/markdown", E = [
|
|
241
|
+
"max-h-[min(640px,65dvh)] w-full overflow-auto rounded-[var(--fb-radius)] border border-[var(--fb-border)] bg-[var(--fb-surface)] p-5 text-left leading-relaxed text-[var(--fb-text)] [overflow-wrap:anywhere]",
|
|
242
|
+
"[&>*+*]:mt-3 [&_h1]:text-[22px] [&_h1]:font-bold [&_h2]:text-[19px] [&_h2]:font-bold [&_h3]:text-[16px] [&_h3]:font-bold",
|
|
243
|
+
"[&_h1]:m-0 [&_h2]:m-0 [&_h3]:m-0 [&_p]:m-0 [&_ul]:m-0 [&_ol]:m-0 [&_ul]:list-disc [&_ol]:list-decimal [&_ul]:pl-5 [&_ol]:pl-5",
|
|
244
|
+
"[&_a]:text-[var(--fb-accent)] [&_a]:underline [&_blockquote]:m-0 [&_blockquote]:border-l-[3px] [&_blockquote]:border-[var(--fb-border-strong)] [&_blockquote]:pl-3 [&_blockquote]:text-[var(--fb-muted)]",
|
|
245
|
+
"[&_code]:rounded-[4px] [&_code]:bg-[var(--fb-surface-2)] [&_code]:px-1 [&_code]:font-mono [&_code]:text-[0.92em]",
|
|
246
|
+
"[&_pre]:m-0 [&_pre]:overflow-auto [&_pre]:rounded-[calc(var(--fb-radius)-2px)] [&_pre]:bg-[var(--fb-surface-2)] [&_pre]:p-3 [&_pre_code]:bg-transparent [&_pre_code]:p-0",
|
|
247
|
+
"[&_hr]:border-0 [&_hr]:border-t [&_hr]:border-[var(--fb-border)] [&_img]:max-w-full",
|
|
248
|
+
"[&_table]:border-collapse [&_th]:border [&_td]:border [&_th]:border-[var(--fb-border)] [&_td]:border-[var(--fb-border)] [&_th]:px-2 [&_td]:px-2"
|
|
249
|
+
].join(" "), D = {
|
|
250
|
+
id: "markdown",
|
|
251
|
+
match: T,
|
|
252
|
+
read: "text",
|
|
253
|
+
component: ({ content: e }) => /* @__PURE__ */ o("div", {
|
|
254
|
+
className: E,
|
|
255
|
+
children: /* @__PURE__ */ o(c, { children: typeof e == "string" ? e : "" })
|
|
256
|
+
})
|
|
257
|
+
}, O = new u({
|
|
258
|
+
headingStyle: "atx",
|
|
259
|
+
codeBlockStyle: "fenced",
|
|
260
|
+
bulletListMarker: "-",
|
|
261
|
+
emDelimiter: "*"
|
|
262
|
+
});
|
|
263
|
+
function k(e) {
|
|
264
|
+
return l.parse(e, {
|
|
265
|
+
async: !1,
|
|
266
|
+
gfm: !0
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
function A(e) {
|
|
270
|
+
let t = O.turndown(e).replace(/[ \t]+$/gm, "").replace(/\n{3,}/g, "\n\n").trim();
|
|
271
|
+
return t ? `${t}\n` : "";
|
|
272
|
+
}
|
|
273
|
+
var j = {
|
|
274
|
+
id: "markdown",
|
|
275
|
+
match: T,
|
|
276
|
+
read: "text",
|
|
277
|
+
component: ({ content: e, item: t, onChange: n }) => {
|
|
278
|
+
let r = typeof e == "string" ? e : "";
|
|
279
|
+
return /* @__PURE__ */ o(w, {
|
|
280
|
+
html: k(r),
|
|
281
|
+
label: `${t.name} contents`,
|
|
282
|
+
onChange: (e) => {
|
|
283
|
+
let t = A(e);
|
|
284
|
+
t !== r && n(t);
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
//#endregion
|
|
290
|
+
export { A as htmlToMarkdown, j as markdownEditor, D as markdownPreviewer, k as markdownToHtml };
|
|
291
|
+
|
|
292
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["__iconNode","__iconNode","__iconNode","__iconNode","__iconNode"],"sources":["../../../node_modules/lucide-react/dist/esm/icons/bold.mjs","../../../node_modules/lucide-react/dist/esm/icons/code.mjs","../../../node_modules/lucide-react/dist/esm/icons/heading-2.mjs","../../../node_modules/lucide-react/dist/esm/icons/italic.mjs","../../../node_modules/lucide-react/dist/esm/icons/link.mjs","../../../node_modules/lucide-react/dist/esm/icons/list-ordered.mjs","../../../src/plugins/rich-text.tsx","../../../src/plugins/markdown/index.tsx"],"sourcesContent":["/**\n * @license lucide-react v1.23.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconNode = [\n [\n \"path\",\n { d: \"M6 12h9a4 4 0 0 1 0 8H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h7a4 4 0 0 1 0 8\", key: \"mg9rjx\" }\n ]\n];\nconst Bold = createLucideIcon(\"bold\", __iconNode);\n\nexport { __iconNode, Bold as default };\n//# sourceMappingURL=bold.mjs.map\n","/**\n * @license lucide-react v1.23.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconNode = [\n [\"path\", { d: \"m16 18 6-6-6-6\", key: \"eg8j8\" }],\n [\"path\", { d: \"m8 6-6 6 6 6\", key: \"ppft3o\" }]\n];\nconst Code = createLucideIcon(\"code\", __iconNode);\n\nexport { __iconNode, Code as default };\n//# sourceMappingURL=code.mjs.map\n","/**\n * @license lucide-react v1.23.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconNode = [\n [\"path\", { d: \"M4 12h8\", key: \"17cfdx\" }],\n [\"path\", { d: \"M4 18V6\", key: \"1rz3zl\" }],\n [\"path\", { d: \"M12 18V6\", key: \"zqpxq5\" }],\n [\"path\", { d: \"M21 18h-4c0-4 4-3 4-6 0-1.5-2-2.5-4-1\", key: \"9jr5yi\" }]\n];\nconst Heading2 = createLucideIcon(\"heading-2\", __iconNode);\n\nexport { __iconNode, Heading2 as default };\n//# sourceMappingURL=heading-2.mjs.map\n","/**\n * @license lucide-react v1.23.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconNode = [\n [\"line\", { x1: \"19\", x2: \"10\", y1: \"4\", y2: \"4\", key: \"15jd3p\" }],\n [\"line\", { x1: \"14\", x2: \"5\", y1: \"20\", y2: \"20\", key: \"bu0au3\" }],\n [\"line\", { x1: \"15\", x2: \"9\", y1: \"4\", y2: \"20\", key: \"uljnxc\" }]\n];\nconst Italic = createLucideIcon(\"italic\", __iconNode);\n\nexport { __iconNode, Italic as default };\n//# sourceMappingURL=italic.mjs.map\n","/**\n * @license lucide-react v1.23.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconNode = [\n [\"path\", { d: \"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\", key: \"1cjeqo\" }],\n [\"path\", { d: \"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\", key: \"19qd67\" }]\n];\nconst Link = createLucideIcon(\"link\", __iconNode);\n\nexport { __iconNode, Link as default };\n//# sourceMappingURL=link.mjs.map\n","/**\n * @license lucide-react v1.23.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconNode = [\n [\"path\", { d: \"M11 5h10\", key: \"1cz7ny\" }],\n [\"path\", { d: \"M11 12h10\", key: \"1438ji\" }],\n [\"path\", { d: \"M11 19h10\", key: \"11t30w\" }],\n [\"path\", { d: \"M4 4h1v5\", key: \"10yrso\" }],\n [\"path\", { d: \"M4 9h2\", key: \"r1h2o0\" }],\n [\"path\", { d: \"M6.5 20H3.4c0-1 2.6-1.925 2.6-3.5a1.5 1.5 0 0 0-2.6-1.02\", key: \"xtkcd5\" }]\n];\nconst ListOrdered = createLucideIcon(\"list-ordered\", __iconNode);\n\nexport { __iconNode, ListOrdered as default };\n//# sourceMappingURL=list-ordered.mjs.map\n","import DOMPurify from 'dompurify'\nimport { Bold, Code, Heading2, Italic, Link as LinkIcon, List, ListOrdered } from 'lucide-react'\nimport type { LucideIcon } from 'lucide-react'\nimport { useEffect, useRef, useState } from 'react'\nimport Squire from 'squire-rte'\n\ntype Command = { id: string; label: string; icon: LucideIcon; tag: string; run: (editor: Squire) => void }\n\nconst COMMANDS: Array<Command | '|'> = [\n\t{ id: 'bold', label: 'Bold', icon: Bold, tag: 'B', run: (editor) => toggle(editor, 'B', 'bold', 'removeBold') },\n\t{\n\t\tid: 'italic',\n\t\tlabel: 'Italic',\n\t\ticon: Italic,\n\t\ttag: 'I',\n\t\trun: (editor) => toggle(editor, 'I', 'italic', 'removeItalic')\n\t},\n\t'|',\n\t{ id: 'heading', label: 'Heading', icon: Heading2, tag: 'H2', run: toggleHeading },\n\t{\n\t\tid: 'bullets',\n\t\tlabel: 'Bulleted list',\n\t\ticon: List,\n\t\ttag: 'UL',\n\t\trun: (editor) => (editor.hasFormat('UL') ? editor.removeList() : editor.makeUnorderedList())\n\t},\n\t{\n\t\tid: 'numbers',\n\t\tlabel: 'Numbered list',\n\t\ticon: ListOrdered,\n\t\ttag: 'OL',\n\t\trun: (editor) => (editor.hasFormat('OL') ? editor.removeList() : editor.makeOrderedList())\n\t},\n\t'|',\n\t{ id: 'link', label: 'Link', icon: LinkIcon, tag: 'A', run: () => undefined },\n\t{ id: 'code', label: 'Code', icon: Code, tag: 'CODE', run: (editor) => editor.toggleCode() }\n]\n\nfunction toggle(editor: Squire, tag: string, add: 'bold' | 'italic', remove: 'removeBold' | 'removeItalic') {\n\tif (editor.hasFormat(tag)) editor[remove]()\n\telse editor[add]()\n}\n\nfunction toggleHeading(editor: Squire) {\n\tconst makeHeading = !editor.hasFormat('H2')\n\teditor.modifyBlocks((fragment) => {\n\t\tfor (const block of Array.from(fragment.children)) {\n\t\t\tconst next = document.createElement(makeHeading ? 'h2' : 'p')\n\t\t\tnext.append(...Array.from(block.childNodes))\n\t\t\tblock.replaceWith(next)\n\t\t}\n\t\treturn fragment\n\t})\n}\n\n// Squire ships no stylesheet. These scoped rules style the editable document and toolbar with --fb-*\n// tokens only, so the editor follows the host's light and dark themes without extra CSS.\nconst RICH_TEXT_CSS = `\n[data-fb-rich-text] .fb-rt-body {\n\tmin-height: min(480px, 60dvh); max-height: min(640px, 65dvh); overflow: auto; padding: 16px 20px; outline: none;\n\tcolor: var(--fb-text); font: inherit; font-size: var(--fb-font); line-height: 1.6; overflow-wrap: anywhere;\n}\n[data-fb-rich-text] .fb-rt-body > * + * { margin-top: 10px; }\n[data-fb-rich-text] .fb-rt-body :is(p, div, ul, ol, pre, blockquote) { margin-bottom: 0; }\n[data-fb-rich-text] .fb-rt-body p { margin-top: 0; }\n[data-fb-rich-text] .fb-rt-body h1 { font-size: 22px; font-weight: 700; margin: 0; }\n[data-fb-rich-text] .fb-rt-body h2 { font-size: 18px; font-weight: 700; margin: 0; }\n[data-fb-rich-text] .fb-rt-body h3 { font-size: 16px; font-weight: 700; margin: 0; }\n[data-fb-rich-text] .fb-rt-body ul { list-style: disc; padding-left: 22px; }\n[data-fb-rich-text] .fb-rt-body ol { list-style: decimal; padding-left: 22px; }\n[data-fb-rich-text] .fb-rt-body a { color: var(--fb-accent); text-decoration: underline; }\n[data-fb-rich-text] .fb-rt-body code {\n\tfont-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.92em;\n\tbackground: var(--fb-surface-2); border-radius: 4px; padding: 1px 4px;\n}\n[data-fb-rich-text] .fb-rt-body pre {\n\tbackground: var(--fb-surface-2); border-radius: calc(var(--fb-radius) - 2px); padding: 10px 12px; overflow: auto;\n}\n[data-fb-rich-text] .fb-rt-body pre code { background: transparent; padding: 0; }\n[data-fb-rich-text] .fb-rt-body blockquote { border-left: 3px solid var(--fb-border-strong); padding-left: 10px; color: var(--fb-muted); margin-left: 0; }\n[data-fb-rich-text] .fb-rt-body img { max-width: 100%; }\n`\n\nconst TOOL =\n\t'grid size-[30px] shrink-0 place-items-center rounded-[calc(var(--fb-radius)-2px)] text-[var(--fb-muted)] outline-none hover:bg-[var(--fb-surface)] hover:text-[var(--fb-text)] focus-visible:ring-[3px] focus-visible:ring-[color-mix(in_oklch,var(--fb-accent)_22%,transparent)] [@media(pointer:coarse)]:size-[calc(var(--fb-gap)*11)]'\n\n/**\n * WYSIWYG editing surface (Squire). Loaded and pasted HTML is cleaned with DOMPurify, so scripts and\n * event handlers never reach the page. Reports the edited HTML after every change.\n */\nexport function RichTextSurface({\n\thtml,\n\tlabel,\n\tonChange\n}: {\n\thtml: string\n\tlabel: string\n\tonChange: (html: string) => void\n}) {\n\tconst hostRef = useRef<HTMLDivElement>(null)\n\tconst editorRef = useRef<Squire | null>(null)\n\tconst onChangeRef = useRef(onChange)\n\tonChangeRef.current = onChange\n\tconst [path, setPath] = useState('')\n\tconst [linkOpen, setLinkOpen] = useState(false)\n\tconst [linkUrl, setLinkUrl] = useState('https://')\n\n\tuseEffect(() => {\n\t\tconst host = hostRef.current\n\t\tif (!host) return\n\t\t// A fresh node per effect run so React StrictMode remounts never leave a second editor behind.\n\t\tconst body = document.createElement('div')\n\t\tbody.className = 'fb-rt-body'\n\t\tbody.setAttribute('role', 'textbox')\n\t\tbody.setAttribute('aria-multiline', 'true')\n\t\tbody.setAttribute('aria-label', label)\n\t\thost.append(body)\n\t\tconst editor = new Squire(body, {\n\t\t\tblockTag: 'P',\n\t\t\tsanitizeToDOMFragment: (dirty) => DOMPurify.sanitize(dirty, { RETURN_DOM_FRAGMENT: true })\n\t\t})\n\t\teditor.setHTML(html)\n\t\teditorRef.current = editor\n\t\teditor.addEventListener('input', () => onChangeRef.current(editor.getHTML()))\n\t\teditor.addEventListener('pathChange', () => setPath(editor.getPath()))\n\t\treturn () => {\n\t\t\teditor.destroy()\n\t\t\tbody.remove()\n\t\t\teditorRef.current = null\n\t\t}\n\t\t// The editor owns its document after mount; the file view remounts it (via `key`) to reload.\n\t\t// oxlint-disable-next-line react/exhaustive-deps\n\t}, [])\n\n\tconst run = (command: Command) => {\n\t\tconst editor = editorRef.current\n\t\tif (!editor) return\n\t\tif (command.id === 'link') {\n\t\t\tif (editor.hasFormat('A')) {\n\t\t\t\teditor.removeLink()\n\t\t\t\tonChangeRef.current(editor.getHTML())\n\t\t\t} else {\n\t\t\t\tsetLinkOpen(true)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tcommand.run(editor)\n\t\teditor.focus()\n\t\tonChangeRef.current(editor.getHTML())\n\t}\n\n\tconst applyLink = () => {\n\t\tconst editor = editorRef.current\n\t\tconst url = linkUrl.trim()\n\t\tsetLinkOpen(false)\n\t\tif (!editor || !/^(https?:|mailto:|\\/|#)/i.test(url)) return\n\t\teditor.makeLink(url)\n\t\teditor.focus()\n\t\tonChangeRef.current(editor.getHTML())\n\t}\n\n\tconst activeTags = new Set(path.split('>').map((segment) => segment.split(/[.#[]/)[0].toUpperCase()))\n\n\treturn (\n\t\t<div\n\t\t\tclassName=\"flex w-full min-w-0 flex-col overflow-hidden rounded-[var(--fb-radius)] border border-[var(--fb-border)] bg-[var(--fb-surface)] text-left\"\n\t\t\tdata-fb-rich-text=\"\"\n\t\t\tref={hostRef}\n\t\t>\n\t\t\t<style>{RICH_TEXT_CSS}</style>\n\t\t\t<div\n\t\t\t\taria-label=\"Formatting\"\n\t\t\t\tclassName=\"flex min-w-0 items-center gap-0.5 overflow-x-auto border-b border-[var(--fb-border)] bg-[var(--fb-surface-2)] p-1.5\"\n\t\t\t\trole=\"toolbar\"\n\t\t\t>\n\t\t\t\t{COMMANDS.map((command, index) =>\n\t\t\t\t\tcommand === '|' ? (\n\t\t\t\t\t\t// oxlint-disable-next-line react/no-array-index-key -- Dividers are static and positional.\n\t\t\t\t\t\t<span aria-hidden=\"true\" className=\"mx-1.5 h-[18px] w-px bg-[var(--fb-border)]\" key={`divider-${index}`} />\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\taria-label={command.label}\n\t\t\t\t\t\t\taria-pressed={activeTags.has(command.tag)}\n\t\t\t\t\t\t\tclassName={`${TOOL} ${activeTags.has(command.tag) ? 'bg-[var(--fb-accent-soft)] text-[var(--fb-text)]' : ''}`}\n\t\t\t\t\t\t\tkey={command.id}\n\t\t\t\t\t\t\t// Keep the editor's selection: a toolbar click must not move focus first.\n\t\t\t\t\t\t\tonMouseDown={(event) => event.preventDefault()}\n\t\t\t\t\t\t\tonClick={() => run(command)}\n\t\t\t\t\t\t\ttitle={command.label}\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<command.icon aria-hidden=\"true\" className=\"size-4\" strokeWidth={2} />\n\t\t\t\t\t\t</button>\n\t\t\t\t\t)\n\t\t\t\t)}\n\t\t\t\t{linkOpen ? (\n\t\t\t\t\t<form\n\t\t\t\t\t\tclassName=\"ml-2 flex min-w-0 items-center gap-1.5\"\n\t\t\t\t\t\tonSubmit={(event) => {\n\t\t\t\t\t\t\tevent.preventDefault()\n\t\t\t\t\t\t\tapplyLink()\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\taria-label=\"Link address\"\n\t\t\t\t\t\t\tautoFocus\n\t\t\t\t\t\t\tclassName=\"h-[30px] w-[220px] min-w-0 rounded-[calc(var(--fb-radius)-2px)] border border-[var(--fb-border)] bg-[var(--fb-surface)] px-2 text-[calc(var(--fb-font)-1px)] text-[var(--fb-text)] outline-none focus:border-[var(--fb-accent)]\"\n\t\t\t\t\t\t\tonChange={(event) => setLinkUrl(event.target.value)}\n\t\t\t\t\t\t\tonKeyDown={(event) => {\n\t\t\t\t\t\t\t\tif (event.key === 'Escape') {\n\t\t\t\t\t\t\t\t\tevent.preventDefault()\n\t\t\t\t\t\t\t\t\tevent.stopPropagation()\n\t\t\t\t\t\t\t\t\tsetLinkOpen(false)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tvalue={linkUrl}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\tclassName=\"h-[30px] rounded-[calc(var(--fb-radius)-2px)] bg-[var(--fb-accent)] px-3 text-[calc(var(--fb-font)-1px)] font-semibold text-[var(--fb-surface)]\"\n\t\t\t\t\t\t\tonMouseDown={(event) => event.preventDefault()}\n\t\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\tAdd link\n\t\t\t\t\t\t</button>\n\t\t\t\t\t</form>\n\t\t\t\t) : null}\n\t\t\t</div>\n\t\t</div>\n\t)\n}\n","import Markdown from 'react-markdown'\nimport { marked } from 'marked'\nimport TurndownService from 'turndown'\nimport type { FileBrowserEditor, FileBrowserPreviewer } from '../../components/file-plugins'\nimport { getFileExtension } from '../../components/file-types'\nimport type { FileNode } from '../../core/types'\nimport { RichTextSurface } from '../rich-text'\n\nconst isMarkdown = (item: FileNode<unknown>) =>\n\t['md', 'markdown', 'mdx'].includes(getFileExtension(item.name)) || item.mimeType === 'text/markdown'\n\n// react-markdown renders to React elements and ignores raw HTML by default, so stored Markdown\n// cannot inject markup. The classes below style its output with --fb-* tokens only.\nconst PROSE = [\n\t'max-h-[min(640px,65dvh)] w-full overflow-auto rounded-[var(--fb-radius)] border border-[var(--fb-border)] bg-[var(--fb-surface)] p-5 text-left leading-relaxed text-[var(--fb-text)] [overflow-wrap:anywhere]',\n\t'[&>*+*]:mt-3 [&_h1]:text-[22px] [&_h1]:font-bold [&_h2]:text-[19px] [&_h2]:font-bold [&_h3]:text-[16px] [&_h3]:font-bold',\n\t'[&_h1]:m-0 [&_h2]:m-0 [&_h3]:m-0 [&_p]:m-0 [&_ul]:m-0 [&_ol]:m-0 [&_ul]:list-disc [&_ol]:list-decimal [&_ul]:pl-5 [&_ol]:pl-5',\n\t'[&_a]:text-[var(--fb-accent)] [&_a]:underline [&_blockquote]:m-0 [&_blockquote]:border-l-[3px] [&_blockquote]:border-[var(--fb-border-strong)] [&_blockquote]:pl-3 [&_blockquote]:text-[var(--fb-muted)]',\n\t'[&_code]:rounded-[4px] [&_code]:bg-[var(--fb-surface-2)] [&_code]:px-1 [&_code]:font-mono [&_code]:text-[0.92em]',\n\t'[&_pre]:m-0 [&_pre]:overflow-auto [&_pre]:rounded-[calc(var(--fb-radius)-2px)] [&_pre]:bg-[var(--fb-surface-2)] [&_pre]:p-3 [&_pre_code]:bg-transparent [&_pre_code]:p-0',\n\t'[&_hr]:border-0 [&_hr]:border-t [&_hr]:border-[var(--fb-border)] [&_img]:max-w-full',\n\t'[&_table]:border-collapse [&_th]:border [&_td]:border [&_th]:border-[var(--fb-border)] [&_td]:border-[var(--fb-border)] [&_th]:px-2 [&_td]:px-2'\n].join(' ')\n\n/** Rendered Markdown preview (react-markdown). */\nexport const markdownPreviewer: FileBrowserPreviewer = {\n\tid: 'markdown',\n\tmatch: isMarkdown,\n\tread: 'text',\n\tcomponent: ({ content }) => (\n\t\t<div className={PROSE}>\n\t\t\t<Markdown>{typeof content === 'string' ? content : ''}</Markdown>\n\t\t</div>\n\t)\n}\n\nconst turndown = new TurndownService({\n\theadingStyle: 'atx',\n\tcodeBlockStyle: 'fenced',\n\tbulletListMarker: '-',\n\temDelimiter: '*'\n})\n\n/** Markdown to the HTML the visual editor shows. */\nexport function markdownToHtml(markdown: string): string {\n\treturn marked.parse(markdown, { async: false, gfm: true })\n}\n\n/** Visual editor HTML back to Markdown, without the empty paragraphs the editor keeps for the caret. */\nexport function htmlToMarkdown(html: string): string {\n\tconst markdown = turndown\n\t\t.turndown(html)\n\t\t.replace(/[ \\t]+$/gm, '')\n\t\t.replace(/\\n{3,}/g, '\\n\\n')\n\t\t.trim()\n\treturn markdown ? `${markdown}\\n` : ''\n}\n\n/**\n * WYSIWYG Markdown editor (Squire, with marked and turndown converting to and from Markdown).\n * Saving rewrites the file's Markdown, so spacing and list markers may be normalized.\n */\nexport const markdownEditor: FileBrowserEditor = {\n\tid: 'markdown',\n\tmatch: isMarkdown,\n\tread: 'text',\n\tcomponent: ({ content, item, onChange }) => {\n\t\tconst source = typeof content === 'string' ? content : ''\n\t\treturn (\n\t\t\t<RichTextSurface\n\t\t\t\thtml={markdownToHtml(source)}\n\t\t\t\tlabel={`${item.name} contents`}\n\t\t\t\tonChange={(html) => {\n\t\t\t\t\tconst next = htmlToMarkdown(html)\n\t\t\t\t\tif (next !== source) onChange(next)\n\t\t\t\t}}\n\t\t\t/>\n\t\t)\n\t}\n}\n"],"x_google_ignoreList":[0,1,2,3,4,5],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WYSIWYG editing surface (Squire). Loaded and pasted HTML is cleaned with DOMPurify, so scripts and
|
|
3
|
+
* event handlers never reach the page. Reports the edited HTML after every change.
|
|
4
|
+
*/
|
|
5
|
+
export declare function RichTextSurface({ html, label, onChange }: {
|
|
6
|
+
html: string;
|
|
7
|
+
label: string;
|
|
8
|
+
onChange: (html: string) => void;
|
|
9
|
+
}): import("react").JSX.Element;
|
|
10
|
+
//# sourceMappingURL=rich-text.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rich-text.d.ts","sourceRoot":"","sources":["../../src/plugins/rich-text.tsx"],"names":[],"mappings":"AAsFA;;;GAGG;AACH,wBAAgB,eAAe,CAAC,EAC/B,IAAI,EACJ,KAAK,EACL,QAAQ,EACR,EAAE;IACF,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CAChC,+BAmIA"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { CellValue } from 'jspreadsheet-ce';
|
|
2
|
+
export type SpreadsheetSheet = {
|
|
3
|
+
name: string;
|
|
4
|
+
rows: CellValue[][];
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Excel-like sheet (jspreadsheet-ce). Editing supports typing, Ctrl/Cmd+C, X, V and Z, and a right-click
|
|
8
|
+
* menu limited to inserting and deleting rows and columns in place. One tab per sheet.
|
|
9
|
+
*/
|
|
10
|
+
export declare function Spreadsheet({ label, onChange, sheets, values }: {
|
|
11
|
+
label: string;
|
|
12
|
+
/** Receives every sheet's cell values after each edit. Omit to render read-only. */
|
|
13
|
+
onChange?: (sheets: CellValue[][][]) => void;
|
|
14
|
+
sheets: SpreadsheetSheet[];
|
|
15
|
+
/** `raw` keeps typed formulas such as `=A1*2`; `computed` reports their results. */
|
|
16
|
+
values?: 'raw' | 'computed';
|
|
17
|
+
}): import("react").JSX.Element;
|
|
18
|
+
/** Drop trailing empty rows and columns that the editor's minimum grid adds. */
|
|
19
|
+
export declare function trimSheet(rows: CellValue[][]): CellValue[][];
|
|
20
|
+
//# sourceMappingURL=spreadsheet.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spreadsheet.d.ts","sourceRoot":"","sources":["../../src/plugins/spreadsheet.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAqB,MAAM,iBAAiB,CAAA;AAGnE,MAAM,MAAM,gBAAgB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,SAAS,EAAE,EAAE,CAAA;CAAE,CAAA;AAyGpE;;;GAGG;AACH,wBAAgB,WAAW,CAAC,EAC3B,KAAK,EACL,QAAQ,EACR,MAAM,EACN,MAAc,EACd,EAAE;IACF,KAAK,EAAE,MAAM,CAAA;IACb,oFAAoF;IACpF,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,IAAI,CAAA;IAC5C,MAAM,EAAE,gBAAgB,EAAE,CAAA;IAC1B,oFAAoF;IACpF,MAAM,CAAC,EAAE,KAAK,GAAG,UAAU,CAAA;CAC3B,+BAmFA;AAqBD,gFAAgF;AAChF,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,GAAG,SAAS,EAAE,EAAE,CAY5D"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { FileBrowserEditor, FileBrowserPreviewer } from '../../components/file-plugins';
|
|
2
|
+
/** Read-only XLSX preview with one tab per sheet (read-excel-file + jspreadsheet-ce). */
|
|
3
|
+
export declare const xlsxPreviewer: FileBrowserPreviewer;
|
|
4
|
+
/** Spreadsheet-style XLSX editor, one tab per sheet. Saving rewrites cell values only (write-excel-file). */
|
|
5
|
+
export declare const xlsxEditor: FileBrowserEditor;
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/plugins/xlsx/index.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,iBAAiB,EAA0B,oBAAoB,EAAE,MAAM,+BAA+B,CAAA;AAgFpH,yFAAyF;AACzF,eAAO,MAAM,aAAa,EAAE,oBAM3B,CAAA;AAED,6GAA6G;AAC7G,eAAO,MAAM,UAAU,EAAE,iBAOxB,CAAA"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { i as e } from "../../chunks/file-types-DZ2qAUnj.js";
|
|
2
|
+
import { n as t, t as n } from "../../chunks/spreadsheet-GqF9DCoj.js";
|
|
3
|
+
import { useEffect as r, useState as i } from "react";
|
|
4
|
+
import { jsx as a } from "react/jsx-runtime";
|
|
5
|
+
import o from "read-excel-file/browser";
|
|
6
|
+
import s from "write-excel-file/browser";
|
|
7
|
+
//#region src/plugins/xlsx/index.tsx
|
|
8
|
+
var c = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", l = (t) => e(t.name) === "xlsx" || t.mimeType === c;
|
|
9
|
+
function u(e) {
|
|
10
|
+
return e == null ? "" : e instanceof Date ? e.toISOString().slice(0, 10) : String(e);
|
|
11
|
+
}
|
|
12
|
+
function d(e, t) {
|
|
13
|
+
return e !== void 0 && u(e) === t ? e instanceof Date ? {
|
|
14
|
+
value: e,
|
|
15
|
+
type: Date,
|
|
16
|
+
format: "yyyy-mm-dd"
|
|
17
|
+
} : e : t === "" ? null : /^-?\d+(\.\d+)?$/.test(t) ? Number(t) : t === "TRUE" || t === "FALSE" ? t === "TRUE" : t;
|
|
18
|
+
}
|
|
19
|
+
function f(e) {
|
|
20
|
+
let [t, n] = i(null);
|
|
21
|
+
return r(() => {
|
|
22
|
+
let t = !0;
|
|
23
|
+
if (e instanceof ArrayBuffer) return o(e).then((e) => t && n({
|
|
24
|
+
sheets: e,
|
|
25
|
+
error: null
|
|
26
|
+
}), (e) => t && n({
|
|
27
|
+
sheets: [],
|
|
28
|
+
error: e instanceof Error ? e.message : String(e)
|
|
29
|
+
})), () => {
|
|
30
|
+
t = !1;
|
|
31
|
+
};
|
|
32
|
+
}, [e]), t;
|
|
33
|
+
}
|
|
34
|
+
function p({ content: e, item: r, onChange: i }) {
|
|
35
|
+
let o = f(e);
|
|
36
|
+
if (!o) return /* @__PURE__ */ a("p", {
|
|
37
|
+
className: "m-0 text-[var(--fb-muted)]",
|
|
38
|
+
children: "Loading workbook…"
|
|
39
|
+
});
|
|
40
|
+
if (o.error) return /* @__PURE__ */ a("p", {
|
|
41
|
+
className: "m-0 text-[var(--fb-danger)]",
|
|
42
|
+
children: o.error
|
|
43
|
+
});
|
|
44
|
+
let c = async (e) => {
|
|
45
|
+
i && i(await s(o.sheets.map((n, r) => ({
|
|
46
|
+
sheet: n.sheet,
|
|
47
|
+
data: t(e[r] ?? []).map((e, t) => e.map((e, r) => d(n.data[t]?.[r], String(e ?? ""))))
|
|
48
|
+
}))).toBlob());
|
|
49
|
+
};
|
|
50
|
+
return /* @__PURE__ */ a(n, {
|
|
51
|
+
label: r.name,
|
|
52
|
+
onChange: i ? (e) => void c(e) : void 0,
|
|
53
|
+
sheets: o.sheets.map((e) => ({
|
|
54
|
+
name: e.sheet,
|
|
55
|
+
rows: e.data.map((e) => e.map(u))
|
|
56
|
+
})),
|
|
57
|
+
values: "computed"
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
var m = {
|
|
61
|
+
id: "xlsx",
|
|
62
|
+
match: l,
|
|
63
|
+
read: "binary",
|
|
64
|
+
maxBytes: 10 * 1024 * 1024,
|
|
65
|
+
component: ({ content: e, item: t }) => /* @__PURE__ */ a(p, {
|
|
66
|
+
content: e,
|
|
67
|
+
item: t
|
|
68
|
+
})
|
|
69
|
+
}, h = {
|
|
70
|
+
id: "xlsx",
|
|
71
|
+
match: l,
|
|
72
|
+
read: "binary",
|
|
73
|
+
maxBytes: 10 * 1024 * 1024,
|
|
74
|
+
saveWarning: "Saving keeps cell values only. Formatting, formulas and charts are lost.",
|
|
75
|
+
component: ({ content: e, item: t, onChange: n }) => /* @__PURE__ */ a(p, {
|
|
76
|
+
content: e,
|
|
77
|
+
item: t,
|
|
78
|
+
onChange: n
|
|
79
|
+
})
|
|
80
|
+
};
|
|
81
|
+
//#endregion
|
|
82
|
+
export { h as xlsxEditor, m as xlsxPreviewer };
|
|
83
|
+
|
|
84
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/plugins/xlsx/index.tsx"],"sourcesContent":["import type { CellValue } from 'jspreadsheet-ce'\nimport { useEffect, useState } from 'react'\nimport readXlsxFile from 'read-excel-file/browser'\nimport type { Sheet } from 'read-excel-file/browser'\nimport writeXlsxFile from 'write-excel-file/browser'\nimport type { Cell } from 'write-excel-file/browser'\nimport type { FileBrowserEditor, FileBrowserFileContent, FileBrowserPreviewer } from '../../components/file-plugins'\nimport { getFileExtension } from '../../components/file-types'\nimport type { FileNode } from '../../core/types'\nimport { Spreadsheet, trimSheet } from '../spreadsheet'\n\ntype SheetCell = Sheet['data'][number][number]\n\nconst XLSX_MIME = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'\nconst isXlsx = (item: FileNode<unknown>) => getFileExtension(item.name) === 'xlsx' || item.mimeType === XLSX_MIME\n\nfunction formatCell(cell: SheetCell): string {\n\tif (cell === null || cell === undefined) return ''\n\tif (cell instanceof Date) return cell.toISOString().slice(0, 10)\n\treturn String(cell)\n}\n\n// Unchanged cells keep their original type; edited cells become numbers or booleans when they look like one.\nfunction toWritableCell(original: SheetCell | undefined, text: string): Cell {\n\tif (original !== undefined && formatCell(original) === text) {\n\t\treturn original instanceof Date ? { value: original, type: Date, format: 'yyyy-mm-dd' } : (original as Cell)\n\t}\n\tif (text === '') return null\n\tif (/^-?\\d+(\\.\\d+)?$/.test(text)) return Number(text)\n\tif (text === 'TRUE' || text === 'FALSE') return text === 'TRUE'\n\treturn text\n}\n\nfunction useSheets(content: FileBrowserFileContent | null) {\n\tconst [state, setState] = useState<{ sheets: Sheet[]; error: string | null } | null>(null)\n\tuseEffect(() => {\n\t\tlet active = true\n\t\tif (!(content instanceof ArrayBuffer)) return\n\t\treadXlsxFile(content).then(\n\t\t\t(sheets) => active && setState({ sheets, error: null }),\n\t\t\t(error: unknown) =>\n\t\t\t\tactive && setState({ sheets: [], error: error instanceof Error ? error.message : String(error) })\n\t\t)\n\t\treturn () => {\n\t\t\tactive = false\n\t\t}\n\t}, [content])\n\treturn state\n}\n\nfunction Workbook({\n\tcontent,\n\titem,\n\tonChange\n}: {\n\tcontent: FileBrowserFileContent | null\n\titem: FileNode<unknown>\n\tonChange?: (next: Blob) => void\n}) {\n\tconst loaded = useSheets(content)\n\tif (!loaded) return <p className=\"m-0 text-[var(--fb-muted)]\">Loading workbook…</p>\n\tif (loaded.error) return <p className=\"m-0 text-[var(--fb-danger)]\">{loaded.error}</p>\n\n\tconst save = async (edited: CellValue[][][]) => {\n\t\tif (!onChange) return\n\t\tconst blob = await writeXlsxFile(\n\t\t\tloaded.sheets.map((entry, index) => ({\n\t\t\t\tsheet: entry.sheet,\n\t\t\t\tdata: trimSheet(edited[index] ?? []).map((row, rowIndex) =>\n\t\t\t\t\trow.map((value, column) => toWritableCell(entry.data[rowIndex]?.[column], String(value ?? '')))\n\t\t\t\t)\n\t\t\t}))\n\t\t).toBlob()\n\t\tonChange(blob)\n\t}\n\n\treturn (\n\t\t<Spreadsheet\n\t\t\tlabel={item.name}\n\t\t\tonChange={onChange ? (sheets) => void save(sheets) : undefined}\n\t\t\tsheets={loaded.sheets.map((entry) => ({ name: entry.sheet, rows: entry.data.map((row) => row.map(formatCell)) }))}\n\t\t\tvalues=\"computed\"\n\t\t/>\n\t)\n}\n\n/** Read-only XLSX preview with one tab per sheet (read-excel-file + jspreadsheet-ce). */\nexport const xlsxPreviewer: FileBrowserPreviewer = {\n\tid: 'xlsx',\n\tmatch: isXlsx,\n\tread: 'binary',\n\tmaxBytes: 10 * 1024 * 1024,\n\tcomponent: ({ content, item }) => <Workbook content={content} item={item} />\n}\n\n/** Spreadsheet-style XLSX editor, one tab per sheet. Saving rewrites cell values only (write-excel-file). */\nexport const xlsxEditor: FileBrowserEditor = {\n\tid: 'xlsx',\n\tmatch: isXlsx,\n\tread: 'binary',\n\tmaxBytes: 10 * 1024 * 1024,\n\tsaveWarning: 'Saving keeps cell values only. Formatting, formulas and charts are lost.',\n\tcomponent: ({ content, item, onChange }) => <Workbook content={content} item={item} onChange={onChange} />\n}\n"],"mappings":";;;;;;;AAaA,IAAM,IAAY,qEACZ,KAAU,MAA4B,EAAiB,EAAK,IAAI,MAAM,UAAU,EAAK,aAAa;AAExG,SAAS,EAAW,GAAyB;CAG5C,OAFI,KAAS,OAAmC,KAC5C,aAAgB,OAAa,EAAK,YAAY,CAAC,CAAC,MAAM,GAAG,EAAE,IACxD,OAAO,CAAI;AACnB;AAGA,SAAS,EAAe,GAAiC,GAAoB;CAO5E,OANI,MAAa,KAAA,KAAa,EAAW,CAAQ,MAAM,IAC/C,aAAoB,OAAO;EAAE,OAAO;EAAU,MAAM;EAAM,QAAQ;CAAa,IAAK,IAExF,MAAS,KAAW,OACpB,kBAAkB,KAAK,CAAI,IAAU,OAAO,CAAI,IAChD,MAAS,UAAU,MAAS,UAAgB,MAAS,SAClD;AACR;AAEA,SAAS,EAAU,GAAwC;CAC1D,IAAM,CAAC,GAAO,KAAY,EAA2D,IAAI;CAazF,OAZA,QAAgB;EACf,IAAI,IAAS;EACP,iBAAmB,aAMzB,OALA,EAAa,CAAO,CAAC,CAAC,MACpB,MAAW,KAAU,EAAS;GAAE;GAAQ,OAAO;EAAK,CAAC,IACrD,MACA,KAAU,EAAS;GAAE,QAAQ,CAAC;GAAG,OAAO,aAAiB,QAAQ,EAAM,UAAU,OAAO,CAAK;EAAE,CAAC,CAClG,SACa;GACZ,IAAS;EACV;CACD,GAAG,CAAC,CAAO,CAAC,GACL;AACR;AAEA,SAAS,EAAS,EACjB,YACA,SACA,eAKE;CACF,IAAM,IAAS,EAAU,CAAO;CAChC,IAAI,CAAC,GAAQ,OAAO,kBAAC,KAAD;EAAG,WAAU;YAA6B;CAAoB,CAAA;CAClF,IAAI,EAAO,OAAO,OAAO,kBAAC,KAAD;EAAG,WAAU;YAA+B,EAAO;CAAS,CAAA;CAErF,IAAM,IAAO,OAAO,MAA4B;EAC1C,KASL,EAAS,MARU,EAClB,EAAO,OAAO,KAAK,GAAO,OAAW;GACpC,OAAO,EAAM;GACb,MAAM,EAAU,EAAO,MAAU,CAAC,CAAC,CAAC,CAAC,KAAK,GAAK,MAC9C,EAAI,KAAK,GAAO,MAAW,EAAe,EAAM,KAAK,EAAS,GAAG,IAAS,OAAO,KAAS,EAAE,CAAC,CAAC,CAC/F;EACD,EAAE,CACH,CAAC,CAAC,OAAO,CACI;CACd;CAEA,OACC,kBAAC,GAAD;EACC,OAAO,EAAK;EACZ,UAAU,KAAY,MAAW,KAAK,EAAK,CAAM,IAAI,KAAA;EACrD,QAAQ,EAAO,OAAO,KAAK,OAAW;GAAE,MAAM,EAAM;GAAO,MAAM,EAAM,KAAK,KAAK,MAAQ,EAAI,IAAI,CAAU,CAAC;EAAE,EAAE;EAChH,QAAO;CACP,CAAA;AAEH;AAGA,IAAa,IAAsC;CAClD,IAAI;CACJ,OAAO;CACP,MAAM;CACN,UAAU,KAAK,OAAO;CACtB,YAAY,EAAE,YAAS,cAAW,kBAAC,GAAD;EAAmB;EAAe;CAAO,CAAA;AAC5E,GAGa,IAAgC;CAC5C,IAAI;CACJ,OAAO;CACP,MAAM;CACN,UAAU,KAAK,OAAO;CACtB,aAAa;CACb,YAAY,EAAE,YAAS,SAAM,kBAAe,kBAAC,GAAD;EAAmB;EAAe;EAAgB;CAAW,CAAA;AAC1G"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@harryy/react-file-browser",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "A standalone, storage-agnostic React file manager component library.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"file-browser",
|
|
@@ -54,6 +54,26 @@
|
|
|
54
54
|
"types": "./dist/adapters/supabase/index.d.ts",
|
|
55
55
|
"import": "./dist/adapters/supabase/index.js"
|
|
56
56
|
},
|
|
57
|
+
"./plugins/code": {
|
|
58
|
+
"types": "./dist/plugins/code/index.d.ts",
|
|
59
|
+
"import": "./dist/plugins/code/index.js"
|
|
60
|
+
},
|
|
61
|
+
"./plugins/markdown": {
|
|
62
|
+
"types": "./dist/plugins/markdown/index.d.ts",
|
|
63
|
+
"import": "./dist/plugins/markdown/index.js"
|
|
64
|
+
},
|
|
65
|
+
"./plugins/csv": {
|
|
66
|
+
"types": "./dist/plugins/csv/index.d.ts",
|
|
67
|
+
"import": "./dist/plugins/csv/index.js"
|
|
68
|
+
},
|
|
69
|
+
"./plugins/xlsx": {
|
|
70
|
+
"types": "./dist/plugins/xlsx/index.d.ts",
|
|
71
|
+
"import": "./dist/plugins/xlsx/index.js"
|
|
72
|
+
},
|
|
73
|
+
"./plugins/docx": {
|
|
74
|
+
"types": "./dist/plugins/docx/index.d.ts",
|
|
75
|
+
"import": "./dist/plugins/docx/index.js"
|
|
76
|
+
},
|
|
57
77
|
"./theme": {
|
|
58
78
|
"types": "./dist/theme/index.d.ts",
|
|
59
79
|
"import": "./dist/theme/index.js"
|
|
@@ -99,26 +119,53 @@
|
|
|
99
119
|
"@testing-library/react": "^16.3.2",
|
|
100
120
|
"@testing-library/user-event": "^14.6.1",
|
|
101
121
|
"@types/node": "^26.1.0",
|
|
122
|
+
"@types/papaparse": "^5.5.2",
|
|
123
|
+
"@types/prismjs": "^1.26.6",
|
|
102
124
|
"@types/react": "^19.2.17",
|
|
103
125
|
"@types/react-dom": "^19.2.3",
|
|
126
|
+
"@types/turndown": "^5.0.6",
|
|
104
127
|
"@vitejs/plugin-react": "^6.0.3",
|
|
128
|
+
"codejar": "^4.3.0",
|
|
129
|
+
"docx-preview": "^0.4.1",
|
|
130
|
+
"dompurify": "3.4.16",
|
|
105
131
|
"jsdom": "^29.1.1",
|
|
132
|
+
"jspreadsheet-ce": "5.0.4",
|
|
106
133
|
"lefthook": "^2.1.9",
|
|
134
|
+
"marked": "^18.0.14",
|
|
107
135
|
"oxfmt": "^0.57.0",
|
|
108
136
|
"oxlint": "^1.72.0",
|
|
109
137
|
"oxlint-tsgolint": "^0.24.0",
|
|
138
|
+
"papaparse": "^5.7.0",
|
|
139
|
+
"prismjs": "^1.30.0",
|
|
140
|
+
"react-markdown": "^10.1.0",
|
|
141
|
+
"read-excel-file": "^9.3.10",
|
|
110
142
|
"semantic-release": "^25.0.5",
|
|
143
|
+
"squire-rte": "2.4.9",
|
|
111
144
|
"tailwindcss": "^4.3.2",
|
|
145
|
+
"turndown": "7.2.4",
|
|
112
146
|
"typescript": "^6.0.3",
|
|
113
147
|
"vite": "^8.1.3",
|
|
114
|
-
"vitest": "^4.1.9"
|
|
148
|
+
"vitest": "^4.1.9",
|
|
149
|
+
"write-excel-file": "^4.1.1"
|
|
115
150
|
},
|
|
116
151
|
"peerDependencies": {
|
|
117
152
|
"@aws-sdk/client-s3": "^3.1079.0",
|
|
118
153
|
"@aws-sdk/s3-request-presigner": "^3.1079.0",
|
|
119
154
|
"@supabase/supabase-js": "^2.110.0",
|
|
155
|
+
"codejar": "^4.3.0",
|
|
156
|
+
"docx-preview": "^0.4.1",
|
|
157
|
+
"dompurify": "^3.4.16",
|
|
158
|
+
"jspreadsheet-ce": "^5.0.4",
|
|
159
|
+
"marked": "^18.0.14",
|
|
160
|
+
"papaparse": "^5.7.0",
|
|
161
|
+
"prismjs": "^1.30.0",
|
|
120
162
|
"react": ">=18 <20",
|
|
121
|
-
"react-dom": ">=18 <20"
|
|
163
|
+
"react-dom": ">=18 <20",
|
|
164
|
+
"react-markdown": "^10.1.0",
|
|
165
|
+
"read-excel-file": "^9.3.10",
|
|
166
|
+
"squire-rte": "^2.4.9",
|
|
167
|
+
"turndown": "^7.2.4",
|
|
168
|
+
"write-excel-file": "^4.1.1"
|
|
122
169
|
},
|
|
123
170
|
"peerDependenciesMeta": {
|
|
124
171
|
"@aws-sdk/client-s3": {
|
|
@@ -129,6 +176,42 @@
|
|
|
129
176
|
},
|
|
130
177
|
"@supabase/supabase-js": {
|
|
131
178
|
"optional": true
|
|
179
|
+
},
|
|
180
|
+
"codejar": {
|
|
181
|
+
"optional": true
|
|
182
|
+
},
|
|
183
|
+
"docx-preview": {
|
|
184
|
+
"optional": true
|
|
185
|
+
},
|
|
186
|
+
"dompurify": {
|
|
187
|
+
"optional": true
|
|
188
|
+
},
|
|
189
|
+
"jspreadsheet-ce": {
|
|
190
|
+
"optional": true
|
|
191
|
+
},
|
|
192
|
+
"marked": {
|
|
193
|
+
"optional": true
|
|
194
|
+
},
|
|
195
|
+
"papaparse": {
|
|
196
|
+
"optional": true
|
|
197
|
+
},
|
|
198
|
+
"prismjs": {
|
|
199
|
+
"optional": true
|
|
200
|
+
},
|
|
201
|
+
"react-markdown": {
|
|
202
|
+
"optional": true
|
|
203
|
+
},
|
|
204
|
+
"read-excel-file": {
|
|
205
|
+
"optional": true
|
|
206
|
+
},
|
|
207
|
+
"squire-rte": {
|
|
208
|
+
"optional": true
|
|
209
|
+
},
|
|
210
|
+
"turndown": {
|
|
211
|
+
"optional": true
|
|
212
|
+
},
|
|
213
|
+
"write-excel-file": {
|
|
214
|
+
"optional": true
|
|
132
215
|
}
|
|
133
216
|
},
|
|
134
217
|
"engines": {
|