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