@vaadin/master-detail-layout 24.8.0-alpha3

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/README.md ADDED
@@ -0,0 +1,58 @@
1
+ # @vaadin/master-detail-layout
2
+
3
+ A web component for building UIs with a master (or primary) area and a detail (or secondary) area.
4
+
5
+ ```html
6
+ <vaadin-master-detail-layout>
7
+ <div>Master content</div>
8
+ <div slot="detail">Detail content</div>
9
+ </vaadin-master-detail-layout>
10
+ ```
11
+
12
+ ## Installation
13
+
14
+ Install the component:
15
+
16
+ ```sh
17
+ npm i @vaadin/master-detail-layout
18
+ ```
19
+
20
+ Once installed, import the component in your application:
21
+
22
+ ```js
23
+ import '@vaadin/master-detail-layout';
24
+ ```
25
+
26
+ ## Themes
27
+
28
+ Vaadin components come with two built-in [themes](https://vaadin.com/docs/latest/styling), Lumo and Material.
29
+ The [main entrypoint](https://github.com/vaadin/web-components/blob/main/packages/master-detail-layout/vaadin-master-detail-layout.js) of the package uses the Lumo theme.
30
+
31
+ To use the Material theme, import the component from the `theme/material` folder:
32
+
33
+ ```js
34
+ import '@vaadin/master-detail-layout/theme/material/vaadin-master-detail-layout.js';
35
+ ```
36
+
37
+ You can also import the Lumo version of the component explicitly:
38
+
39
+ ```js
40
+ import '@vaadin/master-detail-layout/theme/lumo/vaadin-master-detail-layout.js';
41
+ ```
42
+
43
+ Finally, you can import the un-themed component from the `src` folder to get a minimal starting point:
44
+
45
+ ```js
46
+ import '@vaadin/master-detail-layout/src/vaadin-master-detail-layout.js';
47
+ ```
48
+
49
+ ## Contributing
50
+
51
+ Read the [contributing guide](https://vaadin.com/docs/latest/contributing) to learn about our development process, how to propose bugfixes and improvements, and how to test your changes to Vaadin components.
52
+
53
+ ## License
54
+
55
+ Apache License 2.0
56
+
57
+ Vaadin collects usage statistics at development time to improve this product.
58
+ For details and to opt-out, see https://github.com/vaadin/vaadin-usage-statistics.
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@vaadin/master-detail-layout",
3
+ "version": "24.8.0-alpha3",
4
+ "publishConfig": {
5
+ "access": "public"
6
+ },
7
+ "description": "Web component for building UIs with a master area and a detail area.",
8
+ "license": "Apache-2.0",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/vaadin/web-components.git",
12
+ "directory": "packages/master-detail-layout"
13
+ },
14
+ "author": "Vaadin Ltd",
15
+ "homepage": "https://vaadin.com/components",
16
+ "bugs": {
17
+ "url": "https://github.com/vaadin/web-components/issues"
18
+ },
19
+ "main": "vaadin-master-detail-layout.js",
20
+ "module": "vaadin-master-detail-layout.js",
21
+ "type": "module",
22
+ "files": [
23
+ "src",
24
+ "theme",
25
+ "vaadin-*.d.ts",
26
+ "vaadin-*.js",
27
+ "web-types.json",
28
+ "web-types.lit.json"
29
+ ],
30
+ "keywords": [
31
+ "Vaadin",
32
+ "master-detail",
33
+ "web-components",
34
+ "web-component"
35
+ ],
36
+ "dependencies": {
37
+ "@vaadin/component-base": "24.8.0-alpha3",
38
+ "@vaadin/vaadin-lumo-styles": "24.8.0-alpha3",
39
+ "@vaadin/vaadin-material-styles": "24.8.0-alpha3",
40
+ "@vaadin/vaadin-themable-mixin": "24.8.0-alpha3",
41
+ "lit": "^3.0.0"
42
+ },
43
+ "devDependencies": {
44
+ "@vaadin/chai-plugins": "24.8.0-alpha3",
45
+ "@vaadin/testing-helpers": "^1.1.0",
46
+ "sinon": "^18.0.0"
47
+ },
48
+ "web-types": [
49
+ "web-types.json",
50
+ "web-types.lit.json"
51
+ ],
52
+ "gitHead": "8c49e2337a1905ae68d0d7aee2e672500ea72343"
53
+ }
@@ -0,0 +1,65 @@
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 { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
7
+ import { ResizeMixin } from '@vaadin/component-base/src/resize-mixin.js';
8
+ import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
9
+
10
+ /**
11
+ * `<vaadin-master-detail-layout>` is a web component for building UIs with a master
12
+ * (or primary) area and a detail (or secondary) area that is displayed next to, or
13
+ * overlaid on top of, the master area, depending on configuration and viewport size.
14
+ */
15
+ declare class MasterDetailLayout extends ResizeMixin(ThemableMixin(ElementMixin(HTMLElement))) {
16
+ /**
17
+ * Fixed size (in CSS length units) to be set on the detail area.
18
+ * When specified, it prevents the detail area from growing or
19
+ * shrinking. If there is not enough space to show master and detail
20
+ * areas next to each other, the layout switches to the overlay mode.
21
+ *
22
+ * @attr {string} detail-size
23
+ */
24
+ detailSize: string | null | undefined;
25
+
26
+ /**
27
+ * Minimum size (in CSS length units) to be set on the detail area.
28
+ * When specified, it prevents the detail area from shrinking below
29
+ * this size. If there is not enough space to show master and detail
30
+ * areas next to each other, the layout switches to the overlay mode.
31
+ *
32
+ * @attr {string} detail-min-size
33
+ */
34
+ detailMinSize: string | null | undefined;
35
+
36
+ /**
37
+ * Fixed size (in CSS length units) to be set on the master area.
38
+ * When specified, it prevents the master area from growing or
39
+ * shrinking. If there is not enough space to show master and detail
40
+ * areas next to each other, the layout switches to the overlay mode.
41
+ * Setting `100%` enforces the overlay mode to be used by default.
42
+ *
43
+ * @attr {string} master-size
44
+ */
45
+ masterSize: string | null | undefined;
46
+
47
+ /**
48
+ * Minimum size (in CSS length units) to be set on the master area.
49
+ * When specified, it prevents the master area from shrinking below
50
+ * this size. If there is not enough space to show master and detail
51
+ * areas next to each other, the layout switches to the overlay mode.
52
+ * Setting `100%` enforces the overlay mode to be used by default.
53
+ *
54
+ * @attr {string} master-min-size
55
+ */
56
+ masterMinSize: string | null | undefined;
57
+ }
58
+
59
+ declare global {
60
+ interface HTMLElementTagNameMap {
61
+ 'vaadin-master-detail-layout': MasterDetailLayout;
62
+ }
63
+ }
64
+
65
+ export { MasterDetailLayout };
@@ -0,0 +1,266 @@
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, html, LitElement } from 'lit';
7
+ import { defineCustomElement } from '@vaadin/component-base/src/define.js';
8
+ import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
9
+ import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
10
+ import { ResizeMixin } from '@vaadin/component-base/src/resize-mixin.js';
11
+ import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
12
+
13
+ /**
14
+ * `<vaadin-master-detail-layout>` is a web component for building UIs with a master
15
+ * (or primary) area and a detail (or secondary) area that is displayed next to, or
16
+ * overlaid on top of, the master area, depending on configuration and viewport size.
17
+ *
18
+ * @customElement
19
+ * @extends HTMLElement
20
+ * @mixes ThemableMixin
21
+ * @mixes ElementMixin
22
+ * @mixes ResizeMixin
23
+ */
24
+ class MasterDetailLayout extends ResizeMixin(ElementMixin(ThemableMixin(PolylitMixin(LitElement)))) {
25
+ static get is() {
26
+ return 'vaadin-master-detail-layout';
27
+ }
28
+
29
+ static get styles() {
30
+ return css`
31
+ :host {
32
+ display: flex;
33
+ box-sizing: border-box;
34
+ height: 100%;
35
+ }
36
+
37
+ :host([hidden]) {
38
+ display: none !important;
39
+ }
40
+
41
+ :host(:not([has-detail])) [part='detail'] {
42
+ display: none;
43
+ }
44
+
45
+ /* Overlay mode */
46
+ :host([overlay][has-detail]) {
47
+ position: relative;
48
+ }
49
+
50
+ :host([overlay]) [part='detail'] {
51
+ position: absolute;
52
+ inset-inline-end: 0;
53
+ height: 100%;
54
+ width: var(--_detail-min-size, min-content);
55
+ max-width: 100%;
56
+ }
57
+
58
+ :host([overlay]) [part='master'] {
59
+ max-width: 100%;
60
+ }
61
+
62
+ /* No fixed size */
63
+ :host(:not([has-master-size])) [part='master'],
64
+ :host(:not([has-detail-size])) [part='detail'] {
65
+ flex-grow: 1;
66
+ flex-basis: 50%;
67
+ }
68
+
69
+ /* Fixed size */
70
+ :host([has-master-size]) [part='master'] {
71
+ width: var(--_master-size);
72
+ flex-shrink: 0;
73
+ }
74
+
75
+ :host([has-detail-size]) [part='detail'] {
76
+ width: var(--_detail-size);
77
+ flex-shrink: 0;
78
+ }
79
+
80
+ :host([has-master-size][has-detail-size]) [part='master'] {
81
+ flex-grow: 1;
82
+ flex-basis: var(--_master-size);
83
+ }
84
+
85
+ :host([has-master-size][has-detail-size]) [part='detail'] {
86
+ flex-grow: 1;
87
+ flex-basis: var(--_detail-size);
88
+ }
89
+
90
+ /* Min size */
91
+ :host([has-master-min-size]:not([overlay])) [part='master'] {
92
+ min-width: var(--_master-min-size);
93
+ }
94
+
95
+ :host([has-detail-min-size]:not([overlay])) [part='detail'] {
96
+ min-width: var(--_detail-min-size);
97
+ }
98
+
99
+ :host([has-master-min-size]) [part='master'],
100
+ :host([has-detail-min-size]) [part='detail'] {
101
+ flex-shrink: 0;
102
+ }
103
+ `;
104
+ }
105
+
106
+ static get properties() {
107
+ return {
108
+ /**
109
+ * Fixed size (in CSS length units) to be set on the detail area.
110
+ * When specified, it prevents the detail area from growing or
111
+ * shrinking. If there is not enough space to show master and detail
112
+ * areas next to each other, the layout switches to the overlay mode.
113
+ *
114
+ * @attr {string} detail-size
115
+ */
116
+ detailSize: {
117
+ type: String,
118
+ sync: true,
119
+ observer: '__detailSizeChanged',
120
+ },
121
+
122
+ /**
123
+ * Minimum size (in CSS length units) to be set on the detail area.
124
+ * When specified, it prevents the detail area from shrinking below
125
+ * this size. If there is not enough space to show master and detail
126
+ * areas next to each other, the layout switches to the overlay mode.
127
+ *
128
+ * @attr {string} detail-min-size
129
+ */
130
+ detailMinSize: {
131
+ type: String,
132
+ sync: true,
133
+ observer: '__detailMinSizeChanged',
134
+ },
135
+
136
+ /**
137
+ * Fixed size (in CSS length units) to be set on the master area.
138
+ * When specified, it prevents the master area from growing or
139
+ * shrinking. If there is not enough space to show master and detail
140
+ * areas next to each other, the layout switches to the overlay mode.
141
+ * Setting `100%` enforces the overlay mode to be used by default.
142
+ *
143
+ * @attr {string} master-size
144
+ */
145
+ masterSize: {
146
+ type: String,
147
+ sync: true,
148
+ observer: '__masterSizeChanged',
149
+ },
150
+
151
+ /**
152
+ * Minimum size (in CSS length units) to be set on the master area.
153
+ * When specified, it prevents the master area from shrinking below
154
+ * this size. If there is not enough space to show master and detail
155
+ * areas next to each other, the layout switches to the overlay mode.
156
+ * Setting `100%` enforces the overlay mode to be used by default.
157
+ *
158
+ * @attr {string} master-min-size
159
+ */
160
+ masterMinSize: {
161
+ type: String,
162
+ sync: true,
163
+ observer: '__masterMinSizeChanged',
164
+ },
165
+ };
166
+ }
167
+
168
+ /** @protected */
169
+ render() {
170
+ return html`
171
+ <div id="master" part="master">
172
+ <slot></slot>
173
+ </div>
174
+ <div id="detail" part="detail">
175
+ <slot name="detail" @slotchange="${this.__onDetailSlotChange}"></slot>
176
+ </div>
177
+ `;
178
+ }
179
+
180
+ /** @private */
181
+ __onDetailSlotChange(e) {
182
+ this.toggleAttribute('has-detail', e.target.assignedNodes().length > 0);
183
+ this.__detectLayoutMode();
184
+ }
185
+
186
+ /**
187
+ * @protected
188
+ * @override
189
+ */
190
+ _onResize() {
191
+ this.__detectLayoutMode();
192
+ }
193
+
194
+ /** @private */
195
+ __detailSizeChanged(size, oldSize) {
196
+ this.__updateStyleProperty('detail-size', size, oldSize);
197
+ this.__detectLayoutMode();
198
+ }
199
+
200
+ /** @private */
201
+ __detailMinSizeChanged(size, oldSize) {
202
+ this.__updateStyleProperty('detail-min-size', size, oldSize);
203
+ this.__detectLayoutMode();
204
+ }
205
+
206
+ /** @private */
207
+ __masterSizeChanged(size, oldSize) {
208
+ this.__updateStyleProperty('master-size', size, oldSize);
209
+ this.__detectLayoutMode();
210
+ }
211
+
212
+ /** @private */
213
+ __masterMinSizeChanged(size, oldSize) {
214
+ this.__updateStyleProperty('master-min-size', size, oldSize);
215
+ this.__detectLayoutMode();
216
+ }
217
+
218
+ /** @private */
219
+ __updateStyleProperty(prop, size, oldSize) {
220
+ if (size) {
221
+ this.style.setProperty(`--_${prop}`, size);
222
+ } else if (oldSize) {
223
+ this.style.removeProperty(`--_${prop}`);
224
+ }
225
+
226
+ this.toggleAttribute(`has-${prop}`, !!size);
227
+ }
228
+
229
+ /** @private */
230
+ __detectLayoutMode() {
231
+ if (!this.hasAttribute('has-detail')) {
232
+ this.removeAttribute('overlay');
233
+ return;
234
+ }
235
+
236
+ const detailWidth = this.$.detail.offsetWidth;
237
+
238
+ // Detect minimum width needed by master content. Use max-width to ensure
239
+ // the layout can switch back to split mode once there is enough space.
240
+ // If there is master size or min-size set, use that instead to force the
241
+ // overlay mode by setting `masterSize` / `masterMinSize` to 100%/
242
+ this.$.master.style.maxWidth = this.masterSize || this.masterMinSize || 'min-content';
243
+ const masterWidth = this.$.master.offsetWidth;
244
+ this.$.master.style.maxWidth = '';
245
+
246
+ // If the combined minimum size of both the master and the detail content
247
+ // exceeds the size of the layout, the layout changes to the overlay mode.
248
+ if (this.offsetWidth < masterWidth + detailWidth) {
249
+ this.setAttribute('overlay', '');
250
+ } else {
251
+ this.removeAttribute('overlay');
252
+ }
253
+
254
+ // Toggling the overlay resizes master content, which can cause document
255
+ // scroll bar to appear or disappear, and trigger another resize of the
256
+ // layout which can affect previous measurements and end up in horizontal
257
+ // scroll. Check if that is the case and if so, preserve the overlay mode.
258
+ if (this.offsetWidth < this.scrollWidth) {
259
+ this.setAttribute('overlay', '');
260
+ }
261
+ }
262
+ }
263
+
264
+ defineCustomElement(MasterDetailLayout);
265
+
266
+ export { MasterDetailLayout };
@@ -0,0 +1 @@
1
+ import '../../src/vaadin-master-detail-layout.js';
@@ -0,0 +1 @@
1
+ import '../../src/vaadin-master-detail-layout.js';
@@ -0,0 +1 @@
1
+ import '../../src/vaadin-master-detail-layout.js';
@@ -0,0 +1 @@
1
+ import '../../src/vaadin-master-detail-layout.js';
@@ -0,0 +1 @@
1
+ export * from './src/vaadin-master-detail-layout.js';
@@ -0,0 +1,3 @@
1
+ import './theme/lumo/vaadin-master-detail-layout.js';
2
+
3
+ export * from './src/vaadin-master-detail-layout.js';
package/web-types.json ADDED
@@ -0,0 +1,122 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/web-types",
3
+ "name": "@vaadin/master-detail-layout",
4
+ "version": "24.8.0-alpha3",
5
+ "description-markup": "markdown",
6
+ "contributions": {
7
+ "html": {
8
+ "elements": [
9
+ {
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.",
12
+ "attributes": [
13
+ {
14
+ "name": "detail-size",
15
+ "description": "Fixed size (in CSS length units) to be set on the detail area.\nWhen specified, it prevents the detail area from growing or\nshrinking. If there is not enough space to show master and detail\nareas next to each other, the layout switches to the overlay mode.",
16
+ "value": {
17
+ "type": [
18
+ "string",
19
+ "null",
20
+ "undefined"
21
+ ]
22
+ }
23
+ },
24
+ {
25
+ "name": "detail-min-size",
26
+ "description": "Minimum size (in CSS length units) to be set on the detail area.\nWhen specified, it prevents the detail area from shrinking below\nthis size. If there is not enough space to show master and detail\nareas next to each other, the layout switches to the overlay mode.",
27
+ "value": {
28
+ "type": [
29
+ "string",
30
+ "null",
31
+ "undefined"
32
+ ]
33
+ }
34
+ },
35
+ {
36
+ "name": "master-size",
37
+ "description": "Fixed size (in CSS length units) to be set on the master area.\nWhen specified, it prevents the master area from growing or\nshrinking. If there is not enough space to show master and detail\nareas next to each other, the layout switches to the overlay mode.\nSetting `100%` enforces the overlay mode to be used by default.",
38
+ "value": {
39
+ "type": [
40
+ "string",
41
+ "null",
42
+ "undefined"
43
+ ]
44
+ }
45
+ },
46
+ {
47
+ "name": "master-min-size",
48
+ "description": "Minimum size (in CSS length units) to be set on the master area.\nWhen specified, it prevents the master area from shrinking below\nthis size. If there is not enough space to show master and detail\nareas next to each other, the layout switches to the overlay mode.\nSetting `100%` enforces the overlay mode to be used by default.",
49
+ "value": {
50
+ "type": [
51
+ "string",
52
+ "null",
53
+ "undefined"
54
+ ]
55
+ }
56
+ },
57
+ {
58
+ "name": "theme",
59
+ "description": "The theme variants to apply to the component.",
60
+ "value": {
61
+ "type": [
62
+ "string",
63
+ "null",
64
+ "undefined"
65
+ ]
66
+ }
67
+ }
68
+ ],
69
+ "js": {
70
+ "properties": [
71
+ {
72
+ "name": "detailSize",
73
+ "description": "Fixed size (in CSS length units) to be set on the detail area.\nWhen specified, it prevents the detail area from growing or\nshrinking. If there is not enough space to show master and detail\nareas next to each other, the layout switches to the overlay mode.",
74
+ "value": {
75
+ "type": [
76
+ "string",
77
+ "null",
78
+ "undefined"
79
+ ]
80
+ }
81
+ },
82
+ {
83
+ "name": "detailMinSize",
84
+ "description": "Minimum size (in CSS length units) to be set on the detail area.\nWhen specified, it prevents the detail area from shrinking below\nthis size. If there is not enough space to show master and detail\nareas next to each other, the layout switches to the overlay mode.",
85
+ "value": {
86
+ "type": [
87
+ "string",
88
+ "null",
89
+ "undefined"
90
+ ]
91
+ }
92
+ },
93
+ {
94
+ "name": "masterSize",
95
+ "description": "Fixed size (in CSS length units) to be set on the master area.\nWhen specified, it prevents the master area from growing or\nshrinking. If there is not enough space to show master and detail\nareas next to each other, the layout switches to the overlay mode.\nSetting `100%` enforces the overlay mode to be used by default.",
96
+ "value": {
97
+ "type": [
98
+ "string",
99
+ "null",
100
+ "undefined"
101
+ ]
102
+ }
103
+ },
104
+ {
105
+ "name": "masterMinSize",
106
+ "description": "Minimum size (in CSS length units) to be set on the master area.\nWhen specified, it prevents the master area from shrinking below\nthis size. If there is not enough space to show master and detail\nareas next to each other, the layout switches to the overlay mode.\nSetting `100%` enforces the overlay mode to be used by default.",
107
+ "value": {
108
+ "type": [
109
+ "string",
110
+ "null",
111
+ "undefined"
112
+ ]
113
+ }
114
+ }
115
+ ],
116
+ "events": []
117
+ }
118
+ }
119
+ ]
120
+ }
121
+ }
122
+ }
@@ -0,0 +1,55 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/web-types",
3
+ "name": "@vaadin/master-detail-layout",
4
+ "version": "24.8.0-alpha3",
5
+ "description-markup": "markdown",
6
+ "framework": "lit",
7
+ "framework-config": {
8
+ "enable-when": {
9
+ "node-packages": [
10
+ "lit"
11
+ ]
12
+ }
13
+ },
14
+ "contributions": {
15
+ "html": {
16
+ "elements": [
17
+ {
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.",
20
+ "extension": true,
21
+ "attributes": [
22
+ {
23
+ "name": ".detailSize",
24
+ "description": "Fixed size (in CSS length units) to be set on the detail area.\nWhen specified, it prevents the detail area from growing or\nshrinking. If there is not enough space to show master and detail\nareas next to each other, the layout switches to the overlay mode.",
25
+ "value": {
26
+ "kind": "expression"
27
+ }
28
+ },
29
+ {
30
+ "name": ".detailMinSize",
31
+ "description": "Minimum size (in CSS length units) to be set on the detail area.\nWhen specified, it prevents the detail area from shrinking below\nthis size. If there is not enough space to show master and detail\nareas next to each other, the layout switches to the overlay mode.",
32
+ "value": {
33
+ "kind": "expression"
34
+ }
35
+ },
36
+ {
37
+ "name": ".masterSize",
38
+ "description": "Fixed size (in CSS length units) to be set on the master area.\nWhen specified, it prevents the master area from growing or\nshrinking. If there is not enough space to show master and detail\nareas next to each other, the layout switches to the overlay mode.\nSetting `100%` enforces the overlay mode to be used by default.",
39
+ "value": {
40
+ "kind": "expression"
41
+ }
42
+ },
43
+ {
44
+ "name": ".masterMinSize",
45
+ "description": "Minimum size (in CSS length units) to be set on the master area.\nWhen specified, it prevents the master area from shrinking below\nthis size. If there is not enough space to show master and detail\nareas next to each other, the layout switches to the overlay mode.\nSetting `100%` enforces the overlay mode to be used by default.",
46
+ "value": {
47
+ "kind": "expression"
48
+ }
49
+ }
50
+ ]
51
+ }
52
+ ]
53
+ }
54
+ }
55
+ }