feat(dashboard): restructure create product flow (#7374)
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
import { Checkbox } from "@medusajs/ui"
|
||||
import { Controller } from "react-hook-form"
|
||||
import { useDataGridCell } from "../hooks"
|
||||
import { DataGridCellProps } from "../types"
|
||||
import { DataGridCellContainer } from "./data-grid-cell-container"
|
||||
|
||||
export const DataGridBooleanCell = <TData, TValue = any>({
|
||||
field,
|
||||
context,
|
||||
disabled,
|
||||
}: DataGridCellProps<TData, TValue> & { disabled?: boolean }) => {
|
||||
const { control, attributes, container, onChange } = useDataGridCell({
|
||||
field,
|
||||
context,
|
||||
})
|
||||
|
||||
return (
|
||||
<Controller
|
||||
control={control}
|
||||
name={field}
|
||||
render={({ field: { value, onChange: _, ...field } }) => {
|
||||
return (
|
||||
<DataGridCellContainer {...container}>
|
||||
<Checkbox
|
||||
checked={value}
|
||||
onCheckedChange={(next) => onChange(next, value)}
|
||||
{...field}
|
||||
{...attributes}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</DataGridCellContainer>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import { PropsWithChildren } from "react"
|
||||
import { DataGridCellContainerProps } from "../types"
|
||||
|
||||
type ContainerProps = PropsWithChildren<DataGridCellContainerProps>
|
||||
|
||||
export const DataGridCellContainer = ({
|
||||
isAnchor,
|
||||
placeholder,
|
||||
overlay,
|
||||
wrapper,
|
||||
children,
|
||||
}: ContainerProps) => {
|
||||
return (
|
||||
<div className="static size-full">
|
||||
<div className="flex size-full items-start outline-none" tabIndex={-1}>
|
||||
<div {...wrapper} className="relative size-full min-w-0 flex-1">
|
||||
<div className="relative z-[1] flex size-full items-center justify-center">
|
||||
<RenderChildren isAnchor={isAnchor} placeholder={placeholder}>
|
||||
{children}
|
||||
</RenderChildren>
|
||||
</div>
|
||||
{!isAnchor && (
|
||||
<div
|
||||
{...overlay}
|
||||
tabIndex={-1}
|
||||
className="absolute inset-0 z-[2] size-full"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{/* {showDragHandle && (
|
||||
<div className="bg-ui-bg-interactive absolute -bottom-[1.5px] -right-[1.5px] size-[3px]" />
|
||||
)} */}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const RenderChildren = ({
|
||||
isAnchor,
|
||||
placeholder,
|
||||
children,
|
||||
}: PropsWithChildren<
|
||||
Pick<DataGridCellContainerProps, "isAnchor" | "placeholder">
|
||||
>) => {
|
||||
if (!isAnchor && placeholder) {
|
||||
return placeholder
|
||||
}
|
||||
|
||||
return children
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
import { TrianglesMini } from "@medusajs/icons"
|
||||
import { clx } from "@medusajs/ui"
|
||||
import { ComponentPropsWithoutRef, forwardRef, memo } from "react"
|
||||
import { Controller } from "react-hook-form"
|
||||
|
||||
import { countries } from "../../../lib/countries"
|
||||
import { useDataGridCell } from "../hooks"
|
||||
import { DataGridCellProps } from "../types"
|
||||
import { DataGridCellContainer } from "./data-grid-cell-container"
|
||||
|
||||
export const DataGridCountrySelectCell = <TData, TValue = any>({
|
||||
field,
|
||||
context,
|
||||
}: DataGridCellProps<TData, TValue>) => {
|
||||
const { control, attributes, container, onChange } = useDataGridCell({
|
||||
field,
|
||||
context,
|
||||
})
|
||||
|
||||
return (
|
||||
<Controller
|
||||
control={control}
|
||||
name={field}
|
||||
render={({ field: { value, onChange: _, disabled, ...field } }) => {
|
||||
return (
|
||||
<DataGridCellContainer
|
||||
{...container}
|
||||
placeholder={
|
||||
<DataGridCountryCellPlaceholder
|
||||
value={value}
|
||||
disabled={disabled}
|
||||
attributes={attributes}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<MemoizedDataGridCountryCell
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value, value)}
|
||||
disabled={disabled}
|
||||
{...attributes}
|
||||
{...field}
|
||||
/>
|
||||
</DataGridCellContainer>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const DataGridCountryCellPlaceholder = ({
|
||||
value,
|
||||
disabled,
|
||||
attributes,
|
||||
}: {
|
||||
value?: string
|
||||
disabled?: boolean
|
||||
attributes: Record<string, any>
|
||||
}) => {
|
||||
const country = countries.find((c) => c.iso_2 === value)
|
||||
|
||||
return (
|
||||
<div className="relative flex size-full" {...attributes}>
|
||||
<TrianglesMini
|
||||
className={clx(
|
||||
"text-ui-fg-muted transition-fg pointer-events-none absolute right-4 top-1/2 -translate-y-1/2",
|
||||
{
|
||||
"text-ui-fg-disabled": disabled,
|
||||
}
|
||||
)}
|
||||
/>
|
||||
<div
|
||||
className={clx(
|
||||
"txt-compact-small w-full appearance-none bg-transparent px-4 py-2.5 outline-none"
|
||||
)}
|
||||
>
|
||||
{country?.display_name}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const DataGridCountryCellImpl = forwardRef<
|
||||
HTMLSelectElement,
|
||||
ComponentPropsWithoutRef<"select">
|
||||
>(({ disabled, className, ...props }, ref) => {
|
||||
return (
|
||||
<div className="relative flex size-full">
|
||||
<TrianglesMini
|
||||
className={clx(
|
||||
"text-ui-fg-muted transition-fg pointer-events-none absolute right-4 top-1/2 -translate-y-1/2",
|
||||
{
|
||||
"text-ui-fg-disabled": disabled,
|
||||
}
|
||||
)}
|
||||
/>
|
||||
<select
|
||||
{...props}
|
||||
ref={ref}
|
||||
className={clx(
|
||||
"txt-compact-small w-full appearance-none bg-transparent px-4 py-2.5 outline-none",
|
||||
className
|
||||
)}
|
||||
>
|
||||
<option value=""></option>
|
||||
{countries.map((country) => (
|
||||
<option key={country.iso_2} value={country.iso_2}>
|
||||
{country.display_name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
DataGridCountryCellImpl.displayName = "DataGridCountryCell"
|
||||
|
||||
const MemoizedDataGridCountryCell = memo(DataGridCountryCellImpl)
|
||||
@@ -0,0 +1,54 @@
|
||||
import CurrencyInput from "react-currency-input-field"
|
||||
import { Controller } from "react-hook-form"
|
||||
|
||||
import { currencies } from "../../../lib/currencies"
|
||||
import { useDataGridCell } from "../hooks"
|
||||
import { DataGridCellProps } from "../types"
|
||||
import { DataGridCellContainer } from "./data-grid-cell-container"
|
||||
|
||||
interface DataGridCurrencyCellProps<TData, TValue = any>
|
||||
extends DataGridCellProps<TData, TValue> {
|
||||
code: string
|
||||
}
|
||||
|
||||
export const DataGridCurrencyCell = <TData, TValue = any>({
|
||||
field,
|
||||
context,
|
||||
code,
|
||||
}: DataGridCurrencyCellProps<TData, TValue>) => {
|
||||
const { control, attributes, container } = useDataGridCell({
|
||||
field,
|
||||
context,
|
||||
})
|
||||
|
||||
const currency = currencies[code.toUpperCase()]
|
||||
|
||||
return (
|
||||
<Controller
|
||||
control={control}
|
||||
name={field}
|
||||
render={({ field: { value, onChange, ...field } }) => {
|
||||
return (
|
||||
<DataGridCellContainer {...container}>
|
||||
<div className="flex size-full items-center gap-2 px-4 py-2.5">
|
||||
<span className="txt-compact-small text-ui-fg-muted" aria-hidden>
|
||||
{currency.symbol_native}
|
||||
</span>
|
||||
<CurrencyInput
|
||||
{...field}
|
||||
{...attributes}
|
||||
className="txt-compact-small flex-1 appearance-none bg-transparent text-right outline-none"
|
||||
value={value}
|
||||
onValueChange={(_value, _name, values) =>
|
||||
onChange(values?.value)
|
||||
}
|
||||
decimalScale={currency.decimal_digits}
|
||||
decimalsLimit={currency.decimal_digits}
|
||||
/>
|
||||
</div>
|
||||
</DataGridCellContainer>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { useDataGridCell } from "../hooks"
|
||||
import { DataGridCellProps } from "../types"
|
||||
import { DataGridCellContainer } from "./data-grid-cell-container"
|
||||
|
||||
export const DataGridNumberCell = <TData, TValue = any>({
|
||||
field,
|
||||
context,
|
||||
}: DataGridCellProps<TData, TValue>) => {
|
||||
const { register, attributes, container } = useDataGridCell({
|
||||
field,
|
||||
context,
|
||||
})
|
||||
|
||||
return (
|
||||
<DataGridCellContainer {...container}>
|
||||
<input
|
||||
{...attributes}
|
||||
type="number"
|
||||
{...register(field, {
|
||||
valueAsNumber: true,
|
||||
})}
|
||||
/>
|
||||
</DataGridCellContainer>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import { PropsWithChildren } from "react"
|
||||
|
||||
type DataGridReadOnlyCellProps = PropsWithChildren
|
||||
|
||||
export const DataGridReadOnlyCell = ({
|
||||
children,
|
||||
}: DataGridReadOnlyCellProps) => {
|
||||
return (
|
||||
<div className="bg-ui-bg-subtle txt-compact-small text-ui-fg-subtle flex size-full cursor-not-allowed items-center px-4 py-2.5 outline-none">
|
||||
<span>{children}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import { Select, clx } from "@medusajs/ui"
|
||||
import { Controller } from "react-hook-form"
|
||||
import { useDataGridCell } from "../hooks"
|
||||
import { DataGridCellProps } from "../types"
|
||||
import { DataGridCellContainer } from "./data-grid-cell-container"
|
||||
|
||||
interface DataGridSelectCellProps<TData, TValue = any>
|
||||
extends DataGridCellProps<TData, TValue> {
|
||||
options: { label: string; value: string }[]
|
||||
}
|
||||
|
||||
export const DataGridSelectCell = <TData, TValue = any>({
|
||||
context,
|
||||
options,
|
||||
field,
|
||||
}: DataGridSelectCellProps<TData, TValue>) => {
|
||||
const { control, attributes, container } = useDataGridCell({
|
||||
field,
|
||||
context,
|
||||
})
|
||||
|
||||
return (
|
||||
<Controller
|
||||
control={control}
|
||||
name={field}
|
||||
render={({ field: { onChange, ref, ...field } }) => {
|
||||
return (
|
||||
<DataGridCellContainer {...container}>
|
||||
<Select {...field} onValueChange={onChange}>
|
||||
<Select.Trigger
|
||||
{...attributes}
|
||||
ref={ref}
|
||||
className={clx(
|
||||
"h-full w-full rounded-none bg-transparent px-4 py-2.5 shadow-none",
|
||||
"hover:bg-transparent focus:shadow-none data-[state=open]:!shadow-none"
|
||||
)}
|
||||
>
|
||||
<Select.Value />
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
{options.map((option) => (
|
||||
<Select.Item key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</Select.Item>
|
||||
))}
|
||||
</Select.Content>
|
||||
</Select>
|
||||
</DataGridCellContainer>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import { clx } from "@medusajs/ui"
|
||||
import { Controller } from "react-hook-form"
|
||||
|
||||
import { useDataGridCell } from "../hooks"
|
||||
import { DataGridCellProps } from "../types"
|
||||
import { DataGridCellContainer } from "./data-grid-cell-container"
|
||||
|
||||
export const DataGridTextCell = <TData, TValue = any>({
|
||||
field,
|
||||
context,
|
||||
}: DataGridCellProps<TData, TValue>) => {
|
||||
const { control, attributes, container, onChange } = useDataGridCell({
|
||||
field,
|
||||
context,
|
||||
})
|
||||
|
||||
return (
|
||||
<Controller
|
||||
control={control}
|
||||
name={field}
|
||||
render={({ field: { value, onChange: _, ...field } }) => {
|
||||
return (
|
||||
<DataGridCellContainer {...container}>
|
||||
<input
|
||||
className={clx(
|
||||
"txt-compact-small text-ui-fg-subtle flex size-full cursor-pointer items-center justify-center bg-transparent px-4 py-2.5 outline-none",
|
||||
"focus:cursor-text"
|
||||
)}
|
||||
autoComplete="off"
|
||||
tabIndex={-1}
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value, value)}
|
||||
{...attributes}
|
||||
{...field}
|
||||
/>
|
||||
</DataGridCellContainer>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user