Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

useCart + useCountries #57

Merged
merged 13 commits into from
Oct 5, 2022
170 changes: 76 additions & 94 deletions app/components/CountrySelector.tsx
Original file line number Diff line number Diff line change
@@ -1,108 +1,90 @@
import { Link, useAsyncValue, useLocation, Await, useParams } from "@remix-run/react";
import { Country } from "@shopify/hydrogen-ui-alpha/storefront-api-types";

import { Link, useLocation, useParams } from "@remix-run/react";
import {Listbox} from '@headlessui/react';
import { useState, Suspense } from "react";
import { IconCaret, IconCheck } from "./Icon";
import {useRef} from 'react';
import { getLocalizationFromLang } from "~/lib/utils";
import {useCountries} from '~/hooks/useCountries'
import {Heading} from '~/components'

export function CountrySelector({
countries,
}: {
countries: Array <Country>;
}) {
return (
<Suspense fallback={<CountrySelectorFallback />}>
<Await resolve={countries}>
<CountrySelectorElement />
</Await>
</Suspense>
)
}

function CountrySelectorFallback() {
return (
<div className="relative">
<Listbox>
<Listbox.Button
className={'flex items-center justify-between w-full py-3 px-4 border rounded border-contrast/30 dark:border-white'}
>
<span>--</span>
<IconCaret direction={'down'} />
</Listbox.Button>
</Listbox>
</div>
)
}

function CountrySelectorElement() {
const [listboxOpen, setListboxOpen] = useState(false);
const countries = useAsyncValue<Array <Country>>();
export function CountrySelector() {
const countries = useCountries();
const closeRef = useRef<HTMLButtonElement | undefined>();
const { pathname } = useLocation();
const { lang } = useParams();
const { language, country } = getLocalizationFromLang(lang);
const languageIsoCode = language.toLowerCase();
const strippedPathname = pathname.replace(new RegExp(`^\/${lang}\/`), '/')
const currentCountry = countries.find(c => c.isoCode === country);
const currentCountry = countries?.find(c => c.isoCode === country);

return (
<div className="relative">
<Listbox>
{({open}) => {
setTimeout(() => setListboxOpen(open));
return (
<>
<Listbox.Button
className={`flex items-center justify-between w-full py-3 px-4 border ${
open ? 'rounded-b md:rounded-t md:rounded-b-none' : 'rounded'
} border-contrast/30 dark:border-white`}
>
<span>{
currentCountry ?
`${currentCountry.name} (${currentCountry.currency.isoCode} ${currentCountry.currency.symbol})` :
'--'
}</span>
<IconCaret direction={open ? 'up' : 'down'} />
</Listbox.Button>
countries && (
<section className="grid gap-4 w-full md:max-w-[335px] md:ml-auto">
<Heading size="lead" className="cursor-default" as="h3">
Country
</Heading>
<div className="relative">
<Listbox>
{({open}) => {
return (
<>
<Listbox.Button
ref={closeRef}
className={`flex items-center justify-between w-full py-3 px-4 border ${
open ? 'rounded-b md:rounded-t md:rounded-b-none' : 'rounded'
} border-contrast/30 dark:border-white`}
>
<span>{
currentCountry ?
`${currentCountry.name} (${currentCountry.currency.isoCode} ${currentCountry.currency.symbol})` :
'--'
}</span>
<IconCaret direction={open ? 'up' : 'down'} />
</Listbox.Button>

<Listbox.Options
className={`border-t-contrast/30 border-contrast/30 bg-primary dark:bg-contrast absolute bottom-12 z-10 grid
h-48 w-full overflow-y-scroll rounded-t border dark:border-white px-2 py-2
transition-[max-height] duration-150 sm:bottom-auto md:rounded-b md:rounded-t-none
md:border-t-0 md:border-b ${
listboxOpen ? 'max-h-48' : 'max-h-0'
}`}
>
{listboxOpen && Object.values(countries).map(country => {
const isSelected = country.isoCode === currentCountry?.isoCode;
const countryIsoCode = country.isoCode.toLowerCase();
return (
<Listbox.Option key={country.isoCode} value={country}>
{({active}) => (
<Link
to={countryIsoCode !== 'us' ? `/${languageIsoCode}-${countryIsoCode}${strippedPathname}` : strippedPathname}
className={`text-contrast dark:text-primary text-contrast dark:text-primary bg-primary
dark:bg-contrast w-full p-2 transition rounded
flex justify-start items-center text-left cursor-pointer ${
active ? 'bg-primary/10' : null
}`}
>
{country.name} ({country.currency.isoCode} {country.currency.symbol})
{isSelected ? (
<span className="ml-2">
<IconCheck />
</span>
) : null}
</Link>
)}
</Listbox.Option>
);
})}
</Listbox.Options>
</>
);
}}
</Listbox>
</div>
<Listbox.Options
className={`border-t-contrast/30 border-contrast/30 bg-primary dark:bg-contrast absolute bottom-12 z-10 grid
h-48 w-full overflow-y-scroll rounded-t border dark:border-white px-2 py-2
transition-[max-height] duration-150 sm:bottom-auto md:rounded-b md:rounded-t-none
md:border-t-0 md:border-b ${
open ? 'max-h-48' : 'max-h-0'
}`}
>
{open && Object.values(countries).map(country => {
const isSelected = country.isoCode === currentCountry?.isoCode;
const countryIsoCode = country.isoCode.toLowerCase();
return (
<Listbox.Option key={country.isoCode} value={country}>
{({active}) => (
<Link
to={countryIsoCode !== 'us' ? `/${languageIsoCode}-${countryIsoCode}${strippedPathname}` : strippedPathname}
className={`text-contrast dark:text-primary text-contrast dark:text-primary bg-primary
dark:bg-contrast w-full p-2 transition rounded
flex justify-start items-center text-left cursor-pointer ${
active ? 'bg-primary/10' : null
}`}
onClick={() => {
if (!closeRef?.current) return;
closeRef?.current?.click();
}}
>
{country.name} ({country.currency.isoCode} {country.currency.symbol})
{isSelected ? (
<span className="ml-2">
<IconCheck />
</span>
) : null}
</Link>
)}
</Listbox.Option>
);
})}
</Listbox.Options>
</>
);
}}
</Listbox>
</div>
</section>
)
);
}
Loading