From 35bcb633003c5f670aeac08843bfdacfeafaad60 Mon Sep 17 00:00:00 2001 From: Egutierrez Date: Fri, 3 Apr 2026 03:23:32 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20nuevos=20componentes=20UI=20=E2=80=94?= =?UTF-8?q?=20accordion,=20avatar,=20breadcrumb,=20checkbox,=20command,=20?= =?UTF-8?q?dropdown,=20pagination,=20popover,=20radio,=20sheet,=20select,?= =?UTF-8?q?=20switch,=20textarea,=20toast?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Componentes React accesibles basados en Radix UI con soporte de temas via CSS variables. Incluye barrel export en index.ts. Co-Authored-By: Claude Opus 4.6 (1M context) --- frontend/functions/ui/accordion.md | 53 ++++++ frontend/functions/ui/accordion.tsx | 81 ++++++++++ frontend/functions/ui/avatar.md | 70 ++++++++ frontend/functions/ui/avatar.tsx | 69 ++++++++ frontend/functions/ui/breadcrumb.md | 71 +++++++++ frontend/functions/ui/breadcrumb.tsx | 97 +++++++++++ frontend/functions/ui/checkbox.md | 72 +++++++++ frontend/functions/ui/checkbox.tsx | 79 +++++++++ frontend/functions/ui/command.md | 81 ++++++++++ frontend/functions/ui/command.tsx | 204 ++++++++++++++++++++++++ frontend/functions/ui/dropdown_menu.md | 73 +++++++++ frontend/functions/ui/dropdown_menu.tsx | 201 +++++++++++++++++++++++ frontend/functions/ui/index.ts | 121 ++++++++++++++ frontend/functions/ui/pagination.md | 61 +++++++ frontend/functions/ui/pagination.tsx | 100 ++++++++++++ frontend/functions/ui/popover.md | 65 ++++++++ frontend/functions/ui/popover.tsx | 57 +++++++ frontend/functions/ui/radio_group.md | 60 +++++++ frontend/functions/ui/radio_group.tsx | 61 +++++++ frontend/functions/ui/sheet.md | 71 +++++++++ frontend/functions/ui/sheet.tsx | 118 ++++++++++++++ frontend/functions/ui/simple_select.md | 82 ++++++++++ frontend/functions/ui/simple_select.tsx | 84 ++++++++++ frontend/functions/ui/switch_toggle.md | 67 ++++++++ frontend/functions/ui/switch_toggle.tsx | 66 ++++++++ frontend/functions/ui/textarea.md | 66 ++++++++ frontend/functions/ui/textarea.tsx | 47 ++++++ frontend/functions/ui/toast.md | 90 +++++++++++ frontend/functions/ui/toast.tsx | 170 ++++++++++++++++++++ 29 files changed, 2537 insertions(+) create mode 100644 frontend/functions/ui/accordion.md create mode 100644 frontend/functions/ui/accordion.tsx create mode 100644 frontend/functions/ui/avatar.md create mode 100644 frontend/functions/ui/avatar.tsx create mode 100644 frontend/functions/ui/breadcrumb.md create mode 100644 frontend/functions/ui/breadcrumb.tsx create mode 100644 frontend/functions/ui/checkbox.md create mode 100644 frontend/functions/ui/checkbox.tsx create mode 100644 frontend/functions/ui/command.md create mode 100644 frontend/functions/ui/command.tsx create mode 100644 frontend/functions/ui/dropdown_menu.md create mode 100644 frontend/functions/ui/dropdown_menu.tsx create mode 100644 frontend/functions/ui/index.ts create mode 100644 frontend/functions/ui/pagination.md create mode 100644 frontend/functions/ui/pagination.tsx create mode 100644 frontend/functions/ui/popover.md create mode 100644 frontend/functions/ui/popover.tsx create mode 100644 frontend/functions/ui/radio_group.md create mode 100644 frontend/functions/ui/radio_group.tsx create mode 100644 frontend/functions/ui/sheet.md create mode 100644 frontend/functions/ui/sheet.tsx create mode 100644 frontend/functions/ui/simple_select.md create mode 100644 frontend/functions/ui/simple_select.tsx create mode 100644 frontend/functions/ui/switch_toggle.md create mode 100644 frontend/functions/ui/switch_toggle.tsx create mode 100644 frontend/functions/ui/textarea.md create mode 100644 frontend/functions/ui/textarea.tsx create mode 100644 frontend/functions/ui/toast.md create mode 100644 frontend/functions/ui/toast.tsx diff --git a/frontend/functions/ui/accordion.md b/frontend/functions/ui/accordion.md new file mode 100644 index 00000000..f66db4cb --- /dev/null +++ b/frontend/functions/ui/accordion.md @@ -0,0 +1,53 @@ +--- +name: accordion +kind: component +lang: ts +domain: ui +version: "1.0.0" +purity: impure +signature: "Accordion(props: AccordionProps): JSX.Element" +description: "Secciones colapsables con animaciones. Base-UI Collapsible primitive. Composable: AccordionItem + AccordionTrigger + AccordionContent." +tags: [accordion, collapsible, component, ui, interactive, base-ui] +uses_functions: [cn_ts_core] +uses_types: [] +returns: [] +returns_optional: false +error_type: "" +imports: ["@base-ui/react/collapsible", "lucide-react"] +tested: false +tests: [] +test_file_path: "" +file_path: "frontend/functions/ui/accordion.tsx" +props: + - name: className + type: "string" + required: false + description: "Clases CSS adicionales para el contenedor" +emits: [] +has_state: false +framework: react +variant: [] +--- + +## Ejemplo + +```tsx + + + Seccion 1 + + Contenido de la primera seccion. + + + + Seccion 2 + + Contenido de la segunda seccion. + + + +``` + +## Notas + +Cada AccordionItem es un Collapsible independiente — permite multiples items abiertos simultaneamente. Para exclusividad (solo uno abierto), manejar el estado externamente. El chevron rota 180 grados con [data-open]. Exports: Accordion, AccordionItem, AccordionTrigger, AccordionContent. diff --git a/frontend/functions/ui/accordion.tsx b/frontend/functions/ui/accordion.tsx new file mode 100644 index 00000000..c7acabcf --- /dev/null +++ b/frontend/functions/ui/accordion.tsx @@ -0,0 +1,81 @@ +import * as React from "react" +import { Collapsible as CollapsiblePrimitive } from "@base-ui/react/collapsible" +import { ChevronDownIcon } from "lucide-react" +import { cn } from "../core/cn" + +interface AccordionItem { + value: string + trigger: React.ReactNode + content: React.ReactNode + disabled?: boolean +} + +interface AccordionProps { + items?: AccordionItem[] + type?: "single" | "multiple" + defaultValue?: string | string[] + className?: string + itemClassName?: string + children?: React.ReactNode +} + +function Accordion({ className, children, ...props }: React.ComponentProps<"div"> & AccordionProps) { + return ( +
+ {children} +
+ ) +} + +interface AccordionItemProps extends CollapsiblePrimitive.Root.Props { + className?: string +} + +function AccordionItem({ className, ...props }: AccordionItemProps) { + return ( + + ) +} + +function AccordionTrigger({ className, children, ...props }: CollapsiblePrimitive.Trigger.Props) { + return ( + svg]:rotate-180", + className + )} + {...props} + > + {children} + + + ) +} + +function AccordionContent({ className, children, ...props }: CollapsiblePrimitive.Panel.Props) { + return ( + +
{children}
+
+ ) +} + +export { Accordion, AccordionContent, AccordionItem, AccordionTrigger } +export type { AccordionItem as AccordionItemData, AccordionProps } diff --git a/frontend/functions/ui/avatar.md b/frontend/functions/ui/avatar.md new file mode 100644 index 00000000..c153f024 --- /dev/null +++ b/frontend/functions/ui/avatar.md @@ -0,0 +1,70 @@ +--- +name: avatar +kind: component +lang: ts +domain: ui +version: "1.0.0" +purity: impure +signature: "Avatar(props: AvatarProps): JSX.Element" +description: "Imagen de usuario circular con fallback a iniciales generadas automaticamente. 5 tamaños via CVA." +tags: [avatar, user, image, component, ui, cva] +uses_functions: [cn_ts_core] +uses_types: [] +returns: [] +returns_optional: false +error_type: "" +imports: ["class-variance-authority"] +tested: false +tests: [] +test_file_path: "" +file_path: "frontend/functions/ui/avatar.tsx" +props: + - name: src + type: "string" + required: false + description: "URL de la imagen" + - name: alt + type: "string" + required: false + description: "Texto alternativo de la imagen" + - name: fallback + type: "string" + required: false + description: "Nombre completo del que extraer iniciales (ej: 'Juan Perez' -> 'JP')" + - name: initials + type: "string" + required: false + description: "Iniciales explicitas para el fallback (sobrescribe fallback)" + - name: size + type: "'xs' | 'sm' | 'md' | 'lg' | 'xl'" + required: false + description: "Tamanio del avatar (default: md)" + - name: className + type: "string" + required: false + description: "Clases CSS adicionales" +emits: [] +has_state: true +framework: react +variant: [xs, sm, md, lg, xl] +--- + +## Ejemplo + +```tsx +// Con imagen + + +// Con fallback a iniciales + + +// Iniciales explicitas + + +// Maneja error de imagen automaticamente + +``` + +## Notas + +Usa estado interno para manejar errores de carga de imagen (onError). La funcion getInitials extrae 2 iniciales del nombre completo (primera y ultima palabra). Si solo hay una palabra, toma los 2 primeros caracteres. Usa forwardRef para compatibilidad con wrappers. diff --git a/frontend/functions/ui/avatar.tsx b/frontend/functions/ui/avatar.tsx new file mode 100644 index 00000000..012bdae3 --- /dev/null +++ b/frontend/functions/ui/avatar.tsx @@ -0,0 +1,69 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" +import { cn } from "../core/cn" + +const avatarVariants = cva( + "relative inline-flex shrink-0 items-center justify-center overflow-hidden rounded-full bg-muted font-medium text-muted-foreground select-none", + { + variants: { + size: { + xs: "size-6 text-xs", + sm: "size-8 text-sm", + md: "size-10 text-base", + lg: "size-12 text-lg", + xl: "size-16 text-xl", + }, + }, + defaultVariants: { size: "md" }, + } +) + +interface AvatarProps + extends React.ComponentPropsWithoutRef<"span">, + VariantProps { + src?: string + alt?: string + fallback?: string + initials?: string +} + +function getInitials(name?: string): string { + if (!name) return "?" + const parts = name.trim().split(/\s+/) + if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase() + return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase() +} + +const Avatar = React.forwardRef( + ({ className, size, src, alt, fallback, initials, ...props }, ref) => { + const [imgError, setImgError] = React.useState(false) + const showImage = src && !imgError + const displayInitials = initials ?? getInitials(fallback ?? alt) + + return ( + + {showImage ? ( + {alt setImgError(true)} + /> + ) : ( + + )} + + ) + } +) +Avatar.displayName = "Avatar" + +export { Avatar, avatarVariants } +export type { AvatarProps } diff --git a/frontend/functions/ui/breadcrumb.md b/frontend/functions/ui/breadcrumb.md new file mode 100644 index 00000000..c2d788c8 --- /dev/null +++ b/frontend/functions/ui/breadcrumb.md @@ -0,0 +1,71 @@ +--- +name: breadcrumb +kind: component +lang: ts +domain: ui +version: "1.0.0" +purity: impure +signature: "Breadcrumb(props: BreadcrumbProps): JSX.Element" +description: "Navegacion jerarquica con separadores, elipsis para paths largos y soporte para router links via asChild." +tags: [breadcrumb, navigation, component, ui] +uses_functions: [cn_ts_core] +uses_types: [] +returns: [] +returns_optional: false +error_type: "" +imports: ["lucide-react"] +tested: false +tests: [] +test_file_path: "" +file_path: "frontend/functions/ui/breadcrumb.tsx" +props: + - name: className + type: "string" + required: false + description: "Clases CSS adicionales" +emits: [] +has_state: false +framework: react +variant: [] +--- + +## Ejemplo + +```tsx + + + + Inicio + + + + Documentacion + + + + Componentes + + + + +// Con elipsis para paths largos + + + + Inicio + + + + + + + + Pagina actual + + + +``` + +## Notas + +Exports: Breadcrumb (nav), BreadcrumbList (ol), BreadcrumbItem (li), BreadcrumbLink (a con asChild), BreadcrumbPage (span aria-current=page), BreadcrumbSeparator (ChevronRight por defecto, customizable), BreadcrumbEllipsis (MoreHorizontal). BreadcrumbLink acepta asChild para usar con Link de React Router o Next.js. diff --git a/frontend/functions/ui/breadcrumb.tsx b/frontend/functions/ui/breadcrumb.tsx new file mode 100644 index 00000000..8cb47f23 --- /dev/null +++ b/frontend/functions/ui/breadcrumb.tsx @@ -0,0 +1,97 @@ +import * as React from "react" +import { ChevronRightIcon, MoreHorizontalIcon } from "lucide-react" +import { cn } from "../core/cn" + +function Breadcrumb({ ...props }: React.ComponentPropsWithoutRef<"nav">) { + return