-
Notifications
You must be signed in to change notification settings - Fork 6.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(select): allow focusing items by typing #2907
Conversation
4712994
to
81c090f
Compare
81c090f
to
a7ab227
Compare
By when can we expect this feature to be available? |
Can we expect this feature available on 2.0.0-beta.6? |
Does this mean we should be hopeful for next beta? |
@kara, |
I got asked to create a select like this, as well. The select they compared it to was the angularjs ui-select select with search. So far, I am using a read-only input that displays a dropdown, non-input element on click (using a directive to handle clicks outside the container). That element has a search input and list. The search input filters the data and sets the read-only input upon clicking on a list item. |
@crisbeto Any chance you have time to rebase this PR so that it can be merged? :) |
a7ab227
to
425223a
Compare
What is the status of this? Just encountered this issue when implementing md-select. |
Without this functionality, md-select doesn't work how an html select is expected to and it's confusing/counter-intuitive to users (not to mention a real pain when you get more than a couple of items in there). Please please...oh please can we get some love on this one? Sounds like it's fixed and ready to go yes? |
Can we please please please have this feature? I agree that without this functionality it is a show-stopper from using the md-select control. What is needed in order to get this merged? Thanks! |
let item = items[i]; | ||
|
||
// Note that fromCharCode returns uppercase letters. | ||
if (items[i].getFocusableLabel!().toUpperCase().trim().startsWith(inputString)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace items[i]
by item
or remove the var instance.
/** | ||
* Time in ms to wait after the last keypress to focus the active item. | ||
*/ | ||
export const FOCUS_KEY_MANAGER_DEBOUNCE_INTERVAL = 200; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I sent a question off internally to see if there's a well-defined time for this (since the WAI guide just says "rapid succession")
|
||
constructor(items: QueryList<Focusable>) { | ||
super(items); | ||
this._hasLabelFn = items && items.first && 'getFocusableLabel' in items.first; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using the string 'getFocusableLabel'
won't work for minifiers that do property renaming (e.g. Closure Compiler).
(just realized we do this in a couple of places throughout the lib; we should change those)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's been a while since I did this. Revisiting it, it makes sense to require the getFocusableLabel
as a part of the Focusable
interface.
src/lib/core/keyboard/keycodes.ts
Outdated
@@ -29,3 +29,6 @@ export const TAB = 9; | |||
export const ESCAPE = 27; | |||
export const BACKSPACE = 8; | |||
export const DELETE = 46; | |||
|
|||
export const INPUT_KEY_RANGE_START = 65; | |||
export const INPUT_KEY_RANGE_END = 90; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we just make these A
and Z
?
(I'm basing my preference on being used to https://github.com/google/closure-library/blob/master/closure/goog/events/keycodes.js#L37)
} | ||
|
||
/** Debounces the input key events and focuses the proper item after the last keystroke. */ | ||
private _debounceInputEvent(keyCode: number): void { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we do this with rxjs' debounce?
425223a
to
9a788c3
Compare
@jelbourn re-did this one to move all of the logic into the |
Allows for users to skip to a select item by typing, similar to the native select. Fixes angular#2668.
9a788c3
to
72e245d
Compare
src/cdk/a11y/list-key-manager.ts
Outdated
private _tabOut = new Subject<void>(); | ||
private _wrap: boolean = false; | ||
private _wrap = false; | ||
private _pressedInputKeys: number[] = []; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add description for _pressedInputKeys
? This one isn't as immediately obvious as the other properties
src/cdk/a11y/list-key-manager.ts
Outdated
this._typeaheadSubscription.unsubscribe(); | ||
} | ||
|
||
this._typeaheadSubscription = RxChain.from(this._nonNavigationKeyStream) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be good to add a description of how you're using _pressedInputKeys
in this chain
src/cdk/a11y/list-key-manager.ts
Outdated
.call(filter, () => this._pressedInputKeys.length > 0) | ||
.call(map, () => String.fromCharCode(...this._pressedInputKeys)) | ||
.subscribe(inputString => { | ||
const activeItemIndex = this._items.toArray().findIndex(item => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IE11 doesn't have findIndex
or startsWith
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It gets added by core-js
, we have a couple of other places where we use startsWith
.
src/cdk/a11y/list-key-manager.ts
Outdated
.call(filter, () => this._pressedInputKeys.length > 0) | ||
.call(map, () => String.fromCharCode(...this._pressedInputKeys)) | ||
.subscribe(inputString => { | ||
const activeItemIndex = this._items.toArray().findIndex(item => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
activeItemIndex
-> matchedItemIndex
?
src/cdk/a11y/list-key-manager.ts
Outdated
*/ | ||
export interface CanDisable { | ||
export interface ListKeyManagerItem { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe Item
-> Option
? I think every time we use this it's for the listbox
+ option
combo
src/cdk/a11y/list-key-manager.ts
Outdated
return; | ||
case DOWN_ARROW: this.setNextItemActive(); break; | ||
case UP_ARROW: this.setPreviousItemActive(); break; | ||
default: this._nonNavigationKeyStream.next(event.keyCode); return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add back comment about using return
here to skip preventDefault
?
*/ | ||
export interface Focusable extends CanDisable { | ||
export interface Focusable extends ListKeyManagerItem { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe make this something like FocusableOption
now?
src/lib/core/option/option.ts
Outdated
@@ -174,6 +174,11 @@ export class MdOption { | |||
} | |||
} | |||
|
|||
/** Fetches the label to be used when determining whether the option should be focused. */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Fetches" -> "Gets", since "fetch" implies a server call
withTypeAhead(debounceInterval = 200): this { | ||
if (this._items.length && this._items.some(item => typeof item.getLabel !== 'function')) { | ||
throw Error('ListKeyManager items in typeahead mode must implement the `getLabel` method.'); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add test for this error?
Addressed the feedback @jelbourn, can you take another look? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Thank you so much team, awesome work! It was much needed :) |
This is something that came up in angular#2907 (comment). We shouldn't be using the `if ('someString' in obj)` checks, because they can be broken by minifiers that rename class members.
This is something that came up in #2907 (comment). We shouldn't be using the `if ('someString' in obj)` checks, because they can be broken by minifiers that rename class members.
// and convert those letters back into a string. Afterwards find the first item that starts | ||
// with that string and select it. | ||
this._typeaheadSubscription = RxChain.from(this._nonNavigationKeyStream) | ||
.call(filter, keyCode => keyCode >= A && keyCode <= Z) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so, it only searches by letters? What if options contain other characters? Also, a lot of languages have more letters, so some of them don't fit in the A-Z range
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This checks the keyboard codes, not the actual letters. E.g. 68 will correspond to d
in English and д
in a Bulgarian phonetic keyboard. As for the languages with more letters, nothing has come up yet, but we can always update the range. This seems to be what Closure uses as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Russian has 6 letters outside this range, they are on [ ] ; ' , . keys. So, they won't be handled? Also I think non-alphabetic characters should be supported as well
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about numbers? Does it work with numeric values? And when should we expect it to be release?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've submitted #6543 that should handle more or less everything. It'll still fall back to A-Z and 0-9 for browsers that don't support KeyboardEvent.key
(should be a very small percentage).
…cters As per the discussion on angular#2907 (comment), the current typeahead setup won't cover the cases where the user is typing in an alphabet that has more characters than English. These changes update the check so that it catches any typed character (including non-alphanumeric characters).
Any idea when this will be in a release? Our users are complaining, so we're considering ripping out md-select and using a different library's control... but if a release is imminent we'd just wait for that... |
Should this work with latest release (2.0.0-beta.11)? Somehow, its still not working for me. |
I'm in version 2.0.0-beta.11 and does not work. |
do we have a working example of this somewhere? |
Please also add support for: Home key - Which should take to the first item of the list |
+1 When is this expected to be released? |
@kylekrzeski two months ago in |
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
Allows for users to skip to a select item by typing, similar to the native select.
Fixes #2668.