fix: resolve TypeScript errors in migrated components

This commit is contained in:
Jason Woltje
2026-01-29 22:00:14 -06:00
parent d54714ea06
commit abbf886483
36 changed files with 758 additions and 43 deletions

View File

@@ -0,0 +1,121 @@
import * as React from "react";
export interface AlertDialogProps {
open?: boolean;
onOpenChange?: (open: boolean) => void;
children?: React.ReactNode;
}
export interface AlertDialogTriggerProps {
children?: React.ReactNode;
asChild?: boolean;
}
export interface AlertDialogContentProps {
children?: React.ReactNode;
}
export interface AlertDialogHeaderProps {
children?: React.ReactNode;
}
export interface AlertDialogFooterProps {
children?: React.ReactNode;
}
export interface AlertDialogTitleProps {
children?: React.ReactNode;
}
export interface AlertDialogDescriptionProps {
children?: React.ReactNode;
}
export interface AlertDialogActionProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
children?: React.ReactNode;
}
export interface AlertDialogCancelProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
children?: React.ReactNode;
}
const AlertDialogContext = React.createContext<{
open?: boolean;
onOpenChange?: (open: boolean) => void;
}>({});
export function AlertDialog({ open, onOpenChange, children }: AlertDialogProps) {
return (
<AlertDialogContext.Provider value={{ open, onOpenChange }}>
{children}
</AlertDialogContext.Provider>
);
}
export function AlertDialogTrigger({ children, asChild }: AlertDialogTriggerProps) {
const { onOpenChange } = React.useContext(AlertDialogContext);
if (asChild && React.isValidElement(children)) {
return React.cloneElement(children, {
onClick: () => onOpenChange?.(true),
} as React.HTMLAttributes<HTMLElement>);
}
return <div onClick={() => onOpenChange?.(true)}>{children}</div>;
}
export function AlertDialogContent({ children }: AlertDialogContentProps) {
const { open, onOpenChange } = React.useContext(AlertDialogContext);
if (!open) return null;
return (
<div className="fixed inset-0 z-50 flex items-center justify-center">
<div className="fixed inset-0 bg-black/50" onClick={() => onOpenChange?.(false)} />
<div className="relative z-50 w-full max-w-lg rounded-lg bg-white p-6 shadow-lg">
{children}
</div>
</div>
);
}
export function AlertDialogHeader({ children }: AlertDialogHeaderProps) {
return <div className="mb-4">{children}</div>;
}
export function AlertDialogFooter({ children }: AlertDialogFooterProps) {
return <div className="mt-4 flex justify-end gap-2">{children}</div>;
}
export function AlertDialogTitle({ children }: AlertDialogTitleProps) {
return <h2 className="text-lg font-semibold">{children}</h2>;
}
export function AlertDialogDescription({ children }: AlertDialogDescriptionProps) {
return <p className="text-sm text-gray-600">{children}</p>;
}
export function AlertDialogAction({ children, ...props }: AlertDialogActionProps) {
return (
<button
className="rounded-md bg-blue-600 px-4 py-2 text-sm text-white hover:bg-blue-700"
{...props}
>
{children}
</button>
);
}
export function AlertDialogCancel({ children, ...props }: AlertDialogCancelProps) {
const { onOpenChange } = React.useContext(AlertDialogContext);
return (
<button
className="rounded-md border border-gray-300 px-4 py-2 text-sm hover:bg-gray-100"
onClick={() => onOpenChange?.(false)}
{...props}
>
{children}
</button>
);
}

View File

@@ -0,0 +1,22 @@
import { Badge as BaseBadge } from "@mosaic/ui";
import type { BadgeProps as BaseBadgeProps, BadgeVariant as BaseBadgeVariant } from "@mosaic/ui";
// Extend BadgeVariant to include shadcn/ui variants
export type BadgeVariant = BaseBadgeVariant | "secondary" | "outline" | "default" | "destructive";
export interface BadgeProps extends Omit<BaseBadgeProps, "variant"> {
variant?: BadgeVariant;
}
// Map extended variants to base variants
const variantMap: Record<string, BaseBadgeVariant> = {
"secondary": "status-neutral",
"outline": "status-info",
"default": "status-neutral",
"destructive": "status-error",
};
export function Badge({ variant = "default", ...props }: BadgeProps) {
const mappedVariant = (variantMap[variant] || variant) as BaseBadgeVariant;
return <BaseBadge variant={mappedVariant} {...props} />;
}

View File

@@ -0,0 +1,26 @@
import { Button as BaseButton } from "@mosaic/ui";
import type { ButtonProps as BaseButtonProps } from "@mosaic/ui";
import type { ReactNode, ButtonHTMLAttributes } from "react";
// Extend Button to support additional variants
type ExtendedVariant = "primary" | "secondary" | "danger" | "ghost" | "outline" | "destructive" | "link";
export interface ButtonProps extends Omit<BaseButtonProps, "variant"> {
variant?: ExtendedVariant;
size?: "sm" | "md" | "lg" | "icon";
children: ReactNode;
}
// Map extended variants to base variants
const variantMap: Record<string, "primary" | "secondary" | "danger" | "ghost"> = {
"outline": "ghost",
"destructive": "danger",
"link": "ghost",
};
export function Button({ variant = "primary", size = "md", ...props }: ButtonProps) {
const mappedVariant = variantMap[variant] || variant;
const mappedSize = size === "icon" ? "sm" : size;
return <BaseButton variant={mappedVariant as "primary" | "secondary" | "danger" | "ghost"} size={mappedSize as "sm" | "md" | "lg"} {...props} />;
}

View File

@@ -0,0 +1,22 @@
export { Card, CardHeader, CardContent, CardFooter } from "@mosaic/ui";
export type { CardProps, CardHeaderProps, CardContentProps, CardFooterProps } from "@mosaic/ui";
// Additional Card sub-components for shadcn/ui compatibility
import * as React from "react";
export interface CardTitleProps extends React.HTMLAttributes<HTMLHeadingElement> {}
export interface CardDescriptionProps extends React.HTMLAttributes<HTMLParagraphElement> {}
export const CardTitle = React.forwardRef<HTMLHeadingElement, CardTitleProps>(
({ className = "", ...props }, ref) => (
<h3 ref={ref} className={`text-2xl font-semibold leading-none tracking-tight ${className}`} {...props} />
)
);
CardTitle.displayName = "CardTitle";
export const CardDescription = React.forwardRef<HTMLParagraphElement, CardDescriptionProps>(
({ className = "", ...props }, ref) => (
<p ref={ref} className={`text-sm text-gray-600 ${className}`} {...props} />
)
);
CardDescription.displayName = "CardDescription";

View File

@@ -0,0 +1,2 @@
export { Input } from "@mosaic/ui";
export type { InputProps } from "@mosaic/ui";

View File

@@ -0,0 +1,17 @@
import * as React from "react";
export interface LabelProps extends React.LabelHTMLAttributes<HTMLLabelElement> {}
export const Label = React.forwardRef<HTMLLabelElement, LabelProps>(
({ className = "", ...props }, ref) => {
return (
<label
ref={ref}
className={`text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 ${className}`}
{...props}
/>
);
}
);
Label.displayName = "Label";

View File

@@ -0,0 +1,102 @@
import * as React from "react";
export interface SelectProps {
value?: string;
onValueChange?: (value: string) => void;
defaultValue?: string;
disabled?: boolean;
children?: React.ReactNode;
}
export interface SelectTriggerProps {
id?: string;
className?: string;
children?: React.ReactNode;
}
export interface SelectContentProps {
children?: React.ReactNode;
}
export interface SelectItemProps {
value: string;
children?: React.ReactNode;
}
export interface SelectValueProps {
placeholder?: string;
}
const SelectContext = React.createContext<{
value?: string;
onValueChange?: (value: string) => void;
isOpen: boolean;
setIsOpen: (open: boolean) => void;
}>({ isOpen: false, setIsOpen: () => {} });
export function Select({ value, onValueChange, defaultValue, disabled, children }: SelectProps) {
const [isOpen, setIsOpen] = React.useState(false);
const [internalValue, setInternalValue] = React.useState(defaultValue);
const currentValue = value !== undefined ? value : internalValue;
const handleValueChange = (newValue: string) => {
if (value === undefined) {
setInternalValue(newValue);
}
onValueChange?.(newValue);
setIsOpen(false);
};
return (
<SelectContext.Provider value={{ value: currentValue, onValueChange: handleValueChange, isOpen, setIsOpen }}>
<div className="relative">{children}</div>
</SelectContext.Provider>
);
}
export function SelectTrigger({ id, className = "", children }: SelectTriggerProps) {
const { isOpen, setIsOpen } = React.useContext(SelectContext);
return (
<button
id={id}
type="button"
onClick={() => setIsOpen(!isOpen)}
className={`flex h-10 w-full items-center justify-between rounded-md border border-gray-300 bg-white px-3 py-2 text-sm ${className}`}
>
{children}
</button>
);
}
export function SelectValue({ placeholder }: SelectValueProps) {
const { value } = React.useContext(SelectContext);
return <span>{value || placeholder}</span>;
}
export function SelectContent({ children }: SelectContentProps) {
const { isOpen } = React.useContext(SelectContext);
if (!isOpen) return null;
return (
<div className="absolute z-50 mt-1 max-h-60 w-full overflow-auto rounded-md border border-gray-300 bg-white shadow-lg">
{children}
</div>
);
}
export function SelectItem({ value, children }: SelectItemProps) {
const { onValueChange } = React.useContext(SelectContext);
return (
<div
onClick={() => onValueChange?.(value)}
className="cursor-pointer px-3 py-2 text-sm hover:bg-gray-100"
>
{children}
</div>
);
}

View File

@@ -0,0 +1,28 @@
import * as React from "react";
export interface SwitchProps {
id?: string;
checked?: boolean;
onCheckedChange?: (checked: boolean) => void;
disabled?: boolean;
className?: string;
}
export const Switch = React.forwardRef<HTMLInputElement, SwitchProps>(
({ id, checked, onCheckedChange, disabled, className = "" }, ref) => {
return (
<input
type="checkbox"
role="switch"
ref={ref}
id={id}
checked={checked}
onChange={(e) => onCheckedChange?.(e.target.checked)}
disabled={disabled}
className={`w-11 h-6 rounded-full ${className}`}
/>
);
}
);
Switch.displayName = "Switch";

View File

@@ -0,0 +1,2 @@
export { Textarea } from "@mosaic/ui";
export type { TextareaProps } from "@mosaic/ui";