quickit-ui 0.1.16 → 0.1.19

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 CHANGED
@@ -14,6 +14,17 @@ Importa los estilos una sola vez:
14
14
  import "quickit-ui/styles.css";
15
15
  ```
16
16
 
17
+ Si tu app también usa utilidades propias de Tailwind con `dark:`, declara esto en tu CSS global:
18
+
19
+ ```css
20
+ @import "tailwindcss";
21
+ @import "quickit-ui/styles.css";
22
+
23
+ @custom-variant dark (&:where(.dark, .dark *));
24
+ ```
25
+
26
+ Eso hace que tu app y Quickit reaccionen a la misma clase `dark`.
27
+
17
28
  ## Uso rápido
18
29
 
19
30
  ```jsx
@@ -34,7 +45,7 @@ export default function App() {
34
45
 
35
46
  ## Tema
36
47
 
37
- Quickit UI trabaja con `light` y `dark`. El estado del tema vive en tu app; `QuickitProvider` solo lo distribuye al resto de componentes.
48
+ Quickit UI trabaja con `light` y `dark`. Si ya gestionas el tema en tu app, `QuickitProvider` solo lo distribuye al resto de componentes.
38
49
 
39
50
  ```jsx
40
51
  import "quickit-ui/styles.css";
@@ -49,12 +60,122 @@ export default function App() {
49
60
  }
50
61
  ```
51
62
 
52
- También puedes controlar el focus ring globalmente desde el provider:
63
+ Si prefieres que Quickit gestione el toggle y la persistencia, usa `QuickitThemeProvider` con `useQuickitThemeController`:
64
+
65
+ ```jsx
66
+ import "quickit-ui/styles.css";
67
+ import {
68
+ Button,
69
+ QuickitThemeProvider,
70
+ useQuickitThemeController,
71
+ } from "quickit-ui";
72
+
73
+ function ThemeControls() {
74
+ const { resolvedTheme, setTheme, systemTheme, theme, toggleTheme } =
75
+ useQuickitThemeController();
76
+
77
+ return (
78
+ <div className="space-y-4">
79
+ <p>
80
+ Preferencia: {theme}. Sistema: {systemTheme}. Tema activo: {resolvedTheme}
81
+ </p>
82
+
83
+ <div className="flex flex-wrap gap-3">
84
+ <Button onClick={() => setTheme("system")}>Sistema</Button>
85
+ <Button onClick={() => setTheme("light")}>Claro</Button>
86
+ <Button onClick={() => setTheme("dark")}>Oscuro</Button>
87
+ <Button color="brand" variant="outline" onClick={toggleTheme}>
88
+ Alternar desde {resolvedTheme}
89
+ </Button>
90
+ </div>
91
+ </div>
92
+ );
93
+ }
94
+
95
+ export default function App() {
96
+ return (
97
+ <QuickitThemeProvider
98
+ defaultTheme="system"
99
+ storageKey="ava-quickit-theme"
100
+ >
101
+ <ThemeControls />
102
+ </QuickitThemeProvider>
103
+ );
104
+ }
105
+ ```
106
+
107
+ Notas:
108
+
109
+ - el storage key por defecto es `quickit-ui-theme`
110
+ - `defaultTheme` ahora soporta `system | light | dark`
111
+ - `QuickitThemeProvider` aplica la clase `dark` sobre `document.documentElement`
112
+ - puedes sobrescribir el storage key con `storageKey`
113
+ - `useQuickitThemeController()` expone `theme`, `resolvedTheme`, `systemTheme`, `setTheme` y `toggleTheme`
114
+ - `theme` es la preferencia persistida; `resolvedTheme` es el modo que Quickit está aplicando realmente
115
+ - si usas clases propias como `dark:bg-zinc-950`, añade `@custom-variant dark (&:where(.dark, .dark *));` a tu CSS global
116
+
117
+ Si tu layout propio también depende del tema:
118
+
119
+ ```jsx
120
+ import { useQuickitThemeController } from "quickit-ui";
121
+
122
+ function Shell() {
123
+ const { theme, resolvedTheme } = useQuickitThemeController();
124
+
125
+ return (
126
+ <div className="bg-white text-zinc-950 dark:bg-zinc-950 dark:text-white">
127
+ <header className="border-b border-zinc-200 px-6 py-4 dark:border-zinc-800">
128
+ Preferencia: {theme}. Tema efectivo: {resolvedTheme}
129
+ </header>
130
+ <main className="p-6">
131
+ <Dashboard />
132
+ </main>
133
+ </div>
134
+ );
135
+ }
136
+ ```
137
+
138
+ Patrón con `Switch`:
139
+
140
+ ```jsx
141
+ import {
142
+ QuickitThemeProvider,
143
+ Switch,
144
+ Tooltip,
145
+ useQuickitThemeController,
146
+ } from "quickit-ui";
147
+
148
+ function ToggleTheme() {
149
+ const { resolvedTheme, theme, toggleTheme } = useQuickitThemeController();
150
+
151
+ return (
152
+ <Tooltip content="Alternar tema">
153
+ <Switch
154
+ color="brand"
155
+ checked={resolvedTheme === "dark"}
156
+ onCheckedChange={toggleTheme}
157
+ />
158
+ </Tooltip>
159
+ );
160
+ }
161
+
162
+ export function App() {
163
+ return (
164
+ <QuickitThemeProvider storageKey="ava-quickit-theme">
165
+ <ToggleTheme />
166
+ </QuickitThemeProvider>
167
+ );
168
+ }
169
+ ```
170
+
171
+ También puedes controlar el focus ring y el efecto de presión globalmente desde el provider:
53
172
 
54
173
  ```jsx
55
174
  <QuickitProvider
56
175
  theme="dark"
57
176
  focusRing={{ disabledComponents: ["input", "textarea"] }}
177
+ pressEffect="ripple"
178
+ ripple={{ disabledComponents: ["link"] }}
58
179
  >
59
180
  <App />
60
181
  </QuickitProvider>
@@ -65,16 +186,70 @@ Reglas:
65
186
  - por defecto Quickit mantiene focus visible accesible en componentes interactivos
66
187
  - `focusRing={false}` lo desactiva en toda la librería
67
188
  - `focusRing={{ disabledComponents: [...] }}` lo desactiva solo en componentes específicos
189
+ - por defecto Quickit usa `pressEffect="transform"` en `Button` y en `Link` con `appearance="button"`
190
+ - `pressEffect="ripple"` cambia esa política global para usar ripple en lugar de transform
191
+ - `ripple={false}` lo desactiva en toda la librería cuando `pressEffect="ripple"`
192
+ - `ripple={{ disabledComponents: [...] }}` lo desactiva solo en botones o links cuando `pressEffect="ripple"`
68
193
 
69
194
  Si necesitas leer esa decisión desde tu app o desde wrappers propios:
70
195
 
71
196
  ```jsx
72
- import { useQuickitFocusRing } from "quickit-ui";
197
+ import {
198
+ useQuickitFocusRing,
199
+ useQuickitPressEffect,
200
+ useQuickitRipple,
201
+ } from "quickit-ui";
73
202
 
74
203
  function Toolbar() {
75
204
  const buttonFocusRing = useQuickitFocusRing("button");
205
+ const linkFocusRing = useQuickitFocusRing("link");
206
+ const checkboxFocusRing = useQuickitFocusRing("checkbox");
207
+ const radioFocusRing = useQuickitFocusRing("radio");
208
+ const pressEffect = useQuickitPressEffect();
209
+ const buttonRipple = useQuickitRipple("button");
210
+ const linkRipple = useQuickitRipple("link");
211
+
212
+ return (
213
+ <div>
214
+ <span>button focus: {String(buttonFocusRing)}</span>
215
+ <span>link focus: {String(linkFocusRing)}</span>
216
+ <span>checkbox focus: {String(checkboxFocusRing)}</span>
217
+ <span>radio focus: {String(radioFocusRing)}</span>
218
+ <span>pressEffect: {pressEffect}</span>
219
+ <span>button ripple: {String(buttonRipple)}</span>
220
+ <span>link ripple: {String(linkRipple)}</span>
221
+ </div>
222
+ );
223
+ }
224
+ ```
225
+
226
+ Caso real:
76
227
 
77
- return <span>button focus: {String(buttonFocusRing)}</span>;
228
+ ```jsx
229
+ import {
230
+ Button,
231
+ Checkbox,
232
+ Link,
233
+ QuickitProvider,
234
+ Radio,
235
+ } from "quickit-ui";
236
+
237
+ export function LoginOptions() {
238
+ return (
239
+ <QuickitProvider
240
+ pressEffect="ripple"
241
+ focusRing={{ disabledComponents: ["link", "checkbox", "radio"] }}
242
+ >
243
+ <div className="flex flex-wrap items-center gap-4">
244
+ <Link href="#">Ver términos</Link>
245
+ <Checkbox label="Recordarme" defaultChecked />
246
+ <Radio name="login-mode" label="Modo manual" defaultChecked />
247
+ <Button color="neutral" variant="outline">
248
+ Continuar
249
+ </Button>
250
+ </div>
251
+ </QuickitProvider>
252
+ );
78
253
  }
79
254
  ```
80
255
 
@@ -147,6 +322,33 @@ import {
147
322
  } from "quickit-ui";
148
323
  ```
149
324
 
325
+ ## InputGroup
326
+
327
+ Usa `InputGroupAddon` para segmentos pasivos y `InputGroupAction` para segmentos interactivos.
328
+ `InputGroupAction` reutiliza `Button` y renderiza un `<button>` real, así que soporta `type`, `onClick`, `disabled`, foco y teclado.
329
+
330
+ ```jsx
331
+ import {
332
+ Input,
333
+ InputGroup,
334
+ InputGroupAction,
335
+ } from "quickit-ui";
336
+
337
+ export function Filters() {
338
+ return (
339
+ <InputGroup attached>
340
+ <InputGroupAction variant="outline" onClick={() => console.log("todo")}>
341
+ Todo
342
+ </InputGroupAction>
343
+ <Input placeholder="Filtra por nombre o etiqueta" />
344
+ <InputGroupAction variant="outline" onClick={() => console.log("estado")}>
345
+ Estado
346
+ </InputGroupAction>
347
+ </InputGroup>
348
+ );
349
+ }
350
+ ```
351
+
150
352
  ## Ejemplos
151
353
 
152
354
  ### Button
@@ -173,6 +375,9 @@ Notas rápidas de `Button`:
173
375
 
174
376
  - `shape="square"` y `shape="circle"` están pensados para icon buttons.
175
377
  - `shape="square"` y `shape="circle"` salen con `activeMotion` desactivado por defecto.
378
+ - `Button` y `Link` con `appearance="button"` usan `pressEffect="transform"` por defecto.
379
+ - Si quieres ripple, usa `pressEffect="ripple"` en esa instancia o en `QuickitProvider`.
380
+ - Cuando `pressEffect="ripple"`, puedes apagarlo con `ripple={false}`.
176
381
  - Si quieres esa animación en un icon button, usa `activeMotion={true}`.
177
382
 
178
383
  ```jsx