112 lines
2.9 KiB
TypeScript
Executable File
112 lines
2.9 KiB
TypeScript
Executable File
import React from 'react';
|
|
import clsx from 'clsx';
|
|
import {ButtonColor, ButtonVariant} from './get-shared-button-style';
|
|
import {ButtonProps} from './button';
|
|
import {ButtonSize} from './button-size';
|
|
|
|
export interface ButtonGroupProps {
|
|
children: React.ReactNode[];
|
|
color?: ButtonColor;
|
|
variant?: ButtonVariant;
|
|
size?: ButtonSize;
|
|
radius?: string;
|
|
className?: string;
|
|
value?: any;
|
|
onChange?: (newValue: any) => void;
|
|
multiple?: boolean;
|
|
disabled?: boolean;
|
|
}
|
|
export function ButtonGroup({
|
|
children,
|
|
color,
|
|
variant,
|
|
radius = 'rounded-button',
|
|
size,
|
|
className,
|
|
value,
|
|
onChange,
|
|
multiple,
|
|
disabled,
|
|
}: ButtonGroupProps) {
|
|
const isActive = (childValue: any): boolean => {
|
|
// assume that button group is not used as a toggle group, if there is no value given
|
|
if (value === undefined) return false;
|
|
if (multiple) {
|
|
return (value as any[]).includes(childValue);
|
|
}
|
|
return childValue === value;
|
|
};
|
|
|
|
const toggleMultipleValue = (childValue: any) => {
|
|
const newValue = [...value];
|
|
const childIndex = value.indexOf(childValue);
|
|
if (childIndex > -1) {
|
|
newValue.splice(childIndex, 1);
|
|
} else {
|
|
newValue.push(childValue);
|
|
}
|
|
return newValue;
|
|
};
|
|
|
|
const buttons = React.Children.map(children, (button, i) => {
|
|
if (React.isValidElement(button)) {
|
|
const active = isActive(button.props.value);
|
|
const adjustedColor = active ? 'primary' : color;
|
|
return React.cloneElement<ButtonProps>(button as any, {
|
|
color: active ? 'primary' : color,
|
|
variant,
|
|
size,
|
|
radius: null,
|
|
disabled: button.props.disabled || disabled,
|
|
...button.props,
|
|
onClick: e => {
|
|
if (button.props.onClick) {
|
|
button.props.onClick(e);
|
|
}
|
|
if (!onChange) return;
|
|
if (multiple) {
|
|
onChange?.(toggleMultipleValue(button.props.value));
|
|
} else {
|
|
onChange?.(button.props.value);
|
|
}
|
|
},
|
|
className: clsx(
|
|
button.props.className,
|
|
// borders are hidden via negative margin, make sure both are visible for active item
|
|
active ? 'z-20' : 'z-10',
|
|
getStyle(i, children, radius, adjustedColor),
|
|
),
|
|
});
|
|
}
|
|
});
|
|
return (
|
|
<div className={clsx(radius, 'isolate inline-flex', className)}>
|
|
{buttons}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function getStyle(
|
|
i: number,
|
|
children: ButtonGroupProps['children'],
|
|
radius: ButtonGroupProps['radius'],
|
|
color?: ButtonColor,
|
|
): string {
|
|
// first
|
|
if (i === 0) {
|
|
return clsx(
|
|
radius,
|
|
'rounded-tr-none rounded-br-none',
|
|
!color && 'border-r-transparent disabled:border-r-transparent',
|
|
);
|
|
}
|
|
// last
|
|
if (i === children.length - 1) {
|
|
return clsx(radius, 'rounded-tl-none rounded-bl-none -ml-1');
|
|
}
|
|
return clsx(
|
|
'rounded-none -ml-1',
|
|
!color && 'border-r-transparent disabled:border-r-transparent',
|
|
);
|
|
}
|