From 0864693d1883516f16ab62cd88be5a458df5a7eb Mon Sep 17 00:00:00 2001 From: Vin Bui Date: Thu, 12 Dec 2024 08:38:39 -0500 Subject: [PATCH] WEB-21: Add button states --- package.json | 2 +- src/app/globals.css | 2 +- .../announcement/announcementCell.tsx | 6 +- .../announcement/announcementForm.tsx | 12 +-- .../announcement/announcementModal.tsx | 10 +- src/components/landing/landingPastSection.tsx | 6 +- src/components/past/pastFilter.tsx | 6 +- .../system/button/buttonPrimary1.tsx | 100 +++++++++++++++--- .../system/button/buttonPrimary2.tsx | 36 ++++++- .../system/button/buttonPrimary3.tsx | 43 +++++++- .../system/button/buttonSecondary1.tsx | 88 +++++++++++++-- .../system/button/buttonSecondary2.tsx | 41 ++++++- .../system/button/buttonTertiary.tsx | 43 +++++++- src/icons/CaretRightIcon.tsx | 13 --- yarn.lock | 18 ++-- 15 files changed, 347 insertions(+), 79 deletions(-) delete mode 100644 src/icons/CaretRightIcon.tsx diff --git a/package.json b/package.json index cdac08b..02c0bd2 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "clsx": "^2.1.1", "cmdk": "1.0.0", "date-fns": "^4.1.0", - "framer-motion": "^11.13.1", + "framer-motion": "^11.14.0", "lucide-react": "^0.468.0", "next": "^15.0.4-canary.36", "react": "^18", diff --git a/src/app/globals.css b/src/app/globals.css index d49653f..f615bac 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -5,7 +5,7 @@ @layer base { /* Effects */ .opacity-hover { - @apply cursor-pointer hover:opacity-80 transition-all; + @apply cursor-pointer hover:opacity-80 transition-all duration-300; } /* Mobile */ diff --git a/src/components/announcement/announcementCell.tsx b/src/components/announcement/announcementCell.tsx index 1f9ca61..ad07a1c 100644 --- a/src/components/announcement/announcementCell.tsx +++ b/src/components/announcement/announcementCell.tsx @@ -1,9 +1,9 @@ import AppIcon from "@/icons/appIcon"; -import ButtonTertiary from "../system/button/buttonTertiary"; import { Announcement } from "@/models/announcement"; +import { DateFormat } from "@/models/enums/dateFormat"; import { dateInRange, filterActiveAnnouncements, formatDate } from "@/utils/utils"; +import ButtonTertiary from "../system/button/buttonTertiary"; import AnnouncementIndicator from "./announcementIndicator"; -import { DateFormat } from "@/models/enums/dateFormat"; interface Props { announcement: Announcement; @@ -16,7 +16,7 @@ export default function AnnouncementCell({ announcement, onClick, onEditClick }: return (
diff --git a/src/components/announcement/announcementForm.tsx b/src/components/announcement/announcementForm.tsx index ae3ca62..8d0c51a 100644 --- a/src/components/announcement/announcementForm.tsx +++ b/src/components/announcement/announcementForm.tsx @@ -1,15 +1,15 @@ import CrossThinIcon from "@/icons/crossThinIcon"; import SpeakerIcon from "@/icons/speakerIcon"; -import AnnouncementBanner from "./announcementBanner"; -import { useEffect, useMemo, useState } from "react"; import { Announcement } from "@/models/announcement"; -import InputText from "../system/input/inputText"; +import { useUserStore } from "@/stores/useUserStore"; +import { addDays } from "date-fns"; +import { useMemo, useState } from "react"; +import ButtonPrimary1 from "../system/button/buttonPrimary1"; import InputDatePicker from "../system/input/inputDatePicker"; import InputMultiSelect from "../system/input/inputMultiselect"; +import InputText from "../system/input/inputText"; import InputUpload from "../system/input/inputUpload"; -import ButtonPrimary1 from "../system/button/buttonPrimary1"; -import { useUserStore } from "@/stores/useUserStore"; -import { addDays } from "date-fns"; +import AnnouncementBanner from "./announcementBanner"; const dummyAnnouncement: Announcement = { id: "", diff --git a/src/components/announcement/announcementModal.tsx b/src/components/announcement/announcementModal.tsx index 1bf87d4..e2bea1d 100644 --- a/src/components/announcement/announcementModal.tsx +++ b/src/components/announcement/announcementModal.tsx @@ -1,12 +1,12 @@ -import { Announcement } from "@/models/announcement"; -import AnnouncementBanner from "./announcementBanner"; import AppIcon from "@/icons/appIcon"; import CrossThinIcon from "@/icons/crossThinIcon"; -import { dateInRange, formatDate } from "@/utils/utils"; -import AnnouncementIndicator from "./announcementIndicator"; +import { Announcement } from "@/models/announcement"; import { DateFormat } from "@/models/enums/dateFormat"; -import ButtonSecondary2 from "../system/button/buttonSecondary2"; +import { dateInRange, formatDate } from "@/utils/utils"; import ButtonPrimary2 from "../system/button/buttonPrimary2"; +import ButtonSecondary2 from "../system/button/buttonSecondary2"; +import AnnouncementBanner from "./announcementBanner"; +import AnnouncementIndicator from "./announcementIndicator"; interface AnnouncementModalProps { onClose: () => void; diff --git a/src/components/landing/landingPastSection.tsx b/src/components/landing/landingPastSection.tsx index c30075d..7adbff0 100644 --- a/src/components/landing/landingPastSection.tsx +++ b/src/components/landing/landingPastSection.tsx @@ -1,12 +1,12 @@ import CalendarPlainIcon from "@/icons/calendarPlainIcon"; import { Announcement } from "@/models/announcement"; +import { Constants } from "@/utils/constants"; import { filterPastAnnouncements, sortAnnouncementsByStartDate } from "@/utils/utils"; +import { useRouter } from "next/navigation"; +import { useState } from "react"; import AnnouncementCell from "../announcement/announcementCell"; import AnnouncementModal from "../announcement/announcementModal"; -import { useState } from "react"; import ButtonSecondary1 from "../system/button/buttonSecondary1"; -import { Constants } from "@/utils/constants"; -import { useRouter } from "next/navigation"; interface Props { announcements?: Announcement[]; diff --git a/src/components/past/pastFilter.tsx b/src/components/past/pastFilter.tsx index f613f7a..1b10c58 100644 --- a/src/components/past/pastFilter.tsx +++ b/src/components/past/pastFilter.tsx @@ -1,10 +1,10 @@ import { AppName } from "@/models/enums/appName"; -import InputDatePicker from "../system/input/inputDatePicker"; +import { useState } from "react"; import { DateRange } from "react-day-picker"; -import InputMultiSelect from "../system/input/inputMultiselect"; import ButtonPrimary2 from "../system/button/buttonPrimary2"; import ButtonPrimary3 from "../system/button/buttonPrimary3"; -import { useState } from "react"; +import InputDatePicker from "../system/input/inputDatePicker"; +import InputMultiSelect from "../system/input/inputMultiselect"; interface Props { initialDateRange: DateRange | undefined; diff --git a/src/components/system/button/buttonPrimary1.tsx b/src/components/system/button/buttonPrimary1.tsx index fe7a6c7..10d849b 100644 --- a/src/components/system/button/buttonPrimary1.tsx +++ b/src/components/system/button/buttonPrimary1.tsx @@ -1,6 +1,8 @@ "use client"; -import CaretRightIcon from "@/icons/caretRightIcon"; +import { motion } from "framer-motion"; +import { Loader2 } from "lucide-react"; +import { useState } from "react"; interface Props { text: string; @@ -8,30 +10,100 @@ interface Props { disabled?: boolean; className?: string; textStyle?: string; + isLoading?: boolean; } -export default function ButtonPrimary1({ text, action, disabled = false, className, textStyle }: Props) { +export default function ButtonPrimary1({ + text, + action, + disabled = false, + className, + textStyle, + isLoading = false, +}: Props) { + const [isHovered, setIsHovered] = useState(false); + const [isPressed, setIsPressed] = useState(false); + const handleClick = (event: React.MouseEvent) => { event.stopPropagation(); // Prevent event from bubbling up action(); }; + const iconVariants = { + chevron: { x: 0 }, + arrow: { x: 1 }, + }; + + const lineVariants = { + hidden: { scaleX: 0, originX: 1 }, + visible: { scaleX: 1, originX: 1 }, + }; + return ( ); } diff --git a/src/components/system/button/buttonPrimary2.tsx b/src/components/system/button/buttonPrimary2.tsx index 0f6bb90..575a967 100644 --- a/src/components/system/button/buttonPrimary2.tsx +++ b/src/components/system/button/buttonPrimary2.tsx @@ -1,22 +1,52 @@ "use client"; +import { Loader2 } from "lucide-react"; + interface Props { text: string; action: () => void; disabled?: boolean; className?: string; textStyle?: string; + isLoading?: boolean; } -export default function ButtonPrimary2({ text, action, disabled = false, className, textStyle }: Props) { +export default function ButtonPrimary2({ + text, + action, + disabled = false, + className, + textStyle, + isLoading = false, +}: Props) { const handleClick = (event: React.MouseEvent) => { event.stopPropagation(); // Prevent event from bubbling up action(); }; return ( - ); } diff --git a/src/components/system/button/buttonPrimary3.tsx b/src/components/system/button/buttonPrimary3.tsx index 0e86949..b2bd12e 100644 --- a/src/components/system/button/buttonPrimary3.tsx +++ b/src/components/system/button/buttonPrimary3.tsx @@ -1,14 +1,27 @@ "use client"; +import { Loader2 } from "lucide-react"; +import { useState } from "react"; + interface Props { text: string; action: () => void; disabled?: boolean; className?: string; textStyle?: string; + isLoading?: boolean; } -export default function ButtonPrimary3({ text, action, disabled = false, className, textStyle }: Props) { +export default function ButtonPrimary3({ + text, + action, + disabled = false, + className, + textStyle, + isLoading = false, +}: Props) { + const [isPressed, setIsPressed] = useState(false); + const handleClick = (event: React.MouseEvent) => { event.stopPropagation(); // Prevent event from bubbling up action(); @@ -16,11 +29,33 @@ export default function ButtonPrimary3({ text, action, disabled = false, classNa return ( ); } diff --git a/src/components/system/button/buttonSecondary1.tsx b/src/components/system/button/buttonSecondary1.tsx index ae96718..87217c7 100644 --- a/src/components/system/button/buttonSecondary1.tsx +++ b/src/components/system/button/buttonSecondary1.tsx @@ -1,6 +1,8 @@ "use client"; -import CaretRightIcon from "@/icons/caretRightIcon"; +import { motion } from "framer-motion"; +import { Loader2 } from "lucide-react"; +import { useState } from "react"; interface Props { text: string; @@ -8,22 +10,96 @@ interface Props { disabled?: boolean; className?: string; textStyle?: string; + isLoading?: boolean; } -export default function ButtonSecondary1({ text, action, disabled = false, className, textStyle }: Props) { +export default function ButtonSecondary1({ + text, + action, + disabled = false, + className, + textStyle, + isLoading = false, +}: Props) { + const [isHovered, setIsHovered] = useState(false); + const [isPressed, setIsPressed] = useState(false); + const handleClick = (event: React.MouseEvent) => { event.stopPropagation(); // Prevent event from bubbling up action(); }; + const iconVariants = { + chevron: { x: 0 }, + arrow: { x: 1 }, + }; + + const lineVariants = { + hidden: { scaleX: 0, originX: 1 }, + visible: { scaleX: 1, originX: 1 }, + }; + return ( ); } diff --git a/src/components/system/button/buttonSecondary2.tsx b/src/components/system/button/buttonSecondary2.tsx index dd603f2..b248cae 100644 --- a/src/components/system/button/buttonSecondary2.tsx +++ b/src/components/system/button/buttonSecondary2.tsx @@ -1,22 +1,57 @@ "use client"; +import { Loader2 } from "lucide-react"; +import { useState } from "react"; + interface Props { text: string; action: () => void; disabled?: boolean; className?: string; textStyle?: string; + isLoading?: boolean; } -export default function ButtonSecondary2({ text, action, disabled = false, className, textStyle }: Props) { +export default function ButtonSecondary2({ + text, + action, + disabled = false, + className, + textStyle, + isLoading = false, +}: Props) { + const [isPressed, setIsPressed] = useState(false); + const handleClick = (event: React.MouseEvent) => { event.stopPropagation(); // Prevent event from bubbling up action(); }; return ( - ); } diff --git a/src/components/system/button/buttonTertiary.tsx b/src/components/system/button/buttonTertiary.tsx index e9b299b..b269117 100644 --- a/src/components/system/button/buttonTertiary.tsx +++ b/src/components/system/button/buttonTertiary.tsx @@ -1,6 +1,7 @@ "use client"; -import EditIcon from "@/icons/editIcon"; +import { Loader2, PencilIcon } from "lucide-react"; +import { useState } from "react"; interface Props { text: string; @@ -8,9 +9,19 @@ interface Props { disabled?: boolean; className?: string; textStyle?: string; + isLoading?: boolean; } -export default function TertiaryButton({ text, action, disabled = false, className, textStyle }: Props) { +export default function ButtonTertiary({ + text, + action, + disabled = false, + className, + textStyle, + isLoading = false, +}: Props) { + const [isPressed, setIsPressed] = useState(false); + const handleClick = (event: React.MouseEvent) => { event.stopPropagation(); // Prevent event from bubbling up action(); @@ -18,12 +29,34 @@ export default function TertiaryButton({ text, action, disabled = false, classNa return ( ); } diff --git a/src/icons/CaretRightIcon.tsx b/src/icons/CaretRightIcon.tsx deleted file mode 100644 index 4c34c9b..0000000 --- a/src/icons/CaretRightIcon.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import { IconProps } from "@/models/props/iconProps"; - -export default function ({ className }: IconProps) { - return ( - - - - ); -} diff --git a/yarn.lock b/yarn.lock index d63b6d6..83172de 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2691,12 +2691,12 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" -framer-motion@^11.13.1: - version "11.13.5" - resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-11.13.5.tgz#2cec307d2395ba6924a683abd79a517ec1f318f5" - integrity sha512-rArI0zPU9VkpS3Wt0J7dmRxAFUWtzPWoSofNQAP0UO276CmJ+Xlf5xN19GMw3w2QsdrS2sU+0+Q2vtuz4IEZaw== +framer-motion@^11.14.0: + version "11.14.0" + resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-11.14.0.tgz#806a710ec0ad5b6ad4bbcf2f88fb4a2d9c559b08" + integrity sha512-/cPWeZ4GXAJwNj3Z2cp+WtbVDlXyhBWmoIlorgn6FieOcEmoritPGvg+dqO6GaUlXHYgW6YLpsAPzdFfQqWQ5Q== dependencies: - motion-dom "^11.13.0" + motion-dom "^11.14.0" motion-utils "^11.13.0" tslib "^2.4.0" @@ -3523,10 +3523,10 @@ minimatch@^9.0.4: resolved "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== -motion-dom@^11.13.0: - version "11.13.0" - resolved "https://registry.yarnpkg.com/motion-dom/-/motion-dom-11.13.0.tgz#a8f86b3aedb55598a8e3dd4114f1c3347153baf2" - integrity sha512-Oc1MLGJQ6nrvXccXA89lXtOqFyBmvHtaDcTRGT66o8Czl7nuA8BeHAd9MQV1pQKX0d2RHFBFaw5g3k23hQJt0w== +motion-dom@^11.14.0: + version "11.14.0" + resolved "https://registry.yarnpkg.com/motion-dom/-/motion-dom-11.14.0.tgz#5be815b374b9842b09218894d7d83a4a5935d688" + integrity sha512-K7utJZxeMfUJNp/7VNtyasmj4AsqmX6dxT01ox2M9kndcww9soP4gFpf4n0wz7m249+c2ZRfWPnWdInzaxv03w== motion-utils@^11.13.0: version "11.13.0"