@reactberry/system 2.0.0-beta
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 +48 -0
- package/package.json +74 -0
- package/src/blocks/Accordion/index.tsx +158 -0
- package/src/blocks/AnimatedCarousel/index.tsx +188 -0
- package/src/blocks/AppleGlow/index.tsx +144 -0
- package/src/blocks/Avatar/index.tsx +167 -0
- package/src/blocks/Await/index.tsx +45 -0
- package/src/blocks/Cards/AnimatedCard/index.tsx +175 -0
- package/src/blocks/Cards/FluorescentCard/index.tsx +180 -0
- package/src/blocks/Cards/InfoCard/index.tsx +206 -0
- package/src/blocks/Cards/TickerCard/index.tsx +125 -0
- package/src/blocks/Carousel/index.tsx +216 -0
- package/src/blocks/Checkbox/index.tsx +101 -0
- package/src/blocks/Collection/index.tsx +59 -0
- package/src/blocks/Container/index.tsx +55 -0
- package/src/blocks/Controls/Control.tsx +67 -0
- package/src/blocks/Controls/index.tsx +11 -0
- package/src/blocks/CyclingNumber/index.tsx +78 -0
- package/src/blocks/DisplaySet/index.tsx +42 -0
- package/src/blocks/Divider/index.tsx +14 -0
- package/src/blocks/Draggable/index.tsx +266 -0
- package/src/blocks/Drawer/index.tsx +136 -0
- package/src/blocks/DynamicIsland/DynamicIsland.tsx +89 -0
- package/src/blocks/DynamicIsland/index.tsx +2 -0
- package/src/blocks/Fader/index.tsx +145 -0
- package/src/blocks/FamilyDrawer/README.md +116 -0
- package/src/blocks/FamilyDrawer/example.tsx +108 -0
- package/src/blocks/FamilyDrawer/index.tsx +119 -0
- package/src/blocks/FamilyDrawer/views/DefaultView.tsx +93 -0
- package/src/blocks/FamilyDrawer/views/KeyView.tsx +129 -0
- package/src/blocks/FamilyDrawer/views/PhraseView.tsx +129 -0
- package/src/blocks/FamilyDrawer/views/RemoveView.tsx +81 -0
- package/src/blocks/FieldSet/index.tsx +173 -0
- package/src/blocks/Filesystem/index.tsx +198 -0
- package/src/blocks/Gallery/Carousel/index.tsx +257 -0
- package/src/blocks/Gallery/Modal/index.tsx +83 -0
- package/src/blocks/Gallery/index.tsx +57 -0
- package/src/blocks/Gallery/utils/animationVariants.ts +18 -0
- package/src/blocks/Gallery/utils/aspectRatio.ts +14 -0
- package/src/blocks/Gallery/utils/downloadPhoto.ts +24 -0
- package/src/blocks/Gallery/utils/range.ts +11 -0
- package/src/blocks/GradientMesh/index.tsx +106 -0
- package/src/blocks/Group/index.tsx +152 -0
- package/src/blocks/Heading/index.tsx +111 -0
- package/src/blocks/HorizontalScroller/index.tsx +135 -0
- package/src/blocks/Icon/index.tsx +45 -0
- package/src/blocks/Indicator/index.tsx +27 -0
- package/src/blocks/InlineEditor/index.tsx +216 -0
- package/src/blocks/List/index.tsx +657 -0
- package/src/blocks/Main/index.tsx +17 -0
- package/src/blocks/Marquee/index.tsx +116 -0
- package/src/blocks/MaskedField/index.tsx +199 -0
- package/src/blocks/Menu/MenuContent.tsx +246 -0
- package/src/blocks/Menu/MenuContext.tsx +34 -0
- package/src/blocks/Menu/MenuItem.tsx +104 -0
- package/src/blocks/Menu/index.tsx +60 -0
- package/src/blocks/Modal/index.tsx +268 -0
- package/src/blocks/MorphingPopover/index.tsx +294 -0
- package/src/blocks/Overlay/Backdrop.tsx +48 -0
- package/src/blocks/Overlay/OverscrollGuard.tsx +36 -0
- package/src/blocks/Overlay/index.ts +2 -0
- package/src/blocks/Parallax/index.tsx +117 -0
- package/src/blocks/ParallaxSection/index.tsx +61 -0
- package/src/blocks/Placeholder/index.tsx +48 -0
- package/src/blocks/Popover/index.tsx +402 -0
- package/src/blocks/Progress/getProgressColor.ts +61 -0
- package/src/blocks/Progress/index.tsx +179 -0
- package/src/blocks/ProgressiveBlur/index.tsx +75 -0
- package/src/blocks/README.md +15 -0
- package/src/blocks/RenderAsset/index.tsx +18 -0
- package/src/blocks/ScrollContainer/index.tsx +93 -0
- package/src/blocks/ShinyText/index.tsx +72 -0
- package/src/blocks/Skeleton/index.tsx +71 -0
- package/src/blocks/Slider/SliderControls.tsx +119 -0
- package/src/blocks/Slider/index.tsx +140 -0
- package/src/blocks/Slider/useSlider.ts +126 -0
- package/src/blocks/Slideshow/index.tsx +177 -0
- package/src/blocks/Spotlight/index.tsx +144 -0
- package/src/blocks/Steps/StepIndicator.tsx +149 -0
- package/src/blocks/Steps/StepProgress.tsx +164 -0
- package/src/blocks/Steps/Steps.tsx +197 -0
- package/src/blocks/Steps/StepsNav.tsx +30 -0
- package/src/blocks/Steps/StepsTracker.tsx +80 -0
- package/src/blocks/Steps/hooks.ts +71 -0
- package/src/blocks/Steps/index.tsx +16 -0
- package/src/blocks/Steps/types.ts +71 -0
- package/src/blocks/StickySectionStack/index.tsx +136 -0
- package/src/blocks/Switch/index.tsx +85 -0
- package/src/blocks/SystemNotice/index.tsx +81 -0
- package/src/blocks/Table/README.md +251 -0
- package/src/blocks/Table/Table.tsx +207 -0
- package/src/blocks/Table/TablePagination.tsx +189 -0
- package/src/blocks/Table/index.ts +33 -0
- package/src/blocks/Table/useTableControls.ts +331 -0
- package/src/blocks/Tag/index.tsx +27 -0
- package/src/blocks/TextBreak/index.tsx +96 -0
- package/src/blocks/TextReveal/index.tsx +104 -0
- package/src/blocks/Thumbnail/index.tsx +26 -0
- package/src/blocks/Ticker/index.tsx +112 -0
- package/src/blocks/Toast/index.tsx +77 -0
- package/src/blocks/Tooltip/index.tsx +174 -0
- package/src/blocks/Underlay/index.tsx +104 -0
- package/src/blocks/Upload/Dropzone.tsx +92 -0
- package/src/blocks/Upload/UploadBtn.tsx +38 -0
- package/src/blocks/Upload/index.tsx +61 -0
- package/src/blocks/Upload/types.ts +37 -0
- package/src/blocks/VideoMarquee/index.tsx +511 -0
- package/src/blocks/index.ts +119 -0
- package/src/blocks/pagination/Pagination.tsx +148 -0
- package/src/blocks/pagination/PaginationList.tsx +41 -0
- package/src/blocks/pagination/index.ts +2 -0
- package/src/charts/BarChart.tsx +63 -0
- package/src/charts/PieChart.tsx +39 -0
- package/src/charts/index.ts +3 -0
- package/src/charts/utils.ts +103 -0
- package/src/docs/README.md +373 -0
- package/src/docs/reference/README.md +299 -0
- package/src/elements/box.ts +163 -0
- package/src/elements/button.ts +49 -0
- package/src/elements/field.ts +129 -0
- package/src/elements/index.ts +8 -0
- package/src/elements/text.ts +47 -0
- package/src/elements/utils.js +97 -0
- package/src/hooks/use-copy-to-clipboard.tsx +33 -0
- package/src/hooks/use-enter-submit.tsx +23 -0
- package/src/hooks/use-local-storage.ts +42 -0
- package/src/hooks/use-sidebar.tsx +109 -0
- package/src/hooks/useAnimatedText.ts +32 -0
- package/src/hooks/useAutosizeTextArea.ts +45 -0
- package/src/hooks/useBreakpoint.tsx +123 -0
- package/src/hooks/useClickOutside.tsx +38 -0
- package/src/hooks/useHover.tsx +33 -0
- package/src/hooks/useHoverList.tsx +17 -0
- package/src/hooks/useKeyboardShortcuts.ts +91 -0
- package/src/hooks/useKeypress.ts +27 -0
- package/src/hooks/useOverlay.ts +32 -0
- package/src/hooks/useReducedMotion.ts +25 -0
- package/src/hooks/useStandaloneMode.ts +35 -0
- package/src/hooks/useTouchDevice.ts +34 -0
- package/src/icons/index.tsx +129 -0
- package/src/index.ts +12 -0
- package/src/providers/DesignSystemProvider.tsx +35 -0
- package/src/providers/StyledComponentsRegistry.tsx +30 -0
- package/src/providers/index.ts +2 -0
- package/src/themes/README.md +30 -0
- package/src/themes/default/assets/badge-avatar.tsx +45 -0
- package/src/themes/default/assets/logo.tsx +42 -0
- package/src/themes/default/global.ts +138 -0
- package/src/themes/default/modes/dark/config.js +49 -0
- package/src/themes/default/modes/dark/skins.js +631 -0
- package/src/themes/default/modes/dark/theme.js +87 -0
- package/src/themes/default/modes/light/config.js +48 -0
- package/src/themes/default/modes/light/skins.js +1026 -0
- package/src/themes/default/modes/light/theme.js +74 -0
- package/src/themes/default/tokens/controls.js +53 -0
- package/src/themes/default/tokens/shadows.js +63 -0
- package/src/themes/default/tokens/shapes.js +37 -0
- package/src/themes/default/tokens/space.js +143 -0
- package/src/themes/default/tokens/spectre.js +16 -0
- package/src/themes/default/utils.js +523 -0
- package/src/themes/index.ts +11 -0
- package/src/types.ts +394 -0
- package/src/utils/overlayTheme.ts +61 -0
- package/src/utils/pickColor.ts +15 -0
- package/tsconfig.json +24 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Box } from "@/design-system/elements";
|
|
4
|
+
import { motion } from "motion/react";
|
|
5
|
+
import { type ReactNode, useState } from "react";
|
|
6
|
+
|
|
7
|
+
const BOUNCE_VARIANTS = {
|
|
8
|
+
idle: 0.5,
|
|
9
|
+
} as const;
|
|
10
|
+
// 'ease-in': [0.55, 0.055, 0.675, 0.19], // -- ease-in-cubic
|
|
11
|
+
// 'ease-out': [0.23, 1, 0.32, 1], // -- ease-out-quint
|
|
12
|
+
// 'ease-in-out': [0.785, 0.135, 0.15, 0.86], // -- ease-in-out-circ
|
|
13
|
+
const DEFAULT_BOUNCE = 0.5;
|
|
14
|
+
|
|
15
|
+
export type DynamicIslandView = string;
|
|
16
|
+
|
|
17
|
+
export interface DynamicIslandProps {
|
|
18
|
+
view?: DynamicIslandView;
|
|
19
|
+
onViewChange?: (view: DynamicIslandView) => void;
|
|
20
|
+
content?: ReactNode;
|
|
21
|
+
controlIcons?: Record<string, ReactNode>;
|
|
22
|
+
containerProps?: any;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default function DynamicIsland({
|
|
26
|
+
view: controlledView,
|
|
27
|
+
content,
|
|
28
|
+
containerProps,
|
|
29
|
+
}: DynamicIslandProps) {
|
|
30
|
+
const [internalView] = useState<DynamicIslandView>("default");
|
|
31
|
+
const [variantKey] = useState<string>("default");
|
|
32
|
+
|
|
33
|
+
const view = controlledView ?? internalView;
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<Box
|
|
37
|
+
as={motion.div}
|
|
38
|
+
layout
|
|
39
|
+
skin="translucent.dark"
|
|
40
|
+
shape="pill"
|
|
41
|
+
minWidth={"6rem"}
|
|
42
|
+
m="0 auto"
|
|
43
|
+
overflow={"hidden"}
|
|
44
|
+
flex="none"
|
|
45
|
+
height={"fit-content"}
|
|
46
|
+
width={"fit-content"}
|
|
47
|
+
display="flex"
|
|
48
|
+
alignItems="center"
|
|
49
|
+
justifyContent={"center"}
|
|
50
|
+
gap="xxxs"
|
|
51
|
+
// style={{ pointerEvents: "initial" }}
|
|
52
|
+
{...containerProps}
|
|
53
|
+
transition={{
|
|
54
|
+
type: "spring",
|
|
55
|
+
bounce:
|
|
56
|
+
BOUNCE_VARIANTS[variantKey as keyof typeof BOUNCE_VARIANTS] ??
|
|
57
|
+
DEFAULT_BOUNCE,
|
|
58
|
+
}}
|
|
59
|
+
>
|
|
60
|
+
<Box
|
|
61
|
+
as={motion.div}
|
|
62
|
+
animate={{
|
|
63
|
+
scale: 1,
|
|
64
|
+
opacity: 1,
|
|
65
|
+
filter: "blur(0px)",
|
|
66
|
+
originX: 0.5,
|
|
67
|
+
originY: 0.5,
|
|
68
|
+
transition: { delay: 0.05 },
|
|
69
|
+
}}
|
|
70
|
+
initial={{
|
|
71
|
+
scale: 0.9,
|
|
72
|
+
opacity: 0,
|
|
73
|
+
filter: "blur(5px)",
|
|
74
|
+
originX: 0.5,
|
|
75
|
+
originY: 0.5,
|
|
76
|
+
}}
|
|
77
|
+
key={view}
|
|
78
|
+
transition={{
|
|
79
|
+
type: "spring",
|
|
80
|
+
bounce:
|
|
81
|
+
BOUNCE_VARIANTS[variantKey as keyof typeof BOUNCE_VARIANTS] ??
|
|
82
|
+
DEFAULT_BOUNCE,
|
|
83
|
+
}}
|
|
84
|
+
>
|
|
85
|
+
{content}
|
|
86
|
+
</Box>
|
|
87
|
+
</Box>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
import { motion, AnimatePresence } from "motion/react"
|
|
3
|
+
import Box, { BoxProps } from "@/design-system/elements/box"
|
|
4
|
+
|
|
5
|
+
import Image from "next/image"
|
|
6
|
+
|
|
7
|
+
const FADE_DIRECTION = "to bottom"
|
|
8
|
+
|
|
9
|
+
type FaderProps = BoxProps & {
|
|
10
|
+
image?: string | undefined | null
|
|
11
|
+
fade?: boolean
|
|
12
|
+
fadeColor?: string
|
|
13
|
+
fadeDirection?: string
|
|
14
|
+
imgStyle?: {}
|
|
15
|
+
overlay?: {
|
|
16
|
+
start: { color: string; stop: string }
|
|
17
|
+
end: { color: string; stop: string }
|
|
18
|
+
}
|
|
19
|
+
variants?: {}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const overlayOptions = {
|
|
23
|
+
start: {
|
|
24
|
+
color: "transparent",
|
|
25
|
+
stop: "0%",
|
|
26
|
+
},
|
|
27
|
+
end: {
|
|
28
|
+
color: "currentColor",
|
|
29
|
+
stop: "75%",
|
|
30
|
+
},
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// const fixedStyle = {
|
|
34
|
+
// backgroundAttachment: "fixed",
|
|
35
|
+
// backgroundSize: "cover",
|
|
36
|
+
// };
|
|
37
|
+
|
|
38
|
+
// const defaultStyle = {
|
|
39
|
+
// backgroundSize: "cover",
|
|
40
|
+
// backgroundPosition: "center",
|
|
41
|
+
// };
|
|
42
|
+
|
|
43
|
+
const defaultVariants = {
|
|
44
|
+
enter: {
|
|
45
|
+
opacity: 0,
|
|
46
|
+
scale: 1.2,
|
|
47
|
+
y: 30,
|
|
48
|
+
x: 0,
|
|
49
|
+
filter: "blur(20px)",
|
|
50
|
+
transition: { duration: 0.4 },
|
|
51
|
+
},
|
|
52
|
+
center: {
|
|
53
|
+
opacity: 1,
|
|
54
|
+
scale: 1,
|
|
55
|
+
y: 0,
|
|
56
|
+
x: 0,
|
|
57
|
+
filter: "blur(0px)",
|
|
58
|
+
transition: { duration: 0.4 },
|
|
59
|
+
},
|
|
60
|
+
exit: {
|
|
61
|
+
opacity: 0,
|
|
62
|
+
scale: 1.04,
|
|
63
|
+
y: -30,
|
|
64
|
+
x: 0,
|
|
65
|
+
filter: "blur(20px)",
|
|
66
|
+
transition: { duration: 0.4 },
|
|
67
|
+
},
|
|
68
|
+
hover: {
|
|
69
|
+
opacity: 1,
|
|
70
|
+
scale: 1.04,
|
|
71
|
+
y: 0,
|
|
72
|
+
x: 0,
|
|
73
|
+
transition: { duration: 0.2 },
|
|
74
|
+
},
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export default function Fader({
|
|
78
|
+
image,
|
|
79
|
+
fade = true,
|
|
80
|
+
fadeColor = "currentColor",
|
|
81
|
+
fadeDirection = FADE_DIRECTION,
|
|
82
|
+
//imgStyle = "fixed",
|
|
83
|
+
overlay = overlayOptions,
|
|
84
|
+
variants = defaultVariants,
|
|
85
|
+
...props
|
|
86
|
+
}: FaderProps) {
|
|
87
|
+
return (
|
|
88
|
+
<Box
|
|
89
|
+
as={motion.div}
|
|
90
|
+
overflow="hidden"
|
|
91
|
+
position="relative"
|
|
92
|
+
width="100%"
|
|
93
|
+
height="100%"
|
|
94
|
+
{...props}
|
|
95
|
+
>
|
|
96
|
+
{/* Fade overlay */}
|
|
97
|
+
{fade ? (
|
|
98
|
+
<Box
|
|
99
|
+
as={motion.div}
|
|
100
|
+
position={"absolute"}
|
|
101
|
+
color={fadeColor}
|
|
102
|
+
width="100%"
|
|
103
|
+
height="100%"
|
|
104
|
+
top="0"
|
|
105
|
+
bottom="0"
|
|
106
|
+
left="0"
|
|
107
|
+
zIndex={3}
|
|
108
|
+
backgroundImage={`linear-gradient(${fadeDirection}, ${overlay.start.color} ${overlay.start.stop}, ${overlay.end.color} ${overlay.end.stop})`}
|
|
109
|
+
/>
|
|
110
|
+
) : null}
|
|
111
|
+
|
|
112
|
+
{/* Main image */}
|
|
113
|
+
|
|
114
|
+
{image && (
|
|
115
|
+
<AnimatePresence mode="wait">
|
|
116
|
+
<Box
|
|
117
|
+
as={motion.div}
|
|
118
|
+
variants={variants}
|
|
119
|
+
initial="enter"
|
|
120
|
+
animate="center"
|
|
121
|
+
exit="exit"
|
|
122
|
+
transition={{ duration: 2 }}
|
|
123
|
+
width="100%"
|
|
124
|
+
height="100%"
|
|
125
|
+
position="absolute"
|
|
126
|
+
maxWidth="100%"
|
|
127
|
+
top="0"
|
|
128
|
+
left="0"
|
|
129
|
+
zIndex={1}
|
|
130
|
+
>
|
|
131
|
+
<Image
|
|
132
|
+
src={image as string}
|
|
133
|
+
priority={true}
|
|
134
|
+
loading="eager"
|
|
135
|
+
fill
|
|
136
|
+
alt="image"
|
|
137
|
+
sizes="100%"
|
|
138
|
+
style={{ objectFit: "cover" }}
|
|
139
|
+
/>
|
|
140
|
+
</Box>
|
|
141
|
+
</AnimatePresence>
|
|
142
|
+
)}
|
|
143
|
+
</Box>
|
|
144
|
+
)
|
|
145
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# FamilyDrawer Component
|
|
2
|
+
|
|
3
|
+
A sophisticated drawer component with multiple animated views, perfect for wallet management interfaces or any multi-step configuration flow.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Multi-view Navigation**: Seamlessly switch between different views (default, key, phrase, remove)
|
|
8
|
+
- **Smooth Animations**: Height-based animations with blur effects during transitions
|
|
9
|
+
- **Design System Integration**: Built using the existing Drawer component and design system elements
|
|
10
|
+
- **Customizable**: Custom trigger buttons, widths, and callbacks
|
|
11
|
+
- **Accessible**: Proper ARIA labels and keyboard navigation support
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
### Basic Usage
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { FamilyDrawer } from "@/design-system/blocks";
|
|
19
|
+
|
|
20
|
+
function MyComponent() {
|
|
21
|
+
return (
|
|
22
|
+
<FamilyDrawer id="wallet-settings" />
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Advanced Usage
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
import { FamilyDrawer } from "@/design-system/blocks";
|
|
31
|
+
import { Button, Text } from "@/design-system/elements";
|
|
32
|
+
|
|
33
|
+
function MyComponent() {
|
|
34
|
+
const handleViewChange = (view) => {
|
|
35
|
+
console.log("Current view:", view);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<FamilyDrawer
|
|
40
|
+
id="wallet-settings"
|
|
41
|
+
initialView="default"
|
|
42
|
+
width="400px"
|
|
43
|
+
onViewChange={handleViewChange}
|
|
44
|
+
trigger={
|
|
45
|
+
<Button variant="primary" $size="medium">
|
|
46
|
+
<Text>Custom Trigger</Text>
|
|
47
|
+
</Button>
|
|
48
|
+
}
|
|
49
|
+
/>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Props
|
|
55
|
+
|
|
56
|
+
| Prop | Type | Default | Description |
|
|
57
|
+
|------|------|---------|-------------|
|
|
58
|
+
| `id` | `string` | **Required** | Unique identifier for the drawer |
|
|
59
|
+
| `initialView` | `FamilyDrawerView` | `"default"` | Initial view to display |
|
|
60
|
+
| `trigger` | `React.ReactNode` | Default settings button | Custom trigger element |
|
|
61
|
+
| `onViewChange` | `(view: FamilyDrawerView) => void` | `undefined` | Callback when view changes |
|
|
62
|
+
| `width` | `string \| number \| object` | `"360px"` | Drawer width (responsive object supported) |
|
|
63
|
+
|
|
64
|
+
## Views
|
|
65
|
+
|
|
66
|
+
The component includes four built-in views:
|
|
67
|
+
|
|
68
|
+
1. **Default View** (`"default"`): Main options menu with navigation buttons
|
|
69
|
+
2. **Key View** (`"key"`): Private key management with security warnings
|
|
70
|
+
3. **Phrase View** (`"phrase"`): Recovery phrase management with security warnings
|
|
71
|
+
4. **Remove View** (`"remove"`): Wallet removal confirmation with warnings
|
|
72
|
+
|
|
73
|
+
## Customization
|
|
74
|
+
|
|
75
|
+
### Custom Trigger
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
<FamilyDrawer
|
|
79
|
+
id="custom-drawer"
|
|
80
|
+
trigger={
|
|
81
|
+
<Button variant="ghost" $size="small">
|
|
82
|
+
<IconGear size="1rem" />
|
|
83
|
+
Options
|
|
84
|
+
</Button>
|
|
85
|
+
}
|
|
86
|
+
/>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Responsive Width
|
|
90
|
+
|
|
91
|
+
```tsx
|
|
92
|
+
<FamilyDrawer
|
|
93
|
+
id="responsive-drawer"
|
|
94
|
+
width={{ _: "100%", md: "400px" }}
|
|
95
|
+
/>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Animation Details
|
|
99
|
+
|
|
100
|
+
- **Height Animation**: Smooth height transitions using Framer Motion
|
|
101
|
+
- **View Transitions**: Blur and scale effects during view changes
|
|
102
|
+
- **Timing**: 270ms duration with custom easing curves
|
|
103
|
+
- **Performance**: Uses `react-use-measure` for efficient dimension tracking
|
|
104
|
+
|
|
105
|
+
## Dependencies
|
|
106
|
+
|
|
107
|
+
- `motion/react` (Framer Motion)
|
|
108
|
+
- `react-use-measure`
|
|
109
|
+
- Design system components (Box, Button, Text, Drawer)
|
|
110
|
+
- Icon components from the project's icon library
|
|
111
|
+
|
|
112
|
+
## Related Components
|
|
113
|
+
|
|
114
|
+
- [Drawer](../Drawer/README.md) - Base drawer component
|
|
115
|
+
- [Modal](../Modal/README.md) - Alternative overlay component
|
|
116
|
+
- [Popover](../Popover/README.md) - Lightweight overlay component
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { Box, Text, Button } from "@/design-system/elements";
|
|
5
|
+
import { Container } from "@/design-system/blocks";
|
|
6
|
+
import FamilyDrawer from "./index";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Example usage of the FamilyDrawer component
|
|
10
|
+
*
|
|
11
|
+
* This demonstrates how to use the FamilyDrawer with:
|
|
12
|
+
* - Default trigger button
|
|
13
|
+
* - Custom trigger button
|
|
14
|
+
* - View change callback
|
|
15
|
+
* - Custom width
|
|
16
|
+
*/
|
|
17
|
+
export const FamilyDrawerExample: React.FC = () => {
|
|
18
|
+
const handleViewChange = (view: string) => {
|
|
19
|
+
console.log("View changed to:", view);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<Container maxWidth="800px" p="large">
|
|
24
|
+
<Box display="flex" flexDirection="column" gap="large">
|
|
25
|
+
<Box>
|
|
26
|
+
<Text as="h1" fontSize="xxlarge" fontWeight="bold" mb="medium">
|
|
27
|
+
FamilyDrawer Examples
|
|
28
|
+
</Text>
|
|
29
|
+
<Text fontSize="medium" color="secondary" lineHeight="relaxed">
|
|
30
|
+
Interactive drawer component with multiple views for wallet management.
|
|
31
|
+
</Text>
|
|
32
|
+
</Box>
|
|
33
|
+
|
|
34
|
+
<Box
|
|
35
|
+
display="flex"
|
|
36
|
+
flexDirection="column"
|
|
37
|
+
gap="large"
|
|
38
|
+
p="large"
|
|
39
|
+
skin="card"
|
|
40
|
+
shape="rounded"
|
|
41
|
+
>
|
|
42
|
+
<Box>
|
|
43
|
+
<Text as="h2" fontSize="large" fontWeight="semibold" mb="small">
|
|
44
|
+
Default Usage
|
|
45
|
+
</Text>
|
|
46
|
+
<Text fontSize="small" color="secondary" mb="medium">
|
|
47
|
+
Basic FamilyDrawer with default settings and trigger button.
|
|
48
|
+
</Text>
|
|
49
|
+
<FamilyDrawer id="example-1" />
|
|
50
|
+
</Box>
|
|
51
|
+
|
|
52
|
+
<Box>
|
|
53
|
+
<Text as="h2" fontSize="large" fontWeight="semibold" mb="small">
|
|
54
|
+
Custom Trigger
|
|
55
|
+
</Text>
|
|
56
|
+
<Text fontSize="small" color="secondary" mb="medium">
|
|
57
|
+
FamilyDrawer with a custom trigger button.
|
|
58
|
+
</Text>
|
|
59
|
+
<FamilyDrawer
|
|
60
|
+
id="example-2"
|
|
61
|
+
trigger={
|
|
62
|
+
<Button variant="primary" $size="small">
|
|
63
|
+
<Text fontSize="small" fontWeight="semibold">
|
|
64
|
+
Wallet Options
|
|
65
|
+
</Text>
|
|
66
|
+
</Button>
|
|
67
|
+
}
|
|
68
|
+
/>
|
|
69
|
+
</Box>
|
|
70
|
+
|
|
71
|
+
<Box>
|
|
72
|
+
<Text as="h2" fontSize="large" fontWeight="semibold" mb="small">
|
|
73
|
+
With Callbacks
|
|
74
|
+
</Text>
|
|
75
|
+
<Text fontSize="small" color="secondary" mb="medium">
|
|
76
|
+
FamilyDrawer with view change callback and custom width.
|
|
77
|
+
</Text>
|
|
78
|
+
<FamilyDrawer
|
|
79
|
+
id="example-3"
|
|
80
|
+
onViewChange={handleViewChange}
|
|
81
|
+
width="400px"
|
|
82
|
+
initialView="default"
|
|
83
|
+
/>
|
|
84
|
+
</Box>
|
|
85
|
+
</Box>
|
|
86
|
+
|
|
87
|
+
<Box
|
|
88
|
+
p="medium"
|
|
89
|
+
skin="translucent"
|
|
90
|
+
shape="rounded"
|
|
91
|
+
borderLeft="4px solid"
|
|
92
|
+
borderColor="primary"
|
|
93
|
+
>
|
|
94
|
+
<Text fontSize="small" fontWeight="semibold" color="primary" mb="xsmall">
|
|
95
|
+
Usage Notes
|
|
96
|
+
</Text>
|
|
97
|
+
<Text fontSize="small" color="secondary" lineHeight="relaxed">
|
|
98
|
+
Each FamilyDrawer instance requires a unique <code>id</code> prop for proper state management.
|
|
99
|
+
The drawer uses the existing Drawer component from the design system with smooth animations
|
|
100
|
+
between different views.
|
|
101
|
+
</Text>
|
|
102
|
+
</Box>
|
|
103
|
+
</Box>
|
|
104
|
+
</Container>
|
|
105
|
+
);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export default FamilyDrawerExample;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React, { useState, useMemo } from "react";
|
|
4
|
+
import { Box, Button, Text } from "@/design-system/elements";
|
|
5
|
+
import { Drawer, DrawerButton } from "@/design-system/blocks/Drawer";
|
|
6
|
+
import { AnimatePresence, motion } from "motion/react";
|
|
7
|
+
import { IconGear } from "@/design-system/icons";
|
|
8
|
+
import useMeasure from "react-use-measure";
|
|
9
|
+
|
|
10
|
+
import { DefaultView } from "./views/DefaultView";
|
|
11
|
+
import { KeyView } from "./views/KeyView";
|
|
12
|
+
import { PhraseView } from "./views/PhraseView";
|
|
13
|
+
import { RemoveView } from "./views/RemoveView";
|
|
14
|
+
|
|
15
|
+
export type FamilyDrawerView = "default" | "key" | "phrase" | "remove";
|
|
16
|
+
|
|
17
|
+
interface FamilyDrawerProps {
|
|
18
|
+
/** Unique identifier for the drawer */
|
|
19
|
+
id: string;
|
|
20
|
+
/** Initial view to display */
|
|
21
|
+
initialView?: FamilyDrawerView;
|
|
22
|
+
/** Custom trigger button */
|
|
23
|
+
trigger?: React.ReactNode;
|
|
24
|
+
/** Callback when view changes */
|
|
25
|
+
onViewChange?: (view: FamilyDrawerView) => void;
|
|
26
|
+
/** Custom drawer width */
|
|
27
|
+
width?: string | number | object;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const FamilyDrawer: React.FC<FamilyDrawerProps> = ({
|
|
31
|
+
id,
|
|
32
|
+
initialView = "default",
|
|
33
|
+
trigger,
|
|
34
|
+
onViewChange,
|
|
35
|
+
width = "360px",
|
|
36
|
+
}) => {
|
|
37
|
+
const [view, setView] = useState<FamilyDrawerView>(initialView);
|
|
38
|
+
const [elementRef, bounds] = useMeasure();
|
|
39
|
+
|
|
40
|
+
const handleViewChange = (newView: FamilyDrawerView) => {
|
|
41
|
+
setView(newView);
|
|
42
|
+
onViewChange?.(newView);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const content = useMemo(() => {
|
|
46
|
+
const commonProps = { setView: handleViewChange };
|
|
47
|
+
|
|
48
|
+
switch (view) {
|
|
49
|
+
case "default":
|
|
50
|
+
return <DefaultView {...commonProps} />;
|
|
51
|
+
case "phrase":
|
|
52
|
+
return <PhraseView {...commonProps} />;
|
|
53
|
+
case "key":
|
|
54
|
+
return <KeyView {...commonProps} />;
|
|
55
|
+
case "remove":
|
|
56
|
+
return <RemoveView {...commonProps} />;
|
|
57
|
+
default:
|
|
58
|
+
return <DefaultView {...commonProps} />;
|
|
59
|
+
}
|
|
60
|
+
}, [view]);
|
|
61
|
+
|
|
62
|
+
const defaultTrigger = (
|
|
63
|
+
<Button
|
|
64
|
+
variant="ghost"
|
|
65
|
+
$size="medium"
|
|
66
|
+
display="flex"
|
|
67
|
+
alignItems="center"
|
|
68
|
+
gap="small"
|
|
69
|
+
shape="rounded"
|
|
70
|
+
skin="translucent"
|
|
71
|
+
>
|
|
72
|
+
<Text fontSize="medium" fontWeight="semibold">
|
|
73
|
+
Settings
|
|
74
|
+
</Text>
|
|
75
|
+
<Box as={IconGear} size="1.25rem" />
|
|
76
|
+
</Button>
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<>
|
|
81
|
+
<DrawerButton drawerId={id} label="">
|
|
82
|
+
{trigger || defaultTrigger}
|
|
83
|
+
</DrawerButton>
|
|
84
|
+
|
|
85
|
+
<Drawer id={id} title="Options" width={width}>
|
|
86
|
+
<Box
|
|
87
|
+
as={motion.div}
|
|
88
|
+
animate={{ height: bounds.height || "auto" }}
|
|
89
|
+
transition={{
|
|
90
|
+
type: "tween",
|
|
91
|
+
ease: [0.26, 1, 0.5, 1],
|
|
92
|
+
duration: 0.27,
|
|
93
|
+
}}
|
|
94
|
+
overflow="hidden"
|
|
95
|
+
>
|
|
96
|
+
<Box ref={elementRef} p="medium">
|
|
97
|
+
<AnimatePresence mode="wait" initial={false}>
|
|
98
|
+
<Box
|
|
99
|
+
as={motion.div}
|
|
100
|
+
key={view}
|
|
101
|
+
initial={{ opacity: 0, scale: 0.96, filter: "blur(2px)" }}
|
|
102
|
+
animate={{ opacity: 1, scale: 1, filter: "blur(0px)" }}
|
|
103
|
+
exit={{ opacity: 0, scale: 0.96, filter: "blur(2px)" }}
|
|
104
|
+
transition={{
|
|
105
|
+
duration: 0.27,
|
|
106
|
+
ease: [0.26, 0.08, 0.25, 1],
|
|
107
|
+
}}
|
|
108
|
+
>
|
|
109
|
+
{content}
|
|
110
|
+
</Box>
|
|
111
|
+
</AnimatePresence>
|
|
112
|
+
</Box>
|
|
113
|
+
</Box>
|
|
114
|
+
</Drawer>
|
|
115
|
+
</>
|
|
116
|
+
);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export default FamilyDrawer;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { Box, Button, Text } from "@/design-system/elements";
|
|
5
|
+
import { IconLock, IconOWarning, IconText } from "@/design-system/icons";
|
|
6
|
+
import { FamilyDrawerView } from "../index";
|
|
7
|
+
|
|
8
|
+
interface DefaultViewProps {
|
|
9
|
+
setView: (view: FamilyDrawerView) => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const DefaultView: React.FC<DefaultViewProps> = ({ setView }) => {
|
|
13
|
+
return (
|
|
14
|
+
<Box>
|
|
15
|
+
<Box
|
|
16
|
+
display="flex"
|
|
17
|
+
alignItems="center"
|
|
18
|
+
justifyContent="space-between"
|
|
19
|
+
borderBottom="1px solid"
|
|
20
|
+
borderColor="base"
|
|
21
|
+
pb="medium"
|
|
22
|
+
mb="medium"
|
|
23
|
+
>
|
|
24
|
+
<Text fontSize="large" fontWeight="semibold" color="primary">
|
|
25
|
+
Options
|
|
26
|
+
</Text>
|
|
27
|
+
</Box>
|
|
28
|
+
|
|
29
|
+
<Box display="flex" flexDirection="column" gap="small">
|
|
30
|
+
<OptionButton
|
|
31
|
+
title="View Private Key"
|
|
32
|
+
icon={<Box as={IconLock} size="1.25rem" />}
|
|
33
|
+
onClick={() => setView("key")}
|
|
34
|
+
/>
|
|
35
|
+
<OptionButton
|
|
36
|
+
title="View Recovery Phrase"
|
|
37
|
+
icon={<Box as={IconText} size="1.25rem" />}
|
|
38
|
+
onClick={() => setView("phrase")}
|
|
39
|
+
/>
|
|
40
|
+
<OptionButton
|
|
41
|
+
title="Remove Wallet"
|
|
42
|
+
icon={<Box as={IconOWarning} size="1.25rem" />}
|
|
43
|
+
onClick={() => setView("remove")}
|
|
44
|
+
isDestructive
|
|
45
|
+
/>
|
|
46
|
+
</Box>
|
|
47
|
+
</Box>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
interface OptionButtonProps {
|
|
52
|
+
title: string;
|
|
53
|
+
icon: React.ReactNode;
|
|
54
|
+
onClick: () => void;
|
|
55
|
+
isDestructive?: boolean;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const OptionButton: React.FC<OptionButtonProps> = ({
|
|
59
|
+
title,
|
|
60
|
+
icon,
|
|
61
|
+
onClick,
|
|
62
|
+
isDestructive = false,
|
|
63
|
+
}) => {
|
|
64
|
+
return (
|
|
65
|
+
<Button
|
|
66
|
+
variant="ghost"
|
|
67
|
+
$size="large"
|
|
68
|
+
onClick={onClick}
|
|
69
|
+
display="flex"
|
|
70
|
+
alignItems="center"
|
|
71
|
+
justifyContent="flex-start"
|
|
72
|
+
gap="medium"
|
|
73
|
+
p="medium"
|
|
74
|
+
shape="rounded"
|
|
75
|
+
skin={isDestructive ? "danger" : "translucent"}
|
|
76
|
+
color={isDestructive ? "danger" : "secondary"}
|
|
77
|
+
hover={isDestructive ? "danger" : "subtle"}
|
|
78
|
+
interactive={{
|
|
79
|
+
hover: {
|
|
80
|
+
color: isDestructive ? "danger" : "primary",
|
|
81
|
+
}
|
|
82
|
+
}}
|
|
83
|
+
width="100%"
|
|
84
|
+
>
|
|
85
|
+
<Box color={isDestructive ? "danger" : "secondary"}>
|
|
86
|
+
{icon}
|
|
87
|
+
</Box>
|
|
88
|
+
<Text fontSize="medium" fontWeight="medium">
|
|
89
|
+
{title}
|
|
90
|
+
</Text>
|
|
91
|
+
</Button>
|
|
92
|
+
);
|
|
93
|
+
};
|