feat(dashboard): Hitting escape restores previous value (#8654)
* feat(dashboard): Hitting escape restores previous value * update lock
This commit is contained in:
+3
-3
@@ -1,10 +1,10 @@
|
|||||||
import { PropsWithChildren } from "react"
|
import { PropsWithChildren } from "react"
|
||||||
|
|
||||||
type DataGridReadOnlyCellProps = PropsWithChildren
|
type DataGridReadonlyCellProps = PropsWithChildren
|
||||||
|
|
||||||
export const DataGridReadOnlyCell = ({
|
export const DataGridReadonlyCell = ({
|
||||||
children,
|
children,
|
||||||
}: DataGridReadOnlyCellProps) => {
|
}: DataGridReadonlyCellProps) => {
|
||||||
return (
|
return (
|
||||||
<div className="bg-ui-bg-subtle txt-compact-small text-ui-fg-subtle flex size-full cursor-not-allowed items-center overflow-hidden px-4 py-2.5 outline-none">
|
<div className="bg-ui-bg-subtle txt-compact-small text-ui-fg-subtle flex size-full cursor-not-allowed items-center overflow-hidden px-4 py-2.5 outline-none">
|
||||||
<span className="truncate">{children}</span>
|
<span className="truncate">{children}</span>
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export { DataGridBooleanCell } from "./data-grid-boolean-cell"
|
||||||
|
export { DataGridCurrencyCell } from "./data-grid-currency-cell"
|
||||||
|
export { DataGridNumberCell } from "./data-grid-number-cell"
|
||||||
|
export { DataGridReadonlyCell as DataGridReadOnlyCell } from "./data-grid-readonly-cell"
|
||||||
|
export { DataGridTextCell } from "./data-grid-text-cell"
|
||||||
|
|
||||||
+5
-7
@@ -1,12 +1,12 @@
|
|||||||
import { HttpTypes } from "@medusajs/types"
|
import { HttpTypes } from "@medusajs/types"
|
||||||
import { CellContext, ColumnDef } from "@tanstack/react-table"
|
import { CellContext, ColumnDef } from "@tanstack/react-table"
|
||||||
import { TFunction } from "i18next"
|
import { TFunction } from "i18next"
|
||||||
import { IncludesTaxTooltip } from "../../../components/common/tax-badge/tax-badge"
|
import { IncludesTaxTooltip } from "../../common/tax-badge/tax-badge"
|
||||||
import { DataGridCurrencyCell } from "../data-grid-cells/data-grid-currency-cell"
|
import { DataGridCurrencyCell } from "../data-grid-cells/data-grid-currency-cell"
|
||||||
import { DataGridReadOnlyCell } from "../data-grid-cells/data-grid-readonly-cell"
|
import { DataGridReadonlyCell } from "../data-grid-cells/data-grid-readonly-cell"
|
||||||
import { createDataGridHelper } from "../utils"
|
import { createDataGridHelper } from "../utils"
|
||||||
|
|
||||||
export const getPriceColumns = <TData,>({
|
export const createDataGridPriceColumns = <TData,>({
|
||||||
currencies,
|
currencies,
|
||||||
regions,
|
regions,
|
||||||
pricePreferences,
|
pricePreferences,
|
||||||
@@ -44,7 +44,7 @@ export const getPriceColumns = <TData,>({
|
|||||||
),
|
),
|
||||||
cell: (context) => {
|
cell: (context) => {
|
||||||
if (isReadyOnly?.(context)) {
|
if (isReadyOnly?.(context)) {
|
||||||
return <DataGridReadOnlyCell />
|
return <DataGridReadonlyCell />
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -55,7 +55,6 @@ export const getPriceColumns = <TData,>({
|
|||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
type: "string",
|
|
||||||
})
|
})
|
||||||
}) ?? []),
|
}) ?? []),
|
||||||
...(regions?.map((region) => {
|
...(regions?.map((region) => {
|
||||||
@@ -78,7 +77,7 @@ export const getPriceColumns = <TData,>({
|
|||||||
),
|
),
|
||||||
cell: (context) => {
|
cell: (context) => {
|
||||||
if (isReadyOnly?.(context)) {
|
if (isReadyOnly?.(context)) {
|
||||||
return <DataGridReadOnlyCell />
|
return <DataGridReadonlyCell />
|
||||||
}
|
}
|
||||||
|
|
||||||
const currency = currencies?.find((c) => c === region.currency_code)
|
const currency = currencies?.find((c) => c === region.currency_code)
|
||||||
@@ -94,7 +93,6 @@ export const getPriceColumns = <TData,>({
|
|||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
type: "string",
|
|
||||||
})
|
})
|
||||||
}) ?? []),
|
}) ?? []),
|
||||||
]
|
]
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
export * from "./create-data-grid-price-columns"
|
||||||
+60
-19
@@ -29,19 +29,19 @@ import {
|
|||||||
} from "react"
|
} from "react"
|
||||||
import { FieldValues, Path, PathValue, UseFormReturn } from "react-hook-form"
|
import { FieldValues, Path, PathValue, UseFormReturn } from "react-hook-form"
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
import { useCommandHistory } from "../../../hooks/use-command-history"
|
import { useCommandHistory } from "../../hooks/use-command-history"
|
||||||
import { DataGridContext } from "../context"
|
import { DataGridContext } from "./context"
|
||||||
import { useGridQueryTool } from "../hooks"
|
import { useGridQueryTool } from "./hooks"
|
||||||
import { BulkUpdateCommand, Matrix, UpdateCommand } from "../models"
|
import { BulkUpdateCommand, Matrix, UpdateCommand } from "./models"
|
||||||
import { CellCoords, CellType } from "../types"
|
import { CellCoords, CellSnapshot, CellType } from "./types"
|
||||||
import {
|
import {
|
||||||
convertArrayToPrimitive,
|
convertArrayToPrimitive,
|
||||||
generateCellId,
|
generateCellId,
|
||||||
getColumnName,
|
getColumnName,
|
||||||
isCellMatch,
|
isCellMatch,
|
||||||
} from "../utils"
|
} from "./utils"
|
||||||
|
|
||||||
interface DataGridRootProps<
|
export interface DataGridRootProps<
|
||||||
TData,
|
TData,
|
||||||
TFieldValues extends FieldValues = FieldValues
|
TFieldValues extends FieldValues = FieldValues
|
||||||
> {
|
> {
|
||||||
@@ -91,17 +91,8 @@ export const DataGridRoot = <
|
|||||||
const [isDragging, setIsDragging] = useState(false)
|
const [isDragging, setIsDragging] = useState(false)
|
||||||
|
|
||||||
const [isEditing, setIsEditing] = useState(false)
|
const [isEditing, setIsEditing] = useState(false)
|
||||||
|
const [cellValueSnapshot, setCellValueSnapshot] =
|
||||||
const onEditingChangeHandler = useCallback(
|
useState<CellSnapshot<TFieldValues> | null>(null)
|
||||||
(value: boolean) => {
|
|
||||||
if (onEditingChange) {
|
|
||||||
onEditingChange(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsEditing(value)
|
|
||||||
},
|
|
||||||
[onEditingChange]
|
|
||||||
)
|
|
||||||
|
|
||||||
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({})
|
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({})
|
||||||
|
|
||||||
@@ -233,6 +224,53 @@ export const DataGridRoot = <
|
|||||||
|
|
||||||
const queryTool = useGridQueryTool(containerRef)
|
const queryTool = useGridQueryTool(containerRef)
|
||||||
|
|
||||||
|
const createCellSnapshot =
|
||||||
|
useCallback((): CellSnapshot<TFieldValues> | null => {
|
||||||
|
if (!anchor) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const field = matrix.getCellField(anchor)
|
||||||
|
|
||||||
|
if (!field) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = getValues(field as Path<TFieldValues>)
|
||||||
|
|
||||||
|
return {
|
||||||
|
field,
|
||||||
|
value,
|
||||||
|
}
|
||||||
|
}, [getValues, matrix, anchor])
|
||||||
|
|
||||||
|
const restoreSnapshot = useCallback(() => {
|
||||||
|
if (!cellValueSnapshot) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const { field, value } = cellValueSnapshot
|
||||||
|
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
setValue(field as Path<TFieldValues>, value)
|
||||||
|
})
|
||||||
|
}, [setValue, cellValueSnapshot])
|
||||||
|
|
||||||
|
const onEditingChangeHandler = useCallback(
|
||||||
|
(value: boolean) => {
|
||||||
|
if (onEditingChange) {
|
||||||
|
onEditingChange(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
setCellValueSnapshot(createCellSnapshot())
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsEditing(value)
|
||||||
|
},
|
||||||
|
[createCellSnapshot, onEditingChange]
|
||||||
|
)
|
||||||
|
|
||||||
const registerCell = useCallback(
|
const registerCell = useCallback(
|
||||||
(coords: CellCoords, field: string, type: CellType) => {
|
(coords: CellCoords, field: string, type: CellType) => {
|
||||||
matrix.registerField(coords.row, coords.col, field, type)
|
matrix.registerField(coords.row, coords.col, field, type)
|
||||||
@@ -694,11 +732,14 @@ export const DataGridRoot = <
|
|||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
|
|
||||||
|
// try to restore the previous value
|
||||||
|
restoreSnapshot()
|
||||||
|
|
||||||
// Restore focus to the container element
|
// Restore focus to the container element
|
||||||
const container = queryTool?.getContainer(anchor)
|
const container = queryTool?.getContainer(anchor)
|
||||||
container?.focus()
|
container?.focus()
|
||||||
},
|
},
|
||||||
[queryTool, isEditing, anchor]
|
[queryTool, isEditing, anchor, restoreSnapshot]
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleTabKey = useCallback(
|
const handleTabKey = useCallback(
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from "./data-grid-root"
|
|
||||||
+7
-7
@@ -1,16 +1,16 @@
|
|||||||
import { Table } from "@medusajs/ui"
|
import { Table } from "@medusajs/ui"
|
||||||
import { ColumnDef } from "@tanstack/react-table"
|
import { ColumnDef } from "@tanstack/react-table"
|
||||||
import { Skeleton } from "../../../common/skeleton"
|
import { Skeleton } from "../common/skeleton"
|
||||||
|
|
||||||
type DataTableSkeletonProps = {
|
type DataGridSkeletonProps<TData> = {
|
||||||
columns: ColumnDef<any, any>[]
|
columns: ColumnDef<TData>[]
|
||||||
rowCount: number
|
rows?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DataGridSkeleton = ({
|
export const DataGridSkeleton = <TData,>({
|
||||||
columns,
|
columns,
|
||||||
rowCount,
|
rows: rowCount = 10,
|
||||||
}: DataTableSkeletonProps) => {
|
}: DataGridSkeletonProps<TData>) => {
|
||||||
const rows = Array.from({ length: rowCount }, (_, i) => i)
|
const rows = Array.from({ length: rowCount }, (_, i) => i)
|
||||||
|
|
||||||
const colCount = columns.length
|
const colCount = columns.length
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { FieldValues } from "react-hook-form"
|
||||||
|
|
||||||
|
import {
|
||||||
|
DataGridBooleanCell,
|
||||||
|
DataGridCurrencyCell,
|
||||||
|
DataGridNumberCell,
|
||||||
|
DataGridReadOnlyCell,
|
||||||
|
DataGridTextCell,
|
||||||
|
} from "./data-grid-cells"
|
||||||
|
import { DataGridRoot, DataGridRootProps } from "./data-grid-root"
|
||||||
|
import { DataGridSkeleton } from "./data-grid-skeleton"
|
||||||
|
|
||||||
|
interface DataGridProps<TData, TFieldValues extends FieldValues = FieldValues>
|
||||||
|
extends DataGridRootProps<TData, TFieldValues> {
|
||||||
|
isLoading?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const _DataGrid = <TData, TFieldValues extends FieldValues = FieldValues>({
|
||||||
|
isLoading,
|
||||||
|
...props
|
||||||
|
}: DataGridProps<TData, TFieldValues>) => {
|
||||||
|
return isLoading ? (
|
||||||
|
<DataGridSkeleton columns={props.columns} />
|
||||||
|
) : (
|
||||||
|
<DataGridRoot {...props} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DataGrid = Object.assign(_DataGrid, {
|
||||||
|
BooleanCell: DataGridBooleanCell,
|
||||||
|
TextCell: DataGridTextCell,
|
||||||
|
NumberCell: DataGridNumberCell,
|
||||||
|
CurrencyCell: DataGridCurrencyCell,
|
||||||
|
ReadonlyCell: DataGridReadOnlyCell,
|
||||||
|
})
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export * from "./data-grid"
|
||||||
|
export * from "./data-grid-column-helpers"
|
||||||
|
export { createDataGridHelper } from "./utils"
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { CellContext } from "@tanstack/react-table"
|
import { CellContext } from "@tanstack/react-table"
|
||||||
import React, { PropsWithChildren, ReactNode, RefObject } from "react"
|
import React, { PropsWithChildren, ReactNode, RefObject } from "react"
|
||||||
|
import { FieldValues, Path, PathValue } from "react-hook-form"
|
||||||
|
|
||||||
export type CellType = "text" | "number" | "select" | "boolean"
|
export type CellType = "text" | "number" | "select" | "boolean"
|
||||||
|
|
||||||
@@ -70,3 +71,8 @@ export interface DataGridCellContainerProps extends PropsWithChildren<{}> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type DataGridColumnType = "string" | "number" | "boolean"
|
export type DataGridColumnType = "string" | "number" | "boolean"
|
||||||
|
|
||||||
|
export type CellSnapshot<TFieldValues extends FieldValues = FieldValues> = {
|
||||||
|
field: string
|
||||||
|
value: PathValue<TFieldValues, Path<TFieldValues>>
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
export enum GridCellType {
|
|
||||||
VOID = "void",
|
|
||||||
READONLY = "readonly",
|
|
||||||
EDITABLE = "editable",
|
|
||||||
OVERLAY = "overlay",
|
|
||||||
}
|
|
||||||
|
|
||||||
export const NON_INTERACTIVE_CELL_TYPES = [
|
|
||||||
GridCellType.VOID,
|
|
||||||
GridCellType.READONLY,
|
|
||||||
]
|
|
||||||
-653
@@ -1,653 +0,0 @@
|
|||||||
import { clx } from "@medusajs/ui"
|
|
||||||
import {
|
|
||||||
ColumnDef,
|
|
||||||
Row,
|
|
||||||
flexRender,
|
|
||||||
getCoreRowModel,
|
|
||||||
useReactTable,
|
|
||||||
} from "@tanstack/react-table"
|
|
||||||
import { useVirtualizer } from "@tanstack/react-virtual"
|
|
||||||
import {
|
|
||||||
MouseEvent as ReactMouseEvent,
|
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useRef,
|
|
||||||
useState,
|
|
||||||
} from "react"
|
|
||||||
import { FieldValues, Path, UseFormReturn } from "react-hook-form"
|
|
||||||
|
|
||||||
import {
|
|
||||||
Command,
|
|
||||||
useCommandHistory,
|
|
||||||
} from "../../../../hooks/use-command-history"
|
|
||||||
import { GridCellType, NON_INTERACTIVE_CELL_TYPES } from "../../constants"
|
|
||||||
|
|
||||||
type FieldCoordinates = {
|
|
||||||
column: number
|
|
||||||
row: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DataGridRootProps<
|
|
||||||
TData,
|
|
||||||
TFieldValues extends FieldValues = FieldValues
|
|
||||||
> {
|
|
||||||
data?: TData[]
|
|
||||||
columns: ColumnDef<TData>[]
|
|
||||||
state: UseFormReturn<TFieldValues>
|
|
||||||
getSubRows?: (row: TData) => TData[]
|
|
||||||
}
|
|
||||||
|
|
||||||
const ROW_HEIGHT = 40
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO: THIS IS OLD DATAGRID COMPONENT - REMOVE THIS AFTER ALL TABLE HAVE BEEN MIGRATED TO THE NEW DATAGRIDROOT FROM ../../data-grid
|
|
||||||
*/
|
|
||||||
|
|
||||||
export const DataGridRoot = <
|
|
||||||
TData,
|
|
||||||
TFieldValues extends FieldValues = FieldValues
|
|
||||||
>({
|
|
||||||
data = [],
|
|
||||||
columns,
|
|
||||||
state,
|
|
||||||
getSubRows,
|
|
||||||
}: DataGridRootProps<TData, TFieldValues>) => {
|
|
||||||
const tableContainerRef = useRef<HTMLDivElement>(null)
|
|
||||||
|
|
||||||
const { execute, undo, redo, canRedo, canUndo } = useCommandHistory()
|
|
||||||
const { register, control, getValues, setValue } = state
|
|
||||||
|
|
||||||
const grid = useReactTable({
|
|
||||||
data: data,
|
|
||||||
columns,
|
|
||||||
getSubRows,
|
|
||||||
getCoreRowModel: getCoreRowModel(),
|
|
||||||
meta: {
|
|
||||||
register: register,
|
|
||||||
control: control,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const { flatRows } = grid.getRowModel()
|
|
||||||
|
|
||||||
const rowVirtualizer = useVirtualizer({
|
|
||||||
count: flatRows.length,
|
|
||||||
estimateSize: () => ROW_HEIGHT,
|
|
||||||
getScrollElement: () => tableContainerRef.current,
|
|
||||||
measureElement:
|
|
||||||
typeof window !== "undefined" &&
|
|
||||||
navigator.userAgent.indexOf("Firefox") === -1
|
|
||||||
? (element) => element?.getBoundingClientRect().height
|
|
||||||
: undefined,
|
|
||||||
overscan: 5,
|
|
||||||
})
|
|
||||||
|
|
||||||
const [anchor, setAnchor] = useState<FieldCoordinates | null>(null)
|
|
||||||
|
|
||||||
const [isSelecting, setIsSelecting] = useState(false)
|
|
||||||
const [selection, setSelection] = useState<FieldCoordinates[]>([])
|
|
||||||
|
|
||||||
const [isDragging, setIsDragging] = useState(false)
|
|
||||||
const [dragSelection, setDragSelection] = useState<FieldCoordinates[]>([])
|
|
||||||
|
|
||||||
const handleFocusInner = (target: HTMLElement) => {
|
|
||||||
const editableField = target.querySelector("[data-field-id]")
|
|
||||||
|
|
||||||
if (editableField instanceof HTMLInputElement) {
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
editableField.focus()
|
|
||||||
editableField.setSelectionRange(
|
|
||||||
editableField.value.length,
|
|
||||||
editableField.value.length
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleBlurAnchor = () => {
|
|
||||||
const activeElement = document.activeElement
|
|
||||||
|
|
||||||
if (anchor && activeElement instanceof HTMLElement) {
|
|
||||||
activeElement.blur()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const isNonInteractive = (element: HTMLElement) => {
|
|
||||||
const type = element.getAttribute("data-cell-type")
|
|
||||||
|
|
||||||
if (!type) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return NON_INTERACTIVE_CELL_TYPES.includes(type as GridCellType)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleMouseDown = (e: ReactMouseEvent<HTMLTableCellElement>) => {
|
|
||||||
e.stopPropagation()
|
|
||||||
e.preventDefault()
|
|
||||||
|
|
||||||
const target = e.target
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the click was on a presentation element.
|
|
||||||
* If so, we don't want to set the anchor.
|
|
||||||
*/
|
|
||||||
if (target instanceof HTMLElement && isNonInteractive(target)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const rowIndex = parseInt(e.currentTarget.dataset.rowIndex!)
|
|
||||||
const columnIndex = parseInt(e.currentTarget.dataset.columnIndex!)
|
|
||||||
|
|
||||||
const isAnchor = getIsAnchor(rowIndex, columnIndex)
|
|
||||||
|
|
||||||
if (e.detail === 2 || isAnchor) {
|
|
||||||
handleFocusInner(e.currentTarget)
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
// reset focus so the previous cell doesn't keep the focus
|
|
||||||
handleBlurAnchor()
|
|
||||||
}
|
|
||||||
|
|
||||||
const coordinates: FieldCoordinates = {
|
|
||||||
row: rowIndex,
|
|
||||||
column: columnIndex,
|
|
||||||
}
|
|
||||||
|
|
||||||
setSelection([coordinates])
|
|
||||||
setAnchor(coordinates)
|
|
||||||
setIsSelecting(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleDragDown = (e: ReactMouseEvent<HTMLDivElement>) => {
|
|
||||||
e.stopPropagation()
|
|
||||||
setIsDragging(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
const getIsAnchor = (rowIndex: number, columnIndex: number) => {
|
|
||||||
return anchor?.row === rowIndex && anchor?.column === columnIndex
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleMouseOver = (e: ReactMouseEvent<HTMLTableCellElement>) => {
|
|
||||||
/**
|
|
||||||
* If we're not dragging and not selecting or there is no anchor,
|
|
||||||
* then we don't want to do anything.
|
|
||||||
*/
|
|
||||||
if ((!isSelecting && !isDragging) || !anchor) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const target = e.target
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the click was on a presentation element.
|
|
||||||
* If so, we don't want to add it to the selection.
|
|
||||||
*/
|
|
||||||
if (target instanceof HTMLElement && isNonInteractive(target)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const rowIndex = parseInt(e.currentTarget.dataset.rowIndex!)
|
|
||||||
const columnIndex = parseInt(e.currentTarget.dataset.columnIndex!)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If the target column is not the same as the anchor column,
|
|
||||||
* we don't want to add it to the selection.
|
|
||||||
*/
|
|
||||||
if (anchor?.column !== columnIndex) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const direction =
|
|
||||||
rowIndex > anchor.row ? "down" : rowIndex < anchor.row ? "up" : "none"
|
|
||||||
|
|
||||||
const last = selection[selection.length - 1] ?? anchor
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the current cell is a direct neighbour of the last cell
|
|
||||||
* in the selection.
|
|
||||||
*/
|
|
||||||
const isNeighbour = Math.abs(rowIndex - last.row) === 1
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If the current cell is a neighbour, we can simply update
|
|
||||||
* the selection based on the direction.
|
|
||||||
*/
|
|
||||||
if (isNeighbour) {
|
|
||||||
if (isSelecting) {
|
|
||||||
setSelection((prev) => {
|
|
||||||
return prev
|
|
||||||
.filter((cell) => {
|
|
||||||
if (direction === "down") {
|
|
||||||
return (
|
|
||||||
(cell.row <= rowIndex && cell.row >= anchor.row) ||
|
|
||||||
cell.row === anchor.row
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (direction === "up") {
|
|
||||||
return (
|
|
||||||
(cell.row >= rowIndex && cell.row <= anchor.row) ||
|
|
||||||
cell.row === anchor.row
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return cell.row === anchor.row
|
|
||||||
})
|
|
||||||
.concat({ row: rowIndex, column: columnIndex })
|
|
||||||
})
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isDragging) {
|
|
||||||
if (anchor.row === rowIndex) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
setDragSelection((prev) => {
|
|
||||||
return prev
|
|
||||||
.filter((cell) => {
|
|
||||||
if (direction === "down") {
|
|
||||||
return (
|
|
||||||
(cell.row <= rowIndex && cell.row >= anchor.row) ||
|
|
||||||
cell.row === anchor.row
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (direction === "up") {
|
|
||||||
return (
|
|
||||||
(cell.row >= rowIndex && cell.row <= anchor.row) ||
|
|
||||||
cell.row === anchor.row
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return cell.row === anchor.row
|
|
||||||
})
|
|
||||||
.concat({ row: rowIndex, column: columnIndex })
|
|
||||||
})
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If the current cell is not a neighbour, we instead
|
|
||||||
* need to calculate all the valid cells between the
|
|
||||||
* anchor and the current cell.
|
|
||||||
*/
|
|
||||||
let cells: FieldCoordinates[] = []
|
|
||||||
|
|
||||||
function selectCell(i: number, columnIndex: number) {
|
|
||||||
const possibleCell = tableContainerRef.current?.querySelector(
|
|
||||||
`[data-row-index="${i}"][data-column-index="${columnIndex}"]`
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!possibleCell) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const isPresentation = possibleCell.querySelector(
|
|
||||||
"[data-role=presentation]"
|
|
||||||
)
|
|
||||||
|
|
||||||
if (isPresentation) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
cells.push({ row: i, column: columnIndex })
|
|
||||||
}
|
|
||||||
|
|
||||||
if (direction === "down") {
|
|
||||||
for (let i = anchor.row; i <= rowIndex; i++) {
|
|
||||||
selectCell(i, columnIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (direction === "up") {
|
|
||||||
for (let i = anchor.row; i >= rowIndex; i--) {
|
|
||||||
selectCell(i, columnIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isSelecting) {
|
|
||||||
setSelection(cells)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isDragging) {
|
|
||||||
cells = cells.filter((cell) => cell.row !== anchor.row)
|
|
||||||
|
|
||||||
setDragSelection(cells)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const getIsDragTarget = (rowIndex: number, columnIndex: number) => {
|
|
||||||
return dragSelection.some(
|
|
||||||
(cell) => cell.row === rowIndex && cell.column === columnIndex
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const getIsSelected = (rowIndex: number, columnIndex: number) => {
|
|
||||||
return selection.some(
|
|
||||||
(cell) => cell.row === rowIndex && cell.column === columnIndex
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const getSelectionIds = useCallback((fields: FieldCoordinates[]) => {
|
|
||||||
return fields
|
|
||||||
.map((field) => {
|
|
||||||
const element = document.querySelector(
|
|
||||||
`[data-row-index="${field.row}"][data-column-index="${field.column}"]`
|
|
||||||
) as HTMLTableCellElement
|
|
||||||
|
|
||||||
return element
|
|
||||||
?.querySelector("[data-field-id]")
|
|
||||||
?.getAttribute("data-field-id")
|
|
||||||
})
|
|
||||||
.filter(Boolean) as string[]
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const getSelectionValues = useCallback(
|
|
||||||
(ids: string[]): string[] => {
|
|
||||||
const rawValues = ids.map((id) => {
|
|
||||||
return getValues(id as Path<TFieldValues>)
|
|
||||||
})
|
|
||||||
|
|
||||||
return rawValues.map((v) => JSON.stringify(v))
|
|
||||||
},
|
|
||||||
[getValues]
|
|
||||||
)
|
|
||||||
|
|
||||||
const setSelectionValues = useCallback(
|
|
||||||
(ids: string[], values: string[]) => {
|
|
||||||
ids.forEach((id, i) => {
|
|
||||||
const value = values[i]
|
|
||||||
|
|
||||||
if (!value) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
setValue(id as Path<TFieldValues>, JSON.parse(value), {
|
|
||||||
shouldDirty: true,
|
|
||||||
shouldTouch: true,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
[setValue]
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleCopy = useCallback(
|
|
||||||
(e: ClipboardEvent) => {
|
|
||||||
if (selection.length === 0) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const fieldIds = getSelectionIds(selection)
|
|
||||||
const values = getSelectionValues(fieldIds)
|
|
||||||
|
|
||||||
const clipboardData = values.join("\n")
|
|
||||||
|
|
||||||
e.clipboardData?.setData("text/plain", clipboardData)
|
|
||||||
e.preventDefault()
|
|
||||||
},
|
|
||||||
[selection, getSelectionIds, getSelectionValues]
|
|
||||||
)
|
|
||||||
|
|
||||||
const handlePaste = useCallback(
|
|
||||||
(e: ClipboardEvent) => {
|
|
||||||
const data = e.clipboardData?.getData("text/plain")
|
|
||||||
|
|
||||||
if (!data) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const fieldIds = getSelectionIds(selection)
|
|
||||||
|
|
||||||
const prev = getSelectionValues(fieldIds)
|
|
||||||
const next = data.split("\n")
|
|
||||||
|
|
||||||
const command = new GridCommand({
|
|
||||||
next,
|
|
||||||
prev,
|
|
||||||
selection: fieldIds,
|
|
||||||
setter: setSelectionValues,
|
|
||||||
})
|
|
||||||
|
|
||||||
execute(command)
|
|
||||||
},
|
|
||||||
[
|
|
||||||
selection,
|
|
||||||
execute,
|
|
||||||
getSelectionValues,
|
|
||||||
setSelectionValues,
|
|
||||||
getSelectionIds,
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleCommandHistory = useCallback(
|
|
||||||
(e: KeyboardEvent) => {
|
|
||||||
if (!canRedo && !canUndo) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.key.toLowerCase() === "z" && e.metaKey && !e.shiftKey) {
|
|
||||||
console.log(canUndo)
|
|
||||||
e.preventDefault()
|
|
||||||
undo()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.key.toLowerCase() === "z" && e.metaKey && e.shiftKey) {
|
|
||||||
e.preventDefault()
|
|
||||||
redo()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[undo, redo, canRedo, canUndo]
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleEndDrag = useCallback(() => {
|
|
||||||
if (!anchor) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const fieldIds = getSelectionIds(dragSelection)
|
|
||||||
const anchorId = getSelectionIds([anchor])
|
|
||||||
|
|
||||||
const anchorValue = getSelectionValues(anchorId)?.[0]
|
|
||||||
|
|
||||||
const prev = getSelectionValues(fieldIds)
|
|
||||||
const next = prev.map(() => anchorValue)
|
|
||||||
|
|
||||||
const command = new GridCommand({
|
|
||||||
next,
|
|
||||||
prev,
|
|
||||||
selection: fieldIds,
|
|
||||||
setter: setSelectionValues,
|
|
||||||
})
|
|
||||||
|
|
||||||
execute(command)
|
|
||||||
|
|
||||||
setSelection(dragSelection)
|
|
||||||
setDragSelection([])
|
|
||||||
setIsDragging(false)
|
|
||||||
}, [
|
|
||||||
anchor,
|
|
||||||
getSelectionIds,
|
|
||||||
dragSelection,
|
|
||||||
getSelectionValues,
|
|
||||||
setSelectionValues,
|
|
||||||
execute,
|
|
||||||
])
|
|
||||||
|
|
||||||
const handleMouseUp = useCallback(
|
|
||||||
(_e: MouseEvent) => {
|
|
||||||
if (isSelecting) {
|
|
||||||
setIsSelecting(false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isDragging) {
|
|
||||||
handleEndDrag()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[isDragging, isSelecting, handleEndDrag]
|
|
||||||
)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
document.addEventListener("mouseup", handleMouseUp)
|
|
||||||
document.addEventListener("copy", handleCopy)
|
|
||||||
document.addEventListener("paste", handlePaste)
|
|
||||||
document.addEventListener("keydown", handleCommandHistory)
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
document.removeEventListener("mouseup", handleMouseUp)
|
|
||||||
document.removeEventListener("copy", handleCopy)
|
|
||||||
document.removeEventListener("paste", handlePaste)
|
|
||||||
document.removeEventListener("keydown", handleCommandHistory)
|
|
||||||
}
|
|
||||||
}, [handleMouseUp, handleCopy, handlePaste, handleCommandHistory])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="bg-ui-bg-subtle size-full overflow-hidden">
|
|
||||||
<div
|
|
||||||
ref={tableContainerRef}
|
|
||||||
style={{
|
|
||||||
overflow: "auto",
|
|
||||||
position: "relative",
|
|
||||||
height: "100%",
|
|
||||||
userSelect: isSelecting || isDragging ? "none" : "auto",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<table className="text-ui-fg-subtle grid">
|
|
||||||
<thead className="txt-compact-small-plus bg-ui-bg-subtle sticky top-0 z-[1] grid">
|
|
||||||
{grid.getHeaderGroups().map((headerGroup) => (
|
|
||||||
<tr key={headerGroup.id} className="flex h-10 w-full">
|
|
||||||
{headerGroup.headers.map((header) => {
|
|
||||||
return (
|
|
||||||
<th
|
|
||||||
key={header.id}
|
|
||||||
style={{
|
|
||||||
width: header.getSize(),
|
|
||||||
}}
|
|
||||||
className="bg-ui-bg-base flex items-center border-b border-r px-4 py-2.5"
|
|
||||||
>
|
|
||||||
{flexRender(
|
|
||||||
header.column.columnDef.header,
|
|
||||||
header.getContext()
|
|
||||||
)}
|
|
||||||
</th>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</thead>
|
|
||||||
<tbody
|
|
||||||
className="relative grid"
|
|
||||||
style={{
|
|
||||||
height: `${rowVirtualizer.getTotalSize()}px`,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{rowVirtualizer.getVirtualItems().map((virtualRow) => {
|
|
||||||
const row = flatRows[virtualRow.index] as Row<TData>
|
|
||||||
|
|
||||||
return (
|
|
||||||
<tr
|
|
||||||
data-index={virtualRow.index}
|
|
||||||
ref={(node) => rowVirtualizer.measureElement(node)}
|
|
||||||
key={row.id}
|
|
||||||
style={{
|
|
||||||
transform: `translateY(${virtualRow.start}px)`,
|
|
||||||
}}
|
|
||||||
className="bg-ui-bg-subtle txt-compact-small absolute flex h-10 w-full"
|
|
||||||
>
|
|
||||||
{row.getVisibleCells().map((cell, index) => {
|
|
||||||
const isAnchor = getIsAnchor(virtualRow.index, index)
|
|
||||||
const isSelected = getIsSelected(virtualRow.index, index)
|
|
||||||
const isDragTarget = getIsDragTarget(
|
|
||||||
virtualRow.index,
|
|
||||||
index
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<td
|
|
||||||
key={cell.id}
|
|
||||||
style={{
|
|
||||||
width: cell.column.getSize(),
|
|
||||||
}}
|
|
||||||
onMouseDown={handleMouseDown}
|
|
||||||
onMouseOver={handleMouseOver}
|
|
||||||
data-row-index={virtualRow.index}
|
|
||||||
data-column-index={index}
|
|
||||||
className={clx(
|
|
||||||
"bg-ui-bg-base has-[[data-role='presentation']]:bg-ui-bg-subtle relative flex items-center border-b border-r p-0 outline-none",
|
|
||||||
"after:transition-fg after:border-ui-fg-interactive after:pointer-events-none after:invisible after:absolute after:-bottom-px after:-left-px after:-right-px after:-top-px after:box-border after:border-[2px] after:content-['']",
|
|
||||||
{
|
|
||||||
"after:visible": isAnchor,
|
|
||||||
"bg-ui-bg-highlight focus-within:bg-ui-bg-base":
|
|
||||||
isSelected || isAnchor,
|
|
||||||
"bg-ui-bg-base-hover": isDragTarget,
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
tabIndex={-1}
|
|
||||||
>
|
|
||||||
<div className="relative h-full w-full">
|
|
||||||
{flexRender(
|
|
||||||
cell.column.columnDef.cell,
|
|
||||||
cell.getContext()
|
|
||||||
)}
|
|
||||||
{isAnchor && (
|
|
||||||
<div
|
|
||||||
onMouseDown={handleDragDown}
|
|
||||||
className="bg-ui-fg-interactive absolute bottom-0 right-0 z-[3] size-1.5 cursor-ns-resize"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</tr>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
type GridCommandArgs = {
|
|
||||||
selection: string[]
|
|
||||||
setter: (selection: string[], values: string[]) => void
|
|
||||||
prev: string[]
|
|
||||||
next: string[]
|
|
||||||
}
|
|
||||||
|
|
||||||
class GridCommand implements Command {
|
|
||||||
private _selection: string[]
|
|
||||||
|
|
||||||
private _prev: string[]
|
|
||||||
private _next: string[]
|
|
||||||
|
|
||||||
private _setter: (selection: string[], values: string[]) => void
|
|
||||||
|
|
||||||
constructor({ selection, setter, prev, next }: GridCommandArgs) {
|
|
||||||
this._selection = selection
|
|
||||||
this._setter = setter
|
|
||||||
this._prev = prev
|
|
||||||
this._next = next
|
|
||||||
}
|
|
||||||
|
|
||||||
execute() {
|
|
||||||
this._setter(this._selection, this._next)
|
|
||||||
}
|
|
||||||
|
|
||||||
undo() {
|
|
||||||
this._setter(this._selection, this._prev)
|
|
||||||
}
|
|
||||||
|
|
||||||
redo() {
|
|
||||||
this.execute()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from "./data-grid-root"
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
export * from "./data-grid-skeleton"
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import { FieldValues } from "react-hook-form"
|
|
||||||
import { DataGridRoot, DataGridRootProps } from "./data-grid-root"
|
|
||||||
import { DataGridSkeleton } from "./data-grid-skeleton"
|
|
||||||
|
|
||||||
interface DataGridProps<TData, TFieldValues extends FieldValues = any>
|
|
||||||
extends DataGridRootProps<TData, TFieldValues> {
|
|
||||||
isLoading?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export const DataGrid = <TData, TFieldValues extends FieldValues = any>({
|
|
||||||
isLoading,
|
|
||||||
...props
|
|
||||||
}: DataGridProps<TData, TFieldValues>) => {
|
|
||||||
return isLoading ? (
|
|
||||||
<DataGridSkeleton columns={props.columns} rowCount={10} />
|
|
||||||
) : (
|
|
||||||
<DataGridRoot {...props} />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from "./data-grid"
|
|
||||||
-23
@@ -1,23 +0,0 @@
|
|||||||
import { Select } from "@medusajs/ui"
|
|
||||||
import { Controller, FieldValues } from "react-hook-form"
|
|
||||||
import { CellProps } from "../../../types"
|
|
||||||
|
|
||||||
interface BooleanCellProps<TFieldValues extends FieldValues = any>
|
|
||||||
extends CellProps<TFieldValues> {}
|
|
||||||
|
|
||||||
export const BooleanCell = <TFieldValues extends FieldValues = any>({
|
|
||||||
field,
|
|
||||||
meta,
|
|
||||||
}: BooleanCellProps<TFieldValues>) => {
|
|
||||||
const { control } = meta
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name={field}
|
|
||||||
render={({ field: { value, onChange, ref, ...rest } }) => {
|
|
||||||
return <Select value={value} onValueChange={onChange}></Select>
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
export * from "./boolean-cell"
|
|
||||||
-57
@@ -1,57 +0,0 @@
|
|||||||
import { CurrencyDTO } from "@medusajs/types"
|
|
||||||
import { useRef } from "react"
|
|
||||||
import Primitive from "react-currency-input-field"
|
|
||||||
import { Controller, FieldValues } from "react-hook-form"
|
|
||||||
|
|
||||||
import { GridCellType } from "../../../constants"
|
|
||||||
import { CellProps } from "../../../types"
|
|
||||||
|
|
||||||
interface CurrencyCellProps<TFieldValues extends FieldValues = any>
|
|
||||||
extends CellProps<TFieldValues> {
|
|
||||||
currency: CurrencyDTO
|
|
||||||
}
|
|
||||||
|
|
||||||
export const CurrencyCell = ({ currency, field, meta }: CurrencyCellProps) => {
|
|
||||||
const symbolRef = useRef<HTMLSpanElement>(null)
|
|
||||||
// @ts-ignore - Type is wrong
|
|
||||||
const decimalScale = currency.decimal_digits
|
|
||||||
|
|
||||||
const { control } = meta
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name={field}
|
|
||||||
render={({ field: { onChange, ...rest } }) => {
|
|
||||||
return (
|
|
||||||
<div className="relative size-full">
|
|
||||||
<span
|
|
||||||
ref={symbolRef}
|
|
||||||
role="presentation"
|
|
||||||
className="text-ui-fg-muted txt-compact-small pointer-events-none absolute left-0 top-0 select-none py-2.5 pl-4"
|
|
||||||
>
|
|
||||||
{currency.symbol_native}
|
|
||||||
</span>
|
|
||||||
<Primitive
|
|
||||||
data-input-field="true"
|
|
||||||
data-field-id={field}
|
|
||||||
data-cell-type={GridCellType.EDITABLE}
|
|
||||||
className="size-full bg-transparent py-2.5 pr-4 text-right outline-none"
|
|
||||||
style={{
|
|
||||||
paddingLeft: symbolRef.current?.offsetWidth
|
|
||||||
? `${symbolRef.current.offsetWidth + 8}px`
|
|
||||||
: "16px",
|
|
||||||
}}
|
|
||||||
decimalScale={decimalScale}
|
|
||||||
allowDecimals={decimalScale > 0}
|
|
||||||
onValueChange={(_value, _name, values) => {
|
|
||||||
onChange(values?.value)
|
|
||||||
}}
|
|
||||||
{...rest}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
export * from "./currency-cell"
|
|
||||||
-1
@@ -1 +0,0 @@
|
|||||||
export * from "./readonly-cell"
|
|
||||||
-14
@@ -1,14 +0,0 @@
|
|||||||
import { PropsWithChildren } from "react"
|
|
||||||
import { GridCellType } from "../../../constants"
|
|
||||||
|
|
||||||
export const ReadonlyCell = ({ children }: PropsWithChildren) => {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
role="cell"
|
|
||||||
data-cell-type={GridCellType.READONLY}
|
|
||||||
className="bg-ui-bg-base size-full cursor-not-allowed px-4 py-2.5"
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from "./text-cell"
|
|
||||||
-24
@@ -1,24 +0,0 @@
|
|||||||
import { FieldValues } from "react-hook-form"
|
|
||||||
import { CellProps } from "../../../types"
|
|
||||||
|
|
||||||
interface TextCellProps<TFieldValues extends FieldValues = any>
|
|
||||||
extends CellProps<TFieldValues> {}
|
|
||||||
|
|
||||||
export const TextCell = <TFieldValues extends FieldValues = any>({
|
|
||||||
field,
|
|
||||||
meta,
|
|
||||||
}: TextCellProps<TFieldValues>) => {
|
|
||||||
const { register } = meta
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex items-center justify-center px-4 py-2.5">
|
|
||||||
<input
|
|
||||||
className="txt-compact-small text-ui-fg-subtle w-full bg-transparent outline-none"
|
|
||||||
data-input-field="true"
|
|
||||||
data-field-id={field}
|
|
||||||
data-field-type="text"
|
|
||||||
{...register(field)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from "./void-cell"
|
|
||||||
-14
@@ -1,14 +0,0 @@
|
|||||||
import { PropsWithChildren } from "react"
|
|
||||||
import { GridCellType } from "../../../constants"
|
|
||||||
|
|
||||||
export const VoidCell = ({ children }: PropsWithChildren) => {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
role="cell"
|
|
||||||
data-cell-type={GridCellType.VOID}
|
|
||||||
className="bg-ui-bg-subtle size-full cursor-not-allowed px-4 py-2.5"
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import { Control, FieldValues, Path, UseFormRegister } from "react-hook-form"
|
|
||||||
|
|
||||||
export type DataGridMeta<TFieldValues extends FieldValues = FieldValues> = {
|
|
||||||
register: UseFormRegister<TFieldValues>
|
|
||||||
control: Control<TFieldValues>
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CellProps<TFieldValues extends FieldValues = FieldValues> {
|
|
||||||
field: Path<TFieldValues>
|
|
||||||
meta: DataGridMeta<TFieldValues>
|
|
||||||
}
|
|
||||||
+10
-16
@@ -3,9 +3,7 @@ import { useMemo } from "react"
|
|||||||
import { HttpTypes } from "@medusajs/types"
|
import { HttpTypes } from "@medusajs/types"
|
||||||
import { UseFormReturn } from "react-hook-form"
|
import { UseFormReturn } from "react-hook-form"
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
import { DataGridNumberCell } from "../../../../../components/data-grid/data-grid-cells/data-grid-number-cell"
|
import { DataGrid } from "../../../../../components/data-grid"
|
||||||
import { DataGridReadOnlyCell } from "../../../../../components/data-grid/data-grid-cells/data-grid-readonly-cell"
|
|
||||||
import { DataGridRoot } from "../../../../../components/data-grid/data-grid-root"
|
|
||||||
import { createDataGridHelper } from "../../../../../components/data-grid/utils"
|
import { createDataGridHelper } from "../../../../../components/data-grid/utils"
|
||||||
import { useRouteModal } from "../../../../../components/modals"
|
import { useRouteModal } from "../../../../../components/modals"
|
||||||
import { useStockLocations } from "../../../../../hooks/api/stock-locations"
|
import { useStockLocations } from "../../../../../hooks/api/stock-locations"
|
||||||
@@ -22,16 +20,13 @@ export const CreateInventoryAvailabilityForm = ({ form }: Props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="size-full">
|
<div className="size-full">
|
||||||
{isPending ? (
|
<DataGrid
|
||||||
<div>Loading...</div>
|
isLoading={isPending}
|
||||||
) : (
|
columns={columns}
|
||||||
<DataGridRoot
|
data={stock_locations}
|
||||||
columns={columns}
|
state={form}
|
||||||
data={stock_locations}
|
onEditingChange={(editing) => setCloseOnEscape(!editing)}
|
||||||
state={form}
|
/>
|
||||||
onEditingChange={(editing) => setCloseOnEscape(!editing)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -52,7 +47,7 @@ const useColumns = () => {
|
|||||||
),
|
),
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
return (
|
return (
|
||||||
<DataGridReadOnlyCell>{row.original.name}</DataGridReadOnlyCell>
|
<DataGrid.ReadonlyCell>{row.original.name}</DataGrid.ReadonlyCell>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
disableHiding: true,
|
disableHiding: true,
|
||||||
@@ -63,7 +58,7 @@ const useColumns = () => {
|
|||||||
header: t("fields.inStock"),
|
header: t("fields.inStock"),
|
||||||
cell: (context) => {
|
cell: (context) => {
|
||||||
return (
|
return (
|
||||||
<DataGridNumberCell
|
<DataGrid.NumberCell
|
||||||
min={0}
|
min={0}
|
||||||
placeholder="0"
|
placeholder="0"
|
||||||
context={context}
|
context={context}
|
||||||
@@ -72,7 +67,6 @@ const useColumns = () => {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
disableHiding: true,
|
disableHiding: true,
|
||||||
type: "number",
|
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
[t]
|
[t]
|
||||||
|
|||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
import { HttpTypes } from "@medusajs/types"
|
import { HttpTypes } from "@medusajs/types"
|
||||||
import { useMemo } from "react"
|
import { useMemo } from "react"
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
import { getPriceColumns } from "../../../../components/data-grid/data-grid-columns/price-columns"
|
import { createDataGridPriceColumns } from "../../../../components/data-grid/data-grid-column-helpers/create-data-grid-price-columns"
|
||||||
|
|
||||||
export const useShippingOptionPriceColumns = ({
|
export const useShippingOptionPriceColumns = ({
|
||||||
currencies = [],
|
currencies = [],
|
||||||
@@ -15,7 +15,7 @@ export const useShippingOptionPriceColumns = ({
|
|||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
return getPriceColumns({
|
return createDataGridPriceColumns({
|
||||||
currencies,
|
currencies,
|
||||||
regions,
|
regions,
|
||||||
pricePreferences,
|
pricePreferences,
|
||||||
|
|||||||
+4
-3
@@ -1,7 +1,7 @@
|
|||||||
import { useMemo } from "react"
|
import { useMemo } from "react"
|
||||||
import { UseFormReturn } from "react-hook-form"
|
import { UseFormReturn } from "react-hook-form"
|
||||||
|
|
||||||
import { DataGridRoot } from "../../../../../components/data-grid/data-grid-root"
|
import { DataGrid } from "../../../../../components/data-grid"
|
||||||
import { useRouteModal } from "../../../../../components/modals"
|
import { useRouteModal } from "../../../../../components/modals"
|
||||||
import { usePricePreferences } from "../../../../../hooks/api/price-preferences"
|
import { usePricePreferences } from "../../../../../hooks/api/price-preferences"
|
||||||
import { useRegions } from "../../../../../hooks/api/regions"
|
import { useRegions } from "../../../../../hooks/api/regions"
|
||||||
@@ -48,7 +48,7 @@ export const CreateShippingOptionsPricesForm = ({
|
|||||||
pricePreferences,
|
pricePreferences,
|
||||||
})
|
})
|
||||||
|
|
||||||
const initializing = isStoreLoading || !store || isRegionsLoading || !regions
|
const isLoading = isStoreLoading || !store || isRegionsLoading || !regions
|
||||||
|
|
||||||
const data = useMemo(
|
const data = useMemo(
|
||||||
() => [[...(currencies || []), ...(regions || [])]],
|
() => [[...(currencies || []), ...(regions || [])]],
|
||||||
@@ -65,7 +65,8 @@ export const CreateShippingOptionsPricesForm = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex size-full flex-col divide-y overflow-hidden">
|
<div className="flex size-full flex-col divide-y overflow-hidden">
|
||||||
<DataGridRoot
|
<DataGrid
|
||||||
|
isLoading={isLoading}
|
||||||
data={data}
|
data={data}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
state={form}
|
state={form}
|
||||||
|
|||||||
+19
-19
@@ -7,7 +7,7 @@ import { HttpTypes } from "@medusajs/types"
|
|||||||
import { Button, toast } from "@medusajs/ui"
|
import { Button, toast } from "@medusajs/ui"
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
|
|
||||||
import { DataGridRoot } from "../../../../../components/data-grid/data-grid-root"
|
import { DataGrid } from "../../../../../components/data-grid"
|
||||||
import {
|
import {
|
||||||
RouteFocusModal,
|
RouteFocusModal,
|
||||||
useRouteModal,
|
useRouteModal,
|
||||||
@@ -83,9 +83,7 @@ export function EditShippingOptionsPricingForm({
|
|||||||
resolver: zodResolver(EditShippingOptionPricingSchema),
|
resolver: zodResolver(EditShippingOptionPricingSchema),
|
||||||
})
|
})
|
||||||
|
|
||||||
const { mutateAsync, isPending: isLoading } = useUpdateShippingOptions(
|
const { mutateAsync, isPending } = useUpdateShippingOptions(shippingOption.id)
|
||||||
shippingOption.id
|
|
||||||
)
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
store,
|
store,
|
||||||
@@ -198,7 +196,7 @@ export function EditShippingOptionsPricingForm({
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
const initializing =
|
const isLoading =
|
||||||
isStoreLoading || isRegionsLoading || !currencies || !regions
|
isStoreLoading || isRegionsLoading || !currencies || !regions
|
||||||
|
|
||||||
if (isStoreError) {
|
if (isStoreError) {
|
||||||
@@ -215,7 +213,20 @@ export function EditShippingOptionsPricingForm({
|
|||||||
className="flex h-full flex-col overflow-hidden"
|
className="flex h-full flex-col overflow-hidden"
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
>
|
>
|
||||||
<RouteFocusModal.Header>
|
<RouteFocusModal.Header />
|
||||||
|
|
||||||
|
<RouteFocusModal.Body>
|
||||||
|
<div className="flex size-full flex-col divide-y overflow-hidden">
|
||||||
|
<DataGrid
|
||||||
|
isLoading={isLoading}
|
||||||
|
data={data}
|
||||||
|
columns={columns}
|
||||||
|
state={form}
|
||||||
|
onEditingChange={(editing) => setCloseOnEscape(!editing)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</RouteFocusModal.Body>
|
||||||
|
<RouteFocusModal.Footer>
|
||||||
<div className="flex items-center justify-end gap-x-2">
|
<div className="flex items-center justify-end gap-x-2">
|
||||||
<RouteFocusModal.Close asChild>
|
<RouteFocusModal.Close asChild>
|
||||||
<Button variant="secondary" size="small">
|
<Button variant="secondary" size="small">
|
||||||
@@ -225,25 +236,14 @@ export function EditShippingOptionsPricingForm({
|
|||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
className="whitespace-nowrap"
|
className="whitespace-nowrap"
|
||||||
isLoading={isLoading}
|
isLoading={isPending}
|
||||||
onClick={handleSubmit}
|
onClick={handleSubmit}
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
{t("actions.save")}
|
{t("actions.save")}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</RouteFocusModal.Header>
|
</RouteFocusModal.Footer>
|
||||||
|
|
||||||
<RouteFocusModal.Body>
|
|
||||||
<div className="flex size-full flex-col divide-y overflow-hidden">
|
|
||||||
<DataGridRoot
|
|
||||||
data={data}
|
|
||||||
columns={columns}
|
|
||||||
state={form}
|
|
||||||
onEditingChange={(editing) => setCloseOnEscape(!editing)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</RouteFocusModal.Body>
|
|
||||||
</form>
|
</form>
|
||||||
</RouteFocusModal.Form>
|
</RouteFocusModal.Form>
|
||||||
)
|
)
|
||||||
|
|||||||
+7
-7
@@ -4,8 +4,8 @@ import { useMemo } from "react"
|
|||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
|
|
||||||
import { Thumbnail } from "../../../../components/common/thumbnail"
|
import { Thumbnail } from "../../../../components/common/thumbnail"
|
||||||
import { DataGridReadOnlyCell } from "../../../../components/data-grid/data-grid-cells/data-grid-readonly-cell"
|
import { DataGrid } from "../../../../components/data-grid"
|
||||||
import { getPriceColumns } from "../../../../components/data-grid/data-grid-columns/price-columns"
|
import { createDataGridPriceColumns } from "../../../../components/data-grid/data-grid-column-helpers/create-data-grid-price-columns"
|
||||||
import { createDataGridHelper } from "../../../../components/data-grid/utils"
|
import { createDataGridHelper } from "../../../../components/data-grid/utils"
|
||||||
import { isProductRow } from "../utils"
|
import { isProductRow } from "../utils"
|
||||||
|
|
||||||
@@ -35,26 +35,26 @@ export const usePriceListGridColumns = ({
|
|||||||
const entity = row.original
|
const entity = row.original
|
||||||
if (isProductRow(entity)) {
|
if (isProductRow(entity)) {
|
||||||
return (
|
return (
|
||||||
<DataGridReadOnlyCell>
|
<DataGrid.ReadonlyCell>
|
||||||
<div className="flex h-full w-full items-center gap-x-2 overflow-hidden">
|
<div className="flex h-full w-full items-center gap-x-2 overflow-hidden">
|
||||||
<Thumbnail src={entity.thumbnail} />
|
<Thumbnail src={entity.thumbnail} />
|
||||||
<span className="truncate">{entity.title}</span>
|
<span className="truncate">{entity.title}</span>
|
||||||
</div>
|
</div>
|
||||||
</DataGridReadOnlyCell>
|
</DataGrid.ReadonlyCell>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DataGridReadOnlyCell>
|
<DataGrid.ReadonlyCell>
|
||||||
<div className="flex h-full w-full items-center gap-x-2 overflow-hidden">
|
<div className="flex h-full w-full items-center gap-x-2 overflow-hidden">
|
||||||
<span className="truncate">{entity.title}</span>
|
<span className="truncate">{entity.title}</span>
|
||||||
</div>
|
</div>
|
||||||
</DataGridReadOnlyCell>
|
</DataGrid.ReadonlyCell>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
disableHiding: true,
|
disableHiding: true,
|
||||||
}),
|
}),
|
||||||
...getPriceColumns<
|
...createDataGridPriceColumns<
|
||||||
HttpTypes.AdminProduct | HttpTypes.AdminProductVariant
|
HttpTypes.AdminProduct | HttpTypes.AdminProductVariant
|
||||||
>({
|
>({
|
||||||
currencies: currencies.map((c) => c.currency_code),
|
currencies: currencies.map((c) => c.currency_code),
|
||||||
|
|||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
import { HttpTypes } from "@medusajs/types"
|
import { HttpTypes } from "@medusajs/types"
|
||||||
import { useEffect } from "react"
|
import { useEffect } from "react"
|
||||||
import { UseFormReturn, useWatch } from "react-hook-form"
|
import { UseFormReturn, useWatch } from "react-hook-form"
|
||||||
import { DataGridRoot } from "../../../../../components/data-grid/data-grid-root"
|
import { DataGrid } from "../../../../../components/data-grid"
|
||||||
import { useRouteModal } from "../../../../../components/modals"
|
import { useRouteModal } from "../../../../../components/modals"
|
||||||
import { useProducts } from "../../../../../hooks/api/products"
|
import { useProducts } from "../../../../../hooks/api/products"
|
||||||
import { usePriceListGridColumns } from "../../../common/hooks/use-price-list-grid-columns"
|
import { usePriceListGridColumns } from "../../../common/hooks/use-price-list-grid-columns"
|
||||||
@@ -77,7 +77,7 @@ export const PriceListPricesForm = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex size-full flex-col divide-y overflow-hidden">
|
<div className="flex size-full flex-col divide-y overflow-hidden">
|
||||||
<DataGridRoot
|
<DataGrid
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={products}
|
data={products}
|
||||||
getSubRows={(row) => {
|
getSubRows={(row) => {
|
||||||
|
|||||||
+2
-2
@@ -2,7 +2,7 @@ import { HttpTypes } from "@medusajs/types"
|
|||||||
import { useEffect } from "react"
|
import { useEffect } from "react"
|
||||||
import { UseFormReturn, useWatch } from "react-hook-form"
|
import { UseFormReturn, useWatch } from "react-hook-form"
|
||||||
|
|
||||||
import { DataGridRoot } from "../../../../../components/data-grid/data-grid-root"
|
import { DataGrid } from "../../../../../components/data-grid"
|
||||||
import { useRouteModal } from "../../../../../components/modals"
|
import { useRouteModal } from "../../../../../components/modals"
|
||||||
import { useProducts } from "../../../../../hooks/api/products"
|
import { useProducts } from "../../../../../hooks/api/products"
|
||||||
import { usePriceListGridColumns } from "../../../common/hooks/use-price-list-grid-columns"
|
import { usePriceListGridColumns } from "../../../common/hooks/use-price-list-grid-columns"
|
||||||
@@ -78,7 +78,7 @@ export const PriceListPricesAddPricesForm = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex size-full flex-col divide-y overflow-hidden">
|
<div className="flex size-full flex-col divide-y overflow-hidden">
|
||||||
<DataGridRoot
|
<DataGrid
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={products}
|
data={products}
|
||||||
getSubRows={(row) => {
|
getSubRows={(row) => {
|
||||||
|
|||||||
+2
-2
@@ -6,7 +6,7 @@ import { useForm } from "react-hook-form"
|
|||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
|
|
||||||
import { DataGridRoot } from "../../../../../components/data-grid/data-grid-root"
|
import { DataGrid } from "../../../../../components/data-grid"
|
||||||
import {
|
import {
|
||||||
RouteFocusModal,
|
RouteFocusModal,
|
||||||
useRouteModal,
|
useRouteModal,
|
||||||
@@ -92,7 +92,7 @@ export const PriceListPricesEditForm = ({
|
|||||||
<form onSubmit={handleSubmit} className="flex size-full flex-col">
|
<form onSubmit={handleSubmit} className="flex size-full flex-col">
|
||||||
<RouteFocusModal.Header />
|
<RouteFocusModal.Header />
|
||||||
<RouteFocusModal.Body className="flex flex-col overflow-hidden">
|
<RouteFocusModal.Body className="flex flex-col overflow-hidden">
|
||||||
<DataGridRoot
|
<DataGrid
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={products}
|
data={products}
|
||||||
getSubRows={(row) => {
|
getSubRows={(row) => {
|
||||||
|
|||||||
@@ -3,15 +3,16 @@ import { useMemo } from "react"
|
|||||||
import { UseFormReturn, useWatch } from "react-hook-form"
|
import { UseFormReturn, useWatch } from "react-hook-form"
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
|
|
||||||
import { getPriceColumns } from "../../../components/data-grid/data-grid-columns/price-columns"
|
import {
|
||||||
import { DataGridRoot } from "../../../components/data-grid/data-grid-root/data-grid-root"
|
DataGrid,
|
||||||
import { createDataGridHelper } from "../../../components/data-grid/utils.ts"
|
createDataGridHelper,
|
||||||
import { ReadonlyCell } from "../../../components/grid/grid-cells/common/readonly-cell"
|
createDataGridPriceColumns,
|
||||||
import { useRouteModal } from "../../../components/modals/index.ts"
|
} from "../../../components/data-grid"
|
||||||
|
import { useRouteModal } from "../../../components/modals/index"
|
||||||
import { usePricePreferences } from "../../../hooks/api/price-preferences"
|
import { usePricePreferences } from "../../../hooks/api/price-preferences"
|
||||||
import { useRegions } from "../../../hooks/api/regions.tsx"
|
import { useRegions } from "../../../hooks/api/regions.tsx"
|
||||||
import { useStore } from "../../../hooks/api/store"
|
import { useStore } from "../../../hooks/api/store"
|
||||||
import { ProductCreateSchemaType } from "../product-create/types.ts"
|
import { ProductCreateSchemaType } from "../product-create/types"
|
||||||
|
|
||||||
type VariantPricingFormProps = {
|
type VariantPricingFormProps = {
|
||||||
form: UseFormReturn<ProductCreateSchemaType>
|
form: UseFormReturn<ProductCreateSchemaType>
|
||||||
@@ -36,7 +37,7 @@ export const VariantPricingForm = ({ form }: VariantPricingFormProps) => {
|
|||||||
}) as any
|
}) as any
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DataGridRoot
|
<DataGrid
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={variants}
|
data={variants}
|
||||||
state={form}
|
state={form}
|
||||||
@@ -66,16 +67,16 @@ const useVariantPriceGridColumns = ({
|
|||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const entity = row.original
|
const entity = row.original
|
||||||
return (
|
return (
|
||||||
<ReadonlyCell>
|
<DataGrid.ReadonlyCell>
|
||||||
<div className="flex h-full w-full items-center gap-x-2 overflow-hidden">
|
<div className="flex h-full w-full items-center gap-x-2 overflow-hidden">
|
||||||
<span className="truncate">{entity.title}</span>
|
<span className="truncate">{entity.title}</span>
|
||||||
</div>
|
</div>
|
||||||
</ReadonlyCell>
|
</DataGrid.ReadonlyCell>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
disableHiding: true,
|
disableHiding: true,
|
||||||
}),
|
}),
|
||||||
...getPriceColumns<HttpTypes.AdminProductVariant>({
|
...createDataGridPriceColumns<HttpTypes.AdminProductVariant>({
|
||||||
currencies: currencies.map((c) => c.currency_code),
|
currencies: currencies.map((c) => c.currency_code),
|
||||||
regions,
|
regions,
|
||||||
pricePreferences,
|
pricePreferences,
|
||||||
|
|||||||
+17
-26
@@ -3,11 +3,8 @@ import { useMemo } from "react"
|
|||||||
import { UseFormReturn, useWatch } from "react-hook-form"
|
import { UseFormReturn, useWatch } from "react-hook-form"
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
|
|
||||||
import { DataGridBooleanCell } from "../../../../../components/data-grid/data-grid-cells/data-grid-boolean-cell"
|
import { DataGrid } from "../../../../../components/data-grid"
|
||||||
import { DataGridReadOnlyCell } from "../../../../../components/data-grid/data-grid-cells/data-grid-readonly-cell"
|
import { createDataGridPriceColumns } from "../../../../../components/data-grid/data-grid-column-helpers/create-data-grid-price-columns"
|
||||||
import { DataGridTextCell } from "../../../../../components/data-grid/data-grid-cells/data-grid-text-cell"
|
|
||||||
import { getPriceColumns } from "../../../../../components/data-grid/data-grid-columns/price-columns"
|
|
||||||
import { DataGridRoot } from "../../../../../components/data-grid/data-grid-root"
|
|
||||||
import { createDataGridHelper } from "../../../../../components/data-grid/utils"
|
import { createDataGridHelper } from "../../../../../components/data-grid/utils"
|
||||||
import { useRouteModal } from "../../../../../components/modals"
|
import { useRouteModal } from "../../../../../components/modals"
|
||||||
import { usePricePreferences } from "../../../../../hooks/api/price-preferences"
|
import { usePricePreferences } from "../../../../../hooks/api/price-preferences"
|
||||||
@@ -103,16 +100,13 @@ export const ProductCreateVariantsForm = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex size-full flex-col divide-y overflow-hidden">
|
<div className="flex size-full flex-col divide-y overflow-hidden">
|
||||||
{isPending ? (
|
<DataGrid
|
||||||
<div>Loading...</div>
|
isLoading={isPending}
|
||||||
) : (
|
columns={columns}
|
||||||
<DataGridRoot
|
data={variantData}
|
||||||
columns={columns}
|
state={form}
|
||||||
data={variantData}
|
onEditingChange={(editing) => setCloseOnEscape(!editing)}
|
||||||
state={form}
|
/>
|
||||||
onEditingChange={(editing) => setCloseOnEscape(!editing)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -145,9 +139,9 @@ const useColumns = ({
|
|||||||
),
|
),
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
return (
|
return (
|
||||||
<DataGridReadOnlyCell>
|
<DataGrid.ReadonlyCell>
|
||||||
{options.map((o) => row.original.options[o.title]).join(" / ")}
|
{options.map((o) => row.original.options[o.title]).join(" / ")}
|
||||||
</DataGridReadOnlyCell>
|
</DataGrid.ReadonlyCell>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
disableHiding: true,
|
disableHiding: true,
|
||||||
@@ -158,7 +152,7 @@ const useColumns = ({
|
|||||||
header: t("fields.title"),
|
header: t("fields.title"),
|
||||||
cell: (context) => {
|
cell: (context) => {
|
||||||
return (
|
return (
|
||||||
<DataGridTextCell
|
<DataGrid.TextCell
|
||||||
context={context}
|
context={context}
|
||||||
field={`variants.${context.row.index}.title`}
|
field={`variants.${context.row.index}.title`}
|
||||||
/>
|
/>
|
||||||
@@ -171,7 +165,7 @@ const useColumns = ({
|
|||||||
header: t("fields.sku"),
|
header: t("fields.sku"),
|
||||||
cell: (context) => {
|
cell: (context) => {
|
||||||
return (
|
return (
|
||||||
<DataGridTextCell
|
<DataGrid.TextCell
|
||||||
context={context}
|
context={context}
|
||||||
field={`variants.${context.row.index}.sku`}
|
field={`variants.${context.row.index}.sku`}
|
||||||
/>
|
/>
|
||||||
@@ -184,13 +178,12 @@ const useColumns = ({
|
|||||||
header: t("fields.managedInventory"),
|
header: t("fields.managedInventory"),
|
||||||
cell: (context) => {
|
cell: (context) => {
|
||||||
return (
|
return (
|
||||||
<DataGridBooleanCell
|
<DataGrid.BooleanCell
|
||||||
context={context}
|
context={context}
|
||||||
field={`variants.${context.row.index}.manage_inventory`}
|
field={`variants.${context.row.index}.manage_inventory`}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
type: "boolean",
|
|
||||||
}),
|
}),
|
||||||
columnHelper.column({
|
columnHelper.column({
|
||||||
id: "allow_backorder",
|
id: "allow_backorder",
|
||||||
@@ -198,13 +191,12 @@ const useColumns = ({
|
|||||||
header: t("fields.allowBackorder"),
|
header: t("fields.allowBackorder"),
|
||||||
cell: (context) => {
|
cell: (context) => {
|
||||||
return (
|
return (
|
||||||
<DataGridBooleanCell
|
<DataGrid.BooleanCell
|
||||||
context={context}
|
context={context}
|
||||||
field={`variants.${context.row.index}.allow_backorder`}
|
field={`variants.${context.row.index}.allow_backorder`}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
type: "boolean",
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
columnHelper.column({
|
columnHelper.column({
|
||||||
@@ -213,17 +205,16 @@ const useColumns = ({
|
|||||||
header: t("fields.inventoryKit"),
|
header: t("fields.inventoryKit"),
|
||||||
cell: (context) => {
|
cell: (context) => {
|
||||||
return (
|
return (
|
||||||
<DataGridBooleanCell
|
<DataGrid.BooleanCell
|
||||||
context={context}
|
context={context}
|
||||||
field={`variants.${context.row.index}.inventory_kit`}
|
field={`variants.${context.row.index}.inventory_kit`}
|
||||||
disabled={!context.row.original.manage_inventory}
|
disabled={!context.row.original.manage_inventory}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
type: "boolean",
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
...getPriceColumns<ProductCreateVariantSchema>({
|
...createDataGridPriceColumns<ProductCreateVariantSchema>({
|
||||||
currencies,
|
currencies,
|
||||||
regions,
|
regions,
|
||||||
pricePreferences,
|
pricePreferences,
|
||||||
|
|||||||
@@ -106,8 +106,7 @@
|
|||||||
"react-stately": "^3.31.1",
|
"react-stately": "^3.31.1",
|
||||||
"sonner": "^1.5.0",
|
"sonner": "^1.5.0",
|
||||||
"tailwind-merge": "^2.2.1",
|
"tailwind-merge": "^2.2.1",
|
||||||
"upgrade": "^1.1.0",
|
"upgrade": "^1.1.0"
|
||||||
"yarn": "^1.22.22"
|
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "^18.0.0",
|
"react": "^18.0.0",
|
||||||
|
|||||||
@@ -5372,7 +5372,6 @@ __metadata:
|
|||||||
vite: ^4.3.9
|
vite: ^4.3.9
|
||||||
vite-plugin-turbosnap: ^1.0.2
|
vite-plugin-turbosnap: ^1.0.2
|
||||||
vitest: ^0.32.2
|
vitest: ^0.32.2
|
||||||
yarn: ^1.22.22
|
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ^18.0.0
|
react: ^18.0.0
|
||||||
react-dom: ^18.0.0
|
react-dom: ^18.0.0
|
||||||
@@ -31549,16 +31548,6 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"yarn@npm:^1.22.22":
|
|
||||||
version: 1.22.22
|
|
||||||
resolution: "yarn@npm:1.22.22"
|
|
||||||
bin:
|
|
||||||
yarn: bin/yarn.js
|
|
||||||
yarnpkg: bin/yarn.js
|
|
||||||
checksum: 8c77198c93d7542e7f4e131c63b66de357b7076ecfbcfe709ec0d674115c2dd9edaa45196e5510e6e9366d368707a802579e3402071002e1c9d9a99d491478de
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"yauzl@npm:^2.10.0":
|
"yauzl@npm:^2.10.0":
|
||||||
version: 2.10.0
|
version: 2.10.0
|
||||||
resolution: "yauzl@npm:2.10.0"
|
resolution: "yauzl@npm:2.10.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user