// Shared UI components: Icon, Pill, Button, Modal, etc.
function Icon({ name, size = 16, color = "currentColor", strokeWidth = 1.6, style, ...rest }) {
const s = size;
const stroke = { stroke: color, fill: "none", strokeWidth, strokeLinecap: "round", strokeLinejoin: "round" };
const paths = {
home: <>>,
calendar: <>>,
list: <>>,
sparkle: <>>,
bell: <>>,
plus: <>>,
check: <>>,
cross: <>>,
arrow: <>>,
back: <>>,
chev: <>>,
chevDown: <>>,
dot: <>>,
circle: <>>,
halfcircle: <>>,
repeat: <>>,
edit: <>>,
trash: <>>,
flag: <>>,
cake: <>>,
stethoscope: <>>,
users: <>>,
party: <>>,
filter: <>>,
search: <>>,
close: <>>,
moon: <>>,
sun: <>>,
sprout: <>>,
settings: <>>,
star: <>>,
};
return (
);
}
// Icon for kind
function KindIcon({ kind, size = 14, color = "currentColor" }) {
const map = { task: "check", birthday: "cake", appointment: "stethoscope", family: "users", event: "party" };
return ;
}
// Status pill
function StatusPill({ status, compact }) {
const s = STATES[status] || STATES.todo;
return (
{s.label}
);
}
// Priority pill (small flag)
function PriorityFlag({ p, withLabel }) {
const pr = PRIORITIES[p] || PRIORITIES.mid;
return (
{withLabel && pr.label}
);
}
// Kind chip
function KindChip({ kind }) {
const k = KINDS[kind] || KINDS.task;
return (
{k.label}
);
}
// Button
function Button({ variant = "ghost", size = "md", icon, children, style, ...rest }) {
const padBy = { sm: "6px 10px", md: "8px 14px", lg: "10px 18px" };
const variants = {
primary: { background: "var(--terracotta)", color: "#FFF8F0", border: "1px solid var(--terracotta-2)" },
ghost: { background: "transparent", color: "var(--ink)", border: "1px solid var(--line-2)" },
soft: { background: "var(--surface)", color: "var(--ink)", border: "1px solid var(--line)" },
quiet: { background: "transparent", color: "var(--ink-2)", border: "1px solid transparent" },
danger: { background: "transparent", color: "var(--prio-high)", border: "1px solid #E5C0BA" },
};
return (
);
}
// IconButton
function IconButton({ icon, onClick, label, active, size = 32, style }) {
return (
);
}
// Card
function Card({ children, style, padding = 20, ...rest }) {
return (
{children}
);
}
// Section heading
function SectionHeading({ eyebrow, title, action, count }) {
return (
{eyebrow &&
{eyebrow}
}
{title}
{count != null && {count}}
{action}
);
}
// Modal
function Modal({ open, onClose, children, width = 560 }) {
if (!open) return null;
return (
e.stopPropagation()}
style={{
background: "var(--bg-2)", borderRadius: 18,
border: "1px solid var(--line)",
boxShadow: "var(--shadow-lg)",
width: "100%", maxWidth: width,
maxHeight: "90vh", overflow: "auto",
}}>
{children}
);
}
// Empty state
function Empty({ icon = "sparkle", title, hint, action }) {
return (
);
}
// Status dot bar (multiple status colors stacked)
function StatusDots({ items, date, max = 4 }) {
const colors = items.slice(0, max).map(it => {
if (it.kind !== "task") return KINDS[it.kind].color;
const s = statusOn(it, date);
return STATES[s].color;
});
return (
{colors.map((c, i) => (
))}
{items.length > max && (
+{items.length - max}
)}
);
}
Object.assign(window, {
Icon, KindIcon, StatusPill, PriorityFlag, KindChip,
Button, IconButton, Card, SectionHeading, Modal, Empty, StatusDots,
});