@shopify/shop-minis-react 0.1.4 → 0.1.6

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 (125) hide show
  1. package/dist/_virtual/index7.js +2 -3
  2. package/dist/_virtual/index7.js.map +1 -1
  3. package/dist/_virtual/index8.js +3 -2
  4. package/dist/_virtual/index8.js.map +1 -1
  5. package/dist/components/atoms/list.js +110 -48
  6. package/dist/components/atoms/list.js.map +1 -1
  7. package/dist/components/commerce/add-to-cart.js +82 -0
  8. package/dist/components/commerce/add-to-cart.js.map +1 -0
  9. package/dist/components/{atoms → commerce}/favorite-button.js +1 -1
  10. package/dist/components/commerce/favorite-button.js.map +1 -0
  11. package/dist/components/commerce/product-card.js +10 -10
  12. package/dist/components/commerce/product-card.js.map +1 -1
  13. package/dist/components/commerce/product-link.js +6 -6
  14. package/dist/components/commerce/product-link.js.map +1 -1
  15. package/dist/components/commerce/search.js +73 -77
  16. package/dist/components/commerce/search.js.map +1 -1
  17. package/dist/components/ui/accordion.js +1 -1
  18. package/dist/components/ui/alert-dialog.js +1 -1
  19. package/dist/components/ui/avatar.js +1 -1
  20. package/dist/components/ui/checkbox.js +1 -1
  21. package/dist/components/ui/dialog.js +1 -1
  22. package/dist/components/ui/label.js +1 -1
  23. package/dist/components/ui/progress.js +1 -1
  24. package/dist/components/ui/radio-group.js +1 -1
  25. package/dist/components/ui/scroll-area.js +1 -1
  26. package/dist/components/ui/select.js +1 -1
  27. package/dist/components/ui/separator.js +1 -1
  28. package/dist/components/ui/sheet.js +1 -1
  29. package/dist/index.js +276 -274
  30. package/dist/index.js.map +1 -1
  31. package/dist/internal/components/refresh-indicator.js +83 -0
  32. package/dist/internal/components/refresh-indicator.js.map +1 -0
  33. package/dist/internal/usePullToRefresh.js +149 -0
  34. package/dist/internal/usePullToRefresh.js.map +1 -0
  35. package/dist/internal/utils/virtuoso-dom.js +20 -0
  36. package/dist/internal/utils/virtuoso-dom.js.map +1 -0
  37. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-accordion@1.2.11_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@19_krqvbhitcyb3vl2jzw3fp35uhu → @radix-ui_react-accordion@1.2.11_@types_react-dom@19.1.6_@types_react@19.1.6__@types_re_c181f188656cfa5f16c2710ccd54e5dd}/node_modules/@radix-ui/react-accordion/dist/index.js +3 -3
  38. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-accordion@1.2.11_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@19_krqvbhitcyb3vl2jzw3fp35uhu → @radix-ui_react-accordion@1.2.11_@types_react-dom@19.1.6_@types_react@19.1.6__@types_re_c181f188656cfa5f16c2710ccd54e5dd}/node_modules/@radix-ui/react-accordion/dist/index.js.map +1 -1
  39. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-alert-dialog@1.1.14_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react_5smkpul3xqqoqjowwwfk226yey → @radix-ui_react-alert-dialog@1.1.14_@types_react-dom@19.1.6_@types_react@19.1.6__@types_285ea9ce18742a8c4e84cbcde06e0b25}/node_modules/@radix-ui/react-alert-dialog/dist/index.js +1 -1
  40. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-alert-dialog@1.1.14_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react_5smkpul3xqqoqjowwwfk226yey → @radix-ui_react-alert-dialog@1.1.14_@types_react-dom@19.1.6_@types_react@19.1.6__@types_285ea9ce18742a8c4e84cbcde06e0b25}/node_modules/@radix-ui/react-alert-dialog/dist/index.js.map +1 -1
  41. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-arrow@1.1.7_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@19.1.6__tqbcahbocnte57ax4r75bgnpei → @radix-ui_react-arrow@1.1.7_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@1_37a4d12c56f285a88c8895f81bd600a0}/node_modules/@radix-ui/react-arrow/dist/index.js +1 -1
  42. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-arrow@1.1.7_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@19.1.6__tqbcahbocnte57ax4r75bgnpei → @radix-ui_react-arrow@1.1.7_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@1_37a4d12c56f285a88c8895f81bd600a0}/node_modules/@radix-ui/react-arrow/dist/index.js.map +1 -1
  43. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-avatar@1.1.10_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@19.1._hkz57sehyui4ndfh3rsqwxftli → @radix-ui_react-avatar@1.1.10_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react_06170eb4026a170071caea5f0c82612e}/node_modules/@radix-ui/react-avatar/dist/index.js +1 -1
  44. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-avatar@1.1.10_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@19.1._hkz57sehyui4ndfh3rsqwxftli → @radix-ui_react-avatar@1.1.10_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react_06170eb4026a170071caea5f0c82612e}/node_modules/@radix-ui/react-avatar/dist/index.js.map +1 -1
  45. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-checkbox@1.3.2_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@19.1_wopssf2yyhcdxk6apqq3fxlekq → @radix-ui_react-checkbox@1.3.2_@types_react-dom@19.1.6_@types_react@19.1.6__@types_reac_76c2d9960806d267f7566af463150dec}/node_modules/@radix-ui/react-checkbox/dist/index.js +2 -2
  46. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-checkbox@1.3.2_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@19.1_wopssf2yyhcdxk6apqq3fxlekq → @radix-ui_react-checkbox@1.3.2_@types_react-dom@19.1.6_@types_react@19.1.6__@types_reac_76c2d9960806d267f7566af463150dec}/node_modules/@radix-ui/react-checkbox/dist/index.js.map +1 -1
  47. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-collapsible@1.1.11_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@_4e7bghgww3y6lafncm5x7ie54a → @radix-ui_react-collapsible@1.1.11_@types_react-dom@19.1.6_@types_react@19.1.6__@types__4a84ef457ec365b76d5b35ae363501b4}/node_modules/@radix-ui/react-collapsible/dist/index.js +2 -2
  48. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-collapsible@1.1.11_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@_4e7bghgww3y6lafncm5x7ie54a → @radix-ui_react-collapsible@1.1.11_@types_react-dom@19.1.6_@types_react@19.1.6__@types__4a84ef457ec365b76d5b35ae363501b4}/node_modules/@radix-ui/react-collapsible/dist/index.js.map +1 -1
  49. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-collection@1.1.7_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@19_5kif5qsljjcymh5vn5h26ovoye → @radix-ui_react-collection@1.1.7_@types_react-dom@19.1.6_@types_react@19.1.6__@types_re_773c2f41d00caf3f92b3d7605949da61}/node_modules/@radix-ui/react-collection/dist/index.js.map +1 -1
  50. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-dialog@1.1.14_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@19.1._sjczexpvrqz6fttoobpwnns2oa → @radix-ui_react-dialog@1.1.14_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react_00050fb27f6e4250402df169ba62a563}/node_modules/@radix-ui/react-dialog/dist/index.js +5 -5
  51. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-dialog@1.1.14_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@19.1._sjczexpvrqz6fttoobpwnns2oa → @radix-ui_react-dialog@1.1.14_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react_00050fb27f6e4250402df169ba62a563}/node_modules/@radix-ui/react-dialog/dist/index.js.map +1 -1
  52. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-dismissable-layer@1.1.10_@types_react-dom@19.1.6_@types_react@19.1.6__@types__ipor64qsap4jnr6m7nflv4q5v4 → @radix-ui_react-dismissable-layer@1.1.10_@types_react-dom@19.1.6_@types_react@19.1.6__@_4b09e0704ece360f13e36cbb3765ea47}/node_modules/@radix-ui/react-dismissable-layer/dist/index.js +1 -1
  53. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-dismissable-layer@1.1.10_@types_react-dom@19.1.6_@types_react@19.1.6__@types__ipor64qsap4jnr6m7nflv4q5v4 → @radix-ui_react-dismissable-layer@1.1.10_@types_react-dom@19.1.6_@types_react@19.1.6__@_4b09e0704ece360f13e36cbb3765ea47}/node_modules/@radix-ui/react-dismissable-layer/dist/index.js.map +1 -1
  54. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-focus-scope@1.1.7_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@1_zpdtvgogfsfavk62phevpdv4fu → @radix-ui_react-focus-scope@1.1.7_@types_react-dom@19.1.6_@types_react@19.1.6__@types_r_b37fcafba5387ffb2fedae9e4476c837}/node_modules/@radix-ui/react-focus-scope/dist/index.js +1 -1
  55. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-focus-scope@1.1.7_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@1_zpdtvgogfsfavk62phevpdv4fu → @radix-ui_react-focus-scope@1.1.7_@types_react-dom@19.1.6_@types_react@19.1.6__@types_r_b37fcafba5387ffb2fedae9e4476c837}/node_modules/@radix-ui/react-focus-scope/dist/index.js.map +1 -1
  56. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-label@2.1.7_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@19.1.6__a5hm3akpfjy2hepxrptadu7t6e → @radix-ui_react-label@2.1.7_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@1_85b8c95b65bf0dd4cc21629f4084a5b6}/node_modules/@radix-ui/react-label/dist/index.js +1 -1
  57. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-label@2.1.7_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@19.1.6__a5hm3akpfjy2hepxrptadu7t6e → @radix-ui_react-label@2.1.7_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@1_85b8c95b65bf0dd4cc21629f4084a5b6}/node_modules/@radix-ui/react-label/dist/index.js.map +1 -1
  58. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-popper@1.2.7_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@19.1.6_2sktqq4blwmd3t7e5jqffehtuu → @radix-ui_react-popper@1.2.7_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@_7f041a511b7212d213e8dcc35ea98be2}/node_modules/@radix-ui/react-popper/dist/index.js +2 -2
  59. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-popper@1.2.7_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@19.1.6_2sktqq4blwmd3t7e5jqffehtuu → @radix-ui_react-popper@1.2.7_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@_7f041a511b7212d213e8dcc35ea98be2}/node_modules/@radix-ui/react-popper/dist/index.js.map +1 -1
  60. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-portal@1.1.9_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@19.1.6_sr45arufxpp6hzdn2l7kcrtvqe → @radix-ui_react-portal@1.1.9_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@_dbd1c487dbf0f36d89874abfade43936}/node_modules/@radix-ui/react-portal/dist/index.js +1 -1
  61. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-portal@1.1.9_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@19.1.6_sr45arufxpp6hzdn2l7kcrtvqe → @radix-ui_react-portal@1.1.9_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@_dbd1c487dbf0f36d89874abfade43936}/node_modules/@radix-ui/react-portal/dist/index.js.map +1 -1
  62. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-presence@1.1.4_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@19.1_5ga5lr7i6zouk7rhd63vbsmjyu → @radix-ui_react-presence@1.1.4_@types_react-dom@19.1.6_@types_react@19.1.6__@types_reac_14564cac32122309a7cc56096494b6e8}/node_modules/@radix-ui/react-presence/dist/index.js.map +1 -1
  63. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-primitive@2.1.3_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@19._5iu4xtybujtep4zwi6jviky6tu → @radix-ui_react-primitive@2.1.3_@types_react-dom@19.1.6_@types_react@19.1.6__@types_rea_ec571b14c8dc7e1c1fa1383d9a1e10bd}/node_modules/@radix-ui/react-primitive/dist/index.js.map +1 -1
  64. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-progress@1.1.7_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@19.1_psrnvm437duefd2y5sh3bqj3sm → @radix-ui_react-progress@1.1.7_@types_react-dom@19.1.6_@types_react@19.1.6__@types_reac_fa1ba230d1dfc5fe2a0cedd9ff78309e}/node_modules/@radix-ui/react-progress/dist/index.js +1 -1
  65. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-progress@1.1.7_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@19.1_psrnvm437duefd2y5sh3bqj3sm → @radix-ui_react-progress@1.1.7_@types_react-dom@19.1.6_@types_react@19.1.6__@types_reac_fa1ba230d1dfc5fe2a0cedd9ff78309e}/node_modules/@radix-ui/react-progress/dist/index.js.map +1 -1
  66. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-radio-group@1.3.7_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@1_b6vd3n6tkje342bk7jtbsrqyfe → @radix-ui_react-radio-group@1.3.7_@types_react-dom@19.1.6_@types_react@19.1.6__@types_r_4bdbb04c93e325d7f20e33cc8e004c83}/node_modules/@radix-ui/react-radio-group/dist/index.js +3 -3
  67. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-radio-group@1.3.7_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@1_b6vd3n6tkje342bk7jtbsrqyfe → @radix-ui_react-radio-group@1.3.7_@types_react-dom@19.1.6_@types_react@19.1.6__@types_r_4bdbb04c93e325d7f20e33cc8e004c83}/node_modules/@radix-ui/react-radio-group/dist/index.js.map +1 -1
  68. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-roving-focus@1.1.10_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react_g7ajeb2xgflky4nc3ccraxd5vi → @radix-ui_react-roving-focus@1.1.10_@types_react-dom@19.1.6_@types_react@19.1.6__@types_2d3f19e691372edf0bc25a87278ce401}/node_modules/@radix-ui/react-roving-focus/dist/index.js +2 -2
  69. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-roving-focus@1.1.10_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react_g7ajeb2xgflky4nc3ccraxd5vi → @radix-ui_react-roving-focus@1.1.10_@types_react-dom@19.1.6_@types_react@19.1.6__@types_2d3f19e691372edf0bc25a87278ce401}/node_modules/@radix-ui/react-roving-focus/dist/index.js.map +1 -1
  70. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-scroll-area@1.2.9_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@1_na6tcikkwmgt5sgxl6ezf7zaf4 → @radix-ui_react-scroll-area@1.2.9_@types_react-dom@19.1.6_@types_react@19.1.6__@types_r_b5d13464c21f88022505288dd3198025}/node_modules/@radix-ui/react-scroll-area/dist/index.js +2 -2
  71. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-scroll-area@1.2.9_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@1_na6tcikkwmgt5sgxl6ezf7zaf4 → @radix-ui_react-scroll-area@1.2.9_@types_react-dom@19.1.6_@types_react@19.1.6__@types_r_b5d13464c21f88022505288dd3198025}/node_modules/@radix-ui/react-scroll-area/dist/index.js.map +1 -1
  72. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-select@2.2.5_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@19.1.6_pvbs4vbfxc35gflcf62ix5b3oy → @radix-ui_react-select@2.2.5_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@_8c539485311b2ef067a182d5db805374}/node_modules/@radix-ui/react-select/dist/index.js +7 -7
  73. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-select@2.2.5_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@19.1.6_pvbs4vbfxc35gflcf62ix5b3oy → @radix-ui_react-select@2.2.5_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@_8c539485311b2ef067a182d5db805374}/node_modules/@radix-ui/react-select/dist/index.js.map +1 -1
  74. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-separator@1.1.7_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@19._v5c7fe2eicmnfbsit6liht3cja → @radix-ui_react-separator@1.1.7_@types_react-dom@19.1.6_@types_react@19.1.6__@types_rea_c3003570fa0d6f9943d03ab6b06417ce}/node_modules/@radix-ui/react-separator/dist/index.js +1 -1
  75. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-separator@1.1.7_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@19._v5c7fe2eicmnfbsit6liht3cja → @radix-ui_react-separator@1.1.7_@types_react-dom@19.1.6_@types_react@19.1.6__@types_rea_c3003570fa0d6f9943d03ab6b06417ce}/node_modules/@radix-ui/react-separator/dist/index.js.map +1 -1
  76. package/dist/shop-minis-react/node_modules/.pnpm/@radix-ui_react-use-is-hydrated@0.1.0_@types_react@19.1.6_react@19.1.0/node_modules/@radix-ui/react-use-is-hydrated/dist/index.js +1 -1
  77. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-visually-hidden@1.2.3_@types_react-dom@19.1.6_@types_react@19.1.6__@types_rea_3rnyx4jgemf4vn7spx7uohk4ne → @radix-ui_react-visually-hidden@1.2.3_@types_react-dom@19.1.6_@types_react@19.1.6__@typ_4a5a40fef19214ea28d77d9af27dab61}/node_modules/@radix-ui/react-visually-hidden/dist/index.js +1 -1
  78. package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-visually-hidden@1.2.3_@types_react-dom@19.1.6_@types_react@19.1.6__@types_rea_3rnyx4jgemf4vn7spx7uohk4ne → @radix-ui_react-visually-hidden@1.2.3_@types_react-dom@19.1.6_@types_react@19.1.6__@typ_4a5a40fef19214ea28d77d9af27dab61}/node_modules/@radix-ui/react-visually-hidden/dist/index.js.map +1 -1
  79. package/dist/shop-minis-react/node_modules/.pnpm/color-string@1.9.1/node_modules/color-string/index.js +1 -1
  80. package/dist/shop-minis-react/node_modules/.pnpm/motion@12.17.3_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/motion/dist/es/framer-motion/dist/es/components/AnimatePresence/PopChild.js +55 -0
  81. package/dist/shop-minis-react/node_modules/.pnpm/motion@12.17.3_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/motion/dist/es/framer-motion/dist/es/components/AnimatePresence/PopChild.js.map +1 -0
  82. package/dist/shop-minis-react/node_modules/.pnpm/motion@12.17.3_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/motion/dist/es/framer-motion/dist/es/components/AnimatePresence/PresenceChild.js +35 -0
  83. package/dist/shop-minis-react/node_modules/.pnpm/motion@12.17.3_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/motion/dist/es/framer-motion/dist/es/components/AnimatePresence/PresenceChild.js.map +1 -0
  84. package/dist/shop-minis-react/node_modules/.pnpm/motion@12.17.3_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/motion/dist/es/framer-motion/dist/es/components/AnimatePresence/index.js +46 -0
  85. package/dist/shop-minis-react/node_modules/.pnpm/motion@12.17.3_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/motion/dist/es/framer-motion/dist/es/components/AnimatePresence/index.js.map +1 -0
  86. package/dist/shop-minis-react/node_modules/.pnpm/motion@12.17.3_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/motion/dist/es/framer-motion/dist/es/components/AnimatePresence/utils.js +13 -0
  87. package/dist/shop-minis-react/node_modules/.pnpm/motion@12.17.3_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/motion/dist/es/framer-motion/dist/es/components/AnimatePresence/utils.js.map +1 -0
  88. package/dist/shop-minis-react/node_modules/.pnpm/react-virtuoso@4.14.0_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/react-virtuoso/dist/index.js +3132 -0
  89. package/dist/shop-minis-react/node_modules/.pnpm/react-virtuoso@4.14.0_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/react-virtuoso/dist/index.js.map +1 -0
  90. package/dist/shop-minis-react/node_modules/.pnpm/simple-swizzle@0.2.2/node_modules/simple-swizzle/index.js +1 -1
  91. package/dist/shop-minis-react/node_modules/.pnpm/use-sync-external-store@1.5.0_react@19.1.0/node_modules/use-sync-external-store/shim/index.js +1 -1
  92. package/dist/shop-minis-react/node_modules/.pnpm/vaul@1.1.2_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@19.1.6_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/vaul/dist/index.js +1 -1
  93. package/package.json +2 -2
  94. package/src/components/atoms/list.tsx +115 -66
  95. package/src/components/commerce/add-to-cart.test.tsx +73 -0
  96. package/src/components/commerce/add-to-cart.tsx +132 -0
  97. package/src/components/{atoms → commerce}/favorite-button.tsx +1 -1
  98. package/src/components/commerce/product-card.tsx +2 -1
  99. package/src/components/commerce/product-link.tsx +2 -1
  100. package/src/components/commerce/search.tsx +0 -6
  101. package/src/components/index.ts +2 -1
  102. package/src/internal/components/refresh-indicator.tsx +103 -0
  103. package/src/internal/usePullToRefresh.ts +286 -0
  104. package/src/internal/utils/virtuoso-dom.ts +26 -0
  105. package/src/stories/AddToCart.stories.tsx +186 -0
  106. package/src/stories/FavoriteButton.stories.tsx +2 -2
  107. package/src/stories/List.stories.tsx +0 -4
  108. package/src/stories/PullToRefreshList.stories.tsx +122 -0
  109. package/src/styles/animations.css +54 -0
  110. package/src/test-setup.ts +68 -0
  111. package/dist/components/atoms/favorite-button.js.map +0 -1
  112. package/dist/shop-minis-react/node_modules/.pnpm/@babel_runtime@7.27.6/node_modules/@babel/runtime/helpers/esm/assertThisInitialized.js +0 -8
  113. package/dist/shop-minis-react/node_modules/.pnpm/@babel_runtime@7.27.6/node_modules/@babel/runtime/helpers/esm/assertThisInitialized.js.map +0 -1
  114. package/dist/shop-minis-react/node_modules/.pnpm/@babel_runtime@7.27.6/node_modules/@babel/runtime/helpers/esm/inheritsLoose.js +0 -8
  115. package/dist/shop-minis-react/node_modules/.pnpm/@babel_runtime@7.27.6/node_modules/@babel/runtime/helpers/esm/inheritsLoose.js.map +0 -1
  116. package/dist/shop-minis-react/node_modules/.pnpm/@babel_runtime@7.27.6/node_modules/@babel/runtime/helpers/esm/setPrototypeOf.js +0 -9
  117. package/dist/shop-minis-react/node_modules/.pnpm/@babel_runtime@7.27.6/node_modules/@babel/runtime/helpers/esm/setPrototypeOf.js.map +0 -1
  118. package/dist/shop-minis-react/node_modules/.pnpm/memoize-one@5.2.1/node_modules/memoize-one/dist/memoize-one.esm.js +0 -28
  119. package/dist/shop-minis-react/node_modules/.pnpm/memoize-one@5.2.1/node_modules/memoize-one/dist/memoize-one.esm.js.map +0 -1
  120. package/dist/shop-minis-react/node_modules/.pnpm/react-window@1.8.11_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/react-window/dist/index.esm.js +0 -375
  121. package/dist/shop-minis-react/node_modules/.pnpm/react-window@1.8.11_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/react-window/dist/index.esm.js.map +0 -1
  122. /package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-collection@1.1.7_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@19_5kif5qsljjcymh5vn5h26ovoye → @radix-ui_react-collection@1.1.7_@types_react-dom@19.1.6_@types_react@19.1.6__@types_re_773c2f41d00caf3f92b3d7605949da61}/node_modules/@radix-ui/react-collection/dist/index.js +0 -0
  123. /package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-presence@1.1.4_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@19.1_5ga5lr7i6zouk7rhd63vbsmjyu → @radix-ui_react-presence@1.1.4_@types_react-dom@19.1.6_@types_react@19.1.6__@types_reac_14564cac32122309a7cc56096494b6e8}/node_modules/@radix-ui/react-presence/dist/index.js +0 -0
  124. /package/dist/shop-minis-react/node_modules/.pnpm/{@radix-ui_react-primitive@2.1.3_@types_react-dom@19.1.6_@types_react@19.1.6__@types_react@19._5iu4xtybujtep4zwi6jviky6tu → @radix-ui_react-primitive@2.1.3_@types_react-dom@19.1.6_@types_react@19.1.6__@types_rea_ec571b14c8dc7e1c1fa1383d9a1e10bd}/node_modules/@radix-ui/react-primitive/dist/index.js +0 -0
  125. /package/src/components/{atoms → commerce}/favorite-button.test.tsx +0 -0
@@ -1,30 +1,33 @@
1
- import {ComponentType, useCallback, useRef} from 'react'
1
+ import {useCallback, useEffect, useRef} from 'react'
2
2
 
3
- import {
4
- VariableSizeList as _VariableSizeList,
5
- VariableSizeListProps,
6
- } from 'react-window'
3
+ import {Virtuoso, VirtuosoProps} from 'react-virtuoso'
7
4
 
5
+ import {RefreshIndicator} from '../../internal/components/refresh-indicator'
6
+ import {usePullToRefresh} from '../../internal/usePullToRefresh'
7
+ import {findVirtuosoScrollableElement} from '../../internal/utils/virtuoso-dom'
8
8
  import {cn} from '../../lib/utils'
9
9
  import '../../styles/utilities.css'
10
10
 
11
11
  import {Pagination} from './pagination'
12
12
 
13
- const VariableSizeList =
14
- _VariableSizeList as unknown as ComponentType<VariableSizeListProps>
13
+ const DEFAULT_REFRESH_PULL_THRESHOLD = 200
14
+ const ELEMENT_BIND_DELAY = 100
15
15
 
16
16
  interface Props<T = any>
17
17
  extends Omit<
18
- VariableSizeListProps<T>,
19
- 'children' | 'itemCount' | 'width' | 'itemSize' | 'direction'
18
+ VirtuosoProps<T, unknown>,
19
+ 'data' | 'itemContent' | 'endReached'
20
20
  > {
21
21
  items: T[]
22
22
  renderItem: (item: T, index: number) => React.ReactNode
23
- itemSizeForRow: (index: number) => number
24
23
  showScrollbar?: boolean
25
24
  header?: React.ReactNode
26
- headerHeight?: number
27
25
  fetchMore?: () => Promise<void>
26
+ loadingComponent?: React.ReactNode
27
+ isFetchingMore?: boolean
28
+ onRefresh?: () => Promise<void>
29
+ refreshing?: boolean
30
+ enablePullToRefresh?: boolean
28
31
  }
29
32
 
30
33
  export function List<T = any>({
@@ -34,17 +37,23 @@ export function List<T = any>({
34
37
  className,
35
38
  showScrollbar = false,
36
39
  header,
37
- headerHeight,
38
- itemSizeForRow,
39
40
  fetchMore,
40
- overscanCount,
41
- ...listProps
41
+ loadingComponent,
42
+ isFetchingMore,
43
+ onRefresh,
44
+ refreshing,
45
+ enablePullToRefresh = true,
46
+ ...virtuosoProps
42
47
  }: Props<T>) {
43
48
  const inFlightFetchMoreRef = useRef<Promise<void> | null>(null)
49
+ const virtuosoRef = useRef<any>(null)
50
+ const containerRef = useRef<HTMLDivElement>(null)
44
51
 
45
- // This is workaround to prevent multiple calls to fetchMore
46
- // react-window re-renders the rows while scrolling,
47
- // and the TrackingPixel could be triggered multiple times
52
+ const {state: pullToRefreshState, bindToElement} = usePullToRefresh({
53
+ onRefresh,
54
+ threshold: DEFAULT_REFRESH_PULL_THRESHOLD,
55
+ enabled: enablePullToRefresh && Boolean(onRefresh),
56
+ })
48
57
 
49
58
  const _fetchMore = useCallback(() => {
50
59
  // Dedupe concurrent calls by returning the same in-flight promise
@@ -60,62 +69,102 @@ export function List<T = any>({
60
69
  inFlightFetchMoreRef.current = current
61
70
  }, [fetchMore])
62
71
 
63
- const rowRenderer = useCallback(
64
- ({index, style}: {index: number; style: React.CSSProperties}) => {
65
- // prepend the header to the first row if it exists
66
- if (header && index === 0) {
67
- return (
68
- <div style={style}>
69
- {header}
70
- <div style={{top: headerHeight}}>{renderItem(items[0], 0)}</div>
71
- </div>
72
- )
73
- }
72
+ const itemContent = useCallback(
73
+ (index: number, item: T) => <>{renderItem(item, index)}</>,
74
+ [renderItem]
75
+ )
74
76
 
75
- // append the pagination spinner to the last row if fetchMore exists
76
- if (fetchMore && index === items.length - 1) {
77
- return (
78
- <div style={style}>
79
- {renderItem(items[index], index)}
80
- <div style={{bottom: 0}}>
81
- <Pagination fetchMore={_fetchMore} />
82
- </div>
83
- </div>
84
- )
85
- }
77
+ const Footer = useCallback(() => {
78
+ if (!fetchMore) return null
86
79
 
87
- return <div style={style}>{renderItem(items[index], index)}</div>
88
- },
89
- [items, renderItem, header, headerHeight, _fetchMore, fetchMore]
90
- )
80
+ return (
81
+ <Pagination
82
+ fetchMore={_fetchMore}
83
+ loadingComponent={loadingComponent}
84
+ isFetchingMore={isFetchingMore}
85
+ />
86
+ )
87
+ }, [_fetchMore, fetchMore, loadingComponent, isFetchingMore])
91
88
 
92
- const getItemSize = useCallback(
93
- (index: number) => {
94
- // include the header height in the first row height
95
- if (header && index === 0) {
96
- const _headerHeight = headerHeight || 0
97
- return _headerHeight + itemSizeForRow(index)
89
+ const classNames = cn(showScrollbar ? undefined : 'no-scrollbars', className)
90
+
91
+ useEffect(() => {
92
+ if (containerRef.current && enablePullToRefresh && onRefresh) {
93
+ let cleanup: (() => void) | undefined
94
+
95
+ const findAndBind = () => {
96
+ if (!containerRef.current) return
97
+
98
+ const scrollableElement = findVirtuosoScrollableElement(
99
+ containerRef.current
100
+ )
101
+ cleanup = bindToElement(scrollableElement)
98
102
  }
99
103
 
100
- return itemSizeForRow(index)
101
- },
102
- [itemSizeForRow, header, headerHeight]
103
- )
104
+ const timeoutId = setTimeout(findAndBind, ELEMENT_BIND_DELAY)
104
105
 
105
- const classNames = cn(showScrollbar ? undefined : 'no-scrollbars', className)
106
+ return () => {
107
+ clearTimeout(timeoutId)
108
+ if (cleanup) cleanup()
109
+ }
110
+ }
111
+ return undefined
112
+ }, [bindToElement, enablePullToRefresh, onRefresh])
113
+
114
+ const EnhancedHeader = useCallback(() => {
115
+ const effectivePullDistance = refreshing
116
+ ? Math.max(pullToRefreshState.pullDistance, 140)
117
+ : pullToRefreshState.pullDistance
118
+
119
+ const refreshHeaderHeight = Math.min(
120
+ Math.max(effectivePullDistance, 0),
121
+ 140
122
+ )
123
+
124
+ return (
125
+ <>
126
+ {enablePullToRefresh && onRefresh && (
127
+ <div
128
+ className="flex items-center justify-center"
129
+ style={{
130
+ height: refreshHeaderHeight,
131
+ overflow: 'hidden',
132
+ }}
133
+ >
134
+ <RefreshIndicator
135
+ pullDistance={pullToRefreshState.pullDistance}
136
+ threshold={DEFAULT_REFRESH_PULL_THRESHOLD}
137
+ isRefreshing={refreshing ?? false}
138
+ canRefresh={pullToRefreshState.canRefresh}
139
+ className="relative top-0 inset-x-auto"
140
+ />
141
+ </div>
142
+ )}
143
+ {header && <div>{header}</div>}
144
+ </>
145
+ )
146
+ }, [header, enablePullToRefresh, onRefresh, pullToRefreshState, refreshing])
106
147
 
107
148
  return (
108
- <VariableSizeList
109
- className={classNames}
110
- height={height}
111
- direction="vertical"
112
- width="100%"
113
- itemCount={items.length}
114
- overscanCount={overscanCount}
115
- itemSize={getItemSize}
116
- {...listProps}
149
+ <div
150
+ ref={containerRef}
151
+ className={cn('relative transition-all duration-200', classNames)}
152
+ style={{
153
+ height,
154
+ }}
117
155
  >
118
- {rowRenderer}
119
- </VariableSizeList>
156
+ <Virtuoso
157
+ ref={virtuosoRef}
158
+ className="h-full w-full"
159
+ data={items}
160
+ itemContent={itemContent}
161
+ components={{
162
+ Header: EnhancedHeader,
163
+ Footer,
164
+ }}
165
+ endReached={fetchMore ? _fetchMore : undefined}
166
+ {...virtuosoProps}
167
+ />
168
+ </div>
120
169
  )
121
170
  }
@@ -0,0 +1,73 @@
1
+ import {describe, expect, it, vi} from 'vitest'
2
+
3
+ import {render, screen, mockMinisSDK, resetAllMocks} from '../../test-utils'
4
+
5
+ import {AddToCartButton} from './add-to-cart'
6
+
7
+ // Mock hooks
8
+ vi.mock('../../hooks/shop/useShopCartActions', () => ({
9
+ useShopCartActions: () => ({
10
+ addToCart: mockMinisSDK.addToCart,
11
+ buyProduct: mockMinisSDK.buyProduct,
12
+ }),
13
+ }))
14
+
15
+ describe('AddToCartButton', () => {
16
+ const defaultProps = {
17
+ productId: 'gid://shopify/Product/123',
18
+ productVariantId: 'gid://shopify/ProductVariant/456',
19
+ }
20
+
21
+ // eslint-disable-next-line jest/require-top-level-describe
22
+ beforeEach(() => {
23
+ resetAllMocks()
24
+ })
25
+
26
+ it('renders with default text', () => {
27
+ render(<AddToCartButton {...defaultProps} />)
28
+
29
+ expect(screen.getByRole('button')).toBeInTheDocument()
30
+ expect(screen.getByText('Add to cart')).toBeInTheDocument()
31
+ })
32
+
33
+ it('renders with required props', () => {
34
+ render(<AddToCartButton {...defaultProps} />)
35
+
36
+ const button = screen.getByRole('button')
37
+ expect(button).toBeInTheDocument()
38
+ })
39
+
40
+ it('respects disabled prop', () => {
41
+ render(<AddToCartButton {...defaultProps} disabled />)
42
+
43
+ const button = screen.getByRole('button')
44
+ expect(button).toBeDisabled()
45
+ })
46
+
47
+ it('applies custom className', () => {
48
+ render(<AddToCartButton {...defaultProps} className="custom-class" />)
49
+
50
+ const button = screen.getByRole('button')
51
+ expect(button).toHaveClass('custom-class')
52
+ })
53
+
54
+ it('renders with different sizes', () => {
55
+ const {rerender} = render(<AddToCartButton {...defaultProps} size="sm" />)
56
+
57
+ expect(screen.getByRole('button')).toBeInTheDocument()
58
+
59
+ rerender(<AddToCartButton {...defaultProps} size="default" />)
60
+ expect(screen.getByRole('button')).toBeInTheDocument()
61
+
62
+ rerender(<AddToCartButton {...defaultProps} size="lg" />)
63
+ expect(screen.getByRole('button')).toBeInTheDocument()
64
+ })
65
+
66
+ it('renders with discount codes prop', () => {
67
+ const discountCodes = ['SUMMER20', 'FREESHIP']
68
+
69
+ render(<AddToCartButton {...defaultProps} discountCodes={discountCodes} />)
70
+
71
+ expect(screen.getByRole('button')).toBeInTheDocument()
72
+ })
73
+ })
@@ -0,0 +1,132 @@
1
+ import * as React from 'react'
2
+ import {useState, useCallback} from 'react'
3
+
4
+ import {CheckIcon} from 'lucide-react'
5
+ import {motion, AnimatePresence} from 'motion/react'
6
+
7
+ import {useErrorToast, useShopCartActions} from '../../hooks'
8
+ import {cn} from '../../lib/utils'
9
+ import {Button} from '../atoms/button'
10
+
11
+ interface AddToCartButtonProps {
12
+ disabled?: boolean
13
+ className?: string
14
+ size?: 'default' | 'sm' | 'lg'
15
+ /**
16
+ * The discount codes to apply to the cart.
17
+ */
18
+ discountCodes?: string[]
19
+ /**
20
+ * The GID of the product. E.g. `gid://shopify/Product/123`.
21
+ */
22
+ productId: string
23
+ /**
24
+ * The GID of the product variant. E.g. `gid://shopify/ProductVariant/456`.
25
+ */
26
+ productVariantId: string
27
+ }
28
+
29
+ export function AddToCartButton({
30
+ disabled = false,
31
+ className,
32
+ size = 'default',
33
+ productId,
34
+ productVariantId,
35
+ discountCodes,
36
+ }: AddToCartButtonProps) {
37
+ const {addToCart} = useShopCartActions()
38
+ const [isAdded, setIsAdded] = useState(false)
39
+ const timeoutRef = React.useRef<number | undefined>(undefined)
40
+
41
+ const {showErrorToast} = useErrorToast()
42
+
43
+ const handleClick = useCallback(async () => {
44
+ if (isAdded || disabled) return
45
+
46
+ try {
47
+ // Call the callback if provided
48
+ if (productId && productVariantId) {
49
+ // Optimistic update with error toast
50
+ addToCart({
51
+ productId,
52
+ productVariantId,
53
+ quantity: 1,
54
+ discountCodes,
55
+ })
56
+ .then(() => {})
57
+ .catch(() => {
58
+ showErrorToast({message: 'Failed to add to cart'})
59
+ })
60
+ }
61
+
62
+ // Show success state
63
+ setIsAdded(true)
64
+
65
+ // Clear any existing timeout
66
+ if (timeoutRef.current) {
67
+ clearTimeout(timeoutRef.current)
68
+ }
69
+
70
+ // Reset to initial state after delay
71
+ timeoutRef.current = window.setTimeout(() => {
72
+ setIsAdded(false)
73
+ }, 2000)
74
+ } catch (error) {
75
+ // Handle error - reset to initial state
76
+ setIsAdded(false)
77
+ console.error('Failed to add to cart:', error)
78
+ }
79
+ }, [
80
+ isAdded,
81
+ disabled,
82
+ addToCart,
83
+ productId,
84
+ productVariantId,
85
+ discountCodes,
86
+ showErrorToast,
87
+ ])
88
+
89
+ // Cleanup timeout on unmount
90
+ React.useEffect(() => {
91
+ return () => {
92
+ if (timeoutRef.current) {
93
+ clearTimeout(timeoutRef.current)
94
+ }
95
+ }
96
+ }, [])
97
+
98
+ return (
99
+ <Button
100
+ onClick={handleClick}
101
+ disabled={disabled}
102
+ className={cn(
103
+ 'relative overflow-hidden transition-all duration-300',
104
+ className
105
+ )}
106
+ size={size}
107
+ >
108
+ <div className="relative flex items-center justify-center">
109
+ <AnimatePresence>
110
+ {isAdded && (
111
+ <motion.div
112
+ initial={{scale: 0, rotate: -180}}
113
+ animate={{scale: 1, rotate: 0}}
114
+ exit={{scale: 0, rotate: 180}}
115
+ transition={{
116
+ duration: 0.4,
117
+ ease: [0.175, 0.885, 0.32, 1.275], // bounce effect
118
+ }}
119
+ className="absolute left-0"
120
+ style={{x: -8}}
121
+ >
122
+ <CheckIcon className="size-4" />
123
+ </motion.div>
124
+ )}
125
+ </AnimatePresence>
126
+ <span className={cn(isAdded && 'pl-5', 'transition-all duration-300')}>
127
+ {isAdded ? 'Added to cart' : 'Add to cart'}
128
+ </span>
129
+ </div>
130
+ </Button>
131
+ )
132
+ }
@@ -1,6 +1,6 @@
1
1
  import {Heart} from 'lucide-react'
2
2
 
3
- import {IconButton} from './icon-button'
3
+ import {IconButton} from '../atoms/icon-button'
4
4
 
5
5
  export function FavoriteButton({
6
6
  onClick,
@@ -7,12 +7,13 @@ import {useShopNavigation} from '../../hooks/navigation/useShopNavigation'
7
7
  import {useSavedProductsActions} from '../../hooks/user/useSavedProductsActions'
8
8
  import {formatMoney} from '../../lib/formatMoney'
9
9
  import {cn} from '../../lib/utils'
10
- import {FavoriteButton} from '../atoms/favorite-button'
11
10
  import {Image} from '../atoms/image'
12
11
  import {ProductVariantPrice} from '../atoms/product-variant-price'
13
12
  import {Touchable} from '../atoms/touchable'
14
13
  import {Badge} from '../ui/badge'
15
14
 
15
+ import {FavoriteButton} from './favorite-button'
16
+
16
17
  // Context definition
17
18
  interface ProductCardContextValue {
18
19
  // Core data
@@ -9,10 +9,11 @@ import {useShopNavigation} from '../../hooks/navigation/useShopNavigation'
9
9
  import {useSavedProductsActions} from '../../hooks/user/useSavedProductsActions'
10
10
  import {formatMoney} from '../../lib/formatMoney'
11
11
  import {cn} from '../../lib/utils'
12
- import {FavoriteButton} from '../atoms/favorite-button'
13
12
  import {Touchable} from '../atoms/touchable'
14
13
  import {Card, CardContent, CardAction} from '../ui/card'
15
14
 
15
+ import {FavoriteButton} from './favorite-button'
16
+
16
17
  const productLinkVariants = cva('', {
17
18
  variants: {
18
19
  layout: {
@@ -13,8 +13,6 @@ import {Input} from '../ui/input'
13
13
  import {ProductLink} from './product-link'
14
14
  import {ProductLinkSkeleton} from './product-link-skeleton'
15
15
 
16
- const ESTIMATED_PRODUCT_LINK_HEIGHT = 100
17
-
18
16
  interface SearchContextValue {
19
17
  query: string
20
18
  setQuery: (query: string) => void
@@ -146,10 +144,8 @@ export interface SearchResultsListProps {
146
144
  function SearchResultsList({
147
145
  height = window.innerHeight,
148
146
  renderItem,
149
- itemHeight = ESTIMATED_PRODUCT_LINK_HEIGHT,
150
147
  initialStateComponent,
151
148
  showScrollbar,
152
- overscanCount = 5,
153
149
  }: SearchResultsListProps) {
154
150
  const {query, products, loading, fetchMore, hasNextPage, isTyping} =
155
151
  useSearchContext()
@@ -205,10 +201,8 @@ function SearchResultsList({
205
201
  items={products || []}
206
202
  height={height}
207
203
  renderItem={_renderItem}
208
- itemSizeForRow={() => itemHeight}
209
204
  fetchMore={hasNextPage ? fetchMore : undefined}
210
205
  showScrollbar={showScrollbar}
211
- overscanCount={overscanCount}
212
206
  />
213
207
  )
214
208
  }
@@ -1,5 +1,6 @@
1
1
  export * from './MinisContainer'
2
2
 
3
+ export * from './commerce/add-to-cart'
3
4
  export * from './commerce/product-card'
4
5
  export * from './commerce/product-link'
5
6
  export * from './commerce/merchant-card'
@@ -7,6 +8,7 @@ export * from './commerce/product-card-skeleton'
7
8
  export * from './commerce/merchant-card-skeleton'
8
9
  export * from './commerce/quantity-selector'
9
10
  export * from './commerce/search'
11
+ export * from './commerce/favorite-button'
10
12
 
11
13
  export * from './content/image-content-wrapper'
12
14
 
@@ -14,7 +16,6 @@ export * from './navigation/minis-router'
14
16
  export * from './navigation/transition-link'
15
17
 
16
18
  export * from './atoms/button'
17
- export * from './atoms/favorite-button'
18
19
  export * from './atoms/icon-button'
19
20
  export * from './atoms/image'
20
21
  export * from './atoms/touchable'
@@ -0,0 +1,103 @@
1
+ import {forwardRef, useEffect, useState} from 'react'
2
+
3
+ import {cn} from '../../lib/utils'
4
+
5
+ export interface PullToRefreshIndicatorProps {
6
+ pullDistance: number
7
+ threshold: number
8
+ isRefreshing: boolean
9
+ canRefresh: boolean
10
+ className?: string
11
+ }
12
+
13
+ export const RefreshIndicator = forwardRef<
14
+ HTMLDivElement,
15
+ PullToRefreshIndicatorProps
16
+ >(({pullDistance, threshold, isRefreshing, canRefresh, className}, ref) => {
17
+ const [showBumpAnimation, setShowBumpAnimation] = useState(false)
18
+
19
+ const progress = Math.min(pullDistance / threshold, 1)
20
+
21
+ const spinnerProgress = 0.54 + progress * (1 - 0.54)
22
+
23
+ const scale = isRefreshing ? 1 : 0.5 + progress * 0.5
24
+
25
+ const opacity = isRefreshing ? 1 : Math.min(progress * 1.5, 1)
26
+
27
+ const translateY = isRefreshing ? 0 : progress * 3
28
+
29
+ useEffect(() => {
30
+ if (isRefreshing && !showBumpAnimation) {
31
+ setShowBumpAnimation(true)
32
+ const timer = setTimeout(() => setShowBumpAnimation(false), 360)
33
+ return () => clearTimeout(timer)
34
+ }
35
+ return undefined
36
+ }, [isRefreshing, showBumpAnimation])
37
+
38
+ return (
39
+ <div
40
+ ref={ref}
41
+ className={cn(
42
+ 'flex items-center justify-center w-full h-full',
43
+ 'transition-all duration-200 ease-out',
44
+ className
45
+ )}
46
+ style={{
47
+ transform: `translateY(${translateY}px)`,
48
+ }}
49
+ >
50
+ <div
51
+ className={cn(
52
+ 'flex flex-col items-center space-y-2 rounded-full px-4 py-2 backdrop-blur-sm transition-all duration-200',
53
+ canRefresh || isRefreshing
54
+ ? 'bg-primary/20 border-2 border-primary/40'
55
+ : 'bg-background/90'
56
+ )}
57
+ style={{
58
+ opacity,
59
+ transform: `scale(${scale})`,
60
+ }}
61
+ >
62
+ <div
63
+ className={cn(
64
+ 'h-8 w-8 transition-all duration-200',
65
+ showBumpAnimation && 'animate-bump'
66
+ )}
67
+ >
68
+ <svg
69
+ viewBox="0 0 52 58"
70
+ fill="none"
71
+ xmlns="http://www.w3.org/2000/svg"
72
+ className={cn(
73
+ 'h-full w-full transition-colors duration-200',
74
+ canRefresh || isRefreshing
75
+ ? 'text-primary'
76
+ : 'text-muted-foreground'
77
+ )}
78
+ >
79
+ <path
80
+ className={cn(
81
+ 'shop-spinner-path',
82
+ !isRefreshing && 'shop-spinner-progress',
83
+ isRefreshing && 'animate-shop-spin'
84
+ )}
85
+ d="M3 13C5 11.75 10.4968 6.92307 21.5 6.4999C34.5 5.99993 42 13 45 23C48.3 34 42.9211 48.1335 30.5 51C17.5 54 6.6 46 6 37C5.46667 29 10.5 25 14 23"
86
+ stroke="currentColor"
87
+ strokeWidth="8"
88
+ strokeLinecap="square"
89
+ strokeLinejoin="miter"
90
+ style={
91
+ {
92
+ '--spinner-progress': isRefreshing ? '1' : spinnerProgress,
93
+ } as React.CSSProperties
94
+ }
95
+ />
96
+ </svg>
97
+ </div>
98
+ </div>
99
+ </div>
100
+ )
101
+ })
102
+
103
+ RefreshIndicator.displayName = 'RefreshIndicator'