feat(card): refine header and inline actions
This commit is contained in:
@@ -1,3 +1,30 @@
|
|||||||
|
.card {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 28rem;
|
||||||
|
}
|
||||||
|
.cardActions {
|
||||||
|
position: absolute;
|
||||||
|
top: 4.5rem;
|
||||||
|
right: clamp(1.1rem, 3vw, 1.6rem);
|
||||||
|
padding: 4px;
|
||||||
|
border: 1px solid var(--mantine-color-gray-3);
|
||||||
|
border-radius: var(--mantine-radius-lg);
|
||||||
|
background: var(--mantine-color-white);
|
||||||
|
box-shadow: 0 3px 12px #0000001a;
|
||||||
|
}
|
||||||
|
@media (hover: hover) and (pointer: fine) {
|
||||||
|
.cardActions {
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.card:hover .cardActions,
|
||||||
|
.card:focus-within .cardActions {
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.wrap {
|
.wrap {
|
||||||
overflow-wrap: anywhere;
|
overflow-wrap: anywhere;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
@@ -21,7 +48,21 @@
|
|||||||
.operations[data-pending] {
|
.operations[data-pending] {
|
||||||
opacity: 0.55;
|
opacity: 0.55;
|
||||||
}
|
}
|
||||||
|
@media (pointer: coarse) {
|
||||||
|
.cardActions button {
|
||||||
|
min-width: 36px;
|
||||||
|
min-height: 36px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@media (prefers-reduced-motion: no-preference) {
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
|
.cardActions {
|
||||||
|
transition: opacity 180ms ease;
|
||||||
|
}
|
||||||
|
.page button {
|
||||||
|
transition:
|
||||||
|
background-color 180ms ease,
|
||||||
|
color 180ms ease;
|
||||||
|
}
|
||||||
.operations {
|
.operations {
|
||||||
animation: operations-appear 200ms ease-out;
|
animation: operations-appear 200ms ease-out;
|
||||||
transition: opacity 150ms ease;
|
transition: opacity 150ms ease;
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import {
|
|||||||
Button,
|
Button,
|
||||||
Group,
|
Group,
|
||||||
Pagination,
|
Pagination,
|
||||||
Paper,
|
|
||||||
Select,
|
Select,
|
||||||
Skeleton,
|
Skeleton,
|
||||||
Stack,
|
Stack,
|
||||||
@@ -124,41 +123,76 @@ const CardPageView: FC<CardPageViewProps> = (props) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Stack gap="lg">
|
<Stack gap="lg" className={classes.page}>
|
||||||
<Paper p="md" withBorder>
|
<Stack>
|
||||||
<Stack>
|
<Title order={1} className={classes.wrap}>
|
||||||
<Title order={1} className={classes.wrap}>
|
{card.name}
|
||||||
{card.name}
|
</Title>
|
||||||
</Title>
|
<div className={classes.card}>
|
||||||
<BankCardFace card={card} />
|
<BankCardFace card={card} />
|
||||||
<Text c="dimmed">{t("bank.manualBalance")}</Text>
|
<Group gap={4} wrap="nowrap" className={classes.cardActions}>
|
||||||
<Group>
|
<ActionIcon
|
||||||
<Button
|
variant="subtle"
|
||||||
disabled={Boolean(card.archivedAt) || !vm.cards.data}
|
size="md"
|
||||||
onClick={() => vm.setOperation("new")}
|
radius="md"
|
||||||
|
aria-label={t("bank.editCard")}
|
||||||
|
title={t("bank.editCard")}
|
||||||
|
onClick={() => vm.setEditingCard(true)}
|
||||||
>
|
>
|
||||||
{t("bank.addOperation")}
|
<svg
|
||||||
</Button>
|
width="18"
|
||||||
<Button variant="default" onClick={() => vm.setEditingCard(true)}>
|
height="18"
|
||||||
{t("bank.editCard")}
|
viewBox="0 0 24 24"
|
||||||
</Button>
|
fill="none"
|
||||||
<Button
|
stroke="currentColor"
|
||||||
variant="default"
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<path d="m16 3 5 5-12 12-6 1 1-6L16 3Z M13 6l5 5" />
|
||||||
|
</svg>
|
||||||
|
</ActionIcon>
|
||||||
|
<ActionIcon
|
||||||
|
variant="subtle"
|
||||||
|
size="md"
|
||||||
|
radius="md"
|
||||||
|
color="red"
|
||||||
|
aria-label={t(card.archivedAt ? "bank.restore" : "bank.archive")}
|
||||||
|
title={t(card.archivedAt ? "bank.restore" : "bank.archive")}
|
||||||
loading={vm.archivePending}
|
loading={vm.archivePending}
|
||||||
onClick={() => void vm.toggleArchive()}
|
onClick={() => void vm.toggleArchive()}
|
||||||
>
|
>
|
||||||
{t(card.archivedAt ? "bank.restore" : "bank.archive")}
|
<svg
|
||||||
</Button>
|
width="18"
|
||||||
|
height="18"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d={
|
||||||
|
card.archivedAt
|
||||||
|
? "M3 3h18v4H3z M5 7v14h14V7 M12 17V10m-3 3 3-3 3 3"
|
||||||
|
: "M3 6h18 M9 6V3h6v3 M5 6l1 15h12l1-15 M10 10v7 M14 10v7"
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</ActionIcon>
|
||||||
</Group>
|
</Group>
|
||||||
{vm.archiveError ? (
|
</div>
|
||||||
<BankFeedback
|
{vm.archiveError ? (
|
||||||
error={vm.archiveError}
|
<BankFeedback
|
||||||
retry={() => void vm.card.refetch()}
|
error={vm.archiveError}
|
||||||
/>
|
retry={() => void vm.card.refetch()}
|
||||||
) : null}
|
/>
|
||||||
{card.archivedAt && <Text>{t("bank.archiveHint")}</Text>}
|
) : null}
|
||||||
</Stack>
|
{card.archivedAt && <Text>{t("bank.archiveHint")}</Text>}
|
||||||
</Paper>
|
</Stack>
|
||||||
{vm.card.isError && (
|
{vm.card.isError && (
|
||||||
<Group role="alert">
|
<Group role="alert">
|
||||||
<Text>{t("bank.refreshError")}</Text>
|
<Text>{t("bank.refreshError")}</Text>
|
||||||
@@ -185,7 +219,30 @@ const CardPageView: FC<CardPageViewProps> = (props) => {
|
|||||||
retry={() => void vm.summary.refetch()}
|
retry={() => void vm.summary.refetch()}
|
||||||
/>
|
/>
|
||||||
<Group justify="space-between" align="end">
|
<Group justify="space-between" align="end">
|
||||||
<Title order={2}>{t("bank.operations")}</Title>
|
<Group gap="xs">
|
||||||
|
<Title order={2}>{t("bank.operations")}</Title>
|
||||||
|
<ActionIcon
|
||||||
|
variant="subtle"
|
||||||
|
size="lg"
|
||||||
|
aria-label={t("bank.addOperation")}
|
||||||
|
title={t("bank.addOperation")}
|
||||||
|
disabled={Boolean(card.archivedAt) || !vm.cards.data}
|
||||||
|
onClick={() => vm.setOperation("new")}
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
strokeLinecap="round"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<path d="M12 5v14 M5 12h14" />
|
||||||
|
</svg>
|
||||||
|
</ActionIcon>
|
||||||
|
</Group>
|
||||||
<Select
|
<Select
|
||||||
label={t("bank.pageSize")}
|
label={t("bank.pageSize")}
|
||||||
value={vm.pageSize}
|
value={vm.pageSize}
|
||||||
|
|||||||
Reference in New Issue
Block a user