Skip to content

Commit

Permalink
convert item filter into a modal
Browse files Browse the repository at this point in the history
  • Loading branch information
nipuna-g committed Oct 24, 2023
1 parent 71ca716 commit 497fc08
Show file tree
Hide file tree
Showing 2 changed files with 230 additions and 87 deletions.
266 changes: 197 additions & 69 deletions client/components/map/FilterPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,117 +1,245 @@
import {
Box,
VStack,
HStack,
Spacer,
Button,
Text,
Slider,
SliderTrack,
SliderFilledTrack,
SliderThumb,
Modal,
ModalContent,
ModalOverlay,
Divider,
CheckboxGroup,
Checkbox,
Stack,
Button,
Flex,
useCheckbox,
chakra,
CheckboxProps,
useCheckboxGroup,
Spacer,
useRadio,
UseRadioProps,
useRadioGroup,
} from "@chakra-ui/react";
import { XButton } from "./Buttons";
import { COLORS } from "theme";
import { TItemSelection, TEmptyItem } from "app-context/SheetyContext/types";
import { OptionType } from "spa-pages";
import { ChangeEvent } from "react";
import React, { ChangeEvent, PropsWithChildren } from "react";
import { Methods } from "api/sheety/enums";

type FilterProps = {
isMobile: boolean | undefined;
setFilterShow: () => void;
isOpen: boolean;
filterApply: () => void;
handleSliderChange: (val: number) => void;
range: number;
itemState: (TItemSelection | TEmptyItem)[];
selectOptions: OptionType[];
handleCheckboxChange: (e: ChangeEvent<HTMLInputElement>) => void;
selectAllItems: () => void;
};

export const FilterPanel = ({
isMobile,
setFilterShow,
isOpen,
filterApply,
handleSliderChange,
range,
itemState,
selectOptions,
handleCheckboxChange,
selectAllItems,
}: FilterProps) => {
const selectedOptionsWithCheckedState = selectOptions.map((option) => {
const isChecked = itemState.some(
(item) => item.name === option.value && item.method === option.method,
);
return { ...option, isChecked };
});

return (
<Box
position={"fixed"}
height={isMobile ? "calc(92vh)" : "calc(90vh)"}
width={"100vw"}
bg="white"
zIndex={99999}
bottom={0}
overflowY={"scroll"}
overflowX={"hidden"}
>
<VStack p={7} w={"100vw"} gap={5}>
<HStack w="100%" justify="space-between">
<XButton onClick={setFilterShow} />
<Spacer />
<Button
color={COLORS.white}
bgColor={COLORS.Button.primary}
onClick={filterApply}
>
Apply
</Button>
</HStack>

<VStack w="100%">
<Text w="100%" textAlign={"left"} fontWeight={"bold"}>
Distance
</Text>
<HStack w="80%" justify="space-between">
<Modal isOpen={isOpen} onClose={() => undefined}>
<ModalOverlay />
<ModalContent maxWidth="calc(768px - 32px)" marginTop="140px" marginInline="4">
<FilterSection
title="Your items"
button={
<Button
size="sm"
color="white"
bgColor={COLORS.Button.primary}
onClick={filterApply}
>
Apply
</Button>
}
>
<CheckboxGroup
items={selectedOptionsWithCheckedState}
onChange={handleCheckboxChange}
onSelectAll={selectAllItems}
/>
</FilterSection>

<FilterSection title="Sort by">
<ChipRadioGroup items={["Nearest", "Most items"]} />
</FilterSection>

<FilterSection title="Max Distance" hideDivider={true}>
<HStack justify="space-between">
<Text>3km</Text>
<Text>10km</Text>
</HStack>
<Slider
min={30}
max={100}
w="80%"
aria-label="slider-ex-1"
defaultValue={range}
onChangeEnd={(val) => handleSliderChange(val)}
>
<SliderTrack>
<SliderFilledTrack />
<SliderFilledTrack background="#31979566" />
</SliderTrack>
<SliderThumb />
<SliderThumb background={COLORS.Button.primary} />
</Slider>
</VStack>
<Divider borderColor="gray.500" borderWidth="1px" />
<VStack align="flex-start" w="100%">
<Text textAlign={"left"} fontWeight={"bold"}>
Items
</Text>
<CheckboxGroup
colorScheme="blue"
defaultValue={itemState.map((item) => item.name)}
>
<Stack pl={1} spacing={3} direction={"column"}>
{selectOptions.map((item) => (
<Checkbox
onChange={(e) => handleCheckboxChange(e)}
key={item.idx}
data-key={item.idx}
value={item.value}
name={item.method}
>
{item.value}
</Checkbox>
))}
</Stack>
</CheckboxGroup>
</VStack>
</VStack>
</FilterSection>
</ModalContent>
</Modal>
);
};

function FilterSection({
title,
hideDivider,
button,
children,
}: React.PropsWithChildren<{ title: string; hideDivider?: boolean; button?: JSX.Element }>) {
return (
<Box w="100%" p={2} pb={0}>
<HStack justify="space-between" mb={3}>
<Text fontWeight={"bold"}>{title}</Text>
{button}
</HStack>
{children}
{hideDivider ? (
<Spacer mb={4} />
) : (
<Divider background="gray.200" h="1px" marginBlock={4} />
)}
</Box>
);
}

const CheckboxGroup = ({
items,
onChange,
onSelectAll,
}: {
items: Array<{
value: string;
method?: Methods;
isChecked: boolean;
}>;
onChange: (item: any) => void;
onSelectAll: () => void;
}) => {
const { getCheckboxProps } = useCheckboxGroup({
value: items.filter((item) => item.isChecked).map((item) => item.value),
});

return (
<Flex flex={1} gap={2} flexWrap="wrap" maxH="70px" overflow="auto" alignItems="center">
{items.map((item) => (
<ChipCheckbox
key={item.value}
{...getCheckboxProps({
value: item.value,
name: item.method,
})}
onChange={onChange}
/>
))}
<Text
color="black"
fontSize="12px"
fontWeight="bold"
cursor="pointer"
onClick={onSelectAll}
>
Select All
</Text>
</Flex>
);
};

const ChipCheckbox = (props: CheckboxProps) => {
const { state, getInputProps, htmlProps } = useCheckbox(props);

return (
<chakra.label {...htmlProps}>
<input {...getInputProps()} hidden />
<Chip isChecked={state.isChecked}>{props.value}</Chip>
</chakra.label>
);
};

const ChipRadioGroup = ({ items }: { items: Array<string> }) => {
const { getRootProps, getRadioProps } = useRadioGroup({
name: "sortBy",
defaultValue: "Nearest",
});

const group = getRootProps();

return (
<HStack {...group}>
{items.map((value) => {
const radio = getRadioProps({ value });
return (
<ChipRadio key={value} {...radio}>
{value}
</ChipRadio>
);
})}
</HStack>
);
};

const ChipRadio = (props: PropsWithChildren<UseRadioProps>) => {
const { getInputProps, getRadioProps, state } = useRadio(props);

const input = getInputProps();

return (
<Box as="label">
<input {...input} />
<Chip isChecked={state.isChecked} darkBackground={true}>
{props.children}
</Chip>
</Box>
);
};

const Chip = ({
children,
isChecked,
darkBackground,
}: React.PropsWithChildren<{ isChecked: boolean; darkBackground?: boolean }>) => {
const selectedColor = darkBackground ? "teal.500" : "teal.50";
const selectedTextColor = darkBackground ? "white" : "black";

return (
<Text
bg="white"
borderRadius="full"
paddingX="10px"
paddingY="5px"
background={isChecked ? selectedColor : "white"}
color={selectedTextColor && isChecked ? selectedTextColor : "black"}
border="1px solid #D6EAEA"
cursor="pointer"
whiteSpace="nowrap"
fontSize="12px"
>
{children}
</Text>
);
};
Loading

0 comments on commit 497fc08

Please sign in to comment.