@clicktap/ui 0.24.0 → 0.25.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.
Files changed (89) hide show
  1. package/components/Collection/AttributeDataProvider/AttributeDataProvider.d.ts +17 -0
  2. package/components/Collection/AttributeDataProvider/AttributeDataProvider.js +1 -0
  3. package/components/Collection/AttributeDataProvider/index.d.ts +1 -0
  4. package/components/Collection/AttributeDataProvider/index.js +1 -0
  5. package/components/Collection/CollectionFilterChips/CollectionFilterChips.d.ts +7 -0
  6. package/components/Collection/CollectionFilterChips/CollectionFilterChips.js +1 -0
  7. package/components/Collection/CollectionFilterChips/index.d.ts +1 -0
  8. package/components/Collection/CollectionFilterChips/index.js +1 -0
  9. package/components/Collection/CollectionFilters/CollectionFilters.d.ts +13 -0
  10. package/components/Collection/CollectionFilters/CollectionFilters.js +1 -0
  11. package/components/Collection/CollectionFilters/index.d.ts +1 -0
  12. package/components/Collection/CollectionFilters/index.js +1 -0
  13. package/components/Collection/CollectionToolbar/CollectionToolbar.d.ts +12 -0
  14. package/components/Collection/CollectionToolbar/CollectionToolbar.js +1 -0
  15. package/components/Collection/CollectionToolbar/index.d.ts +1 -0
  16. package/components/Collection/CollectionToolbar/index.js +1 -0
  17. package/components/Collection/EmptyResults/EmptyResults.d.ts +6 -0
  18. package/components/Collection/EmptyResults/EmptyResults.js +1 -0
  19. package/components/Collection/EmptyResults/index.d.ts +1 -0
  20. package/components/Collection/EmptyResults/index.js +1 -0
  21. package/components/Collection/FilterAccordionTitle/FilterAccordionTitle.d.ts +8 -0
  22. package/components/Collection/FilterAccordionTitle/FilterAccordionTitle.js +1 -0
  23. package/components/Collection/FilterAccordionTitle/index.d.ts +1 -0
  24. package/components/Collection/FilterAccordionTitle/index.js +1 -0
  25. package/components/Collection/FilterRenderer/FilterRenderer.d.ts +10 -0
  26. package/components/Collection/FilterRenderer/FilterRenderer.js +1 -0
  27. package/components/Collection/FilterRenderer/Renderer/Boolean.d.ts +9 -0
  28. package/components/Collection/FilterRenderer/Renderer/Boolean.js +1 -0
  29. package/components/Collection/FilterRenderer/Renderer/Option.d.ts +9 -0
  30. package/components/Collection/FilterRenderer/Renderer/Option.js +1 -0
  31. package/components/Collection/FilterRenderer/Renderer/Range.d.ts +6 -0
  32. package/components/Collection/FilterRenderer/Renderer/Range.js +1 -0
  33. package/components/Collection/FilterRenderer/Renderer/Swatch/SwatchColor.d.ts +9 -0
  34. package/components/Collection/FilterRenderer/Renderer/Swatch/SwatchColor.js +1 -0
  35. package/components/Collection/FilterRenderer/Renderer/Swatch/SwatchImage.d.ts +9 -0
  36. package/components/Collection/FilterRenderer/Renderer/Swatch/SwatchImage.js +1 -0
  37. package/components/Collection/FilterRenderer/Renderer/Swatch/SwatchText.d.ts +9 -0
  38. package/components/Collection/FilterRenderer/Renderer/Swatch/SwatchText.js +1 -0
  39. package/components/Collection/FilterRenderer/Renderer/Swatch/index.d.ts +3 -0
  40. package/components/Collection/FilterRenderer/Renderer/Swatch/index.js +1 -0
  41. package/components/Collection/FilterRenderer/Renderer/Swatch.d.ts +9 -0
  42. package/components/Collection/FilterRenderer/Renderer/Swatch.js +1 -0
  43. package/components/Collection/FilterRenderer/Renderer/index.d.ts +4 -0
  44. package/components/Collection/FilterRenderer/Renderer/index.js +1 -0
  45. package/components/Collection/FilterRenderer/Renderer/useOptionFilterToggle.d.ts +11 -0
  46. package/components/Collection/FilterRenderer/Renderer/useOptionFilterToggle.js +1 -0
  47. package/components/Collection/FilterRenderer/index.d.ts +1 -0
  48. package/components/Collection/FilterRenderer/index.js +1 -0
  49. package/components/Collection/FilterRenderer/types.d.ts +4 -0
  50. package/components/Collection/ProductCardSwatches/ProductCardSwatches.d.ts +35 -0
  51. package/components/Collection/ProductCardSwatches/ProductCardSwatches.js +1 -0
  52. package/components/Collection/ProductCardSwatches/index.d.ts +1 -0
  53. package/components/Collection/ProductCardSwatches/index.js +1 -0
  54. package/components/Collection/ProductPrice/ProductPrice.d.ts +12 -0
  55. package/components/Collection/ProductPrice/ProductPrice.js +1 -0
  56. package/components/Collection/ProductPrice/index.d.ts +1 -0
  57. package/components/Collection/ProductPrice/index.js +1 -0
  58. package/components/Collection/index.d.ts +9 -0
  59. package/components/Collection/index.js +1 -0
  60. package/components/Icon/CrossSmall.d.ts +3 -0
  61. package/components/Icon/CrossSmall.js +1 -0
  62. package/components/Icon/FilterIcon.d.ts +3 -0
  63. package/components/Icon/FilterIcon.js +1 -0
  64. package/components/Icon/index.d.ts +1 -0
  65. package/components/Icon/index.js +1 -1
  66. package/hooks/useAttributeData.d.ts +1 -0
  67. package/hooks/useAttributeData.js +1 -0
  68. package/hooks/useCollectionState/index.d.ts +2 -0
  69. package/hooks/useCollectionState/index.js +1 -0
  70. package/hooks/useCollectionState/useCollectionState.d.ts +142 -0
  71. package/hooks/useCollectionState/useCollectionState.js +1 -0
  72. package/package.json +5 -2
  73. package/types/collection.d.ts +113 -0
  74. package/utils/collection/buildFilterGroups.d.ts +11 -0
  75. package/utils/collection/buildFilterGroups.js +1 -0
  76. package/utils/collection/filterSearchParams.d.ts +13 -0
  77. package/utils/collection/filterSearchParams.js +1 -0
  78. package/utils/collection/getAppliedFilterChips.d.ts +2 -0
  79. package/utils/collection/getAppliedFilterChips.js +1 -0
  80. package/utils/collection/index.d.ts +5 -0
  81. package/utils/collection/index.js +1 -0
  82. package/utils/collection/mergeFilters.d.ts +2 -0
  83. package/utils/collection/mergeFilters.js +1 -0
  84. package/utils/collection/removeFilterChip.d.ts +2 -0
  85. package/utils/collection/removeFilterChip.js +1 -0
  86. package/utils/color.d.ts +4 -0
  87. package/utils/color.js +1 -0
  88. package/utils/swatch.d.ts +14 -0
  89. package/utils/swatch.js +1 -0
@@ -0,0 +1,17 @@
1
+ import { PropsWithChildren } from 'react';
2
+ import { ProductAttributeData, SwatchData } from '../../../types/collection';
3
+ type SwatchLookupEntry = SwatchData & {
4
+ attributeCode: string;
5
+ optionCode: string;
6
+ };
7
+ type AttributeDataContextValue = {
8
+ attributes: ProductAttributeData[];
9
+ getAttributeByCode: (code: string) => ProductAttributeData | undefined;
10
+ getSwatchLookup: () => Map<string, SwatchLookupEntry>;
11
+ };
12
+ export declare function useAttributeData(): AttributeDataContextValue;
13
+ type AttributeDataProviderProps = {
14
+ attributes: ProductAttributeData[];
15
+ };
16
+ export declare function AttributeDataProvider({ attributes, children, }: PropsWithChildren<AttributeDataProviderProps>): import("react/jsx-runtime").JSX.Element;
17
+ export {};
@@ -0,0 +1 @@
1
+ import{jsx as i}from"react/jsx-runtime";import{createContext as s,useContext as d,useMemo as p}from"react";const n=s(null);function m(){const t=d(n);if(!t)throw new Error("useAttributeData must be used within an AttributeDataProvider");return t}function C({attributes:t,children:u}){const a=p(()=>{const c=new Map(t.map(o=>[o.code,o])),r=new Map;for(const o of t)for(const e of o.options)e.swatch&&r.set(e.id,{...e.swatch,attributeCode:o.code,optionCode:e.code});return{attributes:t,getAttributeByCode:o=>c.get(o),getSwatchLookup:()=>r}},[t]);return i(n.Provider,{value:a,children:u})}export{C as AttributeDataProvider,m as useAttributeData};
@@ -0,0 +1 @@
1
+ export { AttributeDataProvider, useAttributeData, } from './AttributeDataProvider';
@@ -0,0 +1 @@
1
+ import{AttributeDataProvider as o,useAttributeData as a}from"./AttributeDataProvider.js";import"react/jsx-runtime";import"react";export{o as AttributeDataProvider,a as useAttributeData};
@@ -0,0 +1,7 @@
1
+ import { CollectionFilterAppliedInterface } from '../../../types/collection';
2
+ type CollectionFilterChipsProps = {
3
+ appliedFilters: CollectionFilterAppliedInterface[];
4
+ onFilterChange: (filters: CollectionFilterAppliedInterface[]) => void;
5
+ };
6
+ export declare function CollectionFilterChips({ appliedFilters, onFilterChange, }: CollectionFilterChipsProps): import("react/jsx-runtime").JSX.Element | null;
7
+ export {};
@@ -0,0 +1 @@
1
+ import{jsxs as l,jsx as e}from"react/jsx-runtime";import{useMemo as s}from"react";import{Button as i}from"../../Button/Button.js";import{CrossSmall as n}from"../../Icon/CrossSmall.js";import{getAppliedFilterChips as p}from"../../../utils/collection/getAppliedFilterChips.js";import{removeFilterChip as a}from"../../../utils/collection/removeFilterChip.js";import"react-aria-components";import"../../../utils/cn.js";function g({appliedFilters:o,onFilterChange:t}){const m=s(()=>p(o),[o]);return m.length===0?null:l("div",{className:"pt-6 mt-5 border-t border-solid border-gray-100",children:[e("div",{className:"flex gap-3 flex-wrap mb-3",children:m.map(r=>l(i,{variant:"outline",className:"px-2 py-1 h-6 font-medium text-xs flex items-center gap-x-1 rounded-md",onPress:()=>t(a(o,r)),children:[r.label,e(n,{})]},`${r.filterCode}-${r.optionId??"bool"}`))}),e(i,{variant:"ghost",className:"px-2 py-1 h-6 font-bold text-xs text-red-500",onPress:()=>t([]),children:"Clear All"})]})}export{g as CollectionFilterChips};
@@ -0,0 +1 @@
1
+ export { CollectionFilterChips } from './CollectionFilterChips';
@@ -0,0 +1 @@
1
+ import{CollectionFilterChips as f}from"./CollectionFilterChips.js";import"react/jsx-runtime";import"react";import"../../Button/Button.js";import"react-aria-components";import"../../../utils/cn.js";import"../../Icon/CrossSmall.js";import"../../../utils/collection/getAppliedFilterChips.js";import"../../../utils/collection/removeFilterChip.js";export{f as CollectionFilterChips};
@@ -0,0 +1,13 @@
1
+ import { CollectionFilterAppliedInterface, CollectionFilterAvailableInterface } from '../../../types/collection';
2
+ type CollectionFiltersProps = {
3
+ availableFilters: CollectionFilterAvailableInterface[];
4
+ appliedFilters: CollectionFilterAppliedInterface[];
5
+ onFilterChange: (filters: CollectionFilterAppliedInterface[]) => void;
6
+ showMobileFilters: boolean;
7
+ setShowMobileFilters: (open: boolean) => void;
8
+ searchQuery?: string;
9
+ onSearchChange?: (query: string) => void;
10
+ totalItems?: number;
11
+ };
12
+ export declare function CollectionFilters({ availableFilters, appliedFilters, onFilterChange, showMobileFilters, setShowMobileFilters, searchQuery, onSearchChange, totalItems, }: CollectionFiltersProps): import("react/jsx-runtime").JSX.Element;
13
+ export {};
@@ -0,0 +1 @@
1
+ import{jsxs as o,Fragment as f,jsx as e}from"react/jsx-runtime";import{useMemo as b,Suspense as x,useState as v}from"react";import{Accordion as w}from"../../Accordion/Accordion.js";import{AccordionItem as g}from"@nextui-org/accordion";import{Button as h}from"../../Button/Button.js";import{ModalOverlay as y}from"../../ModalOverlay/ModalOverlay.js";import{Modal as N}from"../../Modal/Modal.js";import{Drawer as k}from"../../Drawer/Drawer.js";import{Cross as C}from"../../Icon/Cross.js";import{CrossSmall as A}from"../../Icon/CrossSmall.js";import{Search as D}from"../../Icon/Search.js";import{FilterRenderer as I}from"../FilterRenderer/FilterRenderer.js";import{mergeFilters as S}from"../../../utils/collection/mergeFilters.js";import{FilterAccordionTitle as j}from"../FilterAccordionTitle/FilterAccordionTitle.js";import{EmptyResults as L}from"../EmptyResults/EmptyResults.js";import"../../../utils/cn.js";import"../../Icon/DownArrow.js";import"react-aria-components";import"framer-motion";import"../../DialogTrigger/DialogTrigger.js";import"../../../utils/env.js";import"../../../utils/swatch.js";import"../FilterRenderer/Renderer/Option.js";import"../../Checkbox/Checkbox.js";import"../FilterRenderer/Renderer/useOptionFilterToggle.js";import"../FilterRenderer/Renderer/Swatch.js";import"../FilterRenderer/Renderer/Swatch/SwatchColor.js";import"../../Icon/CheckIcon.js";import"../../../utils/color.js";import"../FilterRenderer/Renderer/Swatch/SwatchImage.js";import"../FilterRenderer/Renderer/Swatch/SwatchText.js";import"../FilterRenderer/Renderer/Boolean.js";import"../../RadioGroup/RadioGroup.js";import"../../Radio/Radio.js";import"../FilterRenderer/Renderer/Range.js";function M(){return e("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",xmlns:"http://www.w3.org/2000/svg",children:e("path",{d:"M4 6L8 10L12 6",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round"})})}function V({value:n,onChange:r}){const[l,c]=v(n),m=()=>{r(l.trim())};return o("div",{className:"relative",children:[e("input",{type:"search",value:l,onChange:i=>c(i.target.value),onKeyDown:i=>{i.key==="Enter"&&(i.preventDefault(),m())},onBlur:m,placeholder:"Search products...","aria-label":"Search products",autoComplete:"off",className:"w-full h-9 pl-8 pr-8 rounded-md border border-slate-300 bg-white text-sm text-slate-900 placeholder:text-slate-400 focus:outline-none focus:border-slate-400 [&::-webkit-search-cancel-button]:hidden"}),l&&e("button",{type:"button",onClick:()=>{c(""),r("")},"aria-label":"Clear search",className:"absolute right-2 top-1/2 -translate-y-1/2 p-0.5 rounded-full hover:bg-slate-100 transition-colors",children:e(A,{})}),e("span",{className:"absolute left-2.5 top-1/2 -translate-y-1/2 w-3.5 h-3.5 [&>svg]:w-full [&>svg]:h-full text-slate-400",children:e(D,{})})]})}function he({availableFilters:n,appliedFilters:r,onFilterChange:l,showMobileFilters:c,setShowMobileFilters:m,searchQuery:s,onSearchChange:a,totalItems:i}){const p=b(()=>S(r,n).filter(t=>t.includeInFilters!==!1),[r,n]),d=a&&e(V,{value:s??"",onChange:a},s),u=()=>p.length>0&&e(w,{selectionMode:"multiple",defaultSelectedKeys:"all",className:"pt-2 mt-3",showDivider:!1,itemClasses:{base:"py-1",title:"text-sm font-medium leading-7",trigger:"py-2 border-b border-gray-200",indicator:'data-[open="true"]:rotate-180',content:"pt-2 pb-3"},children:p.map(t=>e(g,{textValue:t.label,title:e(j,{filter:t,appliedFilters:r,onFilterChange:l}),disableIndicatorAnimation:!0,indicator:M,children:e("div",{className:"pt-2",children:e(I,{filter:t,onFilterChange:l,appliedFilters:r,variant:"sidebar"})})},t.code))});return o(f,{children:[o("div",{className:"hidden lg:flex lg:flex-col lg:basis-[16.5rem] lg:shrink-0",children:[d,u(),r.length>0&&e("button",{type:"button",className:"text-sm text-slate-500 hover:text-slate-700 transition-colors mt-4 mb-8",onClick:()=>l([]),children:"Clear all filters"})]}),e("div",{className:"lg:hidden",children:e(x,{children:e(y,{isOpen:c,onOpenChange:t=>m(t),isDismissable:!0,children:e(N,{children:e(k,{size:"30rem",direction:"left","aria-label":"Filters",className:"p-5",children:({close:t})=>o("div",{className:"flex flex-col w-full h-full overflow-hidden",children:[o("div",{className:"flex items-center w-full justify-between",children:[e("span",{className:"text-xl font-bold text-slate-800",children:"Filters"}),e(h,{variant:"ghost","aria-label":"Close",onPress:()=>t(),className:"justify-start size-auto p-0",children:e(C,{})})]}),o("div",{className:"flex flex-col flex-1 overflow-x-hidden overflow-y-auto [scrollbar-width:none] [-webkit-overflow-scrolling:touch] [&::-webkit-scrollbar]:hidden",children:[d&&e("div",{className:"mt-3",children:d}),u(),i===0&&e(L,{searchQuery:s,hasFilters:r.length>0,onClearFilters:()=>l([]),onClearSearch:s&&a?()=>a(""):void 0})]}),o("div",{className:"shrink-0 pt-3 flex flex-col gap-3 border-t border-gray-200",children:[r.length>0&&e("button",{type:"button",className:"text-sm py-2 text-slate-500 hover:text-slate-700 transition-colors",onClick:()=>l([]),children:"Clear all"}),e(h,{className:"w-full py-6 text-base rounded-xl",variant:"primary",onPress:()=>t(),children:"View Results"})]})]})})})})})})]})}export{he as CollectionFilters};
@@ -0,0 +1 @@
1
+ export { CollectionFilters } from './CollectionFilters';
@@ -0,0 +1 @@
1
+ import{CollectionFilters as L}from"./CollectionFilters.js";import"react/jsx-runtime";import"react";import"../../Accordion/Accordion.js";import"@nextui-org/accordion";import"../../../utils/cn.js";import"../../Icon/DownArrow.js";import"../../Button/Button.js";import"react-aria-components";import"../../ModalOverlay/ModalOverlay.js";import"framer-motion";import"../../DialogTrigger/DialogTrigger.js";import"../../Modal/Modal.js";import"../../Drawer/Drawer.js";import"../../Icon/Cross.js";import"../../Icon/CrossSmall.js";import"../../Icon/Search.js";import"../FilterRenderer/FilterRenderer.js";import"../../../utils/env.js";import"../../../utils/swatch.js";import"../FilterRenderer/Renderer/Option.js";import"../../Checkbox/Checkbox.js";import"../FilterRenderer/Renderer/useOptionFilterToggle.js";import"../FilterRenderer/Renderer/Swatch.js";import"../FilterRenderer/Renderer/Swatch/SwatchColor.js";import"../../Icon/CheckIcon.js";import"../../../utils/color.js";import"../FilterRenderer/Renderer/Swatch/SwatchImage.js";import"../FilterRenderer/Renderer/Swatch/SwatchText.js";import"../FilterRenderer/Renderer/Boolean.js";import"../../RadioGroup/RadioGroup.js";import"../../Radio/Radio.js";import"../FilterRenderer/Renderer/Range.js";import"../../../utils/collection/mergeFilters.js";import"../FilterAccordionTitle/FilterAccordionTitle.js";import"../EmptyResults/EmptyResults.js";export{L as CollectionFilters};
@@ -0,0 +1,12 @@
1
+ import { SortOrder } from '../../../types/collection';
2
+ type CollectionToolbarProps = {
3
+ totalItems: number;
4
+ startItem: number;
5
+ endItem: number;
6
+ setShowMobileFilters: (show: boolean) => void;
7
+ availableSortOrders: SortOrder[];
8
+ appliedSortOrder?: SortOrder;
9
+ onSortChange: (key: React.Key | null) => void;
10
+ };
11
+ export declare function CollectionToolbar({ totalItems, startItem, endItem, setShowMobileFilters, availableSortOrders, appliedSortOrder, onSortChange, }: CollectionToolbarProps): import("react/jsx-runtime").JSX.Element;
12
+ export {};
@@ -0,0 +1 @@
1
+ import{jsxs as t,jsx as l}from"react/jsx-runtime";import{DropdownSelect as x}from"../../Select/DropdownSelect.js";import{Option as d}from"../../Select/Option.js";import"../../Select/Select.js";import{Button as h}from"../../Button/Button.js";import{FilterIcon as p}from"../../Icon/FilterIcon.js";import"react-aria-components";import"../../../utils/cn.js";import"react";import"framer-motion";import"../../Loader/Pulse.js";function k({totalItems:o,startItem:s,endItem:n,setShowMobileFilters:a,availableSortOrders:r,appliedSortOrder:i,onSortChange:c}){const m=[...i?[i]:[],...r];return t("div",{className:"flex flex-col gap-y-5 mb-3",children:[t("div",{className:"flex items-center justify-between lg:justify-end gap-x-2",children:[t(h,{className:"lg:hidden grow-0 shrink-0 basis-auto gap-x-3 px-4",size:"sm",variant:"outline",onPress:()=>a(!0),"aria-details":"show filters",children:[l(p,{className:"w-3 h-3"}),"Filters"]}),l(x,{selectedKey:i?`${i.code}_${i.direction}`:null,onSelectionChange:c,placeholder:"Sort by",label:"Sort By:",classNames:{trigger:"text-xs w-44 h-8 min-h-0",value:"text-xs",label:"hidden lg:block text-xs text-gray-600 whitespace-nowrap"},children:m.map(e=>l(d,{id:`${e.code}_${e.direction}`,textValue:e.label,children:e.label},`${e.code}_${e.direction}`))})]}),o>0&&t("p",{className:"text-sm text-slate-500",children:["Displaying ",s," - ",n," of ",o," items"]})]})}export{k as CollectionToolbar};
@@ -0,0 +1 @@
1
+ export { CollectionToolbar } from './CollectionToolbar';
@@ -0,0 +1 @@
1
+ import{CollectionToolbar as x}from"./CollectionToolbar.js";import"react/jsx-runtime";import"../../Select/DropdownSelect.js";import"react-aria-components";import"../../../utils/cn.js";import"../../Select/Option.js";import"../../Select/Select.js";import"react";import"framer-motion";import"../../Loader/Pulse.js";import"../../Button/Button.js";import"../../Icon/FilterIcon.js";export{x as CollectionToolbar};
@@ -0,0 +1,6 @@
1
+ export declare function EmptyResults({ searchQuery, hasFilters, onClearFilters, onClearSearch, }: {
2
+ searchQuery?: string;
3
+ hasFilters: boolean;
4
+ onClearFilters?: () => void;
5
+ onClearSearch?: () => void;
6
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1 @@
1
+ import{jsxs as o,jsx as t}from"react/jsx-runtime";function m({searchQuery:e,hasFilters:s,onClearFilters:n,onClearSearch:l}){const i=s&&n||e&&l;return o("div",{className:"flex flex-col items-center justify-center py-16 px-4 text-center col-span-full",children:[o("svg",{xmlns:"http://www.w3.org/2000/svg",width:"48",height:"48",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round",className:"text-slate-300 mb-4",children:[t("circle",{cx:"11",cy:"11",r:"8"}),t("path",{d:"m21 21-4.3-4.3"})]}),t("h3",{className:"text-lg font-semibold text-slate-800 mb-2",children:"No results found"}),e&&o("p",{className:"text-sm text-slate-500 mb-4",children:["We couldn't find anything matching “",e,"”"]}),!e&&!s&&t("p",{className:"text-sm text-slate-500 mb-4",children:"No products match your current selection."}),i&&o("div",{className:"flex items-center gap-3",children:[e&&l&&t("button",{type:"button",onClick:l,className:"text-sm font-medium text-slate-700 hover:text-slate-900 underline underline-offset-2 transition-colors",children:"Clear search"}),s&&n&&e&&l&&t("span",{className:"text-slate-300",children:"|"}),s&&n&&t("button",{type:"button",onClick:n,className:"text-sm font-medium text-slate-700 hover:text-slate-900 underline underline-offset-2 transition-colors",children:"Clear all filters"})]}),!i&&t("p",{className:"text-xs text-slate-400",children:"Try a different search term or browse our categories."})]})}export{m as EmptyResults};
@@ -0,0 +1 @@
1
+ export { EmptyResults } from './EmptyResults';
@@ -0,0 +1 @@
1
+ import{EmptyResults as p}from"./EmptyResults.js";import"react/jsx-runtime";export{p as EmptyResults};
@@ -0,0 +1,8 @@
1
+ import { CollectionFilterAppliedInterface, CollectionFilterAvailableInterface } from '../../../types/collection';
2
+ type FilterAccordionTitleProps = {
3
+ filter: CollectionFilterAppliedInterface | CollectionFilterAvailableInterface;
4
+ appliedFilters: CollectionFilterAppliedInterface[];
5
+ onFilterChange: (filters: CollectionFilterAppliedInterface[]) => void;
6
+ };
7
+ export declare function FilterAccordionTitle({ filter, appliedFilters, onFilterChange, }: FilterAccordionTitleProps): import("react/jsx-runtime").JSX.Element;
8
+ export {};
@@ -0,0 +1 @@
1
+ import{jsxs as i,jsx as l}from"react/jsx-runtime";import{CrossSmall as c}from"../../Icon/CrossSmall.js";import"../../../utils/cn.js";function u({filter:e,appliedFilters:n,onFilterChange:t}){const a=n.some(o=>o.code===e.code),r=o=>{o.stopPropagation(),t(n.filter(s=>s.code!==e.code))};return i("span",{className:"flex items-center gap-2 w-full",children:[e.label,a&&l("span",{role:"button",tabIndex:0,onClick:r,onPointerDown:o=>o.stopPropagation(),onKeyDown:o=>{(o.key==="Enter"||o.key===" ")&&(o.preventDefault(),r(o))},"aria-label":`Clear ${e.label} filter`,className:"p-1 rounded-full hover:bg-slate-100 transition-colors cursor-pointer",children:l(c,{})})]})}export{u as FilterAccordionTitle};
@@ -0,0 +1 @@
1
+ export { FilterAccordionTitle } from './FilterAccordionTitle';
@@ -0,0 +1 @@
1
+ import{FilterAccordionTitle as p}from"./FilterAccordionTitle.js";import"react/jsx-runtime";import"../../Icon/CrossSmall.js";import"../../../utils/cn.js";export{p as FilterAccordionTitle};
@@ -0,0 +1,10 @@
1
+ import { ReactNode } from 'react';
2
+ import { CollectionFilterAppliedInterface, CollectionFilterAvailableInterface } from '../../../types/collection';
3
+ type FilterRendererProps = {
4
+ filter: CollectionFilterAppliedInterface | CollectionFilterAvailableInterface;
5
+ onFilterChange: (filters: CollectionFilterAppliedInterface[]) => void;
6
+ appliedFilters: CollectionFilterAppliedInterface[];
7
+ variant?: 'drawer' | 'sidebar';
8
+ };
9
+ export declare function FilterRenderer({ filter, onFilterChange, appliedFilters, variant, }: FilterRendererProps): ReactNode;
10
+ export {};
@@ -0,0 +1 @@
1
+ import{jsx as t}from"react/jsx-runtime";import{isDevelopment as p}from"../../../utils/env.js";import{isSwatchRenderer as m}from"../../../utils/swatch.js";import{FilterRendererOption as c}from"./Renderer/Option.js";import{FilterRendererSwatch as a}from"./Renderer/Swatch.js";import{FilterRendererBoolean as f}from"./Renderer/Boolean.js";import{FilterRendererRange as d}from"./Renderer/Range.js";import"../../Checkbox/Checkbox.js";import"react-aria-components";import"../../../utils/cn.js";import"./Renderer/useOptionFilterToggle.js";import"./Renderer/Swatch/SwatchColor.js";import"../../Icon/CheckIcon.js";import"../../../utils/color.js";import"./Renderer/Swatch/SwatchImage.js";import"./Renderer/Swatch/SwatchText.js";import"../../RadioGroup/RadioGroup.js";import"../../Radio/Radio.js";function $({filter:n,onFilterChange:o,appliedFilters:i,variant:l="drawer"}){const r=n;switch(r.__typename){case"CollectionFilterAppliedOption":case"CollectionFilterAvailableOption":{const e=r;return m(e.renderer)?t(a,{filter:e,onFilterChange:o,appliedFilters:i,variant:l}):(p&&e.renderer!==null&&e.renderer!==void 0&&console.warn(`[Swatch] Unknown filterRenderer "${e.renderer}" for attribute "${e.code}" — falling back to default option filter`),t(c,{filter:e,onFilterChange:o,appliedFilters:i,variant:l}))}case"CollectionFilterAppliedBoolean":case"CollectionFilterAvailableBoolean":return t(f,{filter:r,onFilterChange:o,appliedFilters:i,variant:l});case"CollectionFilterAppliedRange":case"CollectionFilterAvailableRange":return t(d,{filter:r});default:return null}}export{$ as FilterRenderer};
@@ -0,0 +1,9 @@
1
+ import { CollectionFilterAppliedInterface, CollectionFilterAppliedBoolean, CollectionFilterAvailableBoolean } from '../../../../types/collection';
2
+ type FilterRendererBooleanProps = {
3
+ filter: CollectionFilterAppliedBoolean | CollectionFilterAvailableBoolean;
4
+ onFilterChange: (filters: CollectionFilterAppliedInterface[]) => void;
5
+ appliedFilters: CollectionFilterAppliedInterface[];
6
+ variant?: 'drawer' | 'sidebar';
7
+ };
8
+ export declare function FilterRendererBoolean({ filter, onFilterChange, appliedFilters, variant, }: FilterRendererBooleanProps): import("react/jsx-runtime").JSX.Element;
9
+ export default FilterRendererBoolean;
@@ -0,0 +1 @@
1
+ import{jsx as n,jsxs as a}from"react/jsx-runtime";import{RadioGroup as x}from"../../../RadioGroup/RadioGroup.js";import{Radio as d}from"../../../Radio/Radio.js";import"react-aria-components";import"../../../../utils/cn.js";function I({filter:e,onFilterChange:i,appliedFilters:t,variant:m="drawer"}){const p=l=>{const r=l==="true";if(l==="none"){i(t.filter(c=>c.code!==e.code));return}const h={__typename:"CollectionFilterAppliedBoolean",code:e.code,label:e.label,renderer:e.renderer,type:e.type,includeInFilters:e.includeInFilters,trueLabel:e.trueLabel,falseLabel:e.falseLabel,appliedValue:r};i([...t.filter(c=>c.code!==e.code),h])},b=()=>{const l=t.find(r=>r.code===e.code);return l?l.appliedValue?"true":"false":"none"},o="__typename"in e&&e.__typename==="CollectionFilterAvailableBoolean"?e.trueItemCount:null,s="__typename"in e&&e.__typename==="CollectionFilterAvailableBoolean"?e.falseItemCount:null,u=n(x,{value:b(),onChange:p,children:a("div",{className:"space-y-2",children:[n(d,{value:"none",children:n("span",{className:"text-sm",children:"None"})}),a("div",{className:"flex items-center justify-between",children:[n(d,{value:"true",children:n("span",{className:"text-sm",children:e.trueLabel})}),o!=null&&a("span",{className:"text-xs text-slate-500",children:["(",o,")"]})]}),a("div",{className:"flex items-center justify-between",children:[n(d,{value:"false",children:n("span",{className:"text-sm",children:e.falseLabel})}),s!=null&&a("span",{className:"text-xs text-slate-500",children:["(",s,")"]})]})]})});return m==="sidebar"?n("div",{className:"pb-2",children:u}):a("div",{className:"border border-slate-200 rounded-lg p-4",children:[n("h4",{className:"font-medium mb-3",children:e.label}),u]})}export{I as FilterRendererBoolean,I as default};
@@ -0,0 +1,9 @@
1
+ import { CollectionFilterAppliedInterface, CollectionFilterAppliedOption, CollectionFilterAvailableOption } from '../../../../types/collection';
2
+ type FilterRendererOptionProps = {
3
+ filter: CollectionFilterAppliedOption | CollectionFilterAvailableOption;
4
+ onFilterChange: (filters: CollectionFilterAppliedInterface[]) => void;
5
+ appliedFilters: CollectionFilterAppliedInterface[];
6
+ variant?: 'drawer' | 'sidebar';
7
+ };
8
+ export declare function FilterRendererOption({ filter, onFilterChange, appliedFilters, variant, }: FilterRendererOptionProps): import("react/jsx-runtime").JSX.Element;
9
+ export default FilterRendererOption;
@@ -0,0 +1 @@
1
+ import{jsx as r,jsxs as n}from"react/jsx-runtime";import{Checkbox as h}from"../../../Checkbox/Checkbox.js";import{useOptionFilterToggle as p}from"./useOptionFilterToggle.js";import"react-aria-components";import"../../../../utils/cn.js";function C({filter:o,onFilterChange:l,appliedFilters:i,variant:d="drawer"}){const{handleOptionToggle:m,getOptionIsChecked:u}=p({filter:o,onFilterChange:l,appliedFilters:i}),t=o.options.map(e=>{const a=u(e.code),c="itemCount"in e?e.itemCount:null;return r("div",{className:"flex items-center",children:r(h,{isSelected:a,onChange:s=>m(e.code,s),className:"cursor-pointer",children:({isHovered:s})=>n("span",{className:"text-sm transition-colors duration-150",style:{color:s?"#777777":"#303133"},children:[e.label,c!=null&&n("span",{className:"ml-1 transition-opacity duration-150",style:{color:"#777777",opacity:s?.7:1},children:["(",c,")"]})]})},`checkbox-${o.code}-${e.code}-${String(a)}`)},`${o.code}-${e.code}`)});return d==="sidebar"?r("div",{className:"space-y-2 pb-2",children:t}):n("div",{className:"border border-slate-200 rounded-lg p-4",children:[r("h4",{className:"font-medium mb-3",children:o.label}),r("div",{className:"space-y-2 max-h-60 overflow-y-auto",children:t})]})}export{C as FilterRendererOption,C as default};
@@ -0,0 +1,6 @@
1
+ import { CollectionFilterAppliedRange, CollectionFilterAvailableRange } from '../../../../types/collection';
2
+ type FilterRendererRangeProps = {
3
+ filter: CollectionFilterAppliedRange | CollectionFilterAvailableRange;
4
+ };
5
+ export declare function FilterRendererRange({ filter }: FilterRendererRangeProps): import("react/jsx-runtime").JSX.Element;
6
+ export default FilterRendererRange;
@@ -0,0 +1 @@
1
+ import{jsxs as l,jsx as e}from"react/jsx-runtime";function t({filter:r}){return l("div",{className:"border border-slate-200 rounded-lg p-4",children:[e("h4",{className:"font-medium mb-3",children:r.label}),e("div",{className:"text-sm text-slate-500",children:"Range filter coming soon"})]})}export{t as FilterRendererRange,t as default};
@@ -0,0 +1,9 @@
1
+ import { CollectionFilterAppliedInterface, CollectionFilterAppliedOption, CollectionFilterAvailableOption } from '../../../../../types/collection';
2
+ type SwatchColorProps = {
3
+ filter: CollectionFilterAppliedOption | CollectionFilterAvailableOption;
4
+ onFilterChange: (filters: CollectionFilterAppliedInterface[]) => void;
5
+ appliedFilters: CollectionFilterAppliedInterface[];
6
+ variant?: 'drawer' | 'sidebar';
7
+ };
8
+ export declare function SwatchColor({ filter, onFilterChange, appliedFilters, variant, }: SwatchColorProps): import("react/jsx-runtime").JSX.Element;
9
+ export {};
@@ -0,0 +1 @@
1
+ import{jsx as c,jsxs as d}from"react/jsx-runtime";import{Checkbox as p}from"../../../../Checkbox/Checkbox.js";import{CheckIcon as C}from"../../../../Icon/CheckIcon.js";import{cn as x}from"../../../../../utils/cn.js";import{isDevelopment as $}from"../../../../../utils/env.js";import{isVeryLight as v,isValidHex as k}from"../../../../../utils/color.js";import{isValidImageUrl as S}from"../../../../../utils/swatch.js";import{useOptionFilterToggle as I}from"../useOptionFilterToggle.js";import"react-aria-components";function B({filter:r,onFilterChange:b,appliedFilters:w,variant:f="drawer"}){const{handleOptionToggle:m,getOptionIsChecked:y}=I({filter:r,onFilterChange:b,appliedFilters:w}),h=r.options.map(e=>{const t=y(e.code),i="itemCount"in e?e.itemCount:null,n=e.swatch?.imageUrl??null,s=e.swatch?.hex??null,o=S(n),g=k(s);if(!o&&!g)return $&&(n&&!o&&console.warn(`[Swatch] Invalid imageUrl "${n}" on option "${e.code}" for attribute "${r.code}"`),s&&!g&&console.warn(`[Swatch] Invalid hex "${s}" on option "${e.code}" for attribute "${r.code}"`),!n&&!s&&console.warn(`[Swatch] Missing hex and imageUrl on option "${e.code}" for attribute "${r.code}"`)),c("div",{className:"flex items-center",children:c(p,{isSelected:t,className:"cursor-pointer",onChange:a=>m(e.code,a),children:c("span",{className:"text-sm",children:e.label})},`swatch-${r.code}-${e.code}-${String(t)}`)},`${r.code}-${e.code}`);$&&n&&!o&&console.warn(`[Swatch] Falling back to SWATCH (hex) renderer for attribute "${r.code}" option "${e.code}" — invalid imageUrl "${n}"`);const u=o?!1:v(s),N=o?{backgroundImage:`url(${n})`}:{backgroundColor:s??void 0};return c("div",{className:"flex items-center",children:c(p,{isSelected:t,className:"cursor-pointer",onChange:a=>m(e.code,a),slots:{control:({isSelected:a,isHovered:l})=>c("span",{className:x("relative inline-flex items-center justify-center size-[22px] rounded-full shrink-0 transition-all duration-150",o&&"bg-cover bg-center",o&&a&&"ring-2 ring-inset ring-white shadow-[0_0_0_2px_#222]",o&&l&&!a&&"ring-1 ring-slate-400",!o&&u&&"border border-slate-300"),style:N,children:!o&&(a||l)&&c(C,{className:x("w-3 h-3 transition-opacity duration-150",u?"text-slate-400":"text-white drop-shadow-sm",l&&!a&&"opacity-70")})})},children:({isHovered:a})=>d("span",{className:"text-sm transition-colors duration-150",style:{color:a?"#777777":"#303133"},children:[e.label,i!=null&&d("span",{className:"ml-1 transition-opacity duration-150",style:{color:"#777777",opacity:a?.7:1},children:["(",i,")"]})]})},`swatch-${r.code}-${e.code}-${String(t)}`)},`${r.code}-${e.code}`)});return f==="sidebar"?c("div",{className:"grid grid-cols-2 gap-x-2 gap-y-2.5 px-0.5 pb-2",children:h}):d("div",{className:"border border-slate-200 rounded-lg p-4",children:[c("h4",{className:"font-medium mb-3",children:r.label}),c("div",{className:"grid grid-cols-2 gap-x-2 gap-y-2.5 max-h-60 overflow-y-auto",children:h})]})}export{B as SwatchColor};
@@ -0,0 +1,9 @@
1
+ import { CollectionFilterAppliedInterface, CollectionFilterAppliedOption, CollectionFilterAvailableOption } from '../../../../../types/collection';
2
+ type SwatchImageProps = {
3
+ filter: CollectionFilterAppliedOption | CollectionFilterAvailableOption;
4
+ onFilterChange: (filters: CollectionFilterAppliedInterface[]) => void;
5
+ appliedFilters: CollectionFilterAppliedInterface[];
6
+ variant?: 'drawer' | 'sidebar';
7
+ };
8
+ export declare function SwatchImage({ filter, onFilterChange, appliedFilters, variant, }: SwatchImageProps): import("react/jsx-runtime").JSX.Element;
9
+ export {};
@@ -0,0 +1 @@
1
+ import{jsx as r,jsxs as g}from"react/jsx-runtime";import{Button as b}from"react-aria-components";import{cn as u}from"../../../../../utils/cn.js";import{useOptionFilterToggle as p}from"../useOptionFilterToggle.js";function v({filter:a,onFilterChange:o,appliedFilters:c,variant:d="drawer"}){const{handleOptionToggle:n,getOptionIsChecked:i}=p({filter:a,onFilterChange:o,appliedFilters:c}),t=a.options.map(e=>{const l=i(e.code),s=e.swatch?.imageUrl??null;return r(b,{"aria-label":e.label,"aria-pressed":l,className:"cursor-pointer p-0",onPress:()=>n(e.code,!l),children:({isHovered:m})=>r("span",{className:u("block w-full aspect-[2.2/1] border bg-contain bg-center bg-no-repeat transition-all duration-150",l?"border-slate-900 ring-1 ring-slate-900":"border-slate-200",m&&!l&&"border-slate-400",!s&&"flex items-center justify-center text-xs text-slate-500"),title:e.label,style:s?{backgroundImage:`url(${s})`}:void 0,children:!s&&e.label})},`${a.code}-${e.code}`)});return d==="sidebar"?r("div",{className:"grid grid-cols-3 gap-2 pb-2",children:t}):g("div",{className:"border border-slate-200 rounded-lg p-4",children:[r("h4",{className:"font-medium mb-3",children:a.label}),r("div",{className:"grid grid-cols-3 gap-2 max-h-60 overflow-y-auto",children:t})]})}export{v as SwatchImage};
@@ -0,0 +1,9 @@
1
+ import { CollectionFilterAppliedInterface, CollectionFilterAppliedOption, CollectionFilterAvailableOption } from '../../../../../types/collection';
2
+ type SwatchTextProps = {
3
+ filter: CollectionFilterAppliedOption | CollectionFilterAvailableOption;
4
+ onFilterChange: (filters: CollectionFilterAppliedInterface[]) => void;
5
+ appliedFilters: CollectionFilterAppliedInterface[];
6
+ variant?: 'drawer' | 'sidebar';
7
+ };
8
+ export declare function SwatchText({ filter, onFilterChange, appliedFilters, variant, }: SwatchTextProps): import("react/jsx-runtime").JSX.Element;
9
+ export {};
@@ -0,0 +1 @@
1
+ import{jsx as r,jsxs as x}from"react/jsx-runtime";import{Checkbox as p}from"../../../../Checkbox/Checkbox.js";import{cn as b}from"../../../../../utils/cn.js";import{useOptionFilterToggle as u}from"../useOptionFilterToggle.js";import"react-aria-components";function $({filter:t,onFilterChange:c,appliedFilters:l,variant:n="drawer"}){const{handleOptionToggle:d,getOptionIsChecked:i}=u({filter:t,onFilterChange:c,appliedFilters:l}),s=t.options.map(e=>{const a=i(e.code),m=e.swatch?.text??e.label;return r("div",{children:r(p,{isSelected:a,className:"cursor-pointer",onChange:o=>d(e.code,o),slots:{control:({isSelected:o,isHovered:h})=>r("span",{className:b("inline-flex items-center justify-center px-3.5 py-2 text-xs font-medium border shrink-0 transition-colors duration-150",o?"bg-slate-900 text-white border-slate-900":"bg-white text-slate-700 border-slate-300",h&&!o&&"border-slate-500"),children:m})}},`swatch-${t.code}-${e.code}-${String(a)}`)},`${t.code}-${e.code}`)});return n==="sidebar"?r("div",{className:"flex flex-wrap gap-2 pb-2",children:s}):x("div",{className:"border border-slate-200 rounded-lg p-4",children:[r("h4",{className:"font-medium mb-3",children:t.label}),r("div",{className:"flex flex-wrap gap-2 max-h-60 overflow-y-auto",children:s})]})}export{$ as SwatchText};
@@ -0,0 +1,3 @@
1
+ export { SwatchColor } from './SwatchColor';
2
+ export { SwatchImage } from './SwatchImage';
3
+ export { SwatchText } from './SwatchText';
@@ -0,0 +1 @@
1
+ import{SwatchColor as h}from"./SwatchColor.js";import{SwatchImage as S}from"./SwatchImage.js";import{SwatchText as l}from"./SwatchText.js";import"react/jsx-runtime";import"../../../../Checkbox/Checkbox.js";import"react-aria-components";import"../../../../../utils/cn.js";import"../../../../Icon/CheckIcon.js";import"../../../../../utils/env.js";import"../../../../../utils/color.js";import"../../../../../utils/swatch.js";import"../useOptionFilterToggle.js";export{h as SwatchColor,S as SwatchImage,l as SwatchText};
@@ -0,0 +1,9 @@
1
+ import { CollectionFilterAppliedInterface, CollectionFilterAppliedOption, CollectionFilterAvailableOption } from '../../../../types/collection';
2
+ type FilterRendererSwatchProps = {
3
+ filter: CollectionFilterAppliedOption | CollectionFilterAvailableOption;
4
+ onFilterChange: (filters: CollectionFilterAppliedInterface[]) => void;
5
+ appliedFilters: CollectionFilterAppliedInterface[];
6
+ variant?: 'drawer' | 'sidebar';
7
+ };
8
+ export declare function FilterRendererSwatch(props: FilterRendererSwatchProps): import("react/jsx-runtime").JSX.Element;
9
+ export default FilterRendererSwatch;
@@ -0,0 +1 @@
1
+ import{jsx as t}from"react/jsx-runtime";import{SwatchRenderer as e}from"../../../../utils/swatch.js";import{SwatchColor as m}from"./Swatch/SwatchColor.js";import{SwatchImage as o}from"./Swatch/SwatchImage.js";import{SwatchText as i}from"./Swatch/SwatchText.js";import"../../../Checkbox/Checkbox.js";import"react-aria-components";import"../../../../utils/cn.js";import"../../../Icon/CheckIcon.js";import"../../../../utils/env.js";import"../../../../utils/color.js";import"./useOptionFilterToggle.js";function T(r){switch(r.filter.renderer){case e.IMAGE:return t(o,{...r});case e.TEXT:return t(i,{...r});default:return t(m,{...r})}}export{T as FilterRendererSwatch,T as default};
@@ -0,0 +1,4 @@
1
+ export { FilterRendererOption } from './Option';
2
+ export { FilterRendererSwatch } from './Swatch';
3
+ export { FilterRendererBoolean } from './Boolean';
4
+ export { FilterRendererRange } from './Range';
@@ -0,0 +1 @@
1
+ import{FilterRendererOption as g}from"./Option.js";import{FilterRendererSwatch as w}from"./Swatch.js";import{FilterRendererBoolean as O}from"./Boolean.js";import{FilterRendererRange as b}from"./Range.js";import"react/jsx-runtime";import"../../../Checkbox/Checkbox.js";import"react-aria-components";import"../../../../utils/cn.js";import"./useOptionFilterToggle.js";import"../../../../utils/swatch.js";import"./Swatch/SwatchColor.js";import"../../../Icon/CheckIcon.js";import"../../../../utils/env.js";import"../../../../utils/color.js";import"./Swatch/SwatchImage.js";import"./Swatch/SwatchText.js";import"../../../RadioGroup/RadioGroup.js";import"../../../Radio/Radio.js";export{O as FilterRendererBoolean,g as FilterRendererOption,b as FilterRendererRange,w as FilterRendererSwatch};
@@ -0,0 +1,11 @@
1
+ import { CollectionFilterAppliedInterface, CollectionFilterAppliedOption, CollectionFilterAvailableOption } from '../../../../types/collection';
2
+ type UseOptionFilterToggleProps = {
3
+ filter: CollectionFilterAppliedOption | CollectionFilterAvailableOption;
4
+ onFilterChange: (filters: CollectionFilterAppliedInterface[]) => void;
5
+ appliedFilters: CollectionFilterAppliedInterface[];
6
+ };
7
+ export declare function useOptionFilterToggle({ filter, onFilterChange, appliedFilters, }: UseOptionFilterToggleProps): {
8
+ handleOptionToggle: (optionCode: string, checked: boolean) => void;
9
+ getOptionIsChecked: (optionCode: string) => boolean;
10
+ };
11
+ export {};
@@ -0,0 +1 @@
1
+ function r({filter:e,onFilterChange:i,appliedFilters:t}){return{handleOptionToggle:(d,l)=>{const o=t.find(c=>c.code===e.code);if(l){const c=e.options.find(n=>n.code===d);if(o){const n={...o,options:[...o.options,{id:c?.id||"",code:d,label:c?.label||"",isApplied:!0}]};i([...t.filter(s=>s.code!==e.code),n])}else{const n={__typename:"CollectionFilterAppliedOption",code:e.code,label:e.label,renderer:e.renderer,type:e.type,includeInFilters:e.includeInFilters,isSearchable:e.isSearchable,options:[{id:c?.id||"",code:d,label:c?.label||"",isApplied:!0}]};i([...t,n])}}else if(o){const c=o.options.filter(n=>n.code!==d);if(c.length===0)i(t.filter(n=>n.code!==e.code));else{const n={...o,options:c};i([...t.filter(s=>s.code!==e.code),n])}}},getOptionIsChecked:d=>t.find(o=>o.code===e.code)?.options.some(o=>o.code===d&&o.isApplied)||!1}}export{r as useOptionFilterToggle};
@@ -0,0 +1 @@
1
+ export { FilterRenderer } from './FilterRenderer';
@@ -0,0 +1 @@
1
+ import{FilterRenderer as k}from"./FilterRenderer.js";import"react/jsx-runtime";import"../../../utils/env.js";import"../../../utils/swatch.js";import"./Renderer/Option.js";import"../../Checkbox/Checkbox.js";import"react-aria-components";import"../../../utils/cn.js";import"./Renderer/useOptionFilterToggle.js";import"./Renderer/Swatch.js";import"./Renderer/Swatch/SwatchColor.js";import"../../Icon/CheckIcon.js";import"../../../utils/color.js";import"./Renderer/Swatch/SwatchImage.js";import"./Renderer/Swatch/SwatchText.js";import"./Renderer/Boolean.js";import"../../RadioGroup/RadioGroup.js";import"../../Radio/Radio.js";import"./Renderer/Range.js";export{k as FilterRenderer};
@@ -0,0 +1,4 @@
1
+ import { CollectionFilterAppliedOption, CollectionFilterAppliedBoolean, CollectionFilterAppliedRange, CollectionFilterAvailableOption, CollectionFilterAvailableBoolean, CollectionFilterAvailableRange, FilterChip } from '../../../types/collection';
2
+ export type { FilterChip };
3
+ export type CollectionFilterApplied = CollectionFilterAppliedOption | CollectionFilterAppliedBoolean | CollectionFilterAppliedRange;
4
+ export type CollectionFilterAvailable = CollectionFilterAvailableOption | CollectionFilterAvailableBoolean | CollectionFilterAvailableRange;
@@ -0,0 +1,35 @@
1
+ import { CollectionFilterAppliedInterface, CollectionFilterAvailableInterface } from '../../../types/collection';
2
+ type AvailableFilter = CollectionFilterAvailableInterface | CollectionFilterAppliedInterface;
3
+ type Variant = {
4
+ price?: number | null;
5
+ attributes: {
6
+ attribute: {
7
+ code: string;
8
+ };
9
+ option: {
10
+ id: string;
11
+ code: string;
12
+ label: string;
13
+ };
14
+ }[];
15
+ simpleProduct: {
16
+ image?: string | null;
17
+ sku: string;
18
+ };
19
+ };
20
+ type AttributeSwatchConfig = {
21
+ code: string;
22
+ swatch?: {
23
+ listRenderer?: string | null;
24
+ } | null;
25
+ };
26
+ type ProductCardSwatchesProps = {
27
+ variants: Variant[];
28
+ availableFilters: AvailableFilter[];
29
+ attributeSwatchConfigs?: AttributeSwatchConfig[];
30
+ matchedOptionIds: string[];
31
+ selectedOptionId: string | null;
32
+ onSelect: (optionId: string, optionCode: string, image: string | null, attributeCode: string) => void;
33
+ };
34
+ export declare function ProductCardSwatches({ variants, availableFilters, attributeSwatchConfigs, matchedOptionIds, selectedOptionId, onSelect, }: ProductCardSwatchesProps): import("react/jsx-runtime").JSX.Element | null;
35
+ export {};
@@ -0,0 +1 @@
1
+ import{jsx as s}from"react/jsx-runtime";import{Button as w}from"react-aria-components";import{cn as f}from"../../../utils/cn.js";import{CheckIcon as v}from"../../Icon/CheckIcon.js";import{isDevelopment as g}from"../../../utils/env.js";import{isValidHex as y,isVeryLight as N}from"../../../utils/color.js";import{isSwatchRenderer as I,SwatchRenderer as C,isValidImageUrl as R}from"../../../utils/swatch.js";function P(h){const l=new Map;for(const n of h)if("renderer"in n&&I(n.renderer)&&"options"in n)for(const a of n.options)a.swatch&&l.set(a.id,{...a.swatch,attributeCode:n.code,optionCode:a.code});return l}function j(h,l,n){const a=new Set,m=[];for(const c of h)for(const i of c.attributes){const u=l.get(i.option.id);u&&!a.has(i.option.id)&&n.has(i.attribute.code)&&(a.add(i.option.id),m.push({optionId:i.option.id,optionCode:u.optionCode,attributeCode:i.attribute.code,label:i.option.label,swatch:u,image:c.simpleProduct.image??null}))}return m}function B({variants:h,availableFilters:l,attributeSwatchConfigs:n=[],matchedOptionIds:a,selectedOptionId:m,onSelect:c}){const i=new Map;for(const e of n)e.swatch?.listRenderer&&(g&&!I(e.swatch.listRenderer)&&console.warn(`[Swatch] Unknown listRenderer "${e.swatch.listRenderer}" for attribute "${e.code}" — skipping list swatches`),i.set(e.code,e.swatch.listRenderer));const u=P(l),d=j(h,u,i);if(d.length===0)return null;const S=new Set(d.map(e=>e.optionId)),U=a.find(e=>S.has(e)),k=m??U??d[0]?.optionId??null,b=d[0]?i.get(d[0].attributeCode)??null:null;return s("div",{className:"flex flex-row flex-wrap items-center gap-1.5 mt-1.5",children:d.map(e=>{const o=k===e.optionId;if(b===C.IMAGE)return s(w,{"aria-label":e.label,className:"cursor-pointer p-0",onPress:()=>{c(e.optionId,e.optionCode,e.image,e.attributeCode)},children:({isHovered:t})=>s("span",{className:f("block w-8 aspect-[2.2/1] border bg-contain bg-center bg-no-repeat transition-all duration-150",o?"border-slate-900 ring-1 ring-slate-900":"border-slate-200",t&&!o&&"border-slate-400"),title:e.label,style:e.swatch.imageUrl?{backgroundImage:`url(${e.swatch.imageUrl})`}:void 0})},e.optionId);if(b===C.TEXT)return s(w,{"aria-label":e.label,className:"cursor-pointer p-0",onPress:()=>{c(e.optionId,e.optionCode,e.image,e.attributeCode)},children:({isHovered:t})=>s("span",{className:f("inline-flex items-center px-1.5 py-0.5 text-[10px] font-medium border transition-colors duration-150",o?"bg-slate-900 text-white border-slate-900":"bg-white text-slate-700 border-slate-300",t&&!o&&"border-slate-500"),children:e.swatch.text??e.label})},e.optionId);const r=R(e.swatch.imageUrl),p=y(e.swatch.hex);if(!r&&!p){if(g){const t=[];e.swatch.hex&&!p&&t.push(`invalid hex "${e.swatch.hex}"`),e.swatch.imageUrl&&!r&&t.push(`invalid imageUrl "${e.swatch.imageUrl}"`),!e.swatch.hex&&!e.swatch.imageUrl&&t.push("missing hex and imageUrl"),console.warn(`[Swatch] Skipping list swatch for attribute "${e.attributeCode}" option "${e.optionCode}" — ${t.join("; ")}`)}return null}g&&e.swatch.imageUrl&&!r&&console.warn(`[Swatch] Falling back to SWATCH (hex) renderer for attribute "${e.attributeCode}" option "${e.optionCode}" — invalid imageUrl "${e.swatch.imageUrl}"`);const x=r?!1:N(e.swatch.hex??null),$=r?{backgroundImage:`url(${e.swatch.imageUrl})`}:{backgroundColor:e.swatch.hex??void 0};return s(w,{"aria-label":e.label,className:"cursor-pointer size-4 flex items-center justify-center p-0",onPress:()=>{c(e.optionId,e.optionCode,e.image,e.attributeCode)},children:({isHovered:t})=>s("span",{className:f("relative inline-flex items-center justify-center size-4 rounded-full transition-all duration-150",r&&"bg-cover bg-center",r&&o&&"ring-2 ring-inset ring-white shadow-[0_0_0_2px_#222]",r&&t&&!o&&"ring-1 ring-slate-400",!r&&x&&"border border-slate-300"),style:$,children:!r&&(o||t)&&s(v,{className:f("w-2.5 h-2.5 transition-opacity duration-150",x?"text-slate-400":"text-white drop-shadow-sm",t&&!o&&"opacity-70")})})},e.optionId)})})}export{B as ProductCardSwatches};
@@ -0,0 +1 @@
1
+ export { ProductCardSwatches } from './ProductCardSwatches';
@@ -0,0 +1 @@
1
+ import{ProductCardSwatches as e}from"./ProductCardSwatches.js";import"react/jsx-runtime";import"react-aria-components";import"../../../utils/cn.js";import"../../Icon/CheckIcon.js";import"../../../utils/env.js";import"../../../utils/color.js";import"../../../utils/swatch.js";export{e as ProductCardSwatches};
@@ -0,0 +1,12 @@
1
+ type ProductPriceProps = {
2
+ product: {
3
+ type: {
4
+ code: string;
5
+ };
6
+ price?: number;
7
+ minPrice?: number | null;
8
+ maxPrice?: number | null;
9
+ };
10
+ };
11
+ export declare function ProductPrice({ product }: ProductPriceProps): import("react/jsx-runtime").JSX.Element | null;
12
+ export {};
@@ -0,0 +1 @@
1
+ import{jsx as c}from"react/jsx-runtime";const r=e=>Intl.NumberFormat("en-US",{style:"currency",currency:"USD",minimumFractionDigits:0,maximumFractionDigits:0}).format(e);function a({product:e}){const t=e.type.code;if(t==="simple"&&e.price!=null)return c("span",{className:"text-black",children:r(e.price)});if(t==="configurable"){const n=e.minPrice??0,i=e.maxPrice??0;if(n===0&&i===0)return null;const m=n!==i&&i>0?`${r(n)} - ${r(i)}`:r(n);return c("span",{className:"text-black",children:m})}return null}export{a as ProductPrice};
@@ -0,0 +1 @@
1
+ export { ProductPrice } from './ProductPrice';
@@ -0,0 +1 @@
1
+ import{ProductPrice as c}from"./ProductPrice.js";import"react/jsx-runtime";export{c as ProductPrice};
@@ -0,0 +1,9 @@
1
+ export { AttributeDataProvider, useAttributeData, } from './AttributeDataProvider';
2
+ export { EmptyResults } from './EmptyResults';
3
+ export { ProductPrice } from './ProductPrice';
4
+ export { FilterRenderer } from './FilterRenderer';
5
+ export { CollectionFilterChips } from './CollectionFilterChips';
6
+ export { FilterAccordionTitle } from './FilterAccordionTitle';
7
+ export { ProductCardSwatches } from './ProductCardSwatches';
8
+ export { CollectionFilters } from './CollectionFilters';
9
+ export { CollectionToolbar } from './CollectionToolbar';
@@ -0,0 +1 @@
1
+ import{AttributeDataProvider as L,useAttributeData as M}from"./AttributeDataProvider/AttributeDataProvider.js";import{EmptyResults as O}from"./EmptyResults/EmptyResults.js";import{ProductPrice as U}from"./ProductPrice/ProductPrice.js";import{FilterRenderer as W}from"./FilterRenderer/FilterRenderer.js";import{CollectionFilterChips as Y}from"./CollectionFilterChips/CollectionFilterChips.js";import{FilterAccordionTitle as _}from"./FilterAccordionTitle/FilterAccordionTitle.js";import{ProductCardSwatches as rr}from"./ProductCardSwatches/ProductCardSwatches.js";import{CollectionFilters as tr}from"./CollectionFilters/CollectionFilters.js";import{CollectionToolbar as pr}from"./CollectionToolbar/CollectionToolbar.js";import"react/jsx-runtime";import"react";import"../../utils/env.js";import"../../utils/swatch.js";import"./FilterRenderer/Renderer/Option.js";import"../Checkbox/Checkbox.js";import"react-aria-components";import"../../utils/cn.js";import"./FilterRenderer/Renderer/useOptionFilterToggle.js";import"./FilterRenderer/Renderer/Swatch.js";import"./FilterRenderer/Renderer/Swatch/SwatchColor.js";import"../Icon/CheckIcon.js";import"../../utils/color.js";import"./FilterRenderer/Renderer/Swatch/SwatchImage.js";import"./FilterRenderer/Renderer/Swatch/SwatchText.js";import"./FilterRenderer/Renderer/Boolean.js";import"../RadioGroup/RadioGroup.js";import"../Radio/Radio.js";import"./FilterRenderer/Renderer/Range.js";import"../Button/Button.js";import"../Icon/CrossSmall.js";import"../../utils/collection/getAppliedFilterChips.js";import"../../utils/collection/removeFilterChip.js";import"../Accordion/Accordion.js";import"@nextui-org/accordion";import"../Icon/DownArrow.js";import"../ModalOverlay/ModalOverlay.js";import"framer-motion";import"../DialogTrigger/DialogTrigger.js";import"../Modal/Modal.js";import"../Drawer/Drawer.js";import"../Icon/Cross.js";import"../Icon/Search.js";import"../../utils/collection/mergeFilters.js";import"../Select/DropdownSelect.js";import"../Select/Option.js";import"../Select/Select.js";import"../Loader/Pulse.js";import"../Icon/FilterIcon.js";export{L as AttributeDataProvider,Y as CollectionFilterChips,tr as CollectionFilters,pr as CollectionToolbar,O as EmptyResults,_ as FilterAccordionTitle,W as FilterRenderer,rr as ProductCardSwatches,U as ProductPrice,M as useAttributeData};
@@ -0,0 +1,3 @@
1
+ import { IconProps } from './Icon.types';
2
+ export declare function CrossSmall({ className, ...rest }: IconProps): import("react/jsx-runtime").JSX.Element;
3
+ export default CrossSmall;
@@ -0,0 +1 @@
1
+ import{jsxs as e,jsx as o}from"react/jsx-runtime";import{cn as n}from"../../utils/cn.js";function d({className:r,...t}){return e("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",xmlns:"http://www.w3.org/2000/svg",className:n(r),...t,children:[o("path",{d:"M12 4L4 12",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round"}),o("path",{d:"M4 4L12 12",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round"})]})}export{d as CrossSmall,d as default};
@@ -0,0 +1,3 @@
1
+ import { IconProps } from './Icon.types';
2
+ export declare function FilterIcon({ className, ...rest }: IconProps): import("react/jsx-runtime").JSX.Element;
3
+ export default FilterIcon;
@@ -0,0 +1 @@
1
+ import{jsx as o}from"react/jsx-runtime";import{cn as e}from"../../utils/cn.js";function s({className:r,...t}){return o("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",xmlns:"http://www.w3.org/2000/svg",className:e(r),...t,children:o("path",{d:"M14.6668 2H1.3335L6.66683 8.30667V12.6667L9.3335 14V8.30667L14.6668 2Z",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round"})})}export{s as FilterIcon,s as default};
@@ -14,6 +14,7 @@ export { ArrowLeftIcon } from './ArrowLeftIcon';
14
14
  export { CloseIcon } from './CloseIcon';
15
15
  export { CreateIcon } from './CreateIcon';
16
16
  export { DeleteIcon } from './DeleteIcon';
17
+ export { FilterIcon } from './FilterIcon';
17
18
  export { GemIcon, type GemIconProps } from './GemIcon';
18
19
  export { IncludedIcon } from './IncludedIcon';
19
20
  export { InfoIcon } from './InfoIcon';
@@ -1 +1 @@
1
- import{Account as m}from"./Account.js";import{Cart as p}from"./Cart.js";import{Checkmark as c}from"./Checkmark.js";import{Cross as I}from"./Cross.js";import{DownArrow as i}from"./DownArrow.js";import{Hamburger as a}from"./Hamburger.js";import{LinkArrow as l}from"./LinkArrow.js";import{Minus as w}from"./Minus.js";import{Plus as h}from"./Plus.js";import{Search as A}from"./Search.js";import{Trash as L}from"./Trash.js";import{Verified as y}from"./Verified.js";import{ArrowLeftIcon as E}from"./ArrowLeftIcon.js";import{CloseIcon as R}from"./CloseIcon.js";import{CreateIcon as g}from"./CreateIcon.js";import{DeleteIcon as B}from"./DeleteIcon.js";import{GemIcon as H}from"./GemIcon.js";import{IncludedIcon as Q}from"./IncludedIcon.js";import{InfoIcon as V}from"./InfoIcon.js";import{InvoiceIcon as q}from"./InvoiceIcon.js";import{LockIcon as F}from"./LockIcon.js";import{NoEntryIcon as K}from"./NoEntryIcon.js";import{NoteIcon as W}from"./NoteIcon.js";import{PencilIcon as Y}from"./PencilIcon.js";import{PermissionIcon as _}from"./PermissionIcon.js";import{ProductBoxIcon as oo}from"./ProductBoxIcon.js";import{ProductIcon as eo}from"./ProductIcon.js";import{QuoteIcon as mo}from"./QuoteIcon.js";import{ResetPasswordIcon as po}from"./ResetPasswordIcon.js";import{RoleIcon as co}from"./RoleIcon.js";import{ShieldUserIcon as Io}from"./ShieldUserIcon.js";import{SyncIcon as io}from"./SyncIcon.js";import{UpdateIcon as uo}from"./UpdateIcon.js";import{UserEditIcon as Po}from"./UserEditIcon.js";import{UserIcon as Co}from"./UserIcon.js";import"react/jsx-runtime";import"../../utils/cn.js";export{m as Account,E as ArrowLeftIcon,p as Cart,c as Checkmark,R as CloseIcon,g as CreateIcon,I as Cross,B as DeleteIcon,i as DownArrow,H as GemIcon,a as Hamburger,Q as IncludedIcon,V as InfoIcon,q as InvoiceIcon,l as LinkArrow,F as LockIcon,w as Minus,K as NoEntryIcon,W as NoteIcon,Y as PencilIcon,_ as PermissionIcon,h as Plus,oo as ProductBoxIcon,eo as ProductIcon,mo as QuoteIcon,po as ResetPasswordIcon,co as RoleIcon,A as Search,Io as ShieldUserIcon,io as SyncIcon,L as Trash,uo as UpdateIcon,Po as UserEditIcon,Co as UserIcon,y as Verified};
1
+ import{Account as m}from"./Account.js";import{Cart as p}from"./Cart.js";import{Checkmark as c}from"./Checkmark.js";import{Cross as I}from"./Cross.js";import{DownArrow as i}from"./DownArrow.js";import{Hamburger as a}from"./Hamburger.js";import{LinkArrow as u}from"./LinkArrow.js";import{Minus as w}from"./Minus.js";import{Plus as h}from"./Plus.js";import{Search as A}from"./Search.js";import{Trash as L}from"./Trash.js";import{Verified as y}from"./Verified.js";import{ArrowLeftIcon as E}from"./ArrowLeftIcon.js";import{CloseIcon as R}from"./CloseIcon.js";import{CreateIcon as g}from"./CreateIcon.js";import{DeleteIcon as B}from"./DeleteIcon.js";import{FilterIcon as G}from"./FilterIcon.js";import{GemIcon as M}from"./GemIcon.js";import{IncludedIcon as T}from"./IncludedIcon.js";import{InfoIcon as j}from"./InfoIcon.js";import{InvoiceIcon as z}from"./InvoiceIcon.js";import{LockIcon as K}from"./LockIcon.js";import{NoEntryIcon as W}from"./NoEntryIcon.js";import{NoteIcon as Y}from"./NoteIcon.js";import{PencilIcon as _}from"./PencilIcon.js";import{PermissionIcon as oo}from"./PermissionIcon.js";import{ProductBoxIcon as eo}from"./ProductBoxIcon.js";import{ProductIcon as mo}from"./ProductIcon.js";import{QuoteIcon as po}from"./QuoteIcon.js";import{ResetPasswordIcon as co}from"./ResetPasswordIcon.js";import{RoleIcon as Io}from"./RoleIcon.js";import{ShieldUserIcon as io}from"./ShieldUserIcon.js";import{SyncIcon as lo}from"./SyncIcon.js";import{UpdateIcon as Po}from"./UpdateIcon.js";import{UserEditIcon as Co}from"./UserEditIcon.js";import{UserIcon as ko}from"./UserIcon.js";import"react/jsx-runtime";import"../../utils/cn.js";export{m as Account,E as ArrowLeftIcon,p as Cart,c as Checkmark,R as CloseIcon,g as CreateIcon,I as Cross,B as DeleteIcon,i as DownArrow,G as FilterIcon,M as GemIcon,a as Hamburger,T as IncludedIcon,j as InfoIcon,z as InvoiceIcon,u as LinkArrow,K as LockIcon,w as Minus,W as NoEntryIcon,Y as NoteIcon,_ as PencilIcon,oo as PermissionIcon,h as Plus,eo as ProductBoxIcon,mo as ProductIcon,po as QuoteIcon,co as ResetPasswordIcon,Io as RoleIcon,A as Search,io as ShieldUserIcon,lo as SyncIcon,L as Trash,Po as UpdateIcon,Co as UserEditIcon,ko as UserIcon,y as Verified};
@@ -0,0 +1 @@
1
+ export { useAttributeData } from '../components/Collection/AttributeDataProvider';
@@ -0,0 +1 @@
1
+ import{useAttributeData as i}from"../components/Collection/AttributeDataProvider/AttributeDataProvider.js";import"react/jsx-runtime";import"react";export{i as useAttributeData};
@@ -0,0 +1,2 @@
1
+ export { useCollectionState } from './useCollectionState';
2
+ export type { UseCollectionStateProps } from './useCollectionState';
@@ -0,0 +1 @@
1
+ import{useCollectionState as i}from"./useCollectionState.js";import"react";import"../../utils/collection/filterSearchParams.js";export{i as useCollectionState};
@@ -0,0 +1,142 @@
1
+ import { CollectionFilterAppliedInterface } from '../../types/collection';
2
+ type SwatchSelection = {
3
+ optionId: string;
4
+ optionCode: string;
5
+ image: string | null;
6
+ attributeCode: string;
7
+ };
8
+ type CollectionData = {
9
+ collection?: {
10
+ filters: {
11
+ available: CollectionFilterAppliedInterface[];
12
+ applied: CollectionFilterAppliedInterface[];
13
+ };
14
+ sortOrders: {
15
+ available: Array<{
16
+ code: string;
17
+ direction: string;
18
+ label: string;
19
+ }>;
20
+ applied?: Array<{
21
+ code: string;
22
+ direction: string;
23
+ label: string;
24
+ }>;
25
+ };
26
+ pagination?: {
27
+ totalItemCount?: number;
28
+ currentPage?: number;
29
+ pageSize?: number;
30
+ currentPageItemCount?: number;
31
+ };
32
+ items?: Array<{
33
+ id: string;
34
+ name: string;
35
+ image?: string | null;
36
+ canonicalUrl: string;
37
+ type: {
38
+ code: string;
39
+ };
40
+ [key: string]: unknown;
41
+ }>;
42
+ } | null;
43
+ } | null;
44
+ type FetchParams = {
45
+ appliedFilters: CollectionFilterAppliedInterface[];
46
+ sort?: {
47
+ field: string;
48
+ direction: string;
49
+ }[];
50
+ search?: string;
51
+ };
52
+ export type UseCollectionStateProps = {
53
+ initialData: CollectionData;
54
+ searchQuery?: string;
55
+ onFetch: (params: FetchParams) => Promise<CollectionData | null>;
56
+ onUrlUpdate: (url: string) => void;
57
+ };
58
+ export declare function useCollectionState({ initialData, searchQuery, onFetch, onUrlUpdate, }: UseCollectionStateProps): {
59
+ data: {
60
+ collection?: {
61
+ filters: {
62
+ available: CollectionFilterAppliedInterface[];
63
+ applied: CollectionFilterAppliedInterface[];
64
+ };
65
+ sortOrders: {
66
+ available: Array<{
67
+ code: string;
68
+ direction: string;
69
+ label: string;
70
+ }>;
71
+ applied?: Array<{
72
+ code: string;
73
+ direction: string;
74
+ label: string;
75
+ }>;
76
+ };
77
+ pagination?: {
78
+ totalItemCount?: number;
79
+ currentPage?: number;
80
+ pageSize?: number;
81
+ currentPageItemCount?: number;
82
+ };
83
+ items?: Array<{
84
+ id: string;
85
+ name: string;
86
+ image?: string | null;
87
+ canonicalUrl: string;
88
+ type: {
89
+ code: string;
90
+ };
91
+ [key: string]: unknown;
92
+ }>;
93
+ } | null;
94
+ } | null;
95
+ isLoading: boolean;
96
+ showMobileFilters: boolean;
97
+ setShowMobileFilters: import('react').Dispatch<import('react').SetStateAction<boolean>>;
98
+ hidingIds: Set<string>;
99
+ selectedSwatches: Record<string, SwatchSelection>;
100
+ availableFilters: CollectionFilterAppliedInterface[];
101
+ appliedFilters: CollectionFilterAppliedInterface[];
102
+ allFilters: CollectionFilterAppliedInterface[];
103
+ availableSortOrders: {
104
+ code: string;
105
+ direction: string;
106
+ label: string;
107
+ }[];
108
+ appliedSortOrder: {
109
+ code: string;
110
+ direction: string;
111
+ label: string;
112
+ } | undefined;
113
+ totalItems: number;
114
+ startItem: number;
115
+ endItem: number;
116
+ items: {
117
+ [key: string]: unknown;
118
+ id: string;
119
+ name: string;
120
+ image?: string | null;
121
+ canonicalUrl: string;
122
+ type: {
123
+ code: string;
124
+ };
125
+ }[];
126
+ getDisplayImage: (product: {
127
+ id: string;
128
+ image?: string | null;
129
+ matchedVariants?: {
130
+ image?: string | null;
131
+ }[];
132
+ }, variants: {
133
+ simpleProduct: {
134
+ image?: string | null;
135
+ };
136
+ }[]) => string;
137
+ handleSortChange: (key: React.Key | null) => Promise<void>;
138
+ handleFilterChange: (newAppliedFilters: CollectionFilterAppliedInterface[]) => Promise<void>;
139
+ selectSwatch: (productId: string, optionId: string, optionCode: string, image: string | null, attributeCode: string) => void;
140
+ clearHidingId: (id: string) => void;
141
+ };
142
+ export {};
@@ -0,0 +1 @@
1
+ import{useState as m,useMemo as b,useCallback as a}from"react";import{filtersToSearchParams as W,searchParamsToFilters as X}from"../../utils/collection/filterSearchParams.js";function Q({initialData:T,searchQuery:c="",onFetch:f,onUrlUpdate:C}){const[r,v]=m(T),[j,l]=m(!1),[q,z]=m(!1),[E,F]=m(new Set),[h,M]=m({}),I=b(()=>r?.collection?.filters.available??[],[r?.collection?.filters.available]),d=b(()=>r?.collection?.filters.applied??[],[r?.collection?.filters.applied]),k=b(()=>[...I,...d],[I,d]),H=r?.collection?.sortOrders.available??[],R=r?.collection?.sortOrders.applied?.[0],g=r?.collection?.pagination,V=g?.totalItemCount??0,O=g?.currentPage??1,x=g?.pageSize??20,$=g?.currentPageItemCount??0,A=V===0?0:(O-1)*x+1,D=(O-1)*x+$,_=r?.collection?.items??[],p=a((e,s)=>{const n=h[e.id]?.image;if(n)return String(n);const o=e.matchedVariants?.[0]?.image;if(o)return String(o);const t=s[0]?.simpleProduct?.image;return String(t||(e.image??""))},[h]),y=a(async e=>{const s=r?.collection?.items??[],n=e?.collection?.items??[],o=new Set;for(const t of n){const i=s.find(S=>S.id===t.id),u=t.variants??[],w=i?i.variants??[]:[];i&&p(t,u)!==p(i,w)&&o.add(t.id)}o.size>0&&(F(o),await new Promise(t=>setTimeout(t,220))),v(e),l(!1)},[r,p]),L=a(e=>{const s=new URLSearchParams(window.location.search),n=W(e),o=new URLSearchParams;for(const[t,i]of n.entries()){const u=s.get(t);if(u){const w=u.split(","),S=i.split(","),N=S.filter(P=>!w.includes(P)),U=w.filter(P=>S.includes(P));o.set(t,[...U,...N].join(","))}else o.set(t,i)}return o},[]),B=a(async e=>{if(!e)return;l(!0);const s=String(e),[n,o]=s.split("_");try{const t=await f({appliedFilters:d,sort:[{field:n,direction:o}],search:c||void 0});if(t){await y(t);return}}catch(t){console.error("Error fetching sorted products:",t)}finally{l(!1)}},[d,c,f,y]),G=a(async e=>{l(!0);const s=L(e);c&&s.set("q",c);const n=s.toString().split("%2C").join(","),o=n?`${window.location.pathname}?${n}`:window.location.pathname;C(o),s.delete("q");const t=X(Object.fromEntries(s.entries()));try{const i=await f({appliedFilters:t,search:c||void 0});i&&v(i)}catch(i){console.error("Error fetching filtered products:",i)}finally{l(!1)}},[c,L,f,C]),J=a((e,s,n,o,t)=>{M(i=>({...i,[e]:{optionId:s,optionCode:n,image:o,attributeCode:t}}))},[]),K=a(e=>{F(s=>{if(!s.has(e))return s;const n=new Set(s);return n.delete(e),n})},[]);return{data:r,isLoading:j,showMobileFilters:q,setShowMobileFilters:z,hidingIds:E,selectedSwatches:h,availableFilters:I,appliedFilters:d,allFilters:k,availableSortOrders:H,appliedSortOrder:R,totalItems:V,startItem:A,endItem:D,items:_,getDisplayImage:p,handleSortChange:B,handleFilterChange:G,selectSwatch:J,clearHidingId:K}}export{Q as useCollectionState};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clicktap/ui",
3
- "version": "0.24.0",
3
+ "version": "0.25.0",
4
4
  "private": false,
5
5
  "author": "Clicktap",
6
6
  "description": "A library of React UI components and low-level hooks.",
@@ -66,5 +66,8 @@
66
66
  "react-dom": "^17.0.0 || ^18.0.0",
67
67
  "react-international-phone": "^4.3.0"
68
68
  },
69
- "tailwind": "./tailwind.config.js"
69
+ "tailwind": "./tailwind.config.js",
70
+ "scripts": {
71
+ "preinstall": "if [ \"$INIT_CWD\" = \"$PWD\" ]; then echo 'ERROR: Run pnpm install from the monorepo root, not from this directory.' && exit 1; fi"
72
+ }
70
73
  }
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Framework-agnostic type definitions for collection filters, swatches,
3
+ * and attribute data. These mirror the shapes generated by GraphQL codegen
4
+ * so that generated types are structurally assignable without explicit coupling.
5
+ */
6
+ export type SwatchData = {
7
+ hex?: string | null;
8
+ imageUrl?: string | null;
9
+ text?: string | null;
10
+ };
11
+ export type FilterOption = {
12
+ id: string;
13
+ code: string;
14
+ label: string;
15
+ isApplied: boolean;
16
+ itemCount?: number | null;
17
+ swatch?: SwatchData | null;
18
+ };
19
+ export type CollectionFilterAppliedInterface = {
20
+ __typename?: string;
21
+ code: string;
22
+ label: string;
23
+ includeInFilters: boolean;
24
+ renderer?: string | null;
25
+ type: string;
26
+ };
27
+ export type CollectionFilterAvailableInterface = {
28
+ __typename?: string;
29
+ code: string;
30
+ label: string;
31
+ includeInFilters: boolean;
32
+ renderer?: string | null;
33
+ type: string;
34
+ };
35
+ export type CollectionFilterAppliedOption = CollectionFilterAppliedInterface & {
36
+ __typename?: 'CollectionFilterAppliedOption';
37
+ isSearchable: boolean;
38
+ options: FilterOption[];
39
+ };
40
+ export type CollectionFilterAvailableOption = CollectionFilterAvailableInterface & {
41
+ __typename?: 'CollectionFilterAvailableOption';
42
+ isSearchable: boolean;
43
+ options: FilterOption[];
44
+ };
45
+ export type CollectionFilterAppliedBoolean = CollectionFilterAppliedInterface & {
46
+ __typename?: 'CollectionFilterAppliedBoolean';
47
+ appliedValue: boolean;
48
+ trueLabel: string;
49
+ falseLabel: string;
50
+ };
51
+ export type CollectionFilterAvailableBoolean = CollectionFilterAvailableInterface & {
52
+ __typename?: 'CollectionFilterAvailableBoolean';
53
+ trueItemCount?: number | null;
54
+ falseItemCount?: number | null;
55
+ trueLabel: string;
56
+ falseLabel: string;
57
+ };
58
+ export type CollectionFilterAppliedRange = CollectionFilterAppliedInterface & {
59
+ __typename?: 'CollectionFilterAppliedRange';
60
+ };
61
+ export type CollectionFilterAvailableRange = CollectionFilterAvailableInterface & {
62
+ __typename?: 'CollectionFilterAvailableRange';
63
+ };
64
+ export type CollectionFilterApplied = CollectionFilterAppliedOption | CollectionFilterAppliedBoolean | CollectionFilterAppliedRange;
65
+ export type CollectionFilterAvailable = CollectionFilterAvailableOption | CollectionFilterAvailableBoolean | CollectionFilterAvailableRange;
66
+ export declare enum CollectionFilterCondition {
67
+ Eq = "eq",
68
+ Gt = "gt",
69
+ Gteq = "gteq",
70
+ In = "in",
71
+ Like = "like",
72
+ Lt = "lt",
73
+ Lteq = "lteq",
74
+ Neq = "neq",
75
+ Nil = "nil",
76
+ Nin = "nin",
77
+ Nlike = "nlike",
78
+ NotNil = "notNil"
79
+ }
80
+ export type FilterChip = {
81
+ filterCode: string;
82
+ label: string;
83
+ optionId?: string;
84
+ };
85
+ export type SortOrder = {
86
+ code: string;
87
+ direction: string;
88
+ label: string;
89
+ };
90
+ export type AttributeSwatchConfig = {
91
+ code: string;
92
+ swatch?: {
93
+ isEnabled?: boolean | null;
94
+ filterRenderer?: string | null;
95
+ listRenderer?: string | null;
96
+ detailRenderer?: string | null;
97
+ } | null;
98
+ };
99
+ export type ProductAttributeData = {
100
+ id: string;
101
+ code: string;
102
+ label: string;
103
+ type: string;
104
+ isFilterable: boolean;
105
+ isSortable: boolean;
106
+ options: {
107
+ id: string;
108
+ code: string;
109
+ label: string;
110
+ swatch?: SwatchData | null;
111
+ }[];
112
+ swatch?: AttributeSwatchConfig['swatch'];
113
+ };
@@ -0,0 +1,11 @@
1
+ import { CollectionFilterCondition, CollectionFilterAppliedInterface } from '../../types/collection';
2
+ type FilterGroup = {
3
+ filters: Array<{
4
+ field: string;
5
+ condition: CollectionFilterCondition;
6
+ value?: string;
7
+ values?: string[];
8
+ }>;
9
+ };
10
+ export declare function buildFilterGroups(categoryId: string | undefined, appliedFilters: CollectionFilterAppliedInterface[]): FilterGroup[];
11
+ export {};
@@ -0,0 +1 @@
1
+ var a=(e=>(e.Eq="eq",e.Gt="gt",e.Gteq="gteq",e.In="in",e.Like="like",e.Lt="lt",e.Lteq="lteq",e.Neq="neq",e.Nil="nil",e.Nin="nin",e.Nlike="nlike",e.NotNil="notNil",e))(a||{});function l(e,q){const s=[];e&&s.push({field:"categoryId",condition:a.Eq,value:e});for(const p of q){const u="__typename"in p?p.__typename:void 0;if(u==="CollectionFilterAppliedOption"&&"options"in p){const i=p.options.filter(f=>f.isApplied);i.length>0&&s.push({field:p.code,condition:a.In,values:i.map(f=>f.code)})}else if(u==="CollectionFilterAppliedBoolean"&&"appliedValue"in p){const t=p;s.push({field:p.code,condition:a.Eq,value:t.appliedValue?"true":"false"})}}return[{filters:s}]}export{l as buildFilterGroups};
@@ -0,0 +1,13 @@
1
+ import { CollectionFilterAppliedInterface } from '../../types/collection';
2
+ /**
3
+ * Serialize applied filters to URL search params.
4
+ * Option filters: ?color=code1,code2&size=code3
5
+ * Boolean filters: ?is_new=true
6
+ */
7
+ export declare function filtersToSearchParams(appliedFilters: CollectionFilterAppliedInterface[]): URLSearchParams;
8
+ /**
9
+ * Deserialize URL search params into a minimal applied filters structure.
10
+ * Returns partial filter objects with code + options/value — enough for
11
+ * buildFilterGroups to construct the GraphQL query.
12
+ */
13
+ export declare function searchParamsToFilters(params: Record<string, string | string[] | undefined>): CollectionFilterAppliedInterface[];
@@ -0,0 +1 @@
1
+ function p(r){const o=new URLSearchParams;for(const e of r){const i="__typename"in e?e.__typename:void 0;if(i==="CollectionFilterAppliedOption"&&"options"in e){const l=e.options.filter(n=>n.isApplied).map(n=>n.code);l.length>0&&o.set(e.code,l.join(","))}else if(i==="CollectionFilterAppliedBoolean"&&"appliedValue"in e){const t=e;o.set(e.code,t.appliedValue?"true":"false")}}return o}function a(r){const o=[];for(const[e,i]of Object.entries(r)){if(!i)continue;const t=Array.isArray(i)?i[0]:i;if(t)if(t==="true"||t==="false")o.push({__typename:"CollectionFilterAppliedBoolean",code:e,label:e,renderer:"boolean",type:"boolean",includeInFilters:!0,appliedValue:t==="true"});else{const l=t.split(",").filter(Boolean);l.length>0&&o.push({__typename:"CollectionFilterAppliedOption",code:e,label:e,renderer:"option",type:"option",includeInFilters:!0,isSearchable:!1,options:l.map(n=>({id:"",code:n,label:"",isApplied:!0}))})}}return o}export{p as filtersToSearchParams,a as searchParamsToFilters};
@@ -0,0 +1,2 @@
1
+ import { CollectionFilterAppliedInterface, FilterChip } from '../../types/collection';
2
+ export declare function getAppliedFilterChips(appliedFilters: CollectionFilterAppliedInterface[]): FilterChip[];
@@ -0,0 +1 @@
1
+ function n(p){const l=[];for(const e of p){const t="__typename"in e?e.__typename:void 0;if(t==="CollectionFilterAppliedOption"&&"options"in e){const o=e;for(const i of o.options)i.isApplied&&l.push({filterCode:e.code,label:i.label,optionId:i.code})}else if(t==="CollectionFilterAppliedBoolean"&&"appliedValue"in e){const o=e;l.push({filterCode:e.code,label:`${e.label}: ${o.appliedValue?o.trueLabel:o.falseLabel}`})}}return l}export{n as getAppliedFilterChips};
@@ -0,0 +1,5 @@
1
+ export { buildFilterGroups } from './buildFilterGroups';
2
+ export { filtersToSearchParams, searchParamsToFilters, } from './filterSearchParams';
3
+ export { mergeFilters } from './mergeFilters';
4
+ export { getAppliedFilterChips } from './getAppliedFilterChips';
5
+ export { removeFilterChip } from './removeFilterChip';
@@ -0,0 +1 @@
1
+ import{buildFilterGroups as o}from"./buildFilterGroups.js";import{filtersToSearchParams as i,searchParamsToFilters as p}from"./filterSearchParams.js";import{mergeFilters as l}from"./mergeFilters.js";import{getAppliedFilterChips as a}from"./getAppliedFilterChips.js";import{removeFilterChip as x}from"./removeFilterChip.js";export{o as buildFilterGroups,i as filtersToSearchParams,a as getAppliedFilterChips,l as mergeFilters,x as removeFilterChip,p as searchParamsToFilters};
@@ -0,0 +1,2 @@
1
+ import { CollectionFilterAppliedInterface, CollectionFilterAvailableInterface } from '../../types/collection';
2
+ export declare function mergeFilters(appliedFilters: CollectionFilterAppliedInterface[], availableFilters: CollectionFilterAvailableInterface[]): (CollectionFilterAppliedInterface | CollectionFilterAvailableInterface)[];
@@ -0,0 +1 @@
1
+ function r(o,t){const n=new Set(o.map(e=>e.code));return[...o,...t.filter(e=>!n.has(e.code))]}export{r as mergeFilters};
@@ -0,0 +1,2 @@
1
+ import { CollectionFilterAppliedInterface, FilterChip } from '../../types/collection';
2
+ export declare function removeFilterChip(appliedFilters: CollectionFilterAppliedInterface[], chip: FilterChip): CollectionFilterAppliedInterface[];
@@ -0,0 +1 @@
1
+ function l(n,t){return t.optionId?n.map(o=>{if(o.code!==t.filterCode)return o;const e=o;if(!("options"in e))return o;const r=e.options.filter(i=>i.code!==t.optionId);return r.length===0?null:{...e,options:r}}).filter(Boolean):n.filter(o=>o.code!==t.filterCode)}export{l as removeFilterChip};
@@ -0,0 +1,4 @@
1
+ /** Check if a string is a valid hex color. */
2
+ export declare function isValidHex(hex: string | null | undefined): boolean;
3
+ /** Very light colors (white, near-white) need a border and dark checkmark. */
4
+ export declare function isVeryLight(hex: string | null): boolean;
package/utils/color.js ADDED
@@ -0,0 +1 @@
1
+ const e=/^#?([0-9a-f]{3}|[0-9a-f]{6})$/i;function c(t){return t!=null&&e.test(t)}function o(t){if(!t||!c(t))return!1;const n=t.replace("#",""),s=parseInt(n.substring(0,2),16),r=parseInt(n.substring(2,4),16),i=parseInt(n.substring(4,6),16);return(s*299+r*587+i*114)/1e3>230}export{c as isValidHex,o as isVeryLight};
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Swatch renderer constants matching SwatchRenderer enum values
3
+ * from ProductAttributeSwatch.filterRenderer / listRenderer / detailRenderer.
4
+ */
5
+ export declare const SwatchRenderer: {
6
+ readonly SWATCH: "SWATCH";
7
+ readonly IMAGE: "IMAGE";
8
+ readonly PRODUCT_IMAGE: "PRODUCT_IMAGE";
9
+ readonly TEXT: "TEXT";
10
+ };
11
+ export type SwatchRendererType = (typeof SwatchRenderer)[keyof typeof SwatchRenderer];
12
+ /** Check if a string looks like a valid image URL (http(s) or absolute path). */
13
+ export declare function isValidImageUrl(value: string | null | undefined): boolean;
14
+ export declare function isSwatchRenderer(value: string | null | undefined): value is SwatchRendererType;
@@ -0,0 +1 @@
1
+ const t={SWATCH:"SWATCH",IMAGE:"IMAGE",PRODUCT_IMAGE:"PRODUCT_IMAGE",TEXT:"TEXT"},n=/^(https?:\/\/|\/)/;function r(T){return T!=null&&n.test(T)}function A(T){return T===t.SWATCH||T===t.IMAGE||T===t.PRODUCT_IMAGE||T===t.TEXT}export{t as SwatchRenderer,A as isSwatchRenderer,r as isValidImageUrl};