@wix/headless-stores 0.0.57 → 0.0.58

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.
Files changed (33) hide show
  1. package/cjs/dist/react/ProductList.js +1 -0
  2. package/cjs/dist/react/ProductListSort.d.ts +14 -0
  3. package/cjs/dist/react/ProductListSort.js +14 -0
  4. package/cjs/dist/react/core/ProductList.d.ts +3 -0
  5. package/cjs/dist/react/core/ProductList.js +2 -0
  6. package/cjs/dist/react/core/ProductListFilters.d.ts +8 -180
  7. package/cjs/dist/react/core/ProductListFilters.js +137 -171
  8. package/cjs/dist/react/core/ProductListPagination.d.ts +0 -192
  9. package/cjs/dist/react/core/ProductListPagination.js +2 -160
  10. package/cjs/dist/react/core/ProductListSort.d.ts +9 -57
  11. package/cjs/dist/react/core/ProductListSort.js +32 -52
  12. package/cjs/dist/services/index.d.ts +2 -2
  13. package/cjs/dist/services/products-list-search-service.d.ts +3 -162
  14. package/cjs/dist/services/products-list-search-service.js +31 -424
  15. package/cjs/dist/services/products-list-service.d.ts +86 -4
  16. package/cjs/dist/services/products-list-service.js +125 -4
  17. package/dist/react/ProductList.js +1 -0
  18. package/dist/react/ProductListSort.d.ts +14 -0
  19. package/dist/react/ProductListSort.js +14 -0
  20. package/dist/react/core/ProductList.d.ts +3 -0
  21. package/dist/react/core/ProductList.js +2 -0
  22. package/dist/react/core/ProductListFilters.d.ts +8 -180
  23. package/dist/react/core/ProductListFilters.js +137 -171
  24. package/dist/react/core/ProductListPagination.d.ts +0 -192
  25. package/dist/react/core/ProductListPagination.js +2 -160
  26. package/dist/react/core/ProductListSort.d.ts +9 -57
  27. package/dist/react/core/ProductListSort.js +32 -52
  28. package/dist/services/index.d.ts +2 -2
  29. package/dist/services/products-list-search-service.d.ts +3 -162
  30. package/dist/services/products-list-search-service.js +31 -424
  31. package/dist/services/products-list-service.d.ts +86 -4
  32. package/dist/services/products-list-service.js +125 -4
  33. package/package.json +4 -4
@@ -1,54 +1,71 @@
1
- import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useService } from '@wix/services-manager-react';
3
- import { ProductsListSearchServiceDefinition, } from '../../services/products-list-search-service.js';
4
- /**
5
- * Headless component for managing inventory status filters
6
- *
7
- * @component
8
- * @example
9
- * ```tsx
10
- * import { ProductList, ProductListFilters } from '@wix/stores/components';
11
- *
12
- * function InventoryStatusFilter() {
13
- * return (
14
- * <ProductList.Root
15
- * productsListConfig={{ products: [], searchOptions: {}, pagingMetadata: {}, aggregations: {} }}
16
- * productsListSearchConfig={{ customizations: [] }}
17
- * >
18
- * <ProductListFilters.InventoryStatus>
19
- * {({ availableInventoryStatuses, selectedInventoryStatuses, toggleInventoryStatus }) => (
20
- * <div>
21
- * <h4>Inventory Status:</h4>
22
- * {availableInventoryStatuses.map(status => (
23
- * <label key={status}>
24
- * <input
25
- * type="checkbox"
26
- * checked={selectedInventoryStatuses.includes(status)}
27
- * onChange={() => toggleInventoryStatus(status)}
28
- * />
29
- * {status}
30
- * </label>
31
- * ))}
32
- * </div>
33
- * )}
34
- * </ProductListFilters.InventoryStatus>
35
- * </ProductList.Root>
36
- * );
37
- * }
38
- * ```
39
- */
40
- export function InventoryStatus(props) {
41
- const service = useService(ProductsListSearchServiceDefinition);
42
- const availableInventoryStatuses = service.availableInventoryStatuses.get();
43
- const selectedInventoryStatuses = service.selectedInventoryStatuses.get();
44
- const toggleInventoryStatus = service.toggleInventoryStatus;
45
- return typeof props.children === 'function'
46
- ? props.children({
47
- availableInventoryStatuses,
48
- selectedInventoryStatuses,
49
- toggleInventoryStatus,
50
- })
51
- : props.children;
3
+ import { ProductsListServiceDefinition, InventoryStatusType, CategoriesListServiceDefinition, } from '../../services/index.js';
4
+ import { useMemo } from 'react';
5
+ import { Filter as FilterPrimitive, } from '@wix/headless-components/react';
6
+ import { Slot } from '@radix-ui/react-slot';
7
+ import React from 'react';
8
+ // Conversion utilities for platform compatibility
9
+ function getInventoryStatusLabel(status) {
10
+ switch (status) {
11
+ case InventoryStatusType.IN_STOCK:
12
+ return 'In Stock';
13
+ case InventoryStatusType.OUT_OF_STOCK:
14
+ return 'Out of Stock';
15
+ case InventoryStatusType.PARTIALLY_OUT_OF_STOCK:
16
+ return 'Limited Stock';
17
+ default:
18
+ return String(status);
19
+ }
20
+ }
21
+ function buildSearchFilterData(availableOptions, availableInventoryStatuses, availableMinPrice, availableMaxPrice) {
22
+ // Build consolidated filter options using search field names
23
+ const filterOptions = [
24
+ // Price range - use a logical key that maps to both min/max fields
25
+ {
26
+ key: 'priceRange',
27
+ label: 'Price Range',
28
+ type: 'range',
29
+ displayType: 'range',
30
+ validValues: [availableMinPrice, availableMaxPrice],
31
+ valueFormatter: (value) => `$${value}`,
32
+ fieldName: [
33
+ 'actualPriceRange.minValue.amount',
34
+ 'actualPriceRange.maxValue.amount',
35
+ ],
36
+ },
37
+ // Product options (colors, sizes, etc.) - individual filters for each option type
38
+ ...availableOptions.map((option) => ({
39
+ key: option.id,
40
+ label: String(option.name),
41
+ type: 'multi',
42
+ displayType: option.optionRenderType === 'SWATCH_CHOICES'
43
+ ? 'color'
44
+ : 'text',
45
+ fieldName: 'options.choicesSettings.choices.choiceId',
46
+ fieldType: 'array',
47
+ validValues: option.choices.map((choice) => choice.id),
48
+ valueFormatter: (value) => {
49
+ const choice = option.choices.find((c) => c.id === value);
50
+ const name = choice?.name || String(value);
51
+ return option.optionRenderType === 'SWATCH_CHOICES'
52
+ ? name.toLowerCase()
53
+ : name;
54
+ },
55
+ })),
56
+ // Inventory status - use actual search field name
57
+ {
58
+ key: 'inventory.availabilityStatus',
59
+ label: 'Availability',
60
+ type: 'multi',
61
+ displayType: 'text',
62
+ fieldName: 'inventory.availabilityStatus',
63
+ fieldType: 'singular',
64
+ validValues: availableInventoryStatuses,
65
+ valueFormatter: (value) => getInventoryStatusLabel(value),
66
+ },
67
+ ];
68
+ return { filterOptions };
52
69
  }
53
70
  /**
54
71
  * Headless component for resetting all filters
@@ -81,136 +98,85 @@ export function InventoryStatus(props) {
81
98
  * ```
82
99
  */
83
100
  export function ResetTrigger(props) {
84
- const service = useService(ProductsListSearchServiceDefinition);
85
- const resetFilters = service.reset;
86
- const isFiltered = service.isFiltered.get();
101
+ const service = useService(ProductsListServiceDefinition);
102
+ const resetFilters = service.resetFilter;
103
+ const isFiltered = service.isFiltered().get();
87
104
  return typeof props.children === 'function'
88
105
  ? props.children({ resetFilters, isFiltered })
89
106
  : props.children;
90
107
  }
91
- /**
92
- * Headless component for managing price range filters (combined min/max)
93
- *
94
- * @component
95
- * @example
96
- * ```tsx
97
- * import { ProductList, ProductListFilters } from '@wix/stores/components';
98
- *
99
- * function PriceRangeFilter() {
100
- * return (
101
- * <ProductList.Root
102
- * productsListConfig={{ products: [], searchOptions: {}, pagingMetadata: {}, aggregations: {} }}
103
- * productsListSearchConfig={{ customizations: [] }}
104
- * >
105
- * <ProductListFilters.PriceRange>
106
- * {({ minPrice, maxPrice, setSelectedMinPrice, setSelectedMaxPrice }) => (
107
- * <div className="price-range">
108
- * <h4>Price Range:</h4>
109
- * <div className="price-inputs">
110
- * <input
111
- * type="number"
112
- * value={minPrice}
113
- * onChange={(e) => setSelectedMinPrice(Number(e.target.value))}
114
- * placeholder="Min"
115
- * />
116
- * <span>to</span>
117
- * <input
118
- * type="number"
119
- * value={maxPrice}
120
- * onChange={(e) => setSelectedMaxPrice(Number(e.target.value))}
121
- * placeholder="Max"
122
- * />
123
- * </div>
124
- * </div>
125
- * )}
126
- * </ProductListFilters.PriceRange>
127
- * </ProductList.Root>
128
- * );
129
- * }
130
- * ```
131
- */
132
- export function PriceRange(props) {
133
- const service = useService(ProductsListSearchServiceDefinition);
134
- const selectedMinPrice = service.selectedMinPrice.get();
135
- const selectedMaxPrice = service.selectedMaxPrice.get();
136
- const availableMinPrice = service.availableMinPrice.get();
137
- const availableMaxPrice = service.availableMaxPrice.get();
138
- const setSelectedMinPrice = service.setSelectedMinPrice;
139
- const setSelectedMaxPrice = service.setSelectedMaxPrice;
140
- return typeof props.children === 'function'
141
- ? props.children({
142
- availableMinPrice,
143
- selectedMinPrice,
144
- selectedMaxPrice,
145
- availableMaxPrice,
146
- setSelectedMinPrice,
147
- setSelectedMaxPrice,
148
- })
149
- : props.children;
150
- }
151
108
  export function CategoryFilter(props) {
152
- const service = useService(ProductsListSearchServiceDefinition);
153
- const selectedCategory = service.selectedCategory.get();
154
- const setSelectedCategory = service.setSelectedCategory;
109
+ const categoriesService = useService(CategoriesListServiceDefinition);
110
+ const productListService = useService(ProductsListServiceDefinition);
111
+ const categories = categoriesService.categories.get();
112
+ const setSelectedCategory = (category) => {
113
+ const currentFilter = productListService.searchOptions.get().filter || {};
114
+ if (!category) {
115
+ delete currentFilter['allCategoriesInfo.categories'];
116
+ productListService.setFilter(currentFilter);
117
+ return;
118
+ }
119
+ productListService.setFilter({
120
+ ...currentFilter,
121
+ 'allCategoriesInfo.categories': {
122
+ $matchItems: [{ id: { $in: [category._id] } }],
123
+ },
124
+ });
125
+ };
126
+ const selectedCategoryId = productListService.searchOptions.get().filter['allCategoriesInfo.categories']?.$matchItems?.[0]?.id
127
+ ?.$in?.[0];
128
+ const selectedCategory = categories?.find((c) => c._id === selectedCategoryId) || null;
155
129
  return typeof props.children === 'function'
156
130
  ? props.children({ selectedCategory, setSelectedCategory })
157
131
  : props.children;
158
132
  }
159
133
  /**
160
- * Headless component that renders content for each product option in the list.
161
- * Maps over all available product options and provides each option through a render prop.
162
- * Only renders when options are available (not loading, no error, and has options).
163
- * This follows the same collection pattern as ProductList.ItemContent and CategoryList.ItemContent.
164
- *
165
- * @component
166
- * @example
167
- * ```tsx
168
- * import { ProductList, ProductListFilters } from '@wix/stores/components';
169
- *
170
- * function ProductOptionsFilter() {
171
- * return (
172
- * <ProductList.Root
173
- * productsListConfig={{ products: [], searchOptions: {}, pagingMetadata: {}, aggregations: {} }}
174
- * productsListSearchConfig={{ customizations: [] }}
175
- * >
176
- * <ProductListFilters.ProductOptions>
177
- * {({ option, selectedChoices, toggleChoice }) => (
178
- * <div key={option.id}>
179
- * <h4>{option.name}</h4>
180
- * {option.choices.map(choice => (
181
- * <label key={choice.id}>
182
- * <input
183
- * type="checkbox"
184
- * checked={selectedChoices.includes(choice.id)}
185
- * onChange={() => toggleChoice(choice.id)}
186
- * />
187
- * {choice.name}
188
- * </label>
189
- * ))}
190
- * </div>
191
- * )}
192
- * </ProductListFilters.ProductOptions>
193
- * </ProductList.Root>
194
- * );
195
- * }
196
- * ```
134
+ * Internal component that provides filter data for the Filter component.
135
+ * Consolidates data from both search and list services.
197
136
  */
198
- export function ProductOptions(props) {
199
- const service = useService(ProductsListSearchServiceDefinition);
200
- const availableOptions = service.availableProductOptions.get();
201
- const selectedProductOptions = service.selectedProductOptions.get();
202
- // Don't render if no options are available
203
- if (availableOptions.length === 0) {
204
- return null;
205
- }
206
- // Map over options and create render prop for each
207
- return (_jsx(_Fragment, { children: availableOptions.map((option) => {
208
- const selectedChoices = selectedProductOptions[option.id] || [];
209
- const toggleChoice = (choiceId) => {
210
- service.toggleProductOption(option.id, choiceId);
211
- };
212
- return typeof props.children === 'function'
213
- ? props.children({ option, selectedChoices, toggleChoice })
214
- : props.children;
215
- }) }));
137
+ function AllFilters(props) {
138
+ const listService = useService(ProductsListServiceDefinition);
139
+ // Get current filter state
140
+ const currentSearchOptions = listService.searchOptions.get();
141
+ const currentFilter = currentSearchOptions.filter;
142
+ // Get available filter data
143
+ const availableOptions = listService.availableProductOptions.get();
144
+ const availableInventoryStatuses = listService.availableInventoryStatuses.get();
145
+ const availableMinPrice = listService.minPrice.get();
146
+ const availableMaxPrice = listService.maxPrice.get();
147
+ // Get filter state
148
+ const resetFilters = listService.resetFilter;
149
+ const isFiltered = listService.isFiltered().get();
150
+ // Build filter options and handlers
151
+ const searchFilterData = useMemo(() => {
152
+ const { filterOptions } = buildSearchFilterData(availableOptions, availableInventoryStatuses, availableMinPrice, availableMaxPrice);
153
+ const updateFilter = (newFilter) => {
154
+ listService.setFilter(newFilter);
155
+ };
156
+ return {
157
+ filterValue: currentFilter,
158
+ filterOptions,
159
+ updateFilter,
160
+ clearFilters: resetFilters,
161
+ hasFilters: isFiltered,
162
+ };
163
+ }, [
164
+ availableOptions,
165
+ availableInventoryStatuses,
166
+ availableMinPrice,
167
+ availableMaxPrice,
168
+ currentFilter,
169
+ resetFilters,
170
+ isFiltered,
171
+ listService,
172
+ ]);
173
+ return typeof props.children === 'function'
174
+ ? props.children({ searchFilter: searchFilterData })
175
+ : props.children;
216
176
  }
177
+ export const Filter = React.forwardRef(({ children, className, asChild }, ref) => {
178
+ const Comp = asChild ? Slot : 'div';
179
+ return (_jsx(AllFilters, { children: ({ searchFilter }) => {
180
+ return (_jsx(FilterPrimitive.Root, { value: searchFilter.filterValue, onChange: searchFilter.updateFilter, filterOptions: searchFilter.filterOptions, children: _jsx(Comp, { className: className, ref: ref, children: children }) }));
181
+ } }));
182
+ });
@@ -1,195 +1,3 @@
1
- /**
2
- * Props for PageSize headless component
3
- */
4
- export interface PageSizeProps {
5
- /** Content to display (can be a render function receiving page size controls or ReactNode) */
6
- children: ((props: PageSizeRenderProps) => React.ReactNode) | React.ReactNode;
7
- }
8
- /**
9
- * Render props for PageSize component
10
- */
11
- export interface PageSizeRenderProps {
12
- /** Current page size (items per page) */
13
- currentLimit: number;
14
- /** Function to update the page size */
15
- setLimit: (limit: number) => void;
16
- }
17
- /**
18
- * Headless component for managing page size (items per page)
19
- *
20
- * @component
21
- * @example
22
- * ```tsx
23
- * import { ProductList, ProductListPagination } from '@wix/stores/components';
24
- *
25
- * function PageSizeSelector() {
26
- * return (
27
- * <ProductList.Root
28
- * productsListConfig={{ products: [], searchOptions: {}, pagingMetadata: {}, aggregations: {} }}
29
- * productsListSearchConfig={{ customizations: [] }}
30
- * >
31
- * <ProductListPagination.PageSize>
32
- * {({ currentLimit, setLimit }) => (
33
- * <div>
34
- * <label>Items per page:</label>
35
- * <select
36
- * value={currentLimit}
37
- * onChange={(e) => setLimit(Number(e.target.value))}
38
- * >
39
- * <option value={10}>10</option>
40
- * <option value={20}>20</option>
41
- * <option value={50}>50</option>
42
- * </select>
43
- * </div>
44
- * )}
45
- * </ProductListPagination.PageSize>
46
- * </ProductList.Root>
47
- * );
48
- * }
49
- * ```
50
- */
51
- export declare function PageSize(props: PageSizeProps): import("react").ReactNode;
52
- /**
53
- * Props for NextPageTrigger headless component
54
- */
55
- export interface NextPageTriggerProps {
56
- /** Content to display (can be a render function receiving next page controls or ReactNode) */
57
- children: ((props: NextPageTriggerRenderProps) => React.ReactNode) | React.ReactNode;
58
- }
59
- /**
60
- * Render props for NextPageTrigger component
61
- */
62
- export interface NextPageTriggerRenderProps {
63
- /** Function to navigate to the next page */
64
- nextPage: () => void;
65
- /** Whether there is a next page available */
66
- hasNextPage: boolean;
67
- }
68
- /**
69
- * Headless component for navigating to the next page
70
- *
71
- * @component
72
- * @example
73
- * ```tsx
74
- * import { ProductList, ProductListPagination } from '@wix/stores/components';
75
- *
76
- * function NextPageButton() {
77
- * return (
78
- * <ProductList.Root
79
- * productsListConfig={{ products: [], searchOptions: {}, pagingMetadata: {}, aggregations: {} }}
80
- * productsListSearchConfig={{ customizations: [] }}
81
- * >
82
- * <ProductListPagination.NextPageTrigger>
83
- * {({ nextPage, hasNextPage }) => (
84
- * <button
85
- * onClick={nextPage}
86
- * disabled={!hasNextPage}
87
- * className={hasNextPage ? 'enabled' : 'disabled'}
88
- * >
89
- * Next →
90
- * </button>
91
- * )}
92
- * </ProductListPagination.NextPageTrigger>
93
- * </ProductList.Root>
94
- * );
95
- * }
96
- * ```
97
- */
98
- export declare function NextPageTrigger(props: NextPageTriggerProps): import("react").ReactNode;
99
- /**
100
- * Props for PreviousPageTrigger headless component
101
- */
102
- export interface PreviousPageTriggerProps {
103
- /** Content to display (can be a render function receiving previous page controls or ReactNode) */
104
- children: ((props: PreviousPageTriggerRenderProps) => React.ReactNode) | React.ReactNode;
105
- }
106
- /**
107
- * Render props for PreviousPageTrigger component
108
- */
109
- export interface PreviousPageTriggerRenderProps {
110
- /** Function to navigate to the previous page */
111
- prevPage: () => void;
112
- /** Whether there is a previous page available */
113
- hasPrevPage: boolean;
114
- }
115
- /**
116
- * Headless component for navigating to the previous page
117
- *
118
- * @component
119
- * @example
120
- * ```tsx
121
- * import { ProductList, ProductListPagination } from '@wix/stores/components';
122
- *
123
- * function PrevPageButton() {
124
- * return (
125
- * <ProductList.Root
126
- * productsListConfig={{ products: [], searchOptions: {}, pagingMetadata: {}, aggregations: {} }}
127
- * productsListSearchConfig={{ customizations: [] }}
128
- * >
129
- * <ProductListPagination.PreviousPageTrigger>
130
- * {({ prevPage, hasPrevPage }) => (
131
- * <button
132
- * onClick={prevPage}
133
- * disabled={!hasPrevPage}
134
- * className={hasPrevPage ? 'enabled' : 'disabled'}
135
- * >
136
- * ← Previous
137
- * </button>
138
- * )}
139
- * </ProductListPagination.PreviousPageTrigger>
140
- * </ProductList.Root>
141
- * );
142
- * }
143
- * ```
144
- */
145
- export declare function PreviousPageTrigger(props: PreviousPageTriggerProps): import("react").ReactNode;
146
- /**
147
- * Props for FirstPageTrigger headless component
148
- */
149
- export interface FirstPageTriggerProps {
150
- /** Content to display (can be a render function receiving first page controls or ReactNode) */
151
- children: ((props: FirstPageTriggerRenderProps) => React.ReactNode) | React.ReactNode;
152
- }
153
- /**
154
- * Render props for FirstPageTrigger component
155
- */
156
- export interface FirstPageTriggerRenderProps {
157
- /** Function to navigate to the first page */
158
- navigateToFirstPage: () => void;
159
- /** Whether there is a previous page (indicating not on first page) */
160
- hasPrevPage: boolean;
161
- }
162
- /**
163
- * Headless component for navigating to the first page
164
- *
165
- * @component
166
- * @example
167
- * ```tsx
168
- * import { ProductList, ProductListPagination } from '@wix/stores/components';
169
- *
170
- * function FirstPageButton() {
171
- * return (
172
- * <ProductList.Root
173
- * productsListConfig={{ products: [], searchOptions: {}, pagingMetadata: {}, aggregations: {} }}
174
- * productsListSearchConfig={{ customizations: [] }}
175
- * >
176
- * <ProductListPagination.FirstPageTrigger>
177
- * {({ navigateToFirstPage, hasPrevPage }) => (
178
- * <button
179
- * onClick={navigateToFirstPage}
180
- * disabled={!hasPrevPage}
181
- * title="Go to first page"
182
- * >
183
- * ⏮ First
184
- * </button>
185
- * )}
186
- * </ProductListPagination.FirstPageTrigger>
187
- * </ProductList.Root>
188
- * );
189
- * }
190
- * ```
191
- */
192
- export declare function FirstPageTrigger(props: FirstPageTriggerProps): import("react").ReactNode;
193
1
  /**
194
2
  * Props for LoadMoreTrigger headless component
195
3
  */