Skip to content

Commit

Permalink
Merge pull request #16 from deco-sites/karla-discount-and-color-ajust
Browse files Browse the repository at this point in the history
fix: range filter
  • Loading branch information
karlaoshikawa authored Dec 4, 2024
2 parents 56b01a7 + 287c2f1 commit 0549641
Show file tree
Hide file tree
Showing 5 changed files with 493 additions and 220 deletions.
294 changes: 112 additions & 182 deletions components/search/FilterRange.tsx
Original file line number Diff line number Diff line change
@@ -1,195 +1,125 @@
import { useEffect, useId, useRef } from "preact/hooks";
import { RefObject } from "preact";
import { useSignal } from "@preact/signals";
import { formatPrice } from "site/sdk/format.ts";

function useDebounce(
// deno-lint-ignore no-explicit-any
func: (...args: any[]) => void,
timeout = 300,
// deno-lint-ignore no-explicit-any
): (...args: any[]) => void {
let timer: ReturnType<typeof setTimeout>;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => {
func(...args);
}, timeout);
};
import Avatar from "$store/components/ui/Avatar.tsx";
import { formatPrice } from "$store/sdk/format.ts";
import type {
Filter,
FilterToggle,
FilterToggleValue,
ProductListingPage,
} from "apps/commerce/types.ts";
import { parseRange } from "apps/commerce/utils/filters.ts";
import ClearFilters from "site/islands/ClearFilters.tsx";
import RangeFilter from "site/islands/RangeFilter.tsx";

interface Props {
filters: ProductListingPage["filters"];
/** @description O valor minimo esta definido em R$0,00 se quiser mudar o valor preencher o campo abaixo */
min?: number;
/** @description O valor maximo esta definido em R$10.000,00 se quiser mudar o valor preencher o campo abaixo */
max?: number;
}

const thumbsize = 14;
const isToggle = (filter: Filter): filter is FilterToggle =>
filter["@type"] === "FilterToggle";

interface FilterRangeProps {
min: number;
max: number;
currentUrlFilterPrice?: string;
currentMaxFacet?: string;
currentMinFacet?: string;
function ValueItem({ url, selected, label, quantity }: FilterToggleValue) {
console.log(url);
return (
<>
{quantity > 0 && (
<a href={url} rel="nofollow" class="flex items-center gap-2">
<div aria-checked={selected} class="checkbox" />
<span class="text-sm">{label}</span>
{quantity > 0 && (
<span class="text-sm text-base-300">({quantity})</span>
)}
</a>
)}
</>
);
}

function applyFilterPrice(
{ min, max, currentUrlFilterPrice }: FilterRangeProps,
) {
const searchParams = new URLSearchParams(currentUrlFilterPrice);
console.log("searchParams", searchParams);
searchParams.set("filter.price", `${min}:${max}`);
const newUrl = `${window.location.pathname}?${searchParams.toString()}`;
function FilterValues({ key, values }: FilterToggle) {
const flexDirection =
key === "tamanho" || key === "cor" ? "flex-row" : "flex-col";

globalThis.location.href = newUrl;
return (
<>
<ul class={`flex flex-wrap gap-2 ${flexDirection}`}>
{values.map((item) => {
const { url, selected, value } = item;

if (key === "cor" || key === "tamanho") {
return (
<a href={url} rel="nofollow">
<Avatar
content={value}
variant={selected ? "active" : "default"}
/>
</a>
);
}

if (key === "price") {
const range = parseRange(item.value);

return (
range && (
<>
<ValueItem
{...item}
label={`${formatPrice(range.from)} - ${formatPrice(
range.to
)}`}
/>
</>
)
);
}

return <ValueItem {...item} />;
})}
</ul>
</>
);
}

const debouncedApplyFilterPrice = useDebounce((arg) => applyFilterPrice(arg));

function FilterRange(
{
min: minValue,
max: maxValue,
currentUrlFilterPrice = "",
currentMinFacet,
currentMaxFacet,
}: FilterRangeProps,
) {
const id = useId();
const slider: RefObject<HTMLDivElement> = useRef(null);
const min: RefObject<HTMLInputElement> = useRef(null);
const max: RefObject<HTMLInputElement> = useRef(null);
const rangemin = useSignal(Number(currentMinFacet));
const rangemax = useSignal(Number(currentMaxFacet));

const avgvalueprimary = (rangemin.value + rangemax.value) / 2;
const dataValue = useSignal({
min: minValue,
max: maxValue,
rangewitdh: 0,
});

function draw(splitvalue: number) {
if (
min.current &&
max.current &&
slider.current &&
!!dataValue.value.rangewitdh
) {
min.current.setAttribute("max", `${splitvalue}`);
max.current.setAttribute("min", `${splitvalue}`);

// Set css
min.current.style.width = `${
Math.floor(
thumbsize +
((splitvalue - minValue) /
(maxValue - minValue)) *
(dataValue.value.rangewitdh - 2 * thumbsize),
)
}px`;
max.current.style.width = `${
Math.floor(
thumbsize +
((maxValue - splitvalue) /
(maxValue - minValue)) *
(dataValue.value.rangewitdh - 2 * thumbsize),
)
}px`;

min.current.style.left = "0px";
max.current.style.left = min.current.style.width;

slider.current.style.height = `${min.current.offsetHeight}px`;

if (Number(max.current.value) > maxValue) {
max.current.setAttribute("data-value", `${dataValue.value.max}`);
}

rangemin.value = Number(min.current.getAttribute("data-value"));
rangemax.value = Number(max.current.getAttribute("data-value"));
}
}

function update(props: FilterRangeProps): void {
if (min.current && max.current) {
const minvalue = props.min;
const maxvalue = props.max;

min.current.setAttribute("data-value", `${minvalue}`);
max.current.setAttribute("data-value", `${maxvalue}`);

const avgvalue = (minvalue + maxvalue) / 2;
draw(Math.round(avgvalue));
}
}

function handleInput(props: FilterRangeProps) {
update(props);
debouncedApplyFilterPrice({
min: rangemin.value,
max: rangemax.value,
currentUrlFilterPrice,
});
}

useEffect(() => {
if (slider.current) {
dataValue.value.rangewitdh = slider.current.offsetWidth;
draw(Math.round(avgvalueprimary));
}
}, []);

function Filters({ filters, min, max }: Props) {
return (
<div class="flex flex-col">
<div ref={slider} class="relative w-full text-center inline-block">
<label for="min" class="hidden">
Preço minimo
</label>
<input
ref={min}
id={`min-${id}`}
class="cursor-pointer absolute filter-range top-0"
name="min"
type="range"
step="1"
min={minValue}
max={Math.round(maxValue)}
data-value={rangemin.value}
onInput={(ev: React.ChangeEvent<HTMLInputElement>) =>
handleInput({
min: Math.round(Number(ev.currentTarget.value)),
max: rangemax.value,
})}
value={rangemin.value}
/>
<label for="max" class="hidden">
Preço máximo
</label>
<input
ref={max}
id={`max-${id}`}
class="cursor-pointer absolute filter-range top-0"
name="max"
type="range"
step="1"
min={minValue}
max={Math.round(maxValue)}
data-value={Math.round(rangemax.value)}
onInput={(ev: React.ChangeEvent<HTMLInputElement>) =>
handleInput({
max: Math.round(Number(ev.currentTarget.value)),
min: rangemin.value,
})}
value={Math.round(rangemax.value)}
/>
<ul class="flex flex-col py-4">
<div class="collapse collapse-plus collapse-open">
<input type="checkbox" />
<div class="mb-2.5 rounded-none border-b-[#e9e9e9] border-b border-solid collapse-title font-semibold text-[18px] leading-9 after:!w-[30px] after:!h-[30px] after:!flex after:!items-center after:!justify-center after:rounded-md after:border after:border-solid after:border-[#164195]">
SELECIONADOS
</div>
<div class="collapse-content">
{filters
.filter(isToggle)
.map((filter) =>
filter.values.map(
(item) => item.selected && <FilterValues {...filter} />
)
)}
<ClearFilters />
</div>
</div>
<div class="flex justify-end items-center">
<output class="font-caption text-caption">
{formatPrice(rangemin.value, "BRL")}
</output>
<span class="font-caption text-caption block mx-1">-</span>
<output class="font-caption text-caption">
{formatPrice(rangemax.value, "BRL")}
</output>
</div>
</div>

{filters.filter(isToggle).map((filter) =>
filter.label !== "Preço" ? (
<div class="collapse collapse-plus">
<input type="checkbox" />
<div class="mb-2.5 rounded-none border-b-[#e9e9e9] border-b border-solid collapse-title font-semibold text-[18px] leading-9 after:!w-[30px] after:!h-[30px] after:!flex after:!items-center after:!justify-center after:rounded-md after:border after:border-solid after:border-[#164195]">
{filter.label}
</div>
<div class="collapse-content">
<FilterValues {...filter} />
</div>
</div>
) : null
)}

<RangeFilter min={min} max={max} />
</ul>
);
}

export default FilterRange;
export default Filters;
Loading

0 comments on commit 0549641

Please sign in to comment.