@yahoo/uds-mobile 2.21.2 → 2.23.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 (112) hide show
  1. package/README.md +2 -0
  2. package/dist/bin/automated-config/dist/generated/generatedConfigs.mjs +3127 -452
  3. package/dist/bin/uds-mobile.mjs +7 -7
  4. package/dist/components/IconButton.cjs +24 -21
  5. package/dist/components/IconButton.js +25 -22
  6. package/dist/components/IconButton.js.map +1 -1
  7. package/dist/components/Select/Select.cjs +194 -0
  8. package/dist/components/Select/Select.d.cts +47 -0
  9. package/dist/components/Select/Select.d.cts.map +1 -0
  10. package/dist/components/Select/Select.d.ts +47 -0
  11. package/dist/components/Select/Select.d.ts.map +1 -0
  12. package/dist/components/Select/Select.js +194 -0
  13. package/dist/components/Select/Select.js.map +1 -0
  14. package/dist/components/Select/SelectContent.cjs +148 -0
  15. package/dist/components/Select/SelectContent.d.cts +31 -0
  16. package/dist/components/Select/SelectContent.d.cts.map +1 -0
  17. package/dist/components/Select/SelectContent.d.ts +31 -0
  18. package/dist/components/Select/SelectContent.d.ts.map +1 -0
  19. package/dist/components/Select/SelectContent.js +147 -0
  20. package/dist/components/Select/SelectContent.js.map +1 -0
  21. package/dist/components/Select/SelectItem.cjs +138 -0
  22. package/dist/components/Select/SelectItem.d.cts +24 -0
  23. package/dist/components/Select/SelectItem.d.cts.map +1 -0
  24. package/dist/components/Select/SelectItem.d.ts +24 -0
  25. package/dist/components/Select/SelectItem.d.ts.map +1 -0
  26. package/dist/components/Select/SelectItem.js +137 -0
  27. package/dist/components/Select/SelectItem.js.map +1 -0
  28. package/dist/components/Select/SelectTrigger.cjs +140 -0
  29. package/dist/components/Select/SelectTrigger.d.cts +13 -0
  30. package/dist/components/Select/SelectTrigger.d.cts.map +1 -0
  31. package/dist/components/Select/SelectTrigger.d.ts +13 -0
  32. package/dist/components/Select/SelectTrigger.d.ts.map +1 -0
  33. package/dist/components/Select/SelectTrigger.js +139 -0
  34. package/dist/components/Select/SelectTrigger.js.map +1 -0
  35. package/dist/components/Select/index.cjs +8 -0
  36. package/dist/components/Select/index.d.cts +5 -0
  37. package/dist/components/Select/index.d.ts +5 -0
  38. package/dist/components/Select/index.js +5 -0
  39. package/dist/components/Select/selectContext.cjs +22 -0
  40. package/dist/components/Select/selectContext.d.cts +12 -0
  41. package/dist/components/Select/selectContext.d.cts.map +1 -0
  42. package/dist/components/Select/selectContext.d.ts +12 -0
  43. package/dist/components/Select/selectContext.d.ts.map +1 -0
  44. package/dist/components/Select/selectContext.js +19 -0
  45. package/dist/components/Select/selectContext.js.map +1 -0
  46. package/dist/components/Select/selectItemTheme.cjs +39 -0
  47. package/dist/components/Select/selectItemTheme.d.cts +19 -0
  48. package/dist/components/Select/selectItemTheme.d.cts.map +1 -0
  49. package/dist/components/Select/selectItemTheme.d.ts +19 -0
  50. package/dist/components/Select/selectItemTheme.d.ts.map +1 -0
  51. package/dist/components/Select/selectItemTheme.js +39 -0
  52. package/dist/components/Select/selectItemTheme.js.map +1 -0
  53. package/dist/components/Select/types.cjs +1 -0
  54. package/dist/components/Select/types.d.cts +43 -0
  55. package/dist/components/Select/types.d.cts.map +1 -0
  56. package/dist/components/Select/types.d.ts +43 -0
  57. package/dist/components/Select/types.d.ts.map +1 -0
  58. package/dist/components/Select/types.js +1 -0
  59. package/dist/components/Switch.cjs +34 -12
  60. package/dist/components/Switch.d.cts.map +1 -1
  61. package/dist/components/Switch.d.ts.map +1 -1
  62. package/dist/components/Switch.js +36 -14
  63. package/dist/components/Switch.js.map +1 -1
  64. package/dist/components/UDSGestureProvider.cjs +4 -0
  65. package/dist/components/UDSGestureProvider.d.cts +3 -0
  66. package/dist/components/UDSGestureProvider.d.ts +3 -0
  67. package/dist/components/UDSGestureProvider.js +3 -0
  68. package/dist/components/UDSProvider.cjs +10 -5
  69. package/dist/components/UDSProvider.d.cts +15 -7
  70. package/dist/components/UDSProvider.d.cts.map +1 -1
  71. package/dist/components/UDSProvider.d.ts +15 -7
  72. package/dist/components/UDSProvider.d.ts.map +1 -1
  73. package/dist/components/UDSProvider.js +10 -6
  74. package/dist/components/UDSProvider.js.map +1 -1
  75. package/dist/components/internal/Overlay/OverlayPortal.js.map +1 -1
  76. package/dist/components/internal/Overlay/index.cjs +2 -0
  77. package/dist/components/internal/Overlay/index.d.cts +2 -1
  78. package/dist/components/internal/Overlay/index.d.ts +2 -1
  79. package/dist/components/internal/Overlay/index.js +2 -1
  80. package/dist/components/internal/Overlay/measureTrigger.cjs +35 -0
  81. package/dist/components/internal/Overlay/measureTrigger.d.cts +9 -0
  82. package/dist/components/internal/Overlay/measureTrigger.d.cts.map +1 -0
  83. package/dist/components/internal/Overlay/measureTrigger.d.ts +9 -0
  84. package/dist/components/internal/Overlay/measureTrigger.d.ts.map +1 -0
  85. package/dist/components/internal/Overlay/measureTrigger.js +36 -0
  86. package/dist/components/internal/Overlay/measureTrigger.js.map +1 -0
  87. package/dist/jest/mocks/styles.cjs +68 -10
  88. package/dist/jest/mocks/styles.d.cts +5 -2
  89. package/dist/jest/mocks/styles.d.cts.map +1 -1
  90. package/dist/jest/mocks/styles.d.ts +5 -2
  91. package/dist/jest/mocks/styles.d.ts.map +1 -1
  92. package/dist/jest/mocks/styles.js +66 -11
  93. package/dist/jest/mocks/styles.js.map +1 -1
  94. package/dist/jest/mocks/unistyles.cjs +16 -1
  95. package/dist/jest/mocks/unistyles.d.cts +56 -2
  96. package/dist/jest/mocks/unistyles.d.cts.map +1 -1
  97. package/dist/jest/mocks/unistyles.d.ts +56 -2
  98. package/dist/jest/mocks/unistyles.d.ts.map +1 -1
  99. package/dist/jest/mocks/unistyles.js +16 -1
  100. package/dist/jest/mocks/unistyles.js.map +1 -1
  101. package/dist/portal.cjs +1 -1
  102. package/dist/portal.js +1 -1
  103. package/dist/portal.js.map +1 -1
  104. package/dist/types/dist/index.d.cts +28 -1
  105. package/dist/types/dist/index.d.cts.map +1 -1
  106. package/dist/types/dist/index.d.ts +28 -1
  107. package/dist/types/dist/index.d.ts.map +1 -1
  108. package/generated/styles.cjs +125 -0
  109. package/generated/styles.d.ts +39 -0
  110. package/generated/styles.mjs +125 -0
  111. package/generated/unistyles.d.ts +108 -8
  112. package/package.json +21 -1
@@ -97,13 +97,6 @@ async function loadConfigFromFile(configPath) {
97
97
  const moduleUrl = pathToFileURL(absolutePath).href;
98
98
  log(`Loading config from: ${configPath}`, "dim");
99
99
  const configModule = await import(moduleUrl);
100
- for (const key in configModule) if (key.endsWith("Config") || key === "default") {
101
- const config = configModule[key];
102
- if (config && typeof config === "object") {
103
- logStep("Found", `config export: ${key}`);
104
- return config;
105
- }
106
- }
107
100
  for (const name of [
108
101
  "config",
109
102
  "tokens",
@@ -113,6 +106,13 @@ async function loadConfigFromFile(configPath) {
113
106
  logStep("Found", `config export: ${name}`);
114
107
  return configModule[name];
115
108
  }
109
+ for (const key in configModule) if (key.endsWith("Config") || key === "default") {
110
+ const config = configModule[key];
111
+ if (config && typeof config === "object") {
112
+ logStep("Found", `config export: ${key}`);
113
+ return config;
114
+ }
115
+ }
116
116
  throw new Error(`No config export found in ${configPath}. Expected an export ending with "Config" or a default export.`);
117
117
  }
118
118
  async function runSync(opts = {}) {
@@ -59,6 +59,7 @@ function interpolateShadowAlpha(shadow, alpha) {
59
59
  const IconButton = (0, react.memo)(function IconButton({ name, variant = "primary", size = "md", iconVariant = "outline", iconColor, loading, disabled, style, accessibilityLabel, accessibilityHint, disableEffects = false, onPressIn, onPressOut, ref, ...props }) {
60
60
  const isDisabled = disabled || loading;
61
61
  const shouldAnimate = !disableEffects && !isDisabled;
62
+ const shouldAnimateVariantColors = react_native.Platform.OS !== "web";
62
63
  const { theme } = (0, react_native_unistyles.useUnistyles)();
63
64
  const { controlHeight } = (0, react.useMemo)(() => require_components_Button_buttonTheme.getIconButtonControlMetrics(theme, size), [theme, size]);
64
65
  const matchedControlDimensions = controlHeight > 0 ? {
@@ -66,16 +67,16 @@ const IconButton = (0, react.memo)(function IconButton({ name, variant = "primar
66
67
  width: controlHeight
67
68
  } : void 0;
68
69
  const [pressed, setPressed] = (0, react.useState)(false);
69
- generated_styles.iconButtonStyles.useVariants({ size });
70
- generated_styles.buttonStyles.useVariants({
70
+ const resolvedIconButtonStyles = generated_styles.iconButtonStyles.useVariants({ size }) ?? generated_styles.iconButtonStyles;
71
+ const resolvedButtonStyles = generated_styles.buttonStyles.useVariants({
71
72
  variant,
72
73
  disabled: isDisabled,
73
74
  pressed
74
- });
75
- generated_styles.styles.useVariants({ color: iconColor });
76
- const resolvedIconColor = iconColor ? generated_styles.styles.foundation.color : void 0;
77
- const backgroundColor = (0, react_native_unistyles_reanimated.useAnimatedVariantColor)(generated_styles.buttonStyles.root, "backgroundColor");
78
- const borderColor = (0, react_native_unistyles_reanimated.useAnimatedVariantColor)(generated_styles.buttonStyles.root, "borderColor");
75
+ }) ?? generated_styles.buttonStyles;
76
+ const resolvedFoundationStyles = generated_styles.styles.useVariants({ color: iconColor }) ?? generated_styles.styles;
77
+ const resolvedIconColor = iconColor ? resolvedFoundationStyles.foundation.color : void 0;
78
+ const backgroundColor = (0, react_native_unistyles_reanimated.useAnimatedVariantColor)(resolvedButtonStyles.root, "backgroundColor");
79
+ const borderColor = (0, react_native_unistyles_reanimated.useAnimatedVariantColor)(resolvedButtonStyles.root, "borderColor");
79
80
  const animatedTheme = (0, react_native_unistyles_reanimated.useAnimatedTheme)();
80
81
  const scale = (0, react_native_reanimated.useSharedValue)(require_index.SCALE_EFFECTS.none);
81
82
  const handlePressIn = (0, react.useCallback)((event) => {
@@ -108,14 +109,16 @@ const IconButton = (0, react.memo)(function IconButton({ name, variant = "primar
108
109
  const shadowPressed = animatedTheme.value.components[`button/variant/${variant}/root/pressed`]?.boxShadow;
109
110
  return {
110
111
  transform: [{ scale: scale.value }],
111
- backgroundColor: (0, react_native_reanimated.withTiming)(backgroundColor.value, {
112
- duration: 220,
113
- easing: react_native_reanimated.Easing.bezier(0, 0, .2, 1)
114
- }),
115
- borderColor: (0, react_native_reanimated.withTiming)(borderColor.value, {
116
- duration: 220,
117
- easing: react_native_reanimated.Easing.bezier(0, 0, .2, 1)
118
- }),
112
+ ...shouldAnimateVariantColors && {
113
+ backgroundColor: (0, react_native_reanimated.withTiming)(backgroundColor.value, {
114
+ duration: 220,
115
+ easing: react_native_reanimated.Easing.bezier(0, 0, .2, 1)
116
+ }),
117
+ borderColor: (0, react_native_reanimated.withTiming)(borderColor.value, {
118
+ duration: 220,
119
+ easing: react_native_reanimated.Easing.bezier(0, 0, .2, 1)
120
+ })
121
+ },
119
122
  ...shadowPressed && { boxShadow: interpolateShadowAlpha(shadowPressed, pressProgress.value) }
120
123
  };
121
124
  });
@@ -133,21 +136,21 @@ const IconButton = (0, react.memo)(function IconButton({ name, variant = "primar
133
136
  accessibilityRole: "button",
134
137
  accessibilityState: a11yState,
135
138
  style: [
136
- generated_styles.iconButtonStyles.root,
137
- generated_styles.buttonStyles.root,
139
+ resolvedIconButtonStyles.root,
140
+ resolvedButtonStyles.root,
138
141
  matchedControlDimensions,
139
- generated_styles.styles.foundation,
142
+ resolvedFoundationStyles.foundation,
140
143
  animatedRootStyle,
141
144
  typeof style === "function" ? style({ pressed }) : style
142
145
  ],
143
146
  ...props,
144
147
  children: loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.ActivityIndicator, {
145
- size: generated_styles.iconButtonStyles.icon.fontSize,
146
- color: resolvedIconColor ?? generated_styles.buttonStyles.icon.color
148
+ size: resolvedIconButtonStyles.icon.fontSize,
149
+ color: resolvedIconColor ?? resolvedButtonStyles.icon.color
147
150
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_components_Icon.Icon, {
148
151
  name,
149
152
  variant: iconVariant,
150
- style: [generated_styles.iconButtonStyles.icon, generated_styles.buttonStyles.icon],
153
+ style: [resolvedIconButtonStyles.icon, resolvedButtonStyles.icon],
151
154
  dangerouslySetColor: resolvedIconColor
152
155
  })
153
156
  });
@@ -5,7 +5,7 @@ import { Icon } from "./Icon.js";
5
5
  import { getIconButtonControlMetrics } from "./Button/buttonTheme.js";
6
6
  import { AnimatedPressable } from "./Pressable.js";
7
7
  import { memo, useCallback, useMemo, useState } from "react";
8
- import { ActivityIndicator } from "react-native";
8
+ import { ActivityIndicator, Platform } from "react-native";
9
9
  import { jsx } from "react/jsx-runtime";
10
10
  import { buttonStyles, iconButtonStyles, styles } from "../../generated/styles";
11
11
  import { useUnistyles } from "react-native-unistyles";
@@ -57,6 +57,7 @@ function interpolateShadowAlpha(shadow, alpha) {
57
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 }) {
58
58
  const isDisabled = disabled || loading;
59
59
  const shouldAnimate = !disableEffects && !isDisabled;
60
+ const shouldAnimateVariantColors = Platform.OS !== "web";
60
61
  const { theme } = useUnistyles();
61
62
  const { controlHeight } = useMemo(() => getIconButtonControlMetrics(theme, size), [theme, size]);
62
63
  const matchedControlDimensions = controlHeight > 0 ? {
@@ -64,16 +65,16 @@ const IconButton = memo(function IconButton({ name, variant = "primary", size =
64
65
  width: controlHeight
65
66
  } : void 0;
66
67
  const [pressed, setPressed] = useState(false);
67
- iconButtonStyles.useVariants({ size });
68
- buttonStyles.useVariants({
68
+ const resolvedIconButtonStyles = iconButtonStyles.useVariants({ size }) ?? iconButtonStyles;
69
+ const resolvedButtonStyles = buttonStyles.useVariants({
69
70
  variant,
70
71
  disabled: isDisabled,
71
72
  pressed
72
- });
73
- styles.useVariants({ color: iconColor });
74
- const resolvedIconColor = iconColor ? styles.foundation.color : void 0;
75
- const backgroundColor = useAnimatedVariantColor(buttonStyles.root, "backgroundColor");
76
- 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");
77
78
  const animatedTheme = useAnimatedTheme();
78
79
  const scale = useSharedValue(SCALE_EFFECTS.none);
79
80
  const handlePressIn = useCallback((event) => {
@@ -106,14 +107,16 @@ const IconButton = memo(function IconButton({ name, variant = "primary", size =
106
107
  const shadowPressed = animatedTheme.value.components[`button/variant/${variant}/root/pressed`]?.boxShadow;
107
108
  return {
108
109
  transform: [{ scale: scale.value }],
109
- backgroundColor: withTiming(backgroundColor.value, {
110
- duration: 220,
111
- easing: Easing.bezier(0, 0, .2, 1)
112
- }),
113
- borderColor: withTiming(borderColor.value, {
114
- duration: 220,
115
- easing: Easing.bezier(0, 0, .2, 1)
116
- }),
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
+ },
117
120
  ...shadowPressed && { boxShadow: interpolateShadowAlpha(shadowPressed, pressProgress.value) }
118
121
  };
119
122
  });
@@ -131,21 +134,21 @@ const IconButton = memo(function IconButton({ name, variant = "primary", size =
131
134
  accessibilityRole: "button",
132
135
  accessibilityState: a11yState,
133
136
  style: [
134
- iconButtonStyles.root,
135
- buttonStyles.root,
137
+ resolvedIconButtonStyles.root,
138
+ resolvedButtonStyles.root,
136
139
  matchedControlDimensions,
137
- styles.foundation,
140
+ resolvedFoundationStyles.foundation,
138
141
  animatedRootStyle,
139
142
  typeof style === "function" ? style({ pressed }) : style
140
143
  ],
141
144
  ...props,
142
145
  children: loading ? /* @__PURE__ */ jsx(ActivityIndicator, {
143
- size: iconButtonStyles.icon.fontSize,
144
- color: resolvedIconColor ?? buttonStyles.icon.color
146
+ size: resolvedIconButtonStyles.icon.fontSize,
147
+ color: resolvedIconColor ?? resolvedButtonStyles.icon.color
145
148
  }) : /* @__PURE__ */ jsx(Icon, {
146
149
  name,
147
150
  variant: iconVariant,
148
- style: [iconButtonStyles.icon, buttonStyles.icon],
151
+ style: [resolvedIconButtonStyles.icon, resolvedButtonStyles.icon],
149
152
  dangerouslySetColor: resolvedIconColor
150
153
  })
151
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';\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\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 // 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 matchedControlDimensions,\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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"}
@@ -0,0 +1,194 @@
1
+ /*! © 2026 Yahoo, Inc. UDS Mobile v0.0.0-development */
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ require("../../_virtual/_rolldown/runtime.cjs");
4
+ const require_components_HStack = require("../HStack.cjs");
5
+ const require_components_Text = require("../Text.cjs");
6
+ const require_components_InputHelpText = require("../InputHelpText.cjs");
7
+ const require_components_VStack = require("../VStack.cjs");
8
+ const require_components_internal_Overlay_useControllableState = require("../internal/Overlay/useControllableState.cjs");
9
+ const require_components_Select_selectContext = require("./selectContext.cjs");
10
+ const require_components_Select_SelectTrigger = require("./SelectTrigger.cjs");
11
+ let react = require("react");
12
+ let react_jsx_runtime = require("react/jsx-runtime");
13
+ let generated_styles = require("../../../generated/styles");
14
+ let lodash_es = require("lodash-es");
15
+ //#region src/components/Select/Select.tsx
16
+ /**
17
+ * **⚙️ A composable Select component**
18
+ *
19
+ * @description
20
+ * Select lets users pick one value from a list. Compose with `SelectContent` and
21
+ * `SelectItem`, similar to `Tabs` with `TabList` and `Tab`.
22
+ *
23
+ * @category Form
24
+ * @platform mobile
25
+ *
26
+ * @example
27
+ * ```tsx
28
+ * import { Select, SelectContent, SelectItem } from '@yahoo/uds-mobile/Select';
29
+ *
30
+ * <Select label="Country" placeholder="Select a country" defaultValue="us">
31
+ * <SelectContent>
32
+ * <SelectItem value="us">United States</SelectItem>
33
+ * <SelectItem value="ca">Canada</SelectItem>
34
+ * </SelectContent>
35
+ * </Select>
36
+ * ```
37
+ */
38
+ const Select = (0, react.memo)(function Select({ label, helpText, helperTextIcon, placeholder, size = "md", disabled, required, hasError, readOnly, width = "100%", reduceMotion = false, startIcon, endIcon, value: valueProp, defaultValue = "", onChange, children, testID }) {
39
+ const uid = `uds-select-${(0, react.useId)()}`;
40
+ const triggerRef = (0, react.useRef)(null);
41
+ const [triggerRect, setTriggerRect] = (0, react.useState)(null);
42
+ const [highlightedValue, setHighlightedValue] = (0, react.useState)(null);
43
+ const itemsRef = (0, react.useRef)(/* @__PURE__ */ new Map());
44
+ const [, setItemsVersion] = (0, react.useState)(0);
45
+ const [value, setValue] = require_components_internal_Overlay_useControllableState.useControllableState({
46
+ value: valueProp,
47
+ defaultValue,
48
+ onChange
49
+ });
50
+ const [open, setOpen] = require_components_internal_Overlay_useControllableState.useControllableState({
51
+ defaultValue: false,
52
+ onChange: (nextOpen) => {
53
+ if (!nextOpen) setHighlightedValue(null);
54
+ }
55
+ });
56
+ const registerItem = (0, react.useCallback)((itemValue, itemLabel) => {
57
+ if (itemsRef.current.get(itemValue) === itemLabel) return;
58
+ itemsRef.current.set(itemValue, itemLabel);
59
+ setItemsVersion((version) => version + 1);
60
+ }, []);
61
+ const unregisterItem = (0, react.useCallback)((itemValue) => {
62
+ if (!itemsRef.current.has(itemValue)) return;
63
+ itemsRef.current.delete(itemValue);
64
+ setItemsVersion((version) => version + 1);
65
+ }, []);
66
+ const getItemLabel = (0, react.useCallback)((itemValue) => itemsRef.current.get(itemValue), []);
67
+ const fieldContext = (0, react.useMemo)(() => ({
68
+ size,
69
+ disabled,
70
+ readOnly,
71
+ required,
72
+ hasError,
73
+ width,
74
+ reduceMotion,
75
+ placeholder,
76
+ uid
77
+ }), [
78
+ disabled,
79
+ hasError,
80
+ placeholder,
81
+ readOnly,
82
+ reduceMotion,
83
+ required,
84
+ size,
85
+ uid,
86
+ width
87
+ ]);
88
+ const contextValue = (0, react.useMemo)(() => ({
89
+ value,
90
+ setValue,
91
+ open,
92
+ setOpen,
93
+ triggerRef,
94
+ triggerRect,
95
+ setTriggerRect,
96
+ registerItem,
97
+ unregisterItem,
98
+ getItemLabel,
99
+ highlightedValue,
100
+ setHighlightedValue,
101
+ field: fieldContext,
102
+ reduceMotion
103
+ }), [
104
+ fieldContext,
105
+ getItemLabel,
106
+ highlightedValue,
107
+ open,
108
+ reduceMotion,
109
+ registerItem,
110
+ setOpen,
111
+ setValue,
112
+ triggerRect,
113
+ unregisterItem,
114
+ value
115
+ ]);
116
+ const valueState = value.length > 0 ? "filled" : "empty";
117
+ generated_styles.inputStyles.useVariants({
118
+ size,
119
+ value: valueState,
120
+ pressed: open,
121
+ readonly: readOnly,
122
+ invalid: hasError
123
+ });
124
+ const rootStyle = (0, react.useMemo)(() => [{
125
+ width,
126
+ opacity: disabled ? .5 : 1
127
+ }], [disabled, width]);
128
+ const labelContent = (0, react.useMemo)(() => {
129
+ if (!label) return null;
130
+ const content = (0, lodash_es.isFunction)(label) ? label() : label;
131
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_components_HStack.HStack, {
132
+ columnGap: "1",
133
+ alignItems: "flex-end",
134
+ spacingBottom: "2",
135
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_components_Text.Text, {
136
+ style: generated_styles.inputStyles.label,
137
+ children: content
138
+ }), required && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_components_Text.Text, {
139
+ style: generated_styles.inputStyles.labelRequired,
140
+ children: "*"
141
+ })]
142
+ });
143
+ }, [
144
+ label,
145
+ required,
146
+ generated_styles.inputStyles.label,
147
+ generated_styles.inputStyles.labelRequired
148
+ ]);
149
+ const helpTextContent = (0, react.useMemo)(() => {
150
+ if (!helpText) return null;
151
+ const content = (0, lodash_es.isFunction)(helpText) ? helpText() : helpText;
152
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_components_InputHelpText.InputHelpText, {
153
+ startIcon: helperTextIcon,
154
+ size,
155
+ isFilled: valueState === "filled",
156
+ disabled,
157
+ readOnly,
158
+ hasError,
159
+ pressed: open,
160
+ children: content
161
+ });
162
+ }, [
163
+ disabled,
164
+ hasError,
165
+ helpText,
166
+ helperTextIcon,
167
+ open,
168
+ readOnly,
169
+ size,
170
+ valueState
171
+ ]);
172
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_components_Select_selectContext.SelectContext.Provider, {
173
+ value: contextValue,
174
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_components_Select_selectContext.SelectFieldContext.Provider, {
175
+ value: fieldContext,
176
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_components_VStack.VStack, {
177
+ testID,
178
+ style: rootStyle,
179
+ children: [
180
+ labelContent,
181
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_components_Select_SelectTrigger.SelectTrigger, {
182
+ startIcon,
183
+ endIcon
184
+ }),
185
+ helpTextContent,
186
+ children
187
+ ]
188
+ })
189
+ })
190
+ });
191
+ });
192
+ Select.displayName = "Select";
193
+ //#endregion
194
+ exports.Select = Select;
@@ -0,0 +1,47 @@
1
+
2
+ import { UniversalSelectProps } from "../../types/dist/index.cjs";
3
+ import { IconSlotType } from "../IconSlot.cjs";
4
+ import * as _$react from "react";
5
+ import { ReactNode } from "react";
6
+
7
+ //#region src/components/Select/Select.d.ts
8
+ interface SelectProps extends Omit<UniversalSelectProps<IconSlotType>, 'width'> {
9
+ /** Placeholder text shown when no value is selected. */
10
+ placeholder?: string;
11
+ /** Selected value for controlled usage. */
12
+ value?: string;
13
+ /** Initial value for uncontrolled usage. @default '' */
14
+ defaultValue?: string;
15
+ /** Called when the selected value changes. */
16
+ onChange?: (value: string) => void;
17
+ /** Container width. @default '100%' */
18
+ width?: number | `${number}%` | '100%';
19
+ children?: ReactNode;
20
+ testID?: string;
21
+ }
22
+ /**
23
+ * **⚙️ A composable Select component**
24
+ *
25
+ * @description
26
+ * Select lets users pick one value from a list. Compose with `SelectContent` and
27
+ * `SelectItem`, similar to `Tabs` with `TabList` and `Tab`.
28
+ *
29
+ * @category Form
30
+ * @platform mobile
31
+ *
32
+ * @example
33
+ * ```tsx
34
+ * import { Select, SelectContent, SelectItem } from '@yahoo/uds-mobile/Select';
35
+ *
36
+ * <Select label="Country" placeholder="Select a country" defaultValue="us">
37
+ * <SelectContent>
38
+ * <SelectItem value="us">United States</SelectItem>
39
+ * <SelectItem value="ca">Canada</SelectItem>
40
+ * </SelectContent>
41
+ * </Select>
42
+ * ```
43
+ */
44
+ declare const Select: _$react.NamedExoticComponent<SelectProps>;
45
+ //#endregion
46
+ export { Select, type SelectProps };
47
+ //# sourceMappingURL=Select.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Select.d.cts","names":[],"sources":["../../../src/components/Select/Select.tsx"],"mappings":";;;;;;;UAiBU,WAAA,SAAoB,IAAA,CAAK,oBAAA,CAAqB,YAAA;;EAEtD,WAAA;EAFQ;EAIR,KAAA;;EAEA,YAAA;EANiC;EAQjC,QAAA,IAAY,KAAA;EARgB;EAU5B,KAAA;EACA,QAAA,GAAW,SAAA;EACX,MAAA;AAAA;;;;;;;;;;;;;AAAM;;;;;;;;;;cAyBF,MAAA,EAAM,OAAA,CAAA,oBAAA,CAAA,WAAA"}
@@ -0,0 +1,47 @@
1
+
2
+ import { UniversalSelectProps } from "../../types/dist/index.js";
3
+ import { IconSlotType } from "../IconSlot.js";
4
+ import * as _$react from "react";
5
+ import { ReactNode } from "react";
6
+
7
+ //#region src/components/Select/Select.d.ts
8
+ interface SelectProps extends Omit<UniversalSelectProps<IconSlotType>, 'width'> {
9
+ /** Placeholder text shown when no value is selected. */
10
+ placeholder?: string;
11
+ /** Selected value for controlled usage. */
12
+ value?: string;
13
+ /** Initial value for uncontrolled usage. @default '' */
14
+ defaultValue?: string;
15
+ /** Called when the selected value changes. */
16
+ onChange?: (value: string) => void;
17
+ /** Container width. @default '100%' */
18
+ width?: number | `${number}%` | '100%';
19
+ children?: ReactNode;
20
+ testID?: string;
21
+ }
22
+ /**
23
+ * **⚙️ A composable Select component**
24
+ *
25
+ * @description
26
+ * Select lets users pick one value from a list. Compose with `SelectContent` and
27
+ * `SelectItem`, similar to `Tabs` with `TabList` and `Tab`.
28
+ *
29
+ * @category Form
30
+ * @platform mobile
31
+ *
32
+ * @example
33
+ * ```tsx
34
+ * import { Select, SelectContent, SelectItem } from '@yahoo/uds-mobile/Select';
35
+ *
36
+ * <Select label="Country" placeholder="Select a country" defaultValue="us">
37
+ * <SelectContent>
38
+ * <SelectItem value="us">United States</SelectItem>
39
+ * <SelectItem value="ca">Canada</SelectItem>
40
+ * </SelectContent>
41
+ * </Select>
42
+ * ```
43
+ */
44
+ declare const Select: _$react.NamedExoticComponent<SelectProps>;
45
+ //#endregion
46
+ export { Select, type SelectProps };
47
+ //# sourceMappingURL=Select.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Select.d.ts","names":[],"sources":["../../../src/components/Select/Select.tsx"],"mappings":";;;;;;;UAiBU,WAAA,SAAoB,IAAA,CAAK,oBAAA,CAAqB,YAAA;;EAEtD,WAAA;EAFQ;EAIR,KAAA;;EAEA,YAAA;EANiC;EAQjC,QAAA,IAAY,KAAA;EARgB;EAU5B,KAAA;EACA,QAAA,GAAW,SAAA;EACX,MAAA;AAAA;;;;;;;;;;;;;AAAM;;;;;;;;;;cAyBF,MAAA,EAAM,OAAA,CAAA,oBAAA,CAAA,WAAA"}