@scayle/storefront-product-listing 1.8.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +79 -1
- package/dist/index.d.mts +1 -0
- package/dist/index.mjs +1 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +1 -1
- package/dist/runtime/composables/useProductListSort.d.ts +15 -29
- package/dist/runtime/composables/useProductListSort.js +33 -38
- package/dist/runtime/composables/useProductListingSeoData.d.ts +2 -1
- package/dist/runtime/composables/useProductListingSeoData.js +2 -4
- package/dist/runtime/types/sort.d.ts +9 -0
- package/dist/runtime/types/sort.js +0 -0
- package/dist/runtime/utils/category.d.ts +7 -0
- package/dist/runtime/utils/category.js +12 -0
- package/package.json +7 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,83 @@
|
|
|
1
1
|
# @scayle/storefront-product-listing
|
|
2
2
|
|
|
3
|
+
## 2.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- **BREAKING**: Removed the `defaultSortingOptions` export. It is now expected to explicitly pass the `sortingOptions` to `useProductListSort`.
|
|
8
|
+
|
|
9
|
+
**Before**
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { defaultSortingOptions } from "#storefront-product-listing";
|
|
13
|
+
const { sortLinks } = useProductListSort(useRoute(), {
|
|
14
|
+
sortingOptions: defaultSortingOptions,
|
|
15
|
+
});
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
const { sortLinks } = useProductListSort(useRoute());
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
(Both before approaches are equivalent.)
|
|
23
|
+
|
|
24
|
+
**After**
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { SORT_PRICE_ASC, SORT_PRICE_DESC } from "#storefront-product-listing";
|
|
28
|
+
const { sortLinks } = useProductListSort(useRoute(), {
|
|
29
|
+
sortingOptions: [
|
|
30
|
+
{
|
|
31
|
+
...SORT_PRICE_ASC,
|
|
32
|
+
label: "Price Ascending",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
...SORT_PRICE_DESC,
|
|
36
|
+
label: "Price Descending",
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
- **BREAKING**: `useProductListingSeoData` now expects `isDefaultSortSelected` to be passed as the final parameter. This removes the internal dependency on `useProductListSort`. It is typed as `MaybeRefOrGetter<Boolean>` to support reactive usage.
|
|
43
|
+
|
|
44
|
+
**Before**
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
const route = useRoute();
|
|
48
|
+
const { title, robots, canonicalLink, categoryBreadcrumbSchema } =
|
|
49
|
+
useProductListingSeoData(breadcrumbs, route, {
|
|
50
|
+
baseUrl: baseUrl,
|
|
51
|
+
fullPath: fullPath,
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**After**
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
const route = useRoute()
|
|
59
|
+
const { isDefaultSortSelected } = useProductListSort(...)
|
|
60
|
+
const { title, robots, canonicalLink, categoryBreadcrumbSchema } = useProductListingSeoData(
|
|
61
|
+
breadcrumbs,
|
|
62
|
+
route,
|
|
63
|
+
{
|
|
64
|
+
baseUrl: baseUrl,
|
|
65
|
+
fullPath: fullPath,
|
|
66
|
+
},
|
|
67
|
+
isDefaultSortSelected,
|
|
68
|
+
)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Minor Changes
|
|
72
|
+
|
|
73
|
+
- Added exports for individual sort configs. `SORT_TOP_SELLER`, `SORT_DATE_NEWEST`, `SORT_PRICE_DESC`, `SORT_PRICE_ASC`, and `SORT_REDUCTION_DESC`. These can be used to easily construct a list of sort configs to pass to `useProductListSort`.
|
|
74
|
+
- Added new type exports `SelectedSort` and `SortLink`. This improves the experience of using `useProductListSort` by making the parameter and return types of this composable more accessible.
|
|
75
|
+
- The `sortingKey` for the default Topseller sort has changed from `scayle:v1:recommended` to `scayle:v1:balanced-offerings`.
|
|
76
|
+
|
|
77
|
+
### Patch Changes
|
|
78
|
+
|
|
79
|
+
- In `useProductListSort`, the `sortingOptions` option now accepts `MaybeRefOrGetter<SelectedSort[]>`. This allows for reactive usage of the composable.
|
|
80
|
+
|
|
3
81
|
## 1.8.1
|
|
4
82
|
|
|
5
83
|
### Patch Changes
|
|
@@ -143,7 +221,7 @@
|
|
|
143
221
|
They can be used as follows:
|
|
144
222
|
|
|
145
223
|
```ts
|
|
146
|
-
import { filtersFactory } from
|
|
224
|
+
import { filtersFactory } from "@scayle/storefront-product-listing";
|
|
147
225
|
```
|
|
148
226
|
|
|
149
227
|
- Upgrade the `storefront-core` and `storefront-nuxt` packages to version 8 and update your implementation to reflect the changes in RPC composables:
|
package/dist/index.d.mts
CHANGED
package/dist/index.mjs
CHANGED
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { defineNuxtModule, createResolver, addImportsDir } from '@nuxt/kit';
|
|
2
2
|
|
|
3
3
|
const PACKAGE_NAME = "@scayle/storefront-product-listing";
|
|
4
|
-
const PACKAGE_VERSION = "
|
|
4
|
+
const PACKAGE_VERSION = "2.0.0";
|
|
5
5
|
const module = defineNuxtModule({
|
|
6
6
|
meta: {
|
|
7
7
|
name: PACKAGE_NAME,
|
|
@@ -1,13 +1,6 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
4
|
-
type SelectedSort = ProductSortConfig & {
|
|
5
|
-
key: string;
|
|
6
|
-
label: string;
|
|
7
|
-
};
|
|
8
|
-
type SortLink = SelectedSort & {
|
|
9
|
-
to: RouteLocationRaw;
|
|
10
|
-
};
|
|
1
|
+
import type { ComputedRef, MaybeRefOrGetter } from 'vue';
|
|
2
|
+
import type { RouteLocationNormalizedLoadedGeneric } from 'vue-router';
|
|
3
|
+
import type { SelectedSort, SortLink } from '../types/sort.js';
|
|
11
4
|
export interface UseProductListSortReturn {
|
|
12
5
|
/** The currently selected sorting option. */
|
|
13
6
|
selectedSort: ComputedRef<SelectedSort | undefined>;
|
|
@@ -17,40 +10,34 @@ export interface UseProductListSortReturn {
|
|
|
17
10
|
isDefaultSortSelected: ComputedRef<boolean>;
|
|
18
11
|
}
|
|
19
12
|
export interface ProductListSortOptions {
|
|
20
|
-
sortingOptions
|
|
13
|
+
sortingOptions: MaybeRefOrGetter<SelectedSort[]>;
|
|
21
14
|
defaultSortingKey?: string;
|
|
22
15
|
}
|
|
23
|
-
export declare const
|
|
16
|
+
export declare const SORT_TOP_SELLER: {
|
|
24
17
|
key: string;
|
|
25
18
|
sortingKey: string;
|
|
26
19
|
direction: "desc";
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
} | {
|
|
20
|
+
};
|
|
21
|
+
export declare const SORT_DATE_NEWEST: {
|
|
30
22
|
key: string;
|
|
31
23
|
by: "new";
|
|
32
24
|
direction: "desc";
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
} | {
|
|
25
|
+
};
|
|
26
|
+
export declare const SORT_PRICE_DESC: {
|
|
36
27
|
key: string;
|
|
37
28
|
by: "price";
|
|
38
29
|
direction: "desc";
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
} | {
|
|
30
|
+
};
|
|
31
|
+
export declare const SORT_PRICE_ASC: {
|
|
42
32
|
key: string;
|
|
43
33
|
by: "price";
|
|
44
34
|
direction: "asc";
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
} | {
|
|
35
|
+
};
|
|
36
|
+
export declare const SORT_REDUCTION_DESC: {
|
|
48
37
|
key: string;
|
|
49
38
|
by: "reduction";
|
|
50
39
|
direction: "desc";
|
|
51
|
-
|
|
52
|
-
sortingKey?: undefined;
|
|
53
|
-
})[];
|
|
40
|
+
};
|
|
54
41
|
export declare const DEFAULT_SORTING_KEY = "top_seller";
|
|
55
42
|
/**
|
|
56
43
|
* Custom composable to handle product list sorting.
|
|
@@ -62,5 +49,4 @@ export declare const DEFAULT_SORTING_KEY = "top_seller";
|
|
|
62
49
|
*
|
|
63
50
|
* @returns Computed properties and methods related to sorting
|
|
64
51
|
*/
|
|
65
|
-
export declare function useProductListSort(route: RouteLocationNormalizedLoadedGeneric, { sortingOptions, defaultSortingKey, }
|
|
66
|
-
export {};
|
|
52
|
+
export declare function useProductListSort(route: RouteLocationNormalizedLoadedGeneric, { sortingOptions, defaultSortingKey, }: ProductListSortOptions): UseProductListSortReturn;
|
|
@@ -1,52 +1,47 @@
|
|
|
1
1
|
import { APISortOption, APISortOrder } from "@scayle/storefront-nuxt";
|
|
2
|
-
import { computed } from "vue";
|
|
3
|
-
export const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
{
|
|
29
|
-
key: "reduction_desc",
|
|
30
|
-
by: APISortOption.REDUCTION,
|
|
31
|
-
direction: APISortOrder.DESCENDING,
|
|
32
|
-
label: "sorting_select.reduction_desc"
|
|
33
|
-
}
|
|
34
|
-
];
|
|
2
|
+
import { computed, toValue } from "vue";
|
|
3
|
+
export const SORT_TOP_SELLER = {
|
|
4
|
+
key: "top_seller",
|
|
5
|
+
sortingKey: "scayle:v1:balanced-offerings",
|
|
6
|
+
direction: APISortOrder.DESCENDING
|
|
7
|
+
};
|
|
8
|
+
export const SORT_DATE_NEWEST = {
|
|
9
|
+
key: "date_newest",
|
|
10
|
+
by: APISortOption.DATE_ADDED,
|
|
11
|
+
direction: APISortOrder.DESCENDING
|
|
12
|
+
};
|
|
13
|
+
export const SORT_PRICE_DESC = {
|
|
14
|
+
key: "price_desc",
|
|
15
|
+
by: APISortOption.PRICE,
|
|
16
|
+
direction: APISortOrder.DESCENDING
|
|
17
|
+
};
|
|
18
|
+
export const SORT_PRICE_ASC = {
|
|
19
|
+
key: "price_asc",
|
|
20
|
+
by: APISortOption.PRICE,
|
|
21
|
+
direction: APISortOrder.ASCENDING
|
|
22
|
+
};
|
|
23
|
+
export const SORT_REDUCTION_DESC = {
|
|
24
|
+
key: "reduction_desc",
|
|
25
|
+
by: APISortOption.REDUCTION,
|
|
26
|
+
direction: APISortOrder.DESCENDING
|
|
27
|
+
};
|
|
35
28
|
export const DEFAULT_SORTING_KEY = "top_seller";
|
|
36
29
|
export function useProductListSort(route, {
|
|
37
|
-
sortingOptions
|
|
30
|
+
sortingOptions,
|
|
38
31
|
defaultSortingKey = DEFAULT_SORTING_KEY
|
|
39
|
-
}
|
|
32
|
+
}) {
|
|
40
33
|
const selectedSort = computed(() => {
|
|
41
|
-
const
|
|
34
|
+
const options = toValue(sortingOptions);
|
|
35
|
+
const sort = options.find(({ key }) => key === route.query.sort);
|
|
42
36
|
if (!sort) {
|
|
43
|
-
return
|
|
37
|
+
return options?.find(({ key }) => key === defaultSortingKey);
|
|
44
38
|
}
|
|
45
39
|
return sort;
|
|
46
40
|
});
|
|
47
41
|
const sortLinks = computed(() => {
|
|
48
42
|
const { page, ...query } = route.query;
|
|
49
|
-
|
|
43
|
+
const options = toValue(sortingOptions);
|
|
44
|
+
return options?.map((option) => ({
|
|
50
45
|
...option,
|
|
51
46
|
to: {
|
|
52
47
|
path: route.path,
|
|
@@ -22,8 +22,9 @@ export interface UseProductListingSeoDataReturn {
|
|
|
22
22
|
* @param params - URL data for constructing the canonical link.
|
|
23
23
|
* @param params.baseUrl The base URL of the application.
|
|
24
24
|
* @param params.fullPath - The full path of the current route.
|
|
25
|
+
* @param isDefaultSortSelected - If the default sort is applied on the listing.
|
|
25
26
|
*
|
|
26
27
|
* @returns An object containing reactive data and computed properties for managing SEO data.
|
|
27
28
|
*/
|
|
28
|
-
export declare function useProductListingSeoData(breadcrumbs: MaybeRefOrGetter<BreadcrumbItem[]>, route: RouteLocationNormalizedLoadedGeneric, { baseUrl, fullPath }: UrlParams): UseProductListingSeoDataReturn;
|
|
29
|
+
export declare function useProductListingSeoData(breadcrumbs: MaybeRefOrGetter<BreadcrumbItem[]>, route: RouteLocationNormalizedLoadedGeneric, { baseUrl, fullPath }: UrlParams, isDefaultSortSelected: MaybeRefOrGetter<boolean>): UseProductListingSeoDataReturn;
|
|
29
30
|
export {};
|
|
@@ -4,12 +4,10 @@ import {
|
|
|
4
4
|
sanitizeCanonicalURL
|
|
5
5
|
} from "@scayle/storefront-nuxt";
|
|
6
6
|
import { useAppliedFilters } from "./useAppliedFilters.js";
|
|
7
|
-
|
|
8
|
-
export function useProductListingSeoData(breadcrumbs, route, { baseUrl, fullPath }) {
|
|
7
|
+
export function useProductListingSeoData(breadcrumbs, route, { baseUrl, fullPath }, isDefaultSortSelected) {
|
|
9
8
|
const { areFiltersApplied } = useAppliedFilters(route);
|
|
10
|
-
const { isDefaultSortSelected } = useProductListSort(route);
|
|
11
9
|
const robots = computed(() => {
|
|
12
|
-
return !areFiltersApplied.value && isDefaultSortSelected
|
|
10
|
+
return !areFiltersApplied.value && toValue(isDefaultSortSelected) ? "index,follow" : "noindex,follow";
|
|
13
11
|
});
|
|
14
12
|
const canonicalLink = computed(
|
|
15
13
|
() => robots.value.includes("noindex") ? [] : [{
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ProductSortConfig } from '@scayle/storefront-nuxt';
|
|
2
|
+
import type { RouteLocationRaw } from 'vue-router';
|
|
3
|
+
export type SelectedSort = ProductSortConfig & {
|
|
4
|
+
key: string;
|
|
5
|
+
label: string;
|
|
6
|
+
};
|
|
7
|
+
export type SortLink = SelectedSort & {
|
|
8
|
+
to: RouteLocationRaw;
|
|
9
|
+
};
|
|
File without changes
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Category } from '@scayle/storefront-nuxt';
|
|
2
|
+
import type { SelectedSort } from '../types/sort.js';
|
|
2
3
|
/**
|
|
3
4
|
* Get all ancestors of a category.
|
|
4
5
|
* @param category - The category to get the ancestors of.
|
|
@@ -17,3 +18,9 @@ export declare const isSaleCategory: (category: Category) => boolean;
|
|
|
17
18
|
* @returns A flattened array of all categories in the tree.
|
|
18
19
|
*/
|
|
19
20
|
export declare const flattenCategoryTree: (categoryTree: Category[]) => Category[];
|
|
21
|
+
/**
|
|
22
|
+
* Create a default sorting config based on the current category
|
|
23
|
+
* @param category - The category to use for smart and custom sorting keys
|
|
24
|
+
* @returns The correct default sort config for the category
|
|
25
|
+
*/
|
|
26
|
+
export declare function createDefaultSortingOption(category?: Category): Omit<SelectedSort, 'label'>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SORT_TOP_SELLER } from "../composables/useProductListSort.js";
|
|
1
2
|
export const getCategoryAncestors = (category) => {
|
|
2
3
|
if (!category?.parent) {
|
|
3
4
|
return [];
|
|
@@ -21,3 +22,14 @@ export const flattenCategoryTree = (categoryTree) => {
|
|
|
21
22
|
).values()
|
|
22
23
|
];
|
|
23
24
|
};
|
|
25
|
+
export function createDefaultSortingOption(category) {
|
|
26
|
+
const smartSortingKey = category?.productSorting?.smartSortingKey;
|
|
27
|
+
const customSortingKey = category?.productSorting?.customSortingKey;
|
|
28
|
+
if (!smartSortingKey && !customSortingKey) {
|
|
29
|
+
return SORT_TOP_SELLER;
|
|
30
|
+
}
|
|
31
|
+
const sortingKey = [customSortingKey, smartSortingKey].filter(
|
|
32
|
+
(item) => !!item
|
|
33
|
+
);
|
|
34
|
+
return { ...SORT_TOP_SELLER, sortingKey };
|
|
35
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scayle/storefront-product-listing",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Collection of essential composables and utilities to work with product listing",
|
|
5
5
|
"author": "SCAYLE Commerce Engine",
|
|
6
6
|
"license": "MIT",
|
|
@@ -46,17 +46,17 @@
|
|
|
46
46
|
"@nuxt/kit": "3.16.2",
|
|
47
47
|
"@nuxt/module-builder": "1.0.1",
|
|
48
48
|
"@nuxt/schema": "3.16.2",
|
|
49
|
-
"@nuxt/test-utils": "3.
|
|
50
|
-
"@scayle/eslint-config-storefront": "4.5.
|
|
49
|
+
"@nuxt/test-utils": "3.19.2",
|
|
50
|
+
"@scayle/eslint-config-storefront": "4.5.12",
|
|
51
51
|
"@scayle/storefront-api": "18.9.0",
|
|
52
|
-
"@scayle/storefront-nuxt": "8.
|
|
53
|
-
"@types/node": "22.15.
|
|
52
|
+
"@scayle/storefront-nuxt": "8.32.1",
|
|
53
|
+
"@types/node": "22.15.34",
|
|
54
54
|
"@vitest/coverage-v8": "3.2.4",
|
|
55
55
|
"@vue/test-utils": "2.4.6",
|
|
56
56
|
"@vueuse/core": "13.4.0",
|
|
57
|
-
"dprint": "0.50.
|
|
57
|
+
"dprint": "0.50.1",
|
|
58
58
|
"eslint-formatter-gitlab": "6.0.1",
|
|
59
|
-
"eslint": "9.
|
|
59
|
+
"eslint": "9.30.0",
|
|
60
60
|
"fishery": "2.3.1",
|
|
61
61
|
"happy-dom": "18.0.1",
|
|
62
62
|
"nuxt": "3.16.2",
|