kmaterialize 2.0.0 → 2.3.3-1.0.0
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/LICENSE +21 -21
- package/README.md +55 -91
- package/components/appbar/_navbar.old.scss +223 -0
- package/components/appbar/appbar.css +235 -0
- package/components/appbar/appbar.mjs +54 -0
- package/components/appbar/appbar.test.mjs +9 -0
- package/components/atomic/atomic.mjs +19 -0
- package/components/atomic/component.mjs +67 -0
- package/components/badge/_badges.scss +75 -0
- package/components/badge/badge.mjs +11 -0
- package/components/breadcrumb/_breadcrumb.scss +27 -0
- package/components/button/_buttons.scss +352 -0
- package/components/button/button.mjs +11 -0
- package/components/button/buttons.stories.ts +150 -0
- package/components/button/buttons.ts +282 -0
- package/components/button/examples.iso.js +18 -0
- package/components/button/fabSpec.js +65 -0
- package/components/button/test-button.mjs +16 -0
- package/components/card/_cards.scss +229 -0
- package/components/card/card.mjs +10 -0
- package/components/card/cards.ts +185 -0
- package/components/card/cardsSpec.js +269 -0
- package/components/carousel/_carousel.scss +93 -0
- package/components/carousel/_slider.scss +119 -0
- package/components/carousel/carousel.ts +693 -0
- package/components/carousel/carouselSpec.js +61 -0
- package/components/carousel/slider.ts +431 -0
- package/components/carousel/sliderSpec.js +99 -0
- package/components/checkbox/_checkboxes.scss +281 -0
- package/components/checkbox/checkbox.mdx +24 -0
- package/components/checkbox/checkbox.stories.ts +120 -0
- package/components/chip/chip.mjs +37 -0
- package/components/chip/chip.test.mjs +10 -0
- package/components/chip/chips.css +126 -0
- package/components/chip/chips.mdx +33 -0
- package/components/chip/chips.ts +440 -0
- package/components/chip/chipsSpec.js +165 -0
- package/components/chip/readme.md +323 -0
- package/components/collapsible/_collapsible.scss +82 -0
- package/components/collapsible/collapsible.ts +230 -0
- package/components/collapsible/collapsibleSpec.js +240 -0
- package/components/datepicker/_datepicker.scss +268 -0
- package/components/datepicker/datepicker.ts +1450 -0
- package/components/datepicker/datepickerSpec.js +162 -0
- package/components/dialog/_materialbox.scss +42 -0
- package/components/dialog/_modal.scss +79 -0
- package/components/dialog/materialbox.ts +446 -0
- package/components/dialog/materialboxSpec.js +37 -0
- package/components/dialog/modal.ts +111 -0
- package/components/dialog/modalSpec.js +83 -0
- package/components/dialog/readme.md +5 -0
- package/components/divider/readme.md +15 -0
- package/components/dropdown/_dropdown.scss +83 -0
- package/components/dropdown/dropdown.ts +653 -0
- package/components/dropdown/dropdownSpec.js +108 -0
- package/components/list/_collection.scss +114 -0
- package/components/list/_list.scss +6 -0
- package/components/list/list.mjs +58 -0
- package/components/list/list.test.mjs +16 -0
- package/components/loading/loading.mjs +26 -0
- package/components/loading/loading.test.mjs +10 -0
- package/components/navigation-bar/_old +582 -0
- package/components/navigation-bar/navigation-bar.mdx +20 -0
- package/components/navigation-bar/navigation-bar.stories.ts +39 -0
- package/components/navigation-drawer/_sidenav.scss +247 -0
- package/components/navigation-drawer/sidenav.ts +546 -0
- package/components/navigation-drawer/sidenavSpec.js +124 -0
- package/components/pagination/_pagination.scss +47 -0
- package/components/progress/_preloader.scss +416 -0
- package/components/radio-button/_radio-buttons.scss +112 -0
- package/components/radio-button/radio-buttons.mdx +28 -0
- package/components/radio-button/radio-buttons.stories.ts +51 -0
- package/components/search/autocomplete.ts +620 -0
- package/components/search/autocompleteSpec.js +321 -0
- package/components/slider/_range.scss +157 -0
- package/components/slider/range.ts +216 -0
- package/components/snackbar/_toast.scss +68 -0
- package/components/snackbar/toastSpec.js +132 -0
- package/components/snackbar/toasts.ts +312 -0
- package/components/switch/_switches.scss +121 -0
- package/components/tabs/_tabs.scss +171 -0
- package/components/tabs/tabs.mjs +31 -0
- package/components/tabs/tabs.ts +358 -0
- package/components/tabs/tabsSpec.js +180 -0
- package/components/textfield/_file-input.scss +41 -0
- package/components/textfield/_input-fields.scss +368 -0
- package/components/textfield/_select.scss +104 -0
- package/components/textfield/formfields.css +311 -0
- package/components/textfield/forms.ts +182 -0
- package/components/textfield/formsSpec.js +142 -0
- package/components/textfield/select.ts +472 -0
- package/components/textfield/selectSpec.js +390 -0
- package/components/textfield/textfield.mjs +29 -0
- package/components/textfield/textfield.test.mjs +10 -0
- package/components/timepicker/_timepicker.scss +362 -0
- package/components/timepicker/timepicker.ts +905 -0
- package/components/tooltip/_tooltip.scss +40 -0
- package/components/tooltip/tooltip.ts +380 -0
- package/components/tooltip/tooltipSpec.js +145 -0
- package/dist/css/materialize.colors.min.css +6 -0
- package/dist/css/materialize.css +7578 -6326
- package/dist/css/materialize.min.css +6 -13
- package/dist/css/materialize.min.css.map +1 -0
- package/dist/js/materialize.cjs.js +8410 -0
- package/dist/js/materialize.d.ts +2625 -0
- package/dist/js/materialize.js +8415 -12374
- package/dist/js/materialize.min.js +6 -6
- package/dist/js/materialize.mjs +8382 -0
- package/package.json +82 -47
- package/sass/_colors.scss +417 -0
- package/sass/_global.scss +490 -0
- package/sass/_grid.scss +172 -0
- package/sass/_table_of_contents.scss +28 -0
- package/sass/_tapTarget.scss +102 -0
- package/sass/_typography.scss +58 -0
- package/sass/_variables.scss +56 -0
- package/sass/materialize.scss +51 -0
- package/sass/mixins.module.scss +168 -0
- package/extras/noUiSlider/nouislider.css +0 -406
- package/extras/noUiSlider/nouislider.js +0 -2147
- package/extras/noUiSlider/nouislider.min.js +0 -1
- package/js/anime.min.js +0 -34
- package/js/autocomplete.js +0 -448
- package/js/buttons.js +0 -354
- package/js/cards.js +0 -40
- package/js/carousel.js +0 -717
- package/js/cash.js +0 -960
- package/js/characterCounter.js +0 -136
- package/js/chips.js +0 -481
- package/js/collapsible.js +0 -275
- package/js/component.js +0 -44
- package/js/datepicker.js +0 -975
- package/js/dropdown.js +0 -635
- package/js/forms.js +0 -275
- package/js/global.js +0 -444
- package/js/materialbox.js +0 -453
- package/js/modal.js +0 -382
- package/js/parallax.js +0 -138
- package/js/pushpin.js +0 -148
- package/js/range.js +0 -263
- package/js/scrollspy.js +0 -295
- package/js/select.js +0 -450
- package/js/sidenav.js +0 -580
- package/js/slider.js +0 -359
- package/js/tabs.js +0 -402
- package/js/tapTarget.js +0 -314
- package/js/timepicker.js +0 -647
- package/js/toasts.js +0 -310
- package/js/tooltip.js +0 -303
- package/js/waves.js +0 -335
|
@@ -0,0 +1,546 @@
|
|
|
1
|
+
import { Utils } from '../../src/utils';
|
|
2
|
+
import { Component, BaseOptions, InitElements, MElement, Openable } from '../../src/component';
|
|
3
|
+
|
|
4
|
+
export interface SidenavOptions extends BaseOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Side of screen on which Sidenav appears.
|
|
7
|
+
* @default 'left'
|
|
8
|
+
*/
|
|
9
|
+
edge: 'left' | 'right';
|
|
10
|
+
/**
|
|
11
|
+
* Allow swipe gestures to open/close Sidenav.
|
|
12
|
+
* @default true
|
|
13
|
+
*/
|
|
14
|
+
draggable: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Width of the area where you can start dragging.
|
|
17
|
+
* @default '10px'
|
|
18
|
+
*/
|
|
19
|
+
dragTargetWidth: string;
|
|
20
|
+
/**
|
|
21
|
+
* Length in ms of enter transition.
|
|
22
|
+
* @default 250
|
|
23
|
+
*/
|
|
24
|
+
inDuration: number;
|
|
25
|
+
/**
|
|
26
|
+
* Length in ms of exit transition.
|
|
27
|
+
* @default 200
|
|
28
|
+
*/
|
|
29
|
+
outDuration: number;
|
|
30
|
+
/**
|
|
31
|
+
* Prevent page from scrolling while sidenav is open.
|
|
32
|
+
* @default true
|
|
33
|
+
*/
|
|
34
|
+
preventScrolling: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Function called when sidenav starts entering.
|
|
37
|
+
*/
|
|
38
|
+
onOpenStart: (elem: HTMLElement) => void;
|
|
39
|
+
/**
|
|
40
|
+
* Function called when sidenav finishes entering.
|
|
41
|
+
*/
|
|
42
|
+
onOpenEnd: (elem: HTMLElement) => void;
|
|
43
|
+
/**
|
|
44
|
+
* Function called when sidenav starts exiting.
|
|
45
|
+
*/
|
|
46
|
+
onCloseStart: (elem: HTMLElement) => void;
|
|
47
|
+
/**
|
|
48
|
+
* Function called when sidenav finishes exiting.
|
|
49
|
+
*/
|
|
50
|
+
onCloseEnd: (elem: HTMLElement) => void;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const _defaults: SidenavOptions = {
|
|
54
|
+
edge: 'left',
|
|
55
|
+
draggable: true,
|
|
56
|
+
dragTargetWidth: '10px',
|
|
57
|
+
inDuration: 250,
|
|
58
|
+
outDuration: 200,
|
|
59
|
+
onOpenStart: null,
|
|
60
|
+
onOpenEnd: null,
|
|
61
|
+
onCloseStart: null,
|
|
62
|
+
onCloseEnd: null,
|
|
63
|
+
preventScrolling: true
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export class Sidenav extends Component<SidenavOptions> implements Openable {
|
|
67
|
+
id: string;
|
|
68
|
+
/** Describes open/close state of Sidenav. */
|
|
69
|
+
isOpen: boolean;
|
|
70
|
+
/** Describes if sidenav is fixed. */
|
|
71
|
+
isFixed: boolean;
|
|
72
|
+
/** Describes if Sidenav is being dragged. */
|
|
73
|
+
isDragged: boolean;
|
|
74
|
+
lastWindowWidth: number;
|
|
75
|
+
lastWindowHeight: number;
|
|
76
|
+
static _sidenavs: Sidenav[];
|
|
77
|
+
private _overlay: HTMLElement;
|
|
78
|
+
dragTarget: Element;
|
|
79
|
+
private _startingXpos: number;
|
|
80
|
+
private _xPos: number;
|
|
81
|
+
private _time: number;
|
|
82
|
+
private _width: number;
|
|
83
|
+
private _initialScrollTop: number;
|
|
84
|
+
private _verticallyScrolling: boolean;
|
|
85
|
+
private deltaX: number;
|
|
86
|
+
private velocityX: number;
|
|
87
|
+
private percentOpen: number;
|
|
88
|
+
|
|
89
|
+
constructor(el: HTMLElement, options: Partial<SidenavOptions>) {
|
|
90
|
+
super(el, options, Sidenav);
|
|
91
|
+
this.el['M_Sidenav'] = this;
|
|
92
|
+
|
|
93
|
+
this.options = {
|
|
94
|
+
...Sidenav.defaults,
|
|
95
|
+
...options
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
this.id = this.el.id;
|
|
99
|
+
this.isOpen = false;
|
|
100
|
+
this.isFixed = this.el.classList.contains('sidenav-fixed');
|
|
101
|
+
this.isDragged = false;
|
|
102
|
+
// Window size variables for window resize checks
|
|
103
|
+
this.lastWindowWidth = window.innerWidth;
|
|
104
|
+
this.lastWindowHeight = window.innerHeight;
|
|
105
|
+
this._createOverlay();
|
|
106
|
+
this._createDragTarget();
|
|
107
|
+
this._setupEventHandlers();
|
|
108
|
+
this._setupClasses();
|
|
109
|
+
this._setupFixed();
|
|
110
|
+
Sidenav._sidenavs.push(this);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
static get defaults(): SidenavOptions {
|
|
114
|
+
return _defaults;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Initializes instance of Sidenav.
|
|
119
|
+
* @param el HTML element.
|
|
120
|
+
* @param options Component options.
|
|
121
|
+
*/
|
|
122
|
+
static init(el: HTMLElement, options?: Partial<SidenavOptions>): Sidenav;
|
|
123
|
+
/**
|
|
124
|
+
* Initializes instances of Sidenav.
|
|
125
|
+
* @param els HTML elements.
|
|
126
|
+
* @param options Component options.
|
|
127
|
+
*/
|
|
128
|
+
static init(els: InitElements<MElement>, options?: Partial<SidenavOptions>): Sidenav[];
|
|
129
|
+
/**
|
|
130
|
+
* Initializes instances of Sidenav.
|
|
131
|
+
* @param els HTML elements.
|
|
132
|
+
* @param options Component options.
|
|
133
|
+
*/
|
|
134
|
+
static init(
|
|
135
|
+
els: HTMLElement | InitElements<MElement>,
|
|
136
|
+
options: Partial<SidenavOptions> = {}
|
|
137
|
+
): Sidenav | Sidenav[] {
|
|
138
|
+
return super.init(els, options, Sidenav);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
static getInstance(el: HTMLElement): Sidenav {
|
|
142
|
+
return el['M_Sidenav'];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
destroy() {
|
|
146
|
+
this._removeEventHandlers();
|
|
147
|
+
this._enableBodyScrolling();
|
|
148
|
+
this._overlay.parentNode.removeChild(this._overlay);
|
|
149
|
+
this.dragTarget.parentNode.removeChild(this.dragTarget);
|
|
150
|
+
this.el['M_Sidenav'] = undefined;
|
|
151
|
+
this.el.style.transform = '';
|
|
152
|
+
const index = Sidenav._sidenavs.indexOf(this);
|
|
153
|
+
if (index >= 0) {
|
|
154
|
+
Sidenav._sidenavs.splice(index, 1);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
private _createOverlay() {
|
|
159
|
+
this._overlay = document.createElement('div');
|
|
160
|
+
this._overlay.classList.add('sidenav-overlay');
|
|
161
|
+
this._overlay.addEventListener('click', this.close);
|
|
162
|
+
document.body.appendChild(this._overlay);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
private _setupEventHandlers() {
|
|
166
|
+
if (Sidenav._sidenavs.length === 0) {
|
|
167
|
+
document.body.addEventListener('click', this._handleTriggerClick);
|
|
168
|
+
}
|
|
169
|
+
const passiveIfSupported: boolean = null;
|
|
170
|
+
this.dragTarget.addEventListener('touchmove', this._handleDragTargetDrag, passiveIfSupported);
|
|
171
|
+
this.dragTarget.addEventListener('touchend', this._handleDragTargetRelease);
|
|
172
|
+
this._overlay.addEventListener('touchmove', this._handleCloseDrag, passiveIfSupported);
|
|
173
|
+
this._overlay.addEventListener('touchend', this._handleCloseRelease);
|
|
174
|
+
this.el.addEventListener('touchmove', this._handleCloseDrag); // , passiveIfSupported);
|
|
175
|
+
this.el.addEventListener('touchend', this._handleCloseRelease);
|
|
176
|
+
this.el.addEventListener('click', this._handleCloseTriggerClick);
|
|
177
|
+
// Add resize for side nav fixed
|
|
178
|
+
if (this.isFixed) {
|
|
179
|
+
window.addEventListener('resize', this._handleWindowResize);
|
|
180
|
+
}
|
|
181
|
+
/* Set aria-hidden state */
|
|
182
|
+
this._setAriaHidden();
|
|
183
|
+
this._setTabIndex();
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
private _removeEventHandlers() {
|
|
187
|
+
if (Sidenav._sidenavs.length === 1) {
|
|
188
|
+
document.body.removeEventListener('click', this._handleTriggerClick);
|
|
189
|
+
}
|
|
190
|
+
this.dragTarget.removeEventListener('touchmove', this._handleDragTargetDrag);
|
|
191
|
+
this.dragTarget.removeEventListener('touchend', this._handleDragTargetRelease);
|
|
192
|
+
this._overlay.removeEventListener('touchmove', this._handleCloseDrag);
|
|
193
|
+
this._overlay.removeEventListener('touchend', this._handleCloseRelease);
|
|
194
|
+
this.el.removeEventListener('touchmove', this._handleCloseDrag);
|
|
195
|
+
this.el.removeEventListener('touchend', this._handleCloseRelease);
|
|
196
|
+
this.el.removeEventListener('click', this._handleCloseTriggerClick);
|
|
197
|
+
|
|
198
|
+
// Remove resize for side nav fixed
|
|
199
|
+
if (this.isFixed) {
|
|
200
|
+
window.removeEventListener('resize', this._handleWindowResize);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
private _handleTriggerClick(e) {
|
|
205
|
+
const trigger = e.target.closest('.sidenav-trigger');
|
|
206
|
+
if (e.target && trigger) {
|
|
207
|
+
const sidenavId = Utils.getIdFromTrigger(trigger);
|
|
208
|
+
const sidenavInstance = document.getElementById(sidenavId)['M_Sidenav'];
|
|
209
|
+
if (sidenavInstance) {
|
|
210
|
+
sidenavInstance.open();
|
|
211
|
+
}
|
|
212
|
+
e.preventDefault();
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Set variables needed at the beginning of drag and stop any current transition.
|
|
217
|
+
private _startDrag(e) {
|
|
218
|
+
const clientX = e.targetTouches[0].clientX;
|
|
219
|
+
this.isDragged = true;
|
|
220
|
+
this._startingXpos = clientX;
|
|
221
|
+
this._xPos = this._startingXpos;
|
|
222
|
+
this._time = Date.now();
|
|
223
|
+
this._width = this.el.getBoundingClientRect().width;
|
|
224
|
+
this._overlay.style.display = 'block';
|
|
225
|
+
this._initialScrollTop = this.isOpen ? this.el.scrollTop : Utils.getDocumentScrollTop();
|
|
226
|
+
this._verticallyScrolling = false;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
//Set variables needed at each drag move update tick
|
|
230
|
+
private _dragMoveUpdate(e) {
|
|
231
|
+
const clientX = e.targetTouches[0].clientX;
|
|
232
|
+
const currentScrollTop = this.isOpen ? this.el.scrollTop : Utils.getDocumentScrollTop();
|
|
233
|
+
this.deltaX = Math.abs(this._xPos - clientX);
|
|
234
|
+
this._xPos = clientX;
|
|
235
|
+
this.velocityX = this.deltaX / (Date.now() - this._time);
|
|
236
|
+
this._time = Date.now();
|
|
237
|
+
if (this._initialScrollTop !== currentScrollTop) {
|
|
238
|
+
this._verticallyScrolling = true;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
private _handleDragTargetDrag = (e) => {
|
|
243
|
+
// Check if draggable
|
|
244
|
+
if (!this._isDraggable()) return;
|
|
245
|
+
let totalDeltaX = this._calculateDelta(e);
|
|
246
|
+
const dragDirection = totalDeltaX > 0 ? 'right' : 'left';
|
|
247
|
+
// Don't allow totalDeltaX to exceed Sidenav width or be dragged in the opposite direction
|
|
248
|
+
totalDeltaX = Math.min(this._width, Math.abs(totalDeltaX));
|
|
249
|
+
if (this.options.edge === dragDirection) {
|
|
250
|
+
totalDeltaX = 0;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* transformX is the drag displacement
|
|
254
|
+
* transformPrefix is the initial transform placement
|
|
255
|
+
* Invert values if Sidenav is right edge
|
|
256
|
+
*/
|
|
257
|
+
let transformX = totalDeltaX;
|
|
258
|
+
let transformPrefix = 'translateX(-100%)';
|
|
259
|
+
if (this.options.edge === 'right') {
|
|
260
|
+
transformPrefix = 'translateX(100%)';
|
|
261
|
+
transformX = -transformX;
|
|
262
|
+
}
|
|
263
|
+
// Calculate open/close percentage of sidenav, with open = 1 and close = 0
|
|
264
|
+
this.percentOpen = Math.min(1, totalDeltaX / this._width);
|
|
265
|
+
// Set transform and opacity styles
|
|
266
|
+
this.el.style.transform = `${transformPrefix} translateX(${transformX}px)`;
|
|
267
|
+
this._overlay.style.opacity = this.percentOpen.toString();
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
private _handleDragTargetRelease = () => {
|
|
271
|
+
if (this.isDragged) {
|
|
272
|
+
if (this.percentOpen > 0.2) {
|
|
273
|
+
this.open();
|
|
274
|
+
} else {
|
|
275
|
+
this._animateOut();
|
|
276
|
+
}
|
|
277
|
+
this.isDragged = false;
|
|
278
|
+
this._verticallyScrolling = false;
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
private _handleCloseDrag = (e) => {
|
|
283
|
+
// Check if open and draggable
|
|
284
|
+
if (!this.isOpen || !this._isDraggable()) return;
|
|
285
|
+
let totalDeltaX = this._calculateDelta(e);
|
|
286
|
+
// dragDirection is the attempted user drag direction
|
|
287
|
+
const dragDirection = totalDeltaX > 0 ? 'right' : 'left';
|
|
288
|
+
totalDeltaX = Math.min(this._width, Math.abs(totalDeltaX));
|
|
289
|
+
if (this.options.edge !== dragDirection) {
|
|
290
|
+
totalDeltaX = 0;
|
|
291
|
+
}
|
|
292
|
+
let transformX = -totalDeltaX;
|
|
293
|
+
if (this.options.edge === 'right') {
|
|
294
|
+
transformX = -transformX;
|
|
295
|
+
}
|
|
296
|
+
// Calculate open/close percentage of sidenav, with open = 1 and close = 0
|
|
297
|
+
this.percentOpen = Math.min(1, 1 - totalDeltaX / this._width);
|
|
298
|
+
// Set transform and opacity styles
|
|
299
|
+
this.el.style.transform = `translateX(${transformX}px)`;
|
|
300
|
+
this._overlay.style.opacity = this.percentOpen.toString();
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
private _calculateDelta = (e) => {
|
|
304
|
+
// If not being dragged, set initial drag start variables
|
|
305
|
+
if (!this.isDragged) {
|
|
306
|
+
this._startDrag(e);
|
|
307
|
+
}
|
|
308
|
+
// Run touchmove updates
|
|
309
|
+
this._dragMoveUpdate(e);
|
|
310
|
+
// Calculate raw deltaX
|
|
311
|
+
return this._xPos - this._startingXpos;
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
private _handleCloseRelease = () => {
|
|
315
|
+
if (this.isOpen && this.isDragged) {
|
|
316
|
+
if (this.percentOpen > 0.8) {
|
|
317
|
+
this._animateIn();
|
|
318
|
+
} else {
|
|
319
|
+
this.close();
|
|
320
|
+
}
|
|
321
|
+
this.isDragged = false;
|
|
322
|
+
this._verticallyScrolling = false;
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
// Handles closing of Sidenav when element with class .sidenav-close
|
|
327
|
+
private _handleCloseTriggerClick = (e) => {
|
|
328
|
+
const closeTrigger = e.target.closest('.sidenav-close');
|
|
329
|
+
if (closeTrigger && !this._isCurrentlyFixed()) {
|
|
330
|
+
this.close();
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
private _handleWindowResize = () => {
|
|
335
|
+
// Only handle horizontal resizes
|
|
336
|
+
if (this.lastWindowWidth !== window.innerWidth) {
|
|
337
|
+
if (window.innerWidth > 992) {
|
|
338
|
+
this.open();
|
|
339
|
+
} else {
|
|
340
|
+
this.close();
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
this.lastWindowWidth = window.innerWidth;
|
|
344
|
+
this.lastWindowHeight = window.innerHeight;
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
private _setupClasses() {
|
|
348
|
+
if (this.options.edge === 'right') {
|
|
349
|
+
this.el.classList.add('right-aligned');
|
|
350
|
+
this.dragTarget.classList.add('right-aligned');
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
private _removeClasses() {
|
|
355
|
+
this.el.classList.remove('right-aligned');
|
|
356
|
+
this.dragTarget.classList.remove('right-aligned');
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
private _setupFixed() {
|
|
360
|
+
if (this._isCurrentlyFixed()) this.open();
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
private _isDraggable() {
|
|
364
|
+
return this.options.draggable && !this._isCurrentlyFixed() && !this._verticallyScrolling;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
private _isCurrentlyFixed() {
|
|
368
|
+
return this.isFixed && window.innerWidth > 992;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
private _createDragTarget() {
|
|
372
|
+
const dragTarget = document.createElement('div');
|
|
373
|
+
dragTarget.classList.add('drag-target');
|
|
374
|
+
dragTarget.style.width = this.options.dragTargetWidth;
|
|
375
|
+
document.body.appendChild(dragTarget);
|
|
376
|
+
this.dragTarget = dragTarget;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
private _preventBodyScrolling() {
|
|
380
|
+
document.body.style.overflow = 'hidden';
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
private _enableBodyScrolling() {
|
|
384
|
+
document.body.style.overflow = '';
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Opens Sidenav.
|
|
389
|
+
*/
|
|
390
|
+
open = () => {
|
|
391
|
+
if (this.isOpen === true) return;
|
|
392
|
+
this.isOpen = true;
|
|
393
|
+
// Run onOpenStart callback
|
|
394
|
+
if (typeof this.options.onOpenStart === 'function') {
|
|
395
|
+
this.options.onOpenStart.call(this, this.el);
|
|
396
|
+
}
|
|
397
|
+
// Handle fixed Sidenav
|
|
398
|
+
if (this._isCurrentlyFixed()) {
|
|
399
|
+
// Show if fixed
|
|
400
|
+
this.el.style.transform = 'translateX(0)';
|
|
401
|
+
this._enableBodyScrolling();
|
|
402
|
+
this._overlay.style.display = 'none';
|
|
403
|
+
}
|
|
404
|
+
// Handle non-fixed Sidenav
|
|
405
|
+
else {
|
|
406
|
+
if (this.options.preventScrolling) this._preventBodyScrolling();
|
|
407
|
+
if (!this.isDragged || this.percentOpen != 1) this._animateIn();
|
|
408
|
+
/* Set aria-hidden state */
|
|
409
|
+
this._setAriaHidden();
|
|
410
|
+
this._setTabIndex();
|
|
411
|
+
}
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Closes Sidenav.
|
|
416
|
+
*/
|
|
417
|
+
close = () => {
|
|
418
|
+
if (this.isOpen === false) return;
|
|
419
|
+
this.isOpen = false;
|
|
420
|
+
// Run onCloseStart callback
|
|
421
|
+
if (typeof this.options.onCloseStart === 'function') {
|
|
422
|
+
this.options.onCloseStart.call(this, this.el);
|
|
423
|
+
}
|
|
424
|
+
// Handle fixed Sidenav
|
|
425
|
+
if (this._isCurrentlyFixed()) {
|
|
426
|
+
const transformX = this.options.edge === 'left' ? '-105%' : '105%';
|
|
427
|
+
this.el.style.transform = `translateX(${transformX})`;
|
|
428
|
+
}
|
|
429
|
+
// Handle non-fixed Sidenav
|
|
430
|
+
else {
|
|
431
|
+
this._enableBodyScrolling();
|
|
432
|
+
if (!this.isDragged || this.percentOpen != 0) {
|
|
433
|
+
this._animateOut();
|
|
434
|
+
} else {
|
|
435
|
+
this._overlay.style.display = 'none';
|
|
436
|
+
}
|
|
437
|
+
/* Set aria-hidden state */
|
|
438
|
+
this._setAriaHidden();
|
|
439
|
+
this._setTabIndex();
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
private _animateIn() {
|
|
444
|
+
this._animateSidenavIn();
|
|
445
|
+
this._animateOverlayIn();
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
private _animateOut() {
|
|
449
|
+
this._animateSidenavOut();
|
|
450
|
+
this._animateOverlayOut();
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
private _animateSidenavIn() {
|
|
454
|
+
let slideOutPercent = this.options.edge === 'left' ? -1 : 1;
|
|
455
|
+
if (this.isDragged) {
|
|
456
|
+
slideOutPercent =
|
|
457
|
+
this.options.edge === 'left'
|
|
458
|
+
? slideOutPercent + this.percentOpen
|
|
459
|
+
: slideOutPercent - this.percentOpen;
|
|
460
|
+
}
|
|
461
|
+
const duration = this.options.inDuration;
|
|
462
|
+
// from
|
|
463
|
+
this.el.style.transition = 'none';
|
|
464
|
+
this.el.style.transform = 'translateX(' + slideOutPercent * 100 + '%)';
|
|
465
|
+
setTimeout(() => {
|
|
466
|
+
this.el.style.transition = `transform ${duration}ms ease`; // easeOutQuad
|
|
467
|
+
// to
|
|
468
|
+
this.el.style.transform = 'translateX(0)';
|
|
469
|
+
}, 1);
|
|
470
|
+
setTimeout(() => {
|
|
471
|
+
if (typeof this.options.onOpenEnd === 'function') this.options.onOpenEnd.call(this, this.el);
|
|
472
|
+
}, duration);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
private _animateSidenavOut() {
|
|
476
|
+
const endPercent = this.options.edge === 'left' ? -1 : 1;
|
|
477
|
+
// let slideOutPercent = 0;
|
|
478
|
+
// if (this.isDragged) {
|
|
479
|
+
// // @todo unused variable
|
|
480
|
+
// slideOutPercent =
|
|
481
|
+
// this.options.edge === 'left'
|
|
482
|
+
// ? endPercent + this.percentOpen
|
|
483
|
+
// : endPercent - this.percentOpen;
|
|
484
|
+
// }
|
|
485
|
+
|
|
486
|
+
const duration = this.options.outDuration;
|
|
487
|
+
this.el.style.transition = `transform ${duration}ms ease`; // easeOutQuad
|
|
488
|
+
// to
|
|
489
|
+
this.el.style.transform = 'translateX(' + endPercent * 100 + '%)';
|
|
490
|
+
setTimeout(() => {
|
|
491
|
+
if (typeof this.options.onCloseEnd === 'function')
|
|
492
|
+
this.options.onCloseEnd.call(this, this.el);
|
|
493
|
+
}, duration);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
private _animateOverlayIn() {
|
|
497
|
+
let start = 0;
|
|
498
|
+
if (this.isDragged) start = this.percentOpen;
|
|
499
|
+
else this._overlay.style.display = 'block';
|
|
500
|
+
// Animation
|
|
501
|
+
const duration = this.options.inDuration;
|
|
502
|
+
// from
|
|
503
|
+
this._overlay.style.transition = 'none';
|
|
504
|
+
this._overlay.style.opacity = start.toString();
|
|
505
|
+
// easeOutQuad
|
|
506
|
+
setTimeout(() => {
|
|
507
|
+
this._overlay.style.transition = `opacity ${duration}ms ease`;
|
|
508
|
+
// to
|
|
509
|
+
this._overlay.style.opacity = '1';
|
|
510
|
+
}, 1);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
private _animateOverlayOut() {
|
|
514
|
+
const duration = this.options.outDuration;
|
|
515
|
+
// easeOutQuad
|
|
516
|
+
this._overlay.style.transition = `opacity ${duration}ms ease`;
|
|
517
|
+
// to
|
|
518
|
+
this._overlay.style.opacity = '0';
|
|
519
|
+
setTimeout(() => {
|
|
520
|
+
this._overlay.style.display = 'none';
|
|
521
|
+
}, duration);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
private _setAriaHidden = () => {
|
|
525
|
+
this.el.ariaHidden = this.isOpen ? 'false' : 'true';
|
|
526
|
+
const navWrapper = document.querySelector('.nav-wrapper ul') as HTMLUListElement;
|
|
527
|
+
if (navWrapper) navWrapper.ariaHidden = this.isOpen.toString();
|
|
528
|
+
};
|
|
529
|
+
|
|
530
|
+
private _setTabIndex = () => {
|
|
531
|
+
const navLinks = document.querySelectorAll('.nav-wrapper ul li a');
|
|
532
|
+
const sideNavLinks = document.querySelectorAll('.sidenav li a');
|
|
533
|
+
if (navLinks)
|
|
534
|
+
navLinks.forEach((navLink: HTMLAnchorElement) => {
|
|
535
|
+
navLink.tabIndex = this.isOpen ? -1 : 0;
|
|
536
|
+
});
|
|
537
|
+
if (sideNavLinks)
|
|
538
|
+
sideNavLinks.forEach((sideNavLink: HTMLAnchorElement) => {
|
|
539
|
+
sideNavLink.tabIndex = this.isOpen ? 0 : -1;
|
|
540
|
+
});
|
|
541
|
+
};
|
|
542
|
+
|
|
543
|
+
static {
|
|
544
|
+
Sidenav._sidenavs = [];
|
|
545
|
+
}
|
|
546
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
describe('Sidenav Plugin', () => {
|
|
2
|
+
const fixture = `<ul id="slide-out" class="sidenav">
|
|
3
|
+
<li><div class="user-view">
|
|
4
|
+
<div class="background">
|
|
5
|
+
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==">
|
|
6
|
+
</div>
|
|
7
|
+
<a href="#!user"><img class="circle" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="></a>
|
|
8
|
+
<a href="#!name"><span class="white-text name">John Doe</span></a>
|
|
9
|
+
<a href="#!email"><span class="white-text email">jdoe@example.com</span></a>
|
|
10
|
+
</div></li>
|
|
11
|
+
<li><a href="#!"><i class="material-icons">cloud</i>First Link With Icon</a></li>
|
|
12
|
+
<li><a href="#!">Second Link</a></li>
|
|
13
|
+
<li><div class="divider"></div></li>
|
|
14
|
+
<li><a class="subheader">Subheader</a></li>
|
|
15
|
+
<li><a class="waves-effect" href="#!">Third Link With Waves</a></li>
|
|
16
|
+
</ul>
|
|
17
|
+
<a href="#" data-target="slide-out" class="sidenav-trigger"><i class="material-icons">menu</i></a>
|
|
18
|
+
`;
|
|
19
|
+
|
|
20
|
+
beforeEach(() => XloadHtml(fixture));
|
|
21
|
+
afterEach(() => XunloadFixtures());
|
|
22
|
+
|
|
23
|
+
describe('Sidenav', () => {
|
|
24
|
+
afterEach(() => {
|
|
25
|
+
if (M.Sidenav._sidenavs.length)
|
|
26
|
+
M.Sidenav.getInstance(document.querySelector('#slide-out')).destroy();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should not break from multiple initializations', () => {
|
|
30
|
+
expect(M.Sidenav._sidenavs.length).toEqual(0, 'no sidenavs initialized');
|
|
31
|
+
M.Sidenav.init(document.querySelector('#slide-out'));
|
|
32
|
+
M.Sidenav.init(document.querySelector('#slide-out'));
|
|
33
|
+
M.Sidenav.init(document.querySelector('#slide-out'));
|
|
34
|
+
expect(M.Sidenav._sidenavs.length).toEqual(
|
|
35
|
+
1,
|
|
36
|
+
'only 1 sidenav initialized after multiple calls on the same element'
|
|
37
|
+
);
|
|
38
|
+
const dragTarget = document.querySelectorAll('.drag-target');
|
|
39
|
+
expect(dragTarget.length).toEqual(1, 'Should generate only one dragTarget.');
|
|
40
|
+
const overlay = document.querySelectorAll('.sidenav-overlay');
|
|
41
|
+
expect(overlay.length).toEqual(1, 'Should generate only one overlay.');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should open sidenav from left', (done) => {
|
|
45
|
+
const normalActivator = document.querySelector('.sidenav-trigger');
|
|
46
|
+
const normalSidenav = document.querySelector('.sidenav');
|
|
47
|
+
const slideOutSlidenav = M.Sidenav.init(document.querySelector('#slide-out'), {
|
|
48
|
+
inDuration: 0,
|
|
49
|
+
outDuration: 0
|
|
50
|
+
});
|
|
51
|
+
const overlay = document.querySelectorAll('.sidenav-overlay');
|
|
52
|
+
const dragTarget = document.querySelectorAll('.drag-target');
|
|
53
|
+
let sidenavRect = normalSidenav.getBoundingClientRect();
|
|
54
|
+
expect(dragTarget.length).toEqual(1, 'Should generate only one dragTarget.');
|
|
55
|
+
expect(overlay.length).toEqual(1, 'Should generate only one overlay.');
|
|
56
|
+
expect(sidenavRect.left).toEqual(
|
|
57
|
+
-sidenavRect.width * 1.05,
|
|
58
|
+
'Should be hidden before sidenav is opened.'
|
|
59
|
+
);
|
|
60
|
+
click(normalActivator);
|
|
61
|
+
setTimeout(() => {
|
|
62
|
+
sidenavRect = normalSidenav.getBoundingClientRect();
|
|
63
|
+
expect(sidenavRect.left).toEqual(0, 'Should be shown after sidenav is closed.');
|
|
64
|
+
click(slideOutSlidenav._overlay);
|
|
65
|
+
done();
|
|
66
|
+
}, 10);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should have working callbacks', (done) => {
|
|
70
|
+
const normalActivator = document.querySelector('.sidenav-trigger');
|
|
71
|
+
let openStart = false;
|
|
72
|
+
let openEnd = false;
|
|
73
|
+
let closeStart = false;
|
|
74
|
+
let closeEnd = false;
|
|
75
|
+
const sidenav = M.Sidenav.init(document.querySelector('#slide-out'), {
|
|
76
|
+
inDuration: 0,
|
|
77
|
+
outDuration: 0,
|
|
78
|
+
onOpenStart: () => {
|
|
79
|
+
openStart = true;
|
|
80
|
+
},
|
|
81
|
+
onOpenEnd: () => {
|
|
82
|
+
openEnd = true;
|
|
83
|
+
},
|
|
84
|
+
onCloseStart: () => {
|
|
85
|
+
closeStart = true;
|
|
86
|
+
},
|
|
87
|
+
onCloseEnd: () => {
|
|
88
|
+
closeEnd = true;
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
const overlay = sidenav._overlay;
|
|
92
|
+
click(normalActivator);
|
|
93
|
+
expect(openStart).toEqual(true, 'Open start should fire immediately after open');
|
|
94
|
+
expect(openEnd).toEqual(false, 'Open end should not fire immediately after open');
|
|
95
|
+
setTimeout(() => {
|
|
96
|
+
expect(openEnd).toEqual(true, 'Open end should fire after open animation');
|
|
97
|
+
click(overlay);
|
|
98
|
+
expect(closeStart).toEqual(true, 'Close start should fire immediately after close');
|
|
99
|
+
expect(closeEnd).toEqual(false, 'Close end should not fire immediately after close');
|
|
100
|
+
setTimeout(() => {
|
|
101
|
+
expect(closeEnd).toEqual(true, 'Close end should fire after close animation');
|
|
102
|
+
done();
|
|
103
|
+
}, 10);
|
|
104
|
+
}, 10);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('should destroy correctly', function (done) {
|
|
108
|
+
expect(M.Sidenav._sidenavs.length).toEqual(0, 'no sidenavs initialized');
|
|
109
|
+
const sidenav = M.Sidenav.init(document.querySelector('#slide-out'));
|
|
110
|
+
const overlay = sidenav._overlay;
|
|
111
|
+
const dragTarget = sidenav.dragTarget;
|
|
112
|
+
expect(M.Sidenav._sidenavs.length).toEqual(1, 'one sidenav initialized');
|
|
113
|
+
expect(document.contains(overlay)).toEqual(true, 'overlay should be in DOM');
|
|
114
|
+
expect(document.contains(dragTarget)).toEqual(true, 'dragTarget should be in DOM');
|
|
115
|
+
sidenav.destroy();
|
|
116
|
+
setTimeout(() => {
|
|
117
|
+
expect(M.Sidenav._sidenavs.length).toEqual(0, 'sidenav destroyed');
|
|
118
|
+
expect(document.contains(overlay)).toBeFalse('overlay should be deleted');
|
|
119
|
+
expect(document.contains(dragTarget)).toBeFalse('dragTarget should be deleted');
|
|
120
|
+
done();
|
|
121
|
+
}, 10);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
});
|