feat(dashboard): Hitting escape restores previous value (#8654)

* feat(dashboard): Hitting escape restores previous value

* update lock
This commit is contained in:
Kasper Fabricius Kristensen
2024-08-19 14:02:30 +02:00
committed by GitHub
parent a77c23c915
commit 894db4a150
39 changed files with 203 additions and 973 deletions

View File

@@ -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>

View File

@@ -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"

View File

@@ -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",
})
}) ?? []),
]

View File

@@ -0,0 +1 @@
export * from "./create-data-grid-price-columns"

View File

@@ -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(

View File

@@ -1 +0,0 @@
export * from "./data-grid-root"

View File

@@ -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>
)
}

View File

@@ -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,
})

View File

@@ -0,0 +1,3 @@
export * from "./data-grid"
export * from "./data-grid-column-helpers"
export { createDataGridHelper } from "./utils"

View File

@@ -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>>
}