chore: initial Untitled UI Vite scaffold with FontAwesome Pro

This commit is contained in:
2026-03-16 14:23:23 +05:30
commit 3a338b33dd
163 changed files with 27081 additions and 0 deletions

View 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>
);
};