Skip to content

Commit

Permalink
Float overhaul. Created Higher order component to easily implement fl…
Browse files Browse the repository at this point in the history
…oats. Float bug fix
  • Loading branch information
webdevsk committed Oct 11, 2023
1 parent 158c9c5 commit 74d58a6
Show file tree
Hide file tree
Showing 6 changed files with 195 additions and 132 deletions.
46 changes: 7 additions & 39 deletions src/components/Header/BurgerMenu.jsx
Original file line number Diff line number Diff line change
@@ -1,56 +1,24 @@
import { Typography } from "@material-tailwind/react"
import { FloatingOverlay, FloatingPortal, size } from "@floating-ui/react"
import { Float } from "@headlessui-float/react"
import { Popover, Transition } from "@headlessui/react"
import { Fragment, useState } from "react"
import { useState } from "react"
import { HiChevronLeft, HiChevronRight } from "react-icons/hi"
import { Link } from "react-router-dom"
import { useMainMenuContext } from "../../hooks/useMainMenuContext"
import { useHeaderMenuContext } from "../../hooks/useHeaderMenuContext"
import { HigherOrderFloat, HigherOrderPanel } from "../common/HigherOrderFloat"

const BurgerMenu = () => {
return (
<Popover className="relative grid place-items-center">
<Float
portal={true}
shift
offset={16}
arrow={0}
placement="bottom"
transform={false}
middleware={[
size({
apply({ availableHeight, availableWidth, elements }) {
Object.assign(elements.floating.style, {
width: `${availableWidth}px`,
height: `${availableHeight}px`,
})
},
}),
]}
enter="transition duration-200 ease-out"
enterFrom="opacity-0 scale-y-90"
enterTo="scale-y-100 opacity-100"
leave="transition duration-200 ease-in"
leaveFrom="scale-y-100 opacity-100"
leaveTo="scale-y-90 opacity-0"
originClass="origin-top"
floatingAs={Fragment}
>
<HigherOrderFloat placement="bottom" strategy="fixed">
<Popover.Button className="relative [--_stroke-height:0.2em] [--_stroke-width:1.5em] focus-visible:outline-none">
<BurgerMenuBtn />
</Popover.Button>

<Popover.Panel className="pointer-events-none">
<Float.Arrow className="absolute h-5 w-5 rotate-45 border border-gray-200 bg-white" />
<FloatingPortal>
<FloatingOverlay lockScroll />
</FloatingPortal>
<div className="pointer-events-auto relative h-full overflow-x-hidden overflow-y-scroll border border-t-transparent bg-gray-100 text-body">
<BurgerMenuList />
</div>
</Popover.Panel>
</Float>
<HigherOrderPanel fullHeight fullWidth lockScroll>
<BurgerMenuList />
</HigherOrderPanel>
</HigherOrderFloat>
</Popover>
)
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/Header/Header.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ const Header = () => {
<div className="relative h-16">
<div
ref={stickyHeaderRef}
className={`absolute inset-x-0 grid h-16 place-items-center ${
className={`absolute inset-x-0 z-[9999] grid h-16 place-items-center ${
isSticking ? "bg-theme shadow-lg shadow-black/30" : ""
}`}
>
Expand Down
94 changes: 67 additions & 27 deletions src/components/Header/HeaderToolBar.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { Button, Drawer, Typography } from "@material-tailwind/react"
import { useState } from "react"
import { FloatingOverlay, FloatingPortal } from "@floating-ui/react"
import { Fragment, useState } from "react"
import { FloatingOverlay, FloatingPortal, size } from "@floating-ui/react"
import Cart from "../../features/cart/Cart"
import BadgeCounter from "../common/BadgeCounter"
import { Float } from "@headlessui-float/react"
import { Popover } from "@headlessui/react"
import { IoIosLogIn, IoIosLogOut } from "react-icons/io"
import { HigherOrderFloat, HigherOrderPanel } from "../common/HigherOrderFloat"

export const HeaderToolBar = () => {
const [isOpen, setIsOpen] = useState(false)
Expand All @@ -11,31 +15,36 @@ export const HeaderToolBar = () => {
<>
<div className="ms-auto">
<div className="flex flex-wrap gap-4 group-[.floating]/header:gap-2 lg:group-[.floating]/header:gap-4">
<Button
variant="text"
className="flex flex-wrap items-end gap-1 p-0 text-white hover:bg-transparent hover:text-accent active:bg-transparent"
>
<svg
xmlns="https://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="h-7 w-7"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M17.982 18.725A7.488 7.488 0 0012 15.75a7.488 7.488 0 00-5.982 2.975m11.963 0a9 9 0 10-11.963 0m11.963 0A8.966 8.966 0 0112 21a8.966 8.966 0 01-5.982-2.275M15 9.75a3 3 0 11-6 0 3 3 0 016 0z"
/>
</svg>
<Typography
className="group-[.floating]/header:hidden lg:group-[.floating]/header:block"
variant="h6"
>
Account
</Typography>
</Button>
<Popover className="relative">
<HigherOrderFloat offset={16} shift={{ crossAxis: true }}>
<Popover.Button className="flex flex-wrap items-end gap-1 p-0 text-white hover:bg-transparent hover:text-accent active:bg-transparent">
<svg
xmlns="https://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="h-7 w-7"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M17.982 18.725A7.488 7.488 0 0012 15.75a7.488 7.488 0 00-5.982 2.975m11.963 0a9 9 0 10-11.963 0m11.963 0A8.966 8.966 0 0112 21a8.966 8.966 0 01-5.982-2.275M15 9.75a3 3 0 11-6 0 3 3 0 016 0z"
/>
</svg>
<Typography
className="group-[.floating]/header:hidden lg:group-[.floating]/header:block"
variant="h6"
>
Account
</Typography>
</Popover.Button>
<HigherOrderPanel overlay={false}>
<AccountPanel />
</HigherOrderPanel>
</HigherOrderFloat>
</Popover>

<Button
variant="text"
className="flex flex-wrap items-end gap-1 p-0 text-white hover:bg-transparent hover:text-accent active:bg-transparent"
Expand Down Expand Up @@ -89,3 +98,34 @@ export const HeaderToolBar = () => {
</>
)
}

const AccountPanel = () => {
return (
<div
className={`relative max-h-full w-64 max-w-full divide-y overflow-y-auto border border-t-transparent bg-white text-black shadow-md`}
>
{1 && (
<button className="block w-full px-4 py-3 transition-colors hover:bg-gray-100">
<Typography
variant="paragraph"
className="flex items-center gap-2 uppercase"
>
<IoIosLogIn className="text-xl" />
Log in
</Typography>
</button>
)}
{1 && (
<button className="block w-full px-4 py-3 transition-colors hover:bg-gray-100">
<Typography
variant="paragraph"
className="flex items-center gap-2 uppercase"
>
<IoIosLogOut className="text-xl" />
Log Out
</Typography>
</button>
)}
</div>
)
}
74 changes: 20 additions & 54 deletions src/components/Header/MainMenuDesktop.jsx
Original file line number Diff line number Diff line change
@@ -1,42 +1,17 @@
import { Typography } from "@material-tailwind/react"
import { Link } from "react-router-dom"
import { Fragment } from "react"
import { FloatingOverlay, FloatingPortal, size } from "@floating-ui/react"
import { Popover } from "@headlessui/react"
import { Float } from "@headlessui-float/react"
import { HiChevronDown } from "react-icons/hi"
import { useMainMenuContext } from "../../hooks/useMainMenuContext"
import { HigherOrderFloat, HigherOrderPanel } from "../common/HigherOrderFloat"

export const MainMenuDesktop = () => {
const mainMenu = useMainMenuContext()
return (
<Popover.Group as="ul" className="flex flex-wrap gap-2 py-2">
{mainMenu.map((menu) => (
<Popover className="relative" key={menu.id}>
<Float
offset={16}
arrow={0}
shift
placement="bottom-start"
transform={false}
floatingAs={Fragment}
middleware={[
size({
apply({ availableHeight, elements }) {
Object.assign(elements.floating.style, {
height: `${availableHeight}px`,
})
},
}),
]}
enter="transition duration-200 ease-out"
enterFrom="opacity-0 scale-y-90"
enterTo="scale-y-100 opacity-100"
leave="transition duration-200 ease-in"
leaveFrom="scale-y-100 opacity-100"
leaveTo="scale-y-90 opacity-0"
originClass="origin-top"
>
<HigherOrderFloat placement="bottom-start">
<Popover.Button className="group flex items-center gap-1 hover:text-accent focus-visible:outline-none">
<Typography variant="h6" className="capitalize">
{menu.label}
Expand All @@ -46,33 +21,24 @@ export const MainMenuDesktop = () => {
/>
</Popover.Button>

<Popover.Panel className="pointer-events-none [&>*]:pointer-events-auto">
<Float.Arrow className="absolute h-5 w-5 rotate-45 border border-gray-200 bg-white" />
<div
className={`relative max-h-full w-80 overflow-y-scroll border border-t-transparent bg-white py-2 text-black`}
>
{menu.items.map((item, i) => (
<li key={i}>
<Link
to={item}
className="block px-3 py-1 transition-colors hover:bg-gray-100 hover:text-theme"
>
<Typography className="text-base capitalize">
{item}
</Typography>
</Link>
</li>
))}
</div>

<FloatingPortal>
<FloatingOverlay
lockScroll
className="pointer-events-none z-30 h-screen w-full bg-black/30"
/>
</FloatingPortal>
</Popover.Panel>
</Float>
<HigherOrderPanel
lockScroll
containerProps={{ className: "w-80 divide-y overflow-y-scroll" }}
>
{menu.items.map((item, i) => (
<li key={i}>
<Link
to={item}
className="block px-3 py-2 transition-colors hover:bg-gray-200 hover:text-theme"
>
<Typography className="text-base capitalize">
{item}
</Typography>
</Link>
</li>
))}
</HigherOrderPanel>
</HigherOrderFloat>
</Popover>
))}
</Popover.Group>
Expand Down
95 changes: 95 additions & 0 deletions src/components/common/HigherOrderFloat.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { Float } from "@headlessui-float/react"
import { FloatingOverlay, FloatingPortal, size } from "@floating-ui/react"
import { Fragment, forwardRef, memo } from "react"
import { Popover } from "@headlessui/react"

export const HigherOrderFloat = memo((props) => {
return (
<Float
offset={16}
arrow={0}
shift
placement="bottom"
transform={false}
floatingAs={Fragment}
middleware={[
size({
apply({ availableHeight, availableWidth, elements }) {
elements.floating.style.setProperty(
"--_spaceX",
`${availableWidth}px`,
)
elements.floating.style.setProperty(
"--_spaceY",
`${availableHeight}px`,
)
},
}),
]}
enter="transition duration-200 ease-out"
enterFrom="opacity-0 scale-y-90"
enterTo="scale-y-100 opacity-100"
leave="transition duration-200 ease-in"
leaveFrom="scale-y-100 opacity-100"
leaveTo="scale-y-90 opacity-0"
originClass="origin-top"
{...props}
>
{props.children}
</Float>
)
})

HigherOrderFloat.displayName = "HigherOrderFloat"

export const HigherOrderPanel = memo(
forwardRef((props, ref) => {
const {
children,
lockScroll,
fullHeight,
fullWidth,
height,
width,
className,
containerProps,
overlay = true,
...filteredProps
} = props
return (
<Popover.Panel
ref={ref}
className={` ${className ?? ""}`}
{...filteredProps}
>
<Float.Arrow
className={`absolute h-5 w-5 rotate-45 border border-gray-200 bg-white`}
/>

{overlay && (
<FloatingPortal>
<Popover.Button
as={FloatingOverlay}
lockScroll={lockScroll}
className=" z-30 h-screen w-full bg-black/30"
/>
</FloatingPortal>
)}

<div
style={{ height: height, width: width }}
{...containerProps}
className={`pointer-events-auto relative overflow-y-auto overflow-x-hidden border border-t-transparent bg-gray-100 text-body shadow-lg ${
fullHeight ? "h-[--_spaceY]" : "max-h-[--_spaceY] rounded-sm"
} ${fullWidth ? "w-[--_spaceX]" : "max-w-[--_spaceX] rounded-sm"} ${
containerProps?.className ?? ""
}`}
>
{children}
</div>
</Popover.Panel>
)
}),
)

HigherOrderPanel.displayName = "HigherOrderPanel"
Loading

0 comments on commit 74d58a6

Please sign in to comment.