@openui5/sap.ui.documentation 1.151.0 → 1.152.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 (66) hide show
  1. package/THIRDPARTY.txt +5 -5
  2. package/package.json +7 -7
  3. package/src/sap/ui/documentation/.library +1 -1
  4. package/src/sap/ui/documentation/library.js +2 -2
  5. package/src/sap/ui/documentation/messagebundle.properties +6 -0
  6. package/src/sap/ui/documentation/messagebundle_ar.properties +3 -0
  7. package/src/sap/ui/documentation/messagebundle_bg.properties +3 -0
  8. package/src/sap/ui/documentation/messagebundle_ca.properties +3 -0
  9. package/src/sap/ui/documentation/messagebundle_cnr.properties +3 -0
  10. package/src/sap/ui/documentation/messagebundle_cs.properties +3 -0
  11. package/src/sap/ui/documentation/messagebundle_cy.properties +3 -0
  12. package/src/sap/ui/documentation/messagebundle_da.properties +3 -0
  13. package/src/sap/ui/documentation/messagebundle_de.properties +3 -0
  14. package/src/sap/ui/documentation/messagebundle_el.properties +3 -0
  15. package/src/sap/ui/documentation/messagebundle_en.properties +3 -0
  16. package/src/sap/ui/documentation/messagebundle_en_GB.properties +3 -0
  17. package/src/sap/ui/documentation/messagebundle_en_US_sappsd.properties +3 -0
  18. package/src/sap/ui/documentation/messagebundle_en_US_saprigi.properties +3 -0
  19. package/src/sap/ui/documentation/messagebundle_en_US_saptrc.properties +3 -0
  20. package/src/sap/ui/documentation/messagebundle_es.properties +3 -0
  21. package/src/sap/ui/documentation/messagebundle_es_MX.properties +3 -0
  22. package/src/sap/ui/documentation/messagebundle_et.properties +3 -0
  23. package/src/sap/ui/documentation/messagebundle_fi.properties +3 -0
  24. package/src/sap/ui/documentation/messagebundle_fr.properties +3 -0
  25. package/src/sap/ui/documentation/messagebundle_fr_CA.properties +3 -0
  26. package/src/sap/ui/documentation/messagebundle_hi.properties +3 -0
  27. package/src/sap/ui/documentation/messagebundle_hr.properties +3 -0
  28. package/src/sap/ui/documentation/messagebundle_hu.properties +3 -0
  29. package/src/sap/ui/documentation/messagebundle_id.properties +3 -0
  30. package/src/sap/ui/documentation/messagebundle_it.properties +3 -0
  31. package/src/sap/ui/documentation/messagebundle_iw.properties +3 -0
  32. package/src/sap/ui/documentation/messagebundle_ja.properties +3 -0
  33. package/src/sap/ui/documentation/messagebundle_kk.properties +3 -0
  34. package/src/sap/ui/documentation/messagebundle_ko.properties +3 -0
  35. package/src/sap/ui/documentation/messagebundle_lt.properties +3 -0
  36. package/src/sap/ui/documentation/messagebundle_lv.properties +3 -0
  37. package/src/sap/ui/documentation/messagebundle_mk.properties +3 -0
  38. package/src/sap/ui/documentation/messagebundle_ms.properties +3 -0
  39. package/src/sap/ui/documentation/messagebundle_nl.properties +3 -0
  40. package/src/sap/ui/documentation/messagebundle_no.properties +3 -0
  41. package/src/sap/ui/documentation/messagebundle_pl.properties +3 -0
  42. package/src/sap/ui/documentation/messagebundle_pt.properties +3 -0
  43. package/src/sap/ui/documentation/messagebundle_pt_PT.properties +3 -0
  44. package/src/sap/ui/documentation/messagebundle_ro.properties +3 -0
  45. package/src/sap/ui/documentation/messagebundle_ru.properties +3 -0
  46. package/src/sap/ui/documentation/messagebundle_sh.properties +3 -0
  47. package/src/sap/ui/documentation/messagebundle_sk.properties +3 -0
  48. package/src/sap/ui/documentation/messagebundle_sl.properties +3 -0
  49. package/src/sap/ui/documentation/messagebundle_sr.properties +3 -0
  50. package/src/sap/ui/documentation/messagebundle_sv.properties +3 -0
  51. package/src/sap/ui/documentation/messagebundle_th.properties +3 -0
  52. package/src/sap/ui/documentation/messagebundle_tr.properties +3 -0
  53. package/src/sap/ui/documentation/messagebundle_uk.properties +3 -0
  54. package/src/sap/ui/documentation/messagebundle_vi.properties +3 -0
  55. package/src/sap/ui/documentation/messagebundle_zh_CN.properties +3 -0
  56. package/src/sap/ui/documentation/messagebundle_zh_TW.properties +3 -0
  57. package/src/sap/ui/documentation/sdk/controller/App.controller.js +332 -342
  58. package/src/sap/ui/documentation/sdk/controller/SubApiDetail.controller.js +5 -1
  59. package/src/sap/ui/documentation/sdk/controller/Welcome.controller.js +1 -1
  60. package/src/sap/ui/documentation/sdk/controller/util/SearchRenderer.js +167 -0
  61. package/src/sap/ui/documentation/sdk/controller/util/UsageTracker.js +27 -14
  62. package/src/sap/ui/documentation/sdk/thirdparty/highlight.js/highlight.js +3 -3
  63. package/src/sap/ui/documentation/sdk/view/App.view.xml +202 -192
  64. package/src/sap/ui/documentation/themes/base/Documentation.less +26 -141
  65. package/src/sap/ui/documentation/themes/sap_belize_base/Documentation.less +0 -140
  66. package/src/sap/ui/documentation/sdk/view/GlobalSearchPicker.fragment.xml +0 -73
@@ -24,16 +24,13 @@ sap.ui.define([
24
24
  "sap/m/library",
25
25
  "sap/base/Log",
26
26
  "sap/base/util/Version",
27
- "sap/ui/core/Core",
28
27
  "sap/ui/util/openWindow",
29
28
  "sap/ui/documentation/sdk/model/formatter",
30
- "sap/m/ResponsivePopover",
31
- "sap/ui/documentation/sdk/controller/util/Highlighter",
32
29
  "sap/m/Button",
33
- "sap/m/Toolbar",
34
30
  "sap/ui/documentation/sdk/util/Resources",
35
31
  'sap/base/util/LoaderExtensions',
36
- "sap/ui/documentation/sdk/controller/util/ThemePicker"
32
+ "sap/ui/documentation/sdk/controller/util/ThemePicker",
33
+ "sap/ui/documentation/sdk/controller/util/SearchRenderer"
37
34
  ], function(
38
35
  Localization,
39
36
  Element,
@@ -54,16 +51,13 @@ sap.ui.define([
54
51
  mobileLibrary,
55
52
  Log,
56
53
  Version,
57
- Core,
58
54
  openWindow,
59
55
  globalFormatter,
60
- ResponsivePopover,
61
- Highlighter,
62
56
  Button,
63
- Toolbar,
64
57
  ResourcesUtil,
65
58
  LoaderExtensions,
66
- ThemePicker
59
+ ThemePicker,
60
+ SearchRenderer
67
61
  ) {
68
62
  "use strict";
69
63
 
@@ -146,7 +140,6 @@ sap.ui.define([
146
140
  bShowVersionSwitchButton: false,
147
141
  bLandscape: Device.orientation.landscape,
148
142
  bHasMaster: false,
149
- bSearchMode: false,
150
143
  bHideTopicSection: !!window['sap-ui-documentation-hideTopicSection'],
151
144
  bHideApiSection: !!window['sap-ui-documentation-hideApiSection'],
152
145
  sAboutInfoSAPUI5: "Looking for the Demo Kit for a specific SAPUI5 version? " +
@@ -165,6 +158,10 @@ sap.ui.define([
165
158
 
166
159
  ThemePicker.init(oController);
167
160
 
161
+ this.getModel("i18n").getResourceBundle().then(function (oBundle) {
162
+ this._oI18nBundle = oBundle;
163
+ }.bind(this));
164
+
168
165
  this.MENU_LINKS_MAP = {
169
166
  "copyright": "https://www.sap.com/corporate/en/legal/copyright.html",
170
167
  "trademark": "https://www.sap.com/corporate/en/legal/trademark.html",
@@ -278,7 +275,7 @@ sap.ui.define([
278
275
  }.bind(this));
279
276
 
280
277
  // Handle page resize
281
- ResizeHandler.register(this._demoKitPage, this.onPageResize.bind(this));
278
+ this._sResizeHandlerId = ResizeHandler.register(this._demoKitPage, this.onPageResize.bind(this));
282
279
  },
283
280
 
284
281
  onBeforeRendering: function () {
@@ -294,16 +291,16 @@ sap.ui.define([
294
291
  Device.orientation.attachHandler(this._onOrientationChange, this);
295
292
 
296
293
  this._syncNewsModelWithNewsInfo();
297
-
298
- // Adds additional class in order to manipulate the version switch button content in the header
299
- this._adjustVersionSwitchButton();
300
294
  },
301
295
 
302
296
  onExit: function () {
303
297
  Device.orientation.detachHandler(this._onOrientationChange, this);
304
298
 
305
- if (this.highlighter) {
306
- this.highlighter.destroy();
299
+ clearTimeout(this._iSearchDebounceTimer);
300
+
301
+ if (this._sResizeHandlerId) {
302
+ ResizeHandler.deregister(this._sResizeHandlerId);
303
+ this._sResizeHandlerId = null;
307
304
  }
308
305
 
309
306
  this._oCookiesConsentManager = null;
@@ -413,7 +410,7 @@ sap.ui.define([
413
410
  },
414
411
 
415
412
  onNewsButtonPress: function (oEvent) {
416
- var oButton = oEvent.getSource(),
413
+ var oOpener = oEvent.getParameter("targetRef"),
417
414
  oView = this.getView();
418
415
 
419
416
  if (this._oNewsModel.getProperty("/newsCount") === 0) {
@@ -425,10 +422,10 @@ sap.ui.define([
425
422
  }).then(function (oPopover) {
426
423
  oView.addDependent(oPopover);
427
424
  this._oNewsPopover = oPopover;
428
- this._oNewsPopover.openBy(oButton);
425
+ this._oNewsPopover.openBy(oOpener);
429
426
  }.bind(this));
430
427
  } else {
431
- this._oNewsPopover.openBy(oButton);
428
+ this._oNewsPopover.openBy(oOpener);
432
429
  }
433
430
  },
434
431
 
@@ -512,8 +509,47 @@ sap.ui.define([
512
509
  this._oNewsModel.setProperty("/newsPreparationFailureMessage", sPreparationFailureMessage);
513
510
  },
514
511
 
512
+ /**
513
+ * Opens the policies (legal) menu anchored to its button.
514
+ *
515
+ * @param {sap.ui.base.Event} oEvent The button's click event
516
+ */
517
+ onOpenPoliciesMenu: function (oEvent) {
518
+ this._openHeaderMenu("policiesMenu", oEvent.getSource());
519
+ },
520
+
521
+ /**
522
+ * Opens the settings/about menu anchored to its button.
523
+ *
524
+ * @param {sap.ui.base.Event} oEvent The button's click event
525
+ */
526
+ onOpenAboutMenu: function (oEvent) {
527
+ this._openHeaderMenu("aboutMenu", oEvent.getSource());
528
+ },
529
+
530
+ /**
531
+ * Opens a header Web Component menu below the given opener.
532
+ *
533
+ * @param {string} sMenuId The stable (non-prefixed) id of the menu control
534
+ * @param {sap.ui.core.Control} oOpener The control that triggered the menu
535
+ * @private
536
+ */
537
+ _openHeaderMenu: function (sMenuId, oOpener) {
538
+ var oMenu = this.byId(sMenuId);
539
+
540
+ // The opener must be a control (or its id): a UI5 association does not accept
541
+ // a raw DOM node, so passing the click event's targetRef would leave the
542
+ // menu unanchored (positioned at the top-left of the page).
543
+ oMenu.setAssociation("opener", oOpener);
544
+ oMenu.setOpen(true);
545
+ },
546
+
515
547
  onMenuButtonItemSelected: function (oEvent) {
516
- var sTargetText = oEvent.getParameter("item").getKey(),
548
+ var oItem = oEvent.getParameter("item"),
549
+ oKeyData = oItem && oItem.getCustomData().find(function (oData) {
550
+ return oData.getKey() === "action";
551
+ }),
552
+ sTargetText = oKeyData ? oKeyData.getValue() : undefined,
517
553
  sTarget = this.MENU_LINKS_MAP[sTargetText];
518
554
 
519
555
  if (sTargetText === ABOUT_TEXT) {
@@ -536,180 +572,6 @@ sap.ui.define([
536
572
  this.sTarget = sTarget;
537
573
  },
538
574
 
539
- createSearchPopover: function () {
540
- var PlacementType = mobileLibrary.PlacementType,
541
- searchInput = this.getView().byId("searchControl"),
542
- oPopover = new ResponsivePopover({
543
- showArrow: false,
544
- showHeader: false,
545
- contentWidth: "600px",
546
- placement: PlacementType.Vertical,
547
- horizontalScrolling: false,
548
- initialFocus: this.getView().byId("searchControl-searchField")
549
- }).addStyleClass("sapMSltPicker-CTX");
550
-
551
- // implement the same <code>open</code> function as in the dialog
552
- // to allow the controller open the search picker regardless its type (popover or dialog)
553
- oPopover.open = function () {
554
- oPopover.openBy(searchInput);
555
- };
556
-
557
- this.getView().addDependent(oPopover);
558
-
559
- return oPopover;
560
- },
561
-
562
- createSearchPicker: function () {
563
- var oPicker = Device.system.phone ? this.createSearchDialog() : this.createSearchPopover();
564
- this.createSearchPickerContent().then(function (oContent) {
565
- oPicker.addContent(oContent);
566
- });
567
- return oPicker;
568
- },
569
-
570
- createSearchDialog: function () {
571
- var dialog,
572
- originalValue,
573
- dialogSearchField,
574
- customHeader,
575
- okButton,
576
- closeButton,
577
- bSearchRequested;
578
-
579
- var oInput = this.getView().byId("searchControl");
580
-
581
- // Helper functions
582
- function handleDialogButtonPress() {
583
- var sCurrentSearchValue = oInput.getValue();
584
-
585
- if (sCurrentSearchValue !== originalValue) {
586
- oInput._updateValue(sCurrentSearchValue);
587
- oInput.fireLiveChange({ newValue: sCurrentSearchValue });
588
- } else {
589
- oInput.fireLiveChange({ newValue: originalValue });
590
- }
591
-
592
- dialog.close();
593
- oInput._toggleOpen(false);
594
- }
595
-
596
- function moveCursorToEnd(el) {
597
- if (typeof el.selectionStart == "number") {
598
- el.selectionStart = el.selectionEnd = el.value.length;
599
- } else if (typeof el.createTextRange != "undefined") {
600
- el.focus();
601
- var range = el.createTextRange();
602
- range.collapse(false);
603
- range.select();
604
- }
605
- }
606
-
607
- // use sap.ui.require to avoid circular dependency between the SearchField and Suggest
608
- dialogSearchField = new (sap.ui.require('sap/m/SearchField'))({
609
- liveChange: function (oEvent) {
610
- var value = oEvent.getParameter("newValue");
611
- oInput._updateValue(value);
612
- oInput.fireLiveChange({ newValue: value });
613
- },
614
- search: function (oEvent) {
615
- if (!oEvent.getParameter("clearButtonPressed")) {
616
- dialog.close();
617
- bSearchRequested = true;
618
- }
619
- }
620
- });
621
-
622
- closeButton = new Button({
623
- icon: "sap-icon://decline",
624
- press: function () {
625
- handleDialogButtonPress();
626
- }
627
- });
628
-
629
- customHeader = new Toolbar({
630
- content: [dialogSearchField, closeButton]
631
- });
632
-
633
- okButton = new Button({
634
- text: Library.getResourceBundleFor("sap.m").getText("MSGBOX_OK"),
635
- press: function () {
636
- handleDialogButtonPress();
637
- }
638
- });
639
-
640
- dialog = new (sap.ui.require('sap/m/Dialog'))({
641
- stretch: true,
642
- customHeader: customHeader,
643
- beginButton: okButton,
644
- beforeClose: function () {
645
- oInput._bSuggestionSuppressed = true;
646
- },
647
- beforeOpen: function () {
648
- originalValue = oInput.getValue();
649
- dialogSearchField._updateValue(originalValue);
650
- bSearchRequested = false; // reset flag
651
- },
652
- afterOpen: function () {
653
- var $input = dialogSearchField.$().find('input');
654
- $input.trigger("focus");
655
- moveCursorToEnd($input.get(0));
656
- },
657
- afterClose: function (oEvent) {
658
- if (bSearchRequested) { // fire the search event if not cancelled
659
- oInput.fireSearch({
660
- query: oInput.getValue(),
661
- clearButtonPressed: false
662
- });
663
- }
664
-
665
- oInput._toggleOpen(false);
666
- }
667
- });
668
-
669
- this.getView().addDependent(dialog);
670
-
671
- return dialog;
672
- },
673
-
674
- openSearchPicker: function () {
675
- if (!this.oPicker) {
676
- this.oPicker = this.createSearchPicker();
677
- }
678
-
679
- if (!this.oPicker.isOpen()) {
680
- this.oPicker.open();
681
- }
682
- },
683
-
684
- createSearchPickerContent: function () {
685
- return Fragment.load({
686
- name: "sap.ui.documentation.sdk.view.GlobalSearchPicker",
687
- controller: this
688
- }).then(function (oContent) {
689
-
690
- var oShortList = Element.getElementById("shortList"),
691
- oController = this,
692
- sSearchQuery;
693
-
694
- oShortList.addEventDelegate({
695
- onAfterRendering: function () {
696
- var oConfig = {
697
- useExternalStyles: false,
698
- shouldBeObserved: true,
699
- isCaseSensitive: false
700
- };
701
-
702
- oController.highlighter = new Highlighter(oShortList.getDomRef(), oConfig);
703
- sSearchQuery = oController.getModel("searchData").getProperty("/query");
704
- sSearchQuery && oController.highlighter.highlight(sSearchQuery);
705
- oShortList.removeEventDelegate(this);
706
- }
707
- });
708
-
709
- return oContent;
710
- }.bind(this));
711
- },
712
-
713
575
  initSearch: function () {
714
576
  // set the search data to custom search`s suggestions
715
577
  var oModel = this.getModel("searchData"),
@@ -734,61 +596,6 @@ sap.ui.define([
734
596
  }.bind(this));
735
597
  },
736
598
 
737
- getSearchPickerTitle: function (oContext) {
738
- var getMessageBundle = Core.getLibraryResourceBundle("sap.ui.documentation"),
739
- sTitle;
740
-
741
- switch (this.getModel("searchData").getProperty("/preferencedCategory")) {
742
- case "topics":
743
- sTitle = getMessageBundle.getText("SEARCH_SUGGESTIONS_TITLE_DOCUMENTATION");
744
- break;
745
- case "apiref":
746
- sTitle = getMessageBundle.getText("SEARCH_SUGGESTIONS_TITLE_API_REFERENCE");
747
- break;
748
- case "entity":
749
- sTitle = getMessageBundle.getText("SEARCH_SUGGESTIONS_TITLE_SAMPLES");
750
- break;
751
- default:
752
- sTitle = getMessageBundle.getText("SEARCH_SUGGESTIONS_TITLE_ALL");
753
- }
754
-
755
- return sTitle;
756
- },
757
-
758
- formatSuggestionTitle: function (sTitle, sSummary) {
759
- var sFormatted = sTitle || "";
760
- if (sSummary) {
761
- sFormatted += ": " + sSummary;
762
- }
763
- return sFormatted;
764
- },
765
-
766
- onSearchResultsSummaryPress: function (oEvent) {
767
- var sCategory = oEvent.oSource.data("category");
768
- this.navToSearchResults(sCategory);
769
- },
770
-
771
- onSearchPickerItemPress: function (oEvent) {
772
- var contextPath = oEvent.oSource.getBindingContextPath(),
773
- oDataItem = this.getModel("searchData").getProperty(contextPath),
774
- oSearchDataModel = this.getOwnerComponent().getModel("searchData"),
775
- sQuery = oSearchDataModel.getProperty("/query"),
776
- isDocumentationItem = oDataItem.category === "Documentation",
777
- baseURL = oDataItem.external ? new URL(oDataItem.path, document.baseURI).href : oDataItem.path;
778
-
779
- if (sQuery && isDocumentationItem) {
780
- baseURL += `?q=${encodeURIComponent(sQuery)}`;
781
- }
782
-
783
- if (oDataItem.external) {
784
- openWindow(baseURL);
785
- } else {
786
- this.getRouter().parsePath(baseURL);
787
- }
788
-
789
- this.oPicker.close();
790
- },
791
-
792
599
  /**
793
600
  * Updates the appearance of the Demo Kit depending of the incoming appearance keyword.
794
601
  * If the keyword is "auto" the appearance will be updated to light or dark depending on the
@@ -1323,7 +1130,8 @@ sap.ui.define([
1323
1130
  * @param {boolean} bMenu Whether the pressed button is hidden in the 'About' menu
1324
1131
  */
1325
1132
  launchSurvey: function (oEvent, bMenu) {
1326
- var oTarget = this.byId(!bMenu ? "surveyButton" : "aboutMenuButton"),
1133
+ var oTarget = (oEvent && oEvent.getParameter && oEvent.getParameter("targetRef"))
1134
+ || this.byId(bMenu ? "aboutMenuButton" : "surveyButton"),
1327
1135
  oView = this.getView(),
1328
1136
  oViewModel = this.getModel("appView"),
1329
1137
  bShowLongSurvey = oViewModel.getProperty("/bShowLongSurvey");
@@ -1444,14 +1252,56 @@ sap.ui.define([
1444
1252
  },
1445
1253
 
1446
1254
  onSearch: function (oEvent) {
1447
- var sQuery = encodeURIComponent(oEvent.getParameter("query"));
1448
-
1449
- if (!sQuery) {
1450
- return;
1255
+ var oItem = oEvent.getParameter("item"),
1256
+ // Use the model query as the authoritative value — the DOM value may have
1257
+ // been replaced by ShellBarSearch typeahead autocomplete (e.g. "g" → "Git Guidelines").
1258
+ sQuery = this.getModel("searchData").getProperty("/query") || "";
1259
+
1260
+ if (oItem) {
1261
+ var oPayload = typeof oItem.data === "function" ? oItem.data("_payload") : null;
1262
+ if (!oPayload) { return; }
1263
+
1264
+ switch (oPayload.type) {
1265
+ case "noData":
1266
+ return;
1267
+ case "allResults":
1268
+ this.navToSearchResults();
1269
+ this._restoreSearchValue(sQuery);
1270
+ return;
1271
+ case "category":
1272
+ if (sQuery) {
1273
+ this.getRouter().navTo("search", { searchParam: encodeURIComponent(sQuery), "?options": { category: oPayload.key } }, false);
1274
+ }
1275
+ this._restoreSearchValue(sQuery);
1276
+ return;
1277
+ case "result":
1278
+ var sItemText = oPayload.item.summary ? oPayload.item.title + ": " + oPayload.item.summary : (oPayload.item.title || sQuery);
1279
+ this._navigateToSearchItem(oPayload.item, sQuery);
1280
+ this._restoreSearchValue(sItemText);
1281
+ return;
1282
+ }
1451
1283
  }
1452
1284
 
1453
- this.getRouter().navTo("search", { searchParam: sQuery }, false);
1454
- this.oPicker.close();
1285
+ // Plain Enter fall back to DOM value in case model hasn't updated yet (e.g. paste)
1286
+ sQuery = sQuery || this._getSearchValue();
1287
+ if (!sQuery) { return; }
1288
+ this.getRouter().navTo("search", { searchParam: encodeURIComponent(sQuery) }, false);
1289
+ },
1290
+
1291
+ /**
1292
+ * Navigates to a selected search result item (internal route or external link).
1293
+ *
1294
+ * @param {object} oDataItem The search match backing the selected suggestion item
1295
+ * @param {string} sQuery The current search query (appended to Documentation links)
1296
+ * @private
1297
+ */
1298
+ _navigateToSearchItem: function (oDataItem, sQuery) {
1299
+ if (oDataItem.external) {
1300
+ openWindow(new URL(oDataItem.path, document.baseURI).href);
1301
+ } else {
1302
+ var sPath = oDataItem.path + (sQuery && oDataItem.category === "Documentation" ? "?q=" + encodeURIComponent(sQuery) : "");
1303
+ this.getRouter().parsePath(sPath);
1304
+ }
1455
1305
  },
1456
1306
 
1457
1307
  navToSearchResults: function (sCategory) {
@@ -1469,7 +1319,7 @@ sap.ui.define([
1469
1319
  }
1470
1320
 
1471
1321
  this.getRouter().navTo("search", oRouterParams, true);
1472
- this.oPicker.close();
1322
+ if (this._getSearchControl()) { SearchRenderer.clear(this._getSearchControl()); }
1473
1323
  },
1474
1324
 
1475
1325
  /**
@@ -1480,35 +1330,172 @@ sap.ui.define([
1480
1330
  * @param {Object} oEvent - The live change event object.
1481
1331
  * @returns {void}
1482
1332
  */
1483
- onSearchLiveChange: function (oEvent) {
1484
- var oModel = this.getModel("searchData"),
1485
- sQuery = oEvent.getParameter("newValue"),
1486
- sPreferencedCategory = oModel.getProperty("/preferencedCategory"),
1487
- bIncludeDeprecated = oModel.getProperty("/includeDeprecated");
1488
-
1489
- // Handle the case when the user deletes the query and the search picker is open
1490
- // Only on desktop, because on mobile we don't want to close the picker
1491
- if (!sQuery && Device.system.desktop) {
1492
- if (this.oPicker && this.oPicker.isOpen()) {
1493
- this.oPicker.close();
1494
- }
1333
+ onSearchLiveChange: function (/* oEvent */) {
1334
+ // ShellBarSearch fires an input event without a value parameter — read value from DOM directly.
1335
+ var sQuery = this._getSearchValue();
1336
+ this.getModel("searchData").setProperty("/query", sQuery);
1337
+
1338
+ clearTimeout(this._iSearchDebounceTimer);
1495
1339
 
1340
+ if (!sQuery) {
1341
+ this._setSearchMode(false);
1496
1342
  return;
1497
1343
  }
1498
1344
 
1499
- this.openSearchPicker();
1345
+ // Show the popup loading indicator during the debounce + async lookup window,
1346
+ // so the suggest panel shows a spinner rather than a transient "No data" state.
1347
+ this._setSearchLoading(true);
1348
+ this._iSearchDebounceTimer = setTimeout(this._executeSearch.bind(this, sQuery), 200);
1349
+ },
1500
1350
 
1501
- if (this.highlighter) {
1502
- this.highlighter.highlight(sQuery);
1503
- }
1351
+ _executeSearch: function (sQuery) {
1352
+ var oSearchModel = this.getModel("searchData");
1353
+ this._sLastSearchQuery = sQuery;
1504
1354
 
1505
- oModel.setProperty("/query", sQuery);
1506
1355
  SearchUtil.search(sQuery, {
1507
- preferencedCategory: sPreferencedCategory,
1508
- includeDeprecated: bIncludeDeprecated
1356
+ preferencedCategory: oSearchModel.getProperty("/preferencedCategory"),
1357
+ includeDeprecated: oSearchModel.getProperty("/includeDeprecated")
1509
1358
  }).then(function (result) {
1510
- oModel.setProperty("/matches", result.matches);
1511
- });
1359
+
1360
+ if (sQuery !== this._sLastSearchQuery) { return; }
1361
+
1362
+ this._setSearchLoading(false);
1363
+
1364
+ if (!result) {
1365
+ if (this._getSearchControl()) { SearchRenderer.clear(this._getSearchControl()); }
1366
+ oSearchModel.setProperty("/messageAreaText", "");
1367
+ return;
1368
+ }
1369
+
1370
+ var oMatches = result.matches,
1371
+ aTop10 = (oMatches.filteredData || []).slice(0, 10),
1372
+ oCounts = {
1373
+ AllLength: oMatches.AllLength || 0,
1374
+ APILength: (oMatches.aDataAPI || []).length,
1375
+ DocLength: (oMatches.aDataDoc || []).length,
1376
+ SamplesLength: (oMatches.aDataExplored || []).length,
1377
+ ExternalLength:(oMatches.aDataExternal || []).length
1378
+ };
1379
+
1380
+ oSearchModel.setProperty("/matches", {
1381
+ filteredData: aTop10,
1382
+ data: oMatches.data || [],
1383
+ aDataAPI: oMatches.aDataAPI || [],
1384
+ aDataDoc: oMatches.aDataDoc || [],
1385
+ aDataExplored: oMatches.aDataExplored || [],
1386
+ aDataExternal: oMatches.aDataExternal || [],
1387
+ AllLength: oCounts.AllLength,
1388
+ APILength: oCounts.APILength,
1389
+ DocLength: oCounts.DocLength,
1390
+ ExploredLength:oCounts.SamplesLength,
1391
+ SamplesLength: oCounts.SamplesLength,
1392
+ ExternalLength:oCounts.ExternalLength
1393
+ });
1394
+
1395
+ this._renderSearchResults(aTop10, oCounts);
1396
+ }.bind(this)).catch(function () {
1397
+ if (sQuery === this._sLastSearchQuery) {
1398
+ this._setSearchLoading(false);
1399
+ }
1400
+ }.bind(this));
1401
+ },
1402
+
1403
+ /**
1404
+ * Restores the input value after ShellBarSearch overwrites it on item selection.
1405
+ * The component sets its own value after the search event fires (post-click microtask),
1406
+ * so the restore must be deferred past that with setTimeout(0).
1407
+ *
1408
+ *
1409
+ * @param {string} sQuery The value to restore into the search field
1410
+ * @private
1411
+ */
1412
+ _restoreSearchValue: function (sQuery) {
1413
+ setTimeout(function () {
1414
+ var oCtrl = this._getSearchControl();
1415
+ if (oCtrl) {
1416
+ oCtrl.setValue(sQuery);
1417
+ }
1418
+ }.bind(this), 0);
1419
+ },
1420
+
1421
+ /**
1422
+ * Returns the cached searchControl instance, resolving it once via byId.
1423
+ *
1424
+ * @returns {sap.ui.core.Control|undefined} The search control, or undefined if not yet rendered
1425
+ * @private
1426
+ */
1427
+ _getSearchControl: function () {
1428
+ if (!this._oSearchControl) {
1429
+ this._oSearchControl = this.byId("searchControl");
1430
+ if (this._oSearchControl) {
1431
+ this._oSearchControl.addEventDelegate({
1432
+ onfocusin: this._onSuggestionItemFocusIn.bind(this)
1433
+ });
1434
+ }
1435
+ }
1436
+ return this._oSearchControl;
1437
+ },
1438
+
1439
+ /**
1440
+ * Keeps the actual user query in the search field while arrow-navigating the
1441
+ * suggestion list.
1442
+ *
1443
+ * On item focus the ShellBarSearch web component copies the focused item's
1444
+ * text into the field value. That is meaningful for real result items, but
1445
+ * the "All" and per-category entries are navigation shortcuts rather than
1446
+ * search suggestions, so their labels (e.g. "All (1028)") must not appear in
1447
+ * the field. When such an item is focused, the user's query is restored.
1448
+ *
1449
+ * @param {jQuery.Event} oEvent The focusin event
1450
+ * @private
1451
+ */
1452
+ _onSuggestionItemFocusIn: function (oEvent) {
1453
+ var oNode = oEvent.target;
1454
+ if (!oNode || !oNode.id) { return; }
1455
+
1456
+ var oItem = Element.getElementById(oNode.id),
1457
+ oPayload = oItem && typeof oItem.data === "function" ? oItem.data("_payload") : null;
1458
+
1459
+ // Only result items may replace the field value with their own text;
1460
+ // "allResults", "category" and "noData" entries keep the user's query.
1461
+ if (!oPayload || oPayload.type === "result") { return; }
1462
+
1463
+ this._getSearchControl().setValue(this.getModel("searchData").getProperty("/query") || "");
1464
+ },
1465
+
1466
+ /**
1467
+ * Reads the current input value from the ShellBarSearch control.
1468
+ * ShellBarSearch fires input events without a value parameter, so the
1469
+ * value is read from the control property instead.
1470
+ *
1471
+ * @returns {string} The current search field value (empty string if unavailable)
1472
+ * @private
1473
+ */
1474
+ _getSearchValue: function () {
1475
+ return this._getSearchControl() ? this._getSearchControl().getValue().trim() : "";
1476
+ },
1477
+
1478
+ /**
1479
+ * Toggles the ShellBarSearch popup loading indicator, shown while a search
1480
+ * is pending (debounce + async lookup) so the suggest panel displays a
1481
+ * spinner instead of an empty "No data" state.
1482
+ *
1483
+ * @param {boolean} bLoading true to show the loading indicator, false to hide it
1484
+ * @private
1485
+ */
1486
+ _setSearchLoading: function (bLoading) {
1487
+ if (this._getSearchControl()) {
1488
+ this._getSearchControl().setLoading(!!bLoading);
1489
+ }
1490
+ },
1491
+
1492
+ _renderSearchResults: function (aItems, oCounts) {
1493
+ if (!this._getSearchControl()) { return; }
1494
+
1495
+ var bPhone = this.getModel("appView").getProperty("/bPhoneSize"),
1496
+ oSearchModel = this.getModel("searchData");
1497
+
1498
+ SearchRenderer.render(this._getSearchControl(), aItems, oCounts, this._oI18nBundle, bPhone, oSearchModel);
1512
1499
  },
1513
1500
 
1514
1501
  /**
@@ -1535,10 +1522,6 @@ sap.ui.define([
1535
1522
 
1536
1523
  if (bDesktopSize !== oViewModel.getProperty("/bDesktopSize")) {
1537
1524
  oViewModel.setProperty("/bDesktopSize", bDesktopSize);
1538
-
1539
- setTimeout(function () {
1540
- this._updateSearchFieldState();
1541
- }.bind(this), 0);
1542
1525
  }
1543
1526
  },
1544
1527
 
@@ -1550,21 +1533,74 @@ sap.ui.define([
1550
1533
  this.getModel("appView").setProperty("/bLandscape", Device.orientation.landscape);
1551
1534
  },
1552
1535
 
1553
- onToggleSearchMode: function (oEvent) {
1554
- var bSearchMode = oEvent.getParameter("isOpen"),
1555
- oViewModel = this.getModel("appView");
1536
+ /**
1537
+ * Handles the ShellBar search field expanding or collapsing on phone.
1538
+ * When it expands with an empty field, proactively open the suggestion
1539
+ * popup with the category empty-state so it is not blank.
1540
+ *
1541
+ * @param {sap.ui.base.Event} oEvent
1542
+ */
1543
+ onSearchFieldToggle: function (oEvent) {
1544
+ var bOpen = oEvent.getParameter("expanded");
1545
+ if (!bOpen) { return; }
1546
+ var bPhone = this.getModel("appView").getProperty("/bPhoneSize");
1547
+ if (!bPhone) { return; }
1548
+ if (!this._getSearchControl()) { return; }
1549
+ this._setSearchMode(true);
1550
+ this._getSearchControl().setOpen(true);
1551
+ },
1556
1552
 
1557
- // This flag is used to hide action buttons when search is opened
1558
- oViewModel.setProperty("/bSearchMode", bSearchMode);
1553
+ /**
1554
+ * Handles the closing of the search suggestion popup.
1555
+ *
1556
+ * @returns {void}
1557
+ */
1558
+ onSearchClose: function () {
1559
+ this._setSearchMode(false);
1560
+ },
1559
1561
 
1560
- if (bSearchMode) {
1561
- setTimeout(function () {
1562
- if (Device.system.desktop) {
1563
- this._oView.byId("searchControl").getAggregation("_searchField").getFocusDomRef().focus();
1564
- } else {
1565
- this.openSearchPicker();
1566
- }
1567
- }.bind(this), 0);
1562
+ /**
1563
+ * Populates or resets the suggestion popup for the current search state.
1564
+ *
1565
+ * @param {boolean} bOpen Whether the search popup is open
1566
+ * @private
1567
+ */
1568
+ _setSearchMode: function (bOpen) {
1569
+ var oViewModel = this.getModel("appView");
1570
+ if (!bOpen) {
1571
+ if (this._getSearchControl()) { SearchRenderer.clear(this._getSearchControl()); }
1572
+ this._setSearchLoading(false);
1573
+ return;
1574
+ }
1575
+
1576
+ var bPhone = oViewModel.getProperty("/bPhoneSize");
1577
+ if (bPhone) {
1578
+ var oSearchModel = this.getModel("searchData"),
1579
+ sQuery = oSearchModel.getProperty("/query"),
1580
+ sCurrentValue = this._getSearchValue(),
1581
+ oMatches = oSearchModel.getProperty("/matches");
1582
+ if (sCurrentValue && sCurrentValue !== sQuery) {
1583
+ oSearchModel.setProperty("/query", sCurrentValue);
1584
+ this._setSearchLoading(true);
1585
+ this._executeSearch(sCurrentValue);
1586
+ return;
1587
+ }
1588
+ if (sQuery && oMatches) {
1589
+ var aTop10 = oMatches.filteredData || [],
1590
+ oCounts = {
1591
+ AllLength: oMatches.AllLength || 0,
1592
+ APILength: oMatches.APILength || 0,
1593
+ DocLength: oMatches.DocLength || 0,
1594
+ SamplesLength: oMatches.SamplesLength || 0,
1595
+ ExternalLength:oMatches.ExternalLength || 0
1596
+ };
1597
+ SearchRenderer.render(this._getSearchControl(), aTop10, oCounts, this._oI18nBundle, bPhone, oSearchModel);
1598
+ return;
1599
+ }
1600
+ }
1601
+
1602
+ if (this._getSearchControl()) {
1603
+ bPhone ? SearchRenderer.renderEmptyState(this._getSearchControl(), this._oI18nBundle) : SearchRenderer.clear(this._getSearchControl());
1568
1604
  }
1569
1605
  },
1570
1606
 
@@ -1773,26 +1809,6 @@ sap.ui.define([
1773
1809
  this._sKey = sKey;
1774
1810
  },
1775
1811
 
1776
- /**
1777
- * Updates the state of the search field.
1778
- * Expand/collapse the search field depending on the screen size.
1779
- * Hide/show header button actions if the search field is expanded/collapsed.
1780
- *
1781
- * @returns {void}
1782
- */
1783
- _updateSearchFieldState: function () {
1784
- var oViewModel = this.getModel("appView"),
1785
- bDesktopSize = oViewModel.getProperty("/bDesktopSize"),
1786
- oClosingButton = this.byId("searchControl").getAggregation("_closingButton");
1787
-
1788
- if (bDesktopSize) {
1789
- oViewModel.setProperty("/bSearchMode", false);
1790
- oClosingButton.setVisible(false);
1791
- } else {
1792
- oClosingButton.setVisible(true);
1793
- }
1794
- },
1795
-
1796
1812
  /**
1797
1813
  * Sets the selected section title based on the provided key.
1798
1814
  *
@@ -1814,32 +1830,6 @@ sap.ui.define([
1814
1830
 
1815
1831
  var selectedItem = items.find(function (item) { return item.key === sKey; });
1816
1832
  oViewModel.setProperty("/selectedSectionTitle", selectedItem.text);
1817
- },
1818
-
1819
- /**
1820
- * Tweak the appearance of the version switch button.
1821
- * This function adds a CSS class to the button's text content DOM element
1822
- * to customize its styling after rendering.
1823
- *
1824
- * @returns {void} This function does not return a value.
1825
- */
1826
- _adjustVersionSwitchButton: function () {
1827
- var oVersionSwitchButton = this.byId("versionSwitchButton");
1828
-
1829
- if (oVersionSwitchButton) {
1830
- oVersionSwitchButton.addEventDelegate({
1831
- onAfterRendering: function () {
1832
- var oVersionSwitchButtonDomRef = oVersionSwitchButton.getDomRef(),
1833
- oVersionSwitchButtonTextContentDomRef = oVersionSwitchButtonDomRef.querySelector(".sapMBtnContent");
1834
-
1835
- if (oVersionSwitchButtonTextContentDomRef) {
1836
- if (!oVersionSwitchButtonTextContentDomRef.classList.contains("sapUiDemoKitHeaderActionsVersionSwitchTextContent")) {
1837
- oVersionSwitchButtonTextContentDomRef.classList.add("sapUiDemoKitHeaderActionsVersionSwitchTextContent");
1838
- }
1839
- }
1840
- }
1841
- });
1842
- }
1843
1833
  }
1844
1834
  });
1845
1835
  });