@blokkli/editor 2.0.0-alpha.44 → 2.0.0-alpha.46
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/module.d.mts +2 -2
- package/dist/module.json +1 -1
- package/dist/module.mjs +22 -1
- package/dist/modules/agent/index.d.mts +1 -1
- package/dist/modules/charts/index.d.mts +1 -1
- package/dist/modules/drupal/graphql/mutations/add_file.graphql +2 -0
- package/dist/modules/drupal/graphql/mutations/add_image.graphql +2 -0
- package/dist/modules/drupal/graphql/mutations/add_video_remote.graphql +2 -0
- package/dist/modules/drupal/index.d.mts +1 -1
- package/dist/modules/drupal/runtime/adapter/index.js +6 -3
- package/dist/modules/table-of-contents/index.d.mts +1 -1
- package/dist/runtime/editor/components/FlexTextarea/index.vue +220 -11
- package/dist/runtime/editor/css/output.css +1 -1
- package/dist/runtime/editor/features/changelog/Dialog/index.d.vue.ts +7 -0
- package/dist/runtime/editor/features/changelog/Dialog/index.vue +43 -0
- package/dist/runtime/editor/features/changelog/Dialog/index.vue.d.ts +7 -0
- package/dist/runtime/editor/features/changelog/changelog.json +42 -0
- package/dist/runtime/editor/features/changelog/index.d.vue.ts +3 -0
- package/dist/runtime/editor/features/changelog/index.vue +56 -0
- package/dist/runtime/editor/features/changelog/index.vue.d.ts +3 -0
- package/dist/runtime/editor/features/clipboard/index.vue +4 -27
- package/dist/runtime/editor/features/clipboard/types.d.ts +6 -0
- package/dist/runtime/editor/features/editable-field/Overlay/Plaintext/index.vue +0 -1
- package/dist/runtime/editor/translations/de.json +20 -0
- package/dist/runtime/editor/translations/fr.json +20 -0
- package/dist/runtime/editor/translations/gsw_CH.json +20 -0
- package/dist/runtime/editor/translations/it.json +20 -0
- package/dist/shared/{editor.BdBm1Z7C.d.mts → editor.BVregnEC.d.mts} +24 -0
- package/dist/types.d.mts +1 -1
- package/package.json +2 -1
package/dist/module.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { NuxtModule } from 'nuxt/schema';
|
|
2
|
-
import { M as ModuleOptions } from './shared/editor.
|
|
3
|
-
export { a as ModuleHooks } from './shared/editor.
|
|
2
|
+
import { M as ModuleOptions } from './shared/editor.BVregnEC.mjs';
|
|
3
|
+
export { a as ModuleHooks } from './shared/editor.BVregnEC.mjs';
|
|
4
4
|
import 'consola';
|
|
5
5
|
import '../dist/global/types/definitions.js';
|
|
6
6
|
import '../dist/global/types/theme.js';
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -18,7 +18,7 @@ import 'typescript';
|
|
|
18
18
|
import 'oxc-walker';
|
|
19
19
|
|
|
20
20
|
const name = "@blokkli/editor";
|
|
21
|
-
const version = "2.0.0-alpha.
|
|
21
|
+
const version = "2.0.0-alpha.46";
|
|
22
22
|
|
|
23
23
|
function validateOption(optionKey, option, icons) {
|
|
24
24
|
const errors = [];
|
|
@@ -1426,6 +1426,7 @@ const USED_MATERIAL_ICONS = [
|
|
|
1426
1426
|
"bk_mdi_build-fill",
|
|
1427
1427
|
"bk_mdi_calendar_clock",
|
|
1428
1428
|
"bk_mdi_calendar_month",
|
|
1429
|
+
"bk_mdi_campaign",
|
|
1429
1430
|
"bk_mdi_chat",
|
|
1430
1431
|
"bk_mdi_check",
|
|
1431
1432
|
"bk_mdi_check_box",
|
|
@@ -2662,6 +2663,17 @@ export const forceDefaultLanguage = ${JSON.stringify(
|
|
|
2662
2663
|
)}
|
|
2663
2664
|
|
|
2664
2665
|
export const featureFragmentNames = ${JSON.stringify(featureFragmentNames)}
|
|
2666
|
+
|
|
2667
|
+
export const textAutoReplace = ${(() => {
|
|
2668
|
+
const opt = ctx.helper.options.textAutoReplace;
|
|
2669
|
+
const all = opt === void 0 || opt === true;
|
|
2670
|
+
const obj = typeof opt === "object" && opt !== null ? opt : null;
|
|
2671
|
+
return JSON.stringify({
|
|
2672
|
+
quotes: obj ? obj.quotes ?? true : all,
|
|
2673
|
+
ellipsis: obj ? obj.ellipsis ?? true : all,
|
|
2674
|
+
enDash: obj ? obj.enDash ?? true : all
|
|
2675
|
+
});
|
|
2676
|
+
})()}
|
|
2665
2677
|
`;
|
|
2666
2678
|
},
|
|
2667
2679
|
(ctx) => {
|
|
@@ -2719,6 +2731,15 @@ export declare const forceDefaultLanguage: boolean
|
|
|
2719
2731
|
* The fragment names provided by features.
|
|
2720
2732
|
*/
|
|
2721
2733
|
export declare const featureFragmentNames: string[]
|
|
2734
|
+
|
|
2735
|
+
/**
|
|
2736
|
+
* Text auto-replace configuration.
|
|
2737
|
+
*/
|
|
2738
|
+
export declare const textAutoReplace: {
|
|
2739
|
+
quotes: boolean
|
|
2740
|
+
ellipsis: boolean
|
|
2741
|
+
enDash: boolean
|
|
2742
|
+
}
|
|
2722
2743
|
`;
|
|
2723
2744
|
}
|
|
2724
2745
|
);
|
|
@@ -8,6 +8,7 @@ mutation pbAddFile(
|
|
|
8
8
|
$hostUuid: String!
|
|
9
9
|
$hostFieldName: String!
|
|
10
10
|
$afterUuid: String
|
|
11
|
+
$paragraphBundle: String!
|
|
11
12
|
) {
|
|
12
13
|
state: paragraphsEditMutationState(
|
|
13
14
|
entityType: $entityType
|
|
@@ -20,6 +21,7 @@ mutation pbAddFile(
|
|
|
20
21
|
hostType: $hostType
|
|
21
22
|
hostUuid: $hostUuid
|
|
22
23
|
hostFieldName: $hostFieldName
|
|
24
|
+
paragraphBundle: $paragraphBundle
|
|
23
25
|
) {
|
|
24
26
|
...paragraphsBlokkliMutationResult
|
|
25
27
|
}
|
|
@@ -8,6 +8,7 @@ mutation pbAddImage(
|
|
|
8
8
|
$hostUuid: String!
|
|
9
9
|
$hostFieldName: String!
|
|
10
10
|
$afterUuid: String
|
|
11
|
+
$paragraphBundle: String!
|
|
11
12
|
) {
|
|
12
13
|
state: paragraphsEditMutationState(
|
|
13
14
|
entityType: $entityType
|
|
@@ -20,6 +21,7 @@ mutation pbAddImage(
|
|
|
20
21
|
hostType: $hostType
|
|
21
22
|
hostUuid: $hostUuid
|
|
22
23
|
hostFieldName: $hostFieldName
|
|
24
|
+
paragraphBundle: $paragraphBundle
|
|
23
25
|
) {
|
|
24
26
|
...paragraphsBlokkliMutationResult
|
|
25
27
|
}
|
|
@@ -7,6 +7,7 @@ mutation pbAddVideoRemote(
|
|
|
7
7
|
$hostUuid: String!
|
|
8
8
|
$hostFieldName: String!
|
|
9
9
|
$afterUuid: String
|
|
10
|
+
$paragraphBundle: String!
|
|
10
11
|
) {
|
|
11
12
|
state: paragraphsEditMutationState(
|
|
12
13
|
entityType: $entityType
|
|
@@ -18,6 +19,7 @@ mutation pbAddVideoRemote(
|
|
|
18
19
|
hostType: $hostType
|
|
19
20
|
hostUuid: $hostUuid
|
|
20
21
|
hostFieldName: $hostFieldName
|
|
22
|
+
paragraphBundle: $paragraphBundle
|
|
21
23
|
) {
|
|
22
24
|
...paragraphsBlokkliMutationResult
|
|
23
25
|
}
|
|
@@ -887,7 +887,8 @@ export default defineBlokkliEditAdapter(
|
|
|
887
887
|
hostType: e.host.type,
|
|
888
888
|
hostUuid: e.host.uuid,
|
|
889
889
|
hostFieldName: e.host.fieldName,
|
|
890
|
-
afterUuid: e.afterUuid
|
|
890
|
+
afterUuid: e.afterUuid,
|
|
891
|
+
paragraphBundle: e.blockBundle
|
|
891
892
|
}).then(mapMutation);
|
|
892
893
|
} else if (e.item.type === "file" && hasMutation("pbAddFile")) {
|
|
893
894
|
return useGraphqlMutation("pbAddFile", {
|
|
@@ -897,7 +898,8 @@ export default defineBlokkliEditAdapter(
|
|
|
897
898
|
hostType: e.host.type,
|
|
898
899
|
hostUuid: e.host.uuid,
|
|
899
900
|
hostFieldName: e.host.fieldName,
|
|
900
|
-
afterUuid: e.afterUuid
|
|
901
|
+
afterUuid: e.afterUuid,
|
|
902
|
+
paragraphBundle: e.blockBundle
|
|
901
903
|
}).then(mapMutation);
|
|
902
904
|
} else if (e.item.type === "video" && hasMutation("pbAddVideoRemote")) {
|
|
903
905
|
return useGraphqlMutation("pbAddVideoRemote", {
|
|
@@ -906,7 +908,8 @@ export default defineBlokkliEditAdapter(
|
|
|
906
908
|
hostType: e.host.type,
|
|
907
909
|
hostUuid: e.host.uuid,
|
|
908
910
|
hostFieldName: e.host.fieldName,
|
|
909
|
-
afterUuid: e.afterUuid
|
|
911
|
+
afterUuid: e.afterUuid,
|
|
912
|
+
paragraphBundle: e.blockBundle
|
|
910
913
|
}).then(mapMutation);
|
|
911
914
|
}
|
|
912
915
|
};
|
|
@@ -22,9 +22,11 @@
|
|
|
22
22
|
</template>
|
|
23
23
|
|
|
24
24
|
<script setup>
|
|
25
|
-
import { useTemplateRef, ref, computed, watch, onMounted } from "#imports";
|
|
25
|
+
import { useTemplateRef, ref, computed, watch, onMounted, useBlokkli } from "#imports";
|
|
26
26
|
import { onBlokkliEvent } from "#blokkli/editor/composables";
|
|
27
27
|
import { ClipboardData } from "#blokkli/editor/helpers/clipboardData";
|
|
28
|
+
import { textAutoReplace } from "#blokkli-build/editor-config";
|
|
29
|
+
const GUILLEMET_LANGUAGES = ["de", "fr"];
|
|
28
30
|
defineOptions({
|
|
29
31
|
inheritAttrs: false
|
|
30
32
|
});
|
|
@@ -38,6 +40,10 @@ const props = defineProps({
|
|
|
38
40
|
autofocus: { type: Boolean, required: false }
|
|
39
41
|
});
|
|
40
42
|
const emit = defineEmits(["submit", "keydown"]);
|
|
43
|
+
const { ui } = useBlokkli();
|
|
44
|
+
const shouldReplaceQuotes = computed(
|
|
45
|
+
() => textAutoReplace.quotes && GUILLEMET_LANGUAGES.includes(ui.interfaceLanguage.value)
|
|
46
|
+
);
|
|
41
47
|
const modelValue = defineModel({ type: String, ...{ required: true } });
|
|
42
48
|
const textarea = useTemplateRef("textarea");
|
|
43
49
|
const height = ref(props.minHeight);
|
|
@@ -45,12 +51,223 @@ const isScrollable = computed(() => {
|
|
|
45
51
|
if (!props.maxHeight) return false;
|
|
46
52
|
return height.value >= props.maxHeight;
|
|
47
53
|
});
|
|
54
|
+
const IDLE_MS = 400;
|
|
55
|
+
let history = [];
|
|
56
|
+
let historyIndex = -1;
|
|
57
|
+
let lastActionType = null;
|
|
58
|
+
let idleTimer = null;
|
|
59
|
+
let restoringSnapshot = false;
|
|
60
|
+
function currentSnapshot() {
|
|
61
|
+
const el = textarea.value;
|
|
62
|
+
return {
|
|
63
|
+
text: modelValue.value,
|
|
64
|
+
cursor: el?.selectionStart ?? modelValue.value.length
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function pushSnapshot(entry) {
|
|
68
|
+
const snap = entry ?? currentSnapshot();
|
|
69
|
+
if (history[historyIndex]?.text === snap.text) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
history = history.slice(0, historyIndex + 1);
|
|
73
|
+
history.push(snap);
|
|
74
|
+
historyIndex = history.length - 1;
|
|
75
|
+
}
|
|
76
|
+
function pushUndoSnapshot() {
|
|
77
|
+
clearIdleTimer();
|
|
78
|
+
pushSnapshot();
|
|
79
|
+
}
|
|
80
|
+
function clearIdleTimer() {
|
|
81
|
+
if (idleTimer !== null) {
|
|
82
|
+
clearTimeout(idleTimer);
|
|
83
|
+
idleTimer = null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function scheduleIdleSnapshot() {
|
|
87
|
+
clearIdleTimer();
|
|
88
|
+
idleTimer = setTimeout(() => {
|
|
89
|
+
pushSnapshot();
|
|
90
|
+
}, IDLE_MS);
|
|
91
|
+
}
|
|
92
|
+
function undo() {
|
|
93
|
+
if (historyIndex < 0) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (historyIndex === history.length - 1) {
|
|
97
|
+
const snap = currentSnapshot();
|
|
98
|
+
if (history[historyIndex]?.text !== snap.text) {
|
|
99
|
+
pushSnapshot(snap);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (historyIndex <= 0) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
historyIndex--;
|
|
106
|
+
restoreSnapshot(history[historyIndex]);
|
|
107
|
+
}
|
|
108
|
+
function redo() {
|
|
109
|
+
if (historyIndex >= history.length - 1) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
historyIndex++;
|
|
113
|
+
restoreSnapshot(history[historyIndex]);
|
|
114
|
+
}
|
|
115
|
+
function restoreSnapshot(entry) {
|
|
116
|
+
restoringSnapshot = true;
|
|
117
|
+
modelValue.value = entry.text;
|
|
118
|
+
requestAnimationFrame(() => {
|
|
119
|
+
textarea.value?.setSelectionRange(entry.cursor, entry.cursor);
|
|
120
|
+
restoringSnapshot = false;
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
function onInput() {
|
|
124
|
+
if (restoringSnapshot) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const el = textarea.value;
|
|
128
|
+
if (!el) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const text = el.value;
|
|
132
|
+
const prevText = history[historyIndex]?.text ?? "";
|
|
133
|
+
const actionType = text.length > prevText.length ? "insert" : text.length < prevText.length ? "delete" : "other";
|
|
134
|
+
if (lastActionType !== null && actionType !== lastActionType) {
|
|
135
|
+
pushSnapshot({
|
|
136
|
+
text: prevText,
|
|
137
|
+
cursor: history[historyIndex]?.cursor ?? el.selectionStart
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
lastActionType = actionType;
|
|
141
|
+
scheduleIdleSnapshot();
|
|
142
|
+
}
|
|
143
|
+
function tryReplaceQuotes() {
|
|
144
|
+
const el = textarea.value;
|
|
145
|
+
if (!el) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const cursorPos = el.selectionStart;
|
|
149
|
+
const text = modelValue.value;
|
|
150
|
+
const closePos = cursorPos - 1;
|
|
151
|
+
if (text[closePos] !== '"') {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
let quoteCount = 0;
|
|
155
|
+
for (let i = 0; i < closePos; i++) {
|
|
156
|
+
if (text[i] === '"') {
|
|
157
|
+
quoteCount++;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (quoteCount % 2 === 0) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const openPos = text.lastIndexOf('"', closePos - 1);
|
|
164
|
+
if (openPos === -1 || closePos - openPos < 2) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const inner = text.slice(openPos + 1, closePos);
|
|
168
|
+
if (inner.startsWith(" ") || inner.endsWith(" ")) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
pushUndoSnapshot();
|
|
172
|
+
modelValue.value = text.slice(0, openPos) + "\xAB" + inner + "\xBB" + text.slice(closePos + 1);
|
|
173
|
+
requestAnimationFrame(() => {
|
|
174
|
+
el.setSelectionRange(cursorPos, cursorPos);
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
function tryReplaceEllipsis() {
|
|
178
|
+
const el = textarea.value;
|
|
179
|
+
if (!el) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
const cursorPos = el.selectionStart;
|
|
183
|
+
const text = modelValue.value;
|
|
184
|
+
if (cursorPos < 3) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
if (text.slice(cursorPos - 3, cursorPos) !== "...") {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
pushUndoSnapshot();
|
|
191
|
+
const newCursor = cursorPos - 2;
|
|
192
|
+
modelValue.value = text.slice(0, cursorPos - 3) + "\u2026" + text.slice(cursorPos);
|
|
193
|
+
requestAnimationFrame(() => {
|
|
194
|
+
el.setSelectionRange(newCursor, newCursor);
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
function tryReplaceEnDash() {
|
|
198
|
+
const el = textarea.value;
|
|
199
|
+
if (!el) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
const cursorPos = el.selectionStart;
|
|
203
|
+
const text = modelValue.value;
|
|
204
|
+
if (cursorPos < 2) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
if (text.slice(cursorPos - 2, cursorPos) !== "--") {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
if (cursorPos >= 3 && text[cursorPos - 3] === "-") {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
pushUndoSnapshot();
|
|
214
|
+
const newCursor = cursorPos - 1;
|
|
215
|
+
modelValue.value = text.slice(0, cursorPos - 2) + "\u2013" + text.slice(cursorPos);
|
|
216
|
+
requestAnimationFrame(() => {
|
|
217
|
+
el.setSelectionRange(newCursor, newCursor);
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
onMounted(() => {
|
|
221
|
+
pushSnapshot({
|
|
222
|
+
text: modelValue.value,
|
|
223
|
+
cursor: modelValue.value.length
|
|
224
|
+
});
|
|
225
|
+
textarea.value?.addEventListener("input", onInput);
|
|
226
|
+
if (props.autofocus && textarea.value) {
|
|
227
|
+
textarea.value.focus();
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
watch(modelValue, () => {
|
|
231
|
+
if (restoringSnapshot) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (!modelValue.value) {
|
|
235
|
+
height.value = props.minHeight;
|
|
236
|
+
}
|
|
237
|
+
});
|
|
48
238
|
function onPointerDown(e) {
|
|
49
239
|
if (e.target instanceof HTMLElement) {
|
|
50
240
|
e.target.setPointerCapture(e.pointerId);
|
|
51
241
|
}
|
|
52
242
|
}
|
|
53
243
|
function onKeydown(e) {
|
|
244
|
+
if ((e.ctrlKey || e.metaKey) && !e.altKey) {
|
|
245
|
+
if (e.key.toLowerCase() === "z" && !e.shiftKey) {
|
|
246
|
+
e.preventDefault();
|
|
247
|
+
undo();
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
if (e.key.toLowerCase() === "z" && e.shiftKey || e.key === "y") {
|
|
251
|
+
e.preventDefault();
|
|
252
|
+
redo();
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
if (shouldReplaceQuotes.value && e.key === '"') {
|
|
257
|
+
requestAnimationFrame(() => {
|
|
258
|
+
tryReplaceQuotes();
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
if (textAutoReplace.ellipsis && e.key === ".") {
|
|
262
|
+
requestAnimationFrame(() => {
|
|
263
|
+
tryReplaceEllipsis();
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
if (textAutoReplace.enDash && e.key === "-") {
|
|
267
|
+
requestAnimationFrame(() => {
|
|
268
|
+
tryReplaceEnDash();
|
|
269
|
+
});
|
|
270
|
+
}
|
|
54
271
|
emit("keydown", e);
|
|
55
272
|
if (props.submitOnEnter && e.key === "Enter" && !e.shiftKey) {
|
|
56
273
|
e.preventDefault();
|
|
@@ -66,6 +283,7 @@ function onPaste(e) {
|
|
|
66
283
|
}
|
|
67
284
|
if (!props.pasteMarkdown || !data.hasHtml()) return;
|
|
68
285
|
e.preventDefault();
|
|
286
|
+
pushSnapshot();
|
|
69
287
|
const markdown = data.toMarkdown();
|
|
70
288
|
const el = textarea.value;
|
|
71
289
|
if (!el) return;
|
|
@@ -77,23 +295,14 @@ function onPaste(e) {
|
|
|
77
295
|
const newPos = start + markdown.length;
|
|
78
296
|
requestAnimationFrame(() => {
|
|
79
297
|
el.setSelectionRange(newPos, newPos);
|
|
298
|
+
pushSnapshot();
|
|
80
299
|
});
|
|
81
300
|
}
|
|
82
|
-
watch(modelValue, (newValue) => {
|
|
83
|
-
if (!newValue) {
|
|
84
|
-
height.value = props.minHeight;
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
301
|
onBlokkliEvent("animationFrame", () => {
|
|
88
302
|
const scrollHeight = textarea.value?.scrollHeight ?? props.minHeight;
|
|
89
303
|
const newHeight = Math.max(scrollHeight, props.minHeight);
|
|
90
304
|
height.value = props.maxHeight ? Math.min(newHeight, props.maxHeight) : newHeight;
|
|
91
305
|
});
|
|
92
|
-
onMounted(() => {
|
|
93
|
-
if (props.autofocus && textarea.value) {
|
|
94
|
-
textarea.value.focus();
|
|
95
|
-
}
|
|
96
|
-
});
|
|
97
306
|
defineExpose({
|
|
98
307
|
focus: () => textarea.value?.focus(),
|
|
99
308
|
blur: () => textarea.value?.blur(),
|