@softize/opus 22.2.0 → 24.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 +120 -1
- package/bin/cli.mjs +3 -9
- package/bin/lib/check.mjs +242 -49
- package/bin/lib/copy.mjs +17 -35
- package/bin/lib/gen-runner.mjs +2 -0
- package/bin/lib/gen-stubs.mjs +3 -1
- package/docs/adr/0005-structural-surfaces-share-an-explicit-anatomy.md +1 -1
- package/docs/adr/0023-surfaces-share-one-anatomy-and-two-layouts.md +6 -0
- package/docs/adr/0032-tab-baseline-respects-the-container-inset.md +16 -12
- package/docs/adr/0034-tabs-can-own-a-frame-without-owning-a-surface.md +8 -6
- package/docs/adr/0036-tabs-declares-trigger-distribution.md +11 -8
- package/docs/adr/0037-design-data-lives-outside-the-contract.md +1 -1
- package/docs/adr/0038-content-level-defines-primary-presentation.md +62 -0
- package/docs/adr/0039-filter-presentation-is-independent-from-filter-type.md +62 -0
- package/docs/adr/0040-tabs-use-a-single-navigation-presentation.md +59 -0
- package/docs/adr/0042-toggle-group-shares-the-grouped-control-grammar.md +79 -0
- package/docs/adr/0043-neutral-surfaces-and-interactions-use-opaque-role-tokens.md +73 -0
- package/docs/adr/0044-neutral-appearance-derives-from-bounded-axes-per-mode.md +133 -0
- package/docs/adr/0045-surface-replaces-content-and-card.md +230 -0
- package/docs/adr/0046-structural-body-owns-content-rhythm.md +103 -0
- package/docs/adr/0047-pane-is-only-a-split-layout-unit.md +71 -0
- package/docs/adr/0048-modal-intro-separates-content-from-chrome.md +34 -0
- package/docs/code-style.md +1 -1
- package/package.json +5 -1
- package/registry/skills/build-opus-ui/SKILL.md +11 -11
- package/registry/skills/build-opus-ui/references/evaluations.md +10 -9
- package/registry/skills/build-opus-ui/references/ui-patterns.md +17 -16
- package/registry/skills/create-opus-action/references/contract-and-binding.md +3 -3
- package/registry/templates/app/src/App.tsx +24 -12
- package/src/core/contracts.ts +1 -1
- package/src/core/runtime.ts +4 -37
- package/src/core/types.ts +5 -23
- package/src/design/index.ts +14 -3
- package/src/testing/index.ts +6 -26
- package/src/ui/appearance.ts +149 -0
- package/src/ui/components/patterns/action-form-card.tsx +46 -33
- package/src/ui/components/patterns/action-list-dialog.tsx +2 -4
- package/src/ui/components/patterns/confirm.tsx +3 -1
- package/src/ui/components/patterns/fill-region.ts +8 -7
- package/src/ui/components/patterns/form-dialog.tsx +1 -3
- package/src/ui/components/patterns/form.tsx +8 -7
- package/src/ui/components/patterns/list.tsx +64 -14
- package/src/ui/components/patterns/page.tsx +15 -11
- package/src/ui/components/patterns/pane.tsx +9 -240
- package/src/ui/components/patterns/presentation.tsx +14 -14
- package/src/ui/components/patterns/sidebar.tsx +7 -17
- package/src/ui/components/patterns/split.tsx +111 -59
- package/src/ui/components/patterns/{surface-header.tsx → structural-header.tsx} +50 -19
- package/src/ui/components/patterns/structural-media.tsx +26 -0
- package/src/ui/components/patterns/surface-anatomy.ts +71 -61
- package/src/ui/components/patterns/surface-assistant.tsx +2 -4
- package/src/ui/components/patterns/surface.tsx +620 -0
- package/src/ui/components/patterns/trigger.tsx +11 -9
- package/src/ui/components/primitives/ask.tsx +155 -83
- package/src/ui/components/primitives/button.tsx +11 -9
- package/src/ui/components/primitives/checkbox.tsx +8 -8
- package/src/ui/components/primitives/command.tsx +1 -1
- package/src/ui/components/primitives/composer.tsx +47 -45
- package/src/ui/components/primitives/detail.tsx +9 -2
- package/src/ui/components/primitives/dialog.tsx +60 -26
- package/src/ui/components/primitives/drawer.tsx +58 -12
- package/src/ui/components/primitives/icon-picker.tsx +93 -70
- package/src/ui/components/primitives/input-group.tsx +35 -28
- package/src/ui/components/primitives/input-otp.tsx +16 -16
- package/src/ui/components/primitives/input.tsx +20 -10
- package/src/ui/components/primitives/item.tsx +9 -12
- package/src/ui/components/primitives/menu.tsx +44 -65
- package/src/ui/components/primitives/metric-card.tsx +75 -59
- package/src/ui/components/primitives/radio-group.tsx +9 -9
- package/src/ui/components/primitives/select.tsx +344 -209
- package/src/ui/components/primitives/switch.tsx +10 -10
- package/src/ui/components/primitives/table.tsx +29 -24
- package/src/ui/components/primitives/tabs.tsx +58 -88
- package/src/ui/components/primitives/textarea.tsx +6 -6
- package/src/ui/components/primitives/toggle-group-context.ts +24 -0
- package/src/ui/components/primitives/toggle-group.tsx +116 -74
- package/src/ui/components/primitives/toggle.tsx +82 -16
- package/src/ui/docs/DocBrowser.tsx +77 -69
- package/src/ui/docs/content/action-form-card.md +9 -9
- package/src/ui/docs/content/action-list.md +85 -70
- package/src/ui/docs/content/action-view.md +33 -18
- package/src/ui/docs/content/actions.md +6 -7
- package/src/ui/docs/content/button.md +1 -1
- package/src/ui/docs/content/communication.md +1 -1
- package/src/ui/docs/content/customization.md +64 -11
- package/src/ui/docs/content/detail.md +8 -6
- package/src/ui/docs/content/dialog.md +100 -58
- package/src/ui/docs/content/drawer.md +63 -19
- package/src/ui/docs/content/field.md +14 -15
- package/src/ui/docs/content/icon-tile.md +14 -13
- package/src/ui/docs/content/page.md +34 -26
- package/src/ui/docs/content/pane.md +41 -290
- package/src/ui/docs/content/presentation.md +3 -3
- package/src/ui/docs/content/semantic-context.md +0 -1
- package/src/ui/docs/content/separator.md +6 -6
- package/src/ui/docs/content/sidebar.md +68 -266
- package/src/ui/docs/content/skeleton.md +20 -18
- package/src/ui/docs/content/split.md +14 -14
- package/src/ui/docs/content/surface.md +513 -0
- package/src/ui/docs/content/tabs.md +82 -83
- package/src/ui/docs/content/testing.md +2 -3
- package/src/ui/docs/content/toggle.md +120 -63
- package/src/ui/docs/content/tokens.md +124 -40
- package/src/ui/docs/registry.tsx +4 -6
- package/src/ui/drivers/react.tsx +4 -3
- package/src/ui/index.ts +30 -27
- package/src/ui/meta.ts +15 -27
- package/src/ui/react.tsx +42 -46
- package/src/ui/theme.css +311 -64
- package/src/vite/design.ts +23 -21
- package/src/ui/components/patterns/content-header.tsx +0 -532
- package/src/ui/components/patterns/surface-media.tsx +0 -69
- package/src/ui/components/primitives/card.tsx +0 -191
- package/src/ui/docs/content/card.md +0 -292
- package/src/ui/docs/content/content.md +0 -276
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
import { Switch as SwitchPrimitive } from "radix-ui"
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Switch as SwitchPrimitive } from "radix-ui";
|
|
3
3
|
|
|
4
|
-
import { cn } from
|
|
5
|
-
import type { ControlSize } from
|
|
4
|
+
import { cn } from "../../lib/cn.ts";
|
|
5
|
+
import type { ControlSize } from "./control.ts";
|
|
6
6
|
|
|
7
7
|
/** `size` usa os nomes da escala de control.ts; a chave é um trilho (não uma linha), então a
|
|
8
8
|
* medida é a do glifo daquele tamanho: `sm` acompanha o svg de um controle sm (0.875rem). */
|
|
@@ -11,15 +11,15 @@ function Switch({
|
|
|
11
11
|
size = "default",
|
|
12
12
|
...props
|
|
13
13
|
}: React.ComponentProps<typeof SwitchPrimitive.Root> & {
|
|
14
|
-
size?: Extract<ControlSize, "sm" | "default"
|
|
14
|
+
size?: Extract<ControlSize, "sm" | "default">;
|
|
15
15
|
}) {
|
|
16
16
|
return (
|
|
17
17
|
<SwitchPrimitive.Root
|
|
18
18
|
data-slot="switch"
|
|
19
19
|
data-size={size}
|
|
20
20
|
className={cn(
|
|
21
|
-
"peer group/switch inline-flex shrink-0 items-center rounded-full border border-transparent transition-all outline-none focus-visible:border-ring focus-visible:ring-[0.1875rem] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-[1.15rem] data-[size=default]:w-8 data-[size=sm]:h-3.5 data-[size=sm]:w-6 data-[state=checked]:bg-primary data-[state=unchecked]:bg-
|
|
22
|
-
className
|
|
21
|
+
"peer group/switch inline-flex shrink-0 items-center rounded-full border border-transparent transition-all outline-none focus-visible:border-ring focus-visible:ring-[0.1875rem] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-[1.15rem] data-[size=default]:w-8 data-[size=sm]:h-3.5 data-[size=sm]:w-6 data-[state=checked]:bg-primary data-[state=unchecked]:bg-interaction-selected",
|
|
22
|
+
className,
|
|
23
23
|
)}
|
|
24
24
|
{...props}
|
|
25
25
|
>
|
|
@@ -27,11 +27,11 @@ function Switch({
|
|
|
27
27
|
data-slot="switch-thumb"
|
|
28
28
|
// px: o percurso do thumb desconta a borda (um pixel de cada lado) no calc do translate.
|
|
29
29
|
className={cn(
|
|
30
|
-
"pointer-events-none block rounded-full bg-background ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0 dark:data-[state=checked]:bg-primary-foreground dark:data-[state=unchecked]:bg-foreground"
|
|
30
|
+
"pointer-events-none block rounded-full bg-background ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0 dark:data-[state=checked]:bg-primary-foreground dark:data-[state=unchecked]:bg-foreground",
|
|
31
31
|
)}
|
|
32
32
|
/>
|
|
33
33
|
</SwitchPrimitive.Root>
|
|
34
|
-
)
|
|
34
|
+
);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
export { Switch }
|
|
37
|
+
export { Switch };
|
|
@@ -1,18 +1,23 @@
|
|
|
1
|
-
import * as React from "react"
|
|
1
|
+
import * as React from "react";
|
|
2
2
|
|
|
3
|
-
import { cn } from
|
|
3
|
+
import { cn } from "../../lib/cn.ts";
|
|
4
4
|
|
|
5
|
-
type TableVariant =
|
|
6
|
-
const TableVariantContext = React.createContext<TableVariant>(
|
|
5
|
+
type TableVariant = "plain" | "framed";
|
|
6
|
+
const TableVariantContext = React.createContext<TableVariant>("plain");
|
|
7
7
|
|
|
8
8
|
export interface TableProps extends React.ComponentProps<"table"> {
|
|
9
9
|
/** `framed` aplica a moldura canônica de datagrid no contêiner da tabela. */
|
|
10
|
-
variant?: TableVariant
|
|
10
|
+
variant?: TableVariant;
|
|
11
11
|
/** Classes do contêiner responsável pela rolagem e pela moldura da tabela. */
|
|
12
|
-
containerClassName?: string
|
|
12
|
+
containerClassName?: string;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
function Table({
|
|
15
|
+
function Table({
|
|
16
|
+
className,
|
|
17
|
+
containerClassName,
|
|
18
|
+
variant = "plain",
|
|
19
|
+
...props
|
|
20
|
+
}: TableProps) {
|
|
16
21
|
return (
|
|
17
22
|
<TableVariantContext.Provider value={variant}>
|
|
18
23
|
<div
|
|
@@ -20,7 +25,7 @@ function Table({ className, containerClassName, variant = 'plain', ...props }: T
|
|
|
20
25
|
data-variant={variant}
|
|
21
26
|
className={cn(
|
|
22
27
|
"relative w-full overflow-x-auto",
|
|
23
|
-
variant ===
|
|
28
|
+
variant === "framed" && "rounded-lg border border-border",
|
|
24
29
|
containerClassName,
|
|
25
30
|
)}
|
|
26
31
|
>
|
|
@@ -31,22 +36,22 @@ function Table({ className, containerClassName, variant = 'plain', ...props }: T
|
|
|
31
36
|
/>
|
|
32
37
|
</div>
|
|
33
38
|
</TableVariantContext.Provider>
|
|
34
|
-
)
|
|
39
|
+
);
|
|
35
40
|
}
|
|
36
41
|
|
|
37
42
|
function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
|
|
38
|
-
const variant = React.useContext(TableVariantContext)
|
|
43
|
+
const variant = React.useContext(TableVariantContext);
|
|
39
44
|
return (
|
|
40
45
|
<thead
|
|
41
46
|
data-slot="table-header"
|
|
42
47
|
className={cn(
|
|
43
48
|
"[&_tr]:border-b [&_tr:hover]:bg-transparent",
|
|
44
|
-
variant ===
|
|
49
|
+
variant === "framed" && "bg-control",
|
|
45
50
|
className,
|
|
46
51
|
)}
|
|
47
52
|
{...props}
|
|
48
53
|
/>
|
|
49
|
-
)
|
|
54
|
+
);
|
|
50
55
|
}
|
|
51
56
|
|
|
52
57
|
function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
|
|
@@ -56,7 +61,7 @@ function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
|
|
|
56
61
|
className={cn("[&_tr:last-child]:border-0", className)}
|
|
57
62
|
{...props}
|
|
58
63
|
/>
|
|
59
|
-
)
|
|
64
|
+
);
|
|
60
65
|
}
|
|
61
66
|
|
|
62
67
|
function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
|
|
@@ -65,11 +70,11 @@ function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
|
|
|
65
70
|
data-slot="table-footer"
|
|
66
71
|
className={cn(
|
|
67
72
|
"border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
|
|
68
|
-
className
|
|
73
|
+
className,
|
|
69
74
|
)}
|
|
70
75
|
{...props}
|
|
71
76
|
/>
|
|
72
|
-
)
|
|
77
|
+
);
|
|
73
78
|
}
|
|
74
79
|
|
|
75
80
|
function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
|
|
@@ -77,12 +82,12 @@ function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
|
|
|
77
82
|
<tr
|
|
78
83
|
data-slot="table-row"
|
|
79
84
|
className={cn(
|
|
80
|
-
"border-b transition-colors hover:bg-
|
|
81
|
-
className
|
|
85
|
+
"border-b transition-colors hover:bg-interaction-hover has-aria-expanded:bg-interaction-selected data-[state=selected]:bg-interaction-selected",
|
|
86
|
+
className,
|
|
82
87
|
)}
|
|
83
88
|
{...props}
|
|
84
89
|
/>
|
|
85
|
-
)
|
|
90
|
+
);
|
|
86
91
|
}
|
|
87
92
|
|
|
88
93
|
function TableHead({ className, ...props }: React.ComponentProps<"th">) {
|
|
@@ -91,11 +96,11 @@ function TableHead({ className, ...props }: React.ComponentProps<"th">) {
|
|
|
91
96
|
data-slot="table-head"
|
|
92
97
|
className={cn(
|
|
93
98
|
"h-10 px-3 text-left align-middle font-medium whitespace-nowrap text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[0.125rem]",
|
|
94
|
-
className
|
|
99
|
+
className,
|
|
95
100
|
)}
|
|
96
101
|
{...props}
|
|
97
102
|
/>
|
|
98
|
-
)
|
|
103
|
+
);
|
|
99
104
|
}
|
|
100
105
|
|
|
101
106
|
function TableCell({ className, ...props }: React.ComponentProps<"td">) {
|
|
@@ -104,11 +109,11 @@ function TableCell({ className, ...props }: React.ComponentProps<"td">) {
|
|
|
104
109
|
data-slot="table-cell"
|
|
105
110
|
className={cn(
|
|
106
111
|
"px-3 py-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[0.125rem]",
|
|
107
|
-
className
|
|
112
|
+
className,
|
|
108
113
|
)}
|
|
109
114
|
{...props}
|
|
110
115
|
/>
|
|
111
|
-
)
|
|
116
|
+
);
|
|
112
117
|
}
|
|
113
118
|
|
|
114
119
|
function TableCaption({
|
|
@@ -121,7 +126,7 @@ function TableCaption({
|
|
|
121
126
|
className={cn("mt-4 text-sm text-muted-foreground", className)}
|
|
122
127
|
{...props}
|
|
123
128
|
/>
|
|
124
|
-
)
|
|
129
|
+
);
|
|
125
130
|
}
|
|
126
131
|
|
|
127
132
|
export {
|
|
@@ -133,4 +138,4 @@ export {
|
|
|
133
138
|
TableRow,
|
|
134
139
|
TableCell,
|
|
135
140
|
TableCaption,
|
|
136
|
-
}
|
|
141
|
+
};
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
2
|
import { Tabs as TabsPrimitive } from "radix-ui";
|
|
4
3
|
|
|
5
4
|
import { cn } from "../../lib/cn.ts";
|
|
6
5
|
import { controlHeight, type ControlSize } from "./control.ts";
|
|
7
6
|
|
|
8
7
|
// Divergência da casa (ejetado): o Tabs ganhou `size` — Button e Select têm `sm`, o Tabs
|
|
9
|
-
// não tinha, e uma fileira densa ficava com
|
|
8
|
+
// não tinha, e uma fileira densa ficava com as abas 0.25rem mais altas
|
|
10
9
|
// que os irmãos. O tamanho flui pra TabsList via contexto (a altura é dela) e a medida é a
|
|
11
10
|
// da escala de control.ts.
|
|
12
11
|
type TabsSize = Extract<ControlSize, "default" | "sm">;
|
|
@@ -18,9 +17,6 @@ const TabsFramedContext = React.createContext(false);
|
|
|
18
17
|
type TabsOrientation = "horizontal" | "vertical";
|
|
19
18
|
const TabsOrientationContext =
|
|
20
19
|
React.createContext<TabsOrientation>("horizontal");
|
|
21
|
-
const TabsListVariantContext = React.createContext<"default" | "line">(
|
|
22
|
-
"default",
|
|
23
|
-
);
|
|
24
20
|
|
|
25
21
|
function Tabs({
|
|
26
22
|
className,
|
|
@@ -62,87 +58,64 @@ function Tabs({
|
|
|
62
58
|
);
|
|
63
59
|
}
|
|
64
60
|
|
|
65
|
-
const tabsListVariants = cva(
|
|
66
|
-
// A altura horizontal entra como classe simples no componente (abaixo), para que um
|
|
67
|
-
// `className="h-full"` do consumidor consiga substituí-la via tailwind-merge. Seletores
|
|
68
|
-
// condicionais por data-size têm especificidade maior e tornavam a altura inextensível.
|
|
69
|
-
"group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-[0.1875rem] text-muted-foreground group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col data-[variant=line]:rounded-none data-[variant=line]:p-0",
|
|
70
|
-
{
|
|
71
|
-
variants: {
|
|
72
|
-
variant: {
|
|
73
|
-
default: "bg-muted",
|
|
74
|
-
line: "gap-1 bg-transparent",
|
|
75
|
-
},
|
|
76
|
-
},
|
|
77
|
-
defaultVariants: {
|
|
78
|
-
variant: "default",
|
|
79
|
-
},
|
|
80
|
-
},
|
|
81
|
-
);
|
|
82
|
-
|
|
83
61
|
function TabsList({
|
|
84
62
|
className,
|
|
85
63
|
children,
|
|
86
64
|
style,
|
|
87
|
-
variant = "default",
|
|
88
65
|
...props
|
|
89
|
-
}: React.ComponentProps<typeof TabsPrimitive.List>
|
|
90
|
-
VariantProps<typeof tabsListVariants>) {
|
|
66
|
+
}: React.ComponentProps<typeof TabsPrimitive.List>) {
|
|
91
67
|
// O size vem do <Tabs> (contexto) — a altura mora aqui, na list.
|
|
92
68
|
const size = React.useContext(TabsSizeContext);
|
|
93
69
|
const orientation = React.useContext(TabsOrientationContext);
|
|
94
70
|
const distribution = React.useContext(TabsDistributionContext);
|
|
95
|
-
const
|
|
96
|
-
const hasHorizontalLine =
|
|
97
|
-
normalizedVariant === "line" && orientation === "horizontal";
|
|
71
|
+
const framed = React.useContext(TabsFramedContext);
|
|
72
|
+
const hasHorizontalLine = orientation === "horizontal";
|
|
98
73
|
return (
|
|
99
|
-
<
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
data-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
</TabsPrimitive.List>
|
|
141
|
-
</TabsListVariantContext.Provider>
|
|
74
|
+
<TabsPrimitive.List
|
|
75
|
+
data-slot="tabs-list"
|
|
76
|
+
data-size={size}
|
|
77
|
+
data-distribution={distribution}
|
|
78
|
+
className={cn(
|
|
79
|
+
"group/tabs-list inline-flex w-fit items-center justify-center gap-1 rounded-none bg-transparent p-0 text-muted-foreground group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col",
|
|
80
|
+
controlHeight[size],
|
|
81
|
+
distribution === "equal" && orientation === "horizontal" && "w-full",
|
|
82
|
+
framed && orientation === "horizontal" && "px-1 pt-1",
|
|
83
|
+
className,
|
|
84
|
+
)}
|
|
85
|
+
style={{
|
|
86
|
+
...(hasHorizontalLine
|
|
87
|
+
? {
|
|
88
|
+
position: "relative",
|
|
89
|
+
width: "100%",
|
|
90
|
+
height: "auto",
|
|
91
|
+
justifyContent: "flex-start",
|
|
92
|
+
}
|
|
93
|
+
: {}),
|
|
94
|
+
...style,
|
|
95
|
+
}}
|
|
96
|
+
{...props}
|
|
97
|
+
>
|
|
98
|
+
{hasHorizontalLine && (
|
|
99
|
+
<span
|
|
100
|
+
aria-hidden="true"
|
|
101
|
+
data-slot="tabs-line"
|
|
102
|
+
style={{
|
|
103
|
+
position: "absolute",
|
|
104
|
+
insetInline: "0rem",
|
|
105
|
+
bottom: 0,
|
|
106
|
+
borderBottomWidth: "0.0625rem",
|
|
107
|
+
borderBottomStyle: "solid",
|
|
108
|
+
borderBottomColor: "var(--border)",
|
|
109
|
+
pointerEvents: "none",
|
|
110
|
+
}}
|
|
111
|
+
/>
|
|
112
|
+
)}
|
|
113
|
+
{children}
|
|
114
|
+
</TabsPrimitive.List>
|
|
142
115
|
);
|
|
143
116
|
}
|
|
144
117
|
|
|
145
|
-
function
|
|
118
|
+
function TabsListItem({
|
|
146
119
|
className,
|
|
147
120
|
style,
|
|
148
121
|
icon,
|
|
@@ -152,17 +125,16 @@ function TabsTrigger({
|
|
|
152
125
|
/** Ícone decorativo antes do rótulo. Em uma aba sem texto, declare também `aria-label`. */
|
|
153
126
|
icon?: React.ReactNode;
|
|
154
127
|
}) {
|
|
155
|
-
const variant = React.useContext(TabsListVariantContext);
|
|
156
128
|
const orientation = React.useContext(TabsOrientationContext);
|
|
157
129
|
const size = React.useContext(TabsSizeContext);
|
|
158
130
|
const distribution = React.useContext(TabsDistributionContext);
|
|
159
131
|
const framed = React.useContext(TabsFramedContext);
|
|
160
|
-
const hasHorizontalLine =
|
|
132
|
+
const hasHorizontalLine = orientation === "horizontal";
|
|
161
133
|
const content = (
|
|
162
134
|
<>
|
|
163
135
|
{icon !== undefined && (
|
|
164
136
|
<span
|
|
165
|
-
data-slot="tabs-
|
|
137
|
+
data-slot="tabs-list-item-icon"
|
|
166
138
|
aria-hidden="true"
|
|
167
139
|
className="flex size-4 shrink-0 items-center justify-center [&_svg]:size-4"
|
|
168
140
|
>
|
|
@@ -174,17 +146,15 @@ function TabsTrigger({
|
|
|
174
146
|
);
|
|
175
147
|
return (
|
|
176
148
|
<TabsPrimitive.Trigger
|
|
177
|
-
data-slot="tabs-
|
|
149
|
+
data-slot="tabs-list-item"
|
|
178
150
|
// px: altura e indicador compensam a hairline do contêiner (um pixel físico, não escala).
|
|
179
151
|
className={cn(
|
|
180
|
-
"relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap text-foreground/60 transition-all group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:justify-start hover:text-foreground focus-visible:border-ring focus-visible:ring-[0.1875rem] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50
|
|
181
|
-
"
|
|
182
|
-
"data-[state=active]:bg-background data-[state=active]:text-foreground dark:data-[state=active]:border-input dark:data-[state=active]:text-foreground",
|
|
183
|
-
"after:absolute after:bg-primary after:opacity-0 after:transition-opacity group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:bottom-[-1px] group-data-[orientation=horizontal]/tabs:after:h-0.5 group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:-right-1 group-data-[orientation=vertical]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-[state=active]:after:opacity-100",
|
|
152
|
+
"relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap text-foreground/60 transition-all group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:justify-start hover:text-foreground focus-visible:border-ring focus-visible:ring-[0.1875rem] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 data-[state=active]:text-foreground data-[state=active]:shadow-none dark:text-muted-foreground dark:hover:text-foreground dark:data-[state=active]:border-transparent dark:data-[state=active]:text-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
153
|
+
"after:absolute after:bg-primary after:opacity-0 after:transition-opacity group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:bottom-[-1px] group-data-[orientation=horizontal]/tabs:after:h-0.5 group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:-right-1 group-data-[orientation=vertical]/tabs:after:w-0.5 data-[state=active]:after:opacity-100",
|
|
184
154
|
hasHorizontalLine &&
|
|
185
155
|
cn(
|
|
186
|
-
"group/tab-
|
|
187
|
-
framed ? "pb-
|
|
156
|
+
"group/tab-list-item rounded-none px-0 py-0 hover:bg-transparent active:brightness-100 data-[state=active]:bg-transparent data-[state=active]:hover:bg-transparent",
|
|
157
|
+
framed ? "pb-1" : "pb-1.5",
|
|
188
158
|
),
|
|
189
159
|
className,
|
|
190
160
|
)}
|
|
@@ -202,9 +172,9 @@ function TabsTrigger({
|
|
|
202
172
|
>
|
|
203
173
|
{hasHorizontalLine ? (
|
|
204
174
|
<span
|
|
205
|
-
data-slot="tabs-
|
|
175
|
+
data-slot="tabs-list-item-content"
|
|
206
176
|
className={cn(
|
|
207
|
-
"inline-flex items-center justify-center gap-1.5 rounded-md px-3 transition-colors hover:bg-
|
|
177
|
+
"inline-flex items-center justify-center gap-1.5 rounded-md px-3 transition-colors hover:bg-interaction-hover hover:text-accent-foreground active:bg-interaction-pressed group-data-[state=active]/tab-list-item:text-foreground",
|
|
208
178
|
distribution === "equal" && "w-full",
|
|
209
179
|
controlHeight[size],
|
|
210
180
|
)}
|
|
@@ -218,17 +188,17 @@ function TabsTrigger({
|
|
|
218
188
|
);
|
|
219
189
|
}
|
|
220
190
|
|
|
221
|
-
function
|
|
191
|
+
function TabsPanel({
|
|
222
192
|
className,
|
|
223
193
|
...props
|
|
224
194
|
}: React.ComponentProps<typeof TabsPrimitive.Content>) {
|
|
225
195
|
return (
|
|
226
196
|
<TabsPrimitive.Content
|
|
227
|
-
data-slot="tabs-
|
|
197
|
+
data-slot="tabs-panel"
|
|
228
198
|
className={cn("flex-1 outline-none", className)}
|
|
229
199
|
{...props}
|
|
230
200
|
/>
|
|
231
201
|
);
|
|
232
202
|
}
|
|
233
203
|
|
|
234
|
-
export { Tabs, TabsList,
|
|
204
|
+
export { Tabs, TabsList, TabsListItem, TabsPanel };
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import * as React from "react"
|
|
1
|
+
import * as React from "react";
|
|
2
2
|
|
|
3
|
-
import { cn } from
|
|
3
|
+
import { cn } from "../../lib/cn.ts";
|
|
4
4
|
|
|
5
5
|
function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
|
|
6
6
|
return (
|
|
7
7
|
<textarea
|
|
8
8
|
data-slot="textarea"
|
|
9
9
|
className={cn(
|
|
10
|
-
"flex field-sizing-content min-h-16 w-full rounded-md border border-input bg-
|
|
11
|
-
className
|
|
10
|
+
"flex field-sizing-content min-h-16 w-full rounded-md border border-input bg-control px-3 py-2 text-sm transition-[color,box-shadow] outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-[0.1875rem] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-context-danger aria-invalid:ring-context-danger/20 dark:aria-invalid:ring-context-danger/40",
|
|
11
|
+
className,
|
|
12
12
|
)}
|
|
13
13
|
{...props}
|
|
14
14
|
/>
|
|
15
|
-
)
|
|
15
|
+
);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
export { Textarea }
|
|
18
|
+
export { Textarea };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import type { ControlShape } from "./control.ts";
|
|
4
|
+
|
|
5
|
+
type ToggleGroupMode = "connected" | "spaced" | "divided" | "segmented";
|
|
6
|
+
type ToggleGroupDistribution = "content" | "equal";
|
|
7
|
+
|
|
8
|
+
type ToggleGroupContextValue = {
|
|
9
|
+
mode: ToggleGroupMode;
|
|
10
|
+
orientation: "horizontal" | "vertical";
|
|
11
|
+
variant?: "default" | "outline" | null;
|
|
12
|
+
size?: "default" | "sm" | "lg" | null;
|
|
13
|
+
shape?: ControlShape | null;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const ToggleGroupContext = React.createContext<ToggleGroupContextValue | null>(
|
|
17
|
+
null,
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
export {
|
|
21
|
+
ToggleGroupContext,
|
|
22
|
+
type ToggleGroupDistribution,
|
|
23
|
+
type ToggleGroupMode,
|
|
24
|
+
};
|