@holaboss/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 +116 -0
- package/dist/index.cjs +973 -0
- package/dist/index.d.cts +643 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +643 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +904 -0
- package/dist/index.js.map +1 -0
- package/dist/themes/holaos.css +121 -0
- package/dist/tokens.css +446 -0
- package/package.json +72 -0
- package/src/index.ts +108 -0
- package/src/layouts/dashboard-shell.tsx +43 -0
- package/src/layouts/data-table.tsx +110 -0
- package/src/layouts/error-state.tsx +52 -0
- package/src/layouts/filter-bar.tsx +58 -0
- package/src/layouts/loading-state.tsx +67 -0
- package/src/layouts/page-header.tsx +44 -0
- package/src/layouts/section.tsx +49 -0
- package/src/layouts/stat-pill.tsx +57 -0
- package/src/lib/utils.ts +6 -0
- package/src/primitives/alert.tsx +76 -0
- package/src/primitives/badge.tsx +52 -0
- package/src/primitives/button.tsx +61 -0
- package/src/primitives/card.tsx +107 -0
- package/src/primitives/dropdown-menu.tsx +270 -0
- package/src/primitives/empty-state.tsx +112 -0
- package/src/primitives/input.tsx +20 -0
- package/src/primitives/kbd.tsx +38 -0
- package/src/primitives/label.tsx +20 -0
- package/src/primitives/popover.tsx +87 -0
- package/src/primitives/select.tsx +202 -0
- package/src/primitives/status-dot.tsx +85 -0
- package/src/primitives/switch.tsx +19 -0
- package/src/primitives/tabs.tsx +80 -0
- package/src/primitives/tooltip.tsx +64 -0
- package/src/tokens/themes/holaos.css +121 -0
- package/src/tokens/tokens.css +446 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { Select as SelectPrimitive } from "@base-ui/react/select"
|
|
3
|
+
|
|
4
|
+
import { cn } from "../lib/utils"
|
|
5
|
+
import { ChevronDownIcon, CheckIcon, ChevronUpIcon } from "lucide-react"
|
|
6
|
+
|
|
7
|
+
const Select = SelectPrimitive.Root
|
|
8
|
+
|
|
9
|
+
function SelectGroup({ className, ...props }: SelectPrimitive.Group.Props) {
|
|
10
|
+
return (
|
|
11
|
+
<SelectPrimitive.Group
|
|
12
|
+
data-slot="select-group"
|
|
13
|
+
className={cn("scroll-my-1 p-1", className)}
|
|
14
|
+
{...props}
|
|
15
|
+
/>
|
|
16
|
+
)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function SelectValue({ className, ...props }: SelectPrimitive.Value.Props) {
|
|
20
|
+
return (
|
|
21
|
+
<SelectPrimitive.Value
|
|
22
|
+
data-slot="select-value"
|
|
23
|
+
className={cn("flex flex-1 text-left", className)}
|
|
24
|
+
{...props}
|
|
25
|
+
/>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function SelectTrigger({
|
|
30
|
+
className,
|
|
31
|
+
size = "default",
|
|
32
|
+
children,
|
|
33
|
+
...props
|
|
34
|
+
}: SelectPrimitive.Trigger.Props & {
|
|
35
|
+
size?: "sm" | "default"
|
|
36
|
+
}) {
|
|
37
|
+
return (
|
|
38
|
+
<SelectPrimitive.Trigger
|
|
39
|
+
data-slot="select-trigger"
|
|
40
|
+
data-size={size}
|
|
41
|
+
className={cn(
|
|
42
|
+
"flex w-fit items-center justify-between gap-1.5 rounded-lg border border-input bg-transparent py-2 pr-2 pl-2.5 text-sm whitespace-nowrap transition-colors outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-placeholder:text-muted-foreground data-[size=default]:h-8 data-[size=sm]:h-7 data-[size=sm]:rounded-[min(var(--radius-md),10px)] *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
43
|
+
className
|
|
44
|
+
)}
|
|
45
|
+
{...props}
|
|
46
|
+
>
|
|
47
|
+
{children}
|
|
48
|
+
<SelectPrimitive.Icon
|
|
49
|
+
render={
|
|
50
|
+
<ChevronDownIcon className="pointer-events-none size-4 text-muted-foreground" />
|
|
51
|
+
}
|
|
52
|
+
/>
|
|
53
|
+
</SelectPrimitive.Trigger>
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function SelectContent({
|
|
58
|
+
className,
|
|
59
|
+
children,
|
|
60
|
+
side = "bottom",
|
|
61
|
+
sideOffset = 4,
|
|
62
|
+
align = "center",
|
|
63
|
+
alignOffset = 0,
|
|
64
|
+
alignItemWithTrigger = true,
|
|
65
|
+
...props
|
|
66
|
+
}: SelectPrimitive.Popup.Props &
|
|
67
|
+
Pick<
|
|
68
|
+
SelectPrimitive.Positioner.Props,
|
|
69
|
+
"align" | "alignOffset" | "side" | "sideOffset" | "alignItemWithTrigger"
|
|
70
|
+
>) {
|
|
71
|
+
return (
|
|
72
|
+
<SelectPrimitive.Portal>
|
|
73
|
+
<SelectPrimitive.Positioner
|
|
74
|
+
side={side}
|
|
75
|
+
sideOffset={sideOffset}
|
|
76
|
+
align={align}
|
|
77
|
+
alignOffset={alignOffset}
|
|
78
|
+
alignItemWithTrigger={alignItemWithTrigger}
|
|
79
|
+
className="isolate z-50"
|
|
80
|
+
>
|
|
81
|
+
<SelectPrimitive.Popup
|
|
82
|
+
data-slot="select-content"
|
|
83
|
+
data-align-trigger={alignItemWithTrigger}
|
|
84
|
+
className={cn(
|
|
85
|
+
"dark isolate z-50 max-h-(--available-height) w-(--anchor-width) min-w-36 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg text-popover-foreground shadow-md ring-1 ring-border duration-100 data-[align-trigger=true]:animate-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 animate-none! relative bg-popover/70 before:pointer-events-none before:absolute before:inset-0 before:-z-1 before:rounded-[inherit] before:backdrop-blur-2xl before:backdrop-saturate-150 **:data-[slot$=-item]:focus:bg-foreground/10 **:data-[slot$=-item]:data-highlighted:bg-foreground/10 **:data-[slot$=-separator]:bg-fg-5 **:data-[slot$=-trigger]:focus:bg-foreground/10 **:data-[slot$=-trigger]:aria-expanded:bg-foreground/10! **:data-[variant=destructive]:focus:bg-foreground/10! **:data-[variant=destructive]:text-accent-foreground! **:data-[variant=destructive]:**:text-accent-foreground!",
|
|
86
|
+
className
|
|
87
|
+
)}
|
|
88
|
+
{...props}
|
|
89
|
+
>
|
|
90
|
+
<SelectScrollUpButton />
|
|
91
|
+
<SelectPrimitive.List>{children}</SelectPrimitive.List>
|
|
92
|
+
<SelectScrollDownButton />
|
|
93
|
+
</SelectPrimitive.Popup>
|
|
94
|
+
</SelectPrimitive.Positioner>
|
|
95
|
+
</SelectPrimitive.Portal>
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function SelectLabel({
|
|
100
|
+
className,
|
|
101
|
+
...props
|
|
102
|
+
}: SelectPrimitive.GroupLabel.Props) {
|
|
103
|
+
return (
|
|
104
|
+
<SelectPrimitive.GroupLabel
|
|
105
|
+
data-slot="select-label"
|
|
106
|
+
className={cn("px-1.5 py-1 text-xs text-muted-foreground", className)}
|
|
107
|
+
{...props}
|
|
108
|
+
/>
|
|
109
|
+
)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function SelectItem({
|
|
113
|
+
className,
|
|
114
|
+
children,
|
|
115
|
+
...props
|
|
116
|
+
}: SelectPrimitive.Item.Props) {
|
|
117
|
+
return (
|
|
118
|
+
<SelectPrimitive.Item
|
|
119
|
+
data-slot="select-item"
|
|
120
|
+
className={cn(
|
|
121
|
+
"relative flex w-full cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
|
|
122
|
+
className
|
|
123
|
+
)}
|
|
124
|
+
{...props}
|
|
125
|
+
>
|
|
126
|
+
<SelectPrimitive.ItemText className="flex flex-1 shrink-0 items-center gap-2 whitespace-nowrap">
|
|
127
|
+
{children}
|
|
128
|
+
</SelectPrimitive.ItemText>
|
|
129
|
+
<SelectPrimitive.ItemIndicator
|
|
130
|
+
render={
|
|
131
|
+
<span className="pointer-events-none absolute right-2 flex size-4 items-center justify-center" />
|
|
132
|
+
}
|
|
133
|
+
>
|
|
134
|
+
<CheckIcon className="pointer-events-none" />
|
|
135
|
+
</SelectPrimitive.ItemIndicator>
|
|
136
|
+
</SelectPrimitive.Item>
|
|
137
|
+
)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function SelectSeparator({
|
|
141
|
+
className,
|
|
142
|
+
...props
|
|
143
|
+
}: SelectPrimitive.Separator.Props) {
|
|
144
|
+
return (
|
|
145
|
+
<SelectPrimitive.Separator
|
|
146
|
+
data-slot="select-separator"
|
|
147
|
+
className={cn("pointer-events-none -mx-1 my-1 h-px bg-border", className)}
|
|
148
|
+
{...props}
|
|
149
|
+
/>
|
|
150
|
+
)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function SelectScrollUpButton({
|
|
154
|
+
className,
|
|
155
|
+
...props
|
|
156
|
+
}: React.ComponentProps<typeof SelectPrimitive.ScrollUpArrow>) {
|
|
157
|
+
return (
|
|
158
|
+
<SelectPrimitive.ScrollUpArrow
|
|
159
|
+
data-slot="select-scroll-up-button"
|
|
160
|
+
className={cn(
|
|
161
|
+
"top-0 z-10 flex w-full cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4",
|
|
162
|
+
className
|
|
163
|
+
)}
|
|
164
|
+
{...props}
|
|
165
|
+
>
|
|
166
|
+
<ChevronUpIcon
|
|
167
|
+
/>
|
|
168
|
+
</SelectPrimitive.ScrollUpArrow>
|
|
169
|
+
)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function SelectScrollDownButton({
|
|
173
|
+
className,
|
|
174
|
+
...props
|
|
175
|
+
}: React.ComponentProps<typeof SelectPrimitive.ScrollDownArrow>) {
|
|
176
|
+
return (
|
|
177
|
+
<SelectPrimitive.ScrollDownArrow
|
|
178
|
+
data-slot="select-scroll-down-button"
|
|
179
|
+
className={cn(
|
|
180
|
+
"bottom-0 z-10 flex w-full cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4",
|
|
181
|
+
className
|
|
182
|
+
)}
|
|
183
|
+
{...props}
|
|
184
|
+
>
|
|
185
|
+
<ChevronDownIcon
|
|
186
|
+
/>
|
|
187
|
+
</SelectPrimitive.ScrollDownArrow>
|
|
188
|
+
)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export {
|
|
192
|
+
Select,
|
|
193
|
+
SelectContent,
|
|
194
|
+
SelectGroup,
|
|
195
|
+
SelectItem,
|
|
196
|
+
SelectLabel,
|
|
197
|
+
SelectScrollDownButton,
|
|
198
|
+
SelectScrollUpButton,
|
|
199
|
+
SelectSeparator,
|
|
200
|
+
SelectTrigger,
|
|
201
|
+
SelectValue,
|
|
202
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { mergeProps } from "@base-ui/react/merge-props"
|
|
2
|
+
import { useRender } from "@base-ui/react/use-render"
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
4
|
+
|
|
5
|
+
import { cn } from "../lib/utils"
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* StatusDot — small colored dot signalling a state (running, error,
|
|
9
|
+
* working, idle, etc.). Replaces ~18 hand-rolled
|
|
10
|
+
* `<span className="size-X rounded-full bg-X" />` instances across the
|
|
11
|
+
* shell so a single change here propagates everywhere.
|
|
12
|
+
*
|
|
13
|
+
* Default size = `sm` (6px) which matches the dominant existing usage
|
|
14
|
+
* (status pip alongside text). Use `md` (8px) for slightly more
|
|
15
|
+
* presence (sidebar entry status), `lg` (10px) for stand-alone
|
|
16
|
+
* notification-style indicators.
|
|
17
|
+
*
|
|
18
|
+
* `withRing` adds a card-colored ring — used for badge dots that sit on
|
|
19
|
+
* top of an icon and need to read against the underlying surface.
|
|
20
|
+
*/
|
|
21
|
+
const statusDotVariants = cva("inline-block shrink-0 rounded-full", {
|
|
22
|
+
variants: {
|
|
23
|
+
variant: {
|
|
24
|
+
success: "bg-success",
|
|
25
|
+
destructive: "bg-destructive",
|
|
26
|
+
warning: "bg-warning",
|
|
27
|
+
info: "bg-info",
|
|
28
|
+
primary: "bg-primary",
|
|
29
|
+
muted: "bg-muted-foreground",
|
|
30
|
+
neutral: "bg-fg-24",
|
|
31
|
+
},
|
|
32
|
+
size: {
|
|
33
|
+
sm: "size-1.5",
|
|
34
|
+
md: "size-2",
|
|
35
|
+
lg: "size-2.5",
|
|
36
|
+
},
|
|
37
|
+
withRing: {
|
|
38
|
+
true: "border-2 border-card",
|
|
39
|
+
false: "",
|
|
40
|
+
},
|
|
41
|
+
pulse: {
|
|
42
|
+
true: "animate-pulse",
|
|
43
|
+
false: "",
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
defaultVariants: {
|
|
47
|
+
variant: "info",
|
|
48
|
+
size: "sm",
|
|
49
|
+
withRing: false,
|
|
50
|
+
pulse: false,
|
|
51
|
+
},
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
export type StatusDotProps = useRender.ComponentProps<"span"> &
|
|
55
|
+
VariantProps<typeof statusDotVariants>
|
|
56
|
+
|
|
57
|
+
export function StatusDot({
|
|
58
|
+
className,
|
|
59
|
+
variant,
|
|
60
|
+
size,
|
|
61
|
+
withRing,
|
|
62
|
+
pulse,
|
|
63
|
+
render,
|
|
64
|
+
...props
|
|
65
|
+
}: StatusDotProps) {
|
|
66
|
+
return useRender({
|
|
67
|
+
defaultTagName: "span",
|
|
68
|
+
props: mergeProps<"span">(
|
|
69
|
+
{
|
|
70
|
+
className: cn(
|
|
71
|
+
statusDotVariants({ variant, size, withRing, pulse }),
|
|
72
|
+
className,
|
|
73
|
+
),
|
|
74
|
+
"aria-hidden": true,
|
|
75
|
+
},
|
|
76
|
+
props,
|
|
77
|
+
),
|
|
78
|
+
render,
|
|
79
|
+
state: {
|
|
80
|
+
slot: "status-dot",
|
|
81
|
+
variant,
|
|
82
|
+
size,
|
|
83
|
+
},
|
|
84
|
+
})
|
|
85
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Switch as SwitchPrimitive } from "@base-ui/react/switch";
|
|
2
|
+
|
|
3
|
+
import { cn } from "../lib/utils";
|
|
4
|
+
|
|
5
|
+
function Switch({ className, ...props }: SwitchPrimitive.Root.Props) {
|
|
6
|
+
return (
|
|
7
|
+
<SwitchPrimitive.Root
|
|
8
|
+
className={cn(
|
|
9
|
+
"group/switch inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border border-transparent bg-input transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[checked]:bg-primary",
|
|
10
|
+
className,
|
|
11
|
+
)}
|
|
12
|
+
{...props}
|
|
13
|
+
>
|
|
14
|
+
<SwitchPrimitive.Thumb className="pointer-events-none block size-4 rounded-full bg-background shadow-sm ring-0 transition-transform data-[checked]:translate-x-4 data-[unchecked]:translate-x-0" />
|
|
15
|
+
</SwitchPrimitive.Root>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export { Switch };
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { Tabs as TabsPrimitive } from "@base-ui/react/tabs";
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
|
+
|
|
4
|
+
import { cn } from "../lib/utils";
|
|
5
|
+
|
|
6
|
+
function Tabs({
|
|
7
|
+
className,
|
|
8
|
+
orientation = "horizontal",
|
|
9
|
+
...props
|
|
10
|
+
}: TabsPrimitive.Root.Props) {
|
|
11
|
+
return (
|
|
12
|
+
<TabsPrimitive.Root
|
|
13
|
+
data-slot="tabs"
|
|
14
|
+
data-orientation={orientation}
|
|
15
|
+
className={cn(
|
|
16
|
+
"group/tabs flex gap-2 data-horizontal:flex-col",
|
|
17
|
+
className,
|
|
18
|
+
)}
|
|
19
|
+
{...props}
|
|
20
|
+
/>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const tabsListVariants = cva(
|
|
25
|
+
"group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground group-data-horizontal/tabs:h-8 group-data-vertical/tabs:h-fit group-data-vertical/tabs:flex-col data-[variant=line]:rounded-none",
|
|
26
|
+
{
|
|
27
|
+
variants: {
|
|
28
|
+
variant: {
|
|
29
|
+
default: "bg-muted",
|
|
30
|
+
line: "gap-1 bg-transparent",
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
defaultVariants: {
|
|
34
|
+
variant: "default",
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
function TabsList({
|
|
40
|
+
className,
|
|
41
|
+
variant = "default",
|
|
42
|
+
...props
|
|
43
|
+
}: TabsPrimitive.List.Props & VariantProps<typeof tabsListVariants>) {
|
|
44
|
+
return (
|
|
45
|
+
<TabsPrimitive.List
|
|
46
|
+
data-slot="tabs-list"
|
|
47
|
+
data-variant={variant}
|
|
48
|
+
className={cn(tabsListVariants({ variant }), className)}
|
|
49
|
+
{...props}
|
|
50
|
+
/>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function TabsTrigger({ className, ...props }: TabsPrimitive.Tab.Props) {
|
|
55
|
+
return (
|
|
56
|
+
<TabsPrimitive.Tab
|
|
57
|
+
data-slot="tabs-trigger"
|
|
58
|
+
className={cn(
|
|
59
|
+
"relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-1.5 py-0.5 text-sm font-medium whitespace-nowrap text-foreground transition-colors group-data-vertical/tabs:w-full group-data-vertical/tabs:justify-start hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 has-data-[icon=inline-end]:pr-1 has-data-[icon=inline-start]:pl-1 aria-disabled:pointer-events-none aria-disabled:opacity-50 dark:text-muted-foreground dark:hover:text-foreground group-data-[variant=default]/tabs-list:data-active:shadow-sm group-data-[variant=line]/tabs-list:data-active:shadow-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
60
|
+
"group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent dark:group-data-[variant=line]/tabs-list:data-active:border-transparent dark:group-data-[variant=line]/tabs-list:data-active:bg-transparent",
|
|
61
|
+
"data-active:bg-background data-active:text-foreground dark:data-active:border-input dark:data-active:bg-input/30 dark:data-active:text-foreground",
|
|
62
|
+
"after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-horizontal/tabs:after:inset-x-0 group-data-horizontal/tabs:after:bottom-[-5px] group-data-horizontal/tabs:after:h-0.5 group-data-vertical/tabs:after:inset-y-0 group-data-vertical/tabs:after:-right-1 group-data-vertical/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-active:after:opacity-100",
|
|
63
|
+
className,
|
|
64
|
+
)}
|
|
65
|
+
{...props}
|
|
66
|
+
/>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function TabsContent({ className, ...props }: TabsPrimitive.Panel.Props) {
|
|
71
|
+
return (
|
|
72
|
+
<TabsPrimitive.Panel
|
|
73
|
+
data-slot="tabs-content"
|
|
74
|
+
className={cn("flex-1 text-sm outline-none", className)}
|
|
75
|
+
{...props}
|
|
76
|
+
/>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Tooltip as TooltipPrimitive } from "@base-ui/react/tooltip"
|
|
2
|
+
|
|
3
|
+
import { cn } from "../lib/utils"
|
|
4
|
+
|
|
5
|
+
function TooltipProvider({
|
|
6
|
+
delay = 0,
|
|
7
|
+
...props
|
|
8
|
+
}: TooltipPrimitive.Provider.Props) {
|
|
9
|
+
return (
|
|
10
|
+
<TooltipPrimitive.Provider
|
|
11
|
+
data-slot="tooltip-provider"
|
|
12
|
+
delay={delay}
|
|
13
|
+
{...props}
|
|
14
|
+
/>
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function Tooltip({ ...props }: TooltipPrimitive.Root.Props) {
|
|
19
|
+
return <TooltipPrimitive.Root data-slot="tooltip" {...props} />
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function TooltipTrigger({ ...props }: TooltipPrimitive.Trigger.Props) {
|
|
23
|
+
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function TooltipContent({
|
|
27
|
+
className,
|
|
28
|
+
side = "top",
|
|
29
|
+
sideOffset = 4,
|
|
30
|
+
align = "center",
|
|
31
|
+
alignOffset = 0,
|
|
32
|
+
children,
|
|
33
|
+
...props
|
|
34
|
+
}: TooltipPrimitive.Popup.Props &
|
|
35
|
+
Pick<
|
|
36
|
+
TooltipPrimitive.Positioner.Props,
|
|
37
|
+
"align" | "alignOffset" | "side" | "sideOffset"
|
|
38
|
+
>) {
|
|
39
|
+
return (
|
|
40
|
+
<TooltipPrimitive.Portal>
|
|
41
|
+
<TooltipPrimitive.Positioner
|
|
42
|
+
align={align}
|
|
43
|
+
alignOffset={alignOffset}
|
|
44
|
+
side={side}
|
|
45
|
+
sideOffset={sideOffset}
|
|
46
|
+
className="isolate z-50"
|
|
47
|
+
>
|
|
48
|
+
<TooltipPrimitive.Popup
|
|
49
|
+
data-slot="tooltip-content"
|
|
50
|
+
className={cn(
|
|
51
|
+
"z-50 inline-flex w-fit max-w-xs origin-(--transform-origin) items-center gap-1.5 rounded-md bg-foreground px-3 py-1.5 text-xs text-background has-data-[slot=kbd]:pr-1.5 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
|
|
52
|
+
className
|
|
53
|
+
)}
|
|
54
|
+
{...props}
|
|
55
|
+
>
|
|
56
|
+
{children}
|
|
57
|
+
<TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground data-[side=bottom]:top-1 data-[side=inline-end]:top-1/2! data-[side=inline-end]:-left-1 data-[side=inline-end]:-translate-y-1/2 data-[side=inline-start]:top-1/2! data-[side=inline-start]:-right-1 data-[side=inline-start]:-translate-y-1/2 data-[side=left]:top-1/2! data-[side=left]:-right-1 data-[side=left]:-translate-y-1/2 data-[side=right]:top-1/2! data-[side=right]:-left-1 data-[side=right]:-translate-y-1/2 data-[side=top]:-bottom-2.5" />
|
|
58
|
+
</TooltipPrimitive.Popup>
|
|
59
|
+
</TooltipPrimitive.Positioner>
|
|
60
|
+
</TooltipPrimitive.Portal>
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/* holaOS — the brand-native theme. Quiet near-neutral grays with a cool
|
|
2
|
+
undertone and the holaboss orange reserved for brand/CTA. Hairlines read
|
|
3
|
+
as page rhythm, not cell walls; surfaces (card, muted, accent) sit close
|
|
4
|
+
enough to background that elevation comes from structure rather than tint. */
|
|
5
|
+
|
|
6
|
+
/* Light mode */
|
|
7
|
+
[data-theme="holaos-light"] {
|
|
8
|
+
--background: oklch(1 0 0);
|
|
9
|
+
--foreground: oklch(0.18 0.003 250);
|
|
10
|
+
--card: oklch(0.985 0.002 250);
|
|
11
|
+
--card-foreground: oklch(0.18 0.003 250);
|
|
12
|
+
--popover: oklch(1 0 0);
|
|
13
|
+
--popover-foreground: oklch(0.18 0.003 250);
|
|
14
|
+
--primary: oklch(0.624 0.229 32);
|
|
15
|
+
--primary-foreground: oklch(0.98 0.01 32);
|
|
16
|
+
--secondary: oklch(0.97 0.002 250);
|
|
17
|
+
--secondary-foreground: oklch(0.21 0.003 250);
|
|
18
|
+
--muted: oklch(0.97 0.002 250);
|
|
19
|
+
--muted-foreground: oklch(0.56 0.005 250);
|
|
20
|
+
--accent: oklch(0.95 0.002 250);
|
|
21
|
+
--accent-foreground: oklch(0.18 0.003 250);
|
|
22
|
+
--destructive: oklch(0.577 0.245 27.325);
|
|
23
|
+
--destructive-foreground: oklch(1 0 0);
|
|
24
|
+
--border: oklch(0.92 0.003 250);
|
|
25
|
+
--input: oklch(0.92 0.003 250);
|
|
26
|
+
--ring: oklch(0.624 0.229 32);
|
|
27
|
+
--chart-1: oklch(0.646 0.222 41.116);
|
|
28
|
+
--chart-2: oklch(0.6 0.118 184.704);
|
|
29
|
+
--chart-3: oklch(0.398 0.07 227.392);
|
|
30
|
+
--chart-4: oklch(0.828 0.189 84.429);
|
|
31
|
+
--chart-5: oklch(0.769 0.188 70.08);
|
|
32
|
+
--radius: 0.675rem;
|
|
33
|
+
--sidebar: oklch(0.945 0.002 250);
|
|
34
|
+
--sidebar-foreground: oklch(0.18 0.003 250);
|
|
35
|
+
--sidebar-primary: oklch(0.624 0.229 32);
|
|
36
|
+
--sidebar-primary-foreground: oklch(0.98 0.01 32);
|
|
37
|
+
--sidebar-accent: oklch(0.92 0.002 250);
|
|
38
|
+
--sidebar-accent-foreground: oklch(0.21 0.003 250);
|
|
39
|
+
--sidebar-border: oklch(0.9 0.003 250);
|
|
40
|
+
--sidebar-ring: oklch(0.624 0.229 32);
|
|
41
|
+
--font-sans: 'Inter Variable', sans-serif;
|
|
42
|
+
--font-serif: Source Serif 4, serif;
|
|
43
|
+
--font-mono: JetBrains Mono, monospace;
|
|
44
|
+
--spacing: 0.25rem;
|
|
45
|
+
--shadow-2xs:
|
|
46
|
+
inset 0 0.5px 1px #ffffff30, 0 0.5px 1px #0000000a, 0 1px 2px #00000006;
|
|
47
|
+
--shadow-xs:
|
|
48
|
+
inset 0 0.5px 1px #ffffff35, 0 0.5px 1px #0000000c, 0 1px 2px #00000008;
|
|
49
|
+
--shadow-sm:
|
|
50
|
+
inset 0 1px 2px #ffffff40, 0 1px 2px #00000012, 0 2px 4px #0000000a;
|
|
51
|
+
--shadow: inset 0 1px 2px #ffffff50, 0 2px 4px #00000014, 0 4px 6px #0000000c;
|
|
52
|
+
--shadow-md:
|
|
53
|
+
inset 0 1px 2px #ffffff55, 0 3px 6px #00000016, 0 6px 10px #0000000d;
|
|
54
|
+
--shadow-lg:
|
|
55
|
+
inset 0 1px 2px #ffffff60, 0 4px 8px #00000018, 0 8px 15px #00000010;
|
|
56
|
+
--shadow-xl:
|
|
57
|
+
inset 0 1px 2px #ffffff65, 0 6px 12px #0000001a, 0 12px 20px #00000012;
|
|
58
|
+
--shadow-2xl:
|
|
59
|
+
inset 0 1px 2px #ffffff70, 0 8px 15px #0000001c, 0 18px 28px #00000014;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* Dark mode */
|
|
63
|
+
[data-theme="holaos-dark"] {
|
|
64
|
+
--background: oklch(0.16 0.003 250);
|
|
65
|
+
--foreground: oklch(0.97 0.002 250);
|
|
66
|
+
--card: oklch(0.235 0.003 250);
|
|
67
|
+
--card-foreground: oklch(0.97 0.002 250);
|
|
68
|
+
--popover: oklch(0.235 0.003 250);
|
|
69
|
+
--popover-foreground: oklch(0.97 0.002 250);
|
|
70
|
+
--primary: oklch(0.624 0.229 32);
|
|
71
|
+
--primary-foreground: oklch(0.98 0.01 32);
|
|
72
|
+
--secondary: oklch(0.27 0.003 250);
|
|
73
|
+
--secondary-foreground: oklch(0.97 0.002 250);
|
|
74
|
+
/* Muted reads as a soft mid-tone above background so surfaces using
|
|
75
|
+
bg-muted — tab list rails, code blocks, disabled fields, the user
|
|
76
|
+
chat bubble base mix — actually read on a dark page. */
|
|
77
|
+
--muted: oklch(0.205 0.003 250);
|
|
78
|
+
--muted-foreground: oklch(0.66 0.005 250);
|
|
79
|
+
--accent: oklch(0.30 0.003 250);
|
|
80
|
+
--accent-foreground: oklch(0.97 0.002 250);
|
|
81
|
+
--destructive: oklch(0.653 0.232 25.964);
|
|
82
|
+
--destructive-foreground: oklch(1 0 0);
|
|
83
|
+
/* Border sits clearly above card (0.235) so hairlines on cards are
|
|
84
|
+
visible without being loud against the page surface. */
|
|
85
|
+
--border: oklch(0.32 0.003 250);
|
|
86
|
+
--input: oklch(0.32 0.003 250);
|
|
87
|
+
--ring: oklch(0.624 0.229 32);
|
|
88
|
+
--chart-1: oklch(0.44 0.004 250);
|
|
89
|
+
--chart-2: oklch(0.56 0.004 250);
|
|
90
|
+
--chart-3: oklch(0.71 0.004 250);
|
|
91
|
+
--chart-4: oklch(0.87 0.004 250);
|
|
92
|
+
--chart-5: oklch(0.92 0.004 250);
|
|
93
|
+
--radius: 0.675rem;
|
|
94
|
+
--sidebar: oklch(0.125 0.003 250);
|
|
95
|
+
--sidebar-foreground: oklch(0.97 0.002 250);
|
|
96
|
+
--sidebar-primary: oklch(0.624 0.229 32);
|
|
97
|
+
--sidebar-primary-foreground: oklch(0.97 0.002 250);
|
|
98
|
+
--sidebar-accent: oklch(0.20 0.003 250);
|
|
99
|
+
--sidebar-accent-foreground: oklch(0.97 0.002 250);
|
|
100
|
+
--sidebar-border: oklch(0.24 0.003 250);
|
|
101
|
+
--sidebar-ring: oklch(0.624 0.229 32);
|
|
102
|
+
--font-sans: 'Inter Variable', sans-serif;
|
|
103
|
+
--font-serif: Source Serif 4, serif;
|
|
104
|
+
--font-mono: JetBrains Mono, monospace;
|
|
105
|
+
--spacing: 0.25rem;
|
|
106
|
+
--shadow-2xs:
|
|
107
|
+
inset 0 0.5px 1px #ffffff0a, 0 0.5px 1px #00000050, 0 1px 2px #00000030;
|
|
108
|
+
--shadow-xs:
|
|
109
|
+
inset 0 0.5px 1px #ffffff0d, 0 0.5px 1px #00000050, 0 1px 2px #00000030;
|
|
110
|
+
--shadow-sm:
|
|
111
|
+
inset 0 1px 2px #ffffff15, 0 1px 2px #00000040, 0 2px 4px #00000025;
|
|
112
|
+
--shadow: inset 0 1px 2px #ffffff18, 0 1px 3px #00000045, 0 3px 6px #00000030;
|
|
113
|
+
--shadow-md:
|
|
114
|
+
inset 0 1px 2px #ffffff1a, 0 2px 4px #00000045, 0 4px 8px #00000028;
|
|
115
|
+
--shadow-lg:
|
|
116
|
+
inset 0 1px 2px #ffffff1c, 0 4px 6px #00000045, 0 6px 10px #00000030;
|
|
117
|
+
--shadow-xl:
|
|
118
|
+
inset 0 1px 2px #ffffff20, 0 6px 10px #00000045, 0 8px 15px #00000032;
|
|
119
|
+
--shadow-2xl:
|
|
120
|
+
inset 0 1px 2px #ffffff22, 0 10px 15px #00000045, 0 15px 25px #00000030;
|
|
121
|
+
}
|