123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132 |
- import {Children, cloneElement, isValidElement} from 'react';
- import {css} from '@emotion/react';
- import styled from '@emotion/styled';
- import classNames from 'classnames';
- import {ButtonProps, StyledButton} from 'sentry/components/button';
- import space, {ValidSize} from 'sentry/styles/space';
- type ButtonBarProps = {
- children: React.ReactNode;
- active?: ButtonProps['barId'];
- className?: string;
- gap?: ValidSize;
- merged?: boolean;
- };
- function ButtonBar({
- children,
- className,
- active,
- merged = false,
- gap = 0,
- }: ButtonBarProps) {
- const shouldCheckActive = typeof active !== 'undefined';
- return (
- <ButtonGrid merged={merged} gap={gap} className={className}>
- {!shouldCheckActive
- ? children
- : Children.map(children, child => {
- if (!isValidElement(child)) {
- return child;
- }
- const {props: childProps, ...childWithoutProps} = child;
- // We do not want to pass `barId` to <Button>`
- const {barId, ...props} = childProps;
- const isActive = active === barId;
- // This ["primary"] could be customizable with a prop,
- // but let's just enforce one "active" type for now
- const priority = isActive ? 'primary' : childProps.priority || 'default';
- return cloneElement(childWithoutProps as React.ReactElement, {
- ...props,
- className: classNames(className, {active: isActive}),
- priority,
- });
- })}
- </ButtonGrid>
- );
- }
- const MergedStyles = () => css`
- /* Raised buttons show borders on both sides. Useful to create pill bars */
- & > .active {
- z-index: 2;
- }
- & > .dropdown,
- & > button,
- & > input,
- & > a {
- position: relative;
- /* First button is square on the right side */
- &:first-child:not(:last-child) {
- border-top-right-radius: 0;
- border-bottom-right-radius: 0;
- & > .dropdown-actor > ${StyledButton} {
- border-top-right-radius: 0;
- border-bottom-right-radius: 0;
- }
- }
- /* Middle buttons are square */
- &:not(:last-child):not(:first-child) {
- border-radius: 0;
- & > .dropdown-actor > ${StyledButton} {
- border-radius: 0;
- }
- }
- /* Middle buttons only need one border so we don't get a double line */
- &:first-child {
- & + .dropdown:not(:last-child),
- & + a:not(:last-child),
- & + input:not(:last-child),
- & + button:not(:last-child) {
- margin-left: -1px;
- }
- }
- /* Middle buttons only need one border so we don't get a double line */
- /* stylelint-disable-next-line no-duplicate-selectors */
- &:not(:last-child):not(:first-child) {
- & + .dropdown,
- & + button,
- & + input,
- & + a {
- margin-left: -1px;
- }
- }
- /* Last button is square on the left side */
- &:last-child:not(:first-child) {
- border-top-left-radius: 0;
- border-bottom-left-radius: 0;
- margin-left: -1px;
- & > .dropdown-actor > ${StyledButton} {
- border-top-left-radius: 0;
- border-bottom-left-radius: 0;
- margin-left: -1px;
- }
- }
- }
- `;
- const ButtonGrid = styled('div')<{gap: ValidSize; merged: boolean}>`
- display: grid;
- grid-auto-flow: column;
- grid-column-gap: ${p => space(p.gap)};
- align-items: center;
- ${p => p.merged && MergedStyles}
- `;
- export {ButtonGrid, MergedStyles};
- export default ButtonBar;
|