diff --git a/core/modules/toolbar/css/toolbar.base.css b/core/modules/toolbar/css/toolbar.base.css index 33fb0d5..4f501a7 100644 --- a/core/modules/toolbar/css/toolbar.base.css +++ b/core/modules/toolbar/css/toolbar.base.css @@ -24,7 +24,7 @@ body.toolbar { text-align: left; /* LTR */ -webkit-text-size-adjust: none; -webkit-touch-callout: none; - -webkit-user-select: none; + /*-webkit-user-select: none;*/ vertical-align: baseline; } #toolbar { diff --git a/core/modules/toolbar/css/toolbar.theme.css b/core/modules/toolbar/css/toolbar.theme.css index d354dfe..0ee2ad9 100644 --- a/core/modules/toolbar/css/toolbar.theme.css +++ b/core/modules/toolbar/css/toolbar.theme.css @@ -307,7 +307,7 @@ line-height: 1; } #toolbar .dropbutton .dropbutton-link { - border-left: 0; + border-left: 0; } #toolbar .dropbutton .dropbutton-arrow { border-bottom-width: 0; @@ -323,3 +323,66 @@ border-right-width: 0.55em; border-top-width: 0.05em; } + +/* Auto-complete */ +.toolbar-tray input[type="search"], +.toolbar-tray input[type="search"]::-webkit-search-decoration, +.toolbar-tray input[type="search"]::-webkit-search-cancel-button, +.toolbar-tray input[type="search"]::-webkit-search-results-button, +.toolbar-tray input[type="search"]::-webkit-search-results-decoration { + -khtml-appearance:none; + -moz-appearance:none; + -o-appearance:none; + -webkit-appearance:none; + appearance:none; +} + +#toolbar .toolbar-tray input { + border: none; + height: 25px; + background-image:url('../images/maginifier.png'); + background-repeat:no-repeat; + background-position:95% 50%; +} + +#toolbar .toolbar-tray .form-type-search { + border: 1px solid #ddd; + -moz-border-radius:13px; + -webkit-border-radius:13px; + border-radius:13px ; + line-height:25px; + padding-left: 10px; + margin: 0 15px; +} + +#toolbar .toolbar-tray .form-type-search.display-results {} +#toolbar .toolbar-tray .form-type-search.display-results ul { + padding: 0; + margin-top: 0; + margin-bottom: 10px; +} + +#toolbar .toolbar-tray .form-type-search.display-results li { + color: #555; + font-weight: normal; + padding: 0; + margin:0; + list-style:none; +} + +#toolbar .toolbar-tray .form-type-search.display-results li a { + color: inherit; + text-decoration: none; +} + +#toolbar .toolbar-tray .form-type-search.display-results li:hover { + padding-left: 5px; + background-color: #4584DD; + background-image:-moz-linear-gradient(50% 0% -90deg,rgb(69,132,221) 0%,rgb(56,111,186) 100%); + background-image:-webkit-gradient(linear,50% 0%,50% 100%,color-stop(0, rgb(69,132,221)),color-stop(1, rgb(56,111,186))); + background-image:-webkit-linear-gradient(-90deg,rgb(69,132,221) 0%,rgb(56,111,186) 100%); + background-image:linear-gradient(-90deg,rgb(69,132,221) 0%,rgb(56,111,186) 100%); + color: #fff; + width: 90%; + font-weight: bold; +} diff --git a/core/modules/toolbar/js/toolbar.js b/core/modules/toolbar/js/toolbar.js index 94afb90..730b477 100644 --- a/core/modules/toolbar/js/toolbar.js +++ b/core/modules/toolbar/js/toolbar.js @@ -16,7 +16,9 @@ Drupal.behaviors.toolbar = { if ($toolbar.length) { var $bar = $toolbar.find('.toolbar-bar'); var $tray = $toolbar.find('.toolbar-tray'); + var $menu = $tray.find('.toolbar-menu > .menu'); var $trigger = $toolbar.find('.toggle-tray'); + var $filter = $tray.find('.form-type-search input'); // Instanstiate the bar. if ($bar.length) { ToolBar.bar = new ToolBar($bar); @@ -25,18 +27,21 @@ Drupal.behaviors.toolbar = { if ($tray.length && $trigger.length) { ToolBar.tray = new TraySlider($tray, $trigger); } + if ($filter.length) { + ToolBar.filter = new ToolbarFilter($filter, $menu); + } } } }; /** - * Store references to the ToolBar and TraySlider objects in the ToolBar object. + * Store references to the ToolBar components. * - * These references will be available in Drupal.ToolBar.bar and - * Drupal.ToolBar.tray. + * These references will be available in Drupal.ToolBar. */ $.extend(ToolBar, { bar: null, - tray: null + tray: null, + filter: null }); /** * A toolbar is an administration action button container. @@ -119,7 +124,7 @@ function TraySlider ($tray, $trigger) { // Offset value vas changed by a third party script. 'offsettopchange.toolbar': $.proxy(this, 'displace') }); -}; +} /** * Extend the prototype of the TraySlider class. */ @@ -318,6 +323,121 @@ $.extend(TraySlider.prototype, { } }); +/** + * Apply the search bar functionality. + */ +function ToolbarFilter($filter, $menu) { + this.$filter = $filter; + // Target filter container + this.$filterContainer = $('#toolbar .form-type-search'); + this.$menu = $menu; + // Target toolbar + this.$toolbar = $filter.closest('#toolbar'); + // Initialize the current search needle. + this.needle = this.$filter.val(); + // Cache of all links that can be matched in the menu. + this.$links; + // Minimum search needle length. + this.needleMinLength = 2; + // Attach the search input event handler. + this.$filter.on('keyup.toolbarfilter search.toolbarfilter', $.proxy(this, 'keyupHandler')); + // Append the results container. + this.$results = $('
') + .insertAfter(this.$filter) + // Hide the result list after a link has been clicked, useful for overlay. + .on('click.toolbarfilter', 'li', $.proxy(this, 'resultsClickHandler')); +} + + +/** + * Extend the prototype of the ToolbarFilter class. + */ +$.extend(ToolbarFilter.prototype, { + /** + * Executes the search upon user input. + */ + keyupHandler: function (event) { + var value = this.$filter.val(); + var matches, $html; + // Only proceed if the search needle has changed. + if (value !== this.needle) { + this.needle = value; + // Initialize the cache of menu links upon first search. + if (!this.$links && this.needle.length >= this.needleMinLength) { + this.$links = this.buildSearchIndex(this.$menu.find('li a')); + } + // Empty results container when deleting search text. + if (this.needle.length < this.needleMinLength) { + this.$results.empty(); + this.$$filterContainer.removeClass('display-results'); + } + // Only search if the needle is long enough. + if (this.needle.length >= this.needleMinLength && this.$links) { + matches = this.findMatches(this.needle, this.$links); + // Build the list in a detached DOM node. + $html = this.buildResultsList(matches); + // Display results. + this.$results.empty().append($html); + this.$filterContainer.addClass('display-results'); + } + } + }, + /** + * Builds the search index. + */ + buildSearchIndex: function ($links) { + return $links + .map(function () { + var text = (this.textContent || this.innerText); + // Skip menu entries that do not contain any text (e.g., the icon). + if (typeof text === 'undefined') { + return; + } + return { + text: text, + textMatch: text.toLowerCase(), + element: this + }; + }); + }, + /** + * Searches the index for a given needle and returns matching entries. + */ + findMatches: function (needle, links) { + var needleMatch = needle.toLowerCase(); + // Select matching links from the cache. + return $.grep(links, function (link) { + return link.textMatch.indexOf(needleMatch) !== -1; + }); + }, + /** + * Builds the search result list in a detached DOM node. + */ + buildResultsList: function (matches) { + var $html = $('