From 773633d1033a62003a5e48bb2b6f0215acff1f4d Mon Sep 17 00:00:00 2001 From: andrzej Date: Sun, 30 Jun 2024 23:22:46 +0200 Subject: [PATCH] implement inline number input --- prisma/dev.db | Bin 69632 -> 69632 bytes src/app/lib/nameMaps.ts | 8 +++ src/app/lib/update.ts | 2 +- src/app/publication/columns.tsx | 9 ++- src/app/story/[id]/page.tsx | 2 +- src/app/story/columns.tsx | 10 ++- src/app/submission/columns.tsx | 2 + src/app/tailwind.css | 16 ----- src/app/ui/inputs/textInput.tsx | 65 -------------------- src/app/ui/tables/contextMenu.tsx | 3 +- src/app/ui/tables/data-table.tsx | 7 ++- src/app/ui/tables/inputs/inputContainer.tsx | 20 ++++++ src/app/ui/tables/inputs/numberInput.tsx | 53 ++++++++++++++++ src/app/ui/tables/inputs/textInput.tsx | 54 ++++++++++++++++ 14 files changed, 160 insertions(+), 91 deletions(-) create mode 100644 src/app/lib/nameMaps.ts delete mode 100644 src/app/ui/inputs/textInput.tsx create mode 100644 src/app/ui/tables/inputs/inputContainer.tsx create mode 100644 src/app/ui/tables/inputs/numberInput.tsx create mode 100644 src/app/ui/tables/inputs/textInput.tsx diff --git a/prisma/dev.db b/prisma/dev.db index 4841e923154322f28ed2ce77ab9c4e74c50b0dd0..8b7105c6972b3ca0b9f0fe4fd795a65fa53c0d4f 100644 GIT binary patch delta 370 zcmZozz|ydQWr8&0r-?GojGs0pEb`}?&A`U@mx2EzztU!&fZcrR{7MXFzM71#Ic2HE z3VsUi`AIpMc_3DJW=V1eSS%znFMaa6I1@HyW>yAm&B+Vnr6xPYN3t+5Fz8L5AFrh- z&&0~0uFT|-UsRM|q!6B8l#^1N0p{qHq!yPjOxye_UP3`GjDdsiA`|};zMuT^{8fAx z`5pQB`497N<@*kTn*|Lv^0BGOu`n?CPLAo9lvl{iQwT0dOimSH0*Yz!$bdNtVTri~ znMJ9^lllAIS@`EMs7#LPmtmYYxx8PUga15(GLt&D(&T0R`r?|5KyAT{zuhzPi%S$7 zr9fO$#^00Q^h+=c@K4*!KB0pj;h-3+h7BbEs;%@#KAGP9;^bZC2Ny5b^6Y~r&#K$X%L7y_GYRl;*Rnt^$crBA( zTOfqMAEeP#HaMMC=8TVoQ}M178^G|6WAtDHF-!os!=?Q5H^ULSm4n50!A2-8+KYDD zCeEth#3KagcQltwWt3QQC70DQd6P=QtjM9yFprlJzMyy0M_$nVEu8`*f}ei#Gkv7% z-_J!&H)gt~?i3x+4XLyXX6X() @@ -60,7 +61,11 @@ export const columns: ColumnDef[] = [ { accessorKey: "query_after_days", - header: "Query After (days)" + header: "Query After (days)", + meta: { + step: 10 + }, + cell: NumberInputCell }, diff --git a/src/app/story/[id]/page.tsx b/src/app/story/[id]/page.tsx index e1d53cb..8bee28b 100644 --- a/src/app/story/[id]/page.tsx +++ b/src/app/story/[id]/page.tsx @@ -31,7 +31,7 @@ export default async function Page({ params }: { params: { id: string } }) { {story?.title ?? ""} Submissions: - + diff --git a/src/app/story/columns.tsx b/src/app/story/columns.tsx index 10ffa3f..285b3b6 100644 --- a/src/app/story/columns.tsx +++ b/src/app/story/columns.tsx @@ -5,8 +5,9 @@ import { ArrowUpDown } from "lucide-react" import { Button } from "@/components/ui/button" import GenreBadges from "app/ui/genreBadges" import { actions } from "app/ui/tables/actions" -import { TextInputCell } from "app/ui/inputs/textInput" +import { TextInputCell } from "app/ui/tables/inputs/textInput" import { selectCol } from "app/ui/tables/selectColumn" +import NumberInputCell from "app/ui/tables/inputs/numberInput" const columnHelper = createColumnHelper() @@ -42,7 +43,11 @@ export const columns: ColumnDef[] = [ ) }, - enableColumnFilter: false + enableColumnFilter: false, + cell: NumberInputCell, + meta: { + step: 50 + } }, columnHelper.accessor("genres", { cell: props => { @@ -52,5 +57,6 @@ export const columns: ColumnDef[] = [ filterFn: "arrIncludes" //TODO - write custom filter function, to account for an array of objects }), + ] diff --git a/src/app/submission/columns.tsx b/src/app/submission/columns.tsx index c8cd503..112282c 100644 --- a/src/app/submission/columns.tsx +++ b/src/app/submission/columns.tsx @@ -4,11 +4,13 @@ import { ArrowUpDown } from "lucide-react" import { Button } from "@/components/ui/button" import { SubComplete } from "./page" import { actions } from "app/ui/tables/actions" +import { selectCol } from "app/ui/tables/selectColumn" const columnHelper = createColumnHelper() export const columns: ColumnDef[] = [ + selectCol, { accessorFn: row => new Date(row.submitted), id: "submitted", diff --git a/src/app/tailwind.css b/src/app/tailwind.css index 67f9733..c0fc71c 100644 --- a/src/app/tailwind.css +++ b/src/app/tailwind.css @@ -870,14 +870,6 @@ body { height: 100vh; } -.h-full { - height: 100%; -} - -.h-1 { - height: 0.25rem; -} - .max-h-96 { max-height: 24rem; } @@ -1104,10 +1096,6 @@ body { justify-content: space-around; } -.justify-items-center { - justify-items: center; -} - .gap-1 { gap: 0.25rem; } @@ -1439,10 +1427,6 @@ body { padding-top: 0.25rem; } -.pt-2 { - padding-top: 0.5rem; -} - .text-left { text-align: left; } diff --git a/src/app/ui/inputs/textInput.tsx b/src/app/ui/inputs/textInput.tsx deleted file mode 100644 index 9382c7e..0000000 --- a/src/app/ui/inputs/textInput.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { Cell, CellContext, Table } from "@tanstack/react-table" -import { useState, useEffect } from "react" -import { Input } from "@/components/ui/input" -import { Button } from "@/components/ui/button" -import { Check, CircleX } from "lucide-react" -import { updateTextField } from "app/lib/update" - -export const TextInputCell = (props: CellContext) => { - let initialValue = props.getValue() - const [value, setValue] = useState(initialValue) - const [isActive, setIsActive] = useState(false) - - const table = props.table.options.meta.tableName - const id = props.row.original.id - const column = props.column.id - const pathname = props.table.options.meta.pathname - - function handleConfirm() { - updateTextField({ - id, - table, - text: value, - column, - pathname - }) - initialValue = value - handleClose() - } - function handleOpen() { - setIsActive(true) - } - function handleClose() { - setValue(initialValue) - setIsActive(false) - } - function handleKeyDown(event: React.KeyboardEvent) { - if (event.code === "Enter") { - handleConfirm() - } - } - - - return (
setIsActive(prev => !prev)} - className="w-full h-fit flex items-center justify-center" - tabIndex={0} - onKeyDown={e => { - if (e.code === "Enter" && !isActive) { - setIsActive(true) - } - }} - > - {isActive ? - setValue(e.target.value)} // onBlur={handleClose} - autoFocus={true} - onBlur={handleClose} - onKeyDown={handleKeyDown} - className="w-full" - /> - :

{value}

- } -
) -} diff --git a/src/app/ui/tables/contextMenu.tsx b/src/app/ui/tables/contextMenu.tsx index 6665f91..5faf8fc 100644 --- a/src/app/ui/tables/contextMenu.tsx +++ b/src/app/ui/tables/contextMenu.tsx @@ -7,6 +7,7 @@ import Link from "next/link" import { ComponentProps } from "react" import { Row, Table, TableState } from "@tanstack/react-table" import { letterCase } from "app/lib/functions" +import { tableNameToItemName } from "app/lib/nameMaps" export default function FormContextMenu({ table, row }: ComponentProps<"div"> & { table: Table, row: Row }) { const pathname = table.options.meta.pathname @@ -37,7 +38,7 @@ export default function FormContextMenu({ table, row }: ComponentProps<"div"> & Are you sure? - Deleting a {pathname.slice(1)} cannot be undone! + Deleting a {tableNameToItemName(table.options.meta.tableName)} cannot be undone! diff --git a/src/app/ui/tables/data-table.tsx b/src/app/ui/tables/data-table.tsx index 26097d0..cbdc633 100644 --- a/src/app/ui/tables/data-table.tsx +++ b/src/app/ui/tables/data-table.tsx @@ -44,7 +44,8 @@ import { deleteRecords } from "app/lib/del" import { Pathname } from "app/types" import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTrigger } from "@/components/ui/dialog" import pluralize from "app/lib/pluralize" -import { updateTextField } from "app/lib/update" +import { updateField } from "app/lib/update" +import { tableNameToItemName } from "app/lib/nameMaps" export interface DataTableProps { columns: ColumnDef[] @@ -86,7 +87,7 @@ export function DataTable({ }, //this is where you put arbitrary functions etc to make them accessible via the table api meta: { - updateTextField, + updateTextField: updateField, tableName, pathname } @@ -144,7 +145,7 @@ export function DataTable({ {`Delete ${Object.keys(table.getState().rowSelection).length} ${pluralize(pathname.slice(1))}?`} - {`Deleting ${pluralize(pathname.slice(1))} cannot be undone!`} + {`Deleting ${pluralize(tableNameToItemName(table.options.meta.tableName))} cannot be undone!`} diff --git a/src/app/ui/tables/inputs/inputContainer.tsx b/src/app/ui/tables/inputs/inputContainer.tsx new file mode 100644 index 0000000..f23367d --- /dev/null +++ b/src/app/ui/tables/inputs/inputContainer.tsx @@ -0,0 +1,20 @@ +"use client" + +import { ComponentProps, Dispatch, SetStateAction } from "react" + +export function TableInputContainer({ isActive, setIsActive, children }: ComponentProps<"div"> & { isActive: boolean, setIsActive: Dispatch> }) { + return ( +
setIsActive(prev => !prev)} + className="w-full h-fit flex items-center justify-center" + tabIndex={0} + onKeyDown={e => { + if (e.code === "Enter" && !isActive) { + setIsActive(true) + } + }} + > + {children} +
+ ) +} diff --git a/src/app/ui/tables/inputs/numberInput.tsx b/src/app/ui/tables/inputs/numberInput.tsx new file mode 100644 index 0000000..b1ff43f --- /dev/null +++ b/src/app/ui/tables/inputs/numberInput.tsx @@ -0,0 +1,53 @@ +import { Input } from "@/components/ui/input"; +import { CellContext } from "@tanstack/react-table"; +import { updateField } from "app/lib/update"; +import { useState } from "react"; +import { TableInputContainer } from "./inputContainer"; + + +export default function NumberInputCell(props: CellContext) { + let initialValue = props.getValue() + const [value, setValue] = useState(initialValue) + const [isActive, setIsActive] = useState(false) + + const table = props.table.options.meta.tableName + const id = props.row.original.id + const column = props.column.id + const pathname = props.table.options.meta.pathname + + function handleConfirm() { + if (value === initialValue) { + setIsActive(false) + return + } + updateField({ + id, + table, + number: Number(value), + column, + pathname + }) + initialValue = value + setIsActive(false) + } + function handleExit() { + setValue(initialValue) + setIsActive(false) + } + + return ( + + {isActive ? + setValue(e.target.value)} // onBlur={handleClose} + onKeyDown={e => { if (e.code === "Enter") { handleConfirm() } }} + step={props.column.columnDef.meta.step} + /> + :

{value}

+ } +
+ ) +} diff --git a/src/app/ui/tables/inputs/textInput.tsx b/src/app/ui/tables/inputs/textInput.tsx new file mode 100644 index 0000000..4a33cbf --- /dev/null +++ b/src/app/ui/tables/inputs/textInput.tsx @@ -0,0 +1,54 @@ +import { Cell, CellContext, Table } from "@tanstack/react-table" +import { useState, useEffect } from "react" +import { Input } from "@/components/ui/input" +import { Button } from "@/components/ui/button" +import { Check, CircleX } from "lucide-react" +import { updateField } from "app/lib/update" +import { TableInputContainer } from "./inputContainer" + +export const TextInputCell = (props: CellContext) => { + let initialValue = props.getValue() + const [value, setValue] = useState(initialValue) + const [isActive, setIsActive] = useState(false) + + const table = props.table.options.meta.tableName + const id = props.row.original.id + const column = props.column.id + const pathname = props.table.options.meta.pathname + + function handleConfirm() { + if (value === initialValue) { + setIsActive(false) + return + } + updateField({ + id, + table, + text: value, + column, + pathname + }) + initialValue = value + setIsActive(false) + } + function handleExit() { + setValue(initialValue) + setIsActive(false) + } + + return ( + + {isActive ? + setValue(e.target.value)} // onBlur={handleClose} + autoFocus={true} + onBlur={handleExit} + onKeyDown={e => { if (e.code === "Enter") { handleConfirm() } }} + className="w-full" + /> + :

{value}

+ } +
+ ) +}