@sapui5/sap.fe.templates 1.147.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 (38) 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/ListReportController.controller.js +4 -14
  5. package/src/sap/fe/templates/ListReport/ListReportController.controller.ts +2 -12
  6. package/src/sap/fe/templates/ListReport/controls/MultipleModeControl.js +4 -1
  7. package/src/sap/fe/templates/ListReport/controls/MultipleModeControl.ts +3 -0
  8. package/src/sap/fe/templates/ListReport/manifest.json +1 -1
  9. package/src/sap/fe/templates/ObjectPage/ExtensionAPI.js +134 -24
  10. package/src/sap/fe/templates/ObjectPage/ExtensionAPI.ts +132 -19
  11. package/src/sap/fe/templates/ObjectPage/ObjectPageController.controller.js +169 -17
  12. package/src/sap/fe/templates/ObjectPage/ObjectPageController.controller.ts +195 -19
  13. package/src/sap/fe/templates/ObjectPage/ObjectPageTemplating.js +13 -8
  14. package/src/sap/fe/templates/ObjectPage/ObjectPageTemplating.ts +17 -9
  15. package/src/sap/fe/templates/ObjectPage/components/CollaborationDiscardDialog.js +4 -1
  16. package/src/sap/fe/templates/ObjectPage/components/CollaborationDiscardDialog.tsx +1 -0
  17. package/src/sap/fe/templates/ObjectPage/components/CollaborationDraft.js +15 -4
  18. package/src/sap/fe/templates/ObjectPage/components/CollaborationDraft.tsx +11 -2
  19. package/src/sap/fe/templates/ObjectPage/controls/StashableHBox.js +28 -1
  20. package/src/sap/fe/templates/ObjectPage/controls/StashableHBox.ts +31 -0
  21. package/src/sap/fe/templates/ObjectPage/helpers/SectionNavigationHelper.js +72 -0
  22. package/src/sap/fe/templates/ObjectPage/helpers/SectionNavigationHelper.ts +75 -0
  23. package/src/sap/fe/templates/ObjectPage/manifest.json +1 -15
  24. package/src/sap/fe/templates/ObjectPage/overrides/CollaborationManager.js +29 -20
  25. package/src/sap/fe/templates/ObjectPage/overrides/CollaborationManager.ts +28 -21
  26. package/src/sap/fe/templates/ObjectPage/overrides/IntentBasedNavigation.js +4 -3
  27. package/src/sap/fe/templates/ObjectPage/overrides/IntentBasedNavigation.ts +4 -4
  28. package/src/sap/fe/templates/ObjectPage/overrides/ViewState.js +93 -17
  29. package/src/sap/fe/templates/ObjectPage/overrides/ViewState.ts +108 -22
  30. package/src/sap/fe/templates/ObjectPage/view/fragments/EmphasizedFirstHeaderAction.fragment.xml +1 -0
  31. package/src/sap/fe/templates/ObjectPage/view/fragments/ExpandedHeading.fragment.xml +4 -4
  32. package/src/sap/fe/templates/ObjectPage/view/fragments/Heading.fragment.xml +4 -4
  33. package/src/sap/fe/templates/ObjectPage/view/fragments/ObjectPageHeaderAddress.fragment.xml +1 -1
  34. package/src/sap/fe/templates/ObjectPage/view/fragments/ObjectPageHeaderContact.fragment.xml +1 -1
  35. package/src/sap/fe/templates/ObjectPage/view/fragments/ObjectPageHeaderForm.fragment.xml +1 -0
  36. package/src/sap/fe/templates/library.js +1 -1
  37. package/src/sap/fe/templates/messagebundle_no.properties +1 -1
  38. package/src/sap/fe/templates/messagebundle_uk.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.
@@ -505,70 +511,177 @@ class ObjectPageExtensionAPI extends ExtensionAPI {
505
511
  MessageBox.error(sTitle);
506
512
  }
507
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
+
508
545
  /**
509
546
  * Navigate to a specific section or subsection within the current page.
510
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.
511
549
  * @param sectionOrSubSectionId The ID of the target section or subsection (without a view prefix, for example, "fe::FacetSection::TravelData" or "fe::SubSection::Details")
512
550
  * @public
513
551
  */
514
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
+
515
567
  try {
516
568
  // Find the Object Page Layout control
517
569
  const objectPageLayout = this._view.getContent()[0] as ObjectPageLayout | undefined;
518
570
 
519
- if (!objectPageLayout) {
520
- 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;
521
575
  return;
522
576
  }
523
577
 
524
578
  if (!sectionOrSubSectionId) {
525
- this.showNavigationError();
579
+ this.navigateToFirstSection(objectPageLayout);
580
+ resetNavigationFlag(100);
526
581
  return;
527
582
  }
528
583
 
529
584
  const control = this._view.byId(sectionOrSubSectionId);
530
585
 
531
- if (!control) {
586
+ if (!control || !(control as Control).getVisible()) {
587
+ this.navigateToFirstSection(objectPageLayout);
532
588
  this.showNavigationError();
589
+ resetNavigationFlag(100);
533
590
  return;
534
591
  }
535
592
 
536
593
  let targetSubSection: ObjectPageSubSection | undefined;
594
+ let targetSection: ObjectPageSection | undefined;
595
+ let isNavigatingToSection = false; // Track if original target was a Section (not SubSection)
537
596
 
538
597
  // Check if control is a SubSection or Section
539
598
  if (control.isA<ObjectPageSubSection>("sap.uxap.ObjectPageSubSection")) {
540
599
  targetSubSection = control;
541
600
  } else if (control.isA<ObjectPageSection>("sap.uxap.ObjectPageSection")) {
542
- // 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)
543
604
  const subSections = control.getSubSections();
544
605
  targetSubSection = subSections.length > 0 ? subSections[0] : undefined;
545
606
  }
546
607
 
547
- // Validate subsection
548
- 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);
549
612
  this.showNavigationError();
613
+ resetNavigationFlag(100);
550
614
  return;
551
615
  }
552
616
 
553
- // Get the full subsection ID (including view prefix)
554
- 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
+ }
555
624
 
556
- if (!subsectionFullId) {
557
- 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;
558
630
  return;
559
631
  }
560
632
 
561
- // Navigate using ObjectPageLayout API
562
- 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
+ }
563
644
 
564
- // Trigger iApp state change by firing navigate event
565
645
  const parentSection = targetSubSection.getParent() as ObjectPageSection;
566
- objectPageLayout.fireNavigate({
567
- section: parentSection,
568
- subSection: targetSubSection
569
- });
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
+ }
570
683
  } catch (error) {
571
- Log.error("Error during navigation to subsection:", error as Error);
684
+ this.isNavigatingToSubSection = false;
572
685
  }
573
686
  }
574
687
  }