blodemd 0.0.5 → 0.0.7
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 +2 -2
- package/dev-server/app/[[...slug]]/page.tsx +139 -0
- package/dev-server/app/blodemd-dev/invalidate/route.ts +12 -0
- package/dev-server/app/blodemd-dev/static/[...path]/route.ts +32 -0
- package/dev-server/app/blodemd-dev/version/route.ts +14 -0
- package/dev-server/app/blodemd-internal/proxy/route.ts +86 -0
- package/dev-server/app/error.tsx +24 -0
- package/dev-server/app/favicon.ico +0 -0
- package/dev-server/app/globals.css +4 -0
- package/dev-server/app/layout.tsx +38 -0
- package/dev-server/app/not-found.tsx +18 -0
- package/dev-server/app/search/route.ts +17 -0
- package/dev-server/components/dev-reload-script.tsx +86 -0
- package/dev-server/components/providers.tsx +15 -0
- package/dev-server/lib/dev-state.ts +8 -0
- package/dev-server/lib/local-content-source.ts +103 -0
- package/dev-server/lib/local-runtime.tsx +558 -0
- package/dev-server/next-env.d.ts +5 -0
- package/dev-server/next.config.js +46 -0
- package/dev-server/package.json +57 -0
- package/dev-server/postcss.config.mjs +7 -0
- package/dev-server/public/glide-variable.woff2 +0 -0
- package/dev-server/tsconfig.json +50 -0
- package/dist/cli.mjs +311 -86
- package/dist/cli.mjs.map +1 -1
- package/docs/app/globals.css +457 -0
- package/docs/components/api/api-playground.tsx +295 -0
- package/docs/components/api/api-reference.tsx +121 -0
- package/docs/components/content/collection-index.tsx +114 -0
- package/docs/components/docs/contextual-menu.tsx +406 -0
- package/docs/components/docs/copy-page-menu.tsx +255 -0
- package/docs/components/docs/doc-header.tsx +210 -0
- package/docs/components/docs/doc-shell.tsx +313 -0
- package/docs/components/docs/doc-sidebar.tsx +211 -0
- package/docs/components/docs/doc-toc.tsx +45 -0
- package/docs/components/docs/mobile-nav.tsx +205 -0
- package/docs/components/icons/doc-icon.tsx +96 -0
- package/docs/components/mdx/accordion.tsx +83 -0
- package/docs/components/mdx/badge.tsx +79 -0
- package/docs/components/mdx/callout.tsx +88 -0
- package/docs/components/mdx/card.tsx +110 -0
- package/docs/components/mdx/code-block.tsx +75 -0
- package/docs/components/mdx/code-group.tsx +94 -0
- package/docs/components/mdx/color.tsx +87 -0
- package/docs/components/mdx/columns.tsx +25 -0
- package/docs/components/mdx/expandable.tsx +45 -0
- package/docs/components/mdx/field-layout.tsx +77 -0
- package/docs/components/mdx/frame.tsx +23 -0
- package/docs/components/mdx/get-text-content.ts +18 -0
- package/docs/components/mdx/icon.tsx +12 -0
- package/docs/components/mdx/index.tsx +107 -0
- package/docs/components/mdx/installer.tsx +20 -0
- package/docs/components/mdx/panel.tsx +11 -0
- package/docs/components/mdx/param-field.tsx +56 -0
- package/docs/components/mdx/preview.tsx +36 -0
- package/docs/components/mdx/prompt.tsx +63 -0
- package/docs/components/mdx/request-example.tsx +27 -0
- package/docs/components/mdx/response-field.tsx +42 -0
- package/docs/components/mdx/steps.tsx +92 -0
- package/docs/components/mdx/tabs.tsx +88 -0
- package/docs/components/mdx/tile.tsx +43 -0
- package/docs/components/mdx/tooltip.tsx +71 -0
- package/docs/components/mdx/tree.tsx +120 -0
- package/docs/components/mdx/type-table.tsx +71 -0
- package/docs/components/mdx/update.tsx +44 -0
- package/docs/components/mdx/video.tsx +12 -0
- package/docs/components/mdx/view.tsx +66 -0
- package/docs/components/providers.tsx +15 -0
- package/docs/components/ui/breadcrumb.tsx +92 -0
- package/docs/components/ui/button.tsx +90 -0
- package/docs/components/ui/card.tsx +92 -0
- package/docs/components/ui/command.tsx +139 -0
- package/docs/components/ui/dialog.tsx +97 -0
- package/docs/components/ui/field.tsx +237 -0
- package/docs/components/ui/input.tsx +105 -0
- package/docs/components/ui/label.tsx +22 -0
- package/docs/components/ui/popover.tsx +72 -0
- package/docs/components/ui/search.tsx +384 -0
- package/docs/components/ui/separator.tsx +26 -0
- package/docs/components/ui/sheet.tsx +104 -0
- package/docs/components/ui/sidebar.tsx +433 -0
- package/docs/components/ui/theme-toggle.tsx +62 -0
- package/docs/components/ui/tooltip.tsx +53 -0
- package/docs/lib/contextual-options.ts +193 -0
- package/docs/lib/docs-collection.ts +22 -0
- package/docs/lib/mdx.ts +87 -0
- package/docs/lib/navigation.ts +288 -0
- package/docs/lib/openapi.ts +158 -0
- package/docs/lib/routes.ts +44 -0
- package/docs/lib/server-cache.ts +83 -0
- package/docs/lib/shiki.ts +40 -0
- package/docs/lib/theme.ts +29 -0
- package/docs/lib/toc.ts +2 -0
- package/docs/lib/utils.ts +5 -0
- package/package.json +43 -6
- package/packages/@repo/common/dist/index.d.ts +9 -0
- package/packages/@repo/common/dist/index.d.ts.map +1 -0
- package/packages/@repo/common/dist/index.js +42 -0
- package/packages/@repo/common/package.json +34 -0
- package/packages/@repo/common/src/index.ts +51 -0
- package/packages/@repo/contracts/dist/api-key.d.ts +30 -0
- package/packages/@repo/contracts/dist/api-key.d.ts.map +1 -0
- package/packages/@repo/contracts/dist/api-key.js +20 -0
- package/packages/@repo/contracts/dist/dates.d.ts +4 -0
- package/packages/@repo/contracts/dist/dates.d.ts.map +1 -0
- package/packages/@repo/contracts/dist/dates.js +2 -0
- package/packages/@repo/contracts/dist/deployment.d.ts +71 -0
- package/packages/@repo/contracts/dist/deployment.d.ts.map +1 -0
- package/packages/@repo/contracts/dist/deployment.js +46 -0
- package/packages/@repo/contracts/dist/domain.d.ts +94 -0
- package/packages/@repo/contracts/dist/domain.d.ts.map +1 -0
- package/packages/@repo/contracts/dist/domain.js +36 -0
- package/packages/@repo/contracts/dist/ids.d.ts +14 -0
- package/packages/@repo/contracts/dist/ids.d.ts.map +1 -0
- package/packages/@repo/contracts/dist/ids.js +10 -0
- package/packages/@repo/contracts/dist/index.d.ts +10 -0
- package/packages/@repo/contracts/dist/index.d.ts.map +1 -0
- package/packages/@repo/contracts/dist/index.js +11 -0
- package/packages/@repo/contracts/dist/pagination.d.ts +23 -0
- package/packages/@repo/contracts/dist/pagination.d.ts.map +1 -0
- package/packages/@repo/contracts/dist/pagination.js +15 -0
- package/packages/@repo/contracts/dist/project.d.ts +25 -0
- package/packages/@repo/contracts/dist/project.d.ts.map +1 -0
- package/packages/@repo/contracts/dist/project.js +23 -0
- package/packages/@repo/contracts/dist/tenant.d.ts +111 -0
- package/packages/@repo/contracts/dist/tenant.d.ts.map +1 -0
- package/packages/@repo/contracts/dist/tenant.js +56 -0
- package/packages/@repo/contracts/dist/user.d.ts +9 -0
- package/packages/@repo/contracts/dist/user.d.ts.map +1 -0
- package/packages/@repo/contracts/dist/user.js +9 -0
- package/packages/@repo/contracts/package.json +37 -0
- package/packages/@repo/contracts/src/api-key.ts +27 -0
- package/packages/@repo/contracts/src/dates.ts +4 -0
- package/packages/@repo/contracts/src/deployment.ts +73 -0
- package/packages/@repo/contracts/src/domain.ts +51 -0
- package/packages/@repo/contracts/src/ids.ts +22 -0
- package/packages/@repo/contracts/src/index.ts +11 -0
- package/packages/@repo/contracts/src/pagination.ts +21 -0
- package/packages/@repo/contracts/src/project.ts +30 -0
- package/packages/@repo/contracts/src/tenant.ts +92 -0
- package/packages/@repo/contracts/src/user.ts +12 -0
- package/packages/@repo/models/dist/docs-config.d.ts +985 -0
- package/packages/@repo/models/dist/docs-config.d.ts.map +1 -0
- package/packages/@repo/models/dist/docs-config.js +548 -0
- package/packages/@repo/models/dist/index.d.ts +3 -0
- package/packages/@repo/models/dist/index.d.ts.map +1 -0
- package/packages/@repo/models/dist/index.js +3 -0
- package/packages/@repo/models/dist/tenant.d.ts +25 -0
- package/packages/@repo/models/dist/tenant.d.ts.map +1 -0
- package/packages/@repo/models/dist/tenant.js +1 -0
- package/packages/@repo/models/package.json +37 -0
- package/packages/@repo/models/src/docs-config.ts +648 -0
- package/packages/@repo/models/src/index.ts +3 -0
- package/packages/@repo/models/src/tenant.ts +29 -0
- package/packages/@repo/prebuild/dist/index.d.ts +2 -0
- package/packages/@repo/prebuild/dist/index.d.ts.map +1 -0
- package/packages/@repo/prebuild/dist/index.js +2 -0
- package/packages/@repo/prebuild/dist/openapi.d.ts +43 -0
- package/packages/@repo/prebuild/dist/openapi.d.ts.map +1 -0
- package/packages/@repo/prebuild/dist/openapi.js +58 -0
- package/packages/@repo/prebuild/package.json +39 -0
- package/packages/@repo/prebuild/src/index.ts +2 -0
- package/packages/@repo/prebuild/src/openapi.ts +116 -0
- package/packages/@repo/previewing/dist/blob-source.d.ts +16 -0
- package/packages/@repo/previewing/dist/blob-source.d.ts.map +1 -0
- package/packages/@repo/previewing/dist/blob-source.js +110 -0
- package/packages/@repo/previewing/dist/content-source.d.ts +12 -0
- package/packages/@repo/previewing/dist/content-source.d.ts.map +1 -0
- package/packages/@repo/previewing/dist/content-source.js +1 -0
- package/packages/@repo/previewing/dist/fs-source.d.ts +11 -0
- package/packages/@repo/previewing/dist/fs-source.d.ts.map +1 -0
- package/packages/@repo/previewing/dist/fs-source.js +72 -0
- package/packages/@repo/previewing/dist/index.d.ts +120 -0
- package/packages/@repo/previewing/dist/index.d.ts.map +1 -0
- package/packages/@repo/previewing/dist/index.js +984 -0
- package/packages/@repo/previewing/package.json +41 -0
- package/packages/@repo/previewing/src/blob-source.ts +167 -0
- package/packages/@repo/previewing/src/content-source.ts +12 -0
- package/packages/@repo/previewing/src/fs-source.ts +104 -0
- package/packages/@repo/previewing/src/index.ts +1490 -0
- package/packages/@repo/validation/dist/index.d.ts +12 -0
- package/packages/@repo/validation/dist/index.d.ts.map +1 -0
- package/packages/@repo/validation/dist/index.js +30 -0
- package/packages/@repo/validation/package.json +37 -0
- package/packages/@repo/validation/src/index.ts +59 -0
- package/packages/@repo/validation/src/mintlify-docs-schema.json +5016 -0
- package/scripts/prepare-package.mjs +39 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useCallback, useRef, useState } from "react";
|
|
4
|
+
import type { ReactNode } from "react";
|
|
5
|
+
|
|
6
|
+
interface TooltipProps {
|
|
7
|
+
tip: string;
|
|
8
|
+
headline?: string;
|
|
9
|
+
cta?: string;
|
|
10
|
+
href?: string;
|
|
11
|
+
children: ReactNode;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const Tooltip = ({
|
|
15
|
+
tip,
|
|
16
|
+
headline,
|
|
17
|
+
cta,
|
|
18
|
+
href,
|
|
19
|
+
children,
|
|
20
|
+
}: TooltipProps) => {
|
|
21
|
+
const [open, setOpen] = useState(false);
|
|
22
|
+
const timeoutRef = useRef<ReturnType<typeof setTimeout>>(null);
|
|
23
|
+
|
|
24
|
+
const show = useCallback(() => {
|
|
25
|
+
if (timeoutRef.current) {
|
|
26
|
+
clearTimeout(timeoutRef.current);
|
|
27
|
+
}
|
|
28
|
+
setOpen(true);
|
|
29
|
+
}, []);
|
|
30
|
+
|
|
31
|
+
const hide = useCallback(() => {
|
|
32
|
+
timeoutRef.current = setTimeout(() => setOpen(false), 150);
|
|
33
|
+
}, []);
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<span className="relative inline-block">
|
|
37
|
+
<button
|
|
38
|
+
className="cursor-help border-b border-dashed border-muted-foreground bg-transparent p-0 text-inherit"
|
|
39
|
+
onBlur={hide}
|
|
40
|
+
onFocus={show}
|
|
41
|
+
onMouseEnter={show}
|
|
42
|
+
onMouseLeave={hide}
|
|
43
|
+
type="button"
|
|
44
|
+
>
|
|
45
|
+
{children}
|
|
46
|
+
</button>
|
|
47
|
+
{open ? (
|
|
48
|
+
<span
|
|
49
|
+
className="absolute bottom-full left-1/2 z-50 mb-2 w-max max-w-64 -translate-x-1/2 rounded-lg border border-border bg-popover px-3 py-2 text-sm text-popover-foreground shadow-md"
|
|
50
|
+
onMouseEnter={show}
|
|
51
|
+
onMouseLeave={hide}
|
|
52
|
+
role="tooltip"
|
|
53
|
+
>
|
|
54
|
+
{headline ? (
|
|
55
|
+
<span className="mb-1 block font-semibold">{headline}</span>
|
|
56
|
+
) : null}
|
|
57
|
+
<span className="block">{tip}</span>
|
|
58
|
+
{cta && href ? (
|
|
59
|
+
<a
|
|
60
|
+
className="mt-1 block text-primary hover:underline"
|
|
61
|
+
href={href}
|
|
62
|
+
rel="noopener noreferrer"
|
|
63
|
+
>
|
|
64
|
+
{cta}
|
|
65
|
+
</a>
|
|
66
|
+
) : null}
|
|
67
|
+
</span>
|
|
68
|
+
) : null}
|
|
69
|
+
</span>
|
|
70
|
+
);
|
|
71
|
+
};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useCallback, useState } from "react";
|
|
4
|
+
import type { ReactNode } from "react";
|
|
5
|
+
|
|
6
|
+
import { cn } from "@/lib/utils";
|
|
7
|
+
|
|
8
|
+
interface TreeFileProps {
|
|
9
|
+
name: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const TreeFile = ({ name }: TreeFileProps) => (
|
|
13
|
+
<div className="flex items-center gap-2 py-0.5 pl-5 text-sm">
|
|
14
|
+
<svg
|
|
15
|
+
aria-hidden
|
|
16
|
+
className="size-4 shrink-0 text-muted-foreground"
|
|
17
|
+
fill="none"
|
|
18
|
+
stroke="currentColor"
|
|
19
|
+
strokeWidth={2}
|
|
20
|
+
viewBox="0 0 24 24"
|
|
21
|
+
>
|
|
22
|
+
<path
|
|
23
|
+
d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"
|
|
24
|
+
strokeLinecap="round"
|
|
25
|
+
strokeLinejoin="round"
|
|
26
|
+
/>
|
|
27
|
+
<path
|
|
28
|
+
d="M14 2v4a2 2 0 0 0 2 2h4"
|
|
29
|
+
strokeLinecap="round"
|
|
30
|
+
strokeLinejoin="round"
|
|
31
|
+
/>
|
|
32
|
+
</svg>
|
|
33
|
+
<span className="text-foreground">{name}</span>
|
|
34
|
+
</div>
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
interface TreeFolderProps {
|
|
38
|
+
name: string;
|
|
39
|
+
defaultOpen?: boolean;
|
|
40
|
+
openable?: boolean;
|
|
41
|
+
children?: ReactNode;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const TreeFolder = ({
|
|
45
|
+
name,
|
|
46
|
+
defaultOpen = false,
|
|
47
|
+
openable = true,
|
|
48
|
+
children,
|
|
49
|
+
}: TreeFolderProps) => {
|
|
50
|
+
const [open, setOpen] = useState(defaultOpen);
|
|
51
|
+
|
|
52
|
+
const toggle = useCallback(() => {
|
|
53
|
+
if (openable) {
|
|
54
|
+
setOpen((prev) => !prev);
|
|
55
|
+
}
|
|
56
|
+
}, [openable]);
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<div>
|
|
60
|
+
<button
|
|
61
|
+
className={cn(
|
|
62
|
+
"flex w-full items-center gap-2 py-0.5 text-sm border-none bg-transparent text-left",
|
|
63
|
+
openable && "cursor-pointer hover:bg-accent/50 rounded"
|
|
64
|
+
)}
|
|
65
|
+
disabled={!openable}
|
|
66
|
+
onClick={toggle}
|
|
67
|
+
type="button"
|
|
68
|
+
>
|
|
69
|
+
<svg
|
|
70
|
+
aria-hidden
|
|
71
|
+
className={cn(
|
|
72
|
+
"size-3 shrink-0 text-muted-foreground transition-transform",
|
|
73
|
+
open && "rotate-90"
|
|
74
|
+
)}
|
|
75
|
+
fill="currentColor"
|
|
76
|
+
viewBox="0 0 24 24"
|
|
77
|
+
>
|
|
78
|
+
<path d="m9 18 6-6-6-6" />
|
|
79
|
+
</svg>
|
|
80
|
+
<svg
|
|
81
|
+
aria-hidden
|
|
82
|
+
className="size-4 shrink-0 text-muted-foreground"
|
|
83
|
+
fill="none"
|
|
84
|
+
stroke="currentColor"
|
|
85
|
+
strokeWidth={2}
|
|
86
|
+
viewBox="0 0 24 24"
|
|
87
|
+
>
|
|
88
|
+
{open ? (
|
|
89
|
+
<path
|
|
90
|
+
d="M5 19a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2h4l2 2h4a2 2 0 0 1 2 2v1M5 19h14a2 2 0 0 0 2-2v-5a2 2 0 0 0-2-2H9a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2Z"
|
|
91
|
+
strokeLinecap="round"
|
|
92
|
+
strokeLinejoin="round"
|
|
93
|
+
/>
|
|
94
|
+
) : (
|
|
95
|
+
<path
|
|
96
|
+
d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z"
|
|
97
|
+
strokeLinecap="round"
|
|
98
|
+
strokeLinejoin="round"
|
|
99
|
+
/>
|
|
100
|
+
)}
|
|
101
|
+
</svg>
|
|
102
|
+
<span className="font-medium text-foreground">{name}</span>
|
|
103
|
+
</button>
|
|
104
|
+
{open && children ? (
|
|
105
|
+
<div className="ml-3 border-l border-border pl-2">{children}</div>
|
|
106
|
+
) : null}
|
|
107
|
+
</div>
|
|
108
|
+
);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const TreeRoot = ({ children }: { children: ReactNode }) => (
|
|
112
|
+
<div className="my-4 rounded-xl border border-border bg-card p-4 font-mono text-sm">
|
|
113
|
+
{children}
|
|
114
|
+
</div>
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
export const Tree = Object.assign(TreeRoot, {
|
|
118
|
+
File: TreeFile,
|
|
119
|
+
Folder: TreeFolder,
|
|
120
|
+
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
interface TypeField {
|
|
2
|
+
description?: string;
|
|
3
|
+
type?: string;
|
|
4
|
+
optional?: boolean;
|
|
5
|
+
required?: boolean;
|
|
6
|
+
default?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const TypeTable = ({ type }: { type: Record<string, TypeField> }) => {
|
|
10
|
+
const entries = Object.entries(type ?? {}).map(([name, field]) => ({
|
|
11
|
+
name,
|
|
12
|
+
...field,
|
|
13
|
+
}));
|
|
14
|
+
const hasDefault = entries.some((entry) => entry.default);
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<div className="no-scrollbar my-4 w-full overflow-y-auto rounded-xl border border-border">
|
|
18
|
+
<table className="relative w-full overflow-hidden border-none text-sm">
|
|
19
|
+
<thead>
|
|
20
|
+
<tr>
|
|
21
|
+
<th className="border-b border-border bg-background/70 px-3 py-2.5 text-left text-xs uppercase tracking-wider text-muted-foreground">
|
|
22
|
+
Field
|
|
23
|
+
</th>
|
|
24
|
+
<th className="border-b border-border bg-background/70 px-3 py-2.5 text-left text-xs uppercase tracking-wider text-muted-foreground">
|
|
25
|
+
Type
|
|
26
|
+
</th>
|
|
27
|
+
<th className="border-b border-border bg-background/70 px-3 py-2.5 text-left text-xs uppercase tracking-wider text-muted-foreground">
|
|
28
|
+
Description
|
|
29
|
+
</th>
|
|
30
|
+
<th className="border-b border-border bg-background/70 px-3 py-2.5 text-left text-xs uppercase tracking-wider text-muted-foreground">
|
|
31
|
+
Required
|
|
32
|
+
</th>
|
|
33
|
+
{hasDefault ? (
|
|
34
|
+
<th className="border-b border-border bg-background/70 px-3 py-2.5 text-left text-xs uppercase tracking-wider text-muted-foreground">
|
|
35
|
+
Default
|
|
36
|
+
</th>
|
|
37
|
+
) : null}
|
|
38
|
+
</tr>
|
|
39
|
+
</thead>
|
|
40
|
+
<tbody>
|
|
41
|
+
{entries.map((entry) => {
|
|
42
|
+
const required = entry.required ?? !entry.optional;
|
|
43
|
+
return (
|
|
44
|
+
<tr key={entry.name}>
|
|
45
|
+
<td className="border-b border-border px-3 py-2.5 text-left align-top">
|
|
46
|
+
<code className="rounded-md bg-muted px-1.5 py-0.5 font-mono text-xs">
|
|
47
|
+
{entry.name}
|
|
48
|
+
</code>
|
|
49
|
+
</td>
|
|
50
|
+
<td className="border-b border-border px-3 py-2.5 text-left align-top">
|
|
51
|
+
{entry.type ?? "\u2014"}
|
|
52
|
+
</td>
|
|
53
|
+
<td className="border-b border-border px-3 py-2.5 text-left align-top">
|
|
54
|
+
{entry.description ?? ""}
|
|
55
|
+
</td>
|
|
56
|
+
<td className="border-b border-border px-3 py-2.5 text-left align-top">
|
|
57
|
+
{required ? "Yes" : "Optional"}
|
|
58
|
+
</td>
|
|
59
|
+
{hasDefault ? (
|
|
60
|
+
<td className="border-b border-border px-3 py-2.5 text-left align-top">
|
|
61
|
+
{entry.default ?? "\u2014"}
|
|
62
|
+
</td>
|
|
63
|
+
) : null}
|
|
64
|
+
</tr>
|
|
65
|
+
);
|
|
66
|
+
})}
|
|
67
|
+
</tbody>
|
|
68
|
+
</table>
|
|
69
|
+
</div>
|
|
70
|
+
);
|
|
71
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
interface UpdateProps {
|
|
4
|
+
label: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
tags?: string[];
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const Update = ({ label, description, tags, children }: UpdateProps) => {
|
|
11
|
+
const anchorId = label.toLowerCase().replaceAll(/\s+/g, "-");
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<div
|
|
15
|
+
className="relative border-b border-border py-8 first:pt-0 last:border-b-0"
|
|
16
|
+
id={anchorId}
|
|
17
|
+
>
|
|
18
|
+
<div className="mb-4 flex flex-wrap items-center gap-3">
|
|
19
|
+
<a
|
|
20
|
+
className="text-lg font-semibold text-foreground hover:underline"
|
|
21
|
+
href={`#${anchorId}`}
|
|
22
|
+
>
|
|
23
|
+
{label}
|
|
24
|
+
</a>
|
|
25
|
+
{description ? (
|
|
26
|
+
<span className="rounded-full bg-muted px-2.5 py-0.5 text-xs font-medium text-muted-foreground">
|
|
27
|
+
{description}
|
|
28
|
+
</span>
|
|
29
|
+
) : null}
|
|
30
|
+
{tags?.map((tag) => (
|
|
31
|
+
<span
|
|
32
|
+
className="rounded-full bg-primary/10 px-2.5 py-0.5 text-xs font-medium text-primary"
|
|
33
|
+
key={tag}
|
|
34
|
+
>
|
|
35
|
+
{tag}
|
|
36
|
+
</span>
|
|
37
|
+
))}
|
|
38
|
+
</div>
|
|
39
|
+
<div className="prose prose-sm text-muted-foreground [&>p:first-child]:mt-0 [&>p:last-child]:mb-0">
|
|
40
|
+
{children}
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
);
|
|
44
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const Video = ({ src }: { src: string }) => (
|
|
2
|
+
<div className="relative overflow-hidden rounded-xl border border-border bg-black pt-[56.25%]">
|
|
3
|
+
<iframe
|
|
4
|
+
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
|
5
|
+
allowFullScreen
|
|
6
|
+
className="absolute inset-0 h-full w-full border-0"
|
|
7
|
+
sandbox="allow-popups allow-presentation allow-scripts"
|
|
8
|
+
src={src}
|
|
9
|
+
title="Video"
|
|
10
|
+
/>
|
|
11
|
+
</div>
|
|
12
|
+
);
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { isValidElement, useCallback, useMemo, useState } from "react";
|
|
4
|
+
import type { MouseEvent, ReactElement, ReactNode } from "react";
|
|
5
|
+
|
|
6
|
+
import { cn } from "@/lib/utils";
|
|
7
|
+
|
|
8
|
+
interface ViewItemProps {
|
|
9
|
+
title: string;
|
|
10
|
+
icon?: ReactNode;
|
|
11
|
+
children: ReactNode;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const View = ({ children }: ViewItemProps) => children as ReactElement;
|
|
15
|
+
|
|
16
|
+
interface ViewGroupProps {
|
|
17
|
+
children: ReactNode;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const ViewGroup = ({ children }: ViewGroupProps) => {
|
|
21
|
+
const items = useMemo(() => {
|
|
22
|
+
const nodes = Array.isArray(children) ? children : [children];
|
|
23
|
+
return nodes.filter(
|
|
24
|
+
(child): child is ReactElement<ViewItemProps> =>
|
|
25
|
+
isValidElement<ViewItemProps>(child) && child.type === View
|
|
26
|
+
);
|
|
27
|
+
}, [children]);
|
|
28
|
+
|
|
29
|
+
const [active, setActive] = useState(0);
|
|
30
|
+
|
|
31
|
+
const handleSelect = useCallback((event: MouseEvent<HTMLButtonElement>) => {
|
|
32
|
+
const index = Number(event.currentTarget.dataset.index ?? "0");
|
|
33
|
+
setActive(index);
|
|
34
|
+
}, []);
|
|
35
|
+
|
|
36
|
+
if (!items.length) {
|
|
37
|
+
return children as ReactElement;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<div className="my-4">
|
|
42
|
+
<div className="mb-4 flex flex-wrap gap-2">
|
|
43
|
+
{items.map((item, index) => (
|
|
44
|
+
<button
|
|
45
|
+
className={cn(
|
|
46
|
+
"inline-flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-sm font-medium transition-colors",
|
|
47
|
+
index === active
|
|
48
|
+
? "bg-primary text-primary-foreground"
|
|
49
|
+
: "bg-muted text-muted-foreground hover:text-foreground"
|
|
50
|
+
)}
|
|
51
|
+
data-index={index}
|
|
52
|
+
key={item.props.title}
|
|
53
|
+
onClick={handleSelect}
|
|
54
|
+
type="button"
|
|
55
|
+
>
|
|
56
|
+
{item.props.icon ? (
|
|
57
|
+
<span className="shrink-0">{item.props.icon}</span>
|
|
58
|
+
) : null}
|
|
59
|
+
{item.props.title}
|
|
60
|
+
</button>
|
|
61
|
+
))}
|
|
62
|
+
</div>
|
|
63
|
+
<div>{items[active]}</div>
|
|
64
|
+
</div>
|
|
65
|
+
);
|
|
66
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { ThemeProvider } from "next-themes";
|
|
4
|
+
|
|
5
|
+
export const Providers = ({ children }: { children: React.ReactNode }) => (
|
|
6
|
+
<ThemeProvider
|
|
7
|
+
attribute="class"
|
|
8
|
+
defaultTheme="system"
|
|
9
|
+
disableTransitionOnChange
|
|
10
|
+
enableColorScheme
|
|
11
|
+
storageKey="theme"
|
|
12
|
+
>
|
|
13
|
+
{children}
|
|
14
|
+
</ThemeProvider>
|
|
15
|
+
);
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { Slot } from "radix-ui";
|
|
2
|
+
import type { ComponentProps } from "react";
|
|
3
|
+
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
5
|
+
|
|
6
|
+
const Breadcrumb = ({ ...props }: ComponentProps<"nav">) => (
|
|
7
|
+
<nav aria-label="breadcrumb" data-slot="breadcrumb" {...props} />
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
const BreadcrumbList = ({ className, ...props }: ComponentProps<"ol">) => (
|
|
11
|
+
<ol
|
|
12
|
+
data-slot="breadcrumb-list"
|
|
13
|
+
className={cn(
|
|
14
|
+
"flex flex-wrap items-center gap-1.5 text-sm text-muted-foreground break-words sm:gap-2.5",
|
|
15
|
+
className
|
|
16
|
+
)}
|
|
17
|
+
{...props}
|
|
18
|
+
/>
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
const BreadcrumbItem = ({ className, ...props }: ComponentProps<"li">) => (
|
|
22
|
+
<li
|
|
23
|
+
data-slot="breadcrumb-item"
|
|
24
|
+
className={cn("inline-flex items-center gap-1.5", className)}
|
|
25
|
+
{...props}
|
|
26
|
+
/>
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
const BreadcrumbLink = ({
|
|
30
|
+
asChild,
|
|
31
|
+
className,
|
|
32
|
+
...props
|
|
33
|
+
}: ComponentProps<"a"> & { asChild?: boolean }) => {
|
|
34
|
+
const Comp = asChild ? Slot.Root : "a";
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<Comp
|
|
38
|
+
data-slot="breadcrumb-link"
|
|
39
|
+
className={cn("transition-colors hover:text-foreground", className)}
|
|
40
|
+
{...props}
|
|
41
|
+
/>
|
|
42
|
+
);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const BreadcrumbPage = ({ className, ...props }: ComponentProps<"span">) => (
|
|
46
|
+
<span
|
|
47
|
+
data-slot="breadcrumb-page"
|
|
48
|
+
aria-disabled="true"
|
|
49
|
+
aria-current="page"
|
|
50
|
+
className={cn("font-normal text-foreground", className)}
|
|
51
|
+
{...props}
|
|
52
|
+
/>
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const BreadcrumbSeparator = ({
|
|
56
|
+
children,
|
|
57
|
+
className,
|
|
58
|
+
...props
|
|
59
|
+
}: ComponentProps<"li">) => (
|
|
60
|
+
<li
|
|
61
|
+
data-slot="breadcrumb-separator"
|
|
62
|
+
role="presentation"
|
|
63
|
+
aria-hidden="true"
|
|
64
|
+
className={cn("[&>svg]:size-3.5", className)}
|
|
65
|
+
{...props}
|
|
66
|
+
>
|
|
67
|
+
{children ?? (
|
|
68
|
+
<svg
|
|
69
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
70
|
+
width="16"
|
|
71
|
+
height="16"
|
|
72
|
+
viewBox="0 0 24 24"
|
|
73
|
+
fill="none"
|
|
74
|
+
stroke="currentColor"
|
|
75
|
+
strokeWidth="2"
|
|
76
|
+
strokeLinecap="round"
|
|
77
|
+
strokeLinejoin="round"
|
|
78
|
+
>
|
|
79
|
+
<path d="m9 18 6-6-6-6" />
|
|
80
|
+
</svg>
|
|
81
|
+
)}
|
|
82
|
+
</li>
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
export {
|
|
86
|
+
Breadcrumb,
|
|
87
|
+
BreadcrumbItem,
|
|
88
|
+
BreadcrumbLink,
|
|
89
|
+
BreadcrumbList,
|
|
90
|
+
BreadcrumbPage,
|
|
91
|
+
BreadcrumbSeparator,
|
|
92
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { mergeProps } from "@base-ui/react/merge-props";
|
|
2
|
+
import { useRender } from "@base-ui/react/use-render";
|
|
3
|
+
import { cva } from "class-variance-authority";
|
|
4
|
+
import type { VariantProps } from "class-variance-authority";
|
|
5
|
+
import type * as React from "react";
|
|
6
|
+
|
|
7
|
+
import { cn } from "@/lib/utils";
|
|
8
|
+
|
|
9
|
+
const buttonVariants = cva(
|
|
10
|
+
"group/button inline-flex shrink-0 select-none items-center justify-center whitespace-nowrap rounded-lg border border-transparent bg-clip-padding font-medium text-sm outline-none transition-[color,background-color,border-color,box-shadow,opacity,transform] duration-150 ease-out focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/50 focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-45 aria-disabled:pointer-events-none aria-disabled:cursor-not-allowed aria-disabled:opacity-45 aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
11
|
+
{
|
|
12
|
+
defaultVariants: {
|
|
13
|
+
size: "default",
|
|
14
|
+
variant: "default",
|
|
15
|
+
},
|
|
16
|
+
variants: {
|
|
17
|
+
size: {
|
|
18
|
+
default:
|
|
19
|
+
"h-10 gap-1.5 px-3 has-data-[icon=inline-end]:pr-2.5 has-data-[icon=inline-start]:pl-2.5",
|
|
20
|
+
icon: "size-10",
|
|
21
|
+
"icon-lg": "size-11",
|
|
22
|
+
"icon-sm":
|
|
23
|
+
"size-9 in-data-[slot=button-group]:rounded-lg rounded-[min(var(--radius-md),12px)]",
|
|
24
|
+
"icon-xs":
|
|
25
|
+
"size-8 in-data-[slot=button-group]:rounded-lg rounded-[min(var(--radius-md),10px)] [&_svg:not([class*='size-'])]:size-3",
|
|
26
|
+
input:
|
|
27
|
+
"h-[var(--field-height)] gap-2 rounded-[var(--field-radius)] px-[var(--field-padding-x)] py-[var(--field-padding-y)]",
|
|
28
|
+
"input-sm":
|
|
29
|
+
"h-[var(--field-height-sm)] gap-2 rounded-[var(--field-radius)] px-[var(--field-padding-x)] py-[var(--field-padding-y)]",
|
|
30
|
+
lg: "h-11 gap-1.5 px-4 has-data-[icon=inline-end]:pr-3.5 has-data-[icon=inline-start]:pl-3.5",
|
|
31
|
+
sm: "h-9 gap-1 in-data-[slot=button-group]:rounded-lg rounded-[min(var(--radius-md),12px)] px-3 text-[0.8rem] has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-3.5",
|
|
32
|
+
xs: "h-8 gap-1 in-data-[slot=button-group]:rounded-lg rounded-[min(var(--radius-md),10px)] px-2.5 text-xs has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2 [&_svg:not([class*='size-'])]:size-3",
|
|
33
|
+
},
|
|
34
|
+
variant: {
|
|
35
|
+
default:
|
|
36
|
+
"bg-primary text-primary-foreground hover:bg-primary/90 active:bg-primary/95 aria-pressed:bg-primary/95",
|
|
37
|
+
destructive:
|
|
38
|
+
"bg-red-600 text-white hover:bg-red-700 focus-visible:border-red-600 focus-visible:ring-red-500/30 active:bg-red-800 aria-pressed:bg-red-800 dark:bg-red-500 dark:aria-pressed:bg-red-300 dark:active:bg-red-300 dark:hover:bg-red-400",
|
|
39
|
+
destructiveSecondary:
|
|
40
|
+
"border-red-200 text-red-700 hover:bg-red-50 active:bg-red-100 aria-pressed:bg-red-100 dark:border-red-800 dark:text-red-300 dark:aria-pressed:bg-red-950 dark:active:bg-red-950 dark:hover:bg-red-950/60",
|
|
41
|
+
ghost:
|
|
42
|
+
"hover:bg-muted hover:text-foreground active:bg-muted/80 aria-expanded:bg-muted aria-pressed:bg-muted/80 dark:aria-pressed:bg-muted/60 dark:active:bg-muted/60 dark:hover:bg-muted/50",
|
|
43
|
+
input:
|
|
44
|
+
"border-input bg-card font-normal font-sans text-base text-foreground leading-snug shadow-input hover:border-input-hover focus-visible:ring-2 focus-visible:ring-ring/15 focus-visible:ring-offset-1 focus-visible:ring-offset-background active:border-input-hover/80 aria-pressed:border-input-hover aria-invalid:border-destructive-foreground data-[placeholder]:text-placeholder-foreground",
|
|
45
|
+
link: "text-primary underline-offset-4 hover:underline active:opacity-80 aria-pressed:underline aria-pressed:opacity-80",
|
|
46
|
+
outline:
|
|
47
|
+
"border-border bg-background hover:bg-muted hover:text-foreground active:bg-muted/80 aria-expanded:bg-muted aria-pressed:bg-muted/80 dark:border-input dark:bg-input/30 dark:aria-pressed:bg-input/60 dark:active:bg-input/60 dark:hover:bg-input/50",
|
|
48
|
+
secondary:
|
|
49
|
+
"bg-secondary text-secondary-foreground hover:bg-secondary/85 active:bg-secondary/75 aria-expanded:bg-secondary aria-pressed:bg-secondary/75",
|
|
50
|
+
success:
|
|
51
|
+
"bg-green-600 text-white hover:bg-green-700 focus-visible:border-green-600 focus-visible:ring-green-500/30 active:bg-green-800 aria-pressed:bg-green-800 dark:bg-green-500 dark:aria-pressed:bg-green-300 dark:active:bg-green-300 dark:hover:bg-green-400",
|
|
52
|
+
successSecondary:
|
|
53
|
+
"border-green-200 text-green-700 hover:bg-green-50 active:bg-green-100 aria-pressed:bg-green-100 dark:border-green-800 dark:text-green-300 dark:aria-pressed:bg-green-950 dark:active:bg-green-950 dark:hover:bg-green-950/60",
|
|
54
|
+
warning:
|
|
55
|
+
"bg-yellow-600 text-white hover:bg-yellow-700 focus-visible:border-yellow-600 focus-visible:ring-yellow-500/30 active:bg-yellow-800 aria-pressed:bg-yellow-800 dark:bg-yellow-500 dark:text-yellow-950 dark:aria-pressed:bg-yellow-300 dark:active:bg-yellow-300 dark:hover:bg-yellow-400",
|
|
56
|
+
warningSecondary:
|
|
57
|
+
"border-yellow-200 text-yellow-700 hover:bg-yellow-50 active:bg-yellow-100 aria-pressed:bg-yellow-100 dark:border-yellow-800 dark:text-yellow-300 dark:aria-pressed:bg-yellow-950 dark:active:bg-yellow-950 dark:hover:bg-yellow-950/60",
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
}
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const Button = ({
|
|
64
|
+
className,
|
|
65
|
+
variant = "default",
|
|
66
|
+
size = "default",
|
|
67
|
+
asChild = false,
|
|
68
|
+
children,
|
|
69
|
+
...props
|
|
70
|
+
}: React.ComponentProps<"button"> &
|
|
71
|
+
VariantProps<typeof buttonVariants> & {
|
|
72
|
+
asChild?: boolean;
|
|
73
|
+
}) =>
|
|
74
|
+
useRender({
|
|
75
|
+
defaultTagName: "button",
|
|
76
|
+
props: mergeProps<"button">(
|
|
77
|
+
{
|
|
78
|
+
className: cn(buttonVariants({ className, size, variant })),
|
|
79
|
+
},
|
|
80
|
+
asChild ? props : { ...props, children }
|
|
81
|
+
),
|
|
82
|
+
render: asChild ? (children as React.ReactElement) : undefined,
|
|
83
|
+
state: {
|
|
84
|
+
size,
|
|
85
|
+
slot: "button",
|
|
86
|
+
variant,
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
export { Button, buttonVariants };
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type * as React from "react";
|
|
2
|
+
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
|
|
5
|
+
const Card = ({
|
|
6
|
+
className,
|
|
7
|
+
size = "default",
|
|
8
|
+
...props
|
|
9
|
+
}: React.ComponentProps<"div"> & { size?: "default" | "sm" }) => (
|
|
10
|
+
<div
|
|
11
|
+
className={cn(
|
|
12
|
+
"group/card flex flex-col gap-4 overflow-hidden rounded-xl bg-card py-4 text-card-foreground text-sm ring-1 ring-foreground/10 has-[>img:first-child]:pt-0 has-data-[slot=card-footer]:pb-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl",
|
|
13
|
+
className
|
|
14
|
+
)}
|
|
15
|
+
data-size={size}
|
|
16
|
+
data-slot="card"
|
|
17
|
+
{...props}
|
|
18
|
+
/>
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
const CardHeader = ({ className, ...props }: React.ComponentProps<"div">) => (
|
|
22
|
+
<div
|
|
23
|
+
className={cn(
|
|
24
|
+
"group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl px-4 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] group-data-[size=sm]/card:px-3 [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3",
|
|
25
|
+
className
|
|
26
|
+
)}
|
|
27
|
+
data-slot="card-header"
|
|
28
|
+
{...props}
|
|
29
|
+
/>
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const CardTitle = ({ className, ...props }: React.ComponentProps<"div">) => (
|
|
33
|
+
<div
|
|
34
|
+
className={cn(
|
|
35
|
+
"font-medium text-base leading-snug group-data-[size=sm]/card:text-sm",
|
|
36
|
+
className
|
|
37
|
+
)}
|
|
38
|
+
data-slot="card-title"
|
|
39
|
+
{...props}
|
|
40
|
+
/>
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const CardDescription = ({
|
|
44
|
+
className,
|
|
45
|
+
...props
|
|
46
|
+
}: React.ComponentProps<"div">) => (
|
|
47
|
+
<div
|
|
48
|
+
className={cn("text-muted-foreground text-sm", className)}
|
|
49
|
+
data-slot="card-description"
|
|
50
|
+
{...props}
|
|
51
|
+
/>
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const CardAction = ({ className, ...props }: React.ComponentProps<"div">) => (
|
|
55
|
+
<div
|
|
56
|
+
className={cn(
|
|
57
|
+
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
|
|
58
|
+
className
|
|
59
|
+
)}
|
|
60
|
+
data-slot="card-action"
|
|
61
|
+
{...props}
|
|
62
|
+
/>
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const CardContent = ({ className, ...props }: React.ComponentProps<"div">) => (
|
|
66
|
+
<div
|
|
67
|
+
className={cn("px-4 group-data-[size=sm]/card:px-3", className)}
|
|
68
|
+
data-slot="card-content"
|
|
69
|
+
{...props}
|
|
70
|
+
/>
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const CardFooter = ({ className, ...props }: React.ComponentProps<"div">) => (
|
|
74
|
+
<div
|
|
75
|
+
className={cn(
|
|
76
|
+
"flex items-center rounded-b-xl border-t bg-muted/50 p-4 group-data-[size=sm]/card:p-3",
|
|
77
|
+
className
|
|
78
|
+
)}
|
|
79
|
+
data-slot="card-footer"
|
|
80
|
+
{...props}
|
|
81
|
+
/>
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
export {
|
|
85
|
+
Card,
|
|
86
|
+
CardHeader,
|
|
87
|
+
CardFooter,
|
|
88
|
+
CardTitle,
|
|
89
|
+
CardAction,
|
|
90
|
+
CardDescription,
|
|
91
|
+
CardContent,
|
|
92
|
+
};
|