@@ -3,12 +3,16 @@ import type { Slot, DefaultSlot } from "@ui5/webcomponents-base/dist/UI5Element.
33import customElement from "@ui5/webcomponents-base/dist/decorators/customElement.js" ;
44import property from "@ui5/webcomponents-base/dist/decorators/property.js" ;
55import i18n from "@ui5/webcomponents-base/dist/decorators/i18n.js" ;
6- import slot from "@ui5/webcomponents-base/dist/decorators/slot.js" ;
6+ import slot from "@ui5/webcomponents-base/dist/decorators/slot-strict .js" ;
77import jsxRenderer from "@ui5/webcomponents-base/dist/renderer/JsxRenderer.js" ;
88import type { AriaRole } from "@ui5/webcomponents-base" ;
99import type I18nBundle from "@ui5/webcomponents-base/dist/i18nBundle.js" ;
1010import { getEffectiveAriaLabelText } from "@ui5/webcomponents-base/dist/util/AccessibilityTextsHelper.js" ;
1111
12+ // Utils
13+ import { getFormItemLayoutValue , getGroupsColSpan } from "./form-utils/FormUtils.js" ;
14+ import type { Breakpoint } from "./form-utils/FormUtils.js" ;
15+
1216// Template
1317import FormTemplate from "./FormTemplate.js" ;
1418
@@ -22,20 +26,6 @@ import type TitleLevel from "./types/TitleLevel.js";
2226
2327import { FORM_ACCESSIBLE_NAME } from "./generated/i18n/i18n-defaults.js" ;
2428
25- const additionalStylesMap = new Map < string , string > ( ) ;
26-
27- const StepColumn = {
28- "S" : 1 ,
29- "M" : 2 ,
30- "L" : 3 ,
31- "XL" : 6 ,
32- } ;
33-
34- const breakpoints = [ "S" , "M" , "L" , "Xl" ] ;
35- const MAX_FORM_ITEM_CELLS = 12 ;
36- const DEFAULT_FORM_ITEM_LAYOUT = "4fr 8fr 0fr" ;
37- const DEFAULT_FORM_ITEM_LAYOUT_S = "1fr" ;
38-
3929/**
4030 * Interface for components that can be slotted inside `ui5-form` as items.
4131 * @public
@@ -352,6 +342,10 @@ class Form extends UI5Element {
352342 *
353343 * **Note:** Mixing FormGroups and standalone FormItems (not belonging to a group) is not supported.
354344 * Either use FormGroups and make sure all FormItems are part of a FormGroup, or use just FormItems without any FormGroups.
345+ *
346+ * **Note:** As of version 2.21.0 the support for standalone FormItems (not belonging to a group) is deprecated.
347+ * We recommend using FormGroups, as they provide better accessibility and layout options.
348+ *
355349 * @public
356350 */
357351 @slot ( {
@@ -368,40 +362,37 @@ class Form extends UI5Element {
368362 /**
369363 * @private
370364 */
371- @property ( { type : Number } )
365+ @property ( { type : Number , noAttribute : true } )
372366 columnsS = 1 ;
373- @property ( { type : Number } )
367+ @property ( { type : Number , noAttribute : true } )
374368 labelSpanS = 12
375- @property ( { type : Number } )
369+ @property ( { type : Number , noAttribute : true } )
376370 emptySpanS = 0
377371
378- @property ( { type : Number } )
372+ @property ( { type : Number , noAttribute : true } )
379373 columnsM = 1 ;
380- @property ( { type : Number } )
374+ @property ( { type : Number , noAttribute : true } )
381375 labelSpanM = 4 ;
382- @property ( { type : Number } )
376+ @property ( { type : Number , noAttribute : true } )
383377 emptySpanM = 0
384378
385- @property ( { type : Number } )
379+ @property ( { type : Number , noAttribute : true } )
386380 columnsL = 2 ;
387- @property ( { type : Number } )
381+ @property ( { type : Number , noAttribute : true } )
388382 labelSpanL = 4 ;
389- @property ( { type : Number } )
383+ @property ( { type : Number , noAttribute : true } )
390384 emptySpanL = 0
391385
392- @property ( { type : Number } )
386+ @property ( { type : Number , noAttribute : true } )
393387 columnsXl = 3 ;
394- @property ( { type : Number } )
388+ @property ( { type : Number , noAttribute : true } )
395389 labelSpanXl = 4 ;
396- @property ( { type : Number } )
390+ @property ( { type : Number , noAttribute : true } )
397391 emptySpanXl = 0 ;
398392
399393 onBeforeRendering ( ) {
400394 // Parse the layout and set it to the FormGroups/FormItems.
401- this . setColumnLayout ( ) ;
402-
403- // Parse the labelSpan and emptySpan and set it to the FormGroups/FormItems.
404- this . setFormItemLayout ( ) ;
395+ this . parseLayoutConfiguration ( ) ;
405396
406397 // Define how many columns a group should take.
407398 this . setGroupsColSpan ( ) ;
@@ -411,15 +402,11 @@ class Form extends UI5Element {
411402 }
412403
413404 onAfterRendering ( ) {
414- // Create additional CSS for number of columns that are not supported by default.
415- this . createAdditionalCSSStyleSheet ( ) ;
416-
417405 this . setFastNavGroup ( ) ;
418406 }
419407
420- setColumnLayout ( ) {
421- const layoutArr = this . layout . split ( " " ) ;
422- layoutArr . forEach ( ( breakpoint : string ) => {
408+ parseLayoutConfiguration ( ) {
409+ this . layout . split ( " " ) . forEach ( ( breakpoint : string ) => {
423410 if ( breakpoint . startsWith ( "S" ) ) {
424411 this . columnsS = parseInt ( breakpoint . slice ( 1 ) ) ;
425412 } else if ( breakpoint . startsWith ( "M" ) ) {
@@ -430,9 +417,7 @@ class Form extends UI5Element {
430417 this . columnsXl = parseInt ( breakpoint . slice ( 2 ) ) ;
431418 }
432419 } ) ;
433- }
434420
435- parseFormItemSpan ( ) {
436421 this . labelSpan . split ( " " ) . forEach ( ( breakpoint : string ) => {
437422 if ( breakpoint . startsWith ( "S" ) ) {
438423 this . labelSpanS = parseInt ( breakpoint . slice ( 1 ) ) ;
@@ -458,44 +443,25 @@ class Form extends UI5Element {
458443 } ) ;
459444 }
460445
461- setFormItemLayout ( ) {
462- this . parseFormItemSpan ( ) ;
463-
464- [
465- {
466- breakpoint : "S" ,
467- labelSpan : this . labelSpanS ,
468- emptySpan : this . emptySpanS ,
469- } ,
470- {
471- breakpoint : "M" ,
472- labelSpan : this . labelSpanM ,
473- emptySpan : this . emptySpanM ,
474- } ,
475- {
476- breakpoint : "L" ,
477- labelSpan : this . labelSpanL ,
478- emptySpan : this . emptySpanL ,
479- } ,
480- {
481- breakpoint : "XL" ,
482- labelSpan : this . labelSpanXl ,
483- emptySpan : this . emptySpanXl ,
484- } ,
485- ] . forEach ( layout => {
486- if ( this . isValidFormItemLayout ( layout . labelSpan , layout . emptySpan ) ) {
487- const formItemLayout = layout . labelSpan === MAX_FORM_ITEM_CELLS ? `1fr` : `${ layout . labelSpan } fr ${ MAX_FORM_ITEM_CELLS - ( layout . labelSpan + layout . emptySpan ) } fr ${ layout . emptySpan } fr` ;
488- this . style . setProperty ( `--ui5-form-item-layout-${ layout . breakpoint } ` , formItemLayout ) ;
489- } else {
490- // eslint-disable-next-line
491- console . warn ( `Form :: invalid usage of emptySpan and/or labelSpan in ${ layout . breakpoint } size. The labelSpan must be <=12 and when emptySpace is used - their combined values must not exceed 11.` )
492- this . style . setProperty ( `--ui5-form-item-layout-${ layout . breakpoint } ` , layout . breakpoint === "S" ? DEFAULT_FORM_ITEM_LAYOUT_S : DEFAULT_FORM_ITEM_LAYOUT ) ;
493- }
494- } ) ;
495- }
446+ getFormItemLayout ( breakpoint : Breakpoint ) {
447+ let labelSpan ,
448+ emptySpan ;
449+
450+ if ( breakpoint === "S" ) {
451+ labelSpan = this . labelSpanS ;
452+ emptySpan = this . emptySpanS ;
453+ } else if ( breakpoint === "M" ) {
454+ labelSpan = this . labelSpanM ;
455+ emptySpan = this . emptySpanM ;
456+ } else if ( breakpoint === "L" ) {
457+ labelSpan = this . labelSpanL ;
458+ emptySpan = this . emptySpanL ;
459+ } else if ( breakpoint === "XL" ) {
460+ labelSpan = this . labelSpanXl ;
461+ emptySpan = this . emptySpanXl ;
462+ }
496463
497- isValidFormItemLayout ( labelSpan : number , emptySpan : number ) {
498- return emptySpan === 0 ? labelSpan <= MAX_FORM_ITEM_CELLS : labelSpan + emptySpan <= MAX_FORM_ITEM_CELLS - 1 ;
464+ return getFormItemLayoutValue ( breakpoint , labelSpan , emptySpan ) ;
499465 }
500466
501467 setFastNavGroup ( ) {
@@ -517,44 +483,13 @@ class Form extends UI5Element {
517483 } ) ;
518484
519485 sortedItems . forEach ( ( item : IFormItem , idx : number ) => {
520- item . colsXl = this . getGroupsColSpan ( this . columnsXl , itemsCount , idx , item ) ;
521- item . colsL = this . getGroupsColSpan ( this . columnsL , itemsCount , idx , item ) ;
522- item . colsM = this . getGroupsColSpan ( this . columnsM , itemsCount , idx , item ) ;
523- item . colsS = this . getGroupsColSpan ( this . columnsS , itemsCount , idx , item ) ;
486+ item . colsXl = getGroupsColSpan ( this . columnsXl , itemsCount , idx , item ) ;
487+ item . colsL = getGroupsColSpan ( this . columnsL , itemsCount , idx , item ) ;
488+ item . colsM = getGroupsColSpan ( this . columnsM , itemsCount , idx , item ) ;
489+ item . colsS = getGroupsColSpan ( this . columnsS , itemsCount , idx , item ) ;
524490 } ) ;
525491 }
526492
527- getGroupsColSpan ( cols : number , groups : number , index : number , group : IFormItem ) : number {
528- // Case 0: column span is set from outside.
529- if ( group . columnSpan ) {
530- return group . columnSpan ;
531- }
532-
533- // CASE 1: The number of available columns match the number of groups, or only 1 column is available - each group takes 1 column.
534- // For example: 1 column - 1 group, 2 columns - 2 groups, 3 columns - 3 groups, 4columns - 4 groups
535- if ( cols === 1 || cols <= groups ) {
536- return 1 ;
537- }
538-
539- // CASE 2: The number of available columns IS multiple of the number of groups.
540- // For example: 2 column - 1 group, 3 columns - 1 groups, 4 columns - 1 group, 4 columns - 2 groups
541- if ( cols % groups === 0 ) {
542- return cols / groups ;
543- }
544-
545- // CASE 3: The number of available columns IS NOT multiple of the number of groups.
546- const MIN_COL_SPAN = 1 ;
547- const delta = cols - groups ;
548-
549- // 7 cols & 4 groups => 2, 2, 2, 1
550- if ( delta <= groups ) {
551- return index < delta ? MIN_COL_SPAN + 1 : MIN_COL_SPAN ;
552- }
553-
554- // 7 cols & 3 groups => 3, 2, 2
555- return index === 0 ? MIN_COL_SPAN + ( delta - groups ) + 1 : MIN_COL_SPAN + 1 ;
556- }
557-
558493 setItemsState ( ) {
559494 this . items . forEach ( ( item : IFormItem ) => {
560495 item . itemSpacing = this . itemSpacing ;
@@ -600,28 +535,6 @@ class Form extends UI5Element {
600535
601536 get groupItemsInfo ( ) : Array < GroupItemsInfo > {
602537 return this . items . map ( ( groupItem : IFormItem , index : number ) => {
603- const items = this . getItemsInfo ( ( Array . from ( groupItem . children ) as Array < IFormItem > ) ) ;
604- breakpoints . forEach ( breakpoint => {
605- const cols = ( ( groupItem [ `cols${ breakpoint } ` as keyof IFormItem ] ) as number || 1 ) ;
606- const rows = Math . ceil ( items . length / cols ) ;
607- const total = cols * rows ;
608- const lastRowColumns = ( cols - ( total - items . length ) - 1 ) ; // all other indecies start from 0
609- let currentItem = 0 ;
610-
611- for ( let i = 0 ; i < total ; i ++ ) {
612- const column = Math . floor ( i / rows ) ;
613- const row = i % rows ;
614-
615- if ( row === rows - 1 && column > lastRowColumns ) {
616- // eslint-disable-next-line no-continue
617- continue ;
618- }
619-
620- items [ currentItem ] . item . style . setProperty ( `--ui5-form-item-order-${ breakpoint } ` , `${ column + row * cols } ` ) ;
621- currentItem ++ ;
622- }
623- } ) ;
624-
625538 const accessibleNameRef = ( groupItem as FormGroup ) . effectiveAccessibleNameRef ;
626539
627540 return {
@@ -644,88 +557,9 @@ class Form extends UI5Element {
644557 return ( items || this . items ) . map ( ( item : IFormItem ) => {
645558 return {
646559 item,
647- // eslint-disable-next-line
648- // TODO: remove classes and classMap after deleting the hbs template
649- classes : item . columnSpan ? `ui5-form-item-span-${ item . columnSpan } ` : "" ,
650- classMap : {
651- [ `ui5-form-item-span-${ item . columnSpan } ` ] : item . columnSpan !== undefined ,
652- } ,
653560 } ;
654561 } ) ;
655562 }
656-
657- createAdditionalCSSStyleSheet ( ) {
658- [
659- { breakpoint : "S" , columns : this . columnsS } ,
660- { breakpoint : "M" , columns : this . columnsM } ,
661- { breakpoint : "L" , columns : this . columnsL } ,
662- { breakpoint : "XL" , columns : this . columnsXl } ,
663- ] . forEach ( step => {
664- const additionalStyle : string | undefined = this . getAdditionalCSS ( step . breakpoint , step . columns ) ;
665-
666- if ( additionalStyle ) {
667- this . shadowRoot ! . adoptedStyleSheets = [ ...this . shadowRoot ! . adoptedStyleSheets , this . getCSSStyleSheet ( additionalStyle ) ] ;
668- }
669- } ) ;
670- }
671-
672- getAdditionalCSS ( step : string , colsNumber : number ) : string | undefined {
673- if ( StepColumn [ step as keyof typeof StepColumn ] >= colsNumber ) {
674- return ;
675- }
676-
677- const key = `${ step } -${ colsNumber } ` ;
678-
679- if ( ! additionalStylesMap . has ( key ) ) {
680- let containerQuery ;
681- let supporedColumnsNumber = StepColumn . S ;
682- let stepSpanCSS = "" ;
683- let cols = colsNumber ;
684-
685- if ( step === "S" ) {
686- supporedColumnsNumber = StepColumn . S ;
687- containerQuery = `@container (max-width: 599px) {` ;
688- } else if ( step === "M" ) {
689- supporedColumnsNumber = StepColumn . M ;
690- containerQuery = `@container (width > 599px) and (width < 1024px) {` ;
691- } else if ( step === "L" ) {
692- supporedColumnsNumber = StepColumn . L ;
693- containerQuery = `@container (width > 1023px) and (width < 1439px) {` ;
694- } else if ( step === "XL" ) {
695- containerQuery = `@container (min-width: 1440px) {` ;
696- supporedColumnsNumber = StepColumn . XL ;
697- }
698-
699- while ( cols > supporedColumnsNumber ) {
700- stepSpanCSS += `
701- :host([columns-${ step . toLocaleLowerCase ( ) } ="${ cols } "]) .ui5-form-layout {
702- grid-template-columns: repeat(${ cols } , 1fr);
703- }
704-
705- .ui5-form-column-span${ step } -${ cols } ,
706- .ui5-form-item-span-${ cols } {
707- grid-column: span ${ cols } ;
708- }
709-
710- .ui5-form-column-span${ step } -${ cols } .ui5-form-group-layout {
711- grid-template-columns: repeat(${ cols } , 1fr);
712- }
713- ` ;
714- cols -- ;
715- }
716-
717- const css = `${ containerQuery } ${ stepSpanCSS } }` ;
718- additionalStylesMap . set ( key , css ) ;
719- }
720-
721- return additionalStylesMap . get ( key ) ! ;
722- }
723-
724- getCSSStyleSheet ( cssText : string ) : CSSStyleSheet {
725- const style = new CSSStyleSheet ( ) ;
726- style . replaceSync ( cssText ) ;
727- return style ;
728- }
729563}
730564
731565Form . define ( ) ;
0 commit comments