From 6d574b7a385ffe0d45503cd4c88f41aefc61ec89 Mon Sep 17 00:00:00 2001 From: ken Date: Wed, 19 Jun 2024 09:28:46 +0700 Subject: [PATCH 01/41] chore: add transparent header --- app/modules/Layout.tsx | 20 +++++++++++++++++--- app/modules/Logo.tsx | 8 +++++++- app/weaverse/schema.server.ts | 21 ++++++++++++++++++++- 3 files changed, 44 insertions(+), 5 deletions(-) diff --git a/app/modules/Layout.tsx b/app/modules/Layout.tsx index 4e8c36bb..bcf0dd97 100644 --- a/app/modules/Layout.tsx +++ b/app/modules/Layout.tsx @@ -36,6 +36,7 @@ import { Logo } from "./Logo"; import { PredictiveSearch } from "~/components/predictive-search/PredictiveSearch"; import { DesktopMenu } from "./menu/DesktopMenu"; import { MobileMenu } from "./menu/MobileMenu"; +import { useThemeSettings } from "@weaverse/hydrogen"; type LayoutProps = { children: React.ReactNode; @@ -224,16 +225,29 @@ function DesktopHeader({ }) { const params = useParams(); const { y } = useWindowScroll(); + let settings = useThemeSettings(); + let enableTransparent = settings?.enableTransparentHeader; + let isTransparent = enableTransparent && y < 50; return (
50 && " shadow-header", - "hidden h-nav lg:flex items-center sticky transition duration-300 backdrop-blur-lg z-40 top-0 justify-between leading-none gap-8", + enableTransparent ? "fixed" : "sticky", + isTransparent + ? "backdrop-blur-lg text-primary" + : "shadow-header text-body", + "hidden h-nav lg:flex items-center duration-300 transition-all z-40 top-0 justify-between leading-none gap-8 origin-top ease-in-out", "w-full px-6 md:px-8 lg:px-12", )} > +
diff --git a/app/modules/Logo.tsx b/app/modules/Logo.tsx index b715599d..2d7c6f6e 100644 --- a/app/modules/Logo.tsx +++ b/app/modules/Logo.tsx @@ -1,11 +1,17 @@ import { useThemeSettings } from "@weaverse/hydrogen"; import { Image } from "@shopify/hydrogen"; +import useWindowScroll from "react-use/esm/useWindowScroll"; import { Link } from "./Link"; export function Logo() { let settings = useThemeSettings(); + let enableTransparent = settings?.enableTransparentHeader; let logoData = settings?.logoData; + let transparentLogoData = settings?.transparentLogoData; + const { y } = useWindowScroll(); + let isTransparent = enableTransparent && y < 50; + if (!logoData) { return null; } @@ -17,7 +23,7 @@ export function Logo() { >
diff --git a/app/weaverse/schema.server.ts b/app/weaverse/schema.server.ts index aeb4a3fa..150143ac 100644 --- a/app/weaverse/schema.server.ts +++ b/app/weaverse/schema.server.ts @@ -75,8 +75,14 @@ export let themeSchema: HydrogenThemeSchema = { ], }, { - group: "Logo", + group: "Header", inputs: [ + { + type: "switch", + label: "Enable transparent header", + name: "enableTransparentHeader", + defaultValue: true, + }, { type: "image", name: "logoData", @@ -89,8 +95,21 @@ export let themeSchema: HydrogenThemeSchema = { height: 116, }, }, + { + type: "image", + name: "transparentLogoData", + label: "Transparent Logo", + defaultValue: { + id: "gid://shopify/MediaImage/34144817938616", + altText: "Logo", + url: "https://cdn.shopify.com/s/files/1/0838/0052/3057/files/transparent_Pilot_logo.png?v=1718763594", + width: 320, + height: 116, + }, + }, ], }, + { group: "Colors", inputs: [ From c843e81cc568e326a6950ec7d0fc0f18a19c5059 Mon Sep 17 00:00:00 2001 From: ken Date: Mon, 24 Jun 2024 09:17:23 +0700 Subject: [PATCH 02/41] chore: update transparent header --- app/modules/Layout.tsx | 25 ++++++++++++++++--------- app/modules/Logo.tsx | 10 +++------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/app/modules/Layout.tsx b/app/modules/Layout.tsx index bcf0dd97..0cfe15c3 100644 --- a/app/modules/Layout.tsx +++ b/app/modules/Layout.tsx @@ -1,7 +1,7 @@ import { Disclosure } from "@headlessui/react"; import { Await, Form, useLocation, useParams } from "@remix-run/react"; import { CartForm, useAnalytics } from "@shopify/hydrogen"; -import { Suspense, useEffect, useMemo } from "react"; +import { Suspense, useEffect, useMemo, useState } from "react"; import useWindowScroll from "react-use/esm/useWindowScroll"; import clsx from "clsx"; @@ -223,11 +223,16 @@ function DesktopHeader({ menu?: EnhancedMenu; title: string; }) { - const params = useParams(); const { y } = useWindowScroll(); let settings = useThemeSettings(); + let [hovered, setHovered] = useState(false); + let { isOpen, openDrawer, closeDrawer } = useDrawer(); + + let onHover = () => setHovered(true); + let onLeave = () => setHovered(false); + let enableTransparent = settings?.enableTransparentHeader; - let isTransparent = enableTransparent && y < 50; + let isTransparent = enableTransparent && y < 50 && !isOpen && !hovered; return (
- +
- +
@@ -274,8 +282,7 @@ function AccountLink({ className }: { className?: string }) { ); } -function SearchToggle() { - const { isOpen, closeDrawer, openDrawer } = useDrawer(); +function SearchToggle({ isOpen, openDrawer, closeDrawer }: any) { let { pathname } = useLocation(); useEffect(() => { if (isOpen) { diff --git a/app/modules/Logo.tsx b/app/modules/Logo.tsx index 2d7c6f6e..440c4b5c 100644 --- a/app/modules/Logo.tsx +++ b/app/modules/Logo.tsx @@ -1,16 +1,12 @@ import { useThemeSettings } from "@weaverse/hydrogen"; import { Image } from "@shopify/hydrogen"; -import useWindowScroll from "react-use/esm/useWindowScroll"; import { Link } from "./Link"; -export function Logo() { +export function Logo({ showTransparent }: { showTransparent: boolean }) { let settings = useThemeSettings(); - let enableTransparent = settings?.enableTransparentHeader; let logoData = settings?.logoData; let transparentLogoData = settings?.transparentLogoData; - const { y } = useWindowScroll(); - let isTransparent = enableTransparent && y < 50; if (!logoData) { return null; @@ -23,9 +19,9 @@ export function Logo() { >
From 96a9167ecc0ae3e3835ea2156bf9af379ed3fff5 Mon Sep 17 00:00:00 2001 From: ken Date: Wed, 26 Jun 2024 11:14:20 +0700 Subject: [PATCH 03/41] feat: update header with dynamic menu --- app/modules/Layout.tsx | 347 +------------------ app/modules/Logo.tsx | 2 +- app/modules/header/CartCount.tsx | 82 +++++ app/modules/header/DesktopHeader.tsx | 109 ++++++ app/modules/header/index.tsx | 180 ++++++++++ app/modules/header/menu/DesktopMenu.tsx | 225 ++++++++++++ app/modules/{ => header}/menu/MobileMenu.tsx | 5 +- app/modules/{ => header}/menu/defines.ts | 0 app/modules/menu/DesktopMenu.tsx | 175 ---------- app/root.tsx | 29 +- storefrontapi.generated.d.ts | 177 +++++++++- 11 files changed, 806 insertions(+), 525 deletions(-) create mode 100644 app/modules/header/CartCount.tsx create mode 100644 app/modules/header/DesktopHeader.tsx create mode 100644 app/modules/header/index.tsx create mode 100644 app/modules/header/menu/DesktopMenu.tsx rename app/modules/{ => header}/menu/MobileMenu.tsx (97%) rename app/modules/{ => header}/menu/defines.ts (100%) delete mode 100644 app/modules/menu/DesktopMenu.tsx diff --git a/app/modules/Layout.tsx b/app/modules/Layout.tsx index 0cfe15c3..76a024d5 100644 --- a/app/modules/Layout.tsx +++ b/app/modules/Layout.tsx @@ -1,42 +1,21 @@ import { Disclosure } from "@headlessui/react"; -import { Await, Form, useLocation, useParams } from "@remix-run/react"; -import { CartForm, useAnalytics } from "@shopify/hydrogen"; -import { Suspense, useEffect, useMemo, useState } from "react"; -import useWindowScroll from "react-use/esm/useWindowScroll"; -import clsx from "clsx"; +import { Suspense } from "react"; import { type LayoutQuery } from "storefrontapi.generated"; import { - Cart, - CartLoading, + useIsHomePath, + type ChildEnhancedMenuItem, + type EnhancedMenu, +} from "~/lib/utils"; +import { CountrySelector, - Drawer, Heading, - IconAccount, - IconBag, IconCaret, - IconLogin, - IconMenu, - IconSearch, Link, - Section, - Text, - useDrawer, + Section } from "~/modules"; -import { useCartFetchers } from "~/hooks/useCartFetchers"; -import { useIsHydrated } from "~/hooks/useIsHydrated"; -import { - type ChildEnhancedMenuItem, - type EnhancedMenu, - useIsHomePath, -} from "~/lib/utils"; -import { useRootLoaderData } from "~/root"; +import { Header } from "./header"; -import { Logo } from "./Logo"; -import { PredictiveSearch } from "~/components/predictive-search/PredictiveSearch"; -import { DesktopMenu } from "./menu/DesktopMenu"; -import { MobileMenu } from "./menu/MobileMenu"; -import { useThemeSettings } from "@weaverse/hydrogen"; type LayoutProps = { children: React.ReactNode; @@ -68,316 +47,6 @@ export function Layout({ children, layout }: LayoutProps) { ); } -function Header({ title, menu }: { title: string; menu?: EnhancedMenu }) { - const isHome = useIsHomePath(); - - const { - isOpen: isCartOpen, - openDrawer: openCart, - closeDrawer: closeCart, - } = useDrawer(); - - const { - isOpen: isMenuOpen, - openDrawer: openMenu, - closeDrawer: closeMenu, - } = useDrawer(); - - const addToCartFetchers = useCartFetchers(CartForm.ACTIONS.LinesAdd); - - // toggle cart drawer when adding to cart - useEffect(() => { - if (isCartOpen || !addToCartFetchers.length) return; - openCart(); - }, [addToCartFetchers, isCartOpen, openCart]); - - return ( - <> - - {menu && ( - - )} - - - - ); -} - -function CartDrawer({ - isOpen, - onClose, -}: { - isOpen: boolean; - onClose: () => void; -}) { - const rootData = useRootLoaderData(); - - return ( - -
- }> - - {(cart) => } - - -
-
- ); -} - -export function MenuDrawer({ - isOpen, - onClose, - menu, -}: { - isOpen: boolean; - onClose: () => void; - menu: EnhancedMenu; -}) { - return ( - - - - ); -} - -function MobileHeader({ - title, - isHome, - openCart, - openMenu, -}: { - title: string; - isHome: boolean; - openCart: () => void; - openMenu: () => void; -}) { - // useHeaderStyleFix(containerStyle, setContainerStyle, isHome); - - const params = useParams(); - - return ( -
-
- -
- -
-
- - - -
- - -
-
- ); -} - -function DesktopHeader({ - isHome, - menu, - openCart, - title, -}: { - isHome: boolean; - openCart: () => void; - menu?: EnhancedMenu; - title: string; -}) { - const { y } = useWindowScroll(); - let settings = useThemeSettings(); - let [hovered, setHovered] = useState(false); - let { isOpen, openDrawer, closeDrawer } = useDrawer(); - - let onHover = () => setHovered(true); - let onLeave = () => setHovered(false); - - let enableTransparent = settings?.enableTransparentHeader; - let isTransparent = enableTransparent && y < 50 && !isOpen && !hovered; - return ( -
-
- - -
- - - -
-
- ); -} - -function AccountLink({ className }: { className?: string }) { - const rootData = useRootLoaderData(); - const isLoggedIn = rootData?.isLoggedIn; - - return ( - - }> - }> - {(isLoggedIn) => (isLoggedIn ? : )} - - - - ); -} - -function SearchToggle({ isOpen, openDrawer, closeDrawer }: any) { - let { pathname } = useLocation(); - useEffect(() => { - if (isOpen) { - closeDrawer(); - } - }, [pathname]); - return ( - <> - - - - - - ); -} - -function CartCount({ - isHome, - openCart, -}: { - isHome: boolean; - openCart: () => void; -}) { - const rootData = useRootLoaderData(); - - return ( - }> - - {(cart) => ( - - )} - - - ); -} - -function Badge({ - openCart, - dark, - count, - cart, -}: { - count: number; - dark: boolean; - openCart: () => void; - cart: any; -}) { - const isHydrated = useIsHydrated(); - - const BadgeCounter = useMemo( - () => ( - <> - -
- {count || 0} -
- - ), - // eslint-disable-next-line react-hooks/exhaustive-deps - [count, dark], - ); - - const { publish } = useAnalytics(); - - function handleOpenCart() { - publish("custom_sidecart_viewed", { cart }); - openCart(); - } - - return isHydrated ? ( - - ) : ( - - {BadgeCounter} - - ); -} function Footer({ menu }: { menu?: EnhancedMenu }) { const isHome = useIsHomePath(); diff --git a/app/modules/Logo.tsx b/app/modules/Logo.tsx index 440c4b5c..d9685128 100644 --- a/app/modules/Logo.tsx +++ b/app/modules/Logo.tsx @@ -3,7 +3,7 @@ import { Image } from "@shopify/hydrogen"; import { Link } from "./Link"; -export function Logo({ showTransparent }: { showTransparent: boolean }) { +export function Logo({ showTransparent }: { showTransparent?: boolean }) { let settings = useThemeSettings(); let logoData = settings?.logoData; let transparentLogoData = settings?.transparentLogoData; diff --git a/app/modules/header/CartCount.tsx b/app/modules/header/CartCount.tsx new file mode 100644 index 00000000..b46ed410 --- /dev/null +++ b/app/modules/header/CartCount.tsx @@ -0,0 +1,82 @@ +import { Await, Link } from "@remix-run/react"; +import { useAnalytics } from "@shopify/hydrogen"; +import { Suspense, useMemo } from "react"; +import { useIsHydrated } from "~/hooks/useIsHydrated"; +import { useRootLoaderData } from "~/root"; +import { IconBag } from "../Icon"; + + +export function CartCount({ + isHome, + openCart, +}: { + isHome: boolean; + openCart: () => void; +}) { + const rootData = useRootLoaderData(); + + return ( + }> + + {(cart) => ( + + )} + + + ); +} + +function Badge({ + openCart, + dark, + count, + cart, +}: { + count: number; + dark: boolean; + openCart: () => void; + cart: any; +}) { + const isHydrated = useIsHydrated(); + + const BadgeCounter = useMemo( + () => ( + <> + +
+ {count || 0} +
+ + ), + // eslint-disable-next-line react-hooks/exhaustive-deps + [count, dark], + ); + + const { publish } = useAnalytics(); + + function handleOpenCart() { + publish("custom_sidecart_viewed", { cart }); + openCart(); + } + + return isHydrated ? ( + + ) : ( + + {BadgeCounter} + + ); +} diff --git a/app/modules/header/DesktopHeader.tsx b/app/modules/header/DesktopHeader.tsx new file mode 100644 index 00000000..197fc095 --- /dev/null +++ b/app/modules/header/DesktopHeader.tsx @@ -0,0 +1,109 @@ +import { Await, Link, useLoaderData, useLocation } from "@remix-run/react"; +import { useThemeSettings } from "@weaverse/hydrogen"; +import clsx from "clsx"; +import { Suspense, useEffect, useState } from "react"; +import useWindowScroll from "react-use/esm/useWindowScroll"; +import { PredictiveSearch } from "~/components/predictive-search/PredictiveSearch"; +import { type EnhancedMenu } from "~/lib/utils"; +import { useRootLoaderData } from "~/root"; +import { Drawer, useDrawer } from "../Drawer"; +import { IconAccount, IconLogin, IconSearch } from "../Icon"; +import { Logo } from "../Logo"; +import { CartCount } from "./CartCount"; +import { DesktopMenu } from "./menu/DesktopMenu"; + +export function DesktopHeader({ + isHome, + menu, + openCart, + title, +}: { + isHome: boolean; + openCart: () => void; + menu?: EnhancedMenu; + title: string; +}) { + const { y } = useWindowScroll(); + let settings = useThemeSettings(); + let [hovered, setHovered] = useState(false); + let { isOpen, openDrawer, closeDrawer } = useDrawer(); + + let onHover = () => setHovered(true); + let onLeave = () => setHovered(false); + + let enableTransparent = settings?.enableTransparentHeader; + let isTransparent = enableTransparent && y < 50 && !isOpen && !hovered; + return ( +
+
+ + +
+ + + +
+
+ ); +} + +function AccountLink({ className }: { className?: string }) { + const rootData = useRootLoaderData(); + const isLoggedIn = rootData?.isLoggedIn; + + return ( + + }> + }> + {(isLoggedIn) => (isLoggedIn ? : )} + + + + ); +} + +function SearchToggle({ isOpen, openDrawer, closeDrawer }: any) { + let { pathname } = useLocation(); + useEffect(() => { + if (isOpen) { + closeDrawer(); + } + }, [pathname]); + return ( + <> + + + + + + ); +} diff --git a/app/modules/header/index.tsx b/app/modules/header/index.tsx new file mode 100644 index 00000000..46e78735 --- /dev/null +++ b/app/modules/header/index.tsx @@ -0,0 +1,180 @@ +import { Await, Form, Link, useParams } from "@remix-run/react"; +import { CartForm } from "@shopify/hydrogen"; +import { Suspense, useEffect } from "react"; +import { useCartFetchers } from "~/hooks/useCartFetchers"; +import { useIsHomePath, type EnhancedMenu } from "~/lib/utils"; +import { useRootLoaderData } from "~/root"; +import { Cart } from "../Cart"; +import { CartLoading } from "../CartLoading"; +import { Drawer, useDrawer } from "../Drawer"; +import { IconAccount, IconLogin, IconMenu, IconSearch } from "../Icon"; +import { Logo } from "../Logo"; +import { CartCount } from "./CartCount"; +import { DesktopHeader } from "./DesktopHeader"; +import { MobileMenu } from "./menu/MobileMenu"; + +export function Header({ + title, + menu, +}: { + title: string; + menu?: EnhancedMenu; +}) { + const isHome = useIsHomePath(); + + const { + isOpen: isCartOpen, + openDrawer: openCart, + closeDrawer: closeCart, + } = useDrawer(); + + const { + isOpen: isMenuOpen, + openDrawer: openMenu, + closeDrawer: closeMenu, + } = useDrawer(); + + const addToCartFetchers = useCartFetchers(CartForm.ACTIONS.LinesAdd); + + // toggle cart drawer when adding to cart + useEffect(() => { + if (isCartOpen || !addToCartFetchers.length) return; + openCart(); + }, [addToCartFetchers, isCartOpen, openCart]); + + return ( + <> + + {menu && ( + + )} + + + + ); +} + +function CartDrawer({ + isOpen, + onClose, +}: { + isOpen: boolean; + onClose: () => void; +}) { + const rootData = useRootLoaderData(); + + return ( + +
+ }> + + {(cart) => } + + +
+
+ ); +} + +export function MenuDrawer({ + isOpen, + onClose, + menu, +}: { + isOpen: boolean; + onClose: () => void; + menu: EnhancedMenu; +}) { + return ( + + + + ); +} + +function MobileHeader({ + title, + isHome, + openCart, + openMenu, +}: { + title: string; + isHome: boolean; + openCart: () => void; + openMenu: () => void; +}) { + // useHeaderStyleFix(containerStyle, setContainerStyle, isHome); + + const params = useParams(); + + return ( +
+
+ +
+ +
+
+ + + +
+ + +
+
+ ); +} + + +function AccountLink({ className }: { className?: string }) { + const rootData = useRootLoaderData(); + const isLoggedIn = rootData?.isLoggedIn; + + return ( + + }> + }> + {(isLoggedIn) => (isLoggedIn ? : )} + + + + ); +} diff --git a/app/modules/header/menu/DesktopMenu.tsx b/app/modules/header/menu/DesktopMenu.tsx new file mode 100644 index 00000000..04b9b11a --- /dev/null +++ b/app/modules/header/menu/DesktopMenu.tsx @@ -0,0 +1,225 @@ +import { Link, useLoaderData } from "@remix-run/react"; +import { Image } from "@shopify/hydrogen"; +import { + type ImageMenuProps, + type MultiMenuProps, + type SingleMenuProps, +} from "./defines"; +import clsx from "clsx"; +import type { loader } from "~/root"; + +function getMaxDepth(item) { + if (item.items?.length > 0) { + return Math.max(...item.items.map(getMaxDepth)) + 1; + } + return 1; +} + +const commonAnimatedClass = + "absolute h-0 opacity-0 overflow-hidden bg-white shadow-md transition-all ease-out group-hover:opacity-100 group-hover:border-t duration-500 group-hover:duration-300 group-hover:z-50 "; + +// function addPrefixForEachClass(prefix: string, className: string,) { +// return className.split(" ").map((className) => `${prefix}${className}`).join(" "); +// } + +// const commonAnimatedClass = clsx( +// "absolute -translate-y-full overflow-hidden bg-white shadow-md transition-all ease-out duration-300", +// addPrefixForEachClass("group-hover:", "opacity-100 border-t z-50 translate-y-0"), +// ) + +export function DesktopMenu() { + const data = useLoaderData(); + let menu = data?.layout?.headerMenu; + let items = menu?.items; + if (!items) return null; + return ( + + ); +} + +function ItemHeader({ title, to }: { title: string; to: string }) { + return ( +
+ +
+ ); +} + +function MultiMenu(props: MultiMenuProps) { + let { title, items, to, imageItems } = props; + + let renderList = (item, id) => ( +
+
+ + {item.title} + +
+
    + {item.items.map((subItem, ind) => ( +
  • + + {subItem.title} + +
  • + ))} +
+
+ ); + + let renderImageItem = (item, id) => ( +
+ + + +
+ {item.title} +
+
+ ); + return ( +
+ +
+
+
+ {items.map((item, id) => + item.resource && item.items.length === 0 + ? renderImageItem(item, id) + : renderList(item, id), + )} + {imageItems?.map((item, id) => ( +
+ + + +
+ {item.title} +
+
+ ))} +
+
+
+
+
+ ); +} + +function SingleMenu(props: SingleMenuProps) { + let { title, items, to } = props; + return ( +
+ +
+
+
+
+ + {title} + +
+
    + {items.map((subItem, ind) => ( +
  • + + {subItem.title} + +
  • + ))} +
+
+
+
+
+ ); +} + +function ImageMenu({ title, items, to }: ImageMenuProps) { + return ( +
+ +
+
+
+ {items.map((item, id) => ( + +
+ +
+ {item.title} +
+
+ + ))} +
+
+
+
+ ); +} diff --git a/app/modules/menu/MobileMenu.tsx b/app/modules/header/menu/MobileMenu.tsx similarity index 97% rename from app/modules/menu/MobileMenu.tsx rename to app/modules/header/menu/MobileMenu.tsx index 9f125236..dca35cbb 100644 --- a/app/modules/menu/MobileMenu.tsx +++ b/app/modules/header/menu/MobileMenu.tsx @@ -1,14 +1,15 @@ import { Disclosure } from "@headlessui/react"; import { Link } from "@remix-run/react"; import { Image } from "@shopify/hydrogen"; -import { IconCaret, IconCaretDown, IconCaretRight } from "~/components/Icons"; -import { Drawer, useDrawer } from "../Drawer"; +import { IconCaretDown, IconCaretRight } from "~/components/Icons"; import { Nav_Items, type ImageItem, type MultiMenuProps, type SingleMenuProps, } from "./defines"; +import { Drawer, useDrawer } from "~/modules/Drawer"; +import { IconCaret } from "~/modules/Icon"; const MenuByType = { multi: MultiMenu, diff --git a/app/modules/menu/defines.ts b/app/modules/header/menu/defines.ts similarity index 100% rename from app/modules/menu/defines.ts rename to app/modules/header/menu/defines.ts diff --git a/app/modules/menu/DesktopMenu.tsx b/app/modules/menu/DesktopMenu.tsx deleted file mode 100644 index d166b68b..00000000 --- a/app/modules/menu/DesktopMenu.tsx +++ /dev/null @@ -1,175 +0,0 @@ -import { Link } from "@remix-run/react"; -import { Image } from "@shopify/hydrogen"; -import { - Nav_Items, - type ImageMenuProps, - type MultiMenuProps, - type SingleMenuProps, -} from "./defines"; -import clsx from "clsx"; - -const MenuByType = { - multi: MultiMenu, - image: ImageMenu, - single: SingleMenu, -}; - -const commonAnimatedClass = - "absolute h-0 opacity-0 overflow-hidden bg-white shadow-md transition-all ease-out group-hover:opacity-100 group-hover:border-t duration-500 group-hover:duration-300 group-hover:z-50 "; - -export function DesktopMenu() { - return ( - - ); -} - -function ItemHeader({ title, to }: { title: string; to: string }) { - return ( -
- -
- ); -} - -function MultiMenu(props: MultiMenuProps) { - let { title, items, to, imageItems } = props; - return ( -
- -
-
-
- {items.map((item, id) => ( -
-
- - {item.title} - -
-
    - {item.items.map((subItem, ind) => ( -
  • - - {subItem.title} - -
  • - ))} -
-
- ))} - {imageItems.map((item, id) => ( -
- - - -
- {item.title} -
-
- ))} -
-
-
-
-
- ); -} - -function SingleMenu(props: SingleMenuProps) { - let { title, items, to } = props; - return ( -
- -
-
-
-
- - {title} - -
-
    - {items.map((subItem, ind) => ( -
  • - - {subItem.title} - -
  • - ))} -
-
-
-
-
- ); -} - -function ImageMenu({ title, imageItems, to }: ImageMenuProps) { - return ( -
- -
-
-
- {imageItems.map((item, id) => ( - -
- -
- {item.title} -
-
- - ))} -
-
-
-
- ); -} diff --git a/app/root.tsx b/app/root.tsx index 832aac0a..d0596259 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -255,20 +255,47 @@ const LAYOUT_QUERY = `#graphql fragment MenuItem on MenuItem { id resourceId + resource { + ... on Collection { + image { + altText + height + id + url + width + } + } + ... on Product { + image: featuredImage { + altText + height + id + url + width + } + } + } tags title type url } + fragment ChildMenuItem on MenuItem { ...MenuItem } - fragment ParentMenuItem on MenuItem { + fragment ParentMenuItem2 on MenuItem { ...MenuItem items { ...ChildMenuItem } } + fragment ParentMenuItem on MenuItem { + ...MenuItem + items { + ...ParentMenuItem2 + } + } fragment Menu on Menu { id items { diff --git a/storefrontapi.generated.d.ts b/storefrontapi.generated.d.ts index 3fcd6dcc..727e9547 100644 --- a/storefrontapi.generated.d.ts +++ b/storefrontapi.generated.d.ts @@ -1003,8 +1003,40 @@ export type LayoutQuery = { Pick< StorefrontAPI.MenuItem, 'id' | 'resourceId' | 'tags' | 'title' | 'type' | 'url' - > + > & { + items: Array< + Pick< + StorefrontAPI.MenuItem, + 'id' | 'resourceId' | 'tags' | 'title' | 'type' | 'url' + > & { + resource?: StorefrontAPI.Maybe<{ + image?: StorefrontAPI.Maybe< + Pick< + StorefrontAPI.Image, + 'altText' | 'height' | 'id' | 'url' | 'width' + > + >; + }>; + } + >; + resource?: StorefrontAPI.Maybe<{ + image?: StorefrontAPI.Maybe< + Pick< + StorefrontAPI.Image, + 'altText' | 'height' | 'id' | 'url' | 'width' + > + >; + }>; + } >; + resource?: StorefrontAPI.Maybe<{ + image?: StorefrontAPI.Maybe< + Pick< + StorefrontAPI.Image, + 'altText' | 'height' | 'id' | 'url' | 'width' + > + >; + }>; } >; } @@ -1020,8 +1052,40 @@ export type LayoutQuery = { Pick< StorefrontAPI.MenuItem, 'id' | 'resourceId' | 'tags' | 'title' | 'type' | 'url' - > + > & { + items: Array< + Pick< + StorefrontAPI.MenuItem, + 'id' | 'resourceId' | 'tags' | 'title' | 'type' | 'url' + > & { + resource?: StorefrontAPI.Maybe<{ + image?: StorefrontAPI.Maybe< + Pick< + StorefrontAPI.Image, + 'altText' | 'height' | 'id' | 'url' | 'width' + > + >; + }>; + } + >; + resource?: StorefrontAPI.Maybe<{ + image?: StorefrontAPI.Maybe< + Pick< + StorefrontAPI.Image, + 'altText' | 'height' | 'id' | 'url' | 'width' + > + >; + }>; + } >; + resource?: StorefrontAPI.Maybe<{ + image?: StorefrontAPI.Maybe< + Pick< + StorefrontAPI.Image, + 'altText' | 'height' | 'id' | 'url' | 'width' + > + >; + }>; } >; } @@ -1043,12 +1107,50 @@ export type ShopFragment = Pick< export type MenuItemFragment = Pick< StorefrontAPI.MenuItem, 'id' | 'resourceId' | 'tags' | 'title' | 'type' | 'url' ->; +> & { + resource?: StorefrontAPI.Maybe<{ + image?: StorefrontAPI.Maybe< + Pick + >; + }>; +}; export type ChildMenuItemFragment = Pick< StorefrontAPI.MenuItem, 'id' | 'resourceId' | 'tags' | 'title' | 'type' | 'url' ->; +> & { + resource?: StorefrontAPI.Maybe<{ + image?: StorefrontAPI.Maybe< + Pick + >; + }>; +}; + +export type ParentMenuItem2Fragment = Pick< + StorefrontAPI.MenuItem, + 'id' | 'resourceId' | 'tags' | 'title' | 'type' | 'url' +> & { + items: Array< + Pick< + StorefrontAPI.MenuItem, + 'id' | 'resourceId' | 'tags' | 'title' | 'type' | 'url' + > & { + resource?: StorefrontAPI.Maybe<{ + image?: StorefrontAPI.Maybe< + Pick< + StorefrontAPI.Image, + 'altText' | 'height' | 'id' | 'url' | 'width' + > + >; + }>; + } + >; + resource?: StorefrontAPI.Maybe<{ + image?: StorefrontAPI.Maybe< + Pick + >; + }>; +}; export type ParentMenuItemFragment = Pick< StorefrontAPI.MenuItem, @@ -1058,8 +1160,37 @@ export type ParentMenuItemFragment = Pick< Pick< StorefrontAPI.MenuItem, 'id' | 'resourceId' | 'tags' | 'title' | 'type' | 'url' - > + > & { + items: Array< + Pick< + StorefrontAPI.MenuItem, + 'id' | 'resourceId' | 'tags' | 'title' | 'type' | 'url' + > & { + resource?: StorefrontAPI.Maybe<{ + image?: StorefrontAPI.Maybe< + Pick< + StorefrontAPI.Image, + 'altText' | 'height' | 'id' | 'url' | 'width' + > + >; + }>; + } + >; + resource?: StorefrontAPI.Maybe<{ + image?: StorefrontAPI.Maybe< + Pick< + StorefrontAPI.Image, + 'altText' | 'height' | 'id' | 'url' | 'width' + > + >; + }>; + } >; + resource?: StorefrontAPI.Maybe<{ + image?: StorefrontAPI.Maybe< + Pick + >; + }>; }; export type MenuFragment = Pick & { @@ -1072,8 +1203,40 @@ export type MenuFragment = Pick & { Pick< StorefrontAPI.MenuItem, 'id' | 'resourceId' | 'tags' | 'title' | 'type' | 'url' - > + > & { + items: Array< + Pick< + StorefrontAPI.MenuItem, + 'id' | 'resourceId' | 'tags' | 'title' | 'type' | 'url' + > & { + resource?: StorefrontAPI.Maybe<{ + image?: StorefrontAPI.Maybe< + Pick< + StorefrontAPI.Image, + 'altText' | 'height' | 'id' | 'url' | 'width' + > + >; + }>; + } + >; + resource?: StorefrontAPI.Maybe<{ + image?: StorefrontAPI.Maybe< + Pick< + StorefrontAPI.Image, + 'altText' | 'height' | 'id' | 'url' | 'width' + > + >; + }>; + } >; + resource?: StorefrontAPI.Maybe<{ + image?: StorefrontAPI.Maybe< + Pick< + StorefrontAPI.Image, + 'altText' | 'height' | 'id' | 'url' | 'width' + > + >; + }>; } >; }; @@ -1574,7 +1737,7 @@ interface GeneratedQueryTypes { return: MetaObjectsQuery; variables: MetaObjectsQueryVariables; }; - '#graphql\n query layout(\n $language: LanguageCode\n $headerMenuHandle: String!\n $footerMenuHandle: String!\n ) @inContext(language: $language) {\n shop {\n ...Shop\n }\n headerMenu: menu(handle: $headerMenuHandle) {\n ...Menu\n }\n footerMenu: menu(handle: $footerMenuHandle) {\n ...Menu\n }\n }\n fragment Shop on Shop {\n id\n name\n description\n primaryDomain {\n url\n }\n brand {\n logo {\n image {\n url\n }\n }\n }\n }\n fragment MenuItem on MenuItem {\n id\n resourceId\n tags\n title\n type\n url\n }\n fragment ChildMenuItem on MenuItem {\n ...MenuItem\n }\n fragment ParentMenuItem on MenuItem {\n ...MenuItem\n items {\n ...ChildMenuItem\n }\n }\n fragment Menu on Menu {\n id\n items {\n ...ParentMenuItem\n }\n }\n': { + '#graphql\n query layout(\n $language: LanguageCode\n $headerMenuHandle: String!\n $footerMenuHandle: String!\n ) @inContext(language: $language) {\n shop {\n ...Shop\n }\n headerMenu: menu(handle: $headerMenuHandle) {\n ...Menu\n }\n footerMenu: menu(handle: $footerMenuHandle) {\n ...Menu\n }\n }\n fragment Shop on Shop {\n id\n name\n description\n primaryDomain {\n url\n }\n brand {\n logo {\n image {\n url\n }\n }\n }\n }\n fragment MenuItem on MenuItem {\n id\n resourceId\n resource {\n ... on Collection {\n image {\n altText\n height\n id\n url\n width\n }\n }\n ... on Product {\n image: featuredImage {\n altText\n height\n id\n url\n width\n }\n }\n }\n tags\n title\n type\n url\n }\n \n fragment ChildMenuItem on MenuItem {\n ...MenuItem\n }\n fragment ParentMenuItem2 on MenuItem {\n ...MenuItem\n items {\n ...ChildMenuItem\n }\n }\n fragment ParentMenuItem on MenuItem {\n ...MenuItem\n items {\n ...ParentMenuItem2\n }\n }\n fragment Menu on Menu {\n id\n items {\n ...ParentMenuItem\n }\n }\n': { return: LayoutQuery; variables: LayoutQueryVariables; }; From 33ae2c16627fbfbe55eb9a4f396840e0720dff6d Mon Sep 17 00:00:00 2001 From: hta218 Date: Fri, 28 Jun 2024 14:44:44 +0700 Subject: [PATCH 04/41] Update Slideshow presets --- app/sections/slideshow/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/sections/slideshow/index.tsx b/app/sections/slideshow/index.tsx index ad4f2f65..70658c9c 100644 --- a/app/sections/slideshow/index.tsx +++ b/app/sections/slideshow/index.tsx @@ -318,7 +318,7 @@ export let schema: HydrogenComponentSchema = { { type: "slideshow-slide", verticalPadding: "large", - contentPosition: "bottom left", + contentPosition: "center center", backgroundImage: IMAGES_PLACEHOLDERS.banner_2, backgroundFit: "cover", enableOverlay: true, From 99b3c85ad4df6a37a0fcfaa7dae512b1a8746743 Mon Sep 17 00:00:00 2001 From: Paul Date: Fri, 28 Jun 2024 16:33:05 +0700 Subject: [PATCH 05/41] chore: optimize video load --- app/sections/hero-video.tsx | 43 +++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/app/sections/hero-video.tsx b/app/sections/hero-video.tsx index db170235..6c8a808f 100644 --- a/app/sections/hero-video.tsx +++ b/app/sections/hero-video.tsx @@ -6,9 +6,10 @@ import type { VariantProps } from "class-variance-authority"; import { cva } from "class-variance-authority"; import clsx from "clsx"; import type { CSSProperties } from "react"; -import { Suspense, forwardRef, lazy } from "react"; +import { Suspense, forwardRef, lazy, useCallback } from "react"; import type { OverlayProps } from "~/components/Overlay"; import { Overlay, overlayInputs } from "~/components/Overlay"; +import { useInView } from "react-intersection-observer"; const HEIGHTS = { small: { @@ -92,10 +93,22 @@ let HeroVideo = forwardRef((props, ref) => { "--desktop-height": desktopHeight, "--mobile-height": mobileHeight, } as CSSProperties; + const { ref: inViewRef, inView } = useInView(); + + // Use `useCallback` so we don't recreate the function on each render + const setRefs = useCallback( + (node: HTMLElement) => { + // Ref's from useRef needs to have the node assigned to `current` + ref && Object.assign(ref, { current: node }); + // Callback refs, like the one from `useInView`, is a function that takes the node as an argument + inViewRef(node); + }, + [inViewRef], + ); return (
((props, ref) => { "translate-x-[min(0px,calc((var(--mobile-height)/9*16-100vw)/-2))] sm:translate-x-[min(0px,calc((var(--desktop-height)/9*16-100vw)/-2))]", )} > - - - + {inView && ( + + + + )} Date: Fri, 28 Jun 2024 17:36:48 +0700 Subject: [PATCH 06/41] chore: update hero-video --- app/sections/hero-video.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/sections/hero-video.tsx b/app/sections/hero-video.tsx index 6c8a808f..251ade88 100644 --- a/app/sections/hero-video.tsx +++ b/app/sections/hero-video.tsx @@ -93,7 +93,10 @@ let HeroVideo = forwardRef((props, ref) => { "--desktop-height": desktopHeight, "--mobile-height": mobileHeight, } as CSSProperties; - const { ref: inViewRef, inView } = useInView(); + const { ref: inViewRef, inView } = useInView({ + threshold: 0, + triggerOnce: true, + }); // Use `useCallback` so we don't recreate the function on each render const setRefs = useCallback( From 7f3446c63aa06ce748035d280b8740c2a97e2b13 Mon Sep 17 00:00:00 2001 From: Paul Date: Fri, 28 Jun 2024 19:00:29 +0700 Subject: [PATCH 07/41] rename JUDGEME_PUBLIC_TOKEN JUDGEME_PRIVATE_API_TOKEN --- .env.example | 2 +- app/lib/judgeme.ts | 2 +- app/routes/($locale).api.review.$productHandle.tsx | 2 +- app/routes/($locale).products.$productHandle.tsx | 2 +- app/sections/hero-video.tsx | 1 - env.d.ts | 2 +- 6 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.env.example b/.env.example index e6aa9ecf..31a4eaf3 100644 --- a/.env.example +++ b/.env.example @@ -15,5 +15,5 @@ WEAVERSE_PROJECT_ID="your-project-id" # Additional services #PUBLIC_GOOGLE_GTM_ID=G-R1KFYYKE48 -#JUDGEME_PUBLIC_TOKEN="your-judgeme-public-token" +#JUDGEME_PRIVATE_API_TOKEN="your-judgeme-private-api-token" diff --git a/app/lib/judgeme.ts b/app/lib/judgeme.ts index 50690ca7..a69a4fcb 100644 --- a/app/lib/judgeme.ts +++ b/app/lib/judgeme.ts @@ -34,7 +34,7 @@ export let getJudgemeReviews = async ( ) => { if (!api_token) { return { - error: "Missing JUDGEME_PUBLIC_TOKEN", + error: "Missing JUDGEME_PRIVATE_API_TOKEN", }; } let internalId = await getInternalIdByHandle(api_token, shop_domain, handle); diff --git a/app/routes/($locale).api.review.$productHandle.tsx b/app/routes/($locale).api.review.$productHandle.tsx index eef7e14c..655223bc 100644 --- a/app/routes/($locale).api.review.$productHandle.tsx +++ b/app/routes/($locale).api.review.$productHandle.tsx @@ -7,7 +7,7 @@ export async function loader(args: RouteLoaderArgs) { let { params, context } = args; let env = context.env; let handle = params.productHandle; - let api_token = env.JUDGEME_PUBLIC_TOKEN; + let api_token = env.JUDGEME_PRIVATE_API_TOKEN; let shop_domain = env.PUBLIC_STORE_DOMAIN; invariant(handle, "Missing product handle"); let reviews = await getJudgemeReviews(api_token, shop_domain, handle); diff --git a/app/routes/($locale).products.$productHandle.tsx b/app/routes/($locale).products.$productHandle.tsx index 915690e1..2d4ebdf2 100644 --- a/app/routes/($locale).products.$productHandle.tsx +++ b/app/routes/($locale).products.$productHandle.tsx @@ -72,7 +72,7 @@ export async function loader({ params, request, context }: LoaderFunctionArgs) { url: request.url, }); - let judgeme_API_TOKEN = context.env.JUDGEME_PUBLIC_TOKEN; + let judgeme_API_TOKEN = context.env.JUDGEME_PRIVATE_API_TOKEN; let judgemeReviews = null; if (judgeme_API_TOKEN) { let shop_domain = context.env.PUBLIC_STORE_DOMAIN; diff --git a/app/sections/hero-video.tsx b/app/sections/hero-video.tsx index 251ade88..65d5ae7b 100644 --- a/app/sections/hero-video.tsx +++ b/app/sections/hero-video.tsx @@ -94,7 +94,6 @@ let HeroVideo = forwardRef((props, ref) => { "--mobile-height": mobileHeight, } as CSSProperties; const { ref: inViewRef, inView } = useInView({ - threshold: 0, triggerOnce: true, }); diff --git a/env.d.ts b/env.d.ts index 04b47d5e..0061f0cc 100644 --- a/env.d.ts +++ b/env.d.ts @@ -34,7 +34,7 @@ declare global { WEAVERSE_PROJECT_ID: string; WEAVERSE_HOST: string; WEAVERSE_API_KEY: string; - JUDGEME_PUBLIC_TOKEN: string; + JUDGEME_PRIVATE_API_TOKEN: string; PUBLIC_GOOGLE_GTM_ID: string; } } From b5750f48ffd2ef82693a2f29d6ebf73da4d7a1ba Mon Sep 17 00:00:00 2001 From: hta218 Date: Sun, 30 Jun 2024 09:38:09 +0700 Subject: [PATCH 08/41] Remove limit and enabledOn properties from contact-form, featured-collections, and featured-products sections --- app/sections/contact-form.tsx | 3 --- app/sections/featured-collections.tsx | 4 ---- app/sections/featured-products.tsx | 4 ---- 3 files changed, 11 deletions(-) diff --git a/app/sections/contact-form.tsx b/app/sections/contact-form.tsx index d13f5d6e..fe3b07ec 100644 --- a/app/sections/contact-form.tsx +++ b/app/sections/contact-form.tsx @@ -48,9 +48,6 @@ export let schema: HydrogenComponentSchema = { type: "contact-form", title: "Contact form", limit: 1, - enabledOn: { - pages: ["INDEX"], - }, inspector: [ // { // group: 'Contact form', diff --git a/app/sections/featured-collections.tsx b/app/sections/featured-collections.tsx index 69e4558a..2da17ce7 100644 --- a/app/sections/featured-collections.tsx +++ b/app/sections/featured-collections.tsx @@ -50,10 +50,6 @@ export let loader = async ({ weaverse }: ComponentLoaderArgs) => { export let schema: HydrogenComponentSchema = { type: "featured-collections", title: "Featured collections", - limit: 1, - enabledOn: { - pages: ["INDEX"], - }, inspector: [ { group: "Featured collections", diff --git a/app/sections/featured-products.tsx b/app/sections/featured-products.tsx index d46d72fe..e3b95f79 100644 --- a/app/sections/featured-products.tsx +++ b/app/sections/featured-products.tsx @@ -50,10 +50,6 @@ export let loader = async ({ weaverse }: ComponentLoaderArgs) => { export let schema: HydrogenComponentSchema = { type: "featured-products", title: "Featured products", - limit: 1, - enabledOn: { - pages: ["INDEX"], - }, inspector: [ { group: "Featured products", From e31f053d34e00b6f3e28995eb38557c5af253f75 Mon Sep 17 00:00:00 2001 From: hta218 Date: Sun, 30 Jun 2024 20:57:14 +0700 Subject: [PATCH 09/41] Remove `ALI_REVIEWS_API_KEY` from env.d.ts --- env.d.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/env.d.ts b/env.d.ts index 0061f0cc..0a33ab56 100644 --- a/env.d.ts +++ b/env.d.ts @@ -30,7 +30,6 @@ declare global { PUBLIC_CUSTOMER_ACCOUNT_API_URL: string; PUBLIC_CHECKOUT_DOMAIN: string; - ALI_REVIEWS_API_KEY: string; WEAVERSE_PROJECT_ID: string; WEAVERSE_HOST: string; WEAVERSE_API_KEY: string; From 22649465d75b61496e03e6e19e9092d37146caf0 Mon Sep 17 00:00:00 2001 From: hta218 Date: Sun, 30 Jun 2024 20:57:32 +0700 Subject: [PATCH 10/41] Update AliReviews section to fetch reviews from Ali Reviews API with API key --- app/sections/ali-reviews/index.tsx | 61 ++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 19 deletions(-) diff --git a/app/sections/ali-reviews/index.tsx b/app/sections/ali-reviews/index.tsx index 2725dcbb..be3ce2c4 100644 --- a/app/sections/ali-reviews/index.tsx +++ b/app/sections/ali-reviews/index.tsx @@ -8,11 +8,16 @@ import type { SectionProps } from "~/components/Section"; import { Section, layoutInputs } from "~/components/Section"; import { type AliReview } from "./review-item"; -type AliReviewsProps = SectionProps>>; +type AliReviewsData = { + aliReviewsApiKey: string; +}; + +type AliReviewsProps = SectionProps>> & + AliReviewsData; let AliReviewSection = forwardRef( (props, ref) => { - let { children, loaderData, ...rest } = props; + let { children, loaderData, aliReviewsApiKey, ...rest } = props; return (
{children} @@ -22,28 +27,32 @@ let AliReviewSection = forwardRef( ); export type AliReviewsLoaderData = Awaited>; +type AliReviewsAPIPayload = { + data: { reviews: AliReview[]; cursor: string }; + message: string; + status: number; +}; -export let loader = async ({ weaverse }: ComponentLoaderArgs<{}, Env>) => { +export let loader = async ({ + data: { aliReviewsApiKey = "" }, + weaverse, +}: ComponentLoaderArgs) => { let res = await weaverse - .fetchWithCache<{ - data: { reviews: AliReview[]; cursor: string }; - message: string; - status: number; - }>("https://widget-hub-api.alireviews.io/api/public/reviews", { - method: "GET", - headers: { - // https://support.fireapps.io/en/article/ali-reviews-learn-more-about-integration-using-api-key-hklfr0/ - Authorization: `Bearer ${weaverse.env.ALI_REVIEWS_API_KEY ?? ""}`, - "Content-Type": "application/json", + .fetchWithCache( + "https://widget-hub-api.alireviews.io/api/public/reviews", + { + method: "GET", + headers: { + Authorization: `Bearer ${aliReviewsApiKey}`, + "Content-Type": "application/json", + }, }, - }) + ) .catch((err) => { - console.log("🚀 ~ loader ~ err", err); + console.error(err); return { data: { reviews: [], cursor: "" }, message: "", status: 0 }; }); - let { data } = res; - - return data.reviews; + return res?.data?.reviews; }; export default AliReviewSection; @@ -52,6 +61,20 @@ export let schema: HydrogenComponentSchema = { type: "ali-reviews", title: "Ali Reviews box", inspector: [ + { + group: "Integration", + inputs: [ + { + type: "text", + name: "aliReviewsApiKey", + label: "Ali Reviews API key", + defaultValue: "", + placeholder: "Your Ali Reviews API key", + helpText: `Learn how to get your API key from Ali Reviews app.`, + shouldRevalidate: true, + }, + ], + }, { group: "Layout", inputs: layoutInputs.filter((inp) => inp.name !== "borderRadius"), @@ -75,7 +98,7 @@ export let schema: HydrogenComponentSchema = { { type: "paragraph", content: - "This section demonstrates how to integrate with third-party apps using their public APIs. Reviews are fetched from Ali Reviews API.", + "This section demonstrates how to integrate with third-party apps using their public APIs. Reviews are fetched from Ali Reviews API on the server side.", }, { type: "ali-reviews--list", From 7e3db004f178501606314aaa19318289a264f64c Mon Sep 17 00:00:00 2001 From: hta218 Date: Sun, 30 Jun 2024 20:57:38 +0700 Subject: [PATCH 11/41] Update Slideshow presets --- app/sections/slideshow/slide.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/sections/slideshow/slide.tsx b/app/sections/slideshow/slide.tsx index 7a53cd53..5f6b5fbb 100644 --- a/app/sections/slideshow/slide.tsx +++ b/app/sections/slideshow/slide.tsx @@ -134,7 +134,7 @@ export let schema: HydrogenComponentSchema = { ], presets: { verticalPadding: "large", - contentPosition: "bottom left", + contentPosition: "center center", backgroundImage: IMAGES_PLACEHOLDERS.banner_1, backgroundFit: "cover", enableOverlay: true, From fa66bccf5aa3673c4c6f2cfeff354679c195de04 Mon Sep 17 00:00:00 2001 From: ken Date: Mon, 1 Jul 2024 09:32:24 +0700 Subject: [PATCH 12/41] chore: update menu --- app/lib/menu.ts | 6 + app/lib/type.ts | 16 ++ app/modules/Logo.tsx | 2 +- app/modules/header/DesktopHeader.tsx | 8 +- app/modules/header/index.tsx | 2 +- app/modules/header/menu/DesktopMenu.tsx | 124 ++++-------- app/modules/header/menu/MobileMenu.tsx | 106 +++++----- app/modules/header/menu/defines.ts | 249 ------------------------ app/styles/app.css | 22 +++ vite.config.ts | 37 ++-- 10 files changed, 173 insertions(+), 399 deletions(-) create mode 100644 app/lib/menu.ts delete mode 100644 app/modules/header/menu/defines.ts diff --git a/app/lib/menu.ts b/app/lib/menu.ts new file mode 100644 index 00000000..55833b52 --- /dev/null +++ b/app/lib/menu.ts @@ -0,0 +1,6 @@ +export function getMaxDepth(item: { items: any[] }): number { + if (item.items?.length > 0) { + return Math.max(...item.items.map(getMaxDepth)) + 1; + } + return 1; + } \ No newline at end of file diff --git a/app/lib/type.ts b/app/lib/type.ts index 65c0b262..aa0d409c 100644 --- a/app/lib/type.ts +++ b/app/lib/type.ts @@ -25,3 +25,19 @@ export type I18nLocale = Locale & { export type Storefront = HydrogenStorefront; export type Alignment = "left" | "center" | "right"; + +export interface SingleMenuItem { + id: string; + title: string; + items: SingleMenuItem[]; + to: string; + resource?: { + image?: { + altText: string; + height: number; + id: string; + url: string; + width: number; + }; + } +} \ No newline at end of file diff --git a/app/modules/Logo.tsx b/app/modules/Logo.tsx index d9685128..3d838cfb 100644 --- a/app/modules/Logo.tsx +++ b/app/modules/Logo.tsx @@ -13,7 +13,7 @@ export function Logo({ showTransparent }: { showTransparent?: boolean }) { } return ( diff --git a/app/modules/header/DesktopHeader.tsx b/app/modules/header/DesktopHeader.tsx index 197fc095..016ea4c6 100644 --- a/app/modules/header/DesktopHeader.tsx +++ b/app/modules/header/DesktopHeader.tsx @@ -1,4 +1,4 @@ -import { Await, Link, useLoaderData, useLocation } from "@remix-run/react"; +import { Await, Link, useLocation } from "@remix-run/react"; import { useThemeSettings } from "@weaverse/hydrogen"; import clsx from "clsx"; import { Suspense, useEffect, useState } from "react"; @@ -49,7 +49,7 @@ export function DesktopHeader({ >
- -
+ {menu && } +
- + {} ); } diff --git a/app/modules/header/menu/DesktopMenu.tsx b/app/modules/header/menu/DesktopMenu.tsx index 04b9b11a..5212b071 100644 --- a/app/modules/header/menu/DesktopMenu.tsx +++ b/app/modules/header/menu/DesktopMenu.tsx @@ -1,46 +1,27 @@ -import { Link, useLoaderData } from "@remix-run/react"; +import { Link } from "@remix-run/react"; import { Image } from "@shopify/hydrogen"; -import { - type ImageMenuProps, - type MultiMenuProps, - type SingleMenuProps, -} from "./defines"; import clsx from "clsx"; -import type { loader } from "~/root"; +import React from "react"; +import { getMaxDepth } from "~/lib/menu"; +import type { SingleMenuItem } from "~/lib/type"; +import type { EnhancedMenu } from "~/lib/utils"; -function getMaxDepth(item) { - if (item.items?.length > 0) { - return Math.max(...item.items.map(getMaxDepth)) + 1; - } - return 1; -} - -const commonAnimatedClass = - "absolute h-0 opacity-0 overflow-hidden bg-white shadow-md transition-all ease-out group-hover:opacity-100 group-hover:border-t duration-500 group-hover:duration-300 group-hover:z-50 "; - -// function addPrefixForEachClass(prefix: string, className: string,) { -// return className.split(" ").map((className) => `${prefix}${className}`).join(" "); -// } +const dropdownContentClass = + "absolute overflow-hidden bg-white shadow-md z-10 dropdown-transition border-t"; -// const commonAnimatedClass = clsx( -// "absolute -translate-y-full overflow-hidden bg-white shadow-md transition-all ease-out duration-300", -// addPrefixForEachClass("group-hover:", "opacity-100 border-t z-50 translate-y-0"), -// ) - -export function DesktopMenu() { - const data = useLoaderData(); - let menu = data?.layout?.headerMenu; - let items = menu?.items; +export function DesktopMenu(props: { menu: EnhancedMenu }) { + let { menu } = props; + let items = menu.items as unknown as SingleMenuItem[]; if (!items) return null; return ( ); } function ItemHeader({ title, to }: { title: string; to: string }) { return ( -
+