@yahoo/uds-mobile 2.21.1 → 2.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/README.md +2 -0
  2. package/dist/bin/automated-config/dist/generated/generatedConfigs.mjs +67 -51
  3. package/dist/components/Button/buttonTheme.cjs +69 -0
  4. package/dist/components/Button/buttonTheme.d.cts +24 -0
  5. package/dist/components/Button/buttonTheme.d.cts.map +1 -0
  6. package/dist/components/Button/buttonTheme.d.ts +24 -0
  7. package/dist/components/Button/buttonTheme.d.ts.map +1 -0
  8. package/dist/components/Button/buttonTheme.js +68 -0
  9. package/dist/components/Button/buttonTheme.js.map +1 -0
  10. package/dist/components/Button.cjs +7 -1
  11. package/dist/components/Button.d.cts.map +1 -1
  12. package/dist/components/Button.d.ts.map +1 -1
  13. package/dist/components/Button.js +7 -1
  14. package/dist/components/Button.js.map +1 -1
  15. package/dist/components/IconButton.cjs +33 -21
  16. package/dist/components/IconButton.d.cts.map +1 -1
  17. package/dist/components/IconButton.d.ts.map +1 -1
  18. package/dist/components/IconButton.js +34 -22
  19. package/dist/components/IconButton.js.map +1 -1
  20. package/dist/components/Switch.cjs +34 -12
  21. package/dist/components/Switch.d.cts.map +1 -1
  22. package/dist/components/Switch.d.ts.map +1 -1
  23. package/dist/components/Switch.js +36 -14
  24. package/dist/components/Switch.js.map +1 -1
  25. package/dist/components/UDSGestureProvider.cjs +4 -0
  26. package/dist/components/UDSGestureProvider.d.cts +3 -0
  27. package/dist/components/UDSGestureProvider.d.ts +3 -0
  28. package/dist/components/UDSGestureProvider.js +3 -0
  29. package/dist/components/UDSProvider.cjs +10 -5
  30. package/dist/components/UDSProvider.d.cts +15 -7
  31. package/dist/components/UDSProvider.d.cts.map +1 -1
  32. package/dist/components/UDSProvider.d.ts +15 -7
  33. package/dist/components/UDSProvider.d.ts.map +1 -1
  34. package/dist/components/UDSProvider.js +10 -6
  35. package/dist/components/UDSProvider.js.map +1 -1
  36. package/dist/components/internal/Overlay/OverlayPortal.js.map +1 -1
  37. package/dist/jest/mocks/styles.cjs +21 -9
  38. package/dist/jest/mocks/styles.d.cts.map +1 -1
  39. package/dist/jest/mocks/styles.d.ts.map +1 -1
  40. package/dist/jest/mocks/styles.js +21 -9
  41. package/dist/jest/mocks/styles.js.map +1 -1
  42. package/dist/jest/mocks/unistyles.cjs +16 -1
  43. package/dist/jest/mocks/unistyles.d.cts +56 -2
  44. package/dist/jest/mocks/unistyles.d.cts.map +1 -1
  45. package/dist/jest/mocks/unistyles.d.ts +56 -2
  46. package/dist/jest/mocks/unistyles.d.ts.map +1 -1
  47. package/dist/jest/mocks/unistyles.js +16 -1
  48. package/dist/jest/mocks/unistyles.js.map +1 -1
  49. package/dist/portal.cjs +1 -1
  50. package/dist/portal.js +1 -1
  51. package/dist/portal.js.map +1 -1
  52. package/generated/styles.d.ts +1 -1
  53. package/package.json +11 -1
@@ -1 +1 @@
1
- {"version":3,"file":"IconButton.d.cts","names":[],"sources":["../../src/components/IconButton.tsx"],"mappings":";;;;;;;;;;UAiDU,eAAA,SAAwB,IAAA,CAAK,gBAAA;;EAErC,IAAA,EAAM,QAAA;EAFE;EAIR,OAAA,GAAU,iBAAA;;EAEV,IAAA,GAAO,cAAA;EAJD;EAMN,WAAA,GAAc,WAAA;EAFP;EAIP,SAAA,GAAY,UAAA;EAAA;EAEZ,OAAA;EAOM;;;;EAFN,cAAA;EAjBqC;EAmBrC,GAAA,GAAM,GAAA,CAAI,IAAA;AAAA;;;;;;;;;;;;;;;;AAAI;;;;;;;;;;;;;;;;;;cAwCV,UAAA,EAAU,OAAA,CAAA,oBAAA,CAAA,eAAA"}
1
+ {"version":3,"file":"IconButton.d.cts","names":[],"sources":["../../src/components/IconButton.tsx"],"mappings":";;;;;;;;;;UAoDU,eAAA,SAAwB,IAAA,CAAK,gBAAA;;EAErC,IAAA,EAAM,QAAA;EAFE;EAIR,OAAA,GAAU,iBAAA;;EAEV,IAAA,GAAO,cAAA;EAJD;EAMN,WAAA,GAAc,WAAA;EAFP;EAIP,SAAA,GAAY,UAAA;EAAA;EAEZ,OAAA;EAOM;;;;EAFN,cAAA;EAjBqC;EAmBrC,GAAA,GAAM,GAAA,CAAI,IAAA;AAAA;;;;;;;;;;;;;;;;AAAI;;;;;;;;;;;;;;;;;;cAwCV,UAAA,EAAU,OAAA,CAAA,oBAAA,CAAA,eAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"IconButton.d.ts","names":[],"sources":["../../src/components/IconButton.tsx"],"mappings":";;;;;;;;;;UAiDU,eAAA,SAAwB,IAAA,CAAK,gBAAA;;EAErC,IAAA,EAAM,QAAA;EAFE;EAIR,OAAA,GAAU,iBAAA;;EAEV,IAAA,GAAO,cAAA;EAJD;EAMN,WAAA,GAAc,WAAA;EAFP;EAIP,SAAA,GAAY,UAAA;EAAA;EAEZ,OAAA;EAOM;;;;EAFN,cAAA;EAjBqC;EAmBrC,GAAA,GAAM,GAAA,CAAI,IAAA;AAAA;;;;;;;;;;;;;;;;AAAI;;;;;;;;;;;;;;;;;;cAwCV,UAAA,EAAU,OAAA,CAAA,oBAAA,CAAA,eAAA"}
1
+ {"version":3,"file":"IconButton.d.ts","names":[],"sources":["../../src/components/IconButton.tsx"],"mappings":";;;;;;;;;;UAoDU,eAAA,SAAwB,IAAA,CAAK,gBAAA;;EAErC,IAAA,EAAM,QAAA;EAFE;EAIR,OAAA,GAAU,iBAAA;;EAEV,IAAA,GAAO,cAAA;EAJD;EAMN,WAAA,GAAc,WAAA;EAFP;EAIP,SAAA,GAAY,UAAA;EAAA;EAEZ,OAAA;EAOM;;;;EAFN,cAAA;EAjBqC;EAmBrC,GAAA,GAAM,GAAA,CAAI,IAAA;AAAA;;;;;;;;;;;;;;;;AAAI;;;;;;;;;;;;;;;;;;cAwCV,UAAA,EAAU,OAAA,CAAA,oBAAA,CAAA,eAAA"}
@@ -2,11 +2,13 @@
2
2
  import { SCALE_EFFECTS } from "../motion-tokens/dist/index.js";
3
3
  import { BUTTON_SPRING_CONFIG } from "../motion.js";
4
4
  import { Icon } from "./Icon.js";
5
+ import { getIconButtonControlMetrics } from "./Button/buttonTheme.js";
5
6
  import { AnimatedPressable } from "./Pressable.js";
6
7
  import { memo, useCallback, useMemo, useState } from "react";
7
- import { ActivityIndicator } from "react-native";
8
+ import { ActivityIndicator, Platform } from "react-native";
8
9
  import { jsx } from "react/jsx-runtime";
9
10
  import { buttonStyles, iconButtonStyles, styles } from "../../generated/styles";
11
+ import { useUnistyles } from "react-native-unistyles";
10
12
  import { Easing, useAnimatedStyle, useDerivedValue, useSharedValue, withSpring, withTiming } from "react-native-reanimated";
11
13
  import { useAnimatedTheme, useAnimatedVariantColor } from "react-native-unistyles/reanimated";
12
14
  //#region src/components/IconButton.tsx
@@ -55,17 +57,24 @@ function interpolateShadowAlpha(shadow, alpha) {
55
57
  const IconButton = memo(function IconButton({ name, variant = "primary", size = "md", iconVariant = "outline", iconColor, loading, disabled, style, accessibilityLabel, accessibilityHint, disableEffects = false, onPressIn, onPressOut, ref, ...props }) {
56
58
  const isDisabled = disabled || loading;
57
59
  const shouldAnimate = !disableEffects && !isDisabled;
60
+ const shouldAnimateVariantColors = Platform.OS !== "web";
61
+ const { theme } = useUnistyles();
62
+ const { controlHeight } = useMemo(() => getIconButtonControlMetrics(theme, size), [theme, size]);
63
+ const matchedControlDimensions = controlHeight > 0 ? {
64
+ height: controlHeight,
65
+ width: controlHeight
66
+ } : void 0;
58
67
  const [pressed, setPressed] = useState(false);
59
- iconButtonStyles.useVariants({ size });
60
- buttonStyles.useVariants({
68
+ const resolvedIconButtonStyles = iconButtonStyles.useVariants({ size }) ?? iconButtonStyles;
69
+ const resolvedButtonStyles = buttonStyles.useVariants({
61
70
  variant,
62
71
  disabled: isDisabled,
63
72
  pressed
64
- });
65
- styles.useVariants({ color: iconColor });
66
- const resolvedIconColor = iconColor ? styles.foundation.color : void 0;
67
- const backgroundColor = useAnimatedVariantColor(buttonStyles.root, "backgroundColor");
68
- const borderColor = useAnimatedVariantColor(buttonStyles.root, "borderColor");
73
+ }) ?? buttonStyles;
74
+ const resolvedFoundationStyles = styles.useVariants({ color: iconColor }) ?? styles;
75
+ const resolvedIconColor = iconColor ? resolvedFoundationStyles.foundation.color : void 0;
76
+ const backgroundColor = useAnimatedVariantColor(resolvedButtonStyles.root, "backgroundColor");
77
+ const borderColor = useAnimatedVariantColor(resolvedButtonStyles.root, "borderColor");
69
78
  const animatedTheme = useAnimatedTheme();
70
79
  const scale = useSharedValue(SCALE_EFFECTS.none);
71
80
  const handlePressIn = useCallback((event) => {
@@ -98,14 +107,16 @@ const IconButton = memo(function IconButton({ name, variant = "primary", size =
98
107
  const shadowPressed = animatedTheme.value.components[`button/variant/${variant}/root/pressed`]?.boxShadow;
99
108
  return {
100
109
  transform: [{ scale: scale.value }],
101
- backgroundColor: withTiming(backgroundColor.value, {
102
- duration: 220,
103
- easing: Easing.bezier(0, 0, .2, 1)
104
- }),
105
- borderColor: withTiming(borderColor.value, {
106
- duration: 220,
107
- easing: Easing.bezier(0, 0, .2, 1)
108
- }),
110
+ ...shouldAnimateVariantColors && {
111
+ backgroundColor: withTiming(backgroundColor.value, {
112
+ duration: 220,
113
+ easing: Easing.bezier(0, 0, .2, 1)
114
+ }),
115
+ borderColor: withTiming(borderColor.value, {
116
+ duration: 220,
117
+ easing: Easing.bezier(0, 0, .2, 1)
118
+ })
119
+ },
109
120
  ...shadowPressed && { boxShadow: interpolateShadowAlpha(shadowPressed, pressProgress.value) }
110
121
  };
111
122
  });
@@ -123,20 +134,21 @@ const IconButton = memo(function IconButton({ name, variant = "primary", size =
123
134
  accessibilityRole: "button",
124
135
  accessibilityState: a11yState,
125
136
  style: [
126
- iconButtonStyles.root,
127
- buttonStyles.root,
128
- styles.foundation,
137
+ resolvedIconButtonStyles.root,
138
+ resolvedButtonStyles.root,
139
+ matchedControlDimensions,
140
+ resolvedFoundationStyles.foundation,
129
141
  animatedRootStyle,
130
142
  typeof style === "function" ? style({ pressed }) : style
131
143
  ],
132
144
  ...props,
133
145
  children: loading ? /* @__PURE__ */ jsx(ActivityIndicator, {
134
- size: iconButtonStyles.icon.fontSize,
135
- color: resolvedIconColor ?? buttonStyles.icon.color
146
+ size: resolvedIconButtonStyles.icon.fontSize,
147
+ color: resolvedIconColor ?? resolvedButtonStyles.icon.color
136
148
  }) : /* @__PURE__ */ jsx(Icon, {
137
149
  name,
138
150
  variant: iconVariant,
139
- style: [iconButtonStyles.icon, buttonStyles.icon],
151
+ style: [resolvedIconButtonStyles.icon, resolvedButtonStyles.icon],
140
152
  dangerouslySetColor: resolvedIconColor
141
153
  })
142
154
  });
@@ -1 +1 @@
1
- {"version":3,"file":"IconButton.js","names":["foundationStyles"],"sources":["../../src/components/IconButton.tsx"],"sourcesContent":["import type { ButtonVariantFlat, IconButtonSize, IconVariant } from '@yahoo/uds-types';\nimport type { Ref } from 'react';\nimport { memo, useCallback, useMemo, useState } from 'react';\nimport type { View } from 'react-native';\nimport { ActivityIndicator } from 'react-native';\nimport {\n Easing,\n useAnimatedStyle,\n useDerivedValue,\n useSharedValue,\n withSpring,\n withTiming,\n} from 'react-native-reanimated';\nimport { useAnimatedTheme, useAnimatedVariantColor } from 'react-native-unistyles/reanimated';\n\nimport type { StyleProps } from '../../generated/styles';\nimport { buttonStyles, iconButtonStyles, styles as foundationStyles } from '../../generated/styles';\nimport { BUTTON_SPRING_CONFIG, SCALE_EFFECTS } from '../motion';\nimport type { IconName } from './Icon';\nimport { Icon } from './Icon';\nimport type { PressableProps } from './Pressable';\nimport { AnimatedPressable } from './Pressable';\n\n/* -------------------------------------------------------------------------- */\n/* Animation Helpers */\n/* -------------------------------------------------------------------------- */\n\nfunction interpolateShadowAlpha(shadow: string | undefined, alpha: number): string {\n 'worklet';\n if (!shadow) {\n return '';\n }\n if (alpha >= 1) {\n return shadow;\n }\n if (alpha <= 0) {\n return '';\n }\n\n return shadow.replace(/rgba\\(([^,]+),\\s*([^,]+),\\s*([^,]+),\\s*([^)]+)\\)/g, (_, r, g, b, a) => {\n const newAlpha = parseFloat(a) * alpha;\n return `rgba(${r}, ${g}, ${b}, ${newAlpha.toFixed(3)})`;\n });\n}\n\n/* -------------------------------------------------------------------------- */\n/* IconButton Props */\n/* -------------------------------------------------------------------------- */\n\ninterface IconButtonProps extends Omit<PressableProps, 'children'> {\n /** Icon to render from the icons package */\n name: IconName;\n /** The visual style variant @default 'primary' */\n variant?: ButtonVariantFlat;\n /** The size of the button @default 'md' */\n size?: IconButtonSize;\n /** The icon style variant @default 'outline' */\n iconVariant?: IconVariant;\n /** Override the icon color token without changing the button variant tokens */\n iconColor?: StyleProps['color'];\n /** Shows a loading spinner and disables the button */\n loading?: boolean;\n /**\n * Disable motion effects (scale on press, icon animations)\n * @default false\n */\n disableEffects?: boolean;\n /** Ref to the underlying View */\n ref?: Ref<View>;\n}\n\n/* -------------------------------------------------------------------------- */\n/* IconButton Component */\n/* -------------------------------------------------------------------------- */\n\n/**\n * **An icon button element that can be used to trigger an action**\n *\n * @description\n * An icon-only button for actions where space is limited. Features animated\n * scale effect on press and smooth color transitions matching the web UDS\n * IconButton behavior.\n *\n * @category Interactive\n * @platform mobile\n *\n * @example\n * ```tsx\n * import { IconButton } from '@yahoo/uds-mobile/IconButton';\n *\n * <IconButton name=\"Add\" onPress={() => console.log('pressed')} />\n * <IconButton name=\"Close\" variant=\"secondary\" size=\"sm\" />\n * <IconButton name=\"Settings\" loading />\n * ```\n *\n * @usage\n * - Use for toolbar actions\n * - Use for closing modals/dialogs\n * - Always provide accessibilityLabel for screen readers\n *\n * @accessibility\n * - Sets `accessibilityRole=\"button\"` automatically\n * - Announces loading state to screen readers\n * - **Always** provide `accessibilityLabel` since there's no visible text\n *\n * @see {@link Button} for buttons with text labels\n * @see {@link Icon} for non-interactive icons\n */\nconst IconButton = memo(function IconButton({\n name,\n variant = 'primary',\n size = 'md',\n iconVariant = 'outline',\n iconColor,\n loading,\n disabled,\n style,\n accessibilityLabel,\n accessibilityHint,\n disableEffects = false,\n onPressIn,\n onPressOut,\n ref,\n ...props\n}: IconButtonProps) {\n const isDisabled = disabled || loading;\n const shouldAnimate = !disableEffects && !isDisabled;\n\n /* --------------------------------- State ---------------------------------- */\n const [pressed, setPressed] = useState(false);\n\n // Apply layer-based styles with compound variant support\n iconButtonStyles.useVariants({ size });\n buttonStyles.useVariants({ variant, disabled: isDisabled, pressed });\n foundationStyles.useVariants({ color: iconColor });\n\n const resolvedIconColor = iconColor\n ? (foundationStyles.foundation.color as string | undefined)\n : undefined;\n\n // Animate colors using Unistyles' useAnimatedVariantColor\n const backgroundColor = useAnimatedVariantColor(buttonStyles.root, 'backgroundColor');\n const borderColor = useAnimatedVariantColor(buttonStyles.root, 'borderColor');\n\n // Get animated theme for boxShadow\n const animatedTheme = useAnimatedTheme();\n\n /* ------------------------------- Animation -------------------------------- */\n const scale = useSharedValue<number>(SCALE_EFFECTS.none);\n\n const handlePressIn = useCallback<NonNullable<PressableProps['onPressIn']>>(\n (event) => {\n setPressed(true);\n if (shouldAnimate) {\n scale.value = withSpring(SCALE_EFFECTS.down, BUTTON_SPRING_CONFIG);\n }\n onPressIn?.(event);\n },\n [shouldAnimate, scale, onPressIn],\n );\n\n const handlePressOut = useCallback<NonNullable<PressableProps['onPressOut']>>(\n (event) => {\n setPressed(false);\n if (shouldAnimate) {\n scale.value = withSpring(SCALE_EFFECTS.none, BUTTON_SPRING_CONFIG);\n }\n onPressOut?.(event);\n },\n [shouldAnimate, scale, onPressOut],\n );\n\n const a11yState = useMemo(() => ({ disabled: isDisabled, busy: loading }), [isDisabled, loading]);\n\n /* --------------------------------- Styles --------------------------------- */\n // Animate pressed state for shadow\n const pressProgress = useDerivedValue(\n () => withTiming(pressed ? 1 : 0, { duration: 220, easing: Easing.bezier(0, 0, 0.2, 1) }),\n [pressed],\n );\n\n // Animate using Unistyles' variant color system + boxShadow from theme\n const animatedRootStyle = useAnimatedStyle(() => {\n // Get boxShadow from theme using flattened path (no camelCase conversion needed!)\n const components = animatedTheme.value.components as unknown as Record<\n string,\n Record<string, unknown>\n >;\n const shadowPressed = components[`button/variant/${variant}/root/pressed`]?.boxShadow as\n | string\n | undefined;\n\n return {\n transform: [{ scale: scale.value }],\n backgroundColor: withTiming(backgroundColor.value, {\n duration: 220,\n easing: Easing.bezier(0, 0, 0.2, 1),\n }),\n borderColor: withTiming(borderColor.value, {\n duration: 220,\n easing: Easing.bezier(0, 0, 0.2, 1),\n }),\n // Only animate shadow if the theme defines one for this variant\n ...(shadowPressed && {\n boxShadow: interpolateShadowAlpha(shadowPressed, pressProgress.value),\n }),\n };\n });\n\n /* --------------------------------- Render --------------------------------- */\n return (\n <AnimatedPressable\n ref={ref}\n disabled={isDisabled}\n onPressIn={handlePressIn}\n onPressOut={handlePressOut}\n flexDirection=\"row\"\n alignItems=\"center\"\n justifyContent=\"center\"\n overflow=\"hidden\"\n accessibilityLabel={loading ? `${accessibilityLabel ?? ''}, loading` : accessibilityLabel}\n accessibilityHint={accessibilityHint}\n accessibilityRole=\"button\"\n accessibilityState={a11yState}\n style={[\n iconButtonStyles.root,\n buttonStyles.root,\n foundationStyles.foundation,\n animatedRootStyle,\n typeof style === 'function' ? style({ pressed }) : style,\n ]}\n {...props}\n >\n {loading ? (\n <ActivityIndicator\n size={iconButtonStyles.icon.fontSize}\n color={resolvedIconColor ?? buttonStyles.icon.color}\n />\n ) : (\n <Icon\n name={name}\n variant={iconVariant}\n style={[iconButtonStyles.icon, buttonStyles.icon]}\n dangerouslySetColor={resolvedIconColor}\n />\n )}\n </AnimatedPressable>\n );\n});\n\nIconButton.displayName = 'IconButton';\n\nexport { IconButton, type IconButtonProps };\n"],"mappings":"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"}
1
+ {"version":3,"file":"IconButton.js","names":["foundationStyles"],"sources":["../../src/components/IconButton.tsx"],"sourcesContent":["import type { ButtonVariantFlat, IconButtonSize, IconVariant } from '@yahoo/uds-types';\nimport type { Ref } from 'react';\nimport { memo, useCallback, useMemo, useState } from 'react';\nimport type { View } from 'react-native';\nimport { ActivityIndicator, Platform } from 'react-native';\nimport {\n Easing,\n useAnimatedStyle,\n useDerivedValue,\n useSharedValue,\n withSpring,\n withTiming,\n} from 'react-native-reanimated';\n// eslint-disable-next-line uds/no-use-unistyles -- iconbutton control height from theme size layers\nimport { useUnistyles } from 'react-native-unistyles';\nimport { useAnimatedTheme, useAnimatedVariantColor } from 'react-native-unistyles/reanimated';\n\nimport type { StyleProps } from '../../generated/styles';\nimport { buttonStyles, iconButtonStyles, styles as foundationStyles } from '../../generated/styles';\nimport { BUTTON_SPRING_CONFIG, SCALE_EFFECTS } from '../motion';\nimport { getIconButtonControlMetrics } from './Button/buttonTheme';\nimport type { IconName } from './Icon';\nimport { Icon } from './Icon';\nimport type { PressableProps } from './Pressable';\nimport { AnimatedPressable } from './Pressable';\n\n/* -------------------------------------------------------------------------- */\n/* Animation Helpers */\n/* -------------------------------------------------------------------------- */\n\nfunction interpolateShadowAlpha(shadow: string | undefined, alpha: number): string {\n 'worklet';\n if (!shadow) {\n return '';\n }\n if (alpha >= 1) {\n return shadow;\n }\n if (alpha <= 0) {\n return '';\n }\n\n return shadow.replace(/rgba\\(([^,]+),\\s*([^,]+),\\s*([^,]+),\\s*([^)]+)\\)/g, (_, r, g, b, a) => {\n const newAlpha = parseFloat(a) * alpha;\n return `rgba(${r}, ${g}, ${b}, ${newAlpha.toFixed(3)})`;\n });\n}\n\n/* -------------------------------------------------------------------------- */\n/* IconButton Props */\n/* -------------------------------------------------------------------------- */\n\ninterface IconButtonProps extends Omit<PressableProps, 'children'> {\n /** Icon to render from the icons package */\n name: IconName;\n /** The visual style variant @default 'primary' */\n variant?: ButtonVariantFlat;\n /** The size of the button @default 'md' */\n size?: IconButtonSize;\n /** The icon style variant @default 'outline' */\n iconVariant?: IconVariant;\n /** Override the icon color token without changing the button variant tokens */\n iconColor?: StyleProps['color'];\n /** Shows a loading spinner and disables the button */\n loading?: boolean;\n /**\n * Disable motion effects (scale on press, icon animations)\n * @default false\n */\n disableEffects?: boolean;\n /** Ref to the underlying View */\n ref?: Ref<View>;\n}\n\n/* -------------------------------------------------------------------------- */\n/* IconButton Component */\n/* -------------------------------------------------------------------------- */\n\n/**\n * **An icon button element that can be used to trigger an action**\n *\n * @description\n * An icon-only button for actions where space is limited. Features animated\n * scale effect on press and smooth color transitions matching the web UDS\n * IconButton behavior.\n *\n * @category Interactive\n * @platform mobile\n *\n * @example\n * ```tsx\n * import { IconButton } from '@yahoo/uds-mobile/IconButton';\n *\n * <IconButton name=\"Add\" onPress={() => console.log('pressed')} />\n * <IconButton name=\"Close\" variant=\"secondary\" size=\"sm\" />\n * <IconButton name=\"Settings\" loading />\n * ```\n *\n * @usage\n * - Use for toolbar actions\n * - Use for closing modals/dialogs\n * - Always provide accessibilityLabel for screen readers\n *\n * @accessibility\n * - Sets `accessibilityRole=\"button\"` automatically\n * - Announces loading state to screen readers\n * - **Always** provide `accessibilityLabel` since there's no visible text\n *\n * @see {@link Button} for buttons with text labels\n * @see {@link Icon} for non-interactive icons\n */\nconst IconButton = memo(function IconButton({\n name,\n variant = 'primary',\n size = 'md',\n iconVariant = 'outline',\n iconColor,\n loading,\n disabled,\n style,\n accessibilityLabel,\n accessibilityHint,\n disableEffects = false,\n onPressIn,\n onPressOut,\n ref,\n ...props\n}: IconButtonProps) {\n const isDisabled = disabled || loading;\n const shouldAnimate = !disableEffects && !isDisabled;\n const shouldAnimateVariantColors = Platform.OS !== 'web';\n\n const { theme } = useUnistyles();\n const { controlHeight } = useMemo(() => getIconButtonControlMetrics(theme, size), [theme, size]);\n const matchedControlDimensions =\n controlHeight > 0 ? ({ height: controlHeight, width: controlHeight } as const) : undefined;\n\n /* --------------------------------- State ---------------------------------- */\n const [pressed, setPressed] = useState(false);\n\n // On web, useVariants returns the resolved style object instead of mutating\n // the generated styles in place.\n const variantIconButtonStyles = iconButtonStyles.useVariants({ size }) as unknown as\n | typeof iconButtonStyles\n | undefined;\n const resolvedIconButtonStyles = variantIconButtonStyles ?? iconButtonStyles;\n\n const variantButtonStyles = buttonStyles.useVariants({\n variant,\n disabled: isDisabled,\n pressed,\n }) as unknown as typeof buttonStyles | undefined;\n const resolvedButtonStyles = variantButtonStyles ?? buttonStyles;\n\n const variantFoundationStyles = foundationStyles.useVariants({ color: iconColor }) as unknown as\n | typeof foundationStyles\n | undefined;\n const resolvedFoundationStyles = variantFoundationStyles ?? foundationStyles;\n\n const resolvedIconColor = iconColor\n ? (resolvedFoundationStyles.foundation.color as string | undefined)\n : undefined;\n\n // Animate colors using Unistyles' useAnimatedVariantColor\n const backgroundColor = useAnimatedVariantColor(resolvedButtonStyles.root, 'backgroundColor');\n const borderColor = useAnimatedVariantColor(resolvedButtonStyles.root, 'borderColor');\n\n // Get animated theme for boxShadow\n const animatedTheme = useAnimatedTheme();\n\n /* ------------------------------- Animation -------------------------------- */\n const scale = useSharedValue<number>(SCALE_EFFECTS.none);\n\n const handlePressIn = useCallback<NonNullable<PressableProps['onPressIn']>>(\n (event) => {\n setPressed(true);\n if (shouldAnimate) {\n scale.value = withSpring(SCALE_EFFECTS.down, BUTTON_SPRING_CONFIG);\n }\n onPressIn?.(event);\n },\n [shouldAnimate, scale, onPressIn],\n );\n\n const handlePressOut = useCallback<NonNullable<PressableProps['onPressOut']>>(\n (event) => {\n setPressed(false);\n if (shouldAnimate) {\n scale.value = withSpring(SCALE_EFFECTS.none, BUTTON_SPRING_CONFIG);\n }\n onPressOut?.(event);\n },\n [shouldAnimate, scale, onPressOut],\n );\n\n const a11yState = useMemo(() => ({ disabled: isDisabled, busy: loading }), [isDisabled, loading]);\n\n /* --------------------------------- Styles --------------------------------- */\n // Animate pressed state for shadow\n const pressProgress = useDerivedValue(\n () => withTiming(pressed ? 1 : 0, { duration: 220, easing: Easing.bezier(0, 0, 0.2, 1) }),\n [pressed],\n );\n\n // Animate using Unistyles' variant color system + boxShadow from theme\n const animatedRootStyle = useAnimatedStyle(() => {\n // Get boxShadow from theme using flattened path (no camelCase conversion needed!)\n const components = animatedTheme.value.components as unknown as Record<\n string,\n Record<string, unknown>\n >;\n const shadowPressed = components[`button/variant/${variant}/root/pressed`]?.boxShadow as\n | string\n | undefined;\n\n return {\n transform: [{ scale: scale.value }],\n // On web, Unistyles already emits the resolved variant color class. The\n // animated variant color hook can resolve to the black fallback there.\n ...(shouldAnimateVariantColors && {\n backgroundColor: withTiming(backgroundColor.value, {\n duration: 220,\n easing: Easing.bezier(0, 0, 0.2, 1),\n }),\n borderColor: withTiming(borderColor.value, {\n duration: 220,\n easing: Easing.bezier(0, 0, 0.2, 1),\n }),\n }),\n // Only animate shadow if the theme defines one for this variant\n ...(shadowPressed && {\n boxShadow: interpolateShadowAlpha(shadowPressed, pressProgress.value),\n }),\n };\n });\n\n /* --------------------------------- Render --------------------------------- */\n return (\n <AnimatedPressable\n ref={ref}\n disabled={isDisabled}\n onPressIn={handlePressIn}\n onPressOut={handlePressOut}\n flexDirection=\"row\"\n alignItems=\"center\"\n justifyContent=\"center\"\n overflow=\"hidden\"\n accessibilityLabel={loading ? `${accessibilityLabel ?? ''}, loading` : accessibilityLabel}\n accessibilityHint={accessibilityHint}\n accessibilityRole=\"button\"\n accessibilityState={a11yState}\n style={[\n resolvedIconButtonStyles.root,\n resolvedButtonStyles.root,\n matchedControlDimensions,\n resolvedFoundationStyles.foundation,\n animatedRootStyle,\n typeof style === 'function' ? style({ pressed }) : style,\n ]}\n {...props}\n >\n {loading ? (\n <ActivityIndicator\n size={resolvedIconButtonStyles.icon.fontSize}\n color={resolvedIconColor ?? resolvedButtonStyles.icon.color}\n />\n ) : (\n <Icon\n name={name}\n variant={iconVariant}\n style={[resolvedIconButtonStyles.icon, resolvedButtonStyles.icon]}\n dangerouslySetColor={resolvedIconColor}\n />\n )}\n </AnimatedPressable>\n );\n});\n\nIconButton.displayName = 'IconButton';\n\nexport { IconButton, type IconButtonProps };\n"],"mappings":"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"}
@@ -55,6 +55,8 @@ const Switch = (0, react.memo)(function Switch({ isOn: isOnProp, defaultIsOn = f
55
55
  const [internalIsOn, setInternalIsOn] = (0, react.useState)(defaultIsOn);
56
56
  const [prefersReducedMotion, setPrefersReducedMotion] = (0, react.useState)(false);
57
57
  const isOn = isControlled ? isOnProp : internalIsOn;
58
+ const activeVariant = isOn ? "on" : "off";
59
+ const { theme } = (0, react_native_unistyles.useUnistyles)();
58
60
  (0, react.useEffect)(() => {
59
61
  const checkReducedMotion = async () => {
60
62
  setPrefersReducedMotion(await react_native.AccessibilityInfo.isReduceMotionEnabled());
@@ -77,11 +79,21 @@ const Switch = (0, react.memo)(function Switch({ isOn: isOnProp, defaultIsOn = f
77
79
  isControlled,
78
80
  onChange
79
81
  ]);
80
- generated_styles.switchStyles.useVariants({
82
+ const resolvedSwitchStyles = generated_styles.switchStyles.useVariants({
81
83
  size,
82
- variant: isOn ? "on" : "off"
83
- });
84
- const trackBackgroundColor = (0, react_native_unistyles_reanimated.useAnimatedVariantColor)(generated_styles.switchStyles.switch, "backgroundColor");
84
+ variant: activeVariant
85
+ }) ?? generated_styles.switchStyles;
86
+ const variantLayerStyles = (0, react.useMemo)(() => {
87
+ const components = theme.components;
88
+ const getLayerStyle = (layer) => components[`switch/variant/default/active/${activeVariant}/${layer}/rest`];
89
+ return {
90
+ handle: getLayerStyle("handle"),
91
+ handleIcon: getLayerStyle("handleIcon"),
92
+ switch: getLayerStyle("switch"),
93
+ text: getLayerStyle("rootText")
94
+ };
95
+ }, [activeVariant, theme]);
96
+ const trackBackgroundColor = (0, react_native_unistyles_reanimated.useAnimatedVariantColor)(resolvedSwitchStyles.switch, "backgroundColor");
85
97
  const animatedTrackStyle = (0, react_native_reanimated.useAnimatedStyle)(() => {
86
98
  "worklet";
87
99
  return { backgroundColor: (0, react_native_reanimated.withTiming)(trackBackgroundColor.value, { duration: animationDuration }) };
@@ -90,17 +102,27 @@ const Switch = (0, react.memo)(function Switch({ isOn: isOnProp, defaultIsOn = f
90
102
  "worklet";
91
103
  return { transform: [{ translateX: progress.value * travelDistance }] };
92
104
  });
93
- const rootStyle = (0, react.useMemo)(() => [generated_styles.switchStyles.root, switchStaticStyles.root({ disabled })], [generated_styles.switchStyles.root, disabled]);
105
+ const rootStyle = (0, react.useMemo)(() => [resolvedSwitchStyles.root, switchStaticStyles.root({ disabled })], [resolvedSwitchStyles.root, disabled]);
94
106
  const trackStyle = (0, react.useMemo)(() => [
95
- generated_styles.switchStyles.switch,
107
+ resolvedSwitchStyles.switch,
96
108
  switchStaticStyles.track,
109
+ variantLayerStyles.switch,
110
+ react_native.Platform.OS !== "web" && animatedTrackStyle
111
+ ], [
112
+ resolvedSwitchStyles.switch,
113
+ variantLayerStyles.switch,
97
114
  animatedTrackStyle
98
- ], [generated_styles.switchStyles.switch, animatedTrackStyle]);
115
+ ]);
99
116
  const handleStyle = (0, react.useMemo)(() => [
100
- generated_styles.switchStyles.handle,
117
+ resolvedSwitchStyles.handle,
101
118
  switchStaticStyles.handle,
119
+ variantLayerStyles.handle,
102
120
  animatedHandleStyle
103
- ], [generated_styles.switchStyles.handle, animatedHandleStyle]);
121
+ ], [
122
+ resolvedSwitchStyles.handle,
123
+ variantLayerStyles.handle,
124
+ animatedHandleStyle
125
+ ]);
104
126
  const accessibilityLabel = typeof label === "string" ? label : void 0;
105
127
  const resolvedAccessibilityHint = accessibilityHint ?? "Double tap to toggle";
106
128
  const labelContent = label && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_components_FormLabel.FormLabel, {
@@ -109,7 +131,7 @@ const Switch = (0, react.memo)(function Switch({ isOn: isOnProp, defaultIsOn = f
109
131
  label,
110
132
  required,
111
133
  showRequiredAsterisk: required,
112
- style: generated_styles.switchStyles.text
134
+ style: [resolvedSwitchStyles.text, variantLayerStyles.text]
113
135
  });
114
136
  const a11yValue = (0, react.useMemo)(() => ({ text: isOn ? "On" : "Off" }), [isOn]);
115
137
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native.Pressable, {
@@ -139,14 +161,14 @@ const Switch = (0, react.memo)(function Switch({ isOn: isOnProp, defaultIsOn = f
139
161
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_components_IconSlot.IconSlot, {
140
162
  icon: onIcon,
141
163
  variant: "fill",
142
- style: generated_styles.switchStyles.handleIcon
164
+ style: [resolvedSwitchStyles.handleIcon, variantLayerStyles.handleIcon]
143
165
  })
144
166
  }), offIcon && !isOn && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native_reanimated.default.View, {
145
167
  style: switchStaticStyles.iconContainer,
146
168
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_components_IconSlot.IconSlot, {
147
169
  icon: offIcon,
148
170
  variant: "fill",
149
- style: generated_styles.switchStyles.handleIcon
171
+ style: [resolvedSwitchStyles.handleIcon, variantLayerStyles.handleIcon]
150
172
  })
151
173
  })]
152
174
  })
@@ -1 +1 @@
1
- {"version":3,"file":"Switch.d.cts","names":[],"sources":["../../src/components/Switch.tsx"],"mappings":";;;;;;;;UAcU,WAAA,SAAoB,IAAA,CAAK,SAAA,YAAqB,oBAAA,CAAqB,YAAA;;EAE3E,GAAA,GAAM,GAAA,CAAI,IAAA;EAFF;EAIR,QAAA,IAAY,KAAA;;EAEZ,QAAA;EAN2E;EAQ3E,QAAA;EANM;EAQN,iBAAA,GAAoB,kBAAA;AAAA;;;;;;;;;;;;;;;;;;;AAAkB;;;;;;;;;;;;;;;cA2ClC,MAAA,EAAM,OAAA,CAAA,oBAAA,CAAA,WAAA"}
1
+ {"version":3,"file":"Switch.d.cts","names":[],"sources":["../../src/components/Switch.tsx"],"mappings":";;;;;;;;UAsBU,WAAA,SAAoB,IAAA,CAAK,SAAA,YAAqB,oBAAA,CAAqB,YAAA;;EAE3E,GAAA,GAAM,GAAA,CAAI,IAAA;EAFF;EAIR,QAAA,IAAY,KAAA;;EAEZ,QAAA;EAN2E;EAQ3E,QAAA;EANM;EAQN,iBAAA,GAAoB,kBAAA;AAAA;;;;;;;;;;;;;;;;;;;AAAkB;;;;;;;;;;;;;;;cA2ClC,MAAA,EAAM,OAAA,CAAA,oBAAA,CAAA,WAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"Switch.d.ts","names":[],"sources":["../../src/components/Switch.tsx"],"mappings":";;;;;;;;UAcU,WAAA,SAAoB,IAAA,CAAK,SAAA,YAAqB,oBAAA,CAAqB,YAAA;;EAE3E,GAAA,GAAM,GAAA,CAAI,IAAA;EAFF;EAIR,QAAA,IAAY,KAAA;;EAEZ,QAAA;EAN2E;EAQ3E,QAAA;EANM;EAQN,iBAAA,GAAoB,kBAAA;AAAA;;;;;;;;;;;;;;;;;;;AAAkB;;;;;;;;;;;;;;;cA2ClC,MAAA,EAAM,OAAA,CAAA,oBAAA,CAAA,WAAA"}
1
+ {"version":3,"file":"Switch.d.ts","names":[],"sources":["../../src/components/Switch.tsx"],"mappings":";;;;;;;;UAsBU,WAAA,SAAoB,IAAA,CAAK,SAAA,YAAqB,oBAAA,CAAqB,YAAA;;EAE3E,GAAA,GAAM,GAAA,CAAI,IAAA;EAFF;EAIR,QAAA,IAAY,KAAA;;EAEZ,QAAA;EAN2E;EAQ3E,QAAA;EANM;EAQN,iBAAA,GAAoB,kBAAA;AAAA;;;;;;;;;;;;;;;;;;;AAAkB;;;;;;;;;;;;;;;cA2ClC,MAAA,EAAM,OAAA,CAAA,oBAAA,CAAA,WAAA"}
@@ -2,10 +2,10 @@
2
2
  import { IconSlot } from "./IconSlot.js";
3
3
  import { FormLabel } from "./FormLabel.js";
4
4
  import { memo, useCallback, useEffect, useMemo, useState } from "react";
5
- import { AccessibilityInfo, Pressable } from "react-native";
5
+ import { AccessibilityInfo, Platform, Pressable } from "react-native";
6
6
  import { jsx, jsxs } from "react/jsx-runtime";
7
7
  import { switchStyles } from "../../generated/styles";
8
- import { StyleSheet as StyleSheet$1 } from "react-native-unistyles";
8
+ import { StyleSheet as StyleSheet$1, useUnistyles } from "react-native-unistyles";
9
9
  import Animated, { useAnimatedStyle, useDerivedValue, withTiming } from "react-native-reanimated";
10
10
  import { useAnimatedVariantColor } from "react-native-unistyles/reanimated";
11
11
  //#region src/components/Switch.tsx
@@ -52,6 +52,8 @@ const Switch = memo(function Switch({ isOn: isOnProp, defaultIsOn = false, onCha
52
52
  const [internalIsOn, setInternalIsOn] = useState(defaultIsOn);
53
53
  const [prefersReducedMotion, setPrefersReducedMotion] = useState(false);
54
54
  const isOn = isControlled ? isOnProp : internalIsOn;
55
+ const activeVariant = isOn ? "on" : "off";
56
+ const { theme } = useUnistyles();
55
57
  useEffect(() => {
56
58
  const checkReducedMotion = async () => {
57
59
  setPrefersReducedMotion(await AccessibilityInfo.isReduceMotionEnabled());
@@ -74,11 +76,21 @@ const Switch = memo(function Switch({ isOn: isOnProp, defaultIsOn = false, onCha
74
76
  isControlled,
75
77
  onChange
76
78
  ]);
77
- switchStyles.useVariants({
79
+ const resolvedSwitchStyles = switchStyles.useVariants({
78
80
  size,
79
- variant: isOn ? "on" : "off"
80
- });
81
- const trackBackgroundColor = useAnimatedVariantColor(switchStyles.switch, "backgroundColor");
81
+ variant: activeVariant
82
+ }) ?? switchStyles;
83
+ const variantLayerStyles = useMemo(() => {
84
+ const components = theme.components;
85
+ const getLayerStyle = (layer) => components[`switch/variant/default/active/${activeVariant}/${layer}/rest`];
86
+ return {
87
+ handle: getLayerStyle("handle"),
88
+ handleIcon: getLayerStyle("handleIcon"),
89
+ switch: getLayerStyle("switch"),
90
+ text: getLayerStyle("rootText")
91
+ };
92
+ }, [activeVariant, theme]);
93
+ const trackBackgroundColor = useAnimatedVariantColor(resolvedSwitchStyles.switch, "backgroundColor");
82
94
  const animatedTrackStyle = useAnimatedStyle(() => {
83
95
  "worklet";
84
96
  return { backgroundColor: withTiming(trackBackgroundColor.value, { duration: animationDuration }) };
@@ -87,17 +99,27 @@ const Switch = memo(function Switch({ isOn: isOnProp, defaultIsOn = false, onCha
87
99
  "worklet";
88
100
  return { transform: [{ translateX: progress.value * travelDistance }] };
89
101
  });
90
- const rootStyle = useMemo(() => [switchStyles.root, switchStaticStyles.root({ disabled })], [switchStyles.root, disabled]);
102
+ const rootStyle = useMemo(() => [resolvedSwitchStyles.root, switchStaticStyles.root({ disabled })], [resolvedSwitchStyles.root, disabled]);
91
103
  const trackStyle = useMemo(() => [
92
- switchStyles.switch,
104
+ resolvedSwitchStyles.switch,
93
105
  switchStaticStyles.track,
106
+ variantLayerStyles.switch,
107
+ Platform.OS !== "web" && animatedTrackStyle
108
+ ], [
109
+ resolvedSwitchStyles.switch,
110
+ variantLayerStyles.switch,
94
111
  animatedTrackStyle
95
- ], [switchStyles.switch, animatedTrackStyle]);
112
+ ]);
96
113
  const handleStyle = useMemo(() => [
97
- switchStyles.handle,
114
+ resolvedSwitchStyles.handle,
98
115
  switchStaticStyles.handle,
116
+ variantLayerStyles.handle,
99
117
  animatedHandleStyle
100
- ], [switchStyles.handle, animatedHandleStyle]);
118
+ ], [
119
+ resolvedSwitchStyles.handle,
120
+ variantLayerStyles.handle,
121
+ animatedHandleStyle
122
+ ]);
101
123
  const accessibilityLabel = typeof label === "string" ? label : void 0;
102
124
  const resolvedAccessibilityHint = accessibilityHint ?? "Double tap to toggle";
103
125
  const labelContent = label && /* @__PURE__ */ jsx(FormLabel, {
@@ -106,7 +128,7 @@ const Switch = memo(function Switch({ isOn: isOnProp, defaultIsOn = false, onCha
106
128
  label,
107
129
  required,
108
130
  showRequiredAsterisk: required,
109
- style: switchStyles.text
131
+ style: [resolvedSwitchStyles.text, variantLayerStyles.text]
110
132
  });
111
133
  const a11yValue = useMemo(() => ({ text: isOn ? "On" : "Off" }), [isOn]);
112
134
  return /* @__PURE__ */ jsxs(Pressable, {
@@ -136,14 +158,14 @@ const Switch = memo(function Switch({ isOn: isOnProp, defaultIsOn = false, onCha
136
158
  children: /* @__PURE__ */ jsx(IconSlot, {
137
159
  icon: onIcon,
138
160
  variant: "fill",
139
- style: switchStyles.handleIcon
161
+ style: [resolvedSwitchStyles.handleIcon, variantLayerStyles.handleIcon]
140
162
  })
141
163
  }), offIcon && !isOn && /* @__PURE__ */ jsx(Animated.View, {
142
164
  style: switchStaticStyles.iconContainer,
143
165
  children: /* @__PURE__ */ jsx(IconSlot, {
144
166
  icon: offIcon,
145
167
  variant: "fill",
146
- style: switchStyles.handleIcon
168
+ style: [resolvedSwitchStyles.handleIcon, variantLayerStyles.handleIcon]
147
169
  })
148
170
  })]
149
171
  })
@@ -1 +1 @@
1
- {"version":3,"file":"Switch.js","names":["StyleSheet"],"sources":["../../src/components/Switch.tsx"],"sourcesContent":["import type { SwitchSize, UniversalSwitchProps } from '@yahoo/uds-types';\nimport type { Ref } from 'react';\nimport { memo, useCallback, useEffect, useMemo, useState } from 'react';\nimport type { AccessibilityProps, StyleProp, View, ViewProps, ViewStyle } from 'react-native';\nimport { AccessibilityInfo, Pressable } from 'react-native';\nimport Animated, { useAnimatedStyle, useDerivedValue, withTiming } from 'react-native-reanimated';\nimport { StyleSheet } from 'react-native-unistyles';\nimport { useAnimatedVariantColor } from 'react-native-unistyles/reanimated';\n\nimport { switchStyles } from '../../generated/styles';\nimport { FormLabel } from './FormLabel';\nimport type { IconSlotType } from './IconSlot';\nimport { IconSlot } from './IconSlot';\n\ninterface SwitchProps extends Omit<ViewProps, 'style'>, UniversalSwitchProps<IconSlotType> {\n /** Ref to the underlying View */\n ref?: Ref<View>;\n /** Callback when the switch value changes */\n onChange?: (value: boolean) => void;\n /** Whether the switch is disabled */\n disabled?: boolean;\n /** Whether the switch is required (shows asterisk with label) */\n required?: boolean;\n /** Accessibility hint describing what happens when activated */\n accessibilityHint?: AccessibilityProps['accessibilityHint'];\n}\n\nconst HANDLE_TRAVEL: Record<SwitchSize, number> = {\n md: 20,\n sm: 12,\n};\n\nconst ANIMATION_DURATION = 120;\n\n/**\n * **Switch component for toggling options**\n *\n * @description\n * A switch (also called a toggle) is a binary on/off input control.\n * It allows users to pick between two clearly opposite choices.\n *\n * @category Form\n * @platform mobile\n *\n * @example\n * ```tsx\n * import { Switch } from '@yahoo/uds-mobile/Switch';\n *\n * <Switch label=\"Notifications\" />\n * <Switch isOn={enabled} onChange={setEnabled} label=\"Dark mode\" />\n * <Switch onIcon=\"Check\" offIcon=\"Cross\" label=\"Sync\" />\n * ```\n *\n * @usage\n * - Settings: For toggling preferences on/off\n * - Feature flags: For enabling/disabling features\n * - Immediate effect toggles (no submit button needed)\n *\n * @accessibility\n * - Sets `accessibilityRole=\"switch\"` automatically\n * - Announces on/off state to screen readers\n * - Respects system reduce motion preference\n * - Supports `reduceMotion` prop to disable animations\n *\n * @see {@link Checkbox} for forms with submit actions\n * @see {@link Radio} for single-select options\n */\nconst Switch = memo(function Switch({\n isOn: isOnProp,\n defaultIsOn = false,\n onChange,\n label,\n labelPosition = 'start',\n size = 'md',\n onIcon,\n offIcon,\n disabled = false,\n required,\n accessibilityHint,\n reduceMotion = false,\n ref,\n ...viewProps\n}: SwitchProps) {\n const isControlled = isOnProp !== undefined;\n const [internalIsOn, setInternalIsOn] = useState(defaultIsOn);\n const [prefersReducedMotion, setPrefersReducedMotion] = useState(false);\n const isOn = isControlled ? isOnProp : internalIsOn;\n\n // Check system reduced motion preference\n useEffect(() => {\n const checkReducedMotion = async () => {\n const isReduceMotionEnabled = await AccessibilityInfo.isReduceMotionEnabled();\n setPrefersReducedMotion(isReduceMotionEnabled);\n };\n checkReducedMotion();\n\n const subscription = AccessibilityInfo.addEventListener(\n 'reduceMotionChanged',\n setPrefersReducedMotion,\n );\n return () => subscription.remove();\n }, []);\n\n const shouldReduceMotion = reduceMotion || prefersReducedMotion;\n const animationDuration = shouldReduceMotion ? 0 : ANIMATION_DURATION;\n\n const progress = useDerivedValue(\n () => withTiming(isOn ? 1 : 0, { duration: animationDuration }),\n [isOn, animationDuration],\n );\n\n const travelDistance = HANDLE_TRAVEL[size];\n\n const handlePress = useCallback(() => {\n if (disabled) {\n return;\n }\n\n const newValue = !isOn;\n\n if (!isControlled) {\n setInternalIsOn(newValue);\n }\n\n onChange?.(newValue);\n }, [disabled, isOn, isControlled, onChange]);\n\n switchStyles.useVariants({\n size,\n variant: isOn ? 'on' : 'off',\n });\n\n // Get animated track color from design tokens (changes when variant changes)\n const trackBackgroundColor = useAnimatedVariantColor(switchStyles.switch, 'backgroundColor');\n\n const animatedTrackStyle = useAnimatedStyle(() => {\n 'worklet';\n return {\n backgroundColor: withTiming(trackBackgroundColor.value, { duration: animationDuration }),\n };\n });\n\n const animatedHandleStyle = useAnimatedStyle(() => {\n 'worklet';\n return {\n transform: [{ translateX: progress.value * travelDistance }],\n };\n });\n\n const rootStyle: StyleProp<ViewStyle> = useMemo(\n () => [switchStyles.root, switchStaticStyles.root({ disabled })],\n [switchStyles.root, disabled],\n );\n\n const trackStyle: StyleProp<ViewStyle> = useMemo(\n () => [switchStyles.switch, switchStaticStyles.track, animatedTrackStyle],\n [switchStyles.switch, animatedTrackStyle],\n );\n\n const handleStyle: StyleProp<ViewStyle> = useMemo(\n () => [switchStyles.handle, switchStaticStyles.handle, animatedHandleStyle],\n [switchStyles.handle, animatedHandleStyle],\n );\n\n const accessibilityLabel = typeof label === 'string' ? label : undefined;\n const resolvedAccessibilityHint = accessibilityHint ?? 'Double tap to toggle';\n\n const labelContent = label && (\n <FormLabel\n color=\"inherit\"\n variant=\"inherit\"\n label={label}\n required={required}\n showRequiredAsterisk={required}\n style={switchStyles.text}\n />\n );\n\n const a11yValue = useMemo(() => ({ text: isOn ? 'On' : 'Off' }), [isOn]);\n\n return (\n <Pressable\n ref={ref}\n onPress={handlePress}\n disabled={disabled}\n accessible\n accessibilityRole=\"switch\"\n accessibilityState={{ checked: isOn, disabled }}\n accessibilityLabel={accessibilityLabel}\n accessibilityHint={resolvedAccessibilityHint}\n accessibilityValue={a11yValue}\n {...viewProps}\n style={rootStyle}\n >\n {labelPosition === 'start' && labelContent}\n\n <Animated.View style={trackStyle} importantForAccessibility=\"no-hide-descendants\">\n <Animated.View style={handleStyle}>\n {onIcon && isOn && (\n <Animated.View style={switchStaticStyles.iconContainer}>\n <IconSlot icon={onIcon} variant=\"fill\" style={switchStyles.handleIcon} />\n </Animated.View>\n )}\n {offIcon && !isOn && (\n <Animated.View style={switchStaticStyles.iconContainer}>\n <IconSlot icon={offIcon} variant=\"fill\" style={switchStyles.handleIcon} />\n </Animated.View>\n )}\n </Animated.View>\n </Animated.View>\n\n {labelPosition === 'end' && labelContent}\n </Pressable>\n );\n});\n\nSwitch.displayName = 'Switch';\n\nconst switchStaticStyles = StyleSheet.create((theme) => ({\n handle: {\n borderRadius: theme.borderRadius.full,\n alignItems: 'center',\n justifyContent: 'center',\n },\n iconContainer: {\n position: 'absolute',\n alignItems: 'center',\n justifyContent: 'center',\n },\n track: {\n justifyContent: 'center',\n borderRadius: theme.borderRadius.full,\n },\n root: ({ disabled }: { disabled: boolean }) => ({\n flexDirection: 'row',\n alignItems: 'center',\n alignSelf: 'flex-start',\n opacity: disabled ? 0.5 : 1,\n }),\n}));\n\nexport { Switch, type SwitchProps };\n"],"mappings":"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"}
1
+ {"version":3,"file":"Switch.js","names":["StyleSheet"],"sources":["../../src/components/Switch.tsx"],"sourcesContent":["import type { SwitchSize, UniversalSwitchProps } from '@yahoo/uds-types';\nimport type { Ref } from 'react';\nimport { memo, useCallback, useEffect, useMemo, useState } from 'react';\nimport type {\n AccessibilityProps,\n StyleProp,\n TextStyle,\n View,\n ViewProps,\n ViewStyle,\n} from 'react-native';\nimport { AccessibilityInfo, Platform, Pressable } from 'react-native';\nimport Animated, { useAnimatedStyle, useDerivedValue, withTiming } from 'react-native-reanimated';\n// eslint-disable-next-line uds/no-use-unistyles -- switch variant layers need concrete web styles\nimport { StyleSheet, useUnistyles } from 'react-native-unistyles';\nimport { useAnimatedVariantColor } from 'react-native-unistyles/reanimated';\n\nimport { switchStyles } from '../../generated/styles';\nimport { FormLabel } from './FormLabel';\nimport type { IconSlotType } from './IconSlot';\nimport { IconSlot } from './IconSlot';\n\ninterface SwitchProps extends Omit<ViewProps, 'style'>, UniversalSwitchProps<IconSlotType> {\n /** Ref to the underlying View */\n ref?: Ref<View>;\n /** Callback when the switch value changes */\n onChange?: (value: boolean) => void;\n /** Whether the switch is disabled */\n disabled?: boolean;\n /** Whether the switch is required (shows asterisk with label) */\n required?: boolean;\n /** Accessibility hint describing what happens when activated */\n accessibilityHint?: AccessibilityProps['accessibilityHint'];\n}\n\nconst HANDLE_TRAVEL: Record<SwitchSize, number> = {\n md: 20,\n sm: 12,\n};\n\nconst ANIMATION_DURATION = 120;\n\n/**\n * **Switch component for toggling options**\n *\n * @description\n * A switch (also called a toggle) is a binary on/off input control.\n * It allows users to pick between two clearly opposite choices.\n *\n * @category Form\n * @platform mobile\n *\n * @example\n * ```tsx\n * import { Switch } from '@yahoo/uds-mobile/Switch';\n *\n * <Switch label=\"Notifications\" />\n * <Switch isOn={enabled} onChange={setEnabled} label=\"Dark mode\" />\n * <Switch onIcon=\"Check\" offIcon=\"Cross\" label=\"Sync\" />\n * ```\n *\n * @usage\n * - Settings: For toggling preferences on/off\n * - Feature flags: For enabling/disabling features\n * - Immediate effect toggles (no submit button needed)\n *\n * @accessibility\n * - Sets `accessibilityRole=\"switch\"` automatically\n * - Announces on/off state to screen readers\n * - Respects system reduce motion preference\n * - Supports `reduceMotion` prop to disable animations\n *\n * @see {@link Checkbox} for forms with submit actions\n * @see {@link Radio} for single-select options\n */\nconst Switch = memo(function Switch({\n isOn: isOnProp,\n defaultIsOn = false,\n onChange,\n label,\n labelPosition = 'start',\n size = 'md',\n onIcon,\n offIcon,\n disabled = false,\n required,\n accessibilityHint,\n reduceMotion = false,\n ref,\n ...viewProps\n}: SwitchProps) {\n const isControlled = isOnProp !== undefined;\n const [internalIsOn, setInternalIsOn] = useState(defaultIsOn);\n const [prefersReducedMotion, setPrefersReducedMotion] = useState(false);\n const isOn = isControlled ? isOnProp : internalIsOn;\n const activeVariant = isOn ? 'on' : 'off';\n const { theme } = useUnistyles();\n\n // Check system reduced motion preference\n useEffect(() => {\n const checkReducedMotion = async () => {\n const isReduceMotionEnabled = await AccessibilityInfo.isReduceMotionEnabled();\n setPrefersReducedMotion(isReduceMotionEnabled);\n };\n checkReducedMotion();\n\n const subscription = AccessibilityInfo.addEventListener(\n 'reduceMotionChanged',\n setPrefersReducedMotion,\n );\n return () => subscription.remove();\n }, []);\n\n const shouldReduceMotion = reduceMotion || prefersReducedMotion;\n const animationDuration = shouldReduceMotion ? 0 : ANIMATION_DURATION;\n\n const progress = useDerivedValue(\n () => withTiming(isOn ? 1 : 0, { duration: animationDuration }),\n [isOn, animationDuration],\n );\n\n const travelDistance = HANDLE_TRAVEL[size];\n\n const handlePress = useCallback(() => {\n if (disabled) {\n return;\n }\n\n const newValue = !isOn;\n\n if (!isControlled) {\n setInternalIsOn(newValue);\n }\n\n onChange?.(newValue);\n }, [disabled, isOn, isControlled, onChange]);\n\n // On web, useVariants returns the resolved style object instead of mutating\n // switchStyles in place.\n const variantSwitchStyles = switchStyles.useVariants({\n size,\n variant: activeVariant,\n }) as unknown as typeof switchStyles | undefined;\n const resolvedSwitchStyles = variantSwitchStyles ?? switchStyles;\n\n const variantLayerStyles = useMemo(() => {\n const components = theme.components as unknown as Record<string, Record<string, unknown>>;\n const getLayerStyle = <TStyle,>(layer: string) =>\n components[`switch/variant/default/active/${activeVariant}/${layer}/rest`] as\n | TStyle\n | undefined;\n\n return {\n handle: getLayerStyle<ViewStyle>('handle'),\n handleIcon: getLayerStyle<TextStyle>('handleIcon'),\n switch: getLayerStyle<ViewStyle>('switch'),\n text: getLayerStyle<TextStyle>('rootText'),\n };\n }, [activeVariant, theme]);\n\n // Get animated track color from design tokens (changes when variant changes)\n const trackBackgroundColor = useAnimatedVariantColor(\n resolvedSwitchStyles.switch,\n 'backgroundColor',\n );\n\n const animatedTrackStyle = useAnimatedStyle(() => {\n 'worklet';\n return {\n backgroundColor: withTiming(trackBackgroundColor.value, { duration: animationDuration }),\n };\n });\n\n const animatedHandleStyle = useAnimatedStyle(() => {\n 'worklet';\n return {\n transform: [{ translateX: progress.value * travelDistance }],\n };\n });\n\n const rootStyle: StyleProp<ViewStyle> = useMemo(\n () => [resolvedSwitchStyles.root, switchStaticStyles.root({ disabled })],\n [resolvedSwitchStyles.root, disabled],\n );\n\n const trackStyle: StyleProp<ViewStyle> = useMemo(\n () => [\n resolvedSwitchStyles.switch,\n switchStaticStyles.track,\n variantLayerStyles.switch,\n // On web, the animated variant color hook currently resolves to Unistyles'\n // black fallback, so the concrete variant layer provides the track color.\n Platform.OS !== 'web' && animatedTrackStyle,\n ],\n [resolvedSwitchStyles.switch, variantLayerStyles.switch, animatedTrackStyle],\n );\n\n const handleStyle: StyleProp<ViewStyle> = useMemo(\n () => [\n resolvedSwitchStyles.handle,\n switchStaticStyles.handle,\n variantLayerStyles.handle,\n animatedHandleStyle,\n ],\n [resolvedSwitchStyles.handle, variantLayerStyles.handle, animatedHandleStyle],\n );\n\n const accessibilityLabel = typeof label === 'string' ? label : undefined;\n const resolvedAccessibilityHint = accessibilityHint ?? 'Double tap to toggle';\n\n const labelContent = label && (\n <FormLabel\n color=\"inherit\"\n variant=\"inherit\"\n label={label}\n required={required}\n showRequiredAsterisk={required}\n style={[resolvedSwitchStyles.text, variantLayerStyles.text]}\n />\n );\n\n const a11yValue = useMemo(() => ({ text: isOn ? 'On' : 'Off' }), [isOn]);\n\n return (\n <Pressable\n ref={ref}\n onPress={handlePress}\n disabled={disabled}\n accessible\n accessibilityRole=\"switch\"\n accessibilityState={{ checked: isOn, disabled }}\n accessibilityLabel={accessibilityLabel}\n accessibilityHint={resolvedAccessibilityHint}\n accessibilityValue={a11yValue}\n {...viewProps}\n style={rootStyle}\n >\n {labelPosition === 'start' && labelContent}\n\n <Animated.View style={trackStyle} importantForAccessibility=\"no-hide-descendants\">\n <Animated.View style={handleStyle}>\n {onIcon && isOn && (\n <Animated.View style={switchStaticStyles.iconContainer}>\n <IconSlot\n icon={onIcon}\n variant=\"fill\"\n style={[resolvedSwitchStyles.handleIcon, variantLayerStyles.handleIcon]}\n />\n </Animated.View>\n )}\n {offIcon && !isOn && (\n <Animated.View style={switchStaticStyles.iconContainer}>\n <IconSlot\n icon={offIcon}\n variant=\"fill\"\n style={[resolvedSwitchStyles.handleIcon, variantLayerStyles.handleIcon]}\n />\n </Animated.View>\n )}\n </Animated.View>\n </Animated.View>\n\n {labelPosition === 'end' && labelContent}\n </Pressable>\n );\n});\n\nSwitch.displayName = 'Switch';\n\nconst switchStaticStyles = StyleSheet.create((theme) => ({\n handle: {\n borderRadius: theme.borderRadius.full,\n alignItems: 'center',\n justifyContent: 'center',\n },\n iconContainer: {\n position: 'absolute',\n alignItems: 'center',\n justifyContent: 'center',\n },\n track: {\n justifyContent: 'center',\n borderRadius: theme.borderRadius.full,\n },\n root: ({ disabled }: { disabled: boolean }) => ({\n flexDirection: 'row',\n alignItems: 'center',\n alignSelf: 'flex-start',\n opacity: disabled ? 0.5 : 1,\n }),\n}));\n\nexport { Switch, type SwitchProps };\n"],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ /*! © 2026 Yahoo, Inc. UDS Mobile v0.0.0-development */
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const require_components_UDSProvider = require("./UDSProvider.cjs");
4
+ exports.UDSGestureProvider = require_components_UDSProvider.UDSGestureProvider;
@@ -0,0 +1,3 @@
1
+
2
+ import { UDSGestureProvider, UDSGestureProviderProps } from "./UDSProvider.cjs";
3
+ export { UDSGestureProvider, type UDSGestureProviderProps };
@@ -0,0 +1,3 @@
1
+
2
+ import { UDSGestureProvider, UDSGestureProviderProps } from "./UDSProvider.js";
3
+ export { UDSGestureProvider, type UDSGestureProviderProps };
@@ -0,0 +1,3 @@
1
+ /*! © 2026 Yahoo, Inc. UDS Mobile v0.0.0-development */
2
+ import { UDSGestureProvider } from "./UDSProvider.js";
3
+ export { UDSGestureProvider };
@@ -8,29 +8,34 @@ let react_jsx_runtime = require("react/jsx-runtime");
8
8
  let react_native_gesture_handler = require("react-native-gesture-handler");
9
9
  //#region src/components/UDSProvider.tsx
10
10
  /**
11
- * Root provider for UDS Mobile.
11
+ * Root gesture and portal provider for UDS Mobile overlays.
12
12
  *
13
13
  * Place this at the top of your app layout:
14
14
  *
15
15
  * @example
16
16
  * ```tsx
17
- * import { UDSProvider } from '@yahoo/uds-mobile/UDSProvider';
17
+ * import { UDSGestureProvider } from '@yahoo/uds-mobile/UDSGestureProvider';
18
18
  *
19
19
  * export default function RootLayout() {
20
20
  * return (
21
- * <UDSProvider>
21
+ * <UDSGestureProvider>
22
22
  * <Stack />
23
- * </UDSProvider>
23
+ * </UDSGestureProvider>
24
24
  * );
25
25
  * }
26
26
  * ```
27
27
  */
28
- const UDSProvider = (0, react.memo)(function UDSProvider({ children }) {
28
+ const UDSGestureProvider = (0, react.memo)(function UDSGestureProvider({ children }) {
29
29
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native_gesture_handler.GestureHandlerRootView, {
30
30
  style: styles.root,
31
31
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_portal.PortalProvider, { children })
32
32
  });
33
33
  });
34
+ /**
35
+ * @deprecated Use {@link UDSGestureProvider} from `@yahoo/uds-mobile/UDSGestureProvider`.
36
+ */
37
+ const UDSProvider = UDSGestureProvider;
34
38
  const styles = react_native.StyleSheet.create({ root: { flex: 1 } });
35
39
  //#endregion
40
+ exports.UDSGestureProvider = UDSGestureProvider;
36
41
  exports.UDSProvider = UDSProvider;
@@ -3,28 +3,36 @@ import * as _$react from "react";
3
3
  import { ReactNode } from "react";
4
4
 
5
5
  //#region src/components/UDSProvider.d.ts
6
- interface UDSProviderProps {
6
+ interface UDSGestureProviderProps {
7
7
  children: ReactNode;
8
8
  }
9
9
  /**
10
- * Root provider for UDS Mobile.
10
+ * Root gesture and portal provider for UDS Mobile overlays.
11
11
  *
12
12
  * Place this at the top of your app layout:
13
13
  *
14
14
  * @example
15
15
  * ```tsx
16
- * import { UDSProvider } from '@yahoo/uds-mobile/UDSProvider';
16
+ * import { UDSGestureProvider } from '@yahoo/uds-mobile/UDSGestureProvider';
17
17
  *
18
18
  * export default function RootLayout() {
19
19
  * return (
20
- * <UDSProvider>
20
+ * <UDSGestureProvider>
21
21
  * <Stack />
22
- * </UDSProvider>
22
+ * </UDSGestureProvider>
23
23
  * );
24
24
  * }
25
25
  * ```
26
26
  */
27
- declare const UDSProvider: _$react.NamedExoticComponent<UDSProviderProps>;
27
+ declare const UDSGestureProvider: _$react.NamedExoticComponent<UDSGestureProviderProps>;
28
+ /**
29
+ * @deprecated Use {@link UDSGestureProvider} from `@yahoo/uds-mobile/UDSGestureProvider`.
30
+ */
31
+ declare const UDSProvider: _$react.NamedExoticComponent<UDSGestureProviderProps>;
32
+ /**
33
+ * @deprecated Use {@link UDSGestureProviderProps}.
34
+ */
35
+ type UDSProviderProps = UDSGestureProviderProps;
28
36
  //#endregion
29
- export { UDSProvider, type UDSProviderProps };
37
+ export { UDSGestureProvider, type UDSGestureProviderProps, UDSProvider, UDSProviderProps };
30
38
  //# sourceMappingURL=UDSProvider.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"UDSProvider.d.cts","names":[],"sources":["../../src/components/UDSProvider.tsx"],"mappings":";;;;;UAOU,gBAAA;EACR,QAAA,EAAU,SAAA;AAAA;AAR2B;;;;;AAQlB;;;;;;;;;;;;;AARkB,cA6BjC,WAAA,EAAW,OAAA,CAAA,oBAAA,CAAA,gBAAA"}
1
+ {"version":3,"file":"UDSProvider.d.cts","names":[],"sources":["../../src/components/UDSProvider.tsx"],"mappings":";;;;;UAOU,uBAAA;EACR,QAAA,EAAU,SAAA;AAAA;AAR2B;;;;;AAQlB;;;;;AAqBG;;;;;AAwBxB;;;AArDuC,cA6BjC,kBAAA,EAAkB,OAAA,CAAA,oBAAA,CAAA,uBAAA;;;;cAWlB,WAAA,EAAW,OAAA,CAAA,oBAAA,CAAA,uBAAA;;;;KAaL,gBAAA,GAAmB,uBAAA"}
@@ -3,28 +3,36 @@ import * as _$react from "react";
3
3
  import { ReactNode } from "react";
4
4
 
5
5
  //#region src/components/UDSProvider.d.ts
6
- interface UDSProviderProps {
6
+ interface UDSGestureProviderProps {
7
7
  children: ReactNode;
8
8
  }
9
9
  /**
10
- * Root provider for UDS Mobile.
10
+ * Root gesture and portal provider for UDS Mobile overlays.
11
11
  *
12
12
  * Place this at the top of your app layout:
13
13
  *
14
14
  * @example
15
15
  * ```tsx
16
- * import { UDSProvider } from '@yahoo/uds-mobile/UDSProvider';
16
+ * import { UDSGestureProvider } from '@yahoo/uds-mobile/UDSGestureProvider';
17
17
  *
18
18
  * export default function RootLayout() {
19
19
  * return (
20
- * <UDSProvider>
20
+ * <UDSGestureProvider>
21
21
  * <Stack />
22
- * </UDSProvider>
22
+ * </UDSGestureProvider>
23
23
  * );
24
24
  * }
25
25
  * ```
26
26
  */
27
- declare const UDSProvider: _$react.NamedExoticComponent<UDSProviderProps>;
27
+ declare const UDSGestureProvider: _$react.NamedExoticComponent<UDSGestureProviderProps>;
28
+ /**
29
+ * @deprecated Use {@link UDSGestureProvider} from `@yahoo/uds-mobile/UDSGestureProvider`.
30
+ */
31
+ declare const UDSProvider: _$react.NamedExoticComponent<UDSGestureProviderProps>;
32
+ /**
33
+ * @deprecated Use {@link UDSGestureProviderProps}.
34
+ */
35
+ type UDSProviderProps = UDSGestureProviderProps;
28
36
  //#endregion
29
- export { UDSProvider, type UDSProviderProps };
37
+ export { UDSGestureProvider, type UDSGestureProviderProps, UDSProvider, UDSProviderProps };
30
38
  //# sourceMappingURL=UDSProvider.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"UDSProvider.d.ts","names":[],"sources":["../../src/components/UDSProvider.tsx"],"mappings":";;;;;UAOU,gBAAA;EACR,QAAA,EAAU,SAAA;AAAA;AAR2B;;;;;AAQlB;;;;;;;;;;;;;AARkB,cA6BjC,WAAA,EAAW,OAAA,CAAA,oBAAA,CAAA,gBAAA"}
1
+ {"version":3,"file":"UDSProvider.d.ts","names":[],"sources":["../../src/components/UDSProvider.tsx"],"mappings":";;;;;UAOU,uBAAA;EACR,QAAA,EAAU,SAAA;AAAA;AAR2B;;;;;AAQlB;;;;;AAqBG;;;;;AAwBxB;;;AArDuC,cA6BjC,kBAAA,EAAkB,OAAA,CAAA,oBAAA,CAAA,uBAAA;;;;cAWlB,WAAA,EAAW,OAAA,CAAA,oBAAA,CAAA,uBAAA;;;;KAaL,gBAAA,GAAmB,uBAAA"}