@dmitryvim/form-builder 0.2.19 → 0.2.20
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 +223 -88
- package/dist/browser/formbuilder.v0.2.20.min.js +583 -0
- package/dist/cjs/index.cjs +1326 -2
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/index.js +1289 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/form-builder.js +223 -88
- package/dist/types/components/index.d.ts +2 -1
- package/dist/types/components/richinput.d.ts +4 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/types/config.d.ts +4 -0
- package/dist/types/types/index.d.ts +1 -1
- package/dist/types/types/schema.d.ts +14 -1
- 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) {
|
|
@@ -5793,6 +5798,1310 @@ function updateTableField(_element, fieldPath, value, context) {
|
|
|
5793
5798
|
}
|
|
5794
5799
|
}
|
|
5795
5800
|
|
|
5801
|
+
// src/components/richinput.ts
|
|
5802
|
+
function applyAutoExpand2(textarea, backdrop) {
|
|
5803
|
+
textarea.style.overflow = "hidden";
|
|
5804
|
+
textarea.style.resize = "none";
|
|
5805
|
+
const lineCount = (textarea.value.match(/\n/g) || []).length + 1;
|
|
5806
|
+
textarea.rows = Math.max(3, lineCount);
|
|
5807
|
+
const resize = () => {
|
|
5808
|
+
if (!textarea.isConnected) return;
|
|
5809
|
+
textarea.style.height = "0";
|
|
5810
|
+
textarea.style.height = `${textarea.scrollHeight}px`;
|
|
5811
|
+
if (backdrop) {
|
|
5812
|
+
backdrop.style.height = `${textarea.scrollHeight}px`;
|
|
5813
|
+
}
|
|
5814
|
+
};
|
|
5815
|
+
textarea.addEventListener("input", resize);
|
|
5816
|
+
setTimeout(() => {
|
|
5817
|
+
if (textarea.isConnected) resize();
|
|
5818
|
+
}, 0);
|
|
5819
|
+
}
|
|
5820
|
+
function buildFileLabels(files, state) {
|
|
5821
|
+
var _a, _b, _c, _d;
|
|
5822
|
+
const labels = /* @__PURE__ */ new Map();
|
|
5823
|
+
const nameCount = /* @__PURE__ */ new Map();
|
|
5824
|
+
for (const rid of files) {
|
|
5825
|
+
const meta = state.resourceIndex.get(rid);
|
|
5826
|
+
const name = (_a = meta == null ? void 0 : meta.name) != null ? _a : rid;
|
|
5827
|
+
nameCount.set(name, ((_b = nameCount.get(name)) != null ? _b : 0) + 1);
|
|
5828
|
+
}
|
|
5829
|
+
for (const rid of files) {
|
|
5830
|
+
const meta = state.resourceIndex.get(rid);
|
|
5831
|
+
const name = (_c = meta == null ? void 0 : meta.name) != null ? _c : rid;
|
|
5832
|
+
if (((_d = nameCount.get(name)) != null ? _d : 1) > 1 && meta) {
|
|
5833
|
+
labels.set(rid, `${name} (${formatFileSize(meta.size)})`);
|
|
5834
|
+
} else {
|
|
5835
|
+
labels.set(rid, name);
|
|
5836
|
+
}
|
|
5837
|
+
}
|
|
5838
|
+
return labels;
|
|
5839
|
+
}
|
|
5840
|
+
function isImageMeta(meta) {
|
|
5841
|
+
if (!meta) return false;
|
|
5842
|
+
return meta.type.startsWith("image/");
|
|
5843
|
+
}
|
|
5844
|
+
function buildNameToRid(files, state) {
|
|
5845
|
+
const labels = buildFileLabels(files, state);
|
|
5846
|
+
const map = /* @__PURE__ */ new Map();
|
|
5847
|
+
for (const rid of files) {
|
|
5848
|
+
const label = labels.get(rid);
|
|
5849
|
+
if (label) map.set(label, rid);
|
|
5850
|
+
}
|
|
5851
|
+
return map;
|
|
5852
|
+
}
|
|
5853
|
+
function formatMention(name) {
|
|
5854
|
+
if (/\s/.test(name) || name.includes('"')) {
|
|
5855
|
+
return `@"${name.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
5856
|
+
}
|
|
5857
|
+
return `@${name}`;
|
|
5858
|
+
}
|
|
5859
|
+
function findAtTokens(text) {
|
|
5860
|
+
const tokens = [];
|
|
5861
|
+
const len = text.length;
|
|
5862
|
+
let i = 0;
|
|
5863
|
+
while (i < len) {
|
|
5864
|
+
if (text[i] === "@") {
|
|
5865
|
+
if (i > 0 && !/\s/.test(text[i - 1])) {
|
|
5866
|
+
i++;
|
|
5867
|
+
continue;
|
|
5868
|
+
}
|
|
5869
|
+
const start = i;
|
|
5870
|
+
i++;
|
|
5871
|
+
if (i < len && text[i] === '"') {
|
|
5872
|
+
i++;
|
|
5873
|
+
let name = "";
|
|
5874
|
+
while (i < len && text[i] !== '"') {
|
|
5875
|
+
if (text[i] === "\\" && i + 1 < len) {
|
|
5876
|
+
name += text[i + 1];
|
|
5877
|
+
i += 2;
|
|
5878
|
+
} else {
|
|
5879
|
+
name += text[i];
|
|
5880
|
+
i++;
|
|
5881
|
+
}
|
|
5882
|
+
}
|
|
5883
|
+
if (i < len && text[i] === '"') {
|
|
5884
|
+
i++;
|
|
5885
|
+
if (i >= len || /\s/.test(text[i])) {
|
|
5886
|
+
tokens.push({ start, end: i, raw: text.slice(start, i), name });
|
|
5887
|
+
}
|
|
5888
|
+
}
|
|
5889
|
+
} else if (i < len && !/\s/.test(text[i])) {
|
|
5890
|
+
const wordStart = i;
|
|
5891
|
+
while (i < len && !/\s/.test(text[i])) {
|
|
5892
|
+
i++;
|
|
5893
|
+
}
|
|
5894
|
+
const name = text.slice(wordStart, i);
|
|
5895
|
+
tokens.push({ start, end: i, raw: text.slice(start, i), name });
|
|
5896
|
+
}
|
|
5897
|
+
} else {
|
|
5898
|
+
i++;
|
|
5899
|
+
}
|
|
5900
|
+
}
|
|
5901
|
+
return tokens;
|
|
5902
|
+
}
|
|
5903
|
+
function findMentions(text, nameToRid) {
|
|
5904
|
+
if (nameToRid.size === 0) return [];
|
|
5905
|
+
const tokens = findAtTokens(text);
|
|
5906
|
+
const results = [];
|
|
5907
|
+
for (const token of tokens) {
|
|
5908
|
+
const rid = nameToRid.get(token.name);
|
|
5909
|
+
if (rid) {
|
|
5910
|
+
results.push({
|
|
5911
|
+
start: token.start,
|
|
5912
|
+
end: token.end,
|
|
5913
|
+
name: token.name,
|
|
5914
|
+
rid
|
|
5915
|
+
});
|
|
5916
|
+
}
|
|
5917
|
+
}
|
|
5918
|
+
return results;
|
|
5919
|
+
}
|
|
5920
|
+
function replaceFilenamesWithRids(text, nameToRid) {
|
|
5921
|
+
const mentions = findMentions(text, nameToRid);
|
|
5922
|
+
if (mentions.length === 0) return text;
|
|
5923
|
+
let result = "";
|
|
5924
|
+
let lastIdx = 0;
|
|
5925
|
+
for (const m of mentions) {
|
|
5926
|
+
result += text.slice(lastIdx, m.start);
|
|
5927
|
+
result += `@${m.rid}`;
|
|
5928
|
+
lastIdx = m.end;
|
|
5929
|
+
}
|
|
5930
|
+
result += text.slice(lastIdx);
|
|
5931
|
+
return result;
|
|
5932
|
+
}
|
|
5933
|
+
function replaceRidsWithFilenames(text, files, state) {
|
|
5934
|
+
const labels = buildFileLabels(files, state);
|
|
5935
|
+
const ridToLabel = /* @__PURE__ */ new Map();
|
|
5936
|
+
for (const rid of files) {
|
|
5937
|
+
const label = labels.get(rid);
|
|
5938
|
+
if (label) ridToLabel.set(rid, label);
|
|
5939
|
+
}
|
|
5940
|
+
const tokens = findAtTokens(text);
|
|
5941
|
+
if (tokens.length === 0) return text;
|
|
5942
|
+
let result = "";
|
|
5943
|
+
let lastIdx = 0;
|
|
5944
|
+
for (const token of tokens) {
|
|
5945
|
+
result += text.slice(lastIdx, token.start);
|
|
5946
|
+
const label = ridToLabel.get(token.name);
|
|
5947
|
+
if (label) {
|
|
5948
|
+
result += formatMention(label);
|
|
5949
|
+
} else {
|
|
5950
|
+
result += token.raw;
|
|
5951
|
+
}
|
|
5952
|
+
lastIdx = token.end;
|
|
5953
|
+
}
|
|
5954
|
+
result += text.slice(lastIdx);
|
|
5955
|
+
return result;
|
|
5956
|
+
}
|
|
5957
|
+
function renderThumbContent(thumb, rid, meta, state) {
|
|
5958
|
+
clear(thumb);
|
|
5959
|
+
if ((meta == null ? void 0 : meta.file) && isImageMeta(meta)) {
|
|
5960
|
+
const img = document.createElement("img");
|
|
5961
|
+
img.alt = meta.name;
|
|
5962
|
+
img.style.cssText = "width: 100%; height: 100%; object-fit: cover; display: block;";
|
|
5963
|
+
const reader = new FileReader();
|
|
5964
|
+
reader.onload = (e) => {
|
|
5965
|
+
var _a;
|
|
5966
|
+
img.src = ((_a = e.target) == null ? void 0 : _a.result) || "";
|
|
5967
|
+
};
|
|
5968
|
+
reader.readAsDataURL(meta.file);
|
|
5969
|
+
thumb.appendChild(img);
|
|
5970
|
+
} else if (state.config.getThumbnail) {
|
|
5971
|
+
state.config.getThumbnail(rid).then((url) => {
|
|
5972
|
+
var _a;
|
|
5973
|
+
if (!url || !thumb.isConnected) return;
|
|
5974
|
+
const img = document.createElement("img");
|
|
5975
|
+
img.alt = (_a = meta == null ? void 0 : meta.name) != null ? _a : rid;
|
|
5976
|
+
img.src = url;
|
|
5977
|
+
img.style.cssText = "width: 100%; height: 100%; object-fit: cover; display: block;";
|
|
5978
|
+
clear(thumb);
|
|
5979
|
+
thumb.appendChild(img);
|
|
5980
|
+
}).catch((err) => {
|
|
5981
|
+
var _a, _b;
|
|
5982
|
+
(_b = (_a = state.config).onThumbnailError) == null ? void 0 : _b.call(_a, err, rid);
|
|
5983
|
+
});
|
|
5984
|
+
const placeholder = document.createElement("div");
|
|
5985
|
+
placeholder.style.cssText = "width: 100%; height: 100%; background: var(--fb-background-hover-color, #f3f4f6); display: flex; align-items: center; justify-content: center;";
|
|
5986
|
+
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>';
|
|
5987
|
+
thumb.appendChild(placeholder);
|
|
5988
|
+
} else {
|
|
5989
|
+
const icon = document.createElement("div");
|
|
5990
|
+
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;";
|
|
5991
|
+
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>';
|
|
5992
|
+
if (meta == null ? void 0 : meta.name) {
|
|
5993
|
+
const nameEl = document.createElement("span");
|
|
5994
|
+
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);";
|
|
5995
|
+
nameEl.textContent = meta.name;
|
|
5996
|
+
icon.appendChild(nameEl);
|
|
5997
|
+
}
|
|
5998
|
+
thumb.appendChild(icon);
|
|
5999
|
+
}
|
|
6000
|
+
}
|
|
6001
|
+
function renderImagePreview(hoverEl, rid, meta, state) {
|
|
6002
|
+
clear(hoverEl);
|
|
6003
|
+
if ((meta == null ? void 0 : meta.file) && isImageMeta(meta)) {
|
|
6004
|
+
const img = document.createElement("img");
|
|
6005
|
+
img.alt = meta.name;
|
|
6006
|
+
img.style.cssText = "max-width: 120px; max-height: 120px; object-fit: contain; display: block;";
|
|
6007
|
+
const reader = new FileReader();
|
|
6008
|
+
reader.onload = (e) => {
|
|
6009
|
+
var _a;
|
|
6010
|
+
img.src = ((_a = e.target) == null ? void 0 : _a.result) || "";
|
|
6011
|
+
};
|
|
6012
|
+
reader.readAsDataURL(meta.file);
|
|
6013
|
+
hoverEl.appendChild(img);
|
|
6014
|
+
} else if (state.config.getThumbnail) {
|
|
6015
|
+
state.config.getThumbnail(rid).then((url) => {
|
|
6016
|
+
var _a;
|
|
6017
|
+
if (!url || !hoverEl.isConnected) return;
|
|
6018
|
+
const img = document.createElement("img");
|
|
6019
|
+
img.alt = (_a = meta == null ? void 0 : meta.name) != null ? _a : rid;
|
|
6020
|
+
img.src = url;
|
|
6021
|
+
img.style.cssText = "max-width: 120px; max-height: 120px; object-fit: contain; display: block;";
|
|
6022
|
+
clear(hoverEl);
|
|
6023
|
+
hoverEl.appendChild(img);
|
|
6024
|
+
}).catch((err) => {
|
|
6025
|
+
var _a, _b;
|
|
6026
|
+
(_b = (_a = state.config).onThumbnailError) == null ? void 0 : _b.call(_a, err, rid);
|
|
6027
|
+
});
|
|
6028
|
+
}
|
|
6029
|
+
}
|
|
6030
|
+
function positionPortalTooltip(tooltip, anchor) {
|
|
6031
|
+
const rect = anchor.getBoundingClientRect();
|
|
6032
|
+
const ttRect = tooltip.getBoundingClientRect();
|
|
6033
|
+
const left = Math.max(
|
|
6034
|
+
4,
|
|
6035
|
+
Math.min(
|
|
6036
|
+
rect.left + rect.width / 2 - ttRect.width / 2,
|
|
6037
|
+
window.innerWidth - ttRect.width - 4
|
|
6038
|
+
)
|
|
6039
|
+
);
|
|
6040
|
+
const topAbove = rect.top - ttRect.height - 8;
|
|
6041
|
+
const topBelow = rect.bottom + 8;
|
|
6042
|
+
const top = topAbove >= 4 ? topAbove : topBelow;
|
|
6043
|
+
tooltip.style.left = `${left}px`;
|
|
6044
|
+
tooltip.style.top = `${Math.max(4, top)}px`;
|
|
6045
|
+
}
|
|
6046
|
+
function showMentionTooltip(anchor, rid, state) {
|
|
6047
|
+
const meta = state.resourceIndex.get(rid);
|
|
6048
|
+
const tooltip = document.createElement("div");
|
|
6049
|
+
tooltip.className = "fb-richinput-portal-tooltip fb-richinput-mention-tooltip";
|
|
6050
|
+
tooltip.style.cssText = `
|
|
6051
|
+
position: fixed;
|
|
6052
|
+
z-index: 99999;
|
|
6053
|
+
background: #fff;
|
|
6054
|
+
border: 1px solid var(--fb-border-color, #d1d5db);
|
|
6055
|
+
border-radius: 8px;
|
|
6056
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
|
6057
|
+
padding: 4px;
|
|
6058
|
+
pointer-events: none;
|
|
6059
|
+
min-width: 60px;
|
|
6060
|
+
max-width: 140px;
|
|
6061
|
+
`;
|
|
6062
|
+
const preview = document.createElement("div");
|
|
6063
|
+
preview.style.cssText = "min-height: 60px; max-height: 120px; display: flex; align-items: center; justify-content: center;";
|
|
6064
|
+
renderImagePreview(preview, rid, meta, state);
|
|
6065
|
+
tooltip.appendChild(preview);
|
|
6066
|
+
document.body.appendChild(tooltip);
|
|
6067
|
+
positionPortalTooltip(tooltip, anchor);
|
|
6068
|
+
return tooltip;
|
|
6069
|
+
}
|
|
6070
|
+
function showFileTooltip(anchor, opts) {
|
|
6071
|
+
var _a, _b;
|
|
6072
|
+
const { rid, state, isReadonly, onMention, onRemove } = opts;
|
|
6073
|
+
const meta = state.resourceIndex.get(rid);
|
|
6074
|
+
const filename = (_a = meta == null ? void 0 : meta.name) != null ? _a : rid;
|
|
6075
|
+
const tooltip = document.createElement("div");
|
|
6076
|
+
tooltip.className = "fb-richinput-portal-tooltip fb-richinput-file-tooltip";
|
|
6077
|
+
tooltip.style.cssText = `
|
|
6078
|
+
position: fixed;
|
|
6079
|
+
z-index: 99999;
|
|
6080
|
+
background: #fff;
|
|
6081
|
+
border: 1px solid var(--fb-border-color, #d1d5db);
|
|
6082
|
+
border-radius: 8px;
|
|
6083
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
|
6084
|
+
padding: 4px;
|
|
6085
|
+
pointer-events: auto;
|
|
6086
|
+
min-width: 80px;
|
|
6087
|
+
max-width: 260px;
|
|
6088
|
+
`;
|
|
6089
|
+
const preview = document.createElement("div");
|
|
6090
|
+
preview.style.cssText = "min-height: 60px; max-height: 120px; display: flex; align-items: center; justify-content: center;";
|
|
6091
|
+
renderImagePreview(preview, rid, meta, state);
|
|
6092
|
+
tooltip.appendChild(preview);
|
|
6093
|
+
const nameEl = document.createElement("div");
|
|
6094
|
+
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);";
|
|
6095
|
+
nameEl.textContent = filename;
|
|
6096
|
+
tooltip.appendChild(nameEl);
|
|
6097
|
+
const actionsRow = document.createElement("div");
|
|
6098
|
+
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;";
|
|
6099
|
+
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;";
|
|
6100
|
+
const btnHoverIn = (btn) => {
|
|
6101
|
+
btn.style.background = "var(--fb-background-hover-color, #f3f4f6)";
|
|
6102
|
+
btn.style.color = "var(--fb-text-color, #111827)";
|
|
6103
|
+
};
|
|
6104
|
+
const btnHoverOut = (btn) => {
|
|
6105
|
+
btn.style.background = "none";
|
|
6106
|
+
btn.style.color = "var(--fb-text-muted-color, #6b7280)";
|
|
6107
|
+
};
|
|
6108
|
+
if (!isReadonly && onMention) {
|
|
6109
|
+
const mentionBtn = document.createElement("button");
|
|
6110
|
+
mentionBtn.type = "button";
|
|
6111
|
+
mentionBtn.title = "Mention";
|
|
6112
|
+
mentionBtn.style.cssText = btnStyle;
|
|
6113
|
+
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>';
|
|
6114
|
+
mentionBtn.addEventListener("mouseenter", () => btnHoverIn(mentionBtn));
|
|
6115
|
+
mentionBtn.addEventListener("mouseleave", () => btnHoverOut(mentionBtn));
|
|
6116
|
+
mentionBtn.addEventListener("click", (e) => {
|
|
6117
|
+
e.stopPropagation();
|
|
6118
|
+
onMention();
|
|
6119
|
+
tooltip.remove();
|
|
6120
|
+
});
|
|
6121
|
+
actionsRow.appendChild(mentionBtn);
|
|
6122
|
+
}
|
|
6123
|
+
if (state.config.downloadFile) {
|
|
6124
|
+
const dlBtn = document.createElement("button");
|
|
6125
|
+
dlBtn.type = "button";
|
|
6126
|
+
dlBtn.title = "Download";
|
|
6127
|
+
dlBtn.style.cssText = btnStyle;
|
|
6128
|
+
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>';
|
|
6129
|
+
dlBtn.addEventListener("mouseenter", () => btnHoverIn(dlBtn));
|
|
6130
|
+
dlBtn.addEventListener("mouseleave", () => btnHoverOut(dlBtn));
|
|
6131
|
+
dlBtn.addEventListener("click", (e) => {
|
|
6132
|
+
var _a2, _b2;
|
|
6133
|
+
e.stopPropagation();
|
|
6134
|
+
(_b2 = (_a2 = state.config).downloadFile) == null ? void 0 : _b2.call(_a2, rid, filename);
|
|
6135
|
+
});
|
|
6136
|
+
actionsRow.appendChild(dlBtn);
|
|
6137
|
+
}
|
|
6138
|
+
const hasOpenUrl = !!((_b = state.config.getDownloadUrl) != null ? _b : state.config.getThumbnail);
|
|
6139
|
+
if (hasOpenUrl) {
|
|
6140
|
+
const openBtn = document.createElement("button");
|
|
6141
|
+
openBtn.type = "button";
|
|
6142
|
+
openBtn.title = "Open in new window";
|
|
6143
|
+
openBtn.style.cssText = btnStyle;
|
|
6144
|
+
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>';
|
|
6145
|
+
openBtn.addEventListener("mouseenter", () => btnHoverIn(openBtn));
|
|
6146
|
+
openBtn.addEventListener("mouseleave", () => btnHoverOut(openBtn));
|
|
6147
|
+
openBtn.addEventListener("click", (e) => {
|
|
6148
|
+
var _a2, _b2, _c, _d;
|
|
6149
|
+
e.stopPropagation();
|
|
6150
|
+
if (state.config.getDownloadUrl) {
|
|
6151
|
+
const url = state.config.getDownloadUrl(rid);
|
|
6152
|
+
if (url) {
|
|
6153
|
+
window.open(url, "_blank");
|
|
6154
|
+
} else {
|
|
6155
|
+
(_b2 = (_a2 = state.config).getThumbnail) == null ? void 0 : _b2.call(_a2, rid).then((thumbUrl) => {
|
|
6156
|
+
if (thumbUrl) window.open(thumbUrl, "_blank");
|
|
6157
|
+
}).catch(() => {
|
|
6158
|
+
});
|
|
6159
|
+
}
|
|
6160
|
+
} else {
|
|
6161
|
+
(_d = (_c = state.config).getThumbnail) == null ? void 0 : _d.call(_c, rid).then((url) => {
|
|
6162
|
+
if (url) window.open(url, "_blank");
|
|
6163
|
+
}).catch(() => {
|
|
6164
|
+
});
|
|
6165
|
+
}
|
|
6166
|
+
});
|
|
6167
|
+
actionsRow.appendChild(openBtn);
|
|
6168
|
+
}
|
|
6169
|
+
if (!isReadonly && onRemove) {
|
|
6170
|
+
const removeBtn = document.createElement("button");
|
|
6171
|
+
removeBtn.type = "button";
|
|
6172
|
+
removeBtn.title = "Remove";
|
|
6173
|
+
removeBtn.style.cssText = btnStyle;
|
|
6174
|
+
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>';
|
|
6175
|
+
removeBtn.addEventListener("mouseenter", () => {
|
|
6176
|
+
removeBtn.style.background = "var(--fb-background-hover-color, #f3f4f6)";
|
|
6177
|
+
removeBtn.style.color = "var(--fb-error-color, #ef4444)";
|
|
6178
|
+
});
|
|
6179
|
+
removeBtn.addEventListener("mouseleave", () => btnHoverOut(removeBtn));
|
|
6180
|
+
removeBtn.addEventListener("click", (e) => {
|
|
6181
|
+
e.stopPropagation();
|
|
6182
|
+
tooltip.remove();
|
|
6183
|
+
onRemove();
|
|
6184
|
+
});
|
|
6185
|
+
actionsRow.appendChild(removeBtn);
|
|
6186
|
+
}
|
|
6187
|
+
const hasActions = actionsRow.children.length > 0;
|
|
6188
|
+
if (hasActions) {
|
|
6189
|
+
tooltip.appendChild(actionsRow);
|
|
6190
|
+
}
|
|
6191
|
+
document.body.appendChild(tooltip);
|
|
6192
|
+
positionPortalTooltip(tooltip, anchor);
|
|
6193
|
+
return tooltip;
|
|
6194
|
+
}
|
|
6195
|
+
function createTooltipHandle() {
|
|
6196
|
+
return { element: null, hideTimer: null };
|
|
6197
|
+
}
|
|
6198
|
+
function scheduleHideTooltip(handle, delayMs = 150) {
|
|
6199
|
+
if (handle.hideTimer !== null) return;
|
|
6200
|
+
handle.hideTimer = setTimeout(() => {
|
|
6201
|
+
handle.hideTimer = null;
|
|
6202
|
+
if (handle.element) {
|
|
6203
|
+
handle.element.remove();
|
|
6204
|
+
handle.element = null;
|
|
6205
|
+
}
|
|
6206
|
+
}, delayMs);
|
|
6207
|
+
}
|
|
6208
|
+
function cancelHideTooltip(handle) {
|
|
6209
|
+
if (handle.hideTimer !== null) {
|
|
6210
|
+
clearTimeout(handle.hideTimer);
|
|
6211
|
+
handle.hideTimer = null;
|
|
6212
|
+
}
|
|
6213
|
+
}
|
|
6214
|
+
function removePortalTooltip(tooltip) {
|
|
6215
|
+
if (tooltip) tooltip.remove();
|
|
6216
|
+
return null;
|
|
6217
|
+
}
|
|
6218
|
+
function getAtTrigger(textarea) {
|
|
6219
|
+
var _a;
|
|
6220
|
+
const cursorPos = (_a = textarea.selectionStart) != null ? _a : 0;
|
|
6221
|
+
const textBefore = textarea.value.slice(0, cursorPos);
|
|
6222
|
+
for (let i = textBefore.length - 1; i >= 0; i--) {
|
|
6223
|
+
if (textBefore[i] === "@") {
|
|
6224
|
+
if (i === 0 || /\s/.test(textBefore[i - 1])) {
|
|
6225
|
+
let query = textBefore.slice(i + 1);
|
|
6226
|
+
if (query.startsWith('"')) {
|
|
6227
|
+
query = query.slice(1);
|
|
6228
|
+
}
|
|
6229
|
+
return { query, pos: i };
|
|
6230
|
+
}
|
|
6231
|
+
return null;
|
|
6232
|
+
}
|
|
6233
|
+
}
|
|
6234
|
+
return null;
|
|
6235
|
+
}
|
|
6236
|
+
function filterFilesForDropdown(query, files, labels) {
|
|
6237
|
+
const lq = query.toLowerCase();
|
|
6238
|
+
return files.filter((rid) => {
|
|
6239
|
+
var _a;
|
|
6240
|
+
const label = (_a = labels.get(rid)) != null ? _a : rid;
|
|
6241
|
+
return label.toLowerCase().includes(lq);
|
|
6242
|
+
});
|
|
6243
|
+
}
|
|
6244
|
+
var TEXTAREA_FONT = "font-size: var(--fb-font-size, 14px); font-family: var(--fb-font-family, inherit); line-height: 1.6;";
|
|
6245
|
+
var TEXTAREA_PADDING = "padding: 12px 52px 12px 14px;";
|
|
6246
|
+
function renderEditMode(element, ctx, wrapper, pathKey, initialValue) {
|
|
6247
|
+
var _a;
|
|
6248
|
+
const state = ctx.state;
|
|
6249
|
+
const files = [...initialValue.files];
|
|
6250
|
+
const dropdownState = {
|
|
6251
|
+
open: false,
|
|
6252
|
+
query: "",
|
|
6253
|
+
triggerPos: -1,
|
|
6254
|
+
selectedIndex: 0
|
|
6255
|
+
};
|
|
6256
|
+
const docListenerCtrl = new AbortController();
|
|
6257
|
+
const hiddenInput = document.createElement("input");
|
|
6258
|
+
hiddenInput.type = "hidden";
|
|
6259
|
+
hiddenInput.name = pathKey;
|
|
6260
|
+
function getCurrentValue() {
|
|
6261
|
+
var _a2, _b;
|
|
6262
|
+
const rawText = textarea.value;
|
|
6263
|
+
const nameToRid = buildNameToRid(files, state);
|
|
6264
|
+
const submissionText = rawText ? replaceFilenamesWithRids(rawText, nameToRid) : null;
|
|
6265
|
+
const textKey = (_a2 = element.textKey) != null ? _a2 : "text";
|
|
6266
|
+
const filesKey = (_b = element.filesKey) != null ? _b : "files";
|
|
6267
|
+
return {
|
|
6268
|
+
[textKey]: rawText === "" ? null : submissionText,
|
|
6269
|
+
[filesKey]: [...files]
|
|
6270
|
+
};
|
|
6271
|
+
}
|
|
6272
|
+
function writeHidden() {
|
|
6273
|
+
hiddenInput.value = JSON.stringify(getCurrentValue());
|
|
6274
|
+
}
|
|
6275
|
+
const outerDiv = document.createElement("div");
|
|
6276
|
+
outerDiv.className = "fb-richinput-wrapper";
|
|
6277
|
+
outerDiv.style.cssText = `
|
|
6278
|
+
position: relative;
|
|
6279
|
+
border: 1px solid var(--fb-border-color, #d1d5db);
|
|
6280
|
+
border-radius: 16px;
|
|
6281
|
+
background: var(--fb-background-color, #f9fafb);
|
|
6282
|
+
transition: box-shadow 0.15s, border-color 0.15s;
|
|
6283
|
+
`;
|
|
6284
|
+
outerDiv.addEventListener("focusin", () => {
|
|
6285
|
+
outerDiv.style.borderColor = "var(--fb-primary-color, #0066cc)";
|
|
6286
|
+
outerDiv.style.boxShadow = "0 0 0 2px color-mix(in srgb, var(--fb-primary-color, #0066cc) 25%, transparent)";
|
|
6287
|
+
});
|
|
6288
|
+
outerDiv.addEventListener("focusout", () => {
|
|
6289
|
+
outerDiv.style.borderColor = "var(--fb-border-color, #d1d5db)";
|
|
6290
|
+
outerDiv.style.boxShadow = "none";
|
|
6291
|
+
});
|
|
6292
|
+
let dragCounter = 0;
|
|
6293
|
+
outerDiv.addEventListener("dragenter", (e) => {
|
|
6294
|
+
e.preventDefault();
|
|
6295
|
+
dragCounter++;
|
|
6296
|
+
outerDiv.style.borderColor = "var(--fb-primary-color, #0066cc)";
|
|
6297
|
+
outerDiv.style.boxShadow = "0 0 0 2px color-mix(in srgb, var(--fb-primary-color, #0066cc) 25%, transparent)";
|
|
6298
|
+
});
|
|
6299
|
+
outerDiv.addEventListener("dragover", (e) => {
|
|
6300
|
+
e.preventDefault();
|
|
6301
|
+
});
|
|
6302
|
+
outerDiv.addEventListener("dragleave", (e) => {
|
|
6303
|
+
e.preventDefault();
|
|
6304
|
+
dragCounter--;
|
|
6305
|
+
if (dragCounter <= 0) {
|
|
6306
|
+
dragCounter = 0;
|
|
6307
|
+
outerDiv.style.borderColor = "var(--fb-border-color, #d1d5db)";
|
|
6308
|
+
outerDiv.style.boxShadow = "none";
|
|
6309
|
+
}
|
|
6310
|
+
});
|
|
6311
|
+
outerDiv.addEventListener("drop", (e) => {
|
|
6312
|
+
var _a2, _b;
|
|
6313
|
+
e.preventDefault();
|
|
6314
|
+
dragCounter = 0;
|
|
6315
|
+
outerDiv.style.borderColor = "var(--fb-border-color, #d1d5db)";
|
|
6316
|
+
outerDiv.style.boxShadow = "none";
|
|
6317
|
+
const droppedFiles = (_a2 = e.dataTransfer) == null ? void 0 : _a2.files;
|
|
6318
|
+
if (!droppedFiles || !state.config.uploadFile) return;
|
|
6319
|
+
const maxFiles = (_b = element.maxFiles) != null ? _b : Infinity;
|
|
6320
|
+
for (let i = 0; i < droppedFiles.length && files.length < maxFiles; i++) {
|
|
6321
|
+
uploadFile(droppedFiles[i]);
|
|
6322
|
+
}
|
|
6323
|
+
});
|
|
6324
|
+
const filesRow = document.createElement("div");
|
|
6325
|
+
filesRow.className = "fb-richinput-files";
|
|
6326
|
+
filesRow.style.cssText = "display: none; flex-wrap: wrap; gap: 6px; padding: 10px 14px 0; align-items: center;";
|
|
6327
|
+
const fileInput = document.createElement("input");
|
|
6328
|
+
fileInput.type = "file";
|
|
6329
|
+
fileInput.multiple = true;
|
|
6330
|
+
fileInput.style.display = "none";
|
|
6331
|
+
if (element.accept) {
|
|
6332
|
+
if (typeof element.accept === "string") {
|
|
6333
|
+
fileInput.accept = element.accept;
|
|
6334
|
+
} else {
|
|
6335
|
+
fileInput.accept = element.accept.extensions.map((ext) => ext.startsWith(".") ? ext : `.${ext}`).join(",");
|
|
6336
|
+
}
|
|
6337
|
+
}
|
|
6338
|
+
const textareaArea = document.createElement("div");
|
|
6339
|
+
textareaArea.style.cssText = "position: relative;";
|
|
6340
|
+
const backdrop = document.createElement("div");
|
|
6341
|
+
backdrop.className = "fb-richinput-backdrop";
|
|
6342
|
+
backdrop.style.cssText = `
|
|
6343
|
+
position: absolute;
|
|
6344
|
+
top: 0; left: 0; right: 0; bottom: 0;
|
|
6345
|
+
${TEXTAREA_PADDING}
|
|
6346
|
+
${TEXTAREA_FONT}
|
|
6347
|
+
white-space: pre-wrap;
|
|
6348
|
+
word-break: break-word;
|
|
6349
|
+
color: transparent;
|
|
6350
|
+
pointer-events: none;
|
|
6351
|
+
overflow: hidden;
|
|
6352
|
+
border-radius: inherit;
|
|
6353
|
+
box-sizing: border-box;
|
|
6354
|
+
z-index: 2;
|
|
6355
|
+
`;
|
|
6356
|
+
const textarea = document.createElement("textarea");
|
|
6357
|
+
textarea.name = `${pathKey}__text`;
|
|
6358
|
+
textarea.placeholder = element.placeholder || t("richinputPlaceholder", state);
|
|
6359
|
+
const rawInitialText = (_a = initialValue.text) != null ? _a : "";
|
|
6360
|
+
textarea.value = rawInitialText ? replaceRidsWithFilenames(rawInitialText, files, state) : "";
|
|
6361
|
+
textarea.style.cssText = `
|
|
6362
|
+
width: 100%;
|
|
6363
|
+
${TEXTAREA_PADDING}
|
|
6364
|
+
${TEXTAREA_FONT}
|
|
6365
|
+
background: transparent;
|
|
6366
|
+
border: none;
|
|
6367
|
+
outline: none;
|
|
6368
|
+
resize: none;
|
|
6369
|
+
color: var(--fb-text-color, #111827);
|
|
6370
|
+
box-sizing: border-box;
|
|
6371
|
+
position: relative;
|
|
6372
|
+
z-index: 1;
|
|
6373
|
+
caret-color: var(--fb-text-color, #111827);
|
|
6374
|
+
`;
|
|
6375
|
+
applyAutoExpand2(textarea, backdrop);
|
|
6376
|
+
textarea.addEventListener("scroll", () => {
|
|
6377
|
+
backdrop.scrollTop = textarea.scrollTop;
|
|
6378
|
+
});
|
|
6379
|
+
let mentionTooltip = null;
|
|
6380
|
+
backdrop.addEventListener("mouseover", (e) => {
|
|
6381
|
+
var _a2, _b;
|
|
6382
|
+
const mark = (_b = (_a2 = e.target).closest) == null ? void 0 : _b.call(_a2, "mark");
|
|
6383
|
+
if (!(mark == null ? void 0 : mark.dataset.rid)) return;
|
|
6384
|
+
mentionTooltip = removePortalTooltip(mentionTooltip);
|
|
6385
|
+
mentionTooltip = showMentionTooltip(mark, mark.dataset.rid, state);
|
|
6386
|
+
});
|
|
6387
|
+
backdrop.addEventListener("mouseout", (e) => {
|
|
6388
|
+
var _a2, _b, _c;
|
|
6389
|
+
const mark = (_b = (_a2 = e.target).closest) == null ? void 0 : _b.call(_a2, "mark");
|
|
6390
|
+
if (!mark) return;
|
|
6391
|
+
const related = e.relatedTarget;
|
|
6392
|
+
if ((_c = related == null ? void 0 : related.closest) == null ? void 0 : _c.call(related, "mark")) return;
|
|
6393
|
+
mentionTooltip = removePortalTooltip(mentionTooltip);
|
|
6394
|
+
});
|
|
6395
|
+
backdrop.addEventListener("mousedown", (e) => {
|
|
6396
|
+
var _a2, _b;
|
|
6397
|
+
const mark = (_b = (_a2 = e.target).closest) == null ? void 0 : _b.call(_a2, "mark");
|
|
6398
|
+
if (!mark) return;
|
|
6399
|
+
mentionTooltip = removePortalTooltip(mentionTooltip);
|
|
6400
|
+
const marks = backdrop.querySelectorAll("mark");
|
|
6401
|
+
marks.forEach((m) => m.style.pointerEvents = "none");
|
|
6402
|
+
const under = document.elementFromPoint(e.clientX, e.clientY);
|
|
6403
|
+
if (under) {
|
|
6404
|
+
under.dispatchEvent(
|
|
6405
|
+
new MouseEvent("mousedown", {
|
|
6406
|
+
bubbles: true,
|
|
6407
|
+
cancelable: true,
|
|
6408
|
+
view: window,
|
|
6409
|
+
clientX: e.clientX,
|
|
6410
|
+
clientY: e.clientY,
|
|
6411
|
+
button: e.button,
|
|
6412
|
+
buttons: e.buttons,
|
|
6413
|
+
detail: e.detail
|
|
6414
|
+
})
|
|
6415
|
+
);
|
|
6416
|
+
}
|
|
6417
|
+
document.addEventListener(
|
|
6418
|
+
"mouseup",
|
|
6419
|
+
() => {
|
|
6420
|
+
marks.forEach((m) => m.style.pointerEvents = "auto");
|
|
6421
|
+
},
|
|
6422
|
+
{ once: true }
|
|
6423
|
+
);
|
|
6424
|
+
});
|
|
6425
|
+
function updateBackdrop() {
|
|
6426
|
+
const text = textarea.value;
|
|
6427
|
+
const nameToRid = buildNameToRid(files, state);
|
|
6428
|
+
const tokens = findAtTokens(text);
|
|
6429
|
+
if (tokens.length === 0) {
|
|
6430
|
+
backdrop.innerHTML = escapeHtml(text) + "\n";
|
|
6431
|
+
return;
|
|
6432
|
+
}
|
|
6433
|
+
let html = "";
|
|
6434
|
+
let lastIdx = 0;
|
|
6435
|
+
for (const token of tokens) {
|
|
6436
|
+
html += escapeHtml(text.slice(lastIdx, token.start));
|
|
6437
|
+
const rid = nameToRid.get(token.name);
|
|
6438
|
+
if (rid) {
|
|
6439
|
+
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>`;
|
|
6440
|
+
} else {
|
|
6441
|
+
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>`;
|
|
6442
|
+
}
|
|
6443
|
+
lastIdx = token.end;
|
|
6444
|
+
}
|
|
6445
|
+
html += escapeHtml(text.slice(lastIdx));
|
|
6446
|
+
backdrop.innerHTML = html + "\n";
|
|
6447
|
+
}
|
|
6448
|
+
const paperclipBtn = document.createElement("button");
|
|
6449
|
+
paperclipBtn.type = "button";
|
|
6450
|
+
paperclipBtn.title = t("richinputAttachFile", state);
|
|
6451
|
+
paperclipBtn.style.cssText = `
|
|
6452
|
+
position: absolute;
|
|
6453
|
+
right: 10px;
|
|
6454
|
+
bottom: 10px;
|
|
6455
|
+
z-index: 2;
|
|
6456
|
+
width: 32px;
|
|
6457
|
+
height: 32px;
|
|
6458
|
+
border: none;
|
|
6459
|
+
border-radius: 8px;
|
|
6460
|
+
background: transparent;
|
|
6461
|
+
cursor: pointer;
|
|
6462
|
+
display: flex;
|
|
6463
|
+
align-items: center;
|
|
6464
|
+
justify-content: center;
|
|
6465
|
+
color: var(--fb-text-muted-color, #9ca3af);
|
|
6466
|
+
transition: color 0.15s, background 0.15s;
|
|
6467
|
+
`;
|
|
6468
|
+
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>';
|
|
6469
|
+
paperclipBtn.addEventListener("mouseenter", () => {
|
|
6470
|
+
paperclipBtn.style.color = "var(--fb-primary-color, #0066cc)";
|
|
6471
|
+
paperclipBtn.style.background = "var(--fb-background-hover-color, #f3f4f6)";
|
|
6472
|
+
});
|
|
6473
|
+
paperclipBtn.addEventListener("mouseleave", () => {
|
|
6474
|
+
paperclipBtn.style.color = "var(--fb-text-muted-color, #9ca3af)";
|
|
6475
|
+
paperclipBtn.style.background = "transparent";
|
|
6476
|
+
});
|
|
6477
|
+
paperclipBtn.addEventListener("click", () => {
|
|
6478
|
+
var _a2;
|
|
6479
|
+
const maxFiles = (_a2 = element.maxFiles) != null ? _a2 : Infinity;
|
|
6480
|
+
if (files.length < maxFiles) {
|
|
6481
|
+
fileInput.click();
|
|
6482
|
+
}
|
|
6483
|
+
});
|
|
6484
|
+
const dropdown = document.createElement("div");
|
|
6485
|
+
dropdown.className = "fb-richinput-dropdown";
|
|
6486
|
+
dropdown.style.cssText = `
|
|
6487
|
+
display: none;
|
|
6488
|
+
position: absolute;
|
|
6489
|
+
bottom: 100%;
|
|
6490
|
+
left: 0;
|
|
6491
|
+
z-index: 1000;
|
|
6492
|
+
background: #fff;
|
|
6493
|
+
border: 1px solid var(--fb-border-color, #d1d5db);
|
|
6494
|
+
border-radius: var(--fb-border-radius, 6px);
|
|
6495
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.12);
|
|
6496
|
+
min-width: 180px;
|
|
6497
|
+
max-width: 320px;
|
|
6498
|
+
max-height: 200px;
|
|
6499
|
+
overflow-y: auto;
|
|
6500
|
+
margin-bottom: 4px;
|
|
6501
|
+
${TEXTAREA_FONT}
|
|
6502
|
+
`;
|
|
6503
|
+
function buildFileLabelsFromClosure() {
|
|
6504
|
+
return buildFileLabels(files, state);
|
|
6505
|
+
}
|
|
6506
|
+
function renderDropdownItems(filtered) {
|
|
6507
|
+
clear(dropdown);
|
|
6508
|
+
const labels = buildFileLabelsFromClosure();
|
|
6509
|
+
if (filtered.length === 0) {
|
|
6510
|
+
dropdown.style.display = "none";
|
|
6511
|
+
dropdownState.open = false;
|
|
6512
|
+
return;
|
|
6513
|
+
}
|
|
6514
|
+
filtered.forEach((rid, idx) => {
|
|
6515
|
+
var _a2;
|
|
6516
|
+
const meta = state.resourceIndex.get(rid);
|
|
6517
|
+
const item = document.createElement("div");
|
|
6518
|
+
item.className = "fb-richinput-dropdown-item";
|
|
6519
|
+
item.dataset.rid = rid;
|
|
6520
|
+
item.style.cssText = `
|
|
6521
|
+
padding: 5px 10px;
|
|
6522
|
+
cursor: pointer;
|
|
6523
|
+
color: var(--fb-text-color, #111827);
|
|
6524
|
+
background: ${idx === dropdownState.selectedIndex ? "var(--fb-background-hover-color, #f3f4f6)" : "transparent"};
|
|
6525
|
+
display: flex;
|
|
6526
|
+
align-items: center;
|
|
6527
|
+
gap: 8px;
|
|
6528
|
+
`;
|
|
6529
|
+
const thumb = document.createElement("div");
|
|
6530
|
+
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;";
|
|
6531
|
+
if ((meta == null ? void 0 : meta.file) && isImageMeta(meta)) {
|
|
6532
|
+
const img = document.createElement("img");
|
|
6533
|
+
img.style.cssText = "width: 100%; height: 100%; object-fit: cover; display: block;";
|
|
6534
|
+
const reader = new FileReader();
|
|
6535
|
+
reader.onload = (ev) => {
|
|
6536
|
+
var _a3;
|
|
6537
|
+
img.src = ((_a3 = ev.target) == null ? void 0 : _a3.result) || "";
|
|
6538
|
+
};
|
|
6539
|
+
reader.readAsDataURL(meta.file);
|
|
6540
|
+
thumb.appendChild(img);
|
|
6541
|
+
} else if (state.config.getThumbnail) {
|
|
6542
|
+
state.config.getThumbnail(rid).then((url) => {
|
|
6543
|
+
if (!url || !thumb.isConnected) return;
|
|
6544
|
+
const img = document.createElement("img");
|
|
6545
|
+
img.style.cssText = "width: 100%; height: 100%; object-fit: cover; display: block;";
|
|
6546
|
+
img.src = url;
|
|
6547
|
+
clear(thumb);
|
|
6548
|
+
thumb.appendChild(img);
|
|
6549
|
+
}).catch(() => {
|
|
6550
|
+
});
|
|
6551
|
+
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>';
|
|
6552
|
+
} else {
|
|
6553
|
+
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>';
|
|
6554
|
+
}
|
|
6555
|
+
item.appendChild(thumb);
|
|
6556
|
+
const nameSpan = document.createElement("span");
|
|
6557
|
+
nameSpan.style.cssText = "overflow: hidden; text-overflow: ellipsis; white-space: nowrap;";
|
|
6558
|
+
nameSpan.textContent = (_a2 = labels.get(rid)) != null ? _a2 : rid;
|
|
6559
|
+
item.appendChild(nameSpan);
|
|
6560
|
+
dropdown.appendChild(item);
|
|
6561
|
+
});
|
|
6562
|
+
dropdown.onmousemove = (e) => {
|
|
6563
|
+
var _a2, _b, _c;
|
|
6564
|
+
const target = (_b = (_a2 = e.target).closest) == null ? void 0 : _b.call(
|
|
6565
|
+
_a2,
|
|
6566
|
+
".fb-richinput-dropdown-item"
|
|
6567
|
+
);
|
|
6568
|
+
if (!target) return;
|
|
6569
|
+
const newIdx = filtered.indexOf((_c = target.dataset.rid) != null ? _c : "");
|
|
6570
|
+
if (newIdx === -1 || newIdx === dropdownState.selectedIndex) return;
|
|
6571
|
+
const items = dropdown.querySelectorAll(
|
|
6572
|
+
".fb-richinput-dropdown-item"
|
|
6573
|
+
);
|
|
6574
|
+
items.forEach((el, i) => {
|
|
6575
|
+
el.style.background = i === newIdx ? "var(--fb-background-hover-color, #f3f4f6)" : "transparent";
|
|
6576
|
+
});
|
|
6577
|
+
dropdownState.selectedIndex = newIdx;
|
|
6578
|
+
};
|
|
6579
|
+
dropdown.onmousedown = (e) => {
|
|
6580
|
+
var _a2, _b;
|
|
6581
|
+
e.preventDefault();
|
|
6582
|
+
e.stopPropagation();
|
|
6583
|
+
const target = (_b = (_a2 = e.target).closest) == null ? void 0 : _b.call(
|
|
6584
|
+
_a2,
|
|
6585
|
+
".fb-richinput-dropdown-item"
|
|
6586
|
+
);
|
|
6587
|
+
if (!(target == null ? void 0 : target.dataset.rid)) return;
|
|
6588
|
+
insertMention(target.dataset.rid);
|
|
6589
|
+
};
|
|
6590
|
+
dropdown.style.display = "block";
|
|
6591
|
+
dropdownState.open = true;
|
|
6592
|
+
}
|
|
6593
|
+
function openDropdown() {
|
|
6594
|
+
const trigger = getAtTrigger(textarea);
|
|
6595
|
+
if (!trigger) {
|
|
6596
|
+
closeDropdown();
|
|
6597
|
+
return;
|
|
6598
|
+
}
|
|
6599
|
+
dropdownState.query = trigger.query;
|
|
6600
|
+
dropdownState.triggerPos = trigger.pos;
|
|
6601
|
+
dropdownState.selectedIndex = 0;
|
|
6602
|
+
const labels = buildFileLabelsFromClosure();
|
|
6603
|
+
const filtered = filterFilesForDropdown(trigger.query, files, labels);
|
|
6604
|
+
renderDropdownItems(filtered);
|
|
6605
|
+
}
|
|
6606
|
+
function closeDropdown() {
|
|
6607
|
+
dropdown.style.display = "none";
|
|
6608
|
+
dropdownState.open = false;
|
|
6609
|
+
}
|
|
6610
|
+
function insertMention(rid) {
|
|
6611
|
+
var _a2, _b, _c, _d;
|
|
6612
|
+
const labels = buildFileLabelsFromClosure();
|
|
6613
|
+
const label = (_c = (_b = labels.get(rid)) != null ? _b : (_a2 = state.resourceIndex.get(rid)) == null ? void 0 : _a2.name) != null ? _c : rid;
|
|
6614
|
+
const cursorPos = (_d = textarea.selectionStart) != null ? _d : 0;
|
|
6615
|
+
const before = textarea.value.slice(0, dropdownState.triggerPos);
|
|
6616
|
+
const after = textarea.value.slice(cursorPos);
|
|
6617
|
+
const mention = `${formatMention(label)} `;
|
|
6618
|
+
textarea.value = `${before}${mention}${after}`;
|
|
6619
|
+
const newPos = before.length + mention.length;
|
|
6620
|
+
textarea.setSelectionRange(newPos, newPos);
|
|
6621
|
+
textarea.dispatchEvent(new Event("input"));
|
|
6622
|
+
closeDropdown();
|
|
6623
|
+
}
|
|
6624
|
+
textarea.addEventListener("input", () => {
|
|
6625
|
+
var _a2;
|
|
6626
|
+
updateBackdrop();
|
|
6627
|
+
writeHidden();
|
|
6628
|
+
(_a2 = ctx.instance) == null ? void 0 : _a2.triggerOnChange(pathKey, getCurrentValue());
|
|
6629
|
+
if (files.length > 0) {
|
|
6630
|
+
openDropdown();
|
|
6631
|
+
} else {
|
|
6632
|
+
closeDropdown();
|
|
6633
|
+
}
|
|
6634
|
+
});
|
|
6635
|
+
function updateDropdownHighlight() {
|
|
6636
|
+
const items = dropdown.querySelectorAll(
|
|
6637
|
+
".fb-richinput-dropdown-item"
|
|
6638
|
+
);
|
|
6639
|
+
items.forEach((el, i) => {
|
|
6640
|
+
el.style.background = i === dropdownState.selectedIndex ? "var(--fb-background-hover-color, #f3f4f6)" : "transparent";
|
|
6641
|
+
});
|
|
6642
|
+
}
|
|
6643
|
+
textarea.addEventListener("keydown", (e) => {
|
|
6644
|
+
if (!dropdownState.open) return;
|
|
6645
|
+
const labels = buildFileLabelsFromClosure();
|
|
6646
|
+
const filtered = filterFilesForDropdown(
|
|
6647
|
+
dropdownState.query,
|
|
6648
|
+
files,
|
|
6649
|
+
labels
|
|
6650
|
+
);
|
|
6651
|
+
if (e.key === "ArrowDown") {
|
|
6652
|
+
e.preventDefault();
|
|
6653
|
+
dropdownState.selectedIndex = Math.min(
|
|
6654
|
+
dropdownState.selectedIndex + 1,
|
|
6655
|
+
filtered.length - 1
|
|
6656
|
+
);
|
|
6657
|
+
updateDropdownHighlight();
|
|
6658
|
+
} else if (e.key === "ArrowUp") {
|
|
6659
|
+
e.preventDefault();
|
|
6660
|
+
dropdownState.selectedIndex = Math.max(
|
|
6661
|
+
dropdownState.selectedIndex - 1,
|
|
6662
|
+
0
|
|
6663
|
+
);
|
|
6664
|
+
updateDropdownHighlight();
|
|
6665
|
+
} else if (e.key === "Enter" && filtered.length > 0) {
|
|
6666
|
+
e.preventDefault();
|
|
6667
|
+
insertMention(filtered[dropdownState.selectedIndex]);
|
|
6668
|
+
} else if (e.key === "Escape") {
|
|
6669
|
+
closeDropdown();
|
|
6670
|
+
}
|
|
6671
|
+
});
|
|
6672
|
+
document.addEventListener(
|
|
6673
|
+
"click",
|
|
6674
|
+
(e) => {
|
|
6675
|
+
if (!outerDiv.contains(e.target) && !dropdown.contains(e.target)) {
|
|
6676
|
+
closeDropdown();
|
|
6677
|
+
}
|
|
6678
|
+
},
|
|
6679
|
+
{ signal: docListenerCtrl.signal }
|
|
6680
|
+
);
|
|
6681
|
+
function renderFilesRow() {
|
|
6682
|
+
clear(filesRow);
|
|
6683
|
+
if (files.length === 0) {
|
|
6684
|
+
filesRow.style.display = "none";
|
|
6685
|
+
return;
|
|
6686
|
+
}
|
|
6687
|
+
filesRow.style.display = "flex";
|
|
6688
|
+
files.forEach((rid) => {
|
|
6689
|
+
const meta = state.resourceIndex.get(rid);
|
|
6690
|
+
const thumbWrapper = document.createElement("div");
|
|
6691
|
+
thumbWrapper.className = "fb-richinput-file-thumb";
|
|
6692
|
+
thumbWrapper.style.cssText = `
|
|
6693
|
+
position: relative;
|
|
6694
|
+
width: 48px;
|
|
6695
|
+
height: 48px;
|
|
6696
|
+
border: 1px solid var(--fb-border-color, #d1d5db);
|
|
6697
|
+
border-radius: 8px;
|
|
6698
|
+
overflow: hidden;
|
|
6699
|
+
flex-shrink: 0;
|
|
6700
|
+
cursor: pointer;
|
|
6701
|
+
background: #fff;
|
|
6702
|
+
`;
|
|
6703
|
+
const thumbInner = document.createElement("div");
|
|
6704
|
+
thumbInner.style.cssText = "width: 48px; height: 48px; border-radius: inherit; overflow: hidden;";
|
|
6705
|
+
renderThumbContent(thumbInner, rid, meta, state);
|
|
6706
|
+
thumbWrapper.appendChild(thumbInner);
|
|
6707
|
+
const tooltipHandle = createTooltipHandle();
|
|
6708
|
+
const doMention = () => {
|
|
6709
|
+
var _a2, _b, _c;
|
|
6710
|
+
const cursorPos = (_a2 = textarea.selectionStart) != null ? _a2 : textarea.value.length;
|
|
6711
|
+
const labels = buildFileLabelsFromClosure();
|
|
6712
|
+
const label = (_c = (_b = labels.get(rid)) != null ? _b : meta == null ? void 0 : meta.name) != null ? _c : rid;
|
|
6713
|
+
const before = textarea.value.slice(0, cursorPos);
|
|
6714
|
+
const after = textarea.value.slice(cursorPos);
|
|
6715
|
+
const prefix = before.length > 0 && !/[\s\n]$/.test(before) ? "\n" : "";
|
|
6716
|
+
const mention = `${prefix}${formatMention(label)} `;
|
|
6717
|
+
textarea.value = `${before}${mention}${after}`;
|
|
6718
|
+
const newPos = cursorPos + mention.length;
|
|
6719
|
+
textarea.setSelectionRange(newPos, newPos);
|
|
6720
|
+
textarea.focus();
|
|
6721
|
+
textarea.dispatchEvent(new Event("input"));
|
|
6722
|
+
};
|
|
6723
|
+
const doRemove = () => {
|
|
6724
|
+
var _a2;
|
|
6725
|
+
const idx = files.indexOf(rid);
|
|
6726
|
+
if (idx !== -1) files.splice(idx, 1);
|
|
6727
|
+
renderFilesRow();
|
|
6728
|
+
updateBackdrop();
|
|
6729
|
+
writeHidden();
|
|
6730
|
+
(_a2 = ctx.instance) == null ? void 0 : _a2.triggerOnChange(pathKey, getCurrentValue());
|
|
6731
|
+
};
|
|
6732
|
+
thumbWrapper.addEventListener("mouseenter", () => {
|
|
6733
|
+
cancelHideTooltip(tooltipHandle);
|
|
6734
|
+
if (!tooltipHandle.element) {
|
|
6735
|
+
tooltipHandle.element = showFileTooltip(thumbWrapper, {
|
|
6736
|
+
rid,
|
|
6737
|
+
state,
|
|
6738
|
+
isReadonly: false,
|
|
6739
|
+
onMention: doMention,
|
|
6740
|
+
onRemove: doRemove
|
|
6741
|
+
});
|
|
6742
|
+
tooltipHandle.element.addEventListener("mouseenter", () => {
|
|
6743
|
+
cancelHideTooltip(tooltipHandle);
|
|
6744
|
+
});
|
|
6745
|
+
tooltipHandle.element.addEventListener("mouseleave", () => {
|
|
6746
|
+
scheduleHideTooltip(tooltipHandle);
|
|
6747
|
+
});
|
|
6748
|
+
}
|
|
6749
|
+
});
|
|
6750
|
+
thumbWrapper.addEventListener("mouseleave", () => {
|
|
6751
|
+
scheduleHideTooltip(tooltipHandle);
|
|
6752
|
+
});
|
|
6753
|
+
filesRow.appendChild(thumbWrapper);
|
|
6754
|
+
});
|
|
6755
|
+
}
|
|
6756
|
+
function uploadFile(file) {
|
|
6757
|
+
if (!state.config.uploadFile) return;
|
|
6758
|
+
const tempId = `temp-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
|
|
6759
|
+
state.resourceIndex.set(tempId, {
|
|
6760
|
+
name: file.name,
|
|
6761
|
+
type: file.type,
|
|
6762
|
+
size: file.size,
|
|
6763
|
+
uploadedAt: /* @__PURE__ */ new Date(),
|
|
6764
|
+
file
|
|
6765
|
+
});
|
|
6766
|
+
files.push(tempId);
|
|
6767
|
+
renderFilesRow();
|
|
6768
|
+
const thumbs = filesRow.querySelectorAll(
|
|
6769
|
+
".fb-richinput-file-thumb"
|
|
6770
|
+
);
|
|
6771
|
+
const loadingThumb = thumbs[thumbs.length - 1];
|
|
6772
|
+
if (loadingThumb) loadingThumb.style.opacity = "0.5";
|
|
6773
|
+
state.config.uploadFile(file).then((resourceId) => {
|
|
6774
|
+
var _a2;
|
|
6775
|
+
const idx = files.indexOf(tempId);
|
|
6776
|
+
if (idx !== -1) files[idx] = resourceId;
|
|
6777
|
+
state.resourceIndex.delete(tempId);
|
|
6778
|
+
state.resourceIndex.set(resourceId, {
|
|
6779
|
+
name: file.name,
|
|
6780
|
+
type: file.type,
|
|
6781
|
+
size: file.size,
|
|
6782
|
+
uploadedAt: /* @__PURE__ */ new Date(),
|
|
6783
|
+
file
|
|
6784
|
+
});
|
|
6785
|
+
renderFilesRow();
|
|
6786
|
+
updateBackdrop();
|
|
6787
|
+
writeHidden();
|
|
6788
|
+
(_a2 = ctx.instance) == null ? void 0 : _a2.triggerOnChange(pathKey, getCurrentValue());
|
|
6789
|
+
}).catch((err) => {
|
|
6790
|
+
var _a2, _b;
|
|
6791
|
+
const idx = files.indexOf(tempId);
|
|
6792
|
+
if (idx !== -1) files.splice(idx, 1);
|
|
6793
|
+
state.resourceIndex.delete(tempId);
|
|
6794
|
+
renderFilesRow();
|
|
6795
|
+
(_b = (_a2 = state.config).onUploadError) == null ? void 0 : _b.call(_a2, err, file);
|
|
6796
|
+
});
|
|
6797
|
+
}
|
|
6798
|
+
fileInput.addEventListener("change", () => {
|
|
6799
|
+
var _a2;
|
|
6800
|
+
const selected = fileInput.files;
|
|
6801
|
+
if (!selected || selected.length === 0) return;
|
|
6802
|
+
const maxFiles = (_a2 = element.maxFiles) != null ? _a2 : Infinity;
|
|
6803
|
+
for (let i = 0; i < selected.length && files.length < maxFiles; i++) {
|
|
6804
|
+
uploadFile(selected[i]);
|
|
6805
|
+
}
|
|
6806
|
+
fileInput.value = "";
|
|
6807
|
+
});
|
|
6808
|
+
textareaArea.appendChild(backdrop);
|
|
6809
|
+
textareaArea.appendChild(textarea);
|
|
6810
|
+
textareaArea.appendChild(paperclipBtn);
|
|
6811
|
+
textareaArea.appendChild(dropdown);
|
|
6812
|
+
outerDiv.appendChild(filesRow);
|
|
6813
|
+
outerDiv.appendChild(textareaArea);
|
|
6814
|
+
if (element.minLength != null || element.maxLength != null) {
|
|
6815
|
+
const counterRow = document.createElement("div");
|
|
6816
|
+
counterRow.style.cssText = "position: relative; padding: 2px 14px 6px; text-align: right;";
|
|
6817
|
+
const counter = createCharCounter(element, textarea, false);
|
|
6818
|
+
counter.style.cssText = `
|
|
6819
|
+
position: static;
|
|
6820
|
+
display: inline-block;
|
|
6821
|
+
font-size: var(--fb-font-size-small);
|
|
6822
|
+
color: var(--fb-text-secondary-color);
|
|
6823
|
+
pointer-events: none;
|
|
6824
|
+
`;
|
|
6825
|
+
counterRow.appendChild(counter);
|
|
6826
|
+
outerDiv.appendChild(counterRow);
|
|
6827
|
+
}
|
|
6828
|
+
outerDiv.appendChild(hiddenInput);
|
|
6829
|
+
outerDiv.appendChild(fileInput);
|
|
6830
|
+
writeHidden();
|
|
6831
|
+
updateBackdrop();
|
|
6832
|
+
hiddenInput._applyExternalUpdate = (value) => {
|
|
6833
|
+
var _a2;
|
|
6834
|
+
const rawText = (_a2 = value.text) != null ? _a2 : "";
|
|
6835
|
+
textarea.value = rawText ? replaceRidsWithFilenames(rawText, files, state) : "";
|
|
6836
|
+
textarea.dispatchEvent(new Event("input"));
|
|
6837
|
+
files.length = 0;
|
|
6838
|
+
for (const rid of value.files) files.push(rid);
|
|
6839
|
+
renderFilesRow();
|
|
6840
|
+
updateBackdrop();
|
|
6841
|
+
writeHidden();
|
|
6842
|
+
};
|
|
6843
|
+
wrapper.appendChild(outerDiv);
|
|
6844
|
+
renderFilesRow();
|
|
6845
|
+
const observer = new MutationObserver(() => {
|
|
6846
|
+
if (!outerDiv.isConnected) {
|
|
6847
|
+
docListenerCtrl.abort();
|
|
6848
|
+
mentionTooltip = removePortalTooltip(mentionTooltip);
|
|
6849
|
+
observer.disconnect();
|
|
6850
|
+
}
|
|
6851
|
+
});
|
|
6852
|
+
if (outerDiv.parentElement) {
|
|
6853
|
+
observer.observe(outerDiv.parentElement, { childList: true });
|
|
6854
|
+
}
|
|
6855
|
+
}
|
|
6856
|
+
function renderReadonlyMode(_element, ctx, wrapper, _pathKey, value) {
|
|
6857
|
+
var _a, _b;
|
|
6858
|
+
const state = ctx.state;
|
|
6859
|
+
const { text, files } = value;
|
|
6860
|
+
const ridToName = /* @__PURE__ */ new Map();
|
|
6861
|
+
for (const rid of files) {
|
|
6862
|
+
const meta = state.resourceIndex.get(rid);
|
|
6863
|
+
if (meta == null ? void 0 : meta.name) ridToName.set(rid, meta.name);
|
|
6864
|
+
}
|
|
6865
|
+
if (files.length > 0) {
|
|
6866
|
+
const filesRow = document.createElement("div");
|
|
6867
|
+
filesRow.style.cssText = "display: flex; flex-wrap: wrap; gap: 6px; padding-bottom: 8px;";
|
|
6868
|
+
files.forEach((rid) => {
|
|
6869
|
+
const meta = state.resourceIndex.get(rid);
|
|
6870
|
+
const thumbWrapper = document.createElement("div");
|
|
6871
|
+
thumbWrapper.style.cssText = `
|
|
6872
|
+
position: relative;
|
|
6873
|
+
width: 48px; height: 48px;
|
|
6874
|
+
border: 1px solid var(--fb-border-color, #d1d5db);
|
|
6875
|
+
border-radius: 8px;
|
|
6876
|
+
overflow: hidden;
|
|
6877
|
+
flex-shrink: 0;
|
|
6878
|
+
background: #fff;
|
|
6879
|
+
cursor: default;
|
|
6880
|
+
`;
|
|
6881
|
+
const thumbInner = document.createElement("div");
|
|
6882
|
+
thumbInner.style.cssText = "width: 48px; height: 48px; border-radius: inherit; overflow: hidden;";
|
|
6883
|
+
renderThumbContent(thumbInner, rid, meta, state);
|
|
6884
|
+
thumbWrapper.appendChild(thumbInner);
|
|
6885
|
+
const tooltipHandle = createTooltipHandle();
|
|
6886
|
+
thumbWrapper.addEventListener("mouseenter", () => {
|
|
6887
|
+
cancelHideTooltip(tooltipHandle);
|
|
6888
|
+
if (!tooltipHandle.element) {
|
|
6889
|
+
tooltipHandle.element = showFileTooltip(thumbWrapper, {
|
|
6890
|
+
rid,
|
|
6891
|
+
state,
|
|
6892
|
+
isReadonly: true
|
|
6893
|
+
});
|
|
6894
|
+
tooltipHandle.element.addEventListener("mouseenter", () => {
|
|
6895
|
+
cancelHideTooltip(tooltipHandle);
|
|
6896
|
+
});
|
|
6897
|
+
tooltipHandle.element.addEventListener("mouseleave", () => {
|
|
6898
|
+
scheduleHideTooltip(tooltipHandle);
|
|
6899
|
+
});
|
|
6900
|
+
}
|
|
6901
|
+
});
|
|
6902
|
+
thumbWrapper.addEventListener("mouseleave", () => {
|
|
6903
|
+
scheduleHideTooltip(tooltipHandle);
|
|
6904
|
+
});
|
|
6905
|
+
filesRow.appendChild(thumbWrapper);
|
|
6906
|
+
});
|
|
6907
|
+
wrapper.appendChild(filesRow);
|
|
6908
|
+
}
|
|
6909
|
+
if (text) {
|
|
6910
|
+
const textDiv = document.createElement("div");
|
|
6911
|
+
textDiv.style.cssText = `
|
|
6912
|
+
${TEXTAREA_FONT}
|
|
6913
|
+
color: var(--fb-text-color, #111827);
|
|
6914
|
+
white-space: pre-wrap;
|
|
6915
|
+
word-break: break-word;
|
|
6916
|
+
`;
|
|
6917
|
+
const tokens = findAtTokens(text);
|
|
6918
|
+
const resolvedTokens = tokens.filter(
|
|
6919
|
+
(tok) => ridToName.has(tok.name) || [...ridToName.values()].includes(tok.name)
|
|
6920
|
+
);
|
|
6921
|
+
if (resolvedTokens.length === 0) {
|
|
6922
|
+
textDiv.textContent = text;
|
|
6923
|
+
} else {
|
|
6924
|
+
let lastIndex = 0;
|
|
6925
|
+
for (const token of resolvedTokens) {
|
|
6926
|
+
if (token.start > lastIndex) {
|
|
6927
|
+
textDiv.appendChild(
|
|
6928
|
+
document.createTextNode(text.slice(lastIndex, token.start))
|
|
6929
|
+
);
|
|
6930
|
+
}
|
|
6931
|
+
const span = document.createElement("span");
|
|
6932
|
+
span.style.cssText = `
|
|
6933
|
+
display: inline;
|
|
6934
|
+
background: color-mix(in srgb, var(--fb-primary-color, #0066cc) 15%, transparent);
|
|
6935
|
+
color: var(--fb-primary-color, #0066cc);
|
|
6936
|
+
border-radius: 8px;
|
|
6937
|
+
padding: 1px 6px;
|
|
6938
|
+
font-weight: 500;
|
|
6939
|
+
cursor: default;
|
|
6940
|
+
`;
|
|
6941
|
+
const rid = ridToName.has(token.name) ? token.name : (_a = [...ridToName.entries()].find(([, n]) => n === token.name)) == null ? void 0 : _a[0];
|
|
6942
|
+
const displayName = (_b = ridToName.get(token.name)) != null ? _b : token.name;
|
|
6943
|
+
span.textContent = `@${displayName}`;
|
|
6944
|
+
if (rid) {
|
|
6945
|
+
let spanTooltip = null;
|
|
6946
|
+
const mentionRid = rid;
|
|
6947
|
+
span.addEventListener("mouseenter", () => {
|
|
6948
|
+
spanTooltip = removePortalTooltip(spanTooltip);
|
|
6949
|
+
spanTooltip = showMentionTooltip(span, mentionRid, state);
|
|
6950
|
+
});
|
|
6951
|
+
span.addEventListener("mouseleave", () => {
|
|
6952
|
+
spanTooltip = removePortalTooltip(spanTooltip);
|
|
6953
|
+
});
|
|
6954
|
+
}
|
|
6955
|
+
textDiv.appendChild(span);
|
|
6956
|
+
lastIndex = token.end;
|
|
6957
|
+
}
|
|
6958
|
+
if (lastIndex < text.length) {
|
|
6959
|
+
textDiv.appendChild(document.createTextNode(text.slice(lastIndex)));
|
|
6960
|
+
}
|
|
6961
|
+
}
|
|
6962
|
+
wrapper.appendChild(textDiv);
|
|
6963
|
+
}
|
|
6964
|
+
if (!text && files.length === 0) {
|
|
6965
|
+
const empty = document.createElement("div");
|
|
6966
|
+
empty.style.cssText = "color: var(--fb-text-muted-color, #6b7280); font-size: var(--fb-font-size, 14px);";
|
|
6967
|
+
empty.textContent = "\u2014";
|
|
6968
|
+
wrapper.appendChild(empty);
|
|
6969
|
+
}
|
|
6970
|
+
}
|
|
6971
|
+
function renderRichInputElement(element, ctx, wrapper, pathKey) {
|
|
6972
|
+
var _a, _b, _c, _d;
|
|
6973
|
+
const state = ctx.state;
|
|
6974
|
+
const textKey = (_a = element.textKey) != null ? _a : "text";
|
|
6975
|
+
const filesKey = (_b = element.filesKey) != null ? _b : "files";
|
|
6976
|
+
const rawPrefill = ctx.prefill[element.key];
|
|
6977
|
+
let initialValue;
|
|
6978
|
+
if (rawPrefill && typeof rawPrefill === "object" && !Array.isArray(rawPrefill)) {
|
|
6979
|
+
const obj = rawPrefill;
|
|
6980
|
+
const textVal = (_c = obj[textKey]) != null ? _c : obj["text"];
|
|
6981
|
+
const filesVal = (_d = obj[filesKey]) != null ? _d : obj["files"];
|
|
6982
|
+
initialValue = {
|
|
6983
|
+
text: typeof textVal === "string" ? textVal : null,
|
|
6984
|
+
files: Array.isArray(filesVal) ? filesVal : []
|
|
6985
|
+
};
|
|
6986
|
+
} else if (typeof rawPrefill === "string") {
|
|
6987
|
+
initialValue = { text: rawPrefill || null, files: [] };
|
|
6988
|
+
} else {
|
|
6989
|
+
initialValue = { text: null, files: [] };
|
|
6990
|
+
}
|
|
6991
|
+
for (const rid of initialValue.files) {
|
|
6992
|
+
if (!state.resourceIndex.has(rid)) {
|
|
6993
|
+
state.resourceIndex.set(rid, {
|
|
6994
|
+
name: rid,
|
|
6995
|
+
type: "application/octet-stream",
|
|
6996
|
+
size: 0,
|
|
6997
|
+
uploadedAt: /* @__PURE__ */ new Date(),
|
|
6998
|
+
file: void 0
|
|
6999
|
+
});
|
|
7000
|
+
}
|
|
7001
|
+
}
|
|
7002
|
+
if (state.config.readonly) {
|
|
7003
|
+
renderReadonlyMode(element, ctx, wrapper, pathKey, initialValue);
|
|
7004
|
+
} else {
|
|
7005
|
+
if (!state.config.uploadFile) {
|
|
7006
|
+
throw new Error(
|
|
7007
|
+
`RichInput field "${element.key}" requires uploadFile handler in config`
|
|
7008
|
+
);
|
|
7009
|
+
}
|
|
7010
|
+
renderEditMode(element, ctx, wrapper, pathKey, initialValue);
|
|
7011
|
+
}
|
|
7012
|
+
}
|
|
7013
|
+
function validateRichInputElement(element, key, context) {
|
|
7014
|
+
var _a, _b;
|
|
7015
|
+
const { scopeRoot, state, skipValidation } = context;
|
|
7016
|
+
const errors = [];
|
|
7017
|
+
const textKey = (_a = element.textKey) != null ? _a : "text";
|
|
7018
|
+
const filesKey = (_b = element.filesKey) != null ? _b : "files";
|
|
7019
|
+
const hiddenInput = scopeRoot.querySelector(
|
|
7020
|
+
`[name="${key}"]`
|
|
7021
|
+
);
|
|
7022
|
+
if (!hiddenInput) {
|
|
7023
|
+
return { value: null, errors };
|
|
7024
|
+
}
|
|
7025
|
+
let rawValue = {};
|
|
7026
|
+
try {
|
|
7027
|
+
const parsed = JSON.parse(hiddenInput.value);
|
|
7028
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
7029
|
+
rawValue = parsed;
|
|
7030
|
+
} else {
|
|
7031
|
+
errors.push(`${key}: invalid richinput data`);
|
|
7032
|
+
return { value: null, errors };
|
|
7033
|
+
}
|
|
7034
|
+
} catch {
|
|
7035
|
+
errors.push(`${key}: invalid richinput data`);
|
|
7036
|
+
return { value: null, errors };
|
|
7037
|
+
}
|
|
7038
|
+
const textVal = rawValue[textKey];
|
|
7039
|
+
const filesVal = rawValue[filesKey];
|
|
7040
|
+
const text = textVal === null || typeof textVal === "string" ? textVal : null;
|
|
7041
|
+
const files = Array.isArray(filesVal) ? filesVal : [];
|
|
7042
|
+
const value = {
|
|
7043
|
+
[textKey]: text != null ? text : null,
|
|
7044
|
+
[filesKey]: files
|
|
7045
|
+
};
|
|
7046
|
+
if (!skipValidation) {
|
|
7047
|
+
const textEmpty = !text || text.trim() === "";
|
|
7048
|
+
const filesEmpty = files.length === 0;
|
|
7049
|
+
if (element.required && textEmpty && filesEmpty) {
|
|
7050
|
+
errors.push(`${key}: ${t("required", state)}`);
|
|
7051
|
+
}
|
|
7052
|
+
if (!textEmpty && text) {
|
|
7053
|
+
if (element.minLength != null && text.length < element.minLength) {
|
|
7054
|
+
errors.push(
|
|
7055
|
+
`${key}: ${t("minLength", state, { min: element.minLength })}`
|
|
7056
|
+
);
|
|
7057
|
+
}
|
|
7058
|
+
if (element.maxLength != null && text.length > element.maxLength) {
|
|
7059
|
+
errors.push(
|
|
7060
|
+
`${key}: ${t("maxLength", state, { max: element.maxLength })}`
|
|
7061
|
+
);
|
|
7062
|
+
}
|
|
7063
|
+
}
|
|
7064
|
+
if (element.maxFiles != null && files.length > element.maxFiles) {
|
|
7065
|
+
errors.push(
|
|
7066
|
+
`${key}: ${t("maxFiles", state, { max: element.maxFiles })}`
|
|
7067
|
+
);
|
|
7068
|
+
}
|
|
7069
|
+
}
|
|
7070
|
+
return { value, errors };
|
|
7071
|
+
}
|
|
7072
|
+
function updateRichInputField(element, fieldPath, value, context) {
|
|
7073
|
+
var _a, _b, _c, _d;
|
|
7074
|
+
const { scopeRoot } = context;
|
|
7075
|
+
const hiddenInput = scopeRoot.querySelector(
|
|
7076
|
+
`[name="${fieldPath}"]`
|
|
7077
|
+
);
|
|
7078
|
+
if (!hiddenInput) {
|
|
7079
|
+
console.warn(
|
|
7080
|
+
`updateRichInputField: no hidden input found for "${fieldPath}". Re-render to reflect new data.`
|
|
7081
|
+
);
|
|
7082
|
+
return;
|
|
7083
|
+
}
|
|
7084
|
+
let normalized = null;
|
|
7085
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
7086
|
+
const obj = value;
|
|
7087
|
+
const textKey = (_a = element.textKey) != null ? _a : "text";
|
|
7088
|
+
const filesKey = (_b = element.filesKey) != null ? _b : "files";
|
|
7089
|
+
const textVal = (_c = obj[textKey]) != null ? _c : obj["text"];
|
|
7090
|
+
const filesVal = (_d = obj[filesKey]) != null ? _d : obj["files"];
|
|
7091
|
+
if (textVal !== void 0 || filesVal !== void 0) {
|
|
7092
|
+
normalized = {
|
|
7093
|
+
text: typeof textVal === "string" ? textVal : null,
|
|
7094
|
+
files: Array.isArray(filesVal) ? filesVal : []
|
|
7095
|
+
};
|
|
7096
|
+
}
|
|
7097
|
+
}
|
|
7098
|
+
if (normalized && hiddenInput._applyExternalUpdate) {
|
|
7099
|
+
hiddenInput._applyExternalUpdate(normalized);
|
|
7100
|
+
} else if (normalized) {
|
|
7101
|
+
hiddenInput.value = JSON.stringify(normalized);
|
|
7102
|
+
}
|
|
7103
|
+
}
|
|
7104
|
+
|
|
5796
7105
|
// src/components/index.ts
|
|
5797
7106
|
function showTooltip(tooltipId, button) {
|
|
5798
7107
|
const tooltip = document.getElementById(tooltipId);
|
|
@@ -6145,6 +7454,9 @@ function dispatchToRenderer(element, ctx, wrapper, pathKey) {
|
|
|
6145
7454
|
case "table":
|
|
6146
7455
|
renderTableElement(element, ctx, wrapper, pathKey);
|
|
6147
7456
|
break;
|
|
7457
|
+
case "richinput":
|
|
7458
|
+
renderRichInputElement(element, ctx, wrapper, pathKey);
|
|
7459
|
+
break;
|
|
6148
7460
|
default: {
|
|
6149
7461
|
const unsupported = document.createElement("div");
|
|
6150
7462
|
unsupported.className = "text-red-500 text-sm";
|
|
@@ -6241,7 +7553,11 @@ var defaultConfig = {
|
|
|
6241
7553
|
tableRemoveRow: "Remove row",
|
|
6242
7554
|
tableRemoveColumn: "Remove column",
|
|
6243
7555
|
tableMergeCells: "Merge cells (Ctrl+M)",
|
|
6244
|
-
tableSplitCell: "Split cell (Ctrl+Shift+M)"
|
|
7556
|
+
tableSplitCell: "Split cell (Ctrl+Shift+M)",
|
|
7557
|
+
richinputPlaceholder: "Type text...",
|
|
7558
|
+
richinputAttachFile: "Attach file",
|
|
7559
|
+
richinputMention: "Mention",
|
|
7560
|
+
richinputRemoveFile: "Remove"
|
|
6245
7561
|
},
|
|
6246
7562
|
ru: {
|
|
6247
7563
|
// UI texts
|
|
@@ -6293,7 +7609,11 @@ var defaultConfig = {
|
|
|
6293
7609
|
tableRemoveRow: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0441\u0442\u0440\u043E\u043A\u0443",
|
|
6294
7610
|
tableRemoveColumn: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0441\u0442\u043E\u043B\u0431\u0435\u0446",
|
|
6295
7611
|
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)"
|
|
7612
|
+
tableSplitCell: "\u0420\u0430\u0437\u0434\u0435\u043B\u0438\u0442\u044C \u044F\u0447\u0435\u0439\u043A\u0443 (Ctrl+Shift+M)",
|
|
7613
|
+
richinputPlaceholder: "\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0442\u0435\u043A\u0441\u0442...",
|
|
7614
|
+
richinputAttachFile: "\u041F\u0440\u0438\u043A\u0440\u0435\u043F\u0438\u0442\u044C \u0444\u0430\u0439\u043B",
|
|
7615
|
+
richinputMention: "\u0423\u043F\u043E\u043C\u044F\u043D\u0443\u0442\u044C",
|
|
7616
|
+
richinputRemoveFile: "\u0423\u0434\u0430\u043B\u0438\u0442\u044C"
|
|
6297
7617
|
}
|
|
6298
7618
|
},
|
|
6299
7619
|
theme: {}
|
|
@@ -6571,6 +7891,10 @@ var componentRegistry = {
|
|
|
6571
7891
|
table: {
|
|
6572
7892
|
validate: validateTableElement,
|
|
6573
7893
|
update: updateTableField
|
|
7894
|
+
},
|
|
7895
|
+
richinput: {
|
|
7896
|
+
validate: validateRichInputElement,
|
|
7897
|
+
update: updateRichInputField
|
|
6574
7898
|
}
|
|
6575
7899
|
};
|
|
6576
7900
|
function getComponentOperations(elementType) {
|