@vaadin/master-detail-layout 25.2.0-alpha2 → 25.2.0-alpha4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/custom-elements.json
CHANGED
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"type": {
|
|
34
34
|
"text": "string"
|
|
35
35
|
},
|
|
36
|
-
"description": "Size (in CSS length units) to be set on the detail area in\nthe CSS grid layout.
|
|
36
|
+
"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.",
|
|
37
37
|
"attribute": "detail-size"
|
|
38
38
|
},
|
|
39
39
|
{
|
|
@@ -95,6 +95,11 @@
|
|
|
95
95
|
},
|
|
96
96
|
"description": "Size (in CSS length units) for the detail area when shown as an\noverlay. When not set, falls back to `detailSize`. Set to `100%`\nto make the detail cover the full layout.",
|
|
97
97
|
"attribute": "overlay-size"
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"kind": "method",
|
|
101
|
+
"name": "recalculateLayout",
|
|
102
|
+
"description": "When `detailSize` is not explicitly set, re-measures the cached intrinsic size of\nthe detail content by placing it in a min-content CSS grid column, then repeats\nthis process for ancestor master-detail layouts without an explicit `detailSize`,\nif any, so that their detail areas also adapt.\n\nCall this method after changing the detail content in a way that affects its intrinsic\nsize — for example, when opening a detail in a nested master-detail layout that was\nnot previously visible.\n\nNOTE: This method can be expensive in large layouts as it triggers consecutive\nsynchronous DOM reads and writes."
|
|
98
103
|
}
|
|
99
104
|
],
|
|
100
105
|
"events": [
|
|
@@ -119,7 +124,7 @@
|
|
|
119
124
|
"type": {
|
|
120
125
|
"text": "string"
|
|
121
126
|
},
|
|
122
|
-
"description": "Size (in CSS length units) to be set on the detail area in\nthe CSS grid layout.
|
|
127
|
+
"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.",
|
|
123
128
|
"fieldName": "detailSize"
|
|
124
129
|
},
|
|
125
130
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vaadin/master-detail-layout",
|
|
3
|
-
"version": "25.2.0-
|
|
3
|
+
"version": "25.2.0-alpha4",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -34,16 +34,16 @@
|
|
|
34
34
|
"web-component"
|
|
35
35
|
],
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@vaadin/a11y-base": "25.2.0-
|
|
38
|
-
"@vaadin/component-base": "25.2.0-
|
|
39
|
-
"@vaadin/vaadin-themable-mixin": "25.2.0-
|
|
37
|
+
"@vaadin/a11y-base": "25.2.0-alpha4",
|
|
38
|
+
"@vaadin/component-base": "25.2.0-alpha4",
|
|
39
|
+
"@vaadin/vaadin-themable-mixin": "25.2.0-alpha4",
|
|
40
40
|
"lit": "^3.0.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
|
-
"@vaadin/aura": "25.2.0-
|
|
44
|
-
"@vaadin/chai-plugins": "25.2.0-
|
|
43
|
+
"@vaadin/aura": "25.2.0-alpha4",
|
|
44
|
+
"@vaadin/chai-plugins": "25.2.0-alpha4",
|
|
45
45
|
"@vaadin/testing-helpers": "^2.0.0",
|
|
46
|
-
"@vaadin/vaadin-lumo-styles": "25.2.0-
|
|
46
|
+
"@vaadin/vaadin-lumo-styles": "25.2.0-alpha4",
|
|
47
47
|
"sinon": "^21.0.2"
|
|
48
48
|
},
|
|
49
49
|
"customElements": "custom-elements.json",
|
|
@@ -51,5 +51,5 @@
|
|
|
51
51
|
"web-types.json",
|
|
52
52
|
"web-types.lit.json"
|
|
53
53
|
],
|
|
54
|
-
"gitHead": "
|
|
54
|
+
"gitHead": "fc054a1bed540874ef3b5000828c191cc12044ec"
|
|
55
55
|
}
|
|
@@ -8,10 +8,12 @@ import { css } from 'lit';
|
|
|
8
8
|
|
|
9
9
|
export const masterDetailLayoutStyles = css`
|
|
10
10
|
:host {
|
|
11
|
-
--_master-size:
|
|
12
|
-
--
|
|
13
|
-
--
|
|
14
|
-
--_detail-
|
|
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
|
+
|
|
15
17
|
--_transition-duration: 0s;
|
|
16
18
|
--_transition-easing: cubic-bezier(0.78, 0, 0.22, 1);
|
|
17
19
|
--_rtl-multiplier: 1;
|
|
@@ -22,12 +24,16 @@ export const masterDetailLayoutStyles = css`
|
|
|
22
24
|
height: 100%;
|
|
23
25
|
position: relative;
|
|
24
26
|
z-index: 0;
|
|
25
|
-
overflow:
|
|
26
|
-
grid-template-columns:
|
|
27
|
+
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];
|
|
27
32
|
grid-template-rows: 100%;
|
|
28
33
|
}
|
|
29
34
|
|
|
30
|
-
:host([hidden])
|
|
35
|
+
:host([hidden]),
|
|
36
|
+
::slotted([hidden]) {
|
|
31
37
|
display: none !important;
|
|
32
38
|
}
|
|
33
39
|
|
|
@@ -39,7 +45,10 @@ export const masterDetailLayoutStyles = css`
|
|
|
39
45
|
--_detail-offscreen: 0 30px;
|
|
40
46
|
|
|
41
47
|
grid-template-columns: 100%;
|
|
42
|
-
grid-template-rows:
|
|
48
|
+
grid-template-rows:
|
|
49
|
+
[master-start] var(--_master-size) var(--_master-extra)
|
|
50
|
+
[detail-start] var(--_detail-size) var(--_detail-extra)
|
|
51
|
+
[detail-end];
|
|
43
52
|
}
|
|
44
53
|
|
|
45
54
|
:is(#master, #detail, #detail-placeholder, #outgoing) {
|
|
@@ -47,11 +56,11 @@ export const masterDetailLayoutStyles = css`
|
|
|
47
56
|
}
|
|
48
57
|
|
|
49
58
|
#detail-placeholder {
|
|
50
|
-
|
|
59
|
+
visibility: hidden;
|
|
51
60
|
}
|
|
52
61
|
|
|
53
62
|
:host([has-detail-placeholder]:not([has-detail], [overlay])) #detail-placeholder {
|
|
54
|
-
|
|
63
|
+
visibility: visible;
|
|
55
64
|
}
|
|
56
65
|
|
|
57
66
|
#master {
|
|
@@ -86,28 +95,32 @@ export const masterDetailLayoutStyles = css`
|
|
|
86
95
|
|
|
87
96
|
:host([expand='both']),
|
|
88
97
|
:host([expand='master']) {
|
|
89
|
-
--_master-
|
|
98
|
+
--_master-extra: 1fr;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
:host([expand='both']:is([has-detail], [has-detail-placeholder])),
|
|
102
|
+
:host([expand='detail']:is([has-detail], [has-detail-placeholder])) {
|
|
103
|
+
--_detail-extra: 1fr;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
:host([recalculating-detail-size]:is([has-detail], [has-detail-placeholder])) {
|
|
107
|
+
--_detail-extra: 0px;
|
|
90
108
|
}
|
|
91
109
|
|
|
92
110
|
:host([keep-detail-column-offscreen]),
|
|
93
111
|
:host([has-detail-placeholder][overlay]),
|
|
94
112
|
:host(:not([has-detail-placeholder], [has-detail])) {
|
|
95
|
-
--_master-
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
:host([expand='both']),
|
|
99
|
-
:host([expand='detail']) {
|
|
100
|
-
--_detail-column: var(--_detail-size) 1fr;
|
|
113
|
+
--_master-extra: calc(100% - var(--_master-size));
|
|
101
114
|
}
|
|
102
115
|
|
|
103
116
|
:host([orientation='horizontal']) #detail-placeholder,
|
|
104
|
-
:host([orientation='horizontal']
|
|
117
|
+
:host([orientation='horizontal']:not([overlay])) #detail {
|
|
105
118
|
border-inline-start: var(--vaadin-master-detail-layout-border-width, 1px) solid
|
|
106
119
|
var(--vaadin-master-detail-layout-border-color, var(--vaadin-border-color-secondary));
|
|
107
120
|
}
|
|
108
121
|
|
|
109
122
|
:host([orientation='vertical']) #detail-placeholder,
|
|
110
|
-
:host([orientation='vertical']
|
|
123
|
+
:host([orientation='vertical']:not([overlay])) #detail {
|
|
111
124
|
border-top: var(--vaadin-master-detail-layout-border-width, 1px) solid
|
|
112
125
|
var(--vaadin-master-detail-layout-border-color, var(--vaadin-border-color-secondary));
|
|
113
126
|
}
|
|
@@ -115,10 +128,12 @@ export const masterDetailLayoutStyles = css`
|
|
|
115
128
|
/* Detail transition: off-screen by default, on-screen when has-detail */
|
|
116
129
|
#detail {
|
|
117
130
|
translate: var(--_detail-offscreen);
|
|
131
|
+
visibility: hidden;
|
|
118
132
|
}
|
|
119
133
|
|
|
120
134
|
:host([has-detail]) #detail {
|
|
121
135
|
translate: none;
|
|
136
|
+
visibility: visible;
|
|
122
137
|
}
|
|
123
138
|
|
|
124
139
|
#outgoing:not([hidden]) {
|
|
@@ -150,13 +165,15 @@ export const masterDetailLayoutStyles = css`
|
|
|
150
165
|
:host([has-detail][overlay]:not([orientation='vertical'])) :is(#detail, #outgoing) {
|
|
151
166
|
inset-block: 0;
|
|
152
167
|
inset-inline-end: 0;
|
|
153
|
-
width: var(--_overlay-size, var(--_detail-size
|
|
168
|
+
width: var(--_overlay-size, var(--_detail-size));
|
|
169
|
+
max-width: 100%;
|
|
154
170
|
}
|
|
155
171
|
|
|
156
172
|
:host([has-detail][overlay][orientation='vertical']) :is(#detail, #outgoing) {
|
|
157
173
|
inset-inline: 0;
|
|
158
174
|
inset-block-end: 0;
|
|
159
|
-
height: var(--_overlay-size, var(--_detail-size
|
|
175
|
+
height: var(--_overlay-size, var(--_detail-size));
|
|
176
|
+
max-height: 100%;
|
|
160
177
|
}
|
|
161
178
|
|
|
162
179
|
:host([has-detail][overlay][overlay-containment='viewport']) :is(#detail, #outgoing, #backdrop) {
|
|
@@ -84,9 +84,15 @@ export interface MasterDetailLayoutEventMap extends HTMLElementEventMap, MasterD
|
|
|
84
84
|
declare class MasterDetailLayout extends ThemableMixin(ElementMixin(HTMLElement)) {
|
|
85
85
|
/**
|
|
86
86
|
* Size (in CSS length units) to be set on the detail area in
|
|
87
|
-
* the CSS grid layout.
|
|
87
|
+
* the CSS grid layout. When there is not enough space to show
|
|
88
88
|
* master and detail areas next to each other, the detail area
|
|
89
|
-
* is shown as an overlay.
|
|
89
|
+
* is shown as an overlay.
|
|
90
|
+
* <p>
|
|
91
|
+
* If not specified, the size is determined automatically by measuring
|
|
92
|
+
* the detail content in a `min-content` CSS grid column when it first
|
|
93
|
+
* becomes visible, and then caching the resulting intrinsic size. To
|
|
94
|
+
* recalculate the cached intrinsic size, use the `recalculateLayout`
|
|
95
|
+
* method.
|
|
90
96
|
*
|
|
91
97
|
* @attr {string} detail-size
|
|
92
98
|
*/
|
|
@@ -143,6 +149,21 @@ declare class MasterDetailLayout extends ThemableMixin(ElementMixin(HTMLElement)
|
|
|
143
149
|
*/
|
|
144
150
|
noAnimation: boolean;
|
|
145
151
|
|
|
152
|
+
/**
|
|
153
|
+
* When `detailSize` is not explicitly set, re-measures the cached intrinsic size of
|
|
154
|
+
* the detail content by placing it in a min-content CSS grid column, then repeats
|
|
155
|
+
* this process for ancestor master-detail layouts without an explicit `detailSize`,
|
|
156
|
+
* if any, so that their detail areas also adapt.
|
|
157
|
+
*
|
|
158
|
+
* Call this method after changing the detail content in a way that affects its intrinsic
|
|
159
|
+
* size — for example, when opening a detail in a nested master-detail layout that was
|
|
160
|
+
* not previously visible.
|
|
161
|
+
*
|
|
162
|
+
* NOTE: This method can be expensive in large layouts as it triggers consecutive
|
|
163
|
+
* synchronous DOM reads and writes.
|
|
164
|
+
*/
|
|
165
|
+
recalculateLayout(): void;
|
|
166
|
+
|
|
146
167
|
addEventListener<K extends keyof MasterDetailLayoutEventMap>(
|
|
147
168
|
type: K,
|
|
148
169
|
listener: (this: MasterDetailLayout, ev: MasterDetailLayoutEventMap[K]) => void,
|
|
@@ -6,6 +6,7 @@
|
|
|
6
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
|
+
import { getClosestElement } from '@vaadin/component-base/src/dom-utils.js';
|
|
9
10
|
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
|
|
10
11
|
import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
|
|
11
12
|
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
|
|
@@ -20,6 +21,18 @@ function parseTrackSizes(gridTemplate) {
|
|
|
20
21
|
.map(parseFloat);
|
|
21
22
|
}
|
|
22
23
|
|
|
24
|
+
function detectOverflow(hostSize, trackSizes) {
|
|
25
|
+
const [masterSize, masterExtra, detailSize] = trackSizes;
|
|
26
|
+
|
|
27
|
+
if (Math.floor(masterSize + masterExtra + detailSize) <= Math.floor(hostSize)) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
if (Math.floor(masterExtra) >= Math.floor(detailSize)) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
|
|
23
36
|
/**
|
|
24
37
|
* `<vaadin-master-detail-layout>` is a web component for building UIs with a master
|
|
25
38
|
* (or primary) area and a detail (or secondary) area that is displayed next to, or
|
|
@@ -105,9 +118,15 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
|
|
|
105
118
|
return {
|
|
106
119
|
/**
|
|
107
120
|
* Size (in CSS length units) to be set on the detail area in
|
|
108
|
-
* the CSS grid layout.
|
|
121
|
+
* the CSS grid layout. When there is not enough space to show
|
|
109
122
|
* master and detail areas next to each other, the detail area
|
|
110
|
-
* is shown as an overlay.
|
|
123
|
+
* is shown as an overlay.
|
|
124
|
+
* <p>
|
|
125
|
+
* If not specified, the size is determined automatically by measuring
|
|
126
|
+
* the detail content in a `min-content` CSS grid column when it first
|
|
127
|
+
* becomes visible, and then caching the resulting intrinsic size. To
|
|
128
|
+
* recalculate the cached intrinsic size, use the `recalculateLayout`
|
|
129
|
+
* method.
|
|
111
130
|
*
|
|
112
131
|
* @attr {string} detail-size
|
|
113
132
|
*/
|
|
@@ -155,6 +174,7 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
|
|
|
155
174
|
value: 'horizontal',
|
|
156
175
|
reflectToAttribute: true,
|
|
157
176
|
sync: true,
|
|
177
|
+
observer: '__orientationChanged',
|
|
158
178
|
},
|
|
159
179
|
|
|
160
180
|
/**
|
|
@@ -200,6 +220,13 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
|
|
|
200
220
|
type: Boolean,
|
|
201
221
|
sync: true,
|
|
202
222
|
},
|
|
223
|
+
|
|
224
|
+
/** @private */
|
|
225
|
+
__detailCachedSize: {
|
|
226
|
+
type: String,
|
|
227
|
+
observer: '__detailCachedSizeChanged',
|
|
228
|
+
sync: true,
|
|
229
|
+
},
|
|
203
230
|
};
|
|
204
231
|
}
|
|
205
232
|
|
|
@@ -253,11 +280,26 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
|
|
|
253
280
|
/** @private */
|
|
254
281
|
__masterSizeChanged(size, oldSize) {
|
|
255
282
|
this.__updateStyleProperty('master-size', size, oldSize);
|
|
283
|
+
|
|
284
|
+
if (oldSize != null) {
|
|
285
|
+
this.recalculateLayout();
|
|
286
|
+
}
|
|
256
287
|
}
|
|
257
288
|
|
|
258
289
|
/** @private */
|
|
259
290
|
__detailSizeChanged(size, oldSize) {
|
|
260
291
|
this.__updateStyleProperty('detail-size', size, oldSize);
|
|
292
|
+
|
|
293
|
+
if (oldSize != null) {
|
|
294
|
+
this.recalculateLayout();
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/** @private */
|
|
299
|
+
__orientationChanged(_orientation, oldOrientation) {
|
|
300
|
+
if (oldOrientation != null) {
|
|
301
|
+
this.recalculateLayout();
|
|
302
|
+
}
|
|
261
303
|
}
|
|
262
304
|
|
|
263
305
|
/** @private */
|
|
@@ -265,6 +307,11 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
|
|
|
265
307
|
this.__updateStyleProperty('overlay-size', size, oldSize);
|
|
266
308
|
}
|
|
267
309
|
|
|
310
|
+
/** @private */
|
|
311
|
+
__detailCachedSizeChanged(size, oldSize) {
|
|
312
|
+
this.__updateStyleProperty('detail-cached-size', size, oldSize);
|
|
313
|
+
}
|
|
314
|
+
|
|
268
315
|
/** @private */
|
|
269
316
|
__updateStyleProperty(prop, size, oldSize) {
|
|
270
317
|
if (size) {
|
|
@@ -297,9 +344,9 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
|
|
|
297
344
|
* @private
|
|
298
345
|
*/
|
|
299
346
|
__onResize() {
|
|
300
|
-
const state = this.
|
|
347
|
+
const state = this.__readLayoutState();
|
|
301
348
|
cancelAnimationFrame(this.__resizeRaf);
|
|
302
|
-
this.__resizeRaf = requestAnimationFrame(() => this.
|
|
349
|
+
this.__resizeRaf = requestAnimationFrame(() => this.__writeLayoutState(state));
|
|
303
350
|
}
|
|
304
351
|
|
|
305
352
|
/**
|
|
@@ -307,26 +354,56 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
|
|
|
307
354
|
* ResizeObserver callback where layout is already computed (no forced reflow).
|
|
308
355
|
* @private
|
|
309
356
|
*/
|
|
310
|
-
|
|
357
|
+
__readLayoutState() {
|
|
358
|
+
const isVertical = this.orientation === 'vertical';
|
|
359
|
+
|
|
311
360
|
const detailContent = this.querySelector(':scope > [slot="detail"]');
|
|
312
361
|
const detailPlaceholder = this.querySelector(':scope > [slot="detail-placeholder"]');
|
|
313
362
|
|
|
314
363
|
const hadDetail = this.hasAttribute('has-detail');
|
|
315
364
|
const hasDetail = detailContent != null && detailContent.checkVisibility();
|
|
316
365
|
const hasDetailPlaceholder = !!detailPlaceholder;
|
|
317
|
-
const hasOverflow = (hasDetail || hasDetailPlaceholder) && this.__checkOverflow();
|
|
318
366
|
|
|
367
|
+
const computedStyle = getComputedStyle(this);
|
|
368
|
+
const hostSizeProp = isVertical ? 'height' : 'width';
|
|
369
|
+
const hostSize = parseFloat(computedStyle[hostSizeProp]);
|
|
370
|
+
|
|
371
|
+
const trackSizesProp = isVertical ? 'gridTemplateRows' : 'gridTemplateColumns';
|
|
372
|
+
const trackSizes = parseTrackSizes(computedStyle[trackSizesProp]);
|
|
373
|
+
|
|
374
|
+
const hasOverflow = (hasDetail || hasDetailPlaceholder) && detectOverflow(hostSize, trackSizes);
|
|
319
375
|
const focusTarget = !hadDetail && hasDetail && hasOverflow ? getFocusableElements(detailContent)[0] : null;
|
|
320
|
-
|
|
376
|
+
|
|
377
|
+
return {
|
|
378
|
+
hadDetail,
|
|
379
|
+
hasDetail,
|
|
380
|
+
hasDetailPlaceholder,
|
|
381
|
+
hasOverflow,
|
|
382
|
+
focusTarget,
|
|
383
|
+
hostSize,
|
|
384
|
+
trackSizes,
|
|
385
|
+
};
|
|
321
386
|
}
|
|
322
387
|
|
|
323
388
|
/**
|
|
324
389
|
* Applies layout state to DOM attributes. Pure writes, no reads.
|
|
325
390
|
* @private
|
|
326
391
|
*/
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
392
|
+
__writeLayoutState({ hadDetail, hasDetail, hasDetailPlaceholder, hasOverflow, focusTarget, trackSizes }) {
|
|
393
|
+
const [_masterSize, _masterExtra, detailSize] = trackSizes;
|
|
394
|
+
|
|
395
|
+
// If no detailSize is explicitily set, cache the intrinsic size (min-content) of
|
|
396
|
+
// the slotted detail content to use as a fallback for the detail column size
|
|
397
|
+
// while the detail content is rendered in an overlay.
|
|
398
|
+
if ((hasDetail || hasDetailPlaceholder) && this.__isDetailAutoSized && detailSize > 0) {
|
|
399
|
+
this.__detailCachedSize = this.__detailCachedSize || `${Math.ceil(detailSize)}px`;
|
|
400
|
+
} else {
|
|
401
|
+
this.__detailCachedSize = null;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Force the detail column offscreen when it first appears and overflow
|
|
405
|
+
// is already detected. This prevents unnecessary master column shrinking,
|
|
406
|
+
// as the detail content is rendered in an overlay anyway.
|
|
330
407
|
if (!hadDetail && hasDetail && hasOverflow) {
|
|
331
408
|
this.setAttribute('keep-detail-column-offscreen', '');
|
|
332
409
|
} else if (!hasDetail || !hasOverflow) {
|
|
@@ -346,23 +423,59 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
|
|
|
346
423
|
}
|
|
347
424
|
}
|
|
348
425
|
|
|
349
|
-
/**
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
426
|
+
/**
|
|
427
|
+
* When `detailSize` is not explicitly set, re-measures the cached intrinsic size of
|
|
428
|
+
* the detail content by placing it in a min-content CSS grid column, then repeats
|
|
429
|
+
* this process for ancestor master-detail layouts without an explicit `detailSize`,
|
|
430
|
+
* if any, so that their detail areas also adapt.
|
|
431
|
+
*
|
|
432
|
+
* Call this method after changing the detail content in a way that affects its intrinsic
|
|
433
|
+
* size — for example, when opening a detail in a nested master-detail layout that was
|
|
434
|
+
* not previously visible.
|
|
435
|
+
*
|
|
436
|
+
* NOTE: This method can be expensive in large layouts as it triggers consecutive
|
|
437
|
+
* synchronous DOM reads and writes.
|
|
438
|
+
*/
|
|
439
|
+
recalculateLayout() {
|
|
440
|
+
// Cancel any pending ResizeObserver rAF to prevent it from potentially
|
|
441
|
+
// overriding the layout state with stale measurements.
|
|
442
|
+
cancelAnimationFrame(this.__resizeRaf);
|
|
353
443
|
|
|
354
|
-
const
|
|
355
|
-
const [masterSize, masterExtra, detailSize] = parseTrackSizes(
|
|
356
|
-
computedStyle[isVertical ? 'gridTemplateRows' : 'gridTemplateColumns'],
|
|
357
|
-
);
|
|
444
|
+
const invalidatedLayouts = [...this.__ancestorLayouts.filter((layout) => layout.__isDetailAutoSized), this];
|
|
358
445
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
446
|
+
// Write
|
|
447
|
+
invalidatedLayouts.forEach((layout) => {
|
|
448
|
+
layout.__detailCachedSize = null;
|
|
449
|
+
|
|
450
|
+
if (layout.__isDetailAutoSized) {
|
|
451
|
+
layout.removeAttribute('overlay');
|
|
452
|
+
layout.toggleAttribute('recalculating-detail-size', true);
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
// Read/Write
|
|
457
|
+
invalidatedLayouts.forEach((layout) => {
|
|
458
|
+
const state = layout.__readLayoutState();
|
|
459
|
+
layout.__writeLayoutState(state);
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
// Write
|
|
463
|
+
invalidatedLayouts.forEach((layout) => {
|
|
464
|
+
if (layout.__isDetailAutoSized) {
|
|
465
|
+
layout.toggleAttribute('recalculating-detail-size', false);
|
|
466
|
+
}
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/** @private */
|
|
471
|
+
get __isDetailAutoSized() {
|
|
472
|
+
return this.detailSize == null;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/** @private */
|
|
476
|
+
get __ancestorLayouts() {
|
|
477
|
+
const parent = getClosestElement(this.constructor.is, this.parentNode);
|
|
478
|
+
return parent ? [...parent.__ancestorLayouts, parent] : [];
|
|
366
479
|
}
|
|
367
480
|
|
|
368
481
|
/** @private */
|
|
@@ -413,10 +526,7 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
|
|
|
413
526
|
|
|
414
527
|
if (skipTransition || this.noAnimation) {
|
|
415
528
|
updateSlot();
|
|
416
|
-
queueMicrotask(() =>
|
|
417
|
-
const state = this.__computeLayoutState();
|
|
418
|
-
this.__applyLayoutState(state);
|
|
419
|
-
});
|
|
529
|
+
queueMicrotask(() => this.recalculateLayout());
|
|
420
530
|
return Promise.resolve();
|
|
421
531
|
}
|
|
422
532
|
|
|
@@ -561,8 +671,7 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
|
|
|
561
671
|
* @protected
|
|
562
672
|
*/
|
|
563
673
|
_finishTransition() {
|
|
564
|
-
|
|
565
|
-
this.__applyLayoutState(state);
|
|
674
|
+
queueMicrotask(() => this.recalculateLayout());
|
|
566
675
|
}
|
|
567
676
|
|
|
568
677
|
/**
|
|
@@ -650,7 +759,7 @@ class MasterDetailLayout extends ElementMixin(ThemableMixin(PolylitMixin(LitElem
|
|
|
650
759
|
* @private
|
|
651
760
|
*/
|
|
652
761
|
__animate(element, keyframes, options) {
|
|
653
|
-
const animation = element.animate(keyframes, options);
|
|
762
|
+
const animation = element.animate(keyframes, { ...options, fill: 'forwards' });
|
|
654
763
|
|
|
655
764
|
this.__activeAnimations = this.__activeAnimations || [];
|
|
656
765
|
this.__activeAnimations.push(animation);
|
package/web-types.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/web-types",
|
|
3
3
|
"name": "@vaadin/master-detail-layout",
|
|
4
|
-
"version": "25.2.0-
|
|
4
|
+
"version": "25.2.0-alpha4",
|
|
5
5
|
"description-markup": "markdown",
|
|
6
6
|
"contributions": {
|
|
7
7
|
"html": {
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"attributes": [
|
|
13
13
|
{
|
|
14
14
|
"name": "detail-size",
|
|
15
|
-
"description": "Size (in CSS length units) to be set on the detail area in\nthe CSS grid layout.
|
|
15
|
+
"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.",
|
|
16
16
|
"value": {
|
|
17
17
|
"type": [
|
|
18
18
|
"string",
|
|
@@ -103,7 +103,7 @@
|
|
|
103
103
|
"properties": [
|
|
104
104
|
{
|
|
105
105
|
"name": "detailSize",
|
|
106
|
-
"description": "Size (in CSS length units) to be set on the detail area in\nthe CSS grid layout.
|
|
106
|
+
"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.",
|
|
107
107
|
"value": {
|
|
108
108
|
"type": [
|
|
109
109
|
"string",
|
package/web-types.lit.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/web-types",
|
|
3
3
|
"name": "@vaadin/master-detail-layout",
|
|
4
|
-
"version": "25.2.0-
|
|
4
|
+
"version": "25.2.0-alpha4",
|
|
5
5
|
"description-markup": "markdown",
|
|
6
6
|
"framework": "lit",
|
|
7
7
|
"framework-config": {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
},
|
|
29
29
|
{
|
|
30
30
|
"name": ".detailSize",
|
|
31
|
-
"description": "Size (in CSS length units) to be set on the detail area in\nthe CSS grid layout.
|
|
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.",
|
|
32
32
|
"value": {
|
|
33
33
|
"kind": "expression"
|
|
34
34
|
}
|