mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-10 00:42:41 +01:00
Fix blur and sort behavior for autocomplete
Summary: Ref T10163. - If you click a result, we get a blur before your click hits, and deactivate before the click can work. Instead, wait before responding to blur. - Use the standard sort handler which puts unixnames over human names. Also use the standard filter which deals with disabled users not matching unless they're the only match. Test Plan: - Clicked a result, got a replacement. - Named myself "dog dog", typed "@dog", user "@dog" was now first match despite me being "@admin". - Used normal typeaheads to make sure I didn't break sort handler. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10163 Differential Revision: https://secure.phabricator.com/D15031
This commit is contained in:
parent
77447fc945
commit
3c19004f9f
3 changed files with 110 additions and 100 deletions
|
@ -8,7 +8,7 @@
|
|||
return array(
|
||||
'names' => array(
|
||||
'core.pkg.css' => '3a97c8b9',
|
||||
'core.pkg.js' => '1f5f365a',
|
||||
'core.pkg.js' => '5813273d',
|
||||
'darkconsole.pkg.js' => 'e7393ebb',
|
||||
'differential.pkg.css' => '2de124c9',
|
||||
'differential.pkg.js' => 'f83532f8',
|
||||
|
@ -458,7 +458,7 @@ return array(
|
|||
'rsrc/js/core/KeyboardShortcutManager.js' => 'c1700f6f',
|
||||
'rsrc/js/core/MultirowRowManager.js' => 'b5d57730',
|
||||
'rsrc/js/core/Notification.js' => 'ccf1cbf8',
|
||||
'rsrc/js/core/Prefab.js' => 'a15cbd65',
|
||||
'rsrc/js/core/Prefab.js' => 'e67df814',
|
||||
'rsrc/js/core/ShapedRequest.js' => '7cbe244b',
|
||||
'rsrc/js/core/TextAreaUtils.js' => '9e54692d',
|
||||
'rsrc/js/core/Title.js' => 'df5e11d2',
|
||||
|
@ -507,7 +507,7 @@ return array(
|
|||
'rsrc/js/phui/behavior-phui-object-box-tabs.js' => '2bfa2836',
|
||||
'rsrc/js/phuix/PHUIXActionListView.js' => 'b5c256b8',
|
||||
'rsrc/js/phuix/PHUIXActionView.js' => '8cf6d262',
|
||||
'rsrc/js/phuix/PHUIXAutocomplete.js' => 'c5f5e42f',
|
||||
'rsrc/js/phuix/PHUIXAutocomplete.js' => '2b735afc',
|
||||
'rsrc/js/phuix/PHUIXDropdownMenu.js' => 'bd4c8dca',
|
||||
'rsrc/js/phuix/PHUIXFormControl.js' => '8fba1997',
|
||||
'rsrc/js/phuix/PHUIXIconView.js' => 'bff6884b',
|
||||
|
@ -760,7 +760,7 @@ return array(
|
|||
'phabricator-notification-menu-css' => 'f31c0bde',
|
||||
'phabricator-object-selector-css' => '85ee8ce6',
|
||||
'phabricator-phtize' => 'd254d646',
|
||||
'phabricator-prefab' => 'a15cbd65',
|
||||
'phabricator-prefab' => 'e67df814',
|
||||
'phabricator-remarkup-css' => 'b748dc17',
|
||||
'phabricator-search-results-css' => '7dea472c',
|
||||
'phabricator-shaped-request' => '7cbe244b',
|
||||
|
@ -836,7 +836,7 @@ return array(
|
|||
'phui-workpanel-view-css' => 'adec7699',
|
||||
'phuix-action-list-view' => 'b5c256b8',
|
||||
'phuix-action-view' => '8cf6d262',
|
||||
'phuix-autocomplete' => 'c5f5e42f',
|
||||
'phuix-autocomplete' => '2b735afc',
|
||||
'phuix-dropdown-menu' => 'bd4c8dca',
|
||||
'phuix-form-control-view' => '8fba1997',
|
||||
'phuix-icon-view' => 'bff6884b',
|
||||
|
@ -1023,6 +1023,12 @@ return array(
|
|||
'javelin-install',
|
||||
'javelin-util',
|
||||
),
|
||||
'2b735afc' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
'phuix-icon-view',
|
||||
'phabricator-prefab',
|
||||
),
|
||||
'2b8de964' => array(
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
|
@ -1589,18 +1595,6 @@ return array(
|
|||
'javelin-dom',
|
||||
'javelin-reactor-dom',
|
||||
),
|
||||
'a15cbd65' => array(
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
'javelin-dom',
|
||||
'javelin-typeahead',
|
||||
'javelin-tokenizer',
|
||||
'javelin-typeahead-preloaded-source',
|
||||
'javelin-typeahead-ondemand-source',
|
||||
'javelin-dom',
|
||||
'javelin-stratcom',
|
||||
'javelin-util',
|
||||
),
|
||||
'a16ec1c6' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
|
@ -1799,12 +1793,6 @@ return array(
|
|||
'javelin-dom',
|
||||
'javelin-vector',
|
||||
),
|
||||
'c5f5e42f' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
'phuix-icon-view',
|
||||
'phabricator-prefab',
|
||||
),
|
||||
'c6f720ff' => array(
|
||||
'javelin-install',
|
||||
'javelin-dom',
|
||||
|
@ -1977,6 +1965,18 @@ return array(
|
|||
'javelin-workflow',
|
||||
'javelin-magical-init',
|
||||
),
|
||||
'e67df814' => array(
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
'javelin-dom',
|
||||
'javelin-typeahead',
|
||||
'javelin-tokenizer',
|
||||
'javelin-typeahead-preloaded-source',
|
||||
'javelin-typeahead-ondemand-source',
|
||||
'javelin-dom',
|
||||
'javelin-stratcom',
|
||||
'javelin-util',
|
||||
),
|
||||
'e6e25838' => array(
|
||||
'javelin-install',
|
||||
),
|
||||
|
|
|
@ -99,81 +99,8 @@ JX.install('Prefab', {
|
|||
datasource = new JX.TypeaheadPreloadedSource(config.src);
|
||||
}
|
||||
|
||||
// Sort results so that the viewing user always comes up first; after
|
||||
// that, prefer unixname matches to realname matches.
|
||||
|
||||
var sort_handler = function(value, list, cmp) {
|
||||
var priority_hits = {};
|
||||
var self_hits = {};
|
||||
|
||||
var tokens = this.tokenize(value);
|
||||
|
||||
for (var ii = 0; ii < list.length; ii++) {
|
||||
var item = list[ii];
|
||||
|
||||
for (var jj = 0; jj < tokens.length; jj++) {
|
||||
if (item.name.indexOf(tokens[jj]) === 0) {
|
||||
priority_hits[item.id] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!item.priority) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (config.username && item.priority == config.username) {
|
||||
self_hits[item.id] = true;
|
||||
}
|
||||
|
||||
for (var hh = 0; hh < tokens.length; hh++) {
|
||||
if (item.priority.substr(0, tokens[hh].length) == tokens[hh]) {
|
||||
priority_hits[item.id] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
list.sort(function(u, v) {
|
||||
if (self_hits[u.id] != self_hits[v.id]) {
|
||||
return self_hits[v.id] ? 1 : -1;
|
||||
}
|
||||
|
||||
// If one result is open and one is closed, show the open result
|
||||
// first. The "!" tricks here are becaused closed values are display
|
||||
// strings, so the value is either `null` or some truthy string. If
|
||||
// we compare the values directly, we'll apply this rule to two
|
||||
// objects which are both closed but for different reasons, like
|
||||
// "Archived" and "Disabled".
|
||||
|
||||
var u_open = !u.closed;
|
||||
var v_open = !v.closed;
|
||||
|
||||
if (u_open != v_open) {
|
||||
if (u_open) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (priority_hits[u.id] != priority_hits[v.id]) {
|
||||
return priority_hits[v.id] ? 1 : -1;
|
||||
}
|
||||
|
||||
// Sort users ahead of other result types.
|
||||
if (u.priorityType != v.priorityType) {
|
||||
if (u.priorityType == 'user') {
|
||||
return -1;
|
||||
}
|
||||
if (v.priorityType == 'user') {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return cmp(u, v);
|
||||
});
|
||||
};
|
||||
|
||||
datasource.setSortHandler(JX.bind(datasource, sort_handler));
|
||||
datasource.setSortHandler(
|
||||
JX.bind(datasource, JX.Prefab.sortHandler, config));
|
||||
datasource.setFilterHandler(JX.Prefab.filterClosedResults);
|
||||
datasource.setTransformer(JX.Prefab.transformDatasourceResults);
|
||||
|
||||
|
@ -251,6 +178,80 @@ JX.install('Prefab', {
|
|||
};
|
||||
},
|
||||
|
||||
sortHandler: function(config, value, list, cmp) {
|
||||
// Sort results so that the viewing user always comes up first; after
|
||||
// that, prefer unixname matches to realname matches.
|
||||
var priority_hits = {};
|
||||
var self_hits = {};
|
||||
|
||||
var tokens = this.tokenize(value);
|
||||
|
||||
for (var ii = 0; ii < list.length; ii++) {
|
||||
var item = list[ii];
|
||||
|
||||
for (var jj = 0; jj < tokens.length; jj++) {
|
||||
if (item.name.indexOf(tokens[jj]) === 0) {
|
||||
priority_hits[item.id] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!item.priority) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (config.username && item.priority == config.username) {
|
||||
self_hits[item.id] = true;
|
||||
}
|
||||
|
||||
for (var hh = 0; hh < tokens.length; hh++) {
|
||||
if (item.priority.substr(0, tokens[hh].length) == tokens[hh]) {
|
||||
priority_hits[item.id] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
list.sort(function(u, v) {
|
||||
if (self_hits[u.id] != self_hits[v.id]) {
|
||||
return self_hits[v.id] ? 1 : -1;
|
||||
}
|
||||
|
||||
// If one result is open and one is closed, show the open result
|
||||
// first. The "!" tricks here are becaused closed values are display
|
||||
// strings, so the value is either `null` or some truthy string. If
|
||||
// we compare the values directly, we'll apply this rule to two
|
||||
// objects which are both closed but for different reasons, like
|
||||
// "Archived" and "Disabled".
|
||||
|
||||
var u_open = !u.closed;
|
||||
var v_open = !v.closed;
|
||||
|
||||
if (u_open != v_open) {
|
||||
if (u_open) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (priority_hits[u.id] != priority_hits[v.id]) {
|
||||
return priority_hits[v.id] ? 1 : -1;
|
||||
}
|
||||
|
||||
// Sort users ahead of other result types.
|
||||
if (u.priorityType != v.priorityType) {
|
||||
if (u.priorityType == 'user') {
|
||||
return -1;
|
||||
}
|
||||
if (v.priorityType == 'user') {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return cmp(u, v);
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Filter callback for tokenizers and typeaheads which filters out closed
|
||||
* or disabled objects unless they are the only options.
|
||||
|
|
|
@ -60,8 +60,14 @@ JX.install('PHUIXAutocomplete', {
|
|||
var device = JX.bind(this, this._ondevice);
|
||||
JX.Stratcom.listen('phabricator-device-change', null, device);
|
||||
|
||||
// When the user clicks away from the textarea, deactivate.
|
||||
var deactivate = JX.bind(this, this._deactivate);
|
||||
// When the user clicks away from the textarea, deactivate. However, we
|
||||
// don't want to deactivate if we're blurring because they clicked an
|
||||
// option in the dropdown, so put a timeout on the deactivation. This
|
||||
// will let the click run first if they did actually click a result.
|
||||
var deactivate = JX.bind(this, function() {
|
||||
setTimeout(JX.bind(this, this._deactivate), 10);
|
||||
});
|
||||
|
||||
JX.DOM.listen(area, 'blur', null, deactivate);
|
||||
},
|
||||
|
||||
|
@ -134,6 +140,9 @@ JX.install('PHUIXAutocomplete', {
|
|||
JX.bind(this, this._onresults, code));
|
||||
|
||||
datasource.setTransformer(JX.bind(this, this._transformresult));
|
||||
datasource.setSortHandler(
|
||||
JX.bind(datasource, JX.Prefab.sortHandler, {}));
|
||||
datasource.setFilterHandler(JX.Prefab.filterClosedResults);
|
||||
|
||||
this._datasources[code] = datasource;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue