@vaadin/master-detail-layout 25.0.0-alpha2 → 25.0.0-alpha21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaadin/master-detail-layout",
3
- "version": "25.0.0-alpha2",
3
+ "version": "25.0.0-alpha21",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -21,7 +21,6 @@
21
21
  "type": "module",
22
22
  "files": [
23
23
  "src",
24
- "theme",
25
24
  "vaadin-*.d.ts",
26
25
  "vaadin-*.js",
27
26
  "web-types.json",
@@ -34,20 +33,20 @@
34
33
  "web-component"
35
34
  ],
36
35
  "dependencies": {
37
- "@vaadin/a11y-base": "25.0.0-alpha2",
38
- "@vaadin/component-base": "25.0.0-alpha2",
39
- "@vaadin/vaadin-lumo-styles": "25.0.0-alpha2",
40
- "@vaadin/vaadin-themable-mixin": "25.0.0-alpha2",
36
+ "@vaadin/a11y-base": "25.0.0-alpha21",
37
+ "@vaadin/component-base": "25.0.0-alpha21",
38
+ "@vaadin/vaadin-themable-mixin": "25.0.0-alpha21",
41
39
  "lit": "^3.0.0"
42
40
  },
43
41
  "devDependencies": {
44
- "@vaadin/chai-plugins": "25.0.0-alpha2",
42
+ "@vaadin/chai-plugins": "25.0.0-alpha21",
45
43
  "@vaadin/testing-helpers": "^2.0.0",
46
- "sinon": "^18.0.0"
44
+ "@vaadin/vaadin-lumo-styles": "25.0.0-alpha21",
45
+ "sinon": "^21.0.0"
47
46
  },
48
47
  "web-types": [
49
48
  "web-types.json",
50
49
  "web-types.lit.json"
51
50
  ],
52
- "gitHead": "67ffcd5355cf21ce1b5039c598525109fc4c164b"
51
+ "gitHead": "8fb9e9710c01449edf623a1aaac4655cdc11a933"
53
52
  }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2025 - 2025 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+ import type { CSSResult } from 'lit';
7
+
8
+ export const masterDetailLayoutStyles: CSSResult;
@@ -0,0 +1,170 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2025 - 2025 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+ import '@vaadin/component-base/src/styles/style-props.js';
7
+ import { css } from 'lit';
8
+
9
+ export const masterDetailLayoutStyles = css`
10
+ /* Layout and positioning styles */
11
+
12
+ :host {
13
+ display: flex;
14
+ box-sizing: border-box;
15
+ height: 100%;
16
+ max-width: 100%;
17
+ max-height: 100%;
18
+ position: relative; /* Keep the positioning context stable across all modes */
19
+ overflow: hidden;
20
+ }
21
+
22
+ :host([hidden]) {
23
+ display: none !important;
24
+ }
25
+
26
+ :host([orientation='vertical']) {
27
+ flex-direction: column;
28
+ }
29
+
30
+ [part='_detail-internal'] {
31
+ display: contents;
32
+ justify-content: end;
33
+ }
34
+
35
+ :host([orientation='vertical']) [part='_detail-internal'] {
36
+ align-items: end;
37
+ }
38
+
39
+ :host(:is([drawer], [stack])) [part='_detail-internal'],
40
+ :host(:is([drawer], [stack])[has-detail]) [part='backdrop'] {
41
+ display: flex;
42
+ position: absolute;
43
+ z-index: 1;
44
+ inset: 0;
45
+ overscroll-behavior: contain;
46
+ }
47
+
48
+ :host(:not([has-detail])) [part='_detail-internal'],
49
+ [part='backdrop'] {
50
+ display: none;
51
+ }
52
+
53
+ :host([orientation='horizontal'][drawer]) [part='detail'] {
54
+ margin-inline-start: 50px;
55
+ }
56
+
57
+ :host([orientation='vertical'][drawer]) [part='detail'] {
58
+ margin-top: 50px;
59
+ }
60
+
61
+ :host(:is([drawer], [stack])[containment='viewport']) :is([part='_detail-internal'], [part='backdrop']) {
62
+ position: fixed;
63
+ }
64
+
65
+ /* Sizing styles */
66
+
67
+ [part] {
68
+ box-sizing: border-box;
69
+ max-width: 100%;
70
+ max-height: 100%;
71
+ }
72
+
73
+ /* No fixed size */
74
+ :host(:not([has-master-size])) [part='master'],
75
+ :host(:not([has-detail-size]):not([drawer], [stack])) [part='detail'] {
76
+ flex-grow: 1;
77
+ flex-basis: 50%;
78
+ }
79
+
80
+ /* Fixed size */
81
+ :host([has-master-size]) [part='master'],
82
+ :host([has-detail-size]) [part='detail'] {
83
+ flex-shrink: 0;
84
+ }
85
+
86
+ :host([orientation='horizontal'][has-master-size][has-detail]) [part='master'] {
87
+ width: var(--_master-size);
88
+ }
89
+
90
+ :host([orientation='vertical'][has-master-size][has-detail]) [part='master'] {
91
+ height: var(--_master-size);
92
+ }
93
+
94
+ :host([orientation='horizontal'][has-detail-size]:not([stack])) [part='detail'] {
95
+ width: var(--_detail-size);
96
+ }
97
+
98
+ :host([orientation='vertical'][has-detail-size]:not([stack])) [part='detail'] {
99
+ height: var(--_detail-size);
100
+ }
101
+
102
+ :host([has-master-size][has-detail-size]) [part='master'] {
103
+ flex-grow: 1;
104
+ flex-basis: var(--_master-size);
105
+ }
106
+
107
+ :host([has-master-size][has-detail-size]:not([drawer], [stack])) [part='detail'] {
108
+ flex-grow: 1;
109
+ flex-basis: var(--_detail-size);
110
+ }
111
+
112
+ /* Min size */
113
+ :host([orientation='horizontal'][has-master-min-size]) [part='master'] {
114
+ min-width: min(100%, var(--_master-min-size));
115
+ }
116
+
117
+ :host([orientation='vertical'][has-master-min-size]) [part='master'] {
118
+ min-height: min(100%, var(--_master-min-size));
119
+ }
120
+
121
+ :host([orientation='horizontal'][has-detail-min-size]) [part='detail'] {
122
+ min-width: min(100%, var(--_detail-min-size));
123
+ }
124
+
125
+ :host([orientation='vertical'][has-detail-min-size]) [part='detail'] {
126
+ min-height: min(100%, var(--_detail-min-size));
127
+ }
128
+
129
+ :host([drawer]) [part='master'],
130
+ :host([stack]) [part] {
131
+ width: 100% !important;
132
+ height: 100% !important;
133
+ min-width: auto !important;
134
+ min-height: auto !important;
135
+ max-width: 100% !important;
136
+ max-height: 100% !important;
137
+ }
138
+
139
+ /* Decorative/visual styles */
140
+
141
+ [part='backdrop'] {
142
+ background: var(--vaadin-overlay-backdrop-background, rgba(0, 0, 0, 0.2));
143
+ forced-color-adjust: none;
144
+ }
145
+
146
+ :host(:is([drawer], [stack])) [part='detail'] {
147
+ background: var(--vaadin-master-detail-layout-detail-background, var(--vaadin-background-color));
148
+ box-shadow: var(--vaadin-master-detail-layout-detail-shadow, 0 0 20px 0 rgba(0, 0, 0, 0.3));
149
+ }
150
+
151
+ :host([orientation='horizontal']:not([drawer], [stack])) [part='detail'] {
152
+ border-inline-start: var(--vaadin-master-detail-layout-border-width, 1px) solid
153
+ var(--vaadin-master-detail-layout-border-color, var(--vaadin-border-color-secondary));
154
+ }
155
+
156
+ :host([orientation='vertical']:not([drawer], [stack])) [part='detail'] {
157
+ border-top: var(--vaadin-master-detail-layout-border-width, 1px) solid
158
+ var(--vaadin-master-detail-layout-border-color, var(--vaadin-border-color-secondary));
159
+ }
160
+
161
+ @media (forced-colors: active) {
162
+ :host(:is([drawer], [stack])) [part='detail'] {
163
+ outline: 3px solid !important;
164
+ }
165
+
166
+ [part='detail'] {
167
+ background: Canvas !important;
168
+ }
169
+ }
170
+ `;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2025 - 2025 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+ import type { CSSResult } from 'lit';
7
+
8
+ export const masterDetailLayoutTransitionStyles: CSSResult;
@@ -0,0 +1,147 @@
1
+ /**
2
+ * @license
3
+ * Copyright (c) 2025 - 2025 Vaadin Ltd.
4
+ * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
+ */
6
+ import { css } from 'lit';
7
+
8
+ export const masterDetailLayoutTransitionStyles = css`
9
+ @media (prefers-reduced-motion: no-preference) {
10
+ html {
11
+ --_vaadin-mdl-dir-multiplier: 1;
12
+ --_vaadin-mdl-stack-master-offset: 20%;
13
+ --_vaadin-mdl-stack-master-clip-path: inset(0 0 0 var(--_vaadin-mdl-stack-master-offset));
14
+ --_vaadin-mdl-easing: cubic-bezier(0.78, 0, 0.22, 1);
15
+ }
16
+
17
+ html[dir='rtl'] {
18
+ --_vaadin-mdl-dir-multiplier: -1;
19
+ --_vaadin-mdl-stack-master-clip-path: inset(0 var(--_vaadin-mdl-stack-master-offset) 0 0);
20
+ }
21
+
22
+ ::view-transition-group(vaadin-mdl-backdrop),
23
+ ::view-transition-group(vaadin-mdl-master),
24
+ ::view-transition-group(vaadin-mdl-detail) {
25
+ animation-duration: 0.4s;
26
+ }
27
+
28
+ ::view-transition-group(vaadin-mdl-master),
29
+ ::view-transition-group(vaadin-mdl-detail) {
30
+ animation-timing-function: var(--_vaadin-mdl-easing);
31
+ }
32
+
33
+ ::view-transition-image-pair(vaadin-mdl-master),
34
+ ::view-transition-image-pair(vaadin-mdl-detail),
35
+ ::view-transition-new(vaadin-mdl-master),
36
+ ::view-transition-new(vaadin-mdl-detail),
37
+ ::view-transition-old(vaadin-mdl-master),
38
+ ::view-transition-old(vaadin-mdl-detail) {
39
+ animation-timing-function: inherit;
40
+ }
41
+
42
+ /* Needed to promote the backdrop on top the master during the transition */
43
+ vaadin-master-detail-layout[transition]::part(backdrop) {
44
+ view-transition-name: vaadin-mdl-backdrop;
45
+ }
46
+
47
+ vaadin-master-detail-layout[transition]:not([transition='replace']):not([drawer], [stack])::part(detail),
48
+ vaadin-master-detail-layout[transition]:is([drawer], [stack])::part(_detail-internal) {
49
+ view-transition-name: vaadin-mdl-detail;
50
+ }
51
+
52
+ ::view-transition-group(vaadin-mdl-detail) {
53
+ clip-path: inset(0);
54
+ }
55
+
56
+ ::view-transition-new(vaadin-mdl-detail),
57
+ ::view-transition-old(vaadin-mdl-detail) {
58
+ animation-name: vaadin-mdl-detail-slide-in;
59
+ }
60
+
61
+ ::view-transition-old(vaadin-mdl-detail) {
62
+ animation-direction: reverse;
63
+ }
64
+
65
+ @keyframes vaadin-mdl-detail-slide-in {
66
+ 0% {
67
+ translate: calc((100% + 30px) * var(--_vaadin-mdl-dir-multiplier));
68
+ }
69
+ }
70
+
71
+ vaadin-master-detail-layout[orientation='horizontal'][stack][has-detail]::part(master) {
72
+ translate: calc(var(--_vaadin-mdl-stack-master-offset) * var(--_vaadin-mdl-dir-multiplier) * -1);
73
+ opacity: 0;
74
+ }
75
+
76
+ vaadin-master-detail-layout[transition]::part(master) {
77
+ view-transition-name: vaadin-mdl-master;
78
+ }
79
+
80
+ vaadin-master-detail-layout[orientation='horizontal'][stack][transition='add']::part(master) {
81
+ view-transition-class: stack-add;
82
+ }
83
+
84
+ vaadin-master-detail-layout[orientation='horizontal'][stack][transition='remove']::part(master) {
85
+ view-transition-class: stack-remove;
86
+ }
87
+
88
+ ::view-transition-new(vaadin-mdl-master),
89
+ ::view-transition-old(vaadin-mdl-master) {
90
+ object-fit: none;
91
+ object-position: 0% 0;
92
+ width: 100%;
93
+ height: 100%;
94
+ }
95
+
96
+ :dir(rtl)::view-transition-new(vaadin-mdl-master),
97
+ :dir(rtl)::view-transition-old(vaadin-mdl-master) {
98
+ object-position: 100% 0;
99
+ }
100
+
101
+ ::view-transition-new(vaadin-mdl-master.stack-remove),
102
+ ::view-transition-old(vaadin-mdl-master.stack-remove) {
103
+ animation-name: vaadin-mdl-master-stack-remove;
104
+ clip-path: var(--_vaadin-mdl-stack-master-clip-path);
105
+ }
106
+
107
+ @keyframes vaadin-mdl-master-stack-remove {
108
+ 100% {
109
+ clip-path: inset(0);
110
+ }
111
+ }
112
+
113
+ ::view-transition-new(vaadin-mdl-master.stack-add),
114
+ ::view-transition-old(vaadin-mdl-master.stack-add) {
115
+ animation-name: vaadin-mdl-master-stack-add;
116
+ clip-path: inset(0);
117
+ }
118
+
119
+ @keyframes vaadin-mdl-master-stack-add {
120
+ 100% {
121
+ clip-path: var(--_vaadin-mdl-stack-master-clip-path);
122
+ }
123
+ }
124
+
125
+ /* prettier-ignore */
126
+ vaadin-master-detail-layout[orientation='vertical']:not([drawer], [stack])[transition]:not([transition='replace'])::part(detail),
127
+ vaadin-master-detail-layout[orientation='vertical']:is([drawer], [stack])[transition]::part(_detail-internal) {
128
+ view-transition-name: vaadin-mdl-detail;
129
+ view-transition-class: vertical;
130
+ }
131
+
132
+ ::view-transition-new(vaadin-mdl-detail.vertical),
133
+ ::view-transition-old(vaadin-mdl-detail.vertical) {
134
+ animation-name: vaadin-mdl-vertical-detail-slide-in;
135
+ }
136
+
137
+ ::view-transition-old(vaadin-mdl-detail.vertical) {
138
+ animation-direction: reverse;
139
+ }
140
+
141
+ @keyframes vaadin-mdl-vertical-detail-slide-in {
142
+ 0% {
143
+ transform: translateY(calc(100% + 30px));
144
+ }
145
+ }
146
+ }
147
+ `;
@@ -23,13 +23,6 @@ export interface MasterDetailLayoutEventMap extends HTMLElementEventMap, MasterD
23
23
  *
24
24
  * ### Styling
25
25
  *
26
- * The following custom CSS property are available for styling (needed to be set
27
- * on the `<html>` element since they are used by the global view transitions):
28
- *
29
- * Custom CSS property | Description | Default
30
- * -----------------------------------------------------|---------------------|--------
31
- * `--vaadin-master-detail-layout-transition-duration` | Transition duration | 300ms
32
- *
33
26
  * The following shadow DOM parts are available for styling:
34
27
  *
35
28
  * Part name | Description
@@ -3,7 +3,7 @@
3
3
  * Copyright (c) 2025 - 2025 Vaadin Ltd.
4
4
  * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
5
  */
6
- import { css, html, LitElement, nothing } from 'lit';
6
+ import { html, LitElement, nothing } from 'lit';
7
7
  import { getFocusableElements } from '@vaadin/a11y-base/src/focus-utils.js';
8
8
  import { defineCustomElement } from '@vaadin/component-base/src/define.js';
9
9
  import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
@@ -11,7 +11,8 @@ import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
11
11
  import { ResizeMixin } from '@vaadin/component-base/src/resize-mixin.js';
12
12
  import { SlotStylesMixin } from '@vaadin/component-base/src/slot-styles-mixin.js';
13
13
  import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
14
- import { transitionStyles } from './vaadin-master-detail-layout-transition-styles.js';
14
+ import { masterDetailLayoutStyles } from './styles/vaadin-master-detail-layout-base-styles.js';
15
+ import { masterDetailLayoutTransitionStyles } from './styles/vaadin-master-detail-layout-transition-base-styles.js';
15
16
 
16
17
  /**
17
18
  * `<vaadin-master-detail-layout>` is a web component for building UIs with a master
@@ -20,13 +21,6 @@ import { transitionStyles } from './vaadin-master-detail-layout-transition-style
20
21
  *
21
22
  * ### Styling
22
23
  *
23
- * The following custom CSS properties are available for styling (needed to be set
24
- * on the `<html>` element since they are used by the global view transitions):
25
- *
26
- * Custom CSS property | Description | Default
27
- * -----------------------------------------------------|---------------------|--------
28
- * `--vaadin-master-detail-layout-transition-duration` | Transition duration | 300ms
29
- *
30
24
  * The following shadow DOM parts are available for styling:
31
25
  *
32
26
  * Part name | Description
@@ -63,154 +57,7 @@ class MasterDetailLayout extends SlotStylesMixin(ResizeMixin(ElementMixin(Themab
63
57
  }
64
58
 
65
59
  static get styles() {
66
- return css`
67
- :host {
68
- display: flex;
69
- box-sizing: border-box;
70
- height: 100%;
71
- }
72
-
73
- :host([hidden]) {
74
- display: none !important;
75
- }
76
-
77
- :host(:not([has-detail])) [part='detail'],
78
- [part='backdrop'] {
79
- display: none;
80
- }
81
-
82
- :host([orientation='horizontal']) [part='master'] {
83
- max-width: 100%;
84
- }
85
-
86
- /* Drawer mode */
87
- :host(:is([drawer], [stack])) {
88
- position: relative;
89
- }
90
-
91
- :host(:is([drawer], [stack])[containment='layout']) [part='detail'],
92
- :host([drawer][containment='layout']) [part='backdrop'] {
93
- position: absolute;
94
- }
95
-
96
- :host(:is([drawer], [stack])[containment='viewport']) [part='detail'],
97
- :host([drawer][containment='viewport']) [part='backdrop'] {
98
- position: fixed;
99
- }
100
-
101
- :host([drawer][has-detail]) [part='backdrop'] {
102
- display: block;
103
- inset: 0;
104
- z-index: 1;
105
- }
106
-
107
- :host(:is([drawer], [stack])) [part='detail'] {
108
- z-index: 1;
109
- }
110
-
111
- :host([drawer][orientation='horizontal']) [part='detail'] {
112
- inset-inline-end: 0;
113
- height: 100%;
114
- width: var(--_detail-min-size, min-content);
115
- max-width: 100%;
116
- }
117
-
118
- :host([drawer][orientation='horizontal'][containment='viewport']) [part='detail'] {
119
- inset-block-start: 0;
120
- }
121
-
122
- /* No fixed size */
123
- :host(:not([has-master-size])) [part='master'],
124
- :host(:not([has-detail-size])) [part='detail'] {
125
- flex-grow: 1;
126
- flex-basis: 50%;
127
- }
128
-
129
- /* Fixed size */
130
- :host([has-master-size]) [part='master'],
131
- :host([has-detail-size]) [part='detail'] {
132
- flex-shrink: 0;
133
- }
134
-
135
- :host([has-master-size][orientation='horizontal']) [part='master'] {
136
- width: var(--_master-size);
137
- }
138
-
139
- :host([has-detail-size][orientation='horizontal']:not([stack])) [part='detail'] {
140
- width: var(--_detail-size);
141
- }
142
-
143
- :host([has-master-size][has-detail-size]) [part='master'] {
144
- flex-grow: 1;
145
- flex-basis: var(--_master-size);
146
- }
147
-
148
- :host([has-master-size][has-detail-size]) [part='detail'] {
149
- flex-grow: 1;
150
- flex-basis: var(--_detail-size);
151
- }
152
-
153
- /* Min size */
154
- :host([has-master-min-size][has-detail][orientation='horizontal']:not([drawer]):not([stack])) [part='master'] {
155
- min-width: var(--_master-min-size);
156
- }
157
-
158
- :host([has-detail-min-size][orientation='horizontal']:not([drawer]):not([stack])) [part='detail'] {
159
- min-width: var(--_detail-min-size);
160
- }
161
-
162
- :host([has-master-min-size]) [part='master'],
163
- :host([has-detail-min-size]) [part='detail'] {
164
- flex-shrink: 0;
165
- }
166
-
167
- /* Vertical */
168
- :host([orientation='vertical']) {
169
- flex-direction: column;
170
- }
171
-
172
- :host([orientation='vertical'][drawer]) [part='master'] {
173
- max-height: 100%;
174
- }
175
-
176
- :host([orientation='vertical'][drawer]) [part='detail'] {
177
- inset-block-end: 0;
178
- width: 100%;
179
- height: var(--_detail-min-size, min-content);
180
- }
181
-
182
- :host([drawer][orientation='vertical'][containment='viewport']) [part='detail'] {
183
- inset-inline-start: 0;
184
- }
185
-
186
- /* Fixed size */
187
- :host([has-master-size][orientation='vertical']) [part='master'] {
188
- height: var(--_master-size);
189
- }
190
-
191
- :host([has-detail-size][orientation='vertical']:not([stack])) [part='detail'] {
192
- height: var(--_detail-size);
193
- }
194
-
195
- /* Min size */
196
- :host([has-master-min-size][orientation='vertical']:not([drawer])) [part='master'],
197
- :host([has-master-min-size][orientation='vertical'][drawer]) {
198
- min-height: var(--_master-min-size);
199
- }
200
-
201
- :host([has-detail-min-size][orientation='vertical']:not([drawer]):not([stack])) [part='detail'] {
202
- min-height: var(--_detail-min-size);
203
- }
204
-
205
- /* Stack mode */
206
- :host([stack]) [part='master'] {
207
- max-height: 100%;
208
- }
209
-
210
- :host([stack]) [part='detail'] {
211
- inset: 0;
212
- }
213
- `;
60
+ return masterDetailLayoutStyles;
214
61
  }
215
62
 
216
63
  static get properties() {
@@ -388,24 +235,30 @@ class MasterDetailLayout extends SlotStylesMixin(ResizeMixin(ElementMixin(Themab
388
235
 
389
236
  /** @override */
390
237
  get slotStyles() {
391
- return [transitionStyles];
238
+ return [masterDetailLayoutTransitionStyles];
392
239
  }
393
240
 
394
241
  /** @protected */
395
242
  render() {
396
243
  return html`
397
- <div part="backdrop" @click="${this.__onBackdropClick}"></div>
398
- <div id="master" part="master" ?inert="${this._hasDetail && this._drawer && this.containment === 'layout'}">
399
- <slot></slot>
400
- </div>
244
+ <div part="backdrop"></div>
401
245
  <div
402
- id="detail"
403
- part="detail"
404
- role="${this._drawer || this._stack ? 'dialog' : nothing}"
405
- aria-modal="${this._drawer && this.containment === 'viewport' ? 'true' : nothing}"
406
- @keydown="${this.__onDetailKeydown}"
246
+ id="master"
247
+ part="master"
248
+ ?inert="${this._hasDetail && (this._stack || (this._drawer && this.containment === 'layout'))}"
407
249
  >
408
- <slot name="detail" @slotchange="${this.__onDetailSlotChange}"></slot>
250
+ <slot></slot>
251
+ </div>
252
+ <div part="_detail-internal" @click="${this.__onDetailClick}">
253
+ <div
254
+ id="detail"
255
+ part="detail"
256
+ role="${this._drawer || this._stack ? 'dialog' : nothing}"
257
+ aria-modal="${this._drawer && this.containment === 'viewport' ? 'true' : nothing}"
258
+ @keydown="${this.__onDetailKeydown}"
259
+ >
260
+ <slot name="detail" @slotchange="${this.__onDetailSlotChange}"></slot>
261
+ </div>
409
262
  </div>
410
263
  `;
411
264
  }
@@ -428,15 +281,19 @@ class MasterDetailLayout extends SlotStylesMixin(ResizeMixin(ElementMixin(Themab
428
281
  }
429
282
 
430
283
  /** @private */
431
- __onBackdropClick() {
432
- this.dispatchEvent(new CustomEvent('backdrop-click'));
284
+ __onDetailClick(e) {
285
+ // The detail wrapper element fully covers the backdrop part, so listen
286
+ // to click event on it and detect if it was outside the detail content
287
+ if (!e.composedPath().includes(this.$.detail)) {
288
+ this.dispatchEvent(new CustomEvent('backdrop-click'));
289
+ }
433
290
  }
434
291
 
435
292
  /** @private */
436
293
  __onDetailKeydown(event) {
437
- if (event.key === 'Escape') {
294
+ if (event.key === 'Escape' && !event.defaultPrevented) {
438
295
  // Prevent firing on parent layout when using nested layouts
439
- event.stopPropagation();
296
+ event.preventDefault();
440
297
  this.dispatchEvent(new CustomEvent('detail-escape-press'));
441
298
  }
442
299
  }
@@ -666,8 +523,10 @@ class MasterDetailLayout extends SlotStylesMixin(ResizeMixin(ElementMixin(Themab
666
523
  * @protected
667
524
  */
668
525
  async _finishTransition() {
669
- // Detect new layout mode after DOM has been updated
670
- this.__detectLayoutMode();
526
+ // Detect new layout mode after DOM has been updated.
527
+ // The detection is wrapped in queueMicroTask in order to allow custom Lit elements to render before measurement.
528
+ // https://github.com/vaadin/web-components/issues/8969
529
+ queueMicrotask(() => this.__detectLayoutMode());
671
530
 
672
531
  if (!this.__transition) {
673
532
  return Promise.resolve();
@@ -1,3 +1,3 @@
1
- import './theme/lumo/vaadin-master-detail-layout.js';
1
+ import './src/vaadin-master-detail-layout.js';
2
2
 
3
3
  export * from './src/vaadin-master-detail-layout.js';
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.0.0-alpha2",
4
+ "version": "25.0.0-alpha21",
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### Styling\n\nThe following custom CSS properties are available for styling (needed to be set\non the `<html>` element since they are used by the global view transitions):\n\nCustom CSS property | Description | Default\n-----------------------------------------------------|---------------------|--------\n`--vaadin-master-detail-layout-transition-duration` | Transition duration | 300ms\n\nThe following shadow DOM parts are available for styling:\n\nPart name | Description\n---------------|----------------------\n`backdrop` | Backdrop covering the master area in the drawer mode\n`master` | The master area\n`detail` | The detail area\n\nThe following state attributes are available for styling:\n\nAttribute | Description\n---------------| -----------\n`containment` | Set to `layout` or `viewport` depending on the containment.\n`orientation` | Set to `horizontal` or `vertical` depending on the orientation.\n`has-detail` | Set when the detail content is provided.\n`drawer` | Set when the layout is using the drawer mode.\n`stack` | Set when the layout is using the stack mode.\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### 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 drawer mode\n`master` | The master area\n`detail` | The detail area\n\nThe following state attributes are available for styling:\n\nAttribute | Description\n---------------| -----------\n`containment` | Set to `layout` or `viewport` depending on the containment.\n`orientation` | Set to `horizontal` or `vertical` depending on the orientation.\n`has-detail` | Set when the detail content is provided.\n`drawer` | Set when the layout is using the drawer mode.\n`stack` | Set when the layout is using the stack mode.\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.",
12
12
  "attributes": [
13
13
  {
14
14
  "name": "detail-size",
@@ -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.0.0-alpha2",
4
+ "version": "25.0.0-alpha21",
5
5
  "description-markup": "markdown",
6
6
  "framework": "lit",
7
7
  "framework-config": {
@@ -16,7 +16,7 @@
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### Styling\n\nThe following custom CSS properties are available for styling (needed to be set\non the `<html>` element since they are used by the global view transitions):\n\nCustom CSS property | Description | Default\n-----------------------------------------------------|---------------------|--------\n`--vaadin-master-detail-layout-transition-duration` | Transition duration | 300ms\n\nThe following shadow DOM parts are available for styling:\n\nPart name | Description\n---------------|----------------------\n`backdrop` | Backdrop covering the master area in the drawer mode\n`master` | The master area\n`detail` | The detail area\n\nThe following state attributes are available for styling:\n\nAttribute | Description\n---------------| -----------\n`containment` | Set to `layout` or `viewport` depending on the containment.\n`orientation` | Set to `horizontal` or `vertical` depending on the orientation.\n`has-detail` | Set when the detail content is provided.\n`drawer` | Set when the layout is using the drawer mode.\n`stack` | Set when the layout is using the stack mode.\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### 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 drawer mode\n`master` | The master area\n`detail` | The detail area\n\nThe following state attributes are available for styling:\n\nAttribute | Description\n---------------| -----------\n`containment` | Set to `layout` or `viewport` depending on the containment.\n`orientation` | Set to `horizontal` or `vertical` depending on the orientation.\n`has-detail` | Set when the detail content is provided.\n`drawer` | Set when the layout is using the drawer mode.\n`stack` | Set when the layout is using the stack mode.\n\nSee [Styling Components](https://vaadin.com/docs/latest/styling/styling-components) documentation.",
20
20
  "extension": true,
21
21
  "attributes": [
22
22
  {
@@ -1,289 +0,0 @@
1
- /**
2
- * @license
3
- * Copyright (c) 2025 - 2025 Vaadin Ltd.
4
- * This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5
- */
6
- import { css } from 'lit';
7
-
8
- export const transitionStyles = css`
9
- html:not([dir='rtl']) {
10
- --_vaadin-master-detail-layout-dir-multiplier: 1;
11
- }
12
-
13
- html[dir='rtl'] {
14
- --_vaadin-master-detail-layout-dir-multiplier: -1;
15
- }
16
-
17
- /* Default cross-fade animation */
18
- vaadin-master-detail-layout[transition] {
19
- view-transition-name: vaadin-master-detail-layout;
20
- }
21
-
22
- ::view-transition-group(vaadin-master-detail-layout) {
23
- animation-duration: var(--vaadin-master-detail-layout-transition-duration, 300ms);
24
- }
25
-
26
- /* Drawer - horizontal - add */
27
-
28
- vaadin-master-detail-layout[drawer][orientation='horizontal'][transition='add']::part(detail) {
29
- view-transition-name: vaadin-master-detail-layout-drawer-horizontal-detail-add;
30
- }
31
-
32
- ::view-transition-group(vaadin-master-detail-layout-drawer-horizontal-detail-add) {
33
- clip-path: inset(0);
34
- }
35
-
36
- ::view-transition-new(vaadin-master-detail-layout-drawer-horizontal-detail-add) {
37
- animation: var(--vaadin-master-detail-layout-transition-duration, 300ms) ease both
38
- vaadin-master-detail-layout-drawer-horizontal-detail-add;
39
- }
40
-
41
- @keyframes vaadin-master-detail-layout-drawer-horizontal-detail-add {
42
- from {
43
- transform: translateX(calc(100% * var(--_vaadin-master-detail-layout-dir-multiplier)));
44
- }
45
- }
46
-
47
- /* Drawer - horizontal - remove */
48
-
49
- vaadin-master-detail-layout[drawer][orientation='horizontal'][transition='remove']::part(detail) {
50
- view-transition-name: vaadin-master-detail-layout-drawer-horizontal-detail-remove;
51
- }
52
-
53
- ::view-transition-group(vaadin-master-detail-layout-drawer-horizontal-detail-remove) {
54
- clip-path: inset(0);
55
- }
56
-
57
- ::view-transition-old(vaadin-master-detail-layout-drawer-horizontal-detail-remove) {
58
- animation: var(--vaadin-master-detail-layout-transition-duration, 300ms) ease both
59
- vaadin-master-detail-layout-drawer-horizontal-detail-remove;
60
- }
61
-
62
- @keyframes vaadin-master-detail-layout-drawer-horizontal-detail-remove {
63
- to {
64
- transform: translateX(calc(100% * var(--_vaadin-master-detail-layout-dir-multiplier)));
65
- }
66
- }
67
-
68
- /* Stack - horizontal - add */
69
-
70
- vaadin-master-detail-layout[stack][orientation='horizontal'][transition='add'] {
71
- view-transition-name: vaadin-master-detail-layout-stack-horizontal-add;
72
- }
73
-
74
- ::view-transition-group(vaadin-master-detail-layout-stack-horizontal-add) {
75
- clip-path: inset(0);
76
- }
77
-
78
- ::view-transition-new(vaadin-master-detail-layout-stack-horizontal-add) {
79
- animation: var(--vaadin-master-detail-layout-transition-duration, 300ms) ease both
80
- vaadin-master-detail-layout-stack-horizontal-add-new;
81
- }
82
-
83
- ::view-transition-old(vaadin-master-detail-layout-stack-horizontal-add) {
84
- animation: var(--vaadin-master-detail-layout-transition-duration, 300ms) ease both
85
- vaadin-master-detail-layout-stack-horizontal-add-old;
86
- }
87
-
88
- @keyframes vaadin-master-detail-layout-stack-horizontal-add-new {
89
- from {
90
- transform: translateX(calc(100px * var(--_vaadin-master-detail-layout-dir-multiplier)));
91
- opacity: 0;
92
- }
93
- }
94
-
95
- @keyframes vaadin-master-detail-layout-stack-horizontal-add-old {
96
- to {
97
- transform: translateX(calc(-100px * var(--_vaadin-master-detail-layout-dir-multiplier)));
98
- opacity: 0;
99
- }
100
- }
101
-
102
- /* Stack - horizontal - remove */
103
-
104
- vaadin-master-detail-layout[stack][orientation='horizontal'][transition='remove'] {
105
- view-transition-name: vaadin-master-detail-layout-stack-horizontal-remove;
106
- }
107
-
108
- ::view-transition-group(vaadin-master-detail-layout-stack-horizontal-remove) {
109
- clip-path: inset(0);
110
- }
111
-
112
- ::view-transition-new(vaadin-master-detail-layout-stack-horizontal-remove) {
113
- animation: var(--vaadin-master-detail-layout-transition-duration, 300ms) ease both
114
- vaadin-master-detail-layout-stack-horizontal-remove-new;
115
- }
116
-
117
- ::view-transition-old(vaadin-master-detail-layout-stack-horizontal-remove) {
118
- animation: var(--vaadin-master-detail-layout-transition-duration, 300ms) ease both
119
- vaadin-master-detail-layout-stack-horizontal-remove-old;
120
- }
121
-
122
- @keyframes vaadin-master-detail-layout-stack-horizontal-remove-new {
123
- from {
124
- transform: translateX(calc(-100px * var(--_vaadin-master-detail-layout-dir-multiplier)));
125
- opacity: 0;
126
- }
127
- }
128
-
129
- @keyframes vaadin-master-detail-layout-stack-horizontal-remove-old {
130
- to {
131
- transform: translateX(calc(100px * var(--_vaadin-master-detail-layout-dir-multiplier)));
132
- opacity: 0;
133
- }
134
- }
135
-
136
- /* Stack - horizontal - viewport - add */
137
-
138
- vaadin-master-detail-layout[stack][orientation='horizontal'][containment='viewport'][transition='add'] {
139
- view-transition-name: vaadin-master-detail-layout-stack-horizontal-viewport-add;
140
- }
141
-
142
- ::view-transition-new(vaadin-master-detail-layout-stack-horizontal-viewport-add) {
143
- animation: var(--vaadin-master-detail-layout-transition-duration, 300ms) ease both
144
- vaadin-master-detail-layout-stack-horizontal-add-new;
145
- }
146
-
147
- /* Stack - horizontal - viewport - remove */
148
-
149
- vaadin-master-detail-layout[stack][orientation='horizontal'][containment='viewport'][transition='remove'] {
150
- view-transition-name: vaadin-master-detail-layout-stack-horizontal-viewport-remove;
151
- }
152
-
153
- ::view-transition-old(vaadin-master-detail-layout-stack-horizontal-viewport-remove) {
154
- animation: var(--vaadin-master-detail-layout-transition-duration, 300ms) ease both
155
- vaadin-master-detail-layout-stack-horizontal-remove-old;
156
- }
157
-
158
- /* Drawer - vertical - add */
159
-
160
- vaadin-master-detail-layout[drawer][orientation='vertical'][transition='add']::part(detail) {
161
- view-transition-name: vaadin-master-detail-layout-drawer-vertical-detail-add;
162
- }
163
-
164
- ::view-transition-group(vaadin-master-detail-layout-drawer-vertical-detail-add) {
165
- clip-path: inset(0);
166
- }
167
-
168
- ::view-transition-new(vaadin-master-detail-layout-drawer-vertical-detail-add) {
169
- animation: var(--vaadin-master-detail-layout-transition-duration, 300ms) ease both
170
- vaadin-master-detail-layout-drawer-vertical-detail-add;
171
- }
172
-
173
- @keyframes vaadin-master-detail-layout-drawer-vertical-detail-add {
174
- from {
175
- transform: translateY(100%);
176
- }
177
- }
178
-
179
- /* Drawer - vertical - remove */
180
-
181
- vaadin-master-detail-layout[drawer][orientation='vertical'][transition='remove']::part(detail) {
182
- view-transition-name: vaadin-master-detail-layout-drawer-vertical-detail-remove;
183
- }
184
-
185
- ::view-transition-group(vaadin-master-detail-layout-drawer-vertical-detail-remove) {
186
- clip-path: inset(0);
187
- }
188
-
189
- ::view-transition-old(vaadin-master-detail-layout-drawer-vertical-detail-remove) {
190
- animation: var(--vaadin-master-detail-layout-transition-duration, 300ms) ease both
191
- vaadin-master-detail-layout-drawer-vertical-detail-remove;
192
- }
193
-
194
- @keyframes vaadin-master-detail-layout-drawer-vertical-detail-remove {
195
- to {
196
- transform: translateY(100%);
197
- }
198
- }
199
-
200
- /* Stack - vertical - add */
201
-
202
- vaadin-master-detail-layout[stack][orientation='vertical'][transition='add'] {
203
- view-transition-name: vaadin-master-detail-layout-stack-vertical-add;
204
- }
205
-
206
- ::view-transition-group(vaadin-master-detail-layout-stack-vertical-add) {
207
- clip-path: inset(0);
208
- }
209
-
210
- ::view-transition-new(vaadin-master-detail-layout-stack-vertical-add) {
211
- animation: var(--vaadin-master-detail-layout-transition-duration, 300ms) ease both
212
- vaadin-master-detail-layout-stack-vertical-add-new;
213
- }
214
-
215
- ::view-transition-old(vaadin-master-detail-layout-stack-vertical-add) {
216
- animation: var(--vaadin-master-detail-layout-transition-duration, 300ms) ease both
217
- vaadin-master-detail-layout-stack-vertical-add-old;
218
- }
219
-
220
- @keyframes vaadin-master-detail-layout-stack-vertical-add-new {
221
- from {
222
- transform: translateY(100px);
223
- opacity: 0;
224
- }
225
- }
226
-
227
- @keyframes vaadin-master-detail-layout-stack-vertical-add-old {
228
- to {
229
- transform: translateY(-100px);
230
- opacity: 0;
231
- }
232
- }
233
-
234
- /* Stack - vertical - remove */
235
-
236
- vaadin-master-detail-layout[stack][orientation='vertical'][transition='remove'] {
237
- view-transition-name: vaadin-master-detail-layout-stack-vertical-remove;
238
- }
239
-
240
- ::view-transition-group(vaadin-master-detail-layout-stack-vertical-remove) {
241
- clip-path: inset(0);
242
- }
243
-
244
- ::view-transition-new(vaadin-master-detail-layout-stack-vertical-remove) {
245
- animation: var(--vaadin-master-detail-layout-transition-duration, 300ms) ease both
246
- vaadin-master-detail-layout-stack-vertical-remove-new;
247
- }
248
-
249
- ::view-transition-old(vaadin-master-detail-layout-stack-vertical-remove) {
250
- animation: var(--vaadin-master-detail-layout-transition-duration, 300ms) ease both
251
- vaadin-master-detail-layout-stack-vertical-remove-old;
252
- }
253
-
254
- @keyframes vaadin-master-detail-layout-stack-vertical-remove-new {
255
- from {
256
- transform: translateY(-100px);
257
- opacity: 0;
258
- }
259
- }
260
-
261
- @keyframes vaadin-master-detail-layout-stack-vertical-remove-old {
262
- to {
263
- transform: translateY(100px);
264
- opacity: 0;
265
- }
266
- }
267
-
268
- /* Stack - vertical - viewport - add */
269
-
270
- vaadin-master-detail-layout[stack][orientation='vertical'][containment='viewport'][transition='add'] {
271
- view-transition-name: vaadin-master-detail-layout-stack-vertical-viewport-add;
272
- }
273
-
274
- ::view-transition-new(vaadin-master-detail-layout-stack-vertical-viewport-add) {
275
- animation: var(--vaadin-master-detail-layout-transition-duration, 300ms) ease both
276
- vaadin-master-detail-layout-stack-vertical-add-new;
277
- }
278
-
279
- /* Stack - vertical - viewport - remove */
280
-
281
- vaadin-master-detail-layout[stack][orientation='vertical'][containment='viewport'][transition='remove'] {
282
- view-transition-name: vaadin-master-detail-layout-stack-vertical-viewport-remove;
283
- }
284
-
285
- ::view-transition-old(vaadin-master-detail-layout-stack-vertical-viewport-remove) {
286
- animation: var(--vaadin-master-detail-layout-transition-duration, 300ms) ease both
287
- vaadin-master-detail-layout-stack-vertical-remove-old;
288
- }
289
- `;
@@ -1 +0,0 @@
1
- import '@vaadin/vaadin-lumo-styles/color.js';
@@ -1,28 +0,0 @@
1
- import '@vaadin/vaadin-lumo-styles/color.js';
2
- import { css, registerStyles } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
3
-
4
- registerStyles(
5
- 'vaadin-master-detail-layout',
6
- css`
7
- :host(:is([drawer], [stack])) [part='detail'] {
8
- background-color: var(--lumo-base-color);
9
- }
10
-
11
- :host([drawer]) [part='detail'] {
12
- box-shadow: var(--lumo-box-shadow-s);
13
- }
14
-
15
- :host([drawer][orientation='horizontal']) [part='detail'] {
16
- border-inline-start: 1px solid var(--lumo-contrast-10pct);
17
- }
18
-
19
- :host([drawer][orientation='vertical']) [part='detail'] {
20
- border-block-start: 1px solid var(--lumo-contrast-10pct);
21
- }
22
-
23
- :host([drawer]) [part='backdrop'] {
24
- background-color: var(--lumo-shade-20pct);
25
- }
26
- `,
27
- { moduleId: 'lumo-master-detail-layout' },
28
- );
@@ -1,2 +0,0 @@
1
- import './vaadin-master-detail-layout-styles.js';
2
- import '../../src/vaadin-master-detail-layout.js';
@@ -1,2 +0,0 @@
1
- import './vaadin-master-detail-layout-styles.js';
2
- import '../../src/vaadin-master-detail-layout.js';