-
Notifications
You must be signed in to change notification settings - Fork 2.9k
ComboBox/SplitButton Expand on Touch #4353
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
Changes from 20 commits
07b16f9
3502f3a
1deccdd
4f36e14
06806a1
e6e0276
78ee211
aea2641
020ed81
c60caca
8b888d8
fbb8857
4c5720e
90c6ac1
b8b07c9
fab1029
0293d75
fa7671e
c284b76
d6be2f4
30a2933
1fe7db7
ad563fe
1702de4
f20265a
ec0bd53
51e730f
f8fea3b
dd096d8
acce342
dbaa322
e3ebdf7
e57e6e9
dd13382
089233c
a94b7c5
71ade9a
b68bf62
16a88a7
cec47d0
6d3810e
1f9fd86
8ce9bc7
9b45fef
fb63fb8
e7d0229
9d247be
d3b1d13
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| { | ||
| "changes": [ | ||
| { | ||
| "packageName": "office-ui-fabric-react", | ||
| "comment": "SplitButton/ComboBox: added onTouch support for menu expansion.", | ||
| "type": "minor" | ||
| } | ||
| ], | ||
| "packageName": "office-ui-fabric-react", | ||
| "email": "[email protected]" | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,6 +16,7 @@ import { ContextualMenu, IContextualMenuProps } from '../../ContextualMenu'; | |
| import { IButtonProps, IButton } from './Button.types'; | ||
| import { IButtonClassNames, getBaseButtonClassNames } from './BaseButton.classNames'; | ||
| import { getClassNames as getBaseSplitButtonClassNames, ISplitButtonClassNames } from './SplitButton/SplitButton.classNames'; | ||
| import { TouchEvent } from 'react'; | ||
|
|
||
| export interface IBaseButtonProps extends IButtonProps { | ||
| baseClassName?: string; | ||
|
|
@@ -49,6 +50,8 @@ export class BaseButton extends BaseComponent<IBaseButtonProps, IBaseButtonState | |
| private _descriptionId: string; | ||
| private _ariaDescriptionId: string; | ||
| private _classNames: IButtonClassNames; | ||
| private _processingTouch: boolean; | ||
| private _lastTouchTimeoutId: number | undefined; | ||
|
|
||
| constructor(props: IBaseButtonProps, rootClassName: string) { | ||
| super(props); | ||
|
|
@@ -192,6 +195,12 @@ export class BaseButton extends BaseComponent<IBaseButtonProps, IBaseButtonState | |
| return this._onRenderContent(tag, buttonProps); | ||
| } | ||
|
|
||
| public componentDidMount() { | ||
| if (this._isSplitButton && this._splitButtonContainer.value && 'onpointerdown' in this._splitButtonContainer.value) { | ||
| this._events.on(this._splitButtonContainer.value, 'pointerdown', this._onPointerDown, true); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can't you use react eventing? You should avoid using native events when possible.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. React does not support this yet, you could potentially mix it in with a |
||
| } | ||
| } | ||
|
|
||
| public componentDidUpdate(prevProps: IBaseButtonProps, prevState: IBaseButtonState) { | ||
| // If Button's menu was closed, run onAfterMenuDismiss | ||
| if (this.props.onAfterMenuDismiss && prevState.menuProps && !this.state.menuProps) { | ||
|
|
@@ -425,7 +434,6 @@ export class BaseButton extends BaseComponent<IBaseButtonProps, IBaseButtonState | |
| disabled, | ||
| checked, | ||
| getSplitButtonClassNames, | ||
| onClick, | ||
| primaryDisabled | ||
| } = this.props; | ||
|
|
||
|
|
@@ -458,6 +466,7 @@ export class BaseButton extends BaseComponent<IBaseButtonProps, IBaseButtonState | |
| aria-describedby={ buttonProps.ariaDescription } | ||
| className={ classNames && classNames.splitButtonContainer } | ||
| onKeyDown={ this._onSplitButtonContainerKeyDown } | ||
| onTouchStart={ this._onTouchStart } | ||
| ref={ this._splitButtonContainer } | ||
| data-is-focusable={ true } | ||
| onClick={ !disabled && !primaryDisabled ? this._onSplitButtonPrimaryClick : undefined } | ||
|
|
@@ -478,8 +487,11 @@ export class BaseButton extends BaseComponent<IBaseButtonProps, IBaseButtonState | |
| if (this._isExpanded) { | ||
| this._dismissMenu(); | ||
| } | ||
| if (this.props.onClick) { | ||
|
|
||
| if (!this._processingTouch && this.props.onClick) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now if we are not
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're correct, I've changed it to an else if to only do a menu click if we're processingTouch |
||
| this.props.onClick(ev); | ||
| } else { | ||
| this._onMenuClick(ev); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -520,6 +532,14 @@ export class BaseButton extends BaseComponent<IBaseButtonProps, IBaseButtonState | |
| return <BaseButton {...splitButtonProps} onMouseDown={ this._onMouseDown } tabIndex={ -1 } />; | ||
| } | ||
|
|
||
| private _onSplitButtonClick = (ev: React.MouseEvent<HTMLDivElement | HTMLAnchorElement>) => { | ||
|
||
| if (!this._processingTouch && this.props.onClick) { | ||
| this.props.onClick(ev); | ||
| } else { | ||
| this._onMenuClick(ev); | ||
| } | ||
| } | ||
|
|
||
| private _onMouseDown = (ev: React.MouseEvent<BaseButton>) => { | ||
| if (this.props.onMouseDown) { | ||
| this.props.onMouseDown(ev); | ||
|
|
@@ -563,6 +583,36 @@ export class BaseButton extends BaseComponent<IBaseButtonProps, IBaseButtonState | |
| } | ||
| } | ||
|
|
||
| private _onTouchStart: (ev: TouchEvent<HTMLElement>) => void = (ev) => { | ||
| if (this._isSplitButton && this._splitButtonContainer.value && !('onpointerdown' in this._splitButtonContainer.value)) { | ||
| this._handleTouchAndPointerEvent(); | ||
| } | ||
| } | ||
|
|
||
| private _onPointerDown(ev: PointerEvent) { | ||
| if (ev.pointerType === 'touch') { | ||
| this._handleTouchAndPointerEvent(); | ||
|
|
||
| ev.preventDefault(); | ||
| ev.stopImmediatePropagation(); | ||
| } | ||
| } | ||
|
|
||
| private _handleTouchAndPointerEvent() { | ||
| // If we already have an existing timeeout from a previous touch and pointer event | ||
| // cancel that timeout so we can set a nwe one. | ||
| if (this._lastTouchTimeoutId !== undefined) { | ||
| this._async.clearTimeout(this._lastTouchTimeoutId); | ||
| this._lastTouchTimeoutId = undefined; | ||
| } | ||
| this._processingTouch = true; | ||
|
|
||
| this._lastTouchTimeoutId = this._async.setTimeout(() => { | ||
| this._processingTouch = false; | ||
| this._lastTouchTimeoutId = undefined; | ||
| }, 500); | ||
| } | ||
|
|
||
| /** | ||
| * Returns if the user hits a valid keyboard key to open the menu | ||
| * @param ev - the keyboard event | ||
|
|
@@ -576,7 +626,7 @@ export class BaseButton extends BaseComponent<IBaseButtonProps, IBaseButtonState | |
| } | ||
| } | ||
|
|
||
| private _onMenuClick = (ev: React.MouseEvent<HTMLAnchorElement>) => { | ||
| private _onMenuClick = (ev: React.MouseEvent<HTMLDivElement | HTMLAnchorElement>) => { | ||
| const { onMenuClick } = this.props; | ||
| if (onMenuClick) { | ||
| onMenuClick(ev, this); | ||
|
|
||
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.
Perhaps leave a comment here on why! That way the person re-writing the button later can understand your motivation.