create-lexy 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/r/accordion.json +24 -0
- package/assets/r/app-accordion.json +24 -0
- package/assets/r/app-dialog.json +25 -0
- package/assets/r/app-header-bar.json +28 -0
- package/assets/r/app-sidebar.json +27 -0
- package/assets/r/avatar.json +24 -0
- package/assets/r/badge.json +24 -0
- package/assets/r/breadcrumb.json +24 -0
- package/assets/r/button-group.json +27 -0
- package/assets/r/calendar.json +27 -0
- package/assets/r/card.json +24 -0
- package/assets/r/chart.json +24 -0
- package/assets/r/checkbox.json +24 -0
- package/assets/r/combobox.json +26 -0
- package/assets/r/command.json +24 -0
- package/assets/r/counter-badge.json +24 -0
- package/assets/r/date-picker.json +29 -0
- package/assets/r/dialog.json +24 -0
- package/assets/r/dropdown-menu.json +24 -0
- package/assets/r/empty.json +24 -0
- package/assets/r/feature-card.json +22 -0
- package/assets/r/header-bar.json +26 -0
- package/assets/r/input.json +22 -0
- package/assets/r/label.json +25 -0
- package/assets/r/logo.json +130 -0
- package/assets/r/menubar.json +24 -0
- package/assets/r/navigation-menu.json +25 -0
- package/assets/r/pagination.json +27 -0
- package/assets/r/popover.json +24 -0
- package/assets/r/profile-card.json +25 -0
- package/assets/r/progress.json +24 -0
- package/assets/r/radio-group.json +24 -0
- package/assets/r/registry.json +1168 -0
- package/assets/r/scroll-area.json +24 -0
- package/assets/r/searchbox.json +22 -0
- package/assets/r/select.json +24 -0
- package/assets/r/separator.json +24 -0
- package/assets/r/sheet.json +25 -0
- package/assets/r/sidebar.json +33 -0
- package/assets/r/skeleton.json +24 -0
- package/assets/r/slider.json +24 -0
- package/assets/r/snippet.json +24 -0
- package/assets/r/spinner.json +25 -0
- package/assets/r/status-dot.json +24 -0
- package/assets/r/switch.json +24 -0
- package/assets/r/table.json +24 -0
- package/assets/r/tabs.json +24 -0
- package/assets/r/tag.json +24 -0
- package/assets/r/textarea.json +22 -0
- package/assets/r/tooltip.json +24 -0
- package/assets/r/tree.json +22 -0
- package/assets/theme/lexy-theme.css +3 -0
- package/dist/index.js +40 -40
- package/package.json +1 -1
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "scroll-area",
|
|
4
|
+
"type": "registry:ui",
|
|
5
|
+
"title": "ScrollArea",
|
|
6
|
+
"description": "Área de scroll estilizada con barras de scroll personalizadas.",
|
|
7
|
+
"dependencies": [
|
|
8
|
+
"@radix-ui/react-scroll-area"
|
|
9
|
+
],
|
|
10
|
+
"registryDependencies": [],
|
|
11
|
+
"files": [
|
|
12
|
+
{
|
|
13
|
+
"path": "components/base/ScrollArea.tsx",
|
|
14
|
+
"type": "registry:ui",
|
|
15
|
+
"content": "import * as React from \"react\";\nimport * as ScrollAreaPrimitive from \"@radix-ui/react-scroll-area\";\n\nimport { cn } from \"@/lib/utils/cn\";\n\nfunction ScrollArea({\n className,\n children,\n ...props\n}: React.ComponentProps<typeof ScrollAreaPrimitive.Root>) {\n return (\n <ScrollAreaPrimitive.Root\n data-slot=\"scroll-area\"\n className={cn(\"relative\", className)}\n {...props}\n >\n <ScrollAreaPrimitive.Viewport\n data-slot=\"scroll-area-viewport\"\n className=\"size-full rounded-[inherit] transition-[color,box-shadow] outline-none\"\n >\n {children}\n </ScrollAreaPrimitive.Viewport>\n <ScrollBar />\n <ScrollAreaPrimitive.Corner />\n </ScrollAreaPrimitive.Root>\n );\n}\n\nfunction ScrollBar({\n className,\n orientation = \"vertical\",\n ...props\n}: React.ComponentProps<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>) {\n return (\n <ScrollAreaPrimitive.ScrollAreaScrollbar\n data-slot=\"scroll-area-scrollbar\"\n data-orientation={orientation}\n orientation={orientation}\n className={cn(\n \"flex touch-none select-none transition-colors\",\n \"data-[orientation=horizontal]:h-2 data-[orientation=horizontal]:flex-col data-[orientation=horizontal]:border-t data-[orientation=horizontal]:border-t-transparent\",\n \"data-[orientation=vertical]:h-full data-[orientation=vertical]:w-2 data-[orientation=vertical]:border-l data-[orientation=vertical]:border-l-transparent\",\n className\n )}\n {...props}\n >\n <ScrollAreaPrimitive.ScrollAreaThumb\n data-slot=\"scroll-area-thumb\"\n className=\"relative flex-1 rounded-full bg-muted-foreground/20 hover:bg-muted-foreground/30 transition-colors\"\n />\n </ScrollAreaPrimitive.ScrollAreaScrollbar>\n );\n}\n\nexport { ScrollArea, ScrollBar };\n"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"path": "components/base/ScrollArea.md",
|
|
19
|
+
"type": "registry:file",
|
|
20
|
+
"target": "components/base/ScrollArea.md",
|
|
21
|
+
"content": "# ScrollArea — guía de uso\n\nÁrea de scroll estilizada con barras de scroll personalizadas.\nBasado en `@radix-ui/react-scroll-area`. El componente vive en tu proyecto: instálalo con `create-lexy add scroll-area` y edítalo con libertad.\n\n## Import\n\n```tsx\nimport { ScrollArea } from \"@/shared/components/base/ScrollArea\"; // o @/components/base/ScrollArea\n```\n\n## Uso básico\n\n```tsx\n<ScrollArea className=\"h-72 w-72 rounded-md border border-border p-4\">\n <div className=\"space-y-4\">\n {items.map((item) => (\n <div key={item.id}>\n <div className=\"font-medium\">{item.title}</div>\n <div className=\"text-muted-foreground\">{item.description}</div>\n </div>\n ))}\n </div>\n</ScrollArea>\n```\n\n## Scroll horizontal\n\n```tsx\n<ScrollArea className=\"w-96 rounded-md border border-border p-4\">\n <div className=\"flex w-max gap-4\">\n {items.map((item) => (\n <div key={item.id} className=\"w-32 shrink-0\">\n {item.title}\n </div>\n ))}\n </div>\n</ScrollArea>\n```\n\n## Props\n\n| Componente | Props | Descripción |\n|---|---|---|\n| `ScrollArea` | `className?`, `children`, `...` | Contenedor con scroll. |\n| `ScrollBar` | `className?`, `orientation?` | Barra de scroll. Renderiza automáticamente. |\n\n## Reglas\n\n- Define una altura o ancho fijo para que el scroll funcione.\n- El contenido debe ser más grande que el contenedor para que aparezca scroll.\n- No envuelvas toda la página en `ScrollArea` → usa scroll nativo del body.\n- No anides múltiples `ScrollArea`.\n\n## Para IA\n\n1. Identifica si el contenido excede el espacio disponible.\n2. Si necesitas scroll estilizado → usa `ScrollArea`.\n3. Define `height` y/o `width` en el contenedor.\n4. No uses `ScrollArea` para scroll de página completa.\n"
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "searchbox",
|
|
4
|
+
"type": "registry:ui",
|
|
5
|
+
"title": "Searchbox",
|
|
6
|
+
"description": "Campo de búsqueda con icono. Para búsquedas. El componente vive en tu proyecto: instálalo con `create-lexy add searchbox` y edítalo con libertad.",
|
|
7
|
+
"dependencies": [],
|
|
8
|
+
"registryDependencies": [],
|
|
9
|
+
"files": [
|
|
10
|
+
{
|
|
11
|
+
"path": "components/base/Searchbox.tsx",
|
|
12
|
+
"type": "registry:ui",
|
|
13
|
+
"content": "import * as React from \"react\";\n\nimport { cn } from \"@/lib/utils/cn\";\n\nexport interface SearchboxProps\n extends Omit<React.ComponentProps<\"input\">, \"type\"> {\n /** Cuando se define, muestra un botón (×) para limpiar el campo. */\n onClear?: () => void;\n}\n\nconst Searchbox = React.forwardRef<HTMLInputElement, SearchboxProps>(\n ({ className, onClear, value, disabled, ...props }, ref) => {\n const hasValue = value !== undefined && String(value) !== \"\";\n const showClear = !!onClear && hasValue && !disabled;\n\n return (\n <div className={cn(\"relative flex w-full items-center\", className)}>\n <svg\n className='pointer-events-none absolute left-3 h-4 w-4 text-muted-foreground'\n viewBox='0 0 24 24'\n fill='none'\n stroke='currentColor'\n strokeWidth='2'\n strokeLinecap='round'\n strokeLinejoin='round'>\n <circle cx='11' cy='11' r='8' />\n <path d='m21 21-4.3-4.3' />\n </svg>\n <input\n ref={ref}\n type='search'\n role='searchbox'\n value={value}\n disabled={disabled}\n className={cn(\n \"flex h-10 w-full rounded border border-input bg-background pl-9 pr-9 text-sm text-foreground outline-none transition-colors duration-150\",\n \"placeholder:text-muted-foreground\",\n \"hover:border-muted-foreground/40 focus-visible:border-primary\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n \"[&::-webkit-search-cancel-button]:appearance-none\",\n )}\n {...props}\n />\n {showClear && (\n <button\n type='button'\n onClick={onClear}\n aria-label='Limpiar'\n className='absolute right-3 flex h-4 w-4 cursor-pointer items-center justify-center text-muted-foreground transition-colors hover:text-foreground'>\n <svg\n className='h-4 w-4'\n viewBox='0 0 24 24'\n fill='none'\n stroke='currentColor'\n strokeWidth='2'\n strokeLinecap='round'\n strokeLinejoin='round'>\n <path d='M18 6 6 18M6 6l12 12' />\n </svg>\n </button>\n )}\n </div>\n );\n },\n);\nSearchbox.displayName = \"Searchbox\";\n\nexport { Searchbox };\n"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"path": "components/base/Searchbox.md",
|
|
17
|
+
"type": "registry:file",
|
|
18
|
+
"target": "components/base/Searchbox.md",
|
|
19
|
+
"content": "# Searchbox — guía de uso\n\nCampo de búsqueda con icono. Para búsquedas. El componente vive en tu proyecto: instálalo con `create-lexy add searchbox` y edítalo con libertad.\n\n## Import\n\n```tsx\nimport { Searchbox } from \"@/components/base/Searchbox\";\n```\n\n## Uso básico\n\n```tsx\n<Searchbox />\n```\n\n## Props\n\nConsulta la story en Storybook para ver las props disponibles.\n\n## Reglas\n\n- Usa Searchbox según el propósito descrito.\n- No abuses de este componente en contexts donde no aplica.\n\n## Para IA\n\n1. Identifica el contexto de uso del componente.\n2. Importa Searchbox desde el path correcto.\n3. Configura las props según la necesidad.\n4. Verifica que el componente se integre correctamente en el layout.\n5. Solo usa variantes documentadas.\n"
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "select",
|
|
4
|
+
"type": "registry:ui",
|
|
5
|
+
"title": "Select",
|
|
6
|
+
"description": "Lista desplegable de selección **única** (Radix). Ahorra espacio cuando hay muchas opciones y no necesitas verlas todas a la vez. El componente vive en tu proyecto: instálalo con `create-lexy add select` y edítalo con libertad.",
|
|
7
|
+
"dependencies": [
|
|
8
|
+
"@radix-ui/react-select"
|
|
9
|
+
],
|
|
10
|
+
"registryDependencies": [],
|
|
11
|
+
"files": [
|
|
12
|
+
{
|
|
13
|
+
"path": "components/base/Select.tsx",
|
|
14
|
+
"type": "registry:ui",
|
|
15
|
+
"content": "import * as React from \"react\";\nimport * as SelectPrimitive from \"@radix-ui/react-select\";\n\nimport { cn } from \"@/lib/utils/cn\";\n\nconst Select = SelectPrimitive.Root;\nconst SelectGroup = SelectPrimitive.Group;\nconst SelectValue = SelectPrimitive.Value;\n\nconst SelectTrigger = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Trigger>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>\n>(({ className, children, ...props }, ref) => (\n <SelectPrimitive.Trigger\n ref={ref}\n className={cn(\n \"flex h-10 w-full items-center justify-between rounded border border-input bg-background px-3 py-2 text-sm text-foreground outline-none transition-colors duration-150 hover:border-muted-foreground/40 focus:border-primary disabled:cursor-not-allowed disabled:opacity-50 data-[placeholder]:text-muted-foreground [&>span]:line-clamp-1\",\n className,\n )}\n {...props}>\n {children}\n <SelectPrimitive.Icon asChild>\n <svg\n className='h-4 w-4 opacity-50 transition-transform duration-200 data-[state=open]:rotate-180'\n viewBox='0 0 24 24'\n fill='none'\n stroke='currentColor'\n strokeWidth='2'\n strokeLinecap='round'\n strokeLinejoin='round'>\n <path d='m6 9 6 6 6-6' />\n </svg>\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n));\nSelectTrigger.displayName = SelectPrimitive.Trigger.displayName;\n\nconst SelectScrollUpButton = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.ScrollUpButton\n ref={ref}\n className={cn(\"flex cursor-default items-center justify-center py-1\", className)}\n {...props}>\n <svg\n className='h-4 w-4'\n viewBox='0 0 24 24'\n fill='none'\n stroke='currentColor'\n strokeWidth='2'\n strokeLinecap='round'\n strokeLinejoin='round'>\n <path d='m18 15-6-6-6 6' />\n </svg>\n </SelectPrimitive.ScrollUpButton>\n));\nSelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;\n\nconst SelectScrollDownButton = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.ScrollDownButton\n ref={ref}\n className={cn(\"flex cursor-default items-center justify-center py-1\", className)}\n {...props}>\n <svg\n className='h-4 w-4'\n viewBox='0 0 24 24'\n fill='none'\n stroke='currentColor'\n strokeWidth='2'\n strokeLinecap='round'\n strokeLinejoin='round'>\n <path d='m6 9 6 6 6-6' />\n </svg>\n </SelectPrimitive.ScrollDownButton>\n));\nSelectScrollDownButton.displayName =\n SelectPrimitive.ScrollDownButton.displayName;\n\nconst SelectContent = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>\n>(({ className, children, position = \"popper\", ...props }, ref) => (\n <SelectPrimitive.Portal>\n <SelectPrimitive.Content\n ref={ref}\n className={cn(\n \"relative z-50 max-h-96 overflow-hidden rounded border border-border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2\",\n position === \"popper\" &&\n \"w-[var(--radix-select-trigger-width)] data-[side=bottom]:translate-y-1 data-[side=top]:-translate-y-1\",\n className,\n )}\n position={position}\n {...props}>\n <SelectScrollUpButton />\n <SelectPrimitive.Viewport\n className={cn(\n \"p-1\",\n position === \"popper\" &&\n \"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]\",\n )}>\n {children}\n </SelectPrimitive.Viewport>\n <SelectScrollDownButton />\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n));\nSelectContent.displayName = SelectPrimitive.Content.displayName;\n\nconst SelectLabel = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Label>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.Label\n ref={ref}\n className={cn(\n \"px-2 py-1.5 text-xs font-medium text-muted-foreground\",\n className,\n )}\n {...props}\n />\n));\nSelectLabel.displayName = SelectPrimitive.Label.displayName;\n\nconst SelectItem = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Item>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>\n>(({ className, children, ...props }, ref) => (\n <SelectPrimitive.Item\n ref={ref}\n className={cn(\n \"relative flex w-full cursor-pointer select-none items-center rounded py-1.5 pl-8 pr-2 text-sm text-foreground outline-none focus:bg-primary/15 focus:text-primary data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n className,\n )}\n {...props}>\n <span className='absolute left-2 flex h-3.5 w-3.5 items-center justify-center'>\n <SelectPrimitive.ItemIndicator>\n <svg\n className='h-4 w-4'\n viewBox='0 0 24 24'\n fill='none'\n stroke='currentColor'\n strokeWidth='2'\n strokeLinecap='round'\n strokeLinejoin='round'>\n <path d='M20 6 9 17l-5-5' />\n </svg>\n </SelectPrimitive.ItemIndicator>\n </span>\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n </SelectPrimitive.Item>\n));\nSelectItem.displayName = SelectPrimitive.Item.displayName;\n\nconst SelectSeparator = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Separator>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.Separator\n ref={ref}\n className={cn(\"-mx-1 my-1 h-px bg-border\", className)}\n {...props}\n />\n));\nSelectSeparator.displayName = SelectPrimitive.Separator.displayName;\n\nexport {\n Select,\n SelectGroup,\n SelectValue,\n SelectTrigger,\n SelectContent,\n SelectLabel,\n SelectItem,\n SelectSeparator,\n SelectScrollUpButton,\n SelectScrollDownButton,\n};\n"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"path": "components/base/Select.md",
|
|
19
|
+
"type": "registry:file",
|
|
20
|
+
"target": "components/base/Select.md",
|
|
21
|
+
"content": "# Select — guía de uso\n\nLista desplegable de selección **única** (Radix). Ahorra espacio cuando hay muchas opciones y no necesitas verlas todas a la vez. El componente vive en tu proyecto: instálalo con `create-lexy add select` y edítalo con libertad.\n\n## Import\n\n```tsx\nimport {\n Select, SelectTrigger, SelectValue, SelectContent, SelectItem,\n} from \"@/components/base/Select\";\n```\n\n## Uso básico\n\n```tsx\n<div className=\"grid gap-1.5\">\n <Label htmlFor=\"region\">Región</Label>\n <Select onValueChange={setRegion}>\n <SelectTrigger id=\"region\">\n <SelectValue placeholder=\"Selecciona una región\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"rm\">Metropolitana</SelectItem>\n <SelectItem value=\"v\">Valparaíso</SelectItem>\n <SelectItem value=\"viii\">Biobío</SelectItem>\n </SelectContent>\n </Select>\n</div>\n```\n\n## Con grupos\n\n```tsx\n<SelectContent>\n <SelectGroup>\n <SelectLabel>Zona norte</SelectLabel>\n <SelectItem value=\"i\">Tarapacá</SelectItem>\n </SelectGroup>\n <SelectSeparator />\n <SelectGroup>\n <SelectLabel>Zona centro</SelectLabel>\n <SelectItem value=\"rm\">Metropolitana</SelectItem>\n </SelectGroup>\n</SelectContent>\n```\n\n## Exports\n\n`Select`, `SelectTrigger`, `SelectValue`, `SelectContent`, `SelectItem`, `SelectGroup`, `SelectLabel`, `SelectSeparator`, `SelectScrollUpButton`, `SelectScrollDownButton`.\n\n## Props (esenciales)\n\n| Parte | Prop | Descripción |\n|---|---|---|\n| `Select` | `value` / `defaultValue` | Selección controlada / inicial. |\n| `Select` | `onValueChange` | Cambio de selección. |\n| `SelectTrigger` | `id` | Para asociar el `Label`. |\n| `SelectValue` | `placeholder` | Texto cuando no hay selección. |\n| `SelectItem` | `value` | **Requerido.** Valor de la opción. |\n\n## Reglas\n\n- Asocia el `SelectTrigger` con un `Label` (`id`/`htmlFor`).\n- Usa `SelectValue` con `placeholder` claro; no dejes el trigger vacío.\n- Agrupa con `SelectGroup`/`SelectLabel` si hay categorías.\n- Cada `SelectItem` necesita `value` único.\n\n## Cuándo NO usar\n\n- **Pocas opciones (2–4) que conviene ver siempre** → `RadioGroup`.\n- **Necesitas escribir para filtrar** → `Combobox`.\n- **Selección múltiple** → `Checkbox` o `Combobox` con multiselección (no este Select).\n- **Acciones (no selección de valor)** → `DropdownMenu`.\n\n## Para IA\n\n1. Confirma que es selección única de un valor de formulario (no acciones, no multiselección).\n2. Si son ≤4 opciones siempre visibles, considera `RadioGroup`; si requieren búsqueda, `Combobox`.\n3. Arma `Select > SelectTrigger (con SelectValue) > SelectContent > SelectItem`.\n4. Asocia el trigger con un `Label`.\n5. Controla con `value`/`onValueChange` cuando alimente un formulario.\n"
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "separator",
|
|
4
|
+
"type": "registry:ui",
|
|
5
|
+
"title": "Separator",
|
|
6
|
+
"description": "Separador visual. Para dividir secciones. El componente vive en tu proyecto: instálalo con `create-lexy add separator` y edítalo con libertad.",
|
|
7
|
+
"dependencies": [
|
|
8
|
+
"@radix-ui/react-separator"
|
|
9
|
+
],
|
|
10
|
+
"registryDependencies": [],
|
|
11
|
+
"files": [
|
|
12
|
+
{
|
|
13
|
+
"path": "components/base/Separator.tsx",
|
|
14
|
+
"type": "registry:ui",
|
|
15
|
+
"content": "import * as React from \"react\";\nimport * as SeparatorPrimitive from \"@radix-ui/react-separator\";\n\nimport { cn } from \"@/lib/utils/cn\";\n\nconst Separator = React.forwardRef<\n React.ElementRef<typeof SeparatorPrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>\n>(\n (\n { className, orientation = \"horizontal\", decorative = true, ...props },\n ref,\n ) => (\n <SeparatorPrimitive.Root\n ref={ref}\n decorative={decorative}\n orientation={orientation}\n className={cn(\n \"shrink-0 bg-border\",\n orientation === \"horizontal\" ? \"h-px w-full\" : \"h-full w-px\",\n className,\n )}\n {...props}\n />\n ),\n);\nSeparator.displayName = SeparatorPrimitive.Root.displayName;\n\nexport { Separator };\n"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"path": "components/base/Separator.md",
|
|
19
|
+
"type": "registry:file",
|
|
20
|
+
"target": "components/base/Separator.md",
|
|
21
|
+
"content": "# Separator — guía de uso\n\nSeparador visual. Para dividir secciones. El componente vive en tu proyecto: instálalo con `create-lexy add separator` y edítalo con libertad.\n\n## Import\n\n```tsx\nimport { Separator } from \"@/components/base/Separator\";\n```\n\n## Uso básico\n\n```tsx\n<Separator />\n```\n\n## Props\n\nConsulta la story en Storybook para ver las props disponibles.\n\n## Reglas\n\n- Usa Separator según el propósito descrito.\n- No abuses de este componente en contexts donde no aplica.\n\n## Para IA\n\n1. Identifica el contexto de uso del componente.\n2. Importa Separator desde el path correcto.\n3. Configura las props según la necesidad.\n4. Verifica que el componente se integre correctamente en el layout.\n5. Solo usa variantes documentadas.\n"
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "sheet",
|
|
4
|
+
"type": "registry:ui",
|
|
5
|
+
"title": "Sheet",
|
|
6
|
+
"description": "Panel lateral deslizante. Alternativa a `Dialog` para contenido secundario que",
|
|
7
|
+
"dependencies": [
|
|
8
|
+
"@radix-ui/react-dialog",
|
|
9
|
+
"class-variance-authority"
|
|
10
|
+
],
|
|
11
|
+
"registryDependencies": [],
|
|
12
|
+
"files": [
|
|
13
|
+
{
|
|
14
|
+
"path": "components/base/Sheet.tsx",
|
|
15
|
+
"type": "registry:ui",
|
|
16
|
+
"content": "import * as React from \"react\";\nimport * as SheetPrimitive from \"@radix-ui/react-dialog\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"@/lib/utils/cn\";\n\nconst Sheet = SheetPrimitive.Root;\nconst SheetTrigger = SheetPrimitive.Trigger;\nconst SheetClose = SheetPrimitive.Close;\nconst SheetPortal = SheetPrimitive.Portal;\n\nconst SheetOverlay = React.forwardRef<\n React.ElementRef<typeof SheetPrimitive.Overlay>,\n React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n <SheetPrimitive.Overlay\n ref={ref}\n className={cn(\n \"fixed inset-0 z-50 bg-black/50 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n className,\n )}\n {...props}\n />\n));\nSheetOverlay.displayName = SheetPrimitive.Overlay.displayName;\n\nconst sheetVariants = cva(\n \"fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500\",\n {\n variants: {\n side: {\n top: \"inset-x-0 top-0 border-b border-border data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top\",\n bottom:\n \"inset-x-0 bottom-0 border-t border-border data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom\",\n left: \"inset-y-0 left-0 h-full w-3/4 border-r border-border data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm\",\n right:\n \"inset-y-0 right-0 h-full w-3/4 border-l border-border data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm\",\n },\n },\n defaultVariants: { side: \"right\" },\n },\n);\n\ninterface SheetContentProps\n extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,\n VariantProps<typeof sheetVariants> {}\n\nconst SheetContent = React.forwardRef<\n React.ElementRef<typeof SheetPrimitive.Content>,\n SheetContentProps\n>(({ side = \"right\", className, children, ...props }, ref) => (\n <SheetPortal>\n <SheetOverlay />\n <SheetPrimitive.Content\n ref={ref}\n className={cn(sheetVariants({ side }), className)}\n {...props}>\n {children}\n <SheetPrimitive.Close className='absolute right-4 top-4 cursor-pointer rounded opacity-70 transition-opacity hover:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none'>\n <svg\n className='h-4 w-4'\n viewBox='0 0 24 24'\n fill='none'\n stroke='currentColor'\n strokeWidth='2'\n strokeLinecap='round'\n strokeLinejoin='round'>\n <path d='M18 6 6 18M6 6l12 12' />\n </svg>\n <span className='sr-only'>Cerrar</span>\n </SheetPrimitive.Close>\n </SheetPrimitive.Content>\n </SheetPortal>\n));\nSheetContent.displayName = SheetPrimitive.Content.displayName;\n\nconst SheetHeader = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n \"flex flex-col space-y-2 text-center sm:text-left\",\n className,\n )}\n {...props}\n />\n);\nSheetHeader.displayName = \"SheetHeader\";\n\nconst SheetFooter = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n \"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2\",\n className,\n )}\n {...props}\n />\n);\nSheetFooter.displayName = \"SheetFooter\";\n\nconst SheetTitle = React.forwardRef<\n React.ElementRef<typeof SheetPrimitive.Title>,\n React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <SheetPrimitive.Title\n ref={ref}\n className={cn(\"text-lg font-semibold text-foreground\", className)}\n {...props}\n />\n));\nSheetTitle.displayName = SheetPrimitive.Title.displayName;\n\nconst SheetDescription = React.forwardRef<\n React.ElementRef<typeof SheetPrimitive.Description>,\n React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <SheetPrimitive.Description\n ref={ref}\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n));\nSheetDescription.displayName = SheetPrimitive.Description.displayName;\n\nexport {\n Sheet,\n SheetPortal,\n SheetOverlay,\n SheetTrigger,\n SheetClose,\n SheetContent,\n SheetHeader,\n SheetFooter,\n SheetTitle,\n SheetDescription,\n};\n"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"path": "components/base/Sheet.md",
|
|
20
|
+
"type": "registry:file",
|
|
21
|
+
"target": "components/base/Sheet.md",
|
|
22
|
+
"content": "# Sheet — guía de uso\n\nPanel lateral deslizante. Alternativa a `Dialog` para contenido secundario que\nrequiere más espacio o contexto persistente. El componente vive en tu proyecto: instálalo con `create-lexy add sheet` y edítalo con libertad.\n\n## Import\n\n```tsx\nimport {\n Sheet,\n SheetTrigger,\n SheetContent,\n SheetHeader,\n SheetTitle,\n SheetDescription,\n SheetFooter,\n SheetClose,\n} from \"@/components/base/Sheet\";\n```\n\n## Uso básico\n\n```tsx\n<Sheet>\n <SheetTrigger asChild>\n <Button variant=\"outline\">Abrir panel</Button>\n </SheetTrigger>\n <SheetContent>\n <SheetHeader>\n <SheetTitle>Configuración</SheetTitle>\n <SheetDescription>Ajusta las preferencias de tu cuenta.</SheetDescription>\n </SheetHeader>\n <div className=\"py-4\">{/* formulario */}</div>\n <SheetFooter>\n <SheetClose asChild>\n <Button variant=\"ghost\">Cerrar</Button>\n </SheetClose>\n </SheetFooter>\n </SheetContent>\n</Sheet>\n```\n\n## Props\n\n### `SheetContent`\n\n| Prop | Tipo | Default | Descripción |\n|---|---|---|---|\n| `side` | `\"top\" \\| \"bottom\" \\| \"left\" \\| \"right\"` | `\"right\"` | Desde qué borde aparece el panel. |\n| `className` | `string?` | — | Clase adicional. |\n\n### `Sheet` (Root)\n\n| Prop | Tipo | Descripción |\n|---|---|---|\n| `open` | `boolean?` | Estado controlado. |\n| `onOpenChange` | `(boolean) => void` | Callback de cambio. |\n\n## Reglas\n\n- Usa `Sheet` para contenido secundario que necesita más espacio que un `Dialog`.\n- No uses `Sheet` para confirmaciones simples; usa `Dialog` o `AppDialog`.\n- El panel tiene ancho máximo `sm:max-w-sm` en lados (responsive).\n- Incluye `SheetHeader` + `SheetTitle` para accesibilidad.\n- Usa `side=\"bottom\"` para layouts mobile.\n\n## Para IA\n\n1. Identifica si el contenido es secundario y necesita más espacio que un `Dialog`.\n2. Si es secundario/formulario largo → usa `Sheet`.\n3. Elige el `side` apropiado (`right` para desktop, `bottom` para mobile).\n4. Incluye `SheetHeader` + `SheetTitle` para accesibilidad.\n5. Usa `SheetFooter` para botones de acción al final.\n6. Solo usa `Dialog` para confirmaciones simples o alertas.\n"
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "sidebar",
|
|
4
|
+
"type": "registry:ui",
|
|
5
|
+
"title": "Sidebar",
|
|
6
|
+
"description": "Primitivas internas (estilo shadcn) que usa `AppSidebar` por debajo. **No las compongas a mano para una barra de navegación: usa `AppSidebar`** (data-driven). Recurre a estas primitivas solo para layouts a medida. El componente vive en tu proyecto: instálalo con `create-lexy add sidebar` y edítalo con libertad.",
|
|
7
|
+
"dependencies": [
|
|
8
|
+
"@radix-ui/react-slot",
|
|
9
|
+
"class-variance-authority",
|
|
10
|
+
"lucide-react"
|
|
11
|
+
],
|
|
12
|
+
"registryDependencies": [
|
|
13
|
+
"button",
|
|
14
|
+
"input",
|
|
15
|
+
"separator",
|
|
16
|
+
"sheet",
|
|
17
|
+
"skeleton",
|
|
18
|
+
"tooltip"
|
|
19
|
+
],
|
|
20
|
+
"files": [
|
|
21
|
+
{
|
|
22
|
+
"path": "components/base/Sidebar.tsx",
|
|
23
|
+
"type": "registry:ui",
|
|
24
|
+
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { PanelLeft } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils/cn\"\nimport { Button } from \"./Button\"\nimport { Input } from \"./Input\"\nimport { Separator } from \"./Separator\"\nimport {\n Sheet,\n SheetContent,\n SheetDescription,\n SheetHeader,\n SheetTitle,\n} from \"./Sheet\"\nimport { Skeleton } from \"./Skeleton\"\nimport {\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n} from \"./Tooltip\"\n\nconst SIDEBAR_COOKIE_NAME = \"sidebar_state\"\nconst SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7\nconst SIDEBAR_WIDTH = \"20rem\"\nconst SIDEBAR_WIDTH_MOBILE = \"20rem\"\nconst SIDEBAR_WIDTH_ICON = \"4rem\"\nconst SIDEBAR_KEYBOARD_SHORTCUT = \"b\"\nconst MOBILE_BREAKPOINT = 768\n\nfunction useIsMobile() {\n const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)\n\n React.useEffect(() => {\n const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)\n const onChange = () => setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)\n mql.addEventListener(\"change\", onChange)\n setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)\n return () => mql.removeEventListener(\"change\", onChange)\n }, [])\n\n return !!isMobile\n}\n\ntype SidebarContextProps = {\n state: \"expanded\" | \"collapsed\"\n open: boolean\n setOpen: (open: boolean) => void\n openMobile: boolean\n setOpenMobile: (open: boolean) => void\n isMobile: boolean\n toggleSidebar: () => void\n}\n\nconst SidebarContext = React.createContext<SidebarContextProps | null>(null)\n\nfunction useSidebar() {\n const context = React.useContext(SidebarContext)\n if (!context) {\n throw new Error(\"useSidebar must be used within a SidebarProvider.\")\n }\n\n return context\n}\n\nconst SidebarProvider = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\"> & {\n defaultOpen?: boolean\n open?: boolean\n onOpenChange?: (open: boolean) => void\n }\n>(\n (\n {\n defaultOpen = true,\n open: openProp,\n onOpenChange: setOpenProp,\n className,\n style,\n children,\n ...props\n },\n ref\n ) => {\n const isMobile = useIsMobile()\n const [openMobile, setOpenMobile] = React.useState(false)\n\n // This is the internal state of the sidebar.\n // We use openProp and setOpenProp for control from outside the component.\n const [_open, _setOpen] = React.useState(defaultOpen)\n const open = openProp ?? _open\n const setOpen = React.useCallback(\n (value: boolean | ((value: boolean) => boolean)) => {\n const openState = typeof value === \"function\" ? value(open) : value\n if (setOpenProp) {\n setOpenProp(openState)\n } else {\n _setOpen(openState)\n }\n\n // This sets the cookie to keep the sidebar state.\n document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`\n },\n [setOpenProp, open]\n )\n\n // Helper to toggle the sidebar.\n const toggleSidebar = React.useCallback(() => {\n return isMobile\n ? setOpenMobile((open) => !open)\n : setOpen((open) => !open)\n }, [isMobile, setOpen, setOpenMobile])\n\n // Adds a keyboard shortcut to toggle the sidebar.\n React.useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n if (\n event.key === SIDEBAR_KEYBOARD_SHORTCUT &&\n (event.metaKey || event.ctrlKey)\n ) {\n event.preventDefault()\n toggleSidebar()\n }\n }\n\n window.addEventListener(\"keydown\", handleKeyDown)\n return () => window.removeEventListener(\"keydown\", handleKeyDown)\n }, [toggleSidebar])\n\n // We add a state so that we can do data-state=\"expanded\" or \"collapsed\".\n // This makes it easier to style the sidebar with Tailwind classes.\n const state = open ? \"expanded\" : \"collapsed\"\n\n const contextValue = React.useMemo<SidebarContextProps>(\n () => ({\n state,\n open,\n setOpen,\n isMobile,\n openMobile,\n setOpenMobile,\n toggleSidebar,\n }),\n [state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar]\n )\n\n return (\n <SidebarContext.Provider value={contextValue}>\n <TooltipProvider delayDuration={0}>\n <div\n style={\n {\n \"--sidebar-width\": SIDEBAR_WIDTH,\n \"--sidebar-width-icon\": SIDEBAR_WIDTH_ICON,\n ...style,\n } as React.CSSProperties\n }\n className={cn(\n \"group/sidebar-wrapper flex min-h-svh w-full has-[[data-variant=inset]]:bg-sidebar\",\n className\n )}\n ref={ref}\n {...props}\n >\n {children}\n </div>\n </TooltipProvider>\n </SidebarContext.Provider>\n )\n }\n)\nSidebarProvider.displayName = \"SidebarProvider\"\n\nconst Sidebar = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\"> & {\n side?: \"left\" | \"right\"\n variant?: \"sidebar\" | \"floating\" | \"inset\"\n collapsible?: \"offcanvas\" | \"icon\" | \"none\"\n }\n>(\n (\n {\n side = \"left\",\n variant = \"sidebar\",\n collapsible = \"offcanvas\",\n className,\n children,\n ...props\n },\n ref\n ) => {\n const { isMobile, state, openMobile, setOpenMobile } = useSidebar()\n\n if (collapsible === \"none\") {\n return (\n <div\n className={cn(\n \"flex h-full w-(--sidebar-width) flex-col bg-sidebar text-sidebar-foreground\",\n className\n )}\n ref={ref}\n {...props}\n >\n {children}\n </div>\n )\n }\n\n if (isMobile) {\n return (\n <Sheet open={openMobile} onOpenChange={setOpenMobile} {...props}>\n <SheetContent\n data-sidebar=\"sidebar\"\n data-mobile=\"true\"\n className=\"w-(--sidebar-width) bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden\"\n style={\n {\n \"--sidebar-width\": SIDEBAR_WIDTH_MOBILE,\n } as React.CSSProperties\n }\n side={side}\n >\n <SheetHeader className=\"sr-only\">\n <SheetTitle>Sidebar</SheetTitle>\n <SheetDescription>Displays the mobile sidebar.</SheetDescription>\n </SheetHeader>\n <div className=\"flex h-full w-full flex-col\">{children}</div>\n </SheetContent>\n </Sheet>\n )\n }\n\n return (\n <div\n ref={ref}\n className=\"group peer hidden text-sidebar-foreground md:block\"\n data-state={state}\n data-collapsible={state === \"collapsed\" ? collapsible : \"\"}\n data-variant={variant}\n data-side={side}\n >\n {/* This is what handles the sidebar gap on desktop */}\n <div\n className={cn(\n \"relative h-svh w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear\",\n \"group-data-[collapsible=offcanvas]:w-0\",\n \"group-data-[side=right]:rotate-180\",\n variant === \"floating\" || variant === \"inset\"\n ? \"group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4))]\"\n : \"group-data-[collapsible=icon]:w-(--sidebar-width-icon)\"\n )}\n />\n <div\n className={cn(\n \"fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex\",\n side === \"left\"\n ? \"left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]\"\n : \"right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]\",\n // Adjust the padding for floating and inset variants.\n variant === \"floating\" || variant === \"inset\"\n ? \"p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4)_+2px)]\"\n : \"group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l border-sidebar-border\",\n className\n )}\n {...props}\n >\n <div\n data-sidebar=\"sidebar\"\n className=\"flex h-full w-full flex-col bg-sidebar group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:border-sidebar-border group-data-[variant=floating]:shadow\"\n >\n {children}\n </div>\n </div>\n </div>\n )\n }\n)\nSidebar.displayName = \"Sidebar\"\n\nconst SidebarTrigger = React.forwardRef<\n React.ElementRef<typeof Button>,\n React.ComponentProps<typeof Button>\n>(({ className, onClick, ...props }, ref) => {\n const { toggleSidebar } = useSidebar()\n\n return (\n <Button\n ref={ref}\n data-sidebar=\"trigger\"\n variant=\"ghost\"\n size=\"icon\"\n className={cn(\"h-8 w-8 rounded-md hover:bg-secondary\", className)}\n onClick={(event) => {\n onClick?.(event)\n toggleSidebar()\n }}\n {...props}\n >\n <PanelLeft />\n <span className=\"sr-only\">Toggle Sidebar</span>\n </Button>\n )\n})\nSidebarTrigger.displayName = \"SidebarTrigger\"\n\nconst SidebarRail = React.forwardRef<\n HTMLButtonElement,\n React.ComponentProps<\"button\">\n>(({ className, ...props }, ref) => {\n const { toggleSidebar } = useSidebar()\n\n return (\n <button\n ref={ref}\n data-sidebar=\"rail\"\n aria-label=\"Toggle Sidebar\"\n tabIndex={-1}\n onClick={toggleSidebar}\n title=\"Toggle Sidebar\"\n className={cn(\n \"absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear after:absolute after:inset-y-0 after:left-1/2 after:w-0.5 hover:after:bg-sidebar-border group-data-[side=left]:-right-4 group-data-[side=right]:left-0 sm:flex\",\n \"[[data-side=left]_&]:cursor-w-resize [[data-side=right]_&]:cursor-e-resize\",\n \"[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize\",\n \"group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full group-data-[collapsible=offcanvas]:hover:bg-sidebar\",\n \"[[data-side=left][data-collapsible=offcanvas]_&]:-right-2\",\n \"[[data-side=right][data-collapsible=offcanvas]_&]:-left-2\",\n className\n )}\n {...props}\n />\n )\n})\nSidebarRail.displayName = \"SidebarRail\"\n\nconst SidebarInset = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"main\">\n>(({ className, ...props }, ref) => {\n return (\n <main\n ref={ref}\n className={cn(\n \"relative flex w-full flex-1 flex-col bg-background\",\n \"md:peer-data-[variant=inset]:m-2 md:peer-data-[state=collapsed]:peer-data-[variant=inset]:ml-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-lg md:peer-data-[variant=inset]:shadow\",\n className\n )}\n {...props}\n />\n )\n})\nSidebarInset.displayName = \"SidebarInset\"\n\nconst SidebarInput = React.forwardRef<\n React.ElementRef<typeof Input>,\n React.ComponentProps<typeof Input>\n>(({ className, ...props }, ref) => {\n return (\n <Input\n ref={ref}\n data-sidebar=\"input\"\n className={cn(\n \"h-8 w-full bg-background shadow-none focus-visible:ring-2 focus-visible:ring-sidebar-ring\",\n className\n )}\n {...props}\n />\n )\n})\nSidebarInput.displayName = \"SidebarInput\"\n\nconst SidebarHeader = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\">\n>(({ className, ...props }, ref) => {\n return (\n <div\n ref={ref}\n data-sidebar=\"header\"\n className={cn(\"flex flex-col gap-2 p-2\", className)}\n {...props}\n />\n )\n})\nSidebarHeader.displayName = \"SidebarHeader\"\n\nconst SidebarFooter = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\">\n>(({ className, ...props }, ref) => {\n return (\n <div\n ref={ref}\n data-sidebar=\"footer\"\n className={cn(\"flex flex-col gap-2 p-2\", className)}\n {...props}\n />\n )\n})\nSidebarFooter.displayName = \"SidebarFooter\"\n\nconst SidebarSeparator = React.forwardRef<\n React.ElementRef<typeof Separator>,\n React.ComponentProps<typeof Separator>\n>(({ className, ...props }, ref) => {\n return (\n <Separator\n ref={ref}\n data-sidebar=\"separator\"\n className={cn(\"mx-2 w-auto bg-sidebar-border\", className)}\n {...props}\n />\n )\n})\nSidebarSeparator.displayName = \"SidebarSeparator\"\n\nconst SidebarContent = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\">\n>(({ className, ...props }, ref) => {\n return (\n <div\n ref={ref}\n data-sidebar=\"content\"\n className={cn(\n \"flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden [&::-webkit-scrollbar]:w-1 [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-border/50\",\n className\n )}\n {...props}\n />\n )\n})\nSidebarContent.displayName = \"SidebarContent\"\n\nconst SidebarGroup = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\">\n>(({ className, ...props }, ref) => {\n return (\n <div\n ref={ref}\n data-sidebar=\"group\"\n className={cn(\"relative flex w-full min-w-0 flex-col p-2\", className)}\n {...props}\n />\n )\n})\nSidebarGroup.displayName = \"SidebarGroup\"\n\nconst SidebarGroupLabel = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\"> & { asChild?: boolean }\n>(({ className, asChild = false, ...props }, ref) => {\n const Comp = asChild ? Slot : \"div\"\n\n return (\n <Comp\n ref={ref}\n data-sidebar=\"group-label\"\n className={cn(\n \"flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 outline-none ring-sidebar-ring transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0\",\n \"group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0\",\n className\n )}\n {...props}\n />\n )\n})\nSidebarGroupLabel.displayName = \"SidebarGroupLabel\"\n\nconst SidebarGroupAction = React.forwardRef<\n HTMLButtonElement,\n React.ComponentProps<\"button\"> & { asChild?: boolean }\n>(({ className, asChild = false, ...props }, ref) => {\n const Comp = asChild ? Slot : \"button\"\n\n return (\n <Comp\n ref={ref}\n data-sidebar=\"group-action\"\n className={cn(\n \"absolute right-3 top-3.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground outline-none ring-sidebar-ring transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0\",\n // Increases the hit area of the button on mobile.\n \"after:absolute after:-inset-2 after:md:hidden\",\n \"group-data-[collapsible=icon]:hidden\",\n className\n )}\n {...props}\n />\n )\n})\nSidebarGroupAction.displayName = \"SidebarGroupAction\"\n\nconst SidebarGroupContent = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\">\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n data-sidebar=\"group-content\"\n className={cn(\"w-full text-sm\", className)}\n {...props}\n />\n))\nSidebarGroupContent.displayName = \"SidebarGroupContent\"\n\nconst SidebarMenu = React.forwardRef<\n HTMLUListElement,\n React.ComponentProps<\"ul\">\n>(({ className, ...props }, ref) => (\n <ul\n ref={ref}\n data-sidebar=\"menu\"\n className={cn(\"flex w-full min-w-0 flex-col gap-1\", className)}\n {...props}\n />\n))\nSidebarMenu.displayName = \"SidebarMenu\"\n\nconst SidebarMenuItem = React.forwardRef<\n HTMLLIElement,\n React.ComponentProps<\"li\">\n>(({ className, ...props }, ref) => (\n <li\n ref={ref}\n data-sidebar=\"menu-item\"\n className={cn(\"group/menu-item relative\", className)}\n {...props}\n />\n))\nSidebarMenuItem.displayName = \"SidebarMenuItem\"\n\nconst sidebarMenuButtonVariants = cva(\n \"peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-none ring-sidebar-ring transition-[color,background-color,width,height,padding] duration-200 hover:bg-secondary hover:text-sidebar-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-[[data-sidebar=menu-action]]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-secondary data-[state=open]:hover:text-sidebar-foreground group-data-[collapsible=icon]:!size-8 group-data-[collapsible=icon]:!p-2 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0\",\n {\n variants: {\n variant: {\n default: \"hover:bg-secondary hover:text-sidebar-foreground\",\n outline:\n \"bg-background shadow-[0_0_0_1px_var(--color-sidebar-border)] hover:bg-secondary hover:text-sidebar-foreground hover:shadow-[0_0_0_1px_var(--color-sidebar-border)]\",\n },\n size: {\n default: \"h-8 text-sm\",\n sm: \"h-7 text-xs\",\n lg: \"h-12 text-sm group-data-[collapsible=icon]:!p-0\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n)\n\nconst SidebarMenuButton = React.forwardRef<\n HTMLButtonElement,\n React.ComponentProps<\"button\"> & {\n asChild?: boolean\n isActive?: boolean\n tooltip?: string | React.ComponentProps<typeof TooltipContent>\n } & VariantProps<typeof sidebarMenuButtonVariants>\n>(\n (\n {\n asChild = false,\n isActive = false,\n variant = \"default\",\n size = \"default\",\n tooltip,\n className,\n ...props\n },\n ref\n ) => {\n const Comp = asChild ? Slot : \"button\"\n const { isMobile, state } = useSidebar()\n\n const button = (\n <Comp\n ref={ref}\n data-sidebar=\"menu-button\"\n data-size={size}\n data-active={isActive}\n className={cn(sidebarMenuButtonVariants({ variant, size }), className)}\n {...props}\n />\n )\n\n if (!tooltip) {\n return button\n }\n\n if (typeof tooltip === \"string\") {\n tooltip = {\n children: tooltip,\n }\n }\n\n return (\n <Tooltip>\n <TooltipTrigger asChild>{button}</TooltipTrigger>\n <TooltipContent\n side=\"right\"\n align=\"center\"\n hidden={state !== \"collapsed\" || isMobile}\n {...tooltip}\n />\n </Tooltip>\n )\n }\n)\nSidebarMenuButton.displayName = \"SidebarMenuButton\"\n\nconst SidebarMenuAction = React.forwardRef<\n HTMLButtonElement,\n React.ComponentProps<\"button\"> & {\n asChild?: boolean\n showOnHover?: boolean\n }\n>(({ className, asChild = false, showOnHover = false, ...props }, ref) => {\n const Comp = asChild ? Slot : \"button\"\n\n return (\n <Comp\n ref={ref}\n data-sidebar=\"menu-action\"\n className={cn(\n \"absolute right-1 top-1.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground outline-none ring-sidebar-ring transition-colors hover:bg-secondary hover:text-sidebar-foreground focus-visible:ring-2 peer-hover/menu-button:text-sidebar-foreground [&>svg]:size-4 [&>svg]:shrink-0\",\n // Increases the hit area of the button on mobile.\n \"after:absolute after:-inset-2 after:md:hidden\",\n \"peer-data-[size=sm]/menu-button:top-1\",\n \"peer-data-[size=default]/menu-button:top-1.5\",\n \"peer-data-[size=lg]/menu-button:top-2.5\",\n \"group-data-[collapsible=icon]:hidden\",\n showOnHover &&\n \"group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 peer-data-[active=true]/menu-button:text-sidebar-accent-foreground md:opacity-0\",\n className\n )}\n {...props}\n />\n )\n})\nSidebarMenuAction.displayName = \"SidebarMenuAction\"\n\nconst SidebarMenuBadge = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\">\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n data-sidebar=\"menu-badge\"\n className={cn(\n \"pointer-events-none absolute right-1 flex h-5 min-w-5 select-none items-center justify-center rounded-md px-1 text-xs font-medium tabular-nums text-sidebar-foreground\",\n \"peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground\",\n \"peer-data-[size=sm]/menu-button:top-1\",\n \"peer-data-[size=default]/menu-button:top-1.5\",\n \"peer-data-[size=lg]/menu-button:top-2.5\",\n \"group-data-[collapsible=icon]:hidden\",\n className\n )}\n {...props}\n />\n))\nSidebarMenuBadge.displayName = \"SidebarMenuBadge\"\n\nconst SidebarMenuSkeleton = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<\"div\"> & {\n showIcon?: boolean\n }\n>(({ className, showIcon = false, ...props }, ref) => {\n // Random width between 50 to 90%.\n const width = React.useMemo(() => {\n return `${Math.floor(Math.random() * 40) + 50}%`\n }, [])\n\n return (\n <div\n ref={ref}\n data-sidebar=\"menu-skeleton\"\n className={cn(\"flex h-8 items-center gap-2 rounded-md px-2\", className)}\n {...props}\n >\n {showIcon && (\n <Skeleton\n className=\"size-4 rounded-md\"\n data-sidebar=\"menu-skeleton-icon\"\n />\n )}\n <Skeleton\n className=\"h-4 max-w-(--skeleton-width) flex-1\"\n data-sidebar=\"menu-skeleton-text\"\n style={\n {\n \"--skeleton-width\": width,\n } as React.CSSProperties\n }\n />\n </div>\n )\n})\nSidebarMenuSkeleton.displayName = \"SidebarMenuSkeleton\"\n\nconst SidebarMenuSub = React.forwardRef<\n HTMLUListElement,\n React.ComponentProps<\"ul\">\n>(({ className, ...props }, ref) => (\n <ul\n ref={ref}\n data-sidebar=\"menu-sub\"\n className={cn(\n \"mx-0 flex min-w-0 flex-col gap-1 px-0 py-0.5\",\n \"group-data-[collapsible=icon]:hidden\",\n className\n )}\n {...props}\n />\n))\nSidebarMenuSub.displayName = \"SidebarMenuSub\"\n\nconst SidebarMenuSubItem = React.forwardRef<\n HTMLLIElement,\n React.ComponentProps<\"li\">\n>(({ ...props }, ref) => <li ref={ref} {...props} />)\nSidebarMenuSubItem.displayName = \"SidebarMenuSubItem\"\n\nconst SidebarMenuSubButton = React.forwardRef<\n HTMLAnchorElement,\n React.ComponentProps<\"a\"> & {\n asChild?: boolean\n size?: \"sm\" | \"md\"\n isActive?: boolean\n }\n>(({ asChild = false, size = \"md\", isActive, className, ...props }, ref) => {\n const Comp = asChild ? Slot : \"a\"\n\n return (\n <Comp\n ref={ref}\n data-sidebar=\"menu-sub-button\"\n data-size={size}\n data-active={isActive}\n className={cn(\n \"flex h-8 min-w-0 items-center gap-2 overflow-hidden rounded-md px-2 text-sidebar-foreground/80 outline-none ring-sidebar-ring transition-colors duration-200 hover:bg-secondary hover:text-sidebar-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-70 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-muted-foreground\",\n \"data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground\",\n size === \"sm\" && \"text-xs\",\n size === \"md\" && \"text-sm\",\n \"group-data-[collapsible=icon]:hidden\",\n className\n )}\n {...props}\n />\n )\n})\nSidebarMenuSubButton.displayName = \"SidebarMenuSubButton\"\n\nexport {\n Sidebar,\n SidebarContent,\n SidebarFooter,\n SidebarGroup,\n SidebarGroupAction,\n SidebarGroupContent,\n SidebarGroupLabel,\n SidebarHeader,\n SidebarInput,\n SidebarInset,\n SidebarMenu,\n SidebarMenuAction,\n SidebarMenuBadge,\n SidebarMenuButton,\n SidebarMenuItem,\n SidebarMenuSkeleton,\n SidebarMenuSub,\n SidebarMenuSubButton,\n SidebarMenuSubItem,\n SidebarProvider,\n SidebarRail,\n SidebarSeparator,\n SidebarTrigger,\n useSidebar,\n}\n"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"path": "components/base/Sidebar.md",
|
|
28
|
+
"type": "registry:file",
|
|
29
|
+
"target": "components/base/Sidebar.md",
|
|
30
|
+
"content": "# Sidebar — guía de uso\n\nPrimitivas internas (estilo shadcn) que usa `AppSidebar` por debajo. **No las compongas a mano para una barra de navegación: usa `AppSidebar`** (data-driven). Recurre a estas primitivas solo para layouts a medida. El componente vive en tu proyecto: instálalo con `create-lexy add sidebar` y edítalo con libertad.\n\n## Import\n\n```tsx\n// Para el armazón/contenedor (caso a medida):\nimport {\n SidebarProvider, SidebarInset, SidebarTrigger,\n} from \"@/components/base/Sidebar\";\n```\n\n## Vía recomendada: AppSidebar\n\nPara una barra de navegación, **no** uses estas primitivas directamente. Describe la navegación como datos y deja que `AppSidebar` arme la composición correcta:\n\n```tsx\n<SidebarProvider>\n <AppSidebar groups={groups} user={user} logo={<Logo />} />\n <SidebarInset>{/* contenido */}</SidebarInset>\n</SidebarProvider>\n```\n\nLee `AppSidebar.md` para el modelo de datos (`groups`, `items`, `user`).\n\n## Cuándo SÍ usar las primitivas\n\nSolo para shells o composiciones a medida que no encajan en el modelo `groups` de `AppSidebar`:\n`SidebarProvider`, `SidebarInset`, `SidebarTrigger` para el armazón; y `SidebarGroup`, `SidebarMenu`, `SidebarMenuButton`, etc. si necesitas una estructura totalmente custom.\n\n## Exports\n\nContenedor: `Sidebar`, `SidebarProvider`, `SidebarInset`, `SidebarTrigger`, `SidebarRail`, `useSidebar`.\nEstructura: `SidebarHeader`, `SidebarContent`, `SidebarFooter`, `SidebarGroup`, `SidebarGroupLabel`, `SidebarGroupAction`, `SidebarGroupContent`, `SidebarSeparator`, `SidebarInput`.\nMenú: `SidebarMenu`, `SidebarMenuItem`, `SidebarMenuButton`, `SidebarMenuAction`, `SidebarMenuBadge`, `SidebarMenuSkeleton`, `SidebarMenuSub`, `SidebarMenuSubItem`, `SidebarMenuSubButton`.\n\n## Reglas\n\n- Para navegación estándar → `AppSidebar`. No mezcles primitivas sueltas con `AppSidebar` en la misma barra.\n- No insertes piezas sueltas como `SidebarSeparator` dentro de una barra hecha con `AppSidebar`.\n- Si el caso es a medida, mantén un solo enfoque (todo primitivas) y documenta por qué no usaste `AppSidebar`.\n\n## Cuándo NO usar (directamente)\n\n- **Barra de navegación normal** → `AppSidebar`.\n- **Layout de app con header + sidebar** → `SidebarProvider` + `AppSidebar` + `SidebarInset` (ver AppSidebar.md).\n\n## Para IA\n\n1. Por defecto, NO uses estas primitivas: usa `AppSidebar` y su prop `groups`.\n2. Usa `SidebarProvider`/`SidebarInset`/`SidebarTrigger` solo como armazón cuando un layout a medida lo exija.\n3. Si compones a mano, no mezcles con `AppSidebar`; documenta la decisión.\n"
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "skeleton",
|
|
4
|
+
"type": "registry:ui",
|
|
5
|
+
"title": "Skeleton",
|
|
6
|
+
"description": "Esqueleto de carga. Para estados de espera. El componente vive en tu proyecto: instálalo con `create-lexy add skeleton` y edítalo con libertad.",
|
|
7
|
+
"dependencies": [
|
|
8
|
+
"class-variance-authority"
|
|
9
|
+
],
|
|
10
|
+
"registryDependencies": [],
|
|
11
|
+
"files": [
|
|
12
|
+
{
|
|
13
|
+
"path": "components/base/Skeleton.tsx",
|
|
14
|
+
"type": "registry:ui",
|
|
15
|
+
"content": "import * as React from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"@/lib/utils/cn\";\n\nconst skeletonVariants = cva(\"animate-pulse bg-muted motion-reduce:animate-none\", {\n variants: {\n shape: {\n line: \"h-4 w-full rounded\",\n avatar: \"h-10 w-10 rounded-full\",\n block: \"h-24 w-full rounded-md\",\n },\n },\n defaultVariants: { shape: \"line\" },\n});\n\nexport interface SkeletonProps\n extends React.HTMLAttributes<HTMLDivElement>,\n VariantProps<typeof skeletonVariants> {}\n\nconst Skeleton = React.forwardRef<HTMLDivElement, SkeletonProps>(\n ({ shape, className, ...props }, ref) => (\n <div\n ref={ref}\n aria-hidden=\"true\"\n className={cn(skeletonVariants({ shape }), className)}\n {...props}\n />\n ),\n);\nSkeleton.displayName = \"Skeleton\";\n\nexport { Skeleton, skeletonVariants };\n"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"path": "components/base/Skeleton.md",
|
|
19
|
+
"type": "registry:file",
|
|
20
|
+
"target": "components/base/Skeleton.md",
|
|
21
|
+
"content": "# Skeleton — guía de uso\n\nEsqueleto de carga. Para estados de espera. El componente vive en tu proyecto: instálalo con `create-lexy add skeleton` y edítalo con libertad.\n\n## Import\n\n```tsx\nimport { Skeleton } from \"@/components/base/Skeleton\";\n```\n\n## Uso básico\n\n```tsx\n<Skeleton />\n```\n\n## Props\n\nConsulta la story en Storybook para ver las props disponibles.\n\n## Reglas\n\n- Usa Skeleton según el propósito descrito.\n- No abuses de este componente en contexts donde no aplica.\n\n## Para IA\n\n1. Identifica el contexto de uso del componente.\n2. Importa Skeleton desde el path correcto.\n3. Configura las props según la necesidad.\n4. Verifica que el componente se integre correctamente en el layout.\n5. Solo usa variantes documentadas.\n"
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "slider",
|
|
4
|
+
"type": "registry:ui",
|
|
5
|
+
"title": "Slider",
|
|
6
|
+
"description": "Control deslizante. Para selección de rango numérico. El componente vive en tu proyecto: instálalo con `create-lexy add slider` y edítalo con libertad.",
|
|
7
|
+
"dependencies": [
|
|
8
|
+
"@radix-ui/react-slider"
|
|
9
|
+
],
|
|
10
|
+
"registryDependencies": [],
|
|
11
|
+
"files": [
|
|
12
|
+
{
|
|
13
|
+
"path": "components/base/Slider.tsx",
|
|
14
|
+
"type": "registry:ui",
|
|
15
|
+
"content": "import * as React from \"react\";\nimport * as SliderPrimitive from \"@radix-ui/react-slider\";\n\nimport { cn } from \"@/lib/utils/cn\";\n\nconst Slider = React.forwardRef<\n React.ElementRef<typeof SliderPrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>\n>(({ className, ...props }, ref) => (\n <SliderPrimitive.Root\n ref={ref}\n className={cn(\n \"relative flex w-full touch-none select-none items-center\",\n className,\n )}\n {...props}>\n <SliderPrimitive.Track className='relative h-1.5 w-full grow overflow-hidden rounded-full bg-secondary'>\n <SliderPrimitive.Range className='absolute h-full bg-primary' />\n </SliderPrimitive.Track>\n <SliderPrimitive.Thumb className='block h-4 w-4 cursor-pointer rounded-full border border-primary/50 bg-card shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50' />\n </SliderPrimitive.Root>\n));\nSlider.displayName = SliderPrimitive.Root.displayName;\n\nexport { Slider };\n"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"path": "components/base/Slider.md",
|
|
19
|
+
"type": "registry:file",
|
|
20
|
+
"target": "components/base/Slider.md",
|
|
21
|
+
"content": "# Slider — guía de uso\n\nControl deslizante. Para selección de rango numérico. El componente vive en tu proyecto: instálalo con `create-lexy add slider` y edítalo con libertad.\n\n## Import\n\n```tsx\nimport { Slider } from \"@/components/base/Slider\";\n```\n\n## Uso básico\n\n```tsx\n<Slider />\n```\n\n## Props\n\nConsulta la story en Storybook para ver las props disponibles.\n\n## Reglas\n\n- Usa Slider según el propósito descrito.\n- No abuses de este componente en contexts donde no aplica.\n\n## Para IA\n\n1. Identifica el contexto de uso del componente.\n2. Importa Slider desde el path correcto.\n3. Configura las props según la necesidad.\n4. Verifica que el componente se integre correctamente en el layout.\n5. Solo usa variantes documentadas.\n"
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "snippet",
|
|
4
|
+
"type": "registry:ui",
|
|
5
|
+
"title": "Snippet",
|
|
6
|
+
"description": "Fragmento de código. Para mostrar código con copia. El componente vive en tu proyecto: instálalo con `create-lexy add snippet` y edítalo con libertad.",
|
|
7
|
+
"dependencies": [
|
|
8
|
+
"class-variance-authority"
|
|
9
|
+
],
|
|
10
|
+
"registryDependencies": [],
|
|
11
|
+
"files": [
|
|
12
|
+
{
|
|
13
|
+
"path": "components/base/Snippet.tsx",
|
|
14
|
+
"type": "registry:ui",
|
|
15
|
+
"content": "import * as React from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"@/lib/utils/cn\";\n\nconst snippetVariants = cva(\n // py-2 + py-0.5 del pre: el texto queda a la misma distancia del borde\n // superior e inferior que el botón de copiar (caja simétrica, 40px de alto).\n \"relative flex w-full min-w-0 items-start gap-3 rounded-md border bg-background px-3 py-2 text-sm text-foreground\",\n {\n variants: {\n variant: {\n default: \"border-border\",\n success: \"border-success/30\",\n warning: \"border-warning/30\",\n error: \"border-destructive/30\",\n },\n font: {\n mono: \"font-mono\",\n sans: \"\",\n },\n },\n defaultVariants: { variant: \"default\", font: \"mono\" },\n },\n);\n\nexport interface SnippetProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, \"children\">,\n VariantProps<typeof snippetVariants> {\n /** Texto exacto que se copia al portapapeles. */\n children: string;\n lines?: \"single\" | \"multi\";\n onCopy?: () => void;\n copiedDuration?: number;\n}\n\nexport function Snippet({\n variant,\n font,\n lines = \"single\",\n children,\n onCopy,\n copiedDuration = 1500,\n className,\n ...props\n}: SnippetProps) {\n const [copied, setCopied] = React.useState(false);\n\n const copy = async () => {\n try {\n await navigator.clipboard.writeText(children);\n setCopied(true);\n onCopy?.();\n window.setTimeout(() => setCopied(false), copiedDuration);\n } catch {\n /* portapapeles no disponible */\n }\n };\n\n return (\n <div className={cn(snippetVariants({ variant, font }), className)} {...props}>\n <pre\n className={cn(\n \"min-w-0 flex-1 overflow-x-auto py-0.5\",\n lines === \"single\"\n ? \"whitespace-nowrap\"\n : \"max-h-48 whitespace-pre-wrap\",\n )}>\n <code>{children}</code>\n </pre>\n <button\n type='button'\n onClick={copy}\n aria-label={copied ? \"Copiado\" : \"Copiar al portapapeles\"}\n className='m-0 flex shrink-0 cursor-pointer rounded-md p-1 text-muted-foreground transition-colors duration-150 hover:bg-secondary hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring'>\n {copied ? (\n <svg\n className='h-4 w-4 text-success'\n viewBox='0 0 24 24'\n fill='none'\n stroke='currentColor'\n strokeWidth='2'\n strokeLinecap='round'\n strokeLinejoin='round'>\n <path d='M20 6 9 17l-5-5' />\n </svg>\n ) : (\n <svg\n className='h-4 w-4'\n viewBox='0 0 24 24'\n fill='none'\n stroke='currentColor'\n strokeWidth='2'\n strokeLinecap='round'\n strokeLinejoin='round'>\n <rect width='14' height='14' x='8' y='8' rx='2' ry='2' />\n <path d='M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2' />\n </svg>\n )}\n </button>\n </div>\n );\n}\n\nexport { snippetVariants };\n"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"path": "components/base/Snippet.md",
|
|
19
|
+
"type": "registry:file",
|
|
20
|
+
"target": "components/base/Snippet.md",
|
|
21
|
+
"content": "# Snippet — guía de uso\n\nFragmento de código. Para mostrar código con copia. El componente vive en tu proyecto: instálalo con `create-lexy add snippet` y edítalo con libertad.\n\n## Import\n\n```tsx\nimport { Snippet } from \"@/components/base/Snippet\";\n```\n\n## Uso básico\n\n```tsx\n<Snippet />\n```\n\n## Props\n\nConsulta la story en Storybook para ver las props disponibles.\n\n## Reglas\n\n- Usa Snippet según el propósito descrito.\n- No abuses de este componente en contexts donde no aplica.\n\n## Para IA\n\n1. Identifica el contexto de uso del componente.\n2. Importa Snippet desde el path correcto.\n3. Configura las props según la necesidad.\n4. Verifica que el componente se integre correctamente en el layout.\n5. Solo usa variantes documentadas.\n"
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "spinner",
|
|
4
|
+
"type": "registry:ui",
|
|
5
|
+
"title": "Spinner",
|
|
6
|
+
"description": "Indicador de carga visual. Usa el icono `Loader2` de Lucide con animación de rotación. El componente vive en tu proyecto: instálalo con `create-lexy add spinner` y edítalo con libertad.",
|
|
7
|
+
"dependencies": [
|
|
8
|
+
"class-variance-authority",
|
|
9
|
+
"lucide-react"
|
|
10
|
+
],
|
|
11
|
+
"registryDependencies": [],
|
|
12
|
+
"files": [
|
|
13
|
+
{
|
|
14
|
+
"path": "components/base/Spinner.tsx",
|
|
15
|
+
"type": "registry:ui",
|
|
16
|
+
"content": "import * as React from \"react\";\nimport { Loader2Icon } from \"lucide-react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"@/lib/utils/cn\";\n\nconst spinnerVariants = cva(\"animate-spin text-primary\", {\n variants: {\n size: {\n sm: \"size-4\",\n md: \"size-6\",\n lg: \"size-8\",\n xl: \"size-12\",\n },\n },\n defaultVariants: { size: \"md\" },\n});\n\nexport interface SpinnerProps\n extends React.ComponentPropsWithoutRef<\"svg\">,\n VariantProps<typeof spinnerVariants> {}\n\nconst Spinner = React.forwardRef<SVGSVGElement, SpinnerProps>(\n ({ className, size, ...props }, ref) => (\n <Loader2Icon\n ref={ref}\n role=\"status\"\n aria-label=\"Cargando\"\n className={cn(spinnerVariants({ size }), className)}\n {...props}\n />\n ),\n);\nSpinner.displayName = \"Spinner\";\n\nexport { Spinner, spinnerVariants };\n"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"path": "components/base/Spinner.md",
|
|
20
|
+
"type": "registry:file",
|
|
21
|
+
"target": "components/base/Spinner.md",
|
|
22
|
+
"content": "# Spinner — guía de uso\n\nIndicador de carga visual. Usa el icono `Loader2` de Lucide con animación de rotación. El componente vive en tu proyecto: instálalo con `create-lexy add spinner` y edítalo con libertad.\n\n## Import\n\n```tsx\nimport { Spinner } from \"@/shared/components/base/Spinner\"; // o @/components/base/Spinner\n```\n\n## Uso básico\n\n```tsx\n<Spinner size=\"md\" />\n```\n\n## Tamaños\n\n```tsx\n<div className=\"flex gap-4\">\n <Spinner size=\"sm\" />\n <Spinner size=\"md\" />\n <Spinner size=\"lg\" />\n <Spinner size=\"xl\" />\n</div>\n```\n\n## Colores personalizados\n\n```tsx\n<Spinner className=\"text-destructive\" />\n<Spinner className=\"text-success\" />\n```\n\n## En botón cargando\n\n```tsx\n<button disabled className=\"inline-flex items-center gap-2 opacity-50\">\n <Spinner size=\"sm\" className=\"text-primary-foreground\" />\n Guardando...\n</button>\n```\n\n## Props\n\n| Prop | Tipo | Default | Descripción |\n|---|---|---|---|\n| `size` | `\"sm\" \\| \"md\" \\| \"lg\" \\| \"xl\"` | `\"md\"` | Tamaño del spinner. |\n| `className` | `string?` | — | Clase adicional. |\n\n## Reglas\n\n- Usa `Spinner` para operaciones de corta duración (menos de 2-3 segundos).\n- Combina con texto descriptivo cuando el contexto no sea obvio.\n- Usa `size=\"sm\"` dentro de botones.\n- Usa `size=\"lg\"` o `\"xl\"` para estados de carga de página completa.\n- No uses `Spinner` si la operación dura más de 5 segundos → considera `Progress`.\n- No uses `Spinner` sin texto si hay múltiples operaciones simultáneas.\n\n## Para IA\n\n1. Identifica si hay una operación en progreso o carga de datos.\n2. Si es una operación corta → usa `Spinner`.\n3. Elige el tamaño según el contexto.\n4. Agrega texto descriptivo si el contexto no es obvio.\n5. Considera `Skeleton` si conoces la estructura del contenido que cargará.\n6. Considera `Progress` si la operación tiene progreso medible.\n"
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "status-dot",
|
|
4
|
+
"type": "registry:ui",
|
|
5
|
+
"title": "StatusDot",
|
|
6
|
+
"description": "Punto de estado con color, pensado para acompañar texto (estado de una fila, disponibilidad, conexión). El color **nunca** es la única señal: va con texto o `aria-label`. El componente vive en tu proyecto: instálalo con `create-lexy add status-dot` y edítalo con libertad.",
|
|
7
|
+
"dependencies": [
|
|
8
|
+
"class-variance-authority"
|
|
9
|
+
],
|
|
10
|
+
"registryDependencies": [],
|
|
11
|
+
"files": [
|
|
12
|
+
{
|
|
13
|
+
"path": "components/base/StatusDot.tsx",
|
|
14
|
+
"type": "registry:ui",
|
|
15
|
+
"content": "import * as React from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"@/lib/utils/cn\";\n\nconst statusDotVariants = cva(\"inline-block shrink-0 rounded-full\", {\n variants: {\n tone: {\n gray: \"bg-muted-foreground\",\n brand: \"bg-primary\",\n success: \"bg-success\",\n warning: \"bg-warning\",\n danger: \"bg-destructive\",\n info: \"bg-info\",\n },\n size: {\n sm: \"h-1.5 w-1.5\",\n md: \"h-2 w-2\",\n lg: \"h-2.5 w-2.5\",\n },\n },\n defaultVariants: { tone: \"gray\", size: \"md\" },\n});\n\nexport interface StatusDotProps\n extends React.HTMLAttributes<HTMLSpanElement>,\n VariantProps<typeof statusDotVariants> {\n /** Requerido cuando no hay texto (punto solo). */\n \"aria-label\"?: string;\n}\n\nconst StatusDot = React.forwardRef<HTMLSpanElement, StatusDotProps>(\n function StatusDot(\n { tone, size, children, \"aria-label\": ariaLabel, className, ...props },\n ref,\n ) {\n return (\n <span\n ref={ref}\n className={cn(\"inline-flex min-w-0 items-center gap-2\", className)}\n {...props}>\n <span className={statusDotVariants({ tone, size })} aria-hidden />\n {children ? (\n <span className='text-sm text-foreground'>{children}</span>\n ) : (\n <span className='sr-only'>{ariaLabel ?? \"Estado\"}</span>\n )}\n </span>\n );\n },\n);\nStatusDot.displayName = \"StatusDot\";\n\nexport { StatusDot, statusDotVariants };\n"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"path": "components/base/StatusDot.md",
|
|
19
|
+
"type": "registry:file",
|
|
20
|
+
"target": "components/base/StatusDot.md",
|
|
21
|
+
"content": "# StatusDot — guía de uso\n\nPunto de estado con color, pensado para acompañar texto (estado de una fila, disponibilidad, conexión). El color **nunca** es la única señal: va con texto o `aria-label`. El componente vive en tu proyecto: instálalo con `create-lexy add status-dot` y edítalo con libertad.\n\n## Import\n\n```tsx\nimport { StatusDot } from \"@/components/base/StatusDot\";\n```\n\n## Uso básico\n\n```tsx\n<StatusDot tone=\"success\">Activo</StatusDot>\n<StatusDot tone=\"warning\">Pendiente</StatusDot>\n<StatusDot tone=\"danger\">Vencido</StatusDot>\n```\n\nSi va solo (sin texto visible), agrega `aria-label`:\n\n```tsx\n<StatusDot tone=\"success\" aria-label=\"En línea\" />\n```\n\n## Props\n\n| Prop | Tipo | Default | Descripción |\n|---|---|---|---|\n| `tone` | `\"gray\" \\| \"brand\" \\| \"success\" \\| \"warning\" \\| \"danger\" \\| \"info\"` | `\"gray\"` | Color del estado. |\n| `size` | `\"sm\" \\| \"md\" \\| \"lg\"` | `\"md\"` | Tamaño del punto. |\n| `children` | `ReactNode` | — | Texto del estado junto al punto. |\n\n## Reglas\n\n- Acompaña el punto con texto (`children`) o, si va solo, con `aria-label`. El color por sí solo no comunica estado (accesibilidad).\n- Usa el `tone` por significado: `success`/`warning`/`danger` para estados; `gray`/`info`/`brand` para neutros.\n- Mantén el texto de estado corto.\n\n## Cuándo NO usar\n\n- **Etiqueta de estado con fondo** (chip) → `Badge`.\n- **Conteo numérico** → `CounterBadge`.\n- **Chip removible / filtro** → `Tag`.\n\n## Para IA\n\n1. Úsalo para señalar el estado de un ítem o fila, casi siempre dentro de una `Table` o lista.\n2. Empareja el color con texto visible; si no hay texto, agrega `aria-label`.\n3. Elige `tone` por significado, no por estética.\n"
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "switch",
|
|
4
|
+
"type": "registry:ui",
|
|
5
|
+
"title": "Switch",
|
|
6
|
+
"description": "Interruptor de palanca. Para activar/desactivar estados. El componente vive en tu proyecto: instálalo con `create-lexy add switch` y edítalo con libertad.",
|
|
7
|
+
"dependencies": [
|
|
8
|
+
"@radix-ui/react-switch"
|
|
9
|
+
],
|
|
10
|
+
"registryDependencies": [],
|
|
11
|
+
"files": [
|
|
12
|
+
{
|
|
13
|
+
"path": "components/base/Switch.tsx",
|
|
14
|
+
"type": "registry:ui",
|
|
15
|
+
"content": "import * as React from \"react\";\nimport * as SwitchPrimitives from \"@radix-ui/react-switch\";\n\nimport { cn } from \"@/lib/utils/cn\";\n\nconst Switch = React.forwardRef<\n React.ElementRef<typeof SwitchPrimitives.Root>,\n React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>\n>(({ className, ...props }, ref) => (\n <SwitchPrimitives.Root\n className={cn(\n \"peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input\",\n className,\n )}\n {...props}\n ref={ref}>\n <SwitchPrimitives.Thumb\n className={cn(\n \"pointer-events-none block h-5 w-5 rounded-full bg-card shadow-sm ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0\",\n )}\n />\n </SwitchPrimitives.Root>\n));\nSwitch.displayName = SwitchPrimitives.Root.displayName;\n\nexport { Switch };\n"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"path": "components/base/Switch.md",
|
|
19
|
+
"type": "registry:file",
|
|
20
|
+
"target": "components/base/Switch.md",
|
|
21
|
+
"content": "# Switch — guía de uso\n\nInterruptor de palanca. Para activar/desactivar estados. El componente vive en tu proyecto: instálalo con `create-lexy add switch` y edítalo con libertad.\n\n## Import\n\n```tsx\nimport { Switch } from \"@/components/base/Switch\";\n```\n\n## Uso básico\n\n```tsx\n<Switch />\n```\n\n## Props\n\nConsulta la story en Storybook para ver las props disponibles.\n\n## Reglas\n\n- Usa Switch según el propósito descrito.\n- No abuses de este componente en contexts donde no aplica.\n\n## Para IA\n\n1. Identifica el contexto de uso del componente.\n2. Importa Switch desde el path correcto.\n3. Configura las props según la necesidad.\n4. Verifica que el componente se integre correctamente en el layout.\n5. Solo usa variantes documentadas.\n"
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "table",
|
|
4
|
+
"type": "registry:ui",
|
|
5
|
+
"title": "Table",
|
|
6
|
+
"description": "Tabla de datos para comparar/escanear filas y columnas. Componente compuesto (`TableHeader`, `TableContent`, `TableRow`, `TableCell` — también disponibles como alias `Table.Header`, `Table.Content`, `Table.Row`, `Table.Cell`) basado en CSS grid. El componente vive en tu proyecto: instálalo con `create-lexy add table` y edítalo con libertad.",
|
|
7
|
+
"dependencies": [
|
|
8
|
+
"class-variance-authority"
|
|
9
|
+
],
|
|
10
|
+
"registryDependencies": [],
|
|
11
|
+
"files": [
|
|
12
|
+
{
|
|
13
|
+
"path": "components/base/Table.tsx",
|
|
14
|
+
"type": "registry:ui",
|
|
15
|
+
"content": "import * as React from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"@/lib/utils/cn\";\n\n// Tabla en grid de divs (no <table>): el ancho de columnas se controla con\n// `columns` (grid-template-columns), lo que permite truncado y alineación\n// fiables. Compound: Table + TableHeader/TableContent/TableRow/TableCell;\n// los alias estáticos (Table.Header, …) se conservan por compatibilidad.\n\ntype TableVariant = \"normal\" | \"basic\" | \"striped\";\n\ninterface TableContextProps {\n columns?: string;\n variant: TableVariant;\n stickyHeader?: boolean;\n}\n\nconst TableContext = React.createContext<TableContextProps>({\n variant: \"normal\",\n stickyHeader: false,\n});\n\nconst tableVariants = cva(\n \"flex w-full flex-col overflow-hidden rounded-md border border-border bg-card text-sm text-foreground shadow-sm\",\n {\n variants: {\n variant: {\n normal: \"\",\n basic: \"border-none shadow-none\",\n striped: \"\",\n },\n },\n defaultVariants: { variant: \"normal\" },\n },\n);\n\nexport interface TableProps\n extends React.HTMLAttributes<HTMLDivElement>,\n VariantProps<typeof tableVariants> {\n /** grid-template-columns (p. ej. \"2fr 1fr 1fr\"). Sin definir: columnas iguales. */\n columns?: string;\n /** Mantiene el header visible al hacer scroll vertical. */\n stickyHeader?: boolean;\n}\n\nconst TableRoot = React.forwardRef<HTMLDivElement, TableProps>(\n function Table(\n { children, className, columns, variant, stickyHeader = false, ...props },\n ref,\n ) {\n const resolvedVariant = variant ?? \"normal\";\n return (\n <TableContext.Provider\n value={{ columns, variant: resolvedVariant, stickyHeader }}\n >\n <div\n ref={ref}\n role='table'\n className={cn(tableVariants({ variant }), className)}\n {...props}\n >\n <div\n className={cn(\"w-full overflow-x-auto\", {\n \"max-h-125 overflow-y-auto\": stickyHeader,\n })}>\n {children}\n </div>\n </div>\n </TableContext.Provider>\n );\n },\n);\n\nconst getGridStyles = (columns?: string) => {\n if (columns) return { gridTemplateColumns: columns };\n return { gridAutoFlow: \"column\" as const, gridAutoColumns: \"1fr\" };\n};\n\nconst TableHeader = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(function TableHeader({ children, className, style, ...props }, ref) {\n const { columns, variant, stickyHeader } = React.useContext(TableContext);\n\n return (\n <div\n ref={ref}\n role='rowgroup'\n style={{ ...getGridStyles(columns), ...style }}\n className={cn(\n \"grid items-center px-4 py-3 font-semibold text-foreground\",\n {\n \"border-b border-border bg-background\":\n variant === \"basic\" || variant === \"normal\",\n \"bg-muted\": variant === \"striped\",\n \"sticky top-0 z-10 shadow-sm\": stickyHeader,\n },\n className,\n )}\n {...props}\n >\n {children}\n </div>\n );\n});\n\nconst TableContent = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(function TableContent({ className, ...props }, ref) {\n return (\n <div ref={ref} className={cn(\"flex flex-col\", className)} {...props} />\n );\n});\n\nconst TableRow = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(function TableRow({ children, className, style, onClick, ...props }, ref) {\n const { columns, variant } = React.useContext(TableContext);\n\n return (\n <div\n ref={ref}\n role='row'\n onClick={onClick}\n style={{ ...getGridStyles(columns), ...style }}\n className={cn(\n \"grid items-center px-4 py-3 transition-colors hover:bg-muted/60\",\n {\n \"border-b border-border last:border-0\": variant === \"normal\",\n \"border-none\": variant === \"basic\",\n \"border-none odd:bg-muted/40 even:bg-card\": variant === \"striped\",\n \"cursor-pointer\": onClick,\n },\n className,\n )}\n {...props}\n >\n {children}\n </div>\n );\n});\n\nconst TableCell = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(function TableCell({ className, ...props }, ref) {\n return (\n <div\n ref={ref}\n role='cell'\n className={cn(\n \"truncate px-1 leading-relaxed text-muted-foreground\",\n className,\n )}\n {...props}\n />\n );\n});\n\n// Alias estáticos por compatibilidad con el uso Table.Header / Table.Row / …\nconst Table = Object.assign(TableRoot, {\n Header: TableHeader,\n Content: TableContent,\n Row: TableRow,\n Cell: TableCell,\n});\n\nexport { Table, TableHeader, TableContent, TableRow, TableCell, tableVariants };\n"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"path": "components/base/Table.md",
|
|
19
|
+
"type": "registry:file",
|
|
20
|
+
"target": "components/base/Table.md",
|
|
21
|
+
"content": "# Table — guía de uso\n\nTabla de datos para comparar/escanear filas y columnas. Componente compuesto (`TableHeader`, `TableContent`, `TableRow`, `TableCell` — también disponibles como alias `Table.Header`, `Table.Content`, `Table.Row`, `Table.Cell`) basado en CSS grid. El componente vive en tu proyecto: instálalo con `create-lexy add table` y edítalo con libertad.\n\n## Import\n\n```tsx\nimport { Table } from \"@/components/base/Table\";\n```\n\n## Uso básico\n\n```tsx\n<Table columns=\"2fr 1fr 1fr\">\n <Table.Header>\n <Table.Cell>Cliente</Table.Cell>\n <Table.Cell>Estado</Table.Cell>\n <Table.Cell>Monto</Table.Cell>\n </Table.Header>\n <Table.Content>\n {filas.map((f) => (\n <Table.Row key={f.id} onClick={() => abrir(f.id)}>\n <Table.Cell>{f.cliente}</Table.Cell>\n <Table.Cell><StatusDot tone={f.tone}>{f.estado}</StatusDot></Table.Cell>\n <Table.Cell>{f.monto}</Table.Cell>\n </Table.Row>\n ))}\n </Table.Content>\n</Table>\n```\n\n## Props\n\n### `Table`\n| Prop | Tipo | Default | Descripción |\n|---|---|---|---|\n| `columns` | `string` | — | `grid-template-columns` (p. ej. `\"2fr 1fr 1fr\"`). Si se omite, columnas iguales automáticas. |\n| `variant` | `\"normal\" \\| \"basic\" \\| \"striped\"` | `\"normal\"` | Con borde/relleno, sin bordes, o filas alternadas. |\n| `stickyHeader` | `boolean` | `false` | Header fijo con scroll vertical (alto máx. ~500px). |\n\n### Subcomponentes\n`TableHeader`, `TableContent`, `TableRow` (acepta `onClick`), `TableCell` — alias: `Table.Header`, `Table.Content`, `Table.Row`, `Table.Cell`. Todos aceptan `className`, esparcen `...props` al nodo raíz y reenvían `ref`. `tableVariants` (CVA) se exporta para extender estilos.\n\n## Reglas\n\n- Las celdas de `Table.Header` y de cada `Table.Row` deben tener el **mismo número** de columnas que `columns`.\n- Usa `columns` con `fr`/anchos para alinear; no metas anchos a mano por celda.\n- Si las filas son clicables (`onClick`), que toda la fila sea el objetivo y el cursor lo indique.\n- Para estado por fila combina con `StatusDot`/`Badge` (texto, no solo color).\n- `stickyHeader` para listas largas; mantén el header escaneable.\n\n## Cuándo NO usar\n\n- **Lista de un solo dato por ítem** → una lista simple, no una tabla.\n- **Tarjetas con jerarquía visual rica** → `Card`/`FeatureCard`.\n- **Pocos pares clave-valor de un registro** → lista de definición (`dl`) o `Card`, no tabla.\n\n## Para IA\n\n1. Usa tabla solo cuando hay varias columnas que se comparan entre filas.\n2. Define `columns` y respeta ese número de `Table.Cell` en header y filas.\n3. Elige `variant` (`striped` ayuda a escanear filas largas; `basic` para tablas embebidas).\n4. Para estados, añade `StatusDot`/`Badge` con texto, no solo color.\n5. Si las filas navegan, usa `onClick` en `Table.Row` y deja claro que es clicable.\n"
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "tabs",
|
|
4
|
+
"type": "registry:ui",
|
|
5
|
+
"title": "Tabs",
|
|
6
|
+
"description": "Pestañas para alternar entre vistas **del mismo nivel** sin salir de la página (Radix). Progressive disclosure: muestra una categoría a la vez. El componente vive en tu proyecto: instálalo con `create-lexy add tabs` y edítalo con libertad.",
|
|
7
|
+
"dependencies": [
|
|
8
|
+
"@radix-ui/react-tabs"
|
|
9
|
+
],
|
|
10
|
+
"registryDependencies": [],
|
|
11
|
+
"files": [
|
|
12
|
+
{
|
|
13
|
+
"path": "components/base/Tabs.tsx",
|
|
14
|
+
"type": "registry:ui",
|
|
15
|
+
"content": "import * as React from \"react\";\nimport * as TabsPrimitive from \"@radix-ui/react-tabs\";\n\nimport { cn } from \"@/lib/utils/cn\";\n\nconst Tabs = TabsPrimitive.Root;\n\nconst TabsList = React.forwardRef<\n React.ElementRef<typeof TabsPrimitive.List>,\n React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>\n>(({ className, ...props }, ref) => (\n <TabsPrimitive.List\n ref={ref}\n className={cn(\n \"inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground\",\n className,\n )}\n {...props}\n />\n));\nTabsList.displayName = TabsPrimitive.List.displayName;\n\nconst TabsTrigger = React.forwardRef<\n React.ElementRef<typeof TabsPrimitive.Trigger>,\n React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>\n>(({ className, ...props }, ref) => (\n <TabsPrimitive.Trigger\n ref={ref}\n className={cn(\n \"inline-flex cursor-pointer items-center justify-center whitespace-nowrap rounded px-3 py-1.5 text-sm font-medium transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm\",\n className,\n )}\n {...props}\n />\n));\nTabsTrigger.displayName = TabsPrimitive.Trigger.displayName;\n\nconst TabsContent = React.forwardRef<\n React.ElementRef<typeof TabsPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>\n>(({ className, ...props }, ref) => (\n <TabsPrimitive.Content\n ref={ref}\n className={cn(\n \"mt-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n className,\n )}\n {...props}\n />\n));\nTabsContent.displayName = TabsPrimitive.Content.displayName;\n\nexport { Tabs, TabsList, TabsTrigger, TabsContent };\n"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"path": "components/base/Tabs.md",
|
|
19
|
+
"type": "registry:file",
|
|
20
|
+
"target": "components/base/Tabs.md",
|
|
21
|
+
"content": "# Tabs — guía de uso\n\nPestañas para alternar entre vistas **del mismo nivel** sin salir de la página (Radix). Progressive disclosure: muestra una categoría a la vez. El componente vive en tu proyecto: instálalo con `create-lexy add tabs` y edítalo con libertad.\n\n## Import\n\n```tsx\nimport { Tabs, TabsList, TabsTrigger, TabsContent } from \"@/components/base/Tabs\";\n```\n\n## Uso básico\n\n```tsx\n<Tabs defaultValue=\"resumen\">\n <TabsList>\n <TabsTrigger value=\"resumen\">Resumen</TabsTrigger>\n <TabsTrigger value=\"documentos\">Documentos</TabsTrigger>\n <TabsTrigger value=\"actividad\">Actividad</TabsTrigger>\n </TabsList>\n <TabsContent value=\"resumen\">…</TabsContent>\n <TabsContent value=\"documentos\">…</TabsContent>\n <TabsContent value=\"actividad\">…</TabsContent>\n</Tabs>\n```\n\n## Props (esenciales)\n\n| Parte | Prop | Descripción |\n|---|---|---|\n| `Tabs` | `value` / `defaultValue` | Pestaña activa (controlada / inicial). |\n| `Tabs` | `onValueChange` | Cambio de pestaña. |\n| `TabsTrigger` | `value` | **Requerido.** Debe coincidir con un `TabsContent`. |\n| `TabsContent` | `value` | **Requerido.** Empareja con su trigger. |\n\n## Reglas\n\n- Cada `TabsTrigger.value` debe tener su `TabsContent.value` correspondiente.\n- Las pestañas son **pares**: contenido del mismo rango que no necesitas ver junto.\n- Pocas pestañas (idealmente ≤5) con etiquetas cortas y escaneables.\n- No escondas detrás de tabs información crítica o de error que el usuario debe ver siempre.\n\n## Cuándo NO usar\n\n- **Pasos secuenciales de un flujo** → usa un stepper/wizard; las tabs no implican orden.\n- **Navegación entre páginas/rutas** → `NavigationMenu` o enlaces.\n- **Mostrar/ocultar detalle puntual** → `Accordion` o `Dialog`.\n\n## Para IA\n\n1. Confirma que son categorías pares del mismo contexto (no pasos, no rutas).\n2. Mantén ≤5 pestañas con labels en sentence case.\n3. Empareja cada `value` trigger↔content.\n4. No ocultes errores, costos o próximos pasos dentro de una pestaña inactiva.\n"
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "tag",
|
|
4
|
+
"type": "registry:ui",
|
|
5
|
+
"title": "Tag",
|
|
6
|
+
"description": "Etiqueta de categoría. Para clasificación. El componente vive en tu proyecto: instálalo con `create-lexy add tag` y edítalo con libertad.",
|
|
7
|
+
"dependencies": [
|
|
8
|
+
"class-variance-authority"
|
|
9
|
+
],
|
|
10
|
+
"registryDependencies": [],
|
|
11
|
+
"files": [
|
|
12
|
+
{
|
|
13
|
+
"path": "components/base/Tag.tsx",
|
|
14
|
+
"type": "registry:ui",
|
|
15
|
+
"content": "import * as React from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"@/lib/utils/cn\";\n\nconst tagVariants = cva(\n \"inline-flex select-none items-center gap-1 border text-xs font-medium leading-none\",\n {\n variants: {\n tone: {\n gray: \"border-border bg-secondary text-secondary-foreground\",\n brand: \"border-primary/20 bg-primary/10 text-primary\",\n success: \"border-success/20 bg-success/10 text-success\",\n warning: \"border-warning/20 bg-warning/10 text-warning\",\n danger: \"border-destructive/20 bg-destructive/10 text-destructive\",\n },\n shape: {\n square: \"rounded\",\n rounded: \"rounded-full\",\n },\n size: {\n sm: \"h-6 px-2 py-0.5\",\n md: \"h-7 px-2.5 py-1\",\n lg: \"h-8 px-3 py-1\",\n },\n },\n defaultVariants: { tone: \"gray\", shape: \"square\", size: \"sm\" },\n },\n);\n\nexport interface TagProps\n extends Omit<React.HTMLAttributes<HTMLSpanElement>, \"color\">,\n VariantProps<typeof tagVariants> {\n removable?: boolean;\n onRemove?: () => void;\n}\n\nconst Tag = React.forwardRef<HTMLSpanElement, TagProps>(function Tag(\n { tone, shape, size, removable, onRemove, children, className, ...props },\n ref,\n) {\n return (\n <span\n ref={ref}\n className={cn(tagVariants({ tone, shape, size }), className)}\n {...props}>\n {children}\n {removable && (\n <button\n type='button'\n onClick={onRemove}\n aria-label='Quitar'\n className='-mr-0.5 inline-flex cursor-pointer items-center justify-center rounded-full opacity-60 transition-opacity hover:opacity-100 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-current'>\n <svg\n className='h-3 w-3'\n viewBox='0 0 24 24'\n fill='none'\n stroke='currentColor'\n strokeWidth='2'\n strokeLinecap='round'\n strokeLinejoin='round'>\n <path d='M18 6 6 18M6 6l12 12' />\n </svg>\n </button>\n )}\n </span>\n );\n});\nTag.displayName = \"Tag\";\n\nexport { Tag, tagVariants };\n"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"path": "components/base/Tag.md",
|
|
19
|
+
"type": "registry:file",
|
|
20
|
+
"target": "components/base/Tag.md",
|
|
21
|
+
"content": "# Tag — guía de uso\n\nEtiqueta de categoría. Para clasificación. El componente vive en tu proyecto: instálalo con `create-lexy add tag` y edítalo con libertad.\n\n## Import\n\n```tsx\nimport { Tag } from \"@/components/base/Tag\";\n```\n\n## Uso básico\n\n```tsx\n<Tag />\n```\n\n## Props\n\nConsulta la story en Storybook para ver las props disponibles.\n\n## Reglas\n\n- Usa Tag según el propósito descrito.\n- No abuses de este componente en contexts donde no aplica.\n\n## Para IA\n\n1. Identifica el contexto de uso del componente.\n2. Importa Tag desde el path correcto.\n3. Configura las props según la necesidad.\n4. Verifica que el componente se integre correctamente en el layout.\n5. Solo usa variantes documentadas.\n"
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
|
|
3
|
+
"name": "textarea",
|
|
4
|
+
"type": "registry:ui",
|
|
5
|
+
"title": "Textarea",
|
|
6
|
+
"description": "Campo de texto multilínea, redimensionable verticalmente (alto mínimo predefinido). Renderiza un `<textarea>` nativo. El componente vive en tu proyecto: instálalo con `create-lexy add textarea` y edítalo con libertad.",
|
|
7
|
+
"dependencies": [],
|
|
8
|
+
"registryDependencies": [],
|
|
9
|
+
"files": [
|
|
10
|
+
{
|
|
11
|
+
"path": "components/base/Textarea.tsx",
|
|
12
|
+
"type": "registry:ui",
|
|
13
|
+
"content": "import * as React from \"react\";\n\nimport { cn } from \"@/lib/utils/cn\";\n\nconst Textarea = React.forwardRef<\n HTMLTextAreaElement,\n React.ComponentProps<\"textarea\">\n>(({ className, ...props }, ref) => {\n return (\n <textarea\n className={cn(\n \"flex min-h-20 w-full resize-y rounded border border-input bg-background px-3 py-2.5 text-sm text-foreground outline-none transition-colors duration-150\",\n \"placeholder:text-muted-foreground\",\n \"hover:border-muted-foreground/40\",\n \"focus-visible:border-primary\",\n \"aria-invalid:border-destructive aria-invalid:hover:border-destructive aria-invalid:focus-visible:border-destructive\",\n \"disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:border-input\",\n className,\n )}\n ref={ref}\n {...props}\n />\n );\n});\nTextarea.displayName = \"Textarea\";\n\nexport { Textarea };\n"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"path": "components/base/Textarea.md",
|
|
17
|
+
"type": "registry:file",
|
|
18
|
+
"target": "components/base/Textarea.md",
|
|
19
|
+
"content": "# Textarea — guía de uso\n\nCampo de texto multilínea, redimensionable verticalmente (alto mínimo predefinido). Renderiza un `<textarea>` nativo. El componente vive en tu proyecto: instálalo con `create-lexy add textarea` y edítalo con libertad.\n\n## Import\n\n```tsx\nimport { Textarea } from \"@/components/base/Textarea\";\n```\n\n## Uso básico\n\n```tsx\n<div className=\"grid gap-1.5\">\n <Label htmlFor=\"notas\">Notas del caso</Label>\n <Textarea id=\"notas\" placeholder=\"Resumen de la reunión…\" rows={4} />\n</div>\n```\n\n## Props\n\n| Prop | Tipo | Default | Descripción |\n|---|---|---|---|\n| `rows` | `number` | — | Alto inicial en líneas. |\n| `aria-invalid` | `boolean` | — | Activa el estilo de error. |\n| `disabled` | `boolean` | `false` | Deshabilita el campo. |\n| ...resto | `textarea` props | — | `value`, `onChange`, `placeholder`, `maxLength`, etc. |\n\n## Reglas\n\n- Asocia siempre un `Label` por `htmlFor`/`id`.\n- Usa `rows` para insinuar la longitud esperada de la respuesta.\n- Si hay límite de caracteres, muéstralo cerca del campo (no solo un `maxLength` silencioso).\n- Marca errores con `aria-invalid` + mensaje de texto.\n\n## Cuándo NO usar\n\n- **Una sola línea** (nombre, email, RUT) → `Input`.\n- **Editor con formato/markdown** → no existe en el registry; documenta la decisión si usas algo a medida.\n\n## Para IA\n\n1. Úsalo solo cuando se espera texto de varias líneas (comentarios, descripciones, notas).\n2. Empareja con `Label`.\n3. Ajusta `rows` a la longitud esperada.\n4. Si hay tope de caracteres, hazlo visible y redacta el error como corregir.\n"
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
}
|