@vaadin/dialog 22.0.0-alpha10
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/LICENSE +190 -0
- package/README.md +68 -0
- package/package.json +51 -0
- package/src/vaadin-dialog-draggable-mixin.d.ts +29 -0
- package/src/vaadin-dialog-draggable-mixin.js +118 -0
- package/src/vaadin-dialog-resizable-mixin.d.ts +22 -0
- package/src/vaadin-dialog-resizable-mixin.js +250 -0
- package/src/vaadin-dialog-utils.d.ts +20 -0
- package/src/vaadin-dialog-utils.js +20 -0
- package/src/vaadin-dialog.d.ts +165 -0
- package/src/vaadin-dialog.js +351 -0
- package/theme/lumo/vaadin-dialog-styles.js +69 -0
- package/theme/lumo/vaadin-dialog.js +2 -0
- package/theme/material/vaadin-dialog-styles.js +26 -0
- package/theme/material/vaadin-dialog.js +2 -0
- package/vaadin-dialog.d.ts +1 -0
- package/vaadin-dialog.js +2 -0
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright (c) 2021 Vaadin Ltd.
|
|
4
|
+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
|
+
*/
|
|
6
|
+
import { PolymerElement, html } from '@polymer/polymer/polymer-element.js';
|
|
7
|
+
import { IronResizableBehavior } from '@polymer/iron-resizable-behavior/iron-resizable-behavior.js';
|
|
8
|
+
import { mixinBehaviors } from '@polymer/polymer/lib/legacy/class.js';
|
|
9
|
+
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
|
|
10
|
+
import { processTemplates } from '@vaadin/component-base/src/templates.js';
|
|
11
|
+
import { OverlayElement } from '@vaadin/vaadin-overlay/src/vaadin-overlay.js';
|
|
12
|
+
import { ThemePropertyMixin } from '@vaadin/vaadin-themable-mixin/vaadin-theme-property-mixin.js';
|
|
13
|
+
import { registerStyles, css } from '@vaadin/vaadin-themable-mixin/register-styles.js';
|
|
14
|
+
import { DialogDraggableMixin } from './vaadin-dialog-draggable-mixin.js';
|
|
15
|
+
import { DialogResizableMixin } from './vaadin-dialog-resizable-mixin.js';
|
|
16
|
+
|
|
17
|
+
registerStyles(
|
|
18
|
+
'vaadin-dialog-overlay',
|
|
19
|
+
css`
|
|
20
|
+
/*
|
|
21
|
+
NOTE(platosha): Make some min-width to prevent collapsing of the content
|
|
22
|
+
taking the parent width, e. g., <vaadin-grid> and such.
|
|
23
|
+
*/
|
|
24
|
+
[part='content'] {
|
|
25
|
+
min-width: 12em; /* matches the default <vaadin-text-field> width */
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
:host([has-bounds-set]) [part='overlay'] {
|
|
29
|
+
max-width: none;
|
|
30
|
+
}
|
|
31
|
+
`,
|
|
32
|
+
{ moduleId: 'vaadin-dialog-overlay-styles' }
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
let memoizedTemplate;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* An element used internally by `<vaadin-dialog>`. Not intended to be used separately.
|
|
39
|
+
*
|
|
40
|
+
* @extends OverlayElement
|
|
41
|
+
* @private
|
|
42
|
+
*/
|
|
43
|
+
class DialogOverlay extends mixinBehaviors(IronResizableBehavior, OverlayElement) {
|
|
44
|
+
static get is() {
|
|
45
|
+
return 'vaadin-dialog-overlay';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
static get template() {
|
|
49
|
+
if (!memoizedTemplate) {
|
|
50
|
+
memoizedTemplate = super.template.cloneNode(true);
|
|
51
|
+
const contentPart = memoizedTemplate.content.querySelector('[part="content"]');
|
|
52
|
+
const overlayPart = memoizedTemplate.content.querySelector('[part="overlay"]');
|
|
53
|
+
const resizerContainer = document.createElement('div');
|
|
54
|
+
resizerContainer.id = 'resizerContainer';
|
|
55
|
+
resizerContainer.classList.add('resizer-container');
|
|
56
|
+
resizerContainer.appendChild(contentPart);
|
|
57
|
+
overlayPart.appendChild(resizerContainer);
|
|
58
|
+
}
|
|
59
|
+
return memoizedTemplate;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
static get properties() {
|
|
63
|
+
return {
|
|
64
|
+
modeless: Boolean,
|
|
65
|
+
|
|
66
|
+
withBackdrop: Boolean
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Updates the coordinates of the overlay.
|
|
72
|
+
* @param {!DialogOverlayBoundsParam} bounds
|
|
73
|
+
*/
|
|
74
|
+
setBounds(bounds) {
|
|
75
|
+
const overlay = this.$.overlay;
|
|
76
|
+
const parsedBounds = Object.assign({}, bounds);
|
|
77
|
+
|
|
78
|
+
if (overlay.style.position !== 'absolute') {
|
|
79
|
+
overlay.style.position = 'absolute';
|
|
80
|
+
this.setAttribute('has-bounds-set', '');
|
|
81
|
+
this.__forceSafariReflow();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
for (const arg in parsedBounds) {
|
|
85
|
+
if (typeof parsedBounds[arg] === 'number') {
|
|
86
|
+
parsedBounds[arg] = `${parsedBounds[arg]}px`;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
Object.assign(overlay.style, parsedBounds);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Retrieves the coordinates of the overlay.
|
|
95
|
+
* @return {!DialogOverlayBounds}
|
|
96
|
+
*/
|
|
97
|
+
getBounds() {
|
|
98
|
+
const overlayBounds = this.$.overlay.getBoundingClientRect();
|
|
99
|
+
const containerBounds = this.getBoundingClientRect();
|
|
100
|
+
const top = overlayBounds.top - containerBounds.top;
|
|
101
|
+
const left = overlayBounds.left - containerBounds.left;
|
|
102
|
+
const width = overlayBounds.width;
|
|
103
|
+
const height = overlayBounds.height;
|
|
104
|
+
return { top, left, width, height };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Safari 13 renders overflowing elements incorrectly.
|
|
109
|
+
* This forces it to recalculate height.
|
|
110
|
+
* @private
|
|
111
|
+
*/
|
|
112
|
+
__forceSafariReflow() {
|
|
113
|
+
const scrollPosition = this.$.resizerContainer.scrollTop;
|
|
114
|
+
const overlay = this.$.overlay;
|
|
115
|
+
overlay.style.display = 'block';
|
|
116
|
+
|
|
117
|
+
requestAnimationFrame(() => {
|
|
118
|
+
overlay.style.display = '';
|
|
119
|
+
this.$.resizerContainer.scrollTop = scrollPosition;
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
customElements.define(DialogOverlay.is, DialogOverlay);
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* `<vaadin-dialog>` is a Web Component for creating customized modal dialogs.
|
|
128
|
+
*
|
|
129
|
+
* ### Rendering
|
|
130
|
+
*
|
|
131
|
+
* The content of the dialog can be populated by using the renderer callback function.
|
|
132
|
+
*
|
|
133
|
+
* The renderer function provides `root`, `dialog` arguments.
|
|
134
|
+
* Generate DOM content, append it to the `root` element and control the state
|
|
135
|
+
* of the host element by accessing `dialog`. Before generating new content,
|
|
136
|
+
* users are able to check if there is already content in `root` for reusing it.
|
|
137
|
+
*
|
|
138
|
+
* ```html
|
|
139
|
+
* <vaadin-dialog id="dialog"></vaadin-dialog>
|
|
140
|
+
* ```
|
|
141
|
+
* ```js
|
|
142
|
+
* const dialog = document.querySelector('#dialog');
|
|
143
|
+
* dialog.renderer = function(root, dialog) {
|
|
144
|
+
* root.textContent = "Sample dialog";
|
|
145
|
+
* };
|
|
146
|
+
* ```
|
|
147
|
+
*
|
|
148
|
+
* Renderer is called on the opening of the dialog.
|
|
149
|
+
* DOM generated during the renderer call can be reused
|
|
150
|
+
* in the next renderer call and will be provided with the `root` argument.
|
|
151
|
+
* On first call it will be empty.
|
|
152
|
+
*
|
|
153
|
+
* ### Styling
|
|
154
|
+
*
|
|
155
|
+
* `<vaadin-dialog>` uses `<vaadin-dialog-overlay>` internal
|
|
156
|
+
* themable component as the actual visible dialog overlay.
|
|
157
|
+
*
|
|
158
|
+
* See [`<vaadin-overlay>`](#/elements/vaadin-overlay) documentation.
|
|
159
|
+
* for `<vaadin-dialog-overlay>` parts.
|
|
160
|
+
*
|
|
161
|
+
* Note: the `theme` attribute value set on `<vaadin-dialog>` is
|
|
162
|
+
* propagated to the internal `<vaadin-dialog-overlay>` component.
|
|
163
|
+
*
|
|
164
|
+
* See [Styling Components](https://vaadin.com/docs/latest/ds/customization/styling-components) documentation.
|
|
165
|
+
*
|
|
166
|
+
* @fires {CustomEvent} resize - Fired when the dialog resize is finished.
|
|
167
|
+
* @fires {CustomEvent} opened-changed - Fired when the `opened` property changes.
|
|
168
|
+
*
|
|
169
|
+
* @extends HTMLElement
|
|
170
|
+
* @mixes ThemePropertyMixin
|
|
171
|
+
* @mixes ElementMixin
|
|
172
|
+
* @mixes DialogDraggableMixin
|
|
173
|
+
* @mixes DialogResizableMixin
|
|
174
|
+
*/
|
|
175
|
+
class Dialog extends ThemePropertyMixin(ElementMixin(DialogDraggableMixin(DialogResizableMixin(PolymerElement)))) {
|
|
176
|
+
static get template() {
|
|
177
|
+
return html`
|
|
178
|
+
<style>
|
|
179
|
+
:host {
|
|
180
|
+
display: none;
|
|
181
|
+
}
|
|
182
|
+
</style>
|
|
183
|
+
|
|
184
|
+
<vaadin-dialog-overlay
|
|
185
|
+
id="overlay"
|
|
186
|
+
on-opened-changed="_onOverlayOpened"
|
|
187
|
+
on-mousedown="_bringOverlayToFront"
|
|
188
|
+
on-touchstart="_bringOverlayToFront"
|
|
189
|
+
theme$="[[theme]]"
|
|
190
|
+
modeless="[[modeless]]"
|
|
191
|
+
with-backdrop="[[!modeless]]"
|
|
192
|
+
resizable$="[[resizable]]"
|
|
193
|
+
focus-trap
|
|
194
|
+
></vaadin-dialog-overlay>
|
|
195
|
+
`;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
static get is() {
|
|
199
|
+
return 'vaadin-dialog';
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
static get properties() {
|
|
203
|
+
return {
|
|
204
|
+
/**
|
|
205
|
+
* True if the overlay is currently displayed.
|
|
206
|
+
* @type {boolean}
|
|
207
|
+
*/
|
|
208
|
+
opened: {
|
|
209
|
+
type: Boolean,
|
|
210
|
+
value: false,
|
|
211
|
+
notify: true
|
|
212
|
+
},
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Set to true to disable closing dialog on outside click
|
|
216
|
+
* @attr {boolean} no-close-on-outside-click
|
|
217
|
+
* @type {boolean}
|
|
218
|
+
*/
|
|
219
|
+
noCloseOnOutsideClick: {
|
|
220
|
+
type: Boolean,
|
|
221
|
+
value: false
|
|
222
|
+
},
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Set to true to disable closing dialog on Escape press
|
|
226
|
+
* @attr {boolean} no-close-on-esc
|
|
227
|
+
* @type {boolean}
|
|
228
|
+
*/
|
|
229
|
+
noCloseOnEsc: {
|
|
230
|
+
type: Boolean,
|
|
231
|
+
value: false
|
|
232
|
+
},
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Set the `aria-label` attribute for assistive technologies like
|
|
236
|
+
* screen readers. An empty string value for this property (the
|
|
237
|
+
* default) means that the `aria-label` attribute is not present.
|
|
238
|
+
*/
|
|
239
|
+
ariaLabel: {
|
|
240
|
+
type: String,
|
|
241
|
+
value: ''
|
|
242
|
+
},
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Custom function for rendering the content of the dialog.
|
|
246
|
+
* Receives two arguments:
|
|
247
|
+
*
|
|
248
|
+
* - `root` The root container DOM element. Append your content to it.
|
|
249
|
+
* - `dialog` The reference to the `<vaadin-dialog>` element.
|
|
250
|
+
* @type {DialogRenderer | undefined}
|
|
251
|
+
*/
|
|
252
|
+
renderer: Function,
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Set to true to remove backdrop and allow click events on background elements.
|
|
256
|
+
* @type {boolean}
|
|
257
|
+
*/
|
|
258
|
+
modeless: {
|
|
259
|
+
type: Boolean,
|
|
260
|
+
value: false
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
static get observers() {
|
|
266
|
+
return ['_openedChanged(opened)', '_ariaLabelChanged(ariaLabel)', '_rendererChanged(renderer)'];
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/** @protected */
|
|
270
|
+
ready() {
|
|
271
|
+
super.ready();
|
|
272
|
+
this.$.overlay.setAttribute('role', 'dialog');
|
|
273
|
+
this.$.overlay.addEventListener('vaadin-overlay-outside-click', this._handleOutsideClick.bind(this));
|
|
274
|
+
this.$.overlay.addEventListener('vaadin-overlay-escape-press', this._handleEscPress.bind(this));
|
|
275
|
+
|
|
276
|
+
processTemplates(this);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Requests an update for the content of the dialog.
|
|
281
|
+
* While performing the update, it invokes the renderer passed in the `renderer` property.
|
|
282
|
+
*
|
|
283
|
+
* It is not guaranteed that the update happens immediately (synchronously) after it is requested.
|
|
284
|
+
*/
|
|
285
|
+
requestContentUpdate() {
|
|
286
|
+
this.$.overlay.requestContentUpdate();
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/** @private */
|
|
290
|
+
_rendererChanged(renderer) {
|
|
291
|
+
this.$.overlay.setProperties({ owner: this, renderer });
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/** @protected */
|
|
295
|
+
disconnectedCallback() {
|
|
296
|
+
super.disconnectedCallback();
|
|
297
|
+
this.opened = false;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/** @private */
|
|
301
|
+
_openedChanged(opened) {
|
|
302
|
+
this.$.overlay.opened = opened;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/** @private */
|
|
306
|
+
_ariaLabelChanged(ariaLabel) {
|
|
307
|
+
if (ariaLabel) {
|
|
308
|
+
this.$.overlay.setAttribute('aria-label', ariaLabel);
|
|
309
|
+
} else {
|
|
310
|
+
this.$.overlay.removeAttribute('aria-label');
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/** @private */
|
|
315
|
+
_onOverlayOpened(e) {
|
|
316
|
+
if (e.detail.value === false) {
|
|
317
|
+
this.opened = false;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Close the dialog if `noCloseOnOutsideClick` isn't set to true
|
|
323
|
+
* @private
|
|
324
|
+
*/
|
|
325
|
+
_handleOutsideClick(e) {
|
|
326
|
+
if (this.noCloseOnOutsideClick) {
|
|
327
|
+
e.preventDefault();
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Close the dialog if `noCloseOnEsc` isn't set to true
|
|
333
|
+
* @private
|
|
334
|
+
*/
|
|
335
|
+
_handleEscPress(e) {
|
|
336
|
+
if (this.noCloseOnEsc) {
|
|
337
|
+
e.preventDefault();
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/** @private */
|
|
342
|
+
_bringOverlayToFront() {
|
|
343
|
+
if (this.modeless) {
|
|
344
|
+
this.$.overlay.bringToFront();
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
customElements.define(Dialog.is, Dialog);
|
|
350
|
+
|
|
351
|
+
export { Dialog };
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { registerStyles, css } from '@vaadin/vaadin-themable-mixin/register-styles.js';
|
|
2
|
+
import '@vaadin/vaadin-lumo-styles/spacing.js';
|
|
3
|
+
import { overlay } from '@vaadin/vaadin-lumo-styles/mixins/overlay.js';
|
|
4
|
+
|
|
5
|
+
const dialogOverlay = css`
|
|
6
|
+
/* Optical centering */
|
|
7
|
+
:host::before,
|
|
8
|
+
:host::after {
|
|
9
|
+
content: '';
|
|
10
|
+
flex-basis: 0;
|
|
11
|
+
flex-grow: 1;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
:host::after {
|
|
15
|
+
flex-grow: 1.1;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
[part='overlay'] {
|
|
19
|
+
border-radius: var(--lumo-border-radius-l);
|
|
20
|
+
box-shadow: 0 0 0 1px var(--lumo-shade-5pct), var(--lumo-box-shadow-xl);
|
|
21
|
+
background-image: none;
|
|
22
|
+
outline: none;
|
|
23
|
+
-webkit-tap-highlight-color: transparent;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
[part='content'] {
|
|
27
|
+
padding: var(--lumo-space-l);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/* No padding */
|
|
31
|
+
:host([theme~='no-padding']) [part='content'] {
|
|
32
|
+
padding: 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* Animations */
|
|
36
|
+
|
|
37
|
+
:host([opening]),
|
|
38
|
+
:host([closing]) {
|
|
39
|
+
animation: 0.25s lumo-overlay-dummy-animation;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
:host([opening]) [part='overlay'] {
|
|
43
|
+
animation: 0.12s 0.05s vaadin-dialog-enter cubic-bezier(0.215, 0.61, 0.355, 1) both;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@keyframes vaadin-dialog-enter {
|
|
47
|
+
0% {
|
|
48
|
+
opacity: 0;
|
|
49
|
+
transform: scale(0.95);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
:host([closing]) [part='overlay'] {
|
|
54
|
+
animation: 0.1s 0.03s vaadin-dialog-exit cubic-bezier(0.55, 0.055, 0.675, 0.19) both;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
:host([closing]) [part='backdrop'] {
|
|
58
|
+
animation-delay: 0.05s;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
@keyframes vaadin-dialog-exit {
|
|
62
|
+
100% {
|
|
63
|
+
opacity: 0;
|
|
64
|
+
transform: scale(1.02);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
`;
|
|
68
|
+
|
|
69
|
+
registerStyles('vaadin-dialog-overlay', [overlay, dialogOverlay], { moduleId: 'lumo-dialog' });
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { registerStyles, css } from '@vaadin/vaadin-themable-mixin/register-styles.js';
|
|
2
|
+
import { overlay } from '@vaadin/vaadin-material-styles/mixins/overlay.js';
|
|
3
|
+
import '@vaadin/vaadin-material-styles/shadow.js';
|
|
4
|
+
|
|
5
|
+
const dialogOverlay = css`
|
|
6
|
+
[part='overlay'] {
|
|
7
|
+
box-shadow: var(--material-shadow-elevation-24dp);
|
|
8
|
+
outline: none;
|
|
9
|
+
max-width: 560px;
|
|
10
|
+
min-width: 280px;
|
|
11
|
+
-webkit-tap-highlight-color: transparent;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
[part='content'] {
|
|
15
|
+
padding: 24px;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/* No padding */
|
|
19
|
+
:host([theme~='no-padding']) [part='content'] {
|
|
20
|
+
padding: 0;
|
|
21
|
+
}
|
|
22
|
+
`;
|
|
23
|
+
|
|
24
|
+
registerStyles('vaadin-dialog-overlay', [overlay, dialogOverlay], {
|
|
25
|
+
moduleId: 'material-dialog'
|
|
26
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './src/vaadin-dialog.js';
|
package/vaadin-dialog.js
ADDED