namds/refactor-base-layout #8
|
|
@ -20,22 +20,13 @@ export default function VtButton({
|
|||
(CustomIcons as Record<string, any>)[iconName]
|
||||
: undefined
|
||||
return (
|
||||
<>
|
||||
{props?.icon && (
|
||||
<IconButton className={props?.className ?? ""}>
|
||||
{IconComponent && (
|
||||
<IconComponent className={props?.iconClassName ?? ""} />
|
||||
)}
|
||||
{props?.label && (
|
||||
<span className={props?.labelClassName ?? ""}>{props.label}</span>
|
||||
)}
|
||||
</IconButton>
|
||||
<IconButton className={props?.className ?? ""}>
|
||||
{IconComponent && (
|
||||
<IconComponent className={props?.iconClassName ?? ""} />
|
||||
)}
|
||||
{!props?.icon && (
|
||||
<Button className={props?.className ?? ""}>
|
||||
{props?.label && <span>{props.label}</span>}
|
||||
</Button>
|
||||
{props?.label && (
|
||||
<span className={props?.labelClassName ?? ""}>{props.label}</span>
|
||||
)}
|
||||
</>
|
||||
</IconButton>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) ?? {}
|
||||
return (
|
||||
<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>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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