Implement inline editing and image upload/delete/edit

This commit is contained in:
april
2024-01-15 16:33:26 -06:00
parent 325730a9da
commit 924252b38f
17 changed files with 1213 additions and 50 deletions

View File

@@ -0,0 +1,71 @@
import { CloseButton, NumberInput } from "@mantine/core";
import { Dispatch, SetStateAction } from "react";
function HourInput({
label,
value,
setValue,
}: {
label: string;
value: number | string | null;
setValue: Dispatch<SetStateAction<string | number | null>>;
}) {
return (
<NumberInput
label={label}
decimalScale={1}
step={0.1}
min={0}
fixedDecimalScale
leftSection={
<CloseButton
aria-label="Clear input"
onClick={() => setValue("")}
style={{
display: Number.isFinite(value) ? undefined : undefined,
}}
/>
}
onChange={(value) =>
setValue(Number.isFinite(value) ? (value as number) : 0)
}
/>
);
}
function ZeroHourInput({
label,
value,
setValue,
error = null,
}: {
label: string;
value: number | null;
setValue: Dispatch<SetStateAction<number | null>>;
error?: string | null;
}) {
return (
<NumberInput
label={label}
decimalScale={1}
step={0.1}
min={0}
fixedDecimalScale
error={error}
leftSection={
<CloseButton
aria-label="Clear input"
onClick={() => setValue(0)}
style={{
display: !value || value === 0 ? "none" : undefined,
}}
/>
}
onChange={(value) =>
setValue(Number.isFinite(value) ? (value as number) : 0)
}
/>
);
}
export { HourInput, ZeroHourInput };

View File

@@ -0,0 +1,65 @@
import { CloseButton, NumberInput } from "@mantine/core";
import { Dispatch, SetStateAction } from "react";
function IntInput({
label,
value,
setValue,
}: {
label: string;
value: number | string | undefined;
setValue: Dispatch<SetStateAction<number | string | undefined>>;
}) {
return (
<NumberInput
label={label}
min={0}
allowDecimal={false}
value={value}
leftSection={
<CloseButton
aria-label="Clear input"
onClick={() => setValue("")}
style={{
display: Number.isFinite(value) ? "none" : undefined,
}}
/>
}
onChange={setValue}
/>
);
}
function ZeroIntInput({
label,
value,
setValue,
error = null,
}: {
label: string;
value: number;
setValue: Dispatch<SetStateAction<number>>;
error: string | null;
}) {
return (
<NumberInput
label={label}
min={0}
allowDecimal={false}
error={error}
value={value}
leftSection={
<CloseButton
aria-label="Clear input"
onClick={() => setValue(0)}
style={{
display: !value || value === 0 ? "none" : undefined,
}}
/>
}
onChange={(value) => setValue(Number(value))}
/>
);
}
export { IntInput, ZeroIntInput };

View File

@@ -0,0 +1,54 @@
import { Pill, PillsInput } from "@mantine/core";
import { Dispatch, SetStateAction, useState } from "react";
export default function ListInput({
label,
value,
setValue,
}: {
label: string;
value: string[];
setValue: Dispatch<SetStateAction<string[]>>;
}) {
const [inputValue, setInputValue] = useState<string>("");
const handleKeyDown = (event: React.KeyboardEvent) => {
if (event.key === "Enter" || event.key === ",") {
event.preventDefault();
const newItem = inputValue.trim();
if (newItem && value.indexOf(newItem) == -1) {
setValue([...value, newItem]);
setInputValue("");
}
} else if (event.key === "Backspace") {
const newItem = inputValue.trim();
if (newItem === "") {
setValue(value.slice(0, -1));
}
}
};
return (
<PillsInput label={label} description="Press enter or comma to add item">
<Pill.Group>
{value.map((item: string) => (
<Pill
radius="sm"
key={item}
withRemoveButton
onRemove={() =>
setValue(value.filter((value: string) => value !== item))
}
>
{item}
</Pill>
))}
<PillsInput.Field
value={inputValue}
onChange={(event) => setInputValue(event.currentTarget.value)}
onKeyDown={handleKeyDown}
/>
</Pill.Group>
</PillsInput>
);
}

View File

@@ -0,0 +1,53 @@
import { ActionIcon, CloseButton, NumberInput, Tooltip } from "@mantine/core";
import { IconClock } from "@tabler/icons-react";
import dayjs from "dayjs";
import { Dispatch, SetStateAction } from "react";
export default function TimeInput({
label,
value,
setValue,
allowLeadingZeros = false,
error = "",
}: {
label: string;
value: number | string | undefined;
setValue: Dispatch<SetStateAction<number | string | undefined>>;
allowLeadingZeros?: boolean;
error?: string | null;
}) {
return (
<NumberInput
label={label}
allowDecimal={false}
min={0}
max={2359}
allowLeadingZeros={allowLeadingZeros}
error={error}
value={value}
leftSection={
<CloseButton
aria-label="Clear input"
onClick={() => setValue("")}
style={{
display: Number.isFinite(value) ? "none" : undefined,
}}
/>
}
rightSection={
<Tooltip label="Now">
<ActionIcon
variant="transparent"
mr="sm"
onClick={() => {
setValue(dayjs().format("HHmm"));
}}
>
<IconClock style={{ width: "70%", height: "70%" }} />
</ActionIcon>
</Tooltip>
}
onChange={setValue}
/>
);
}