Skip to content

Commit 566242e

Browse files
authored
fix: fully reimplement code to calculate widest drop text width (#407)
* fix: fully reimplement code to calculate widest drop text width
1 parent fb16b84 commit 566242e

File tree

2 files changed

+51
-21
lines changed

2 files changed

+51
-21
lines changed

packages/multiple-select-vanilla/src/MultipleSelectInstance.ts

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
createDomElement,
1616
emptyElement,
1717
findParent,
18+
getComputedSize,
1819
getOffset,
1920
getSize,
2021
insertAfter,
@@ -1981,31 +1982,49 @@ export class MultipleSelectInstance {
19811982
const parentWidth = this.parentElm.scrollWidth;
19821983

19831984
// keep the dropWidth/width as reference, if our new calculated width is below then we will re-adjust (else do nothing)
1984-
let currentDefinedWidth: number | string = parentWidth;
1985+
let selectParentWidth: number | string = parentWidth;
19851986
if (this.options.dropWidth || this.options.width) {
1986-
currentDefinedWidth = this.options.dropWidth || this.options.width || 0;
1987+
selectParentWidth = this.options.dropWidth || this.options.width || 0;
19871988
}
19881989

19891990
// calculate the "Select All" element width, this text is configurable which is why we recalculate every time
19901991
const selectAllSpanElm = this.dropElm.querySelector<HTMLSpanElement>('.ms-select-all span');
19911992
const dropUlElm = this.dropElm.querySelector<HTMLUListElement>('ul');
19921993

19931994
if (dropUlElm) {
1994-
const liPadding = 26; // there are multiple padding involved, let's fix it at 26px
1995-
1996-
const selectAllElmWidth = selectAllSpanElm?.clientWidth ?? 0 + liPadding;
1995+
let contentWidth = 0;
1996+
let allPaddingSizes = 0; // calculate all possible ul>li>label paddings
1997+
const selectAllElmWidth = (selectAllSpanElm?.clientWidth ?? 0) + allPaddingSizes;
19971998
const hasScrollbar = dropUlElm.scrollHeight > dropUlElm.clientHeight;
19981999
const scrollbarWidth = hasScrollbar ? this.getScrollbarWidth() : 0;
1999-
let contentWidth = 0;
20002000

2001-
this.dropElm.querySelectorAll('li label').forEach(elm => {
2002-
if (elm.scrollWidth > contentWidth) {
2003-
contentWidth = elm.scrollWidth;
2001+
// 1. find first item that has text value and measure all paddings
2002+
allPaddingSizes += getComputedSize(dropUlElm, 'paddingLeft') * 2;
2003+
2004+
let foundFilledItem = false;
2005+
for (const liElm of Array.from(dropUlElm.querySelectorAll<HTMLLIElement>('li'))) {
2006+
const labelElm = liElm.querySelector<HTMLLabelElement>('label');
2007+
const iconElm = liElm.querySelector<HTMLLabelElement>('.icon-checkbox-container');
2008+
const spanElm = liElm.querySelector<HTMLSpanElement>('span');
2009+
if (labelElm && spanElm?.textContent) {
2010+
// find first item that has value and calculate its padding sizes
2011+
if (!foundFilledItem) {
2012+
allPaddingSizes += getComputedSize(liElm, 'paddingLeft') * 2;
2013+
allPaddingSizes += getComputedSize(labelElm, 'paddingLeft') * 2;
2014+
allPaddingSizes += getComputedSize(spanElm, 'paddingLeft');
2015+
allPaddingSizes += iconElm?.offsetWidth ?? 0;
2016+
foundFilledItem = true;
2017+
}
2018+
2019+
// keep ref of the widest text width
2020+
if (spanElm.offsetWidth > contentWidth) {
2021+
contentWidth = spanElm.offsetWidth;
2022+
}
20042023
}
2005-
});
2024+
}
20062025

20072026
// add a padding & include the browser scrollbar width
2008-
contentWidth += liPadding + scrollbarWidth;
2027+
contentWidth += allPaddingSizes + scrollbarWidth;
20092028

20102029
// make sure the new calculated width is wide enough to include the "Select All" text (this text is configurable)
20112030
if (contentWidth < selectAllElmWidth) {
@@ -2022,10 +2041,10 @@ export class MultipleSelectInstance {
20222041
contentWidth = this.options.minWidth;
20232042
}
20242043

2025-
// finally re-adjust the drop to the new calculated width when necessary
2026-
if (currentDefinedWidth === '100%' || +currentDefinedWidth < contentWidth) {
2044+
// finally if text is wider than select container, then re-adjust the drop to the new calculated width
2045+
if (selectParentWidth === '100%' || +selectParentWidth < contentWidth) {
2046+
this.dropElm.style.minWidth = 'auto';
20272047
this.dropElm.style.width = `${contentWidth}px`;
2028-
this.dropElm.style.maxWidth = `${contentWidth}px`;
20292048
}
20302049
}
20312050
}

packages/multiple-select-vanilla/src/utils/domUtils.ts

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -168,11 +168,7 @@ export function getSize(elm: HTMLElement | undefined, mode: 'inner' | 'outer' |
168168
const prevPosition = elm.style.position;
169169
elm.style.display = 'block';
170170
elm.style.position = 'absolute';
171-
const widthStr = window.getComputedStyle(elm)[type];
172-
size = Number.parseFloat(widthStr);
173-
if (Number.isNaN(size)) {
174-
size = 0;
175-
}
171+
size = getComputedSize(elm, type);
176172

177173
// reapply original values
178174
elm.style.display = prevDisplay;
@@ -182,12 +178,27 @@ export function getSize(elm: HTMLElement | undefined, mode: 'inner' | 'outer' |
182178
return size || 0;
183179
}
184180

181+
/**
182+
* use `getComputedStyle()` and return size has a number or default to 0
183+
* @param elm - HTML element
184+
* @param styleType - CSS style type (e.g. 'width', 'paddingLeft', ...)
185+
* @returns - return size as a number or 0 when unparseable
186+
*/
187+
export function getComputedSize(elm: HTMLElement, styleType: string) {
188+
const elmStyle = window.getComputedStyle(elm)[styleType as any];
189+
let size = Number.parseFloat(elmStyle);
190+
if (Number.isNaN(size)) {
191+
size = 0;
192+
}
193+
return size;
194+
}
195+
185196
/**
186197
* Find a single parent by a simple selector, it only works with a simple selector
187198
* for example: "input.some-class", ".some-class", "input#some-id"
188199
* Note: it won't work with complex selector like "div.some-class input.my-class"
189-
* @param elm
190-
* @param selector
200+
* @param elm - HTML element
201+
* @param selector - HTML selector
191202
* @returns
192203
*/
193204
export function findParent(elm: HTMLElement, selector: string) {

0 commit comments

Comments
 (0)