Add web interface and tests
This commit is contained in:
103
packages/web/src/components/ItemFieldValue.tsx
Normal file
103
packages/web/src/components/ItemFieldValue.tsx
Normal file
@ -0,0 +1,103 @@
|
||||
import styled from "@emotion/styled"
|
||||
import type { ItemSection, ItemField } from "opvault.js"
|
||||
import { FieldType } from "opvault.js"
|
||||
import { useCallback, useMemo, useState } from "react"
|
||||
import { parseMonthYear } from "../utils"
|
||||
import { ErrorBoundary } from "./ErrorBoundary"
|
||||
import { useItemFieldContextMenu } from "./ItemFieldContextMenu"
|
||||
|
||||
const Container = styled.div``
|
||||
|
||||
const Password: React.FC<{
|
||||
field: ItemSection.Concealed
|
||||
}> = ({ field }) => {
|
||||
const [show, setShow] = useState(false)
|
||||
const { onRightClick, ContextMenuContainer, Item } = useItemFieldContextMenu()
|
||||
const onToggle = useCallback(() => setShow(x => !x), [])
|
||||
const onCopy = useCallback(() => {
|
||||
navigator.clipboard.writeText(field.v)
|
||||
}, [field.v])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Container
|
||||
onContextMenu={onRightClick}
|
||||
onDoubleClick={() => setShow(x => !x)}
|
||||
style={{
|
||||
fontFamily: "var(--monospace)",
|
||||
...(!show && { userSelect: "none" }),
|
||||
}}
|
||||
>
|
||||
{show ? field.v : "·".repeat(10)}
|
||||
</Container>
|
||||
<ContextMenuContainer>
|
||||
<Item onClick={onCopy}>Copier</Item>
|
||||
<Item onClick={onToggle}>{show ? "Cacher" : "Afficher"}</Item>
|
||||
</ContextMenuContainer>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const MonthYear: React.FC<{ field: ItemSection.MonthYear }> = ({ field }) => {
|
||||
const { year, month } = parseMonthYear(field.v)
|
||||
return (
|
||||
<Container>
|
||||
{month.toString().padStart(2, "0")}/{year.toString().padStart(4, "0")}
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
const DateView: React.FC<{ field: ItemSection.Date }> = ({ field }) => {
|
||||
const date = useMemo(() => new Date(field.v * 1000), [field.v])
|
||||
return <Container>{date.toLocaleDateString()}</Container>
|
||||
}
|
||||
|
||||
export const ItemFieldValue: React.FC<{
|
||||
field: ItemSection.Any
|
||||
}> = ({ field }) => {
|
||||
if (field.v == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
switch (field.k) {
|
||||
case "concealed":
|
||||
return <Password field={field} />
|
||||
case "monthYear":
|
||||
return <MonthYear field={field} />
|
||||
case "date":
|
||||
return <DateView field={field} />
|
||||
case "address":
|
||||
return (
|
||||
<Container style={{ whiteSpace: "pre" }}>
|
||||
<div>{field.v.street}</div>
|
||||
<div>
|
||||
{field.v.city}, {field.v.state} ({field.v.zip})
|
||||
</div>
|
||||
<div>{field.v.country}</div>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<Container>{field.v}</Container>
|
||||
</ErrorBoundary>
|
||||
)
|
||||
}
|
||||
|
||||
export const ItemDetailsFieldValue: React.FC<{
|
||||
field: ItemField
|
||||
}> = ({ field }) => {
|
||||
if (
|
||||
field.type === FieldType.Password ||
|
||||
(field.type === FieldType.Text && field.designation === "password")
|
||||
) {
|
||||
return <Password field={{ v: field.value } as any} />
|
||||
}
|
||||
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<Container>{field.value}</Container>
|
||||
</ErrorBoundary>
|
||||
)
|
||||
}
|
Reference in New Issue
Block a user