@manhphi1309/drawer 0.2.3 → 0.2.5

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 CHANGED
@@ -16,6 +16,7 @@ This package exports the following subcomponents:
16
16
  - `DrawerFooter`
17
17
  - `DrawerTitle`
18
18
  - `DrawerDescription`
19
+ - `useDrawerPanel` (Hook)
19
20
 
20
21
  ## Dependencies
21
22
 
@@ -71,10 +72,66 @@ export default function App() {
71
72
  }
72
73
  ```
73
74
 
75
+ ## Multi-Step Wizard Example
76
+
77
+ The drawer has built-in support for a multi-step sliding wizard with lazy loading. You can control it manually from the parent, or let it manage its own state internally using the `useDrawerPanel` hook.
78
+
79
+ ```tsx
80
+ import { Button } from "@manhphi1309/button"
81
+ import {
82
+ Drawer,
83
+ DrawerContent,
84
+ DrawerHeader,
85
+ DrawerTitle,
86
+ DrawerTrigger,
87
+ useDrawerPanel,
88
+ } from "@manhphi1309/drawer"
89
+
90
+ function PanelOne() {
91
+ const { nextPanel } = useDrawerPanel()
92
+ return (
93
+ <div className="p-4 flex flex-col gap-4 h-full">
94
+ <DrawerHeader className="px-0">
95
+ <DrawerTitle>Step 1</DrawerTitle>
96
+ </DrawerHeader>
97
+ <div className="mt-auto">
98
+ <Button onClick={nextPanel}>Next Step</Button>
99
+ </div>
100
+ </div>
101
+ )
102
+ }
103
+
104
+ function PanelTwo() {
105
+ const { prevPanel } = useDrawerPanel()
106
+ return (
107
+ <div className="p-4 flex flex-col gap-4 h-full">
108
+ <DrawerHeader className="px-0">
109
+ <DrawerTitle>Step 2</DrawerTitle>
110
+ </DrawerHeader>
111
+ <div className="mt-auto">
112
+ <Button variant="outline" onClick={prevPanel}>Go Back</Button>
113
+ </div>
114
+ </div>
115
+ )
116
+ }
117
+
118
+ export default function WizardApp() {
119
+ return (
120
+ <Drawer>
121
+ <DrawerTrigger asChild>
122
+ <Button>Open Wizard</Button>
123
+ </DrawerTrigger>
124
+ <DrawerContent panels={[<PanelOne key="1" />, <PanelTwo key="2" />]} />
125
+ </Drawer>
126
+ )
127
+ }
128
+ ```
129
+
74
130
  ## Props
75
131
 
76
132
  The `Drawer` component natively wraps and accepts all props provided by the `vaul` library.
77
133
 
134
+ ### Drawer
78
135
  | Prop | Type | Description |
79
136
  | :--- | :--- | :--- |
80
137
  | `direction` | `"top" \| "bottom" \| "left" \| "right"` | Direction the drawer slides in from. |
@@ -82,4 +139,13 @@ The `Drawer` component natively wraps and accepts all props provided by the `vau
82
139
  | `onOpenChange` | `(open: boolean) => void` | Callback when the open state changes. |
83
140
  | `snapPoints` | `number[] \| string[]` | Array of numbers from 0 to 1 or string values representing heights to snap to. |
84
141
 
142
+ ### DrawerContent (Wizard Mode)
143
+ If you pass the `panels` prop, `DrawerContent` transforms into a multi-step sliding wizard.
144
+
145
+ | Prop | Type | Description |
146
+ | :--- | :--- | :--- |
147
+ | `panels` | `React.ReactNode[]` | An array of panel components to render in the sliding track. |
148
+ | `activePanelIndex` | `number` | (Controlled mode) The index of the currently active panel. |
149
+ | `onPanelIndexChange` | `(index: number) => void` | (Controlled mode) Callback fired when a navigation hook triggers a slide. |
150
+
85
151
  For a complete and advanced list of properties (like `fadeFromIndex`, `activeSnapPoint`, etc.), please refer to the official [Vaul documentation](https://github.com/emilkowalski/vaul).
package/dist/index.cjs CHANGED
@@ -29,6 +29,13 @@ let _manhphi1309_hooks = require("@manhphi1309/hooks");
29
29
  let _manhphi1309_utils = require("@manhphi1309/utils");
30
30
  let react_jsx_runtime = require("react/jsx-runtime");
31
31
  //#region index.tsx
32
+ const DrawerPanelContext = react.createContext(null);
33
+ const DrawerContext = react.createContext(null);
34
+ function useDrawerPanel() {
35
+ const context = react.useContext(DrawerPanelContext);
36
+ if (!context) throw new Error("useDrawerPanel must be used within a DrawerContent that has panels");
37
+ return context;
38
+ }
32
39
  function Drawer({ ...props }) {
33
40
  const [internalOpen, setInternalOpen] = react.useState(props.defaultOpen ?? false);
34
41
  const isControlled = props.open !== void 0;
@@ -39,11 +46,17 @@ function Drawer({ ...props }) {
39
46
  onOpenChange?.(newOpen);
40
47
  }, [isControlled, onOpenChange]);
41
48
  (0, _manhphi1309_hooks.useThemeColorAnimation)(open ?? false);
42
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(vaul.Drawer.Root, {
43
- "data-slot": "drawer",
44
- open,
45
- onOpenChange: handleOpenChange,
46
- ...props
49
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DrawerContext.Provider, {
50
+ value: {
51
+ open: open ?? false,
52
+ onOpenChange: handleOpenChange
53
+ },
54
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(vaul.Drawer.Root, {
55
+ "data-slot": "drawer",
56
+ open,
57
+ onOpenChange: handleOpenChange,
58
+ ...props
59
+ })
47
60
  });
48
61
  }
49
62
  function DrawerTrigger({ ...props }) {
@@ -71,21 +84,98 @@ function DrawerOverlay({ className, ...props }) {
71
84
  ...props
72
85
  });
73
86
  }
74
- function DrawerContent({ className, children, ...props }) {
75
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(DrawerPortal, {
76
- "data-slot": "drawer-portal",
77
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(DrawerOverlay, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(vaul.Drawer.Content, {
78
- "data-slot": "drawer-content",
79
- className: (0, _manhphi1309_utils.cn)("group/drawer-content fixed z-50 flex h-auto flex-col bg-popover text-sm text-popover-foreground data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[96dvh] data-[vaul-drawer-direction=bottom]:rounded-t-xl data-[vaul-drawer-direction=bottom]:border-t data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:rounded-r-xl data-[vaul-drawer-direction=left]:border-r data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:rounded-l-xl data-[vaul-drawer-direction=right]:border-l data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[96dvh] data-[vaul-drawer-direction=top]:rounded-b-xl data-[vaul-drawer-direction=top]:border-b data-[vaul-drawer-direction=left]:sm:max-w-sm data-[vaul-drawer-direction=right]:sm:max-w-sm", className),
80
- ...props,
81
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "mx-auto mt-4 hidden h-1 w-[100px] shrink-0 rounded-full bg-muted group-data-[vaul-drawer-direction=bottom]/drawer-content:block" }), children]
82
- })]
87
+ function DrawerContent({ className, children, panels, activePanelIndex = 0, onPanelIndexChange, ...props }) {
88
+ const [internalIndex, setInternalIndex] = react.useState(0);
89
+ const drawerContext = react.useContext(DrawerContext);
90
+ const isControlled = activePanelIndex !== void 0 && onPanelIndexChange !== void 0;
91
+ const controlledIndex = isControlled ? activePanelIndex : internalIndex;
92
+ const [visitedPanels, setVisitedPanels] = react.useState(new Set([
93
+ controlledIndex,
94
+ controlledIndex + 1,
95
+ controlledIndex - 1
96
+ ]));
97
+ react.useEffect(() => {
98
+ if (drawerContext && !drawerContext.open) {
99
+ const timer = setTimeout(() => {
100
+ setInternalIndex(0);
101
+ setVisitedPanels(new Set([
102
+ 0,
103
+ 1,
104
+ -1
105
+ ]));
106
+ }, 500);
107
+ return () => clearTimeout(timer);
108
+ }
109
+ }, [drawerContext]);
110
+ if (!visitedPanels.has(controlledIndex)) setVisitedPanels((prev) => {
111
+ const newSet = new Set(prev);
112
+ newSet.add(controlledIndex);
113
+ newSet.add(controlledIndex + 1);
114
+ newSet.add(controlledIndex - 1);
115
+ return newSet;
116
+ });
117
+ const nextPanel = react.useCallback(() => {
118
+ if (!panels) return;
119
+ const newIndex = Math.min(controlledIndex + 1, panels.length - 1);
120
+ if (!isControlled) setInternalIndex(newIndex);
121
+ onPanelIndexChange?.(newIndex);
122
+ }, [
123
+ controlledIndex,
124
+ isControlled,
125
+ onPanelIndexChange,
126
+ panels
127
+ ]);
128
+ const prevPanel = react.useCallback(() => {
129
+ const newIndex = Math.max(controlledIndex - 1, 0);
130
+ if (!isControlled) setInternalIndex(newIndex);
131
+ onPanelIndexChange?.(newIndex);
132
+ }, [
133
+ controlledIndex,
134
+ isControlled,
135
+ onPanelIndexChange
136
+ ]);
137
+ const value = react.useMemo(() => ({
138
+ activePanelIndex: controlledIndex,
139
+ setActivePanelIndex: (index) => {
140
+ if (!isControlled) setInternalIndex(index);
141
+ onPanelIndexChange?.(index);
142
+ },
143
+ nextPanel,
144
+ prevPanel
145
+ }), [
146
+ controlledIndex,
147
+ isControlled,
148
+ nextPanel,
149
+ prevPanel,
150
+ onPanelIndexChange
151
+ ]);
152
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DrawerPanelContext.Provider, {
153
+ value,
154
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(DrawerPortal, {
155
+ "data-slot": "drawer-portal",
156
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(DrawerOverlay, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(vaul.Drawer.Content, {
157
+ "data-slot": "drawer-content",
158
+ className: (0, _manhphi1309_utils.cn)("group/drawer-content fixed z-50 flex h-auto flex-col bg-popover text-sm text-popover-foreground data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[96dvh] data-[vaul-drawer-direction=bottom]:rounded-t-xl data-[vaul-drawer-direction=bottom]:border-t data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:rounded-r-xl data-[vaul-drawer-direction=left]:border-r data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:rounded-l-xl data-[vaul-drawer-direction=right]:border-l data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[96dvh] data-[vaul-drawer-direction=top]:rounded-b-xl data-[vaul-drawer-direction=top]:border-b data-[vaul-drawer-direction=left]:sm:max-w-sm data-[vaul-drawer-direction=right]:sm:max-w-sm", className),
159
+ ...props,
160
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "mx-auto mt-4 hidden h-1 w-[40px] shrink-0 rounded-full bg-muted group-data-[vaul-drawer-direction=bottom]/drawer-content:block" }), !panels ? children : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
161
+ className: "overflow-hidden",
162
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
163
+ style: { transform: `translateX(${controlledIndex * -100}%)` },
164
+ className: "flex h-full w-full flex-row transition-transform duration-300 ease-in-out",
165
+ children: panels.map((panel, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
166
+ className: "h-full w-full shrink-0",
167
+ children: visitedPanels.has(index) ? panel : null
168
+ }, index))
169
+ })
170
+ })]
171
+ })]
172
+ })
83
173
  });
84
174
  }
85
175
  function DrawerHeader({ className, leftAction, rightAction, children, ...props }) {
86
176
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
87
177
  "data-slot": "drawer-header",
88
- className: (0, _manhphi1309_utils.cn)("flex items-center p-4 group-data-[vaul-drawer-direction=bottom]/drawer-content:text-center group-data-[vaul-drawer-direction=top]/drawer-content:text-center md:text-left", className),
178
+ className: (0, _manhphi1309_utils.cn)("flex items-center px-4 py-2 group-data-[vaul-drawer-direction=bottom]/drawer-content:text-center group-data-[vaul-drawer-direction=top]/drawer-content:text-center md:text-left", className),
89
179
  ...props,
90
180
  children: leftAction || rightAction ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
91
181
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -138,3 +228,4 @@ exports.DrawerOverlay = DrawerOverlay;
138
228
  exports.DrawerPortal = DrawerPortal;
139
229
  exports.DrawerTitle = DrawerTitle;
140
230
  exports.DrawerTrigger = DrawerTrigger;
231
+ exports.useDrawerPanel = useDrawerPanel;
package/dist/index.d.cts CHANGED
@@ -2,6 +2,13 @@ import * as React from "react";
2
2
  import { Drawer as Drawer$1 } from "vaul";
3
3
 
4
4
  //#region index.d.ts
5
+ type DrawerPanelContextValue = {
6
+ activePanelIndex: number;
7
+ setActivePanelIndex: (index: number) => void;
8
+ nextPanel: () => void;
9
+ prevPanel: () => void;
10
+ };
11
+ declare function useDrawerPanel(): DrawerPanelContextValue;
5
12
  declare function Drawer({
6
13
  ...props
7
14
  }: React.ComponentProps<typeof Drawer$1.Root>): React.JSX.Element;
@@ -18,11 +25,19 @@ declare function DrawerOverlay({
18
25
  className,
19
26
  ...props
20
27
  }: React.ComponentProps<typeof Drawer$1.Overlay>): React.JSX.Element;
28
+ interface DrawerContentProps extends React.ComponentProps<typeof Drawer$1.Content> {
29
+ panels?: React.ReactNode[];
30
+ activePanelIndex?: number;
31
+ onPanelIndexChange?: (index: number) => void;
32
+ }
21
33
  declare function DrawerContent({
22
34
  className,
23
35
  children,
36
+ panels,
37
+ activePanelIndex,
38
+ onPanelIndexChange,
24
39
  ...props
25
- }: React.ComponentProps<typeof Drawer$1.Content>): React.JSX.Element;
40
+ }: DrawerContentProps): React.JSX.Element;
26
41
  declare function DrawerHeader({
27
42
  className,
28
43
  leftAction,
@@ -46,4 +61,4 @@ declare function DrawerDescription({
46
61
  ...props
47
62
  }: React.ComponentProps<typeof Drawer$1.Description>): React.JSX.Element;
48
63
  //#endregion
49
- export { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger };
64
+ export { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, useDrawerPanel };
package/dist/index.d.mts CHANGED
@@ -2,6 +2,13 @@ import * as React from "react";
2
2
  import { Drawer as Drawer$1 } from "vaul";
3
3
 
4
4
  //#region index.d.ts
5
+ type DrawerPanelContextValue = {
6
+ activePanelIndex: number;
7
+ setActivePanelIndex: (index: number) => void;
8
+ nextPanel: () => void;
9
+ prevPanel: () => void;
10
+ };
11
+ declare function useDrawerPanel(): DrawerPanelContextValue;
5
12
  declare function Drawer({
6
13
  ...props
7
14
  }: React.ComponentProps<typeof Drawer$1.Root>): React.JSX.Element;
@@ -18,11 +25,19 @@ declare function DrawerOverlay({
18
25
  className,
19
26
  ...props
20
27
  }: React.ComponentProps<typeof Drawer$1.Overlay>): React.JSX.Element;
28
+ interface DrawerContentProps extends React.ComponentProps<typeof Drawer$1.Content> {
29
+ panels?: React.ReactNode[];
30
+ activePanelIndex?: number;
31
+ onPanelIndexChange?: (index: number) => void;
32
+ }
21
33
  declare function DrawerContent({
22
34
  className,
23
35
  children,
36
+ panels,
37
+ activePanelIndex,
38
+ onPanelIndexChange,
24
39
  ...props
25
- }: React.ComponentProps<typeof Drawer$1.Content>): React.JSX.Element;
40
+ }: DrawerContentProps): React.JSX.Element;
26
41
  declare function DrawerHeader({
27
42
  className,
28
43
  leftAction,
@@ -46,4 +61,4 @@ declare function DrawerDescription({
46
61
  ...props
47
62
  }: React.ComponentProps<typeof Drawer$1.Description>): React.JSX.Element;
48
63
  //#endregion
49
- export { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger };
64
+ export { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, useDrawerPanel };
package/dist/index.mjs CHANGED
@@ -5,6 +5,13 @@ import { useThemeColorAnimation } from "@manhphi1309/hooks";
5
5
  import { cn } from "@manhphi1309/utils";
6
6
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
7
  //#region index.tsx
8
+ const DrawerPanelContext = React.createContext(null);
9
+ const DrawerContext = React.createContext(null);
10
+ function useDrawerPanel() {
11
+ const context = React.useContext(DrawerPanelContext);
12
+ if (!context) throw new Error("useDrawerPanel must be used within a DrawerContent that has panels");
13
+ return context;
14
+ }
8
15
  function Drawer({ ...props }) {
9
16
  const [internalOpen, setInternalOpen] = React.useState(props.defaultOpen ?? false);
10
17
  const isControlled = props.open !== void 0;
@@ -15,11 +22,17 @@ function Drawer({ ...props }) {
15
22
  onOpenChange?.(newOpen);
16
23
  }, [isControlled, onOpenChange]);
17
24
  useThemeColorAnimation(open ?? false);
18
- return /* @__PURE__ */ jsx(Drawer$1.Root, {
19
- "data-slot": "drawer",
20
- open,
21
- onOpenChange: handleOpenChange,
22
- ...props
25
+ return /* @__PURE__ */ jsx(DrawerContext.Provider, {
26
+ value: {
27
+ open: open ?? false,
28
+ onOpenChange: handleOpenChange
29
+ },
30
+ children: /* @__PURE__ */ jsx(Drawer$1.Root, {
31
+ "data-slot": "drawer",
32
+ open,
33
+ onOpenChange: handleOpenChange,
34
+ ...props
35
+ })
23
36
  });
24
37
  }
25
38
  function DrawerTrigger({ ...props }) {
@@ -47,21 +60,98 @@ function DrawerOverlay({ className, ...props }) {
47
60
  ...props
48
61
  });
49
62
  }
50
- function DrawerContent({ className, children, ...props }) {
51
- return /* @__PURE__ */ jsxs(DrawerPortal, {
52
- "data-slot": "drawer-portal",
53
- children: [/* @__PURE__ */ jsx(DrawerOverlay, {}), /* @__PURE__ */ jsxs(Drawer$1.Content, {
54
- "data-slot": "drawer-content",
55
- className: cn("group/drawer-content fixed z-50 flex h-auto flex-col bg-popover text-sm text-popover-foreground data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[96dvh] data-[vaul-drawer-direction=bottom]:rounded-t-xl data-[vaul-drawer-direction=bottom]:border-t data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:rounded-r-xl data-[vaul-drawer-direction=left]:border-r data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:rounded-l-xl data-[vaul-drawer-direction=right]:border-l data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[96dvh] data-[vaul-drawer-direction=top]:rounded-b-xl data-[vaul-drawer-direction=top]:border-b data-[vaul-drawer-direction=left]:sm:max-w-sm data-[vaul-drawer-direction=right]:sm:max-w-sm", className),
56
- ...props,
57
- children: [/* @__PURE__ */ jsx("div", { className: "mx-auto mt-4 hidden h-1 w-[100px] shrink-0 rounded-full bg-muted group-data-[vaul-drawer-direction=bottom]/drawer-content:block" }), children]
58
- })]
63
+ function DrawerContent({ className, children, panels, activePanelIndex = 0, onPanelIndexChange, ...props }) {
64
+ const [internalIndex, setInternalIndex] = React.useState(0);
65
+ const drawerContext = React.useContext(DrawerContext);
66
+ const isControlled = activePanelIndex !== void 0 && onPanelIndexChange !== void 0;
67
+ const controlledIndex = isControlled ? activePanelIndex : internalIndex;
68
+ const [visitedPanels, setVisitedPanels] = React.useState(new Set([
69
+ controlledIndex,
70
+ controlledIndex + 1,
71
+ controlledIndex - 1
72
+ ]));
73
+ React.useEffect(() => {
74
+ if (drawerContext && !drawerContext.open) {
75
+ const timer = setTimeout(() => {
76
+ setInternalIndex(0);
77
+ setVisitedPanels(new Set([
78
+ 0,
79
+ 1,
80
+ -1
81
+ ]));
82
+ }, 500);
83
+ return () => clearTimeout(timer);
84
+ }
85
+ }, [drawerContext]);
86
+ if (!visitedPanels.has(controlledIndex)) setVisitedPanels((prev) => {
87
+ const newSet = new Set(prev);
88
+ newSet.add(controlledIndex);
89
+ newSet.add(controlledIndex + 1);
90
+ newSet.add(controlledIndex - 1);
91
+ return newSet;
92
+ });
93
+ const nextPanel = React.useCallback(() => {
94
+ if (!panels) return;
95
+ const newIndex = Math.min(controlledIndex + 1, panels.length - 1);
96
+ if (!isControlled) setInternalIndex(newIndex);
97
+ onPanelIndexChange?.(newIndex);
98
+ }, [
99
+ controlledIndex,
100
+ isControlled,
101
+ onPanelIndexChange,
102
+ panels
103
+ ]);
104
+ const prevPanel = React.useCallback(() => {
105
+ const newIndex = Math.max(controlledIndex - 1, 0);
106
+ if (!isControlled) setInternalIndex(newIndex);
107
+ onPanelIndexChange?.(newIndex);
108
+ }, [
109
+ controlledIndex,
110
+ isControlled,
111
+ onPanelIndexChange
112
+ ]);
113
+ const value = React.useMemo(() => ({
114
+ activePanelIndex: controlledIndex,
115
+ setActivePanelIndex: (index) => {
116
+ if (!isControlled) setInternalIndex(index);
117
+ onPanelIndexChange?.(index);
118
+ },
119
+ nextPanel,
120
+ prevPanel
121
+ }), [
122
+ controlledIndex,
123
+ isControlled,
124
+ nextPanel,
125
+ prevPanel,
126
+ onPanelIndexChange
127
+ ]);
128
+ return /* @__PURE__ */ jsx(DrawerPanelContext.Provider, {
129
+ value,
130
+ children: /* @__PURE__ */ jsxs(DrawerPortal, {
131
+ "data-slot": "drawer-portal",
132
+ children: [/* @__PURE__ */ jsx(DrawerOverlay, {}), /* @__PURE__ */ jsxs(Drawer$1.Content, {
133
+ "data-slot": "drawer-content",
134
+ className: cn("group/drawer-content fixed z-50 flex h-auto flex-col bg-popover text-sm text-popover-foreground data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[96dvh] data-[vaul-drawer-direction=bottom]:rounded-t-xl data-[vaul-drawer-direction=bottom]:border-t data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:rounded-r-xl data-[vaul-drawer-direction=left]:border-r data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:rounded-l-xl data-[vaul-drawer-direction=right]:border-l data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[96dvh] data-[vaul-drawer-direction=top]:rounded-b-xl data-[vaul-drawer-direction=top]:border-b data-[vaul-drawer-direction=left]:sm:max-w-sm data-[vaul-drawer-direction=right]:sm:max-w-sm", className),
135
+ ...props,
136
+ children: [/* @__PURE__ */ jsx("div", { className: "mx-auto mt-4 hidden h-1 w-[40px] shrink-0 rounded-full bg-muted group-data-[vaul-drawer-direction=bottom]/drawer-content:block" }), !panels ? children : /* @__PURE__ */ jsx("div", {
137
+ className: "overflow-hidden",
138
+ children: /* @__PURE__ */ jsx("div", {
139
+ style: { transform: `translateX(${controlledIndex * -100}%)` },
140
+ className: "flex h-full w-full flex-row transition-transform duration-300 ease-in-out",
141
+ children: panels.map((panel, index) => /* @__PURE__ */ jsx("div", {
142
+ className: "h-full w-full shrink-0",
143
+ children: visitedPanels.has(index) ? panel : null
144
+ }, index))
145
+ })
146
+ })]
147
+ })]
148
+ })
59
149
  });
60
150
  }
61
151
  function DrawerHeader({ className, leftAction, rightAction, children, ...props }) {
62
152
  return /* @__PURE__ */ jsx("div", {
63
153
  "data-slot": "drawer-header",
64
- className: cn("flex items-center p-4 group-data-[vaul-drawer-direction=bottom]/drawer-content:text-center group-data-[vaul-drawer-direction=top]/drawer-content:text-center md:text-left", className),
154
+ className: cn("flex items-center px-4 py-2 group-data-[vaul-drawer-direction=bottom]/drawer-content:text-center group-data-[vaul-drawer-direction=top]/drawer-content:text-center md:text-left", className),
65
155
  ...props,
66
156
  children: leftAction || rightAction ? /* @__PURE__ */ jsxs(Fragment, { children: [
67
157
  /* @__PURE__ */ jsx("div", {
@@ -104,4 +194,4 @@ function DrawerDescription({ className, ...props }) {
104
194
  });
105
195
  }
106
196
  //#endregion
107
- export { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger };
197
+ export { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, useDrawerPanel };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@manhphi1309/drawer",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "sideEffects": false,
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",