@dmitryvim/form-builder 0.2.19 → 0.2.21
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/browser/formbuilder.min.js +251 -97
- package/dist/browser/formbuilder.v0.2.21.min.js +602 -0
- package/dist/cjs/index.cjs +1644 -62
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/index.js +1590 -52
- package/dist/esm/index.js.map +1 -1
- package/dist/form-builder.js +251 -97
- package/dist/types/components/index.d.ts +2 -1
- package/dist/types/components/richinput.d.ts +4 -0
- package/dist/types/components/table.d.ts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/types/config.d.ts +12 -0
- package/dist/types/types/index.d.ts +1 -1
- package/dist/types/types/schema.d.ts +31 -9
- package/dist/types/utils/helpers.d.ts +5 -0
- package/package.json +1 -1
- package/dist/browser/formbuilder.v0.2.19.min.js +0 -448
package/dist/cjs/index.cjs
CHANGED
|
@@ -223,6 +223,11 @@ function pathJoin(base, key) {
|
|
|
223
223
|
function clear(node) {
|
|
224
224
|
while (node.firstChild) node.removeChild(node.firstChild);
|
|
225
225
|
}
|
|
226
|
+
function formatFileSize(bytes) {
|
|
227
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
228
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
229
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
230
|
+
}
|
|
226
231
|
|
|
227
232
|
// src/utils/enable-conditions.ts
|
|
228
233
|
function getValueByPath(data, path) {
|
|
@@ -4773,18 +4778,54 @@ function updateGroupField(element, fieldPath, value, context) {
|
|
|
4773
4778
|
}
|
|
4774
4779
|
|
|
4775
4780
|
// src/components/table.ts
|
|
4781
|
+
function isLegacyMerge(m) {
|
|
4782
|
+
return m !== null && typeof m === "object" && "row" in m && "col" in m && "rowspan" in m && "colspan" in m;
|
|
4783
|
+
}
|
|
4784
|
+
function isValidMergeShape(m) {
|
|
4785
|
+
if (m === null || typeof m !== "object") return false;
|
|
4786
|
+
const o = m;
|
|
4787
|
+
return typeof o.top === "number" && typeof o.left === "number" && typeof o.bottom === "number" && typeof o.right === "number";
|
|
4788
|
+
}
|
|
4789
|
+
function migrateMerge(m) {
|
|
4790
|
+
if (isLegacyMerge(m)) {
|
|
4791
|
+
return {
|
|
4792
|
+
top: m.row,
|
|
4793
|
+
left: m.col,
|
|
4794
|
+
bottom: m.row + m.rowspan - 1,
|
|
4795
|
+
right: m.col + m.colspan - 1
|
|
4796
|
+
};
|
|
4797
|
+
}
|
|
4798
|
+
if (isValidMergeShape(m)) return m;
|
|
4799
|
+
return null;
|
|
4800
|
+
}
|
|
4801
|
+
function migrateMerges(merges) {
|
|
4802
|
+
return merges.map(migrateMerge).filter((m) => m !== null);
|
|
4803
|
+
}
|
|
4776
4804
|
function createEmptyCells(rows, cols) {
|
|
4777
4805
|
return Array.from(
|
|
4778
4806
|
{ length: rows },
|
|
4779
4807
|
() => Array.from({ length: cols }, () => "")
|
|
4780
4808
|
);
|
|
4781
4809
|
}
|
|
4810
|
+
function validateMerges(merges, rows, cols) {
|
|
4811
|
+
for (let i = 0; i < merges.length; i++) {
|
|
4812
|
+
const a = merges[i];
|
|
4813
|
+
if (a.top < 0 || a.left < 0 || a.bottom >= rows || a.right >= cols || a.top > a.bottom || a.left > a.right)
|
|
4814
|
+
return `Merge ${i} out of bounds`;
|
|
4815
|
+
for (let j = i + 1; j < merges.length; j++) {
|
|
4816
|
+
const b = merges[j];
|
|
4817
|
+
if (a.top <= b.bottom && a.bottom >= b.top && a.left <= b.right && a.right >= b.left)
|
|
4818
|
+
return `Merges ${i} and ${j} overlap`;
|
|
4819
|
+
}
|
|
4820
|
+
}
|
|
4821
|
+
return null;
|
|
4822
|
+
}
|
|
4782
4823
|
function getShadowingMerge(row, col, merges) {
|
|
4783
4824
|
for (const m of merges) {
|
|
4784
|
-
if (m.
|
|
4825
|
+
if (m.top === row && m.left === col) {
|
|
4785
4826
|
return null;
|
|
4786
4827
|
}
|
|
4787
|
-
if (row >= m.
|
|
4828
|
+
if (row >= m.top && row <= m.bottom && col >= m.left && col <= m.right) {
|
|
4788
4829
|
return m;
|
|
4789
4830
|
}
|
|
4790
4831
|
}
|
|
@@ -4792,7 +4833,7 @@ function getShadowingMerge(row, col, merges) {
|
|
|
4792
4833
|
}
|
|
4793
4834
|
function getMergeAt(row, col, merges) {
|
|
4794
4835
|
var _a;
|
|
4795
|
-
return (_a = merges.find((m) => m.
|
|
4836
|
+
return (_a = merges.find((m) => m.top === row && m.left === col)) != null ? _a : null;
|
|
4796
4837
|
}
|
|
4797
4838
|
function selectionRange(sel) {
|
|
4798
4839
|
var _a;
|
|
@@ -4868,8 +4909,10 @@ function renderReadonlyTable(data, wrapper) {
|
|
|
4868
4909
|
const merge = getMergeAt(rIdx, cIdx, merges);
|
|
4869
4910
|
const td = document.createElement(rIdx === 0 ? "th" : "td");
|
|
4870
4911
|
if (merge) {
|
|
4871
|
-
|
|
4872
|
-
|
|
4912
|
+
const rowspan = merge.bottom - merge.top + 1;
|
|
4913
|
+
const colspan = merge.right - merge.left + 1;
|
|
4914
|
+
if (rowspan > 1) td.rowSpan = rowspan;
|
|
4915
|
+
if (colspan > 1) td.colSpan = colspan;
|
|
4873
4916
|
}
|
|
4874
4917
|
td.textContent = (_b = rowData[cIdx]) != null ? _b : "";
|
|
4875
4918
|
td.style.cssText = `
|
|
@@ -4956,22 +4999,56 @@ function startCellEditing(span, r, c, getCells, persistValue, selectCell) {
|
|
|
4956
4999
|
span.addEventListener("keydown", onKeyDown);
|
|
4957
5000
|
span.addEventListener("blur", onBlur);
|
|
4958
5001
|
}
|
|
5002
|
+
function ensureSpinKeyframes() {
|
|
5003
|
+
if (document.getElementById("fb-spin-keyframes")) return;
|
|
5004
|
+
const style = document.createElement("style");
|
|
5005
|
+
style.id = "fb-spin-keyframes";
|
|
5006
|
+
style.textContent = `@keyframes fb-spin { to { transform: rotate(360deg); } }`;
|
|
5007
|
+
document.head.appendChild(style);
|
|
5008
|
+
}
|
|
5009
|
+
function showLoadingOverlay(parent, text) {
|
|
5010
|
+
ensureSpinKeyframes();
|
|
5011
|
+
const overlay = document.createElement("div");
|
|
5012
|
+
overlay.className = "fb-table-loading-overlay";
|
|
5013
|
+
overlay.style.cssText = `
|
|
5014
|
+
position: absolute; top: 0; left: 0; right: 0; bottom: 0;
|
|
5015
|
+
background: rgba(255,255,255,0.8); display: flex;
|
|
5016
|
+
align-items: center; justify-content: center; flex-direction: column;
|
|
5017
|
+
gap: 8px; z-index: 100;
|
|
5018
|
+
`;
|
|
5019
|
+
const spinner = document.createElement("div");
|
|
5020
|
+
spinner.style.cssText = `
|
|
5021
|
+
width: 24px; height: 24px; border: 3px solid var(--fb-border-color, #ccc);
|
|
5022
|
+
border-top-color: var(--fb-primary-color, #0066cc); border-radius: 50%;
|
|
5023
|
+
animation: fb-spin 0.8s linear infinite;
|
|
5024
|
+
`;
|
|
5025
|
+
const label = document.createElement("span");
|
|
5026
|
+
label.textContent = text;
|
|
5027
|
+
label.style.cssText = `font-size: var(--fb-font-size-small, 12px); color: var(--fb-text-color, #333);`;
|
|
5028
|
+
overlay.appendChild(spinner);
|
|
5029
|
+
overlay.appendChild(label);
|
|
5030
|
+
parent.appendChild(overlay);
|
|
5031
|
+
return overlay;
|
|
5032
|
+
}
|
|
4959
5033
|
function renderEditTable(element, initialData, pathKey, ctx, wrapper) {
|
|
4960
|
-
var _a, _b;
|
|
5034
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
4961
5035
|
const state = ctx.state;
|
|
4962
5036
|
const instance = ctx.instance;
|
|
4963
|
-
const
|
|
5037
|
+
const mergeAllowed = element.mergeAllowed !== false;
|
|
5038
|
+
const cellsKey = (_b = (_a = element.fieldNames) == null ? void 0 : _a.cells) != null ? _b : "cells";
|
|
5039
|
+
const mergesKey = (_d = (_c = element.fieldNames) == null ? void 0 : _c.merges) != null ? _d : "merges";
|
|
5040
|
+
const cells = initialData.cells.length > 0 ? initialData.cells.map((r) => [...r]) : createEmptyCells((_e = element.rows) != null ? _e : 3, (_f = element.columns) != null ? _f : 3);
|
|
4964
5041
|
let merges = initialData.merges ? [...initialData.merges] : [];
|
|
4965
5042
|
const sel = { anchor: null, focus: null, dragging: false };
|
|
4966
5043
|
const hiddenInput = document.createElement("input");
|
|
4967
5044
|
hiddenInput.type = "hidden";
|
|
4968
5045
|
hiddenInput.name = pathKey;
|
|
4969
|
-
hiddenInput.value = JSON.stringify({ cells, merges });
|
|
5046
|
+
hiddenInput.value = JSON.stringify({ [cellsKey]: cells, [mergesKey]: merges });
|
|
4970
5047
|
wrapper.appendChild(hiddenInput);
|
|
4971
5048
|
function persistValue() {
|
|
4972
|
-
hiddenInput.value = JSON.stringify({ cells, merges });
|
|
5049
|
+
hiddenInput.value = JSON.stringify({ [cellsKey]: cells, [mergesKey]: merges });
|
|
4973
5050
|
if (instance) {
|
|
4974
|
-
instance.triggerOnChange(pathKey, { cells, merges });
|
|
5051
|
+
instance.triggerOnChange(pathKey, { [cellsKey]: cells, [mergesKey]: merges });
|
|
4975
5052
|
}
|
|
4976
5053
|
}
|
|
4977
5054
|
hiddenInput._applyExternalUpdate = (data) => {
|
|
@@ -4997,6 +5074,116 @@ function renderEditTable(element, initialData, pathKey, ctx, wrapper) {
|
|
|
4997
5074
|
table-layout: fixed;
|
|
4998
5075
|
`;
|
|
4999
5076
|
tableWrapper.appendChild(tableEl);
|
|
5077
|
+
const acceptExts = (_h = (_g = element.importAccept) == null ? void 0 : _g.map((ext) => `.${ext.toLowerCase()}`)) != null ? _h : [];
|
|
5078
|
+
async function importFile(file) {
|
|
5079
|
+
var _a2, _b2, _c2;
|
|
5080
|
+
if (!state.config.parseTableFile) return;
|
|
5081
|
+
if (acceptExts.length > 0) {
|
|
5082
|
+
const ext = file.name.toLowerCase().replace(/^.*(\.[^.]+)$/, "$1");
|
|
5083
|
+
if (!acceptExts.includes(ext)) return;
|
|
5084
|
+
}
|
|
5085
|
+
const overlay = showLoadingOverlay(
|
|
5086
|
+
tableWrapper,
|
|
5087
|
+
t("tableImporting", state)
|
|
5088
|
+
);
|
|
5089
|
+
try {
|
|
5090
|
+
const result = await state.config.parseTableFile(file);
|
|
5091
|
+
const importedCells = result.cells;
|
|
5092
|
+
const importedMerges = (_a2 = result.merges) != null ? _a2 : [];
|
|
5093
|
+
if (importedMerges.length > 0) {
|
|
5094
|
+
const err = validateMerges(
|
|
5095
|
+
importedMerges,
|
|
5096
|
+
importedCells.length,
|
|
5097
|
+
(_c2 = (_b2 = importedCells[0]) == null ? void 0 : _b2.length) != null ? _c2 : 0
|
|
5098
|
+
);
|
|
5099
|
+
if (err) throw new Error(err);
|
|
5100
|
+
}
|
|
5101
|
+
cells.length = 0;
|
|
5102
|
+
importedCells.forEach((row) => cells.push([...row]));
|
|
5103
|
+
merges.length = 0;
|
|
5104
|
+
importedMerges.forEach((m) => merges.push({ ...m }));
|
|
5105
|
+
sel.anchor = null;
|
|
5106
|
+
sel.focus = null;
|
|
5107
|
+
persistValue();
|
|
5108
|
+
rebuild();
|
|
5109
|
+
} catch (e) {
|
|
5110
|
+
const errMsg = e instanceof Error ? e.message : String(e);
|
|
5111
|
+
console.error(
|
|
5112
|
+
t("tableImportError", state).replace("{error}", errMsg)
|
|
5113
|
+
);
|
|
5114
|
+
} finally {
|
|
5115
|
+
overlay.remove();
|
|
5116
|
+
}
|
|
5117
|
+
}
|
|
5118
|
+
if (element.importAccept && state.config.parseTableFile) {
|
|
5119
|
+
const importBtn = document.createElement("button");
|
|
5120
|
+
importBtn.type = "button";
|
|
5121
|
+
importBtn.title = t("tableImportFile", state);
|
|
5122
|
+
importBtn.innerHTML = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.44 11.05l-9.19 9.19a6 6 0 01-8.49-8.49l9.19-9.19a4 4 0 015.66 5.66l-9.2 9.19a2 2 0 01-2.83-2.83l8.49-8.48"/></svg>`;
|
|
5123
|
+
importBtn.style.cssText = `
|
|
5124
|
+
position: absolute; top: 2px; left: 2px;
|
|
5125
|
+
width: 20px; height: 20px;
|
|
5126
|
+
padding: 0;
|
|
5127
|
+
display: flex; align-items: center; justify-content: center;
|
|
5128
|
+
color: var(--fb-text-color, #999);
|
|
5129
|
+
border: none;
|
|
5130
|
+
background: transparent;
|
|
5131
|
+
cursor: pointer;
|
|
5132
|
+
z-index: 2;
|
|
5133
|
+
`;
|
|
5134
|
+
importBtn.addEventListener("mouseenter", () => {
|
|
5135
|
+
importBtn.style.color = "var(--fb-primary-color, #0066cc)";
|
|
5136
|
+
});
|
|
5137
|
+
importBtn.addEventListener("mouseleave", () => {
|
|
5138
|
+
importBtn.style.color = "var(--fb-text-color, #999)";
|
|
5139
|
+
});
|
|
5140
|
+
const importInput = document.createElement("input");
|
|
5141
|
+
importInput.type = "file";
|
|
5142
|
+
importInput.accept = acceptExts.join(",");
|
|
5143
|
+
importInput.style.display = "none";
|
|
5144
|
+
tableWrapper.appendChild(importInput);
|
|
5145
|
+
importBtn.addEventListener("click", () => {
|
|
5146
|
+
importInput.click();
|
|
5147
|
+
});
|
|
5148
|
+
importInput.addEventListener("change", () => {
|
|
5149
|
+
var _a2;
|
|
5150
|
+
const file = (_a2 = importInput.files) == null ? void 0 : _a2[0];
|
|
5151
|
+
if (file) importFile(file);
|
|
5152
|
+
importInput.value = "";
|
|
5153
|
+
});
|
|
5154
|
+
tableWrapper.appendChild(importBtn);
|
|
5155
|
+
let dragCounter = 0;
|
|
5156
|
+
tableWrapper.addEventListener("dragenter", (e) => {
|
|
5157
|
+
e.preventDefault();
|
|
5158
|
+
dragCounter++;
|
|
5159
|
+
if (dragCounter === 1) {
|
|
5160
|
+
tableWrapper.style.outline = "2px dashed var(--fb-primary-color, #0066cc)";
|
|
5161
|
+
tableWrapper.style.outlineOffset = "-2px";
|
|
5162
|
+
}
|
|
5163
|
+
});
|
|
5164
|
+
tableWrapper.addEventListener("dragover", (e) => {
|
|
5165
|
+
e.preventDefault();
|
|
5166
|
+
if (e.dataTransfer) e.dataTransfer.dropEffect = "copy";
|
|
5167
|
+
});
|
|
5168
|
+
tableWrapper.addEventListener("dragleave", (e) => {
|
|
5169
|
+
e.preventDefault();
|
|
5170
|
+
dragCounter--;
|
|
5171
|
+
if (dragCounter <= 0) {
|
|
5172
|
+
dragCounter = 0;
|
|
5173
|
+
tableWrapper.style.outline = "";
|
|
5174
|
+
tableWrapper.style.outlineOffset = "";
|
|
5175
|
+
}
|
|
5176
|
+
});
|
|
5177
|
+
tableWrapper.addEventListener("drop", (e) => {
|
|
5178
|
+
var _a2, _b2;
|
|
5179
|
+
e.preventDefault();
|
|
5180
|
+
dragCounter = 0;
|
|
5181
|
+
tableWrapper.style.outline = "";
|
|
5182
|
+
tableWrapper.style.outlineOffset = "";
|
|
5183
|
+
const file = (_b2 = (_a2 = e.dataTransfer) == null ? void 0 : _a2.files) == null ? void 0 : _b2[0];
|
|
5184
|
+
if (file) importFile(file);
|
|
5185
|
+
});
|
|
5186
|
+
}
|
|
5000
5187
|
wrapper.appendChild(tableWrapper);
|
|
5001
5188
|
const contextMenu = document.createElement("div");
|
|
5002
5189
|
contextMenu.style.cssText = `
|
|
@@ -5040,6 +5227,7 @@ function renderEditTable(element, initialData, pathKey, ctx, wrapper) {
|
|
|
5040
5227
|
return btn;
|
|
5041
5228
|
}
|
|
5042
5229
|
function showContextMenu(x, y) {
|
|
5230
|
+
if (!mergeAllowed) return;
|
|
5043
5231
|
contextMenu.innerHTML = "";
|
|
5044
5232
|
contextMenu.style.display = "flex";
|
|
5045
5233
|
const range = selectionRange(sel);
|
|
@@ -5123,11 +5311,11 @@ function renderEditTable(element, initialData, pathKey, ctx, wrapper) {
|
|
|
5123
5311
|
const insertAt = afterIndex !== void 0 ? afterIndex + 1 : cells.length;
|
|
5124
5312
|
cells.splice(insertAt, 0, newRow);
|
|
5125
5313
|
merges = merges.map((m) => {
|
|
5126
|
-
if (m.
|
|
5127
|
-
return { ...m,
|
|
5314
|
+
if (m.top >= insertAt) {
|
|
5315
|
+
return { ...m, top: m.top + 1, bottom: m.bottom + 1 };
|
|
5128
5316
|
}
|
|
5129
|
-
if (m.
|
|
5130
|
-
return { ...m,
|
|
5317
|
+
if (m.top < insertAt && m.bottom >= insertAt) {
|
|
5318
|
+
return { ...m, bottom: m.bottom + 1 };
|
|
5131
5319
|
}
|
|
5132
5320
|
return m;
|
|
5133
5321
|
});
|
|
@@ -5138,19 +5326,18 @@ function renderEditTable(element, initialData, pathKey, ctx, wrapper) {
|
|
|
5138
5326
|
if (cells.length <= 1) return;
|
|
5139
5327
|
const rowToRemove = targetRow !== void 0 ? targetRow : sel.anchor ? sel.anchor.row : cells.length - 1;
|
|
5140
5328
|
merges = merges.map((m) => {
|
|
5141
|
-
|
|
5142
|
-
if (m.
|
|
5143
|
-
|
|
5144
|
-
return { ...m, row: m.row + 1, rowspan: m.rowspan - 1 };
|
|
5329
|
+
if (m.top === rowToRemove && m.bottom === rowToRemove) return null;
|
|
5330
|
+
if (m.top === rowToRemove) {
|
|
5331
|
+
return { ...m, bottom: m.bottom - 1 };
|
|
5145
5332
|
}
|
|
5146
|
-
if (
|
|
5147
|
-
return { ...m,
|
|
5333
|
+
if (m.bottom === rowToRemove) {
|
|
5334
|
+
return { ...m, bottom: m.bottom - 1 };
|
|
5148
5335
|
}
|
|
5149
|
-
if (m.
|
|
5150
|
-
return { ...m,
|
|
5336
|
+
if (m.top < rowToRemove && m.bottom > rowToRemove) {
|
|
5337
|
+
return { ...m, bottom: m.bottom - 1 };
|
|
5151
5338
|
}
|
|
5152
|
-
if (m.
|
|
5153
|
-
return { ...m,
|
|
5339
|
+
if (m.top > rowToRemove) {
|
|
5340
|
+
return { ...m, top: m.top - 1, bottom: m.bottom - 1 };
|
|
5154
5341
|
}
|
|
5155
5342
|
return m;
|
|
5156
5343
|
}).filter((m) => m !== null);
|
|
@@ -5166,11 +5353,11 @@ function renderEditTable(element, initialData, pathKey, ctx, wrapper) {
|
|
|
5166
5353
|
const insertAt = afterIndex !== void 0 ? afterIndex + 1 : (_b2 = (_a2 = cells[0]) == null ? void 0 : _a2.length) != null ? _b2 : 0;
|
|
5167
5354
|
cells.forEach((row) => row.splice(insertAt, 0, ""));
|
|
5168
5355
|
merges = merges.map((m) => {
|
|
5169
|
-
if (m.
|
|
5170
|
-
return { ...m,
|
|
5356
|
+
if (m.left >= insertAt) {
|
|
5357
|
+
return { ...m, left: m.left + 1, right: m.right + 1 };
|
|
5171
5358
|
}
|
|
5172
|
-
if (m.
|
|
5173
|
-
return { ...m,
|
|
5359
|
+
if (m.left < insertAt && m.right >= insertAt) {
|
|
5360
|
+
return { ...m, right: m.right + 1 };
|
|
5174
5361
|
}
|
|
5175
5362
|
return m;
|
|
5176
5363
|
});
|
|
@@ -5181,19 +5368,18 @@ function renderEditTable(element, initialData, pathKey, ctx, wrapper) {
|
|
|
5181
5368
|
if (cells.length === 0 || cells[0].length <= 1) return;
|
|
5182
5369
|
const colToRemove = targetCol !== void 0 ? targetCol : sel.anchor ? sel.anchor.col : cells[0].length - 1;
|
|
5183
5370
|
merges = merges.map((m) => {
|
|
5184
|
-
|
|
5185
|
-
if (m.
|
|
5186
|
-
|
|
5187
|
-
return { ...m, col: m.col + 1, colspan: m.colspan - 1 };
|
|
5371
|
+
if (m.left === colToRemove && m.right === colToRemove) return null;
|
|
5372
|
+
if (m.left === colToRemove) {
|
|
5373
|
+
return { ...m, right: m.right - 1 };
|
|
5188
5374
|
}
|
|
5189
|
-
if (
|
|
5190
|
-
return { ...m,
|
|
5375
|
+
if (m.right === colToRemove) {
|
|
5376
|
+
return { ...m, right: m.right - 1 };
|
|
5191
5377
|
}
|
|
5192
|
-
if (m.
|
|
5193
|
-
return { ...m,
|
|
5378
|
+
if (m.left < colToRemove && m.right > colToRemove) {
|
|
5379
|
+
return { ...m, right: m.right - 1 };
|
|
5194
5380
|
}
|
|
5195
|
-
if (m.
|
|
5196
|
-
return { ...m,
|
|
5381
|
+
if (m.left > colToRemove) {
|
|
5382
|
+
return { ...m, left: m.left - 1, right: m.right - 1 };
|
|
5197
5383
|
}
|
|
5198
5384
|
return m;
|
|
5199
5385
|
}).filter((m) => m !== null);
|
|
@@ -5205,14 +5391,14 @@ function renderEditTable(element, initialData, pathKey, ctx, wrapper) {
|
|
|
5205
5391
|
rebuild();
|
|
5206
5392
|
}
|
|
5207
5393
|
function mergeCells() {
|
|
5394
|
+
var _a2, _b2;
|
|
5395
|
+
if (!mergeAllowed) return;
|
|
5208
5396
|
const range = selectionRange(sel);
|
|
5209
5397
|
if (!range) return;
|
|
5210
5398
|
const { r1, c1, r2, c2 } = range;
|
|
5211
5399
|
if (r1 === r2 && c1 === c2) return;
|
|
5212
5400
|
merges = merges.filter((m) => {
|
|
5213
|
-
const
|
|
5214
|
-
const mEndCol = m.col + m.colspan - 1;
|
|
5215
|
-
const overlaps = m.row <= r2 && mEndRow >= r1 && m.col <= c2 && mEndCol >= c1;
|
|
5401
|
+
const overlaps = m.top <= r2 && m.bottom >= r1 && m.left <= c2 && m.right >= c1;
|
|
5216
5402
|
return !overlaps;
|
|
5217
5403
|
});
|
|
5218
5404
|
const anchorText = cells[r1][c1];
|
|
@@ -5224,16 +5410,24 @@ function renderEditTable(element, initialData, pathKey, ctx, wrapper) {
|
|
|
5224
5410
|
}
|
|
5225
5411
|
}
|
|
5226
5412
|
cells[r1][c1] = anchorText;
|
|
5227
|
-
|
|
5413
|
+
const newMerge = { top: r1, left: c1, bottom: r2, right: c2 };
|
|
5414
|
+
const testMerges = [...merges, newMerge];
|
|
5415
|
+
const err = validateMerges(testMerges, cells.length, (_b2 = (_a2 = cells[0]) == null ? void 0 : _a2.length) != null ? _b2 : 0);
|
|
5416
|
+
if (err) {
|
|
5417
|
+
console.warn("Merge validation failed:", err);
|
|
5418
|
+
return;
|
|
5419
|
+
}
|
|
5420
|
+
merges.push(newMerge);
|
|
5228
5421
|
sel.anchor = { row: r1, col: c1 };
|
|
5229
5422
|
sel.focus = null;
|
|
5230
5423
|
persistValue();
|
|
5231
5424
|
rebuild();
|
|
5232
5425
|
}
|
|
5233
5426
|
function splitCell() {
|
|
5427
|
+
if (!mergeAllowed) return;
|
|
5234
5428
|
if (!sel.anchor) return;
|
|
5235
5429
|
const { row, col } = sel.anchor;
|
|
5236
|
-
const mIdx = merges.findIndex((m) => m.
|
|
5430
|
+
const mIdx = merges.findIndex((m) => m.top === row && m.left === col);
|
|
5237
5431
|
if (mIdx === -1) return;
|
|
5238
5432
|
merges.splice(mIdx, 1);
|
|
5239
5433
|
sel.focus = null;
|
|
@@ -5258,8 +5452,10 @@ function renderEditTable(element, initialData, pathKey, ctx, wrapper) {
|
|
|
5258
5452
|
td.setAttribute("data-row", String(rIdx));
|
|
5259
5453
|
td.setAttribute("data-col", String(cIdx));
|
|
5260
5454
|
if (merge) {
|
|
5261
|
-
|
|
5262
|
-
|
|
5455
|
+
const rowspan = merge.bottom - merge.top + 1;
|
|
5456
|
+
const colspan = merge.right - merge.left + 1;
|
|
5457
|
+
if (rowspan > 1) td.rowSpan = rowspan;
|
|
5458
|
+
if (colspan > 1) td.colSpan = colspan;
|
|
5263
5459
|
}
|
|
5264
5460
|
const inRange = range !== null && rIdx >= range.r1 && rIdx <= range.r2 && cIdx >= range.c1 && cIdx <= range.c2;
|
|
5265
5461
|
const isAnchor = sel.anchor !== null && sel.anchor.row === rIdx && sel.anchor.col === cIdx;
|
|
@@ -5322,6 +5518,7 @@ function renderEditTable(element, initialData, pathKey, ctx, wrapper) {
|
|
|
5322
5518
|
}
|
|
5323
5519
|
});
|
|
5324
5520
|
td.addEventListener("contextmenu", (e) => {
|
|
5521
|
+
if (!mergeAllowed) return;
|
|
5325
5522
|
const currentRange = selectionRange(sel);
|
|
5326
5523
|
const isMulti = currentRange && (currentRange.r1 !== currentRange.r2 || currentRange.c1 !== currentRange.c2);
|
|
5327
5524
|
const isMerged = sel.anchor && getMergeAt(sel.anchor.row, sel.anchor.col, merges);
|
|
@@ -5383,18 +5580,18 @@ function renderEditTable(element, initialData, pathKey, ctx, wrapper) {
|
|
|
5383
5580
|
selectCell(nr, nc);
|
|
5384
5581
|
return;
|
|
5385
5582
|
}
|
|
5386
|
-
if (e.key === "m" && e.ctrlKey && !e.shiftKey) {
|
|
5583
|
+
if (mergeAllowed && e.key === "m" && e.ctrlKey && !e.shiftKey) {
|
|
5387
5584
|
e.preventDefault();
|
|
5388
5585
|
mergeCells();
|
|
5389
5586
|
return;
|
|
5390
5587
|
}
|
|
5391
|
-
if (e.key === "M" && e.ctrlKey && e.shiftKey) {
|
|
5588
|
+
if (mergeAllowed && e.key === "M" && e.ctrlKey && e.shiftKey) {
|
|
5392
5589
|
e.preventDefault();
|
|
5393
5590
|
splitCell();
|
|
5394
5591
|
}
|
|
5395
5592
|
};
|
|
5396
5593
|
tableEl.oncopy = (e) => {
|
|
5397
|
-
var _a3, _b3,
|
|
5594
|
+
var _a3, _b3, _c2, _d2;
|
|
5398
5595
|
const range2 = selectionRange(sel);
|
|
5399
5596
|
if (!range2) return;
|
|
5400
5597
|
e.preventDefault();
|
|
@@ -5415,11 +5612,11 @@ function renderEditTable(element, initialData, pathKey, ctx, wrapper) {
|
|
|
5415
5612
|
}
|
|
5416
5613
|
const tsvText = tsvRows.join("\n");
|
|
5417
5614
|
const htmlText = `<table>${htmlRows.join("")}</table>`;
|
|
5418
|
-
(
|
|
5419
|
-
(
|
|
5615
|
+
(_c2 = e.clipboardData) == null ? void 0 : _c2.setData("text/plain", tsvText);
|
|
5616
|
+
(_d2 = e.clipboardData) == null ? void 0 : _d2.setData("text/html", htmlText);
|
|
5420
5617
|
};
|
|
5421
5618
|
tableEl.onpaste = (e) => {
|
|
5422
|
-
var _a3, _b3,
|
|
5619
|
+
var _a3, _b3, _c2, _d2, _e2, _f2, _g2, _h2, _i;
|
|
5423
5620
|
const anchor = sel.anchor;
|
|
5424
5621
|
if (!anchor) return;
|
|
5425
5622
|
const text = (_b3 = (_a3 = e.clipboardData) == null ? void 0 : _a3.getData("text/plain")) != null ? _b3 : "";
|
|
@@ -5430,15 +5627,15 @@ function renderEditTable(element, initialData, pathKey, ctx, wrapper) {
|
|
|
5430
5627
|
if (editing) {
|
|
5431
5628
|
editing.contentEditable = "inherit";
|
|
5432
5629
|
const r = parseInt(
|
|
5433
|
-
(
|
|
5630
|
+
(_d2 = (_c2 = editing.closest("td")) == null ? void 0 : _c2.getAttribute("data-row")) != null ? _d2 : "0",
|
|
5434
5631
|
10
|
|
5435
5632
|
);
|
|
5436
5633
|
const c = parseInt(
|
|
5437
|
-
(
|
|
5634
|
+
(_f2 = (_e2 = editing.closest("td")) == null ? void 0 : _e2.getAttribute("data-col")) != null ? _f2 : "0",
|
|
5438
5635
|
10
|
|
5439
5636
|
);
|
|
5440
5637
|
if (cells[r]) {
|
|
5441
|
-
cells[r][c] = (
|
|
5638
|
+
cells[r][c] = (_g2 = editing.textContent) != null ? _g2 : "";
|
|
5442
5639
|
}
|
|
5443
5640
|
}
|
|
5444
5641
|
if (!text.trim()) return;
|
|
@@ -5450,7 +5647,7 @@ function renderEditTable(element, initialData, pathKey, ctx, wrapper) {
|
|
|
5450
5647
|
const startC = anchor.col;
|
|
5451
5648
|
const neededRows = startR + pasteRows.length;
|
|
5452
5649
|
while (cells.length < neededRows) {
|
|
5453
|
-
cells.push(Array((_i = (
|
|
5650
|
+
cells.push(Array((_i = (_h2 = cells[0]) == null ? void 0 : _h2.length) != null ? _i : 1).fill(""));
|
|
5454
5651
|
}
|
|
5455
5652
|
const maxPasteCols = Math.max(...pasteRows.map((r) => r.length));
|
|
5456
5653
|
const neededCols = startC + maxPasteCols;
|
|
@@ -5739,10 +5936,50 @@ function defaultTableData(element) {
|
|
|
5739
5936
|
function isTableData(v) {
|
|
5740
5937
|
return v !== null && typeof v === "object" && "cells" in v && Array.isArray(v.cells);
|
|
5741
5938
|
}
|
|
5939
|
+
function isTableDataWithFieldNames(v, cellsKey) {
|
|
5940
|
+
return v !== null && typeof v === "object" && cellsKey in v && Array.isArray(v[cellsKey]);
|
|
5941
|
+
}
|
|
5742
5942
|
function renderTableElement(element, ctx, wrapper, pathKey) {
|
|
5943
|
+
var _a, _b, _c, _d;
|
|
5743
5944
|
const state = ctx.state;
|
|
5744
5945
|
const rawPrefill = ctx.prefill[element.key];
|
|
5745
|
-
const
|
|
5946
|
+
const cellsKey = (_b = (_a = element.fieldNames) == null ? void 0 : _a.cells) != null ? _b : "cells";
|
|
5947
|
+
const mergesKey = (_d = (_c = element.fieldNames) == null ? void 0 : _c.merges) != null ? _d : "merges";
|
|
5948
|
+
let initialData;
|
|
5949
|
+
if (isTableData(rawPrefill)) {
|
|
5950
|
+
initialData = {
|
|
5951
|
+
cells: rawPrefill.cells,
|
|
5952
|
+
merges: rawPrefill.merges ? migrateMerges(rawPrefill.merges) : []
|
|
5953
|
+
};
|
|
5954
|
+
} else if (rawPrefill && isTableDataWithFieldNames(rawPrefill, cellsKey)) {
|
|
5955
|
+
const rawMerges = rawPrefill[mergesKey];
|
|
5956
|
+
initialData = {
|
|
5957
|
+
cells: rawPrefill[cellsKey],
|
|
5958
|
+
merges: rawMerges ? migrateMerges(rawMerges) : []
|
|
5959
|
+
};
|
|
5960
|
+
} else if (isTableData(element.default)) {
|
|
5961
|
+
initialData = {
|
|
5962
|
+
cells: element.default.cells,
|
|
5963
|
+
merges: element.default.merges ? migrateMerges(element.default.merges) : []
|
|
5964
|
+
};
|
|
5965
|
+
} else if (element.default && isTableDataWithFieldNames(element.default, cellsKey)) {
|
|
5966
|
+
const rawMerges = element.default[mergesKey];
|
|
5967
|
+
initialData = {
|
|
5968
|
+
cells: element.default[cellsKey],
|
|
5969
|
+
merges: rawMerges ? migrateMerges(rawMerges) : []
|
|
5970
|
+
};
|
|
5971
|
+
} else {
|
|
5972
|
+
initialData = defaultTableData(element);
|
|
5973
|
+
}
|
|
5974
|
+
if (initialData.merges && initialData.merges.length > 0) {
|
|
5975
|
+
const rows = initialData.cells.length;
|
|
5976
|
+
const cols = rows > 0 ? initialData.cells[0].length : 0;
|
|
5977
|
+
const err = validateMerges(initialData.merges, rows, cols);
|
|
5978
|
+
if (err) {
|
|
5979
|
+
console.warn(`Table "${element.key}": invalid prefill merges stripped (${err})`);
|
|
5980
|
+
initialData = { ...initialData, merges: [] };
|
|
5981
|
+
}
|
|
5982
|
+
}
|
|
5746
5983
|
if (state.config.readonly) {
|
|
5747
5984
|
renderReadonlyTable(initialData, wrapper);
|
|
5748
5985
|
} else {
|
|
@@ -5750,8 +5987,10 @@ function renderTableElement(element, ctx, wrapper, pathKey) {
|
|
|
5750
5987
|
}
|
|
5751
5988
|
}
|
|
5752
5989
|
function validateTableElement(element, key, context) {
|
|
5990
|
+
var _a, _b;
|
|
5753
5991
|
const { scopeRoot, skipValidation } = context;
|
|
5754
5992
|
const errors = [];
|
|
5993
|
+
const cellsKey = (_b = (_a = element.fieldNames) == null ? void 0 : _a.cells) != null ? _b : "cells";
|
|
5755
5994
|
const hiddenInput = scopeRoot.querySelector(
|
|
5756
5995
|
`[name="${key}"]`
|
|
5757
5996
|
);
|
|
@@ -5766,7 +6005,8 @@ function validateTableElement(element, key, context) {
|
|
|
5766
6005
|
return { value: null, errors };
|
|
5767
6006
|
}
|
|
5768
6007
|
if (!skipValidation && element.required) {
|
|
5769
|
-
const
|
|
6008
|
+
const cells = value[cellsKey];
|
|
6009
|
+
const hasContent = cells == null ? void 0 : cells.some(
|
|
5770
6010
|
(row) => row.some((cell) => cell.trim() !== "")
|
|
5771
6011
|
);
|
|
5772
6012
|
if (!hasContent) {
|
|
@@ -5775,8 +6015,11 @@ function validateTableElement(element, key, context) {
|
|
|
5775
6015
|
}
|
|
5776
6016
|
return { value, errors };
|
|
5777
6017
|
}
|
|
5778
|
-
function updateTableField(
|
|
6018
|
+
function updateTableField(element, fieldPath, value, context) {
|
|
6019
|
+
var _a, _b, _c, _d;
|
|
5779
6020
|
const { scopeRoot } = context;
|
|
6021
|
+
const cellsKey = (_b = (_a = element.fieldNames) == null ? void 0 : _a.cells) != null ? _b : "cells";
|
|
6022
|
+
const mergesKey = (_d = (_c = element.fieldNames) == null ? void 0 : _c.merges) != null ? _d : "merges";
|
|
5780
6023
|
const hiddenInput = scopeRoot.querySelector(
|
|
5781
6024
|
`[name="${fieldPath}"]`
|
|
5782
6025
|
);
|
|
@@ -5786,13 +6029,1330 @@ function updateTableField(_element, fieldPath, value, context) {
|
|
|
5786
6029
|
);
|
|
5787
6030
|
return;
|
|
5788
6031
|
}
|
|
5789
|
-
|
|
5790
|
-
|
|
6032
|
+
let tableData = null;
|
|
6033
|
+
if (isTableData(value)) {
|
|
6034
|
+
tableData = {
|
|
6035
|
+
cells: value.cells,
|
|
6036
|
+
merges: value.merges ? migrateMerges(value.merges) : []
|
|
6037
|
+
};
|
|
6038
|
+
} else if (value && isTableDataWithFieldNames(value, cellsKey)) {
|
|
6039
|
+
const rawMerges = value[mergesKey];
|
|
6040
|
+
tableData = {
|
|
6041
|
+
cells: value[cellsKey],
|
|
6042
|
+
merges: rawMerges ? migrateMerges(rawMerges) : []
|
|
6043
|
+
};
|
|
6044
|
+
}
|
|
6045
|
+
if (tableData && hiddenInput._applyExternalUpdate) {
|
|
6046
|
+
hiddenInput._applyExternalUpdate(tableData);
|
|
5791
6047
|
} else {
|
|
5792
6048
|
hiddenInput.value = JSON.stringify(value);
|
|
5793
6049
|
}
|
|
5794
6050
|
}
|
|
5795
6051
|
|
|
6052
|
+
// src/components/richinput.ts
|
|
6053
|
+
function applyAutoExpand2(textarea, backdrop) {
|
|
6054
|
+
textarea.style.overflow = "hidden";
|
|
6055
|
+
textarea.style.resize = "none";
|
|
6056
|
+
const lineCount = (textarea.value.match(/\n/g) || []).length + 1;
|
|
6057
|
+
textarea.rows = Math.max(3, lineCount);
|
|
6058
|
+
const resize = () => {
|
|
6059
|
+
if (!textarea.isConnected) return;
|
|
6060
|
+
textarea.style.height = "0";
|
|
6061
|
+
textarea.style.height = `${textarea.scrollHeight}px`;
|
|
6062
|
+
if (backdrop) {
|
|
6063
|
+
backdrop.style.height = `${textarea.scrollHeight}px`;
|
|
6064
|
+
}
|
|
6065
|
+
};
|
|
6066
|
+
textarea.addEventListener("input", resize);
|
|
6067
|
+
setTimeout(() => {
|
|
6068
|
+
if (textarea.isConnected) resize();
|
|
6069
|
+
}, 0);
|
|
6070
|
+
}
|
|
6071
|
+
function buildFileLabels(files, state) {
|
|
6072
|
+
var _a, _b, _c, _d;
|
|
6073
|
+
const labels = /* @__PURE__ */ new Map();
|
|
6074
|
+
const nameCount = /* @__PURE__ */ new Map();
|
|
6075
|
+
for (const rid of files) {
|
|
6076
|
+
const meta = state.resourceIndex.get(rid);
|
|
6077
|
+
const name = (_a = meta == null ? void 0 : meta.name) != null ? _a : rid;
|
|
6078
|
+
nameCount.set(name, ((_b = nameCount.get(name)) != null ? _b : 0) + 1);
|
|
6079
|
+
}
|
|
6080
|
+
for (const rid of files) {
|
|
6081
|
+
const meta = state.resourceIndex.get(rid);
|
|
6082
|
+
const name = (_c = meta == null ? void 0 : meta.name) != null ? _c : rid;
|
|
6083
|
+
if (((_d = nameCount.get(name)) != null ? _d : 1) > 1 && meta) {
|
|
6084
|
+
labels.set(rid, `${name} (${formatFileSize(meta.size)})`);
|
|
6085
|
+
} else {
|
|
6086
|
+
labels.set(rid, name);
|
|
6087
|
+
}
|
|
6088
|
+
}
|
|
6089
|
+
return labels;
|
|
6090
|
+
}
|
|
6091
|
+
function isImageMeta(meta) {
|
|
6092
|
+
if (!meta) return false;
|
|
6093
|
+
return meta.type.startsWith("image/");
|
|
6094
|
+
}
|
|
6095
|
+
function buildNameToRid(files, state) {
|
|
6096
|
+
const labels = buildFileLabels(files, state);
|
|
6097
|
+
const map = /* @__PURE__ */ new Map();
|
|
6098
|
+
for (const rid of files) {
|
|
6099
|
+
const label = labels.get(rid);
|
|
6100
|
+
if (label) map.set(label, rid);
|
|
6101
|
+
}
|
|
6102
|
+
return map;
|
|
6103
|
+
}
|
|
6104
|
+
function formatMention(name) {
|
|
6105
|
+
if (/\s/.test(name) || name.includes('"')) {
|
|
6106
|
+
return `@"${name.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
6107
|
+
}
|
|
6108
|
+
return `@${name}`;
|
|
6109
|
+
}
|
|
6110
|
+
function findAtTokens(text) {
|
|
6111
|
+
const tokens = [];
|
|
6112
|
+
const len = text.length;
|
|
6113
|
+
let i = 0;
|
|
6114
|
+
while (i < len) {
|
|
6115
|
+
if (text[i] === "@") {
|
|
6116
|
+
if (i > 0 && !/\s/.test(text[i - 1])) {
|
|
6117
|
+
i++;
|
|
6118
|
+
continue;
|
|
6119
|
+
}
|
|
6120
|
+
const start = i;
|
|
6121
|
+
i++;
|
|
6122
|
+
if (i < len && text[i] === '"') {
|
|
6123
|
+
i++;
|
|
6124
|
+
let name = "";
|
|
6125
|
+
while (i < len && text[i] !== '"') {
|
|
6126
|
+
if (text[i] === "\\" && i + 1 < len) {
|
|
6127
|
+
name += text[i + 1];
|
|
6128
|
+
i += 2;
|
|
6129
|
+
} else {
|
|
6130
|
+
name += text[i];
|
|
6131
|
+
i++;
|
|
6132
|
+
}
|
|
6133
|
+
}
|
|
6134
|
+
if (i < len && text[i] === '"') {
|
|
6135
|
+
i++;
|
|
6136
|
+
if (i >= len || /\s/.test(text[i])) {
|
|
6137
|
+
tokens.push({ start, end: i, raw: text.slice(start, i), name });
|
|
6138
|
+
}
|
|
6139
|
+
}
|
|
6140
|
+
} else if (i < len && !/\s/.test(text[i])) {
|
|
6141
|
+
const wordStart = i;
|
|
6142
|
+
while (i < len && !/\s/.test(text[i])) {
|
|
6143
|
+
i++;
|
|
6144
|
+
}
|
|
6145
|
+
const name = text.slice(wordStart, i);
|
|
6146
|
+
tokens.push({ start, end: i, raw: text.slice(start, i), name });
|
|
6147
|
+
}
|
|
6148
|
+
} else {
|
|
6149
|
+
i++;
|
|
6150
|
+
}
|
|
6151
|
+
}
|
|
6152
|
+
return tokens;
|
|
6153
|
+
}
|
|
6154
|
+
function findMentions(text, nameToRid) {
|
|
6155
|
+
if (nameToRid.size === 0) return [];
|
|
6156
|
+
const tokens = findAtTokens(text);
|
|
6157
|
+
const results = [];
|
|
6158
|
+
for (const token of tokens) {
|
|
6159
|
+
const rid = nameToRid.get(token.name);
|
|
6160
|
+
if (rid) {
|
|
6161
|
+
results.push({
|
|
6162
|
+
start: token.start,
|
|
6163
|
+
end: token.end,
|
|
6164
|
+
name: token.name,
|
|
6165
|
+
rid
|
|
6166
|
+
});
|
|
6167
|
+
}
|
|
6168
|
+
}
|
|
6169
|
+
return results;
|
|
6170
|
+
}
|
|
6171
|
+
function replaceFilenamesWithRids(text, nameToRid) {
|
|
6172
|
+
const mentions = findMentions(text, nameToRid);
|
|
6173
|
+
if (mentions.length === 0) return text;
|
|
6174
|
+
let result = "";
|
|
6175
|
+
let lastIdx = 0;
|
|
6176
|
+
for (const m of mentions) {
|
|
6177
|
+
result += text.slice(lastIdx, m.start);
|
|
6178
|
+
result += `@${m.rid}`;
|
|
6179
|
+
lastIdx = m.end;
|
|
6180
|
+
}
|
|
6181
|
+
result += text.slice(lastIdx);
|
|
6182
|
+
return result;
|
|
6183
|
+
}
|
|
6184
|
+
function replaceRidsWithFilenames(text, files, state) {
|
|
6185
|
+
const labels = buildFileLabels(files, state);
|
|
6186
|
+
const ridToLabel = /* @__PURE__ */ new Map();
|
|
6187
|
+
for (const rid of files) {
|
|
6188
|
+
const label = labels.get(rid);
|
|
6189
|
+
if (label) ridToLabel.set(rid, label);
|
|
6190
|
+
}
|
|
6191
|
+
const tokens = findAtTokens(text);
|
|
6192
|
+
if (tokens.length === 0) return text;
|
|
6193
|
+
let result = "";
|
|
6194
|
+
let lastIdx = 0;
|
|
6195
|
+
for (const token of tokens) {
|
|
6196
|
+
result += text.slice(lastIdx, token.start);
|
|
6197
|
+
const label = ridToLabel.get(token.name);
|
|
6198
|
+
if (label) {
|
|
6199
|
+
result += formatMention(label);
|
|
6200
|
+
} else {
|
|
6201
|
+
result += token.raw;
|
|
6202
|
+
}
|
|
6203
|
+
lastIdx = token.end;
|
|
6204
|
+
}
|
|
6205
|
+
result += text.slice(lastIdx);
|
|
6206
|
+
return result;
|
|
6207
|
+
}
|
|
6208
|
+
function renderThumbContent(thumb, rid, meta, state) {
|
|
6209
|
+
clear(thumb);
|
|
6210
|
+
if ((meta == null ? void 0 : meta.file) && isImageMeta(meta)) {
|
|
6211
|
+
const img = document.createElement("img");
|
|
6212
|
+
img.alt = meta.name;
|
|
6213
|
+
img.style.cssText = "width: 100%; height: 100%; object-fit: cover; display: block;";
|
|
6214
|
+
const reader = new FileReader();
|
|
6215
|
+
reader.onload = (e) => {
|
|
6216
|
+
var _a;
|
|
6217
|
+
img.src = ((_a = e.target) == null ? void 0 : _a.result) || "";
|
|
6218
|
+
};
|
|
6219
|
+
reader.readAsDataURL(meta.file);
|
|
6220
|
+
thumb.appendChild(img);
|
|
6221
|
+
} else if (state.config.getThumbnail) {
|
|
6222
|
+
state.config.getThumbnail(rid).then((url) => {
|
|
6223
|
+
var _a;
|
|
6224
|
+
if (!url || !thumb.isConnected) return;
|
|
6225
|
+
const img = document.createElement("img");
|
|
6226
|
+
img.alt = (_a = meta == null ? void 0 : meta.name) != null ? _a : rid;
|
|
6227
|
+
img.src = url;
|
|
6228
|
+
img.style.cssText = "width: 100%; height: 100%; object-fit: cover; display: block;";
|
|
6229
|
+
clear(thumb);
|
|
6230
|
+
thumb.appendChild(img);
|
|
6231
|
+
}).catch((err) => {
|
|
6232
|
+
var _a, _b;
|
|
6233
|
+
(_b = (_a = state.config).onThumbnailError) == null ? void 0 : _b.call(_a, err, rid);
|
|
6234
|
+
});
|
|
6235
|
+
const placeholder = document.createElement("div");
|
|
6236
|
+
placeholder.style.cssText = "width: 100%; height: 100%; background: var(--fb-background-hover-color, #f3f4f6); display: flex; align-items: center; justify-content: center;";
|
|
6237
|
+
placeholder.innerHTML = '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>';
|
|
6238
|
+
thumb.appendChild(placeholder);
|
|
6239
|
+
} else {
|
|
6240
|
+
const icon = document.createElement("div");
|
|
6241
|
+
icon.style.cssText = "width: 100%; height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px; padding: 2px; box-sizing: border-box;";
|
|
6242
|
+
icon.innerHTML = '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>';
|
|
6243
|
+
if (meta == null ? void 0 : meta.name) {
|
|
6244
|
+
const nameEl = document.createElement("span");
|
|
6245
|
+
nameEl.style.cssText = "font-size: 9px; text-align: center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 44px; color: var(--fb-text-color, #111827);";
|
|
6246
|
+
nameEl.textContent = meta.name;
|
|
6247
|
+
icon.appendChild(nameEl);
|
|
6248
|
+
}
|
|
6249
|
+
thumb.appendChild(icon);
|
|
6250
|
+
}
|
|
6251
|
+
}
|
|
6252
|
+
function renderImagePreview(hoverEl, rid, meta, state) {
|
|
6253
|
+
clear(hoverEl);
|
|
6254
|
+
if ((meta == null ? void 0 : meta.file) && isImageMeta(meta)) {
|
|
6255
|
+
const img = document.createElement("img");
|
|
6256
|
+
img.alt = meta.name;
|
|
6257
|
+
img.style.cssText = "max-width: 120px; max-height: 120px; object-fit: contain; display: block;";
|
|
6258
|
+
const reader = new FileReader();
|
|
6259
|
+
reader.onload = (e) => {
|
|
6260
|
+
var _a;
|
|
6261
|
+
img.src = ((_a = e.target) == null ? void 0 : _a.result) || "";
|
|
6262
|
+
};
|
|
6263
|
+
reader.readAsDataURL(meta.file);
|
|
6264
|
+
hoverEl.appendChild(img);
|
|
6265
|
+
} else if (state.config.getThumbnail) {
|
|
6266
|
+
state.config.getThumbnail(rid).then((url) => {
|
|
6267
|
+
var _a;
|
|
6268
|
+
if (!url || !hoverEl.isConnected) return;
|
|
6269
|
+
const img = document.createElement("img");
|
|
6270
|
+
img.alt = (_a = meta == null ? void 0 : meta.name) != null ? _a : rid;
|
|
6271
|
+
img.src = url;
|
|
6272
|
+
img.style.cssText = "max-width: 120px; max-height: 120px; object-fit: contain; display: block;";
|
|
6273
|
+
clear(hoverEl);
|
|
6274
|
+
hoverEl.appendChild(img);
|
|
6275
|
+
}).catch((err) => {
|
|
6276
|
+
var _a, _b;
|
|
6277
|
+
(_b = (_a = state.config).onThumbnailError) == null ? void 0 : _b.call(_a, err, rid);
|
|
6278
|
+
});
|
|
6279
|
+
}
|
|
6280
|
+
}
|
|
6281
|
+
function positionPortalTooltip(tooltip, anchor) {
|
|
6282
|
+
const rect = anchor.getBoundingClientRect();
|
|
6283
|
+
const ttRect = tooltip.getBoundingClientRect();
|
|
6284
|
+
const left = Math.max(
|
|
6285
|
+
4,
|
|
6286
|
+
Math.min(
|
|
6287
|
+
rect.left + rect.width / 2 - ttRect.width / 2,
|
|
6288
|
+
window.innerWidth - ttRect.width - 4
|
|
6289
|
+
)
|
|
6290
|
+
);
|
|
6291
|
+
const topAbove = rect.top - ttRect.height - 8;
|
|
6292
|
+
const topBelow = rect.bottom + 8;
|
|
6293
|
+
const top = topAbove >= 4 ? topAbove : topBelow;
|
|
6294
|
+
tooltip.style.left = `${left}px`;
|
|
6295
|
+
tooltip.style.top = `${Math.max(4, top)}px`;
|
|
6296
|
+
}
|
|
6297
|
+
function showMentionTooltip(anchor, rid, state) {
|
|
6298
|
+
const meta = state.resourceIndex.get(rid);
|
|
6299
|
+
const tooltip = document.createElement("div");
|
|
6300
|
+
tooltip.className = "fb-richinput-portal-tooltip fb-richinput-mention-tooltip";
|
|
6301
|
+
tooltip.style.cssText = `
|
|
6302
|
+
position: fixed;
|
|
6303
|
+
z-index: 99999;
|
|
6304
|
+
background: #fff;
|
|
6305
|
+
border: 1px solid var(--fb-border-color, #d1d5db);
|
|
6306
|
+
border-radius: 8px;
|
|
6307
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
|
6308
|
+
padding: 4px;
|
|
6309
|
+
pointer-events: none;
|
|
6310
|
+
min-width: 60px;
|
|
6311
|
+
max-width: 140px;
|
|
6312
|
+
`;
|
|
6313
|
+
const preview = document.createElement("div");
|
|
6314
|
+
preview.style.cssText = "min-height: 60px; max-height: 120px; display: flex; align-items: center; justify-content: center;";
|
|
6315
|
+
renderImagePreview(preview, rid, meta, state);
|
|
6316
|
+
tooltip.appendChild(preview);
|
|
6317
|
+
document.body.appendChild(tooltip);
|
|
6318
|
+
positionPortalTooltip(tooltip, anchor);
|
|
6319
|
+
return tooltip;
|
|
6320
|
+
}
|
|
6321
|
+
function showFileTooltip(anchor, opts) {
|
|
6322
|
+
var _a, _b;
|
|
6323
|
+
const { rid, state, isReadonly, onMention, onRemove } = opts;
|
|
6324
|
+
const meta = state.resourceIndex.get(rid);
|
|
6325
|
+
const filename = (_a = meta == null ? void 0 : meta.name) != null ? _a : rid;
|
|
6326
|
+
const tooltip = document.createElement("div");
|
|
6327
|
+
tooltip.className = "fb-richinput-portal-tooltip fb-richinput-file-tooltip";
|
|
6328
|
+
tooltip.style.cssText = `
|
|
6329
|
+
position: fixed;
|
|
6330
|
+
z-index: 99999;
|
|
6331
|
+
background: #fff;
|
|
6332
|
+
border: 1px solid var(--fb-border-color, #d1d5db);
|
|
6333
|
+
border-radius: 8px;
|
|
6334
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
|
6335
|
+
padding: 4px;
|
|
6336
|
+
pointer-events: auto;
|
|
6337
|
+
min-width: 80px;
|
|
6338
|
+
max-width: 260px;
|
|
6339
|
+
`;
|
|
6340
|
+
const preview = document.createElement("div");
|
|
6341
|
+
preview.style.cssText = "min-height: 60px; max-height: 120px; display: flex; align-items: center; justify-content: center;";
|
|
6342
|
+
renderImagePreview(preview, rid, meta, state);
|
|
6343
|
+
tooltip.appendChild(preview);
|
|
6344
|
+
const nameEl = document.createElement("div");
|
|
6345
|
+
nameEl.style.cssText = "padding: 4px 6px 2px; font-size: 12px; color: var(--fb-text-color, #111827); word-break: break-word; border-top: 1px solid var(--fb-border-color, #d1d5db);";
|
|
6346
|
+
nameEl.textContent = filename;
|
|
6347
|
+
tooltip.appendChild(nameEl);
|
|
6348
|
+
const actionsRow = document.createElement("div");
|
|
6349
|
+
actionsRow.style.cssText = "display: flex; align-items: center; gap: 2px; padding: 3px 4px 2px; border-top: 1px solid var(--fb-border-color, #d1d5db); justify-content: center;";
|
|
6350
|
+
const btnStyle = "background: none; border: none; cursor: pointer; padding: 3px 5px; border-radius: 4px; color: var(--fb-text-muted-color, #6b7280); display: flex; align-items: center; transition: background 0.1s, color 0.1s;";
|
|
6351
|
+
const btnHoverIn = (btn) => {
|
|
6352
|
+
btn.style.background = "var(--fb-background-hover-color, #f3f4f6)";
|
|
6353
|
+
btn.style.color = "var(--fb-text-color, #111827)";
|
|
6354
|
+
};
|
|
6355
|
+
const btnHoverOut = (btn) => {
|
|
6356
|
+
btn.style.background = "none";
|
|
6357
|
+
btn.style.color = "var(--fb-text-muted-color, #6b7280)";
|
|
6358
|
+
};
|
|
6359
|
+
if (!isReadonly && onMention) {
|
|
6360
|
+
const mentionBtn = document.createElement("button");
|
|
6361
|
+
mentionBtn.type = "button";
|
|
6362
|
+
mentionBtn.title = "Mention";
|
|
6363
|
+
mentionBtn.style.cssText = btnStyle;
|
|
6364
|
+
mentionBtn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><circle cx="12" cy="12" r="4"/><path d="M16 8v5a3 3 0 0 0 6 0v-1a10 10 0 1 0-3.92 7.94"/></svg>';
|
|
6365
|
+
mentionBtn.addEventListener("mouseenter", () => btnHoverIn(mentionBtn));
|
|
6366
|
+
mentionBtn.addEventListener("mouseleave", () => btnHoverOut(mentionBtn));
|
|
6367
|
+
mentionBtn.addEventListener("click", (e) => {
|
|
6368
|
+
e.stopPropagation();
|
|
6369
|
+
onMention();
|
|
6370
|
+
tooltip.remove();
|
|
6371
|
+
});
|
|
6372
|
+
actionsRow.appendChild(mentionBtn);
|
|
6373
|
+
}
|
|
6374
|
+
if (state.config.downloadFile) {
|
|
6375
|
+
const dlBtn = document.createElement("button");
|
|
6376
|
+
dlBtn.type = "button";
|
|
6377
|
+
dlBtn.title = "Download";
|
|
6378
|
+
dlBtn.style.cssText = btnStyle;
|
|
6379
|
+
dlBtn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>';
|
|
6380
|
+
dlBtn.addEventListener("mouseenter", () => btnHoverIn(dlBtn));
|
|
6381
|
+
dlBtn.addEventListener("mouseleave", () => btnHoverOut(dlBtn));
|
|
6382
|
+
dlBtn.addEventListener("click", (e) => {
|
|
6383
|
+
var _a2, _b2;
|
|
6384
|
+
e.stopPropagation();
|
|
6385
|
+
(_b2 = (_a2 = state.config).downloadFile) == null ? void 0 : _b2.call(_a2, rid, filename);
|
|
6386
|
+
});
|
|
6387
|
+
actionsRow.appendChild(dlBtn);
|
|
6388
|
+
}
|
|
6389
|
+
const hasOpenUrl = !!((_b = state.config.getDownloadUrl) != null ? _b : state.config.getThumbnail);
|
|
6390
|
+
if (hasOpenUrl) {
|
|
6391
|
+
const openBtn = document.createElement("button");
|
|
6392
|
+
openBtn.type = "button";
|
|
6393
|
+
openBtn.title = "Open in new window";
|
|
6394
|
+
openBtn.style.cssText = btnStyle;
|
|
6395
|
+
openBtn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>';
|
|
6396
|
+
openBtn.addEventListener("mouseenter", () => btnHoverIn(openBtn));
|
|
6397
|
+
openBtn.addEventListener("mouseleave", () => btnHoverOut(openBtn));
|
|
6398
|
+
openBtn.addEventListener("click", (e) => {
|
|
6399
|
+
var _a2, _b2, _c, _d;
|
|
6400
|
+
e.stopPropagation();
|
|
6401
|
+
if (state.config.getDownloadUrl) {
|
|
6402
|
+
const url = state.config.getDownloadUrl(rid);
|
|
6403
|
+
if (url) {
|
|
6404
|
+
window.open(url, "_blank");
|
|
6405
|
+
} else {
|
|
6406
|
+
(_b2 = (_a2 = state.config).getThumbnail) == null ? void 0 : _b2.call(_a2, rid).then((thumbUrl) => {
|
|
6407
|
+
if (thumbUrl) window.open(thumbUrl, "_blank");
|
|
6408
|
+
}).catch(() => {
|
|
6409
|
+
});
|
|
6410
|
+
}
|
|
6411
|
+
} else {
|
|
6412
|
+
(_d = (_c = state.config).getThumbnail) == null ? void 0 : _d.call(_c, rid).then((url) => {
|
|
6413
|
+
if (url) window.open(url, "_blank");
|
|
6414
|
+
}).catch(() => {
|
|
6415
|
+
});
|
|
6416
|
+
}
|
|
6417
|
+
});
|
|
6418
|
+
actionsRow.appendChild(openBtn);
|
|
6419
|
+
}
|
|
6420
|
+
if (!isReadonly && onRemove) {
|
|
6421
|
+
const removeBtn = document.createElement("button");
|
|
6422
|
+
removeBtn.type = "button";
|
|
6423
|
+
removeBtn.title = "Remove";
|
|
6424
|
+
removeBtn.style.cssText = btnStyle;
|
|
6425
|
+
removeBtn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><line x1="10" y1="11" x2="10" y2="17"/><line x1="14" y1="11" x2="14" y2="17"/></svg>';
|
|
6426
|
+
removeBtn.addEventListener("mouseenter", () => {
|
|
6427
|
+
removeBtn.style.background = "var(--fb-background-hover-color, #f3f4f6)";
|
|
6428
|
+
removeBtn.style.color = "var(--fb-error-color, #ef4444)";
|
|
6429
|
+
});
|
|
6430
|
+
removeBtn.addEventListener("mouseleave", () => btnHoverOut(removeBtn));
|
|
6431
|
+
removeBtn.addEventListener("click", (e) => {
|
|
6432
|
+
e.stopPropagation();
|
|
6433
|
+
tooltip.remove();
|
|
6434
|
+
onRemove();
|
|
6435
|
+
});
|
|
6436
|
+
actionsRow.appendChild(removeBtn);
|
|
6437
|
+
}
|
|
6438
|
+
const hasActions = actionsRow.children.length > 0;
|
|
6439
|
+
if (hasActions) {
|
|
6440
|
+
tooltip.appendChild(actionsRow);
|
|
6441
|
+
}
|
|
6442
|
+
document.body.appendChild(tooltip);
|
|
6443
|
+
positionPortalTooltip(tooltip, anchor);
|
|
6444
|
+
return tooltip;
|
|
6445
|
+
}
|
|
6446
|
+
function createTooltipHandle() {
|
|
6447
|
+
return { element: null, hideTimer: null };
|
|
6448
|
+
}
|
|
6449
|
+
function scheduleHideTooltip(handle, delayMs = 150) {
|
|
6450
|
+
if (handle.hideTimer !== null) return;
|
|
6451
|
+
handle.hideTimer = setTimeout(() => {
|
|
6452
|
+
handle.hideTimer = null;
|
|
6453
|
+
if (handle.element) {
|
|
6454
|
+
handle.element.remove();
|
|
6455
|
+
handle.element = null;
|
|
6456
|
+
}
|
|
6457
|
+
}, delayMs);
|
|
6458
|
+
}
|
|
6459
|
+
function cancelHideTooltip(handle) {
|
|
6460
|
+
if (handle.hideTimer !== null) {
|
|
6461
|
+
clearTimeout(handle.hideTimer);
|
|
6462
|
+
handle.hideTimer = null;
|
|
6463
|
+
}
|
|
6464
|
+
}
|
|
6465
|
+
function removePortalTooltip(tooltip) {
|
|
6466
|
+
if (tooltip) tooltip.remove();
|
|
6467
|
+
return null;
|
|
6468
|
+
}
|
|
6469
|
+
function getAtTrigger(textarea) {
|
|
6470
|
+
var _a;
|
|
6471
|
+
const cursorPos = (_a = textarea.selectionStart) != null ? _a : 0;
|
|
6472
|
+
const textBefore = textarea.value.slice(0, cursorPos);
|
|
6473
|
+
for (let i = textBefore.length - 1; i >= 0; i--) {
|
|
6474
|
+
if (textBefore[i] === "@") {
|
|
6475
|
+
if (i === 0 || /\s/.test(textBefore[i - 1])) {
|
|
6476
|
+
let query = textBefore.slice(i + 1);
|
|
6477
|
+
if (query.startsWith('"')) {
|
|
6478
|
+
query = query.slice(1);
|
|
6479
|
+
}
|
|
6480
|
+
return { query, pos: i };
|
|
6481
|
+
}
|
|
6482
|
+
return null;
|
|
6483
|
+
}
|
|
6484
|
+
}
|
|
6485
|
+
return null;
|
|
6486
|
+
}
|
|
6487
|
+
function filterFilesForDropdown(query, files, labels) {
|
|
6488
|
+
const lq = query.toLowerCase();
|
|
6489
|
+
return files.filter((rid) => {
|
|
6490
|
+
var _a;
|
|
6491
|
+
const label = (_a = labels.get(rid)) != null ? _a : rid;
|
|
6492
|
+
return label.toLowerCase().includes(lq);
|
|
6493
|
+
});
|
|
6494
|
+
}
|
|
6495
|
+
var TEXTAREA_FONT = "font-size: var(--fb-font-size, 14px); font-family: var(--fb-font-family, inherit); line-height: 1.6;";
|
|
6496
|
+
var TEXTAREA_PADDING = "padding: 12px 52px 12px 14px;";
|
|
6497
|
+
function renderEditMode(element, ctx, wrapper, pathKey, initialValue) {
|
|
6498
|
+
var _a;
|
|
6499
|
+
const state = ctx.state;
|
|
6500
|
+
const files = [...initialValue.files];
|
|
6501
|
+
const dropdownState = {
|
|
6502
|
+
open: false,
|
|
6503
|
+
query: "",
|
|
6504
|
+
triggerPos: -1,
|
|
6505
|
+
selectedIndex: 0
|
|
6506
|
+
};
|
|
6507
|
+
const docListenerCtrl = new AbortController();
|
|
6508
|
+
const hiddenInput = document.createElement("input");
|
|
6509
|
+
hiddenInput.type = "hidden";
|
|
6510
|
+
hiddenInput.name = pathKey;
|
|
6511
|
+
function getCurrentValue() {
|
|
6512
|
+
var _a2, _b;
|
|
6513
|
+
const rawText = textarea.value;
|
|
6514
|
+
const nameToRid = buildNameToRid(files, state);
|
|
6515
|
+
const submissionText = rawText ? replaceFilenamesWithRids(rawText, nameToRid) : null;
|
|
6516
|
+
const textKey = (_a2 = element.textKey) != null ? _a2 : "text";
|
|
6517
|
+
const filesKey = (_b = element.filesKey) != null ? _b : "files";
|
|
6518
|
+
return {
|
|
6519
|
+
[textKey]: rawText === "" ? null : submissionText,
|
|
6520
|
+
[filesKey]: [...files]
|
|
6521
|
+
};
|
|
6522
|
+
}
|
|
6523
|
+
function writeHidden() {
|
|
6524
|
+
hiddenInput.value = JSON.stringify(getCurrentValue());
|
|
6525
|
+
}
|
|
6526
|
+
const outerDiv = document.createElement("div");
|
|
6527
|
+
outerDiv.className = "fb-richinput-wrapper";
|
|
6528
|
+
outerDiv.style.cssText = `
|
|
6529
|
+
position: relative;
|
|
6530
|
+
border: 1px solid var(--fb-border-color, #d1d5db);
|
|
6531
|
+
border-radius: 16px;
|
|
6532
|
+
background: var(--fb-background-color, #f9fafb);
|
|
6533
|
+
transition: box-shadow 0.15s, border-color 0.15s;
|
|
6534
|
+
`;
|
|
6535
|
+
outerDiv.addEventListener("focusin", () => {
|
|
6536
|
+
outerDiv.style.borderColor = "var(--fb-primary-color, #0066cc)";
|
|
6537
|
+
outerDiv.style.boxShadow = "0 0 0 2px color-mix(in srgb, var(--fb-primary-color, #0066cc) 25%, transparent)";
|
|
6538
|
+
});
|
|
6539
|
+
outerDiv.addEventListener("focusout", () => {
|
|
6540
|
+
outerDiv.style.borderColor = "var(--fb-border-color, #d1d5db)";
|
|
6541
|
+
outerDiv.style.boxShadow = "none";
|
|
6542
|
+
});
|
|
6543
|
+
let dragCounter = 0;
|
|
6544
|
+
outerDiv.addEventListener("dragenter", (e) => {
|
|
6545
|
+
e.preventDefault();
|
|
6546
|
+
dragCounter++;
|
|
6547
|
+
outerDiv.style.borderColor = "var(--fb-primary-color, #0066cc)";
|
|
6548
|
+
outerDiv.style.boxShadow = "0 0 0 2px color-mix(in srgb, var(--fb-primary-color, #0066cc) 25%, transparent)";
|
|
6549
|
+
});
|
|
6550
|
+
outerDiv.addEventListener("dragover", (e) => {
|
|
6551
|
+
e.preventDefault();
|
|
6552
|
+
});
|
|
6553
|
+
outerDiv.addEventListener("dragleave", (e) => {
|
|
6554
|
+
e.preventDefault();
|
|
6555
|
+
dragCounter--;
|
|
6556
|
+
if (dragCounter <= 0) {
|
|
6557
|
+
dragCounter = 0;
|
|
6558
|
+
outerDiv.style.borderColor = "var(--fb-border-color, #d1d5db)";
|
|
6559
|
+
outerDiv.style.boxShadow = "none";
|
|
6560
|
+
}
|
|
6561
|
+
});
|
|
6562
|
+
outerDiv.addEventListener("drop", (e) => {
|
|
6563
|
+
var _a2, _b;
|
|
6564
|
+
e.preventDefault();
|
|
6565
|
+
dragCounter = 0;
|
|
6566
|
+
outerDiv.style.borderColor = "var(--fb-border-color, #d1d5db)";
|
|
6567
|
+
outerDiv.style.boxShadow = "none";
|
|
6568
|
+
const droppedFiles = (_a2 = e.dataTransfer) == null ? void 0 : _a2.files;
|
|
6569
|
+
if (!droppedFiles || !state.config.uploadFile) return;
|
|
6570
|
+
const maxFiles = (_b = element.maxFiles) != null ? _b : Infinity;
|
|
6571
|
+
for (let i = 0; i < droppedFiles.length && files.length < maxFiles; i++) {
|
|
6572
|
+
uploadFile(droppedFiles[i]);
|
|
6573
|
+
}
|
|
6574
|
+
});
|
|
6575
|
+
const filesRow = document.createElement("div");
|
|
6576
|
+
filesRow.className = "fb-richinput-files";
|
|
6577
|
+
filesRow.style.cssText = "display: none; flex-wrap: wrap; gap: 6px; padding: 10px 14px 0; align-items: center;";
|
|
6578
|
+
const fileInput = document.createElement("input");
|
|
6579
|
+
fileInput.type = "file";
|
|
6580
|
+
fileInput.multiple = true;
|
|
6581
|
+
fileInput.style.display = "none";
|
|
6582
|
+
if (element.accept) {
|
|
6583
|
+
if (typeof element.accept === "string") {
|
|
6584
|
+
fileInput.accept = element.accept;
|
|
6585
|
+
} else {
|
|
6586
|
+
fileInput.accept = element.accept.extensions.map((ext) => ext.startsWith(".") ? ext : `.${ext}`).join(",");
|
|
6587
|
+
}
|
|
6588
|
+
}
|
|
6589
|
+
const textareaArea = document.createElement("div");
|
|
6590
|
+
textareaArea.style.cssText = "position: relative;";
|
|
6591
|
+
const backdrop = document.createElement("div");
|
|
6592
|
+
backdrop.className = "fb-richinput-backdrop";
|
|
6593
|
+
backdrop.style.cssText = `
|
|
6594
|
+
position: absolute;
|
|
6595
|
+
top: 0; left: 0; right: 0; bottom: 0;
|
|
6596
|
+
${TEXTAREA_PADDING}
|
|
6597
|
+
${TEXTAREA_FONT}
|
|
6598
|
+
white-space: pre-wrap;
|
|
6599
|
+
word-break: break-word;
|
|
6600
|
+
color: transparent;
|
|
6601
|
+
pointer-events: none;
|
|
6602
|
+
overflow: hidden;
|
|
6603
|
+
border-radius: inherit;
|
|
6604
|
+
box-sizing: border-box;
|
|
6605
|
+
z-index: 2;
|
|
6606
|
+
`;
|
|
6607
|
+
const textarea = document.createElement("textarea");
|
|
6608
|
+
textarea.name = `${pathKey}__text`;
|
|
6609
|
+
textarea.placeholder = element.placeholder || t("richinputPlaceholder", state);
|
|
6610
|
+
const rawInitialText = (_a = initialValue.text) != null ? _a : "";
|
|
6611
|
+
textarea.value = rawInitialText ? replaceRidsWithFilenames(rawInitialText, files, state) : "";
|
|
6612
|
+
textarea.style.cssText = `
|
|
6613
|
+
width: 100%;
|
|
6614
|
+
${TEXTAREA_PADDING}
|
|
6615
|
+
${TEXTAREA_FONT}
|
|
6616
|
+
background: transparent;
|
|
6617
|
+
border: none;
|
|
6618
|
+
outline: none;
|
|
6619
|
+
resize: none;
|
|
6620
|
+
color: var(--fb-text-color, #111827);
|
|
6621
|
+
box-sizing: border-box;
|
|
6622
|
+
position: relative;
|
|
6623
|
+
z-index: 1;
|
|
6624
|
+
caret-color: var(--fb-text-color, #111827);
|
|
6625
|
+
`;
|
|
6626
|
+
applyAutoExpand2(textarea, backdrop);
|
|
6627
|
+
textarea.addEventListener("scroll", () => {
|
|
6628
|
+
backdrop.scrollTop = textarea.scrollTop;
|
|
6629
|
+
});
|
|
6630
|
+
let mentionTooltip = null;
|
|
6631
|
+
backdrop.addEventListener("mouseover", (e) => {
|
|
6632
|
+
var _a2, _b;
|
|
6633
|
+
const mark = (_b = (_a2 = e.target).closest) == null ? void 0 : _b.call(_a2, "mark");
|
|
6634
|
+
if (!(mark == null ? void 0 : mark.dataset.rid)) return;
|
|
6635
|
+
mentionTooltip = removePortalTooltip(mentionTooltip);
|
|
6636
|
+
mentionTooltip = showMentionTooltip(mark, mark.dataset.rid, state);
|
|
6637
|
+
});
|
|
6638
|
+
backdrop.addEventListener("mouseout", (e) => {
|
|
6639
|
+
var _a2, _b, _c;
|
|
6640
|
+
const mark = (_b = (_a2 = e.target).closest) == null ? void 0 : _b.call(_a2, "mark");
|
|
6641
|
+
if (!mark) return;
|
|
6642
|
+
const related = e.relatedTarget;
|
|
6643
|
+
if ((_c = related == null ? void 0 : related.closest) == null ? void 0 : _c.call(related, "mark")) return;
|
|
6644
|
+
mentionTooltip = removePortalTooltip(mentionTooltip);
|
|
6645
|
+
});
|
|
6646
|
+
backdrop.addEventListener("mousedown", (e) => {
|
|
6647
|
+
var _a2, _b;
|
|
6648
|
+
const mark = (_b = (_a2 = e.target).closest) == null ? void 0 : _b.call(_a2, "mark");
|
|
6649
|
+
if (!mark) return;
|
|
6650
|
+
mentionTooltip = removePortalTooltip(mentionTooltip);
|
|
6651
|
+
const marks = backdrop.querySelectorAll("mark");
|
|
6652
|
+
marks.forEach((m) => m.style.pointerEvents = "none");
|
|
6653
|
+
const under = document.elementFromPoint(e.clientX, e.clientY);
|
|
6654
|
+
if (under) {
|
|
6655
|
+
under.dispatchEvent(
|
|
6656
|
+
new MouseEvent("mousedown", {
|
|
6657
|
+
bubbles: true,
|
|
6658
|
+
cancelable: true,
|
|
6659
|
+
view: window,
|
|
6660
|
+
clientX: e.clientX,
|
|
6661
|
+
clientY: e.clientY,
|
|
6662
|
+
button: e.button,
|
|
6663
|
+
buttons: e.buttons,
|
|
6664
|
+
detail: e.detail
|
|
6665
|
+
})
|
|
6666
|
+
);
|
|
6667
|
+
}
|
|
6668
|
+
document.addEventListener(
|
|
6669
|
+
"mouseup",
|
|
6670
|
+
() => {
|
|
6671
|
+
marks.forEach((m) => m.style.pointerEvents = "auto");
|
|
6672
|
+
},
|
|
6673
|
+
{ once: true }
|
|
6674
|
+
);
|
|
6675
|
+
});
|
|
6676
|
+
function updateBackdrop() {
|
|
6677
|
+
const text = textarea.value;
|
|
6678
|
+
const nameToRid = buildNameToRid(files, state);
|
|
6679
|
+
const tokens = findAtTokens(text);
|
|
6680
|
+
if (tokens.length === 0) {
|
|
6681
|
+
backdrop.innerHTML = escapeHtml(text) + "\n";
|
|
6682
|
+
return;
|
|
6683
|
+
}
|
|
6684
|
+
let html = "";
|
|
6685
|
+
let lastIdx = 0;
|
|
6686
|
+
for (const token of tokens) {
|
|
6687
|
+
html += escapeHtml(text.slice(lastIdx, token.start));
|
|
6688
|
+
const rid = nameToRid.get(token.name);
|
|
6689
|
+
if (rid) {
|
|
6690
|
+
html += `<mark data-rid="${escapeHtml(rid)}" style="background: color-mix(in srgb, var(--fb-primary-color, #0066cc) 15%, transparent); color: transparent; border-radius: 8px; padding: 0; border: none; box-shadow: 0 0 0 2px color-mix(in srgb, var(--fb-primary-color, #0066cc) 15%, transparent), 0 0 0 3px color-mix(in srgb, var(--fb-primary-color, #0066cc) 30%, transparent); box-decoration-break: clone; -webkit-box-decoration-break: clone; pointer-events: auto; cursor: text;">${escapeHtml(text.slice(token.start, token.end))}</mark>`;
|
|
6691
|
+
} else {
|
|
6692
|
+
html += `<mark style="color: transparent; background: none; padding: 0; border: none; text-decoration-line: underline; text-decoration-style: wavy; text-decoration-color: rgba(239, 68, 68, 0.45); text-underline-offset: 2px;">${escapeHtml(text.slice(token.start, token.end))}</mark>`;
|
|
6693
|
+
}
|
|
6694
|
+
lastIdx = token.end;
|
|
6695
|
+
}
|
|
6696
|
+
html += escapeHtml(text.slice(lastIdx));
|
|
6697
|
+
backdrop.innerHTML = html + "\n";
|
|
6698
|
+
}
|
|
6699
|
+
const paperclipBtn = document.createElement("button");
|
|
6700
|
+
paperclipBtn.type = "button";
|
|
6701
|
+
paperclipBtn.title = t("richinputAttachFile", state);
|
|
6702
|
+
paperclipBtn.style.cssText = `
|
|
6703
|
+
position: absolute;
|
|
6704
|
+
right: 10px;
|
|
6705
|
+
bottom: 10px;
|
|
6706
|
+
z-index: 2;
|
|
6707
|
+
width: 32px;
|
|
6708
|
+
height: 32px;
|
|
6709
|
+
border: none;
|
|
6710
|
+
border-radius: 8px;
|
|
6711
|
+
background: transparent;
|
|
6712
|
+
cursor: pointer;
|
|
6713
|
+
display: flex;
|
|
6714
|
+
align-items: center;
|
|
6715
|
+
justify-content: center;
|
|
6716
|
+
color: var(--fb-text-muted-color, #9ca3af);
|
|
6717
|
+
transition: color 0.15s, background 0.15s;
|
|
6718
|
+
`;
|
|
6719
|
+
paperclipBtn.innerHTML = '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"/></svg>';
|
|
6720
|
+
paperclipBtn.addEventListener("mouseenter", () => {
|
|
6721
|
+
paperclipBtn.style.color = "var(--fb-primary-color, #0066cc)";
|
|
6722
|
+
paperclipBtn.style.background = "var(--fb-background-hover-color, #f3f4f6)";
|
|
6723
|
+
});
|
|
6724
|
+
paperclipBtn.addEventListener("mouseleave", () => {
|
|
6725
|
+
paperclipBtn.style.color = "var(--fb-text-muted-color, #9ca3af)";
|
|
6726
|
+
paperclipBtn.style.background = "transparent";
|
|
6727
|
+
});
|
|
6728
|
+
paperclipBtn.addEventListener("click", () => {
|
|
6729
|
+
var _a2;
|
|
6730
|
+
const maxFiles = (_a2 = element.maxFiles) != null ? _a2 : Infinity;
|
|
6731
|
+
if (files.length < maxFiles) {
|
|
6732
|
+
fileInput.click();
|
|
6733
|
+
}
|
|
6734
|
+
});
|
|
6735
|
+
const dropdown = document.createElement("div");
|
|
6736
|
+
dropdown.className = "fb-richinput-dropdown";
|
|
6737
|
+
dropdown.style.cssText = `
|
|
6738
|
+
display: none;
|
|
6739
|
+
position: absolute;
|
|
6740
|
+
bottom: 100%;
|
|
6741
|
+
left: 0;
|
|
6742
|
+
z-index: 1000;
|
|
6743
|
+
background: #fff;
|
|
6744
|
+
border: 1px solid var(--fb-border-color, #d1d5db);
|
|
6745
|
+
border-radius: var(--fb-border-radius, 6px);
|
|
6746
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.12);
|
|
6747
|
+
min-width: 180px;
|
|
6748
|
+
max-width: 320px;
|
|
6749
|
+
max-height: 200px;
|
|
6750
|
+
overflow-y: auto;
|
|
6751
|
+
margin-bottom: 4px;
|
|
6752
|
+
${TEXTAREA_FONT}
|
|
6753
|
+
`;
|
|
6754
|
+
function buildFileLabelsFromClosure() {
|
|
6755
|
+
return buildFileLabels(files, state);
|
|
6756
|
+
}
|
|
6757
|
+
function renderDropdownItems(filtered) {
|
|
6758
|
+
clear(dropdown);
|
|
6759
|
+
const labels = buildFileLabelsFromClosure();
|
|
6760
|
+
if (filtered.length === 0) {
|
|
6761
|
+
dropdown.style.display = "none";
|
|
6762
|
+
dropdownState.open = false;
|
|
6763
|
+
return;
|
|
6764
|
+
}
|
|
6765
|
+
filtered.forEach((rid, idx) => {
|
|
6766
|
+
var _a2;
|
|
6767
|
+
const meta = state.resourceIndex.get(rid);
|
|
6768
|
+
const item = document.createElement("div");
|
|
6769
|
+
item.className = "fb-richinput-dropdown-item";
|
|
6770
|
+
item.dataset.rid = rid;
|
|
6771
|
+
item.style.cssText = `
|
|
6772
|
+
padding: 5px 10px;
|
|
6773
|
+
cursor: pointer;
|
|
6774
|
+
color: var(--fb-text-color, #111827);
|
|
6775
|
+
background: ${idx === dropdownState.selectedIndex ? "var(--fb-background-hover-color, #f3f4f6)" : "transparent"};
|
|
6776
|
+
display: flex;
|
|
6777
|
+
align-items: center;
|
|
6778
|
+
gap: 8px;
|
|
6779
|
+
`;
|
|
6780
|
+
const thumb = document.createElement("div");
|
|
6781
|
+
thumb.style.cssText = "width: 24px; height: 24px; border-radius: 4px; overflow: hidden; flex-shrink: 0; background: var(--fb-background-hover-color, #f3f4f6); display: flex; align-items: center; justify-content: center;";
|
|
6782
|
+
if ((meta == null ? void 0 : meta.file) && isImageMeta(meta)) {
|
|
6783
|
+
const img = document.createElement("img");
|
|
6784
|
+
img.style.cssText = "width: 100%; height: 100%; object-fit: cover; display: block;";
|
|
6785
|
+
const reader = new FileReader();
|
|
6786
|
+
reader.onload = (ev) => {
|
|
6787
|
+
var _a3;
|
|
6788
|
+
img.src = ((_a3 = ev.target) == null ? void 0 : _a3.result) || "";
|
|
6789
|
+
};
|
|
6790
|
+
reader.readAsDataURL(meta.file);
|
|
6791
|
+
thumb.appendChild(img);
|
|
6792
|
+
} else if (state.config.getThumbnail) {
|
|
6793
|
+
state.config.getThumbnail(rid).then((url) => {
|
|
6794
|
+
if (!url || !thumb.isConnected) return;
|
|
6795
|
+
const img = document.createElement("img");
|
|
6796
|
+
img.style.cssText = "width: 100%; height: 100%; object-fit: cover; display: block;";
|
|
6797
|
+
img.src = url;
|
|
6798
|
+
clear(thumb);
|
|
6799
|
+
thumb.appendChild(img);
|
|
6800
|
+
}).catch(() => {
|
|
6801
|
+
});
|
|
6802
|
+
thumb.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>';
|
|
6803
|
+
} else {
|
|
6804
|
+
thumb.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>';
|
|
6805
|
+
}
|
|
6806
|
+
item.appendChild(thumb);
|
|
6807
|
+
const nameSpan = document.createElement("span");
|
|
6808
|
+
nameSpan.style.cssText = "overflow: hidden; text-overflow: ellipsis; white-space: nowrap;";
|
|
6809
|
+
nameSpan.textContent = (_a2 = labels.get(rid)) != null ? _a2 : rid;
|
|
6810
|
+
item.appendChild(nameSpan);
|
|
6811
|
+
dropdown.appendChild(item);
|
|
6812
|
+
});
|
|
6813
|
+
dropdown.onmousemove = (e) => {
|
|
6814
|
+
var _a2, _b, _c;
|
|
6815
|
+
const target = (_b = (_a2 = e.target).closest) == null ? void 0 : _b.call(
|
|
6816
|
+
_a2,
|
|
6817
|
+
".fb-richinput-dropdown-item"
|
|
6818
|
+
);
|
|
6819
|
+
if (!target) return;
|
|
6820
|
+
const newIdx = filtered.indexOf((_c = target.dataset.rid) != null ? _c : "");
|
|
6821
|
+
if (newIdx === -1 || newIdx === dropdownState.selectedIndex) return;
|
|
6822
|
+
const items = dropdown.querySelectorAll(
|
|
6823
|
+
".fb-richinput-dropdown-item"
|
|
6824
|
+
);
|
|
6825
|
+
items.forEach((el, i) => {
|
|
6826
|
+
el.style.background = i === newIdx ? "var(--fb-background-hover-color, #f3f4f6)" : "transparent";
|
|
6827
|
+
});
|
|
6828
|
+
dropdownState.selectedIndex = newIdx;
|
|
6829
|
+
};
|
|
6830
|
+
dropdown.onmousedown = (e) => {
|
|
6831
|
+
var _a2, _b;
|
|
6832
|
+
e.preventDefault();
|
|
6833
|
+
e.stopPropagation();
|
|
6834
|
+
const target = (_b = (_a2 = e.target).closest) == null ? void 0 : _b.call(
|
|
6835
|
+
_a2,
|
|
6836
|
+
".fb-richinput-dropdown-item"
|
|
6837
|
+
);
|
|
6838
|
+
if (!(target == null ? void 0 : target.dataset.rid)) return;
|
|
6839
|
+
insertMention(target.dataset.rid);
|
|
6840
|
+
};
|
|
6841
|
+
dropdown.style.display = "block";
|
|
6842
|
+
dropdownState.open = true;
|
|
6843
|
+
}
|
|
6844
|
+
function openDropdown() {
|
|
6845
|
+
const trigger = getAtTrigger(textarea);
|
|
6846
|
+
if (!trigger) {
|
|
6847
|
+
closeDropdown();
|
|
6848
|
+
return;
|
|
6849
|
+
}
|
|
6850
|
+
dropdownState.query = trigger.query;
|
|
6851
|
+
dropdownState.triggerPos = trigger.pos;
|
|
6852
|
+
dropdownState.selectedIndex = 0;
|
|
6853
|
+
const labels = buildFileLabelsFromClosure();
|
|
6854
|
+
const filtered = filterFilesForDropdown(trigger.query, files, labels);
|
|
6855
|
+
renderDropdownItems(filtered);
|
|
6856
|
+
}
|
|
6857
|
+
function closeDropdown() {
|
|
6858
|
+
dropdown.style.display = "none";
|
|
6859
|
+
dropdownState.open = false;
|
|
6860
|
+
}
|
|
6861
|
+
function insertMention(rid) {
|
|
6862
|
+
var _a2, _b, _c, _d;
|
|
6863
|
+
const labels = buildFileLabelsFromClosure();
|
|
6864
|
+
const label = (_c = (_b = labels.get(rid)) != null ? _b : (_a2 = state.resourceIndex.get(rid)) == null ? void 0 : _a2.name) != null ? _c : rid;
|
|
6865
|
+
const cursorPos = (_d = textarea.selectionStart) != null ? _d : 0;
|
|
6866
|
+
const before = textarea.value.slice(0, dropdownState.triggerPos);
|
|
6867
|
+
const after = textarea.value.slice(cursorPos);
|
|
6868
|
+
const mention = `${formatMention(label)} `;
|
|
6869
|
+
textarea.value = `${before}${mention}${after}`;
|
|
6870
|
+
const newPos = before.length + mention.length;
|
|
6871
|
+
textarea.setSelectionRange(newPos, newPos);
|
|
6872
|
+
textarea.dispatchEvent(new Event("input"));
|
|
6873
|
+
closeDropdown();
|
|
6874
|
+
}
|
|
6875
|
+
textarea.addEventListener("input", () => {
|
|
6876
|
+
var _a2;
|
|
6877
|
+
updateBackdrop();
|
|
6878
|
+
writeHidden();
|
|
6879
|
+
(_a2 = ctx.instance) == null ? void 0 : _a2.triggerOnChange(pathKey, getCurrentValue());
|
|
6880
|
+
if (files.length > 0) {
|
|
6881
|
+
openDropdown();
|
|
6882
|
+
} else {
|
|
6883
|
+
closeDropdown();
|
|
6884
|
+
}
|
|
6885
|
+
});
|
|
6886
|
+
function updateDropdownHighlight() {
|
|
6887
|
+
const items = dropdown.querySelectorAll(
|
|
6888
|
+
".fb-richinput-dropdown-item"
|
|
6889
|
+
);
|
|
6890
|
+
items.forEach((el, i) => {
|
|
6891
|
+
el.style.background = i === dropdownState.selectedIndex ? "var(--fb-background-hover-color, #f3f4f6)" : "transparent";
|
|
6892
|
+
});
|
|
6893
|
+
}
|
|
6894
|
+
textarea.addEventListener("keydown", (e) => {
|
|
6895
|
+
if (!dropdownState.open) return;
|
|
6896
|
+
const labels = buildFileLabelsFromClosure();
|
|
6897
|
+
const filtered = filterFilesForDropdown(
|
|
6898
|
+
dropdownState.query,
|
|
6899
|
+
files,
|
|
6900
|
+
labels
|
|
6901
|
+
);
|
|
6902
|
+
if (e.key === "ArrowDown") {
|
|
6903
|
+
e.preventDefault();
|
|
6904
|
+
dropdownState.selectedIndex = Math.min(
|
|
6905
|
+
dropdownState.selectedIndex + 1,
|
|
6906
|
+
filtered.length - 1
|
|
6907
|
+
);
|
|
6908
|
+
updateDropdownHighlight();
|
|
6909
|
+
} else if (e.key === "ArrowUp") {
|
|
6910
|
+
e.preventDefault();
|
|
6911
|
+
dropdownState.selectedIndex = Math.max(
|
|
6912
|
+
dropdownState.selectedIndex - 1,
|
|
6913
|
+
0
|
|
6914
|
+
);
|
|
6915
|
+
updateDropdownHighlight();
|
|
6916
|
+
} else if (e.key === "Enter" && filtered.length > 0) {
|
|
6917
|
+
e.preventDefault();
|
|
6918
|
+
insertMention(filtered[dropdownState.selectedIndex]);
|
|
6919
|
+
} else if (e.key === "Escape") {
|
|
6920
|
+
closeDropdown();
|
|
6921
|
+
}
|
|
6922
|
+
});
|
|
6923
|
+
document.addEventListener(
|
|
6924
|
+
"click",
|
|
6925
|
+
(e) => {
|
|
6926
|
+
if (!outerDiv.contains(e.target) && !dropdown.contains(e.target)) {
|
|
6927
|
+
closeDropdown();
|
|
6928
|
+
}
|
|
6929
|
+
},
|
|
6930
|
+
{ signal: docListenerCtrl.signal }
|
|
6931
|
+
);
|
|
6932
|
+
function renderFilesRow() {
|
|
6933
|
+
clear(filesRow);
|
|
6934
|
+
if (files.length === 0) {
|
|
6935
|
+
filesRow.style.display = "none";
|
|
6936
|
+
return;
|
|
6937
|
+
}
|
|
6938
|
+
filesRow.style.display = "flex";
|
|
6939
|
+
files.forEach((rid) => {
|
|
6940
|
+
const meta = state.resourceIndex.get(rid);
|
|
6941
|
+
const thumbWrapper = document.createElement("div");
|
|
6942
|
+
thumbWrapper.className = "fb-richinput-file-thumb";
|
|
6943
|
+
thumbWrapper.style.cssText = `
|
|
6944
|
+
position: relative;
|
|
6945
|
+
width: 48px;
|
|
6946
|
+
height: 48px;
|
|
6947
|
+
border: 1px solid var(--fb-border-color, #d1d5db);
|
|
6948
|
+
border-radius: 8px;
|
|
6949
|
+
overflow: hidden;
|
|
6950
|
+
flex-shrink: 0;
|
|
6951
|
+
cursor: pointer;
|
|
6952
|
+
background: #fff;
|
|
6953
|
+
`;
|
|
6954
|
+
const thumbInner = document.createElement("div");
|
|
6955
|
+
thumbInner.style.cssText = "width: 48px; height: 48px; border-radius: inherit; overflow: hidden;";
|
|
6956
|
+
renderThumbContent(thumbInner, rid, meta, state);
|
|
6957
|
+
thumbWrapper.appendChild(thumbInner);
|
|
6958
|
+
const tooltipHandle = createTooltipHandle();
|
|
6959
|
+
const doMention = () => {
|
|
6960
|
+
var _a2, _b, _c;
|
|
6961
|
+
const cursorPos = (_a2 = textarea.selectionStart) != null ? _a2 : textarea.value.length;
|
|
6962
|
+
const labels = buildFileLabelsFromClosure();
|
|
6963
|
+
const label = (_c = (_b = labels.get(rid)) != null ? _b : meta == null ? void 0 : meta.name) != null ? _c : rid;
|
|
6964
|
+
const before = textarea.value.slice(0, cursorPos);
|
|
6965
|
+
const after = textarea.value.slice(cursorPos);
|
|
6966
|
+
const prefix = before.length > 0 && !/[\s\n]$/.test(before) ? "\n" : "";
|
|
6967
|
+
const mention = `${prefix}${formatMention(label)} `;
|
|
6968
|
+
textarea.value = `${before}${mention}${after}`;
|
|
6969
|
+
const newPos = cursorPos + mention.length;
|
|
6970
|
+
textarea.setSelectionRange(newPos, newPos);
|
|
6971
|
+
textarea.focus();
|
|
6972
|
+
textarea.dispatchEvent(new Event("input"));
|
|
6973
|
+
};
|
|
6974
|
+
const doRemove = () => {
|
|
6975
|
+
var _a2;
|
|
6976
|
+
const idx = files.indexOf(rid);
|
|
6977
|
+
if (idx !== -1) files.splice(idx, 1);
|
|
6978
|
+
renderFilesRow();
|
|
6979
|
+
updateBackdrop();
|
|
6980
|
+
writeHidden();
|
|
6981
|
+
(_a2 = ctx.instance) == null ? void 0 : _a2.triggerOnChange(pathKey, getCurrentValue());
|
|
6982
|
+
};
|
|
6983
|
+
thumbWrapper.addEventListener("mouseenter", () => {
|
|
6984
|
+
cancelHideTooltip(tooltipHandle);
|
|
6985
|
+
if (!tooltipHandle.element) {
|
|
6986
|
+
tooltipHandle.element = showFileTooltip(thumbWrapper, {
|
|
6987
|
+
rid,
|
|
6988
|
+
state,
|
|
6989
|
+
isReadonly: false,
|
|
6990
|
+
onMention: doMention,
|
|
6991
|
+
onRemove: doRemove
|
|
6992
|
+
});
|
|
6993
|
+
tooltipHandle.element.addEventListener("mouseenter", () => {
|
|
6994
|
+
cancelHideTooltip(tooltipHandle);
|
|
6995
|
+
});
|
|
6996
|
+
tooltipHandle.element.addEventListener("mouseleave", () => {
|
|
6997
|
+
scheduleHideTooltip(tooltipHandle);
|
|
6998
|
+
});
|
|
6999
|
+
}
|
|
7000
|
+
});
|
|
7001
|
+
thumbWrapper.addEventListener("mouseleave", () => {
|
|
7002
|
+
scheduleHideTooltip(tooltipHandle);
|
|
7003
|
+
});
|
|
7004
|
+
filesRow.appendChild(thumbWrapper);
|
|
7005
|
+
});
|
|
7006
|
+
}
|
|
7007
|
+
function uploadFile(file) {
|
|
7008
|
+
if (!state.config.uploadFile) return;
|
|
7009
|
+
const tempId = `temp-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
|
|
7010
|
+
state.resourceIndex.set(tempId, {
|
|
7011
|
+
name: file.name,
|
|
7012
|
+
type: file.type,
|
|
7013
|
+
size: file.size,
|
|
7014
|
+
uploadedAt: /* @__PURE__ */ new Date(),
|
|
7015
|
+
file
|
|
7016
|
+
});
|
|
7017
|
+
files.push(tempId);
|
|
7018
|
+
renderFilesRow();
|
|
7019
|
+
const thumbs = filesRow.querySelectorAll(
|
|
7020
|
+
".fb-richinput-file-thumb"
|
|
7021
|
+
);
|
|
7022
|
+
const loadingThumb = thumbs[thumbs.length - 1];
|
|
7023
|
+
if (loadingThumb) loadingThumb.style.opacity = "0.5";
|
|
7024
|
+
state.config.uploadFile(file).then((resourceId) => {
|
|
7025
|
+
var _a2;
|
|
7026
|
+
const idx = files.indexOf(tempId);
|
|
7027
|
+
if (idx !== -1) files[idx] = resourceId;
|
|
7028
|
+
state.resourceIndex.delete(tempId);
|
|
7029
|
+
state.resourceIndex.set(resourceId, {
|
|
7030
|
+
name: file.name,
|
|
7031
|
+
type: file.type,
|
|
7032
|
+
size: file.size,
|
|
7033
|
+
uploadedAt: /* @__PURE__ */ new Date(),
|
|
7034
|
+
file
|
|
7035
|
+
});
|
|
7036
|
+
renderFilesRow();
|
|
7037
|
+
updateBackdrop();
|
|
7038
|
+
writeHidden();
|
|
7039
|
+
(_a2 = ctx.instance) == null ? void 0 : _a2.triggerOnChange(pathKey, getCurrentValue());
|
|
7040
|
+
}).catch((err) => {
|
|
7041
|
+
var _a2, _b;
|
|
7042
|
+
const idx = files.indexOf(tempId);
|
|
7043
|
+
if (idx !== -1) files.splice(idx, 1);
|
|
7044
|
+
state.resourceIndex.delete(tempId);
|
|
7045
|
+
renderFilesRow();
|
|
7046
|
+
(_b = (_a2 = state.config).onUploadError) == null ? void 0 : _b.call(_a2, err, file);
|
|
7047
|
+
});
|
|
7048
|
+
}
|
|
7049
|
+
fileInput.addEventListener("change", () => {
|
|
7050
|
+
var _a2;
|
|
7051
|
+
const selected = fileInput.files;
|
|
7052
|
+
if (!selected || selected.length === 0) return;
|
|
7053
|
+
const maxFiles = (_a2 = element.maxFiles) != null ? _a2 : Infinity;
|
|
7054
|
+
for (let i = 0; i < selected.length && files.length < maxFiles; i++) {
|
|
7055
|
+
uploadFile(selected[i]);
|
|
7056
|
+
}
|
|
7057
|
+
fileInput.value = "";
|
|
7058
|
+
});
|
|
7059
|
+
textareaArea.appendChild(backdrop);
|
|
7060
|
+
textareaArea.appendChild(textarea);
|
|
7061
|
+
textareaArea.appendChild(paperclipBtn);
|
|
7062
|
+
textareaArea.appendChild(dropdown);
|
|
7063
|
+
outerDiv.appendChild(filesRow);
|
|
7064
|
+
outerDiv.appendChild(textareaArea);
|
|
7065
|
+
if (element.minLength != null || element.maxLength != null) {
|
|
7066
|
+
const counterRow = document.createElement("div");
|
|
7067
|
+
counterRow.style.cssText = "position: relative; padding: 2px 14px 6px; text-align: right;";
|
|
7068
|
+
const counter = createCharCounter(element, textarea, false);
|
|
7069
|
+
counter.style.cssText = `
|
|
7070
|
+
position: static;
|
|
7071
|
+
display: inline-block;
|
|
7072
|
+
font-size: var(--fb-font-size-small);
|
|
7073
|
+
color: var(--fb-text-secondary-color);
|
|
7074
|
+
pointer-events: none;
|
|
7075
|
+
`;
|
|
7076
|
+
counterRow.appendChild(counter);
|
|
7077
|
+
outerDiv.appendChild(counterRow);
|
|
7078
|
+
}
|
|
7079
|
+
outerDiv.appendChild(hiddenInput);
|
|
7080
|
+
outerDiv.appendChild(fileInput);
|
|
7081
|
+
writeHidden();
|
|
7082
|
+
updateBackdrop();
|
|
7083
|
+
hiddenInput._applyExternalUpdate = (value) => {
|
|
7084
|
+
var _a2;
|
|
7085
|
+
const rawText = (_a2 = value.text) != null ? _a2 : "";
|
|
7086
|
+
textarea.value = rawText ? replaceRidsWithFilenames(rawText, files, state) : "";
|
|
7087
|
+
textarea.dispatchEvent(new Event("input"));
|
|
7088
|
+
files.length = 0;
|
|
7089
|
+
for (const rid of value.files) files.push(rid);
|
|
7090
|
+
renderFilesRow();
|
|
7091
|
+
updateBackdrop();
|
|
7092
|
+
writeHidden();
|
|
7093
|
+
};
|
|
7094
|
+
wrapper.appendChild(outerDiv);
|
|
7095
|
+
renderFilesRow();
|
|
7096
|
+
const observer = new MutationObserver(() => {
|
|
7097
|
+
if (!outerDiv.isConnected) {
|
|
7098
|
+
docListenerCtrl.abort();
|
|
7099
|
+
mentionTooltip = removePortalTooltip(mentionTooltip);
|
|
7100
|
+
observer.disconnect();
|
|
7101
|
+
}
|
|
7102
|
+
});
|
|
7103
|
+
if (outerDiv.parentElement) {
|
|
7104
|
+
observer.observe(outerDiv.parentElement, { childList: true });
|
|
7105
|
+
}
|
|
7106
|
+
}
|
|
7107
|
+
function renderReadonlyMode(_element, ctx, wrapper, _pathKey, value) {
|
|
7108
|
+
var _a, _b;
|
|
7109
|
+
const state = ctx.state;
|
|
7110
|
+
const { text, files } = value;
|
|
7111
|
+
const ridToName = /* @__PURE__ */ new Map();
|
|
7112
|
+
for (const rid of files) {
|
|
7113
|
+
const meta = state.resourceIndex.get(rid);
|
|
7114
|
+
if (meta == null ? void 0 : meta.name) ridToName.set(rid, meta.name);
|
|
7115
|
+
}
|
|
7116
|
+
if (files.length > 0) {
|
|
7117
|
+
const filesRow = document.createElement("div");
|
|
7118
|
+
filesRow.style.cssText = "display: flex; flex-wrap: wrap; gap: 6px; padding-bottom: 8px;";
|
|
7119
|
+
files.forEach((rid) => {
|
|
7120
|
+
const meta = state.resourceIndex.get(rid);
|
|
7121
|
+
const thumbWrapper = document.createElement("div");
|
|
7122
|
+
thumbWrapper.style.cssText = `
|
|
7123
|
+
position: relative;
|
|
7124
|
+
width: 48px; height: 48px;
|
|
7125
|
+
border: 1px solid var(--fb-border-color, #d1d5db);
|
|
7126
|
+
border-radius: 8px;
|
|
7127
|
+
overflow: hidden;
|
|
7128
|
+
flex-shrink: 0;
|
|
7129
|
+
background: #fff;
|
|
7130
|
+
cursor: default;
|
|
7131
|
+
`;
|
|
7132
|
+
const thumbInner = document.createElement("div");
|
|
7133
|
+
thumbInner.style.cssText = "width: 48px; height: 48px; border-radius: inherit; overflow: hidden;";
|
|
7134
|
+
renderThumbContent(thumbInner, rid, meta, state);
|
|
7135
|
+
thumbWrapper.appendChild(thumbInner);
|
|
7136
|
+
const tooltipHandle = createTooltipHandle();
|
|
7137
|
+
thumbWrapper.addEventListener("mouseenter", () => {
|
|
7138
|
+
cancelHideTooltip(tooltipHandle);
|
|
7139
|
+
if (!tooltipHandle.element) {
|
|
7140
|
+
tooltipHandle.element = showFileTooltip(thumbWrapper, {
|
|
7141
|
+
rid,
|
|
7142
|
+
state,
|
|
7143
|
+
isReadonly: true
|
|
7144
|
+
});
|
|
7145
|
+
tooltipHandle.element.addEventListener("mouseenter", () => {
|
|
7146
|
+
cancelHideTooltip(tooltipHandle);
|
|
7147
|
+
});
|
|
7148
|
+
tooltipHandle.element.addEventListener("mouseleave", () => {
|
|
7149
|
+
scheduleHideTooltip(tooltipHandle);
|
|
7150
|
+
});
|
|
7151
|
+
}
|
|
7152
|
+
});
|
|
7153
|
+
thumbWrapper.addEventListener("mouseleave", () => {
|
|
7154
|
+
scheduleHideTooltip(tooltipHandle);
|
|
7155
|
+
});
|
|
7156
|
+
filesRow.appendChild(thumbWrapper);
|
|
7157
|
+
});
|
|
7158
|
+
wrapper.appendChild(filesRow);
|
|
7159
|
+
}
|
|
7160
|
+
if (text) {
|
|
7161
|
+
const textDiv = document.createElement("div");
|
|
7162
|
+
textDiv.style.cssText = `
|
|
7163
|
+
${TEXTAREA_FONT}
|
|
7164
|
+
color: var(--fb-text-color, #111827);
|
|
7165
|
+
white-space: pre-wrap;
|
|
7166
|
+
word-break: break-word;
|
|
7167
|
+
`;
|
|
7168
|
+
const tokens = findAtTokens(text);
|
|
7169
|
+
const resolvedTokens = tokens.filter(
|
|
7170
|
+
(tok) => ridToName.has(tok.name) || [...ridToName.values()].includes(tok.name)
|
|
7171
|
+
);
|
|
7172
|
+
if (resolvedTokens.length === 0) {
|
|
7173
|
+
textDiv.textContent = text;
|
|
7174
|
+
} else {
|
|
7175
|
+
let lastIndex = 0;
|
|
7176
|
+
for (const token of resolvedTokens) {
|
|
7177
|
+
if (token.start > lastIndex) {
|
|
7178
|
+
textDiv.appendChild(
|
|
7179
|
+
document.createTextNode(text.slice(lastIndex, token.start))
|
|
7180
|
+
);
|
|
7181
|
+
}
|
|
7182
|
+
const span = document.createElement("span");
|
|
7183
|
+
span.style.cssText = `
|
|
7184
|
+
display: inline;
|
|
7185
|
+
background: color-mix(in srgb, var(--fb-primary-color, #0066cc) 15%, transparent);
|
|
7186
|
+
color: var(--fb-primary-color, #0066cc);
|
|
7187
|
+
border-radius: 8px;
|
|
7188
|
+
padding: 1px 6px;
|
|
7189
|
+
font-weight: 500;
|
|
7190
|
+
cursor: default;
|
|
7191
|
+
`;
|
|
7192
|
+
const rid = ridToName.has(token.name) ? token.name : (_a = [...ridToName.entries()].find(([, n]) => n === token.name)) == null ? void 0 : _a[0];
|
|
7193
|
+
const displayName = (_b = ridToName.get(token.name)) != null ? _b : token.name;
|
|
7194
|
+
span.textContent = `@${displayName}`;
|
|
7195
|
+
if (rid) {
|
|
7196
|
+
let spanTooltip = null;
|
|
7197
|
+
const mentionRid = rid;
|
|
7198
|
+
span.addEventListener("mouseenter", () => {
|
|
7199
|
+
spanTooltip = removePortalTooltip(spanTooltip);
|
|
7200
|
+
spanTooltip = showMentionTooltip(span, mentionRid, state);
|
|
7201
|
+
});
|
|
7202
|
+
span.addEventListener("mouseleave", () => {
|
|
7203
|
+
spanTooltip = removePortalTooltip(spanTooltip);
|
|
7204
|
+
});
|
|
7205
|
+
}
|
|
7206
|
+
textDiv.appendChild(span);
|
|
7207
|
+
lastIndex = token.end;
|
|
7208
|
+
}
|
|
7209
|
+
if (lastIndex < text.length) {
|
|
7210
|
+
textDiv.appendChild(document.createTextNode(text.slice(lastIndex)));
|
|
7211
|
+
}
|
|
7212
|
+
}
|
|
7213
|
+
wrapper.appendChild(textDiv);
|
|
7214
|
+
}
|
|
7215
|
+
if (!text && files.length === 0) {
|
|
7216
|
+
const empty = document.createElement("div");
|
|
7217
|
+
empty.style.cssText = "color: var(--fb-text-muted-color, #6b7280); font-size: var(--fb-font-size, 14px);";
|
|
7218
|
+
empty.textContent = "\u2014";
|
|
7219
|
+
wrapper.appendChild(empty);
|
|
7220
|
+
}
|
|
7221
|
+
}
|
|
7222
|
+
function renderRichInputElement(element, ctx, wrapper, pathKey) {
|
|
7223
|
+
var _a, _b, _c, _d;
|
|
7224
|
+
const state = ctx.state;
|
|
7225
|
+
const textKey = (_a = element.textKey) != null ? _a : "text";
|
|
7226
|
+
const filesKey = (_b = element.filesKey) != null ? _b : "files";
|
|
7227
|
+
const rawPrefill = ctx.prefill[element.key];
|
|
7228
|
+
let initialValue;
|
|
7229
|
+
if (rawPrefill && typeof rawPrefill === "object" && !Array.isArray(rawPrefill)) {
|
|
7230
|
+
const obj = rawPrefill;
|
|
7231
|
+
const textVal = (_c = obj[textKey]) != null ? _c : obj["text"];
|
|
7232
|
+
const filesVal = (_d = obj[filesKey]) != null ? _d : obj["files"];
|
|
7233
|
+
initialValue = {
|
|
7234
|
+
text: typeof textVal === "string" ? textVal : null,
|
|
7235
|
+
files: Array.isArray(filesVal) ? filesVal : []
|
|
7236
|
+
};
|
|
7237
|
+
} else if (typeof rawPrefill === "string") {
|
|
7238
|
+
initialValue = { text: rawPrefill || null, files: [] };
|
|
7239
|
+
} else {
|
|
7240
|
+
initialValue = { text: null, files: [] };
|
|
7241
|
+
}
|
|
7242
|
+
for (const rid of initialValue.files) {
|
|
7243
|
+
if (!state.resourceIndex.has(rid)) {
|
|
7244
|
+
state.resourceIndex.set(rid, {
|
|
7245
|
+
name: rid,
|
|
7246
|
+
type: "application/octet-stream",
|
|
7247
|
+
size: 0,
|
|
7248
|
+
uploadedAt: /* @__PURE__ */ new Date(),
|
|
7249
|
+
file: void 0
|
|
7250
|
+
});
|
|
7251
|
+
}
|
|
7252
|
+
}
|
|
7253
|
+
if (state.config.readonly) {
|
|
7254
|
+
renderReadonlyMode(element, ctx, wrapper, pathKey, initialValue);
|
|
7255
|
+
} else {
|
|
7256
|
+
if (!state.config.uploadFile) {
|
|
7257
|
+
throw new Error(
|
|
7258
|
+
`RichInput field "${element.key}" requires uploadFile handler in config`
|
|
7259
|
+
);
|
|
7260
|
+
}
|
|
7261
|
+
renderEditMode(element, ctx, wrapper, pathKey, initialValue);
|
|
7262
|
+
}
|
|
7263
|
+
}
|
|
7264
|
+
function validateRichInputElement(element, key, context) {
|
|
7265
|
+
var _a, _b;
|
|
7266
|
+
const { scopeRoot, state, skipValidation } = context;
|
|
7267
|
+
const errors = [];
|
|
7268
|
+
const textKey = (_a = element.textKey) != null ? _a : "text";
|
|
7269
|
+
const filesKey = (_b = element.filesKey) != null ? _b : "files";
|
|
7270
|
+
const hiddenInput = scopeRoot.querySelector(
|
|
7271
|
+
`[name="${key}"]`
|
|
7272
|
+
);
|
|
7273
|
+
if (!hiddenInput) {
|
|
7274
|
+
return { value: null, errors };
|
|
7275
|
+
}
|
|
7276
|
+
let rawValue = {};
|
|
7277
|
+
try {
|
|
7278
|
+
const parsed = JSON.parse(hiddenInput.value);
|
|
7279
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
7280
|
+
rawValue = parsed;
|
|
7281
|
+
} else {
|
|
7282
|
+
errors.push(`${key}: invalid richinput data`);
|
|
7283
|
+
return { value: null, errors };
|
|
7284
|
+
}
|
|
7285
|
+
} catch {
|
|
7286
|
+
errors.push(`${key}: invalid richinput data`);
|
|
7287
|
+
return { value: null, errors };
|
|
7288
|
+
}
|
|
7289
|
+
const textVal = rawValue[textKey];
|
|
7290
|
+
const filesVal = rawValue[filesKey];
|
|
7291
|
+
const text = textVal === null || typeof textVal === "string" ? textVal : null;
|
|
7292
|
+
const files = Array.isArray(filesVal) ? filesVal : [];
|
|
7293
|
+
const value = {
|
|
7294
|
+
[textKey]: text != null ? text : null,
|
|
7295
|
+
[filesKey]: files
|
|
7296
|
+
};
|
|
7297
|
+
if (!skipValidation) {
|
|
7298
|
+
const textEmpty = !text || text.trim() === "";
|
|
7299
|
+
const filesEmpty = files.length === 0;
|
|
7300
|
+
if (element.required && textEmpty && filesEmpty) {
|
|
7301
|
+
errors.push(`${key}: ${t("required", state)}`);
|
|
7302
|
+
}
|
|
7303
|
+
if (!textEmpty && text) {
|
|
7304
|
+
if (element.minLength != null && text.length < element.minLength) {
|
|
7305
|
+
errors.push(
|
|
7306
|
+
`${key}: ${t("minLength", state, { min: element.minLength })}`
|
|
7307
|
+
);
|
|
7308
|
+
}
|
|
7309
|
+
if (element.maxLength != null && text.length > element.maxLength) {
|
|
7310
|
+
errors.push(
|
|
7311
|
+
`${key}: ${t("maxLength", state, { max: element.maxLength })}`
|
|
7312
|
+
);
|
|
7313
|
+
}
|
|
7314
|
+
}
|
|
7315
|
+
if (element.maxFiles != null && files.length > element.maxFiles) {
|
|
7316
|
+
errors.push(
|
|
7317
|
+
`${key}: ${t("maxFiles", state, { max: element.maxFiles })}`
|
|
7318
|
+
);
|
|
7319
|
+
}
|
|
7320
|
+
}
|
|
7321
|
+
return { value, errors };
|
|
7322
|
+
}
|
|
7323
|
+
function updateRichInputField(element, fieldPath, value, context) {
|
|
7324
|
+
var _a, _b, _c, _d;
|
|
7325
|
+
const { scopeRoot } = context;
|
|
7326
|
+
const hiddenInput = scopeRoot.querySelector(
|
|
7327
|
+
`[name="${fieldPath}"]`
|
|
7328
|
+
);
|
|
7329
|
+
if (!hiddenInput) {
|
|
7330
|
+
console.warn(
|
|
7331
|
+
`updateRichInputField: no hidden input found for "${fieldPath}". Re-render to reflect new data.`
|
|
7332
|
+
);
|
|
7333
|
+
return;
|
|
7334
|
+
}
|
|
7335
|
+
let normalized = null;
|
|
7336
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
7337
|
+
const obj = value;
|
|
7338
|
+
const textKey = (_a = element.textKey) != null ? _a : "text";
|
|
7339
|
+
const filesKey = (_b = element.filesKey) != null ? _b : "files";
|
|
7340
|
+
const textVal = (_c = obj[textKey]) != null ? _c : obj["text"];
|
|
7341
|
+
const filesVal = (_d = obj[filesKey]) != null ? _d : obj["files"];
|
|
7342
|
+
if (textVal !== void 0 || filesVal !== void 0) {
|
|
7343
|
+
normalized = {
|
|
7344
|
+
text: typeof textVal === "string" ? textVal : null,
|
|
7345
|
+
files: Array.isArray(filesVal) ? filesVal : []
|
|
7346
|
+
};
|
|
7347
|
+
}
|
|
7348
|
+
}
|
|
7349
|
+
if (normalized && hiddenInput._applyExternalUpdate) {
|
|
7350
|
+
hiddenInput._applyExternalUpdate(normalized);
|
|
7351
|
+
} else if (normalized) {
|
|
7352
|
+
hiddenInput.value = JSON.stringify(normalized);
|
|
7353
|
+
}
|
|
7354
|
+
}
|
|
7355
|
+
|
|
5796
7356
|
// src/components/index.ts
|
|
5797
7357
|
function showTooltip(tooltipId, button) {
|
|
5798
7358
|
const tooltip = document.getElementById(tooltipId);
|
|
@@ -6145,6 +7705,9 @@ function dispatchToRenderer(element, ctx, wrapper, pathKey) {
|
|
|
6145
7705
|
case "table":
|
|
6146
7706
|
renderTableElement(element, ctx, wrapper, pathKey);
|
|
6147
7707
|
break;
|
|
7708
|
+
case "richinput":
|
|
7709
|
+
renderRichInputElement(element, ctx, wrapper, pathKey);
|
|
7710
|
+
break;
|
|
6148
7711
|
default: {
|
|
6149
7712
|
const unsupported = document.createElement("div");
|
|
6150
7713
|
unsupported.className = "text-red-500 text-sm";
|
|
@@ -6189,6 +7752,7 @@ var defaultConfig = {
|
|
|
6189
7752
|
enableFilePreview: true,
|
|
6190
7753
|
maxPreviewSize: "200px",
|
|
6191
7754
|
readonly: false,
|
|
7755
|
+
parseTableFile: null,
|
|
6192
7756
|
locale: "en",
|
|
6193
7757
|
translations: {
|
|
6194
7758
|
en: {
|
|
@@ -6241,7 +7805,14 @@ var defaultConfig = {
|
|
|
6241
7805
|
tableRemoveRow: "Remove row",
|
|
6242
7806
|
tableRemoveColumn: "Remove column",
|
|
6243
7807
|
tableMergeCells: "Merge cells (Ctrl+M)",
|
|
6244
|
-
tableSplitCell: "Split cell (Ctrl+Shift+M)"
|
|
7808
|
+
tableSplitCell: "Split cell (Ctrl+Shift+M)",
|
|
7809
|
+
tableImportFile: "Import",
|
|
7810
|
+
tableImporting: "Importing...",
|
|
7811
|
+
tableImportError: "Import failed: {error}",
|
|
7812
|
+
richinputPlaceholder: "Type text...",
|
|
7813
|
+
richinputAttachFile: "Attach file",
|
|
7814
|
+
richinputMention: "Mention",
|
|
7815
|
+
richinputRemoveFile: "Remove"
|
|
6245
7816
|
},
|
|
6246
7817
|
ru: {
|
|
6247
7818
|
// UI texts
|
|
@@ -6293,7 +7864,14 @@ var defaultConfig = {
|
|
|
6293
7864
|
tableRemoveRow: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0441\u0442\u0440\u043E\u043A\u0443",
|
|
6294
7865
|
tableRemoveColumn: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0441\u0442\u043E\u043B\u0431\u0435\u0446",
|
|
6295
7866
|
tableMergeCells: "\u041E\u0431\u044A\u0435\u0434\u0438\u043D\u0438\u0442\u044C \u044F\u0447\u0435\u0439\u043A\u0438 (Ctrl+M)",
|
|
6296
|
-
tableSplitCell: "\u0420\u0430\u0437\u0434\u0435\u043B\u0438\u0442\u044C \u044F\u0447\u0435\u0439\u043A\u0443 (Ctrl+Shift+M)"
|
|
7867
|
+
tableSplitCell: "\u0420\u0430\u0437\u0434\u0435\u043B\u0438\u0442\u044C \u044F\u0447\u0435\u0439\u043A\u0443 (Ctrl+Shift+M)",
|
|
7868
|
+
tableImportFile: "\u0418\u043C\u043F\u043E\u0440\u0442",
|
|
7869
|
+
tableImporting: "\u0418\u043C\u043F\u043E\u0440\u0442...",
|
|
7870
|
+
tableImportError: "\u041E\u0448\u0438\u0431\u043A\u0430 \u0438\u043C\u043F\u043E\u0440\u0442\u0430: {error}",
|
|
7871
|
+
richinputPlaceholder: "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0442\u0435\u043A\u0441\u0442...",
|
|
7872
|
+
richinputAttachFile: "\u041F\u0440\u0438\u043A\u0440\u0435\u043F\u0438\u0442\u044C \u0444\u0430\u0439\u043B",
|
|
7873
|
+
richinputMention: "\u0423\u043F\u043E\u043C\u044F\u043D\u0443\u0442\u044C",
|
|
7874
|
+
richinputRemoveFile: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C"
|
|
6297
7875
|
}
|
|
6298
7876
|
},
|
|
6299
7877
|
theme: {}
|
|
@@ -6571,6 +8149,10 @@ var componentRegistry = {
|
|
|
6571
8149
|
table: {
|
|
6572
8150
|
validate: validateTableElement,
|
|
6573
8151
|
update: updateTableField
|
|
8152
|
+
},
|
|
8153
|
+
richinput: {
|
|
8154
|
+
validate: validateRichInputElement,
|
|
8155
|
+
update: updateRichInputField
|
|
6574
8156
|
}
|
|
6575
8157
|
};
|
|
6576
8158
|
function getComponentOperations(elementType) {
|