From ac9f3d9f6f7475f41cb099be7e2c99dfae2b4c9a Mon Sep 17 00:00:00 2001 From: hta218 Date: Wed, 27 Nov 2024 09:28:01 +0700 Subject: [PATCH 01/39] Update desktop menu animation --- app/modules/header/menu/desktop-menu.tsx | 4 ++-- tailwind.config.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/modules/header/menu/desktop-menu.tsx b/app/modules/header/menu/desktop-menu.tsx index 3fb21a9d..0fa761d3 100644 --- a/app/modules/header/menu/desktop-menu.tsx +++ b/app/modules/header/menu/desktop-menu.tsx @@ -173,8 +173,8 @@ function SlideIn(props: { )} style={ { - "--left-distance": "60px", - "--slide-left-and-fade-duration": "200ms", + "--left-distance": "40px", + "--slide-left-and-fade-duration": "400ms", ...style, } as React.CSSProperties } diff --git a/tailwind.config.js b/tailwind.config.js index f962d2eb..a6ad4d99 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -50,7 +50,7 @@ export default { underline: "underline 400ms linear", "fade-in": "fade-in var(--fade-in-duration, .5s) ease-in forwards", "slide-left-and-fade": - "slide-left-and-fade var(--slide-left-and-fade-duration, .5s) cubic-bezier(0.14, 0.8, 0.6, 1) forwards", + "slide-left-and-fade var(--slide-left-and-fade-duration, .5s) cubic-bezier(.165,.84,.44,1) forwards", }, borderWidth: { 6: "6px", From eb173a7d1cdb031fc82cf3a95de59978defb4204 Mon Sep 17 00:00:00 2001 From: hta218 Date: Wed, 27 Nov 2024 10:35:09 +0700 Subject: [PATCH 02/39] Refactor country selector's onClick handler for improved readability and inline function usage --- app/modules/country-selector.tsx | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/app/modules/country-selector.tsx b/app/modules/country-selector.tsx index 04807803..a81d6080 100644 --- a/app/modules/country-selector.tsx +++ b/app/modules/country-selector.tsx @@ -101,25 +101,24 @@ export function CountrySelector() { countryLocale.language === selectedLocale.language && countryLocale.country === selectedLocale.country; - let countryUrlPath = getCountryUrlPath({ - countryLocale, - defaultLocalePrefix, - pathWithoutLocale, - }); - let onChangeLocale = () => - handleLocaleChange({ - redirectTo: countryUrlPath, - buyerIdentity: { - countryCode: countryLocale.country, - }, - }); return ( + handleLocaleChange({ + redirectTo: getCountryUrlPath({ + countryLocale, + defaultLocalePrefix, + pathWithoutLocale, + }), + buyerIdentity: { + countryCode: countryLocale.country, + }, + }) + } + className="text-white bg-neutral-800 hover:bg-neutral-600 w-full p-2 transition flex gap-2 items-center text-left cursor-pointer py-2 px-4 text-sm" > Date: Thu, 28 Nov 2024 12:08:38 +0700 Subject: [PATCH 03/39] Optimize mobile menu render with collapsible comp --- app/modules/header/index.tsx | 29 +-- app/modules/header/menu/mobile-menu.tsx | 297 +++++++----------------- package-lock.json | 49 +++- package.json | 3 +- 4 files changed, 135 insertions(+), 243 deletions(-) diff --git a/app/modules/header/index.tsx b/app/modules/header/index.tsx index 92b6bda3..af9a343d 100644 --- a/app/modules/header/index.tsx +++ b/app/modules/header/index.tsx @@ -8,7 +8,6 @@ import type { RootLoader } from "~/root"; import { CartLoading } from "../cart-loading"; import { Drawer, useDrawer } from "../drawer"; import { DesktopHeader } from "./desktop-header"; -import { MobileMenu } from "./menu/mobile-menu"; import { MobileHeader } from "./mobile-header"; import { ScrollingAnnouncement } from "./scrolling-announcement"; @@ -39,10 +38,7 @@ export function Header({ return ( <> - - {menu && ( - - )} + {/* */} ); } - -export function MenuDrawer({ - isOpen, - onClose, - menu, -}: { - isOpen: boolean; - onClose: () => void; - menu: EnhancedMenu; -}) { - return ( - - - - ); -} diff --git a/app/modules/header/menu/mobile-menu.tsx b/app/modules/header/menu/mobile-menu.tsx index 1d366ded..6e345afd 100644 --- a/app/modules/header/menu/mobile-menu.tsx +++ b/app/modules/header/menu/mobile-menu.tsx @@ -1,228 +1,99 @@ -import { - Disclosure, - DisclosureButton, - DisclosurePanel, -} from "@headlessui/react"; -import { Link } from "@remix-run/react"; -import { Image } from "@shopify/hydrogen"; -import clsx from "clsx"; -import { IconCaretDown, IconCaretRight } from "~/components/icons"; -import { getMaxDepth } from "~/lib/menu"; +import { CaretRight, List, X } from "@phosphor-icons/react"; +import * as Collapsible from "@radix-ui/react-collapsible"; +import * as Dialog from "@radix-ui/react-dialog"; +import { useRouteLoaderData } from "@remix-run/react"; +import { forwardRef } from "react"; +import Link from "~/components/link"; +import { cn } from "~/lib/cn"; import type { SingleMenuItem } from "~/lib/type"; import type { EnhancedMenu } from "~/lib/utils"; -import { Drawer, useDrawer } from "~/modules/drawer"; +import type { RootLoader } from "~/root"; -export function MobileMenu({ menu }: { menu: EnhancedMenu }) { - let items = menu.items as unknown as SingleMenuItem[]; - return ( - - ); -} +export function MobileMenu() { + let data = useRouteLoaderData("root"); + let menu = data?.layout?.headerMenu as EnhancedMenu; -function MultiMenu(props: SingleMenuItem) { - const { - isOpen: isMenuOpen, - openDrawer: openMenu, - closeDrawer: closeMenu, - } = useDrawer(); - let { title, to, items } = props; - let content = ( - -
- {items.map((item, id) => ( -
- - {({ open }) => ( - <> - -
- - {item.title} - - {item?.items?.length > 0 && ( - - {open ? ( - - ) : ( - - )} - - )} -
-
- {item?.items?.length > 0 ? ( -
- -
    - {item.items.map((subItem, ind) => ( -
  • - - {subItem.title} - -
  • - ))} -
-
-
- ) : null} - - )} -
-
- ))} -
-
- ); - return ( -
-
- - {title} - - -
- {content} -
- ); -} + if (!menu) return ; -function ImageMenu({ title, items, to }: SingleMenuItem) { - const { - isOpen: isMenuOpen, - openDrawer: openMenu, - closeDrawer: closeMenu, - } = useDrawer(); - let content = ( - -
- {items.map((item, id) => ( - -
- -
- {item.title} -
-
- - ))} -
-
- ); return ( -
-
+ - - {title} - - -
- {content} -
+ + + + + + + + +
Menu
+
+
+ {menu.items.map((item) => ( + + ))} +
+ + + ); } -function SingleMenu(props: SingleMenuItem) { - const { - isOpen: isMenuOpen, - openDrawer: openMenu, - closeDrawer: closeMenu, - } = useDrawer(); - let { title, items, to } = props; - let content = ( - -
-
    - {items.map((subItem, ind) => ( -
  • - - {subItem.title} - -
  • - ))} -
-
-
- ); - return ( -
-
- - {title} +function CollapsibleMenuItem({ item }: { item: SingleMenuItem }) { + let { title, to, items } = item; + + if (!items?.length) { + return ( + + + {title} - -
- {content} -
- ); -} + + ); + } -function ItemHeader({ title, to }: { title: string; to: string }) { return ( -
- - {title} - -
+ + + + + + {items.map((item) => ( + + ))} + + ); } + +let MenuTrigger = forwardRef( + (props, ref) => { + return ( + + ); + }, +); diff --git a/package-lock.json b/package-lock.json index dd32a2ac..e95444a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,8 @@ "@headlessui/react": "2.2.0", "@phosphor-icons/react": "2.1.7", "@radix-ui/react-checkbox": "1.1.2", - "@radix-ui/react-dialog": "1.1.2", + "@radix-ui/react-collapsible": "^1.1.1", + "@radix-ui/react-dialog": "^1.1.2", "@radix-ui/react-menubar": "^1.1.2", "@radix-ui/react-tooltip": "1.1.4", "@remix-run/react": "2.14.0", @@ -4423,6 +4424,51 @@ } } }, + "node_modules/@radix-ui/react-collapsible": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.1.tgz", + "integrity": "sha512-1///SnrfQHJEofLokyczERxQbWfCGQlQ2XsCZMucVs6it+lq9iw4vXy+uDn1edlb58cOZOWSldnfPAYcT4O/Yg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-presence": "1.1.1", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-context": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz", + "integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-collection": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.0.tgz", @@ -4481,6 +4527,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.2.tgz", "integrity": "sha512-Yj4dZtqa2o+kG61fzB0H2qUvmwBA2oyQroGLyNtBj1beo1khoQ3q1a2AO8rrQYjd8256CO9+N8L9tvsS+bnIyA==", + "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.0", "@radix-ui/react-compose-refs": "1.1.0", diff --git a/package.json b/package.json index dea82ba4..089a40af 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,8 @@ "@headlessui/react": "2.2.0", "@phosphor-icons/react": "2.1.7", "@radix-ui/react-checkbox": "1.1.2", - "@radix-ui/react-dialog": "1.1.2", + "@radix-ui/react-collapsible": "^1.1.1", + "@radix-ui/react-dialog": "^1.1.2", "@radix-ui/react-menubar": "^1.1.2", "@radix-ui/react-tooltip": "1.1.4", "@remix-run/react": "2.14.0", From abb4922b49da4db8df69e3fe93ea9cf6893f06eb Mon Sep 17 00:00:00 2001 From: hta218 Date: Thu, 28 Nov 2024 12:08:54 +0700 Subject: [PATCH 04/39] Add more tailwind config for animations --- tailwind.config.js | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/tailwind.config.js b/tailwind.config.js index a6ad4d99..49a38f9b 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -13,9 +13,17 @@ export default { extend: { keyframes: { "fade-in": { - from: { opacity: "0.5" }, + from: { opacity: "0" }, to: { opacity: "1" }, }, + "slide-down": { + from: { height: 0 }, + to: { height: "var(--slide-down-to)" }, + }, + "slide-up": { + from: { height: "var(--slide-up-from)" }, + to: { height: 0 }, + }, "slide-left-and-fade": { from: { opacity: 0, @@ -23,6 +31,14 @@ export default { }, to: { opacity: 1, transform: "translateX(0)" }, }, + "enter-from-left": { + from: { transform: "translateX(-100%)" }, + to: { transform: "translateX(0)" }, + }, + "enter-from-right": { + from: { transform: "translateX(100%)" }, + to: { transform: "translateX(0)" }, + }, marquee: { from: { transform: "translateZ(0)" }, to: { transform: "translate3d(-100%,0,0)" }, @@ -49,8 +65,15 @@ export default { marquee: "marquee var(--marquee-duration, 15s) linear infinite", underline: "underline 400ms linear", "fade-in": "fade-in var(--fade-in-duration, .5s) ease-in forwards", + "slide-down": + "slide-down var(--slide-down-duration, .3s) ease-out forwards", + "slide-up": "slide-up var(--slide-up-duration, .3s) ease-out forwards", "slide-left-and-fade": "slide-left-and-fade var(--slide-left-and-fade-duration, .5s) cubic-bezier(.165,.84,.44,1) forwards", + "enter-from-left": + "enter-from-left var(--enter-from-left-duration, .3s) ease-out forwards", + "enter-from-right": + "enter-from-right var(--enter-from-right-duration, .3s) ease-out forwards", }, borderWidth: { 6: "6px", From 5cad041fc3ac7d9d26bde95e15e7b2936dc802bb Mon Sep 17 00:00:00 2001 From: hta218 Date: Thu, 28 Nov 2024 12:09:17 +0700 Subject: [PATCH 05/39] Fix header z-indexes --- app/modules/header/mobile-header.tsx | 19 +++++++------------ app/modules/header/scrolling-announcement.tsx | 2 +- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/app/modules/header/mobile-header.tsx b/app/modules/header/mobile-header.tsx index fe00986c..7d07990a 100644 --- a/app/modules/header/mobile-header.tsx +++ b/app/modules/header/mobile-header.tsx @@ -8,12 +8,13 @@ import { import { useThemeSettings } from "@weaverse/hydrogen"; import { Suspense } from "react"; import useWindowScroll from "react-use/esm/useWindowScroll"; -import { IconList, IconMagnifyingGlass, IconUser } from "~/components/icons"; +import { IconMagnifyingGlass, IconUser } from "~/components/icons"; import { Logo } from "~/components/logo"; import { cn } from "~/lib/cn"; import { useIsHomePath } from "~/lib/utils"; import type { RootLoader } from "~/root"; import { CartCount } from "./cart-count"; +import { MobileMenu } from "./menu/mobile-menu"; export function MobileHeader({ shopName, @@ -35,29 +36,23 @@ export function MobileHeader({
- +
Date: Thu, 28 Nov 2024 16:13:04 +0700 Subject: [PATCH 06/39] Add scroll area component --- app/components/scroll-area.tsx | 79 ++++++++++++++++++++++++++++++++++ package-lock.json | 55 +++++++++++++++++++++++ package.json | 2 + 3 files changed, 136 insertions(+) create mode 100644 app/components/scroll-area.tsx diff --git a/app/components/scroll-area.tsx b/app/components/scroll-area.tsx new file mode 100644 index 00000000..73904b9c --- /dev/null +++ b/app/components/scroll-area.tsx @@ -0,0 +1,79 @@ +import type { ScrollAreaProps as RadixScrollAreaProps } from "@radix-ui/react-scroll-area"; +import { Root, Scrollbar, Thumb, Viewport } from "@radix-ui/react-scroll-area"; +import { type VariantProps, cva } from "class-variance-authority"; +import { forwardRef } from "react"; +import { cn } from "~/lib/cn"; + +let variants = cva("", { + variants: { + size: { + sm: "data-[orientation=horizontal]:h-1 data-[orientation=vertical]:w-1", + md: "data-[orientation=horizontal]:h-1.5 data-[orientation=vertical]:w-1.5", + lg: "data-[orientation=horizontal]:h-2 data-[orientation=vertical]:w-2", + xl: "data-[orientation=horizontal]:h-3 data-[orientation=vertical]:w-3", + }, + }, + defaultVariants: { + size: "md", + }, +}); + +interface ScrollAreaProps + extends RadixScrollAreaProps, + VariantProps { + style?: React.CSSProperties; + rootClassName?: string; + scrollbarClassName?: string; + thumbClassName?: string; +} + +export let ScrollArea = forwardRef( + ( + { + size, + rootClassName, + className, + scrollbarClassName, + thumbClassName, + children, + style, + ...rest + }, + ref, + ) => { + return ( + + + {children} + + + + + + ); + }, +); diff --git a/package-lock.json b/package-lock.json index e95444a8..b8dc3ef5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,9 @@ "@radix-ui/react-collapsible": "^1.1.1", "@radix-ui/react-dialog": "^1.1.2", "@radix-ui/react-menubar": "^1.1.2", + "@radix-ui/react-scroll-area": "^1.2.1", "@radix-ui/react-tooltip": "1.1.4", + "@radix-ui/react-visually-hidden": "^1.1.0", "@remix-run/react": "2.14.0", "@remix-run/server-runtime": "2.14.0", "@shopify/cli": "^3.70.0", @@ -4352,6 +4354,12 @@ "node": ">=18" } }, + "node_modules/@radix-ui/number": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.0.tgz", + "integrity": "sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==", + "license": "MIT" + }, "node_modules/@radix-ui/primitive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.0.tgz", @@ -4902,6 +4910,52 @@ } } }, + "node_modules/@radix-ui/react-scroll-area": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.1.tgz", + "integrity": "sha512-FnM1fHfCtEZ1JkyfH/1oMiTcFBQvHKl4vD9WnpwkLgtF+UmnXMCad6ECPTaAjcDjam+ndOEJWgHyKDGNteWSHw==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.0", + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-presence": "1.1.1", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-scroll-area/node_modules/@radix-ui/react-context": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz", + "integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz", @@ -5081,6 +5135,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.0.tgz", "integrity": "sha512-N8MDZqtgCgG5S3aV60INAB475osJousYpZ4cTJ2cFbMpdHS5Y6loLTH8LPtkj2QN0x93J30HT/M3qJXM0+lyeQ==", + "license": "MIT", "dependencies": { "@radix-ui/react-primitive": "2.0.0" }, diff --git a/package.json b/package.json index 089a40af..1ecbba6c 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,9 @@ "@radix-ui/react-collapsible": "^1.1.1", "@radix-ui/react-dialog": "^1.1.2", "@radix-ui/react-menubar": "^1.1.2", + "@radix-ui/react-scroll-area": "^1.2.1", "@radix-ui/react-tooltip": "1.1.4", + "@radix-ui/react-visually-hidden": "^1.1.0", "@remix-run/react": "2.14.0", "@remix-run/server-runtime": "2.14.0", "@shopify/cli": "^3.70.0", From b18fda0197fe1d551d3bc8373ae19fc359235153 Mon Sep 17 00:00:00 2001 From: hta218 Date: Thu, 28 Nov 2024 16:19:02 +0700 Subject: [PATCH 07/39] Allowing mobile menu to scroll --- app/modules/header/menu/mobile-menu.tsx | 30 ++++++++++++++++--------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/app/modules/header/menu/mobile-menu.tsx b/app/modules/header/menu/mobile-menu.tsx index 6e345afd..4b9b06ab 100644 --- a/app/modules/header/menu/mobile-menu.tsx +++ b/app/modules/header/menu/mobile-menu.tsx @@ -1,9 +1,11 @@ import { CaretRight, List, X } from "@phosphor-icons/react"; import * as Collapsible from "@radix-ui/react-collapsible"; import * as Dialog from "@radix-ui/react-dialog"; +import * as VisuallyHidden from "@radix-ui/react-visually-hidden"; import { useRouteLoaderData } from "@remix-run/react"; import { forwardRef } from "react"; import Link from "~/components/link"; +import { ScrollArea } from "~/components/scroll-area"; import { cn } from "~/lib/cn"; import type { SingleMenuItem } from "~/lib/type"; import type { EnhancedMenu } from "~/lib/utils"; @@ -30,7 +32,7 @@ export function MobileMenu() { /> + + Mobile menu + -
Menu
-
-
- {menu.items.map((item) => ( - - ))} +
Menu
+
+
+ +
+ {menu.items.map((item) => ( + + ))} +
+
From d827238c88554f306548e61db559753c21c4b593 Mon Sep 17 00:00:00 2001 From: hta218 Date: Thu, 28 Nov 2024 22:00:49 +0700 Subject: [PATCH 08/39] Simplify layout by getting menu from `useShopMenu` hook --- app/components/logo.tsx | 10 ++++------ app/hooks/use-shop-menu.tsx | 15 +++++++++++++++ app/modules/footer.tsx | 14 +++++--------- app/modules/header/index.tsx | 22 +++------------------- app/modules/header/menu/desktop-menu.tsx | 10 ++++++---- app/modules/header/menu/mobile-menu.tsx | 11 ++++------- app/modules/header/mobile-header.tsx | 6 +----- app/modules/layout.tsx | 23 ++++++----------------- app/root.tsx | 5 +---- 9 files changed, 45 insertions(+), 71 deletions(-) create mode 100644 app/hooks/use-shop-menu.tsx diff --git a/app/components/logo.tsx b/app/components/logo.tsx index 09bd3f3f..a36239f8 100644 --- a/app/components/logo.tsx +++ b/app/components/logo.tsx @@ -2,13 +2,11 @@ import { Image } from "@shopify/hydrogen"; import { useThemeSettings } from "@weaverse/hydrogen"; import clsx from "clsx"; import { Link } from "~/components/link"; +import { useShopMenu } from "~/hooks/use-shop-menu"; -export function Logo({ - isTransparent, - shopName, -}: { isTransparent?: boolean; shopName: string }) { - let settings = useThemeSettings(); - let { logoData, transparentLogoData, logoWidth } = settings; +export function Logo({ isTransparent }: { isTransparent?: boolean }) { + let { shopName } = useShopMenu(); + let { logoData, transparentLogoData, logoWidth } = useThemeSettings(); return ( ("root"); + let shopName = layout?.shop?.name; + let headerMenu = layout?.headerMenu as EnhancedMenu; + let footerMenu = layout?.footerMenu as EnhancedMenu; + return { + shopName, + headerMenu, + footerMenu, + }; +} diff --git a/app/modules/footer.tsx b/app/modules/footer.tsx index aced7097..4a5f8a70 100644 --- a/app/modules/footer.tsx +++ b/app/modules/footer.tsx @@ -14,8 +14,9 @@ import { IconLinkedinLogo, IconXLogo, } from "~/components/icons"; +import { useShopMenu } from "~/hooks/use-shop-menu"; import { cn } from "~/lib/cn"; -import type { ChildEnhancedMenuItem, EnhancedMenu } from "~/lib/utils"; +import type { ChildEnhancedMenuItem } from "~/lib/utils"; import { Input } from "~/modules/input"; import { CountrySelector } from "./country-selector"; @@ -34,13 +35,8 @@ let variants = cva("divide-y divide-line-subtle space-y-9", { }, }); -export function Footer({ - menu, - shopName, -}: { - menu?: EnhancedMenu; - shopName: string; -}) { +export function Footer() { + let { footerMenu, shopName } = useShopMenu(); let { footerWidth, socialFacebook, @@ -60,7 +56,7 @@ export function Footer({ newsletterButtonText, } = useThemeSettings(); - let { items = [] } = menu || {}; + let { items = [] } = footerMenu || {}; let socialItems = [ { name: "Instagram", diff --git a/app/modules/header/index.tsx b/app/modules/header/index.tsx index af9a343d..41246e15 100644 --- a/app/modules/header/index.tsx +++ b/app/modules/header/index.tsx @@ -2,7 +2,6 @@ import { Await, useRouteLoaderData } from "@remix-run/react"; import { CartForm, type CartReturn } from "@shopify/hydrogen"; import { Suspense, useEffect } from "react"; import { useCartFetchers } from "~/hooks/use-cart-fetchers"; -import type { EnhancedMenu } from "~/lib/utils"; import { Cart } from "~/modules/cart"; import type { RootLoader } from "~/root"; import { CartLoading } from "../cart-loading"; @@ -11,23 +10,12 @@ import { DesktopHeader } from "./desktop-header"; import { MobileHeader } from "./mobile-header"; import { ScrollingAnnouncement } from "./scrolling-announcement"; -export function Header({ - shopName, - menu, -}: { - shopName: string; - menu?: EnhancedMenu; -}) { +export function Header() { let { isOpen: isCartOpen, openDrawer: openCart, closeDrawer: closeCart, } = useDrawer(); - let { - isOpen: isMenuOpen, - openDrawer: openMenu, - closeDrawer: closeMenu, - } = useDrawer(); let addToCartFetchers = useCartFetchers(CartForm.ACTIONS.LinesAdd); // toggle cart drawer when adding to cart @@ -40,12 +28,8 @@ export function Header({ <> {/* */} - - + + ); } diff --git a/app/modules/header/menu/desktop-menu.tsx b/app/modules/header/menu/desktop-menu.tsx index 0fa761d3..815ca665 100644 --- a/app/modules/header/menu/desktop-menu.tsx +++ b/app/modules/header/menu/desktop-menu.tsx @@ -5,16 +5,18 @@ import { useThemeSettings } from "@weaverse/hydrogen"; import clsx from "clsx"; import { useState } from "react"; import Link from "~/components/link"; +import { useShopMenu } from "~/hooks/use-shop-menu"; import { cn } from "~/lib/cn"; import { getMaxDepth } from "~/lib/menu"; import type { SingleMenuItem } from "~/lib/type"; -import type { EnhancedMenu } from "~/lib/utils"; -export function DesktopMenu({ menu }: { menu: EnhancedMenu }) { +export function DesktopMenu() { + let { headerMenu } = useShopMenu(); let { openMenuBy } = useThemeSettings(); let [value, setValue] = useState(null); - if (menu?.items?.length) { - let items = menu.items as unknown as SingleMenuItem[]; + + if (headerMenu?.items?.length) { + let items = headerMenu.items as unknown as SingleMenuItem[]; return ( ("root"); - let menu = data?.layout?.headerMenu as EnhancedMenu; + let { headerMenu } = useShopMenu(); - if (!menu) return ; + if (!headerMenu) return ; return ( @@ -53,7 +50,7 @@ export function MobileMenu() {
- {menu.items.map((item) => ( + {headerMenu.items.map((item) => ( void; - openMenu: () => void; }) { let isHome = useIsHomePath(); let { enableTransparentHeader } = useThemeSettings(); @@ -66,7 +62,7 @@ export function MobileHeader({
- +
@@ -21,16 +14,12 @@ export function Layout({ children, layout }: LayoutProps) { Skip to content
- {headerMenu && layout?.shop.name && ( -
- )} +
{children}
- {footerMenu && layout?.shop.name && ( -
- )} +
); } diff --git a/app/root.tsx b/app/root.tsx index d71ae34e..e7005b33 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -190,10 +190,7 @@ function Layout({ children }: { children?: React.ReactNode }) { consent={data.consent} > - + {children} From 9ec48830ba706933e970ab98fd6779947e57bf0f Mon Sep 17 00:00:00 2001 From: hta218 Date: Thu, 28 Nov 2024 22:01:38 +0700 Subject: [PATCH 09/39] Update default theme settings & cleaning up --- app/modules/product-form/variants.tsx | 1 - app/weaverse/schema.server.ts | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/modules/product-form/variants.tsx b/app/modules/product-form/variants.tsx index fe0080b7..6c2dfb2a 100644 --- a/app/modules/product-form/variants.tsx +++ b/app/modules/product-form/variants.tsx @@ -75,7 +75,6 @@ export function ProductVariants(props: ProductVariantsProps) { options={options.filter((option) => option.optionValues.length > 1)} > {({ option }) => { - console.log("option", option); let optionName = option.name; let clonedSelectedOptionMap = new Map(selectedOptionMap); let values = option.values diff --git a/app/weaverse/schema.server.ts b/app/weaverse/schema.server.ts index 349648d4..7fee7da0 100644 --- a/app/weaverse/schema.server.ts +++ b/app/weaverse/schema.server.ts @@ -298,7 +298,7 @@ export let themeSchema: HydrogenThemeSchema = { type: "color", label: "Lines and borders", name: "colorLine", - defaultValue: "#a8a29e", + defaultValue: "#3B352C", }, { type: "heading", @@ -534,7 +534,7 @@ export let themeSchema: HydrogenThemeSchema = { step: 1, unit: "px", }, - defaultValue: 16, + defaultValue: 14, }, { type: "range", From b17e68278a50bfd2d1da2c56fe600732d3b6a3a8 Mon Sep 17 00:00:00 2001 From: hta218 Date: Thu, 28 Nov 2024 22:50:02 +0700 Subject: [PATCH 10/39] Update predictive search and enhance header structure --- .../header/cart-count.tsx | 0 .../header/desktop-header.tsx | 73 ++++-------------- app/{modules => components}/header/index.tsx | 6 +- .../header/menu/desktop-menu.tsx | 0 .../header/menu/mobile-menu.tsx | 2 +- .../header/mobile-header.tsx | 0 .../header/predictive-search/index.tsx | 74 +++++++++++++++++++ .../predictive-search-result.tsx | 2 +- .../predictive-search-results.tsx | 4 +- .../header}/predictive-search/result-item.tsx | 2 +- .../header}/predictive-search/search-form.tsx | 2 +- .../header/scrolling-announcement.tsx | 0 app/modules/layout.tsx | 2 +- app/modules/predictive-search/index.tsx | 33 --------- 14 files changed, 97 insertions(+), 103 deletions(-) rename app/{modules => components}/header/cart-count.tsx (100%) rename app/{modules => components}/header/desktop-header.tsx (64%) rename app/{modules => components}/header/index.tsx (92%) rename app/{modules => components}/header/menu/desktop-menu.tsx (100%) rename app/{modules => components}/header/menu/mobile-menu.tsx (97%) rename app/{modules => components}/header/mobile-header.tsx (100%) create mode 100644 app/components/header/predictive-search/index.tsx rename app/{modules => components/header}/predictive-search/predictive-search-result.tsx (97%) rename app/{modules => components/header}/predictive-search/predictive-search-results.tsx (94%) rename app/{modules => components/header}/predictive-search/result-item.tsx (96%) rename app/{modules => components/header}/predictive-search/search-form.tsx (97%) rename app/{modules => components}/header/scrolling-announcement.tsx (100%) delete mode 100644 app/modules/predictive-search/index.tsx diff --git a/app/modules/header/cart-count.tsx b/app/components/header/cart-count.tsx similarity index 100% rename from app/modules/header/cart-count.tsx rename to app/components/header/cart-count.tsx diff --git a/app/modules/header/desktop-header.tsx b/app/components/header/desktop-header.tsx similarity index 64% rename from app/modules/header/desktop-header.tsx rename to app/components/header/desktop-header.tsx index 2fbe4715..d8da64b9 100644 --- a/app/modules/header/desktop-header.tsx +++ b/app/components/header/desktop-header.tsx @@ -1,23 +1,21 @@ import { Await, Link, - useLocation, useRouteError, useRouteLoaderData, } from "@remix-run/react"; import { useThemeSettings } from "@weaverse/hydrogen"; import { cva } from "class-variance-authority"; -import { Suspense, useEffect } from "react"; +import { Suspense } from "react"; import useWindowScroll from "react-use/esm/useWindowScroll"; -import { IconMagnifyingGlass, IconUser } from "~/components/icons"; +import { DialogDemo } from "~/components/drawer"; +import { IconUser } from "~/components/icons"; import { Logo } from "~/components/logo"; import { cn } from "~/lib/cn"; -import { type EnhancedMenu, useIsHomePath } from "~/lib/utils"; -import { PredictiveSearch } from "~/modules/predictive-search"; +import { useIsHomePath } from "~/lib/utils"; import type { RootLoader } from "~/root"; -import { Drawer, useDrawer } from "../drawer"; -import { CartCount } from "./cart-count"; import { DesktopMenu } from "./menu/desktop-menu"; +import { PredictiveSearchButton } from "./predictive-search"; let variants = cva("", { variants: { @@ -34,19 +32,10 @@ let variants = cva("", { }, }); -export function DesktopHeader({ - menu, - openCart, - shopName, -}: { - openCart: () => void; - menu?: EnhancedMenu; - shopName: string; -}) { +export function DesktopHeader() { let { enableTransparentHeader, headerWidth } = useThemeSettings(); let isHome = useIsHomePath(); let { y } = useWindowScroll(); - let { isOpen, openDrawer, closeDrawer } = useDrawer(); let routeError = useRouteError(); let scrolled = y >= 50; @@ -57,7 +46,7 @@ export function DesktopHeader({
- - + +
- + - + {/* + /> */}
@@ -121,36 +107,3 @@ function AccountLink({ className }: { className?: string }) { ); } - -function SearchToggle({ - isOpen, - openDrawer, - closeDrawer, -}: { - isOpen: boolean; - openDrawer: () => void; - closeDrawer: () => void; -}) { - let { pathname } = useLocation(); - // biome-ignore lint/correctness/useExhaustiveDependencies: - useEffect(() => { - if (isOpen) { - closeDrawer(); - } - }, [pathname]); - - return ( - <> - - - - - - ); -} diff --git a/app/modules/header/index.tsx b/app/components/header/index.tsx similarity index 92% rename from app/modules/header/index.tsx rename to app/components/header/index.tsx index 41246e15..33f5e605 100644 --- a/app/modules/header/index.tsx +++ b/app/components/header/index.tsx @@ -4,8 +4,8 @@ import { Suspense, useEffect } from "react"; import { useCartFetchers } from "~/hooks/use-cart-fetchers"; import { Cart } from "~/modules/cart"; import type { RootLoader } from "~/root"; -import { CartLoading } from "../cart-loading"; -import { Drawer, useDrawer } from "../drawer"; +import { CartLoading } from "../../modules/cart-loading"; +import { Drawer, useDrawer } from "../../modules/drawer"; import { DesktopHeader } from "./desktop-header"; import { MobileHeader } from "./mobile-header"; import { ScrollingAnnouncement } from "./scrolling-announcement"; @@ -28,7 +28,7 @@ export function Header() { <> {/* */} - + ); diff --git a/app/modules/header/menu/desktop-menu.tsx b/app/components/header/menu/desktop-menu.tsx similarity index 100% rename from app/modules/header/menu/desktop-menu.tsx rename to app/components/header/menu/desktop-menu.tsx diff --git a/app/modules/header/menu/mobile-menu.tsx b/app/components/header/menu/mobile-menu.tsx similarity index 97% rename from app/modules/header/menu/mobile-menu.tsx rename to app/components/header/menu/mobile-menu.tsx index f7931706..2c846322 100644 --- a/app/modules/header/menu/mobile-menu.tsx +++ b/app/components/header/menu/mobile-menu.tsx @@ -29,7 +29,7 @@ export function MobileMenu() { /> + + + + + + + + Predictive search + + + + + + ); +} + +function PredictiveSearch() { + return ( +
+ + {({ fetchResults, inputRef }) => ( +
+ + } + autoFocus={true} + /> +
+ )} +
+ +
+ ); +} diff --git a/app/modules/predictive-search/predictive-search-result.tsx b/app/components/header/predictive-search/predictive-search-result.tsx similarity index 97% rename from app/modules/predictive-search/predictive-search-result.tsx rename to app/components/header/predictive-search/predictive-search-result.tsx index a2063839..067f8095 100644 --- a/app/modules/predictive-search/predictive-search-result.tsx +++ b/app/components/header/predictive-search/predictive-search-result.tsx @@ -5,7 +5,7 @@ import type { NormalizedPredictiveSearchResultItem, NormalizedPredictiveSearchResults, SearchResultTypeProps, -} from "../../types/predictive-search"; +} from "../../../types/predictive-search"; export function PredictiveSearchResult({ goToSearchResult, diff --git a/app/modules/predictive-search/predictive-search-results.tsx b/app/components/header/predictive-search/predictive-search-results.tsx similarity index 94% rename from app/modules/predictive-search/predictive-search-results.tsx rename to app/components/header/predictive-search/predictive-search-results.tsx index e7142b6e..818e3f78 100644 --- a/app/modules/predictive-search/predictive-search-results.tsx +++ b/app/components/header/predictive-search/predictive-search-results.tsx @@ -28,14 +28,14 @@ export function PredictiveSearchResults() { if (!totalResults) { return ( -
+
); } return (
-
+
- - {({ fetchResults, inputRef }) => ( -
- - } - autoFocus={true} - /> -
- )} -
- {isOpen && } -
- ); -} From 26b17cf4b31a75608e6ad32c03664d5a0b029b78 Mon Sep 17 00:00:00 2001 From: hta218 Date: Thu, 28 Nov 2024 22:52:47 +0700 Subject: [PATCH 11/39] Using radix dialog for predictive search popup --- app/components/drawer.tsx | 39 +++++++++++++++++++ .../predictive-search-result.tsx | 4 +- .../predictive-search-results.tsx | 8 ---- .../header/predictive-search/search-form.tsx | 14 +++---- tailwind.config.js | 6 +++ 5 files changed, 54 insertions(+), 17 deletions(-) create mode 100644 app/components/drawer.tsx diff --git a/app/components/drawer.tsx b/app/components/drawer.tsx new file mode 100644 index 00000000..22961637 --- /dev/null +++ b/app/components/drawer.tsx @@ -0,0 +1,39 @@ +import * as Dialog from "@radix-ui/react-dialog"; +import { cn } from "~/lib/cn"; + +export function DialogDemo({ + width = "400px", + openFrom, +}: { width?: string; openFrom: "left" | "right" }) { + return ( + + + + + + + + content goes here + + + + ); +} diff --git a/app/components/header/predictive-search/predictive-search-result.tsx b/app/components/header/predictive-search/predictive-search-result.tsx index 067f8095..ec574dd4 100644 --- a/app/components/header/predictive-search/predictive-search-result.tsx +++ b/app/components/header/predictive-search/predictive-search-result.tsx @@ -1,11 +1,11 @@ import { Link } from "@remix-run/react"; import clsx from "clsx"; -import { SearchResultItem } from "./result-item"; import type { NormalizedPredictiveSearchResultItem, NormalizedPredictiveSearchResults, SearchResultTypeProps, -} from "../../../types/predictive-search"; +} from "~/types/predictive-search"; +import { SearchResultItem } from "./result-item"; export function PredictiveSearchResult({ goToSearchResult, diff --git a/app/components/header/predictive-search/predictive-search-results.tsx b/app/components/header/predictive-search/predictive-search-results.tsx index 818e3f78..ebae9eb4 100644 --- a/app/components/header/predictive-search/predictive-search-results.tsx +++ b/app/components/header/predictive-search/predictive-search-results.tsx @@ -72,14 +72,6 @@ export function PredictiveSearchResults() { View all products - {/* - View all products - - */}
)}
diff --git a/app/components/header/predictive-search/search-form.tsx b/app/components/header/predictive-search/search-form.tsx index 4c6e71f4..13574dfc 100644 --- a/app/components/header/predictive-search/search-form.tsx +++ b/app/components/header/predictive-search/search-form.tsx @@ -3,7 +3,7 @@ import { useEffect, useRef } from "react"; import type { NormalizedPredictiveSearchResults, SearchFromProps, -} from "../../../types/predictive-search"; +} from "~/types/predictive-search"; /** * Search form component that posts search requests to the `/search` route @@ -15,16 +15,16 @@ export function PredictiveSearchForm({ method = "POST", ...props }: SearchFromProps) { - const params = useParams(); - const fetcher = useFetcher(); - const inputRef = useRef(null); + let params = useParams(); + let fetcher = useFetcher(); + let inputRef = useRef(null); function fetchResults(event: React.ChangeEvent) { - const searchAction = action ?? "/api/predictive-search"; - const localizedAction = params.locale + let searchAction = action ?? "/api/predictive-search"; + let localizedAction = params.locale ? `/${params.locale}${searchAction}` : searchAction; - const newSearchTerm = event.target.value || ""; + let newSearchTerm = event.target.value || ""; fetcher.submit( { q: newSearchTerm, limit: "6" }, { method, action: localizedAction }, diff --git a/tailwind.config.js b/tailwind.config.js index 49a38f9b..a0cefe1e 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -39,6 +39,10 @@ export default { from: { transform: "translateX(100%)" }, to: { transform: "translateX(0)" }, }, + "enter-from-top": { + from: { transform: "translateY(-100%)" }, + to: { transform: "translateY(0)" }, + }, marquee: { from: { transform: "translateZ(0)" }, to: { transform: "translate3d(-100%,0,0)" }, @@ -74,6 +78,8 @@ export default { "enter-from-left var(--enter-from-left-duration, .3s) ease-out forwards", "enter-from-right": "enter-from-right var(--enter-from-right-duration, .3s) ease-out forwards", + "enter-from-top": + "enter-from-top var(--enter-from-top-duration, .3s) ease-out forwards", }, borderWidth: { 6: "6px", From 266a50f411898063532615c0aaa565b76d77643b Mon Sep 17 00:00:00 2001 From: hta218 Date: Sat, 30 Nov 2024 09:55:51 +0700 Subject: [PATCH 12/39] Remove outline of search toggle button on focus --- app/components/header/predictive-search/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/header/predictive-search/index.tsx b/app/components/header/predictive-search/index.tsx index f6e275d9..21082faa 100644 --- a/app/components/header/predictive-search/index.tsx +++ b/app/components/header/predictive-search/index.tsx @@ -12,7 +12,7 @@ export function PredictiveSearchButton() { + + + +
+ ); +} + +function LayoutList(props: IconProps) { + return ( + + + + + + ); +} + +function TwoColumns(props: IconProps) { + return ( + + + + + ); +} + +function ThreeColumns(props: IconProps) { + return ( + + + + + + ); +} + +function FourColumns(props: IconProps) { + return ( + + + + + + + ); +} From 68fa3ee5b2c28cfa049bf5bea32e43e4b81542e9 Mon Sep 17 00:00:00 2001 From: hta218 Date: Sun, 1 Dec 2024 07:09:10 +0700 Subject: [PATCH 16/39] Restructuring collection filters --- .../collection-filters}/drawer-filter.tsx | 150 ++---------------- app/sections/collection-filters/index.tsx | 3 +- 2 files changed, 15 insertions(+), 138 deletions(-) rename app/{modules => sections/collection-filters}/drawer-filter.tsx (64%) diff --git a/app/modules/drawer-filter.tsx b/app/sections/collection-filters/drawer-filter.tsx similarity index 64% rename from app/modules/drawer-filter.tsx rename to app/sections/collection-filters/drawer-filter.tsx index 395789b4..1ad4593b 100644 --- a/app/modules/drawer-filter.tsx +++ b/app/sections/collection-filters/drawer-filter.tsx @@ -2,34 +2,25 @@ import { Disclosure, DisclosureButton, DisclosurePanel, - Menu, - MenuButton, - MenuItem, - MenuItems, } from "@headlessui/react"; -import { CaretDown, Sliders } from "@phosphor-icons/react"; -import { - Link, - useLocation, - useNavigate, - useSearchParams, -} from "@remix-run/react"; +import { Sliders } from "@phosphor-icons/react"; +import { useLocation, useNavigate, useSearchParams } from "@remix-run/react"; import type { Filter, ProductFilter, } from "@shopify/hydrogen/storefront-api-types"; -import clsx from "clsx"; import type { SyntheticEvent } from "react"; import { useState } from "react"; import { Button } from "~/components/button"; import { Checkbox } from "~/components/checkbox"; import { IconCaretDown, IconCaretRight } from "~/components/icons"; import { FILTER_URL_PREFIX } from "~/lib/const"; -import type { AppliedFilter, SortParam } from "~/lib/filter"; -import { getAppliedFilterLink, getFilterLink, getSortLink } from "~/lib/filter"; -import { Drawer, useDrawer } from "./drawer"; -import { IconFourGrid, IconOneGrid, IconThreeGrid, IconTwoGrid } from "./icon"; -import { Input } from "./input"; +import type { AppliedFilter } from "~/lib/filter"; +import { getAppliedFilterLink, getFilterLink } from "~/lib/filter"; +import { Drawer, useDrawer } from "../../modules/drawer"; +import { Input } from "../../modules/input"; +import { LayoutSwitcher } from "./layout-switcher"; +import { Sort } from "./sort"; type DrawerFilterProps = { productNumber?: number; @@ -53,51 +44,14 @@ export function DrawerFilter({ return (
-
- - - - -
+ {productNumber} Products
- + - - - + {[1, 2, 3, 4, 5].map((col) => { + let Icon = LAYOUT_ICONS[col]; + return ( + + ); + })}
); } @@ -59,14 +53,14 @@ export function LayoutSwitcher({ function LayoutList(props: IconProps) { return ( - - - + ); } @@ -74,13 +68,15 @@ function LayoutList(props: IconProps) { function TwoColumns(props: IconProps) { return ( - - + + ); } @@ -88,14 +84,16 @@ function TwoColumns(props: IconProps) { function ThreeColumns(props: IconProps) { return ( - - - + + + ); } @@ -103,18 +101,36 @@ function ThreeColumns(props: IconProps) { function FourColumns(props: IconProps) { return ( + + + + + + ); +} + +function FiveColumns(props: IconProps) { + return ( + - - - - + + + + + ); } From bb36a0c838d5f856f2e45b26f269614127e5d524 Mon Sep 17 00:00:00 2001 From: hta218 Date: Thu, 5 Dec 2024 16:30:56 +0700 Subject: [PATCH 19/39] Add filters component for product collections with price range functionality --- app/sections/collection-filters/filters.tsx | 238 ++++++++++++++++++++ 1 file changed, 238 insertions(+) create mode 100644 app/sections/collection-filters/filters.tsx diff --git a/app/sections/collection-filters/filters.tsx b/app/sections/collection-filters/filters.tsx new file mode 100644 index 00000000..c8dd9400 --- /dev/null +++ b/app/sections/collection-filters/filters.tsx @@ -0,0 +1,238 @@ +import { + Disclosure, + DisclosureButton, + DisclosurePanel, +} from "@headlessui/react"; +import { Sliders } from "@phosphor-icons/react"; +import * as Dialog from "@radix-ui/react-dialog"; +import { + useLoaderData, + useLocation, + useNavigate, + useSearchParams, +} from "@remix-run/react"; +import type { + Filter, + ProductFilter, +} from "@shopify/hydrogen/storefront-api-types"; +import clsx from "clsx"; +import type { SyntheticEvent } from "react"; +import { useState } from "react"; +import type { CollectionDetailsQuery } from "storefrontapi.generated"; +import { Button } from "~/components/button"; +import { Checkbox } from "~/components/checkbox"; +import { IconCaretDown, IconCaretRight } from "~/components/icons"; +import { FILTER_URL_PREFIX } from "~/lib/const"; +import type { AppliedFilter } from "~/lib/filter"; +import { getAppliedFilterLink, getFilterLink } from "~/lib/filter"; +import { Input } from "../../modules/input"; + +export function Filters() { + let { collection, appliedFilters } = useLoaderData< + CollectionDetailsQuery & { + collections: Array<{ handle: string; title: string }>; + appliedFilters: AppliedFilter[]; + } + >(); + + return ( + + + + + + + + + + + + ); +} + +function ListItemFilter({ + option, + appliedFilters, +}: { + option: Filter["values"][0]; + appliedFilters: AppliedFilter[]; +}) { + let navigate = useNavigate(); + let [params] = useSearchParams(); + let location = useLocation(); + let filter = appliedFilters.find( + (filter) => JSON.stringify(filter.filter) === option.input, + ); + let [checked, setChecked] = useState(!!filter); + + let handleCheckedChange = (checked: boolean) => { + setChecked(checked); + if (checked) { + const link = getFilterLink(option.input as string, params, location); + navigate(link); + } else if (filter) { + let link = getAppliedFilterLink(filter, params, location); + navigate(link); + } + }; + return ( +
+ +
+ ); +} + +export function FiltersList({ filters = [], appliedFilters = [] }) { + let [params] = useSearchParams(); + let filterMarkup = (filter: Filter, option: Filter["values"][0]) => { + switch (filter.type) { + case "PRICE_RANGE": { + let priceFilter = params.get(`${FILTER_URL_PREFIX}price`); + let price = priceFilter + ? (JSON.parse(priceFilter) as ProductFilter["price"]) + : undefined; + let min = Number.isNaN(Number(price?.min)) + ? undefined + : Number(price?.min); + let max = Number.isNaN(Number(price?.max)) + ? undefined + : Number(price?.max); + return ; + } + + default: + return ( + + ); + } + }; + + return ( +
+ {filters.map((filter: Filter) => ( + + {({ open }) => ( + <> + + {filter.label} + {open ? ( + + ) : ( + + )} + + +
    + {filter.values?.map((option) => { + return ( +
  • {filterMarkup(filter, option)}
  • + ); + })} +
+
+ + )} +
+ ))} +
+ ); +} + +// const PRICE_RANGE_FILTER_DEBOUNCE = 500; + +function PriceRangeFilter({ max, min }: { max?: number; min?: number }) { + // const location = useLocation(); + // const params = useMemo( + // () => new URLSearchParams(location.search), + // [location.search], + // ); + // const navigate = useNavigate(); + + const [minPrice, setMinPrice] = useState(min); + const [maxPrice, setMaxPrice] = useState(max); + + // useDebounce( + // () => { + // if (minPrice === undefined && maxPrice === undefined) { + // params.delete(`${FILTER_URL_PREFIX}price`); + // navigate(`${location.pathname}?${params.toString()}`); + // return; + // } + + // const price = { + // ...(minPrice === undefined ? {} : {min: minPrice}), + // ...(maxPrice === undefined ? {} : {max: maxPrice}), + // }; + // const newParams = filterInputToParams({price}, params); + // navigate(`${location.pathname}?${newParams.toString()}`); + // }, + // PRICE_RANGE_FILTER_DEBOUNCE, + // [minPrice, maxPrice], + // ); + + const onChangeMax = (event: SyntheticEvent) => { + const value = (event.target as HTMLInputElement).value; + const newMaxPrice = Number.isNaN(Number.parseFloat(value)) + ? undefined + : Number.parseFloat(value); + setMaxPrice(newMaxPrice); + }; + + const onChangeMin = (event: SyntheticEvent) => { + const value = (event.target as HTMLInputElement).value; + const newMinPrice = Number.isNaN(Number.parseFloat(value)) + ? undefined + : Number.parseFloat(value); + setMinPrice(newMinPrice); + }; + + return ( +
+ + +
+ ); +} From d5e72c1684a2de58a6ce9d3db45b8321fef3c89d Mon Sep 17 00:00:00 2001 From: hta218 Date: Thu, 5 Dec 2024 16:31:09 +0700 Subject: [PATCH 20/39] Add collection toolbars --- app/sections/collection-filters/tools-bar.tsx | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 app/sections/collection-filters/tools-bar.tsx diff --git a/app/sections/collection-filters/tools-bar.tsx b/app/sections/collection-filters/tools-bar.tsx new file mode 100644 index 00000000..d1eba741 --- /dev/null +++ b/app/sections/collection-filters/tools-bar.tsx @@ -0,0 +1,53 @@ +import { type VariantProps, cva } from "class-variance-authority"; +import { Filters } from "./filters"; +import { LayoutSwitcher } from "./layout-switcher"; +import { Sort } from "./sort"; +import { cn } from "~/lib/cn"; + +let variants = cva("", { + variants: { + width: { + full: "", + stretch: "-mx-3 px-3 md:-mx-10 md:px-10 lg:-mx-16 lg:px-16", + fixed: [ + "-mx-3 px-3 md:-mx-10 md:px-10", + "lg:-mx-[max(calc((100vw-var(--page-width))/2),1.5rem)]", + "lg:px-[max(calc((100vw-var(--page-width))/2),1.5rem)]", + ], + }, + }, +}); + +interface ToolsBarProps extends VariantProps { + productsCount?: number; + showSearchSort?: boolean; + numberInRow?: number; + onLayoutChange: (number: number) => void; +} + +export function ToolsBar({ + width, + numberInRow, + productsCount = 0, + showSearchSort = false, + onLayoutChange, +}: ToolsBarProps) { + return ( +
+
+ + {productsCount} Products +
+ + +
+
+
+ ); +} From 90401b6126bed91bf6ba59f08842709f945ccc35 Mon Sep 17 00:00:00 2001 From: hta218 Date: Thu, 5 Dec 2024 16:31:31 +0700 Subject: [PATCH 21/39] Add config for border subtle --- app/weaverse/schema.server.ts | 8 +++++++- app/weaverse/style.tsx | 4 ++-- package-lock.json | 7 ------- package.json | 1 - 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/app/weaverse/schema.server.ts b/app/weaverse/schema.server.ts index 7fee7da0..a44887d0 100644 --- a/app/weaverse/schema.server.ts +++ b/app/weaverse/schema.server.ts @@ -296,10 +296,16 @@ export let themeSchema: HydrogenThemeSchema = { }, { type: "color", - label: "Lines and borders", + label: "Borders", name: "colorLine", defaultValue: "#3B352C", }, + { + type: "color", + label: "Borders (subtle)", + name: "colorLineSubtle", + defaultValue: "#A19B91", + }, { type: "heading", label: "Announcement bar", diff --git a/app/weaverse/style.tsx b/app/weaverse/style.tsx index 0a157cab..e438f10a 100644 --- a/app/weaverse/style.tsx +++ b/app/weaverse/style.tsx @@ -1,5 +1,4 @@ import { useThemeSettings } from "@weaverse/hydrogen"; -import { colord } from "colord"; export function GlobalStyle() { let settings = useThemeSettings(); @@ -10,6 +9,7 @@ export function GlobalStyle() { colorTextSubtle, colorTextInverse, colorLine, + colorLineSubtle, topbarTextColor, topbarBgColor, headerBgColor, @@ -58,7 +58,7 @@ export function GlobalStyle() { --color-text-subtle: ${colorTextSubtle}; --color-text-inverse: ${colorTextInverse}; --color-line: ${colorLine}; - --color-line-subtle: ${colord(colorLine).lighten(0.3).toHex()}; + --color-line-subtle: ${colorLineSubtle}; /* Colors (header & footer) */ --color-topbar-text: ${topbarTextColor}; diff --git a/package-lock.json b/package-lock.json index b8dc3ef5..2e7b9b1e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,7 +27,6 @@ "@weaverse/hydrogen": "4.0.0", "class-variance-authority": "0.7.0", "clsx": "2.1.1", - "colord": "^2.9.3", "cross-env": "7.0.3", "framer-motion": "^11.11.0", "graphql": "16.9.0", @@ -7669,12 +7668,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "node_modules/colord": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", - "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", - "license": "MIT" - }, "node_modules/colorette": { "version": "2.0.20", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", diff --git a/package.json b/package.json index 1ecbba6c..a7bf1c5d 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,6 @@ "@weaverse/hydrogen": "4.0.0", "class-variance-authority": "0.7.0", "clsx": "2.1.1", - "colord": "^2.9.3", "cross-env": "7.0.3", "framer-motion": "^11.11.0", "graphql": "16.9.0", From aebf1b552e2dd93e4828c7d121049f44c376cd18 Mon Sep 17 00:00:00 2001 From: hta218 Date: Thu, 5 Dec 2024 16:31:46 +0700 Subject: [PATCH 22/39] Update collection details page --- .../collection-filters/drawer-filter.tsx | 254 ------------------ app/sections/collection-filters/index.tsx | 59 ++-- .../collection-filters/layout-switcher.tsx | 2 +- 3 files changed, 31 insertions(+), 284 deletions(-) delete mode 100644 app/sections/collection-filters/drawer-filter.tsx diff --git a/app/sections/collection-filters/drawer-filter.tsx b/app/sections/collection-filters/drawer-filter.tsx deleted file mode 100644 index 1ad4593b..00000000 --- a/app/sections/collection-filters/drawer-filter.tsx +++ /dev/null @@ -1,254 +0,0 @@ -import { - Disclosure, - DisclosureButton, - DisclosurePanel, -} from "@headlessui/react"; -import { Sliders } from "@phosphor-icons/react"; -import { useLocation, useNavigate, useSearchParams } from "@remix-run/react"; -import type { - Filter, - ProductFilter, -} from "@shopify/hydrogen/storefront-api-types"; -import type { SyntheticEvent } from "react"; -import { useState } from "react"; -import { Button } from "~/components/button"; -import { Checkbox } from "~/components/checkbox"; -import { IconCaretDown, IconCaretRight } from "~/components/icons"; -import { FILTER_URL_PREFIX } from "~/lib/const"; -import type { AppliedFilter } from "~/lib/filter"; -import { getAppliedFilterLink, getFilterLink } from "~/lib/filter"; -import { Drawer, useDrawer } from "../../modules/drawer"; -import { Input } from "../../modules/input"; -import { LayoutSwitcher } from "./layout-switcher"; -import { Sort } from "./sort"; - -type DrawerFilterProps = { - productNumber?: number; - filters: Filter[]; - appliedFilters?: AppliedFilter[]; - collections?: Array<{ handle: string; title: string }>; - showSearchSort?: boolean; - numberInRow?: number; - onLayoutChange: (number: number) => void; -}; - -export function DrawerFilter({ - filters, - numberInRow, - onLayoutChange, - appliedFilters = [], - productNumber = 0, - showSearchSort = false, -}: DrawerFilterProps) { - const { openDrawer, isOpen, closeDrawer } = useDrawer(); - return ( -
-
- - {productNumber} Products -
- - - -
- -
-
-
-
-
- ); -} - -function ListItemFilter({ - option, - appliedFilters, -}: { - option: Filter["values"][0]; - appliedFilters: AppliedFilter[]; -}) { - const navigate = useNavigate(); - const [params] = useSearchParams(); - const location = useLocation(); - let filter = appliedFilters.find( - (filter) => JSON.stringify(filter.filter) === option.input, - ); - let [checked, setChecked] = useState(!!filter); - - let handleCheckedChange = (checked: boolean) => { - setChecked(checked); - if (checked) { - const link = getFilterLink(option.input as string, params, location); - navigate(link); - } else if (filter) { - let link = getAppliedFilterLink(filter, params, location); - navigate(link); - } - }; - return ( -
- -
- ); -} - -export function FiltersDrawer({ - filters = [], - appliedFilters = [], -}: Omit) { - const [params] = useSearchParams(); - const filterMarkup = (filter: Filter, option: Filter["values"][0]) => { - switch (filter.type) { - case "PRICE_RANGE": { - let priceFilter = params.get(`${FILTER_URL_PREFIX}price`); - let price = priceFilter - ? (JSON.parse(priceFilter) as ProductFilter["price"]) - : undefined; - let min = Number.isNaN(Number(price?.min)) - ? undefined - : Number(price?.min); - let max = Number.isNaN(Number(price?.max)) - ? undefined - : Number(price?.max); - return ; - } - - default: - return ( - - ); - } - }; - - return ( -
- {filters.map((filter: Filter) => ( - - {({ open }) => ( - <> - - {filter.label} - {open ? ( - - ) : ( - - )} - - -
    - {filter.values?.map((option) => { - return ( -
  • {filterMarkup(filter, option)}
  • - ); - })} -
-
- - )} -
- ))} -
- ); -} - -// const PRICE_RANGE_FILTER_DEBOUNCE = 500; - -function PriceRangeFilter({ max, min }: { max?: number; min?: number }) { - // const location = useLocation(); - // const params = useMemo( - // () => new URLSearchParams(location.search), - // [location.search], - // ); - // const navigate = useNavigate(); - - const [minPrice, setMinPrice] = useState(min); - const [maxPrice, setMaxPrice] = useState(max); - - // useDebounce( - // () => { - // if (minPrice === undefined && maxPrice === undefined) { - // params.delete(`${FILTER_URL_PREFIX}price`); - // navigate(`${location.pathname}?${params.toString()}`); - // return; - // } - - // const price = { - // ...(minPrice === undefined ? {} : {min: minPrice}), - // ...(maxPrice === undefined ? {} : {max: maxPrice}), - // }; - // const newParams = filterInputToParams({price}, params); - // navigate(`${location.pathname}?${newParams.toString()}`); - // }, - // PRICE_RANGE_FILTER_DEBOUNCE, - // [minPrice, maxPrice], - // ); - - const onChangeMax = (event: SyntheticEvent) => { - const value = (event.target as HTMLInputElement).value; - const newMaxPrice = Number.isNaN(Number.parseFloat(value)) - ? undefined - : Number.parseFloat(value); - setMaxPrice(newMaxPrice); - }; - - const onChangeMin = (event: SyntheticEvent) => { - const value = (event.target as HTMLInputElement).value; - const newMinPrice = Number.isNaN(Number.parseFloat(value)) - ? undefined - : Number.parseFloat(value); - setMinPrice(newMinPrice); - }; - - return ( -
- - -
- ); -} diff --git a/app/sections/collection-filters/index.tsx b/app/sections/collection-filters/index.tsx index aec95ba6..9706b84e 100644 --- a/app/sections/collection-filters/index.tsx +++ b/app/sections/collection-filters/index.tsx @@ -1,56 +1,54 @@ import { useLoaderData } from "@remix-run/react"; import { Pagination } from "@shopify/hydrogen"; -import type { Filter } from "@shopify/hydrogen/storefront-api-types"; import type { HydrogenComponentSchema } from "@weaverse/hydrogen"; import { forwardRef, useState } from "react"; import { useInView } from "react-intersection-observer"; import type { CollectionDetailsQuery } from "storefrontapi.generated"; +import Link from "~/components/link"; import { Section, type SectionProps, layoutInputs } from "~/components/section"; import { Button } from "~/modules/button"; -import type { AppliedFilter } from "~/modules/sort-filter"; -import { DrawerFilter } from "./drawer-filter"; import { ProductsLoadedOnScroll } from "./products-loaded-on-scroll"; +import { ToolsBar } from "./tools-bar"; interface CollectionFiltersProps extends SectionProps { - showCollectionDescription: boolean; loadPrevText: string; loadMoreText: string; } let CollectionFilters = forwardRef( (props, sectionRef) => { - let { showCollectionDescription, loadPrevText, loadMoreText, ...rest } = - props; + let { loadPrevText, loadMoreText, ...rest } = props; let { ref, inView } = useInView(); let [numberInRow, setNumberInRow] = useState(4); let onLayoutChange = (number: number) => { setNumberInRow(number); }; - let { collection, collections, appliedFilters } = useLoaderData< + let { collection, collections } = useLoaderData< CollectionDetailsQuery & { collections: Array<{ handle: string; title: string }>; - appliedFilters: AppliedFilter[]; } >(); let productNumber = collection?.products.nodes.length; if (collection?.products && collections) { return ( -
-
{collection.title}
- {showCollectionDescription && collection?.description && ( -
- {collection.description} +
+
+
+ + Home + + / + {collection.title}
- )} - {collection.title} +
+ {({ @@ -62,7 +60,7 @@ let CollectionFilters = forwardRef( hasNextPage, state, }) => ( - <> +
- +
)}
@@ -106,16 +104,19 @@ export let schema: HydrogenComponentSchema = { pages: ["COLLECTION"], }, inspector: [ - { group: "Layout", inputs: layoutInputs }, { - group: "Collection filters", + group: "Layout", + inputs: layoutInputs.filter((inp) => { + return inp.name !== "borderRadius" && inp.name !== "gap"; + }), + }, + { + group: "Filtering and sorting", + inputs: [], + }, + { + group: "Load more buttons", inputs: [ - { - type: "switch", - name: "showCollectionDescription", - label: "Show collection description", - defaultValue: true, - }, { type: "text", name: "loadPrevText", diff --git a/app/sections/collection-filters/layout-switcher.tsx b/app/sections/collection-filters/layout-switcher.tsx index 3a0365be..3ec8f0fd 100644 --- a/app/sections/collection-filters/layout-switcher.tsx +++ b/app/sections/collection-filters/layout-switcher.tsx @@ -22,7 +22,7 @@ export function LayoutSwitcher({ return (
button]:text-[#b7b7b7] [&>button]:border-[#b7b7b7]", '[&>button[data-active="true"]]:text-[#696662]', '[&>button[data-active="true"]]:border-[#696662]', From 9df18904bd76e43269031fea35f302e068e79ce3 Mon Sep 17 00:00:00 2001 From: hta218 Date: Fri, 6 Dec 2024 09:11:21 +0700 Subject: [PATCH 23/39] Refactor sort component to use Radix UI's dropdown menu --- app/sections/collection-filters/sort.tsx | 57 +++++++++++++----------- package-lock.json | 45 +++++++++++++++++++ package.json | 1 + 3 files changed, 77 insertions(+), 26 deletions(-) diff --git a/app/sections/collection-filters/sort.tsx b/app/sections/collection-filters/sort.tsx index b22156f5..ab6f7856 100644 --- a/app/sections/collection-filters/sort.tsx +++ b/app/sections/collection-filters/sort.tsx @@ -1,9 +1,9 @@ -import { Menu, MenuButton, MenuItem, MenuItems } from "@headlessui/react"; import { CaretDown } from "@phosphor-icons/react"; +import * as DropdownMenu from "@radix-ui/react-dropdown-menu"; import { Link, useLocation, useSearchParams } from "@remix-run/react"; import clsx from "clsx"; +import { cn } from "~/lib/cn"; import type { SortParam } from "~/lib/filter"; -import { getSortLink } from "~/lib/filter"; const PRODUCT_SORT: { label: string; key: SortParam }[] = [ { label: "Featured", key: "featured" }, @@ -48,36 +48,41 @@ export function Sort({ let [params] = useSearchParams(); let location = useLocation(); let sortList = show ? SEARCH_SORT : PRODUCT_SORT; - let active = + let { key: currentSortValue } = sortList.find(({ key }) => key === params.get("sort")) || sortList[0]; return ( - - + + Sort by - - - {sortList.map((item) => ( - - {() => ( - -

+ + + {sortList.map(({ key, label }) => { + let pr = new URLSearchParams(params); + pr.set("sort", key); + let sortUrl = `${location.pathname}?${pr.toString()}`; + return ( + + - {item.label} -

- - )} -
- ))} -
-
+ {label} + + + ); + })} + + + ); } diff --git a/package-lock.json b/package-lock.json index 2e7b9b1e..e9d75be6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@radix-ui/react-checkbox": "1.1.2", "@radix-ui/react-collapsible": "^1.1.1", "@radix-ui/react-dialog": "^1.1.2", + "@radix-ui/react-dropdown-menu": "^2.1.2", "@radix-ui/react-menubar": "^1.1.2", "@radix-ui/react-scroll-area": "^1.2.1", "@radix-ui/react-tooltip": "1.1.4", @@ -4621,6 +4622,50 @@ } } }, + "node_modules/@radix-ui/react-dropdown-menu": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.2.tgz", + "integrity": "sha512-GVZMR+eqK8/Kes0a36Qrv+i20bAPXSn8rCBTHx30w+3ECnR5o3xixAlqcVaYvLeyKUsm0aqyhWfmUcqufM8nYA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-menu": "2.1.2", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-controllable-state": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-context": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz", + "integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-focus-guards": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.1.tgz", diff --git a/package.json b/package.json index a7bf1c5d..f85b7bd0 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "@radix-ui/react-checkbox": "1.1.2", "@radix-ui/react-collapsible": "^1.1.1", "@radix-ui/react-dialog": "^1.1.2", + "@radix-ui/react-dropdown-menu": "^2.1.2", "@radix-ui/react-menubar": "^1.1.2", "@radix-ui/react-scroll-area": "^1.2.1", "@radix-ui/react-tooltip": "1.1.4", From fb19a56c7468ddbbe5017d7260a8adbe8aa8d65d Mon Sep 17 00:00:00 2001 From: Paul Date: Fri, 6 Dec 2024 13:48:06 +0700 Subject: [PATCH 24/39] Update to support defining localization --- app/lib/type.ts | 13 +++++-------- app/root.tsx | 2 +- app/weaverse/components.ts | 10 +++++----- app/weaverse/schema.server.ts | 8 ++++++++ 4 files changed, 19 insertions(+), 14 deletions(-) diff --git a/app/lib/type.ts b/app/lib/type.ts index 289bfeb2..0b623c37 100644 --- a/app/lib/type.ts +++ b/app/lib/type.ts @@ -1,17 +1,14 @@ -import type { Storefront as HydrogenStorefront } from "@shopify/hydrogen"; import type { - CountryCode, - CurrencyCode, - LanguageCode, -} from "@shopify/hydrogen/storefront-api-types"; + Storefront as HydrogenStorefront, + I18nBase, +} from "@shopify/hydrogen"; +import type { CurrencyCode } from "@shopify/hydrogen/storefront-api-types"; export type NonNullableFields = { [P in keyof T]: NonNullable; }; -export type Locale = { - language: LanguageCode; - country: CountryCode; +export type Locale = I18nBase & { label: string; currency: CurrencyCode; }; diff --git a/app/root.tsx b/app/root.tsx index e7005b33..418a728c 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -218,7 +218,7 @@ function App() { export default withWeaverse(App); export function ErrorBoundary({ error }: { error: Error }) { - let routeError = useRouteError(); + let routeError = useRouteError() as any; let isRouteError = isRouteErrorResponse(routeError); let pageType = "page"; diff --git a/app/weaverse/components.ts b/app/weaverse/components.ts index 80b4a89c..537c0d98 100644 --- a/app/weaverse/components.ts +++ b/app/weaverse/components.ts @@ -1,6 +1,6 @@ import type { HydrogenComponent } from "@weaverse/hydrogen"; -import * as Link from "~/components/link"; import * as Heading from "~/components/heading"; +import * as Link from "~/components/link"; import * as Paragraph from "~/components/paragraph"; import * as SubHeading from "~/components/subheading"; import * as Judgeme from "~/modules/product-form/judgeme-review"; @@ -30,6 +30,8 @@ import * as ImageGalleryItems from "~/sections/image-gallery/items"; import * as ImageWithText from "~/sections/image-with-text"; import * as ImageWithTextContent from "~/sections/image-with-text/content"; import * as ImageWithTextImage from "~/sections/image-with-text/image"; +import * as JudgemeReview from "~/sections/judgeme-reviews"; +import * as ReviewIndex from "~/sections/judgeme-reviews/review-index"; import * as MapSection from "~/sections/map"; import * as NewsLetter from "~/sections/newsletter"; import * as NewsLetterForm from "~/sections/newsletter/newsletter-form"; @@ -52,16 +54,14 @@ import * as TestimonialItem from "~/sections/testimonials/item"; import * as TestimonialItems from "~/sections/testimonials/items"; import * as VideoEmbed from "~/sections/video-embed"; import * as VideoEmbedItem from "~/sections/video-embed/video"; -import * as JudgemeReview from "~/sections/judgeme-reviews"; -import * as ReviewIndex from "~/sections/judgeme-reviews/review-index"; export let components: HydrogenComponent[] = [ SubHeading, Heading, Paragraph, Link, - AliReview, - AliReviewList, + // AliReview, + // AliReviewList, AllProducts, FeaturedCollections, FeaturedCollectionItems, diff --git a/app/weaverse/schema.server.ts b/app/weaverse/schema.server.ts index a44887d0..aabeb9ed 100644 --- a/app/weaverse/schema.server.ts +++ b/app/weaverse/schema.server.ts @@ -1,4 +1,5 @@ import type { HydrogenThemeSchema, SwatchesConfigs } from "@weaverse/hydrogen"; +import { countries } from "~/data/countries"; import pkg from "../../package.json"; let swatchesConfigs: SwatchesConfigs = { @@ -84,6 +85,13 @@ export let themeSchema: HydrogenThemeSchema = { documentationUrl: "https://weaverse.io/docs", supportUrl: "https://weaverse.io/contact", }, + i18n: Object.values(countries).map((i) => { + return { + language: i.language, + country: i.country, + label: i.label, + }; + }), inspector: [ { group: "Layout", From 5f98a509c5aab7180988af83e0f0b3eed5f1d7c3 Mon Sep 17 00:00:00 2001 From: Paul Date: Fri, 6 Dec 2024 13:49:46 +0700 Subject: [PATCH 25/39] Update to support defining localization --- CHANGELOG.md | 7 +++++++ package.json | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fe5b793..550235ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # @weaverse/pilot +## 3.1.1 + +### Patch Changes + +- Updated dependencies + - @weaverse/hydrogen@4.0.1 + ## 3.0.4 ### Patch Changes diff --git a/package.json b/package.json index a7bf1c5d..52d5932b 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "@weaverse/pilot", "private": true, "sideEffects": false, - "version": "3.1.0", + "version": "3.2.0", "type": "module", "author": "Weaverse", "scripts": { @@ -37,7 +37,7 @@ "@shopify/cli": "^3.70.0", "@shopify/hydrogen": "2024.10.0", "@shopify/remix-oxygen": "2.0.9", - "@weaverse/hydrogen": "4.0.0", + "@weaverse/hydrogen": "4.0.1", "class-variance-authority": "0.7.0", "clsx": "2.1.1", "cross-env": "7.0.3", From 6183649bf01ff7a5855d9af3174028af4b8bad9e Mon Sep 17 00:00:00 2001 From: hta218 Date: Sat, 7 Dec 2024 15:41:11 +0700 Subject: [PATCH 26/39] Use *.ts file for hooks --- app/hooks/use-weaverse-component.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 app/hooks/use-weaverse-component.ts diff --git a/app/hooks/use-weaverse-component.ts b/app/hooks/use-weaverse-component.ts new file mode 100644 index 00000000..e69de29b From 48db92a0a411a016d38fd8879a5fd6bf4c50f265 Mon Sep 17 00:00:00 2001 From: hta218 Date: Sat, 7 Dec 2024 15:41:26 +0700 Subject: [PATCH 27/39] Use *.ts file for hooks --- app/hooks/{use-analytics.tsx => use-analytics.ts} | 0 app/hooks/{use-animation.tsx => use-animation.ts} | 0 app/hooks/{use-cart-fetchers.tsx => use-cart-fetchers.ts} | 0 app/hooks/{use-is-hydrated.tsx => use-is-hydrated.ts} | 0 app/hooks/{use-page-analytics.tsx => use-page-analytics.ts} | 0 app/hooks/{use-shop-menu.tsx => use-shop-menu.ts} | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename app/hooks/{use-analytics.tsx => use-analytics.ts} (100%) rename app/hooks/{use-animation.tsx => use-animation.ts} (100%) rename app/hooks/{use-cart-fetchers.tsx => use-cart-fetchers.ts} (100%) rename app/hooks/{use-is-hydrated.tsx => use-is-hydrated.ts} (100%) rename app/hooks/{use-page-analytics.tsx => use-page-analytics.ts} (100%) rename app/hooks/{use-shop-menu.tsx => use-shop-menu.ts} (100%) diff --git a/app/hooks/use-analytics.tsx b/app/hooks/use-analytics.ts similarity index 100% rename from app/hooks/use-analytics.tsx rename to app/hooks/use-analytics.ts diff --git a/app/hooks/use-animation.tsx b/app/hooks/use-animation.ts similarity index 100% rename from app/hooks/use-animation.tsx rename to app/hooks/use-animation.ts diff --git a/app/hooks/use-cart-fetchers.tsx b/app/hooks/use-cart-fetchers.ts similarity index 100% rename from app/hooks/use-cart-fetchers.tsx rename to app/hooks/use-cart-fetchers.ts diff --git a/app/hooks/use-is-hydrated.tsx b/app/hooks/use-is-hydrated.ts similarity index 100% rename from app/hooks/use-is-hydrated.tsx rename to app/hooks/use-is-hydrated.ts diff --git a/app/hooks/use-page-analytics.tsx b/app/hooks/use-page-analytics.ts similarity index 100% rename from app/hooks/use-page-analytics.tsx rename to app/hooks/use-page-analytics.ts diff --git a/app/hooks/use-shop-menu.tsx b/app/hooks/use-shop-menu.ts similarity index 100% rename from app/hooks/use-shop-menu.tsx rename to app/hooks/use-shop-menu.ts From 8541d62f935fbaa53afbc63e066d0dc86768bc65 Mon Sep 17 00:00:00 2001 From: hta218 Date: Sun, 8 Dec 2024 10:54:43 +0700 Subject: [PATCH 28/39] Add hook to get closest weaverse instance of any component --- app/hooks/use-closest-weaverse-item.ts | 22 ++++++++++++++++++++++ app/hooks/use-weaverse-component.ts | 0 2 files changed, 22 insertions(+) create mode 100644 app/hooks/use-closest-weaverse-item.ts delete mode 100644 app/hooks/use-weaverse-component.ts diff --git a/app/hooks/use-closest-weaverse-item.ts b/app/hooks/use-closest-weaverse-item.ts new file mode 100644 index 00000000..69657a91 --- /dev/null +++ b/app/hooks/use-closest-weaverse-item.ts @@ -0,0 +1,22 @@ +import { useItemInstance } from "@weaverse/hydrogen"; +import { useEffect, useState } from "react"; + +export function useClosestWeaverseItem(selector: string) { + let [weaverseId, setWeaverseId] = useState(""); + let weaverseItem = useItemInstance(weaverseId); + + // biome-ignore lint/correctness/useExhaustiveDependencies: + useEffect(() => { + if (!weaverseItem) { + let target = document.querySelector(selector); + if (target) { + let closest = target.closest("[data-wv-id]"); + if (closest) { + setWeaverseId(closest.getAttribute("data-wv-id")); + } + } + } + }, []); + + return weaverseItem; +} diff --git a/app/hooks/use-weaverse-component.ts b/app/hooks/use-weaverse-component.ts deleted file mode 100644 index e69de29b..00000000 From 684ba7a27fc6ebae1b68df10a494192035324dee Mon Sep 17 00:00:00 2001 From: hta218 Date: Sun, 8 Dec 2024 10:55:04 +0700 Subject: [PATCH 29/39] Update collection filters layout switcher --- .../collection-filters/layout-switcher.tsx | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/app/sections/collection-filters/layout-switcher.tsx b/app/sections/collection-filters/layout-switcher.tsx index 3ec8f0fd..1c00c7bb 100644 --- a/app/sections/collection-filters/layout-switcher.tsx +++ b/app/sections/collection-filters/layout-switcher.tsx @@ -10,15 +10,18 @@ const LAYOUT_ICONS = { 5: FiveColumns, }; +export type LayoutSwitcherProps = { + gridSizeDesktop: number; + gridSizeMobile: number; + onGridSizeChange: (number: number) => void; +}; + export function LayoutSwitcher({ - value, - onChange, + gridSizeDesktop, + gridSizeMobile, + onGridSizeChange, className, -}: { - value: number; - onChange: (number: number) => void; - className?: string; -}) { +}: LayoutSwitcherProps & { className?: string }) { return (
onChange(col)} + data-active={ + col > 2 ? gridSizeDesktop === col : gridSizeMobile === col + } + onClick={() => onGridSizeChange(col)} className={clsx( - "border w-10 h-10 items-center justify-center", - col <= 2 ? "flex lg:hidden" : "hidden lg:flex", + "border w-12 h-12 items-center justify-center", + col > 2 ? "hidden lg:flex" : "flex lg:hidden", )} > - + ); })} From c3b7514165df44a1dcf3e1c629d98a289cfb5697 Mon Sep 17 00:00:00 2001 From: hta218 Date: Sun, 8 Dec 2024 10:55:17 +0700 Subject: [PATCH 30/39] Update collection sorting --- app/lib/filter.ts | 9 --------- app/sections/collection-filters/sort.tsx | 11 +++-------- 2 files changed, 3 insertions(+), 17 deletions(-) diff --git a/app/lib/filter.ts b/app/lib/filter.ts index 11c4536f..788e0442 100644 --- a/app/lib/filter.ts +++ b/app/lib/filter.ts @@ -27,15 +27,6 @@ export function getAppliedFilterLink( return `${location.pathname}?${paramsClone.toString()}`; } -export function getSortLink( - sort: SortParam, - params: URLSearchParams, - location: Location, -) { - params.set("sort", sort); - return `${location.pathname}?${params.toString()}`; -} - export function getFilterLink( rawInput: string | ProductFilter, params: URLSearchParams, diff --git a/app/sections/collection-filters/sort.tsx b/app/sections/collection-filters/sort.tsx index ab6f7856..34455287 100644 --- a/app/sections/collection-filters/sort.tsx +++ b/app/sections/collection-filters/sort.tsx @@ -1,7 +1,6 @@ import { CaretDown } from "@phosphor-icons/react"; import * as DropdownMenu from "@radix-ui/react-dropdown-menu"; import { Link, useLocation, useSearchParams } from "@remix-run/react"; -import clsx from "clsx"; import { cn } from "~/lib/cn"; import type { SortParam } from "~/lib/filter"; @@ -40,20 +39,16 @@ const SEARCH_SORT: { label: string; key: SortParam }[] = [ }, ]; -export function Sort({ - show = false, -}: { - show?: boolean; -}) { +export function Sort() { let [params] = useSearchParams(); let location = useLocation(); - let sortList = show ? SEARCH_SORT : PRODUCT_SORT; + let sortList = PRODUCT_SORT; let { key: currentSortValue } = sortList.find(({ key }) => key === params.get("sort")) || sortList[0]; return ( - + Sort by From 11b37cfc88b059e945c482bd059e0e3a72cbff08 Mon Sep 17 00:00:00 2001 From: hta218 Date: Sun, 8 Dec 2024 10:55:35 +0700 Subject: [PATCH 31/39] Update collection filters toolbar --- app/sections/collection-filters/filters.tsx | 164 ++++++------- app/sections/collection-filters/index.tsx | 219 ++++++++++++++---- app/sections/collection-filters/tools-bar.tsx | 89 +++++-- 3 files changed, 306 insertions(+), 166 deletions(-) diff --git a/app/sections/collection-filters/filters.tsx b/app/sections/collection-filters/filters.tsx index c8dd9400..fe62a65d 100644 --- a/app/sections/collection-filters/filters.tsx +++ b/app/sections/collection-filters/filters.tsx @@ -1,10 +1,4 @@ -import { - Disclosure, - DisclosureButton, - DisclosurePanel, -} from "@headlessui/react"; -import { Sliders } from "@phosphor-icons/react"; -import * as Dialog from "@radix-ui/react-dialog"; +import * as Accordion from "@radix-ui/react-accordion"; import { useLoaderData, useLocation, @@ -15,64 +9,96 @@ import type { Filter, ProductFilter, } from "@shopify/hydrogen/storefront-api-types"; -import clsx from "clsx"; import type { SyntheticEvent } from "react"; import { useState } from "react"; import type { CollectionDetailsQuery } from "storefrontapi.generated"; -import { Button } from "~/components/button"; import { Checkbox } from "~/components/checkbox"; -import { IconCaretDown, IconCaretRight } from "~/components/icons"; +import { IconCaretRight } from "~/components/icons"; import { FILTER_URL_PREFIX } from "~/lib/const"; import type { AppliedFilter } from "~/lib/filter"; import { getAppliedFilterLink, getFilterLink } from "~/lib/filter"; import { Input } from "../../modules/input"; +import { useClosestWeaverseItem } from "~/hooks/use-closest-weaverse-item"; +import type { CollectionFiltersData } from "."; export function Filters() { + let parentInstance = useClosestWeaverseItem(".filters-list"); + let parentData = parentInstance.data as unknown as CollectionFiltersData; + let { expandFilters, showFiltersCount } = parentData; + let [params] = useSearchParams(); let { collection, appliedFilters } = useLoaderData< CollectionDetailsQuery & { collections: Array<{ handle: string; title: string }>; appliedFilters: AppliedFilter[]; } >(); + let filters = collection.products.filters as Filter[]; - return ( - - - - - - - - { + switch (filter.type) { + case "PRICE_RANGE": { + let priceFilter = params.get(`${FILTER_URL_PREFIX}price`); + let price = priceFilter + ? (JSON.parse(priceFilter) as ProductFilter["price"]) + : undefined; + let min = Number.isNaN(Number(price?.min)) + ? undefined + : Number(price?.min); + let max = Number.isNaN(Number(price?.max)) + ? undefined + : Number(price?.max); + return ; + } + + default: + return ( + - - - + ); + } + }; + + return ( + filter.id) : []} + > + {filters.map((filter: Filter) => ( + + + {filter.label} + + + +
    + {filter.values?.map((option) => { + return
  • {filterMarkup(filter, option)}
  • ; + })} +
+
+
+ ))} +
); } function ListItemFilter({ option, appliedFilters, + showFiltersCount, }: { option: Filter["values"][0]; appliedFilters: AppliedFilter[]; + showFiltersCount: boolean; }) { let navigate = useNavigate(); let [params] = useSearchParams(); @@ -97,72 +123,14 @@ function ListItemFilter({
); } -export function FiltersList({ filters = [], appliedFilters = [] }) { - let [params] = useSearchParams(); - let filterMarkup = (filter: Filter, option: Filter["values"][0]) => { - switch (filter.type) { - case "PRICE_RANGE": { - let priceFilter = params.get(`${FILTER_URL_PREFIX}price`); - let price = priceFilter - ? (JSON.parse(priceFilter) as ProductFilter["price"]) - : undefined; - let min = Number.isNaN(Number(price?.min)) - ? undefined - : Number(price?.min); - let max = Number.isNaN(Number(price?.max)) - ? undefined - : Number(price?.max); - return ; - } - - default: - return ( - - ); - } - }; - - return ( -
- {filters.map((filter: Filter) => ( - - {({ open }) => ( - <> - - {filter.label} - {open ? ( - - ) : ( - - )} - - -
    - {filter.values?.map((option) => { - return ( -
  • {filterMarkup(filter, option)}
  • - ); - })} -
-
- - )} -
- ))} -
- ); -} - // const PRICE_RANGE_FILTER_DEBOUNCE = 500; function PriceRangeFilter({ max, min }: { max?: number; min?: number }) { diff --git a/app/sections/collection-filters/index.tsx b/app/sections/collection-filters/index.tsx index 9706b84e..a65e58a5 100644 --- a/app/sections/collection-filters/index.tsx +++ b/app/sections/collection-filters/index.tsx @@ -1,35 +1,63 @@ import { useLoaderData } from "@remix-run/react"; import { Pagination } from "@shopify/hydrogen"; import type { HydrogenComponentSchema } from "@weaverse/hydrogen"; -import { forwardRef, useState } from "react"; +import { forwardRef, useEffect, useState } from "react"; import { useInView } from "react-intersection-observer"; import type { CollectionDetailsQuery } from "storefrontapi.generated"; import Link from "~/components/link"; import { Section, type SectionProps, layoutInputs } from "~/components/section"; -import { Button } from "~/modules/button"; +import { Filters } from "./filters"; import { ProductsLoadedOnScroll } from "./products-loaded-on-scroll"; import { ToolsBar } from "./tools-bar"; -interface CollectionFiltersProps extends SectionProps { +export interface CollectionFiltersData { + enableSort: boolean; + showProductsCount: boolean; + enableFilter: boolean; + filtersPosition: "sidebar" | "drawer"; + expandFilters: boolean; + showFiltersCount: boolean; + productsPerRowDesktop: number; + productsPerRowMobile: number; loadPrevText: string; loadMoreText: string; } +interface CollectionFiltersProps extends SectionProps, CollectionFiltersData {} + let CollectionFilters = forwardRef( (props, sectionRef) => { - let { loadPrevText, loadMoreText, ...rest } = props; - + let { + enableSort, + showFiltersCount, + enableFilter, + filtersPosition, + expandFilters, + showProductsCount, + productsPerRowDesktop, + productsPerRowMobile, + loadPrevText, + loadMoreText, + ...rest + } = props; let { ref, inView } = useInView(); - let [numberInRow, setNumberInRow] = useState(4); - let onLayoutChange = (number: number) => { - setNumberInRow(number); - }; let { collection, collections } = useLoaderData< CollectionDetailsQuery & { collections: Array<{ handle: string; title: string }>; } >(); - let productNumber = collection?.products.nodes.length; + + let [gridSizeDesktop, setGridSizeDesktop] = useState( + Number(productsPerRowDesktop) || 3, + ); + let [gridSizeMobile, setGridSizeMobile] = useState( + Number(productsPerRowMobile) || 1, + ); + + useEffect(() => { + setGridSizeDesktop(Number(productsPerRowDesktop) || 3); + setGridSizeMobile(Number(productsPerRowMobile) || 1); + }, [productsPerRowDesktop, productsPerRowMobile]); if (collection?.products && collections) { return ( @@ -46,47 +74,67 @@ let CollectionFilters = forwardRef(
{ + if (v > 2) { + setGridSizeDesktop(v); + } else { + setGridSizeMobile(v); + } + }} + {...props} /> - - {({ - nodes, - isLoading, - PreviousLink, - NextLink, - nextPageUrl, - hasNextPage, - state, - }) => ( -
-
- -
- -
- -
+
+ {filtersPosition === "sidebar" && ( +
+
Filters
+
)} - + + {({ + nodes, + isLoading, + nextPageUrl, + previousPageUrl, + hasNextPage, + hasPreviousPage, + state, + }) => ( +
+ {hasPreviousPage && ( + + {isLoading ? "Loading..." : loadPrevText} + + )} + + {hasNextPage && ( + + {isLoading ? "Loading..." : loadMoreText} + + )} +
+ )} +
+
); } @@ -112,11 +160,82 @@ export let schema: HydrogenComponentSchema = { }, { group: "Filtering and sorting", - inputs: [], + inputs: [ + { + type: "switch", + name: "enableSort", + label: "Enable sorting", + defaultValue: true, + }, + { + type: "switch", + name: "showProductsCount", + label: "Show products count", + defaultValue: true, + }, + { + type: "switch", + name: "enableFilter", + label: "Enable filtering", + defaultValue: true, + }, + { + type: "select", + name: "filtersPosition", + label: "Filters position", + configs: { + options: [ + { value: "sidebar", label: "Sidebar" }, + { value: "drawer", label: "Drawer" }, + ], + }, + defaultValue: "sidebar", + condition: "enableFilter.eq.true", + }, + { + type: "switch", + name: "expandFilters", + label: "Expand filters", + defaultValue: true, + condition: "enableFilter.eq.true", + }, + { + type: "switch", + name: "showFiltersCount", + label: "Show filters count", + defaultValue: true, + condition: "enableFilter.eq.true", + }, + ], }, { - group: "Load more buttons", + group: "Products", inputs: [ + { + type: "select", + name: "productsPerRowDesktop", + label: "Default products per row (desktop)", + configs: { + options: [ + { value: "3", label: "3" }, + { value: "4", label: "4" }, + { value: "5", label: "5" }, + ], + }, + defaultValue: "3", + }, + { + type: "select", + name: "productsPerRowMobile", + label: "Default products per row (mobile)", + configs: { + options: [ + { value: "1", label: "1" }, + { value: "2", label: "2" }, + ], + }, + defaultValue: "1", + }, { type: "text", name: "loadPrevText", diff --git a/app/sections/collection-filters/tools-bar.tsx b/app/sections/collection-filters/tools-bar.tsx index d1eba741..855710a1 100644 --- a/app/sections/collection-filters/tools-bar.tsx +++ b/app/sections/collection-filters/tools-bar.tsx @@ -1,8 +1,14 @@ +import { useLoaderData } from "@remix-run/react"; import { type VariantProps, cva } from "class-variance-authority"; +import type { CollectionDetailsQuery } from "storefrontapi.generated"; +import { cn } from "~/lib/cn"; import { Filters } from "./filters"; -import { LayoutSwitcher } from "./layout-switcher"; +import { LayoutSwitcher, type LayoutSwitcherProps } from "./layout-switcher"; import { Sort } from "./sort"; -import { cn } from "~/lib/cn"; +import * as Dialog from "@radix-ui/react-dialog"; +import { Button } from "~/components/button"; +import { Sliders } from "@phosphor-icons/react"; +import clsx from "clsx"; let variants = cva("", { variants: { @@ -18,36 +24,83 @@ let variants = cva("", { }, }); -interface ToolsBarProps extends VariantProps { - productsCount?: number; - showSearchSort?: boolean; - numberInRow?: number; - onLayoutChange: (number: number) => void; +interface ToolsBarProps + extends VariantProps, + LayoutSwitcherProps { + enableSort: boolean; + showProductsCount: boolean; + enableFilter: boolean; + filtersPosition: "sidebar" | "drawer"; + expandFilters: boolean; + showFiltersCount: boolean; } export function ToolsBar({ + enableSort, + enableFilter, + filtersPosition, + showProductsCount, width, - numberInRow, - productsCount = 0, - showSearchSort = false, - onLayoutChange, + gridSizeDesktop, + gridSizeMobile, + onGridSizeChange, }: ToolsBarProps) { + let { collection } = useLoaderData(); return (
- {productsCount} Products -
- - + {showProductsCount && ( + + {collection?.products.nodes.length} Products + + )} +
+ {enableSort && } + {enableFilter && }
); } + +function FiltersDrawer({ + filtersPosition, +}: { filtersPosition: ToolsBarProps["filtersPosition"] }) { + return ( + + + + + + + + + + + + ); +} From 639569a5a7d6d0161f3966ecd3ac2d5ecdfd0ead Mon Sep 17 00:00:00 2001 From: hta218 Date: Sun, 8 Dec 2024 10:55:45 +0700 Subject: [PATCH 32/39] Update product card size --- app/modules/product-card/index.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/app/modules/product-card/index.tsx b/app/modules/product-card/index.tsx index 0512227e..9cb7a784 100644 --- a/app/modules/product-card/index.tsx +++ b/app/modules/product-card/index.tsx @@ -39,13 +39,16 @@ export function ProductCard({ return (
-
+
{image && ( - + {image.altText Date: Sun, 8 Dec 2024 10:55:58 +0700 Subject: [PATCH 33/39] Update collection list product grid --- .../products-loaded-on-scroll.tsx | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/app/sections/collection-filters/products-loaded-on-scroll.tsx b/app/sections/collection-filters/products-loaded-on-scroll.tsx index e23df346..ad7adf02 100644 --- a/app/sections/collection-filters/products-loaded-on-scroll.tsx +++ b/app/sections/collection-filters/products-loaded-on-scroll.tsx @@ -1,26 +1,27 @@ import { useNavigate } from "@remix-run/react"; import { useEffect } from "react"; import { getImageLoadingPriority } from "~/lib/const"; -import { Grid } from "~/modules/grid"; import { ProductCard } from "~/modules/product-card"; -type ProductsLoadedOnScrollProps = { +interface ProductsLoadedOnScrollProps { + gridSizeDesktop: number; + gridSizeMobile: number; nodes: any; - numberInRow: number; inView: boolean; nextPageUrl: string; hasNextPage: boolean; state: any; -}; +} export function ProductsLoadedOnScroll(props: ProductsLoadedOnScrollProps) { let { + gridSizeMobile, + gridSizeDesktop, nodes, inView, nextPageUrl, hasNextPage, state, - numberInRow = 4, } = props; let navigate = useNavigate(); @@ -35,7 +36,15 @@ export function ProductsLoadedOnScroll(props: ProductsLoadedOnScrollProps) { }, [inView, navigate, state, nextPageUrl, hasNextPage]); return ( - +
{nodes.map((product: any, i: number) => ( ))} - +
); } From ecb011124f067b0b08581e6393de1e5aa888a2bc Mon Sep 17 00:00:00 2001 From: hta218 Date: Sun, 8 Dec 2024 10:56:11 +0700 Subject: [PATCH 34/39] Adding radix dep for handling accordions --- package-lock.json | 47 +++++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + 2 files changed, 48 insertions(+) diff --git a/package-lock.json b/package-lock.json index e9d75be6..6abbe60a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@graphql-codegen/cli": "5.0.3", "@headlessui/react": "2.2.0", "@phosphor-icons/react": "2.1.7", + "@radix-ui/react-accordion": "^1.2.1", "@radix-ui/react-checkbox": "1.1.2", "@radix-ui/react-collapsible": "^1.1.1", "@radix-ui/react-dialog": "^1.1.2", @@ -4365,6 +4366,52 @@ "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.0.tgz", "integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==" }, + "node_modules/@radix-ui/react-accordion": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.1.tgz", + "integrity": "sha512-bg/l7l5QzUjgsh8kjwDFommzAshnUsuVMV5NM56QVCm+7ZckYdd9P/ExR8xG/Oup0OajVxNLaHJ1tb8mXk+nzQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-collapsible": "1.1.1", + "@radix-ui/react-collection": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-controllable-state": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-accordion/node_modules/@radix-ui/react-context": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz", + "integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-arrow": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.0.tgz", diff --git a/package.json b/package.json index f85b7bd0..f8ba08a2 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "@graphql-codegen/cli": "5.0.3", "@headlessui/react": "2.2.0", "@phosphor-icons/react": "2.1.7", + "@radix-ui/react-accordion": "^1.2.1", "@radix-ui/react-checkbox": "1.1.2", "@radix-ui/react-collapsible": "^1.1.1", "@radix-ui/react-dialog": "^1.1.2", From cfaa13925f07c48028a88c8c3a07b62276d9a18f Mon Sep 17 00:00:00 2001 From: hta218 Date: Sun, 8 Dec 2024 11:02:45 +0700 Subject: [PATCH 35/39] Enhance accordion behavior in filters with animations --- app/sections/collection-filters/filters.tsx | 21 ++++++++++++++++++--- tailwind.config.js | 5 +++-- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/app/sections/collection-filters/filters.tsx b/app/sections/collection-filters/filters.tsx index fe62a65d..b3a54f7a 100644 --- a/app/sections/collection-filters/filters.tsx +++ b/app/sections/collection-filters/filters.tsx @@ -9,17 +9,18 @@ import type { Filter, ProductFilter, } from "@shopify/hydrogen/storefront-api-types"; +import clsx from "clsx"; import type { SyntheticEvent } from "react"; import { useState } from "react"; import type { CollectionDetailsQuery } from "storefrontapi.generated"; import { Checkbox } from "~/components/checkbox"; import { IconCaretRight } from "~/components/icons"; +import { useClosestWeaverseItem } from "~/hooks/use-closest-weaverse-item"; import { FILTER_URL_PREFIX } from "~/lib/const"; import type { AppliedFilter } from "~/lib/filter"; import { getAppliedFilterLink, getFilterLink } from "~/lib/filter"; -import { Input } from "../../modules/input"; -import { useClosestWeaverseItem } from "~/hooks/use-closest-weaverse-item"; import type { CollectionFiltersData } from "."; +import { Input } from "../../modules/input"; export function Filters() { let parentInstance = useClosestWeaverseItem(".filters-list"); @@ -78,7 +79,21 @@ export function Filters() { {filter.label} - +
    {filter.values?.map((option) => { return
  • {filterMarkup(filter, option)}
  • ; diff --git a/tailwind.config.js b/tailwind.config.js index a0cefe1e..e5340e24 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -70,8 +70,9 @@ export default { underline: "underline 400ms linear", "fade-in": "fade-in var(--fade-in-duration, .5s) ease-in forwards", "slide-down": - "slide-down var(--slide-down-duration, .3s) ease-out forwards", - "slide-up": "slide-up var(--slide-up-duration, .3s) ease-out forwards", + "slide-down var(--slide-down-duration, .3s) cubic-bezier(0.87, 0, 0.13, 1) forwards", + "slide-up": + "slide-up var(--slide-up-duration, .3s) cubic-bezier(0.87, 0, 0.13, 1) forwards", "slide-left-and-fade": "slide-left-and-fade var(--slide-left-and-fade-duration, .5s) cubic-bezier(.165,.84,.44,1) forwards", "enter-from-left": From 5210307d34e44713d903d2ec98fc7fe94d729472 Mon Sep 17 00:00:00 2001 From: hta218 Date: Sun, 8 Dec 2024 11:14:45 +0700 Subject: [PATCH 36/39] Refactor filters drawer layout: add header with close button and styling adjustments for better usability --- app/sections/collection-filters/filters.tsx | 4 ++-- app/sections/collection-filters/tools-bar.tsx | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/app/sections/collection-filters/filters.tsx b/app/sections/collection-filters/filters.tsx index b3a54f7a..6632b47c 100644 --- a/app/sections/collection-filters/filters.tsx +++ b/app/sections/collection-filters/filters.tsx @@ -65,7 +65,7 @@ export function Filters() { return ( filter.id) : []} > @@ -73,7 +73,7 @@ export function Filters() { {filter.label} diff --git a/app/sections/collection-filters/tools-bar.tsx b/app/sections/collection-filters/tools-bar.tsx index 855710a1..ea966d4e 100644 --- a/app/sections/collection-filters/tools-bar.tsx +++ b/app/sections/collection-filters/tools-bar.tsx @@ -7,7 +7,7 @@ import { LayoutSwitcher, type LayoutSwitcherProps } from "./layout-switcher"; import { Sort } from "./sort"; import * as Dialog from "@radix-ui/react-dialog"; import { Button } from "~/components/button"; -import { Sliders } from "@phosphor-icons/react"; +import { Sliders, X } from "@phosphor-icons/react"; import clsx from "clsx"; let variants = cva("", { @@ -98,7 +98,17 @@ function FiltersDrawer({ "left-0 -translate-x-full data-[state=open]:animate-enter-from-left", ])} > - +
    +
    + Filters + + + +
    + +
    From b6d73ab0ef9a7dba759227f3b372455f625928e0 Mon Sep 17 00:00:00 2001 From: hta218 Date: Sun, 8 Dec 2024 11:24:14 +0700 Subject: [PATCH 37/39] Type the error type in ErrorBoundary with better typing for routeError variable in app/root.tsx --- app/root.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/root.tsx b/app/root.tsx index 418a728c..bbb397cc 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -218,7 +218,7 @@ function App() { export default withWeaverse(App); export function ErrorBoundary({ error }: { error: Error }) { - let routeError = useRouteError() as any; + let routeError: { status?: number; data?: any } = useRouteError(); let isRouteError = isRouteErrorResponse(routeError); let pageType = "page"; From 871c0b8bbe5296f98b444645173f281338b52c06 Mon Sep 17 00:00:00 2001 From: "Tuan Anh (Leo) Huynh" Date: Sat, 7 Dec 2024 20:26:25 -0800 Subject: [PATCH 38/39] Add aria-label to filters drawer close button Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- app/sections/collection-filters/tools-bar.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/sections/collection-filters/tools-bar.tsx b/app/sections/collection-filters/tools-bar.tsx index ea966d4e..880b6946 100644 --- a/app/sections/collection-filters/tools-bar.tsx +++ b/app/sections/collection-filters/tools-bar.tsx @@ -102,7 +102,9 @@ function FiltersDrawer({
    Filters - From 55da6586c508236b032fba12ef71816cb7fe06e4 Mon Sep 17 00:00:00 2001 From: hta218 Date: Sun, 8 Dec 2024 11:30:58 +0700 Subject: [PATCH 39/39] Remove changelog file --- CHANGELOG.md | 120 --------------------------------------------------- 1 file changed, 120 deletions(-) delete mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 550235ab..00000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,120 +0,0 @@ -# @weaverse/pilot - -## 3.1.1 - -### Patch Changes - -- Updated dependencies - - @weaverse/hydrogen@4.0.1 - -## 3.0.4 - -### Patch Changes - -- Updated dependencies - - @weaverse/hydrogen@4.0.0 - -## 2.8.1 - -### Patch Changes - -- Updated dependencies - - @weaverse/hydrogen@3.4.1 - -## 2.7.8 - -### Patch Changes - -- Updated dependencies - - @weaverse/hydrogen@3.4.0 - -## 2.6.8 - -### Patch Changes - -- Updated dependencies - - @weaverse/hydrogen@3.1.9 - -## 2.6.7 - -### Patch Changes - -- @weaverse/hydrogen@3.1.8 - -## 2.6.6 - -### Patch Changes - -- Updated dependencies - - @weaverse/hydrogen@3.1.7 - -## 2.6.5 - -### Patch Changes - -- Updated dependencies - - @weaverse/hydrogen@3.1.6 - -## 2.6.4 - -### Patch Changes - -- Updated dependencies - - @weaverse/hydrogen@3.1.5 - -## 2.6.3 - -### Patch Changes - -- Updated dependencies - - @weaverse/hydrogen@3.1.3 - -## 2.6.2 - -### Patch Changes - -- Updated dependencies - - @weaverse/hydrogen@3.1.2 - -## 2.5.3 - -### Patch Changes - -- 3a975da: Adding url input type -- Updated dependencies [3a975da] - - @weaverse/hydrogen@3.1.1 - -## 2.5.2 - -### Patch Changes - -- Updated dependencies - - @weaverse/hydrogen@3.1.0 - -## 2.5.1 - -### Patch Changes - -- Updated dependencies - - @weaverse/hydrogen@3.0.1 - -## 2.4.5 - -### Patch Changes - -- Updated dependencies - - @weaverse/hydrogen@2.10.1 - -## 2.4.5-next.0 - -### Patch Changes - -- Updated dependencies - - @weaverse/hydrogen@2.10.1-next.0 - -## 2.4.3 - -### Patch Changes - -- Updated dependencies - - @weaverse/hydrogen@2.9.0