Menubar - Add "Find menu item..." search feature
authorColeman Watts <coleman@civicrm.org>
Wed, 19 Feb 2020 21:45:39 +0000 (16:45 -0500)
committerColeman Watts <coleman@civicrm.org>
Fri, 21 Feb 2020 23:50:34 +0000 (18:50 -0500)
Allows user to locate menu items by typing a few letters.
Find tool is located under the "Home" (Civi logo) menu.

css/crm-menubar.css
extension-compatibility.json
js/crm.menubar.js
templates/CRM/common/accesskeys.hlp

index 5b584524f4676dc20ffd93394ceee3f219d29662..63f421f12183f6c77ec19bf111e40b20f0fb8d83 100644 (file)
@@ -183,6 +183,22 @@ body.crm-menubar-over-cms-menu #crm-menubar-toggle-position a i {
   transform: rotate(180deg);
 }
 
+/* Drilldown menu item finder */
+#civicrm-menu [data-name=MenubarDrillDown] > a {
+  padding-top: 2px;
+  padding-bottom: 2px;
+}
+#crm-menubar-drilldown {
+  padding: 4px;
+  background-color: #eee;
+}
+#crm-menubar-drilldown:focus {
+  background-color: white;
+}
+#crm-menubar-drilldown + .sub-arrow:before {
+  margin-top: 5px;
+}
+
 @media (min-width: $breakMin) {
 
   /* Switch to desktop layout
index b7b4031ae3cf7df6b7791bae2d0f271a72034208..2424792ee3ee8bb083bc16d926bea830d284b4a6 100644 (file)
@@ -1,4 +1,9 @@
 {
+  "com.civibridge.quickmenu": {
+    "obsolete": "5.24",
+    "disable": true,
+    "uninstall": true
+  },
   "org.civicrm.exportui": {
     "obsolete": "5.23",
     "disable": true,
index 9fa49f63d69a5fc0d23f4bdf21f3c9ea74eec42f..eba37ed3c46d6c90b6281b3be088900ce874018a 100644 (file)
             })
             .on('show.smapi', function(e, menu) {
               // Focus menu when opened with an accesskey
-              $(menu).siblings('a[accesskey]').focus();
+              if ($(menu).parent().data('name') === 'Home') {
+                $('#crm-menubar-drilldown').focus();
+              } else {
+                $(menu).siblings('a[accesskey]').focus();
+              }
             })
             .smartmenus(CRM.menubar.settings);
           initialized = true;
           CRM.menubar.initializeResponsive();
           CRM.menubar.initializeSearch();
+          CRM.menubar.initializeDrill();
         });
       }
     },
     getItem: function(itemName) {
       return traverse(CRM.menubar.data.menu, itemName, 'get');
     },
+    findItems: function(searchTerm) {
+      return findRecursive(CRM.menubar.data.menu, searchTerm.toLowerCase().replace(/ /g, ''));
+    },
     addItems: function(position, targetName, items) {
       var list, container, $ul;
       if (position === 'before' || position === 'after') {
         return !$('ul.crm-quickSearch-results').is(':visible:not(.ui-state-disabled)');
       });
     },
+    initializeDrill: function() {
+      $('#civicrm-menu').on('keyup', '#crm-menubar-drilldown', function() {
+        var term = $(this).val(),
+          results = term ? CRM.menubar.findItems(term).slice(0, 20) : [];
+        $(this).parent().next('ul').html(getTpl('branch')({items: results, branchTpl: getTpl('branch'), drillTpl: _.noop}));
+        $('#civicrm-menu').smartmenus('refresh').smartmenus('itemActivate', $(this).closest('a'));
+      });
+    },
     treeTpl:
       '<nav id="civicrm-menu-nav">' +
         '<input id="crm-menubar-state" type="checkbox" />' +
           '<% }) %>' +
         '</ul>' +
       '</li>',
+    drillTpl:
+      '<li class="crm-menu-border-bottom" data-name="MenubarDrillDown">' +
+        '<a href="#"><input type="text" id="crm-menubar-drilldown" placeholder="' + _.escape(ts('Find menu item...')) + '"></a>' +
+        '<ul></ul>' +
+      '</li>',
     branchTpl:
       '<% _.forEach(items, function(item) { %>' +
         '<li <%= attr("li", item) %>>' +
             '<% } %>' +
           '</a>' +
           '<% if (item.child) { %>' +
-            '<ul><%= branchTpl({items: item.child, branchTpl: branchTpl}) %></ul>' +
+            '<ul>' +
+              '<% if (item.name === "Home") { %><%= drillTpl() %><% } %>' +
+              '<%= branchTpl({items: item.child, branchTpl: branchTpl}) %>' +
+            '</ul>' +
           '<% } %>' +
         '</li>' +
       '<% }) %>'
   function getTpl(name) {
     if (!templates) {
       templates = {
-        branch: _.template(CRM.menubar.branchTpl, {imports: {_: _, attr: attr}}),
+        drill: _.template(CRM.menubar.drillTpl, {}),
         search: _.template(CRM.menubar.searchTpl, {imports: {_: _, ts: ts, CRM: CRM}})
       };
+      templates.branch = _.template(CRM.menubar.branchTpl, {imports: {_: _, attr: attr, drillTpl: templates.drill}});
       templates.tree = _.template(CRM.menubar.treeTpl, {imports: {branchTpl: templates.branch, searchTpl: templates.search, ts: ts}});
     }
     return templates[name];
     return found;
   }
 
+  function findRecursive(collection, searchTerm) {
+    var items = _.filter(collection, function(item) {
+      return item.label && _.includes(item.label.toLowerCase().replace(/ /g, ''), searchTerm);
+    });
+    _.each(collection, function(item) {
+      if (_.isPlainObject(item) && item.child) {
+        var childMatches = findRecursive(item.child, searchTerm);
+        if (childMatches.length) {
+          Array.prototype.push.apply(items, childMatches);
+        }
+      }
+    });
+    return items;
+  }
+
   function attr(el, item) {
     var ret = [], attr = _.cloneDeep(item.attr || {}), a = ['rel', 'accesskey', 'target'];
     if (el === 'a') {
index 6100246650a59c920f738a811930544674637197..240fd96217981a881bb2927fceeac71aac158244 100644 (file)
@@ -23,7 +23,7 @@
     <li>{$accessKey}+<span>E</span> - {ts}Edit Contact (View Contact Summary Page){/ts}</li>
     <li>{$accessKey}+<span>S</span> - {ts}Save Button{/ts}</li>
     <li>{$accessKey}+<span>N</span> - {ts}Add a new record in each tab (Activities, Tags,..etc){/ts}</li>
-    <li>{$accessKey}+<span>M</span> - {ts}Open the CiviCRM menubar{/ts}</li>
+    <li>{$accessKey}+<span>M</span> - {ts}Find menu item...{/ts}</li>
     <li>{$accessKey}+<span>Q</span> - {ts}Quicksearch{/ts}</li>
   </ul>
   {literal}<style type="text/css">