@@ -7,6 +7,8 @@ import NavigationMode from "@ui5/webcomponents-base/dist/types/NavigationMode.js
7
7
import Float from "@ui5/webcomponents-base/dist/types/Float.js" ;
8
8
import ResizeHandler from "@ui5/webcomponents-base/dist/delegate/ResizeHandler.js" ;
9
9
import { isPhone } from "@ui5/webcomponents-base/dist/Device.js" ;
10
+ import Button from "@ui5/webcomponents/dist/Button.js" ;
11
+ import ResponsivePopover from "@ui5/webcomponents/dist/ResponsivePopover.js" ;
10
12
11
13
// Texts
12
14
import {
@@ -20,7 +22,18 @@ import WizardStep from "./WizardStep.js";
20
22
21
23
// Template and Styles
22
24
import WizardTemplate from "./generated/templates/WizardTemplate.lit.js" ;
25
+ import WizardPopoverTemplate from "./generated/templates/WizardPopoverTemplate.lit.js" ;
23
26
import WizardCss from "./generated/themes/Wizard.css.js" ;
27
+ import WizardPopoverCss from "./generated/themes/WizardPopover.css.js" ;
28
+
29
+
30
+ const MIN_STEP_WIDTH_NO_TITLE = 64 ;
31
+ const MIN_STEP_WIDTH_WITH_TITLE = 200 ;
32
+
33
+ const EXPANDED_STEP = "data-ui5-wizard-expanded-tab" ;
34
+ const AFTER_EXPANDED_STEP = "data-ui5-wizard-expanded-tab-next" ;
35
+ const AFTER_CURRENT_STEP = "data-ui5-wizard-after-current-tab" ;
36
+ const BEFORE_EXPANDED_STEP = "data-ui5-wizard-expanded-tab-prev" ;
24
37
25
38
/**
26
39
* @public
@@ -48,6 +61,11 @@ const metadata = {
48
61
width : {
49
62
type : Float ,
50
63
} ,
64
+
65
+ _groupedTabs : {
66
+ type : String ,
67
+ multiple : true ,
68
+ } ,
51
69
} ,
52
70
slots : /** @lends sap.ui.webcomponents.fiori.Wizard.prototype */ {
53
71
/**
@@ -165,6 +183,9 @@ class Wizard extends UI5Element {
165
183
// e.g. the steps' starting point.
166
184
this . stepScrollOffsets = [ ] ;
167
185
186
+ // Stores references to the grouped steps.
187
+ this . _groupedTabs = [ ] ;
188
+
168
189
// Keeps track of the selected step index.
169
190
this . selectedStepIndex = 0 ;
170
191
@@ -194,16 +215,35 @@ class Wizard extends UI5Element {
194
215
return litRender ;
195
216
}
196
217
218
+ get classes ( ) {
219
+ return {
220
+ popover : {
221
+ "ui5-wizard-responsive-popover" : true ,
222
+ "ui5-wizard-popover" : ! isPhone ( ) ,
223
+ "ui5-wizard-dialog" : isPhone ( ) ,
224
+ } ,
225
+ } ;
226
+ }
227
+
197
228
static get styles ( ) {
198
229
return WizardCss ;
199
230
}
200
231
232
+ static get staticAreaStyles ( ) {
233
+ return WizardPopoverCss ;
234
+ }
235
+
201
236
static get template ( ) {
202
237
return WizardTemplate ;
203
238
}
204
239
205
240
static get dependencies ( ) {
206
- return [ WizardTab , WizardStep ] ;
241
+ return [
242
+ WizardTab ,
243
+ WizardStep ,
244
+ ResponsivePopover ,
245
+ Button ,
246
+ ] ;
207
247
}
208
248
209
249
static async onDefine ( ) {
@@ -222,6 +262,10 @@ class Wizard extends UI5Element {
222
262
return 80 ;
223
263
}
224
264
265
+ static get staticAreaTemplate ( ) {
266
+ return WizardPopoverTemplate ;
267
+ }
268
+
225
269
onEnterDOM ( ) {
226
270
ResizeHandler . register ( this , this . _onResize ) ;
227
271
}
@@ -364,6 +408,145 @@ class Wizard extends UI5Element {
364
408
*/
365
409
onResize ( ) {
366
410
this . width = this . getBoundingClientRect ( ) . width ;
411
+
412
+ if ( this . responsivePopover && this . responsivePopover . opened ) {
413
+ this . _closeRespPopover ( ) ;
414
+ }
415
+ }
416
+
417
+ /**
418
+ * Updates the expanded attribute for each ui5-wizard-tab based on the ui5-wizard width
419
+ * @private
420
+ */
421
+ _adjustHeaderOverflow ( ) {
422
+ let counter = 0 ;
423
+ let isForward = true ;
424
+ const iWidth = this . width ;
425
+ const iCurrStep = this . getSelectedStepIndex ( ) ;
426
+ const iStepsToShow = this . steps . length ? Math . floor ( iWidth / MIN_STEP_WIDTH_WITH_TITLE ) : Math . floor ( iWidth / MIN_STEP_WIDTH_NO_TITLE ) ;
427
+
428
+ const tabs = this . shadowRoot . querySelectorAll ( "ui5-wizard-tab" ) ;
429
+
430
+ if ( ! tabs . length ) {
431
+ return ;
432
+ }
433
+
434
+ [ ] . forEach . call ( tabs , ( step , index ) => {
435
+ step . setAttribute ( EXPANDED_STEP , false ) ;
436
+ step . setAttribute ( BEFORE_EXPANDED_STEP , false ) ;
437
+ step . setAttribute ( AFTER_EXPANDED_STEP , false ) ;
438
+
439
+ // Add "data-ui5-wizard-after-current-tab" to all tabs after the current one
440
+ if ( index > iCurrStep ) {
441
+ tabs [ index ] . setAttribute ( AFTER_CURRENT_STEP , true ) ;
442
+ } else {
443
+ tabs [ index ] . removeAttribute ( AFTER_CURRENT_STEP ) ;
444
+ }
445
+ } ) ;
446
+
447
+ // Add "data-ui5-wizard-expanded-tab" to the current step
448
+ if ( tabs [ iCurrStep ] ) {
449
+ tabs [ iCurrStep ] . setAttribute ( EXPANDED_STEP , true ) ;
450
+ }
451
+
452
+ // Set the "data-ui5-wizard-expanded-tab" to the steps that are expanded
453
+ // The algorithm is as follows:
454
+ // 1. A step towards the end is expanded
455
+ // 1.2. If there are no available steps towards the end a step towards the beginning is expanded
456
+ // 2. A step towards the beginning is expanded
457
+ // 2.2. If there are no available steps towards the beginning a step towards the end is expanded
458
+ for ( let i = 1 ; i < iStepsToShow ; i ++ ) {
459
+ if ( isForward ) {
460
+ counter += 1 ;
461
+ }
462
+
463
+ if ( isForward && tabs [ iCurrStep + counter ] ) {
464
+ tabs [ iCurrStep + counter ] . setAttribute ( EXPANDED_STEP , true ) ;
465
+ isForward = ! isForward ;
466
+ } else if ( ! isForward && tabs [ iCurrStep - counter ] ) {
467
+ tabs [ iCurrStep - counter ] . setAttribute ( EXPANDED_STEP , true ) ;
468
+ isForward = ! isForward ;
469
+ } else if ( tabs [ iCurrStep + counter + 1 ] ) {
470
+ counter += 1 ;
471
+ tabs [ iCurrStep + counter ] . setAttribute ( EXPANDED_STEP , true ) ;
472
+ isForward = true ;
473
+ } else if ( tabs [ iCurrStep - counter ] ) {
474
+ tabs [ iCurrStep - counter ] . setAttribute ( EXPANDED_STEP , true ) ;
475
+ counter += 1 ;
476
+ isForward = false ;
477
+ }
478
+ }
479
+
480
+ // mark the topmost steps of both groups (in the beginning and the end),
481
+ // using the "data-ui5-wizard-after-current-tab" and "data-ui5-wizard-expanded-tab-prev" attributes
482
+ for ( let i = 0 ; i < tabs . length ; i ++ ) {
483
+ if ( tabs [ i ] . getAttribute ( EXPANDED_STEP ) === "true" && tabs [ i - 1 ] && tabs [ i - 1 ] . getAttribute ( EXPANDED_STEP ) === "false" ) {
484
+ tabs [ i - 1 ] . setAttribute ( BEFORE_EXPANDED_STEP , true ) ;
485
+ }
486
+
487
+ if ( tabs [ i ] . getAttribute ( EXPANDED_STEP ) === "false" && tabs [ i - 1 ] && tabs [ i - 1 ] . getAttribute ( EXPANDED_STEP ) === "true" ) {
488
+ tabs [ i ] . setAttribute ( AFTER_EXPANDED_STEP , true ) ;
489
+ break ;
490
+ }
491
+ }
492
+ }
493
+
494
+ _isGroupAtStart ( selectedStep ) {
495
+ const iStepNumber = this . stepsInHeaderDOM . indexOf ( selectedStep ) ;
496
+
497
+ return selectedStep . getAttribute ( EXPANDED_STEP ) === "false" && selectedStep . getAttribute ( BEFORE_EXPANDED_STEP ) === "true" && iStepNumber > 0 ;
498
+ }
499
+
500
+ _isGroupAtEnd ( selectedStep ) {
501
+ const iStepNumber = this . stepsInHeaderDOM . indexOf ( selectedStep ) ;
502
+
503
+ return selectedStep . getAttribute ( EXPANDED_STEP ) === "false" && selectedStep . getAttribute ( AFTER_EXPANDED_STEP ) === "true" && ( iStepNumber + 1 < this . steps . length ) ;
504
+ }
505
+
506
+ async _showPopover ( oDomTarget , bAtStart ) {
507
+ const tabs = Array . from ( this . shadowRoot . querySelectorAll ( "ui5-wizard-tab" ) ) ;
508
+ this . _groupedTabs = [ ] ;
509
+
510
+ const iFromStep = bAtStart ? 0 : this . stepsInHeaderDOM . indexOf ( oDomTarget ) ;
511
+ const iToStep = bAtStart ? this . stepsInHeaderDOM . indexOf ( oDomTarget ) : tabs . length - 1 ;
512
+
513
+ for ( let i = iFromStep ; i <= iToStep ; i ++ ) {
514
+ this . _groupedTabs . push ( tabs [ i ] ) ;
515
+ }
516
+
517
+ this . responsivePopover = await this . _respPopover ( ) ;
518
+ this . responsivePopover . open ( oDomTarget ) ;
519
+ }
520
+
521
+ async _onGroupedTabClick ( event ) {
522
+ if ( this . _isGroupAtStart ( event . target ) ) {
523
+ return this . _showPopover ( event . target , true ) ;
524
+ }
525
+
526
+ if ( this . _isGroupAtEnd ( event . target ) ) {
527
+ return this . _showPopover ( event . target , false ) ;
528
+ }
529
+ }
530
+
531
+ _onOverflowStepButtonClick ( event ) {
532
+ const tabs = Array . from ( this . shadowRoot . querySelectorAll ( "ui5-wizard-tab" ) ) ;
533
+ const stepRefId = event . target . getAttribute ( "data-ui5-header-tab-ref-id" ) ;
534
+ const stepToSelect = this . slottedSteps [ stepRefId - 1 ] ;
535
+ const selectedStep = this . selectedStep ;
536
+ const newlySelectedIndex = this . slottedSteps . indexOf ( stepToSelect ) ;
537
+
538
+ this . switchSelectionFromOldToNewStep ( selectedStep , stepToSelect , newlySelectedIndex ) ;
539
+ this . _closeRespPopover ( ) ;
540
+ tabs [ newlySelectedIndex ] . focus ( ) ;
541
+ }
542
+
543
+ _closeRespPopover ( ) {
544
+ this . responsivePopover . close ( ) ;
545
+ }
546
+
547
+ async _respPopover ( ) {
548
+ const staticAreaItem = await this . getStaticAreaItemDomRef ( ) ;
549
+ return staticAreaItem . querySelector ( `.ui5-wizard-responsive-popover` ) ;
367
550
}
368
551
369
552
/**
@@ -400,6 +583,8 @@ class Wizard extends UI5Element {
400
583
const stepRefId = stepInHeader . getAttribute ( "data-ui5-content-ref-id" ) ;
401
584
const selectedStep = this . selectedStep ;
402
585
const stepToSelect = this . getStepByRefId ( stepRefId ) ;
586
+ const bExpanded = stepInHeader . getAttribute ( EXPANDED_STEP ) === "true" ;
587
+ const newlySelectedIndex = this . slottedSteps . indexOf ( stepToSelect ) ;
403
588
404
589
// If the currently selected (active) step is clicked,
405
590
// just scroll to its starting point and stop.
@@ -408,9 +593,10 @@ class Wizard extends UI5Element {
408
593
return ;
409
594
}
410
595
411
- // Change selection and fire "selection-change".
412
- const newlySelectedIndex = this . slottedSteps . indexOf ( stepToSelect ) ;
413
- this . switchSelectionFromOldToNewStep ( selectedStep , stepToSelect , newlySelectedIndex ) ;
596
+ if ( bExpanded || ( ! bExpanded && ( newlySelectedIndex === 0 || newlySelectedIndex === this . steps . length - 1 ) ) ) {
597
+ // Change selection and fire "selection-change".
598
+ this . switchSelectionFromOldToNewStep ( selectedStep , stepToSelect , newlySelectedIndex ) ;
599
+ }
414
600
}
415
601
416
602
get _stepsInHeader ( ) {
@@ -503,22 +689,25 @@ class Wizard extends UI5Element {
503
689
getStepsInfo ( ) {
504
690
const lastEnabledStepIndex = this . getLastEnabledStepIndex ( ) ;
505
691
const stepsCount = this . stepsCount ;
692
+ const selectedStepIndex = this . getSelectedStepIndex ( ) ;
693
+ let inintialZIndex = this . steps . length + 10 ;
694
+
695
+ this . _adjustHeaderOverflow ( ) ;
506
696
507
697
return this . steps . map ( ( step , idx ) => {
508
698
const pos = idx + 1 ;
509
699
510
- // Hide separator if:
511
- // (1) its size is under the phone breakpoint
512
- // (2) it's the last step and it's not a branching one
513
- const hideSeparator = this . phoneMode || ( ( idx === stepsCount - 1 ) && ! step . branching ) ;
700
+ // Hide separator if it's the last step and it's not a branching one
701
+ const hideSeparator = ( idx === stepsCount - 1 ) && ! step . branching ;
514
702
515
703
// Calculate the step's aria-roledectioption: "1. heading" or "Step 1".
516
704
const roleDescription = step . heading ? `${ pos } . ${ step . heading } ` : `${ this . navStepDefaultHeading } ${ pos } ` ;
705
+ const isAfterCurrent = ( idx > selectedStepIndex ) ;
517
706
518
707
return {
519
708
icon : step . icon ,
520
- heading : this . phoneMode ? "" : step . heading ,
521
- subheading : this . phoneMode ? "" : step . subheading ,
709
+ heading : step . heading ,
710
+ subheading : step . subheading ,
522
711
number : pos ,
523
712
selected : step . selected ,
524
713
disabled : step . disabled ,
@@ -531,6 +720,7 @@ class Wizard extends UI5Element {
531
720
ariaLabel : getEffectiveAriaLabelText ( step ) ,
532
721
refStepId : step . _id ,
533
722
tabIndex : this . selectedStepIndex === idx ? "0" : "-1" ,
723
+ styles : `z-index: ${ isAfterCurrent ? -- inintialZIndex : 1 } ` ,
534
724
} ;
535
725
} ) ;
536
726
}
@@ -643,10 +833,10 @@ class Wizard extends UI5Element {
643
833
*
644
834
* @param {HTMLElement } selectedStep the old step
645
835
* @param {HTMLElement } stepToSelect the step to be selected
646
- * @param {Integer } selectedStepIndex the index of the newly selected step
836
+ * @param {Integer } stepToSelectIndex the index of the newly selected step
647
837
* @private
648
838
*/
649
- switchSelectionFromOldToNewStep ( selectedStep , stepToSelect , selectedStepIndex ) {
839
+ switchSelectionFromOldToNewStep ( selectedStep , stepToSelect , stepToSelectIndex ) {
650
840
if ( selectedStep && stepToSelect ) {
651
841
selectedStep . selected = false ;
652
842
stepToSelect . selected = true ;
@@ -656,7 +846,7 @@ class Wizard extends UI5Element {
656
846
previouslySelectedStep : selectedStep ,
657
847
} ) ;
658
848
659
- this . selectedStepIndex = selectedStepIndex ;
849
+ this . selectedStepIndex = stepToSelectIndex ;
660
850
}
661
851
}
662
852
0 commit comments