Index: univention-management-console/www/management/main.js =================================================================== diff --git a/branches/ucs-4.2/ucs-4.2-1/management/univention-management-console/www/management/main.js b/branches/ucs-4.2/ucs-4.2-1/management/univention-management-console/www/management/main.js --- a/branches/ucs-4.2/ucs-4.2-1/management/univention-management-console/www/management/main.js (Revision 81242) +++ b/branches/ucs-4.2/ucs-4.2-1/management/univention-management-console/www/management/main.js (Arbeitskopie) @@ -46,6 +46,7 @@ "dojo/promise/all", "dojo/cookie", "dojo/topic", + "dojo/query", "dojo/io-query", "dojo/store/Memory", "dojo/store/Observable", @@ -85,7 +86,7 @@ "umc/i18n!management", "dojo/sniff" // has("ie"), has("ff") ], function(declare, lang, kernel, array, baseWin, win, on, mouse, touch, tap, aspect, has, - Evented, Deferred, all, cookie, topic, ioQuery, Memory, Observable, + Evented, Deferred, all, cookie, topic, domQuery, ioQuery, Memory, Observable, dom, domAttr, domClass, domGeometry, domConstruct, put, hash, styles, entities, gfx, registry, tools, login, dialog, store, _WidgetBase, Menu, MenuItem, PopupMenuItem, MenuSeparator, Tooltip, DropDownButton, StackContainer, menu, MenuButton, TabController, LiveSearch, GalleryPane, ContainerWidget, Page, Form, Button, Text, _ @@ -1327,10 +1328,61 @@ if (!this._header._search) { return; } + + on(baseWin.doc, 'keyup', lang.hitch(this, function(keyEvent) { + // focus search if Ctr+Enter was pressed + + keyEvent = (keyEvent) ? keyEvent : window.event; + var keyCode = keyEvent.which || keyEvent.keyCode; + // keyCode 13 is return key + if (keyCode === 13 && keyEvent.ctrlKey) { + this._header._search.focus(); + } + })); + this._header._search.on('search', lang.hitch(this, function() { this.switchToOverview(); this._updateQuery(null); })); + + this._header._search.on('tab', lang.hitch(this, function(shiftPressed) { + // move the focus to the next or previous search entry + + var focusedModule = domQuery('.umcGalleryItemKeyboardFocus', this._grid.contentNode)[0]; + if (!focusedModule) { + // the search did not yield searchresults + return; + } + + var newFocusedModule; + if (shiftPressed) { + // focus the previous search entry or the last entry if at the beginning + newFocusedModule = focusedModule.previousSibling ? focusedModule.previousSibling : domQuery('.umcGalleryWrapperItem', this._grid.contentNode).pop(); + } else { + // focus the next search entry or the first entry if at the end + newFocusedModule = focusedModule.nextSibling ? focusedModule.nextSibling : domQuery('.umcGalleryWrapperItem', this._grid.contentNode).shift(); + } + + // remove focus from old module and focus new module + domClass.add(newFocusedModule, 'umcGalleryItemKeyboardFocus umcGalleryItemNoTransition umcGalleryItemActive'); + domClass.remove(focusedModule, 'umcGalleryItemKeyboardFocus umcGalleryItemNoTransition umcGalleryItemActive'); + })); + + this._header._search.on('enterKey', lang.hitch(this, function(searchString) { + // open the focused module + + // return if we have not searched anything or we are not in the overview + if (!searchString || !domClass.contains(baseWin.body(), 'umcOverviewShown')) { + return; + } + + var modules = this._grid.store.query(this._grid.query, this._grid.queryOptions); + // Only open if there are modules for the search + if (modules.length) { + var index = domQuery('.umcGalleryItemKeyboardFocus', this._grid.contentNode)[0].rowIndex; + this.openModule(modules[index]); + } + })); }, _lastSearchPattern: null, @@ -1370,6 +1422,14 @@ this._grid.updateQuery(searchPattern, searchQuery, category); + // focus the first search entry that can be opened with the keyboard + if (!category) { + var modules = domQuery('.umcGalleryWrapperItem', this._grid.contentNode); + if (modules.length) { + domClass.add(modules[0], 'umcGalleryItemKeyboardFocus umcGalleryItemNoTransition umcGalleryItemActive'); + } + } + // update the search label domClass.toggle(this._searchText.domNode, 'dijitDisplayNone', !!category); this._searchText.set('content', _('Search query ›%s‹', entities.encode(searchPattern))); @@ -1489,7 +1549,7 @@ styles.insertCssRule(lang.replace('{0}.umcModuleTab-{1}.dijitTabChecked', [defaultClasses, module_flavor_css]), cssProperties); // color action buttons in an dgrid styles.insertCssRule(lang.replace('.umcModule.color-{0} .umcGridHeader .dijitButtonText', [tab.categoryColor]), lang.replace('color: {0}', [color])); - // color scroll to top floating button + // color scroll to top floating button styles.insertCssRule(lang.replace('.umcModule.color-{0} .scrollToTopFloatingButton', [tab.categoryColor]), lang.replace('background-color: {0}', [color])); }, Index: univention-web/css/site/gallerypane.styl =================================================================== diff --git a/branches/ucs-4.2/ucs-4.2-1/management/univention-web/css/site/gallerypane.styl b/branches/ucs-4.2/ucs-4.2-1/management/univention-web/css/site/gallerypane.styl --- a/branches/ucs-4.2/ucs-4.2-1/management/univention-web/css/site/gallerypane.styl (Revision 81242) +++ b/branches/ucs-4.2/ucs-4.2-1/management/univention-web/css/site/gallerypane.styl (Arbeitskopie) @@ -133,6 +133,11 @@ background-position: -20px -40px opacity: 0.67 + .umcGalleryItemNoTransition .umcGalleryItem, + .umcGalleryItemNoTransition .umcGalleryItem .umcGalleryName, + .umcGalleryItemNoTransition .umcGalleryItem .umcGalleryDescription + transition: none + body:not(.umcTouchDevices) .umcGalleryPane .umcGalleryItem:hover cursor: pointer .umcGalleryCategoryFavorite Index: univention-web/js/widgets/LiveSearch.js =================================================================== diff --git a/branches/ucs-4.2/ucs-4.2-1/management/univention-web/js/widgets/LiveSearch.js b/branches/ucs-4.2/ucs-4.2-1/management/univention-web/js/widgets/LiveSearch.js --- a/branches/ucs-4.2/ucs-4.2-1/management/univention-web/js/widgets/LiveSearch.js (Revision 81242) +++ b/branches/ucs-4.2/ucs-4.2-1/management/univention-web/js/widgets/LiveSearch.js (Arbeitskopie) @@ -63,7 +63,8 @@ this.inherited(arguments); this._searchTextBox = new SearchBox({ - inlineLabel: this.searchLabel || _('Search term') + inlineLabel: this.searchLabel || _('Search term'), + intermediateChanges: true }); this.addChild(this._searchTextBox); }, @@ -70,7 +71,9 @@ postCreate: function() { this.inherited(arguments); - this._searchTextBox.on('keyup', lang.hitch(this, 'search')); + this._searchTextBox.on('keydown', lang.hitch(this, 'processKeydown')); + this._searchTextBox.on('keyup', lang.hitch(this, 'processKeyup')); + this._searchTextBox.on('change', lang.hitch(this, 'onSearch')); this._searchTextBox.on('focus', lang.hitch(this, 'onFocus')); this._searchTextBox.on('blur', lang.hitch(this, 'onBlur')); if (this.collapsible) { @@ -115,21 +118,49 @@ // event stub }, - _lastValue: null, - search: function() { - // ignore empty search expect something was searched before - // (e.g. deleting the last letter should make a new search so that everything is shown) - var searchPattern = this.get('value'); - if (searchPattern || this._lastValue) { - this._lastValue = searchPattern; - this.onSearch(); + processKeydown: function(keyEvent) { + keyEvent = (keyEvent) ? keyEvent : window.event; + var keyCode = keyEvent.which || keyEvent.keyCode; + + // if the searchbox has a value and tab is pressed + // fire an onTab event and prevent default of tab + if (keyCode === 9 && this.get('value')) { + keyEvent.preventDefault(); + this.onTab(keyEvent.shiftKey); } }, + processKeyup: function(keyEvent) { + keyEvent = (keyEvent) ? keyEvent : window.event; + var keyCode = keyEvent.which || keyEvent.keyCode; + + // if the enter key is pressed send enter pressed event + if (keyCode === 13) { + this.onEnterKey(this.get('value')); + return; + } + + // if the escape key is pressed clear the search + if (keyCode === 27) { + this.set('value', ''); + return; + } + }, + onSearch: function() { // event stub }, + onEnterKey: function(searchString) { + // event stub + }, + + onTab: function(shiftPressed) { + // event stub + // fires when the tab key is pressed + // does not fire on empty search strings + }, + getSearchQuery: function(searchPattern) { // sanitize the search pattern searchPattern = regexp.escapeString(searchPattern);