@ng-matero/extensions 15.4.2 → 15.5.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.
@@ -1,57 +1,28 @@
1
- import * as i4 from '@angular/cdk/a11y';
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, TemplateRef, ElementRef, Directive, Inject, Input, Optional, Component, ViewEncapsulation, ChangeDetectionStrategy, ViewChild, NgModule } from '@angular/core';
3
+ import * as i5 from '@angular/common';
4
+ import { DOCUMENT, CommonModule } from '@angular/common';
5
+ import * as i3 from '@angular/cdk/a11y';
2
6
  import { A11yModule } from '@angular/cdk/a11y';
3
7
  import * as i1 from '@angular/cdk/overlay';
4
8
  import { Overlay, OverlayModule } from '@angular/cdk/overlay';
5
- import * as i2 from '@angular/cdk/scrolling';
6
9
  import { CdkScrollableModule } from '@angular/cdk/scrolling';
7
- import * as i7 from '@angular/common';
8
- import { CommonModule } from '@angular/common';
9
- import * as i0 from '@angular/core';
10
- import { InjectionToken, TemplateRef, Directive, Inject, Optional, Input, Component, ViewEncapsulation, ChangeDetectionStrategy, NgModule } from '@angular/core';
11
10
  import { MatCommonModule } from '@angular/material/core';
12
- import * as i8 from '@ng-matero/extensions/core';
11
+ import * as i6 from '@ng-matero/extensions/core';
13
12
  import { MtxPipesModule } from '@ng-matero/extensions/core';
14
- import { coerceBooleanProperty } from '@angular/cdk/coercion';
13
+ import { coerceBooleanProperty, coerceNumberProperty } from '@angular/cdk/coercion';
15
14
  import { ESCAPE, hasModifierKey } from '@angular/cdk/keycodes';
16
- import * as i6 from '@angular/cdk/layout';
17
- import { Breakpoints } from '@angular/cdk/layout';
18
- import * as i3 from '@angular/cdk/platform';
15
+ import * as i2 from '@angular/cdk/platform';
19
16
  import { normalizePassiveListenerOptions } from '@angular/cdk/platform';
20
17
  import { ComponentPortal } from '@angular/cdk/portal';
18
+ import { ANIMATION_MODULE_TYPE } from '@angular/platform-browser/animations';
21
19
  import { Subject } from 'rxjs';
22
20
  import { takeUntil, take } from 'rxjs/operators';
23
- import { trigger, state, style, transition, animate, keyframes } from '@angular/animations';
24
- import * as i5 from '@angular/cdk/bidi';
25
-
26
- /**
27
- * Animations used by MtxTooltip.
28
- * @docs-private
29
- */
30
- const mtxTooltipAnimations = {
31
- /** Animation that transitions a tooltip in and out. */
32
- tooltipState: trigger('state', [
33
- state('initial, void, hidden', style({ opacity: 0, transform: 'scale(0)' })),
34
- state('visible', style({ transform: 'scale(1)' })),
35
- transition('* => visible', animate('200ms cubic-bezier(0, 0, 0.2, 1)', keyframes([
36
- style({ opacity: 0, transform: 'scale(0)', offset: 0 }),
37
- style({ opacity: 0.5, transform: 'scale(0.99)', offset: 0.5 }),
38
- style({ opacity: 1, transform: 'scale(1)', offset: 1 }),
39
- ]))),
40
- transition('* => hidden', animate('100ms cubic-bezier(0, 0, 0.2, 1)', style({ opacity: 0 }))),
41
- ]),
42
- };
21
+ import * as i4 from '@angular/cdk/bidi';
22
+ import { trigger, state, style, transition, animate } from '@angular/animations';
43
23
 
44
24
  /** Time in ms to throttle repositioning after scroll events. */
45
25
  const SCROLL_THROTTLE_MS = 20;
46
- /** CSS class that will be attached to the overlay panel. */
47
- const TOOLTIP_PANEL_CLASS = 'mtx-tooltip-panel';
48
- /** Options used to bind passive event listeners. */
49
- const passiveListenerOptions = normalizePassiveListenerOptions({ passive: true });
50
- /**
51
- * Time between the user putting the pointer on a tooltip
52
- * trigger and the long press event being fired.
53
- */
54
- const LONGPRESS_DELAY = 500;
55
26
  /**
56
27
  * Creates an error to be thrown if the user supplied an invalid tooltip position.
57
28
  * @docs-private
@@ -71,11 +42,6 @@ const MTX_TOOLTIP_SCROLL_STRATEGY_FACTORY_PROVIDER = {
71
42
  deps: [Overlay],
72
43
  useFactory: MTX_TOOLTIP_SCROLL_STRATEGY_FACTORY,
73
44
  };
74
- /** Injection token to be used to override the default options for `matTooltip`. */
75
- const MTX_TOOLTIP_DEFAULT_OPTIONS = new InjectionToken('mtx-tooltip-default-options', {
76
- providedIn: 'root',
77
- factory: MTX_TOOLTIP_DEFAULT_OPTIONS_FACTORY,
78
- });
79
45
  /** @docs-private */
80
46
  function MTX_TOOLTIP_DEFAULT_OPTIONS_FACTORY() {
81
47
  return {
@@ -84,13 +50,32 @@ function MTX_TOOLTIP_DEFAULT_OPTIONS_FACTORY() {
84
50
  touchendHideDelay: 1500,
85
51
  };
86
52
  }
53
+ /** Injection token to be used to override the default options for `mtxTooltip`. */
54
+ const MTX_TOOLTIP_DEFAULT_OPTIONS = new InjectionToken('mtx-tooltip-default-options', {
55
+ providedIn: 'root',
56
+ factory: MTX_TOOLTIP_DEFAULT_OPTIONS_FACTORY,
57
+ });
87
58
  /**
88
- * Directive that attaches a material design tooltip to the host element. Animates the showing and
89
- * hiding of a tooltip provided position (defaults to below the element).
90
- *
91
- * https://material.io/design/components/tooltips.html
59
+ * CSS class that will be attached to the overlay panel.
60
+ * @deprecated
61
+ * @breaking-change 13.0.0 remove this variable
62
+ */
63
+ const TOOLTIP_PANEL_CLASS = 'mtx-mdc-tooltip-panel';
64
+ const PANEL_CLASS = 'tooltip-panel';
65
+ /** Options used to bind passive event listeners. */
66
+ const passiveListenerOptions = normalizePassiveListenerOptions({ passive: true });
67
+ /**
68
+ * Time between the user putting the pointer on a tooltip
69
+ * trigger and the long press event being fired.
92
70
  */
93
- class MtxTooltip {
71
+ const LONGPRESS_DELAY = 500;
72
+ // These constants were taken from MDC's `numbers` object. We can't import them from MDC,
73
+ // because they have some top-level references to `window` which break during SSR.
74
+ const MIN_VIEWPORT_TOOLTIP_THRESHOLD = 8;
75
+ const UNBOUNDED_ANCHOR_GAP = 8;
76
+ const MIN_HEIGHT = 24;
77
+ const MAX_WIDTH = 200;
78
+ class _MtxTooltipBase {
94
79
  /** Allows the user to define the position of the tooltip relative to the parent element */
95
80
  get position() {
96
81
  return this._position;
@@ -99,14 +84,20 @@ class MtxTooltip {
99
84
  if (value !== this._position) {
100
85
  this._position = value;
101
86
  if (this._overlayRef) {
102
- this._updatePosition();
103
- if (this._tooltipInstance) {
104
- this._tooltipInstance.show(0);
105
- }
87
+ this._updatePosition(this._overlayRef);
88
+ this._tooltipInstance?.show(0);
106
89
  this._overlayRef.updatePosition();
107
90
  }
108
91
  }
109
92
  }
93
+ get positionAtOrigin() {
94
+ return this._positionAtOrigin;
95
+ }
96
+ set positionAtOrigin(value) {
97
+ this._positionAtOrigin = coerceBooleanProperty(value);
98
+ this._detach();
99
+ this._overlayRef = null;
100
+ }
110
101
  /** Disables the display of the tooltip. */
111
102
  get disabled() {
112
103
  return this._disabled;
@@ -121,14 +112,33 @@ class MtxTooltip {
121
112
  this._setupPointerEnterEventsIfNeeded();
122
113
  }
123
114
  }
115
+ /** The default delay in ms before showing the tooltip after show is called */
116
+ get showDelay() {
117
+ return this._showDelay;
118
+ }
119
+ set showDelay(value) {
120
+ this._showDelay = coerceNumberProperty(value);
121
+ }
122
+ /** The default delay in ms before hiding the tooltip after hide is called */
123
+ get hideDelay() {
124
+ return this._hideDelay;
125
+ }
126
+ set hideDelay(value) {
127
+ this._hideDelay = coerceNumberProperty(value);
128
+ if (this._tooltipInstance) {
129
+ this._tooltipInstance._mouseLeaveHideDelay = this._hideDelay;
130
+ }
131
+ }
124
132
  /** The message to be displayed in the tooltip */
125
133
  get message() {
126
134
  return this._message;
127
135
  }
128
136
  set message(value) {
129
- this._ariaDescriber.removeDescription(this._elementRef.nativeElement, this._message);
137
+ this._ariaDescriber.removeDescription(this._elementRef.nativeElement, this._message, 'tooltip');
130
138
  // TODO: If the message is a TemplateRef, it's hard to support a11y.
131
139
  // If the message is not a string (e.g. number), convert it to a string and trim it.
140
+ // Must convert with `String(value)`, not `${value}`, otherwise Closure Compiler optimises
141
+ // away the string-conversion: https://github.com/angular/components/issues/20684
132
142
  this._message = value instanceof TemplateRef ? value : value != null ? `${value}`.trim() : '';
133
143
  if (!this._message && this._isTooltipVisible()) {
134
144
  this.hide(0);
@@ -142,11 +152,19 @@ class MtxTooltip {
142
152
  // has a data-bound `aria-label` or when it'll be set for the first time. We can avoid the
143
153
  // issue by deferring the description by a tick so Angular has time to set the `aria-label`.
144
154
  Promise.resolve().then(() => {
145
- this._ariaDescriber.describe(this._elementRef.nativeElement, this.message);
155
+ this._ariaDescriber.describe(this._elementRef.nativeElement, this.message, 'tooltip');
146
156
  });
147
157
  });
148
158
  }
149
159
  }
160
+ /** Context to be passed to the tooltip. */
161
+ get tooltipContext() {
162
+ return this._tooltipContext;
163
+ }
164
+ set tooltipContext(value) {
165
+ this._tooltipContext = value;
166
+ this._setTooltipContext(this._tooltipContext);
167
+ }
150
168
  /** Classes to be passed to the tooltip. Supports the same syntax as `ngClass`. */
151
169
  get tooltipClass() {
152
170
  return this._tooltipClass;
@@ -157,7 +175,7 @@ class MtxTooltip {
157
175
  this._setTooltipClass(this._tooltipClass);
158
176
  }
159
177
  }
160
- constructor(_overlay, _elementRef, _scrollDispatcher, _viewContainerRef, _ngZone, _platform, _ariaDescriber, _focusMonitor, scrollStrategy, _dir, _defaultOptions) {
178
+ constructor(_overlay, _elementRef, _scrollDispatcher, _viewContainerRef, _ngZone, _platform, _ariaDescriber, _focusMonitor, scrollStrategy, _dir, _defaultOptions, _document) {
161
179
  this._overlay = _overlay;
162
180
  this._elementRef = _elementRef;
163
181
  this._scrollDispatcher = _scrollDispatcher;
@@ -169,13 +187,14 @@ class MtxTooltip {
169
187
  this._dir = _dir;
170
188
  this._defaultOptions = _defaultOptions;
171
189
  this._position = 'below';
190
+ this._positionAtOrigin = false;
172
191
  this._disabled = false;
173
192
  this._viewInitialized = false;
174
193
  this._pointerExitEventsInitialized = false;
175
- /** The default delay in ms before showing the tooltip after show is called */
176
- this.showDelay = this._defaultOptions.showDelay;
177
- /** The default delay in ms before hiding the tooltip after hide is called */
178
- this.hideDelay = this._defaultOptions.hideDelay;
194
+ this._viewportMargin = 8;
195
+ this._cssClassPrefix = 'mtx';
196
+ this._showDelay = this._defaultOptions.showDelay;
197
+ this._hideDelay = this._defaultOptions.hideDelay;
179
198
  /**
180
199
  * How touch gestures should be handled by the tooltip. On touch devices the tooltip directive
181
200
  * uses a long press gesture to show and hide, however it can conflict with the native browser
@@ -196,28 +215,23 @@ class MtxTooltip {
196
215
  this._passiveListeners = [];
197
216
  /** Emits when the component is destroyed. */
198
217
  this._destroyed = new Subject();
199
- /**
200
- * Handles the keydown events on the host element.
201
- * Needs to be an arrow function so that we can use it in addEventListener.
202
- */
203
- this._handleKeydown = (event) => {
204
- if (this._isTooltipVisible() && event.keyCode === ESCAPE && !hasModifierKey(event)) {
205
- event.preventDefault();
206
- event.stopPropagation();
207
- this._ngZone.run(() => this.hide(0));
208
- }
209
- };
210
218
  this._scrollStrategy = scrollStrategy;
219
+ this._document = _document;
211
220
  if (_defaultOptions) {
212
221
  if (_defaultOptions.position) {
213
222
  this.position = _defaultOptions.position;
214
223
  }
224
+ if (_defaultOptions.positionAtOrigin) {
225
+ this.positionAtOrigin = _defaultOptions.positionAtOrigin;
226
+ }
215
227
  if (_defaultOptions.touchGestures) {
216
228
  this.touchGestures = _defaultOptions.touchGestures;
217
229
  }
218
230
  }
219
- _ngZone.runOutsideAngular(() => {
220
- _elementRef.nativeElement.addEventListener('keydown', this._handleKeydown);
231
+ _dir.change.pipe(takeUntil(this._destroyed)).subscribe(() => {
232
+ if (this._overlayRef) {
233
+ this._updatePosition(this._overlayRef);
234
+ }
221
235
  });
222
236
  }
223
237
  ngAfterViewInit() {
@@ -248,66 +262,79 @@ class MtxTooltip {
248
262
  this._tooltipInstance = null;
249
263
  }
250
264
  // Clean up the event listeners set in the constructor
251
- nativeElement.removeEventListener('keydown', this._handleKeydown);
252
265
  this._passiveListeners.forEach(([event, listener]) => {
253
266
  nativeElement.removeEventListener(event, listener, passiveListenerOptions);
254
267
  });
255
268
  this._passiveListeners.length = 0;
256
269
  this._destroyed.next();
257
270
  this._destroyed.complete();
258
- this._ariaDescriber.removeDescription(nativeElement, this.message);
271
+ this._ariaDescriber.removeDescription(nativeElement, this.message, 'tooltip');
259
272
  this._focusMonitor.stopMonitoring(nativeElement);
260
273
  }
261
274
  /** Shows the tooltip after the delay in ms, defaults to tooltip-delay-show or 0ms if no input */
262
- show(delay = this.showDelay) {
263
- if (this.disabled ||
264
- !this.message ||
265
- (this._isTooltipVisible() &&
266
- !this._tooltipInstance._showTimeoutId &&
267
- !this._tooltipInstance._hideTimeoutId)) {
275
+ show(delay = this.showDelay, origin) {
276
+ if (this.disabled || !this.message || this._isTooltipVisible()) {
277
+ this._tooltipInstance?._cancelPendingAnimations();
268
278
  return;
269
279
  }
270
- const overlayRef = this._createOverlay();
280
+ const overlayRef = this._createOverlay(origin);
271
281
  this._detach();
272
- this._portal = this._portal || new ComponentPortal(TooltipComponent, this._viewContainerRef);
273
- this._tooltipInstance = overlayRef.attach(this._portal).instance;
274
- this._tooltipInstance
282
+ this._portal =
283
+ this._portal || new ComponentPortal(this._tooltipComponent, this._viewContainerRef);
284
+ const instance = (this._tooltipInstance = overlayRef.attach(this._portal).instance);
285
+ instance._triggerElement = this._elementRef.nativeElement;
286
+ instance._mouseLeaveHideDelay = this._hideDelay;
287
+ instance
275
288
  .afterHidden()
276
289
  .pipe(takeUntil(this._destroyed))
277
290
  .subscribe(() => this._detach());
278
291
  this._setTooltipClass(this._tooltipClass);
292
+ this._setTooltipContext(this._tooltipContext);
279
293
  this._updateTooltipMessage();
280
- this._tooltipInstance.show(delay);
294
+ instance.show(delay);
281
295
  }
282
296
  /** Hides the tooltip after the delay in ms, defaults to tooltip-delay-hide or 0ms if no input */
283
297
  hide(delay = this.hideDelay) {
284
- if (this._tooltipInstance) {
285
- this._tooltipInstance.hide(delay);
298
+ const instance = this._tooltipInstance;
299
+ if (instance) {
300
+ if (instance.isVisible()) {
301
+ instance.hide(delay);
302
+ }
303
+ else {
304
+ instance._cancelPendingAnimations();
305
+ this._detach();
306
+ }
286
307
  }
287
308
  }
288
309
  /** Shows/hides the tooltip */
289
- toggle() {
290
- this._isTooltipVisible() ? this.hide() : this.show();
310
+ toggle(origin) {
311
+ this._isTooltipVisible() ? this.hide() : this.show(undefined, origin);
291
312
  }
292
313
  /** Returns true if the tooltip is currently visible to the user */
293
314
  _isTooltipVisible() {
294
315
  return !!this._tooltipInstance && this._tooltipInstance.isVisible();
295
316
  }
296
317
  /** Create the overlay config and position strategy */
297
- _createOverlay() {
318
+ _createOverlay(origin) {
298
319
  if (this._overlayRef) {
299
- return this._overlayRef;
320
+ const existingStrategy = this._overlayRef.getConfig()
321
+ .positionStrategy;
322
+ if ((!this.positionAtOrigin || !origin) && existingStrategy._origin instanceof ElementRef) {
323
+ return this._overlayRef;
324
+ }
325
+ this._detach();
300
326
  }
301
327
  const scrollableAncestors = this._scrollDispatcher.getAncestorScrollContainers(this._elementRef);
302
328
  // Create connected position strategy that listens for scroll events to reposition.
303
329
  const strategy = this._overlay
304
330
  .position()
305
- .flexibleConnectedTo(this._elementRef)
306
- .withTransformOriginOn('.mtx-tooltip')
331
+ .flexibleConnectedTo(this.positionAtOrigin ? origin || this._elementRef : this._elementRef)
332
+ .withTransformOriginOn(`.${this._cssClassPrefix}-tooltip`)
307
333
  .withFlexibleDimensions(false)
308
- .withViewportMargin(8)
334
+ .withViewportMargin(this._viewportMargin)
309
335
  .withScrollableContainers(scrollableAncestors);
310
336
  strategy.positionChanges.pipe(takeUntil(this._destroyed)).subscribe(change => {
337
+ this._updateCurrentPositionClass(change.connectionPair);
311
338
  if (this._tooltipInstance) {
312
339
  if (change.scrollableViewProperties.isOverlayClipped && this._tooltipInstance.isVisible()) {
313
340
  // After position changes occur and the overlay is clipped by
@@ -319,14 +346,31 @@ class MtxTooltip {
319
346
  this._overlayRef = this._overlay.create({
320
347
  direction: this._dir,
321
348
  positionStrategy: strategy,
322
- panelClass: TOOLTIP_PANEL_CLASS,
349
+ panelClass: `${this._cssClassPrefix}-${PANEL_CLASS}`,
323
350
  scrollStrategy: this._scrollStrategy(),
324
351
  });
325
- this._updatePosition();
352
+ this._updatePosition(this._overlayRef);
326
353
  this._overlayRef
327
354
  .detachments()
328
355
  .pipe(takeUntil(this._destroyed))
329
356
  .subscribe(() => this._detach());
357
+ this._overlayRef
358
+ .outsidePointerEvents()
359
+ .pipe(takeUntil(this._destroyed))
360
+ .subscribe(() => this._tooltipInstance?._handleBodyInteraction());
361
+ this._overlayRef
362
+ .keydownEvents()
363
+ .pipe(takeUntil(this._destroyed))
364
+ .subscribe(event => {
365
+ if (this._isTooltipVisible() && event.keyCode === ESCAPE && !hasModifierKey(event)) {
366
+ event.preventDefault();
367
+ event.stopPropagation();
368
+ this._ngZone.run(() => this.hide(0));
369
+ }
370
+ });
371
+ if (this._defaultOptions?.disableTooltipInteractivity) {
372
+ this._overlayRef.addPanelClass(`${this._cssClassPrefix}-tooltip-panel-non-interactive`);
373
+ }
330
374
  return this._overlayRef;
331
375
  }
332
376
  /** Detaches the currently-attached tooltip. */
@@ -337,35 +381,38 @@ class MtxTooltip {
337
381
  this._tooltipInstance = null;
338
382
  }
339
383
  /** Updates the position of the current tooltip. */
340
- _updatePosition() {
341
- const position = this._overlayRef.getConfig()
342
- .positionStrategy;
384
+ _updatePosition(overlayRef) {
385
+ const position = overlayRef.getConfig().positionStrategy;
343
386
  const origin = this._getOrigin();
344
387
  const overlay = this._getOverlayPosition();
345
388
  position.withPositions([
346
- { ...origin.main, ...overlay.main },
347
- { ...origin.fallback, ...overlay.fallback },
389
+ this._addOffset({ ...origin.main, ...overlay.main }),
390
+ this._addOffset({ ...origin.fallback, ...overlay.fallback }),
348
391
  ]);
349
392
  }
393
+ /** Adds the configured offset to a position. Used as a hook for child classes. */
394
+ _addOffset(position) {
395
+ return position;
396
+ }
350
397
  /**
351
398
  * Returns the origin position and a fallback position based on the user's position preference.
352
399
  * The fallback position is the inverse of the origin (e.g. `'below' -> 'above'`).
353
400
  */
354
401
  _getOrigin() {
355
- const isLtr = !this._dir || this._dir.value === 'ltr';
402
+ const isLtr = !this._dir || this._dir.value == 'ltr';
356
403
  const position = this.position;
357
404
  let originPosition;
358
- if (position === 'above' || position === 'below') {
359
- originPosition = { originX: 'center', originY: position === 'above' ? 'top' : 'bottom' };
405
+ if (position == 'above' || position == 'below') {
406
+ originPosition = { originX: 'center', originY: position == 'above' ? 'top' : 'bottom' };
360
407
  }
361
- else if (position === 'before' ||
362
- (position === 'left' && isLtr) ||
363
- (position === 'right' && !isLtr)) {
408
+ else if (position == 'before' ||
409
+ (position == 'left' && isLtr) ||
410
+ (position == 'right' && !isLtr)) {
364
411
  originPosition = { originX: 'start', originY: 'center' };
365
412
  }
366
- else if (position === 'after' ||
367
- (position === 'right' && isLtr) ||
368
- (position === 'left' && !isLtr)) {
413
+ else if (position == 'after' ||
414
+ (position == 'right' && isLtr) ||
415
+ (position == 'left' && !isLtr)) {
369
416
  originPosition = { originX: 'end', originY: 'center' };
370
417
  }
371
418
  else {
@@ -379,23 +426,23 @@ class MtxTooltip {
379
426
  }
380
427
  /** Returns the overlay position and a fallback position based on the user's preference */
381
428
  _getOverlayPosition() {
382
- const isLtr = !this._dir || this._dir.value === 'ltr';
429
+ const isLtr = !this._dir || this._dir.value == 'ltr';
383
430
  const position = this.position;
384
431
  let overlayPosition;
385
- if (position === 'above') {
432
+ if (position == 'above') {
386
433
  overlayPosition = { overlayX: 'center', overlayY: 'bottom' };
387
434
  }
388
- else if (position === 'below') {
435
+ else if (position == 'below') {
389
436
  overlayPosition = { overlayX: 'center', overlayY: 'top' };
390
437
  }
391
- else if (position === 'before' ||
392
- (position === 'left' && isLtr) ||
393
- (position === 'right' && !isLtr)) {
438
+ else if (position == 'before' ||
439
+ (position == 'left' && isLtr) ||
440
+ (position == 'right' && !isLtr)) {
394
441
  overlayPosition = { overlayX: 'end', overlayY: 'center' };
395
442
  }
396
- else if (position === 'after' ||
397
- (position === 'right' && isLtr) ||
398
- (position === 'left' && !isLtr)) {
443
+ else if (position == 'after' ||
444
+ (position == 'right' && isLtr) ||
445
+ (position == 'left' && !isLtr)) {
399
446
  overlayPosition = { overlayX: 'start', overlayY: 'center' };
400
447
  }
401
448
  else {
@@ -414,16 +461,20 @@ class MtxTooltip {
414
461
  if (this._tooltipInstance) {
415
462
  this._tooltipInstance.message = this.message;
416
463
  this._tooltipInstance._markForCheck();
417
- this._ngZone.onMicrotaskEmpty
418
- .asObservable()
419
- .pipe(take(1), takeUntil(this._destroyed))
420
- .subscribe(() => {
464
+ this._ngZone.onMicrotaskEmpty.pipe(take(1), takeUntil(this._destroyed)).subscribe(() => {
421
465
  if (this._tooltipInstance) {
422
466
  this._overlayRef.updatePosition();
423
467
  }
424
468
  });
425
469
  }
426
470
  }
471
+ /** Updates the tooltip context */
472
+ _setTooltipContext(tooltipContext) {
473
+ if (this._tooltipInstance) {
474
+ this._tooltipInstance.tooltipContext = tooltipContext;
475
+ this._tooltipInstance._markForCheck();
476
+ }
477
+ }
427
478
  /** Updates the tooltip class */
428
479
  _setTooltipClass(tooltipClass) {
429
480
  if (this._tooltipInstance) {
@@ -451,6 +502,36 @@ class MtxTooltip {
451
502
  }
452
503
  return { x, y };
453
504
  }
505
+ /** Updates the class on the overlay panel based on the current position of the tooltip. */
506
+ _updateCurrentPositionClass(connectionPair) {
507
+ const { overlayY, originX, originY } = connectionPair;
508
+ let newPosition;
509
+ // If the overlay is in the middle along the Y axis,
510
+ // it means that it's either before or after.
511
+ if (overlayY === 'center') {
512
+ // Note that since this information is used for styling, we want to
513
+ // resolve `start` and `end` to their real values, otherwise consumers
514
+ // would have to remember to do it themselves on each consumption.
515
+ if (this._dir && this._dir.value === 'rtl') {
516
+ newPosition = originX === 'end' ? 'left' : 'right';
517
+ }
518
+ else {
519
+ newPosition = originX === 'start' ? 'left' : 'right';
520
+ }
521
+ }
522
+ else {
523
+ newPosition = overlayY === 'bottom' && originY === 'top' ? 'above' : 'below';
524
+ }
525
+ if (newPosition !== this._currentPosition) {
526
+ const overlayRef = this._overlayRef;
527
+ if (overlayRef) {
528
+ const classPrefix = `${this._cssClassPrefix}-${PANEL_CLASS}-`;
529
+ overlayRef.removePanelClass(classPrefix + this._currentPosition);
530
+ overlayRef.addPanelClass(classPrefix + newPosition);
531
+ }
532
+ this._currentPosition = newPosition;
533
+ }
534
+ }
454
535
  /** Binds the pointer events to the tooltip trigger. */
455
536
  _setupPointerEnterEventsIfNeeded() {
456
537
  // Optimization: Defer hooking up events if there's no message or the tooltip is disabled.
@@ -465,9 +546,13 @@ class MtxTooltip {
465
546
  if (this._platformSupportsMouseEvents()) {
466
547
  this._passiveListeners.push([
467
548
  'mouseenter',
468
- () => {
549
+ event => {
469
550
  this._setupPointerExitEventsIfNeeded();
470
- this.show();
551
+ let point = undefined;
552
+ if (event.x !== undefined && event.y !== undefined) {
553
+ point = event;
554
+ }
555
+ this.show(undefined, point);
471
556
  },
472
557
  ]);
473
558
  }
@@ -475,12 +560,14 @@ class MtxTooltip {
475
560
  this._disableNativeGesturesIfNecessary();
476
561
  this._passiveListeners.push([
477
562
  'touchstart',
478
- () => {
563
+ event => {
564
+ const touch = event.targetTouches?.[0];
565
+ const origin = touch ? { x: touch.clientX, y: touch.clientY } : undefined;
479
566
  // Note that it's important that we don't `preventDefault` here,
480
567
  // because it can prevent click events from firing on the element.
481
568
  this._setupPointerExitEventsIfNeeded();
482
569
  clearTimeout(this._touchstartTimeout);
483
- this._touchstartTimeout = setTimeout(() => this.show(), LONGPRESS_DELAY);
570
+ this._touchstartTimeout = setTimeout(() => this.show(undefined, origin), LONGPRESS_DELAY);
484
571
  },
485
572
  ]);
486
573
  }
@@ -493,7 +580,15 @@ class MtxTooltip {
493
580
  this._pointerExitEventsInitialized = true;
494
581
  const exitListeners = [];
495
582
  if (this._platformSupportsMouseEvents()) {
496
- exitListeners.push(['mouseleave', () => this.hide()]);
583
+ exitListeners.push([
584
+ 'mouseleave',
585
+ event => {
586
+ const newTarget = event.relatedTarget;
587
+ if (!newTarget || !this._overlayRef?.overlayElement.contains(newTarget)) {
588
+ this.hide();
589
+ }
590
+ },
591
+ ], ['wheel', event => this._wheelListener(event)]);
497
592
  }
498
593
  else if (this.touchGestures !== 'off') {
499
594
  this._disableNativeGesturesIfNecessary();
@@ -514,6 +609,20 @@ class MtxTooltip {
514
609
  _platformSupportsMouseEvents() {
515
610
  return !this._platform.IOS && !this._platform.ANDROID;
516
611
  }
612
+ /** Listener for the `wheel` event on the element. */
613
+ _wheelListener(event) {
614
+ if (this._isTooltipVisible()) {
615
+ const elementUnderPointer = this._document.elementFromPoint(event.clientX, event.clientY);
616
+ const element = this._elementRef.nativeElement;
617
+ // On non-touch devices we depend on the `mouseleave` event to close the tooltip, but it
618
+ // won't fire if the user scrolls away using the wheel without moving their cursor. We
619
+ // work around it by finding the element under the user's cursor and closing the tooltip
620
+ // if it's not the trigger.
621
+ if (elementUnderPointer !== element && !element.contains(elementUnderPointer)) {
622
+ this.hide();
623
+ }
624
+ }
625
+ }
517
626
  /** Disables the native browser gestures, based on how the tooltip has been configured. */
518
627
  _disableNativeGesturesIfNecessary() {
519
628
  const gestures = this.touchGestures;
@@ -535,36 +644,23 @@ class MtxTooltip {
535
644
  style.webkitUserDrag = 'none';
536
645
  }
537
646
  style.touchAction = 'none';
538
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
539
- // @ts-ignore
540
647
  style.webkitTapHighlightColor = 'transparent';
541
648
  }
542
649
  }
543
650
  }
544
- /** @nocollapse */ MtxTooltip.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.1", ngImport: i0, type: MtxTooltip, deps: [{ token: i1.Overlay }, { token: i0.ElementRef }, { token: i2.ScrollDispatcher }, { token: i0.ViewContainerRef }, { token: i0.NgZone }, { token: i3.Platform }, { token: i4.AriaDescriber }, { token: i4.FocusMonitor }, { token: MTX_TOOLTIP_SCROLL_STRATEGY }, { token: i5.Directionality, optional: true }, { token: MTX_TOOLTIP_DEFAULT_OPTIONS, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
545
- /** @nocollapse */ MtxTooltip.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.1", type: MtxTooltip, selector: "[mtxTooltip]", inputs: { position: ["mtxTooltipPosition", "position"], disabled: ["mtxTooltipDisabled", "disabled"], showDelay: ["mtxTooltipShowDelay", "showDelay"], hideDelay: ["mtxTooltipHideDelay", "hideDelay"], touchGestures: ["mtxTooltipTouchGestures", "touchGestures"], message: ["mtxTooltip", "message"], tooltipClass: ["mtxTooltipClass", "tooltipClass"] }, host: { classAttribute: "mtx-tooltip-trigger" }, exportAs: ["mtxTooltip"], ngImport: i0 });
546
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.1", ngImport: i0, type: MtxTooltip, decorators: [{
547
- type: Directive,
548
- args: [{
549
- selector: '[mtxTooltip]',
550
- exportAs: 'mtxTooltip',
551
- host: {
552
- class: 'mtx-tooltip-trigger',
553
- },
554
- }]
555
- }], ctorParameters: function () { return [{ type: i1.Overlay }, { type: i0.ElementRef }, { type: i2.ScrollDispatcher }, { type: i0.ViewContainerRef }, { type: i0.NgZone }, { type: i3.Platform }, { type: i4.AriaDescriber }, { type: i4.FocusMonitor }, { type: undefined, decorators: [{
556
- type: Inject,
557
- args: [MTX_TOOLTIP_SCROLL_STRATEGY]
558
- }] }, { type: i5.Directionality, decorators: [{
559
- type: Optional
560
- }] }, { type: undefined, decorators: [{
561
- type: Optional
562
- }, {
651
+ /** @nocollapse */ _MtxTooltipBase.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.1", ngImport: i0, type: _MtxTooltipBase, deps: "invalid", target: i0.ɵɵFactoryTarget.Directive });
652
+ /** @nocollapse */ _MtxTooltipBase.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.1", type: _MtxTooltipBase, inputs: { position: ["mtxTooltipPosition", "position"], positionAtOrigin: ["mtxTooltipPositionAtOrigin", "positionAtOrigin"], disabled: ["mtxTooltipDisabled", "disabled"], showDelay: ["mtxTooltipShowDelay", "showDelay"], hideDelay: ["mtxTooltipHideDelay", "hideDelay"], touchGestures: ["mtxTooltipTouchGestures", "touchGestures"], message: ["mtxTooltip", "message"], tooltipContext: ["mtxTooltipContext", "tooltipContext"], tooltipClass: ["mtxTooltipClass", "tooltipClass"] }, ngImport: i0 });
653
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.1", ngImport: i0, type: _MtxTooltipBase, decorators: [{
654
+ type: Directive
655
+ }], ctorParameters: function () { return [{ type: i1.Overlay }, { type: i0.ElementRef }, { type: i1.ScrollDispatcher }, { type: i0.ViewContainerRef }, { type: i0.NgZone }, { type: i2.Platform }, { type: i3.AriaDescriber }, { type: i3.FocusMonitor }, { type: undefined }, { type: i4.Directionality }, { type: undefined }, { type: undefined, decorators: [{
563
656
  type: Inject,
564
- args: [MTX_TOOLTIP_DEFAULT_OPTIONS]
657
+ args: [DOCUMENT]
565
658
  }] }]; }, propDecorators: { position: [{
566
659
  type: Input,
567
660
  args: ['mtxTooltipPosition']
661
+ }], positionAtOrigin: [{
662
+ type: Input,
663
+ args: ['mtxTooltipPositionAtOrigin']
568
664
  }], disabled: [{
569
665
  type: Input,
570
666
  args: ['mtxTooltipDisabled']
@@ -580,26 +676,79 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.1", ngImpor
580
676
  }], message: [{
581
677
  type: Input,
582
678
  args: ['mtxTooltip']
679
+ }], tooltipContext: [{
680
+ type: Input,
681
+ args: ['mtxTooltipContext']
583
682
  }], tooltipClass: [{
584
683
  type: Input,
585
684
  args: ['mtxTooltipClass']
586
685
  }] } });
587
686
  /**
588
- * Internal component that wraps the tooltip's content.
589
- * @docs-private
687
+ * Directive that attaches a material design tooltip to the host element. Animates the showing and
688
+ * hiding of a tooltip provided position (defaults to below the element).
689
+ *
690
+ * https://material.io/design/components/tooltips.html
590
691
  */
591
- class TooltipComponent {
592
- constructor(_changeDetectorRef, _breakpointObserver) {
692
+ class MtxTooltip extends _MtxTooltipBase {
693
+ constructor(overlay, elementRef, scrollDispatcher, viewContainerRef, ngZone, platform, ariaDescriber, focusMonitor, scrollStrategy, dir, defaultOptions, _document) {
694
+ super(overlay, elementRef, scrollDispatcher, viewContainerRef, ngZone, platform, ariaDescriber, focusMonitor, scrollStrategy, dir, defaultOptions, _document);
695
+ this._tooltipComponent = TooltipComponent;
696
+ this._cssClassPrefix = 'mtx-mdc';
697
+ this._viewportMargin = MIN_VIEWPORT_TOOLTIP_THRESHOLD;
698
+ }
699
+ _addOffset(position) {
700
+ const offset = UNBOUNDED_ANCHOR_GAP;
701
+ const isLtr = !this._dir || this._dir.value == 'ltr';
702
+ if (position.originY === 'top') {
703
+ position.offsetY = -offset;
704
+ }
705
+ else if (position.originY === 'bottom') {
706
+ position.offsetY = offset;
707
+ }
708
+ else if (position.originX === 'start') {
709
+ position.offsetX = isLtr ? -offset : offset;
710
+ }
711
+ else if (position.originX === 'end') {
712
+ position.offsetX = isLtr ? offset : -offset;
713
+ }
714
+ return position;
715
+ }
716
+ }
717
+ /** @nocollapse */ MtxTooltip.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.1", ngImport: i0, type: MtxTooltip, deps: [{ token: i1.Overlay }, { token: i0.ElementRef }, { token: i1.ScrollDispatcher }, { token: i0.ViewContainerRef }, { token: i0.NgZone }, { token: i2.Platform }, { token: i3.AriaDescriber }, { token: i3.FocusMonitor }, { token: MTX_TOOLTIP_SCROLL_STRATEGY }, { token: i4.Directionality, optional: true }, { token: MTX_TOOLTIP_DEFAULT_OPTIONS, optional: true }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Directive });
718
+ /** @nocollapse */ MtxTooltip.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.1", type: MtxTooltip, selector: "[mtxTooltip]", host: { classAttribute: "mtx-mdc-tooltip-trigger" }, exportAs: ["mtxTooltip"], usesInheritance: true, ngImport: i0 });
719
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.1", ngImport: i0, type: MtxTooltip, decorators: [{
720
+ type: Directive,
721
+ args: [{
722
+ selector: '[mtxTooltip]',
723
+ exportAs: 'mtxTooltip',
724
+ host: {
725
+ class: 'mtx-mdc-tooltip-trigger',
726
+ },
727
+ }]
728
+ }], ctorParameters: function () { return [{ type: i1.Overlay }, { type: i0.ElementRef }, { type: i1.ScrollDispatcher }, { type: i0.ViewContainerRef }, { type: i0.NgZone }, { type: i2.Platform }, { type: i3.AriaDescriber }, { type: i3.FocusMonitor }, { type: undefined, decorators: [{
729
+ type: Inject,
730
+ args: [MTX_TOOLTIP_SCROLL_STRATEGY]
731
+ }] }, { type: i4.Directionality, decorators: [{
732
+ type: Optional
733
+ }] }, { type: undefined, decorators: [{
734
+ type: Optional
735
+ }, {
736
+ type: Inject,
737
+ args: [MTX_TOOLTIP_DEFAULT_OPTIONS]
738
+ }] }, { type: undefined, decorators: [{
739
+ type: Inject,
740
+ args: [DOCUMENT]
741
+ }] }]; } });
742
+ class _TooltipComponentBase {
743
+ constructor(_changeDetectorRef, animationMode) {
593
744
  this._changeDetectorRef = _changeDetectorRef;
594
- this._breakpointObserver = _breakpointObserver;
595
- /** Property watched by the animation framework to show or hide the tooltip */
596
- this._visibility = 'initial';
597
745
  /** Whether interactions on the page should close the tooltip */
598
746
  this._closeOnInteraction = false;
747
+ /** Whether the tooltip is currently visible. */
748
+ this._isVisible = false;
599
749
  /** Subject for notifying that the tooltip has been hidden from the view */
600
750
  this._onHide = new Subject();
601
- /** Stream that emits whether the user has a handset-sized display. */
602
- this._isHandset = this._breakpointObserver.observe(Breakpoints.Handset);
751
+ this._animationsDisabled = animationMode === 'NoopAnimations';
603
752
  }
604
753
  /**
605
754
  * Shows the tooltip with an animation originating from the provided origin
@@ -607,18 +756,10 @@ class TooltipComponent {
607
756
  */
608
757
  show(delay) {
609
758
  // Cancel the delayed hide if it is scheduled
610
- if (this._hideTimeoutId) {
611
- clearTimeout(this._hideTimeoutId);
612
- this._hideTimeoutId = null;
613
- }
614
- // Body interactions should cancel the tooltip if there is a delay in showing.
615
- this._closeOnInteraction = true;
759
+ clearTimeout(this._hideTimeoutId);
616
760
  this._showTimeoutId = setTimeout(() => {
617
- this._visibility = 'visible';
618
- this._showTimeoutId = null;
619
- // Mark for check so if any parent component has set the
620
- // ChangeDetectionStrategy to OnPush it will be checked anyways
621
- this._markForCheck();
761
+ this._toggleVisibility(true);
762
+ this._showTimeoutId = undefined;
622
763
  }, delay);
623
764
  }
624
765
  /**
@@ -627,40 +768,24 @@ class TooltipComponent {
627
768
  */
628
769
  hide(delay) {
629
770
  // Cancel the delayed show if it is scheduled
630
- if (this._showTimeoutId) {
631
- clearTimeout(this._showTimeoutId);
632
- this._showTimeoutId = null;
633
- }
771
+ clearTimeout(this._showTimeoutId);
634
772
  this._hideTimeoutId = setTimeout(() => {
635
- this._visibility = 'hidden';
636
- this._hideTimeoutId = null;
637
- // Mark for check so if any parent component has set the
638
- // ChangeDetectionStrategy to OnPush it will be checked anyways
639
- this._markForCheck();
773
+ this._toggleVisibility(false);
774
+ this._hideTimeoutId = undefined;
640
775
  }, delay);
641
776
  }
642
777
  /** Returns an observable that notifies when the tooltip has been hidden from view. */
643
778
  afterHidden() {
644
- return this._onHide.asObservable();
779
+ return this._onHide;
645
780
  }
646
781
  /** Whether the tooltip is being displayed. */
647
782
  isVisible() {
648
- return this._visibility === 'visible';
783
+ return this._isVisible;
649
784
  }
650
785
  ngOnDestroy() {
786
+ this._cancelPendingAnimations();
651
787
  this._onHide.complete();
652
- }
653
- _animationStart() {
654
- this._closeOnInteraction = false;
655
- }
656
- _animationDone(event) {
657
- const toState = event.toState;
658
- if (toState === 'hidden' && !this.isVisible()) {
659
- this._onHide.next();
660
- }
661
- if (toState === 'visible' || toState === 'hidden') {
662
- this._closeOnInteraction = true;
663
- }
788
+ this._triggerElement = null;
664
789
  }
665
790
  /**
666
791
  * Interactions on the HTML body should close the tooltip immediately as defined in the
@@ -680,19 +805,130 @@ class TooltipComponent {
680
805
  _markForCheck() {
681
806
  this._changeDetectorRef.markForCheck();
682
807
  }
808
+ _handleMouseLeave({ relatedTarget }) {
809
+ if (!relatedTarget || !this._triggerElement.contains(relatedTarget)) {
810
+ if (this.isVisible()) {
811
+ this.hide(this._mouseLeaveHideDelay);
812
+ }
813
+ else {
814
+ this._finalizeAnimation(false);
815
+ }
816
+ }
817
+ }
818
+ /**
819
+ * Callback for when the timeout in this.show() gets completed.
820
+ * This method is only needed by the mdc-tooltip, and so it is only implemented
821
+ * in the mdc-tooltip, not here.
822
+ */
823
+ _onShow() { }
824
+ /** Event listener dispatched when an animation on the tooltip finishes. */
825
+ _handleAnimationEnd({ animationName }) {
826
+ if (animationName === this._showAnimation || animationName === this._hideAnimation) {
827
+ this._finalizeAnimation(animationName === this._showAnimation);
828
+ }
829
+ }
830
+ /** Cancels any pending animation sequences. */
831
+ _cancelPendingAnimations() {
832
+ clearTimeout(this._showTimeoutId);
833
+ clearTimeout(this._hideTimeoutId);
834
+ this._showTimeoutId = this._hideTimeoutId = undefined;
835
+ }
836
+ /** Handles the cleanup after an animation has finished. */
837
+ _finalizeAnimation(toVisible) {
838
+ if (toVisible) {
839
+ this._closeOnInteraction = true;
840
+ }
841
+ else if (!this.isVisible()) {
842
+ this._onHide.next();
843
+ }
844
+ }
845
+ /** Toggles the visibility of the tooltip element. */
846
+ _toggleVisibility(isVisible) {
847
+ // We set the classes directly here ourselves so that toggling the tooltip state
848
+ // isn't bound by change detection. This allows us to hide it even if the
849
+ // view ref has been detached from the CD tree.
850
+ const tooltip = this._tooltip.nativeElement;
851
+ const showClass = this._showAnimation;
852
+ const hideClass = this._hideAnimation;
853
+ tooltip.classList.remove(isVisible ? hideClass : showClass);
854
+ tooltip.classList.add(isVisible ? showClass : hideClass);
855
+ this._isVisible = isVisible;
856
+ // It's common for internal apps to disable animations using `* { animation: none !important }`
857
+ // which can break the opening sequence. Try to detect such cases and work around them.
858
+ if (isVisible && !this._animationsDisabled && typeof getComputedStyle === 'function') {
859
+ const styles = getComputedStyle(tooltip);
860
+ // Use `getPropertyValue` to avoid issues with property renaming.
861
+ if (styles.getPropertyValue('animation-duration') === '0s' ||
862
+ styles.getPropertyValue('animation-name') === 'none') {
863
+ this._animationsDisabled = true;
864
+ }
865
+ }
866
+ if (isVisible) {
867
+ this._onShow();
868
+ }
869
+ if (this._animationsDisabled) {
870
+ tooltip.classList.add('_mtx-animation-noopable');
871
+ this._finalizeAnimation(isVisible);
872
+ }
873
+ }
874
+ }
875
+ /** @nocollapse */ _TooltipComponentBase.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.1", ngImport: i0, type: _TooltipComponentBase, deps: [{ token: i0.ChangeDetectorRef }, { token: ANIMATION_MODULE_TYPE, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
876
+ /** @nocollapse */ _TooltipComponentBase.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "15.2.1", type: _TooltipComponentBase, ngImport: i0 });
877
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.1", ngImport: i0, type: _TooltipComponentBase, decorators: [{
878
+ type: Directive
879
+ }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }, { type: undefined, decorators: [{
880
+ type: Optional
881
+ }, {
882
+ type: Inject,
883
+ args: [ANIMATION_MODULE_TYPE]
884
+ }] }]; } });
885
+ /**
886
+ * Internal component that wraps the tooltip's content.
887
+ * @docs-private
888
+ */
889
+ class TooltipComponent extends _TooltipComponentBase {
890
+ constructor(changeDetectorRef, _elementRef, animationMode) {
891
+ super(changeDetectorRef, animationMode);
892
+ this._elementRef = _elementRef;
893
+ /* Whether the tooltip text overflows to multiple lines */
894
+ this._isMultiline = false;
895
+ this._showAnimation = 'mtx-mdc-tooltip-show';
896
+ this._hideAnimation = 'mtx-mdc-tooltip-hide';
897
+ }
898
+ _onShow() {
899
+ this._isMultiline = this._isTooltipMultiline();
900
+ this._markForCheck();
901
+ }
902
+ /** Whether the tooltip text has overflown to the next line */
903
+ _isTooltipMultiline() {
904
+ const rect = this._elementRef.nativeElement.getBoundingClientRect();
905
+ return rect.height > MIN_HEIGHT && rect.width >= MAX_WIDTH;
906
+ }
683
907
  }
684
- /** @nocollapse */ TooltipComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.1", ngImport: i0, type: TooltipComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i6.BreakpointObserver }], target: i0.ɵɵFactoryTarget.Component });
685
- /** @nocollapse */ TooltipComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.1", type: TooltipComponent, selector: "mtx-tooltip-component", host: { attributes: { "aria-hidden": "true" }, listeners: { "body:click": "this._handleBodyInteraction()" }, properties: { "style.zoom": "_visibility === \"visible\" ? 1 : null" } }, ngImport: i0, template: "<div class=\"mtx-tooltip\"\n [ngClass]=\"tooltipClass\"\n [class.mtx-tooltip-handset]=\"(_isHandset | async)?.matches\"\n [@state]=\"_visibility\"\n (@state.start)=\"_animationStart()\"\n (@state.done)=\"_animationDone($event)\">\n <ng-template [ngIf]=\"message | isTemplateRef\" [ngIfElse]=\"msgStrTpl\">\n <ng-template [ngTemplateOutlet]=\"$any(message)\"></ng-template>\n </ng-template>\n <ng-template #msgStrTpl>{{message}}</ng-template>\n</div>\n", styles: [".mtx-tooltip-panel{pointer-events:none!important}.mtx-tooltip{color:#fff;border-radius:4px;margin:14px;max-width:250px;padding-left:8px;padding-right:8px;overflow:hidden;text-overflow:ellipsis}.cdk-high-contrast-active .mtx-tooltip{outline:solid 1px}.mtx-tooltip-handset{margin:24px;padding-left:16px;padding-right:16px}\n"], dependencies: [{ kind: "directive", type: i7.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i7.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i7.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i7.AsyncPipe, name: "async" }, { kind: "pipe", type: i8.MtxIsTemplateRefPipe, name: "isTemplateRef" }], animations: [mtxTooltipAnimations.tooltipState], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
908
+ /** @nocollapse */ TooltipComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.1", ngImport: i0, type: TooltipComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i0.ElementRef }, { token: ANIMATION_MODULE_TYPE, optional: true }], target: i0.ɵɵFactoryTarget.Component });
909
+ /** @nocollapse */ TooltipComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.1", type: TooltipComponent, selector: "mtx-tooltip-component", host: { attributes: { "aria-hidden": "true" }, listeners: { "mouseleave": "_handleMouseLeave($event)" }, properties: { "style.zoom": "isVisible() ? 1 : null" } }, viewQueries: [{ propertyName: "_tooltip", first: true, predicate: ["tooltip"], descendants: true, static: true }], usesInheritance: true, ngImport: i0, template: "<div #tooltip\n class=\"mdc-tooltip mdc-tooltip--shown mtx-mdc-tooltip\"\n [ngClass]=\"tooltipClass\"\n (animationend)=\"_handleAnimationEnd($event)\"\n [class.mdc-tooltip--multiline]=\"_isMultiline\">\n <div class=\"mdc-tooltip__surface mdc-tooltip__surface-animation\">\n <ng-template [ngIf]=\"message | isTemplateRef\" [ngIfElse]=\"msgStrTpl\">\n <ng-template [ngTemplateOutlet]=\"$any(message)\"\n [ngTemplateOutletContext]=\"{ $implicit: tooltipContext }\"></ng-template>\n </ng-template>\n <ng-template #msgStrTpl>{{message}}</ng-template>\n </div>\n</div>\n", styles: [".mdc-tooltip__surface{word-break:var(--mdc-tooltip-word-break, normal);overflow-wrap:anywhere}.mdc-tooltip{position:fixed;display:none;z-index:9}.mdc-tooltip-wrapper--rich{position:relative}.mdc-tooltip--shown,.mdc-tooltip--showing,.mdc-tooltip--hide{display:inline-flex}.mdc-tooltip--shown.mdc-tooltip--rich,.mdc-tooltip--showing.mdc-tooltip--rich,.mdc-tooltip--hide.mdc-tooltip--rich{display:inline-block;left:-320px;position:absolute}.mdc-tooltip__surface{line-height:16px;padding:4px 8px;min-width:40px;max-width:200px;min-height:24px;max-height:40vh;box-sizing:border-box;overflow:hidden;text-align:center}.mdc-tooltip__surface:before{position:absolute;box-sizing:border-box;width:100%;height:100%;top:0;left:0;border:1px solid transparent;border-radius:inherit;content:\"\";pointer-events:none}@media screen and (forced-colors: active){.mdc-tooltip__surface:before{border-color:CanvasText}}.mdc-tooltip--rich .mdc-tooltip__surface{align-items:flex-start;display:flex;flex-direction:column;min-height:24px;min-width:40px;max-width:320px;position:relative}.mdc-tooltip--multiline .mdc-tooltip__surface{text-align:left}[dir=rtl] .mdc-tooltip--multiline .mdc-tooltip__surface,.mdc-tooltip--multiline .mdc-tooltip__surface[dir=rtl]{text-align:right}.mdc-tooltip__surface .mdc-tooltip__title{margin:0 8px}.mdc-tooltip__surface .mdc-tooltip__content{max-width:184px;margin:8px;text-align:left}[dir=rtl] .mdc-tooltip__surface .mdc-tooltip__content,.mdc-tooltip__surface .mdc-tooltip__content[dir=rtl]{text-align:right}.mdc-tooltip--rich .mdc-tooltip__surface .mdc-tooltip__content{max-width:304px;align-self:stretch}.mdc-tooltip__surface .mdc-tooltip__content-link{text-decoration:none}.mdc-tooltip--rich-actions,.mdc-tooltip__content,.mdc-tooltip__title{z-index:1}.mdc-tooltip__surface-animation{opacity:0;transform:scale(.8);will-change:transform,opacity}.mdc-tooltip--shown .mdc-tooltip__surface-animation{transform:scale(1);opacity:1}.mdc-tooltip--hide .mdc-tooltip__surface-animation{transform:scale(1)}.mdc-tooltip__caret-surface-top,.mdc-tooltip__caret-surface-bottom{position:absolute;height:24px;width:24px;transform:rotate(35deg) skewY(20deg) scaleX(.9396926208)}.mdc-tooltip__caret-surface-top .mdc-elevation-overlay,.mdc-tooltip__caret-surface-bottom .mdc-elevation-overlay{width:100%;height:100%;top:0;left:0}.mdc-tooltip__caret-surface-bottom{outline:1px solid transparent;z-index:-1}@media screen and (forced-colors: active){.mdc-tooltip__caret-surface-bottom{outline-color:CanvasText}}.mdc-tooltip__surface{background-color:var(--mdc-plain-tooltip-container-color, #fff)}.mdc-tooltip__surface,.mdc-tooltip__caret-surface-top,.mdc-tooltip__caret-surface-bottom{border-radius:var(--mdc-plain-tooltip-container-shape, var(--mdc-shape-small, 4px))}.mdc-tooltip__surface{color:var(--mdc-plain-tooltip-supporting-text-color, #000)}.mdc-tooltip__surface{font-family:var(--mdc-plain-tooltip-supporting-text-font, inherit);font-size:var(--mdc-plain-tooltip-supporting-text-size, inherit);font-weight:var(--mdc-plain-tooltip-supporting-text-weight, inherit);letter-spacing:var(--mdc-plain-tooltip-supporting-text-tracking, inherit)}.mtx-mdc-tooltip{position:relative;transform:scale(0)}.mtx-mdc-tooltip:before{content:\"\";inset:0;z-index:-1;position:absolute}.mtx-mdc-tooltip-panel-below .mtx-mdc-tooltip:before{top:-8px}.mtx-mdc-tooltip-panel-above .mtx-mdc-tooltip:before{bottom:-8px}.mtx-mdc-tooltip-panel-right .mtx-mdc-tooltip:before{left:-8px}.mtx-mdc-tooltip-panel-left .mtx-mdc-tooltip:before{right:-8px}.mtx-mdc-tooltip._mtx-animation-noopable{animation:none;transform:scale(1)}.mtx-mdc-tooltip-panel-non-interactive{pointer-events:none}@keyframes mtx-mdc-tooltip-show{0%{opacity:0;transform:scale(.8)}to{opacity:1;transform:scale(1)}}@keyframes mtx-mdc-tooltip-hide{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.8)}}.mtx-mdc-tooltip-show{animation:mtx-mdc-tooltip-show .15s cubic-bezier(0,0,.2,1) forwards}.mtx-mdc-tooltip-hide{animation:mtx-mdc-tooltip-hide 75ms cubic-bezier(.4,0,1,1) forwards}\n"], dependencies: [{ kind: "directive", type: i5.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i5.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i5.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "pipe", type: i6.MtxIsTemplateRefPipe, name: "isTemplateRef" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
686
910
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.1", ngImport: i0, type: TooltipComponent, decorators: [{
687
911
  type: Component,
688
- args: [{ selector: 'mtx-tooltip-component', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, animations: [mtxTooltipAnimations.tooltipState], host: {
912
+ args: [{ selector: 'mtx-tooltip-component', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: {
689
913
  // Forces the element to have a layout in IE and Edge. This fixes issues where the element
690
914
  // won't be rendered if the animations are disabled or there is no web animations polyfill.
691
- '[style.zoom]': '_visibility === "visible" ? 1 : null',
692
- '(body:click)': 'this._handleBodyInteraction()',
915
+ '[style.zoom]': 'isVisible() ? 1 : null',
916
+ '(mouseleave)': '_handleMouseLeave($event)',
693
917
  'aria-hidden': 'true',
694
- }, template: "<div class=\"mtx-tooltip\"\n [ngClass]=\"tooltipClass\"\n [class.mtx-tooltip-handset]=\"(_isHandset | async)?.matches\"\n [@state]=\"_visibility\"\n (@state.start)=\"_animationStart()\"\n (@state.done)=\"_animationDone($event)\">\n <ng-template [ngIf]=\"message | isTemplateRef\" [ngIfElse]=\"msgStrTpl\">\n <ng-template [ngTemplateOutlet]=\"$any(message)\"></ng-template>\n </ng-template>\n <ng-template #msgStrTpl>{{message}}</ng-template>\n</div>\n", styles: [".mtx-tooltip-panel{pointer-events:none!important}.mtx-tooltip{color:#fff;border-radius:4px;margin:14px;max-width:250px;padding-left:8px;padding-right:8px;overflow:hidden;text-overflow:ellipsis}.cdk-high-contrast-active .mtx-tooltip{outline:solid 1px}.mtx-tooltip-handset{margin:24px;padding-left:16px;padding-right:16px}\n"] }]
695
- }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }, { type: i6.BreakpointObserver }]; } });
918
+ }, template: "<div #tooltip\n class=\"mdc-tooltip mdc-tooltip--shown mtx-mdc-tooltip\"\n [ngClass]=\"tooltipClass\"\n (animationend)=\"_handleAnimationEnd($event)\"\n [class.mdc-tooltip--multiline]=\"_isMultiline\">\n <div class=\"mdc-tooltip__surface mdc-tooltip__surface-animation\">\n <ng-template [ngIf]=\"message | isTemplateRef\" [ngIfElse]=\"msgStrTpl\">\n <ng-template [ngTemplateOutlet]=\"$any(message)\"\n [ngTemplateOutletContext]=\"{ $implicit: tooltipContext }\"></ng-template>\n </ng-template>\n <ng-template #msgStrTpl>{{message}}</ng-template>\n </div>\n</div>\n", styles: [".mdc-tooltip__surface{word-break:var(--mdc-tooltip-word-break, normal);overflow-wrap:anywhere}.mdc-tooltip{position:fixed;display:none;z-index:9}.mdc-tooltip-wrapper--rich{position:relative}.mdc-tooltip--shown,.mdc-tooltip--showing,.mdc-tooltip--hide{display:inline-flex}.mdc-tooltip--shown.mdc-tooltip--rich,.mdc-tooltip--showing.mdc-tooltip--rich,.mdc-tooltip--hide.mdc-tooltip--rich{display:inline-block;left:-320px;position:absolute}.mdc-tooltip__surface{line-height:16px;padding:4px 8px;min-width:40px;max-width:200px;min-height:24px;max-height:40vh;box-sizing:border-box;overflow:hidden;text-align:center}.mdc-tooltip__surface:before{position:absolute;box-sizing:border-box;width:100%;height:100%;top:0;left:0;border:1px solid transparent;border-radius:inherit;content:\"\";pointer-events:none}@media screen and (forced-colors: active){.mdc-tooltip__surface:before{border-color:CanvasText}}.mdc-tooltip--rich .mdc-tooltip__surface{align-items:flex-start;display:flex;flex-direction:column;min-height:24px;min-width:40px;max-width:320px;position:relative}.mdc-tooltip--multiline .mdc-tooltip__surface{text-align:left}[dir=rtl] .mdc-tooltip--multiline .mdc-tooltip__surface,.mdc-tooltip--multiline .mdc-tooltip__surface[dir=rtl]{text-align:right}.mdc-tooltip__surface .mdc-tooltip__title{margin:0 8px}.mdc-tooltip__surface .mdc-tooltip__content{max-width:184px;margin:8px;text-align:left}[dir=rtl] .mdc-tooltip__surface .mdc-tooltip__content,.mdc-tooltip__surface .mdc-tooltip__content[dir=rtl]{text-align:right}.mdc-tooltip--rich .mdc-tooltip__surface .mdc-tooltip__content{max-width:304px;align-self:stretch}.mdc-tooltip__surface .mdc-tooltip__content-link{text-decoration:none}.mdc-tooltip--rich-actions,.mdc-tooltip__content,.mdc-tooltip__title{z-index:1}.mdc-tooltip__surface-animation{opacity:0;transform:scale(.8);will-change:transform,opacity}.mdc-tooltip--shown .mdc-tooltip__surface-animation{transform:scale(1);opacity:1}.mdc-tooltip--hide .mdc-tooltip__surface-animation{transform:scale(1)}.mdc-tooltip__caret-surface-top,.mdc-tooltip__caret-surface-bottom{position:absolute;height:24px;width:24px;transform:rotate(35deg) skewY(20deg) scaleX(.9396926208)}.mdc-tooltip__caret-surface-top .mdc-elevation-overlay,.mdc-tooltip__caret-surface-bottom .mdc-elevation-overlay{width:100%;height:100%;top:0;left:0}.mdc-tooltip__caret-surface-bottom{outline:1px solid transparent;z-index:-1}@media screen and (forced-colors: active){.mdc-tooltip__caret-surface-bottom{outline-color:CanvasText}}.mdc-tooltip__surface{background-color:var(--mdc-plain-tooltip-container-color, #fff)}.mdc-tooltip__surface,.mdc-tooltip__caret-surface-top,.mdc-tooltip__caret-surface-bottom{border-radius:var(--mdc-plain-tooltip-container-shape, var(--mdc-shape-small, 4px))}.mdc-tooltip__surface{color:var(--mdc-plain-tooltip-supporting-text-color, #000)}.mdc-tooltip__surface{font-family:var(--mdc-plain-tooltip-supporting-text-font, inherit);font-size:var(--mdc-plain-tooltip-supporting-text-size, inherit);font-weight:var(--mdc-plain-tooltip-supporting-text-weight, inherit);letter-spacing:var(--mdc-plain-tooltip-supporting-text-tracking, inherit)}.mtx-mdc-tooltip{position:relative;transform:scale(0)}.mtx-mdc-tooltip:before{content:\"\";inset:0;z-index:-1;position:absolute}.mtx-mdc-tooltip-panel-below .mtx-mdc-tooltip:before{top:-8px}.mtx-mdc-tooltip-panel-above .mtx-mdc-tooltip:before{bottom:-8px}.mtx-mdc-tooltip-panel-right .mtx-mdc-tooltip:before{left:-8px}.mtx-mdc-tooltip-panel-left .mtx-mdc-tooltip:before{right:-8px}.mtx-mdc-tooltip._mtx-animation-noopable{animation:none;transform:scale(1)}.mtx-mdc-tooltip-panel-non-interactive{pointer-events:none}@keyframes mtx-mdc-tooltip-show{0%{opacity:0;transform:scale(.8)}to{opacity:1;transform:scale(1)}}@keyframes mtx-mdc-tooltip-hide{0%{opacity:1;transform:scale(1)}to{opacity:0;transform:scale(.8)}}.mtx-mdc-tooltip-show{animation:mtx-mdc-tooltip-show .15s cubic-bezier(0,0,.2,1) forwards}.mtx-mdc-tooltip-hide{animation:mtx-mdc-tooltip-hide 75ms cubic-bezier(.4,0,1,1) forwards}\n"] }]
919
+ }], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }, { type: i0.ElementRef }, { type: undefined, decorators: [{
920
+ type: Optional
921
+ }, {
922
+ type: Inject,
923
+ args: [ANIMATION_MODULE_TYPE]
924
+ }] }]; }, propDecorators: { _tooltip: [{
925
+ type: ViewChild,
926
+ args: ['tooltip', {
927
+ // Use a static query here since we interact directly with
928
+ // the DOM which can happen before `ngAfterViewInit`.
929
+ static: true,
930
+ }]
931
+ }] } });
696
932
 
697
933
  class MtxTooltipModule {
698
934
  }
@@ -709,9 +945,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.1", ngImpor
709
945
  }]
710
946
  }] });
711
947
 
948
+ /**
949
+ * Animations used by MatTooltip.
950
+ * @docs-private
951
+ */
952
+ const matTooltipAnimations = {
953
+ /** Animation that transitions a tooltip in and out. */
954
+ tooltipState: trigger('state', [
955
+ // TODO(crisbeto): these values are based on MDC's CSS.
956
+ // We should be able to use their styles directly once we land #19432.
957
+ state('initial, void, hidden', style({ opacity: 0, transform: 'scale(0.8)' })),
958
+ state('visible', style({ transform: 'scale(1)' })),
959
+ transition('* => visible', animate('150ms cubic-bezier(0, 0, 0.2, 1)')),
960
+ transition('* => hidden', animate('75ms cubic-bezier(0.4, 0, 1, 1)')),
961
+ ]),
962
+ };
963
+
712
964
  /**
713
965
  * Generated bundle index. Do not edit.
714
966
  */
715
967
 
716
- export { MTX_TOOLTIP_DEFAULT_OPTIONS, MTX_TOOLTIP_DEFAULT_OPTIONS_FACTORY, MTX_TOOLTIP_SCROLL_STRATEGY, MTX_TOOLTIP_SCROLL_STRATEGY_FACTORY, MTX_TOOLTIP_SCROLL_STRATEGY_FACTORY_PROVIDER, MtxTooltip, MtxTooltipModule, SCROLL_THROTTLE_MS, TOOLTIP_PANEL_CLASS, TooltipComponent, getMtxTooltipInvalidPositionError, mtxTooltipAnimations };
968
+ export { MTX_TOOLTIP_DEFAULT_OPTIONS, MTX_TOOLTIP_DEFAULT_OPTIONS_FACTORY, MTX_TOOLTIP_SCROLL_STRATEGY, MTX_TOOLTIP_SCROLL_STRATEGY_FACTORY, MTX_TOOLTIP_SCROLL_STRATEGY_FACTORY_PROVIDER, MtxTooltip, MtxTooltipModule, SCROLL_THROTTLE_MS, TOOLTIP_PANEL_CLASS, TooltipComponent, _MtxTooltipBase, _TooltipComponentBase, getMtxTooltipInvalidPositionError, matTooltipAnimations };
717
969
  //# sourceMappingURL=mtxTooltip.mjs.map