@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.
- package/package.json +1 -1
- package/src/sap/fe/templates/.library +1 -1
- package/src/sap/fe/templates/AnalyticalListPage/manifest.json +1 -1
- package/src/sap/fe/templates/ListReport/ExtensionAPI.js +1 -1
- package/src/sap/fe/templates/ListReport/ExtensionAPI.ts +1 -1
- package/src/sap/fe/templates/ListReport/ListReport.view.xml +45 -10
- package/src/sap/fe/templates/ListReport/ListReportController.controller.js +43 -16
- package/src/sap/fe/templates/ListReport/ListReportController.controller.ts +51 -15
- package/src/sap/fe/templates/ListReport/ListReportTemplating.js +39 -3
- package/src/sap/fe/templates/ListReport/ListReportTemplating.ts +35 -1
- package/src/sap/fe/templates/ListReport/controls/MultipleModeControl.js +4 -1
- package/src/sap/fe/templates/ListReport/controls/MultipleModeControl.ts +3 -0
- package/src/sap/fe/templates/ListReport/manifest.json +1 -1
- package/src/sap/fe/templates/ListReport/overrides/MessageHandler.js +3 -2
- package/src/sap/fe/templates/ListReport/overrides/MessageHandler.ts +2 -1
- package/src/sap/fe/templates/ObjectPage/Component.js +4 -2
- package/src/sap/fe/templates/ObjectPage/Component.ts +3 -1
- package/src/sap/fe/templates/ObjectPage/ExtensionAPI.js +163 -28
- package/src/sap/fe/templates/ObjectPage/ExtensionAPI.ts +163 -23
- package/src/sap/fe/templates/ObjectPage/ObjectPageController.controller.js +246 -35
- package/src/sap/fe/templates/ObjectPage/ObjectPageController.controller.ts +286 -42
- package/src/sap/fe/templates/ObjectPage/ObjectPageTemplating.js +32 -16
- package/src/sap/fe/templates/ObjectPage/ObjectPageTemplating.ts +46 -33
- package/src/sap/fe/templates/ObjectPage/components/CollaborationDiscardDialog.js +4 -1
- package/src/sap/fe/templates/ObjectPage/components/CollaborationDiscardDialog.tsx +1 -0
- package/src/sap/fe/templates/ObjectPage/components/CollaborationDraft.js +15 -4
- package/src/sap/fe/templates/ObjectPage/components/CollaborationDraft.tsx +11 -2
- package/src/sap/fe/templates/ObjectPage/controls/StashableHBox.js +28 -1
- package/src/sap/fe/templates/ObjectPage/controls/StashableHBox.ts +31 -0
- package/src/sap/fe/templates/ObjectPage/helpers/SectionNavigationHelper.js +72 -0
- package/src/sap/fe/templates/ObjectPage/helpers/SectionNavigationHelper.ts +75 -0
- package/src/sap/fe/templates/ObjectPage/manifest.json +1 -15
- package/src/sap/fe/templates/ObjectPage/overrides/CollaborationManager.js +29 -20
- package/src/sap/fe/templates/ObjectPage/overrides/CollaborationManager.ts +28 -21
- package/src/sap/fe/templates/ObjectPage/overrides/IntentBasedNavigation.js +4 -3
- package/src/sap/fe/templates/ObjectPage/overrides/IntentBasedNavigation.ts +4 -4
- package/src/sap/fe/templates/ObjectPage/overrides/Share.js +2 -2
- package/src/sap/fe/templates/ObjectPage/overrides/Share.ts +1 -1
- package/src/sap/fe/templates/ObjectPage/overrides/ViewState.js +93 -17
- package/src/sap/fe/templates/ObjectPage/overrides/ViewState.ts +108 -22
- package/src/sap/fe/templates/ObjectPage/view/fragments/Actions.fragment.xml +12 -2
- package/src/sap/fe/templates/ObjectPage/view/fragments/EmphasizedFirstHeaderAction.fragment.xml +182 -0
- package/src/sap/fe/templates/ObjectPage/view/fragments/ExpandedHeading.fragment.xml +4 -4
- package/src/sap/fe/templates/ObjectPage/view/fragments/Heading.fragment.xml +4 -4
- package/src/sap/fe/templates/ObjectPage/view/fragments/ObjectPageHeaderAddress.fragment.xml +1 -1
- package/src/sap/fe/templates/ObjectPage/view/fragments/ObjectPageHeaderContact.fragment.xml +1 -1
- package/src/sap/fe/templates/ObjectPage/view/fragments/ObjectPageHeaderForm.fragment.xml +1 -0
- package/src/sap/fe/templates/ObjectPage/view/fragments/Section.fragment.xml +2 -0
- package/src/sap/fe/templates/ObjectPage/view/fragments/SectionContent.fragment.xml +18 -12
- package/src/sap/fe/templates/ObjectPage/view/fragments/SectionFormContent.fragment.xml +2 -0
- package/src/sap/fe/templates/ObjectPage/view/fragments/SectionMoreFormContent.fragment.xml +2 -0
- package/src/sap/fe/templates/library.js +1 -1
- package/src/sap/fe/templates/messagebundle.properties +1 -1
- package/src/sap/fe/templates/messagebundle_ar.properties +1 -1
- package/src/sap/fe/templates/messagebundle_bg.properties +1 -1
- package/src/sap/fe/templates/messagebundle_ca.properties +1 -1
- package/src/sap/fe/templates/messagebundle_cnr.properties +1 -1
- package/src/sap/fe/templates/messagebundle_cs.properties +1 -1
- package/src/sap/fe/templates/messagebundle_cy.properties +1 -1
- package/src/sap/fe/templates/messagebundle_da.properties +1 -1
- package/src/sap/fe/templates/messagebundle_de.properties +1 -1
- package/src/sap/fe/templates/messagebundle_el.properties +1 -1
- package/src/sap/fe/templates/messagebundle_en.properties +1 -1
- package/src/sap/fe/templates/messagebundle_en_GB.properties +1 -1
- package/src/sap/fe/templates/messagebundle_en_US_saprigi.properties +1 -1
- package/src/sap/fe/templates/messagebundle_es.properties +1 -1
- package/src/sap/fe/templates/messagebundle_es_MX.properties +1 -1
- package/src/sap/fe/templates/messagebundle_et.properties +1 -1
- package/src/sap/fe/templates/messagebundle_fi.properties +1 -1
- package/src/sap/fe/templates/messagebundle_fr.properties +1 -1
- package/src/sap/fe/templates/messagebundle_fr_CA.properties +1 -1
- package/src/sap/fe/templates/messagebundle_hi.properties +1 -1
- package/src/sap/fe/templates/messagebundle_hr.properties +1 -1
- package/src/sap/fe/templates/messagebundle_hu.properties +1 -1
- package/src/sap/fe/templates/messagebundle_id.properties +1 -1
- package/src/sap/fe/templates/messagebundle_it.properties +1 -1
- package/src/sap/fe/templates/messagebundle_iw.properties +2 -2
- package/src/sap/fe/templates/messagebundle_ja.properties +1 -1
- package/src/sap/fe/templates/messagebundle_kk.properties +1 -1
- package/src/sap/fe/templates/messagebundle_ko.properties +1 -1
- package/src/sap/fe/templates/messagebundle_lt.properties +1 -1
- package/src/sap/fe/templates/messagebundle_lv.properties +1 -1
- package/src/sap/fe/templates/messagebundle_mk.properties +1 -1
- package/src/sap/fe/templates/messagebundle_ms.properties +1 -1
- package/src/sap/fe/templates/messagebundle_nl.properties +1 -1
- package/src/sap/fe/templates/messagebundle_no.properties +2 -2
- package/src/sap/fe/templates/messagebundle_pl.properties +1 -1
- package/src/sap/fe/templates/messagebundle_pt.properties +1 -1
- package/src/sap/fe/templates/messagebundle_pt_PT.properties +1 -1
- package/src/sap/fe/templates/messagebundle_ro.properties +1 -1
- package/src/sap/fe/templates/messagebundle_ru.properties +1 -1
- package/src/sap/fe/templates/messagebundle_sh.properties +1 -1
- package/src/sap/fe/templates/messagebundle_sk.properties +1 -1
- package/src/sap/fe/templates/messagebundle_sl.properties +1 -1
- package/src/sap/fe/templates/messagebundle_sr.properties +1 -1
- package/src/sap/fe/templates/messagebundle_sv.properties +1 -1
- package/src/sap/fe/templates/messagebundle_th.properties +1 -1
- package/src/sap/fe/templates/messagebundle_tr.properties +1 -1
- package/src/sap/fe/templates/messagebundle_uk.properties +3 -3
- package/src/sap/fe/templates/messagebundle_vi.properties +1 -1
- package/src/sap/fe/templates/messagebundle_zh_CN.properties +1 -1
- package/src/sap/fe/templates/messagebundle_zh_TW.properties +1 -1
|
@@ -48,17 +48,22 @@ import type { default as ObjectPageExtensionAPI } from "sap/fe/templates/ObjectP
|
|
|
48
48
|
import { default as ExtensionAPI } from "sap/fe/templates/ObjectPage/ExtensionAPI";
|
|
49
49
|
import CollaborationDiscard from "sap/fe/templates/ObjectPage/components/CollaborationDiscardDialog";
|
|
50
50
|
import type SubSectionBlock from "sap/fe/templates/ObjectPage/controls/SubSectionBlock";
|
|
51
|
+
import { pollForSectionStability } from "sap/fe/templates/ObjectPage/helpers/SectionNavigationHelper";
|
|
51
52
|
import TableScroller from "sap/fe/templates/TableScroller";
|
|
52
53
|
import type Button from "sap/m/Button";
|
|
53
54
|
import type InputBase from "sap/m/InputBase";
|
|
54
55
|
import InstanceManager from "sap/m/InstanceManager";
|
|
56
|
+
import type MenuButton from "sap/m/MenuButton";
|
|
55
57
|
import type NavContainer from "sap/m/NavContainer";
|
|
56
58
|
import type Popover from "sap/m/Popover";
|
|
59
|
+
import type SearchField from "sap/m/SearchField";
|
|
57
60
|
import type ToolbarSpacer from "sap/m/ToolbarSpacer";
|
|
58
61
|
import Device from "sap/ui/Device";
|
|
59
62
|
import type UI5Event from "sap/ui/base/Event";
|
|
63
|
+
import type ManagedObject from "sap/ui/base/ManagedObject";
|
|
60
64
|
import type { CommandExecution$ExecuteEvent } from "sap/ui/core/CommandExecution";
|
|
61
65
|
import type Control from "sap/ui/core/Control";
|
|
66
|
+
import type CustomData from "sap/ui/core/CustomData";
|
|
62
67
|
import UI5Element from "sap/ui/core/Element";
|
|
63
68
|
import type InvisibleText from "sap/ui/core/InvisibleText";
|
|
64
69
|
import Library from "sap/ui/core/Lib";
|
|
@@ -101,6 +106,8 @@ export type BindingParams = {
|
|
|
101
106
|
bPersistOPScroll?: boolean;
|
|
102
107
|
listBinding?: ODataListBinding;
|
|
103
108
|
showPlaceholder?: boolean;
|
|
109
|
+
requestOnBinding?: string[];
|
|
110
|
+
targetControlId?: string;
|
|
104
111
|
};
|
|
105
112
|
|
|
106
113
|
const ProgrammingModel = FELibrary.ProgrammingModel;
|
|
@@ -186,6 +193,44 @@ class ObjectPageController extends PageController {
|
|
|
186
193
|
|
|
187
194
|
private previousBindingContextPath?: string;
|
|
188
195
|
|
|
196
|
+
private pendingTargetControlId?: string;
|
|
197
|
+
|
|
198
|
+
private targetSubSection?: ObjectPageSubSection;
|
|
199
|
+
|
|
200
|
+
// Tracks if initial ObjectPage load has completed
|
|
201
|
+
private hasInitialLoadCompleted = false;
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Returns controls (Button / MenuButton) that are marked as "primary action".
|
|
205
|
+
* The marker is set by the converters when an action is emphasized (annotation-based) or
|
|
206
|
+
* configured as primaryAction (custom action defined in the manifest).
|
|
207
|
+
* @param root Root control to start the search from
|
|
208
|
+
* @param onlyActionable If true, only actionable (enabled) candidates are returned
|
|
209
|
+
* @returns An array of controls that are candidates for primary action shortcuts
|
|
210
|
+
*/
|
|
211
|
+
private _getPrimaryActionShortcutCandidates(root: ManagedObject, onlyActionable = false): Array<Button | MenuButton> {
|
|
212
|
+
return root.findAggregatedObjects(true, (object: ManagedObject) => {
|
|
213
|
+
const isButton = object.isA("sap.m.Button");
|
|
214
|
+
const isMenuButton = object.isA("sap.m.MenuButton");
|
|
215
|
+
if (!isButton && !isMenuButton) {
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
const ctrl = object as Button | MenuButton;
|
|
219
|
+
const flag = ctrl
|
|
220
|
+
.getCustomData()
|
|
221
|
+
.find((customData: CustomData) => customData.getKey() === "fePrimaryActionShortcut")
|
|
222
|
+
?.getValue();
|
|
223
|
+
if (flag !== true && flag !== "true") {
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
if (!ctrl.getVisible()) {
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
// For focus placement we must ignore disabled candidates.
|
|
230
|
+
return onlyActionable ? ctrl.getEnabled() : true;
|
|
231
|
+
}) as Array<Button | MenuButton>;
|
|
232
|
+
}
|
|
233
|
+
|
|
189
234
|
@publicExtension()
|
|
190
235
|
@finalExtension()
|
|
191
236
|
getExtensionAPI(sId?: string): ExtensionAPI {
|
|
@@ -298,12 +343,13 @@ class ObjectPageController extends PageController {
|
|
|
298
343
|
}
|
|
299
344
|
|
|
300
345
|
/**
|
|
301
|
-
*
|
|
346
|
+
* Add the sapUxAPObjectPageSubSectionFitContainer CSS class to every visible subsection that contains only a single GridTable or TreeTable.
|
|
347
|
+
* This makes the table fill the full height of its container so that the scrollbar appears on the table itself rather than on the page.
|
|
302
348
|
* @param subSections The sub sections to look for
|
|
303
349
|
*/
|
|
304
350
|
private checkSectionsForNonResponsiveTable(subSections: ObjectPageSubSection[]): void {
|
|
305
|
-
const changeClassForTables = (event: Event,
|
|
306
|
-
const blocks = [...
|
|
351
|
+
const changeClassForTables = (event: Event, visibleSubSection: ObjectPageSubSection): void => {
|
|
352
|
+
const blocks = [...visibleSubSection.getBlocks(), ...visibleSubSection.getMoreBlocks()];
|
|
307
353
|
if (blocks.length === 1) {
|
|
308
354
|
const table = this.searchTableInBlock(blocks[0] as SubSectionBlock);
|
|
309
355
|
if (!table) {
|
|
@@ -317,17 +363,15 @@ class ObjectPageController extends PageController {
|
|
|
317
363
|
tableAPI?.getTableDefinition().control.rowCountMode === "Auto"
|
|
318
364
|
) {
|
|
319
365
|
//In case there is only a single table in a subSection we fit that to the whole page so that the scrollbar comes only on table and not on page
|
|
320
|
-
|
|
321
|
-
|
|
366
|
+
visibleSubSection.addStyleClass("sapUxAPObjectPageSubSectionFitContainer");
|
|
367
|
+
visibleSubSection.detachEvent("modelContextChange", changeClassForTables, this);
|
|
322
368
|
}
|
|
323
369
|
}
|
|
324
370
|
};
|
|
325
|
-
for (
|
|
326
|
-
if (
|
|
327
|
-
const lastVisibleSubSection = subSections[subSectionIndex];
|
|
371
|
+
for (const subSection of subSections) {
|
|
372
|
+
if (subSection.getVisible()) {
|
|
328
373
|
// We need to attach this event in order to manage the Object Page Lazy Loading mechanism
|
|
329
|
-
|
|
330
|
-
break;
|
|
374
|
+
subSection.attachEvent("modelContextChange", subSection, changeClassForTables, this);
|
|
331
375
|
}
|
|
332
376
|
}
|
|
333
377
|
}
|
|
@@ -396,6 +440,9 @@ class ObjectPageController extends PageController {
|
|
|
396
440
|
}
|
|
397
441
|
|
|
398
442
|
_onBeforeBinding(oContext: ODataV4Context, mParameters: BindingParams = {}): void {
|
|
443
|
+
// Store targetControlId for scrolling after binding completes
|
|
444
|
+
this.pendingTargetControlId = mParameters.targetControlId;
|
|
445
|
+
|
|
399
446
|
// TODO: we should check how this comes together with the transaction helper, same to the change in the afterBinding
|
|
400
447
|
this.previousBindingContextPath = this.getView().getBindingContext()?.getPath();
|
|
401
448
|
const aTables = this._findTables(),
|
|
@@ -507,12 +554,21 @@ class ObjectPageController extends PageController {
|
|
|
507
554
|
}
|
|
508
555
|
|
|
509
556
|
/**
|
|
510
|
-
* Get the first clickable element in the
|
|
557
|
+
* Get the first clickable element in the object page.
|
|
511
558
|
* @private
|
|
512
559
|
* @param objectPage Object Page Layout control
|
|
513
|
-
* @returns The first clickable element found
|
|
560
|
+
* @returns The first clickable element found
|
|
514
561
|
*/
|
|
515
562
|
private getFirstClickableElement(objectPage: ObjectPageLayout): UI5Element | undefined {
|
|
563
|
+
// In display mode, if a visible primary action with the emphasis exists in the footer, it must be the first clickable element on the page.
|
|
564
|
+
const footer = objectPage.getFooter();
|
|
565
|
+
const footerPrimaryAction = footer
|
|
566
|
+
? this._getPrimaryActionShortcutCandidates(footer as unknown as ManagedObject, true)[0]
|
|
567
|
+
: undefined;
|
|
568
|
+
if (footerPrimaryAction) {
|
|
569
|
+
return footerPrimaryAction;
|
|
570
|
+
}
|
|
571
|
+
// Fallback: first visible & enabled header action (Edit, etc.)
|
|
516
572
|
let firstClickableElement;
|
|
517
573
|
const actions = objectPage.getHeaderTitle() && (objectPage.getHeaderTitle() as ObjectPageDynamicHeaderTitle).getActions();
|
|
518
574
|
if (actions?.length) {
|
|
@@ -648,6 +704,7 @@ class ObjectPageController extends PageController {
|
|
|
648
704
|
/**
|
|
649
705
|
* Set the initial focus in edit mode.
|
|
650
706
|
* @param aSubSections Object page sub sections
|
|
707
|
+
* @param fromTabNavigation Indicates if the focus is set after a tab navigation
|
|
651
708
|
*/
|
|
652
709
|
_updateFocusInEditMode(aSubSections: ObjectPageSubSection[], fromTabNavigation = false): void {
|
|
653
710
|
setTimeout(
|
|
@@ -655,7 +712,7 @@ class ObjectPageController extends PageController {
|
|
|
655
712
|
// We set the focus in a timeeout, otherwise the focus sometimes goes to the TabBar
|
|
656
713
|
const oObjectPage = this._getObjectPageLayoutControl();
|
|
657
714
|
const oMandatoryField = this._getFirstEmptyMandatoryFieldFromSubSection(aSubSections);
|
|
658
|
-
let oFieldToFocus;
|
|
715
|
+
let oFieldToFocus: UI5Element | undefined;
|
|
659
716
|
if (oMandatoryField) {
|
|
660
717
|
if (oMandatoryField.isA("sap.fe.macros.MultiValueField")) {
|
|
661
718
|
oFieldToFocus = (oMandatoryField as unknown as MultiValueFieldBlock).getMultiValueField();
|
|
@@ -671,9 +728,22 @@ class ObjectPageController extends PageController {
|
|
|
671
728
|
const focusInfo = oFieldToFocus.getFocusInfo() as { targetInfo: object };
|
|
672
729
|
focusInfo.targetInfo = { silent: true };
|
|
673
730
|
if (oFieldToFocus.isA("sap.ui.mdc.field.FieldInput")) {
|
|
674
|
-
oFieldToFocus = oFieldToFocus.getParent();
|
|
731
|
+
oFieldToFocus = oFieldToFocus.getParent() as UI5Element | undefined;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
if (
|
|
735
|
+
oFieldToFocus?.isA<SearchField>("sap.m.SearchField") &&
|
|
736
|
+
oFieldToFocus.getEnableSuggestions() &&
|
|
737
|
+
!oFieldToFocus.isBound("enableSuggestions")
|
|
738
|
+
) {
|
|
739
|
+
// In case the focus is set on a search field with suggestions, we temporarily disable the suggestions to avoid triggering them on focus
|
|
740
|
+
// unless this property is bound (to avoid modifying something in a model)
|
|
741
|
+
oFieldToFocus.setEnableSuggestions(false);
|
|
742
|
+
oFieldToFocus.focus(focusInfo);
|
|
743
|
+
oFieldToFocus.setEnableSuggestions(true);
|
|
744
|
+
} else {
|
|
745
|
+
oFieldToFocus?.focus(focusInfo);
|
|
675
746
|
}
|
|
676
|
-
oFieldToFocus.focus(focusInfo);
|
|
677
747
|
}
|
|
678
748
|
}.bind(this),
|
|
679
749
|
fromTabNavigation ? 300 : 0
|
|
@@ -735,15 +805,24 @@ class ObjectPageController extends PageController {
|
|
|
735
805
|
|
|
736
806
|
// Function to navigate back, or display the launchpad if we're on the first page of the history
|
|
737
807
|
const navBack = (): void => {
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
808
|
+
// Use the new Navigation API if it's available in the browser, to check if we can go back in history.
|
|
809
|
+
const canGoBack = (window as { navigation?: { canGoBack?: boolean } }).navigation?.canGoBack;
|
|
810
|
+
if (canGoBack === true) {
|
|
811
|
+
history.back();
|
|
812
|
+
} else if (canGoBack === false) {
|
|
813
|
+
this._routing.navigateBackFromContext(oContext);
|
|
814
|
+
} else {
|
|
815
|
+
// Fallback in case the browser doesn't support the Navigation API
|
|
816
|
+
const currentURL = document.URL;
|
|
817
|
+
history.back();
|
|
818
|
+
// In case there is no previous page in the history, history.back does nothing.
|
|
819
|
+
// In this case, we need to use navigateBackFromContext, that will display the home page
|
|
820
|
+
setTimeout(() => {
|
|
821
|
+
if (document.URL === currentURL) {
|
|
822
|
+
this._routing.navigateBackFromContext(oContext);
|
|
823
|
+
}
|
|
824
|
+
}, 500);
|
|
825
|
+
}
|
|
747
826
|
};
|
|
748
827
|
|
|
749
828
|
if (this.getAppComponent().getRouterProxy().checkIfBackHasSameContext()) {
|
|
@@ -775,13 +854,16 @@ class ObjectPageController extends PageController {
|
|
|
775
854
|
}
|
|
776
855
|
|
|
777
856
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
778
|
-
_onAfterBinding(inputBindingContext:
|
|
857
|
+
_onAfterBinding(inputBindingContext: ODataV4Context | undefined, mParameters: BindingParams | undefined): void {
|
|
779
858
|
const view = this.getView();
|
|
780
859
|
const viewLevel = view?.getViewData()?.viewLevel;
|
|
781
860
|
const oObjectPage = this._getObjectPageLayoutControl();
|
|
782
861
|
const aTables = this._findTables();
|
|
862
|
+
if ([NavigationReason.EditFlowAction, NavigationReason.RowPress].includes(mParameters?.reason as NavigationReason)) {
|
|
863
|
+
this.editFlow.storeSiblingContextData(inputBindingContext);
|
|
864
|
+
}
|
|
783
865
|
|
|
784
|
-
this.
|
|
866
|
+
this.sideEffects.clearFieldGroupsValidity();
|
|
785
867
|
|
|
786
868
|
// TODO: this is only a temp solution as long as the model fix the cache issue and we use this additional
|
|
787
869
|
// binding with ownRequest
|
|
@@ -939,6 +1021,143 @@ class ObjectPageController extends PageController {
|
|
|
939
1021
|
applyAppState,
|
|
940
1022
|
this.previousBindingContextPath !== this.getView().getBindingContext()?.getPath()
|
|
941
1023
|
);
|
|
1024
|
+
|
|
1025
|
+
// Capture synchronously to avoid race with subsequent navigations
|
|
1026
|
+
const pendingTargetControlId = this.pendingTargetControlId;
|
|
1027
|
+
this.pendingTargetControlId = undefined;
|
|
1028
|
+
|
|
1029
|
+
this.handlePendingTargetControlIdNavigation(applyAppState, oObjectPage, pendingTargetControlId);
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
/**
|
|
1033
|
+
* Handles navigation to a target section or subsection after app state is applied.
|
|
1034
|
+
* This coordinates targetControlId navigation from two sources:
|
|
1035
|
+
* - In-app navigation: pendingTargetControlId captured before applyAppState.
|
|
1036
|
+
* - Cross-app navigation: xAppState targetControlId stored by AppStateHandler during LR to OP transition.
|
|
1037
|
+
* @param applyAppState Promise that resolves when app state has been applied.
|
|
1038
|
+
* @param oObjectPage The ObjectPageLayout control.
|
|
1039
|
+
* @param pendingTargetControlId The targetControlId captured before applyAppState, if any.
|
|
1040
|
+
*/
|
|
1041
|
+
private handlePendingTargetControlIdNavigation(
|
|
1042
|
+
applyAppState: Promise<void | object>,
|
|
1043
|
+
oObjectPage: ObjectPageLayout,
|
|
1044
|
+
pendingTargetControlId: string | undefined
|
|
1045
|
+
): void {
|
|
1046
|
+
applyAppState
|
|
1047
|
+
.then(async () => {
|
|
1048
|
+
const xAppStateTargetControlId = this.getAppComponent().getAppStateHandler()?.consumePendingTargetControlId();
|
|
1049
|
+
const targetControlId = pendingTargetControlId ?? xAppStateTargetControlId;
|
|
1050
|
+
if (!targetControlId) {
|
|
1051
|
+
return;
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
// Wait for visibility bindings (needed for lazy loading scenarios)
|
|
1055
|
+
try {
|
|
1056
|
+
await Promise.all(this.waitForVisibilityBindings);
|
|
1057
|
+
} catch {
|
|
1058
|
+
// Some visibility bindings failed to resolve, proceeding anyway
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
// Wait for pageReady to ensure ObjectPage is fully initialized
|
|
1062
|
+
await this.pageReady.waitPageReady();
|
|
1063
|
+
|
|
1064
|
+
// Navigate to target section
|
|
1065
|
+
this.navigateToTargetSection(oObjectPage, targetControlId);
|
|
1066
|
+
return; // Required by linter rule promise/always-return
|
|
1067
|
+
})
|
|
1068
|
+
.catch(() => {
|
|
1069
|
+
// applyAppState failed - skip targetControlId navigation
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
/**
|
|
1074
|
+
* Navigates to a target section with automatic re-navigation if UX rules reset the selection.
|
|
1075
|
+
* Polls the section state since setSelectedSection() doesn't fire the navigate event.
|
|
1076
|
+
* @param oObjectPage The ObjectPageLayout control.
|
|
1077
|
+
* @param targetControlId The local ID of the target section or subsection.
|
|
1078
|
+
*/
|
|
1079
|
+
private navigateToTargetSection(oObjectPage: ObjectPageLayout, targetControlId: string): void {
|
|
1080
|
+
// Determine target section ID for comparison
|
|
1081
|
+
const targetControl = this.getView().byId(targetControlId);
|
|
1082
|
+
const isSection = targetControl?.isA("sap.uxap.ObjectPageSection");
|
|
1083
|
+
const targetSectionId = isSection ? targetControl?.getId() : (targetControl?.getParent() as ObjectPageSection)?.getId();
|
|
1084
|
+
|
|
1085
|
+
// Navigate function with guard to prevent duplicate calls when already on target
|
|
1086
|
+
const navigateToTarget = (): void => {
|
|
1087
|
+
if (oObjectPage.getSelectedSection() === targetSectionId) {
|
|
1088
|
+
return; // Already on target section
|
|
1089
|
+
}
|
|
1090
|
+
this.navigateWithConfirmation(targetControlId);
|
|
1091
|
+
};
|
|
1092
|
+
|
|
1093
|
+
// Navigate immediately
|
|
1094
|
+
navigateToTarget();
|
|
1095
|
+
|
|
1096
|
+
// Skip polling if already on target section (navigation was no-op or succeeded immediately)
|
|
1097
|
+
if (oObjectPage.getSelectedSection() === targetSectionId) {
|
|
1098
|
+
this.hasInitialLoadCompleted = true;
|
|
1099
|
+
return;
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
// Skip polling on subsequent navigations
|
|
1103
|
+
if (this.hasInitialLoadCompleted) {
|
|
1104
|
+
return;
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
// Poll to detect UX rules reset during initial load
|
|
1108
|
+
// Uses shared utility from helpers/SectionNavigationHelper.ts
|
|
1109
|
+
pollForSectionStability({
|
|
1110
|
+
getSelectedSection: () => oObjectPage.getSelectedSection(),
|
|
1111
|
+
targetSectionId,
|
|
1112
|
+
onResetDetected: navigateToTarget
|
|
1113
|
+
})
|
|
1114
|
+
.then(() => {
|
|
1115
|
+
this.hasInitialLoadCompleted = true;
|
|
1116
|
+
return; // Required by linter rule promise/always-return
|
|
1117
|
+
})
|
|
1118
|
+
.catch(() => {
|
|
1119
|
+
// Required by linter rule promise/catch-or-return - polling failures are non-critical
|
|
1120
|
+
});
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
/**
|
|
1124
|
+
* Sets targetSubSection based on the target control ID for app state persistence.
|
|
1125
|
+
* @param targetControlId The local ID of the target section or subsection.
|
|
1126
|
+
*/
|
|
1127
|
+
private setTargetSubSection(targetControlId: string): void {
|
|
1128
|
+
const targetControl = this.getView().byId(targetControlId);
|
|
1129
|
+
if (targetControl?.isA<ObjectPageSubSection>("sap.uxap.ObjectPageSubSection")) {
|
|
1130
|
+
this.targetSubSection = targetControl;
|
|
1131
|
+
} else if (targetControl?.isA<ObjectPageSection>("sap.uxap.ObjectPageSection")) {
|
|
1132
|
+
const subSections = targetControl.getSubSections();
|
|
1133
|
+
this.targetSubSection = subSections[0];
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
/**
|
|
1138
|
+
* Performs navigation to the target section and then updates the app state.
|
|
1139
|
+
* @param targetControlId The local ID of the target section or subsection.
|
|
1140
|
+
*/
|
|
1141
|
+
private navigateWithConfirmation(targetControlId: string): void {
|
|
1142
|
+
// Set targetSubSection BEFORE navigation for app state saving.
|
|
1143
|
+
// scrollToSection() doesn't fire the navigate event, so onNavigateChange won't be called.
|
|
1144
|
+
this.setTargetSubSection(targetControlId);
|
|
1145
|
+
|
|
1146
|
+
this.getExtensionAPI().navigateToSubSection(targetControlId);
|
|
1147
|
+
|
|
1148
|
+
// Wait for ExtensionAPI polling to complete (~100ms) plus scroll to finish
|
|
1149
|
+
// before updating app state and setting focus.
|
|
1150
|
+
setTimeout(() => {
|
|
1151
|
+
this.viewState.updateAppStateDebounced();
|
|
1152
|
+
|
|
1153
|
+
// Set focus if in edit mode (for in-app navigation to sub-object page in edit mode)
|
|
1154
|
+
// This is needed because scrollToSection() doesn't fire onNavigateChange,
|
|
1155
|
+
// so we need to manually trigger focus setting here
|
|
1156
|
+
const isInEditMode = CommonUtils.getIsEditable(this.getView());
|
|
1157
|
+
if (isInEditMode && this.targetSubSection) {
|
|
1158
|
+
this._updateFocusInEditMode([this.targetSubSection], false);
|
|
1159
|
+
}
|
|
1160
|
+
}, 150);
|
|
942
1161
|
}
|
|
943
1162
|
|
|
944
1163
|
/**
|
|
@@ -1339,9 +1558,7 @@ class ObjectPageController extends PageController {
|
|
|
1339
1558
|
// If there is at least one global SideEffects for the related entity, execute it/them
|
|
1340
1559
|
if (globalSideEffects.length) {
|
|
1341
1560
|
await this.editFlow.syncTask();
|
|
1342
|
-
return Promise.all(
|
|
1343
|
-
globalSideEffects.map(async (sideEffects) => this._sideEffects.requestSideEffects(sideEffects, context))
|
|
1344
|
-
);
|
|
1561
|
+
return Promise.all(globalSideEffects.map(async (sideEffects) => this.sideEffects.requestSideEffects(sideEffects, context)));
|
|
1345
1562
|
}
|
|
1346
1563
|
}
|
|
1347
1564
|
|
|
@@ -1415,10 +1632,11 @@ class ObjectPageController extends PageController {
|
|
|
1415
1632
|
);
|
|
1416
1633
|
}
|
|
1417
1634
|
});
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1635
|
+
let isStandardSave: boolean | undefined;
|
|
1636
|
+
if (skipBindingToView && typeof skipBindingToView === "object") {
|
|
1637
|
+
const command = (skipBindingToView as CommandExecution$ExecuteEvent)?.getSource?.()?.getCommand?.();
|
|
1638
|
+
isStandardSave = command === "Save";
|
|
1639
|
+
}
|
|
1422
1640
|
const bindings = await Promise.all(awaitCreateDocuments);
|
|
1423
1641
|
// We need to either reject or resolve a promise here and return it since this save
|
|
1424
1642
|
// function is not only called when pressing the save button in the footer, but also
|
|
@@ -1961,8 +2179,27 @@ class ObjectPageController extends PageController {
|
|
|
1961
2179
|
}
|
|
1962
2180
|
): void {
|
|
1963
2181
|
/**
|
|
1964
|
-
* Invokes the page primary action
|
|
2182
|
+
* Invokes the page primary action when the user presses Ctrl+Enter (Cmd+Enter on macOS).
|
|
1965
2183
|
*/
|
|
2184
|
+
|
|
2185
|
+
const primaryCandidate = oController._getPrimaryActionShortcutCandidates(oView)[0];
|
|
2186
|
+
|
|
2187
|
+
/* By design, there must be at most one candidate due to the "single emphasis per page" rule.
|
|
2188
|
+
If a primary-enter target is found, it must fully take over Ctrl/Cmd+Enter.*/
|
|
2189
|
+
|
|
2190
|
+
if (primaryCandidate) {
|
|
2191
|
+
if (!primaryCandidate.getEnabled()) {
|
|
2192
|
+
// Take over Ctrl/Cmd+Enter, but do not trigger anything when the primary action is disabled.
|
|
2193
|
+
return;
|
|
2194
|
+
}
|
|
2195
|
+
if (primaryCandidate.isA("sap.m.Button")) {
|
|
2196
|
+
(primaryCandidate as Button).firePress();
|
|
2197
|
+
}
|
|
2198
|
+
if ((primaryCandidate as MenuButton).isA("sap.m.MenuButton")) {
|
|
2199
|
+
(primaryCandidate as MenuButton).fireDefaultAction();
|
|
2200
|
+
}
|
|
2201
|
+
return;
|
|
2202
|
+
}
|
|
1966
2203
|
const iViewLevel = oController.getView().getViewData().viewLevel;
|
|
1967
2204
|
if (mConditions.positiveActionVisible) {
|
|
1968
2205
|
if (mConditions.positiveActionEnabled) {
|
|
@@ -1990,7 +2227,7 @@ class ObjectPageController extends PageController {
|
|
|
1990
2227
|
const tableAPI = event.getSource();
|
|
1991
2228
|
const table = tableAPI.content;
|
|
1992
2229
|
const currentActionPromise = this.editFlow.getCurrentActionPromise();
|
|
1993
|
-
const tableContexts = this._getTableBinding(table)?.
|
|
2230
|
+
const tableContexts = this._getTableBinding(table)?.getAllCurrentContexts();
|
|
1994
2231
|
|
|
1995
2232
|
if (currentActionPromise && tableContexts?.length) {
|
|
1996
2233
|
try {
|
|
@@ -1998,10 +2235,13 @@ class ObjectPageController extends PageController {
|
|
|
1998
2235
|
if (actionResponse?.controlId === table.getId()) {
|
|
1999
2236
|
const actionData = actionResponse.oData;
|
|
2000
2237
|
const keys = actionResponse.keys;
|
|
2001
|
-
const newItem =
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2238
|
+
const newItem =
|
|
2239
|
+
tableContexts
|
|
2240
|
+
.find((tableContext: Context) => {
|
|
2241
|
+
const tableData = tableContext.getObject();
|
|
2242
|
+
return keys.every((key: string) => tableData[key] === actionData[key]);
|
|
2243
|
+
})
|
|
2244
|
+
?.getIndex() ?? -1;
|
|
2005
2245
|
if (newItem !== -1) {
|
|
2006
2246
|
const dialog = InstanceManager.getOpenDialogs().find((dialog) => dialog.data("FullScreenDialog") !== true);
|
|
2007
2247
|
if (dialog) {
|
|
@@ -2136,17 +2376,21 @@ class ObjectPageController extends PageController {
|
|
|
2136
2376
|
|
|
2137
2377
|
onNavigateChange(this: ObjectPageController, oEvent: UI5Event<{ subSection: ObjectPageSubSection }>): void {
|
|
2138
2378
|
//will be called always when we click on a section tab
|
|
2139
|
-
this.getExtensionAPI().updateAppState();
|
|
2140
2379
|
this.bSectionNavigated = true;
|
|
2141
2380
|
|
|
2381
|
+
const subSection = oEvent.getParameter("subSection");
|
|
2382
|
+
this.targetSubSection = subSection;
|
|
2383
|
+
|
|
2384
|
+
this.getExtensionAPI().updateAppState();
|
|
2385
|
+
|
|
2142
2386
|
const oInternalModelContext = this.getView().getBindingContext("internal") as InternalModelContext;
|
|
2387
|
+
|
|
2143
2388
|
if (
|
|
2144
2389
|
CommonUtils.getIsEditable(this.getView()) &&
|
|
2145
2390
|
this.getView().getViewData().sectionLayout === "Tabs" &&
|
|
2146
2391
|
oInternalModelContext.getProperty("errorNavigationSectionFlag") === false
|
|
2147
2392
|
) {
|
|
2148
|
-
|
|
2149
|
-
this._updateFocusInEditMode([oSubSection], true);
|
|
2393
|
+
this._updateFocusInEditMode([subSection], true);
|
|
2150
2394
|
}
|
|
2151
2395
|
},
|
|
2152
2396
|
onVariantSelected: function (this: ObjectPageController): void {
|