From e8f30eb97fb265ae9fd7762095893003658dfb69 Mon Sep 17 00:00:00 2001
From: Mateusz Lazaru
Date: Thu, 9 Nov 2023 15:11:19 +0100
Subject: [PATCH 01/19] stepperPage - added demo
---
src/demo/pages.tsx | 6 ++++++
.../pages/components/stepper/StepperPage.tsx | 21 +++++++++++++++++++
2 files changed, 27 insertions(+)
create mode 100644 src/demo/pages/components/stepper/StepperPage.tsx
diff --git a/src/demo/pages.tsx b/src/demo/pages.tsx
index ed11b84..13f469b 100644
--- a/src/demo/pages.tsx
+++ b/src/demo/pages.tsx
@@ -58,6 +58,7 @@ import ToastsPage from "./pages/components/toasts/ToastsPage";
import SelectPage from "./pages/forms/select/SelectPage";
import CarouselPage from "./pages/components/carousel/CarouselPage";
import VideoCarouselPage from "./pages/components/video-carousel/VideoCarouselPage";
+import StepperPage from "./pages/components/stepper/StepperPage";
//examples pages
import ButtonExamples from "./pages/components/buttons/exampleList";
@@ -197,6 +198,11 @@ const componentsPages: Pages[] = [
path: "/components/video-carousel",
element: ,
},
+ {
+ name: "stepper",
+ path: "/components/stepper",
+ element: ,
+ },
];
const contentStylesPages: Pages[] = [
diff --git a/src/demo/pages/components/stepper/StepperPage.tsx b/src/demo/pages/components/stepper/StepperPage.tsx
new file mode 100644
index 0000000..d2dc312
--- /dev/null
+++ b/src/demo/pages/components/stepper/StepperPage.tsx
@@ -0,0 +1,21 @@
+import React from "react";
+import TEStepper from "../../../../lib/components/Stepper/Stepper";
+import TEStepperStep from "../../../../lib/components/Stepper/StepperStep/StepperStep";
+
+const StepperPage = () => {
+ return (
+
+
+ Step 1 content
+
+
+ Step 2 content
+
+
+ Step 3 content
+
+
+ );
+};
+
+export default StepperPage;
From 52284c23fb707430da6b8dabd99704b6ee30d117 Mon Sep 17 00:00:00 2001
From: Mateusz Lazaru
Date: Fri, 17 Nov 2023 15:36:30 +0100
Subject: [PATCH 02/19] new component - stepper - created componend with basic
funcionalities
---
.../pages/components/stepper/StepperPage.tsx | 83 ++++++++++++++++---
src/lib/components/Stepper/Stepper.tsx | 69 +++++++++++++++
src/lib/components/Stepper/StepperContext.ts | 19 +++++
.../Stepper/StepperStep/StepperStep.tsx | 81 ++++++++++++++++++
.../Stepper/StepperStep/stepperStepTheme.ts | 37 +++++++++
.../components/Stepper/StepperStep/types.ts | 26 ++++++
.../Stepper/hooks/useHeadIconClasses.ts | 29 +++++++
.../hooks/useHorizontalStepperHeight.ts | 41 +++++++++
.../Stepper/hooks/useIsStepCompleted.ts | 23 +++++
src/lib/components/Stepper/stepperTheme.ts | 8 ++
src/lib/components/Stepper/types.ts | 23 +++++
src/lib/components/Stepper/utils/utils.ts | 10 +++
src/lib/hooks/useActiveValue.ts | 13 +++
13 files changed, 450 insertions(+), 12 deletions(-)
create mode 100644 src/lib/components/Stepper/Stepper.tsx
create mode 100644 src/lib/components/Stepper/StepperContext.ts
create mode 100644 src/lib/components/Stepper/StepperStep/StepperStep.tsx
create mode 100644 src/lib/components/Stepper/StepperStep/stepperStepTheme.ts
create mode 100644 src/lib/components/Stepper/StepperStep/types.ts
create mode 100644 src/lib/components/Stepper/hooks/useHeadIconClasses.ts
create mode 100644 src/lib/components/Stepper/hooks/useHorizontalStepperHeight.ts
create mode 100644 src/lib/components/Stepper/hooks/useIsStepCompleted.ts
create mode 100644 src/lib/components/Stepper/stepperTheme.ts
create mode 100644 src/lib/components/Stepper/types.ts
create mode 100644 src/lib/components/Stepper/utils/utils.ts
create mode 100644 src/lib/hooks/useActiveValue.ts
diff --git a/src/demo/pages/components/stepper/StepperPage.tsx b/src/demo/pages/components/stepper/StepperPage.tsx
index d2dc312..26a0564 100644
--- a/src/demo/pages/components/stepper/StepperPage.tsx
+++ b/src/demo/pages/components/stepper/StepperPage.tsx
@@ -1,20 +1,79 @@
-import React from "react";
+import React, { useState } from "react";
import TEStepper from "../../../../lib/components/Stepper/Stepper";
import TEStepperStep from "../../../../lib/components/Stepper/StepperStep/StepperStep";
const StepperPage = () => {
+ const [activeStep, setActiveStep] = useState(1);
return (
-
-
- Step 1 content
-
-
- Step 2 content
-
-
- Step 3 content
-
-
+
+
+
+ Basic example
+
+
+
+
+ Step 1 content
+ XXXXs
+ Step 1 content
+ XXXXs
+ Step 1 content
+ XXXXs
+ Step 1 content
+ XXXXs
+ Step 1 content
+ XXXXs
+
+
+ Step 2 content
+
+
+ Step 3 content
+
+
+
+
+
+
+
+ Controlled active step
+
+
+
+
{
+ console.log("onChange: ", stepId);
+ setActiveStep(stepId);
+ }}
+ >
+
+ Step 1 content
+ XXXXs
+ Step 1 content
+ XXXXs
+ Step 1 content
+ XXXXs
+ Step 1 content
+ XXXXs
+ Step 1 content
+ XXXXs
+
+
+ Step 2 content
+
+
+ Step 3 content
+
+
+
+
+
);
};
diff --git a/src/lib/components/Stepper/Stepper.tsx b/src/lib/components/Stepper/Stepper.tsx
new file mode 100644
index 0000000..aebd251
--- /dev/null
+++ b/src/lib/components/Stepper/Stepper.tsx
@@ -0,0 +1,69 @@
+import clsx from "clsx";
+import React, { useEffect, useRef, useState, useMemo } from "react";
+import StepperContext from "./StepperContext";
+import { StepperStepProps } from "./StepperStep/types";
+import StepperTheme from "./stepperTheme";
+import useActiveValue from "../../hooks/useActiveValue";
+import type { StepperProps } from "./types";
+// import MDBStepperMobileHead from "./StepperMobileHead/StepperMobileHead";
+// import MDBStepperMobileFooter from "./StepperMobileFooter/StepperMobileFooter";
+
+const TEStepper: React.FC = ({
+ theme: customTheme,
+ className,
+ defaultStep = 1,
+ activeStep: activeStepProp,
+ children,
+ onChange,
+}) => {
+ const theme = {
+ ...StepperTheme,
+ ...customTheme,
+ };
+ const classes = clsx(theme.stepper, className);
+ const [activeStepState, setActiveStepState] = useState(defaultStep);
+ const activeStep = useActiveValue(activeStepProp, activeStepState);
+ const stepperRef = useRef(null);
+ const [stepperHeight, setStepperHeight] = useState("0");
+
+ const childrenArray = useMemo(() => {
+ return React.Children.toArray(
+ children
+ ) as React.ReactElement[];
+ }, [children]);
+
+ const onChangeHandler = (id: number) => {
+ onChange?.(id);
+ setActiveStepState(id);
+ };
+ return (
+ <>
+
+
+ {childrenArray.map((ChildComponent, index: number) => {
+ return React.cloneElement(ChildComponent, {
+ itemId: index + 1,
+ activeStep,
+ key: "stepper-step-" + index,
+ onChange: onChangeHandler,
+ });
+ })}
+
+
+ >
+ );
+};
+
+export default TEStepper;
diff --git a/src/lib/components/Stepper/StepperContext.ts b/src/lib/components/Stepper/StepperContext.ts
new file mode 100644
index 0000000..8ebddf4
--- /dev/null
+++ b/src/lib/components/Stepper/StepperContext.ts
@@ -0,0 +1,19 @@
+import { createContext } from "react";
+
+interface StepperContextProps {
+ activeStep: number;
+ onChange?: (id: number) => void;
+ stepperRef: React.RefObject | null;
+ stepperHeight: string;
+ setStepperHeight: (height: string) => void;
+}
+
+const StepperContext = createContext({
+ activeStep: 1,
+ onChange: () => {},
+ stepperRef: null,
+ stepperHeight: "0",
+ setStepperHeight: () => {},
+});
+
+export default StepperContext;
diff --git a/src/lib/components/Stepper/StepperStep/StepperStep.tsx b/src/lib/components/Stepper/StepperStep/StepperStep.tsx
new file mode 100644
index 0000000..a6409af
--- /dev/null
+++ b/src/lib/components/Stepper/StepperStep/StepperStep.tsx
@@ -0,0 +1,81 @@
+import React, { useMemo, useRef, useContext } from "react";
+import StepperStepTheme from "./stepperStepTheme";
+import StepperContext from "../StepperContext";
+import { StepperStepProps } from "./types";
+import clx from "clsx";
+import useHeadIconClasses from "../hooks/useHeadIconClasses";
+import useIsStepCompleted from "../hooks/useIsStepCompleted";
+import useStepperHeight from "../hooks/useHorizontalStepperHeight";
+import { getTranslateDirection } from "../utils/utils";
+
+const TEStepperStep: React.FC = ({
+ theme: customTheme,
+ className,
+ itemId = 1,
+ headIcon = "",
+ headText = "",
+ children,
+}) => {
+ const headRef = useRef(null);
+ const contentRef = useRef(null);
+ const { activeStep, onChange } = useContext(StepperContext);
+
+ const animationDirection = useMemo(() => {
+ return getTranslateDirection(activeStep, itemId);
+ }, [activeStep, itemId]);
+
+ const isActive = useMemo(() => {
+ return activeStep === itemId;
+ }, [activeStep, itemId]);
+ const isCompleted = useIsStepCompleted(activeStep, isActive, itemId);
+ const theme = {
+ ...StepperStepTheme,
+ ...customTheme,
+ };
+
+ const stepperStepClasses = clx(theme.stepperStep, className);
+ const headIconClasses = useHeadIconClasses(
+ isActive,
+ isCompleted,
+ false,
+ theme
+ );
+
+ useStepperHeight(isActive, headRef, contentRef, false, children);
+ const dynamicAnimationDirection: string = `stepperContentTranslated${animationDirection}`;
+
+ return (
+
+ onChange?.(itemId)}
+ ref={headRef}
+ >
+ {headIcon}
+
+ {headText}
+
+
+ {
+ console.log(e);
+ }}
+ ref={contentRef}
+ >
+ {children}
+
+
+ );
+};
+
+export default TEStepperStep;
diff --git a/src/lib/components/Stepper/StepperStep/stepperStepTheme.ts b/src/lib/components/Stepper/StepperStep/stepperStepTheme.ts
new file mode 100644
index 0000000..0dbe14e
--- /dev/null
+++ b/src/lib/components/Stepper/StepperStep/stepperStepTheme.ts
@@ -0,0 +1,37 @@
+interface StepperStepThemeProps {
+ stepperStep: string;
+ stepperHead: string;
+ stepperHeadIcon: string;
+ stepperHeadIconActive: string;
+ stepperHeadIconCompleted: string;
+ stepperHeadText: string;
+ stepperHeadTextActive: string;
+ stepperContentTranslatedLeft: string;
+ stepperContentTranslatedRight: string;
+ stepperContentActive: string;
+}
+
+const StepperStepTheme = {
+ stepperStep: "w-[4.5rem] flex-auto",
+ stepperHead:
+ "flex cursor-pointer items-center leading-[1.3rem] no-underline before:mr-2 before:h-px before:w-full before:flex-1 before:bg-[#e0e0e0] before:content-[''] after:ml-2 after:h-px after:w-full after:flex-1 after:bg-[#e0e0e0] after:content-[''] hover:bg-[#f9f9f9] focus:outline-none dark:before:bg-neutral-600 dark:after:bg-neutral-600 dark:hover:bg-[#3b3b3b]",
+ stepperHeadIcon:
+ "my-6 mr-2 flex h-[1.938rem] w-[1.938rem] items-center justify-center rounded-full bg-[#ebedef] text-sm font-medium text-[#40464f]",
+ stepperHeadIconActive:
+ "my-6 mr-2 flex h-[1.938rem] w-[1.938rem] items-center justify-center rounded-full bg-[#ebedef] text-sm font-medium text-[#40464f] !bg-primary-100 !text-primary-700",
+ stepperHeadIconCompleted:
+ "my-6 mr-2 flex h-[1.938rem] w-[1.938rem] items-center justify-center rounded-full bg-[#ebedef] text-sm font-medium text-[#40464f] !bg-success-100 !text-success-700",
+ stepperHeadText:
+ "text-neutral-500 after:flex after:text-[0.8rem] after:content-[data-content] dark:text-neutral-300",
+ stepperHeadTextActive:
+ "font-medium after:flex after:text-[0.8rem] after:content-[data-content]",
+ stepperContentTranslatedLeft:
+ "absolute left-0 w-full p-4 transition-all duration-500 ease-in-out translate-0 -translate-x-[150%]",
+ stepperContentTranslatedRight:
+ "absolute left-0 w-full p-4 transition-all duration-500 ease-in-out translate-0 translate-x-[150%]",
+ stepperContentActive:
+ "absolute left-0 w-full p-4 transition-all duration-500 ease-in-out translate-0",
+};
+
+export default StepperStepTheme;
+export type { StepperStepThemeProps };
diff --git a/src/lib/components/Stepper/StepperStep/types.ts b/src/lib/components/Stepper/StepperStep/types.ts
new file mode 100644
index 0000000..37ccedb
--- /dev/null
+++ b/src/lib/components/Stepper/StepperStep/types.ts
@@ -0,0 +1,26 @@
+import React from "react";
+import { BaseComponent } from "../../../types/baseComponent";
+
+interface StepperThemeProps {
+ stepperStep: string;
+ stepperHead: string;
+ stepperHeadActive: string;
+ stepperHeadIcon: string;
+ stepperHeadText: string;
+ stepperHeadTextActive: string;
+ stepperContent: string;
+ stepperContentActive: string;
+}
+
+interface StepperStepProps extends BaseComponent {
+ theme?: StepperThemeProps;
+ headClassName?: string;
+ contentClassName?: string;
+ itemId?: number;
+ headIcon?: React.ReactNode;
+ headText?: React.ReactNode;
+ onComplete?: (id: number) => void;
+ activeStep?: number;
+}
+
+export type { StepperStepProps };
diff --git a/src/lib/components/Stepper/hooks/useHeadIconClasses.ts b/src/lib/components/Stepper/hooks/useHeadIconClasses.ts
new file mode 100644
index 0000000..d941305
--- /dev/null
+++ b/src/lib/components/Stepper/hooks/useHeadIconClasses.ts
@@ -0,0 +1,29 @@
+const useHeadIconClasses = (
+ isActive: boolean,
+ isCompleted: boolean,
+ isInvalid: boolean,
+ theme: any
+): string => {
+ const {
+ stepperHeadIcon,
+ stepperHeadIconActive,
+ stepperHeadIconCompleted,
+ stepperHeadIconInvalid,
+ } = theme;
+
+ if (isActive) {
+ return stepperHeadIconActive;
+ }
+
+ if (isCompleted) {
+ return stepperHeadIconCompleted;
+ }
+
+ if (isInvalid) {
+ return stepperHeadIconInvalid;
+ }
+
+ return stepperHeadIcon;
+};
+
+export default useHeadIconClasses;
diff --git a/src/lib/components/Stepper/hooks/useHorizontalStepperHeight.ts b/src/lib/components/Stepper/hooks/useHorizontalStepperHeight.ts
new file mode 100644
index 0000000..3c528d0
--- /dev/null
+++ b/src/lib/components/Stepper/hooks/useHorizontalStepperHeight.ts
@@ -0,0 +1,41 @@
+import { useEffect, useContext, RefObject } from "react";
+import StepperContext from "../StepperContext";
+
+const useStepperHeight = (
+ isActive: boolean,
+ headRef: RefObject,
+ stepRef: RefObject,
+ vertical: boolean,
+ children: React.ReactNode | React.ReactNode[]
+) => {
+ if (vertical) return;
+ const { setStepperHeight } = useContext(StepperContext);
+
+ const handleResize = () => {
+ if (!isActive) {
+ return;
+ }
+ if (stepRef.current && headRef.current) {
+ setStepperHeight(
+ String(stepRef.current.scrollHeight + headRef.current.scrollHeight)
+ );
+ }
+ };
+ // if (vertical) {
+ // return setStepperHeight("unset");
+ // }
+
+ useEffect(() => {
+ handleResize();
+ }, [isActive, children]);
+
+ useEffect(() => {
+ if (!isActive) return;
+ window.addEventListener("resize", handleResize);
+ return () => {
+ window.removeEventListener("resize", handleResize);
+ };
+ }, [isActive, children]);
+};
+
+export default useStepperHeight;
diff --git a/src/lib/components/Stepper/hooks/useIsStepCompleted.ts b/src/lib/components/Stepper/hooks/useIsStepCompleted.ts
new file mode 100644
index 0000000..7af5ffe
--- /dev/null
+++ b/src/lib/components/Stepper/hooks/useIsStepCompleted.ts
@@ -0,0 +1,23 @@
+import { useState, useEffect, useRef } from "react";
+
+const useIsStepCompleted = (
+ activeStep: number,
+ isActive: boolean,
+ itemId: number
+) => {
+ const [isCompleted, setIsCompleted] = useState(false);
+ const wasActive = useRef(isActive || false);
+
+ useEffect(() => {
+ if (isActive) {
+ wasActive.current = isActive;
+ }
+ if (wasActive.current && activeStep >= itemId) {
+ setIsCompleted(true);
+ }
+ }, [isActive]);
+
+ return isCompleted;
+};
+
+export default useIsStepCompleted;
diff --git a/src/lib/components/Stepper/stepperTheme.ts b/src/lib/components/Stepper/stepperTheme.ts
new file mode 100644
index 0000000..4a92a76
--- /dev/null
+++ b/src/lib/components/Stepper/stepperTheme.ts
@@ -0,0 +1,8 @@
+const StepperTheme = {
+ stepper:
+ "relative m-0 flex list-none justify-between overflow-hidden p-0 transition-[height] duration-200 ease-in-out",
+ stepperVertical:
+ "relative m-0 w-full list-none overflow-hidden p-0 transition-[height] duration-200 ease-in-out",
+};
+
+export default StepperTheme;
diff --git a/src/lib/components/Stepper/types.ts b/src/lib/components/Stepper/types.ts
new file mode 100644
index 0000000..077232e
--- /dev/null
+++ b/src/lib/components/Stepper/types.ts
@@ -0,0 +1,23 @@
+import React from "react";
+import { BaseComponent } from "../../types/baseComponent";
+import { StepperStepProps } from "./StepperStep/types";
+
+interface StepperThemeProps {
+ stepper: string;
+}
+
+type StepperProps = Omit & {
+ stepperTheme?: StepperThemeProps;
+ disableHeadSteps?: boolean;
+ defaultStep?: number;
+ activeStep?: number;
+ linear?: boolean;
+ noEditable?: boolean;
+ type?: "horizontal" | "vertical";
+ onChange?: (id: number) => void;
+ children:
+ | React.ReactElement[]
+ | React.ReactElement;
+};
+
+export type { StepperProps };
diff --git a/src/lib/components/Stepper/utils/utils.ts b/src/lib/components/Stepper/utils/utils.ts
new file mode 100644
index 0000000..3ce0979
--- /dev/null
+++ b/src/lib/components/Stepper/utils/utils.ts
@@ -0,0 +1,10 @@
+const getTranslateDirection = (activeStep: number, step: number) => {
+ if (activeStep > step) {
+ return "Left";
+ }
+ if (activeStep < step) {
+ return "Right";
+ }
+};
+
+export { getTranslateDirection };
diff --git a/src/lib/hooks/useActiveValue.ts b/src/lib/hooks/useActiveValue.ts
new file mode 100644
index 0000000..38f68f8
--- /dev/null
+++ b/src/lib/hooks/useActiveValue.ts
@@ -0,0 +1,13 @@
+import { useMemo } from "react";
+
+const useActiveValue = (propValue: any, stateValue: any) => {
+ return useMemo(() => {
+ if (propValue !== undefined) {
+ return propValue;
+ }
+
+ return stateValue;
+ }, [propValue, stateValue]);
+};
+
+export default useActiveValue;
From 1f8a2472cc20029ec7a0c0c0d2b24fd8b998daf5 Mon Sep 17 00:00:00 2001
From: Mateusz Lazaru
Date: Mon, 20 Nov 2023 22:01:43 +0100
Subject: [PATCH 03/19] stepper - added 'vertical' option, fixed invalid styles
in stepperStepTheme
---
.../pages/components/stepper/StepperPage.tsx | 218 ++++++++++++++++++
src/lib/components/Stepper/Stepper.tsx | 14 +-
src/lib/components/Stepper/StepperContext.ts | 4 +
.../Stepper/StepperStep/StepperStep.tsx | 67 ++++--
.../Stepper/StepperStep/stepperStepTheme.ts | 36 ++-
.../Stepper/hooks/useHeadClasses.ts | 26 +++
.../Stepper/hooks/useHeadIconClasses.ts | 26 ++-
.../hooks/useHorizontalStepperHeight.ts | 6 -
.../Stepper/hooks/useVerticalStepHeight.ts | 36 +++
9 files changed, 381 insertions(+), 52 deletions(-)
create mode 100644 src/lib/components/Stepper/hooks/useHeadClasses.ts
create mode 100644 src/lib/components/Stepper/hooks/useVerticalStepHeight.ts
diff --git a/src/demo/pages/components/stepper/StepperPage.tsx b/src/demo/pages/components/stepper/StepperPage.tsx
index 26a0564..44a1e0d 100644
--- a/src/demo/pages/components/stepper/StepperPage.tsx
+++ b/src/demo/pages/components/stepper/StepperPage.tsx
@@ -1,9 +1,20 @@
import React, { useState } from "react";
import TEStepper from "../../../../lib/components/Stepper/Stepper";
import TEStepperStep from "../../../../lib/components/Stepper/StepperStep/StepperStep";
+import { TECollapse } from "tw-elements-react";
const StepperPage = () => {
const [activeStep, setActiveStep] = useState(1);
+ const [activeElement, setActiveElement] = useState("");
+
+ const handleClick = (value: string) => {
+ if (value === activeElement) {
+ setActiveElement("");
+ } else {
+ setActiveElement(value);
+ }
+ };
+ const [addForm, setAddForm] = useState(false);
return (
+
setAddForm(!addForm)}>Add Form
Basic example
@@ -72,6 +84,212 @@ const StepperPage = () => {
+
+
+
+
+ Vertical stepper
+
+
+
+
+
+ Step 1 content
+ XXXXs
+ Step 1 content
+ XXXXs
+ Step 1 content
+ XXXXs
+ Step 1 content
+ XXXXs
+ Step 1 content
+ XXXXs
+
+
+
+
Step 2 content
+ {addForm && (
+ <>
+
+
+
+ handleClick("element1")}
+ aria-expanded="true"
+ aria-controls="collapseOne"
+ >
+ Accordion Item #1
+
+
+
+
+
+
+
+
+
+
+
+ This is the first item's accordion body.
+ {" "}
+ Lorem ipsum dolor sit amet, consectetur adipiscing
+ elit. Vestibulum eu rhoncus purus, vitae tincidunt
+ nibh. Vivamus elementum egestas ligula in varius.
+ Proin ac erat pretium, ultricies leo at, cursus
+ ante. Pellentesque at odio euismod, mattis urna ac,
+ accumsan metus. Nam nisi leo, malesuada vitae
+ pretium et, laoreet at lorem. Curabitur non
+ sollicitudin neque.
+
+
+
+
+
+
+ handleClick("element2")}
+ aria-expanded="true"
+ aria-controls="collapseOne"
+ >
+ Accordion Item #2
+
+
+
+
+
+
+
+
+
+
+ This is the second item's accordion body.
+ {" "}
+ Lorem ipsum dolor sit amet, consectetur adipiscing
+ elit. Vestibulum eu rhoncus purus, vitae tincidunt
+ nibh. Vivamus elementum egestas ligula in varius.
+ Proin ac erat pretium, ultricies leo at, cursus ante.
+ Pellentesque at odio euismod, mattis urna ac, accumsan
+ metus. Nam nisi leo, malesuada vitae pretium et,
+ laoreet at lorem. Curabitur non sollicitudin neque.
+
+
+
+
+
+ handleClick("element3")}
+ aria-expanded="true"
+ aria-controls="collapseOne"
+ >
+ Accordion Item #3
+
+
+
+
+
+
+
+
+
+
+ This is the third item's accordion body.
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing
+ elit. Vestibulum eu rhoncus purus, vitae tincidunt
+ nibh. Vivamus elementum egestas ligula in varius.
+ Proin ac erat pretium, ultricies leo at, cursus ante.
+ Pellentesque at odio euismod, mattis urna ac, accumsan
+ metus. Nam nisi leo, malesuada vitae pretium et,
+ laoreet at lorem. Curabitur non sollicitudin neque.
+
+
+
+ >
+ )}
+
+
+
+ Step 3 content
+
+
+
);
diff --git a/src/lib/components/Stepper/Stepper.tsx b/src/lib/components/Stepper/Stepper.tsx
index aebd251..b2386a8 100644
--- a/src/lib/components/Stepper/Stepper.tsx
+++ b/src/lib/components/Stepper/Stepper.tsx
@@ -1,12 +1,10 @@
import clsx from "clsx";
-import React, { useEffect, useRef, useState, useMemo } from "react";
+import React, { useRef, useState, useMemo } from "react";
import StepperContext from "./StepperContext";
import { StepperStepProps } from "./StepperStep/types";
import StepperTheme from "./stepperTheme";
import useActiveValue from "../../hooks/useActiveValue";
import type { StepperProps } from "./types";
-// import MDBStepperMobileHead from "./StepperMobileHead/StepperMobileHead";
-// import MDBStepperMobileFooter from "./StepperMobileFooter/StepperMobileFooter";
const TEStepper: React.FC = ({
theme: customTheme,
@@ -15,12 +13,16 @@ const TEStepper: React.FC = ({
activeStep: activeStepProp,
children,
onChange,
+ vertical,
}) => {
const theme = {
...StepperTheme,
...customTheme,
};
- const classes = clsx(theme.stepper, className);
+ const classes = clsx(
+ vertical ? theme.stepperVertical : theme.stepper,
+ className
+ );
const [activeStepState, setActiveStepState] = useState(defaultStep);
const activeStep = useActiveValue(activeStepProp, activeStepState);
const stepperRef = useRef(null);
@@ -45,12 +47,14 @@ const TEStepper: React.FC = ({
stepperRef,
stepperHeight,
setStepperHeight,
+ vertical,
+ stepsAmount: childrenArray.length,
}}
>
{childrenArray.map((ChildComponent, index: number) => {
return React.cloneElement(ChildComponent, {
diff --git a/src/lib/components/Stepper/StepperContext.ts b/src/lib/components/Stepper/StepperContext.ts
index 8ebddf4..7d112c4 100644
--- a/src/lib/components/Stepper/StepperContext.ts
+++ b/src/lib/components/Stepper/StepperContext.ts
@@ -6,6 +6,8 @@ interface StepperContextProps {
stepperRef: React.RefObject | null;
stepperHeight: string;
setStepperHeight: (height: string) => void;
+ vertical: boolean;
+ stepsAmount: number;
}
const StepperContext = createContext({
@@ -14,6 +16,8 @@ const StepperContext = createContext({
stepperRef: null,
stepperHeight: "0",
setStepperHeight: () => {},
+ vertical: false,
+ stepsAmount: 0,
});
export default StepperContext;
diff --git a/src/lib/components/Stepper/StepperStep/StepperStep.tsx b/src/lib/components/Stepper/StepperStep/StepperStep.tsx
index a6409af..0e4f290 100644
--- a/src/lib/components/Stepper/StepperStep/StepperStep.tsx
+++ b/src/lib/components/Stepper/StepperStep/StepperStep.tsx
@@ -7,6 +7,8 @@ import useHeadIconClasses from "../hooks/useHeadIconClasses";
import useIsStepCompleted from "../hooks/useIsStepCompleted";
import useStepperHeight from "../hooks/useHorizontalStepperHeight";
import { getTranslateDirection } from "../utils/utils";
+import useVerticalStepHeight from "../hooks/useVerticalStepHeight";
+import useHeadClasses from "../hooks/useHeadClasses";
const TEStepperStep: React.FC = ({
theme: customTheme,
@@ -15,10 +17,13 @@ const TEStepperStep: React.FC = ({
headIcon = "",
headText = "",
children,
+ invalid,
+ style,
}) => {
const headRef = useRef(null);
const contentRef = useRef(null);
- const { activeStep, onChange } = useContext(StepperContext);
+ const { activeStep, onChange, vertical, stepsAmount } =
+ useContext(StepperContext);
const animationDirection = useMemo(() => {
return getTranslateDirection(activeStep, itemId);
@@ -27,28 +32,57 @@ const TEStepperStep: React.FC = ({
const isActive = useMemo(() => {
return activeStep === itemId;
}, [activeStep, itemId]);
+
+ const isLastStep = useMemo(() => {
+ return itemId === stepsAmount;
+ }, [itemId, stepsAmount]);
+
const isCompleted = useIsStepCompleted(activeStep, isActive, itemId);
const theme = {
...StepperStepTheme,
...customTheme,
};
- const stepperStepClasses = clx(theme.stepperStep, className);
const headIconClasses = useHeadIconClasses(
isActive,
isCompleted,
- false,
- theme
+ invalid,
+ theme,
+ vertical
+ );
+ const stepperHeadClasses = useHeadClasses(theme, itemId);
+ const stepperStepClasses = clx(
+ vertical
+ ? isLastStep
+ ? theme.stepperLastStepVertical
+ : theme.stepperStepVertical
+ : theme.stepperStep,
+
+ className
+ );
+
+ const dynamicAnimationDirection: string = `stepperContentTranslate${animationDirection}`;
+ const stepperContentClasses = clx(
+ vertical ? theme.stepperVerticalContent : theme.stepperContent,
+ !vertical && theme[dynamicAnimationDirection as keyof typeof theme]
);
- useStepperHeight(isActive, headRef, contentRef, false, children);
- const dynamicAnimationDirection: string = `stepperContentTranslated${animationDirection}`;
+ const headClickHandler = () => {
+ itemId != activeStep && onChange?.(itemId);
+ };
+
+ useStepperHeight(isActive, headRef, contentRef, vertical, children);
+ const verticalStepHeight = useVerticalStepHeight(
+ isActive,
+ contentRef,
+ children
+ );
return (
onChange?.(itemId)}
+ className={stepperHeadClasses}
+ onClick={headClickHandler}
ref={headRef}
>
{headIcon}
@@ -61,18 +95,15 @@ const TEStepperStep: React.FC = ({
{
- console.log(e);
+ style={{
+ height: vertical ? verticalStepHeight : "auto",
+ visibility: isActive ? "visible" : "hidden",
}}
- ref={contentRef}
+ className={theme.stepperContentWrapper}
>
- {children}
+
+ {children}
+
);
diff --git a/src/lib/components/Stepper/StepperStep/stepperStepTheme.ts b/src/lib/components/Stepper/StepperStep/stepperStepTheme.ts
index 0dbe14e..c517186 100644
--- a/src/lib/components/Stepper/StepperStep/stepperStepTheme.ts
+++ b/src/lib/components/Stepper/StepperStep/stepperStepTheme.ts
@@ -13,24 +13,38 @@ interface StepperStepThemeProps {
const StepperStepTheme = {
stepperStep: "w-[4.5rem] flex-auto",
+ stepperStepVertical:
+ "relative h-fit after:absolute after:left-[2.45rem] after:top-[3.6rem] after:mt-px after:h-[calc(100%-2.45rem)] after:w-px after:bg-[#e0e0e0] dark:after:bg-neutral-600",
+ stepperLastStepVertical: "relative h-fit",
stepperHead:
- "flex cursor-pointer items-center leading-[1.3rem] no-underline before:mr-2 before:h-px before:w-full before:flex-1 before:bg-[#e0e0e0] before:content-[''] after:ml-2 after:h-px after:w-full after:flex-1 after:bg-[#e0e0e0] after:content-[''] hover:bg-[#f9f9f9] focus:outline-none dark:before:bg-neutral-600 dark:after:bg-neutral-600 dark:hover:bg-[#3b3b3b]",
+ "flex cursor-pointer items-center leading-[1.3rem] no-underline hover:bg-[#f9f9f9] focus:outline-none dark:hover:bg-[#3b3b3b]",
+ stepperHeadBeforeLineHorizontal:
+ "before:mr-2 before:h-px before:w-full before:flex-1 before:bg-[#e0e0e0] dark:before:bg-neutral-600",
+ stepperHeadAfterLineHorizontal:
+ "after:ml-2 after:h-px after:w-full after:flex-1 after:bg-[#e0e0e0] dark:after:bg-neutral-600",
+ stepperHeadAfterLineVertical:
+ "after:absolute after:left-[2.45rem] after:top-[3.6rem] after:mt-px after:h-[calc(100%-2.45rem)] after:w-px after:bg-[#e0e0e0] after:content-[''] dark:after:bg-neutral-600",
+ stepperHeadVertical:
+ "flex cursor-pointer items-center p-6 leading-[1.3rem] no-underline hover:bg-[#f9f9f9] focus:outline-none dark:hover:bg-[#3b3b3b]",
stepperHeadIcon:
"my-6 mr-2 flex h-[1.938rem] w-[1.938rem] items-center justify-center rounded-full bg-[#ebedef] text-sm font-medium text-[#40464f]",
- stepperHeadIconActive:
- "my-6 mr-2 flex h-[1.938rem] w-[1.938rem] items-center justify-center rounded-full bg-[#ebedef] text-sm font-medium text-[#40464f] !bg-primary-100 !text-primary-700",
- stepperHeadIconCompleted:
- "my-6 mr-2 flex h-[1.938rem] w-[1.938rem] items-center justify-center rounded-full bg-[#ebedef] text-sm font-medium text-[#40464f] !bg-success-100 !text-success-700",
+ stepperHeadIconVertical:
+ "mr-3 flex h-[1.938rem] w-[1.938rem] items-center justify-center rounded-full bg-[#ebedef] text-sm font-medium text-[#40464f]",
+ stepperHeadIconCompletedBg: "!bg-success-100 !text-success-700",
+ stepperHeadIconActiveBg: "!bg-primary-100 !text-primary-700",
+ stepperHeadIconInvalidBg: "!bg-danger-100 !text-danger-700",
stepperHeadText:
- "text-neutral-500 after:flex after:text-[0.8rem] after:content-[data-content] dark:text-neutral-300",
+ "text-neutral-500 after:flex after:text-[0.8rem] dark:text-neutral-300",
stepperHeadTextActive:
"font-medium after:flex after:text-[0.8rem] after:content-[data-content]",
- stepperContentTranslatedLeft:
- "absolute left-0 w-full p-4 transition-all duration-500 ease-in-out translate-0 -translate-x-[150%]",
- stepperContentTranslatedRight:
- "absolute left-0 w-full p-4 transition-all duration-500 ease-in-out translate-0 translate-x-[150%]",
- stepperContentActive:
+ stepperContentWrapper:
+ "transition-all duration-500 ease-in-out overflow-hidden",
+ stepperContent:
"absolute left-0 w-full p-4 transition-all duration-500 ease-in-out translate-0",
+ stepperContentTranslateLeft: "-translate-x-[150%]",
+ stepperContentTranslateRight: "translate-x-[150%]",
+ stepperVerticalContent:
+ "transition-[height, margin-bottom, padding-top, padding-bottom] left-0 overflow-hidden pb-6 pl-[3.75rem] pr-6 duration-300 ease-in-out",
};
export default StepperStepTheme;
diff --git a/src/lib/components/Stepper/hooks/useHeadClasses.ts b/src/lib/components/Stepper/hooks/useHeadClasses.ts
new file mode 100644
index 0000000..b5fda02
--- /dev/null
+++ b/src/lib/components/Stepper/hooks/useHeadClasses.ts
@@ -0,0 +1,26 @@
+import { useContext, useMemo } from "react";
+import StepperContext from "../StepperContext";
+import clsx from "clsx";
+
+export default function useHeadClasses(theme: any, itemId: number) {
+ const { stepsAmount, vertical } = useContext(StepperContext);
+
+ const beforeLine = vertical ? "" : "stepperHeadBeforeLineHorizontal";
+ const afterLine = vertical
+ ? "stepperHeadAfterLineVertical"
+ : "stepperHeadAfterLineHorizontal";
+
+ const isFirstStep = useMemo(() => itemId === 1, [itemId]);
+ const isLastStep = useMemo(
+ () => itemId === stepsAmount,
+ [itemId, stepsAmount]
+ );
+
+ const stepperHeadClasses = clsx(
+ vertical ? theme.stepperHeadVertical : theme.stepperHead,
+ !isFirstStep && theme[beforeLine],
+ !isLastStep && theme[afterLine]
+ );
+
+ return stepperHeadClasses;
+}
diff --git a/src/lib/components/Stepper/hooks/useHeadIconClasses.ts b/src/lib/components/Stepper/hooks/useHeadIconClasses.ts
index d941305..88770a4 100644
--- a/src/lib/components/Stepper/hooks/useHeadIconClasses.ts
+++ b/src/lib/components/Stepper/hooks/useHeadIconClasses.ts
@@ -2,28 +2,30 @@ const useHeadIconClasses = (
isActive: boolean,
isCompleted: boolean,
isInvalid: boolean,
- theme: any
+ theme: any,
+ vertical: boolean
): string => {
const {
stepperHeadIcon,
- stepperHeadIconActive,
- stepperHeadIconCompleted,
- stepperHeadIconInvalid,
+ stepperHeadIconVertical,
+ stepperHeadIconActiveBg,
+ stepperHeadIconCompletedBg,
+ stepperHeadIconInvalidBg,
} = theme;
- if (isActive) {
- return stepperHeadIconActive;
- }
+ const headIconTheme = vertical ? stepperHeadIconVertical : stepperHeadIcon;
- if (isCompleted) {
- return stepperHeadIconCompleted;
+ if (isActive) {
+ return headIconTheme + " " + stepperHeadIconActiveBg;
}
-
if (isInvalid) {
- return stepperHeadIconInvalid;
+ return headIconTheme + stepperHeadIconInvalidBg;
+ }
+ if (isCompleted) {
+ return headIconTheme + " " + stepperHeadIconCompletedBg;
}
- return stepperHeadIcon;
+ return headIconTheme;
};
export default useHeadIconClasses;
diff --git a/src/lib/components/Stepper/hooks/useHorizontalStepperHeight.ts b/src/lib/components/Stepper/hooks/useHorizontalStepperHeight.ts
index 3c528d0..623aec7 100644
--- a/src/lib/components/Stepper/hooks/useHorizontalStepperHeight.ts
+++ b/src/lib/components/Stepper/hooks/useHorizontalStepperHeight.ts
@@ -21,15 +21,9 @@ const useStepperHeight = (
);
}
};
- // if (vertical) {
- // return setStepperHeight("unset");
- // }
useEffect(() => {
handleResize();
- }, [isActive, children]);
-
- useEffect(() => {
if (!isActive) return;
window.addEventListener("resize", handleResize);
return () => {
diff --git a/src/lib/components/Stepper/hooks/useVerticalStepHeight.ts b/src/lib/components/Stepper/hooks/useVerticalStepHeight.ts
new file mode 100644
index 0000000..89a4bc8
--- /dev/null
+++ b/src/lib/components/Stepper/hooks/useVerticalStepHeight.ts
@@ -0,0 +1,36 @@
+import { useState, useEffect, RefObject } from "react";
+
+export default function useVerticalStepHeight(
+ isActive: boolean,
+ contentWrapperRef: RefObject,
+ children: React.ReactNode | React.ReactNode[]
+) {
+ const [height, setHeight] = useState("0px");
+
+ useEffect(() => {
+ const observer = new ResizeObserver((entries) => {
+ if (!isActive) {
+ return setHeight("0px");
+ }
+ const contentWrapperHeight = entries[0].contentRect.height;
+ const computed = window.getComputedStyle(
+ contentWrapperRef.current as Element
+ );
+
+ const offsetY =
+ parseFloat(computed.paddingTop) +
+ parseFloat(computed.paddingBottom) +
+ parseFloat(computed.marginBottom) +
+ parseFloat(computed.marginTop);
+
+ setHeight(`${contentWrapperHeight + offsetY}px`);
+ });
+
+ observer.observe(contentWrapperRef.current as Element);
+ return () => {
+ observer.disconnect();
+ };
+ }, [isActive, children]);
+
+ return height;
+}
From b797abc195ac3c10811341889f5c918d0f310bee Mon Sep 17 00:00:00 2001
From: Mateusz Lazaru
Date: Tue, 21 Nov 2023 10:04:38 +0100
Subject: [PATCH 04/19] stepper - imported stepper in index.tsx, created
separated demo examples
---
.../pages/components/stepper/StepperPage.tsx | 266 +-----------------
.../pages/components/stepper/exampleList.tsx | 0
.../stepper/examples/StepperBasicExample.tsx | 21 ++
.../examples/StepperControlledStep.tsx | 28 ++
.../examples/StepperVerticalExample.tsx | 21 ++
src/lib/index.tsx | 4 +
6 files changed, 81 insertions(+), 259 deletions(-)
create mode 100644 src/demo/pages/components/stepper/exampleList.tsx
create mode 100644 src/demo/pages/components/stepper/examples/StepperBasicExample.tsx
create mode 100644 src/demo/pages/components/stepper/examples/StepperControlledStep.tsx
create mode 100644 src/demo/pages/components/stepper/examples/StepperVerticalExample.tsx
diff --git a/src/demo/pages/components/stepper/StepperPage.tsx b/src/demo/pages/components/stepper/StepperPage.tsx
index 44a1e0d..66df066 100644
--- a/src/demo/pages/components/stepper/StepperPage.tsx
+++ b/src/demo/pages/components/stepper/StepperPage.tsx
@@ -1,20 +1,9 @@
-import React, { useState } from "react";
-import TEStepper from "../../../../lib/components/Stepper/Stepper";
-import TEStepperStep from "../../../../lib/components/Stepper/StepperStep/StepperStep";
-import { TECollapse } from "tw-elements-react";
+import React from "react";
+import StepperControlledStep from "./examples/StepperControlledStep";
+import StepperVerticalExample from "./examples/StepperVerticalExample";
+import StepperBasicExample from "./examples/StepperBasicExample";
const StepperPage = () => {
- const [activeStep, setActiveStep] = useState(1);
- const [activeElement, setActiveElement] = useState("");
-
- const handleClick = (value: string) => {
- if (value === activeElement) {
- setActiveElement("");
- } else {
- setActiveElement(value);
- }
- };
- const [addForm, setAddForm] = useState(false);
return (
-
setAddForm(!addForm)}>Add Form
Basic example
-
-
- Step 1 content
- XXXXs
- Step 1 content
- XXXXs
- Step 1 content
- XXXXs
- Step 1 content
- XXXXs
- Step 1 content
- XXXXs
-
-
- Step 2 content
-
-
- Step 3 content
-
-
+
@@ -57,32 +26,7 @@ const StepperPage = () => {
-
{
- console.log("onChange: ", stepId);
- setActiveStep(stepId);
- }}
- >
-
- Step 1 content
- XXXXs
- Step 1 content
- XXXXs
- Step 1 content
- XXXXs
- Step 1 content
- XXXXs
- Step 1 content
- XXXXs
-
-
- Step 2 content
-
-
- Step 3 content
-
-
+
@@ -92,203 +36,7 @@ const StepperPage = () => {
-
-
- Step 1 content
- XXXXs
- Step 1 content
- XXXXs
- Step 1 content
- XXXXs
- Step 1 content
- XXXXs
- Step 1 content
- XXXXs
-
-
-
-
Step 2 content
- {addForm && (
- <>
-
-
-
- handleClick("element1")}
- aria-expanded="true"
- aria-controls="collapseOne"
- >
- Accordion Item #1
-
-
-
-
-
-
-
-
-
-
-
- This is the first item's accordion body.
- {" "}
- Lorem ipsum dolor sit amet, consectetur adipiscing
- elit. Vestibulum eu rhoncus purus, vitae tincidunt
- nibh. Vivamus elementum egestas ligula in varius.
- Proin ac erat pretium, ultricies leo at, cursus
- ante. Pellentesque at odio euismod, mattis urna ac,
- accumsan metus. Nam nisi leo, malesuada vitae
- pretium et, laoreet at lorem. Curabitur non
- sollicitudin neque.
-
-
-
-
-
-
- handleClick("element2")}
- aria-expanded="true"
- aria-controls="collapseOne"
- >
- Accordion Item #2
-
-
-
-
-
-
-
-
-
-
- This is the second item's accordion body.
- {" "}
- Lorem ipsum dolor sit amet, consectetur adipiscing
- elit. Vestibulum eu rhoncus purus, vitae tincidunt
- nibh. Vivamus elementum egestas ligula in varius.
- Proin ac erat pretium, ultricies leo at, cursus ante.
- Pellentesque at odio euismod, mattis urna ac, accumsan
- metus. Nam nisi leo, malesuada vitae pretium et,
- laoreet at lorem. Curabitur non sollicitudin neque.
-
-
-
-
-
- handleClick("element3")}
- aria-expanded="true"
- aria-controls="collapseOne"
- >
- Accordion Item #3
-
-
-
-
-
-
-
-
-
-
- This is the third item's accordion body.
-
- Lorem ipsum dolor sit amet, consectetur adipiscing
- elit. Vestibulum eu rhoncus purus, vitae tincidunt
- nibh. Vivamus elementum egestas ligula in varius.
- Proin ac erat pretium, ultricies leo at, cursus ante.
- Pellentesque at odio euismod, mattis urna ac, accumsan
- metus. Nam nisi leo, malesuada vitae pretium et,
- laoreet at lorem. Curabitur non sollicitudin neque.
-
-
-
- >
- )}
-
-
-
- Step 3 content
-
-
+
diff --git a/src/demo/pages/components/stepper/exampleList.tsx b/src/demo/pages/components/stepper/exampleList.tsx
new file mode 100644
index 0000000..e69de29
diff --git a/src/demo/pages/components/stepper/examples/StepperBasicExample.tsx b/src/demo/pages/components/stepper/examples/StepperBasicExample.tsx
new file mode 100644
index 0000000..18cd984
--- /dev/null
+++ b/src/demo/pages/components/stepper/examples/StepperBasicExample.tsx
@@ -0,0 +1,21 @@
+import React from "react";
+import { TEStepper, TEStepperStep } from "tw-elements-react";
+
+export default function StepperBasicExample(): JSX.Element {
+ return (
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
+ tempor incididunt ut labore et dolore magna aliqua.
+
+
+ Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi
+ ut aliquip ex ea commodo consequat.
+
+
+ Duis aute irure dolor in reprehenderit in voluptate velit esse cillum
+ dolore eu fugiat nulla pariatur.
+
+
+ );
+}
diff --git a/src/demo/pages/components/stepper/examples/StepperControlledStep.tsx b/src/demo/pages/components/stepper/examples/StepperControlledStep.tsx
new file mode 100644
index 0000000..6c219dd
--- /dev/null
+++ b/src/demo/pages/components/stepper/examples/StepperControlledStep.tsx
@@ -0,0 +1,28 @@
+import React, { useState } from "react";
+import { TEStepper, TEStepperStep } from "tw-elements-react";
+
+export default function StepperControlledStep(): JSX.Element {
+ const [activeStep, setActiveStep] = useState(1);
+
+ return (
+ {
+ setActiveStep(stepId);
+ }}
+ >
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
+ tempor incididunt ut labore et dolore magna aliqua.
+
+
+ Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi
+ ut aliquip ex ea commodo consequat.
+
+
+ Duis aute irure dolor in reprehenderit in voluptate velit esse cillum
+ dolore eu fugiat nulla pariatur.
+
+
+ );
+}
diff --git a/src/demo/pages/components/stepper/examples/StepperVerticalExample.tsx b/src/demo/pages/components/stepper/examples/StepperVerticalExample.tsx
new file mode 100644
index 0000000..c5fc0a7
--- /dev/null
+++ b/src/demo/pages/components/stepper/examples/StepperVerticalExample.tsx
@@ -0,0 +1,21 @@
+import React from "react";
+import { TEStepper, TEStepperStep } from "tw-elements-react";
+
+export default function StepperVerticalExample(): JSX.Element {
+ return (
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
+ tempor incididunt ut labore et dolore magna aliqua.
+
+
+ Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi
+ ut aliquip ex ea commodo consequat.
+
+
+ Duis aute irure dolor in reprehenderit in voluptate velit esse cillum
+ dolore eu fugiat nulla pariatur.
+
+
+ );
+}
diff --git a/src/lib/index.tsx b/src/lib/index.tsx
index 8d575e7..01d5910 100644
--- a/src/lib/index.tsx
+++ b/src/lib/index.tsx
@@ -27,6 +27,8 @@ import TEToast from "./components/Toasts/Toast";
import TESelect from "./forms/Select/Select";
import TECarousel from "./components/Carousel/Carousel";
import TECarouselItem from "./components/Carousel/CarouselItem/CarouselItem";
+import TEStepper from "./components/Stepper/Stepper";
+import TEStepperStep from "./components/Stepper/StepperStep/StepperStep";
export {
TECollapse,
@@ -58,4 +60,6 @@ export {
TESelect,
TECarousel,
TECarouselItem,
+ TEStepper,
+ TEStepperStep,
};
From 2b54daa92749f3d038a62bc4bab0cd2ff7119273 Mon Sep 17 00:00:00 2001
From: Mateusz Lazaru
Date: Tue, 21 Nov 2023 10:14:06 +0100
Subject: [PATCH 05/19] stepper - changed type 'vertical' to 'type', improved
horizontal stepper height calculating
---
src/lib/components/Stepper/Stepper.tsx | 6 ++-
.../hooks/useHorizontalStepperHeight.ts | 41 +++++++++++--------
2 files changed, 29 insertions(+), 18 deletions(-)
diff --git a/src/lib/components/Stepper/Stepper.tsx b/src/lib/components/Stepper/Stepper.tsx
index b2386a8..ee720ad 100644
--- a/src/lib/components/Stepper/Stepper.tsx
+++ b/src/lib/components/Stepper/Stepper.tsx
@@ -13,12 +13,14 @@ const TEStepper: React.FC = ({
activeStep: activeStepProp,
children,
onChange,
- vertical,
+ type = "horizontal",
+ style,
}) => {
const theme = {
...StepperTheme,
...customTheme,
};
+ const vertical = type === "vertical";
const classes = clsx(
vertical ? theme.stepperVertical : theme.stepper,
className
@@ -54,7 +56,7 @@ const TEStepper: React.FC = ({
{childrenArray.map((ChildComponent, index: number) => {
return React.cloneElement(ChildComponent, {
diff --git a/src/lib/components/Stepper/hooks/useHorizontalStepperHeight.ts b/src/lib/components/Stepper/hooks/useHorizontalStepperHeight.ts
index 623aec7..801c91d 100644
--- a/src/lib/components/Stepper/hooks/useHorizontalStepperHeight.ts
+++ b/src/lib/components/Stepper/hooks/useHorizontalStepperHeight.ts
@@ -8,26 +8,35 @@ const useStepperHeight = (
vertical: boolean,
children: React.ReactNode | React.ReactNode[]
) => {
- if (vertical) return;
const { setStepperHeight } = useContext(StepperContext);
- const handleResize = () => {
- if (!isActive) {
- return;
- }
- if (stepRef.current && headRef.current) {
- setStepperHeight(
- String(stepRef.current.scrollHeight + headRef.current.scrollHeight)
- );
- }
- };
-
useEffect(() => {
- handleResize();
- if (!isActive) return;
- window.addEventListener("resize", handleResize);
+ if (vertical) return;
+ const headHeight = headRef.current?.offsetHeight || 0;
+
+ const handleResize = (entries: Array) => {
+ if (!isActive) {
+ return;
+ }
+ const stepHeight = entries[0].contentRect.height;
+ const computed = window.getComputedStyle(stepRef.current as Element);
+
+ const offsetY =
+ parseFloat(computed.paddingTop) +
+ parseFloat(computed.paddingBottom) +
+ parseFloat(computed.marginBottom) +
+ parseFloat(computed.marginTop);
+
+ setStepperHeight(`${stepHeight + offsetY + headHeight}px`);
+ };
+
+ const observer = new ResizeObserver((entries) => {
+ handleResize(entries);
+ });
+
+ observer.observe(stepRef.current as Element);
return () => {
- window.removeEventListener("resize", handleResize);
+ observer.disconnect();
};
}, [isActive, children]);
};
From 27adb7c89dc040ad66314c061ff1cd4ad400d100 Mon Sep 17 00:00:00 2001
From: Mateusz Lazaru
Date: Tue, 21 Nov 2023 13:27:16 +0100
Subject: [PATCH 06/19] stepper - fixed head classes
---
src/lib/components/Stepper/Stepper.tsx | 2 +-
.../Stepper/StepperStep/stepperStepTheme.ts | 14 ++++++--------
.../components/Stepper/hooks/useHeadClasses.ts | 17 ++++-------------
3 files changed, 11 insertions(+), 22 deletions(-)
diff --git a/src/lib/components/Stepper/Stepper.tsx b/src/lib/components/Stepper/Stepper.tsx
index ee720ad..44dd9a1 100644
--- a/src/lib/components/Stepper/Stepper.tsx
+++ b/src/lib/components/Stepper/Stepper.tsx
@@ -28,7 +28,7 @@ const TEStepper: React.FC = ({
const [activeStepState, setActiveStepState] = useState(defaultStep);
const activeStep = useActiveValue(activeStepProp, activeStepState);
const stepperRef = useRef(null);
- const [stepperHeight, setStepperHeight] = useState("0");
+ const [stepperHeight, setStepperHeight] = useState("auto");
const childrenArray = useMemo(() => {
return React.Children.toArray(
diff --git a/src/lib/components/Stepper/StepperStep/stepperStepTheme.ts b/src/lib/components/Stepper/StepperStep/stepperStepTheme.ts
index c517186..7f5ab86 100644
--- a/src/lib/components/Stepper/StepperStep/stepperStepTheme.ts
+++ b/src/lib/components/Stepper/StepperStep/stepperStepTheme.ts
@@ -16,14 +16,12 @@ const StepperStepTheme = {
stepperStepVertical:
"relative h-fit after:absolute after:left-[2.45rem] after:top-[3.6rem] after:mt-px after:h-[calc(100%-2.45rem)] after:w-px after:bg-[#e0e0e0] dark:after:bg-neutral-600",
stepperLastStepVertical: "relative h-fit",
- stepperHead:
- "flex cursor-pointer items-center leading-[1.3rem] no-underline hover:bg-[#f9f9f9] focus:outline-none dark:hover:bg-[#3b3b3b]",
- stepperHeadBeforeLineHorizontal:
- "before:mr-2 before:h-px before:w-full before:flex-1 before:bg-[#e0e0e0] dark:before:bg-neutral-600",
- stepperHeadAfterLineHorizontal:
- "after:ml-2 after:h-px after:w-full after:flex-1 after:bg-[#e0e0e0] dark:after:bg-neutral-600",
- stepperHeadAfterLineVertical:
- "after:absolute after:left-[2.45rem] after:top-[3.6rem] after:mt-px after:h-[calc(100%-2.45rem)] after:w-px after:bg-[#e0e0e0] after:content-[''] dark:after:bg-neutral-600",
+ stepperHeadHorizontal:
+ "flex cursor-pointer items-center leading-[1.3rem] no-underline before:mr-2 before:h-px before:w-full before:flex-1 before:bg-[#e0e0e0] before:content-[''] after:ml-2 after:h-px after:w-full after:flex-1 after:bg-[#e0e0e0] after:content-[''] hover:bg-[#f9f9f9] focus:outline-none dark:before:bg-neutral-600 dark:after:bg-neutral-600 dark:hover:bg-[#3b3b3b]",
+ stepperFirstStepHeadHorizontal:
+ "flex cursor-pointer items-center pl-2 leading-[1.3rem] no-underline after:ml-2 after:h-px after:w-full after:flex-1 after:bg-[#e0e0e0] after:content-[''] hover:bg-[#f9f9f9] focus:outline-none dark:after:bg-neutral-600 dark:hover:bg-[#3b3b3b]",
+ stepperLastStepHeadHorizontal:
+ "flex cursor-pointer items-center pr-2 leading-[1.3rem] no-underline before:mr-2 before:h-px before:w-full before:flex-1 before:bg-[#e0e0e0] before:content-[''] hover:bg-[#f9f9f9] focus:outline-none dark:before:bg-neutral-600 dark:after:bg-neutral-600 dark:hover:bg-[#3b3b3b]",
stepperHeadVertical:
"flex cursor-pointer items-center p-6 leading-[1.3rem] no-underline hover:bg-[#f9f9f9] focus:outline-none dark:hover:bg-[#3b3b3b]",
stepperHeadIcon:
diff --git a/src/lib/components/Stepper/hooks/useHeadClasses.ts b/src/lib/components/Stepper/hooks/useHeadClasses.ts
index b5fda02..1c2f52c 100644
--- a/src/lib/components/Stepper/hooks/useHeadClasses.ts
+++ b/src/lib/components/Stepper/hooks/useHeadClasses.ts
@@ -1,26 +1,17 @@
import { useContext, useMemo } from "react";
import StepperContext from "../StepperContext";
-import clsx from "clsx";
export default function useHeadClasses(theme: any, itemId: number) {
const { stepsAmount, vertical } = useContext(StepperContext);
- const beforeLine = vertical ? "" : "stepperHeadBeforeLineHorizontal";
- const afterLine = vertical
- ? "stepperHeadAfterLineVertical"
- : "stepperHeadAfterLineHorizontal";
-
const isFirstStep = useMemo(() => itemId === 1, [itemId]);
const isLastStep = useMemo(
() => itemId === stepsAmount,
[itemId, stepsAmount]
);
- const stepperHeadClasses = clsx(
- vertical ? theme.stepperHeadVertical : theme.stepperHead,
- !isFirstStep && theme[beforeLine],
- !isLastStep && theme[afterLine]
- );
-
- return stepperHeadClasses;
+ if (vertical) return theme.stepperHeadVertical;
+ if (isFirstStep) return theme.stepperFirstStepHeadHorizontal;
+ if (isLastStep) return theme.stepperLastStepHeadHorizontal;
+ return theme.stepperHeadHorizontal;
}
From 38314a18f43682051c3b23a497a99f213387b1cc Mon Sep 17 00:00:00 2001
From: Mateusz Lazaru
Date: Thu, 23 Nov 2023 00:33:17 +0100
Subject: [PATCH 07/19] stepper - improved typing, removed unused props,
improved code readability
---
src/lib/components/Stepper/Stepper.tsx | 2 +-
.../Stepper/StepperStep/StepperStep.tsx | 11 ++++----
.../Stepper/StepperStep/stepperStepTheme.ts | 27 ++++++++++++-------
.../components/Stepper/StepperStep/types.ts | 15 ++---------
.../Stepper/hooks/useHeadClasses.ts | 6 ++++-
.../Stepper/hooks/useHeadIconClasses.ts | 17 ++++++------
src/lib/components/Stepper/stepperTheme.ts | 2 +-
src/lib/components/Stepper/types.ts | 11 +++-----
8 files changed, 44 insertions(+), 47 deletions(-)
diff --git a/src/lib/components/Stepper/Stepper.tsx b/src/lib/components/Stepper/Stepper.tsx
index 44dd9a1..450ea46 100644
--- a/src/lib/components/Stepper/Stepper.tsx
+++ b/src/lib/components/Stepper/Stepper.tsx
@@ -22,7 +22,7 @@ const TEStepper: React.FC = ({
};
const vertical = type === "vertical";
const classes = clsx(
- vertical ? theme.stepperVertical : theme.stepper,
+ vertical ? theme.stepperVertical : theme.stepperHorizontal,
className
);
const [activeStepState, setActiveStepState] = useState(defaultStep);
diff --git a/src/lib/components/Stepper/StepperStep/StepperStep.tsx b/src/lib/components/Stepper/StepperStep/StepperStep.tsx
index 0e4f290..a2cf878 100644
--- a/src/lib/components/Stepper/StepperStep/StepperStep.tsx
+++ b/src/lib/components/Stepper/StepperStep/StepperStep.tsx
@@ -1,8 +1,8 @@
import React, { useMemo, useRef, useContext } from "react";
+import clx from "clsx";
import StepperStepTheme from "./stepperStepTheme";
import StepperContext from "../StepperContext";
import { StepperStepProps } from "./types";
-import clx from "clsx";
import useHeadIconClasses from "../hooks/useHeadIconClasses";
import useIsStepCompleted from "../hooks/useIsStepCompleted";
import useStepperHeight from "../hooks/useHorizontalStepperHeight";
@@ -13,11 +13,12 @@ import useHeadClasses from "../hooks/useHeadClasses";
const TEStepperStep: React.FC = ({
theme: customTheme,
className,
+ contentClassName,
+ headClassName,
itemId = 1,
headIcon = "",
headText = "",
children,
- invalid,
style,
}) => {
const headRef = useRef(null);
@@ -46,11 +47,10 @@ const TEStepperStep: React.FC = ({
const headIconClasses = useHeadIconClasses(
isActive,
isCompleted,
- invalid,
theme,
vertical
);
- const stepperHeadClasses = useHeadClasses(theme, itemId);
+ const stepperHeadClasses = clx(useHeadClasses(theme, itemId), headClassName);
const stepperStepClasses = clx(
vertical
? isLastStep
@@ -64,7 +64,8 @@ const TEStepperStep: React.FC = ({
const dynamicAnimationDirection: string = `stepperContentTranslate${animationDirection}`;
const stepperContentClasses = clx(
vertical ? theme.stepperVerticalContent : theme.stepperContent,
- !vertical && theme[dynamicAnimationDirection as keyof typeof theme]
+ !vertical && theme[dynamicAnimationDirection as keyof typeof theme],
+ contentClassName
);
const headClickHandler = () => {
diff --git a/src/lib/components/Stepper/StepperStep/stepperStepTheme.ts b/src/lib/components/Stepper/StepperStep/stepperStepTheme.ts
index 7f5ab86..6fe927a 100644
--- a/src/lib/components/Stepper/StepperStep/stepperStepTheme.ts
+++ b/src/lib/components/Stepper/StepperStep/stepperStepTheme.ts
@@ -1,17 +1,25 @@
interface StepperStepThemeProps {
stepperStep: string;
- stepperHead: string;
- stepperHeadIcon: string;
- stepperHeadIconActive: string;
- stepperHeadIconCompleted: string;
+ stepperStepVertical: string;
+ stepperLastStepVertical: string;
+ stepperHeadHorizontal: string;
+ stepperFirstStepHeadHorizontal: string;
+ stepperLastStepHeadHorizontal: string;
+ stepperHeadVertical: string;
+ stepperHeadIconHorizontal: string;
+ stepperHeadIconVertical: string;
+ stepperHeadIconActiveBg: string;
+ stepperHeadIconCompletedBg: string;
stepperHeadText: string;
stepperHeadTextActive: string;
- stepperContentTranslatedLeft: string;
- stepperContentTranslatedRight: string;
- stepperContentActive: string;
+ stepperContent: string;
+ stepperContentTranslateLeft: string;
+ stepperContentTranslateRight: string;
+ stepperVerticalContent: string;
+ stepperContentWrapper: string;
}
-const StepperStepTheme = {
+const StepperStepTheme: StepperStepThemeProps = {
stepperStep: "w-[4.5rem] flex-auto",
stepperStepVertical:
"relative h-fit after:absolute after:left-[2.45rem] after:top-[3.6rem] after:mt-px after:h-[calc(100%-2.45rem)] after:w-px after:bg-[#e0e0e0] dark:after:bg-neutral-600",
@@ -24,13 +32,12 @@ const StepperStepTheme = {
"flex cursor-pointer items-center pr-2 leading-[1.3rem] no-underline before:mr-2 before:h-px before:w-full before:flex-1 before:bg-[#e0e0e0] before:content-[''] hover:bg-[#f9f9f9] focus:outline-none dark:before:bg-neutral-600 dark:after:bg-neutral-600 dark:hover:bg-[#3b3b3b]",
stepperHeadVertical:
"flex cursor-pointer items-center p-6 leading-[1.3rem] no-underline hover:bg-[#f9f9f9] focus:outline-none dark:hover:bg-[#3b3b3b]",
- stepperHeadIcon:
+ stepperHeadIconHorizontal:
"my-6 mr-2 flex h-[1.938rem] w-[1.938rem] items-center justify-center rounded-full bg-[#ebedef] text-sm font-medium text-[#40464f]",
stepperHeadIconVertical:
"mr-3 flex h-[1.938rem] w-[1.938rem] items-center justify-center rounded-full bg-[#ebedef] text-sm font-medium text-[#40464f]",
stepperHeadIconCompletedBg: "!bg-success-100 !text-success-700",
stepperHeadIconActiveBg: "!bg-primary-100 !text-primary-700",
- stepperHeadIconInvalidBg: "!bg-danger-100 !text-danger-700",
stepperHeadText:
"text-neutral-500 after:flex after:text-[0.8rem] dark:text-neutral-300",
stepperHeadTextActive:
diff --git a/src/lib/components/Stepper/StepperStep/types.ts b/src/lib/components/Stepper/StepperStep/types.ts
index 37ccedb..4934ef4 100644
--- a/src/lib/components/Stepper/StepperStep/types.ts
+++ b/src/lib/components/Stepper/StepperStep/types.ts
@@ -1,26 +1,15 @@
import React from "react";
import { BaseComponent } from "../../../types/baseComponent";
-interface StepperThemeProps {
- stepperStep: string;
- stepperHead: string;
- stepperHeadActive: string;
- stepperHeadIcon: string;
- stepperHeadText: string;
- stepperHeadTextActive: string;
- stepperContent: string;
- stepperContentActive: string;
-}
+import type { StepperStepThemeProps } from "./stepperStepTheme";
interface StepperStepProps extends BaseComponent {
- theme?: StepperThemeProps;
+ theme?: StepperStepThemeProps;
headClassName?: string;
contentClassName?: string;
itemId?: number;
headIcon?: React.ReactNode;
headText?: React.ReactNode;
- onComplete?: (id: number) => void;
- activeStep?: number;
}
export type { StepperStepProps };
diff --git a/src/lib/components/Stepper/hooks/useHeadClasses.ts b/src/lib/components/Stepper/hooks/useHeadClasses.ts
index 1c2f52c..87edb5b 100644
--- a/src/lib/components/Stepper/hooks/useHeadClasses.ts
+++ b/src/lib/components/Stepper/hooks/useHeadClasses.ts
@@ -1,7 +1,11 @@
import { useContext, useMemo } from "react";
import StepperContext from "../StepperContext";
+import type { StepperStepThemeProps } from "../StepperStep/stepperStepTheme";
-export default function useHeadClasses(theme: any, itemId: number) {
+export default function useHeadClasses(
+ theme: StepperStepThemeProps,
+ itemId: number
+) {
const { stepsAmount, vertical } = useContext(StepperContext);
const isFirstStep = useMemo(() => itemId === 1, [itemId]);
diff --git a/src/lib/components/Stepper/hooks/useHeadIconClasses.ts b/src/lib/components/Stepper/hooks/useHeadIconClasses.ts
index 88770a4..8a563c5 100644
--- a/src/lib/components/Stepper/hooks/useHeadIconClasses.ts
+++ b/src/lib/components/Stepper/hooks/useHeadIconClasses.ts
@@ -1,28 +1,27 @@
+import clsx from "clsx";
+
const useHeadIconClasses = (
isActive: boolean,
isCompleted: boolean,
- isInvalid: boolean,
theme: any,
vertical: boolean
): string => {
const {
- stepperHeadIcon,
+ stepperHeadIconHorizontal,
stepperHeadIconVertical,
stepperHeadIconActiveBg,
stepperHeadIconCompletedBg,
- stepperHeadIconInvalidBg,
} = theme;
- const headIconTheme = vertical ? stepperHeadIconVertical : stepperHeadIcon;
+ const headIconTheme = vertical
+ ? stepperHeadIconVertical
+ : stepperHeadIconHorizontal;
if (isActive) {
- return headIconTheme + " " + stepperHeadIconActiveBg;
- }
- if (isInvalid) {
- return headIconTheme + stepperHeadIconInvalidBg;
+ return clsx(headIconTheme, stepperHeadIconActiveBg);
}
if (isCompleted) {
- return headIconTheme + " " + stepperHeadIconCompletedBg;
+ return clsx(headIconTheme, stepperHeadIconCompletedBg);
}
return headIconTheme;
diff --git a/src/lib/components/Stepper/stepperTheme.ts b/src/lib/components/Stepper/stepperTheme.ts
index 4a92a76..1f5fd88 100644
--- a/src/lib/components/Stepper/stepperTheme.ts
+++ b/src/lib/components/Stepper/stepperTheme.ts
@@ -1,5 +1,5 @@
const StepperTheme = {
- stepper:
+ stepperHorizontal:
"relative m-0 flex list-none justify-between overflow-hidden p-0 transition-[height] duration-200 ease-in-out",
stepperVertical:
"relative m-0 w-full list-none overflow-hidden p-0 transition-[height] duration-200 ease-in-out",
diff --git a/src/lib/components/Stepper/types.ts b/src/lib/components/Stepper/types.ts
index 077232e..d6a3cc2 100644
--- a/src/lib/components/Stepper/types.ts
+++ b/src/lib/components/Stepper/types.ts
@@ -1,18 +1,15 @@
-import React from "react";
import { BaseComponent } from "../../types/baseComponent";
-import { StepperStepProps } from "./StepperStep/types";
+import type { StepperStepProps } from "./StepperStep/types";
interface StepperThemeProps {
stepper: string;
+ stepperVertical: string;
}
type StepperProps = Omit & {
- stepperTheme?: StepperThemeProps;
- disableHeadSteps?: boolean;
- defaultStep?: number;
activeStep?: number;
- linear?: boolean;
- noEditable?: boolean;
+ defaultStep?: number;
+ theme?: StepperThemeProps;
type?: "horizontal" | "vertical";
onChange?: (id: number) => void;
children:
From 7854395e5bdbdbb93ed27439e3eb87f122f66da3 Mon Sep 17 00:00:00 2001
From: Mateusz Lazaru
Date: Thu, 23 Nov 2023 13:44:31 +0100
Subject: [PATCH 08/19] stepper - prepared demo and docs
---
.../docs/react/components/stepper/a-ss.html | 12 +
.../docs/react/components/stepper/a.html | 692 ++++++++++++++++++
.../react/components/stepper/index-ss.html | 12 +
.../docs/react/components/stepper/index.html | 449 ++++++++++++
src/demo/pages.tsx | 2 +
.../pages/components/stepper/exampleList.tsx | 22 +
6 files changed, 1189 insertions(+)
create mode 100644 site/content/docs/react/components/stepper/a-ss.html
create mode 100644 site/content/docs/react/components/stepper/a.html
create mode 100644 site/content/docs/react/components/stepper/index-ss.html
create mode 100644 site/content/docs/react/components/stepper/index.html
diff --git a/site/content/docs/react/components/stepper/a-ss.html b/site/content/docs/react/components/stepper/a-ss.html
new file mode 100644
index 0000000..083d6d1
--- /dev/null
+++ b/site/content/docs/react/components/stepper/a-ss.html
@@ -0,0 +1,12 @@
+---
+---
+
+
+ Import
+
+
+ Properties
+
+
+ Events
+
\ No newline at end of file
diff --git a/site/content/docs/react/components/stepper/a.html b/site/content/docs/react/components/stepper/a.html
new file mode 100644
index 0000000..becbf99
--- /dev/null
+++ b/site/content/docs/react/components/stepper/a.html
@@ -0,0 +1,692 @@
+---
+---
+
+
+
+
+
+
+
+ Import
+
+
+
+ {{< twsnippet/no-demo id="api-example41" >}}
+
+
+ {{< twsnippet/wrapper "javascript" >}}
+
+ {{< twsnippet/code active=true lang="js" >}}
+ import {
+ TEStepper,
+ TEStepperStep,
+ } from "tw-elements-react";
+ {{< /twsnippet/code >}}
+ {{< /twsnippet/wrapper >}}
+
+
+
+
+
+
+
+
+
+
+
+
+ Properties
+
+
+
+ TEStepper
+
+
+
+
+
+
+
+
+
+
+ Name
+
+
+ Type
+
+
+ Default
+
+ Description
+
+
+
+
+
+ activeStep
+
+
+ Number
+
+
+ -
+
+
+ Controls the active step.
+ In most cases the value should be managed with onChange
handler.
+
+
+
+
+ defaultStep
+
+
+ Number
+
+
+ -
+
+
+ Sets default step. Does not change the active step.
+
+
+
+
+ theme
+
+
+ object
+
+
+ {}
+
+
+ Allows to change the Tailwind classes used in the component.
+
+
+
+
+
+
+
+
+
+
+ TEStepperStep
+
+
+
+
+
+
+
+
+
+
+ Name
+
+
+ Type
+
+
+ Default
+
+ Description
+
+
+
+
+
+ contentClassName
+
+
+ String
+
+
+ ''
+
+
+ Adds custom classes to the content wrapper.
+
+
+
+
+ headClassName
+
+
+ String
+
+
+ ''
+
+
+ Adds custom classes to the step head.
+
+
+
+
+ theme
+
+
+ object
+
+
+ {}
+
+
+ Allows to change the Tailwind classes used in the component.
+
+
+
+
+ headIcon
+
+
+ ReactNode
+
+
+ ''
+
+
+ Sets the icon in the step head.
+
+
+
+
+ headText
+
+
+ ReactNode
+
+
+ ''
+
+
+ Sets the text in the step head.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Classes
+
+
+ Custom classes can be passed via theme
prop. Create an object with classes like below and pass it to the prop.
+
+
+ TEStepper
+
+
+
+
+
+
+
+
+
+
+ Name
+
+
+ Default
+
+ Description
+
+
+
+
+
+ stepperHorizontal
+
+
+ "relative m-0 flex list-none justify-between overflow-hidden p-0 transition-[height] duration-200 ease-in-out",
+
+
+ Sets styles to the TEStepper
element in horizontal mode.
+
+
+
+
+ stepperVertical
+
+
+ "relative m-0 w-full list-none overflow-hidden p-0 transition-[height] duration-200 ease-in-out",
+
+
+ Sets styles to the TEStepper
element in vertical mode.
+
+
+
+
+
+
+
+
+
+
+ TEStepperStep
+
+
+
+
+
+
+
+
+
+
+ Name
+
+
+ Default
+
+ Description
+
+
+
+
+
+ stepperStep
+
+
+ w-[4.5rem] flex-auto
+
+
+ Sets styles to the stepperStep
element.
+
+
+
+
+ stepperStepVertical
+
+
+ relative h-fit after:absolute after:left-[2.45rem] after:top-[3.6rem] after:mt-px after:h-[calc(100%-2.45rem)] after:w-px after:bg-[#e0e0e0] dark:after:bg-neutral-600
+
+
+ Sets styles to the stepperStep
element in vertical mode.
+
+
+
+
+ stepperLastStepVertical
+
+
+ relative h-fit
+
+
+ Sets styles to the last stepperStep
element in vertical mode.
+
+
+
+
+ stepperHeadHorizontal
+
+
+ flex cursor-pointer items-center leading-[1.3rem] no-underline before:mr-2 before:h-px before:w-full before:flex-1 before:bg-[#e0e0e0] before:content-[''] after:ml-2 after:h-px after:w-full after:flex-1 after:bg-[#e0e0e0] after:content-[''] hover:bg-[#f9f9f9] focus:outline-none dark:before:bg-neutral-600 dark:after:bg-neutral-600 dark:hover:bg-[#3b3b3b]
+
+
+ Sets styles to the stepperHead
element in horizontal mode.
+
+
+
+
+ stepperFirstStepHeadHorizontal
+
+
+ flex cursor-pointer items-center pl-2 leading-[1.3rem] no-underline after:ml-2 after:h-px after:w-full after:flex-1 after:bg-[#e0e0e0] after:content-[''] hover:bg-[#f9f9f9] focus:outline-none dark:after:bg-neutral-600 dark:hover:bg-[#3b3b3b]
+
+
+ Sets styles to the stepperHead
element in horizontal mode for the first step.
+
+
+
+
+ stepperLastStepHeadHorizontal
+
+
+ flex cursor-pointer items-center pr-2 leading-[1.3rem] no-underline before:mr-2 before:h-px before:w-full before:flex-1 before:bg-[#e0e0e0] before:content-[''] hover:bg-[#f9f9f9] focus:outline-none dark:before:bg-neutral-600 dark:after:bg-neutral-600 dark:hover:bg-[#3b3b3b]
+
+
+ Sets styles to the stepperHead
element in horizontal mode for the last step.
+
+
+
+
+ stepperHeadVertical
+
+
+ flex cursor-pointer items-center p-6 leading-[1.3rem] no-underline hover:bg-[#f9f9f9] focus:outline-none dark:hover:bg-[#3b3b3b]
+
+
+ Sets styles to the stepperHead
element in vertical mode.
+
+
+
+
+ stepperHeadIconHorizontal
+
+
+ my-6 mr-2 flex h-[1.938rem] w-[1.938rem] items-center justify-center rounded-full bg-[#ebedef] text-sm font-medium text-[#40464f]
+
+
+ Sets styles to the stepperHeadIcon
element in horizontal mode.
+
+
+
+
+ stepperHeadIconVertical
+
+
+ mr-3 flex h-[1.938rem] w-[1.938rem] items-center justify-center rounded-full bg-[#ebedef] text-sm font-medium text-[#40464f]
+
+
+ Sets styles to the stepperHeadIcon
element in vertical mode.
+
+
+
+
+ stepperHeadIconCompletedBg
+
+
+ !bg-success-100 !text-success-700
+
+
+ Sets background color applied to the stepperHeadIcon
element when step is completed.
+
+
+
+
+ stepperHeadIconActiveBg
+
+
+ !bg-primary-100 !text-primary-700
+
+
+ Sets background color applied to the stepperHeadIcon
element when step is active.
+
+
+
+
+ stepperHeadText
+
+
+ text-neutral-500 after:flex after:text-[0.8rem] dark:text-neutral-300
+
+
+ Sets styles to the stepperHeadText
element.
+
+
+
+
+ stepperHeadTextActive
+
+
+ font-medium after:flex after:text-[0.8rem] after:content-[data-content]
+
+
+ Sets styles to the stepperHeadText
element when step is active.
+
+
+
+
+ stepperContentWrapper
+
+
+ transition-all duration-500 ease-in-out overflow-hidden
+
+
+ Sets styles to the stepperContent
wrapper element.
+
+
+
+
+ stepperContent
+
+
+ absolute left-0 w-full p-4 transition-all duration-500 ease-in-out translate-0
+
+
+ Sets styles to the stepperContent
element.
+
+
+
+
+ stepperVerticalContent
+
+
+ transition-[height, margin-bottom, padding-top, padding-bottom] left-0 overflow-hidden pb-6 pl-[3.75rem] pr-6 duration-300 ease-in-out
+
+
+ Sets styles to the stepperContent
element in vertical mode.
+
+
+
+
+ stepperContentTranslateLeft
+
+
+ -translate-x-[150%]
+
+
+ Additional animation style to the stepperContent
element when step is active and it is on the left side of the screen.
+
+
+
+
+ stepperContentTranslateRight
+
+
+ translate-x-[150%]
+
+
+ Additional animation style to the stepperContent
element when step is active and it is on the right side of the screen.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Events
+
+
+
+
+
+
+
+
+
+
+ Event type
+
+ Description
+
+
+
+
+
+ onChange?: (id: number) => void
+
+
+ Fired when stepper demands to change the active step. It can be triggered in TEStepper
element.
+
+
+
+
+
+
+
+
+
+
+ {{< twsnippet/no-demo id="api-example6" >}}
+
+
+
+ {{< twsnippet/wrapper "JSX" >}}
+ {{< twsnippet/code active=true lang="jsx" >}}
+ import React from "react";
+ import {
+ TEDropdown,
+ TEDropdownToggle,
+ TEDropdownMenu,
+ TEDropdownItem,
+ TERipple,
+ } from "tw-elements-react";
+
+ export default function DropdownEventsExample(): JSX.Element {
+ return (
+
console.log("onShow")}>
+
+
+ Dropdown button
+
+
+
+
+
+
+
+
+
+
+ Action
+
+
+ Another action
+
+
+ Something else here
+
+
+
+ );
+ }
+ {{< /twsnippet/code >}}
+ {{< /twsnippet/wrapper >}}
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/site/content/docs/react/components/stepper/index-ss.html b/site/content/docs/react/components/stepper/index-ss.html
new file mode 100644
index 0000000..faff06c
--- /dev/null
+++ b/site/content/docs/react/components/stepper/index-ss.html
@@ -0,0 +1,12 @@
+---
+---
+
+
+ Basic example
+
+
+ Controlled step
+
+
+ Vertical
+
diff --git a/site/content/docs/react/components/stepper/index.html b/site/content/docs/react/components/stepper/index.html
new file mode 100644
index 0000000..2d3c592
--- /dev/null
+++ b/site/content/docs/react/components/stepper/index.html
@@ -0,0 +1,449 @@
+---
+title: "Stepper"
+date: 2023-11-20T16:00:58+02:00
+draft: false
+main_title: "Stepper"
+subheading: "Tailwind CSS React Stepper"
+seo_title: "Tailwind CSS React Stepper - Free Examples & Tutorial"
+description: "Use responsive stepper component with helper examples for stepper ui, stepper form, vertical stepper, progress steps & more. Free download, open-source license."
+image: "https://tecdn.b-cdn.net/img/docs/components/stepper.webp"
+video: "https://www.youtube.com/watch?v=-GmnyjgI4Jc&ab_channel=Keepcoding"
+url: "docs/react/components/stepper/"
+menu:
+ components:
+ name: "Stepper"
+---
+
+
+
+
+
+ Basic example
+
+
+
+ Use horizontally aligned timeline component to show a series of data in a chronological order.
+
+
+
+ {{< twsnippet/demo-iframe id="example1" iframe="/components/stepper/examples/stepper-basic-example" title="Stepper Basic Example" >}}
+
+
+
+
+ {{< twsnippet/wrapper "JSX" >}}
+ {{< twsnippet/code active=true lang="jsx" >}}
+ import React from "react";
+ import { TEStepper, TEStepperStep } from "tw-elements-react";
+
+ export default function StepperBasicExample(): JSX.Element {
+ return (
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
+ tempor incididunt ut labore et dolore magna aliqua.
+
+
+ Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi
+ ut aliquip ex ea commodo consequat.
+
+
+ Duis aute irure dolor in reprehenderit in voluptate velit esse cillum
+ dolore eu fugiat nulla pariatur.
+
+
+ );
+ }
+
+ {{< /twsnippet/code >}}
+ {{< /twsnippet/wrapper >}}
+
+
+
+
+
+
+
+
+
+
+
+
+ Controlled stepper
+
+
+
+ Use the activeStep
prop to control the stepper.
+
+
+
+ {{< twsnippet/demo-iframe id="example2" iframe="/components/stepper/examples/stepper-controlled-step" title="Stepper controlled step" >}}
+
+
+
+
+
+ {{< twsnippet/wrapper "JSX" >}}
+ {{< twsnippet/code active=true lang="jsx" >}}
+ import React, { useState } from "react";
+ import { TEStepper, TEStepperStep } from "tw-elements-react";
+
+ export default function StepperControlledStep(): JSX.Element {
+ const [activeStep, setActiveStep] = useState(1);
+
+ return (
+ {
+ setActiveStep(stepId);
+ }}
+ >
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
+ tempor incididunt ut labore et dolore magna aliqua.
+
+
+ Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi
+ ut aliquip ex ea commodo consequat.
+
+
+ Duis aute irure dolor in reprehenderit in voluptate velit esse cillum
+ dolore eu fugiat nulla pariatur.
+
+
+ );
+ }
+ {{< /twsnippet/code >}}
+ {{< /twsnippet/wrapper >}}
+
+
+
+
+
+
+
+
+
+ Vertical stepper
+
+
+
+ Add type='vertical'
to change stepper orientation.
+
+
+
+ {{< twsnippet/demo-iframe id="example3" iframe="/components/stepper/examples/stepper-vertical-example" title="Stepper vertical" >}}
+
+
+
+
+ {{< twsnippet/wrapper "JSX" >}}
+ {{< twsnippet/code active=true lang="jsx" >}}
+ import React from "react";
+ import { TEStepper, TEStepperStep } from "tw-elements-react";
+
+ export default function StepperVerticalExample(): JSX.Element {
+ return (
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
+ tempor incididunt ut labore et dolore magna aliqua.
+
+
+ Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi
+ ut aliquip ex ea commodo consequat.
+
+
+ Duis aute irure dolor in reprehenderit in voluptate velit esse cillum
+ dolore eu fugiat nulla pariatur.
+
+
+ );
+ }
+ {{< /twsnippet/code >}}
+ {{< /twsnippet/wrapper >}}
+
+
+
+
+
+
+
+
+ If you are looking for more advanced options, try
+ Bootstrap Stepper
+ from MDBootstrap.
+
+
diff --git a/src/demo/pages.tsx b/src/demo/pages.tsx
index 13f469b..9271c70 100644
--- a/src/demo/pages.tsx
+++ b/src/demo/pages.tsx
@@ -95,6 +95,7 @@ import ToastsExamples from "./pages/components/toasts/exampleList";
import SelectExamples from "./pages/forms/select/exampleList";
import CarouselExamples from "./pages/components/carousel/exampleList";
import VideoCarouselExamples from "./pages/components/video-carousel/exampleList";
+import StepperExamples from "./pages/components/stepper/exampleList";
interface Pages {
name: string;
@@ -414,6 +415,7 @@ export const examplesPages: Pages[] = [
...SelectExamples,
...CarouselExamples,
...VideoCarouselExamples,
+ ...StepperExamples,
];
export default demoPages;
diff --git a/src/demo/pages/components/stepper/exampleList.tsx b/src/demo/pages/components/stepper/exampleList.tsx
index e69de29..5501faa 100644
--- a/src/demo/pages/components/stepper/exampleList.tsx
+++ b/src/demo/pages/components/stepper/exampleList.tsx
@@ -0,0 +1,22 @@
+import React from "react";
+import StepperBasicExample from "./examples/StepperBasicExample";
+import StepperControlledStep from "./examples/StepperControlledStep";
+import StepperVerticalExample from "./examples/StepperVerticalExample";
+
+export default [
+ {
+ name: "StepperBasicExample",
+ path: "/components/stepper/examples/stepper-basic-example",
+ element: ,
+ },
+ {
+ name: "StepperControlledStep",
+ path: "/components/stepper/examples/stepper-controlled-step",
+ element: ,
+ },
+ {
+ name: "StepperVerticalExample",
+ path: "/components/stepper/examples/stepper-vertical-example",
+ element: ,
+ },
+];
From 56a70b7d866a2bf349827921475cdf1b279b45e6 Mon Sep 17 00:00:00 2001
From: Mateusz Lazaru
Date: Mon, 27 Nov 2023 08:07:50 +0100
Subject: [PATCH 09/19] stepper - added custom validation demo
---
.../pages/components/stepper/StepperPage.tsx | 11 +
.../pages/components/stepper/exampleList.tsx | 6 +
.../stepper/examples/StepperLinear.tsx | 196 ++++++++++++++++++
src/lib/components/Stepper/Stepper.tsx | 2 +
src/lib/components/Stepper/StepperContext.ts | 2 +
.../hooks/useStepperLinearValidation.ts | 28 +++
src/lib/components/Stepper/types.ts | 1 +
7 files changed, 246 insertions(+)
create mode 100644 src/demo/pages/components/stepper/examples/StepperLinear.tsx
create mode 100644 src/lib/components/Stepper/hooks/useStepperLinearValidation.ts
diff --git a/src/demo/pages/components/stepper/StepperPage.tsx b/src/demo/pages/components/stepper/StepperPage.tsx
index 66df066..2433100 100644
--- a/src/demo/pages/components/stepper/StepperPage.tsx
+++ b/src/demo/pages/components/stepper/StepperPage.tsx
@@ -2,6 +2,7 @@ import React from "react";
import StepperControlledStep from "./examples/StepperControlledStep";
import StepperVerticalExample from "./examples/StepperVerticalExample";
import StepperBasicExample from "./examples/StepperBasicExample";
+import StepperLinearExample from "./examples/StepperLinear";
const StepperPage = () => {
return (
@@ -38,6 +39,16 @@ const StepperPage = () => {
+
+
+
+
+ Linear stepper
+
+
+
+
+
);
diff --git a/src/demo/pages/components/stepper/exampleList.tsx b/src/demo/pages/components/stepper/exampleList.tsx
index 5501faa..f301fcd 100644
--- a/src/demo/pages/components/stepper/exampleList.tsx
+++ b/src/demo/pages/components/stepper/exampleList.tsx
@@ -2,6 +2,7 @@ import React from "react";
import StepperBasicExample from "./examples/StepperBasicExample";
import StepperControlledStep from "./examples/StepperControlledStep";
import StepperVerticalExample from "./examples/StepperVerticalExample";
+import StepperLinearExample from "./examples/StepperLinear";
export default [
{
@@ -19,4 +20,9 @@ export default [
path: "/components/stepper/examples/stepper-vertical-example",
element: ,
},
+ {
+ name: "StepperLinearExample",
+ path: "/components/stepper/examples/stepper-linear-example",
+ element: ,
+ },
];
diff --git a/src/demo/pages/components/stepper/examples/StepperLinear.tsx b/src/demo/pages/components/stepper/examples/StepperLinear.tsx
new file mode 100644
index 0000000..6456c0d
--- /dev/null
+++ b/src/demo/pages/components/stepper/examples/StepperLinear.tsx
@@ -0,0 +1,196 @@
+import React, { useState } from "react";
+import { TEStepper, TEStepperStep, TEInput } from "tw-elements-react";
+
+const validTheme = {
+ notchLeadingDefault: "border-green-300 dark:border-green-600",
+ notchMiddleDefault: "border-green-300 dark:border-green-600",
+ notchTrailingDefault: "border-green-300 dark:border-green-600",
+ focusedNotchLeadingDefault:
+ "shadow-[-1px_0_0_#10b339,_0_1px_0_0_#10b339,_0_-1px_0_0_#10b339] border-green-300 dark:border-green-600",
+ focusedNotchMiddleDefault:
+ "shadow-[0_1px_0_0_#10b339] border-green-300 dark:border-green-600",
+ focusedNotchTrailingDefault:
+ "shadow-[1px_0_0_#10b339,_0_1px_0_0_#10b339,_0_-1px_0_0_#10b339] border-green-300 dark:border-green-600",
+ labelDefault: "text-green-500 dark:text-green-600",
+};
+
+const invalidTheme = {
+ notchLeadingDefault: "border-red-300 dark:border-red-600",
+ notchMiddleDefault: "border-red-300 dark:border-red-600",
+ notchTrailingDefault: "border-red-300 dark:border-red-600",
+ focusedNotchLeadingDefault:
+ "shadow-[-1px_0_0_#f87171,_0_1px_0_0_#f87171,_0_-1px_0_0_#f87171] border-red-300 dark:border-red-600",
+ focusedNotchMiddleDefault:
+ "shadow-[0_1px_0_0_#f87171] border-red-300 dark:border-red-600",
+ focusedNotchTrailingDefault:
+ "shadow-[1px_0_0_#f87171,_0_1px_0_0_#f87171,_0_-1px_0_0_#f87171] border-red-300 dark:border-red-600",
+ labelDefault: "text-red-300 dark:text-red-600",
+};
+
+const isFormValid = (formValidity: object) => {
+ return Object.values(formValidity).every((item) => item);
+};
+
+export default function StepperLinearExample() {
+ const [activeStep, setActiveStep] = useState(1);
+ const [isStepValidated, setIsStepValidated] = useState(false);
+
+ const [step1FormValidity, setStep1FormValidity] = useState({
+ email: false,
+ });
+
+ const [step2FormValidity, setStep2FormValidity] = useState({
+ firstName: false,
+ lastName: false,
+ phone: false,
+ });
+
+ const handleStepChange = (stepId: number) => {
+ setIsStepValidated(true);
+ if (
+ (activeStep === 1 && !isFormValid(step1FormValidity)) ||
+ (activeStep === 2 && !isFormValid(step2FormValidity))
+ ) {
+ return;
+ }
+
+ setActiveStep(stepId);
+ setIsStepValidated(false);
+ };
+
+ return (
+
+
+
+
+
+
+
+
+ {
+ handleStepChange(1);
+ }}
+ >
+ Go back
+
+
+ {
+ handleStepChange(3);
+ }}
+ >
+ Next
+
+
+
+
+
+
+ You're all set! We will contact you soon!
+
+
+
+
+ );
+}
diff --git a/src/lib/components/Stepper/Stepper.tsx b/src/lib/components/Stepper/Stepper.tsx
index 450ea46..65486cd 100644
--- a/src/lib/components/Stepper/Stepper.tsx
+++ b/src/lib/components/Stepper/Stepper.tsx
@@ -14,6 +14,7 @@ const TEStepper: React.FC = ({
children,
onChange,
type = "horizontal",
+ linear,
style,
}) => {
const theme = {
@@ -51,6 +52,7 @@ const TEStepper: React.FC = ({
setStepperHeight,
vertical,
stepsAmount: childrenArray.length,
+ linear,
}}
>
void;
vertical: boolean;
stepsAmount: number;
+ linear?: boolean;
}
const StepperContext = createContext({
@@ -18,6 +19,7 @@ const StepperContext = createContext({
setStepperHeight: () => {},
vertical: false,
stepsAmount: 0,
+ linear: false,
});
export default StepperContext;
diff --git a/src/lib/components/Stepper/hooks/useStepperLinearValidation.ts b/src/lib/components/Stepper/hooks/useStepperLinearValidation.ts
new file mode 100644
index 0000000..50d3f2e
--- /dev/null
+++ b/src/lib/components/Stepper/hooks/useStepperLinearValidation.ts
@@ -0,0 +1,28 @@
+import { useContext, useEffect, useState, useMemo } from "react";
+import StepperContext from "../StepperContext";
+
+export default function useStepperLinearValidaTion(
+ itemId: number,
+ contentRef: React.RefObject
+) {
+ const { activeStep, linear, stepsAmount } = useContext(StepperContext);
+ const [isStepValid, setIsStepValid] = useState(true);
+ const isActive = useMemo(() => activeStep === itemId, [activeStep]);
+ useEffect(() => {
+ if (!linear || !isActive) return;
+ // const form = contentRef.current?.querySelector("form");
+ // const inputs = form?.querySelectorAll("input, select, textarea");
+ // const isFormValid = form?.checkValidity();
+ // console.log("isFormValid", isFormValid);
+ // const isInputsValid = Array.from(inputs || []).every((input) =>
+ // input.checkValidity()
+ // );
+ // const isStepValid = isFormValid && isInputsValid;
+ // setIsStepValid(isStepValid);
+
+ // if (!isStepValid) {
+ // form?.reportValidity();
+ // }
+ }, [activeStep, linear, stepsAmount, isStepValid, isActive]);
+ return true;
+}
diff --git a/src/lib/components/Stepper/types.ts b/src/lib/components/Stepper/types.ts
index d6a3cc2..efc6324 100644
--- a/src/lib/components/Stepper/types.ts
+++ b/src/lib/components/Stepper/types.ts
@@ -9,6 +9,7 @@ interface StepperThemeProps {
type StepperProps = Omit & {
activeStep?: number;
defaultStep?: number;
+ linear?: boolean;
theme?: StepperThemeProps;
type?: "horizontal" | "vertical";
onChange?: (id: number) => void;
From 5661ecd5401748f0a542d85774b65675e2306d60 Mon Sep 17 00:00:00 2001
From: Mateusz Lazaru
Date: Tue, 28 Nov 2023 10:36:52 +0100
Subject: [PATCH 10/19] stepper - added 'noEditable' option. Updated styles.
---
.../pages/components/stepper/StepperPage.tsx | 11 +
.../stepper/examples/StepperLinear.tsx | 196 ++++++++++++++++++
.../stepper/examples/StepperNoEditable.tsx | 32 +++
src/lib/components/Stepper/Stepper.tsx | 9 +-
src/lib/components/Stepper/StepperContext.ts | 2 +
.../Stepper/StepperStep/StepperStep.tsx | 27 ++-
.../Stepper/StepperStep/stepperStepTheme.ts | 15 +-
.../Stepper/hooks/useHeadIconClasses.ts | 5 +
src/lib/components/Stepper/types.ts | 1 +
9 files changed, 282 insertions(+), 16 deletions(-)
create mode 100644 src/demo/pages/components/stepper/examples/StepperLinear.tsx
create mode 100644 src/demo/pages/components/stepper/examples/StepperNoEditable.tsx
diff --git a/src/demo/pages/components/stepper/StepperPage.tsx b/src/demo/pages/components/stepper/StepperPage.tsx
index 66df066..44722bd 100644
--- a/src/demo/pages/components/stepper/StepperPage.tsx
+++ b/src/demo/pages/components/stepper/StepperPage.tsx
@@ -2,6 +2,7 @@ import React from "react";
import StepperControlledStep from "./examples/StepperControlledStep";
import StepperVerticalExample from "./examples/StepperVerticalExample";
import StepperBasicExample from "./examples/StepperBasicExample";
+import StepperNoEditable from "./examples/StepperNoEditable";
const StepperPage = () => {
return (
@@ -38,6 +39,16 @@ const StepperPage = () => {
+
+
+
+
+ Stepper with no editable steps
+
+
+
+
+
);
diff --git a/src/demo/pages/components/stepper/examples/StepperLinear.tsx b/src/demo/pages/components/stepper/examples/StepperLinear.tsx
new file mode 100644
index 0000000..6456c0d
--- /dev/null
+++ b/src/demo/pages/components/stepper/examples/StepperLinear.tsx
@@ -0,0 +1,196 @@
+import React, { useState } from "react";
+import { TEStepper, TEStepperStep, TEInput } from "tw-elements-react";
+
+const validTheme = {
+ notchLeadingDefault: "border-green-300 dark:border-green-600",
+ notchMiddleDefault: "border-green-300 dark:border-green-600",
+ notchTrailingDefault: "border-green-300 dark:border-green-600",
+ focusedNotchLeadingDefault:
+ "shadow-[-1px_0_0_#10b339,_0_1px_0_0_#10b339,_0_-1px_0_0_#10b339] border-green-300 dark:border-green-600",
+ focusedNotchMiddleDefault:
+ "shadow-[0_1px_0_0_#10b339] border-green-300 dark:border-green-600",
+ focusedNotchTrailingDefault:
+ "shadow-[1px_0_0_#10b339,_0_1px_0_0_#10b339,_0_-1px_0_0_#10b339] border-green-300 dark:border-green-600",
+ labelDefault: "text-green-500 dark:text-green-600",
+};
+
+const invalidTheme = {
+ notchLeadingDefault: "border-red-300 dark:border-red-600",
+ notchMiddleDefault: "border-red-300 dark:border-red-600",
+ notchTrailingDefault: "border-red-300 dark:border-red-600",
+ focusedNotchLeadingDefault:
+ "shadow-[-1px_0_0_#f87171,_0_1px_0_0_#f87171,_0_-1px_0_0_#f87171] border-red-300 dark:border-red-600",
+ focusedNotchMiddleDefault:
+ "shadow-[0_1px_0_0_#f87171] border-red-300 dark:border-red-600",
+ focusedNotchTrailingDefault:
+ "shadow-[1px_0_0_#f87171,_0_1px_0_0_#f87171,_0_-1px_0_0_#f87171] border-red-300 dark:border-red-600",
+ labelDefault: "text-red-300 dark:text-red-600",
+};
+
+const isFormValid = (formValidity: object) => {
+ return Object.values(formValidity).every((item) => item);
+};
+
+export default function StepperLinearExample() {
+ const [activeStep, setActiveStep] = useState(1);
+ const [isStepValidated, setIsStepValidated] = useState(false);
+
+ const [step1FormValidity, setStep1FormValidity] = useState({
+ email: false,
+ });
+
+ const [step2FormValidity, setStep2FormValidity] = useState({
+ firstName: false,
+ lastName: false,
+ phone: false,
+ });
+
+ const handleStepChange = (stepId: number) => {
+ setIsStepValidated(true);
+ if (
+ (activeStep === 1 && !isFormValid(step1FormValidity)) ||
+ (activeStep === 2 && !isFormValid(step2FormValidity))
+ ) {
+ return;
+ }
+
+ setActiveStep(stepId);
+ setIsStepValidated(false);
+ };
+
+ return (
+
+
+
+
+
+
+
+
+ {
+ handleStepChange(1);
+ }}
+ >
+ Go back
+
+
+ {
+ handleStepChange(3);
+ }}
+ >
+ Next
+
+
+
+
+
+
+ You're all set! We will contact you soon!
+
+
+
+
+ );
+}
diff --git a/src/demo/pages/components/stepper/examples/StepperNoEditable.tsx b/src/demo/pages/components/stepper/examples/StepperNoEditable.tsx
new file mode 100644
index 0000000..986b1a5
--- /dev/null
+++ b/src/demo/pages/components/stepper/examples/StepperNoEditable.tsx
@@ -0,0 +1,32 @@
+import React from "react";
+import { TEStepper, TEStepperStep } from "tw-elements-react";
+
+export default function StepperNoEditable() {
+ return (
+
+
+
+ After changing the active step, the previous steps can't be accessed
+ anymore.
+
+
+
+
+ Lorem ipsum dolor, sit amet consectetur adipisicing elit. Pariatur,
+ tempora esse iusto tempore quod, aspernatur incidunt minima magnam,
+ quaerat aut vel expedita illum molestias repellendus asperiores id
+ suscipit saepe. Maxime.
+
+
+
+
+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam
+ voluptatum, quod, voluptate quia, ipsam illum quibusdam dolorum
+ voluptatibus laboriosam quae voluptates? Quisquam, voluptatibus
+ voluptas. Quisquam, voluptatibus voluptas. Quisquam, voluptatibus
+ voluptas.
+
+
+
+ );
+}
diff --git a/src/lib/components/Stepper/Stepper.tsx b/src/lib/components/Stepper/Stepper.tsx
index 450ea46..33d0ecf 100644
--- a/src/lib/components/Stepper/Stepper.tsx
+++ b/src/lib/components/Stepper/Stepper.tsx
@@ -12,6 +12,7 @@ const TEStepper: React.FC = ({
defaultStep = 1,
activeStep: activeStepProp,
children,
+ noEditable = false,
onChange,
type = "horizontal",
style,
@@ -36,7 +37,8 @@ const TEStepper: React.FC = ({
) as React.ReactElement[];
}, [children]);
- const onChangeHandler = (id: number) => {
+ const stepChangeHandler = (id: number) => {
+ if (noEditable && id < activeStep) return;
onChange?.(id);
setActiveStepState(id);
};
@@ -45,12 +47,13 @@ const TEStepper: React.FC = ({
= ({
itemId: index + 1,
activeStep,
key: "stepper-step-" + index,
- onChange: onChangeHandler,
+ onChange: stepChangeHandler,
});
})}
diff --git a/src/lib/components/Stepper/StepperContext.ts b/src/lib/components/Stepper/StepperContext.ts
index 7d112c4..784359a 100644
--- a/src/lib/components/Stepper/StepperContext.ts
+++ b/src/lib/components/Stepper/StepperContext.ts
@@ -8,6 +8,7 @@ interface StepperContextProps {
setStepperHeight: (height: string) => void;
vertical: boolean;
stepsAmount: number;
+ noEditable?: boolean;
}
const StepperContext = createContext({
@@ -18,6 +19,7 @@ const StepperContext = createContext({
setStepperHeight: () => {},
vertical: false,
stepsAmount: 0,
+ noEditable: false,
});
export default StepperContext;
diff --git a/src/lib/components/Stepper/StepperStep/StepperStep.tsx b/src/lib/components/Stepper/StepperStep/StepperStep.tsx
index a2cf878..52ae3c6 100644
--- a/src/lib/components/Stepper/StepperStep/StepperStep.tsx
+++ b/src/lib/components/Stepper/StepperStep/StepperStep.tsx
@@ -1,4 +1,4 @@
-import React, { useMemo, useRef, useContext } from "react";
+import React, { useState, useMemo, useRef, useContext, useEffect } from "react";
import clx from "clsx";
import StepperStepTheme from "./stepperStepTheme";
import StepperContext from "../StepperContext";
@@ -22,8 +22,9 @@ const TEStepperStep: React.FC = ({
style,
}) => {
const headRef = useRef(null);
+ const [isDisabled, setIsDisabled] = useState(false);
const contentRef = useRef(null);
- const { activeStep, onChange, vertical, stepsAmount } =
+ const { activeStep, noEditable, onChange, vertical, stepsAmount } =
useContext(StepperContext);
const animationDirection = useMemo(() => {
@@ -47,9 +48,22 @@ const TEStepperStep: React.FC = ({
const headIconClasses = useHeadIconClasses(
isActive,
isCompleted,
+ isDisabled,
theme,
vertical
);
+
+ const headTextClasses = clx(
+ isActive ? theme.stepperHeadTextActive : theme.stepperHeadText,
+ isDisabled && theme.disabledStep
+ );
+
+ useEffect(() => {
+ if (isCompleted && noEditable) {
+ setIsDisabled(true);
+ }
+ }, [isCompleted, noEditable]);
+
const stepperHeadClasses = clx(useHeadClasses(theme, itemId), headClassName);
const stepperStepClasses = clx(
vertical
@@ -57,6 +71,7 @@ const TEStepperStep: React.FC = ({
? theme.stepperLastStepVertical
: theme.stepperStepVertical
: theme.stepperStep,
+ isDisabled && theme.disabledStep,
className
);
@@ -87,13 +102,7 @@ const TEStepperStep: React.FC = ({
ref={headRef}
>
{headIcon}
-
- {headText}
-
+ {headText}
{
@@ -11,6 +12,7 @@ const useHeadIconClasses = (
stepperHeadIconVertical,
stepperHeadIconActiveBg,
stepperHeadIconCompletedBg,
+ stepperHeadIconDisabledBg,
} = theme;
const headIconTheme = vertical
@@ -20,6 +22,9 @@ const useHeadIconClasses = (
if (isActive) {
return clsx(headIconTheme, stepperHeadIconActiveBg);
}
+ if (isDisabled) {
+ return clsx(headIconTheme, stepperHeadIconDisabledBg);
+ }
if (isCompleted) {
return clsx(headIconTheme, stepperHeadIconCompletedBg);
}
diff --git a/src/lib/components/Stepper/types.ts b/src/lib/components/Stepper/types.ts
index d6a3cc2..7a5bc9c 100644
--- a/src/lib/components/Stepper/types.ts
+++ b/src/lib/components/Stepper/types.ts
@@ -9,6 +9,7 @@ interface StepperThemeProps {
type StepperProps = Omit & {
activeStep?: number;
defaultStep?: number;
+ noEditable?: boolean;
theme?: StepperThemeProps;
type?: "horizontal" | "vertical";
onChange?: (id: number) => void;
From e4bba77b196f5cfea6795561734e9f7e5b331a0c Mon Sep 17 00:00:00 2001
From: Mateusz Lazaru
Date: Tue, 30 Jan 2024 23:53:29 +0100
Subject: [PATCH 11/19] fix(stepper) - changed vertical step height calculating
to gridTemplateRows animation
---
.../Stepper/StepperStep/StepperStep.tsx | 12 ++++--------
.../Stepper/StepperStep/stepperStepTheme.ts | 17 ++++++++++++-----
2 files changed, 16 insertions(+), 13 deletions(-)
diff --git a/src/lib/components/Stepper/StepperStep/StepperStep.tsx b/src/lib/components/Stepper/StepperStep/StepperStep.tsx
index a2cf878..3f7da8f 100644
--- a/src/lib/components/Stepper/StepperStep/StepperStep.tsx
+++ b/src/lib/components/Stepper/StepperStep/StepperStep.tsx
@@ -7,7 +7,6 @@ import useHeadIconClasses from "../hooks/useHeadIconClasses";
import useIsStepCompleted from "../hooks/useIsStepCompleted";
import useStepperHeight from "../hooks/useHorizontalStepperHeight";
import { getTranslateDirection } from "../utils/utils";
-import useVerticalStepHeight from "../hooks/useVerticalStepHeight";
import useHeadClasses from "../hooks/useHeadClasses";
const TEStepperStep: React.FC = ({
@@ -65,7 +64,8 @@ const TEStepperStep: React.FC = ({
const stepperContentClasses = clx(
vertical ? theme.stepperVerticalContent : theme.stepperContent,
!vertical && theme[dynamicAnimationDirection as keyof typeof theme],
- contentClassName
+ contentClassName,
+ isActive ? "pb-6" : "pb-0"
);
const headClickHandler = () => {
@@ -73,11 +73,6 @@ const TEStepperStep: React.FC = ({
};
useStepperHeight(isActive, headRef, contentRef, vertical, children);
- const verticalStepHeight = useVerticalStepHeight(
- isActive,
- contentRef,
- children
- );
return (
@@ -97,8 +92,9 @@ const TEStepperStep: React.FC = ({
diff --git a/src/lib/components/Stepper/StepperStep/stepperStepTheme.ts b/src/lib/components/Stepper/StepperStep/stepperStepTheme.ts
index 6fe927a..215732b 100644
--- a/src/lib/components/Stepper/StepperStep/stepperStepTheme.ts
+++ b/src/lib/components/Stepper/StepperStep/stepperStepTheme.ts
@@ -2,6 +2,7 @@ interface StepperStepThemeProps {
stepperStep: string;
stepperStepVertical: string;
stepperLastStepVertical: string;
+ disabledStep: string;
stepperHeadHorizontal: string;
stepperFirstStepHeadHorizontal: string;
stepperLastStepHeadHorizontal: string;
@@ -9,6 +10,7 @@ interface StepperStepThemeProps {
stepperHeadIconHorizontal: string;
stepperHeadIconVertical: string;
stepperHeadIconActiveBg: string;
+ stepperHeadIconDisabledBg: string;
stepperHeadIconCompletedBg: string;
stepperHeadText: string;
stepperHeadTextActive: string;
@@ -24,6 +26,7 @@ const StepperStepTheme: StepperStepThemeProps = {
stepperStepVertical:
"relative h-fit after:absolute after:left-[2.45rem] after:top-[3.6rem] after:mt-px after:h-[calc(100%-2.45rem)] after:w-px after:bg-[#e0e0e0] dark:after:bg-neutral-600",
stepperLastStepVertical: "relative h-fit",
+ disabledStep: "pointer-events-none",
stepperHeadHorizontal:
"flex cursor-pointer items-center leading-[1.3rem] no-underline before:mr-2 before:h-px before:w-full before:flex-1 before:bg-[#e0e0e0] before:content-[''] after:ml-2 after:h-px after:w-full after:flex-1 after:bg-[#e0e0e0] after:content-[''] hover:bg-[#f9f9f9] focus:outline-none dark:before:bg-neutral-600 dark:after:bg-neutral-600 dark:hover:bg-[#3b3b3b]",
stepperFirstStepHeadHorizontal:
@@ -33,11 +36,15 @@ const StepperStepTheme: StepperStepThemeProps = {
stepperHeadVertical:
"flex cursor-pointer items-center p-6 leading-[1.3rem] no-underline hover:bg-[#f9f9f9] focus:outline-none dark:hover:bg-[#3b3b3b]",
stepperHeadIconHorizontal:
- "my-6 mr-2 flex h-[1.938rem] w-[1.938rem] items-center justify-center rounded-full bg-[#ebedef] text-sm font-medium text-[#40464f]",
+ "my-6 mr-2 flex h-[1.938rem] w-[1.938rem] items-center justify-center rounded-full bg-[#6d6d6d] text-sm font-medium text-[#fff] dark:bg-[#757575]",
stepperHeadIconVertical:
- "mr-3 flex h-[1.938rem] w-[1.938rem] items-center justify-center rounded-full bg-[#ebedef] text-sm font-medium text-[#40464f]",
- stepperHeadIconCompletedBg: "!bg-success-100 !text-success-700",
- stepperHeadIconActiveBg: "!bg-primary-100 !text-primary-700",
+ "mr-3 flex h-[1.938rem] w-[1.938rem] items-center justify-center rounded-full bg-[#6d6d6d] text-sm font-medium text-[#fff] dark:bg-[#757575]",
+ stepperHeadIconCompletedBg:
+ "!bg-success-100 !text-success-700 dark:!bg-[#04201f] dark:!text-[#72c894]",
+ stepperHeadIconActiveBg:
+ "!bg-primary-100 !text-primary-700 dark:!bg-[#0c1728] dark:!text-[#628dd5]",
+ stepperHeadIconDisabledBg:
+ "!bg-[#6d6d6d] !text-neutral-300 dark:!bg-[#757575]",
stepperHeadText:
"text-neutral-500 after:flex after:text-[0.8rem] dark:text-neutral-300",
stepperHeadTextActive:
@@ -49,7 +56,7 @@ const StepperStepTheme: StepperStepThemeProps = {
stepperContentTranslateLeft: "-translate-x-[150%]",
stepperContentTranslateRight: "translate-x-[150%]",
stepperVerticalContent:
- "transition-[height, margin-bottom, padding-top, padding-bottom] left-0 overflow-hidden pb-6 pl-[3.75rem] pr-6 duration-300 ease-in-out",
+ "transition-[height, margin-bottom, padding-top, padding-bottom] left-0 overflow-hidden pl-[3.75rem] pr-6 duration-300 ease-in-out",
};
export default StepperStepTheme;
From 7fa782d83e0a3227d1173901c0e6c4fcfef595e1 Mon Sep 17 00:00:00 2001
From: Mateusz Lazaru
Date: Mon, 5 Feb 2024 16:30:02 +0100
Subject: [PATCH 12/19] feat(stepper) - added 'linear', 'customValidation',
crated demo examples
---
.../pages/components/stepper/StepperPage.tsx | 11 +
.../pages/components/stepper/exampleList.tsx | 6 +
.../examples/StepperControlledStep.tsx | 4 +-
.../stepper/examples/StepperLinear.tsx | 194 ++--------------
.../StepperLinearCustomValidation.tsx | 218 ++++++++++++++++++
src/lib/components/Stepper/Stepper.tsx | 78 ++++++-
src/lib/components/Stepper/StepperContext.ts | 5 +
.../Stepper/StepperStep/StepperStep.tsx | 37 ++-
.../Stepper/StepperStep/stepperStepTheme.ts | 3 +
.../Stepper/hooks/useHeadIconClasses.ts | 10 +-
.../hooks/useHorizontalStepperHeight.ts | 2 +-
.../hooks/useStepperLinearValidation.ts | 28 ---
.../Stepper/hooks/useVerticalStepHeight.ts | 36 ---
src/lib/components/Stepper/types.ts | 10 +-
src/lib/components/Stepper/utils/utils.ts | 78 ++++++-
15 files changed, 465 insertions(+), 255 deletions(-)
create mode 100644 src/demo/pages/components/stepper/examples/StepperLinearCustomValidation.tsx
delete mode 100644 src/lib/components/Stepper/hooks/useStepperLinearValidation.ts
delete mode 100644 src/lib/components/Stepper/hooks/useVerticalStepHeight.ts
diff --git a/src/demo/pages/components/stepper/StepperPage.tsx b/src/demo/pages/components/stepper/StepperPage.tsx
index 2433100..466766c 100644
--- a/src/demo/pages/components/stepper/StepperPage.tsx
+++ b/src/demo/pages/components/stepper/StepperPage.tsx
@@ -3,6 +3,7 @@ import StepperControlledStep from "./examples/StepperControlledStep";
import StepperVerticalExample from "./examples/StepperVerticalExample";
import StepperBasicExample from "./examples/StepperBasicExample";
import StepperLinearExample from "./examples/StepperLinear";
+import StepperLinearCustomValidationExample from "./examples/StepperLinearCustomValidation";
const StepperPage = () => {
return (
@@ -49,6 +50,16 @@ const StepperPage = () => {
+
+
+
+
+ Linear stepper with custom validation
+
+
+
+
+
);
diff --git a/src/demo/pages/components/stepper/exampleList.tsx b/src/demo/pages/components/stepper/exampleList.tsx
index f301fcd..8b5c2c3 100644
--- a/src/demo/pages/components/stepper/exampleList.tsx
+++ b/src/demo/pages/components/stepper/exampleList.tsx
@@ -3,6 +3,7 @@ import StepperBasicExample from "./examples/StepperBasicExample";
import StepperControlledStep from "./examples/StepperControlledStep";
import StepperVerticalExample from "./examples/StepperVerticalExample";
import StepperLinearExample from "./examples/StepperLinear";
+import StepperLinearCustomValidationExample from "./examples/StepperLinearCustomValidation";
export default [
{
@@ -25,4 +26,9 @@ export default [
path: "/components/stepper/examples/stepper-linear-example",
element: ,
},
+ {
+ name: "StepperLinearCustomValidationExample",
+ path: "/components/stepper/examples/stepper-linear-custom-validation-example",
+ element: ,
+ },
];
diff --git a/src/demo/pages/components/stepper/examples/StepperControlledStep.tsx b/src/demo/pages/components/stepper/examples/StepperControlledStep.tsx
index 6c219dd..ca6d7dc 100644
--- a/src/demo/pages/components/stepper/examples/StepperControlledStep.tsx
+++ b/src/demo/pages/components/stepper/examples/StepperControlledStep.tsx
@@ -7,8 +7,8 @@ export default function StepperControlledStep(): JSX.Element {
return (
{
- setActiveStep(stepId);
+ onChange={(_, next) => {
+ setActiveStep(next);
}}
>
diff --git a/src/demo/pages/components/stepper/examples/StepperLinear.tsx b/src/demo/pages/components/stepper/examples/StepperLinear.tsx
index 6456c0d..a92161f 100644
--- a/src/demo/pages/components/stepper/examples/StepperLinear.tsx
+++ b/src/demo/pages/components/stepper/examples/StepperLinear.tsx
@@ -1,196 +1,42 @@
-import React, { useState } from "react";
+import React from "react";
import { TEStepper, TEStepperStep, TEInput } from "tw-elements-react";
-const validTheme = {
- notchLeadingDefault: "border-green-300 dark:border-green-600",
- notchMiddleDefault: "border-green-300 dark:border-green-600",
- notchTrailingDefault: "border-green-300 dark:border-green-600",
- focusedNotchLeadingDefault:
- "shadow-[-1px_0_0_#10b339,_0_1px_0_0_#10b339,_0_-1px_0_0_#10b339] border-green-300 dark:border-green-600",
- focusedNotchMiddleDefault:
- "shadow-[0_1px_0_0_#10b339] border-green-300 dark:border-green-600",
- focusedNotchTrailingDefault:
- "shadow-[1px_0_0_#10b339,_0_1px_0_0_#10b339,_0_-1px_0_0_#10b339] border-green-300 dark:border-green-600",
- labelDefault: "text-green-500 dark:text-green-600",
-};
-
-const invalidTheme = {
- notchLeadingDefault: "border-red-300 dark:border-red-600",
- notchMiddleDefault: "border-red-300 dark:border-red-600",
- notchTrailingDefault: "border-red-300 dark:border-red-600",
- focusedNotchLeadingDefault:
- "shadow-[-1px_0_0_#f87171,_0_1px_0_0_#f87171,_0_-1px_0_0_#f87171] border-red-300 dark:border-red-600",
- focusedNotchMiddleDefault:
- "shadow-[0_1px_0_0_#f87171] border-red-300 dark:border-red-600",
- focusedNotchTrailingDefault:
- "shadow-[1px_0_0_#f87171,_0_1px_0_0_#f87171,_0_-1px_0_0_#f87171] border-red-300 dark:border-red-600",
- labelDefault: "text-red-300 dark:text-red-600",
-};
-
-const isFormValid = (formValidity: object) => {
- return Object.values(formValidity).every((item) => item);
-};
-
export default function StepperLinearExample() {
- const [activeStep, setActiveStep] = useState(1);
- const [isStepValidated, setIsStepValidated] = useState(false);
-
- const [step1FormValidity, setStep1FormValidity] = useState({
- email: false,
- });
-
- const [step2FormValidity, setStep2FormValidity] = useState({
- firstName: false,
- lastName: false,
- phone: false,
- });
-
- const handleStepChange = (stepId: number) => {
- setIsStepValidated(true);
- if (
- (activeStep === 1 && !isFormValid(step1FormValidity)) ||
- (activeStep === 2 && !isFormValid(step2FormValidity))
- ) {
- return;
- }
-
- setActiveStep(stepId);
- setIsStepValidated(false);
- };
-
return (
-
-
-
-
-
-
+
You're almost there! Just a few more details!
- {
- setStep2FormValidity((prev: object) => {
- return { ...prev, firstName: e.target.checkValidity() };
- });
- }}
- />
+
- {
- setStep2FormValidity((prev: object) => {
- return { ...prev, lastName: e.target.checkValidity() };
- });
- }}
- />
+
- {
- setStep2FormValidity((prev: object) => {
- return { ...prev, phone: e.target.checkValidity() };
- });
- }}
- />
+
-
-
-
- {
- handleStepChange(1);
- }}
- >
- Go back
-
-
- {
- handleStepChange(3);
- }}
- >
- Next
-
-
-
-
-
-
- You're all set! We will contact you soon!
-
-
-
-
+
+
+
+
+ You're all set! We will contact you soon!
+
+
+
+
+
);
}
diff --git a/src/demo/pages/components/stepper/examples/StepperLinearCustomValidation.tsx b/src/demo/pages/components/stepper/examples/StepperLinearCustomValidation.tsx
new file mode 100644
index 0000000..4b3fe54
--- /dev/null
+++ b/src/demo/pages/components/stepper/examples/StepperLinearCustomValidation.tsx
@@ -0,0 +1,218 @@
+import React, { useState } from "react";
+import { TEStepper, TEStepperStep, TEInput } from "tw-elements-react";
+
+const validTheme = {
+ notchLeadingDefault: "border-green-300 dark:border-green-600",
+ notchMiddleDefault: "border-green-300 dark:border-green-600",
+ notchTrailingDefault: "border-green-300 dark:border-green-600",
+ focusedNotchLeadingDefault:
+ "shadow-[-1px_0_0_#10b339,_0_1px_0_0_#10b339,_0_-1px_0_0_#10b339] border-green-300 dark:border-green-600",
+ focusedNotchMiddleDefault:
+ "shadow-[0_1px_0_0_#10b339] border-green-300 dark:border-green-600",
+ focusedNotchTrailingDefault:
+ "shadow-[1px_0_0_#10b339,_0_1px_0_0_#10b339,_0_-1px_0_0_#10b339] border-green-300 dark:border-green-600",
+ labelDefault: "text-green-500 dark:text-green-600",
+};
+
+const invalidTheme = {
+ notchLeadingDefault: "border-red-300 dark:border-red-600",
+ notchMiddleDefault: "border-red-300 dark:border-red-600",
+ notchTrailingDefault: "border-red-300 dark:border-red-600",
+ focusedNotchLeadingDefault:
+ "shadow-[-1px_0_0_#f87171,_0_1px_0_0_#f87171,_0_-1px_0_0_#f87171] border-red-300 dark:border-red-600",
+ focusedNotchMiddleDefault:
+ "shadow-[0_1px_0_0_#f87171] border-red-300 dark:border-red-600",
+ focusedNotchTrailingDefault:
+ "shadow-[1px_0_0_#f87171,_0_1px_0_0_#f87171,_0_-1px_0_0_#f87171] border-red-300 dark:border-red-600",
+ labelDefault: "text-red-300 dark:text-red-600",
+};
+
+type formItemsValidityTypes = {
+ email: boolean | null;
+ firstName: boolean | null;
+ lastName: boolean | null;
+ phone: boolean | null;
+};
+
+export default function StepperLinearCustomValidationExample() {
+ const [formItemsValidity, setFormItemsValidity] =
+ useState({
+ email: false,
+ firstName: false,
+ lastName: false,
+ phone: false,
+ });
+
+ const [validatedSteps, setValidatedSteps] = useState({
+ step1: false,
+ step2: false,
+ step3: false,
+ });
+
+ return (
+
+ );
+}
diff --git a/src/lib/components/Stepper/Stepper.tsx b/src/lib/components/Stepper/Stepper.tsx
index 65486cd..9a7cde3 100644
--- a/src/lib/components/Stepper/Stepper.tsx
+++ b/src/lib/components/Stepper/Stepper.tsx
@@ -5,6 +5,14 @@ import { StepperStepProps } from "./StepperStep/types";
import StepperTheme from "./stepperTheme";
import useActiveValue from "../../hooks/useActiveValue";
import type { StepperProps } from "./types";
+import { validateStepContent, checkStepsBetweenValidity } from "./utils/utils";
+
+interface StepsValidity {
+ [key: string]: {
+ wasValidated: boolean;
+ isValid: boolean;
+ };
+}
const TEStepper: React.FC = ({
theme: customTheme,
@@ -13,9 +21,11 @@ const TEStepper: React.FC = ({
activeStep: activeStepProp,
children,
onChange,
+ onInvalid,
type = "horizontal",
linear,
style,
+ customValidation,
}) => {
const theme = {
...StepperTheme,
@@ -29,6 +39,8 @@ const TEStepper: React.FC = ({
const [activeStepState, setActiveStepState] = useState(defaultStep);
const activeStep = useActiveValue(activeStepProp, activeStepState);
const stepperRef = useRef(null);
+ const [activeStepContent, setActiveStepContent] =
+ useState(null);
const [stepperHeight, setStepperHeight] = useState("auto");
const childrenArray = useMemo(() => {
@@ -37,9 +49,65 @@ const TEStepper: React.FC = ({
) as React.ReactElement[];
}, [children]);
- const onChangeHandler = (id: number) => {
- onChange?.(id);
- setActiveStepState(id);
+ const [stepsValidity, setStepsValidity] = useState(() => {
+ if (!linear) {
+ return {};
+ }
+
+ const obj: StepsValidity = {};
+
+ childrenArray.forEach((_, i) => {
+ obj["step" + Number(i + 1)] = {
+ wasValidated: false,
+ isValid: false,
+ };
+ });
+
+ return obj;
+ });
+
+ const onChangeHandler = (targetStepId: number) => {
+ if (linear) {
+ if (!activeStepContent) {
+ return;
+ }
+
+ const isGoingForward = activeStep < targetStepId;
+
+ const isCurrentStepValid = validateStepContent(
+ activeStepContent as HTMLElement,
+ customValidation
+ );
+
+ const isStepsBetweenValid = checkStepsBetweenValidity(
+ activeStep,
+ targetStepId,
+ stepsValidity,
+ setStepsValidity
+ );
+
+ setStepsValidity((prev) => {
+ return {
+ ...prev,
+
+ ["step" + activeStep]: {
+ wasValidated: true,
+ isValid: isCurrentStepValid,
+ },
+
+ ["step" + targetStepId]: {
+ wasValidated: prev![`step${targetStepId}`].wasValidated,
+ isValid: false,
+ },
+ };
+ });
+ if ((!isCurrentStepValid || !isStepsBetweenValid) && isGoingForward) {
+ onInvalid?.(activeStep, targetStepId);
+ return;
+ }
+ }
+ onChange?.(activeStep, targetStepId);
+ setActiveStepState(targetStepId);
};
return (
<>
@@ -49,7 +117,10 @@ const TEStepper: React.FC = ({
onChange: onChangeHandler,
stepperRef,
stepperHeight,
+ stepsValidity,
setStepperHeight,
+ setActiveStepContent,
+
vertical,
stepsAmount: childrenArray.length,
linear,
@@ -74,4 +145,5 @@ const TEStepper: React.FC = ({
);
};
+export type { StepsValidity };
export default TEStepper;
diff --git a/src/lib/components/Stepper/StepperContext.ts b/src/lib/components/Stepper/StepperContext.ts
index 07040a9..413b5e0 100644
--- a/src/lib/components/Stepper/StepperContext.ts
+++ b/src/lib/components/Stepper/StepperContext.ts
@@ -1,25 +1,30 @@
import { createContext } from "react";
+import { StepsValidity } from "./Stepper";
interface StepperContextProps {
activeStep: number;
onChange?: (id: number) => void;
stepperRef: React.RefObject | null;
+ stepsValidity: StepsValidity | null;
stepperHeight: string;
setStepperHeight: (height: string) => void;
vertical: boolean;
stepsAmount: number;
linear?: boolean;
+ setActiveStepContent: React.Dispatch>;
}
const StepperContext = createContext({
activeStep: 1,
onChange: () => {},
stepperRef: null,
+ stepsValidity: null,
stepperHeight: "0",
setStepperHeight: () => {},
vertical: false,
stepsAmount: 0,
linear: false,
+ setActiveStepContent: () => {},
});
export default StepperContext;
diff --git a/src/lib/components/Stepper/StepperStep/StepperStep.tsx b/src/lib/components/Stepper/StepperStep/StepperStep.tsx
index 3f7da8f..87e1e1d 100644
--- a/src/lib/components/Stepper/StepperStep/StepperStep.tsx
+++ b/src/lib/components/Stepper/StepperStep/StepperStep.tsx
@@ -1,4 +1,4 @@
-import React, { useMemo, useRef, useContext } from "react";
+import React, { useMemo, useRef, useContext, useEffect } from "react";
import clx from "clsx";
import StepperStepTheme from "./stepperStepTheme";
import StepperContext from "../StepperContext";
@@ -20,10 +20,28 @@ const TEStepperStep: React.FC = ({
children,
style,
}) => {
+ const stepRef = useRef(null);
const headRef = useRef(null);
const contentRef = useRef(null);
- const { activeStep, onChange, vertical, stepsAmount } =
- useContext(StepperContext);
+ const {
+ activeStep,
+ onChange,
+ vertical,
+ linear,
+ stepsAmount,
+ setActiveStepContent,
+ stepsValidity,
+ } = useContext(StepperContext);
+
+ const isInvalid = useMemo(() => {
+ if (!linear) {
+ return false;
+ }
+ return (
+ stepsValidity?.["step" + itemId].wasValidated &&
+ !stepsValidity?.["step" + itemId].isValid
+ );
+ }, [stepsValidity, itemId]);
const animationDirection = useMemo(() => {
return getTranslateDirection(activeStep, itemId);
@@ -44,10 +62,11 @@ const TEStepperStep: React.FC = ({
};
const headIconClasses = useHeadIconClasses(
+ theme,
+ vertical,
isActive,
isCompleted,
- theme,
- vertical
+ isInvalid
);
const stepperHeadClasses = clx(useHeadClasses(theme, itemId), headClassName);
const stepperStepClasses = clx(
@@ -68,6 +87,12 @@ const TEStepperStep: React.FC = ({
isActive ? "pb-6" : "pb-0"
);
+ useEffect(() => {
+ if (isActive && contentRef.current && setActiveStepContent) {
+ setActiveStepContent(contentRef.current);
+ }
+ }, [isActive, contentRef, children]);
+
const headClickHandler = () => {
itemId != activeStep && onChange?.(itemId);
};
@@ -75,7 +100,7 @@ const TEStepperStep: React.FC = ({
useStepperHeight(isActive, headRef, contentRef, vertical, children);
return (
-
+
{
const {
stepperHeadIconHorizontal,
stepperHeadIconVertical,
stepperHeadIconActiveBg,
stepperHeadIconCompletedBg,
+ stepperHeadIconInvalidBg,
} = theme;
const headIconTheme = vertical
? stepperHeadIconVertical
: stepperHeadIconHorizontal;
+ if (isInvalid) {
+ return clsx(headIconTheme, stepperHeadIconInvalidBg);
+ }
+
if (isActive) {
return clsx(headIconTheme, stepperHeadIconActiveBg);
}
diff --git a/src/lib/components/Stepper/hooks/useHorizontalStepperHeight.ts b/src/lib/components/Stepper/hooks/useHorizontalStepperHeight.ts
index 801c91d..dfbfba3 100644
--- a/src/lib/components/Stepper/hooks/useHorizontalStepperHeight.ts
+++ b/src/lib/components/Stepper/hooks/useHorizontalStepperHeight.ts
@@ -15,7 +15,7 @@ const useStepperHeight = (
const headHeight = headRef.current?.offsetHeight || 0;
const handleResize = (entries: Array
) => {
- if (!isActive) {
+ if (!isActive || !stepRef.current) {
return;
}
const stepHeight = entries[0].contentRect.height;
diff --git a/src/lib/components/Stepper/hooks/useStepperLinearValidation.ts b/src/lib/components/Stepper/hooks/useStepperLinearValidation.ts
deleted file mode 100644
index 50d3f2e..0000000
--- a/src/lib/components/Stepper/hooks/useStepperLinearValidation.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import { useContext, useEffect, useState, useMemo } from "react";
-import StepperContext from "../StepperContext";
-
-export default function useStepperLinearValidaTion(
- itemId: number,
- contentRef: React.RefObject
-) {
- const { activeStep, linear, stepsAmount } = useContext(StepperContext);
- const [isStepValid, setIsStepValid] = useState(true);
- const isActive = useMemo(() => activeStep === itemId, [activeStep]);
- useEffect(() => {
- if (!linear || !isActive) return;
- // const form = contentRef.current?.querySelector("form");
- // const inputs = form?.querySelectorAll("input, select, textarea");
- // const isFormValid = form?.checkValidity();
- // console.log("isFormValid", isFormValid);
- // const isInputsValid = Array.from(inputs || []).every((input) =>
- // input.checkValidity()
- // );
- // const isStepValid = isFormValid && isInputsValid;
- // setIsStepValid(isStepValid);
-
- // if (!isStepValid) {
- // form?.reportValidity();
- // }
- }, [activeStep, linear, stepsAmount, isStepValid, isActive]);
- return true;
-}
diff --git a/src/lib/components/Stepper/hooks/useVerticalStepHeight.ts b/src/lib/components/Stepper/hooks/useVerticalStepHeight.ts
deleted file mode 100644
index 89a4bc8..0000000
--- a/src/lib/components/Stepper/hooks/useVerticalStepHeight.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-import { useState, useEffect, RefObject } from "react";
-
-export default function useVerticalStepHeight(
- isActive: boolean,
- contentWrapperRef: RefObject,
- children: React.ReactNode | React.ReactNode[]
-) {
- const [height, setHeight] = useState("0px");
-
- useEffect(() => {
- const observer = new ResizeObserver((entries) => {
- if (!isActive) {
- return setHeight("0px");
- }
- const contentWrapperHeight = entries[0].contentRect.height;
- const computed = window.getComputedStyle(
- contentWrapperRef.current as Element
- );
-
- const offsetY =
- parseFloat(computed.paddingTop) +
- parseFloat(computed.paddingBottom) +
- parseFloat(computed.marginBottom) +
- parseFloat(computed.marginTop);
-
- setHeight(`${contentWrapperHeight + offsetY}px`);
- });
-
- observer.observe(contentWrapperRef.current as Element);
- return () => {
- observer.disconnect();
- };
- }, [isActive, children]);
-
- return height;
-}
diff --git a/src/lib/components/Stepper/types.ts b/src/lib/components/Stepper/types.ts
index efc6324..4029e2f 100644
--- a/src/lib/components/Stepper/types.ts
+++ b/src/lib/components/Stepper/types.ts
@@ -6,16 +6,22 @@ interface StepperThemeProps {
stepperVertical: string;
}
+type customValidationType = (
+ validableElement: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement
+) => boolean;
+
type StepperProps = Omit & {
activeStep?: number;
defaultStep?: number;
linear?: boolean;
theme?: StepperThemeProps;
type?: "horizontal" | "vertical";
- onChange?: (id: number) => void;
+ onChange?: (prevStepId: number, nextStepId: number) => void;
+ onInvalid?: (prevStepId: number, nextStepId: number) => void;
children:
| React.ReactElement[]
| React.ReactElement;
+ customValidation?: customValidationType;
};
-export type { StepperProps };
+export type { StepperProps, customValidationType };
diff --git a/src/lib/components/Stepper/utils/utils.ts b/src/lib/components/Stepper/utils/utils.ts
index 3ce0979..c1b0751 100644
--- a/src/lib/components/Stepper/utils/utils.ts
+++ b/src/lib/components/Stepper/utils/utils.ts
@@ -1,3 +1,6 @@
+import type { customValidationType } from "../../Stepper/types";
+import type { StepsValidity } from "../Stepper";
+
const getTranslateDirection = (activeStep: number, step: number) => {
if (activeStep > step) {
return "Left";
@@ -7,4 +10,77 @@ const getTranslateDirection = (activeStep: number, step: number) => {
}
};
-export { getTranslateDirection };
+const validateStepContent = (
+ stepContent: HTMLElement,
+ customValidation?: customValidationType
+): boolean => {
+ let isFormValid = true;
+ const validableElements: HTMLInputElement[] = Array.from(
+ stepContent.querySelectorAll(
+ "input[required], select[required], textarea[required]"
+ )
+ );
+
+ if (customValidation) {
+ validableElements.forEach((el) => {
+ const isElementValid = customValidation(el);
+ if (!isElementValid) {
+ isFormValid = false;
+ }
+ });
+ }
+
+ // in this case we expect native-like validation, which means we'll stop the check
+ // after the first invalid element is found
+ if (!customValidation) {
+ validableElements.every((el) => {
+ const isElementValid = el.checkValidity();
+ if (!isElementValid) {
+ isFormValid = false;
+ el.reportValidity();
+ }
+ return isElementValid;
+ });
+ }
+
+ return isFormValid;
+};
+
+/**
+ * Checks the validity of steps between the active step and the target step.
+ * @return {boolean} Returns true if all the steps between the active step and the target step ID are valid, otherwise false
+ */
+const checkStepsBetweenValidity = (
+ activeStep: number,
+ targetStepId: number,
+ stepsValidity: StepsValidity,
+ setStepsValidity: React.Dispatch>
+) => {
+ if (activeStep > targetStepId) {
+ [activeStep, targetStepId] = [targetStepId, activeStep];
+ }
+
+ for (let step = activeStep + 1; step < targetStepId; step++) {
+ setStepsValidity((prev: StepsValidity) => {
+ return {
+ ...prev,
+ [`step${step}`]: {
+ ...prev[`step${step}`],
+ wasValidated: true,
+ },
+ };
+ });
+
+ if (!stepsValidity[`step${step}`].isValid) {
+ return false;
+ }
+ }
+
+ return true;
+};
+
+export {
+ getTranslateDirection,
+ validateStepContent,
+ checkStepsBetweenValidity,
+};
From 494f0b3546c4de3c2c8d79e4f6b9e43491c8d92a Mon Sep 17 00:00:00 2001
From: Mateusz Lazaru
Date: Tue, 13 Feb 2024 11:16:55 +0100
Subject: [PATCH 13/19] fix(stepper) - fixed types in stepper, stepperTheme,
stepperStepTheme
---
.../Stepper/StepperStep/stepperStepTheme.ts | 44 +++++++++----------
src/lib/components/Stepper/stepperTheme.ts | 8 +++-
src/lib/components/Stepper/types.ts | 8 +---
3 files changed, 31 insertions(+), 29 deletions(-)
diff --git a/src/lib/components/Stepper/StepperStep/stepperStepTheme.ts b/src/lib/components/Stepper/StepperStep/stepperStepTheme.ts
index 92dc65b..3bcd87e 100644
--- a/src/lib/components/Stepper/StepperStep/stepperStepTheme.ts
+++ b/src/lib/components/Stepper/StepperStep/stepperStepTheme.ts
@@ -1,25 +1,25 @@
interface StepperStepThemeProps {
- stepperStep: string;
- stepperStepVertical: string;
- stepperLastStepVertical: string;
- disabledStep: string;
- stepperHeadHorizontal: string;
- stepperFirstStepHeadHorizontal: string;
- stepperLastStepHeadHorizontal: string;
- stepperHeadVertical: string;
- stepperHeadIconHorizontal: string;
- stepperHeadIconVertical: string;
- stepperHeadIconActiveBg: string;
- stepperHeadIconInvalidBg: string;
- stepperHeadIconDisabledBg: string;
- stepperHeadIconCompletedBg: string;
- stepperHeadText: string;
- stepperHeadTextActive: string;
- stepperContent: string;
- stepperContentTranslateLeft: string;
- stepperContentTranslateRight: string;
- stepperVerticalContent: string;
- stepperContentWrapper: string;
+ stepperStep?: string;
+ stepperStepVertical?: string;
+ stepperLastStepVertical?: string;
+ disabledStep?: string;
+ stepperHeadHorizontal?: string;
+ stepperFirstStepHeadHorizontal?: string;
+ stepperLastStepHeadHorizontal?: string;
+ stepperHeadVertical?: string;
+ stepperHeadIconHorizontal?: string;
+ stepperHeadIconVertical?: string;
+ stepperHeadIconActiveBg?: string;
+ stepperHeadIconInvalidBg?: string;
+ stepperHeadIconDisabledBg?: string;
+ stepperHeadIconCompletedBg?: string;
+ stepperHeadText?: string;
+ stepperHeadTextActive?: string;
+ stepperContent?: string;
+ stepperContentTranslateLeft?: string;
+ stepperContentTranslateRight?: string;
+ stepperVerticalContent?: string;
+ stepperContentWrapper?: string;
}
const StepperStepTheme: StepperStepThemeProps = {
@@ -59,7 +59,7 @@ const StepperStepTheme: StepperStepThemeProps = {
stepperContentTranslateLeft: "-translate-x-[150%]",
stepperContentTranslateRight: "translate-x-[150%]",
stepperVerticalContent:
- "transition-[height, margin-bottom, padding-top, padding-bottom] left-0 overflow-hidden pl-[3.75rem] pr-6 duration-300 ease-in-out",
+ "transition-[height,_margin-bottom,_padding-top,_padding-bottom] left-0 overflow-hidden pl-[3.75rem] pr-6 duration-300 ease-in-out",
};
export default StepperStepTheme;
diff --git a/src/lib/components/Stepper/stepperTheme.ts b/src/lib/components/Stepper/stepperTheme.ts
index 1f5fd88..10be936 100644
--- a/src/lib/components/Stepper/stepperTheme.ts
+++ b/src/lib/components/Stepper/stepperTheme.ts
@@ -1,8 +1,14 @@
-const StepperTheme = {
+interface StepperThemeProps {
+ stepperHorizontal?: string;
+ stepperVertical?: string;
+}
+
+const StepperTheme: StepperThemeProps = {
stepperHorizontal:
"relative m-0 flex list-none justify-between overflow-hidden p-0 transition-[height] duration-200 ease-in-out",
stepperVertical:
"relative m-0 w-full list-none overflow-hidden p-0 transition-[height] duration-200 ease-in-out",
};
+export type { StepperThemeProps };
export default StepperTheme;
diff --git a/src/lib/components/Stepper/types.ts b/src/lib/components/Stepper/types.ts
index 4029e2f..ee965fc 100644
--- a/src/lib/components/Stepper/types.ts
+++ b/src/lib/components/Stepper/types.ts
@@ -1,10 +1,6 @@
import { BaseComponent } from "../../types/baseComponent";
import type { StepperStepProps } from "./StepperStep/types";
-
-interface StepperThemeProps {
- stepper: string;
- stepperVertical: string;
-}
+import type { StepperThemeProps } from "./stepperTheme";
type customValidationType = (
validableElement: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement
@@ -24,4 +20,4 @@ type StepperProps = Omit & {
customValidation?: customValidationType;
};
-export type { StepperProps, customValidationType };
+export type { StepperProps, StepperThemeProps, customValidationType };
From f53127a58f59e47aecc0f2cf395b6bb750c26779 Mon Sep 17 00:00:00 2001
From: Mateusz Lazaru
Date: Tue, 13 Feb 2024 11:18:16 +0100
Subject: [PATCH 14/19] refactor(stepper) - minor refactoring, changed inline
style to tailwind class
---
.../components/Stepper/StepperStep/StepperStep.tsx | 14 +++++++-------
src/lib/components/Stepper/hooks/useHeadClasses.ts | 12 +++++++++---
.../Stepper/hooks/useHorizontalStepperHeight.ts | 4 +++-
3 files changed, 19 insertions(+), 11 deletions(-)
diff --git a/src/lib/components/Stepper/StepperStep/StepperStep.tsx b/src/lib/components/Stepper/StepperStep/StepperStep.tsx
index 87e1e1d..0bc9746 100644
--- a/src/lib/components/Stepper/StepperStep/StepperStep.tsx
+++ b/src/lib/components/Stepper/StepperStep/StepperStep.tsx
@@ -12,8 +12,6 @@ import useHeadClasses from "../hooks/useHeadClasses";
const TEStepperStep: React.FC = ({
theme: customTheme,
className,
- contentClassName,
- headClassName,
itemId = 1,
headIcon = "",
headText = "",
@@ -68,7 +66,7 @@ const TEStepperStep: React.FC = ({
isCompleted,
isInvalid
);
- const stepperHeadClasses = clx(useHeadClasses(theme, itemId), headClassName);
+ const stepperHeadClasses = clx(useHeadClasses(theme, itemId));
const stepperStepClasses = clx(
vertical
? isLastStep
@@ -80,10 +78,14 @@ const TEStepperStep: React.FC = ({
);
const dynamicAnimationDirection: string = `stepperContentTranslate${animationDirection}`;
+ const stepperContentWrapperClasses = clx(
+ theme.stepperContentWrapper,
+ isActive ? "visible" : "invisible",
+ vertical ? "grid" : "block"
+ );
const stepperContentClasses = clx(
vertical ? theme.stepperVerticalContent : theme.stepperContent,
!vertical && theme[dynamicAnimationDirection as keyof typeof theme],
- contentClassName,
isActive ? "pb-6" : "pb-0"
);
@@ -117,11 +119,9 @@ const TEStepperStep: React.FC = ({
{children}
diff --git a/src/lib/components/Stepper/hooks/useHeadClasses.ts b/src/lib/components/Stepper/hooks/useHeadClasses.ts
index 87edb5b..cd44ee9 100644
--- a/src/lib/components/Stepper/hooks/useHeadClasses.ts
+++ b/src/lib/components/Stepper/hooks/useHeadClasses.ts
@@ -14,8 +14,14 @@ export default function useHeadClasses(
[itemId, stepsAmount]
);
- if (vertical) return theme.stepperHeadVertical;
- if (isFirstStep) return theme.stepperFirstStepHeadHorizontal;
- if (isLastStep) return theme.stepperLastStepHeadHorizontal;
+ if (vertical) {
+ return theme.stepperHeadVertical;
+ }
+ if (isFirstStep) {
+ return theme.stepperFirstStepHeadHorizontal;
+ }
+ if (isLastStep) {
+ return theme.stepperLastStepHeadHorizontal;
+ }
return theme.stepperHeadHorizontal;
}
diff --git a/src/lib/components/Stepper/hooks/useHorizontalStepperHeight.ts b/src/lib/components/Stepper/hooks/useHorizontalStepperHeight.ts
index dfbfba3..dad07c0 100644
--- a/src/lib/components/Stepper/hooks/useHorizontalStepperHeight.ts
+++ b/src/lib/components/Stepper/hooks/useHorizontalStepperHeight.ts
@@ -11,7 +11,9 @@ const useStepperHeight = (
const { setStepperHeight } = useContext(StepperContext);
useEffect(() => {
- if (vertical) return;
+ if (vertical) {
+ return;
+ }
const headHeight = headRef.current?.offsetHeight || 0;
const handleResize = (entries: Array
) => {
From 571f2f716c3fc43a0a4990ccfbdab8b5f5991b48 Mon Sep 17 00:00:00 2001
From: Mateusz Lazaru
Date: Tue, 13 Feb 2024 13:47:29 +0100
Subject: [PATCH 15/19] doc(stepper) - added stepper linear and linear with
custom validation examples
---
.../react/components/stepper/index-ss.html | 6 +
.../docs/react/components/stepper/index.html | 321 +++++++++++++++++-
2 files changed, 323 insertions(+), 4 deletions(-)
diff --git a/site/content/docs/react/components/stepper/index-ss.html b/site/content/docs/react/components/stepper/index-ss.html
index faff06c..080959c 100644
--- a/site/content/docs/react/components/stepper/index-ss.html
+++ b/site/content/docs/react/components/stepper/index-ss.html
@@ -10,3 +10,9 @@
Vertical
+
+ Linear
+
+
+ Linear with custom validation
+
diff --git a/site/content/docs/react/components/stepper/index.html b/site/content/docs/react/components/stepper/index.html
index 2d3c592..89ad83f 100644
--- a/site/content/docs/react/components/stepper/index.html
+++ b/site/content/docs/react/components/stepper/index.html
@@ -39,7 +39,7 @@
import React from "react";
import { TEStepper, TEStepperStep } from "tw-elements-react";
- export default function StepperBasicExample(): JSX.Element {
+ export default function StepperBasicExample() {
return (
@@ -353,8 +353,8 @@
import React, { useState } from "react";
import { TEStepper, TEStepperStep } from "tw-elements-react";
- export default function StepperControlledStep(): JSX.Element {
- const [activeStep, setActiveStep] = useState(1);
+ export default function StepperControlledStep() {
+ const [activeStep, setActiveStep] = useState(1);
return (
@@ -435,6 +435,319 @@
+
+
+
+
+ Linear stepper
+
+
+
+ Add linear
to enable step validation before proceeding to next step.
+
+
+
+ {{< twsnippet/demo-iframe id="example4" iframe="/components/stepper/examples/stepper-linear-example" title="Stepper linear" >}}
+
+
+
+
+ {{< twsnippet/wrapper "JSX" >}}
+ {{< twsnippet/code active=true lang="jsx" >}}
+ import React from "react";
+ import { TEStepper, TEStepperStep, TEInput } from "tw-elements-react";
+
+ export default function StepperLinearExample() {
+ return (
+
+ );
+ }
+
+ {{< /twsnippet/code >}}
+ {{< /twsnippet/wrapper >}}
+
+
+
+
+
+
+
+
+
+ Linear stepper with custom validation
+
+
+
+ Use customValidation
to enable custom step validation before proceeding to next step.
+
+
+
+ {{< twsnippet/demo-iframe id="example5" iframe="/components/stepper/examples/stepper-linear-custom-validation-example" title="Stepper linear" >}}
+
+
+
+
+ {{< twsnippet/wrapper "JSX" >}}
+ {{< twsnippet/code active=true lang="jsx" >}}
+ import React, { useState } from "react";
+ import { TEStepper, TEStepperStep, TEInput } from "tw-elements-react";
+
+ const validTheme = {
+ notchLeadingDefault: "border-green-300 dark:border-green-600",
+ notchMiddleDefault: "border-green-300 dark:border-green-600",
+ notchTrailingDefault: "border-green-300 dark:border-green-600",
+ focusedNotchLeadingDefault:
+ "shadow-[-1px_0_0_#10b339,_0_1px_0_0_#10b339,_0_-1px_0_0_#10b339] border-green-300 dark:border-green-600",
+ focusedNotchMiddleDefault:
+ "shadow-[0_1px_0_0_#10b339] border-green-300 dark:border-green-600",
+ focusedNotchTrailingDefault:
+ "shadow-[1px_0_0_#10b339,_0_1px_0_0_#10b339,_0_-1px_0_0_#10b339] border-green-300 dark:border-green-600",
+ labelDefault: "text-green-500 dark:text-green-600",
+ };
+
+ const invalidTheme = {
+ notchLeadingDefault: "border-red-300 dark:border-red-600",
+ notchMiddleDefault: "border-red-300 dark:border-red-600",
+ notchTrailingDefault: "border-red-300 dark:border-red-600",
+ focusedNotchLeadingDefault:
+ "shadow-[-1px_0_0_#f87171,_0_1px_0_0_#f87171,_0_-1px_0_0_#f87171] border-red-300 dark:border-red-600",
+ focusedNotchMiddleDefault:
+ "shadow-[0_1px_0_0_#f87171] border-red-300 dark:border-red-600",
+ focusedNotchTrailingDefault:
+ "shadow-[1px_0_0_#f87171,_0_1px_0_0_#f87171,_0_-1px_0_0_#f87171] border-red-300 dark:border-red-600",
+ labelDefault: "text-red-300 dark:text-red-600",
+ };
+
+ export default function StepperLinearCustomValidationExample() {
+ const [formItemsValidity, setFormItemsValidity] =
+ useState({
+ email: false,
+ firstName: false,
+ lastName: false,
+ phone: false,
+ });
+
+ const [validatedSteps, setValidatedSteps] = useState({
+ step1: false,
+ step2: false,
+ step3: false,
+ });
+
+ return (
+
+ );
+ }
+
+ {{< /twsnippet/code >}}
+ {{< /twsnippet/wrapper >}}
+
+
+
+
+
From c09a7b3360d3ed189f31b512d9bdd888bf08d31f Mon Sep 17 00:00:00 2001
From: Mateusz Lazaru
Date: Thu, 15 Feb 2024 09:20:02 +0100
Subject: [PATCH 16/19] feat(stepper) - added 'noEditable' option
---
.../pages/components/stepper/StepperPage.tsx | 11 +++++++
.../stepper/examples/StepperNoEditable.tsx | 32 +++++++++++++++++++
src/lib/components/Stepper/Stepper.tsx | 7 +++-
src/lib/components/Stepper/StepperContext.ts | 2 ++
.../Stepper/StepperStep/StepperStep.tsx | 29 +++++++++++------
.../Stepper/hooks/useHeadIconClasses.ts | 9 +++++-
6 files changed, 78 insertions(+), 12 deletions(-)
create mode 100644 src/demo/pages/components/stepper/examples/StepperNoEditable.tsx
diff --git a/src/demo/pages/components/stepper/StepperPage.tsx b/src/demo/pages/components/stepper/StepperPage.tsx
index 466766c..4db7b89 100644
--- a/src/demo/pages/components/stepper/StepperPage.tsx
+++ b/src/demo/pages/components/stepper/StepperPage.tsx
@@ -2,6 +2,7 @@ import React from "react";
import StepperControlledStep from "./examples/StepperControlledStep";
import StepperVerticalExample from "./examples/StepperVerticalExample";
import StepperBasicExample from "./examples/StepperBasicExample";
+import StepperNoEditable from "./examples/StepperNoEditable";
import StepperLinearExample from "./examples/StepperLinear";
import StepperLinearCustomValidationExample from "./examples/StepperLinearCustomValidation";
@@ -43,6 +44,16 @@ const StepperPage = () => {
+
+ Stepper with no editable steps
+
+
+
+
+
+
+
+
Linear stepper
diff --git a/src/demo/pages/components/stepper/examples/StepperNoEditable.tsx b/src/demo/pages/components/stepper/examples/StepperNoEditable.tsx
new file mode 100644
index 0000000..986b1a5
--- /dev/null
+++ b/src/demo/pages/components/stepper/examples/StepperNoEditable.tsx
@@ -0,0 +1,32 @@
+import React from "react";
+import { TEStepper, TEStepperStep } from "tw-elements-react";
+
+export default function StepperNoEditable() {
+ return (
+
+
+
+ After changing the active step, the previous steps can't be accessed
+ anymore.
+
+
+
+
+ Lorem ipsum dolor, sit amet consectetur adipisicing elit. Pariatur,
+ tempora esse iusto tempore quod, aspernatur incidunt minima magnam,
+ quaerat aut vel expedita illum molestias repellendus asperiores id
+ suscipit saepe. Maxime.
+
+
+
+
+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam
+ voluptatum, quod, voluptate quia, ipsam illum quibusdam dolorum
+ voluptatibus laboriosam quae voluptates? Quisquam, voluptatibus
+ voluptas. Quisquam, voluptatibus voluptas. Quisquam, voluptatibus
+ voluptas.
+
+
+
+ );
+}
diff --git a/src/lib/components/Stepper/Stepper.tsx b/src/lib/components/Stepper/Stepper.tsx
index 9a7cde3..33b82ad 100644
--- a/src/lib/components/Stepper/Stepper.tsx
+++ b/src/lib/components/Stepper/Stepper.tsx
@@ -20,6 +20,7 @@ const TEStepper: React.FC = ({
defaultStep = 1,
activeStep: activeStepProp,
children,
+ noEditable = false,
onChange,
onInvalid,
type = "horizontal",
@@ -67,6 +68,10 @@ const TEStepper: React.FC = ({
});
const onChangeHandler = (targetStepId: number) => {
+ if (noEditable && targetStepId < activeStep) {
+ return;
+ }
+
if (linear) {
if (!activeStepContent) {
return;
@@ -120,10 +125,10 @@ const TEStepper: React.FC = ({
stepsValidity,
setStepperHeight,
setActiveStepContent,
-
vertical,
stepsAmount: childrenArray.length,
linear,
+ noEditable,
}}
>
>;
+ noEditable?: boolean;
}
const StepperContext = createContext({
@@ -25,6 +26,7 @@ const StepperContext = createContext({
stepsAmount: 0,
linear: false,
setActiveStepContent: () => {},
+ noEditable: false,
});
export default StepperContext;
diff --git a/src/lib/components/Stepper/StepperStep/StepperStep.tsx b/src/lib/components/Stepper/StepperStep/StepperStep.tsx
index 0bc9746..63732a4 100644
--- a/src/lib/components/Stepper/StepperStep/StepperStep.tsx
+++ b/src/lib/components/Stepper/StepperStep/StepperStep.tsx
@@ -1,4 +1,4 @@
-import React, { useMemo, useRef, useContext, useEffect } from "react";
+import React, { useState, useMemo, useRef, useContext, useEffect } from "react";
import clx from "clsx";
import StepperStepTheme from "./stepperStepTheme";
import StepperContext from "../StepperContext";
@@ -18,11 +18,13 @@ const TEStepperStep: React.FC = ({
children,
style,
}) => {
+ const [isDisabled, setIsDisabled] = useState(false);
const stepRef = useRef(null);
const headRef = useRef(null);
const contentRef = useRef(null);
const {
activeStep,
+ noEditable,
onChange,
vertical,
linear,
@@ -64,8 +66,21 @@ const TEStepperStep: React.FC = ({
vertical,
isActive,
isCompleted,
- isInvalid
+ isInvalid,
+ isDisabled
);
+
+ const headTextClasses = clx(
+ isActive ? theme.stepperHeadTextActive : theme.stepperHeadText,
+ isDisabled && theme.disabledStep
+ );
+
+ useEffect(() => {
+ if (isCompleted && noEditable && !isActive) {
+ setIsDisabled(true);
+ }
+ }, [isCompleted, noEditable, isActive]);
+
const stepperHeadClasses = clx(useHeadClasses(theme, itemId));
const stepperStepClasses = clx(
vertical
@@ -73,7 +88,7 @@ const TEStepperStep: React.FC = ({
? theme.stepperLastStepVertical
: theme.stepperStepVertical
: theme.stepperStep,
-
+ isDisabled && theme.disabledStep,
className
);
@@ -109,13 +124,7 @@ const TEStepperStep: React.FC = ({
ref={headRef}
>
{headIcon}
-
- {headText}
-
+ {headText}
{
const {
stepperHeadIconHorizontal,
@@ -13,6 +14,7 @@ const useHeadIconClasses = (
stepperHeadIconActiveBg,
stepperHeadIconCompletedBg,
stepperHeadIconInvalidBg,
+ stepperHeadIconDisabledBg,
} = theme;
const headIconTheme = vertical
@@ -26,6 +28,11 @@ const useHeadIconClasses = (
if (isActive) {
return clsx(headIconTheme, stepperHeadIconActiveBg);
}
+
+ if (isDisabled) {
+ return clsx(headIconTheme, stepperHeadIconDisabledBg);
+ }
+
if (isCompleted) {
return clsx(headIconTheme, stepperHeadIconCompletedBg);
}
From 7971810198ae351d551ff5df2855b6cae1a47949 Mon Sep 17 00:00:00 2001
From: Mateusz Lazaru
Date: Thu, 15 Feb 2024 09:20:39 +0100
Subject: [PATCH 17/19] Revert "stepper - added 'noEditable' option. Updated
styles."
This reverts commit 5661ecd5401748f0a542d85774b65675e2306d60.
---
.../pages/components/stepper/StepperPage.tsx | 11 -
.../stepper/examples/StepperLinear.tsx | 196 ------------------
.../stepper/examples/StepperNoEditable.tsx | 32 ---
src/lib/components/Stepper/Stepper.tsx | 9 +-
src/lib/components/Stepper/StepperContext.ts | 2 -
.../Stepper/StepperStep/StepperStep.tsx | 27 +--
.../Stepper/StepperStep/stepperStepTheme.ts | 15 +-
.../Stepper/hooks/useHeadIconClasses.ts | 5 -
src/lib/components/Stepper/types.ts | 1 -
9 files changed, 16 insertions(+), 282 deletions(-)
delete mode 100644 src/demo/pages/components/stepper/examples/StepperLinear.tsx
delete mode 100644 src/demo/pages/components/stepper/examples/StepperNoEditable.tsx
diff --git a/src/demo/pages/components/stepper/StepperPage.tsx b/src/demo/pages/components/stepper/StepperPage.tsx
index 44722bd..66df066 100644
--- a/src/demo/pages/components/stepper/StepperPage.tsx
+++ b/src/demo/pages/components/stepper/StepperPage.tsx
@@ -2,7 +2,6 @@ import React from "react";
import StepperControlledStep from "./examples/StepperControlledStep";
import StepperVerticalExample from "./examples/StepperVerticalExample";
import StepperBasicExample from "./examples/StepperBasicExample";
-import StepperNoEditable from "./examples/StepperNoEditable";
const StepperPage = () => {
return (
@@ -39,16 +38,6 @@ const StepperPage = () => {
-
-
-
-
- Stepper with no editable steps
-
-
-
-
-
);
diff --git a/src/demo/pages/components/stepper/examples/StepperLinear.tsx b/src/demo/pages/components/stepper/examples/StepperLinear.tsx
deleted file mode 100644
index 6456c0d..0000000
--- a/src/demo/pages/components/stepper/examples/StepperLinear.tsx
+++ /dev/null
@@ -1,196 +0,0 @@
-import React, { useState } from "react";
-import { TEStepper, TEStepperStep, TEInput } from "tw-elements-react";
-
-const validTheme = {
- notchLeadingDefault: "border-green-300 dark:border-green-600",
- notchMiddleDefault: "border-green-300 dark:border-green-600",
- notchTrailingDefault: "border-green-300 dark:border-green-600",
- focusedNotchLeadingDefault:
- "shadow-[-1px_0_0_#10b339,_0_1px_0_0_#10b339,_0_-1px_0_0_#10b339] border-green-300 dark:border-green-600",
- focusedNotchMiddleDefault:
- "shadow-[0_1px_0_0_#10b339] border-green-300 dark:border-green-600",
- focusedNotchTrailingDefault:
- "shadow-[1px_0_0_#10b339,_0_1px_0_0_#10b339,_0_-1px_0_0_#10b339] border-green-300 dark:border-green-600",
- labelDefault: "text-green-500 dark:text-green-600",
-};
-
-const invalidTheme = {
- notchLeadingDefault: "border-red-300 dark:border-red-600",
- notchMiddleDefault: "border-red-300 dark:border-red-600",
- notchTrailingDefault: "border-red-300 dark:border-red-600",
- focusedNotchLeadingDefault:
- "shadow-[-1px_0_0_#f87171,_0_1px_0_0_#f87171,_0_-1px_0_0_#f87171] border-red-300 dark:border-red-600",
- focusedNotchMiddleDefault:
- "shadow-[0_1px_0_0_#f87171] border-red-300 dark:border-red-600",
- focusedNotchTrailingDefault:
- "shadow-[1px_0_0_#f87171,_0_1px_0_0_#f87171,_0_-1px_0_0_#f87171] border-red-300 dark:border-red-600",
- labelDefault: "text-red-300 dark:text-red-600",
-};
-
-const isFormValid = (formValidity: object) => {
- return Object.values(formValidity).every((item) => item);
-};
-
-export default function StepperLinearExample() {
- const [activeStep, setActiveStep] = useState(1);
- const [isStepValidated, setIsStepValidated] = useState(false);
-
- const [step1FormValidity, setStep1FormValidity] = useState({
- email: false,
- });
-
- const [step2FormValidity, setStep2FormValidity] = useState({
- firstName: false,
- lastName: false,
- phone: false,
- });
-
- const handleStepChange = (stepId: number) => {
- setIsStepValidated(true);
- if (
- (activeStep === 1 && !isFormValid(step1FormValidity)) ||
- (activeStep === 2 && !isFormValid(step2FormValidity))
- ) {
- return;
- }
-
- setActiveStep(stepId);
- setIsStepValidated(false);
- };
-
- return (
-
-
-
-
-
-
-
-
- {
- handleStepChange(1);
- }}
- >
- Go back
-
-
- {
- handleStepChange(3);
- }}
- >
- Next
-
-
-
-
-
-
- You're all set! We will contact you soon!
-
-
-
-
- );
-}
diff --git a/src/demo/pages/components/stepper/examples/StepperNoEditable.tsx b/src/demo/pages/components/stepper/examples/StepperNoEditable.tsx
deleted file mode 100644
index 986b1a5..0000000
--- a/src/demo/pages/components/stepper/examples/StepperNoEditable.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-import React from "react";
-import { TEStepper, TEStepperStep } from "tw-elements-react";
-
-export default function StepperNoEditable() {
- return (
-
-
-
- After changing the active step, the previous steps can't be accessed
- anymore.
-
-
-
-
- Lorem ipsum dolor, sit amet consectetur adipisicing elit. Pariatur,
- tempora esse iusto tempore quod, aspernatur incidunt minima magnam,
- quaerat aut vel expedita illum molestias repellendus asperiores id
- suscipit saepe. Maxime.
-
-
-
-
- Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam
- voluptatum, quod, voluptate quia, ipsam illum quibusdam dolorum
- voluptatibus laboriosam quae voluptates? Quisquam, voluptatibus
- voluptas. Quisquam, voluptatibus voluptas. Quisquam, voluptatibus
- voluptas.
-
-
-
- );
-}
diff --git a/src/lib/components/Stepper/Stepper.tsx b/src/lib/components/Stepper/Stepper.tsx
index 33d0ecf..450ea46 100644
--- a/src/lib/components/Stepper/Stepper.tsx
+++ b/src/lib/components/Stepper/Stepper.tsx
@@ -12,7 +12,6 @@ const TEStepper: React.FC = ({
defaultStep = 1,
activeStep: activeStepProp,
children,
- noEditable = false,
onChange,
type = "horizontal",
style,
@@ -37,8 +36,7 @@ const TEStepper: React.FC = ({
) as React.ReactElement[];
}, [children]);
- const stepChangeHandler = (id: number) => {
- if (noEditable && id < activeStep) return;
+ const onChangeHandler = (id: number) => {
onChange?.(id);
setActiveStepState(id);
};
@@ -47,13 +45,12 @@ const TEStepper: React.FC = ({
= ({
itemId: index + 1,
activeStep,
key: "stepper-step-" + index,
- onChange: stepChangeHandler,
+ onChange: onChangeHandler,
});
})}
diff --git a/src/lib/components/Stepper/StepperContext.ts b/src/lib/components/Stepper/StepperContext.ts
index 784359a..7d112c4 100644
--- a/src/lib/components/Stepper/StepperContext.ts
+++ b/src/lib/components/Stepper/StepperContext.ts
@@ -8,7 +8,6 @@ interface StepperContextProps {
setStepperHeight: (height: string) => void;
vertical: boolean;
stepsAmount: number;
- noEditable?: boolean;
}
const StepperContext = createContext({
@@ -19,7 +18,6 @@ const StepperContext = createContext({
setStepperHeight: () => {},
vertical: false,
stepsAmount: 0,
- noEditable: false,
});
export default StepperContext;
diff --git a/src/lib/components/Stepper/StepperStep/StepperStep.tsx b/src/lib/components/Stepper/StepperStep/StepperStep.tsx
index 52ae3c6..a2cf878 100644
--- a/src/lib/components/Stepper/StepperStep/StepperStep.tsx
+++ b/src/lib/components/Stepper/StepperStep/StepperStep.tsx
@@ -1,4 +1,4 @@
-import React, { useState, useMemo, useRef, useContext, useEffect } from "react";
+import React, { useMemo, useRef, useContext } from "react";
import clx from "clsx";
import StepperStepTheme from "./stepperStepTheme";
import StepperContext from "../StepperContext";
@@ -22,9 +22,8 @@ const TEStepperStep: React.FC = ({
style,
}) => {
const headRef = useRef(null);
- const [isDisabled, setIsDisabled] = useState(false);
const contentRef = useRef(null);
- const { activeStep, noEditable, onChange, vertical, stepsAmount } =
+ const { activeStep, onChange, vertical, stepsAmount } =
useContext(StepperContext);
const animationDirection = useMemo(() => {
@@ -48,22 +47,9 @@ const TEStepperStep: React.FC = ({
const headIconClasses = useHeadIconClasses(
isActive,
isCompleted,
- isDisabled,
theme,
vertical
);
-
- const headTextClasses = clx(
- isActive ? theme.stepperHeadTextActive : theme.stepperHeadText,
- isDisabled && theme.disabledStep
- );
-
- useEffect(() => {
- if (isCompleted && noEditable) {
- setIsDisabled(true);
- }
- }, [isCompleted, noEditable]);
-
const stepperHeadClasses = clx(useHeadClasses(theme, itemId), headClassName);
const stepperStepClasses = clx(
vertical
@@ -71,7 +57,6 @@ const TEStepperStep: React.FC = ({
? theme.stepperLastStepVertical
: theme.stepperStepVertical
: theme.stepperStep,
- isDisabled && theme.disabledStep,
className
);
@@ -102,7 +87,13 @@ const TEStepperStep: React.FC = ({
ref={headRef}
>
{headIcon}
- {headText}
+
+ {headText}
+
{
@@ -12,7 +11,6 @@ const useHeadIconClasses = (
stepperHeadIconVertical,
stepperHeadIconActiveBg,
stepperHeadIconCompletedBg,
- stepperHeadIconDisabledBg,
} = theme;
const headIconTheme = vertical
@@ -22,9 +20,6 @@ const useHeadIconClasses = (
if (isActive) {
return clsx(headIconTheme, stepperHeadIconActiveBg);
}
- if (isDisabled) {
- return clsx(headIconTheme, stepperHeadIconDisabledBg);
- }
if (isCompleted) {
return clsx(headIconTheme, stepperHeadIconCompletedBg);
}
diff --git a/src/lib/components/Stepper/types.ts b/src/lib/components/Stepper/types.ts
index 7a5bc9c..d6a3cc2 100644
--- a/src/lib/components/Stepper/types.ts
+++ b/src/lib/components/Stepper/types.ts
@@ -9,7 +9,6 @@ interface StepperThemeProps {
type StepperProps = Omit & {
activeStep?: number;
defaultStep?: number;
- noEditable?: boolean;
theme?: StepperThemeProps;
type?: "horizontal" | "vertical";
onChange?: (id: number) => void;
From 6e2612b0a65201294f0eb8cc0b78961dd2c05ff9 Mon Sep 17 00:00:00 2001
From: Mateusz Lazaru
Date: Thu, 15 Feb 2024 09:31:49 +0100
Subject: [PATCH 18/19] stepper - added padding to stepperContentWrapper
---
src/lib/components/Stepper/StepperStep/StepperStep.tsx | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/lib/components/Stepper/StepperStep/StepperStep.tsx b/src/lib/components/Stepper/StepperStep/StepperStep.tsx
index 63732a4..e5f7b78 100644
--- a/src/lib/components/Stepper/StepperStep/StepperStep.tsx
+++ b/src/lib/components/Stepper/StepperStep/StepperStep.tsx
@@ -96,12 +96,12 @@ const TEStepperStep: React.FC = ({
const stepperContentWrapperClasses = clx(
theme.stepperContentWrapper,
isActive ? "visible" : "invisible",
- vertical ? "grid" : "block"
+ vertical ? "grid" : "block",
+ isActive ? "pb-6" : "pb-0"
);
const stepperContentClasses = clx(
vertical ? theme.stepperVerticalContent : theme.stepperContent,
- !vertical && theme[dynamicAnimationDirection as keyof typeof theme],
- isActive ? "pb-6" : "pb-0"
+ !vertical && theme[dynamicAnimationDirection as keyof typeof theme]
);
useEffect(() => {
From d224658537133f52b334f8957397d0a62b144d9b Mon Sep 17 00:00:00 2001
From: Mateusz Lazaru
Date: Thu, 15 Feb 2024 10:50:50 +0100
Subject: [PATCH 19/19] doc(stepper) - added noEditable demo, updated api
section
---
.../docs/react/components/stepper/a.html | 181 +++++++++++++-----
.../react/components/stepper/index-ss.html | 3 +
.../docs/react/components/stepper/index.html | 69 ++++++-
site/static/search-react.json | 6 +
.../pages/components/stepper/exampleList.tsx | 6 +
.../components/Stepper/StepperStep/types.ts | 2 -
src/lib/components/Stepper/types.ts | 4 +-
7 files changed, 216 insertions(+), 55 deletions(-)
diff --git a/site/content/docs/react/components/stepper/a.html b/site/content/docs/react/components/stepper/a.html
index becbf99..9fab41e 100644
--- a/site/content/docs/react/components/stepper/a.html
+++ b/site/content/docs/react/components/stepper/a.html
@@ -95,6 +95,24 @@
In most cases the value should be managed with onChange
handler.
+
+
+ customValidation
+
+
+ (validableElement: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement) => boolean;
+
+
+ -
+
+
+ Sets custom validation for each validable element in linear stepper.
+
+
+
+
+ linear
+
+
+ bolean
+
+
+ false
+
+
+ Linear stepper prevents going back to the previous step after it was completed.
+
+
+
+
+ type
+
+
+ 'vertical' | 'horizontal'
+
+
+ 'horizontal'
+
+
+ Sets stepper view mode.
+
+
@@ -171,42 +225,6 @@
-
-
- contentClassName
-
-
- String
-
-
- ''
-
-
- Adds custom classes to the content wrapper.
-
-
-
-
- headClassName
-
-
- String
-
-
- ''
-
-
- Adds custom classes to the step head.
-
-
stepperHorizontal
- "relative m-0 flex list-none justify-between overflow-hidden p-0 transition-[height] duration-200 ease-in-out",
+ relative m-0 flex list-none justify-between overflow-hidden p-0 transition-[height] duration-200 ease-in-out
Sets styles to the TEStepper
element in horizontal mode.
@@ -330,7 +348,7 @@
stepperVertical
- "relative m-0 w-full list-none overflow-hidden p-0 transition-[height] duration-200 ease-in-out",
+ relative m-0 w-full list-none overflow-hidden p-0 transition-[height] duration-200 ease-in-out
Sets styles to the TEStepper
element in vertical mode.
@@ -406,6 +424,17 @@
Sets styles to the last stepperStep
element in vertical mode.
+
+
+ disabledStep
+
+
+ pointer-events-none
+
+
+ Sets styles to disabled step in noEditable
mode.
+
+
stepperHeadHorizontal
@@ -455,7 +484,7 @@
stepperHeadIconHorizontal
- my-6 mr-2 flex h-[1.938rem] w-[1.938rem] items-center justify-center rounded-full bg-[#ebedef] text-sm font-medium text-[#40464f]
+ my-6 mr-2 flex h-[1.938rem] w-[1.938rem] items-center justify-center rounded-full bg-[#6d6d6d] text-sm font-medium text-[#fff] dark:bg-[#757575]
Sets styles to the stepperHeadIcon
element in horizontal mode.
@@ -466,7 +495,7 @@
stepperHeadIconVertical
- mr-3 flex h-[1.938rem] w-[1.938rem] items-center justify-center rounded-full bg-[#ebedef] text-sm font-medium text-[#40464f]
+ mr-3 flex h-[1.938rem] w-[1.938rem] items-center justify-center rounded-full bg-[#6d6d6d] text-sm font-medium text-[#fff] dark:bg-[#757575]
Sets styles to the stepperHeadIcon
element in vertical mode.
@@ -477,7 +506,7 @@
stepperHeadIconCompletedBg
- !bg-success-100 !text-success-700
+ !bg-success-100 !text-success-700 dark:!bg-[#04201f] dark:!text-[#72c894]
Sets background color applied to the stepperHeadIcon
element when step is completed.
@@ -488,10 +517,32 @@
stepperHeadIconActiveBg
- !bg-primary-100 !text-primary-700
+ !bg-primary-100 !text-primary-700 dark:!bg-[#0c1728] dark:!text-[#628dd5]
- Sets background color applied to the stepperHeadIcon
element when step is active.
+ Sets background color applied to the stepperHeadIcon
element when the step is active.
+
+
+
+
+ stepperHeadIconInvalidBg
+
+
+ !bg-danger-100 !text-danger-700 dark:!bg-[#2c0f14] dark:!text-[#e37083]
+
+
+ Sets background color applied to the stepperHeadIcon
element when the step is invalid.
+
+
+
+
+ stepperHeadIconDisabledBg
+
+
+ !bg-[#6d6d6d] !text-neutral-300 dark:!bg-[#757575]
+
+
+ Sets background color applied to the stepperHeadIcon
element when the step is disabled.
@@ -543,7 +594,7 @@
stepperVerticalContent
- transition-[height, margin-bottom, padding-top, padding-bottom] left-0 overflow-hidden pb-6 pl-[3.75rem] pr-6 duration-300 ease-in-out
+ transition-[height,_margin-bottom,_padding-top,_padding-bottom] left-0 overflow-hidden pl-[3.75rem] pr-6 duration-300 ease-in-out
Sets styles to the stepperContent
element in vertical mode.
@@ -603,7 +654,17 @@
- Event type
+ Name
+
+
+ Type
+
+
+ Default
Description
@@ -611,12 +672,38 @@
+
+ onChange
+
+
+ (prevStepId: number, targetStepId: number) => void;
+
+
+ -
+
+
+ Event fires when the stepper demands to change step.
+
+
+
+
+ onInvalid
+
- onChange?: (id: number) => void
+ (prevStepId: number, targetStepId: number) => void;
+
+
+ -
- Fired when stepper demands to change the active step. It can be triggered in TEStepper
element.
+ Event fires when the stepper demands to change step, but linear option prevents it.
diff --git a/site/content/docs/react/components/stepper/index-ss.html b/site/content/docs/react/components/stepper/index-ss.html
index 080959c..5238d28 100644
--- a/site/content/docs/react/components/stepper/index-ss.html
+++ b/site/content/docs/react/components/stepper/index-ss.html
@@ -10,6 +10,9 @@
Vertical
+
+ No editable
+
Linear
diff --git a/site/content/docs/react/components/stepper/index.html b/site/content/docs/react/components/stepper/index.html
index 89ad83f..7128596 100644
--- a/site/content/docs/react/components/stepper/index.html
+++ b/site/content/docs/react/components/stepper/index.html
@@ -435,6 +435,67 @@
+
+
+
+
+ No editable stepper
+
+
+
+ Add noEditable
to disable editing a completed step.
+
+
+
+ {{< twsnippet/demo-iframe id="example4" iframe="/components/stepper/examples/stepper-no-editable-example" title="Stepper no editable" >}}
+
+
+
+
+ {{< twsnippet/wrapper "JSX" >}}
+ {{< twsnippet/code active=true lang="jsx" >}}
+ import React from "react";
+ import { TEStepper, TEStepperStep } from "tw-elements-react";
+
+ export default function StepperNoEditable() {
+ return (
+
+
+
+ After changing the active step, the previous steps can't be accessed
+ anymore.
+
+
+
+
+ Lorem ipsum dolor, sit amet consectetur adipisicing elit. Pariatur,
+ tempora esse iusto tempore quod, aspernatur incidunt minima magnam,
+ quaerat aut vel expedita illum molestias repellendus asperiores id
+ suscipit saepe. Maxime.
+
+
+
+
+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam
+ voluptatum, quod, voluptate quia, ipsam illum quibusdam dolorum
+ voluptatibus laboriosam quae voluptates? Quisquam, voluptatibus
+ voluptas. Quisquam, voluptatibus voluptas. Quisquam, voluptatibus
+ voluptas.
+
+
+
+ );
+ }
+ {{< /twsnippet/code >}}
+ {{< /twsnippet/wrapper >}}
+
+
+
+
+
- {{< twsnippet/demo-iframe id="example4" iframe="/components/stepper/examples/stepper-linear-example" title="Stepper linear" >}}
+ {{< twsnippet/demo-iframe id="example5" iframe="/components/stepper/examples/stepper-linear-example" title="Stepper linear" >}}
-
+
{{< twsnippet/wrapper "JSX" >}}
{{< twsnippet/code active=true lang="jsx" >}}
@@ -522,10 +583,10 @@
- {{< twsnippet/demo-iframe id="example5" iframe="/components/stepper/examples/stepper-linear-custom-validation-example" title="Stepper linear" >}}
+ {{< twsnippet/demo-iframe id="example6" iframe="/components/stepper/examples/stepper-linear-custom-validation-example" title="Stepper linear" >}}
-
+
{{< twsnippet/wrapper "JSX" >}}
{{< twsnippet/code active=true lang="jsx" >}}
diff --git a/site/static/search-react.json b/site/static/search-react.json
index 4d72ed4..cb4c4c7 100644
--- a/site/static/search-react.json
+++ b/site/static/search-react.json
@@ -137,6 +137,12 @@
"keywords": ["loading"],
"category": "Components"
},
+ {
+ "href": "/docs/react/components/stepper/",
+ "name": "Stepper",
+ "keywords": ["stepper", "progress", "steps", "wizard"],
+ "category": "Components"
+ },
{
"href": "/docs/react/forms/checkbox/",
"name": "Checkbox",
diff --git a/src/demo/pages/components/stepper/exampleList.tsx b/src/demo/pages/components/stepper/exampleList.tsx
index 8b5c2c3..aa8f7d0 100644
--- a/src/demo/pages/components/stepper/exampleList.tsx
+++ b/src/demo/pages/components/stepper/exampleList.tsx
@@ -4,6 +4,7 @@ import StepperControlledStep from "./examples/StepperControlledStep";
import StepperVerticalExample from "./examples/StepperVerticalExample";
import StepperLinearExample from "./examples/StepperLinear";
import StepperLinearCustomValidationExample from "./examples/StepperLinearCustomValidation";
+import StepperNoEditable from "./examples/StepperNoEditable";
export default [
{
@@ -21,6 +22,11 @@ export default [
path: "/components/stepper/examples/stepper-vertical-example",
element: ,
},
+ {
+ name: "StepperNoEditable",
+ path: "/components/stepper/examples/stepper-no-editable-example",
+ element: ,
+ },
{
name: "StepperLinearExample",
path: "/components/stepper/examples/stepper-linear-example",
diff --git a/src/lib/components/Stepper/StepperStep/types.ts b/src/lib/components/Stepper/StepperStep/types.ts
index 4934ef4..fb8fa7f 100644
--- a/src/lib/components/Stepper/StepperStep/types.ts
+++ b/src/lib/components/Stepper/StepperStep/types.ts
@@ -5,8 +5,6 @@ import type { StepperStepThemeProps } from "./stepperStepTheme";
interface StepperStepProps extends BaseComponent {
theme?: StepperStepThemeProps;
- headClassName?: string;
- contentClassName?: string;
itemId?: number;
headIcon?: React.ReactNode;
headText?: React.ReactNode;
diff --git a/src/lib/components/Stepper/types.ts b/src/lib/components/Stepper/types.ts
index ee965fc..55c8b1e 100644
--- a/src/lib/components/Stepper/types.ts
+++ b/src/lib/components/Stepper/types.ts
@@ -12,8 +12,8 @@ type StepperProps = Omit & {
linear?: boolean;
theme?: StepperThemeProps;
type?: "horizontal" | "vertical";
- onChange?: (prevStepId: number, nextStepId: number) => void;
- onInvalid?: (prevStepId: number, nextStepId: number) => void;
+ onChange?: (prevStepId: number, targetStepId: number) => void;
+ onInvalid?: (prevStepId: number, targetStepId: number) => void;
children:
| React.ReactElement[]
| React.ReactElement;