Skip to content

Commit 47576b8

Browse files
authored
fix: simplify code and avoid reusing code (DRY) (#367)
1 parent 5908698 commit 47576b8

File tree

6 files changed

+69
-84
lines changed

6 files changed

+69
-84
lines changed

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

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ import {
1515
createDomElement,
1616
emptyElement,
1717
findParent,
18-
getElementOffset,
19-
getElementSize,
18+
getOffset,
19+
getSize,
2020
insertAfter,
2121
toggleElement,
2222
} from './utils/domUtils.js';
@@ -801,10 +801,10 @@ export class MultipleSelectInstance {
801801
if (window.getComputedStyle) {
802802
computedWidth = window.getComputedStyle(this.elm).width;
803803
if (computedWidth === 'auto') {
804-
computedWidth = getElementSize(this.dropElm, 'outer', 'width') + 20;
804+
computedWidth = getSize(this.dropElm, 'outer', 'width') + 20;
805805
}
806806
} else {
807-
computedWidth = getElementSize(this.elm, 'outer', 'width') + 20;
807+
computedWidth = getSize(this.elm, 'outer', 'width') + 20;
808808
}
809809

810810
this.parentElm.style.width = `${this.options.width || computedWidth}px`;
@@ -1271,7 +1271,7 @@ export class MultipleSelectInstance {
12711271
protected adjustDropSizeAndPosition() {
12721272
if (this.dropElm) {
12731273
if (this.options.container) {
1274-
const offset = getElementOffset(this.dropElm);
1274+
const offset = getOffset(this.dropElm);
12751275
let container: HTMLElement;
12761276
if (this.options.container instanceof Node) {
12771277
container = this.options.container as HTMLElement;
@@ -1282,13 +1282,13 @@ export class MultipleSelectInstance {
12821282
this.dropElm.style.top = `${offset?.top ?? 0}px`;
12831283
this.dropElm.style.left = `${offset?.left ?? 0}px`;
12841284
this.dropElm.style.minWidth = 'auto';
1285-
this.dropElm.style.width = `${getElementSize(this.parentElm, 'outer', 'width')}px`;
1285+
this.dropElm.style.width = `${getSize(this.parentElm, 'outer', 'width')}px`;
12861286
}
12871287

12881288
const minHeight = this.options.minHeight;
12891289
let maxHeight = this.options.maxHeight;
12901290
if (this.options.maxHeightUnit === 'row') {
1291-
maxHeight = getElementSize(this.dropElm.querySelector('ul>li') as HTMLLIElement, 'outer', 'height') * this.options.maxHeight;
1291+
maxHeight = getSize(this.dropElm.querySelector('ul>li') as HTMLLIElement, 'outer', 'height') * this.options.maxHeight;
12921292
}
12931293
this.ulElm ??= this.dropElm.querySelector('ul');
12941294
if (this.ulElm) {
@@ -1898,7 +1898,7 @@ export class MultipleSelectInstance {
18981898

18991899
if (this.dropElm && this.parentElm) {
19001900
const { bottom: spaceBottom, top: spaceTop } = calculateAvailableSpace(this.dropElm);
1901-
const { top: selectOffsetTop, left: selectOffsetLeft } = getElementOffset(this.parentElm) as HtmlElementPosition;
1901+
const { top: selectOffsetTop, left: selectOffsetLeft } = getOffset(this.parentElm) as HtmlElementPosition;
19021902
const msDropHeight = this.dropElm.getBoundingClientRect().height;
19031903
const msDropWidth = this.dropElm.getBoundingClientRect().width;
19041904
const windowWidth = document.body.offsetWidth || window.innerWidth;

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ export {
1212
createDomStructure,
1313
emptyElement,
1414
findParent,
15-
getElementOffset,
16-
getElementSize,
15+
getOffset,
16+
getSize,
1717
insertAfter,
1818
omitProp,
1919
toggleElement,

packages/multiple-select-vanilla/src/services/binding-event.service.ts

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -35,27 +35,12 @@ export class BindingEventService {
3535

3636
if (typeof (elementOrElements as NodeListOf<H>)?.forEach === 'function') {
3737
// multiple elements to bind to
38-
(elementOrElements as NodeListOf<H>).forEach(element => {
39-
for (const eventName of eventNames) {
40-
if (!this._distinctEvent || (this._distinctEvent && !this.hasBinding(element, eventName))) {
41-
element.addEventListener(eventName, listener as EventListener, listenerOptions);
42-
this._boundedEvents.push({ element, eventName, listener: listener as EventListener, groupName });
43-
}
44-
}
45-
});
38+
(elementOrElements as NodeListOf<H>).forEach(element =>
39+
this.bindElementEvents(element, eventNames, listener, listenerOptions, groupName),
40+
);
4641
} else {
4742
// single elements to bind to
48-
for (const eventName of eventNames) {
49-
if (!this._distinctEvent || (this._distinctEvent && !this.hasBinding(elementOrElements as H, eventName))) {
50-
(elementOrElements as H).addEventListener(eventName, listener as EventListener, listenerOptions);
51-
this._boundedEvents.push({
52-
element: elementOrElements as H,
53-
eventName,
54-
listener: listener as EventListener,
55-
groupName,
56-
});
57-
}
58-
}
43+
this.bindElementEvents(elementOrElements as H, eventNames, listener, listenerOptions, groupName);
5944
}
6045
}
6146

@@ -115,4 +100,23 @@ export class BindingEventService {
115100
}
116101
}
117102
}
103+
104+
// --
105+
// private functions
106+
107+
/** bind all event(s) to the element */
108+
private bindElementEvents(
109+
element: HTMLElement,
110+
eventNames: Array<keyof HTMLElementEventMap>,
111+
listener: EventListener,
112+
listenerOptions?: boolean | AddEventListenerOptions,
113+
groupName = '',
114+
) {
115+
for (const eventName of eventNames) {
116+
if (!this._distinctEvent || (this._distinctEvent && !this.hasBinding(element, eventName))) {
117+
element.addEventListener(eventName, listener as EventListener, listenerOptions);
118+
this._boundedEvents.push({ element, eventName, listener: listener as EventListener, groupName });
119+
}
120+
}
121+
}
118122
}

packages/multiple-select-vanilla/src/services/virtual-scroll.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import Constants from '../constants.js';
22
import type { HtmlStruct, VirtualCache, VirtualScrollOption } from '../models/interfaces.js';
3-
import { convertItemRowToHtml, emptyElement } from '../utils/domUtils.js';
3+
import { convertItemRowToHtml, createDomElement, emptyElement } from '../utils/domUtils.js';
44

55
export class VirtualScroll {
66
protected clusterRows?: number;
@@ -157,8 +157,7 @@ export class VirtualScroll {
157157
}
158158

159159
protected getExtra(className: string, height: number) {
160-
const tag = document.createElement('li');
161-
tag.className = `virtual-scroll-${className}`;
160+
const tag = createDomElement('li', { className: `virtual-scroll-${className}` });
162161
if (height) {
163162
tag.style.height = `${height}px`;
164163
}

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

Lines changed: 33 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -8,30 +8,18 @@ export interface HtmlElementPosition {
88
right: number;
99
}
1010

11-
/** calculate available space for each side of the DOM element */
12-
export function calculateAvailableSpace(element: HTMLElement): { top: number; bottom: number; left: number; right: number } {
13-
let bottom = 0;
14-
let top = 0;
15-
let left = 0;
16-
let right = 0;
11+
/** Calculate available space for each side of the DOM element */
12+
export function calculateAvailableSpace(elm: HTMLElement): { top: number; bottom: number; left: number; right: number } {
13+
const { innerHeight: windowHeight = 0, innerWidth: windowWidth = 0 } = window;
14+
const { top: pageScrollTop, left: pageScrollLeft } = windowScrollPosition();
15+
const { top = 0, left = 0 } = getOffset(elm) || {};
1716

18-
const windowHeight = window.innerHeight ?? 0;
19-
const windowWidth = window.innerWidth ?? 0;
20-
const scrollPosition = windowScrollPosition();
21-
const pageScrollTop = scrollPosition.top;
22-
const pageScrollLeft = scrollPosition.left;
23-
const elmOffset = getElementOffset(element);
24-
25-
if (elmOffset) {
26-
const elementOffsetTop = elmOffset.top ?? 0;
27-
const elementOffsetLeft = elmOffset.left ?? 0;
28-
top = elementOffsetTop - pageScrollTop;
29-
bottom = windowHeight - (elementOffsetTop - pageScrollTop);
30-
left = elementOffsetLeft - pageScrollLeft;
31-
right = windowWidth - (elementOffsetLeft - pageScrollLeft);
32-
}
33-
34-
return { top, bottom, left, right };
17+
return {
18+
top: top - pageScrollTop,
19+
bottom: windowHeight - (top - pageScrollTop),
20+
left: left - pageScrollLeft,
21+
right: windowWidth - (left - pageScrollLeft),
22+
};
3523
}
3624

3725
/**
@@ -72,9 +60,7 @@ export function createDomElement<T extends keyof HTMLElementTagNameMap, K extend
7260
}
7361
});
7462
}
75-
if (appendToParent?.appendChild) {
76-
appendToParent.appendChild(elm);
77-
}
63+
appendToParent?.appendChild(elm);
7864
return elm;
7965
}
8066

@@ -128,53 +114,48 @@ export function convertItemRowToHtml(item: HtmlStruct): HTMLElement {
128114
* Empty a DOM element by removing all of its DOM element children leaving with an empty element (basically an empty shell)
129115
* @return {object} element - updated element
130116
*/
131-
export function emptyElement<T extends Element = Element>(element?: T | null): T | undefined | null {
132-
while (element?.firstChild) {
133-
if (element.lastChild) {
134-
element.removeChild(element.lastChild);
117+
export function emptyElement<T extends Element = Element>(elm?: T | null): T | undefined | null {
118+
while (elm?.firstChild) {
119+
if (elm.lastChild) {
120+
elm.removeChild(elm.lastChild);
135121
}
136122
}
137-
return element;
123+
return elm;
138124
}
139125

140126
/** Get HTML element offset with pure JS */
141-
export function getElementOffset(element?: HTMLElement): HtmlElementPosition | undefined {
142-
if (!element) {
143-
return undefined;
144-
}
145-
const rect = element?.getBoundingClientRect?.();
146-
let top = 0;
147-
let left = 0;
148-
let bottom = 0;
149-
let right = 0;
150-
151-
if (rect?.top !== undefined && rect.left !== undefined) {
152-
top = rect.top + window.pageYOffset;
153-
left = rect.left + window.pageXOffset;
154-
right = rect.right;
155-
bottom = rect.bottom;
156-
}
157-
return { top, left, bottom, right };
127+
export function getOffset(element?: HTMLElement): HtmlElementPosition | undefined {
128+
if (element) {
129+
const { top, left, bottom, right } = element.getBoundingClientRect();
130+
return {
131+
top: top + window.pageYOffset,
132+
left: left + window.pageXOffset,
133+
bottom,
134+
right,
135+
};
136+
}
137+
return undefined;
158138
}
159139

160-
export function getElementSize(elm: HTMLElement | undefined, mode: 'inner' | 'outer' | 'scroll', type: 'height' | 'width') {
140+
export function getSize(elm: HTMLElement | undefined, mode: 'inner' | 'outer' | 'scroll', type: 'height' | 'width') {
161141
if (!elm) {
162142
return 0;
163143
}
164144

165145
// first try defined style width or offsetWidth (which include scroll & padding)
166146
let size = Number.parseFloat(elm.style[type]);
167147
if (!size || Number.isNaN(size)) {
148+
const pascalType = type === 'height' ? 'Height' : 'Width';
168149
switch (mode) {
169150
case 'outer':
170-
size = elm[type === 'width' ? 'offsetWidth' : 'offsetHeight'];
151+
size = elm[`offset${pascalType}`];
171152
break;
172153
case 'scroll':
173-
size = elm[type === 'width' ? 'scrollWidth' : 'scrollHeight'];
154+
size = elm[`scroll${pascalType}`];
174155
break;
175156
case 'inner':
176157
default:
177-
size = elm[type === 'width' ? 'clientWidth' : 'clientHeight'];
158+
size = elm[`client${pascalType}`];
178159
break;
179160
}
180161
size = elm.getBoundingClientRect()[type];

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export function compareObjects(objectA: any, objectB: any, compareLength = false
1616
return true;
1717
}
1818

19+
/** make deep copy clone of an object */
1920
export function deepCopy(obj: any): any {
2021
if (typeof obj !== 'object' || obj === null) {
2122
return obj;

0 commit comments

Comments
 (0)