{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"empty-state","type":"registry:ui","title":"Empty State","description":"What a list, table or panel shows while it has nothing yet – an icon on a soft tile, a title, one line and a button for the first step, framed or bare","dependencies":[],"files":[{"path":"registry/empty-state/empty-state.tsx","type":"registry:ui","content":"\"use client\";\n\nimport { createElement, type ComponentPropsWithRef, type ReactNode } from \"react\";\n\n/*\n * Empty State – a single self-contained file.\n * What a list, table or panel shows while there is nothing in it: an icon on a soft tile, a title, one line on what\n * will show up here, and a button for the first step. Framed in a dashed drop area, or bare inside a panel.\n * The styles are injected below, so no extra files are needed.\n */\n\n/** An icon from Hugeicons (e.g. `import { Plug01Icon } from \"@hugeicons/core-free-icons\"`) – drawn inline */\nexport type HugeIcon = readonly (readonly [string, { readonly [key: string]: string | number }])[];\n\nexport type EmptyStateProps = Omit<ComponentPropsWithRef<\"div\">, \"title\"> & {\n  /** What's empty, e.g. \"No schedules yet\" */\n  title: ReactNode;\n  /** One line on what will show up here, or how to get there */\n  text?: ReactNode;\n  /** The first step – usually a small secondary Button; several buttons sit in a row */\n  action?: ReactNode;\n  /** The glyph on the tile: an inline SVG or a Hugeicons icon object. Defaults to a file; null shows no tile */\n  icon?: ReactNode | HugeIcon;\n  /** Without the frame – for a panel or page where it is the only content */\n  bare?: boolean;\n  /** Fills its parent's height and centres itself in it – for a list or table area that keeps its size */\n  tall?: boolean;\n};\n\nexport function EmptyState({ title, text, action, icon = FILE, bare, tall, className, ...rest }: EmptyStateProps) {\n  return (\n    <>\n      {/* React 19 hoists this into <head> once, no matter how many render */}\n      <style href=\"ui-empty-state\" precedence=\"default\">\n        {css}\n      </style>\n      <div className={[\"ui-est\", className].filter(Boolean).join(\" \")} data-bare={bare || undefined} data-tall={tall || undefined} {...rest}>\n        {/* icon={null}: no tile at all, the title moves up */}\n        {icon != null && icon !== false && (\n          <span className=\"ui-est__icon\" aria-hidden=\"true\">\n            <Glyph icon={icon} />\n          </span>\n        )}\n        <p className=\"ui-est__title\">{title}</p>\n        {text != null && text !== false && <p className=\"ui-est__text\">{text}</p>}\n        {action != null && action !== false && <div className=\"ui-est__action\">{action}</div>}\n      </div>\n    </>\n  );\n}\n\nconst isHugeIcon = (icon: unknown): icon is HugeIcon =>\n  Array.isArray(icon) && icon.length > 0 && icon.every((part) => Array.isArray(part) && typeof part[0] === \"string\");\n\n/** Draws a Hugeicons icon object as an inline SVG; anything else is shown as it is */\nfunction Glyph({ icon }: { icon: ReactNode | HugeIcon }) {\n  if (!isHugeIcon(icon)) return <>{icon}</>;\n  return (\n    <svg viewBox=\"0 0 24 24\" fill=\"none\" focusable=\"false\">\n      {icon.map(([tag, { key, ...attrs }], i) =>\n        createElement(tag, { ...attrs, key: String(key ?? i), ...(\"strokeWidth\" in attrs ? { strokeWidth: STROKE } : null) }),\n      )}\n    </svg>\n  );\n}\nconst STROKE = 1.75;\n\n/** The default glyph: a file – Hugeicons Free (MIT, © Hugeicons, hugeicons.com) */\nconst line = { stroke: \"currentColor\", strokeLinecap: \"round\", strokeLinejoin: \"round\", strokeWidth: \"1.5\" };\nconst FILE: HugeIcon = [\n  [\"path\", { d: \"M8 17H16\", ...line }],\n  [\"path\", { d: \"M8 13H12\", ...line }],\n  [\n    \"path\",\n    {\n      d: \"M13 2.5V3C13 5.82843 13 7.24264 13.8787 8.12132C14.7574 9 16.1716 9 19 9H19.5M20 10.6569V14C20 17.7712 20 19.6569 18.8284 20.8284C17.6569 22 15.7712 22 12 22C8.22876 22 6.34315 22 5.17157 20.8284C4 19.6569 4 17.7712 4 14V9.45584C4 6.21082 4 4.58831 4.88607 3.48933C5.06508 3.26731 5.26731 3.06508 5.48933 2.88607C6.58831 2 8.21082 2 11.4558 2C12.1614 2 12.5141 2 12.8372 2.11401C12.9044 2.13772 12.9702 2.165 13.0345 2.19575C13.3436 2.34355 13.593 2.593 14.0919 3.09188L18.8284 7.82843C19.4065 8.40649 19.6955 8.69552 19.8478 9.06306C20 9.4306 20 9.83935 20 10.6569Z\",\n      ...line,\n    },\n  ],\n];\n\n/* ---------- Styles ---------- */\n\nconst css = /* css */ `\n/* Colors as variables – override them on .ui-est (see Theming in the docs) */\n.ui-est {\n  --ui-est-title: var(--aui-ink, #171717);\n  --ui-est-text: var(--aui-muted, #737373);\n  --ui-est-icon: var(--aui-muted, #737373);\n  --ui-est-tile: var(--aui-surface, #ffffff);\n  --ui-est-tile-line: var(--aui-border, #e5e5e5);\n  --ui-est-tile-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);\n  --ui-est-frame: #fafafa;\n  --ui-est-line: var(--aui-border, #e5e5e5);\n\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  box-sizing: border-box;\n  min-width: 0;\n  padding: 48px 24px 52px;\n  border: 1px dashed var(--ui-est-line);\n  border-radius: calc(var(--aui-radius, 12px) + 4px);\n  background: var(--ui-est-frame);\n  font-family: var(--aui-font, \"Inter\", ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif);\n  font-size: 14px;\n  line-height: 21px;\n  letter-spacing: -0.01em;\n  text-align: center;\n  -webkit-font-smoothing: antialiased;\n}\n/* bare: no frame – the panel or page around it is the frame */\n.ui-est[data-bare] {\n  padding: 0;\n  border: 0;\n  background: none;\n}\n/* tall: fills its parent's height, the content in the middle */\n.ui-est[data-tall] {\n  justify-content: center;\n  min-height: 100%;\n}\n\n/* The icon on a small white card */\n.ui-est__icon {\n  display: grid;\n  place-items: center;\n  flex: none;\n  box-sizing: border-box;\n  width: 44px;\n  height: 44px;\n  border: 1px solid var(--ui-est-tile-line);\n  border-radius: 12px;\n  background: var(--ui-est-tile);\n  box-shadow: var(--ui-est-tile-shadow);\n  color: var(--ui-est-icon);\n}\n.ui-est__icon > svg,\n.ui-est__icon > img {\n  display: block;\n  width: 20px;\n  height: 20px;\n}\n.ui-est__title {\n  max-width: 100%;\n  margin: 0;\n  font-weight: 500;\n  color: var(--ui-est-title);\n  overflow-wrap: anywhere;\n}\n.ui-est__icon + .ui-est__title { margin-top: 16px; }\n.ui-est__text {\n  max-width: 430px;\n  margin: 4px 0 0;\n  font-size: 13.5px;\n  line-height: 20px;\n  color: var(--ui-est-text);\n  text-wrap: balance;\n  overflow-wrap: anywhere;\n}\n.ui-est__action {\n  display: flex;\n  flex-wrap: wrap;\n  justify-content: center;\n  gap: 8px;\n  margin-top: 20px;\n}\n\n/* Small screens: a tighter frame */\n@media (max-width: 560px) {\n  .ui-est:not([data-bare]) {\n    padding: 36px 18px 40px;\n  }\n}\n\n/* Dark mode: follows a .dark class or data-theme=\"dark\" on any ancestor, e.g. <html> */\n:where(.dark, [data-theme=\"dark\"]) .ui-est {\n  --ui-est-title: var(--aui-ink, #ededed);\n  --ui-est-text: var(--aui-muted, #8f8f8f);\n  --ui-est-icon: var(--aui-muted, #8f8f8f);\n  --ui-est-tile: var(--aui-surface, #232326);\n  --ui-est-tile-line: var(--aui-border, rgba(255, 255, 255, 0.1));\n  --ui-est-tile-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);\n  --ui-est-frame: rgba(255, 255, 255, 0.02);\n  --ui-est-line: var(--aui-border, rgba(255, 255, 255, 0.12));\n}\n`;\n"}]}