@dotconnor/grommet
Version:
focus on the essential experience
344 lines (290 loc) • 12.6 kB
JavaScript
"use strict";
exports.__esModule = true;
exports.Button = void 0;
var _react = _interopRequireWildcard(require("react"));
var _styledComponents = require("styled-components");
var _utils = require("../../utils");
var _defaultProps = require("../../default-props");
var _Box = require("../Box");
var _Tip = require("../Tip");
var _StyledButton = require("./StyledButton");
var _StyledButtonKind = require("./StyledButtonKind");
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; }
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
// We have two Styled* components to separate
// the newer default|primary|secondary approach,
// which we use the term "kind" to refer to,
// from the previous approach. Hopefully, when we get to grommet v3,
// we can drop the old way and just use kind.
//
// In the kind approach, we rely on the basic structure of the theme
// being repeated. For example: button.default, button.active,
// button.active.default all refer to a similar object containing
// { background, border, color, padding }.
// This allows us to use the same code to evaluate color and generate CSS.
// We just need to build up CSS, since selectors override previous ones.
// See StyledButtonKind.kindStyles() for this.
// And we build down to determine icon color, once we have a color from
// the latest applicable state, we can stop. See Button.getIconColor() for this.
// backgroundAndTextColor() is used in both cases to ensure we are determining
// color in the same way, so the icon and label align.
// only when default is in the theme
// Used to get the color for the icon to match what StyledButtonKind
// and backgroundStyle() will do for the label.
// The paths are ordered from basic to specific. Go through them
// specific to base until we find one that has a color and use that.
var getIconColor = function getIconColor(paths, theme, colorProp) {
if (paths === void 0) {
paths = [];
}
var result = [];
var index = paths.length - 1; // stop when we have a color or no more paths
while (index >= 0 && !result[1]) {
var obj = theme.button; // find the sub-object under the button them that corresponds with this path
// for example: 'active.primary'
if (paths[index]) {
var parts = paths[index].split('.');
while (obj && parts.length) {
obj = obj[parts.shift()];
}
}
if (obj) {
// use passed in color for background if the theme has a background color
var background = colorProp && obj.background && obj.background.color ? colorProp : obj.background; // if theme object explicitly sets the color to undefined, pass false
// to indicate that the theme doesn't want any text color
var objColor = obj.color || (Object.prototype.hasOwnProperty.call(obj, 'color') && obj.color === undefined ? false : undefined); // use passed in color for text if the theme doesn't have
// background or border color
var color = colorProp && (!obj.background || !obj.background.color) && (!obj.border || !obj.border.color) ? colorProp : objColor;
result = (0, _utils.backgroundAndTextColors)(background, color, theme);
}
index -= 1;
}
return result[1] || undefined;
};
var Button = /*#__PURE__*/(0, _react.forwardRef)(function (_ref, ref) {
var a11yTitle = _ref.a11yTitle,
active = _ref.active,
_ref$align = _ref.align,
align = _ref$align === void 0 ? 'center' : _ref$align,
color = _ref.color,
children = _ref.children,
disabled = _ref.disabled,
icon = _ref.icon,
_ref$focusIndicator = _ref.focusIndicator,
focusIndicator = _ref$focusIndicator === void 0 ? true : _ref$focusIndicator,
_ref$gap = _ref.gap,
gap = _ref$gap === void 0 ? 'small' : _ref$gap,
fill = _ref.fill,
href = _ref.href,
kindArg = _ref.kind,
label = _ref.label,
_onBlur = _ref.onBlur,
onClick = _ref.onClick,
_onFocus = _ref.onFocus,
onMouseOut = _ref.onMouseOut,
onMouseOver = _ref.onMouseOver,
plain = _ref.plain,
primary = _ref.primary,
reverse = _ref.reverse,
secondary = _ref.secondary,
selected = _ref.selected,
size = _ref.size,
tip = _ref.tip,
_ref$type = _ref.type,
type = _ref$type === void 0 ? 'button' : _ref$type,
as = _ref.as,
rest = _objectWithoutPropertiesLoose(_ref, ["a11yTitle", "active", "align", "color", "children", "disabled", "icon", "focusIndicator", "gap", "fill", "href", "kind", "label", "onBlur", "onClick", "onFocus", "onMouseOut", "onMouseOver", "plain", "primary", "reverse", "secondary", "selected", "size", "tip", "type", "as"]);
var theme = (0, _react.useContext)(_styledComponents.ThemeContext) || _defaultProps.defaultProps.theme;
var _useState = (0, _react.useState)(),
focus = _useState[0],
setFocus = _useState[1];
var _useState2 = (0, _react.useState)(false),
hover = _useState2[0],
setHover = _useState2[1];
if ((icon || label) && children) {
console.warn('Button should not have children if icon or label is provided');
} // if the theme has button.default, what kind of Button is this
var kind = (0, _react.useMemo)(function () {
if (theme.button["default"]) {
if (kindArg) return kindArg;
if (primary) return 'primary';
if (secondary) return 'secondary';
return 'default';
}
return undefined; // pre-default, no kind
}, [kindArg, primary, secondary, theme.button["default"]]); // When we have a kind and are not plain, themePaths stores the relative
// paths within the theme for the current kind and state of the button.
// These paths are used with getIconColor() above and kindStyle() within
// StyledButtonKind.
var themePaths = (0, _react.useMemo)(function () {
if (!kind || plain) return undefined;
var result = {
base: [],
hover: []
};
result.base.push(kind);
if (selected) {
result.base.push('selected');
if (kind) result.base.push("selected." + kind);
}
if (disabled) {
result.base.push('disabled');
if (kind) result.base.push("disabled." + kind);
} else {
if (active) {
result.base.push('active');
if (kind) result.base.push("active." + kind);
}
result.hover.push('hover');
if (kind) result.hover.push("hover." + kind);
if (active) {
result.hover.push("hover.active");
if (kind) {
result.hover.push("hover.active." + kind);
}
}
}
return result;
}, [active, disabled, kind, plain, selected]); // only used when theme does not have button.default
var isDarkBackground = function isDarkBackground() {
var backgroundColor = (0, _utils.normalizeBackground)((0, _utils.normalizeColor)(color || theme.button.primary && theme.button.primary.color || theme.global.colors.control || 'brand', theme), theme);
return (0, _utils.colorIsDark)(backgroundColor, theme);
};
var onMouseOverButton = function onMouseOverButton(event) {
setHover(true);
if (onMouseOver) {
onMouseOver(event);
}
};
var onMouseOutButton = function onMouseOutButton(event) {
setHover(false);
if (onMouseOut) {
onMouseOut(event);
}
};
var buttonIcon = icon; // only change color if user did not specify the color themselves...
if (icon && !icon.props.color) {
if (kind) {
if (!plain) {
// match what the label will use
var iconColor = hover && getIconColor(themePaths.hover, theme) || getIconColor(themePaths.base, theme, color);
if (iconColor) buttonIcon = /*#__PURE__*/(0, _react.cloneElement)(icon, {
color: iconColor
});
}
} else if (primary) {
buttonIcon = /*#__PURE__*/(0, _react.cloneElement)(icon, {
color: theme.global.colors.text[isDarkBackground() ? 'dark' : 'light']
});
}
}
var domTag = !as && href ? 'a' : as;
var first = reverse ? label : buttonIcon;
var second = reverse ? buttonIcon : label;
var contents;
if (first && second) {
contents = /*#__PURE__*/_react["default"].createElement(_Box.Box, {
direction: "row",
align: "center",
justify: align === 'center' ? 'center' : 'between',
gap: gap,
responsive: false
}, first, second);
} else if (typeof children === 'function') {
contents = children({
disabled: disabled,
hover: hover,
focus: focus
});
} else {
contents = first || second || children;
}
var styledButtonResult;
if (kind) {
styledButtonResult = /*#__PURE__*/_react["default"].createElement(_StyledButtonKind.StyledButtonKind, _extends({}, rest, {
as: domTag,
ref: ref,
active: active,
align: align,
"aria-label": a11yTitle,
colorValue: color,
disabled: disabled,
gap: gap,
fillContainer: fill,
focus: focus,
focusIndicator: focusIndicator,
href: href,
kind: kind,
themePaths: themePaths,
onClick: onClick,
onFocus: function onFocus(event) {
setFocus(true);
if (_onFocus) _onFocus(event);
},
onBlur: function onBlur(event) {
setFocus(false);
if (_onBlur) _onBlur(event);
},
onMouseOver: onMouseOverButton,
onMouseOut: onMouseOutButton,
plain: plain || _react.Children.count(children) > 0,
primary: primary,
sizeProp: size,
type: !href ? type : undefined
}), contents);
} else {
styledButtonResult = /*#__PURE__*/_react["default"].createElement(_StyledButton.StyledButton, _extends({}, rest, {
as: domTag,
ref: ref,
"aria-label": a11yTitle,
colorValue: color,
active: active,
selected: selected,
disabled: disabled,
hasIcon: !!icon,
gap: gap,
hasLabel: !!label,
fillContainer: fill,
focus: focus,
focusIndicator: focusIndicator,
href: href,
kind: kind,
themePaths: themePaths,
onClick: onClick,
onFocus: function onFocus(event) {
setFocus(true);
if (_onFocus) _onFocus(event);
},
onBlur: function onBlur(event) {
setFocus(false);
if (_onBlur) _onBlur(event);
},
onMouseOver: onMouseOverButton,
onMouseOut: onMouseOutButton,
pad: !plain,
plain: typeof plain !== 'undefined' ? plain : _react.Children.count(children) > 0 || icon && !label,
primary: primary,
sizeProp: size,
type: !href ? type : undefined
}), contents);
}
if (tip) {
if (typeof tip === 'string') {
return /*#__PURE__*/_react["default"].createElement(_Tip.Tip, {
content: tip
}, styledButtonResult);
}
return /*#__PURE__*/_react["default"].createElement(_Tip.Tip, tip, styledButtonResult);
}
return styledButtonResult;
});
Button.displayName = 'Button';
var ButtonDoc;
if (process.env.NODE_ENV !== 'production') {
// eslint-disable-next-line global-require
ButtonDoc = require('./doc').doc(Button);
}
var ButtonWrapper = ButtonDoc || Button;
exports.Button = ButtonWrapper;