add drawer component
This commit is contained in:
@@ -10,7 +10,7 @@
|
|||||||
"lint": "eslint ."
|
"lint": "eslint ."
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@base-ui/react": "^1.0.0",
|
"@base-ui/react": "^1.2.0",
|
||||||
"@fontsource-variable/inter": "^5.2.8",
|
"@fontsource-variable/inter": "^5.2.8",
|
||||||
"@fontsource/instrument-serif": "^5.2.8",
|
"@fontsource/instrument-serif": "^5.2.8",
|
||||||
"@tailwindcss/vite": "^4.1.8",
|
"@tailwindcss/vite": "^4.1.8",
|
||||||
|
|||||||
148
apps/web/src/components/ui/drawer.tsx
Normal file
148
apps/web/src/components/ui/drawer.tsx
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
import { DrawerPreview as BaseDrawer } from "@base-ui/react/drawer";
|
||||||
|
import { tv } from "tailwind-variants";
|
||||||
|
|
||||||
|
const drawerStyles = tv({
|
||||||
|
slots: {
|
||||||
|
viewport: "fixed inset-y-0 right-0 pointer-events-none",
|
||||||
|
popup: [
|
||||||
|
"pointer-events-auto",
|
||||||
|
"fixed top-0 right-0 h-dvh w-[400px]",
|
||||||
|
"bg-card text-card-foreground border-l border-border shadow-lg",
|
||||||
|
"flex flex-col",
|
||||||
|
"transition-transform duration-100 ease-out",
|
||||||
|
],
|
||||||
|
title: "text-lg font-semibold",
|
||||||
|
description: "text-sm text-muted-foreground",
|
||||||
|
close: [
|
||||||
|
"focus-visible:ring-ring focus-visible:outline-none",
|
||||||
|
"focus-visible:ring-2 focus-visible:ring-offset-2",
|
||||||
|
],
|
||||||
|
content: "flex-1 overflow-y-auto",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { viewport, popup, title, description, close, content } = drawerStyles();
|
||||||
|
|
||||||
|
type DrawerRootProps = React.ComponentProps<typeof BaseDrawer.Root>;
|
||||||
|
|
||||||
|
function DrawerRoot(props: DrawerRootProps) {
|
||||||
|
return <BaseDrawer.Root modal={false} swipeDirection="right" {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
type DrawerTriggerProps = React.ComponentProps<typeof BaseDrawer.Trigger>;
|
||||||
|
|
||||||
|
function DrawerTrigger({ className, ...props }: DrawerTriggerProps) {
|
||||||
|
return <BaseDrawer.Trigger className={className} {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
type DrawerPortalProps = React.ComponentProps<typeof BaseDrawer.Portal>;
|
||||||
|
|
||||||
|
function DrawerPortal(props: DrawerPortalProps) {
|
||||||
|
return <BaseDrawer.Portal {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
type DrawerViewportProps = React.ComponentProps<typeof BaseDrawer.Viewport>;
|
||||||
|
|
||||||
|
function DrawerViewport({ className, ...props }: DrawerViewportProps) {
|
||||||
|
return (
|
||||||
|
<BaseDrawer.Viewport
|
||||||
|
className={(state) =>
|
||||||
|
viewport({
|
||||||
|
class: typeof className === "function" ? className(state) : className,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
type DrawerPopupProps = React.ComponentProps<typeof BaseDrawer.Popup>;
|
||||||
|
|
||||||
|
function DrawerPopup({ className, ...props }: DrawerPopupProps) {
|
||||||
|
return (
|
||||||
|
<BaseDrawer.Popup
|
||||||
|
data-drawer-popup=""
|
||||||
|
className={(state) =>
|
||||||
|
popup({
|
||||||
|
class: typeof className === "function" ? className(state) : className,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
type DrawerTitleProps = React.ComponentProps<typeof BaseDrawer.Title>;
|
||||||
|
|
||||||
|
function DrawerTitle({ className, ...props }: DrawerTitleProps) {
|
||||||
|
return (
|
||||||
|
<BaseDrawer.Title
|
||||||
|
className={(state) =>
|
||||||
|
title({
|
||||||
|
class: typeof className === "function" ? className(state) : className,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
type DrawerDescriptionProps = React.ComponentProps<
|
||||||
|
typeof BaseDrawer.Description
|
||||||
|
>;
|
||||||
|
|
||||||
|
function DrawerDescription({ className, ...props }: DrawerDescriptionProps) {
|
||||||
|
return (
|
||||||
|
<BaseDrawer.Description
|
||||||
|
className={(state) =>
|
||||||
|
description({
|
||||||
|
class: typeof className === "function" ? className(state) : className,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
type DrawerCloseProps = React.ComponentProps<typeof BaseDrawer.Close>;
|
||||||
|
|
||||||
|
function DrawerClose({ className, ...props }: DrawerCloseProps) {
|
||||||
|
return (
|
||||||
|
<BaseDrawer.Close
|
||||||
|
className={(state) =>
|
||||||
|
close({
|
||||||
|
class: typeof className === "function" ? className(state) : className,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
type DrawerContentProps = React.ComponentProps<typeof BaseDrawer.Content>;
|
||||||
|
|
||||||
|
function DrawerContent({ className, ...props }: DrawerContentProps) {
|
||||||
|
return (
|
||||||
|
<BaseDrawer.Content
|
||||||
|
className={(state) =>
|
||||||
|
content({
|
||||||
|
class: typeof className === "function" ? className(state) : className,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
DrawerRoot,
|
||||||
|
DrawerTrigger,
|
||||||
|
DrawerPortal,
|
||||||
|
DrawerViewport,
|
||||||
|
DrawerPopup,
|
||||||
|
DrawerTitle,
|
||||||
|
DrawerDescription,
|
||||||
|
DrawerClose,
|
||||||
|
DrawerContent,
|
||||||
|
drawerStyles,
|
||||||
|
};
|
||||||
@@ -129,3 +129,8 @@
|
|||||||
@apply font-sans;
|
@apply font-sans;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[data-drawer-popup][data-starting-style],
|
||||||
|
[data-drawer-popup][data-ending-style] {
|
||||||
|
translate: 100% 0;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,8 +1,19 @@
|
|||||||
import { createFileRoute } from "@tanstack/react-router";
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
import { Heart, Search } from "lucide-react";
|
import { Heart, Search, X } from "lucide-react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
DrawerClose,
|
||||||
|
DrawerContent,
|
||||||
|
DrawerDescription,
|
||||||
|
DrawerPopup,
|
||||||
|
DrawerPortal,
|
||||||
|
DrawerRoot,
|
||||||
|
DrawerTitle,
|
||||||
|
DrawerTrigger,
|
||||||
|
DrawerViewport,
|
||||||
|
} from "@/components/ui/drawer";
|
||||||
import {
|
import {
|
||||||
FieldControl,
|
FieldControl,
|
||||||
FieldDescription,
|
FieldDescription,
|
||||||
@@ -166,6 +177,57 @@ function DemoPage() {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
|
{/* Drawer */}
|
||||||
|
<Section title="Drawer">
|
||||||
|
<DrawerRoot>
|
||||||
|
<DrawerTrigger>
|
||||||
|
<Button variant="outline">Open Drawer</Button>
|
||||||
|
</DrawerTrigger>
|
||||||
|
<DrawerPortal>
|
||||||
|
<DrawerViewport>
|
||||||
|
<DrawerPopup>
|
||||||
|
<div className="flex items-center justify-between border-b border-border p-6">
|
||||||
|
<div className="space-y-1">
|
||||||
|
<DrawerTitle>Edit Item</DrawerTitle>
|
||||||
|
<DrawerDescription>
|
||||||
|
Update the details for this timeline item.
|
||||||
|
</DrawerDescription>
|
||||||
|
</div>
|
||||||
|
<DrawerClose>
|
||||||
|
<Button variant="outline" size="icon" aria-label="Close">
|
||||||
|
<X className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</DrawerClose>
|
||||||
|
</div>
|
||||||
|
<DrawerContent>
|
||||||
|
<form
|
||||||
|
className="grid gap-4 p-6"
|
||||||
|
onSubmit={(e) => e.preventDefault()}
|
||||||
|
>
|
||||||
|
<FieldRoot>
|
||||||
|
<FieldLabel>Title</FieldLabel>
|
||||||
|
<FieldControl placeholder="Item title" />
|
||||||
|
</FieldRoot>
|
||||||
|
<FieldRoot>
|
||||||
|
<FieldLabel>Date</FieldLabel>
|
||||||
|
<FieldControl type="date" />
|
||||||
|
</FieldRoot>
|
||||||
|
<FieldRoot>
|
||||||
|
<FieldLabel>Description</FieldLabel>
|
||||||
|
<FieldControl placeholder="Describe this event" />
|
||||||
|
<FieldDescription>Optional</FieldDescription>
|
||||||
|
</FieldRoot>
|
||||||
|
<Button type="submit" className="w-fit">
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</DrawerContent>
|
||||||
|
</DrawerPopup>
|
||||||
|
</DrawerViewport>
|
||||||
|
</DrawerPortal>
|
||||||
|
</DrawerRoot>
|
||||||
|
</Section>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
19
pnpm-lock.yaml
generated
19
pnpm-lock.yaml
generated
@@ -58,8 +58,8 @@ importers:
|
|||||||
apps/web:
|
apps/web:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@base-ui/react':
|
'@base-ui/react':
|
||||||
specifier: ^1.0.0
|
specifier: ^1.2.0
|
||||||
version: 1.1.0(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
version: 1.2.0(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||||
'@fontsource-variable/inter':
|
'@fontsource-variable/inter':
|
||||||
specifier: ^5.2.8
|
specifier: ^5.2.8
|
||||||
version: 5.2.8
|
version: 5.2.8
|
||||||
@@ -468,8 +468,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==}
|
resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
||||||
'@base-ui/react@1.1.0':
|
'@base-ui/react@1.2.0':
|
||||||
resolution: {integrity: sha512-ikcJRNj1mOiF2HZ5jQHrXoVoHcNHdBU5ejJljcBl+VTLoYXR6FidjTN86GjO6hyshi6TZFuNvv0dEOgaOFv6Lw==}
|
resolution: {integrity: sha512-O6aEQHcm+QyGTFY28xuwRD3SEJGZOBDpyjN2WvpfWYFVhg+3zfXPysAILqtM0C1kWC82MccOE/v1j+GHXE4qIw==}
|
||||||
engines: {node: '>=14.0.0'}
|
engines: {node: '>=14.0.0'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@types/react': ^17 || ^18 || ^19
|
'@types/react': ^17 || ^18 || ^19
|
||||||
@@ -479,8 +479,8 @@ packages:
|
|||||||
'@types/react':
|
'@types/react':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@base-ui/utils@0.2.4':
|
'@base-ui/utils@0.2.5':
|
||||||
resolution: {integrity: sha512-smZwpMhjO29v+jrZusBSc5T+IJ3vBb9cjIiBjtKcvWmRj9Z4DWGVR3efr1eHR56/bqY5a4qyY9ElkOY5ljo3ng==}
|
resolution: {integrity: sha512-oYC7w0gp76RI5MxprlGLV0wze0SErZaRl3AAkeP3OnNB/UBMb6RqNf6ZSIlxOc9Qp68Ab3C2VOcJQyRs7Xc7Vw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@types/react': ^17 || ^18 || ^19
|
'@types/react': ^17 || ^18 || ^19
|
||||||
react: ^17 || ^18 || ^19
|
react: ^17 || ^18 || ^19
|
||||||
@@ -4821,21 +4821,20 @@ snapshots:
|
|||||||
'@babel/helper-string-parser': 7.27.1
|
'@babel/helper-string-parser': 7.27.1
|
||||||
'@babel/helper-validator-identifier': 7.28.5
|
'@babel/helper-validator-identifier': 7.28.5
|
||||||
|
|
||||||
'@base-ui/react@1.1.0(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
|
'@base-ui/react@1.2.0(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.28.6
|
'@babel/runtime': 7.28.6
|
||||||
'@base-ui/utils': 0.2.4(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
'@base-ui/utils': 0.2.5(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||||
'@floating-ui/react-dom': 2.1.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
'@floating-ui/react-dom': 2.1.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
|
||||||
'@floating-ui/utils': 0.2.10
|
'@floating-ui/utils': 0.2.10
|
||||||
react: 19.2.3
|
react: 19.2.3
|
||||||
react-dom: 19.2.3(react@19.2.3)
|
react-dom: 19.2.3(react@19.2.3)
|
||||||
reselect: 5.1.1
|
|
||||||
tabbable: 6.4.0
|
tabbable: 6.4.0
|
||||||
use-sync-external-store: 1.6.0(react@19.2.3)
|
use-sync-external-store: 1.6.0(react@19.2.3)
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/react': 19.2.7
|
'@types/react': 19.2.7
|
||||||
|
|
||||||
'@base-ui/utils@0.2.4(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
|
'@base-ui/utils@0.2.5(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.28.6
|
'@babel/runtime': 7.28.6
|
||||||
'@floating-ui/utils': 0.2.10
|
'@floating-ui/utils': 0.2.10
|
||||||
|
|||||||
Reference in New Issue
Block a user