create-lexy 0.6.0 → 0.6.1
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 +1 -1
- package/assets/r/app-accordion.json +1 -1
- package/assets/r/app-dialog.json +1 -1
- package/assets/r/app-header-bar.json +1 -1
- package/assets/r/app-sidebar.json +1 -1
- package/assets/r/avatar.json +1 -1
- package/assets/r/badge.json +1 -1
- package/assets/r/brand-background.json +135 -0
- package/assets/r/breadcrumb.json +1 -1
- package/assets/r/button-group.json +1 -1
- package/assets/r/button.json +1 -1
- package/assets/r/calendar.json +1 -1
- 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 +1 -1
- 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 +2 -2
- package/assets/r/crm-desk.json +2 -2
- package/assets/r/crm-detalle-caso.json +2 -2
- package/assets/r/date-picker.json +1 -1
- 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 +1 -1
- package/assets/r/header-bar.json +1 -1
- package/assets/r/input.json +1 -1
- package/assets/r/intake-wizard.json +2 -2
- package/assets/r/label.json +1 -1
- package/assets/r/login.json +2 -2
- 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 +1 -1
- package/assets/r/popover.json +1 -1
- package/assets/r/profile-card.json +1 -1
- package/assets/r/progress.json +1 -1
- package/assets/r/radio-group.json +1 -1
- package/assets/r/registry.json +116 -0
- 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 +2 -2
- 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/assets/registry-version +1 -1
- package/assets/theme/lexy-theme.css +15 -4
- package/dist/index.js +1663 -1735
- package/package.json +6 -5
package/assets/r/breadcrumb.json
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"path": "components/base/Breadcrumb.md",
|
|
20
20
|
"type": "registry:file",
|
|
21
21
|
"target": "components/base/Breadcrumb.md",
|
|
22
|
-
"content": "# Breadcrumb — guía de uso\n\nComponente Breadcrumb. El componente vive en tu proyecto: instálalo con `create-lexy add breadcrumb` y edítalo con libertad.\n\n##
|
|
22
|
+
"content": "# Breadcrumb — guía de uso\n\nComponente Breadcrumb. El componente vive en tu proyecto: instálalo con `create-lexy add breadcrumb` y edítalo con libertad.\n\n## Cuándo usarlo\n\nPara ubicar a la persona en jerarquías de más de dos niveles y darle retorno: Casos / Caso 482 / Documentos. Si la app es plana (una sidebar y ya), el breadcrumb es ruido.\n\n## Composición\n\n`Breadcrumb` > `BreadcrumbList` > `BreadcrumbItem`s: `BreadcrumbLink` para los antecesores, `BreadcrumbPage` para el nivel actual, con `BreadcrumbSeparator` entre medio y `BreadcrumbEllipsis` para rutas largas.\n\n## Uso básico\n\n```tsx\n<Breadcrumb />\n```\n\n## Reglas\n\n- Usa Breadcrumb según el propósito descrito.\n- No abuses de este componente en contexts donde no aplica.\n\n## Import\n\n```tsx\nimport { Breadcrumb } from \"@/components/base/Breadcrumb\";\n```\n\n## Props\n\nConsulta la story en Storybook para ver las props disponibles.\n\n## Para IA\n\n1. Identifica el contexto de uso del componente.\n2. Importa Breadcrumb 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"
|
|
23
23
|
}
|
|
24
24
|
]
|
|
25
25
|
}
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"path": "components/base/ButtonGroup.md",
|
|
23
23
|
"type": "registry:file",
|
|
24
24
|
"target": "components/base/ButtonGroup.md",
|
|
25
|
-
"content": "# ButtonGroup — guía de uso\n\nAgrupa botones relacionados en una sola unidad visual. Sirve para acciones hermanas, comandos de toolbar o split actions. No mantiene estado de selección por sí mismo. El componente vive en tu proyecto: instálalo con `create-lexy add button-group` y edítalo con libertad.\n\n##
|
|
25
|
+
"content": "# ButtonGroup — guía de uso\n\nAgrupa botones relacionados en una sola unidad visual. Sirve para acciones hermanas, comandos de toolbar o split actions. No mantiene estado de selección por sí mismo. El componente vive en tu proyecto: instálalo con `create-lexy add button-group` y edítalo con libertad.\n\n## Cuándo usarlo\n\nPara acciones hermanas que operan sobre lo mismo: comandos de toolbar, split actions, pares como “Aprobar / Rechazar”. Una sola unidad visual en vez de botones sueltos. No guarda selección: para elegir opciones usa `RadioGroup` o `Tabs`.\n\n## Composición\n\n`ButtonGroup` agrupa `Button`s; `ButtonGroupSeparator` divide segmentos y `ButtonGroupText` intercala texto auxiliar. Orientación horizontal o vertical.\n\n## Uso básico\n\n```tsx\n<ButtonGroup aria-label=\"Acciones del documento\">\n <Button variant=\"outline\">Editar</Button>\n <Button variant=\"outline\">Duplicar</Button>\n <Button variant=\"outline\">Archivar</Button>\n</ButtonGroup>\n```\n\n## Orientación vertical\n\n```tsx\n<ButtonGroup orientation=\"vertical\" aria-label=\"Acciones rápidas\">\n <Button variant=\"outline\">Asignar</Button>\n <Button variant=\"outline\">Comentar</Button>\n <Button variant=\"outline\">Cerrar</Button>\n</ButtonGroup>\n```\n\n## Separadores\n\nUsa `ButtonGroupSeparator` cuando el grupo mezcla acciones con pesos distintos o cuando las variantes sin borde necesitan separación explícita.\n\n```tsx\n<ButtonGroup aria-label=\"Exportar\">\n <Button variant=\"secondary\">Exportar PDF</Button>\n <ButtonGroupSeparator />\n <Button variant=\"secondary\" size=\"icon\" aria-label=\"Más formatos\">\n <ChevronDown />\n </Button>\n</ButtonGroup>\n```\n\n## Texto auxiliar\n\n`ButtonGroupText` permite agregar una etiqueta breve alineada con los controles.\n\n```tsx\n<ButtonGroup aria-label=\"Filtros de estado\">\n <ButtonGroupText>Estado</ButtonGroupText>\n <Button variant=\"outline\">Activo</Button>\n <Button variant=\"outline\">Pendiente</Button>\n</ButtonGroup>\n```\n\n## Reglas\n\n- Usa `ButtonGroup` solo para acciones relacionadas por contexto inmediato.\n- Agrega `aria-label` o `aria-labelledby` si el grupo no tiene un título visible.\n- Mantén los botones con variantes compatibles; `outline` funciona bien para grupos conectados.\n- En botones solo-icono, cada `Button` debe tener su propio `aria-label`.\n- No uses el grupo para mostrar selección persistente: visualmente son acciones, no opciones activas.\n\n## Cuándo NO usar\n\n- **Elegir una opción activa** → `Tabs`, `RadioGroup` o `Select`.\n- **Activar/desactivar una preferencia** → `Switch` o `Checkbox`.\n- **Barra de navegación** → navegación real con `Link`/`a`, no botones agrupados.\n- **Acciones sin relación fuerte** → botones separados con espaciado normal.\n\n## Import\n\n```tsx\nimport {\n Button,\n ButtonGroup,\n ButtonGroupSeparator,\n ButtonGroupText,\n} from \"@/components/base/ButtonGroup\";\n```\n\n## Props\n\n| Prop | Tipo | Default | Descripción |\n|---|---|---|---|\n| `orientation` | `\"horizontal\" \\| \"vertical\"` | `\"horizontal\"` | Dirección del grupo y forma de conectar bordes/radios. |\n| `aria-label` / `aria-labelledby` | `string` | — | Etiqueta accesible del grupo cuando no hay título visible. |\n| ...resto | `HTMLAttributes<HTMLDivElement>` | — | `className`, `id`, `data-*`, etc. |\n\n## Para IA\n\n1. Primero decide si son acciones hermanas o si realmente son opciones seleccionables.\n2. El componente vive en tu proyecto: impórtalo local (`@/components/base/ButtonGroup`) y edítalo con libertad si necesitas cambiar estructura, lógica o defaults.\n3. Usa `ButtonGroup` con `aria-label` claro.\n4. Usa `Button` dentro del grupo; no reconstruyas botones con `div` o clases manuales.\n5. Si hay icon-only buttons, agrega `aria-label` individual.\n6. Para selección persistente, cambia a `Tabs`/`RadioGroup` en vez de forzar `ButtonGroup`.\n"
|
|
26
26
|
}
|
|
27
27
|
]
|
|
28
28
|
}
|
package/assets/r/button.json
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"path": "components/base/Button.md",
|
|
21
21
|
"type": "registry:file",
|
|
22
22
|
"target": "components/base/Button.md",
|
|
23
|
-
"content": "# Button — guía de uso\n\nBotón de acción. Renderiza un `<button>` nativo; acepta todos sus atributos (`onClick`, `type`, `disabled`, `aria-*`). El componente vive en tu proyecto: instálalo con `create-lexy add button` y edítalo con libertad.\n\n##
|
|
23
|
+
"content": "# Button — guía de uso\n\nBotón de acción. Renderiza un `<button>` nativo; acepta todos sus atributos (`onClick`, `type`, `disabled`, `aria-*`). El componente vive en tu proyecto: instálalo con `create-lexy add button` y edítalo con libertad.\n\n## Cuándo usarlo\n\nPara ejecutar una acción: guardar, enviar, confirmar. La variante dice la jerarquía — una sola acción primaria por vista, el resto baja a `secondary`, `outline` o `ghost`. Si el click lleva a otra página, es un enlace con apariencia de botón (`asChild`), no un `<button>`.\n\n## Composición\n\nUna sola pieza (`Button`) con variantes de intención y tamaños; `size=\"icon\"` para el botón cuadrado solo-icono (siempre con `aria-label`). Con `asChild` viste de botón a un enlace real; `buttonVariants` exporta las clases para casos sin envoltura.\n\n## Uso básico\n\n```tsx\n<Button onClick={save}>Guardar cambios</Button>\n```\n\n## Variantes\n\n```tsx\n<Button variant=\"default\">Guardar</Button> {/* acción primaria */}\n<Button variant=\"secondary\">Cancelar</Button> {/* secundaria */}\n<Button variant=\"outline\">Filtrar</Button> {/* secundaria, menos peso */}\n<Button variant=\"ghost\">Ver más</Button> {/* terciaria, sin fondo */}\n<Button variant=\"link\">Olvidé mi contraseña</Button> {/* navegación inline */}\n<Button variant=\"destructive\">Eliminar</Button> {/* borrar o revertir */}\n```\n\n## Tamaños\n\n```tsx\n<Button size=\"sm\">Compacto</Button>\n<Button size=\"default\">Normal</Button>\n<Button size=\"lg\">Grande</Button>\n<Button size=\"icon\" aria-label=\"Buscar\"><Search /></Button> {/* solo icono */}\n```\n\n## asChild — navegación con apariencia de botón\n\n```tsx\n<Button asChild variant=\"outline\">\n <Link to=\"/cuenta\">Ir a mi cuenta</Link>\n</Button>\n```\n\nCon `asChild` el hijo se vuelve el elemento raíz (patrón Slot de Radix): el botón aporta estilos y props, el hijo aporta la semántica (`<a>`, `Link`). Es la vía preferida para enlaces con apariencia de botón; `buttonVariants` queda para casos donde no puedas envolver.\n\n## Reglas\n\n- **Una sola** acción primaria (`default`) por vista o sección: si todo es primario, nada lo es.\n- Usa `destructive` solo para acciones que borran o revierten; combínalo con confirmación (`AppDialog`).\n- `size=\"icon\"` **requiere** `aria-label`: sin texto visible, es la única etiqueta accesible.\n- `variant=\"link\"` es para navegación inline dentro de texto, no para la acción principal de un formulario.\n- No juntes muchas acciones de alto peso; baja a `ghost`/`link` las de menor prioridad.\n\n## Cuándo NO usar\n\n- **Navegación entre páginas/rutas** → usa `asChild` con un `<a>`/`Link` real. Un `<button>` no es un enlace.\n- **Activar/desactivar un estado** → `Switch` (ajustes) o `Checkbox` (selección en formulario).\n- **Elegir entre opciones** → `RadioGroup`, `Select` o `Tabs`, no varios botones.\n\n## Import\n\n```tsx\nimport { Button } from \"@/components/base/Button\";\n```\n\n## Props\n\n| Prop | Tipo | Default | Descripción |\n|---|---|---|---|\n| `variant` | `\"default\" \\| \"destructive\" \\| \"outline\" \\| \"secondary\" \\| \"ghost\" \\| \"link\"` | `\"default\"` | Jerarquía/intención visual. |\n| `size` | `\"default\" \\| \"sm\" \\| \"lg\" \\| \"icon\"` | `\"default\"` | Tamaño. `icon` para botón cuadrado solo-icono. |\n| `asChild` | `boolean` | `false` | Renderiza el hijo como elemento raíz (Slot). |\n| `disabled` | `boolean` | `false` | Deshabilita e ignora clics. |\n| ...resto | `ButtonHTMLAttributes` | — | `onClick`, `type`, `form`, `aria-*`, etc. |\n\n`buttonVariants` se exporta: úsalo para dar estilo de botón a un `<a>` real cuando no puedas usar `asChild` — `<a className={buttonVariants({ variant: \"outline\" })}>`.\n\n## Para IA\n\n1. Define la jerarquía: ¿esta acción es la principal de la vista, una alternativa o algo terciario?\n2. Asigna la variante por intención, no por color: primaria=`default`, secundaria=`secondary`/`outline`, terciaria=`ghost`, navegación inline=`link`, irreversible=`destructive`.\n3. Mantén una sola acción primaria visible por sección.\n4. Si es solo-icono, agrega `aria-label` con el verbo de la acción.\n5. Si la acción navega a otra ruta, usa `asChild` con un enlace real en vez de un `<button>`.\n6. Para acciones destructivas, envuélvelas en `AppDialog` con la consecuencia explícita.\n"
|
|
24
24
|
}
|
|
25
25
|
]
|
|
26
26
|
}
|
package/assets/r/calendar.json
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"path": "components/base/Calendar.md",
|
|
23
23
|
"type": "registry:file",
|
|
24
24
|
"target": "components/base/Calendar.md",
|
|
25
|
-
"content": "# Calendar — guía de uso\n\nSelector de fechas construido sobre [React DayPicker](https://daypicker.dev),\ncon los estilos y el `Button` de Lexy. Soporta selección simple, múltiple y de\nrango, navegación por mes/año y localización. El componente vive en tu proyecto: instálalo con `create-lexy add calendar` y edítalo con libertad.\n\n##
|
|
25
|
+
"content": "# Calendar — guía de uso\n\nSelector de fechas construido sobre [React DayPicker](https://daypicker.dev),\ncon los estilos y el `Button` de Lexy. Soporta selección simple, múltiple y de\nrango, navegación por mes/año y localización. El componente vive en tu proyecto: instálalo con `create-lexy add calendar` y edítalo con libertad.\n\n## Cuándo usarlo\n\nPara elegir fechas viendo el contexto del mes: agendar, rangos de reserva, fechas próximas. Si el campo vive en un formulario, usa `DatePicker` (que ya lo envuelve en un `Popover`); usa `Calendar` directo cuando el calendario es protagonista de la vista.\n\n## Composición\n\nConstruido sobre React DayPicker: `Calendar` renderiza la grilla del mes con su navegación, y `CalendarDayButton` es el botón de día personalizable. Los modos `single`, `range` y `multiple` cambian qué se selecciona.\n\n## Uso básico (fecha única)\n\n```tsx\nconst [date, setDate] = React.useState<Date | undefined>(new Date());\n\n<Calendar\n mode=\"single\"\n selected={date}\n onSelect={setDate}\n locale={es}\n className=\"rounded-lg border border-border\"\n/>;\n```\n\n## Rango\n\n```tsx\nconst [range, setRange] = React.useState<DateRange | undefined>();\n\n<Calendar\n mode=\"range\"\n selected={range}\n onSelect={setRange}\n numberOfMonths={2}\n locale={es}\n className=\"rounded-lg border border-border\"\n/>;\n```\n\n## Selección múltiple\n\n```tsx\n<Calendar mode=\"multiple\" selected={dates} onSelect={setDates} locale={es} />\n```\n\n## Navegación con dropdowns\n\n`captionLayout=\"dropdown\"` reemplaza el título por selectores de mes y año.\n\n```tsx\n<Calendar\n mode=\"single\"\n captionLayout=\"dropdown\"\n locale={es}\n className=\"rounded-lg border border-border\"\n/>\n```\n\n## Reglas\n\n- Es un componente controlado: maneja `selected`/`onSelect` con estado propio.\n- Pasa `locale` (p. ej. `es`) para meses y días en español.\n- Para elegir una fecha dentro de un formulario, combínalo con `Popover` + un `Button` que muestre la fecha (patrón date-picker).\n- No reconstruyas la grilla a mano; toda la estructura sale de React DayPicker.\n\n## Cuándo NO usar\n\n- **Una hora/duración** sin fecha → un `Input type=\"time\"` o un selector dedicado.\n- **Rango de fechas en texto** con muchos presets → considera un date-picker con presets sobre este Calendar.\n\n## Import\n\n```tsx\nimport { Calendar } from \"@/components/base/Calendar\";\nimport { es } from \"react-day-picker/locale\";\n```\n\n## Props (las más usadas)\n\n| Prop | Tipo | Descripción |\n|---|---|---|\n| `mode` | `\"single\" \\| \"multiple\" \\| \"range\"` | Tipo de selección. |\n| `selected` / `onSelect` | según `mode` | Valor controlado y callback. |\n| `captionLayout` | `\"label\" \\| \"dropdown\" \\| \"dropdown-months\" \\| \"dropdown-years\"` | Encabezado como texto o como selectores. |\n| `numberOfMonths` | `number` | Cantidad de meses visibles. |\n| `locale` | `Locale` | Localización (p. ej. `es` de `react-day-picker/locale`). |\n| `buttonVariant` | variante de `Button` | Variante de los botones de navegación. |\n| `disabled` | `Matcher \\| Matcher[]` | Días deshabilitados. |\n| ...resto | `DayPickerProps` | Todas las props de React DayPicker. |\n\n## Para IA\n\n1. Decide el `mode` (`single`, `multiple` o `range`) según el caso.\n2. Importa `Calendar` local (`@/components/base/Calendar`) y el locale (`es`) desde `react-day-picker/locale`.\n3. Mantén el estado de `selected` y pásalo con su `onSelect`.\n4. Para un date-picker de formulario, envuélvelo en `Popover` con un `Button` disparador.\n5. Usa `captionLayout=\"dropdown\"` cuando el usuario deba saltar muchos meses/años.\n"
|
|
26
26
|
}
|
|
27
27
|
]
|
|
28
28
|
}
|
package/assets/r/card.json
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"path": "components/base/Card.md",
|
|
20
20
|
"type": "registry:file",
|
|
21
21
|
"target": "components/base/Card.md",
|
|
22
|
-
"content": "# Card — guía de uso\n\nContenedor de contenido. Para agrupar información relacionada. El componente vive en tu proyecto: instálalo con `create-lexy add card` y edítalo con libertad.\n\n##
|
|
22
|
+
"content": "# Card — guía de uso\n\nContenedor de contenido. Para agrupar información relacionada. El componente vive en tu proyecto: instálalo con `create-lexy add card` y edítalo con libertad.\n\n## Cuándo usarlo\n\nPara agrupar información relacionada en una unidad con jerarquía propia: un resumen, una entidad, un bloque de ajustes. Si la vista entera es un listado denso y comparable, una tabla escanea mejor que una grilla de cards.\n\n## Composición\n\nComposición por slots: `CardHeader` (con `CardTitle`, `CardDescription` y `CardAction` opcional), `CardContent` para el cuerpo y `CardFooter` para acciones o metadatos. Usa solo los slots que necesitas — la caja mantiene su ritmo interno.\n\n## Uso básico\n\n```tsx\n<Card />\n```\n\n## Reglas\n\n- Usa Card según el propósito descrito.\n- No abuses de este componente en contexts donde no aplica.\n\n## Import\n\n```tsx\nimport { Card } from \"@/components/base/Card\";\n```\n\n## Props\n\nConsulta la story en Storybook para ver las props disponibles.\n\n## Para IA\n\n1. Identifica el contexto de uso del componente.\n2. Importa Card 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"
|
|
23
23
|
}
|
|
24
24
|
]
|
|
25
25
|
}
|
package/assets/r/chart.json
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"path": "components/base/Chart.md",
|
|
20
20
|
"type": "registry:file",
|
|
21
21
|
"target": "components/base/Chart.md",
|
|
22
|
-
"content": "# Chart — guía de uso\n\nGráficos construidos sobre [Recharts](https://recharts.org). No es un componente\nde gráfico cerrado: son primitivos (`ChartContainer`, tooltip y leyenda con\nestilos de Lexy) que envuelven los charts de Recharts y los conectan a los\ntokens del tema vía un `ChartConfig`. El componente vive en tu proyecto: instálalo con `create-lexy add chart` y edítalo con libertad.\n\n##
|
|
22
|
+
"content": "# Chart — guía de uso\n\nGráficos construidos sobre [Recharts](https://recharts.org). No es un componente\nde gráfico cerrado: son primitivos (`ChartContainer`, tooltip y leyenda con\nestilos de Lexy) que envuelven los charts de Recharts y los conectan a los\ntokens del tema vía un `ChartConfig`. El componente vive en tu proyecto: instálalo con `create-lexy add chart` y edítalo con libertad.\n\n## Cuándo usarlo\n\nPara mostrar tendencia, comparación o distribución — lo que un número solo no cuenta. Elige el tipo por la pregunta que responde: línea para evolución, barras para comparar, área para volumen. Si el dato importante es un valor puntual, un stat con texto grande comunica más rápido que un gráfico.\n\n## Composición\n\nNo es un componente único sino un kit sobre Recharts: `ChartContainer` fija tema y proporciones desde un `config`; `ChartTooltip`/`ChartTooltipContent` y `ChartLegend`/`ChartLegendContent` visten tooltip y leyenda con los tokens del sistema. Las series toman color de la paleta `chart-1…5`.\n\n## Config\n\nEl `ChartConfig` mapea cada serie a su etiqueta y color. El color se expone como\nvariable CSS `--color-<clave>`, que luego usas en el `fill`/`stroke` del chart.\n\n```tsx\nconst chartConfig = {\n desktop: { label: \"Escritorio\", color: \"var(--color-chart-1)\" },\n mobile: { label: \"Móvil\", color: \"var(--color-chart-2)\" },\n} satisfies ChartConfig;\n```\n\n## Uso básico\n\n```tsx\n<ChartContainer config={chartConfig} className=\"min-h-[200px] w-full\">\n <BarChart accessibilityLayer data={chartData}>\n <CartesianGrid vertical={false} />\n <XAxis\n dataKey=\"month\"\n tickLine={false}\n axisLine={false}\n tickMargin={10}\n tickFormatter={(value) => value.slice(0, 3)}\n />\n <ChartTooltip content={<ChartTooltipContent />} />\n <ChartLegend content={<ChartLegendContent />} />\n <Bar dataKey=\"desktop\" fill=\"var(--color-desktop)\" radius={4} />\n <Bar dataKey=\"mobile\" fill=\"var(--color-mobile)\" radius={4} />\n </BarChart>\n</ChartContainer>\n```\n\n## Colores\n\nUsa los tokens `--color-chart-1` … `--color-chart-5` del tema para series\ncategóricas. Están anclados al índigo de marca y pensados para distinguirse\nentre sí. Para un color puntual puedes pasar cualquier valor CSS en `color`.\n\n## Componentes\n\n| Componente | Rol |\n|---|---|\n| `ChartContainer` | Provee el contexto de config, dimensiona (responsive) y aplica los estilos base sobre los nodos de Recharts. |\n| `ChartTooltip` | Alias de `Tooltip` de Recharts. |\n| `ChartTooltipContent` | Contenido del tooltip con estilos Lexy (`indicator: dot \\| line \\| dashed`). |\n| `ChartLegend` | Alias de `Legend` de Recharts. |\n| `ChartLegendContent` | Contenido de la leyenda con estilos Lexy. |\n\n## Reglas\n\n- Siempre envuelve el chart de Recharts en `ChartContainer` y pásale un `config`.\n- Da al contenedor un **ancho definido** (p. ej. dentro de una `Card` o con `w-full` en un padre con ancho); el `ResponsiveContainer` necesita medir.\n- Referencia los colores por `var(--color-<clave>)`, no hardcodees hex en el chart.\n- Mantén las claves del `config` iguales a los `dataKey` de las series.\n\n## Cuándo NO usar\n\n- **Una sola métrica destacada** → un número grande / `StatusDot`, no un chart.\n- **Tabla de datos exactos** → `Table`.\n- **Progreso de una tarea** → `Progress`.\n\n## Import\n\n```tsx\nimport {\n ChartContainer,\n ChartTooltip,\n ChartTooltipContent,\n ChartLegend,\n ChartLegendContent,\n type ChartConfig,\n} from \"@/components/base/Chart\";\nimport { Bar, BarChart, CartesianGrid, XAxis } from \"recharts\";\n```\n\n## Para IA\n\n1. Define primero el `ChartConfig` (una entrada por serie, con `label` y `color`).\n2. Importa los primitivos de chart local (`@/components/base/Chart`) y los del gráfico (`Bar`, `BarChart`, `XAxis`, …) desde `recharts`.\n3. Envuelve todo en `ChartContainer` con ancho definido (normalmente dentro de una `Card`).\n4. Usa `var(--color-<clave>)` para `fill`/`stroke`; los colores salen del `config`.\n5. Agrega `ChartTooltip`/`ChartLegend` con sus `*Content` para tooltips y leyenda con estilo Lexy.\n"
|
|
23
23
|
}
|
|
24
24
|
]
|
|
25
25
|
}
|
package/assets/r/checkbox.json
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"path": "components/base/Checkbox.md",
|
|
20
20
|
"type": "registry:file",
|
|
21
21
|
"target": "components/base/Checkbox.md",
|
|
22
|
-
"content": "# Checkbox — guía de uso\n\nCasilla de verificación (Radix). Para un sí/no independiente o para elegir **varias** opciones de una lista. No trae label: emparéjalo con `Label`. El componente vive en tu proyecto: instálalo con `create-lexy add checkbox` y edítalo con libertad.\n\n##
|
|
22
|
+
"content": "# Checkbox — guía de uso\n\nCasilla de verificación (Radix). Para un sí/no independiente o para elegir **varias** opciones de una lista. No trae label: emparéjalo con `Label`. El componente vive en tu proyecto: instálalo con `create-lexy add checkbox` y edítalo con libertad.\n\n## Cuándo usarlo\n\nPara un sí/no independiente (acepto los términos) o para elegir varias opciones de una lista visible. Cada checkbox es autónomo: marcar uno no afecta a los demás. Si la elección es excluyente, es un `RadioGroup`; si es un ajuste de efecto inmediato, un `Switch`.\n\n## Composición\n\nUna sola pieza (`Checkbox`, Radix) sin label propio: emparéjala siempre con `Label` apuntando a su `id`. En listas, un checkbox por fila con su label clickeable.\n\n## Uso básico\n\n```tsx\n<div className=\"flex items-center gap-2\">\n <Checkbox id=\"terminos\" />\n <Label htmlFor=\"terminos\">Acepto los términos</Label>\n</div>\n```\n\n## Selección múltiple\n\n```tsx\n<fieldset className=\"grid gap-2\">\n <legend className=\"text-sm font-medium\">Notificarme sobre</legend>\n {opciones.map((o) => (\n <div key={o.id} className=\"flex items-center gap-2\">\n <Checkbox\n id={o.id}\n checked={seleccion.includes(o.id)}\n onCheckedChange={(c) => toggle(o.id, c)}\n />\n <Label htmlFor={o.id}>{o.label}</Label>\n </div>\n ))}\n</fieldset>\n```\n\n## Reglas\n\n- Empareja con `Label` por `htmlFor`/`id`; al pulsar el label debe activarse la casilla.\n- Para **grupos**, envuelve en `fieldset` + `legend` que describa el grupo.\n- Usa `\"indeterminate\"` para un \"seleccionar todo\" parcialmente marcado.\n- No uses checkbox para activar algo inmediato a nivel de sistema → eso es `Switch`.\n\n## Cuándo NO usar\n\n- **Encender/apagar un ajuste con efecto inmediato** → `Switch`.\n- **Elegir una sola opción entre varias** → `RadioGroup`.\n- **Más de ~7 opciones o con búsqueda** → `Select`/`Combobox` con multiselección.\n\n## Import\n\n```tsx\nimport { Checkbox } from \"@/components/base/Checkbox\";\n```\n\n## Props\n\n| Prop | Tipo | Default | Descripción |\n|---|---|---|---|\n| `checked` | `boolean \\| \"indeterminate\"` | — | Estado controlado. |\n| `defaultChecked` | `boolean` | — | Estado inicial no controlado. |\n| `onCheckedChange` | `(checked) => void` | — | Cambio de estado. |\n| `disabled` | `boolean` | `false` | Deshabilita. |\n\n## Para IA\n\n1. ¿Es selección múltiple o un consentimiento aislado? Si es \"una entre varias\", usa `RadioGroup`; si es ajuste de sistema, `Switch`.\n2. Crea el par `Checkbox` + `Label` con `id`/`htmlFor`.\n3. Agrupa opciones relacionadas en `fieldset`/`legend`.\n4. Controla con `checked` + `onCheckedChange` cuando dependa del estado.\n"
|
|
23
23
|
}
|
|
24
24
|
]
|
|
25
25
|
}
|
package/assets/r/combobox.json
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"path": "components/base/Combobox.md",
|
|
22
22
|
"type": "registry:file",
|
|
23
23
|
"target": "components/base/Combobox.md",
|
|
24
|
-
"content": "# Combobox — guía de uso\n\nCampo 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.\n\n##
|
|
24
|
+
"content": "# Combobox — guía de uso\n\nCampo 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.\n\n## Cuándo usarlo\n\nPara buscar y elegir en listas largas: clientes, comunas, causas. El filtrado mientras se escribe convierte doscientas opciones en tres. Con pocas opciones, un `Select` simple pide menos esfuerzo.\n\n## Composición\n\nReceta empaquetada: un `Button` de campo que abre un `Popover` con un `Command` dentro (input de búsqueda + lista filtrable).\n\n## Uso básico\n\n```tsx\n<Combobox />\n```\n\n## Receta compuesta\n\n`Combobox` es un wrapper delgado sobre `Popover` + `Command`. Sus props extra (`disabled`, `id`, `aria-*`…) pasan al botón trigger y el `ref` apunta a ese botón. Si necesitas variar la estructura (grupos, ítems con icono o descripción, multi-selección, crear opciones al vuelo), compón las piezas directamente:\n\n```tsx\nimport { Button } from \"@/components/base/Button\";\nimport { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from \"@/components/base/Command\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"@/components/base/Popover\";\n\n<Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <Button variant=\"outline\" role=\"combobox\" aria-expanded={open}>…</Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-[var(--radix-popover-trigger-width)] p-0\" align=\"start\">\n <Command>\n <CommandInput placeholder=\"Buscar…\" />\n <CommandList>\n <CommandEmpty>Sin resultados</CommandEmpty>\n <CommandGroup heading=\"Sugeridos\">{/* CommandItem con iconos, etc. */}</CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n</Popover>\n```\n\n## Reglas\n\n- Usa Combobox según el propósito descrito.\n- No abuses de este componente en contexts donde no aplica.\n\n## Import\n\n```tsx\nimport { Combobox } from \"@/components/base/Combobox\";\n```\n\n## Props\n\nConsulta la story en Storybook para ver las props disponibles.\n\n## Para IA\n\n1. Identifica el contexto de uso del componente.\n2. Importa Combobox 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"
|
|
25
25
|
}
|
|
26
26
|
]
|
|
27
27
|
}
|
package/assets/r/command.json
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"path": "components/base/Command.md",
|
|
20
20
|
"type": "registry:file",
|
|
21
21
|
"target": "components/base/Command.md",
|
|
22
|
-
"content": "# Command — guía de uso\n\nPaleta de comandos. Para búsqueda y acciones rápidas. El componente vive en tu proyecto: instálalo con `create-lexy add command` y edítalo con libertad.\n\n##
|
|
22
|
+
"content": "# Command — guía de uso\n\nPaleta de comandos. Para búsqueda y acciones rápidas. El componente vive en tu proyecto: instálalo con `create-lexy add command` y edítalo con libertad.\n\n## Cuándo usarlo\n\nPara búsqueda y comandos rápidos tipo paleta (⌘K): saltar a un caso, ejecutar una acción, navegar sin mouse. Para usuarios frecuentes es el atajo que más se agradece.\n\n## Composición\n\n`Command` contiene `CommandInput`, `CommandList` con `CommandGroup`s de `CommandItem`s, `CommandEmpty` para el sin-resultados, más `CommandSeparator` y `CommandShortcut`. Dentro de un `Dialog` se vuelve paleta flotante.\n\n## Uso básico\n\n```tsx\n<Command />\n```\n\n## Reglas\n\n- Usa Command según el propósito descrito.\n- No abuses de este componente en contexts donde no aplica.\n\n## Import\n\n```tsx\nimport { Command } from \"@/components/base/Command\";\n```\n\n## Props\n\nConsulta la story en Storybook para ver las props disponibles.\n\n## Para IA\n\n1. Identifica el contexto de uso del componente.\n2. Importa Command 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"
|
|
23
23
|
}
|
|
24
24
|
]
|
|
25
25
|
}
|
|
@@ -15,13 +15,13 @@
|
|
|
15
15
|
"path": "views/EnvioConfirmado.tsx",
|
|
16
16
|
"type": "registry:file",
|
|
17
17
|
"target": "views/EnvioConfirmado.tsx",
|
|
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-
|
|
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-margin-mobile 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 w-full sm:w-auto\">Volver al inicio</Button>\n </main>\n );\n}\n"
|
|
19
19
|
},
|
|
20
20
|
{
|
|
21
21
|
"path": "views/confirmacion.md",
|
|
22
22
|
"type": "registry:file",
|
|
23
23
|
"target": "views/confirmacion.md",
|
|
24
|
-
"content": "# confirmacion — block\n\nPantalla de confirmación / éxito para **mundo cliente**: celebra breve, dice qué pasa ahora y dónde seguir. Instálalo con `create-lexy add confirmacion` — trae `Button` (el icono es de lucide). La vista queda en tu proyecto (`views/EnvioConfirmado.tsx`): edítala con libertad.\n\n## Adaptación\n\n- Título en pasado y concreto (*\"¡Enviamos tus documentos!\"*); el párrafo dice qué pasa ahora y dónde seguir el estado.\n- El CTA lleva a la siguiente acción natural (inicio, estado del trámite), no a un dead-end.\n- Para éxito *dentro* de una vista (guardado puntual) usa `Toaster`, no esta pantalla.\n\n## Evita\n\nConfeti/celebración larga, varios CTAs compitiendo, y texto legal en este momento del flujo.\n"
|
|
24
|
+
"content": "# confirmacion — block\n\nPantalla de confirmación / éxito para **mundo cliente**: celebra breve, dice qué pasa ahora y dónde seguir. Instálalo con `create-lexy add confirmacion` — trae `Button` (el icono es de lucide). La vista queda en tu proyecto (`views/EnvioConfirmado.tsx`): edítala con libertad.\n\n## Adaptación\n\n- Título en pasado y concreto (*\"¡Enviamos tus documentos!\"*); el párrafo dice qué pasa ahora y dónde seguir el estado.\n- El CTA lleva a la siguiente acción natural (inicio, estado del trámite), no a un dead-end.\n- Para éxito *dentro* de una vista (guardado puntual) usa `Toaster`, no esta pantalla.\n\n## Evita\n\nConfeti/celebración larga, varios CTAs compitiendo, y texto legal en este momento del flujo.\n\n## Degradación móvil\n\nColumna angosta centrada; bajo `sm` el CTA es full-width (target táctil cómodo con una mano). No requiere más adaptación.\n"
|
|
25
25
|
}
|
|
26
26
|
]
|
|
27
27
|
}
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"path": "components/base/CounterBadge.md",
|
|
20
20
|
"type": "registry:file",
|
|
21
21
|
"target": "components/base/CounterBadge.md",
|
|
22
|
-
"content": "# CounterBadge — guía de uso\n\nBadge con contador numérico. Para notificaciones. El componente vive en tu proyecto: instálalo con `create-lexy add counter-badge` y edítalo con libertad.\n\n##
|
|
22
|
+
"content": "# CounterBadge — guía de uso\n\nBadge con contador numérico. Para notificaciones. El componente vive en tu proyecto: instálalo con `create-lexy add counter-badge` y edítalo con libertad.\n\n## Cuándo usarlo\n\nPara conteos que piden atención: mensajes sin leer, tareas pendientes. Vive junto al ítem que cuantifica (icono de campana, item de menú). Si el número no cambia ni pide acción, probablemente es un dato de tabla, no un contador.\n\n## Composición\n\nUna sola pieza (`CounterBadge`) que formatea el número (99+) y se ancla al elemento que acompaña.\n\n## Uso básico\n\n```tsx\n<CounterBadge />\n```\n\n## Reglas\n\n- Usa CounterBadge según el propósito descrito.\n- No abuses de este componente en contexts donde no aplica.\n\n## Import\n\n```tsx\nimport { CounterBadge } from \"@/components/base/CounterBadge\";\n```\n\n## Props\n\nConsulta la story en Storybook para ver las props disponibles.\n\n## Para IA\n\n1. Identifica el contexto de uso del componente.\n2. Importa CounterBadge 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"
|
|
23
23
|
}
|
|
24
24
|
]
|
|
25
25
|
}
|
|
@@ -17,13 +17,13 @@
|
|
|
17
17
|
"path": "views/AppLayout.tsx",
|
|
18
18
|
"type": "registry:file",
|
|
19
19
|
"target": "views/AppLayout.tsx",
|
|
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
|
|
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, SidebarTrigger } 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. En móvil el sidebar vive en un Sheet (lo maneja el\n// componente) y un header solo-móvil expone el trigger. 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>\n {/* Header solo-móvil: bajo md el sidebar es un Sheet y necesita un\n trigger visible (pauta §5: no escondas acciones frecuentes). */}\n <header className=\"flex h-12 shrink-0 items-center gap-2 border-b border-border px-margin-mobile md:hidden\">\n <SidebarTrigger aria-label=\"Abrir navegación\" />\n <Logo layout=\"horizontal\" />\n </header>\n {children}\n </SidebarInset>\n </SidebarProvider>\n );\n}\n"
|
|
21
21
|
},
|
|
22
22
|
{
|
|
23
23
|
"path": "views/crm-app-layout.md",
|
|
24
24
|
"type": "registry:file",
|
|
25
25
|
"target": "views/crm-app-layout.md",
|
|
26
|
-
"content": "# crm-app-layout — block\n\nEstructura persistente de la app interna (**CRM**): navegación lateral colapsable data-driven (`AppSidebar`) y área de trabajo (`SidebarInset`). Instálalo con `create-lexy add crm-app-layout` — trae `AppSidebar`, `Sidebar` y `Logo`. La vista queda en tu proyecto (`views/AppLayout.tsx`): edítala con libertad.\n\n## Montaje\n\nEnvuelve tus rutas internas; el contenido (desk, detalle) se renderiza como `children`:\n\n```tsx\n<AppLayout>\n <CasosDesk />\n</AppLayout>\n```\n\n## Adaptación\n\n- La navegación se describe como datos en `groups` (lee `AppSidebar.md` antes de tocarla; no compongas a mano las primitivas de `Sidebar`).\n- Marca el ítem activo con `isActive` desde tu router.\n- Pie de usuario: pasa `user={{ name, email, avatar }}` a `AppSidebar`.\n\n## Evita\n\nUsar este layout en pantallas de cliente sin panel lateral (ahí va `AppHeaderBar` + secciones) y duplicar el logo dentro del contenido.\n"
|
|
26
|
+
"content": "# crm-app-layout — block\n\nEstructura persistente de la app interna (**CRM**): navegación lateral colapsable data-driven (`AppSidebar`) y área de trabajo (`SidebarInset`). Instálalo con `create-lexy add crm-app-layout` — trae `AppSidebar`, `Sidebar` y `Logo`. La vista queda en tu proyecto (`views/AppLayout.tsx`): edítala con libertad.\n\n## Montaje\n\nEnvuelve tus rutas internas; el contenido (desk, detalle) se renderiza como `children`:\n\n```tsx\n<AppLayout>\n <CasosDesk />\n</AppLayout>\n```\n\n## Adaptación\n\n- La navegación se describe como datos en `groups` (lee `AppSidebar.md` antes de tocarla; no compongas a mano las primitivas de `Sidebar`).\n- Marca el ítem activo con `isActive` desde tu router.\n- Pie de usuario: pasa `user={{ name, email, avatar }}` a `AppSidebar`.\n\n## Evita\n\nUsar este layout en pantallas de cliente sin panel lateral (ahí va `AppHeaderBar` + secciones) y duplicar el logo dentro del contenido.\n\n## Degradación móvil\n\nEn móvil el sidebar vive en un `Sheet` (lo maneja el componente `Sidebar`) y aparece un header solo-móvil (`md:hidden`) con `SidebarTrigger` y logo. No escondas la navegación: el trigger queda siempre visible.\n"
|
|
27
27
|
}
|
|
28
28
|
]
|
|
29
29
|
}
|
package/assets/r/crm-desk.json
CHANGED
|
@@ -19,13 +19,13 @@
|
|
|
19
19
|
"path": "views/CasosDesk.tsx",
|
|
20
20
|
"type": "registry:file",
|
|
21
21
|
"target": "views/CasosDesk.tsx",
|
|
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
|
|
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// Bajo md: la tabla densa pasa a lista y la toolbar apila (pauta §5).\n// Ver crm-desk.md.\n\nexport function CasosDesk() {\n return (\n <main className=\"w-full p-4\">\n <header className=\"mb-4 flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\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 w-full items-center gap-2 sm:w-auto\">\n <Input\n className=\"h-9 w-full sm: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 {/* Patrón §5: la tabla densa pasa a lista bajo md — misma información,\n detalle secundario colapsado, sin scroll horizontal a ciegas. */}\n <ul className=\"space-y-2 md:hidden\" aria-label=\"Casos\">\n <li className=\"rounded-md border border-border bg-card p-4\">\n <div className=\"flex items-center justify-between gap-2\">\n <p className=\"font-medium text-foreground\">María Pérez</p>\n <StatusDot tone=\"warning\">En revisión</StatusDot>\n </div>\n <p className=\"mt-1 text-sm text-muted-foreground\">Despido</p>\n <p className=\"mt-1 text-sm font-medium text-warning\">Vence hoy</p>\n </li>\n <li className=\"rounded-md border border-border bg-card p-4\">\n <div className=\"flex items-center justify-between gap-2\">\n <p className=\"font-medium text-foreground\">Juan Soto</p>\n <StatusDot tone=\"success\">Al día</StatusDot>\n </div>\n <p className=\"mt-1 text-sm text-muted-foreground\">Deuda</p>\n <p className=\"mt-1 text-sm text-muted-foreground\">12 días</p>\n </li>\n </ul>\n\n <div className=\"hidden md:block\">\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 </div>\n\n <footer className=\"mt-4 flex flex-col items-center gap-3 sm:flex-row sm: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",
|
|
26
26
|
"type": "registry:file",
|
|
27
27
|
"target": "views/crm-desk.md",
|
|
28
|
-
"content": "# crm-desk — block\n\nDesk de trabajo para **CRM**: ancho completo, toolbar con búsqueda y acción primaria, tabla densa y escaneable con estado por fila, y paginación al pie. Instálalo con `create-lexy add crm-desk` — trae `Table`, `StatusDot`, `Pagination`, `Input` y `Button`. La vista queda en tu proyecto (`views/CasosDesk.tsx`): edítala con libertad.\n\n## Montaje\n\nVive dentro del layout de aplicación (`crm-app-layout`), en el `SidebarInset`. Reemplaza las filas de ejemplo por tus datos y conecta `onClick` de cada fila a la navegación al detalle (`crm-detalle-caso`).\n\n## Adaptación\n\n- El subtítulo del header resume el estado del desk (*\"24 activos · 3 vencen hoy\"*): cámbialo por tus métricas reales.\n- Los cuatro estados de datos son obligatorios: loading (`Skeleton` con la forma de la tabla), vacío y error (`Empty` con salida), contenido.\n- Columnas vía `columns` (grid-template-columns); el estado por fila siempre con `StatusDot` + texto.\n\n## Evita\n\nEnvolver cada fila en una card, espaciado de cliente, color como único signo de estado, y listas de cientos de filas sin `Pagination`.\n"
|
|
28
|
+
"content": "# crm-desk — block\n\nDesk de trabajo para **CRM**: ancho completo, toolbar con búsqueda y acción primaria, tabla densa y escaneable con estado por fila, y paginación al pie. Instálalo con `create-lexy add crm-desk` — trae `Table`, `StatusDot`, `Pagination`, `Input` y `Button`. La vista queda en tu proyecto (`views/CasosDesk.tsx`): edítala con libertad.\n\n## Montaje\n\nVive dentro del layout de aplicación (`crm-app-layout`), en el `SidebarInset`. Reemplaza las filas de ejemplo por tus datos y conecta `onClick` de cada fila a la navegación al detalle (`crm-detalle-caso`).\n\n## Adaptación\n\n- El subtítulo del header resume el estado del desk (*\"24 activos · 3 vencen hoy\"*): cámbialo por tus métricas reales.\n- Los cuatro estados de datos son obligatorios: loading (`Skeleton` con la forma de la tabla), vacío y error (`Empty` con salida), contenido.\n- Columnas vía `columns` (grid-template-columns); el estado por fila siempre con `StatusDot` + texto.\n\n## Evita\n\nEnvolver cada fila en una card, espaciado de cliente, color como único signo de estado, y listas de cientos de filas sin `Pagination`.\n\n## Degradación móvil\n\nBajo `md` la tabla pasa a lista (un `li` por caso con `StatusDot`); la toolbar apila y la búsqueda es full-width. Si agregas columnas, decide cuáles sobreviven en la lista móvil: prioriza identidad, estado y plazo. Evita el scroll horizontal como única respuesta.\n"
|
|
29
29
|
}
|
|
30
30
|
]
|
|
31
31
|
}
|
|
@@ -16,13 +16,13 @@
|
|
|
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 { 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-
|
|
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. Bajo lg las columnas apilan:\n// contexto arriba, trabajo debajo (pauta §5). Ver crm-detalle-caso.md.\n\nexport function CasoDetalle() {\n return (\n <main className=\"flex w-full flex-col gap-4 p-4 lg:flex-row\">\n <aside className=\"w-full space-y-4 lg:w-72 lg:shrink-0\">\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",
|
|
23
23
|
"type": "registry:file",
|
|
24
24
|
"target": "views/crm-detalle-caso.md",
|
|
25
|
-
"content": "# crm-detalle-caso — block\n\nDetalle 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.\n\n## Montaje\n\nVive dentro del layout de aplicación (`crm-app-layout`); se llega desde una fila del desk (`crm-desk`).\n\n## Adaptación\n\n- El `aside` lleva la identidad (quién, qué caso) + estado (`Tag` con texto) + datos clave en `dl`; agrega pares `dt`/`dd` según tu dominio.\n- Cada `TabsContent` resuelve sus cuatro estados de datos (loading/vacío/error/contenido).\n- Acciones secundarias del caso → `DropdownMenu` junto al botón primario, no una fila de botones.\n\n## Evita\n\nRepetir la identidad del caso dentro de cada tab, panel izquierdo scrolleable independiente sin necesidad, y tabs con una sola pestaña.\n"
|
|
25
|
+
"content": "# crm-detalle-caso — block\n\nDetalle 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.\n\n## Montaje\n\nVive dentro del layout de aplicación (`crm-app-layout`); se llega desde una fila del desk (`crm-desk`).\n\n## Adaptación\n\n- El `aside` lleva la identidad (quién, qué caso) + estado (`Tag` con texto) + datos clave en `dl`; agrega pares `dt`/`dd` según tu dominio.\n- Cada `TabsContent` resuelve sus cuatro estados de datos (loading/vacío/error/contenido).\n- Acciones secundarias del caso → `DropdownMenu` junto al botón primario, no una fila de botones.\n\n## Evita\n\nRepetir la identidad del caso dentro de cada tab, panel izquierdo scrolleable independiente sin necesidad, y tabs con una sola pestaña.\n\n## Degradación móvil\n\nBajo `lg` las columnas apilan: contexto arriba, trabajo debajo. Las acciones del área de trabajo siguen visibles sin scroll; si el contexto crece, colapsa su detalle secundario en un `Accordion`.\n"
|
|
26
26
|
}
|
|
27
27
|
]
|
|
28
28
|
}
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"path": "components/base/DatePicker.md",
|
|
25
25
|
"type": "registry:file",
|
|
26
26
|
"target": "components/base/DatePicker.md",
|
|
27
|
-
"content": "# DatePicker — guía de uso\n\nSelector de fecha en un campo: un `Button` que abre un `Popover` con un\n`Calendar` adentro. Es la composición estándar para elegir una fecha en\nformularios. Muestra la fecha elegida formateada (en español por defecto). El componente vive en tu proyecto: instálalo con `create-lexy add date-picker` y edítalo con libertad.\n\n##
|
|
27
|
+
"content": "# DatePicker — guía de uso\n\nSelector de fecha en un campo: un `Button` que abre un `Popover` con un\n`Calendar` adentro. Es la composición estándar para elegir una fecha en\nformularios. Muestra la fecha elegida formateada (en español por defecto). El componente vive en tu proyecto: instálalo con `create-lexy add date-picker` y edítalo con libertad.\n\n## Cuándo usarlo\n\nPara capturar una fecha dentro de un formulario sin ocupar pantalla: el campo muestra la fecha elegida y el calendario aparece solo al abrirlo. Para fecha de nacimiento activa los dropdowns de mes/año — nadie navega trescientos meses con flechas.\n\n## Composición\n\nReceta empaquetada: un `Button` con apariencia de campo que abre un `Popover` con un `Calendar` dentro. Emparéjalo con `Label`, como a cualquier campo de formulario.\n\n## Uso básico (no controlado)\n\n```tsx\n<DatePicker />\n```\n\n## Controlado\n\n```tsx\nconst [date, setDate] = React.useState<Date | undefined>();\n\n<DatePicker value={date} onChange={setDate} />;\n```\n\n## Fecha de nacimiento (dropdowns de mes/año)\n\n```tsx\n<DatePicker captionLayout=\"dropdown\" placeholder=\"Fecha de nacimiento\" />\n```\n\n## Receta compuesta\n\n`DatePicker` es un wrapper delgado sobre `Popover` + `Button` + `Calendar`. Sus props extra (`variant`, `disabled`, `id`, `aria-*`…) pasan al botón trigger y el `ref` apunta a ese botón. Si necesitas variar la estructura (rango, dos meses, footer con acciones, otro trigger), compón las piezas directamente:\n\n```tsx\nimport { Button } from \"@/components/base/Button\";\nimport { Calendar } from \"@/components/base/Calendar\";\nimport { Popover, PopoverTrigger, PopoverContent } from \"@/components/base/Popover\";\n\n<Popover>\n <PopoverTrigger asChild>\n <Button variant=\"outline\">{fecha ? formatear(fecha) : \"Selecciona una fecha\"}</Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-auto p-0\" align=\"start\">\n <Calendar mode=\"range\" numberOfMonths={2} selected={rango} onSelect={setRango} />\n </PopoverContent>\n</Popover>\n```\n\n## Reglas\n\n- Para una sola fecha en un formulario, usa `DatePicker`; para elegir un período usa `Calendar` con `mode=\"range\"` dentro de un `Popover`.\n- En modo controlado pasa `value` **y** `onChange`; sin `onChange` el componente maneja su propio estado.\n- El popover se cierra al seleccionar una fecha.\n- Ajusta el ancho del campo con `className` (por defecto `w-65`).\n\n## Cuándo NO usar\n\n- **Elegir un rango** → `Calendar` con `mode=\"range\"` (date range picker).\n- **Mostrar un calendario siempre visible** → `Calendar` directo, sin `Popover`.\n- **Solo hora** → un input de tiempo.\n\n## Import\n\n```tsx\nimport { DatePicker } from \"@/components/base/DatePicker\";\n```\n\n## Props\n\n| Prop | Tipo | Default | Descripción |\n|---|---|---|---|\n| `value` | `Date` | — | Fecha seleccionada (modo controlado, junto a `onChange`). |\n| `defaultValue` | `Date` | — | Fecha inicial en modo no controlado. |\n| `onChange` | `(date?: Date) => void` | — | Callback de selección. Activa el modo controlado. |\n| `placeholder` | `string` | `\"Selecciona una fecha\"` | Texto cuando no hay fecha. |\n| `disabled` | `boolean` | `false` | Deshabilita el disparador. |\n| `captionLayout` | `\"label\" \\| \"dropdown\" \\| …` | `\"label\"` | Encabezado del calendario. |\n| `locale` | `Locale` | `es` | Localización (de `react-day-picker/locale`). |\n| `align` | `\"start\" \\| \"center\" \\| \"end\"` | `\"start\"` | Alineación del popover. |\n| `className` | `string` | — | Clases del botón disparador (p. ej. ancho). |\n\n## Para IA\n\n1. Para una fecha en formulario, usa `DatePicker` (no reconstruyas Popover+Calendar a mano salvo que necesites rango).\n2. Modo controlado: `value` + `onChange` con estado propio; no controlado: solo `<DatePicker />`.\n3. Usa `captionLayout=\"dropdown\"` para fechas lejanas (nacimiento, vencimientos).\n4. Para rango, compón `Popover` + `Button` + `Calendar mode=\"range\"`.\n"
|
|
28
28
|
}
|
|
29
29
|
]
|
|
30
30
|
}
|
package/assets/r/dialog.json
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"path": "components/base/Dialog.md",
|
|
20
20
|
"type": "registry:file",
|
|
21
21
|
"target": "components/base/Dialog.md",
|
|
22
|
-
"content": "# Dialog — guía de uso\n\nDiálogo modal primitivo (Radix). **Para la mayoría de los casos usa `AppDialog`**, que ya arma la composición Material 3 (icono, título, descripción, acciones). Usa `Dialog` directo solo para layouts modales a medida sin esa estructura. El componente vive en tu proyecto: instálalo con `create-lexy add dialog` y edítalo con libertad.\n\n##
|
|
22
|
+
"content": "# Dialog — guía de uso\n\nDiálogo modal primitivo (Radix). **Para la mayoría de los casos usa `AppDialog`**, que ya arma la composición Material 3 (icono, título, descripción, acciones). Usa `Dialog` directo solo para layouts modales a medida sin esa estructura. El componente vive en tu proyecto: instálalo con `create-lexy add dialog` y edítalo con libertad.\n\n## Cuándo usarlo\n\nEl modal primitivo para layouts a medida: formularios en modal, contenido especial. Para la mayoría de los casos usa `AppDialog`, que ya trae la estructura canónica; `Dialog` es el plan B cuando esa estructura no alcanza.\n\n## Composición\n\nComposición Radix: `DialogTrigger` abre `DialogContent` (overlay y portal ya resueltos); dentro van `DialogHeader` (`DialogTitle`, `DialogDescription`), el cuerpo libre y `DialogFooter`; `DialogClose` cierra.\n\n## Uso básico\n\n```tsx\n<Dialog>\n <DialogTrigger asChild>\n <Button variant=\"outline\">Ver detalle</Button>\n </DialogTrigger>\n <DialogContent>\n <DialogHeader>\n <DialogTitle>Detalle del documento</DialogTitle>\n <DialogDescription>Resumen generado automáticamente.</DialogDescription>\n </DialogHeader>\n {/* contenido a medida */}\n <DialogFooter>\n <DialogClose asChild><Button variant=\"ghost\">Cerrar</Button></DialogClose>\n </DialogFooter>\n </DialogContent>\n</Dialog>\n```\n\n## Reglas\n\n- Incluye **siempre** un `DialogTitle` (accesibilidad: nombra el diálogo). Si lo ocultas visualmente, mantenlo para lectores de pantalla.\n- Usa `DialogDescription` para el contexto; no dejes el cuerpo sin explicación.\n- Usa `DialogTrigger asChild` / `DialogClose asChild` sobre tus propios botones.\n- No anides diálogos; un modal a la vez.\n- El diálogo **no trae X de cierre**: se cierra con Escape, click en el overlay o un botón explícito con `DialogClose` (el patrón Lexy es nombrar la acción, p. ej. «Cancelar»).\n- Explica la consecuencia de las acciones (ver pauta de UX writing), sin \"¿estás seguro?\".\n\n## Cuándo NO usar\n\n- **Confirmación o formulario modal estándar** → `AppDialog` (menos código, patrón correcto).\n- **Panel lateral** (filtros, detalle largo) → `Sheet`.\n- **Ayuda breve anclada a un control** → `Popover`/`Tooltip`.\n- **Mensaje efímero de resultado** → `Toast`.\n\n## Import\n\n```tsx\nimport {\n Dialog, DialogTrigger, DialogContent, DialogHeader,\n DialogTitle, DialogDescription, DialogFooter, DialogClose,\n} from \"@/components/base/Dialog\";\n```\n\n## Exports\n\n`Dialog`, `DialogTrigger`, `DialogContent`, `DialogHeader`, `DialogFooter`, `DialogTitle`, `DialogDescription`, `DialogClose`, `DialogOverlay`, `DialogPortal`.\n\n## Para IA\n\n1. Pregúntate primero si `AppDialog` cubre el caso (confirmación/formulario): si sí, úsalo.\n2. Usa `Dialog` directo solo para un layout modal a medida.\n3. Garantiza `DialogTitle` (visible o sr-only) y descripción.\n4. Conecta trigger/close con `asChild` a tus botones.\n5. Redacta acciones por su consecuencia; usa `Sheet`/`Popover`/`Toast` si encajan mejor.\n"
|
|
23
23
|
}
|
|
24
24
|
]
|
|
25
25
|
}
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"path": "components/base/DropdownMenu.md",
|
|
20
20
|
"type": "registry:file",
|
|
21
21
|
"target": "components/base/DropdownMenu.md",
|
|
22
|
-
"content": "# DropdownMenu — guía de uso\n\nMenú de **acciones** contextuales que se abre desde un disparador (Radix). Para agrupar acciones secundarias (\"⋯\", \"Opciones\") sin llenar la pantalla de botones. El componente vive en tu proyecto: instálalo con `create-lexy add dropdown-menu` y edítalo con libertad.\n\n##
|
|
22
|
+
"content": "# DropdownMenu — guía de uso\n\nMenú de **acciones** contextuales que se abre desde un disparador (Radix). Para agrupar acciones secundarias (\"⋯\", \"Opciones\") sin llenar la pantalla de botones. El componente vive en tu proyecto: instálalo con `create-lexy add dropdown-menu` y edítalo con libertad.\n\n## Cuándo usarlo\n\nPara acciones contextuales agrupadas tras un disparador (“⋯”, “Opciones”): editar, duplicar, archivar. Saca de la vista lo secundario sin perderlo. Si se trata de elegir un valor de formulario, eso es un `Select`.\n\n## Composición\n\n`DropdownMenuTrigger` abre `DropdownMenuContent` con `DropdownMenuItem`s, organizables con `DropdownMenuGroup`, `DropdownMenuLabel` y `DropdownMenuSeparator`, más variantes de check/radio y submenús.\n\n## Uso básico\n\n```tsx\n<DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"ghost\" size=\"icon\" aria-label=\"Más acciones\"><MoreHorizontal /></Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuLabel>Acciones</DropdownMenuLabel>\n <DropdownMenuItem onSelect={editar}>Editar</DropdownMenuItem>\n <DropdownMenuItem onSelect={duplicar}>Duplicar</DropdownMenuItem>\n <DropdownMenuSeparator />\n <DropdownMenuItem className=\"text-destructive\" onSelect={eliminar}>\n Eliminar\n </DropdownMenuItem>\n </DropdownMenuContent>\n</DropdownMenu>\n```\n\n## Reglas\n\n- Es para **acciones**, no para seleccionar el valor de un formulario.\n- Da `aria-label` al trigger solo-icono.\n- Usa `DropdownMenuSeparator` para aislar acciones destructivas; márcalas con color **y** texto claro.\n- Agrupa con `DropdownMenuLabel`; no metas formularios largos dentro.\n- Submenús (`Sub`) solo para jerarquía real; evita anidar de más.\n\n## Cuándo NO usar\n\n- **Seleccionar un valor en un formulario** → `Select`.\n- **Buscar/filtrar opciones** → `Combobox` o `Command`.\n- **Contenido o formulario interactivo** → `Popover`.\n- **Navegación principal del sitio** → `NavigationMenu`.\n\n## Import\n\n```tsx\nimport {\n DropdownMenu, DropdownMenuTrigger, DropdownMenuContent,\n DropdownMenuItem, DropdownMenuSeparator, DropdownMenuLabel,\n} from \"@/components/base/DropdownMenu\";\n```\n\n## Exports\n\nRoot y trigger: `DropdownMenu`, `DropdownMenuTrigger`, `DropdownMenuContent`.\nÍtems: `DropdownMenuItem`, `DropdownMenuCheckboxItem`, `DropdownMenuRadioItem`, `DropdownMenuRadioGroup`.\nEstructura: `DropdownMenuLabel`, `DropdownMenuSeparator`, `DropdownMenuGroup`, `DropdownMenuShortcut`.\nSubmenús: `DropdownMenuSub`, `DropdownMenuSubTrigger`, `DropdownMenuSubContent`, `DropdownMenuPortal`.\n\n## Props (esenciales)\n\n| Parte | Prop | Descripción |\n|---|---|---|\n| `DropdownMenuTrigger` | `asChild` | Usa tu botón como disparador. |\n| `DropdownMenuContent` | `align` | `\"start\" \\| \"center\" \\| \"end\"`. |\n| `DropdownMenuItem` | `onSelect` | Acción al elegir. |\n| `DropdownMenuItem` | `disabled` | Deshabilita la acción. |\n| `DropdownMenuCheckboxItem` | `checked` / `onCheckedChange` | Alternar opción. |\n\n## Para IA\n\n1. Confirma que son acciones (verbos), no selección de datos.\n2. Usa `DropdownMenuTrigger asChild` sobre un `Button` (con `aria-label` si es icono).\n3. Ordena por frecuencia; separa y marca las destructivas.\n4. Si en realidad es selección de valor o búsqueda, cambia a `Select`/`Combobox`.\n"
|
|
23
23
|
}
|
|
24
24
|
]
|
|
25
25
|
}
|
package/assets/r/empty.json
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"path": "components/base/Empty.md",
|
|
20
20
|
"type": "registry:file",
|
|
21
21
|
"target": "components/base/Empty.md",
|
|
22
|
-
"content": "# Empty — guía de uso\n\nEstado vacío para cuando no hay datos, resultados o contenido que mostrar.\nCompuesto por sub-componentes que permiten construir distintas variantes. El componente vive en tu proyecto: instálalo con `create-lexy add empty` y edítalo con libertad.\n\n##
|
|
22
|
+
"content": "# Empty — guía de uso\n\nEstado vacío para cuando no hay datos, resultados o contenido que mostrar.\nCompuesto por sub-componentes que permiten construir distintas variantes. El componente vive en tu proyecto: instálalo con `create-lexy add empty` y edítalo con libertad.\n\n## Cuándo usarlo\n\nPara el momento en que no hay nada que mostrar: sin resultados, sin datos aún, primer uso. Un buen estado vacío dice qué pasa y qué hacer después — nunca dejes una tabla en blanco sin explicación.\n\n## Composición\n\nSlots: `EmptyMedia` (icono o ilustración), `EmptyHeader` con `EmptyTitle` y `EmptyDescription`, y `EmptyContent` para la acción de salida (normalmente un `Button`).\n\n## Uso básico\n\n```tsx\n<Empty>\n <EmptyHeader>\n <EmptyTitle>Sin resultados</EmptyTitle>\n <EmptyDescription>\n No se encontraron elementos que coincidan con tu búsqueda.\n </EmptyDescription>\n </EmptyHeader>\n</Empty>\n```\n\n## Con icono\n\n```tsx\n<Empty>\n <EmptyHeader>\n <EmptyMedia variant=\"icon\">\n <Inbox className=\"h-6 w-6\" />\n </EmptyMedia>\n <EmptyTitle>Bandeja vacía</EmptyTitle>\n <EmptyDescription>\n No tienes mensajes pendientes.\n </EmptyDescription>\n </EmptyHeader>\n</Empty>\n```\n\n## Con acción\n\n```tsx\n<Empty>\n <EmptyHeader>\n <EmptyMedia variant=\"icon\">\n <Upload className=\"h-6 w-6\" />\n </EmptyMedia>\n <EmptyTitle>Sube tu primer archivo</EmptyTitle>\n <EmptyDescription>\n Arrastra archivos aquí o haz click para seleccionarlos.\n </EmptyDescription>\n </EmptyHeader>\n <EmptyContent>\n <Button>Seleccionar archivos</Button>\n </EmptyContent>\n</Empty>\n```\n\n## Reglas\n\n- Usa `Empty` cuando no hay datos que mostrar.\n- No uses `Empty` para estados de carga → usa `Skeleton` o `Spinner`.\n- No uses `Empty` en espacios muy pequeños → usa un mensaje inline.\n- Ofrece una acción clara cuando sea posible (crear, buscar, filtrar).\n- Usa `variant=\"icon\"` en `EmptyMedia` para dar contexto visual.\n\n## Import\n\n```tsx\nimport { Empty, EmptyHeader, EmptyTitle, EmptyDescription, EmptyContent, EmptyMedia } from \"@/shared/components/base/Empty\"; // o @/components/base/Empty\nimport { Inbox } from \"lucide-react\";\n```\n\n## Props\n\n### Sub-componentes\n\n| Componente | Props | Descripción |\n|---|---|---|\n| `Empty` | `className?` | Contenedor principal. |\n| `EmptyHeader` | `className?` | Agrupa título, descripción e icono. |\n| `EmptyMedia` | `variant?: \"default\" \\| \"icon\"`, `className?` | Icono o media decorativa. |\n| `EmptyTitle` | `className?` | Título principal. |\n| `EmptyDescription` | `className?` | Texto explicativo. Soporta enlaces. |\n| `EmptyContent` | `className?` | Área para botones o acciones. |\n\n## Para IA\n\n1. Identifica si la pantalla o sección está vacía por falta de datos.\n2. Si es un estado vacío → usa `Empty`.\n3. Crea `EmptyHeader` con `EmptyTitle` y `EmptyDescription`.\n4. Agrega `EmptyMedia` con `variant=\"icon\"` si un icono aporta contexto.\n5. Agrega `EmptyContent` con botones o links cuando haya una acción posible.\n6. No uses `Empty` para estados de carga.\n7. Asegúrate de que el mensaje sea útil y no genérico.\n"
|
|
23
23
|
}
|
|
24
24
|
]
|
|
25
25
|
}
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"path": "components/base/FeatureCard.md",
|
|
18
18
|
"type": "registry:file",
|
|
19
19
|
"target": "components/base/FeatureCard.md",
|
|
20
|
-
"content": "# FeatureCard — guía de uso\n\nTarjeta con icono y descripción. Para destacar características. El componente vive en tu proyecto: instálalo con `create-lexy add feature-card` y edítalo con libertad.\n\n##
|
|
20
|
+
"content": "# FeatureCard — guía de uso\n\nTarjeta con icono y descripción. Para destacar características. El componente vive en tu proyecto: instálalo con `create-lexy add feature-card` y edítalo con libertad.\n\n## Cuándo usarlo\n\nPara destacar una capacidad o beneficio con icono y descripción: grillas de “qué incluye”, onboarding, páginas de marketing. Si el contenido es operable (datos, acciones), es un `Card` normal.\n\n## Composición\n\nUna sola pieza (`FeatureCard`) con icono, título y descripción. Se usa en grillas de dos o tres columnas.\n\n## Uso básico\n\n```tsx\n<FeatureCard />\n```\n\n## Reglas\n\n- Usa FeatureCard según el propósito descrito.\n- No abuses de este componente en contexts donde no aplica.\n\n## Import\n\n```tsx\nimport { FeatureCard } from \"@/components/base/FeatureCard\";\n```\n\n## Props\n\nConsulta la story en Storybook para ver las props disponibles.\n\n## Para IA\n\n1. Identifica el contexto de uso del componente.\n2. Importa FeatureCard 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"
|
|
21
21
|
}
|
|
22
22
|
]
|
|
23
23
|
}
|
package/assets/r/form.json
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"path": "components/base/Form.md",
|
|
25
25
|
"type": "registry:file",
|
|
26
26
|
"target": "components/base/Form.md",
|
|
27
|
-
"content": "# Form — guía de uso\n\nFormularios con validación (patrón shadcn sobre `react-hook-form` + `zod`). `Form` provee el contexto, `FormField` registra cada campo y `FormItem`/`FormLabel`/`FormControl` cablean ids y `aria-*` automáticamente; `FormMessage` muestra el error del campo. `create-lexy add form` instala también `zod` y `@hookform/resolvers`. El componente vive en tu proyecto: instálalo con `create-lexy add form` y edítalo con libertad.\n\n##
|
|
27
|
+
"content": "# Form — guía de uso\n\nFormularios con validación (patrón shadcn sobre `react-hook-form` + `zod`). `Form` provee el contexto, `FormField` registra cada campo y `FormItem`/`FormLabel`/`FormControl` cablean ids y `aria-*` automáticamente; `FormMessage` muestra el error del campo. `create-lexy add form` instala también `zod` y `@hookform/resolvers`. El componente vive en tu proyecto: instálalo con `create-lexy add form` y edítalo con libertad.\n\n## Cuándo usarlo\n\nPara formularios con validación: registra cada campo, muestra errores que explican cómo salir de ellos y cablea la accesibilidad sola. Para un campo suelto sin validación (un buscador), no hace falta — usa el input directo.\n\n## Composición\n\nPatrón shadcn sobre react-hook-form + zod: `Form` provee el contexto, `FormField` registra cada campo, y `FormItem`/`FormLabel`/`FormControl` conectan ids y `aria-*` automáticamente; `FormMessage` muestra el error del campo y `FormDescription` la ayuda.\n\n## Uso básico\n\n```tsx\nconst schema = z.object({\n email: z.string().email(\"Ingresa un correo válido\"),\n nombre: z.string().min(2, \"Ingresa tu nombre completo\"),\n});\n\nfunction PerfilForm() {\n const form = useForm<z.infer<typeof schema>>({\n resolver: zodResolver(schema),\n defaultValues: { email: \"\", nombre: \"\" },\n });\n\n function onSubmit(values: z.infer<typeof schema>) {\n // values ya viene validado y tipado\n }\n\n return (\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"grid gap-6\">\n <FormField\n control={form.control}\n name=\"email\"\n render={({ field }) => (\n <FormItem>\n <FormLabel required>Correo</FormLabel>\n <FormControl>\n <Input type=\"email\" placeholder=\"nombre@empresa.cl\" {...field} />\n </FormControl>\n <FormDescription>Usaremos este correo para avisarte.</FormDescription>\n <FormMessage />\n </FormItem>\n )}\n />\n <Button type=\"submit\">Guardar cambios</Button>\n </form>\n </Form>\n );\n}\n```\n\n## Integración con los inputs del sistema\n\n`FormControl` es un Slot: envuelve cualquier control del registry sin nodo extra. Con `Input`, `Textarea` y `Searchbox` pasa `{...field}` directo. Para controles con API propia, mapea explícito:\n\n```tsx\n{/* Select */}\n<Select onValueChange={field.onChange} value={field.value}>\n <FormControl>\n <SelectTrigger><SelectValue placeholder=\"Selecciona\" /></SelectTrigger>\n </FormControl>\n <SelectContent>…</SelectContent>\n</Select>\n\n{/* Checkbox / Switch */}\n<FormControl>\n <Checkbox checked={field.value} onCheckedChange={field.onChange} />\n</FormControl>\n\n{/* DatePicker */}\n<FormControl>\n <DatePicker value={field.value} onChange={field.onChange} />\n</FormControl>\n```\n\n## Reglas (UX writing de errores)\n\n- El mensaje de error dice **qué pasó y cómo arreglarlo, en tono neutro**: *\"Ingresa un correo válido\"*, no *\"Error de validación\"* ni *\"Campo inválido\"*.\n- Escribe los mensajes en el schema de zod (segundo argumento de cada validación): ahí viven junto a la regla.\n- El label existe siempre, aunque haya placeholder; campo obligatorio se marca con `required` en `FormLabel`.\n- Los inputs del sistema ya pintan el borde rojo con `aria-invalid` (se lo pone `FormControl`); no dupliques estilos de error.\n- Valida en el submit (default de react-hook-form); evita validar tecla a tecla salvo necesidad real (`mode: \"onBlur\"` si el flujo lo pide).\n\n## Cuándo NO usar\n\n- **Un solo campo suelto** (un buscador, un toggle) → usa el control directo con estado local; el patrón Form paga cuando hay validación y varios campos.\n- **Filtros que aplican al instante** → estado local o de URL, sin submit.\n\n## Import\n\n```tsx\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport { useForm } from \"react-hook-form\";\nimport { z } from \"zod\";\n\nimport {\n Form,\n FormControl,\n FormDescription,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n} from \"@/components/base/Form\";\n```\n\n## Props\n\n| Pieza | Qué es |\n|---|---|\n| `Form` | Alias de `FormProvider` de react-hook-form: recibe `{...form}`. |\n| `FormField` | `Controller` + contexto del nombre del campo (`control`, `name`, `render`). |\n| `FormItem` | Contenedor del campo (`grid gap-2`); genera el id base. `className`, `...props`, `ref`. |\n| `FormLabel` | `Label` del sistema conectado al control (`htmlFor` automático, rojo con error). Acepta `required`. |\n| `FormControl` | Slot que inyecta `id`, `aria-describedby`, `aria-invalid` al control envuelto. |\n| `FormDescription` | Ayuda contextual (`text-muted-foreground`). |\n| `FormMessage` | Error del campo (`text-destructive`); no renderiza nada si no hay error. |\n| `useFormField` | Hook con el estado del campo, para piezas custom. |\n\n## Para IA\n\n1. Define el schema zod arriba del componente; mensajes de error en español neutro que digan cómo corregir.\n2. Un `FormField` por campo, siempre con `FormItem` + `FormLabel` + `FormControl` + `FormMessage`.\n3. Usa los controles del registry dentro de `FormControl` (Input, Select, Checkbox, DatePicker…), no inputs nativos sin estilo.\n4. `FormDescription` solo si la ayuda aporta; no repitas el label.\n5. El botón de submit es `Button` con `type=\"submit\"` dentro del `<form>`.\n"
|
|
28
28
|
}
|
|
29
29
|
]
|
|
30
30
|
}
|
package/assets/r/header-bar.json
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"path": "components/base/HeaderBar.md",
|
|
22
22
|
"type": "registry:file",
|
|
23
23
|
"target": "components/base/HeaderBar.md",
|
|
24
|
-
"content": "# HeaderBar — guía de uso\n\nBarra superior de aplicación o sitio: marca a la izquierda y acciones a la derecha. Renderiza un `<header>` semántico. El componente vive en tu proyecto: instálalo con `create-lexy add header-bar` y edítalo con libertad.\n\n##
|
|
24
|
+
"content": "# HeaderBar — guía de uso\n\nBarra superior de aplicación o sitio: marca a la izquierda y acciones a la derecha. Renderiza un `<header>` semántico. El componente vive en tu proyecto: instálalo con `create-lexy add header-bar` y edítalo con libertad.\n\n## Cuándo usarlo\n\nPara la barra superior de un sitio o app: marca a la izquierda, acciones a la derecha, semántica de `<header>`. Si puedes describir la navegación como datos, `AppHeaderBar` te ahorra la composición.\n\n## Composición\n\nUna pieza (`HeaderBar`) con slots `brand` y `actions`, variantes de superficie (clara / navy) y modo sticky.\n\n## Uso básico\n\n```tsx\n<HeaderBar\n actions={\n <>\n <Button variant=\"ghost\">Ingresar</Button>\n <Button>Crear cuenta</Button>\n </>\n }\n/>\n```\n\nPor defecto muestra el `Logo`. Sobrescribe la marca con `brand`:\n\n```tsx\n<HeaderBar brand={<Logo layout=\"horizontal\" />} actions={<UserMenu />} />\n```\n\n## Reglas\n\n- Mantén pocas acciones a la derecha; agrupa el resto en un `DropdownMenu`.\n- `surface=\"navy\"` ajusta el logo a su versión clara automáticamente; no fuerces el color del logo.\n- Si es una app con barra lateral, usa `SidebarProvider` + `AppSidebar` + `SidebarInset` (ver AppSidebar.md).\n- `sticky` para navegación persistente; cuida no tapar contenido al anclar.\n\n## Cuándo NO usar\n\n- **Layout completo de app con sidebar** → `SidebarProvider` + `AppSidebar` + `SidebarInset` (ver AppSidebar.md).\n- **Navegación lateral** → `AppSidebar`.\n- **Menús de navegación con dropdowns ricos** → combínalo con `NavigationMenu` dentro de `actions`/`brand`.\n\n## Import\n\n```tsx\nimport { HeaderBar } from \"@/components/base/HeaderBar\";\n```\n\n## Props\n\n| Prop | Tipo | Default | Descripción |\n|---|---|---|---|\n| `surface` | `\"default\" \\| \"transparent\" \\| \"navy\"` | `\"default\"` | Fondo. `navy` usa el ink de marca con texto blanco. |\n| `bordered` | `boolean` | `true` | Borde inferior. |\n| `padding` | `\"page\" \\| \"none\"` | `\"page\"` | Padding horizontal estándar o nulo. |\n| `sticky` | `boolean` | `false` | Fija la barra al hacer scroll. |\n| `actions` | `ReactNode` | — | Contenido alineado a la derecha. |\n| `brand` | `ReactNode` | `<Logo>` | Sobrescribe el área de marca. |\n\n## Para IA\n\n1. Úsalo como cabecera de sitio o barra superior simple.\n2. Pon la marca (o deja el `Logo` por defecto) y 1–3 acciones en `actions`.\n3. Elige `surface`/`sticky` según el contexto; agrupa acciones extra en `DropdownMenu`.\n4. Si la pantalla es una app con sidebar, prefiere `SidebarProvider` + `AppSidebar` + `SidebarInset` en lugar de componer a mano.\n"
|
|
25
25
|
}
|
|
26
26
|
]
|
|
27
27
|
}
|
package/assets/r/input.json
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"path": "components/base/Input.md",
|
|
18
18
|
"type": "registry:file",
|
|
19
19
|
"target": "components/base/Input.md",
|
|
20
|
-
"content": "# Input — guía de uso\n\nCampo de texto de una línea. Renderiza un `<input>` nativo; acepta todos sus atributos (`type`, `value`, `onChange`, `placeholder`, `aria-*`). El componente vive en tu proyecto: instálalo con `create-lexy add input` y edítalo con libertad.\n\n##
|
|
20
|
+
"content": "# Input — guía de uso\n\nCampo de texto de una línea. Renderiza un `<input>` nativo; acepta todos sus atributos (`type`, `value`, `onChange`, `placeholder`, `aria-*`). El componente vive en tu proyecto: instálalo con `create-lexy add input` y edítalo con libertad.\n\n## Cuándo usarlo\n\nPara texto corto de una línea: nombre, RUT, correo, monto. El tipo correcto (`email`, `tel`, `number`) trae el teclado correcto en móvil. Para texto que respira (comentarios, relatos), usa `Textarea`; para buscar, `Searchbox`.\n\n## Composición\n\nUna sola pieza (`Input`, un `<input>` nativo) sin label propio: siempre con `Label` visible arriba — el placeholder no reemplaza al label. En formularios validados se envuelve con `FormControl`.\n\n## Uso básico\n\nSiempre con un `Label` asociado por `id`/`htmlFor`:\n\n```tsx\n<div className=\"grid gap-1.5\">\n <Label htmlFor=\"email\">Email</Label>\n <Input id=\"email\" type=\"email\" placeholder=\"nombre@empresa.cl\" />\n</div>\n```\n\n## Estado de error\n\n```tsx\n<div className=\"grid gap-1.5\">\n <Label htmlFor=\"rut\">RUT</Label>\n <Input id=\"rut\" aria-invalid aria-describedby=\"rut-error\" />\n <p id=\"rut-error\" className=\"text-sm text-destructive\">\n Ingresa un RUT válido, por ejemplo 12.345.678-9.\n </p>\n</div>\n```\n\n## Reglas\n\n- **Siempre** asocia un `Label` por `htmlFor`/`id`. El `placeholder` no es un label: desaparece al escribir.\n- Marca el error con `aria-invalid` **y** un mensaje de texto cercano; el color del borde no basta (accesibilidad).\n- Enlaza el mensaje de error con `aria-describedby` para lectores de pantalla.\n- Usa el `type` correcto (`email`, `number`, `tel`): mejora el teclado móvil y la validación.\n\n## Cuándo NO usar\n\n- **Texto largo / multilínea** → `Textarea`.\n- **Elegir de una lista fija** → `Select` (o `Combobox` si hay búsqueda).\n- **Sí/no o selección múltiple** → `Checkbox` / `RadioGroup` / `Switch`.\n- **Buscar con icono y limpiar** → `Searchbox`.\n\n## Import\n\n```tsx\nimport { Input } from \"@/components/base/Input\";\n```\n\n## Props\n\n| Prop | Tipo | Default | Descripción |\n|---|---|---|---|\n| `type` | `string` | `\"text\"` | `text`, `email`, `password`, `number`, `search`, `file`, etc. |\n| `aria-invalid` | `boolean` | — | Activa el estilo de error (borde destructive). |\n| `disabled` | `boolean` | `false` | Deshabilita el campo. |\n| ...resto | `input` props | — | `value`, `onChange`, `placeholder`, `name`, `required`, etc. |\n\n## Para IA\n\n1. Confirma que el dato es texto corto de una línea; si no, elige el control adecuado (ver arriba).\n2. Crea el par `Label` + `Input` con `htmlFor`/`id` coincidentes.\n3. Elige `type` según el dato.\n4. Para validación, usa `aria-invalid` + mensaje con `aria-describedby`, redactado como corregir (no solo \"campo inválido\").\n5. No uses el `placeholder` como única etiqueta.\n"
|
|
21
21
|
}
|
|
22
22
|
]
|
|
23
23
|
}
|
|
@@ -16,13 +16,13 @@
|
|
|
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=\"mx-auto w-full max-w-xl px-
|
|
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. Móvil primero: bajo sm las\n// acciones apilan full-width (pauta buenas-practicas.md §5). Ver intake-wizard.md.\n\nexport function IntakeDatosPersonales() {\n return (\n <main className=\"mx-auto w-full max-w-xl px-margin-mobile py-6 sm: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 flex-col-reverse gap-2 pt-2 sm:flex-row sm:items-center sm:justify-between\">\n <Button type=\"button\" variant=\"ghost\" className=\"w-full sm:w-auto\">\n Volver\n </Button>\n <Button type=\"submit\" className=\"w-full sm:w-auto\">\n Guardar y continuar\n </Button>\n </div>\n </form>\n </main>\n );\n}\n"
|
|
20
20
|
},
|
|
21
21
|
{
|
|
22
22
|
"path": "views/intake-wizard.md",
|
|
23
23
|
"type": "registry:file",
|
|
24
24
|
"target": "views/intake-wizard.md",
|
|
25
|
-
"content": "# intake-wizard — block\n\nPaso 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.\n\n## Montaje\n\nEs una vista: conéctala a tu router y duplica el archivo por cada paso del wizard (un paso = una pantalla).\n\n```tsx\nimport { IntakeDatosPersonales } from \"@/views/IntakeDatosPersonales\";\n```\n\n## Adaptación\n\n- Cambia `Paso 2 de 4` y `value` del `Progress` según el paso real.\n- Para validación, instala `form` (`create-lexy add form`) y envuelve los campos con `FormField`.\n- El botón \"Volver\" es `ghost` y va a la izquierda; el primario siempre a la derecha.\n\n## Evita\n\nHero comercial, eyebrow decorativo, una card por campo, y mostrar los 4 pasos en una sola pantalla.\n"
|
|
25
|
+
"content": "# intake-wizard — block\n\nPaso 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.\n\n## Montaje\n\nEs una vista: conéctala a tu router y duplica el archivo por cada paso del wizard (un paso = una pantalla).\n\n```tsx\nimport { IntakeDatosPersonales } from \"@/views/IntakeDatosPersonales\";\n```\n\n## Adaptación\n\n- Cambia `Paso 2 de 4` y `value` del `Progress` según el paso real.\n- Para validación, instala `form` (`create-lexy add form`) y envuelve los campos con `FormField`.\n- El botón \"Volver\" es `ghost` y va a la izquierda; el primario siempre a la derecha.\n\n## Evita\n\nHero comercial, eyebrow decorativo, una card por campo, y mostrar los 4 pasos en una sola pantalla.\n\n## Degradación móvil\n\nEs móvil primero: columna angosta y foco único. Bajo `sm` las acciones apilan full-width con el CTA primero; los controles usan `size=\"default\"` (target táctil ~44px).\n"
|
|
26
26
|
}
|
|
27
27
|
]
|
|
28
28
|
}
|