{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "glass-skeleton",
  "title": "Glass Skeleton",
  "description": "A quiet placeholder with a slow sheen, shaped like what will replace it.",
  "registryDependencies": [
    "@glassyui-native/glass-provider"
  ],
  "files": [
    {
      "path": "registry/glassyui-native/glass-skeleton.tsx",
      "content": "import { forwardRef, useCallback, useState } from 'react';\nimport {\n  Animated,\n  I18nManager,\n  StyleSheet,\n  View,\n  type LayoutChangeEvent,\n  type ViewProps,\n} from 'react-native';\nimport { useGlassTheme } from './glass-provider';\nimport { GLASS_RADIUS } from './lib/tokens';\nimport { useLoop } from './lib/use-loop';\n\nexport type GlassSkeletonVariant = 'block' | 'circle' | 'text';\n\nexport type GlassSkeletonProps = Omit<ViewProps, 'children'> & {\n  /** How many lines a `text` skeleton draws. The last is shorter. Defaults to 3. */\n  lines?: number;\n  /** A `block` is sized with `style`, a `circle` is square and a `text` is a stack of lines. */\n  variant?: GlassSkeletonVariant;\n};\n\n// The band of light that crosses each placeholder, brighter over the light theme's pale fill.\nconst SHEEN = {\n  dark: 'rgba(255, 255, 255, 0.14)',\n  light: 'rgba(255, 255, 255, 0.75)',\n} as const;\n\nconst SHEEN_MS = 1800;\n\n// The last line of a paragraph stops short, as real text does.\nconst SHORT_LINE = 0.62;\n\ntype SheenProps = {\n  progress: Animated.Value;\n  /** The placeholder's width: the band starts one width before it and ends one width after. */\n  width: number;\n};\n\n// The band crosses in the reading direction, from start to end.\nfunction Sheen({ progress, width }: SheenProps) {\n  const { preferences } = useGlassTheme();\n  const direction = I18nManager.isRTL ? -1 : 1;\n  const angle = I18nManager.isRTL ? 260 : 100;\n\n  return (\n    <Animated.View\n      style={[\n        StyleSheet.absoluteFill,\n        styles.sheen,\n        {\n          experimental_backgroundImage: `linear-gradient(${angle}deg, rgba(255, 255, 255, 0) 20%, ${SHEEN[preferences.theme]} 50%, rgba(255, 255, 255, 0) 80%)`,\n          transform: [\n            {\n              translateX: progress.interpolate({\n                inputRange: [0, 1],\n                outputRange: [-width * direction, width * direction],\n              }),\n            },\n          ],\n        },\n      ]}\n    />\n  );\n}\n\n/**\n * A quiet placeholder with a slow sheen, still under Reduce Motion. It is hidden from assistive\n * technology, so mark the region that is loading with `accessibilityState={{ busy: true }}`.\n */\nexport const GlassSkeleton = forwardRef<View, GlassSkeletonProps>(\n  ({ lines = 3, onLayout, style, variant = 'block', ...rest }, ref) => {\n    const { colors, preferences } = useGlassTheme();\n    const [width, setWidth] = useState(0);\n    const sweep = useLoop(SHEEN_MS, !preferences.reduceMotion);\n    const shine = !preferences.reduceMotion && width > 0;\n    const fill = { backgroundColor: colors.controlSurface };\n\n    const measure = useCallback(\n      (event: LayoutChangeEvent) => {\n        onLayout?.(event);\n        setWidth(event.nativeEvent.layout.width);\n      },\n      [onLayout],\n    );\n\n    if (variant === 'text') {\n      const count = Math.max(1, lines);\n\n      return (\n        <View\n          ref={ref}\n          accessibilityElementsHidden\n          importantForAccessibility=\"no-hide-descendants\"\n          onLayout={measure}\n          style={[styles.text, style]}\n          {...rest}\n        >\n          {Array.from({ length: count }, (_, index) => {\n            const short = count > 1 && index === count - 1;\n\n            return (\n              <View\n                key={index}\n                style={[styles.placeholder, styles.line, fill, short && styles.short]}\n              >\n                {shine ? (\n                  <Sheen progress={sweep} width={short ? width * SHORT_LINE : width} />\n                ) : null}\n              </View>\n            );\n          })}\n        </View>\n      );\n    }\n\n    return (\n      <View\n        ref={ref}\n        accessibilityElementsHidden\n        importantForAccessibility=\"no-hide-descendants\"\n        onLayout={measure}\n        style={[\n          styles.placeholder,\n          variant === 'circle' ? styles.circle : styles.block,\n          fill,\n          style,\n        ]}\n        {...rest}\n      >\n        {shine ? <Sheen progress={sweep} width={width} /> : null}\n      </View>\n    );\n  },\n);\n\nGlassSkeleton.displayName = 'GlassSkeleton';\n\nconst styles = StyleSheet.create({\n  block: { borderRadius: 10, minHeight: 16 },\n  circle: { aspectRatio: 1, borderRadius: GLASS_RADIUS.capsule, width: 40 },\n  line: { borderRadius: GLASS_RADIUS.capsule, height: 14 },\n  placeholder: { borderCurve: 'continuous', overflow: 'hidden' },\n  sheen: { pointerEvents: 'none' },\n  short: { width: `${SHORT_LINE * 100}%` },\n  text: { gap: 10, width: '100%' },\n});\n",
      "type": "registry:ui",
      "target": "components/glassyui/glass-skeleton.tsx"
    },
    {
      "path": "registry/glassyui-native/lib/use-loop.ts",
      "content": "import { useEffect, useState } from 'react';\nimport { Animated, Easing } from 'react-native';\nimport { GLASS_MOTION } from './tokens';\n\n/**\n * A value that runs from 0 to 1 over `duration`, then starts again, for the library's only loops:\n * progress, spinners and skeletons. It rests at 0 while `running` is false and stops on unmount.\n */\nexport function useLoop(duration: number, running: boolean) {\n  // State, not a ref, holds the value: render reads it, and it never changes identity.\n  const [progress] = useState(() => new Animated.Value(0));\n\n  useEffect(() => {\n    progress.setValue(0);\n\n    if (!running) return;\n\n    const loop = Animated.loop(\n      Animated.timing(progress, {\n        duration,\n        easing: Easing.linear,\n        toValue: 1,\n        useNativeDriver: GLASS_MOTION.nativeDriver,\n      }),\n    );\n\n    loop.start();\n\n    return () => loop.stop();\n  }, [duration, progress, running]);\n\n  return progress;\n}\n",
      "type": "registry:lib",
      "target": "components/glassyui/lib/use-loop.ts"
    }
  ],
  "type": "registry:ui"
}