diff --git a/components/Icons/Accessibility.tsx b/components/Icons/Accessibility.tsx new file mode 100644 index 000000000..60d9a79d4 --- /dev/null +++ b/components/Icons/Accessibility.tsx @@ -0,0 +1,40 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function AccessibilityIcon({ + className, + color, + ...props +}: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/Bar.tsx b/components/Icons/Bar.tsx new file mode 100644 index 000000000..45796be66 --- /dev/null +++ b/components/Icons/Bar.tsx @@ -0,0 +1,36 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function BarIcon({ className, color, ...props }: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/Biking.tsx b/components/Icons/Biking.tsx new file mode 100644 index 000000000..1cc9b3b9f --- /dev/null +++ b/components/Icons/Biking.tsx @@ -0,0 +1,36 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function BikingIcon({ className, color, ...props }: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/Coffee.tsx b/components/Icons/Coffee.tsx new file mode 100644 index 000000000..840f78b0d --- /dev/null +++ b/components/Icons/Coffee.tsx @@ -0,0 +1,36 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function CoffeeIcon({ className, color, ...props }: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/Concierge.tsx b/components/Icons/Concierge.tsx new file mode 100644 index 000000000..69119a969 --- /dev/null +++ b/components/Icons/Concierge.tsx @@ -0,0 +1,40 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function ConciergeIcon({ + className, + color, + ...props +}: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/DoorOpen.tsx b/components/Icons/DoorOpen.tsx new file mode 100644 index 000000000..f70c29859 --- /dev/null +++ b/components/Icons/DoorOpen.tsx @@ -0,0 +1,36 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function CoffeeIcon({ className, color, ...props }: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/ElectricBike.tsx b/components/Icons/ElectricBike.tsx new file mode 100644 index 000000000..44e796cd8 --- /dev/null +++ b/components/Icons/ElectricBike.tsx @@ -0,0 +1,40 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function ElectricBikeIcon({ + className, + color, + ...props +}: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/Fitness.tsx b/components/Icons/Fitness.tsx new file mode 100644 index 000000000..69b965a7b --- /dev/null +++ b/components/Icons/Fitness.tsx @@ -0,0 +1,36 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function FitnessIcon({ className, color, ...props }: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/Parking.tsx b/components/Icons/Parking.tsx new file mode 100644 index 000000000..3680757e4 --- /dev/null +++ b/components/Icons/Parking.tsx @@ -0,0 +1,36 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function ParkingIcon({ className, color, ...props }: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/Pets.tsx b/components/Icons/Pets.tsx new file mode 100644 index 000000000..db832a5af --- /dev/null +++ b/components/Icons/Pets.tsx @@ -0,0 +1,36 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function PetsIcon({ className, color, ...props }: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/Restaurant.tsx b/components/Icons/Restaurant.tsx new file mode 100644 index 000000000..4ac06eb32 --- /dev/null +++ b/components/Icons/Restaurant.tsx @@ -0,0 +1,40 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function RestaurantIcon({ + className, + color, + ...props +}: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/TshirtWash.tsx b/components/Icons/TshirtWash.tsx new file mode 100644 index 000000000..7b952490f --- /dev/null +++ b/components/Icons/TshirtWash.tsx @@ -0,0 +1,40 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function TshirtWashIcon({ + className, + color, + ...props +}: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/Wifi.tsx b/components/Icons/Wifi.tsx new file mode 100644 index 000000000..80d2d1000 --- /dev/null +++ b/components/Icons/Wifi.tsx @@ -0,0 +1,36 @@ +import { iconVariants } from "./variants" + +import type { IconProps } from "@/types/components/icon" + +export default function BarIcon({ className, color, ...props }: IconProps) { + const classNames = iconVariants({ className, color }) + return ( + + + + + + + + + ) +} diff --git a/components/Icons/get-icon-by-icon-name.ts b/components/Icons/get-icon-by-icon-name.ts new file mode 100644 index 000000000..5feab994d --- /dev/null +++ b/components/Icons/get-icon-by-icon-name.ts @@ -0,0 +1,106 @@ +import { FC } from "react" + +import { + AccessibilityIcon, + AccountCircleIcon, + ArrowRightIcon, + BarIcon, + BikingIcon, + CalendarIcon, + CellphoneIcon, + CheckCircleIcon, + CheckIcon, + ChevronDownIcon, + ChevronRightIcon, + CloseIcon, + CoffeeIcon, + ConciergeIcon, + DoorOpenIcon, + ElectricBikeIcon, + EmailIcon, + FitnessIcon, + GlobeIcon, + HouseIcon, + InfoCircleIcon, + LocationIcon, + LockIcon, + ParkingIcon, + PersonIcon, + PetsIcon, + PhoneIcon, + PlusCircleIcon, + RestaurantIcon, + TshirtWashIcon, + WifiIcon, +} from "." + +import { IconName, IconProps } from "@/types/components/icon" + +export function getIconByIconName(icon?: IconName): FC | null { + switch (icon) { + case IconName.Accessibility: + return AccessibilityIcon + case IconName.AccountCircle: + return AccountCircleIcon + case IconName.ArrowRight: + return ArrowRightIcon + case IconName.Bar: + return BarIcon + case IconName.Biking: + return BikingIcon + case IconName.Calendar: + return CalendarIcon + case IconName.Cellphone: + return CellphoneIcon + case IconName.Check: + return CheckIcon + case IconName.CheckCircle: + return CheckCircleIcon + case IconName.ChevronDown: + return ChevronDownIcon + case IconName.ChevronRight: + return ChevronRightIcon + case IconName.Close: + return CloseIcon + case IconName.Coffee: + return CoffeeIcon + case IconName.Concierge: + return ConciergeIcon + case IconName.DoorOpen: + return DoorOpenIcon + case IconName.ElectricBike: + return ElectricBikeIcon + case IconName.Email: + return EmailIcon + case IconName.Fitness: + return FitnessIcon + case IconName.Globe: + return GlobeIcon + case IconName.House: + return HouseIcon + case IconName.InfoCircle: + return InfoCircleIcon + case IconName.Location: + return LocationIcon + case IconName.Lock: + return LockIcon + case IconName.Parking: + return ParkingIcon + case IconName.Person: + return PersonIcon + case IconName.Pets: + return PetsIcon + case IconName.Phone: + return PhoneIcon + case IconName.PlusCircle: + return PlusCircleIcon + case IconName.Restaurant: + return RestaurantIcon + case IconName.TshirtWash: + return TshirtWashIcon + case IconName.Wifi: + return WifiIcon + default: + return null + } +} diff --git a/components/Icons/index.tsx b/components/Icons/index.tsx index 7db0306cf..125925da3 100644 --- a/components/Icons/index.tsx +++ b/components/Icons/index.tsx @@ -1,5 +1,8 @@ +export { default as AccessibilityIcon } from "./Accessibility" export { default as AccountCircleIcon } from "./AccountCircle" export { default as ArrowRightIcon } from "./ArrowRight" +export { default as BarIcon } from "./Bar" +export { default as BikingIcon } from "./Biking" export { default as CalendarIcon } from "./Calendar" export { default as CellphoneIcon } from "./Cellphone" export { default as CheckIcon } from "./Check" @@ -7,12 +10,22 @@ export { default as CheckCircleIcon } from "./CheckCircle" export { default as ChevronDownIcon } from "./ChevronDown" export { default as ChevronRightIcon } from "./ChevronRight" export { default as CloseIcon } from "./Close" +export { default as CoffeeIcon } from "./Coffee" +export { default as ConciergeIcon } from "./Concierge" +export { default as DoorOpenIcon } from "./DoorOpen" +export { default as ElectricBikeIcon } from "./ElectricBike" export { default as EmailIcon } from "./Email" +export { default as FitnessIcon } from "./Fitness" export { default as GlobeIcon } from "./Globe" export { default as HouseIcon } from "./House" export { default as InfoCircleIcon } from "./InfoCircle" export { default as LocationIcon } from "./Location" export { default as LockIcon } from "./Lock" +export { default as ParkingIcon } from "./Parking" export { default as PersonIcon } from "./Person" +export { default as PetsIcon } from "./Pets" export { default as PhoneIcon } from "./Phone" export { default as PlusCircleIcon } from "./PlusCircle" +export { default as RestaurantIcon } from "./Restaurant" +export { default as TshirtWashIcon } from "./TshirtWash" +export { default as WifiIcon } from "./Wifi" diff --git a/components/TempDesignSystem/Accordion/AccordionItem/accordionItem.module.css b/components/TempDesignSystem/Accordion/AccordionItem/accordionItem.module.css new file mode 100644 index 000000000..234566ac5 --- /dev/null +++ b/components/TempDesignSystem/Accordion/AccordionItem/accordionItem.module.css @@ -0,0 +1,62 @@ +.accordionItem { + border-bottom: 1px solid var(--Base-Border-Subtle); +} + +.accordionItem.card { + padding: var(--Spacing-x1); +} + +.accordionItem.light { + background-color: var(--Base-Surface-Primary-Normal); +} +.accordionItem.subtle { + background-color: var(--Base-Background-Normal); +} + +.summary { + position: relative; + display: flex; + align-items: center; + gap: var(--Spacing-x1); + cursor: pointer; + color: var(--Base-Text-High-contrast); + font-family: var(--typography-Body-Bold-fontFamily); + font-size: var(--typography-Body-Bold-fontSize); + font-weight: var(--typography-Body-Bold-fontWeight); + transition: background-color 0.3s; +} +.summary:hover, +.accordionItem details[open] .summary { + background-color: var(--Base-Surface-Primary-light-Hover-alt, #f2ece6); +} +.accordionItem.light .summary:hover, +.accordionItem.light details[open] .summary { + background-color: var(--Base-Surface-Primary-light-Hover, #f9f6f4); +} +.accordionItem.subtle .summary:hover, +.accordionItem.subtle details[open] .summary { + background-color: var(--Base-Surface-Primary-Normal); +} + +.accordionItem.card .summary { + padding: var(--Spacing-x1) var(--Spacing-x-one-and-half); + border-radius: var(--Corner-radius-Medium); +} + +.title { + flex-grow: 1; +} + +.content { + padding: 0 var(--Spacing-x-one-and-half); + overflow: hidden; + max-height: 0; + transition: max-height 0.3s; +} + +.chevron { + transition: transform 0.3s; +} +details[open] .chevron { + transform: rotate(180deg); +} diff --git a/components/TempDesignSystem/Accordion/AccordionItem/accordionItem.ts b/components/TempDesignSystem/Accordion/AccordionItem/accordionItem.ts new file mode 100644 index 000000000..aebdba957 --- /dev/null +++ b/components/TempDesignSystem/Accordion/AccordionItem/accordionItem.ts @@ -0,0 +1,12 @@ +import { VariantProps } from "class-variance-authority" + +import { accordionItemVariants } from "./variants" + +import { IconName } from "@/types/components/icon" + +export interface AccordionItemProps + extends React.HtmlHTMLAttributes, + VariantProps { + title: string + icon?: IconName +} diff --git a/components/TempDesignSystem/Accordion/AccordionItem/index.tsx b/components/TempDesignSystem/Accordion/AccordionItem/index.tsx new file mode 100644 index 000000000..84be2d010 --- /dev/null +++ b/components/TempDesignSystem/Accordion/AccordionItem/index.tsx @@ -0,0 +1,59 @@ +"use client" + +import { useRef } from "react" + +import { ChevronDownIcon } from "@/components/Icons" +import { getIconByIconName } from "@/components/Icons/get-icon-by-icon-name" + +import { AccordionItemProps } from "./accordionItem" +import { accordionItemVariants } from "./variants" + +import styles from "./accordionItem.module.css" + +export default function AccordionItem({ + children, + icon, + title, + theme, + variant, +}: AccordionItemProps) { + const contentRef = useRef(null) + const detailsRef = useRef(null) + + const IconComp = getIconByIconName(icon) + + function toggleAccordion() { + const details = detailsRef.current + const content = contentRef.current + if (details && content) { + if (details.open) { + content.style.maxHeight = `${content.scrollHeight}px` + content.addEventListener( + "transitionend", + () => { + // Remove maxHeight after transition to allow content to transition multiple times + content.style.maxHeight = "none" + }, + { once: true } + ) + } else { + content.style.maxHeight = "0" + } + } + } + + return ( +
  • +
    + + {IconComp && } + {title} + + +
    + {children} +
    +
    +
  • + ) +} diff --git a/components/TempDesignSystem/Accordion/AccordionItem/variants.ts b/components/TempDesignSystem/Accordion/AccordionItem/variants.ts new file mode 100644 index 000000000..dde0e253d --- /dev/null +++ b/components/TempDesignSystem/Accordion/AccordionItem/variants.ts @@ -0,0 +1,20 @@ +import { cva } from "class-variance-authority" + +import styles from "./accordionItem.module.css" + +export const accordionItemVariants = cva(styles.accordionItem, { + variants: { + variant: { + card: styles.card, + }, + theme: { + default: styles.default, + light: styles.light, + subtle: styles.subtle, + }, + }, + defaultVariants: { + variant: "card", + theme: "default", + }, +}) diff --git a/components/TempDesignSystem/Accordion/accordion.module.css b/components/TempDesignSystem/Accordion/accordion.module.css new file mode 100644 index 000000000..63529495b --- /dev/null +++ b/components/TempDesignSystem/Accordion/accordion.module.css @@ -0,0 +1,14 @@ +.accordion { + list-style: none; +} + +.accordion.card { + border-radius: var(--Corner-radius-Medium); +} + +.accordion.light { + background-color: var(--Base-Surface-Primary-Normal); +} +.accordion.subtle { + background-color: var(--Base-Background-Normal); +} diff --git a/components/TempDesignSystem/Accordion/accordion.ts b/components/TempDesignSystem/Accordion/accordion.ts new file mode 100644 index 000000000..fa0ade85c --- /dev/null +++ b/components/TempDesignSystem/Accordion/accordion.ts @@ -0,0 +1,7 @@ +import { VariantProps } from "class-variance-authority" + +import { accordionVariants } from "./variants" + +export interface AccordionProps + extends React.HtmlHTMLAttributes, + VariantProps {} diff --git a/components/TempDesignSystem/Accordion/index.tsx b/components/TempDesignSystem/Accordion/index.tsx new file mode 100644 index 000000000..64afe99b9 --- /dev/null +++ b/components/TempDesignSystem/Accordion/index.tsx @@ -0,0 +1,24 @@ +import { Children, cloneElement, isValidElement } from "react" + +import { AccordionItemProps } from "./AccordionItem/accordionItem" +import { AccordionProps } from "./accordion" +import { accordionVariants } from "./variants" + +export default function Accordion({ + children, + className, + theme, + variant, +}: AccordionProps) { + return ( +
      + {Children.map(children, (child) => { + if (isValidElement(child)) { + return cloneElement(child, { variant, theme }) + } else { + return child + } + })} +
    + ) +} diff --git a/components/TempDesignSystem/Accordion/variants.ts b/components/TempDesignSystem/Accordion/variants.ts new file mode 100644 index 000000000..6fd5b8a7f --- /dev/null +++ b/components/TempDesignSystem/Accordion/variants.ts @@ -0,0 +1,20 @@ +import { cva } from "class-variance-authority" + +import styles from "./accordion.module.css" + +export const accordionVariants = cva(styles.accordion, { + variants: { + variant: { + card: styles.card, + }, + theme: { + default: styles.default, + light: styles.light, + subtle: styles.subtle, + }, + }, + defaultVariants: { + variant: "card", + theme: "default", + }, +}) diff --git a/types/components/icon.ts b/types/components/icon.ts index bb2030a17..1885c221c 100644 --- a/types/components/icon.ts +++ b/types/components/icon.ts @@ -5,3 +5,37 @@ import { iconVariants } from "@/components/Icons/variants" export interface IconProps extends Omit, "color">, VariantProps {} + +export enum IconName { + Accessibility = "Accessibility", + AccountCircle = "AccountCircle", + ArrowRight = "ArrowRight", + Bar = "Bar", + Biking = "Biking", + Calendar = "Calendar", + Cellphone = "Cellphone", + Check = "Check", + CheckCircle = "CheckCircle", + ChevronDown = "ChevronDown", + ChevronRight = "ChevronRight", + Close = "Close", + Coffee = "Coffee", + Concierge = "Concierge", + DoorOpen = "DoorOpen", + ElectricBike = "ElectricBike", + Email = "Email", + Fitness = "Fitness", + Globe = "Globe", + House = "House", + InfoCircle = "InfoCircle", + Location = "Location", + Lock = "Lock", + Parking = "Parking", + Person = "Person", + Pets = "Pets", + Phone = "Phone", + PlusCircle = "PlusCircle", + Restaurant = "Restaurant", + TshirtWash = "TshirtWash", + Wifi = "Wifi", +}