@yahoo/uds-mobile 2.24.0-beta.3 → 2.24.0-beta.5
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/dist/components/Avatar/AvatarIcon.cjs +8 -2
- package/dist/components/Avatar/AvatarIcon.d.cts.map +1 -1
- package/dist/components/Avatar/AvatarIcon.d.ts.map +1 -1
- package/dist/components/Avatar/AvatarIcon.js +9 -3
- package/dist/components/Avatar/AvatarIcon.js.map +1 -1
- package/dist/components/Avatar/AvatarImage.cjs +9 -2
- package/dist/components/Avatar/AvatarImage.d.cts.map +1 -1
- package/dist/components/Avatar/AvatarImage.d.ts.map +1 -1
- package/dist/components/Avatar/AvatarImage.js +10 -3
- package/dist/components/Avatar/AvatarImage.js.map +1 -1
- package/dist/components/Avatar/AvatarText.cjs +8 -2
- package/dist/components/Avatar/AvatarText.d.cts.map +1 -1
- package/dist/components/Avatar/AvatarText.d.ts.map +1 -1
- package/dist/components/Avatar/AvatarText.js +9 -3
- package/dist/components/Avatar/AvatarText.js.map +1 -1
- package/dist/components/Avatar/types.d.cts +7 -3
- package/dist/components/Avatar/types.d.cts.map +1 -1
- package/dist/components/Avatar/types.d.ts +7 -3
- package/dist/components/Avatar/types.d.ts.map +1 -1
- package/dist/components/Avatar/utils.cjs +9 -6
- package/dist/components/Avatar/utils.d.cts +2 -2
- package/dist/components/Avatar/utils.d.cts.map +1 -1
- package/dist/components/Avatar/utils.d.ts +2 -2
- package/dist/components/Avatar/utils.d.ts.map +1 -1
- package/dist/components/Avatar/utils.js +9 -6
- package/dist/components/Avatar/utils.js.map +1 -1
- package/dist/components/Button.cjs +5 -1
- package/dist/components/Button.d.cts.map +1 -1
- package/dist/components/Button.d.ts.map +1 -1
- package/dist/components/Button.js +5 -1
- package/dist/components/Button.js.map +1 -1
- package/dist/components/Checkbox.cjs +3 -1
- package/dist/components/Checkbox.js +3 -1
- package/dist/components/Checkbox.js.map +1 -1
- package/dist/components/Icon.cjs +4 -3
- package/dist/components/Icon.d.cts.map +1 -1
- package/dist/components/Icon.d.ts.map +1 -1
- package/dist/components/Icon.js +4 -3
- package/dist/components/Icon.js.map +1 -1
- package/dist/components/Input.cjs +54 -18
- package/dist/components/Input.d.cts +5 -0
- package/dist/components/Input.d.cts.map +1 -1
- package/dist/components/Input.d.ts +5 -0
- package/dist/components/Input.d.ts.map +1 -1
- package/dist/components/Input.js +54 -18
- package/dist/components/Input.js.map +1 -1
- package/dist/components/InputHelpText.cjs +6 -1
- package/dist/components/InputHelpText.d.cts +2 -0
- package/dist/components/InputHelpText.d.cts.map +1 -1
- package/dist/components/InputHelpText.d.ts +2 -0
- package/dist/components/InputHelpText.d.ts.map +1 -1
- package/dist/components/InputHelpText.js +6 -1
- package/dist/components/InputHelpText.js.map +1 -1
- package/dist/components/Select/Select.cjs +11 -2
- package/dist/components/Select/Select.d.cts +2 -0
- package/dist/components/Select/Select.d.cts.map +1 -1
- package/dist/components/Select/Select.d.ts +2 -0
- package/dist/components/Select/Select.d.ts.map +1 -1
- package/dist/components/Select/Select.js +11 -2
- package/dist/components/Select/Select.js.map +1 -1
- package/dist/components/Select/SelectItem.cjs +23 -12
- 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 +23 -12
- package/dist/components/Select/SelectItem.js.map +1 -1
- package/dist/components/Select/SelectTrigger.cjs +28 -13
- 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 +28 -13
- package/dist/components/Select/SelectTrigger.js.map +1 -1
- package/dist/components/Select/types.d.cts +2 -0
- package/dist/components/Select/types.d.cts.map +1 -1
- package/dist/components/Select/types.d.ts +2 -0
- package/dist/components/Select/types.d.ts.map +1 -1
- package/dist/components/Switch.cjs +2 -2
- package/dist/components/Switch.d.cts.map +1 -1
- package/dist/components/Switch.d.ts.map +1 -1
- package/dist/components/Switch.js +2 -2
- package/dist/components/Switch.js.map +1 -1
- package/dist/components/Tabs/Tab.cjs +5 -1
- package/dist/components/Tabs/Tab.d.cts.map +1 -1
- package/dist/components/Tabs/Tab.d.ts.map +1 -1
- package/dist/components/Tabs/Tab.js +5 -1
- package/dist/components/Tabs/Tab.js.map +1 -1
- package/dist/components/Text.cjs +4 -0
- package/dist/components/Text.d.cts.map +1 -1
- package/dist/components/Text.d.ts.map +1 -1
- package/dist/components/Text.js +4 -0
- package/dist/components/Text.js.map +1 -1
- package/dist/components/UDSProvider.cjs +1 -1
- package/dist/components/UDSProvider.js +1 -1
- package/dist/fontScaling/FontScalingContext.cjs +14 -0
- package/dist/fontScaling/FontScalingContext.d.cts +7 -1
- package/dist/fontScaling/FontScalingContext.d.cts.map +1 -1
- package/dist/fontScaling/FontScalingContext.d.ts +7 -1
- package/dist/fontScaling/FontScalingContext.d.ts.map +1 -1
- package/dist/fontScaling/FontScalingContext.js +15 -2
- package/dist/fontScaling/FontScalingContext.js.map +1 -1
- package/dist/fontScaling/constants.cjs +33 -0
- package/dist/fontScaling/constants.d.cts +11 -1
- package/dist/fontScaling/constants.d.cts.map +1 -1
- package/dist/fontScaling/constants.d.ts +11 -1
- package/dist/fontScaling/constants.d.ts.map +1 -1
- package/dist/fontScaling/constants.js +33 -1
- package/dist/fontScaling/constants.js.map +1 -1
- package/dist/fontScaling/index.cjs +2 -1
- package/dist/fontScaling/index.d.cts +2 -2
- package/dist/fontScaling/index.d.ts +2 -2
- package/dist/fontScaling/index.js +3 -3
- package/dist/jest/mocks/styles.cjs +13 -1
- package/dist/jest/mocks/styles.d.cts.map +1 -1
- package/dist/jest/mocks/styles.d.ts.map +1 -1
- package/dist/jest/mocks/styles.js +13 -1
- package/dist/jest/mocks/styles.js.map +1 -1
- package/dist/portal.cjs +8 -2
- package/dist/portal.d.cts.map +1 -1
- package/dist/portal.d.ts.map +1 -1
- package/dist/portal.js +8 -2
- package/dist/portal.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Button.js","names":["Text"],"sources":["../../src/components/Button.tsx"],"sourcesContent":["import type { ButtonSize, ButtonVariantFlat, IconSize, IconVariant } from '@yahoo/uds-types';\nimport type { Ref } from 'react';\nimport { isValidElement, memo, useCallback, useMemo, useState } from 'react';\nimport type { View } from 'react-native';\nimport { ActivityIndicator } from 'react-native';\nimport Animated, {\n Easing,\n interpolate,\n useAnimatedStyle,\n useDerivedValue,\n useSharedValue,\n withSpring,\n withTiming,\n} from 'react-native-reanimated';\n// eslint-disable-next-line uds/no-use-unistyles -- button control height from theme size layers\nimport { useUnistyles } from 'react-native-unistyles';\nimport { useAnimatedTheme } from 'react-native-unistyles/reanimated';\n\nimport { buttonStyles } from '../../generated/styles';\nimport { useFontScale } from '../fontScaling/useFontScale';\nimport { useMaxFontSizeMultiplier } from '../fontScaling/useMaxFontSizeMultiplier';\nimport { BUTTON_SPRING_CONFIG, SCALE_EFFECTS } from '../motion';\nimport { getButtonControlMetrics, getButtonTextStyle } from './Button/buttonTheme';\nimport type { IconSlotType } from './IconSlot';\nimport { IconSlot } from './IconSlot';\nimport type { PressableProps } from './Pressable';\nimport { AnimatedPressable } from './Pressable';\nimport { Text } from './Text';\n\n/* -------------------------------------------------------------------------- */\n/* Animation Helpers */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Interpolates a boxShadow string by scaling the alpha of all colors.\n * This allows smooth fade-in/out of shadows.\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/* Animated Icon Slot */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Animated wrapper for icon/loading content.\n * Matches web Button icon animation: scale 0.7→1, opacity 0→1, width 0→auto\n * Uses staggered animation: opacity waits until halfway through width animation.\n */\nfunction AnimatedIconSlot({\n children,\n visible,\n iconSize,\n iconCellHeight,\n gap,\n}: {\n children: React.ReactNode;\n visible: boolean;\n iconSize: number;\n iconCellHeight: number;\n gap: number;\n}) {\n // Use useDerivedValue instead of useEffect + useSharedValue\n // This is the idiomatic Reanimated pattern for deriving animated values from React state\n const progress = useDerivedValue(\n () => withSpring(visible ? 1 : 0, BUTTON_SPRING_CONFIG),\n [visible],\n );\n\n const animatedStyle = useAnimatedStyle(() => {\n // Total width includes icon + gap when visible\n const totalWidth = iconSize + gap;\n const width = interpolate(progress.value, [0, 1], [0, totalWidth]);\n\n // Staggered animation: opacity starts at 50% of width animation\n // On enter: width expands first, then icon fades in\n // On exit: icon fades out first, then width collapses\n const opacity = interpolate(progress.value, [0.5, 1], [0, 1], 'clamp');\n const scale = interpolate(progress.value, [0.5, 1], [0.7, 1], 'clamp');\n\n return {\n width,\n opacity,\n transform: [{ scale }],\n overflow: 'hidden' as const,\n minHeight: iconCellHeight,\n justifyContent: 'center' as const,\n };\n });\n\n return <Animated.View style={animatedStyle}>{children}</Animated.View>;\n}\n\n// function LoadingIcon({ size, variant }: { size: ButtonSize, variant: ButtonVariantFlat }) {\n// const { theme } = useUnistyles();\n// const themeKey = `buttonVariant${variantToCapitalMap[variant]}IconRest` as const;\n// const iconSize = theme.components.buttonSizeLgIconRest.fontSize;\n// return <ActivityIndicator size={iconSize} color={theme.colors.text.primary} />;\n// }\n\n/* -------------------------------------------------------------------------- */\n/* Button Props */\n/* -------------------------------------------------------------------------- */\n\ninterface ButtonProps extends Omit<PressableProps, 'children' | 'disabled'> {\n /** The visual style variant of the button @default 'primary' */\n variant?: ButtonVariantFlat;\n /** The size of the button @default 'md' */\n size?: ButtonSize;\n /** The icon style variant @default 'outline' */\n iconVariant?: IconVariant;\n /** Icon displayed before the button label */\n startIcon?: IconSlotType;\n /** Icon displayed after the button label */\n endIcon?: IconSlotType;\n /** Shows a loading spinner and disables the button */\n loading?: boolean;\n /** Whether the button is disabled */\n disabled?: boolean;\n /** Button label content */\n children?: React.ReactNode;\n /**\n * Disable motion effects (scale on press, icon animations)\n * @default false\n */\n disableEffects?: boolean;\n /**\n * Caps how far the label and icons grow with the OS text-size setting; the\n * button grows to fit the scaled content. Set null to remove the cap;\n * 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/* Button Component */\n/* -------------------------------------------------------------------------- */\n\n/**\n * **🖲️ A button element that can be used to trigger an action**\n *\n * @description\n * A button is a fundamental component used to trigger an action or event.\n * Buttons are interactive elements that users can click, tap, or otherwise\n * engage with to submit forms, open dialogues, or perform any other interaction.\n *\n * Features animated scale effect on press and smooth icon transitions matching\n * the web UDS Button behavior.\n *\n * @category Interactive\n * @platform mobile\n *\n * @example\n * ```tsx\n * import { Button } from '@yahoo/uds-mobile/Button';\n *\n * <Button onPress={() => console.log('pressed')}>Save</Button>\n * <Button variant=\"secondary\">Cancel</Button>\n * <Button startIcon=\"Add\" variant=\"brand\">Add Item</Button>\n * <Button loading>Saving...</Button>\n * ```\n *\n * @accessibility\n * - Sets `accessibilityRole=\"button\"` automatically\n * - Announces loading state to screen readers\n * - Use `accessibilityLabel` for icon-only buttons\n *\n * @see {@link IconButton} for icon-only buttons\n * @see {@link Link} for navigation actions\n */\nconst Button = memo(function Button({\n variant = 'primary',\n size = 'md',\n iconVariant = 'outline',\n startIcon,\n endIcon,\n loading,\n disabled,\n width: _width,\n children,\n style,\n accessibilityLabel,\n accessibilityHint,\n disableEffects = false,\n maxFontSizeMultiplier,\n onPressIn,\n onPressOut,\n ref,\n ...props\n}: ButtonProps) {\n const shouldAnimate = !disableEffects;\n\n // Inside a control, \"no cap resolved\" must force-uncap (0) rather than let\n // the label's own variant default or an inherited cap apply.\n const resolvedMaxFontSizeMultiplier =\n useMaxFontSizeMultiplier('control', maxFontSizeMultiplier) ?? 0;\n const fontScaleFactor = useFontScale(resolvedMaxFontSizeMultiplier);\n\n const { theme } = useUnistyles();\n const { minHeight, labelLineHeight, iconCellHeight, paddingVertical } = useMemo(\n () => getButtonControlMetrics(theme, size, fontScaleFactor),\n [theme, size, fontScaleFactor],\n );\n\n /* --------------------------------- State ---------------------------------- */\n const [pressed, setPressed] = useState(false);\n\n buttonStyles.useVariants({ size, variant, disabled, pressed });\n\n // Get gap and icon size from current variant styles\n const buttonGap = buttonStyles.root.gap;\n const iconSize = buttonStyles.icon.fontSize;\n\n // Get animated theme for boxShadow (useAnimatedVariantColor doesn't support non-color props)\n const animatedTheme = useAnimatedTheme();\n\n /* ------------------------------- Animation -------------------------------- */\n const scale = useSharedValue<number>(SCALE_EFFECTS.none);\n\n const handlePressIn = useCallback(\n (event: Parameters<NonNullable<PressableProps['onPressIn']>>[0]) => {\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(\n (event: Parameters<NonNullable<PressableProps['onPressOut']>>[0]) => {\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 /* -------------------------------- Content --------------------------------- */\n const childrenNode =\n children &&\n (isValidElement(children) ? (\n children\n ) : (\n <Text\n numberOfLines={1}\n textAlign=\"center\"\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n style={getButtonTextStyle(buttonStyles.text, labelLineHeight)}\n >\n {children}\n </Text>\n ));\n\n const a11yState = useMemo(() => ({ disabled, busy: loading }), [disabled, loading]);\n\n /* --------------------------------- Styles --------------------------------- */\n // Animate pressed state for shadow (0 = rest, 1 = pressed)\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 animatedStyles = useAnimatedStyle(() => {\n // Get boxShadow from theme using flattened path (no camelCase conversion needed!)\n const components = animatedTheme.value.components;\n const buttonVariantPath = `button/variant/${variant}/root/pressed` as const;\n const shadowPressed = components[buttonVariantPath]?.boxShadow;\n\n return {\n transform: [{ scale: scale.value }],\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 // Determine what should be visible in start slot\n const showLoading = !!loading;\n const showStartIcon = !!startIcon && !loading;\n const showEndIcon = !!endIcon && !loading;\n\n const iconSizeToken = (buttonStyles.icon.iconSizeToken as IconSize) ?? 'sm';\n\n // Containers pre-scale by the capped factor; icon styles stay unscaled and\n // native grows them by the same capped multiplier.\n const scaledIconSize = Math.round(iconSize * fontScaleFactor);\n const scaledIconCellHeight = Math.ceil(iconCellHeight * fontScaleFactor);\n\n // Start slot: either loading spinner or start icon\n const startContent = (\n <AnimatedIconSlot\n visible={showLoading || showStartIcon}\n iconSize={scaledIconSize}\n iconCellHeight={scaledIconCellHeight}\n gap={buttonGap}\n >\n {showLoading ? (\n <ActivityIndicator size={scaledIconSize} color={buttonStyles.icon.color} />\n ) : (\n <IconSlot\n icon={startIcon}\n size={iconSizeToken}\n variant={iconVariant}\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n style={[buttonStyles.icon, { lineHeight: iconCellHeight }]}\n />\n )}\n </AnimatedIconSlot>\n );\n\n // End slot: only end icon (no loading here)\n const endContent = (\n <AnimatedIconSlot\n visible={showEndIcon}\n iconSize={scaledIconSize}\n iconCellHeight={scaledIconCellHeight}\n gap={buttonGap}\n >\n <IconSlot\n icon={endIcon}\n size={iconSizeToken}\n variant={iconVariant}\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n style={[buttonStyles.icon, { lineHeight: iconCellHeight }]}\n />\n </AnimatedIconSlot>\n );\n\n const rootStyles = useMemo(\n () => [\n buttonStyles.root,\n { minHeight, paddingVertical },\n animatedStyles,\n typeof style === 'function' ? style({ pressed }) : style,\n ],\n [buttonStyles.root, minHeight, paddingVertical, animatedStyles, style, pressed],\n );\n\n /* --------------------------------- Render --------------------------------- */\n return (\n <AnimatedPressable\n ref={ref}\n disabled={disabled}\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 alignContent=\"center\"\n style={rootStyles}\n {...props}\n >\n {startContent}\n {childrenNode}\n {endContent}\n </AnimatedPressable>\n );\n});\n\nButton.displayName = 'Button';\n\nexport { Button, type ButtonProps };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Button.js","names":["Text"],"sources":["../../src/components/Button.tsx"],"sourcesContent":["import type { ButtonSize, ButtonVariantFlat, IconSize, IconVariant } from '@yahoo/uds-types';\nimport type { Ref } from 'react';\nimport { isValidElement, memo, useCallback, useMemo, useState } from 'react';\nimport type { View } from 'react-native';\nimport { ActivityIndicator } from 'react-native';\nimport Animated, {\n Easing,\n interpolate,\n useAnimatedStyle,\n useDerivedValue,\n useSharedValue,\n withSpring,\n withTiming,\n} from 'react-native-reanimated';\n// eslint-disable-next-line uds/no-use-unistyles -- button control height from theme size layers\nimport { useUnistyles } from 'react-native-unistyles';\nimport { useAnimatedTheme } from 'react-native-unistyles/reanimated';\n\nimport { buttonStyles } from '../../generated/styles';\nimport { ResolvedControlFontScalingProvider } from '../fontScaling/FontScalingContext';\nimport { useFontScale } from '../fontScaling/useFontScale';\nimport { useMaxFontSizeMultiplier } from '../fontScaling/useMaxFontSizeMultiplier';\nimport { BUTTON_SPRING_CONFIG, SCALE_EFFECTS } from '../motion';\nimport { getButtonControlMetrics, getButtonTextStyle } from './Button/buttonTheme';\nimport type { IconSlotType } from './IconSlot';\nimport { IconSlot } from './IconSlot';\nimport type { PressableProps } from './Pressable';\nimport { AnimatedPressable } from './Pressable';\nimport { Text } from './Text';\n\n/* -------------------------------------------------------------------------- */\n/* Animation Helpers */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Interpolates a boxShadow string by scaling the alpha of all colors.\n * This allows smooth fade-in/out of shadows.\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/* Animated Icon Slot */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Animated wrapper for icon/loading content.\n * Matches web Button icon animation: scale 0.7→1, opacity 0→1, width 0→auto\n * Uses staggered animation: opacity waits until halfway through width animation.\n */\nfunction AnimatedIconSlot({\n children,\n visible,\n iconSize,\n iconCellHeight,\n gap,\n}: {\n children: React.ReactNode;\n visible: boolean;\n iconSize: number;\n iconCellHeight: number;\n gap: number;\n}) {\n // Use useDerivedValue instead of useEffect + useSharedValue\n // This is the idiomatic Reanimated pattern for deriving animated values from React state\n const progress = useDerivedValue(\n () => withSpring(visible ? 1 : 0, BUTTON_SPRING_CONFIG),\n [visible],\n );\n\n const animatedStyle = useAnimatedStyle(() => {\n // Total width includes icon + gap when visible\n const totalWidth = iconSize + gap;\n const width = interpolate(progress.value, [0, 1], [0, totalWidth]);\n\n // Staggered animation: opacity starts at 50% of width animation\n // On enter: width expands first, then icon fades in\n // On exit: icon fades out first, then width collapses\n const opacity = interpolate(progress.value, [0.5, 1], [0, 1], 'clamp');\n const scale = interpolate(progress.value, [0.5, 1], [0.7, 1], 'clamp');\n\n return {\n width,\n opacity,\n transform: [{ scale }],\n overflow: 'hidden' as const,\n minHeight: iconCellHeight,\n justifyContent: 'center' as const,\n };\n });\n\n return <Animated.View style={animatedStyle}>{children}</Animated.View>;\n}\n\n// function LoadingIcon({ size, variant }: { size: ButtonSize, variant: ButtonVariantFlat }) {\n// const { theme } = useUnistyles();\n// const themeKey = `buttonVariant${variantToCapitalMap[variant]}IconRest` as const;\n// const iconSize = theme.components.buttonSizeLgIconRest.fontSize;\n// return <ActivityIndicator size={iconSize} color={theme.colors.text.primary} />;\n// }\n\n/* -------------------------------------------------------------------------- */\n/* Button Props */\n/* -------------------------------------------------------------------------- */\n\ninterface ButtonProps extends Omit<PressableProps, 'children' | 'disabled'> {\n /** The visual style variant of the button @default 'primary' */\n variant?: ButtonVariantFlat;\n /** The size of the button @default 'md' */\n size?: ButtonSize;\n /** The icon style variant @default 'outline' */\n iconVariant?: IconVariant;\n /** Icon displayed before the button label */\n startIcon?: IconSlotType;\n /** Icon displayed after the button label */\n endIcon?: IconSlotType;\n /** Shows a loading spinner and disables the button */\n loading?: boolean;\n /** Whether the button is disabled */\n disabled?: boolean;\n /** Button label content */\n children?: React.ReactNode;\n /**\n * Disable motion effects (scale on press, icon animations)\n * @default false\n */\n disableEffects?: boolean;\n /**\n * Caps how far the label and icons grow with the OS text-size setting; the\n * button grows to fit the scaled content. Set null to remove the cap;\n * 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/* Button Component */\n/* -------------------------------------------------------------------------- */\n\n/**\n * **🖲️ A button element that can be used to trigger an action**\n *\n * @description\n * A button is a fundamental component used to trigger an action or event.\n * Buttons are interactive elements that users can click, tap, or otherwise\n * engage with to submit forms, open dialogues, or perform any other interaction.\n *\n * Features animated scale effect on press and smooth icon transitions matching\n * the web UDS Button behavior.\n *\n * @category Interactive\n * @platform mobile\n *\n * @example\n * ```tsx\n * import { Button } from '@yahoo/uds-mobile/Button';\n *\n * <Button onPress={() => console.log('pressed')}>Save</Button>\n * <Button variant=\"secondary\">Cancel</Button>\n * <Button startIcon=\"Add\" variant=\"brand\">Add Item</Button>\n * <Button loading>Saving...</Button>\n * ```\n *\n * @accessibility\n * - Sets `accessibilityRole=\"button\"` automatically\n * - Announces loading state to screen readers\n * - Use `accessibilityLabel` for icon-only buttons\n *\n * @see {@link IconButton} for icon-only buttons\n * @see {@link Link} for navigation actions\n */\nconst Button = memo(function Button({\n variant = 'primary',\n size = 'md',\n iconVariant = 'outline',\n startIcon,\n endIcon,\n loading,\n disabled,\n width: _width,\n children,\n style,\n accessibilityLabel,\n accessibilityHint,\n disableEffects = false,\n maxFontSizeMultiplier,\n onPressIn,\n onPressOut,\n ref,\n ...props\n}: ButtonProps) {\n const shouldAnimate = !disableEffects;\n\n // Inside a control, \"no cap resolved\" must force-uncap (0) rather than let\n // the label's own variant default or an inherited cap apply.\n const resolvedMaxFontSizeMultiplier =\n useMaxFontSizeMultiplier('control', maxFontSizeMultiplier) ?? 0;\n const fontScaleFactor = useFontScale(resolvedMaxFontSizeMultiplier);\n\n const { theme } = useUnistyles();\n const { minHeight, labelLineHeight, iconCellHeight, paddingVertical } = useMemo(\n () => getButtonControlMetrics(theme, size, fontScaleFactor),\n [theme, size, fontScaleFactor],\n );\n\n /* --------------------------------- State ---------------------------------- */\n const [pressed, setPressed] = useState(false);\n\n buttonStyles.useVariants({ size, variant, disabled, pressed });\n\n // Get gap and icon size from current variant styles\n const buttonGap = buttonStyles.root.gap;\n const iconSize = buttonStyles.icon.fontSize;\n\n // Get animated theme for boxShadow (useAnimatedVariantColor doesn't support non-color props)\n const animatedTheme = useAnimatedTheme();\n\n /* ------------------------------- Animation -------------------------------- */\n const scale = useSharedValue<number>(SCALE_EFFECTS.none);\n\n const handlePressIn = useCallback(\n (event: Parameters<NonNullable<PressableProps['onPressIn']>>[0]) => {\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(\n (event: Parameters<NonNullable<PressableProps['onPressOut']>>[0]) => {\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 /* -------------------------------- Content --------------------------------- */\n const childrenNode =\n children &&\n (isValidElement(children) ? (\n <ResolvedControlFontScalingProvider maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}>\n {children}\n </ResolvedControlFontScalingProvider>\n ) : (\n <Text\n numberOfLines={1}\n textAlign=\"center\"\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n style={getButtonTextStyle(buttonStyles.text, labelLineHeight)}\n >\n {children}\n </Text>\n ));\n\n const a11yState = useMemo(() => ({ disabled, busy: loading }), [disabled, loading]);\n\n /* --------------------------------- Styles --------------------------------- */\n // Animate pressed state for shadow (0 = rest, 1 = pressed)\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 animatedStyles = useAnimatedStyle(() => {\n // Get boxShadow from theme using flattened path (no camelCase conversion needed!)\n const components = animatedTheme.value.components;\n const buttonVariantPath = `button/variant/${variant}/root/pressed` as const;\n const shadowPressed = components[buttonVariantPath]?.boxShadow;\n\n return {\n transform: [{ scale: scale.value }],\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 // Determine what should be visible in start slot\n const showLoading = !!loading;\n const showStartIcon = !!startIcon && !loading;\n const showEndIcon = !!endIcon && !loading;\n\n const iconSizeToken = (buttonStyles.icon.iconSizeToken as IconSize) ?? 'sm';\n\n // Containers pre-scale by the capped factor; icon styles stay unscaled and\n // native grows them by the same capped multiplier.\n const scaledIconSize = Math.round(iconSize * fontScaleFactor);\n const scaledIconCellHeight = Math.ceil(iconCellHeight * fontScaleFactor);\n\n // Start slot: either loading spinner or start icon\n const startContent = (\n <AnimatedIconSlot\n visible={showLoading || showStartIcon}\n iconSize={scaledIconSize}\n iconCellHeight={scaledIconCellHeight}\n gap={buttonGap}\n >\n {showLoading ? (\n <ActivityIndicator size={scaledIconSize} color={buttonStyles.icon.color} />\n ) : (\n <IconSlot\n icon={startIcon}\n size={iconSizeToken}\n variant={iconVariant}\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n style={[buttonStyles.icon, { lineHeight: iconCellHeight }]}\n />\n )}\n </AnimatedIconSlot>\n );\n\n // End slot: only end icon (no loading here)\n const endContent = (\n <AnimatedIconSlot\n visible={showEndIcon}\n iconSize={scaledIconSize}\n iconCellHeight={scaledIconCellHeight}\n gap={buttonGap}\n >\n <IconSlot\n icon={endIcon}\n size={iconSizeToken}\n variant={iconVariant}\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n style={[buttonStyles.icon, { lineHeight: iconCellHeight }]}\n />\n </AnimatedIconSlot>\n );\n\n const rootStyles = useMemo(\n () => [\n buttonStyles.root,\n { minHeight, paddingVertical },\n animatedStyles,\n typeof style === 'function' ? style({ pressed }) : style,\n ],\n [buttonStyles.root, minHeight, paddingVertical, animatedStyles, style, pressed],\n );\n\n /* --------------------------------- Render --------------------------------- */\n return (\n <AnimatedPressable\n ref={ref}\n disabled={disabled}\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 alignContent=\"center\"\n style={rootStyles}\n {...props}\n >\n {startContent}\n {childrenNode}\n {endContent}\n </AnimatedPressable>\n );\n});\n\nButton.displayName = 'Button';\n\nexport { Button, type ButtonProps };\n"],"mappings":"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"}
|
|
@@ -127,6 +127,7 @@ const Checkbox = (0, react.memo)(function Checkbox({ label, labelPosition = "sta
|
|
|
127
127
|
required,
|
|
128
128
|
showRequiredAsterisk: required,
|
|
129
129
|
hasError,
|
|
130
|
+
flexShrink: "1",
|
|
130
131
|
maxFontSizeMultiplier: resolvedMaxFontSizeMultiplier,
|
|
131
132
|
style: generated_styles.checkboxStyles.text
|
|
132
133
|
});
|
|
@@ -150,7 +151,8 @@ const Checkbox = (0, react.memo)(function Checkbox({ label, labelPosition = "sta
|
|
|
150
151
|
disabled,
|
|
151
152
|
flexDirection: labelPosition === "start" ? "row" : "row-reverse",
|
|
152
153
|
alignItems: "center",
|
|
153
|
-
alignSelf: "
|
|
154
|
+
alignSelf: "stretch",
|
|
155
|
+
minHeight: 52,
|
|
154
156
|
accessibilityRole: "checkbox",
|
|
155
157
|
accessibilityState: a11yState,
|
|
156
158
|
accessibilityLabel: typeof label === "string" ? label : void 0,
|
|
@@ -124,6 +124,7 @@ const Checkbox = memo(function Checkbox({ label, labelPosition = "start", varian
|
|
|
124
124
|
required,
|
|
125
125
|
showRequiredAsterisk: required,
|
|
126
126
|
hasError,
|
|
127
|
+
flexShrink: "1",
|
|
127
128
|
maxFontSizeMultiplier: resolvedMaxFontSizeMultiplier,
|
|
128
129
|
style: checkboxStyles.text
|
|
129
130
|
});
|
|
@@ -147,7 +148,8 @@ const Checkbox = memo(function Checkbox({ label, labelPosition = "start", varian
|
|
|
147
148
|
disabled,
|
|
148
149
|
flexDirection: labelPosition === "start" ? "row" : "row-reverse",
|
|
149
150
|
alignItems: "center",
|
|
150
|
-
alignSelf: "
|
|
151
|
+
alignSelf: "stretch",
|
|
152
|
+
minHeight: 52,
|
|
151
153
|
accessibilityRole: "checkbox",
|
|
152
154
|
accessibilityState: a11yState,
|
|
153
155
|
accessibilityLabel: typeof label === "string" ? label : void 0,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Checkbox.js","names":[],"sources":["../../src/components/Checkbox.tsx"],"sourcesContent":["import { ICON_SIZE_MAP } from '@yahoo/uds-icons/tokens';\nimport type { CheckboxValue, CheckboxVariant, UniversalCheckboxProps } from '@yahoo/uds-types';\nimport type { Ref } from 'react';\nimport { memo, useCallback, useId, useMemo, useState } from 'react';\nimport type { View, ViewProps } from 'react-native';\nimport Animated, { useAnimatedStyle, useDerivedValue, withSpring } from 'react-native-reanimated';\n\nimport { checkboxStyles } from '../../generated/styles';\nimport { useFontScale } from '../fontScaling/useFontScale';\nimport { useMaxFontSizeMultiplier } from '../fontScaling/useMaxFontSizeMultiplier';\nimport { BUTTON_SPRING_CONFIG, SCALE_EFFECTS } from '../motion';\nimport { FormLabel } from './FormLabel';\nimport { Icon } from './Icon';\nimport { Pressable } from './Pressable';\n\n/* -------------------------------------------------------------------------- */\n/* Types */\n/* -------------------------------------------------------------------------- */\n\n/** Derived from CheckboxValue - maps boolean to 'checked'/'unchecked' string literals */\ntype CheckboxValueState = Exclude<CheckboxValue, boolean> | 'checked' | 'unchecked';\n\ninterface CheckboxProps extends Omit<ViewProps, 'style'>, UniversalCheckboxProps {\n /** Ref to the underlying View */\n ref?: Ref<View>;\n /** Default checked state for uncontrolled mode */\n defaultChecked?: boolean;\n /** Whether the checkbox is disabled */\n disabled?: boolean;\n /** Whether the checkbox is required */\n required?: boolean;\n /**\n * Caps how far the box, checkmark, and label grow with the OS text-size\n * setting; all three scale by the same factor. Set null to remove the cap;\n * override app-wide via UDSFontScalingProvider.\n * @default 2\n */\n maxFontSizeMultiplier?: number | null;\n /** Callback fired when the checked state changes */\n onChange?: (checked: boolean) => void;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Variant Mapping */\n/* -------------------------------------------------------------------------- */\n\n/** All style variants available in the generated styles (includes error variants) */\ntype CheckboxStyleVariant = 'primary' | 'secondary' | 'alert' | 'alert-secondary';\n\nconst VARIANT_ERROR_MAP: Record<CheckboxVariant, CheckboxStyleVariant> = {\n primary: 'alert',\n secondary: 'alert-secondary',\n};\n\n/* -------------------------------------------------------------------------- */\n/* Checkbox Component */\n/* -------------------------------------------------------------------------- */\n\n/**\n * **⚙️ A checkbox component**\n *\n * @description\n * A checkbox component allows users to select one or multiple options from a set.\n * It represents a binary state, typically as checked or unchecked, and optionally\n * includes a third \"indeterminate\" state to indicate partial selection.\n *\n * @category Form\n * @platform mobile\n *\n * @example\n * ```tsx\n * import { Checkbox } from '@yahoo/uds-mobile/Checkbox';\n *\n * <Checkbox label=\"Agree to terms\" />\n * <Checkbox label=\"Subscribe\" checked={true} onChange={setChecked} />\n * <Checkbox label=\"Partial\" checked=\"indeterminate\" />\n * ```\n *\n * @usage\n * - Forms: For selecting options\n * - Settings: For toggling preferences\n * - Filters: For multi-select filtering\n *\n * @accessibility\n * - Sets `accessibilityRole=\"checkbox\"` automatically\n * - Announces checked/unchecked/mixed state\n * - Label is used as accessibility label\n * - Supports `reduceMotion` for users who prefer reduced motion\n *\n * @see {@link Radio} for single-select options\n * @see {@link Switch} for binary toggles\n */\nconst Checkbox = memo(function Checkbox({\n // Checkbox props\n label,\n labelPosition = 'start',\n variant: variantProp = 'primary',\n size = 'md',\n checked: checkedProp,\n hasError,\n reduceMotion,\n // Native props\n defaultChecked,\n disabled,\n required,\n maxFontSizeMultiplier,\n onChange,\n ref,\n ...viewProps\n}: CheckboxProps) {\n const generatedId = useId();\n const uid = `uds-checkbox-${generatedId}`;\n\n // One capped factor governs box, checkmark, and label so they stay cohesive.\n const resolvedMaxFontSizeMultiplier =\n useMaxFontSizeMultiplier('control', maxFontSizeMultiplier) ?? 0;\n const fontScaleFactor = useFontScale(resolvedMaxFontSizeMultiplier);\n // The check/minus glyph is drawn at the 'sm' icon size; scale it in step with\n // the box rather than letting native font scaling size it independently.\n const scaledMarkSize = Math.round(ICON_SIZE_MAP.sm * fontScaleFactor);\n\n /* --------------------------------- State ---------------------------------- */\n const isControlled = checkedProp !== undefined;\n const [internalChecked, setInternalChecked] = useState<CheckboxValue>(\n isControlled ? checkedProp : (defaultChecked ?? false),\n );\n const [pressed, setPressed] = useState(false);\n\n // Use controlled value if provided, otherwise use internal state\n const checked = isControlled ? checkedProp : internalChecked;\n\n /* ------------------------------ Derived Values ---------------------------- */\n const valueState: CheckboxValueState = useMemo(() => {\n if (checked === 'indeterminate') {\n return 'indeterminate';\n }\n return checked ? 'checked' : 'unchecked';\n }, [checked]);\n\n const iconName = valueState === 'indeterminate' ? 'Minus' : 'Check';\n const showIcon = valueState !== 'unchecked';\n\n /* -------------------------------- Animation ------------------------------- */\n const shouldAnimate = !reduceMotion;\n\n // Scale animation for press feedback (disabled when reduceMotion is true)\n const scaleProgress = useDerivedValue(\n () =>\n shouldAnimate\n ? withSpring(pressed ? SCALE_EFFECTS.down : SCALE_EFFECTS.none, BUTTON_SPRING_CONFIG)\n : SCALE_EFFECTS.none, // No scale effect when reduceMotion is true\n [pressed, shouldAnimate],\n );\n\n const checkboxAnimatedStyle = useAnimatedStyle(() => ({\n transform: [{ scale: scaleProgress.value }],\n }));\n\n /* -------------------------------- Handlers -------------------------------- */\n const handlePress = useCallback(() => {\n if (disabled) {\n return;\n }\n\n const newChecked = checked === 'indeterminate' ? true : !checked;\n\n if (!isControlled) {\n setInternalChecked(newChecked);\n }\n\n onChange?.(newChecked);\n }, [disabled, checked, isControlled, onChange]);\n\n const handlePressIn = useCallback(() => {\n setPressed(true);\n }, []);\n\n const handlePressOut = useCallback(() => {\n setPressed(false);\n }, []);\n\n /* --------------------------------- Styles --------------------------------- */\n // Compute the style variant (primary/secondary maps to alert variants when hasError)\n const styleVariant: CheckboxStyleVariant = hasError\n ? VARIANT_ERROR_MAP[variantProp]\n : variantProp;\n\n checkboxStyles.useVariants({\n size,\n variant: styleVariant, // primary, secondary, alert, alert-secondary\n value: valueState, // checked, unchecked, indeterminate\n disabled,\n pressed,\n });\n\n const rootStyle = useMemo(\n () => [checkboxStyles.root, { opacity: disabled ? 0.5 : 1 }],\n [checkboxStyles.root, disabled],\n );\n\n const checkboxBoxStyle = useMemo(() => {\n // The box grows by the capped factor to keep containing the checkmark.\n // borderWidth stays fixed (stroke weight doesn't scale in the OS's own\n // controls either).\n const { width, height, borderRadius } = checkboxStyles.checkbox;\n const scaledBox =\n fontScaleFactor !== 1 && typeof width === 'number' && typeof height === 'number'\n ? {\n width: Math.round(width * fontScaleFactor),\n height: Math.round(height * fontScaleFactor),\n ...(typeof borderRadius === 'number' && {\n borderRadius: Math.round(borderRadius * fontScaleFactor),\n }),\n }\n : undefined;\n\n return [\n checkboxStyles.checkbox,\n scaledBox,\n { alignItems: 'center' as const, justifyContent: 'center' as const },\n checkboxAnimatedStyle,\n ];\n }, [checkboxStyles.checkbox, fontScaleFactor, checkboxAnimatedStyle]);\n\n /* ---------------------------- Render Helpers ------------------------------ */\n const labelContent = useMemo(() => {\n if (!label) {\n return null;\n }\n\n return (\n <FormLabel\n color=\"inherit\"\n variant=\"inherit\"\n label={label}\n required={required}\n showRequiredAsterisk={required}\n hasError={hasError}\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n style={checkboxStyles.text}\n />\n );\n }, [label, required, hasError, resolvedMaxFontSizeMultiplier, checkboxStyles.text]);\n\n const a11yState = useMemo(\n () => ({\n disabled,\n checked:\n valueState === 'checked'\n ? true\n : valueState === 'indeterminate'\n ? ('mixed' as const)\n : false,\n }),\n [disabled, valueState],\n );\n\n /* --------------------------------- Render --------------------------------- */\n return (\n <Pressable\n ref={ref}\n nativeID={uid}\n onPress={handlePress}\n onPressIn={handlePressIn}\n onPressOut={handlePressOut}\n disabled={disabled}\n flexDirection={labelPosition === 'start' ? 'row' : 'row-reverse'}\n alignItems=\"center\"\n alignSelf=\"flex-start\"\n accessibilityRole=\"checkbox\"\n accessibilityState={a11yState}\n accessibilityLabel={typeof label === 'string' ? label : undefined}\n {...viewProps}\n style={rootStyle}\n >\n <Animated.View style={checkboxBoxStyle}>\n {showIcon && (\n <Icon\n name={iconName}\n size=\"sm\"\n // Size the mark from the same factor that scaled the box, with\n // native scaling off: letting the box scale in JS while the glyph\n // scaled natively left the two to round independently (and to\n // disagree outright under Android's non-linear scaling), which\n // drifted the mark off-centre as sizes grew.\n dangerouslySetSize={scaledMarkSize}\n allowFontScaling={false}\n style={checkboxStyles.checkboxIcon}\n />\n )}\n </Animated.View>\n\n {labelContent}\n </Pressable>\n );\n});\n\nCheckbox.displayName = 'Checkbox';\n\nexport { Checkbox, type CheckboxProps };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Checkbox.js","names":[],"sources":["../../src/components/Checkbox.tsx"],"sourcesContent":["import { ICON_SIZE_MAP } from '@yahoo/uds-icons/tokens';\nimport type { CheckboxValue, CheckboxVariant, UniversalCheckboxProps } from '@yahoo/uds-types';\nimport type { Ref } from 'react';\nimport { memo, useCallback, useId, useMemo, useState } from 'react';\nimport type { View, ViewProps } from 'react-native';\nimport Animated, { useAnimatedStyle, useDerivedValue, withSpring } from 'react-native-reanimated';\n\nimport { checkboxStyles } from '../../generated/styles';\nimport { useFontScale } from '../fontScaling/useFontScale';\nimport { useMaxFontSizeMultiplier } from '../fontScaling/useMaxFontSizeMultiplier';\nimport { BUTTON_SPRING_CONFIG, SCALE_EFFECTS } from '../motion';\nimport { FormLabel } from './FormLabel';\nimport { Icon } from './Icon';\nimport { Pressable } from './Pressable';\n\n/* -------------------------------------------------------------------------- */\n/* Types */\n/* -------------------------------------------------------------------------- */\n\n/** Derived from CheckboxValue - maps boolean to 'checked'/'unchecked' string literals */\ntype CheckboxValueState = Exclude<CheckboxValue, boolean> | 'checked' | 'unchecked';\n\ninterface CheckboxProps extends Omit<ViewProps, 'style'>, UniversalCheckboxProps {\n /** Ref to the underlying View */\n ref?: Ref<View>;\n /** Default checked state for uncontrolled mode */\n defaultChecked?: boolean;\n /** Whether the checkbox is disabled */\n disabled?: boolean;\n /** Whether the checkbox is required */\n required?: boolean;\n /**\n * Caps how far the box, checkmark, and label grow with the OS text-size\n * setting; all three scale by the same factor. Set null to remove the cap;\n * override app-wide via UDSFontScalingProvider.\n * @default 2\n */\n maxFontSizeMultiplier?: number | null;\n /** Callback fired when the checked state changes */\n onChange?: (checked: boolean) => void;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Variant Mapping */\n/* -------------------------------------------------------------------------- */\n\n/** All style variants available in the generated styles (includes error variants) */\ntype CheckboxStyleVariant = 'primary' | 'secondary' | 'alert' | 'alert-secondary';\n\nconst VARIANT_ERROR_MAP: Record<CheckboxVariant, CheckboxStyleVariant> = {\n primary: 'alert',\n secondary: 'alert-secondary',\n};\n\n/* -------------------------------------------------------------------------- */\n/* Checkbox Component */\n/* -------------------------------------------------------------------------- */\n\n/**\n * **⚙️ A checkbox component**\n *\n * @description\n * A checkbox component allows users to select one or multiple options from a set.\n * It represents a binary state, typically as checked or unchecked, and optionally\n * includes a third \"indeterminate\" state to indicate partial selection.\n *\n * @category Form\n * @platform mobile\n *\n * @example\n * ```tsx\n * import { Checkbox } from '@yahoo/uds-mobile/Checkbox';\n *\n * <Checkbox label=\"Agree to terms\" />\n * <Checkbox label=\"Subscribe\" checked={true} onChange={setChecked} />\n * <Checkbox label=\"Partial\" checked=\"indeterminate\" />\n * ```\n *\n * @usage\n * - Forms: For selecting options\n * - Settings: For toggling preferences\n * - Filters: For multi-select filtering\n *\n * @accessibility\n * - Sets `accessibilityRole=\"checkbox\"` automatically\n * - Announces checked/unchecked/mixed state\n * - Label is used as accessibility label\n * - Supports `reduceMotion` for users who prefer reduced motion\n *\n * @see {@link Radio} for single-select options\n * @see {@link Switch} for binary toggles\n */\nconst Checkbox = memo(function Checkbox({\n // Checkbox props\n label,\n labelPosition = 'start',\n variant: variantProp = 'primary',\n size = 'md',\n checked: checkedProp,\n hasError,\n reduceMotion,\n // Native props\n defaultChecked,\n disabled,\n required,\n maxFontSizeMultiplier,\n onChange,\n ref,\n ...viewProps\n}: CheckboxProps) {\n const generatedId = useId();\n const uid = `uds-checkbox-${generatedId}`;\n\n // One capped factor governs box, checkmark, and label so they stay cohesive.\n const resolvedMaxFontSizeMultiplier =\n useMaxFontSizeMultiplier('control', maxFontSizeMultiplier) ?? 0;\n const fontScaleFactor = useFontScale(resolvedMaxFontSizeMultiplier);\n // The check/minus glyph is drawn at the 'sm' icon size; scale it in step with\n // the box rather than letting native font scaling size it independently.\n const scaledMarkSize = Math.round(ICON_SIZE_MAP.sm * fontScaleFactor);\n\n /* --------------------------------- State ---------------------------------- */\n const isControlled = checkedProp !== undefined;\n const [internalChecked, setInternalChecked] = useState<CheckboxValue>(\n isControlled ? checkedProp : (defaultChecked ?? false),\n );\n const [pressed, setPressed] = useState(false);\n\n // Use controlled value if provided, otherwise use internal state\n const checked = isControlled ? checkedProp : internalChecked;\n\n /* ------------------------------ Derived Values ---------------------------- */\n const valueState: CheckboxValueState = useMemo(() => {\n if (checked === 'indeterminate') {\n return 'indeterminate';\n }\n return checked ? 'checked' : 'unchecked';\n }, [checked]);\n\n const iconName = valueState === 'indeterminate' ? 'Minus' : 'Check';\n const showIcon = valueState !== 'unchecked';\n\n /* -------------------------------- Animation ------------------------------- */\n const shouldAnimate = !reduceMotion;\n\n // Scale animation for press feedback (disabled when reduceMotion is true)\n const scaleProgress = useDerivedValue(\n () =>\n shouldAnimate\n ? withSpring(pressed ? SCALE_EFFECTS.down : SCALE_EFFECTS.none, BUTTON_SPRING_CONFIG)\n : SCALE_EFFECTS.none, // No scale effect when reduceMotion is true\n [pressed, shouldAnimate],\n );\n\n const checkboxAnimatedStyle = useAnimatedStyle(() => ({\n transform: [{ scale: scaleProgress.value }],\n }));\n\n /* -------------------------------- Handlers -------------------------------- */\n const handlePress = useCallback(() => {\n if (disabled) {\n return;\n }\n\n const newChecked = checked === 'indeterminate' ? true : !checked;\n\n if (!isControlled) {\n setInternalChecked(newChecked);\n }\n\n onChange?.(newChecked);\n }, [disabled, checked, isControlled, onChange]);\n\n const handlePressIn = useCallback(() => {\n setPressed(true);\n }, []);\n\n const handlePressOut = useCallback(() => {\n setPressed(false);\n }, []);\n\n /* --------------------------------- Styles --------------------------------- */\n // Compute the style variant (primary/secondary maps to alert variants when hasError)\n const styleVariant: CheckboxStyleVariant = hasError\n ? VARIANT_ERROR_MAP[variantProp]\n : variantProp;\n\n checkboxStyles.useVariants({\n size,\n variant: styleVariant, // primary, secondary, alert, alert-secondary\n value: valueState, // checked, unchecked, indeterminate\n disabled,\n pressed,\n });\n\n const rootStyle = useMemo(\n () => [checkboxStyles.root, { opacity: disabled ? 0.5 : 1 }],\n [checkboxStyles.root, disabled],\n );\n\n const checkboxBoxStyle = useMemo(() => {\n // The box grows by the capped factor to keep containing the checkmark.\n // borderWidth stays fixed (stroke weight doesn't scale in the OS's own\n // controls either).\n const { width, height, borderRadius } = checkboxStyles.checkbox;\n const scaledBox =\n fontScaleFactor !== 1 && typeof width === 'number' && typeof height === 'number'\n ? {\n width: Math.round(width * fontScaleFactor),\n height: Math.round(height * fontScaleFactor),\n ...(typeof borderRadius === 'number' && {\n borderRadius: Math.round(borderRadius * fontScaleFactor),\n }),\n }\n : undefined;\n\n return [\n checkboxStyles.checkbox,\n scaledBox,\n { alignItems: 'center' as const, justifyContent: 'center' as const },\n checkboxAnimatedStyle,\n ];\n }, [checkboxStyles.checkbox, fontScaleFactor, checkboxAnimatedStyle]);\n\n /* ---------------------------- Render Helpers ------------------------------ */\n const labelContent = useMemo(() => {\n if (!label) {\n return null;\n }\n\n return (\n <FormLabel\n color=\"inherit\"\n variant=\"inherit\"\n label={label}\n required={required}\n showRequiredAsterisk={required}\n hasError={hasError}\n flexShrink=\"1\"\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n style={checkboxStyles.text}\n />\n );\n }, [label, required, hasError, resolvedMaxFontSizeMultiplier, checkboxStyles.text]);\n\n const a11yState = useMemo(\n () => ({\n disabled,\n checked:\n valueState === 'checked'\n ? true\n : valueState === 'indeterminate'\n ? ('mixed' as const)\n : false,\n }),\n [disabled, valueState],\n );\n\n /* --------------------------------- Render --------------------------------- */\n return (\n <Pressable\n ref={ref}\n nativeID={uid}\n onPress={handlePress}\n onPressIn={handlePressIn}\n onPressOut={handlePressOut}\n disabled={disabled}\n flexDirection={labelPosition === 'start' ? 'row' : 'row-reverse'}\n alignItems=\"center\"\n alignSelf=\"stretch\"\n minHeight={52}\n accessibilityRole=\"checkbox\"\n accessibilityState={a11yState}\n accessibilityLabel={typeof label === 'string' ? label : undefined}\n {...viewProps}\n style={rootStyle}\n >\n <Animated.View style={checkboxBoxStyle}>\n {showIcon && (\n <Icon\n name={iconName}\n size=\"sm\"\n // Size the mark from the same factor that scaled the box, with\n // native scaling off: letting the box scale in JS while the glyph\n // scaled natively left the two to round independently (and to\n // disagree outright under Android's non-linear scaling), which\n // drifted the mark off-centre as sizes grew.\n dangerouslySetSize={scaledMarkSize}\n allowFontScaling={false}\n style={checkboxStyles.checkboxIcon}\n />\n )}\n </Animated.View>\n\n {labelContent}\n </Pressable>\n );\n});\n\nCheckbox.displayName = 'Checkbox';\n\nexport { Checkbox, type CheckboxProps };\n"],"mappings":"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"}
|
package/dist/components/Icon.cjs
CHANGED
|
@@ -56,7 +56,8 @@ const MulticolorIcon = (0, react.memo)(function MulticolorIcon({ name, size = DE
|
|
|
56
56
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native_svg.SvgXml, {
|
|
57
57
|
xml: svgContent,
|
|
58
58
|
width: renderedSize,
|
|
59
|
-
height: renderedSize
|
|
59
|
+
height: renderedSize,
|
|
60
|
+
viewBox: `0 0 ${pixelSize} ${pixelSize}`
|
|
60
61
|
})
|
|
61
62
|
});
|
|
62
63
|
});
|
|
@@ -113,12 +114,12 @@ const OutlineOrFillIcon = (0, react.memo)(function OutlineOrFillIcon({ name, siz
|
|
|
113
114
|
lineHeight: require_components_Button_buttonTheme.getSafeIconCell(pixelSize)
|
|
114
115
|
},
|
|
115
116
|
generated_styles.styles.foundation,
|
|
117
|
+
style,
|
|
116
118
|
dangerouslySetColor ? { color: dangerouslySetColor } : void 0,
|
|
117
119
|
dangerouslySetSize ? {
|
|
118
120
|
fontSize: dangerouslySetSize,
|
|
119
121
|
lineHeight: require_components_Button_buttonTheme.getSafeIconCell(dangerouslySetSize)
|
|
120
|
-
} : void 0
|
|
121
|
-
style
|
|
122
|
+
} : void 0
|
|
122
123
|
], [
|
|
123
124
|
dangerouslySetColor,
|
|
124
125
|
dangerouslySetSize,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Icon.d.cts","names":[],"sources":["../../src/components/Icon.tsx"],"mappings":";;;;;;;;;;KAqCK,QAAA,GAAW,SAAA,GAAY,YAAA;AAAA,UAElB,SAAA,SAAkB,IAAA,CAAK,SAAA;;EAE/B,GAAA,GAAM,GAAA,CAAI,IAAA;EAJC;EAMX,IAAA,EAAM,QAAA;EANQ;EAQd,IAAA,GAAO,QAAA;EANC;EAQR,KAAA,GAAQ,UAAA;;EAER,OAAA,GAAU,WAAA;EARA;;;;;;EAeV,qBAAA;EAEA,eAAA,GAAkB,UAAA;EAElB,YAAA,GAAe,UAAA;EACf,oBAAA,GAAuB,UAAA;EACvB,kBAAA,GAAqB,UAAA;EACrB,uBAAA,GAA0B,UAAA;EAC1B,qBAAA,GAAwB,UAAA;EACxB,WAAA,GAAc,UAAA;EACd,gBAAA,GAAmB,UAAA;EACnB,cAAA,GAAiB,UAAA;EACjB,cAAA,GAAiB,UAAA;EACjB,iBAAA,GAAoB,UAAA;EACpB,WAAA,GAAc,UAAA;EACd,mBAAA,GAAsB,UAAA;EACtB,qBAAA,GAAwB,UAAA;EACxB,gBAAA,GAAmB,UAAA;EACnB,cAAA,GAAiB,UAAA;EACjB,cAAA,GAAiB,UAAA;EACjB,iBAAA,GAAoB,UAAA;EAEpB,UAAA,GAAa,UAAA;EAEb,YAAA,GAAe,UAAA;EACf,UAAA,GAAa,UAAA;EACb,UAAA,GAAa,UAAA;EACb,aAAA,GAAgB,UAAA;EAChB,iBAAA,GAAoB,UAAA;EACpB,eAAA,GAAkB,UAAA;EAClB,OAAA,GAAU,UAAA;EAEV,WAAA,GAAc,UAAA;EACd,SAAA,GAAY,UAAA;EACZ,SAAA,GAAY,UAAA;EACZ,YAAA,GAAe,UAAA;EACf,gBAAA,GAAmB,UAAA;EACnB,cAAA,GAAiB,UAAA;EACjB,MAAA,GAAS,UAAA;EAMD;EAJR,mBAAA;EAzD8B;EA2D9B,kBAAA;EA3D0B;EA6D1B,KAAA,GAAQ,SAAA,CAAU,SAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"Icon.d.cts","names":[],"sources":["../../src/components/Icon.tsx"],"mappings":";;;;;;;;;;KAqCK,QAAA,GAAW,SAAA,GAAY,YAAA;AAAA,UAElB,SAAA,SAAkB,IAAA,CAAK,SAAA;;EAE/B,GAAA,GAAM,GAAA,CAAI,IAAA;EAJC;EAMX,IAAA,EAAM,QAAA;EANQ;EAQd,IAAA,GAAO,QAAA;EANC;EAQR,KAAA,GAAQ,UAAA;;EAER,OAAA,GAAU,WAAA;EARA;;;;;;EAeV,qBAAA;EAEA,eAAA,GAAkB,UAAA;EAElB,YAAA,GAAe,UAAA;EACf,oBAAA,GAAuB,UAAA;EACvB,kBAAA,GAAqB,UAAA;EACrB,uBAAA,GAA0B,UAAA;EAC1B,qBAAA,GAAwB,UAAA;EACxB,WAAA,GAAc,UAAA;EACd,gBAAA,GAAmB,UAAA;EACnB,cAAA,GAAiB,UAAA;EACjB,cAAA,GAAiB,UAAA;EACjB,iBAAA,GAAoB,UAAA;EACpB,WAAA,GAAc,UAAA;EACd,mBAAA,GAAsB,UAAA;EACtB,qBAAA,GAAwB,UAAA;EACxB,gBAAA,GAAmB,UAAA;EACnB,cAAA,GAAiB,UAAA;EACjB,cAAA,GAAiB,UAAA;EACjB,iBAAA,GAAoB,UAAA;EAEpB,UAAA,GAAa,UAAA;EAEb,YAAA,GAAe,UAAA;EACf,UAAA,GAAa,UAAA;EACb,UAAA,GAAa,UAAA;EACb,aAAA,GAAgB,UAAA;EAChB,iBAAA,GAAoB,UAAA;EACpB,eAAA,GAAkB,UAAA;EAClB,OAAA,GAAU,UAAA;EAEV,WAAA,GAAc,UAAA;EACd,SAAA,GAAY,UAAA;EACZ,SAAA,GAAY,UAAA;EACZ,YAAA,GAAe,UAAA;EACf,gBAAA,GAAmB,UAAA;EACnB,cAAA,GAAiB,UAAA;EACjB,MAAA,GAAS,UAAA;EAMD;EAJR,mBAAA;EAzD8B;EA2D9B,kBAAA;EA3D0B;EA6D1B,KAAA,GAAQ,SAAA,CAAU,SAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAgPd,IAAA,EAAI,OAAA,CAAA,oBAAA,CAAA,SAAA;AAAA,cAaG,SAAA;AAAA,cACA,mBAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Icon.d.ts","names":[],"sources":["../../src/components/Icon.tsx"],"mappings":";;;;;;;;;;KAqCK,QAAA,GAAW,SAAA,GAAY,YAAA;AAAA,UAElB,SAAA,SAAkB,IAAA,CAAK,SAAA;;EAE/B,GAAA,GAAM,GAAA,CAAI,IAAA;EAJC;EAMX,IAAA,EAAM,QAAA;EANQ;EAQd,IAAA,GAAO,QAAA;EANC;EAQR,KAAA,GAAQ,UAAA;;EAER,OAAA,GAAU,WAAA;EARA;;;;;;EAeV,qBAAA;EAEA,eAAA,GAAkB,UAAA;EAElB,YAAA,GAAe,UAAA;EACf,oBAAA,GAAuB,UAAA;EACvB,kBAAA,GAAqB,UAAA;EACrB,uBAAA,GAA0B,UAAA;EAC1B,qBAAA,GAAwB,UAAA;EACxB,WAAA,GAAc,UAAA;EACd,gBAAA,GAAmB,UAAA;EACnB,cAAA,GAAiB,UAAA;EACjB,cAAA,GAAiB,UAAA;EACjB,iBAAA,GAAoB,UAAA;EACpB,WAAA,GAAc,UAAA;EACd,mBAAA,GAAsB,UAAA;EACtB,qBAAA,GAAwB,UAAA;EACxB,gBAAA,GAAmB,UAAA;EACnB,cAAA,GAAiB,UAAA;EACjB,cAAA,GAAiB,UAAA;EACjB,iBAAA,GAAoB,UAAA;EAEpB,UAAA,GAAa,UAAA;EAEb,YAAA,GAAe,UAAA;EACf,UAAA,GAAa,UAAA;EACb,UAAA,GAAa,UAAA;EACb,aAAA,GAAgB,UAAA;EAChB,iBAAA,GAAoB,UAAA;EACpB,eAAA,GAAkB,UAAA;EAClB,OAAA,GAAU,UAAA;EAEV,WAAA,GAAc,UAAA;EACd,SAAA,GAAY,UAAA;EACZ,SAAA,GAAY,UAAA;EACZ,YAAA,GAAe,UAAA;EACf,gBAAA,GAAmB,UAAA;EACnB,cAAA,GAAiB,UAAA;EACjB,MAAA,GAAS,UAAA;EAMD;EAJR,mBAAA;EAzD8B;EA2D9B,kBAAA;EA3D0B;EA6D1B,KAAA,GAAQ,SAAA,CAAU,SAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"Icon.d.ts","names":[],"sources":["../../src/components/Icon.tsx"],"mappings":";;;;;;;;;;KAqCK,QAAA,GAAW,SAAA,GAAY,YAAA;AAAA,UAElB,SAAA,SAAkB,IAAA,CAAK,SAAA;;EAE/B,GAAA,GAAM,GAAA,CAAI,IAAA;EAJC;EAMX,IAAA,EAAM,QAAA;EANQ;EAQd,IAAA,GAAO,QAAA;EANC;EAQR,KAAA,GAAQ,UAAA;;EAER,OAAA,GAAU,WAAA;EARA;;;;;;EAeV,qBAAA;EAEA,eAAA,GAAkB,UAAA;EAElB,YAAA,GAAe,UAAA;EACf,oBAAA,GAAuB,UAAA;EACvB,kBAAA,GAAqB,UAAA;EACrB,uBAAA,GAA0B,UAAA;EAC1B,qBAAA,GAAwB,UAAA;EACxB,WAAA,GAAc,UAAA;EACd,gBAAA,GAAmB,UAAA;EACnB,cAAA,GAAiB,UAAA;EACjB,cAAA,GAAiB,UAAA;EACjB,iBAAA,GAAoB,UAAA;EACpB,WAAA,GAAc,UAAA;EACd,mBAAA,GAAsB,UAAA;EACtB,qBAAA,GAAwB,UAAA;EACxB,gBAAA,GAAmB,UAAA;EACnB,cAAA,GAAiB,UAAA;EACjB,cAAA,GAAiB,UAAA;EACjB,iBAAA,GAAoB,UAAA;EAEpB,UAAA,GAAa,UAAA;EAEb,YAAA,GAAe,UAAA;EACf,UAAA,GAAa,UAAA;EACb,UAAA,GAAa,UAAA;EACb,aAAA,GAAgB,UAAA;EAChB,iBAAA,GAAoB,UAAA;EACpB,eAAA,GAAkB,UAAA;EAClB,OAAA,GAAU,UAAA;EAEV,WAAA,GAAc,UAAA;EACd,SAAA,GAAY,UAAA;EACZ,SAAA,GAAY,UAAA;EACZ,YAAA,GAAe,UAAA;EACf,gBAAA,GAAmB,UAAA;EACnB,cAAA,GAAiB,UAAA;EACjB,MAAA,GAAS,UAAA;EAMD;EAJR,mBAAA;EAzD8B;EA2D9B,kBAAA;EA3D0B;EA6D1B,KAAA,GAAQ,SAAA,CAAU,SAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAgPd,IAAA,EAAI,OAAA,CAAA,oBAAA,CAAA,SAAA;AAAA,cAaG,SAAA;AAAA,cACA,mBAAA"}
|
package/dist/components/Icon.js
CHANGED
|
@@ -54,7 +54,8 @@ const MulticolorIcon = memo(function MulticolorIcon({ name, size = DEFAULT_PROPS
|
|
|
54
54
|
children: /* @__PURE__ */ jsx(SvgXml, {
|
|
55
55
|
xml: svgContent,
|
|
56
56
|
width: renderedSize,
|
|
57
|
-
height: renderedSize
|
|
57
|
+
height: renderedSize,
|
|
58
|
+
viewBox: `0 0 ${pixelSize} ${pixelSize}`
|
|
58
59
|
})
|
|
59
60
|
});
|
|
60
61
|
});
|
|
@@ -111,12 +112,12 @@ const OutlineOrFillIcon = memo(function OutlineOrFillIcon({ name, size = DEFAULT
|
|
|
111
112
|
lineHeight: getSafeIconCell(pixelSize)
|
|
112
113
|
},
|
|
113
114
|
styles.foundation,
|
|
115
|
+
style,
|
|
114
116
|
dangerouslySetColor ? { color: dangerouslySetColor } : void 0,
|
|
115
117
|
dangerouslySetSize ? {
|
|
116
118
|
fontSize: dangerouslySetSize,
|
|
117
119
|
lineHeight: getSafeIconCell(dangerouslySetSize)
|
|
118
|
-
} : void 0
|
|
119
|
-
style
|
|
120
|
+
} : void 0
|
|
120
121
|
], [
|
|
121
122
|
dangerouslySetColor,
|
|
122
123
|
dangerouslySetSize,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Icon.js","names":[],"sources":["../../src/components/Icon.tsx"],"sourcesContent":["import type { GlyphName } from '@yahoo/uds-icons/glyphMap';\nimport { glyphMap, glyphNames } from '@yahoo/uds-icons/glyphMap';\nimport type { SvgGlyphKey, SvgGlyphName } from '@yahoo/uds-icons/svgMap';\nimport { svgGlyphNames, svgMap } from '@yahoo/uds-icons/svgMap';\nimport { ICON_SIZE_MAP } from '@yahoo/uds-icons/tokens';\nimport type { IconSize, IconVariant } from '@yahoo/uds-types';\nimport type { Ref } from 'react';\nimport { memo, useMemo } from 'react';\nimport type {\n StyleProp,\n Text as RNText,\n TextProps as RNTextProps,\n TextStyle,\n ViewProps,\n} from 'react-native';\nimport { Text, View } from 'react-native';\nimport { SvgXml } from 'react-native-svg';\n// eslint-disable-next-line uds/no-use-unistyles\nimport { useUnistyles } from 'react-native-unistyles';\n\nimport type { StyleProps } from '../../generated/styles';\nimport { styles } from '../../generated/styles';\nimport { useFontScale } from '../fontScaling/useFontScale';\nimport { useMaxFontSizeMultiplier } from '../fontScaling/useMaxFontSizeMultiplier';\nimport { getSafeIconCell } from './Button/buttonTheme';\n\nconst DEFAULT_PROPS = {\n size: 'md',\n color: 'primary',\n variant: 'outline',\n flexShrink: '0',\n} as const;\n\n/* -------------------------------------------------------------------------- */\n/* Icon Props */\n/* -------------------------------------------------------------------------- */\n\ntype IconName = GlyphName | SvgGlyphName;\n\ninterface IconProps extends Omit<RNTextProps, 'style'> {\n /** Ref to the underlying Text element */\n ref?: Ref<RNText>;\n /** The icon name */\n name: IconName;\n /** Icon size */\n size?: IconSize;\n /** Icon color */\n color?: StyleProps['color'];\n /** Icon variant (outline, fill, multicolor) */\n variant?: IconVariant;\n /**\n * Caps how far the icon grows with the OS text-size setting. Applies to both\n * font glyphs and multicolor SVGs. Set null to remove the cap; override\n * app-wide via UDSFontScalingProvider.\n * @default 2\n */\n maxFontSizeMultiplier?: number | null;\n // Background\n backgroundColor?: StyleProps['backgroundColor'];\n // Border\n borderRadius?: StyleProps['borderRadius'];\n borderTopStartRadius?: StyleProps['borderTopStartRadius'];\n borderTopEndRadius?: StyleProps['borderTopEndRadius'];\n borderBottomStartRadius?: StyleProps['borderBottomStartRadius'];\n borderBottomEndRadius?: StyleProps['borderBottomEndRadius'];\n borderColor?: StyleProps['borderColor'];\n borderStartColor?: StyleProps['borderStartColor'];\n borderEndColor?: StyleProps['borderEndColor'];\n borderTopColor?: StyleProps['borderTopColor'];\n borderBottomColor?: StyleProps['borderBottomColor'];\n borderWidth?: StyleProps['borderWidth'];\n borderVerticalWidth?: StyleProps['borderVerticalWidth'];\n borderHorizontalWidth?: StyleProps['borderHorizontalWidth'];\n borderStartWidth?: StyleProps['borderStartWidth'];\n borderEndWidth?: StyleProps['borderEndWidth'];\n borderTopWidth?: StyleProps['borderTopWidth'];\n borderBottomWidth?: StyleProps['borderBottomWidth'];\n // Flex\n flexShrink?: StyleProps['flexShrink'];\n // Spacing\n spacingStart?: StyleProps['spacingStart'];\n spacingEnd?: StyleProps['spacingEnd'];\n spacingTop?: StyleProps['spacingTop'];\n spacingBottom?: StyleProps['spacingBottom'];\n spacingHorizontal?: StyleProps['spacingHorizontal'];\n spacingVertical?: StyleProps['spacingVertical'];\n spacing?: StyleProps['spacing'];\n // Offset\n offsetStart?: StyleProps['offsetStart'];\n offsetEnd?: StyleProps['offsetEnd'];\n offsetTop?: StyleProps['offsetTop'];\n offsetBottom?: StyleProps['offsetBottom'];\n offsetHorizontal?: StyleProps['offsetHorizontal'];\n offsetVertical?: StyleProps['offsetVertical'];\n offset?: StyleProps['offset'];\n /** Icon color override */\n dangerouslySetColor?: string;\n /** Icon font size override */\n dangerouslySetSize?: number;\n /** Optional style override */\n style?: StyleProp<TextStyle>;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Multicolor Icon */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Separate component for multicolor SVG icons.\n * Uses useUnistyles() for reactive theme access - isolated here so\n * glyph-based icons don't pay the hook overhead.\n */\nconst MulticolorIcon = memo(function MulticolorIcon({\n name,\n size = DEFAULT_PROPS.size,\n flexShrink = DEFAULT_PROPS.flexShrink,\n backgroundColor,\n dangerouslySetSize,\n maxFontSizeMultiplier,\n allowFontScaling = true,\n accessible,\n accessibilityElementsHidden,\n accessibilityHint,\n accessibilityLabel = name,\n accessibilityRole = 'image',\n accessibilityState,\n accessibilityValue,\n importantForAccessibility,\n testID,\n}: Omit<IconProps, 'variant'>) {\n // Reactive theme subscription - only multicolor icons need this\n const { rt } = useUnistyles();\n const colorScheme = rt.themeName === 'dark' ? 'dark' : 'light';\n\n const pixelSize = ICON_SIZE_MAP[size];\n const resolvedMaxFontSizeMultiplier = useMaxFontSizeMultiplier('control', maxFontSizeMultiplier);\n const fontScaleFactor = useFontScale(allowFontScaling ? resolvedMaxFontSizeMultiplier : 1);\n const renderedSize = Math.round((dangerouslySetSize ?? pixelSize) * fontScaleFactor);\n const svgKey = `${name}-${colorScheme}-${pixelSize}` as SvgGlyphKey;\n const svgContent = svgMap[svgKey];\n\n if (!svgContent) {\n console.warn(`Multicolor icon \"${svgKey}\" not found`);\n return null;\n }\n\n styles.useVariants({\n flexShrink,\n backgroundColor,\n });\n\n return (\n <View\n accessible={accessible}\n accessibilityElementsHidden={accessibilityElementsHidden}\n accessibilityHint={accessibilityHint}\n accessibilityLabel={accessibilityLabel}\n accessibilityRole={accessibilityRole}\n accessibilityState={accessibilityState}\n accessibilityValue={accessibilityValue}\n importantForAccessibility={importantForAccessibility}\n testID={testID}\n style={styles.foundation as ViewProps['style']}\n >\n <SvgXml xml={svgContent} width={renderedSize} height={renderedSize} />\n </View>\n );\n});\n\nconst OutlineOrFillIcon = memo(function OutlineOrFillIcon({\n name,\n size = DEFAULT_PROPS.size,\n color = DEFAULT_PROPS.color,\n variant = DEFAULT_PROPS.variant,\n // Background\n backgroundColor,\n // Border\n borderRadius,\n borderTopStartRadius,\n borderTopEndRadius,\n borderBottomStartRadius,\n borderBottomEndRadius,\n borderColor,\n borderStartColor,\n borderEndColor,\n borderTopColor,\n borderBottomColor,\n borderWidth,\n borderVerticalWidth,\n borderHorizontalWidth,\n borderStartWidth,\n borderEndWidth,\n borderTopWidth,\n borderBottomWidth,\n // Flex\n flexShrink = '0',\n // Spacing\n spacingStart,\n spacingEnd,\n spacingTop,\n spacingBottom,\n spacingHorizontal,\n spacingVertical,\n spacing,\n // Offset\n offsetStart,\n offsetEnd,\n offsetTop,\n offsetBottom,\n offsetHorizontal,\n offsetVertical,\n offset,\n // Dangerously set props\n dangerouslySetColor,\n dangerouslySetSize,\n maxFontSizeMultiplier,\n style,\n ref,\n ...props\n}: Omit<IconProps, 'variant'> & { variant?: 'outline' | 'fill' }) {\n const pixelSize = ICON_SIZE_MAP[size];\n // Native scales fontSize and lineHeight together under the cap, so the\n // glyph cell grows proportionally; glyph selection stays at pixelSize.\n const resolvedMaxFontSizeMultiplier = useMaxFontSizeMultiplier('control', maxFontSizeMultiplier);\n // Glyph-based icons - no useUnistyles needed, styles.useVariants is reactive\n styles.useVariants({\n color,\n backgroundColor,\n // Border\n borderRadius,\n borderTopStartRadius,\n borderTopEndRadius,\n borderBottomStartRadius,\n borderBottomEndRadius,\n borderColor,\n borderStartColor,\n borderEndColor,\n borderTopColor,\n borderBottomColor,\n borderWidth,\n borderVerticalWidth,\n borderHorizontalWidth,\n borderStartWidth,\n borderEndWidth,\n borderTopWidth,\n borderBottomWidth,\n // Flex\n flexShrink,\n // Spacing\n spacingStart,\n spacingEnd,\n spacingTop,\n spacingBottom,\n spacingHorizontal,\n spacingVertical,\n spacing,\n // Offset\n offsetStart,\n offsetEnd,\n offsetTop,\n offsetBottom,\n offsetHorizontal,\n offsetVertical,\n offset,\n });\n const glyphName = `${name}-${variant}-${pixelSize}`;\n const glyph = (glyphMap as Record<string, string>)[glyphName];\n const textStyles: StyleProp<TextStyle> = useMemo(\n () => [\n {\n fontFamily: 'uds-icons',\n textAlign: 'center',\n textAlignVertical: 'center',\n fontSize: pixelSize,\n lineHeight: getSafeIconCell(pixelSize),\n },\n styles.foundation,\n dangerouslySetColor ? { color: dangerouslySetColor } : undefined,\n dangerouslySetSize\n ? {\n fontSize: dangerouslySetSize,\n lineHeight: getSafeIconCell(dangerouslySetSize),\n }\n : undefined,\n style,\n ],\n [dangerouslySetColor, dangerouslySetSize, style, styles.foundation, pixelSize],\n );\n\n return (\n <Text\n ref={ref}\n accessibilityRole=\"image\"\n accessibilityLabel={name}\n style={textStyles}\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n {...props}\n >\n {glyph}\n </Text>\n );\n});\n\n/**\n * **🎨 An icon component using UDS icon fonts and multicolor SVGs**\n *\n * @description\n * Renders icons from the UDS icon library. Supports multiple variants\n * (outline, fill, multicolor) and sizes.\n *\n * @category Display\n * @platform mobile\n *\n * @example\n * ```tsx\n * import { Icon } from '@yahoo/uds-mobile/Icon';\n *\n * <Icon name=\"ChevronRight\" size=\"md\" color=\"primary\" />\n * <Icon name=\"Star\" variant=\"fill\" color=\"accent\" />\n * <Icon name=\"Yahoo\" variant=\"multicolor\" />\n * ```\n *\n * @usage\n * - Use name prop to specify the icon\n * - Use variant prop for outline/fill/multicolor\n * - Use size prop for icon size (xs, sm, md, lg, xl)\n *\n * @accessibility\n * - Sets `accessibilityRole=\"image\"` automatically\n * - Icon name is used as accessibility label\n * - For decorative icons, set `accessibilityElementsHidden`\n *\n * @see {@link IconButton} for clickable icons\n * @see {@link IconSlot} for flexible icon rendering in components\n */\nconst Icon = memo(function Icon({ variant, ...props }: IconProps) {\n // Delegate to MulticolorIcon for SVG-based icons (has its own useUnistyles)\n if (variant === 'multicolor') {\n return <MulticolorIcon {...props} />;\n }\n\n return <OutlineOrFillIcon variant={variant} {...props} />;\n});\n\nIcon.displayName = 'Icon';\n\nexport { Icon };\nexport type { IconName, IconProps };\nexport const iconNames = glyphNames;\nexport const multicolorIconNames = svgGlyphNames;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Icon.js","names":[],"sources":["../../src/components/Icon.tsx"],"sourcesContent":["import type { GlyphName } from '@yahoo/uds-icons/glyphMap';\nimport { glyphMap, glyphNames } from '@yahoo/uds-icons/glyphMap';\nimport type { SvgGlyphKey, SvgGlyphName } from '@yahoo/uds-icons/svgMap';\nimport { svgGlyphNames, svgMap } from '@yahoo/uds-icons/svgMap';\nimport { ICON_SIZE_MAP } from '@yahoo/uds-icons/tokens';\nimport type { IconSize, IconVariant } from '@yahoo/uds-types';\nimport type { Ref } from 'react';\nimport { memo, useMemo } from 'react';\nimport type {\n StyleProp,\n Text as RNText,\n TextProps as RNTextProps,\n TextStyle,\n ViewProps,\n} from 'react-native';\nimport { Text, View } from 'react-native';\nimport { SvgXml } from 'react-native-svg';\n// eslint-disable-next-line uds/no-use-unistyles\nimport { useUnistyles } from 'react-native-unistyles';\n\nimport type { StyleProps } from '../../generated/styles';\nimport { styles } from '../../generated/styles';\nimport { useFontScale } from '../fontScaling/useFontScale';\nimport { useMaxFontSizeMultiplier } from '../fontScaling/useMaxFontSizeMultiplier';\nimport { getSafeIconCell } from './Button/buttonTheme';\n\nconst DEFAULT_PROPS = {\n size: 'md',\n color: 'primary',\n variant: 'outline',\n flexShrink: '0',\n} as const;\n\n/* -------------------------------------------------------------------------- */\n/* Icon Props */\n/* -------------------------------------------------------------------------- */\n\ntype IconName = GlyphName | SvgGlyphName;\n\ninterface IconProps extends Omit<RNTextProps, 'style'> {\n /** Ref to the underlying Text element */\n ref?: Ref<RNText>;\n /** The icon name */\n name: IconName;\n /** Icon size */\n size?: IconSize;\n /** Icon color */\n color?: StyleProps['color'];\n /** Icon variant (outline, fill, multicolor) */\n variant?: IconVariant;\n /**\n * Caps how far the icon grows with the OS text-size setting. Applies to both\n * font glyphs and multicolor SVGs. Set null to remove the cap; override\n * app-wide via UDSFontScalingProvider.\n * @default 2\n */\n maxFontSizeMultiplier?: number | null;\n // Background\n backgroundColor?: StyleProps['backgroundColor'];\n // Border\n borderRadius?: StyleProps['borderRadius'];\n borderTopStartRadius?: StyleProps['borderTopStartRadius'];\n borderTopEndRadius?: StyleProps['borderTopEndRadius'];\n borderBottomStartRadius?: StyleProps['borderBottomStartRadius'];\n borderBottomEndRadius?: StyleProps['borderBottomEndRadius'];\n borderColor?: StyleProps['borderColor'];\n borderStartColor?: StyleProps['borderStartColor'];\n borderEndColor?: StyleProps['borderEndColor'];\n borderTopColor?: StyleProps['borderTopColor'];\n borderBottomColor?: StyleProps['borderBottomColor'];\n borderWidth?: StyleProps['borderWidth'];\n borderVerticalWidth?: StyleProps['borderVerticalWidth'];\n borderHorizontalWidth?: StyleProps['borderHorizontalWidth'];\n borderStartWidth?: StyleProps['borderStartWidth'];\n borderEndWidth?: StyleProps['borderEndWidth'];\n borderTopWidth?: StyleProps['borderTopWidth'];\n borderBottomWidth?: StyleProps['borderBottomWidth'];\n // Flex\n flexShrink?: StyleProps['flexShrink'];\n // Spacing\n spacingStart?: StyleProps['spacingStart'];\n spacingEnd?: StyleProps['spacingEnd'];\n spacingTop?: StyleProps['spacingTop'];\n spacingBottom?: StyleProps['spacingBottom'];\n spacingHorizontal?: StyleProps['spacingHorizontal'];\n spacingVertical?: StyleProps['spacingVertical'];\n spacing?: StyleProps['spacing'];\n // Offset\n offsetStart?: StyleProps['offsetStart'];\n offsetEnd?: StyleProps['offsetEnd'];\n offsetTop?: StyleProps['offsetTop'];\n offsetBottom?: StyleProps['offsetBottom'];\n offsetHorizontal?: StyleProps['offsetHorizontal'];\n offsetVertical?: StyleProps['offsetVertical'];\n offset?: StyleProps['offset'];\n /** Icon color override */\n dangerouslySetColor?: string;\n /** Icon font size override */\n dangerouslySetSize?: number;\n /** Optional style override */\n style?: StyleProp<TextStyle>;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Multicolor Icon */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Separate component for multicolor SVG icons.\n * Uses useUnistyles() for reactive theme access - isolated here so\n * glyph-based icons don't pay the hook overhead.\n */\nconst MulticolorIcon = memo(function MulticolorIcon({\n name,\n size = DEFAULT_PROPS.size,\n flexShrink = DEFAULT_PROPS.flexShrink,\n backgroundColor,\n dangerouslySetSize,\n maxFontSizeMultiplier,\n allowFontScaling = true,\n accessible,\n accessibilityElementsHidden,\n accessibilityHint,\n accessibilityLabel = name,\n accessibilityRole = 'image',\n accessibilityState,\n accessibilityValue,\n importantForAccessibility,\n testID,\n}: Omit<IconProps, 'variant'>) {\n // Reactive theme subscription - only multicolor icons need this\n const { rt } = useUnistyles();\n const colorScheme = rt.themeName === 'dark' ? 'dark' : 'light';\n\n const pixelSize = ICON_SIZE_MAP[size];\n const resolvedMaxFontSizeMultiplier = useMaxFontSizeMultiplier('control', maxFontSizeMultiplier);\n const fontScaleFactor = useFontScale(allowFontScaling ? resolvedMaxFontSizeMultiplier : 1);\n const renderedSize = Math.round((dangerouslySetSize ?? pixelSize) * fontScaleFactor);\n const svgKey = `${name}-${colorScheme}-${pixelSize}` as SvgGlyphKey;\n const svgContent = svgMap[svgKey];\n\n if (!svgContent) {\n console.warn(`Multicolor icon \"${svgKey}\" not found`);\n return null;\n }\n\n styles.useVariants({\n flexShrink,\n backgroundColor,\n });\n\n return (\n <View\n accessible={accessible}\n accessibilityElementsHidden={accessibilityElementsHidden}\n accessibilityHint={accessibilityHint}\n accessibilityLabel={accessibilityLabel}\n accessibilityRole={accessibilityRole}\n accessibilityState={accessibilityState}\n accessibilityValue={accessibilityValue}\n importantForAccessibility={importantForAccessibility}\n testID={testID}\n style={styles.foundation as ViewProps['style']}\n >\n <SvgXml\n xml={svgContent}\n width={renderedSize}\n height={renderedSize}\n viewBox={`0 0 ${pixelSize} ${pixelSize}`}\n />\n </View>\n );\n});\n\nconst OutlineOrFillIcon = memo(function OutlineOrFillIcon({\n name,\n size = DEFAULT_PROPS.size,\n color = DEFAULT_PROPS.color,\n variant = DEFAULT_PROPS.variant,\n // Background\n backgroundColor,\n // Border\n borderRadius,\n borderTopStartRadius,\n borderTopEndRadius,\n borderBottomStartRadius,\n borderBottomEndRadius,\n borderColor,\n borderStartColor,\n borderEndColor,\n borderTopColor,\n borderBottomColor,\n borderWidth,\n borderVerticalWidth,\n borderHorizontalWidth,\n borderStartWidth,\n borderEndWidth,\n borderTopWidth,\n borderBottomWidth,\n // Flex\n flexShrink = '0',\n // Spacing\n spacingStart,\n spacingEnd,\n spacingTop,\n spacingBottom,\n spacingHorizontal,\n spacingVertical,\n spacing,\n // Offset\n offsetStart,\n offsetEnd,\n offsetTop,\n offsetBottom,\n offsetHorizontal,\n offsetVertical,\n offset,\n // Dangerously set props\n dangerouslySetColor,\n dangerouslySetSize,\n maxFontSizeMultiplier,\n style,\n ref,\n ...props\n}: Omit<IconProps, 'variant'> & { variant?: 'outline' | 'fill' }) {\n const pixelSize = ICON_SIZE_MAP[size];\n // Native scales fontSize and lineHeight together under the cap, so the\n // glyph cell grows proportionally; glyph selection stays at pixelSize.\n const resolvedMaxFontSizeMultiplier = useMaxFontSizeMultiplier('control', maxFontSizeMultiplier);\n // Glyph-based icons - no useUnistyles needed, styles.useVariants is reactive\n styles.useVariants({\n color,\n backgroundColor,\n // Border\n borderRadius,\n borderTopStartRadius,\n borderTopEndRadius,\n borderBottomStartRadius,\n borderBottomEndRadius,\n borderColor,\n borderStartColor,\n borderEndColor,\n borderTopColor,\n borderBottomColor,\n borderWidth,\n borderVerticalWidth,\n borderHorizontalWidth,\n borderStartWidth,\n borderEndWidth,\n borderTopWidth,\n borderBottomWidth,\n // Flex\n flexShrink,\n // Spacing\n spacingStart,\n spacingEnd,\n spacingTop,\n spacingBottom,\n spacingHorizontal,\n spacingVertical,\n spacing,\n // Offset\n offsetStart,\n offsetEnd,\n offsetTop,\n offsetBottom,\n offsetHorizontal,\n offsetVertical,\n offset,\n });\n const glyphName = `${name}-${variant}-${pixelSize}`;\n const glyph = (glyphMap as Record<string, string>)[glyphName];\n const textStyles: StyleProp<TextStyle> = useMemo(\n () => [\n {\n fontFamily: 'uds-icons',\n textAlign: 'center',\n textAlignVertical: 'center',\n fontSize: pixelSize,\n lineHeight: getSafeIconCell(pixelSize),\n },\n styles.foundation,\n style,\n dangerouslySetColor ? { color: dangerouslySetColor } : undefined,\n dangerouslySetSize\n ? {\n fontSize: dangerouslySetSize,\n lineHeight: getSafeIconCell(dangerouslySetSize),\n }\n : undefined,\n ],\n [dangerouslySetColor, dangerouslySetSize, style, styles.foundation, pixelSize],\n );\n\n return (\n <Text\n ref={ref}\n accessibilityRole=\"image\"\n accessibilityLabel={name}\n style={textStyles}\n maxFontSizeMultiplier={resolvedMaxFontSizeMultiplier}\n {...props}\n >\n {glyph}\n </Text>\n );\n});\n\n/**\n * **🎨 An icon component using UDS icon fonts and multicolor SVGs**\n *\n * @description\n * Renders icons from the UDS icon library. Supports multiple variants\n * (outline, fill, multicolor) and sizes.\n *\n * @category Display\n * @platform mobile\n *\n * @example\n * ```tsx\n * import { Icon } from '@yahoo/uds-mobile/Icon';\n *\n * <Icon name=\"ChevronRight\" size=\"md\" color=\"primary\" />\n * <Icon name=\"Star\" variant=\"fill\" color=\"accent\" />\n * <Icon name=\"Yahoo\" variant=\"multicolor\" />\n * ```\n *\n * @usage\n * - Use name prop to specify the icon\n * - Use variant prop for outline/fill/multicolor\n * - Use size prop for icon size (xs, sm, md, lg, xl)\n *\n * @accessibility\n * - Sets `accessibilityRole=\"image\"` automatically\n * - Icon name is used as accessibility label\n * - For decorative icons, set `accessibilityElementsHidden`\n *\n * @see {@link IconButton} for clickable icons\n * @see {@link IconSlot} for flexible icon rendering in components\n */\nconst Icon = memo(function Icon({ variant, ...props }: IconProps) {\n // Delegate to MulticolorIcon for SVG-based icons (has its own useUnistyles)\n if (variant === 'multicolor') {\n return <MulticolorIcon {...props} />;\n }\n\n return <OutlineOrFillIcon variant={variant} {...props} />;\n});\n\nIcon.displayName = 'Icon';\n\nexport { Icon };\nexport type { IconName, IconProps };\nexport const iconNames = glyphNames;\nexport const multicolorIconNames = svgGlyphNames;\n"],"mappings":"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"}
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
require("../_virtual/_rolldown/runtime.cjs");
|
|
4
4
|
const require_components_HStack = require("./HStack.cjs");
|
|
5
|
+
const require_fontScaling_useFontScale = require("../fontScaling/useFontScale.cjs");
|
|
6
|
+
const require_fontScaling_useMaxFontSizeMultiplier = require("../fontScaling/useMaxFontSizeMultiplier.cjs");
|
|
5
7
|
const require_components_IconSlot = require("./IconSlot.cjs");
|
|
6
8
|
const require_components_Text = require("./Text.cjs");
|
|
7
9
|
const require_components_InputHelpText = require("./InputHelpText.cjs");
|
|
@@ -47,8 +49,10 @@ let lodash_es = require("lodash-es");
|
|
|
47
49
|
* @see {@link Checkbox} for boolean selections
|
|
48
50
|
* @see {@link Radio} for single-select options
|
|
49
51
|
*/
|
|
50
|
-
const Input = (0, react.memo)(function Input({ label, size = "md", startIcon, endIcon, helpText, helperTextIcon, hasError, disabled, readOnly, required, width = "100%", defaultValue, value: controlledValue, onChangeText, onFocus, onBlur, placeholder, placeholderTextColor, ref, ...textInputProps }) {
|
|
52
|
+
const Input = (0, react.memo)(function Input({ label, size = "md", startIcon, endIcon, helpText, helperTextIcon, hasError, disabled, readOnly, required, width = "100%", defaultValue, value: controlledValue, onChangeText, onFocus, onBlur, placeholder, placeholderTextColor, maxFontSizeMultiplier, ref, ...textInputProps }) {
|
|
51
53
|
const uid = `uds-input-${(0, react.useId)()}`;
|
|
54
|
+
const resolvedMaxFontSizeMultiplier = require_fontScaling_useMaxFontSizeMultiplier.useMaxFontSizeMultiplier("control", maxFontSizeMultiplier) ?? 0;
|
|
55
|
+
const fontScaleFactor = require_fontScaling_useFontScale.useFontScale(resolvedMaxFontSizeMultiplier);
|
|
52
56
|
const [internalValue, setInternalValue] = (0, react.useState)(defaultValue ?? "");
|
|
53
57
|
const [isFocused, setIsFocused] = (0, react.useState)(false);
|
|
54
58
|
const isControlled = controlledValue !== void 0;
|
|
@@ -78,25 +82,41 @@ const Input = (0, react.memo)(function Input({ label, size = "md", startIcon, en
|
|
|
78
82
|
width,
|
|
79
83
|
opacity: disabled ? .5 : 1
|
|
80
84
|
}], [width, disabled]);
|
|
81
|
-
const inputWrapperStyle = (0, react.useMemo)(() =>
|
|
82
|
-
generated_styles.inputStyles.
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
85
|
+
const inputWrapperStyle = (0, react.useMemo)(() => {
|
|
86
|
+
const { gap, paddingVertical } = generated_styles.inputStyles.inputWrapperStatic;
|
|
87
|
+
const scaledMetrics = fontScaleFactor === 1 ? void 0 : {
|
|
88
|
+
...typeof gap === "number" && { gap: Math.round(gap * fontScaleFactor) },
|
|
89
|
+
...typeof paddingVertical === "number" && { paddingVertical: Math.round(paddingVertical * fontScaleFactor) }
|
|
90
|
+
};
|
|
91
|
+
return [
|
|
92
|
+
generated_styles.inputStyles.inputWrapper,
|
|
93
|
+
generated_styles.inputStyles.inputWrapperStatic,
|
|
94
|
+
generated_styles.inputStyles.inputWrapperDynamic,
|
|
95
|
+
scaledMetrics,
|
|
96
|
+
{
|
|
97
|
+
flexDirection: "row",
|
|
98
|
+
alignItems: "center"
|
|
99
|
+
}
|
|
100
|
+
];
|
|
101
|
+
}, [
|
|
102
|
+
fontScaleFactor,
|
|
90
103
|
generated_styles.inputStyles.inputWrapper,
|
|
91
104
|
generated_styles.inputStyles.inputWrapperStatic,
|
|
92
105
|
generated_styles.inputStyles.inputWrapperDynamic
|
|
93
106
|
]);
|
|
94
|
-
const textInputStyle = (0, react.useMemo)(() =>
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
107
|
+
const textInputStyle = (0, react.useMemo)(() => {
|
|
108
|
+
const inputHeight = generated_styles.inputStyles.input.height;
|
|
109
|
+
return [
|
|
110
|
+
generated_styles.inputStyles.input,
|
|
111
|
+
typeof inputHeight === "number" ? { height: Math.round(inputHeight * fontScaleFactor) } : void 0,
|
|
112
|
+
{
|
|
113
|
+
flex: 1,
|
|
114
|
+
includeFontPadding: false,
|
|
115
|
+
textAlignVertical: "center",
|
|
116
|
+
paddingVertical: 0
|
|
117
|
+
}
|
|
118
|
+
];
|
|
119
|
+
}, [fontScaleFactor, generated_styles.inputStyles.input]);
|
|
100
120
|
const labelContent = (0, react.useMemo)(() => {
|
|
101
121
|
if (!label) return null;
|
|
102
122
|
const content = (0, lodash_es.isFunction)(label) ? label() : label;
|
|
@@ -105,9 +125,11 @@ const Input = (0, react.memo)(function Input({ label, size = "md", startIcon, en
|
|
|
105
125
|
alignItems: "flex-end",
|
|
106
126
|
spacingBottom: "2",
|
|
107
127
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_components_Text.Text, {
|
|
128
|
+
maxFontSizeMultiplier: resolvedMaxFontSizeMultiplier,
|
|
108
129
|
style: generated_styles.inputStyles.label,
|
|
109
130
|
children: content
|
|
110
131
|
}), required && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_components_Text.Text, {
|
|
132
|
+
maxFontSizeMultiplier: resolvedMaxFontSizeMultiplier,
|
|
111
133
|
style: generated_styles.inputStyles.labelRequired,
|
|
112
134
|
children: "*"
|
|
113
135
|
})]
|
|
@@ -115,6 +137,7 @@ const Input = (0, react.memo)(function Input({ label, size = "md", startIcon, en
|
|
|
115
137
|
}, [
|
|
116
138
|
label,
|
|
117
139
|
required,
|
|
140
|
+
resolvedMaxFontSizeMultiplier,
|
|
118
141
|
generated_styles.inputStyles.label,
|
|
119
142
|
generated_styles.inputStyles.labelRequired
|
|
120
143
|
]);
|
|
@@ -123,17 +146,27 @@ const Input = (0, react.memo)(function Input({ label, size = "md", startIcon, en
|
|
|
123
146
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_components_IconSlot.IconSlot, {
|
|
124
147
|
icon: startIcon,
|
|
125
148
|
variant: "outline",
|
|
149
|
+
maxFontSizeMultiplier: resolvedMaxFontSizeMultiplier,
|
|
126
150
|
style: generated_styles.inputStyles.startIcon
|
|
127
151
|
});
|
|
128
|
-
}, [
|
|
152
|
+
}, [
|
|
153
|
+
startIcon,
|
|
154
|
+
resolvedMaxFontSizeMultiplier,
|
|
155
|
+
generated_styles.inputStyles.startIcon
|
|
156
|
+
]);
|
|
129
157
|
const endIconContent = (0, react.useMemo)(() => {
|
|
130
158
|
if (!endIcon) return null;
|
|
131
159
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_components_IconSlot.IconSlot, {
|
|
132
160
|
icon: endIcon,
|
|
133
161
|
variant: "outline",
|
|
162
|
+
maxFontSizeMultiplier: resolvedMaxFontSizeMultiplier,
|
|
134
163
|
style: generated_styles.inputStyles.endIcon
|
|
135
164
|
});
|
|
136
|
-
}, [
|
|
165
|
+
}, [
|
|
166
|
+
endIcon,
|
|
167
|
+
resolvedMaxFontSizeMultiplier,
|
|
168
|
+
generated_styles.inputStyles.endIcon
|
|
169
|
+
]);
|
|
137
170
|
const helpTextContent = (0, react.useMemo)(() => {
|
|
138
171
|
if (!helpText) return null;
|
|
139
172
|
const content = (0, lodash_es.isFunction)(helpText) ? helpText() : helpText;
|
|
@@ -145,6 +178,7 @@ const Input = (0, react.memo)(function Input({ label, size = "md", startIcon, en
|
|
|
145
178
|
readOnly,
|
|
146
179
|
hasError,
|
|
147
180
|
pressed: isFocused,
|
|
181
|
+
maxFontSizeMultiplier: resolvedMaxFontSizeMultiplier,
|
|
148
182
|
children: content
|
|
149
183
|
});
|
|
150
184
|
}, [
|
|
@@ -154,6 +188,7 @@ const Input = (0, react.memo)(function Input({ label, size = "md", startIcon, en
|
|
|
154
188
|
helperTextIcon,
|
|
155
189
|
isFocused,
|
|
156
190
|
readOnly,
|
|
191
|
+
resolvedMaxFontSizeMultiplier,
|
|
157
192
|
size,
|
|
158
193
|
valueState
|
|
159
194
|
]);
|
|
@@ -178,6 +213,7 @@ const Input = (0, react.memo)(function Input({ label, size = "md", startIcon, en
|
|
|
178
213
|
placeholder,
|
|
179
214
|
placeholderTextColor: computedPlaceholderColor,
|
|
180
215
|
editable: !disabled && !readOnly,
|
|
216
|
+
maxFontSizeMultiplier: resolvedMaxFontSizeMultiplier,
|
|
181
217
|
style: textInputStyle,
|
|
182
218
|
accessibilityLabel: typeof label === "string" ? label : void 0,
|
|
183
219
|
accessibilityHint: typeof helpText === "string" ? helpText : void 0,
|
|
@@ -10,6 +10,11 @@ import { TextInput, TextInputProps } from "react-native";
|
|
|
10
10
|
interface InputProps extends Omit<UniversalInputProps<IconSlotType>, 'width'>, Omit<TextInputProps, 'style' | 'editable'>, Pick<SizeProps, 'width'> {
|
|
11
11
|
/** Ref to the underlying TextInput element */
|
|
12
12
|
ref?: Ref<TextInput>;
|
|
13
|
+
/**
|
|
14
|
+
* Caps the field text, labels, icons, and geometry at the control scale.
|
|
15
|
+
* Set null to remove the cap. @default 2
|
|
16
|
+
*/
|
|
17
|
+
maxFontSizeMultiplier?: number | null;
|
|
13
18
|
}
|
|
14
19
|
/**
|
|
15
20
|
* **📦 An input that allows users to enter text and collect data.**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Input.d.cts","names":[],"sources":["../../src/components/Input.tsx"],"mappings":";;;;;;;;;
|
|
1
|
+
{"version":3,"file":"Input.d.cts","names":[],"sources":["../../src/components/Input.tsx"],"mappings":";;;;;;;;;UAsBU,UAAA,SAEN,IAAA,CAAK,mBAAA,CAAoB,YAAA,aACzB,IAAA,CAAK,cAAA,yBACL,IAAA,CAAK,SAAA;;EAEP,GAAA,GAAM,GAAA,CAAI,SAAA;EALV;;;;EAUA,qBAAA;AAAA;;;;;;;;;;;;;;;;;;;;;AAAqB;;;;;;;;;;;;;;;cA0CjB,KAAA,EAAK,OAAA,CAAA,oBAAA,CAAA,UAAA"}
|
|
@@ -10,6 +10,11 @@ import { TextInput, TextInputProps } from "react-native";
|
|
|
10
10
|
interface InputProps extends Omit<UniversalInputProps<IconSlotType>, 'width'>, Omit<TextInputProps, 'style' | 'editable'>, Pick<SizeProps, 'width'> {
|
|
11
11
|
/** Ref to the underlying TextInput element */
|
|
12
12
|
ref?: Ref<TextInput>;
|
|
13
|
+
/**
|
|
14
|
+
* Caps the field text, labels, icons, and geometry at the control scale.
|
|
15
|
+
* Set null to remove the cap. @default 2
|
|
16
|
+
*/
|
|
17
|
+
maxFontSizeMultiplier?: number | null;
|
|
13
18
|
}
|
|
14
19
|
/**
|
|
15
20
|
* **📦 An input that allows users to enter text and collect data.**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Input.d.ts","names":[],"sources":["../../src/components/Input.tsx"],"mappings":";;;;;;;;;
|
|
1
|
+
{"version":3,"file":"Input.d.ts","names":[],"sources":["../../src/components/Input.tsx"],"mappings":";;;;;;;;;UAsBU,UAAA,SAEN,IAAA,CAAK,mBAAA,CAAoB,YAAA,aACzB,IAAA,CAAK,cAAA,yBACL,IAAA,CAAK,SAAA;;EAEP,GAAA,GAAM,GAAA,CAAI,SAAA;EALV;;;;EAUA,qBAAA;AAAA;;;;;;;;;;;;;;;;;;;;;AAAqB;;;;;;;;;;;;;;;cA0CjB,KAAA,EAAK,OAAA,CAAA,oBAAA,CAAA,UAAA"}
|