office-ui-fabric-react
Version:
Reusable React components for building experiences for Microsoft 365.
608 lines • 39 kB
JavaScript
define(["require", "exports", "tslib", "react", "../../Utilities", "../../Icon", "../../common/DirectionalHint", "../../ContextualMenu", "./BaseButton.classNames", "./SplitButton/SplitButton.classNames", "../../KeytipData", "../../Utilities"], function (require, exports, tslib_1, React, Utilities_1, Icon_1, DirectionalHint_1, ContextualMenu_1, BaseButton_classNames_1, SplitButton_classNames_1, KeytipData_1, Utilities_2) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var TouchIdleDelay = 500; /* ms */
var COMPONENT_NAME = 'BaseButton';
/**
* {@docCategory Button}
*/
var BaseButton = /** @class */ (function (_super) {
tslib_1.__extends(BaseButton, _super);
function BaseButton(props) {
var _this = _super.call(this, props) || this;
_this._buttonElement = React.createRef();
_this._splitButtonContainer = React.createRef();
_this._mergedRef = Utilities_1.createMergedRef();
_this._renderedVisibleMenu = false;
_this._getMemoizedMenuButtonKeytipProps = Utilities_1.memoizeFunction(function (keytipProps) {
return tslib_1.__assign(tslib_1.__assign({}, keytipProps), { hasMenu: true });
});
_this._onRenderIcon = function (buttonProps, defaultRender) {
var iconProps = _this.props.iconProps;
if (iconProps && (iconProps.iconName !== undefined || iconProps.imageProps)) {
var className = iconProps.className, imageProps = iconProps.imageProps, rest = tslib_1.__rest(iconProps, ["className", "imageProps"]);
// If the styles prop is specified as part of iconProps, fall back to regular Icon as FontIcon and ImageIcon
// do not have this prop.
if (iconProps.styles) {
return React.createElement(Icon_1.Icon, tslib_1.__assign({ className: Utilities_1.css(_this._classNames.icon, className), imageProps: imageProps }, rest));
}
if (iconProps.iconName) {
return React.createElement(Icon_1.FontIcon, tslib_1.__assign({ className: Utilities_1.css(_this._classNames.icon, className) }, rest));
}
if (imageProps) {
return React.createElement(Icon_1.ImageIcon, tslib_1.__assign({ className: Utilities_1.css(_this._classNames.icon, className), imageProps: imageProps }, rest));
}
}
return null;
};
_this._onRenderTextContents = function () {
var _a = _this.props, text = _a.text, children = _a.children,
// eslint-disable-next-line deprecation/deprecation
_b = _a.secondaryText,
// eslint-disable-next-line deprecation/deprecation
secondaryText = _b === void 0 ? _this.props.description : _b, _c = _a.onRenderText, onRenderText = _c === void 0 ? _this._onRenderText : _c, _d = _a.onRenderDescription, onRenderDescription = _d === void 0 ? _this._onRenderDescription : _d;
if (text || typeof children === 'string' || secondaryText) {
return (React.createElement("span", { className: _this._classNames.textContainer },
onRenderText(_this.props, _this._onRenderText),
onRenderDescription(_this.props, _this._onRenderDescription)));
}
return [onRenderText(_this.props, _this._onRenderText), onRenderDescription(_this.props, _this._onRenderDescription)];
};
_this._onRenderText = function () {
var text = _this.props.text;
var children = _this.props.children;
// For backwards compat, we should continue to take in the text content from children.
if (text === undefined && typeof children === 'string') {
text = children;
}
if (_this._hasText()) {
return (React.createElement("span", { key: _this._labelId, className: _this._classNames.label, id: _this._labelId }, text));
}
return null;
};
_this._onRenderChildren = function () {
var children = _this.props.children;
// If children is just a string, either it or the text will be rendered via onRenderLabel
// If children is another component, it will be rendered after text
if (typeof children === 'string') {
return null;
}
return children;
};
_this._onRenderDescription = function (props) {
// eslint-disable-next-line deprecation/deprecation
var _a = props.secondaryText, secondaryText = _a === void 0 ? _this.props.description : _a;
// ms-Button-description is only shown when the button type is compound.
// In other cases it will not be displayed.
return secondaryText ? (React.createElement("span", { key: _this._descriptionId, className: _this._classNames.description, id: _this._descriptionId }, secondaryText)) : null;
};
_this._onRenderAriaDescription = function () {
var ariaDescription = _this.props.ariaDescription;
// If ariaDescription is given, descriptionId will be assigned to ariaDescriptionSpan,
// otherwise it will be assigned to descriptionSpan.
return ariaDescription ? (React.createElement("span", { className: _this._classNames.screenReaderText, id: _this._ariaDescriptionId }, ariaDescription)) : null;
};
_this._onRenderMenuIcon = function (props) {
var menuIconProps = _this.props.menuIconProps;
return React.createElement(Icon_1.FontIcon, tslib_1.__assign({ iconName: "ChevronDown" }, menuIconProps, { className: _this._classNames.menuIcon }));
};
_this._onRenderMenu = function (menuProps) {
var MenuType = _this.props.menuAs ? Utilities_2.composeComponentAs(_this.props.menuAs, ContextualMenu_1.ContextualMenu) : ContextualMenu_1.ContextualMenu;
return React.createElement(MenuType, tslib_1.__assign({}, menuProps));
};
_this._onDismissMenu = function (ev) {
var menuProps = _this.props.menuProps;
if (menuProps && menuProps.onDismiss) {
menuProps.onDismiss(ev);
}
if (!ev || !ev.defaultPrevented) {
_this._dismissMenu();
}
};
_this._dismissMenu = function () {
_this._menuShouldFocusOnMount = undefined;
_this._menuShouldFocusOnContainer = undefined;
_this.setState({ menuHidden: true });
};
_this._openMenu = function (shouldFocusOnContainer, shouldFocusOnMount) {
if (shouldFocusOnMount === void 0) { shouldFocusOnMount = true; }
if (_this.props.menuProps) {
_this._menuShouldFocusOnContainer = shouldFocusOnContainer;
_this._menuShouldFocusOnMount = shouldFocusOnMount;
_this._renderedVisibleMenu = true;
_this.setState({ menuHidden: false });
}
};
_this._onToggleMenu = function (shouldFocusOnContainer) {
var shouldFocusOnMount = true;
if (_this.props.menuProps && _this.props.menuProps.shouldFocusOnMount === false) {
shouldFocusOnMount = false;
}
_this.state.menuHidden ? _this._openMenu(shouldFocusOnContainer, shouldFocusOnMount) : _this._dismissMenu();
};
_this._onSplitContainerFocusCapture = function (ev) {
var container = _this._splitButtonContainer.current;
// If the target is coming from the portal we do not need to set focus on the container.
if (!container || (ev.target && Utilities_1.portalContainsElement(ev.target, container))) {
return;
}
// We should never be able to focus the individual buttons in a split button. Focus
// should always remain on the container.
container.focus();
};
_this._onSplitButtonPrimaryClick = function (ev) {
if (!_this.state.menuHidden) {
_this._dismissMenu();
}
if (!_this._processingTouch && _this.props.onClick) {
_this.props.onClick(ev);
}
else if (_this._processingTouch) {
_this._onMenuClick(ev);
}
};
_this._onKeyDown = function (ev) {
// explicity cancelling event so click won't fire after this
if (_this.props.disabled && (ev.which === Utilities_1.KeyCodes.enter || ev.which === Utilities_1.KeyCodes.space)) {
ev.preventDefault();
ev.stopPropagation();
}
else if (!_this.props.disabled) {
if (_this.props.menuProps) {
_this._onMenuKeyDown(ev);
}
else if (_this.props.onKeyDown !== undefined) {
_this.props.onKeyDown(ev); // not cancelling event because it's not disabled
}
}
};
_this._onKeyUp = function (ev) {
if (!_this.props.disabled && _this.props.onKeyUp !== undefined) {
_this.props.onKeyUp(ev); // not cancelling event because it's not disabled
}
};
_this._onKeyPress = function (ev) {
if (!_this.props.disabled && _this.props.onKeyPress !== undefined) {
_this.props.onKeyPress(ev); // not cancelling event because it's not disabled
}
};
_this._onMouseUp = function (ev) {
if (!_this.props.disabled && _this.props.onMouseUp !== undefined) {
_this.props.onMouseUp(ev); // not cancelling event because it's not disabled
}
};
_this._onMouseDown = function (ev) {
if (!_this.props.disabled && _this.props.onMouseDown !== undefined) {
_this.props.onMouseDown(ev); // not cancelling event because it's not disabled
}
};
_this._onClick = function (ev) {
if (!_this.props.disabled) {
if (_this.props.menuProps) {
_this._onMenuClick(ev);
}
else if (_this.props.onClick !== undefined) {
_this.props.onClick(ev); // not cancelling event because it's not disabled
}
}
};
_this._onSplitButtonContainerKeyDown = function (ev) {
if (ev.which === Utilities_1.KeyCodes.enter || ev.which === Utilities_1.KeyCodes.space) {
if (_this._buttonElement.current) {
_this._buttonElement.current.click();
ev.preventDefault();
ev.stopPropagation();
}
}
else {
_this._onMenuKeyDown(ev);
}
};
_this._onMenuKeyDown = function (ev) {
if (_this.props.disabled) {
return;
}
if (_this.props.onKeyDown) {
_this.props.onKeyDown(ev);
}
var isUp = ev.which === Utilities_1.KeyCodes.up;
var isDown = ev.which === Utilities_1.KeyCodes.down;
if (!ev.defaultPrevented && _this._isValidMenuOpenKey(ev)) {
var onMenuClick = _this.props.onMenuClick;
if (onMenuClick) {
onMenuClick(ev, _this.props);
}
_this._onToggleMenu(false);
ev.preventDefault();
ev.stopPropagation();
}
if (ev.which === Utilities_1.KeyCodes.enter || ev.which === Utilities_1.KeyCodes.space) {
// We manually set the focus visibility to true if opening via Enter or Space to account for the scenario where
// a user clicks on the button, closes the menu and then opens it via keyboard. In this scenario our default logic
// for setting focus visibility is not triggered since there is no keyboard navigation present beforehand.
Utilities_1.setFocusVisibility(true, ev.target);
}
if (!(ev.altKey || ev.metaKey) && (isUp || isDown)) {
// Suppose a menu, with shouldFocusOnMount: false, is open, and user wants to keyboard to the menu items
// We need to re-render the menu with shouldFocusOnMount as true.
if (!_this.state.menuHidden && _this.props.menuProps) {
var currentShouldFocusOnMount = _this._menuShouldFocusOnMount !== undefined
? _this._menuShouldFocusOnMount
: _this.props.menuProps.shouldFocusOnMount;
if (!currentShouldFocusOnMount) {
ev.preventDefault();
ev.stopPropagation();
_this._menuShouldFocusOnMount = true;
_this.forceUpdate();
}
}
}
};
_this._onTouchStart = function () {
if (_this._isSplitButton &&
_this._splitButtonContainer.current &&
!('onpointerdown' in _this._splitButtonContainer.current)) {
_this._handleTouchAndPointerEvent();
}
};
_this._onMenuClick = function (ev) {
var onMenuClick = _this.props.onMenuClick;
if (onMenuClick) {
onMenuClick(ev, _this.props);
}
if (!ev.defaultPrevented) {
// When Edge + Narrator are used together (regardless of if the button is in a form or not), pressing
// "Enter" fires this method and not _onMenuKeyDown. Checking ev.nativeEvent.detail differentiates
// between a real click event and a keypress event (detail should be the number of mouse clicks).
// ...Plot twist! For a real click event in IE 11, detail is always 0 (Edge sets it properly to 1).
// So we also check the pointerType property, which both Edge and IE set to "mouse" for real clicks
// and "" for pressing "Enter" with Narrator on.
var shouldFocusOnContainer = ev.nativeEvent.detail !== 0 || ev.nativeEvent.pointerType === 'mouse';
_this._onToggleMenu(shouldFocusOnContainer);
ev.preventDefault();
ev.stopPropagation();
}
};
Utilities_1.initializeComponentRef(_this);
_this._async = new Utilities_1.Async(_this);
_this._events = new Utilities_1.EventGroup(_this);
Utilities_1.warnConditionallyRequiredProps(COMPONENT_NAME, props, ['menuProps', 'onClick'], 'split', _this.props.split);
Utilities_1.warnDeprecations(COMPONENT_NAME, props, {
rootProps: undefined,
description: 'secondaryText',
toggled: 'checked',
});
_this._labelId = Utilities_1.getId();
_this._descriptionId = Utilities_1.getId();
_this._ariaDescriptionId = Utilities_1.getId();
_this.state = {
menuHidden: true,
};
return _this;
}
Object.defineProperty(BaseButton.prototype, "_isSplitButton", {
get: function () {
return !!this.props.menuProps && !!this.props.onClick && this.props.split === true;
},
enumerable: true,
configurable: true
});
BaseButton.prototype.render = function () {
var _a;
var _b = this.props, ariaDescription = _b.ariaDescription, ariaLabel = _b.ariaLabel, ariaHidden = _b.ariaHidden, className = _b.className, disabled = _b.disabled, allowDisabledFocus = _b.allowDisabledFocus, primaryDisabled = _b.primaryDisabled,
// eslint-disable-next-line deprecation/deprecation
_c = _b.secondaryText,
// eslint-disable-next-line deprecation/deprecation
secondaryText = _c === void 0 ? this.props.description : _c, href = _b.href, iconProps = _b.iconProps, menuIconProps = _b.menuIconProps, styles = _b.styles, checked = _b.checked, variantClassName = _b.variantClassName, theme = _b.theme, toggle = _b.toggle, getClassNames = _b.getClassNames, role = _b.role;
var menuHidden = this.state.menuHidden;
// Button is disabled if the whole button (in case of splitButton is disabled) or if the primary action is disabled
var isPrimaryButtonDisabled = disabled || primaryDisabled;
this._classNames = getClassNames
? getClassNames(theme, className, variantClassName, iconProps && iconProps.className, menuIconProps && menuIconProps.className, isPrimaryButtonDisabled, checked, !menuHidden, !!this.props.menuProps, this.props.split, !!allowDisabledFocus)
: BaseButton_classNames_1.getBaseButtonClassNames(theme, styles, className, variantClassName, iconProps && iconProps.className, menuIconProps && menuIconProps.className, isPrimaryButtonDisabled, !!this.props.menuProps, checked, !menuHidden, this.props.split);
var _d = this, _ariaDescriptionId = _d._ariaDescriptionId, _labelId = _d._labelId, _descriptionId = _d._descriptionId;
// Anchor tag cannot be disabled hence in disabled state rendering
// anchor button as normal button
var renderAsAnchor = !isPrimaryButtonDisabled && !!href;
var tag = renderAsAnchor ? 'a' : 'button';
var nativeProps = Utilities_1.getNativeProps(
// eslint-disable-next-line deprecation/deprecation
Utilities_1.assign(renderAsAnchor ? {} : { type: 'button' }, this.props.rootProps, this.props), renderAsAnchor ? Utilities_1.anchorProperties : Utilities_1.buttonProperties, [
'disabled',
]);
// Check for ariaLabel passed in via Button props, and fall back to aria-label passed in via native props
var resolvedAriaLabel = ariaLabel || nativeProps['aria-label'];
// Check for ariaDescription, secondaryText or aria-describedby in the native props to determine source of
// aria-describedby. Otherwise default to undefined so property does not appear in output.
var ariaDescribedBy = undefined;
if (ariaDescription) {
ariaDescribedBy = _ariaDescriptionId;
}
else if (secondaryText && this.props.onRenderDescription !== Utilities_1.nullRender) {
// for buttons like CompoundButton with a valid onRenderDescription, we need to set an ariaDescribedBy
// for buttons that do not render anything (via nullRender), we should not set an ariaDescribedBy
ariaDescribedBy = _descriptionId;
}
else if (nativeProps['aria-describedby']) {
ariaDescribedBy = nativeProps['aria-describedby'];
}
// If an explicit ariaLabel is given, use that as the label and we're done.
// If an explicit aria-labelledby is given, use that and we're done.
// If any kind of description is given (which will end up as an aria-describedby attribute),
// set the labelledby element. Otherwise, the button is labeled implicitly by the descendent
// text on the button (if it exists). Never set both aria-label and aria-labelledby.
var ariaLabelledBy = undefined;
if (!resolvedAriaLabel) {
if (nativeProps['aria-labelledby']) {
ariaLabelledBy = nativeProps['aria-labelledby'];
}
else if (ariaDescribedBy) {
ariaLabelledBy = this._hasText() ? _labelId : undefined;
}
}
var dataIsFocusable = this.props['data-is-focusable'] === false || (disabled && !allowDisabledFocus) || this._isSplitButton
? false
: true;
var isCheckboxTypeRole = role === 'menuitemcheckbox' || role === 'checkbox';
// if isCheckboxTypeRole, always return a checked value.
// Otherwise only return checked value if toggle is set to true.
// This is because role="checkbox" always needs to have an aria-checked value
// but our checked prop only sets aria-pressed if we mark the button as a toggle="true"
var checkedOrPressedValue = isCheckboxTypeRole ? !!checked : toggle === true ? !!checked : undefined;
var buttonProps = Utilities_1.assign(nativeProps, (_a = {
className: this._classNames.root,
// eslint-disable-next-line deprecation/deprecation
ref: this._mergedRef(this.props.elementRef, this._buttonElement),
disabled: isPrimaryButtonDisabled && !allowDisabledFocus,
onKeyDown: this._onKeyDown,
onKeyPress: this._onKeyPress,
onKeyUp: this._onKeyUp,
onMouseDown: this._onMouseDown,
onMouseUp: this._onMouseUp,
onClick: this._onClick,
'aria-label': resolvedAriaLabel,
'aria-labelledby': ariaLabelledBy,
'aria-describedby': ariaDescribedBy,
'aria-disabled': isPrimaryButtonDisabled,
'data-is-focusable': dataIsFocusable
},
// aria-pressed attribute should only be present for toggle buttons
// aria-checked attribute should only be present for toggle buttons with checkbox type role
_a[isCheckboxTypeRole ? 'aria-checked' : 'aria-pressed'] = checkedOrPressedValue,
_a));
if (ariaHidden) {
buttonProps['aria-hidden'] = true;
}
if (this._isSplitButton) {
return this._onRenderSplitButtonContent(tag, buttonProps);
}
else if (this.props.menuProps) {
Utilities_1.assign(buttonProps, {
'aria-expanded': !menuHidden,
'aria-owns': !menuHidden ? this._labelId + '-menu' : null,
'aria-haspopup': true,
});
}
return this._onRenderContent(tag, buttonProps);
};
BaseButton.prototype.componentDidMount = function () {
// For split buttons, touching anywhere in the button should drop the dropdown, which should contain the
// primary action. This gives more hit target space for touch environments. We're setting the onpointerdown here,
// because React does not support Pointer events yet.
if (this._isSplitButton && this._splitButtonContainer.current) {
if ('onpointerdown' in this._splitButtonContainer.current) {
this._events.on(this._splitButtonContainer.current, 'pointerdown', this._onPointerDown, true);
}
if ('onpointerup' in this._splitButtonContainer.current && this.props.onPointerUp) {
this._events.on(this._splitButtonContainer.current, 'pointerup', this.props.onPointerUp, true);
}
}
};
BaseButton.prototype.componentDidUpdate = function (prevProps, prevState) {
// If Button's menu was closed, run onAfterMenuDismiss.
if (this.props.onAfterMenuDismiss && !prevState.menuHidden && this.state.menuHidden) {
this.props.onAfterMenuDismiss();
}
};
BaseButton.prototype.componentWillUnmount = function () {
this._async.dispose();
this._events.dispose();
};
BaseButton.prototype.focus = function () {
if (this._isSplitButton && this._splitButtonContainer.current) {
Utilities_1.setFocusVisibility(true);
this._splitButtonContainer.current.focus();
}
else if (this._buttonElement.current) {
Utilities_1.setFocusVisibility(true);
this._buttonElement.current.focus();
}
};
BaseButton.prototype.dismissMenu = function () {
this._dismissMenu();
};
BaseButton.prototype.openMenu = function (shouldFocusOnContainer, shouldFocusOnMount) {
this._openMenu(shouldFocusOnContainer, shouldFocusOnMount);
};
BaseButton.prototype._onRenderContent = function (tag, buttonProps) {
var _this = this;
var props = this.props;
var Tag = tag;
var menuIconProps = props.menuIconProps, menuProps = props.menuProps, _a = props.onRenderIcon, onRenderIcon = _a === void 0 ? this._onRenderIcon : _a, _b = props.onRenderAriaDescription, onRenderAriaDescription = _b === void 0 ? this._onRenderAriaDescription : _b, _c = props.onRenderChildren, onRenderChildren = _c === void 0 ? this._onRenderChildren : _c,
// eslint-disable-next-line deprecation/deprecation
_d = props.onRenderMenu,
// eslint-disable-next-line deprecation/deprecation
onRenderMenu = _d === void 0 ? this._onRenderMenu : _d, _e = props.onRenderMenuIcon, onRenderMenuIcon = _e === void 0 ? this._onRenderMenuIcon : _e, disabled = props.disabled;
var keytipProps = props.keytipProps;
if (keytipProps && menuProps) {
keytipProps = this._getMemoizedMenuButtonKeytipProps(keytipProps);
}
var Button = function (keytipAttributes) { return (React.createElement(Tag, tslib_1.__assign({}, buttonProps, keytipAttributes),
React.createElement("span", { className: _this._classNames.flexContainer, "data-automationid": "splitbuttonprimary" },
onRenderIcon(props, _this._onRenderIcon),
_this._onRenderTextContents(),
onRenderAriaDescription(props, _this._onRenderAriaDescription),
onRenderChildren(props, _this._onRenderChildren),
!_this._isSplitButton &&
(menuProps || menuIconProps || _this.props.onRenderMenuIcon) &&
onRenderMenuIcon(_this.props, _this._onRenderMenuIcon),
menuProps &&
!menuProps.doNotLayer &&
_this._shouldRenderMenu() &&
onRenderMenu(_this._getMenuProps(menuProps), _this._onRenderMenu)))); };
var Content = keytipProps ? (
// If we're making a split button, we won't put the keytip here
React.createElement(KeytipData_1.KeytipData, { keytipProps: !this._isSplitButton ? keytipProps : undefined, ariaDescribedBy: buttonProps['aria-describedby'], disabled: disabled }, function (keytipAttributes) { return Button(keytipAttributes); })) : (Button());
if (menuProps && menuProps.doNotLayer) {
return (React.createElement("span", { style: { display: 'inline-block' } },
Content,
this._shouldRenderMenu() && onRenderMenu(this._getMenuProps(menuProps), this._onRenderMenu)));
}
return (React.createElement(React.Fragment, null,
Content,
React.createElement(Utilities_1.FocusRects, null)));
};
/**
* Method to help determine if the menu's component tree should
* be rendered. It takes into account whether the menu is expanded,
* whether it is a persisted menu and whether it has been shown to the user.
*/
BaseButton.prototype._shouldRenderMenu = function () {
var menuHidden = this.state.menuHidden;
// eslint-disable-next-line deprecation/deprecation
var _a = this.props, persistMenu = _a.persistMenu, renderPersistedMenuHiddenOnMount = _a.renderPersistedMenuHiddenOnMount;
if (!menuHidden) {
// Always should render a menu when it is expanded
return true;
}
else if (persistMenu && (this._renderedVisibleMenu || renderPersistedMenuHiddenOnMount)) {
// _renderedVisibleMenu ensures that the first rendering of
// the menu happens on-screen, as edge's scrollbar calculations are off if done while hidden.
return true;
}
return false;
};
BaseButton.prototype._hasText = function () {
// _onRenderTextContents and _onRenderText do not perform the same checks. Below is parity with what _onRenderText
// used to have before the refactor that introduced this function. _onRenderTextContents does not require props.
// text to be undefined in order for props.children to be used as a fallback.
// Purely a code maintainability/reuse issue, but logged as Issue #4979.
return this.props.text !== null && (this.props.text !== undefined || typeof this.props.children === 'string');
};
BaseButton.prototype._getMenuProps = function (menuProps) {
var persistMenu = this.props.persistMenu;
var menuHidden = this.state.menuHidden;
// the accessible menu label (accessible name) has a relationship to the button.
// If the menu props do not specify an explicit value for aria-label or aria-labelledBy,
// AND the button has text, we'll set the menu aria-labelledBy to the text element id.
if (!menuProps.ariaLabel && !menuProps.labelElementId && this._hasText()) {
menuProps = tslib_1.__assign(tslib_1.__assign({}, menuProps), { labelElementId: this._labelId });
}
return tslib_1.__assign(tslib_1.__assign({ id: this._labelId + '-menu', directionalHint: DirectionalHint_1.DirectionalHint.bottomLeftEdge }, menuProps), { shouldFocusOnContainer: this._menuShouldFocusOnContainer, shouldFocusOnMount: this._menuShouldFocusOnMount, hidden: persistMenu ? menuHidden : undefined, className: Utilities_1.css('ms-BaseButton-menuhost', menuProps.className), target: this._isSplitButton ? this._splitButtonContainer.current : this._buttonElement.current, onDismiss: this._onDismissMenu });
};
BaseButton.prototype._onRenderSplitButtonContent = function (tag, buttonProps) {
var _this = this;
var _a = this.props, _b = _a.styles, styles = _b === void 0 ? {} : _b, disabled = _a.disabled, allowDisabledFocus = _a.allowDisabledFocus, checked = _a.checked, getSplitButtonClassNames = _a.getSplitButtonClassNames, primaryDisabled = _a.primaryDisabled, menuProps = _a.menuProps, toggle = _a.toggle, role = _a.role, primaryActionButtonProps = _a.primaryActionButtonProps;
var keytipProps = this.props.keytipProps;
var menuHidden = this.state.menuHidden;
var classNames = getSplitButtonClassNames
? getSplitButtonClassNames(!!disabled, !menuHidden, !!checked, !!allowDisabledFocus)
: styles && SplitButton_classNames_1.getSplitButtonClassNames(styles, !!disabled, !menuHidden, !!checked, !!primaryDisabled);
Utilities_1.assign(buttonProps, {
onClick: undefined,
onPointerDown: undefined,
onPointerUp: undefined,
tabIndex: -1,
'data-is-focusable': false,
});
if (keytipProps && menuProps) {
keytipProps = this._getMemoizedMenuButtonKeytipProps(keytipProps);
}
var containerProps = Utilities_1.getNativeProps(buttonProps, [], ['disabled']);
// Add additional props to apply on primary action button
if (primaryActionButtonProps) {
Utilities_1.assign(buttonProps, primaryActionButtonProps);
}
var SplitButton = function (keytipAttributes) { return (React.createElement("div", tslib_1.__assign({}, containerProps, { "data-ktp-target": keytipAttributes ? keytipAttributes['data-ktp-target'] : undefined, role: role ? role : 'button', "aria-disabled": disabled, "aria-haspopup": true, "aria-expanded": !menuHidden, "aria-pressed": toggle ? !!checked : undefined, "aria-describedby": Utilities_1.mergeAriaAttributeValues(buttonProps['aria-describedby'], keytipAttributes ? keytipAttributes['aria-describedby'] : undefined), className: classNames && classNames.splitButtonContainer, onKeyDown: _this._onSplitButtonContainerKeyDown, onTouchStart: _this._onTouchStart, ref: _this._splitButtonContainer, "data-is-focusable": true, onClick: !disabled && !primaryDisabled ? _this._onSplitButtonPrimaryClick : undefined, tabIndex: (!disabled && !primaryDisabled) || allowDisabledFocus ? 0 : undefined, "aria-roledescription": buttonProps['aria-roledescription'], onFocusCapture: _this._onSplitContainerFocusCapture }),
React.createElement("span", { style: { display: 'flex' } },
_this._onRenderContent(tag, buttonProps),
_this._onRenderSplitButtonMenuButton(classNames, keytipAttributes),
_this._onRenderSplitButtonDivider(classNames)))); };
return keytipProps ? (React.createElement(KeytipData_1.KeytipData, { keytipProps: keytipProps, disabled: disabled }, function (keytipAttributes) { return SplitButton(keytipAttributes); })) : (SplitButton());
};
BaseButton.prototype._onRenderSplitButtonDivider = function (classNames) {
if (classNames && classNames.divider) {
var onClick = function (ev) {
ev.stopPropagation();
};
return React.createElement("span", { className: classNames.divider, "aria-hidden": true, onClick: onClick });
}
return null;
};
BaseButton.prototype._onRenderSplitButtonMenuButton = function (classNames, keytipAttributes) {
var _a = this.props, allowDisabledFocus = _a.allowDisabledFocus, checked = _a.checked, disabled = _a.disabled, splitButtonMenuProps = _a.splitButtonMenuProps, splitButtonAriaLabel = _a.splitButtonAriaLabel, primaryDisabled = _a.primaryDisabled;
var menuHidden = this.state.menuHidden;
var menuIconProps = this.props.menuIconProps;
if (menuIconProps === undefined) {
menuIconProps = {
iconName: 'ChevronDown',
};
}
var splitButtonProps = tslib_1.__assign(tslib_1.__assign({}, splitButtonMenuProps), { styles: classNames, checked: checked, disabled: disabled, allowDisabledFocus: allowDisabledFocus, onClick: this._onMenuClick, menuProps: undefined, iconProps: tslib_1.__assign(tslib_1.__assign({}, menuIconProps), { className: this._classNames.menuIcon }), ariaLabel: splitButtonAriaLabel, 'aria-haspopup': true, 'aria-expanded': !menuHidden, 'data-is-focusable': false });
// Add data-ktp-execute-target to the split button if the keytip is defined
return (React.createElement(BaseButton, tslib_1.__assign({}, splitButtonProps, { "data-ktp-execute-target": keytipAttributes ? keytipAttributes['data-ktp-execute-target'] : keytipAttributes, onMouseDown: this._onMouseDown, tabIndex: primaryDisabled && !allowDisabledFocus ? 0 : -1 })));
};
BaseButton.prototype._onPointerDown = function (ev) {
var onPointerDown = this.props.onPointerDown;
if (onPointerDown) {
onPointerDown(ev);
}
if (ev.pointerType === 'touch') {
this._handleTouchAndPointerEvent();
ev.preventDefault();
ev.stopImmediatePropagation();
}
};
BaseButton.prototype._handleTouchAndPointerEvent = function () {
var _this = this;
// If we already have an existing timeout from a previous touch and pointer event
// cancel that timeout so we can set a new one.
if (this._lastTouchTimeoutId !== undefined) {
this._async.clearTimeout(this._lastTouchTimeoutId);
this._lastTouchTimeoutId = undefined;
}
this._processingTouch = true;
this._lastTouchTimeoutId = this._async.setTimeout(function () {
_this._processingTouch = false;
_this._lastTouchTimeoutId = undefined;
// Touch and pointer events don't focus the button naturally,
// so adding an imperative focus call to guarantee this behavior.
_this.focus();
}, TouchIdleDelay);
};
/**
* Returns if the user hits a valid keyboard key to open the menu
* @param ev - the keyboard event
* @returns True if user clicks on custom trigger key if enabled or alt + down arrow if not. False otherwise.
*/
BaseButton.prototype._isValidMenuOpenKey = function (ev) {
if (this.props.menuTriggerKeyCode) {
return ev.which === this.props.menuTriggerKeyCode;
}
else if (this.props.menuProps) {
return ev.which === Utilities_1.KeyCodes.down && (ev.altKey || ev.metaKey);
}
// Note: When enter is pressed, we will let the event continue to propagate
// to trigger the onClick event on the button
return false;
};
BaseButton.defaultProps = {
baseClassName: 'ms-Button',
styles: {},
split: false,
};
return BaseButton;
}(React.Component));
exports.BaseButton = BaseButton;
});
//# sourceMappingURL=BaseButton.js.map