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.
Files changed (150) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +55 -91
  3. package/components/appbar/_navbar.old.scss +223 -0
  4. package/components/appbar/appbar.css +235 -0
  5. package/components/appbar/appbar.mjs +54 -0
  6. package/components/appbar/appbar.test.mjs +9 -0
  7. package/components/atomic/atomic.mjs +19 -0
  8. package/components/atomic/component.mjs +67 -0
  9. package/components/badge/_badges.scss +75 -0
  10. package/components/badge/badge.mjs +11 -0
  11. package/components/breadcrumb/_breadcrumb.scss +27 -0
  12. package/components/button/_buttons.scss +352 -0
  13. package/components/button/button.mjs +11 -0
  14. package/components/button/buttons.stories.ts +150 -0
  15. package/components/button/buttons.ts +282 -0
  16. package/components/button/examples.iso.js +18 -0
  17. package/components/button/fabSpec.js +65 -0
  18. package/components/button/test-button.mjs +16 -0
  19. package/components/card/_cards.scss +229 -0
  20. package/components/card/card.mjs +10 -0
  21. package/components/card/cards.ts +185 -0
  22. package/components/card/cardsSpec.js +269 -0
  23. package/components/carousel/_carousel.scss +93 -0
  24. package/components/carousel/_slider.scss +119 -0
  25. package/components/carousel/carousel.ts +693 -0
  26. package/components/carousel/carouselSpec.js +61 -0
  27. package/components/carousel/slider.ts +431 -0
  28. package/components/carousel/sliderSpec.js +99 -0
  29. package/components/checkbox/_checkboxes.scss +281 -0
  30. package/components/checkbox/checkbox.mdx +24 -0
  31. package/components/checkbox/checkbox.stories.ts +120 -0
  32. package/components/chip/chip.mjs +37 -0
  33. package/components/chip/chip.test.mjs +10 -0
  34. package/components/chip/chips.css +126 -0
  35. package/components/chip/chips.mdx +33 -0
  36. package/components/chip/chips.ts +440 -0
  37. package/components/chip/chipsSpec.js +165 -0
  38. package/components/chip/readme.md +323 -0
  39. package/components/collapsible/_collapsible.scss +82 -0
  40. package/components/collapsible/collapsible.ts +230 -0
  41. package/components/collapsible/collapsibleSpec.js +240 -0
  42. package/components/datepicker/_datepicker.scss +268 -0
  43. package/components/datepicker/datepicker.ts +1450 -0
  44. package/components/datepicker/datepickerSpec.js +162 -0
  45. package/components/dialog/_materialbox.scss +42 -0
  46. package/components/dialog/_modal.scss +79 -0
  47. package/components/dialog/materialbox.ts +446 -0
  48. package/components/dialog/materialboxSpec.js +37 -0
  49. package/components/dialog/modal.ts +111 -0
  50. package/components/dialog/modalSpec.js +83 -0
  51. package/components/dialog/readme.md +5 -0
  52. package/components/divider/readme.md +15 -0
  53. package/components/dropdown/_dropdown.scss +83 -0
  54. package/components/dropdown/dropdown.ts +653 -0
  55. package/components/dropdown/dropdownSpec.js +108 -0
  56. package/components/list/_collection.scss +114 -0
  57. package/components/list/_list.scss +6 -0
  58. package/components/list/list.mjs +58 -0
  59. package/components/list/list.test.mjs +16 -0
  60. package/components/loading/loading.mjs +26 -0
  61. package/components/loading/loading.test.mjs +10 -0
  62. package/components/navigation-bar/_old +582 -0
  63. package/components/navigation-bar/navigation-bar.mdx +20 -0
  64. package/components/navigation-bar/navigation-bar.stories.ts +39 -0
  65. package/components/navigation-drawer/_sidenav.scss +247 -0
  66. package/components/navigation-drawer/sidenav.ts +546 -0
  67. package/components/navigation-drawer/sidenavSpec.js +124 -0
  68. package/components/pagination/_pagination.scss +47 -0
  69. package/components/progress/_preloader.scss +416 -0
  70. package/components/radio-button/_radio-buttons.scss +112 -0
  71. package/components/radio-button/radio-buttons.mdx +28 -0
  72. package/components/radio-button/radio-buttons.stories.ts +51 -0
  73. package/components/search/autocomplete.ts +620 -0
  74. package/components/search/autocompleteSpec.js +321 -0
  75. package/components/slider/_range.scss +157 -0
  76. package/components/slider/range.ts +216 -0
  77. package/components/snackbar/_toast.scss +68 -0
  78. package/components/snackbar/toastSpec.js +132 -0
  79. package/components/snackbar/toasts.ts +312 -0
  80. package/components/switch/_switches.scss +121 -0
  81. package/components/tabs/_tabs.scss +171 -0
  82. package/components/tabs/tabs.mjs +31 -0
  83. package/components/tabs/tabs.ts +358 -0
  84. package/components/tabs/tabsSpec.js +180 -0
  85. package/components/textfield/_file-input.scss +41 -0
  86. package/components/textfield/_input-fields.scss +368 -0
  87. package/components/textfield/_select.scss +104 -0
  88. package/components/textfield/formfields.css +311 -0
  89. package/components/textfield/forms.ts +182 -0
  90. package/components/textfield/formsSpec.js +142 -0
  91. package/components/textfield/select.ts +472 -0
  92. package/components/textfield/selectSpec.js +390 -0
  93. package/components/textfield/textfield.mjs +29 -0
  94. package/components/textfield/textfield.test.mjs +10 -0
  95. package/components/timepicker/_timepicker.scss +362 -0
  96. package/components/timepicker/timepicker.ts +905 -0
  97. package/components/tooltip/_tooltip.scss +40 -0
  98. package/components/tooltip/tooltip.ts +380 -0
  99. package/components/tooltip/tooltipSpec.js +145 -0
  100. package/dist/css/materialize.colors.min.css +6 -0
  101. package/dist/css/materialize.css +7578 -6326
  102. package/dist/css/materialize.min.css +6 -13
  103. package/dist/css/materialize.min.css.map +1 -0
  104. package/dist/js/materialize.cjs.js +8410 -0
  105. package/dist/js/materialize.d.ts +2625 -0
  106. package/dist/js/materialize.js +8415 -12374
  107. package/dist/js/materialize.min.js +6 -6
  108. package/dist/js/materialize.mjs +8382 -0
  109. package/package.json +82 -47
  110. package/sass/_colors.scss +417 -0
  111. package/sass/_global.scss +490 -0
  112. package/sass/_grid.scss +172 -0
  113. package/sass/_table_of_contents.scss +28 -0
  114. package/sass/_tapTarget.scss +102 -0
  115. package/sass/_typography.scss +58 -0
  116. package/sass/_variables.scss +56 -0
  117. package/sass/materialize.scss +51 -0
  118. package/sass/mixins.module.scss +168 -0
  119. package/extras/noUiSlider/nouislider.css +0 -406
  120. package/extras/noUiSlider/nouislider.js +0 -2147
  121. package/extras/noUiSlider/nouislider.min.js +0 -1
  122. package/js/anime.min.js +0 -34
  123. package/js/autocomplete.js +0 -448
  124. package/js/buttons.js +0 -354
  125. package/js/cards.js +0 -40
  126. package/js/carousel.js +0 -717
  127. package/js/cash.js +0 -960
  128. package/js/characterCounter.js +0 -136
  129. package/js/chips.js +0 -481
  130. package/js/collapsible.js +0 -275
  131. package/js/component.js +0 -44
  132. package/js/datepicker.js +0 -975
  133. package/js/dropdown.js +0 -635
  134. package/js/forms.js +0 -275
  135. package/js/global.js +0 -444
  136. package/js/materialbox.js +0 -453
  137. package/js/modal.js +0 -382
  138. package/js/parallax.js +0 -138
  139. package/js/pushpin.js +0 -148
  140. package/js/range.js +0 -263
  141. package/js/scrollspy.js +0 -295
  142. package/js/select.js +0 -450
  143. package/js/sidenav.js +0 -580
  144. package/js/slider.js +0 -359
  145. package/js/tabs.js +0 -402
  146. package/js/tapTarget.js +0 -314
  147. package/js/timepicker.js +0 -647
  148. package/js/toasts.js +0 -310
  149. package/js/tooltip.js +0 -303
  150. 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
+ });