add some more data manipulation buttons
This commit is contained in:
parent
e3c6f6ec92
commit
0d30b03b93
4 changed files with 123 additions and 38 deletions
|
@ -8,11 +8,27 @@ export interface Props {
|
|||
}
|
||||
|
||||
const ClickableContainer = styled.span`
|
||||
display: inline-block;
|
||||
padding: 4px 10px;
|
||||
margin: 4px;
|
||||
border: 1px solid #eee;
|
||||
border-radius: 5px;
|
||||
width: 120px;
|
||||
|
||||
&:hover {
|
||||
background-color: #eee;
|
||||
}
|
||||
`;
|
||||
|
||||
const EditingBox = styled.input`
|
||||
display: inline-block;
|
||||
padding: 4px 10px;
|
||||
margin: 4px;
|
||||
border: 1px solid #eee;
|
||||
border-radius: 5px;
|
||||
width: 120px;
|
||||
`;
|
||||
|
||||
export default function EditBox({ valueAtom }: Props) {
|
||||
const [value, setValue] = useAtom(valueAtom);
|
||||
const [valueInput, setValueInput] = useState("");
|
||||
|
@ -32,6 +48,7 @@ export default function EditBox({ valueAtom }: Props) {
|
|||
e.preventDefault();
|
||||
try {
|
||||
const n = parseFloat(valueInput);
|
||||
if (isNaN(n) || !isFinite(n)) return;
|
||||
setValue(n);
|
||||
setEditing(false);
|
||||
} catch (e) {
|
||||
|
@ -42,7 +59,7 @@ export default function EditBox({ valueAtom }: Props) {
|
|||
if (editing) {
|
||||
return (
|
||||
<form onSubmit={finalize} style={{ display: "inline" }}>
|
||||
<input
|
||||
<EditingBox
|
||||
autoFocus={true}
|
||||
type="number"
|
||||
step="0.01"
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { Atom, useAtom } from "jotai";
|
||||
import { Badge, ListGroup } from "react-bootstrap";
|
||||
|
||||
export interface IPerson {
|
||||
name: string;
|
||||
|
@ -8,7 +9,25 @@ export interface Props {
|
|||
personAtom: Atom<IPerson>;
|
||||
}
|
||||
|
||||
export default function Person({ personAtom }: Props) {
|
||||
const [person, _] = useAtom(personAtom);
|
||||
return <span style={{ marginInline: "5px" }}>{person.name}</span>;
|
||||
export default function Person({ personAtom, splitBetweenAtom }: Props) {
|
||||
const [person] = useAtom(personAtom);
|
||||
const [splitBetween, setSplitBetween] = useAtom(splitBetweenAtom);
|
||||
|
||||
const removeSelf = (_) => {
|
||||
setSplitBetween([...splitBetween.filter((x) => x != personAtom)]);
|
||||
};
|
||||
|
||||
return (
|
||||
<ListGroup.Item>
|
||||
{person.name}
|
||||
<Badge
|
||||
bg="danger"
|
||||
pill
|
||||
onClick={removeSelf}
|
||||
style={{ cursor: "pointer" }}
|
||||
>
|
||||
×
|
||||
</Badge>
|
||||
</ListGroup.Item>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
import { Atom, useAtom } from "jotai";
|
||||
import { atom, Atom, useAtom, useAtomValue } from "jotai";
|
||||
import { useState } from "react";
|
||||
import { Badge, Card, ListGroup } from "react-bootstrap";
|
||||
import { receiptAtom } from "../lib/state";
|
||||
import EditBox from "./EditBox";
|
||||
import Person, { IPerson } from "./Person";
|
||||
|
||||
|
@ -13,16 +16,49 @@ export interface Props {
|
|||
}
|
||||
|
||||
function SplitBetween({ splitBetweenAtom }) {
|
||||
const [splitBetween, _] = useAtom(splitBetweenAtom);
|
||||
return splitBetween.length > 0 ? (
|
||||
const [splitBetween, setSplitBetween] = useAtom(splitBetweenAtom);
|
||||
const [input, setInput] = useState("");
|
||||
const [editing, setEditing] = useState(false);
|
||||
|
||||
const startEditing = (_) => {
|
||||
setInput("");
|
||||
setEditing(true);
|
||||
};
|
||||
|
||||
const addPerson = (e) => {
|
||||
e.preventDefault();
|
||||
setSplitBetween([...splitBetween, atom({ name: input })]);
|
||||
setEditing(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
Split between ({splitBetween.length}):
|
||||
{splitBetween.map((a, i) => (
|
||||
<Person personAtom={a} key={`split-${i}`} />
|
||||
))}
|
||||
<ListGroup horizontal>
|
||||
{splitBetween.map((a, i) => (
|
||||
<Person
|
||||
personAtom={a}
|
||||
key={`split-${i}`}
|
||||
splitBetweenAtom={splitBetweenAtom}
|
||||
/>
|
||||
))}
|
||||
<ListGroup.Item onClick={startEditing}>
|
||||
{editing ? (
|
||||
<form onSubmit={addPerson}>
|
||||
<input
|
||||
autoFocus={true}
|
||||
type="text"
|
||||
value={input}
|
||||
onBlur={(_) => setEditing(false)}
|
||||
onInput={(e) => setInput(e.target.value)}
|
||||
/>
|
||||
</form>
|
||||
) : (
|
||||
"[+]"
|
||||
)}
|
||||
</ListGroup.Item>
|
||||
</ListGroup>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -31,14 +67,28 @@ function Price({ priceAtom }) {
|
|||
}
|
||||
|
||||
export default function ReceiptItem({ itemAtom }: Props) {
|
||||
const [receipt, setReceipt] = useAtom(receiptAtom);
|
||||
const [item, _] = useAtom(itemAtom);
|
||||
|
||||
const removeSelf = (_) => {
|
||||
setReceipt([...receipt.filter((x) => x != itemAtom)]);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<span>
|
||||
<b>{item.name}</b>
|
||||
</span>
|
||||
(<Price priceAtom={item.price} />)
|
||||
<SplitBetween splitBetweenAtom={item.splitBetween} />
|
||||
</>
|
||||
<Card>
|
||||
<Card.Body>
|
||||
<Card.Title>{item.name}</Card.Title>
|
||||
Item price: <Price priceAtom={item.price} />
|
||||
<Badge
|
||||
bg="danger"
|
||||
pill
|
||||
onClick={removeSelf}
|
||||
style={{ cursor: "pointer" }}
|
||||
>
|
||||
×
|
||||
</Badge>
|
||||
<SplitBetween splitBetweenAtom={item.splitBetween} />
|
||||
</Card.Body>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -51,26 +51,25 @@ const Home: NextPage = () => {
|
|||
<EditBox valueAtom={totalAtom} />
|
||||
</div>
|
||||
|
||||
<ul>
|
||||
{receipt.map((itemAtom, i) => {
|
||||
return (
|
||||
<li key={`receiptItem-${i}`}>
|
||||
<ReceiptItem itemAtom={itemAtom} />
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
{receipt.map((itemAtom, i) => {
|
||||
return <ReceiptItem itemAtom={itemAtom} key={`receiptItem-${i}`} />;
|
||||
})}
|
||||
|
||||
<div>
|
||||
Total breakdown:
|
||||
<ul>
|
||||
{[...total.entries()].map(([person, value], i) => (
|
||||
<li key={`breakdown-${i}`}>
|
||||
<b>{person}</b>: {formatter.format(value)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
{total.size > 0 && (
|
||||
<>
|
||||
<h3>Weighted Breakdown</h3>
|
||||
|
||||
<div>
|
||||
<ul>
|
||||
{[...total.entries()].map(([person, value], i) => (
|
||||
<li key={`breakdown-${i}`}>
|
||||
<b>{person}</b>: {formatter.format(value)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<small>
|
||||
<a href="https://github.com/iptq/wisesplit/">[source]</a>
|
||||
|
|
Loading…
Reference in a new issue