feat(dashboard): Hitting escape restores previous value (#8654)
* feat(dashboard): Hitting escape restores previous value * update lock
This commit is contained in:
committed by
GitHub
parent
a77c23c915
commit
894db4a150
@@ -1,10 +1,10 @@
|
||||
import { PropsWithChildren } from "react"
|
||||
|
||||
type DataGridReadOnlyCellProps = PropsWithChildren
|
||||
type DataGridReadonlyCellProps = PropsWithChildren
|
||||
|
||||
export const DataGridReadOnlyCell = ({
|
||||
export const DataGridReadonlyCell = ({
|
||||
children,
|
||||
}: DataGridReadOnlyCellProps) => {
|
||||
}: DataGridReadonlyCellProps) => {
|
||||
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">
|
||||
<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"
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { HttpTypes } from "@medusajs/types"
|
||||
import { CellContext, ColumnDef } from "@tanstack/react-table"
|
||||
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 { DataGridReadOnlyCell } from "../data-grid-cells/data-grid-readonly-cell"
|
||||
import { DataGridReadonlyCell } from "../data-grid-cells/data-grid-readonly-cell"
|
||||
import { createDataGridHelper } from "../utils"
|
||||
|
||||
export const getPriceColumns = <TData,>({
|
||||
export const createDataGridPriceColumns = <TData,>({
|
||||
currencies,
|
||||
regions,
|
||||
pricePreferences,
|
||||
@@ -44,7 +44,7 @@ export const getPriceColumns = <TData,>({
|
||||
),
|
||||
cell: (context) => {
|
||||
if (isReadyOnly?.(context)) {
|
||||
return <DataGridReadOnlyCell />
|
||||
return <DataGridReadonlyCell />
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -55,7 +55,6 @@ export const getPriceColumns = <TData,>({
|
||||
/>
|
||||
)
|
||||
},
|
||||
type: "string",
|
||||
})
|
||||
}) ?? []),
|
||||
...(regions?.map((region) => {
|
||||
@@ -78,7 +77,7 @@ export const getPriceColumns = <TData,>({
|
||||
),
|
||||
cell: (context) => {
|
||||
if (isReadyOnly?.(context)) {
|
||||
return <DataGridReadOnlyCell />
|
||||
return <DataGridReadonlyCell />
|
||||
}
|
||||
|
||||
const currency = currencies?.find((c) => c === region.currency_code)
|
||||
@@ -94,7 +93,6 @@ export const getPriceColumns = <TData,>({
|
||||
/>
|
||||
)
|
||||
},
|
||||
type: "string",
|
||||
})
|
||||
}) ?? []),
|
||||
]
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./create-data-grid-price-columns"
|
||||
@@ -29,19 +29,19 @@ import {
|
||||
} from "react"
|
||||
import { FieldValues, Path, PathValue, UseFormReturn } from "react-hook-form"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { useCommandHistory } from "../../../hooks/use-command-history"
|
||||
import { DataGridContext } from "../context"
|
||||
import { useGridQueryTool } from "../hooks"
|
||||
import { BulkUpdateCommand, Matrix, UpdateCommand } from "../models"
|
||||
import { CellCoords, CellType } from "../types"
|
||||
import { useCommandHistory } from "../../hooks/use-command-history"
|
||||
import { DataGridContext } from "./context"
|
||||
import { useGridQueryTool } from "./hooks"
|
||||
import { BulkUpdateCommand, Matrix, UpdateCommand } from "./models"
|
||||
import { CellCoords, CellSnapshot, CellType } from "./types"
|
||||
import {
|
||||
convertArrayToPrimitive,
|
||||
generateCellId,
|
||||
getColumnName,
|
||||
isCellMatch,
|
||||
} from "../utils"
|
||||
} from "./utils"
|
||||
|
||||
interface DataGridRootProps<
|
||||
export interface DataGridRootProps<
|
||||
TData,
|
||||
TFieldValues extends FieldValues = FieldValues
|
||||
> {
|
||||
@@ -91,17 +91,8 @@ export const DataGridRoot = <
|
||||
const [isDragging, setIsDragging] = useState(false)
|
||||
|
||||
const [isEditing, setIsEditing] = useState(false)
|
||||
|
||||
const onEditingChangeHandler = useCallback(
|
||||
(value: boolean) => {
|
||||
if (onEditingChange) {
|
||||
onEditingChange(value)
|
||||
}
|
||||
|
||||
setIsEditing(value)
|
||||
},
|
||||
[onEditingChange]
|
||||
)
|
||||
const [cellValueSnapshot, setCellValueSnapshot] =
|
||||
useState<CellSnapshot<TFieldValues> | null>(null)
|
||||
|
||||
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({})
|
||||
|
||||
@@ -233,6 +224,53 @@ export const DataGridRoot = <
|
||||
|
||||
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(
|
||||
(coords: CellCoords, field: string, type: CellType) => {
|
||||
matrix.registerField(coords.row, coords.col, field, type)
|
||||
@@ -694,11 +732,14 @@ export const DataGridRoot = <
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
|
||||
// try to restore the previous value
|
||||
restoreSnapshot()
|
||||
|
||||
// Restore focus to the container element
|
||||
const container = queryTool?.getContainer(anchor)
|
||||
container?.focus()
|
||||
},
|
||||
[queryTool, isEditing, anchor]
|
||||
[queryTool, isEditing, anchor, restoreSnapshot]
|
||||
)
|
||||
|
||||
const handleTabKey = useCallback(
|
||||
@@ -1 +0,0 @@
|
||||
export * from "./data-grid-root"
|
||||
@@ -0,0 +1,52 @@
|
||||
import { Table } from "@medusajs/ui"
|
||||
import { ColumnDef } from "@tanstack/react-table"
|
||||
import { Skeleton } from "../common/skeleton"
|
||||
|
||||
type DataGridSkeletonProps<TData> = {
|
||||
columns: ColumnDef<TData>[]
|
||||
rows?: number
|
||||
}
|
||||
|
||||
export const DataGridSkeleton = <TData,>({
|
||||
columns,
|
||||
rows: rowCount = 10,
|
||||
}: DataGridSkeletonProps<TData>) => {
|
||||
const rows = Array.from({ length: rowCount }, (_, i) => i)
|
||||
|
||||
const colCount = columns.length
|
||||
const colWidth = 100 / colCount
|
||||
|
||||
return (
|
||||
<Table>
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
{columns.map((_col, i) => {
|
||||
return (
|
||||
<Table.HeaderCell
|
||||
key={i}
|
||||
style={{
|
||||
width: `${colWidth}%`,
|
||||
}}
|
||||
>
|
||||
<Skeleton className="h-7" />
|
||||
</Table.HeaderCell>
|
||||
)
|
||||
})}
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
{rows.map((_, j) => (
|
||||
<Table.Row key={j}>
|
||||
{columns.map((_col, k) => {
|
||||
return (
|
||||
<Table.Cell key={k}>
|
||||
<Skeleton className="h-7" />
|
||||
</Table.Cell>
|
||||
)
|
||||
})}
|
||||
</Table.Row>
|
||||
))}
|
||||
</Table.Body>
|
||||
</Table>
|
||||
)
|
||||
}
|
||||
@@ -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 React, { PropsWithChildren, ReactNode, RefObject } from "react"
|
||||
import { FieldValues, Path, PathValue } from "react-hook-form"
|
||||
|
||||
export type CellType = "text" | "number" | "select" | "boolean"
|
||||
|
||||
@@ -70,3 +71,8 @@ export interface DataGridCellContainerProps extends PropsWithChildren<{}> {
|
||||
}
|
||||
|
||||
export type DataGridColumnType = "string" | "number" | "boolean"
|
||||
|
||||
export type CellSnapshot<TFieldValues extends FieldValues = FieldValues> = {
|
||||
field: string
|
||||
value: PathValue<TFieldValues, Path<TFieldValues>>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user