create-lexy 0.3.0 → 0.4.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 +1 -1
- package/assets/r/alert-dialog.json +2 -2
- package/assets/r/app-accordion.json +2 -2
- package/assets/r/app-dialog.json +3 -3
- package/assets/r/app-header-bar.json +4 -4
- package/assets/r/app-sidebar.json +3 -3
- package/assets/r/avatar.json +1 -1
- package/assets/r/badge.json +1 -1
- package/assets/r/breadcrumb.json +1 -1
- package/assets/r/button-group.json +2 -2
- package/assets/r/button.json +1 -1
- package/assets/r/calendar.json +2 -2
- package/assets/r/card.json +1 -1
- package/assets/r/chart.json +1 -1
- package/assets/r/checkbox.json +1 -1
- package/assets/r/combobox.json +4 -4
- package/assets/r/command.json +1 -1
- package/assets/r/confirmacion.json +2 -2
- package/assets/r/counter-badge.json +1 -1
- package/assets/r/crm-app-layout.json +4 -4
- package/assets/r/crm-desk.json +6 -6
- package/assets/r/crm-detalle-caso.json +5 -5
- package/assets/r/date-picker.json +4 -4
- package/assets/r/dialog.json +1 -1
- package/assets/r/dropdown-menu.json +1 -1
- package/assets/r/empty.json +1 -1
- package/assets/r/feature-card.json +1 -1
- package/assets/r/form.json +2 -2
- package/assets/r/header-bar.json +2 -2
- package/assets/r/intake-wizard.json +5 -5
- package/assets/r/label.json +1 -1
- package/assets/r/login.json +5 -5
- package/assets/r/logo.json +1 -1
- package/assets/r/menubar.json +1 -1
- package/assets/r/navigation-menu.json +1 -1
- package/assets/r/pagination.json +2 -2
- package/assets/r/popover.json +1 -1
- package/assets/r/profile-card.json +3 -3
- package/assets/r/progress.json +1 -1
- package/assets/r/radio-group.json +1 -1
- package/assets/r/registry.json +49 -49
- package/assets/r/scroll-area.json +1 -1
- package/assets/r/searchbox.json +1 -1
- package/assets/r/select.json +1 -1
- package/assets/r/separator.json +1 -1
- package/assets/r/sheet.json +1 -1
- package/assets/r/sidebar.json +7 -7
- package/assets/r/skeleton.json +1 -1
- package/assets/r/slider.json +1 -1
- package/assets/r/snippet.json +1 -1
- package/assets/r/spinner.json +1 -1
- package/assets/r/status-dot.json +1 -1
- package/assets/r/switch.json +1 -1
- package/assets/r/table.json +1 -1
- package/assets/r/tabs.json +1 -1
- package/assets/r/tag.json +1 -1
- package/assets/r/textarea.json +1 -1
- package/assets/r/toaster.json +1 -1
- package/assets/r/tooltip.json +1 -1
- package/assets/r/tree.json +1 -1
- package/dist/index.js +117 -46
- package/package.json +1 -1
package/assets/r/checkbox.json
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"path": "components/base/Checkbox.tsx",
|
|
14
14
|
"type": "registry:ui",
|
|
15
15
|
"target": "components/base/Checkbox.tsx",
|
|
16
|
-
"content": "import * as
|
|
16
|
+
"content": "import * as CheckboxPrimitive from \"@radix-ui/react-checkbox\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils/cn\";\n\nconst Checkbox = React.forwardRef<\n React.ElementRef<typeof CheckboxPrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>\n>(({ className, ...props }, ref) => (\n <CheckboxPrimitive.Root\n ref={ref}\n className={cn(\n \"peer h-4 w-4 shrink-0 cursor-pointer rounded border border-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground\",\n className,\n )}\n {...props}\n >\n <CheckboxPrimitive.Indicator className={cn(\"flex items-center justify-center text-current\")}>\n <svg\n className=\"h-4 w-4\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"3\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n </CheckboxPrimitive.Indicator>\n </CheckboxPrimitive.Root>\n));\nCheckbox.displayName = CheckboxPrimitive.Root.displayName;\n\nexport { Checkbox };\n"
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
19
|
"path": "components/base/Checkbox.md",
|
package/assets/r/combobox.json
CHANGED
|
@@ -6,16 +6,16 @@
|
|
|
6
6
|
"description": "Campo de entrada con lista desplegable filtrable. Para búsqueda + selección. El componente vive en tu proyecto: instálalo con `create-lexy add combobox` y edítalo con libertad.",
|
|
7
7
|
"dependencies": [],
|
|
8
8
|
"registryDependencies": [
|
|
9
|
-
"button",
|
|
10
|
-
"command",
|
|
11
|
-
"popover"
|
|
9
|
+
"@lexy/button",
|
|
10
|
+
"@lexy/command",
|
|
11
|
+
"@lexy/popover"
|
|
12
12
|
],
|
|
13
13
|
"files": [
|
|
14
14
|
{
|
|
15
15
|
"path": "components/base/Combobox.tsx",
|
|
16
16
|
"type": "registry:ui",
|
|
17
17
|
"target": "components/base/Combobox.tsx",
|
|
18
|
-
"content": "import * as React from \"react\";\n\nimport { cn } from \"@/lib/utils/cn\";\nimport { Button } from \"./Button\";\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from \"./Command\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"./Popover\";\n\nexport interface ComboboxOption {\n value: string;\n label: string;\n}\n\n/**\n * Wrapper ergonómico sobre la receta compuesta Popover + Command. Si necesitas\n * variar la estructura (grupos, ítems con iconos, multi-selección, creación de\n * opciones), compón las piezas directamente — ver \"Receta compuesta\" en\n * Combobox.md.\n */\nexport interface ComboboxProps
|
|
18
|
+
"content": "import * as React from \"react\";\n\nimport { cn } from \"@/lib/utils/cn\";\n\nimport { Button } from \"./Button\";\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from \"./Command\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"./Popover\";\n\nexport interface ComboboxOption {\n value: string;\n label: string;\n}\n\n/**\n * Wrapper ergonómico sobre la receta compuesta Popover + Command. Si necesitas\n * variar la estructura (grupos, ítems con iconos, multi-selección, creación de\n * opciones), compón las piezas directamente — ver \"Receta compuesta\" en\n * Combobox.md.\n */\nexport interface ComboboxProps extends Omit<\n React.ComponentPropsWithoutRef<typeof Button>,\n \"value\" | \"defaultValue\" | \"onChange\"\n> {\n options: ComboboxOption[];\n value?: string;\n defaultValue?: string;\n onValueChange?: (value: string) => void;\n placeholder?: string;\n searchPlaceholder?: string;\n emptyMessage?: string;\n}\n\nconst Combobox = React.forwardRef<HTMLButtonElement, ComboboxProps>(function Combobox(\n {\n options,\n value,\n defaultValue,\n onValueChange,\n placeholder = \"Seleccionar…\",\n searchPlaceholder = \"Buscar…\",\n emptyMessage = \"Sin resultados\",\n className,\n ...props\n },\n ref,\n) {\n const [open, setOpen] = React.useState(false);\n const [internal, setInternal] = React.useState(defaultValue ?? \"\");\n const current = value ?? internal;\n const selected = options.find((option) => option.value === current);\n\n const handleSelect = (next: string) => {\n const resolved = next === current ? \"\" : next;\n setInternal(resolved);\n onValueChange?.(resolved);\n setOpen(false);\n };\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <Button\n ref={ref}\n variant=\"outline\"\n role=\"combobox\"\n aria-expanded={open}\n className={cn(\"w-full justify-between font-normal\", className)}\n {...props}\n >\n {selected ? selected.label : <span className=\"text-muted-foreground\">{placeholder}</span>}\n <svg\n className=\"ml-2 h-4 w-4 shrink-0 opacity-50\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"m7 15 5 5 5-5M7 9l5-5 5 5\" />\n </svg>\n </Button>\n </PopoverTrigger>\n <PopoverContent align=\"start\" className=\"w-[var(--radix-popover-trigger-width)] p-0\">\n <Command>\n <CommandInput placeholder={searchPlaceholder} />\n <CommandList>\n <CommandEmpty>{emptyMessage}</CommandEmpty>\n <CommandGroup>\n {options.map((option) => (\n <CommandItem\n key={option.value}\n value={option.label}\n onSelect={() => handleSelect(option.value)}\n >\n <svg\n className={cn(\n \"mr-2 h-4 w-4\",\n current === option.value ? \"opacity-100\" : \"opacity-0\",\n )}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n {option.label}\n </CommandItem>\n ))}\n </CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n );\n});\nCombobox.displayName = \"Combobox\";\n\nexport { Combobox };\n"
|
|
19
19
|
},
|
|
20
20
|
{
|
|
21
21
|
"path": "components/base/Combobox.md",
|
package/assets/r/command.json
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"path": "components/base/Command.tsx",
|
|
14
14
|
"type": "registry:ui",
|
|
15
15
|
"target": "components/base/Command.tsx",
|
|
16
|
-
"content": "import
|
|
16
|
+
"content": "import { Command as CommandPrimitive } from \"cmdk\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils/cn\";\n\nconst Command = React.forwardRef<\n React.ElementRef<typeof CommandPrimitive>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive>\n>(({ className, ...props }, ref) => (\n <CommandPrimitive\n ref={ref}\n className={cn(\n \"flex h-full w-full flex-col overflow-hidden rounded bg-popover text-popover-foreground\",\n className,\n )}\n {...props}\n />\n));\nCommand.displayName = CommandPrimitive.displayName;\n\nconst CommandInput = React.forwardRef<\n React.ElementRef<typeof CommandPrimitive.Input>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>\n>(({ className, ...props }, ref) => (\n <div className=\"flex items-center border-b border-border px-3\" cmdk-input-wrapper=\"\">\n <svg\n className=\"mr-2 h-4 w-4 shrink-0 opacity-50\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <circle cx=\"11\" cy=\"11\" r=\"8\" />\n <path d=\"m21 21-4.3-4.3\" />\n </svg>\n <CommandPrimitive.Input\n ref={ref}\n className={cn(\n \"flex h-10 w-full rounded bg-transparent py-3 text-sm text-foreground outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50\",\n className,\n )}\n {...props}\n />\n </div>\n));\nCommandInput.displayName = CommandPrimitive.Input.displayName;\n\nconst CommandList = React.forwardRef<\n React.ElementRef<typeof CommandPrimitive.List>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>\n>(({ className, ...props }, ref) => (\n <CommandPrimitive.List\n ref={ref}\n className={cn(\"max-h-75 overflow-y-auto overflow-x-hidden\", className)}\n {...props}\n />\n));\nCommandList.displayName = CommandPrimitive.List.displayName;\n\nconst CommandEmpty = React.forwardRef<\n React.ElementRef<typeof CommandPrimitive.Empty>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty>\n>((props, ref) => (\n <CommandPrimitive.Empty\n ref={ref}\n className=\"py-6 text-center text-sm text-muted-foreground\"\n {...props}\n />\n));\nCommandEmpty.displayName = CommandPrimitive.Empty.displayName;\n\nconst CommandGroup = React.forwardRef<\n React.ElementRef<typeof CommandPrimitive.Group>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group>\n>(({ className, ...props }, ref) => (\n <CommandPrimitive.Group\n ref={ref}\n className={cn(\n \"overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground\",\n className,\n )}\n {...props}\n />\n));\nCommandGroup.displayName = CommandPrimitive.Group.displayName;\n\nconst CommandSeparator = React.forwardRef<\n React.ElementRef<typeof CommandPrimitive.Separator>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n <CommandPrimitive.Separator\n ref={ref}\n className={cn(\"-mx-1 h-px bg-border\", className)}\n {...props}\n />\n));\nCommandSeparator.displayName = CommandPrimitive.Separator.displayName;\n\nconst CommandItem = React.forwardRef<\n React.ElementRef<typeof CommandPrimitive.Item>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item>\n>(({ className, ...props }, ref) => (\n <CommandPrimitive.Item\n ref={ref}\n className={cn(\n \"relative flex cursor-pointer select-none items-center gap-2 rounded px-2 py-1.5 text-sm text-foreground outline-none data-[disabled=true]:pointer-events-none data-[selected=true]:bg-primary/15 data-[selected=true]:text-primary data-[disabled=true]:opacity-50\",\n className,\n )}\n {...props}\n />\n));\nCommandItem.displayName = CommandPrimitive.Item.displayName;\n\nconst CommandShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => (\n <span\n className={cn(\"ml-auto text-xs tracking-widest text-muted-foreground\", className)}\n {...props}\n />\n);\nCommandShortcut.displayName = \"CommandShortcut\";\n\nexport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n CommandSeparator,\n CommandShortcut,\n};\n"
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
19
|
"path": "components/base/Command.md",
|
|
@@ -8,14 +8,14 @@
|
|
|
8
8
|
"lucide-react"
|
|
9
9
|
],
|
|
10
10
|
"registryDependencies": [
|
|
11
|
-
"button"
|
|
11
|
+
"@lexy/button"
|
|
12
12
|
],
|
|
13
13
|
"files": [
|
|
14
14
|
{
|
|
15
15
|
"path": "views/EnvioConfirmado.tsx",
|
|
16
16
|
"type": "registry:file",
|
|
17
17
|
"target": "views/EnvioConfirmado.tsx",
|
|
18
|
-
"content": "import { CheckCircle2 } from \"lucide-react\";\nimport { Button } from \"@/components/base/Button\";\n\n// Receta cliente — confirmación / éxito: celebra breve, di qué pasa ahora y\n// dónde seguir. Ver confirmacion.md.\n\nexport function EnvioConfirmado() {\n return (\n <main className
|
|
18
|
+
"content": "import { CheckCircle2 } from \"lucide-react\";\n\nimport { Button } from \"@/components/base/Button\";\n\n// Receta cliente — confirmación / éxito: celebra breve, di qué pasa ahora y\n// dónde seguir. Ver confirmacion.md.\n\nexport function EnvioConfirmado() {\n return (\n <main className=\"mx-auto flex min-h-[60vh] w-full max-w-md flex-col items-center justify-center px-4 text-center\">\n <span className=\"mb-4 flex size-12 items-center justify-center rounded-full bg-success/10\">\n <CheckCircle2 className=\"size-6 text-success\" aria-hidden />\n </span>\n <h1 className=\"text-2xl font-bold text-foreground\">¡Enviamos tus documentos!</h1>\n <p className=\"mt-2 text-base text-muted-foreground\">\n El equipo legal los está revisando. Podrás seguir su estado en la pestaña «Enviados» y te\n avisaremos si falta algo.\n </p>\n <Button className=\"mt-6\">Volver al inicio</Button>\n </main>\n );\n}\n"
|
|
19
19
|
},
|
|
20
20
|
{
|
|
21
21
|
"path": "views/confirmacion.md",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"path": "components/base/CounterBadge.tsx",
|
|
14
14
|
"type": "registry:ui",
|
|
15
15
|
"target": "components/base/CounterBadge.tsx",
|
|
16
|
-
"content": "import
|
|
16
|
+
"content": "import { cva, type VariantProps } from \"class-variance-authority\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils/cn\";\n\nfunction formatCount(children: React.ReactNode, max?: number): React.ReactNode {\n if (max == null) return children;\n const n =\n typeof children === \"number\"\n ? children\n : typeof children === \"string\" && /^\\d+$/.test(children.trim())\n ? Number.parseInt(children, 10)\n : null;\n if (n == null || n <= max) return children;\n return `${max}+`;\n}\n\nconst counterBadgeVariants = cva(\n \"inline-flex select-none items-center justify-center rounded-full bg-primary font-semibold tabular-nums text-primary-foreground\",\n {\n variants: {\n size: {\n sm: \"h-4 min-w-4 px-1 text-xs leading-none\",\n md: \"h-5 min-w-5 px-1.5 text-xs leading-none\",\n lg: \"h-6 min-w-6 px-1.5 text-xs/[1]\",\n },\n },\n defaultVariants: { size: \"md\" },\n },\n);\n\nexport interface CounterBadgeProps\n extends React.HTMLAttributes<HTMLSpanElement>, VariantProps<typeof counterBadgeVariants> {\n /** Tope numérico (ej. 99 → \"99+\"). */\n max?: number;\n}\n\nconst CounterBadge = React.forwardRef<HTMLSpanElement, CounterBadgeProps>(\n ({ size, max, className, children, ...props }, ref) => (\n <span ref={ref} className={cn(counterBadgeVariants({ size }), className)} {...props}>\n {formatCount(children, max)}\n </span>\n ),\n);\nCounterBadge.displayName = \"CounterBadge\";\n\nexport { CounterBadge, counterBadgeVariants };\n"
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
19
|
"path": "components/base/CounterBadge.md",
|
|
@@ -8,16 +8,16 @@
|
|
|
8
8
|
"lucide-react"
|
|
9
9
|
],
|
|
10
10
|
"registryDependencies": [
|
|
11
|
-
"app-sidebar",
|
|
12
|
-
"logo",
|
|
13
|
-
"sidebar"
|
|
11
|
+
"@lexy/app-sidebar",
|
|
12
|
+
"@lexy/logo",
|
|
13
|
+
"@lexy/sidebar"
|
|
14
14
|
],
|
|
15
15
|
"files": [
|
|
16
16
|
{
|
|
17
17
|
"path": "views/AppLayout.tsx",
|
|
18
18
|
"type": "registry:file",
|
|
19
19
|
"target": "views/AppLayout.tsx",
|
|
20
|
-
"content": "import
|
|
20
|
+
"content": "import { FolderKanban, Inbox, Settings } from \"lucide-react\";\nimport * as React from \"react\";\n\nimport { AppSidebar } from \"@/components/base/AppSidebar\";\nimport { Logo } from \"@/components/base/Logo\";\nimport { SidebarInset, SidebarProvider } from \"@/components/base/Sidebar\";\n\n// Receta CRM — layout de aplicación: navegación lateral colapsable data-driven\n// y área de trabajo. Las vistas del desk y el detalle viven dentro de\n// SidebarInset. Ver crm-app-layout.md.\n\nexport function AppLayout({ children }: { children: React.ReactNode }) {\n return (\n <SidebarProvider>\n <AppSidebar\n logo={<Logo layout=\"horizontal\" />}\n groups={[\n {\n label: \"Trabajo\",\n items: [\n { title: \"Casos\", icon: FolderKanban, url: \"/casos\", isActive: true },\n { title: \"Bandeja\", icon: Inbox, url: \"/bandeja\" },\n ],\n },\n {\n label: \"Sistema\",\n items: [{ title: \"Configuración\", icon: Settings, url: \"/config\" }],\n },\n ]}\n />\n <SidebarInset>{children}</SidebarInset>\n </SidebarProvider>\n );\n}\n"
|
|
21
21
|
},
|
|
22
22
|
{
|
|
23
23
|
"path": "views/crm-app-layout.md",
|
package/assets/r/crm-desk.json
CHANGED
|
@@ -8,18 +8,18 @@
|
|
|
8
8
|
"lucide-react"
|
|
9
9
|
],
|
|
10
10
|
"registryDependencies": [
|
|
11
|
-
"button",
|
|
12
|
-
"input",
|
|
13
|
-
"pagination",
|
|
14
|
-
"status-dot",
|
|
15
|
-
"table"
|
|
11
|
+
"@lexy/button",
|
|
12
|
+
"@lexy/input",
|
|
13
|
+
"@lexy/pagination",
|
|
14
|
+
"@lexy/status-dot",
|
|
15
|
+
"@lexy/table"
|
|
16
16
|
],
|
|
17
17
|
"files": [
|
|
18
18
|
{
|
|
19
19
|
"path": "views/CasosDesk.tsx",
|
|
20
20
|
"type": "registry:file",
|
|
21
21
|
"target": "views/CasosDesk.tsx",
|
|
22
|
-
"content": "import { Plus } from \"lucide-react\";\nimport { Button } from \"@/components/base/Button\";\nimport { Input } from \"@/components/base/Input\";\nimport {
|
|
22
|
+
"content": "import { Plus } from \"lucide-react\";\n\nimport { Button } from \"@/components/base/Button\";\nimport { Input } from \"@/components/base/Input\";\nimport {\n Pagination,\n PaginationContent,\n PaginationItem,\n PaginationLink,\n PaginationNext,\n PaginationPrevious,\n} from \"@/components/base/Pagination\";\nimport { StatusDot } from \"@/components/base/StatusDot\";\nimport { Table } from \"@/components/base/Table\";\n\n// Receta CRM — desk con tabla y acciones: ancho completo, toolbar con búsqueda\n// y acción primaria, tabla densa con estado por fila, paginación al pie.\n// Ver crm-desk.md.\n\nexport function CasosDesk() {\n return (\n <main className=\"w-full p-4\">\n <header className=\"mb-4 flex items-center justify-between gap-4\">\n <div>\n <h1 className=\"text-xl font-semibold text-foreground\">Casos</h1>\n <p className=\"text-sm text-muted-foreground\">24 activos · 3 vencen hoy</p>\n </div>\n <div className=\"flex items-center gap-2\">\n <Input\n className=\"h-9 w-64\"\n placeholder=\"Buscar por cliente o RUT\"\n aria-label=\"Buscar casos\"\n />\n <Button size=\"sm\">\n <Plus /> Nuevo caso\n </Button>\n </div>\n </header>\n\n <Table columns=\"2fr 1.5fr 1fr 1fr\" stickyHeader>\n <Table.Header>\n <Table.Cell>Cliente</Table.Cell>\n <Table.Cell>Materia</Table.Cell>\n <Table.Cell>Estado</Table.Cell>\n <Table.Cell>Plazo</Table.Cell>\n </Table.Header>\n <Table.Content>\n <Table.Row onClick={() => {}}>\n <Table.Cell className=\"font-medium\">María Pérez</Table.Cell>\n <Table.Cell>Despido</Table.Cell>\n <Table.Cell>\n <StatusDot tone=\"warning\">En revisión</StatusDot>\n </Table.Cell>\n <Table.Cell className=\"text-warning\">Vence hoy</Table.Cell>\n </Table.Row>\n <Table.Row onClick={() => {}}>\n <Table.Cell className=\"font-medium\">Juan Soto</Table.Cell>\n <Table.Cell>Deuda</Table.Cell>\n <Table.Cell>\n <StatusDot tone=\"success\">Al día</StatusDot>\n </Table.Cell>\n <Table.Cell className=\"text-muted-foreground\">12 días</Table.Cell>\n </Table.Row>\n </Table.Content>\n </Table>\n\n <footer className=\"mt-4 flex items-center justify-between\">\n <p className=\"text-sm text-muted-foreground\">Mostrando 1–20 de 64 casos</p>\n <Pagination className=\"mx-0 w-auto justify-end\">\n <PaginationContent>\n <PaginationItem>\n <PaginationPrevious href=\"#\" />\n </PaginationItem>\n <PaginationItem>\n <PaginationLink href=\"#\" isActive>\n 1\n </PaginationLink>\n </PaginationItem>\n <PaginationItem>\n <PaginationLink href=\"#\">2</PaginationLink>\n </PaginationItem>\n <PaginationItem>\n <PaginationLink href=\"#\">3</PaginationLink>\n </PaginationItem>\n <PaginationItem>\n <PaginationNext href=\"#\" />\n </PaginationItem>\n </PaginationContent>\n </Pagination>\n </footer>\n </main>\n );\n}\n"
|
|
23
23
|
},
|
|
24
24
|
{
|
|
25
25
|
"path": "views/crm-desk.md",
|
|
@@ -6,17 +6,17 @@
|
|
|
6
6
|
"description": "Detalle master-detail para **CRM**: contexto del caso a la izquierda (panel fijo de 288px), trabajo al centro en tabs, acción primaria a la mano. La información que se consulta junta vive junta. Instálalo con `create-lexy add crm-detalle-caso` — trae `Tabs`, `Tag`, `Separator` y `Button`. La vista queda en tu proyecto (`views/CasoDetalle.tsx`): edítala con libertad.",
|
|
7
7
|
"dependencies": [],
|
|
8
8
|
"registryDependencies": [
|
|
9
|
-
"button",
|
|
10
|
-
"separator",
|
|
11
|
-
"tabs",
|
|
12
|
-
"tag"
|
|
9
|
+
"@lexy/button",
|
|
10
|
+
"@lexy/separator",
|
|
11
|
+
"@lexy/tabs",
|
|
12
|
+
"@lexy/tag"
|
|
13
13
|
],
|
|
14
14
|
"files": [
|
|
15
15
|
{
|
|
16
16
|
"path": "views/CasoDetalle.tsx",
|
|
17
17
|
"type": "registry:file",
|
|
18
18
|
"target": "views/CasoDetalle.tsx",
|
|
19
|
-
"content": "import { Button } from \"@/components/base/Button\";\nimport { Separator } from \"@/components/base/Separator\";\nimport {
|
|
19
|
+
"content": "import { Button } from \"@/components/base/Button\";\nimport { Separator } from \"@/components/base/Separator\";\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from \"@/components/base/Tabs\";\nimport { Tag } from \"@/components/base/Tag\";\n\n// Receta CRM — detalle de caso (master-detail): contexto a la izquierda,\n// trabajo al centro, acciones a la mano. Ver crm-detalle-caso.md.\n\nexport function CasoDetalle() {\n return (\n <main className=\"flex w-full gap-4 p-4\">\n <aside className=\"w-72 shrink-0 space-y-4\">\n <div>\n <h1 className=\"text-lg font-semibold text-foreground\">María Pérez</h1>\n <p className=\"text-sm text-muted-foreground\">Caso #1042 · Despido</p>\n </div>\n <Tag tone=\"warning\">En revisión</Tag>\n <Separator />\n <dl className=\"space-y-2 text-sm\">\n <div className=\"flex justify-between\">\n <dt className=\"text-muted-foreground\">Abogado</dt>\n <dd className=\"text-foreground\">C. Rivera</dd>\n </div>\n <div className=\"flex justify-between\">\n <dt className=\"text-muted-foreground\">Plazo</dt>\n <dd className=\"text-warning\">Vence hoy</dd>\n </div>\n </dl>\n </aside>\n\n <section className=\"min-w-0 flex-1\">\n <div className=\"mb-3 flex items-center justify-between\">\n <h2 className=\"text-base font-semibold text-foreground\">Gestiones</h2>\n <Button size=\"sm\">Nueva gestión</Button>\n </div>\n <Tabs defaultValue=\"actividad\">\n <TabsList>\n <TabsTrigger value=\"actividad\">Actividad</TabsTrigger>\n <TabsTrigger value=\"documentos\">Documentos</TabsTrigger>\n <TabsTrigger value=\"notas\">Notas</TabsTrigger>\n </TabsList>\n <TabsContent value=\"actividad\">{/* timeline */}</TabsContent>\n <TabsContent value=\"documentos\">{/* lista */}</TabsContent>\n <TabsContent value=\"notas\">{/* notas */}</TabsContent>\n </Tabs>\n </section>\n </main>\n );\n}\n"
|
|
20
20
|
},
|
|
21
21
|
{
|
|
22
22
|
"path": "views/crm-detalle-caso.md",
|
|
@@ -9,16 +9,16 @@
|
|
|
9
9
|
"react-day-picker@^9.8.0"
|
|
10
10
|
],
|
|
11
11
|
"registryDependencies": [
|
|
12
|
-
"button",
|
|
13
|
-
"calendar",
|
|
14
|
-
"popover"
|
|
12
|
+
"@lexy/button",
|
|
13
|
+
"@lexy/calendar",
|
|
14
|
+
"@lexy/popover"
|
|
15
15
|
],
|
|
16
16
|
"files": [
|
|
17
17
|
{
|
|
18
18
|
"path": "components/base/DatePicker.tsx",
|
|
19
19
|
"type": "registry:ui",
|
|
20
20
|
"target": "components/base/DatePicker.tsx",
|
|
21
|
-
"content": "
|
|
21
|
+
"content": "/* eslint-disable jsx-a11y/no-autofocus -- el foco entra al calendario al abrir el popover (patrón diálogo) */\nimport { CalendarIcon } from \"lucide-react\";\nimport * as React from \"react\";\nimport type { Locale } from \"react-day-picker\";\nimport { es } from \"react-day-picker/locale\";\n\nimport { cn } from \"@/lib/utils/cn\";\n\nimport { Button } from \"./Button\";\nimport { Calendar } from \"./Calendar\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"./Popover\";\n\nfunction formatDate(date: Date, localeCode?: string) {\n return date.toLocaleDateString(localeCode, {\n day: \"numeric\",\n month: \"long\",\n year: \"numeric\",\n });\n}\n\n/**\n * Wrapper ergonómico sobre la receta compuesta Popover + Button + Calendar.\n * Si necesitas variar la estructura (rango, dos meses, footer, otro trigger),\n * compón las piezas directamente — ver \"Receta compuesta\" en DatePicker.md.\n */\nexport interface DatePickerProps extends Omit<\n React.ComponentPropsWithoutRef<typeof Button>,\n \"value\" | \"defaultValue\" | \"onChange\"\n> {\n /** Fecha seleccionada (modo controlado: pásala junto a `onChange`). */\n value?: Date;\n /** Fecha inicial en modo no controlado. */\n defaultValue?: Date;\n /** Callback de selección (activa el modo controlado). */\n onChange?: (date: Date | undefined) => void;\n placeholder?: string;\n captionLayout?: React.ComponentProps<typeof Calendar>[\"captionLayout\"];\n locale?: Partial<Locale>;\n align?: React.ComponentProps<typeof PopoverContent>[\"align\"];\n}\n\nconst DatePicker = React.forwardRef<HTMLButtonElement, DatePickerProps>(function DatePicker(\n {\n value,\n defaultValue,\n onChange,\n placeholder = \"Selecciona una fecha\",\n captionLayout = \"label\",\n locale = es,\n align = \"start\",\n className,\n ...props\n },\n ref,\n) {\n const [open, setOpen] = React.useState(false);\n const [internal, setInternal] = React.useState<Date | undefined>(defaultValue);\n\n // Controlado si se provee `onChange`; si no, mantenemos estado interno.\n const selected = onChange ? value : internal;\n\n const handleSelect = (date: Date | undefined) => {\n if (onChange) onChange(date);\n else setInternal(date);\n setOpen(false);\n };\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <Button\n ref={ref}\n type=\"button\"\n variant=\"outline\"\n data-empty={!selected}\n className={cn(\n \"w-65 justify-start text-left font-normal data-[empty=true]:text-muted-foreground\",\n className,\n )}\n {...props}\n >\n <CalendarIcon />\n {selected ? formatDate(selected, locale?.code) : placeholder}\n </Button>\n </PopoverTrigger>\n {/* El panel toma el ancho del trigger (igual que Combobox); el calendario\n (248px natural) se centra dentro. */}\n <PopoverContent\n className=\"flex w-[var(--radix-popover-trigger-width)] justify-center p-0\"\n align={align}\n >\n <Calendar\n mode=\"single\"\n selected={selected}\n onSelect={handleSelect}\n captionLayout={captionLayout}\n locale={locale}\n autoFocus\n />\n </PopoverContent>\n </Popover>\n );\n});\nDatePicker.displayName = \"DatePicker\";\n\nexport { DatePicker };\n"
|
|
22
22
|
},
|
|
23
23
|
{
|
|
24
24
|
"path": "components/base/DatePicker.md",
|
package/assets/r/dialog.json
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"path": "components/base/Dialog.tsx",
|
|
14
14
|
"type": "registry:ui",
|
|
15
15
|
"target": "components/base/Dialog.tsx",
|
|
16
|
-
"content": "import * as
|
|
16
|
+
"content": "import * as DialogPrimitive from \"@radix-ui/react-dialog\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils/cn\";\n\nconst Dialog = DialogPrimitive.Root;\nconst DialogTrigger = DialogPrimitive.Trigger;\nconst DialogPortal = DialogPrimitive.Portal;\nconst DialogClose = DialogPrimitive.Close;\n\nconst DialogOverlay = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Overlay>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.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));\nDialogOverlay.displayName = DialogPrimitive.Overlay.displayName;\n\nconst DialogContent = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>\n>(({ className, children, ...props }, ref) => (\n <DialogPortal>\n <DialogOverlay />\n <DialogPrimitive.Content\n ref={ref}\n className={cn(\n \"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-border bg-background p-6 shadow-lg duration-200 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 sm:rounded-lg\",\n className,\n )}\n {...props}\n >\n {children}\n {/* Sin X de cierre: se cierra con Escape, click en el overlay o una\n acción explícita (DialogClose / botón Cancelar de AppDialog). */}\n </DialogPrimitive.Content>\n </DialogPortal>\n));\nDialogContent.displayName = DialogPrimitive.Content.displayName;\n\nconst DialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (\n <div className={cn(\"flex flex-col space-y-2 text-center sm:text-left\", className)} {...props} />\n);\nDialogHeader.displayName = \"DialogHeader\";\n\nconst DialogFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2\", className)}\n {...props}\n />\n);\nDialogFooter.displayName = \"DialogFooter\";\n\nconst DialogTitle = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Title>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Title\n ref={ref}\n className={cn(\"text-lg font-semibold leading-none tracking-tight text-foreground\", className)}\n {...props}\n />\n));\nDialogTitle.displayName = DialogPrimitive.Title.displayName;\n\nconst DialogDescription = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Description>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Description\n ref={ref}\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n));\nDialogDescription.displayName = DialogPrimitive.Description.displayName;\n\nexport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogOverlay,\n DialogPortal,\n DialogTitle,\n DialogTrigger,\n};\n"
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
19
|
"path": "components/base/Dialog.md",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"path": "components/base/DropdownMenu.tsx",
|
|
14
14
|
"type": "registry:ui",
|
|
15
15
|
"target": "components/base/DropdownMenu.tsx",
|
|
16
|
-
"content": "import * as
|
|
16
|
+
"content": "import * as DropdownMenuPrimitive from \"@radix-ui/react-dropdown-menu\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils/cn\";\n\nconst DropdownMenu = DropdownMenuPrimitive.Root;\nconst DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;\nconst DropdownMenuGroup = DropdownMenuPrimitive.Group;\nconst DropdownMenuPortal = DropdownMenuPrimitive.Portal;\nconst DropdownMenuSub = DropdownMenuPrimitive.Sub;\nconst DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;\n\nconst ChevronRightIcon = () => (\n <svg\n className=\"ml-auto 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 >\n <path d=\"m9 18 6-6-6-6\" />\n </svg>\n);\n\nconst CheckIcon = () => (\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 >\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n);\n\nconst DotIcon = () => (\n <svg className=\"h-2 w-2 fill-current\" viewBox=\"0 0 24 24\">\n <circle cx=\"12\" cy=\"12\" r=\"12\" />\n </svg>\n);\n\nconst DropdownMenuSubTrigger = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {\n inset?: boolean;\n }\n>(({ className, inset, children, ...props }, ref) => (\n <DropdownMenuPrimitive.SubTrigger\n ref={ref}\n className={cn(\n \"flex cursor-pointer select-none items-center rounded px-2 py-1.5 text-sm outline-none focus:bg-primary/15 focus:text-primary data-[state=open]:bg-primary/15 data-[state=open]:text-primary\",\n inset && \"pl-8\",\n className,\n )}\n {...props}\n >\n {children}\n <ChevronRightIcon />\n </DropdownMenuPrimitive.SubTrigger>\n));\nDropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName;\n\nconst DropdownMenuSubContent = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>\n>(({ className, ...props }, ref) => (\n <DropdownMenuPrimitive.SubContent\n ref={ref}\n className={cn(\n \"z-50 min-w-32 overflow-hidden rounded border border-border bg-popover p-1 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\",\n className,\n )}\n {...props}\n />\n));\nDropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName;\n\nconst DropdownMenuContent = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>\n>(({ className, sideOffset = 4, ...props }, ref) => (\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.Content\n ref={ref}\n sideOffset={sideOffset}\n className={cn(\n \"z-50 min-w-32 overflow-hidden rounded border border-border bg-popover p-1 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\",\n className,\n )}\n {...props}\n />\n </DropdownMenuPrimitive.Portal>\n));\nDropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;\n\nconst DropdownMenuItem = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Item>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {\n inset?: boolean;\n }\n>(({ className, inset, ...props }, ref) => (\n <DropdownMenuPrimitive.Item\n ref={ref}\n className={cn(\n \"relative flex cursor-pointer select-none items-center gap-2 rounded px-2 py-1.5 text-sm outline-none transition-colors focus:bg-primary/15 focus:text-primary data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n inset && \"pl-8\",\n className,\n )}\n {...props}\n />\n));\nDropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;\n\nconst DropdownMenuCheckboxItem = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>\n>(({ className, children, checked, ...props }, ref) => (\n <DropdownMenuPrimitive.CheckboxItem\n ref={ref}\n className={cn(\n \"relative flex cursor-pointer select-none items-center rounded py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-primary/15 focus:text-primary data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n className,\n )}\n checked={checked}\n {...props}\n >\n <span className=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\">\n <DropdownMenuPrimitive.ItemIndicator>\n <CheckIcon />\n </DropdownMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </DropdownMenuPrimitive.CheckboxItem>\n));\nDropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName;\n\nconst DropdownMenuRadioItem = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>\n>(({ className, children, ...props }, ref) => (\n <DropdownMenuPrimitive.RadioItem\n ref={ref}\n className={cn(\n \"relative flex cursor-pointer select-none items-center rounded py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-primary/15 focus:text-primary data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n className,\n )}\n {...props}\n >\n <span className=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\">\n <DropdownMenuPrimitive.ItemIndicator>\n <DotIcon />\n </DropdownMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </DropdownMenuPrimitive.RadioItem>\n));\nDropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;\n\nconst DropdownMenuLabel = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Label>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {\n inset?: boolean;\n }\n>(({ className, inset, ...props }, ref) => (\n <DropdownMenuPrimitive.Label\n ref={ref}\n className={cn(\"px-2 py-1.5 text-sm font-semibold text-foreground\", inset && \"pl-8\", className)}\n {...props}\n />\n));\nDropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;\n\nconst DropdownMenuSeparator = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Separator>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n <DropdownMenuPrimitive.Separator\n ref={ref}\n className={cn(\"-mx-1 my-1 h-px bg-border\", className)}\n {...props}\n />\n));\nDropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;\n\nconst DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => (\n <span\n className={cn(\"ml-auto text-xs tracking-widest text-muted-foreground\", className)}\n {...props}\n />\n);\nDropdownMenuShortcut.displayName = \"DropdownMenuShortcut\";\n\nexport {\n DropdownMenu,\n DropdownMenuCheckboxItem,\n DropdownMenuContent,\n DropdownMenuGroup,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuPortal,\n DropdownMenuRadioGroup,\n DropdownMenuRadioItem,\n DropdownMenuSeparator,\n DropdownMenuShortcut,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n DropdownMenuTrigger,\n};\n"
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
19
|
"path": "components/base/DropdownMenu.md",
|
package/assets/r/empty.json
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"path": "components/base/Empty.tsx",
|
|
14
14
|
"type": "registry:ui",
|
|
15
15
|
"target": "components/base/Empty.tsx",
|
|
16
|
-
"content": "import
|
|
16
|
+
"content": "import { cva, type VariantProps } from \"class-variance-authority\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils/cn\";\n\nfunction Empty({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"empty\"\n className={cn(\n \"flex w-full min-w-0 flex-1 flex-col items-center justify-center gap-4 rounded-lg border-dashed p-12 text-center text-balance\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction EmptyHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"empty-header\"\n className={cn(\"flex max-w-sm flex-col items-center gap-2\", className)}\n {...props}\n />\n );\n}\n\nconst emptyMediaVariants = cva(\n \"mb-2 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0\",\n {\n variants: {\n variant: {\n default: \"bg-transparent\",\n icon: \"flex size-10 shrink-0 items-center justify-center rounded-lg bg-muted text-foreground [&_svg:not([class*='size-'])]:size-6\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n },\n },\n);\n\nfunction EmptyMedia({\n className,\n variant = \"default\",\n ...props\n}: React.ComponentProps<\"div\"> & VariantProps<typeof emptyMediaVariants>) {\n return (\n <div\n data-slot=\"empty-icon\"\n data-variant={variant}\n className={cn(emptyMediaVariants({ variant, className }))}\n {...props}\n />\n );\n}\n\nfunction EmptyTitle({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"empty-title\"\n className={cn(\"font-sans text-lg font-medium tracking-tight\", className)}\n {...props}\n />\n );\n}\n\nfunction EmptyDescription({ className, ...props }: React.ComponentProps<\"p\">) {\n return (\n <div\n data-slot=\"empty-description\"\n className={cn(\n \"text-sm/relaxed text-muted-foreground [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction EmptyContent({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"empty-content\"\n className={cn(\n \"flex w-full max-w-sm min-w-0 flex-col items-center gap-4 text-sm text-balance\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport {\n Empty,\n EmptyContent,\n EmptyDescription,\n EmptyHeader,\n EmptyMedia,\n emptyMediaVariants,\n EmptyTitle,\n};\n"
|
|
17
17
|
},
|
|
18
18
|
{
|
|
19
19
|
"path": "components/base/Empty.md",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"path": "components/base/FeatureCard.tsx",
|
|
12
12
|
"type": "registry:ui",
|
|
13
13
|
"target": "components/base/FeatureCard.tsx",
|
|
14
|
-
"content": "import * as React from \"react\";\n\nimport { cn } from \"@/lib/utils/cn\";\n\nexport interface FeatureCardProps
|
|
14
|
+
"content": "import * as React from \"react\";\n\nimport { cn } from \"@/lib/utils/cn\";\n\nexport interface FeatureCardProps extends Omit<React.HTMLAttributes<HTMLElement>, \"title\"> {\n title: React.ReactNode;\n description?: React.ReactNode;\n /** Icono (cualquier nodo: SVG, lucide, etc.). */\n icon?: React.ReactNode;\n /** Si se define, renderiza como <a> con afordancia de hover/foco. */\n href?: string;\n target?: string;\n rel?: string;\n}\n\nconst cardClassName = (interactive: boolean) =>\n cn(\n \"group flex w-full flex-col gap-4 rounded-lg border border-border bg-background p-6 transition-[border-color,box-shadow] duration-150\",\n interactive &&\n \"cursor-pointer hover:border-primary/40 hover:shadow-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\",\n );\n\nfunction FeatureCardContent({\n title,\n description,\n icon,\n interactive,\n}: {\n title: React.ReactNode;\n description?: React.ReactNode;\n icon?: React.ReactNode;\n interactive: boolean;\n}) {\n return (\n <>\n {(icon || interactive) && (\n <div className=\"flex items-start justify-between gap-3\">\n {icon ? (\n <span\n className=\"inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-md bg-accent text-primary [&>svg]:h-4 [&>svg]:w-4\"\n aria-hidden\n >\n {icon}\n </span>\n ) : (\n <span className=\"min-w-0 flex-1\" />\n )}\n {interactive && (\n <svg\n className=\"h-4 w-4 shrink-0 text-muted-foreground transition-transform group-hover:-translate-y-0.5 group-hover:translate-x-0.5 group-hover:text-foreground\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden\n >\n <path d=\"M7 7h10v10M7 17 17 7\" />\n </svg>\n )}\n </div>\n )}\n <div className=\"flex flex-col gap-1\">\n <h3 className=\"text-sm font-medium text-foreground\">{title}</h3>\n {description != null && description !== \"\" && (\n <p className=\"text-xs leading-relaxed text-muted-foreground\">{description}</p>\n )}\n </div>\n </>\n );\n}\n\nexport const FeatureCard = React.forwardRef<HTMLElement, FeatureCardProps>(function FeatureCard(\n { title, description, icon, href, target, rel, className, ...props },\n ref,\n) {\n const interactive = href != null && href !== \"\";\n const classes = cn(cardClassName(interactive), className);\n\n if (interactive) {\n const resolvedRel = rel ?? (target === \"_blank\" ? \"noopener noreferrer\" : undefined);\n return (\n <a\n ref={ref as React.Ref<HTMLAnchorElement>}\n href={href}\n target={target}\n rel={resolvedRel}\n className={classes}\n {...(props as React.AnchorHTMLAttributes<HTMLAnchorElement>)}\n >\n <FeatureCardContent title={title} description={description} icon={icon} interactive />\n </a>\n );\n }\n\n return (\n <article\n ref={ref as React.Ref<HTMLElement>}\n className={classes}\n {...(props as React.HTMLAttributes<HTMLElement>)}\n >\n <FeatureCardContent title={title} description={description} icon={icon} interactive={false} />\n </article>\n );\n});\n"
|
|
15
15
|
},
|
|
16
16
|
{
|
|
17
17
|
"path": "components/base/FeatureCard.md",
|
package/assets/r/form.json
CHANGED
|
@@ -11,14 +11,14 @@
|
|
|
11
11
|
"@hookform/resolvers@^5.2.0"
|
|
12
12
|
],
|
|
13
13
|
"registryDependencies": [
|
|
14
|
-
"label"
|
|
14
|
+
"@lexy/label"
|
|
15
15
|
],
|
|
16
16
|
"files": [
|
|
17
17
|
{
|
|
18
18
|
"path": "components/base/Form.tsx",
|
|
19
19
|
"type": "registry:ui",
|
|
20
20
|
"target": "components/base/Form.tsx",
|
|
21
|
-
"content": "import
|
|
21
|
+
"content": "import { Slot } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport {\n Controller,\n type ControllerProps,\n type FieldPath,\n type FieldValues,\n FormProvider,\n useFormContext,\n useFormState,\n} from \"react-hook-form\";\n\nimport { cn } from \"@/lib/utils/cn\";\n\nimport { Label } from \"./Label\";\n\n// Patrón shadcn sobre react-hook-form: Form provee el contexto, FormField\n// registra el campo (Controller) y FormItem/FormLabel/FormControl cablean\n// ids y aria-* automáticamente. Validación con zod vía\n// zodResolver(@hookform/resolvers) — `add form` instala ambas deps.\n\nconst Form = FormProvider;\n\ninterface FormFieldContextValue<\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n> {\n name: TName;\n}\n\nconst FormFieldContext = React.createContext<FormFieldContextValue>({} as FormFieldContextValue);\n\nconst FormField = <\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n>({\n ...props\n}: ControllerProps<TFieldValues, TName>) => {\n return (\n <FormFieldContext.Provider value={{ name: props.name }}>\n <Controller {...props} />\n </FormFieldContext.Provider>\n );\n};\n\nconst useFormField = () => {\n const fieldContext = React.useContext(FormFieldContext);\n const itemContext = React.useContext(FormItemContext);\n const { getFieldState } = useFormContext();\n const formState = useFormState({ name: fieldContext.name });\n const fieldState = getFieldState(fieldContext.name, formState);\n\n if (!fieldContext) {\n throw new Error(\"useFormField debe usarse dentro de <FormField>\");\n }\n\n const { id } = itemContext;\n\n return {\n id,\n name: fieldContext.name,\n formItemId: `${id}-form-item`,\n formDescriptionId: `${id}-form-item-description`,\n formMessageId: `${id}-form-item-message`,\n ...fieldState,\n };\n};\n\ninterface FormItemContextValue {\n id: string;\n}\n\nconst FormItemContext = React.createContext<FormItemContextValue>({} as FormItemContextValue);\n\nconst FormItem = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(\n ({ className, ...props }, ref) => {\n const id = React.useId();\n\n return (\n <FormItemContext.Provider value={{ id }}>\n <div ref={ref} className={cn(\"grid gap-2\", className)} {...props} />\n </FormItemContext.Provider>\n );\n },\n);\nFormItem.displayName = \"FormItem\";\n\nconst FormLabel = React.forwardRef<\n React.ElementRef<typeof Label>,\n React.ComponentPropsWithoutRef<typeof Label>\n>(({ className, ...props }, ref) => {\n const { error, formItemId } = useFormField();\n\n return (\n <Label\n ref={ref}\n className={cn(error && \"text-destructive\", className)}\n htmlFor={formItemId}\n {...props}\n />\n );\n});\nFormLabel.displayName = \"FormLabel\";\n\nconst FormControl = React.forwardRef<\n React.ElementRef<typeof Slot>,\n React.ComponentPropsWithoutRef<typeof Slot>\n>(({ ...props }, ref) => {\n const { error, formItemId, formDescriptionId, formMessageId } = useFormField();\n\n return (\n <Slot\n ref={ref}\n id={formItemId}\n aria-describedby={!error ? `${formDescriptionId}` : `${formDescriptionId} ${formMessageId}`}\n aria-invalid={!!error}\n {...props}\n />\n );\n});\nFormControl.displayName = \"FormControl\";\n\nconst FormDescription = React.forwardRef<\n HTMLParagraphElement,\n React.HTMLAttributes<HTMLParagraphElement>\n>(({ className, ...props }, ref) => {\n const { formDescriptionId } = useFormField();\n\n return (\n <p\n ref={ref}\n id={formDescriptionId}\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n );\n});\nFormDescription.displayName = \"FormDescription\";\n\nconst FormMessage = React.forwardRef<\n HTMLParagraphElement,\n React.HTMLAttributes<HTMLParagraphElement>\n>(({ className, children, ...props }, ref) => {\n const { error, formMessageId } = useFormField();\n const body = error ? String(error?.message ?? \"\") : children;\n\n if (!body) {\n return null;\n }\n\n return (\n <p\n ref={ref}\n id={formMessageId}\n className={cn(\"text-sm text-destructive\", className)}\n {...props}\n >\n {body}\n </p>\n );\n});\nFormMessage.displayName = \"FormMessage\";\n\nexport {\n Form,\n FormControl,\n FormDescription,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n useFormField,\n};\n"
|
|
22
22
|
},
|
|
23
23
|
{
|
|
24
24
|
"path": "components/base/Form.md",
|
package/assets/r/header-bar.json
CHANGED
|
@@ -8,14 +8,14 @@
|
|
|
8
8
|
"class-variance-authority"
|
|
9
9
|
],
|
|
10
10
|
"registryDependencies": [
|
|
11
|
-
"logo"
|
|
11
|
+
"@lexy/logo"
|
|
12
12
|
],
|
|
13
13
|
"files": [
|
|
14
14
|
{
|
|
15
15
|
"path": "components/base/HeaderBar.tsx",
|
|
16
16
|
"type": "registry:ui",
|
|
17
17
|
"target": "components/base/HeaderBar.tsx",
|
|
18
|
-
"content": "import
|
|
18
|
+
"content": "import { cva, type VariantProps } from \"class-variance-authority\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils/cn\";\n\nimport { Logo } from \"./Logo\";\n\n// `brand-navy` = navy/ink de marca Lexy (mismo del wordmark oscuro), definido\n// en el theme como --color-brand-navy.\nconst headerVariants = cva(\"flex h-16 w-full shrink-0 items-center justify-between gap-6\", {\n variants: {\n surface: {\n default: \"bg-background text-foreground\",\n transparent: \"bg-transparent text-foreground\",\n navy: \"bg-brand-navy text-white\",\n },\n bordered: { true: \"border-b\", false: \"\" },\n padding: {\n page: \"px-4 md:px-6 lg:px-8\",\n none: \"px-0\",\n },\n sticky: { true: \"sticky top-0 z-50\", false: \"\" },\n },\n compoundVariants: [\n { surface: \"default\", bordered: true, class: \"border-border\" },\n { surface: \"transparent\", bordered: true, class: \"border-border\" },\n { surface: \"navy\", bordered: true, class: \"border-white/15\" },\n ],\n defaultVariants: {\n surface: \"default\",\n bordered: true,\n padding: \"page\",\n sticky: false,\n },\n});\n\nexport interface HeaderBarProps\n extends React.HTMLAttributes<HTMLElement>, VariantProps<typeof headerVariants> {\n /** Acciones alineadas a la derecha (botones, avatar, menús). */\n actions?: React.ReactNode;\n /** Sobrescribe el área de marca (por defecto, el `Logo`). */\n brand?: React.ReactNode;\n}\n\nexport const HeaderBar = React.forwardRef<HTMLElement, HeaderBarProps>(function HeaderBar(\n { surface = \"default\", bordered, padding, sticky, actions, brand, className, ...props },\n ref,\n) {\n const logoSurface = surface === \"navy\" ? \"dark\" : \"light\";\n\n return (\n <header\n ref={ref}\n className={cn(headerVariants({ surface, bordered, padding, sticky }), className)}\n {...props}\n >\n <div className=\"flex min-w-0 items-center gap-6\">\n {brand ?? <Logo surface={logoSurface} />}\n </div>\n {actions != null && <div className=\"flex shrink-0 items-center gap-2\">{actions}</div>}\n </header>\n );\n});\n\nHeaderBar.displayName = \"HeaderBar\";\n\nexport { headerVariants };\n"
|
|
19
19
|
},
|
|
20
20
|
{
|
|
21
21
|
"path": "components/base/HeaderBar.md",
|
|
@@ -6,17 +6,17 @@
|
|
|
6
6
|
"description": "Paso de wizard de intake para **mundo cliente**: un paso enfocado, columna angosta (`max-w-xl`), indicador de avance arriba y CTA al final. Instálalo con `create-lexy add intake-wizard` — trae consigo `Progress`, `Input`, `Label` y `Button`. La vista queda en tu proyecto (`views/IntakeDatosPersonales.tsx`): edítala con libertad.",
|
|
7
7
|
"dependencies": [],
|
|
8
8
|
"registryDependencies": [
|
|
9
|
-
"button",
|
|
10
|
-
"input",
|
|
11
|
-
"label",
|
|
12
|
-
"progress"
|
|
9
|
+
"@lexy/button",
|
|
10
|
+
"@lexy/input",
|
|
11
|
+
"@lexy/label",
|
|
12
|
+
"@lexy/progress"
|
|
13
13
|
],
|
|
14
14
|
"files": [
|
|
15
15
|
{
|
|
16
16
|
"path": "views/IntakeDatosPersonales.tsx",
|
|
17
17
|
"type": "registry:file",
|
|
18
18
|
"target": "views/IntakeDatosPersonales.tsx",
|
|
19
|
-
"content": "import { Button } from \"@/components/base/Button\";\nimport { Input } from \"@/components/base/Input\";\nimport { Label } from \"@/components/base/Label\";\nimport { Progress } from \"@/components/base/Progress\";\n\n// Receta cliente — intake / ficha web (paso de wizard): un paso enfocado,\n// columna angosta, calma, CTA al final. Ver intake-wizard.md.\n\nexport function IntakeDatosPersonales() {\n return (\n <main className
|
|
19
|
+
"content": "import { Button } from \"@/components/base/Button\";\nimport { Input } from \"@/components/base/Input\";\nimport { Label } from \"@/components/base/Label\";\nimport { Progress } from \"@/components/base/Progress\";\n\n// Receta cliente — intake / ficha web (paso de wizard): un paso enfocado,\n// columna angosta, calma, CTA al final. Ver intake-wizard.md.\n\nexport function IntakeDatosPersonales() {\n return (\n <main className=\"mx-auto w-full max-w-xl px-4 py-10\">\n <div className=\"mb-8\">\n <p className=\"mb-2 text-sm text-muted-foreground\">Paso 2 de 4</p>\n <Progress value={50} aria-label=\"Avance del formulario\" />\n </div>\n\n <header className=\"mb-6\">\n <h1 className=\"text-2xl font-bold text-foreground\">Cuéntanos tus datos</h1>\n <p className=\"mt-2 text-base text-muted-foreground\">\n Usaremos esta información para preparar la primera revisión de tu caso.\n </p>\n </header>\n\n <form className=\"space-y-5\">\n <fieldset className=\"space-y-5 border-0 p-0\">\n <legend className=\"sr-only\">Datos personales</legend>\n\n <div className=\"space-y-2\">\n <Label htmlFor=\"nombre\">Nombre completo</Label>\n <Input id=\"nombre\" name=\"nombre\" placeholder=\"Escribe tu nombre\" />\n </div>\n\n <div className=\"space-y-2\">\n <Label htmlFor=\"correo\">Correo</Label>\n <Input id=\"correo\" name=\"correo\" type=\"email\" placeholder=\"tu@correo.cl\" />\n <p className=\"text-xs text-muted-foreground\">\n Te escribiremos solo para avanzar con tu caso.\n </p>\n </div>\n </fieldset>\n\n <div className=\"flex items-center justify-between pt-2\">\n <Button type=\"button\" variant=\"ghost\">\n Volver\n </Button>\n <Button type=\"submit\">Guardar y continuar</Button>\n </div>\n </form>\n </main>\n );\n}\n"
|
|
20
20
|
},
|
|
21
21
|
{
|
|
22
22
|
"path": "views/intake-wizard.md",
|
package/assets/r/label.json
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"path": "components/base/Label.tsx",
|
|
15
15
|
"type": "registry:ui",
|
|
16
16
|
"target": "components/base/Label.tsx",
|
|
17
|
-
"content": "import * as
|
|
17
|
+
"content": "import * as LabelPrimitive from \"@radix-ui/react-label\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils/cn\";\n\nconst labelVariants = cva(\n \"text-sm font-medium leading-none text-foreground peer-disabled:cursor-not-allowed peer-disabled:opacity-70\",\n);\n\nconst Label = React.forwardRef<\n React.ElementRef<typeof LabelPrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &\n VariantProps<typeof labelVariants> & {\n required?: boolean;\n }\n>(({ className, required, children, ...props }, ref) => (\n <LabelPrimitive.Root ref={ref} className={cn(labelVariants(), className)} {...props}>\n {children}\n {required && (\n <span aria-hidden=\"true\" className=\"ml-0.5 text-destructive\">\n *\n </span>\n )}\n </LabelPrimitive.Root>\n));\nLabel.displayName = LabelPrimitive.Root.displayName;\n\nexport { Label, labelVariants };\n"
|
|
18
18
|
},
|
|
19
19
|
{
|
|
20
20
|
"path": "components/base/Label.md",
|
package/assets/r/login.json
CHANGED
|
@@ -6,17 +6,17 @@
|
|
|
6
6
|
"description": "Pantalla de acceso: una sola tarea, foco único, ayuda contextual breve. Instálalo con `create-lexy add login` — trae `Card`, `Input`, `Label` y `Button`. La vista queda en tu proyecto (`views/Login.tsx`): edítala con libertad.",
|
|
7
7
|
"dependencies": [],
|
|
8
8
|
"registryDependencies": [
|
|
9
|
-
"button",
|
|
10
|
-
"card",
|
|
11
|
-
"input",
|
|
12
|
-
"label"
|
|
9
|
+
"@lexy/button",
|
|
10
|
+
"@lexy/card",
|
|
11
|
+
"@lexy/input",
|
|
12
|
+
"@lexy/label"
|
|
13
13
|
],
|
|
14
14
|
"files": [
|
|
15
15
|
{
|
|
16
16
|
"path": "views/Login.tsx",
|
|
17
17
|
"type": "registry:file",
|
|
18
18
|
"target": "views/Login.tsx",
|
|
19
|
-
"content": "import { Button } from \"@/components/base/Button\";\nimport { Card,
|
|
19
|
+
"content": "import { Button } from \"@/components/base/Button\";\nimport { Card, CardContent, CardDescription, CardHeader, CardTitle } from \"@/components/base/Card\";\nimport { Input } from \"@/components/base/Input\";\nimport { Label } from \"@/components/base/Label\";\n\n// Receta cliente — acceso (login): una sola tarea, foco único, ayuda\n// contextual breve. Para CRM, ver la variante en login.md. Ver login.md.\n\nexport function Login() {\n return (\n <main className=\"flex min-h-screen items-center justify-center bg-background px-4\">\n <Card className=\"w-full max-w-sm\">\n <CardHeader>\n <CardTitle>Ingresa a Lexy</CardTitle>\n <CardDescription>Te acompañamos con tu caso desde aquí.</CardDescription>\n </CardHeader>\n <CardContent>\n <form className=\"space-y-4\">\n <div className=\"space-y-2\">\n <Label htmlFor=\"email\">Correo</Label>\n <Input id=\"email\" type=\"email\" placeholder=\"tu@correo.cl\" />\n </div>\n <div className=\"space-y-2\">\n <Label htmlFor=\"pass\">Contraseña</Label>\n <Input id=\"pass\" type=\"password\" placeholder=\"Ingresa tu contraseña\" />\n </div>\n <Button type=\"submit\" className=\"w-full\">\n Entrar\n </Button>\n </form>\n </CardContent>\n </Card>\n </main>\n );\n}\n"
|
|
20
20
|
},
|
|
21
21
|
{
|
|
22
22
|
"path": "views/login.md",
|