@antdv-next/x 1.2.2 → 1.2.3-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -1
- package/dist/index.umd.js +13111 -748
- package/dist/package.js +1 -1
- package/dist/sender/Sender.d.ts +23 -5
- package/dist/sender/Sender.js +13 -3
- package/dist/sender/components/Skill.d.ts +11 -1
- package/dist/sender/components/Skill.js +31 -3
- package/dist/sender/components/SlotTextAreaProseMirror.js +1144 -0
- package/dist/sender/components/TextArea.js +44 -3
- package/dist/sender/components/prosemirror/model.d.ts +15 -0
- package/dist/sender/components/prosemirror/model.js +276 -0
- package/dist/sender/components/prosemirror/value.d.ts +11 -0
- package/dist/sender/components/prosemirror/value.js +171 -0
- package/dist/sender/index.d.ts +2 -2
- package/dist/sender/interface.d.ts +14 -1
- package/dist/sender/style/slot-textarea.js +9 -2
- package/dist/thought-chain/style/index.d.ts +71 -3
- package/dist/thought-chain/style/index.js +25 -14
- package/dist/thought-chain/style/item.d.ts +5 -5
- package/dist/thought-chain/style/item.js +5 -5
- package/dist/thought-chain/style/thought-chain.d.ts +2 -1
- package/dist/thought-chain/style/thought-chain.js +8 -20
- package/es/antdv-next-x.esm.js +14919 -6911
- package/package.json +7 -1
- package/dist/sender/components/SlotTextArea.js +0 -808
- /package/dist/sender/components/{SlotTextArea.d.ts → SlotTextAreaProseMirror.d.ts} +0 -0
|
@@ -1,808 +0,0 @@
|
|
|
1
|
-
import warning from "../../_utils/warning.js";
|
|
2
|
-
import { useSenderContext } from "../context.js";
|
|
3
|
-
import Skill_default from "./Skill.js";
|
|
4
|
-
import { classNames } from "@v-c/util";
|
|
5
|
-
import { computed, createTextVNode, createVNode, defineComponent, nextTick, onBeforeUnmount, ref, render, watch } from "vue";
|
|
6
|
-
import { Dropdown, Input } from "antdv-next";
|
|
7
|
-
//#region components/sender/components/SlotTextArea.tsx
|
|
8
|
-
var SUPPORTED_INPUT_TYPES = /* @__PURE__ */ new Set([
|
|
9
|
-
"input",
|
|
10
|
-
"select",
|
|
11
|
-
"custom",
|
|
12
|
-
"content"
|
|
13
|
-
]);
|
|
14
|
-
function stringifyValue(value) {
|
|
15
|
-
if (value === void 0 || value === null) return "";
|
|
16
|
-
if (typeof value === "string") return value;
|
|
17
|
-
if (typeof value === "number" || typeof value === "boolean") return `${value}`;
|
|
18
|
-
try {
|
|
19
|
-
return JSON.stringify(value);
|
|
20
|
-
} catch {
|
|
21
|
-
return "";
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
function getDefaultSlotValue(config) {
|
|
25
|
-
if (!config.key) return void 0;
|
|
26
|
-
const props = config.props ?? {};
|
|
27
|
-
if (SUPPORTED_INPUT_TYPES.has(config.type)) return props.defaultValue ?? "";
|
|
28
|
-
return props.value ?? props.label ?? "";
|
|
29
|
-
}
|
|
30
|
-
var SlotTextArea_default = /* @__PURE__ */ defineComponent({
|
|
31
|
-
name: "SlotTextArea",
|
|
32
|
-
setup(_, { expose }) {
|
|
33
|
-
const senderCtx = useSenderContext();
|
|
34
|
-
const editableRef = ref();
|
|
35
|
-
const slotDomMap = ref(/* @__PURE__ */ new Map());
|
|
36
|
-
const slotConfigMap = ref(/* @__PURE__ */ new Map());
|
|
37
|
-
const slotValues = ref({});
|
|
38
|
-
const mountedDomSet = ref(/* @__PURE__ */ new Set());
|
|
39
|
-
const skillDomRef = ref(null);
|
|
40
|
-
const currentSkillRef = ref(void 0);
|
|
41
|
-
const lastSlotConfigRef = ref(void 0);
|
|
42
|
-
const lastSkillRef = ref(void 0);
|
|
43
|
-
const isComposing = ref(false);
|
|
44
|
-
const keyLock = ref(false);
|
|
45
|
-
const prefixCls = computed(() => senderCtx.value.prefixCls || "antd-sender");
|
|
46
|
-
const mergeInputStyle = computed(() => ({
|
|
47
|
-
...senderCtx.value.styles?.input,
|
|
48
|
-
...getAutoSizeStyle()
|
|
49
|
-
}));
|
|
50
|
-
const getAutoSizeStyle = () => {
|
|
51
|
-
const autoSize = senderCtx.value.autoSize;
|
|
52
|
-
if (autoSize === true) return { height: "auto" };
|
|
53
|
-
if (!autoSize || typeof autoSize !== "object") return {};
|
|
54
|
-
const lineHeight = 22;
|
|
55
|
-
return {
|
|
56
|
-
minHeight: autoSize.minRows ? `${autoSize.minRows * lineHeight}px` : void 0,
|
|
57
|
-
maxHeight: autoSize.maxRows ? `${autoSize.maxRows * lineHeight}px` : void 0,
|
|
58
|
-
overflowY: "auto"
|
|
59
|
-
};
|
|
60
|
-
};
|
|
61
|
-
const getNodeInfo = (targetNode) => {
|
|
62
|
-
if (!targetNode?.dataset) return null;
|
|
63
|
-
const slotKey = targetNode.dataset.slotKey;
|
|
64
|
-
return {
|
|
65
|
-
slotKey,
|
|
66
|
-
skillKey: targetNode.dataset.skillKey,
|
|
67
|
-
nodeType: targetNode.dataset.nodeType,
|
|
68
|
-
slotConfig: slotKey ? slotConfigMap.value.get(slotKey) : void 0
|
|
69
|
-
};
|
|
70
|
-
};
|
|
71
|
-
const unmountDom = (container) => {
|
|
72
|
-
if (!container) return;
|
|
73
|
-
render(null, container);
|
|
74
|
-
mountedDomSet.value.delete(container);
|
|
75
|
-
};
|
|
76
|
-
const unmountAllPortals = () => {
|
|
77
|
-
mountedDomSet.value.forEach((container) => {
|
|
78
|
-
render(null, container);
|
|
79
|
-
});
|
|
80
|
-
mountedDomSet.value.clear();
|
|
81
|
-
};
|
|
82
|
-
const buildSkillSpan = (key) => {
|
|
83
|
-
const span = document.createElement("span");
|
|
84
|
-
span.setAttribute("contenteditable", "false");
|
|
85
|
-
span.dataset.skillKey = key;
|
|
86
|
-
span.dataset.placeholder = senderCtx.value.placeholder || "";
|
|
87
|
-
span.className = `${prefixCls.value}-skill`;
|
|
88
|
-
return span;
|
|
89
|
-
};
|
|
90
|
-
const buildSlotSpan = (key) => {
|
|
91
|
-
const span = document.createElement("span");
|
|
92
|
-
span.setAttribute("contenteditable", "false");
|
|
93
|
-
span.dataset.slotKey = key;
|
|
94
|
-
span.className = `${prefixCls.value}-slot`;
|
|
95
|
-
return span;
|
|
96
|
-
};
|
|
97
|
-
const buildEditSlotSpan = (config) => {
|
|
98
|
-
const span = document.createElement("span");
|
|
99
|
-
span.setAttribute("contenteditable", senderCtx.value.readOnly ? "false" : "true");
|
|
100
|
-
span.dataset.slotKey = config.key;
|
|
101
|
-
span.className = `${prefixCls.value}-slot ${prefixCls.value}-slot-content`;
|
|
102
|
-
return span;
|
|
103
|
-
};
|
|
104
|
-
const buildSpaceSpan = (slotKey, position) => {
|
|
105
|
-
const span = document.createElement("span");
|
|
106
|
-
span.setAttribute("contenteditable", "false");
|
|
107
|
-
span.dataset.slotKey = slotKey;
|
|
108
|
-
span.dataset.nodeType = "nbsp";
|
|
109
|
-
span.className = `${prefixCls.value}-slot-${position} ${prefixCls.value}-slot-no-width`;
|
|
110
|
-
span.textContent = "\xA0";
|
|
111
|
-
return span;
|
|
112
|
-
};
|
|
113
|
-
const getNodeTextValue = (node) => {
|
|
114
|
-
if (node.nodeType === Node.TEXT_NODE) return node.textContent ?? "";
|
|
115
|
-
if (node.nodeType !== Node.ELEMENT_NODE) return "";
|
|
116
|
-
const element = node;
|
|
117
|
-
const nodeInfo = getNodeInfo(element);
|
|
118
|
-
if (!nodeInfo) return element.innerText || "";
|
|
119
|
-
if (nodeInfo.skillKey) return "";
|
|
120
|
-
if (nodeInfo.slotKey) {
|
|
121
|
-
if (nodeInfo.nodeType === "nbsp") return " ";
|
|
122
|
-
const config = nodeInfo.slotConfig;
|
|
123
|
-
if (!config) return element.innerText || "";
|
|
124
|
-
const rawValue = config.type === "content" ? element.innerText || "" : slotValues.value[nodeInfo.slotKey];
|
|
125
|
-
return config.formatResult?.(rawValue) ?? stringifyValue(rawValue);
|
|
126
|
-
}
|
|
127
|
-
return element.innerText || "";
|
|
128
|
-
};
|
|
129
|
-
const getEditorValue = () => {
|
|
130
|
-
const editable = editableRef.value;
|
|
131
|
-
const emptyResult = {
|
|
132
|
-
value: "",
|
|
133
|
-
slotConfig: [],
|
|
134
|
-
skill: void 0
|
|
135
|
-
};
|
|
136
|
-
if (!editable) return emptyResult;
|
|
137
|
-
const childNodes = editable.childNodes;
|
|
138
|
-
if (childNodes.length === 0) return emptyResult;
|
|
139
|
-
const textList = [];
|
|
140
|
-
const currentSlotConfig = [];
|
|
141
|
-
let currentSkill;
|
|
142
|
-
childNodes.forEach((node) => {
|
|
143
|
-
const textValue = getNodeTextValue(node);
|
|
144
|
-
textList.push(textValue);
|
|
145
|
-
if (node.nodeType === Node.TEXT_NODE) {
|
|
146
|
-
if (textValue) currentSlotConfig.push({
|
|
147
|
-
type: "text",
|
|
148
|
-
value: textValue
|
|
149
|
-
});
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
153
|
-
const info = getNodeInfo(node);
|
|
154
|
-
if (!info) return;
|
|
155
|
-
if (info.skillKey && currentSkillRef.value) currentSkill = currentSkillRef.value;
|
|
156
|
-
if (info.slotKey && info.nodeType !== "nbsp") {
|
|
157
|
-
const cfg = slotConfigMap.value.get(info.slotKey);
|
|
158
|
-
if (cfg) currentSlotConfig.push({
|
|
159
|
-
...cfg,
|
|
160
|
-
value: textValue
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
});
|
|
165
|
-
return {
|
|
166
|
-
value: textList.join(""),
|
|
167
|
-
slotConfig: currentSlotConfig,
|
|
168
|
-
skill: currentSkill
|
|
169
|
-
};
|
|
170
|
-
};
|
|
171
|
-
const updateSubmitDisabled = () => {
|
|
172
|
-
const value = getEditorValue();
|
|
173
|
-
senderCtx.value.setSubmitDisabled?.(!value.value && value.slotConfig.length === 0 && !value.skill);
|
|
174
|
-
};
|
|
175
|
-
const triggerValueChange = (event) => {
|
|
176
|
-
const value = getEditorValue();
|
|
177
|
-
senderCtx.value.onChange?.(value.value, event, value.slotConfig, value.skill);
|
|
178
|
-
updateSkillEmptyStatus(value);
|
|
179
|
-
updateSubmitDisabled();
|
|
180
|
-
};
|
|
181
|
-
const updateSlot = (key, value, event) => {
|
|
182
|
-
slotValues.value = {
|
|
183
|
-
...slotValues.value,
|
|
184
|
-
[key]: value
|
|
185
|
-
};
|
|
186
|
-
const config = slotConfigMap.value.get(key);
|
|
187
|
-
const dom = slotDomMap.value.get(key);
|
|
188
|
-
if (config && dom && config.type !== "content") renderSlot(config, dom);
|
|
189
|
-
triggerValueChange(event);
|
|
190
|
-
};
|
|
191
|
-
const buildSelectMenuItems = (options) => {
|
|
192
|
-
return (options ?? []).map((option) => ({
|
|
193
|
-
label: option,
|
|
194
|
-
key: option
|
|
195
|
-
}));
|
|
196
|
-
};
|
|
197
|
-
const renderSlot = (config, slotDom) => {
|
|
198
|
-
if (!("key" in config) || !config.key) return;
|
|
199
|
-
const slotValue = slotValues.value[config.key];
|
|
200
|
-
let node = null;
|
|
201
|
-
switch (config.type) {
|
|
202
|
-
case "input":
|
|
203
|
-
node = createVNode(Input, {
|
|
204
|
-
"class": `${prefixCls.value}-slot-input`,
|
|
205
|
-
"placeholder": config.props?.placeholder,
|
|
206
|
-
"value": slotValue ?? "",
|
|
207
|
-
"size": "small",
|
|
208
|
-
"variant": "borderless",
|
|
209
|
-
"readonly": senderCtx.value.readOnly,
|
|
210
|
-
"disabled": senderCtx.value.disabled,
|
|
211
|
-
"onKeydown": onInternalKeyDown,
|
|
212
|
-
"onChange": (e) => {
|
|
213
|
-
updateSlot(config.key, e?.target?.value ?? "", e);
|
|
214
|
-
}
|
|
215
|
-
}, null);
|
|
216
|
-
break;
|
|
217
|
-
case "select": {
|
|
218
|
-
const displayValue = stringifyValue(slotValue);
|
|
219
|
-
node = createVNode(Dropdown, {
|
|
220
|
-
"disabled": senderCtx.value.readOnly || senderCtx.value.disabled,
|
|
221
|
-
"trigger": ["click"],
|
|
222
|
-
"menu": {
|
|
223
|
-
items: buildSelectMenuItems(config.props?.options),
|
|
224
|
-
selectable: true,
|
|
225
|
-
selectedKeys: displayValue ? [displayValue] : [],
|
|
226
|
-
onSelect: ({ key, domEvent }) => {
|
|
227
|
-
updateSlot(config.key, key, domEvent);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
}, { default: () => [createVNode("span", { "class": classNames([`${prefixCls.value}-slot-select`, {
|
|
231
|
-
placeholder: !displayValue,
|
|
232
|
-
[`${prefixCls.value}-slot-select-selector-value`]: !!displayValue
|
|
233
|
-
}]) }, [createVNode("span", {
|
|
234
|
-
"data-placeholder": config.props?.placeholder,
|
|
235
|
-
"class": `${prefixCls.value}-slot-select-value`
|
|
236
|
-
}, [displayValue]), createVNode("span", { "class": `${prefixCls.value}-slot-select-arrow` }, [createTextVNode("▼")])])] });
|
|
237
|
-
break;
|
|
238
|
-
}
|
|
239
|
-
case "tag":
|
|
240
|
-
node = createVNode("span", { "class": `${prefixCls.value}-slot-tag` }, [config.props?.label ?? config.props?.value ?? ""]);
|
|
241
|
-
break;
|
|
242
|
-
case "custom":
|
|
243
|
-
node = config.customRender?.(slotValue, (nextValue) => {
|
|
244
|
-
updateSlot(config.key, nextValue);
|
|
245
|
-
}, {
|
|
246
|
-
disabled: senderCtx.value.disabled,
|
|
247
|
-
readOnly: senderCtx.value.readOnly
|
|
248
|
-
}, config);
|
|
249
|
-
break;
|
|
250
|
-
case "content":
|
|
251
|
-
slotDom.innerText = stringifyValue(slotValue);
|
|
252
|
-
slotDom.dataset.placeholder = config.props?.placeholder ?? "";
|
|
253
|
-
return;
|
|
254
|
-
default: return;
|
|
255
|
-
}
|
|
256
|
-
if (!node) return;
|
|
257
|
-
mountedDomSet.value.add(slotDom);
|
|
258
|
-
render(node, slotDom);
|
|
259
|
-
};
|
|
260
|
-
const buildSlotNodes = (configs) => {
|
|
261
|
-
const list = [];
|
|
262
|
-
configs.forEach((config) => {
|
|
263
|
-
if (config.type === "text") {
|
|
264
|
-
list.push(document.createTextNode(config.value || ""));
|
|
265
|
-
return;
|
|
266
|
-
}
|
|
267
|
-
if (!("key" in config) || !config.key) {
|
|
268
|
-
warning(false, "Sender", "Slot key is missing");
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
|
-
slotConfigMap.value.set(config.key, config);
|
|
272
|
-
if (config.type === "content") {
|
|
273
|
-
const before = buildSpaceSpan(config.key, "before");
|
|
274
|
-
const slot = buildEditSlotSpan(config);
|
|
275
|
-
const after = buildSpaceSpan(config.key, "after");
|
|
276
|
-
slotValues.value[config.key] = slotValues.value[config.key] ?? getDefaultSlotValue(config);
|
|
277
|
-
slot.innerText = stringifyValue(slotValues.value[config.key]);
|
|
278
|
-
slot.dataset.placeholder = config.props?.placeholder ?? "";
|
|
279
|
-
slotDomMap.value.set(`${config.key}_before`, before);
|
|
280
|
-
slotDomMap.value.set(config.key, slot);
|
|
281
|
-
slotDomMap.value.set(`${config.key}_after`, after);
|
|
282
|
-
list.push(before, slot, after);
|
|
283
|
-
return;
|
|
284
|
-
}
|
|
285
|
-
const slot = buildSlotSpan(config.key);
|
|
286
|
-
slotValues.value[config.key] = slotValues.value[config.key] ?? getDefaultSlotValue(config);
|
|
287
|
-
slotDomMap.value.set(config.key, slot);
|
|
288
|
-
renderSlot(config, slot);
|
|
289
|
-
list.push(slot);
|
|
290
|
-
});
|
|
291
|
-
return list;
|
|
292
|
-
};
|
|
293
|
-
const mergeSlotConfig = (configs) => {
|
|
294
|
-
const nextValues = { ...slotValues.value };
|
|
295
|
-
configs.forEach((config) => {
|
|
296
|
-
if ("key" in config && config.key) {
|
|
297
|
-
slotConfigMap.value.set(config.key, config);
|
|
298
|
-
if (!(config.key in nextValues)) nextValues[config.key] = getDefaultSlotValue(config);
|
|
299
|
-
}
|
|
300
|
-
});
|
|
301
|
-
slotValues.value = nextValues;
|
|
302
|
-
};
|
|
303
|
-
const clearEditor = () => {
|
|
304
|
-
unmountAllPortals();
|
|
305
|
-
slotDomMap.value.clear();
|
|
306
|
-
slotConfigMap.value.clear();
|
|
307
|
-
slotValues.value = {};
|
|
308
|
-
currentSkillRef.value = void 0;
|
|
309
|
-
if (skillDomRef.value) unmountDom(skillDomRef.value);
|
|
310
|
-
skillDomRef.value = null;
|
|
311
|
-
if (editableRef.value) editableRef.value.innerHTML = "";
|
|
312
|
-
};
|
|
313
|
-
const getSelection = () => {
|
|
314
|
-
if (typeof window === "undefined") return null;
|
|
315
|
-
return window.getSelection();
|
|
316
|
-
};
|
|
317
|
-
const setRange = (range, selection) => {
|
|
318
|
-
selection.removeAllRanges();
|
|
319
|
-
selection.addRange(range);
|
|
320
|
-
};
|
|
321
|
-
const setCursorPosition = (targetNode, offset, preventScroll) => {
|
|
322
|
-
const editable = editableRef.value;
|
|
323
|
-
if (!editable) return;
|
|
324
|
-
editable.focus({ preventScroll });
|
|
325
|
-
const selection = getSelection();
|
|
326
|
-
if (!selection) return;
|
|
327
|
-
const range = document.createRange();
|
|
328
|
-
range.setStart(targetNode, offset);
|
|
329
|
-
range.collapse(true);
|
|
330
|
-
setRange(range, selection);
|
|
331
|
-
};
|
|
332
|
-
const setStartCursor = (preventScroll) => {
|
|
333
|
-
const editable = editableRef.value;
|
|
334
|
-
if (!editable) return;
|
|
335
|
-
const startIndex = skillDomRef.value ? 1 : 0;
|
|
336
|
-
setCursorPosition(editable, startIndex, preventScroll);
|
|
337
|
-
};
|
|
338
|
-
const setEndCursor = (preventScroll) => {
|
|
339
|
-
const editable = editableRef.value;
|
|
340
|
-
if (!editable) return;
|
|
341
|
-
editable.focus({ preventScroll });
|
|
342
|
-
const selection = getSelection();
|
|
343
|
-
if (!selection) return;
|
|
344
|
-
const range = document.createRange();
|
|
345
|
-
range.selectNodeContents(editable);
|
|
346
|
-
range.collapse(false);
|
|
347
|
-
setRange(range, selection);
|
|
348
|
-
};
|
|
349
|
-
const selectAll = (preventScroll) => {
|
|
350
|
-
const editable = editableRef.value;
|
|
351
|
-
if (!editable) return;
|
|
352
|
-
editable.focus({ preventScroll });
|
|
353
|
-
const selection = getSelection();
|
|
354
|
-
if (!selection) return;
|
|
355
|
-
const range = document.createRange();
|
|
356
|
-
range.selectNodeContents(editable);
|
|
357
|
-
if (skillDomRef.value) range.setStart(editable, 1);
|
|
358
|
-
setRange(range, selection);
|
|
359
|
-
};
|
|
360
|
-
const focusSlot = (key, preventScroll) => {
|
|
361
|
-
const editable = editableRef.value;
|
|
362
|
-
if (!editable) return;
|
|
363
|
-
const resolveTarget = (slotKey) => {
|
|
364
|
-
const slotDom = slotDomMap.value.get(slotKey);
|
|
365
|
-
const slotCfg = slotConfigMap.value.get(slotKey);
|
|
366
|
-
if (!slotDom || !slotCfg) return null;
|
|
367
|
-
if (slotCfg.type === "input") return slotDom.querySelector("input");
|
|
368
|
-
if (slotCfg.type === "content") return slotDom;
|
|
369
|
-
return null;
|
|
370
|
-
};
|
|
371
|
-
if (key) {
|
|
372
|
-
const target = resolveTarget(key);
|
|
373
|
-
if (target instanceof HTMLInputElement) {
|
|
374
|
-
target.focus({ preventScroll });
|
|
375
|
-
return;
|
|
376
|
-
}
|
|
377
|
-
if (target instanceof HTMLSpanElement) {
|
|
378
|
-
setCursorPosition(target, 0, preventScroll);
|
|
379
|
-
return;
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
for (const node of Array.from(editable.childNodes)) {
|
|
383
|
-
if (!(node instanceof HTMLElement)) continue;
|
|
384
|
-
const slotKey = node.dataset.slotKey;
|
|
385
|
-
const nodeType = node.dataset.nodeType;
|
|
386
|
-
if (!slotKey || nodeType === "nbsp") continue;
|
|
387
|
-
const target = resolveTarget(slotKey);
|
|
388
|
-
if (target instanceof HTMLInputElement) {
|
|
389
|
-
target.focus({ preventScroll });
|
|
390
|
-
return;
|
|
391
|
-
}
|
|
392
|
-
if (target instanceof HTMLSpanElement) {
|
|
393
|
-
setCursorPosition(target, 0, preventScroll);
|
|
394
|
-
return;
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
setEndCursor(preventScroll);
|
|
398
|
-
};
|
|
399
|
-
const removeSlot = (key, event) => {
|
|
400
|
-
const editable = editableRef.value;
|
|
401
|
-
if (!editable) return;
|
|
402
|
-
editable.querySelectorAll(`[data-slot-key="${key}"]`).forEach((element) => {
|
|
403
|
-
unmountDom(element);
|
|
404
|
-
element.remove();
|
|
405
|
-
});
|
|
406
|
-
const nextValues = { ...slotValues.value };
|
|
407
|
-
delete nextValues[key];
|
|
408
|
-
slotValues.value = nextValues;
|
|
409
|
-
slotConfigMap.value.delete(key);
|
|
410
|
-
slotDomMap.value.delete(key);
|
|
411
|
-
slotDomMap.value.delete(`${key}_before`);
|
|
412
|
-
slotDomMap.value.delete(`${key}_after`);
|
|
413
|
-
triggerValueChange(event);
|
|
414
|
-
};
|
|
415
|
-
const removeSkill = (triggerChange = true) => {
|
|
416
|
-
const skillDom = skillDomRef.value;
|
|
417
|
-
if (!skillDom) return;
|
|
418
|
-
unmountDom(skillDom);
|
|
419
|
-
skillDom.remove();
|
|
420
|
-
skillDomRef.value = null;
|
|
421
|
-
currentSkillRef.value = void 0;
|
|
422
|
-
if (triggerChange) triggerValueChange();
|
|
423
|
-
};
|
|
424
|
-
const renderSkill = () => {
|
|
425
|
-
const editable = editableRef.value;
|
|
426
|
-
const skill = senderCtx.value.skill;
|
|
427
|
-
if (!editable) return;
|
|
428
|
-
if (!skill) {
|
|
429
|
-
removeSkill(false);
|
|
430
|
-
return;
|
|
431
|
-
}
|
|
432
|
-
let skillDom = skillDomRef.value;
|
|
433
|
-
if (!skillDom) {
|
|
434
|
-
skillDom = buildSkillSpan(skill.value);
|
|
435
|
-
editable.insertBefore(skillDom, editable.firstChild);
|
|
436
|
-
skillDomRef.value = skillDom;
|
|
437
|
-
}
|
|
438
|
-
currentSkillRef.value = skill;
|
|
439
|
-
mountedDomSet.value.add(skillDom);
|
|
440
|
-
render(createVNode(Skill_default, {
|
|
441
|
-
"prefixCls": prefixCls.value,
|
|
442
|
-
"skill": skill,
|
|
443
|
-
"removeSkill": () => removeSkill(true)
|
|
444
|
-
}, null), skillDom);
|
|
445
|
-
};
|
|
446
|
-
const updateSkillEmptyStatus = (value = getEditorValue()) => {
|
|
447
|
-
const skillDom = skillDomRef.value;
|
|
448
|
-
if (!skillDom) return;
|
|
449
|
-
if (!value.value && value.slotConfig.length === 0 && !!senderCtx.value.placeholder && !skillDom.hasChildNodes()) {
|
|
450
|
-
skillDom.setAttribute("contenteditable", "true");
|
|
451
|
-
skillDom.classList.add(`${prefixCls.value}-skill-empty`);
|
|
452
|
-
} else {
|
|
453
|
-
skillDom.setAttribute("contenteditable", "false");
|
|
454
|
-
skillDom.classList.remove(`${prefixCls.value}-skill-empty`);
|
|
455
|
-
}
|
|
456
|
-
};
|
|
457
|
-
const initFromSlotConfig = (configs) => {
|
|
458
|
-
const editable = editableRef.value;
|
|
459
|
-
if (!editable) return;
|
|
460
|
-
clearEditor();
|
|
461
|
-
const mergeConfigs = configs ?? [];
|
|
462
|
-
mergeConfigs.forEach((config) => {
|
|
463
|
-
if ("key" in config && config.key) {
|
|
464
|
-
slotConfigMap.value.set(config.key, config);
|
|
465
|
-
const defaultValue = getDefaultSlotValue(config);
|
|
466
|
-
if (defaultValue !== void 0) slotValues.value[config.key] = defaultValue;
|
|
467
|
-
}
|
|
468
|
-
});
|
|
469
|
-
buildSlotNodes(mergeConfigs).forEach((node) => {
|
|
470
|
-
editable.appendChild(node);
|
|
471
|
-
});
|
|
472
|
-
renderSkill();
|
|
473
|
-
nextTick(() => {
|
|
474
|
-
triggerValueChange();
|
|
475
|
-
});
|
|
476
|
-
};
|
|
477
|
-
const removeSpecificBRs = () => {
|
|
478
|
-
if (senderCtx.value.submitType !== "enter") return;
|
|
479
|
-
editableRef.value?.querySelectorAll("br").forEach((br) => br.remove());
|
|
480
|
-
};
|
|
481
|
-
const shouldSubmit = (event) => {
|
|
482
|
-
const { key, shiftKey, ctrlKey, altKey, metaKey } = event;
|
|
483
|
-
if (key !== "Enter") return false;
|
|
484
|
-
const isModifierPressed = ctrlKey || altKey || metaKey;
|
|
485
|
-
const submitType = senderCtx.value.submitType ?? "enter";
|
|
486
|
-
return submitType === "enter" && !shiftKey && !isModifierPressed || submitType === "shiftEnter" && shiftKey && !isModifierPressed;
|
|
487
|
-
};
|
|
488
|
-
const findOuterContainer = (node) => {
|
|
489
|
-
const editable = editableRef.value;
|
|
490
|
-
if (!node || !editable) return null;
|
|
491
|
-
let current = node;
|
|
492
|
-
if (current.nodeType === Node.TEXT_NODE) current = current.parentNode;
|
|
493
|
-
while (current && current !== editable) {
|
|
494
|
-
if (current instanceof HTMLElement && current.tagName === "SPAN") return current;
|
|
495
|
-
current = current.parentNode;
|
|
496
|
-
}
|
|
497
|
-
return null;
|
|
498
|
-
};
|
|
499
|
-
const handleDeleteOperation = (event, operationType) => {
|
|
500
|
-
const selection = getSelection();
|
|
501
|
-
const editable = editableRef.value;
|
|
502
|
-
if (!editable || !selection || selection.rangeCount === 0) return false;
|
|
503
|
-
const range = selection.getRangeAt(0);
|
|
504
|
-
const anchorNode = selection.anchorNode;
|
|
505
|
-
const focusOffset = selection.focusOffset;
|
|
506
|
-
if (!anchorNode || !editable.contains(anchorNode)) return false;
|
|
507
|
-
if (anchorNode.nodeType === Node.TEXT_NODE && range) {
|
|
508
|
-
const parentElement = anchorNode.parentNode;
|
|
509
|
-
const nodeInfo = getNodeInfo(parentElement);
|
|
510
|
-
const selectedText = range.toString();
|
|
511
|
-
const textLength = anchorNode.textContent?.length ?? 0;
|
|
512
|
-
const isFullTextSelected = textLength === selectedText.length;
|
|
513
|
-
const isSingleCharAtEnd = textLength === 1 && focusOffset === 1;
|
|
514
|
-
if (nodeInfo?.slotConfig?.type === "content" && (isFullTextSelected || isSingleCharAtEnd)) {
|
|
515
|
-
event.preventDefault();
|
|
516
|
-
parentElement.innerHTML = "";
|
|
517
|
-
parentElement.innerText = "";
|
|
518
|
-
return true;
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
const isIgnorableBoundaryNode = (node) => {
|
|
522
|
-
if (node.nodeType === Node.TEXT_NODE) return !(node.textContent ?? "");
|
|
523
|
-
if (!(node instanceof HTMLElement)) return true;
|
|
524
|
-
if (node.tagName === "BR") return true;
|
|
525
|
-
return !node.textContent;
|
|
526
|
-
};
|
|
527
|
-
const getPreviousSibling = () => {
|
|
528
|
-
if (operationType !== "backspace") return null;
|
|
529
|
-
if (anchorNode === editable) return editable.childNodes[focusOffset - 1] || null;
|
|
530
|
-
if (focusOffset !== 0) return null;
|
|
531
|
-
const outer = findOuterContainer(anchorNode);
|
|
532
|
-
return (outer && editable.contains(outer) ? outer : anchorNode.nodeType === Node.TEXT_NODE ? anchorNode : anchorNode).previousSibling;
|
|
533
|
-
};
|
|
534
|
-
const findPreviousDeletableNode = (node) => {
|
|
535
|
-
let current = node;
|
|
536
|
-
while (current) {
|
|
537
|
-
if (current instanceof HTMLElement) {
|
|
538
|
-
const nodeInfo = getNodeInfo(current);
|
|
539
|
-
if (nodeInfo?.slotKey || nodeInfo?.skillKey) return current;
|
|
540
|
-
}
|
|
541
|
-
if (!isIgnorableBoundaryNode(current)) return null;
|
|
542
|
-
current = current.previousSibling;
|
|
543
|
-
}
|
|
544
|
-
return null;
|
|
545
|
-
};
|
|
546
|
-
const target = findPreviousDeletableNode(getPreviousSibling());
|
|
547
|
-
if (!target) return false;
|
|
548
|
-
const info = getNodeInfo(target);
|
|
549
|
-
if (info?.slotKey) {
|
|
550
|
-
event.preventDefault();
|
|
551
|
-
removeSlot(info.slotKey, event);
|
|
552
|
-
return true;
|
|
553
|
-
}
|
|
554
|
-
if (info?.skillKey) {
|
|
555
|
-
event.preventDefault();
|
|
556
|
-
removeSkill(true);
|
|
557
|
-
return true;
|
|
558
|
-
}
|
|
559
|
-
return false;
|
|
560
|
-
};
|
|
561
|
-
const onInternalKeyDown = (event) => {
|
|
562
|
-
if (senderCtx.value.onKeyDown?.(event) === false || keyLock.value || isComposing.value) return;
|
|
563
|
-
if (event.key === "Backspace" && handleDeleteOperation(event, "backspace")) return;
|
|
564
|
-
if (shouldSubmit(event)) {
|
|
565
|
-
event.preventDefault();
|
|
566
|
-
keyLock.value = true;
|
|
567
|
-
senderCtx.value.triggerSend?.();
|
|
568
|
-
return;
|
|
569
|
-
}
|
|
570
|
-
if ((event.key === "a" || event.key === "A") && (event.ctrlKey || event.metaKey)) {
|
|
571
|
-
event.preventDefault();
|
|
572
|
-
selectAll(true);
|
|
573
|
-
}
|
|
574
|
-
};
|
|
575
|
-
const onInternalKeyUp = (event) => {
|
|
576
|
-
if (event.key === "Enter") keyLock.value = false;
|
|
577
|
-
senderCtx.value.onKeyUp?.(event);
|
|
578
|
-
};
|
|
579
|
-
const normalizeSkillTextInput = () => {
|
|
580
|
-
const editable = editableRef.value;
|
|
581
|
-
const skillDom = skillDomRef.value;
|
|
582
|
-
if (!editable || !skillDom) return;
|
|
583
|
-
const selection = getSelection();
|
|
584
|
-
const cursorNode = selection?.anchorNode && skillDom.contains(selection.anchorNode) ? selection.anchorNode : null;
|
|
585
|
-
const cursorOffset = selection?.anchorOffset ?? 0;
|
|
586
|
-
const wrapperCls = `${prefixCls.value}-skill-wrapper`;
|
|
587
|
-
const textNodes = Array.from(skillDom.childNodes).filter((node) => {
|
|
588
|
-
if (node instanceof HTMLElement && node.classList.contains(wrapperCls)) return false;
|
|
589
|
-
return node.textContent;
|
|
590
|
-
});
|
|
591
|
-
const referenceNode = skillDom.nextSibling;
|
|
592
|
-
textNodes.forEach((node) => {
|
|
593
|
-
editable.insertBefore(node, referenceNode);
|
|
594
|
-
});
|
|
595
|
-
if (cursorNode && editable.contains(cursorNode)) {
|
|
596
|
-
const range = document.createRange();
|
|
597
|
-
const nextOffset = cursorNode.nodeType === Node.TEXT_NODE ? Math.min(cursorOffset, cursorNode.textContent?.length ?? 0) : Math.min(cursorOffset, cursorNode.childNodes.length);
|
|
598
|
-
range.setStart(cursorNode, nextOffset);
|
|
599
|
-
range.collapse(true);
|
|
600
|
-
selection?.removeAllRanges();
|
|
601
|
-
selection?.addRange(range);
|
|
602
|
-
}
|
|
603
|
-
};
|
|
604
|
-
const onInternalInput = (event) => {
|
|
605
|
-
removeSpecificBRs();
|
|
606
|
-
normalizeSkillTextInput();
|
|
607
|
-
triggerValueChange(event);
|
|
608
|
-
};
|
|
609
|
-
const onInternalPaste = (event) => {
|
|
610
|
-
event.preventDefault();
|
|
611
|
-
const files = event.clipboardData?.files;
|
|
612
|
-
const text = event.clipboardData?.getData("text/plain") ?? "";
|
|
613
|
-
if (!text && files?.length && senderCtx.value.onPasteFile) {
|
|
614
|
-
senderCtx.value.onPasteFile(files);
|
|
615
|
-
return;
|
|
616
|
-
}
|
|
617
|
-
if (text) {
|
|
618
|
-
const selection = getSelection();
|
|
619
|
-
if (selection && selection.rangeCount > 0) {
|
|
620
|
-
const range = selection.getRangeAt(0);
|
|
621
|
-
range.deleteContents();
|
|
622
|
-
const textNode = document.createTextNode(text.replace(/\u200B/g, ""));
|
|
623
|
-
range.insertNode(textNode);
|
|
624
|
-
range.setStartAfter(textNode);
|
|
625
|
-
range.collapse(true);
|
|
626
|
-
selection.removeAllRanges();
|
|
627
|
-
selection.addRange(range);
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
onInternalInput(event);
|
|
631
|
-
senderCtx.value.onPaste?.(event);
|
|
632
|
-
};
|
|
633
|
-
const onInternalSelect = () => {
|
|
634
|
-
const selection = getSelection();
|
|
635
|
-
const editable = editableRef.value;
|
|
636
|
-
if (!selection || !editable) return;
|
|
637
|
-
if (selection.focusNode === editable && selection.focusOffset === 0 && getEditorValue().skill) setCursorPosition(editable, 1);
|
|
638
|
-
};
|
|
639
|
-
const getRangeForInsert = (position = "cursor") => {
|
|
640
|
-
const editable = editableRef.value;
|
|
641
|
-
if (!editable) return null;
|
|
642
|
-
const selection = getSelection();
|
|
643
|
-
const range = document.createRange();
|
|
644
|
-
if (position === "start") {
|
|
645
|
-
const index = skillDomRef.value ? 1 : 0;
|
|
646
|
-
range.setStart(editable, index);
|
|
647
|
-
range.collapse(true);
|
|
648
|
-
return {
|
|
649
|
-
range,
|
|
650
|
-
selection
|
|
651
|
-
};
|
|
652
|
-
}
|
|
653
|
-
if (position === "end") {
|
|
654
|
-
range.selectNodeContents(editable);
|
|
655
|
-
range.collapse(false);
|
|
656
|
-
return {
|
|
657
|
-
range,
|
|
658
|
-
selection
|
|
659
|
-
};
|
|
660
|
-
}
|
|
661
|
-
if (selection && selection.rangeCount > 0 && editable.contains(selection.getRangeAt(0).startContainer)) {
|
|
662
|
-
const currentRange = selection.getRangeAt(0).cloneRange();
|
|
663
|
-
const outer = findOuterContainer(currentRange.startContainer);
|
|
664
|
-
const outerInfo = outer ? getNodeInfo(outer) : null;
|
|
665
|
-
if (outer && outerInfo?.slotKey && outerInfo.slotConfig?.type !== "content") {
|
|
666
|
-
currentRange.setStartAfter(outer);
|
|
667
|
-
currentRange.collapse(true);
|
|
668
|
-
}
|
|
669
|
-
return {
|
|
670
|
-
range: currentRange,
|
|
671
|
-
selection
|
|
672
|
-
};
|
|
673
|
-
}
|
|
674
|
-
range.selectNodeContents(editable);
|
|
675
|
-
range.collapse(false);
|
|
676
|
-
return {
|
|
677
|
-
range,
|
|
678
|
-
selection
|
|
679
|
-
};
|
|
680
|
-
};
|
|
681
|
-
const insert = (slotConfig, position = "cursor", replaceCharacters, preventScroll) => {
|
|
682
|
-
const editable = editableRef.value;
|
|
683
|
-
if (!editable || !slotConfig.length) return;
|
|
684
|
-
mergeSlotConfig(slotConfig);
|
|
685
|
-
const nodes = buildSlotNodes(slotConfig);
|
|
686
|
-
if (!nodes.length) return;
|
|
687
|
-
const insertContext = getRangeForInsert(position);
|
|
688
|
-
if (!insertContext?.range) return;
|
|
689
|
-
const { range, selection } = insertContext;
|
|
690
|
-
editable.focus({ preventScroll });
|
|
691
|
-
if (replaceCharacters) {
|
|
692
|
-
const { startContainer, startOffset } = range;
|
|
693
|
-
if ((startContainer.nodeType === Node.TEXT_NODE ? startContainer.textContent?.slice(0, startOffset) ?? "" : "").endsWith(replaceCharacters)) range.setStart(startContainer, Math.max(0, startOffset - replaceCharacters.length));
|
|
694
|
-
}
|
|
695
|
-
range.deleteContents();
|
|
696
|
-
nodes.forEach((node) => {
|
|
697
|
-
range.insertNode(node);
|
|
698
|
-
range.setStartAfter(node);
|
|
699
|
-
});
|
|
700
|
-
range.collapse(true);
|
|
701
|
-
if (selection) {
|
|
702
|
-
selection.removeAllRanges();
|
|
703
|
-
selection.addRange(range);
|
|
704
|
-
}
|
|
705
|
-
onInternalInput();
|
|
706
|
-
};
|
|
707
|
-
const focus = (options) => {
|
|
708
|
-
const cursor = options?.cursor ?? "end";
|
|
709
|
-
if (cursor === "start") {
|
|
710
|
-
setStartCursor(options?.preventScroll);
|
|
711
|
-
return;
|
|
712
|
-
}
|
|
713
|
-
if (cursor === "all") {
|
|
714
|
-
selectAll(options?.preventScroll);
|
|
715
|
-
return;
|
|
716
|
-
}
|
|
717
|
-
if (cursor === "slot") {
|
|
718
|
-
focusSlot(options?.key, options?.preventScroll);
|
|
719
|
-
return;
|
|
720
|
-
}
|
|
721
|
-
setEndCursor(options?.preventScroll);
|
|
722
|
-
};
|
|
723
|
-
const clear = () => {
|
|
724
|
-
clearEditor();
|
|
725
|
-
renderSkill();
|
|
726
|
-
onInternalInput();
|
|
727
|
-
};
|
|
728
|
-
expose({
|
|
729
|
-
get nativeElement() {
|
|
730
|
-
return editableRef.value ?? null;
|
|
731
|
-
},
|
|
732
|
-
focus,
|
|
733
|
-
blur() {
|
|
734
|
-
editableRef.value?.blur();
|
|
735
|
-
},
|
|
736
|
-
insert,
|
|
737
|
-
clear,
|
|
738
|
-
getValue: getEditorValue
|
|
739
|
-
});
|
|
740
|
-
const applySlotConfig = (configs, force = false) => {
|
|
741
|
-
if (!force && configs === lastSlotConfigRef.value) return;
|
|
742
|
-
lastSlotConfigRef.value = configs;
|
|
743
|
-
initFromSlotConfig(configs);
|
|
744
|
-
};
|
|
745
|
-
watch(() => editableRef.value, (editable) => {
|
|
746
|
-
if (!editable) return;
|
|
747
|
-
applySlotConfig(senderCtx.value.slotConfig, true);
|
|
748
|
-
}, { immediate: true });
|
|
749
|
-
watch(() => senderCtx.value.slotConfig, (configs) => {
|
|
750
|
-
applySlotConfig(configs);
|
|
751
|
-
}, { immediate: true });
|
|
752
|
-
watch(() => senderCtx.value.skill, (skill) => {
|
|
753
|
-
if (skill === lastSkillRef.value && skillDomRef.value) return;
|
|
754
|
-
lastSkillRef.value = skill;
|
|
755
|
-
renderSkill();
|
|
756
|
-
nextTick(() => {
|
|
757
|
-
triggerValueChange();
|
|
758
|
-
});
|
|
759
|
-
}, { immediate: true });
|
|
760
|
-
watch(() => senderCtx.value.readOnly, (readOnly) => {
|
|
761
|
-
slotConfigMap.value.forEach((config, key) => {
|
|
762
|
-
if (config.type === "content") slotDomMap.value.get(key)?.setAttribute("contenteditable", readOnly ? "false" : "true");
|
|
763
|
-
});
|
|
764
|
-
});
|
|
765
|
-
onBeforeUnmount(() => {
|
|
766
|
-
unmountAllPortals();
|
|
767
|
-
});
|
|
768
|
-
return () => {
|
|
769
|
-
const inputCls = `${prefixCls.value}-input`;
|
|
770
|
-
return createVNode("div", {
|
|
771
|
-
"ref": editableRef,
|
|
772
|
-
"role": "textbox",
|
|
773
|
-
"tabindex": 0,
|
|
774
|
-
"class": classNames([
|
|
775
|
-
inputCls,
|
|
776
|
-
`${inputCls}-slot`,
|
|
777
|
-
senderCtx.value.classNames?.input
|
|
778
|
-
]),
|
|
779
|
-
"style": mergeInputStyle.value,
|
|
780
|
-
"data-placeholder": senderCtx.value.placeholder,
|
|
781
|
-
"contenteditable": !senderCtx.value.readOnly,
|
|
782
|
-
"spellcheck": false,
|
|
783
|
-
"onInput": onInternalInput,
|
|
784
|
-
"onKeydown": onInternalKeyDown,
|
|
785
|
-
"onKeyup": onInternalKeyUp,
|
|
786
|
-
"onPaste": onInternalPaste,
|
|
787
|
-
"onCut": onInternalInput,
|
|
788
|
-
"onSelect": onInternalSelect,
|
|
789
|
-
"onFocus": (e) => {
|
|
790
|
-
senderCtx.value.onFocus?.(e);
|
|
791
|
-
},
|
|
792
|
-
"onBlur": (e) => {
|
|
793
|
-
keyLock.value = false;
|
|
794
|
-
senderCtx.value.onBlur?.(e);
|
|
795
|
-
},
|
|
796
|
-
"onCompositionstart": () => {
|
|
797
|
-
isComposing.value = true;
|
|
798
|
-
},
|
|
799
|
-
"onCompositionend": () => {
|
|
800
|
-
isComposing.value = false;
|
|
801
|
-
keyLock.value = false;
|
|
802
|
-
}
|
|
803
|
-
}, null);
|
|
804
|
-
};
|
|
805
|
-
}
|
|
806
|
-
});
|
|
807
|
-
//#endregion
|
|
808
|
-
export { SlotTextArea_default as default };
|