@vaadin/dialog 24.2.0-alpha4 → 24.2.0-dev.538d07bdf

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.
@@ -3,379 +3,45 @@
3
3
  * Copyright (c) 2017 - 2023 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
- import { Overlay } from '@vaadin/overlay/src/vaadin-overlay.js';
7
- import { css, registerStyles } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
6
+ import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
7
+ import { DirMixin } from '@vaadin/component-base/src/dir-mixin.js';
8
+ import { overlayStyles } from '@vaadin/overlay/src/vaadin-overlay-styles.js';
9
+ import { registerStyles, ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
10
+ import { DialogOverlayMixin } from './vaadin-dialog-overlay-mixin.js';
11
+ import { dialogOverlay, resizableOverlay } from './vaadin-dialog-styles.js';
8
12
 
9
- registerStyles(
10
- 'vaadin-dialog-overlay',
11
- css`
12
- [part='header'],
13
- [part='header-content'],
14
- [part='footer'] {
15
- display: flex;
16
- align-items: center;
17
- flex-wrap: wrap;
18
- flex: none;
19
- pointer-events: none;
20
- z-index: 1;
21
- }
22
-
23
- [part='header'] {
24
- flex-wrap: nowrap;
25
- }
26
-
27
- ::slotted([slot='header-content']),
28
- ::slotted([slot='title']),
29
- ::slotted([slot='footer']) {
30
- display: contents;
31
- pointer-events: auto;
32
- }
33
-
34
- ::slotted([slot='title']) {
35
- font: inherit !important;
36
- overflow-wrap: anywhere;
37
- }
38
-
39
- [part='header-content'] {
40
- flex: 1;
41
- }
42
-
43
- :host([has-title]) [part='header-content'],
44
- [part='footer'] {
45
- justify-content: flex-end;
46
- }
47
-
48
- :host(:not([has-title]):not([has-header])) [part='header'],
49
- :host(:not([has-header])) [part='header-content'],
50
- :host(:not([has-title])) [part='title'],
51
- :host(:not([has-footer])) [part='footer'] {
52
- display: none !important;
53
- }
54
-
55
- :host(:is([has-title], [has-header], [has-footer])) [part='content'] {
56
- height: auto;
57
- }
58
-
59
- @media (min-height: 320px) {
60
- :host(:is([has-title], [has-header], [has-footer])) .resizer-container {
61
- overflow: hidden;
62
- display: flex;
63
- flex-direction: column;
64
- }
65
-
66
- :host(:is([has-title], [has-header], [has-footer])) [part='content'] {
67
- flex: 1;
68
- overflow: auto;
69
- }
70
- }
71
-
72
- /*
73
- NOTE(platosha): Make some min-width to prevent collapsing of the content
74
- taking the parent width, e. g., <vaadin-grid> and such.
75
- */
76
- [part='content'] {
77
- min-width: 12em; /* matches the default <vaadin-text-field> width */
78
- }
79
-
80
- :host([has-bounds-set]) [part='overlay'] {
81
- max-width: none;
82
- }
83
-
84
- @media (forced-colors: active) {
85
- [part='overlay'] {
86
- outline: 3px solid !important;
87
- }
88
- }
89
- `,
90
- { moduleId: 'vaadin-dialog-overlay-styles' },
91
- );
92
-
93
- let memoizedTemplate;
13
+ registerStyles('vaadin-dialog-overlay', [overlayStyles, dialogOverlay, resizableOverlay], {
14
+ moduleId: 'vaadin-dialog-overlay-styles',
15
+ });
94
16
 
95
17
  /**
96
18
  * An element used internally by `<vaadin-dialog>`. Not intended to be used separately.
97
19
  *
98
- * @extends Overlay
20
+ * @extends HTMLElement
21
+ * @mixes DialogOverlayMixin
22
+ * @mixes DirMixin
23
+ * @mixes ThemableMixin
99
24
  * @private
100
25
  */
101
- export class DialogOverlay extends Overlay {
26
+ export class DialogOverlay extends DialogOverlayMixin(DirMixin(ThemableMixin(PolymerElement))) {
102
27
  static get is() {
103
28
  return 'vaadin-dialog-overlay';
104
29
  }
105
30
 
106
31
  static get template() {
107
- if (!memoizedTemplate) {
108
- memoizedTemplate = super.template.cloneNode(true);
109
- const contentPart = memoizedTemplate.content.querySelector('[part="content"]');
110
- const overlayPart = memoizedTemplate.content.querySelector('[part="overlay"]');
111
- const resizerContainer = document.createElement('section');
112
- resizerContainer.id = 'resizerContainer';
113
- resizerContainer.classList.add('resizer-container');
114
- resizerContainer.appendChild(contentPart);
115
- overlayPart.appendChild(resizerContainer);
116
-
117
- const headerContainer = document.createElement('header');
118
- headerContainer.setAttribute('part', 'header');
119
- resizerContainer.insertBefore(headerContainer, contentPart);
120
-
121
- const titleContainer = document.createElement('div');
122
- titleContainer.setAttribute('part', 'title');
123
- headerContainer.appendChild(titleContainer);
124
-
125
- const titleSlot = document.createElement('slot');
126
- titleSlot.setAttribute('name', 'title');
127
- titleContainer.appendChild(titleSlot);
128
-
129
- const headerContentContainer = document.createElement('div');
130
- headerContentContainer.setAttribute('part', 'header-content');
131
- headerContainer.appendChild(headerContentContainer);
132
-
133
- const headerSlot = document.createElement('slot');
134
- headerSlot.setAttribute('name', 'header-content');
135
- headerContentContainer.appendChild(headerSlot);
136
-
137
- const footerContainer = document.createElement('footer');
138
- footerContainer.setAttribute('part', 'footer');
139
- resizerContainer.appendChild(footerContainer);
140
-
141
- const footerSlot = document.createElement('slot');
142
- footerSlot.setAttribute('name', 'footer');
143
- footerContainer.appendChild(footerSlot);
144
- }
145
- return memoizedTemplate;
146
- }
147
-
148
- static get observers() {
149
- return [
150
- '_headerFooterRendererChange(headerRenderer, footerRenderer, opened)',
151
- '_headerTitleChanged(headerTitle, opened)',
152
- ];
153
- }
154
-
155
- static get properties() {
156
- return {
157
- modeless: Boolean,
158
-
159
- withBackdrop: Boolean,
160
-
161
- headerTitle: String,
162
-
163
- headerRenderer: Function,
164
-
165
- footerRenderer: Function,
166
- };
167
- }
168
-
169
- /** @protected */
170
- ready() {
171
- super.ready();
172
-
173
- // Update overflow attribute on resize
174
- this.__resizeObserver = new ResizeObserver(() => {
175
- this.__updateOverflow();
176
- });
177
- this.__resizeObserver.observe(this.$.resizerContainer);
178
-
179
- // Update overflow attribute on scroll
180
- this.$.content.addEventListener('scroll', () => {
181
- this.__updateOverflow();
182
- });
183
- }
184
-
185
- /** @private */
186
- __createContainer(slot) {
187
- const container = document.createElement('div');
188
- container.setAttribute('slot', slot);
189
- return container;
190
- }
191
-
192
- /** @private */
193
- __clearContainer(container) {
194
- container.innerHTML = '';
195
- // Whenever a Lit-based renderer is used, it assigns a Lit part to the node it was rendered into.
196
- // When clearing the rendered content, this part needs to be manually disposed of.
197
- // Otherwise, using a Lit-based renderer on the same node will throw an exception or render nothing afterward.
198
- delete container._$litPart$;
199
- }
200
-
201
- /** @private */
202
- __initContainer(container, slot) {
203
- if (container) {
204
- // Reset existing container in case if a new renderer is set.
205
- this.__clearContainer(container);
206
- } else {
207
- // Create the container, but wait to append it until requestContentUpdate is called.
208
- container = this.__createContainer(slot);
209
- }
210
- return container;
211
- }
212
-
213
- /** @private */
214
- _headerFooterRendererChange(headerRenderer, footerRenderer, opened) {
215
- const headerRendererChanged = this.__oldHeaderRenderer !== headerRenderer;
216
- this.__oldHeaderRenderer = headerRenderer;
217
-
218
- const footerRendererChanged = this.__oldFooterRenderer !== footerRenderer;
219
- this.__oldFooterRenderer = footerRenderer;
220
-
221
- const openedChanged = this._oldOpenedFooterHeader !== opened;
222
- this._oldOpenedFooterHeader = opened;
223
-
224
- // Set attributes here to update styles before detecting content overflow
225
- this.toggleAttribute('has-header', !!headerRenderer);
226
- this.toggleAttribute('has-footer', !!footerRenderer);
227
-
228
- if (headerRendererChanged) {
229
- if (headerRenderer) {
230
- this.headerContainer = this.__initContainer(this.headerContainer, 'header-content');
231
- } else if (this.headerContainer) {
232
- this.headerContainer.remove();
233
- this.headerContainer = null;
234
- this.__updateOverflow();
235
- }
236
- }
237
-
238
- if (footerRendererChanged) {
239
- if (footerRenderer) {
240
- this.footerContainer = this.__initContainer(this.footerContainer, 'footer');
241
- } else if (this.footerContainer) {
242
- this.footerContainer.remove();
243
- this.footerContainer = null;
244
- this.__updateOverflow();
245
- }
246
- }
247
-
248
- if (
249
- (headerRenderer && (headerRendererChanged || openedChanged)) ||
250
- (footerRenderer && (footerRendererChanged || openedChanged))
251
- ) {
252
- if (opened) {
253
- this.requestContentUpdate();
254
- }
255
- }
256
- }
257
-
258
- /** @private */
259
- _headerTitleChanged(headerTitle, opened) {
260
- this.toggleAttribute('has-title', !!headerTitle);
261
-
262
- if (opened && (headerTitle || this._oldHeaderTitle)) {
263
- this.requestContentUpdate();
264
- }
265
- this._oldHeaderTitle = headerTitle;
266
- }
267
-
268
- /** @private */
269
- _headerTitleRenderer() {
270
- if (this.headerTitle) {
271
- if (!this.headerTitleElement) {
272
- this.headerTitleElement = document.createElement('h2');
273
- this.headerTitleElement.setAttribute('slot', 'title');
274
- this.headerTitleElement.classList.add('draggable');
275
- }
276
- this.appendChild(this.headerTitleElement);
277
- this.headerTitleElement.textContent = this.headerTitle;
278
- } else if (this.headerTitleElement) {
279
- this.headerTitleElement.remove();
280
- this.headerTitleElement = null;
281
- }
282
- }
283
-
284
- /**
285
- * @override
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
-
319
- /**
320
- * Updates the coordinates of the overlay.
321
- * @param {!DialogOverlayBoundsParam} bounds
322
- */
323
- setBounds(bounds) {
324
- const overlay = this.$.overlay;
325
- const parsedBounds = { ...bounds };
326
-
327
- if (overlay.style.position !== 'absolute') {
328
- overlay.style.position = 'absolute';
329
- this.setAttribute('has-bounds-set', '');
330
- }
331
-
332
- Object.keys(parsedBounds).forEach((arg) => {
333
- if (typeof parsedBounds[arg] === 'number') {
334
- parsedBounds[arg] = `${parsedBounds[arg]}px`;
335
- }
336
- });
337
-
338
- Object.assign(overlay.style, parsedBounds);
339
- }
340
-
341
- /**
342
- * Retrieves the coordinates of the overlay.
343
- * @return {!DialogOverlayBounds}
344
- */
345
- getBounds() {
346
- const overlayBounds = this.$.overlay.getBoundingClientRect();
347
- const containerBounds = this.getBoundingClientRect();
348
- const top = overlayBounds.top - containerBounds.top;
349
- const left = overlayBounds.left - containerBounds.left;
350
- const width = overlayBounds.width;
351
- const height = overlayBounds.height;
352
- return { top, left, width, height };
353
- }
354
-
355
- /** @private */
356
- __updateOverflow() {
357
- let overflow = '';
358
-
359
- // Only set "overflow" attribute if the dialog has a header, title or footer.
360
- // Check for state attributes as extending components might not use renderers.
361
- if (this.hasAttribute('has-header') || this.hasAttribute('has-footer') || this.headerTitle) {
362
- const content = this.$.content;
363
-
364
- if (content.scrollTop > 0) {
365
- overflow += ' top';
366
- }
367
-
368
- if (content.scrollTop < content.scrollHeight - content.clientHeight) {
369
- overflow += ' bottom';
370
- }
371
- }
372
-
373
- const value = overflow.trim();
374
- if (value.length > 0 && this.getAttribute('overflow') !== value) {
375
- this.setAttribute('overflow', value);
376
- } else if (value.length === 0 && this.hasAttribute('overflow')) {
377
- this.removeAttribute('overflow');
378
- }
32
+ return html`
33
+ <div id="backdrop" part="backdrop" hidden$="[[!withBackdrop]]"></div>
34
+ <div part="overlay" id="overlay" tabindex="0">
35
+ <section id="resizerContainer" class="resizer-container">
36
+ <header part="header">
37
+ <div part="title"><slot name="title"></slot></div>
38
+ <div part="header-content"><slot name="header-content"></slot></div>
39
+ </header>
40
+ <div part="content" id="content"><slot></slot></div>
41
+ <footer part="footer"><slot name="footer"></slot></footer>
42
+ </section>
43
+ </div>
44
+ `;
379
45
  }
380
46
  }
381
47
 
@@ -0,0 +1,68 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2017 - 2023 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+ import type { Constructor } from '@open-wc/dedupe-mixin';
7
+ import type { DialogRenderer } from './vaadin-dialog.js';
8
+
9
+ export declare function DialogRendererMixin<T extends Constructor<HTMLElement>>(
10
+ base: T,
11
+ ): Constructor<DialogRendererMixinClass> & T;
12
+
13
+ export declare class DialogRendererMixinClass {
14
+ /**
15
+ * Custom function for rendering the content of the dialog.
16
+ * Receives two arguments:
17
+ *
18
+ * - `root` The root container DOM element. Append your content to it.
19
+ * - `dialog` The reference to the `<vaadin-dialog>` element.
20
+ */
21
+ renderer: DialogRenderer | null | undefined;
22
+
23
+ /**
24
+ * String used for rendering a dialog title.
25
+ *
26
+ * If both `headerTitle` and `headerRenderer` are defined, the title
27
+ * and the elements created by the renderer will be placed next to
28
+ * each other, with the title coming first.
29
+ *
30
+ * When `headerTitle` is set, the attribute `has-title` is added to the overlay element.
31
+ * @attr {string} header-title
32
+ */
33
+ headerTitle: string | null | undefined;
34
+
35
+ /**
36
+ * Custom function for rendering the dialog header.
37
+ * Receives two arguments:
38
+ *
39
+ * - `root` The root container DOM element. Append your content to it.
40
+ * - `dialog` The reference to the `<vaadin-dialog>` element.
41
+ *
42
+ * If both `headerTitle` and `headerRenderer` are defined, the title
43
+ * and the elements created by the renderer will be placed next to
44
+ * each other, with the title coming first.
45
+ *
46
+ * When `headerRenderer` is set, the attribute `has-header` is added to the overlay element.
47
+ */
48
+ headerRenderer: DialogRenderer | null | undefined;
49
+
50
+ /**
51
+ * Custom function for rendering the dialog footer.
52
+ * Receives two arguments:
53
+ *
54
+ * - `root` The root container DOM element. Append your content to it.
55
+ * - `dialog` The reference to the `<vaadin-dialog>` element.
56
+ *
57
+ * When `footerRenderer` is set, the attribute `has-footer` is added to the overlay element.
58
+ */
59
+ footerRenderer: DialogRenderer | null | undefined;
60
+
61
+ /**
62
+ * While performing the update, it invokes the renderer passed in the `renderer` property,
63
+ * as well as `headerRender` and `footerRenderer` properties, if these are defined.
64
+ *
65
+ * It is not guaranteed that the update happens immediately (synchronously) after it is requested.
66
+ */
67
+ requestContentUpdate(): void;
68
+ }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2017 - 2023 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+
7
+ /**
8
+ * @polymerMixin
9
+ */
10
+ export const DialogRendererMixin = (superClass) =>
11
+ class DialogRendererMixin extends superClass {
12
+ static get properties() {
13
+ return {
14
+ /**
15
+ * Custom function for rendering the content of the dialog.
16
+ * Receives two arguments:
17
+ *
18
+ * - `root` The root container DOM element. Append your content to it.
19
+ * - `dialog` The reference to the `<vaadin-dialog>` element.
20
+ * @type {DialogRenderer | undefined}
21
+ */
22
+ renderer: {
23
+ type: Object,
24
+ },
25
+
26
+ /**
27
+ * String used for rendering a dialog title.
28
+ *
29
+ * If both `headerTitle` and `headerRenderer` are defined, the title
30
+ * and the elements created by the renderer will be placed next to
31
+ * each other, with the title coming first.
32
+ *
33
+ * When `headerTitle` is set, the attribute `has-title` is added to the overlay element.
34
+ * @attr {string} header-title
35
+ */
36
+ headerTitle: String,
37
+
38
+ /**
39
+ * Custom function for rendering the dialog header.
40
+ * Receives two arguments:
41
+ *
42
+ * - `root` The root container DOM element. Append your content to it.
43
+ * - `dialog` The reference to the `<vaadin-dialog>` element.
44
+ *
45
+ * If both `headerTitle` and `headerRenderer` are defined, the title
46
+ * and the elements created by the renderer will be placed next to
47
+ * each other, with the title coming first.
48
+ *
49
+ * When `headerRenderer` is set, the attribute `has-header` is added to the overlay element.
50
+ * @type {DialogRenderer | undefined}
51
+ */
52
+ headerRenderer: {
53
+ type: Object,
54
+ },
55
+
56
+ /**
57
+ * Custom function for rendering the dialog footer.
58
+ * Receives two arguments:
59
+ *
60
+ * - `root` The root container DOM element. Append your content to it.
61
+ * - `dialog` The reference to the `<vaadin-dialog>` element.
62
+ *
63
+ * When `footerRenderer` is set, the attribute `has-footer` is added to the overlay element.
64
+ * @type {DialogRenderer | undefined}
65
+ */
66
+ footerRenderer: {
67
+ type: Object,
68
+ },
69
+ };
70
+ }
71
+
72
+ /**
73
+ * Requests an update for the content of the dialog.
74
+ * While performing the update, it invokes the renderer passed in the `renderer` property,
75
+ * as well as `headerRender` and `footerRenderer` properties, if these are defined.
76
+ *
77
+ * It is not guaranteed that the update happens immediately (synchronously) after it is requested.
78
+ */
79
+ requestContentUpdate() {
80
+ if (this._overlayElement) {
81
+ this._overlayElement.requestContentUpdate();
82
+ }
83
+ }
84
+ };
@@ -3,108 +3,7 @@
3
3
  * Copyright (c) 2017 - 2023 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
- import { css, registerStyles } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
7
6
  import { eventInWindow, getMouseOrFirstTouchEvent } from './vaadin-dialog-utils.js';
8
-
9
- registerStyles(
10
- 'vaadin-dialog-overlay',
11
- css`
12
- [part='overlay'] {
13
- position: relative;
14
- overflow: visible;
15
- max-height: 100%;
16
- display: flex;
17
- }
18
-
19
- [part='content'] {
20
- box-sizing: border-box;
21
- height: 100%;
22
- }
23
-
24
- .resizer-container {
25
- overflow: auto;
26
- flex-grow: 1;
27
- border-radius: inherit; /* prevent child elements being drawn outside part=overlay */
28
- }
29
-
30
- [part='overlay'][style] .resizer-container {
31
- min-height: 100%;
32
- width: 100%;
33
- }
34
-
35
- :host(:not([resizable])) .resizer {
36
- display: none;
37
- }
38
-
39
- :host([resizable]) [part='title'] {
40
- cursor: move;
41
- -webkit-user-select: none;
42
- user-select: none;
43
- }
44
-
45
- .resizer {
46
- position: absolute;
47
- height: 16px;
48
- width: 16px;
49
- }
50
-
51
- .resizer.edge {
52
- height: 8px;
53
- width: 8px;
54
- inset: -4px;
55
- }
56
-
57
- .resizer.edge.n {
58
- width: auto;
59
- bottom: auto;
60
- cursor: ns-resize;
61
- }
62
-
63
- .resizer.ne {
64
- top: -4px;
65
- right: -4px;
66
- cursor: nesw-resize;
67
- }
68
-
69
- .resizer.edge.e {
70
- height: auto;
71
- left: auto;
72
- cursor: ew-resize;
73
- }
74
-
75
- .resizer.se {
76
- bottom: -4px;
77
- right: -4px;
78
- cursor: nwse-resize;
79
- }
80
-
81
- .resizer.edge.s {
82
- width: auto;
83
- top: auto;
84
- cursor: ns-resize;
85
- }
86
-
87
- .resizer.sw {
88
- bottom: -4px;
89
- left: -4px;
90
- cursor: nesw-resize;
91
- }
92
-
93
- .resizer.edge.w {
94
- height: auto;
95
- right: auto;
96
- cursor: ew-resize;
97
- }
98
-
99
- .resizer.nw {
100
- top: -4px;
101
- left: -4px;
102
- cursor: nwse-resize;
103
- }
104
- `,
105
- { moduleId: 'vaadin-dialog-resizable-overlay-styles' },
106
- );
107
-
108
7
  /**
109
8
  * @polymerMixin
110
9
  */
@@ -125,11 +24,14 @@ export const DialogResizableMixin = (superClass) =>
125
24
  }
126
25
 
127
26
  /** @protected */
128
- ready() {
27
+ async ready() {
129
28
  super.ready();
130
29
  this._originalBounds = {};
131
30
  this._originalMouseCoords = {};
132
31
  this._resizeListeners = { start: {}, resize: {}, stop: {} };
32
+
33
+ // Wait for overlay render
34
+ await new Promise(requestAnimationFrame);
133
35
  this._addResizeListeners();
134
36
  }
135
37
 
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2021 - 2023 Vaadin Ltd..
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+ import type { CSSResult } from 'lit';
7
+
8
+ export const dialogOverlay: CSSResult;
9
+
10
+ export const resizableOverlay: CSSResult;