@assembleo/ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +89 -0
- package/dist/index.d.ts +676 -0
- package/dist/index.js +1594 -0
- package/dist/theme.css +538 -0
- package/package.json +89 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1594 @@
|
|
|
1
|
+
import { cva, cx } from 'class-variance-authority';
|
|
2
|
+
import { Dialog as Dialog$1, Popover as Popover$1, Tabs as Tabs$1, Avatar as Avatar$1, Checkbox as Checkbox$1, Progress as Progress$1, RadioGroup as RadioGroup$1, ToggleGroup, Select as Select$1, Separator as Separator$1, Switch as Switch$1, Toast, Tooltip as Tooltip$1 } from 'radix-ui';
|
|
3
|
+
import { createContext, Children, useContext, useId, useRef, useState, useCallback, useMemo, useEffect } from 'react';
|
|
4
|
+
import { extendTailwindMerge } from 'tailwind-merge';
|
|
5
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
6
|
+
import { CircleAlert, TriangleAlert, CircleCheck, Info, X, Check, Minus, ChevronRight, Search, ChevronsUpDown } from 'lucide-react';
|
|
7
|
+
|
|
8
|
+
// src/components/avatar/avatar.tsx
|
|
9
|
+
var fontSizes = [
|
|
10
|
+
"display",
|
|
11
|
+
"title-1",
|
|
12
|
+
"title-2",
|
|
13
|
+
"title-3",
|
|
14
|
+
"headline",
|
|
15
|
+
"body",
|
|
16
|
+
"callout",
|
|
17
|
+
"subhead",
|
|
18
|
+
"footnote",
|
|
19
|
+
"caption"
|
|
20
|
+
];
|
|
21
|
+
var twMergeAssembleo = extendTailwindMerge({
|
|
22
|
+
extend: {
|
|
23
|
+
classGroups: {
|
|
24
|
+
"font-size": fontSizes.map((size) => `text-${size}`)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
function cn(...classes) {
|
|
29
|
+
return twMergeAssembleo(cx(classes));
|
|
30
|
+
}
|
|
31
|
+
var avatar = cva(
|
|
32
|
+
"relative inline-flex shrink-0 select-none items-center justify-center overflow-hidden rounded-full",
|
|
33
|
+
{
|
|
34
|
+
variants: {
|
|
35
|
+
size: {
|
|
36
|
+
sm: "size-7",
|
|
37
|
+
md: "size-9",
|
|
38
|
+
lg: "size-touch",
|
|
39
|
+
xl: "size-16"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
defaultVariants: {
|
|
43
|
+
size: "md"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
var initialsSizeClasses = {
|
|
48
|
+
sm: "text-caption",
|
|
49
|
+
md: "text-footnote",
|
|
50
|
+
lg: "text-callout",
|
|
51
|
+
xl: "text-title-3"
|
|
52
|
+
};
|
|
53
|
+
var expressiveBackgrounds = [
|
|
54
|
+
"bg-terracota-soft",
|
|
55
|
+
"bg-jardim-soft",
|
|
56
|
+
"bg-ambar-soft",
|
|
57
|
+
"bg-lavanda-soft",
|
|
58
|
+
"bg-ceu-soft"
|
|
59
|
+
];
|
|
60
|
+
function backgroundFor(name) {
|
|
61
|
+
let sum = 0;
|
|
62
|
+
for (const char of name) {
|
|
63
|
+
sum += char.codePointAt(0) ?? 0;
|
|
64
|
+
}
|
|
65
|
+
return expressiveBackgrounds[sum % expressiveBackgrounds.length] ?? "bg-terracota-soft";
|
|
66
|
+
}
|
|
67
|
+
function initialsFor(name) {
|
|
68
|
+
return name.trim().split(/\s+/).slice(0, 2).map((word) => word[0] ?? "").join("").toUpperCase();
|
|
69
|
+
}
|
|
70
|
+
function Avatar({ name, src, size, className, ...props }) {
|
|
71
|
+
return (
|
|
72
|
+
/*
|
|
73
|
+
* O nome acessível mora no Root: com ou sem foto carregada, o leitor de
|
|
74
|
+
* tela anuncia a pessoa uma vez só — a foto e as iniciais são decorativas.
|
|
75
|
+
*/
|
|
76
|
+
/* @__PURE__ */ jsxs(
|
|
77
|
+
Avatar$1.Root,
|
|
78
|
+
{
|
|
79
|
+
role: "img",
|
|
80
|
+
"aria-label": name,
|
|
81
|
+
className: cn(avatar({ size }), className),
|
|
82
|
+
...props,
|
|
83
|
+
children: [
|
|
84
|
+
src ? /* @__PURE__ */ jsx(Avatar$1.Image, { src, alt: "", className: "size-full object-cover" }) : null,
|
|
85
|
+
/* @__PURE__ */ jsx(
|
|
86
|
+
Avatar$1.Fallback,
|
|
87
|
+
{
|
|
88
|
+
"aria-hidden": "true",
|
|
89
|
+
className: cn(
|
|
90
|
+
"flex size-full items-center justify-center font-friendly font-semibold text-text",
|
|
91
|
+
initialsSizeClasses[size ?? "md"],
|
|
92
|
+
backgroundFor(name)
|
|
93
|
+
),
|
|
94
|
+
children: initialsFor(name)
|
|
95
|
+
}
|
|
96
|
+
)
|
|
97
|
+
]
|
|
98
|
+
}
|
|
99
|
+
)
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
function AvatarGroup({ max, size, className, children, ...props }) {
|
|
103
|
+
const items = Children.toArray(children);
|
|
104
|
+
const visible = max != null && max >= 0 ? items.slice(0, max) : items;
|
|
105
|
+
const excess = items.length - visible.length;
|
|
106
|
+
return (
|
|
107
|
+
/*
|
|
108
|
+
* A sobreposição conta a cena — "estas pessoas juntas" — e o anel na cor
|
|
109
|
+
* da superfície recorta cada rosto contra o vizinho de trás.
|
|
110
|
+
*/
|
|
111
|
+
/* @__PURE__ */ jsxs(
|
|
112
|
+
"div",
|
|
113
|
+
{
|
|
114
|
+
className: cn(
|
|
115
|
+
"flex items-center -space-x-2 *:rounded-full *:ring-2 *:ring-surface",
|
|
116
|
+
className
|
|
117
|
+
),
|
|
118
|
+
...props,
|
|
119
|
+
children: [
|
|
120
|
+
visible,
|
|
121
|
+
excess > 0 ? /* @__PURE__ */ jsxs(
|
|
122
|
+
"span",
|
|
123
|
+
{
|
|
124
|
+
className: cn(
|
|
125
|
+
avatar({ size }),
|
|
126
|
+
"bg-surface-sunken text-caption font-semibold text-text-secondary"
|
|
127
|
+
),
|
|
128
|
+
children: [
|
|
129
|
+
"+",
|
|
130
|
+
excess
|
|
131
|
+
]
|
|
132
|
+
}
|
|
133
|
+
) : null
|
|
134
|
+
]
|
|
135
|
+
}
|
|
136
|
+
)
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
var badge = cva(
|
|
140
|
+
"inline-flex items-center gap-1 rounded-pill px-2.5 py-0.5 text-caption font-semibold",
|
|
141
|
+
{
|
|
142
|
+
variants: {
|
|
143
|
+
tone: {
|
|
144
|
+
neutral: "bg-surface-sunken text-text-secondary",
|
|
145
|
+
accent: "bg-accent-soft text-accent-soft-text",
|
|
146
|
+
success: "bg-success-soft text-success-soft-text",
|
|
147
|
+
warning: "bg-warning-soft text-warning-soft-text",
|
|
148
|
+
danger: "bg-danger-soft text-danger-soft-text",
|
|
149
|
+
/*
|
|
150
|
+
* A vila não tem par `-soft-text`: o texto fica na voz secundária —
|
|
151
|
+
* contraste AA garantido pelo próprio tema — e a identidade da cor
|
|
152
|
+
* vem do fundo suave com a bolinha forte, não do texto.
|
|
153
|
+
*/
|
|
154
|
+
terracota: "bg-terracota-soft text-text-secondary",
|
|
155
|
+
jardim: "bg-jardim-soft text-text-secondary",
|
|
156
|
+
ambar: "bg-ambar-soft text-text-secondary",
|
|
157
|
+
lavanda: "bg-lavanda-soft text-text-secondary",
|
|
158
|
+
ceu: "bg-ceu-soft text-text-secondary"
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
defaultVariants: {
|
|
162
|
+
tone: "neutral"
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
);
|
|
166
|
+
var dotToneClasses = {
|
|
167
|
+
neutral: "bg-text-muted",
|
|
168
|
+
accent: "bg-accent",
|
|
169
|
+
success: "bg-success",
|
|
170
|
+
warning: "bg-warning",
|
|
171
|
+
danger: "bg-danger",
|
|
172
|
+
terracota: "bg-terracota",
|
|
173
|
+
jardim: "bg-jardim",
|
|
174
|
+
ambar: "bg-ambar",
|
|
175
|
+
lavanda: "bg-lavanda",
|
|
176
|
+
ceu: "bg-ceu"
|
|
177
|
+
};
|
|
178
|
+
function Badge({ tone, dot = false, className, children, ...props }) {
|
|
179
|
+
return /* @__PURE__ */ jsxs("span", { className: cn(badge({ tone }), className), ...props, children: [
|
|
180
|
+
dot ? /* @__PURE__ */ jsx(
|
|
181
|
+
"span",
|
|
182
|
+
{
|
|
183
|
+
"aria-hidden": "true",
|
|
184
|
+
className: cn("size-1.5 shrink-0 rounded-full", dotToneClasses[tone ?? "neutral"])
|
|
185
|
+
}
|
|
186
|
+
) : null,
|
|
187
|
+
children
|
|
188
|
+
] });
|
|
189
|
+
}
|
|
190
|
+
var banner = cva("flex gap-3 rounded-surface squircle p-4 text-subhead", {
|
|
191
|
+
variants: {
|
|
192
|
+
/* Sempre o par soft/soft-text: aviso é fundo calmo, nunca bloco gritado. */
|
|
193
|
+
tone: {
|
|
194
|
+
info: "bg-accent-soft text-accent-soft-text",
|
|
195
|
+
success: "bg-success-soft text-success-soft-text",
|
|
196
|
+
warning: "bg-warning-soft text-warning-soft-text",
|
|
197
|
+
danger: "bg-danger-soft text-danger-soft-text"
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
defaultVariants: {
|
|
201
|
+
tone: "info"
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
var toneIcon = {
|
|
205
|
+
info: Info,
|
|
206
|
+
success: CircleCheck,
|
|
207
|
+
warning: TriangleAlert,
|
|
208
|
+
danger: CircleAlert
|
|
209
|
+
};
|
|
210
|
+
function Banner({ tone, title, onDismiss, className, children, ...props }) {
|
|
211
|
+
const Icon = toneIcon[tone ?? "info"];
|
|
212
|
+
return /* @__PURE__ */ jsxs(
|
|
213
|
+
"div",
|
|
214
|
+
{
|
|
215
|
+
role: tone === "danger" ? "alert" : "status",
|
|
216
|
+
className: cn(banner({ tone }), className),
|
|
217
|
+
...props,
|
|
218
|
+
children: [
|
|
219
|
+
/* @__PURE__ */ jsx(Icon, { "aria-hidden": "true", className: "mt-0.5 size-5 shrink-0" }),
|
|
220
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
|
|
221
|
+
title ? /* @__PURE__ */ jsx("p", { className: "font-semibold", children: title }) : null,
|
|
222
|
+
children
|
|
223
|
+
] }),
|
|
224
|
+
onDismiss ? /* @__PURE__ */ jsx(
|
|
225
|
+
"button",
|
|
226
|
+
{
|
|
227
|
+
type: "button",
|
|
228
|
+
"aria-label": "Dispensar aviso",
|
|
229
|
+
onClick: onDismiss,
|
|
230
|
+
className: cn(
|
|
231
|
+
"relative -my-1 -mr-1 grid size-8 shrink-0 place-items-center self-start rounded-control-sm",
|
|
232
|
+
"opacity-70 transition-opacity duration-(--motion-quick) ease-(--ease-out-soft) hover:opacity-100",
|
|
233
|
+
// O X herda o tom do banner e ganha 44px de alvo via pseudo-elemento.
|
|
234
|
+
"before:absolute before:-inset-1.5"
|
|
235
|
+
),
|
|
236
|
+
children: /* @__PURE__ */ jsx(X, { "aria-hidden": "true", className: "size-4" })
|
|
237
|
+
}
|
|
238
|
+
) : null
|
|
239
|
+
]
|
|
240
|
+
}
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
var spinner = cva("inline-block animate-spin", {
|
|
244
|
+
variants: {
|
|
245
|
+
size: {
|
|
246
|
+
sm: "size-4",
|
|
247
|
+
md: "size-5",
|
|
248
|
+
lg: "size-7"
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
defaultVariants: {
|
|
252
|
+
size: "md"
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
function Spinner({ size, label = "Carregando", className }) {
|
|
256
|
+
return /* @__PURE__ */ jsxs("span", { role: "status", className: cn(spinner({ size }), className), children: [
|
|
257
|
+
/* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", className: "size-full", children: [
|
|
258
|
+
/* @__PURE__ */ jsx(
|
|
259
|
+
"circle",
|
|
260
|
+
{
|
|
261
|
+
cx: "12",
|
|
262
|
+
cy: "12",
|
|
263
|
+
r: "10",
|
|
264
|
+
stroke: "currentColor",
|
|
265
|
+
strokeWidth: "3",
|
|
266
|
+
className: "opacity-25"
|
|
267
|
+
}
|
|
268
|
+
),
|
|
269
|
+
/* @__PURE__ */ jsx(
|
|
270
|
+
"path",
|
|
271
|
+
{
|
|
272
|
+
d: "M22 12a10 10 0 0 0-10-10",
|
|
273
|
+
stroke: "currentColor",
|
|
274
|
+
strokeWidth: "3",
|
|
275
|
+
strokeLinecap: "round"
|
|
276
|
+
}
|
|
277
|
+
)
|
|
278
|
+
] }),
|
|
279
|
+
label ? /* @__PURE__ */ jsx("span", { className: "sr-only", children: label }) : null
|
|
280
|
+
] });
|
|
281
|
+
}
|
|
282
|
+
var button = cva(
|
|
283
|
+
[
|
|
284
|
+
"relative inline-flex items-center justify-center gap-2",
|
|
285
|
+
"rounded-control squircle select-none",
|
|
286
|
+
"transition-[transform,background-color,border-color,opacity,box-shadow]",
|
|
287
|
+
"duration-(--motion-quick) ease-(--ease-out-soft)",
|
|
288
|
+
// O aperto: encolher um fio no active é o feedback tátil da Apple.
|
|
289
|
+
"active:scale-[0.97]",
|
|
290
|
+
"disabled:pointer-events-none disabled:opacity-50"
|
|
291
|
+
],
|
|
292
|
+
{
|
|
293
|
+
variants: {
|
|
294
|
+
variant: {
|
|
295
|
+
primary: "bg-accent text-accent-text hover:bg-accent-hover active:bg-accent-active",
|
|
296
|
+
/* O botão "tingido" do iOS: presença sem gritar. */
|
|
297
|
+
tonal: "bg-accent-soft text-accent-soft-text hover:brightness-97 dark:hover:brightness-110",
|
|
298
|
+
secondary: "bg-surface text-text border border-border-strong hover:bg-surface-sunken",
|
|
299
|
+
ghost: "bg-transparent text-accent-soft-text hover:bg-accent-soft",
|
|
300
|
+
danger: "bg-danger text-danger-text hover:brightness-110",
|
|
301
|
+
"danger-tonal": "bg-danger-soft text-danger-soft-text hover:brightness-97"
|
|
302
|
+
},
|
|
303
|
+
size: {
|
|
304
|
+
sm: "min-h-9 px-3.5 text-subhead font-semibold",
|
|
305
|
+
md: "min-h-touch px-5 text-headline",
|
|
306
|
+
lg: "min-h-13 px-6 text-headline"
|
|
307
|
+
},
|
|
308
|
+
/**
|
|
309
|
+
* Em celular o padrão é ocupar a largura toda: alvo maior e polegar não
|
|
310
|
+
* precisa mirar. `auto` é a exceção, para botão em linha.
|
|
311
|
+
*/
|
|
312
|
+
width: {
|
|
313
|
+
full: "w-full",
|
|
314
|
+
auto: "w-auto"
|
|
315
|
+
}
|
|
316
|
+
},
|
|
317
|
+
defaultVariants: {
|
|
318
|
+
variant: "primary",
|
|
319
|
+
size: "md",
|
|
320
|
+
width: "full"
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
);
|
|
324
|
+
function Button({
|
|
325
|
+
variant,
|
|
326
|
+
size,
|
|
327
|
+
width,
|
|
328
|
+
loading = false,
|
|
329
|
+
className,
|
|
330
|
+
type,
|
|
331
|
+
disabled,
|
|
332
|
+
children,
|
|
333
|
+
...props
|
|
334
|
+
}) {
|
|
335
|
+
return (
|
|
336
|
+
// Sem `type` explícito, um botão dentro de formulário submete — que é o
|
|
337
|
+
// default do HTML e quase nunca o que se quis escrever.
|
|
338
|
+
/* @__PURE__ */ jsxs(
|
|
339
|
+
"button",
|
|
340
|
+
{
|
|
341
|
+
type: type ?? "button",
|
|
342
|
+
className: cn(button({ variant, size, width }), className),
|
|
343
|
+
disabled: disabled || loading,
|
|
344
|
+
"aria-busy": loading || void 0,
|
|
345
|
+
...props,
|
|
346
|
+
children: [
|
|
347
|
+
loading ? /* @__PURE__ */ jsx("span", { className: "absolute inset-0 grid place-items-center", children: /* @__PURE__ */ jsx(Spinner, { size: size === "sm" ? "sm" : "md" }) }) : null,
|
|
348
|
+
/* @__PURE__ */ jsx("span", { className: cn("inline-flex items-center gap-2", loading && "invisible"), children })
|
|
349
|
+
]
|
|
350
|
+
}
|
|
351
|
+
)
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
var card = cva(
|
|
355
|
+
// A superfície branca sobre o fundo cinza: é dessa inversão que nasce a
|
|
356
|
+
// profundidade do sistema — nunca de bordas pesadas.
|
|
357
|
+
["rounded-surface squircle text-text"],
|
|
358
|
+
{
|
|
359
|
+
variants: {
|
|
360
|
+
variant: {
|
|
361
|
+
/* Sem sombra: para quando o cartão já está dentro de outra superfície. */
|
|
362
|
+
flat: "border border-border bg-surface",
|
|
363
|
+
raised: "bg-surface shadow-level-1",
|
|
364
|
+
/* Acima de tudo na tela — em tema escuro a superfície mais clara é que separa. */
|
|
365
|
+
floating: "bg-surface-raised shadow-level-2"
|
|
366
|
+
},
|
|
367
|
+
padding: {
|
|
368
|
+
/* Para conteúdo que sangra até a borda (imagem, lista interna). */
|
|
369
|
+
none: "p-0",
|
|
370
|
+
md: "p-5",
|
|
371
|
+
lg: "p-6"
|
|
372
|
+
}
|
|
373
|
+
},
|
|
374
|
+
defaultVariants: {
|
|
375
|
+
variant: "raised",
|
|
376
|
+
padding: "md"
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
);
|
|
380
|
+
function Card({
|
|
381
|
+
variant,
|
|
382
|
+
padding,
|
|
383
|
+
interactive = false,
|
|
384
|
+
className,
|
|
385
|
+
children,
|
|
386
|
+
...props
|
|
387
|
+
}) {
|
|
388
|
+
if (interactive) {
|
|
389
|
+
return /* @__PURE__ */ jsx(
|
|
390
|
+
"button",
|
|
391
|
+
{
|
|
392
|
+
type: "button",
|
|
393
|
+
className: cn(
|
|
394
|
+
card({ variant, padding }),
|
|
395
|
+
"block w-full text-left",
|
|
396
|
+
"transition-[transform,box-shadow] duration-(--motion-quick) ease-(--ease-spring)",
|
|
397
|
+
// O aperto tem mola: o cartão afunda um fio e a sombra sobe no hover.
|
|
398
|
+
"hover:shadow-level-2 active:scale-[0.99]",
|
|
399
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
400
|
+
className
|
|
401
|
+
),
|
|
402
|
+
...props,
|
|
403
|
+
children
|
|
404
|
+
}
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
return /* @__PURE__ */ jsx("div", { className: cn(card({ variant, padding }), className), ...props, children });
|
|
408
|
+
}
|
|
409
|
+
var FieldContext = createContext(null);
|
|
410
|
+
function useFieldContext() {
|
|
411
|
+
return useContext(FieldContext);
|
|
412
|
+
}
|
|
413
|
+
function Field({ label, hint, error, required, className, children }) {
|
|
414
|
+
const id = useId();
|
|
415
|
+
const controlId = `${id}-controle`;
|
|
416
|
+
const hintId = hint ? `${id}-dica` : void 0;
|
|
417
|
+
const errorId = error ? `${id}-erro` : void 0;
|
|
418
|
+
return /* @__PURE__ */ jsx(FieldContext.Provider, { value: { controlId, hintId, errorId, invalid: Boolean(error) }, children: /* @__PURE__ */ jsxs("div", { className: cn("flex w-full flex-col gap-1.5", className), children: [
|
|
419
|
+
/* @__PURE__ */ jsxs("label", { htmlFor: controlId, className: "text-subhead font-medium text-text", children: [
|
|
420
|
+
label,
|
|
421
|
+
required ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "text-danger", children: " *" }) : null
|
|
422
|
+
] }),
|
|
423
|
+
children,
|
|
424
|
+
hint && !error ? /* @__PURE__ */ jsx("p", { id: hintId, className: "text-footnote text-text-muted", children: hint }) : null,
|
|
425
|
+
/* @__PURE__ */ jsx(
|
|
426
|
+
"p",
|
|
427
|
+
{
|
|
428
|
+
id: errorId,
|
|
429
|
+
"aria-live": "polite",
|
|
430
|
+
className: cn("text-footnote text-danger-soft-text", !error && "hidden"),
|
|
431
|
+
children: error
|
|
432
|
+
}
|
|
433
|
+
)
|
|
434
|
+
] }) });
|
|
435
|
+
}
|
|
436
|
+
var boxClasses = [
|
|
437
|
+
// `group` é o que deixa o ícone interno saber se o estado é indeterminado.
|
|
438
|
+
"group size-6 shrink-0 rounded-control-sm squircle",
|
|
439
|
+
"bg-surface-sunken border border-border-strong",
|
|
440
|
+
"transition-[background-color,border-color] duration-(--motion-quick) ease-(--ease-out-soft)",
|
|
441
|
+
"data-[state=checked]:bg-accent data-[state=checked]:border-accent data-[state=checked]:text-accent-text",
|
|
442
|
+
"data-[state=indeterminate]:bg-accent data-[state=indeterminate]:border-accent data-[state=indeterminate]:text-accent-text",
|
|
443
|
+
"aria-invalid:border-danger",
|
|
444
|
+
"disabled:pointer-events-none disabled:opacity-50"
|
|
445
|
+
].join(" ");
|
|
446
|
+
function Checkbox({ label, className, id, ...props }) {
|
|
447
|
+
const field = useFieldContext();
|
|
448
|
+
const generatedId = useId();
|
|
449
|
+
const controlId = id ?? field?.controlId ?? generatedId;
|
|
450
|
+
const box = /* @__PURE__ */ jsx(
|
|
451
|
+
Checkbox$1.Root,
|
|
452
|
+
{
|
|
453
|
+
id: controlId,
|
|
454
|
+
"aria-invalid": field?.invalid || void 0,
|
|
455
|
+
"aria-describedby": field ? [field.errorId, field.hintId].filter(Boolean).join(" ") || void 0 : void 0,
|
|
456
|
+
className: cn(boxClasses, className),
|
|
457
|
+
...props,
|
|
458
|
+
children: /* @__PURE__ */ jsxs(Checkbox$1.Indicator, { className: "flex items-center justify-center animate-pop-in", children: [
|
|
459
|
+
/* @__PURE__ */ jsx(
|
|
460
|
+
Check,
|
|
461
|
+
{
|
|
462
|
+
"aria-hidden": "true",
|
|
463
|
+
strokeWidth: 3,
|
|
464
|
+
className: "size-4 group-data-[state=indeterminate]:hidden"
|
|
465
|
+
}
|
|
466
|
+
),
|
|
467
|
+
/* @__PURE__ */ jsx(
|
|
468
|
+
Minus,
|
|
469
|
+
{
|
|
470
|
+
"aria-hidden": "true",
|
|
471
|
+
strokeWidth: 3,
|
|
472
|
+
className: "hidden size-4 group-data-[state=indeterminate]:block"
|
|
473
|
+
}
|
|
474
|
+
)
|
|
475
|
+
] })
|
|
476
|
+
}
|
|
477
|
+
);
|
|
478
|
+
if (!label) {
|
|
479
|
+
return box;
|
|
480
|
+
}
|
|
481
|
+
return /* @__PURE__ */ jsxs("label", { htmlFor: controlId, className: "flex min-h-touch select-none items-center gap-3", children: [
|
|
482
|
+
box,
|
|
483
|
+
/* @__PURE__ */ jsx("span", { className: "text-body text-text", children: label })
|
|
484
|
+
] });
|
|
485
|
+
}
|
|
486
|
+
var Dialog = Dialog$1.Root;
|
|
487
|
+
function DialogTrigger({ asChild = true, ...props }) {
|
|
488
|
+
return /* @__PURE__ */ jsx(Dialog$1.Trigger, { asChild, ...props });
|
|
489
|
+
}
|
|
490
|
+
function DialogContent({ className, children, ...props }) {
|
|
491
|
+
return /* @__PURE__ */ jsxs(Dialog$1.Portal, { children: [
|
|
492
|
+
/* @__PURE__ */ jsx(Dialog$1.Overlay, { className: "fixed inset-0 z-(--z-overlay) bg-overlay animate-fade-in" }),
|
|
493
|
+
/* @__PURE__ */ jsxs(
|
|
494
|
+
Dialog$1.Content,
|
|
495
|
+
{
|
|
496
|
+
className: cn(
|
|
497
|
+
"fixed top-1/2 left-1/2 z-(--z-modal) -translate-x-1/2 -translate-y-1/2",
|
|
498
|
+
// Em celular a margem de 1.25rem por lado respira; max-w segura no desktop.
|
|
499
|
+
"w-[calc(100vw-2.5rem)] max-w-md",
|
|
500
|
+
"rounded-surface-lg squircle bg-surface p-6 shadow-level-3",
|
|
501
|
+
"animate-pop-in",
|
|
502
|
+
className
|
|
503
|
+
),
|
|
504
|
+
...props,
|
|
505
|
+
children: [
|
|
506
|
+
children,
|
|
507
|
+
/* @__PURE__ */ jsx(
|
|
508
|
+
Dialog$1.Close,
|
|
509
|
+
{
|
|
510
|
+
"aria-label": "Fechar",
|
|
511
|
+
className: cn(
|
|
512
|
+
"absolute top-3 right-3 grid size-9 place-items-center rounded-control-sm squircle",
|
|
513
|
+
"text-text-secondary transition-colors duration-(--motion-quick) ease-(--ease-out-soft)",
|
|
514
|
+
"hover:bg-surface-sunken active:bg-surface-sunken"
|
|
515
|
+
),
|
|
516
|
+
children: /* @__PURE__ */ jsx(X, { "aria-hidden": "true", className: "size-5" })
|
|
517
|
+
}
|
|
518
|
+
)
|
|
519
|
+
]
|
|
520
|
+
}
|
|
521
|
+
)
|
|
522
|
+
] });
|
|
523
|
+
}
|
|
524
|
+
function DialogHeader({ className, children, ...props }) {
|
|
525
|
+
return (
|
|
526
|
+
// pr-6 reserva o canto do botão X — título longo passa por baixo sem colidir.
|
|
527
|
+
/* @__PURE__ */ jsx("div", { className: cn("flex flex-col gap-1.5 pr-6", className), ...props, children })
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
function DialogTitle({ className, ...props }) {
|
|
531
|
+
return /* @__PURE__ */ jsx(Dialog$1.Title, { className: cn("text-title-3", className), ...props });
|
|
532
|
+
}
|
|
533
|
+
function DialogDescription({ className, ...props }) {
|
|
534
|
+
return /* @__PURE__ */ jsx(
|
|
535
|
+
Dialog$1.Description,
|
|
536
|
+
{
|
|
537
|
+
className: cn("text-callout text-text-secondary", className),
|
|
538
|
+
...props
|
|
539
|
+
}
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
function DialogFooter({ className, children, ...props }) {
|
|
543
|
+
return (
|
|
544
|
+
// Em celular os botões empilham com o principal embaixo — onde o polegar já
|
|
545
|
+
// está; no desktop, lado a lado à direita, com o principal por último.
|
|
546
|
+
/* @__PURE__ */ jsx(
|
|
547
|
+
"div",
|
|
548
|
+
{
|
|
549
|
+
className: cn("mt-6 flex flex-col-reverse gap-3 sm:flex-row sm:justify-end", className),
|
|
550
|
+
...props,
|
|
551
|
+
children
|
|
552
|
+
}
|
|
553
|
+
)
|
|
554
|
+
);
|
|
555
|
+
}
|
|
556
|
+
function DialogClose({ asChild = true, ...props }) {
|
|
557
|
+
return /* @__PURE__ */ jsx(Dialog$1.Close, { asChild, ...props });
|
|
558
|
+
}
|
|
559
|
+
function EmptyState({
|
|
560
|
+
illustration,
|
|
561
|
+
icon,
|
|
562
|
+
title,
|
|
563
|
+
description,
|
|
564
|
+
action,
|
|
565
|
+
className,
|
|
566
|
+
...props
|
|
567
|
+
}) {
|
|
568
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col items-center gap-4 py-10 text-center", className), ...props, children: [
|
|
569
|
+
illustration ?? (icon ? (
|
|
570
|
+
// Decoração pura: o título logo abaixo já diz tudo ao leitor de tela.
|
|
571
|
+
/* @__PURE__ */ jsx(
|
|
572
|
+
"div",
|
|
573
|
+
{
|
|
574
|
+
"aria-hidden": "true",
|
|
575
|
+
className: "grid size-16 place-items-center rounded-expressive squircle bg-accent-soft text-accent-soft-text [&_svg]:size-8",
|
|
576
|
+
children: icon
|
|
577
|
+
}
|
|
578
|
+
)
|
|
579
|
+
) : null),
|
|
580
|
+
/* @__PURE__ */ jsx("p", { className: "text-title-3", children: title }),
|
|
581
|
+
description ? /* @__PURE__ */ jsx("p", { className: "max-w-sm text-balance text-callout text-text-secondary", children: description }) : null,
|
|
582
|
+
action
|
|
583
|
+
] });
|
|
584
|
+
}
|
|
585
|
+
var iconButton = cva(
|
|
586
|
+
[
|
|
587
|
+
"inline-flex shrink-0 items-center justify-center",
|
|
588
|
+
"rounded-control squircle select-none",
|
|
589
|
+
// O ícone tem tamanho fixo independente do que o lucide mandar por padrão.
|
|
590
|
+
"[&_svg]:size-5 [&_svg]:shrink-0",
|
|
591
|
+
"transition-[transform,background-color,border-color,opacity,box-shadow]",
|
|
592
|
+
// Mola em vez de curva suave: sem rótulo, o quique é o que confirma o toque.
|
|
593
|
+
"duration-(--motion-quick) ease-(--ease-spring)",
|
|
594
|
+
"active:scale-[0.94]",
|
|
595
|
+
"disabled:pointer-events-none disabled:opacity-50"
|
|
596
|
+
],
|
|
597
|
+
{
|
|
598
|
+
variants: {
|
|
599
|
+
/* O mesmo vocabulário de cor do Button — quem aprende um, leu os dois. */
|
|
600
|
+
variant: {
|
|
601
|
+
primary: "bg-accent text-accent-text hover:bg-accent-hover active:bg-accent-active",
|
|
602
|
+
tonal: "bg-accent-soft text-accent-soft-text hover:brightness-97 dark:hover:brightness-110",
|
|
603
|
+
secondary: "bg-surface text-text border border-border-strong hover:bg-surface-sunken",
|
|
604
|
+
ghost: "bg-transparent text-accent-soft-text hover:bg-accent-soft",
|
|
605
|
+
"danger-tonal": "bg-danger-soft text-danger-soft-text hover:brightness-97"
|
|
606
|
+
},
|
|
607
|
+
size: {
|
|
608
|
+
sm: "size-9",
|
|
609
|
+
md: "size-touch"
|
|
610
|
+
}
|
|
611
|
+
},
|
|
612
|
+
defaultVariants: {
|
|
613
|
+
variant: "primary",
|
|
614
|
+
size: "md"
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
);
|
|
618
|
+
function IconButton({
|
|
619
|
+
label,
|
|
620
|
+
variant,
|
|
621
|
+
size,
|
|
622
|
+
className,
|
|
623
|
+
type,
|
|
624
|
+
children,
|
|
625
|
+
...props
|
|
626
|
+
}) {
|
|
627
|
+
return (
|
|
628
|
+
// Mesmo cuidado do Button: sem `type`, um botão em formulário submete.
|
|
629
|
+
/* @__PURE__ */ jsx(
|
|
630
|
+
"button",
|
|
631
|
+
{
|
|
632
|
+
type: type ?? "button",
|
|
633
|
+
"aria-label": label,
|
|
634
|
+
className: cn(iconButton({ variant, size }), className),
|
|
635
|
+
...props,
|
|
636
|
+
children
|
|
637
|
+
}
|
|
638
|
+
)
|
|
639
|
+
);
|
|
640
|
+
}
|
|
641
|
+
var fieldControlClasses = [
|
|
642
|
+
"w-full rounded-control squircle",
|
|
643
|
+
"bg-surface-sunken text-text text-body",
|
|
644
|
+
"placeholder:text-text-muted",
|
|
645
|
+
"border border-transparent",
|
|
646
|
+
"transition-[border-color,box-shadow] duration-(--motion-quick) ease-(--ease-out-soft)",
|
|
647
|
+
"outline-none focus-visible:outline-none",
|
|
648
|
+
"focus:border-accent focus:ring-4 focus:ring-accent/15",
|
|
649
|
+
"aria-invalid:border-danger aria-invalid:ring-4 aria-invalid:ring-danger/15",
|
|
650
|
+
"disabled:pointer-events-none disabled:opacity-50"
|
|
651
|
+
].join(" ");
|
|
652
|
+
function Input({ className, id, type, ...props }) {
|
|
653
|
+
const field = useFieldContext();
|
|
654
|
+
return /* @__PURE__ */ jsx(
|
|
655
|
+
"input",
|
|
656
|
+
{
|
|
657
|
+
id: id ?? field?.controlId,
|
|
658
|
+
type: type ?? "text",
|
|
659
|
+
"aria-invalid": field?.invalid || void 0,
|
|
660
|
+
"aria-describedby": field ? [field.errorId, field.hintId].filter(Boolean).join(" ") || void 0 : void 0,
|
|
661
|
+
className: cn(fieldControlClasses, "min-h-touch px-4", className),
|
|
662
|
+
...props
|
|
663
|
+
}
|
|
664
|
+
);
|
|
665
|
+
}
|
|
666
|
+
function Textarea({ className, id, rows, ...props }) {
|
|
667
|
+
const field = useFieldContext();
|
|
668
|
+
return /* @__PURE__ */ jsx(
|
|
669
|
+
"textarea",
|
|
670
|
+
{
|
|
671
|
+
id: id ?? field?.controlId,
|
|
672
|
+
rows: rows ?? 4,
|
|
673
|
+
"aria-invalid": field?.invalid || void 0,
|
|
674
|
+
"aria-describedby": field ? [field.errorId, field.hintId].filter(Boolean).join(" ") || void 0 : void 0,
|
|
675
|
+
className: cn(fieldControlClasses, "min-h-touch resize-y px-4 py-3", className),
|
|
676
|
+
...props
|
|
677
|
+
}
|
|
678
|
+
);
|
|
679
|
+
}
|
|
680
|
+
function List({ header, footer, className, children }) {
|
|
681
|
+
const headerId = useId();
|
|
682
|
+
return /* @__PURE__ */ jsxs("section", { className: cn("w-full", className), children: [
|
|
683
|
+
header ? /* @__PURE__ */ jsx(
|
|
684
|
+
"p",
|
|
685
|
+
{
|
|
686
|
+
id: headerId,
|
|
687
|
+
className: "px-5 pb-2 text-footnote font-medium text-text-secondary uppercase tracking-wide",
|
|
688
|
+
children: header
|
|
689
|
+
}
|
|
690
|
+
) : null,
|
|
691
|
+
/* @__PURE__ */ jsx(
|
|
692
|
+
"ul",
|
|
693
|
+
{
|
|
694
|
+
"aria-labelledby": header ? headerId : void 0,
|
|
695
|
+
className: "divide-y divide-border overflow-hidden rounded-surface squircle bg-surface",
|
|
696
|
+
children
|
|
697
|
+
}
|
|
698
|
+
),
|
|
699
|
+
footer ? /* @__PURE__ */ jsx("p", { className: "px-5 pt-2 text-footnote text-text-muted", children: footer }) : null
|
|
700
|
+
] });
|
|
701
|
+
}
|
|
702
|
+
var iconBadge = cva(
|
|
703
|
+
// O quadradinho colorido dos Ajustes do iOS: o ícone ganha um chão próprio
|
|
704
|
+
// e a lista ganha ritmo visual sem que nenhum texto dependa dessa cor.
|
|
705
|
+
"grid size-8 shrink-0 place-items-center rounded-control-sm squircle [&_svg]:size-5",
|
|
706
|
+
{
|
|
707
|
+
variants: {
|
|
708
|
+
tone: {
|
|
709
|
+
accent: "bg-accent text-accent-text",
|
|
710
|
+
terracota: "bg-terracota text-white",
|
|
711
|
+
jardim: "bg-jardim text-white",
|
|
712
|
+
ambar: "bg-ambar text-white",
|
|
713
|
+
lavanda: "bg-lavanda text-white",
|
|
714
|
+
ceu: "bg-ceu text-white"
|
|
715
|
+
}
|
|
716
|
+
},
|
|
717
|
+
defaultVariants: {
|
|
718
|
+
tone: "accent"
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
);
|
|
722
|
+
function ListItem({
|
|
723
|
+
icon,
|
|
724
|
+
iconTone,
|
|
725
|
+
title,
|
|
726
|
+
subtitle,
|
|
727
|
+
value,
|
|
728
|
+
onClick,
|
|
729
|
+
className
|
|
730
|
+
}) {
|
|
731
|
+
const content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
732
|
+
icon ? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: iconBadge({ tone: iconTone }), children: icon }) : null,
|
|
733
|
+
/* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-1 flex-col", children: [
|
|
734
|
+
/* @__PURE__ */ jsx("span", { className: "truncate text-body", children: title }),
|
|
735
|
+
subtitle ? /* @__PURE__ */ jsx("span", { className: "truncate text-footnote text-text-muted", children: subtitle }) : null
|
|
736
|
+
] }),
|
|
737
|
+
value ? /* @__PURE__ */ jsx("span", { className: "shrink-0 text-body text-text-secondary", children: value }) : null,
|
|
738
|
+
onClick ? (
|
|
739
|
+
/* O chevron é redundante para o leitor de tela — o papel de botão já diz tudo. */
|
|
740
|
+
/* @__PURE__ */ jsx(ChevronRight, { "aria-hidden": "true", className: "size-4.5 shrink-0 text-text-muted" })
|
|
741
|
+
) : null
|
|
742
|
+
] });
|
|
743
|
+
const rowClasses = "flex min-h-touch w-full items-center gap-3 px-5 py-2";
|
|
744
|
+
return /* @__PURE__ */ jsx("li", { className: cn(className), children: onClick ? /* @__PURE__ */ jsx(
|
|
745
|
+
"button",
|
|
746
|
+
{
|
|
747
|
+
type: "button",
|
|
748
|
+
onClick,
|
|
749
|
+
className: cn(
|
|
750
|
+
rowClasses,
|
|
751
|
+
"text-left transition-colors duration-(--motion-quick) ease-(--ease-out-soft)",
|
|
752
|
+
"hover:bg-surface-sunken active:bg-surface-sunken"
|
|
753
|
+
),
|
|
754
|
+
children: content
|
|
755
|
+
}
|
|
756
|
+
) : /* @__PURE__ */ jsx("div", { className: rowClasses, children: content }) });
|
|
757
|
+
}
|
|
758
|
+
var Popover = Popover$1.Root;
|
|
759
|
+
function PopoverTrigger({ asChild = true, ...props }) {
|
|
760
|
+
return /* @__PURE__ */ jsx(Popover$1.Trigger, { asChild, ...props });
|
|
761
|
+
}
|
|
762
|
+
function PopoverContent({
|
|
763
|
+
className,
|
|
764
|
+
sideOffset = 8,
|
|
765
|
+
collisionPadding = 16,
|
|
766
|
+
children,
|
|
767
|
+
...props
|
|
768
|
+
}) {
|
|
769
|
+
return /* @__PURE__ */ jsx(Popover$1.Portal, { children: /* @__PURE__ */ jsx(
|
|
770
|
+
Popover$1.Content,
|
|
771
|
+
{
|
|
772
|
+
sideOffset,
|
|
773
|
+
collisionPadding,
|
|
774
|
+
className: cn(
|
|
775
|
+
"z-(--z-overlay) w-72 rounded-surface squircle border border-border",
|
|
776
|
+
"material-thick p-4 shadow-level-3",
|
|
777
|
+
"animate-pop-in",
|
|
778
|
+
className
|
|
779
|
+
),
|
|
780
|
+
...props,
|
|
781
|
+
children
|
|
782
|
+
}
|
|
783
|
+
) });
|
|
784
|
+
}
|
|
785
|
+
function Progress({ value, label, className }) {
|
|
786
|
+
const indeterminate = value === void 0;
|
|
787
|
+
return /* @__PURE__ */ jsx(
|
|
788
|
+
Progress$1.Root,
|
|
789
|
+
{
|
|
790
|
+
value: value ?? null,
|
|
791
|
+
"aria-label": label,
|
|
792
|
+
className: cn(
|
|
793
|
+
"h-2 w-full overflow-hidden rounded-pill bg-surface-sunken",
|
|
794
|
+
// Sem número, o próprio trilho pulsa — movimento sem prometer medida.
|
|
795
|
+
indeterminate && "animate-shimmer",
|
|
796
|
+
className
|
|
797
|
+
),
|
|
798
|
+
children: indeterminate ? null : /* @__PURE__ */ jsx(
|
|
799
|
+
Progress$1.Indicator,
|
|
800
|
+
{
|
|
801
|
+
className: cn(
|
|
802
|
+
"h-full rounded-pill bg-accent",
|
|
803
|
+
// Gentil de propósito: o quórum sobe conforme confirmações chegam,
|
|
804
|
+
// e o deslizamento comunica "andou" melhor que um salto seco.
|
|
805
|
+
"transition-[width] duration-(--motion-gentle) ease-(--ease-out-soft)"
|
|
806
|
+
),
|
|
807
|
+
style: { width: `${value}%` }
|
|
808
|
+
}
|
|
809
|
+
)
|
|
810
|
+
}
|
|
811
|
+
);
|
|
812
|
+
}
|
|
813
|
+
function RadioGroup({ className, id, ...props }) {
|
|
814
|
+
const field = useFieldContext();
|
|
815
|
+
return /* @__PURE__ */ jsx(
|
|
816
|
+
RadioGroup$1.Root,
|
|
817
|
+
{
|
|
818
|
+
id: id ?? field?.controlId,
|
|
819
|
+
"aria-invalid": field?.invalid || void 0,
|
|
820
|
+
"aria-describedby": field ? [field.errorId, field.hintId].filter(Boolean).join(" ") || void 0 : void 0,
|
|
821
|
+
className: cn("flex flex-col", className),
|
|
822
|
+
...props
|
|
823
|
+
}
|
|
824
|
+
);
|
|
825
|
+
}
|
|
826
|
+
var circleClasses = [
|
|
827
|
+
"flex size-6 shrink-0 items-center justify-center rounded-full",
|
|
828
|
+
"bg-surface-sunken border border-border-strong",
|
|
829
|
+
"transition-[border-color,background-color] duration-(--motion-quick) ease-(--ease-out-soft)",
|
|
830
|
+
"data-[state=checked]:border-accent",
|
|
831
|
+
"disabled:pointer-events-none disabled:opacity-50"
|
|
832
|
+
].join(" ");
|
|
833
|
+
function RadioGroupItem({ label, className, id, ...props }) {
|
|
834
|
+
const generatedId = useId();
|
|
835
|
+
const itemId = id ?? generatedId;
|
|
836
|
+
return (
|
|
837
|
+
// O label embrulha o círculo e aponta para ele por id: clicar no texto
|
|
838
|
+
// seleciona, e a linha inteira vira alvo de toque de 44px sem inflar o
|
|
839
|
+
// controle.
|
|
840
|
+
/* @__PURE__ */ jsxs("label", { htmlFor: itemId, className: "flex min-h-touch select-none items-center gap-3", children: [
|
|
841
|
+
/* @__PURE__ */ jsx(RadioGroup$1.Item, { id: itemId, className: cn(circleClasses, className), ...props, children: /* @__PURE__ */ jsx(RadioGroup$1.Indicator, { className: "flex items-center justify-center", children: /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "size-3 rounded-full bg-accent animate-pop-in" }) }) }),
|
|
842
|
+
/* @__PURE__ */ jsx("span", { className: "text-body text-text", children: label })
|
|
843
|
+
] })
|
|
844
|
+
);
|
|
845
|
+
}
|
|
846
|
+
function SearchField({
|
|
847
|
+
onClear,
|
|
848
|
+
className,
|
|
849
|
+
onChange,
|
|
850
|
+
"aria-label": ariaLabel,
|
|
851
|
+
...props
|
|
852
|
+
}) {
|
|
853
|
+
const inputRef = useRef(null);
|
|
854
|
+
const [internalValue, setInternalValue] = useState(String(props.defaultValue ?? ""));
|
|
855
|
+
const currentValue = props.value ?? internalValue;
|
|
856
|
+
const hasValue = String(currentValue).length > 0;
|
|
857
|
+
const handleChange = (event) => {
|
|
858
|
+
setInternalValue(event.target.value);
|
|
859
|
+
onChange?.(event);
|
|
860
|
+
};
|
|
861
|
+
const handleClear = () => {
|
|
862
|
+
if (props.value === void 0 && inputRef.current) {
|
|
863
|
+
inputRef.current.value = "";
|
|
864
|
+
setInternalValue("");
|
|
865
|
+
}
|
|
866
|
+
onClear?.();
|
|
867
|
+
inputRef.current?.focus();
|
|
868
|
+
};
|
|
869
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("relative", className), children: [
|
|
870
|
+
/* @__PURE__ */ jsx(
|
|
871
|
+
Search,
|
|
872
|
+
{
|
|
873
|
+
"aria-hidden": "true",
|
|
874
|
+
className: "pointer-events-none absolute top-1/2 left-3.5 size-4.5 -translate-y-1/2 text-text-muted"
|
|
875
|
+
}
|
|
876
|
+
),
|
|
877
|
+
/* @__PURE__ */ jsx(
|
|
878
|
+
"input",
|
|
879
|
+
{
|
|
880
|
+
ref: inputRef,
|
|
881
|
+
type: "search",
|
|
882
|
+
"aria-label": ariaLabel ?? "Buscar",
|
|
883
|
+
onChange: handleChange,
|
|
884
|
+
className: cn(
|
|
885
|
+
fieldControlClasses,
|
|
886
|
+
"min-h-touch rounded-pill pl-10 pr-10 [&::-webkit-search-cancel-button]:appearance-none"
|
|
887
|
+
),
|
|
888
|
+
...props
|
|
889
|
+
}
|
|
890
|
+
),
|
|
891
|
+
hasValue ? /* @__PURE__ */ jsx(
|
|
892
|
+
"button",
|
|
893
|
+
{
|
|
894
|
+
type: "button",
|
|
895
|
+
"aria-label": "Limpar busca",
|
|
896
|
+
onClick: handleClear,
|
|
897
|
+
className: cn(
|
|
898
|
+
// A coluna inteira à direita é clicável — 44px de alvo dentro do campo.
|
|
899
|
+
"absolute inset-y-0 right-0 grid w-touch place-items-center rounded-pill",
|
|
900
|
+
"text-text-muted transition-colors duration-(--motion-quick) ease-(--ease-out-soft)",
|
|
901
|
+
"hover:text-text"
|
|
902
|
+
),
|
|
903
|
+
children: /* @__PURE__ */ jsx(X, { "aria-hidden": "true", className: "size-4" })
|
|
904
|
+
}
|
|
905
|
+
) : null
|
|
906
|
+
] });
|
|
907
|
+
}
|
|
908
|
+
var container = cva("gap-1 rounded-control squircle bg-surface-sunken p-1", {
|
|
909
|
+
variants: {
|
|
910
|
+
/** Como no Button: em celular o padrão é a largura toda. */
|
|
911
|
+
width: {
|
|
912
|
+
full: "flex w-full",
|
|
913
|
+
auto: "inline-flex w-auto"
|
|
914
|
+
}
|
|
915
|
+
},
|
|
916
|
+
defaultVariants: {
|
|
917
|
+
width: "full"
|
|
918
|
+
}
|
|
919
|
+
});
|
|
920
|
+
var segmentClasses = [
|
|
921
|
+
"min-h-9 flex-1 select-none rounded-control-sm squircle px-3.5",
|
|
922
|
+
"text-subhead font-medium text-text-secondary",
|
|
923
|
+
"transition-[background-color,color,box-shadow] duration-(--motion-quick) ease-(--ease-out-soft)",
|
|
924
|
+
// O selecionado "sobe" um nível: superfície clara e sombra de repouso.
|
|
925
|
+
"data-[state=on]:bg-surface data-[state=on]:text-text data-[state=on]:shadow-level-1",
|
|
926
|
+
"disabled:pointer-events-none disabled:opacity-50"
|
|
927
|
+
].join(" ");
|
|
928
|
+
function SegmentedControl({
|
|
929
|
+
options,
|
|
930
|
+
value,
|
|
931
|
+
defaultValue,
|
|
932
|
+
onValueChange,
|
|
933
|
+
"aria-label": ariaLabel,
|
|
934
|
+
width,
|
|
935
|
+
disabled,
|
|
936
|
+
className
|
|
937
|
+
}) {
|
|
938
|
+
const [internalValue, setInternalValue] = useState(defaultValue ?? options[0]?.value);
|
|
939
|
+
const currentValue = value ?? internalValue;
|
|
940
|
+
const handleValueChange = (next) => {
|
|
941
|
+
if (!next || next === currentValue) {
|
|
942
|
+
return;
|
|
943
|
+
}
|
|
944
|
+
setInternalValue(next);
|
|
945
|
+
onValueChange?.(next);
|
|
946
|
+
};
|
|
947
|
+
return /* @__PURE__ */ jsx(
|
|
948
|
+
ToggleGroup.Root,
|
|
949
|
+
{
|
|
950
|
+
type: "single",
|
|
951
|
+
value: currentValue,
|
|
952
|
+
onValueChange: handleValueChange,
|
|
953
|
+
"aria-label": ariaLabel,
|
|
954
|
+
disabled,
|
|
955
|
+
className: cn(container({ width }), className),
|
|
956
|
+
children: options.map((option) => /* @__PURE__ */ jsx(
|
|
957
|
+
ToggleGroup.Item,
|
|
958
|
+
{
|
|
959
|
+
value: option.value,
|
|
960
|
+
className: segmentClasses,
|
|
961
|
+
children: option.label
|
|
962
|
+
},
|
|
963
|
+
option.value
|
|
964
|
+
))
|
|
965
|
+
}
|
|
966
|
+
);
|
|
967
|
+
}
|
|
968
|
+
function Select({
|
|
969
|
+
placeholder,
|
|
970
|
+
"aria-label": ariaLabel,
|
|
971
|
+
id,
|
|
972
|
+
className,
|
|
973
|
+
children,
|
|
974
|
+
...props
|
|
975
|
+
}) {
|
|
976
|
+
const field = useFieldContext();
|
|
977
|
+
return /* @__PURE__ */ jsxs(Select$1.Root, { ...props, children: [
|
|
978
|
+
/* @__PURE__ */ jsxs(
|
|
979
|
+
Select$1.Trigger,
|
|
980
|
+
{
|
|
981
|
+
id: id ?? field?.controlId,
|
|
982
|
+
"aria-label": ariaLabel,
|
|
983
|
+
"aria-invalid": field?.invalid || void 0,
|
|
984
|
+
"aria-describedby": field ? [field.errorId, field.hintId].filter(Boolean).join(" ") || void 0 : void 0,
|
|
985
|
+
className: cn(
|
|
986
|
+
fieldControlClasses,
|
|
987
|
+
"inline-flex min-h-touch items-center justify-between gap-2 px-4 text-left",
|
|
988
|
+
"data-[placeholder]:text-text-muted",
|
|
989
|
+
className
|
|
990
|
+
),
|
|
991
|
+
children: [
|
|
992
|
+
/* @__PURE__ */ jsx(Select$1.Value, { placeholder }),
|
|
993
|
+
/* @__PURE__ */ jsx(Select$1.Icon, { asChild: true, children: /* @__PURE__ */ jsx(ChevronsUpDown, { "aria-hidden": "true", className: "size-4 shrink-0 text-text-muted" }) })
|
|
994
|
+
]
|
|
995
|
+
}
|
|
996
|
+
),
|
|
997
|
+
/* @__PURE__ */ jsx(Select$1.Portal, { children: /* @__PURE__ */ jsx(
|
|
998
|
+
Select$1.Content,
|
|
999
|
+
{
|
|
1000
|
+
position: "popper",
|
|
1001
|
+
sideOffset: 6,
|
|
1002
|
+
className: cn(
|
|
1003
|
+
"z-(--z-overlay) overflow-hidden rounded-surface squircle",
|
|
1004
|
+
"material-thick border border-border shadow-level-3",
|
|
1005
|
+
"animate-pop-in",
|
|
1006
|
+
// A lista nunca fica mais estreita que o campo que a abriu.
|
|
1007
|
+
"min-w-(--radix-select-trigger-width) max-h-(--radix-select-content-available-height)"
|
|
1008
|
+
),
|
|
1009
|
+
children: /* @__PURE__ */ jsx(Select$1.Viewport, { className: "p-1", children })
|
|
1010
|
+
}
|
|
1011
|
+
) })
|
|
1012
|
+
] });
|
|
1013
|
+
}
|
|
1014
|
+
function SelectItem({ className, children, ...props }) {
|
|
1015
|
+
return /* @__PURE__ */ jsxs(
|
|
1016
|
+
Select$1.Item,
|
|
1017
|
+
{
|
|
1018
|
+
className: cn(
|
|
1019
|
+
"flex min-h-touch select-none items-center justify-between gap-2 rounded-control-sm squircle px-3",
|
|
1020
|
+
"text-body text-text",
|
|
1021
|
+
// Aqui o destaque É o anel de foco: o item realçado troca de fundo,
|
|
1022
|
+
// então o outline global só duplicaria o sinal.
|
|
1023
|
+
"outline-none data-[highlighted]:bg-accent-soft data-[highlighted]:text-accent-soft-text",
|
|
1024
|
+
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
1025
|
+
className
|
|
1026
|
+
),
|
|
1027
|
+
...props,
|
|
1028
|
+
children: [
|
|
1029
|
+
/* @__PURE__ */ jsx(Select$1.ItemText, { children }),
|
|
1030
|
+
/* @__PURE__ */ jsx(Select$1.ItemIndicator, { asChild: true, children: /* @__PURE__ */ jsx(Check, { "aria-hidden": "true", className: "size-4 shrink-0" }) })
|
|
1031
|
+
]
|
|
1032
|
+
}
|
|
1033
|
+
);
|
|
1034
|
+
}
|
|
1035
|
+
function Separator({
|
|
1036
|
+
orientation = "horizontal",
|
|
1037
|
+
decorative = true,
|
|
1038
|
+
className,
|
|
1039
|
+
...props
|
|
1040
|
+
}) {
|
|
1041
|
+
return /* @__PURE__ */ jsx(
|
|
1042
|
+
Separator$1.Root,
|
|
1043
|
+
{
|
|
1044
|
+
orientation,
|
|
1045
|
+
decorative,
|
|
1046
|
+
className: cn(
|
|
1047
|
+
"shrink-0 bg-border",
|
|
1048
|
+
orientation === "horizontal" ? "h-px w-full" : "h-full w-px",
|
|
1049
|
+
className
|
|
1050
|
+
),
|
|
1051
|
+
...props
|
|
1052
|
+
}
|
|
1053
|
+
);
|
|
1054
|
+
}
|
|
1055
|
+
var Sheet = Dialog$1.Root;
|
|
1056
|
+
function SheetTrigger({ asChild = true, ...props }) {
|
|
1057
|
+
return /* @__PURE__ */ jsx(Dialog$1.Trigger, { asChild, ...props });
|
|
1058
|
+
}
|
|
1059
|
+
function SheetContent({ className, children, ...props }) {
|
|
1060
|
+
return /* @__PURE__ */ jsxs(Dialog$1.Portal, { children: [
|
|
1061
|
+
/* @__PURE__ */ jsx(Dialog$1.Overlay, { className: "fixed inset-0 z-(--z-overlay) bg-overlay animate-fade-in" }),
|
|
1062
|
+
/* @__PURE__ */ jsxs(
|
|
1063
|
+
Dialog$1.Content,
|
|
1064
|
+
{
|
|
1065
|
+
className: cn(
|
|
1066
|
+
"fixed inset-x-0 bottom-0 z-(--z-modal)",
|
|
1067
|
+
"rounded-t-surface-lg squircle material-thick border-t border-border shadow-level-3",
|
|
1068
|
+
// O pb estica até o maior entre o padding do sheet e a safe area — o
|
|
1069
|
+
// conteúdo nunca fica embaixo da barra de gestos do iPhone.
|
|
1070
|
+
"p-6 pb-[max(1.5rem,env(safe-area-inset-bottom))]",
|
|
1071
|
+
"max-h-[85dvh] overflow-y-auto",
|
|
1072
|
+
"animate-slide-up",
|
|
1073
|
+
className
|
|
1074
|
+
),
|
|
1075
|
+
...props,
|
|
1076
|
+
children: [
|
|
1077
|
+
/* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: "mx-auto mb-4 h-1.5 w-10 rounded-pill bg-border-strong" }),
|
|
1078
|
+
children
|
|
1079
|
+
]
|
|
1080
|
+
}
|
|
1081
|
+
)
|
|
1082
|
+
] });
|
|
1083
|
+
}
|
|
1084
|
+
function SheetHeader({ className, children, ...props }) {
|
|
1085
|
+
return /* @__PURE__ */ jsx("div", { className: cn("flex flex-col gap-1.5", className), ...props, children });
|
|
1086
|
+
}
|
|
1087
|
+
function SheetTitle({ className, ...props }) {
|
|
1088
|
+
return /* @__PURE__ */ jsx(Dialog$1.Title, { className: cn("text-title-3", className), ...props });
|
|
1089
|
+
}
|
|
1090
|
+
function SheetDescription({ className, ...props }) {
|
|
1091
|
+
return /* @__PURE__ */ jsx(
|
|
1092
|
+
Dialog$1.Description,
|
|
1093
|
+
{
|
|
1094
|
+
className: cn("text-callout text-text-secondary", className),
|
|
1095
|
+
...props
|
|
1096
|
+
}
|
|
1097
|
+
);
|
|
1098
|
+
}
|
|
1099
|
+
var skeleton = cva("bg-surface-sunken animate-shimmer", {
|
|
1100
|
+
variants: {
|
|
1101
|
+
shape: {
|
|
1102
|
+
text: "h-4 w-full rounded-control-sm",
|
|
1103
|
+
title: "h-6 w-2/5 rounded-control-sm",
|
|
1104
|
+
block: "h-24 rounded-surface",
|
|
1105
|
+
circle: "size-10 rounded-full"
|
|
1106
|
+
}
|
|
1107
|
+
},
|
|
1108
|
+
defaultVariants: {
|
|
1109
|
+
shape: "text"
|
|
1110
|
+
}
|
|
1111
|
+
});
|
|
1112
|
+
function Skeleton({ shape, className, ...props }) {
|
|
1113
|
+
return /* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: cn(skeleton({ shape }), className), ...props });
|
|
1114
|
+
}
|
|
1115
|
+
var trackClasses = [
|
|
1116
|
+
"inline-flex h-8 w-13 shrink-0 items-center rounded-pill",
|
|
1117
|
+
"bg-surface-sunken border border-border",
|
|
1118
|
+
"transition-[background-color,border-color] duration-(--motion-quick) ease-(--ease-out-soft)",
|
|
1119
|
+
"data-[state=checked]:bg-accent data-[state=checked]:border-accent",
|
|
1120
|
+
"disabled:pointer-events-none disabled:opacity-50"
|
|
1121
|
+
].join(" ");
|
|
1122
|
+
var thumbClasses = [
|
|
1123
|
+
// Branco fixo nos dois temas, como no iOS: sobre o trilho rebaixado ou sobre
|
|
1124
|
+
// o accent, é a sombra que garante a leitura do polegar.
|
|
1125
|
+
"pointer-events-none block size-7 rounded-full bg-white shadow-level-1",
|
|
1126
|
+
"transition-transform duration-(--motion-quick) ease-(--ease-out-soft)",
|
|
1127
|
+
"translate-x-0.5 data-[state=checked]:translate-x-5"
|
|
1128
|
+
].join(" ");
|
|
1129
|
+
function Switch({ label, className, id, ...props }) {
|
|
1130
|
+
const field = useFieldContext();
|
|
1131
|
+
const generatedId = useId();
|
|
1132
|
+
const controlId = id ?? field?.controlId ?? generatedId;
|
|
1133
|
+
const control = /* @__PURE__ */ jsx(
|
|
1134
|
+
Switch$1.Root,
|
|
1135
|
+
{
|
|
1136
|
+
id: controlId,
|
|
1137
|
+
"aria-invalid": field?.invalid || void 0,
|
|
1138
|
+
"aria-describedby": field ? [field.errorId, field.hintId].filter(Boolean).join(" ") || void 0 : void 0,
|
|
1139
|
+
className: cn(trackClasses, className),
|
|
1140
|
+
...props,
|
|
1141
|
+
children: /* @__PURE__ */ jsx(Switch$1.Thumb, { className: thumbClasses })
|
|
1142
|
+
}
|
|
1143
|
+
);
|
|
1144
|
+
if (!label) {
|
|
1145
|
+
return control;
|
|
1146
|
+
}
|
|
1147
|
+
return /* @__PURE__ */ jsxs(
|
|
1148
|
+
"label",
|
|
1149
|
+
{
|
|
1150
|
+
htmlFor: controlId,
|
|
1151
|
+
className: "flex min-h-touch w-full select-none items-center justify-between gap-3",
|
|
1152
|
+
children: [
|
|
1153
|
+
/* @__PURE__ */ jsx("span", { className: "text-body text-text", children: label }),
|
|
1154
|
+
control
|
|
1155
|
+
]
|
|
1156
|
+
}
|
|
1157
|
+
);
|
|
1158
|
+
}
|
|
1159
|
+
var Tabs = Tabs$1.Root;
|
|
1160
|
+
function TabsList({ className, children, ...props }) {
|
|
1161
|
+
return (
|
|
1162
|
+
// A borda inferior é o trilho onde a sublinha do trigger ativo se apoia.
|
|
1163
|
+
/* @__PURE__ */ jsx(Tabs$1.List, { className: cn("flex gap-1 border-b border-border", className), ...props, children })
|
|
1164
|
+
);
|
|
1165
|
+
}
|
|
1166
|
+
function TabsTrigger({ className, children, ...props }) {
|
|
1167
|
+
return /* @__PURE__ */ jsxs(
|
|
1168
|
+
Tabs$1.Trigger,
|
|
1169
|
+
{
|
|
1170
|
+
className: cn(
|
|
1171
|
+
"group relative min-h-touch select-none px-4 text-subhead font-medium text-text-secondary",
|
|
1172
|
+
"transition-colors duration-(--motion-quick) ease-(--ease-out-soft)",
|
|
1173
|
+
"hover:text-text",
|
|
1174
|
+
"data-[state=active]:text-accent-soft-text",
|
|
1175
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
1176
|
+
className
|
|
1177
|
+
),
|
|
1178
|
+
...props,
|
|
1179
|
+
children: [
|
|
1180
|
+
children,
|
|
1181
|
+
/* @__PURE__ */ jsx(
|
|
1182
|
+
"span",
|
|
1183
|
+
{
|
|
1184
|
+
"aria-hidden": "true",
|
|
1185
|
+
className: cn(
|
|
1186
|
+
"absolute inset-x-3 -bottom-px h-0.5 rounded-pill bg-accent",
|
|
1187
|
+
"opacity-0 transition-opacity duration-(--motion-quick) ease-(--ease-out-soft)",
|
|
1188
|
+
"group-data-[state=active]:opacity-100"
|
|
1189
|
+
)
|
|
1190
|
+
}
|
|
1191
|
+
)
|
|
1192
|
+
]
|
|
1193
|
+
}
|
|
1194
|
+
);
|
|
1195
|
+
}
|
|
1196
|
+
function TabsContent({ className, children, ...props }) {
|
|
1197
|
+
return /* @__PURE__ */ jsx(Tabs$1.Content, { className: cn("pt-5", className), ...props, children });
|
|
1198
|
+
}
|
|
1199
|
+
var ToastContext = createContext(null);
|
|
1200
|
+
function useToast() {
|
|
1201
|
+
const context = useContext(ToastContext);
|
|
1202
|
+
if (!context) {
|
|
1203
|
+
throw new Error(
|
|
1204
|
+
"useToast precisa de um <ToastProvider> acima na \xE1rvore \u2014 envolva a app com ele."
|
|
1205
|
+
);
|
|
1206
|
+
}
|
|
1207
|
+
return context;
|
|
1208
|
+
}
|
|
1209
|
+
var variantIcon = {
|
|
1210
|
+
success: { icon: CircleCheck, tint: "text-success" },
|
|
1211
|
+
danger: { icon: CircleAlert, tint: "text-danger" }
|
|
1212
|
+
};
|
|
1213
|
+
function ToastCard({ toast, onClose }) {
|
|
1214
|
+
const visual = variantIcon[toast.variant ?? "default"];
|
|
1215
|
+
return /* @__PURE__ */ jsxs(
|
|
1216
|
+
Toast.Root,
|
|
1217
|
+
{
|
|
1218
|
+
onOpenChange: (open) => {
|
|
1219
|
+
if (!open) onClose();
|
|
1220
|
+
},
|
|
1221
|
+
className: cn(
|
|
1222
|
+
"relative flex w-full gap-3 rounded-surface squircle border border-border material-thick p-4 pr-10 shadow-level-3",
|
|
1223
|
+
"animate-slide-up",
|
|
1224
|
+
// O arrasto acompanha o dedo; soltar aquém do limiar devolve com mola.
|
|
1225
|
+
"data-[swipe=move]:translate-y-(--radix-toast-swipe-move-y) data-[swipe=move]:transition-none",
|
|
1226
|
+
"data-[swipe=cancel]:translate-y-0 data-[swipe=cancel]:transition-transform data-[swipe=cancel]:duration-(--motion-quick) data-[swipe=cancel]:ease-(--ease-out-soft)"
|
|
1227
|
+
),
|
|
1228
|
+
children: [
|
|
1229
|
+
visual ? /* @__PURE__ */ jsx(visual.icon, { "aria-hidden": "true", className: cn("mt-0.5 size-5 shrink-0", visual.tint) }) : null,
|
|
1230
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5", children: [
|
|
1231
|
+
/* @__PURE__ */ jsx(Toast.Title, { className: "text-subhead font-semibold", children: toast.title }),
|
|
1232
|
+
toast.description ? /* @__PURE__ */ jsx(Toast.Description, { className: "text-footnote text-text-secondary", children: toast.description }) : null
|
|
1233
|
+
] }),
|
|
1234
|
+
/* @__PURE__ */ jsx(
|
|
1235
|
+
Toast.Close,
|
|
1236
|
+
{
|
|
1237
|
+
"aria-label": "Fechar aviso",
|
|
1238
|
+
className: cn(
|
|
1239
|
+
"absolute top-2 right-2 grid size-8 place-items-center rounded-control-sm text-text-muted",
|
|
1240
|
+
"transition-colors duration-(--motion-quick) ease-(--ease-out-soft) hover:text-text",
|
|
1241
|
+
// O X é discreto no olho, não no dedo: o pseudo-elemento completa 44px.
|
|
1242
|
+
"before:absolute before:-inset-1.5"
|
|
1243
|
+
),
|
|
1244
|
+
children: /* @__PURE__ */ jsx(X, { "aria-hidden": "true", className: "size-4" })
|
|
1245
|
+
}
|
|
1246
|
+
)
|
|
1247
|
+
]
|
|
1248
|
+
}
|
|
1249
|
+
);
|
|
1250
|
+
}
|
|
1251
|
+
function ToastProvider({ children }) {
|
|
1252
|
+
const [toasts, setToasts] = useState([]);
|
|
1253
|
+
const nextId = useRef(0);
|
|
1254
|
+
const toast = useCallback((options) => {
|
|
1255
|
+
nextId.current += 1;
|
|
1256
|
+
const id = nextId.current;
|
|
1257
|
+
setToasts((current) => [...current, { id, ...options }]);
|
|
1258
|
+
}, []);
|
|
1259
|
+
const remove = useCallback((id) => {
|
|
1260
|
+
setToasts((current) => current.filter((item) => item.id !== id));
|
|
1261
|
+
}, []);
|
|
1262
|
+
const value = useMemo(() => ({ toast }), [toast]);
|
|
1263
|
+
return /* @__PURE__ */ jsxs(ToastContext.Provider, { value, children: [
|
|
1264
|
+
children,
|
|
1265
|
+
/* @__PURE__ */ jsxs(Toast.Provider, { duration: 5e3, swipeDirection: "down", label: "Aviso", children: [
|
|
1266
|
+
toasts.map((item) => /* @__PURE__ */ jsx(ToastCard, { toast: item, onClose: () => remove(item.id) }, item.id)),
|
|
1267
|
+
/* @__PURE__ */ jsx(
|
|
1268
|
+
Toast.Viewport,
|
|
1269
|
+
{
|
|
1270
|
+
label: "Avisos ({hotkey})",
|
|
1271
|
+
className: cn(
|
|
1272
|
+
"fixed bottom-0 inset-x-0 z-(--z-toast) m-0 flex w-full list-none flex-col gap-2 p-5",
|
|
1273
|
+
// Em PWA no iPhone, a barra de gestos comeria o último toast.
|
|
1274
|
+
"pb-[max(1.25rem,env(safe-area-inset-bottom))]",
|
|
1275
|
+
"sm:left-auto sm:max-w-sm"
|
|
1276
|
+
)
|
|
1277
|
+
}
|
|
1278
|
+
)
|
|
1279
|
+
] })
|
|
1280
|
+
] });
|
|
1281
|
+
}
|
|
1282
|
+
function Tooltip({ content, side, className, children }) {
|
|
1283
|
+
return (
|
|
1284
|
+
// A dica é só texto, nunca interativa: sem conteúdo "hoverable" ela some
|
|
1285
|
+
// assim que o mouse sai do gatilho, sem área de tolerância fantasma.
|
|
1286
|
+
/* @__PURE__ */ jsx(Tooltip$1.Provider, { delayDuration: 400, skipDelayDuration: 300, disableHoverableContent: true, children: /* @__PURE__ */ jsxs(Tooltip$1.Root, { children: [
|
|
1287
|
+
/* @__PURE__ */ jsx(Tooltip$1.Trigger, { asChild: true, children }),
|
|
1288
|
+
/* @__PURE__ */ jsx(Tooltip$1.Portal, { children: /* @__PURE__ */ jsx(
|
|
1289
|
+
Tooltip$1.Content,
|
|
1290
|
+
{
|
|
1291
|
+
side,
|
|
1292
|
+
sideOffset: 6,
|
|
1293
|
+
className: cn(
|
|
1294
|
+
"z-(--z-tooltip) rounded-control-sm squircle border border-border bg-surface-raised px-3 py-1.5 shadow-level-2",
|
|
1295
|
+
"text-footnote animate-fade-in",
|
|
1296
|
+
className
|
|
1297
|
+
),
|
|
1298
|
+
children: content
|
|
1299
|
+
}
|
|
1300
|
+
) })
|
|
1301
|
+
] }) })
|
|
1302
|
+
);
|
|
1303
|
+
}
|
|
1304
|
+
var band = {
|
|
1305
|
+
terracota: "bg-terracota-soft",
|
|
1306
|
+
jardim: "bg-jardim-soft",
|
|
1307
|
+
ambar: "bg-ambar-soft",
|
|
1308
|
+
lavanda: "bg-lavanda-soft",
|
|
1309
|
+
ceu: "bg-ceu-soft"
|
|
1310
|
+
};
|
|
1311
|
+
var cracha = cva(
|
|
1312
|
+
[
|
|
1313
|
+
"relative flex flex-col items-center overflow-hidden text-center",
|
|
1314
|
+
"rounded-expressive squircle bg-surface shadow-level-1"
|
|
1315
|
+
],
|
|
1316
|
+
{
|
|
1317
|
+
variants: {
|
|
1318
|
+
size: {
|
|
1319
|
+
md: "w-40 pb-5",
|
|
1320
|
+
lg: "w-48 pb-6"
|
|
1321
|
+
}
|
|
1322
|
+
},
|
|
1323
|
+
defaultVariants: {
|
|
1324
|
+
size: "md"
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
);
|
|
1328
|
+
function Cracha({
|
|
1329
|
+
name,
|
|
1330
|
+
unit,
|
|
1331
|
+
src,
|
|
1332
|
+
memberRole,
|
|
1333
|
+
tone = "ceu",
|
|
1334
|
+
size,
|
|
1335
|
+
className
|
|
1336
|
+
}) {
|
|
1337
|
+
return /* @__PURE__ */ jsxs("div", { className: cn(cracha({ size }), className), children: [
|
|
1338
|
+
/* @__PURE__ */ jsx("div", { className: cn("flex h-16 w-full justify-center pt-3", band[tone]), children: /* @__PURE__ */ jsx(
|
|
1339
|
+
"div",
|
|
1340
|
+
{
|
|
1341
|
+
"aria-hidden": "true",
|
|
1342
|
+
className: "h-2.5 w-10 rounded-pill bg-bg shadow-[inset_0_1px_2px_rgb(0_0_0/0.18)]"
|
|
1343
|
+
}
|
|
1344
|
+
) }),
|
|
1345
|
+
/* @__PURE__ */ jsx("div", { className: "-mt-8 rounded-full ring-4 ring-surface", children: /* @__PURE__ */ jsx(Avatar, { name, src, size: "xl" }) }),
|
|
1346
|
+
/* @__PURE__ */ jsx("p", { className: "mt-3 px-4 font-friendly text-headline text-text", children: name }),
|
|
1347
|
+
unit ? /* @__PURE__ */ jsx("p", { className: "mt-0.5 px-4 text-footnote text-text-secondary", children: unit }) : null,
|
|
1348
|
+
memberRole ? /* @__PURE__ */ jsx("span", { className: "mt-2.5", children: /* @__PURE__ */ jsx(Badge, { tone: "accent", children: memberRole }) }) : null
|
|
1349
|
+
] });
|
|
1350
|
+
}
|
|
1351
|
+
var cover = {
|
|
1352
|
+
terracota: "bg-terracota-soft",
|
|
1353
|
+
jardim: "bg-jardim-soft",
|
|
1354
|
+
ambar: "bg-ambar-soft",
|
|
1355
|
+
lavanda: "bg-lavanda-soft",
|
|
1356
|
+
ceu: "bg-ceu-soft"
|
|
1357
|
+
};
|
|
1358
|
+
var spine = {
|
|
1359
|
+
terracota: "bg-terracota",
|
|
1360
|
+
jardim: "bg-jardim",
|
|
1361
|
+
ambar: "bg-ambar",
|
|
1362
|
+
lavanda: "bg-lavanda",
|
|
1363
|
+
ceu: "bg-ceu"
|
|
1364
|
+
};
|
|
1365
|
+
var livreto = cva(
|
|
1366
|
+
[
|
|
1367
|
+
"relative flex aspect-[3/4] flex-col overflow-hidden",
|
|
1368
|
+
"rounded-surface squircle shadow-level-1"
|
|
1369
|
+
],
|
|
1370
|
+
{
|
|
1371
|
+
variants: {
|
|
1372
|
+
size: {
|
|
1373
|
+
sm: "w-28 p-3 pl-5",
|
|
1374
|
+
md: "w-36 p-4 pl-6",
|
|
1375
|
+
lg: "w-44 p-5 pl-7"
|
|
1376
|
+
}
|
|
1377
|
+
},
|
|
1378
|
+
defaultVariants: {
|
|
1379
|
+
size: "md"
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
);
|
|
1383
|
+
var titleBySize = {
|
|
1384
|
+
sm: "text-subhead font-semibold",
|
|
1385
|
+
md: "text-headline",
|
|
1386
|
+
lg: "text-title-3"
|
|
1387
|
+
};
|
|
1388
|
+
function Livreto({
|
|
1389
|
+
title,
|
|
1390
|
+
kind = "Pauta",
|
|
1391
|
+
tone = "lavanda",
|
|
1392
|
+
size,
|
|
1393
|
+
footer,
|
|
1394
|
+
className
|
|
1395
|
+
}) {
|
|
1396
|
+
return /* @__PURE__ */ jsxs("div", { className: cn(livreto({ size }), cover[tone], className), children: [
|
|
1397
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: cn("absolute inset-y-0 left-0 w-2", spine[tone]) }),
|
|
1398
|
+
/* @__PURE__ */ jsx(
|
|
1399
|
+
"span",
|
|
1400
|
+
{
|
|
1401
|
+
"aria-hidden": "true",
|
|
1402
|
+
className: "absolute inset-y-2 right-0 w-1 rounded-l-pill bg-surface opacity-80"
|
|
1403
|
+
}
|
|
1404
|
+
),
|
|
1405
|
+
/* @__PURE__ */ jsx("p", { className: "text-caption font-semibold uppercase tracking-[0.12em] text-text-secondary", children: kind }),
|
|
1406
|
+
/* @__PURE__ */ jsx(
|
|
1407
|
+
"p",
|
|
1408
|
+
{
|
|
1409
|
+
lang: "pt-BR",
|
|
1410
|
+
className: cn(
|
|
1411
|
+
"mt-1.5 line-clamp-4 font-friendly text-text",
|
|
1412
|
+
"break-words hyphens-auto",
|
|
1413
|
+
titleBySize[size ?? "md"]
|
|
1414
|
+
),
|
|
1415
|
+
children: title
|
|
1416
|
+
}
|
|
1417
|
+
),
|
|
1418
|
+
footer ? /* @__PURE__ */ jsx("div", { className: "mt-auto pt-3", children: footer }) : null
|
|
1419
|
+
] });
|
|
1420
|
+
}
|
|
1421
|
+
var TONES = ["terracota", "jardim", "ambar", "lavanda", "ceu"];
|
|
1422
|
+
function hashSeed(seed) {
|
|
1423
|
+
let hash = 0;
|
|
1424
|
+
for (const char of seed) {
|
|
1425
|
+
hash = (hash * 31 + (char.codePointAt(0) ?? 0)) % 2147483647;
|
|
1426
|
+
}
|
|
1427
|
+
return hash;
|
|
1428
|
+
}
|
|
1429
|
+
var predio = cva("inline-block", {
|
|
1430
|
+
variants: {
|
|
1431
|
+
size: {
|
|
1432
|
+
sm: "h-20",
|
|
1433
|
+
md: "h-28",
|
|
1434
|
+
lg: "h-40"
|
|
1435
|
+
}
|
|
1436
|
+
},
|
|
1437
|
+
defaultVariants: {
|
|
1438
|
+
size: "md"
|
|
1439
|
+
}
|
|
1440
|
+
});
|
|
1441
|
+
var GROUND_Y = 138;
|
|
1442
|
+
var ROW_HEIGHT = 16;
|
|
1443
|
+
var WINDOW_COLUMNS = [31, 53, 75];
|
|
1444
|
+
var WINDOW_WIDTH = 14;
|
|
1445
|
+
var WINDOW_HEIGHT = 10;
|
|
1446
|
+
var DOOR_HEIGHT = 18;
|
|
1447
|
+
function Predio({ name, tone, floors, size, decorative = false, className }) {
|
|
1448
|
+
const hash = hashSeed(name);
|
|
1449
|
+
const floorCount = Math.min(6, Math.max(3, floors ?? 3 + hash % 4));
|
|
1450
|
+
const chosenTone = tone ?? TONES[hash % TONES.length] ?? "ceu";
|
|
1451
|
+
const bodyTop = GROUND_Y - DOOR_HEIGHT - floorCount * ROW_HEIGHT - 8;
|
|
1452
|
+
const strong = `var(--assembleo-${chosenTone})`;
|
|
1453
|
+
const soft = `var(--assembleo-${chosenTone}-soft)`;
|
|
1454
|
+
const windows = Array.from(
|
|
1455
|
+
{ length: floorCount },
|
|
1456
|
+
(_, floor) => WINDOW_COLUMNS.map((x, column) => {
|
|
1457
|
+
const index = floor * WINDOW_COLUMNS.length + column;
|
|
1458
|
+
const lit = (hash >> index % 30 & 1) === 1;
|
|
1459
|
+
return {
|
|
1460
|
+
x,
|
|
1461
|
+
y: bodyTop + 10 + floor * ROW_HEIGHT,
|
|
1462
|
+
lit,
|
|
1463
|
+
key: `${floor}-${column}`
|
|
1464
|
+
};
|
|
1465
|
+
})
|
|
1466
|
+
).flat();
|
|
1467
|
+
return /* @__PURE__ */ jsxs(
|
|
1468
|
+
"svg",
|
|
1469
|
+
{
|
|
1470
|
+
viewBox: "0 0 120 148",
|
|
1471
|
+
role: decorative ? void 0 : "img",
|
|
1472
|
+
"aria-label": decorative ? void 0 : `Pr\xE9dio do condom\xEDnio ${name}`,
|
|
1473
|
+
"aria-hidden": decorative || void 0,
|
|
1474
|
+
className: cn(predio({ size }), className),
|
|
1475
|
+
children: [
|
|
1476
|
+
/* @__PURE__ */ jsx("rect", { x: "10", y: GROUND_Y, width: "100", height: "4", rx: "2", fill: "var(--assembleo-border)" }),
|
|
1477
|
+
/* @__PURE__ */ jsx("rect", { x: "16", y: bodyTop - 8, width: "88", height: "12", rx: "6", fill: strong }),
|
|
1478
|
+
/* @__PURE__ */ jsx("rect", { x: "20", y: bodyTop, width: "80", height: GROUND_Y - bodyTop, rx: "6", fill: soft }),
|
|
1479
|
+
windows.map((window2) => /* @__PURE__ */ jsx(
|
|
1480
|
+
"rect",
|
|
1481
|
+
{
|
|
1482
|
+
x: window2.x,
|
|
1483
|
+
y: window2.y,
|
|
1484
|
+
width: WINDOW_WIDTH,
|
|
1485
|
+
height: WINDOW_HEIGHT,
|
|
1486
|
+
rx: "3",
|
|
1487
|
+
fill: window2.lit ? "var(--assembleo-ambar)" : "var(--assembleo-surface)"
|
|
1488
|
+
},
|
|
1489
|
+
window2.key
|
|
1490
|
+
)),
|
|
1491
|
+
/* @__PURE__ */ jsx(
|
|
1492
|
+
"path",
|
|
1493
|
+
{
|
|
1494
|
+
d: `M52 ${GROUND_Y} v-${DOOR_HEIGHT - 8} a8 8 0 0 1 16 0 v${DOOR_HEIGHT - 8} z`,
|
|
1495
|
+
fill: strong
|
|
1496
|
+
}
|
|
1497
|
+
),
|
|
1498
|
+
/* @__PURE__ */ jsx("circle", { cx: "14", cy: GROUND_Y - 5, r: "6", fill: "var(--assembleo-jardim)", opacity: "0.85" }),
|
|
1499
|
+
/* @__PURE__ */ jsx("circle", { cx: "23", cy: GROUND_Y - 3, r: "4", fill: "var(--assembleo-jardim)", opacity: "0.65" })
|
|
1500
|
+
]
|
|
1501
|
+
}
|
|
1502
|
+
);
|
|
1503
|
+
}
|
|
1504
|
+
function VisuallyHidden({ children }) {
|
|
1505
|
+
return /* @__PURE__ */ jsx("span", { className: "absolute -m-px h-px w-px overflow-hidden whitespace-nowrap border-0 p-0 [clip-path:inset(50%)]", children });
|
|
1506
|
+
}
|
|
1507
|
+
var STORAGE_KEY = "assembleo-theme";
|
|
1508
|
+
function isTheme(value) {
|
|
1509
|
+
return value === "light" || value === "dark" || value === "system";
|
|
1510
|
+
}
|
|
1511
|
+
function readStoredTheme() {
|
|
1512
|
+
try {
|
|
1513
|
+
const stored = window.localStorage.getItem(STORAGE_KEY);
|
|
1514
|
+
return isTheme(stored) ? stored : "system";
|
|
1515
|
+
} catch {
|
|
1516
|
+
return "system";
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
function persistTheme(theme) {
|
|
1520
|
+
try {
|
|
1521
|
+
if (theme === "system") {
|
|
1522
|
+
window.localStorage.removeItem(STORAGE_KEY);
|
|
1523
|
+
} else {
|
|
1524
|
+
window.localStorage.setItem(STORAGE_KEY, theme);
|
|
1525
|
+
}
|
|
1526
|
+
} catch {
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
function applyTheme(theme) {
|
|
1530
|
+
if (theme === "system") {
|
|
1531
|
+
delete document.documentElement.dataset.theme;
|
|
1532
|
+
} else {
|
|
1533
|
+
document.documentElement.dataset.theme = theme;
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
function initTheme() {
|
|
1537
|
+
applyTheme(readStoredTheme());
|
|
1538
|
+
}
|
|
1539
|
+
var ThemeContext = createContext(null);
|
|
1540
|
+
function ThemeProvider({ children }) {
|
|
1541
|
+
const [theme, setThemeState] = useState(() => readStoredTheme());
|
|
1542
|
+
const [systemDark, setSystemDark] = useState(
|
|
1543
|
+
() => window.matchMedia("(prefers-color-scheme: dark)").matches
|
|
1544
|
+
);
|
|
1545
|
+
useEffect(() => {
|
|
1546
|
+
applyTheme(theme);
|
|
1547
|
+
persistTheme(theme);
|
|
1548
|
+
}, [theme]);
|
|
1549
|
+
useEffect(() => {
|
|
1550
|
+
const media = window.matchMedia("(prefers-color-scheme: dark)");
|
|
1551
|
+
const onChange = (event) => setSystemDark(event.matches);
|
|
1552
|
+
media.addEventListener("change", onChange);
|
|
1553
|
+
return () => media.removeEventListener("change", onChange);
|
|
1554
|
+
}, []);
|
|
1555
|
+
useEffect(() => {
|
|
1556
|
+
const onStorage = (event) => {
|
|
1557
|
+
if (event.key === STORAGE_KEY || event.key === null) {
|
|
1558
|
+
setThemeState(readStoredTheme());
|
|
1559
|
+
}
|
|
1560
|
+
};
|
|
1561
|
+
window.addEventListener("storage", onStorage);
|
|
1562
|
+
return () => window.removeEventListener("storage", onStorage);
|
|
1563
|
+
}, []);
|
|
1564
|
+
const resolvedTheme = theme === "system" ? systemDark ? "dark" : "light" : theme;
|
|
1565
|
+
return /* @__PURE__ */ jsx(ThemeContext.Provider, { value: { theme, resolvedTheme, setTheme: setThemeState }, children });
|
|
1566
|
+
}
|
|
1567
|
+
function useTheme() {
|
|
1568
|
+
const context = useContext(ThemeContext);
|
|
1569
|
+
if (!context) {
|
|
1570
|
+
throw new Error("useTheme precisa de um <ThemeProvider> acima na \xE1rvore.");
|
|
1571
|
+
}
|
|
1572
|
+
return context;
|
|
1573
|
+
}
|
|
1574
|
+
var OPTIONS = [
|
|
1575
|
+
{ value: "light", label: "Claro" },
|
|
1576
|
+
{ value: "dark", label: "Escuro" },
|
|
1577
|
+
{ value: "system", label: "Sistema" }
|
|
1578
|
+
];
|
|
1579
|
+
function ThemeToggle({ width, className }) {
|
|
1580
|
+
const { theme, setTheme } = useTheme();
|
|
1581
|
+
return /* @__PURE__ */ jsx(
|
|
1582
|
+
SegmentedControl,
|
|
1583
|
+
{
|
|
1584
|
+
"aria-label": "Tema",
|
|
1585
|
+
options: OPTIONS,
|
|
1586
|
+
value: theme,
|
|
1587
|
+
onValueChange: (value) => setTheme(value),
|
|
1588
|
+
width,
|
|
1589
|
+
className
|
|
1590
|
+
}
|
|
1591
|
+
);
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
export { Avatar, AvatarGroup, Badge, Banner, Button, Card, Checkbox, Cracha, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, EmptyState, Field, IconButton, Input, List, ListItem, Livreto, Popover, PopoverContent, PopoverTrigger, Predio, Progress, RadioGroup, RadioGroupItem, SearchField, SegmentedControl, Select, SelectItem, Separator, Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetTrigger, Skeleton, Spinner, Switch, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, ThemeProvider, ThemeToggle, ToastProvider, Tooltip, VisuallyHidden, cn, initTheme, useFieldContext, useTheme, useToast };
|