@sapui5/sap.fe.templates 1.146.0 → 1.148.0

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.
Files changed (102) hide show
  1. package/package.json +1 -1
  2. package/src/sap/fe/templates/.library +1 -1
  3. package/src/sap/fe/templates/AnalyticalListPage/manifest.json +1 -1
  4. package/src/sap/fe/templates/ListReport/ExtensionAPI.js +1 -1
  5. package/src/sap/fe/templates/ListReport/ExtensionAPI.ts +1 -1
  6. package/src/sap/fe/templates/ListReport/ListReport.view.xml +45 -10
  7. package/src/sap/fe/templates/ListReport/ListReportController.controller.js +43 -16
  8. package/src/sap/fe/templates/ListReport/ListReportController.controller.ts +51 -15
  9. package/src/sap/fe/templates/ListReport/ListReportTemplating.js +39 -3
  10. package/src/sap/fe/templates/ListReport/ListReportTemplating.ts +35 -1
  11. package/src/sap/fe/templates/ListReport/controls/MultipleModeControl.js +4 -1
  12. package/src/sap/fe/templates/ListReport/controls/MultipleModeControl.ts +3 -0
  13. package/src/sap/fe/templates/ListReport/manifest.json +1 -1
  14. package/src/sap/fe/templates/ListReport/overrides/MessageHandler.js +3 -2
  15. package/src/sap/fe/templates/ListReport/overrides/MessageHandler.ts +2 -1
  16. package/src/sap/fe/templates/ObjectPage/Component.js +4 -2
  17. package/src/sap/fe/templates/ObjectPage/Component.ts +3 -1
  18. package/src/sap/fe/templates/ObjectPage/ExtensionAPI.js +163 -28
  19. package/src/sap/fe/templates/ObjectPage/ExtensionAPI.ts +163 -23
  20. package/src/sap/fe/templates/ObjectPage/ObjectPageController.controller.js +246 -35
  21. package/src/sap/fe/templates/ObjectPage/ObjectPageController.controller.ts +286 -42
  22. package/src/sap/fe/templates/ObjectPage/ObjectPageTemplating.js +32 -16
  23. package/src/sap/fe/templates/ObjectPage/ObjectPageTemplating.ts +46 -33
  24. package/src/sap/fe/templates/ObjectPage/components/CollaborationDiscardDialog.js +4 -1
  25. package/src/sap/fe/templates/ObjectPage/components/CollaborationDiscardDialog.tsx +1 -0
  26. package/src/sap/fe/templates/ObjectPage/components/CollaborationDraft.js +15 -4
  27. package/src/sap/fe/templates/ObjectPage/components/CollaborationDraft.tsx +11 -2
  28. package/src/sap/fe/templates/ObjectPage/controls/StashableHBox.js +28 -1
  29. package/src/sap/fe/templates/ObjectPage/controls/StashableHBox.ts +31 -0
  30. package/src/sap/fe/templates/ObjectPage/helpers/SectionNavigationHelper.js +72 -0
  31. package/src/sap/fe/templates/ObjectPage/helpers/SectionNavigationHelper.ts +75 -0
  32. package/src/sap/fe/templates/ObjectPage/manifest.json +1 -15
  33. package/src/sap/fe/templates/ObjectPage/overrides/CollaborationManager.js +29 -20
  34. package/src/sap/fe/templates/ObjectPage/overrides/CollaborationManager.ts +28 -21
  35. package/src/sap/fe/templates/ObjectPage/overrides/IntentBasedNavigation.js +4 -3
  36. package/src/sap/fe/templates/ObjectPage/overrides/IntentBasedNavigation.ts +4 -4
  37. package/src/sap/fe/templates/ObjectPage/overrides/Share.js +2 -2
  38. package/src/sap/fe/templates/ObjectPage/overrides/Share.ts +1 -1
  39. package/src/sap/fe/templates/ObjectPage/overrides/ViewState.js +93 -17
  40. package/src/sap/fe/templates/ObjectPage/overrides/ViewState.ts +108 -22
  41. package/src/sap/fe/templates/ObjectPage/view/fragments/Actions.fragment.xml +12 -2
  42. package/src/sap/fe/templates/ObjectPage/view/fragments/EmphasizedFirstHeaderAction.fragment.xml +182 -0
  43. package/src/sap/fe/templates/ObjectPage/view/fragments/ExpandedHeading.fragment.xml +4 -4
  44. package/src/sap/fe/templates/ObjectPage/view/fragments/Heading.fragment.xml +4 -4
  45. package/src/sap/fe/templates/ObjectPage/view/fragments/ObjectPageHeaderAddress.fragment.xml +1 -1
  46. package/src/sap/fe/templates/ObjectPage/view/fragments/ObjectPageHeaderContact.fragment.xml +1 -1
  47. package/src/sap/fe/templates/ObjectPage/view/fragments/ObjectPageHeaderForm.fragment.xml +1 -0
  48. package/src/sap/fe/templates/ObjectPage/view/fragments/Section.fragment.xml +2 -0
  49. package/src/sap/fe/templates/ObjectPage/view/fragments/SectionContent.fragment.xml +18 -12
  50. package/src/sap/fe/templates/ObjectPage/view/fragments/SectionFormContent.fragment.xml +2 -0
  51. package/src/sap/fe/templates/ObjectPage/view/fragments/SectionMoreFormContent.fragment.xml +2 -0
  52. package/src/sap/fe/templates/library.js +1 -1
  53. package/src/sap/fe/templates/messagebundle.properties +1 -1
  54. package/src/sap/fe/templates/messagebundle_ar.properties +1 -1
  55. package/src/sap/fe/templates/messagebundle_bg.properties +1 -1
  56. package/src/sap/fe/templates/messagebundle_ca.properties +1 -1
  57. package/src/sap/fe/templates/messagebundle_cnr.properties +1 -1
  58. package/src/sap/fe/templates/messagebundle_cs.properties +1 -1
  59. package/src/sap/fe/templates/messagebundle_cy.properties +1 -1
  60. package/src/sap/fe/templates/messagebundle_da.properties +1 -1
  61. package/src/sap/fe/templates/messagebundle_de.properties +1 -1
  62. package/src/sap/fe/templates/messagebundle_el.properties +1 -1
  63. package/src/sap/fe/templates/messagebundle_en.properties +1 -1
  64. package/src/sap/fe/templates/messagebundle_en_GB.properties +1 -1
  65. package/src/sap/fe/templates/messagebundle_en_US_saprigi.properties +1 -1
  66. package/src/sap/fe/templates/messagebundle_es.properties +1 -1
  67. package/src/sap/fe/templates/messagebundle_es_MX.properties +1 -1
  68. package/src/sap/fe/templates/messagebundle_et.properties +1 -1
  69. package/src/sap/fe/templates/messagebundle_fi.properties +1 -1
  70. package/src/sap/fe/templates/messagebundle_fr.properties +1 -1
  71. package/src/sap/fe/templates/messagebundle_fr_CA.properties +1 -1
  72. package/src/sap/fe/templates/messagebundle_hi.properties +1 -1
  73. package/src/sap/fe/templates/messagebundle_hr.properties +1 -1
  74. package/src/sap/fe/templates/messagebundle_hu.properties +1 -1
  75. package/src/sap/fe/templates/messagebundle_id.properties +1 -1
  76. package/src/sap/fe/templates/messagebundle_it.properties +1 -1
  77. package/src/sap/fe/templates/messagebundle_iw.properties +2 -2
  78. package/src/sap/fe/templates/messagebundle_ja.properties +1 -1
  79. package/src/sap/fe/templates/messagebundle_kk.properties +1 -1
  80. package/src/sap/fe/templates/messagebundle_ko.properties +1 -1
  81. package/src/sap/fe/templates/messagebundle_lt.properties +1 -1
  82. package/src/sap/fe/templates/messagebundle_lv.properties +1 -1
  83. package/src/sap/fe/templates/messagebundle_mk.properties +1 -1
  84. package/src/sap/fe/templates/messagebundle_ms.properties +1 -1
  85. package/src/sap/fe/templates/messagebundle_nl.properties +1 -1
  86. package/src/sap/fe/templates/messagebundle_no.properties +2 -2
  87. package/src/sap/fe/templates/messagebundle_pl.properties +1 -1
  88. package/src/sap/fe/templates/messagebundle_pt.properties +1 -1
  89. package/src/sap/fe/templates/messagebundle_pt_PT.properties +1 -1
  90. package/src/sap/fe/templates/messagebundle_ro.properties +1 -1
  91. package/src/sap/fe/templates/messagebundle_ru.properties +1 -1
  92. package/src/sap/fe/templates/messagebundle_sh.properties +1 -1
  93. package/src/sap/fe/templates/messagebundle_sk.properties +1 -1
  94. package/src/sap/fe/templates/messagebundle_sl.properties +1 -1
  95. package/src/sap/fe/templates/messagebundle_sr.properties +1 -1
  96. package/src/sap/fe/templates/messagebundle_sv.properties +1 -1
  97. package/src/sap/fe/templates/messagebundle_th.properties +1 -1
  98. package/src/sap/fe/templates/messagebundle_tr.properties +1 -1
  99. package/src/sap/fe/templates/messagebundle_uk.properties +3 -3
  100. package/src/sap/fe/templates/messagebundle_vi.properties +1 -1
  101. package/src/sap/fe/templates/messagebundle_zh_CN.properties +1 -1
  102. package/src/sap/fe/templates/messagebundle_zh_TW.properties +1 -1
@@ -48,6 +48,12 @@ class ObjectPageExtensionAPI extends ExtensionAPI {
48
48
  */
49
49
  private customMessageStripId?: string;
50
50
 
51
+ /**
52
+ * Flag to prevent concurrent navigateToSubSection calls.
53
+ * Set to true when navigation starts, reset after navigation completes (including polling).
54
+ */
55
+ private isNavigatingToSubSection = false;
56
+
51
57
  /**
52
58
  * Refreshes either the whole object page or only parts of it.
53
59
  * @param [vPath] Path or array of paths referring to entities or properties to be refreshed.
@@ -130,9 +136,32 @@ class ObjectPageExtensionAPI extends ExtensionAPI {
130
136
  */
131
137
  showSideContent(sSubSectionKey: string, bShow?: boolean): void {
132
138
  const sBlockID = getSideContentLayoutID(sSubSectionKey),
133
- oBlock = this._view.byId(sBlockID),
134
- bBlockState = bShow === undefined ? !(oBlock as DynamicSideContent).getShowSideContent() : bShow;
135
- (oBlock as DynamicSideContent).setShowSideContent(bBlockState, false);
139
+ oBlock = this._view.byId(sBlockID) as DynamicSideContent;
140
+
141
+ if (!oBlock) {
142
+ return;
143
+ }
144
+
145
+ const sCurrentBreakpoint = oBlock.getCurrentBreakpoint();
146
+ const bBlockState = bShow === undefined ? !oBlock.getShowSideContent() : bShow;
147
+
148
+ // On small screens (S breakpoint), side content should replace main content
149
+ if (sCurrentBreakpoint === "S") {
150
+ if (bBlockState) {
151
+ // Show side content, hide main content
152
+ oBlock.setShowSideContent(true, false);
153
+ oBlock.setShowMainContent(false, false);
154
+ } else {
155
+ // Hide side content, show main content
156
+ oBlock.setShowSideContent(false, false);
157
+ oBlock.setShowMainContent(true, false);
158
+ }
159
+ } else {
160
+ // On larger screens, show side content alongside main content
161
+ // Always keep main content visible on large screens
162
+ oBlock.setShowMainContent(true, false);
163
+ oBlock.setShowSideContent(bBlockState, false);
164
+ }
136
165
  }
137
166
 
138
167
  /**
@@ -313,7 +342,11 @@ class ObjectPageExtensionAPI extends ExtensionAPI {
313
342
  */
314
343
  _showMessages(messages: Message[], origin: "Backend" | "Custom" = "Custom"): void {
315
344
  try {
316
- this.removeCustomMessageStrip();
345
+ if (messages.length > 0) {
346
+ // Only if there are messages to show, we remove custom message strip.
347
+ // This way we ensure that we don't interfere with custom message strip's lifecyle until there are new messages to show in the standard message strip.
348
+ this.removeCustomMessageStrip();
349
+ }
317
350
 
318
351
  const view = this._view;
319
352
  const internalModel = view.getModel("internal");
@@ -478,70 +511,177 @@ class ObjectPageExtensionAPI extends ExtensionAPI {
478
511
  MessageBox.error(sTitle);
479
512
  }
480
513
 
514
+ /**
515
+ * Navigate to the first section of the object page.
516
+ * Used as a fallback when the target section is not found or invalid.
517
+ * @param objectPageLayout The ObjectPageLayout control to navigate within
518
+ */
519
+ private navigateToFirstSection(objectPageLayout: ObjectPageLayout): void {
520
+ const sections = objectPageLayout.getSections();
521
+ if (!sections || sections.length === 0) {
522
+ return;
523
+ }
524
+
525
+ const firstSection = sections[0];
526
+ const subSections = firstSection.getSubSections();
527
+ if (!subSections || subSections.length === 0) {
528
+ return;
529
+ }
530
+
531
+ const firstSubSection = subSections[0];
532
+ const subSectionFullId = firstSubSection.getId();
533
+
534
+ if (!subSectionFullId) {
535
+ return;
536
+ }
537
+
538
+ objectPageLayout.setSelectedSection(subSectionFullId);
539
+ objectPageLayout.fireNavigate({
540
+ section: firstSection,
541
+ subSection: firstSubSection
542
+ });
543
+ }
544
+
481
545
  /**
482
546
  * Navigate to a specific section or subsection within the current page.
483
547
  * Works with Object Page layouts and other section-based layouts.
548
+ * If the target section is not found or invalid, the app automatically falls back to the first available section.
484
549
  * @param sectionOrSubSectionId The ID of the target section or subsection (without a view prefix, for example, "fe::FacetSection::TravelData" or "fe::SubSection::Details")
485
550
  * @public
486
551
  */
487
552
  navigateToSubSection(sectionOrSubSectionId: string): void {
553
+ // Guard against concurrent navigation calls to prevent infinite loops
554
+ // from multiple polling mechanisms (ViewState.apply and ObjectPageController._navigateToTargetSection)
555
+ if (this.isNavigatingToSubSection) {
556
+ return;
557
+ }
558
+ this.isNavigatingToSubSection = true;
559
+
560
+ // Helper to reset navigation flag after a delay
561
+ const resetNavigationFlag = (delayMs: number): void => {
562
+ setTimeout(() => {
563
+ this.isNavigatingToSubSection = false;
564
+ }, delayMs);
565
+ };
566
+
488
567
  try {
489
568
  // Find the Object Page Layout control
490
569
  const objectPageLayout = this._view.getContent()[0] as ObjectPageLayout | undefined;
491
570
 
492
- if (!objectPageLayout) {
493
- this.showNavigationError();
571
+ if (!objectPageLayout || !objectPageLayout.getDomRef()) {
572
+ // ObjectPageLayout not available or destroyed - silently return during navigation transitions
573
+ // This is expected when ViewState.apply runs on a view being navigated away from
574
+ this.isNavigatingToSubSection = false;
494
575
  return;
495
576
  }
496
577
 
497
578
  if (!sectionOrSubSectionId) {
498
- this.showNavigationError();
579
+ this.navigateToFirstSection(objectPageLayout);
580
+ resetNavigationFlag(100);
499
581
  return;
500
582
  }
501
583
 
502
584
  const control = this._view.byId(sectionOrSubSectionId);
503
585
 
504
- if (!control) {
586
+ if (!control || !(control as Control).getVisible()) {
587
+ this.navigateToFirstSection(objectPageLayout);
505
588
  this.showNavigationError();
589
+ resetNavigationFlag(100);
506
590
  return;
507
591
  }
508
592
 
509
593
  let targetSubSection: ObjectPageSubSection | undefined;
594
+ let targetSection: ObjectPageSection | undefined;
595
+ let isNavigatingToSection = false; // Track if original target was a Section (not SubSection)
510
596
 
511
597
  // Check if control is a SubSection or Section
512
598
  if (control.isA<ObjectPageSubSection>("sap.uxap.ObjectPageSubSection")) {
513
599
  targetSubSection = control;
514
600
  } else if (control.isA<ObjectPageSection>("sap.uxap.ObjectPageSection")) {
515
- // If it's a section, get the first subsection
601
+ targetSection = control;
602
+ isNavigatingToSection = true;
603
+ // If it's a section, get the first subsection (if available)
516
604
  const subSections = control.getSubSections();
517
605
  targetSubSection = subSections.length > 0 ? subSections[0] : undefined;
518
606
  }
519
607
 
520
- // Validate subsection
521
- if (!targetSubSection || !targetSubSection.getVisible()) {
608
+ // Validate we have either a section or subsection
609
+ const hasValidTarget = targetSubSection || targetSection;
610
+ if (!hasValidTarget || (targetSubSection && !targetSubSection.getVisible())) {
611
+ this.navigateToFirstSection(objectPageLayout);
522
612
  this.showNavigationError();
613
+ resetNavigationFlag(100);
523
614
  return;
524
615
  }
525
616
 
526
- // Get the full subsection ID (including view prefix)
527
- const subsectionFullId = targetSubSection.getId();
617
+ // If we have a section but no subsection (lazy loading scenario), scroll directly to the section
618
+ if (targetSection && !targetSubSection) {
619
+ const sectionFullId = targetSection.getId();
620
+ objectPageLayout.scrollToSection(sectionFullId, 0);
621
+ resetNavigationFlag(100);
622
+ return;
623
+ }
528
624
 
529
- if (!subsectionFullId) {
530
- Log.error("Subsection ID is undefined, cannot set selected section.");
625
+ // At this point, targetSubSection is guaranteed to be defined
626
+ // (we've already returned if it was undefined with a valid targetSection)
627
+ if (!targetSubSection) {
628
+ // This should never happen, but satisfies TypeScript
629
+ this.isNavigatingToSubSection = false;
531
630
  return;
532
631
  }
533
632
 
534
- // Navigate using ObjectPageLayout API
535
- objectPageLayout.setSelectedSection(subsectionFullId);
633
+ // Note: we intentionally do not check getVisible() here.
634
+ // When navigating to a subsection in an inactive IconTabBar tab, the subsection is not yet visible.
635
+ // setSelectedSection will activate the parent section and make the subsection visible.
636
+
637
+ // Get the full subsection ID (including view prefix)
638
+ const subSectionFullId = targetSubSection.getId();
639
+
640
+ if (!subSectionFullId) {
641
+ this.isNavigatingToSubSection = false;
642
+ return;
643
+ }
536
644
 
537
- // Trigger iApp state change by firing navigate event
538
645
  const parentSection = targetSubSection.getParent() as ObjectPageSection;
539
- objectPageLayout.fireNavigate({
540
- section: parentSection,
541
- subSection: targetSubSection
542
- });
646
+ const parentSectionFullId = parentSection.getId();
647
+ const isParentSectionSelected = objectPageLayout.getSelectedSection() === parentSectionFullId;
648
+
649
+ if (!isParentSectionSelected && !isNavigatingToSection) {
650
+ // When navigating to a subsection in an inactive section, we must first activate
651
+ // the parent section, then wait for subsection to be ready before scrolling to it
652
+ objectPageLayout.scrollToSection(parentSectionFullId, 0);
653
+
654
+ // Poll for subsection DOM to be ready after parent section activation
655
+ // Related: helpers/SectionNavigationHelper.ts (used by ViewState.apply and ObjectPageController.navigateToTargetSection)
656
+ // This polling differs: checks DOM readiness (binary), not section ID stability
657
+ let attempts = 0;
658
+ const maxAttempts = 10;
659
+ const checkSubsectionReady = (): void => {
660
+ attempts++;
661
+ const subSectionDom = targetSubSection.getDomRef();
662
+ if (subSectionDom) {
663
+ // Subsection is ready, scroll to it
664
+ objectPageLayout.scrollToSection(subSectionFullId, 0);
665
+ resetNavigationFlag(100);
666
+ } else if (attempts < maxAttempts) {
667
+ // Not ready yet, check again
668
+ setTimeout(checkSubsectionReady, 50);
669
+ } else {
670
+ // Timeout - try scrolling anyway
671
+ objectPageLayout.scrollToSection(subSectionFullId, 0);
672
+ resetNavigationFlag(100);
673
+ }
674
+ };
675
+ // Start checking after 100ms initial delay
676
+ setTimeout(checkSubsectionReady, 100);
677
+ } else {
678
+ // Parent section already selected or navigating to section - scroll directly
679
+ const scrollTarget = isNavigatingToSection ? parentSectionFullId : subSectionFullId;
680
+ objectPageLayout.scrollToSection(scrollTarget, 0);
681
+ resetNavigationFlag(100);
682
+ }
543
683
  } catch (error) {
544
- Log.error("Error during navigation to subsection:", error as Error);
684
+ this.isNavigatingToSubSection = false;
545
685
  }
546
686
  }
547
687
  }