Skip to content

Commit c1efe28

Browse files
committed
feat: add FormRow and br support to autoResponsive mode (#8699)
1 parent 60a3e9f commit c1efe28

28 files changed

+581
-118
lines changed

dev/form-layout-auto-responsive.html

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import '@vaadin/button';
2828
import '@vaadin/tabsheet';
2929
import '@vaadin/form-layout';
30+
import '@vaadin/form-layout/vaadin-form-row.js';
3031
import '@vaadin/form-layout/vaadin-form-item.js';
3132
import '@vaadin/text-area';
3233
import '@vaadin/text-field';
@@ -77,6 +78,21 @@ <h1>autoResponsive + br</h1>
7778
<vaadin-password-field label="Confirm"></vaadin-password-field>
7879
</vaadin-form-layout>
7980
81+
<h1>autoResponsive + FormRow</h1>
82+
<vaadin-form-layout auto-responsive>
83+
<vaadin-form-row>
84+
<vaadin-text-field label="First name"></vaadin-text-field>
85+
<vaadin-text-field label="Last name"></vaadin-text-field>
86+
</vaadin-form-row>
87+
<vaadin-form-row>
88+
<vaadin-text-field label="Username" colspan="2"></vaadin-text-field>
89+
</vaadin-form-row>
90+
<vaadin-form-row>
91+
<vaadin-password-field label="Password"></vaadin-password-field>
92+
<vaadin-password-field label="Confirm"></vaadin-password-field>
93+
</vaadin-form-row>
94+
</vaadin-form-layout>
95+
8096
<h1>autoResponsive inside HorizontalLayout + flex:1</h1>
8197
<vaadin-horizontal-layout theme="spacing">
8298
<vaadin-form-layout style="flex: 1;" auto-responsive auto-rows max-columns="2">

packages/form-layout/src/vaadin-form-layout-mixin.d.ts

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -62,16 +62,19 @@ export declare class FormLayoutMixinClass {
6262
responsiveSteps: FormLayoutResponsiveStep[];
6363

6464
/**
65-
* When enabled, the layout automatically creates and adjusts columns based on
66-
* the container's width. Columns have a fixed width defined by `columnWidth`
67-
* and their number increases up to the limit set by `maxColumns`. The layout
68-
* dynamically adjusts the number of columns as the container size changes.
65+
* Enables the auto responsive mode where the component automatically creates and adjusts
66+
* columns based on the container's width. Columns have a fixed width defined by `columnWidth`
67+
* and their number increases up to the limit set by `maxColumns`. The component dynamically
68+
* adjusts the number of columns as the container size changes. When this mode is enabled,
69+
* the `responsiveSteps` are ignored.
6970
*
70-
* By default, each field is placed on a new row. To group fields in the same row,
71-
* wrap them in `<vaadin-form-row>` or enable the `autoRows` property, which allows
72-
* the layout to fit as many fields as possible in a row before wrapping.
71+
* By default, each field is placed on a new row. To organize fields into rows, there are
72+
* two options:
7373
*
74-
* NOTE: In this mode, `responsiveSteps` are ignored.
74+
* 1. Use `<vaadin-form-row>` to explicitly group fields into rows.
75+
*
76+
* 2. Enable the `autoRows` property to automatically arrange fields in available columns,
77+
* wrapping to a new row when necessary. `<br>` elements can be used to force a new row.
7578
*
7679
* @attr {boolean} auto-responsive
7780
*/
@@ -89,7 +92,7 @@ export declare class FormLayoutMixinClass {
8992
/**
9093
* When `autoResponsive` is enabled, defines the maximum number of columns
9194
* that the layout can create. The layout will create columns up to this
92-
* limit based on the available container width.
95+
* limit based on the available container width. The default value is `10`.
9396
*
9497
* @attr {number} max-columns
9598
*/
@@ -98,7 +101,8 @@ export declare class FormLayoutMixinClass {
98101
/**
99102
* When enabled with `autoResponsive`, distributes fields across columns
100103
* by placing each field in the next available column and wrapping to
101-
* the next row when the current row is full.
104+
* the next row when the current row is full. `<br>` elements can be
105+
* used to force a new row.
102106
*
103107
* @attr {boolean} auto-rows
104108
*/

packages/form-layout/src/vaadin-form-layout-mixin.js

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -82,16 +82,19 @@ export const FormLayoutMixin = (superClass) =>
8282
},
8383

8484
/**
85-
* When enabled, the layout automatically creates and adjusts columns based on
86-
* the container's width. Columns have a fixed width defined by `columnWidth`
87-
* and their number increases up to the limit set by `maxColumns`. The layout
88-
* dynamically adjusts the number of columns as the container size changes.
85+
* Enables the auto responsive mode where the component automatically creates and adjusts
86+
* columns based on the container's width. Columns have a fixed width defined by `columnWidth`
87+
* and their number increases up to the limit set by `maxColumns`. The component dynamically
88+
* adjusts the number of columns as the container size changes. When this mode is enabled,
89+
* the `responsiveSteps` are ignored.
8990
*
90-
* By default, each field is placed on a new row. To group fields in the same row,
91-
* wrap them in `<vaadin-form-row>` or enable the `autoRows` property, which allows
92-
* the layout to fit as many fields as possible in a row before wrapping.
91+
* By default, each field is placed on a new row. To organize fields into rows, there are
92+
* two options:
9393
*
94-
* NOTE: In this mode, `responsiveSteps` are ignored.
94+
* 1. Use `<vaadin-form-row>` to explicitly group fields into rows.
95+
*
96+
* 2. Enable the `autoRows` property to automatically arrange fields in available columns,
97+
* wrapping to a new row when necessary. `<br>` elements can be used to force a new row.
9598
*
9699
* @attr {boolean} auto-responsive
97100
*/
@@ -118,7 +121,7 @@ export const FormLayoutMixin = (superClass) =>
118121
/**
119122
* When `autoResponsive` is enabled, defines the maximum number of columns
120123
* that the layout can create. The layout will create columns up to this
121-
* limit based on the available container width.
124+
* limit based on the available container width. The default value is `10`.
122125
*
123126
* @attr {number} max-columns
124127
*/
@@ -131,7 +134,8 @@ export const FormLayoutMixin = (superClass) =>
131134
/**
132135
* When enabled with `autoResponsive`, distributes fields across columns
133136
* by placing each field in the next available column and wrapping to
134-
* the next row when the current row is full.
137+
* the next row when the current row is full. `<br>` elements can be
138+
* used to force a new row.
135139
*
136140
* @attr {boolean} auto-rows
137141
*/
@@ -299,6 +303,7 @@ export const FormLayoutMixin = (superClass) =>
299303
}
300304

301305
if (this.autoResponsive) {
306+
this.__updateCSSGridLayout();
302307
return;
303308
}
304309

@@ -385,6 +390,31 @@ export const FormLayoutMixin = (superClass) =>
385390
});
386391
}
387392

393+
/** @private */
394+
__updateCSSGridLayout() {
395+
let resetColumn = false;
396+
397+
[...this.children]
398+
.flatMap((child) => {
399+
return child.localName === 'vaadin-form-row' ? [...child.children] : child;
400+
})
401+
.forEach((child) => {
402+
const { parentElement } = child;
403+
404+
if (child.localName === 'br' || parentElement.localName === 'vaadin-form-row') {
405+
resetColumn = true;
406+
return;
407+
}
408+
409+
if (resetColumn && !isElementHidden(child) && parentElement.localName !== 'vaadin-form-row') {
410+
child.style.setProperty('--_column-start', 1);
411+
resetColumn = false;
412+
} else {
413+
child.style.removeProperty('--_column-start');
414+
}
415+
});
416+
}
417+
388418
/**
389419
* @protected
390420
* @override

packages/form-layout/src/vaadin-form-layout-styles.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,26 @@ export const formLayoutStyles = css`
8585
}
8686
8787
:host([auto-responsive][auto-rows]) #layout ::slotted(*) {
88+
grid-column-start: var(--_column-start, auto);
89+
}
90+
`;
91+
92+
export const formRowStyles = css`
93+
:host {
94+
display: contents;
95+
}
96+
97+
:host([hidden]) {
98+
display: none !important;
99+
}
100+
101+
::slotted(*) {
88102
grid-column-start: auto;
89103
}
104+
105+
::slotted(:first-child) {
106+
grid-column-start: 1;
107+
}
90108
`;
91109

92110
export const formItemStyles = css`
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* @license
3+
* Copyright (c) 2017 - 2025 Vaadin Ltd.
4+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5+
*/
6+
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
7+
8+
/**
9+
* `<vaadin-form-row>` is a web component for arranging fields into rows
10+
* inside a `<vaadin-form-layout>` that is set to autoResponsive mode.
11+
*
12+
* Each `<vaadin-form-row>` always starts on a new row. Fields that exceed
13+
* the available columns wrap to a new row, which then remains reserved
14+
* exclusively for the fields of that `<vaadin-form-row>` element.
15+
*/
16+
declare class FormRow extends ThemableMixin(HTMLElement) {}
17+
18+
declare global {
19+
interface HTMLElementTagNameMap {
20+
'vaadin-form-row': FormRow;
21+
}
22+
}
23+
24+
export { FormRow };
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/**
2+
* @license
3+
* Copyright (c) 2017 - 2025 Vaadin Ltd.
4+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5+
*/
6+
import { html, PolymerElement } from '@polymer/polymer/polymer-element.js';
7+
import { defineCustomElement } from '@vaadin/component-base/src/define.js';
8+
import { registerStyles, ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
9+
import { formRowStyles } from './vaadin-form-layout-styles.js';
10+
11+
registerStyles('vaadin-form-row', formRowStyles, { moduleId: 'vaadin-form-row-styles' });
12+
13+
/**
14+
* `<vaadin-form-row>` is a web component for arranging fields into rows
15+
* inside a `<vaadin-form-layout>` that is set to autoResponsive mode.
16+
*
17+
* Each `<vaadin-form-row>` always starts on a new row. Fields that exceed
18+
* the available columns wrap to a new row, which then remains reserved
19+
* exclusively for the fields of that `<vaadin-form-row>` element.
20+
*
21+
* @customElement
22+
* @extends HTMLElement
23+
* @mixes ThemableMixin
24+
*/
25+
class FormRow extends ThemableMixin(PolymerElement) {
26+
static get is() {
27+
return 'vaadin-form-row';
28+
}
29+
30+
static get template() {
31+
return html`<slot></slot>`;
32+
}
33+
}
34+
35+
defineCustomElement(FormRow);
36+
37+
export { FormRow };
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/**
2+
* @license
3+
* Copyright (c) 2017 - 2025 Vaadin Ltd.
4+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5+
*/
6+
export * from './vaadin-form-row.js';
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* @license
3+
* Copyright (c) 2017 - 2025 Vaadin Ltd.
4+
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
5+
*/
6+
import { html, LitElement } from 'lit';
7+
import { defineCustomElement } from '@vaadin/component-base/src/define.js';
8+
import { PolylitMixin } from '@vaadin/component-base/src/polylit-mixin.js';
9+
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
10+
import { formRowStyles } from './vaadin-form-layout-styles.js';
11+
12+
/**
13+
* LitElement based version of `<vaadin-form-row>` web component.
14+
*
15+
* ## Disclaimer
16+
*
17+
* This component is an experiment and not yet a part of Vaadin platform.
18+
* There is no ETA regarding specific Vaadin version where it'll land.
19+
* Feel free to try this code in your apps as per Apache 2.0 license.
20+
*/
21+
class FormRow extends ThemableMixin(PolylitMixin(LitElement)) {
22+
static get is() {
23+
return 'vaadin-form-row';
24+
}
25+
26+
static get styles() {
27+
return formRowStyles;
28+
}
29+
30+
/** @protected */
31+
render() {
32+
return html`<slot></slot>`;
33+
}
34+
}
35+
36+
defineCustomElement(FormRow);
37+
38+
export { FormRow };
Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* @web/test-runner snapshot v1 */
22
export const snapshots = {};
33

4-
snapshots["vaadin-form-layout auto-responsive host default"] =
4+
snapshots["vaadin-form-layout auto-responsive basic host default"] =
55
`<vaadin-form-layout
66
auto-responsive=""
77
style="--_column-width: 13em; --_max-columns: 10;"
@@ -12,53 +12,61 @@ snapshots["vaadin-form-layout auto-responsive host default"] =
1212
<input placeholder="Phone">
1313
</vaadin-form-layout>
1414
`;
15-
/* end snapshot vaadin-form-layout auto-responsive host default */
15+
/* end snapshot vaadin-form-layout auto-responsive basic host default */
1616

17-
snapshots["vaadin-form-layout auto-responsive host autoRows"] =
17+
snapshots["vaadin-form-layout auto-responsive basic host maxColumns"] =
1818
`<vaadin-form-layout
1919
auto-responsive=""
20-
auto-rows=""
21-
style="--_column-width: 13em; --_max-columns: 10;"
20+
style="--_column-width: 13em; --_max-columns: 3;"
2221
>
2322
<input placeholder="First name">
2423
<input placeholder="Last name">
2524
<input placeholder="Email">
2625
<input placeholder="Phone">
2726
</vaadin-form-layout>
2827
`;
29-
/* end snapshot vaadin-form-layout auto-responsive host autoRows */
28+
/* end snapshot vaadin-form-layout auto-responsive basic host maxColumns */
3029

31-
snapshots["vaadin-form-layout auto-responsive host maxColumns"] =
30+
snapshots["vaadin-form-layout auto-responsive basic host columnWidth"] =
3231
`<vaadin-form-layout
3332
auto-responsive=""
34-
style="--_column-width: 13em; --_max-columns: 3;"
33+
style="--_column-width: 15em; --_max-columns: 10;"
3534
>
3635
<input placeholder="First name">
3736
<input placeholder="Last name">
3837
<input placeholder="Email">
3938
<input placeholder="Phone">
4039
</vaadin-form-layout>
4140
`;
42-
/* end snapshot vaadin-form-layout auto-responsive host maxColumns */
41+
/* end snapshot vaadin-form-layout auto-responsive basic host columnWidth */
42+
43+
snapshots["vaadin-form-layout auto-responsive basic shadow default"] =
44+
`<div id="layout">
45+
<slot id="slot">
46+
</slot>
47+
</div>
48+
`;
49+
/* end snapshot vaadin-form-layout auto-responsive basic shadow default */
4350

44-
snapshots["vaadin-form-layout auto-responsive host columnWidth"] =
51+
snapshots["vaadin-form-layout auto-responsive autoRows default"] =
4552
`<vaadin-form-layout
4653
auto-responsive=""
47-
style="--_column-width: 15em; --_max-columns: 10;"
54+
auto-rows=""
55+
style="--_column-width: 13em; --_max-columns: 10;"
4856
>
4957
<input placeholder="First name">
5058
<input placeholder="Last name">
51-
<input placeholder="Email">
59+
<br>
60+
<input
61+
hidden=""
62+
placeholder="Address"
63+
>
64+
<input
65+
placeholder="Email"
66+
style="--_column-start: 1;"
67+
>
5268
<input placeholder="Phone">
5369
</vaadin-form-layout>
5470
`;
55-
/* end snapshot vaadin-form-layout auto-responsive host columnWidth */
56-
57-
snapshots["vaadin-form-layout auto-responsive shadow default"] =
58-
`<div id="layout">
59-
<slot id="slot">
60-
</slot>
61-
</div>
62-
`;
63-
/* end snapshot vaadin-form-layout auto-responsive shadow default */
71+
/* end snapshot vaadin-form-layout auto-responsive autoRows default */
6472

0 commit comments

Comments
 (0)