@vaadin/dialog 23.0.4 → 23.1.0-alpha1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaadin/dialog",
3
- "version": "23.0.4",
3
+ "version": "23.1.0-alpha1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -35,18 +35,18 @@
35
35
  "dependencies": {
36
36
  "@open-wc/dedupe-mixin": "^1.3.0",
37
37
  "@polymer/polymer": "^3.0.0",
38
- "@vaadin/component-base": "^23.0.4",
39
- "@vaadin/vaadin-lumo-styles": "^23.0.4",
40
- "@vaadin/vaadin-material-styles": "^23.0.4",
41
- "@vaadin/vaadin-overlay": "^23.0.4",
42
- "@vaadin/vaadin-themable-mixin": "^23.0.4"
38
+ "@vaadin/component-base": "23.1.0-alpha1",
39
+ "@vaadin/vaadin-lumo-styles": "23.1.0-alpha1",
40
+ "@vaadin/vaadin-material-styles": "23.1.0-alpha1",
41
+ "@vaadin/vaadin-overlay": "23.1.0-alpha1",
42
+ "@vaadin/vaadin-themable-mixin": "23.1.0-alpha1"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@esm-bundle/chai": "^4.3.4",
46
- "@vaadin/polymer-legacy-adapter": "^23.0.4",
46
+ "@vaadin/polymer-legacy-adapter": "23.1.0-alpha1",
47
47
  "@vaadin/testing-helpers": "^0.3.2",
48
- "@vaadin/text-area": "^23.0.4",
48
+ "@vaadin/text-area": "23.1.0-alpha1",
49
49
  "sinon": "^9.2.1"
50
50
  },
51
- "gitHead": "d8db2046661c42fb5aac09ed683b500bf4613b26"
51
+ "gitHead": "5d0cdee069f866037c507265fafb4d0476795333"
52
52
  }
@@ -94,6 +94,23 @@ export type DialogEventMap = HTMLElementEventMap & DialogCustomEventMap;
94
94
  * See [`<vaadin-overlay>`](#/elements/vaadin-overlay) documentation.
95
95
  * for `<vaadin-dialog-overlay>` parts.
96
96
  *
97
+ * In addition to `<vaadin-overlay>` parts, the following parts are available for styling:
98
+ *
99
+ * Part name | Description
100
+ * -----------------|-------------------------------------------
101
+ * `header` | Element wrapping title and header content
102
+ * `header-content` | Element wrapping the header content slot
103
+ * `title` | Element wrapping the title slot
104
+ * `footer` | Element wrapping the footer slot
105
+ *
106
+ * The following state attributes are available for styling:
107
+ *
108
+ * Attribute | Description
109
+ * -----------------|--------------------------------------------
110
+ * `has-title` | Set when the element has a title
111
+ * `has-header` | Set when the element has header renderer
112
+ * `has-footer` | Set when the element has footer renderer
113
+ *
97
114
  * Note: the `theme` attribute value set on `<vaadin-dialog>` is
98
115
  * propagated to the internal `<vaadin-dialog-overlay>` component.
99
116
  *
@@ -136,14 +153,52 @@ declare class Dialog extends ThemePropertyMixin(ElementMixin(DialogDraggableMixi
136
153
  */
137
154
  renderer: DialogRenderer | null | undefined;
138
155
 
156
+ /**
157
+ * String used for rendering a dialog title.
158
+ *
159
+ * If both `headerTitle` and `headerRenderer` are defined, the title
160
+ * and the elements created by the renderer will be placed next to
161
+ * each other, with the title coming first.
162
+ *
163
+ * When `headerTitle` is set, the attribute `has-title` is added to the overlay element.
164
+ * @attr {string} header-title
165
+ */
166
+ headerTitle: string | null | undefined;
167
+
168
+ /**
169
+ * Custom function for rendering the dialog header.
170
+ * Receives two arguments:
171
+ *
172
+ * - `root` The root container DOM element. Append your content to it.
173
+ * - `dialog` The reference to the `<vaadin-dialog>` element.
174
+ *
175
+ * If both `headerTitle` and `headerRenderer` are defined, the title
176
+ * and the elements created by the renderer will be placed next to
177
+ * each other, with the title coming first.
178
+ *
179
+ * When `headerRenderer` is set, the attribute `has-header` is added to the overlay element.
180
+ */
181
+ headerRenderer: DialogRenderer | null | undefined;
182
+
183
+ /**
184
+ * Custom function for rendering the dialog footer.
185
+ * Receives two arguments:
186
+ *
187
+ * - `root` The root container DOM element. Append your content to it.
188
+ * - `dialog` The reference to the `<vaadin-dialog>` element.
189
+ *
190
+ * When `footerRenderer` is set, the attribute `has-footer` is added to the overlay element.
191
+ */
192
+ footerRenderer: DialogRenderer | null | undefined;
193
+
139
194
  /**
140
195
  * Set to true to remove backdrop and allow click events on background elements.
141
196
  */
142
197
  modeless: boolean;
143
198
 
144
199
  /**
145
- * Requests an update for the content of the dialog.
146
- * While performing the update, it invokes the renderer passed in the `renderer` property.
200
+ * While performing the update, it invokes the renderer passed in the `renderer` property,
201
+ * as well as `headerRender` and `footerRenderer` properties, if these are defined.
147
202
  *
148
203
  * It is not guaranteed that the update happens immediately (synchronously) after it is requested.
149
204
  */
@@ -15,6 +15,50 @@ import { DialogResizableMixin } from './vaadin-dialog-resizable-mixin.js';
15
15
  registerStyles(
16
16
  'vaadin-dialog-overlay',
17
17
  css`
18
+ /* prefixing with the element tag to avoid styling confirm-dialog header part */
19
+ header[part='header'] {
20
+ display: flex;
21
+ }
22
+
23
+ [part='header-content'] {
24
+ flex: 1;
25
+ }
26
+
27
+ /* prefixing with the element tag to avoid styling confirm-dialog footer part */
28
+ footer[part='footer'] {
29
+ display: flex;
30
+ justify-content: flex-end;
31
+ }
32
+
33
+ :host(:not([has-title]):not([has-header])) header[part='header'],
34
+ :host(:not([has-title])) [part='title'] {
35
+ display: none;
36
+ }
37
+
38
+ :host(:not([has-footer])) footer[part='footer'] {
39
+ display: none;
40
+ }
41
+
42
+ :host(:is([has-title], [has-header], [has-footer])) [part='content'] {
43
+ min-height: 100%;
44
+ height: auto;
45
+ }
46
+
47
+ @media (min-height: 320px) {
48
+ :host(:is([has-title], [has-header], [has-footer])) .resizer-container {
49
+ overflow: hidden;
50
+ display: flex;
51
+ flex-direction: column;
52
+ }
53
+
54
+ :host(:is([has-title], [has-header], [has-footer])) [part='content'] {
55
+ flex: 1;
56
+ overflow: auto;
57
+ min-height: auto;
58
+ height: 100%;
59
+ }
60
+ }
61
+
18
62
  /*
19
63
  NOTE(platosha): Make some min-width to prevent collapsing of the content
20
64
  taking the parent width, e. g., <vaadin-grid> and such.
@@ -53,18 +97,188 @@ export class DialogOverlay extends OverlayElement {
53
97
  resizerContainer.classList.add('resizer-container');
54
98
  resizerContainer.appendChild(contentPart);
55
99
  overlayPart.appendChild(resizerContainer);
100
+
101
+ const headerContainer = document.createElement('header');
102
+ headerContainer.setAttribute('part', 'header');
103
+ resizerContainer.insertBefore(headerContainer, contentPart);
104
+
105
+ const titleContainer = document.createElement('div');
106
+ titleContainer.setAttribute('part', 'title');
107
+ headerContainer.appendChild(titleContainer);
108
+
109
+ const titleSlot = document.createElement('slot');
110
+ titleSlot.setAttribute('name', 'title');
111
+ titleContainer.appendChild(titleSlot);
112
+
113
+ const headerContentContainer = document.createElement('div');
114
+ headerContentContainer.setAttribute('part', 'header-content');
115
+ headerContainer.appendChild(headerContentContainer);
116
+
117
+ const headerSlot = document.createElement('slot');
118
+ headerSlot.setAttribute('name', 'header-content');
119
+ headerContentContainer.appendChild(headerSlot);
120
+
121
+ const footerContainer = document.createElement('footer');
122
+ footerContainer.setAttribute('part', 'footer');
123
+ resizerContainer.appendChild(footerContainer);
124
+
125
+ const footerSlot = document.createElement('slot');
126
+ footerSlot.setAttribute('name', 'footer');
127
+ footerContainer.appendChild(footerSlot);
56
128
  }
57
129
  return memoizedTemplate;
58
130
  }
59
131
 
132
+ static get observers() {
133
+ return [
134
+ '_headerFooterRendererChange(headerRenderer, footerRenderer, opened)',
135
+ '_headerTitleChanged(headerTitle, opened)'
136
+ ];
137
+ }
138
+
60
139
  static get properties() {
61
140
  return {
62
141
  modeless: Boolean,
63
142
 
64
- withBackdrop: Boolean
143
+ withBackdrop: Boolean,
144
+
145
+ headerTitle: String,
146
+
147
+ headerRenderer: Function,
148
+
149
+ footerRenderer: Function
65
150
  };
66
151
  }
67
152
 
153
+ ready() {
154
+ super.ready();
155
+
156
+ const uniqueId = (DialogOverlay._uniqueId = 1 + DialogOverlay._uniqueId || 0);
157
+ this._titleId = `${this.constructor.is}-title-${uniqueId}`;
158
+ }
159
+
160
+ /** @private */
161
+ __createContainer(slot) {
162
+ const container = document.createElement('div');
163
+ container.setAttribute('slot', slot);
164
+ return container;
165
+ }
166
+
167
+ /** @private */
168
+ __clearContainer(container) {
169
+ container.innerHTML = '';
170
+ // Whenever a Lit-based renderer is used, it assigns a Lit part to the node it was rendered into.
171
+ // When clearing the rendered content, this part needs to be manually disposed of.
172
+ // Otherwise, using a Lit-based renderer on the same node will throw an exception or render nothing afterward.
173
+ delete container._$litPart$;
174
+ }
175
+
176
+ /** @private */
177
+ __initContainer(container, slot) {
178
+ if (container) {
179
+ // Reset existing container in case if a new renderer is set.
180
+ this.__clearContainer(container);
181
+ } else {
182
+ // Create the container, but wait to append it until requestContentUpdate is called.
183
+ container = this.__createContainer(slot);
184
+ }
185
+ return container;
186
+ }
187
+
188
+ /** @private */
189
+ _headerFooterRendererChange(headerRenderer, footerRenderer, opened) {
190
+ const headerRendererChanged = this.__oldHeaderRenderer !== headerRenderer;
191
+ this.__oldHeaderRenderer = headerRenderer;
192
+
193
+ const footerRendererChanged = this.__oldFooterRenderer !== footerRenderer;
194
+ this.__oldFooterRenderer = footerRenderer;
195
+
196
+ const openedChanged = this._oldOpenedFooterHeader !== opened;
197
+ this._oldOpenedFooterHeader = opened;
198
+
199
+ if (headerRendererChanged) {
200
+ if (headerRenderer) {
201
+ this.headerContainer = this.__initContainer(this.headerContainer, 'header-content');
202
+ } else if (this.headerContainer) {
203
+ this.headerContainer.remove();
204
+ this.headerContainer = null;
205
+ }
206
+ }
207
+
208
+ if (footerRendererChanged) {
209
+ if (footerRenderer) {
210
+ this.footerContainer = this.__initContainer(this.footerContainer, 'footer');
211
+ } else if (this.footerContainer) {
212
+ this.footerContainer.remove();
213
+ this.footerContainer = null;
214
+ }
215
+ }
216
+
217
+ if (
218
+ (headerRenderer && (headerRendererChanged || openedChanged)) ||
219
+ (footerRenderer && (footerRendererChanged || openedChanged))
220
+ ) {
221
+ if (opened) {
222
+ this.requestContentUpdate();
223
+ }
224
+ }
225
+
226
+ this.toggleAttribute('has-header', !!headerRenderer);
227
+ this.toggleAttribute('has-footer', !!footerRenderer);
228
+ }
229
+
230
+ /** @private */
231
+ _headerTitleChanged(headerTitle, opened) {
232
+ if (opened && (headerTitle || this._oldHeaderTitle)) {
233
+ this.requestContentUpdate();
234
+ }
235
+ this._oldHeaderTitle = headerTitle;
236
+ this.toggleAttribute('has-title', !!headerTitle);
237
+ }
238
+
239
+ /** @private */
240
+ _headerTitleRenderer() {
241
+ if (this.headerTitle) {
242
+ if (!this.headerTitleElement) {
243
+ this.headerTitleElement = document.createElement('span');
244
+ this.headerTitleElement.id = this._titleId;
245
+ this.headerTitleElement.setAttribute('slot', 'title');
246
+ this.headerTitleElement.classList.add('draggable');
247
+
248
+ this.setAttribute('aria-labelledby', this._titleId);
249
+ }
250
+ this.appendChild(this.headerTitleElement);
251
+ this.headerTitleElement.textContent = this.headerTitle;
252
+ } else if (this.headerTitleElement) {
253
+ this.headerTitleElement.remove();
254
+ this.headerTitleElement = null;
255
+ this.removeAttribute('aria-labelledby');
256
+ }
257
+ }
258
+
259
+ requestContentUpdate() {
260
+ super.requestContentUpdate();
261
+
262
+ // If a new renderer has been set, make sure to reattach the header/footer roots
263
+ if (this.headerContainer && !this.headerContainer.parentElement) {
264
+ this.appendChild(this.headerContainer);
265
+ }
266
+
267
+ if (this.footerContainer && !this.footerContainer.parentElement) {
268
+ this.appendChild(this.footerContainer);
269
+ }
270
+
271
+ if (this.headerRenderer) {
272
+ this.headerRenderer.call(this.owner, this.headerContainer, this.owner);
273
+ }
274
+
275
+ if (this.footerRenderer) {
276
+ this.footerRenderer.call(this.owner, this.footerContainer, this.owner);
277
+ }
278
+
279
+ this._headerTitleRenderer();
280
+ }
281
+
68
282
  /**
69
283
  * Updates the coordinates of the overlay.
70
284
  * @param {!DialogOverlayBoundsParam} bounds
@@ -156,6 +370,23 @@ customElements.define(DialogOverlay.is, DialogOverlay);
156
370
  * See [`<vaadin-overlay>`](#/elements/vaadin-overlay) documentation.
157
371
  * for `<vaadin-dialog-overlay>` parts.
158
372
  *
373
+ * In addition to `<vaadin-overlay>` parts, the following parts are available for styling:
374
+ *
375
+ * Part name | Description
376
+ * -----------------|-------------------------------------------
377
+ * `header` | Element wrapping title and header content
378
+ * `header-content` | Element wrapping the header content slot
379
+ * `title` | Element wrapping the title slot
380
+ * `footer` | Element wrapping the footer slot
381
+ *
382
+ * The following state attributes are available for styling:
383
+ *
384
+ * Attribute | Description
385
+ * -----------------|--------------------------------------------
386
+ * `has-title` | Set when the element has a title
387
+ * `has-header` | Set when the element has header renderer
388
+ * `has-footer` | Set when the element has footer renderer
389
+ *
159
390
  * Note: the `theme` attribute value set on `<vaadin-dialog>` is
160
391
  * propagated to the internal `<vaadin-dialog-overlay>` component.
161
392
  *
@@ -175,16 +406,17 @@ class Dialog extends ThemePropertyMixin(ElementMixin(DialogDraggableMixin(Dialog
175
406
  return html`
176
407
  <style>
177
408
  :host {
178
- display: none;
409
+ display: none !important;
179
410
  }
180
411
  </style>
181
412
 
182
413
  <vaadin-dialog-overlay
183
414
  id="overlay"
415
+ header-title="[[headerTitle]]"
184
416
  on-opened-changed="_onOverlayOpened"
185
417
  on-mousedown="_bringOverlayToFront"
186
418
  on-touchstart="_bringOverlayToFront"
187
- theme$="[[theme]]"
419
+ theme$="[[_theme]]"
188
420
  modeless="[[modeless]]"
189
421
  with-backdrop="[[!modeless]]"
190
422
  resizable$="[[resizable]]"
@@ -249,6 +481,46 @@ class Dialog extends ThemePropertyMixin(ElementMixin(DialogDraggableMixin(Dialog
249
481
  */
250
482
  renderer: Function,
251
483
 
484
+ /**
485
+ * String used for rendering a dialog title.
486
+ *
487
+ * If both `headerTitle` and `headerRenderer` are defined, the title
488
+ * and the elements created by the renderer will be placed next to
489
+ * each other, with the title coming first.
490
+ *
491
+ * When `headerTitle` is set, the attribute `has-title` is added to the overlay element.
492
+ * @attr {string} header-title
493
+ */
494
+ headerTitle: String,
495
+
496
+ /**
497
+ * Custom function for rendering the dialog header.
498
+ * Receives two arguments:
499
+ *
500
+ * - `root` The root container DOM element. Append your content to it.
501
+ * - `dialog` The reference to the `<vaadin-dialog>` element.
502
+ *
503
+ * If both `headerTitle` and `headerRenderer` are defined, the title
504
+ * and the elements created by the renderer will be placed next to
505
+ * each other, with the title coming first.
506
+ *
507
+ * When `headerRenderer` is set, the attribute `has-header` is added to the overlay element.
508
+ * @type {DialogRenderer | undefined}
509
+ */
510
+ headerRenderer: Function,
511
+
512
+ /**
513
+ * Custom function for rendering the dialog footer.
514
+ * Receives two arguments:
515
+ *
516
+ * - `root` The root container DOM element. Append your content to it.
517
+ * - `dialog` The reference to the `<vaadin-dialog>` element.
518
+ *
519
+ * When `footerRenderer` is set, the attribute `has-footer` is added to the overlay element.
520
+ * @type {DialogRenderer | undefined}
521
+ */
522
+ footerRenderer: Function,
523
+
252
524
  /**
253
525
  * Set to true to remove backdrop and allow click events on background elements.
254
526
  * @type {boolean}
@@ -261,7 +533,11 @@ class Dialog extends ThemePropertyMixin(ElementMixin(DialogDraggableMixin(Dialog
261
533
  }
262
534
 
263
535
  static get observers() {
264
- return ['_openedChanged(opened)', '_ariaLabelChanged(ariaLabel)', '_rendererChanged(renderer)'];
536
+ return [
537
+ '_openedChanged(opened)',
538
+ '_ariaLabelChanged(ariaLabel)',
539
+ '_rendererChanged(renderer, headerRenderer, footerRenderer)'
540
+ ];
265
541
  }
266
542
 
267
543
  /** @protected */
@@ -276,7 +552,8 @@ class Dialog extends ThemePropertyMixin(ElementMixin(DialogDraggableMixin(Dialog
276
552
 
277
553
  /**
278
554
  * Requests an update for the content of the dialog.
279
- * While performing the update, it invokes the renderer passed in the `renderer` property.
555
+ * While performing the update, it invokes the renderer passed in the `renderer` property,
556
+ * as well as `headerRender` and `footerRenderer` properties, if these are defined.
280
557
  *
281
558
  * It is not guaranteed that the update happens immediately (synchronously) after it is requested.
282
559
  */
@@ -285,8 +562,8 @@ class Dialog extends ThemePropertyMixin(ElementMixin(DialogDraggableMixin(Dialog
285
562
  }
286
563
 
287
564
  /** @private */
288
- _rendererChanged(renderer) {
289
- this.$.overlay.setProperties({ owner: this, renderer });
565
+ _rendererChanged(renderer, headerRenderer, footerRenderer) {
566
+ this.$.overlay.setProperties({ owner: this, renderer, headerRenderer, footerRenderer });
290
567
  }
291
568
 
292
569
  /** @protected */
@@ -27,11 +27,35 @@ const dialogOverlay = css`
27
27
  padding: var(--lumo-space-l);
28
28
  }
29
29
 
30
+ [part='header'] {
31
+ gap: var(--lumo-space-s);
32
+ }
33
+
34
+ :host([has-header]) [part='header'],
35
+ :host([has-title]) [part='header'] {
36
+ padding: var(--lumo-space-m) var(--lumo-space-l);
37
+ border-bottom: 1px solid var(--lumo-contrast-10pct);
38
+ }
39
+
40
+ :host([has-footer]) [part='footer'] {
41
+ padding: var(--lumo-space-s) var(--lumo-space-m);
42
+ border-top: 1px solid var(--lumo-contrast-10pct);
43
+ }
44
+
30
45
  /* No padding */
31
46
  :host([theme~='no-padding']) [part='content'] {
32
47
  padding: 0;
33
48
  }
34
49
 
50
+ :host([theme~='no-padding'][has-header]) [part='header'],
51
+ :host([theme~='no-padding'][has-title]) [part='header'] {
52
+ padding: 0;
53
+ }
54
+
55
+ :host([theme~='no-padding'][has-footer]) [part='footer'] {
56
+ padding: 0;
57
+ }
58
+
35
59
  /* Animations */
36
60
 
37
61
  :host([opening]),
@@ -1,4 +1,5 @@
1
1
  import '@vaadin/vaadin-material-styles/shadow.js';
2
+ import '@vaadin/vaadin-material-styles/color.js';
2
3
  import { overlay } from '@vaadin/vaadin-material-styles/mixins/overlay.js';
3
4
  import { css, registerStyles } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
4
5
 
@@ -15,6 +16,19 @@ const dialogOverlay = css`
15
16
  padding: 24px;
16
17
  }
17
18
 
19
+ :host([has-header]) [part='header'],
20
+ :host([has-title]) [part='header'] {
21
+ padding: 9px 24px;
22
+ font-weight: 500;
23
+ gap: 8px;
24
+ border-bottom: 1px solid var(--material-divider-color);
25
+ }
26
+
27
+ :host([has-footer]) [part='footer'] {
28
+ padding: 8px;
29
+ border-top: 1px solid var(--material-divider-color);
30
+ }
31
+
18
32
  /* No padding */
19
33
  :host([theme~='no-padding']) [part='content'] {
20
34
  padding: 0;