@vaadin/master-detail-layout 25.2.0-alpha6 → 25.2.0-alpha8
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/custom-elements.json +47 -11
- package/package.json +8 -8
- package/src/styles/vaadin-master-detail-layout-base-styles.js +171 -85
- package/src/vaadin-master-detail-layout.d.ts +29 -8
- package/src/vaadin-master-detail-layout.js +118 -92
- package/web-types.json +54 -10
- package/web-types.lit.json +23 -9
package/custom-elements.json
CHANGED
|
@@ -243,7 +243,7 @@
|
|
|
243
243
|
"declarations": [
|
|
244
244
|
{
|
|
245
245
|
"kind": "class",
|
|
246
|
-
"description": "`<vaadin-master-detail-layout>` is a web component for building UIs with a master\n(or primary) area and a detail (or secondary) area that is displayed next to, or\noverlaid on top of, the master area, depending on configuration and viewport size.\n\n### Slots\n\nThe component has two main content areas: the master area (default slot)\nand the detail area (`detail` slot). When the detail doesn't fit next to\nthe master, it is shown as an overlay on top of the master area:\n\n```html\n<vaadin-master-detail-layout>\n <div>Master content</div>\n <div slot=\"detail\">Detail content</div>\n</vaadin-master-detail-layout>\n```\n\nThe component also supports a `detail-placeholder` slot for content shown\nin the detail area when no detail is selected. Unlike the `detail` slot,\nthe placeholder is simply hidden when it doesn't fit next to the master area,\nrather than shown as an overlay:\n\n```html\n<vaadin-master-detail-layout>\n <div>Master content</div>\n <div slot=\"detail-placeholder\">Select an item</div>\n</vaadin-master-detail-layout>\n```\n\n### Styling\n\nThe following shadow DOM parts are available for styling:\n\nPart name | Description\n----------------------|----------------------\n`backdrop` | Backdrop covering the master area in the overlay mode\n`master` | The master area\n`detail` | The detail area\n`detail-placeholder` | The detail placeholder area\n\nThe following state attributes are available for styling:\n\nAttribute | Description\n--------------------------|----------------------\n`expand`
|
|
246
|
+
"description": "`<vaadin-master-detail-layout>` is a web component for building UIs with a master\n(or primary) area and a detail (or secondary) area that is displayed next to, or\noverlaid on top of, the master area, depending on configuration and viewport size.\n\n### Slots\n\nThe component has two main content areas: the master area (default slot)\nand the detail area (`detail` slot). When the detail doesn't fit next to\nthe master, it is shown as an overlay on top of the master area:\n\n```html\n<vaadin-master-detail-layout>\n <div>Master content</div>\n <div slot=\"detail\">Detail content</div>\n</vaadin-master-detail-layout>\n```\n\nThe component also supports a `detail-placeholder` slot for content shown\nin the detail area when no detail is selected. Unlike the `detail` slot,\nthe placeholder is simply hidden when it doesn't fit next to the master area,\nrather than shown as an overlay:\n\n```html\n<vaadin-master-detail-layout>\n <div>Master content</div>\n <div slot=\"detail-placeholder\">Select an item</div>\n</vaadin-master-detail-layout>\n```\n\n### Styling\n\nThe following shadow DOM parts are available for styling:\n\nPart name | Description\n----------------------|----------------------\n`backdrop` | Backdrop covering the master area in the overlay mode\n`master` | The master area\n`detail` | The detail area\n`detail-placeholder` | The detail placeholder area\n\nThe following state attributes are available for styling:\n\nAttribute | Description\n--------------------------|----------------------\n`expand-master` | Set when the master area expands to fill available space.\n`expand-detail` | Set when the detail area expands to fill available space.\n`orientation` | Set to `horizontal` or `vertical` depending on the orientation.\n`has-detail` | Set when the detail content is provided and visible.\n`has-detail-placeholder` | Set when the detail placeholder content is provided.\n`overlay` | Set when columns don't fit and the detail is shown as an overlay.\n`overlay-containment` | Set to `layout` or `page`.\n\nThe following custom CSS properties are available for styling:\n\nCustom CSS property |\n:----------------------------------------------------|\n| `--vaadin-master-detail-layout-border-color` |\n| `--vaadin-master-detail-layout-border-width` |\n| `--vaadin-master-detail-layout-detail-background` |\n| `--vaadin-master-detail-layout-detail-shadow` |\n| `--vaadin-overlay-backdrop-background` |\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.",
|
|
247
247
|
"name": "MasterDetailLayout",
|
|
248
248
|
"members": [
|
|
249
249
|
{
|
|
@@ -258,13 +258,33 @@
|
|
|
258
258
|
},
|
|
259
259
|
{
|
|
260
260
|
"kind": "field",
|
|
261
|
-
"name": "
|
|
261
|
+
"name": "expandDetail",
|
|
262
262
|
"privacy": "public",
|
|
263
263
|
"type": {
|
|
264
|
-
"text": "
|
|
264
|
+
"text": "boolean"
|
|
265
|
+
},
|
|
266
|
+
"description": "When true, the detail area grows to fill the available space.\nIf `expandMaster` is also true, both areas share the available\nspace equally.",
|
|
267
|
+
"attribute": "expand-detail"
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
"kind": "field",
|
|
271
|
+
"name": "expandMaster",
|
|
272
|
+
"privacy": "public",
|
|
273
|
+
"type": {
|
|
274
|
+
"text": "boolean"
|
|
275
|
+
},
|
|
276
|
+
"description": "When true, the master area grows to fill the available space.\nIf `expandDetail` is also true, both areas share the available\nspace equally.",
|
|
277
|
+
"attribute": "expand-master"
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
"kind": "field",
|
|
281
|
+
"name": "forceOverlay",
|
|
282
|
+
"privacy": "public",
|
|
283
|
+
"type": {
|
|
284
|
+
"text": "boolean"
|
|
265
285
|
},
|
|
266
|
-
"description": "
|
|
267
|
-
"attribute": "
|
|
286
|
+
"description": "When true, the layout forces the detail area to be shown as an overlay,\neven if there is enough space for master and detail to be shown next to\neach other using the default (split) mode.",
|
|
287
|
+
"attribute": "force-overlay"
|
|
268
288
|
},
|
|
269
289
|
{
|
|
270
290
|
"kind": "field",
|
|
@@ -303,7 +323,7 @@
|
|
|
303
323
|
"type": {
|
|
304
324
|
"text": "string"
|
|
305
325
|
},
|
|
306
|
-
"description": "Defines the containment of the detail area when the layout is in\noverlay mode. When set to `layout`, the overlay is confined to the\nlayout. When set to `
|
|
326
|
+
"description": "Defines the containment of the detail area when the layout is in\noverlay mode. When set to `layout`, the overlay is confined to the\nlayout. When set to `page`, the overlay is confined to the\nbrowser's viewport. Defaults to `layout`.",
|
|
307
327
|
"attribute": "overlay-containment"
|
|
308
328
|
},
|
|
309
329
|
{
|
|
@@ -348,12 +368,28 @@
|
|
|
348
368
|
"fieldName": "detailSize"
|
|
349
369
|
},
|
|
350
370
|
{
|
|
351
|
-
"name": "expand",
|
|
371
|
+
"name": "expand-detail",
|
|
352
372
|
"type": {
|
|
353
|
-
"text": "
|
|
373
|
+
"text": "boolean"
|
|
374
|
+
},
|
|
375
|
+
"description": "When true, the detail area grows to fill the available space.\nIf `expandMaster` is also true, both areas share the available\nspace equally.",
|
|
376
|
+
"fieldName": "expandDetail"
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
"name": "expand-master",
|
|
380
|
+
"type": {
|
|
381
|
+
"text": "boolean"
|
|
382
|
+
},
|
|
383
|
+
"description": "When true, the master area grows to fill the available space.\nIf `expandDetail` is also true, both areas share the available\nspace equally.",
|
|
384
|
+
"fieldName": "expandMaster"
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
"name": "force-overlay",
|
|
388
|
+
"type": {
|
|
389
|
+
"text": "boolean"
|
|
354
390
|
},
|
|
355
|
-
"description": "
|
|
356
|
-
"fieldName": "
|
|
391
|
+
"description": "When true, the layout forces the detail area to be shown as an overlay,\neven if there is enough space for master and detail to be shown next to\neach other using the default (split) mode.",
|
|
392
|
+
"fieldName": "forceOverlay"
|
|
357
393
|
},
|
|
358
394
|
{
|
|
359
395
|
"name": "master-size",
|
|
@@ -384,7 +420,7 @@
|
|
|
384
420
|
"type": {
|
|
385
421
|
"text": "string"
|
|
386
422
|
},
|
|
387
|
-
"description": "Defines the containment of the detail area when the layout is in\noverlay mode. When set to `layout`, the overlay is confined to the\nlayout. When set to `
|
|
423
|
+
"description": "Defines the containment of the detail area when the layout is in\noverlay mode. When set to `layout`, the overlay is confined to the\nlayout. When set to `page`, the overlay is confined to the\nbrowser's viewport. Defaults to `layout`.",
|
|
388
424
|
"fieldName": "overlayContainment"
|
|
389
425
|
},
|
|
390
426
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vaadin/master-detail-layout",
|
|
3
|
-
"version": "25.2.0-
|
|
3
|
+
"version": "25.2.0-alpha8",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -34,16 +34,16 @@
|
|
|
34
34
|
"web-component"
|
|
35
35
|
],
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@vaadin/a11y-base": "25.2.0-
|
|
38
|
-
"@vaadin/component-base": "25.2.0-
|
|
39
|
-
"@vaadin/vaadin-themable-mixin": "25.2.0-
|
|
37
|
+
"@vaadin/a11y-base": "25.2.0-alpha8",
|
|
38
|
+
"@vaadin/component-base": "25.2.0-alpha8",
|
|
39
|
+
"@vaadin/vaadin-themable-mixin": "25.2.0-alpha8",
|
|
40
40
|
"lit": "^3.0.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
|
-
"@vaadin/aura": "25.2.0-
|
|
44
|
-
"@vaadin/chai-plugins": "25.2.0-
|
|
43
|
+
"@vaadin/aura": "25.2.0-alpha8",
|
|
44
|
+
"@vaadin/chai-plugins": "25.2.0-alpha8",
|
|
45
45
|
"@vaadin/testing-helpers": "^2.0.0",
|
|
46
|
-
"@vaadin/vaadin-lumo-styles": "25.2.0-
|
|
46
|
+
"@vaadin/vaadin-lumo-styles": "25.2.0-alpha8",
|
|
47
47
|
"sinon": "^21.0.2"
|
|
48
48
|
},
|
|
49
49
|
"customElements": "custom-elements.json",
|
|
@@ -51,5 +51,5 @@
|
|
|
51
51
|
"web-types.json",
|
|
52
52
|
"web-types.lit.json"
|
|
53
53
|
],
|
|
54
|
-
"gitHead": "
|
|
54
|
+
"gitHead": "2b82e20cdfc605b1187e9a24ae42869e1500ab68"
|
|
55
55
|
}
|
|
@@ -7,85 +7,132 @@ import '@vaadin/component-base/src/styles/style-props.js';
|
|
|
7
7
|
import { css } from 'lit';
|
|
8
8
|
|
|
9
9
|
export const masterDetailLayoutStyles = css`
|
|
10
|
+
/* stylelint-disable no-duplicate-selectors */
|
|
10
11
|
:host {
|
|
11
|
-
--_master-size: 30rem;
|
|
12
|
-
--_master-extra: 0px;
|
|
13
|
-
--_detail-size: var(--_detail-cached-size);
|
|
14
|
-
--_detail-extra: 0px;
|
|
15
|
-
--_detail-cached-size: min-content;
|
|
16
|
-
|
|
17
12
|
--_rtl-multiplier: 1;
|
|
18
13
|
--_transition-duration: 0s;
|
|
19
14
|
--_transition-easing: cubic-bezier(0.78, 0, 0.22, 1);
|
|
20
|
-
--_transition-offset: calc(30px * var(--_rtl-multiplier));
|
|
21
15
|
|
|
22
16
|
display: grid;
|
|
23
17
|
box-sizing: border-box;
|
|
24
18
|
height: 100%;
|
|
25
19
|
position: relative;
|
|
26
|
-
z-index: 0;
|
|
27
20
|
overflow: clip;
|
|
28
|
-
grid-template-columns:
|
|
29
|
-
[master-start] var(--_master-size) var(--_master-extra)
|
|
30
|
-
[detail-start] var(--_detail-size) var(--_detail-extra)
|
|
31
|
-
[detail-end];
|
|
32
|
-
grid-template-rows: 100%;
|
|
33
21
|
}
|
|
34
22
|
|
|
35
|
-
:host([
|
|
36
|
-
|
|
37
|
-
display: none !important;
|
|
23
|
+
:host:not([overlay-containment='page']) {
|
|
24
|
+
z-index: 0;
|
|
38
25
|
}
|
|
39
26
|
|
|
40
27
|
:host([dir='rtl']) {
|
|
41
28
|
--_rtl-multiplier: -1;
|
|
42
29
|
}
|
|
43
30
|
|
|
31
|
+
:host([orientation='horizontal']) {
|
|
32
|
+
--_transition-offset: calc(30px * var(--_rtl-multiplier));
|
|
33
|
+
}
|
|
34
|
+
|
|
44
35
|
:host([orientation='vertical']) {
|
|
45
36
|
--_transition-offset: 0 30px;
|
|
37
|
+
}
|
|
46
38
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
39
|
+
:host([hidden]),
|
|
40
|
+
::slotted([hidden]) {
|
|
41
|
+
display: none !important;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* CSS grid template */
|
|
45
|
+
|
|
46
|
+
:host {
|
|
47
|
+
--_master-size: min(100%, 30rem);
|
|
48
|
+
--_master-extra: 0px;
|
|
49
|
+
--_detail-size: var(--_detail-cached-size);
|
|
50
|
+
--_detail-extra: 0px;
|
|
51
|
+
--_detail-cached-size: min-content;
|
|
52
|
+
|
|
53
|
+
/* prettier-ignore */
|
|
54
|
+
--_grid-template:
|
|
55
|
+
[master-start] var(--_master-size) [master-extra] var(--_master-extra)
|
|
56
|
+
[detail-start] var(--_detail-size) [detail-extra] var(--_detail-extra)
|
|
51
57
|
[detail-end];
|
|
52
58
|
}
|
|
53
59
|
|
|
54
|
-
:
|
|
55
|
-
|
|
60
|
+
:host([force-overlay]) {
|
|
61
|
+
/* prettier-ignore */
|
|
62
|
+
--_grid-template:
|
|
63
|
+
[master-start] var(--_master-size) [master-extra] var(--_master-extra)
|
|
64
|
+
[detail-start] 0px [detail-extra] 0px
|
|
65
|
+
[detail-end];
|
|
56
66
|
}
|
|
57
67
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
pointer-events: none;
|
|
68
|
+
:host([orientation='horizontal']) {
|
|
69
|
+
grid-template-columns: var(--_grid-template);
|
|
70
|
+
grid-template-rows: 100%;
|
|
62
71
|
}
|
|
63
72
|
|
|
64
|
-
:host([
|
|
65
|
-
|
|
66
|
-
|
|
73
|
+
:host([orientation='vertical']) {
|
|
74
|
+
grid-template-columns: 100%;
|
|
75
|
+
grid-template-rows: var(--_grid-template);
|
|
67
76
|
}
|
|
68
77
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
78
|
+
/* CSS grid placement */
|
|
79
|
+
|
|
80
|
+
:host {
|
|
81
|
+
--_master-area: master-start / detail-start;
|
|
82
|
+
|
|
83
|
+
/*
|
|
84
|
+
When the detail size isn't explicitly defined and the detail is set to expand,
|
|
85
|
+
the detail column template is 'min-content 1fr'. In this case, the detail area
|
|
86
|
+
should not span both columns initially (and when recalculating the detail size)
|
|
87
|
+
as spanning both would effectively collapse them into a single '1fr' column where
|
|
88
|
+
min-content resolves to 0, making it impossible to measure the detail's intrinsic
|
|
89
|
+
minimum width from JavaScript.
|
|
90
|
+
*/
|
|
91
|
+
--_detail-area: detail-start / detail-extra;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
:host(:is([has-detail], [has-detail-placeholder]):not([recalculating-detail-size])) {
|
|
95
|
+
--_detail-area: detail-start / detail-end;
|
|
72
96
|
}
|
|
73
97
|
|
|
74
|
-
:
|
|
75
|
-
grid-column:
|
|
98
|
+
:host([orientation='horizontal']) #master {
|
|
99
|
+
grid-column: var(--_master-area);
|
|
76
100
|
grid-row: 1;
|
|
77
101
|
}
|
|
78
102
|
|
|
79
103
|
:host([orientation='vertical']) #master {
|
|
80
104
|
grid-column: 1;
|
|
81
|
-
grid-row:
|
|
105
|
+
grid-row: var(--_master-area);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
:host([orientation='horizontal']) :is(#detail, #detailPlaceholder, #detailOutgoing) {
|
|
109
|
+
grid-column: var(--_detail-area);
|
|
110
|
+
grid-row: 1;
|
|
82
111
|
}
|
|
83
112
|
|
|
84
|
-
:host([orientation='vertical']) :is(#detail, #
|
|
113
|
+
:host([orientation='vertical']) :is(#detail, #detailPlaceholder, #detailOutgoing) {
|
|
85
114
|
grid-column: 1;
|
|
86
|
-
grid-row:
|
|
115
|
+
grid-row: var(--_detail-area);
|
|
87
116
|
}
|
|
88
117
|
|
|
118
|
+
/* Expand */
|
|
119
|
+
|
|
120
|
+
:host([expand-master]) {
|
|
121
|
+
--_master-extra: 1fr;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
:host([expand-detail]) {
|
|
125
|
+
--_detail-extra: 1fr;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
:host([keep-detail-column-offscreen]),
|
|
129
|
+
:host([has-detail-placeholder][overlay]:not([has-detail])),
|
|
130
|
+
:host(:not([has-detail-placeholder], [has-detail])) {
|
|
131
|
+
--_master-extra: calc(100% - var(--_master-size));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/* Backdrop base styles */
|
|
135
|
+
|
|
89
136
|
#backdrop {
|
|
90
137
|
--_transition-easing: linear;
|
|
91
138
|
|
|
@@ -98,56 +145,78 @@ export const masterDetailLayoutStyles = css`
|
|
|
98
145
|
forced-color-adjust: none;
|
|
99
146
|
}
|
|
100
147
|
|
|
101
|
-
|
|
102
|
-
:host([expand='master']) {
|
|
103
|
-
--_master-extra: 1fr;
|
|
104
|
-
}
|
|
148
|
+
/* Master base styles */
|
|
105
149
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
150
|
+
#master {
|
|
151
|
+
opacity: 0;
|
|
152
|
+
pointer-events: none;
|
|
153
|
+
box-sizing: border-box;
|
|
109
154
|
}
|
|
110
155
|
|
|
111
|
-
:host([
|
|
112
|
-
|
|
156
|
+
:host([has-master]) #master {
|
|
157
|
+
opacity: 1;
|
|
158
|
+
pointer-events: auto;
|
|
113
159
|
}
|
|
114
160
|
|
|
115
|
-
|
|
116
|
-
:host([has-detail-placeholder][overlay]),
|
|
117
|
-
:host(:not([has-detail-placeholder], [has-detail])) {
|
|
118
|
-
--_master-extra: calc(100% - var(--_master-size));
|
|
119
|
-
}
|
|
161
|
+
/* Detail base styles */
|
|
120
162
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
163
|
+
#detail {
|
|
164
|
+
translate: var(--_transition-offset);
|
|
165
|
+
opacity: 0;
|
|
166
|
+
z-index: 4;
|
|
125
167
|
}
|
|
126
168
|
|
|
127
|
-
:host([
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
var(--vaadin-master-detail-layout-border-color, var(--vaadin-border-color-secondary));
|
|
169
|
+
:host([has-detail]) #detail {
|
|
170
|
+
translate: none;
|
|
171
|
+
opacity: 1;
|
|
131
172
|
}
|
|
132
173
|
|
|
133
|
-
#
|
|
174
|
+
#detailOutgoing {
|
|
134
175
|
position: absolute;
|
|
135
176
|
z-index: 3;
|
|
177
|
+
display: none;
|
|
136
178
|
}
|
|
137
179
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
180
|
+
:host([transition='replace']) #detailOutgoing {
|
|
181
|
+
display: block;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
#detailPlaceholder {
|
|
185
|
+
z-index: 1;
|
|
141
186
|
opacity: 0;
|
|
142
|
-
|
|
187
|
+
pointer-events: none;
|
|
143
188
|
}
|
|
144
189
|
|
|
145
|
-
:host([has-detail]) #
|
|
146
|
-
translate: none;
|
|
190
|
+
:host([has-detail-placeholder]:not([has-detail], [overlay])) #detailPlaceholder {
|
|
147
191
|
opacity: 1;
|
|
192
|
+
pointer-events: auto;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
:is(#detail, #detailPlaceholder, #detailOutgoing) {
|
|
196
|
+
box-sizing: border-box;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/* Detail borders */
|
|
200
|
+
|
|
201
|
+
#detail,
|
|
202
|
+
#detailPlaceholder {
|
|
203
|
+
border-color: var(--vaadin-master-detail-layout-border-color, var(--vaadin-border-color-secondary));
|
|
204
|
+
border-width: var(--vaadin-master-detail-layout-border-width, 1px);
|
|
148
205
|
}
|
|
149
206
|
|
|
150
|
-
:host([
|
|
207
|
+
:host([orientation='horizontal']) #detailPlaceholder,
|
|
208
|
+
:host([orientation='horizontal']:not([overlay])) #detail {
|
|
209
|
+
border-inline-start-style: solid;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
:host([orientation='vertical']) #detailPlaceholder,
|
|
213
|
+
:host([orientation='vertical']:not([overlay])) #detail {
|
|
214
|
+
border-block-start-style: solid;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/* Overlay */
|
|
218
|
+
|
|
219
|
+
:host([overlay][orientation='horizontal']) {
|
|
151
220
|
--_transition-offset: calc((100% + 30px) * var(--_rtl-multiplier));
|
|
152
221
|
}
|
|
153
222
|
|
|
@@ -155,7 +224,12 @@ export const masterDetailLayoutStyles = css`
|
|
|
155
224
|
--_transition-offset: 0 calc(100% + 30px);
|
|
156
225
|
}
|
|
157
226
|
|
|
158
|
-
:host([has-detail][overlay])
|
|
227
|
+
:host([has-detail][overlay]) #backdrop {
|
|
228
|
+
opacity: 1;
|
|
229
|
+
pointer-events: auto;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
:host([has-detail][overlay]) :is(#detail, #detailOutgoing) {
|
|
159
233
|
position: absolute;
|
|
160
234
|
background: var(--vaadin-master-detail-layout-detail-background, var(--vaadin-background-color));
|
|
161
235
|
box-shadow: var(--vaadin-master-detail-layout-detail-shadow, 0 0 20px 0 rgba(0, 0, 0, 0.3));
|
|
@@ -163,40 +237,40 @@ export const masterDetailLayoutStyles = css`
|
|
|
163
237
|
grid-row: none;
|
|
164
238
|
}
|
|
165
239
|
|
|
166
|
-
:host([has-detail][overlay]) #
|
|
167
|
-
opacity: 1;
|
|
168
|
-
pointer-events: auto;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
:host([has-detail][overlay]:not([orientation='vertical'])) :is(#detail, #outgoing) {
|
|
240
|
+
:host([has-detail][overlay][orientation='horizontal']) :is(#detail, #detailOutgoing) {
|
|
172
241
|
inset-block: 0;
|
|
173
242
|
inset-inline-end: 0;
|
|
174
243
|
width: var(--_overlay-size, var(--_detail-size));
|
|
175
244
|
max-width: 100%;
|
|
176
245
|
}
|
|
177
246
|
|
|
178
|
-
:host([has-detail][overlay][orientation='vertical']) :is(#detail, #
|
|
247
|
+
:host([has-detail][overlay][orientation='vertical']) :is(#detail, #detailOutgoing) {
|
|
179
248
|
inset-inline: 0;
|
|
180
249
|
inset-block-end: 0;
|
|
181
250
|
height: var(--_overlay-size, var(--_detail-size));
|
|
182
251
|
max-height: 100%;
|
|
183
252
|
}
|
|
184
253
|
|
|
185
|
-
:host([has-detail][overlay][overlay-containment='
|
|
254
|
+
:host([has-detail][overlay][overlay-containment='page']) :is(#detail, #detailOutgoing, #backdrop) {
|
|
186
255
|
position: fixed;
|
|
256
|
+
padding-top: env(safe-area-inset-top);
|
|
257
|
+
padding-bottom: env(safe-area-inset-bottom);
|
|
258
|
+
padding-right: env(safe-area-inset-right);
|
|
259
|
+
--safe-area-inset-top: 0px;
|
|
260
|
+
--safe-area-inset-bottom: 0px;
|
|
261
|
+
--safe-area-inset-left: 0px;
|
|
262
|
+
--safe-area-inset-right: 0px;
|
|
263
|
+
--safe-area-inset-inline-start: 0px;
|
|
264
|
+
--safe-area-inset-inline-end: 0px;
|
|
187
265
|
}
|
|
188
266
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
:is(#detail, #detail-placeholder, #outgoing) {
|
|
195
|
-
background: Canvas !important;
|
|
196
|
-
}
|
|
267
|
+
:host([dir='rtl'][has-detail][overlay][overlay-containment='page']) :is(#detail, #detailOutgoing, #backdrop) {
|
|
268
|
+
padding-right: 0;
|
|
269
|
+
padding-left: env(safe-area-inset-left);
|
|
197
270
|
}
|
|
198
271
|
|
|
199
|
-
/*
|
|
272
|
+
/* Transitions */
|
|
273
|
+
|
|
200
274
|
@media (prefers-reduced-motion: no-preference) {
|
|
201
275
|
:host(:not([no-animation], [transition='replace'])) {
|
|
202
276
|
--_transition-duration: 200ms;
|
|
@@ -206,4 +280,16 @@ export const masterDetailLayoutStyles = css`
|
|
|
206
280
|
--_transition-duration: 300ms;
|
|
207
281
|
}
|
|
208
282
|
}
|
|
283
|
+
|
|
284
|
+
/* Forced colors */
|
|
285
|
+
|
|
286
|
+
@media (forced-colors: active) {
|
|
287
|
+
:host([has-detail][overlay]) :is(#detail, #detailOutgoing) {
|
|
288
|
+
outline: 3px solid !important;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
:is(#detail, #detailPlaceholder, #detailOutgoing) {
|
|
292
|
+
background: Canvas !important;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
209
295
|
`;
|
|
@@ -59,12 +59,13 @@ export interface MasterDetailLayoutEventMap extends HTMLElementEventMap, MasterD
|
|
|
59
59
|
*
|
|
60
60
|
* Attribute | Description
|
|
61
61
|
* --------------------------|----------------------
|
|
62
|
-
* `expand`
|
|
62
|
+
* `expand-master` | Set when the master area expands to fill available space.
|
|
63
|
+
* `expand-detail` | Set when the detail area expands to fill available space.
|
|
63
64
|
* `orientation` | Set to `horizontal` or `vertical` depending on the orientation.
|
|
64
65
|
* `has-detail` | Set when the detail content is provided and visible.
|
|
65
66
|
* `has-detail-placeholder` | Set when the detail placeholder content is provided.
|
|
66
67
|
* `overlay` | Set when columns don't fit and the detail is shown as an overlay.
|
|
67
|
-
* `overlay-containment` | Set to `layout` or `
|
|
68
|
+
* `overlay-containment` | Set to `layout` or `page`.
|
|
68
69
|
*
|
|
69
70
|
* The following custom CSS properties are available for styling:
|
|
70
71
|
*
|
|
@@ -128,19 +129,30 @@ declare class MasterDetailLayout extends ThemableMixin(ElementMixin(HTMLElement)
|
|
|
128
129
|
/**
|
|
129
130
|
* Defines the containment of the detail area when the layout is in
|
|
130
131
|
* overlay mode. When set to `layout`, the overlay is confined to the
|
|
131
|
-
* layout. When set to `
|
|
132
|
+
* layout. When set to `page`, the overlay is confined to the
|
|
132
133
|
* browser's viewport. Defaults to `layout`.
|
|
133
134
|
*
|
|
134
135
|
* @attr {string} overlay-containment
|
|
135
136
|
*/
|
|
136
|
-
overlayContainment: 'layout' | '
|
|
137
|
+
overlayContainment: 'layout' | 'page';
|
|
137
138
|
|
|
138
139
|
/**
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
140
|
+
* When true, the master area grows to fill the available space.
|
|
141
|
+
* If `expandDetail` is also true, both areas share the available
|
|
142
|
+
* space equally.
|
|
143
|
+
*
|
|
144
|
+
* @attr {boolean} expand-master
|
|
145
|
+
*/
|
|
146
|
+
expandMaster: boolean;
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* When true, the detail area grows to fill the available space.
|
|
150
|
+
* If `expandMaster` is also true, both areas share the available
|
|
151
|
+
* space equally.
|
|
152
|
+
*
|
|
153
|
+
* @attr {boolean} expand-detail
|
|
142
154
|
*/
|
|
143
|
-
|
|
155
|
+
expandDetail: boolean;
|
|
144
156
|
|
|
145
157
|
/**
|
|
146
158
|
* When true, the layout does not use animated transitions for the detail area.
|
|
@@ -149,6 +161,15 @@ declare class MasterDetailLayout extends ThemableMixin(ElementMixin(HTMLElement)
|
|
|
149
161
|
*/
|
|
150
162
|
noAnimation: boolean;
|
|
151
163
|
|
|
164
|
+
/**
|
|
165
|
+
* When true, the layout forces the detail area to be shown as an overlay,
|
|
166
|
+
* even if there is enough space for master and detail to be shown next to
|
|
167
|
+
* each other using the default (split) mode.
|
|
168
|
+
*
|
|
169
|
+
* @attr {boolean} force-overlay
|
|
170
|
+
*/
|
|
171
|
+
forceOverlay: boolean;
|
|
172
|
+
|
|
152
173
|
/**
|
|
153
174
|
* When `detailSize` is not explicitly set, re-measures the cached intrinsic size of
|
|
154
175
|
* the detail content by placing it in a min-content CSS grid column, then repeats
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
|
|
5
5
|
*/
|
|
6
6
|
import { html, LitElement, nothing } from 'lit';
|
|
7
|
-
import { getFocusableElements } from '@vaadin/a11y-base/src/focus-utils.js';
|
|
7
|
+
import { getFocusableElements, isKeyboardActive } from '@vaadin/a11y-base/src/focus-utils.js';
|
|
8
8
|
import { defineCustomElement } from '@vaadin/component-base/src/define.js';
|
|
9
9
|
import { getClosestElement } from '@vaadin/component-base/src/dom-utils.js';
|
|
10
10
|
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
|
|
@@ -65,12 +65,13 @@ import {
|
|
|
65
65
|
*
|
|
66
66
|
* Attribute | Description
|
|
67
67
|
* --------------------------|----------------------
|
|
68
|
-
* `expand`
|
|
68
|
+
* `expand-master` | Set when the master area expands to fill available space.
|
|
69
|
+
* `expand-detail` | Set when the detail area expands to fill available space.
|
|
69
70
|
* `orientation` | Set to `horizontal` or `vertical` depending on the orientation.
|
|
70
71
|
* `has-detail` | Set when the detail content is provided and visible.
|
|
71
72
|
* `has-detail-placeholder` | Set when the detail placeholder content is provided.
|
|
72
73
|
* `overlay` | Set when columns don't fit and the detail is shown as an overlay.
|
|
73
|
-
* `overlay-containment` | Set to `layout` or `
|
|
74
|
+
* `overlay-containment` | Set to `layout` or `page`.
|
|
74
75
|
*
|
|
75
76
|
* The following custom CSS properties are available for styling:
|
|
76
77
|
*
|
|
@@ -120,7 +121,6 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
|
|
|
120
121
|
detailSize: {
|
|
121
122
|
type: String,
|
|
122
123
|
sync: true,
|
|
123
|
-
observer: '__detailSizeChanged',
|
|
124
124
|
},
|
|
125
125
|
|
|
126
126
|
/**
|
|
@@ -134,7 +134,6 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
|
|
|
134
134
|
masterSize: {
|
|
135
135
|
type: String,
|
|
136
136
|
sync: true,
|
|
137
|
-
observer: '__masterSizeChanged',
|
|
138
137
|
},
|
|
139
138
|
|
|
140
139
|
/**
|
|
@@ -161,13 +160,12 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
|
|
|
161
160
|
value: 'horizontal',
|
|
162
161
|
reflectToAttribute: true,
|
|
163
162
|
sync: true,
|
|
164
|
-
observer: '__orientationChanged',
|
|
165
163
|
},
|
|
166
164
|
|
|
167
165
|
/**
|
|
168
166
|
* Defines the containment of the detail area when the layout is in
|
|
169
167
|
* overlay mode. When set to `layout`, the overlay is confined to the
|
|
170
|
-
* layout. When set to `
|
|
168
|
+
* layout. When set to `page`, the overlay is confined to the
|
|
171
169
|
* browser's viewport. Defaults to `layout`.
|
|
172
170
|
*
|
|
173
171
|
* @attr {string} overlay-containment
|
|
@@ -180,13 +178,29 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
|
|
|
180
178
|
},
|
|
181
179
|
|
|
182
180
|
/**
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
*
|
|
181
|
+
* When true, the master area grows to fill the available space.
|
|
182
|
+
* If `expandDetail` is also true, both areas share the available
|
|
183
|
+
* space equally.
|
|
184
|
+
*
|
|
185
|
+
* @attr {boolean} expand-master
|
|
186
186
|
*/
|
|
187
|
-
|
|
188
|
-
type:
|
|
189
|
-
value:
|
|
187
|
+
expandMaster: {
|
|
188
|
+
type: Boolean,
|
|
189
|
+
value: false,
|
|
190
|
+
reflectToAttribute: true,
|
|
191
|
+
sync: true,
|
|
192
|
+
},
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* When true, the detail area grows to fill the available space.
|
|
196
|
+
* If `expandMaster` is also true, both areas share the available
|
|
197
|
+
* space equally.
|
|
198
|
+
*
|
|
199
|
+
* @attr {boolean} expand-detail
|
|
200
|
+
*/
|
|
201
|
+
expandDetail: {
|
|
202
|
+
type: Boolean,
|
|
203
|
+
value: false,
|
|
190
204
|
reflectToAttribute: true,
|
|
191
205
|
sync: true,
|
|
192
206
|
},
|
|
@@ -202,9 +216,17 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
|
|
|
202
216
|
reflectToAttribute: true,
|
|
203
217
|
},
|
|
204
218
|
|
|
205
|
-
/**
|
|
206
|
-
|
|
219
|
+
/**
|
|
220
|
+
* When true, the layout forces the detail area to be shown as an overlay,
|
|
221
|
+
* even if there is enough space for master and detail to be shown next to
|
|
222
|
+
* each other using the default (split) mode.
|
|
223
|
+
*
|
|
224
|
+
* @attr {boolean} force-overlay
|
|
225
|
+
*/
|
|
226
|
+
forceOverlay: {
|
|
207
227
|
type: Boolean,
|
|
228
|
+
value: false,
|
|
229
|
+
reflectToAttribute: true,
|
|
208
230
|
sync: true,
|
|
209
231
|
},
|
|
210
232
|
|
|
@@ -224,27 +246,27 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
|
|
|
224
246
|
/** @protected */
|
|
225
247
|
render() {
|
|
226
248
|
const isOverlay = this.hasAttribute('has-detail') && this.hasAttribute('overlay');
|
|
227
|
-
const
|
|
228
|
-
const isLayoutContained = isOverlay && !
|
|
249
|
+
const isPage = isOverlay && this.overlayContainment === 'page';
|
|
250
|
+
const isLayoutContained = isOverlay && !isPage;
|
|
229
251
|
|
|
230
252
|
return html`
|
|
231
253
|
<div id="backdrop" part="backdrop" @click="${this.__onBackdropClick}"></div>
|
|
232
254
|
<div id="master" part="master" ?inert="${isLayoutContained}">
|
|
233
255
|
<slot @slotchange="${this.__onSlotChange}"></slot>
|
|
234
256
|
</div>
|
|
235
|
-
<div id="
|
|
257
|
+
<div id="detailOutgoing" inert>
|
|
236
258
|
<slot name="detail-outgoing"></slot>
|
|
237
259
|
</div>
|
|
238
260
|
<div
|
|
239
261
|
id="detail"
|
|
240
262
|
part="detail"
|
|
241
263
|
role="${isOverlay ? 'dialog' : nothing}"
|
|
242
|
-
aria-modal="${
|
|
264
|
+
aria-modal="${isPage ? 'true' : nothing}"
|
|
243
265
|
@keydown="${this.__onDetailKeydown}"
|
|
244
266
|
>
|
|
245
267
|
<slot name="detail" @slotchange="${this.__onSlotChange}"></slot>
|
|
246
268
|
</div>
|
|
247
|
-
<div id="
|
|
269
|
+
<div id="detailPlaceholder" part="detail-placeholder">
|
|
248
270
|
<slot name="detail-placeholder" @slotchange="${this.__onSlotChange}"></slot>
|
|
249
271
|
</div>
|
|
250
272
|
`;
|
|
@@ -254,6 +276,17 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
|
|
|
254
276
|
connectedCallback() {
|
|
255
277
|
super.connectedCallback();
|
|
256
278
|
this.__initResizeObserver();
|
|
279
|
+
|
|
280
|
+
const ancestorLayouts = this.__ancestorLayouts;
|
|
281
|
+
if (ancestorLayouts.length > 0) {
|
|
282
|
+
ancestorLayouts.forEach((layout) => {
|
|
283
|
+
cancelAnimationFrame(layout.__initialRaf);
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
this.__initialRaf = requestAnimationFrame(() => {
|
|
287
|
+
this.recalculateLayout();
|
|
288
|
+
});
|
|
289
|
+
}
|
|
257
290
|
}
|
|
258
291
|
|
|
259
292
|
/** @protected */
|
|
@@ -261,51 +294,40 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
|
|
|
261
294
|
super.disconnectedCallback();
|
|
262
295
|
this.__resizeObserver.disconnect();
|
|
263
296
|
cancelAnimationFrame(this.__resizeRaf);
|
|
297
|
+
cancelAnimationFrame(this.__initialRaf);
|
|
264
298
|
cancelAnimations(this);
|
|
265
299
|
}
|
|
266
300
|
|
|
267
|
-
/** @
|
|
268
|
-
|
|
269
|
-
|
|
301
|
+
/** @protected */
|
|
302
|
+
updated(props) {
|
|
303
|
+
super.updated(props);
|
|
270
304
|
|
|
271
|
-
if (
|
|
272
|
-
this.
|
|
305
|
+
if (props.has('masterSize')) {
|
|
306
|
+
this.style.setProperty('--_master-size', this.masterSize);
|
|
273
307
|
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
/** @private */
|
|
277
|
-
__detailSizeChanged(size, oldSize) {
|
|
278
|
-
this.__updateStyleProperty('detail-size', size, oldSize);
|
|
279
308
|
|
|
280
|
-
if (
|
|
281
|
-
this.
|
|
309
|
+
if (props.has('detailSize')) {
|
|
310
|
+
this.style.setProperty('--_detail-size', this.detailSize);
|
|
282
311
|
}
|
|
283
|
-
}
|
|
284
312
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
313
|
+
if (
|
|
314
|
+
(props.has('masterSize') && props.get('masterSize') != null) ||
|
|
315
|
+
(props.has('detailSize') && props.get('detailSize') != null) ||
|
|
316
|
+
(props.has('orientation') && props.get('orientation') != null) ||
|
|
317
|
+
(props.has('forceOverlay') && props.get('forceOverlay') != null)
|
|
318
|
+
) {
|
|
288
319
|
this.recalculateLayout();
|
|
289
320
|
}
|
|
290
321
|
}
|
|
291
322
|
|
|
292
323
|
/** @private */
|
|
293
|
-
__overlaySizeChanged(size
|
|
294
|
-
this.
|
|
324
|
+
__overlaySizeChanged(size) {
|
|
325
|
+
this.style.setProperty('--_overlay-size', size);
|
|
295
326
|
}
|
|
296
327
|
|
|
297
328
|
/** @private */
|
|
298
|
-
__detailCachedSizeChanged(size
|
|
299
|
-
this.
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
/** @private */
|
|
303
|
-
__updateStyleProperty(prop, size, oldSize) {
|
|
304
|
-
if (size) {
|
|
305
|
-
this.style.setProperty(`--_${prop}`, size);
|
|
306
|
-
} else if (oldSize) {
|
|
307
|
-
this.style.removeProperty(`--_${prop}`);
|
|
308
|
-
}
|
|
329
|
+
__detailCachedSizeChanged(size) {
|
|
330
|
+
this.style.setProperty('--_detail-cached-size', size);
|
|
309
331
|
}
|
|
310
332
|
|
|
311
333
|
/** @private */
|
|
@@ -318,9 +340,10 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
|
|
|
318
340
|
this.__resizeObserver = this.__resizeObserver || new ResizeObserver(() => this.__onResize());
|
|
319
341
|
this.__resizeObserver.disconnect();
|
|
320
342
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
343
|
+
[this, this.$.master, this.$.detail, this.__slottedMaster, this.__slottedDetail].forEach((node) => {
|
|
344
|
+
if (node) {
|
|
345
|
+
this.__resizeObserver.observe(node);
|
|
346
|
+
}
|
|
324
347
|
});
|
|
325
348
|
}
|
|
326
349
|
|
|
@@ -344,12 +367,14 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
|
|
|
344
367
|
__readLayoutState() {
|
|
345
368
|
const isVertical = this.orientation === 'vertical';
|
|
346
369
|
|
|
347
|
-
const
|
|
348
|
-
const
|
|
370
|
+
const slottedMaster = this.__slottedMaster;
|
|
371
|
+
const slottedDetail = this.__slottedDetail;
|
|
372
|
+
const slottedDetailPlaceholder = this.__slottedDetailPlaceholder;
|
|
349
373
|
|
|
374
|
+
const hasMaster = !!slottedMaster;
|
|
350
375
|
const hadDetail = this.hasAttribute('has-detail');
|
|
351
|
-
const hasDetail =
|
|
352
|
-
const hasDetailPlaceholder = !!
|
|
376
|
+
const hasDetail = slottedDetail != null && slottedDetail.checkVisibility();
|
|
377
|
+
const hasDetailPlaceholder = !!slottedDetailPlaceholder;
|
|
353
378
|
|
|
354
379
|
const computedStyle = getComputedStyle(this);
|
|
355
380
|
const hostSizeProp = isVertical ? 'height' : 'width';
|
|
@@ -358,10 +383,12 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
|
|
|
358
383
|
const trackSizesProp = isVertical ? 'gridTemplateRows' : 'gridTemplateColumns';
|
|
359
384
|
const trackSizes = parseTrackSizes(computedStyle[trackSizesProp]);
|
|
360
385
|
|
|
361
|
-
const hasOverflow =
|
|
362
|
-
|
|
386
|
+
const hasOverflow =
|
|
387
|
+
(hasDetail || hasDetailPlaceholder) && (this.forceOverlay || detectOverflow(hostSize, trackSizes));
|
|
388
|
+
const focusTarget = !hadDetail && hasDetail && hasOverflow ? getFocusableElements(slottedDetail)[0] : null;
|
|
363
389
|
|
|
364
390
|
return {
|
|
391
|
+
hasMaster,
|
|
365
392
|
hadDetail,
|
|
366
393
|
hasDetail,
|
|
367
394
|
hasDetailPlaceholder,
|
|
@@ -376,7 +403,7 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
|
|
|
376
403
|
* Applies layout state to DOM attributes. Pure writes, no reads.
|
|
377
404
|
* @private
|
|
378
405
|
*/
|
|
379
|
-
__writeLayoutState({ hadDetail, hasDetail, hasDetailPlaceholder, hasOverflow, focusTarget, trackSizes }) {
|
|
406
|
+
__writeLayoutState({ hasMaster, hadDetail, hasDetail, hasDetailPlaceholder, hasOverflow, focusTarget, trackSizes }) {
|
|
380
407
|
const [_masterSize, _masterExtra, detailSize] = trackSizes;
|
|
381
408
|
|
|
382
409
|
// If no detailSize is explicitily set, cache the intrinsic size (min-content) of
|
|
@@ -398,6 +425,7 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
|
|
|
398
425
|
}
|
|
399
426
|
|
|
400
427
|
this.toggleAttribute('overlay', hasOverflow);
|
|
428
|
+
this.toggleAttribute('has-master', hasMaster);
|
|
401
429
|
this.toggleAttribute('has-detail', hasDetail);
|
|
402
430
|
this.toggleAttribute('has-detail-placeholder', hasDetailPlaceholder);
|
|
403
431
|
|
|
@@ -406,7 +434,7 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
|
|
|
406
434
|
this.requestUpdate();
|
|
407
435
|
|
|
408
436
|
if (focusTarget) {
|
|
409
|
-
focusTarget.focus({ preventScroll: true });
|
|
437
|
+
focusTarget.focus({ preventScroll: true, focusVisible: isKeyboardActive() });
|
|
410
438
|
}
|
|
411
439
|
}
|
|
412
440
|
|
|
@@ -424,14 +452,14 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
|
|
|
424
452
|
* synchronous DOM reads and writes.
|
|
425
453
|
*/
|
|
426
454
|
recalculateLayout() {
|
|
427
|
-
|
|
428
|
-
// overriding the layout state with stale measurements.
|
|
429
|
-
cancelAnimationFrame(this.__resizeRaf);
|
|
430
|
-
|
|
431
|
-
const invalidatedLayouts = [...this.__ancestorLayouts.filter((layout) => layout.__isDetailAutoSized), this];
|
|
455
|
+
const invalidatedLayouts = [...this.__ancestorLayouts, this];
|
|
432
456
|
|
|
433
457
|
// Write
|
|
434
458
|
invalidatedLayouts.forEach((layout) => {
|
|
459
|
+
// Cancel any pending ResizeObserver rAF to prevent it from potentially
|
|
460
|
+
// overriding the layout state with stale measurements.
|
|
461
|
+
cancelAnimationFrame(layout.__resizeRaf);
|
|
462
|
+
|
|
435
463
|
layout.__detailCachedSize = null;
|
|
436
464
|
|
|
437
465
|
if (layout.__isDetailAutoSized) {
|
|
@@ -496,7 +524,7 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
|
|
|
496
524
|
*/
|
|
497
525
|
async _setDetail(newDetail, skipTransition) {
|
|
498
526
|
// Don't start a transition if detail didn't change
|
|
499
|
-
const oldDetail = this.
|
|
527
|
+
const oldDetail = this.__slottedDetail;
|
|
500
528
|
if (oldDetail === (newDetail || null)) {
|
|
501
529
|
return;
|
|
502
530
|
}
|
|
@@ -522,7 +550,7 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
|
|
|
522
550
|
return;
|
|
523
551
|
}
|
|
524
552
|
|
|
525
|
-
const hasPlaceholder = !!this.
|
|
553
|
+
const hasPlaceholder = !!this.__slottedDetailPlaceholder;
|
|
526
554
|
if ((oldDetail && newDetail) || (hasPlaceholder && !this.hasAttribute('overlay'))) {
|
|
527
555
|
await this._startTransition('replace', updateSlot);
|
|
528
556
|
} else if (!oldDetail && newDetail) {
|
|
@@ -594,18 +622,27 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
|
|
|
594
622
|
|
|
595
623
|
/** @private */
|
|
596
624
|
async __replaceTransition(updateSlot) {
|
|
625
|
+
const oldDetail = this.__slottedDetail;
|
|
626
|
+
if (oldDetail) {
|
|
627
|
+
oldDetail.slot = 'detail-outgoing';
|
|
628
|
+
}
|
|
629
|
+
|
|
597
630
|
try {
|
|
598
|
-
this.
|
|
631
|
+
this.$.detailOutgoing.style.width = this.__detailCachedSize;
|
|
599
632
|
|
|
600
633
|
await updateSlot();
|
|
601
634
|
|
|
602
635
|
const progress = getCurrentAnimationProgress(this.$.detail);
|
|
603
636
|
await Promise.all([
|
|
604
637
|
animateIn(this.$.detail, ['fade', 'slide'], progress),
|
|
605
|
-
animateOut(this.$.
|
|
638
|
+
animateOut(this.$.detailOutgoing, ['fade', 'slide'], progress),
|
|
606
639
|
]);
|
|
607
640
|
} finally {
|
|
608
|
-
|
|
641
|
+
// Skip removal if the slot was reassigned during the transition.
|
|
642
|
+
// The React component does this to let React handle the removal.
|
|
643
|
+
if (oldDetail && oldDetail.slot === 'detail-outgoing') {
|
|
644
|
+
oldDetail.remove();
|
|
645
|
+
}
|
|
609
646
|
}
|
|
610
647
|
}
|
|
611
648
|
|
|
@@ -620,30 +657,19 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
|
|
|
620
657
|
await updateSlot();
|
|
621
658
|
}
|
|
622
659
|
|
|
623
|
-
/**
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
* light DOM so light DOM styles continue to apply.
|
|
627
|
-
* @private
|
|
628
|
-
*/
|
|
629
|
-
__snapshotOutgoing() {
|
|
630
|
-
const currentDetail = this.querySelector('[slot="detail"]');
|
|
631
|
-
if (!currentDetail) {
|
|
632
|
-
return;
|
|
633
|
-
}
|
|
634
|
-
currentDetail.setAttribute('slot', 'detail-outgoing');
|
|
635
|
-
this.$.outgoing.style.width = this.__detailCachedSize;
|
|
636
|
-
this.__replacing = true;
|
|
660
|
+
/** @private */
|
|
661
|
+
get __slottedMaster() {
|
|
662
|
+
return this.querySelector(':scope > :is([slot=""], :not([slot]))');
|
|
637
663
|
}
|
|
638
664
|
|
|
639
|
-
/**
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
this.
|
|
665
|
+
/** @private */
|
|
666
|
+
get __slottedDetail() {
|
|
667
|
+
return this.querySelector(':scope > [slot="detail"]');
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
/** @private */
|
|
671
|
+
get __slottedDetailPlaceholder() {
|
|
672
|
+
return this.querySelector(':scope > [slot="detail-placeholder"]');
|
|
647
673
|
}
|
|
648
674
|
|
|
649
675
|
/**
|
package/web-types.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/web-types",
|
|
3
3
|
"name": "@vaadin/master-detail-layout",
|
|
4
|
-
"version": "25.2.0-
|
|
4
|
+
"version": "25.2.0-alpha8",
|
|
5
5
|
"description-markup": "markdown",
|
|
6
6
|
"contributions": {
|
|
7
7
|
"html": {
|
|
8
8
|
"elements": [
|
|
9
9
|
{
|
|
10
10
|
"name": "vaadin-master-detail-layout",
|
|
11
|
-
"description": "`<vaadin-master-detail-layout>` is a web component for building UIs with a master\n(or primary) area and a detail (or secondary) area that is displayed next to, or\noverlaid on top of, the master area, depending on configuration and viewport size.\n\n### Slots\n\nThe component has two main content areas: the master area (default slot)\nand the detail area (`detail` slot). When the detail doesn't fit next to\nthe master, it is shown as an overlay on top of the master area:\n\n```html\n<vaadin-master-detail-layout>\n <div>Master content</div>\n <div slot=\"detail\">Detail content</div>\n</vaadin-master-detail-layout>\n```\n\nThe component also supports a `detail-placeholder` slot for content shown\nin the detail area when no detail is selected. Unlike the `detail` slot,\nthe placeholder is simply hidden when it doesn't fit next to the master area,\nrather than shown as an overlay:\n\n```html\n<vaadin-master-detail-layout>\n <div>Master content</div>\n <div slot=\"detail-placeholder\">Select an item</div>\n</vaadin-master-detail-layout>\n```\n\n### Styling\n\nThe following shadow DOM parts are available for styling:\n\nPart name | Description\n----------------------|----------------------\n`backdrop` | Backdrop covering the master area in the overlay mode\n`master` | The master area\n`detail` | The detail area\n`detail-placeholder` | The detail placeholder area\n\nThe following state attributes are available for styling:\n\nAttribute | Description\n--------------------------|----------------------\n`expand`
|
|
11
|
+
"description": "`<vaadin-master-detail-layout>` is a web component for building UIs with a master\n(or primary) area and a detail (or secondary) area that is displayed next to, or\noverlaid on top of, the master area, depending on configuration and viewport size.\n\n### Slots\n\nThe component has two main content areas: the master area (default slot)\nand the detail area (`detail` slot). When the detail doesn't fit next to\nthe master, it is shown as an overlay on top of the master area:\n\n```html\n<vaadin-master-detail-layout>\n <div>Master content</div>\n <div slot=\"detail\">Detail content</div>\n</vaadin-master-detail-layout>\n```\n\nThe component also supports a `detail-placeholder` slot for content shown\nin the detail area when no detail is selected. Unlike the `detail` slot,\nthe placeholder is simply hidden when it doesn't fit next to the master area,\nrather than shown as an overlay:\n\n```html\n<vaadin-master-detail-layout>\n <div>Master content</div>\n <div slot=\"detail-placeholder\">Select an item</div>\n</vaadin-master-detail-layout>\n```\n\n### Styling\n\nThe following shadow DOM parts are available for styling:\n\nPart name | Description\n----------------------|----------------------\n`backdrop` | Backdrop covering the master area in the overlay mode\n`master` | The master area\n`detail` | The detail area\n`detail-placeholder` | The detail placeholder area\n\nThe following state attributes are available for styling:\n\nAttribute | Description\n--------------------------|----------------------\n`expand-master` | Set when the master area expands to fill available space.\n`expand-detail` | Set when the detail area expands to fill available space.\n`orientation` | Set to `horizontal` or `vertical` depending on the orientation.\n`has-detail` | Set when the detail content is provided and visible.\n`has-detail-placeholder` | Set when the detail placeholder content is provided.\n`overlay` | Set when columns don't fit and the detail is shown as an overlay.\n`overlay-containment` | Set to `layout` or `page`.\n\nThe following custom CSS properties are available for styling:\n\nCustom CSS property |\n:----------------------------------------------------|\n| `--vaadin-master-detail-layout-border-color` |\n| `--vaadin-master-detail-layout-border-width` |\n| `--vaadin-master-detail-layout-detail-background` |\n| `--vaadin-master-detail-layout-detail-shadow` |\n| `--vaadin-overlay-backdrop-background` |\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.",
|
|
12
12
|
"attributes": [
|
|
13
13
|
{
|
|
14
14
|
"name": "detail-size",
|
|
@@ -22,11 +22,33 @@
|
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
24
|
{
|
|
25
|
-
"name": "expand",
|
|
26
|
-
"description": "
|
|
25
|
+
"name": "expand-detail",
|
|
26
|
+
"description": "When true, the detail area grows to fill the available space.\nIf `expandMaster` is also true, both areas share the available\nspace equally.",
|
|
27
27
|
"value": {
|
|
28
28
|
"type": [
|
|
29
|
-
"
|
|
29
|
+
"boolean",
|
|
30
|
+
"null",
|
|
31
|
+
"undefined"
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"name": "expand-master",
|
|
37
|
+
"description": "When true, the master area grows to fill the available space.\nIf `expandDetail` is also true, both areas share the available\nspace equally.",
|
|
38
|
+
"value": {
|
|
39
|
+
"type": [
|
|
40
|
+
"boolean",
|
|
41
|
+
"null",
|
|
42
|
+
"undefined"
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"name": "force-overlay",
|
|
48
|
+
"description": "When true, the layout forces the detail area to be shown as an overlay,\neven if there is enough space for master and detail to be shown next to\neach other using the default (split) mode.",
|
|
49
|
+
"value": {
|
|
50
|
+
"type": [
|
|
51
|
+
"boolean",
|
|
30
52
|
"null",
|
|
31
53
|
"undefined"
|
|
32
54
|
]
|
|
@@ -67,7 +89,7 @@
|
|
|
67
89
|
},
|
|
68
90
|
{
|
|
69
91
|
"name": "overlay-containment",
|
|
70
|
-
"description": "Defines the containment of the detail area when the layout is in\noverlay mode. When set to `layout`, the overlay is confined to the\nlayout. When set to `
|
|
92
|
+
"description": "Defines the containment of the detail area when the layout is in\noverlay mode. When set to `layout`, the overlay is confined to the\nlayout. When set to `page`, the overlay is confined to the\nbrowser's viewport. Defaults to `layout`.",
|
|
71
93
|
"value": {
|
|
72
94
|
"type": [
|
|
73
95
|
"string",
|
|
@@ -113,11 +135,33 @@
|
|
|
113
135
|
}
|
|
114
136
|
},
|
|
115
137
|
{
|
|
116
|
-
"name": "
|
|
117
|
-
"description": "
|
|
138
|
+
"name": "expandDetail",
|
|
139
|
+
"description": "When true, the detail area grows to fill the available space.\nIf `expandMaster` is also true, both areas share the available\nspace equally.",
|
|
118
140
|
"value": {
|
|
119
141
|
"type": [
|
|
120
|
-
"
|
|
142
|
+
"boolean",
|
|
143
|
+
"null",
|
|
144
|
+
"undefined"
|
|
145
|
+
]
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"name": "expandMaster",
|
|
150
|
+
"description": "When true, the master area grows to fill the available space.\nIf `expandDetail` is also true, both areas share the available\nspace equally.",
|
|
151
|
+
"value": {
|
|
152
|
+
"type": [
|
|
153
|
+
"boolean",
|
|
154
|
+
"null",
|
|
155
|
+
"undefined"
|
|
156
|
+
]
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"name": "forceOverlay",
|
|
161
|
+
"description": "When true, the layout forces the detail area to be shown as an overlay,\neven if there is enough space for master and detail to be shown next to\neach other using the default (split) mode.",
|
|
162
|
+
"value": {
|
|
163
|
+
"type": [
|
|
164
|
+
"boolean",
|
|
121
165
|
"null",
|
|
122
166
|
"undefined"
|
|
123
167
|
]
|
|
@@ -158,7 +202,7 @@
|
|
|
158
202
|
},
|
|
159
203
|
{
|
|
160
204
|
"name": "overlayContainment",
|
|
161
|
-
"description": "Defines the containment of the detail area when the layout is in\noverlay mode. When set to `layout`, the overlay is confined to the\nlayout. When set to `
|
|
205
|
+
"description": "Defines the containment of the detail area when the layout is in\noverlay mode. When set to `layout`, the overlay is confined to the\nlayout. When set to `page`, the overlay is confined to the\nbrowser's viewport. Defaults to `layout`.",
|
|
162
206
|
"value": {
|
|
163
207
|
"type": [
|
|
164
208
|
"string",
|
package/web-types.lit.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/web-types",
|
|
3
3
|
"name": "@vaadin/master-detail-layout",
|
|
4
|
-
"version": "25.2.0-
|
|
4
|
+
"version": "25.2.0-alpha8",
|
|
5
5
|
"description-markup": "markdown",
|
|
6
6
|
"framework": "lit",
|
|
7
7
|
"framework-config": {
|
|
@@ -16,26 +16,40 @@
|
|
|
16
16
|
"elements": [
|
|
17
17
|
{
|
|
18
18
|
"name": "vaadin-master-detail-layout",
|
|
19
|
-
"description": "`<vaadin-master-detail-layout>` is a web component for building UIs with a master\n(or primary) area and a detail (or secondary) area that is displayed next to, or\noverlaid on top of, the master area, depending on configuration and viewport size.\n\n### Slots\n\nThe component has two main content areas: the master area (default slot)\nand the detail area (`detail` slot). When the detail doesn't fit next to\nthe master, it is shown as an overlay on top of the master area:\n\n```html\n<vaadin-master-detail-layout>\n <div>Master content</div>\n <div slot=\"detail\">Detail content</div>\n</vaadin-master-detail-layout>\n```\n\nThe component also supports a `detail-placeholder` slot for content shown\nin the detail area when no detail is selected. Unlike the `detail` slot,\nthe placeholder is simply hidden when it doesn't fit next to the master area,\nrather than shown as an overlay:\n\n```html\n<vaadin-master-detail-layout>\n <div>Master content</div>\n <div slot=\"detail-placeholder\">Select an item</div>\n</vaadin-master-detail-layout>\n```\n\n### Styling\n\nThe following shadow DOM parts are available for styling:\n\nPart name | Description\n----------------------|----------------------\n`backdrop` | Backdrop covering the master area in the overlay mode\n`master` | The master area\n`detail` | The detail area\n`detail-placeholder` | The detail placeholder area\n\nThe following state attributes are available for styling:\n\nAttribute | Description\n--------------------------|----------------------\n`expand`
|
|
19
|
+
"description": "`<vaadin-master-detail-layout>` is a web component for building UIs with a master\n(or primary) area and a detail (or secondary) area that is displayed next to, or\noverlaid on top of, the master area, depending on configuration and viewport size.\n\n### Slots\n\nThe component has two main content areas: the master area (default slot)\nand the detail area (`detail` slot). When the detail doesn't fit next to\nthe master, it is shown as an overlay on top of the master area:\n\n```html\n<vaadin-master-detail-layout>\n <div>Master content</div>\n <div slot=\"detail\">Detail content</div>\n</vaadin-master-detail-layout>\n```\n\nThe component also supports a `detail-placeholder` slot for content shown\nin the detail area when no detail is selected. Unlike the `detail` slot,\nthe placeholder is simply hidden when it doesn't fit next to the master area,\nrather than shown as an overlay:\n\n```html\n<vaadin-master-detail-layout>\n <div>Master content</div>\n <div slot=\"detail-placeholder\">Select an item</div>\n</vaadin-master-detail-layout>\n```\n\n### Styling\n\nThe following shadow DOM parts are available for styling:\n\nPart name | Description\n----------------------|----------------------\n`backdrop` | Backdrop covering the master area in the overlay mode\n`master` | The master area\n`detail` | The detail area\n`detail-placeholder` | The detail placeholder area\n\nThe following state attributes are available for styling:\n\nAttribute | Description\n--------------------------|----------------------\n`expand-master` | Set when the master area expands to fill available space.\n`expand-detail` | Set when the detail area expands to fill available space.\n`orientation` | Set to `horizontal` or `vertical` depending on the orientation.\n`has-detail` | Set when the detail content is provided and visible.\n`has-detail-placeholder` | Set when the detail placeholder content is provided.\n`overlay` | Set when columns don't fit and the detail is shown as an overlay.\n`overlay-containment` | Set to `layout` or `page`.\n\nThe following custom CSS properties are available for styling:\n\nCustom CSS property |\n:----------------------------------------------------|\n| `--vaadin-master-detail-layout-border-color` |\n| `--vaadin-master-detail-layout-border-width` |\n| `--vaadin-master-detail-layout-detail-background` |\n| `--vaadin-master-detail-layout-detail-shadow` |\n| `--vaadin-overlay-backdrop-background` |\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.",
|
|
20
20
|
"extension": true,
|
|
21
21
|
"attributes": [
|
|
22
22
|
{
|
|
23
|
-
"name": "?
|
|
24
|
-
"description": "When true, the
|
|
23
|
+
"name": "?expandDetail",
|
|
24
|
+
"description": "When true, the detail area grows to fill the available space.\nIf `expandMaster` is also true, both areas share the available\nspace equally.",
|
|
25
25
|
"value": {
|
|
26
26
|
"kind": "expression"
|
|
27
27
|
}
|
|
28
28
|
},
|
|
29
29
|
{
|
|
30
|
-
"name": "
|
|
31
|
-
"description": "
|
|
30
|
+
"name": "?expandMaster",
|
|
31
|
+
"description": "When true, the master area grows to fill the available space.\nIf `expandDetail` is also true, both areas share the available\nspace equally.",
|
|
32
|
+
"value": {
|
|
33
|
+
"kind": "expression"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"name": "?forceOverlay",
|
|
38
|
+
"description": "When true, the layout forces the detail area to be shown as an overlay,\neven if there is enough space for master and detail to be shown next to\neach other using the default (split) mode.",
|
|
32
39
|
"value": {
|
|
33
40
|
"kind": "expression"
|
|
34
41
|
}
|
|
35
42
|
},
|
|
36
43
|
{
|
|
37
|
-
"name": "
|
|
38
|
-
"description": "
|
|
44
|
+
"name": "?noAnimation",
|
|
45
|
+
"description": "When true, the layout does not use animated transitions for the detail area.",
|
|
46
|
+
"value": {
|
|
47
|
+
"kind": "expression"
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"name": ".detailSize",
|
|
52
|
+
"description": "Size (in CSS length units) to be set on the detail area in\nthe CSS grid layout. When there is not enough space to show\nmaster and detail areas next to each other, the detail area\nis shown as an overlay.\n<p>\nIf not specified, the size is determined automatically by measuring\nthe detail content in a `min-content` CSS grid column when it first\nbecomes visible, and then caching the resulting intrinsic size. To\nrecalculate the cached intrinsic size, use the `recalculateLayout`\nmethod.",
|
|
39
53
|
"value": {
|
|
40
54
|
"kind": "expression"
|
|
41
55
|
}
|
|
@@ -56,7 +70,7 @@
|
|
|
56
70
|
},
|
|
57
71
|
{
|
|
58
72
|
"name": ".overlayContainment",
|
|
59
|
-
"description": "Defines the containment of the detail area when the layout is in\noverlay mode. When set to `layout`, the overlay is confined to the\nlayout. When set to `
|
|
73
|
+
"description": "Defines the containment of the detail area when the layout is in\noverlay mode. When set to `layout`, the overlay is confined to the\nlayout. When set to `page`, the overlay is confined to the\nbrowser's viewport. Defaults to `layout`.",
|
|
60
74
|
"value": {
|
|
61
75
|
"kind": "expression"
|
|
62
76
|
}
|