@vaadin/notification 24.6.0-alpha4 → 24.6.0-alpha6

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.
@@ -4,81 +4,33 @@
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
6
  import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
7
- import { render } from 'lit';
8
- import { isTemplateResult } from 'lit/directive-helpers.js';
9
- import { isIOS } from '@vaadin/component-base/src/browser-utils.js';
10
7
  import { defineCustomElement } from '@vaadin/component-base/src/define.js';
11
8
  import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
12
- import { OverlayClassMixin } from '@vaadin/component-base/src/overlay-class-mixin.js';
13
- import { processTemplates } from '@vaadin/component-base/src/templates.js';
14
- import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
15
- import { ThemePropertyMixin } from '@vaadin/vaadin-themable-mixin/vaadin-theme-property-mixin.js';
9
+ import { registerStyles, ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
10
+ import { NotificationContainerMixin, NotificationMixin } from './vaadin-notification-mixin.js';
11
+ import { notificationCardStyles, notificationContainerStyles } from './vaadin-notification-styles.js';
12
+
13
+ registerStyles('vaadin-notification-container', notificationContainerStyles, {
14
+ moduleId: 'vaadin-notification-container-styles',
15
+ });
16
+
17
+ registerStyles('vaadin-notification-card', notificationCardStyles, {
18
+ moduleId: 'vaadin-notification-card-styles',
19
+ });
16
20
 
17
21
  /**
18
22
  * An element used internally by `<vaadin-notification>`. Not intended to be used separately.
19
23
  *
20
24
  * @customElement
21
25
  * @extends HTMLElement
26
+ * @mixes NotificationContainerMixin
22
27
  * @mixes ElementMixin
23
28
  * @mixes ThemableMixin
24
29
  * @private
25
30
  */
26
- class NotificationContainer extends ThemableMixin(ElementMixin(PolymerElement)) {
31
+ class NotificationContainer extends NotificationContainerMixin(ThemableMixin(ElementMixin(PolymerElement))) {
27
32
  static get template() {
28
33
  return html`
29
- <style>
30
- :host {
31
- position: fixed;
32
- z-index: 1000;
33
- top: 0;
34
- left: 0;
35
- bottom: 0;
36
- right: 0;
37
- box-sizing: border-box;
38
-
39
- display: flex;
40
- flex-direction: column;
41
- align-items: stretch;
42
- pointer-events: none;
43
- }
44
-
45
- [region-group] {
46
- flex: 1 1 0%;
47
- display: flex;
48
- }
49
-
50
- [region-group='top'] {
51
- align-items: flex-start;
52
- }
53
-
54
- [region-group='bottom'] {
55
- align-items: flex-end;
56
- }
57
-
58
- [region-group] > [region] {
59
- flex: 1 1 0%;
60
- }
61
-
62
- @media (max-width: 420px) {
63
- [region-group] {
64
- flex-direction: column;
65
- align-items: stretch;
66
- }
67
-
68
- [region-group='top'] {
69
- justify-content: flex-start;
70
- }
71
-
72
- [region-group='bottom'] {
73
- justify-content: flex-end;
74
- }
75
-
76
- [region-group] > [region] {
77
- flex: initial;
78
- }
79
- }
80
- </style>
81
-
82
34
  <div region="top-stretch"><slot name="top-stretch"></slot></div>
83
35
  <div region-group="top">
84
36
  <div region="top-start"><slot name="top-start"></slot></div>
@@ -98,71 +50,6 @@ class NotificationContainer extends ThemableMixin(ElementMixin(PolymerElement))
98
50
  static get is() {
99
51
  return 'vaadin-notification-container';
100
52
  }
101
-
102
- static get properties() {
103
- return {
104
- /**
105
- * True when the container is opened
106
- * @type {boolean}
107
- */
108
- opened: {
109
- type: Boolean,
110
- value: false,
111
- observer: '_openedChanged',
112
- },
113
- };
114
- }
115
-
116
- constructor() {
117
- super();
118
-
119
- this._boundVaadinOverlayClose = this._onVaadinOverlayClose.bind(this);
120
- if (isIOS) {
121
- this._boundIosResizeListener = () => this._detectIosNavbar();
122
- }
123
- }
124
-
125
- /** @private */
126
- _openedChanged(opened) {
127
- if (opened) {
128
- document.body.appendChild(this);
129
- document.addEventListener('vaadin-overlay-close', this._boundVaadinOverlayClose);
130
- if (this._boundIosResizeListener) {
131
- this._detectIosNavbar();
132
- window.addEventListener('resize', this._boundIosResizeListener);
133
- }
134
- } else {
135
- document.body.removeChild(this);
136
- document.removeEventListener('vaadin-overlay-close', this._boundVaadinOverlayClose);
137
- if (this._boundIosResizeListener) {
138
- window.removeEventListener('resize', this._boundIosResizeListener);
139
- }
140
- }
141
- }
142
-
143
- /** @private */
144
- _detectIosNavbar() {
145
- const innerHeight = window.innerHeight;
146
- const innerWidth = window.innerWidth;
147
- const landscape = innerWidth > innerHeight;
148
- const clientHeight = document.documentElement.clientHeight;
149
- if (landscape && clientHeight > innerHeight) {
150
- this.style.bottom = `${clientHeight - innerHeight}px`;
151
- } else {
152
- this.style.bottom = '0';
153
- }
154
- }
155
-
156
- /** @private */
157
- _onVaadinOverlayClose(event) {
158
- // Notifications are a separate overlay mechanism from vaadin-overlay, and
159
- // interacting with them should not close modal overlays
160
- const sourceEvent = event.detail.sourceEvent;
161
- const isFromNotification = sourceEvent && sourceEvent.composedPath().indexOf(this) >= 0;
162
- if (isFromNotification) {
163
- event.preventDefault();
164
- }
165
- }
166
53
  }
167
54
 
168
55
  /**
@@ -176,22 +63,6 @@ class NotificationContainer extends ThemableMixin(ElementMixin(PolymerElement))
176
63
  class NotificationCard extends ThemableMixin(PolymerElement) {
177
64
  static get template() {
178
65
  return html`
179
- <style>
180
- :host {
181
- display: block;
182
- }
183
-
184
- [part='overlay'] {
185
- pointer-events: auto;
186
- }
187
-
188
- @media (forced-colors: active) {
189
- [part='overlay'] {
190
- outline: 3px solid;
191
- }
192
- }
193
- </style>
194
-
195
66
  <div part="overlay">
196
67
  <div part="content">
197
68
  <slot></slot>
@@ -260,11 +131,10 @@ class NotificationCard extends ThemableMixin(PolymerElement) {
260
131
  *
261
132
  * @customElement
262
133
  * @extends HTMLElement
263
- * @mixes ThemePropertyMixin
134
+ * @mixes NotificationMixin
264
135
  * @mixes ElementMixin
265
- * @mixes OverlayClassMixin
266
136
  */
267
- class Notification extends OverlayClassMixin(ThemePropertyMixin(ElementMixin(PolymerElement))) {
137
+ class Notification extends NotificationMixin(ElementMixin(PolymerElement)) {
268
138
  static get template() {
269
139
  return html`
270
140
  <style>
@@ -282,329 +152,6 @@ class Notification extends OverlayClassMixin(ThemePropertyMixin(ElementMixin(Pol
282
152
  static get is() {
283
153
  return 'vaadin-notification';
284
154
  }
285
-
286
- static get properties() {
287
- return {
288
- /**
289
- * When true, the notification card has `aria-live` attribute set to
290
- * `assertive` instead of `polite`. This makes screen readers announce
291
- * the notification content immediately when it appears.
292
- */
293
- assertive: {
294
- type: Boolean,
295
- value: false,
296
- },
297
-
298
- /**
299
- * The duration in milliseconds to show the notification.
300
- * Set to `0` or a negative number to disable the notification auto-closing.
301
- * @type {number}
302
- */
303
- duration: {
304
- type: Number,
305
- value: 5000,
306
- },
307
-
308
- /**
309
- * True if the notification is currently displayed.
310
- * @type {boolean}
311
- */
312
- opened: {
313
- type: Boolean,
314
- value: false,
315
- notify: true,
316
- observer: '_openedChanged',
317
- },
318
-
319
- /**
320
- * Alignment of the notification in the viewport
321
- * Valid values are `top-stretch|top-start|top-center|top-end|middle|bottom-start|bottom-center|bottom-end|bottom-stretch`
322
- * @type {!NotificationPosition}
323
- */
324
- position: {
325
- type: String,
326
- value: 'bottom-start',
327
- observer: '_positionChanged',
328
- },
329
-
330
- /**
331
- * Custom function for rendering the content of the notification.
332
- * Receives two arguments:
333
- *
334
- * - `root` The `<vaadin-notification-card>` DOM element. Append
335
- * your content to it.
336
- * - `notification` The reference to the `<vaadin-notification>` element.
337
- * @type {!NotificationRenderer | undefined}
338
- */
339
- renderer: Function,
340
- };
341
- }
342
-
343
- static get observers() {
344
- return ['_durationChanged(duration, opened)', '_rendererChanged(renderer, opened, _overlayElement)'];
345
- }
346
-
347
- /**
348
- * Shows a notification with the given content.
349
- * By default, positions the notification at `bottom-start` and uses a 5 second duration.
350
- * An options object can be passed to configure the notification.
351
- * The options object has the following structure:
352
- *
353
- * ```
354
- * {
355
- * assertive?: boolean
356
- * position?: string
357
- * duration?: number
358
- * theme?: string
359
- * }
360
- * ```
361
- *
362
- * See the individual documentation for:
363
- * - [`assertive`](#/elements/vaadin-notification#property-assertive)
364
- * - [`position`](#/elements/vaadin-notification#property-position)
365
- * - [`duration`](#/elements/vaadin-notification#property-duration)
366
- *
367
- * @param contents the contents to show, either as a string or a Lit template.
368
- * @param options optional options for customizing the notification.
369
- */
370
- static show(contents, options) {
371
- if (isTemplateResult(contents)) {
372
- return Notification._createAndShowNotification((root) => {
373
- render(contents, root);
374
- }, options);
375
- }
376
- return Notification._createAndShowNotification((root) => {
377
- root.innerText = contents;
378
- }, options);
379
- }
380
-
381
- /** @private */
382
- static _createAndShowNotification(renderer, options) {
383
- const notification = document.createElement(Notification.is);
384
- if (options && Number.isFinite(options.duration)) {
385
- notification.duration = options.duration;
386
- }
387
- if (options && options.position) {
388
- notification.position = options.position;
389
- }
390
- if (options && options.assertive) {
391
- notification.assertive = options.assertive;
392
- }
393
- if (options && options.theme) {
394
- notification.setAttribute('theme', options.theme);
395
- }
396
- notification.renderer = renderer;
397
- document.body.appendChild(notification);
398
- notification.opened = true;
399
-
400
- notification.addEventListener('opened-changed', (e) => {
401
- if (!e.detail.value) {
402
- notification.remove();
403
- }
404
- });
405
-
406
- return notification;
407
- }
408
-
409
- /** @private */
410
- get _container() {
411
- if (!Notification._container) {
412
- Notification._container = document.createElement('vaadin-notification-container');
413
- document.body.appendChild(Notification._container);
414
- }
415
- return Notification._container;
416
- }
417
-
418
- /** @protected */
419
- get _card() {
420
- return this._overlayElement;
421
- }
422
-
423
- /** @protected */
424
- ready() {
425
- super.ready();
426
-
427
- this._overlayElement = this.shadowRoot.querySelector('vaadin-notification-card');
428
-
429
- processTemplates(this);
430
- }
431
-
432
- /** @protected */
433
- disconnectedCallback() {
434
- super.disconnectedCallback();
435
- queueMicrotask(() => {
436
- if (!this.isConnected) {
437
- this.opened = false;
438
- }
439
- });
440
- }
441
-
442
- /**
443
- * Requests an update for the content of the notification.
444
- * While performing the update, it invokes the renderer passed in the `renderer` property.
445
- *
446
- * It is not guaranteed that the update happens immediately (synchronously) after it is requested.
447
- */
448
- requestContentUpdate() {
449
- if (!this.renderer) {
450
- return;
451
- }
452
-
453
- this.renderer(this._card, this);
454
- }
455
-
456
- /** @private */
457
- __computeAriaLive(assertive) {
458
- return assertive ? 'assertive' : 'polite';
459
- }
460
-
461
- /** @private */
462
- _rendererChanged(renderer, opened, card) {
463
- if (!card) {
464
- return;
465
- }
466
-
467
- const rendererChanged = this._oldRenderer !== renderer;
468
- this._oldRenderer = renderer;
469
-
470
- if (rendererChanged) {
471
- card.innerHTML = '';
472
- // Whenever a Lit-based renderer is used, it assigns a Lit part to the node it was rendered into.
473
- // When clearing the rendered content, this part needs to be manually disposed of.
474
- // Otherwise, using a Lit-based renderer on the same node will throw an exception or render nothing afterward.
475
- delete card._$litPart$;
476
- }
477
-
478
- if (opened) {
479
- if (!this._didAnimateNotificationAppend) {
480
- this._animatedAppendNotificationCard();
481
- }
482
- this.requestContentUpdate();
483
- }
484
- }
485
-
486
- /**
487
- * Opens the notification.
488
- */
489
- open() {
490
- this.opened = true;
491
- }
492
-
493
- /**
494
- * Closes the notification.
495
- */
496
- close() {
497
- this.opened = false;
498
- }
499
-
500
- /** @private */
501
- _openedChanged(opened) {
502
- if (opened) {
503
- this._container.opened = true;
504
- this._animatedAppendNotificationCard();
505
- } else if (this._card) {
506
- this._closeNotificationCard();
507
- }
508
- }
509
-
510
- /** @private */
511
- __cleanUpOpeningClosingState() {
512
- this._card.removeAttribute('opening');
513
- this._card.removeAttribute('closing');
514
- this._card.removeEventListener('animationend', this.__animationEndListener);
515
- }
516
-
517
- /** @private */
518
- _animatedAppendNotificationCard() {
519
- if (this._card) {
520
- this.__cleanUpOpeningClosingState();
521
-
522
- this._card.setAttribute('opening', '');
523
- this._appendNotificationCard();
524
- this.__animationEndListener = () => this.__cleanUpOpeningClosingState();
525
- this._card.addEventListener('animationend', this.__animationEndListener);
526
- this._didAnimateNotificationAppend = true;
527
- } else {
528
- this._didAnimateNotificationAppend = false;
529
- }
530
- }
531
-
532
- /** @private */
533
- _appendNotificationCard() {
534
- if (!this._card) {
535
- return;
536
- }
537
-
538
- if (!this._container.shadowRoot.querySelector(`slot[name="${this.position}"]`)) {
539
- console.warn(`Invalid alignment parameter provided: position=${this.position}`);
540
- return;
541
- }
542
-
543
- this._card.slot = this.position;
544
- if (this._container.firstElementChild && /top/u.test(this.position)) {
545
- this._container.insertBefore(this._card, this._container.firstElementChild);
546
- } else {
547
- this._container.appendChild(this._card);
548
- }
549
- }
550
-
551
- /** @private */
552
- _removeNotificationCard() {
553
- if (this._card.parentNode) {
554
- this._card.parentNode.removeChild(this._card);
555
- }
556
- this._card.removeAttribute('closing');
557
- this._container.opened = Boolean(this._container.firstElementChild);
558
- this.dispatchEvent(new CustomEvent('closed'));
559
- }
560
-
561
- /** @private */
562
- _closeNotificationCard() {
563
- if (this._durationTimeoutId) {
564
- clearTimeout(this._durationTimeoutId);
565
- }
566
- this._animatedRemoveNotificationCard();
567
- }
568
-
569
- /** @private */
570
- _animatedRemoveNotificationCard() {
571
- this.__cleanUpOpeningClosingState();
572
-
573
- this._card.setAttribute('closing', '');
574
- const name = getComputedStyle(this._card).getPropertyValue('animation-name');
575
- if (name && name !== 'none') {
576
- this.__animationEndListener = () => {
577
- this._removeNotificationCard();
578
- this.__cleanUpOpeningClosingState();
579
- };
580
- this._card.addEventListener('animationend', this.__animationEndListener);
581
- } else {
582
- this._removeNotificationCard();
583
- }
584
- }
585
-
586
- /** @private */
587
- _positionChanged() {
588
- if (this.opened) {
589
- this._animatedAppendNotificationCard();
590
- }
591
- }
592
-
593
- /** @private */
594
- _durationChanged(duration, opened) {
595
- if (opened) {
596
- clearTimeout(this._durationTimeoutId);
597
- if (duration > 0) {
598
- this._durationTimeoutId = setTimeout(() => this.close(), duration);
599
- }
600
- }
601
- }
602
-
603
- /**
604
- * Fired when the notification is closed.
605
- *
606
- * @event closed
607
- */
608
155
  }
609
156
 
610
157
  defineCustomElement(NotificationContainer);
@@ -0,0 +1,2 @@
1
+ import './vaadin-notification-styles.js';
2
+ import '../../src/vaadin-lit-notification.js';
@@ -0,0 +1,2 @@
1
+ import './vaadin-notification-styles.js';
2
+ import '../../src/vaadin-lit-notification.js';
@@ -0,0 +1,2 @@
1
+ import './vaadin-notification-styles.js';
2
+ import '../../src/vaadin-lit-notification.js';
@@ -0,0 +1,2 @@
1
+ import './vaadin-notification-styles.js';
2
+ import '../../src/vaadin-lit-notification.js';
@@ -0,0 +1 @@
1
+ export * from './vaadin-notification.js';
@@ -0,0 +1,2 @@
1
+ import './theme/lumo/vaadin-lit-notification.js';
2
+ export * from './src/vaadin-lit-notification.js';
package/web-types.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/web-types",
3
3
  "name": "@vaadin/notification",
4
- "version": "24.6.0-alpha4",
4
+ "version": "24.6.0-alpha6",
5
5
  "description-markup": "markdown",
6
6
  "contributions": {
7
7
  "html": {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/web-types",
3
3
  "name": "@vaadin/notification",
4
- "version": "24.6.0-alpha4",
4
+ "version": "24.6.0-alpha6",
5
5
  "description-markup": "markdown",
6
6
  "framework": "lit",
7
7
  "framework-config": {