Skip to content

Commit 3109a28

Browse files
committed
fix(Form): fix tab order
Closes #645
1 parent 972ee49 commit 3109a28

File tree

3 files changed

+125
-51
lines changed

3 files changed

+125
-51
lines changed

packages/main/src/components/Form/Form.jss.ts

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,52 @@ const styles = {
88
columnGap: '0.5rem',
99
gridTemplateColumns: `repeat(12, 1fr)`,
1010
'--ui5wcr_form_full_span': 'span 12',
11-
'--ui5wcr_form_label_text_align': 'end'
11+
'--ui5wcr_form_label_text_align': 'end',
12+
'&[data-columns="1"]': {},
13+
'&[data-columns="2"]': {
14+
gridTemplateColumns: `repeat(24, 1fr)`,
15+
'--ui5wcr_form_full_span': 'span 24'
16+
},
17+
'&[data-columns="3"]': {
18+
gridTemplateColumns: `repeat(36, 1fr)`,
19+
'--ui5wcr_form_full_span': 'span 36'
20+
},
21+
'&[data-columns="4"]': {
22+
gridTemplateColumns: `repeat(48, 1fr)`,
23+
'--ui5wcr_form_full_span': 'span 48'
24+
},
25+
'&[data-columns="5"]': {
26+
gridTemplateColumns: `repeat(60, 1fr)`,
27+
'--ui5wcr_form_full_span': 'span 60'
28+
},
29+
'&[data-columns="6"]': {
30+
gridTemplateColumns: `repeat(72, 1fr)`,
31+
'--ui5wcr_form_full_span': 'span 72'
32+
},
33+
'&[data-columns="7"]': {
34+
gridTemplateColumns: `repeat(84, 1fr)`,
35+
'--ui5wcr_form_full_span': 'span 84'
36+
},
37+
'&[data-columns="8"]': {
38+
gridTemplateColumns: `repeat(96, 1fr)`,
39+
'--ui5wcr_form_full_span': 'span 96'
40+
},
41+
'&[data-columns="9"]': {
42+
gridTemplateColumns: `repeat(108, 1fr)`,
43+
'--ui5wcr_form_full_span': 'span 108'
44+
},
45+
'&[data-columns="10"]': {
46+
gridTemplateColumns: `repeat(120, 1fr)`,
47+
'--ui5wcr_form_full_span': 'span 120'
48+
},
49+
'&[data-columns="11"]': {
50+
gridTemplateColumns: `repeat(132, 1fr)`,
51+
'--ui5wcr_form_full_span': 'span 132'
52+
},
53+
'&[data-columns="12"]': {
54+
gridTemplateColumns: `repeat(144, 1fr)`,
55+
'--ui5wcr_form_full_span': 'span 144'
56+
}
1257
},
1358
formTitle: {
1459
borderBottom: `1px solid ${ThemingParameters.sapGroup_TitleBorderColor}`,

packages/main/src/components/Form/index.tsx

Lines changed: 62 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -146,79 +146,96 @@ const Form: FC<FormPropTypes> = forwardRef((props: FormPropTypes, ref: Ref<HTMLD
146146

147147
const classes = useStyles();
148148

149+
const currentNumberOfColumns = columnsMap.get(currentRange);
150+
const currentLabelSpan = labelSpanMap.get(currentRange);
151+
149152
const [formGroups, updatedTitle] = useMemo(() => {
150153
const computedFormGroups: any[] = [];
151154

152155
if (Children.count(children) === 1 && !title && (children as ReactElement).props.title?.length > 0) {
153156
return [cloneElement(children as ReactElement, { title: null }), (children as ReactElement).props.title];
154157
}
155158

156-
const currentColumnCount = columnsMap.get(currentRange);
159+
const currentColumnCount = currentNumberOfColumns;
157160
if (currentColumnCount === 1) {
158161
return [children, title];
159162
}
160163

161-
// if we are running on a large desktop device, we need some special logic for splitting up the form groups
162-
const columns = createArrayOfLength(currentColumnCount);
163-
Children.toArray(children).forEach((child, index) => {
164-
columns[index % currentColumnCount].push(child);
165-
});
164+
const rows = [];
165+
const childrenArray = Children.toArray(children);
166+
const estimatedNumberOfGroupRows = childrenArray.length / currentColumnCount;
167+
for (let i = 0; i < estimatedNumberOfGroupRows; i++) {
168+
rows[i] = childrenArray.slice(i * currentColumnCount, i * currentColumnCount + currentColumnCount);
169+
}
166170

167-
// no column can have more rows than the first column
168-
let totalRowCount = 1;
169-
for (let i = 0; i < columns[0].length; i++) {
170-
const formGroupsForRow = columns.map((groups) => groups[i]);
171-
const childrenOfFormGroups = createArrayOfLength(currentColumnCount);
172-
173-
formGroupsForRow.forEach((formGroup, index) => {
174-
if (formGroup) {
175-
computedFormGroups.push(
176-
<Title
177-
level={TitleLevel.H5}
178-
style={{ paddingBottom: '0.75rem', gridColumn: 'span 12' }}
179-
key={`title-col-${index}-row-${totalRowCount}`}
180-
>
181-
{(formGroup as ReactElement)?.props?.title ?? ''}
182-
</Title>
183-
);
184-
childrenOfFormGroups[index] =
185-
((formGroup as ReactElement).type as any).displayName === 'FormItem'
186-
? [formGroup]
187-
: Children.toArray((formGroup as ReactElement)?.props?.children);
171+
const maxRowsPerRow: number[] = [];
172+
rows.forEach((rowGroup: ReactElement[], rowIndex) => {
173+
const numberOfRowsOfEachForm = rowGroup.map((row) => {
174+
if ((row.type as any).displayName === 'FormItem') {
175+
return 1;
188176
}
177+
return Children.count(row.props.children) + (row.props?.title?.length > 0 ? 1 : 0);
189178
});
190-
totalRowCount++;
191179

192-
const maxChildCount = Math.max(...childrenOfFormGroups.map((c) => c.length));
180+
maxRowsPerRow[rowIndex] = Math.max(...numberOfRowsOfEachForm);
181+
});
182+
183+
let totalRowCount = 2;
184+
185+
rows.forEach((column: ReactElement[], rowIndex) => {
186+
const rowsForThisRow = maxRowsPerRow[rowIndex];
187+
column.forEach((cell, columnIndex) => {
188+
computedFormGroups.push(
189+
<Title
190+
level={TitleLevel.H5}
191+
style={{
192+
paddingBottom: '0.75rem',
193+
gridColumnEnd: 'span 12',
194+
gridColumnStart: columnIndex * 12 + 1,
195+
gridRowStart: totalRowCount
196+
}}
197+
key={`title-col-${columnIndex}-row-${totalRowCount}`}
198+
>
199+
{cell?.props?.title ?? ''}
200+
</Title>
201+
);
193202

194-
for (let childIndex = 0; childIndex < maxChildCount; childIndex++) {
195-
childrenOfFormGroups.forEach((child, columnIndex) => {
196-
if (child[childIndex]) {
197-
// @ts-ignore
203+
for (let i = 0; i < rowsForThisRow; i++) {
204+
const itemToRender =
205+
(cell.type as any).displayName === 'FormGroup'
206+
? Children.toArray(cell.props.children)[i]
207+
: (cell.type as any).displayName === 'FormItem' && i === 0
208+
? cell
209+
: null;
210+
211+
if (itemToRender) {
198212
computedFormGroups.push(
199-
cloneElement(child[childIndex] as ReactElement, {
200-
key: `col-${columnIndex}-row-${totalRowCount}`,
201-
columnIndex
213+
cloneElement(itemToRender as ReactElement, {
214+
key: `col-${columnIndex}-row-${totalRowCount + i}`,
215+
columnIndex,
216+
rowIndex: totalRowCount + i + 1,
217+
labelSpan: currentLabelSpan
202218
})
203219
);
204220
}
205-
});
206-
totalRowCount++;
221+
}
222+
});
223+
totalRowCount += rowsForThisRow;
224+
if (rowsForThisRow === 1) {
225+
totalRowCount += 1;
207226
}
208-
}
227+
});
209228

210229
return [computedFormGroups, title];
211-
}, [children, currentRange, title, columnsMap.get(currentRange)]);
230+
}, [children, currentRange, title, currentNumberOfColumns, currentLabelSpan]);
212231

213232
const passThroughProps = usePassThroughHtmlProps(props);
214233

215234
const formClassNames = StyleClassHelper.of(classes.form).putIfPresent(className);
216235

217236
const gridStyles: CSSProperties = {};
218-
gridStyles['--ui5wcr_form_content_span'] = 12 - labelSpanMap.get(currentRange);
219-
gridStyles['--ui5wcr_form_label_span'] = labelSpanMap.get(currentRange);
220-
gridStyles['--ui5wcr_form_full_span'] = `span ${columnsMap.get(currentRange) * 12}`;
221-
gridStyles.gridTemplateColumns = `repeat(${columnsMap.get(currentRange) * 12}, 1fr)`;
237+
gridStyles['--ui5wcr_form_content_span'] = 12 - currentLabelSpan;
238+
gridStyles['--ui5wcr_form_label_span'] = currentLabelSpan;
222239

223240
// special case for phones or label span 12
224241
if (gridStyles['--ui5wcr_form_content_span'] <= 0) {
@@ -236,6 +253,7 @@ const Form: FC<FormPropTypes> = forwardRef((props: FormPropTypes, ref: Ref<HTMLD
236253
...gridStyles,
237254
...(style || {})
238255
}}
256+
data-columns={currentNumberOfColumns}
239257
{...passThroughProps}
240258
>
241259
{updatedTitle && (

packages/main/src/components/FormItem/index.tsx

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ export interface FormItemProps {
1313
children: ReactNode | ReactNodeArray;
1414
}
1515

16+
interface InternalProps extends FormItemProps {
17+
columnIndex?: number;
18+
labelSpan?: number;
19+
rowIndex?: number;
20+
}
21+
1622
const useStyles = createComponentStyles(
1723
{
1824
label: {
@@ -28,17 +34,20 @@ const useStyles = createComponentStyles(
2834
);
2935

3036
const FormItem: FC<FormItemProps> = (props: FormItemProps) => {
31-
const { label, children } = props;
37+
const { label, children, columnIndex, rowIndex, labelSpan } = props as InternalProps;
3238

3339
const classes = useStyles();
3440

35-
// @ts-ignore
36-
const gridColumnStart = (props.columnIndex ?? 0) * 12 + 1;
41+
const gridColumnStart = (columnIndex ?? 0) * 12 + 1;
42+
const gridRowStart = rowIndex ?? 0;
43+
44+
const contentGridColumnStart =
45+
columnIndex != null ? (labelSpan === 12 ? gridColumnStart : gridColumnStart + (labelSpan ?? 0)) : undefined;
3746

3847
return (
3948
<>
4049
{typeof label === 'string' && (
41-
<Label className={classes.label} style={{ gridColumnStart }} wrap>
50+
<Label className={classes.label} style={{ gridColumnStart, gridRowStart }} wrap>
4251
{label ? `${label}:` : ''}
4352
</Label>
4453
)}
@@ -48,12 +57,14 @@ const FormItem: FC<FormItemProps> = (props: FormItemProps) => {
4857
{
4958
wrap: label.props.wrap ?? true,
5059
className: `${classes.label} ${label.props.className ?? ''}`,
51-
style: { gridColumnStart, ...(label.props.style || {}) }
60+
style: { gridColumnStart, gridRowStart, ...(label.props.style || {}) }
5261
},
5362
label.props.children ? `${label.props.children}:` : ''
5463
)}
5564

56-
<div className={classes.content}>{children}</div>
65+
<div className={classes.content} style={{ gridColumnStart: contentGridColumnStart, gridRowStart }}>
66+
{children}
67+
</div>
5768
</>
5869
);
5970
};

0 commit comments

Comments
 (0)