{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"file-type-icon","title":"File Type Icon","description":"Show a file’s type as a document icon with its extension. Search terms: file icon, attachment, pdf.","dependencies":["react-file-icon@1.6.0"],"devDependencies":["@types/react-file-icon@1.0.5"],"registryDependencies":["@siteplane/base"],"files":[{"path":"packages/ui/src/components/file-type-icon/file-type-icon.tsx","content":"\"use client\";\n\nimport * as React from \"react\";\nimport {\n  FileIcon,\n  defaultStyles,\n  type FileIconProps,\n  type IconType,\n} from \"react-file-icon\";\nimport { cn } from \"../../system/utils/cn\";\n\nexport type FileTypeIconProps = {\n  /** The file name used to identify the extension. Render its accessible name alongside the icon. */\n  name: string;\n  /** Optional extension override, for files without a suffix. */\n  extension?: string | undefined;\n  className?: string | undefined;\n};\n\ntype FileTypeIconTone = \"error\" | \"success\" | \"warning\" | \"accent\" | \"neutral\";\n\n// The label takes the kit's solid status and brand fills, the fills of the\n// destructive and accent Badge, with white text, so a brand override reaches\n// it: PDFs red, sheets green, slides and archives orange, documents and media\n// the accent, code and unknown files neutral.\nconst tones: Partial<Record<IconType, FileTypeIconTone>> = {\n  acrobat: \"error\",\n  document: \"accent\",\n  spreadsheet: \"success\",\n  presentation: \"warning\",\n  image: \"accent\",\n  vector: \"accent\",\n  video: \"accent\",\n  audio: \"accent\",\n  compressed: \"warning\",\n  code: \"neutral\",\n  code2: \"neutral\",\n};\n\nconst labelColors: Record<FileTypeIconTone, string> = {\n  error: \"var(--sp-rw-bg-destructive)\",\n  success: \"var(--sp-rw-bg-positive)\",\n  warning: \"var(--sp-rw-bg-attention)\",\n  accent: \"var(--sp-rw-bg-accent)\",\n  neutral: \"var(--sp-rw-fg-tertiary)\",\n};\n\n/**\n * react-file-icon 1.6.0 gives the page group of every icon the same\n * `id=\"file\"`, so a page with two icons repeats an id, in the server HTML as\n * well. Nothing refers to that id, and the library's other ids are unique per\n * icon, so the kit renders the library's tree without it.\n */\nfunction FileIconWithoutFixedId(props: FileIconProps): React.ReactElement {\n  // The library's component is a plain function: called here, its useId runs\n  // as part of this component, in the same order on every render.\n  const icon = FileIcon(props) as React.ReactElement<{\n    children?: React.ReactNode;\n  }>;\n\n  return React.cloneElement(\n    icon,\n    undefined,\n    React.Children.map(icon.props.children, (child) =>\n      React.isValidElement<{ id?: string | undefined }>(child) &&\n      child.props.id === \"file\"\n        ? React.cloneElement(child, { id: undefined })\n        : child,\n    ),\n  );\n}\n\n/** Decorative SVG file icon; the complete glyph and label scale together. */\nexport function FileTypeIcon({\n  name,\n  extension,\n  className,\n}: FileTypeIconProps): React.ReactElement {\n  const basename = name.split(/[\\\\/]/).pop() ?? name;\n  const suffix = (\n    extension ??\n    (basename.lastIndexOf(\".\") > 0 ? basename.split(\".\").pop() : \"\") ??\n    \"\"\n  )\n    .replace(/^\\./, \"\")\n    .toLowerCase();\n  const preset = Object.hasOwn(defaultStyles, suffix)\n    ? defaultStyles[suffix as keyof typeof defaultStyles]\n    : undefined;\n  const type =\n    preset?.type ??\n    (/^(tsx|vue|svelte|yaml)$/.test(suffix)\n      ? \"code\"\n      : suffix === \"zip\" || suffix === \"7z\"\n        ? \"compressed\"\n        : \"document\");\n  const tone: FileTypeIconTone =\n    preset || /^(tsx|vue|svelte|yaml|7z)$/.test(suffix)\n      ? (tones[type] ?? \"neutral\")\n      : \"neutral\";\n  return (\n    <span\n      aria-hidden=\"true\"\n      data-slot=\"file-type-icon\"\n      data-extension={suffix || undefined}\n      data-tone={tone}\n      className={cn(\n        \"sp-file-type-icon\",\n        \"inline-flex h-10 w-[34px] shrink-0 items-center justify-center [&>svg]:block [&>svg]:h-full [&>svg]:w-full [&>svg]:opacity-100\",\n        className,\n      )}\n    >\n      <FileIconWithoutFixedId\n        type={type}\n        extension={\n          suffix.length <= 5 && /^[a-z0-9]+$/.test(suffix)\n            ? suffix.toUpperCase()\n            : \"FILE\"\n        }\n        color=\"var(--sp-semantic-color-muted)\"\n        foldColor=\"var(--sp-semantic-color-border)\"\n        glyphColor=\"var(--sp-semantic-color-muted-foreground)\"\n        labelColor={labelColors[tone]}\n        labelTextColor=\"var(--sp-rw-fg-emphasis)\"\n        gradientOpacity={0}\n        radius={3}\n      />\n    </span>\n  );\n}\n","type":"registry:ui","target":"~/src/siteplane/components/file-type-icon/file-type-icon.tsx"},{"path":"packages/ui/src/components/file-type-icon/index.ts","content":"export * from \"./file-type-icon\";\n","type":"registry:ui","target":"~/src/siteplane/components/file-type-icon/index.ts"}],"type":"registry:ui","docs":"https://ui.siteplane.io/docs-markdown/components/file-type-icon.md","categories":["Data display"],"meta":{"avoidWhen":["Avoid when a thumbnail would identify an image or video better.","Avoid as the only cue for a file’s type: the icon is decorative, so keep the file name visible."],"description":"Show a file’s type as a document icon with its extension.","knownLimitations":["The type comes from the file name or the extension prop only; the file’s contents and MIME type are not read.","There is no size prop: resize the box with className and keep its proportion.","The label color follows the type, from the kit status and accent colors; no prop sets it."],"providerNotes":["Depends on the react-file-icon package, which the registry installs with the component."],"releaseStatus":"stable","searchTerms":["file-type-icon","file icon","document icon","extension","attachment","pdf"],"useWhen":["Use when a list, an attachment or an upload row names a file and should show its type at a glance."]}}