{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "item",
  "title": "Item",
  "description": "A row with media, a title, a description and actions, alone or in a list.",
  "dependencies": [
    "@base-ui/react",
    "@stylexjs/stylex"
  ],
  "registryDependencies": [
    "https://constella.aayush.cv/r/base.json",
    "https://constella.aayush.cv/r/separator.json"
  ],
  "files": [
    {
      "path": "src/components/primitives/item.tsx",
      "content": "import { mergeProps } from \"@base-ui/react/merge-props\";\nimport { useRender } from \"@base-ui/react/use-render\";\nimport * as stylex from \"@stylexjs/stylex\";\nimport type { ComponentProps } from \"react\";\n\nimport { Separator } from \"@/components/primitives/separator\";\nimport type { SeparatorProps } from \"@/components/primitives/separator\";\nimport type { Sx } from \"@/lib/constella/styles\";\nimport { typeStyles } from \"@/lib/constella/styles\";\nimport {\n  colors,\n  durations,\n  easings,\n  fontSizes,\n  fontWeights,\n  lineHeights,\n  media,\n  presses,\n  radii,\n  sizes,\n  space,\n  strokes,\n} from \"@/lib/constella/tokens.stylex\";\n\nconst itemVariants = [\"default\", \"outline\", \"secondary\"] as const;\nconst itemSizes = [\"xs\", \"sm\", \"default\"] as const;\nconst itemMediaVariants = [\"default\", \"icon\", \"image\", \"tile\"] as const;\n\ntype ItemVariant = (typeof itemVariants)[number];\ntype ItemSize = (typeof itemSizes)[number];\ntype ItemMediaVariant = (typeof itemMediaVariants)[number];\n\ntype Styled<T> = Omit<T, \"className\" | \"style\"> & {\n  sx?: Sx;\n};\n\ntype ItemProps = Styled<useRender.ComponentProps<\"div\">> & {\n  size?: ItemSize;\n  variant?: ItemVariant;\n};\n\n// An item only answers when it's rendered as a link, a button or a label for its control.\nconst pressable = \":is(a, button, label)\";\nconst inSize = (size: ItemSize) =>\n  `:is([data-slot='item'][data-size='${size}'] *)`;\nconst withDescription =\n  \":is([data-slot='item']:has([data-slot='item-description']) > *)\";\n\nconst styles = stylex.create({\n  item: {\n    alignItems: \"center\",\n    backgroundClip: \"padding-box\",\n    backgroundColor: {\n      default: \"transparent\",\n      [media.hover]: {\n        default: \"transparent\",\n        [`${pressable}:hover`]: colors.fillSubtle,\n      },\n      [`${pressable}:active`]: colors.fillSubtle,\n    },\n    borderBlockEndColor: \"transparent\",\n    borderBlockStartColor: \"transparent\",\n    borderInlineEndColor: \"transparent\",\n    borderInlineStartColor: \"transparent\",\n    borderBlockEndStyle: \"solid\",\n    borderBlockStartStyle: \"solid\",\n    borderInlineEndStyle: \"solid\",\n    borderInlineStartStyle: \"solid\",\n    borderBlockEndWidth: strokes.border,\n    borderBlockStartWidth: strokes.border,\n    borderInlineEndWidth: strokes.border,\n    borderInlineStartWidth: strokes.border,\n    borderStartStartRadius: radii.sm,\n    borderStartEndRadius: radii.sm,\n    borderEndStartRadius: radii.sm,\n    borderEndEndRadius: radii.sm,\n    color: colors.textPrimary,\n    columnGap: space.popup,\n    display: \"flex\",\n    flexWrap: \"wrap\",\n    paddingBlockEnd: space.popup,\n    paddingBlockStart: space.popup,\n    paddingInlineEnd: space.sm,\n    paddingInlineStart: space.sm,\n    rowGap: space.popup,\n    textAlign: \"start\",\n    textDecorationLine: \"none\",\n    transform: {\n      default: null,\n      [`${pressable}:active`]: {\n        default: presses.row,\n        [media.reducedMotion]: null,\n      },\n    },\n    transitionDuration: `${durations.hover}, ${durations.press}`,\n    transitionProperty: \"background-color, transform\",\n    transitionTimingFunction: `ease, ${easings.out}`,\n    width: \"100%\",\n  },\n  outline: {\n    borderBlockEndColor: colors.border,\n    borderBlockStartColor: colors.border,\n    borderInlineEndColor: colors.border,\n    borderInlineStartColor: colors.border,\n  },\n  secondary: {\n    backgroundColor: {\n      default: colors.fillSubtle,\n      [media.hover]: {\n        default: colors.fillSubtle,\n        [`${pressable}:hover`]: colors.fill,\n      },\n      [`${pressable}:active`]: colors.fill,\n    },\n  },\n  xs: {\n    columnGap: space.xs,\n    paddingBlockEnd: space.xs,\n    paddingBlockStart: space.xs,\n    paddingInlineEnd: space.popup,\n    paddingInlineStart: space.popup,\n    rowGap: space.xs,\n  },\n  media: {\n    alignItems: \"center\",\n    alignSelf: { default: null, [withDescription]: \"flex-start\" },\n    display: \"flex\",\n    flexShrink: 0,\n    gap: space.xs,\n    justifyContent: \"center\",\n    // Level with the title's first line, not the block.\n    transform: {\n      default: null,\n      [withDescription]: `translateY(${space.xxxs})`,\n    },\n  },\n  icon: {\n    color: colors.textSecondary,\n  },\n  image: {\n    borderStartStartRadius: radii.xs,\n    borderStartEndRadius: radii.xs,\n    borderEndStartRadius: radii.xs,\n    borderEndEndRadius: radii.xs,\n    height: {\n      default: sizes.media,\n      [inSize(\"sm\")]: sizes.controlMd,\n      [inSize(\"xs\")]: sizes.controlXs,\n    },\n    overflow: \"hidden\",\n    width: {\n      default: sizes.media,\n      [inSize(\"sm\")]: sizes.controlMd,\n      [inSize(\"xs\")]: sizes.controlXs,\n    },\n  },\n  // A mark that isn't a picture, like a logo, sits on a filled square in the image's box.\n  tile: {\n    backgroundColor: colors.fillSubtle,\n  },\n  // The basis wraps actions under the text once both no longer fit on one line.\n  content: {\n    display: \"flex\",\n    flex: {\n      default: `1 1 ${sizes.itemText}`,\n      \":is([data-slot='item-content'] + *)\": \"none\",\n    },\n    flexDirection: \"column\",\n    gap: { default: space.xxxs, [inSize(\"xs\")]: 0 },\n    minWidth: 0,\n  },\n  title: {\n    alignItems: \"center\",\n    display: \"flex\",\n    fontWeight: fontWeights.regular,\n    gap: space.xs,\n    lineHeight: \"inherit\",\n    overflow: \"hidden\",\n    overflowWrap: \"anywhere\",\n    // Held to the content's width, so a nowrap title truncates rather than overflowing.\n    maxWidth: \"100%\",\n    width: \"fit-content\",\n  },\n  // A default item's rows read at its own size; smaller items keep the control size.\n  description: {\n    color: colors.textSecondary,\n    fontSize: {\n      default: fontSizes.textMd,\n      [inSize(\"sm\")]: fontSizes.textSm,\n      [inSize(\"xs\")]: fontSizes.textSm,\n    },\n    lineHeight: {\n      default: lineHeights.textMd,\n      [inSize(\"sm\")]: lineHeights.textSm,\n      [inSize(\"xs\")]: lineHeights.textSm,\n    },\n    display: \"-webkit-box\",\n    marginBlockEnd: 0,\n    marginBlockStart: 0,\n    overflow: \"hidden\",\n    overflowWrap: \"anywhere\",\n    textWrap: \"pretty\",\n    WebkitBoxOrient: \"vertical\",\n    WebkitLineClamp: 2,\n  },\n  actions: {\n    alignItems: \"center\",\n    display: \"flex\",\n    gap: space.xs,\n  },\n  // Wraps, so a long title pushes its badge to the next line rather than squeezing beside it.\n  edge: {\n    alignItems: \"center\",\n    display: \"flex\",\n    flexBasis: \"100%\",\n    flexWrap: \"wrap\",\n    gap: space.xs,\n    justifyContent: \"space-between\",\n  },\n  group: {\n    display: \"flex\",\n    flexDirection: \"column\",\n    // Separated rows space by their own padding, with the line between them.\n    gap: {\n      default: space.md,\n      \":has([data-size='sm'])\": space.popup,\n      \":has([data-size='xs'])\": space.xs,\n      \":has(> [data-slot='item-separator'])\": 0,\n    },\n    listStyleType: \"none\",\n    paddingInlineEnd: 0,\n    paddingInlineStart: 0,\n    width: \"100%\",\n  },\n});\n\nconst variantStyles = {\n  default: null,\n  outline: styles.outline,\n  secondary: styles.secondary,\n} satisfies Record<ItemVariant, stylex.StyleXStyles | null>;\n\nconst mediaStyles = {\n  default: null,\n  icon: styles.icon,\n  image: styles.image,\n  tile: [styles.image, styles.tile],\n} satisfies Record<ItemMediaVariant, stylex.StyleXStyles | null>;\n\n/** Render it as a `ul`, with each Item as an `li`, when its items are a list. */\nfunction ItemGroup({\n  render,\n  sx,\n  ...props\n}: Styled<useRender.ComponentProps<\"div\">>) {\n  return useRender({\n    defaultTagName: \"div\",\n    props: mergeProps<\"div\">(props, stylex.props(styles.group, sx)),\n    render,\n    state: { slot: \"item-group\" },\n  });\n}\n\nfunction ItemSeparator({ sx, ...props }: SeparatorProps) {\n  return (\n    <Separator\n      data-slot=\"item-separator\"\n      orientation=\"horizontal\"\n      {...props}\n      sx={sx}\n    />\n  );\n}\n\nfunction Item({\n  render,\n  size = \"default\",\n  sx,\n  variant = \"default\",\n  ...props\n}: ItemProps) {\n  return useRender({\n    defaultTagName: \"div\",\n    props: mergeProps<\"div\">(\n      props,\n      stylex.props(\n        [\n          size === \"default\" ? typeStyles.textMd : typeStyles.textSm,\n          styles.item,\n        ],\n        variantStyles[variant],\n        size === \"xs\" && styles.xs,\n        sx\n      )\n    ),\n    render,\n    // Base UI writes state as data attributes: `data-slot`, `data-size`, `data-variant`.\n    state: { size, slot: \"item\", variant },\n  });\n}\n\nfunction ItemMedia({\n  sx,\n  variant = \"default\",\n  ...props\n}: Styled<ComponentProps<\"div\">> & { variant?: ItemMediaVariant }) {\n  return (\n    <div\n      data-slot=\"item-media\"\n      data-variant={variant}\n      {...props}\n      {...stylex.props(styles.media, mediaStyles[variant], sx)}\n    />\n  );\n}\n\nfunction ItemContent({ sx, ...props }: Styled<ComponentProps<\"div\">>) {\n  return (\n    <div\n      data-slot=\"item-content\"\n      {...props}\n      {...stylex.props(styles.content, sx)}\n    />\n  );\n}\n\nfunction ItemTitle({\n  render,\n  sx,\n  ...props\n}: Styled<useRender.ComponentProps<\"div\">>) {\n  return useRender({\n    defaultTagName: \"div\",\n    props: mergeProps<\"div\">(props, stylex.props(styles.title, sx)),\n    render,\n    state: { slot: \"item-title\" },\n  });\n}\n\nfunction ItemDescription({ sx, ...props }: Styled<ComponentProps<\"p\">>) {\n  return (\n    <p\n      data-slot=\"item-description\"\n      {...props}\n      {...stylex.props(styles.description, sx)}\n    />\n  );\n}\n\nfunction ItemActions({ sx, ...props }: Styled<ComponentProps<\"div\">>) {\n  return (\n    <div\n      data-slot=\"item-actions\"\n      {...props}\n      {...stylex.props(styles.actions, sx)}\n    />\n  );\n}\n\nfunction ItemHeader({ sx, ...props }: Styled<ComponentProps<\"div\">>) {\n  return (\n    <div\n      data-slot=\"item-header\"\n      {...props}\n      {...stylex.props(styles.edge, sx)}\n    />\n  );\n}\n\nfunction ItemFooter({ sx, ...props }: Styled<ComponentProps<\"div\">>) {\n  return (\n    <div\n      data-slot=\"item-footer\"\n      {...props}\n      {...stylex.props(styles.edge, sx)}\n    />\n  );\n}\n\nexport {\n  Item,\n  ItemActions,\n  ItemContent,\n  ItemDescription,\n  ItemFooter,\n  ItemGroup,\n  ItemHeader,\n  ItemMedia,\n  itemMediaVariants,\n  ItemSeparator,\n  itemSizes,\n  ItemTitle,\n  itemVariants,\n};\nexport type { ItemMediaVariant, ItemProps, ItemSize, ItemVariant };\n",
      "type": "registry:component"
    }
  ],
  "docs": "Usage: https://constella.aayush.cv/docs/components/item/index.md",
  "type": "registry:ui"
}