-
Notifications
You must be signed in to change notification settings - Fork 81
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
allow kselect to extend outside of kmodal #429
Changes from all commits
c10dd10
ae3438e
67b4388
afb762d
427537f
bef67e9
2b8e49d
dd0c728
499c7b7
75a66c8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,13 +9,18 @@ | |
@keyup.esc.stop="emitCancelEvent" | ||
@keyup.enter="handleEnter" | ||
> | ||
<!-- KeenUiSelect targets modal by using div.modal selector --> | ||
<div | ||
ref="modal" | ||
class="modal" | ||
:tabindex="0" | ||
role="dialog" | ||
aria-labelledby="modal-title" | ||
:style="[ modalSizeStyles, { background: $themeTokens.surface } ]" | ||
:style="[ | ||
modalSizeStyles, | ||
{ background: $themeTokens.surface }, | ||
containsKSelect ? { overflowY: 'unset' } : { overflowY: 'auto' } | ||
]" | ||
> | ||
|
||
<!-- Modal Title --> | ||
|
@@ -48,7 +53,10 @@ | |
borderTop: `1px solid ${$themeTokens.fineLine}`, | ||
borderBottom: `1px solid ${$themeTokens.fineLine}`, | ||
} : {} ]" | ||
:class="{ 'scroll-shadow': scrollShadow }" | ||
:class="{ | ||
'scroll-shadow': scrollShadow, | ||
'contains-kselect': containsKSelect | ||
}" | ||
> | ||
<!-- @slot Main content of modal --> | ||
<slot></slot> | ||
|
@@ -182,11 +190,19 @@ | |
lastFocus: null, | ||
maxContentHeight: '1000', | ||
contentHeight: 0, | ||
containsKSelect: false, | ||
scrollShadow: false, | ||
delayedEnough: false, | ||
}; | ||
}, | ||
computed: { | ||
modalContentHeight() { | ||
// if modal contains KSelect, the correct value of its content.scrollHeight is overwritten by the height of the | ||
// KSelect options once KSelect is opened & the modal will elongate after KSelect is closed. | ||
// in that case, getBoundingClientRect().height is a better reflection of content height but during the loading | ||
// state it is temporarily 0 and fallback content.scrollHeight is accurate, as KSelect has not yet been opened | ||
return this.$refs.content.getBoundingClientRect().height || this.$refs.content.scrollHeight; | ||
}, | ||
modalSizeStyles() { | ||
return { | ||
'max-width': `${this.maxModalWidth - 32}px`, | ||
|
@@ -241,6 +257,10 @@ | |
}); | ||
window.addEventListener('focus', this.focusElementTest, true); | ||
window.setTimeout(() => (this.delayedEnough = true), 500); | ||
|
||
// if modal contains KSelect, special classes & styles will be applied | ||
const kSelectCheck = document.querySelector('div.modal div.ui-select'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd recommend adding a comment to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. added! (see related comment 🙂) |
||
this.containsKSelect = !!kSelectCheck; | ||
}, | ||
updated() { | ||
this.updateContentSectionStyle(); | ||
|
@@ -265,24 +285,30 @@ | |
updateContentSectionStyle: debounce(function() { | ||
if (this.$refs.title && this.$refs.actions) { | ||
if (Math.abs(this.$refs.content.scrollHeight - this.contentHeight) >= 8) { | ||
this.contentHeight = this.$refs.content.scrollHeight; | ||
// if there's dropdown & it is opened, the new scrollHeight detected shouldn't be applied, | ||
// or else the modal will elongate after the dropdown content has been closed | ||
this.contentHeight = this.containsKSelect | ||
? this.modalContentHeight | ||
: this.$refs.content.scrollHeight; | ||
} | ||
|
||
const maxContentHeightCheck = | ||
this.windowHeight - | ||
this.$refs.title.clientHeight - | ||
this.$refs.actions.clientHeight - | ||
32; | ||
|
||
// to prevent max height from toggling between pixels | ||
// we set a threshold of how many pixels the height should change before we update | ||
if (Math.abs(maxContentHeightCheck - this.maxContentHeight) >= 8) { | ||
this.maxContentHeight = maxContentHeightCheck; | ||
this.scrollShadow = this.maxContentHeight < this.$refs.content.scrollHeight; | ||
} | ||
|
||
// make sure that overflow-y won't be updated to 'auto' if this | ||
// function is running for the first time | ||
// (otherwise Firefox would add a vertical scrollbar right away) | ||
if (this.$refs.content.clientHeight !== 0) { | ||
// make sure that overflow-y won't be updated to 'auto' if this function is running for the first time | ||
// (otherwise Firefox would add a vertical scrollbar right away) + don't apply if modal contains KSelect | ||
// (otherwise KSelect will be trapped inside modal if KSelect is opened a second time) | ||
if (this.$refs.content.clientHeight !== 0 && !this.containsKSelect) { | ||
// add a vertical scrollbar if content doesn't fit | ||
if (this.$refs.content.scrollHeight > this.$refs.content.clientHeight) { | ||
this.$refs.content.style.overflowY = 'auto'; | ||
|
@@ -365,7 +391,6 @@ | |
top: 50%; | ||
left: 50%; | ||
margin: 0 auto; | ||
overflow-y: auto; | ||
border-radius: $radius; | ||
transform: translate(-50%, -50%); | ||
|
||
|
@@ -409,6 +434,10 @@ | |
background-size: 100% 20px, 100% 20px, 100% 10px, 100% 10px; | ||
} | ||
|
||
.contains-kselect { | ||
overflow: unset; | ||
} | ||
|
||
.actions { | ||
padding: 24px; | ||
text-align: right; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,8 @@ | |
The formatting has been changed to match our linters. We may eventually | ||
want to simply consolidate it with our component and remove any unused | ||
functionality. | ||
|
||
BELOW: KModal targets KeenUiSelect by using div.ui-select selector | ||
--> | ||
<div class="ui-select" :class="classes"> | ||
<input | ||
|
@@ -316,6 +318,7 @@ | |
data() { | ||
return { | ||
query: '', | ||
isInsideModal: false, | ||
isActive: false, | ||
isTouched: false, | ||
highlightedOption: null, | ||
|
@@ -552,6 +555,12 @@ | |
break; | ||
} | ||
} | ||
|
||
// look for KSelects nested within modals | ||
const allSelects = document.querySelectorAll('div.modal div.ui-select'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd recommend adding a comment to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. added explanatory comments to both |
||
// create array from a nodelist [IE does not support Array.from()] | ||
thanksameeelian marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const allSelectsArr = Array.prototype.slice.call(allSelects); | ||
this.isInsideModal = allSelectsArr.includes(this.$el); | ||
}, | ||
|
||
beforeDestroy() { | ||
|
@@ -744,7 +753,11 @@ | |
}, | ||
|
||
toggleDropdown() { | ||
this.calculateSpaceBelow(); | ||
// if called on dropdown inside modal, dropdown will generally render above input/placeholder when opened, | ||
// rather than below it: we want to render dropdown above input only in cases where there isn't enough | ||
// space available beneath input, but when dropdown extends outside a modal the func doesn't work as intended | ||
if (!this.isInsideModal) this.calculateSpaceBelow(); | ||
|
||
this[this.showDropdown ? 'closeDropdown' : 'openDropdown'](); | ||
}, | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to understand, what situation was this
||
supposed to model?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when the page initially loads,
this.$refs.content.getBoundingClientRect().height
will very temporarily be0
, so in that case we will usethis.$refs.content.scrollHeight
, which will be accurate to the real contents of the modal (rather than the length of the dropdown) because the dropdown has not yet been opened.i'll add in a code comment - please let me know if you think it's a sufficient explanation 🙂 this and several other of my additions were written to counteract all the strange cases i was able to encounter - without this specific check, upon initial load the modal is only rendered tall enough to contain its title, with its contents floating out in space over the grey overlay.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, thank you