eco-vue-js 0.11.19 → 0.11.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/components/Dropdown/utils/DropdownStyle.d.ts.map +1 -1
- package/dist/components/Dropdown/utils/DropdownStyle.js +5 -4
- package/dist/components/Input/WInput.vue.d.ts.map +1 -1
- package/dist/components/Input/WInput.vue.js +4 -4
- package/dist/components/Input/components/ContentEditable.vue.js +2 -261
- package/dist/components/Input/components/ContentEditable.vue2.js +261 -2
- package/package.json +1 -1
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"DropdownStyle.d.ts","sourceRoot":"","sources":["../../../../../src/components/Dropdown/utils/DropdownStyle.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,eAAe,EAAC,MAAM,yBAAyB,CAAA;
|
1
|
+
{"version":3,"file":"DropdownStyle.d.ts","sourceRoot":"","sources":["../../../../../src/components/Dropdown/utils/DropdownStyle.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,eAAe,EAAC,MAAM,yBAAyB,CAAA;AAGvD,KAAK,WAAW,GAAG;IACjB,MAAM,EAAE,CAAC,CAAC,UAAU,EAAE,OAAO,KAAK,OAAO,CAAC,GAAG,SAAS,CAAA;CACvD,CAAA;AAID,oBAAY,OAAO;IACjB,GAAG,kBAAkB;IACrB,MAAM,gBAAgB;IACtB,MAAM,mBAAmB;CAC1B;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,OAAO,CAAA;IACd,MAAM,EAAE,OAAO,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC9B,CAAC,EAAE,CAAC,UAAU,EAAE,OAAO,KAAK,MAAM,CAAA;CACnC,GAAG,WAAW,CAAA;AA6Df,oBAAY,OAAO;IACjB,IAAI,kBAAkB;IACtB,KAAK,gBAAgB;IACrB,MAAM,mBAAmB;CAC1B;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,mBAAmB,EAAE,CAAC,cAAc,EAAE,cAAc,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;IACxE,MAAM,EAAE,OAAO,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAA;IAC1C,WAAW,CAAC,EAAE,CAAC,UAAU,EAAE,OAAO,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7D,CAAC,EAAE,CAAC,UAAU,EAAE,OAAO,KAAK,MAAM,CAAA;CACnC,GAAG,WAAW,CAAA;AAIf;;GAEG;AACH,eAAO,MAAM,UAAU,EAAE,gBAMvB,CAAA;AAEF;;GAEG;AACH,eAAO,MAAM,UAAU,EAAE,gBAKvB,CAAA;AAkCF;;GAEG;AACH,eAAO,MAAM,SAAS,EAAE,gBAMtB,CAAA;AAEF;;GAEG;AACH,eAAO,MAAM,SAAS,EAAE,gBAKtB,CAAA;AAEF;;GAEG;AACH,eAAO,MAAM,UAAU,EAAE,gBAGvB,CAAA;AAEF,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAAC,eAAe,EAAE,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,GAAG,CAAC,gBAAgB,CAAC,CASvH,CAAA;AAED,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,gBAAgB,GAAG,cAAc,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,OAAO,GAAG,CAAC,CAItH"}
|
@@ -1,8 +1,9 @@
|
|
1
1
|
import { markRaw } from 'vue';
|
2
2
|
import { HorizontalAlign } from '../../../utils/HorizontalAlign.js';
|
3
|
+
import { isClientSide } from '../../../utils/utils.js';
|
3
4
|
|
4
5
|
const EDGE_FACTOR = 0.66;
|
5
|
-
const BOTTOM_EDGE = window.innerHeight * EDGE_FACTOR;
|
6
|
+
const BOTTOM_EDGE = isClientSide ? window.innerHeight * EDGE_FACTOR : 0;
|
6
7
|
const BottomInner = markRaw({
|
7
8
|
isTop: false,
|
8
9
|
origin: "content-start" /* TOP */,
|
@@ -22,7 +23,7 @@ const VerticalCenter = markRaw({
|
|
22
23
|
y: (parentRect) => Math.round(parentRect.top + parentRect.height / 2),
|
23
24
|
isEdge: void 0
|
24
25
|
});
|
25
|
-
const TOP_EDGE = window.innerHeight * (1 - EDGE_FACTOR);
|
26
|
+
const TOP_EDGE = isClientSide ? window.innerHeight * (1 - EDGE_FACTOR) : 0;
|
26
27
|
const TopOuter = markRaw({
|
27
28
|
isTop: true,
|
28
29
|
origin: "content-end" /* BOTTOM */,
|
@@ -41,7 +42,7 @@ var OriginX = /* @__PURE__ */ ((OriginX2) => {
|
|
41
42
|
OriginX2["CENTER"] = "justify-center";
|
42
43
|
return OriginX2;
|
43
44
|
})(OriginX || {});
|
44
|
-
const RIGHT_EDGE = window.innerWidth * EDGE_FACTOR;
|
45
|
+
const RIGHT_EDGE = isClientSide ? window.innerWidth * EDGE_FACTOR : 0;
|
45
46
|
const RightOuter = markRaw({
|
46
47
|
verticalGetterOrder: [BottomInner, TopInner],
|
47
48
|
origin: "justify-start" /* LEFT */,
|
@@ -73,7 +74,7 @@ const Center = markRaw({
|
|
73
74
|
x: (parentRect) => Math.round(parentRect.left + parentRect.width / 2),
|
74
75
|
isEdge: void 0
|
75
76
|
});
|
76
|
-
const LEFT_EDGE = window.innerWidth * (1 - EDGE_FACTOR);
|
77
|
+
const LEFT_EDGE = isClientSide ? window.innerWidth * (1 - EDGE_FACTOR) : 0;
|
77
78
|
const LeftInner = markRaw({
|
78
79
|
verticalGetterOrder: [BottomOuter, TopOuter],
|
79
80
|
origin: "justify-end" /* RIGHT */,
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"WInput.vue.d.ts","sourceRoot":"","sources":["../../../../src/components/Input/WInput.vue"],"names":[],"mappings":"AAoPA;AAqiBA,OAAO,KAAK,EAAC,UAAU,EAAE,aAAa,EAAC,MAAM,SAAS,CAAA;
|
1
|
+
{"version":3,"file":"WInput.vue.d.ts","sourceRoot":"","sources":["../../../../src/components/Input/WInput.vue"],"names":[],"mappings":"AAoPA;AAqiBA,OAAO,KAAK,EAAC,UAAU,EAAE,aAAa,EAAC,MAAM,SAAS,CAAA;AAatD,OAAO,EAAC,KAAK,WAAW,EAAC,MAAM,gBAAgB,CAAA;yBAE9B,IAAI,SAAS,SAAS,GAAG,MAAM,EAC/C,aAAa,WAAW,CAAC,OAAO,CAAC,OAAO,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,EAC9D,YAAY,mBAAmB,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,OAAO,WAAW,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC,EAC3G,eAAe,WAAW,CAAC,OAAO,CAAC,OAAO,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,EACjE;WAoyBO,mBAAmB,CAAC,oCAAkE,CAAC,4BAA2B;oBACzG,OAAO,KAAK,EAAE,gBAAgB;qBAzmB7B,IAAI;oBAOL,IAAI;+BAkCS,aAAa;0BA1KlB,MAAM,QAAQ,MAAM,KAAG,IAAI;wBAN/B,WAAW;;;oBAyBf,IAAI;oBAgBJ,IAAI;MAusBgD,GAAG,IAAI;WACpE,GAAG;;uBAhEgB,GAAG;0BACA,GAAG;wBACJ,GAAG;;;YAEH,GAAG;;mCArgBF,aAAa;YAogBb,GAAG;;;YAEJ,GAAG;uBACJ,GAAG;wBACF,GAAG;uBACJ,GAAG;uBACH,GAAG;wBACF,GAAG;;;YAttB1B,oBAAoB,SAAS,4CAAa,SAAS,GAAG,IAAI;YAC1D,gBAAgB,SAAS,aAAa,GAAG,IAAI;YAC7C,aAAa,SAAS,aAAa,GAAG,IAAI;YAC1C,eAAe,SAAS,aAAa,GAAG,IAAI;YAC5C,iBAAiB,SAAS,aAAa,GAAG,IAAI;YAC9C,oBAAoB,SAAS,aAAa,GAAG,IAAI;YACjD,aAAa,GAAG,IAAI;YACpB,OAAO,SAAS,UAAU,GAAG,SAAS,GAAG,IAAI;YAC7C,MAAM,SAAS,UAAU,GAAG,IAAI;YAChC,OAAO,SAAS,UAAU,GAAG,IAAI;YACjC,WAAW,SAAS,UAAU,GAAG,IAAI;YACrC,cAAc,SAAS,UAAU,GAAG,IAAI;YACxC,cAAc,SAAS,KAAK,GAAG,IAAI;YACnC,OAAO,GAAG,IAAI;;EAmwBhB,KACQ,OAAO,KAAK,EAAE,KAAK,GAAG;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC,OAAO,WAAW,CAAC,CAAA;CAAE;AA/yBzE,wBA+yB4E;AAC5E,KAAK,mBAAmB,CAAC,CAAC,IAAI;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAAG,GAAG,EAAE,CAAC"}
|
@@ -5,8 +5,7 @@ import { Notify } from '../../utils/Notify.js';
|
|
5
5
|
import { useComponentStates } from '../../utils/useComponentStates.js';
|
6
6
|
import { checkPermissionPaste } from '../../utils/useCopy.js';
|
7
7
|
import { debounce } from '../../utils/utils.js';
|
8
|
-
import _sfc_main$2 from './components/
|
9
|
-
import _sfc_main$3 from './components/InputActions.vue.js';
|
8
|
+
import _sfc_main$2 from './components/InputActions.vue.js';
|
10
9
|
|
11
10
|
const _hoisted_1 = { class: "relative flex min-h-full flex-1" };
|
12
11
|
const _sfc_main = /* @__PURE__ */ defineComponent({
|
@@ -67,6 +66,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
67
66
|
},
|
68
67
|
emits: ["update:model-value", "keypress:enter", "keypress:up", "keypress:down", "keypress:delete", "keypress:backspace", "click:clear", "focus", "blur", "click", "mousedown", "click:suffix", "select:input", "paste"],
|
69
68
|
setup(__props, { expose: __expose, emit: __emit }) {
|
69
|
+
const ContentEditable = defineAsyncComponent(() => import('./components/ContentEditable.vue.js'));
|
70
70
|
const InputToolbar = defineAsyncComponent(() => import('./components/InputToolbar.vue.js'));
|
71
71
|
const props = __props;
|
72
72
|
const emit = __emit;
|
@@ -378,7 +378,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
378
378
|
}, [
|
379
379
|
createElementVNode("div", _hoisted_1, [
|
380
380
|
renderSlot(_ctx.$slots, "before", normalizeProps(guardReactiveProps({ modelValue: _ctx.modelValue }))),
|
381
|
-
(openBlock(), createBlock(resolveDynamicComponent(_ctx.textarea ?
|
381
|
+
(openBlock(), createBlock(resolveDynamicComponent(_ctx.textarea ? unref(ContentEditable) : "input"), {
|
382
382
|
id,
|
383
383
|
ref: "input",
|
384
384
|
class: normalizeClass(["w-input min-h-full flex-1 basis-auto appearance-none border-none bg-[inherit] outline-0 placeholder:text-gray-400 disabled:cursor-not-allowed dark:placeholder:text-gray-500", {
|
@@ -432,7 +432,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
432
432
|
], 2)
|
433
433
|
], 2)
|
434
434
|
], 2),
|
435
|
-
!_ctx.seamless || focused ? (openBlock(), createBlock(_sfc_main$
|
435
|
+
!_ctx.seamless || focused ? (openBlock(), createBlock(_sfc_main$2, {
|
436
436
|
key: 2,
|
437
437
|
"model-value": _ctx.modelValue,
|
438
438
|
loading: _ctx.loading,
|
@@ -1,264 +1,5 @@
|
|
1
|
-
import
|
2
|
-
|
3
|
-
import { preserveIndentation } from '../models/toolbarActions.js';
|
4
|
-
import { getCaretOffset, setCaretOffset } from '../models/utils.js';
|
1
|
+
import _sfc_main from './ContentEditable.vue2.js';
|
2
|
+
|
5
3
|
|
6
|
-
const _hoisted_1 = ["contenteditable", "placeholder"];
|
7
|
-
const _sfc_main = /* @__PURE__ */ defineComponent({
|
8
|
-
__name: "ContentEditable",
|
9
|
-
props: {
|
10
|
-
value: {},
|
11
|
-
placeholder: {},
|
12
|
-
maxLength: {},
|
13
|
-
textParts: {},
|
14
|
-
readonly: { type: Boolean },
|
15
|
-
disabled: { type: Boolean }
|
16
|
-
},
|
17
|
-
emits: ["update:model-value", "focus", "blur", "keydown"],
|
18
|
-
setup(__props, { expose: __expose, emit: __emit }) {
|
19
|
-
const props = __props;
|
20
|
-
const emit = __emit;
|
21
|
-
const elementRef = useTemplateRef("element");
|
22
|
-
const focused = ref(false);
|
23
|
-
const updateTextParts = () => {
|
24
|
-
if (!elementRef.value || !props.textParts) return;
|
25
|
-
const offsets = getCaret();
|
26
|
-
let nodeIndex = 0;
|
27
|
-
for (const item of props.textParts) {
|
28
|
-
const existingNode = elementRef.value.childNodes[nodeIndex] ?? null;
|
29
|
-
if (typeof item === "string") {
|
30
|
-
const displayText = item.replace(/\n$/g, "\n ");
|
31
|
-
if (existingNode instanceof Text) {
|
32
|
-
if (existingNode.textContent !== displayText) existingNode.textContent = displayText;
|
33
|
-
} else {
|
34
|
-
const textNode = document.createTextNode(displayText);
|
35
|
-
elementRef.value.insertBefore(textNode, existingNode);
|
36
|
-
}
|
37
|
-
} else {
|
38
|
-
if (existingNode instanceof HTMLElement && existingNode.tagName.toLowerCase() === item.tag.toLowerCase()) {
|
39
|
-
if (existingNode.textContent !== item.value) existingNode.textContent = item.value;
|
40
|
-
if (existingNode.className !== (item.class || "")) existingNode.className = item.class || "";
|
41
|
-
if (item.edit === false && existingNode.getAttribute("contenteditable") !== "false") {
|
42
|
-
existingNode.setAttribute("contenteditable", "false");
|
43
|
-
}
|
44
|
-
} else {
|
45
|
-
const element = document.createElement(item.tag);
|
46
|
-
element.textContent = item.value;
|
47
|
-
element.setAttribute("contenteditable", item.edit ? "plaintext-only" : "false");
|
48
|
-
if (item.class) element.className = item.class;
|
49
|
-
elementRef.value.insertBefore(element, existingNode);
|
50
|
-
}
|
51
|
-
}
|
52
|
-
nodeIndex++;
|
53
|
-
}
|
54
|
-
while (elementRef.value.childNodes.length > props.textParts.length) {
|
55
|
-
const nodeToRemove = elementRef.value.childNodes[props.textParts.length];
|
56
|
-
elementRef.value.removeChild(nodeToRemove);
|
57
|
-
}
|
58
|
-
if (focused.value && !isSetCaretNext) setCaret(offsets.start, offsets.end !== offsets.start ? void 0 : offsets.end);
|
59
|
-
};
|
60
|
-
watch(() => props.textParts, updateTextParts, { immediate: true });
|
61
|
-
const updateTextValue = (value) => {
|
62
|
-
if (props.textParts || !elementRef.value) return;
|
63
|
-
if (elementRef.value.textContent !== value) {
|
64
|
-
const offsets = getCaret();
|
65
|
-
elementRef.value.textContent = value;
|
66
|
-
if (focused.value) setCaret(offsets.start, offsets.end !== offsets.start ? void 0 : offsets.end);
|
67
|
-
}
|
68
|
-
};
|
69
|
-
watch(() => props.value, updateTextValue, { immediate: true });
|
70
|
-
const textPartsToText = (parts) => {
|
71
|
-
return parts.map((part) => typeof part === "string" ? part : part.value).join("");
|
72
|
-
};
|
73
|
-
const getCurrentText = () => {
|
74
|
-
return props.textParts ? textPartsToText(props.textParts) : props.value;
|
75
|
-
};
|
76
|
-
const lineBreakEvents = ["insertParagraph", "insertLineBreak"];
|
77
|
-
const insertParagraph = (e) => {
|
78
|
-
if (lineBreakEvents.includes(e.inputType)) {
|
79
|
-
e.preventDefault();
|
80
|
-
insertPlain("\n");
|
81
|
-
}
|
82
|
-
};
|
83
|
-
const regexDifferentEnding = /\r\n?/g;
|
84
|
-
const regexSpaces = /\n \n/g;
|
85
|
-
const regexEnding = / +$/gm;
|
86
|
-
const normalizeText = (text) => {
|
87
|
-
return text.replace(regexDifferentEnding, "\n").replace(regexSpaces, "\n\n").replace(regexEnding, "");
|
88
|
-
};
|
89
|
-
const onInput = (e) => {
|
90
|
-
e.stopImmediatePropagation();
|
91
|
-
if (!(e.target instanceof HTMLDivElement)) return;
|
92
|
-
const rawText = e.target.textContent ?? "";
|
93
|
-
const text = normalizeText(rawText);
|
94
|
-
const currentText = getCurrentText();
|
95
|
-
if (text === currentText) return;
|
96
|
-
if (props.maxLength && typeof text === "string" && text.length > props.maxLength) {
|
97
|
-
e.preventDefault();
|
98
|
-
const substring = text.substring(0, props.maxLength);
|
99
|
-
if (!props.textParts) updateTextValue(substring);
|
100
|
-
else updateTextParts();
|
101
|
-
emit("update:model-value", substring);
|
102
|
-
} else {
|
103
|
-
emit("update:model-value", text);
|
104
|
-
}
|
105
|
-
};
|
106
|
-
const onPaste = async (e) => {
|
107
|
-
e.preventDefault();
|
108
|
-
navigator.clipboard.readText();
|
109
|
-
const text = (e.clipboardData?.getData("text/plain") || await navigator.clipboard.readText()).replace(/\r\n?/g, "\n");
|
110
|
-
insertPlain(text);
|
111
|
-
};
|
112
|
-
const insertPlain = (text) => {
|
113
|
-
const root = elementRef.value;
|
114
|
-
if (!root) return;
|
115
|
-
const { start, end, trail } = getCaret();
|
116
|
-
const currentText = getCurrentText();
|
117
|
-
const next = (currentText ?? "").slice(0, start) + " ".repeat(trail) + text + ((currentText ?? "").slice(end) || " ");
|
118
|
-
const caretAfter = start + text.length + trail;
|
119
|
-
emit("update:model-value", props.maxLength && next.length > props.maxLength ? next.substring(0, props.maxLength) : next);
|
120
|
-
nextTick(() => setCaret(props.maxLength ? Math.min(caretAfter, props.maxLength) : caretAfter));
|
121
|
-
};
|
122
|
-
const getCaret = () => getCaretOffset(elementRef.value);
|
123
|
-
let isSetCaretNext = false;
|
124
|
-
const setCaret = (indexStart, indexEnd) => {
|
125
|
-
isSetCaretNext = false;
|
126
|
-
setCaretOffset(elementRef.value, indexStart, indexEnd);
|
127
|
-
};
|
128
|
-
const collapseList = [" ", "\n"];
|
129
|
-
let offsetsOld = null;
|
130
|
-
const wrapSelection = (value) => {
|
131
|
-
if (focused.value || !offsetsOld) offsetsOld = getCaret();
|
132
|
-
const offsets = offsetsOld;
|
133
|
-
const currentText = getCurrentText() ?? "";
|
134
|
-
let newText = "";
|
135
|
-
let newCursorStart = offsets.start;
|
136
|
-
let newCursorEnd = void 0;
|
137
|
-
switch (value.type) {
|
138
|
-
case WrapSelectionType.TOGGLE:
|
139
|
-
let startLen = value.start.length;
|
140
|
-
const endLen = value.end.length;
|
141
|
-
const textWithContext = currentText.slice(
|
142
|
-
Math.max(0, offsets.start - startLen),
|
143
|
-
Math.min(currentText.length, offsets.end + endLen)
|
144
|
-
);
|
145
|
-
if ((!value.start || textWithContext.startsWith(value.start)) && (!value.end || textWithContext.endsWith(value.end))) {
|
146
|
-
let expandedStart = Math.max(0, offsets.start - startLen);
|
147
|
-
let start = currentText.slice(0, expandedStart);
|
148
|
-
const middle = currentText.slice(offsets.start, offsets.end);
|
149
|
-
let end = currentText.slice(Math.min(currentText.length, offsets.end + endLen));
|
150
|
-
for (const item of collapseList) {
|
151
|
-
if (value.start.startsWith(item) && !start.endsWith(item) && !middle.startsWith(item)) {
|
152
|
-
start += item;
|
153
|
-
expandedStart += item.length;
|
154
|
-
}
|
155
|
-
if (value.end.endsWith(item) && !end.startsWith(item) && !middle.endsWith(item)) end = item + end;
|
156
|
-
}
|
157
|
-
newText = start + middle + end;
|
158
|
-
if (value.prepare) newText = value.prepare(newText, 0);
|
159
|
-
newCursorStart = expandedStart;
|
160
|
-
newCursorEnd = expandedStart + offsets.end - offsets.start;
|
161
|
-
} else {
|
162
|
-
if (!value.start || !value.end) {
|
163
|
-
const offset = value.start ? offsets.start : offsets.end;
|
164
|
-
let start = currentText.slice(0, offset);
|
165
|
-
let end = currentText.slice(offset);
|
166
|
-
for (const item of collapseList) {
|
167
|
-
if (value.start.startsWith(item) && start.endsWith(item)) {
|
168
|
-
start = start.slice(0, offsets.start - item.length);
|
169
|
-
startLen -= item.length;
|
170
|
-
}
|
171
|
-
if (value.end.endsWith(item) && end.startsWith(item)) end = end.slice(item.length);
|
172
|
-
}
|
173
|
-
newText = (value.prepare?.(start, 0) ?? start) + (value.start || value.end) + (value.prepare?.(end, offset) ?? end);
|
174
|
-
} else {
|
175
|
-
let start = currentText.slice(0, offsets.start);
|
176
|
-
const middle = currentText.slice(offsets.start, offsets.end);
|
177
|
-
let end = currentText.slice(offsets.end);
|
178
|
-
for (const item of collapseList) {
|
179
|
-
if (value.start.startsWith(item) && start.endsWith(item)) {
|
180
|
-
start = start.slice(0, offsets.start - item.length);
|
181
|
-
startLen -= item.length;
|
182
|
-
}
|
183
|
-
if (value.end.endsWith(item) && end.startsWith(item)) end = end.slice(item.length);
|
184
|
-
}
|
185
|
-
newText = (value.prepare?.(start, 0) ?? start) + value.start + (value.prepare?.(middle, offsets.start) ?? middle) + value.end + (value.prepare?.(end, offsets.end) ?? end);
|
186
|
-
}
|
187
|
-
newCursorStart = offsets.start + startLen;
|
188
|
-
if (offsets.start !== offsets.end) newCursorEnd = newCursorStart + offsets.end - offsets.start;
|
189
|
-
}
|
190
|
-
break;
|
191
|
-
case WrapSelectionType.LINE_PREFIX:
|
192
|
-
const lineStart = currentText.lastIndexOf("\n", offsets.start - 1) + 1;
|
193
|
-
const lineEnd = currentText.indexOf("\n", offsets.end);
|
194
|
-
const actualLineEnd = lineEnd === -1 ? currentText.length : lineEnd;
|
195
|
-
const linesText = currentText.slice(lineStart, actualLineEnd);
|
196
|
-
const beforeLines = currentText.slice(0, lineStart);
|
197
|
-
const afterLines = currentText.slice(actualLineEnd);
|
198
|
-
const lines = linesText.split("\n");
|
199
|
-
if (lines.length === 0) lines.push("");
|
200
|
-
const allLinesHavePrefix = value.detectPattern ? lines.every((line) => !line.trim() || value.detectPattern.test(line)) : value.linePrefix ? lines.every((line) => !line.trim() || line.startsWith(value.linePrefix)) : false;
|
201
|
-
if (allLinesHavePrefix) {
|
202
|
-
const cleanText = lines.map((line) => preserveIndentation(line, "")).join("\n");
|
203
|
-
newText = beforeLines + cleanText + afterLines;
|
204
|
-
newCursorStart = lineStart;
|
205
|
-
newCursorEnd = lineStart + cleanText.length;
|
206
|
-
} else {
|
207
|
-
const processedText = value.lineTransformAll ? value.lineTransformAll(lines) : value.lineTransform ? lines.map(value.lineTransform).join("\n") : lines.map((line) => line.trim() ? preserveIndentation(line, value.linePrefix) : line).join("\n");
|
208
|
-
newText = beforeLines + processedText + afterLines;
|
209
|
-
newCursorStart = lineStart;
|
210
|
-
newCursorEnd = lineStart + processedText.length;
|
211
|
-
}
|
212
|
-
break;
|
213
|
-
}
|
214
|
-
isSetCaretNext = true;
|
215
|
-
emit("update:model-value", newText);
|
216
|
-
requestAnimationFrame(() => setCaret(newCursorStart, newCursorEnd));
|
217
|
-
};
|
218
|
-
const focus = () => {
|
219
|
-
elementRef.value?.focus();
|
220
|
-
};
|
221
|
-
const blur = () => {
|
222
|
-
elementRef.value?.blur();
|
223
|
-
};
|
224
|
-
onMounted(() => {
|
225
|
-
updateTextValue(props.value);
|
226
|
-
updateTextParts();
|
227
|
-
});
|
228
|
-
__expose({
|
229
|
-
focus,
|
230
|
-
blur,
|
231
|
-
wrapSelection,
|
232
|
-
setCaret,
|
233
|
-
getCaret,
|
234
|
-
get offsetWidth() {
|
235
|
-
return elementRef.value?.offsetWidth ?? 0;
|
236
|
-
}
|
237
|
-
});
|
238
|
-
return (_ctx, _cache) => {
|
239
|
-
return openBlock(), createElementBlock("div", {
|
240
|
-
ref: "element",
|
241
|
-
contenteditable: _ctx.readonly || _ctx.disabled ? "false" : "plaintext-only",
|
242
|
-
role: "textbox",
|
243
|
-
"aria-multiline": "true",
|
244
|
-
spellcheck: "false",
|
245
|
-
placeholder: _ctx.placeholder,
|
246
|
-
class: "relative whitespace-pre",
|
247
|
-
onInput,
|
248
|
-
onBeforeinput: _cache[0] || (_cache[0] = ($event) => insertParagraph($event)),
|
249
|
-
onPaste,
|
250
|
-
onKeydown: _cache[1] || (_cache[1] = ($event) => _ctx.$emit("keydown", $event)),
|
251
|
-
onFocus: _cache[2] || (_cache[2] = ($event) => {
|
252
|
-
_ctx.$emit("focus", $event);
|
253
|
-
focused.value = true;
|
254
|
-
}),
|
255
|
-
onBlur: _cache[3] || (_cache[3] = ($event) => {
|
256
|
-
_ctx.$emit("blur", $event);
|
257
|
-
focused.value = false;
|
258
|
-
})
|
259
|
-
}, null, 40, _hoisted_1);
|
260
|
-
};
|
261
|
-
}
|
262
|
-
});
|
263
4
|
|
264
5
|
export { _sfc_main as default };
|
@@ -1,5 +1,264 @@
|
|
1
|
-
import
|
2
|
-
|
1
|
+
import { defineComponent, useTemplateRef, ref, watch, onMounted, createElementBlock, openBlock, nextTick } from 'vue';
|
2
|
+
import { WrapSelectionType } from '../../../utils/utils.js';
|
3
|
+
import { preserveIndentation } from '../models/toolbarActions.js';
|
4
|
+
import { getCaretOffset, setCaretOffset } from '../models/utils.js';
|
3
5
|
|
6
|
+
const _hoisted_1 = ["contenteditable", "placeholder"];
|
7
|
+
const _sfc_main = /* @__PURE__ */ defineComponent({
|
8
|
+
__name: "ContentEditable",
|
9
|
+
props: {
|
10
|
+
value: {},
|
11
|
+
placeholder: {},
|
12
|
+
maxLength: {},
|
13
|
+
textParts: {},
|
14
|
+
readonly: { type: Boolean },
|
15
|
+
disabled: { type: Boolean }
|
16
|
+
},
|
17
|
+
emits: ["update:model-value", "focus", "blur", "keydown"],
|
18
|
+
setup(__props, { expose: __expose, emit: __emit }) {
|
19
|
+
const props = __props;
|
20
|
+
const emit = __emit;
|
21
|
+
const elementRef = useTemplateRef("element");
|
22
|
+
const focused = ref(false);
|
23
|
+
const updateTextParts = () => {
|
24
|
+
if (!elementRef.value || !props.textParts) return;
|
25
|
+
const offsets = getCaret();
|
26
|
+
let nodeIndex = 0;
|
27
|
+
for (const item of props.textParts) {
|
28
|
+
const existingNode = elementRef.value.childNodes[nodeIndex] ?? null;
|
29
|
+
if (typeof item === "string") {
|
30
|
+
const displayText = item.replace(/\n$/g, "\n ");
|
31
|
+
if (existingNode instanceof Text) {
|
32
|
+
if (existingNode.textContent !== displayText) existingNode.textContent = displayText;
|
33
|
+
} else {
|
34
|
+
const textNode = document.createTextNode(displayText);
|
35
|
+
elementRef.value.insertBefore(textNode, existingNode);
|
36
|
+
}
|
37
|
+
} else {
|
38
|
+
if (existingNode instanceof HTMLElement && existingNode.tagName.toLowerCase() === item.tag.toLowerCase()) {
|
39
|
+
if (existingNode.textContent !== item.value) existingNode.textContent = item.value;
|
40
|
+
if (existingNode.className !== (item.class || "")) existingNode.className = item.class || "";
|
41
|
+
if (item.edit === false && existingNode.getAttribute("contenteditable") !== "false") {
|
42
|
+
existingNode.setAttribute("contenteditable", "false");
|
43
|
+
}
|
44
|
+
} else {
|
45
|
+
const element = document.createElement(item.tag);
|
46
|
+
element.textContent = item.value;
|
47
|
+
element.setAttribute("contenteditable", item.edit ? "plaintext-only" : "false");
|
48
|
+
if (item.class) element.className = item.class;
|
49
|
+
elementRef.value.insertBefore(element, existingNode);
|
50
|
+
}
|
51
|
+
}
|
52
|
+
nodeIndex++;
|
53
|
+
}
|
54
|
+
while (elementRef.value.childNodes.length > props.textParts.length) {
|
55
|
+
const nodeToRemove = elementRef.value.childNodes[props.textParts.length];
|
56
|
+
elementRef.value.removeChild(nodeToRemove);
|
57
|
+
}
|
58
|
+
if (focused.value && !isSetCaretNext) setCaret(offsets.start, offsets.end !== offsets.start ? void 0 : offsets.end);
|
59
|
+
};
|
60
|
+
watch(() => props.textParts, updateTextParts, { immediate: true });
|
61
|
+
const updateTextValue = (value) => {
|
62
|
+
if (props.textParts || !elementRef.value) return;
|
63
|
+
if (elementRef.value.textContent !== value) {
|
64
|
+
const offsets = getCaret();
|
65
|
+
elementRef.value.textContent = value;
|
66
|
+
if (focused.value) setCaret(offsets.start, offsets.end !== offsets.start ? void 0 : offsets.end);
|
67
|
+
}
|
68
|
+
};
|
69
|
+
watch(() => props.value, updateTextValue, { immediate: true });
|
70
|
+
const textPartsToText = (parts) => {
|
71
|
+
return parts.map((part) => typeof part === "string" ? part : part.value).join("");
|
72
|
+
};
|
73
|
+
const getCurrentText = () => {
|
74
|
+
return props.textParts ? textPartsToText(props.textParts) : props.value;
|
75
|
+
};
|
76
|
+
const lineBreakEvents = ["insertParagraph", "insertLineBreak"];
|
77
|
+
const insertParagraph = (e) => {
|
78
|
+
if (lineBreakEvents.includes(e.inputType)) {
|
79
|
+
e.preventDefault();
|
80
|
+
insertPlain("\n");
|
81
|
+
}
|
82
|
+
};
|
83
|
+
const regexDifferentEnding = /\r\n?/g;
|
84
|
+
const regexSpaces = /\n \n/g;
|
85
|
+
const regexEnding = / +$/gm;
|
86
|
+
const normalizeText = (text) => {
|
87
|
+
return text.replace(regexDifferentEnding, "\n").replace(regexSpaces, "\n\n").replace(regexEnding, "");
|
88
|
+
};
|
89
|
+
const onInput = (e) => {
|
90
|
+
e.stopImmediatePropagation();
|
91
|
+
if (!(e.target instanceof HTMLDivElement)) return;
|
92
|
+
const rawText = e.target.textContent ?? "";
|
93
|
+
const text = normalizeText(rawText);
|
94
|
+
const currentText = getCurrentText();
|
95
|
+
if (text === currentText) return;
|
96
|
+
if (props.maxLength && typeof text === "string" && text.length > props.maxLength) {
|
97
|
+
e.preventDefault();
|
98
|
+
const substring = text.substring(0, props.maxLength);
|
99
|
+
if (!props.textParts) updateTextValue(substring);
|
100
|
+
else updateTextParts();
|
101
|
+
emit("update:model-value", substring);
|
102
|
+
} else {
|
103
|
+
emit("update:model-value", text);
|
104
|
+
}
|
105
|
+
};
|
106
|
+
const onPaste = async (e) => {
|
107
|
+
e.preventDefault();
|
108
|
+
navigator.clipboard.readText();
|
109
|
+
const text = (e.clipboardData?.getData("text/plain") || await navigator.clipboard.readText()).replace(/\r\n?/g, "\n");
|
110
|
+
insertPlain(text);
|
111
|
+
};
|
112
|
+
const insertPlain = (text) => {
|
113
|
+
const root = elementRef.value;
|
114
|
+
if (!root) return;
|
115
|
+
const { start, end, trail } = getCaret();
|
116
|
+
const currentText = getCurrentText();
|
117
|
+
const next = (currentText ?? "").slice(0, start) + " ".repeat(trail) + text + ((currentText ?? "").slice(end) || " ");
|
118
|
+
const caretAfter = start + text.length + trail;
|
119
|
+
emit("update:model-value", props.maxLength && next.length > props.maxLength ? next.substring(0, props.maxLength) : next);
|
120
|
+
nextTick(() => setCaret(props.maxLength ? Math.min(caretAfter, props.maxLength) : caretAfter));
|
121
|
+
};
|
122
|
+
const getCaret = () => getCaretOffset(elementRef.value);
|
123
|
+
let isSetCaretNext = false;
|
124
|
+
const setCaret = (indexStart, indexEnd) => {
|
125
|
+
isSetCaretNext = false;
|
126
|
+
setCaretOffset(elementRef.value, indexStart, indexEnd);
|
127
|
+
};
|
128
|
+
const collapseList = [" ", "\n"];
|
129
|
+
let offsetsOld = null;
|
130
|
+
const wrapSelection = (value) => {
|
131
|
+
if (focused.value || !offsetsOld) offsetsOld = getCaret();
|
132
|
+
const offsets = offsetsOld;
|
133
|
+
const currentText = getCurrentText() ?? "";
|
134
|
+
let newText = "";
|
135
|
+
let newCursorStart = offsets.start;
|
136
|
+
let newCursorEnd = void 0;
|
137
|
+
switch (value.type) {
|
138
|
+
case WrapSelectionType.TOGGLE:
|
139
|
+
let startLen = value.start.length;
|
140
|
+
const endLen = value.end.length;
|
141
|
+
const textWithContext = currentText.slice(
|
142
|
+
Math.max(0, offsets.start - startLen),
|
143
|
+
Math.min(currentText.length, offsets.end + endLen)
|
144
|
+
);
|
145
|
+
if ((!value.start || textWithContext.startsWith(value.start)) && (!value.end || textWithContext.endsWith(value.end))) {
|
146
|
+
let expandedStart = Math.max(0, offsets.start - startLen);
|
147
|
+
let start = currentText.slice(0, expandedStart);
|
148
|
+
const middle = currentText.slice(offsets.start, offsets.end);
|
149
|
+
let end = currentText.slice(Math.min(currentText.length, offsets.end + endLen));
|
150
|
+
for (const item of collapseList) {
|
151
|
+
if (value.start.startsWith(item) && !start.endsWith(item) && !middle.startsWith(item)) {
|
152
|
+
start += item;
|
153
|
+
expandedStart += item.length;
|
154
|
+
}
|
155
|
+
if (value.end.endsWith(item) && !end.startsWith(item) && !middle.endsWith(item)) end = item + end;
|
156
|
+
}
|
157
|
+
newText = start + middle + end;
|
158
|
+
if (value.prepare) newText = value.prepare(newText, 0);
|
159
|
+
newCursorStart = expandedStart;
|
160
|
+
newCursorEnd = expandedStart + offsets.end - offsets.start;
|
161
|
+
} else {
|
162
|
+
if (!value.start || !value.end) {
|
163
|
+
const offset = value.start ? offsets.start : offsets.end;
|
164
|
+
let start = currentText.slice(0, offset);
|
165
|
+
let end = currentText.slice(offset);
|
166
|
+
for (const item of collapseList) {
|
167
|
+
if (value.start.startsWith(item) && start.endsWith(item)) {
|
168
|
+
start = start.slice(0, offsets.start - item.length);
|
169
|
+
startLen -= item.length;
|
170
|
+
}
|
171
|
+
if (value.end.endsWith(item) && end.startsWith(item)) end = end.slice(item.length);
|
172
|
+
}
|
173
|
+
newText = (value.prepare?.(start, 0) ?? start) + (value.start || value.end) + (value.prepare?.(end, offset) ?? end);
|
174
|
+
} else {
|
175
|
+
let start = currentText.slice(0, offsets.start);
|
176
|
+
const middle = currentText.slice(offsets.start, offsets.end);
|
177
|
+
let end = currentText.slice(offsets.end);
|
178
|
+
for (const item of collapseList) {
|
179
|
+
if (value.start.startsWith(item) && start.endsWith(item)) {
|
180
|
+
start = start.slice(0, offsets.start - item.length);
|
181
|
+
startLen -= item.length;
|
182
|
+
}
|
183
|
+
if (value.end.endsWith(item) && end.startsWith(item)) end = end.slice(item.length);
|
184
|
+
}
|
185
|
+
newText = (value.prepare?.(start, 0) ?? start) + value.start + (value.prepare?.(middle, offsets.start) ?? middle) + value.end + (value.prepare?.(end, offsets.end) ?? end);
|
186
|
+
}
|
187
|
+
newCursorStart = offsets.start + startLen;
|
188
|
+
if (offsets.start !== offsets.end) newCursorEnd = newCursorStart + offsets.end - offsets.start;
|
189
|
+
}
|
190
|
+
break;
|
191
|
+
case WrapSelectionType.LINE_PREFIX:
|
192
|
+
const lineStart = currentText.lastIndexOf("\n", offsets.start - 1) + 1;
|
193
|
+
const lineEnd = currentText.indexOf("\n", offsets.end);
|
194
|
+
const actualLineEnd = lineEnd === -1 ? currentText.length : lineEnd;
|
195
|
+
const linesText = currentText.slice(lineStart, actualLineEnd);
|
196
|
+
const beforeLines = currentText.slice(0, lineStart);
|
197
|
+
const afterLines = currentText.slice(actualLineEnd);
|
198
|
+
const lines = linesText.split("\n");
|
199
|
+
if (lines.length === 0) lines.push("");
|
200
|
+
const allLinesHavePrefix = value.detectPattern ? lines.every((line) => !line.trim() || value.detectPattern.test(line)) : value.linePrefix ? lines.every((line) => !line.trim() || line.startsWith(value.linePrefix)) : false;
|
201
|
+
if (allLinesHavePrefix) {
|
202
|
+
const cleanText = lines.map((line) => preserveIndentation(line, "")).join("\n");
|
203
|
+
newText = beforeLines + cleanText + afterLines;
|
204
|
+
newCursorStart = lineStart;
|
205
|
+
newCursorEnd = lineStart + cleanText.length;
|
206
|
+
} else {
|
207
|
+
const processedText = value.lineTransformAll ? value.lineTransformAll(lines) : value.lineTransform ? lines.map(value.lineTransform).join("\n") : lines.map((line) => line.trim() ? preserveIndentation(line, value.linePrefix) : line).join("\n");
|
208
|
+
newText = beforeLines + processedText + afterLines;
|
209
|
+
newCursorStart = lineStart;
|
210
|
+
newCursorEnd = lineStart + processedText.length;
|
211
|
+
}
|
212
|
+
break;
|
213
|
+
}
|
214
|
+
isSetCaretNext = true;
|
215
|
+
emit("update:model-value", newText);
|
216
|
+
requestAnimationFrame(() => setCaret(newCursorStart, newCursorEnd));
|
217
|
+
};
|
218
|
+
const focus = () => {
|
219
|
+
elementRef.value?.focus();
|
220
|
+
};
|
221
|
+
const blur = () => {
|
222
|
+
elementRef.value?.blur();
|
223
|
+
};
|
224
|
+
onMounted(() => {
|
225
|
+
updateTextValue(props.value);
|
226
|
+
updateTextParts();
|
227
|
+
});
|
228
|
+
__expose({
|
229
|
+
focus,
|
230
|
+
blur,
|
231
|
+
wrapSelection,
|
232
|
+
setCaret,
|
233
|
+
getCaret,
|
234
|
+
get offsetWidth() {
|
235
|
+
return elementRef.value?.offsetWidth ?? 0;
|
236
|
+
}
|
237
|
+
});
|
238
|
+
return (_ctx, _cache) => {
|
239
|
+
return openBlock(), createElementBlock("div", {
|
240
|
+
ref: "element",
|
241
|
+
contenteditable: _ctx.readonly || _ctx.disabled ? "false" : "plaintext-only",
|
242
|
+
role: "textbox",
|
243
|
+
"aria-multiline": "true",
|
244
|
+
spellcheck: "false",
|
245
|
+
placeholder: _ctx.placeholder,
|
246
|
+
class: "relative whitespace-pre",
|
247
|
+
onInput,
|
248
|
+
onBeforeinput: _cache[0] || (_cache[0] = ($event) => insertParagraph($event)),
|
249
|
+
onPaste,
|
250
|
+
onKeydown: _cache[1] || (_cache[1] = ($event) => _ctx.$emit("keydown", $event)),
|
251
|
+
onFocus: _cache[2] || (_cache[2] = ($event) => {
|
252
|
+
_ctx.$emit("focus", $event);
|
253
|
+
focused.value = true;
|
254
|
+
}),
|
255
|
+
onBlur: _cache[3] || (_cache[3] = ($event) => {
|
256
|
+
_ctx.$emit("blur", $event);
|
257
|
+
focused.value = false;
|
258
|
+
})
|
259
|
+
}, null, 40, _hoisted_1);
|
260
|
+
};
|
261
|
+
}
|
262
|
+
});
|
4
263
|
|
5
264
|
export { _sfc_main as default };
|