mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage
synced 2026-04-12 02:38:15 +00:00
chore: initial Untitled UI Vite scaffold with FontAwesome Pro
This commit is contained in:
104
src/components/base/button-group/button-group.tsx
Normal file
104
src/components/base/button-group/button-group.tsx
Normal file
@@ -0,0 +1,104 @@
|
||||
import { type FC, type PropsWithChildren, type ReactNode, type RefAttributes, createContext, isValidElement, useContext } from "react";
|
||||
import {
|
||||
ToggleButton as AriaToggleButton,
|
||||
ToggleButtonGroup as AriaToggleButtonGroup,
|
||||
type ToggleButtonGroupProps,
|
||||
type ToggleButtonProps,
|
||||
} from "react-aria-components";
|
||||
import { cx, sortCx } from "@/utils/cx";
|
||||
import { isReactComponent } from "@/utils/is-react-component";
|
||||
|
||||
export const styles = sortCx({
|
||||
common: {
|
||||
root: [
|
||||
"group/button-group inline-flex h-max cursor-pointer items-center bg-primary font-semibold whitespace-nowrap text-secondary shadow-skeumorphic ring-1 ring-primary outline-brand transition duration-100 ease-linear ring-inset",
|
||||
// Hover and focus styles
|
||||
"hover:bg-primary_hover hover:text-secondary_hover focus-visible:z-10 focus-visible:outline-2 focus-visible:outline-offset-2",
|
||||
// Disabled styles
|
||||
"disabled:cursor-not-allowed disabled:bg-primary disabled:text-disabled",
|
||||
// Selected styles
|
||||
"selected:bg-active selected:text-secondary_hover selected:disabled:bg-disabled_subtle",
|
||||
].join(" "),
|
||||
icon: "pointer-events-none text-fg-quaternary transition-[inherit] group-hover/button-group:text-fg-quaternary_hover group-disabled/button-group:text-fg-disabled_subtle",
|
||||
},
|
||||
|
||||
sizes: {
|
||||
sm: {
|
||||
root: "gap-1.5 px-3.5 py-2 text-sm not-last:pr-[calc(calc(var(--spacing)*3.5)+1px)] first:rounded-l-lg last:rounded-r-lg data-icon-leading:pl-3 data-icon-only:p-2",
|
||||
icon: "size-5",
|
||||
},
|
||||
md: {
|
||||
root: "gap-1.5 px-4 py-2.5 text-sm not-last:pr-[calc(calc(var(--spacing)*4)+1px)] first:rounded-l-lg last:rounded-r-lg data-icon-leading:pl-3.5 data-icon-only:px-3",
|
||||
icon: "size-5",
|
||||
},
|
||||
lg: {
|
||||
root: "gap-2 px-4.5 py-2.5 text-md not-last:pr-[calc(calc(var(--spacing)*4.5)+1px)] first:rounded-l-lg last:rounded-r-lg data-icon-leading:pl-4 data-icon-only:p-3",
|
||||
icon: "size-5",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
type ButtonSize = keyof typeof styles.sizes;
|
||||
|
||||
const ButtonGroupContext = createContext<{ size: ButtonSize }>({ size: "md" });
|
||||
|
||||
interface ButtonGroupItemProps extends ToggleButtonProps, RefAttributes<HTMLButtonElement> {
|
||||
iconLeading?: FC<{ className?: string }> | ReactNode;
|
||||
iconTrailing?: FC<{ className?: string }> | ReactNode;
|
||||
onClick?: () => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const ButtonGroupItem = ({
|
||||
iconLeading: IconLeading,
|
||||
iconTrailing: IconTrailing,
|
||||
children,
|
||||
className,
|
||||
...otherProps
|
||||
}: PropsWithChildren<ButtonGroupItemProps>) => {
|
||||
const context = useContext(ButtonGroupContext);
|
||||
|
||||
if (!context) {
|
||||
throw new Error("ButtonGroupItem must be used within a ButtonGroup component");
|
||||
}
|
||||
|
||||
const { size } = context;
|
||||
|
||||
const isIcon = (IconLeading || IconTrailing) && !children;
|
||||
|
||||
return (
|
||||
<AriaToggleButton
|
||||
{...otherProps}
|
||||
data-icon-only={isIcon ? true : undefined}
|
||||
data-icon-leading={IconLeading ? true : undefined}
|
||||
className={cx(styles.common.root, styles.sizes[size].root, className)}
|
||||
>
|
||||
{isReactComponent(IconLeading) && <IconLeading className={cx(styles.common.icon, styles.sizes[size].icon)} />}
|
||||
{isValidElement(IconLeading) && IconLeading}
|
||||
|
||||
{children}
|
||||
|
||||
{isReactComponent(IconTrailing) && <IconTrailing className={cx(styles.common.icon, styles.sizes[size].icon)} />}
|
||||
{isValidElement(IconTrailing) && IconTrailing}
|
||||
</AriaToggleButton>
|
||||
);
|
||||
};
|
||||
|
||||
interface ButtonGroupProps extends Omit<ToggleButtonGroupProps, "orientation">, RefAttributes<HTMLDivElement> {
|
||||
size?: ButtonSize;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const ButtonGroup = ({ children, size = "md", className, ...otherProps }: ButtonGroupProps) => {
|
||||
return (
|
||||
<ButtonGroupContext.Provider value={{ size }}>
|
||||
<AriaToggleButtonGroup
|
||||
selectionMode="single"
|
||||
className={cx("relative z-0 inline-flex w-max -space-x-px rounded-lg shadow-xs", className)}
|
||||
{...otherProps}
|
||||
>
|
||||
{children}
|
||||
</AriaToggleButtonGroup>
|
||||
</ButtonGroupContext.Provider>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user