@adamosuiteservices/ui 1.2.4 → 1.3.5
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/README.md +4 -0
- package/dist/accordion-rounded.cjs +1 -1
- package/dist/accordion-rounded.js +1 -1
- package/dist/accordion.cjs +1 -1
- package/dist/accordion.js +1 -1
- package/dist/avatar.cjs +1 -1
- package/dist/avatar.js +1 -1
- package/dist/badge.cjs +1 -1
- package/dist/badge.js +1 -1
- package/dist/breadcrumb.cjs +1 -0
- package/dist/breadcrumb.js +105 -0
- package/dist/{button-C1n6snOY.js → button-2GdKenQI.js} +1 -1
- package/dist/{button-BV-_FVKZ.cjs → button-DEQVHMrX.cjs} +1 -1
- package/dist/button-group.cjs +1 -1
- package/dist/button-group.js +2 -2
- package/dist/button.cjs +1 -1
- package/dist/button.js +1 -1
- package/dist/calendar.cjs +1 -1
- package/dist/calendar.js +1 -1
- package/dist/{checkbox-BrmXPKTn.js → checkbox-Dr487kAg.js} +3 -3
- package/dist/{checkbox-Lq-HvSgc.cjs → checkbox-YWAnswaW.cjs} +1 -1
- package/dist/checkbox.cjs +1 -1
- package/dist/checkbox.js +1 -1
- package/dist/collapsible.cjs +1 -1
- package/dist/collapsible.js +1 -1
- package/dist/combobox.cjs +1 -1
- package/dist/combobox.js +6 -6
- package/dist/components/ui/breadcrumb/breadcrumb.d.ts +11 -0
- package/dist/components/ui/breadcrumb/breadcrumb.stories.d.ts +26 -0
- package/dist/components/ui/breadcrumb/index.d.ts +1 -0
- package/dist/components/ui/dialog/dialog.d.ts +2 -1
- package/dist/context-menu.cjs +1 -1
- package/dist/context-menu.js +2 -2
- package/dist/custom-layered-styles.css +1 -1
- package/dist/dialog.cjs +1 -1
- package/dist/dialog.js +33 -19
- package/dist/dropdown-menu.cjs +1 -1
- package/dist/dropdown-menu.js +3 -3
- package/dist/ellipsis-CryjZKZn.js +15 -0
- package/dist/ellipsis-Ct9VTDOG.cjs +6 -0
- package/dist/field.cjs +1 -1
- package/dist/field.js +2 -2
- package/dist/hover-card.cjs +1 -1
- package/dist/hover-card.js +6 -6
- package/dist/{index-CAOY367Y.js → index-B0M7VOwp.js} +2 -2
- package/dist/{index-B-ZRqW0J.js → index-BBoIAjAs.js} +3 -3
- package/dist/{index-gO_QEiaK.cjs → index-BDs8lUfq.cjs} +1 -1
- package/dist/index-BFyr34mw.cjs +5 -0
- package/dist/index-BMWt1NBG.js +79 -0
- package/dist/{index-yR-v1A4G.js → index-BX9hz-JD.js} +1 -1
- package/dist/{index-BGiGvaq8.cjs → index-BcGMAmWE.cjs} +1 -1
- package/dist/{index-IKJMQref.cjs → index-Bd0gQB0k.cjs} +1 -1
- package/dist/{index-VIUqZjyP.cjs → index-BeWgla7c.cjs} +1 -1
- package/dist/{index-EUea2gfp.js → index-BpWB3aFK.js} +1 -1
- package/dist/index-BvLQnI56.js +59 -0
- package/dist/{index-CwUFT-GQ.js → index-C0YiLSjW.js} +4 -4
- package/dist/{index-o0sNTcKe.js → index-CBjZooac.js} +2 -2
- package/dist/{index-DnS_sBBe.cjs → index-COuvjZLM.cjs} +1 -1
- package/dist/index-CTjlbbt9.cjs +1 -0
- package/dist/index-CUWMxxKG.js +97 -0
- package/dist/{index-C329e3yQ.js → index-CZZ3llmi.js} +2 -2
- package/dist/index-CjyiloO7.cjs +1 -0
- package/dist/{index-D3wSWKST.cjs → index-Cmx9M9cZ.cjs} +1 -1
- package/dist/index-CocSS1YK.cjs +1 -0
- package/dist/index-CzRiuk60.cjs +1 -0
- package/dist/index-DFPDUUq7.js +658 -0
- package/dist/{index-D3S7dBDI.cjs → index-DIwmXz1u.cjs} +1 -1
- package/dist/index-DLcqcWxM.js +29 -0
- package/dist/index-DMLQL2aG.js +286 -0
- package/dist/{index-DXQ-7kNJ.cjs → index-DMs8RL3E.cjs} +1 -1
- package/dist/{index-Ce3QBKyj.cjs → index-Dbj9vHNq.cjs} +1 -1
- package/dist/{index-BRLtxFFr.cjs → index-DmGzwG2z.cjs} +1 -1
- package/dist/{index-P1sVIHE3.js → index-PYkEXTqJ.js} +1 -1
- package/dist/{index-DulPG3F9.js → index-Se4vRnIO.js} +3 -3
- package/dist/index-_XxjJPRD.cjs +1 -0
- package/dist/{index-B-cHTKrs.js → index-yWvyIlmA.js} +4 -4
- package/dist/input-group.cjs +1 -1
- package/dist/input-group.js +1 -1
- package/dist/{label-Cne2J57f.cjs → label-BjXORCBM.cjs} +1 -1
- package/dist/{label-Ky8qBEC3.js → label-CmwGvhy1.js} +1 -1
- package/dist/label.cjs +1 -1
- package/dist/label.js +1 -1
- package/dist/pagination.cjs +1 -6
- package/dist/pagination.js +58 -69
- package/dist/popover-3rIoNCXs.js +306 -0
- package/dist/popover-FCKBtFo-.cjs +1 -0
- package/dist/popover.cjs +1 -1
- package/dist/popover.js +1 -1
- package/dist/progress.cjs +1 -1
- package/dist/progress.js +1 -1
- package/dist/radio-group.cjs +1 -1
- package/dist/radio-group.js +5 -5
- package/dist/select.cjs +2 -2
- package/dist/select.js +585 -542
- package/dist/{separator-CGnu_jIu.cjs → separator-BaZqZZ9R.cjs} +1 -1
- package/dist/{separator-BH73A90k.js → separator-DR7lQjv9.js} +1 -1
- package/dist/separator.cjs +1 -1
- package/dist/separator.js +1 -1
- package/dist/{sheet-CcxnJ6LH.cjs → sheet-CU-sFSaJ.cjs} +1 -1
- package/dist/{sheet-_DVpQIVF.js → sheet-UZWAbdXr.js} +1 -1
- package/dist/sheet.cjs +1 -1
- package/dist/sheet.js +1 -1
- package/dist/sidebar.cjs +1 -1
- package/dist/sidebar.js +4 -4
- package/dist/slider.cjs +1 -1
- package/dist/slider.js +3 -3
- package/dist/styles.css +1 -1
- package/dist/switch.cjs +1 -1
- package/dist/switch.js +2 -2
- package/dist/tabs-underline.cjs +1 -1
- package/dist/tabs-underline.js +1 -1
- package/dist/tabs.cjs +1 -1
- package/dist/tabs.js +1 -1
- package/dist/toaster.cjs +1 -1
- package/dist/toaster.js +1 -1
- package/dist/toggle.cjs +1 -1
- package/dist/toggle.js +1 -1
- package/dist/tooltip.cjs +1 -1
- package/dist/tooltip.js +114 -108
- package/dist/typography.cjs +1 -1
- package/dist/typography.js +16 -16
- package/docs/AI-GUIDE.md +321 -0
- package/docs/components/layout/sidebar.md +330 -0
- package/docs/components/layout/toaster.md +436 -0
- package/docs/components/ui/accordion-rounded.md +583 -0
- package/docs/components/ui/accordion.md +267 -0
- package/docs/components/ui/alert.md +671 -0
- package/docs/components/ui/avatar.md +588 -0
- package/docs/components/ui/badge.md +1024 -0
- package/docs/components/ui/breadcrumb.md +614 -0
- package/docs/components/ui/button-group.md +1002 -0
- package/docs/components/ui/button.md +1078 -0
- package/docs/components/ui/calendar.md +1159 -0
- package/docs/components/ui/card.md +1265 -0
- package/docs/components/ui/checkbox.md +292 -0
- package/docs/components/ui/collapsible.md +320 -0
- package/docs/components/ui/combobox.md +328 -0
- package/docs/components/ui/command.md +454 -0
- package/docs/components/ui/context-menu.md +540 -0
- package/docs/components/ui/dialog.md +628 -0
- package/docs/components/ui/dropdown-menu.md +731 -0
- package/docs/components/ui/field.md +706 -0
- package/docs/components/ui/hover-card.md +446 -0
- package/docs/components/ui/input-group.md +509 -0
- package/docs/components/ui/input.md +362 -0
- package/docs/components/ui/kbd.md +434 -0
- package/docs/components/ui/label.md +359 -0
- package/docs/components/ui/pagination.md +650 -0
- package/docs/components/ui/popover.md +536 -0
- package/docs/components/ui/progress.md +182 -0
- package/docs/components/ui/radio-group.md +311 -0
- package/docs/components/ui/select.md +352 -0
- package/docs/components/ui/separator.md +214 -0
- package/docs/components/ui/sheet.md +142 -0
- package/docs/components/ui/skeleton.md +140 -0
- package/docs/components/ui/slider.md +341 -0
- package/docs/components/ui/spinner.md +170 -0
- package/docs/components/ui/switch.md +402 -0
- package/docs/components/ui/table.md +183 -0
- package/docs/components/ui/tabs-underline.md +106 -0
- package/docs/components/ui/tabs.md +122 -0
- package/docs/components/ui/textarea.md +243 -0
- package/docs/components/ui/toggle.md +243 -0
- package/docs/components/ui/tooltip.md +320 -0
- package/docs/components/ui/typography.md +191 -0
- package/package.json +11 -5
- package/dist/index-6oTEokEx.js +0 -82
- package/dist/index-B-NyefE0.js +0 -243
- package/dist/index-BKbK2GzY.cjs +0 -1
- package/dist/index-BMitW9UR.cjs +0 -1
- package/dist/index-BpvjJ_T6.cjs +0 -5
- package/dist/index-C5wjudc-.js +0 -36
- package/dist/index-CezwiPd_.js +0 -615
- package/dist/index-D02K8KOB.js +0 -54
- package/dist/index-D7hQvndv.cjs +0 -1
- package/dist/index-DQvx1rG_.cjs +0 -1
- package/dist/popover-BjdTqaB8.cjs +0 -1
- package/dist/popover-EnVfE0YA.js +0 -263
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Tabs
|
|
2
|
+
|
|
3
|
+
Pestañas con background. Basado en Radix UI.
|
|
4
|
+
|
|
5
|
+
## Descripción
|
|
6
|
+
|
|
7
|
+
El componente `Tabs` permite organizar contenido en secciones que se pueden alternar.
|
|
8
|
+
|
|
9
|
+
## Importación
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
import {
|
|
13
|
+
Tabs,
|
|
14
|
+
TabsList,
|
|
15
|
+
TabsTrigger,
|
|
16
|
+
TabsContent,
|
|
17
|
+
} from "@adamosuiteservices/ui/tabs";
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Anatomía
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
<Tabs defaultValue="account">
|
|
24
|
+
<TabsList>
|
|
25
|
+
<TabsTrigger value="account">Account</TabsTrigger>
|
|
26
|
+
<TabsTrigger value="password">Password</TabsTrigger>
|
|
27
|
+
</TabsList>
|
|
28
|
+
<TabsContent value="account">Account settings content</TabsContent>
|
|
29
|
+
<TabsContent value="password">Password settings content</TabsContent>
|
|
30
|
+
</Tabs>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Componentes**: 4 (Tabs, TabsList, TabsTrigger, TabsContent)
|
|
34
|
+
|
|
35
|
+
## Props
|
|
36
|
+
|
|
37
|
+
### Tabs (Root)
|
|
38
|
+
|
|
39
|
+
| Prop | Tipo | Descripción |
|
|
40
|
+
| --------------- | ------------------------- | -------------------------- |
|
|
41
|
+
| `defaultValue` | `string` | Tab inicial (uncontrolled) |
|
|
42
|
+
| `value` | `string` | Tab controlado |
|
|
43
|
+
| `onValueChange` | `(value: string) => void` | Callback al cambiar |
|
|
44
|
+
|
|
45
|
+
### TabsTrigger
|
|
46
|
+
|
|
47
|
+
| Prop | Tipo | Descripción |
|
|
48
|
+
| ---------- | --------- | ------------------- |
|
|
49
|
+
| `value` | `string` | Valor único del tab |
|
|
50
|
+
| `disabled` | `boolean` | Deshabilitar tab |
|
|
51
|
+
|
|
52
|
+
## Patrones de Uso
|
|
53
|
+
|
|
54
|
+
### Básico
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
<Tabs defaultValue="account">
|
|
58
|
+
<TabsList>
|
|
59
|
+
<TabsTrigger value="account">Account</TabsTrigger>
|
|
60
|
+
<TabsTrigger value="password">Password</TabsTrigger>
|
|
61
|
+
</TabsList>
|
|
62
|
+
<TabsContent value="account">Account settings content</TabsContent>
|
|
63
|
+
<TabsContent value="password">Password settings content</TabsContent>
|
|
64
|
+
</Tabs>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Controlado
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
const [activeTab, setActiveTab] = useState("overview");
|
|
71
|
+
|
|
72
|
+
<Tabs value={activeTab} onValueChange={setActiveTab}>
|
|
73
|
+
<TabsList>
|
|
74
|
+
<TabsTrigger value="overview">Overview</TabsTrigger>
|
|
75
|
+
<TabsTrigger value="analytics">Analytics</TabsTrigger>
|
|
76
|
+
</TabsList>
|
|
77
|
+
<TabsContent value="overview">Overview content</TabsContent>
|
|
78
|
+
<TabsContent value="analytics">Analytics content</TabsContent>
|
|
79
|
+
</Tabs>;
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Con Iconos
|
|
83
|
+
|
|
84
|
+
```tsx
|
|
85
|
+
import { UserIcon, KeyIcon, HomeIcon, SettingsIcon } from "lucide-react";
|
|
86
|
+
|
|
87
|
+
<TabsList>
|
|
88
|
+
<TabsTrigger value="home">
|
|
89
|
+
<HomeIcon />
|
|
90
|
+
Home
|
|
91
|
+
</TabsTrigger>
|
|
92
|
+
<TabsTrigger value="settings">
|
|
93
|
+
<SettingsIcon />
|
|
94
|
+
Settings
|
|
95
|
+
</TabsTrigger>
|
|
96
|
+
<TabsTrigger value="profile">
|
|
97
|
+
<UserIcon />
|
|
98
|
+
Profile
|
|
99
|
+
</TabsTrigger>
|
|
100
|
+
<TabsTrigger value="security">
|
|
101
|
+
<KeyIcon />
|
|
102
|
+
Security
|
|
103
|
+
</TabsTrigger>
|
|
104
|
+
</TabsList>;
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Casos de Uso
|
|
108
|
+
|
|
109
|
+
**Settings**: Secciones de configuración
|
|
110
|
+
**Navigation**: Navegación por secciones
|
|
111
|
+
**Data views**: Diferentes vistas de datos
|
|
112
|
+
|
|
113
|
+
## Estilos Base
|
|
114
|
+
|
|
115
|
+
- **List**: `bg-muted rounded-lg h-9`
|
|
116
|
+
- **Trigger active**: `bg-primary text-primary-foreground shadow-sm`
|
|
117
|
+
- **Trigger**: `rounded-md px-2 py-1 font-semibold`
|
|
118
|
+
|
|
119
|
+
## Referencias
|
|
120
|
+
|
|
121
|
+
- **Radix UI Tabs**: <https://www.radix-ui.com/primitives/docs/components/tabs>
|
|
122
|
+
- **shadcn/ui Tabs**: <https://ui.shadcn.com/docs/components/tabs>
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# Textarea
|
|
2
|
+
|
|
3
|
+
Campo de texto multilínea con auto-resize. Wrapper nativo de `<textarea>` HTML.
|
|
4
|
+
|
|
5
|
+
## Descripción
|
|
6
|
+
|
|
7
|
+
El componente `Textarea` proporciona un campo de entrada de texto multilínea.
|
|
8
|
+
|
|
9
|
+
## Importación
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
import { Textarea } from "@adamosuiteservices/ui/textarea";
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Anatomía
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
<Textarea placeholder="Type your message here." />
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Componentes**: 1 (Textarea)
|
|
22
|
+
|
|
23
|
+
## Props
|
|
24
|
+
|
|
25
|
+
| Prop | Tipo | Descripción |
|
|
26
|
+
| -------------- | -------------------------- | ---------------------------- |
|
|
27
|
+
| `placeholder` | `string` | Texto placeholder |
|
|
28
|
+
| `value` | `string` | Valor controlado |
|
|
29
|
+
| `defaultValue` | `string` | Valor inicial (uncontrolled) |
|
|
30
|
+
| `onChange` | `(e: ChangeEvent) => void` | Callback al cambiar |
|
|
31
|
+
| `disabled` | `boolean` | Deshabilita el textarea |
|
|
32
|
+
| `readOnly` | `boolean` | Solo lectura |
|
|
33
|
+
| `required` | `boolean` | Campo requerido |
|
|
34
|
+
| `rows` | `number` | Número de filas |
|
|
35
|
+
| `maxLength` | `number` | Longitud máxima |
|
|
36
|
+
| `className` | `string` | Clases CSS adicionales |
|
|
37
|
+
|
|
38
|
+
**Nota**: Acepta todas las props HTML de `<textarea>`
|
|
39
|
+
|
|
40
|
+
## Patrones de Uso
|
|
41
|
+
|
|
42
|
+
### Básico
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
import { Textarea } from "@adamosuiteservices/ui/textarea";
|
|
46
|
+
|
|
47
|
+
<Textarea placeholder="Type your message here." />;
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Con Label
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
import { Label } from "@adamosuiteservices/ui/label";
|
|
54
|
+
|
|
55
|
+
<div className="grid w-full gap-2">
|
|
56
|
+
<Label htmlFor="message">Your message</Label>
|
|
57
|
+
<Textarea placeholder="Type your message here." id="message" />
|
|
58
|
+
</div>;
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Controlado con Contador
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
import { useState } from "react";
|
|
65
|
+
|
|
66
|
+
function App() {
|
|
67
|
+
const [value, setValue] = useState("");
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<div className="grid w-full gap-2">
|
|
71
|
+
<Label htmlFor="feedback">
|
|
72
|
+
Your feedback ({value.length}/500 characters)
|
|
73
|
+
</Label>
|
|
74
|
+
<Textarea
|
|
75
|
+
id="feedback"
|
|
76
|
+
placeholder="Share your thoughts..."
|
|
77
|
+
value={value}
|
|
78
|
+
onChange={(e) => setValue(e.target.value)}
|
|
79
|
+
maxLength={500}
|
|
80
|
+
/>
|
|
81
|
+
<div className="flex justify-between text-sm text-muted-foreground">
|
|
82
|
+
<span>{value.length} characters</span>
|
|
83
|
+
<span>{500 - value.length} remaining</span>
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Con Validación
|
|
91
|
+
|
|
92
|
+
```tsx
|
|
93
|
+
import { Button } from "@adamosuiteservices/ui/button";
|
|
94
|
+
import {
|
|
95
|
+
Card,
|
|
96
|
+
CardContent,
|
|
97
|
+
CardFooter,
|
|
98
|
+
CardHeader,
|
|
99
|
+
CardTitle,
|
|
100
|
+
} from "@adamosuiteservices/ui/card";
|
|
101
|
+
|
|
102
|
+
function CommentForm() {
|
|
103
|
+
const [comment, setComment] = useState("");
|
|
104
|
+
const [error, setError] = useState("");
|
|
105
|
+
|
|
106
|
+
const handleSubmit = () => {
|
|
107
|
+
if (comment.trim().length < 10) {
|
|
108
|
+
setError("Comment must be at least 10 characters long");
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
setError("");
|
|
112
|
+
// Submit logic
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<Card>
|
|
117
|
+
<CardHeader>
|
|
118
|
+
<CardTitle>Leave a Comment</CardTitle>
|
|
119
|
+
</CardHeader>
|
|
120
|
+
<CardContent className="space-y-2">
|
|
121
|
+
<Textarea
|
|
122
|
+
placeholder="What did you think?"
|
|
123
|
+
value={comment}
|
|
124
|
+
onChange={(e) => {
|
|
125
|
+
setComment(e.target.value);
|
|
126
|
+
if (error) setError("");
|
|
127
|
+
}}
|
|
128
|
+
aria-invalid={!!error}
|
|
129
|
+
rows={4}
|
|
130
|
+
/>
|
|
131
|
+
{error && <p className="text-destructive text-sm">{error}</p>}
|
|
132
|
+
</CardContent>
|
|
133
|
+
<CardFooter>
|
|
134
|
+
<Button onClick={handleSubmit}>Submit Comment</Button>
|
|
135
|
+
</CardFooter>
|
|
136
|
+
</Card>
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Chat Input con Enter
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
function ChatInput() {
|
|
145
|
+
const [message, setMessage] = useState("");
|
|
146
|
+
|
|
147
|
+
const handleSend = () => {
|
|
148
|
+
if (message.trim()) {
|
|
149
|
+
// Send message logic
|
|
150
|
+
setMessage("");
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|
155
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
156
|
+
e.preventDefault();
|
|
157
|
+
handleSend();
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
return (
|
|
162
|
+
<div className="relative">
|
|
163
|
+
<Textarea
|
|
164
|
+
placeholder="Type a message... (Enter to send, Shift+Enter for new line)"
|
|
165
|
+
value={message}
|
|
166
|
+
onChange={(e) => setMessage(e.target.value)}
|
|
167
|
+
onKeyDown={handleKeyDown}
|
|
168
|
+
rows={3}
|
|
169
|
+
className="pr-12 resize-none"
|
|
170
|
+
/>
|
|
171
|
+
<Button
|
|
172
|
+
size="sm"
|
|
173
|
+
className="absolute bottom-2 right-2"
|
|
174
|
+
onClick={handleSend}
|
|
175
|
+
>
|
|
176
|
+
Send
|
|
177
|
+
</Button>
|
|
178
|
+
</div>
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Resize Options
|
|
184
|
+
|
|
185
|
+
```tsx
|
|
186
|
+
// Auto-resize (default con field-sizing-content)
|
|
187
|
+
<Textarea placeholder="Auto-resize..." className="min-h-[80px] resize-none" />
|
|
188
|
+
|
|
189
|
+
// Manual resize (ambas direcciones)
|
|
190
|
+
<Textarea placeholder="Drag corner to resize..." className="min-h-[80px] resize-both" />
|
|
191
|
+
|
|
192
|
+
// Vertical resize only
|
|
193
|
+
<Textarea placeholder="Resize vertically..." className="min-h-[80px] resize-y" />
|
|
194
|
+
|
|
195
|
+
// No resize
|
|
196
|
+
<Textarea placeholder="Fixed size..." rows={4} className="resize-none" />
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Casos de Uso
|
|
200
|
+
|
|
201
|
+
**Comentarios**: Feedback, reviews, mensajes
|
|
202
|
+
**Formularios**: Descripción, bio, notas
|
|
203
|
+
**Chat**: Mensajes multilínea
|
|
204
|
+
**Code editor**: Snippets de código
|
|
205
|
+
**Feedback**: Formularios de opinión
|
|
206
|
+
|
|
207
|
+
## Estilos Base
|
|
208
|
+
|
|
209
|
+
- **Min height**: `min-h-16` default
|
|
210
|
+
- **Border**: `border-input` con `shadow-xs`
|
|
211
|
+
- **Focus**: `ring-ring/50` con `ring-[3px]`
|
|
212
|
+
- **Padding**: `px-3 py-2`
|
|
213
|
+
- **Font**: `text-base` en móvil, `text-sm` en md+
|
|
214
|
+
- **Field sizing**: `field-sizing-content` para auto-resize
|
|
215
|
+
- **Invalid**: `border-destructive` con `ring-destructive/20`
|
|
216
|
+
|
|
217
|
+
## Accesibilidad
|
|
218
|
+
|
|
219
|
+
- ✅ **Label**: Asociar con `htmlFor` e `id`
|
|
220
|
+
- ✅ **ARIA**: `aria-invalid` para errores
|
|
221
|
+
- ✅ **Placeholder**: No usar como label única
|
|
222
|
+
- ✅ **Error messages**: Describir errores claramente
|
|
223
|
+
- ✅ **Keyboard**: Enter, Tab, Shift+Enter funcionan nativamente
|
|
224
|
+
|
|
225
|
+
## Notas de Implementación
|
|
226
|
+
|
|
227
|
+
- **HTML nativo**: Wrapper de `<textarea>` HTML nativo
|
|
228
|
+
- **Field sizing**: `field-sizing-content` permite auto-resize basado en contenido
|
|
229
|
+
- **No external library**: Sin dependencias de Radix UI
|
|
230
|
+
- **Data attribute**: `data-slot="textarea"` para identificación
|
|
231
|
+
|
|
232
|
+
## Troubleshooting
|
|
233
|
+
|
|
234
|
+
**No auto-resize**: Verifica `field-sizing-content` en className (Chrome 123+)
|
|
235
|
+
**Resize manual no funciona**: Usa `resize-y`, `resize-both`, o remueve `resize-none`
|
|
236
|
+
**Height fijo**: Usa prop `rows` para altura específica
|
|
237
|
+
**Contador de caracteres**: Usa `value.length` y `maxLength`
|
|
238
|
+
**Enter no funciona en chat**: Usa `onKeyDown` con `e.key === "Enter" && !e.shiftKey`
|
|
239
|
+
|
|
240
|
+
## Referencias
|
|
241
|
+
|
|
242
|
+
- **MDN textarea**: <https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea>
|
|
243
|
+
- **shadcn/ui Textarea**: <https://ui.shadcn.com/docs/components/textarea>
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# Toggle
|
|
2
|
+
|
|
3
|
+
Botón de dos estados (on/off). Basado en Radix UI con variantes CVA.
|
|
4
|
+
|
|
5
|
+
## Descripción
|
|
6
|
+
|
|
7
|
+
El componente `Toggle` es un botón de dos estados (presionado/no presionado).
|
|
8
|
+
|
|
9
|
+
## Importación
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
import { Toggle } from "@adamosuiteservices/ui/toggle";
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Anatomía
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
<Toggle aria-label="Toggle italic">
|
|
19
|
+
<ItalicIcon />
|
|
20
|
+
</Toggle>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**Componentes**: 1 (Toggle)
|
|
24
|
+
|
|
25
|
+
## Props
|
|
26
|
+
|
|
27
|
+
| Prop | Tipo | Descripción |
|
|
28
|
+
| ----------------- | ---------------------------- | ----------------------------- |
|
|
29
|
+
| `defaultPressed` | `boolean` | Estado inicial (uncontrolled) |
|
|
30
|
+
| `pressed` | `boolean` | Estado controlado |
|
|
31
|
+
| `onPressedChange` | `(pressed: boolean) => void` | Callback al cambiar |
|
|
32
|
+
| `disabled` | `boolean` | Deshabilita el toggle |
|
|
33
|
+
| `variant` | `"default" \| "outline"` | Estilo visual |
|
|
34
|
+
| `size` | `"sm" \| "default" \| "lg"` | Tamaño del toggle |
|
|
35
|
+
| `className` | `string` | Clases CSS adicionales |
|
|
36
|
+
|
|
37
|
+
## Patrones de Uso
|
|
38
|
+
|
|
39
|
+
### Básico
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
import { Toggle } from "@adamosuiteservices/ui/toggle";
|
|
43
|
+
import { ItalicIcon } from "lucide-react";
|
|
44
|
+
|
|
45
|
+
<Toggle aria-label="Toggle italic">
|
|
46
|
+
<ItalicIcon />
|
|
47
|
+
</Toggle>;
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Con Texto
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
<Toggle aria-label="Toggle bold">
|
|
54
|
+
<BoldIcon />
|
|
55
|
+
Bold
|
|
56
|
+
</Toggle>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Variantes
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
// Default (transparent background)
|
|
63
|
+
<Toggle aria-label="Default">
|
|
64
|
+
<ItalicIcon />
|
|
65
|
+
</Toggle>
|
|
66
|
+
|
|
67
|
+
// Outline (con border)
|
|
68
|
+
<Toggle variant="outline" aria-label="Outline">
|
|
69
|
+
<ItalicIcon />
|
|
70
|
+
</Toggle>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Tamaños
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
import { UnderlineIcon } from "lucide-react";
|
|
77
|
+
|
|
78
|
+
<Toggle size="sm"><UnderlineIcon /></Toggle>
|
|
79
|
+
<Toggle size="default"><UnderlineIcon /></Toggle>
|
|
80
|
+
<Toggle size="lg"><UnderlineIcon /></Toggle>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Controlado
|
|
84
|
+
|
|
85
|
+
```tsx
|
|
86
|
+
import { useState } from "react";
|
|
87
|
+
|
|
88
|
+
function App() {
|
|
89
|
+
const [isPressed, setIsPressed] = useState(false);
|
|
90
|
+
|
|
91
|
+
return (
|
|
92
|
+
<Toggle
|
|
93
|
+
pressed={isPressed}
|
|
94
|
+
onPressedChange={setIsPressed}
|
|
95
|
+
aria-label="Toggle bold"
|
|
96
|
+
>
|
|
97
|
+
<BoldIcon />
|
|
98
|
+
{isPressed ? "Bold (On)" : "Bold (Off)"}
|
|
99
|
+
</Toggle>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Text Editor Formatting
|
|
105
|
+
|
|
106
|
+
```tsx
|
|
107
|
+
import { BoldIcon, ItalicIcon, UnderlineIcon } from "lucide-react";
|
|
108
|
+
|
|
109
|
+
function TextEditor() {
|
|
110
|
+
const [formatting, setFormatting] = useState({
|
|
111
|
+
bold: false,
|
|
112
|
+
italic: false,
|
|
113
|
+
underline: false,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
return (
|
|
117
|
+
<div className="flex gap-1">
|
|
118
|
+
<Toggle
|
|
119
|
+
pressed={formatting.bold}
|
|
120
|
+
onPressedChange={(pressed) =>
|
|
121
|
+
setFormatting((prev) => ({ ...prev, bold: pressed }))
|
|
122
|
+
}
|
|
123
|
+
size="sm"
|
|
124
|
+
>
|
|
125
|
+
<BoldIcon />
|
|
126
|
+
</Toggle>
|
|
127
|
+
<Toggle
|
|
128
|
+
pressed={formatting.italic}
|
|
129
|
+
onPressedChange={(pressed) =>
|
|
130
|
+
setFormatting((prev) => ({ ...prev, italic: pressed }))
|
|
131
|
+
}
|
|
132
|
+
size="sm"
|
|
133
|
+
>
|
|
134
|
+
<ItalicIcon />
|
|
135
|
+
</Toggle>
|
|
136
|
+
<Toggle
|
|
137
|
+
pressed={formatting.underline}
|
|
138
|
+
onPressedChange={(pressed) =>
|
|
139
|
+
setFormatting((prev) => ({ ...prev, underline: pressed }))
|
|
140
|
+
}
|
|
141
|
+
size="sm"
|
|
142
|
+
>
|
|
143
|
+
<UnderlineIcon />
|
|
144
|
+
</Toggle>
|
|
145
|
+
</div>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Media Controls
|
|
151
|
+
|
|
152
|
+
```tsx
|
|
153
|
+
import { MicIcon, MicOffIcon, VideoIcon, VideoOffIcon } from "lucide-react";
|
|
154
|
+
|
|
155
|
+
function MediaControls() {
|
|
156
|
+
const [media, setMedia] = useState({
|
|
157
|
+
microphone: false,
|
|
158
|
+
camera: false,
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
return (
|
|
162
|
+
<div className="flex gap-3">
|
|
163
|
+
<Toggle
|
|
164
|
+
pressed={media.microphone}
|
|
165
|
+
onPressedChange={(pressed) =>
|
|
166
|
+
setMedia((prev) => ({ ...prev, microphone: pressed }))
|
|
167
|
+
}
|
|
168
|
+
variant="outline"
|
|
169
|
+
className={
|
|
170
|
+
!media.microphone ? "bg-destructive text-destructive-foreground" : ""
|
|
171
|
+
}
|
|
172
|
+
>
|
|
173
|
+
{media.microphone ? <MicIcon /> : <MicOffIcon />}
|
|
174
|
+
</Toggle>
|
|
175
|
+
|
|
176
|
+
<Toggle
|
|
177
|
+
pressed={media.camera}
|
|
178
|
+
onPressedChange={(pressed) =>
|
|
179
|
+
setMedia((prev) => ({ ...prev, camera: pressed }))
|
|
180
|
+
}
|
|
181
|
+
variant="outline"
|
|
182
|
+
className={
|
|
183
|
+
!media.camera ? "bg-destructive text-destructive-foreground" : ""
|
|
184
|
+
}
|
|
185
|
+
>
|
|
186
|
+
{media.camera ? <VideoIcon /> : <VideoOffIcon />}
|
|
187
|
+
</Toggle>
|
|
188
|
+
</div>
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Deshabilitado
|
|
194
|
+
|
|
195
|
+
```tsx
|
|
196
|
+
<Toggle disabled><UnderlineIcon /></Toggle>
|
|
197
|
+
<Toggle disabled defaultPressed><UnderlineIcon /></Toggle>
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Casos de Uso
|
|
201
|
+
|
|
202
|
+
**Text formatting**: Bold, italic, underline toggles
|
|
203
|
+
**Media controls**: Mic/camera on/off
|
|
204
|
+
**View options**: List/grid view toggle
|
|
205
|
+
**Preferences**: Dark mode, notifications
|
|
206
|
+
**Filters**: Show/hide options
|
|
207
|
+
|
|
208
|
+
## Estilos Base
|
|
209
|
+
|
|
210
|
+
- **Sizes**: `h-8 min-w-8` (sm), `h-9 min-w-9` (default), `h-10 min-w-10` (lg)
|
|
211
|
+
- **State off**: `bg-transparent`
|
|
212
|
+
- **State on**: `bg-accent text-accent-foreground`
|
|
213
|
+
- **Hover**: `bg-muted text-muted-foreground`
|
|
214
|
+
- **Outline**: `border border-input shadow-xs`
|
|
215
|
+
- **Focus**: `ring-ring/50` con `ring-[3px]`
|
|
216
|
+
|
|
217
|
+
## Accesibilidad
|
|
218
|
+
|
|
219
|
+
- ✅ **Role**: `role="button"` con `aria-pressed`
|
|
220
|
+
- ✅ **ARIA**: `aria-label` requerido para icon-only
|
|
221
|
+
- ✅ **Keyboard**: Space/Enter para toggle
|
|
222
|
+
- ✅ **State**: Screen readers anuncian pressed/not pressed
|
|
223
|
+
- ✅ **Focus**: Focus ring visible
|
|
224
|
+
|
|
225
|
+
## Notas de Implementación
|
|
226
|
+
|
|
227
|
+
- **Radix UI**: Basado en `@radix-ui/react-toggle`
|
|
228
|
+
- **CVA**: Variantes con class-variance-authority
|
|
229
|
+
- **Data state**: `data-state="on|off"` para estilos
|
|
230
|
+
- **Controlled**: Usa `pressed` + `onPressedChange`
|
|
231
|
+
- **Uncontrolled**: Usa `defaultPressed`
|
|
232
|
+
|
|
233
|
+
## Troubleshooting
|
|
234
|
+
|
|
235
|
+
**Estado no cambia**: En modo controlado usa `pressed` + `onPressedChange`, no `defaultPressed`
|
|
236
|
+
**No accesible**: Agrega `aria-label` en toggles solo con iconos
|
|
237
|
+
**Estilo no actualiza**: Verifica `data-state="on"` aplicado cuando pressed
|
|
238
|
+
**Variant no funciona**: Usa `variant="outline"` o `variant="default"`
|
|
239
|
+
|
|
240
|
+
## Referencias
|
|
241
|
+
|
|
242
|
+
- **Radix UI Toggle**: <https://www.radix-ui.com/primitives/docs/components/toggle>
|
|
243
|
+
- **shadcn/ui Toggle**: <https://ui.shadcn.com/docs/components/toggle>
|