{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "glass-switch",
  "title": "Glass Switch",
  "description": "An on/off switch whose knob turns to glass under a finger as it slides.",
  "registryDependencies": [
    "@glassyui-native/glass-provider",
    "@glassyui-native/glass-surface"
  ],
  "files": [
    {
      "path": "registry/glassyui-native/glass-switch.tsx",
      "content": "import { forwardRef, useCallback, useEffect, useState } from 'react';\nimport {\n  Animated,\n  I18nManager,\n  Pressable,\n  StyleSheet,\n  View,\n  type GestureResponderEvent,\n  type PressableProps,\n  type StyleProp,\n  type ViewStyle,\n} from 'react-native';\nimport { useGlassTheme } from './glass-provider';\nimport { GlassSurface } from './glass-surface';\nimport { GLASS_MOTION, GLASS_RADIUS } from './lib/tokens';\nimport { useControllableState } from './lib/use-controllable-state';\n\nexport type GlassSwitchProps = Omit<PressableProps, 'children' | 'onPress' | 'style'> & {\n  checked?: boolean;\n  defaultChecked?: boolean;\n  onCheckedChange?: (checked: boolean) => void;\n  size?: 'sm' | 'md';\n  style?: StyleProp<ViewStyle>;\n};\n\nconst SIZE = {\n  sm: { height: 24, inset: 2, knobHeight: 20, knobWidth: 28, width: 46 },\n  md: { height: 28, inset: 2, knobHeight: 24, knobWidth: 36, width: 60 },\n} as const;\n\n/**\n * A switch whose white knob turns to clear glass under a finger and slides to its new side.\n * Reduce Motion keeps the end state and drops the slide.\n */\nexport const GlassSwitch = forwardRef<View, GlassSwitchProps>(\n  (\n    {\n      checked,\n      defaultChecked = false,\n      disabled,\n      onCheckedChange,\n      onPressIn,\n      onPressOut,\n      size = 'md',\n      style,\n      ...rest\n    },\n    ref,\n  ) => {\n    const { colors, preferences } = useGlassTheme();\n    const [on, setOn] = useControllableState(checked, defaultChecked, onCheckedChange);\n    const [progress] = useState(() => new Animated.Value(on ? 1 : 0));\n    const [pressed] = useState(() => new Animated.Value(0));\n    const metrics = SIZE[size];\n\n    const travel =\n      (metrics.width - metrics.knobWidth - metrics.inset * 2) * (I18nManager.isRTL ? -1 : 1);\n\n    useEffect(() => {\n      if (preferences.reduceMotion) {\n        progress.setValue(on ? 1 : 0);\n\n        return;\n      }\n\n      Animated.spring(progress, {\n        ...GLASS_MOTION.spring,\n        toValue: on ? 1 : 0,\n        useNativeDriver: GLASS_MOTION.nativeDriver,\n      }).start();\n    }, [on, preferences.reduceMotion, progress]);\n\n    const toggle = useCallback(() => setOn(!on), [on, setOn]);\n\n    const handlePressIn = useCallback(\n      (event: GestureResponderEvent) => {\n        onPressIn?.(event);\n        Animated.timing(pressed, {\n          duration: 120,\n          toValue: 1,\n          useNativeDriver: GLASS_MOTION.nativeDriver,\n        }).start();\n      },\n      [onPressIn, pressed],\n    );\n\n    const handlePressOut = useCallback(\n      (event: GestureResponderEvent) => {\n        onPressOut?.(event);\n        Animated.timing(pressed, {\n          duration: 260,\n          toValue: 0,\n          useNativeDriver: GLASS_MOTION.nativeDriver,\n        }).start();\n      },\n      [onPressOut, pressed],\n    );\n\n    return (\n      <Pressable\n        ref={ref}\n        accessibilityRole=\"switch\"\n        accessibilityState={{ checked: on, disabled: Boolean(disabled) }}\n        disabled={disabled}\n        hitSlop={8}\n        onPress={toggle}\n        onPressIn={handlePressIn}\n        onPressOut={handlePressOut}\n        style={[\n          styles.track,\n          { backgroundColor: colors.track, height: metrics.height, width: metrics.width },\n          disabled && styles.disabled,\n          style,\n        ]}\n        {...rest}\n      >\n        <Animated.View\n          style={[\n            StyleSheet.absoluteFill,\n            styles.round,\n            styles.layer,\n            { backgroundColor: colors.accent, opacity: progress },\n          ]}\n        />\n        <Animated.View\n          style={{\n            height: metrics.knobHeight,\n            left: metrics.inset,\n            pointerEvents: 'none',\n            position: 'absolute',\n            top: metrics.inset,\n            width: metrics.knobWidth,\n            transform: [\n              {\n                translateX: progress.interpolate({ inputRange: [0, 1], outputRange: [0, travel] }),\n              },\n              {\n                scale: preferences.reduceMotion\n                  ? 1\n                  : pressed.interpolate({ inputRange: [0, 1], outputRange: [1, 1.18] }),\n              },\n            ],\n          }}\n        >\n          <GlassSurface\n            elevation=\"none\"\n            preset=\"subtle\"\n            style={[StyleSheet.absoluteFill, styles.round]}\n          />\n          <Animated.View\n            style={[\n              StyleSheet.absoluteFill,\n              styles.round,\n              {\n                backgroundColor: colors.knob,\n                boxShadow: '0px 2px 6px rgba(28, 28, 30, 0.16), 0px 1px 2px rgba(28, 28, 30, 0.1)',\n                opacity: preferences.reduceTransparency\n                  ? 1\n                  : pressed.interpolate({ inputRange: [0, 1], outputRange: [1, 0.12] }),\n              },\n            ]}\n          />\n        </Animated.View>\n      </Pressable>\n    );\n  },\n);\n\nGlassSwitch.displayName = 'GlassSwitch';\n\nconst styles = StyleSheet.create({\n  disabled: { opacity: 0.45 },\n  layer: { pointerEvents: 'none' },\n  round: { borderCurve: 'continuous', borderRadius: GLASS_RADIUS.capsule },\n  track: { borderCurve: 'continuous', borderRadius: GLASS_RADIUS.capsule },\n});\n",
      "type": "registry:ui",
      "target": "components/glassyui/glass-switch.tsx"
    }
  ],
  "type": "registry:ui"
}