@yahoo/uds-mobile 2.24.0-beta.8 → 2.24.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 +1 -0
- package/dist/components/Avatar/useAvatarFontScale.cjs +2 -2
- package/dist/components/Avatar/useAvatarFontScale.js +3 -3
- package/dist/components/Avatar/useAvatarFontScale.js.map +1 -1
- package/dist/components/Button.cjs +4 -3
- package/dist/components/Button.js +5 -4
- package/dist/components/Button.js.map +1 -1
- package/dist/components/Checkbox.cjs +2 -2
- package/dist/components/Checkbox.js +3 -3
- package/dist/components/Checkbox.js.map +1 -1
- package/dist/components/Chip/ChipBase.cjs +1 -1
- package/dist/components/Chip/ChipBase.js +2 -2
- package/dist/components/Chip/ChipBase.js.map +1 -1
- package/dist/components/Icon.cjs +6 -5
- package/dist/components/Icon.d.cts +2 -0
- package/dist/components/Icon.d.cts.map +1 -1
- package/dist/components/Icon.d.ts +2 -0
- package/dist/components/Icon.d.ts.map +1 -1
- package/dist/components/Icon.js +7 -6
- package/dist/components/Icon.js.map +1 -1
- package/dist/components/IconButton.cjs +1 -1
- package/dist/components/IconButton.js +2 -2
- package/dist/components/IconButton.js.map +1 -1
- package/dist/components/Input.cjs +2 -1
- package/dist/components/Input.js +3 -2
- package/dist/components/Input.js.map +1 -1
- package/dist/components/InputHelpText.cjs +1 -0
- package/dist/components/InputHelpText.js +1 -0
- package/dist/components/InputHelpText.js.map +1 -1
- package/dist/components/Link.cjs +6 -2
- package/dist/components/Link.js +7 -3
- package/dist/components/Link.js.map +1 -1
- package/dist/components/Pagination/PaginationEllipsis.cjs +1 -1
- package/dist/components/Pagination/PaginationEllipsis.js +2 -2
- package/dist/components/Pagination/PaginationEllipsis.js.map +1 -1
- package/dist/components/Pagination/usePaginationControlStyles.cjs +1 -1
- package/dist/components/Pagination/usePaginationControlStyles.js +2 -2
- package/dist/components/Pagination/usePaginationControlStyles.js.map +1 -1
- package/dist/components/Radio/Radio.cjs +1 -1
- package/dist/components/Radio/Radio.js +2 -2
- package/dist/components/Radio/Radio.js.map +1 -1
- package/dist/components/Select/Select.cjs +1 -0
- package/dist/components/Select/Select.js +1 -0
- package/dist/components/Select/Select.js.map +1 -1
- package/dist/components/Select/SelectItem.cjs +1 -4
- package/dist/components/Select/SelectItem.d.cts.map +1 -1
- package/dist/components/Select/SelectItem.d.ts.map +1 -1
- package/dist/components/Select/SelectItem.js +2 -5
- package/dist/components/Select/SelectItem.js.map +1 -1
- package/dist/components/Select/SelectTrigger.cjs +1 -4
- package/dist/components/Select/SelectTrigger.d.cts.map +1 -1
- package/dist/components/Select/SelectTrigger.d.ts.map +1 -1
- package/dist/components/Select/SelectTrigger.js +2 -5
- package/dist/components/Select/SelectTrigger.js.map +1 -1
- package/dist/components/Switch.cjs +4 -4
- package/dist/components/Switch.js +5 -5
- package/dist/components/Switch.js.map +1 -1
- package/dist/components/Tabs/Tab.cjs +1 -1
- package/dist/components/Tabs/Tab.js +2 -2
- package/dist/components/Tabs/Tab.js.map +1 -1
- package/dist/components/Text.cjs +1 -1
- package/dist/components/Text.js +2 -2
- package/dist/components/Text.js.map +1 -1
- package/dist/components/UDSProvider.cjs +6 -3
- package/dist/components/UDSProvider.d.cts.map +1 -1
- package/dist/components/UDSProvider.d.ts.map +1 -1
- package/dist/components/UDSProvider.js +8 -5
- package/dist/components/UDSProvider.js.map +1 -1
- package/dist/fontScaling/FontScalingContext.cjs +14 -3
- package/dist/fontScaling/FontScalingContext.d.cts +8 -2
- package/dist/fontScaling/FontScalingContext.d.cts.map +1 -1
- package/dist/fontScaling/FontScalingContext.d.ts +8 -2
- package/dist/fontScaling/FontScalingContext.d.ts.map +1 -1
- package/dist/fontScaling/FontScalingContext.js +14 -4
- package/dist/fontScaling/FontScalingContext.js.map +1 -1
- package/dist/fontScaling/androidNonlinearFontScaling.cjs +85 -9
- package/dist/fontScaling/androidNonlinearFontScaling.d.cts +2 -2
- package/dist/fontScaling/androidNonlinearFontScaling.d.cts.map +1 -1
- package/dist/fontScaling/androidNonlinearFontScaling.d.ts +2 -2
- package/dist/fontScaling/androidNonlinearFontScaling.d.ts.map +1 -1
- package/dist/fontScaling/androidNonlinearFontScaling.js +85 -9
- package/dist/fontScaling/androidNonlinearFontScaling.js.map +1 -1
- package/dist/fontScaling/useFontScale.cjs +18 -5
- package/dist/fontScaling/useFontScale.d.cts +6 -18
- package/dist/fontScaling/useFontScale.d.cts.map +1 -1
- package/dist/fontScaling/useFontScale.d.ts +6 -18
- package/dist/fontScaling/useFontScale.d.ts.map +1 -1
- package/dist/fontScaling/useFontScale.js +18 -6
- package/dist/fontScaling/useFontScale.js.map +1 -1
- package/dist/mobile/generated/fontMetrics.cjs +4 -4
- package/dist/mobile/generated/fontMetrics.js +4 -4
- package/dist/mobile/generated/fontMetrics.js.map +1 -1
- package/dist/types/dist/index.d.cts +4 -0
- package/dist/types/dist/index.d.cts.map +1 -1
- package/dist/types/dist/index.d.ts +4 -0
- package/dist/types/dist/index.d.ts.map +1 -1
- package/fonts/uds-icons.ttf +0 -0
- package/generated/fontMetrics.ts +4 -4
- package/package.json +2 -2
|
@@ -67,7 +67,7 @@ const IconButton = (0, react.memo)(function IconButton({ name, variant = "primar
|
|
|
67
67
|
const [pressed, setPressed] = (0, react.useState)(false);
|
|
68
68
|
const resolvedIconButtonStyles = generated_styles.iconButtonStyles.useVariants({ size }) ?? generated_styles.iconButtonStyles;
|
|
69
69
|
const iconBaseSize = (0, react.useMemo)(() => require_components_Button_buttonTheme.getIconButtonIconSize(theme, size), [theme, size]);
|
|
70
|
-
const iconScaleFactor = require_fontScaling_useFontScale.
|
|
70
|
+
const iconScaleFactor = require_fontScaling_useFontScale.useComponentFontScale(resolvedMaxFontSizeMultiplier, iconBaseSize);
|
|
71
71
|
const { minHeight, iconCellHeight, paddingVertical, hasExplicitHeight } = (0, react.useMemo)(() => require_components_Button_buttonTheme.getIconButtonControlMetrics(theme, size, iconScaleFactor), [
|
|
72
72
|
theme,
|
|
73
73
|
size,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*! © 2026 Yahoo, Inc. UDS Mobile v0.0.0-development */
|
|
2
2
|
import { SCALE_EFFECTS } from "../motion-tokens/dist/index.js";
|
|
3
3
|
import { BUTTON_SPRING_CONFIG } from "../motion.js";
|
|
4
|
-
import {
|
|
4
|
+
import { useComponentFontScale } from "../fontScaling/useFontScale.js";
|
|
5
5
|
import { useMaxFontSizeMultiplier } from "../fontScaling/useMaxFontSizeMultiplier.js";
|
|
6
6
|
import { getIconButtonControlMetrics, getIconButtonIconSize } from "./Button/buttonTheme.js";
|
|
7
7
|
import { Icon } from "./Icon.js";
|
|
@@ -65,7 +65,7 @@ const IconButton = memo(function IconButton({ name, variant = "primary", size =
|
|
|
65
65
|
const [pressed, setPressed] = useState(false);
|
|
66
66
|
const resolvedIconButtonStyles = iconButtonStyles.useVariants({ size }) ?? iconButtonStyles;
|
|
67
67
|
const iconBaseSize = useMemo(() => getIconButtonIconSize(theme, size), [theme, size]);
|
|
68
|
-
const iconScaleFactor =
|
|
68
|
+
const iconScaleFactor = useComponentFontScale(resolvedMaxFontSizeMultiplier, iconBaseSize);
|
|
69
69
|
const { minHeight, iconCellHeight, paddingVertical, hasExplicitHeight } = useMemo(() => getIconButtonControlMetrics(theme, size, iconScaleFactor), [
|
|
70
70
|
theme,
|
|
71
71
|
size,
|
|
@@ -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, 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 { useFontScale } from '../fontScaling/useFontScale';\nimport { useMaxFontSizeMultiplier } from '../fontScaling/useMaxFontSizeMultiplier';\nimport { BUTTON_SPRING_CONFIG, SCALE_EFFECTS } from '../motion';\nimport { getIconButtonControlMetrics, getIconButtonIconSize } 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 /**\n * Caps how far the icon and control grow with the OS text-size setting.\n * Set null to remove the cap; override app-wide via UDSFontScalingProvider.\n * @default 2\n */\n maxFontSizeMultiplier?: number | null;\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 maxFontSizeMultiplier,\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 resolvedMaxFontSizeMultiplier = useMaxFontSizeMultiplier('control', maxFontSizeMultiplier);\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 | undefined;\n const resolvedIconButtonStyles = variantIconButtonStyles ?? iconButtonStyles;\n\n const iconBaseSize = useMemo(() => getIconButtonIconSize(theme, size), [theme, size]);\n const iconScaleFactor = useFontScale(resolvedMaxFontSizeMultiplier, iconBaseSize);\n const { minHeight, iconCellHeight, paddingVertical, hasExplicitHeight } = useMemo(\n () => getIconButtonControlMetrics(theme, size, iconScaleFactor),\n [theme, size, iconScaleFactor],\n );\n const scaledIconSize = Math.round(iconBaseSize * iconScaleFactor);\n // Explicit token height (match-button-height) becomes a minimum with redistributed\n // padding so the safe icon cell fits and font scaling can grow the control.\n const controlDimensions = hasExplicitHeight\n ? ({\n height: 'auto',\n width: 'auto',\n minHeight,\n minWidth: minHeight,\n paddingVertical,\n } as const)\n : ({ minHeight, paddingVertical } as const);\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 | 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 | 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 controlDimensions,\n resolvedFoundationStyles.foundation,\n animatedRootStyle,\n typeof style === 'function' ? style({ pressed }) : style,\n ]}\n {...props}\n >\n {loading ? (\n <ActivityIndicator\n size={scaledIconSize}\n color={resolvedIconColor ?? resolvedButtonStyles.icon.color}\n />\n ) : (\n <Icon\n name={name}\n variant={iconVariant}\n style={[\n resolvedIconButtonStyles.icon,\n resolvedButtonStyles.icon,\n { lineHeight: iconCellHeight },\n ]}\n dangerouslySetColor={resolvedIconColor}\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\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 { useComponentFontScale } from '../fontScaling/useFontScale';\nimport { useMaxFontSizeMultiplier } from '../fontScaling/useMaxFontSizeMultiplier';\nimport { BUTTON_SPRING_CONFIG, SCALE_EFFECTS } from '../motion';\nimport { getIconButtonControlMetrics, getIconButtonIconSize } 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 /**\n * Caps how far the icon and control grow with the OS text-size setting.\n * Set null to remove the cap; override app-wide via UDSFontScalingProvider.\n * @default 2\n */\n maxFontSizeMultiplier?: number | null;\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 maxFontSizeMultiplier,\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 resolvedMaxFontSizeMultiplier = useMaxFontSizeMultiplier('control', maxFontSizeMultiplier);\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 | undefined;\n const resolvedIconButtonStyles = variantIconButtonStyles ?? iconButtonStyles;\n\n const iconBaseSize = useMemo(() => getIconButtonIconSize(theme, size), [theme, size]);\n const iconScaleFactor = useComponentFontScale(resolvedMaxFontSizeMultiplier, iconBaseSize);\n const { minHeight, iconCellHeight, paddingVertical, hasExplicitHeight } = useMemo(\n () => getIconButtonControlMetrics(theme, size, iconScaleFactor),\n [theme, size, iconScaleFactor],\n );\n const scaledIconSize = Math.round(iconBaseSize * iconScaleFactor);\n // Explicit token height (match-button-height) becomes a minimum with redistributed\n // padding so the safe icon cell fits and font scaling can grow the control.\n const controlDimensions = hasExplicitHeight\n ? ({\n height: 'auto',\n width: 'auto',\n minHeight,\n minWidth: minHeight,\n paddingVertical,\n } as const)\n : ({ minHeight, paddingVertical } as const);\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 | 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 | 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 controlDimensions,\n resolvedFoundationStyles.foundation,\n animatedRootStyle,\n typeof style === 'function' ? style({ pressed }) : style,\n ]}\n {...props}\n >\n {loading ? (\n <ActivityIndicator\n size={scaledIconSize}\n color={resolvedIconColor ?? resolvedButtonStyles.icon.color}\n />\n ) : (\n <Icon\n name={name}\n variant={iconVariant}\n style={[\n resolvedIconButtonStyles.icon,\n resolvedButtonStyles.icon,\n { lineHeight: iconCellHeight },\n ]}\n dangerouslySetColor={resolvedIconColor}\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n />\n )}\n </AnimatedPressable>\n );\n});\n\nIconButton.displayName = 'IconButton';\n\nexport { IconButton, type IconButtonProps };\n"],"mappings":"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"}
|
|
@@ -76,7 +76,7 @@ const Input = (0, react.memo)(function Input({ label, size = "md", startIcon, en
|
|
|
76
76
|
readonly: readOnly,
|
|
77
77
|
invalid: hasError
|
|
78
78
|
});
|
|
79
|
-
const fontScaleFactor = require_fontScaling_useFontScale.
|
|
79
|
+
const fontScaleFactor = require_fontScaling_useFontScale.useComponentFontScale(resolvedMaxFontSizeMultiplier, (typeof generated_styles.inputStyles.inputWrapperStatic.lineHeight === "number" ? generated_styles.inputStyles.inputWrapperStatic.lineHeight : void 0) ?? (typeof generated_styles.inputStyles.inputWrapperStatic.fontSize === "number" ? generated_styles.inputStyles.inputWrapperStatic.fontSize : void 0));
|
|
80
80
|
const computedPlaceholderColor = placeholderTextColor ?? generated_styles.inputStyles.inputPlaceholder.color;
|
|
81
81
|
const rootStyle = (0, react.useMemo)(() => [{
|
|
82
82
|
width,
|
|
@@ -125,6 +125,7 @@ const Input = (0, react.memo)(function Input({ label, size = "md", startIcon, en
|
|
|
125
125
|
alignItems: "flex-end",
|
|
126
126
|
spacingBottom: "2",
|
|
127
127
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_components_Text.Text, {
|
|
128
|
+
variant: "inherit",
|
|
128
129
|
maxFontSizeMultiplier: resolvedMaxFontSizeMultiplier,
|
|
129
130
|
style: generated_styles.inputStyles.label,
|
|
130
131
|
children: content
|
package/dist/components/Input.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/*! © 2026 Yahoo, Inc. UDS Mobile v0.0.0-development */
|
|
2
2
|
import { HStack } from "./HStack.js";
|
|
3
|
-
import {
|
|
3
|
+
import { useComponentFontScale } from "../fontScaling/useFontScale.js";
|
|
4
4
|
import { useMaxFontSizeMultiplier } from "../fontScaling/useMaxFontSizeMultiplier.js";
|
|
5
5
|
import { IconSlot } from "./IconSlot.js";
|
|
6
6
|
import { Text as Text$1 } from "./Text.js";
|
|
@@ -74,7 +74,7 @@ const Input = memo(function Input({ label, size = "md", startIcon, endIcon, help
|
|
|
74
74
|
readonly: readOnly,
|
|
75
75
|
invalid: hasError
|
|
76
76
|
});
|
|
77
|
-
const fontScaleFactor =
|
|
77
|
+
const fontScaleFactor = useComponentFontScale(resolvedMaxFontSizeMultiplier, (typeof inputStyles.inputWrapperStatic.lineHeight === "number" ? inputStyles.inputWrapperStatic.lineHeight : void 0) ?? (typeof inputStyles.inputWrapperStatic.fontSize === "number" ? inputStyles.inputWrapperStatic.fontSize : void 0));
|
|
78
78
|
const computedPlaceholderColor = placeholderTextColor ?? inputStyles.inputPlaceholder.color;
|
|
79
79
|
const rootStyle = useMemo(() => [{
|
|
80
80
|
width,
|
|
@@ -123,6 +123,7 @@ const Input = memo(function Input({ label, size = "md", startIcon, endIcon, help
|
|
|
123
123
|
alignItems: "flex-end",
|
|
124
124
|
spacingBottom: "2",
|
|
125
125
|
children: [/* @__PURE__ */ jsx(Text$1, {
|
|
126
|
+
variant: "inherit",
|
|
126
127
|
maxFontSizeMultiplier: resolvedMaxFontSizeMultiplier,
|
|
127
128
|
style: inputStyles.label,
|
|
128
129
|
children: content
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Input.js","names":["Text"],"sources":["../../src/components/Input.tsx"],"sourcesContent":["import type { UniversalInputProps } from '@yahoo/uds-types';\nimport { isFunction } from 'lodash-es';\nimport type { Ref } from 'react';\nimport { memo, useCallback, useId, useMemo, useState } from 'react';\nimport type { TextInputProps } from 'react-native';\nimport { TextInput, View } from 'react-native';\n\nimport { inputStyles } from '../../generated/styles';\nimport { useFontScale } from '../fontScaling/useFontScale';\nimport { useMaxFontSizeMultiplier } from '../fontScaling/useMaxFontSizeMultiplier';\nimport type { SizeProps } from '../types';\nimport { HStack } from './HStack';\nimport type { IconSlotType } from './IconSlot';\nimport { IconSlot } from './IconSlot';\nimport { InputHelpText } from './InputHelpText';\nimport { Text } from './Text';\nimport { VStack } from './VStack';\n\n/* -------------------------------------------------------------------------- */\n/* Types */\n/* -------------------------------------------------------------------------- */\n\ninterface InputProps\n extends\n Omit<UniversalInputProps<IconSlotType>, 'width'>,\n Omit<TextInputProps, 'style' | 'editable'>,\n Pick<SizeProps, 'width'> {\n /** Ref to the underlying TextInput element */\n ref?: Ref<TextInput>;\n /**\n * Caps the field text, labels, icons, and geometry at the control scale.\n * Set null to remove the cap. @default 2\n */\n maxFontSizeMultiplier?: number | null;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Input Component */\n/* -------------------------------------------------------------------------- */\n\n/**\n * **📦 An input that allows users to enter text and collect data.**\n *\n * @description\n * An input field is a component that takes text typed into it. It can also serve\n * as a way to display a selection and trigger a dropdown menu. Inputs are interactive\n * elements that users can click, tap, or otherwise engage with to collect data.\n *\n * @category Form\n * @platform mobile\n *\n * @example\n * ```tsx\n * import { Input } from '@yahoo/uds-mobile/Input';\n *\n * <Input label=\"Name\" placeholder=\"Enter your name\" required />\n * <Input label=\"Email\" startIcon=\"Mail\" helpText=\"We'll never share your email\" />\n * <Input label=\"Password\" secureTextEntry hasError helpText=\"Password is required\" />\n * ```\n *\n * @usage\n * - Forms: For collecting data like names, emails, passwords, etc.\n * - Search Bars: Allowing users to enter search queries\n * - Filters/Settings: When users need to specify preferences\n * - Feedback/Comments: Letting users leave reviews or comments\n *\n * @accessibility\n * - Label is automatically associated with the input\n * - Help text is announced as accessibility hint\n * - Error state is communicated to screen readers\n * - Disabled state prevents interaction\n *\n * @see {@link Checkbox} for boolean selections\n * @see {@link Radio} for single-select options\n */\nconst Input = memo(function Input({\n // Input props\n label,\n size = 'md',\n startIcon,\n endIcon,\n helpText,\n helperTextIcon,\n hasError,\n disabled,\n readOnly,\n required,\n // Size props\n width = '100%',\n // TextInput props\n defaultValue,\n value: controlledValue,\n onChangeText,\n onFocus,\n onBlur,\n placeholder,\n placeholderTextColor,\n maxFontSizeMultiplier,\n ref,\n ...textInputProps\n}: InputProps) {\n const generatedId = useId();\n const uid = `uds-input-${generatedId}`;\n const resolvedMaxFontSizeMultiplier = useMaxFontSizeMultiplier('control', maxFontSizeMultiplier);\n\n /* --------------------------------- State ---------------------------------- */\n const [internalValue, setInternalValue] = useState(defaultValue ?? '');\n const [isFocused, setIsFocused] = useState(false);\n\n // Support both controlled and uncontrolled modes\n const isControlled = controlledValue !== undefined;\n const value = isControlled ? controlledValue : internalValue;\n const valueState = value ? 'filled' : 'empty';\n\n /* -------------------------------- Handlers -------------------------------- */\n const handleChangeText = useCallback(\n (text: string) => {\n if (!isControlled) {\n setInternalValue(text);\n }\n onChangeText?.(text);\n },\n [isControlled, onChangeText],\n );\n\n const handleFocus = useCallback<NonNullable<TextInputProps['onFocus']>>(\n (e) => {\n setIsFocused(true);\n onFocus?.(e);\n },\n [onFocus],\n );\n\n const handleBlur = useCallback<NonNullable<TextInputProps['onBlur']>>(\n (e) => {\n setIsFocused(false);\n onBlur?.(e);\n },\n [onBlur],\n );\n\n /* --------------------------------- Styles --------------------------------- */\n inputStyles.useVariants({\n size,\n value: valueState,\n pressed: isFocused,\n readonly: readOnly,\n invalid: hasError,\n });\n const fontScaleFactor = useFontScale(\n resolvedMaxFontSizeMultiplier,\n (typeof inputStyles.inputWrapperStatic.lineHeight === 'number'\n ? inputStyles.inputWrapperStatic.lineHeight\n : undefined) ??\n (typeof inputStyles.inputWrapperStatic.fontSize === 'number'\n ? inputStyles.inputWrapperStatic.fontSize\n : undefined),\n );\n\n // Get placeholder color from theme styles\n const computedPlaceholderColor = placeholderTextColor ?? inputStyles.inputPlaceholder.color;\n\n const rootStyle = useMemo(() => [{ width, opacity: disabled ? 0.5 : 1 }], [width, disabled]);\n\n // `inputWrapper` carries only background/border colors; the size-axis spacing\n // (`inputWrapperStatic`) and border width/radius (`inputWrapperDynamic`) live\n // on their own layers after the wrapper split. All three must be applied to\n // the wrapper View — matching the web component, which drives the static and\n // dynamic layers via separate variants — or the input loses its padding, gap,\n // and border sizing.\n const inputWrapperStyle = useMemo(() => {\n const { gap, paddingVertical } = inputStyles.inputWrapperStatic;\n const scaledMetrics =\n fontScaleFactor === 1\n ? undefined\n : {\n ...(typeof gap === 'number' && { gap: Math.round(gap * fontScaleFactor) }),\n ...(typeof paddingVertical === 'number' && {\n paddingVertical: Math.round(paddingVertical * fontScaleFactor),\n }),\n };\n\n return [\n inputStyles.inputWrapper,\n inputStyles.inputWrapperStatic,\n inputStyles.inputWrapperDynamic,\n scaledMetrics,\n { flexDirection: 'row' as const, alignItems: 'center' as const },\n ];\n }, [\n fontScaleFactor,\n inputStyles.inputWrapper,\n inputStyles.inputWrapperStatic,\n inputStyles.inputWrapperDynamic,\n ]);\n\n // Android-specific fixes: TextInput on Android has rendering issues with text visibility\n // - includeFontPadding: false removes Android's extra font padding that can clip text\n // - textAlignVertical: 'center' ensures text is vertically centered in the input\n // - paddingVertical: 0 removes default padding that interferes with flex layout\n const textInputStyle = useMemo(() => {\n const inputHeight = inputStyles.input.height;\n return [\n inputStyles.input,\n typeof inputHeight === 'number'\n ? { height: Math.round(inputHeight * fontScaleFactor) }\n : undefined,\n {\n flex: 1,\n includeFontPadding: false,\n textAlignVertical: 'center' as const,\n paddingVertical: 0,\n },\n ];\n }, [fontScaleFactor, inputStyles.input]);\n\n /* ---------------------------- Render Helpers ------------------------------ */\n const labelContent = useMemo(() => {\n if (!label) {\n return null;\n }\n const content = isFunction(label) ? label() : label;\n return (\n <HStack columnGap=\"1\" alignItems=\"flex-end\" spacingBottom=\"2\">\n <Text maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier} style={inputStyles.label}>\n {content}\n </Text>\n {required && (\n <Text\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n style={inputStyles.labelRequired}\n >\n *\n </Text>\n )}\n </HStack>\n );\n }, [\n label,\n required,\n resolvedMaxFontSizeMultiplier,\n inputStyles.label,\n inputStyles.labelRequired,\n ]);\n\n const startIconContent = useMemo(() => {\n if (!startIcon) {\n return null;\n }\n return (\n <IconSlot\n icon={startIcon}\n variant=\"outline\"\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n style={inputStyles.startIcon}\n />\n );\n }, [startIcon, resolvedMaxFontSizeMultiplier, inputStyles.startIcon]);\n\n const endIconContent = useMemo(() => {\n if (!endIcon) {\n return null;\n }\n return (\n <IconSlot\n icon={endIcon}\n variant=\"outline\"\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n style={inputStyles.endIcon}\n />\n );\n }, [endIcon, resolvedMaxFontSizeMultiplier, inputStyles.endIcon]);\n\n const helpTextContent = useMemo(() => {\n if (!helpText) {\n return null;\n }\n const content = isFunction(helpText) ? helpText() : helpText;\n return (\n <InputHelpText\n startIcon={helperTextIcon}\n size={size}\n isFilled={valueState === 'filled'}\n disabled={disabled}\n readOnly={readOnly}\n hasError={hasError}\n pressed={isFocused}\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n >\n {content}\n </InputHelpText>\n );\n }, [\n disabled,\n hasError,\n helpText,\n helperTextIcon,\n isFocused,\n readOnly,\n resolvedMaxFontSizeMultiplier,\n size,\n valueState,\n ]);\n\n /* --------------------------------- Render --------------------------------- */\n return (\n <VStack style={rootStyle}>\n {labelContent}\n\n <View\n style={inputWrapperStyle}\n accessible\n accessibilityRole=\"none\"\n accessibilityLabel={typeof label === 'string' ? label : undefined}\n >\n {startIconContent}\n\n <TextInput\n ref={ref}\n nativeID={uid}\n value={value}\n onChangeText={handleChangeText}\n onFocus={handleFocus}\n onBlur={handleBlur}\n placeholder={placeholder}\n placeholderTextColor={computedPlaceholderColor}\n editable={!disabled && !readOnly}\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n style={textInputStyle}\n accessibilityLabel={typeof label === 'string' ? label : undefined}\n accessibilityHint={typeof helpText === 'string' ? helpText : undefined}\n accessibilityState={{ disabled }}\n {...textInputProps}\n />\n\n {endIconContent}\n </View>\n\n {helpTextContent}\n </VStack>\n );\n});\n\nInput.displayName = 'Input';\n\nexport { Input, type InputProps };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Input.js","names":["Text"],"sources":["../../src/components/Input.tsx"],"sourcesContent":["import type { UniversalInputProps } from '@yahoo/uds-types';\nimport { isFunction } from 'lodash-es';\nimport type { Ref } from 'react';\nimport { memo, useCallback, useId, useMemo, useState } from 'react';\nimport type { TextInputProps } from 'react-native';\nimport { TextInput, View } from 'react-native';\n\nimport { inputStyles } from '../../generated/styles';\nimport { useComponentFontScale } from '../fontScaling/useFontScale';\nimport { useMaxFontSizeMultiplier } from '../fontScaling/useMaxFontSizeMultiplier';\nimport type { SizeProps } from '../types';\nimport { HStack } from './HStack';\nimport type { IconSlotType } from './IconSlot';\nimport { IconSlot } from './IconSlot';\nimport { InputHelpText } from './InputHelpText';\nimport { Text } from './Text';\nimport { VStack } from './VStack';\n\n/* -------------------------------------------------------------------------- */\n/* Types */\n/* -------------------------------------------------------------------------- */\n\ninterface InputProps\n extends\n Omit<UniversalInputProps<IconSlotType>, 'width'>,\n Omit<TextInputProps, 'style' | 'editable'>,\n Pick<SizeProps, 'width'> {\n /** Ref to the underlying TextInput element */\n ref?: Ref<TextInput>;\n /**\n * Caps the field text, labels, icons, and geometry at the control scale.\n * Set null to remove the cap. @default 2\n */\n maxFontSizeMultiplier?: number | null;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Input Component */\n/* -------------------------------------------------------------------------- */\n\n/**\n * **📦 An input that allows users to enter text and collect data.**\n *\n * @description\n * An input field is a component that takes text typed into it. It can also serve\n * as a way to display a selection and trigger a dropdown menu. Inputs are interactive\n * elements that users can click, tap, or otherwise engage with to collect data.\n *\n * @category Form\n * @platform mobile\n *\n * @example\n * ```tsx\n * import { Input } from '@yahoo/uds-mobile/Input';\n *\n * <Input label=\"Name\" placeholder=\"Enter your name\" required />\n * <Input label=\"Email\" startIcon=\"Mail\" helpText=\"We'll never share your email\" />\n * <Input label=\"Password\" secureTextEntry hasError helpText=\"Password is required\" />\n * ```\n *\n * @usage\n * - Forms: For collecting data like names, emails, passwords, etc.\n * - Search Bars: Allowing users to enter search queries\n * - Filters/Settings: When users need to specify preferences\n * - Feedback/Comments: Letting users leave reviews or comments\n *\n * @accessibility\n * - Label is automatically associated with the input\n * - Help text is announced as accessibility hint\n * - Error state is communicated to screen readers\n * - Disabled state prevents interaction\n *\n * @see {@link Checkbox} for boolean selections\n * @see {@link Radio} for single-select options\n */\nconst Input = memo(function Input({\n // Input props\n label,\n size = 'md',\n startIcon,\n endIcon,\n helpText,\n helperTextIcon,\n hasError,\n disabled,\n readOnly,\n required,\n // Size props\n width = '100%',\n // TextInput props\n defaultValue,\n value: controlledValue,\n onChangeText,\n onFocus,\n onBlur,\n placeholder,\n placeholderTextColor,\n maxFontSizeMultiplier,\n ref,\n ...textInputProps\n}: InputProps) {\n const generatedId = useId();\n const uid = `uds-input-${generatedId}`;\n const resolvedMaxFontSizeMultiplier = useMaxFontSizeMultiplier('control', maxFontSizeMultiplier);\n\n /* --------------------------------- State ---------------------------------- */\n const [internalValue, setInternalValue] = useState(defaultValue ?? '');\n const [isFocused, setIsFocused] = useState(false);\n\n // Support both controlled and uncontrolled modes\n const isControlled = controlledValue !== undefined;\n const value = isControlled ? controlledValue : internalValue;\n const valueState = value ? 'filled' : 'empty';\n\n /* -------------------------------- Handlers -------------------------------- */\n const handleChangeText = useCallback(\n (text: string) => {\n if (!isControlled) {\n setInternalValue(text);\n }\n onChangeText?.(text);\n },\n [isControlled, onChangeText],\n );\n\n const handleFocus = useCallback<NonNullable<TextInputProps['onFocus']>>(\n (e) => {\n setIsFocused(true);\n onFocus?.(e);\n },\n [onFocus],\n );\n\n const handleBlur = useCallback<NonNullable<TextInputProps['onBlur']>>(\n (e) => {\n setIsFocused(false);\n onBlur?.(e);\n },\n [onBlur],\n );\n\n /* --------------------------------- Styles --------------------------------- */\n inputStyles.useVariants({\n size,\n value: valueState,\n pressed: isFocused,\n readonly: readOnly,\n invalid: hasError,\n });\n const fontScaleFactor = useComponentFontScale(\n resolvedMaxFontSizeMultiplier,\n (typeof inputStyles.inputWrapperStatic.lineHeight === 'number'\n ? inputStyles.inputWrapperStatic.lineHeight\n : undefined) ??\n (typeof inputStyles.inputWrapperStatic.fontSize === 'number'\n ? inputStyles.inputWrapperStatic.fontSize\n : undefined),\n );\n\n // Get placeholder color from theme styles\n const computedPlaceholderColor = placeholderTextColor ?? inputStyles.inputPlaceholder.color;\n\n const rootStyle = useMemo(() => [{ width, opacity: disabled ? 0.5 : 1 }], [width, disabled]);\n\n // `inputWrapper` carries only background/border colors; the size-axis spacing\n // (`inputWrapperStatic`) and border width/radius (`inputWrapperDynamic`) live\n // on their own layers after the wrapper split. All three must be applied to\n // the wrapper View — matching the web component, which drives the static and\n // dynamic layers via separate variants — or the input loses its padding, gap,\n // and border sizing.\n const inputWrapperStyle = useMemo(() => {\n const { gap, paddingVertical } = inputStyles.inputWrapperStatic;\n const scaledMetrics =\n fontScaleFactor === 1\n ? undefined\n : {\n ...(typeof gap === 'number' && { gap: Math.round(gap * fontScaleFactor) }),\n ...(typeof paddingVertical === 'number' && {\n paddingVertical: Math.round(paddingVertical * fontScaleFactor),\n }),\n };\n\n return [\n inputStyles.inputWrapper,\n inputStyles.inputWrapperStatic,\n inputStyles.inputWrapperDynamic,\n scaledMetrics,\n { flexDirection: 'row' as const, alignItems: 'center' as const },\n ];\n }, [\n fontScaleFactor,\n inputStyles.inputWrapper,\n inputStyles.inputWrapperStatic,\n inputStyles.inputWrapperDynamic,\n ]);\n\n // Android-specific fixes: TextInput on Android has rendering issues with text visibility\n // - includeFontPadding: false removes Android's extra font padding that can clip text\n // - textAlignVertical: 'center' ensures text is vertically centered in the input\n // - paddingVertical: 0 removes default padding that interferes with flex layout\n const textInputStyle = useMemo(() => {\n const inputHeight = inputStyles.input.height;\n return [\n inputStyles.input,\n typeof inputHeight === 'number'\n ? { height: Math.round(inputHeight * fontScaleFactor) }\n : undefined,\n {\n flex: 1,\n includeFontPadding: false,\n textAlignVertical: 'center' as const,\n paddingVertical: 0,\n },\n ];\n }, [fontScaleFactor, inputStyles.input]);\n\n /* ---------------------------- Render Helpers ------------------------------ */\n const labelContent = useMemo(() => {\n if (!label) {\n return null;\n }\n const content = isFunction(label) ? label() : label;\n return (\n <HStack columnGap=\"1\" alignItems=\"flex-end\" spacingBottom=\"2\">\n <Text\n variant=\"inherit\"\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n style={inputStyles.label}\n >\n {content}\n </Text>\n {required && (\n // Default variant, not \"inherit\": the `labelRequired` layer carries\n // only color, so inherit would leave the asterisk in the platform\n // system font instead of its longstanding body1 typography.\n <Text\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n style={inputStyles.labelRequired}\n >\n *\n </Text>\n )}\n </HStack>\n );\n }, [\n label,\n required,\n resolvedMaxFontSizeMultiplier,\n inputStyles.label,\n inputStyles.labelRequired,\n ]);\n\n const startIconContent = useMemo(() => {\n if (!startIcon) {\n return null;\n }\n return (\n <IconSlot\n icon={startIcon}\n variant=\"outline\"\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n style={inputStyles.startIcon}\n />\n );\n }, [startIcon, resolvedMaxFontSizeMultiplier, inputStyles.startIcon]);\n\n const endIconContent = useMemo(() => {\n if (!endIcon) {\n return null;\n }\n return (\n <IconSlot\n icon={endIcon}\n variant=\"outline\"\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n style={inputStyles.endIcon}\n />\n );\n }, [endIcon, resolvedMaxFontSizeMultiplier, inputStyles.endIcon]);\n\n const helpTextContent = useMemo(() => {\n if (!helpText) {\n return null;\n }\n const content = isFunction(helpText) ? helpText() : helpText;\n return (\n <InputHelpText\n startIcon={helperTextIcon}\n size={size}\n isFilled={valueState === 'filled'}\n disabled={disabled}\n readOnly={readOnly}\n hasError={hasError}\n pressed={isFocused}\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n >\n {content}\n </InputHelpText>\n );\n }, [\n disabled,\n hasError,\n helpText,\n helperTextIcon,\n isFocused,\n readOnly,\n resolvedMaxFontSizeMultiplier,\n size,\n valueState,\n ]);\n\n /* --------------------------------- Render --------------------------------- */\n return (\n <VStack style={rootStyle}>\n {labelContent}\n\n <View\n style={inputWrapperStyle}\n accessible\n accessibilityRole=\"none\"\n accessibilityLabel={typeof label === 'string' ? label : undefined}\n >\n {startIconContent}\n\n <TextInput\n ref={ref}\n nativeID={uid}\n value={value}\n onChangeText={handleChangeText}\n onFocus={handleFocus}\n onBlur={handleBlur}\n placeholder={placeholder}\n placeholderTextColor={computedPlaceholderColor}\n editable={!disabled && !readOnly}\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n style={textInputStyle}\n accessibilityLabel={typeof label === 'string' ? label : undefined}\n accessibilityHint={typeof helpText === 'string' ? helpText : undefined}\n accessibilityState={{ disabled }}\n {...textInputProps}\n />\n\n {endIconContent}\n </View>\n\n {helpTextContent}\n </VStack>\n );\n});\n\nInput.displayName = 'Input';\n\nexport { Input, type InputProps };\n"],"mappings":"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"}
|
|
@@ -37,6 +37,7 @@ const InputHelpText = (0, react.memo)(function InputHelpText({ children, startIc
|
|
|
37
37
|
...resolvedIconProps
|
|
38
38
|
}),
|
|
39
39
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_components_Text.Text, {
|
|
40
|
+
variant: "inherit",
|
|
40
41
|
maxFontSizeMultiplier: resolvedMaxFontSizeMultiplier,
|
|
41
42
|
style: generated_styles.inputStyles.helperText,
|
|
42
43
|
children
|
|
@@ -35,6 +35,7 @@ const InputHelpText = memo(function InputHelpText({ children, startIcon, endIcon
|
|
|
35
35
|
...resolvedIconProps
|
|
36
36
|
}),
|
|
37
37
|
/* @__PURE__ */ jsx(Text, {
|
|
38
|
+
variant: "inherit",
|
|
38
39
|
maxFontSizeMultiplier: resolvedMaxFontSizeMultiplier,
|
|
39
40
|
style: inputStyles.helperText,
|
|
40
41
|
children
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InputHelpText.js","names":[],"sources":["../../src/components/InputHelpText.tsx"],"sourcesContent":["import type { InputSize } from '@yahoo/uds-types';\nimport type { ReactNode, Ref } from 'react';\nimport { memo, useMemo } from 'react';\nimport type { View } from 'react-native';\n\nimport { inputStyles } from '../../generated/styles';\nimport { useMaxFontSizeMultiplier } from '../fontScaling/useMaxFontSizeMultiplier';\nimport type { HStackProps } from './HStack';\nimport { HStack } from './HStack';\nimport type { IconSlotProps, IconSlotType } from './IconSlot';\nimport { IconSlot } from './IconSlot';\nimport type { TextProps } from './Text';\nimport { Text } from './Text';\n\ninterface InputHelpTextProps extends Omit<HStackProps, 'children' | 'ref'> {\n /** Ref to the underlying root View. */\n ref?: Ref<View>;\n /** Helper text content. */\n children?: ReactNode;\n /** Icon to render before the help text. */\n startIcon?: IconSlotType;\n /** Icon to render after the help text. */\n endIcon?: IconSlotType;\n /** Props to apply to both helper icons. */\n iconProps?: Omit<IconSlotProps, 'icon' | 'ref'>;\n /** Input size used to resolve configured helper text styles. */\n size?: InputSize;\n /** Whether the associated input has a value. */\n isFilled?: boolean;\n /** Whether the associated input is disabled. */\n disabled?: boolean;\n /** Whether the associated input is read-only. */\n readOnly?: boolean;\n /** Whether the associated input has a validation error. */\n hasError?: boolean;\n /** Whether the associated input is focused or pressed. */\n pressed?: boolean;\n /** Caps helper text and icons at the control scale. Set null to remove the cap. @default 2 */\n maxFontSizeMultiplier?: number | null;\n}\n\nconst InputHelpText = memo(function InputHelpText({\n children,\n startIcon,\n endIcon,\n iconProps,\n size = 'md',\n isFilled = false,\n disabled: _disabled,\n readOnly,\n hasError,\n pressed,\n maxFontSizeMultiplier,\n ref,\n ...hStackProps\n}: InputHelpTextProps) {\n const { style: iconStyle, variant = 'outline', ...resolvedIconProps } = iconProps ?? {};\n const value = isFilled ? 'filled' : 'empty';\n const resolvedMaxFontSizeMultiplier = useMaxFontSizeMultiplier('control', maxFontSizeMultiplier);\n\n inputStyles.useVariants({\n size,\n value,\n pressed,\n readonly: readOnly,\n invalid: hasError,\n });\n\n const resolvedIconStyle = useMemo(\n () => [inputStyles.helperIcon, iconStyle],\n [iconStyle, inputStyles.helperIcon],\n );\n\n if (children === null || children === undefined || children === false) {\n return null;\n }\n\n return (\n <HStack ref={ref} columnGap=\"1\" alignItems=\"center\" spacingTop=\"2\" {...hStackProps}>\n {startIcon && (\n <IconSlot\n icon={startIcon}\n variant={variant}\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n style={resolvedIconStyle}\n {...resolvedIconProps}\n />\n )}\n\n <Text\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n style={inputStyles.helperText as TextProps['style']}\n >\n {children}\n </Text>\n\n {endIcon && (\n <IconSlot\n icon={endIcon}\n variant={variant}\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n style={resolvedIconStyle}\n {...resolvedIconProps}\n />\n )}\n </HStack>\n );\n});\n\nInputHelpText.displayName = 'InputHelpText';\n\nexport { InputHelpText, type InputHelpTextProps };\n"],"mappings":";;;;;;;;;AAyCA,MAAM,gBAAgB,KAAK,SAAS,cAAc,EAChD,UACA,WACA,SACA,WACA,OAAO,MACP,WAAW,OACX,UAAU,WACV,UACA,UACA,SACA,uBACA,KACA,GAAG,eACkB;CACrB,MAAM,EAAE,OAAO,WAAW,UAAU,WAAW,GAAG,sBAAsB,aAAa,EAAE;CACvF,MAAM,QAAQ,WAAW,WAAW;CACpC,MAAM,gCAAgC,yBAAyB,WAAW,sBAAsB;CAEhG,YAAY,YAAY;EACtB;EACA;EACA;EACA,UAAU;EACV,SAAS;EACV,CAAC;CAEF,MAAM,oBAAoB,cAClB,CAAC,YAAY,YAAY,UAAU,EACzC,CAAC,WAAW,YAAY,WAAW,CACpC;CAED,IAAI,aAAa,QAAQ,aAAa,KAAA,KAAa,aAAa,OAC9D,OAAO;CAGT,OACE,qBAAC,QAAD;EAAa;EAAK,WAAU;EAAI,YAAW;EAAS,YAAW;EAAI,GAAI;YAAvE;GACG,aACC,oBAAC,UAAD;IACE,MAAM;IACG;IACT,uBAAuB;IACvB,OAAO;IACP,GAAI;IACJ,CAAA;GAGJ,oBAAC,MAAD;IACE,uBAAuB;IACvB,OAAO,YAAY;IAElB;IACI,CAAA;GAEN,WACC,oBAAC,UAAD;IACE,MAAM;IACG;IACT,uBAAuB;IACvB,OAAO;IACP,GAAI;IACJ,CAAA;GAEG;;EAEX;AAEF,cAAc,cAAc"}
|
|
1
|
+
{"version":3,"file":"InputHelpText.js","names":[],"sources":["../../src/components/InputHelpText.tsx"],"sourcesContent":["import type { InputSize } from '@yahoo/uds-types';\nimport type { ReactNode, Ref } from 'react';\nimport { memo, useMemo } from 'react';\nimport type { View } from 'react-native';\n\nimport { inputStyles } from '../../generated/styles';\nimport { useMaxFontSizeMultiplier } from '../fontScaling/useMaxFontSizeMultiplier';\nimport type { HStackProps } from './HStack';\nimport { HStack } from './HStack';\nimport type { IconSlotProps, IconSlotType } from './IconSlot';\nimport { IconSlot } from './IconSlot';\nimport type { TextProps } from './Text';\nimport { Text } from './Text';\n\ninterface InputHelpTextProps extends Omit<HStackProps, 'children' | 'ref'> {\n /** Ref to the underlying root View. */\n ref?: Ref<View>;\n /** Helper text content. */\n children?: ReactNode;\n /** Icon to render before the help text. */\n startIcon?: IconSlotType;\n /** Icon to render after the help text. */\n endIcon?: IconSlotType;\n /** Props to apply to both helper icons. */\n iconProps?: Omit<IconSlotProps, 'icon' | 'ref'>;\n /** Input size used to resolve configured helper text styles. */\n size?: InputSize;\n /** Whether the associated input has a value. */\n isFilled?: boolean;\n /** Whether the associated input is disabled. */\n disabled?: boolean;\n /** Whether the associated input is read-only. */\n readOnly?: boolean;\n /** Whether the associated input has a validation error. */\n hasError?: boolean;\n /** Whether the associated input is focused or pressed. */\n pressed?: boolean;\n /** Caps helper text and icons at the control scale. Set null to remove the cap. @default 2 */\n maxFontSizeMultiplier?: number | null;\n}\n\nconst InputHelpText = memo(function InputHelpText({\n children,\n startIcon,\n endIcon,\n iconProps,\n size = 'md',\n isFilled = false,\n disabled: _disabled,\n readOnly,\n hasError,\n pressed,\n maxFontSizeMultiplier,\n ref,\n ...hStackProps\n}: InputHelpTextProps) {\n const { style: iconStyle, variant = 'outline', ...resolvedIconProps } = iconProps ?? {};\n const value = isFilled ? 'filled' : 'empty';\n const resolvedMaxFontSizeMultiplier = useMaxFontSizeMultiplier('control', maxFontSizeMultiplier);\n\n inputStyles.useVariants({\n size,\n value,\n pressed,\n readonly: readOnly,\n invalid: hasError,\n });\n\n const resolvedIconStyle = useMemo(\n () => [inputStyles.helperIcon, iconStyle],\n [iconStyle, inputStyles.helperIcon],\n );\n\n if (children === null || children === undefined || children === false) {\n return null;\n }\n\n return (\n <HStack ref={ref} columnGap=\"1\" alignItems=\"center\" spacingTop=\"2\" {...hStackProps}>\n {startIcon && (\n <IconSlot\n icon={startIcon}\n variant={variant}\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n style={resolvedIconStyle}\n {...resolvedIconProps}\n />\n )}\n\n <Text\n variant=\"inherit\"\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n style={inputStyles.helperText as TextProps['style']}\n >\n {children}\n </Text>\n\n {endIcon && (\n <IconSlot\n icon={endIcon}\n variant={variant}\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n style={resolvedIconStyle}\n {...resolvedIconProps}\n />\n )}\n </HStack>\n );\n});\n\nInputHelpText.displayName = 'InputHelpText';\n\nexport { InputHelpText, type InputHelpTextProps };\n"],"mappings":";;;;;;;;;AAyCA,MAAM,gBAAgB,KAAK,SAAS,cAAc,EAChD,UACA,WACA,SACA,WACA,OAAO,MACP,WAAW,OACX,UAAU,WACV,UACA,UACA,SACA,uBACA,KACA,GAAG,eACkB;CACrB,MAAM,EAAE,OAAO,WAAW,UAAU,WAAW,GAAG,sBAAsB,aAAa,EAAE;CACvF,MAAM,QAAQ,WAAW,WAAW;CACpC,MAAM,gCAAgC,yBAAyB,WAAW,sBAAsB;CAEhG,YAAY,YAAY;EACtB;EACA;EACA;EACA,UAAU;EACV,SAAS;EACV,CAAC;CAEF,MAAM,oBAAoB,cAClB,CAAC,YAAY,YAAY,UAAU,EACzC,CAAC,WAAW,YAAY,WAAW,CACpC;CAED,IAAI,aAAa,QAAQ,aAAa,KAAA,KAAa,aAAa,OAC9D,OAAO;CAGT,OACE,qBAAC,QAAD;EAAa;EAAK,WAAU;EAAI,YAAW;EAAS,YAAW;EAAI,GAAI;YAAvE;GACG,aACC,oBAAC,UAAD;IACE,MAAM;IACG;IACT,uBAAuB;IACvB,OAAO;IACP,GAAI;IACJ,CAAA;GAGJ,oBAAC,MAAD;IACE,SAAQ;IACR,uBAAuB;IACvB,OAAO,YAAY;IAElB;IACI,CAAA;GAEN,WACC,oBAAC,UAAD;IACE,MAAM;IACG;IACT,uBAAuB;IACvB,OAAO;IACP,GAAI;IACJ,CAAA;GAEG;;EAEX;AAEF,cAAc,cAAc"}
|
package/dist/components/Link.cjs
CHANGED
|
@@ -55,10 +55,10 @@ const noUnderline = { textDecorationLine: "none" };
|
|
|
55
55
|
*/
|
|
56
56
|
const Link = (0, react.memo)(function Link({ children, variant = "primary", textVariant, alwaysUnderline = false, startIcon, endIcon, maxFontSizeMultiplier, onPress, style, ref, ...rest }) {
|
|
57
57
|
const [pressed, setPressed] = (0, react.useState)(false);
|
|
58
|
-
const
|
|
58
|
+
const dynamicTypeRampEnabled = require_fontScaling_FontScalingContext.useDynamicTypeRampEnabled();
|
|
59
59
|
const resolvedTextVariant = textVariant;
|
|
60
60
|
const resolvedMaxFontSizeMultiplier = require_fontScaling_useMaxFontSizeMultiplier.useMaxFontSizeMultiplier(resolvedTextVariant, maxFontSizeMultiplier);
|
|
61
|
-
const dynamicTypeRamp =
|
|
61
|
+
const dynamicTypeRamp = dynamicTypeRampEnabled && resolvedTextVariant ? require_fontScaling_constants.DEFAULT_DYNAMIC_TYPE_RAMPS[resolvedTextVariant] : void 0;
|
|
62
62
|
const handlePressIn = (0, react.useCallback)(() => {
|
|
63
63
|
setPressed(true);
|
|
64
64
|
}, []);
|
|
@@ -140,7 +140,9 @@ const Link = (0, react.memo)(function Link({ children, variant = "primary", text
|
|
|
140
140
|
startIcon && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_components_IconSlot.IconSlot, {
|
|
141
141
|
icon: startIcon,
|
|
142
142
|
variant: "outline",
|
|
143
|
+
inheritFontScaling: true,
|
|
143
144
|
maxFontSizeMultiplier: resolvedMaxFontSizeMultiplier,
|
|
145
|
+
dynamicTypeRamp,
|
|
144
146
|
style: startIconStyles
|
|
145
147
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Text, {
|
|
146
148
|
style: noUnderline,
|
|
@@ -153,7 +155,9 @@ const Link = (0, react.memo)(function Link({ children, variant = "primary", text
|
|
|
153
155
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_components_IconSlot.IconSlot, {
|
|
154
156
|
icon: endIcon,
|
|
155
157
|
variant: "outline",
|
|
158
|
+
inheritFontScaling: true,
|
|
156
159
|
maxFontSizeMultiplier: resolvedMaxFontSizeMultiplier,
|
|
160
|
+
dynamicTypeRamp,
|
|
157
161
|
style: endIconStyles
|
|
158
162
|
})] })
|
|
159
163
|
]
|
package/dist/components/Link.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*! © 2026 Yahoo, Inc. UDS Mobile v0.0.0-development */
|
|
2
|
-
import {
|
|
2
|
+
import { useDynamicTypeRampEnabled } from "../fontScaling/FontScalingContext.js";
|
|
3
3
|
import { DEFAULT_DYNAMIC_TYPE_RAMPS } from "../fontScaling/constants.js";
|
|
4
4
|
import { useMaxFontSizeMultiplier } from "../fontScaling/useMaxFontSizeMultiplier.js";
|
|
5
5
|
import { IconSlot } from "./IconSlot.js";
|
|
@@ -52,10 +52,10 @@ const noUnderline = { textDecorationLine: "none" };
|
|
|
52
52
|
*/
|
|
53
53
|
const Link = memo(function Link({ children, variant = "primary", textVariant, alwaysUnderline = false, startIcon, endIcon, maxFontSizeMultiplier, onPress, style, ref, ...rest }) {
|
|
54
54
|
const [pressed, setPressed] = useState(false);
|
|
55
|
-
const
|
|
55
|
+
const dynamicTypeRampEnabled = useDynamicTypeRampEnabled();
|
|
56
56
|
const resolvedTextVariant = textVariant;
|
|
57
57
|
const resolvedMaxFontSizeMultiplier = useMaxFontSizeMultiplier(resolvedTextVariant, maxFontSizeMultiplier);
|
|
58
|
-
const dynamicTypeRamp =
|
|
58
|
+
const dynamicTypeRamp = dynamicTypeRampEnabled && resolvedTextVariant ? DEFAULT_DYNAMIC_TYPE_RAMPS[resolvedTextVariant] : void 0;
|
|
59
59
|
const handlePressIn = useCallback(() => {
|
|
60
60
|
setPressed(true);
|
|
61
61
|
}, []);
|
|
@@ -137,7 +137,9 @@ const Link = memo(function Link({ children, variant = "primary", textVariant, al
|
|
|
137
137
|
startIcon && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(IconSlot, {
|
|
138
138
|
icon: startIcon,
|
|
139
139
|
variant: "outline",
|
|
140
|
+
inheritFontScaling: true,
|
|
140
141
|
maxFontSizeMultiplier: resolvedMaxFontSizeMultiplier,
|
|
142
|
+
dynamicTypeRamp,
|
|
141
143
|
style: startIconStyles
|
|
142
144
|
}), /* @__PURE__ */ jsx(Text, {
|
|
143
145
|
style: noUnderline,
|
|
@@ -150,7 +152,9 @@ const Link = memo(function Link({ children, variant = "primary", textVariant, al
|
|
|
150
152
|
}), /* @__PURE__ */ jsx(IconSlot, {
|
|
151
153
|
icon: endIcon,
|
|
152
154
|
variant: "outline",
|
|
155
|
+
inheritFontScaling: true,
|
|
153
156
|
maxFontSizeMultiplier: resolvedMaxFontSizeMultiplier,
|
|
157
|
+
dynamicTypeRamp,
|
|
154
158
|
style: endIconStyles
|
|
155
159
|
})] })
|
|
156
160
|
]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Link.js","names":["RNText"],"sources":["../../src/components/Link.tsx"],"sourcesContent":["import type { UniversalLinkProps } from '@yahoo/uds-types';\nimport type { ReactNode, Ref } from 'react';\nimport { memo, useCallback, useMemo, useState } from 'react';\nimport type { GestureResponderEvent, TextStyle } from 'react-native';\nimport { Text as RNText } from 'react-native';\nimport Animated, {\n Easing,\n interpolateColor,\n useAnimatedStyle,\n useDerivedValue,\n withTiming,\n} from 'react-native-reanimated';\nimport { useAnimatedTheme } from 'react-native-unistyles/reanimated';\n\nimport { linkStyles } from '../../generated/styles';\nimport { DEFAULT_DYNAMIC_TYPE_RAMPS } from '../fontScaling/constants';\nimport {
|
|
1
|
+
{"version":3,"file":"Link.js","names":["RNText"],"sources":["../../src/components/Link.tsx"],"sourcesContent":["import type { UniversalLinkProps } from '@yahoo/uds-types';\nimport type { ReactNode, Ref } from 'react';\nimport { memo, useCallback, useMemo, useState } from 'react';\nimport type { GestureResponderEvent, TextStyle } from 'react-native';\nimport { Text as RNText } from 'react-native';\nimport Animated, {\n Easing,\n interpolateColor,\n useAnimatedStyle,\n useDerivedValue,\n withTiming,\n} from 'react-native-reanimated';\nimport { useAnimatedTheme } from 'react-native-unistyles/reanimated';\n\nimport { linkStyles } from '../../generated/styles';\nimport { DEFAULT_DYNAMIC_TYPE_RAMPS } from '../fontScaling/constants';\nimport { useDynamicTypeRampEnabled } from '../fontScaling/FontScalingContext';\nimport { useMaxFontSizeMultiplier } from '../fontScaling/useMaxFontSizeMultiplier';\nimport type { IconSlotType } from './IconSlot';\nimport { IconSlot } from './IconSlot';\nimport type { TextProps, TextVariant } from './Text';\n\nconst AnimatedText = Animated.Text;\n\n// Prevent icons from inheriting underline from parent/theme (matches web behavior)\nconst noUnderline: TextStyle = { textDecorationLine: 'none' };\n\ninterface LinkProps extends UniversalLinkProps<IconSlotType> {\n /** Style override for the link text */\n style?: TextStyle;\n /** Callback fired when the link is pressed */\n onPress?: TextProps['onPress'];\n /** Ref to the underlying Text element */\n ref?: Ref<RNText>;\n /** Link content, typically text */\n children?: ReactNode;\n /**\n * Caps how far the link text and inline icons grow with the OS text-size\n * setting. Set null to remove the cap; override app-wide via\n * UDSFontScalingProvider.\n */\n maxFontSizeMultiplier?: number | null;\n}\n\n/**\n * **🔗 A navigation link component**\n *\n * @description\n * A styled link component for navigation. Rendered as Text so it can be\n * nested inline within other Text. Supports optional start/end icons.\n *\n * @category Interactive\n * @platform mobile\n *\n * @example\n * ```tsx\n * import { Link } from '@yahoo/uds-mobile/Link';\n *\n * // Standalone link\n * <Link onPress={() => navigate('/profile')}>Go to Profile</Link>\n *\n * // Inline within text\n * <Text>Read our <Link>Terms of Service</Link> and <Link>Privacy Policy</Link>.</Text>\n *\n * // With icons\n * <Link startIcon=\"AffiliateLink\">External link</Link>\n * <Link endIcon=\"ChevronRight\">Navigate forward</Link>\n * ```\n *\n * @usage\n * - Use for navigation actions\n * - Can be nested within Text for inline links\n * - Use alwaysUnderline for links that need to be visually distinct\n *\n * @accessibility\n * - Link text is the accessible name\n * - Shows underline on press for visual feedback\n * - Use descriptive link text (avoid \"click here\")\n *\n * @see {@link Button} for primary actions\n * @see {@link Text} for non-interactive text\n */\nconst Link = memo(function Link({\n children,\n variant = 'primary',\n textVariant,\n alwaysUnderline = false,\n startIcon,\n endIcon,\n maxFontSizeMultiplier,\n onPress,\n style,\n ref,\n ...rest\n}: LinkProps) {\n const [pressed, setPressed] = useState(false);\n const dynamicTypeRampEnabled = useDynamicTypeRampEnabled();\n const resolvedTextVariant = textVariant as TextVariant | undefined;\n const resolvedMaxFontSizeMultiplier = useMaxFontSizeMultiplier(\n resolvedTextVariant,\n maxFontSizeMultiplier,\n );\n const dynamicTypeRamp =\n dynamicTypeRampEnabled && resolvedTextVariant\n ? DEFAULT_DYNAMIC_TYPE_RAMPS[resolvedTextVariant]\n : undefined;\n\n const handlePressIn = useCallback(() => {\n setPressed(true);\n }, []);\n\n const handlePressOut = useCallback(() => {\n setPressed(false);\n }, []);\n\n // Must have onPress for touch events to register (RN requirement)\n // Even without a user-provided handler, we need a function to make text touchable\n const handlePress = useCallback(\n (event: GestureResponderEvent) => {\n onPress?.(event);\n },\n [onPress],\n );\n\n linkStyles.useVariants({\n textStyle: textVariant,\n variant,\n pressed,\n });\n\n // Get theme as SharedValue for worklet access (zero re-renders)\n const animatedTheme = useAnimatedTheme();\n\n // Derive underline visibility from pressed state\n // useDerivedValue handles the animation automatically when deps change\n const underlineProgress = useDerivedValue(() => {\n const targetValue = pressed || alwaysUnderline ? 1 : 0;\n return withTiming(targetValue, {\n duration: 150,\n easing: Easing.bezier(0, 0, 0.2, 1),\n });\n }, [pressed, alwaysUnderline]);\n\n // Combined animated style for color and underline\n const animatedTextStyle = useAnimatedStyle(() => {\n // Access text color from theme using variant path\n const components = animatedTheme.value.components;\n const state = pressed ? 'pressed' : 'rest';\n const textVariantPath = `link/variant/${variant}/rootText/${state}` as const;\n const textColor = components[textVariantPath]?.color;\n\n if (!textColor) {\n return {};\n }\n\n const color = withTiming(textColor, {\n duration: 150,\n easing: Easing.bezier(0, 0, 0.2, 1),\n });\n\n // Interpolate underline opacity: 0 = transparent, 1 = text color\n const underlineColor = interpolateColor(\n underlineProgress.value,\n [0, 1],\n ['transparent', textColor],\n );\n\n return {\n color,\n textDecorationColor: underlineColor,\n };\n });\n\n const textStyles = useMemo(() => {\n return [linkStyles.root, linkStyles.text, animatedTextStyle, style];\n }, [linkStyles.text, animatedTextStyle, style, linkStyles.root]);\n\n const startIconStyles = useMemo(() => {\n return [linkStyles.icon, linkStyles.iconStart, noUnderline];\n }, [linkStyles.icon, linkStyles.iconStart, noUnderline]);\n\n const endIconStyles = useMemo(() => {\n return [linkStyles.icon, linkStyles.iconEnd, noUnderline];\n }, [linkStyles.icon, linkStyles.iconEnd, noUnderline]);\n\n return (\n <AnimatedText\n ref={ref}\n onPress={handlePress}\n onPressIn={handlePressIn}\n onPressOut={handlePressOut}\n suppressHighlighting\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n dynamicTypeRamp={dynamicTypeRamp}\n style={textStyles}\n {...rest}\n >\n {startIcon && (\n <>\n <IconSlot\n icon={startIcon}\n variant=\"outline\"\n inheritFontScaling\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n dynamicTypeRamp={dynamicTypeRamp}\n style={startIconStyles}\n />\n {/* TODO: need to add hairline space character to icon font so we can have space between icon and text https://hybridheroes.de/blog/2022-11-17-text-with-inline-icons-in-react-native/ */}\n <RNText style={noUnderline}>{' \\u200A'}</RNText>\n </>\n )}\n {children}\n {endIcon && (\n <>\n {/* TODO: need to add hairline space character to icon font so we can have space between icon and text https://hybridheroes.de/blog/2022-11-17-text-with-inline-icons-in-react-native/ */}\n <RNText style={noUnderline}>{' \\u200A'}</RNText>\n <IconSlot\n icon={endIcon}\n variant=\"outline\"\n inheritFontScaling\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n dynamicTypeRamp={dynamicTypeRamp}\n style={endIconStyles}\n />\n </>\n )}\n </AnimatedText>\n );\n});\n\nLink.displayName = 'Link';\n\nexport { Link, type LinkProps };\n"],"mappings":";;;;;;;;;;;;AAsBA,MAAM,eAAe,SAAS;AAG9B,MAAM,cAAyB,EAAE,oBAAoB,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyD7D,MAAM,OAAO,KAAK,SAAS,KAAK,EAC9B,UACA,UAAU,WACV,aACA,kBAAkB,OAClB,WACA,SACA,uBACA,SACA,OACA,KACA,GAAG,QACS;CACZ,MAAM,CAAC,SAAS,cAAc,SAAS,MAAM;CAC7C,MAAM,yBAAyB,2BAA2B;CAC1D,MAAM,sBAAsB;CAC5B,MAAM,gCAAgC,yBACpC,qBACA,sBACD;CACD,MAAM,kBACJ,0BAA0B,sBACtB,2BAA2B,uBAC3B,KAAA;CAEN,MAAM,gBAAgB,kBAAkB;EACtC,WAAW,KAAK;IACf,EAAE,CAAC;CAEN,MAAM,iBAAiB,kBAAkB;EACvC,WAAW,MAAM;IAChB,EAAE,CAAC;CAIN,MAAM,cAAc,aACjB,UAAiC;EAChC,UAAU,MAAM;IAElB,CAAC,QAAQ,CACV;CAED,WAAW,YAAY;EACrB,WAAW;EACX;EACA;EACD,CAAC;CAGF,MAAM,gBAAgB,kBAAkB;CAIxC,MAAM,oBAAoB,sBAAsB;EAE9C,OAAO,WADa,WAAW,kBAAkB,IAAI,GACtB;GAC7B,UAAU;GACV,QAAQ,OAAO,OAAO,GAAG,GAAG,IAAK,EAAE;GACpC,CAAC;IACD,CAAC,SAAS,gBAAgB,CAAC;CAG9B,MAAM,oBAAoB,uBAAuB;EAK/C,MAAM,YAHa,cAAc,MAAM,WAGV,gBADW,QAAQ,YADlC,UAAU,YAAY,WAEW;EAE/C,IAAI,CAAC,WACH,OAAO,EAAE;EAeX,OAAO;GACL,OAbY,WAAW,WAAW;IAClC,UAAU;IACV,QAAQ,OAAO,OAAO,GAAG,GAAG,IAAK,EAAE;IACpC,CAUM;GACL,qBARqB,iBACrB,kBAAkB,OAClB,CAAC,GAAG,EAAE,EACN,CAAC,eAAe,UAAU,CAKS;GACpC;GACD;CAEF,MAAM,aAAa,cAAc;EAC/B,OAAO;GAAC,WAAW;GAAM,WAAW;GAAM;GAAmB;GAAM;IAClE;EAAC,WAAW;EAAM;EAAmB;EAAO,WAAW;EAAK,CAAC;CAEhE,MAAM,kBAAkB,cAAc;EACpC,OAAO;GAAC,WAAW;GAAM,WAAW;GAAW;GAAY;IAC1D;EAAC,WAAW;EAAM,WAAW;EAAW;EAAY,CAAC;CAExD,MAAM,gBAAgB,cAAc;EAClC,OAAO;GAAC,WAAW;GAAM,WAAW;GAAS;GAAY;IACxD;EAAC,WAAW;EAAM,WAAW;EAAS;EAAY,CAAC;CAEtD,OACE,qBAAC,cAAD;EACO;EACL,SAAS;EACT,WAAW;EACX,YAAY;EACZ,sBAAA;EACA,uBAAuB;EACN;EACjB,OAAO;EACP,GAAI;YATN;GAWG,aACC,qBAAA,UAAA,EAAA,UAAA,CACE,oBAAC,UAAD;IACE,MAAM;IACN,SAAQ;IACR,oBAAA;IACA,uBAAuB;IACN;IACjB,OAAO;IACP,CAAA,EAEF,oBAACA,MAAD;IAAQ,OAAO;cAAc;IAAmB,CAAA,CAC/C,EAAA,CAAA;GAEJ;GACA,WACC,qBAAA,UAAA,EAAA,UAAA,CAEE,oBAACA,MAAD;IAAQ,OAAO;cAAc;IAAmB,CAAA,EAChD,oBAAC,UAAD;IACE,MAAM;IACN,SAAQ;IACR,oBAAA;IACA,uBAAuB;IACN;IACjB,OAAO;IACP,CAAA,CACD,EAAA,CAAA;GAEQ;;EAEjB;AAEF,KAAK,cAAc"}
|
|
@@ -23,7 +23,7 @@ const PaginationEllipsis = (0, react.memo)(function PaginationEllipsis({ style }
|
|
|
23
23
|
const { size, variant, maxFontSizeMultiplier } = require_components_Pagination_paginationContext.usePaginationContext();
|
|
24
24
|
const { theme } = (0, react_native_unistyles.useUnistyles)();
|
|
25
25
|
const sizeTextStyle = require_components_Pagination_paginationTheme.getPaginationSizeLayerStyle(theme, size, "text");
|
|
26
|
-
const fontScaleFactor = require_fontScaling_useFontScale.
|
|
26
|
+
const fontScaleFactor = require_fontScaling_useFontScale.useComponentFontScale(maxFontSizeMultiplier, typeof sizeTextStyle.lineHeight === "number" ? sizeTextStyle.lineHeight : sizeTextStyle.fontSize);
|
|
27
27
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
|
|
28
28
|
accessibilityElementsHidden: true,
|
|
29
29
|
importantForAccessibility: "no-hide-descendants",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*! © 2026 Yahoo, Inc. UDS Mobile v0.0.0-development */
|
|
2
|
-
import {
|
|
2
|
+
import { useComponentFontScale } from "../../fontScaling/useFontScale.js";
|
|
3
3
|
import { Text as Text$1 } from "../Text.js";
|
|
4
4
|
import { usePaginationContext } from "./paginationContext.js";
|
|
5
5
|
import { getMergedPaginationControlRootStyle, getMergedPaginationTextStyle, getPaginationControlMetrics, getPaginationSizeLayerStyle } from "./paginationTheme.js";
|
|
@@ -21,7 +21,7 @@ const PaginationEllipsis = memo(function PaginationEllipsis({ style }) {
|
|
|
21
21
|
const { size, variant, maxFontSizeMultiplier } = usePaginationContext();
|
|
22
22
|
const { theme } = useUnistyles();
|
|
23
23
|
const sizeTextStyle = getPaginationSizeLayerStyle(theme, size, "text");
|
|
24
|
-
const fontScaleFactor =
|
|
24
|
+
const fontScaleFactor = useComponentFontScale(maxFontSizeMultiplier, typeof sizeTextStyle.lineHeight === "number" ? sizeTextStyle.lineHeight : sizeTextStyle.fontSize);
|
|
25
25
|
return /* @__PURE__ */ jsx(View, {
|
|
26
26
|
accessibilityElementsHidden: true,
|
|
27
27
|
importantForAccessibility: "no-hide-descendants",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PaginationEllipsis.js","names":["Text"],"sources":["../../../src/components/Pagination/PaginationEllipsis.tsx"],"sourcesContent":["import { memo, useMemo } from 'react';\nimport type { StyleProp, TextStyle, ViewStyle } from 'react-native';\nimport { StyleSheet, View } from 'react-native';\n// eslint-disable-next-line uds/no-use-unistyles -- pagination token lookups\nimport { useUnistyles } from 'react-native-unistyles';\n\nimport {
|
|
1
|
+
{"version":3,"file":"PaginationEllipsis.js","names":["Text"],"sources":["../../../src/components/Pagination/PaginationEllipsis.tsx"],"sourcesContent":["import { memo, useMemo } from 'react';\nimport type { StyleProp, TextStyle, ViewStyle } from 'react-native';\nimport { StyleSheet, View } from 'react-native';\n// eslint-disable-next-line uds/no-use-unistyles -- pagination token lookups\nimport { useUnistyles } from 'react-native-unistyles';\n\nimport { useComponentFontScale } from '../../fontScaling/useFontScale';\nimport { Text } from '../Text';\nimport { usePaginationContext } from './paginationContext';\nimport {\n getMergedPaginationControlRootStyle,\n getMergedPaginationTextStyle,\n getPaginationControlMetrics,\n getPaginationSizeLayerStyle,\n} from './paginationTheme';\n\ninterface PaginationEllipsisProps {\n style?: StyleProp<ViewStyle>;\n}\n\n/**\n * **PaginationEllipsis — truncated page range indicator**\n *\n * @description\n * Non-interactive ellipsis shown when page numbers are collapsed.\n *\n * @category Navigation\n * @platform mobile\n */\nconst PaginationEllipsis = memo(function PaginationEllipsis({ style }: PaginationEllipsisProps) {\n const { size, variant, maxFontSizeMultiplier } = usePaginationContext();\n const { theme } = useUnistyles();\n const sizeTextStyle = getPaginationSizeLayerStyle(theme, size, 'text') as TextStyle;\n const fontScaleFactor = useComponentFontScale(\n maxFontSizeMultiplier,\n typeof sizeTextStyle.lineHeight === 'number'\n ? sizeTextStyle.lineHeight\n : sizeTextStyle.fontSize,\n );\n\n const rootStyle = useMemo<StyleProp<ViewStyle>>(() => {\n const { controlSize: baseControlSize, borderRadius } = getPaginationControlMetrics(theme, size);\n const controlSize = Math.round(baseControlSize * fontScaleFactor);\n\n return [\n paginationStaticStyles.root,\n getMergedPaginationControlRootStyle(theme, size, variant, 'off', 'rest'),\n {\n borderRadius,\n height: controlSize,\n width: controlSize,\n },\n style,\n ];\n }, [fontScaleFactor, size, style, theme, variant]);\n\n const textStyle = useMemo(\n () => getMergedPaginationTextStyle(theme, size, variant, 'off', 'rest'),\n [size, theme, variant],\n );\n\n return (\n <View\n accessibilityElementsHidden\n importantForAccessibility=\"no-hide-descendants\"\n style={rootStyle}\n >\n <Text\n color=\"inherit\"\n variant=\"inherit\"\n maxFontSizeMultiplier={maxFontSizeMultiplier}\n style={textStyle}\n >\n …\n </Text>\n </View>\n );\n});\n\nPaginationEllipsis.displayName = 'PaginationEllipsis';\n\nconst paginationStaticStyles = StyleSheet.create({\n root: {\n alignItems: 'center',\n justifyContent: 'center',\n flexShrink: 0,\n },\n});\n\nexport { PaginationEllipsis, type PaginationEllipsisProps };\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA6BA,MAAM,qBAAqB,KAAK,SAAS,mBAAmB,EAAE,SAAkC;CAC9F,MAAM,EAAE,MAAM,SAAS,0BAA0B,sBAAsB;CACvE,MAAM,EAAE,UAAU,cAAc;CAChC,MAAM,gBAAgB,4BAA4B,OAAO,MAAM,OAAO;CACtE,MAAM,kBAAkB,sBACtB,uBACA,OAAO,cAAc,eAAe,WAChC,cAAc,aACd,cAAc,SACnB;CAuBD,OACE,oBAAC,MAAD;EACE,6BAAA;EACA,2BAA0B;EAC1B,OAzBc,cAAoC;GACpD,MAAM,EAAE,aAAa,iBAAiB,iBAAiB,4BAA4B,OAAO,KAAK;GAC/F,MAAM,cAAc,KAAK,MAAM,kBAAkB,gBAAgB;GAEjE,OAAO;IACL,uBAAuB;IACvB,oCAAoC,OAAO,MAAM,SAAS,OAAO,OAAO;IACxE;KACE;KACA,QAAQ;KACR,OAAO;KACR;IACD;IACD;KACA;GAAC;GAAiB;GAAM;GAAO;GAAO;GAAQ,CAW7B;YAEhB,oBAACA,QAAD;GACE,OAAM;GACN,SAAQ;GACe;GACvB,OAfY,cACV,6BAA6B,OAAO,MAAM,SAAS,OAAO,OAAO,EACvE;IAAC;IAAM;IAAO;IAAQ,CAaF;aACjB;GAEM,CAAA;EACF,CAAA;EAET;AAEF,mBAAmB,cAAc;AAEjC,MAAM,yBAAyB,WAAW,OAAO,EAC/C,MAAM;CACJ,YAAY;CACZ,gBAAgB;CAChB,YAAY;CACb,EACF,CAAC"}
|
|
@@ -15,7 +15,7 @@ function usePaginationControlStyles({ size, variant, isActive, pressed, layout =
|
|
|
15
15
|
const interaction = pressed ? "pressed" : "rest";
|
|
16
16
|
const prevNextActive = "off";
|
|
17
17
|
const sizeTextStyle = require_components_Pagination_paginationTheme.getPaginationSizeLayerStyle(theme, size, "text");
|
|
18
|
-
const fontScaleFactor = require_fontScaling_useFontScale.
|
|
18
|
+
const fontScaleFactor = require_fontScaling_useFontScale.useComponentFontScale(maxFontSizeMultiplier, typeof sizeTextStyle.lineHeight === "number" ? sizeTextStyle.lineHeight : sizeTextStyle.fontSize);
|
|
19
19
|
return {
|
|
20
20
|
rootStyle: (0, react.useMemo)(() => {
|
|
21
21
|
const { controlSize: baseControlSize, borderRadius } = require_components_Pagination_paginationTheme.getPaginationControlMetrics(theme, size);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*! © 2026 Yahoo, Inc. UDS Mobile v0.0.0-development */
|
|
2
|
-
import {
|
|
2
|
+
import { useComponentFontScale } from "../../fontScaling/useFontScale.js";
|
|
3
3
|
import { usePaginationContext } from "./paginationContext.js";
|
|
4
4
|
import { getMergedPaginationControlRootStyle, getMergedPaginationIconStyle, getMergedPaginationTextStyle, getPaginationControlMetrics, getPaginationSizeLayerStyle } from "./paginationTheme.js";
|
|
5
5
|
import { useMemo } from "react";
|
|
@@ -13,7 +13,7 @@ function usePaginationControlStyles({ size, variant, isActive, pressed, layout =
|
|
|
13
13
|
const interaction = pressed ? "pressed" : "rest";
|
|
14
14
|
const prevNextActive = "off";
|
|
15
15
|
const sizeTextStyle = getPaginationSizeLayerStyle(theme, size, "text");
|
|
16
|
-
const fontScaleFactor =
|
|
16
|
+
const fontScaleFactor = useComponentFontScale(maxFontSizeMultiplier, typeof sizeTextStyle.lineHeight === "number" ? sizeTextStyle.lineHeight : sizeTextStyle.fontSize);
|
|
17
17
|
return {
|
|
18
18
|
rootStyle: useMemo(() => {
|
|
19
19
|
const { controlSize: baseControlSize, borderRadius } = getPaginationControlMetrics(theme, size);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usePaginationControlStyles.js","names":[],"sources":["../../../src/components/Pagination/usePaginationControlStyles.ts"],"sourcesContent":["import type { PaginationSize, PaginationVariant } from '@yahoo/uds-types';\nimport { useMemo } from 'react';\nimport type { StyleProp, TextStyle, ViewStyle } from 'react-native';\nimport { StyleSheet } from 'react-native';\n// eslint-disable-next-line uds/no-use-unistyles -- pagination token lookups for variant + size layers\nimport { useUnistyles } from 'react-native-unistyles';\n\nimport {
|
|
1
|
+
{"version":3,"file":"usePaginationControlStyles.js","names":[],"sources":["../../../src/components/Pagination/usePaginationControlStyles.ts"],"sourcesContent":["import type { PaginationSize, PaginationVariant } from '@yahoo/uds-types';\nimport { useMemo } from 'react';\nimport type { StyleProp, TextStyle, ViewStyle } from 'react-native';\nimport { StyleSheet } from 'react-native';\n// eslint-disable-next-line uds/no-use-unistyles -- pagination token lookups for variant + size layers\nimport { useUnistyles } from 'react-native-unistyles';\n\nimport { useComponentFontScale } from '../../fontScaling/useFontScale';\nimport { usePaginationContext } from './paginationContext';\nimport {\n getMergedPaginationControlRootStyle,\n getMergedPaginationIconStyle,\n getMergedPaginationTextStyle,\n getPaginationControlMetrics,\n getPaginationSizeLayerStyle,\n} from './paginationTheme';\n\ninterface UsePaginationControlStylesOptions {\n size: PaginationSize;\n variant: PaginationVariant;\n isActive: boolean;\n pressed: boolean;\n layout?: 'page' | 'prevNext';\n}\n\nfunction usePaginationControlStyles({\n size,\n variant,\n isActive,\n pressed,\n layout = 'page',\n}: UsePaginationControlStylesOptions) {\n const { theme } = useUnistyles();\n const { maxFontSizeMultiplier } = usePaginationContext();\n const active: 'on' | 'off' = isActive ? 'on' : 'off';\n const interaction = pressed ? 'pressed' : 'rest';\n const prevNextActive: 'on' | 'off' = 'off';\n const sizeTextStyle = getPaginationSizeLayerStyle(theme, size, 'text') as TextStyle;\n const fontScaleFactor = useComponentFontScale(\n maxFontSizeMultiplier,\n typeof sizeTextStyle.lineHeight === 'number'\n ? sizeTextStyle.lineHeight\n : sizeTextStyle.fontSize,\n );\n\n const rootStyle = useMemo<StyleProp<ViewStyle>>(() => {\n const { controlSize: baseControlSize, borderRadius } = getPaginationControlMetrics(theme, size);\n const controlSize = Math.round(baseControlSize * fontScaleFactor);\n const variantActive = layout === 'prevNext' ? prevNextActive : active;\n\n return [\n layout === 'page' ? paginationStaticStyles.pageRoot : paginationStaticStyles.prevNextRoot,\n getMergedPaginationControlRootStyle(theme, size, variant, variantActive, interaction),\n {\n borderRadius,\n ...(layout === 'page'\n ? { height: controlSize, width: controlSize }\n : { minHeight: controlSize }),\n },\n ];\n }, [active, fontScaleFactor, interaction, layout, size, theme, variant]);\n\n const textStyle = useMemo<StyleProp<TextStyle>>(() => {\n const variantActive = layout === 'prevNext' ? prevNextActive : active;\n\n return [\n paginationStaticStyles.text,\n getMergedPaginationTextStyle(theme, size, variant, variantActive, interaction),\n ];\n }, [active, interaction, layout, size, theme, variant]);\n\n const iconStyle = useMemo<StyleProp<TextStyle>>(\n () =>\n getMergedPaginationIconStyle(\n theme,\n size,\n variant,\n layout === 'prevNext' ? prevNextActive : active,\n interaction,\n ),\n [active, interaction, layout, size, theme, variant],\n );\n\n return { rootStyle, textStyle, iconStyle };\n}\n\nconst paginationStaticStyles = StyleSheet.create({\n pageRoot: {\n alignItems: 'center',\n justifyContent: 'center',\n flexShrink: 0,\n },\n prevNextRoot: {\n alignItems: 'center',\n flexDirection: 'row',\n flexShrink: 0,\n gap: 4,\n justifyContent: 'center',\n },\n text: {\n flexShrink: 0,\n fontVariant: ['tabular-nums'],\n textAlign: 'center',\n },\n});\n\nexport { usePaginationControlStyles };\n"],"mappings":";;;;;;;;AAyBA,SAAS,2BAA2B,EAClC,MACA,SACA,UACA,SACA,SAAS,UAC2B;CACpC,MAAM,EAAE,UAAU,cAAc;CAChC,MAAM,EAAE,0BAA0B,sBAAsB;CACxD,MAAM,SAAuB,WAAW,OAAO;CAC/C,MAAM,cAAc,UAAU,YAAY;CAC1C,MAAM,iBAA+B;CACrC,MAAM,gBAAgB,4BAA4B,OAAO,MAAM,OAAO;CACtE,MAAM,kBAAkB,sBACtB,uBACA,OAAO,cAAc,eAAe,WAChC,cAAc,aACd,cAAc,SACnB;CAwCD,OAAO;EAAE,WAtCS,cAAoC;GACpD,MAAM,EAAE,aAAa,iBAAiB,iBAAiB,4BAA4B,OAAO,KAAK;GAC/F,MAAM,cAAc,KAAK,MAAM,kBAAkB,gBAAgB;GACjE,MAAM,gBAAgB,WAAW,aAAa,iBAAiB;GAE/D,OAAO;IACL,WAAW,SAAS,uBAAuB,WAAW,uBAAuB;IAC7E,oCAAoC,OAAO,MAAM,SAAS,eAAe,YAAY;IACrF;KACE;KACA,GAAI,WAAW,SACX;MAAE,QAAQ;MAAa,OAAO;MAAa,GAC3C,EAAE,WAAW,aAAa;KAC/B;IACF;KACA;GAAC;GAAQ;GAAiB;GAAa;GAAQ;GAAM;GAAO;GAAQ,CAuBrD;EAAE,WArBF,cAAoC;GACpD,MAAM,gBAAgB,WAAW,aAAa,iBAAiB;GAE/D,OAAO,CACL,uBAAuB,MACvB,6BAA6B,OAAO,MAAM,SAAS,eAAe,YAAY,CAC/E;KACA;GAAC;GAAQ;GAAa;GAAQ;GAAM;GAAO;GAAQ,CAczB;EAAE,WAZb,cAEd,6BACE,OACA,MACA,SACA,WAAW,aAAa,iBAAiB,QACzC,YACD,EACH;GAAC;GAAQ;GAAa;GAAQ;GAAM;GAAO;GAAQ,CAGb;EAAE;;AAG5C,MAAM,yBAAyB,WAAW,OAAO;CAC/C,UAAU;EACR,YAAY;EACZ,gBAAgB;EAChB,YAAY;EACb;CACD,cAAc;EACZ,YAAY;EACZ,eAAe;EACf,YAAY;EACZ,KAAK;EACL,gBAAgB;EACjB;CACD,MAAM;EACJ,YAAY;EACZ,aAAa,CAAC,eAAe;EAC7B,WAAW;EACZ;CACF,CAAC"}
|
|
@@ -110,7 +110,7 @@ const Radio = (0, react.memo)(function Radio({ label, labelPosition = "start", v
|
|
|
110
110
|
pressed
|
|
111
111
|
});
|
|
112
112
|
const radioBaseSize = generated_styles.radioStyles.radio.width;
|
|
113
|
-
const fontScaleFactor = require_fontScaling_useFontScale.
|
|
113
|
+
const fontScaleFactor = require_fontScaling_useFontScale.useComponentFontScale(resolvedMaxFontSizeMultiplier, typeof radioBaseSize === "number" ? radioBaseSize : void 0);
|
|
114
114
|
const rootStyle = (0, react.useMemo)(() => [generated_styles.radioStyles.root, { opacity: disabled ? .5 : 1 }], [generated_styles.radioStyles.root, disabled]);
|
|
115
115
|
const radioBoxStyle = (0, react.useMemo)(() => {
|
|
116
116
|
const { width, height } = generated_styles.radioStyles.radio;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*! © 2026 Yahoo, Inc. UDS Mobile v0.0.0-development */
|
|
2
2
|
import { SCALE_EFFECTS } from "../../motion-tokens/dist/index.js";
|
|
3
3
|
import { BUTTON_SPRING_CONFIG } from "../../motion.js";
|
|
4
|
-
import {
|
|
4
|
+
import { useComponentFontScale } from "../../fontScaling/useFontScale.js";
|
|
5
5
|
import { useMaxFontSizeMultiplier } from "../../fontScaling/useMaxFontSizeMultiplier.js";
|
|
6
6
|
import { Pressable as Pressable$1 } from "../Pressable.js";
|
|
7
7
|
import { FormLabel } from "../FormLabel.js";
|
|
@@ -107,7 +107,7 @@ const Radio = memo(function Radio({ label, labelPosition = "start", variant: var
|
|
|
107
107
|
pressed
|
|
108
108
|
});
|
|
109
109
|
const radioBaseSize = radioStyles.radio.width;
|
|
110
|
-
const fontScaleFactor =
|
|
110
|
+
const fontScaleFactor = useComponentFontScale(resolvedMaxFontSizeMultiplier, typeof radioBaseSize === "number" ? radioBaseSize : void 0);
|
|
111
111
|
const rootStyle = useMemo(() => [radioStyles.root, { opacity: disabled ? .5 : 1 }], [radioStyles.root, disabled]);
|
|
112
112
|
const radioBoxStyle = useMemo(() => {
|
|
113
113
|
const { width, height } = radioStyles.radio;
|