@vaadin/dialog 23.0.6 → 23.1.0-alpha3

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.6",
3
+ "version": "23.1.0-alpha3",
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.6",
39
- "@vaadin/vaadin-lumo-styles": "^23.0.6",
40
- "@vaadin/vaadin-material-styles": "^23.0.6",
41
- "@vaadin/vaadin-overlay": "^23.0.6",
42
- "@vaadin/vaadin-themable-mixin": "^23.0.6"
38
+ "@vaadin/component-base": "23.1.0-alpha3",
39
+ "@vaadin/vaadin-lumo-styles": "23.1.0-alpha3",
40
+ "@vaadin/vaadin-material-styles": "23.1.0-alpha3",
41
+ "@vaadin/vaadin-overlay": "23.1.0-alpha3",
42
+ "@vaadin/vaadin-themable-mixin": "23.1.0-alpha3"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@esm-bundle/chai": "^4.3.4",
46
- "@vaadin/polymer-legacy-adapter": "^23.0.6",
46
+ "@vaadin/polymer-legacy-adapter": "23.1.0-alpha3",
47
47
  "@vaadin/testing-helpers": "^0.3.2",
48
- "@vaadin/text-area": "^23.0.6",
49
- "sinon": "^9.2.1"
48
+ "@vaadin/text-area": "23.1.0-alpha3",
49
+ "sinon": "^13.0.2"
50
50
  },
51
- "gitHead": "82ca8522e24a63343fb28bcb4c686e55d25c8858"
51
+ "gitHead": "8c9e64e8dfa158dd52a9bf6da351ff038c88ca85"
52
52
  }
@@ -36,6 +36,12 @@ registerStyles(
36
36
  display: none;
37
37
  }
38
38
 
39
+ :host([resizable]) [part='title'] {
40
+ cursor: move;
41
+ -webkit-user-select: none;
42
+ user-select: none;
43
+ }
44
+
39
45
  .resizer {
40
46
  position: absolute;
41
47
  height: 16px;
@@ -94,6 +94,24 @@ 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
+ * `overflow` | Set to `top`, `bottom`, none or both
114
+ *
97
115
  * Note: the `theme` attribute value set on `<vaadin-dialog>` is
98
116
  * propagated to the internal `<vaadin-dialog-overlay>` component.
99
117
  *
@@ -136,14 +154,52 @@ declare class Dialog extends ThemePropertyMixin(ElementMixin(DialogDraggableMixi
136
154
  */
137
155
  renderer: DialogRenderer | null | undefined;
138
156
 
157
+ /**
158
+ * String used for rendering a dialog title.
159
+ *
160
+ * If both `headerTitle` and `headerRenderer` are defined, the title
161
+ * and the elements created by the renderer will be placed next to
162
+ * each other, with the title coming first.
163
+ *
164
+ * When `headerTitle` is set, the attribute `has-title` is added to the overlay element.
165
+ * @attr {string} header-title
166
+ */
167
+ headerTitle: string | null | undefined;
168
+
169
+ /**
170
+ * Custom function for rendering the dialog header.
171
+ * Receives two arguments:
172
+ *
173
+ * - `root` The root container DOM element. Append your content to it.
174
+ * - `dialog` The reference to the `<vaadin-dialog>` element.
175
+ *
176
+ * If both `headerTitle` and `headerRenderer` are defined, the title
177
+ * and the elements created by the renderer will be placed next to
178
+ * each other, with the title coming first.
179
+ *
180
+ * When `headerRenderer` is set, the attribute `has-header` is added to the overlay element.
181
+ */
182
+ headerRenderer: DialogRenderer | null | undefined;
183
+
184
+ /**
185
+ * Custom function for rendering the dialog footer.
186
+ * Receives two arguments:
187
+ *
188
+ * - `root` The root container DOM element. Append your content to it.
189
+ * - `dialog` The reference to the `<vaadin-dialog>` element.
190
+ *
191
+ * When `footerRenderer` is set, the attribute `has-footer` is added to the overlay element.
192
+ */
193
+ footerRenderer: DialogRenderer | null | undefined;
194
+
139
195
  /**
140
196
  * Set to true to remove backdrop and allow click events on background elements.
141
197
  */
142
198
  modeless: boolean;
143
199
 
144
200
  /**
145
- * Requests an update for the content of the dialog.
146
- * While performing the update, it invokes the renderer passed in the `renderer` property.
201
+ * While performing the update, it invokes the renderer passed in the `renderer` property,
202
+ * as well as `headerRender` and `footerRenderer` properties, if these are defined.
147
203
  *
148
204
  * It is not guaranteed that the update happens immediately (synchronously) after it is requested.
149
205
  */
@@ -15,6 +15,62 @@ import { DialogResizableMixin } from './vaadin-dialog-resizable-mixin.js';
15
15
  registerStyles(
16
16
  'vaadin-dialog-overlay',
17
17
  css`
18
+ /* prefixing with the element tags to avoid styling confirm-dialog header part */
19
+ header[part='header'],
20
+ [part='header-content'],
21
+ footer[part='footer'] {
22
+ display: flex;
23
+ align-items: center;
24
+ flex-wrap: wrap;
25
+ flex: none;
26
+ pointer-events: none;
27
+ z-index: 1;
28
+ }
29
+
30
+ :host(:is([has-title], [has-header])) ::slotted([slot='header']),
31
+ ::slotted([slot='header-content']),
32
+ ::slotted([slot='title']),
33
+ :host([has-footer]) ::slotted([slot='footer']) {
34
+ display: contents;
35
+ pointer-events: auto;
36
+ }
37
+
38
+ [part='header-content'] {
39
+ flex: 1;
40
+ }
41
+
42
+ /* prefixing with the element tag to avoid styling confirm-dialog footer part */
43
+ :host([has-title]) [part='header-content'],
44
+ footer[part='footer'] {
45
+ justify-content: flex-end;
46
+ }
47
+
48
+ :host(:not([has-title]):not([has-header])) header[part='header'],
49
+ :host(:not([has-title])) [part='title'] {
50
+ display: none;
51
+ }
52
+
53
+ :host(:not([has-footer])) footer[part='footer'] {
54
+ display: none;
55
+ }
56
+
57
+ :host(:is([has-title], [has-header], [has-footer])) [part='content'] {
58
+ height: auto;
59
+ }
60
+
61
+ @media (min-height: 320px) {
62
+ :host(:is([has-title], [has-header], [has-footer])) .resizer-container {
63
+ overflow: hidden;
64
+ display: flex;
65
+ flex-direction: column;
66
+ }
67
+
68
+ :host(:is([has-title], [has-header], [has-footer])) [part='content'] {
69
+ flex: 1;
70
+ overflow: auto;
71
+ }
72
+ }
73
+
18
74
  /*
19
75
  NOTE(platosha): Make some min-width to prevent collapsing of the content
20
76
  taking the parent width, e. g., <vaadin-grid> and such.
@@ -53,18 +109,213 @@ export class DialogOverlay extends OverlayElement {
53
109
  resizerContainer.classList.add('resizer-container');
54
110
  resizerContainer.appendChild(contentPart);
55
111
  overlayPart.appendChild(resizerContainer);
112
+
113
+ const headerContainer = document.createElement('header');
114
+ headerContainer.setAttribute('part', 'header');
115
+ resizerContainer.insertBefore(headerContainer, contentPart);
116
+
117
+ const titleContainer = document.createElement('div');
118
+ titleContainer.setAttribute('part', 'title');
119
+ headerContainer.appendChild(titleContainer);
120
+
121
+ const titleSlot = document.createElement('slot');
122
+ titleSlot.setAttribute('name', 'title');
123
+ titleContainer.appendChild(titleSlot);
124
+
125
+ const headerContentContainer = document.createElement('div');
126
+ headerContentContainer.setAttribute('part', 'header-content');
127
+ headerContainer.appendChild(headerContentContainer);
128
+
129
+ const headerSlot = document.createElement('slot');
130
+ headerSlot.setAttribute('name', 'header-content');
131
+ headerContentContainer.appendChild(headerSlot);
132
+
133
+ const footerContainer = document.createElement('footer');
134
+ footerContainer.setAttribute('part', 'footer');
135
+ resizerContainer.appendChild(footerContainer);
136
+
137
+ const footerSlot = document.createElement('slot');
138
+ footerSlot.setAttribute('name', 'footer');
139
+ footerContainer.appendChild(footerSlot);
56
140
  }
57
141
  return memoizedTemplate;
58
142
  }
59
143
 
144
+ static get observers() {
145
+ return [
146
+ '_headerFooterRendererChange(headerRenderer, footerRenderer, opened)',
147
+ '_headerTitleChanged(headerTitle, opened)'
148
+ ];
149
+ }
150
+
60
151
  static get properties() {
61
152
  return {
62
153
  modeless: Boolean,
63
154
 
64
- withBackdrop: Boolean
155
+ withBackdrop: Boolean,
156
+
157
+ headerTitle: String,
158
+
159
+ headerRenderer: Function,
160
+
161
+ footerRenderer: Function
65
162
  };
66
163
  }
67
164
 
165
+ /** @protected */
166
+ ready() {
167
+ super.ready();
168
+
169
+ const uniqueId = (DialogOverlay._uniqueId = 1 + DialogOverlay._uniqueId || 0);
170
+ this._titleId = `${this.constructor.is}-title-${uniqueId}`;
171
+
172
+ // Update overflow attribute on resize
173
+ this.__resizeObserver = new ResizeObserver(() => {
174
+ this.__updateOverflow();
175
+ });
176
+ this.__resizeObserver.observe(this.$.resizerContainer);
177
+
178
+ // Update overflow attribute on scroll
179
+ this.$.content.addEventListener('scroll', () => {
180
+ this.__updateOverflow();
181
+ });
182
+ }
183
+
184
+ /** @private */
185
+ __createContainer(slot) {
186
+ const container = document.createElement('div');
187
+ container.setAttribute('slot', slot);
188
+ return container;
189
+ }
190
+
191
+ /** @private */
192
+ __clearContainer(container) {
193
+ container.innerHTML = '';
194
+ // Whenever a Lit-based renderer is used, it assigns a Lit part to the node it was rendered into.
195
+ // When clearing the rendered content, this part needs to be manually disposed of.
196
+ // Otherwise, using a Lit-based renderer on the same node will throw an exception or render nothing afterward.
197
+ delete container._$litPart$;
198
+ }
199
+
200
+ /** @private */
201
+ __initContainer(container, slot) {
202
+ if (container) {
203
+ // Reset existing container in case if a new renderer is set.
204
+ this.__clearContainer(container);
205
+ } else {
206
+ // Create the container, but wait to append it until requestContentUpdate is called.
207
+ container = this.__createContainer(slot);
208
+ }
209
+ return container;
210
+ }
211
+
212
+ /** @private */
213
+ _headerFooterRendererChange(headerRenderer, footerRenderer, opened) {
214
+ const headerRendererChanged = this.__oldHeaderRenderer !== headerRenderer;
215
+ this.__oldHeaderRenderer = headerRenderer;
216
+
217
+ const footerRendererChanged = this.__oldFooterRenderer !== footerRenderer;
218
+ this.__oldFooterRenderer = footerRenderer;
219
+
220
+ const openedChanged = this._oldOpenedFooterHeader !== opened;
221
+ this._oldOpenedFooterHeader = opened;
222
+
223
+ // Set attributes here to update styles before detecting content overflow
224
+ this.toggleAttribute('has-header', !!headerRenderer);
225
+ this.toggleAttribute('has-footer', !!footerRenderer);
226
+
227
+ if (headerRendererChanged) {
228
+ if (headerRenderer) {
229
+ this.headerContainer = this.__initContainer(this.headerContainer, 'header-content');
230
+ } else if (this.headerContainer) {
231
+ this.headerContainer.remove();
232
+ this.headerContainer = null;
233
+ this.__updateOverflow();
234
+ }
235
+ }
236
+
237
+ if (footerRendererChanged) {
238
+ if (footerRenderer) {
239
+ this.footerContainer = this.__initContainer(this.footerContainer, 'footer');
240
+ } else if (this.footerContainer) {
241
+ this.footerContainer.remove();
242
+ this.footerContainer = null;
243
+ this.__updateOverflow();
244
+ }
245
+ }
246
+
247
+ if (
248
+ (headerRenderer && (headerRendererChanged || openedChanged)) ||
249
+ (footerRenderer && (footerRendererChanged || openedChanged))
250
+ ) {
251
+ if (opened) {
252
+ this.requestContentUpdate();
253
+ }
254
+ }
255
+ }
256
+
257
+ /** @private */
258
+ _headerTitleChanged(headerTitle, opened) {
259
+ this.toggleAttribute('has-title', !!headerTitle);
260
+
261
+ if (opened && (headerTitle || this._oldHeaderTitle)) {
262
+ this.requestContentUpdate();
263
+ }
264
+ this._oldHeaderTitle = headerTitle;
265
+ }
266
+
267
+ /** @private */
268
+ _headerTitleRenderer() {
269
+ if (this.headerTitle) {
270
+ if (!this.headerTitleElement) {
271
+ this.headerTitleElement = document.createElement('span');
272
+ this.headerTitleElement.id = this._titleId;
273
+ this.headerTitleElement.setAttribute('slot', 'title');
274
+ this.headerTitleElement.classList.add('draggable');
275
+
276
+ this.setAttribute('aria-labelledby', this._titleId);
277
+ }
278
+ this.appendChild(this.headerTitleElement);
279
+ this.headerTitleElement.textContent = this.headerTitle;
280
+ } else if (this.headerTitleElement) {
281
+ this.headerTitleElement.remove();
282
+ this.headerTitleElement = null;
283
+ this.removeAttribute('aria-labelledby');
284
+ }
285
+ }
286
+
287
+ requestContentUpdate() {
288
+ super.requestContentUpdate();
289
+
290
+ if (this.headerContainer) {
291
+ // If a new renderer has been set, make sure to reattach the container
292
+ if (!this.headerContainer.parentElement) {
293
+ this.appendChild(this.headerContainer);
294
+ }
295
+
296
+ if (this.headerRenderer) {
297
+ // Only call header renderer after the container has been initialized
298
+ this.headerRenderer.call(this.owner, this.headerContainer, this.owner);
299
+ }
300
+ }
301
+
302
+ if (this.footerContainer) {
303
+ // If a new renderer has been set, make sure to reattach the container
304
+ if (!this.footerContainer.parentElement) {
305
+ this.appendChild(this.footerContainer);
306
+ }
307
+
308
+ if (this.footerRenderer) {
309
+ // Only call header renderer after the container has been initialized
310
+ this.footerRenderer.call(this.owner, this.footerContainer, this.owner);
311
+ }
312
+ }
313
+
314
+ this._headerTitleRenderer();
315
+
316
+ this.__updateOverflow();
317
+ }
318
+
68
319
  /**
69
320
  * Updates the coordinates of the overlay.
70
321
  * @param {!DialogOverlayBoundsParam} bounds
@@ -117,6 +368,32 @@ export class DialogOverlay extends OverlayElement {
117
368
  this.$.resizerContainer.scrollTop = scrollPosition;
118
369
  });
119
370
  }
371
+
372
+ /** @private */
373
+ __updateOverflow() {
374
+ let overflow = '';
375
+
376
+ // Only set "overflow" attribute if the dialog has a header, title or footer.
377
+ // Check for state attributes as extending components might not use renderers.
378
+ if (this.hasAttribute('has-header') || this.hasAttribute('has-footer') || this.headerTitle) {
379
+ const content = this.$.content;
380
+
381
+ if (content.scrollTop > 0) {
382
+ overflow += ' top';
383
+ }
384
+
385
+ if (content.scrollTop < content.scrollHeight - content.clientHeight) {
386
+ overflow += ' bottom';
387
+ }
388
+ }
389
+
390
+ const value = overflow.trim();
391
+ if (value.length > 0 && this.getAttribute('overflow') !== value) {
392
+ this.setAttribute('overflow', value);
393
+ } else if (value.length === 0 && this.hasAttribute('overflow')) {
394
+ this.removeAttribute('overflow');
395
+ }
396
+ }
120
397
  }
121
398
 
122
399
  customElements.define(DialogOverlay.is, DialogOverlay);
@@ -156,6 +433,24 @@ customElements.define(DialogOverlay.is, DialogOverlay);
156
433
  * See [`<vaadin-overlay>`](#/elements/vaadin-overlay) documentation.
157
434
  * for `<vaadin-dialog-overlay>` parts.
158
435
  *
436
+ * In addition to `<vaadin-overlay>` parts, the following parts are available for styling:
437
+ *
438
+ * Part name | Description
439
+ * -----------------|-------------------------------------------
440
+ * `header` | Element wrapping title and header content
441
+ * `header-content` | Element wrapping the header content slot
442
+ * `title` | Element wrapping the title slot
443
+ * `footer` | Element wrapping the footer slot
444
+ *
445
+ * The following state attributes are available for styling:
446
+ *
447
+ * Attribute | Description
448
+ * -----------------|--------------------------------------------
449
+ * `has-title` | Set when the element has a title
450
+ * `has-header` | Set when the element has header renderer
451
+ * `has-footer` | Set when the element has footer renderer
452
+ * `overflow` | Set to `top`, `bottom`, none or both
453
+ *
159
454
  * Note: the `theme` attribute value set on `<vaadin-dialog>` is
160
455
  * propagated to the internal `<vaadin-dialog-overlay>` component.
161
456
  *
@@ -175,16 +470,17 @@ class Dialog extends ThemePropertyMixin(ElementMixin(DialogDraggableMixin(Dialog
175
470
  return html`
176
471
  <style>
177
472
  :host {
178
- display: none;
473
+ display: none !important;
179
474
  }
180
475
  </style>
181
476
 
182
477
  <vaadin-dialog-overlay
183
478
  id="overlay"
479
+ header-title="[[headerTitle]]"
184
480
  on-opened-changed="_onOverlayOpened"
185
481
  on-mousedown="_bringOverlayToFront"
186
482
  on-touchstart="_bringOverlayToFront"
187
- theme$="[[theme]]"
483
+ theme$="[[_theme]]"
188
484
  modeless="[[modeless]]"
189
485
  with-backdrop="[[!modeless]]"
190
486
  resizable$="[[resizable]]"
@@ -249,6 +545,46 @@ class Dialog extends ThemePropertyMixin(ElementMixin(DialogDraggableMixin(Dialog
249
545
  */
250
546
  renderer: Function,
251
547
 
548
+ /**
549
+ * String used for rendering a dialog title.
550
+ *
551
+ * If both `headerTitle` and `headerRenderer` are defined, the title
552
+ * and the elements created by the renderer will be placed next to
553
+ * each other, with the title coming first.
554
+ *
555
+ * When `headerTitle` is set, the attribute `has-title` is added to the overlay element.
556
+ * @attr {string} header-title
557
+ */
558
+ headerTitle: String,
559
+
560
+ /**
561
+ * Custom function for rendering the dialog header.
562
+ * Receives two arguments:
563
+ *
564
+ * - `root` The root container DOM element. Append your content to it.
565
+ * - `dialog` The reference to the `<vaadin-dialog>` element.
566
+ *
567
+ * If both `headerTitle` and `headerRenderer` are defined, the title
568
+ * and the elements created by the renderer will be placed next to
569
+ * each other, with the title coming first.
570
+ *
571
+ * When `headerRenderer` is set, the attribute `has-header` is added to the overlay element.
572
+ * @type {DialogRenderer | undefined}
573
+ */
574
+ headerRenderer: Function,
575
+
576
+ /**
577
+ * Custom function for rendering the dialog footer.
578
+ * Receives two arguments:
579
+ *
580
+ * - `root` The root container DOM element. Append your content to it.
581
+ * - `dialog` The reference to the `<vaadin-dialog>` element.
582
+ *
583
+ * When `footerRenderer` is set, the attribute `has-footer` is added to the overlay element.
584
+ * @type {DialogRenderer | undefined}
585
+ */
586
+ footerRenderer: Function,
587
+
252
588
  /**
253
589
  * Set to true to remove backdrop and allow click events on background elements.
254
590
  * @type {boolean}
@@ -261,7 +597,11 @@ class Dialog extends ThemePropertyMixin(ElementMixin(DialogDraggableMixin(Dialog
261
597
  }
262
598
 
263
599
  static get observers() {
264
- return ['_openedChanged(opened)', '_ariaLabelChanged(ariaLabel)', '_rendererChanged(renderer)'];
600
+ return [
601
+ '_openedChanged(opened)',
602
+ '_ariaLabelChanged(ariaLabel)',
603
+ '_rendererChanged(renderer, headerRenderer, footerRenderer)'
604
+ ];
265
605
  }
266
606
 
267
607
  /** @protected */
@@ -276,7 +616,8 @@ class Dialog extends ThemePropertyMixin(ElementMixin(DialogDraggableMixin(Dialog
276
616
 
277
617
  /**
278
618
  * Requests an update for the content of the dialog.
279
- * While performing the update, it invokes the renderer passed in the `renderer` property.
619
+ * While performing the update, it invokes the renderer passed in the `renderer` property,
620
+ * as well as `headerRender` and `footerRenderer` properties, if these are defined.
280
621
  *
281
622
  * It is not guaranteed that the update happens immediately (synchronously) after it is requested.
282
623
  */
@@ -285,8 +626,8 @@ class Dialog extends ThemePropertyMixin(ElementMixin(DialogDraggableMixin(Dialog
285
626
  }
286
627
 
287
628
  /** @private */
288
- _rendererChanged(renderer) {
289
- this.$.overlay.setProperties({ owner: this, renderer });
629
+ _rendererChanged(renderer, headerRenderer, footerRenderer) {
630
+ this.$.overlay.setProperties({ owner: this, renderer, headerRenderer, footerRenderer });
290
631
  }
291
632
 
292
633
  /** @protected */
@@ -27,11 +27,47 @@ const dialogOverlay = css`
27
27
  padding: var(--lumo-space-l);
28
28
  }
29
29
 
30
+ :host(:is([has-header], [has-title])) [part='header'] + [part='content'] {
31
+ padding-top: 0;
32
+ }
33
+
34
+ :host(:is([has-header], [has-title])) [part='header'],
35
+ :host([has-header]) [part='header-content'],
36
+ :host([has-footer]) [part='footer'] {
37
+ gap: var(--lumo-space-xs) var(--lumo-space-s);
38
+ line-height: var(--lumo-line-height-s);
39
+ }
40
+
41
+ :host(:is([has-header], [has-title])) [part='header'] {
42
+ padding: var(--lumo-space-m);
43
+ background-color: var(--lumo-base-color);
44
+ border-radius: var(--lumo-border-radius-l) var(--lumo-border-radius-l) 0 0; /* Needed for Safari */
45
+ }
46
+
47
+ :host([has-footer]) [part='footer'] {
48
+ padding: var(--lumo-space-s) var(--lumo-space-m);
49
+ background-color: var(--lumo-contrast-5pct);
50
+ border-radius: 0 0 var(--lumo-border-radius-l) var(--lumo-border-radius-l); /* Needed for Safari */
51
+ }
52
+
53
+ [part='title'] {
54
+ font-size: var(--lumo-font-size-xl);
55
+ font-weight: 600;
56
+ color: var(--lumo-header-text-color);
57
+ margin-inline-start: calc(var(--lumo-space-l) - var(--lumo-space-m));
58
+ }
59
+
30
60
  /* No padding */
31
61
  :host([theme~='no-padding']) [part='content'] {
32
62
  padding: 0;
33
63
  }
34
64
 
65
+ @media (min-height: 320px) {
66
+ :host(:is([has-header], [has-title])[overflow~='top']) [part='header'] {
67
+ box-shadow: 0 1px 0 0 var(--lumo-contrast-10pct);
68
+ }
69
+ }
70
+
35
71
  /* Animations */
36
72
 
37
73
  :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,41 @@ const dialogOverlay = css`
15
16
  padding: 24px;
16
17
  }
17
18
 
19
+ :host(:is([has-header], [has-title])) [part='header'] {
20
+ padding: 16px;
21
+ }
22
+
23
+ :host(:is([has-header], [has-title])) [part='header'] + [part='content'] {
24
+ padding-top: 0;
25
+ }
26
+
27
+ :host(:is([has-header], [has-title])) [part='header'],
28
+ :host([has-header]) [part='header-content'],
29
+ :host([has-footer]) [part='footer'] {
30
+ gap: 8px;
31
+ line-height: 1.2;
32
+ }
33
+
34
+ [part='title'] {
35
+ font-size: var(--material-h5-font-size);
36
+ font-weight: 500;
37
+ margin-inline-start: 8px;
38
+ }
39
+
40
+ :host([has-footer]) [part='footer'] {
41
+ padding: 8px;
42
+ }
43
+
44
+ @media (min-height: 320px) {
45
+ :host(:is([has-header], [has-title])[overflow~='top']) [part='header'] {
46
+ box-shadow: 0 1px 0 0 var(--material-divider-color);
47
+ }
48
+
49
+ :host([has-footer][overflow~='bottom']) [part='footer'] {
50
+ box-shadow: 0 -1px 0 0 var(--material-divider-color);
51
+ }
52
+ }
53
+
18
54
  /* No padding */
19
55
  :host([theme~='no-padding']) [part='content'] {
20
56
  padding: 0;