azure-devops-ui
Version:
React components for building web UI in Azure DevOps
134 lines (133 loc) • 7.2 kB
JavaScript
import "../../CommonImports";
import "../../Core/core.css";
import "./Pill.css";
import * as React from "react";
import { format } from '../../Core/Util/String';
import { Button } from '../../Button';
import { FocusZoneContext } from '../../FocusZone';
import { Icon, IconSize } from '../../Icon';
import * as Resources from '../../Resources.Widgets';
import { css, getSafeId, KeyCode } from '../../Util';
import { darken, getColorString, isDark } from '../../Utilities/Color';
import { getTabIndex } from '../../Utilities/Focus';
import { PillSize, PillVariant } from "./Pill.Props";
export class Pill extends React.Component {
constructor(props) {
super(props);
this.getChildText = () => {
let text = "";
React.Children.map(this.props.children, child => {
if (typeof child === "string") {
text += child;
}
});
return text;
};
this.onKeyDown = (event) => {
const keyCode = event.which;
if (keyCode === KeyCode.enter) {
this.props.onClick && this.props.onClick();
}
};
this.onMouseEnter = (event) => {
this.props.onMouseEnter && this.props.onMouseEnter(event);
this.setState({
isHoveringPrimaryElement: true
});
};
/**
* onMouseLeaveButton fires first; if it leaves the container too
* onMouseLeave will setState again, which will prevent weird behavior
*/
this.onMouseLeave = (event) => {
this.props.onMouseLeave && this.props.onMouseLeave(event);
this.setState({
isHoveringPrimaryElement: false
});
};
this.onMouseLeaveButton = () => {
this.setState({
isHoveringPrimaryElement: true
});
};
this.onMouseOverButton = () => {
this.setState({
isHoveringPrimaryElement: false
});
};
this.state = {
isHoveringPrimaryElement: false
};
}
static getColorStyle(color, isHoveringPrimaryElement, onClick) {
if (!color) {
return undefined;
}
const renderColor = onClick && isHoveringPrimaryElement ? darken(color, 0.06) : color;
return { backgroundColor: getColorString(renderColor) };
}
static getSizeClass(size) {
switch (size) {
case PillSize.compact:
return "compact";
case PillSize.large:
return "large";
case PillSize.regular:
default:
return "regular";
}
}
static getVariantClass(variant, color) {
switch (variant) {
case PillVariant.outlined:
return "outlined";
case PillVariant.colored:
if (color) {
return css("colored", isDark(color) ? "dark" : "light");
}
else {
return "standard";
}
case PillVariant.themedStandard:
return "themed-standard";
case PillVariant.standard:
default:
return "standard";
}
}
static getDerivedStateFromProps(props, state) {
if (false) {
const { color, iconProps, onRenderFilledVisual, size = PillSize.regular, variant } = props;
// Checking for unsupported compact fields and warning if there are any
if (size === PillSize.compact) {
const unsupportedFields = [];
onRenderFilledVisual && unsupportedFields.push("onRenderFilledVisual");
if (unsupportedFields.length > 0) {
console.warn(`Pill Size is Compact, but the following fields were provided: ${unsupportedFields.join(", ")} - these will be ignored. Consider changing Pill Size to Regular or Large if you need to support these items`);
}
}
else {
if (onRenderFilledVisual && iconProps) {
console.warn("onRenderFilledVisual and iconProps have both been supplied; using onRenderFilledVisual");
}
}
if (variant === PillVariant.colored && !color) {
console.warn("Pill Variant is set to Colored, but not color was provided - Pill will render as Standard");
}
else if (color && variant !== PillVariant.colored) {
console.warn("Color was provided, but Pill Variant is not set to Colored - Pill will render as whatever variant was provided");
}
}
return state;
}
render() {
const { ariaHidden, contentClassName, className, color, containsCount = false, iconProps, id, isListItem, onClick, onBlur, onFocus, onRemoveClick, onRenderFilledVisual, size = PillSize.regular, variant = PillVariant.standard } = this.props;
const { isHoveringPrimaryElement } = this.state;
const ariaLabel = this.props.ariaLabel || this.getChildText();
return (React.createElement(FocusZoneContext.Consumer, null, (zoneContext) => (React.createElement("div", { className: css(className, "bolt-pill flex-row flex-center", Pill.getVariantClass(variant, color), Pill.getSizeClass(size), containsCount && "count", isHoveringPrimaryElement && "hover", onClick && "clickable", onRenderFilledVisual && "has-filled-visual", iconProps && !onRenderFilledVisual && "has-icon", onRemoveClick && "has-remove-button"), id: getSafeId(id), onClick: onClick, onBlur: onBlur, onMouseEnter: this.onMouseEnter, onMouseLeave: this.onMouseLeave, style: Pill.getColorStyle(color, isHoveringPrimaryElement, onClick), role: isListItem ? "listitem" : undefined },
onRenderFilledVisual && React.createElement("div", { className: "bolt-pill-filled-visual flex-noshrink" }, onRenderFilledVisual()),
iconProps && !onRenderFilledVisual && React.createElement(Icon, Object.assign({}, iconProps, { className: css(iconProps.className, "bolt-pill-icon") })),
React.createElement("div", { "aria-label": ariaLabel, "aria-hidden": ariaHidden, className: css(contentClassName, "bolt-pill-content text-ellipsis"), "data-focuszone": !this.props.excludeFocusZone ? zoneContext.focuszoneId : undefined, onFocus: onFocus, onKeyDown: this.onKeyDown, role: this.props.role || (onClick ? "button" : "presentation"), tabIndex: onClick || onFocus ? getTabIndex(this.props) : undefined }, this.props.children),
onRemoveClick && (React.createElement(Button, { ariaLabel: format(Resources.RemovePillLabel, ariaLabel), className: "bolt-pill-button", iconProps: { iconName: "Cancel", size: IconSize.inherit }, onClick: onRemoveClick, onMouseLeave: this.onMouseLeaveButton, onMouseOver: this.onMouseOverButton, subtle: true, tooltipProps: { text: format(Resources.RemovePillLabel, ariaLabel) }, tabIndex: this.props.removeButtonTabIndex }))))));
}
}