@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.
@@ -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` | Set to `master`, `detail`, or `both`.\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 `viewport`.\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.",
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": "expand",
261
+ "name": "expandDetail",
262
262
  "privacy": "public",
263
263
  "type": {
264
- "text": "string"
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": "Controls which column(s) expand to fill available space.\nPossible values: `'master'`, `'detail'`, `'both'`.\nDefaults to `'master'`.",
267
- "attribute": "expand"
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 `viewport`, the overlay is confined to the\nbrowser's viewport. Defaults to `layout`.",
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": "string"
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": "Controls which column(s) expand to fill available space.\nPossible values: `'master'`, `'detail'`, `'both'`.\nDefaults to `'master'`.",
356
- "fieldName": "expand"
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 `viewport`, the overlay is confined to the\nbrowser's viewport. Defaults to `layout`.",
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-alpha6",
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-alpha6",
38
- "@vaadin/component-base": "25.2.0-alpha6",
39
- "@vaadin/vaadin-themable-mixin": "25.2.0-alpha6",
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-alpha6",
44
- "@vaadin/chai-plugins": "25.2.0-alpha6",
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-alpha6",
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": "30f23c65765f27616f2db292406d5759a7e987c3"
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([hidden]),
36
- ::slotted([hidden]) {
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
- grid-template-columns: 100%;
48
- grid-template-rows:
49
- [master-start] var(--_master-size) var(--_master-extra)
50
- [detail-start] var(--_detail-size) var(--_detail-extra)
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
- :is(#master, #detail, #detail-placeholder, #outgoing) {
55
- box-sizing: border-box;
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
- #detail-placeholder {
59
- z-index: 1;
60
- opacity: 0;
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([has-detail-placeholder]:not([has-detail], [overlay])) #detail-placeholder {
65
- opacity: 1;
66
- pointer-events: auto;
73
+ :host([orientation='vertical']) {
74
+ grid-template-columns: 100%;
75
+ grid-template-rows: var(--_grid-template);
67
76
  }
68
77
 
69
- #master {
70
- grid-column: master-start / detail-start;
71
- grid-row: 1;
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
- :is(#detail, #detail-placeholder, #outgoing) {
75
- grid-column: detail-start / detail-end;
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: master-start / detail-start;
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, #detail-placeholder, #outgoing) {
113
+ :host([orientation='vertical']) :is(#detail, #detailPlaceholder, #detailOutgoing) {
85
114
  grid-column: 1;
86
- grid-row: detail-start / detail-end;
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
- :host([expand='both']),
102
- :host([expand='master']) {
103
- --_master-extra: 1fr;
104
- }
148
+ /* Master base styles */
105
149
 
106
- :host([expand='both']:is([has-detail], [has-detail-placeholder])),
107
- :host([expand='detail']:is([has-detail], [has-detail-placeholder])) {
108
- --_detail-extra: 1fr;
150
+ #master {
151
+ opacity: 0;
152
+ pointer-events: none;
153
+ box-sizing: border-box;
109
154
  }
110
155
 
111
- :host([recalculating-detail-size]:is([has-detail], [has-detail-placeholder])) {
112
- --_detail-extra: 0px;
156
+ :host([has-master]) #master {
157
+ opacity: 1;
158
+ pointer-events: auto;
113
159
  }
114
160
 
115
- :host([keep-detail-column-offscreen]),
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
- :host([orientation='horizontal']) #detail-placeholder,
122
- :host([orientation='horizontal']:not([overlay])) #detail {
123
- border-inline-start: var(--vaadin-master-detail-layout-border-width, 1px) solid
124
- var(--vaadin-master-detail-layout-border-color, var(--vaadin-border-color-secondary));
163
+ #detail {
164
+ translate: var(--_transition-offset);
165
+ opacity: 0;
166
+ z-index: 4;
125
167
  }
126
168
 
127
- :host([orientation='vertical']) #detail-placeholder,
128
- :host([orientation='vertical']:not([overlay])) #detail {
129
- border-top: var(--vaadin-master-detail-layout-border-width, 1px) solid
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
- #outgoing {
174
+ #detailOutgoing {
134
175
  position: absolute;
135
176
  z-index: 3;
177
+ display: none;
136
178
  }
137
179
 
138
- /* Detail transition: off-screen by default, on-screen when has-detail */
139
- #detail {
140
- translate: var(--_transition-offset);
180
+ :host([transition='replace']) #detailOutgoing {
181
+ display: block;
182
+ }
183
+
184
+ #detailPlaceholder {
185
+ z-index: 1;
141
186
  opacity: 0;
142
- z-index: 4;
187
+ pointer-events: none;
143
188
  }
144
189
 
145
- :host([has-detail]) #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([overlay]) {
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]) :is(#detail, #outgoing) {
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]) #backdrop {
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, #outgoing) {
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='viewport']) :is(#detail, #outgoing, #backdrop) {
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
- @media (forced-colors: active) {
190
- :host([has-detail][overlay]) :is(#detail, #outgoing) {
191
- outline: 3px solid !important;
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
- /* Enable transitions when motion is allowed */
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` | Set to `master`, `detail`, or `both`.
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 `viewport`.
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 `viewport`, the overlay is confined to the
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' | 'viewport';
137
+ overlayContainment: 'layout' | 'page';
137
138
 
138
139
  /**
139
- * Controls which column(s) expand to fill available space.
140
- * Possible values: `'master'`, `'detail'`, `'both'`.
141
- * Defaults to `'master'`.
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
- expand: 'master' | 'detail' | 'both';
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` | Set to `master`, `detail`, or `both`.
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 `viewport`.
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 `viewport`, the overlay is confined to the
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
- * Controls which column(s) expand to fill available space.
184
- * Possible values: `'master'`, `'detail'`, `'both'`.
185
- * Defaults to `'master'`.
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
- expand: {
188
- type: String,
189
- value: 'master',
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
- /** @private */
206
- __replacing: {
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 isViewport = isOverlay && this.overlayContainment === 'viewport';
228
- const isLayoutContained = isOverlay && !isViewport;
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="outgoing" inert ?hidden="${!this.__replacing}">
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="${isViewport ? 'true' : nothing}"
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="detail-placeholder" part="detail-placeholder">
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
- /** @private */
268
- __masterSizeChanged(size, oldSize) {
269
- this.__updateStyleProperty('master-size', size, oldSize);
301
+ /** @protected */
302
+ updated(props) {
303
+ super.updated(props);
270
304
 
271
- if (oldSize != null) {
272
- this.recalculateLayout();
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 (oldSize != null) {
281
- this.recalculateLayout();
309
+ if (props.has('detailSize')) {
310
+ this.style.setProperty('--_detail-size', this.detailSize);
282
311
  }
283
- }
284
312
 
285
- /** @private */
286
- __orientationChanged(_orientation, oldOrientation) {
287
- if (oldOrientation != null) {
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, oldSize) {
294
- this.__updateStyleProperty('overlay-size', size, oldSize);
324
+ __overlaySizeChanged(size) {
325
+ this.style.setProperty('--_overlay-size', size);
295
326
  }
296
327
 
297
328
  /** @private */
298
- __detailCachedSizeChanged(size, oldSize) {
299
- this.__updateStyleProperty('detail-cached-size', size, oldSize);
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
- const children = this.querySelectorAll(':scope > [slot="detail"], :scope >:not([slot])');
322
- [this, this.$.master, this.$.detail, ...children].forEach((node) => {
323
- this.__resizeObserver.observe(node);
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 detailContent = this.querySelector(':scope > [slot="detail"]');
348
- const detailPlaceholder = this.querySelector(':scope > [slot="detail-placeholder"]');
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 = detailContent != null && detailContent.checkVisibility();
352
- const hasDetailPlaceholder = !!detailPlaceholder;
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 = (hasDetail || hasDetailPlaceholder) && detectOverflow(hostSize, trackSizes);
362
- const focusTarget = !hadDetail && hasDetail && hasOverflow ? getFocusableElements(detailContent)[0] : null;
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
- // Cancel any pending ResizeObserver rAF to prevent it from potentially
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.querySelector('[slot="detail"]');
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.querySelector('[slot="detail-placeholder"]');
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.__snapshotOutgoing();
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.$.outgoing, ['fade', 'slide'], progress),
638
+ animateOut(this.$.detailOutgoing, ['fade', 'slide'], progress),
606
639
  ]);
607
640
  } finally {
608
- this.__clearOutgoing();
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
- * Moves the current detail content to the outgoing slot so it can
625
- * slide out while the new content slides in. Keeps the element in
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
- * Clears the outgoing container after the replace transition completes.
641
- * @private
642
- */
643
- __clearOutgoing() {
644
- this.querySelectorAll('[slot="detail-outgoing"]').forEach((el) => el.remove());
645
- this.$.outgoing.style.width = '';
646
- this.__replacing = false;
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-alpha6",
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` | Set to `master`, `detail`, or `both`.\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 `viewport`.\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.",
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": "Controls which column(s) expand to fill available space.\nPossible values: `'master'`, `'detail'`, `'both'`.\nDefaults to `'master'`.",
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
- "string",
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 `viewport`, the overlay is confined to the\nbrowser's viewport. Defaults to `layout`.",
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": "expand",
117
- "description": "Controls which column(s) expand to fill available space.\nPossible values: `'master'`, `'detail'`, `'both'`.\nDefaults to `'master'`.",
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
- "string",
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 `viewport`, the overlay is confined to the\nbrowser's viewport. Defaults to `layout`.",
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",
@@ -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-alpha6",
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` | Set to `master`, `detail`, or `both`.\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 `viewport`.\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.",
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": "?noAnimation",
24
- "description": "When true, the layout does not use animated transitions for the detail area.",
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": ".detailSize",
31
- "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.",
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": ".expand",
38
- "description": "Controls which column(s) expand to fill available space.\nPossible values: `'master'`, `'detail'`, `'both'`.\nDefaults to `'master'`.",
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 `viewport`, the overlay is confined to the\nbrowser's viewport. Defaults to `layout`.",
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
  }