@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,149 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { motion } from "motion/react";
|
|
5
|
+
import { Box, Text } from "@/design-system/elements";
|
|
6
|
+
import Tooltip from "@/design-system/blocks/Tooltip";
|
|
7
|
+
import { useHover } from "./hooks";
|
|
8
|
+
import { StepThemeConfig } from "./types";
|
|
9
|
+
import { IconDCheck } from "@/design-system/icons";
|
|
10
|
+
|
|
11
|
+
interface StepIndicatorProps {
|
|
12
|
+
/** Whether this step is currently active */
|
|
13
|
+
active: boolean;
|
|
14
|
+
/** The step number (1-based) */
|
|
15
|
+
index: number;
|
|
16
|
+
/** The label text for this step */
|
|
17
|
+
label: string;
|
|
18
|
+
/** Whether this step is completed */
|
|
19
|
+
completed: boolean;
|
|
20
|
+
/** Theme configuration */
|
|
21
|
+
variant: StepThemeConfig;
|
|
22
|
+
/** Whether to use simple mode (no numbers/icons) */
|
|
23
|
+
simple?: boolean;
|
|
24
|
+
/** Whether to show labels */
|
|
25
|
+
showLabels?: boolean;
|
|
26
|
+
/** Whether to show tooltips on hover */
|
|
27
|
+
showTooltip?: boolean;
|
|
28
|
+
/** Callback when step is clicked */
|
|
29
|
+
setActive?: (index: number) => void;
|
|
30
|
+
/** Additional props */
|
|
31
|
+
[key: string]: any;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface DotProps {
|
|
35
|
+
state: {
|
|
36
|
+
bg: string;
|
|
37
|
+
color: string;
|
|
38
|
+
};
|
|
39
|
+
variant: StepThemeConfig;
|
|
40
|
+
active: boolean;
|
|
41
|
+
children: React.ReactNode;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function Dot({ state, variant, active, children }: DotProps) {
|
|
45
|
+
return (
|
|
46
|
+
<Box
|
|
47
|
+
as={motion.div}
|
|
48
|
+
bg={state.bg}
|
|
49
|
+
color={state.color}
|
|
50
|
+
border=".25rem solid"
|
|
51
|
+
borderColor={variant.borderColor}
|
|
52
|
+
shape="circle"
|
|
53
|
+
flex="none"
|
|
54
|
+
width={`${parseFloat(variant.size) * 8}${variant.unit}`}
|
|
55
|
+
height={`${parseFloat(variant.size) * 8}${variant.unit}`}
|
|
56
|
+
display="flex"
|
|
57
|
+
alignItems="center"
|
|
58
|
+
justifyContent="center"
|
|
59
|
+
initial={{ scale: 0.75 }}
|
|
60
|
+
animate={{ scale: active ? 1.125 : 0.75 }}
|
|
61
|
+
exit={{ scale: 0.75 }}
|
|
62
|
+
// transition={{ type: "spring", stiffness: 500 }}
|
|
63
|
+
style={{ pointerEvents: "none" }}
|
|
64
|
+
>
|
|
65
|
+
{children}
|
|
66
|
+
</Box>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function StepIndicator({
|
|
71
|
+
active,
|
|
72
|
+
index,
|
|
73
|
+
label,
|
|
74
|
+
completed,
|
|
75
|
+
variant,
|
|
76
|
+
simple = false,
|
|
77
|
+
showLabels = true,
|
|
78
|
+
showTooltip = true,
|
|
79
|
+
setActive,
|
|
80
|
+
...rest
|
|
81
|
+
}: StepIndicatorProps) {
|
|
82
|
+
const [hoverRef, isHovered] = useHover();
|
|
83
|
+
|
|
84
|
+
const getCurrentTheme = () => {
|
|
85
|
+
if (active) {
|
|
86
|
+
return variant.presets.active;
|
|
87
|
+
}
|
|
88
|
+
if (completed) {
|
|
89
|
+
return variant.presets.completed;
|
|
90
|
+
}
|
|
91
|
+
return variant.presets.default;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const currentTheme = getCurrentTheme();
|
|
95
|
+
|
|
96
|
+
const handleClick = () => {
|
|
97
|
+
if (setActive) {
|
|
98
|
+
setActive(index - 1); // Convert to 0-based index
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const stepContent = (
|
|
103
|
+
<Box
|
|
104
|
+
display="flex"
|
|
105
|
+
alignItems="center"
|
|
106
|
+
flexDirection="column"
|
|
107
|
+
flex={active ? "auto" : "none"}
|
|
108
|
+
mx="auto"
|
|
109
|
+
position="relative"
|
|
110
|
+
zIndex={isHovered ? 3 : 2}
|
|
111
|
+
cursor={setActive ? "pointer" : "default"}
|
|
112
|
+
onClick={handleClick}
|
|
113
|
+
{...rest}
|
|
114
|
+
ref={hoverRef}
|
|
115
|
+
>
|
|
116
|
+
<Dot active={active} variant={variant} state={currentTheme}>
|
|
117
|
+
{!simple && active && (
|
|
118
|
+
<Text fontSize={variant.fontSize} color="inherit">
|
|
119
|
+
{index}
|
|
120
|
+
</Text>
|
|
121
|
+
)}
|
|
122
|
+
{!simple && completed && (
|
|
123
|
+
<Box as={IconDCheck} size="1.5rem" color="inherit" />
|
|
124
|
+
)}
|
|
125
|
+
</Dot>
|
|
126
|
+
|
|
127
|
+
{showLabels && (
|
|
128
|
+
<Text
|
|
129
|
+
fontWeight="700"
|
|
130
|
+
fontSize={variant.fontSize}
|
|
131
|
+
display={["none", "flex"]}
|
|
132
|
+
color={active ? variant.baseColor : variant.textColor}
|
|
133
|
+
textAlign="center"
|
|
134
|
+
mt="xxs"
|
|
135
|
+
>
|
|
136
|
+
{label}
|
|
137
|
+
</Text>
|
|
138
|
+
)}
|
|
139
|
+
</Box>
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
if (showTooltip) {
|
|
143
|
+
return <Tooltip content={label}>{stepContent}</Tooltip>;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return stepContent;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export default StepIndicator;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { Box } from "@/design-system/elements";
|
|
5
|
+
import StepIndicator from "./StepIndicator";
|
|
6
|
+
import { BaseStepProps, StepThemeConfig, StepVariant } from "./types";
|
|
7
|
+
|
|
8
|
+
interface StepProgressProps extends BaseStepProps {
|
|
9
|
+
/** Index of the completed step (all steps up to this index are completed) */
|
|
10
|
+
completed?: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const defaultThemeConfig: Record<StepVariant, StepThemeConfig> = {
|
|
14
|
+
light: {
|
|
15
|
+
baseColor: "primary",
|
|
16
|
+
textColor: "secondary",
|
|
17
|
+
borderColor: "white",
|
|
18
|
+
trackColor: "tertiary",
|
|
19
|
+
completedTrackColor: "accent",
|
|
20
|
+
size: "0.3",
|
|
21
|
+
unit: "rem",
|
|
22
|
+
fontSize: "small",
|
|
23
|
+
presets: {
|
|
24
|
+
active: {
|
|
25
|
+
bg: "accent",
|
|
26
|
+
color: "white",
|
|
27
|
+
},
|
|
28
|
+
completed: {
|
|
29
|
+
bg: "accent",
|
|
30
|
+
color: "white",
|
|
31
|
+
},
|
|
32
|
+
default: {
|
|
33
|
+
bg: "tertiary",
|
|
34
|
+
color: "secondary",
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
dark: {
|
|
39
|
+
baseColor: "primary",
|
|
40
|
+
textColor: "secondary",
|
|
41
|
+
borderColor: "tertiary",
|
|
42
|
+
trackColor: "secondary",
|
|
43
|
+
completedTrackColor: "brand",
|
|
44
|
+
size: "0.3",
|
|
45
|
+
unit: "rem",
|
|
46
|
+
fontSize: "small",
|
|
47
|
+
presets: {
|
|
48
|
+
active: {
|
|
49
|
+
bg: "white",
|
|
50
|
+
color: "primary",
|
|
51
|
+
},
|
|
52
|
+
completed: {
|
|
53
|
+
bg: "primary",
|
|
54
|
+
color: "white",
|
|
55
|
+
},
|
|
56
|
+
default: {
|
|
57
|
+
bg: "secondary",
|
|
58
|
+
color: "white",
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
function StepProgress({
|
|
65
|
+
config,
|
|
66
|
+
active,
|
|
67
|
+
variant = "light",
|
|
68
|
+
skin,
|
|
69
|
+
simple = false,
|
|
70
|
+
showLabels = true,
|
|
71
|
+
showEdges = true,
|
|
72
|
+
showTooltip = true,
|
|
73
|
+
childProps,
|
|
74
|
+
setActive,
|
|
75
|
+
...rest
|
|
76
|
+
}: StepProgressProps) {
|
|
77
|
+
const currentTheme = skin || defaultThemeConfig[variant];
|
|
78
|
+
|
|
79
|
+
// Calculate the progress width based on active step
|
|
80
|
+
const progressWidth = `${
|
|
81
|
+
(active + 1) * (100 / config.length) - 100 / (config.length * 2)
|
|
82
|
+
}%`;
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<Box
|
|
86
|
+
display="grid"
|
|
87
|
+
gridTemplateColumns={`repeat(${config.length}, 1fr)`}
|
|
88
|
+
position="relative"
|
|
89
|
+
alignItems="center"
|
|
90
|
+
width="100%"
|
|
91
|
+
{...rest}
|
|
92
|
+
>
|
|
93
|
+
{/* Step Indicators */}
|
|
94
|
+
{config.map((item, i) => (
|
|
95
|
+
<StepIndicator
|
|
96
|
+
key={i}
|
|
97
|
+
active={active === i}
|
|
98
|
+
index={i + 1}
|
|
99
|
+
completed={active > i}
|
|
100
|
+
showLabels={showLabels}
|
|
101
|
+
showTooltip={showTooltip}
|
|
102
|
+
variant={currentTheme}
|
|
103
|
+
simple={simple}
|
|
104
|
+
setActive={setActive}
|
|
105
|
+
{...item}
|
|
106
|
+
{...childProps}
|
|
107
|
+
/>
|
|
108
|
+
))}
|
|
109
|
+
|
|
110
|
+
{/* Edge Circles */}
|
|
111
|
+
{showEdges && (
|
|
112
|
+
<>
|
|
113
|
+
<Box
|
|
114
|
+
size="1rem"
|
|
115
|
+
shape="circle"
|
|
116
|
+
bg={currentTheme.presets.default.bg}
|
|
117
|
+
border="3px solid"
|
|
118
|
+
borderColor={currentTheme.borderColor}
|
|
119
|
+
position="absolute"
|
|
120
|
+
top="50%"
|
|
121
|
+
left="-1px"
|
|
122
|
+
mt="-1.375rem"
|
|
123
|
+
zIndex={1}
|
|
124
|
+
/>
|
|
125
|
+
<Box
|
|
126
|
+
width="1rem"
|
|
127
|
+
height="1rem"
|
|
128
|
+
shape="circle"
|
|
129
|
+
bg={currentTheme.presets.default.bg}
|
|
130
|
+
border="3px solid"
|
|
131
|
+
borderColor={currentTheme.borderColor}
|
|
132
|
+
position="absolute"
|
|
133
|
+
top="50%"
|
|
134
|
+
right="0"
|
|
135
|
+
mt="-1.375rem"
|
|
136
|
+
zIndex={1}
|
|
137
|
+
/>
|
|
138
|
+
</>
|
|
139
|
+
)}
|
|
140
|
+
|
|
141
|
+
{/* Progress Track */}
|
|
142
|
+
<Box
|
|
143
|
+
width="100%"
|
|
144
|
+
height={`${currentTheme.size}${currentTheme.unit}`}
|
|
145
|
+
bg={currentTheme.trackColor}
|
|
146
|
+
shape="pill"
|
|
147
|
+
position="absolute"
|
|
148
|
+
top="50%"
|
|
149
|
+
mt="-1rem"
|
|
150
|
+
zIndex={0}
|
|
151
|
+
>
|
|
152
|
+
{/* Completed Progress */}
|
|
153
|
+
<Box
|
|
154
|
+
width={progressWidth}
|
|
155
|
+
height="100%"
|
|
156
|
+
bg={currentTheme.completedTrackColor}
|
|
157
|
+
shape="pill"
|
|
158
|
+
/>
|
|
159
|
+
</Box>
|
|
160
|
+
</Box>
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export default StepProgress;
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { Box, Text } from "@/design-system/elements";
|
|
5
|
+
import StepProgress from "./StepProgress";
|
|
6
|
+
import { BaseStepProps } from "./types";
|
|
7
|
+
import { useStepNavigation } from "./hooks";
|
|
8
|
+
|
|
9
|
+
interface StepsProps extends Omit<BaseStepProps, "active"> {
|
|
10
|
+
/** Initial active step (0-based) */
|
|
11
|
+
initialStep?: number;
|
|
12
|
+
/** Whether navigation is controlled externally */
|
|
13
|
+
controlled?: boolean;
|
|
14
|
+
/** Current active step when controlled */
|
|
15
|
+
activeStep?: number;
|
|
16
|
+
/** Callback when step changes */
|
|
17
|
+
onStepChange?: (step: number) => void;
|
|
18
|
+
/** Whether to show navigation buttons */
|
|
19
|
+
showNavigation?: boolean;
|
|
20
|
+
/** Custom labels for navigation buttons */
|
|
21
|
+
navigationLabels?: {
|
|
22
|
+
next?: string;
|
|
23
|
+
previous?: string;
|
|
24
|
+
finish?: string;
|
|
25
|
+
};
|
|
26
|
+
/** Whether steps can be clicked to navigate */
|
|
27
|
+
allowStepClick?: boolean;
|
|
28
|
+
/** Validation function for step navigation */
|
|
29
|
+
validateStep?: (
|
|
30
|
+
fromStep: number,
|
|
31
|
+
toStep: number
|
|
32
|
+
) => boolean | Promise<boolean>;
|
|
33
|
+
/** Loading state */
|
|
34
|
+
loading?: boolean;
|
|
35
|
+
/** Callback when all steps are completed */
|
|
36
|
+
onComplete?: () => void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function Steps({
|
|
40
|
+
config,
|
|
41
|
+
initialStep = 0,
|
|
42
|
+
controlled = false,
|
|
43
|
+
activeStep: controlledActiveStep,
|
|
44
|
+
onStepChange,
|
|
45
|
+
showNavigation = false,
|
|
46
|
+
navigationLabels = {
|
|
47
|
+
next: "Next",
|
|
48
|
+
previous: "Previous",
|
|
49
|
+
finish: "Finish",
|
|
50
|
+
},
|
|
51
|
+
allowStepClick = true,
|
|
52
|
+
validateStep,
|
|
53
|
+
loading = false,
|
|
54
|
+
onComplete,
|
|
55
|
+
variant = "light",
|
|
56
|
+
skin,
|
|
57
|
+
simple = false,
|
|
58
|
+
showLabels = true,
|
|
59
|
+
showEdges = true,
|
|
60
|
+
showTooltip = true,
|
|
61
|
+
childProps,
|
|
62
|
+
...rest
|
|
63
|
+
}: StepsProps) {
|
|
64
|
+
const {
|
|
65
|
+
activeStep: internalActiveStep,
|
|
66
|
+
nextStep,
|
|
67
|
+
prevStep,
|
|
68
|
+
goToStep,
|
|
69
|
+
isFirst,
|
|
70
|
+
isLast,
|
|
71
|
+
} = useStepNavigation(config.length, initialStep);
|
|
72
|
+
|
|
73
|
+
const activeStep = controlled
|
|
74
|
+
? controlledActiveStep ?? 0
|
|
75
|
+
: internalActiveStep;
|
|
76
|
+
|
|
77
|
+
const handleStepChange = async (newStep: number) => {
|
|
78
|
+
if (validateStep) {
|
|
79
|
+
const isValid = await validateStep(activeStep, newStep);
|
|
80
|
+
if (!isValid) return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (controlled) {
|
|
84
|
+
onStepChange?.(newStep);
|
|
85
|
+
} else {
|
|
86
|
+
goToStep(newStep);
|
|
87
|
+
onStepChange?.(newStep);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const handleNext = async () => {
|
|
92
|
+
if (isLast && onComplete) {
|
|
93
|
+
onComplete();
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (controlled) {
|
|
98
|
+
onStepChange?.(activeStep + 1);
|
|
99
|
+
} else {
|
|
100
|
+
const newStep = activeStep + 1;
|
|
101
|
+
if (validateStep) {
|
|
102
|
+
const isValid = await validateStep(activeStep, newStep);
|
|
103
|
+
if (!isValid) return;
|
|
104
|
+
}
|
|
105
|
+
nextStep();
|
|
106
|
+
onStepChange?.(newStep);
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const handlePrevious = () => {
|
|
111
|
+
if (controlled) {
|
|
112
|
+
onStepChange?.(activeStep - 1);
|
|
113
|
+
} else {
|
|
114
|
+
prevStep();
|
|
115
|
+
onStepChange?.(activeStep - 1);
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const handleStepClick = allowStepClick ? handleStepChange : undefined;
|
|
120
|
+
|
|
121
|
+
return (
|
|
122
|
+
<Box display="flex" flexDirection="column" gap="large">
|
|
123
|
+
{/* Step Progress */}
|
|
124
|
+
<StepProgress
|
|
125
|
+
config={config}
|
|
126
|
+
active={activeStep}
|
|
127
|
+
setActive={handleStepClick}
|
|
128
|
+
variant={variant}
|
|
129
|
+
skin={skin}
|
|
130
|
+
simple={simple}
|
|
131
|
+
showLabels={showLabels}
|
|
132
|
+
showEdges={showEdges}
|
|
133
|
+
showTooltip={showTooltip}
|
|
134
|
+
childProps={childProps}
|
|
135
|
+
{...rest}
|
|
136
|
+
/>
|
|
137
|
+
|
|
138
|
+
{/* Navigation Buttons */}
|
|
139
|
+
{showNavigation && (
|
|
140
|
+
<Box
|
|
141
|
+
display="flex"
|
|
142
|
+
justifyContent="space-between"
|
|
143
|
+
alignItems="center"
|
|
144
|
+
gap="medium"
|
|
145
|
+
>
|
|
146
|
+
<Box>
|
|
147
|
+
{!isFirst && (
|
|
148
|
+
<Box
|
|
149
|
+
as="button"
|
|
150
|
+
skin="default"
|
|
151
|
+
px="medium"
|
|
152
|
+
py="small"
|
|
153
|
+
shape="pill"
|
|
154
|
+
cursor="pointer"
|
|
155
|
+
disabled={loading}
|
|
156
|
+
onClick={handlePrevious}
|
|
157
|
+
>
|
|
158
|
+
<Text fontSize="small" color="inherit">
|
|
159
|
+
{navigationLabels.previous}
|
|
160
|
+
</Text>
|
|
161
|
+
</Box>
|
|
162
|
+
)}
|
|
163
|
+
</Box>
|
|
164
|
+
|
|
165
|
+
<Box display="flex" gap="small">
|
|
166
|
+
{/* Step Counter */}
|
|
167
|
+
<Text fontSize="small" color="secondary">
|
|
168
|
+
{activeStep + 1} of {config.length}
|
|
169
|
+
</Text>
|
|
170
|
+
</Box>
|
|
171
|
+
|
|
172
|
+
<Box>
|
|
173
|
+
<Box
|
|
174
|
+
as="button"
|
|
175
|
+
px="medium"
|
|
176
|
+
py="small"
|
|
177
|
+
shape="pill"
|
|
178
|
+
cursor="pointer"
|
|
179
|
+
disabled={loading}
|
|
180
|
+
onClick={handleNext}
|
|
181
|
+
>
|
|
182
|
+
<Text fontSize="small" color="inherit">
|
|
183
|
+
{loading
|
|
184
|
+
? "Loading..."
|
|
185
|
+
: isLast
|
|
186
|
+
? navigationLabels.finish
|
|
187
|
+
: navigationLabels.next}
|
|
188
|
+
</Text>
|
|
189
|
+
</Box>
|
|
190
|
+
</Box>
|
|
191
|
+
</Box>
|
|
192
|
+
)}
|
|
193
|
+
</Box>
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export default Steps;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import StepProgress from './StepProgress';
|
|
5
|
+
import { BaseStepProps } from './types';
|
|
6
|
+
|
|
7
|
+
interface StepsNavProps extends BaseStepProps {
|
|
8
|
+
/** Additional margin top spacing */
|
|
9
|
+
mt?: string | number | object;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function StepsNav({
|
|
13
|
+
config,
|
|
14
|
+
active,
|
|
15
|
+
mt = "medium",
|
|
16
|
+
showLabels = true,
|
|
17
|
+
...rest
|
|
18
|
+
}: StepsNavProps) {
|
|
19
|
+
return (
|
|
20
|
+
<StepProgress
|
|
21
|
+
showLabels={showLabels}
|
|
22
|
+
config={config}
|
|
23
|
+
active={active}
|
|
24
|
+
mt={mt}
|
|
25
|
+
{...rest}
|
|
26
|
+
/>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export default StepsNav;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { Box } from "@/design-system/elements";
|
|
5
|
+
|
|
6
|
+
import Tooltip from "../Tooltip";
|
|
7
|
+
import { motion } from "motion/react";
|
|
8
|
+
|
|
9
|
+
interface StepsTrackerProps {
|
|
10
|
+
/** Index of the completed step (all steps up to this index are completed) */
|
|
11
|
+
completed?: number;
|
|
12
|
+
config: { label: string }[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface TrackSegmentProps {
|
|
16
|
+
isCompleted: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface TrackStepProps {
|
|
20
|
+
isCompleted: boolean;
|
|
21
|
+
isActive?: boolean;
|
|
22
|
+
item: { label: string };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function TrackStep({ isCompleted, isActive, item }: TrackStepProps) {
|
|
26
|
+
return (
|
|
27
|
+
<Tooltip content={item.label} placement="bottom" zIndex={100003}>
|
|
28
|
+
<Box
|
|
29
|
+
as={motion.div}
|
|
30
|
+
flex="none"
|
|
31
|
+
bg={isCompleted ? "accent" : "tertiary"}
|
|
32
|
+
shape="circle"
|
|
33
|
+
size="1rem"
|
|
34
|
+
display="flex"
|
|
35
|
+
alignItems="center"
|
|
36
|
+
justifyContent="center"
|
|
37
|
+
initial={{ scale: 0.85 }}
|
|
38
|
+
animate={{ scale: isActive ? 1.125 : 0.85 }}
|
|
39
|
+
exit={{ scale: 0.85 }}
|
|
40
|
+
whileHover={{ scale: 1.125 }}
|
|
41
|
+
//style={{ pointerEvents: "none" }}
|
|
42
|
+
/>
|
|
43
|
+
</Tooltip>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function TrackSegment({ isCompleted }: TrackSegmentProps) {
|
|
48
|
+
return (
|
|
49
|
+
<Box
|
|
50
|
+
flex="auto"
|
|
51
|
+
height="0.25rem"
|
|
52
|
+
bg={isCompleted ? "accent" : "tertiary"}
|
|
53
|
+
shape="pill"
|
|
54
|
+
alignSelf="center"
|
|
55
|
+
/>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function StepsTracker({ config, completed = 0, ...rest }: StepsTrackerProps) {
|
|
60
|
+
return (
|
|
61
|
+
<Box display="flex" alignItems="flex-start" gap="xs" width="100%" {...rest}>
|
|
62
|
+
{config.map((item, i) => (
|
|
63
|
+
<React.Fragment key={i}>
|
|
64
|
+
{/* Step Indicator */}
|
|
65
|
+
<TrackStep
|
|
66
|
+
item={item}
|
|
67
|
+
isCompleted={completed >= i}
|
|
68
|
+
isActive={completed === i}
|
|
69
|
+
/>
|
|
70
|
+
{/* Track Segment (if not the last step) */}
|
|
71
|
+
{i < config.length - 1 && (
|
|
72
|
+
<TrackSegment isCompleted={completed >= i + 1} />
|
|
73
|
+
)}
|
|
74
|
+
</React.Fragment>
|
|
75
|
+
))}
|
|
76
|
+
</Box>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export default StepsTracker;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Custom hook for managing hover state on an element
|
|
7
|
+
* @returns A tuple with [ref, isHovered] where ref should be attached to the element
|
|
8
|
+
*/
|
|
9
|
+
export function useHover(): [React.RefObject<HTMLElement | null>, boolean] {
|
|
10
|
+
const [isHovered, setIsHovered] = useState(false);
|
|
11
|
+
const ref = useRef<HTMLElement | null>(null);
|
|
12
|
+
|
|
13
|
+
const handleMouseEnter = useCallback(() => setIsHovered(true), []);
|
|
14
|
+
const handleMouseLeave = useCallback(() => setIsHovered(false), []);
|
|
15
|
+
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
const node = ref.current;
|
|
18
|
+
if (node) {
|
|
19
|
+
node.addEventListener("mouseenter", handleMouseEnter);
|
|
20
|
+
node.addEventListener("mouseleave", handleMouseLeave);
|
|
21
|
+
|
|
22
|
+
return () => {
|
|
23
|
+
node.removeEventListener("mouseenter", handleMouseEnter);
|
|
24
|
+
node.removeEventListener("mouseleave", handleMouseLeave);
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
}, [handleMouseEnter, handleMouseLeave]);
|
|
28
|
+
|
|
29
|
+
return [ref, isHovered];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Custom hook for managing step navigation
|
|
34
|
+
* @param totalSteps Total number of steps
|
|
35
|
+
* @param initialStep Initial active step (0-based)
|
|
36
|
+
*/
|
|
37
|
+
export function useStepNavigation(totalSteps: number, initialStep = 0) {
|
|
38
|
+
const [activeStep, setActiveStep] = useState(initialStep);
|
|
39
|
+
|
|
40
|
+
const nextStep = useCallback(() => {
|
|
41
|
+
setActiveStep((prev) => Math.min(prev + 1, totalSteps - 1));
|
|
42
|
+
}, [totalSteps]);
|
|
43
|
+
|
|
44
|
+
const prevStep = useCallback(() => {
|
|
45
|
+
setActiveStep((prev) => Math.max(prev - 1, 0));
|
|
46
|
+
}, []);
|
|
47
|
+
|
|
48
|
+
const goToStep = useCallback(
|
|
49
|
+
(step: number) => {
|
|
50
|
+
if (step >= 0 && step < totalSteps) {
|
|
51
|
+
setActiveStep(step);
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
[totalSteps]
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const isFirst = activeStep === 0;
|
|
58
|
+
const isLast = activeStep === totalSteps - 1;
|
|
59
|
+
const progress = totalSteps > 0 ? ((activeStep + 1) / totalSteps) * 100 : 0;
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
activeStep,
|
|
63
|
+
nextStep,
|
|
64
|
+
prevStep,
|
|
65
|
+
goToStep,
|
|
66
|
+
isFirst,
|
|
67
|
+
isLast,
|
|
68
|
+
progress,
|
|
69
|
+
setActiveStep,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
export { default as StepProgress } from "./StepProgress";
|
|
4
|
+
export { default as StepIndicator } from "./StepIndicator";
|
|
5
|
+
export { default as StepsNav } from "./StepsNav";
|
|
6
|
+
export { default as Steps } from "./Steps";
|
|
7
|
+
export { useHover, useStepNavigation } from "./hooks";
|
|
8
|
+
export type {
|
|
9
|
+
StepConfig,
|
|
10
|
+
StepThemeConfig,
|
|
11
|
+
StepVariant,
|
|
12
|
+
BaseStepProps,
|
|
13
|
+
} from "./types";
|
|
14
|
+
|
|
15
|
+
// Default export is the comprehensive Steps component
|
|
16
|
+
export { default } from "./Steps";
|