@underverse-ui/underverse 1.0.22 → 1.0.24
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/dist/index.cjs +265 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +35 -2
- package/dist/index.d.ts +35 -2
- package/dist/index.js +266 -25
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -166,6 +166,7 @@ __export(index_exports, {
|
|
|
166
166
|
Tooltip: () => Tooltip,
|
|
167
167
|
TranslationProvider: () => TranslationProvider,
|
|
168
168
|
UEditor: () => UEditor_default,
|
|
169
|
+
UEditorPrepareContentForSaveError: () => UEditorPrepareContentForSaveError,
|
|
169
170
|
UnderverseProvider: () => UnderverseProvider,
|
|
170
171
|
VARIANT_STYLES_ALERT: () => VARIANT_STYLES_ALERT,
|
|
171
172
|
VARIANT_STYLES_BTN: () => VARIANT_STYLES_BTN,
|
|
@@ -176,6 +177,7 @@ __export(index_exports, {
|
|
|
176
177
|
getAnimationStyles: () => getAnimationStyles,
|
|
177
178
|
getUnderverseMessages: () => getUnderverseMessages,
|
|
178
179
|
injectAnimationStyles: () => injectAnimationStyles,
|
|
180
|
+
prepareUEditorContentForSave: () => prepareUEditorContentForSave,
|
|
179
181
|
shadcnAnimationStyles: () => shadcnAnimationStyles2,
|
|
180
182
|
underverseMessages: () => underverseMessages,
|
|
181
183
|
useFormField: () => useFormField,
|
|
@@ -19430,6 +19432,14 @@ function useStickyColumns(columns, visibleKeys) {
|
|
|
19430
19432
|
}
|
|
19431
19433
|
return positions;
|
|
19432
19434
|
}, [leafColumns]);
|
|
19435
|
+
const { leftBoundaryKey, rightBoundaryKey } = import_react36.default.useMemo(() => {
|
|
19436
|
+
const leftFixed = leafColumns.filter((c) => c.fixed === "left");
|
|
19437
|
+
const rightFixed = leafColumns.filter((c) => c.fixed === "right");
|
|
19438
|
+
return {
|
|
19439
|
+
leftBoundaryKey: leftFixed.length > 0 ? leftFixed[leftFixed.length - 1].key : null,
|
|
19440
|
+
rightBoundaryKey: rightFixed.length > 0 ? rightFixed[0].key : null
|
|
19441
|
+
};
|
|
19442
|
+
}, [leafColumns]);
|
|
19433
19443
|
const getStickyColumnStyle = import_react36.default.useCallback(
|
|
19434
19444
|
(col) => {
|
|
19435
19445
|
if (!col.fixed) return {};
|
|
@@ -19441,24 +19451,38 @@ function useStickyColumns(columns, visibleKeys) {
|
|
|
19441
19451
|
},
|
|
19442
19452
|
[stickyPositions]
|
|
19443
19453
|
);
|
|
19444
|
-
const
|
|
19445
|
-
|
|
19446
|
-
|
|
19447
|
-
|
|
19448
|
-
|
|
19449
|
-
col.fixed === "right" &&
|
|
19450
|
-
|
|
19451
|
-
|
|
19452
|
-
|
|
19453
|
-
|
|
19454
|
-
|
|
19455
|
-
|
|
19456
|
-
|
|
19457
|
-
|
|
19458
|
-
col.fixed
|
|
19459
|
-
|
|
19460
|
-
|
|
19461
|
-
|
|
19454
|
+
const getBoundaryShadowClass = import_react36.default.useCallback(
|
|
19455
|
+
(col) => {
|
|
19456
|
+
if (col.fixed === "left" && col.key === leftBoundaryKey) {
|
|
19457
|
+
return "border-r border-border/80 shadow-[10px_0_16px_-10px_rgba(0,0,0,0.55)]";
|
|
19458
|
+
}
|
|
19459
|
+
if (col.fixed === "right" && col.key === rightBoundaryKey) {
|
|
19460
|
+
return "border-l border-border/80 shadow-[-10px_0_16px_-10px_rgba(0,0,0,0.55)]";
|
|
19461
|
+
}
|
|
19462
|
+
return "";
|
|
19463
|
+
},
|
|
19464
|
+
[leftBoundaryKey, rightBoundaryKey]
|
|
19465
|
+
);
|
|
19466
|
+
const getStickyHeaderClass = import_react36.default.useCallback(
|
|
19467
|
+
(col) => {
|
|
19468
|
+
if (!col.fixed) return "";
|
|
19469
|
+
return cn("sticky", col.fixed === "left" && "left-0", col.fixed === "right" && "right-0", getBoundaryShadowClass(col), "z-50 !bg-muted");
|
|
19470
|
+
},
|
|
19471
|
+
[getBoundaryShadowClass]
|
|
19472
|
+
);
|
|
19473
|
+
const getStickyCellClass = import_react36.default.useCallback(
|
|
19474
|
+
(col, isStripedRow) => {
|
|
19475
|
+
if (!col.fixed) return "";
|
|
19476
|
+
return cn(
|
|
19477
|
+
"sticky z-10",
|
|
19478
|
+
col.fixed === "left" && "left-0",
|
|
19479
|
+
col.fixed === "right" && "right-0",
|
|
19480
|
+
getBoundaryShadowClass(col),
|
|
19481
|
+
isStripedRow ? "!bg-surface-1" : "!bg-surface-0"
|
|
19482
|
+
);
|
|
19483
|
+
},
|
|
19484
|
+
[getBoundaryShadowClass]
|
|
19485
|
+
);
|
|
19462
19486
|
const getStickyHeaderCellStyle = import_react36.default.useCallback(
|
|
19463
19487
|
(headerCell) => {
|
|
19464
19488
|
const col = headerCell.column;
|
|
@@ -21439,7 +21463,7 @@ function useSmartLocale() {
|
|
|
21439
21463
|
}
|
|
21440
21464
|
|
|
21441
21465
|
// ../../components/ui/UEditor/UEditor.tsx
|
|
21442
|
-
var import_react51 = require("react");
|
|
21466
|
+
var import_react51 = __toESM(require("react"), 1);
|
|
21443
21467
|
var import_next_intl6 = require("next-intl");
|
|
21444
21468
|
var import_react52 = require("@tiptap/react");
|
|
21445
21469
|
|
|
@@ -24333,14 +24357,207 @@ var CharacterCountDisplay = ({ editor, maxCharacters }) => {
|
|
|
24333
24357
|
] });
|
|
24334
24358
|
};
|
|
24335
24359
|
|
|
24360
|
+
// ../../components/ui/UEditor/prepare-content-for-save.ts
|
|
24361
|
+
var MIME_EXTENSION_MAP = {
|
|
24362
|
+
"image/png": "png",
|
|
24363
|
+
"image/jpeg": "jpg",
|
|
24364
|
+
"image/webp": "webp",
|
|
24365
|
+
"image/gif": "gif",
|
|
24366
|
+
"image/svg+xml": "svg",
|
|
24367
|
+
"image/bmp": "bmp",
|
|
24368
|
+
"image/x-icon": "ico",
|
|
24369
|
+
"image/avif": "avif"
|
|
24370
|
+
};
|
|
24371
|
+
function isDataImageUrl(value) {
|
|
24372
|
+
return /^data:image\//i.test(value.trim());
|
|
24373
|
+
}
|
|
24374
|
+
function parseDataImageUrl(dataUrl) {
|
|
24375
|
+
const value = dataUrl.trim();
|
|
24376
|
+
if (!isDataImageUrl(value)) return null;
|
|
24377
|
+
const commaIndex = value.indexOf(",");
|
|
24378
|
+
if (commaIndex < 0) return null;
|
|
24379
|
+
const header = value.slice(5, commaIndex);
|
|
24380
|
+
const base64Data = value.slice(commaIndex + 1).trim();
|
|
24381
|
+
if (!/;base64/i.test(header)) return null;
|
|
24382
|
+
const mime = header.split(";")[0]?.trim().toLowerCase();
|
|
24383
|
+
if (!mime || !base64Data) return null;
|
|
24384
|
+
return { mime, base64Data };
|
|
24385
|
+
}
|
|
24386
|
+
function decodeBase64ToBytes(base64Data) {
|
|
24387
|
+
const normalized = base64Data.replace(/\s+/g, "");
|
|
24388
|
+
const binary = atob(normalized);
|
|
24389
|
+
const bytes = new Uint8Array(binary.length);
|
|
24390
|
+
for (let i = 0; i < binary.length; i += 1) {
|
|
24391
|
+
bytes[i] = binary.charCodeAt(i);
|
|
24392
|
+
}
|
|
24393
|
+
return bytes;
|
|
24394
|
+
}
|
|
24395
|
+
function inferFileExtension(mime) {
|
|
24396
|
+
return MIME_EXTENSION_MAP[mime] ?? "bin";
|
|
24397
|
+
}
|
|
24398
|
+
function createFileFromDataImageUrl(dataUrl, index) {
|
|
24399
|
+
const parsed = parseDataImageUrl(dataUrl);
|
|
24400
|
+
if (!parsed) {
|
|
24401
|
+
throw new Error("Invalid data image URL format.");
|
|
24402
|
+
}
|
|
24403
|
+
const bytes = decodeBase64ToBytes(parsed.base64Data);
|
|
24404
|
+
const extension = inferFileExtension(parsed.mime);
|
|
24405
|
+
const name = `ueditor-image-${index + 1}.${extension}`;
|
|
24406
|
+
return new File([bytes], name, { type: parsed.mime });
|
|
24407
|
+
}
|
|
24408
|
+
function normalizeUploadResult(result) {
|
|
24409
|
+
if (typeof result === "string") {
|
|
24410
|
+
const url2 = result.trim();
|
|
24411
|
+
if (!url2) throw new Error("Upload handler returned an empty URL.");
|
|
24412
|
+
return { url: url2 };
|
|
24413
|
+
}
|
|
24414
|
+
if (!result || typeof result !== "object") {
|
|
24415
|
+
throw new Error("Upload handler returned invalid result.");
|
|
24416
|
+
}
|
|
24417
|
+
const url = typeof result.url === "string" ? result.url.trim() : "";
|
|
24418
|
+
if (!url) throw new Error("Upload handler object result is missing `url`.");
|
|
24419
|
+
const { url: _ignoredUrl, ...rest } = result;
|
|
24420
|
+
const meta = Object.keys(rest).length > 0 ? rest : void 0;
|
|
24421
|
+
return { url, meta };
|
|
24422
|
+
}
|
|
24423
|
+
function getErrorReason(error) {
|
|
24424
|
+
if (error instanceof Error && error.message) return error.message;
|
|
24425
|
+
if (typeof error === "string" && error.trim()) return error;
|
|
24426
|
+
return "Unknown upload error.";
|
|
24427
|
+
}
|
|
24428
|
+
function replaceSrcInTag(match, nextSrc) {
|
|
24429
|
+
if (!match.srcAttr) return match.tag;
|
|
24430
|
+
const { start, end, quote } = match.srcAttr;
|
|
24431
|
+
const escaped = quote === '"' ? nextSrc.replace(/"/g, """) : quote === "'" ? nextSrc.replace(/'/g, "'") : nextSrc;
|
|
24432
|
+
const srcAttr = quote ? `src=${quote}${escaped}${quote}` : `src=${escaped}`;
|
|
24433
|
+
return `${match.tag.slice(0, start)}${srcAttr}${match.tag.slice(end)}`;
|
|
24434
|
+
}
|
|
24435
|
+
function collectImgTagMatches(html) {
|
|
24436
|
+
const matches = [];
|
|
24437
|
+
const imgRegex = /<img\b[^>]*>/gi;
|
|
24438
|
+
const srcAttrRegex = /\bsrc\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+))/i;
|
|
24439
|
+
let tagMatch = imgRegex.exec(html);
|
|
24440
|
+
while (tagMatch) {
|
|
24441
|
+
const tag = tagMatch[0];
|
|
24442
|
+
const start = tagMatch.index;
|
|
24443
|
+
const end = start + tag.length;
|
|
24444
|
+
const srcMatch = srcAttrRegex.exec(tag);
|
|
24445
|
+
let srcAttr = null;
|
|
24446
|
+
if (srcMatch) {
|
|
24447
|
+
const value = srcMatch[1] ?? srcMatch[2] ?? srcMatch[3] ?? "";
|
|
24448
|
+
const quote = srcMatch[1] !== void 0 ? '"' : srcMatch[2] !== void 0 ? "'" : "";
|
|
24449
|
+
srcAttr = {
|
|
24450
|
+
start: srcMatch.index,
|
|
24451
|
+
end: srcMatch.index + srcMatch[0].length,
|
|
24452
|
+
value,
|
|
24453
|
+
quote
|
|
24454
|
+
};
|
|
24455
|
+
}
|
|
24456
|
+
matches.push({ start, end, tag, srcAttr });
|
|
24457
|
+
tagMatch = imgRegex.exec(html);
|
|
24458
|
+
}
|
|
24459
|
+
return matches;
|
|
24460
|
+
}
|
|
24461
|
+
var UEditorPrepareContentForSaveError = class extends Error {
|
|
24462
|
+
constructor(result) {
|
|
24463
|
+
super(
|
|
24464
|
+
`Failed to upload ${result.errors.length} image(s): ${result.errors.map((item) => `#${item.index} ${item.reason}`).join("; ")}`
|
|
24465
|
+
);
|
|
24466
|
+
this.name = "UEditorPrepareContentForSaveError";
|
|
24467
|
+
this.result = result;
|
|
24468
|
+
}
|
|
24469
|
+
};
|
|
24470
|
+
async function prepareUEditorContentForSave({
|
|
24471
|
+
html,
|
|
24472
|
+
uploadImageForSave
|
|
24473
|
+
}) {
|
|
24474
|
+
if (!html || !html.includes("<img")) {
|
|
24475
|
+
return { html, uploaded: [], errors: [] };
|
|
24476
|
+
}
|
|
24477
|
+
const imgMatches = collectImgTagMatches(html);
|
|
24478
|
+
if (imgMatches.length === 0) {
|
|
24479
|
+
return { html, uploaded: [], errors: [] };
|
|
24480
|
+
}
|
|
24481
|
+
const base64Candidates = [];
|
|
24482
|
+
for (const match of imgMatches) {
|
|
24483
|
+
if (!match.srcAttr) continue;
|
|
24484
|
+
const src = match.srcAttr.value.trim();
|
|
24485
|
+
if (!isDataImageUrl(src)) continue;
|
|
24486
|
+
base64Candidates.push({
|
|
24487
|
+
id: `${match.start}:${match.end}`,
|
|
24488
|
+
match,
|
|
24489
|
+
index: base64Candidates.length,
|
|
24490
|
+
src
|
|
24491
|
+
});
|
|
24492
|
+
}
|
|
24493
|
+
if (base64Candidates.length === 0) {
|
|
24494
|
+
return { html, uploaded: [], errors: [] };
|
|
24495
|
+
}
|
|
24496
|
+
if (!uploadImageForSave) {
|
|
24497
|
+
return {
|
|
24498
|
+
html,
|
|
24499
|
+
uploaded: [],
|
|
24500
|
+
errors: base64Candidates.map((item) => ({
|
|
24501
|
+
index: item.index,
|
|
24502
|
+
reason: "`uploadImageForSave` is required to transform base64 images before save."
|
|
24503
|
+
}))
|
|
24504
|
+
};
|
|
24505
|
+
}
|
|
24506
|
+
const uploaded = [];
|
|
24507
|
+
const errors = [];
|
|
24508
|
+
const replacements = /* @__PURE__ */ new Map();
|
|
24509
|
+
const uploadResults = await Promise.all(
|
|
24510
|
+
base64Candidates.map(async (candidate) => {
|
|
24511
|
+
try {
|
|
24512
|
+
const file = createFileFromDataImageUrl(candidate.src, candidate.index);
|
|
24513
|
+
const uploadResult = await uploadImageForSave(file);
|
|
24514
|
+
const normalized = normalizeUploadResult(uploadResult);
|
|
24515
|
+
return { candidate, file, ...normalized };
|
|
24516
|
+
} catch (error) {
|
|
24517
|
+
return { candidate, error: getErrorReason(error) };
|
|
24518
|
+
}
|
|
24519
|
+
})
|
|
24520
|
+
);
|
|
24521
|
+
for (const item of uploadResults) {
|
|
24522
|
+
if ("error" in item) {
|
|
24523
|
+
errors.push({
|
|
24524
|
+
index: item.candidate.index,
|
|
24525
|
+
reason: item.error ?? "Unknown upload error."
|
|
24526
|
+
});
|
|
24527
|
+
continue;
|
|
24528
|
+
}
|
|
24529
|
+
replacements.set(item.candidate.id, item.url);
|
|
24530
|
+
uploaded.push({
|
|
24531
|
+
url: item.url,
|
|
24532
|
+
file: item.file,
|
|
24533
|
+
meta: item.meta
|
|
24534
|
+
});
|
|
24535
|
+
}
|
|
24536
|
+
if (replacements.size === 0) {
|
|
24537
|
+
return { html, uploaded, errors };
|
|
24538
|
+
}
|
|
24539
|
+
let transformed = "";
|
|
24540
|
+
let cursor = 0;
|
|
24541
|
+
for (const match of imgMatches) {
|
|
24542
|
+
transformed += html.slice(cursor, match.start);
|
|
24543
|
+
const replacementKey = `${match.start}:${match.end}`;
|
|
24544
|
+
const replacementUrl = replacements.get(replacementKey);
|
|
24545
|
+
transformed += replacementUrl ? replaceSrcInTag(match, replacementUrl) : match.tag;
|
|
24546
|
+
cursor = match.end;
|
|
24547
|
+
}
|
|
24548
|
+
transformed += html.slice(cursor);
|
|
24549
|
+
return { html: transformed, uploaded, errors };
|
|
24550
|
+
}
|
|
24551
|
+
|
|
24336
24552
|
// ../../components/ui/UEditor/UEditor.tsx
|
|
24337
24553
|
var import_jsx_runtime86 = require("react/jsx-runtime");
|
|
24338
|
-
var UEditor = ({
|
|
24554
|
+
var UEditor = import_react51.default.forwardRef(({
|
|
24339
24555
|
content = "",
|
|
24340
24556
|
onChange,
|
|
24341
24557
|
onHtmlChange,
|
|
24342
24558
|
onJsonChange,
|
|
24343
24559
|
uploadImage,
|
|
24560
|
+
uploadImageForSave,
|
|
24344
24561
|
imageInsertMode = "base64",
|
|
24345
24562
|
placeholder,
|
|
24346
24563
|
className,
|
|
@@ -24354,9 +24571,10 @@ var UEditor = ({
|
|
|
24354
24571
|
minHeight = "200px",
|
|
24355
24572
|
maxHeight = "auto",
|
|
24356
24573
|
variant = "default"
|
|
24357
|
-
}) => {
|
|
24574
|
+
}, ref) => {
|
|
24358
24575
|
const t = (0, import_next_intl6.useTranslations)("UEditor");
|
|
24359
24576
|
const effectivePlaceholder = placeholder ?? t("placeholder");
|
|
24577
|
+
const inFlightPrepareRef = (0, import_react51.useRef)(null);
|
|
24360
24578
|
const extensions = (0, import_react51.useMemo)(
|
|
24361
24579
|
() => buildUEditorExtensions({ placeholder: effectivePlaceholder, maxCharacters, uploadImage, imageInsertMode, editable }),
|
|
24362
24580
|
[effectivePlaceholder, maxCharacters, uploadImage, imageInsertMode, editable]
|
|
@@ -24466,6 +24684,28 @@ var UEditor = ({
|
|
|
24466
24684
|
onJsonChange?.(editor2.getJSON());
|
|
24467
24685
|
}
|
|
24468
24686
|
});
|
|
24687
|
+
(0, import_react51.useImperativeHandle)(
|
|
24688
|
+
ref,
|
|
24689
|
+
() => ({
|
|
24690
|
+
prepareContentForSave: async ({ throwOnError = false } = {}) => {
|
|
24691
|
+
if (!inFlightPrepareRef.current) {
|
|
24692
|
+
const htmlSnapshot = editor?.getHTML() ?? content ?? "";
|
|
24693
|
+
inFlightPrepareRef.current = prepareUEditorContentForSave({
|
|
24694
|
+
html: htmlSnapshot,
|
|
24695
|
+
uploadImageForSave
|
|
24696
|
+
}).finally(() => {
|
|
24697
|
+
inFlightPrepareRef.current = null;
|
|
24698
|
+
});
|
|
24699
|
+
}
|
|
24700
|
+
const result = await inFlightPrepareRef.current;
|
|
24701
|
+
if (throwOnError && result.errors.length > 0) {
|
|
24702
|
+
throw new UEditorPrepareContentForSaveError(result);
|
|
24703
|
+
}
|
|
24704
|
+
return result;
|
|
24705
|
+
}
|
|
24706
|
+
}),
|
|
24707
|
+
[content, editor, uploadImageForSave]
|
|
24708
|
+
);
|
|
24469
24709
|
(0, import_react51.useEffect)(() => {
|
|
24470
24710
|
if (editor && content !== editor.getHTML()) {
|
|
24471
24711
|
if (editor.isEmpty && content) {
|
|
@@ -24513,7 +24753,8 @@ var UEditor = ({
|
|
|
24513
24753
|
]
|
|
24514
24754
|
}
|
|
24515
24755
|
);
|
|
24516
|
-
};
|
|
24756
|
+
});
|
|
24757
|
+
UEditor.displayName = "UEditor";
|
|
24517
24758
|
var UEditor_default = UEditor;
|
|
24518
24759
|
|
|
24519
24760
|
// src/index.ts
|
|
@@ -24659,6 +24900,7 @@ function getUnderverseMessages(locale = "en") {
|
|
|
24659
24900
|
Tooltip,
|
|
24660
24901
|
TranslationProvider,
|
|
24661
24902
|
UEditor,
|
|
24903
|
+
UEditorPrepareContentForSaveError,
|
|
24662
24904
|
UnderverseProvider,
|
|
24663
24905
|
VARIANT_STYLES_ALERT,
|
|
24664
24906
|
VARIANT_STYLES_BTN,
|
|
@@ -24669,6 +24911,7 @@ function getUnderverseMessages(locale = "en") {
|
|
|
24669
24911
|
getAnimationStyles,
|
|
24670
24912
|
getUnderverseMessages,
|
|
24671
24913
|
injectAnimationStyles,
|
|
24914
|
+
prepareUEditorContentForSave,
|
|
24672
24915
|
shadcnAnimationStyles,
|
|
24673
24916
|
underverseMessages,
|
|
24674
24917
|
useFormField,
|