blodemd 0.0.11 → 0.0.13
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 +11 -47
- package/dev-server/app/layout.tsx +1 -1
- package/dev-server/next.config.js +19 -9
- package/dev-server/tsconfig.json +0 -3
- package/dist/cli.mjs +732 -123
- package/dist/cli.mjs.map +1 -1
- package/docs/app/globals.css +15 -1
- package/docs/components/api/api-playground.tsx +2 -2
- package/docs/components/docs/copy-page-menu.tsx +55 -27
- package/docs/components/docs/doc-header.tsx +1 -1
- package/docs/components/docs/doc-shell.tsx +89 -88
- package/docs/components/docs/doc-sidebar.tsx +6 -3
- package/docs/components/docs/doc-toc.tsx +1 -1
- package/docs/components/docs/mobile-nav.tsx +8 -16
- package/docs/components/docs/sidebar-scroll-area.tsx +58 -0
- package/docs/components/git/repo-picker.tsx +526 -0
- package/docs/components/mdx/agent-instructions.tsx +17 -0
- package/docs/components/mdx/code-block.tsx +6 -1
- package/docs/components/mdx/code-group.tsx +1 -1
- package/docs/components/mdx/iframe.tsx +62 -0
- package/docs/components/mdx/index.tsx +4 -0
- package/docs/components/mdx/tabs.tsx +5 -5
- package/docs/components/mdx/video.tsx +45 -12
- package/docs/components/third-parties.tsx +29 -0
- package/docs/components/ui/badge.tsx +61 -0
- package/docs/components/ui/breadcrumb.tsx +61 -41
- package/docs/components/ui/button-group.tsx +83 -0
- package/docs/components/ui/button.tsx +30 -55
- package/docs/components/ui/command.tsx +32 -4
- package/docs/components/ui/copy-button.tsx +12 -19
- package/docs/components/ui/dialog.tsx +50 -1
- package/docs/components/ui/input.tsx +16 -97
- package/docs/components/ui/kbd.tsx +98 -0
- package/docs/components/ui/morph-icon.tsx +79 -0
- package/docs/components/ui/popover.tsx +225 -30
- package/docs/components/ui/search.tsx +0 -9
- package/docs/components/ui/sheet.tsx +30 -1
- package/docs/components/ui/sidebar.tsx +332 -7
- package/docs/components/ui/site-footer.tsx +6 -4
- package/docs/components/ui/skeleton.tsx +11 -0
- package/docs/components/ui/switch.tsx +32 -0
- package/docs/components/ui/tabs.tsx +138 -0
- package/docs/lib/api-client.ts +72 -0
- package/docs/lib/contextual-options.ts +9 -0
- package/docs/lib/dashboard-session.ts +167 -0
- package/docs/lib/db.ts +13 -0
- package/docs/lib/env.ts +4 -3
- package/docs/lib/etag.ts +22 -0
- package/docs/lib/github-install.ts +33 -0
- package/docs/lib/project-authz.ts +46 -0
- package/docs/lib/routes.ts +5 -1
- package/docs/lib/supabase.ts +30 -6
- package/docs/lib/tenancy.ts +1 -0
- package/docs/lib/tenant-static.ts +206 -4
- package/docs/lib/tenants.ts +5 -1
- package/docs/lib/time-ago.ts +24 -0
- package/docs/lib/use-tab-observer.ts +71 -0
- package/package.json +3 -1
- package/packages/@repo/common/package.json +2 -2
- package/packages/@repo/contracts/dist/git.d.ts +28 -0
- package/packages/@repo/contracts/dist/git.d.ts.map +1 -0
- package/packages/@repo/contracts/dist/git.js +24 -0
- package/packages/@repo/contracts/dist/index.d.ts +1 -1
- package/packages/@repo/contracts/dist/index.d.ts.map +1 -1
- package/packages/@repo/contracts/dist/index.js +1 -1
- package/packages/@repo/contracts/package.json +2 -2
- package/packages/@repo/contracts/src/git.ts +31 -0
- package/packages/@repo/contracts/src/index.ts +1 -1
- package/packages/@repo/models/dist/docs-config.d.ts +6 -0
- package/packages/@repo/models/dist/docs-config.d.ts.map +1 -1
- package/packages/@repo/models/dist/docs-config.js +1 -0
- package/packages/@repo/models/package.json +2 -2
- package/packages/@repo/models/src/docs-config.ts +1 -0
- package/packages/@repo/prebuild/package.json +2 -2
- package/packages/@repo/previewing/dist/index.d.ts +3 -0
- package/packages/@repo/previewing/dist/index.d.ts.map +1 -1
- package/packages/@repo/previewing/dist/index.js +48 -0
- package/packages/@repo/previewing/package.json +2 -2
- package/packages/@repo/previewing/src/index.ts +56 -0
- package/packages/@repo/validation/package.json +2 -2
- package/packages/@repo/validation/src/blodemd-docs-schema.json +1 -0
- package/scripts/prepare-package.mjs +14 -0
- package/packages/@repo/contracts/dist/api-key.d.ts +0 -30
- package/packages/@repo/contracts/dist/api-key.d.ts.map +0 -1
- package/packages/@repo/contracts/dist/api-key.js +0 -20
- package/packages/@repo/contracts/src/api-key.ts +0 -27
|
@@ -4,6 +4,7 @@ import { XIcon } from "blode-icons-react";
|
|
|
4
4
|
import { Dialog as DialogPrimitive } from "radix-ui";
|
|
5
5
|
import * as React from "react";
|
|
6
6
|
|
|
7
|
+
import { Button } from "@/components/ui/button";
|
|
7
8
|
import { cn } from "@/lib/utils";
|
|
8
9
|
|
|
9
10
|
const Dialog = ({
|
|
@@ -12,12 +13,24 @@ const Dialog = ({
|
|
|
12
13
|
<DialogPrimitive.Root data-slot="dialog" {...props} />
|
|
13
14
|
);
|
|
14
15
|
|
|
16
|
+
const DialogTrigger = ({
|
|
17
|
+
...props
|
|
18
|
+
}: React.ComponentProps<typeof DialogPrimitive.Trigger>) => (
|
|
19
|
+
<DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
|
|
20
|
+
);
|
|
21
|
+
|
|
15
22
|
const DialogPortal = ({
|
|
16
23
|
...props
|
|
17
24
|
}: React.ComponentProps<typeof DialogPrimitive.Portal>) => (
|
|
18
25
|
<DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
|
|
19
26
|
);
|
|
20
27
|
|
|
28
|
+
const DialogClose = ({
|
|
29
|
+
...props
|
|
30
|
+
}: React.ComponentProps<typeof DialogPrimitive.Close>) => (
|
|
31
|
+
<DialogPrimitive.Close data-slot="dialog-close" {...props} />
|
|
32
|
+
);
|
|
33
|
+
|
|
21
34
|
const DialogOverlay = ({
|
|
22
35
|
className,
|
|
23
36
|
...props
|
|
@@ -72,6 +85,31 @@ const DialogHeader = ({ className, ...props }: React.ComponentProps<"div">) => (
|
|
|
72
85
|
/>
|
|
73
86
|
);
|
|
74
87
|
|
|
88
|
+
const DialogFooter = ({
|
|
89
|
+
className,
|
|
90
|
+
showCloseButton = false,
|
|
91
|
+
children,
|
|
92
|
+
...props
|
|
93
|
+
}: React.ComponentProps<"div"> & {
|
|
94
|
+
showCloseButton?: boolean;
|
|
95
|
+
}) => (
|
|
96
|
+
<div
|
|
97
|
+
data-slot="dialog-footer"
|
|
98
|
+
className={cn(
|
|
99
|
+
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
|
100
|
+
className
|
|
101
|
+
)}
|
|
102
|
+
{...props}
|
|
103
|
+
>
|
|
104
|
+
{children}
|
|
105
|
+
{showCloseButton && (
|
|
106
|
+
<DialogPrimitive.Close asChild>
|
|
107
|
+
<Button variant="outline">Close</Button>
|
|
108
|
+
</DialogPrimitive.Close>
|
|
109
|
+
)}
|
|
110
|
+
</div>
|
|
111
|
+
);
|
|
112
|
+
|
|
75
113
|
const DialogTitle = ({
|
|
76
114
|
className,
|
|
77
115
|
...props
|
|
@@ -94,4 +132,15 @@ const DialogDescription = ({
|
|
|
94
132
|
/>
|
|
95
133
|
);
|
|
96
134
|
|
|
97
|
-
export {
|
|
135
|
+
export {
|
|
136
|
+
Dialog,
|
|
137
|
+
DialogClose,
|
|
138
|
+
DialogContent,
|
|
139
|
+
DialogDescription,
|
|
140
|
+
DialogFooter,
|
|
141
|
+
DialogHeader,
|
|
142
|
+
DialogOverlay,
|
|
143
|
+
DialogPortal,
|
|
144
|
+
DialogTitle,
|
|
145
|
+
DialogTrigger,
|
|
146
|
+
};
|
|
@@ -1,104 +1,23 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { CircleXFilledIcon } from "blode-icons-react";
|
|
4
1
|
import * as React from "react";
|
|
5
2
|
|
|
6
3
|
import { cn } from "@/lib/utils";
|
|
7
4
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
hasError,
|
|
25
|
-
clearable,
|
|
26
|
-
onClear,
|
|
27
|
-
leftAddon,
|
|
28
|
-
rightAddon,
|
|
29
|
-
leftControl,
|
|
30
|
-
rightControl,
|
|
31
|
-
...props
|
|
32
|
-
},
|
|
33
|
-
ref
|
|
34
|
-
) => (
|
|
35
|
-
<label
|
|
36
|
-
className={cn("relative w-full", {
|
|
37
|
-
"input-group": !!leftAddon || !!rightAddon,
|
|
38
|
-
})}
|
|
39
|
-
>
|
|
40
|
-
{leftAddon && (
|
|
41
|
-
<span className="shrink-0 cursor-pointer">{leftAddon}</span>
|
|
42
|
-
)}
|
|
43
|
-
|
|
44
|
-
{leftControl && (
|
|
45
|
-
<div className="absolute top-0 left-0 flex h-full flex-row place-items-center items-center justify-center">
|
|
46
|
-
{leftControl}
|
|
47
|
-
</div>
|
|
48
|
-
)}
|
|
49
|
-
|
|
50
|
-
<div className="w-full">
|
|
51
|
-
<input
|
|
52
|
-
className={cn(
|
|
53
|
-
"input flex h-[var(--field-height)] w-full rounded-[var(--field-radius)] border border-input bg-card px-[var(--field-padding-x)] py-[var(--field-padding-y)] font-normal font-sans text-base text-foreground leading-snug shadow-input transition-colors placeholder:text-placeholder-foreground hover:border-input-hover focus:border-ring focus:outline-hidden focus:ring-2 focus:ring-ring/15 focus:ring-offset-1 focus:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50",
|
|
54
|
-
{
|
|
55
|
-
"border-destructive-foreground": hasError,
|
|
56
|
-
"hover:border-input! focus:border-input!": props.readOnly,
|
|
57
|
-
"pr-12": clearable && !!props.value && rightControl,
|
|
58
|
-
"pr-9": clearable && !!props.value,
|
|
59
|
-
},
|
|
60
|
-
className
|
|
61
|
-
)}
|
|
62
|
-
ref={ref}
|
|
63
|
-
{...props}
|
|
64
|
-
/>
|
|
65
|
-
|
|
66
|
-
{clearable && !!props.value && (
|
|
67
|
-
<div className="absolute top-0 right-0 flex flex-row gap-1 pr-3">
|
|
68
|
-
<button
|
|
69
|
-
aria-label="clear input"
|
|
70
|
-
className={cn(
|
|
71
|
-
"flex h-[var(--field-height)] cursor-pointer items-center justify-center p-0! text-muted-foreground",
|
|
72
|
-
clearClassName
|
|
73
|
-
)}
|
|
74
|
-
onClick={onClear}
|
|
75
|
-
type="button"
|
|
76
|
-
>
|
|
77
|
-
<CircleXFilledIcon className="size-5 text-muted-foreground/50" />
|
|
78
|
-
</button>
|
|
79
|
-
</div>
|
|
80
|
-
)}
|
|
81
|
-
</div>
|
|
82
|
-
|
|
83
|
-
{rightControl && (
|
|
84
|
-
<div
|
|
85
|
-
className={cn(
|
|
86
|
-
"absolute top-0 right-0 flex h-full flex-row place-items-center items-center justify-center",
|
|
87
|
-
{
|
|
88
|
-
"right-9": clearable && !!props.value,
|
|
89
|
-
}
|
|
90
|
-
)}
|
|
91
|
-
>
|
|
92
|
-
{rightControl}
|
|
93
|
-
</div>
|
|
94
|
-
)}
|
|
95
|
-
|
|
96
|
-
{rightAddon && (
|
|
97
|
-
<span className="shrink-0 cursor-pointer">{rightAddon}</span>
|
|
98
|
-
)}
|
|
99
|
-
</label>
|
|
100
|
-
)
|
|
5
|
+
const Input = ({
|
|
6
|
+
className,
|
|
7
|
+
type,
|
|
8
|
+
...props
|
|
9
|
+
}: React.ComponentProps<"input">) => (
|
|
10
|
+
<input
|
|
11
|
+
type={type}
|
|
12
|
+
data-slot="input"
|
|
13
|
+
className={cn(
|
|
14
|
+
"h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base transition-[color,box-shadow] outline-none selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:bg-input/30",
|
|
15
|
+
"focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50",
|
|
16
|
+
"aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40",
|
|
17
|
+
className
|
|
18
|
+
)}
|
|
19
|
+
{...props}
|
|
20
|
+
/>
|
|
101
21
|
);
|
|
102
|
-
Input.displayName = "Input";
|
|
103
22
|
|
|
104
23
|
export { Input };
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ArrowCornerDownLeftIcon,
|
|
3
|
+
ArrowDownIcon,
|
|
4
|
+
ArrowLeftXIcon,
|
|
5
|
+
ArrowUpIcon,
|
|
6
|
+
ArrowWall2RightIcon,
|
|
7
|
+
CmdIcon,
|
|
8
|
+
ControlIcon,
|
|
9
|
+
OptIcon,
|
|
10
|
+
ShiftIcon,
|
|
11
|
+
} from "blode-icons-react";
|
|
12
|
+
import { cva } from "class-variance-authority";
|
|
13
|
+
import type { VariantProps } from "class-variance-authority";
|
|
14
|
+
import * as React from "react";
|
|
15
|
+
|
|
16
|
+
import { cn } from "@/lib/utils";
|
|
17
|
+
|
|
18
|
+
type KbdIcon =
|
|
19
|
+
| "mod"
|
|
20
|
+
| "shift"
|
|
21
|
+
| "enter"
|
|
22
|
+
| "command"
|
|
23
|
+
| "ctrl"
|
|
24
|
+
| "alt"
|
|
25
|
+
| "tab"
|
|
26
|
+
| "backspace"
|
|
27
|
+
| "up"
|
|
28
|
+
| "down";
|
|
29
|
+
|
|
30
|
+
const kbdVariants = cva(
|
|
31
|
+
"pointer-events-none inline-flex h-5 w-fit min-w-5 select-none items-center justify-center gap-1 rounded-sm px-1.5 font-medium font-sans text-xs ring-1 ring-inset [&_svg:not([class*='size-'])]:size-3",
|
|
32
|
+
{
|
|
33
|
+
defaultVariants: {
|
|
34
|
+
variant: "default",
|
|
35
|
+
},
|
|
36
|
+
variants: {
|
|
37
|
+
variant: {
|
|
38
|
+
default:
|
|
39
|
+
"bg-muted text-muted-foreground ring-border [[data-slot=tooltip-content]_&]:bg-background/20 [[data-slot=tooltip-content]_&]:text-background [[data-slot=tooltip-content]_&]:ring-background/20 dark:[[data-slot=tooltip-content]_&]:bg-background/10 dark:[[data-slot=tooltip-content]_&]:ring-background/10",
|
|
40
|
+
tooltip:
|
|
41
|
+
"bg-background/20 text-background ring-background/20 dark:bg-background/10 dark:ring-background/10",
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
interface KbdProps
|
|
48
|
+
extends
|
|
49
|
+
React.ComponentPropsWithoutRef<"kbd">,
|
|
50
|
+
VariantProps<typeof kbdVariants> {
|
|
51
|
+
icon?: KbdIcon;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const iconMap: Record<KbdIcon, React.ReactNode> = {
|
|
55
|
+
alt: <OptIcon className="size-3" />,
|
|
56
|
+
backspace: <ArrowLeftXIcon className="size-3" />,
|
|
57
|
+
command: <CmdIcon className="size-3" />,
|
|
58
|
+
ctrl: <ControlIcon className="size-3" />,
|
|
59
|
+
down: <ArrowDownIcon className="size-3" />,
|
|
60
|
+
enter: <ArrowCornerDownLeftIcon className="size-3" />,
|
|
61
|
+
mod: <CmdIcon className="size-3" />,
|
|
62
|
+
shift: <ShiftIcon className="size-3" />,
|
|
63
|
+
tab: <ArrowWall2RightIcon className="size-3" />,
|
|
64
|
+
up: <ArrowUpIcon className="size-3" />,
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const Kbd = React.forwardRef<React.ElementRef<"kbd">, KbdProps>(
|
|
68
|
+
({ className, variant, children, icon, ...props }, ref) => {
|
|
69
|
+
const content = icon ? iconMap[icon] : children;
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<kbd
|
|
73
|
+
className={cn(kbdVariants({ variant }), className)}
|
|
74
|
+
data-slot="kbd"
|
|
75
|
+
ref={ref}
|
|
76
|
+
{...props}
|
|
77
|
+
>
|
|
78
|
+
{content}
|
|
79
|
+
</kbd>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
);
|
|
83
|
+
Kbd.displayName = "Kbd";
|
|
84
|
+
|
|
85
|
+
const KbdGroup = React.forwardRef<
|
|
86
|
+
React.ElementRef<"kbd">,
|
|
87
|
+
React.ComponentPropsWithoutRef<"kbd">
|
|
88
|
+
>(({ className, ...props }, ref) => (
|
|
89
|
+
<kbd
|
|
90
|
+
className={cn("inline-flex items-center gap-1", className)}
|
|
91
|
+
data-slot="kbd-group"
|
|
92
|
+
ref={ref}
|
|
93
|
+
{...props}
|
|
94
|
+
/>
|
|
95
|
+
));
|
|
96
|
+
KbdGroup.displayName = "KbdGroup";
|
|
97
|
+
|
|
98
|
+
export { Kbd, KbdGroup };
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { motion, useReducedMotion } from "motion/react";
|
|
4
|
+
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
|
|
7
|
+
type MorphIconName = "cross" | "menu";
|
|
8
|
+
|
|
9
|
+
interface MorphIconProps extends React.SVGAttributes<SVGSVGElement> {
|
|
10
|
+
icon: MorphIconName;
|
|
11
|
+
size?: number;
|
|
12
|
+
strokeWidth?: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const ICONS = {
|
|
16
|
+
cross: {
|
|
17
|
+
opacity: [1, 1, 0] as const,
|
|
18
|
+
paths: ["M2.5 2.5L11.5 11.5", "M11.5 2.5L2.5 11.5", "M7 7L7 7"] as const,
|
|
19
|
+
},
|
|
20
|
+
menu: {
|
|
21
|
+
opacity: [1, 1, 1] as const,
|
|
22
|
+
paths: ["M1 2.5L13 2.5", "M1 7L13 7", "M1 11.5L13 11.5"] as const,
|
|
23
|
+
},
|
|
24
|
+
} as const;
|
|
25
|
+
|
|
26
|
+
const transition = {
|
|
27
|
+
duration: 0.3,
|
|
28
|
+
ease: [0.4, 0, 0.2, 1] as const,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const instantTransition = { duration: 0 };
|
|
32
|
+
|
|
33
|
+
export const MorphIcon = ({
|
|
34
|
+
icon,
|
|
35
|
+
size = 32,
|
|
36
|
+
strokeWidth = 2,
|
|
37
|
+
className,
|
|
38
|
+
style,
|
|
39
|
+
...props
|
|
40
|
+
}: MorphIconProps) => {
|
|
41
|
+
const prefersReducedMotion = useReducedMotion();
|
|
42
|
+
const def = ICONS[icon];
|
|
43
|
+
const [p0, p1, p2] = def.paths;
|
|
44
|
+
const [o0, o1, o2] = def.opacity;
|
|
45
|
+
const t = prefersReducedMotion ? instantTransition : transition;
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<svg
|
|
49
|
+
aria-hidden="true"
|
|
50
|
+
className={cn("text-current", className)}
|
|
51
|
+
fill="none"
|
|
52
|
+
height={size}
|
|
53
|
+
stroke="currentColor"
|
|
54
|
+
strokeLinecap="round"
|
|
55
|
+
strokeWidth={strokeWidth}
|
|
56
|
+
style={style}
|
|
57
|
+
viewBox="0 0 14 14"
|
|
58
|
+
width={size}
|
|
59
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
60
|
+
{...props}
|
|
61
|
+
>
|
|
62
|
+
<motion.path
|
|
63
|
+
initial={false}
|
|
64
|
+
animate={{ d: p0, opacity: o0 }}
|
|
65
|
+
transition={t}
|
|
66
|
+
/>
|
|
67
|
+
<motion.path
|
|
68
|
+
initial={false}
|
|
69
|
+
animate={{ d: p1, opacity: o1 }}
|
|
70
|
+
transition={t}
|
|
71
|
+
/>
|
|
72
|
+
<motion.path
|
|
73
|
+
initial={false}
|
|
74
|
+
animate={{ d: p2, opacity: o2 }}
|
|
75
|
+
transition={t}
|
|
76
|
+
/>
|
|
77
|
+
</svg>
|
|
78
|
+
);
|
|
79
|
+
};
|
|
@@ -1,15 +1,92 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { Popover as PopoverPrimitive } from "@base-ui/react/popover";
|
|
4
|
-
import
|
|
4
|
+
import * as React from "react";
|
|
5
5
|
|
|
6
6
|
import { cn } from "@/lib/utils";
|
|
7
7
|
|
|
8
|
+
type PopoverInteractOutsideEvent = Event & {
|
|
9
|
+
preventDefault: () => void;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
interface PopoverContextType {
|
|
13
|
+
anchor: HTMLElement | null;
|
|
14
|
+
setAnchor: (anchor: HTMLElement | null) => void;
|
|
15
|
+
setOnInteractOutside: (
|
|
16
|
+
handler?: (event: PopoverInteractOutsideEvent) => void
|
|
17
|
+
) => void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const PopoverContext = React.createContext<PopoverContextType | undefined>(
|
|
21
|
+
undefined
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
const usePopoverContext = () => React.useContext(PopoverContext);
|
|
25
|
+
|
|
26
|
+
const setRef = <T,>(ref: React.Ref<T> | undefined, value: T) => {
|
|
27
|
+
if (typeof ref === "function") {
|
|
28
|
+
ref(value);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (ref && "current" in ref) {
|
|
33
|
+
ref.current = value;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
8
37
|
const Popover = ({
|
|
38
|
+
onOpenChange,
|
|
9
39
|
...props
|
|
10
|
-
}: React.ComponentProps<typeof PopoverPrimitive.Root>) =>
|
|
11
|
-
|
|
12
|
-
|
|
40
|
+
}: React.ComponentProps<typeof PopoverPrimitive.Root>) => {
|
|
41
|
+
const [anchor, setAnchor] = React.useState<HTMLElement | null>(null);
|
|
42
|
+
const onInteractOutsideRef = React.useRef<
|
|
43
|
+
((event: PopoverInteractOutsideEvent) => void) | null
|
|
44
|
+
>(null);
|
|
45
|
+
|
|
46
|
+
const handleOpenChange = React.useCallback<
|
|
47
|
+
NonNullable<
|
|
48
|
+
React.ComponentProps<typeof PopoverPrimitive.Root>["onOpenChange"]
|
|
49
|
+
>
|
|
50
|
+
>(
|
|
51
|
+
(nextOpen, eventDetails) => {
|
|
52
|
+
if (!nextOpen && eventDetails.reason === "outside-press") {
|
|
53
|
+
const interactHandler = onInteractOutsideRef.current;
|
|
54
|
+
|
|
55
|
+
if (interactHandler) {
|
|
56
|
+
const wrappedEvent = Object.create(
|
|
57
|
+
eventDetails.event
|
|
58
|
+
) as PopoverInteractOutsideEvent;
|
|
59
|
+
wrappedEvent.preventDefault = () => eventDetails.cancel();
|
|
60
|
+
interactHandler(wrappedEvent);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
onOpenChange?.(nextOpen, eventDetails);
|
|
65
|
+
},
|
|
66
|
+
[onOpenChange]
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const contextValue = React.useMemo<PopoverContextType>(
|
|
70
|
+
() => ({
|
|
71
|
+
anchor,
|
|
72
|
+
setAnchor,
|
|
73
|
+
setOnInteractOutside: (handler) => {
|
|
74
|
+
onInteractOutsideRef.current = handler ?? null;
|
|
75
|
+
},
|
|
76
|
+
}),
|
|
77
|
+
[anchor]
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<PopoverContext.Provider value={contextValue}>
|
|
82
|
+
<PopoverPrimitive.Root
|
|
83
|
+
data-slot="popover"
|
|
84
|
+
onOpenChange={handleOpenChange}
|
|
85
|
+
{...props}
|
|
86
|
+
/>
|
|
87
|
+
</PopoverContext.Provider>
|
|
88
|
+
);
|
|
89
|
+
};
|
|
13
90
|
|
|
14
91
|
const PopoverTrigger = ({
|
|
15
92
|
asChild = false,
|
|
@@ -19,7 +96,7 @@ const PopoverTrigger = ({
|
|
|
19
96
|
asChild?: boolean;
|
|
20
97
|
}) => {
|
|
21
98
|
const render =
|
|
22
|
-
asChild && children
|
|
99
|
+
asChild && React.isValidElement(children)
|
|
23
100
|
? (children as React.ReactElement)
|
|
24
101
|
: undefined;
|
|
25
102
|
|
|
@@ -34,39 +111,157 @@ const PopoverTrigger = ({
|
|
|
34
111
|
);
|
|
35
112
|
};
|
|
36
113
|
|
|
114
|
+
type PopoverContentProps = React.ComponentProps<typeof PopoverPrimitive.Popup> &
|
|
115
|
+
Pick<
|
|
116
|
+
React.ComponentProps<typeof PopoverPrimitive.Positioner>,
|
|
117
|
+
"align" | "alignOffset" | "side" | "sideOffset"
|
|
118
|
+
> & {
|
|
119
|
+
asChild?: boolean;
|
|
120
|
+
onInteractOutside?: (event: PopoverInteractOutsideEvent) => void;
|
|
121
|
+
onOpenAutoFocus?: (event: Event) => void;
|
|
122
|
+
};
|
|
123
|
+
|
|
37
124
|
const PopoverContent = ({
|
|
125
|
+
asChild = false,
|
|
38
126
|
children,
|
|
39
127
|
className,
|
|
40
128
|
align = "center",
|
|
41
129
|
alignOffset = 0,
|
|
42
130
|
side = "bottom",
|
|
43
131
|
sideOffset = 4,
|
|
132
|
+
initialFocus,
|
|
133
|
+
onInteractOutside,
|
|
134
|
+
onOpenAutoFocus,
|
|
44
135
|
...props
|
|
45
|
-
}:
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
136
|
+
}: PopoverContentProps) => {
|
|
137
|
+
const popoverContext = usePopoverContext();
|
|
138
|
+
|
|
139
|
+
React.useEffect(() => {
|
|
140
|
+
popoverContext?.setOnInteractOutside(onInteractOutside);
|
|
141
|
+
|
|
142
|
+
return () => {
|
|
143
|
+
popoverContext?.setOnInteractOutside(undefined);
|
|
144
|
+
};
|
|
145
|
+
}, [onInteractOutside, popoverContext]);
|
|
146
|
+
|
|
147
|
+
React.useEffect(() => {
|
|
148
|
+
if (!onOpenAutoFocus) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const event = new Event("openAutoFocus", { cancelable: true });
|
|
153
|
+
onOpenAutoFocus(event);
|
|
154
|
+
}, [onOpenAutoFocus]);
|
|
155
|
+
|
|
156
|
+
const render =
|
|
157
|
+
asChild && React.isValidElement(children)
|
|
158
|
+
? (children as React.ReactElement)
|
|
159
|
+
: undefined;
|
|
160
|
+
|
|
161
|
+
return (
|
|
162
|
+
<PopoverPrimitive.Portal>
|
|
163
|
+
<PopoverPrimitive.Positioner
|
|
164
|
+
align={align}
|
|
165
|
+
alignOffset={alignOffset}
|
|
166
|
+
anchor={popoverContext?.anchor}
|
|
167
|
+
className="isolate z-50"
|
|
168
|
+
side={side}
|
|
169
|
+
sideOffset={sideOffset}
|
|
65
170
|
>
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
171
|
+
<PopoverPrimitive.Popup
|
|
172
|
+
className={cn(
|
|
173
|
+
"data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-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 z-50 w-72 origin-(--transform-origin) rounded-md border bg-popover p-4 text-popover-foreground shadow-soft outline-hidden data-closed:animate-out data-open:animate-in",
|
|
174
|
+
className
|
|
175
|
+
)}
|
|
176
|
+
data-slot="popover-content"
|
|
177
|
+
initialFocus={onOpenAutoFocus ? false : initialFocus}
|
|
178
|
+
render={render}
|
|
179
|
+
{...props}
|
|
180
|
+
>
|
|
181
|
+
{asChild ? null : children}
|
|
182
|
+
</PopoverPrimitive.Popup>
|
|
183
|
+
</PopoverPrimitive.Positioner>
|
|
184
|
+
</PopoverPrimitive.Portal>
|
|
185
|
+
);
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const PopoverAnchor = ({
|
|
189
|
+
asChild = false,
|
|
190
|
+
children,
|
|
191
|
+
ref,
|
|
192
|
+
...props
|
|
193
|
+
}: React.ComponentProps<"div"> & {
|
|
194
|
+
asChild?: boolean;
|
|
195
|
+
}) => {
|
|
196
|
+
const popoverContext = usePopoverContext();
|
|
197
|
+
|
|
198
|
+
const handleRef = React.useCallback(
|
|
199
|
+
(node: HTMLElement | null) => {
|
|
200
|
+
popoverContext?.setAnchor(node);
|
|
201
|
+
setRef(ref as React.Ref<HTMLElement> | undefined, node);
|
|
202
|
+
},
|
|
203
|
+
[popoverContext, ref]
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
if (asChild && React.isValidElement(children)) {
|
|
207
|
+
const child = children as React.ReactElement<{
|
|
208
|
+
ref?: React.Ref<HTMLElement>;
|
|
209
|
+
[key: string]: unknown;
|
|
210
|
+
}>;
|
|
211
|
+
|
|
212
|
+
// oxlint-disable-next-line eslint-plugin-react/no-clone-element -- asChild escape hatch
|
|
213
|
+
return React.cloneElement(child, {
|
|
214
|
+
...props,
|
|
215
|
+
ref: (node: HTMLElement | null) => {
|
|
216
|
+
setRef(child.props.ref, node);
|
|
217
|
+
handleRef(node);
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return (
|
|
223
|
+
<div data-slot="popover-anchor" ref={handleRef} {...props}>
|
|
224
|
+
{children}
|
|
225
|
+
</div>
|
|
226
|
+
);
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
const PopoverHeader = ({
|
|
230
|
+
className,
|
|
231
|
+
...props
|
|
232
|
+
}: React.ComponentProps<"div">) => (
|
|
233
|
+
<div
|
|
234
|
+
className={cn("flex flex-col gap-1 text-sm", className)}
|
|
235
|
+
data-slot="popover-header"
|
|
236
|
+
{...props}
|
|
237
|
+
/>
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
const PopoverTitle = ({ className, ...props }: React.ComponentProps<"h2">) => (
|
|
241
|
+
<div
|
|
242
|
+
className={cn("font-medium", className)}
|
|
243
|
+
data-slot="popover-title"
|
|
244
|
+
{...props}
|
|
245
|
+
/>
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
const PopoverDescription = ({
|
|
249
|
+
className,
|
|
250
|
+
...props
|
|
251
|
+
}: React.ComponentProps<"p">) => (
|
|
252
|
+
<p
|
|
253
|
+
className={cn("text-muted-foreground", className)}
|
|
254
|
+
data-slot="popover-description"
|
|
255
|
+
{...props}
|
|
256
|
+
/>
|
|
70
257
|
);
|
|
71
258
|
|
|
72
|
-
export {
|
|
259
|
+
export {
|
|
260
|
+
Popover,
|
|
261
|
+
PopoverTrigger,
|
|
262
|
+
PopoverContent,
|
|
263
|
+
PopoverAnchor,
|
|
264
|
+
PopoverHeader,
|
|
265
|
+
PopoverTitle,
|
|
266
|
+
PopoverDescription,
|
|
267
|
+
};
|
|
@@ -319,11 +319,6 @@ export const Search = ({ basePath }: { basePath: string }) => {
|
|
|
319
319
|
[closeSearch]
|
|
320
320
|
);
|
|
321
321
|
|
|
322
|
-
const handleOpenAutoFocus = useCallback((event: Event) => {
|
|
323
|
-
event.preventDefault();
|
|
324
|
-
inputRef.current?.focus();
|
|
325
|
-
}, []);
|
|
326
|
-
|
|
327
322
|
const warmSearch = useCallback(async () => {
|
|
328
323
|
try {
|
|
329
324
|
await loadSearchItems();
|
|
@@ -442,15 +437,11 @@ export const Search = ({ basePath }: { basePath: string }) => {
|
|
|
442
437
|
>
|
|
443
438
|
<span className="hidden lg:inline-flex">Search documentation...</span>
|
|
444
439
|
<span className="inline-flex lg:hidden">Search...</span>
|
|
445
|
-
<span className="ml-auto hidden pr-3 text-[11px] text-muted-foreground sm:inline-flex">
|
|
446
|
-
Cmd K
|
|
447
|
-
</span>
|
|
448
440
|
</button>
|
|
449
441
|
<Dialog onOpenChange={handleOpenChange} open={open}>
|
|
450
442
|
<DialogContent
|
|
451
443
|
className="max-w-2xl gap-0 overflow-hidden p-0"
|
|
452
444
|
onKeyDown={handleDialogKeyDown}
|
|
453
|
-
onOpenAutoFocus={handleOpenAutoFocus}
|
|
454
445
|
showCloseButton={false}
|
|
455
446
|
>
|
|
456
447
|
<DialogTitle className="sr-only">Search documentation</DialogTitle>
|