{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "states",
"title": "Loading, empty, error, not found",
"description": "The states every screen has besides its data, one look for each.",
"dependencies": [
"@radix-ui/themes"
],
"files": [
{
"path": "src/components/states/States.tsx",
"content": "/**\n * The three states every screen and panel has besides \"here is the data\": still loading, nothing there,\n * and failed. One look for each, so no screen invents its own — and none forgets one.\n *\n * An error shows the API's own reason (a refusal says why) and, when the caller can, a way to try again.\n */\nimport { Box, Button, Callout, Flex, Heading, Spinner, Text } from \"@radix-ui/themes\";\nimport type { ReactNode } from \"react\";\n\nexport function LoadingState({ label = \"Loading…\" }: { label?: string }) {\n return (\n <Flex align=\"center\" gap=\"2\" py=\"4\" role=\"status\" aria-live=\"polite\">\n <Spinner />\n <Text color=\"gray\">{label}</Text>\n </Flex>\n );\n}\n\nexport interface EmptyStateProps {\n title: string;\n description?: ReactNode;\n /** The next step, e.g. \"Import a list\" — shown only when the caller can take it. */\n action?: ReactNode;\n}\n\nexport function EmptyState({ title, description, action }: EmptyStateProps) {\n return (\n <Box py=\"5\" px=\"4\" style={{ textAlign: \"center\", border: \"1px dashed var(--gray-a6)\", borderRadius: \"var(--radius-3)\" }}>\n <Heading as=\"h3\" size=\"3\" mb=\"1\">{title}</Heading>\n {description && <Text as=\"p\" size=\"2\" color=\"gray\" mb={action ? \"3\" : \"0\"}>{description}</Text>}\n {action && <Flex justify=\"center\">{action}</Flex>}\n </Box>\n );\n}\n\nexport interface ErrorStateProps {\n /** The reason, in the API's words. */\n error: string;\n title?: string;\n onRetry?: () => void;\n}\n\nexport function ErrorState({ error, title = \"That did not work\", onRetry }: ErrorStateProps) {\n return (\n <Callout.Root color=\"red\" role=\"alert\">\n <Callout.Text>\n <Text weight=\"bold\">{title}. </Text>\n {error}\n </Callout.Text>\n {onRetry && (\n <Box>\n <Button size=\"1\" variant=\"soft\" color=\"red\" onClick={onRetry}>Try again</Button>\n </Box>\n )}\n </Callout.Root>\n );\n}\n\n/** Not found — the same answer the API gives for a row the caller may not see (a 404, never a 403). */\nexport function NotFoundState({ what = \"It\", back }: { what?: string; back?: ReactNode }) {\n return (\n <Box py=\"6\">\n <Heading as=\"h1\" size=\"6\" mb=\"2\">Not found</Heading>\n <Text as=\"p\" color=\"gray\" mb=\"3\">{what} does not exist, or you cannot see it.</Text>\n {back}\n </Box>\n );\n}\n",
"type": "registry:component",
"target": "erp-ui/components/states/States.tsx"
}
],
"type": "registry:component"
}