diff --git a/packages/primevue/src/cascadeselect/BaseCascadeSelect.vue b/packages/primevue/src/cascadeselect/BaseCascadeSelect.vue index 1cf8e5cb6..3031c819d 100644 --- a/packages/primevue/src/cascadeselect/BaseCascadeSelect.vue +++ b/packages/primevue/src/cascadeselect/BaseCascadeSelect.vue @@ -14,6 +14,10 @@ export default { optionGroupLabel: null, optionGroupChildren: null, placeholder: String, + breakpoint: { + type: String, + default: '960px' + }, variant: { type: String, default: null diff --git a/packages/primevue/src/cascadeselect/CascadeSelect.vue b/packages/primevue/src/cascadeselect/CascadeSelect.vue index 1a4b0e280..ca2065ce1 100644 --- a/packages/primevue/src/cascadeselect/CascadeSelect.vue +++ b/packages/primevue/src/cascadeselect/CascadeSelect.vue @@ -109,6 +109,7 @@ export default { $pcFluid: { default: null } }, outsideClickListener: null, + matchMediaListener: null, scrollHandler: null, resizeListener: null, overlay: null, @@ -122,7 +123,10 @@ export default { focusedOptionInfo: { index: -1, level: 0, parentKey: '' }, activeOptionPath: [], overlayVisible: false, - dirty: false + dirty: false, + mobileActive: false, + query: null, + queryMatches: false }; }, watch: { @@ -136,10 +140,12 @@ export default { mounted() { this.id = this.id || UniqueComponentId(); this.autoUpdateModel(); + this.bindMatchMediaListener(); }, beforeUnmount() { this.unbindOutsideClickListener(); this.unbindResizeListener(); + this.unbindMatchMediaListener(); if (this.scrollHandler) { this.scrollHandler.destroy(); @@ -150,6 +156,10 @@ export default { ZIndex.clear(this.overlay); this.overlay = null; } + + if (this.mobileActive) { + this.mobileActive = false; + } }, methods: { getOptionLabel(option) { @@ -571,6 +581,27 @@ export default { this.resizeListener = null; } }, + bindMatchMediaListener() { + if (!this.matchMediaListener) { + const query = matchMedia(`(max-width: ${this.breakpoint})`); + + this.query = query; + this.queryMatches = query.matches; + + this.matchMediaListener = () => { + this.queryMatches = query.matches; + this.mobileActive = false; + }; + + this.query.addEventListener('change', this.matchMediaListener); + } + }, + unbindMatchMediaListener() { + if (this.matchMediaListener) { + this.query.removeEventListener('change', this.matchMediaListener); + this.matchMediaListener = null; + } + }, isOptionMatched(processedOption) { return this.isValidOption(processedOption) && this.getProccessedOptionLabel(processedOption)?.toLocaleLowerCase(this.searchLocale).startsWith(this.searchValue.toLocaleLowerCase(this.searchLocale)); }, diff --git a/packages/primevue/src/cascadeselect/CascadeSelectSub.vue b/packages/primevue/src/cascadeselect/CascadeSelectSub.vue index cabe4727d..91bad4584 100644 --- a/packages/primevue/src/cascadeselect/CascadeSelectSub.vue +++ b/packages/primevue/src/cascadeselect/CascadeSelectSub.vue @@ -48,7 +48,6 @@ @option-focus-change="onOptionFocusChange" :pt="pt" :unstyled="unstyled" - :isParentMount="mounted" /> @@ -86,23 +85,7 @@ export default { templates: null, isParentMount: Boolean }, - data() { - return { - mounted: false - }; - }, - watch: { - isParentMount: { - handler(newValue) { - newValue && nestedPosition(this.container, this.level); - } - } - }, - mounted() { - // entering order correction when an option is selected - (this.isParentMount || this.level === 0) && nestedPosition(this.container, this.level); - this.mounted = true; - }, + methods: { getOptionId(processedOption) { return `${this.selectId}_${processedOption.key}`; diff --git a/packages/primevue/src/cascadeselect/style/CascadeSelectStyle.js b/packages/primevue/src/cascadeselect/style/CascadeSelectStyle.js index d83ed181d..d734c42e5 100644 --- a/packages/primevue/src/cascadeselect/style/CascadeSelectStyle.js +++ b/packages/primevue/src/cascadeselect/style/CascadeSelectStyle.js @@ -144,11 +144,15 @@ const theme = ({ dt }) => ` .p-cascadeselect-option-active { overflow: visible; - background: ${dt('cascadeselect.option.focus.background')}; - color: ${dt('cascadeselect.option.focus.color')}; } -.p-cascadeselect-option:not(.p-cascadeselect-option-selected):not(.p-disabled).p-focus { +.p-cascadeselect-option-active > .p-cascadeselect-option-content { + background: ${dt('cascadeselect.option.focus.background')}; + color: ${dt('cascadeselect.option.focus.color')}; + border-radius: ${dt('cascadeselect.option.border.radius')}; +} + +.p-cascadeselect-option:not(.p-cascadeselect-option-selected):not(.p-disabled).p-focus > .p-cascadeselect-option-content { background: ${dt('cascadeselect.option.focus.background')}; color: ${dt('cascadeselect.option.focus.color')}; } @@ -157,7 +161,7 @@ const theme = ({ dt }) => ` color: ${dt('cascadeselect.option.icon.focus.color')}; } -.p-cascadeselect-option-selected { +.p-cascadeselect-option-selected .p-cascadeselect-option-content { background: ${dt('cascadeselect.option.selected.background')}; color: ${dt('cascadeselect.option.selected.color')}; } @@ -188,6 +192,39 @@ const theme = ({ dt }) => ` height: ${dt('cascadeselect.option.icon.size')}; color: ${dt('cascadeselect.option.icon.color')}; } + +.p-cascadeselect-mobile-active .p-cascadeselect-option-content { + border-radius: ${dt('cascadeselect.option.border.radius')}; +} + +.p-cascadeselect-mobile-active-active .p-cascadeselect-list { + display: flex; + flex-direction: column; + top: 100%; + left: 0; + z-index: 1; +} + +.p-cascadeselect-mobile-active .p-cascadeselect-list > .p-cascadeselect-option > .p-cascadeselect-option-content .p-cascadeselect-group-icon { + margin-left: auto; + transition: transform 0.2s; +} + +.p-cascadeselect-mobile-active .p-cascadeselect-list .p-cascadeselect-group-icon { + transition: transform 0.2s; + transform: rotate(90deg); +} + +.p-cascadeselect-mobile-active .p-cascadeselect-option-active > .p-cascadeselect-option-content .p-cascadeselect-group-icon { + transform: rotate(-90deg); +} + +.p-cascadeselect-mobile-active .p-cascadeselect-option-list { + position: static; + box-shadow: none; + border: 0 none; + padding-left: ${dt('cascadeselect.list.mobile.indent')}; +} `; const inlineStyles = { @@ -198,6 +235,7 @@ const classes = { root: ({ instance, props }) => [ 'p-cascadeselect p-component p-inputwrapper', { + 'p-cascadeselect-mobile': instance.queryMatches, 'p-disabled': props.disabled, 'p-invalid': props.invalid, 'p-variant-filled': props.variant ? props.variant === 'filled' : instance.$primevue.config.inputStyle === 'filled' || instance.$primevue.config.inputVariant === 'filled', @@ -218,8 +256,13 @@ const classes = { dropdown: 'p-cascadeselect-dropdown', loadingIcon: 'p-cascadeselect-loading-icon', dropdownIcon: 'p-cascadeselect-dropdown-icon', - overlay: 'p-cascadeselect-overlay p-component', - listContainer: 'p-cascadeselect-list-container', + overlay: ({ instance }) => [ + 'p-cascadeselect-overlay p-component', + { + 'p-cascadeselect-mobile-active': instance.queryMatches + } + ], + listContainer: 'p-cascadeselect-list', list: 'p-cascadeselect-list', option: ({ instance, processedOption }) => [ 'p-cascadeselect-option', diff --git a/packages/themes/src/presets/aura/cascadeselect/index.js b/packages/themes/src/presets/aura/cascadeselect/index.js index f19c746d2..a96da0652 100644 --- a/packages/themes/src/presets/aura/cascadeselect/index.js +++ b/packages/themes/src/presets/aura/cascadeselect/index.js @@ -38,7 +38,8 @@ export default { }, list: { padding: '{list.padding}', - gap: '{list.gap}' + gap: '{list.gap}', + mobileIndent: '1rem' }, option: { focusBackground: '{list.option.focus.background}', diff --git a/packages/themes/src/presets/lara/cascadeselect/index.js b/packages/themes/src/presets/lara/cascadeselect/index.js index f19c746d2..7d2a9d0ff 100644 --- a/packages/themes/src/presets/lara/cascadeselect/index.js +++ b/packages/themes/src/presets/lara/cascadeselect/index.js @@ -38,7 +38,8 @@ export default { }, list: { padding: '{list.padding}', - gap: '{list.gap}' + gap: '{list.gap}', + mobileIndent: '1.25rem' }, option: { focusBackground: '{list.option.focus.background}', diff --git a/packages/themes/src/presets/material/cascadeselect/index.js b/packages/themes/src/presets/material/cascadeselect/index.js index e17764179..6b6fbcc09 100644 --- a/packages/themes/src/presets/material/cascadeselect/index.js +++ b/packages/themes/src/presets/material/cascadeselect/index.js @@ -38,7 +38,8 @@ export default { }, list: { padding: '{list.padding}', - gap: '{list.gap}' + gap: '{list.gap}', + mobileIndent: '1rem' }, option: { focusBackground: '{list.option.focus.background}', diff --git a/packages/themes/src/presets/nora/cascadeselect/index.js b/packages/themes/src/presets/nora/cascadeselect/index.js index f19c746d2..a96da0652 100644 --- a/packages/themes/src/presets/nora/cascadeselect/index.js +++ b/packages/themes/src/presets/nora/cascadeselect/index.js @@ -38,7 +38,8 @@ export default { }, list: { padding: '{list.padding}', - gap: '{list.gap}' + gap: '{list.gap}', + mobileIndent: '1rem' }, option: { focusBackground: '{list.option.focus.background}',