• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IgniteUI / igniteui-angular / 13331632524

14 Feb 2025 02:51PM CUT coverage: 22.015% (-69.6%) from 91.622%
13331632524

Pull #15372

github

web-flow
Merge d52d57714 into bcb78ae0a
Pull Request #15372: chore(*): test ci passing

1990 of 15592 branches covered (12.76%)

431 of 964 new or added lines in 18 files covered. (44.71%)

19956 existing lines in 307 files now uncovered.

6452 of 29307 relevant lines covered (22.02%)

249.17 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.92
/projects/igniteui-angular/src/lib/navigation-drawer/navigation-drawer.component.ts
1
import {
2
    AfterContentInit,
3
    Component,
4
    ContentChild,
5
    ElementRef,
6
    EventEmitter,
7
    HostBinding,
8
    Inject,
9
    Input,
10
    OnChanges,
11
    OnDestroy,
12
    OnInit,
13
    Optional,
14
    Output,
15
    SimpleChange,
16
    ViewChild,
17
    Renderer2,
18
    booleanAttribute
19
} from '@angular/core';
20
import { fromEvent, interval, Subscription } from 'rxjs';
21
import { debounce } from 'rxjs/operators';
22
import { IgxNavigationService, IToggleView } from '../core/navigation';
23
import { HammerGesturesManager } from '../core/touch';
24
import { IgxNavDrawerMiniTemplateDirective, IgxNavDrawerTemplateDirective, IgxNavDrawerItemDirective } from './navigation-drawer.directives';
25
import { PlatformUtil } from '../core/utils';
26
import { NgTemplateOutlet } from '@angular/common';
27
import { HammerInput } from '../core/touch-annotations';
28

29
let NEXT_ID = 0;
2✔
30
/**
31
 * **Ignite UI for Angular Navigation Drawer** -
32
 * [Documentation](https://www.infragistics.com/products/ignite-ui-angular/angular/components/navdrawer)
33
 *
34
 * The Ignite UI Navigation Drawer is a collapsible side navigation container commonly used in combination with the Navbar.
35
 *
36
 * Example:
37
 * ```html
38
 * <igx-nav-drawer id="navigation" [isOpen]="true">
39
 *   <ng-template igxDrawer>
40
 *     <nav>
41
 *       <span igxDrawerItem [isHeader]="true">Email</span>
42
 *       <span igxDrawerItem igxRipple>Inbox</span>
43
 *       <span igxDrawerItem igxRipple>Deleted</span>
44
 *       <span igxDrawerItem igxRipple>Sent</span>
45
 *     </nav>
46
 *   </ng-template>
47
 * </igx-nav-drawer>
48
 * ```
49
 */
50
@Component({
51
    providers: [HammerGesturesManager],
52
    selector: 'igx-nav-drawer',
53
    templateUrl: 'navigation-drawer.component.html',
54
    styles: [`
55
        :host {
56
            display: block;
57
            height: 100%;
58
        }
59
    `],
60
    imports: [IgxNavDrawerItemDirective, NgTemplateOutlet]
61
})
62
export class IgxNavigationDrawerComponent implements
2✔
63
    IToggleView,
64
    OnInit,
65
    AfterContentInit,
66
    OnDestroy,
67
    OnChanges {
68

69
    /** @hidden @internal */
70
    @HostBinding('class.igx-nav-drawer')
UNCOV
71
    public cssClass = true;
×
72

73
    /**
74
     * ID of the component
75
     *
76
     * ```typescript
77
     * // get
78
     * let myNavDrawerId = this.navdrawer.id;
79
     * ```
80
     *
81
     * ```html
82
     * <!--set-->
83
     *  <igx-nav-drawer id='navdrawer'></igx-nav-drawer>
84
     * ```
85
     */
86
    @HostBinding('attr.id')
UNCOV
87
    @Input() public id = `igx-nav-drawer-${NEXT_ID++}`;
×
88

89
    /**
90
     * Position of the Navigation Drawer. Can be "left"(default) or "right".
91
     *
92
     * ```typescript
93
     * // get
94
     * let myNavDrawerPosition = this.navdrawer.position;
95
     * ```
96
     *
97
     * ```html
98
     * <!--set-->
99
     * <igx-nav-drawer [position]="'left'"></igx-nav-drawer>
100
     * ```
101
     */
UNCOV
102
    @Input() public position = 'left';
×
103

104
    /**
105
     * Enables the use of touch gestures to manipulate the drawer:
106
     * - swipe/pan from edge to open, swipe-toggle and pan-drag.
107
     *
108
     * ```typescript
109
     * // get
110
     * let gesturesEnabled = this.navdrawer.enableGestures;
111
     * ```
112
     *
113
     * ```html
114
     * <!--set-->
115
     * <igx-nav-drawer [enableGestures]='true'></igx-nav-drawer>
116
     * ```
117
     */
UNCOV
118
    @Input({ transform: booleanAttribute }) public enableGestures = true;
×
119

120
    /**
121
     * @hidden
122
     */
UNCOV
123
    @Output() public isOpenChange = new EventEmitter<boolean>();
×
124

125
    /**
126
     * Minimum device width required for automatic pin to be toggled.
127
     * Default is 1024, can be set to a falsy value to disable this behavior.
128
     *
129
     * ```typescript
130
     * // get
131
     * let navDrawerPinThreshold = this.navdrawer.pinThreshold;
132
     * ```
133
     *
134
     * ```html
135
     * <!--set-->
136
     * <igx-nav-drawer [pinThreshold]='1024'></igx-nav-drawer>
137
     * ```
138
     */
UNCOV
139
    @Input() public pinThreshold = 1024;
×
140

141
    /**
142
     * When pinned the drawer is relatively positioned instead of sitting above content.
143
     * May require additional layout styling.
144
     *
145
     * ```typescript
146
     * // get
147
     * let navDrawerIsPinned = this.navdrawer.pin;
148
     * ```
149
     *
150
     * ```html
151
     * <!--set-->
152
     * <igx-nav-drawer [pin]='false'></igx-nav-drawer>
153
     * ```
154
     */
UNCOV
155
    @Input({ transform: booleanAttribute }) public pin = false;
×
156

157
    /**
158
     * Width of the drawer in its open state.
159
     *
160
     * ```typescript
161
     * // get
162
     * let navDrawerWidth = this.navdrawer.width;
163
     * ```
164
     *
165
     * ```html
166
     * <!--set-->
167
     * <igx-nav-drawer [width]="'228px'"></igx-nav-drawer>
168
     * ```
169
     */
170
    private _width: string;
171

172
    @Input()
173
    public get width() {
UNCOV
174
        return this._width;
×
175
    }
176
    public set width(value: string) {
UNCOV
177
        this._width = value;
×
178
    }
179

180

181
    /**
182
     * Enables/disables the animation, when toggling the drawer. Set to `false` by default.
183
     * ````html
184
     * <igx-nav-drawer [disableAnimation]="true"></igx-nav-drawer>
185
     * ````
186
     */
187
    @HostBinding('class.igx-nav-drawer--disable-animation')
UNCOV
188
    @Input({ transform: booleanAttribute }) public disableAnimation = false;
×
189

190
    /**
191
     * Width of the drawer in its mini state.
192
     *
193
     * ```typescript
194
     * // get
195
     * let navDrawerMiniWidth = this.navdrawer.miniWidth;
196
     * ```
197
     *
198
     * ```html
199
     * <!--set-->
200
     * <igx-nav-drawer [miniWidth]="'34px'"></igx-nav-drawer>
201
     * ```
202
     */
203
    @Input() public miniWidth: string;
204

205
    /**
206
     * Pinned state change output for two-way binding.
207
     *
208
     * ```html
209
     * <igx-nav-drawer [(pin)]='isPinned'></igx-nav-drawer>
210
     * ```
211
     */
UNCOV
212
    @Output() public pinChange = new EventEmitter<boolean>(true);
×
213
    /**
214
     * Event fired as the Navigation Drawer is about to open.
215
     *
216
     * ```html
217
     *  <igx-nav-drawer (opening)='onOpening()'></igx-nav-drawer>
218
     * ```
219
     */
UNCOV
220
    @Output() public opening = new EventEmitter();
×
221
    /**
222
     * Event fired when the Navigation Drawer has opened.
223
     *
224
     * ```html
225
     * <igx-nav-drawer (opened)='onOpened()'></igx-nav-drawer>
226
     * ```
227
     */
UNCOV
228
    @Output() public opened = new EventEmitter();
×
229
    /**
230
     * Event fired as the Navigation Drawer is about to close.
231
     *
232
     * ```html
233
     * <igx-nav-drawer (closing)='onClosing()'></igx-nav-drawer>
234
     * ```
235
     */
UNCOV
236
    @Output() public closing = new EventEmitter();
×
237
    /**
238
     * Event fired when the Navigation Drawer has closed.
239
     *
240
     * ```html
241
     * <igx-nav-drawer (closed)='onClosed()'></igx-nav-drawer>
242
     * ```
243
     */
UNCOV
244
    @Output() public closed = new EventEmitter();
×
245

246
    /**
247
     * @hidden
248
     */
249
    @ContentChild(IgxNavDrawerTemplateDirective, { read: IgxNavDrawerTemplateDirective })
250
    protected contentTemplate: IgxNavDrawerTemplateDirective;
251

252
    @ViewChild('aside', { static: true }) private _drawer: ElementRef;
253
    @ViewChild('overlay', { static: true }) private _overlay: ElementRef;
254
    @ViewChild('dummy', { static: true }) private _styleDummy: ElementRef;
255

UNCOV
256
    private _isOpen = false;
×
257

258
    /**
259
     * State of the drawer.
260
     *
261
     * ```typescript
262
     * // get
263
     * let navDrawerIsOpen = this.navdrawer.isOpen;
264
     * ```
265
     *
266
     * ```html
267
     * <!--set-->
268
     * <igx-nav-drawer [isOpen]='false'></igx-nav-drawer>
269
     * ```
270
     *
271
     * Two-way data binding.
272
     * ```html
273
     * <!--set-->
274
     * <igx-nav-drawer [(isOpen)]='model.isOpen'></igx-nav-drawer>
275
     * ```
276
     */
277
    @Input({ transform: booleanAttribute })
278
    public get isOpen() {
UNCOV
279
        return this._isOpen;
×
280
    }
281
    public set isOpen(value) {
UNCOV
282
        this._isOpen = value;
×
UNCOV
283
        this.isOpenChange.emit(this._isOpen);
×
284
    }
285

286
    /**
287
     * Returns nativeElement of the component.
288
     *
289
     * @hidden
290
     */
291
    public get element() {
UNCOV
292
        return this.elementRef.nativeElement;
×
293
    }
294

295
    /**
296
     * @hidden
297
     */
298
    public get template() {
UNCOV
299
        if (this.miniTemplate && !this.isOpen) {
×
UNCOV
300
            return this.miniTemplate.template;
×
UNCOV
301
        } else if (this.contentTemplate) {
×
UNCOV
302
            return this.contentTemplate.template;
×
303
        }
304
    }
305

306
    private _miniTemplate: IgxNavDrawerMiniTemplateDirective;
307
    /**
308
     * @hidden
309
     */
310
    public get miniTemplate(): IgxNavDrawerMiniTemplateDirective {
UNCOV
311
        return this._miniTemplate;
×
312
    }
313

314
    /**
315
     * @hidden
316
     */
317
    @ContentChild(IgxNavDrawerMiniTemplateDirective, { read: IgxNavDrawerMiniTemplateDirective })
318
    public set miniTemplate(v: IgxNavDrawerMiniTemplateDirective) {
UNCOV
319
        this._miniTemplate = v;
×
320
    }
321

322
    /** @hidden @internal */
323
    @HostBinding('class.igx-nav-drawer--mini')
324
    public get isMini(): boolean {
UNCOV
325
        return !!this._miniTemplate && !this.isOpen;
×
326
    }
327

328
    /** @hidden @internal */
329
    @HostBinding('class.igx-nav-drawer--pinned')
330
    public get pinned(): boolean {
UNCOV
331
        return !!this.pin;
×
332
    }
333

334
    /**
335
     * @hidden
336
     */
337
    @HostBinding('style.--igx-nav-drawer-size')
338
    public get normalSize() {
UNCOV
339
        if (!this.isOpen) {
×
UNCOV
340
            return '0px';
×
341
        }
342

UNCOV
343
        return this.width;
×
344
    }
345

346
    /**
347
     * @hidden
348
     */
349
    @HostBinding('style.--igx-nav-drawer-size--mini')
350
    public get miniSize() {
UNCOV
351
        if (this.miniTemplate && this.miniWidth) {
×
UNCOV
352
            return this.miniWidth;
×
353
        }
354
    }
355

356
    /** @hidden */
357
    @HostBinding('style.order')
358
    public get isPinnedRight() {
UNCOV
359
        return this.pin && this.position === 'right' ? '1' : '0';
×
360
    }
361

UNCOV
362
    private _gesturesAttached = false;
×
UNCOV
363
    private _widthCache: { width: number; miniWidth: number; windowWidth: number } = { width: null, miniWidth: null, windowWidth: null };
×
364
    private _resizeObserver: Subscription;
UNCOV
365
    private css: { [name: string]: string } = {
×
366
        drawer: 'igx-nav-drawer__aside',
367
        mini: 'igx-nav-drawer__aside--mini',
368
        overlay: 'igx-nav-drawer__overlay',
369
        styleDummy: 'igx-nav-drawer__style-dummy'
370
    };
371

372
    /**
373
     * @hidden
374
     */
375
    public get drawer() {
UNCOV
376
        return this._drawer.nativeElement;
×
377
    }
378

379
    /**
380
     * @hidden
381
     */
382
    public get overlay() {
UNCOV
383
        return this._overlay.nativeElement;
×
384
    }
385

386
    /**
387
     * @hidden
388
     */
389
    public get styleDummy() {
UNCOV
390
        return this._styleDummy.nativeElement;
×
391
    }
392

393
    /** Pan animation properties */
UNCOV
394
    private _panning = false;
×
395
    private _panStartWidth: number;
396
    private _panLimit: number;
397

398
    /**
399
     * Property to decide whether to change width or translate the drawer from pan gesture.
400
     *
401
     * @hidden
402
     */
403
    public get hasAnimateWidth(): boolean {
UNCOV
404
        return this.pin || !!this.miniTemplate;
×
405
    }
406

UNCOV
407
    private _maxEdgeZone = 50;
×
408
    /**
409
     * Used for touch gestures (swipe and pan).
410
     * Defaults to 50 (in px) and is extended to at least 110% of the mini template width if available.
411
     *
412
     * @hidden
413
     */
414
    public get maxEdgeZone() {
UNCOV
415
        return this._maxEdgeZone;
×
416
    }
417

418
    /**
419
     * Gets the Drawer width for specific state.
420
     * Will attempt to evaluate requested state and cache.
421
     *
422
     *
423
     * @hidden
424
     */
425
    public get expectedWidth() {
426
        return this.getExpectedWidth(false);
×
427
    }
428

429
    /**
430
     * Get the Drawer mini width for specific state.
431
     * Will attempt to evaluate requested state and cache.
432
     *
433
     * @hidden
434
     */
435
    public get expectedMiniWidth() {
436
        return this.getExpectedWidth(true);
×
437
    }
438

439
    /**
440
     * @hidden
441
     */
442
    public get touchManager() {
UNCOV
443
        return this._touchManager;
×
444
    }
445

446
    /**
447
     * Exposes optional navigation service
448
     *
449
     * @hidden
450
     */
451
    public get state() {
UNCOV
452
        return this._state;
×
453
    }
454

455
    constructor(
UNCOV
456
        @Inject(ElementRef) private elementRef: ElementRef,
×
UNCOV
457
        @Optional() private _state: IgxNavigationService,
×
UNCOV
458
        protected renderer: Renderer2,
×
UNCOV
459
        private _touchManager: HammerGesturesManager,
×
UNCOV
460
        private platformUtil: PlatformUtil) {
×
461
    }
462

463
    /**
464
     * @hidden
465
     */
466
    public ngOnInit() {
467
        // DOM and @Input()-s initialized
UNCOV
468
        if (this._state) {
×
UNCOV
469
            this._state.add(this.id, this);
×
470
        }
471
    }
472

473
    /**
474
     * @hidden
475
     */
476
    public ngAfterContentInit() {
477
        // wait for template and ng-content to be ready
UNCOV
478
        this.updateEdgeZone();
×
UNCOV
479
        this.checkPinThreshold();
×
480

UNCOV
481
        this.ensureEvents();
×
482

483
        // TODO: apply platform-safe Ruler from http://plnkr.co/edit/81nWDyreYMzkunihfRgX?p=preview
484
        // (https://github.com/angular/angular/issues/6515), blocked by https://github.com/angular/angular/issues/6904
485
    }
486

487
    /**
488
     * @hidden
489
     */
490
    public ngOnDestroy() {
UNCOV
491
        this._touchManager.destroy();
×
UNCOV
492
        if (this._state) {
×
UNCOV
493
            this._state.remove(this.id);
×
494
        }
UNCOV
495
        if (this._resizeObserver) {
×
UNCOV
496
            this._resizeObserver.unsubscribe();
×
497
        }
498
    }
499

500
    /**
501
     * @hidden
502
     */
503
    public ngOnChanges(changes: { [propName: string]: SimpleChange }) {
504
        // simple settings can come from attribute set (rather than binding), make sure boolean props are converted
UNCOV
505
        if (changes.enableGestures && changes.enableGestures.currentValue !== undefined) {
×
UNCOV
506
            this.enableGestures = !!(this.enableGestures && this.enableGestures.toString() === 'true');
×
UNCOV
507
            this.ensureEvents();
×
508
        }
UNCOV
509
        if (changes.pin && changes.pin.currentValue !== undefined) {
×
UNCOV
510
            this.pin = !!(this.pin && this.pin.toString() === 'true');
×
UNCOV
511
            if (this.pin) {
×
UNCOV
512
                this._touchManager.destroy();
×
UNCOV
513
                this._gesturesAttached = false;
×
514
            } else {
UNCOV
515
                this.ensureEvents();
×
516
            }
517
        }
518

UNCOV
519
        if (changes.pinThreshold) {
×
UNCOV
520
            if (this.pinThreshold) {
×
UNCOV
521
                this.ensureEvents();
×
UNCOV
522
                this.checkPinThreshold();
×
523
            }
524
        }
525

UNCOV
526
        if (changes.miniWidth) {
×
UNCOV
527
            this.updateEdgeZone();
×
528
        }
529
    }
530

531
    /**
532
     * Toggle the open state of the Navigation Drawer.
533
     *
534
     * ```typescript
535
     * this.navdrawer.toggle();
536
     * ```
537
     */
538
    public toggle() {
UNCOV
539
        if (this.isOpen) {
×
UNCOV
540
            this.close();
×
541
        } else {
UNCOV
542
            this.open();
×
543
        }
544
    }
545

546
    /**
547
     * Open the Navigation Drawer. Has no effect if already opened.
548
     *
549
     * ```typescript
550
     * this.navdrawer.open();
551
     * ```
552
     */
553
    public open() {
UNCOV
554
        if (this._panning) {
×
555
            this.resetPan();
×
556
        }
557

UNCOV
558
        if (this.isOpen) {
×
UNCOV
559
            return;
×
560
        }
561

UNCOV
562
        this.opening.emit();
×
UNCOV
563
        this.isOpen = true;
×
564

565
        // TODO: Switch to animate API when available
566
        // var animationCss = this.animate.css();
567
        //     animationCss
568
        //         .setStyles({'width':'50px'}, {'width':'400px'})
569
        //         .start(this.elementRef.nativeElement)
570
        //         .onComplete(() => animationCss.setToStyles({'width':'auto'}).start(this.elementRef.nativeElement));
571

UNCOV
572
        this.elementRef.nativeElement.addEventListener('transitionend', this.toggleOpenedEvent, false);
×
573

UNCOV
574
        requestAnimationFrame(()=>{});
×
575
    }
576

577
    /**
578
     * Close the Navigation Drawer. Has no effect if already closed.
579
     *
580
     * ```typescript
581
     * this.navdrawer.close();
582
     * ```
583
     */
584
    public close() {
UNCOV
585
        if (this._panning) {
×
586
            this.resetPan();
×
587
        }
588

UNCOV
589
        if (!this.isOpen) {
×
UNCOV
590
            return;
×
591
        }
592

UNCOV
593
        this.closing.emit();
×
594

UNCOV
595
        this.isOpen = false;
×
UNCOV
596
        this.elementRef.nativeElement.addEventListener('transitionend', this.toggleClosedEvent, false);
×
597
    }
598

599
    /**
600
     * @hidden
601
     */
602
    protected set_maxEdgeZone(value: number) {
UNCOV
603
        this._maxEdgeZone = value;
×
604
    }
605

606
    /**
607
     * Get the Drawer width for specific state. Will attempt to evaluate requested state and cache.
608
     *
609
     * @hidden
610
     * @param [mini] - Request mini width instead
611
     */
612
    protected getExpectedWidth(mini?: boolean): number {
UNCOV
613
        if (mini) {
×
UNCOV
614
            if (!this.miniTemplate) {
×
UNCOV
615
                return 0;
×
616
            }
UNCOV
617
            if (this.miniWidth) {
×
UNCOV
618
                return parseFloat(this.miniWidth);
×
619
            } else {
620
                // if (!this.isOpen) { // This WON'T work due to transition timings...
621
                //     return this.elementRef.nativeElement.children[1].offsetWidth;
622
                // } else {
UNCOV
623
                if (this._widthCache.miniWidth === null) {
×
624
                    // force class for width calc. TODO?
625
                    // force class for width calc. TODO?
UNCOV
626
                    this.renderer.addClass(this.styleDummy, this.css.drawer);
×
UNCOV
627
                    this.renderer.addClass(this.styleDummy, this.css.mini);
×
UNCOV
628
                    this._widthCache.miniWidth = this.styleDummy.offsetWidth;
×
UNCOV
629
                    this.renderer.removeClass(this.styleDummy, this.css.drawer);
×
UNCOV
630
                    this.renderer.removeClass(this.styleDummy, this.css.mini);
×
631
                }
UNCOV
632
                return this._widthCache.miniWidth;
×
633
            }
634
        } else {
UNCOV
635
            if (this.width) {
×
UNCOV
636
                return parseFloat(this.width);
×
637
            } else {
UNCOV
638
                if (this._widthCache.width === null) {
×
639
                    // force class for width calc. TODO?
640
                    // force class for width calc. TODO?
UNCOV
641
                    this.renderer.addClass(this.styleDummy, this.css.drawer);
×
UNCOV
642
                    this._widthCache.width = this.styleDummy.offsetWidth;
×
UNCOV
643
                    this.renderer.removeClass(this.styleDummy, this.css.drawer);
×
644
                }
UNCOV
645
                return this._widthCache.width;
×
646
            }
647
        }
648
    }
649

650
    private getWindowWidth() {
UNCOV
651
        return (window.innerWidth > 0) ? window.innerWidth : screen.width;
×
652
    }
653

654
    /**
655
     * Get current Drawer width.
656
     */
657
    private getDrawerWidth(): number {
658
        return this.drawer.offsetWidth;
×
659
    }
660

661
    private ensureEvents() {
662
        // set listeners for swipe/pan only if needed, but just once
UNCOV
663
        if (this.enableGestures && !this.pin && !this._gesturesAttached) {
×
664
            // Built-in manager handler(L20887) causes endless loop and max stack exception.
665
            // https://github.com/angular/angular/issues/6993
666
            // Use ours for now (until beta.10):
667
            // this.renderer.listen(document, "swipe", this.swipe);
UNCOV
668
            this._touchManager.addGlobalEventListener('document', 'swipe', this.swipe);
×
UNCOV
669
            this._gesturesAttached = true;
×
670

671
            // this.renderer.listen(document, "panstart", this.panstart);
672
            // this.renderer.listen(document, "pan", this.pan);
UNCOV
673
            this._touchManager.addGlobalEventListener('document', 'panstart', this.panstart);
×
UNCOV
674
            this._touchManager.addGlobalEventListener('document', 'panmove', this.pan);
×
UNCOV
675
            this._touchManager.addGlobalEventListener('document', 'panend', this.panEnd);
×
676
        }
UNCOV
677
        if (!this._resizeObserver && this.platformUtil.isBrowser) {
×
UNCOV
678
            this._resizeObserver = fromEvent(window, 'resize').pipe(debounce(() => interval(150)))
×
679
                .subscribe((value) => {
UNCOV
680
                    this.checkPinThreshold(value);
×
681
                });
682
        }
683
    }
684

685
    private updateEdgeZone() {
686
        let maxValue;
687

UNCOV
688
        if (this.miniTemplate) {
×
UNCOV
689
            maxValue = Math.max(this._maxEdgeZone, this.getExpectedWidth(true) * 1.1);
×
UNCOV
690
            this.set_maxEdgeZone(maxValue);
×
691
        }
692
    }
693

UNCOV
694
    private checkPinThreshold = (evt?: Event) => {
×
UNCOV
695
        if (!this.platformUtil.isBrowser) {
×
696
            return;
×
697
        }
698
        let windowWidth;
UNCOV
699
        if (this.pinThreshold) {
×
UNCOV
700
            windowWidth = this.getWindowWidth();
×
UNCOV
701
            if (evt && this._widthCache.windowWidth === windowWidth) {
×
UNCOV
702
                return;
×
703
            }
UNCOV
704
            this._widthCache.windowWidth = windowWidth;
×
UNCOV
705
            if (!this.pin && windowWidth >= this.pinThreshold) {
×
UNCOV
706
                this.pin = true;
×
UNCOV
707
                this.pinChange.emit(true);
×
UNCOV
708
            } else if (this.pin && windowWidth < this.pinThreshold) {
×
UNCOV
709
                this.pin = false;
×
UNCOV
710
                this.pinChange.emit(false);
×
711
            }
712
        }
713
    };
714

UNCOV
715
    private swipe = (evt: HammerInput) => {
×
716
        // TODO: Could also force input type: http://stackoverflow.com/a/27108052
717
        if (!this.enableGestures || evt.pointerType !== 'touch') {
×
718
            return;
×
719
        }
720

721
        // HammerJS swipe is horizontal-only by default, don't check deltaY
722
        let deltaX;
723
        let startPosition;
724
        if (this.position === 'right') {
×
725
            // when on the right use inverse of deltaX
726
            deltaX = -evt.deltaX;
×
727
            startPosition = this.getWindowWidth() - (evt.center.x + evt.distance);
×
728
        } else {
729
            deltaX = evt.deltaX;
×
730
            startPosition = evt.center.x - evt.distance;
×
731
        }
732
        // only accept closing swipe (ignoring minEdgeZone) when the drawer is expanded:
733
        if ((this.isOpen && deltaX < 0) ||
×
734
            // positive deltaX from the edge:
735
            (deltaX > 0 && startPosition < this.maxEdgeZone)) {
736
            this.toggle();
×
737
        }
738
    };
739

UNCOV
740
    private panstart = (evt: HammerInput) => { // TODO: test code
×
UNCOV
741
        if (!this.enableGestures || this.pin || evt.pointerType !== 'touch') {
×
742
            return;
×
743
        }
UNCOV
744
        const startPosition = this.position === 'right' ? this.getWindowWidth() - (evt.center.x + evt.distance)
×
745
            : evt.center.x - evt.distance;
746

747
        // cache width during animation, flag to allow further handling
UNCOV
748
        if (this.isOpen || (startPosition < this.maxEdgeZone)) {
×
UNCOV
749
            this._panning = true;
×
UNCOV
750
            this._panStartWidth = this.getExpectedWidth(!this.isOpen);
×
UNCOV
751
            this._panLimit = this.getExpectedWidth(this.isOpen);
×
752

UNCOV
753
            this.renderer.addClass(this.overlay, 'panning');
×
UNCOV
754
            this.renderer.addClass(this.drawer, 'panning');
×
755
        }
756
    };
757

UNCOV
758
    private pan = (evt: HammerInput) => {
×
759
        // TODO: input.deltaX = prevDelta.x + (center.x - offset.x);
760
        // get actual delta (not total session one) from event?
761
        // pan WILL also fire after a full swipe, only resize on flag
UNCOV
762
        if (!this._panning) {
×
UNCOV
763
            return;
×
764
        }
UNCOV
765
        const right: boolean = this.position === 'right';
×
766
        // when on the right use inverse of deltaX
UNCOV
767
        const deltaX = right ? -evt.deltaX : evt.deltaX;
×
768
        let newX;
769
        let percent;
UNCOV
770
        const visibleWidth = this._panStartWidth + deltaX;
×
771

UNCOV
772
        if (this.isOpen && deltaX < 0) {
×
773
            // when visibleWidth hits limit - stop animating
UNCOV
774
            if (visibleWidth <= this._panLimit) {
×
UNCOV
775
                return;
×
776
            }
777

UNCOV
778
            if (this.hasAnimateWidth) {
×
779
                percent = (visibleWidth - this._panLimit) / (this._panStartWidth - this._panLimit);
×
780
                newX = visibleWidth;
×
781
            } else {
UNCOV
782
                percent = visibleWidth / this._panStartWidth;
×
UNCOV
783
                newX = evt.deltaX;
×
784
            }
UNCOV
785
            this.setXSize(newX, percent.toPrecision(2));
×
786

UNCOV
787
        } else if (!this.isOpen && deltaX > 0) {
×
788
            // when visibleWidth hits limit - stop animating
UNCOV
789
            if (visibleWidth >= this._panLimit) {
×
UNCOV
790
                return;
×
791
            }
792

UNCOV
793
            if (this.hasAnimateWidth) {
×
794
                percent = (visibleWidth - this._panStartWidth) / (this._panLimit - this._panStartWidth);
×
795
                newX = visibleWidth;
×
796
            } else {
UNCOV
797
                percent = visibleWidth / this._panLimit;
×
UNCOV
798
                newX = (this._panLimit - visibleWidth) * (right ? 1 : -1);
×
799
            }
UNCOV
800
            this.setXSize(newX, percent.toPrecision(2));
×
801
        }
802
    };
803

UNCOV
804
    private panEnd = (evt: HammerInput) => {
×
UNCOV
805
        if (this._panning) {
×
UNCOV
806
            const deltaX = this.position === 'right' ? -evt.deltaX : evt.deltaX;
×
UNCOV
807
            const visibleWidth: number = this._panStartWidth + deltaX;
×
UNCOV
808
            this.resetPan();
×
809

810
            // check if pan brought the drawer to 50%
UNCOV
811
            if (this.isOpen && visibleWidth <= this._panStartWidth / 2) {
×
UNCOV
812
                this.close();
×
UNCOV
813
            } else if (!this.isOpen && visibleWidth >= this._panLimit / 2) {
×
UNCOV
814
                this.open();
×
815
            }
UNCOV
816
            this._panStartWidth = null;
×
817
        }
818
    };
819

820
    private resetPan() {
UNCOV
821
        this._panning = false;
×
822
        /* styles fail to apply when set on parent due to extra attributes, prob ng bug */
823
        /* styles fail to apply when set on parent due to extra attributes, prob ng bug */
UNCOV
824
        this.renderer.removeClass(this.overlay, 'panning');
×
UNCOV
825
        this.renderer.removeClass(this.drawer, 'panning');
×
UNCOV
826
        this.setXSize(0, '');
×
827
    }
828

829
    /**
830
     * Sets the absolute position or width in case the drawer doesn't change position.
831
     *
832
     * @param x the number pixels to translate on the X axis or the width to set. 0 width will clear the style instead.
833
     * @param opacity optional value to apply to the overlay
834
     */
835
    private setXSize(x: number, opacity?: string) {
836
        // Angular polyfills patches window.requestAnimationFrame, but switch to DomAdapter API (TODO)
UNCOV
837
        window.requestAnimationFrame(() => {
×
UNCOV
838
            if (this.hasAnimateWidth) {
×
839
                this.renderer.setStyle(this.drawer, 'width', x ? Math.abs(x) + 'px' : '');
×
840
            } else {
UNCOV
841
                this.renderer.setStyle(this.drawer, 'transform', x ? 'translate3d(' + x + 'px,0,0)' : '');
×
UNCOV
842
                this.renderer.setStyle(this.drawer, '-webkit-transform', x ? 'translate3d(' + x + 'px,0,0)' : '');
×
843
            }
UNCOV
844
            if (opacity !== undefined) {
×
UNCOV
845
                this.renderer.setStyle(this.overlay, 'opacity', opacity);
×
846
            }
847
        });
848
    }
849

UNCOV
850
    private toggleOpenedEvent = () => {
×
UNCOV
851
        this.elementRef.nativeElement.removeEventListener('transitionend', this.toggleOpenedEvent, false);
×
UNCOV
852
        this.opened.emit();
×
853
    };
854

UNCOV
855
    private toggleClosedEvent = () => {
×
UNCOV
856
        this.elementRef.nativeElement.removeEventListener('transitionend', this.toggleClosedEvent, false);
×
UNCOV
857
        this.closed.emit();
×
858
    };
859
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc