@yahoo/uds 3.59.0-beta.1 → 3.59.0-beta.2
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/cli/bin/uds-darwin-arm64-baseline +0 -0
- package/cli/bin/uds-darwin-x64 +0 -0
- package/cli/bin/uds-linux-arm64 +0 -0
- package/cli/bin/uds-linux-x64-baseline +0 -0
- package/dist/components/Box.d.ts +2 -2
- package/dist/components/Box.d.ts.map +1 -1
- package/dist/components/Divider/Divider.d.cts +2 -2
- package/dist/components/Divider/Divider.d.ts +2 -2
- package/dist/components/Divider/DividerCore.d.cts +2 -2
- package/dist/components/Divider/DividerCore.d.ts +2 -2
- package/dist/components/Divider/DividerInternal.d.cts +2 -2
- package/dist/components/Divider/DividerInternal.d.ts +2 -2
- package/dist/components/FormLabel.d.ts +2 -2
- package/dist/components/HStack.d.ts +2 -2
- package/dist/components/Icon.d.ts +2 -2
- package/dist/components/Link.d.ts +2 -2
- package/dist/components/Text.d.cts +5 -5
- package/dist/components/Text.d.cts.map +1 -1
- package/dist/components/Text.d.ts +8 -8
- package/dist/components/Text.d.ts.map +1 -1
- package/dist/components/VStack.d.ts +2 -2
- package/dist/components/VStack.d.ts.map +1 -1
- package/dist/components/client/AnimateHeightChange.d.cts +2 -2
- package/dist/components/client/AnimateHeightChange.d.ts +2 -2
- package/dist/components/client/Avatar/Avatar.d.cts +2 -2
- package/dist/components/client/Avatar/Avatar.d.cts.map +1 -1
- package/dist/components/client/Avatar/Avatar.d.ts +2 -2
- package/dist/components/client/Avatar/AvatarIcon.d.cts +2 -2
- package/dist/components/client/Avatar/AvatarIcon.d.cts.map +1 -1
- package/dist/components/client/Avatar/AvatarIcon.d.ts +2 -2
- package/dist/components/client/Avatar/AvatarImage.d.cts +2 -2
- package/dist/components/client/Avatar/AvatarImage.d.ts +2 -2
- package/dist/components/client/Avatar/AvatarText.d.cts +2 -2
- package/dist/components/client/Avatar/AvatarText.d.ts +2 -2
- package/dist/components/client/IconButton.d.cts +2 -2
- package/dist/components/client/IconButton.d.ts +2 -2
- package/dist/components/client/IconButton.d.ts.map +1 -1
- package/dist/components/client/Input/Input.d.cts +2 -2
- package/dist/components/client/Input/Input.d.ts +2 -2
- package/dist/components/client/Input/InputHelpText.d.cts +2 -2
- package/dist/components/client/Input/InputHelpText.d.cts.map +1 -1
- package/dist/components/client/Input/InputHelpText.d.ts +2 -2
- package/dist/components/client/Input/InputHelpTextInternal.d.cts +4 -4
- package/dist/components/client/Input/InputHelpTextInternal.d.cts.map +1 -1
- package/dist/components/client/Input/InputHelpTextInternal.d.ts +4 -4
- package/dist/components/client/Menu/Menu.Content.d.cts +2 -2
- package/dist/components/client/Menu/Menu.Content.d.ts +2 -2
- package/dist/components/client/Menu/Menu.Divider.d.cts +2 -2
- package/dist/components/client/Menu/Menu.Divider.d.ts +2 -2
- package/dist/components/client/Menu/Menu.Item.d.cts +2 -2
- package/dist/components/client/Menu/Menu.ItemBase.d.cts +2 -2
- package/dist/components/client/Menu/Menu.ItemBase.d.ts +2 -2
- package/dist/components/client/Menu/Menu.ItemCheckbox.d.cts +2 -2
- package/dist/components/client/Menu/Menu.ItemCheckbox.d.ts +2 -2
- package/dist/components/client/Menu/Menu.Provider.d.cts +2 -2
- package/dist/components/client/Menu/Menu.Provider.d.ts +2 -2
- package/dist/components/client/Menu/Menu.Trigger.d.cts +2 -2
- package/dist/components/client/Menu/Menu.Trigger.d.ts +2 -2
- package/dist/components/client/Pressable.d.cts +2 -2
- package/dist/components/client/Pressable.d.ts +2 -2
- package/dist/components/client/Pressable.d.ts.map +1 -1
- package/dist/components/client/Radio/RadioGroupStore.d.ts +2 -2
- package/dist/components/client/SpringMotionConfig.d.cts +3 -3
- package/dist/components/client/SpringMotionConfig.d.ts +3 -3
- package/dist/components/client/SpringMotionConfig.d.ts.map +1 -1
- package/dist/components/experimental/Spinner.d.cts +2 -2
- package/dist/components/experimental/Spinner.d.cts.map +1 -1
- package/dist/components/experimental/Spinner.d.ts +2 -2
- package/dist/components/experimental/Table.d.cts +2 -2
- package/dist/components/experimental/Table.d.ts +2 -2
- package/dist/components/experimental/client/Accordion.d.cts +2 -2
- package/dist/components/experimental/client/Accordion.d.cts.map +1 -1
- package/dist/components/experimental/client/Accordion.d.ts +2 -2
- package/dist/components/experimental/client/AvoidMotionLibraryProvider.d.cts +2 -2
- package/dist/components/experimental/client/AvoidMotionLibraryProvider.d.ts +2 -2
- package/dist/components/experimental/client/Modal.d.cts +3 -3
- package/dist/components/experimental/client/Modal.d.cts.map +1 -1
- package/dist/components/experimental/client/Modal.d.ts +3 -3
- package/dist/components/experimental/client/Popover.d.cts +3 -3
- package/dist/components/experimental/client/Popover.d.cts.map +1 -1
- package/dist/components/experimental/client/Popover.d.ts +3 -3
- package/dist/components/experimental/client/Tabs.d.cts +3 -3
- package/dist/components/experimental/client/Tabs.d.ts +6 -6
- package/dist/components/experimental/client/Toast.d.cts +2 -2
- package/dist/components/experimental/client/Toast.d.ts +2 -2
- package/dist/providers/ColorModeProvider.d.cts +4 -4
- package/dist/providers/ColorModeProvider.d.cts.map +1 -1
- package/dist/providers/ColorModeProvider.d.ts +4 -4
- package/dist/providers/ColorModeProvider.d.ts.map +1 -1
- package/dist/providers/ScaleModeProvider.d.cts +4 -4
- package/dist/providers/ScaleModeProvider.d.cts.map +1 -1
- package/dist/providers/ScaleModeProvider.d.ts +4 -4
- package/dist/providers/ScaleModeProvider.d.ts.map +1 -1
- package/dist/providers/ThemeProvider.d.cts +2 -2
- package/dist/providers/ThemeProvider.d.cts.map +1 -1
- package/dist/providers/ThemeProvider.d.ts +2 -2
- package/dist/styles/styler.d.cts +33 -33
- package/dist/styles/styler.d.ts +33 -33
- package/dist/styles/stylerTypes.d.ts.map +1 -1
- package/dist/tailwind/tailwind.config.d.ts +3 -3
- package/dist/tailwind/tailwindPlugin.d.ts +3 -3
- package/dist/tailwind/utils/getMotionStyles.d.cts +1 -1
- package/dist/tailwind/utils/getMotionStyles.d.ts +1 -1
- package/dist/tokens/automation/utils/getConfigVariantProperties.d.cts +2 -2
- package/dist/tokens/automation/utils/getConfigVariantProperties.d.cts.map +1 -1
- package/dist/tokens/automation/utils/getConfigVariantProperties.d.ts +2 -2
- package/dist/tokens/automation/utils/getConfigVariantProperties.d.ts.map +1 -1
- package/dist/tokens/types.d.cts.map +1 -1
- package/dist/tokens/types.d.ts.map +1 -1
- package/dist/utils/intersperse.d.cts +2 -2
- package/dist/utils/intersperse.d.ts +2 -2
- package/package.json +1 -3
- package/cli/FlattenButtonVariant.mock.tsx +0 -17
- package/src/components/Box.tsx +0 -181
- package/src/components/Divider/Divider.tsx +0 -44
- package/src/components/Divider/DividerCore.tsx +0 -101
- package/src/components/Divider/DividerInternal.tsx +0 -56
- package/src/components/Divider/index.ts +0 -1
- package/src/components/FormLabel.tsx +0 -66
- package/src/components/HStack.tsx +0 -53
- package/src/components/Icon.tsx +0 -82
- package/src/components/IconSlot.tsx +0 -82
- package/src/components/Image.tsx +0 -162
- package/src/components/Link.tsx +0 -134
- package/src/components/Text.tsx +0 -272
- package/src/components/VStack.tsx +0 -53
- package/src/components/client/AnimateHeightChange.tsx +0 -50
- package/src/components/client/Avatar/Avatar.tsx +0 -31
- package/src/components/client/Avatar/AvatarIcon.tsx +0 -103
- package/src/components/client/Avatar/AvatarImage.tsx +0 -147
- package/src/components/client/Avatar/AvatarText.tsx +0 -88
- package/src/components/client/Avatar/index.ts +0 -4
- package/src/components/client/Avatar/utils.ts +0 -105
- package/src/components/client/Badge.tsx +0 -133
- package/src/components/client/Button.tsx +0 -312
- package/src/components/client/Checkbox.tsx +0 -377
- package/src/components/client/Chip/Chip.tsx +0 -81
- package/src/components/client/Chip/ChipBase.tsx +0 -151
- package/src/components/client/Chip/ChipButton.tsx +0 -54
- package/src/components/client/Chip/ChipDismissible.tsx +0 -90
- package/src/components/client/Chip/ChipLink.tsx +0 -60
- package/src/components/client/Chip/ChipToggle.tsx +0 -79
- package/src/components/client/Chip/index.ts +0 -5
- package/src/components/client/IconButton.tsx +0 -198
- package/src/components/client/Input/Input.tsx +0 -323
- package/src/components/client/Input/InputHelpText.tsx +0 -52
- package/src/components/client/Input/InputHelpTextInternal.tsx +0 -81
- package/src/components/client/Input/index.ts +0 -2
- package/src/components/client/Menu/Menu.Content.tsx +0 -391
- package/src/components/client/Menu/Menu.Divider.tsx +0 -102
- package/src/components/client/Menu/Menu.Item.tsx +0 -114
- package/src/components/client/Menu/Menu.ItemBase.tsx +0 -265
- package/src/components/client/Menu/Menu.ItemCheckbox.tsx +0 -197
- package/src/components/client/Menu/Menu.Provider.tsx +0 -154
- package/src/components/client/Menu/Menu.Trigger.tsx +0 -119
- package/src/components/client/Menu/Menu.index.tsx +0 -9
- package/src/components/client/Menu/Menu.tsx +0 -2
- package/src/components/client/Menu/index.ts +0 -7
- package/src/components/client/Menu/utils/transformAriakitPlacement.ts +0 -66
- package/src/components/client/Pressable.tsx +0 -194
- package/src/components/client/Radio/Radio.tsx +0 -351
- package/src/components/client/Radio/RadioGroupProvider.tsx +0 -122
- package/src/components/client/Radio/RadioGroupStore.tsx +0 -56
- package/src/components/client/Radio/index.ts +0 -2
- package/src/components/client/Radio/useRadioGroup.ts +0 -149
- package/src/components/client/SpringMotionConfig.tsx +0 -151
- package/src/components/client/Switch.tsx +0 -377
- package/src/components/client/index.ts +0 -30
- package/src/components/client/motionFeatures/domAnimation.ts +0 -2
- package/src/components/client/motionFeatures/domMax.ts +0 -2
- package/src/components/experimental/Spinner.tsx +0 -30
- package/src/components/experimental/Table.mocks.tsx +0 -38
- package/src/components/experimental/Table.tsx +0 -239
- package/src/components/experimental/client/Accordion.tsx +0 -77
- package/src/components/experimental/client/AvoidMotionLibraryProvider.tsx +0 -10
- package/src/components/experimental/client/Modal.tsx +0 -68
- package/src/components/experimental/client/Popover.tsx +0 -63
- package/src/components/experimental/client/SwitchV2.tsx +0 -343
- package/src/components/experimental/client/Tabs.tsx +0 -106
- package/src/components/experimental/client/Toast.tsx +0 -186
- package/src/components/experimental/client/index.ts +0 -15
- package/src/components/experimental/index.ts +0 -48
- package/src/components/index.ts +0 -41
- package/src/defaultTokensConfig.ts +0 -31
- package/src/fixtures/index.ts +0 -638
- package/src/fixtures/macros/typesToConstants.ts +0 -42
- package/src/fixtures/utils/getScaleModeRamp.ts +0 -48
- package/src/fixtures/utils/getTailwindAsUdsColors.ts +0 -115
- package/src/flags.ts +0 -45
- package/src/hooks/useForkRef.ts +0 -34
- package/src/hooks/useNestedBorderRadius.ts +0 -138
- package/src/hooks/useRtl.ts +0 -64
- package/src/index.ts +0 -4
- package/src/providers/ColorModeProvider.tsx +0 -21
- package/src/providers/ScaleModeProvider.tsx +0 -29
- package/src/providers/ThemeProvider.tsx +0 -26
- package/src/styles/styler.ts +0 -194
- package/src/styles/stylerTypes.ts +0 -81
- package/src/styles/toast.css +0 -0
- package/src/styles/variants.ts +0 -1351
- package/src/tailwind/base/addColorModeVars.ts +0 -23
- package/src/tailwind/base/addColorModeVarsV2.ts +0 -57
- package/src/tailwind/base/addFontFaceDeclarations.ts +0 -13
- package/src/tailwind/base/addFontVars.ts +0 -9
- package/src/tailwind/base/addMotionVars.ts +0 -9
- package/src/tailwind/base/addScaleModeVars.ts +0 -33
- package/src/tailwind/base/types.ts +0 -8
- package/src/tailwind/components/getButtonStyles.ts +0 -240
- package/src/tailwind/components/getFocusRingStyles.ts +0 -32
- package/src/tailwind/components/getGroupedTextStyles.ts +0 -29
- package/src/tailwind/components/getHitTargetStyles.ts +0 -23
- package/src/tailwind/components/getIconButtonStyles.ts +0 -49
- package/src/tailwind/components/getIconStyles.ts +0 -13
- package/src/tailwind/components/getInputStyles.ts +0 -20
- package/src/tailwind/components/getResponsiveTextStyles.ts +0 -161
- package/src/tailwind/components/types.ts +0 -14
- package/src/tailwind/components/utils.ts +0 -73
- package/src/tailwind/defaultTailwindThemeAsUdsConfig.ts +0 -78
- package/src/tailwind/postcss.config.ts +0 -6
- package/src/tailwind/purger/index.ts +0 -1
- package/src/tailwind/tailwind.config.ts +0 -50
- package/src/tailwind/tailwind.d.ts +0 -5
- package/src/tailwind/tailwindPlugin.ts +0 -724
- package/src/tailwind/theme/getFontFamilyTheme.ts +0 -28
- package/src/tailwind/tsMorph.ts +0 -1
- package/src/tailwind/uds.css +0 -3
- package/src/tailwind/utils/addFontsPlugin.ts +0 -17
- package/src/tailwind/utils/getColorModeStyles.ts +0 -21
- package/src/tailwind/utils/getFontFaceDeclarations.ts +0 -12
- package/src/tailwind/utils/getFontStyles.ts +0 -38
- package/src/tailwind/utils/getMotionStyles.ts +0 -35
- package/src/tailwind/utils/getScaleModeStyles.ts +0 -23
- package/src/tailwind/utils/getShadowPresetValues.ts +0 -133
- package/src/tailwind/utils/getShadowStyles.ts +0 -26
- package/src/tailwind/utils/getShadowVars.ts +0 -40
- package/src/tailwind/utils/index.ts +0 -9
- package/src/tokens/automation/configs/avatar.ts +0 -297
- package/src/tokens/automation/configs/badge.ts +0 -306
- package/src/tokens/automation/configs/checkbox.ts +0 -226
- package/src/tokens/automation/configs/chip.ts +0 -521
- package/src/tokens/automation/configs/divider.ts +0 -112
- package/src/tokens/automation/configs/index.ts +0 -10
- package/src/tokens/automation/configs/input.ts +0 -496
- package/src/tokens/automation/configs/link.ts +0 -183
- package/src/tokens/automation/configs/menu.ts +0 -344
- package/src/tokens/automation/configs/radio.ts +0 -225
- package/src/tokens/automation/configs/switch.ts +0 -323
- package/src/tokens/automation/index.ts +0 -5
- package/src/tokens/automation/mapTextVariantFixtureToValue.ts +0 -8
- package/src/tokens/automation/properties.ts +0 -433
- package/src/tokens/automation/types/ComponentConfig.ts +0 -116
- package/src/tokens/automation/types/ComponentDB.ts +0 -1
- package/src/tokens/automation/types/ComponentStyles.ts +0 -47
- package/src/tokens/automation/types/index.ts +0 -2
- package/src/tokens/automation/utils/cartesianProduct.ts +0 -13
- package/src/tokens/automation/utils/coalesceConfigVariant.ts +0 -49
- package/src/tokens/automation/utils/defaults.ts +0 -29
- package/src/tokens/automation/utils/generateKeyFromFlatConfigPath.ts +0 -15
- package/src/tokens/automation/utils/generateSchemaKey.ts +0 -29
- package/src/tokens/automation/utils/getConfigComponentVariant.ts +0 -28
- package/src/tokens/automation/utils/getConfigVariantComponentStatesMatrix.ts +0 -40
- package/src/tokens/automation/utils/getConfigVariantProperties.ts +0 -32
- package/src/tokens/automation/utils/getConfigVariantPseudoStates.ts +0 -70
- package/src/tokens/automation/utils/getConfigVariants.ts +0 -73
- package/src/tokens/automation/utils/index.ts +0 -939
- package/src/tokens/automation/utils/mapColorFixtureToValue.ts +0 -34
- package/src/tokens/automation/utils/subcomponents.ts +0 -13
- package/src/tokens/automation/utils/variableData.ts +0 -79
- package/src/tokens/automation/utils/variantConfigGuards.ts +0 -17
- package/src/tokens/configs/borderRadius.ts +0 -23
- package/src/tokens/configs/borderWidth.ts +0 -17
- package/src/tokens/configs/button.ts +0 -888
- package/src/tokens/configs/colorMode.ts +0 -14
- package/src/tokens/configs/font.ts +0 -9
- package/src/tokens/configs/iconButton.ts +0 -28
- package/src/tokens/configs/motion.ts +0 -135
- package/src/tokens/configs/palette.ts +0 -124
- package/src/tokens/configs/scaleMode.ts +0 -28
- package/src/tokens/configs/shadow.ts +0 -286
- package/src/tokens/configs/sizes.ts +0 -23
- package/src/tokens/configs/spectrum.ts +0 -729
- package/src/tokens/configs/typography.ts +0 -417
- package/src/tokens/configs/yosConfig.ts +0 -7651
- package/src/tokens/consts/buttonMotionTokens.ts +0 -99
- package/src/tokens/consts/cssTokens.ts +0 -110
- package/src/tokens/consts/defaultModes.ts +0 -15
- package/src/tokens/consts/fontDeclarationsMap.ts +0 -509
- package/src/tokens/index.ts +0 -15
- package/src/tokens/parseButtonVariants.ts +0 -59
- package/src/tokens/parseTokens.ts +0 -122
- package/src/tokens/types.ts +0 -1882
- package/src/tokens/utils/entries.ts +0 -14
- package/src/tokens/utils/fromEntries.ts +0 -11
- package/src/tokens/utils/mapValues.ts +0 -15
- package/src/types.ts +0 -2
- package/src/utils/assertUnreachable.ts +0 -6
- package/src/utils/composeRefs.ts +0 -23
- package/src/utils/createSlot.tsx +0 -131
- package/src/utils/entries.ts +0 -6
- package/src/utils/falsyToString.ts +0 -3
- package/src/utils/getMotionVar.ts +0 -18
- package/src/utils/intersperse.ts +0 -45
- package/src/utils/mapValues.ts +0 -15
- package/src/utils/mergeSlotProps.ts +0 -70
|
@@ -1,351 +0,0 @@
|
|
|
1
|
-
import type { RadioSize, RadioVariant, UniversalRadioProps } from '@yahoo/uds/tokens';
|
|
2
|
-
import { m, useReducedMotion } from 'motion/react';
|
|
3
|
-
import type { ChangeEvent, FocusEvent } from 'react';
|
|
4
|
-
import React, { forwardRef, useCallback, useId, useMemo, useRef, useState } from 'react';
|
|
5
|
-
|
|
6
|
-
import { cx, getStyles } from '../../../styles/styler';
|
|
7
|
-
import { Box } from '../../Box';
|
|
8
|
-
import { FormLabel } from '../../FormLabel';
|
|
9
|
-
import { SpringMotionConfig } from '../SpringMotionConfig';
|
|
10
|
-
import { useRadioGroup } from './useRadioGroup';
|
|
11
|
-
|
|
12
|
-
type NativeInputProps = Omit<
|
|
13
|
-
React.InputHTMLAttributes<HTMLInputElement>,
|
|
14
|
-
'type' | 'checked' | 'value' | 'size' | 'height' | 'width' | 'color' | 'required'
|
|
15
|
-
>;
|
|
16
|
-
|
|
17
|
-
interface RadioProps extends NativeInputProps, UniversalRadioProps {
|
|
18
|
-
layerClassNames?: {
|
|
19
|
-
root?: string;
|
|
20
|
-
radio?: string;
|
|
21
|
-
label?: string;
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const RADIO_VARIANTS: RadioVariant[] = ['primary', 'secondary'];
|
|
26
|
-
const RADIO_SIZES: RadioSize[] = ['sm', 'md'];
|
|
27
|
-
|
|
28
|
-
const VARIANT_ERROR_MAP: Record<RadioVariant, 'alert' | 'alert-secondary'> = {
|
|
29
|
-
primary: 'alert',
|
|
30
|
-
secondary: 'alert-secondary',
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
const MotionBox = m.create(Box);
|
|
34
|
-
|
|
35
|
-
const hoverTransition = { layoutVariant: 'smooth', layoutSpeed: '3' } as const;
|
|
36
|
-
const pressTransition = { layoutVariant: 'veryBouncy', layoutSpeed: '4' } as const;
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* **⚙️️ A Radio.
|
|
40
|
-
*
|
|
41
|
-
* @componentType Client component
|
|
42
|
-
*
|
|
43
|
-
* @description
|
|
44
|
-
* A radio component allows users to select one or multiple options from a set. Unlike checkbox, radio buttons cannot have multiple selected in a form, allowing only 1 selection in a group. It represents a binary state, typically as selected or unselected.
|
|
45
|
-
*
|
|
46
|
-
* @see
|
|
47
|
-
* Check out the {@link https://uds.build/docs/components/radio Radio Docs} for more info
|
|
48
|
-
*
|
|
49
|
-
* @usage
|
|
50
|
-
* - Forms: Radio
|
|
51
|
-
*
|
|
52
|
-
* @example
|
|
53
|
-
* ```tsx
|
|
54
|
-
* 'use client';
|
|
55
|
-
* import { Radio } from "@yahoo/uds";
|
|
56
|
-
*
|
|
57
|
-
* <Radio label="Name" />
|
|
58
|
-
*```
|
|
59
|
-
*
|
|
60
|
-
* @related [Radio](https://uds.build/docs/components/Radio).
|
|
61
|
-
**/
|
|
62
|
-
const Radio = forwardRef<HTMLInputElement, RadioProps>(function Radio(
|
|
63
|
-
{
|
|
64
|
-
id,
|
|
65
|
-
name: nameProp,
|
|
66
|
-
label,
|
|
67
|
-
labelPosition = 'start',
|
|
68
|
-
variant: variantProp = 'primary',
|
|
69
|
-
size = 'md',
|
|
70
|
-
reduceMotion: forceReduceMotion,
|
|
71
|
-
value: valueProp = '', // Matches HTML
|
|
72
|
-
checked: checkedProp,
|
|
73
|
-
defaultChecked,
|
|
74
|
-
disabled,
|
|
75
|
-
hasError,
|
|
76
|
-
onChange,
|
|
77
|
-
onFocus,
|
|
78
|
-
onBlur,
|
|
79
|
-
className,
|
|
80
|
-
layerClassNames,
|
|
81
|
-
...radioProps
|
|
82
|
-
},
|
|
83
|
-
parentRef,
|
|
84
|
-
) {
|
|
85
|
-
const generatedId = useId();
|
|
86
|
-
const uid = id ?? `uds-radio-${generatedId}`;
|
|
87
|
-
|
|
88
|
-
const innerRef = useRef<HTMLInputElement | null>(null);
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* State
|
|
92
|
-
*/
|
|
93
|
-
const { name, isControlled, checked, setGroupValue } = useRadioGroup({
|
|
94
|
-
name: nameProp,
|
|
95
|
-
value: valueProp,
|
|
96
|
-
checked: checkedProp,
|
|
97
|
-
defaultChecked,
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
const [isHovered, setIsHovered] = useState(false);
|
|
101
|
-
const [isPressed, setIsPressed] = useState(false);
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Derived values
|
|
105
|
-
*/
|
|
106
|
-
const variant = useMemo(
|
|
107
|
-
() => (hasError ? VARIANT_ERROR_MAP[variantProp] : variantProp),
|
|
108
|
-
[hasError, variantProp],
|
|
109
|
-
);
|
|
110
|
-
|
|
111
|
-
const showHoverEffect = useMemo(() => !disabled && isHovered, [disabled, isHovered]);
|
|
112
|
-
|
|
113
|
-
const showPressedEffect = useMemo(
|
|
114
|
-
() => !disabled && !checked && isPressed,
|
|
115
|
-
[checked, disabled, isPressed],
|
|
116
|
-
);
|
|
117
|
-
|
|
118
|
-
const scale = useMemo(
|
|
119
|
-
() => (showPressedEffect ? 0.9 : showHoverEffect ? 1.1 : 1),
|
|
120
|
-
[showHoverEffect, showPressedEffect],
|
|
121
|
-
);
|
|
122
|
-
|
|
123
|
-
const checkOpacity = useMemo(
|
|
124
|
-
() => (checked ? (showPressedEffect ? 'opacity-55' : 'opacity-100') : 'opacity-0'),
|
|
125
|
-
[checked, showPressedEffect],
|
|
126
|
-
);
|
|
127
|
-
|
|
128
|
-
const showShadow = useMemo(() => {
|
|
129
|
-
const pressedAndUnchecked = showPressedEffect && !checked;
|
|
130
|
-
const hoverAndChecked = showHoverEffect && checked;
|
|
131
|
-
|
|
132
|
-
return !disabled && (pressedAndUnchecked || hoverAndChecked);
|
|
133
|
-
}, [checked, disabled, showHoverEffect, showPressedEffect]);
|
|
134
|
-
|
|
135
|
-
const motionAnimate = useMemo(() => ({ scale }), [scale]);
|
|
136
|
-
|
|
137
|
-
const motionTransition = useMemo(
|
|
138
|
-
() => (isPressed ? pressTransition : hoverTransition),
|
|
139
|
-
[isPressed],
|
|
140
|
-
);
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Handlers
|
|
144
|
-
*/
|
|
145
|
-
const handleOnChange = useCallback(
|
|
146
|
-
(e: ChangeEvent<HTMLInputElement>) => {
|
|
147
|
-
setGroupValue(e.target.value);
|
|
148
|
-
onChange?.(e);
|
|
149
|
-
},
|
|
150
|
-
[onChange, setGroupValue],
|
|
151
|
-
);
|
|
152
|
-
|
|
153
|
-
const handleFocus = useCallback(
|
|
154
|
-
(e: FocusEvent<HTMLInputElement>) => {
|
|
155
|
-
onFocus?.(e);
|
|
156
|
-
},
|
|
157
|
-
[onFocus],
|
|
158
|
-
);
|
|
159
|
-
|
|
160
|
-
const handleBlur = useCallback(
|
|
161
|
-
(e: FocusEvent<HTMLInputElement>) => {
|
|
162
|
-
onBlur?.(e);
|
|
163
|
-
},
|
|
164
|
-
[onBlur],
|
|
165
|
-
);
|
|
166
|
-
|
|
167
|
-
const handleHoverStart = useCallback(() => {
|
|
168
|
-
setIsHovered(true);
|
|
169
|
-
}, []);
|
|
170
|
-
|
|
171
|
-
const handleHoverEnd = useCallback(() => {
|
|
172
|
-
setIsHovered(false);
|
|
173
|
-
}, []);
|
|
174
|
-
|
|
175
|
-
const handleTapStart = useCallback(() => {
|
|
176
|
-
setIsPressed(true);
|
|
177
|
-
}, []);
|
|
178
|
-
|
|
179
|
-
const handleTapEnd = useCallback(() => {
|
|
180
|
-
setIsPressed(false);
|
|
181
|
-
}, []);
|
|
182
|
-
|
|
183
|
-
// Motion
|
|
184
|
-
const prefersReducedMotion = useReducedMotion();
|
|
185
|
-
const reduceMotion = forceReduceMotion ? 'always' : 'user';
|
|
186
|
-
|
|
187
|
-
const cssAnimationDuration =
|
|
188
|
-
prefersReducedMotion || forceReduceMotion ? 'duration-0' : 'duration-120';
|
|
189
|
-
|
|
190
|
-
const classNames = {
|
|
191
|
-
root: getStyles({
|
|
192
|
-
radioSizeRoot: size,
|
|
193
|
-
radioVariantValueRoot: checked ? 'checked' : 'unchecked',
|
|
194
|
-
radioVariantRoot: variant,
|
|
195
|
-
display: 'flex',
|
|
196
|
-
flexDirection: labelPosition === 'start' ? 'row' : 'row-reverse',
|
|
197
|
-
alignItems: 'center',
|
|
198
|
-
className: cx([
|
|
199
|
-
{
|
|
200
|
-
'cursor-pointer': !disabled,
|
|
201
|
-
'cursor-default': disabled,
|
|
202
|
-
'opacity-50': disabled,
|
|
203
|
-
},
|
|
204
|
-
|
|
205
|
-
className,
|
|
206
|
-
layerClassNames?.root,
|
|
207
|
-
]),
|
|
208
|
-
}),
|
|
209
|
-
|
|
210
|
-
radio: getStyles({
|
|
211
|
-
radioSizeRadio: size,
|
|
212
|
-
radioVariantRadio: variant,
|
|
213
|
-
radioVariantValueRadio: checked ? 'checked' : 'unchecked',
|
|
214
|
-
className: cx([
|
|
215
|
-
'border',
|
|
216
|
-
|
|
217
|
-
'uds-ring',
|
|
218
|
-
'uds-ring-within',
|
|
219
|
-
|
|
220
|
-
cssAnimationDuration,
|
|
221
|
-
'transition-[background-color,border-color]',
|
|
222
|
-
'transition-shadow',
|
|
223
|
-
|
|
224
|
-
layerClassNames?.radio,
|
|
225
|
-
]),
|
|
226
|
-
}),
|
|
227
|
-
|
|
228
|
-
radioCircle: getStyles({
|
|
229
|
-
radioVariantRadioCircle: variant,
|
|
230
|
-
radioVariantValueRadioCircle: checked ? 'checked' : 'unchecked',
|
|
231
|
-
className: cx([
|
|
232
|
-
'pointer-events-none',
|
|
233
|
-
'w-[8px]',
|
|
234
|
-
'h-[8px]',
|
|
235
|
-
|
|
236
|
-
// Variant/state specific check opacity override only used when checked.
|
|
237
|
-
checkOpacity,
|
|
238
|
-
|
|
239
|
-
cssAnimationDuration,
|
|
240
|
-
'transition-opacity',
|
|
241
|
-
]),
|
|
242
|
-
}),
|
|
243
|
-
|
|
244
|
-
htmlRadio: cx(
|
|
245
|
-
// Control the cursor at the top of the component
|
|
246
|
-
// and don't let the input override it back to the
|
|
247
|
-
// default value.
|
|
248
|
-
'cursor-[inherit]',
|
|
249
|
-
|
|
250
|
-
'absolute',
|
|
251
|
-
'opacity-0',
|
|
252
|
-
'top-1/2',
|
|
253
|
-
'left-1/2',
|
|
254
|
-
'w-[calc(100%+2px)]',
|
|
255
|
-
'h-[calc(100%+2px)]',
|
|
256
|
-
'transform',
|
|
257
|
-
'translate-x-[-50%]',
|
|
258
|
-
'translate-y-[-50%]',
|
|
259
|
-
),
|
|
260
|
-
|
|
261
|
-
label: cx(labelPosition === 'start' ? 'text-start' : 'text-end', layerClassNames?.label),
|
|
262
|
-
};
|
|
263
|
-
|
|
264
|
-
const RootElement = label ? m.label : m.div;
|
|
265
|
-
|
|
266
|
-
return (
|
|
267
|
-
<SpringMotionConfig reducedMotion={reduceMotion} {...motionTransition}>
|
|
268
|
-
<RootElement
|
|
269
|
-
className={classNames.root}
|
|
270
|
-
data-testid="container"
|
|
271
|
-
onHoverStart={handleHoverStart}
|
|
272
|
-
onHoverEnd={handleHoverEnd}
|
|
273
|
-
onTapStart={handleTapStart}
|
|
274
|
-
onTap={handleTapEnd}
|
|
275
|
-
onTapCancel={handleTapEnd}
|
|
276
|
-
tabIndex={-1}
|
|
277
|
-
>
|
|
278
|
-
<MotionBox
|
|
279
|
-
position="relative"
|
|
280
|
-
borderRadius="full"
|
|
281
|
-
alignItems="center"
|
|
282
|
-
justifyContent="center"
|
|
283
|
-
flex="none"
|
|
284
|
-
className={classNames.radio}
|
|
285
|
-
animate={motionAnimate}
|
|
286
|
-
insetShadow={showShadow ? 'lg-invert' : 'none'}
|
|
287
|
-
>
|
|
288
|
-
<input
|
|
289
|
-
type="radio"
|
|
290
|
-
// Required for Safari to render a focus ring
|
|
291
|
-
tabIndex={0}
|
|
292
|
-
// These are standard HTML input props for customization.
|
|
293
|
-
{...radioProps}
|
|
294
|
-
//
|
|
295
|
-
// Provide a ref to the underlying input element for customization.
|
|
296
|
-
ref={(ref) => {
|
|
297
|
-
// Set internal ref.
|
|
298
|
-
innerRef.current = ref;
|
|
299
|
-
|
|
300
|
-
// Forward the ref to parent.
|
|
301
|
-
if (typeof parentRef === 'function') {
|
|
302
|
-
parentRef(ref);
|
|
303
|
-
} else if (parentRef !== null) {
|
|
304
|
-
parentRef.current = ref;
|
|
305
|
-
}
|
|
306
|
-
}}
|
|
307
|
-
//
|
|
308
|
-
// Track focus as a prop for easier styling.
|
|
309
|
-
onFocus={handleFocus}
|
|
310
|
-
onBlur={handleBlur}
|
|
311
|
-
//
|
|
312
|
-
// Core props for the Radio.
|
|
313
|
-
id={uid}
|
|
314
|
-
name={name}
|
|
315
|
-
value={valueProp}
|
|
316
|
-
disabled={disabled}
|
|
317
|
-
aria-invalid={hasError}
|
|
318
|
-
onChange={handleOnChange}
|
|
319
|
-
{...(isControlled
|
|
320
|
-
? {
|
|
321
|
-
checked,
|
|
322
|
-
}
|
|
323
|
-
: {
|
|
324
|
-
defaultChecked,
|
|
325
|
-
})}
|
|
326
|
-
//
|
|
327
|
-
// styles
|
|
328
|
-
className={classNames.htmlRadio}
|
|
329
|
-
/>
|
|
330
|
-
|
|
331
|
-
<Box borderRadius="full" className={classNames.radioCircle} />
|
|
332
|
-
</MotionBox>
|
|
333
|
-
|
|
334
|
-
{label && (
|
|
335
|
-
<FormLabel
|
|
336
|
-
as="div"
|
|
337
|
-
variant="inherit"
|
|
338
|
-
color="inherit"
|
|
339
|
-
label={label}
|
|
340
|
-
className={classNames.label}
|
|
341
|
-
/>
|
|
342
|
-
)}
|
|
343
|
-
</RootElement>
|
|
344
|
-
</SpringMotionConfig>
|
|
345
|
-
);
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
// Need to re-set this because of the forwardRef wrapper
|
|
349
|
-
Radio.displayName = 'Radio';
|
|
350
|
-
|
|
351
|
-
export { Radio, RADIO_SIZES, RADIO_VARIANTS, type RadioProps };
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import type { UniversalRadioGroupProps } from '@yahoo/uds/tokens';
|
|
2
|
-
import React, { forwardRef, useEffect, useMemo } from 'react';
|
|
3
|
-
|
|
4
|
-
import type { DivProps } from '../../../components/Box';
|
|
5
|
-
import { createSlot } from '../../../utils/createSlot';
|
|
6
|
-
import { RadioGroupContext, RadioGroupStore } from './RadioGroupStore';
|
|
7
|
-
import { useRadioGroupName } from './useRadioGroup';
|
|
8
|
-
|
|
9
|
-
const Slot = createSlot<React.ComponentType<DivProps>>();
|
|
10
|
-
|
|
11
|
-
type NativeDivProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange' | 'defaultValue'>;
|
|
12
|
-
|
|
13
|
-
interface RadioGroupProviderProps extends UniversalRadioGroupProps, NativeDivProps {}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* A custom RadioGroupStore that extends the default RadioGroupStore to allow for controlled RadioGroups.
|
|
17
|
-
*/
|
|
18
|
-
class RadioGroupProviderStore extends RadioGroupStore {
|
|
19
|
-
// The callback to notify the controlled RadioGroup of a value change.
|
|
20
|
-
onChangeCb?: (value: string | undefined) => void;
|
|
21
|
-
|
|
22
|
-
// Whether the RadioGroup is controlled
|
|
23
|
-
isControlled: boolean;
|
|
24
|
-
|
|
25
|
-
constructor(name: string, isControlled: boolean) {
|
|
26
|
-
super(name);
|
|
27
|
-
|
|
28
|
-
this.isControlled = isControlled;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Overloads the default setValue to notify the controlled RadioGroup of a value change rather than setting the value directly.
|
|
32
|
-
setValue(value: string | undefined, force = false) {
|
|
33
|
-
// If we are being used inside RadioGroupProvider, we can force the store value to be set (and notify the listeners).
|
|
34
|
-
if (!this.isControlled || force) {
|
|
35
|
-
super.setValue(value);
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
this.onChangeCb?.(value);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* **⚙️️ A RadioGroup that improves accessibility and provides a central component for state manangement.
|
|
45
|
-
*
|
|
46
|
-
* @componentType Client component
|
|
47
|
-
*
|
|
48
|
-
* @description
|
|
49
|
-
* Provides a radio group
|
|
50
|
-
*
|
|
51
|
-
* @see
|
|
52
|
-
* Check out the {@link https://uds.build/docs/components/radio Radio Docs} for more info
|
|
53
|
-
*
|
|
54
|
-
* @usage
|
|
55
|
-
* - Forms: RadioGroup
|
|
56
|
-
*
|
|
57
|
-
* @example
|
|
58
|
-
* ```tsx
|
|
59
|
-
* 'use client';
|
|
60
|
-
* import { RadioGroup } from "@yahoo/uds";
|
|
61
|
-
*
|
|
62
|
-
* <RadioGroup name="items" value="1">
|
|
63
|
-
* <Radio value="1" label="One" />
|
|
64
|
-
* <Radio value="2" label="Two" />
|
|
65
|
-
* </RadioGroup>
|
|
66
|
-
*```
|
|
67
|
-
*
|
|
68
|
-
* @related [Radio](https://uds.build/docs/components/radio).
|
|
69
|
-
**/
|
|
70
|
-
const RadioGroupProvider = forwardRef<HTMLDivElement, RadioGroupProviderProps>(
|
|
71
|
-
function RadioGroupProvider(
|
|
72
|
-
{ asChild, name, value: valueProp, defaultValue, onChange, ...props },
|
|
73
|
-
ref,
|
|
74
|
-
) {
|
|
75
|
-
const isControlled = valueProp !== undefined;
|
|
76
|
-
const localName = useRadioGroupName(name);
|
|
77
|
-
|
|
78
|
-
// The store for this RadioGroup. Will be created once, memoized and used as the context value.
|
|
79
|
-
const store = useMemo(() => {
|
|
80
|
-
const s = new RadioGroupProviderStore(localName, isControlled);
|
|
81
|
-
|
|
82
|
-
// Set the initial value of the store
|
|
83
|
-
s.value = isControlled ? valueProp : defaultValue;
|
|
84
|
-
|
|
85
|
-
return s;
|
|
86
|
-
}, [defaultValue, isControlled, localName, valueProp]);
|
|
87
|
-
|
|
88
|
-
useEffect(() => {
|
|
89
|
-
// The store should call the onChange callback when the a change event is emitted.
|
|
90
|
-
store.onChangeCb = onChange;
|
|
91
|
-
}, [onChange, store]);
|
|
92
|
-
|
|
93
|
-
useEffect(() => {
|
|
94
|
-
if (!isControlled) {
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
store.setValue(
|
|
99
|
-
valueProp,
|
|
100
|
-
true, // Force set the value since we are responding to a controlled prop change.
|
|
101
|
-
);
|
|
102
|
-
}, [isControlled, store, valueProp]);
|
|
103
|
-
|
|
104
|
-
// When the store changes, trigger a change.
|
|
105
|
-
useEffect(() => {
|
|
106
|
-
return store.onChange((value) => onChange?.(value));
|
|
107
|
-
}, [onChange, store]);
|
|
108
|
-
|
|
109
|
-
const Comp = asChild ? Slot : 'div';
|
|
110
|
-
|
|
111
|
-
return (
|
|
112
|
-
<RadioGroupContext.Provider value={store}>
|
|
113
|
-
<Comp role="radiogroup" ref={ref} {...props} />
|
|
114
|
-
</RadioGroupContext.Provider>
|
|
115
|
-
);
|
|
116
|
-
},
|
|
117
|
-
);
|
|
118
|
-
|
|
119
|
-
// Need to re-set this because of the forwardRef wrapper
|
|
120
|
-
RadioGroupProvider.displayName = 'RadioGroupProvider';
|
|
121
|
-
|
|
122
|
-
export { RadioGroupProvider, type RadioGroupProviderProps };
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { createContext } from 'react';
|
|
2
|
-
|
|
3
|
-
type RadioGroupStoreOnChange = (value: string | undefined) => void;
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* A store for a single RadioGroup. This store is used to manage the state of all Radios within a group.
|
|
7
|
-
*/
|
|
8
|
-
class RadioGroupStore {
|
|
9
|
-
// The html name attribute of the RadioGroup.
|
|
10
|
-
name: string;
|
|
11
|
-
|
|
12
|
-
// The current value of the RadioGroup.
|
|
13
|
-
value?: string;
|
|
14
|
-
|
|
15
|
-
// A count of the number of active Radios in the group. Used to cleanup stores when all Radios are unmounted.
|
|
16
|
-
activeCount = 0;
|
|
17
|
-
|
|
18
|
-
// A set of listeners to call when the value of the RadioGroup changes.
|
|
19
|
-
#changeListeners = new Set<RadioGroupStoreOnChange>();
|
|
20
|
-
|
|
21
|
-
constructor(name: string) {
|
|
22
|
-
this.name = name;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// Register a listener to be called when the value of the RadioGroup changes.
|
|
26
|
-
onChange(cb: RadioGroupStoreOnChange) {
|
|
27
|
-
this.#changeListeners.add(cb);
|
|
28
|
-
|
|
29
|
-
// Return a function to remove the listener (used as unmount handler in useEffect).
|
|
30
|
-
return () => {
|
|
31
|
-
this.#changeListeners.delete(cb);
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// Set the value of the RadioGroup and notify all listeners.
|
|
36
|
-
setValue(value: string | undefined) {
|
|
37
|
-
this.value = value;
|
|
38
|
-
this.#changeListeners.forEach((cb) => cb(value));
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Create a new store when first encounting a new RadioGroup name.
|
|
42
|
-
static findOrCreate(radioGroupStores: Map<string, RadioGroupStore>, name: string) {
|
|
43
|
-
let store = radioGroupStores.get(name);
|
|
44
|
-
|
|
45
|
-
if (!store) {
|
|
46
|
-
store = new RadioGroupStore(name);
|
|
47
|
-
radioGroupStores.set(name, store);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return store;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const RadioGroupContext = createContext<Map<string, RadioGroupStore> | RadioGroupStore>(new Map());
|
|
55
|
-
|
|
56
|
-
export { RadioGroupContext, RadioGroupStore };
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
import { useCallback, useContext, useEffect, useId, useMemo, useState } from 'react';
|
|
2
|
-
|
|
3
|
-
import { RadioGroupContext, RadioGroupStore } from './RadioGroupStore';
|
|
4
|
-
|
|
5
|
-
const useRadioGroupName = (name?: string) => {
|
|
6
|
-
const uid = useId();
|
|
7
|
-
return name ?? `uds-radio-group-${uid}`;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* A hook that connects a single Radio to its RadioGroupStore. If the Radio is used within a RadioGroupProvider,
|
|
12
|
-
* it will use the context store. Otherwise, it will use a shared global store across all un-grouped Radios by name.
|
|
13
|
-
*
|
|
14
|
-
* @param name The name of the Radio which should be shared across the group.
|
|
15
|
-
* @param value The value of the Radio.
|
|
16
|
-
* @param checked Whether the Radio is checked by its controlled prop.
|
|
17
|
-
* @param defaultChecked The uncontrolled default checked state of the Radio.
|
|
18
|
-
* @returns The name of the RadioGroup, whether the Radio is controlled, the checked state of the Radio, and a function to set the RadioGroup value.
|
|
19
|
-
*/
|
|
20
|
-
const useRadioGroup = ({
|
|
21
|
-
name: radioName,
|
|
22
|
-
value: radioValue,
|
|
23
|
-
checked: radioIsChecked,
|
|
24
|
-
defaultChecked: radioDefaultIsChecked,
|
|
25
|
-
}: {
|
|
26
|
-
name?: string | undefined;
|
|
27
|
-
value: string;
|
|
28
|
-
checked?: boolean;
|
|
29
|
-
defaultChecked?: boolean;
|
|
30
|
-
}): {
|
|
31
|
-
name: string;
|
|
32
|
-
isControlled: boolean;
|
|
33
|
-
checked: boolean | undefined;
|
|
34
|
-
setGroupValue: (value: string) => void;
|
|
35
|
-
} => {
|
|
36
|
-
const ctx = useContext(RadioGroupContext);
|
|
37
|
-
const hasRadioGroupProvider = ctx instanceof RadioGroupStore;
|
|
38
|
-
const isControlled = radioIsChecked !== undefined || hasRadioGroupProvider;
|
|
39
|
-
|
|
40
|
-
// Use either the name passed as a prop on Radio or generate a unique name.
|
|
41
|
-
const uniqueName = useRadioGroupName(radioName);
|
|
42
|
-
|
|
43
|
-
// Get the Store controlling this Radio.
|
|
44
|
-
const store = useMemo(() => {
|
|
45
|
-
// Either from RadioGroupProvider
|
|
46
|
-
if (hasRadioGroupProvider) {
|
|
47
|
-
return ctx;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Or indexed by name in the global store.
|
|
51
|
-
return RadioGroupStore.findOrCreate(ctx, uniqueName);
|
|
52
|
-
}, [ctx, hasRadioGroupProvider, uniqueName]);
|
|
53
|
-
|
|
54
|
-
// The internal value of the RadioGroup.
|
|
55
|
-
const [radioGroupValue, setInternalRadioGroupValue] = useState<string | undefined>(store.value);
|
|
56
|
-
|
|
57
|
-
// If the store has a name set, all Radios inside the group should use that name. Otherwise use the Radio's name.
|
|
58
|
-
const name = store.name ?? uniqueName;
|
|
59
|
-
|
|
60
|
-
// Set the value of the RadioGroup.
|
|
61
|
-
const setGroupValue = useCallback(
|
|
62
|
-
(value: string) => {
|
|
63
|
-
// Notify the store.
|
|
64
|
-
store.setValue(value);
|
|
65
|
-
|
|
66
|
-
if (isControlled) {
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// and optimistically update the internal value.
|
|
71
|
-
setInternalRadioGroupValue(value);
|
|
72
|
-
},
|
|
73
|
-
[isControlled, store],
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
// Listen for store changes and update the internal value when it changes.
|
|
77
|
-
useEffect(() => store.onChange(setInternalRadioGroupValue), [store]);
|
|
78
|
-
|
|
79
|
-
useEffect(() => {
|
|
80
|
-
// Only update the store if a new Radio in the group has become checked.
|
|
81
|
-
if (radioIsChecked !== true) {
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
setGroupValue(radioValue);
|
|
86
|
-
}, [radioIsChecked, radioValue, setGroupValue]);
|
|
87
|
-
|
|
88
|
-
// Register with the global store for relating Radios without a RadioGroupProvider.
|
|
89
|
-
useEffect(() => {
|
|
90
|
-
// Don't need the global store if wrapped in a RadioGroupProvider.
|
|
91
|
-
if (hasRadioGroupProvider) {
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Get a store given the RadioGroup name.
|
|
96
|
-
const store = RadioGroupStore.findOrCreate(ctx, name);
|
|
97
|
-
|
|
98
|
-
// Increment the number of Radios using this store.
|
|
99
|
-
store.activeCount += 1;
|
|
100
|
-
|
|
101
|
-
// Save the store in the context.
|
|
102
|
-
ctx.set(name, store);
|
|
103
|
-
|
|
104
|
-
// On unmount.
|
|
105
|
-
return () => {
|
|
106
|
-
// If this radio is in the store...
|
|
107
|
-
const entry = ctx.get(name);
|
|
108
|
-
|
|
109
|
-
if (!entry) {
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Decrement the number of Radios using this store.
|
|
114
|
-
entry.activeCount -= 1;
|
|
115
|
-
|
|
116
|
-
// If there are no more Radios using this store, delete it.
|
|
117
|
-
if (entry.activeCount === 0) {
|
|
118
|
-
ctx.delete(name);
|
|
119
|
-
}
|
|
120
|
-
};
|
|
121
|
-
}, [ctx, hasRadioGroupProvider, name]);
|
|
122
|
-
|
|
123
|
-
// Complicated boolean logic reads better as a IIFE than a ternary.
|
|
124
|
-
const checked = (() => {
|
|
125
|
-
// If the Radio is controlled by its checked prop AND it's not in a RadioGroupProvider, use the prop.
|
|
126
|
-
if (isControlled && !hasRadioGroupProvider) {
|
|
127
|
-
return radioIsChecked;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// If the RadioGroup is controlled by a value prop...
|
|
131
|
-
if (radioGroupValue !== undefined) {
|
|
132
|
-
// The Radio is checked if its value matches the current value of the group.
|
|
133
|
-
return radioValue === radioGroupValue;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Otherwise, we are in an uncontrolled state. If present, our internal value is the defaultChecked prop,
|
|
137
|
-
// otherwise we are not checked.
|
|
138
|
-
return radioDefaultIsChecked ?? false;
|
|
139
|
-
})();
|
|
140
|
-
|
|
141
|
-
return {
|
|
142
|
-
name,
|
|
143
|
-
isControlled,
|
|
144
|
-
checked,
|
|
145
|
-
setGroupValue,
|
|
146
|
-
};
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
export { useRadioGroup, useRadioGroupName };
|