@thangph2146/lexical-editor 0.0.11 → 0.0.12
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 +2 -1
- package/dist/editor-x/editor.cjs +280 -20
- package/dist/editor-x/editor.cjs.map +1 -1
- package/dist/editor-x/editor.css +27 -3
- package/dist/editor-x/editor.css.map +1 -1
- package/dist/editor-x/editor.js +281 -21
- package/dist/editor-x/editor.js.map +1 -1
- package/dist/index.cjs +292 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +27 -3
- package/dist/index.css.map +1 -1
- package/dist/index.d.cts +26 -1
- package/dist/index.d.ts +26 -1
- package/dist/index.js +293 -24
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/lexical-editor.tsx +19 -6
- package/src/context/uploads-context.tsx +1 -0
- package/src/editor-ui/content-editable.tsx +18 -2
- package/src/editor-x/nodes.ts +2 -0
- package/src/nodes/download-link-node.tsx +118 -0
- package/src/plugins/floating-link-editor-plugin.tsx +338 -91
- package/src/themes/plugins/_floating-link-editor.scss +28 -2
- package/src/themes/ui-components/_button.scss +1 -1
- package/src/themes/ui-components/_flex.scss +1 -0
- package/src/ui/button-group.tsx +10 -10
- package/src/ui/button.tsx +38 -38
- package/src/ui/collapsible.tsx +67 -67
- package/src/ui/command.tsx +48 -48
- package/src/ui/dialog.tsx +146 -146
- package/src/ui/flex.tsx +45 -45
- package/src/ui/input.tsx +20 -20
- package/src/ui/label.tsx +20 -20
- package/src/ui/number-input.tsx +104 -104
- package/src/ui/popover.tsx +128 -128
- package/src/ui/scroll-area.tsx +17 -17
- package/src/ui/select.tsx +171 -171
- package/src/ui/separator.tsx +20 -20
- package/src/ui/slider.tsx +14 -14
- package/src/ui/slot.tsx +3 -3
- package/src/ui/tabs.tsx +87 -87
- package/src/ui/toggle-group.tsx +109 -109
- package/src/ui/toggle.tsx +28 -28
- package/src/ui/tooltip.tsx +28 -28
- package/src/ui/typography.tsx +44 -44
package/README.md
CHANGED
|
@@ -37,7 +37,7 @@ function MyEditor() {
|
|
|
37
37
|
<LexicalEditor
|
|
38
38
|
placeholder="Nhập nội dung tại đây..."
|
|
39
39
|
onChange={handleChange}
|
|
40
|
-
/>
|
|
40
|
+
/>gioi-thieu:team-section
|
|
41
41
|
);
|
|
42
42
|
}
|
|
43
43
|
```
|
|
@@ -51,5 +51,6 @@ MIT
|
|
|
51
51
|
```bash
|
|
52
52
|
pnpm build
|
|
53
53
|
npm whoami
|
|
54
|
+
npm login
|
|
54
55
|
npm publish --access public
|
|
55
56
|
```
|
package/dist/editor-x/editor.cjs
CHANGED
|
@@ -20,9 +20,9 @@ var LexicalHistoryPlugin = require('@lexical/react/LexicalHistoryPlugin');
|
|
|
20
20
|
var LexicalRichTextPlugin = require('@lexical/react/LexicalRichTextPlugin');
|
|
21
21
|
var LexicalErrorBoundary = require('@lexical/react/LexicalErrorBoundary');
|
|
22
22
|
var list = require('@lexical/list');
|
|
23
|
+
var link = require('@lexical/link');
|
|
23
24
|
var code = require('@lexical/code');
|
|
24
25
|
var hashtag = require('@lexical/hashtag');
|
|
25
|
-
var link = require('@lexical/link');
|
|
26
26
|
var overflow = require('@lexical/overflow');
|
|
27
27
|
var LexicalHorizontalRuleNode = require('@lexical/react/LexicalHorizontalRuleNode');
|
|
28
28
|
var richText = require('@lexical/rich-text');
|
|
@@ -5095,12 +5095,14 @@ var init_image_placeholder = __esm({
|
|
|
5095
5095
|
}
|
|
5096
5096
|
});
|
|
5097
5097
|
function ContentEditable({
|
|
5098
|
-
placeholder,
|
|
5098
|
+
placeholder = "",
|
|
5099
5099
|
className,
|
|
5100
5100
|
placeholderClassName,
|
|
5101
5101
|
placeholderDefaults = true
|
|
5102
5102
|
}) {
|
|
5103
5103
|
const isReadOnlyOrReview = className?.includes("--readonly") || className?.includes("--review");
|
|
5104
|
+
const text = placeholder.trim();
|
|
5105
|
+
const showLexicalPlaceholder = text.length > 0;
|
|
5104
5106
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
5105
5107
|
LexicalContentEditable.ContentEditable,
|
|
5106
5108
|
{
|
|
@@ -5109,7 +5111,17 @@ function ContentEditable({
|
|
|
5109
5111
|
!isReadOnlyOrReview && "min-h-72 px-8 py-4",
|
|
5110
5112
|
className
|
|
5111
5113
|
),
|
|
5112
|
-
"aria-label": "Editor n\u1ED9i dung"
|
|
5114
|
+
"aria-label": "Editor n\u1ED9i dung",
|
|
5115
|
+
...showLexicalPlaceholder ? {
|
|
5116
|
+
"aria-placeholder": text,
|
|
5117
|
+
placeholder: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5118
|
+
"div",
|
|
5119
|
+
{
|
|
5120
|
+
className: cn(placeholderDefaults && "editor-placeholder", placeholderClassName),
|
|
5121
|
+
children: text
|
|
5122
|
+
}
|
|
5123
|
+
)
|
|
5124
|
+
} : { placeholder: null }
|
|
5113
5125
|
}
|
|
5114
5126
|
);
|
|
5115
5127
|
}
|
|
@@ -6245,6 +6257,86 @@ var init_list_with_color_node = __esm({
|
|
|
6245
6257
|
};
|
|
6246
6258
|
}
|
|
6247
6259
|
});
|
|
6260
|
+
function $createDownloadLinkNode(url, download = null, attributes) {
|
|
6261
|
+
return new DownloadLinkNode(url, download, attributes);
|
|
6262
|
+
}
|
|
6263
|
+
function $isDownloadLinkNode(node) {
|
|
6264
|
+
return node instanceof DownloadLinkNode;
|
|
6265
|
+
}
|
|
6266
|
+
var DownloadLinkNode;
|
|
6267
|
+
var init_download_link_node = __esm({
|
|
6268
|
+
"src/nodes/download-link-node.tsx"() {
|
|
6269
|
+
DownloadLinkNode = class _DownloadLinkNode extends link.LinkNode {
|
|
6270
|
+
__download;
|
|
6271
|
+
static getType() {
|
|
6272
|
+
return "download-link";
|
|
6273
|
+
}
|
|
6274
|
+
static clone(node) {
|
|
6275
|
+
return new _DownloadLinkNode(
|
|
6276
|
+
node.getURL(),
|
|
6277
|
+
node.__download,
|
|
6278
|
+
{
|
|
6279
|
+
rel: node.getRel(),
|
|
6280
|
+
target: node.getTarget(),
|
|
6281
|
+
title: node.getTitle()
|
|
6282
|
+
},
|
|
6283
|
+
node.__key
|
|
6284
|
+
);
|
|
6285
|
+
}
|
|
6286
|
+
constructor(url, download = null, attributes, key) {
|
|
6287
|
+
super(url, attributes, key);
|
|
6288
|
+
this.__download = download;
|
|
6289
|
+
}
|
|
6290
|
+
getDownload() {
|
|
6291
|
+
return this.__download;
|
|
6292
|
+
}
|
|
6293
|
+
setDownload(download) {
|
|
6294
|
+
const writable = this.getWritable();
|
|
6295
|
+
writable.__download = download;
|
|
6296
|
+
return this;
|
|
6297
|
+
}
|
|
6298
|
+
createDOM(config) {
|
|
6299
|
+
const dom = super.createDOM(config);
|
|
6300
|
+
this.applyDownloadDOM(dom);
|
|
6301
|
+
return dom;
|
|
6302
|
+
}
|
|
6303
|
+
updateLinkDOM(prevNode, anchorElem, config) {
|
|
6304
|
+
super.updateLinkDOM(prevNode, anchorElem, config);
|
|
6305
|
+
this.applyDownloadDOM(anchorElem);
|
|
6306
|
+
}
|
|
6307
|
+
exportJSON() {
|
|
6308
|
+
return {
|
|
6309
|
+
...super.exportJSON(),
|
|
6310
|
+
type: _DownloadLinkNode.getType(),
|
|
6311
|
+
version: 1,
|
|
6312
|
+
download: this.__download
|
|
6313
|
+
};
|
|
6314
|
+
}
|
|
6315
|
+
static importJSON(serializedNode) {
|
|
6316
|
+
const node = new _DownloadLinkNode(
|
|
6317
|
+
serializedNode.url,
|
|
6318
|
+
serializedNode.download,
|
|
6319
|
+
{
|
|
6320
|
+
rel: serializedNode.rel ?? null,
|
|
6321
|
+
target: serializedNode.target ?? null,
|
|
6322
|
+
title: serializedNode.title ?? null
|
|
6323
|
+
},
|
|
6324
|
+
serializedNode.key
|
|
6325
|
+
);
|
|
6326
|
+
return node;
|
|
6327
|
+
}
|
|
6328
|
+
applyDownloadDOM(dom) {
|
|
6329
|
+
if (dom instanceof HTMLAnchorElement) {
|
|
6330
|
+
if (this.__download === null) {
|
|
6331
|
+
dom.removeAttribute("download");
|
|
6332
|
+
} else {
|
|
6333
|
+
dom.setAttribute("download", this.__download);
|
|
6334
|
+
}
|
|
6335
|
+
}
|
|
6336
|
+
}
|
|
6337
|
+
};
|
|
6338
|
+
}
|
|
6339
|
+
});
|
|
6248
6340
|
function $convertMentionElement(domNode) {
|
|
6249
6341
|
const textContent = domNode.textContent;
|
|
6250
6342
|
if (textContent !== null) {
|
|
@@ -6357,6 +6449,7 @@ var init_nodes = __esm({
|
|
|
6357
6449
|
init_layout_container_node();
|
|
6358
6450
|
init_layout_item_node();
|
|
6359
6451
|
init_list_with_color_node();
|
|
6452
|
+
init_download_link_node();
|
|
6360
6453
|
init_mention_node();
|
|
6361
6454
|
nodes = [
|
|
6362
6455
|
richText.HeadingNode,
|
|
@@ -6372,6 +6465,7 @@ var init_nodes = __esm({
|
|
|
6372
6465
|
ListWithColorNode,
|
|
6373
6466
|
list.ListItemNode,
|
|
6374
6467
|
link.LinkNode,
|
|
6468
|
+
DownloadLinkNode,
|
|
6375
6469
|
overflow.OverflowNode,
|
|
6376
6470
|
hashtag.HashtagNode,
|
|
6377
6471
|
table.TableNode,
|
|
@@ -28024,6 +28118,44 @@ var init_url = __esm({
|
|
|
28024
28118
|
);
|
|
28025
28119
|
}
|
|
28026
28120
|
});
|
|
28121
|
+
function shouldTreatUrlAsDownload(url) {
|
|
28122
|
+
if (typeof url !== "string") return false;
|
|
28123
|
+
const u = url.toLowerCase();
|
|
28124
|
+
if (u.includes("/api/uploads/") || u.includes("/uploads/") || u.includes("/api/admin/uploads/") || u.includes("/admin/uploads/"))
|
|
28125
|
+
return true;
|
|
28126
|
+
return /\.(pdf|doc|docx|xls|xlsx|csv|zip|rar|7z|txt|rtf|png|jpg|jpeg|gif|webp|mp3|wav|mp4|mov|avi)(\?.*)?$/.test(
|
|
28127
|
+
u
|
|
28128
|
+
);
|
|
28129
|
+
}
|
|
28130
|
+
function inferDownloadFileName(url) {
|
|
28131
|
+
try {
|
|
28132
|
+
const path = url.split("?")[0] ?? "";
|
|
28133
|
+
const last = path.split("/").filter(Boolean).pop();
|
|
28134
|
+
return last ? decodeURIComponent(last) : "download";
|
|
28135
|
+
} catch {
|
|
28136
|
+
return "download";
|
|
28137
|
+
}
|
|
28138
|
+
}
|
|
28139
|
+
function getCookieValue(name) {
|
|
28140
|
+
if (typeof document === "undefined") return null;
|
|
28141
|
+
const row = document.cookie.split("; ").find((item) => item.startsWith(`${name}=`));
|
|
28142
|
+
if (!row) return null;
|
|
28143
|
+
return row.split("=").slice(1).join("=") || null;
|
|
28144
|
+
}
|
|
28145
|
+
function buildHrefFromJsDownloadArg(jsArg) {
|
|
28146
|
+
const firstSegment = typeof window !== "undefined" ? window.location.pathname.split("/").filter(Boolean)[0] ?? "" : "";
|
|
28147
|
+
const serveBase = firstSegment === "admin" ? "/api/admin/uploads/serve" : "/api/uploads/serve";
|
|
28148
|
+
const arg = jsArg.trim();
|
|
28149
|
+
if (!arg) return "about:blank";
|
|
28150
|
+
if (/^https?:\/\//i.test(arg)) return arg;
|
|
28151
|
+
if (arg.startsWith("/api/")) return arg;
|
|
28152
|
+
if (arg.startsWith("images/") || arg.startsWith("files/")) {
|
|
28153
|
+
return `${serveBase}/${arg}`;
|
|
28154
|
+
}
|
|
28155
|
+
const m = arg.match(/(images|files)\/.+/i);
|
|
28156
|
+
if (m?.[0]) return `${serveBase}/${m[0]}`;
|
|
28157
|
+
return "about:blank";
|
|
28158
|
+
}
|
|
28027
28159
|
function FloatingLinkEditor({
|
|
28028
28160
|
editor,
|
|
28029
28161
|
isLink,
|
|
@@ -28037,6 +28169,9 @@ function FloatingLinkEditor({
|
|
|
28037
28169
|
const [linkUrl, setLinkUrl] = React21.useState("");
|
|
28038
28170
|
const [editedLinkUrl, setEditedLinkUrl] = React21.useState("https://");
|
|
28039
28171
|
const [lastSelection, setLastSelection] = React21.useState(null);
|
|
28172
|
+
const [isUploadingFile, setIsUploadingFile] = React21.useState(false);
|
|
28173
|
+
const fileInputRef = React21.useRef(null);
|
|
28174
|
+
const { onUploadFile } = useEditorUploads();
|
|
28040
28175
|
const $updateLinkEditor = React21.useCallback(() => {
|
|
28041
28176
|
const selection = lexical.$getSelection();
|
|
28042
28177
|
let linkNode = null;
|
|
@@ -28230,9 +28365,11 @@ function FloatingLinkEditor({
|
|
|
28230
28365
|
setIsLinkEditMode(false);
|
|
28231
28366
|
}
|
|
28232
28367
|
};
|
|
28233
|
-
const handleLinkSubmission = () => {
|
|
28234
|
-
const
|
|
28235
|
-
|
|
28368
|
+
const handleLinkSubmission = (submittedUrl, originalFileName) => {
|
|
28369
|
+
const rawUrl = typeof submittedUrl === "string" ? submittedUrl : editedLinkUrl;
|
|
28370
|
+
const url = sanitizeUrl(rawUrl);
|
|
28371
|
+
const downloadFileName = originalFileName || (shouldTreatUrlAsDownload(url) ? inferDownloadFileName(url) : null);
|
|
28372
|
+
if (url && url !== "about:blank" && url !== "https://" && url !== "http://") {
|
|
28236
28373
|
editor.update(() => {
|
|
28237
28374
|
let selection = lexical.$getSelection();
|
|
28238
28375
|
if (!selection && lastSelection !== null) {
|
|
@@ -28257,8 +28394,11 @@ function FloatingLinkEditor({
|
|
|
28257
28394
|
const existingLinkNode = utils.$findMatchingParent(node, link.$isLinkNode) || (link.$isLinkNode(node.getParent()) ? node.getParent() : null);
|
|
28258
28395
|
if (existingLinkNode) {
|
|
28259
28396
|
existingLinkNode.setURL(url);
|
|
28397
|
+
if (downloadFileName && $isDownloadLinkNode(existingLinkNode)) {
|
|
28398
|
+
existingLinkNode.setDownload(downloadFileName);
|
|
28399
|
+
}
|
|
28260
28400
|
} else {
|
|
28261
|
-
const linkNode = link.$createLinkNode(url);
|
|
28401
|
+
const linkNode = downloadFileName ? $createDownloadLinkNode(url, downloadFileName) : link.$createLinkNode(url);
|
|
28262
28402
|
utils.$wrapNodeInElement(node, () => linkNode);
|
|
28263
28403
|
}
|
|
28264
28404
|
}
|
|
@@ -28267,25 +28407,114 @@ function FloatingLinkEditor({
|
|
|
28267
28407
|
editor.dispatchCommand(link.TOGGLE_LINK_COMMAND, url);
|
|
28268
28408
|
const parent = getSelectedNode(selection).getParent();
|
|
28269
28409
|
if (link.$isAutoLinkNode(parent)) {
|
|
28270
|
-
const linkNode =
|
|
28410
|
+
const linkNode = downloadFileName ? $createDownloadLinkNode(parent.getURL(), downloadFileName, {
|
|
28411
|
+
rel: parent.__rel,
|
|
28412
|
+
target: parent.__target,
|
|
28413
|
+
title: parent.__title
|
|
28414
|
+
}) : link.$createLinkNode(parent.getURL(), {
|
|
28271
28415
|
rel: parent.__rel,
|
|
28272
28416
|
target: parent.__target,
|
|
28273
28417
|
title: parent.__title
|
|
28274
28418
|
});
|
|
28275
28419
|
parent.replace(linkNode, true);
|
|
28276
28420
|
}
|
|
28421
|
+
if (downloadFileName) {
|
|
28422
|
+
const selectedNode = getSelectedNode(selection);
|
|
28423
|
+
const linkNode = utils.$findMatchingParent(selectedNode, link.$isLinkNode) || (link.$isLinkNode(selectedNode) ? selectedNode : null);
|
|
28424
|
+
if (linkNode) {
|
|
28425
|
+
const currentText = linkNode.getTextContent();
|
|
28426
|
+
const targetText = originalFileName || downloadFileName;
|
|
28427
|
+
const downloadLinkNode = $createDownloadLinkNode(url, downloadFileName, {
|
|
28428
|
+
rel: linkNode.getRel(),
|
|
28429
|
+
target: linkNode.getTarget(),
|
|
28430
|
+
title: linkNode.getTitle()
|
|
28431
|
+
});
|
|
28432
|
+
if (currentText === url && targetText) {
|
|
28433
|
+
downloadLinkNode.append(lexical.$createTextNode(targetText));
|
|
28434
|
+
} else {
|
|
28435
|
+
const children = linkNode.getChildren();
|
|
28436
|
+
if (children.length > 0) {
|
|
28437
|
+
downloadLinkNode.append(...children);
|
|
28438
|
+
}
|
|
28439
|
+
}
|
|
28440
|
+
linkNode.replace(downloadLinkNode);
|
|
28441
|
+
}
|
|
28442
|
+
}
|
|
28277
28443
|
}
|
|
28278
28444
|
});
|
|
28279
28445
|
setEditedLinkUrl("https://");
|
|
28280
28446
|
setIsLinkEditMode(false);
|
|
28281
28447
|
}
|
|
28282
28448
|
};
|
|
28449
|
+
const handlePickLocalFile = () => {
|
|
28450
|
+
if (isUploadingFile) return;
|
|
28451
|
+
fileInputRef.current?.click();
|
|
28452
|
+
};
|
|
28453
|
+
const handleUploadLocalFile = async (event) => {
|
|
28454
|
+
const file = event.target.files?.[0];
|
|
28455
|
+
if (!file) return;
|
|
28456
|
+
try {
|
|
28457
|
+
setIsUploadingFile(true);
|
|
28458
|
+
let uploadedUrl = void 0;
|
|
28459
|
+
if (onUploadFile) {
|
|
28460
|
+
const result = await onUploadFile(file);
|
|
28461
|
+
if (result.error) throw new Error(result.error);
|
|
28462
|
+
uploadedUrl = result.url;
|
|
28463
|
+
} else {
|
|
28464
|
+
const formData = new FormData();
|
|
28465
|
+
formData.append("file", file);
|
|
28466
|
+
const firstSegment = typeof window !== "undefined" ? window.location.pathname.split("/").filter(Boolean)[0] ?? "" : "";
|
|
28467
|
+
const pathPart = firstSegment === "admin" ? "/admin/uploads" : "/uploads";
|
|
28468
|
+
const endpoint = firstSegment === "admin" ? `/admin/api${pathPart}` : `/api${pathPart}`;
|
|
28469
|
+
const userId = getCookieValue("app_user_id");
|
|
28470
|
+
const authToken = getCookieValue("auth-token");
|
|
28471
|
+
const headers = {};
|
|
28472
|
+
if (userId) headers["X-User-Id"] = userId;
|
|
28473
|
+
if (authToken) headers["Authorization"] = `Bearer ${authToken}`;
|
|
28474
|
+
const res = await fetch(endpoint, {
|
|
28475
|
+
method: "POST",
|
|
28476
|
+
credentials: "include",
|
|
28477
|
+
headers: Object.keys(headers).length > 0 ? headers : void 0,
|
|
28478
|
+
body: formData
|
|
28479
|
+
});
|
|
28480
|
+
if (!res.ok) {
|
|
28481
|
+
throw new Error(`Upload failed: HTTP ${res.status}`);
|
|
28482
|
+
}
|
|
28483
|
+
const payload = await res.json();
|
|
28484
|
+
uploadedUrl = payload?.data?.url;
|
|
28485
|
+
if (!payload?.success || !uploadedUrl) {
|
|
28486
|
+
throw new Error(payload?.message || payload?.error || "Upload failed");
|
|
28487
|
+
}
|
|
28488
|
+
}
|
|
28489
|
+
if (uploadedUrl) {
|
|
28490
|
+
setEditedLinkUrl(uploadedUrl);
|
|
28491
|
+
handleLinkSubmission(uploadedUrl, file.name);
|
|
28492
|
+
}
|
|
28493
|
+
} catch (error) {
|
|
28494
|
+
console.error("[FloatingLinkEditor] Upload local file failed:", error);
|
|
28495
|
+
} finally {
|
|
28496
|
+
setIsUploadingFile(false);
|
|
28497
|
+
if (fileInputRef.current) {
|
|
28498
|
+
fileInputRef.current.value = "";
|
|
28499
|
+
}
|
|
28500
|
+
}
|
|
28501
|
+
};
|
|
28283
28502
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
28284
28503
|
"div",
|
|
28285
28504
|
{
|
|
28286
28505
|
ref: editorRef,
|
|
28287
28506
|
className: "editor-floating-link-editor",
|
|
28288
|
-
children: isLinkEditMode || isLink ? isLinkEditMode ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "editor-floating-link-editor__input-container", children: [
|
|
28507
|
+
children: isLinkEditMode || isLink ? isLinkEditMode ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "editor-floating-link-editor__input-container", children: [
|
|
28508
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
28509
|
+
"input",
|
|
28510
|
+
{
|
|
28511
|
+
ref: fileInputRef,
|
|
28512
|
+
type: "file",
|
|
28513
|
+
className: "hidden",
|
|
28514
|
+
onChange: handleUploadLocalFile,
|
|
28515
|
+
accept: ".pdf,.doc,.docx,.xls,.xlsx,.csv,.rtf,.txt,.zip,.rar,.7z,.ppt,.pptx,.jpg,.jpeg,.png,.gif,.webp,.svg,.mp3,.wav,.mp4,.mov,.avi"
|
|
28516
|
+
}
|
|
28517
|
+
),
|
|
28289
28518
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
28290
28519
|
Input,
|
|
28291
28520
|
{
|
|
@@ -28296,6 +28525,18 @@ function FloatingLinkEditor({
|
|
|
28296
28525
|
className: "editor-flex-grow"
|
|
28297
28526
|
}
|
|
28298
28527
|
),
|
|
28528
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
28529
|
+
Button,
|
|
28530
|
+
{
|
|
28531
|
+
size: "icon",
|
|
28532
|
+
variant: "ghost",
|
|
28533
|
+
onClick: handlePickLocalFile,
|
|
28534
|
+
className: "editor-shrink-0",
|
|
28535
|
+
disabled: isUploadingFile,
|
|
28536
|
+
title: isUploadingFile ? "Uploading..." : "Upload file t\u1EEB thi\u1EBFt b\u1ECB",
|
|
28537
|
+
children: isUploadingFile ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "editor-icon-sm animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Upload, { className: "editor-icon-sm" })
|
|
28538
|
+
}
|
|
28539
|
+
),
|
|
28299
28540
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
28300
28541
|
Button,
|
|
28301
28542
|
{
|
|
@@ -28313,22 +28554,39 @@ function FloatingLinkEditor({
|
|
|
28313
28554
|
Button,
|
|
28314
28555
|
{
|
|
28315
28556
|
size: "icon",
|
|
28316
|
-
onClick: handleLinkSubmission,
|
|
28557
|
+
onClick: () => handleLinkSubmission(),
|
|
28317
28558
|
className: "editor-shrink-0",
|
|
28318
28559
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "editor-icon-sm" })
|
|
28319
28560
|
}
|
|
28320
28561
|
)
|
|
28321
|
-
] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "editor-floating-link-editor__view-container", children: [
|
|
28322
|
-
|
|
28323
|
-
|
|
28324
|
-
|
|
28325
|
-
|
|
28326
|
-
|
|
28327
|
-
|
|
28328
|
-
|
|
28329
|
-
|
|
28562
|
+
] }) }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "editor-floating-link-editor__view-container", children: [
|
|
28563
|
+
(() => {
|
|
28564
|
+
let href = sanitizeUrl(linkUrl);
|
|
28565
|
+
const jsDownloadMatch = typeof linkUrl === "string" ? linkUrl.match(/^javascript:download\(\s*(['"])(.*?)\1\s*\)\s*$/i) : null;
|
|
28566
|
+
let downloadAttr;
|
|
28567
|
+
if (jsDownloadMatch) {
|
|
28568
|
+
const jsArg = jsDownloadMatch[2] ?? "";
|
|
28569
|
+
href = buildHrefFromJsDownloadArg(jsArg);
|
|
28570
|
+
if (href !== "about:blank") {
|
|
28571
|
+
downloadAttr = inferDownloadFileName(href);
|
|
28572
|
+
}
|
|
28573
|
+
} else if (shouldTreatUrlAsDownload(href)) {
|
|
28574
|
+
downloadAttr = inferDownloadFileName(href);
|
|
28330
28575
|
}
|
|
28331
|
-
|
|
28576
|
+
const isDownload = typeof downloadAttr === "string" && downloadAttr.length > 0;
|
|
28577
|
+
const text = jsDownloadMatch ? "Download" : shouldTreatUrlAsDownload(href) ? inferDownloadFileName(href) : href === "about:blank" ? "Invalid URL" : linkUrl;
|
|
28578
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
28579
|
+
"a",
|
|
28580
|
+
{
|
|
28581
|
+
href,
|
|
28582
|
+
download: downloadAttr,
|
|
28583
|
+
target: isDownload ? "_self" : "_blank",
|
|
28584
|
+
rel: isDownload ? void 0 : "noopener noreferrer",
|
|
28585
|
+
className: "editor-floating-link-editor__link",
|
|
28586
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(TypographyPSmall, { className: "editor-truncate", children: text })
|
|
28587
|
+
}
|
|
28588
|
+
);
|
|
28589
|
+
})(),
|
|
28332
28590
|
/* @__PURE__ */ jsxRuntime.jsxs(Flex, { gap: 0, className: "editor-shrink-0", children: [
|
|
28333
28591
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
28334
28592
|
Button,
|
|
@@ -28642,6 +28900,8 @@ var init_floating_link_editor_plugin = __esm({
|
|
|
28642
28900
|
init_flex();
|
|
28643
28901
|
init_typography();
|
|
28644
28902
|
init_image_node();
|
|
28903
|
+
init_download_link_node();
|
|
28904
|
+
init_uploads_context();
|
|
28645
28905
|
}
|
|
28646
28906
|
});
|
|
28647
28907
|
|