mirror of
https://github.com/lukevella/rallly.git
synced 2025-06-06 04:31:50 +02:00
🎨 Update UI package (#1070)
Add new components and update various existing ones.
This commit is contained in:
parent
a4ffbee081
commit
e6792a4283
33 changed files with 386 additions and 56 deletions
|
@ -63,6 +63,7 @@ export const PollDetailsForm = () => {
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
id="location"
|
id="location"
|
||||||
|
className="w-full"
|
||||||
placeholder={t("locationPlaceholder")}
|
placeholder={t("locationPlaceholder")}
|
||||||
{...register("location")}
|
{...register("location")}
|
||||||
/>
|
/>
|
||||||
|
@ -77,6 +78,7 @@ export const PollDetailsForm = () => {
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<Textarea
|
<Textarea
|
||||||
|
className="w-full"
|
||||||
id="description"
|
id="description"
|
||||||
placeholder={t("descriptionPlaceholder")}
|
placeholder={t("descriptionPlaceholder")}
|
||||||
rows={5}
|
rows={5}
|
||||||
|
|
|
@ -49,7 +49,7 @@ const DateTimePreferencesForm = () => {
|
||||||
form.reset(data);
|
form.reset(data);
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<div className="space-y-4">
|
<div className="space-y-6">
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="timeZone"
|
name="timeZone"
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { Tooltip, TooltipContent, TooltipTrigger } from "@rallly/ui/tooltip";
|
||||||
import { InfoIcon } from "lucide-react";
|
import { InfoIcon } from "lucide-react";
|
||||||
|
|
||||||
export const Settings = ({ children }: React.PropsWithChildren) => {
|
export const Settings = ({ children }: React.PropsWithChildren) => {
|
||||||
return <div className="">{children}</div>;
|
return <div className="space-y-6">{children}</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SettingsHeader = ({ children }: React.PropsWithChildren) => {
|
export const SettingsHeader = ({ children }: React.PropsWithChildren) => {
|
||||||
|
@ -51,7 +51,7 @@ export const SettingsItemTitle = ({
|
||||||
{hint ? (
|
{hint ? (
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<InfoIcon className="size-4 inline-block text-gray-500" />
|
<InfoIcon className="inline-block size-4 text-gray-500" />
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent side="right" className="max-w-sm">
|
<TooltipContent side="right" className="max-w-sm">
|
||||||
{hint}
|
{hint}
|
||||||
|
|
4
packages/ui/custom.d.ts
vendored
Normal file
4
packages/ui/custom.d.ts
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
declare module "*.svg" {
|
||||||
|
const content: React.FunctionComponent<React.SVGAttributes<SVGElement>>;
|
||||||
|
export default content;
|
||||||
|
}
|
|
@ -4,7 +4,7 @@ import * as AccordionPrimitive from "@radix-ui/react-accordion";
|
||||||
import { ChevronDown } from "lucide-react";
|
import { ChevronDown } from "lucide-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { cn } from "@rallly/ui";
|
import { cn } from "./lib/utils";
|
||||||
|
|
||||||
const Accordion = AccordionPrimitive.Root;
|
const Accordion = AccordionPrimitive.Root;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { type VariantProps, cva } from "class-variance-authority";
|
import { type VariantProps, cva } from "class-variance-authority";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { cn } from "@rallly/ui";
|
import { cn } from "./lib/utils";
|
||||||
|
|
||||||
const alertVariants = cva(
|
const alertVariants = cva(
|
||||||
"flex sm:flex-row flex-col gap-x-3 gap-y-2 rounded-md border p-4",
|
"flex sm:flex-row flex-col gap-x-3 gap-y-2 rounded-md border p-4",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { type VariantProps, cva } from "class-variance-authority";
|
import { type VariantProps, cva } from "class-variance-authority";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { cn } from "@rallly/ui";
|
import { cn } from "./lib/utils";
|
||||||
|
|
||||||
const badgeVariants = cva(
|
const badgeVariants = cva(
|
||||||
"inline-flex items-center rounded-full border px-2 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
"inline-flex items-center rounded-full border px-2 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { CheckCircle2Icon } from "lucide-react";
|
import { CheckCircle2Icon } from "lucide-react";
|
||||||
|
|
||||||
import { cn } from "@rallly/ui";
|
import { cn } from "./lib/utils";
|
||||||
|
|
||||||
export const BillingPlan = ({
|
export const BillingPlan = ({
|
||||||
children,
|
children,
|
||||||
|
|
|
@ -4,22 +4,22 @@ import { Loader2Icon } from "lucide-react";
|
||||||
import { cva, VariantProps } from "class-variance-authority";
|
import { cva, VariantProps } from "class-variance-authority";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { cn } from "@rallly/ui";
|
import { cn } from "./lib/utils";
|
||||||
|
|
||||||
const buttonVariants = cva(
|
const buttonVariants = cva(
|
||||||
cn(
|
cn(
|
||||||
"inline-flex border font-medium disabled:text-muted-foreground disabled:bg-muted disabled:pointer-events-none select-none items-center justify-center whitespace-nowrap rounded-md border",
|
"inline-flex border font-medium disabled:pointer-events-none select-none disabled:opacity-50 items-center justify-center whitespace-nowrap rounded-md border",
|
||||||
"focus-visible:ring-offset-input-background focus-visible:border-primary-400 focus-visible:ring-2 focus-visible:ring-indigo-100",
|
"focus:ring-offset-input-background focus:border-primary-400 focus:ring-2 focus:ring-indigo-100",
|
||||||
),
|
),
|
||||||
{
|
{
|
||||||
variants: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
primary:
|
primary:
|
||||||
"border-transparent bg-primary text-white focus:ring-offset-1 shadow-sm hover:bg-primary-500 active:bg-primary-700",
|
"border-primary-700 bg-primary disabled:bg-gray-400 disabled:border-transparent text-primary-foreground shadow-sm hover:bg-primary-500 active:bg-primary-700",
|
||||||
destructive:
|
destructive:
|
||||||
"bg-destructive text-destructive-foreground focus:ring-offset-1 active:bg-destructive border-destructive hover:bg-destructive/90",
|
"bg-destructive text-destructive-foreground focus:ring-offset-1 active:bg-destructive border-destructive hover:bg-destructive/90",
|
||||||
default:
|
default:
|
||||||
"rounded-md px-3.5 py-2.5 data-[state=open]:shadow-none data-[state=open]:bg-gray-100 active:bg-gray-200 focus:border-gray-300 hover:bg-gray-100 bg-gray-50",
|
"rounded-md px-3.5 py-2.5 data-[state=open]:shadow-none data-[state=open]:bg-gray-100 active:bg-gray-200 hover:bg-gray-100 bg-gray-50",
|
||||||
secondary:
|
secondary:
|
||||||
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||||
ghost: "border-transparent hover:bg-gray-200 active:bg-gray-300",
|
ghost: "border-transparent hover:bg-gray-200 active:bg-gray-300",
|
||||||
|
@ -27,7 +27,7 @@ const buttonVariants = cva(
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
default: "h-9 px-2.5 gap-x-2.5 text-sm",
|
default: "h-9 px-2.5 gap-x-2.5 text-sm",
|
||||||
sm: "h-7 text-xs px-2 gap-x-1.5 rounded-md",
|
sm: "h-7 text-xs px-1.5 gap-x-1.5 rounded-md",
|
||||||
lg: "h-11 text-base gap-x-3 px-4 rounded-md",
|
lg: "h-11 text-base gap-x-3 px-4 rounded-md",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -65,6 +65,7 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||||
return (
|
return (
|
||||||
<Comp
|
<Comp
|
||||||
className={cn(
|
className={cn(
|
||||||
|
"group",
|
||||||
buttonVariants({ variant, size }),
|
buttonVariants({ variant, size }),
|
||||||
{
|
{
|
||||||
"pointer-events-none": loading,
|
"pointer-events-none": loading,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { cn } from "@rallly/ui";
|
import { cn } from "./lib/utils";
|
||||||
|
|
||||||
const Card = React.forwardRef<
|
const Card = React.forwardRef<
|
||||||
HTMLDivElement,
|
HTMLDivElement,
|
||||||
|
@ -23,7 +23,7 @@ const CardHeader = React.forwardRef<
|
||||||
>(({ className, ...props }, ref) => (
|
>(({ className, ...props }, ref) => (
|
||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn("grid border-b border-gray-100 p-3 sm:p-4", className)}
|
className={cn("grid rounded-t-md border-b p-2 py-3 sm:px-5", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
|
@ -35,10 +35,7 @@ const CardTitle = React.forwardRef<
|
||||||
>(({ className, ...props }, ref) => (
|
>(({ className, ...props }, ref) => (
|
||||||
<h3
|
<h3
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn("font-semibold tracking-tight sm:leading-tight", className)}
|
||||||
"mb-1 font-semibold tracking-tight sm:leading-tight",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
|
@ -50,7 +47,7 @@ const CardDescription = React.forwardRef<
|
||||||
>(({ className, ...props }, ref) => (
|
>(({ className, ...props }, ref) => (
|
||||||
<p
|
<p
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn("text-muted-foreground text-sm", className)}
|
className={cn("text-muted-foreground mt-1 text-sm", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
|
|
3
packages/ui/src/checkbox-check.svg
Normal file
3
packages/ui/src/checkbox-check.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 10 8">
|
||||||
|
<path fill-rule="evenodd" d="M9.33 1.066a1.25 1.25 0 0 1 .104 1.764l-4 4.5a1.25 1.25 0 0 1-1.627.21l-3-2a1.25 1.25 0 0 1 1.386-2.08l2.095 1.397L7.566 1.17a1.25 1.25 0 0 1 1.764-.104Z" clip-rule="evenodd"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 295 B |
|
@ -2,9 +2,8 @@
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
|
import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
|
||||||
import { CheckIcon } from "lucide-react";
|
import CheckboxCheckIcon from "./checkbox-check.svg";
|
||||||
|
import { cn } from "./lib/utils";
|
||||||
import { cn } from "@rallly/ui";
|
|
||||||
|
|
||||||
const Checkbox = React.forwardRef<
|
const Checkbox = React.forwardRef<
|
||||||
React.ElementRef<typeof CheckboxPrimitive.Root>,
|
React.ElementRef<typeof CheckboxPrimitive.Root>,
|
||||||
|
@ -13,7 +12,9 @@ const Checkbox = React.forwardRef<
|
||||||
<CheckboxPrimitive.Root
|
<CheckboxPrimitive.Root
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"border-primary ring-offset-background focus-visible:ring-ring data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground peer h-4 w-4 shrink-0 rounded-sm border focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
"data-[state=checked]:bg-primary data-[state=checked]:border-primary-600 data-[state=checked]:focus:ring-primary-200 data-[state=checked]:focus:ring-2",
|
||||||
|
"focus-visible:ring-gray-100",
|
||||||
|
"peer inline-flex h-5 w-5 shrink-0 items-center justify-center rounded border border-gray-200 bg-gray-50 ring-0 disabled:cursor-not-allowed disabled:opacity-50",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
@ -21,7 +22,7 @@ const Checkbox = React.forwardRef<
|
||||||
<CheckboxPrimitive.Indicator
|
<CheckboxPrimitive.Indicator
|
||||||
className={cn("flex items-center justify-center text-current")}
|
className={cn("flex items-center justify-center text-current")}
|
||||||
>
|
>
|
||||||
<CheckIcon className="h-4 w-4" />
|
<CheckboxCheckIcon className="text-primary-50 size-2.5" />
|
||||||
</CheckboxPrimitive.Indicator>
|
</CheckboxPrimitive.Indicator>
|
||||||
</CheckboxPrimitive.Root>
|
</CheckboxPrimitive.Root>
|
||||||
));
|
));
|
||||||
|
|
|
@ -5,8 +5,8 @@ import { SearchIcon } from "lucide-react";
|
||||||
import { Command as CommandPrimitive } from "cmdk";
|
import { Command as CommandPrimitive } from "cmdk";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { Dialog, DialogContent } from "@rallly/ui/dialog";
|
import { Dialog, DialogContent } from "./dialog";
|
||||||
import { cn } from "@rallly/ui";
|
import { cn } from "./lib/utils";
|
||||||
|
|
||||||
const Command = React.forwardRef<
|
const Command = React.forwardRef<
|
||||||
React.ElementRef<typeof CommandPrimitive>,
|
React.ElementRef<typeof CommandPrimitive>,
|
||||||
|
|
|
@ -4,7 +4,7 @@ import * as DialogPrimitive from "@radix-ui/react-dialog";
|
||||||
import { XIcon } from "lucide-react";
|
import { XIcon } from "lucide-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { cn } from "@rallly/ui";
|
import { cn } from "./lib/utils";
|
||||||
|
|
||||||
const Dialog = DialogPrimitive.Root;
|
const Dialog = DialogPrimitive.Root;
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,8 @@ import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
||||||
import { CheckIcon, ChevronRightIcon, PlusCircleIcon } from "lucide-react";
|
import { CheckIcon, ChevronRightIcon, PlusCircleIcon } from "lucide-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { cn } from "@rallly/ui";
|
import { cn } from "./lib/utils";
|
||||||
|
import { Icon } from "./icon";
|
||||||
|
|
||||||
const DropdownMenu = DropdownMenuPrimitive.Root;
|
const DropdownMenu = DropdownMenuPrimitive.Root;
|
||||||
|
|
||||||
|
@ -83,6 +84,7 @@ const DropdownMenuItem = React.forwardRef<
|
||||||
<DropdownMenuPrimitive.Item
|
<DropdownMenuPrimitive.Item
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
|
"flex items-center gap-x-2.5",
|
||||||
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm font-medium outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm font-medium outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||||
inset && "pl-8",
|
inset && "pl-8",
|
||||||
className,
|
className,
|
||||||
|
@ -107,7 +109,9 @@ const DropdownMenuCheckboxItem = React.forwardRef<
|
||||||
>
|
>
|
||||||
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||||
<DropdownMenuPrimitive.ItemIndicator>
|
<DropdownMenuPrimitive.ItemIndicator>
|
||||||
<CheckIcon className="size-4" />
|
<Icon variant="success">
|
||||||
|
<CheckIcon />
|
||||||
|
</Icon>
|
||||||
</DropdownMenuPrimitive.ItemIndicator>
|
</DropdownMenuPrimitive.ItemIndicator>
|
||||||
</span>
|
</span>
|
||||||
{children}
|
{children}
|
||||||
|
@ -123,7 +127,7 @@ const DropdownMenuRadioItem = React.forwardRef<
|
||||||
<DropdownMenuPrimitive.RadioItem
|
<DropdownMenuPrimitive.RadioItem
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm font-medium outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
75
packages/ui/src/flex.tsx
Normal file
75
packages/ui/src/flex.tsx
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { Slot } from "@radix-ui/react-slot";
|
||||||
|
import { cn } from "./lib/utils";
|
||||||
|
import { VariantProps, cva } from "class-variance-authority";
|
||||||
|
import { ComponentPropsWithout, RemovedProps } from "./helpers/component-props";
|
||||||
|
|
||||||
|
type FlexElement = React.ElementRef<"div">;
|
||||||
|
type FlexDivProps = { as?: "div" } & ComponentPropsWithout<"div", RemovedProps>;
|
||||||
|
type FlexSpanProps = { as: "span" } & ComponentPropsWithout<
|
||||||
|
"span",
|
||||||
|
RemovedProps
|
||||||
|
>;
|
||||||
|
type FlexAttributeProps = FlexSpanProps | FlexDivProps;
|
||||||
|
|
||||||
|
const flexVariants = cva("box-border flex justify-start", {
|
||||||
|
variants: {
|
||||||
|
direction: {
|
||||||
|
row: "flex-row",
|
||||||
|
column: "flex-col",
|
||||||
|
},
|
||||||
|
align: {
|
||||||
|
start: "items-start",
|
||||||
|
center: "items-center",
|
||||||
|
end: "items-end",
|
||||||
|
stretch: "items-stretch",
|
||||||
|
},
|
||||||
|
justify: {
|
||||||
|
start: "justify-start",
|
||||||
|
center: "justify-center",
|
||||||
|
end: "justify-end",
|
||||||
|
between: "justify-between",
|
||||||
|
around: "justify-around",
|
||||||
|
},
|
||||||
|
wrap: {
|
||||||
|
noWrap: "flex-nowrap",
|
||||||
|
wrap: "flex-wrap",
|
||||||
|
wrapReverse: "flex-wrap-reverse",
|
||||||
|
},
|
||||||
|
gap: {
|
||||||
|
none: "gap-0",
|
||||||
|
sm: "gap-2.5",
|
||||||
|
md: "gap-4",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
direction: "row",
|
||||||
|
align: "start",
|
||||||
|
justify: "start",
|
||||||
|
wrap: "noWrap",
|
||||||
|
gap: "md",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
interface FlexProps extends VariantProps<typeof flexVariants> {
|
||||||
|
asChild?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Flex = React.forwardRef<FlexElement, FlexProps & FlexAttributeProps>(
|
||||||
|
(props, forwardedRef) => {
|
||||||
|
const { className, asChild, as: Tag = "div", ...flexProps } = props;
|
||||||
|
const Comp = asChild ? Slot : Tag;
|
||||||
|
return (
|
||||||
|
<Comp
|
||||||
|
{...flexProps}
|
||||||
|
ref={forwardedRef}
|
||||||
|
className={cn(flexVariants(flexProps), className)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
Flex.displayName = "Flex";
|
||||||
|
|
||||||
|
export { Flex };
|
||||||
|
export type { FlexProps };
|
|
@ -11,7 +11,7 @@ import {
|
||||||
} from "react-hook-form";
|
} from "react-hook-form";
|
||||||
|
|
||||||
import { Label } from "./label";
|
import { Label } from "./label";
|
||||||
import { cn } from "@rallly/ui";
|
import { cn } from "./lib/utils";
|
||||||
|
|
||||||
const Form = FormProvider;
|
const Form = FormProvider;
|
||||||
|
|
||||||
|
@ -78,7 +78,7 @@ const FormItem = React.forwardRef<
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormItemContext.Provider value={{ id }}>
|
<FormItemContext.Provider value={{ id }}>
|
||||||
<div ref={ref} className={cn("grid gap-y-2.5", className)} {...props} />
|
<div ref={ref} className={cn("space-y-2.5", className)} {...props} />
|
||||||
</FormItemContext.Provider>
|
</FormItemContext.Provider>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
22
packages/ui/src/helpers/component-props.ts
Normal file
22
packages/ui/src/helpers/component-props.ts
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import type React from "react";
|
||||||
|
|
||||||
|
type ComponentPropsAs<
|
||||||
|
C extends React.ElementType<any>,
|
||||||
|
T extends React.ComponentPropsWithoutRef<C>["as"],
|
||||||
|
> = Omit<
|
||||||
|
Extract<React.ComponentPropsWithoutRef<C>, { as: T }>,
|
||||||
|
"as" | "asChild"
|
||||||
|
>;
|
||||||
|
|
||||||
|
// Omits the specified props from the component props. Autocomplete will suggest props
|
||||||
|
// of the component, but won't restrict the omittable props to those that actually exist.
|
||||||
|
type ComponentPropsWithout<
|
||||||
|
T extends React.ElementType,
|
||||||
|
O extends
|
||||||
|
| Omit<string, keyof React.ComponentPropsWithoutRef<T>>
|
||||||
|
| keyof React.ComponentPropsWithoutRef<T>,
|
||||||
|
> = Omit<React.ComponentPropsWithoutRef<T>, O & string>;
|
||||||
|
|
||||||
|
type RemovedProps = "asChild" | "defaultChecked" | "defaultValue" | "color";
|
||||||
|
|
||||||
|
export type { ComponentPropsAs, ComponentPropsWithout, RemovedProps };
|
85
packages/ui/src/helpers/input-attributes.ts
Normal file
85
packages/ui/src/helpers/input-attributes.ts
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attributes
|
||||||
|
|
||||||
|
type InputAttributes =
|
||||||
|
| "accept"
|
||||||
|
| "alt"
|
||||||
|
| "autocapitalize"
|
||||||
|
| "autocomplete"
|
||||||
|
| "capture"
|
||||||
|
| "checked"
|
||||||
|
| "defaultChecked"
|
||||||
|
| "defaultValue"
|
||||||
|
| "disabled"
|
||||||
|
| "form"
|
||||||
|
| "formaction"
|
||||||
|
| "formenctype"
|
||||||
|
| "formmethod"
|
||||||
|
| "formnovalidate"
|
||||||
|
| "formtarget"
|
||||||
|
| "height"
|
||||||
|
| "list"
|
||||||
|
| "max"
|
||||||
|
| "maxlength"
|
||||||
|
| "min"
|
||||||
|
| "minlength"
|
||||||
|
| "multiple"
|
||||||
|
| "name"
|
||||||
|
| "pattern"
|
||||||
|
| "placeholder"
|
||||||
|
| "popovertarget"
|
||||||
|
| "popovertargetaction"
|
||||||
|
| "readonly"
|
||||||
|
| "required"
|
||||||
|
| "size"
|
||||||
|
| "src"
|
||||||
|
| "step"
|
||||||
|
| "type"
|
||||||
|
| "value"
|
||||||
|
| "width";
|
||||||
|
|
||||||
|
// Includes all text-like inputs, e.g. text, email, password, number, date, etc.
|
||||||
|
type InputTextualAttributes =
|
||||||
|
| "autoCapitalize"
|
||||||
|
| "autoComplete"
|
||||||
|
| "defaultValue"
|
||||||
|
| "disabled"
|
||||||
|
| "form"
|
||||||
|
| "list"
|
||||||
|
| "maxLength"
|
||||||
|
| "minLength"
|
||||||
|
| "min"
|
||||||
|
| "multiple"
|
||||||
|
| "max"
|
||||||
|
| "name"
|
||||||
|
| "pattern"
|
||||||
|
| "placeholder"
|
||||||
|
| "readOnly"
|
||||||
|
| "required"
|
||||||
|
| "size"
|
||||||
|
| "step"
|
||||||
|
| "type"
|
||||||
|
| "value";
|
||||||
|
|
||||||
|
type InputRadioAttributes =
|
||||||
|
| "checked"
|
||||||
|
| "defaultChecked"
|
||||||
|
| "defaultValue"
|
||||||
|
| "disabled"
|
||||||
|
| "form"
|
||||||
|
| "name"
|
||||||
|
| "required"
|
||||||
|
| "value";
|
||||||
|
|
||||||
|
type NotInputRadioAttributes = Exclude<InputAttributes, InputRadioAttributes>;
|
||||||
|
type NotInputTextualAttributes = Exclude<
|
||||||
|
InputAttributes,
|
||||||
|
InputTextualAttributes
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type {
|
||||||
|
InputAttributes,
|
||||||
|
InputRadioAttributes,
|
||||||
|
InputTextualAttributes,
|
||||||
|
NotInputRadioAttributes,
|
||||||
|
NotInputTextualAttributes,
|
||||||
|
};
|
40
packages/ui/src/icon.tsx
Normal file
40
packages/ui/src/icon.tsx
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
"use client";
|
||||||
|
import { Slot } from "@radix-ui/react-slot";
|
||||||
|
import { cn } from "./lib/utils";
|
||||||
|
import { VariantProps, cva } from "class-variance-authority";
|
||||||
|
|
||||||
|
const iconVariants = cva("", {
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default: "text-gray-500",
|
||||||
|
success: "text-green-500",
|
||||||
|
danger: "text-rose-500",
|
||||||
|
primary: "text-primary-200",
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
md: "w-4 h-4",
|
||||||
|
lg: "w-5 h-5",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
size: "md",
|
||||||
|
variant: "default",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export interface IconProps extends VariantProps<typeof iconVariants> {
|
||||||
|
children?: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Icon({ children, size, variant }: IconProps) {
|
||||||
|
return (
|
||||||
|
<Slot
|
||||||
|
className={cn(
|
||||||
|
iconVariants({ size, variant }),
|
||||||
|
"group-[.bg-primary]:text-primary-200 group",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Slot>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { cn } from "@rallly/ui";
|
import { cn } from "./lib/utils";
|
||||||
import { cva } from "class-variance-authority";
|
import { cva } from "class-variance-authority";
|
||||||
|
|
||||||
export type InputProps = Omit<
|
export type InputProps = Omit<
|
||||||
|
@ -13,15 +13,21 @@ export type InputProps = Omit<
|
||||||
|
|
||||||
const inputVariants = cva(
|
const inputVariants = cva(
|
||||||
cn(
|
cn(
|
||||||
"border-input placeholder:text-muted-foreground flex h-9 w-full rounded border bg-transparent file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:cursor-not-allowed disabled:opacity-50",
|
"focus:visible:border-primary-400 focus:visible:ring-primary-200 focus:visible:ring-2",
|
||||||
|
"border-input placeholder:text-muted-foreground h-9 rounded border bg-gray-50 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:cursor-not-allowed disabled:opacity-50",
|
||||||
),
|
),
|
||||||
{
|
{
|
||||||
variants: {
|
variants: {
|
||||||
size: {
|
size: {
|
||||||
sm: "h-6 text-sm px-1",
|
sm: "h-7 text-xs px-1",
|
||||||
md: "h-9 text-sm px-2",
|
md: "h-9 text-sm px-2",
|
||||||
lg: "h-12 text-lg px-3",
|
lg: "h-12 text-lg px-3",
|
||||||
},
|
},
|
||||||
|
variant: {
|
||||||
|
default: "border-primary-400 focus-visible:border-primary-400",
|
||||||
|
error: "border-rose-400 focus-visible:border-rose-400",
|
||||||
|
ghost: "border-transparent focus-visible:border-primary-400",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
defaultVariants: {
|
defaultVariants: {
|
||||||
size: "md",
|
size: "md",
|
||||||
|
@ -35,7 +41,6 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
||||||
<input
|
<input
|
||||||
type={type}
|
type={type}
|
||||||
className={cn(
|
className={cn(
|
||||||
"focus-visible:ring-offset-input-background focus-visible:ring-1 focus-visible:ring-offset-1",
|
|
||||||
inputVariants({ size }),
|
inputVariants({ size }),
|
||||||
error
|
error
|
||||||
? "focus-visible:border-rose-400 focus-visible:ring-rose-100"
|
? "focus-visible:border-rose-400 focus-visible:ring-rose-100"
|
||||||
|
|
|
@ -4,7 +4,7 @@ import * as LabelPrimitive from "@radix-ui/react-label";
|
||||||
import { cva, VariantProps } from "class-variance-authority";
|
import { cva, VariantProps } from "class-variance-authority";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { cn } from "@rallly/ui";
|
import { cn } from "./lib/utils";
|
||||||
|
|
||||||
const labelVariants = cva(
|
const labelVariants = cva(
|
||||||
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
|
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { cn } from "@rallly/ui";
|
import { cn } from "./lib/utils";
|
||||||
|
|
||||||
const Popover = PopoverPrimitive.Root;
|
const Popover = PopoverPrimitive.Root;
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
|
||||||
import { CircleIcon } from "lucide-react";
|
import { CircleIcon } from "lucide-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { cn } from "@rallly/ui";
|
import { cn } from "./lib/utils";
|
||||||
|
|
||||||
const RadioGroup = React.forwardRef<
|
const RadioGroup = React.forwardRef<
|
||||||
React.ElementRef<typeof RadioGroupPrimitive.Root>,
|
React.ElementRef<typeof RadioGroupPrimitive.Root>,
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import * as SelectPrimitive from "@radix-ui/react-select";
|
import * as SelectPrimitive from "@radix-ui/react-select";
|
||||||
import { CheckIcon, ChevronDownIcon } from "lucide-react";
|
import { CheckIcon, ChevronsUpDownIcon } from "lucide-react";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { cn } from "@rallly/ui";
|
import { cn } from "./lib/utils";
|
||||||
|
import { buttonVariants } from "./button";
|
||||||
|
import { Icon } from "./icon";
|
||||||
|
|
||||||
const Select = SelectPrimitive.Root;
|
const Select = SelectPrimitive.Root;
|
||||||
|
|
||||||
|
@ -18,15 +20,12 @@ const SelectTrigger = React.forwardRef<
|
||||||
>(({ className, children, ...props }, ref) => (
|
>(({ className, children, ...props }, ref) => (
|
||||||
<SelectPrimitive.Trigger
|
<SelectPrimitive.Trigger
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(buttonVariants(), className)}
|
||||||
"flex h-10 w-full items-center justify-between rounded-md border bg-white px-3 py-2 text-sm focus:outline-none disabled:cursor-not-allowed disabled:opacity-50",
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
<SelectPrimitive.Icon asChild>
|
<SelectPrimitive.Icon asChild>
|
||||||
<ChevronDownIcon className="ml-2 size-4 opacity-50" />
|
<ChevronsUpDownIcon className="ml-2 size-4 opacity-50" />
|
||||||
</SelectPrimitive.Icon>
|
</SelectPrimitive.Icon>
|
||||||
</SelectPrimitive.Trigger>
|
</SelectPrimitive.Trigger>
|
||||||
));
|
));
|
||||||
|
@ -87,7 +86,9 @@ const SelectItem = React.forwardRef<
|
||||||
>
|
>
|
||||||
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||||
<SelectPrimitive.ItemIndicator>
|
<SelectPrimitive.ItemIndicator>
|
||||||
|
<Icon>
|
||||||
<CheckIcon className="size-4" />
|
<CheckIcon className="size-4" />
|
||||||
|
</Icon>
|
||||||
</SelectPrimitive.ItemIndicator>
|
</SelectPrimitive.ItemIndicator>
|
||||||
</span>
|
</span>
|
||||||
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
import * as SwitchPrimitives from "@radix-ui/react-switch";
|
import * as SwitchPrimitives from "@radix-ui/react-switch";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { cn } from "@rallly/ui";
|
import { cn } from "./lib/utils";
|
||||||
|
|
||||||
const Switch = React.forwardRef<
|
const Switch = React.forwardRef<
|
||||||
React.ElementRef<typeof SwitchPrimitives.Root>,
|
React.ElementRef<typeof SwitchPrimitives.Root>,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { cn } from "@rallly/ui";
|
import { cn } from "./lib/utils";
|
||||||
|
|
||||||
const Table = React.forwardRef<
|
const Table = React.forwardRef<
|
||||||
HTMLTableElement,
|
HTMLTableElement,
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
import * as TabsPrimitive from "@radix-ui/react-tabs";
|
import * as TabsPrimitive from "@radix-ui/react-tabs";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { cn } from "@rallly/ui";
|
import { cn } from "./lib/utils";
|
||||||
|
|
||||||
const Tabs = TabsPrimitive.Root;
|
const Tabs = TabsPrimitive.Root;
|
||||||
|
|
||||||
|
|
90
packages/ui/src/text-field.tsx
Normal file
90
packages/ui/src/text-field.tsx
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { cn } from "./lib/utils";
|
||||||
|
import { VariantProps, cva } from "class-variance-authority";
|
||||||
|
import { Icon, IconProps } from "./icon";
|
||||||
|
|
||||||
|
const inputVariants = cva(
|
||||||
|
cn(
|
||||||
|
"focus:border-primary-400 focus-visible:ring-primary-100 focus-visible:ring-2",
|
||||||
|
"border-input placeholder:text-muted-foreground h-9 rounded border bg-gray-50 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:cursor-not-allowed disabled:opacity-50",
|
||||||
|
),
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
size: {
|
||||||
|
sm: "h-7 text-xs px-1",
|
||||||
|
md: "h-9 text-sm px-2",
|
||||||
|
lg: "h-12 text-lg px-3",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
size: "md",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export type InputProps = Omit<
|
||||||
|
React.InputHTMLAttributes<HTMLInputElement>,
|
||||||
|
"size"
|
||||||
|
> &
|
||||||
|
VariantProps<typeof inputVariants>;
|
||||||
|
|
||||||
|
const TextFieldInput = React.forwardRef<HTMLInputElement, InputProps>(
|
||||||
|
({ className, size, type, ...props }, ref) => {
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
type={type}
|
||||||
|
className={cn(
|
||||||
|
inputVariants({ size }),
|
||||||
|
"group-[.text-field]:border-0 group-[.text-field]:bg-transparent group-[.text-field]:p-0 group-[.text-field]:ring-0 group-[.text-field]:ring-offset-0",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
ref={ref}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
interface TextFieldProps extends InputProps {
|
||||||
|
children?: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TextField = React.forwardRef<HTMLDivElement, TextFieldProps>(
|
||||||
|
({ className, size, type, children, ...props }, ref) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
onClick={(e) => {
|
||||||
|
const target = e.target as HTMLElement;
|
||||||
|
// find an input that s a direct descendant of the target
|
||||||
|
target.querySelector("input")?.focus();
|
||||||
|
}}
|
||||||
|
className={cn(
|
||||||
|
"flex items-center",
|
||||||
|
"text-field group",
|
||||||
|
"focus-within:border-primary-400 focus-within:ring-primary-100 focus-within:ring-2",
|
||||||
|
inputVariants({ size }),
|
||||||
|
"p-0",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
TextField.displayName = "TextField";
|
||||||
|
|
||||||
|
function TextFieldIcon(props: IconProps) {
|
||||||
|
return (
|
||||||
|
<div className="pointer-events-none px-2">
|
||||||
|
<Icon {...props} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { TextField, TextFieldIcon, TextFieldInput };
|
|
@ -1,6 +1,6 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { cn } from "@rallly/ui";
|
import { cn } from "./lib/utils";
|
||||||
|
|
||||||
export type TextareaProps = React.TextareaHTMLAttributes<HTMLTextAreaElement>;
|
export type TextareaProps = React.TextareaHTMLAttributes<HTMLTextAreaElement>;
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
||||||
return (
|
return (
|
||||||
<textarea
|
<textarea
|
||||||
className={cn(
|
className={cn(
|
||||||
"border-input placeholder:text-muted-foreground flex min-h-[80px] w-full rounded border bg-transparent px-3 py-2 text-sm disabled:cursor-not-allowed disabled:opacity-50",
|
"border-input placeholder:text-muted-foreground flex min-h-[80px] rounded border bg-gray-50 px-2 py-2 text-sm disabled:cursor-not-allowed disabled:opacity-50",
|
||||||
"focus-visible:ring-offset-input-background focus-visible:ring-1 focus-visible:ring-offset-1",
|
"focus-visible:ring-offset-input-background focus-visible:ring-1 focus-visible:ring-offset-1",
|
||||||
"focus-visible:border-primary-400 focus-visible:ring-primary-100",
|
"focus-visible:border-primary-400 focus-visible:ring-primary-100",
|
||||||
className,
|
className,
|
||||||
|
|
|
@ -3,7 +3,7 @@ import * as ToastPrimitives from "@radix-ui/react-toast";
|
||||||
import { cva, type VariantProps } from "class-variance-authority";
|
import { cva, type VariantProps } from "class-variance-authority";
|
||||||
import { X } from "lucide-react";
|
import { X } from "lucide-react";
|
||||||
|
|
||||||
import { cn } from "@rallly/ui";
|
import { cn } from "./lib/utils";
|
||||||
|
|
||||||
const ToastProvider = ToastPrimitives.Provider;
|
const ToastProvider = ToastPrimitives.Provider;
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
|
||||||
import { cn } from "@rallly/ui";
|
import { cn } from "./lib/utils";
|
||||||
|
|
||||||
const TooltipProvider = TooltipPrimitive.Provider;
|
const TooltipProvider = TooltipPrimitive.Provider;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": "@rallly/tsconfig/react.json",
|
"extends": "@rallly/tsconfig/next.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"paths": {
|
"paths": {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue