@boyernick/standard-ui-react 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 +9 -0
- package/package.json +46 -0
- package/src/accordion.tsx +84 -0
- package/src/alert-dialog.tsx +101 -0
- package/src/attachment.tsx +138 -0
- package/src/autocomplete.tsx +281 -0
- package/src/avatar.tsx +56 -0
- package/src/badge.tsx +28 -0
- package/src/brand.tsx +70 -0
- package/src/breadcrumb.tsx +81 -0
- package/src/button.tsx +129 -0
- package/src/calendar.tsx +84 -0
- package/src/card.tsx +54 -0
- package/src/carousel.tsx +253 -0
- package/src/chart.tsx +185 -0
- package/src/checkbox-group.tsx +14 -0
- package/src/checkbox.tsx +31 -0
- package/src/code-block.tsx +124 -0
- package/src/collapsible.tsx +61 -0
- package/src/combobox.tsx +324 -0
- package/src/command.tsx +377 -0
- package/src/context-menu.tsx +250 -0
- package/src/dialog.tsx +78 -0
- package/src/drawer.tsx +156 -0
- package/src/empty.tsx +52 -0
- package/src/field.tsx +75 -0
- package/src/fieldset.tsx +25 -0
- package/src/form.tsx +14 -0
- package/src/icons.tsx +91 -0
- package/src/illustrations.tsx +167 -0
- package/src/image-modal.tsx +110 -0
- package/src/index.ts +833 -0
- package/src/input.tsx +68 -0
- package/src/lib/cn.ts +3 -0
- package/src/lib/motion.ts +23 -0
- package/src/markdown-editor.tsx +302 -0
- package/src/menu.tsx +205 -0
- package/src/menubar.tsx +22 -0
- package/src/meter.tsx +61 -0
- package/src/navigation-menu.tsx +217 -0
- package/src/number-field.tsx +119 -0
- package/src/orb.tsx +33 -0
- package/src/otp-field.tsx +45 -0
- package/src/pagination.tsx +91 -0
- package/src/popover.tsx +92 -0
- package/src/preview-card.tsx +103 -0
- package/src/progress.tsx +60 -0
- package/src/radio.tsx +43 -0
- package/src/scroll-area.tsx +67 -0
- package/src/select.tsx +161 -0
- package/src/separator.tsx +17 -0
- package/src/sidebar.tsx +82 -0
- package/src/skeleton.tsx +32 -0
- package/src/slider.tsx +56 -0
- package/src/sounds.tsx +270 -0
- package/src/spinner.tsx +45 -0
- package/src/switch.tsx +19 -0
- package/src/table.tsx +81 -0
- package/src/tabs.tsx +62 -0
- package/src/text-animate.tsx +155 -0
- package/src/textarea.tsx +58 -0
- package/src/ticker.tsx +74 -0
- package/src/toast.tsx +142 -0
- package/src/toggle.tsx +32 -0
- package/src/toolbar.tsx +75 -0
- package/src/tooltip.tsx +55 -0
- package/src/video-player.tsx +241 -0
package/src/avatar.tsx
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { Avatar as BaseAvatar } from "@base-ui/react/avatar"
|
|
4
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
5
|
+
import type { ComponentProps } from "react"
|
|
6
|
+
import { cn } from "./lib/cn"
|
|
7
|
+
|
|
8
|
+
const avatarVariants = cva(
|
|
9
|
+
"relative inline-flex shrink-0 items-center justify-center overflow-hidden rounded-full bg-background-tertiary text-fg-primary align-middle shadow-hairline select-none",
|
|
10
|
+
{
|
|
11
|
+
variants: {
|
|
12
|
+
size: {
|
|
13
|
+
sm: "size-8 text-xs",
|
|
14
|
+
md: "size-10 text-xs-strong",
|
|
15
|
+
lg: "size-12 text-sm",
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
defaultVariants: {
|
|
19
|
+
size: "md",
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
export type AvatarProps = ComponentProps<typeof BaseAvatar.Root> &
|
|
25
|
+
VariantProps<typeof avatarVariants>
|
|
26
|
+
export type AvatarImageProps = ComponentProps<typeof BaseAvatar.Image>
|
|
27
|
+
export type AvatarFallbackProps = ComponentProps<typeof BaseAvatar.Fallback>
|
|
28
|
+
|
|
29
|
+
export const Avatar = ({ className, size, ...props }: AvatarProps) => (
|
|
30
|
+
<BaseAvatar.Root
|
|
31
|
+
className={cn(avatarVariants({ size }), className)}
|
|
32
|
+
{...props}
|
|
33
|
+
/>
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
export const AvatarImage = ({ className, ...props }: AvatarImageProps) => (
|
|
37
|
+
<BaseAvatar.Image
|
|
38
|
+
className={cn("size-full object-cover", className)}
|
|
39
|
+
{...props}
|
|
40
|
+
/>
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
export const AvatarFallback = ({
|
|
44
|
+
className,
|
|
45
|
+
...props
|
|
46
|
+
}: AvatarFallbackProps) => (
|
|
47
|
+
<BaseAvatar.Fallback
|
|
48
|
+
className={cn(
|
|
49
|
+
"flex size-full items-center justify-center bg-background-tertiary tracking-tight text-inherit",
|
|
50
|
+
className,
|
|
51
|
+
)}
|
|
52
|
+
{...props}
|
|
53
|
+
/>
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
export { avatarVariants }
|
package/src/badge.tsx
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
2
|
+
import type { HTMLAttributes } from "react"
|
|
3
|
+
import { cn } from "./lib/cn"
|
|
4
|
+
|
|
5
|
+
const badgeVariants = cva(
|
|
6
|
+
"text-xs-strong inline-flex items-center rounded-full py-0.5",
|
|
7
|
+
{
|
|
8
|
+
variants: {
|
|
9
|
+
variant: {
|
|
10
|
+
default: "bg-background-tertiary px-3 text-fg-primary",
|
|
11
|
+
outline: "border border-border-primary px-2 text-fg-secondary",
|
|
12
|
+
destructive: "bg-destructive/10 px-2 text-destructive",
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
defaultVariants: {
|
|
16
|
+
variant: "default",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
export type BadgeProps = HTMLAttributes<HTMLSpanElement> &
|
|
22
|
+
VariantProps<typeof badgeVariants>
|
|
23
|
+
|
|
24
|
+
export const Badge = ({ className, variant, ...props }: BadgeProps) => (
|
|
25
|
+
<span className={cn(badgeVariants({ variant }), className)} {...props} />
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
export { badgeVariants }
|
package/src/brand.tsx
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { SVGProps } from "react"
|
|
2
|
+
import { cn } from "./lib/cn"
|
|
3
|
+
|
|
4
|
+
export type BrandMarkProps = SVGProps<SVGSVGElement> & {
|
|
5
|
+
size?: number
|
|
6
|
+
title?: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** Solid disk — canonical StandardUI mark. Color via currentColor / parent text. */
|
|
10
|
+
export const BrandMark = ({
|
|
11
|
+
size = 24,
|
|
12
|
+
className,
|
|
13
|
+
title = "StandardUI",
|
|
14
|
+
...props
|
|
15
|
+
}: BrandMarkProps) => (
|
|
16
|
+
<svg
|
|
17
|
+
width={size}
|
|
18
|
+
height={size}
|
|
19
|
+
viewBox="0 0 128 128"
|
|
20
|
+
fill="none"
|
|
21
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
22
|
+
className={cn("shrink-0 text-current", className)}
|
|
23
|
+
aria-hidden={title ? undefined : true}
|
|
24
|
+
role={title ? "img" : undefined}
|
|
25
|
+
{...props}
|
|
26
|
+
>
|
|
27
|
+
{title ? <title>{title}</title> : null}
|
|
28
|
+
<circle cx="64" cy="64" r="64" fill="currentColor" />
|
|
29
|
+
</svg>
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
export type BrandWordmarkProps = {
|
|
33
|
+
className?: string
|
|
34
|
+
markSize?: number
|
|
35
|
+
/** Dense chrome: mark + “UI” only. Default lockup is mark + “StandardUI”. */
|
|
36
|
+
compact?: boolean
|
|
37
|
+
/** Chrome lockups use sm; marketing uses md */
|
|
38
|
+
size?: "sm" | "md"
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Disk + StandardUI — matches the canonical lockup. Inherits color for light/dark. */
|
|
42
|
+
export const BrandWordmark = ({
|
|
43
|
+
className,
|
|
44
|
+
markSize,
|
|
45
|
+
compact = false,
|
|
46
|
+
size = "md",
|
|
47
|
+
}: BrandWordmarkProps) => {
|
|
48
|
+
const resolvedMarkSize = markSize ?? (size === "sm" ? 16 : 28)
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<span
|
|
52
|
+
className={cn(
|
|
53
|
+
"inline-flex items-center",
|
|
54
|
+
size === "sm" ? "gap-1" : "gap-1.5",
|
|
55
|
+
className,
|
|
56
|
+
)}
|
|
57
|
+
aria-label="StandardUI"
|
|
58
|
+
>
|
|
59
|
+
<BrandMark size={resolvedMarkSize} title="" aria-hidden />
|
|
60
|
+
<span
|
|
61
|
+
className={cn(
|
|
62
|
+
"tracking-tight",
|
|
63
|
+
size === "sm" ? "text-sm-strong" : "heading-lg-sans",
|
|
64
|
+
)}
|
|
65
|
+
>
|
|
66
|
+
{compact ? "UI" : "StandardUI"}
|
|
67
|
+
</span>
|
|
68
|
+
</span>
|
|
69
|
+
)
|
|
70
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { ComponentProps, ReactNode } from "react";
|
|
2
|
+
import { IconChevronRightSmall } from "./icons";
|
|
3
|
+
import { cn } from "./lib/cn";
|
|
4
|
+
import { motion } from "./lib/motion";
|
|
5
|
+
|
|
6
|
+
export type BreadcrumbProps = ComponentProps<"nav">;
|
|
7
|
+
export type BreadcrumbListProps = ComponentProps<"ol">;
|
|
8
|
+
export type BreadcrumbItemProps = ComponentProps<"li">;
|
|
9
|
+
export type BreadcrumbLinkProps = ComponentProps<"a">;
|
|
10
|
+
export type BreadcrumbPageProps = ComponentProps<"span">;
|
|
11
|
+
export type BreadcrumbSeparatorProps = ComponentProps<"li"> & {
|
|
12
|
+
children?: ReactNode;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const Breadcrumb = ({ className, ...props }: BreadcrumbProps) => (
|
|
16
|
+
<nav
|
|
17
|
+
aria-label="Breadcrumb"
|
|
18
|
+
className={cn("text-sm text-fg-secondary", className)}
|
|
19
|
+
{...props}
|
|
20
|
+
/>
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
export const BreadcrumbList = ({
|
|
24
|
+
className,
|
|
25
|
+
...props
|
|
26
|
+
}: BreadcrumbListProps) => (
|
|
27
|
+
<ol
|
|
28
|
+
className={cn("flex flex-wrap items-center gap-1.5", className)}
|
|
29
|
+
{...props}
|
|
30
|
+
/>
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
export const BreadcrumbItem = ({
|
|
34
|
+
className,
|
|
35
|
+
...props
|
|
36
|
+
}: BreadcrumbItemProps) => (
|
|
37
|
+
<li
|
|
38
|
+
className={cn("inline-flex items-center gap-1.5", className)}
|
|
39
|
+
{...props}
|
|
40
|
+
/>
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
export const BreadcrumbLink = ({
|
|
44
|
+
className,
|
|
45
|
+
...props
|
|
46
|
+
}: BreadcrumbLinkProps) => (
|
|
47
|
+
<a
|
|
48
|
+
className={cn(
|
|
49
|
+
"cursor-pointer rounded-sm text-fg-secondary outline-none hover:text-fg-primary focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-offset-1 focus-visible:ring-offset-background-primary focus-visible:ring-ring/20",
|
|
50
|
+
motion.colors,
|
|
51
|
+
className,
|
|
52
|
+
)}
|
|
53
|
+
{...props}
|
|
54
|
+
/>
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
export const BreadcrumbPage = ({
|
|
58
|
+
className,
|
|
59
|
+
...props
|
|
60
|
+
}: BreadcrumbPageProps) => (
|
|
61
|
+
<span
|
|
62
|
+
aria-current="page"
|
|
63
|
+
className={cn("font-medium text-fg-primary", className)}
|
|
64
|
+
{...props}
|
|
65
|
+
/>
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
export const BreadcrumbSeparator = ({
|
|
69
|
+
className,
|
|
70
|
+
children,
|
|
71
|
+
...props
|
|
72
|
+
}: BreadcrumbSeparatorProps) => (
|
|
73
|
+
<li
|
|
74
|
+
role="presentation"
|
|
75
|
+
aria-hidden
|
|
76
|
+
className={cn("text-fg-tertiary [&_svg]:size-3.5", className)}
|
|
77
|
+
{...props}
|
|
78
|
+
>
|
|
79
|
+
{children ?? <IconChevronRightSmall />}
|
|
80
|
+
</li>
|
|
81
|
+
);
|
package/src/button.tsx
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
2
|
+
import type { ButtonHTMLAttributes, ReactNode } from "react"
|
|
3
|
+
import { cn } from "./lib/cn"
|
|
4
|
+
|
|
5
|
+
const buttonVariants = cva(
|
|
6
|
+
"text-sm inline-flex cursor-pointer items-center justify-center gap-1.5 whitespace-nowrap transition-all duration-150 ease-out outline-none focus-visible:ring-[3px] focus-visible:ring-offset-1 focus-visible:ring-offset-background-primary disabled:cursor-not-allowed disabled:opacity-50 disabled:active:translate-y-0 active:translate-y-px [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
7
|
+
{
|
|
8
|
+
variants: {
|
|
9
|
+
variant: {
|
|
10
|
+
primary:
|
|
11
|
+
"border border-brand-primary-border bg-brand-primary text-brand-foreground inset-shadow-solid-top hover:bg-brand-primary-hover active:bg-brand-primary-active focus-visible:border-ring focus-visible:ring-ring/20",
|
|
12
|
+
secondary:
|
|
13
|
+
"border border-transparent bg-background-tertiary text-fg-primary hover:bg-background-quaternary active:bg-background-active focus-visible:border-ring focus-visible:ring-ring/20 dark:bg-gray-250 dark:hover:bg-gray-350 dark:active:bg-gray-200",
|
|
14
|
+
outline:
|
|
15
|
+
"border border-border-secondary bg-surface text-fg-primary inset-shadow-outline-top hover:bg-background-tertiary focus-visible:border-ring focus-visible:ring-ring/20",
|
|
16
|
+
ghost:
|
|
17
|
+
"border border-transparent text-fg-secondary hover:bg-background-tertiary hover:text-fg-primary focus-visible:border-ring focus-visible:ring-ring/20",
|
|
18
|
+
destructive:
|
|
19
|
+
"border border-destructive-active bg-destructive text-white inset-shadow-solid-top hover:bg-destructive-active focus-visible:border-destructive focus-visible:ring-destructive/20",
|
|
20
|
+
},
|
|
21
|
+
size: {
|
|
22
|
+
sm: "h-8 px-3",
|
|
23
|
+
md: "h-9 px-3.5",
|
|
24
|
+
lg: "h-10 px-4",
|
|
25
|
+
},
|
|
26
|
+
rounded: {
|
|
27
|
+
true: "rounded-full",
|
|
28
|
+
false: "rounded-md",
|
|
29
|
+
},
|
|
30
|
+
iconOnly: {
|
|
31
|
+
true: "px-0",
|
|
32
|
+
false: "",
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
compoundVariants: [
|
|
36
|
+
{ iconOnly: true, size: "sm", class: "size-8" },
|
|
37
|
+
{ iconOnly: true, size: "md", class: "size-9" },
|
|
38
|
+
{ iconOnly: true, size: "lg", class: "size-10" },
|
|
39
|
+
],
|
|
40
|
+
defaultVariants: {
|
|
41
|
+
variant: "primary",
|
|
42
|
+
size: "md",
|
|
43
|
+
rounded: false,
|
|
44
|
+
iconOnly: false,
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
const ButtonSpinner = ({ className }: { className?: string }) => (
|
|
50
|
+
<svg
|
|
51
|
+
className={cn("size-4 animate-spin motion-reduce:animate-none", className)}
|
|
52
|
+
viewBox="0 0 24 24"
|
|
53
|
+
fill="none"
|
|
54
|
+
aria-hidden
|
|
55
|
+
>
|
|
56
|
+
<path
|
|
57
|
+
d="M21 12C21 16.9706 16.9706 21 12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3C16.9706 3 21 7.02944 21 12Z"
|
|
58
|
+
stroke="currentColor"
|
|
59
|
+
strokeOpacity="0.3"
|
|
60
|
+
strokeWidth="2"
|
|
61
|
+
/>
|
|
62
|
+
<path
|
|
63
|
+
d="M21 12C21 16.9706 16.9706 21 12 21"
|
|
64
|
+
stroke="currentColor"
|
|
65
|
+
strokeWidth="2"
|
|
66
|
+
/>
|
|
67
|
+
</svg>
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
export type ButtonProps = Omit<
|
|
71
|
+
ButtonHTMLAttributes<HTMLButtonElement>,
|
|
72
|
+
"prefix"
|
|
73
|
+
> &
|
|
74
|
+
VariantProps<typeof buttonVariants> & {
|
|
75
|
+
/** Shows a spinner, sets aria-busy, and disables the button */
|
|
76
|
+
loading?: boolean
|
|
77
|
+
/** Square control sized to the height of `size` — pass an icon as children */
|
|
78
|
+
iconOnly?: boolean
|
|
79
|
+
/** Pill shape (`rounded-full`) */
|
|
80
|
+
rounded?: boolean
|
|
81
|
+
/** Icon before the label (replaced by spinner when loading) */
|
|
82
|
+
prefix?: ReactNode
|
|
83
|
+
/** Icon after the label */
|
|
84
|
+
suffix?: ReactNode
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export const Button = ({
|
|
88
|
+
className,
|
|
89
|
+
variant,
|
|
90
|
+
size,
|
|
91
|
+
rounded,
|
|
92
|
+
iconOnly,
|
|
93
|
+
loading = false,
|
|
94
|
+
disabled,
|
|
95
|
+
prefix,
|
|
96
|
+
suffix,
|
|
97
|
+
children,
|
|
98
|
+
type = "button",
|
|
99
|
+
...props
|
|
100
|
+
}: ButtonProps) => {
|
|
101
|
+
const isDisabled = Boolean(disabled || loading)
|
|
102
|
+
const showPrefix = loading ? <ButtonSpinner /> : prefix
|
|
103
|
+
|
|
104
|
+
return (
|
|
105
|
+
<button
|
|
106
|
+
type={type}
|
|
107
|
+
disabled={isDisabled}
|
|
108
|
+
aria-busy={loading || undefined}
|
|
109
|
+
data-loading={loading || undefined}
|
|
110
|
+
className={cn(
|
|
111
|
+
buttonVariants({ variant, size, rounded, iconOnly }),
|
|
112
|
+
className,
|
|
113
|
+
)}
|
|
114
|
+
{...props}
|
|
115
|
+
>
|
|
116
|
+
{loading && iconOnly ? (
|
|
117
|
+
<ButtonSpinner />
|
|
118
|
+
) : (
|
|
119
|
+
<>
|
|
120
|
+
{showPrefix}
|
|
121
|
+
{children}
|
|
122
|
+
{suffix}
|
|
123
|
+
</>
|
|
124
|
+
)}
|
|
125
|
+
</button>
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export { buttonVariants }
|
package/src/calendar.tsx
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { DayPicker, type ChevronProps, type DayPickerProps } from "react-day-picker"
|
|
4
|
+
import { IconChevronBottom } from "./icons"
|
|
5
|
+
import { cn } from "./lib/cn"
|
|
6
|
+
import { motion } from "./lib/motion"
|
|
7
|
+
|
|
8
|
+
export type CalendarProps = DayPickerProps
|
|
9
|
+
|
|
10
|
+
const navButtonClassName = cn(
|
|
11
|
+
"inline-flex size-8 items-center justify-center rounded-md text-fg-tertiary",
|
|
12
|
+
motion.colors,
|
|
13
|
+
"hover:bg-background-tertiary hover:text-fg-primary outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-offset-1 focus-visible:ring-offset-background-primary focus-visible:ring-ring/20 disabled:cursor-not-allowed disabled:opacity-50",
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
const CalendarChevron = ({
|
|
17
|
+
className,
|
|
18
|
+
orientation,
|
|
19
|
+
size = 16,
|
|
20
|
+
}: ChevronProps) => {
|
|
21
|
+
const rotation =
|
|
22
|
+
orientation === "left"
|
|
23
|
+
? "rotate-90"
|
|
24
|
+
: orientation === "right"
|
|
25
|
+
? "-rotate-90"
|
|
26
|
+
: orientation === "up"
|
|
27
|
+
? "rotate-180"
|
|
28
|
+
: undefined
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<IconChevronBottom
|
|
32
|
+
size={size}
|
|
33
|
+
className={cn("text-fg-tertiary", rotation, className)}
|
|
34
|
+
aria-hidden
|
|
35
|
+
/>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const Calendar = ({
|
|
40
|
+
className,
|
|
41
|
+
classNames,
|
|
42
|
+
showOutsideDays = true,
|
|
43
|
+
components,
|
|
44
|
+
...props
|
|
45
|
+
}: CalendarProps) => (
|
|
46
|
+
<DayPicker
|
|
47
|
+
showOutsideDays={showOutsideDays}
|
|
48
|
+
className={cn("w-fit p-3", className)}
|
|
49
|
+
classNames={{
|
|
50
|
+
months: "relative flex flex-col gap-4 sm:flex-row",
|
|
51
|
+
month: "flex w-full flex-col gap-3",
|
|
52
|
+
month_caption: "relative flex h-8 items-center justify-center px-8",
|
|
53
|
+
caption_label: "text-sm-strong text-fg-primary",
|
|
54
|
+
nav: "absolute inset-x-0 top-0 flex items-center justify-between",
|
|
55
|
+
button_previous: navButtonClassName,
|
|
56
|
+
button_next: navButtonClassName,
|
|
57
|
+
month_grid: "w-full border-collapse",
|
|
58
|
+
weekdays: "flex",
|
|
59
|
+
weekday:
|
|
60
|
+
"flex size-8 items-center justify-center text-xs font-normal text-fg-tertiary",
|
|
61
|
+
weeks: "flex flex-col",
|
|
62
|
+
week: "mt-0.5 flex w-full",
|
|
63
|
+
day: "relative p-0 text-center",
|
|
64
|
+
day_button: cn(
|
|
65
|
+
"inline-flex size-8 items-center justify-center rounded-md text-sm text-fg-primary",
|
|
66
|
+
motion.colors,
|
|
67
|
+
"hover:bg-background-tertiary outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-offset-1 focus-visible:ring-offset-background-primary focus-visible:ring-ring/20 disabled:cursor-not-allowed disabled:opacity-50 aria-selected:opacity-100",
|
|
68
|
+
),
|
|
69
|
+
selected:
|
|
70
|
+
"[&>button]:bg-brand-primary [&>button]:text-brand-foreground [&>button]:ring-0 [&>button]:hover:bg-brand-primary-hover [&>button]:hover:text-brand-foreground",
|
|
71
|
+
today:
|
|
72
|
+
"[&>button]:font-medium [&>button]:ring-1 [&>button]:ring-inset [&>button]:ring-border-secondary",
|
|
73
|
+
outside: "[&>button]:text-fg-quaternary",
|
|
74
|
+
disabled: "[&>button]:opacity-40",
|
|
75
|
+
hidden: "invisible",
|
|
76
|
+
...classNames,
|
|
77
|
+
}}
|
|
78
|
+
components={{
|
|
79
|
+
Chevron: CalendarChevron,
|
|
80
|
+
...components,
|
|
81
|
+
}}
|
|
82
|
+
{...({ mode: "single", ...props } as DayPickerProps)}
|
|
83
|
+
/>
|
|
84
|
+
)
|
package/src/card.tsx
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { HTMLAttributes } from "react"
|
|
2
|
+
import { cn } from "./lib/cn"
|
|
3
|
+
|
|
4
|
+
export type CardProps = HTMLAttributes<HTMLDivElement>
|
|
5
|
+
export type CardHeaderProps = HTMLAttributes<HTMLDivElement>
|
|
6
|
+
export type CardTitleProps = HTMLAttributes<HTMLHeadingElement>
|
|
7
|
+
export type CardDescriptionProps = HTMLAttributes<HTMLParagraphElement>
|
|
8
|
+
export type CardContentProps = HTMLAttributes<HTMLDivElement>
|
|
9
|
+
export type CardFooterProps = HTMLAttributes<HTMLDivElement>
|
|
10
|
+
|
|
11
|
+
export const Card = ({ className, ...props }: CardProps) => (
|
|
12
|
+
<div
|
|
13
|
+
className={cn(
|
|
14
|
+
"flex flex-col overflow-hidden rounded-xl border border-border-primary bg-surface",
|
|
15
|
+
className,
|
|
16
|
+
)}
|
|
17
|
+
{...props}
|
|
18
|
+
/>
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
export const CardHeader = ({ className, ...props }: CardHeaderProps) => (
|
|
22
|
+
<div
|
|
23
|
+
className={cn("flex flex-col gap-1 px-5 pt-5 pb-3", className)}
|
|
24
|
+
{...props}
|
|
25
|
+
/>
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
export const CardTitle = ({ className, ...props }: CardTitleProps) => (
|
|
29
|
+
<h3 className={cn("text-sm-strong text-fg-primary", className)} {...props} />
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
export const CardDescription = ({
|
|
33
|
+
className,
|
|
34
|
+
...props
|
|
35
|
+
}: CardDescriptionProps) => (
|
|
36
|
+
<p
|
|
37
|
+
className={cn("text-sm leading-relaxed text-fg-secondary", className)}
|
|
38
|
+
{...props}
|
|
39
|
+
/>
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
export const CardContent = ({ className, ...props }: CardContentProps) => (
|
|
43
|
+
<div className={cn("px-5 pb-5", className)} {...props} />
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
export const CardFooter = ({ className, ...props }: CardFooterProps) => (
|
|
47
|
+
<div
|
|
48
|
+
className={cn(
|
|
49
|
+
"flex items-center gap-2 border-t border-border-primary px-5 py-4",
|
|
50
|
+
className,
|
|
51
|
+
)}
|
|
52
|
+
{...props}
|
|
53
|
+
/>
|
|
54
|
+
)
|