diff --git a/components/lib/api/FilterService.js b/components/lib/api/FilterService.js
index 1fb8b05cc..534b5491b 100644
--- a/components/lib/api/FilterService.js
+++ b/components/lib/api/FilterService.js
@@ -30,7 +30,7 @@ const FilterService = {
},
filters: {
startsWith(value, filter, filterLocale) {
- if (filter === undefined || filter === null || filter.trim() === '') {
+ if (filter === undefined || filter === null || filter === '') {
return true;
}
@@ -44,7 +44,7 @@ const FilterService = {
return stringValue.slice(0, filterValue.length) === filterValue;
},
contains(value, filter, filterLocale) {
- if (filter === undefined || filter === null || (typeof filter === 'string' && filter.trim() === '')) {
+ if (filter === undefined || filter === null || filter === '') {
return true;
}
@@ -58,7 +58,7 @@ const FilterService = {
return stringValue.indexOf(filterValue) !== -1;
},
notContains(value, filter, filterLocale) {
- if (filter === undefined || filter === null || (typeof filter === 'string' && filter.trim() === '')) {
+ if (filter === undefined || filter === null || filter === '') {
return true;
}
@@ -72,7 +72,7 @@ const FilterService = {
return stringValue.indexOf(filterValue) === -1;
},
endsWith(value, filter, filterLocale) {
- if (filter === undefined || filter === null || filter.trim() === '') {
+ if (filter === undefined || filter === null || filter === '') {
return true;
}
@@ -86,7 +86,7 @@ const FilterService = {
return stringValue.indexOf(filterValue, stringValue.length - filterValue.length) !== -1;
},
equals(value, filter, filterLocale) {
- if (filter === undefined || filter === null || (typeof filter === 'string' && filter.trim() === '')) {
+ if (filter === undefined || filter === null || filter === '') {
return true;
}
@@ -98,7 +98,7 @@ const FilterService = {
else return ObjectUtils.removeAccents(value.toString()).toLocaleLowerCase(filterLocale) == ObjectUtils.removeAccents(filter.toString()).toLocaleLowerCase(filterLocale);
},
notEquals(value, filter, filterLocale) {
- if (filter === undefined || filter === null || (typeof filter === 'string' && filter.trim() === '')) {
+ if (filter === undefined || filter === null || filter === '') {
return false;
}
diff --git a/components/lib/autocomplete/AutoComplete.d.ts b/components/lib/autocomplete/AutoComplete.d.ts
index 14d24a845..5bf2ae40d 100755
--- a/components/lib/autocomplete/AutoComplete.d.ts
+++ b/components/lib/autocomplete/AutoComplete.d.ts
@@ -447,6 +447,11 @@ export interface AutoCompleteProps {
* @defaultValue false
*/
selectOnFocus?: boolean | undefined;
+ /**
+ * When enabled, the focus is placed on the hovered option.
+ * @defaultValue true
+ */
+ focusOnHover?: boolean | undefined;
/**
* Locale to use in searching. The default locale is the host environment's current locale.
*/
diff --git a/components/lib/autocomplete/AutoComplete.vue b/components/lib/autocomplete/AutoComplete.vue
index e2e9eceff..f038bedc4 100755
--- a/components/lib/autocomplete/AutoComplete.vue
+++ b/components/lib/autocomplete/AutoComplete.vue
@@ -197,11 +197,11 @@ export default {
overlay: null,
virtualScroller: null,
searchTimeout: null,
- focusOnHover: false,
dirty: false,
data() {
return {
id: this.$attrs.id,
+ clicked: false,
focused: false,
focusedOptionIndex: -1,
focusedMultipleOptionIndex: -1,
@@ -298,6 +298,7 @@ export default {
this.$emit('before-hide');
this.dirty = isFocus;
this.overlayVisible = false;
+ this.clicked = false;
this.focusedOptionIndex = -1;
isFocus && DomHandler.focus(this.$refs.focusInput);
@@ -319,8 +320,12 @@ export default {
this.dirty = true;
this.focused = true;
- this.focusedOptionIndex = this.focusedOptionIndex !== -1 ? this.focusedOptionIndex : this.overlayVisible && this.autoOptionFocus ? this.findFirstFocusedOptionIndex() : -1;
- this.overlayVisible && this.scrollInView(this.focusedOptionIndex);
+
+ if (this.overlayVisible) {
+ this.focusedOptionIndex = this.focusedOptionIndex !== -1 ? this.focusedOptionIndex : this.overlayVisible && this.autoOptionFocus ? this.findFirstFocusedOptionIndex() : -1;
+ this.scrollInView(this.focusedOptionIndex);
+ }
+
this.$emit('focus', event);
},
onBlur(event) {
@@ -394,6 +399,8 @@ export default {
default:
break;
}
+
+ this.clicked = false;
},
onInput(event) {
if (this.searchTimeout) {
@@ -479,6 +486,8 @@ export default {
}
},
onContainerClick(event) {
+ this.clicked = true;
+
if (this.disabled || this.searching || this.loading || this.isInputClicked(event) || this.isDropdownClicked(event)) {
return;
}
@@ -545,7 +554,7 @@ export default {
return;
}
- const optionIndex = this.focusedOptionIndex !== -1 ? this.findNextOptionIndex(this.focusedOptionIndex) : this.findFirstFocusedOptionIndex();
+ const optionIndex = this.focusedOptionIndex !== -1 ? this.findNextOptionIndex(this.focusedOptionIndex) : this.clicked ? this.findFirstOptionIndex() : this.findFirstFocusedOptionIndex();
this.changeFocusedOptionIndex(event, optionIndex);
@@ -564,7 +573,7 @@ export default {
this.overlayVisible && this.hide();
event.preventDefault();
} else {
- const optionIndex = this.focusedOptionIndex !== -1 ? this.findPrevOptionIndex(this.focusedOptionIndex) : this.findLastFocusedOptionIndex();
+ const optionIndex = this.focusedOptionIndex !== -1 ? this.findPrevOptionIndex(this.focusedOptionIndex) : this.clicked ? this.findLastOptionIndex() : this.findLastFocusedOptionIndex();
this.changeFocusedOptionIndex(event, optionIndex);
@@ -618,6 +627,7 @@ export default {
},
onEnterKey(event) {
if (!this.overlayVisible) {
+ this.focusedOptionIndex = -1; // reset
this.onArrowDownKey(event);
} else {
if (this.focusedOptionIndex !== -1) {
@@ -838,16 +848,16 @@ export default {
}
},
scrollInView(index = -1) {
- const id = index !== -1 ? `${this.id}_${index}` : this.focusedOptionId;
- const element = DomHandler.findSingle(this.list, `li[id="${id}"]`);
+ this.$nextTick(() => {
+ const id = index !== -1 ? `${this.id}_${index}` : this.focusedOptionId;
+ const element = DomHandler.findSingle(this.list, `li[id="${id}"]`);
- if (element) {
- element.scrollIntoView && element.scrollIntoView({ block: 'nearest', inline: 'start' });
- } else if (!this.virtualScrollerDisabled) {
- setTimeout(() => {
+ if (element) {
+ element.scrollIntoView && element.scrollIntoView({ block: 'nearest', inline: 'start' });
+ } else if (!this.virtualScrollerDisabled) {
this.virtualScroller && this.virtualScroller.scrollToIndex(index !== -1 ? index : this.focusedOptionIndex);
- }, 0);
- }
+ }
+ });
},
autoUpdateModel() {
if ((this.selectOnFocus || this.autoHighlight) && this.autoOptionFocus && !this.hasSelectedOption) {
diff --git a/components/lib/autocomplete/BaseAutoComplete.vue b/components/lib/autocomplete/BaseAutoComplete.vue
index 779f5bfb9..df9b9e647 100644
--- a/components/lib/autocomplete/BaseAutoComplete.vue
+++ b/components/lib/autocomplete/BaseAutoComplete.vue
@@ -133,6 +133,10 @@ export default {
type: Boolean,
default: false
},
+ focusOnHover: {
+ type: Boolean,
+ default: true
+ },
searchLocale: {
type: String,
default: undefined
diff --git a/components/lib/cascadeselect/BaseCascadeSelect.vue b/components/lib/cascadeselect/BaseCascadeSelect.vue
index 33556e5e7..abd4f72e7 100644
--- a/components/lib/cascadeselect/BaseCascadeSelect.vue
+++ b/components/lib/cascadeselect/BaseCascadeSelect.vue
@@ -72,6 +72,10 @@ export default {
type: Boolean,
default: false
},
+ focusOnHover: {
+ type: Boolean,
+ default: true
+ },
searchLocale: {
type: String,
default: undefined
diff --git a/components/lib/cascadeselect/CascadeSelect.d.ts b/components/lib/cascadeselect/CascadeSelect.d.ts
index a565736a7..c7d1ee9e3 100644
--- a/components/lib/cascadeselect/CascadeSelect.d.ts
+++ b/components/lib/cascadeselect/CascadeSelect.d.ts
@@ -343,6 +343,11 @@ export interface CascadeSelectProps {
* @defaultValue false
*/
selectOnFocus?: boolean | undefined;
+ /**
+ * When enabled, the focus is placed on the hovered option.
+ * @defaultValue true
+ */
+ focusOnHover?: boolean | undefined;
/**
* Locale to use in searching. The default locale is the host environment's current locale.
*/
diff --git a/components/lib/cascadeselect/CascadeSelect.vue b/components/lib/cascadeselect/CascadeSelect.vue
index 998184732..6126c5435 100644
--- a/components/lib/cascadeselect/CascadeSelect.vue
+++ b/components/lib/cascadeselect/CascadeSelect.vue
@@ -62,6 +62,7 @@
:optionGroupLabel="optionGroupLabel"
:optionGroupChildren="optionGroupChildren"
@option-change="onOptionChange"
+ @option-focus-change="onOptionFocusChange"
:pt="pt"
:unstyled="unstyled"
/>
@@ -95,10 +96,10 @@ export default {
overlay: null,
searchTimeout: null,
searchValue: null,
- focusOnHover: false,
data() {
return {
id: this.$attrs.id,
+ clicked: false,
focused: false,
focusedOptionInfo: { index: -1, level: 0, parentKey: '' },
activeOptionPath: [],
@@ -169,9 +170,9 @@ export default {
if (this.hasSelectedOption && ObjectUtils.isNotEmpty(this.activeOptionPath)) {
const processedOption = this.activeOptionPath[this.activeOptionPath.length - 1];
- this.focusedOptionInfo = { index: this.autoOptionFocus ? processedOption.index : -1, level: processedOption.level, parentKey: processedOption.parentKey };
+ this.focusedOptionInfo = { index: processedOption.index, level: processedOption.level, parentKey: processedOption.parentKey };
} else {
- this.focusedOptionInfo = { index: this.autoOptionFocus ? this.findFirstFocusedOptionIndex() : -1, level: 0, parentKey: '' };
+ this.focusedOptionInfo = { index: this.autoOptionFocus ? this.findFirstFocusedOptionIndex() : this.findSelectedOptionIndex(), level: 0, parentKey: '' };
}
isFocus && DomHandler.focus(this.$refs.focusInput);
@@ -180,6 +181,7 @@ export default {
const _hide = () => {
this.$emit('before-hide');
this.overlayVisible = false;
+ this.clicked = false;
this.activeOptionPath = [];
this.focusedOptionInfo = { index: -1, level: 0, parentKey: '' };
@@ -272,6 +274,8 @@ export default {
break;
}
+
+ this.clicked = false;
},
onOptionChange(event) {
const { originalEvent, processedOption, isFocus, isHide } = event;
@@ -285,12 +289,21 @@ export default {
activeOptionPath.push(processedOption);
- this.focusedOptionInfo = originalEvent?.type === 'click' ? { index: -1, level, parentKey } : { index, level, parentKey };
+ this.focusedOptionInfo = { index, level, parentKey };
this.activeOptionPath = activeOptionPath;
grouped ? this.onOptionGroupSelect(originalEvent, processedOption) : this.onOptionSelect(originalEvent, processedOption, isHide);
isFocus && DomHandler.focus(this.$refs.focusInput);
},
+ onOptionFocusChange(event) {
+ if (this.focusOnHover) {
+ const { originalEvent, processedOption } = event;
+ const { index, level, parentKey } = processedOption;
+
+ this.focusedOptionInfo = { index, level, parentKey };
+ this.changeFocusedOptionIndex(originalEvent, index);
+ }
+ },
onOptionSelect(event, processedOption, isHide = true) {
const value = this.getOptionValue(processedOption?.option);
@@ -312,6 +325,7 @@ export default {
DomHandler.focus(this.$refs.focusInput);
}
+ this.clicked = true;
this.$emit('click', event);
},
onOverlayClick(event) {
@@ -331,11 +345,14 @@ export default {
}
},
onArrowDownKey(event) {
- const optionIndex = this.focusedOptionInfo.index !== -1 ? this.findNextOptionIndex(this.focusedOptionInfo.index) : this.findFirstFocusedOptionIndex();
+ if (!this.overlayVisible) {
+ this.show();
+ } else {
+ const optionIndex = this.focusedOptionInfo.index !== -1 ? this.findNextOptionIndex(this.focusedOptionInfo.index) : this.clicked ? this.findFirstOptionIndex() : this.findFirstFocusedOptionIndex();
- this.changeFocusedOptionIndex(event, optionIndex);
+ this.changeFocusedOptionIndex(event, optionIndex);
+ }
- !this.overlayVisible && this.show();
event.preventDefault();
},
onArrowUpKey(event) {
@@ -350,7 +367,7 @@ export default {
this.overlayVisible && this.hide();
event.preventDefault();
} else {
- const optionIndex = this.focusedOptionInfo.index !== -1 ? this.findPrevOptionIndex(this.focusedOptionInfo.index) : this.findLastFocusedOptionIndex();
+ const optionIndex = this.focusedOptionInfo.index !== -1 ? this.findPrevOptionIndex(this.focusedOptionInfo.index) : this.clicked ? this.findLastOptionIndex() : this.findLastFocusedOptionIndex();
this.changeFocusedOptionIndex(event, optionIndex);
@@ -412,6 +429,7 @@ export default {
},
onEnterKey(event) {
if (!this.overlayVisible) {
+ this.focusedOptionInfo.index !== -1; // reset
this.onArrowDownKey(event);
} else {
if (this.focusedOptionInfo.index !== -1) {
@@ -595,23 +613,25 @@ export default {
let optionIndex = -1;
let matched = false;
- if (this.focusedOptionInfo.index !== -1) {
- optionIndex = this.visibleOptions.slice(this.focusedOptionInfo.index).findIndex((processedOption) => this.isOptionMatched(processedOption));
- optionIndex = optionIndex === -1 ? this.visibleOptions.slice(0, this.focusedOptionInfo.index).findIndex((processedOption) => this.isOptionMatched(processedOption)) : optionIndex + this.focusedOptionInfo.index;
- } else {
- optionIndex = this.visibleOptions.findIndex((processedOption) => this.isOptionMatched(processedOption));
- }
+ if (ObjectUtils.isNotEmpty(this.searchValue)) {
+ if (this.focusedOptionInfo.index !== -1) {
+ optionIndex = this.visibleOptions.slice(this.focusedOptionInfo.index).findIndex((processedOption) => this.isOptionMatched(processedOption));
+ optionIndex = optionIndex === -1 ? this.visibleOptions.slice(0, this.focusedOptionInfo.index).findIndex((processedOption) => this.isOptionMatched(processedOption)) : optionIndex + this.focusedOptionInfo.index;
+ } else {
+ optionIndex = this.visibleOptions.findIndex((processedOption) => this.isOptionMatched(processedOption));
+ }
- if (optionIndex !== -1) {
- matched = true;
- }
+ if (optionIndex !== -1) {
+ matched = true;
+ }
- if (optionIndex === -1 && this.focusedOptionInfo.index === -1) {
- optionIndex = this.findFirstFocusedOptionIndex();
- }
+ if (optionIndex === -1 && this.focusedOptionInfo.index === -1) {
+ optionIndex = this.findFirstFocusedOptionIndex();
+ }
- if (optionIndex !== -1) {
- this.changeFocusedOptionIndex(event, optionIndex);
+ if (optionIndex !== -1) {
+ this.changeFocusedOptionIndex(event, optionIndex);
+ }
}
if (this.searchTimeout) {
@@ -636,12 +656,14 @@ export default {
}
},
scrollInView(index = -1) {
- const id = index !== -1 ? `${this.id}_${index}` : this.focusedOptionId;
- const element = DomHandler.findSingle(this.list, `li[id="${id}"]`);
+ this.$nextTick(() => {
+ const id = index !== -1 ? `${this.id}_${index}` : this.focusedOptionId;
+ const element = DomHandler.findSingle(this.list, `li[id="${id}"]`);
- if (element) {
- element.scrollIntoView && element.scrollIntoView({ block: 'nearest', inline: 'start' });
- }
+ if (element) {
+ element.scrollIntoView && element.scrollIntoView({ block: 'nearest', inline: 'start' });
+ }
+ });
},
autoUpdateModel() {
if (this.selectOnFocus && this.autoOptionFocus && !this.hasSelectedOption) {
diff --git a/components/lib/cascadeselect/CascadeSelectSub.vue b/components/lib/cascadeselect/CascadeSelectSub.vue
index c4904d45b..917abb5a1 100644
--- a/components/lib/cascadeselect/CascadeSelectSub.vue
+++ b/components/lib/cascadeselect/CascadeSelectSub.vue
@@ -17,7 +17,7 @@
:data-p-focus="isOptionFocused(processedOption)"
:data-p-disabled="isOptionDisabled(processedOption)"
>
-
+
{{ getOptionLabelToRender(processedOption) }}
@@ -43,6 +43,7 @@
:optionGroupLabel="optionGroupLabel"
:optionGroupChildren="optionGroupChildren"
@option-change="onOptionChange"
+ @option-focus-change="onOptionFocusChange"
:pt="pt"
:unstyled="unstyled"
:isParentMount="mounted"
@@ -62,7 +63,7 @@ export default {
name: 'CascadeSelectSub',
hostName: 'CascadeSelect',
extends: BaseComponent,
- emits: ['option-change'],
+ emits: ['option-change', 'option-focus-change'],
container: null,
props: {
selectId: String,
@@ -149,9 +150,15 @@ export default {
onOptionClick(event, processedOption) {
this.$emit('option-change', { originalEvent: event, processedOption, isFocus: true });
},
+ onOptionMouseMove(event, processedOption) {
+ this.$emit('option-focus-change', { originalEvent: event, processedOption });
+ },
onOptionChange(event) {
this.$emit('option-change', event);
},
+ onOptionFocusChange(event) {
+ this.$emit('option-focus-change', event);
+ },
containerRef(el) {
this.container = el;
}
diff --git a/components/lib/dropdown/BaseDropdown.vue b/components/lib/dropdown/BaseDropdown.vue
index 29738e548..f14ce08a7 100644
--- a/components/lib/dropdown/BaseDropdown.vue
+++ b/components/lib/dropdown/BaseDropdown.vue
@@ -126,6 +126,10 @@ export default {
type: Boolean,
default: false
},
+ focusOnHover: {
+ type: Boolean,
+ default: true
+ },
filterMessage: {
type: String,
default: null
diff --git a/components/lib/dropdown/Dropdown.d.ts b/components/lib/dropdown/Dropdown.d.ts
index a214573b9..3bb49f28d 100755
--- a/components/lib/dropdown/Dropdown.d.ts
+++ b/components/lib/dropdown/Dropdown.d.ts
@@ -413,6 +413,11 @@ export interface DropdownProps {
* @defaultValue false
*/
selectOnFocus?: boolean | undefined;
+ /**
+ * When enabled, the focus is placed on the hovered option.
+ * @defaultValue true
+ */
+ focusOnHover?: boolean | undefined;
/**
* Text to be displayed in hidden accessible field when filtering returns any results. Defaults to value from PrimeVue locale configuration.
* @defaultValue '{0} results are available'
diff --git a/components/lib/dropdown/Dropdown.vue b/components/lib/dropdown/Dropdown.vue
index 451c2884f..8ac476725 100755
--- a/components/lib/dropdown/Dropdown.vue
+++ b/components/lib/dropdown/Dropdown.vue
@@ -194,10 +194,10 @@ export default {
searchTimeout: null,
searchValue: null,
isModelValueChanged: false,
- focusOnHover: false,
data() {
return {
id: this.$attrs.id,
+ clicked: false,
focused: false,
focusedOptionIndex: -1,
filterValue: null,
@@ -284,7 +284,7 @@ export default {
show(isFocus) {
this.$emit('before-show');
this.overlayVisible = true;
- this.focusedOptionIndex = this.focusedOptionIndex !== -1 ? this.focusedOptionIndex : this.autoOptionFocus ? this.findFirstFocusedOptionIndex() : -1;
+ this.focusedOptionIndex = this.focusedOptionIndex !== -1 ? this.focusedOptionIndex : this.autoOptionFocus ? this.findFirstFocusedOptionIndex() : this.editable ? -1 : this.findSelectedOptionIndex();
isFocus && DomHandler.focus(this.$refs.focusInput);
},
@@ -292,6 +292,7 @@ export default {
const _hide = () => {
this.$emit('before-hide');
this.overlayVisible = false;
+ this.clicked = false;
this.focusedOptionIndex = -1;
this.searchValue = '';
@@ -310,8 +311,12 @@ export default {
}
this.focused = true;
- this.focusedOptionIndex = this.focusedOptionIndex !== -1 ? this.focusedOptionIndex : this.overlayVisible && this.autoOptionFocus ? this.findFirstFocusedOptionIndex() : -1;
- this.overlayVisible && this.scrollInView(this.focusedOptionIndex);
+
+ if (this.overlayVisible) {
+ this.focusedOptionIndex = this.focusedOptionIndex !== -1 ? this.focusedOptionIndex : this.autoOptionFocus ? this.findFirstFocusedOptionIndex() : this.editable ? -1 : this.findSelectedOptionIndex();
+ this.scrollInView(this.focusedOptionIndex);
+ }
+
this.$emit('focus', event);
},
onBlur(event) {
@@ -396,6 +401,8 @@ export default {
break;
}
+
+ this.clicked = false;
},
onEditableInput(event) {
const value = event.target.value;
@@ -419,6 +426,8 @@ export default {
} else if (!this.overlay || !this.overlay.contains(event.target)) {
this.overlayVisible ? this.hide(true) : this.show(true);
}
+
+ this.clicked = true;
},
onClearClick(event) {
this.updateModel(event, null);
@@ -525,11 +534,15 @@ export default {
}
},
onArrowDownKey(event) {
- const optionIndex = this.focusedOptionIndex !== -1 ? this.findNextOptionIndex(this.focusedOptionIndex) : this.findFirstFocusedOptionIndex();
+ if (!this.overlayVisible) {
+ this.show();
+ this.editable && this.changeFocusedOptionIndex(event, this.findSelectedOptionIndex());
+ } else {
+ const optionIndex = this.focusedOptionIndex !== -1 ? this.findNextOptionIndex(this.focusedOptionIndex) : this.clicked ? this.findFirstOptionIndex() : this.findFirstFocusedOptionIndex();
- this.changeFocusedOptionIndex(event, optionIndex);
+ this.changeFocusedOptionIndex(event, optionIndex);
+ }
- !this.overlayVisible && this.show();
event.preventDefault();
},
onArrowUpKey(event, pressedInInputText = false) {
@@ -541,7 +554,7 @@ export default {
this.overlayVisible && this.hide();
event.preventDefault();
} else {
- const optionIndex = this.focusedOptionIndex !== -1 ? this.findPrevOptionIndex(this.focusedOptionIndex) : this.findLastFocusedOptionIndex();
+ const optionIndex = this.focusedOptionIndex !== -1 ? this.findPrevOptionIndex(this.focusedOptionIndex) : this.clicked ? this.findLastOptionIndex() : this.findLastFocusedOptionIndex();
this.changeFocusedOptionIndex(event, optionIndex);
@@ -589,6 +602,7 @@ export default {
},
onEnterKey(event) {
if (!this.overlayVisible) {
+ this.focusedOptionIndex = -1; // reset
this.onArrowDownKey(event);
} else {
if (this.focusedOptionIndex !== -1) {
@@ -784,23 +798,25 @@ export default {
let optionIndex = -1;
let matched = false;
- if (this.focusedOptionIndex !== -1) {
- optionIndex = this.visibleOptions.slice(this.focusedOptionIndex).findIndex((option) => this.isOptionMatched(option));
- optionIndex = optionIndex === -1 ? this.visibleOptions.slice(0, this.focusedOptionIndex).findIndex((option) => this.isOptionMatched(option)) : optionIndex + this.focusedOptionIndex;
- } else {
- optionIndex = this.visibleOptions.findIndex((option) => this.isOptionMatched(option));
- }
+ if (ObjectUtils.isNotEmpty(this.searchValue)) {
+ if (this.focusedOptionIndex !== -1) {
+ optionIndex = this.visibleOptions.slice(this.focusedOptionIndex).findIndex((option) => this.isOptionMatched(option));
+ optionIndex = optionIndex === -1 ? this.visibleOptions.slice(0, this.focusedOptionIndex).findIndex((option) => this.isOptionMatched(option)) : optionIndex + this.focusedOptionIndex;
+ } else {
+ optionIndex = this.visibleOptions.findIndex((option) => this.isOptionMatched(option));
+ }
- if (optionIndex !== -1) {
- matched = true;
- }
+ if (optionIndex !== -1) {
+ matched = true;
+ }
- if (optionIndex === -1 && this.focusedOptionIndex === -1) {
- optionIndex = this.findFirstFocusedOptionIndex();
- }
+ if (optionIndex === -1 && this.focusedOptionIndex === -1) {
+ optionIndex = this.findFirstFocusedOptionIndex();
+ }
- if (optionIndex !== -1) {
- this.changeFocusedOptionIndex(event, optionIndex);
+ if (optionIndex !== -1) {
+ this.changeFocusedOptionIndex(event, optionIndex);
+ }
}
if (this.searchTimeout) {
@@ -825,16 +841,16 @@ export default {
}
},
scrollInView(index = -1) {
- const id = index !== -1 ? `${this.id}_${index}` : this.focusedOptionId;
- const element = DomHandler.findSingle(this.list, `li[id="${id}"]`);
+ this.$nextTick(() => {
+ const id = index !== -1 ? `${this.id}_${index}` : this.focusedOptionId;
+ const element = DomHandler.findSingle(this.list, `li[id="${id}"]`);
- if (element) {
- element.scrollIntoView && element.scrollIntoView({ block: 'nearest', inline: 'start' });
- } else if (!this.virtualScrollerDisabled) {
- setTimeout(() => {
+ if (element) {
+ element.scrollIntoView && element.scrollIntoView({ block: 'nearest', inline: 'start' });
+ } else if (!this.virtualScrollerDisabled) {
this.virtualScroller && this.virtualScroller.scrollToIndex(index !== -1 ? index : this.focusedOptionIndex);
- }, 0);
- }
+ }
+ });
},
autoUpdateModel() {
if (this.selectOnFocus && this.autoOptionFocus && !this.hasSelectedOption) {
diff --git a/components/lib/listbox/BaseListbox.vue b/components/lib/listbox/BaseListbox.vue
index 67e022626..65767edb2 100644
--- a/components/lib/listbox/BaseListbox.vue
+++ b/components/lib/listbox/BaseListbox.vue
@@ -51,6 +51,10 @@ export default {
type: Boolean,
default: false
},
+ focusOnHover: {
+ type: Boolean,
+ default: true
+ },
filterMessage: {
type: String,
default: null
diff --git a/components/lib/listbox/Listbox.d.ts b/components/lib/listbox/Listbox.d.ts
index a3eed3b0a..cfefd63f3 100755
--- a/components/lib/listbox/Listbox.d.ts
+++ b/components/lib/listbox/Listbox.d.ts
@@ -301,6 +301,11 @@ export interface ListboxProps {
* @defaultValue false
*/
selectOnFocus?: boolean | undefined;
+ /**
+ * When enabled, the focus is placed on the hovered option.
+ * @defaultValue true
+ */
+ focusOnHover?: boolean | undefined;
/**
* Text to be displayed in hidden accessible field when filtering returns any results. Defaults to value from PrimeVue locale configuration.
* @defaultValue '{0} results are available'
diff --git a/components/lib/listbox/Listbox.vue b/components/lib/listbox/Listbox.vue
index 036039c0a..ee62c366b 100755
--- a/components/lib/listbox/Listbox.vue
+++ b/components/lib/listbox/Listbox.vue
@@ -139,7 +139,6 @@ export default {
startRangeIndex: -1,
searchTimeout: null,
searchValue: '',
- focusOnHover: false,
data() {
return {
id: this.$attrs.id,
@@ -228,7 +227,7 @@ export default {
},
onListFocus(event) {
this.focused = true;
- this.focusedOptionIndex = this.focusedOptionIndex !== -1 ? this.focusedOptionIndex : this.autoOptionFocus ? this.findFirstFocusedOptionIndex() : -1;
+ this.focusedOptionIndex = this.focusedOptionIndex !== -1 ? this.focusedOptionIndex : this.autoOptionFocus ? this.findFirstFocusedOptionIndex() : this.findSelectedOptionIndex();
this.autoUpdateModel();
this.$emit('focus', event);
},
@@ -306,13 +305,13 @@ export default {
this.multiple ? this.onOptionSelectMultiple(event, option) : this.onOptionSelectSingle(event, option);
this.optionTouched = false;
- index !== -1 && (this.focusedOptionIndex = event?.type === 'click' ? -1 : index);
+ index !== -1 && (this.focusedOptionIndex = index);
},
onOptionMouseDown(event, index) {
this.changeFocusedOptionIndex(event, index);
},
onOptionMouseMove(event, index) {
- if (this.focusOnHover) {
+ if (this.focusOnHover && this.focused) {
this.changeFocusedOptionIndex(event, index);
}
},
@@ -520,11 +519,14 @@ export default {
isValidSelectedOption(option) {
return this.isValidOption(option) && this.isSelected(option);
},
+ isEquals(value1, value2) {
+ return ObjectUtils.equals(value1, value2, this.equalityKey);
+ },
isSelected(option) {
const optionValue = this.getOptionValue(option);
- if (this.multiple) return (this.modelValue || []).some((value) => ObjectUtils.equals(value, optionValue, this.equalityKey));
- else return ObjectUtils.equals(this.modelValue, optionValue, this.equalityKey);
+ if (this.multiple) return (this.modelValue || []).some((value) => this.isEquals(value, optionValue));
+ else return this.isEquals(this.modelValue, optionValue);
},
findFirstOptionIndex() {
return this.visibleOptions.findIndex((option) => this.isValidOption(option));
@@ -542,6 +544,22 @@ export default {
return matchedOptionIndex > -1 ? matchedOptionIndex : index;
},
+ findSelectedOptionIndex() {
+ if (this.hasSelectedOption) {
+ if (this.multiple) {
+ for (let index = this.modelValue.length - 1; index >= 0; index--) {
+ const value = this.modelValue[index];
+ const matchedOptionIndex = this.visibleOptions.findIndex((option) => this.isValidSelectedOption(option) && this.isEquals(value, this.getOptionValue(option)));
+
+ if (matchedOptionIndex > -1) return matchedOptionIndex;
+ }
+ } else {
+ return this.visibleOptions.findIndex((option) => this.isValidSelectedOption(option));
+ }
+ }
+
+ return -1;
+ },
findFirstSelectedOptionIndex() {
return this.hasSelectedOption ? this.visibleOptions.findIndex((option) => this.isValidSelectedOption(option)) : -1;
},
@@ -588,19 +606,21 @@ export default {
let optionIndex = -1;
- if (this.focusedOptionIndex !== -1) {
- optionIndex = this.visibleOptions.slice(this.focusedOptionIndex).findIndex((option) => this.isOptionMatched(option));
- optionIndex = optionIndex === -1 ? this.visibleOptions.slice(0, this.focusedOptionIndex).findIndex((option) => this.isOptionMatched(option)) : optionIndex + this.focusedOptionIndex;
- } else {
- optionIndex = this.visibleOptions.findIndex((option) => this.isOptionMatched(option));
- }
+ if (ObjectUtils.isNotEmpty(this.searchValue)) {
+ if (this.focusedOptionIndex !== -1) {
+ optionIndex = this.visibleOptions.slice(this.focusedOptionIndex).findIndex((option) => this.isOptionMatched(option));
+ optionIndex = optionIndex === -1 ? this.visibleOptions.slice(0, this.focusedOptionIndex).findIndex((option) => this.isOptionMatched(option)) : optionIndex + this.focusedOptionIndex;
+ } else {
+ optionIndex = this.visibleOptions.findIndex((option) => this.isOptionMatched(option));
+ }
- if (optionIndex === -1 && this.focusedOptionIndex === -1) {
- optionIndex = this.findFirstFocusedOptionIndex();
- }
+ if (optionIndex === -1 && this.focusedOptionIndex === -1) {
+ optionIndex = this.findFirstFocusedOptionIndex();
+ }
- if (optionIndex !== -1) {
- this.changeFocusedOptionIndex(event, optionIndex);
+ if (optionIndex !== -1) {
+ this.changeFocusedOptionIndex(event, optionIndex);
+ }
}
if (this.searchTimeout) {
@@ -626,14 +646,16 @@ export default {
}
},
scrollInView(index = -1) {
- const id = index !== -1 ? `${this.id}_${index}` : this.focusedOptionId;
- const element = DomHandler.findSingle(this.list, `li[id="${id}"]`);
+ this.$nextTick(() => {
+ const id = index !== -1 ? `${this.id}_${index}` : this.focusedOptionId;
+ const element = DomHandler.findSingle(this.list, `li[id="${id}"]`);
- if (element) {
- element.scrollIntoView && element.scrollIntoView({ block: 'nearest', inline: 'nearest' });
- } else if (!this.virtualScrollerDisabled) {
- this.virtualScroller && this.virtualScroller.scrollToIndex(index !== -1 ? index : this.focusedOptionIndex);
- }
+ if (element) {
+ element.scrollIntoView && element.scrollIntoView({ block: 'nearest', inline: 'nearest' });
+ } else if (!this.virtualScrollerDisabled) {
+ this.virtualScroller && this.virtualScroller.scrollToIndex(index !== -1 ? index : this.focusedOptionIndex);
+ }
+ });
},
autoUpdateModel() {
if (this.selectOnFocus && this.autoOptionFocus && !this.hasSelectedOption && !this.multiple && this.focused) {
diff --git a/components/lib/multiselect/BaseMultiSelect.vue b/components/lib/multiselect/BaseMultiSelect.vue
index cb1495937..d7d2928d7 100644
--- a/components/lib/multiselect/BaseMultiSelect.vue
+++ b/components/lib/multiselect/BaseMultiSelect.vue
@@ -131,6 +131,10 @@ export default {
type: Boolean,
default: false
},
+ focusOnHover: {
+ type: Boolean,
+ default: true
+ },
highlightOnSelect: {
type: Boolean,
default: false
diff --git a/components/lib/multiselect/MultiSelect.d.ts b/components/lib/multiselect/MultiSelect.d.ts
index a1eeb92be..b9345c3ed 100755
--- a/components/lib/multiselect/MultiSelect.d.ts
+++ b/components/lib/multiselect/MultiSelect.d.ts
@@ -478,6 +478,11 @@ export interface MultiSelectProps {
* @defaultValue false
*/
autoFilterFocus?: boolean | undefined;
+ /**
+ * When enabled, the focus is placed on the hovered option.
+ * @defaultValue true
+ */
+ focusOnHover?: boolean | undefined;
/**
* Highlights automatically the first item.
* @defaultValue false
diff --git a/components/lib/multiselect/MultiSelect.vue b/components/lib/multiselect/MultiSelect.vue
index 9bd69632b..e93db6d63 100755
--- a/components/lib/multiselect/MultiSelect.vue
+++ b/components/lib/multiselect/MultiSelect.vue
@@ -223,10 +223,10 @@ export default {
searchTimeout: null,
searchValue: '',
selectOnFocus: false,
- focusOnHover: false,
data() {
return {
id: this.$attrs.id,
+ clicked: false,
focused: false,
focusedOptionIndex: -1,
filterValue: null,
@@ -312,7 +312,7 @@ export default {
show(isFocus) {
this.$emit('before-show');
this.overlayVisible = true;
- this.focusedOptionIndex = this.focusedOptionIndex !== -1 ? this.focusedOptionIndex : this.autoOptionFocus ? this.findFirstFocusedOptionIndex() : -1;
+ this.focusedOptionIndex = this.focusedOptionIndex !== -1 ? this.focusedOptionIndex : this.autoOptionFocus ? this.findFirstFocusedOptionIndex() : this.findSelectedOptionIndex();
isFocus && DomHandler.focus(this.$refs.focusInput);
},
@@ -320,6 +320,7 @@ export default {
const _hide = () => {
this.$emit('before-hide');
this.overlayVisible = false;
+ this.clicked = false;
this.focusedOptionIndex = -1;
this.searchValue = '';
@@ -338,11 +339,16 @@ export default {
}
this.focused = true;
- this.focusedOptionIndex = this.focusedOptionIndex !== -1 ? this.focusedOptionIndex : this.overlayVisible && this.autoOptionFocus ? this.findFirstFocusedOptionIndex() : -1;
- this.overlayVisible && this.scrollInView(this.focusedOptionIndex);
+
+ if (this.overlayVisible) {
+ this.focusedOptionIndex = this.focusedOptionIndex !== -1 ? this.focusedOptionIndex : this.autoOptionFocus ? this.findFirstFocusedOptionIndex() : this.findSelectedOptionIndex();
+ this.scrollInView(this.focusedOptionIndex);
+ }
+
this.$emit('focus', event);
},
onBlur(event) {
+ this.clicked = false;
this.focused = false;
this.focusedOptionIndex = -1;
this.searchValue = '';
@@ -419,6 +425,8 @@ export default {
break;
}
+
+ this.clicked = false;
},
onContainerClick(event) {
if (this.disabled || this.loading) {
@@ -428,6 +436,8 @@ export default {
if (!this.overlay || !this.overlay.contains(event.target)) {
this.overlayVisible ? this.hide(true) : this.show(true);
}
+
+ this.clicked = true;
},
onFirstHiddenFocus(event) {
const focusableEl = event.relatedTarget === this.$refs.focusInput ? DomHandler.getFirstFocusableElement(this.overlay, ':not([data-p-hidden-focusable="true"])') : this.$refs.focusInput;
@@ -454,7 +464,7 @@ export default {
else value = [...(this.modelValue || []), this.getOptionValue(option)];
this.updateModel(event, value);
- index !== -1 && (this.focusedOptionIndex = event?.type === 'click' ? -1 : index);
+ index !== -1 && (this.focusedOptionIndex = index);
isFocus && DomHandler.focus(this.$refs.focusInput);
},
onOptionMouseMove(event, index) {
@@ -551,15 +561,18 @@ export default {
}
},
onArrowDownKey(event) {
- const optionIndex = this.focusedOptionIndex !== -1 ? this.findNextOptionIndex(this.focusedOptionIndex) : this.findFirstFocusedOptionIndex();
+ if (!this.overlayVisible) {
+ this.show();
+ } else {
+ const optionIndex = this.focusedOptionIndex !== -1 ? this.findNextOptionIndex(this.focusedOptionIndex) : this.clicked ? this.findFirstOptionIndex() : this.findFirstFocusedOptionIndex();
- if (event.shiftKey) {
- this.onOptionSelectRange(event, this.startRangeIndex, optionIndex);
+ if (event.shiftKey) {
+ this.onOptionSelectRange(event, this.startRangeIndex, optionIndex);
+ }
+
+ this.changeFocusedOptionIndex(event, optionIndex);
}
- this.changeFocusedOptionIndex(event, optionIndex);
-
- !this.overlayVisible && this.show();
event.preventDefault();
},
onArrowUpKey(event, pressedInInputText = false) {
@@ -571,7 +584,7 @@ export default {
this.overlayVisible && this.hide();
event.preventDefault();
} else {
- const optionIndex = this.focusedOptionIndex !== -1 ? this.findPrevOptionIndex(this.focusedOptionIndex) : this.findLastFocusedOptionIndex();
+ const optionIndex = this.focusedOptionIndex !== -1 ? this.findPrevOptionIndex(this.focusedOptionIndex) : this.clicked ? this.findLastOptionIndex() : this.findLastFocusedOptionIndex();
if (event.shiftKey) {
this.onOptionSelectRange(event, optionIndex, this.startRangeIndex);
@@ -642,6 +655,7 @@ export default {
},
onEnterKey(event) {
if (!this.overlayVisible) {
+ this.focusedOptionIndex = -1; // reset
this.onArrowDownKey(event);
} else {
if (this.focusedOptionIndex !== -1) {
@@ -807,10 +821,13 @@ export default {
isValidSelectedOption(option) {
return this.isValidOption(option) && this.isSelected(option);
},
+ isEquals(value1, value2) {
+ return ObjectUtils.equals(value1, value2, this.equalityKey);
+ },
isSelected(option) {
const optionValue = this.getOptionValue(option);
- return (this.modelValue || []).some((value) => ObjectUtils.equals(value, optionValue, this.equalityKey));
+ return (this.modelValue || []).some((value) => this.isEquals(value, optionValue));
},
findFirstOptionIndex() {
return this.visibleOptions.findIndex((option) => this.isValidOption(option));
@@ -828,6 +845,18 @@ export default {
return matchedOptionIndex > -1 ? matchedOptionIndex : index;
},
+ findSelectedOptionIndex() {
+ if (this.hasSelectedOption) {
+ for (let index = this.modelValue.length - 1; index >= 0; index--) {
+ const value = this.modelValue[index];
+ const matchedOptionIndex = this.visibleOptions.findIndex((option) => this.isValidSelectedOption(option) && this.isEquals(value, this.getOptionValue(option)));
+
+ if (matchedOptionIndex > -1) return matchedOptionIndex;
+ }
+ }
+
+ return -1;
+ },
findFirstSelectedOptionIndex() {
return this.hasSelectedOption ? this.visibleOptions.findIndex((option) => this.isValidSelectedOption(option)) : -1;
},
@@ -860,12 +889,12 @@ export default {
return matchedOptionIndex > -1 ? matchedOptionIndex : index;
},
findFirstFocusedOptionIndex() {
- const selectedIndex = this.findFirstSelectedOptionIndex();
+ const selectedIndex = this.findSelectedOptionIndex();
return selectedIndex < 0 ? this.findFirstOptionIndex() : selectedIndex;
},
findLastFocusedOptionIndex() {
- const selectedIndex = this.findLastSelectedOptionIndex();
+ const selectedIndex = this.findSelectedOptionIndex();
return selectedIndex < 0 ? this.findLastOptionIndex() : selectedIndex;
},
@@ -874,19 +903,21 @@ export default {
let optionIndex = -1;
- if (this.focusedOptionIndex !== -1) {
- optionIndex = this.visibleOptions.slice(this.focusedOptionIndex).findIndex((option) => this.isOptionMatched(option));
- optionIndex = optionIndex === -1 ? this.visibleOptions.slice(0, this.focusedOptionIndex).findIndex((option) => this.isOptionMatched(option)) : optionIndex + this.focusedOptionIndex;
- } else {
- optionIndex = this.visibleOptions.findIndex((option) => this.isOptionMatched(option));
- }
+ if (ObjectUtils.isNotEmpty(this.searchValue)) {
+ if (this.focusedOptionIndex !== -1) {
+ optionIndex = this.visibleOptions.slice(this.focusedOptionIndex).findIndex((option) => this.isOptionMatched(option));
+ optionIndex = optionIndex === -1 ? this.visibleOptions.slice(0, this.focusedOptionIndex).findIndex((option) => this.isOptionMatched(option)) : optionIndex + this.focusedOptionIndex;
+ } else {
+ optionIndex = this.visibleOptions.findIndex((option) => this.isOptionMatched(option));
+ }
- if (optionIndex === -1 && this.focusedOptionIndex === -1) {
- optionIndex = this.findFirstFocusedOptionIndex();
- }
+ if (optionIndex === -1 && this.focusedOptionIndex === -1) {
+ optionIndex = this.findFirstFocusedOptionIndex();
+ }
- if (optionIndex !== -1) {
- this.changeFocusedOptionIndex(event, optionIndex);
+ if (optionIndex !== -1) {
+ this.changeFocusedOptionIndex(event, optionIndex);
+ }
}
if (this.searchTimeout) {
@@ -902,17 +933,23 @@ export default {
if (this.focusedOptionIndex !== index) {
this.focusedOptionIndex = index;
this.scrollInView();
+
+ if (this.selectOnFocus) {
+ this.onOptionSelect(event, this.visibleOptions[index]);
+ }
}
},
scrollInView(index = -1) {
- const id = index !== -1 ? `${this.id}_${index}` : this.focusedOptionId;
- const element = DomHandler.findSingle(this.list, `li[id="${id}"]`);
+ this.$nextTick(() => {
+ const id = index !== -1 ? `${this.id}_${index}` : this.focusedOptionId;
+ const element = DomHandler.findSingle(this.list, `li[id="${id}"]`);
- if (element) {
- element.scrollIntoView && element.scrollIntoView({ block: 'nearest', inline: 'nearest' });
- } else if (!this.virtualScrollerDisabled) {
- this.virtualScroller && this.virtualScroller.scrollToIndex(index !== -1 ? index : this.focusedOptionIndex);
- }
+ if (element) {
+ element.scrollIntoView && element.scrollIntoView({ block: 'nearest', inline: 'nearest' });
+ } else if (!this.virtualScrollerDisabled) {
+ this.virtualScroller && this.virtualScroller.scrollToIndex(index !== -1 ? index : this.focusedOptionIndex);
+ }
+ });
},
autoUpdateModel() {
if (this.selectOnFocus && this.autoOptionFocus && !this.hasSelectedOption) {
diff --git a/components/lib/virtualscroller/VirtualScroller.vue b/components/lib/virtualscroller/VirtualScroller.vue
index faa506f74..9c1110e73 100644
--- a/components/lib/virtualscroller/VirtualScroller.vue
+++ b/components/lib/virtualscroller/VirtualScroller.vue
@@ -161,64 +161,72 @@ export default {
scrollToIndex(index, behavior = 'auto') {
const both = this.isBoth();
const horizontal = this.isHorizontal();
- const first = this.first;
- const { numToleratedItems } = this.calculateNumItems();
- const contentPos = this.getContentPosition();
- const itemSize = this.itemSize;
- const calculateFirst = (_index = 0, _numT) => (_index <= _numT ? 0 : _index);
- const calculateCoord = (_first, _size, _cpos) => _first * _size + _cpos;
- const scrollTo = (left = 0, top = 0) => this.scrollTo({ left, top, behavior });
- let newFirst = both ? { rows: 0, cols: 0 } : 0;
- let isRangeChanged = false;
+ const valid = both ? index.every((i) => i > -1) : index > -1;
- if (both) {
- newFirst = { rows: calculateFirst(index[0], numToleratedItems[0]), cols: calculateFirst(index[1], numToleratedItems[1]) };
- scrollTo(calculateCoord(newFirst.cols, itemSize[1], contentPos.left), calculateCoord(newFirst.rows, itemSize[0], contentPos.top));
- isRangeChanged = newFirst.rows !== first.rows || newFirst.cols !== first.cols;
- } else {
- newFirst = calculateFirst(index, numToleratedItems);
- horizontal ? scrollTo(calculateCoord(newFirst, itemSize, contentPos.left), 0) : scrollTo(0, calculateCoord(newFirst, itemSize, contentPos.top));
- isRangeChanged = newFirst !== first;
+ if (valid) {
+ const first = this.first;
+ const { numToleratedItems } = this.calculateNumItems();
+ const contentPos = this.getContentPosition();
+ const itemSize = this.itemSize;
+ const calculateFirst = (_index = 0, _numT) => (_index <= _numT ? 0 : _index);
+ const calculateCoord = (_first, _size, _cpos) => _first * _size + _cpos;
+ const scrollTo = (left = 0, top = 0) => this.scrollTo({ left, top, behavior });
+ let newFirst = both ? { rows: 0, cols: 0 } : 0;
+ let isRangeChanged = false;
+
+ if (both) {
+ newFirst = { rows: calculateFirst(index[0], numToleratedItems[0]), cols: calculateFirst(index[1], numToleratedItems[1]) };
+ scrollTo(calculateCoord(newFirst.cols, itemSize[1], contentPos.left), calculateCoord(newFirst.rows, itemSize[0], contentPos.top));
+ isRangeChanged = newFirst.rows !== first.rows || newFirst.cols !== first.cols;
+ } else {
+ newFirst = calculateFirst(index, numToleratedItems);
+ horizontal ? scrollTo(calculateCoord(newFirst, itemSize, contentPos.left), 0) : scrollTo(0, calculateCoord(newFirst, itemSize, contentPos.top));
+ isRangeChanged = newFirst !== first;
+ }
+
+ this.isRangeChanged = isRangeChanged;
+ this.first = newFirst;
}
-
- this.isRangeChanged = isRangeChanged;
- this.first = newFirst;
},
scrollInView(index, to, behavior = 'auto') {
if (to) {
const both = this.isBoth();
const horizontal = this.isHorizontal();
- const { first, viewport } = this.getRenderedRange();
- const scrollTo = (left = 0, top = 0) => this.scrollTo({ left, top, behavior });
- const isToStart = to === 'to-start';
- const isToEnd = to === 'to-end';
+ const valid = both ? index.every((i) => i > -1) : index > -1;
- if (isToStart) {
- if (both) {
- if (viewport.first.rows - first.rows > index[0]) {
- scrollTo(viewport.first.cols * this.itemSize[1], (viewport.first.rows - 1) * this.itemSize[0]);
- } else if (viewport.first.cols - first.cols > index[1]) {
- scrollTo((viewport.first.cols - 1) * this.itemSize[1], viewport.first.rows * this.itemSize[0]);
- }
- } else {
- if (viewport.first - first > index) {
- const pos = (viewport.first - 1) * this.itemSize;
+ if (valid) {
+ const { first, viewport } = this.getRenderedRange();
+ const scrollTo = (left = 0, top = 0) => this.scrollTo({ left, top, behavior });
+ const isToStart = to === 'to-start';
+ const isToEnd = to === 'to-end';
- horizontal ? scrollTo(pos, 0) : scrollTo(0, pos);
- }
- }
- } else if (isToEnd) {
- if (both) {
- if (viewport.last.rows - first.rows <= index[0] + 1) {
- scrollTo(viewport.first.cols * this.itemSize[1], (viewport.first.rows + 1) * this.itemSize[0]);
- } else if (viewport.last.cols - first.cols <= index[1] + 1) {
- scrollTo((viewport.first.cols + 1) * this.itemSize[1], viewport.first.rows * this.itemSize[0]);
- }
- } else {
- if (viewport.last - first <= index + 1) {
- const pos = (viewport.first + 1) * this.itemSize;
+ if (isToStart) {
+ if (both) {
+ if (viewport.first.rows - first.rows > index[0]) {
+ scrollTo(viewport.first.cols * this.itemSize[1], (viewport.first.rows - 1) * this.itemSize[0]);
+ } else if (viewport.first.cols - first.cols > index[1]) {
+ scrollTo((viewport.first.cols - 1) * this.itemSize[1], viewport.first.rows * this.itemSize[0]);
+ }
+ } else {
+ if (viewport.first - first > index) {
+ const pos = (viewport.first - 1) * this.itemSize;
- horizontal ? scrollTo(pos, 0) : scrollTo(0, pos);
+ horizontal ? scrollTo(pos, 0) : scrollTo(0, pos);
+ }
+ }
+ } else if (isToEnd) {
+ if (both) {
+ if (viewport.last.rows - first.rows <= index[0] + 1) {
+ scrollTo(viewport.first.cols * this.itemSize[1], (viewport.first.rows + 1) * this.itemSize[0]);
+ } else if (viewport.last.cols - first.cols <= index[1] + 1) {
+ scrollTo((viewport.first.cols + 1) * this.itemSize[1], viewport.first.rows * this.itemSize[0]);
+ }
+ } else {
+ if (viewport.last - first <= index + 1) {
+ const pos = (viewport.first + 1) * this.itemSize;
+
+ horizontal ? scrollTo(pos, 0) : scrollTo(0, pos);
+ }
}
}
}