feat: reuse country select and refactor some component
This commit is contained in:
parent
d8e78b71e4
commit
f869d12c7a
|
|
@ -20,8 +20,6 @@ export default function VtButton({
|
||||||
(CustomIcons as Record<string, any>)[iconName]
|
(CustomIcons as Record<string, any>)[iconName]
|
||||||
: undefined
|
: undefined
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
{props?.icon && (
|
|
||||||
<IconButton className={props?.className ?? ""}>
|
<IconButton className={props?.className ?? ""}>
|
||||||
{IconComponent && (
|
{IconComponent && (
|
||||||
<IconComponent className={props?.iconClassName ?? ""} />
|
<IconComponent className={props?.iconClassName ?? ""} />
|
||||||
|
|
@ -30,12 +28,5 @@ export default function VtButton({
|
||||||
<span className={props?.labelClassName ?? ""}>{props.label}</span>
|
<span className={props?.labelClassName ?? ""}>{props.label}</span>
|
||||||
)}
|
)}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
|
||||||
{!props?.icon && (
|
|
||||||
<Button className={props?.className ?? ""}>
|
|
||||||
{props?.label && <span>{props.label}</span>}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,114 @@
|
||||||
|
"use client"
|
||||||
|
import { Select } from "@medusajs/ui"
|
||||||
|
import ChevronDown from "@modules/common/icons/chevron-down"
|
||||||
|
import {
|
||||||
|
LayoutComponentDefinition,
|
||||||
|
LayoutContext,
|
||||||
|
} from "@vibentec/component-map"
|
||||||
|
import { useMemo, useState, useEffect } from "react"
|
||||||
|
import { useParams, usePathname, useRouter } from "next/navigation"
|
||||||
|
import ReactCountryFlag from "react-country-flag"
|
||||||
|
import { HttpTypes } from "@medusajs/types"
|
||||||
|
|
||||||
|
export default function VtCountrySelectClient({
|
||||||
|
nodes,
|
||||||
|
context,
|
||||||
|
regions,
|
||||||
|
}: {
|
||||||
|
nodes: LayoutComponentDefinition
|
||||||
|
context: LayoutContext
|
||||||
|
regions?: HttpTypes.StoreRegion[]
|
||||||
|
}) {
|
||||||
|
const props = nodes.config ?? {}
|
||||||
|
const triggerText = props?.trigger?.text
|
||||||
|
const [items, setItems] = useState<{ text: string; label?: string }[]>([])
|
||||||
|
const { countryCode } = useParams()
|
||||||
|
console.log(regions)
|
||||||
|
useEffect(() => {
|
||||||
|
if (!regions || regions.length === 0) {
|
||||||
|
setItems([])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const opts = regions
|
||||||
|
.map((r) =>
|
||||||
|
(r.countries || []).map((c) => ({
|
||||||
|
text: c.iso_2 ?? "",
|
||||||
|
label: c.display_name,
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
.flat()
|
||||||
|
.filter((o) => o.text)
|
||||||
|
.sort((a, b) => (a.label ?? "").localeCompare(b.label ?? ""))
|
||||||
|
console.log(opts)
|
||||||
|
setItems(opts)
|
||||||
|
}, [regions])
|
||||||
|
|
||||||
|
const initialValue = (countryCode as string) || triggerText || ""
|
||||||
|
const [value, setValue] = useState(initialValue)
|
||||||
|
const pathname = usePathname()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const handleChange = (next: string) => {
|
||||||
|
setValue(next)
|
||||||
|
if (!pathname || !next) return
|
||||||
|
const parts = pathname.split("/")
|
||||||
|
if (parts.length > 1) {
|
||||||
|
parts[1] = next.toLowerCase()
|
||||||
|
const newPath = parts.join("/")
|
||||||
|
router.replace(newPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const selectedItem = useMemo(() => {
|
||||||
|
return items.find((i) => i.text === value)
|
||||||
|
}, [items, value])
|
||||||
|
|
||||||
|
if (!triggerText && items.length === 0) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Select value={value} onValueChange={handleChange}>
|
||||||
|
<Select.Trigger
|
||||||
|
className={
|
||||||
|
(props.trigger?.className ?? "") +
|
||||||
|
"flex items-center gap-1 [&_svg:not(:first-of-type)]:hidden"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<span className="txt-compact-small flex items-center">
|
||||||
|
{/* @ts-ignore */}
|
||||||
|
{props.trigger?.isFlag && (
|
||||||
|
<ReactCountryFlag
|
||||||
|
svg
|
||||||
|
style={{
|
||||||
|
width: "16px",
|
||||||
|
height: "16px",
|
||||||
|
}}
|
||||||
|
countryCode={value ?? ""}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
{selectedItem?.text.toUpperCase() || value} <ChevronDown />
|
||||||
|
</Select.Trigger>
|
||||||
|
<Select.Content>
|
||||||
|
{items.length > 0 &&
|
||||||
|
items.map((item: { text: string; label?: string }, index: number) => (
|
||||||
|
<Select.Item value={item.text || ""} key={item.text + index}>
|
||||||
|
<div className="flex items-center w-full gap-3">
|
||||||
|
{props.trigger?.isFlag && item.text && (
|
||||||
|
<ReactCountryFlag
|
||||||
|
svg
|
||||||
|
style={{
|
||||||
|
width: "16px",
|
||||||
|
height: "16px",
|
||||||
|
}}
|
||||||
|
countryCode={item.text ?? ""}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{item.text.toUpperCase()}
|
||||||
|
</div>
|
||||||
|
</Select.Item>
|
||||||
|
))}
|
||||||
|
</Select.Content>
|
||||||
|
</Select>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { listRegions, getRegion } from "@lib/data/regions"
|
||||||
|
import { HttpTypes } from "@medusajs/types"
|
||||||
|
import {
|
||||||
|
LayoutComponentDefinition,
|
||||||
|
LayoutContext,
|
||||||
|
} from "@vibentec/component-map"
|
||||||
|
import VtCountryCodeSelectClient from "./index"
|
||||||
|
|
||||||
|
export default async function VtCountryCodeSelect({
|
||||||
|
nodes,
|
||||||
|
context,
|
||||||
|
}: {
|
||||||
|
nodes: LayoutComponentDefinition
|
||||||
|
context: LayoutContext
|
||||||
|
}) {
|
||||||
|
const regions = (await listRegions()) as HttpTypes.StoreRegion[]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<VtCountryCodeSelectClient nodes={nodes} context={context} regions={regions} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
"use client"
|
||||||
|
import { Select } from "@medusajs/ui"
|
||||||
|
import LocalizedClientLink from "@modules/common/components/localized-client-link"
|
||||||
|
import ChevronDown from "@modules/common/icons/chevron-down"
|
||||||
|
import {
|
||||||
|
LayoutComponentDefinition,
|
||||||
|
LayoutContext,
|
||||||
|
} from "@vibentec/component-map"
|
||||||
|
|
||||||
|
export default function VtCurrencySelect({
|
||||||
|
nodes,
|
||||||
|
context,
|
||||||
|
}: {
|
||||||
|
nodes: LayoutComponentDefinition
|
||||||
|
context: LayoutContext
|
||||||
|
}) {
|
||||||
|
const props = nodes.config ?? {}
|
||||||
|
if (!props.trigger.text || props.items.length === 0) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Select>
|
||||||
|
<Select.Trigger
|
||||||
|
className={props.trigger.className + " flex items-center gap-1"}
|
||||||
|
>
|
||||||
|
{props.trigger.icon && (
|
||||||
|
<img
|
||||||
|
src={props.trigger.icon}
|
||||||
|
alt={props.trigger.text}
|
||||||
|
className="w-5 h-5 rounded-[50%] mr-3"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{props.trigger.text} {props.trigger.isShowArrow && <ChevronDown />}
|
||||||
|
</Select.Trigger>
|
||||||
|
<Select.Content>
|
||||||
|
{props.items.length > 0 &&
|
||||||
|
props.items.map(
|
||||||
|
(
|
||||||
|
item: {
|
||||||
|
text: string
|
||||||
|
className?: string
|
||||||
|
href?: string
|
||||||
|
icon?: string
|
||||||
|
},
|
||||||
|
index: number
|
||||||
|
) => (
|
||||||
|
<Select.Item
|
||||||
|
value={item.text || ""}
|
||||||
|
key={item.text + index}
|
||||||
|
className={item.className || ""}
|
||||||
|
>
|
||||||
|
{item.icon && (
|
||||||
|
<img
|
||||||
|
src={item.icon}
|
||||||
|
alt={item.text}
|
||||||
|
className="w-5 h-5 rounded-[50%] mr-3"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{item.href ? (
|
||||||
|
<LocalizedClientLink href={item.href}>
|
||||||
|
{item.text}
|
||||||
|
</LocalizedClientLink>
|
||||||
|
) : (
|
||||||
|
item.text
|
||||||
|
)}
|
||||||
|
</Select.Item>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</Select.Content>
|
||||||
|
</Select>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -21,7 +21,7 @@ export default function VtImage({
|
||||||
const props = (nodes.config as VtImageConfig) ?? {}
|
const props = (nodes.config as VtImageConfig) ?? {}
|
||||||
return (
|
return (
|
||||||
<div className={clx("relative", props.className)}>
|
<div className={clx("relative", props.className)}>
|
||||||
<Image src={props.src} alt={props.alt} fill objectFit={props.objectFit ?? "contain"} />
|
<img src={props.src} alt={props.alt} className={clx("w-full h-full object-cover", props.objectFit)} />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
"use client"
|
||||||
|
import {
|
||||||
|
LayoutComponentDefinition,
|
||||||
|
LayoutContext,
|
||||||
|
} from "@vibentec/component-map"
|
||||||
|
import LocalizedClientLink from "@modules/common/components/localized-client-link"
|
||||||
|
import * as MedusaIcons from "@medusajs/icons"
|
||||||
|
import * as CustomIcons from "@modules/common/icons"
|
||||||
|
export default function VtMenuItem({
|
||||||
|
nodes,
|
||||||
|
context,
|
||||||
|
}: {
|
||||||
|
nodes: LayoutComponentDefinition
|
||||||
|
context: LayoutContext
|
||||||
|
}) {
|
||||||
|
const props = nodes.config ?? {}
|
||||||
|
const title = props.title ?? ""
|
||||||
|
const items = props.items ?? []
|
||||||
|
const icon = props.icon ?? ""
|
||||||
|
|
||||||
|
const getIconComponent = (icon: string | undefined) => {
|
||||||
|
return icon
|
||||||
|
? (MedusaIcons as Record<string, any>)[icon] ??
|
||||||
|
(CustomIcons as Record<string, any>)[icon]
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className={props.className ?? "flex flex-col gap-y-2"}>
|
||||||
|
<span>{title}</span>
|
||||||
|
<ul className="grid grid-cols-1 gap-2" data-testid="footer-categories">
|
||||||
|
{items.map((item: { text: string; href?: string; icon?: string }, index: number) => {
|
||||||
|
const Icon = getIconComponent(item.icon)
|
||||||
|
return (
|
||||||
|
<li
|
||||||
|
key={`${item.text}-${index}`}
|
||||||
|
className={props.itemClassName ?? "text-ui-fg-subtle txt-small"}
|
||||||
|
>
|
||||||
|
{Icon && <Icon className={props.iconClassName ?? "inline-block mr-2"} />}
|
||||||
|
{item.href ? (
|
||||||
|
<LocalizedClientLink
|
||||||
|
className="hover:text-ui-fg-base"
|
||||||
|
href={item.href}
|
||||||
|
data-testid="category-link"
|
||||||
|
>
|
||||||
|
{item.text}
|
||||||
|
</LocalizedClientLink>
|
||||||
|
) : (
|
||||||
|
<span className="hover:text-ui-fg-base">{item.text}</span>
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue