feat(web): shared design primitives (IconChip, SectionHeader, EmptyState)
This commit is contained in:
parent
66d82b8b68
commit
e0c07be232
4 changed files with 130 additions and 0 deletions
48
src/ds/EmptyState.tsx
Normal file
48
src/ds/EmptyState.tsx
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
import { Icon, type IconName } from "./icons";
|
||||||
|
|
||||||
|
/** A designed empty / low-data state: a soft icon medallion, a title, and a
|
||||||
|
* one-line hint. Replaces the bare "Гүйлгээ алга" text so a quiet screen still
|
||||||
|
* feels intentional. */
|
||||||
|
export function EmptyState({
|
||||||
|
icon,
|
||||||
|
title,
|
||||||
|
hint,
|
||||||
|
compact = false,
|
||||||
|
}: {
|
||||||
|
icon: IconName;
|
||||||
|
title: string;
|
||||||
|
hint?: string;
|
||||||
|
compact?: boolean;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
alignItems: "center",
|
||||||
|
textAlign: "center",
|
||||||
|
gap: 10,
|
||||||
|
padding: compact ? "20px 16px" : "36px 20px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
aria-hidden
|
||||||
|
style={{
|
||||||
|
width: 52,
|
||||||
|
height: 52,
|
||||||
|
borderRadius: 18,
|
||||||
|
display: "grid",
|
||||||
|
placeItems: "center",
|
||||||
|
color: "var(--seed-color-fg-neutral-muted, #8b8b8b)",
|
||||||
|
background: "var(--seed-color-bg-neutral-subtle, #eef0f2)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon name={icon} size={26} />
|
||||||
|
</span>
|
||||||
|
<span style={{ fontSize: 15, fontWeight: 700, color: "var(--seed-color-fg-neutral)" }}>{title}</span>
|
||||||
|
{hint ? (
|
||||||
|
<span style={{ fontSize: 13, maxWidth: 280, color: "var(--seed-color-fg-neutral-muted, #8b8b8b)" }}>{hint}</span>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
35
src/ds/IconChip.tsx
Normal file
35
src/ds/IconChip.tsx
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { Icon, type IconName } from "./icons";
|
||||||
|
|
||||||
|
/** The rounded, tinted icon square used on every list row (transactions,
|
||||||
|
* accounts, assets, planner categories, profile menu). One source of truth for
|
||||||
|
* the chip so all rows share the exact same shape/size. */
|
||||||
|
export function IconChip({
|
||||||
|
icon,
|
||||||
|
tint,
|
||||||
|
fg,
|
||||||
|
size = 40,
|
||||||
|
}: {
|
||||||
|
icon: IconName;
|
||||||
|
tint: string;
|
||||||
|
fg: string;
|
||||||
|
size?: number;
|
||||||
|
}) {
|
||||||
|
const radius = Math.round(size * 0.32);
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
aria-hidden
|
||||||
|
style={{
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
|
flexShrink: 0,
|
||||||
|
borderRadius: radius,
|
||||||
|
display: "grid",
|
||||||
|
placeItems: "center",
|
||||||
|
color: fg,
|
||||||
|
background: tint,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon name={icon} size={Math.round(size * 0.5)} />
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
44
src/ds/SectionHeader.tsx
Normal file
44
src/ds/SectionHeader.tsx
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
import type { ReactNode } from "react";
|
||||||
|
import { Icon } from "./icons";
|
||||||
|
|
||||||
|
/** A section title with an optional trailing action (e.g. a `+` add button).
|
||||||
|
* Used to head every list block so spacing + type are consistent. */
|
||||||
|
export function SectionHeader({
|
||||||
|
title,
|
||||||
|
action,
|
||||||
|
onAction,
|
||||||
|
actionLabel,
|
||||||
|
}: {
|
||||||
|
title: string;
|
||||||
|
action?: ReactNode;
|
||||||
|
onAction?: () => void;
|
||||||
|
actionLabel?: string;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12 }}>
|
||||||
|
<h2 style={{ margin: 0, fontSize: 17, fontWeight: 700, color: "var(--seed-color-fg-neutral)" }}>{title}</h2>
|
||||||
|
{onAction ? (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={onAction}
|
||||||
|
aria-label={actionLabel}
|
||||||
|
style={{
|
||||||
|
all: "unset",
|
||||||
|
cursor: "pointer",
|
||||||
|
width: 32,
|
||||||
|
height: 32,
|
||||||
|
borderRadius: 10,
|
||||||
|
display: "grid",
|
||||||
|
placeItems: "center",
|
||||||
|
color: "var(--seed-color-fg-neutral)",
|
||||||
|
background: "var(--seed-color-bg-neutral-subtle, #eef0f2)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon name="plus" size={18} />
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
action ?? null
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -17,3 +17,6 @@ export { Card } from "./Card";
|
||||||
export type { CardProps } from "./Card";
|
export type { CardProps } from "./Card";
|
||||||
export * from "./categoryStyle";
|
export * from "./categoryStyle";
|
||||||
export * from "./icons";
|
export * from "./icons";
|
||||||
|
export * from "./IconChip";
|
||||||
|
export * from "./SectionHeader";
|
||||||
|
export * from "./EmptyState";
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue