dodil/erp-ui

public/r/states.json

{
  "$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"
}