generated from deco-sites/storefront
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #16 from deco-sites/karla-discount-and-color-ajust
fix: range filter
- Loading branch information
Showing
5 changed files
with
493 additions
and
220 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
Oops, something went wrong.