@godxjp/editor 31.18.0 → 31.20.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 +4 -2
- package/dist/index.d.ts +10 -2
- package/dist/index.js +32 -11
- package/package.json +6 -4
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ rendered by [`@godxjp/markdown`](../markdown) (the same sanitiser as the publish
|
|
|
5
5
|
pasted, dropped or attached through an upload function **the host** provides.
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
pnpm add @godxjp/editor @godxjp/markdown @godxjp/ui
|
|
8
|
+
pnpm add @godxjp/editor @godxjp/markdown @godxjp/ui
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
```tsx
|
|
@@ -30,7 +30,9 @@ import { FormField } from "@godxjp/ui/data-entry";
|
|
|
30
30
|
`readOnly`, its actions stay focusable but do nothing (`aria-disabled`).
|
|
31
31
|
- Files: `upload(file) => Promise<{ url, name? }>`. A placeholder marks each upload and is replaced
|
|
32
32
|
by `` (images) or `[name](url)`; a failure removes it and says which file failed.
|
|
33
|
-
`uploadBlockedReason` refuses files with the host's message
|
|
33
|
+
`uploadBlockedReason` refuses files with the host's message — with or without `upload`: a
|
|
34
|
+
pasted or dropped file shows it, and the attach button names the reason and shows it when
|
|
35
|
+
pressed instead of opening the picker. With neither prop, files are left to the browser.
|
|
34
36
|
- Strings come from the kit's catalogue in ja / en / vi; `labels` overrides any of them.
|
|
35
37
|
|
|
36
38
|
## Props
|
package/dist/index.d.ts
CHANGED
|
@@ -164,7 +164,11 @@ type MarkdownEditorProps = Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement
|
|
|
164
164
|
* (an app's own storage today, the media service later). Omit it and files are not accepted.
|
|
165
165
|
*/
|
|
166
166
|
upload?: (file: File) => Promise<MarkdownEditorUploadResult>;
|
|
167
|
-
/**
|
|
167
|
+
/**
|
|
168
|
+
* When set, files are refused with this message (a quota, a read-only space) — with or without
|
|
169
|
+
* `upload`. Pasted / dropped files show it; the attach button carries it in its name and tooltip
|
|
170
|
+
* and shows it when pressed, instead of opening a file picker whose choice would be refused.
|
|
171
|
+
*/
|
|
168
172
|
uploadBlockedReason?: string | null;
|
|
169
173
|
/** The preview. Default: `<Prose><Markdown>{value}</Markdown></Prose>` from `@godxjp/markdown`. */
|
|
170
174
|
renderPreview?: (value: string) => React.ReactNode;
|
|
@@ -197,7 +201,11 @@ declare const MarkdownEditor: React.ForwardRefExoticComponent<Omit<React.Textare
|
|
|
197
201
|
* (an app's own storage today, the media service later). Omit it and files are not accepted.
|
|
198
202
|
*/
|
|
199
203
|
upload?: (file: File) => Promise<MarkdownEditorUploadResult>;
|
|
200
|
-
/**
|
|
204
|
+
/**
|
|
205
|
+
* When set, files are refused with this message (a quota, a read-only space) — with or without
|
|
206
|
+
* `upload`. Pasted / dropped files show it; the attach button carries it in its name and tooltip
|
|
207
|
+
* and shows it when pressed, instead of opening a file picker whose choice would be refused.
|
|
208
|
+
*/
|
|
201
209
|
uploadBlockedReason?: string | null;
|
|
202
210
|
/** The preview. Default: `<Prose><Markdown>{value}</Markdown></Prose>` from `@godxjp/markdown`. */
|
|
203
211
|
renderPreview?: (value: string) => React.ReactNode;
|
package/dist/index.js
CHANGED
|
@@ -36,12 +36,29 @@ function wrapSelection(text, { start, end }, before, after, placeholder) {
|
|
|
36
36
|
select: { start: outerStart, end: outerStart + selected.length }
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
|
-
|
|
39
|
+
if (!selected) {
|
|
40
|
+
return {
|
|
41
|
+
from: start,
|
|
42
|
+
to: end,
|
|
43
|
+
insert: `${before}${placeholder}${after}`,
|
|
44
|
+
select: { start: start + before.length, end: start + before.length + placeholder.length }
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
const count = (value) => before === after ? value.split(before).length - 1 : 0;
|
|
48
|
+
let from = start;
|
|
49
|
+
let to = end;
|
|
50
|
+
if (text.slice(start - before.length, start) === before && count(text.slice(0, start)) % 2 === 1) {
|
|
51
|
+
from = start - before.length;
|
|
52
|
+
}
|
|
53
|
+
if (text.slice(end, end + after.length) === after && count(text.slice(0, end)) % 2 === 1) {
|
|
54
|
+
to = end + after.length;
|
|
55
|
+
}
|
|
56
|
+
const body = text.slice(from, to).split(before).join("").split(after).join("");
|
|
40
57
|
return {
|
|
41
|
-
from
|
|
42
|
-
to
|
|
58
|
+
from,
|
|
59
|
+
to,
|
|
43
60
|
insert: `${before}${body}${after}`,
|
|
44
|
-
select: { start:
|
|
61
|
+
select: { start: from + before.length, end: from + before.length + body.length }
|
|
45
62
|
};
|
|
46
63
|
}
|
|
47
64
|
function lineRange(text, { start, end }) {
|
|
@@ -290,12 +307,14 @@ var MarkdownEditor = React.forwardRef(
|
|
|
290
307
|
const [uploadError, setUploadError] = React.useState(null);
|
|
291
308
|
const uploadSeq = React.useRef(0);
|
|
292
309
|
const fileInput = React.useRef(null);
|
|
310
|
+
const takesFiles = Boolean(upload) || Boolean(uploadBlockedReason);
|
|
293
311
|
const acceptFiles = async (files) => {
|
|
294
|
-
if (
|
|
312
|
+
if (files.length === 0 || !editable) return;
|
|
295
313
|
if (uploadBlockedReason) {
|
|
296
314
|
setUploadError(uploadBlockedReason);
|
|
297
315
|
return;
|
|
298
316
|
}
|
|
317
|
+
if (!upload) return;
|
|
299
318
|
setUploadError(null);
|
|
300
319
|
for (const file of files) {
|
|
301
320
|
const placeholder = `![${label("uploading")} ${file.name.replace(/[[\]\\]/g, "")} #${++uploadSeq.current}]()`;
|
|
@@ -361,12 +380,14 @@ var MarkdownEditor = React.forwardRef(
|
|
|
361
380
|
},
|
|
362
381
|
{ key: "quote", label: label("quote"), icon: /* @__PURE__ */ jsx(Quote, {}), run: () => lines("> ") }
|
|
363
382
|
];
|
|
364
|
-
if (
|
|
383
|
+
if (takesFiles) {
|
|
365
384
|
builtIn.push({
|
|
366
385
|
key: "attach",
|
|
367
|
-
|
|
386
|
+
// A blocked attach says why in its own name and tooltip, BEFORE anyone opens the picker.
|
|
387
|
+
label: uploadBlockedReason ? `${label("attach")} \u2014 ${uploadBlockedReason}` : label("attach"),
|
|
368
388
|
icon: /* @__PURE__ */ jsx(Paperclip, {}),
|
|
369
|
-
|
|
389
|
+
// …and a press repeats the reason instead of opening a picker whose choice would be refused.
|
|
390
|
+
run: () => uploadBlockedReason ? setUploadError(uploadBlockedReason) : fileInput.current?.click()
|
|
370
391
|
});
|
|
371
392
|
}
|
|
372
393
|
const all = [...builtIn, ...actions];
|
|
@@ -397,7 +418,7 @@ var MarkdownEditor = React.forwardRef(
|
|
|
397
418
|
onPaste: (event) => {
|
|
398
419
|
onPaste?.(event);
|
|
399
420
|
const files = Array.from(event.clipboardData?.files ?? []);
|
|
400
|
-
if (!event.defaultPrevented &&
|
|
421
|
+
if (!event.defaultPrevented && takesFiles && files.length > 0) {
|
|
401
422
|
event.preventDefault();
|
|
402
423
|
void acceptFiles(files);
|
|
403
424
|
}
|
|
@@ -405,7 +426,7 @@ var MarkdownEditor = React.forwardRef(
|
|
|
405
426
|
onDrop: (event) => {
|
|
406
427
|
onDrop?.(event);
|
|
407
428
|
const files = Array.from(event.dataTransfer?.files ?? []);
|
|
408
|
-
if (!event.defaultPrevented &&
|
|
429
|
+
if (!event.defaultPrevented && takesFiles && files.length > 0) {
|
|
409
430
|
event.preventDefault();
|
|
410
431
|
void acceptFiles(files);
|
|
411
432
|
}
|
|
@@ -450,7 +471,7 @@ var MarkdownEditor = React.forwardRef(
|
|
|
450
471
|
/* @__PURE__ */ jsx("div", { hidden: mode === "preview", children: textarea }),
|
|
451
472
|
mode === "preview" ? preview : null
|
|
452
473
|
] }),
|
|
453
|
-
upload ? /* @__PURE__ */ jsx(
|
|
474
|
+
upload && !uploadBlockedReason ? /* @__PURE__ */ jsx(
|
|
454
475
|
"input",
|
|
455
476
|
{
|
|
456
477
|
ref: fileInput,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@godxjp/editor",
|
|
3
|
-
"version": "31.
|
|
3
|
+
"version": "31.20.0",
|
|
4
4
|
"description": "Markdown editor for GoDX apps: formatting toolbar, write / preview / side by side with @godxjp/markdown, paste / drop / attach through a host upload function, IME-safe, ja / en / vi.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -28,10 +28,12 @@
|
|
|
28
28
|
"directory": "packages/editor"
|
|
29
29
|
},
|
|
30
30
|
"license": "Apache-2.0",
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"lucide-react": "^1.37.0"
|
|
33
|
+
},
|
|
31
34
|
"peerDependencies": {
|
|
32
|
-
"@godxjp/markdown": "~31.
|
|
33
|
-
"@godxjp/ui": "~31.
|
|
34
|
-
"lucide-react": ">=1.0.0",
|
|
35
|
+
"@godxjp/markdown": "~31.20.0",
|
|
36
|
+
"@godxjp/ui": "~31.20.0",
|
|
35
37
|
"react": ">=19.0.0"
|
|
36
38
|
},
|
|
37
39
|
"keywords": [
|