@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.
- package/README.md +2 -0
- package/dist/bin/automated-config/dist/generated/generatedConfigs.mjs +3127 -452
- package/dist/bin/uds-mobile.mjs +7 -7
- package/dist/components/IconButton.cjs +24 -21
- package/dist/components/IconButton.js +25 -22
- package/dist/components/IconButton.js.map +1 -1
- package/dist/components/Select/Select.cjs +194 -0
- package/dist/components/Select/Select.d.cts +47 -0
- package/dist/components/Select/Select.d.cts.map +1 -0
- package/dist/components/Select/Select.d.ts +47 -0
- package/dist/components/Select/Select.d.ts.map +1 -0
- package/dist/components/Select/Select.js +194 -0
- package/dist/components/Select/Select.js.map +1 -0
- package/dist/components/Select/SelectContent.cjs +148 -0
- package/dist/components/Select/SelectContent.d.cts +31 -0
- package/dist/components/Select/SelectContent.d.cts.map +1 -0
- package/dist/components/Select/SelectContent.d.ts +31 -0
- package/dist/components/Select/SelectContent.d.ts.map +1 -0
- package/dist/components/Select/SelectContent.js +147 -0
- package/dist/components/Select/SelectContent.js.map +1 -0
- package/dist/components/Select/SelectItem.cjs +138 -0
- package/dist/components/Select/SelectItem.d.cts +24 -0
- package/dist/components/Select/SelectItem.d.cts.map +1 -0
- package/dist/components/Select/SelectItem.d.ts +24 -0
- package/dist/components/Select/SelectItem.d.ts.map +1 -0
- package/dist/components/Select/SelectItem.js +137 -0
- package/dist/components/Select/SelectItem.js.map +1 -0
- package/dist/components/Select/SelectTrigger.cjs +140 -0
- package/dist/components/Select/SelectTrigger.d.cts +13 -0
- package/dist/components/Select/SelectTrigger.d.cts.map +1 -0
- package/dist/components/Select/SelectTrigger.d.ts +13 -0
- package/dist/components/Select/SelectTrigger.d.ts.map +1 -0
- package/dist/components/Select/SelectTrigger.js +139 -0
- package/dist/components/Select/SelectTrigger.js.map +1 -0
- package/dist/components/Select/index.cjs +8 -0
- package/dist/components/Select/index.d.cts +5 -0
- package/dist/components/Select/index.d.ts +5 -0
- package/dist/components/Select/index.js +5 -0
- package/dist/components/Select/selectContext.cjs +22 -0
- package/dist/components/Select/selectContext.d.cts +12 -0
- package/dist/components/Select/selectContext.d.cts.map +1 -0
- package/dist/components/Select/selectContext.d.ts +12 -0
- package/dist/components/Select/selectContext.d.ts.map +1 -0
- package/dist/components/Select/selectContext.js +19 -0
- package/dist/components/Select/selectContext.js.map +1 -0
- package/dist/components/Select/selectItemTheme.cjs +39 -0
- package/dist/components/Select/selectItemTheme.d.cts +19 -0
- package/dist/components/Select/selectItemTheme.d.cts.map +1 -0
- package/dist/components/Select/selectItemTheme.d.ts +19 -0
- package/dist/components/Select/selectItemTheme.d.ts.map +1 -0
- package/dist/components/Select/selectItemTheme.js +39 -0
- package/dist/components/Select/selectItemTheme.js.map +1 -0
- package/dist/components/Select/types.cjs +1 -0
- package/dist/components/Select/types.d.cts +43 -0
- package/dist/components/Select/types.d.cts.map +1 -0
- package/dist/components/Select/types.d.ts +43 -0
- package/dist/components/Select/types.d.ts.map +1 -0
- package/dist/components/Select/types.js +1 -0
- package/dist/components/Switch.cjs +34 -12
- package/dist/components/Switch.d.cts.map +1 -1
- package/dist/components/Switch.d.ts.map +1 -1
- package/dist/components/Switch.js +36 -14
- package/dist/components/Switch.js.map +1 -1
- package/dist/components/UDSGestureProvider.cjs +4 -0
- package/dist/components/UDSGestureProvider.d.cts +3 -0
- package/dist/components/UDSGestureProvider.d.ts +3 -0
- package/dist/components/UDSGestureProvider.js +3 -0
- package/dist/components/UDSProvider.cjs +10 -5
- package/dist/components/UDSProvider.d.cts +15 -7
- package/dist/components/UDSProvider.d.cts.map +1 -1
- package/dist/components/UDSProvider.d.ts +15 -7
- package/dist/components/UDSProvider.d.ts.map +1 -1
- package/dist/components/UDSProvider.js +10 -6
- package/dist/components/UDSProvider.js.map +1 -1
- package/dist/components/internal/Overlay/OverlayPortal.js.map +1 -1
- package/dist/components/internal/Overlay/index.cjs +2 -0
- package/dist/components/internal/Overlay/index.d.cts +2 -1
- package/dist/components/internal/Overlay/index.d.ts +2 -1
- package/dist/components/internal/Overlay/index.js +2 -1
- package/dist/components/internal/Overlay/measureTrigger.cjs +35 -0
- package/dist/components/internal/Overlay/measureTrigger.d.cts +9 -0
- package/dist/components/internal/Overlay/measureTrigger.d.cts.map +1 -0
- package/dist/components/internal/Overlay/measureTrigger.d.ts +9 -0
- package/dist/components/internal/Overlay/measureTrigger.d.ts.map +1 -0
- package/dist/components/internal/Overlay/measureTrigger.js +36 -0
- package/dist/components/internal/Overlay/measureTrigger.js.map +1 -0
- package/dist/jest/mocks/styles.cjs +68 -10
- package/dist/jest/mocks/styles.d.cts +5 -2
- package/dist/jest/mocks/styles.d.cts.map +1 -1
- package/dist/jest/mocks/styles.d.ts +5 -2
- package/dist/jest/mocks/styles.d.ts.map +1 -1
- package/dist/jest/mocks/styles.js +66 -11
- package/dist/jest/mocks/styles.js.map +1 -1
- package/dist/jest/mocks/unistyles.cjs +16 -1
- package/dist/jest/mocks/unistyles.d.cts +56 -2
- package/dist/jest/mocks/unistyles.d.cts.map +1 -1
- package/dist/jest/mocks/unistyles.d.ts +56 -2
- package/dist/jest/mocks/unistyles.d.ts.map +1 -1
- package/dist/jest/mocks/unistyles.js +16 -1
- package/dist/jest/mocks/unistyles.js.map +1 -1
- package/dist/portal.cjs +1 -1
- package/dist/portal.js +1 -1
- package/dist/portal.js.map +1 -1
- package/dist/types/dist/index.d.cts +28 -1
- package/dist/types/dist/index.d.cts.map +1 -1
- package/dist/types/dist/index.d.ts +28 -1
- package/dist/types/dist/index.d.ts.map +1 -1
- package/generated/styles.cjs +125 -0
- package/generated/styles.d.ts +39 -0
- package/generated/styles.mjs +125 -0
- package/generated/unistyles.d.ts +108 -8
- package/package.json +21 -1
package/dist/bin/uds-mobile.mjs
CHANGED
|
@@ -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 ?
|
|
77
|
-
const backgroundColor = (0, react_native_unistyles_reanimated.useAnimatedVariantColor)(
|
|
78
|
-
const borderColor = (0, react_native_unistyles_reanimated.useAnimatedVariantColor)(
|
|
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
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
-
|
|
137
|
-
|
|
139
|
+
resolvedIconButtonStyles.root,
|
|
140
|
+
resolvedButtonStyles.root,
|
|
138
141
|
matchedControlDimensions,
|
|
139
|
-
|
|
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:
|
|
146
|
-
color: resolvedIconColor ??
|
|
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: [
|
|
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 ?
|
|
75
|
-
const backgroundColor = useAnimatedVariantColor(
|
|
76
|
-
const borderColor = useAnimatedVariantColor(
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
|
|
135
|
-
|
|
137
|
+
resolvedIconButtonStyles.root,
|
|
138
|
+
resolvedButtonStyles.root,
|
|
136
139
|
matchedControlDimensions,
|
|
137
|
-
|
|
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:
|
|
144
|
-
color: resolvedIconColor ??
|
|
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: [
|
|
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":";;;;;;;;;;;;;;AA8BA,SAAS,uBAAuB,QAA4B,OAAuB;AACjF;CACA,IAAI,CAAC,QACH,OAAO;CAET,IAAI,SAAS,GACX,OAAO;CAET,IAAI,SAAS,GACX,OAAO;CAGT,OAAO,OAAO,QAAQ,sDAAsD,GAAG,GAAG,GAAG,GAAG,MAAM;EAE5F,OAAO,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,KADZ,WAAW,EAAE,GAAG,OACS,QAAQ,EAAE,CAAC;GACrD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkEJ,MAAM,aAAa,KAAK,SAAS,WAAW,EAC1C,MACA,UAAU,WACV,OAAO,MACP,cAAc,WACd,WACA,SACA,UACA,OACA,oBACA,mBACA,iBAAiB,OACjB,WACA,YACA,KACA,GAAG,SACe;CAClB,MAAM,aAAa,YAAY;CAC/B,MAAM,gBAAgB,CAAC,kBAAkB,CAAC;CAC1C,MAAM,6BAA6B,SAAS,OAAO;CAEnD,MAAM,EAAE,UAAU,cAAc;CAChC,MAAM,EAAE,kBAAkB,cAAc,4BAA4B,OAAO,KAAK,EAAE,CAAC,OAAO,KAAK,CAAC;CAChG,MAAM,2BACJ,gBAAgB,IAAK;EAAE,QAAQ;EAAe,OAAO;EAAe,GAAa,KAAA;CAGnF,MAAM,CAAC,SAAS,cAAc,SAAS,MAAM;CAO7C,MAAM,2BAH0B,iBAAiB,YAAY,EAAE,MAAM,CAGb,IAAI;CAO5D,MAAM,uBALsB,aAAa,YAAY;EACnD;EACA,UAAU;EACV;EACD,CAC+C,IAAI;CAKpD,MAAM,2BAH0BA,OAAiB,YAAY,EAAE,OAAO,WAAW,CAGzB,IAAIA;CAE5D,MAAM,oBAAoB,YACrB,yBAAyB,WAAW,QACrC,KAAA;CAGJ,MAAM,kBAAkB,wBAAwB,qBAAqB,MAAM,kBAAkB;CAC7F,MAAM,cAAc,wBAAwB,qBAAqB,MAAM,cAAc;CAGrF,MAAM,gBAAgB,kBAAkB;CAGxC,MAAM,QAAQ,eAAuB,cAAc,KAAK;CAExD,MAAM,gBAAgB,aACnB,UAAU;EACT,WAAW,KAAK;EAChB,IAAI,eACF,MAAM,QAAQ,WAAW,cAAc,MAAM,qBAAqB;EAEpE,YAAY,MAAM;IAEpB;EAAC;EAAe;EAAO;EAAU,CAClC;CAED,MAAM,iBAAiB,aACpB,UAAU;EACT,WAAW,MAAM;EACjB,IAAI,eACF,MAAM,QAAQ,WAAW,cAAc,MAAM,qBAAqB;EAEpE,aAAa,MAAM;IAErB;EAAC;EAAe;EAAO;EAAW,CACnC;CAED,MAAM,YAAY,eAAe;EAAE,UAAU;EAAY,MAAM;EAAS,GAAG,CAAC,YAAY,QAAQ,CAAC;CAIjG,MAAM,gBAAgB,sBACd,WAAW,UAAU,IAAI,GAAG;EAAE,UAAU;EAAK,QAAQ,OAAO,OAAO,GAAG,GAAG,IAAK,EAAE;EAAE,CAAC,EACzF,CAAC,QAAQ,CACV;CAGD,MAAM,oBAAoB,uBAAuB;EAM/C,MAAM,gBAJa,cAAc,MAAM,WAIN,kBAAkB,QAAQ,iBAAiB;EAI5E,OAAO;GACL,WAAW,CAAC,EAAE,OAAO,MAAM,OAAO,CAAC;GAGnC,GAAI,8BAA8B;IAChC,iBAAiB,WAAW,gBAAgB,OAAO;KACjD,UAAU;KACV,QAAQ,OAAO,OAAO,GAAG,GAAG,IAAK,EAAE;KACpC,CAAC;IACF,aAAa,WAAW,YAAY,OAAO;KACzC,UAAU;KACV,QAAQ,OAAO,OAAO,GAAG,GAAG,IAAK,EAAE;KACpC,CAAC;IACH;GAED,GAAI,iBAAiB,EACnB,WAAW,uBAAuB,eAAe,cAAc,MAAM,EACtE;GACF;GACD;CAGF,OACE,oBAAC,mBAAD;EACO;EACL,UAAU;EACV,WAAW;EACX,YAAY;EACZ,eAAc;EACd,YAAW;EACX,gBAAe;EACf,UAAS;EACT,oBAAoB,UAAU,GAAG,sBAAsB,GAAG,aAAa;EACpD;EACnB,mBAAkB;EAClB,oBAAoB;EACpB,OAAO;GACL,yBAAyB;GACzB,qBAAqB;GACrB;GACA,yBAAyB;GACzB;GACA,OAAO,UAAU,aAAa,MAAM,EAAE,SAAS,CAAC,GAAG;GACpD;EACD,GAAI;YAEH,UACC,oBAAC,mBAAD;GACE,MAAM,yBAAyB,KAAK;GACpC,OAAO,qBAAqB,qBAAqB,KAAK;GACtD,CAAA,GAEF,oBAAC,MAAD;GACQ;GACN,SAAS;GACT,OAAO,CAAC,yBAAyB,MAAM,qBAAqB,KAAK;GACjE,qBAAqB;GACrB,CAAA;EAEc,CAAA;EAEtB;AAEF,WAAW,cAAc"}
|
|
@@ -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"}
|