@motor-hero/ui-kit 0.37.3 → 0.38.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@motor-hero/ui-kit",
3
- "version": "0.37.3",
3
+ "version": "0.38.0",
4
4
  "description": "Componentes React reutilizáveis para o ecossistema MotorHero — shadcn/ui + Tailwind CSS v4",
5
5
  "author": "MotorHero",
6
6
  "license": "UNLICENSED",
@@ -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,4 +1,5 @@
1
1
  import type { ReactNode } from "react"
2
+ import { EmptyState } from "./empty-state"
2
3
 
3
4
  interface ResponsiveDataViewProps {
4
5
  table: ReactNode
@@ -8,23 +9,44 @@ interface ResponsiveDataViewProps {
8
9
  emptyIcon?: ReactNode
9
10
  emptyTitle?: string
10
11
  emptyDescription?: string
12
+ /** CTA do estado vazio — normalmente o mesmo botão "Adicionar X" da página. */
13
+ emptyAction?: ReactNode
11
14
  pagination?: ReactNode
12
15
  }
13
16
 
14
17
  export function ResponsiveDataView({
15
- table, cards, isEmpty, isLoading, emptyIcon, emptyTitle = "Nenhum registro encontrado", emptyDescription, pagination,
18
+ table,
19
+ cards,
20
+ isEmpty,
21
+ isLoading,
22
+ emptyIcon,
23
+ emptyTitle = "Nenhum registro encontrado",
24
+ emptyDescription,
25
+ emptyAction,
26
+ pagination,
16
27
  }: ResponsiveDataViewProps) {
28
+ // Lista vazia mostra só o EmptyState. Antes renderizava a caixa com o
29
+ // cabeçalho da tabela vazio, o bloco de texto por fora dela e a paginação
30
+ // desabilitada — três blocos empilhados para zero registro.
31
+ if (!isLoading && isEmpty) {
32
+ return (
33
+ <div className="rounded-md border">
34
+ <EmptyState
35
+ icon={emptyIcon}
36
+ title={emptyTitle}
37
+ description={emptyDescription}
38
+ action={emptyAction}
39
+ />
40
+ </div>
41
+ )
42
+ }
43
+
17
44
  return (
18
45
  <div className="space-y-4">
19
- <div className="hidden overflow-x-auto rounded-md border md:block">{table}</div>
46
+ <div className="hidden overflow-x-auto rounded-md border md:block">
47
+ {table}
48
+ </div>
20
49
  <div className="md:hidden">{cards}</div>
21
- {!isLoading && isEmpty && (
22
- <div className="flex flex-col items-center justify-center py-16 text-center">
23
- {emptyIcon && <div className="mb-4 text-muted-foreground">{emptyIcon}</div>}
24
- <h3 className="text-lg font-semibold tracking-tight">{emptyTitle}</h3>
25
- {emptyDescription && <p className="mt-1 max-w-sm text-sm text-muted-foreground">{emptyDescription}</p>}
26
- </div>
27
- )}
28
50
  {pagination}
29
51
  </div>
30
52
  )
@@ -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: "bg-gray-100 text-gray-700 dark:bg-gray-800 dark:text-gray-300",
23
- dot: "bg-gray-400",
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: "bg-blue-100 text-blue-700 dark:bg-blue-950 dark:text-blue-400",
27
- dot: "bg-blue-500",
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: "bg-green-100 text-green-700 dark:bg-green-950 dark:text-green-400",
31
- dot: "bg-green-500",
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: "bg-red-100 text-red-700 dark:bg-red-950 dark:text-red-400",
35
- dot: "bg-red-500",
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: "bg-amber-100 text-amber-700 dark:bg-amber-950 dark:text-amber-400",
39
- dot: "bg-amber-500",
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-4 align-middle">
13
- <div className="h-5 w-full animate-pulse rounded bg-muted" />
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>