@motor-hero/ui-kit 0.37.4 → 0.39.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/dist/components/responsive-data-view.d.ts +9 -1
- package/dist/components/responsive-data-view.d.ts.map +1 -1
- package/dist/components/status-badge.d.ts.map +1 -1
- package/dist/components/table-skeleton.d.ts.map +1 -1
- package/dist/index.cjs +44 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +44 -20
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/confirm-dialog.tsx +1 -1
- package/src/components/form-dialog.tsx +1 -1
- package/src/components/responsive-data-view.tsx +50 -10
- package/src/components/status-badge.tsx +23 -10
- package/src/components/table-skeleton.tsx +5 -2
package/package.json
CHANGED
|
@@ -63,7 +63,7 @@ export function ConfirmDialog({
|
|
|
63
63
|
<AlertDialog.Action
|
|
64
64
|
onClick={onConfirm}
|
|
65
65
|
disabled={loading}
|
|
66
|
-
className={`inline-flex h-9 items-center justify-center rounded-md px-4 text-sm font-medium shadow transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 cursor-pointer ${
|
|
66
|
+
className={`inline-flex h-9 items-center justify-center rounded-md px-4 text-sm font-medium shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 cursor-pointer ${
|
|
67
67
|
variant === "destructive"
|
|
68
68
|
? "bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
|
69
69
|
: "bg-primary text-primary-foreground hover:bg-primary/90"
|
|
@@ -41,7 +41,7 @@ export function FormDialogLayout({
|
|
|
41
41
|
<button
|
|
42
42
|
type="submit"
|
|
43
43
|
disabled={isSubmitting || isDisabled}
|
|
44
|
-
className="inline-flex h-9 items-center justify-center rounded-md bg-primary px-4 text-sm font-medium text-primary-foreground shadow transition-colors hover:bg-primary/90 cursor-pointer disabled:pointer-events-none disabled:opacity-50"
|
|
44
|
+
className="inline-flex h-9 items-center justify-center rounded-md bg-primary px-4 text-sm font-medium text-primary-foreground shadow-sm transition-colors hover:bg-primary/90 cursor-pointer disabled:pointer-events-none disabled:opacity-50"
|
|
45
45
|
>
|
|
46
46
|
{isSubmitting ? "Salvando..." : submitLabel}
|
|
47
47
|
</button>
|
|
@@ -1,30 +1,70 @@
|
|
|
1
1
|
import type { ReactNode } from "react"
|
|
2
|
+
import { EmptyState } from "./empty-state"
|
|
3
|
+
|
|
4
|
+
import { cn } from "../lib/utils"
|
|
2
5
|
|
|
3
6
|
interface ResponsiveDataViewProps {
|
|
4
7
|
table: ReactNode
|
|
5
8
|
cards: ReactNode
|
|
6
9
|
isEmpty: boolean
|
|
7
10
|
isLoading: boolean
|
|
11
|
+
/**
|
|
12
|
+
* Dados desatualizados na tela enquanto um refetch está em voo (ex.:
|
|
13
|
+
* `isPlaceholderData` do TanStack Query). Esmaece e bloqueia a interação
|
|
14
|
+
* com table/cards; a paginação continua clicável.
|
|
15
|
+
*/
|
|
16
|
+
isBusy?: boolean
|
|
8
17
|
emptyIcon?: ReactNode
|
|
9
18
|
emptyTitle?: string
|
|
10
19
|
emptyDescription?: string
|
|
20
|
+
/** CTA do estado vazio — normalmente o mesmo botão "Adicionar X" da página. */
|
|
21
|
+
emptyAction?: ReactNode
|
|
11
22
|
pagination?: ReactNode
|
|
12
23
|
}
|
|
13
24
|
|
|
14
25
|
export function ResponsiveDataView({
|
|
15
|
-
table,
|
|
26
|
+
table,
|
|
27
|
+
cards,
|
|
28
|
+
isEmpty,
|
|
29
|
+
isLoading,
|
|
30
|
+
isBusy = false,
|
|
31
|
+
emptyIcon,
|
|
32
|
+
emptyTitle = "Nenhum registro encontrado",
|
|
33
|
+
emptyDescription,
|
|
34
|
+
emptyAction,
|
|
35
|
+
pagination,
|
|
16
36
|
}: ResponsiveDataViewProps) {
|
|
37
|
+
// Lista vazia mostra só o EmptyState. Antes renderizava a caixa com o
|
|
38
|
+
// cabeçalho da tabela vazio, o bloco de texto por fora dela e a paginação
|
|
39
|
+
// desabilitada — três blocos empilhados para zero registro.
|
|
40
|
+
if (!isLoading && !isBusy && isEmpty) {
|
|
41
|
+
return (
|
|
42
|
+
<div className="rounded-md border">
|
|
43
|
+
<EmptyState
|
|
44
|
+
icon={emptyIcon}
|
|
45
|
+
title={emptyTitle}
|
|
46
|
+
description={emptyDescription}
|
|
47
|
+
action={emptyAction}
|
|
48
|
+
/>
|
|
49
|
+
</div>
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const busyClass = isBusy && "pointer-events-none opacity-50"
|
|
17
54
|
return (
|
|
18
55
|
<div className="space-y-4">
|
|
19
|
-
<div
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
56
|
+
<div
|
|
57
|
+
aria-busy={isBusy}
|
|
58
|
+
className={cn(
|
|
59
|
+
"hidden overflow-x-auto rounded-md border transition-opacity md:block",
|
|
60
|
+
busyClass,
|
|
61
|
+
)}
|
|
62
|
+
>
|
|
63
|
+
{table}
|
|
64
|
+
</div>
|
|
65
|
+
<div aria-busy={isBusy} className={cn("transition-opacity md:hidden", busyClass)}>
|
|
66
|
+
{cards}
|
|
67
|
+
</div>
|
|
28
68
|
{pagination}
|
|
29
69
|
</div>
|
|
30
70
|
)
|
|
@@ -14,29 +14,42 @@ interface StatusBadgeProps {
|
|
|
14
14
|
children: ReactNode
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Cada variante lê os tokens semânticos `--status-*` do app, com fallback para
|
|
19
|
+
* a paleta Tailwind que o componente já usava. Um app que define os tokens
|
|
20
|
+
* (ex.: IMMA, de neutros quentes) ganha as cores do seu design system; um app
|
|
21
|
+
* que não define continua com a aparência de antes — a troca não quebra nenhum
|
|
22
|
+
* consumidor. Também dispensa `dark:`, já que os tokens de quem os define
|
|
23
|
+
* mudam sozinhos no escopo `.dark`.
|
|
24
|
+
*/
|
|
17
25
|
const variantClasses: Record<
|
|
18
26
|
StatusBadgeVariant,
|
|
19
27
|
{ badge: string; dot: string }
|
|
20
28
|
> = {
|
|
21
29
|
neutral: {
|
|
22
|
-
badge:
|
|
23
|
-
|
|
30
|
+
badge:
|
|
31
|
+
"bg-[var(--status-neutral-surface,#f3f4f6)] text-[var(--status-neutral-text,#374151)]",
|
|
32
|
+
dot: "bg-[var(--status-neutral,#9ca3af)]",
|
|
24
33
|
},
|
|
25
34
|
info: {
|
|
26
|
-
badge:
|
|
27
|
-
|
|
35
|
+
badge:
|
|
36
|
+
"bg-[var(--status-info-surface,#dbeafe)] text-[var(--status-info-text,#1d4ed8)]",
|
|
37
|
+
dot: "bg-[var(--status-info,#3b82f6)]",
|
|
28
38
|
},
|
|
29
39
|
success: {
|
|
30
|
-
badge:
|
|
31
|
-
|
|
40
|
+
badge:
|
|
41
|
+
"bg-[var(--status-success-surface,#dcfce7)] text-[var(--status-success-text,#15803d)]",
|
|
42
|
+
dot: "bg-[var(--status-success,#22c55e)]",
|
|
32
43
|
},
|
|
33
44
|
danger: {
|
|
34
|
-
badge:
|
|
35
|
-
|
|
45
|
+
badge:
|
|
46
|
+
"bg-[var(--status-danger-surface,#fee2e2)] text-[var(--status-danger-text,#b91c1c)]",
|
|
47
|
+
dot: "bg-[var(--status-danger,#ef4444)]",
|
|
36
48
|
},
|
|
37
49
|
warning: {
|
|
38
|
-
badge:
|
|
39
|
-
|
|
50
|
+
badge:
|
|
51
|
+
"bg-[var(--status-warning-surface,#fef3c7)] text-[var(--status-warning-text,#b45309)]",
|
|
52
|
+
dot: "bg-[var(--status-warning,#f59e0b)]",
|
|
40
53
|
},
|
|
41
54
|
}
|
|
42
55
|
|
|
@@ -7,10 +7,13 @@ export function TableSkeleton({ rows = 5, columns = 4 }: TableSkeletonProps) {
|
|
|
7
7
|
return (
|
|
8
8
|
<>
|
|
9
9
|
{Array.from({ length: rows }).map((_, i) => (
|
|
10
|
+
// p-2/h-4 espelha o TableCell real. Com p-4/h-5 a linha do skeleton
|
|
11
|
+
// ficava ~16px mais alta que a linha de dados, e a tabela encolhia
|
|
12
|
+
// visivelmente quando a resposta chegava.
|
|
10
13
|
<tr key={i} className="border-b transition-colors">
|
|
11
14
|
{Array.from({ length: columns }).map((_, j) => (
|
|
12
|
-
<td key={j} className="p-
|
|
13
|
-
<div className="h-
|
|
15
|
+
<td key={j} className="p-2 align-middle first:pl-4 last:pr-4">
|
|
16
|
+
<div className="h-4 w-full animate-pulse rounded bg-muted" />
|
|
14
17
|
</td>
|
|
15
18
|
))}
|
|
16
19
|
</tr>
|