Install

npx @react-native-reusables/cli@latest add https://rnraielements.com/r/nativewind/package-info.json

Dependencies

No required npm packages — this item installs through the registry dependencies below.

https://reactnativereusables.com/r/nativewind/badge.json
https://reactnativereusables.com/r/nativewind/text.json
https://rnraielements.com/r/nativewind/mono.json
https://rnraielements.com/r/nativewind/snippet.json
https://rnraielements.com/r/nativewind/status.json
https://rnraielements.com/r/nativewind/table.json

AI SDK peers:ai@^7.0.89@ai-sdk/react@^4.0.92

Source

package-info.tsx
import { Badge } from '@/registry/nativewind/components/ui/badge'; import { Text } from '@/registry/nativewind/components/ui/text'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/registry/nativewind/components/ui/table'; import { Snippet, SnippetAddon, SnippetCopyButton, SnippetInput, SnippetText, } from '@/registry/nativewind/components/ai/snippet'; import { monoStyle } from '@/registry/nativewind/lib/mono'; import { cn } from '@/registry/nativewind/lib/utils'; import * as React from 'react'; import { View, type ViewProps } from 'react-native'; import { installCommand, packageChangeTypeMeta, versionTransitionSegments, type PackageChangeType, type VersionTransition, } from './package-info.logic'; /** * PackageInfo — a package metadata card (UC-CODE-01 AC-3: name, version, and an * install command that copies to the clipboard). * * THE PRD VERDICT IS PORT-AT-PARITY: "Card of package name, version, and a copyable * install command; all RNR primitives." The upstream part set, from the KB, ported * part for part: PackageInfo root (name required / currentVersion / newVersion / * changeType), PackageInfoHeader, PackageInfoName, PackageInfoVersion (the KB's * "version transition display" — current → next), PackageInfoDescription, * PackageInfoContent, PackageInfoDependencies, PackageInfoDependency (name, version), * and PackageInfoChangeType (Badge). * * THE ONE DECLARED ADDITION: upstream ships no install-command part, but AC-3 is the * binding acceptance and the verdict's own words ("a copyable install command") demand * one — so PackageInfoInstall composes the registry's existing Snippet (the copyable * command organism) inside the card. The line comes from package-info.logic's * installCommand(): the NEW version pins the install when present, otherwise latest. * * COLOR COMPRESSION rides package-info.logic's map: the web's five change-type hues * compress onto the house status vocabulary, and the badge text ("major", "added") * carries the kind — color is never the sole channel. * * COMPOSITION: the build plan names `table` as the backing primitive for this surface, * so PackageInfoDependencies is ONE Table (package/version header, one row per * dependency) — the same scaffold environment-variables uses. The name and version * ride the house mono family (lib/mono). */ type PackageInfoContextValue = { name: string; /** Segmented transition — the upgrade target emphasized over the installed version. */ versionTransition: VersionTransition; changeType?: PackageChangeType; /** The copyable line the install part renders. */ installCommandText: string; }; const PackageInfoContext = React.createContext<PackageInfoContextValue | null>(null); function usePackageInfo() { const ctx = React.useContext(PackageInfoContext); if (!ctx) throw new Error('PackageInfo sub-components must be used within <PackageInfo>'); return ctx; } export type PackageInfoProps = Omit<ViewProps, 'children'> & { /** Required upstream, and kept required — an unnamed package is not package info. */ name: string; /** What's installed now. */ currentVersion?: string; /** What the card is announcing (the upgrade target). */ newVersion?: string; changeType?: PackageChangeType; /** Overrides the default install line (pnpm add …, bun add …). */ installCommand?: string; children?: React.ReactNode; }; function PackageInfo({ name, currentVersion, newVersion, changeType, installCommand: installCommandOverride, className, children, ...props }: PackageInfoProps) { const contextValue = React.useMemo<PackageInfoContextValue>( () => ({ name, versionTransition: versionTransitionSegments(currentVersion, newVersion), changeType, // The NEW version pins the install when present (an upgrade card pins its // target); otherwise latest — currentVersion describes, it does not pin. installCommandText: installCommandOverride ?? installCommand(name, newVersion), }), [name, currentVersion, newVersion, changeType, installCommandOverride], ); return ( <PackageInfoContext.Provider value={contextValue}> <View className={cn('overflow-hidden rounded-lg border border-border bg-card', className)} {...props} > {children} </View> </PackageInfoContext.Provider> ); } function PackageInfoHeader({ className, ...props }: ViewProps & { children?: React.ReactNode }) { return ( <View className={cn('flex-row items-start justify-between gap-3 px-3 py-3', className)} {...props} /> ); } /** The package name — mono, like every house surface that shows a code identifier. */ function PackageInfoName({ children, className }: { children?: string; className?: string }) { const { name } = usePackageInfo(); return ( <Text numberOfLines={1} style={monoStyle} className={cn('text-sm font-medium text-foreground', className)} > {children ?? name} </Text> ); } /** * The transition line, from context. The upgrade case (current → next) renders as * two spans so the NEW version — what the card is announcing — is emphasized over * the installed one; single-version cards render plainly. Null context (no versions * yet) renders NOTHING — never "undefined → undefined" mid-stream. `children` * overrides with a plain string, as upstream allows. */ function PackageInfoVersion({ children, className }: { children?: string; className?: string }) { const { versionTransition } = usePackageInfo(); if (children !== undefined) { return ( <Text style={monoStyle} className={cn('text-xs text-muted-foreground', className)}> {children} </Text> ); } if (versionTransition === null) return null; if (versionTransition.kind === 'both') { return ( <View className={cn('flex-row items-baseline', className)}> <Text numberOfLines={1} style={monoStyle} className="text-xs text-muted-foreground"> {versionTransition.current} </Text> <Text style={monoStyle} className="px-0.5 text-xs text-muted-foreground"> → </Text> <Text numberOfLines={1} style={monoStyle} className="max-w-28 text-xs font-semibold text-foreground"> {versionTransition.next} </Text> </View> ); } return ( <Text numberOfLines={1} style={monoStyle} className={cn('text-xs text-muted-foreground', className)}> {versionTransition.value} </Text> ); } /** * The change badge: the compressed tone plus its WORD. `children` overrides the label, * exactly as the web allows. No changeType in the root renders nothing. */ function PackageInfoChangeType({ children, className }: { children?: string; className?: string }) { const { changeType } = usePackageInfo(); if (!changeType) return null; const meta = packageChangeTypeMeta(changeType); return ( <Badge variant="outline" className={cn('shrink-0', meta.className, className)}> <Text>{children ?? meta.label}</Text> </Badge> ); } /** The muted one-liner under the header ("ORM with first-class edge support"). */ function PackageInfoDescription({ className, ...props }: React.ComponentProps<typeof Text>) { return ( <Text numberOfLines={2} className={cn('px-3 pb-3 text-xs text-muted-foreground', className)} {...props} /> ); } function PackageInfoContent({ className, children, ...props }: ViewProps & { children?: React.ReactNode }) { return ( <View className={cn('gap-3 border-t border-border p-3', className)} {...props}> {children} </View> ); } /** * AC-3's copyable install line, composed on the Snippet organism — the `$` prefix, * the readOnly field that scrolls a long command, and the flip-and-revert copy button * are all Snippet's, and none of them are rebuilt here. */ function PackageInfoInstall({ className }: { className?: string }) { const { installCommandText } = usePackageInfo(); return ( <Snippet code={installCommandText} className={className}> <SnippetAddon> <SnippetText>$</SnippetText> </SnippetAddon> <SnippetInput /> <SnippetCopyButton /> </Snippet> ); } /** * The dependency table: package/version header, one row per PackageInfoDependency. * Fixed column sizes + the table's own horizontal scroll handle overflowing names. */ function PackageInfoDependencies({ className, children }: { className?: string; children?: React.ReactNode }) { return ( <Table scrollable className={className}> <TableHeader> <TableHead className="w-40 shrink-0"> <Text className="text-xs font-medium text-muted-foreground">Package</Text> </TableHead> <TableHead className="w-24 shrink-0"> <Text className="text-xs font-medium text-muted-foreground">Version</Text> </TableHead> </TableHeader> <TableBody>{children}</TableBody> </Table> ); } export type PackageInfoDependencyProps = { /** Required upstream, and kept required. */ name: string; /** Missing version renders an em dash, never an empty cell. */ version?: string; className?: string; }; function PackageInfoDependency({ name, version, className }: PackageInfoDependencyProps) { return ( <TableRow className={className}> <TableCell className="w-40 shrink-0"> <Text numberOfLines={1} style={monoStyle} className="text-xs text-foreground"> {name} </Text> </TableCell> <TableCell className="w-24 shrink-0"> <Text numberOfLines={1} style={monoStyle} className="text-xs text-muted-foreground"> {version?.trim() || '—'} </Text> </TableCell> </TableRow> ); } export { PackageInfo, PackageInfoChangeType, PackageInfoContent, PackageInfoDependencies, PackageInfoDependency, PackageInfoDescription, PackageInfoHeader, PackageInfoInstall, PackageInfoName, PackageInfoVersion, usePackageInfo, };
package-info.logic.ts
/** * PackageInfo — pure logic. Zero react-native imports, so the Vitest tier owns it (see * the header of lib/reasoning-lifecycle.ts for why component files themselves cannot * load under Node). * * COLOR COMPRESSION, DECLARED — same move as commit.logic, one file earlier in the * wave family. The web paints change types with FIVE escape-hatch hues (major red, * minor yellow, patch green, added blue, removed gray). The styling contract permits * exactly three non-token colors, confined to lib/status.ts, so the five compress onto * the house StatusTone vocabulary — major→error, minor→denied, patch→success, * added→running (the accent role carries "blue"), removed→pending (the muted pole * carries "gray"). The badge's own TEXT carries the kind ("major", "added"), so the * compression loses no information — color is never the sole channel (WCAG 1.4.1). * * The version line is the KB's "version transition display": current → next with an * arrow, each half optional, whitespace-trimmed (streamed metadata arrives dirty). * The install command is UC-CODE-01 AC-3's copyable line: `npm install <name>` with an * optional `@version` pin — the NEW version when one is present (an upgrade card pins * the target), nothing otherwise (a fresh install tracks latest). */ import { statusColor } from '@/registry/nativewind/lib/status'; /** The web original's change-type union, verbatim. */ export type PackageChangeType = 'major' | 'minor' | 'patch' | 'added' | 'removed'; /** Every change type, spelled out — exhaustiveness for the meta table. */ export const PACKAGE_CHANGE_TYPE_KEYS = [ 'major', 'minor', 'patch', 'added', 'removed', ] as const satisfies readonly PackageChangeType[]; export type PackageChangeTypeMeta = { /** The badge's default label — the word itself carries the kind. */ label: string; /** Precomposed text class from the shared statusColor map. */ className: string; }; /** change type → label + tone, one record, exhaustive by type. */ export const PACKAGE_CHANGE_TYPE_META: Record<PackageChangeType, PackageChangeTypeMeta> = { major: { label: 'major', className: statusColor.error }, minor: { label: 'minor', className: statusColor.denied }, patch: { label: 'patch', className: statusColor.success }, added: { label: 'added', className: statusColor.running }, removed: { label: 'removed', className: statusColor.pending }, }; export function packageChangeTypeMeta(changeType: PackageChangeType): PackageChangeTypeMeta { return PACKAGE_CHANGE_TYPE_META[changeType]; } /** * The version transition as SEGMENTS: "1.2.3 → 2.0.0" splits so the card can * emphasize the upgrade target (new version bold/dark — the web's treatment) while * the old version stays muted. "1.2.3" (installed) / "2.0.0" (announced) render as * a single segment; null when neither arrives yet — null means render nothing, never * "undefined → undefined" mid-stream. */ export type VersionTransition = | { kind: 'both'; current: string; next: string } | { kind: 'single'; value: string } | null; export function versionTransitionSegments(currentVersion?: string, newVersion?: string): VersionTransition { const current = currentVersion?.trim(); const next = newVersion?.trim(); if (current && next) return { kind: 'both', current, next }; const value = current || next; return value ? { kind: 'single', value } : null; } /** * The flat string form of the same transition, for plain-text callers (a11y labels, * log lines). Pure join of the segments — one source of truth, never a second parse. */ export function formatVersionTransition(currentVersion?: string, newVersion?: string): string | null { const segments = versionTransitionSegments(currentVersion, newVersion); if (!segments) return null; return segments.kind === 'both' ? `${segments.current} → ${segments.next}` : segments.value; } /** AC-3's copyable line. `version` pins the install; absent means latest. */ export function installCommand(name: string, version?: string): string { const pkg = name.trim(); const pinned = version?.trim(); return pinned ? `npm install ${pkg}@${pinned}` : `npm install ${pkg}`; }

Props

NameTypeDefaultDescription
namestring—Required upstream, and kept required — an unnamed package is not package info.
currentVersionstring | undefined—What's installed now.
newVersionstring | undefined—What the card is announcing (the upgrade target).
changeTypePackageChangeType | undefined——
installCommandstring | undefined—Overrides the default install line (pnpm add …, bun add …).
childrenReact.ReactNode——

Theming

Themed via border-border, bg-card, text-foreground, text-muted-foreground.

Usage

() => ( <PackageInfo name="@rnr-ai-elements/registry" currentVersion="0.1.0" newVersion="0.2.0" changeType="patch"> <PackageInfoHeader> <PackageInfoTitle>Registry</PackageInfoTitle> <PackageInfoName /> </PackageInfoHeader> <PackageInfoDescription>The copy-paste registry both engines fan out from</PackageInfoDescription> <PackageInfoInstall /> </PackageInfo> )