@materializecss/materialize 2.0.3-beta → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/css/materialize.css +37 -15
- package/dist/css/materialize.min.css +2 -2
- package/dist/js/materialize.js +503 -1974
- package/dist/js/materialize.min.js +2 -2
- package/dist/js/materialize.min.js.map +1 -1
- package/dist/src/buttons.d.ts.map +1 -1
- package/dist/src/cards.d.ts.map +1 -1
- package/dist/src/collapsible.d.ts +1 -0
- package/dist/src/collapsible.d.ts.map +1 -1
- package/dist/src/dropdown.d.ts.map +1 -1
- package/dist/src/global.d.ts.map +1 -1
- package/dist/src/materialbox.d.ts +14 -10
- package/dist/src/materialbox.d.ts.map +1 -1
- package/dist/src/modal.d.ts.map +1 -1
- package/dist/src/range.d.ts.map +1 -1
- package/dist/src/scrollspy.d.ts.map +1 -1
- package/dist/src/sidenav.d.ts +25 -25
- package/dist/src/sidenav.d.ts.map +1 -1
- package/dist/src/slider.d.ts +12 -12
- package/dist/src/slider.d.ts.map +1 -1
- package/dist/src/tabs.d.ts +1 -1
- package/dist/src/tabs.d.ts.map +1 -1
- package/dist/src/toasts.d.ts +7 -2
- package/dist/src/toasts.d.ts.map +1 -1
- package/dist/src/tooltip.d.ts.map +1 -1
- package/package.json +14 -10
- package/sass/components/_collapsible.scss +14 -2
- package/sass/components/_materialbox.scss +2 -2
- package/sass/components/_modal.scss +0 -1
- package/sass/components/_tooltip.scss +18 -8
- package/sass/components/_variables.scss +2 -2
- package/Gruntfile.js +0 -385
- package/src/autocomplete.ts +0 -553
- package/src/bounding.ts +0 -6
- package/src/buttons.ts +0 -260
- package/src/cards.ts +0 -53
- package/src/carousel.ts +0 -676
- package/src/characterCounter.ts +0 -117
- package/src/chips.ts +0 -439
- package/src/collapsible.ts +0 -249
- package/src/component.ts +0 -120
- package/src/datepicker.ts +0 -1076
- package/src/dropdown.ts +0 -644
- package/src/edges.ts +0 -6
- package/src/forms.ts +0 -132
- package/src/global.ts +0 -114
- package/src/index.ts +0 -26
- package/src/materialbox.ts +0 -404
- package/src/modal.ts +0 -341
- package/src/parallax.ts +0 -149
- package/src/pushpin.ts +0 -165
- package/src/range.ts +0 -198
- package/src/scrollspy.ts +0 -263
- package/src/select.ts +0 -484
- package/src/sidenav.ts +0 -543
- package/src/slider.ts +0 -474
- package/src/tabs.ts +0 -347
- package/src/tapTarget.ts +0 -273
- package/src/timepicker.ts +0 -832
- package/src/toasts.ts +0 -290
- package/src/tooltip.ts +0 -366
- package/src/utils.ts +0 -271
- package/src/waves.ts +0 -70
package/src/dropdown.ts
DELETED
|
@@ -1,644 +0,0 @@
|
|
|
1
|
-
import anim from "animejs";
|
|
2
|
-
|
|
3
|
-
import { Utils } from "./utils";
|
|
4
|
-
import { Component, BaseOptions, InitElements, MElement, Openable } from "./component";
|
|
5
|
-
|
|
6
|
-
export interface DropdownOptions extends BaseOptions {
|
|
7
|
-
/**
|
|
8
|
-
* Defines the edge the menu is aligned to.
|
|
9
|
-
* @default 'left'
|
|
10
|
-
*/
|
|
11
|
-
alignment: 'left' | 'right';
|
|
12
|
-
/**
|
|
13
|
-
* If true, automatically focus dropdown el for keyboard.
|
|
14
|
-
* @default true
|
|
15
|
-
*/
|
|
16
|
-
autoFocus: boolean;
|
|
17
|
-
/**
|
|
18
|
-
* If true, constrainWidth to the size of the dropdown activator.
|
|
19
|
-
* @default true
|
|
20
|
-
*/
|
|
21
|
-
constrainWidth: boolean;
|
|
22
|
-
/**
|
|
23
|
-
* Provide an element that will be the bounding container of the dropdown.
|
|
24
|
-
* @default null
|
|
25
|
-
*/
|
|
26
|
-
container: Element;
|
|
27
|
-
/**
|
|
28
|
-
* If false, the dropdown will show below the trigger.
|
|
29
|
-
* @default true
|
|
30
|
-
*/
|
|
31
|
-
coverTrigger: boolean;
|
|
32
|
-
/**
|
|
33
|
-
* If true, close dropdown on item click.
|
|
34
|
-
* @default true
|
|
35
|
-
*/
|
|
36
|
-
closeOnClick: boolean;
|
|
37
|
-
/**
|
|
38
|
-
* If true, the dropdown will open on hover.
|
|
39
|
-
* @default false
|
|
40
|
-
*/
|
|
41
|
-
hover: boolean;
|
|
42
|
-
/**
|
|
43
|
-
* The duration of the transition enter in milliseconds.
|
|
44
|
-
* @default 150
|
|
45
|
-
*/
|
|
46
|
-
inDuration: number;
|
|
47
|
-
/**
|
|
48
|
-
* The duration of the transition out in milliseconds.
|
|
49
|
-
* @default 250
|
|
50
|
-
*/
|
|
51
|
-
outDuration: number;
|
|
52
|
-
/**
|
|
53
|
-
* Function called when dropdown starts entering.
|
|
54
|
-
* @default null
|
|
55
|
-
*/
|
|
56
|
-
onOpenStart: (el: HTMLElement) => void;
|
|
57
|
-
/**
|
|
58
|
-
* Function called when dropdown finishes entering.
|
|
59
|
-
* @default null
|
|
60
|
-
*/
|
|
61
|
-
onOpenEnd: (el: HTMLElement) => void;
|
|
62
|
-
/**
|
|
63
|
-
* Function called when dropdown starts exiting.
|
|
64
|
-
* @default null
|
|
65
|
-
*/
|
|
66
|
-
onCloseStart: (el: HTMLElement) => void;
|
|
67
|
-
/**
|
|
68
|
-
* Function called when dropdown finishes exiting.
|
|
69
|
-
* @default null
|
|
70
|
-
*/
|
|
71
|
-
onCloseEnd: (el: HTMLElement) => void;
|
|
72
|
-
/**
|
|
73
|
-
* Function called when item is clicked.
|
|
74
|
-
* @default null
|
|
75
|
-
*/
|
|
76
|
-
onItemClick: (el: HTMLLIElement) => void;
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
const _defaults: DropdownOptions = {
|
|
80
|
-
alignment: 'left',
|
|
81
|
-
autoFocus: true,
|
|
82
|
-
constrainWidth: true,
|
|
83
|
-
container: null,
|
|
84
|
-
coverTrigger: true,
|
|
85
|
-
closeOnClick: true,
|
|
86
|
-
hover: false,
|
|
87
|
-
inDuration: 150,
|
|
88
|
-
outDuration: 250,
|
|
89
|
-
onOpenStart: null,
|
|
90
|
-
onOpenEnd: null,
|
|
91
|
-
onCloseStart: null,
|
|
92
|
-
onCloseEnd: null,
|
|
93
|
-
onItemClick: null
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
export class Dropdown extends Component<DropdownOptions> implements Openable {
|
|
97
|
-
static _dropdowns: Dropdown[] = [];
|
|
98
|
-
/** ID of the dropdown element. */
|
|
99
|
-
id: string;
|
|
100
|
-
/** The DOM element of the dropdown. */
|
|
101
|
-
dropdownEl: HTMLElement;
|
|
102
|
-
/** If the dropdown is open. */
|
|
103
|
-
isOpen: boolean;
|
|
104
|
-
/** If the dropdown content is scrollable. */
|
|
105
|
-
isScrollable: boolean;
|
|
106
|
-
isTouchMoving: boolean;
|
|
107
|
-
/** The index of the item focused. */
|
|
108
|
-
focusedIndex: number;
|
|
109
|
-
filterQuery: any[];
|
|
110
|
-
filterTimeout: NodeJS.Timeout;
|
|
111
|
-
|
|
112
|
-
constructor(el: HTMLElement, options: Partial<DropdownOptions>) {
|
|
113
|
-
super(el, options, Dropdown);
|
|
114
|
-
(this.el as any).M_Dropdown = this;
|
|
115
|
-
|
|
116
|
-
Dropdown._dropdowns.push(this);
|
|
117
|
-
this.id = Utils.getIdFromTrigger(el);
|
|
118
|
-
this.dropdownEl = document.getElementById(this.id);
|
|
119
|
-
|
|
120
|
-
this.options = {
|
|
121
|
-
...Dropdown.defaults,
|
|
122
|
-
...options
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
this.isOpen = false;
|
|
126
|
-
this.isScrollable = false;
|
|
127
|
-
this.isTouchMoving = false;
|
|
128
|
-
this.focusedIndex = -1;
|
|
129
|
-
this.filterQuery = [];
|
|
130
|
-
|
|
131
|
-
// Move dropdown-content after dropdown-trigger
|
|
132
|
-
this._moveDropdown();
|
|
133
|
-
this._makeDropdownFocusable();
|
|
134
|
-
this._setupEventHandlers();
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
static get defaults(): DropdownOptions {
|
|
138
|
-
return _defaults;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Initializes instance of Dropdown.
|
|
143
|
-
* @param el HTML element.
|
|
144
|
-
* @param options Component options.
|
|
145
|
-
*/
|
|
146
|
-
static init(el: HTMLElement, options?: Partial<DropdownOptions>): Dropdown;
|
|
147
|
-
/**
|
|
148
|
-
* Initializes instances of Dropdown.
|
|
149
|
-
* @param els HTML elements.
|
|
150
|
-
* @param options Component options.
|
|
151
|
-
*/
|
|
152
|
-
static init(els: InitElements<MElement>, options?: Partial<DropdownOptions>): Dropdown[];
|
|
153
|
-
/**
|
|
154
|
-
* Initializes instances of Dropdown.
|
|
155
|
-
* @param els HTML elements.
|
|
156
|
-
* @param options Component options.
|
|
157
|
-
*/
|
|
158
|
-
static init(els: HTMLElement | InitElements<MElement>, options: Partial<DropdownOptions> = {}): Dropdown | Dropdown[] {
|
|
159
|
-
return super.init(els, options, Dropdown);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
static getInstance(el: HTMLElement): Dropdown {
|
|
163
|
-
return (el as any).M_Dropdown;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
destroy() {
|
|
167
|
-
this._resetDropdownStyles();
|
|
168
|
-
this._removeEventHandlers();
|
|
169
|
-
Dropdown._dropdowns.splice(Dropdown._dropdowns.indexOf(this), 1);
|
|
170
|
-
(this.el as any).M_Dropdown = undefined;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
_setupEventHandlers() {
|
|
174
|
-
// Trigger keydown handler
|
|
175
|
-
this.el.addEventListener('keydown', this._handleTriggerKeydown);
|
|
176
|
-
// Item click handler
|
|
177
|
-
this.dropdownEl?.addEventListener('click', this._handleDropdownClick);
|
|
178
|
-
// Hover event handlers
|
|
179
|
-
if (this.options.hover) {
|
|
180
|
-
this.el.addEventListener('mouseenter', this._handleMouseEnter);
|
|
181
|
-
this.el.addEventListener('mouseleave', this._handleMouseLeave);
|
|
182
|
-
this.dropdownEl.addEventListener('mouseleave', this._handleMouseLeave);
|
|
183
|
-
// Click event handlers
|
|
184
|
-
} else {
|
|
185
|
-
this.el.addEventListener('click', this._handleClick);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
_removeEventHandlers() {
|
|
190
|
-
this.el.removeEventListener('keydown', this._handleTriggerKeydown);
|
|
191
|
-
this.dropdownEl.removeEventListener('click', this._handleDropdownClick);
|
|
192
|
-
if (this.options.hover) {
|
|
193
|
-
this.el.removeEventListener('mouseenter', this._handleMouseEnter);
|
|
194
|
-
this.el.removeEventListener('mouseleave', this._handleMouseLeave);
|
|
195
|
-
this.dropdownEl.removeEventListener('mouseleave', this._handleMouseLeave);
|
|
196
|
-
} else {
|
|
197
|
-
this.el.removeEventListener('click', this._handleClick);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
_setupTemporaryEventHandlers() {
|
|
202
|
-
// Use capture phase event handler to prevent click
|
|
203
|
-
document.body.addEventListener('click', this._handleDocumentClick, true);
|
|
204
|
-
document.body.addEventListener('touchmove', this._handleDocumentTouchmove);
|
|
205
|
-
this.dropdownEl.addEventListener('keydown', this._handleDropdownKeydown);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
_removeTemporaryEventHandlers() {
|
|
209
|
-
// Use capture phase event handler to prevent click
|
|
210
|
-
document.body.removeEventListener('click', this._handleDocumentClick, true);
|
|
211
|
-
document.body.removeEventListener('touchmove', this._handleDocumentTouchmove);
|
|
212
|
-
this.dropdownEl.removeEventListener('keydown', this._handleDropdownKeydown);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
_handleClick = (e: MouseEvent) => {
|
|
216
|
-
e.preventDefault();
|
|
217
|
-
this.open();
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
_handleMouseEnter = () => {
|
|
221
|
-
this.open();
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
_handleMouseLeave = (e: MouseEvent) => {
|
|
225
|
-
const toEl = e.relatedTarget as HTMLElement;
|
|
226
|
-
const leaveToDropdownContent = !!toEl.closest('.dropdown-content');
|
|
227
|
-
let leaveToActiveDropdownTrigger = false;
|
|
228
|
-
const closestTrigger = toEl.closest('.dropdown-trigger');
|
|
229
|
-
if (
|
|
230
|
-
closestTrigger &&
|
|
231
|
-
!!(<any>closestTrigger).M_Dropdown &&
|
|
232
|
-
(<any>closestTrigger).M_Dropdown.isOpen
|
|
233
|
-
) {
|
|
234
|
-
leaveToActiveDropdownTrigger = true;
|
|
235
|
-
}
|
|
236
|
-
// Close hover dropdown if mouse did not leave to either active dropdown-trigger or dropdown-content
|
|
237
|
-
if (!leaveToActiveDropdownTrigger && !leaveToDropdownContent) {
|
|
238
|
-
this.close();
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
_handleDocumentClick = (e: MouseEvent) => {
|
|
243
|
-
const target = <HTMLElement>e.target;
|
|
244
|
-
if (
|
|
245
|
-
this.options.closeOnClick &&
|
|
246
|
-
target.closest('.dropdown-content') &&
|
|
247
|
-
!this.isTouchMoving
|
|
248
|
-
) {
|
|
249
|
-
// isTouchMoving to check if scrolling on mobile.
|
|
250
|
-
//setTimeout(() => {
|
|
251
|
-
this.close();
|
|
252
|
-
//}, 0);
|
|
253
|
-
}
|
|
254
|
-
else if (
|
|
255
|
-
target.closest('.dropdown-trigger') ||
|
|
256
|
-
!target.closest('.dropdown-content')
|
|
257
|
-
) {
|
|
258
|
-
//setTimeout(() => {
|
|
259
|
-
this.close();
|
|
260
|
-
//}, 0);
|
|
261
|
-
}
|
|
262
|
-
this.isTouchMoving = false;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
_handleTriggerKeydown = (e: KeyboardEvent) => {
|
|
266
|
-
// ARROW DOWN OR ENTER WHEN SELECT IS CLOSED - open Dropdown
|
|
267
|
-
const arrowDownOrEnter = Utils.keys.ARROW_DOWN.includes(e.key) || Utils.keys.ENTER.includes(e.key);
|
|
268
|
-
if (arrowDownOrEnter && !this.isOpen) {
|
|
269
|
-
e.preventDefault();
|
|
270
|
-
this.open();
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
_handleDocumentTouchmove = (e: TouchEvent) => {
|
|
275
|
-
const target = <HTMLElement>e.target;
|
|
276
|
-
if (target.closest('.dropdown-content')) {
|
|
277
|
-
this.isTouchMoving = true;
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
_handleDropdownClick = (e: MouseEvent) => {
|
|
282
|
-
// onItemClick callback
|
|
283
|
-
if (typeof this.options.onItemClick === 'function') {
|
|
284
|
-
const itemEl = (<HTMLElement>e.target).closest('li');
|
|
285
|
-
this.options.onItemClick.call(this, itemEl);
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
_handleDropdownKeydown = (e: KeyboardEvent) => {
|
|
290
|
-
const arrowUpOrDown = Utils.keys.ARROW_DOWN.includes(e.key) || Utils.keys.ARROW_UP.includes(e.key);
|
|
291
|
-
if (Utils.keys.TAB.includes(e.key)) {
|
|
292
|
-
e.preventDefault();
|
|
293
|
-
this.close();
|
|
294
|
-
}
|
|
295
|
-
// Navigate down dropdown list
|
|
296
|
-
else if (arrowUpOrDown && this.isOpen) {
|
|
297
|
-
e.preventDefault();
|
|
298
|
-
const direction = Utils.keys.ARROW_DOWN.includes(e.key) ? 1 : -1;
|
|
299
|
-
let newFocusedIndex = this.focusedIndex;
|
|
300
|
-
let hasFoundNewIndex = false;
|
|
301
|
-
do {
|
|
302
|
-
newFocusedIndex = newFocusedIndex + direction;
|
|
303
|
-
if (
|
|
304
|
-
!!this.dropdownEl.children[newFocusedIndex] &&
|
|
305
|
-
(<any>this.dropdownEl.children[newFocusedIndex]).tabIndex !== -1
|
|
306
|
-
) {
|
|
307
|
-
hasFoundNewIndex = true;
|
|
308
|
-
break;
|
|
309
|
-
}
|
|
310
|
-
} while (newFocusedIndex < this.dropdownEl.children.length && newFocusedIndex >= 0);
|
|
311
|
-
|
|
312
|
-
if (hasFoundNewIndex) {
|
|
313
|
-
// Remove active class from old element
|
|
314
|
-
if (this.focusedIndex >= 0)
|
|
315
|
-
this.dropdownEl.children[this.focusedIndex].classList.remove('active');
|
|
316
|
-
this.focusedIndex = newFocusedIndex;
|
|
317
|
-
this._focusFocusedItem();
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
// ENTER selects choice on focused item
|
|
321
|
-
else if (Utils.keys.ENTER.includes(e.key) && this.isOpen) {
|
|
322
|
-
// Search for <a> and <button>
|
|
323
|
-
const focusedElement = this.dropdownEl.children[this.focusedIndex];
|
|
324
|
-
const activatableElement = <HTMLElement>focusedElement.querySelector('a, button');
|
|
325
|
-
// Click a or button tag if exists, otherwise click li tag
|
|
326
|
-
if (!!activatableElement) {
|
|
327
|
-
activatableElement.click();
|
|
328
|
-
}
|
|
329
|
-
else if (!!focusedElement) {
|
|
330
|
-
if (focusedElement instanceof HTMLElement) {
|
|
331
|
-
focusedElement.click();
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
// Close dropdown on ESC
|
|
336
|
-
else if (Utils.keys.ESC.includes(e.key) && this.isOpen) {
|
|
337
|
-
e.preventDefault();
|
|
338
|
-
this.close();
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
// CASE WHEN USER TYPE LTTERS
|
|
342
|
-
const keyText = e.key.toLowerCase();
|
|
343
|
-
const isLetter = /[a-zA-Z0-9-_]/.test(keyText);
|
|
344
|
-
const specialKeys = [...Utils.keys.ARROW_DOWN, ...Utils.keys.ARROW_UP, ...Utils.keys.ENTER, ...Utils.keys.ESC, ...Utils.keys.TAB];
|
|
345
|
-
if (isLetter && !specialKeys.includes(e.key)) {
|
|
346
|
-
this.filterQuery.push(keyText);
|
|
347
|
-
const string = this.filterQuery.join('');
|
|
348
|
-
const newOptionEl = Array.from(this.dropdownEl.querySelectorAll('li'))
|
|
349
|
-
.find((el) => el.innerText.toLowerCase().indexOf(string) === 0);
|
|
350
|
-
if (newOptionEl) {
|
|
351
|
-
this.focusedIndex = [...newOptionEl.parentNode.children].indexOf(newOptionEl);
|
|
352
|
-
this._focusFocusedItem();
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
this.filterTimeout = setTimeout(this._resetFilterQuery, 1000);
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
_resetFilterQuery = () => {
|
|
359
|
-
this.filterQuery = [];
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
_resetDropdownStyles() {
|
|
363
|
-
this.dropdownEl.style.display = '';
|
|
364
|
-
this.dropdownEl.style.width = '';
|
|
365
|
-
this.dropdownEl.style.height = '';
|
|
366
|
-
this.dropdownEl.style.left = '';
|
|
367
|
-
this.dropdownEl.style.top = '';
|
|
368
|
-
this.dropdownEl.style.transformOrigin = '';
|
|
369
|
-
this.dropdownEl.style.transform = '';
|
|
370
|
-
this.dropdownEl.style.opacity = '';
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
// Move dropdown after container or trigger
|
|
374
|
-
_moveDropdown(containerEl: HTMLElement = null) {
|
|
375
|
-
if (!!this.options.container) {
|
|
376
|
-
this.options.container.append(this.dropdownEl);
|
|
377
|
-
}
|
|
378
|
-
else if (containerEl) {
|
|
379
|
-
if (!containerEl.contains(this.dropdownEl)) {
|
|
380
|
-
containerEl.append(this.dropdownEl);
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
else {
|
|
384
|
-
this.el.after(this.dropdownEl);
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
_makeDropdownFocusable() {
|
|
389
|
-
if (!this.dropdownEl) return;
|
|
390
|
-
// Needed for arrow key navigation
|
|
391
|
-
this.dropdownEl.tabIndex = 0;
|
|
392
|
-
// Only set tabindex if it hasn't been set by user
|
|
393
|
-
Array.from(this.dropdownEl.children).forEach((el)=> {
|
|
394
|
-
if (!el.getAttribute('tabindex'))
|
|
395
|
-
el.setAttribute('tabindex', '0');
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
_focusFocusedItem() {
|
|
400
|
-
if (
|
|
401
|
-
this.focusedIndex >= 0 &&
|
|
402
|
-
this.focusedIndex < this.dropdownEl.children.length &&
|
|
403
|
-
this.options.autoFocus
|
|
404
|
-
) {
|
|
405
|
-
(this.dropdownEl.children[this.focusedIndex] as HTMLElement).focus({
|
|
406
|
-
preventScroll: true
|
|
407
|
-
});
|
|
408
|
-
this.dropdownEl.children[this.focusedIndex].scrollIntoView({
|
|
409
|
-
behavior: 'smooth',
|
|
410
|
-
block: 'nearest',
|
|
411
|
-
inline: 'nearest'
|
|
412
|
-
});
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
_getDropdownPosition(closestOverflowParent: HTMLElement) {
|
|
417
|
-
const offsetParentBRect = this.el.offsetParent.getBoundingClientRect();
|
|
418
|
-
const triggerBRect = this.el.getBoundingClientRect();
|
|
419
|
-
const dropdownBRect = this.dropdownEl.getBoundingClientRect();
|
|
420
|
-
|
|
421
|
-
let idealHeight = dropdownBRect.height;
|
|
422
|
-
let idealWidth = dropdownBRect.width;
|
|
423
|
-
let idealXPos = triggerBRect.left - dropdownBRect.left;
|
|
424
|
-
let idealYPos = triggerBRect.top - dropdownBRect.top;
|
|
425
|
-
|
|
426
|
-
const dropdownBounds = {
|
|
427
|
-
left: idealXPos,
|
|
428
|
-
top: idealYPos,
|
|
429
|
-
height: idealHeight,
|
|
430
|
-
width: idealWidth
|
|
431
|
-
};
|
|
432
|
-
|
|
433
|
-
const alignments = Utils.checkPossibleAlignments(
|
|
434
|
-
this.el,
|
|
435
|
-
closestOverflowParent,
|
|
436
|
-
dropdownBounds,
|
|
437
|
-
this.options.coverTrigger ? 0 : triggerBRect.height
|
|
438
|
-
);
|
|
439
|
-
|
|
440
|
-
let verticalAlignment = 'top';
|
|
441
|
-
let horizontalAlignment = this.options.alignment;
|
|
442
|
-
idealYPos += this.options.coverTrigger ? 0 : triggerBRect.height;
|
|
443
|
-
|
|
444
|
-
// Reset isScrollable
|
|
445
|
-
this.isScrollable = false;
|
|
446
|
-
|
|
447
|
-
if (!alignments.top) {
|
|
448
|
-
if (alignments.bottom) {
|
|
449
|
-
verticalAlignment = 'bottom';
|
|
450
|
-
|
|
451
|
-
if (!this.options.coverTrigger) {
|
|
452
|
-
idealYPos -= triggerBRect.height;
|
|
453
|
-
}
|
|
454
|
-
} else {
|
|
455
|
-
this.isScrollable = true;
|
|
456
|
-
|
|
457
|
-
// Determine which side has most space and cutoff at correct height
|
|
458
|
-
idealHeight -= 20; // Add padding when cutoff
|
|
459
|
-
if (alignments.spaceOnTop > alignments.spaceOnBottom) {
|
|
460
|
-
verticalAlignment = 'bottom';
|
|
461
|
-
idealHeight += alignments.spaceOnTop;
|
|
462
|
-
idealYPos -= this.options.coverTrigger
|
|
463
|
-
? alignments.spaceOnTop - 20
|
|
464
|
-
: alignments.spaceOnTop - 20 + triggerBRect.height;
|
|
465
|
-
} else {
|
|
466
|
-
idealHeight += alignments.spaceOnBottom;
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
// If preferred horizontal alignment is possible
|
|
472
|
-
if (!alignments[horizontalAlignment]) {
|
|
473
|
-
const oppositeAlignment = horizontalAlignment === 'left' ? 'right' : 'left';
|
|
474
|
-
if (alignments[oppositeAlignment]) {
|
|
475
|
-
horizontalAlignment = oppositeAlignment;
|
|
476
|
-
} else {
|
|
477
|
-
// Determine which side has most space and cutoff at correct height
|
|
478
|
-
if (alignments.spaceOnLeft > alignments.spaceOnRight) {
|
|
479
|
-
horizontalAlignment = 'right';
|
|
480
|
-
idealWidth += alignments.spaceOnLeft;
|
|
481
|
-
idealXPos -= alignments.spaceOnLeft;
|
|
482
|
-
} else {
|
|
483
|
-
horizontalAlignment = 'left';
|
|
484
|
-
idealWidth += alignments.spaceOnRight;
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
if (verticalAlignment === 'bottom') {
|
|
490
|
-
idealYPos =
|
|
491
|
-
idealYPos - dropdownBRect.height + (this.options.coverTrigger ? triggerBRect.height : 0);
|
|
492
|
-
}
|
|
493
|
-
if (horizontalAlignment === 'right') {
|
|
494
|
-
idealXPos = idealXPos - dropdownBRect.width + triggerBRect.width;
|
|
495
|
-
}
|
|
496
|
-
return {
|
|
497
|
-
x: idealXPos,
|
|
498
|
-
y: idealYPos,
|
|
499
|
-
verticalAlignment: verticalAlignment,
|
|
500
|
-
horizontalAlignment: horizontalAlignment,
|
|
501
|
-
height: idealHeight,
|
|
502
|
-
width: idealWidth
|
|
503
|
-
};
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
_animateIn() {
|
|
507
|
-
anim.remove(this.dropdownEl);
|
|
508
|
-
anim({
|
|
509
|
-
targets: this.dropdownEl,
|
|
510
|
-
opacity: {
|
|
511
|
-
value: [0, 1],
|
|
512
|
-
easing: 'easeOutQuad'
|
|
513
|
-
},
|
|
514
|
-
scaleX: [0.3, 1],
|
|
515
|
-
scaleY: [0.3, 1],
|
|
516
|
-
duration: this.options.inDuration,
|
|
517
|
-
easing: 'easeOutQuint',
|
|
518
|
-
complete: (anim) => {
|
|
519
|
-
if (this.options.autoFocus) this.dropdownEl.focus();
|
|
520
|
-
// onOpenEnd callback
|
|
521
|
-
if (typeof this.options.onOpenEnd === 'function') {
|
|
522
|
-
this.options.onOpenEnd.call(this, this.el);
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
});
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
_animateOut() {
|
|
529
|
-
anim.remove(this.dropdownEl);
|
|
530
|
-
anim({
|
|
531
|
-
targets: this.dropdownEl,
|
|
532
|
-
opacity: {
|
|
533
|
-
value: 0,
|
|
534
|
-
easing: 'easeOutQuint'
|
|
535
|
-
},
|
|
536
|
-
scaleX: 0.3,
|
|
537
|
-
scaleY: 0.3,
|
|
538
|
-
duration: this.options.outDuration,
|
|
539
|
-
easing: 'easeOutQuint',
|
|
540
|
-
complete: (anim) => {
|
|
541
|
-
this._resetDropdownStyles();
|
|
542
|
-
// onCloseEnd callback
|
|
543
|
-
if (typeof this.options.onCloseEnd === 'function') {
|
|
544
|
-
this.options.onCloseEnd.call(this, this.el);
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
});
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
private _getClosestAncestor(el: HTMLElement, condition: Function): HTMLElement {
|
|
551
|
-
let ancestor = el.parentNode;
|
|
552
|
-
while (ancestor !== null && ancestor !== document) {
|
|
553
|
-
if (condition(ancestor)) {
|
|
554
|
-
return <HTMLElement>ancestor;
|
|
555
|
-
}
|
|
556
|
-
ancestor = ancestor.parentElement;
|
|
557
|
-
}
|
|
558
|
-
return null;
|
|
559
|
-
};
|
|
560
|
-
|
|
561
|
-
_placeDropdown() {
|
|
562
|
-
// Container here will be closest ancestor with overflow: hidden
|
|
563
|
-
let closestOverflowParent: HTMLElement = this._getClosestAncestor(this.dropdownEl, (ancestor: HTMLElement) => {
|
|
564
|
-
return !['HTML','BODY'].includes(ancestor.tagName) && getComputedStyle(ancestor).overflow !== 'visible';
|
|
565
|
-
});
|
|
566
|
-
// Fallback
|
|
567
|
-
if (!closestOverflowParent) {
|
|
568
|
-
closestOverflowParent = <HTMLElement>(!!this.dropdownEl.offsetParent
|
|
569
|
-
? this.dropdownEl.offsetParent
|
|
570
|
-
: this.dropdownEl.parentNode);
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
if (getComputedStyle(closestOverflowParent).position === 'static')
|
|
574
|
-
closestOverflowParent.style.position = 'relative';
|
|
575
|
-
|
|
576
|
-
this._moveDropdown(closestOverflowParent);
|
|
577
|
-
|
|
578
|
-
// Set width before calculating positionInfo
|
|
579
|
-
const idealWidth = this.options.constrainWidth
|
|
580
|
-
? this.el.getBoundingClientRect().width
|
|
581
|
-
: this.dropdownEl.getBoundingClientRect().width;
|
|
582
|
-
this.dropdownEl.style.width = idealWidth + 'px';
|
|
583
|
-
|
|
584
|
-
const positionInfo = this._getDropdownPosition(closestOverflowParent);
|
|
585
|
-
this.dropdownEl.style.left = positionInfo.x + 'px';
|
|
586
|
-
this.dropdownEl.style.top = positionInfo.y + 'px';
|
|
587
|
-
this.dropdownEl.style.height = positionInfo.height + 'px';
|
|
588
|
-
this.dropdownEl.style.width = positionInfo.width + 'px';
|
|
589
|
-
this.dropdownEl.style.transformOrigin = `${
|
|
590
|
-
positionInfo.horizontalAlignment === 'left' ? '0' : '100%'
|
|
591
|
-
} ${positionInfo.verticalAlignment === 'top' ? '0' : '100%'}`;
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
/**
|
|
595
|
-
* Open dropdown.
|
|
596
|
-
*/
|
|
597
|
-
open = () => {
|
|
598
|
-
if (this.isOpen) return;
|
|
599
|
-
this.isOpen = true;
|
|
600
|
-
// onOpenStart callback
|
|
601
|
-
if (typeof this.options.onOpenStart === 'function') {
|
|
602
|
-
this.options.onOpenStart.call(this, this.el);
|
|
603
|
-
}
|
|
604
|
-
// Reset styles
|
|
605
|
-
this._resetDropdownStyles();
|
|
606
|
-
this.dropdownEl.style.display = 'block';
|
|
607
|
-
this._placeDropdown();
|
|
608
|
-
this._animateIn();
|
|
609
|
-
this._setupTemporaryEventHandlers();
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
/**
|
|
613
|
-
* Close dropdown.
|
|
614
|
-
*/
|
|
615
|
-
close = () => {
|
|
616
|
-
if (!this.isOpen) return;
|
|
617
|
-
this.isOpen = false;
|
|
618
|
-
this.focusedIndex = -1;
|
|
619
|
-
// onCloseStart callback
|
|
620
|
-
if (typeof this.options.onCloseStart === 'function') {
|
|
621
|
-
this.options.onCloseStart.call(this, this.el);
|
|
622
|
-
}
|
|
623
|
-
this._animateOut();
|
|
624
|
-
this._removeTemporaryEventHandlers();
|
|
625
|
-
if (this.options.autoFocus) {
|
|
626
|
-
this.el.focus();
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
/**
|
|
631
|
-
* While dropdown is open, you can recalculate its dimensions if its contents have changed.
|
|
632
|
-
*/
|
|
633
|
-
recalculateDimensions = () => {
|
|
634
|
-
if (this.isOpen) {
|
|
635
|
-
this.dropdownEl.style.width = '';
|
|
636
|
-
this.dropdownEl.style.height = '';
|
|
637
|
-
this.dropdownEl.style.left = '';
|
|
638
|
-
this.dropdownEl.style.top = '';
|
|
639
|
-
this.dropdownEl.style.transformOrigin = '';
|
|
640
|
-
this._placeDropdown();
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
}
|
package/src/edges.ts
DELETED