-
Notifications
You must be signed in to change notification settings - Fork 67
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(search): filters and design (#239)
- Loading branch information
1 parent
b294182
commit c7c5fa7
Showing
12 changed files
with
549 additions
and
355 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
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
45 changes: 45 additions & 0 deletions
45
templates/vue-demo-store/components/listing-filters/ListingFilter.vue
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 |
---|---|---|
@@ -0,0 +1,45 @@ | ||
<script setup lang="ts"> | ||
import { ListingFilter } from "@shopware-pwa/types"; | ||
import ListingFiltersPrice from "./ListingFiltersPrice.vue"; | ||
import ListingFiltersProperties from "./ListingFiltersProperties.vue"; | ||
import ListingFiltersRating from "./ListingFiltersRating.vue"; | ||
import ListingFiltersShippingFree from "./ListingFiltersShippingFree.vue"; | ||
const emit = defineEmits<{ | ||
( | ||
e: "selectFilterValue", | ||
{ code, value }: { code: string; value: string } | ||
): void; | ||
}>(); | ||
const props = defineProps<{ | ||
filter: ListingFilter; | ||
selectedFilters: { | ||
[key: string]: object; | ||
}; | ||
}>(); | ||
const cmsMap = () => { | ||
const map: { | ||
[key: string]: object; | ||
} = { | ||
manufacturer: ListingFiltersProperties, | ||
properties: ListingFiltersProperties, | ||
price: ListingFiltersPrice, | ||
rating: ListingFiltersRating, | ||
"shipping-free": ListingFiltersShippingFree, | ||
}; | ||
return map[props.filter?.code]; | ||
}; | ||
</script> | ||
<template> | ||
<div> | ||
<component | ||
:is="cmsMap()" | ||
:filter="filter" | ||
:selected-filters="selectedFilters" | ||
@select-value="emit('selectFilterValue', $event)" | ||
/> | ||
</div> | ||
</template> |
185 changes: 149 additions & 36 deletions
185
templates/vue-demo-store/components/listing-filters/ListingFilters.vue
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,55 +1,168 @@ | ||
<script setup lang="ts"> | ||
import { ListingFilterCode } from "@shopware-pwa/types"; | ||
import ListingFilter from "./ListingFilter.vue"; | ||
import { | ||
ComputedRef, | ||
UnwrapNestedRefs, | ||
computed, | ||
provide, | ||
reactive, | ||
} from "vue"; | ||
const { getAvailableFilters, setCurrentFilters, resetFilters } = useListing({ | ||
const { | ||
filtersToQuery, | ||
getCurrentFilters, | ||
getCurrentSortingOrder, | ||
getInitialFilters, | ||
search, | ||
} = useListing({ | ||
listingType: "productSearchListing", | ||
}); | ||
const mapComponent = (code: ListingFilterCode): string => { | ||
const components: { [key: string]: string } = { | ||
manufacturer: "ListingFiltersManufacturer", | ||
properties: "ListingFiltersProperties", | ||
price: "ListingFiltersPrice", | ||
rating: "ListingFiltersRating", | ||
"shipping-free": "ListingFiltersShippingFree", | ||
}; | ||
const route = useRoute(); | ||
const router = useRouter(); | ||
return components[code]; | ||
}; | ||
// @ToDo: clean up the "any" inside searchSelectedFilters | ||
/* eslint-disable */ | ||
const searchSelectedFilters: UnwrapNestedRefs<{ | ||
[key: string]: any; | ||
}> = reactive<{ | ||
manufacturer: Set<any>; | ||
properties: Set<any>; | ||
"min-price": string | number | undefined; | ||
"max-price": string | number | undefined; | ||
rating: string | number | undefined; | ||
"shipping-free": boolean | undefined; | ||
}>({ | ||
manufacturer: new Set(), | ||
properties: new Set(), | ||
"min-price": undefined, | ||
"max-price": undefined, | ||
rating: undefined, | ||
"shipping-free": undefined, | ||
}); | ||
/* eslint-enable */ | ||
const dirty = ref(false); | ||
const searchCriteriaForRequest: ComputedRef<{ | ||
[code: string]: string | string[] | number | number[] | boolean | undefined; | ||
}> = computed(() => ({ | ||
// turn Set to array and then into string with | separator | ||
manufacturer: [...searchSelectedFilters.manufacturer]?.join("|"), | ||
// turn Set to array and then into string with | separator | ||
properties: [...searchSelectedFilters.properties]?.join("|"), | ||
"min-price": searchSelectedFilters["min-price"], | ||
"max-price": searchSelectedFilters["max-price"], | ||
order: getCurrentSortingOrder.value, | ||
"shipping-free": searchSelectedFilters["shipping-free"], | ||
rating: searchSelectedFilters["rating"], | ||
})); | ||
const updateFilter = (filter: { | ||
for (const param in route.query) { | ||
if (searchSelectedFilters.hasOwnProperty(param)) { | ||
if ( | ||
searchSelectedFilters[param] && | ||
typeof searchSelectedFilters[param] === "object" | ||
) { | ||
(route.query[param] as unknown as string) | ||
.split("|") | ||
.forEach((element) => { | ||
searchSelectedFilters[param].add(element); | ||
}); | ||
} else { | ||
searchSelectedFilters[param] = route.query[param]; | ||
} | ||
} | ||
} | ||
const onOptionSelectToggle = async ({ | ||
code, | ||
value, | ||
}: { | ||
code: string; | ||
value: string | boolean | number; | ||
value: string; | ||
}) => { | ||
setCurrentFilters({ [filter.code]: filter.value }); | ||
dirty.value = true; | ||
if (!["properties", "manufacturer"].includes(code)) { | ||
searchSelectedFilters[code] = value; | ||
} else { | ||
if (searchSelectedFilters[code]?.has(value)) { | ||
searchSelectedFilters[code]?.delete(value); | ||
} else { | ||
searchSelectedFilters[code]?.add(value); | ||
} | ||
} | ||
await search({ | ||
...searchCriteriaForRequest.value, | ||
...route.query, | ||
}); | ||
await router.push({ | ||
query: { | ||
query: route.query.query, | ||
...filtersToQuery(searchCriteriaForRequest.value), | ||
}, | ||
}); | ||
}; | ||
const handleReset = () => { | ||
dirty.value = false; | ||
resetFilters(); | ||
const clearFilters = async () => { | ||
searchSelectedFilters.manufacturer.clear(); | ||
searchSelectedFilters.properties.clear(); | ||
searchSelectedFilters["min-price"] = undefined; | ||
searchSelectedFilters["max-price"] = undefined; | ||
searchSelectedFilters["rating"] = undefined; | ||
searchSelectedFilters["shipping-free"] = undefined; | ||
search({ | ||
...route.query, | ||
...filtersToQuery(searchCriteriaForRequest.value), | ||
}); | ||
await router.push({ | ||
query: { | ||
query: route.query.query, | ||
...filtersToQuery(searchCriteriaForRequest.value), | ||
}, | ||
}); | ||
}; | ||
async function invokeCleanFilters() { | ||
await search({ | ||
...route.query, | ||
}); | ||
clearFilters(); | ||
} | ||
const selectedOptionIds = computed(() => [ | ||
...searchSelectedFilters.properties, | ||
...searchSelectedFilters.manufacturer, | ||
]); | ||
provide("selectedOptionIds", selectedOptionIds); | ||
</script> | ||
|
||
<template> | ||
<div class="flex flex-wrap gap-5"> | ||
<component | ||
:is="mapComponent(filter.code)" | ||
v-for="filter in getAvailableFilters" | ||
:key="filter.code" | ||
:filter="filter" | ||
@select-value="updateFilter" | ||
/> | ||
|
||
<button | ||
v-show="dirty" | ||
class="justify-center py-2 px-6 border border-transparent shadow-sm text-md font-medium rounded-md text-white bg-black hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500" | ||
type="button" | ||
@click="handleReset" | ||
> | ||
{{ $t("listing.resetFilters") }} | ||
</button> | ||
<div v-if="getInitialFilters.length" class="flex flex-wrap"> | ||
<div | ||
v-for="filter in getInitialFilters" | ||
:key="`${filter?.id || filter?.code}`" | ||
class="mb-2 w-full" | ||
> | ||
<ListingFilter | ||
class="relative" | ||
:selected-filters="getCurrentFilters" | ||
:filter="filter" | ||
@select-filter-value="onOptionSelectToggle" | ||
/> | ||
</div> | ||
<div v-if="selectedOptionIds.length !== 0" class="mx-auto mt-4 mb-2"> | ||
<button | ||
class="w-full justify-center py-2 px-6 border border-transparent shadow-sm text-md font-medium rounded-md text-white bg-black hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500" | ||
type="button" | ||
@click="invokeCleanFilters" | ||
> | ||
{{ $t("search.resetFilters") }} | ||
<span | ||
class="w-6 h-6 i-carbon-close-filled inline-block align-middle ml-2" | ||
></span> | ||
</button> | ||
</div> | ||
</div> | ||
</div> | ||
</template> |
89 changes: 0 additions & 89 deletions
89
templates/vue-demo-store/components/listing-filters/ListingFiltersManufacturer.vue
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.