From a3b2ab0311b314e197b125d9bb0ad99ec330e114 Mon Sep 17 00:00:00 2001 From: Boris Diakur Date: Thu, 1 Jul 2021 13:30:20 +0200 Subject: [PATCH] feat(ld-select): implement scrollable options container --- src/liquid/components/ld-option/ld-option.css | 6 - src/liquid/components/ld-select/ld-select.css | 23 +++- src/liquid/components/ld-select/ld-select.tsx | 119 ++++++++++-------- src/liquid/components/ld-select/readme.md | 19 +++ 4 files changed, 108 insertions(+), 59 deletions(-) diff --git a/src/liquid/components/ld-option/ld-option.css b/src/liquid/components/ld-option/ld-option.css index fd333f3983..aafe4c9eaa 100644 --- a/src/liquid/components/ld-option/ld-option.css +++ b/src/liquid/components/ld-option/ld-option.css @@ -12,18 +12,12 @@ -webkit-touch-callout: none; [data-popper-placement*='bottom'] & { - &:first-of-type { - border-top: solid var(--ld-col-rblck1) 1px; - } &:last-of-type { border-bottom-left-radius: var(--ld-br-m); border-bottom-right-radius: var(--ld-br-m); } } [data-popper-placement*='top'] & { - &:last-of-type { - border-bottom: solid var(--ld-col-rblck1) 1px; - } &:first-of-type { border-top-left-radius: var(--ld-br-m); border-top-right-radius: var(--ld-br-m); diff --git a/src/liquid/components/ld-select/ld-select.css b/src/liquid/components/ld-select/ld-select.css index c15f60456a..42a19de92a 100644 --- a/src/liquid/components/ld-select/ld-select.css +++ b/src/liquid/components/ld-select/ld-select.css @@ -1,4 +1,5 @@ .ld-select { + --ld-select-popper-max-height: 16rem; display: inline-flex; } @@ -19,11 +20,14 @@ user-select: none; touch-action: manipulation; background-color: var(--ld-col-wht); - box-shadow: inset 0 0 0 0.1rem var(--ld-col-rblck1); line-height: 1; text-align: left; -webkit-touch-callout: none; + &:where([aria-expanded='false']) { + box-shadow: inset 0 0 0 0.1rem var(--ld-col-rblck1); + } + &:where(:disabled), &:where([aria-disabled]) { opacity: 0.2; @@ -89,6 +93,11 @@ position: absolute; } +.ld-select__scroll-container { + max-height: var(--ld-select-popper-max-height); + overflow-y: auto; +} + .ld-select__popper--expanded { visibility: inherit; @@ -108,10 +117,22 @@ &:after { bottom: 0; } + + .ld-select__scroll-container { + border-top: solid var(--ld-col-rblck1) 1px; + border-bottom-left-radius: var(--ld-br-m); + border-bottom-right-radius: var(--ld-br-m); + } } &[data-popper-placement*='top'] { &:after { top: 0; } + + .ld-select__scroll-container { + border-bottom: solid var(--ld-col-rblck1) 1px; + border-top-left-radius: var(--ld-br-m); + border-top-right-radius: var(--ld-br-m); + } } } diff --git a/src/liquid/components/ld-select/ld-select.tsx b/src/liquid/components/ld-select/ld-select.tsx index 940322977a..3db64f9cd6 100644 --- a/src/liquid/components/ld-select/ld-select.tsx +++ b/src/liquid/components/ld-select/ld-select.tsx @@ -35,6 +35,7 @@ export class LdSelect { private selectRef!: HTMLElement private triggerRef!: HTMLElement private popperRef!: HTMLElement + private scrollContainerRef!: HTMLElement private popper: PopperInstance private observer: MutationObserver @@ -80,6 +81,7 @@ export class LdSelect { this.popper = createPopper( this.selectRef, this.popperRef, + // portal, { modifiers: [preventOverflow, flip /*, offset*/], placement: 'bottom-start', @@ -89,7 +91,7 @@ export class LdSelect { } private initOptions() { - const children = this.popperRef.children + const children = this.scrollContainerRef.children if (!children.length) { throw new TypeError( `ld-select requires at least one ld-option element as a child, but found none.` @@ -114,7 +116,6 @@ export class LdSelect { } private togglePopper() { - console.info('toggle popper') this.expanded = !this.expanded if (this.expanded) { @@ -149,12 +150,59 @@ export class LdSelect { this.initOptions() } + private expandAndFocus() { + this.handleTriggerClick() + setTimeout(() => { + // If selected in single select mode, focus selected + let optionToFocus + if (!this.multiple) { + optionToFocus = this.popperRef.querySelector( + 'ld-option[aria-selected="true"]' + ) + } + if (!optionToFocus) { + optionToFocus = this.triggerRef + } + optionToFocus.focus() + }) + } + + private handleHome(ev) { + // Move focus to the first option. + if (this.expanded) { + ev.preventDefault() + if (this.popperRef.dataset.popperPlacement.includes('top')) { + this.popperRef.querySelector('ld-option')?.focus() + } else { + this.triggerRef.focus() + } + } + } + + private handleEnd(ev) { + // Move focus to the last option. + if (this.expanded) { + ev.preventDefault() + if (this.popperRef.dataset.popperPlacement.includes('top')) { + this.triggerRef.focus() + } else { + const options = this.popperRef.querySelectorAll('ld-option') + options[options.length - 1]?.focus() + } + } + } + private handleKeyDown(ev: KeyboardEvent) { switch (ev.key) { case 'ArrowDown': { // Move focus to the next option. ev.preventDefault() if (this.expanded) { + if (ev.metaKey) { + this.handleEnd(ev) + return + } + if (document.activeElement.nextElementSibling) { ;(document.activeElement.nextElementSibling as HTMLElement)?.focus() } else { @@ -165,20 +213,7 @@ export class LdSelect { } } } else { - this.handleTriggerClick() - setTimeout(() => { - // If selected in single select mode, focus selected - let optionToFocus - if (!this.multiple) { - optionToFocus = this.popperRef.querySelector( - 'ld-option[aria-selected="true"]' - ) - } - if (!optionToFocus) { - optionToFocus = this.triggerRef - } - optionToFocus.focus() - }) + this.expandAndFocus() } break } @@ -186,6 +221,11 @@ export class LdSelect { // Move focus to the previous option. ev.preventDefault() if (this.expanded) { + if (ev.metaKey) { + this.handleHome(ev) + return + } + if (document.activeElement.previousElementSibling) { ;(document.activeElement .previousElementSibling as HTMLElement)?.focus() @@ -200,44 +240,14 @@ export class LdSelect { } } } else { - this.handleTriggerClick() - setTimeout(() => { - // If selected in single select mode, focus selected - let optionToFocus - if (!this.multiple) { - optionToFocus = this.popperRef.querySelector( - 'ld-option[aria-selected="true"]' - ) - } - if (!optionToFocus) { - optionToFocus = this.triggerRef - } - optionToFocus.focus() - }) + this.expandAndFocus() } break case 'Home': - // Move focus to the first option. - if (this.expanded) { - ev.preventDefault() - if (this.popperRef.dataset.popperPlacement.includes('top')) { - this.popperRef.querySelector('ld-option')?.focus() - } else { - this.triggerRef.focus() - } - } + this.handleHome(ev) break case 'End': - // Move focus to the last option. - if (this.expanded) { - ev.preventDefault() - if (this.popperRef.dataset.popperPlacement.includes('top')) { - this.triggerRef.focus() - } else { - const options = this.popperRef.querySelectorAll('ld-option') - options[options.length - 1]?.focus() - } - } + this.handleEnd(ev) break case ' ': // If expanded: Select focused option, close (if single select). @@ -356,13 +366,18 @@ export class LdSelect { this.placeholder} - +
(this.scrollContainerRef = el as HTMLElement)} + class="ld-select__scroll-container" + > + +
+ ) } diff --git a/src/liquid/components/ld-select/readme.md b/src/liquid/components/ld-select/readme.md index 019782f299..d172703146 100644 --- a/src/liquid/components/ld-select/readme.md +++ b/src/liquid/components/ld-select/readme.md @@ -21,6 +21,15 @@ permalink: components/ld-select/ Banana Strawberry Watermelon + Honeymelon + Rasberry + Cherry + Blueberry + Peach + Grape + Pear + Pineapple + Plum {% endexample %} @@ -31,6 +40,16 @@ permalink: components/ld-select/ Apple Banana Strawberry + Watermelon + Honeymelon + Rasberry + Cherry + Blueberry + Peach + Grape + Pear + Pineapple + Plum {% endexample %}