feat(dimentorin): slicing landing page (#46)
* feat(landing): slicing navigation bar * feat(landing): slicing hero section * feat(landing): slicing what we offer section * feat(landing): add animation on hero and what we offer section * feat(landing): slicing testimonial section * feat(landing): change the animation for what we offer section * feat: add utility for readable looping and conditional rendering of components * feat(landing): slicing faq section * feat(landing): change border radius of card on `what we offer` and `testimonial` section * feat(landing): slicing cta section * feat: modify accordion component * feat(landing): add shape background at faq section * feat(landing): slicing footer * feat: toggle header visibility on scroll
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
import { cn, Show } from "@imphnen-frontend-service/utils"
|
||||
import { DetailedHTMLProps, FC, HTMLAttributes, useState } from "react"
|
||||
import { AnimatePresence, motion, Variants } from "framer-motion"
|
||||
import { MinusOutlined, PlusOutlined } from "@ant-design/icons"
|
||||
|
||||
export type AccordionOptions = {
|
||||
title: string
|
||||
description: string
|
||||
}
|
||||
|
||||
export type AccordionProps = DetailedHTMLProps<
|
||||
HTMLAttributes<HTMLDivElement>,
|
||||
HTMLDivElement
|
||||
> & AccordionOptions & {
|
||||
titleClassName?: string
|
||||
contentClassName?: string
|
||||
}
|
||||
|
||||
export const Accordion: FC<AccordionProps> = ({ title, description, className, titleClassName, contentClassName, ...rest }) => {
|
||||
const [expand, setExpand] = useState(false)
|
||||
|
||||
const variants: Variants = {
|
||||
expand: { opacity: 1, y: 0, marginTop: 16, height: 'auto' },
|
||||
collapse: { opacity: 0, y: -10, marginTop: 0, height: 0 },
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cn("bg-white rounded-lg shadow cursor-pointer pb-4", className)} {...rest}>
|
||||
<div className="px-5 pt-4 select-none flex justify-between items-center gap-x-2" onClick={() => setExpand(prev => !prev)}>
|
||||
<h4 className={cn("text-primary-500 font-semibold text-lg leading-tight md:text-xl", titleClassName)}>{title}</h4>
|
||||
<div className={cn("text-primary-500 transition-all duration-300", expand && "rotate-90")}>
|
||||
<Show condition={expand} fallback={<PlusOutlined />}>
|
||||
<MinusOutlined className="rotate-90" />
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
<AnimatePresence>
|
||||
{expand && (
|
||||
<motion.p
|
||||
className={cn("mt-4 px-5 text-neutral-500 text-sm font-medium cursor-text md:text-base", contentClassName)}
|
||||
variants={variants}
|
||||
initial="collapse"
|
||||
animate="expand"
|
||||
exit="collapse"
|
||||
transition={{ duration: 0.3 }}
|
||||
>
|
||||
{description}
|
||||
</motion.p>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './accordion';
|
||||
@@ -4,3 +4,4 @@ export * from './input-field';
|
||||
export * from './pagination';
|
||||
export * from './modal/modal';
|
||||
export * from './stepper';
|
||||
export * from './accordion';
|
||||
@@ -6,3 +6,4 @@ export * from './local-storage';
|
||||
export * from './cookies';
|
||||
export * from './session';
|
||||
export * from './constants';
|
||||
export * from './logic';
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
export interface ForProps<T, U> {
|
||||
data: readonly T[]
|
||||
children: (item: T, index: number) => U | null
|
||||
fallback?: React.ReactNode | null
|
||||
}
|
||||
|
||||
/**
|
||||
* A functional component that renders a list of children components from a given
|
||||
* array of data.
|
||||
*
|
||||
* @param data - The array of data to render
|
||||
* @param children - A function that takes the current item and index and returns
|
||||
* the child component to render
|
||||
* @param fallback - An optional fallback component to render when the data is empty
|
||||
*
|
||||
* @returns An array of rendered child components if the data is not empty, otherwise
|
||||
* the fallback component if it is provided, null otherwise
|
||||
*/
|
||||
export function For<T, U extends React.JSX.Element>({
|
||||
data,
|
||||
children,
|
||||
fallback = null
|
||||
}: ForProps<T, U>): (U | null)[] | React.ReactNode | null {
|
||||
if (!Array.isArray(data) || !data?.length) return fallback
|
||||
|
||||
return data.map((item, idx) => children(item, idx))
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './for'
|
||||
export * from './show'
|
||||
@@ -0,0 +1,19 @@
|
||||
export interface ShowProps {
|
||||
condition: boolean
|
||||
children: React.ReactNode
|
||||
fallback?: React.ReactNode | null
|
||||
}
|
||||
|
||||
/**
|
||||
* Conditionally renders the `children` component if `condition` is true. If
|
||||
* `condition` is false, renders the `fallback` component instead.
|
||||
*
|
||||
* If `fallback` is `null`, renders nothing.
|
||||
*
|
||||
* @param condition - The condition to check
|
||||
* @param children - The component to render if `condition` is true
|
||||
* @param fallback - The component to render if `condition` is false
|
||||
*/
|
||||
export function Show({ condition, children, fallback = null }: ShowProps) {
|
||||
return condition ? children : fallback
|
||||
}
|
||||
Reference in New Issue
Block a user