diff --git a/app/components/button.tsx b/app/components/button.tsx index 19d577a6..9bab6d38 100644 --- a/app/components/button.tsx +++ b/app/components/button.tsx @@ -1,9 +1,9 @@ +import { CircleNotch } from "@phosphor-icons/react"; import type { VariantProps } from "class-variance-authority"; import { cva } from "class-variance-authority"; import type { HTMLAttributes } from "react"; import { forwardRef } from "react"; import { cn } from "~/lib/cn"; -import { IconCircleNotch } from "./icons"; export let variants = cva( [ @@ -146,7 +146,7 @@ function Spinner() { className="button-spinner absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2" style={style} > - + ); } diff --git a/app/components/header/cart-count.tsx b/app/components/header/cart-count.tsx deleted file mode 100644 index dba7e42b..00000000 --- a/app/components/header/cart-count.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import { Await, Link, useRouteLoaderData } from "@remix-run/react"; -import { useAnalytics } from "@shopify/hydrogen"; -import clsx from "clsx"; -import { Suspense, useMemo } from "react"; -import { IconHandBag } from "~/components/icons"; -import { useIsHydrated } from "~/hooks/use-is-hydrated"; -import type { RootLoader } from "~/root"; - -export function CartCount({ - openCart, - isTransparent, -}: { - openCart: () => void; - isTransparent: boolean; -}) { - let rootData = useRouteLoaderData("root"); - return ( - - } - > - - {(cart) => ( - - )} - - - ); -} - -function Badge({ - openCart, - count, - cart, - isTransparent, -}: { - count: number; - openCart: () => void; - cart?: any; - isTransparent: boolean; -}) { - let isHydrated = useIsHydrated(); - let BadgeCounter = useMemo( - () => ( - <> - - {count > 0 && ( -
- {count} -
- )} - - ), - [count, isTransparent], - ); - - let { publish } = useAnalytics(); - - function handleOpenCart() { - publish("custom_sidecart_viewed", { cart }); - openCart(); - } - - return isHydrated ? ( - - ) : ( - - {BadgeCounter} - - ); -} diff --git a/app/components/header/cart-drawer.tsx b/app/components/header/cart-drawer.tsx index 211a8ac9..21d119e5 100644 --- a/app/components/header/cart-drawer.tsx +++ b/app/components/header/cart-drawer.tsx @@ -3,15 +3,27 @@ import * as Dialog from "@radix-ui/react-dialog"; import { Await, useRouteLoaderData } from "@remix-run/react"; import { type CartReturn, useAnalytics } from "@shopify/hydrogen"; import clsx from "clsx"; -import { Suspense } from "react"; +import { Suspense, useState } from "react"; import Link from "~/components/link"; import { ScrollArea } from "~/components/scroll-area"; import { Cart } from "~/modules/cart"; import type { RootLoader } from "~/root"; -export function CartDrawer({ isTransparent }: { isTransparent: boolean }) { +export let openCartDrawer = () => {}; + +export function CartDrawer() { let rootData = useRouteLoaderData("root"); let { publish } = useAnalytics(); + let [open, setOpen] = useState(false); + openCartDrawer = () => setOpen(true); + + // Toggle cart drawer when adding to cart + // let addToCartFetchers = useCartFetchers(CartForm.ACTIONS.LinesAdd); + // useEffect(() => { + // if (!open && addToCartFetchers.length) { + // setOpen(true); + // } + // }, [addToCartFetchers, open]); return ( {(cart) => ( - + publish("custom_sidecart_viewed", { cart })} className="relative flex items-center justify-center w-8 h-8 focus:ring-border" @@ -35,17 +47,16 @@ export function CartDrawer({ isTransparent }: { isTransparent: boolean }) { {cart?.totalQuantity > 0 && (
- {cart?.totalQuantity} + {cart?.totalQuantity}
)}
@@ -63,8 +74,8 @@ export function CartDrawer({ isTransparent }: { isTransparent: boolean }) { >
- - Cart + + Cart - -
- -
- - -
- - ); -} - -function AccountLink({ className }: { className?: string }) { - let rootData = useRouteLoaderData("root"); - let isLoggedIn = rootData?.isLoggedIn; - - return ( - - }> - } - > - {(isLoggedIn) => - isLoggedIn ? ( - - ) : ( - - ) - } - - - - ); -} diff --git a/app/components/header/predictive-search/index.tsx b/app/components/header/predictive-search/index.tsx index 21082faa..af1e92c8 100644 --- a/app/components/header/predictive-search/index.tsx +++ b/app/components/header/predictive-search/index.tsx @@ -1,7 +1,6 @@ import { MagnifyingGlass } from "@phosphor-icons/react"; import * as Dialog from "@radix-ui/react-dialog"; import * as VisuallyHidden from "@radix-ui/react-visually-hidden"; -import { IconMagnifyingGlass } from "~/components/icons"; import { cn } from "~/lib/cn"; import { Input } from "~/modules/input"; import { PredictiveSearchResults } from "./predictive-search-results"; @@ -12,7 +11,7 @@ export function PredictiveSearchButton() { {codes?.join(", ")} @@ -339,7 +336,7 @@ function ItemRemoveButton({ type="submit" > Remove -
+
{children} diff --git a/app/modules/sort-filter.tsx b/app/modules/sort-filter.tsx deleted file mode 100644 index 4820c88c..00000000 --- a/app/modules/sort-filter.tsx +++ /dev/null @@ -1,374 +0,0 @@ -import { - Disclosure, - Menu, - MenuButton, - MenuItem, - MenuItems, -} from "@headlessui/react"; -import type { Location } from "@remix-run/react"; -import { - Link, - useLocation, - useNavigate, - useSearchParams, -} from "@remix-run/react"; -import type { - Filter, - ProductFilter, -} from "@shopify/hydrogen/storefront-api-types"; -import type { SyntheticEvent } from "react"; -import { useMemo, useState } from "react"; -import useDebounce from "react-use/esm/useDebounce"; -import { IconCaret, IconFilters, IconXMark } from "~/modules/icon"; -import { Heading, Text } from "~/modules/text"; - -export type AppliedFilter = { - label: string; - filter: ProductFilter; -}; - -export type SortParam = - | "price-low-high" - | "price-high-low" - | "best-selling" - | "newest" - | "featured"; - -type Props = { - filters: Filter[]; - appliedFilters?: AppliedFilter[]; - children: React.ReactNode; - collections?: Array<{ handle: string; title: string }>; -}; -export const FILTER_URL_PREFIX = "filter."; - -export function SortFilter({ - filters, - appliedFilters = [], - children, - collections = [], -}: Props) { - const [isOpen, setIsOpen] = useState(false); - return ( - <> -
- - -
-
-
- -
-
{children}
-
- - ); -} - -export function FiltersDrawer({ - filters = [], - appliedFilters = [], -}: Omit) { - const [params] = useSearchParams(); - const location = useLocation(); - - const filterMarkup = (filter: Filter, option: Filter["values"][0]) => { - switch (filter.type) { - case "PRICE_RANGE": { - const priceFilter = params.get(`${FILTER_URL_PREFIX}price`); - const price = priceFilter - ? (JSON.parse(priceFilter) as ProductFilter["price"]) - : undefined; - const min = Number.isNaN(Number(price?.min)) - ? undefined - : Number(price?.min); - const max = Number.isNaN(Number(price?.max)) - ? undefined - : Number(price?.max); - - return ; - } - - default: { - const to = getFilterLink(option.input as string, params, location); - return ( - - {option.label} - - ); - } - } - }; - - return ( - <> - - - ); -} - -function AppliedFilters({ filters = [] }: { filters: AppliedFilter[] }) { - const [params] = useSearchParams(); - const location = useLocation(); - return ( - <> - - Applied filters - -
- {filters.map((filter: AppliedFilter) => { - return ( - - {filter.label} - - - - - ); - })} -
- - ); -} - -function getAppliedFilterLink( - filter: AppliedFilter, - params: URLSearchParams, - location: Location, -) { - let paramsClone = new URLSearchParams(params); - for (let [key, value] of Object.entries(filter.filter)) { - let fullKey = FILTER_URL_PREFIX + key; - paramsClone.delete(fullKey, JSON.stringify(value)); - } - return `${location.pathname}?${paramsClone.toString()}`; -} - -function getSortLink( - sort: SortParam, - params: URLSearchParams, - location: Location, -) { - params.set("sort", sort); - return `${location.pathname}?${params.toString()}`; -} - -function getFilterLink( - rawInput: string | ProductFilter, - params: URLSearchParams, - location: ReturnType, -) { - const paramsClone = new URLSearchParams(params); - const newParams = filterInputToParams(rawInput, paramsClone); - return `${location.pathname}?${newParams.toString()}`; -} - -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 ( -
- - -
- ); -} - -function filterInputToParams( - rawInput: string | ProductFilter, - params: URLSearchParams, -) { - let input = - typeof rawInput === "string" - ? (JSON.parse(rawInput) as ProductFilter) - : rawInput; - - for (let [key, value] of Object.entries(input)) { - if (params.has(`${FILTER_URL_PREFIX}${key}`, JSON.stringify(value))) { - continue; - } - if (key === "price") { - // For price, we want to overwrite - params.set(`${FILTER_URL_PREFIX}${key}`, JSON.stringify(value)); - } else { - params.append(`${FILTER_URL_PREFIX}${key}`, JSON.stringify(value)); - } - } - - return params; -} - -export default function SortMenu() { - const items: { label: string; key: SortParam }[] = [ - { label: "Featured", key: "featured" }, - { - label: "Price: Low - High", - key: "price-low-high", - }, - { - label: "Price: High - Low", - key: "price-high-low", - }, - { - label: "Best Selling", - key: "best-selling", - }, - { - label: "Newest", - key: "newest", - }, - ]; - const [params] = useSearchParams(); - const location = useLocation(); - const activeItem = items.find((item) => item.key === params.get("sort")); - - return ( - - - - Sort by: - {(activeItem || items[0]).label} - - - - - {items.map((item) => ( - - {() => ( - - {item.label} - - )} - - ))} - - - ); -} diff --git a/app/routes/($locale).account.tsx b/app/routes/($locale).account.tsx index 9ba6cb58..76e1c657 100644 --- a/app/routes/($locale).account.tsx +++ b/app/routes/($locale).account.tsx @@ -1,3 +1,4 @@ +import { SignOut } from "@phosphor-icons/react"; import { Await, Form, @@ -13,7 +14,6 @@ import type { OrderCardFragment, } from "customer-accountapi.generated"; import { Suspense } from "react"; -import { IconSignOut } from "~/components/icons"; import Link from "~/components/link"; import { CACHE_NONE, routeHeaders } from "~/data/cache"; import { CUSTOMER_DETAILS_QUERY } from "~/graphql/customer-account/customer-details-query"; @@ -108,7 +108,7 @@ function Account({ customer, heading, featuredDataPromise }: AccountType) { type="submit" className="text-body-subtle group flex gap-2 items-center" > - + Sign out diff --git a/app/routes/($locale).collections.$collectionHandle.tsx b/app/routes/($locale).collections.$collectionHandle.tsx index 5557b7ce..bdb50344 100644 --- a/app/routes/($locale).collections.$collectionHandle.tsx +++ b/app/routes/($locale).collections.$collectionHandle.tsx @@ -5,10 +5,7 @@ import { getPaginationVariables, getSeoMeta, } from "@shopify/hydrogen"; -import type { - ProductCollectionSortKeys, - ProductFilter, -} from "@shopify/hydrogen/storefront-api-types"; +import type { ProductFilter } from "@shopify/hydrogen/storefront-api-types"; import { type LoaderFunctionArgs, type MetaArgs, @@ -20,11 +17,10 @@ import invariant from "tiny-invariant"; import { routeHeaders } from "~/data/cache"; import { COLLECTION_QUERY } from "~/data/queries"; import { getSortValuesFromParam } from "~/lib/collections"; -import { PAGINATION_SIZE } from "~/lib/const"; +import { FILTER_URL_PREFIX, PAGINATION_SIZE } from "~/lib/const"; +import type { SortParam } from "~/lib/filter"; import { seoPayload } from "~/lib/seo.server"; import { parseAsCurrency } from "~/lib/utils"; -import type { SortParam } from "~/modules/sort-filter"; -import { FILTER_URL_PREFIX } from "~/modules/sort-filter"; import { WeaverseContent } from "~/weaverse"; export let headers = routeHeaders; @@ -63,6 +59,9 @@ export async function loader({ params, request, context }: LoaderFunctionArgs) { reverse, country: context.storefront.i18n.country, language: context.storefront.i18n.language, + // Query custom banner stored in Shopify's collection metafields + customBannerNamespace: "custom", + customBannerKey: "collection_banner", }, }) .catch((e) => { diff --git a/app/sections/ali-reviews/rating.tsx b/app/sections/ali-reviews/rating.tsx index 80ef8344..6a0efc25 100644 --- a/app/sections/ali-reviews/rating.tsx +++ b/app/sections/ali-reviews/rating.tsx @@ -1,20 +1,16 @@ -import { - IconStar, - IconStarFilled, - IconStarHalfFilled, -} from "~/components/icons"; +import { Star, StarHalf } from "@phosphor-icons/react"; export function Rating({ rating }: { rating: number }) { return (
{Array.from({ length: 5 }).map((_, i) => { if (rating >= i + 1) { - return ; + return ; } if (rating >= i + 0.5) { - return ; + return ; } - return ; + return ; })}
); diff --git a/app/sections/ali-reviews/review-item.tsx b/app/sections/ali-reviews/review-item.tsx index bac8d0cb..c13fb46d 100644 --- a/app/sections/ali-reviews/review-item.tsx +++ b/app/sections/ali-reviews/review-item.tsx @@ -1,7 +1,8 @@ +import { SealCheck } from "@phosphor-icons/react"; import clsx from "clsx"; import { useState } from "react"; import ReactCountryFlag from "react-country-flag"; -import { IconSealCheck, IconX } from "~/components/icons"; +import { IconX } from "~/components/icons"; import { Rating } from "./rating"; export type AliReview = { @@ -79,7 +80,7 @@ export function ReviewItem(props: ReviewItemProps) {
{showVerifiedBadge && (
- +

{verifiedBadgeText}

)} diff --git a/app/sections/collection-filters/index.tsx b/app/sections/collection-filters/index.tsx index 588a53c3..2026db2e 100644 --- a/app/sections/collection-filters/index.tsx +++ b/app/sections/collection-filters/index.tsx @@ -7,8 +7,13 @@ import { Section, type SectionProps, layoutInputs } from "~/components/section"; import { Filters } from "./filters"; import { ProductsPagination } from "./products-pagination"; import { ToolsBar } from "./tools-bar"; +import { Image } from "@shopify/hydrogen"; export interface CollectionFiltersData { + showBreadcrumb: boolean; + showDescription: boolean; + showBanner: boolean; + bannerHeightDesktop: number; enableSort: boolean; showProductsCount: boolean; enableFilter: boolean; @@ -28,6 +33,10 @@ interface CollectionFiltersProps extends SectionProps, CollectionFiltersData {} let CollectionFilters = forwardRef( (props, sectionRef) => { let { + showBreadcrumb, + showDescription, + showBanner, + bannerHeightDesktop, enableSort, showFiltersCount, enableFilter, @@ -61,17 +70,46 @@ let CollectionFilters = forwardRef( }, [productsPerRowDesktop, productsPerRowMobile]); if (collection?.products && collections) { + let banner = collection.metafield + ? collection.metafield.reference.image + : collection.image; return (
-
-
- - Home - - / - {collection.title} -
+
+ {showBreadcrumb && ( +
+ + Home + + / + {collection.title} +
+ )}

{collection.title}

+ {showDescription && collection.description && ( +

+ {collection.description} +

+ )} + {showBanner && banner && ( +
+ +
+ )}
{ - return inp.name !== "borderRadius" && inp.name !== "gap"; - }), + inputs: [ + ...layoutInputs.filter((inp) => { + return inp.name !== "borderRadius" && inp.name !== "gap"; + }), + { + type: "switch", + name: "showBreadcrumb", + label: "Show breadcrumb", + defaultValue: true, + }, + { + type: "switch", + name: "showDescription", + label: "Show description", + defaultValue: false, + }, + { + type: "switch", + name: "showBanner", + label: "Show banner", + defaultValue: true, + helpText: + "A custom banner can be stored under `custom.collection_banner` metafield.", + }, + { + type: "range", + name: "bannerHeightDesktop", + label: "Banner height (desktop)", + defaultValue: 350, + configs: { + min: 100, + max: 600, + step: 1, + }, + condition: "showBanner.eq.true", + }, + { + type: "range", + name: "bannerHeightMobile", + label: "Banner height (mobile)", + defaultValue: 200, + configs: { + min: 50, + max: 400, + step: 1, + }, + condition: "showBanner.eq.true", + }, + ], }, { group: "Filtering and sorting", diff --git a/app/sections/collection-filters/sort.tsx b/app/sections/collection-filters/sort.tsx index fc24429f..3a0a7ec7 100644 --- a/app/sections/collection-filters/sort.tsx +++ b/app/sections/collection-filters/sort.tsx @@ -40,9 +40,10 @@ export function Sort() { return ( - + Sort by: {currentSort.label} + Sort diff --git a/app/sections/judgeme-reviews/review-form.tsx b/app/sections/judgeme-reviews/review-form.tsx index 7348fd5c..5f78fe2c 100644 --- a/app/sections/judgeme-reviews/review-form.tsx +++ b/app/sections/judgeme-reviews/review-form.tsx @@ -1,8 +1,8 @@ +import { Star } from "@phosphor-icons/react"; import { useFetcher, useLoaderData } from "@remix-run/react"; import clsx from "clsx"; import { type FormEvent, useEffect, useRef, useState } from "react"; import { Button } from "~/components/button"; -import { IconStar, IconStarFilled } from "~/components/icons"; import type { JudgemeReviewsData } from "~/lib/judgeme"; import { StarRating } from "~/modules/star-rating"; import type { ProductLoaderType } from "~/routes/($locale).products.$productHandle"; @@ -122,12 +122,11 @@ export function ReviewForm({ onMouseEnter={() => setHover(ratingValue)} onMouseLeave={() => setHover(0)} aria-label={`Rate ${ratingValue} out of 5 stars`} - role="button" > {ratingValue <= (hover || rating) ? ( - + ) : ( - + )}
); diff --git a/app/sections/newsletter/newsletter-form.tsx b/app/sections/newsletter/newsletter-form.tsx index 0697d47a..7af2d817 100644 --- a/app/sections/newsletter/newsletter-form.tsx +++ b/app/sections/newsletter/newsletter-form.tsx @@ -1,3 +1,4 @@ +import { EnvelopeSimple } from "@phosphor-icons/react"; import { useFetcher } from "@remix-run/react"; import type { HydrogenComponentProps, @@ -6,7 +7,6 @@ import type { import clsx from "clsx"; import { forwardRef } from "react"; import { Button } from "~/components/button"; -import { IconEnvelopeSimple } from "~/components/icons"; import type { CustomerApiPlayLoad } from "~/routes/($locale).api.customer"; interface NewsLetterInputProps extends HydrogenComponentProps { @@ -35,7 +35,7 @@ let NewsLetterForm = forwardRef( data-motion="fade-up" >
- + ( target="_blank" className="text-gray-500 hover:text-gray-900" > - + )} @@ -83,7 +79,7 @@ let TeamMembers = forwardRef( target="_blank" className="text-gray-500 hover:text-gray-900" > - + )} @@ -94,7 +90,7 @@ let TeamMembers = forwardRef( target="_blank" className="text-gray-500 hover:text-gray-900" > - + )} diff --git a/app/sections/product-list.tsx b/app/sections/product-list.tsx index 195c63ce..f1a18f41 100644 --- a/app/sections/product-list.tsx +++ b/app/sections/product-list.tsx @@ -8,8 +8,8 @@ import { forwardRef } from "react"; import { COLLECTION_QUERY } from "~/data/queries"; import { getSortValuesFromParam } from "~/lib/collections"; import { PAGINATION_SIZE } from "~/lib/const"; +import type { SortParam } from "~/lib/filter"; import { ProductSwimlane } from "~/modules/product-swimlane"; -import type { SortParam } from "~/modules/sort-filter"; interface ProductListProps extends HydrogenComponentProps>> { diff --git a/package-lock.json b/package-lock.json index 8671f82c..993c3713 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@weaverse/pilot", - "version": "3.2.1", + "version": "3.2.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@weaverse/pilot", - "version": "3.2.1", + "version": "3.2.2", "dependencies": { "@fontsource/poppins": "5.1.0", "@graphql-codegen/cli": "5.0.3", diff --git a/package.json b/package.json index 41200632..23c268c5 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "@weaverse/pilot", "private": true, "sideEffects": false, - "version": "3.2.1", + "version": "3.2.2", "type": "module", "author": "Weaverse", "scripts": { diff --git a/storefrontapi.generated.d.ts b/storefrontapi.generated.d.ts index 0bf1944a..f799f5b4 100644 --- a/storefrontapi.generated.d.ts +++ b/storefrontapi.generated.d.ts @@ -665,6 +665,8 @@ export type CollectionDetailsQueryVariables = StorefrontAPI.Exact<{ endCursor?: StorefrontAPI.InputMaybe< StorefrontAPI.Scalars['String']['input'] >; + customBannerNamespace: StorefrontAPI.Scalars['String']['input']; + customBannerKey: StorefrontAPI.Scalars['String']['input']; }>; export type CollectionDetailsQuery = { @@ -674,6 +676,18 @@ export type CollectionDetailsQuery = { 'id' | 'handle' | 'title' | 'description' > & { seo: Pick; + metafield?: StorefrontAPI.Maybe< + Pick< + StorefrontAPI.Metafield, + 'id' | 'type' | 'description' | 'value' + > & { + reference?: StorefrontAPI.Maybe<{ + image?: StorefrontAPI.Maybe< + Pick + >; + }>; + } + >; image?: StorefrontAPI.Maybe< Pick >; @@ -1826,7 +1840,7 @@ interface GeneratedQueryTypes { return: CollectionInfoQuery; variables: CollectionInfoQueryVariables; }; - '#graphql\n query CollectionDetails(\n $handle: String!\n $country: CountryCode\n $language: LanguageCode\n $filters: [ProductFilter!]\n $sortKey: ProductCollectionSortKeys!\n $reverse: Boolean\n $first: Int\n $last: Int\n $startCursor: String\n $endCursor: String\n ) @inContext(country: $country, language: $language) {\n collection(handle: $handle) {\n id\n handle\n title\n description\n seo {\n description\n title\n }\n image {\n id\n url\n width\n height\n altText\n }\n products(\n first: $first,\n last: $last,\n before: $startCursor,\n after: $endCursor,\n filters: $filters,\n sortKey: $sortKey,\n reverse: $reverse\n ) {\n filters {\n id\n label\n type\n values {\n id\n label\n count\n input\n }\n }\n nodes {\n ...ProductCard\n }\n pageInfo {\n hasPreviousPage\n hasNextPage\n endCursor\n startCursor\n }\n }\n highestPriceProduct: products(first: 1, sortKey: PRICE, reverse: true) {\n nodes {\n id\n title\n handle\n priceRange {\n minVariantPrice {\n amount\n currencyCode\n }\n maxVariantPrice {\n amount\n currencyCode\n }\n }\n }\n }\n lowestPriceProduct: products(first: 1, sortKey: PRICE) {\n nodes {\n id\n title\n handle\n priceRange {\n minVariantPrice {\n amount\n currencyCode\n }\n maxVariantPrice {\n amount\n currencyCode\n }\n }\n }\n }\n }\n collections(first: 100) {\n edges {\n node {\n title\n handle\n }\n }\n }\n }\n #graphql\n fragment ProductCard on Product {\n id\n title\n publishedAt\n handle\n vendor\n priceRange {\n minVariantPrice {\n amount\n currencyCode\n }\n maxVariantPrice {\n amount\n currencyCode\n }\n }\n variants(first: 10) {\n nodes {\n id\n availableForSale\n image {\n url\n altText\n width\n height\n }\n price {\n amount\n currencyCode\n }\n compareAtPrice {\n amount\n currencyCode\n }\n selectedOptions {\n name\n value\n }\n product {\n handle\n title\n }\n sku\n }\n }\n }\n\n': { + '#graphql\n query CollectionDetails(\n $handle: String!\n $country: CountryCode\n $language: LanguageCode\n $filters: [ProductFilter!]\n $sortKey: ProductCollectionSortKeys!\n $reverse: Boolean\n $first: Int\n $last: Int\n $startCursor: String\n $endCursor: String\n $customBannerNamespace: String!\n $customBannerKey: String!\n ) @inContext(country: $country, language: $language) {\n collection(handle: $handle) {\n id\n handle\n title\n description\n seo {\n description\n title\n }\n metafield(namespace: $customBannerNamespace, key: $customBannerKey) {\n id\n type\n description\n value\n reference {\n ... on MediaImage {\n image {\n id\n url\n }\n }\n }\n }\n image {\n id\n url\n width\n height\n altText\n }\n products(\n first: $first,\n last: $last,\n before: $startCursor,\n after: $endCursor,\n filters: $filters,\n sortKey: $sortKey,\n reverse: $reverse\n ) {\n filters {\n id\n label\n type\n values {\n id\n label\n count\n input\n }\n }\n nodes {\n ...ProductCard\n }\n pageInfo {\n hasPreviousPage\n hasNextPage\n endCursor\n startCursor\n }\n }\n highestPriceProduct: products(first: 1, sortKey: PRICE, reverse: true) {\n nodes {\n id\n title\n handle\n priceRange {\n minVariantPrice {\n amount\n currencyCode\n }\n maxVariantPrice {\n amount\n currencyCode\n }\n }\n }\n }\n lowestPriceProduct: products(first: 1, sortKey: PRICE) {\n nodes {\n id\n title\n handle\n priceRange {\n minVariantPrice {\n amount\n currencyCode\n }\n maxVariantPrice {\n amount\n currencyCode\n }\n }\n }\n }\n }\n collections(first: 100) {\n edges {\n node {\n title\n handle\n }\n }\n }\n }\n #graphql\n fragment ProductCard on Product {\n id\n title\n publishedAt\n handle\n vendor\n priceRange {\n minVariantPrice {\n amount\n currencyCode\n }\n maxVariantPrice {\n amount\n currencyCode\n }\n }\n variants(first: 10) {\n nodes {\n id\n availableForSale\n image {\n url\n altText\n width\n height\n }\n price {\n amount\n currencyCode\n }\n compareAtPrice {\n amount\n currencyCode\n }\n selectedOptions {\n name\n value\n }\n product {\n handle\n title\n }\n sku\n }\n }\n }\n\n': { return: CollectionDetailsQuery; variables: CollectionDetailsQueryVariables; }; diff --git a/tailwind.config.js b/tailwind.config.js index b023c22e..62774da3 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -128,15 +128,21 @@ export default { "screen-no-nav": "calc(var(--screen-height, 100vh) - var(--height-nav) - var(--initial-topbar-height))", "screen-dynamic": "var(--screen-height-dynamic, 100vh)", + 4.5: "1.125rem", }, width: { mobileGallery: "calc(100vw - 3rem)", page: "var(--page-width, 1280px)", + 4.5: "1.125rem", }, maxWidth: { page: "var(--page-width, 1280px)", "prose-narrow": "45ch", "prose-wide": "80ch", + 4.5: "1.125rem", + }, + minWidth: { + 4.5: "1.125rem", }, fontFamily: { sans: ["Poppins", "ui-sans-serif", "system-ui", "sans-serif"],