@vuetify/nightly 3.9.5-dev.2025-08-23 → 3.9.5-dev.2025-08-24

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.
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Vuetify v3.9.5-dev.2025-08-23
2
+ * Vuetify v3.9.5-dev.2025-08-24
3
3
  * Forged by John Leider
4
4
  * Released under the MIT License.
5
5
  */
@@ -13747,6 +13747,7 @@
13747
13747
  const keys = options?.filterKeys ? wrapInArray(options.filterKeys) : false;
13748
13748
  const customFiltersLength = Object.keys(options?.customKeyFilter ?? {}).length;
13749
13749
  if (!items?.length) return array;
13750
+ let lookAheadItem = null;
13750
13751
  loop: for (let i = 0; i < items.length; i++) {
13751
13752
  const [item, transformed = item] = wrapInArray(items[i]);
13752
13753
  const customMatches = {};
@@ -13755,6 +13756,14 @@
13755
13756
  if ((query || customFiltersLength > 0) && !options?.noFilter) {
13756
13757
  if (typeof item === 'object') {
13757
13758
  if (item.type === 'divider' || item.type === 'subheader') {
13759
+ if (lookAheadItem?.type === 'divider' && item.type === 'subheader') {
13760
+ array.push(lookAheadItem); // divider before subheader
13761
+ }
13762
+ lookAheadItem = {
13763
+ index: i,
13764
+ matches: {},
13765
+ type: item.type
13766
+ };
13758
13767
  continue;
13759
13768
  }
13760
13769
  const filterKeys = keys || Object.keys(transformed);
@@ -13780,6 +13789,10 @@
13780
13789
  if (options?.filterMode === 'union' && customMatchesLength !== customFiltersLength && !defaultMatchesLength) continue;
13781
13790
  if (options?.filterMode === 'intersection' && (customMatchesLength !== customFiltersLength || !defaultMatchesLength)) continue;
13782
13791
  }
13792
+ if (lookAheadItem) {
13793
+ array.push(lookAheadItem);
13794
+ lookAheadItem = null;
13795
+ }
13783
13796
  array.push({
13784
13797
  index: i,
13785
13798
  matches: {
@@ -33016,59 +33029,140 @@
33016
33029
  emit
33017
33030
  } = _ref;
33018
33031
  const vTextFieldRef = vue.ref();
33019
- const selection = vue.shallowRef(0);
33020
- const lazySelection = vue.shallowRef(0);
33032
+ const inputAction = vue.shallowRef();
33033
+ const caretPosition = vue.shallowRef(0);
33021
33034
  const mask = useMask(props);
33022
33035
  const returnMaskedValue = vue.computed(() => props.mask && props.returnMaskedValue);
33023
33036
  const model = useProxiedModel(props, 'modelValue', undefined,
33024
33037
  // Always display masked value in input when mask is applied
33025
33038
  val => props.mask ? mask.mask(mask.unmask(val)) : val, val => {
33026
33039
  if (props.mask) {
33027
- const valueBeforeChange = mask.unmask(model.value);
33040
+ const valueWithoutDelimiters = removeMaskDelimiters(val);
33041
+
33028
33042
  // E.g. mask is #-# and the input value is '2-23'
33029
33043
  // model-value should be enforced to '2-2'
33030
- const enforcedMaskedValue = mask.mask(mask.unmask(val));
33031
- const newUnmaskedValue = mask.unmask(enforcedMaskedValue);
33032
- if (newUnmaskedValue === valueBeforeChange) {
33033
- vTextFieldRef.value.value = enforcedMaskedValue;
33034
- }
33035
- val = newUnmaskedValue;
33036
- updateRange();
33037
- return returnMaskedValue.value ? mask.mask(val) : val;
33044
+ const newMaskedValue = mask.mask(valueWithoutDelimiters);
33045
+ const newUnmaskedValue = mask.unmask(newMaskedValue);
33046
+ const newCaretPosition = getNewCaretPosition({
33047
+ oldValue: model.value,
33048
+ newValue: newMaskedValue,
33049
+ oldCaret: caretPosition.value
33050
+ });
33051
+ vTextFieldRef.value.value = newMaskedValue;
33052
+ vTextFieldRef.value.setSelectionRange(newCaretPosition, newCaretPosition);
33053
+ return returnMaskedValue.value ? mask.mask(newUnmaskedValue) : newUnmaskedValue;
33038
33054
  }
33039
33055
  return val;
33040
33056
  });
33041
33057
  const validationValue = vue.toRef(() => returnMaskedValue.value ? model.value : mask.unmask(model.value));
33058
+ function removeMaskDelimiters(val) {
33059
+ return val.split('').filter(ch => !isMaskDelimiter(ch)).join('');
33060
+ }
33061
+ function getNewCaretPosition(_ref2) {
33062
+ let {
33063
+ oldValue,
33064
+ newValue,
33065
+ oldCaret
33066
+ } = _ref2;
33067
+ if (!newValue) return 0;
33068
+ if (!oldValue) return newValue.length;
33069
+ let newCaret;
33070
+ if (inputAction.value === 'Backspace') {
33071
+ newCaret = oldCaret - 1;
33072
+ while (newCaret > 0 && isMaskDelimiter(newValue[newCaret - 1])) newCaret--;
33073
+ } else if (inputAction.value === 'Delete') {
33074
+ newCaret = oldCaret;
33075
+ } else {
33076
+ // insertion
33077
+ newCaret = oldCaret + 1;
33078
+ while (isMaskDelimiter(newValue[newCaret])) newCaret++;
33079
+ if (isMaskDelimiter(newValue[oldCaret])) newCaret++;
33080
+ }
33081
+ return newCaret;
33082
+ }
33042
33083
  vue.onBeforeMount(() => {
33043
33084
  if (props.returnMaskedValue) {
33044
33085
  emit('update:modelValue', model.value);
33045
33086
  }
33046
33087
  });
33047
- function setCaretPosition(newSelection) {
33048
- selection.value = newSelection;
33049
- vTextFieldRef.value && vTextFieldRef.value.setSelectionRange(selection.value, selection.value);
33088
+ function onKeyDown(e) {
33089
+ if (e.metaKey) return;
33090
+ const inputElement = e.target;
33091
+ caretPosition.value = inputElement.selectionStart || 0;
33092
+ inputAction.value = e.key;
33093
+ const hasSelection = inputElement.selectionStart !== inputElement.selectionEnd;
33094
+ if (e.key === 'Backspace' && hasSelection) {
33095
+ e.preventDefault();
33096
+ deleteSelection(e);
33097
+ }
33098
+ }
33099
+ async function onCut(e) {
33100
+ e.preventDefault();
33101
+ copySelectionToClipboard(e);
33102
+ deleteSelection(e);
33050
33103
  }
33051
- function resetSelections() {
33052
- if (!vTextFieldRef.value?.selectionEnd) return;
33053
- selection.value = vTextFieldRef.value.selectionEnd;
33054
- lazySelection.value = 0;
33055
- for (let index = 0; index < selection.value; index++) {
33056
- isMaskDelimiter(vTextFieldRef.value.value[index]) || lazySelection.value++;
33104
+ async function onPaste(e) {
33105
+ e.preventDefault();
33106
+ const inputElement = e.target;
33107
+ const pastedString = removeMaskDelimiters(e.clipboardData?.getData('text') || '');
33108
+ if (!pastedString) return;
33109
+ const pastedCharacters = [...pastedString];
33110
+ const hasSelection = inputElement.selectionStart !== inputElement.selectionEnd;
33111
+ if (hasSelection) {
33112
+ replaceSelection(inputElement, pastedCharacters);
33113
+ } else {
33114
+ insertCharacters(inputElement, pastedCharacters);
33057
33115
  }
33058
33116
  }
33059
- function updateRange() {
33060
- if (!vTextFieldRef.value) return;
33061
- resetSelections();
33062
- let selection = 0;
33063
- const newValue = vTextFieldRef.value.value;
33064
- if (newValue) {
33065
- for (let index = 0; index < newValue.length; index++) {
33066
- if (lazySelection.value <= 0) break;
33067
- isMaskDelimiter(newValue[index]) || lazySelection.value--;
33068
- selection++;
33069
- }
33117
+ function copySelectionToClipboard(e) {
33118
+ const inputElement = e.target;
33119
+ const start = inputElement.selectionStart || 0;
33120
+ const end = inputElement.selectionEnd || 0;
33121
+ const selectedText = inputElement.value.substring(start, end);
33122
+ navigator.clipboard.writeText(selectedText);
33123
+ }
33124
+ async function deleteSelection(e) {
33125
+ const inputElement = e.target;
33126
+ const curStart = inputElement.selectionStart || 0;
33127
+ caretPosition.value = inputElement.selectionEnd || 0;
33128
+ while (caretPosition.value > curStart) {
33129
+ const success = await simulateBackspace(inputElement);
33130
+ if (!success) break;
33131
+ }
33132
+ }
33133
+ async function simulateBackspace(inputElement) {
33134
+ inputAction.value = 'Backspace';
33135
+ model.value = inputElement.value.slice(0, caretPosition.value - 1) + inputElement.value.slice(caretPosition.value);
33136
+ inputAction.value = '';
33137
+ if (caretPosition.value === inputElement.selectionEnd) return false;
33138
+ caretPosition.value = inputElement.selectionEnd || 0;
33139
+ await vue.nextTick();
33140
+ return true;
33141
+ }
33142
+ async function insertCharacters(inputElement, pastedCharacters) {
33143
+ for (let i = 0; i < pastedCharacters.length; i++) {
33144
+ await insertCharacter(inputElement, pastedCharacters[i]);
33070
33145
  }
33071
- setCaretPosition(selection);
33146
+ }
33147
+ async function insertCharacter(inputElement, character) {
33148
+ caretPosition.value = inputElement.selectionEnd || 0;
33149
+ model.value = inputElement.value.slice(0, caretPosition.value) + character + inputElement.value.slice(caretPosition.value);
33150
+ await vue.nextTick();
33151
+ }
33152
+ async function replaceSelection(inputElement, pastedCharacters) {
33153
+ caretPosition.value = inputElement.selectionStart || 0;
33154
+ for (let i = 0; i < pastedCharacters.length; i++) {
33155
+ await replaceCharacter(caretPosition.value, pastedCharacters[i]);
33156
+ caretPosition.value++;
33157
+ }
33158
+ }
33159
+ async function replaceCharacter(index, character) {
33160
+ let targetIndex = index;
33161
+
33162
+ // Find next non-delimiter position
33163
+ while (targetIndex < model.value.length && isMaskDelimiter(model.value[targetIndex])) targetIndex++;
33164
+ model.value = model.value.slice(0, targetIndex) + character + model.value.slice(targetIndex + 1);
33165
+ await vue.nextTick();
33072
33166
  }
33073
33167
  useRender(() => {
33074
33168
  const textFieldProps = VTextField.filterProps(props);
@@ -33076,7 +33170,10 @@
33076
33170
  "modelValue": model.value,
33077
33171
  "onUpdate:modelValue": $event => model.value = $event,
33078
33172
  "ref": vTextFieldRef,
33079
- "validationValue": validationValue.value
33173
+ "validationValue": validationValue.value,
33174
+ "onCut": onCut,
33175
+ "onPaste": onPaste,
33176
+ "onKeydown": onKeyDown
33080
33177
  }), {
33081
33178
  ...slots
33082
33179
  });
@@ -35702,7 +35799,7 @@
35702
35799
  };
35703
35800
  });
35704
35801
  }
35705
- const version$1 = "3.9.5-dev.2025-08-23";
35802
+ const version$1 = "3.9.5-dev.2025-08-24";
35706
35803
  createVuetify$1.version = version$1;
35707
35804
 
35708
35805
  // Vue's inject() can only be used in setup
@@ -36000,7 +36097,7 @@
36000
36097
 
36001
36098
  /* eslint-disable local-rules/sort-imports */
36002
36099
 
36003
- const version = "3.9.5-dev.2025-08-23";
36100
+ const version = "3.9.5-dev.2025-08-24";
36004
36101
 
36005
36102
  /* eslint-disable local-rules/sort-imports */
36006
36103