Browse Source
Update TabFilters component
Update TabFilters component
* Add a renderTabsTimeFlightTab() function to render the time of flight tab * Add a renderTabsTypeOfAirlines() function to render the type of airlines tab * Add a handleChangeAirlines() function to handle the change of airlinesmain
John Doe
1 year ago
1 changed files with 858 additions and 0 deletions
@ -0,0 +1,858 @@ |
|||||
|
"use client"; |
||||
|
|
||||
|
import React, { Fragment, useState } from "react"; |
||||
|
import { Dialog, Popover, Tab, Transition } from "@headlessui/react"; |
||||
|
import ButtonPrimary from "@/shared/ButtonPrimary"; |
||||
|
import ButtonThird from "@/shared/ButtonThird"; |
||||
|
import ButtonClose from "@/shared/ButtonClose"; |
||||
|
import Checkbox from "@/shared/Checkbox"; |
||||
|
import convertNumbThousand from "@/utils/convertNumbThousand"; |
||||
|
import Slider from "rc-slider"; |
||||
|
import { XMarkIcon } from "@heroicons/react/24/outline"; |
||||
|
|
||||
|
// DEMO DATA
|
||||
|
const typeOfAirlines = [ |
||||
|
{ |
||||
|
name: "Star Alliance", |
||||
|
}, |
||||
|
{ |
||||
|
name: "Air China", |
||||
|
}, |
||||
|
{ |
||||
|
name: "Air India", |
||||
|
}, |
||||
|
{ |
||||
|
name: "Air New Zealand", |
||||
|
}, |
||||
|
{ |
||||
|
name: "Asiana", |
||||
|
}, |
||||
|
{ |
||||
|
name: "Bangkok Airways", |
||||
|
}, |
||||
|
]; |
||||
|
const stopPoints = [ |
||||
|
{ |
||||
|
name: "Nonstop", |
||||
|
}, |
||||
|
{ |
||||
|
name: "Up to 1 stops", |
||||
|
}, |
||||
|
{ |
||||
|
name: "Up to 2 stops", |
||||
|
}, |
||||
|
{ |
||||
|
name: "Any number of stops", |
||||
|
}, |
||||
|
]; |
||||
|
|
||||
|
//
|
||||
|
const TabFilters = () => { |
||||
|
const [isOpenMoreFilter, setisOpenMoreFilter] = useState(false); |
||||
|
//
|
||||
|
const [isOnSale, setIsOnSale] = useState(true); |
||||
|
const [rangePrices, setRangePrices] = useState([100, 5000]); |
||||
|
const [tripTimes, setTripTimes] = useState(10); |
||||
|
const [stopPontsStates, setStopPontsStates] = useState<string[]>([]); |
||||
|
const [airlinesStates, setAirlinesStates] = useState<string[]>([]); |
||||
|
|
||||
|
//
|
||||
|
let [catTimes, setCatTimes] = useState({ |
||||
|
"Take Off": { |
||||
|
Departure: [0, 24], |
||||
|
Arrival: [0, 24], |
||||
|
}, |
||||
|
Landing: { |
||||
|
Departure: [0, 24], |
||||
|
Arrival: [0, 24], |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
//
|
||||
|
const closeModalMoreFilter = () => setisOpenMoreFilter(false); |
||||
|
const openModalMoreFilter = () => setisOpenMoreFilter(true); |
||||
|
|
||||
|
//
|
||||
|
const handleChangeStopPoint = (checked: boolean, name: string) => { |
||||
|
checked |
||||
|
? setStopPontsStates([...stopPontsStates, name]) |
||||
|
: setStopPontsStates(stopPontsStates.filter((i) => i !== name)); |
||||
|
}; |
||||
|
|
||||
|
const handleChangeAirlines = (checked: boolean, name: string) => { |
||||
|
checked |
||||
|
? setAirlinesStates([...airlinesStates, name]) |
||||
|
: setAirlinesStates(airlinesStates.filter((i) => i !== name)); |
||||
|
}; |
||||
|
|
||||
|
//
|
||||
|
|
||||
|
const renderXClear = () => { |
||||
|
return ( |
||||
|
<span className="w-4 h-4 rounded-full bg-primary-500 text-white flex items-center justify-center ml-3 cursor-pointer"> |
||||
|
<XMarkIcon className="h-3 w-3" /> |
||||
|
</span> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
const renderTabsTimeFlightTab = () => { |
||||
|
return ( |
||||
|
<div> |
||||
|
<Tab.Group> |
||||
|
<Tab.List className="flex p-1 space-x-1 bg-primary-900/10 rounded-xl"> |
||||
|
{Object.keys(catTimes).map((category) => ( |
||||
|
<Tab |
||||
|
key={category} |
||||
|
className={({ selected }) => |
||||
|
`w-full py-2.5 text-sm leading-5 font-medium text-primary-700 dark:text-primary-400 rounded-lg focus:outline-none focus:ring-2 ring-offset-2 ring-offset-blue-400 ring-white ring-opacity-60 ${ |
||||
|
selected |
||||
|
? "bg-white dark:bg-neutral-800 shadow" |
||||
|
: " hover:bg-white/[0.15] dark:hover:bg-neutral-800" |
||||
|
}`
|
||||
|
} |
||||
|
> |
||||
|
{category} |
||||
|
</Tab> |
||||
|
))} |
||||
|
</Tab.List> |
||||
|
<Tab.Panels className="mt-2"> |
||||
|
{Object.values(catTimes).map((posts, idx) => { |
||||
|
return ( |
||||
|
<Tab.Panel |
||||
|
key={idx} |
||||
|
className={ |
||||
|
"bg-neutral-50 dark:bg-neutral-900 rounded-xl p-3 space-y-8 focus:outline-none focus:ring-2 ring-offset-2 ring-offset-blue-400 ring-white ring-opacity-60" |
||||
|
} |
||||
|
> |
||||
|
<span className=" text-neutral-6000 dark:text-neutral-300 text-sm"> |
||||
|
{idx ? " Tokyo to Singapore" : " Singapore to Tokyo"} |
||||
|
</span> |
||||
|
<div></div> |
||||
|
<div className="space-y-3"> |
||||
|
<div className="flex space-x-2"> |
||||
|
<i className="text-lg las la-plane-departure"></i> |
||||
|
<span className="text-xs">Departure time:</span> |
||||
|
<span className="text-xs text-primary-500 dark:text-primary-400"> |
||||
|
{posts.Departure[0]}:00 - {posts.Departure[1]} |
||||
|
:00 |
||||
|
</span> |
||||
|
</div> |
||||
|
<Slider |
||||
|
range |
||||
|
min={0} |
||||
|
max={24} |
||||
|
defaultValue={posts.Departure} |
||||
|
onChange={(val) => |
||||
|
setCatTimes((catTimes) => |
||||
|
!idx |
||||
|
? { |
||||
|
...catTimes, |
||||
|
"Take Off": { |
||||
|
...posts, |
||||
|
Departure: val as [number, number], |
||||
|
}, |
||||
|
} |
||||
|
: { |
||||
|
...catTimes, |
||||
|
Landing: { |
||||
|
...posts, |
||||
|
Departure: val as [number, number], |
||||
|
}, |
||||
|
} |
||||
|
) |
||||
|
} |
||||
|
allowCross={false} |
||||
|
/> |
||||
|
</div> |
||||
|
<div className="space-y-3"> |
||||
|
<div className="flex space-x-2"> |
||||
|
<i className="text-lg las la-plane-arrival"></i> |
||||
|
<span className="text-xs">Arrival time:</span> |
||||
|
<span className="text-xs text-primary-500 dark:text-primary-400"> |
||||
|
{posts.Arrival[0]}:00 - {posts.Arrival[1]}:00 |
||||
|
</span> |
||||
|
</div> |
||||
|
<Slider |
||||
|
range |
||||
|
min={0} |
||||
|
max={24} |
||||
|
defaultValue={posts.Arrival} |
||||
|
onChange={(val) => |
||||
|
setCatTimes((catTimes) => |
||||
|
!idx |
||||
|
? { |
||||
|
...catTimes, |
||||
|
"Take Off": { |
||||
|
...posts, |
||||
|
Arrival: val as [number, number], |
||||
|
}, |
||||
|
} |
||||
|
: { |
||||
|
...catTimes, |
||||
|
Landing: { |
||||
|
...posts, |
||||
|
Arrival: val as [number, number], |
||||
|
}, |
||||
|
} |
||||
|
) |
||||
|
} |
||||
|
allowCross={false} |
||||
|
/> |
||||
|
</div> |
||||
|
</Tab.Panel> |
||||
|
); |
||||
|
})} |
||||
|
</Tab.Panels> |
||||
|
</Tab.Group> |
||||
|
</div> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
const renderTabsTypeOfAirlines = () => { |
||||
|
return ( |
||||
|
<Popover className="relative"> |
||||
|
{({ open, close }) => ( |
||||
|
<> |
||||
|
<Popover.Button |
||||
|
className={`flex items-center justify-center px-4 py-2 text-sm rounded-full border border-neutral-300 dark:border-neutral-700 focus:outline-none
|
||||
|
${open ? "!border-primary-500 " : ""} |
||||
|
${ |
||||
|
!!airlinesStates.length |
||||
|
? "!border-primary-500 bg-primary-50" |
||||
|
: "" |
||||
|
} |
||||
|
`}
|
||||
|
> |
||||
|
<span>Airlines</span> |
||||
|
{!airlinesStates.length ? ( |
||||
|
<i className="las la-angle-down ml-2"></i> |
||||
|
) : ( |
||||
|
<span onClick={() => setAirlinesStates([])}> |
||||
|
{renderXClear()} |
||||
|
</span> |
||||
|
)} |
||||
|
</Popover.Button> |
||||
|
<Transition |
||||
|
as={Fragment} |
||||
|
enter="transition ease-out duration-200" |
||||
|
enterFrom="opacity-0 translate-y-1" |
||||
|
enterTo="opacity-100 translate-y-0" |
||||
|
leave="transition ease-in duration-150" |
||||
|
leaveFrom="opacity-100 translate-y-0" |
||||
|
leaveTo="opacity-0 translate-y-1" |
||||
|
> |
||||
|
<Popover.Panel className="absolute z-10 w-screen max-w-sm px-4 mt-3 left-0 sm:px-0 lg:max-w-md"> |
||||
|
<div className="overflow-hidden rounded-2xl shadow-xl bg-white dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-700"> |
||||
|
<div className="relative flex flex-col px-5 py-6 space-y-5"> |
||||
|
<Checkbox |
||||
|
name="All Airlines" |
||||
|
label="All Airlines" |
||||
|
defaultChecked={airlinesStates.includes("All Airlines")} |
||||
|
onChange={(checked) => |
||||
|
handleChangeAirlines(checked, "All Airlines") |
||||
|
} |
||||
|
/> |
||||
|
<hr /> |
||||
|
{typeOfAirlines.map((item) => ( |
||||
|
<div key={item.name} className=""> |
||||
|
<Checkbox |
||||
|
name={item.name} |
||||
|
label={item.name} |
||||
|
defaultChecked={airlinesStates.includes(item.name)} |
||||
|
onChange={(checked) => |
||||
|
handleChangeAirlines(checked, item.name) |
||||
|
} |
||||
|
/> |
||||
|
</div> |
||||
|
))} |
||||
|
</div> |
||||
|
<div className="p-5 bg-neutral-50 dark:bg-neutral-900 dark:border-t dark:border-neutral-800 flex items-center justify-between"> |
||||
|
<ButtonThird |
||||
|
onClick={() => { |
||||
|
close(); |
||||
|
setAirlinesStates([]); |
||||
|
}} |
||||
|
sizeClass="px-4 py-2 sm:px-5" |
||||
|
> |
||||
|
Clear |
||||
|
</ButtonThird> |
||||
|
<ButtonPrimary |
||||
|
onClick={close} |
||||
|
sizeClass="px-4 py-2 sm:px-5" |
||||
|
> |
||||
|
Apply |
||||
|
</ButtonPrimary> |
||||
|
</div> |
||||
|
</div> |
||||
|
</Popover.Panel> |
||||
|
</Transition> |
||||
|
</> |
||||
|
)} |
||||
|
</Popover> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
const renderTabsStopPoints = () => { |
||||
|
return ( |
||||
|
<Popover className="relative"> |
||||
|
{({ open, close }) => ( |
||||
|
<> |
||||
|
<Popover.Button |
||||
|
className={`flex items-center justify-center px-4 py-2 text-sm rounded-full border border-neutral-300 dark:border-neutral-700 focus:outline-none
|
||||
|
${open ? "!border-primary-500 " : ""} |
||||
|
${ |
||||
|
!!stopPontsStates.length |
||||
|
? "!border-primary-500 bg-primary-50" |
||||
|
: "" |
||||
|
} |
||||
|
`}
|
||||
|
> |
||||
|
<span>Stop points</span> |
||||
|
{!stopPontsStates.length ? ( |
||||
|
<i className="las la-angle-down ml-2"></i> |
||||
|
) : ( |
||||
|
<span onClick={() => setStopPontsStates([])}> |
||||
|
{renderXClear()} |
||||
|
</span> |
||||
|
)} |
||||
|
</Popover.Button> |
||||
|
<Transition |
||||
|
as={Fragment} |
||||
|
enter="transition ease-out duration-200" |
||||
|
enterFrom="opacity-0 translate-y-1" |
||||
|
enterTo="opacity-100 translate-y-0" |
||||
|
leave="transition ease-in duration-150" |
||||
|
leaveFrom="opacity-100 translate-y-0" |
||||
|
leaveTo="opacity-0 translate-y-1" |
||||
|
> |
||||
|
<Popover.Panel className="absolute z-10 w-screen max-w-sm px-4 mt-3 left-0 sm:px-0 lg:max-w-md"> |
||||
|
<div className="overflow-hidden rounded-2xl shadow-xl bg-white dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-700"> |
||||
|
<div className="relative flex flex-col px-5 py-6 space-y-5"> |
||||
|
{stopPoints.map((item) => ( |
||||
|
<div key={item.name} className=""> |
||||
|
<Checkbox |
||||
|
name={item.name} |
||||
|
label={item.name} |
||||
|
defaultChecked={stopPontsStates.includes(item.name)} |
||||
|
onChange={(checked) => |
||||
|
handleChangeStopPoint(checked, item.name) |
||||
|
} |
||||
|
/> |
||||
|
</div> |
||||
|
))} |
||||
|
</div> |
||||
|
<div className="p-5 bg-neutral-50 dark:bg-neutral-900 dark:border-t dark:border-neutral-800 flex items-center justify-between"> |
||||
|
<ButtonThird |
||||
|
onClick={() => { |
||||
|
close(); |
||||
|
setStopPontsStates([]); |
||||
|
}} |
||||
|
sizeClass="px-4 py-2 sm:px-5" |
||||
|
> |
||||
|
Clear |
||||
|
</ButtonThird> |
||||
|
<ButtonPrimary |
||||
|
onClick={close} |
||||
|
sizeClass="px-4 py-2 sm:px-5" |
||||
|
> |
||||
|
Apply |
||||
|
</ButtonPrimary> |
||||
|
</div> |
||||
|
</div> |
||||
|
</Popover.Panel> |
||||
|
</Transition> |
||||
|
</> |
||||
|
)} |
||||
|
</Popover> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
const renderTabsTimeFlight = () => { |
||||
|
return ( |
||||
|
<Popover className="relative"> |
||||
|
{({ open, close }) => ( |
||||
|
<> |
||||
|
<Popover.Button |
||||
|
className={`flex items-center justify-center px-4 py-2 text-sm rounded-full border border-neutral-300 dark:border-neutral-700 focus:outline-none ${ |
||||
|
open ? "!border-primary-500 " : "" |
||||
|
}`}
|
||||
|
> |
||||
|
<span>Flight time</span> |
||||
|
<i className="las la-angle-down ml-2"></i> |
||||
|
</Popover.Button> |
||||
|
<Transition |
||||
|
as={Fragment} |
||||
|
enter="transition ease-out duration-200" |
||||
|
enterFrom="opacity-0 translate-y-1" |
||||
|
enterTo="opacity-100 translate-y-0" |
||||
|
leave="transition ease-in duration-150" |
||||
|
leaveFrom="opacity-100 translate-y-0" |
||||
|
leaveTo="opacity-0 translate-y-1" |
||||
|
> |
||||
|
<Popover.Panel className="absolute z-10 w-screen max-w-sm px-4 mt-3 left-0 sm:px-0 lg:max-w-md"> |
||||
|
<div className="overflow-hidden rounded-2xl shadow-xl bg-white dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-700"> |
||||
|
<div className="relative flex flex-col px-5 py-6 space-y-5"> |
||||
|
{renderTabsTimeFlightTab()} |
||||
|
</div> |
||||
|
<div className="p-5 bg-neutral-50 dark:bg-neutral-900 dark:border-t dark:border-neutral-800 flex items-center justify-between"> |
||||
|
<ButtonThird onClick={close} sizeClass="px-4 py-2 sm:px-5"> |
||||
|
Clear |
||||
|
</ButtonThird> |
||||
|
<ButtonPrimary |
||||
|
onClick={close} |
||||
|
sizeClass="px-4 py-2 sm:px-5" |
||||
|
> |
||||
|
Apply |
||||
|
</ButtonPrimary> |
||||
|
</div> |
||||
|
</div> |
||||
|
</Popover.Panel> |
||||
|
</Transition> |
||||
|
</> |
||||
|
)} |
||||
|
</Popover> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
const renderTabsTripTime = () => { |
||||
|
return ( |
||||
|
<Popover className="relative"> |
||||
|
{({ open, close }) => ( |
||||
|
<> |
||||
|
<Popover.Button |
||||
|
className={`flex items-center justify-center px-4 py-2 text-sm rounded-full border border-primary-500 bg-primary-50 text-primary-700 focus:outline-none `} |
||||
|
> |
||||
|
<span>less than {tripTimes} hours</span> |
||||
|
{renderXClear()} |
||||
|
</Popover.Button> |
||||
|
<Transition |
||||
|
as={Fragment} |
||||
|
enter="transition ease-out duration-200" |
||||
|
enterFrom="opacity-0 translate-y-1" |
||||
|
enterTo="opacity-100 translate-y-0" |
||||
|
leave="transition ease-in duration-150" |
||||
|
leaveFrom="opacity-100 translate-y-0" |
||||
|
leaveTo="opacity-0 translate-y-1" |
||||
|
> |
||||
|
<Popover.Panel className="absolute z-10 w-screen max-w-sm px-4 mt-3 left-0 sm:px-0 "> |
||||
|
<div className="overflow-hidden rounded-2xl shadow-xl bg-white dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-700"> |
||||
|
<div className="relative flex flex-col px-5 py-6 space-y-8"> |
||||
|
<div className="space-y-5"> |
||||
|
<div className="font-medium"> |
||||
|
Trip time: |
||||
|
<span className="text-sm font-normal ml-1 text-primary-500">{` <${tripTimes} hours`}</span> |
||||
|
</div> |
||||
|
|
||||
|
<Slider |
||||
|
min={1} |
||||
|
max={72} |
||||
|
defaultValue={tripTimes} |
||||
|
onChange={(e) => setTripTimes(e as number)} |
||||
|
/> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div className="p-5 bg-neutral-50 dark:bg-neutral-900 dark:border-t dark:border-neutral-800 flex items-center justify-between"> |
||||
|
<ButtonThird onClick={close} sizeClass="px-4 py-2 sm:px-5"> |
||||
|
Clear |
||||
|
</ButtonThird> |
||||
|
<ButtonPrimary |
||||
|
onClick={close} |
||||
|
sizeClass="px-4 py-2 sm:px-5" |
||||
|
> |
||||
|
Apply |
||||
|
</ButtonPrimary> |
||||
|
</div> |
||||
|
</div> |
||||
|
</Popover.Panel> |
||||
|
</Transition> |
||||
|
</> |
||||
|
)} |
||||
|
</Popover> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
const renderTabsPriceRage = () => { |
||||
|
return ( |
||||
|
<Popover className="relative"> |
||||
|
{({ open, close }) => ( |
||||
|
<> |
||||
|
<Popover.Button |
||||
|
className={`flex items-center justify-center px-4 py-2 text-sm rounded-full border border-primary-500 bg-primary-50 text-primary-700 focus:outline-none `} |
||||
|
> |
||||
|
<span> |
||||
|
{`$${convertNumbThousand( |
||||
|
rangePrices[0] |
||||
|
)} - $${convertNumbThousand(rangePrices[1])}`}{" "}
|
||||
|
</span> |
||||
|
{renderXClear()} |
||||
|
</Popover.Button> |
||||
|
<Transition |
||||
|
as={Fragment} |
||||
|
enter="transition ease-out duration-200" |
||||
|
enterFrom="opacity-0 translate-y-1" |
||||
|
enterTo="opacity-100 translate-y-0" |
||||
|
leave="transition ease-in duration-150" |
||||
|
leaveFrom="opacity-100 translate-y-0" |
||||
|
leaveTo="opacity-0 translate-y-1" |
||||
|
> |
||||
|
<Popover.Panel className="absolute z-10 w-screen max-w-sm px-4 mt-3 left-0 sm:px-0 "> |
||||
|
<div className="overflow-hidden rounded-2xl shadow-xl bg-white dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-700"> |
||||
|
<div className="relative flex flex-col px-5 py-6 space-y-8"> |
||||
|
<div className="space-y-5"> |
||||
|
<span className="font-medium">Price per person</span> |
||||
|
<Slider |
||||
|
range |
||||
|
min={100} |
||||
|
max={5000} |
||||
|
defaultValue={[rangePrices[0], rangePrices[1]]} |
||||
|
allowCross={false} |
||||
|
onChange={(e) => setRangePrices(e as number[])} |
||||
|
/> |
||||
|
</div> |
||||
|
|
||||
|
<div className="flex justify-between space-x-5"> |
||||
|
<div> |
||||
|
<label |
||||
|
htmlFor="minPrice" |
||||
|
className="block text-sm font-medium text-neutral-700 dark:text-neutral-300" |
||||
|
> |
||||
|
Min price |
||||
|
</label> |
||||
|
<div className="mt-1 relative rounded-md"> |
||||
|
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none"> |
||||
|
<span className="text-neutral-500 sm:text-sm"> |
||||
|
$ |
||||
|
</span> |
||||
|
</div> |
||||
|
<input |
||||
|
type="text" |
||||
|
name="minPrice" |
||||
|
disabled |
||||
|
id="minPrice" |
||||
|
className="focus:ring-indigo-500 focus:border-indigo-500 block w-full pl-7 pr-3 sm:text-sm border-neutral-200 rounded-full text-neutral-900" |
||||
|
value={rangePrices[0]} |
||||
|
/> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div> |
||||
|
<label |
||||
|
htmlFor="maxPrice" |
||||
|
className="block text-sm font-medium text-neutral-700 dark:text-neutral-300" |
||||
|
> |
||||
|
Max price |
||||
|
</label> |
||||
|
<div className="mt-1 relative rounded-md"> |
||||
|
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none"> |
||||
|
<span className="text-neutral-500 sm:text-sm"> |
||||
|
$ |
||||
|
</span> |
||||
|
</div> |
||||
|
<input |
||||
|
type="text" |
||||
|
disabled |
||||
|
name="maxPrice" |
||||
|
id="maxPrice" |
||||
|
className="focus:ring-indigo-500 focus:border-indigo-500 block w-full pl-7 pr-3 sm:text-sm border-neutral-200 rounded-full text-neutral-900" |
||||
|
value={rangePrices[1]} |
||||
|
/> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div className="p-5 bg-neutral-50 dark:bg-neutral-900 dark:border-t dark:border-neutral-800 flex items-center justify-between"> |
||||
|
<ButtonThird onClick={close} sizeClass="px-4 py-2 sm:px-5"> |
||||
|
Clear |
||||
|
</ButtonThird> |
||||
|
<ButtonPrimary |
||||
|
onClick={close} |
||||
|
sizeClass="px-4 py-2 sm:px-5" |
||||
|
> |
||||
|
Apply |
||||
|
</ButtonPrimary> |
||||
|
</div> |
||||
|
</div> |
||||
|
</Popover.Panel> |
||||
|
</Transition> |
||||
|
</> |
||||
|
)} |
||||
|
</Popover> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
const renderTabOnSale = () => { |
||||
|
return ( |
||||
|
<div |
||||
|
className={`flex items-center justify-center px-4 py-2 text-sm rounded-full border focus:outline-none cursor-pointer transition-all ${ |
||||
|
isOnSale |
||||
|
? "border-primary-500 bg-primary-50 text-primary-700" |
||||
|
: "border-neutral-300 dark:border-neutral-700" |
||||
|
}`}
|
||||
|
onClick={() => setIsOnSale(!isOnSale)} |
||||
|
> |
||||
|
<span>On sale</span> |
||||
|
{isOnSale && renderXClear()} |
||||
|
</div> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
const renderMoreFilterItem = ( |
||||
|
data: { |
||||
|
name: string; |
||||
|
description?: string; |
||||
|
defaultChecked?: boolean; |
||||
|
}[] |
||||
|
) => { |
||||
|
const list1 = data.filter((_, i) => i < data.length / 2); |
||||
|
const list2 = data.filter((_, i) => i >= data.length / 2); |
||||
|
return ( |
||||
|
<div className="grid grid-cols-2 gap-8"> |
||||
|
<div className="flex flex-col space-y-5"> |
||||
|
{list1.map((item) => ( |
||||
|
<Checkbox |
||||
|
key={item.name} |
||||
|
name={item.name} |
||||
|
subLabel={item.description} |
||||
|
label={item.name} |
||||
|
defaultChecked={!!item.defaultChecked} |
||||
|
/> |
||||
|
))} |
||||
|
</div> |
||||
|
<div className="flex flex-col space-y-5"> |
||||
|
{list2.map((item) => ( |
||||
|
<Checkbox |
||||
|
key={item.name} |
||||
|
name={item.name} |
||||
|
subLabel={item.description} |
||||
|
label={item.name} |
||||
|
defaultChecked={!!item.defaultChecked} |
||||
|
/> |
||||
|
))} |
||||
|
</div> |
||||
|
</div> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
// FOR RESPONSIVE MOBILE
|
||||
|
const renderTabMobileFilter = () => { |
||||
|
return ( |
||||
|
<div> |
||||
|
<div |
||||
|
className={`flex items-center justify-center px-4 py-2 text-sm rounded-full border border-primary-500 bg-primary-50 text-primary-700 focus:outline-none cursor-pointer`} |
||||
|
onClick={openModalMoreFilter} |
||||
|
> |
||||
|
<span> |
||||
|
<span className="hidden sm:inline">Flights</span> filters (3) |
||||
|
</span> |
||||
|
{renderXClear()} |
||||
|
</div> |
||||
|
|
||||
|
<Transition appear show={isOpenMoreFilter} as={Fragment}> |
||||
|
<Dialog |
||||
|
as="div" |
||||
|
className="fixed inset-0 z-50 overflow-y-auto" |
||||
|
onClose={closeModalMoreFilter} |
||||
|
> |
||||
|
<div className="min-h-screen text-center"> |
||||
|
<Transition.Child |
||||
|
as={Fragment} |
||||
|
enter="ease-out duration-300" |
||||
|
enterFrom="opacity-0" |
||||
|
enterTo="opacity-100" |
||||
|
leave="ease-in duration-200" |
||||
|
leaveFrom="opacity-100" |
||||
|
leaveTo="opacity-0" |
||||
|
> |
||||
|
<Dialog.Overlay className="fixed inset-0 bg-black bg-opacity-40 dark:bg-opacity-60" /> |
||||
|
</Transition.Child> |
||||
|
|
||||
|
{/* This element is to trick the browser into centering the modal contents. */} |
||||
|
<span |
||||
|
className="inline-block h-screen align-middle" |
||||
|
aria-hidden="true" |
||||
|
> |
||||
|
​ |
||||
|
</span> |
||||
|
<Transition.Child |
||||
|
className="inline-block py-8 px-2 h-screen w-full max-w-4xl" |
||||
|
enter="ease-out duration-300" |
||||
|
enterFrom="opacity-0 scale-95" |
||||
|
enterTo="opacity-100 scale-100" |
||||
|
leave="ease-in duration-200" |
||||
|
leaveFrom="opacity-100 scale-100" |
||||
|
leaveTo="opacity-0 scale-95" |
||||
|
> |
||||
|
<div className="inline-flex flex-col w-full max-w-4xl text-left align-middle transition-all transform overflow-hidden rounded-2xl bg-white dark:bg-neutral-900 dark:border dark:border-neutral-700 dark:text-neutral-100 shadow-xl h-full"> |
||||
|
<div className="relative flex-shrink-0 px-6 py-4 border-b border-neutral-200 dark:border-neutral-800 text-center"> |
||||
|
<Dialog.Title |
||||
|
as="h3" |
||||
|
className="text-lg font-medium leading-6 text-gray-900" |
||||
|
> |
||||
|
Flight filters |
||||
|
</Dialog.Title> |
||||
|
<span className="absolute left-3 top-3"> |
||||
|
<ButtonClose onClick={closeModalMoreFilter} /> |
||||
|
</span> |
||||
|
</div> |
||||
|
|
||||
|
<div className="flex-grow overflow-y-auto"> |
||||
|
<div className="px-4 md:px-10 divide-y divide-neutral-200 dark:divide-neutral-800"> |
||||
|
{/* --------- */} |
||||
|
{/* ---- */} |
||||
|
<div className="py-7"> |
||||
|
<h3 className="text-xl font-medium">Airlines</h3> |
||||
|
<div className="mt-6 relative "> |
||||
|
{renderMoreFilterItem(typeOfAirlines)} |
||||
|
</div> |
||||
|
</div> |
||||
|
{/* --------- */} |
||||
|
{/* ---- */} |
||||
|
<div className="py-7"> |
||||
|
<h3 className="text-xl font-medium">Stop points</h3> |
||||
|
<div className="mt-6 relative "> |
||||
|
{renderMoreFilterItem(stopPoints)} |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
{/* --------- */} |
||||
|
{/* ---- */} |
||||
|
<div className="py-7"> |
||||
|
<h3 className="text-xl font-medium">Range Prices</h3> |
||||
|
<div className="mt-6 relative "> |
||||
|
<div className="relative flex flex-col space-y-8"> |
||||
|
<div className="space-y-5"> |
||||
|
<Slider |
||||
|
range |
||||
|
className="text-red-400" |
||||
|
min={0} |
||||
|
max={2000} |
||||
|
defaultValue={[0, 1000]} |
||||
|
allowCross={false} |
||||
|
onChange={(e) => setRangePrices(e as number[])} |
||||
|
/> |
||||
|
</div> |
||||
|
|
||||
|
<div className="flex justify-between space-x-5"> |
||||
|
<div> |
||||
|
<label |
||||
|
htmlFor="minPrice" |
||||
|
className="block text-sm font-medium text-neutral-700 dark:text-neutral-300" |
||||
|
> |
||||
|
Min price |
||||
|
</label> |
||||
|
<div className="mt-1 relative rounded-md"> |
||||
|
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none"> |
||||
|
<span className="text-neutral-500 sm:text-sm"> |
||||
|
$ |
||||
|
</span> |
||||
|
</div> |
||||
|
<input |
||||
|
type="text" |
||||
|
name="minPrice" |
||||
|
disabled |
||||
|
id="minPrice" |
||||
|
className="focus:ring-indigo-500 focus:border-indigo-500 block w-full pl-7 pr-3 sm:text-sm border-neutral-200 rounded-full text-neutral-900" |
||||
|
value={rangePrices[0]} |
||||
|
/> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div> |
||||
|
<label |
||||
|
htmlFor="maxPrice" |
||||
|
className="block text-sm font-medium text-neutral-700 dark:text-neutral-300" |
||||
|
> |
||||
|
Max price |
||||
|
</label> |
||||
|
<div className="mt-1 relative rounded-md"> |
||||
|
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none"> |
||||
|
<span className="text-neutral-500 sm:text-sm"> |
||||
|
$ |
||||
|
</span> |
||||
|
</div> |
||||
|
<input |
||||
|
type="text" |
||||
|
disabled |
||||
|
name="maxPrice" |
||||
|
id="maxPrice" |
||||
|
className="focus:ring-indigo-500 focus:border-indigo-500 block w-full pl-7 pr-3 sm:text-sm border-neutral-200 rounded-full text-neutral-900" |
||||
|
value={rangePrices[1]} |
||||
|
/> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
{/* --------- */} |
||||
|
{/* ---- */} |
||||
|
<div className="py-7"> |
||||
|
<h3 className="text-xl font-medium"> |
||||
|
Strip times |
||||
|
<span className="text-sm font-normal ml-1 text-primary-500">{` <${tripTimes} hours`}</span> |
||||
|
</h3> |
||||
|
<div className="mt-6 relative "> |
||||
|
<Slider |
||||
|
min={1} |
||||
|
max={72} |
||||
|
defaultValue={tripTimes} |
||||
|
onChange={(e) => setTripTimes(e as number)} |
||||
|
/> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
{/* --------- */} |
||||
|
{/* ---- */} |
||||
|
<div className="py-7"> |
||||
|
<h3 className="text-xl font-medium">Flight times</h3> |
||||
|
<div className="relative flex flex-col py-5 space-y-5"> |
||||
|
{renderTabsTimeFlightTab()} |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div className="p-4 sm:p-6 flex-shrink-0 bg-neutral-50 dark:bg-neutral-900 dark:border-t dark:border-neutral-800 flex items-center justify-between"> |
||||
|
<ButtonThird |
||||
|
onClick={closeModalMoreFilter} |
||||
|
sizeClass="px-4 py-2 sm:px-5" |
||||
|
> |
||||
|
Clear |
||||
|
</ButtonThird> |
||||
|
<ButtonPrimary |
||||
|
onClick={closeModalMoreFilter} |
||||
|
sizeClass="px-4 py-2 sm:px-5" |
||||
|
> |
||||
|
Apply |
||||
|
</ButtonPrimary> |
||||
|
</div> |
||||
|
</div> |
||||
|
</Transition.Child> |
||||
|
</div> |
||||
|
</Dialog> |
||||
|
</Transition> |
||||
|
</div> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
return ( |
||||
|
<div className="flex lg:space-x-4"> |
||||
|
{/* FOR DESKTOP */} |
||||
|
<div className="hidden lg:flex space-x-4"> |
||||
|
{renderTabsTypeOfAirlines()} |
||||
|
{renderTabsTripTime()} |
||||
|
{renderTabsStopPoints()} |
||||
|
{renderTabsPriceRage()} |
||||
|
{renderTabsTimeFlight()} |
||||
|
{renderTabOnSale()} |
||||
|
</div> |
||||
|
|
||||
|
{/* FOR RESPONSIVE MOBILE */} |
||||
|
<div className="flex lg:hidden space-x-4"> |
||||
|
{renderTabMobileFilter()} |
||||
|
{renderTabOnSale()} |
||||
|
</div> |
||||
|
</div> |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
export default TabFilters; |
Write
Preview
Loading…
Cancel
Save
Reference in new issue