@sdata/web-vue 3.27.0 → 3.28.0
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/sd.css +4 -0
- package/dist/sd.min.css +1 -1
- package/es/_utils/grapheme.d.ts +5 -0
- package/es/_utils/grapheme.js +93 -0
- package/es/basic-crud-table/basic-crud-table.vue_vue_type_script_setup_true_lang.js +33 -23
- package/es/basic-crud-table/index.d.ts +1 -1
- package/es/basic-crud-table/types.d.ts +9 -1
- package/es/components.d.ts +1 -0
- package/es/index.css +4 -0
- package/es/index.d.ts +3 -0
- package/es/index.js +4 -1
- package/es/index.scss +1 -0
- package/es/input/input.js +5 -6
- package/es/input-mask/index.d.ts +120 -0
- package/es/input-mask/index.js +12 -0
- package/es/input-mask/input-mask.js +5 -0
- package/es/input-mask/input-mask.vue.d.ts +49 -0
- package/es/input-mask/input-mask.vue_vue_type_script_setup_true_lang.js +346 -0
- package/es/input-mask/mask-engine.d.ts +18 -0
- package/es/input-mask/mask-engine.js +176 -0
- package/es/input-mask/presets.d.ts +2 -0
- package/es/input-mask/presets.js +220 -0
- package/es/input-mask/style/css.js +1 -0
- package/es/input-mask/style/index.css +20 -0
- package/es/input-mask/style/index.d.ts +2 -0
- package/es/input-mask/style/index.js +1 -0
- package/es/input-mask/style/index.scss +10 -0
- package/es/input-mask/style/token.scss +5 -0
- package/es/input-mask/types.d.ts +49 -0
- package/es/sd-vue.js +2 -0
- package/es/textarea/textarea.vue_vue_type_script_setup_true_lang.js +5 -6
- package/es/toolbar/toolbar.vue_vue_type_script_setup_true_lang.js +8 -10
- package/es/toolbar/types.d.ts +3 -0
- package/json/vetur-attributes.json +6 -0
- package/json/vetur-tags.json +5 -0
- package/json/web-types.json +23 -1
- package/package.json +1 -1
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
import _objectSpread2 from "../_virtual/_@oxc-project_runtime@0.139.0/helpers/esm/objectSpread2.js";
|
|
2
|
+
import { getPrefixCls } from "../_utils/global-config.js";
|
|
3
|
+
import Input from "../input/index.js";
|
|
4
|
+
import { formatInputMask, resolveDeletion } from "./mask-engine.js";
|
|
5
|
+
import { inputMaskPresets } from "./presets.js";
|
|
6
|
+
import { computed, createBlock, createSlots, defineComponent, mergeProps, nextTick, onBeforeUnmount, onMounted, openBlock, ref, renderSlot, unref, useAttrs, watch, withCtx } from "vue";
|
|
7
|
+
//#region components/input-mask/input-mask.vue?vue&type=script&setup=true&lang.ts
|
|
8
|
+
var input_mask_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent(_objectSpread2(_objectSpread2({}, {
|
|
9
|
+
name: "InputMask",
|
|
10
|
+
inheritAttrs: false
|
|
11
|
+
}), {}, {
|
|
12
|
+
__name: "input-mask",
|
|
13
|
+
props: {
|
|
14
|
+
modelValue: {},
|
|
15
|
+
defaultValue: { default: "" },
|
|
16
|
+
mask: {},
|
|
17
|
+
preset: {},
|
|
18
|
+
maskChar: { default: "_" },
|
|
19
|
+
formatChars: {},
|
|
20
|
+
alwaysShowMask: {
|
|
21
|
+
type: Boolean,
|
|
22
|
+
default: false
|
|
23
|
+
},
|
|
24
|
+
beforeMaskedValueChange: {},
|
|
25
|
+
size: {},
|
|
26
|
+
allowClear: {
|
|
27
|
+
type: Boolean,
|
|
28
|
+
default: false
|
|
29
|
+
},
|
|
30
|
+
disabled: {
|
|
31
|
+
type: Boolean,
|
|
32
|
+
default: false
|
|
33
|
+
},
|
|
34
|
+
readonly: {
|
|
35
|
+
type: [Boolean, String],
|
|
36
|
+
default: false
|
|
37
|
+
},
|
|
38
|
+
error: {
|
|
39
|
+
type: Boolean,
|
|
40
|
+
default: false
|
|
41
|
+
},
|
|
42
|
+
placeholder: {},
|
|
43
|
+
fitWidth: {
|
|
44
|
+
type: Boolean,
|
|
45
|
+
default: false
|
|
46
|
+
},
|
|
47
|
+
maxWFull: {
|
|
48
|
+
type: Boolean,
|
|
49
|
+
default: true
|
|
50
|
+
},
|
|
51
|
+
showWordLimit: {
|
|
52
|
+
type: Boolean,
|
|
53
|
+
default: false
|
|
54
|
+
},
|
|
55
|
+
maxLength: {},
|
|
56
|
+
wordLength: {},
|
|
57
|
+
wordSlice: {},
|
|
58
|
+
inputAttrs: {},
|
|
59
|
+
prepend: {},
|
|
60
|
+
append: {}
|
|
61
|
+
},
|
|
62
|
+
emits: [
|
|
63
|
+
"update:modelValue",
|
|
64
|
+
"input",
|
|
65
|
+
"change",
|
|
66
|
+
"complete",
|
|
67
|
+
"pressEnter",
|
|
68
|
+
"clear",
|
|
69
|
+
"focus",
|
|
70
|
+
"blur"
|
|
71
|
+
],
|
|
72
|
+
setup(__props, { expose: __expose, emit: __emit }) {
|
|
73
|
+
var _props$modelValue;
|
|
74
|
+
const props = __props;
|
|
75
|
+
const emit = __emit;
|
|
76
|
+
const attrs = useAttrs();
|
|
77
|
+
const prefixCls = getPrefixCls("input-mask");
|
|
78
|
+
const inputComponentRef = ref();
|
|
79
|
+
const focused = ref(false);
|
|
80
|
+
const lastSelection = ref(null);
|
|
81
|
+
const complete = ref(false);
|
|
82
|
+
const presetDefinition = computed(() => props.preset ? inputMaskPresets[props.preset] : void 0);
|
|
83
|
+
const effectiveMask = computed(() => {
|
|
84
|
+
var _props$mask, _presetDefinition$val;
|
|
85
|
+
return (_props$mask = props.mask) !== null && _props$mask !== void 0 ? _props$mask : (_presetDefinition$val = presetDefinition.value) === null || _presetDefinition$val === void 0 ? void 0 : _presetDefinition$val.mask;
|
|
86
|
+
});
|
|
87
|
+
const effectiveFormatChars = computed(() => {
|
|
88
|
+
var _presetDefinition$val2;
|
|
89
|
+
return _objectSpread2(_objectSpread2({}, (_presetDefinition$val2 = presetDefinition.value) === null || _presetDefinition$val2 === void 0 ? void 0 : _presetDefinition$val2.formatChars), props.formatChars);
|
|
90
|
+
});
|
|
91
|
+
const effectiveMaskChar = computed(() => {
|
|
92
|
+
var _Array$from$;
|
|
93
|
+
if (props.maskChar === null) return null;
|
|
94
|
+
return (_Array$from$ = Array.from(props.maskChar)[0]) !== null && _Array$from$ !== void 0 ? _Array$from$ : "_";
|
|
95
|
+
});
|
|
96
|
+
const lengthManaged = computed(() => Boolean(effectiveMask.value) || Boolean(props.preset));
|
|
97
|
+
const normalizePresetValue = (value, previousValue) => {
|
|
98
|
+
var _preset$normalize, _preset$normalize2, _preset$accepts;
|
|
99
|
+
const preset = presetDefinition.value;
|
|
100
|
+
if (!preset || effectiveMask.value) return value;
|
|
101
|
+
const normalized = (_preset$normalize = (_preset$normalize2 = preset.normalize) === null || _preset$normalize2 === void 0 ? void 0 : _preset$normalize2.call(preset, value)) !== null && _preset$normalize !== void 0 ? _preset$normalize : value;
|
|
102
|
+
return ((_preset$accepts = preset.accepts) === null || _preset$accepts === void 0 ? void 0 : _preset$accepts.call(preset, normalized)) === false ? previousValue : normalized;
|
|
103
|
+
};
|
|
104
|
+
const applyNormalization = (value) => {
|
|
105
|
+
if (effectiveMask.value) {
|
|
106
|
+
const result = formatInputMask(value, null, effectiveMask.value, {
|
|
107
|
+
maskChar: effectiveMaskChar.value,
|
|
108
|
+
showMask: Boolean(value),
|
|
109
|
+
formatChars: effectiveFormatChars.value
|
|
110
|
+
});
|
|
111
|
+
return {
|
|
112
|
+
value: result.value,
|
|
113
|
+
complete: result.complete
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
value: normalizePresetValue(value, ""),
|
|
118
|
+
complete: false
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
const normalizeStoredValue = (value) => applyNormalization(value).value;
|
|
122
|
+
const innerValue = ref(normalizeStoredValue((_props$modelValue = props.modelValue) !== null && _props$modelValue !== void 0 ? _props$modelValue : props.defaultValue));
|
|
123
|
+
const displayValue = computed(() => {
|
|
124
|
+
if (!effectiveMask.value) return innerValue.value;
|
|
125
|
+
return formatInputMask(innerValue.value, null, effectiveMask.value, {
|
|
126
|
+
maskChar: effectiveMaskChar.value,
|
|
127
|
+
showMask: focused.value || props.alwaysShowMask || Boolean(innerValue.value),
|
|
128
|
+
formatChars: effectiveFormatChars.value
|
|
129
|
+
}).value;
|
|
130
|
+
});
|
|
131
|
+
const mergedPlaceholder = computed(() => {
|
|
132
|
+
var _props$placeholder, _presetDefinition$val3;
|
|
133
|
+
return (_props$placeholder = props.placeholder) !== null && _props$placeholder !== void 0 ? _props$placeholder : (_presetDefinition$val3 = presetDefinition.value) === null || _presetDefinition$val3 === void 0 ? void 0 : _presetDefinition$val3.placeholder;
|
|
134
|
+
});
|
|
135
|
+
const mergedInputAttrs = computed(() => {
|
|
136
|
+
var _presetDefinition$val4;
|
|
137
|
+
const result = _objectSpread2({}, props.inputAttrs);
|
|
138
|
+
if (result.inputmode === void 0 && ((_presetDefinition$val4 = presetDefinition.value) === null || _presetDefinition$val4 === void 0 ? void 0 : _presetDefinition$val4.inputMode)) result.inputmode = presetDefinition.value.inputMode;
|
|
139
|
+
if (props.preset) result["data-mask-preset"] = props.preset;
|
|
140
|
+
return result;
|
|
141
|
+
});
|
|
142
|
+
const forwardedAttrs = computed(() => _objectSpread2(_objectSpread2({}, attrs), {}, { class: [prefixCls, attrs.class] }));
|
|
143
|
+
const getInputElement = () => {
|
|
144
|
+
var _inputComponentRef$va;
|
|
145
|
+
return (_inputComponentRef$va = inputComponentRef.value) === null || _inputComponentRef$va === void 0 ? void 0 : _inputComponentRef$va.inputRef;
|
|
146
|
+
};
|
|
147
|
+
const syncComplete = (value, nextComplete) => {
|
|
148
|
+
if (nextComplete && !complete.value) emit("complete", value);
|
|
149
|
+
complete.value = nextComplete;
|
|
150
|
+
};
|
|
151
|
+
const commitValue = (rawValue) => {
|
|
152
|
+
var _input$selectionStart, _props$beforeMaskedVa, _props$beforeMaskedVa2;
|
|
153
|
+
const input = getInputElement();
|
|
154
|
+
const rawCursor = (_input$selectionStart = input === null || input === void 0 ? void 0 : input.selectionStart) !== null && _input$selectionStart !== void 0 ? _input$selectionStart : rawValue.length;
|
|
155
|
+
const previousState = {
|
|
156
|
+
value: innerValue.value,
|
|
157
|
+
selection: lastSelection.value
|
|
158
|
+
};
|
|
159
|
+
let nextState;
|
|
160
|
+
let nextComplete = false;
|
|
161
|
+
if (effectiveMask.value) {
|
|
162
|
+
const deletion = lastSelection.value ? resolveDeletion(innerValue.value, lastSelection.value, rawValue, rawCursor, effectiveMask.value, effectiveFormatChars.value, effectiveMaskChar.value) : null;
|
|
163
|
+
if (deletion) {
|
|
164
|
+
nextState = {
|
|
165
|
+
value: deletion.value,
|
|
166
|
+
selection: {
|
|
167
|
+
start: deletion.cursor,
|
|
168
|
+
end: deletion.cursor
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
nextComplete = deletion.complete;
|
|
172
|
+
} else {
|
|
173
|
+
const result = formatInputMask(rawValue, rawCursor, effectiveMask.value, {
|
|
174
|
+
maskChar: effectiveMaskChar.value,
|
|
175
|
+
showMask: focused.value || props.alwaysShowMask || Boolean(rawValue),
|
|
176
|
+
formatChars: effectiveFormatChars.value
|
|
177
|
+
});
|
|
178
|
+
nextState = {
|
|
179
|
+
value: result.value,
|
|
180
|
+
selection: result.selection
|
|
181
|
+
};
|
|
182
|
+
nextComplete = result.complete;
|
|
183
|
+
}
|
|
184
|
+
} else {
|
|
185
|
+
const value = normalizePresetValue(rawValue, innerValue.value);
|
|
186
|
+
const cursor = rawCursor === rawValue.length ? value.length : Math.min(rawCursor, value.length);
|
|
187
|
+
nextState = {
|
|
188
|
+
value,
|
|
189
|
+
selection: {
|
|
190
|
+
start: cursor,
|
|
191
|
+
end: cursor
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
nextState = (_props$beforeMaskedVa = (_props$beforeMaskedVa2 = props.beforeMaskedValueChange) === null || _props$beforeMaskedVa2 === void 0 ? void 0 : _props$beforeMaskedVa2.call(props, nextState, previousState)) !== null && _props$beforeMaskedVa !== void 0 ? _props$beforeMaskedVa : nextState;
|
|
196
|
+
innerValue.value = nextState.value;
|
|
197
|
+
lastSelection.value = nextState.selection;
|
|
198
|
+
emit("update:modelValue", nextState.value);
|
|
199
|
+
syncComplete(nextState.value, nextComplete);
|
|
200
|
+
nextTick(() => {
|
|
201
|
+
const element = getInputElement();
|
|
202
|
+
if (focused.value && element && nextState.selection) element.setSelectionRange(nextState.selection.start, nextState.selection.end);
|
|
203
|
+
});
|
|
204
|
+
};
|
|
205
|
+
const handleModelUpdate = (value) => commitValue(value);
|
|
206
|
+
const handleInput = (_value, event) => emit("input", innerValue.value, event);
|
|
207
|
+
const handleChange = (_value, event) => emit("change", innerValue.value, event);
|
|
208
|
+
const handlePressEnter = (event) => emit("pressEnter", event);
|
|
209
|
+
const handleClear = (event) => emit("clear", event);
|
|
210
|
+
const handleFocus = (event) => {
|
|
211
|
+
focused.value = true;
|
|
212
|
+
emit("focus", event);
|
|
213
|
+
};
|
|
214
|
+
const handleBlur = (event) => {
|
|
215
|
+
focused.value = false;
|
|
216
|
+
emit("blur", event);
|
|
217
|
+
};
|
|
218
|
+
const trackedEvents = [
|
|
219
|
+
"keyup",
|
|
220
|
+
"mouseup",
|
|
221
|
+
"select"
|
|
222
|
+
];
|
|
223
|
+
const updateSelectionFromDom = () => {
|
|
224
|
+
const el = getInputElement();
|
|
225
|
+
if (!el || document.activeElement !== el) return;
|
|
226
|
+
const start = el.selectionStart;
|
|
227
|
+
const end = el.selectionEnd;
|
|
228
|
+
if (start !== null && end !== null) lastSelection.value = {
|
|
229
|
+
start,
|
|
230
|
+
end
|
|
231
|
+
};
|
|
232
|
+
};
|
|
233
|
+
onMounted(() => {
|
|
234
|
+
const input = getInputElement();
|
|
235
|
+
if (input) trackedEvents.forEach((event) => input.addEventListener(event, updateSelectionFromDom));
|
|
236
|
+
document.addEventListener("selectionchange", updateSelectionFromDom);
|
|
237
|
+
});
|
|
238
|
+
onBeforeUnmount(() => {
|
|
239
|
+
const input = getInputElement();
|
|
240
|
+
if (input) trackedEvents.forEach((event) => input.removeEventListener(event, updateSelectionFromDom));
|
|
241
|
+
document.removeEventListener("selectionchange", updateSelectionFromDom);
|
|
242
|
+
});
|
|
243
|
+
watch(() => props.modelValue, (value) => {
|
|
244
|
+
if (value === void 0) return;
|
|
245
|
+
const { value: next, complete: nextComplete } = applyNormalization(value !== null && value !== void 0 ? value : "");
|
|
246
|
+
innerValue.value = next;
|
|
247
|
+
if (next !== value) emit("update:modelValue", next);
|
|
248
|
+
syncComplete(next, nextComplete);
|
|
249
|
+
});
|
|
250
|
+
watch([
|
|
251
|
+
effectiveMask,
|
|
252
|
+
presetDefinition,
|
|
253
|
+
effectiveMaskChar,
|
|
254
|
+
effectiveFormatChars
|
|
255
|
+
], () => {
|
|
256
|
+
const { value: next, complete: nextComplete } = applyNormalization(innerValue.value);
|
|
257
|
+
const changed = next !== innerValue.value;
|
|
258
|
+
innerValue.value = next;
|
|
259
|
+
if (changed) emit("update:modelValue", next);
|
|
260
|
+
syncComplete(next, nextComplete);
|
|
261
|
+
});
|
|
262
|
+
__expose({
|
|
263
|
+
get inputRef() {
|
|
264
|
+
return getInputElement();
|
|
265
|
+
},
|
|
266
|
+
focus: () => {
|
|
267
|
+
var _inputComponentRef$va2;
|
|
268
|
+
return (_inputComponentRef$va2 = inputComponentRef.value) === null || _inputComponentRef$va2 === void 0 ? void 0 : _inputComponentRef$va2.focus();
|
|
269
|
+
},
|
|
270
|
+
blur: () => {
|
|
271
|
+
var _inputComponentRef$va3;
|
|
272
|
+
return (_inputComponentRef$va3 = inputComponentRef.value) === null || _inputComponentRef$va3 === void 0 ? void 0 : _inputComponentRef$va3.blur();
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
return (_ctx, _cache) => {
|
|
276
|
+
return openBlock(), createBlock(unref(Input), mergeProps({
|
|
277
|
+
ref_key: "inputComponentRef",
|
|
278
|
+
ref: inputComponentRef
|
|
279
|
+
}, forwardedAttrs.value, {
|
|
280
|
+
"model-value": displayValue.value,
|
|
281
|
+
size: __props.size,
|
|
282
|
+
"allow-clear": __props.allowClear && Boolean(innerValue.value),
|
|
283
|
+
disabled: __props.disabled,
|
|
284
|
+
readonly: __props.readonly,
|
|
285
|
+
error: __props.error,
|
|
286
|
+
placeholder: mergedPlaceholder.value,
|
|
287
|
+
"fit-width": __props.fitWidth,
|
|
288
|
+
"max-w-full": __props.maxWFull,
|
|
289
|
+
"max-length": lengthManaged.value ? void 0 : __props.maxLength,
|
|
290
|
+
"show-word-limit": lengthManaged.value ? false : __props.showWordLimit,
|
|
291
|
+
"word-length": __props.wordLength,
|
|
292
|
+
"word-slice": __props.wordSlice,
|
|
293
|
+
"input-attrs": mergedInputAttrs.value,
|
|
294
|
+
prepend: __props.prepend,
|
|
295
|
+
append: __props.append,
|
|
296
|
+
"onUpdate:modelValue": handleModelUpdate,
|
|
297
|
+
onInput: handleInput,
|
|
298
|
+
onChange: handleChange,
|
|
299
|
+
onPressEnter: handlePressEnter,
|
|
300
|
+
onClear: handleClear,
|
|
301
|
+
onFocus: handleFocus,
|
|
302
|
+
onBlur: handleBlur
|
|
303
|
+
}), createSlots({ _: 2 }, [
|
|
304
|
+
_ctx.$slots.prefix ? {
|
|
305
|
+
name: "prefix",
|
|
306
|
+
fn: withCtx(() => [renderSlot(_ctx.$slots, "prefix")]),
|
|
307
|
+
key: "0"
|
|
308
|
+
} : void 0,
|
|
309
|
+
_ctx.$slots.suffix ? {
|
|
310
|
+
name: "suffix",
|
|
311
|
+
fn: withCtx(() => [renderSlot(_ctx.$slots, "suffix")]),
|
|
312
|
+
key: "1"
|
|
313
|
+
} : void 0,
|
|
314
|
+
_ctx.$slots.prepend ? {
|
|
315
|
+
name: "prepend",
|
|
316
|
+
fn: withCtx(() => [renderSlot(_ctx.$slots, "prepend")]),
|
|
317
|
+
key: "2"
|
|
318
|
+
} : void 0,
|
|
319
|
+
_ctx.$slots.append ? {
|
|
320
|
+
name: "append",
|
|
321
|
+
fn: withCtx(() => [renderSlot(_ctx.$slots, "append")]),
|
|
322
|
+
key: "3"
|
|
323
|
+
} : void 0
|
|
324
|
+
]), 1040, [
|
|
325
|
+
"model-value",
|
|
326
|
+
"size",
|
|
327
|
+
"allow-clear",
|
|
328
|
+
"disabled",
|
|
329
|
+
"readonly",
|
|
330
|
+
"error",
|
|
331
|
+
"placeholder",
|
|
332
|
+
"fit-width",
|
|
333
|
+
"max-w-full",
|
|
334
|
+
"max-length",
|
|
335
|
+
"show-word-limit",
|
|
336
|
+
"word-length",
|
|
337
|
+
"word-slice",
|
|
338
|
+
"input-attrs",
|
|
339
|
+
"prepend",
|
|
340
|
+
"append"
|
|
341
|
+
]);
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
}));
|
|
345
|
+
//#endregion
|
|
346
|
+
export { input_mask_vue_vue_type_script_setup_true_lang_default as default };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { InputMaskPattern, InputMaskSelection } from './types';
|
|
2
|
+
export interface FormatMaskOptions {
|
|
3
|
+
maskChar: string | null;
|
|
4
|
+
showMask: boolean;
|
|
5
|
+
formatChars?: Readonly<Record<string, RegExp>>;
|
|
6
|
+
}
|
|
7
|
+
export interface FormatMaskResult {
|
|
8
|
+
value: string;
|
|
9
|
+
selection: InputMaskSelection;
|
|
10
|
+
complete: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare const defaultInputMaskFormatChars: Readonly<Record<string, RegExp>>;
|
|
13
|
+
export declare function formatInputMask(rawValue: string, rawCursor: number | null, mask: InputMaskPattern, options: FormatMaskOptions): FormatMaskResult;
|
|
14
|
+
export declare function resolveDeletion(previousValue: string, previousSelection: InputMaskSelection | null, rawValue: string, rawCursor: number, mask: InputMaskPattern, formatChars?: Readonly<Record<string, RegExp>>, maskChar?: string | null): {
|
|
15
|
+
value: string;
|
|
16
|
+
cursor: number;
|
|
17
|
+
complete: boolean;
|
|
18
|
+
} | null;
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import _objectSpread2 from "../_virtual/_@oxc-project_runtime@0.139.0/helpers/esm/objectSpread2.js";
|
|
2
|
+
import { codeUnitToGraphemeIndex, graphemeIndexToCodeUnit, splitGraphemes } from "../_utils/grapheme.js";
|
|
3
|
+
//#region components/input-mask/mask-engine.ts
|
|
4
|
+
var defaultInputMaskFormatChars = {
|
|
5
|
+
"9": /[0-9]/,
|
|
6
|
+
"a": /[A-Za-z]/,
|
|
7
|
+
"*": /[A-Za-z0-9]/,
|
|
8
|
+
"H": /* @__PURE__ */ new RegExp("\\p{Script=Han}", "u"),
|
|
9
|
+
"L": /* @__PURE__ */ new RegExp("\\p{Letter}", "u"),
|
|
10
|
+
"N": /* @__PURE__ */ new RegExp("[\\p{Letter}\\p{Number}]", "u")
|
|
11
|
+
};
|
|
12
|
+
var matches = (pattern, character) => {
|
|
13
|
+
pattern.lastIndex = 0;
|
|
14
|
+
return pattern.test(character);
|
|
15
|
+
};
|
|
16
|
+
var parseStringMask = (mask, formatChars) => {
|
|
17
|
+
const tokens = [];
|
|
18
|
+
let escaped = false;
|
|
19
|
+
for (const character of Array.from(mask)) {
|
|
20
|
+
if (!escaped && character === "\\") {
|
|
21
|
+
escaped = true;
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
const pattern = !escaped ? formatChars[character] : void 0;
|
|
25
|
+
tokens.push(pattern ? { pattern } : { literal: character });
|
|
26
|
+
escaped = false;
|
|
27
|
+
}
|
|
28
|
+
if (escaped) tokens.push({ literal: "\\" });
|
|
29
|
+
return tokens;
|
|
30
|
+
};
|
|
31
|
+
var parseMask = (mask, formatChars) => {
|
|
32
|
+
if (typeof mask === "string") return parseStringMask(mask, formatChars);
|
|
33
|
+
return mask.map((token) => token instanceof RegExp ? { pattern: token } : { literal: token });
|
|
34
|
+
};
|
|
35
|
+
var mergeFormatChars = (formatChars) => _objectSpread2(_objectSpread2({}, defaultInputMaskFormatChars), formatChars);
|
|
36
|
+
var readEditableCharacters = (rawValue, tokens, maskChar) => {
|
|
37
|
+
const input = splitGraphemes(rawValue);
|
|
38
|
+
const values = [];
|
|
39
|
+
let inputIndex = 0;
|
|
40
|
+
for (const token of tokens) {
|
|
41
|
+
if (token.literal !== void 0) {
|
|
42
|
+
if (input[inputIndex] === token.literal) inputIndex += 1;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
while (inputIndex < input.length) {
|
|
46
|
+
const character = input[inputIndex++];
|
|
47
|
+
if (maskChar && character === maskChar) continue;
|
|
48
|
+
if (token.pattern && matches(token.pattern, character)) {
|
|
49
|
+
values.push(character);
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return values;
|
|
55
|
+
};
|
|
56
|
+
var buildLayout = (tokens, values, maskChar, showMask) => {
|
|
57
|
+
const positions = [];
|
|
58
|
+
const showTemplate = showMask && Boolean(maskChar);
|
|
59
|
+
let valueIndex = 0;
|
|
60
|
+
let previousEditableFilled = false;
|
|
61
|
+
for (const token of tokens) {
|
|
62
|
+
if (token.literal !== void 0) {
|
|
63
|
+
if (showTemplate || valueIndex < values.length || previousEditableFilled) positions.push({
|
|
64
|
+
char: token.literal,
|
|
65
|
+
literal: true,
|
|
66
|
+
filled: false
|
|
67
|
+
});
|
|
68
|
+
previousEditableFilled = false;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const value = values[valueIndex];
|
|
72
|
+
if (value !== void 0) {
|
|
73
|
+
positions.push({
|
|
74
|
+
char: value,
|
|
75
|
+
literal: false,
|
|
76
|
+
filled: true
|
|
77
|
+
});
|
|
78
|
+
valueIndex += 1;
|
|
79
|
+
previousEditableFilled = true;
|
|
80
|
+
} else if (showTemplate) {
|
|
81
|
+
positions.push({
|
|
82
|
+
char: maskChar,
|
|
83
|
+
literal: false,
|
|
84
|
+
filled: false
|
|
85
|
+
});
|
|
86
|
+
previousEditableFilled = false;
|
|
87
|
+
} else previousEditableFilled = false;
|
|
88
|
+
}
|
|
89
|
+
return positions;
|
|
90
|
+
};
|
|
91
|
+
var renderMask = (tokens, values, maskChar, showMask) => {
|
|
92
|
+
if (!values.length && !showMask) return "";
|
|
93
|
+
return buildLayout(tokens, values, maskChar, showMask).map((position) => position.char).join("");
|
|
94
|
+
};
|
|
95
|
+
var sliceToGraphemeBoundary = (value, cursor) => {
|
|
96
|
+
const graphemeIndex = codeUnitToGraphemeIndex(value, cursor);
|
|
97
|
+
return value.slice(0, graphemeIndexToCodeUnit(value, graphemeIndex));
|
|
98
|
+
};
|
|
99
|
+
function formatInputMask(rawValue, rawCursor, mask, options) {
|
|
100
|
+
const tokens = parseMask(mask, mergeFormatChars(options.formatChars));
|
|
101
|
+
const values = readEditableCharacters(rawValue, tokens, options.maskChar);
|
|
102
|
+
const value = renderMask(tokens, values, options.maskChar, options.showMask);
|
|
103
|
+
const editableCount = tokens.filter((token) => token.pattern).length;
|
|
104
|
+
const cursorValue = renderMask(tokens, readEditableCharacters(rawCursor === null ? rawValue : sliceToGraphemeBoundary(rawValue, rawCursor), tokens, options.maskChar), null, false);
|
|
105
|
+
const cursor = Math.min(cursorValue.length, value.length);
|
|
106
|
+
return {
|
|
107
|
+
value,
|
|
108
|
+
selection: {
|
|
109
|
+
start: cursor,
|
|
110
|
+
end: cursor
|
|
111
|
+
},
|
|
112
|
+
complete: values.length === editableCount
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
var layoutOffset = (positions, index) => {
|
|
116
|
+
let offset = 0;
|
|
117
|
+
for (let i = 0; i < index && i < positions.length; i++) offset += positions[i].char.length;
|
|
118
|
+
return offset;
|
|
119
|
+
};
|
|
120
|
+
var layoutIndexAtOffset = (positions, offset) => {
|
|
121
|
+
let acc = 0;
|
|
122
|
+
for (let i = 0; i < positions.length; i++) {
|
|
123
|
+
const len = positions[i].char.length;
|
|
124
|
+
if (offset < acc + len) return i;
|
|
125
|
+
acc += len;
|
|
126
|
+
}
|
|
127
|
+
return positions.length - 1;
|
|
128
|
+
};
|
|
129
|
+
var nearestFilled = (positions, fromIndex, step) => {
|
|
130
|
+
for (let i = fromIndex + step; i >= 0 && i < positions.length; i += step) if (positions[i].filled) return i;
|
|
131
|
+
return -1;
|
|
132
|
+
};
|
|
133
|
+
/**
|
|
134
|
+
* Resolves a single-character deletion so that backspace/delete on a fixed
|
|
135
|
+
* separator (or an attempt to delete an empty placeholder) clears the adjacent
|
|
136
|
+
* filled editable value instead of being a no-op. This mirrors react-input-mask's
|
|
137
|
+
* `getLeftEditablePosition` / `getRightEditablePosition` behavior.
|
|
138
|
+
*
|
|
139
|
+
* Returns the re-formatted value, cursor and completeness, or `null` to let the
|
|
140
|
+
* caller fall back to a full reformat (insertions, multi-char deletions, range
|
|
141
|
+
* edits, or nothing-to-delete). The result is always canonical (re-packed), so
|
|
142
|
+
* the stored value, the displayed value and the emitted modelValue stay in sync.
|
|
143
|
+
*/
|
|
144
|
+
function resolveDeletion(previousValue, previousSelection, rawValue, rawCursor, mask, formatChars, maskChar = null) {
|
|
145
|
+
if (!previousSelection || previousSelection.start !== previousSelection.end) return null;
|
|
146
|
+
if (previousValue.length - rawValue.length !== 1) return null;
|
|
147
|
+
const prevCursor = previousSelection.start;
|
|
148
|
+
const backspace = rawCursor === prevCursor - 1;
|
|
149
|
+
if (!backspace && !(rawCursor === prevCursor)) return null;
|
|
150
|
+
const merged = mergeFormatChars(formatChars);
|
|
151
|
+
const tokens = parseMask(mask, merged);
|
|
152
|
+
const layout = buildLayout(tokens, readEditableCharacters(previousValue, tokens, maskChar), maskChar, true);
|
|
153
|
+
const deletedIndex = layoutIndexAtOffset(layout, backspace ? prevCursor - 1 : prevCursor);
|
|
154
|
+
if (deletedIndex < 0) return null;
|
|
155
|
+
const deleted = layout[deletedIndex];
|
|
156
|
+
let targetIndex;
|
|
157
|
+
if (deleted.literal) {
|
|
158
|
+
targetIndex = nearestFilled(layout, deletedIndex, backspace ? -1 : 1);
|
|
159
|
+
if (targetIndex < 0) return null;
|
|
160
|
+
} else if (deleted.filled) targetIndex = deletedIndex;
|
|
161
|
+
else return null;
|
|
162
|
+
const targetOffset = layoutOffset(layout, targetIndex);
|
|
163
|
+
const targetLen = layout[targetIndex].char.length;
|
|
164
|
+
const result = formatInputMask(previousValue.slice(0, targetOffset) + previousValue.slice(targetOffset + targetLen), targetOffset, mask, {
|
|
165
|
+
maskChar,
|
|
166
|
+
showMask: true,
|
|
167
|
+
formatChars: merged
|
|
168
|
+
});
|
|
169
|
+
return {
|
|
170
|
+
value: result.value,
|
|
171
|
+
cursor: result.selection.start,
|
|
172
|
+
complete: result.complete
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
//#endregion
|
|
176
|
+
export { defaultInputMaskFormatChars, formatInputMask, resolveDeletion };
|