@softize/opus 12.10.0 → 13.0.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/CHANGELOG.md +56 -0
- package/bin/lib/check.mjs +1098 -310
- package/bin/lib/copy.mjs +12 -5
- package/docs/adr/0003-dictionary-presentation-is-declared.md +3 -0
- package/docs/adr/0004-page-content-state-is-composed.md +65 -0
- package/docs/adr/0005-structural-surfaces-share-an-explicit-anatomy.md +180 -0
- package/docs/adr/0006-semantic-context-precedes-visual-variant.md +182 -0
- package/docs/adr/0007-toast-actions-form-an-ordered-collection.md +63 -0
- package/docs/adr/0008-hierarchical-navigation-is-composed-at-the-consumer-boundary.md +71 -0
- package/docs/radius-scale.md +1 -1
- package/package.json +1 -1
- package/registry/instructions/opus.md +5 -0
- package/registry/skills/build-opus-ui/SKILL.md +27 -16
- package/registry/skills/build-opus-ui/references/evaluations.md +16 -5
- package/registry/skills/build-opus-ui/references/ui-patterns.md +38 -15
- package/registry/skills/maintain-opus-docs/SKILL.md +83 -0
- package/registry/skills/maintain-opus-docs/agents/openai.yaml +4 -0
- package/registry/skills/maintain-opus-docs/references/editorial-standard.md +85 -0
- package/registry/skills/maintain-opus-docs/references/evaluations.md +34 -0
- package/registry/skills/maintain-opus-docs/scripts/audit-docs.mjs +81 -0
- package/registry/skills/model-opus-dictionary/SKILL.md +4 -2
- package/registry/skills/model-opus-dictionary/references/evaluations.md +4 -3
- package/registry/templates/app/src/App.tsx +1 -1
- package/src/core/dictionary.ts +52 -14
- package/src/core/index.ts +10 -0
- package/src/core/ui-context.ts +29 -0
- package/src/schema/drivers/zod.ts +17 -8
- package/src/ui/components/patterns/action-form-card.tsx +18 -12
- package/src/ui/components/patterns/confirm.tsx +163 -40
- package/src/ui/components/patterns/content-header.tsx +335 -61
- package/src/ui/components/patterns/data-state.tsx +23 -10
- package/src/ui/components/patterns/list.tsx +1097 -783
- package/src/ui/components/patterns/page-state.tsx +115 -0
- package/src/ui/components/patterns/page.tsx +231 -41
- package/src/ui/components/patterns/sidebar.tsx +357 -83
- package/src/ui/components/patterns/trigger.tsx +37 -30
- package/src/ui/components/patterns/view.tsx +7 -11
- package/src/ui/components/primitives/alert.tsx +298 -110
- package/src/ui/components/primitives/ask.tsx +2 -1
- package/src/ui/components/primitives/badge.tsx +91 -30
- package/src/ui/components/primitives/button.tsx +99 -60
- package/src/ui/components/primitives/calendar.tsx +39 -39
- package/src/ui/components/primitives/card.tsx +96 -23
- package/src/ui/components/primitives/detail.tsx +2 -2
- package/src/ui/components/primitives/dialog.tsx +196 -39
- package/src/ui/components/primitives/dictionary-value.tsx +9 -14
- package/src/ui/components/primitives/dot.tsx +74 -21
- package/src/ui/components/primitives/drawer.tsx +40 -24
- package/src/ui/components/primitives/empty.tsx +3 -3
- package/src/ui/components/primitives/item.tsx +135 -79
- package/src/ui/components/primitives/menu.tsx +11 -3
- package/src/ui/components/primitives/metric-card.tsx +133 -0
- package/src/ui/components/primitives/sonner.tsx +187 -8
- package/src/ui/components/primitives/table.tsx +2 -2
- package/src/ui/docs/DocBrowser.tsx +104 -25
- package/src/ui/docs/changelog.tsx +1 -1
- package/src/ui/docs/content/accordion.md +22 -16
- package/src/ui/docs/content/action-form-card.md +8 -8
- package/src/ui/docs/content/action-form-dialog.md +9 -9
- package/src/ui/docs/content/action-form.md +28 -34
- package/src/ui/docs/content/action-list-dialog.md +11 -6
- package/src/ui/docs/content/action-list.md +64 -39
- package/src/ui/docs/content/action-trigger.md +21 -14
- package/src/ui/docs/content/action-view.md +8 -8
- package/src/ui/docs/content/actions.md +9 -9
- package/src/ui/docs/content/ai.md +3 -3
- package/src/ui/docs/content/alert.md +54 -28
- package/src/ui/docs/content/aspect-ratio.md +4 -4
- package/src/ui/docs/content/audit.md +2 -2
- package/src/ui/docs/content/auth.md +3 -3
- package/src/ui/docs/content/avatar.md +34 -14
- package/src/ui/docs/content/badge.md +21 -22
- package/src/ui/docs/content/breadcrumb.md +13 -8
- package/src/ui/docs/content/button.md +93 -15
- package/src/ui/docs/content/calendar.md +5 -5
- package/src/ui/docs/content/card.md +6 -6
- package/src/ui/docs/content/carousel.md +16 -11
- package/src/ui/docs/content/chat.md +3 -3
- package/src/ui/docs/content/checkbox.md +7 -7
- package/src/ui/docs/content/cli.md +5 -5
- package/src/ui/docs/content/collapsible.md +8 -8
- package/src/ui/docs/content/command.md +16 -8
- package/src/ui/docs/content/composer.md +2 -2
- package/src/ui/docs/content/content.md +44 -0
- package/src/ui/docs/content/copyable.md +4 -3
- package/src/ui/docs/content/customization.md +7 -7
- package/src/ui/docs/content/cycle.md +3 -3
- package/src/ui/docs/content/data-state.md +11 -12
- package/src/ui/docs/content/data.md +26 -33
- package/src/ui/docs/content/detail.md +8 -5
- package/src/ui/docs/content/dialog.md +339 -31
- package/src/ui/docs/content/dictionary-value.md +19 -18
- package/src/ui/docs/content/dock.md +3 -3
- package/src/ui/docs/content/dot.md +7 -7
- package/src/ui/docs/content/drawer.md +32 -16
- package/src/ui/docs/content/empty-value.md +2 -2
- package/src/ui/docs/content/empty.md +19 -12
- package/src/ui/docs/content/events.md +4 -4
- package/src/ui/docs/content/field.md +34 -12
- package/src/ui/docs/content/getting-started.md +1 -1
- package/src/ui/docs/content/icon-picker.md +8 -4
- package/src/ui/docs/content/input-otp.md +20 -12
- package/src/ui/docs/content/input.md +121 -9
- package/src/ui/docs/content/item.md +64 -24
- package/src/ui/docs/content/kbd.md +19 -11
- package/src/ui/docs/content/label.md +5 -3
- package/src/ui/docs/content/log.md +4 -4
- package/src/ui/docs/content/markdown.md +7 -6
- package/src/ui/docs/content/mcp.md +13 -15
- package/src/ui/docs/content/menu.md +36 -17
- package/src/ui/docs/content/metric-card.md +41 -0
- package/src/ui/docs/content/observability.md +2 -2
- package/src/ui/docs/content/page.md +93 -10
- package/src/ui/docs/content/pagination.md +22 -17
- package/src/ui/docs/content/popover.md +16 -8
- package/src/ui/docs/content/progress.md +7 -5
- package/src/ui/docs/content/queue.md +5 -5
- package/src/ui/docs/content/radio-group.md +20 -12
- package/src/ui/docs/content/router.md +11 -6
- package/src/ui/docs/content/scheduler.md +4 -5
- package/src/ui/docs/content/scroll-area.md +12 -7
- package/src/ui/docs/content/select.md +42 -29
- package/src/ui/docs/content/semantic-context.md +63 -0
- package/src/ui/docs/content/separator.md +5 -5
- package/src/ui/docs/content/sidebar.md +325 -56
- package/src/ui/docs/content/skeleton.md +5 -4
- package/src/ui/docs/content/slider.md +8 -7
- package/src/ui/docs/content/spinner.md +8 -8
- package/src/ui/docs/content/split.md +8 -5
- package/src/ui/docs/content/storage.md +6 -8
- package/src/ui/docs/content/switch.md +8 -7
- package/src/ui/docs/content/table.md +16 -6
- package/src/ui/docs/content/tabs.md +28 -14
- package/src/ui/docs/content/testing.md +9 -11
- package/src/ui/docs/content/textarea.md +5 -4
- package/src/ui/docs/content/toast.md +47 -13
- package/src/ui/docs/content/toggle.md +75 -7
- package/src/ui/docs/content/tokens.md +31 -3
- package/src/ui/docs/content/tooltip.md +19 -11
- package/src/ui/docs/content/truncate.md +7 -8
- package/src/ui/docs/content/ui.md +10 -9
- package/src/ui/docs/content/upgrading.md +7 -8
- package/src/ui/docs/doc-client.tsx +2 -2
- package/src/ui/docs/registry.tsx +580 -229
- package/src/ui/lib/semantic-context.ts +30 -0
- package/src/ui/meta.ts +278 -286
- package/src/ui/react.tsx +377 -111
- package/src/ui/theme.css +116 -0
- package/src/ui/components/primitives/alert-dialog.tsx +0 -190
- package/src/ui/docs/content/alert-dialog.md +0 -73
- package/src/ui/docs/content/button-group.md +0 -71
- package/src/ui/docs/content/confirm.md +0 -120
- package/src/ui/docs/content/input-group.md +0 -78
- package/src/ui/docs/content/toggle-group.md +0 -81
|
@@ -1,72 +1,115 @@
|
|
|
1
1
|
import * as React from 'react'
|
|
2
2
|
import { Slot } from '@radix-ui/react-slot'
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
import type { UiContext } from '../../../core/ui-context.ts'
|
|
4
5
|
import { cn } from '../../lib/cn.ts'
|
|
5
6
|
import type { ControlShape } from './control.ts'
|
|
6
7
|
import { Spinner } from './spinner.tsx'
|
|
7
8
|
|
|
9
|
+
export type ButtonContext = Extract<UiContext, 'neutral' | 'primary' | 'danger'>
|
|
10
|
+
export type ButtonVariant = 'solid' | 'subtle' | 'outline' | 'ghost' | 'link'
|
|
11
|
+
export type LegacyButtonVariant = 'default' | 'secondary' | 'destructive'
|
|
12
|
+
export type ButtonSize = 'default' | 'sm' | 'lg' | 'icon' | 'icon-sm' | 'icon-xs'
|
|
13
|
+
|
|
14
|
+
export interface ButtonStyleOptions {
|
|
15
|
+
context?: ButtonContext | null
|
|
16
|
+
variant?: ButtonVariant | LegacyButtonVariant | null
|
|
17
|
+
size?: ButtonSize | null
|
|
18
|
+
shape?: ControlShape | null
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const baseClasses =
|
|
22
|
+
'inline-flex shrink-0 items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium outline-none transition-all focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 active:brightness-90 aria-invalid:border-context-danger aria-invalid:ring-context-danger/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=size-])]:size-4'
|
|
23
|
+
|
|
24
|
+
const sizeClasses: Record<ButtonSize, string> = {
|
|
25
|
+
default: 'h-9 px-4 py-2 has-[>svg]:px-3',
|
|
26
|
+
sm: 'h-8 gap-1.5 px-3 has-[>svg]:px-2.5',
|
|
27
|
+
lg: 'h-10 px-6 has-[>svg]:px-4',
|
|
28
|
+
icon: 'size-9',
|
|
29
|
+
'icon-sm': 'size-8',
|
|
30
|
+
'icon-xs': 'size-6',
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const solidClasses: Record<ButtonContext, string> = {
|
|
34
|
+
neutral: 'bg-context-neutral text-context-neutral-foreground hover:brightness-95',
|
|
35
|
+
primary: 'bg-context-primary text-context-primary-foreground hover:brightness-95',
|
|
36
|
+
danger: 'bg-context-danger text-context-danger-foreground hover:brightness-95 focus-visible:ring-context-danger/20',
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const subtleClasses: Record<ButtonContext, string> = {
|
|
40
|
+
neutral: 'bg-context-neutral-subtle text-foreground hover:bg-context-neutral',
|
|
41
|
+
primary: 'bg-context-primary-subtle text-context-primary-emphasis hover:bg-context-primary/15',
|
|
42
|
+
danger: 'bg-context-danger-subtle text-context-danger-emphasis hover:bg-context-danger/15',
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const outlineClasses: Record<ButtonContext, string> = {
|
|
46
|
+
neutral: 'border border-input bg-background text-foreground hover:bg-accent hover:text-accent-foreground',
|
|
47
|
+
primary: 'border border-input bg-background text-foreground hover:bg-accent hover:text-accent-foreground',
|
|
48
|
+
danger: 'border border-context-danger-border bg-background text-context-danger-emphasis hover:bg-context-danger-subtle',
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const ghostClasses: Record<ButtonContext, string> = {
|
|
52
|
+
neutral: 'text-foreground hover:bg-accent hover:text-accent-foreground',
|
|
53
|
+
primary: 'text-context-primary-emphasis hover:bg-context-primary-subtle',
|
|
54
|
+
danger: 'text-context-danger-emphasis hover:bg-context-danger-subtle',
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const linkClasses: Record<ButtonContext, string> = {
|
|
58
|
+
neutral: 'text-foreground underline-offset-4 hover:underline',
|
|
59
|
+
primary: 'text-context-primary-emphasis underline-offset-4 hover:underline',
|
|
60
|
+
danger: 'text-context-danger-emphasis underline-offset-4 hover:underline',
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function resolveButtonStyle({ context, variant }: ButtonStyleOptions = {}): {
|
|
64
|
+
context: ButtonContext
|
|
65
|
+
variant: ButtonVariant
|
|
66
|
+
} {
|
|
67
|
+
const legacy = variant === 'default' || variant === 'secondary' || variant === 'destructive'
|
|
68
|
+
if (context != null && legacy) {
|
|
69
|
+
throw new Error('Button não permite combinar context com uma variante semântica legada.')
|
|
70
|
+
}
|
|
71
|
+
if (variant === 'default') return { context: 'primary', variant: 'solid' }
|
|
72
|
+
if (variant === 'secondary') return { context: 'neutral', variant: 'solid' }
|
|
73
|
+
if (variant === 'destructive') return { context: 'danger', variant: 'solid' }
|
|
74
|
+
return { context: context ?? 'primary', variant: variant ?? 'solid' }
|
|
75
|
+
}
|
|
76
|
+
|
|
8
77
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* `active:brightness-90` é toque da casa (feedback de clique) — fora do shadcn de propósito.
|
|
12
|
-
* Era `active:scale-95`, mas press GEOMÉTRICO move o rect do gatilho e todo popover
|
|
13
|
-
* ancorado persegue a âncora durante a transição (a "dançada" do menu).
|
|
78
|
+
* Classes do Button. `context` seleciona a família semântica e `variant` seleciona
|
|
79
|
+
* o tratamento visual. `default`, `secondary` e `destructive` são aliases temporários.
|
|
14
80
|
*/
|
|
15
|
-
export
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
outline
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
// Ícone na escala `sm` (2rem) — o par do `sm` para fileiras densas, onde os irmãos
|
|
35
|
-
// também usam `sm`. Sem ele, um botão de ícone ao lado desses controles ficava
|
|
36
|
-
// 0.25rem mais alto ou virava override local.
|
|
37
|
-
'icon-sm': 'size-8',
|
|
38
|
-
// Ícone menor (1.5rem) — a ação dentro de um campo (clear/mostrar senha no
|
|
39
|
-
// `trailing` do Input/Select), que precisa caber folgado num h-9. É o mesmo tamanho
|
|
40
|
-
// do `icon-xs` do InputGroupButton; sem ele, o exemplo virava `className="size-6"`.
|
|
41
|
-
'icon-xs': 'size-6',
|
|
42
|
-
},
|
|
43
|
-
shape: {
|
|
44
|
-
default: '',
|
|
45
|
-
pill: 'rounded-full',
|
|
46
|
-
},
|
|
47
|
-
},
|
|
48
|
-
defaultVariants: { variant: 'default', size: 'default', shape: 'default' },
|
|
49
|
-
},
|
|
50
|
-
)
|
|
81
|
+
export function buttonVariants(options: ButtonStyleOptions = {}): string {
|
|
82
|
+
const resolved = resolveButtonStyle(options)
|
|
83
|
+
const variantClasses =
|
|
84
|
+
resolved.variant === 'solid'
|
|
85
|
+
? solidClasses[resolved.context]
|
|
86
|
+
: resolved.variant === 'subtle'
|
|
87
|
+
? subtleClasses[resolved.context]
|
|
88
|
+
: resolved.variant === 'outline'
|
|
89
|
+
? outlineClasses[resolved.context]
|
|
90
|
+
: resolved.variant === 'ghost'
|
|
91
|
+
? ghostClasses[resolved.context]
|
|
92
|
+
: linkClasses[resolved.context]
|
|
93
|
+
return cn(
|
|
94
|
+
baseClasses,
|
|
95
|
+
variantClasses,
|
|
96
|
+
sizeClasses[options.size ?? 'default'],
|
|
97
|
+
options.shape === 'pill' && 'rounded-full',
|
|
98
|
+
)
|
|
99
|
+
}
|
|
51
100
|
|
|
52
|
-
export interface ButtonProps
|
|
53
|
-
extends React.ComponentProps<'button'>,
|
|
54
|
-
VariantProps<typeof buttonVariants> {
|
|
101
|
+
export interface ButtonProps extends React.ComponentProps<'button'>, ButtonStyleOptions {
|
|
55
102
|
/** Renderiza como o filho (Radix Slot) em vez de <button> — recompõe sem forkar. */
|
|
56
103
|
asChild?: boolean
|
|
57
|
-
/** Estado
|
|
58
|
-
* Spinner à esquerda e desabilita. NÃO é "carregando" (ANTES: conteúdo ainda não chegou
|
|
59
|
-
* → Spinner centralizado/Skeleton num nível de página, não no botão). Ignorado com asChild. */
|
|
104
|
+
/** Estado de ação em andamento; mostra Spinner e desabilita o controle. */
|
|
60
105
|
busy?: boolean
|
|
61
|
-
/** Ícone à esquerda
|
|
62
|
-
* pelo Spinner — não soma (evita ícone + spinner juntos). Ignorado com asChild. */
|
|
106
|
+
/** Ícone à esquerda. No `busy`, o Spinner ocupa o mesmo espaço. */
|
|
63
107
|
icon?: React.ElementType
|
|
64
|
-
/** Geometria do controle; `pill` não altera cor nem hierarquia da variante. */
|
|
65
|
-
shape?: ControlShape
|
|
66
108
|
}
|
|
67
109
|
|
|
68
110
|
export function Button({
|
|
69
111
|
className,
|
|
112
|
+
context,
|
|
70
113
|
variant,
|
|
71
114
|
size,
|
|
72
115
|
shape,
|
|
@@ -78,13 +121,14 @@ export function Button({
|
|
|
78
121
|
...props
|
|
79
122
|
}: ButtonProps): React.ReactElement {
|
|
80
123
|
const Comp = asChild ? Slot : 'button'
|
|
124
|
+
const resolved = resolveButtonStyle({ context, variant })
|
|
81
125
|
const iconSize = size === 'sm' ? 'size-3' : 'size-4'
|
|
82
|
-
// busy implica disabled; o feedback visual vem do disabled:opacity-50 da cva. O slot
|
|
83
|
-
// à esquerda é UM só: Spinner no busy, senão o ícone (icon e spinner nunca somam).
|
|
84
126
|
return (
|
|
85
127
|
<Comp
|
|
86
128
|
data-slot="button"
|
|
87
|
-
|
|
129
|
+
data-context={resolved.context}
|
|
130
|
+
data-variant={resolved.variant}
|
|
131
|
+
className={cn(buttonVariants({ ...resolved, size, shape }), className)}
|
|
88
132
|
disabled={disabled || busy}
|
|
89
133
|
{...props}
|
|
90
134
|
>
|
|
@@ -103,8 +147,3 @@ export function Button({
|
|
|
103
147
|
</Comp>
|
|
104
148
|
)
|
|
105
149
|
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Metadado de descoberta — lido pelo manifesto/MCP em build-time (não vai pro runtime;
|
|
109
|
-
* o barrel não re-exporta `meta`). Escrever o componente já o documenta.
|
|
110
|
-
*/
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import * as React from "react"
|
|
1
|
+
import * as React from "react";
|
|
2
2
|
import {
|
|
3
3
|
ChevronDownIcon,
|
|
4
4
|
ChevronLeftIcon,
|
|
5
5
|
ChevronRightIcon,
|
|
6
|
-
} from "lucide-react"
|
|
6
|
+
} from "lucide-react";
|
|
7
7
|
import {
|
|
8
8
|
DayPicker,
|
|
9
9
|
getDefaultClassNames,
|
|
10
10
|
type DayButton,
|
|
11
|
-
} from "react-day-picker"
|
|
11
|
+
} from "react-day-picker";
|
|
12
12
|
|
|
13
|
-
import { cn } from
|
|
14
|
-
import { Button, buttonVariants } from
|
|
13
|
+
import { cn } from "../../lib/cn.ts";
|
|
14
|
+
import { Button, buttonVariants } from "./button.tsx";
|
|
15
15
|
|
|
16
16
|
function Calendar({
|
|
17
17
|
className,
|
|
@@ -23,18 +23,18 @@ function Calendar({
|
|
|
23
23
|
components,
|
|
24
24
|
...props
|
|
25
25
|
}: React.ComponentProps<typeof DayPicker> & {
|
|
26
|
-
buttonVariant?: React.ComponentProps<typeof Button>["variant"]
|
|
26
|
+
buttonVariant?: React.ComponentProps<typeof Button>["variant"];
|
|
27
27
|
}) {
|
|
28
|
-
const defaultClassNames = getDefaultClassNames()
|
|
28
|
+
const defaultClassNames = getDefaultClassNames();
|
|
29
29
|
|
|
30
30
|
return (
|
|
31
31
|
<DayPicker
|
|
32
32
|
showOutsideDays={showOutsideDays}
|
|
33
33
|
className={cn(
|
|
34
|
-
"group/calendar bg-background p-3 [--cell-size:--spacing(8)] [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent",
|
|
34
|
+
"group/calendar bg-background p-3 [--cell-size:--spacing(8)] [[data-slot=card-body]_&]:bg-transparent [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent",
|
|
35
35
|
String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`,
|
|
36
36
|
String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
|
|
37
|
-
className
|
|
37
|
+
className,
|
|
38
38
|
)}
|
|
39
39
|
captionLayout={captionLayout}
|
|
40
40
|
formatters={{
|
|
@@ -46,85 +46,85 @@ function Calendar({
|
|
|
46
46
|
root: cn("w-fit", defaultClassNames.root),
|
|
47
47
|
months: cn(
|
|
48
48
|
"relative flex flex-col gap-4 md:flex-row",
|
|
49
|
-
defaultClassNames.months
|
|
49
|
+
defaultClassNames.months,
|
|
50
50
|
),
|
|
51
51
|
month: cn("flex w-full flex-col gap-4", defaultClassNames.month),
|
|
52
52
|
nav: cn(
|
|
53
53
|
"absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1",
|
|
54
|
-
defaultClassNames.nav
|
|
54
|
+
defaultClassNames.nav,
|
|
55
55
|
),
|
|
56
56
|
button_previous: cn(
|
|
57
57
|
buttonVariants({ variant: buttonVariant }),
|
|
58
58
|
"size-(--cell-size) p-0 select-none aria-disabled:opacity-50",
|
|
59
|
-
defaultClassNames.button_previous
|
|
59
|
+
defaultClassNames.button_previous,
|
|
60
60
|
),
|
|
61
61
|
button_next: cn(
|
|
62
62
|
buttonVariants({ variant: buttonVariant }),
|
|
63
63
|
"size-(--cell-size) p-0 select-none aria-disabled:opacity-50",
|
|
64
|
-
defaultClassNames.button_next
|
|
64
|
+
defaultClassNames.button_next,
|
|
65
65
|
),
|
|
66
66
|
month_caption: cn(
|
|
67
67
|
"flex h-(--cell-size) w-full items-center justify-center px-(--cell-size)",
|
|
68
|
-
defaultClassNames.month_caption
|
|
68
|
+
defaultClassNames.month_caption,
|
|
69
69
|
),
|
|
70
70
|
dropdowns: cn(
|
|
71
71
|
"flex h-(--cell-size) w-full items-center justify-center gap-1.5 text-sm font-medium",
|
|
72
|
-
defaultClassNames.dropdowns
|
|
72
|
+
defaultClassNames.dropdowns,
|
|
73
73
|
),
|
|
74
74
|
dropdown_root: cn(
|
|
75
75
|
"relative rounded-md border border-input has-focus:border-ring has-focus:ring-[0.1875rem] has-focus:ring-ring/50",
|
|
76
|
-
defaultClassNames.dropdown_root
|
|
76
|
+
defaultClassNames.dropdown_root,
|
|
77
77
|
),
|
|
78
78
|
dropdown: cn(
|
|
79
79
|
"absolute inset-0 bg-popover text-popover-foreground opacity-0",
|
|
80
|
-
defaultClassNames.dropdown
|
|
80
|
+
defaultClassNames.dropdown,
|
|
81
81
|
),
|
|
82
82
|
caption_label: cn(
|
|
83
83
|
"font-medium select-none",
|
|
84
84
|
captionLayout === "label"
|
|
85
85
|
? "text-sm"
|
|
86
86
|
: "flex h-8 items-center gap-1 rounded-md pr-1 pl-2 text-sm [&>svg]:size-3.5 [&>svg]:text-muted-foreground",
|
|
87
|
-
defaultClassNames.caption_label
|
|
87
|
+
defaultClassNames.caption_label,
|
|
88
88
|
),
|
|
89
89
|
month_grid: cn("w-full border-collapse", defaultClassNames.month_grid),
|
|
90
90
|
weekdays: cn("flex", defaultClassNames.weekdays),
|
|
91
91
|
weekday: cn(
|
|
92
92
|
"flex-1 rounded-md text-[0.8rem] font-normal text-muted-foreground select-none",
|
|
93
|
-
defaultClassNames.weekday
|
|
93
|
+
defaultClassNames.weekday,
|
|
94
94
|
),
|
|
95
95
|
week: cn("mt-2 flex w-full", defaultClassNames.week),
|
|
96
96
|
week_number_header: cn(
|
|
97
97
|
"w-(--cell-size) select-none",
|
|
98
|
-
defaultClassNames.week_number_header
|
|
98
|
+
defaultClassNames.week_number_header,
|
|
99
99
|
),
|
|
100
100
|
week_number: cn(
|
|
101
101
|
"text-[0.8rem] text-muted-foreground select-none",
|
|
102
|
-
defaultClassNames.week_number
|
|
102
|
+
defaultClassNames.week_number,
|
|
103
103
|
),
|
|
104
104
|
day: cn(
|
|
105
105
|
"group/day relative aspect-square h-full w-full p-0 text-center select-none [&:last-child[data-selected=true]_button]:rounded-r-md",
|
|
106
106
|
props.showWeekNumber
|
|
107
107
|
? "[&:nth-child(2)[data-selected=true]_button]:rounded-l-md"
|
|
108
108
|
: "[&:first-child[data-selected=true]_button]:rounded-l-md",
|
|
109
|
-
defaultClassNames.day
|
|
109
|
+
defaultClassNames.day,
|
|
110
110
|
),
|
|
111
111
|
range_start: cn(
|
|
112
112
|
"rounded-l-md bg-accent",
|
|
113
|
-
defaultClassNames.range_start
|
|
113
|
+
defaultClassNames.range_start,
|
|
114
114
|
),
|
|
115
115
|
range_middle: cn("rounded-none", defaultClassNames.range_middle),
|
|
116
116
|
range_end: cn("rounded-r-md bg-accent", defaultClassNames.range_end),
|
|
117
117
|
today: cn(
|
|
118
118
|
"rounded-md bg-accent text-accent-foreground data-[selected=true]:rounded-none",
|
|
119
|
-
defaultClassNames.today
|
|
119
|
+
defaultClassNames.today,
|
|
120
120
|
),
|
|
121
121
|
outside: cn(
|
|
122
122
|
"text-muted-foreground aria-selected:text-muted-foreground",
|
|
123
|
-
defaultClassNames.outside
|
|
123
|
+
defaultClassNames.outside,
|
|
124
124
|
),
|
|
125
125
|
disabled: cn(
|
|
126
126
|
"text-muted-foreground opacity-50",
|
|
127
|
-
defaultClassNames.disabled
|
|
127
|
+
defaultClassNames.disabled,
|
|
128
128
|
),
|
|
129
129
|
hidden: cn("invisible", defaultClassNames.hidden),
|
|
130
130
|
...classNames,
|
|
@@ -138,13 +138,13 @@ function Calendar({
|
|
|
138
138
|
className={cn(className)}
|
|
139
139
|
{...props}
|
|
140
140
|
/>
|
|
141
|
-
)
|
|
141
|
+
);
|
|
142
142
|
},
|
|
143
143
|
Chevron: ({ className, orientation, ...props }) => {
|
|
144
144
|
if (orientation === "left") {
|
|
145
145
|
return (
|
|
146
146
|
<ChevronLeftIcon className={cn("size-4", className)} {...props} />
|
|
147
|
-
)
|
|
147
|
+
);
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
if (orientation === "right") {
|
|
@@ -153,12 +153,12 @@ function Calendar({
|
|
|
153
153
|
className={cn("size-4", className)}
|
|
154
154
|
{...props}
|
|
155
155
|
/>
|
|
156
|
-
)
|
|
156
|
+
);
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
return (
|
|
160
160
|
<ChevronDownIcon className={cn("size-4", className)} {...props} />
|
|
161
|
-
)
|
|
161
|
+
);
|
|
162
162
|
},
|
|
163
163
|
DayButton: CalendarDayButton,
|
|
164
164
|
WeekNumber: ({ children, ...props }) => {
|
|
@@ -168,13 +168,13 @@ function Calendar({
|
|
|
168
168
|
{children}
|
|
169
169
|
</div>
|
|
170
170
|
</td>
|
|
171
|
-
)
|
|
171
|
+
);
|
|
172
172
|
},
|
|
173
173
|
...components,
|
|
174
174
|
}}
|
|
175
175
|
{...props}
|
|
176
176
|
/>
|
|
177
|
-
)
|
|
177
|
+
);
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
function CalendarDayButton({
|
|
@@ -183,12 +183,12 @@ function CalendarDayButton({
|
|
|
183
183
|
modifiers,
|
|
184
184
|
...props
|
|
185
185
|
}: React.ComponentProps<typeof DayButton>) {
|
|
186
|
-
const defaultClassNames = getDefaultClassNames()
|
|
186
|
+
const defaultClassNames = getDefaultClassNames();
|
|
187
187
|
|
|
188
|
-
const ref = React.useRef<HTMLButtonElement>(null)
|
|
188
|
+
const ref = React.useRef<HTMLButtonElement>(null);
|
|
189
189
|
React.useEffect(() => {
|
|
190
|
-
if (modifiers.focused) ref.current?.focus()
|
|
191
|
-
}, [modifiers.focused])
|
|
190
|
+
if (modifiers.focused) ref.current?.focus();
|
|
191
|
+
}, [modifiers.focused]);
|
|
192
192
|
|
|
193
193
|
return (
|
|
194
194
|
<Button
|
|
@@ -208,11 +208,11 @@ function CalendarDayButton({
|
|
|
208
208
|
className={cn(
|
|
209
209
|
"flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-[0.1875rem] group-data-[focused=true]/day:ring-ring/50 data-[range-end=true]:rounded-md data-[range-end=true]:rounded-r-md data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground data-[range-middle=true]:rounded-none data-[range-middle=true]:bg-accent data-[range-middle=true]:text-accent-foreground data-[range-start=true]:rounded-md data-[range-start=true]:rounded-l-md data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground dark:hover:text-accent-foreground [&>span]:text-xs [&>span]:opacity-70",
|
|
210
210
|
defaultClassNames.day,
|
|
211
|
-
className
|
|
211
|
+
className,
|
|
212
212
|
)}
|
|
213
213
|
{...props}
|
|
214
214
|
/>
|
|
215
|
-
)
|
|
215
|
+
);
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
-
export { Calendar, CalendarDayButton }
|
|
218
|
+
export { Calendar, CalendarDayButton };
|
|
@@ -1,56 +1,129 @@
|
|
|
1
|
-
import * as React from
|
|
2
|
-
import { Slot } from
|
|
3
|
-
import { cn } from
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
3
|
+
import { cn } from "../../lib/cn.ts";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Card — a superfície da casa: SÓ a casca (borda + raio + bg). Divergência DECLARADA vs
|
|
7
7
|
* shadcn: FLAT (sem `shadow-sm`) e SEM `flex/gap/py` forçados. O upstream embute
|
|
8
8
|
* `py-6 gap-6` + slots `px-6`, o que briga com card-box
|
|
9
9
|
* simples (o `gap-6` do Card + o `space-y` do consumidor = espaçamento dobrado) — por isso
|
|
10
|
-
* a casa hand-rollava. Aqui o padding mora no SLOT (Header/
|
|
10
|
+
* a casa hand-rollava. Aqui o padding mora no SLOT (Header/Body/Footer) ou no `className`
|
|
11
11
|
* do consumidor (box simples) — mesmo modelo do Dialog.
|
|
12
12
|
*
|
|
13
13
|
* Box simples: <Card className="p-4">…</Card>
|
|
14
|
-
* Estruturado: <Card><CardHeader>…</CardHeader><
|
|
14
|
+
* Estruturado: <Card><CardHeader>…</CardHeader><CardBody>…</CardBody><CardFooter>…</CardFooter></Card>
|
|
15
15
|
*/
|
|
16
16
|
export function Card({
|
|
17
17
|
className,
|
|
18
18
|
asChild = false,
|
|
19
19
|
...props
|
|
20
|
-
}: React.ComponentProps<
|
|
20
|
+
}: React.ComponentProps<"div"> & {
|
|
21
21
|
/** Renderiza como o filho (Radix Slot) — pra card CLICÁVEL manter a semântica do filho
|
|
22
22
|
* (ex.: <button>) com a superfície do Card: `<Card asChild><button onClick>…</button></Card>`. */
|
|
23
|
-
asChild?: boolean
|
|
23
|
+
asChild?: boolean;
|
|
24
24
|
}): React.ReactElement {
|
|
25
|
-
const Comp = asChild ? Slot :
|
|
25
|
+
const Comp = asChild ? Slot : "div";
|
|
26
26
|
return (
|
|
27
|
-
<Comp
|
|
28
|
-
|
|
27
|
+
<Comp
|
|
28
|
+
data-slot="card"
|
|
29
|
+
className={cn(
|
|
30
|
+
"rounded-xl border border-border bg-card text-card-foreground",
|
|
31
|
+
className,
|
|
32
|
+
)}
|
|
33
|
+
{...props}
|
|
34
|
+
/>
|
|
35
|
+
);
|
|
29
36
|
}
|
|
30
37
|
|
|
31
|
-
export function CardHeader({
|
|
38
|
+
export function CardHeader({
|
|
39
|
+
className,
|
|
40
|
+
...props
|
|
41
|
+
}: React.ComponentProps<"div">): React.ReactElement {
|
|
32
42
|
return (
|
|
33
|
-
<div
|
|
34
|
-
|
|
43
|
+
<div
|
|
44
|
+
data-slot="card-header"
|
|
45
|
+
className={cn("flex flex-col gap-1.5 px-5 pt-5 pb-3", className)}
|
|
46
|
+
{...props}
|
|
47
|
+
/>
|
|
48
|
+
);
|
|
35
49
|
}
|
|
36
50
|
|
|
37
|
-
export function CardTitle({
|
|
38
|
-
|
|
51
|
+
export function CardTitle({
|
|
52
|
+
className,
|
|
53
|
+
...props
|
|
54
|
+
}: React.ComponentProps<"div">): React.ReactElement {
|
|
55
|
+
return (
|
|
56
|
+
<div
|
|
57
|
+
data-slot="card-title"
|
|
58
|
+
className={cn("font-semibold leading-none", className)}
|
|
59
|
+
{...props}
|
|
60
|
+
/>
|
|
61
|
+
);
|
|
39
62
|
}
|
|
40
63
|
|
|
41
|
-
export function CardDescription({
|
|
42
|
-
|
|
64
|
+
export function CardDescription({
|
|
65
|
+
className,
|
|
66
|
+
...props
|
|
67
|
+
}: React.ComponentProps<"div">): React.ReactElement {
|
|
68
|
+
return (
|
|
69
|
+
<div
|
|
70
|
+
data-slot="card-description"
|
|
71
|
+
className={cn("text-sm text-muted-foreground", className)}
|
|
72
|
+
{...props}
|
|
73
|
+
/>
|
|
74
|
+
);
|
|
43
75
|
}
|
|
44
76
|
|
|
45
77
|
/** Slot de ação no header (botão/menu) — o consumidor posiciona (ex.: header em flex-row). */
|
|
46
|
-
export function CardAction({
|
|
47
|
-
|
|
78
|
+
export function CardAction({
|
|
79
|
+
className,
|
|
80
|
+
...props
|
|
81
|
+
}: React.ComponentProps<"div">): React.ReactElement {
|
|
82
|
+
return (
|
|
83
|
+
<div
|
|
84
|
+
data-slot="card-action"
|
|
85
|
+
className={cn("shrink-0", className)}
|
|
86
|
+
{...props}
|
|
87
|
+
/>
|
|
88
|
+
);
|
|
48
89
|
}
|
|
49
90
|
|
|
50
|
-
export function
|
|
51
|
-
|
|
91
|
+
export function CardBody({
|
|
92
|
+
className,
|
|
93
|
+
...props
|
|
94
|
+
}: React.ComponentProps<"div">): React.ReactElement {
|
|
95
|
+
return (
|
|
96
|
+
<div
|
|
97
|
+
data-slot="card-body"
|
|
98
|
+
className={cn("px-5 py-3", className)}
|
|
99
|
+
{...props}
|
|
100
|
+
/>
|
|
101
|
+
);
|
|
52
102
|
}
|
|
53
103
|
|
|
54
|
-
|
|
55
|
-
|
|
104
|
+
/** @deprecated Use CardBody. Mantido durante a versão 12 para migração gradual. */
|
|
105
|
+
export function CardContent({
|
|
106
|
+
className,
|
|
107
|
+
...props
|
|
108
|
+
}: React.ComponentProps<"div">): React.ReactElement {
|
|
109
|
+
return (
|
|
110
|
+
<div
|
|
111
|
+
data-slot="card-content"
|
|
112
|
+
className={cn("px-5 py-3", className)}
|
|
113
|
+
{...props}
|
|
114
|
+
/>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function CardFooter({
|
|
119
|
+
className,
|
|
120
|
+
...props
|
|
121
|
+
}: React.ComponentProps<"div">): React.ReactElement {
|
|
122
|
+
return (
|
|
123
|
+
<div
|
|
124
|
+
data-slot="card-footer"
|
|
125
|
+
className={cn("flex items-center px-5 pt-3 pb-5", className)}
|
|
126
|
+
{...props}
|
|
127
|
+
/>
|
|
128
|
+
);
|
|
56
129
|
}
|
|
@@ -41,7 +41,7 @@ function DetailGroup({
|
|
|
41
41
|
data-dividers={dividers}
|
|
42
42
|
data-orientation={orientation}
|
|
43
43
|
className={cn(
|
|
44
|
-
'group/detail-group grid min-w-0',
|
|
44
|
+
'group/detail-group grid min-w-0 [--detail-label-width:7rem]',
|
|
45
45
|
columnClasses[columns],
|
|
46
46
|
variant === 'plain' && !dividers && 'gap-x-6 gap-y-4',
|
|
47
47
|
dividers && 'gap-px overflow-hidden',
|
|
@@ -88,7 +88,7 @@ function DetailField({
|
|
|
88
88
|
data-slot="detail-field"
|
|
89
89
|
className={cn(
|
|
90
90
|
'grid min-w-0 items-start',
|
|
91
|
-
'group-data-[orientation=vertical]/detail-group:grid-cols-1 group-data-[orientation=horizontal]/detail-group:grid-cols-[minmax(0,
|
|
91
|
+
'group-data-[orientation=vertical]/detail-group:grid-cols-1 group-data-[orientation=horizontal]/detail-group:grid-cols-[minmax(0,var(--detail-label-width))_minmax(0,1fr)]',
|
|
92
92
|
'group-data-[orientation=vertical]/detail-group:gap-y-1 group-data-[orientation=horizontal]/detail-group:items-baseline group-data-[orientation=horizontal]/detail-group:gap-x-3',
|
|
93
93
|
'group-data-[dividers=true]/detail-group:outline group-data-[dividers=true]/detail-group:outline-1 group-data-[dividers=true]/detail-group:outline-border',
|
|
94
94
|
'group-data-[variant=framed]/detail-group:bg-card group-data-[variant=framed]/detail-group:text-card-foreground',
|