@saas-ui/forms 1.5.2 → 2.0.0-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/dist/ajv/index.d.ts +21 -2
- package/dist/ajv/index.js +31 -2
- package/dist/ajv/index.js.map +1 -1
- package/dist/ajv/index.mjs +25 -0
- package/dist/ajv/index.mjs.map +1 -0
- package/dist/index.d.ts +606 -19
- package/dist/index.js +1251 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1140 -0
- package/dist/index.mjs.map +1 -0
- package/dist/yup/index.d.ts +41 -2
- package/dist/yup/index.js +95 -2
- package/dist/yup/index.js.map +1 -1
- package/dist/yup/index.mjs +86 -0
- package/dist/yup/index.mjs.map +1 -0
- package/dist/zod/index.d.ts +38 -2
- package/dist/zod/index.js +95 -2
- package/dist/zod/index.js.map +1 -1
- package/dist/zod/index.mjs +85 -0
- package/dist/zod/index.mjs.map +1 -0
- package/package.json +18 -28
- package/src/array-field.tsx +20 -13
- package/src/auto-form.tsx +13 -19
- package/src/field-resolver.ts +1 -1
- package/src/field.tsx +4 -9
- package/src/fields.tsx +1 -3
- package/src/form.tsx +70 -26
- package/src/index.ts +3 -1
- package/src/input-right-button/index.ts +1 -0
- package/src/input-right-button/input-right-button.stories.tsx +47 -0
- package/src/input-right-button/input-right-button.test.tsx +12 -0
- package/src/input-right-button/input-right-button.tsx +26 -0
- package/src/layout.tsx +1 -1
- package/src/number-input/index.ts +1 -0
- package/src/number-input/number-input.stories.tsx +39 -0
- package/src/number-input/number-input.test.tsx +6 -0
- package/src/number-input/number-input.tsx +56 -0
- package/src/object-field.tsx +1 -1
- package/src/password-input/index.ts +1 -0
- package/src/password-input/password-input.stories.tsx +50 -0
- package/src/password-input/password-input.test.tsx +20 -0
- package/src/password-input/password-input.tsx +69 -0
- package/src/pin-input/index.ts +1 -0
- package/src/pin-input/pin-input.stories.tsx +38 -0
- package/src/pin-input/pin-input.test.tsx +6 -0
- package/src/pin-input/pin-input.tsx +50 -0
- package/src/radio/index.ts +1 -0
- package/src/radio/radio-input.stories.tsx +45 -0
- package/src/radio/radio-input.tsx +58 -0
- package/src/radio/radio.test.tsx +6 -0
- package/src/select/index.ts +2 -0
- package/src/select/native-select.tsx +42 -0
- package/src/select/select.stories.tsx +144 -0
- package/src/select/select.test.tsx +8 -0
- package/src/select/select.tsx +185 -0
- package/src/step-form.tsx +24 -13
- package/src/submit-button.tsx +32 -38
- package/src/use-step-form.tsx +1 -1
- package/ajv/package.json +0 -28
- package/dist/ajv/ajv-resolver.d.ts +0 -11
- package/dist/ajv/ajv-resolver.d.ts.map +0 -1
- package/dist/ajv/index.d.ts.map +0 -1
- package/dist/ajv/index.modern.mjs +0 -2
- package/dist/ajv/index.modern.mjs.map +0 -1
- package/dist/array-field.d.ts +0 -64
- package/dist/array-field.d.ts.map +0 -1
- package/dist/auto-form.d.ts +0 -32
- package/dist/auto-form.d.ts.map +0 -1
- package/dist/display-field.d.ts +0 -10
- package/dist/display-field.d.ts.map +0 -1
- package/dist/display-if.d.ts +0 -15
- package/dist/display-if.d.ts.map +0 -1
- package/dist/field-resolver.d.ts +0 -13
- package/dist/field-resolver.d.ts.map +0 -1
- package/dist/field.d.ts +0 -147
- package/dist/field.d.ts.map +0 -1
- package/dist/fields.d.ts +0 -9
- package/dist/fields.d.ts.map +0 -1
- package/dist/form.d.ts +0 -44
- package/dist/form.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.modern.mjs +0 -2
- package/dist/index.modern.mjs.map +0 -1
- package/dist/layout.d.ts +0 -14
- package/dist/layout.d.ts.map +0 -1
- package/dist/object-field.d.ts +0 -12
- package/dist/object-field.d.ts.map +0 -1
- package/dist/step-form.d.ts +0 -38
- package/dist/step-form.d.ts.map +0 -1
- package/dist/submit-button.d.ts +0 -20
- package/dist/submit-button.d.ts.map +0 -1
- package/dist/use-array-field.d.ts +0 -95
- package/dist/use-array-field.d.ts.map +0 -1
- package/dist/use-step-form.d.ts +0 -40
- package/dist/use-step-form.d.ts.map +0 -1
- package/dist/utils.d.ts +0 -3
- package/dist/utils.d.ts.map +0 -1
- package/dist/watch-field.d.ts +0 -11
- package/dist/watch-field.d.ts.map +0 -1
- package/dist/yup/index.d.ts.map +0 -1
- package/dist/yup/index.modern.mjs +0 -2
- package/dist/yup/index.modern.mjs.map +0 -1
- package/dist/yup/yup-resolver.d.ts +0 -29
- package/dist/yup/yup-resolver.d.ts.map +0 -1
- package/dist/zod/index.d.ts.map +0 -1
- package/dist/zod/index.modern.mjs +0 -2
- package/dist/zod/index.modern.mjs.map +0 -1
- package/dist/zod/zod-resolver.d.ts +0 -35
- package/dist/zod/zod-resolver.d.ts.map +0 -1
- package/yup/package.json +0 -26
- package/zod/package.json +0 -27
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Button,
|
|
5
|
+
ButtonProps,
|
|
6
|
+
forwardRef,
|
|
7
|
+
InputRightElement,
|
|
8
|
+
} from '@chakra-ui/react'
|
|
9
|
+
|
|
10
|
+
import { __DEV__ } from '@chakra-ui/utils'
|
|
11
|
+
|
|
12
|
+
export type InputRightButtonProps = ButtonProps
|
|
13
|
+
|
|
14
|
+
export const InputRightButton = forwardRef<InputRightButtonProps, 'div'>(
|
|
15
|
+
(props, ref) => {
|
|
16
|
+
return (
|
|
17
|
+
<InputRightElement w="auto" px="1" py="1" alignItems="stretch">
|
|
18
|
+
<Button ref={ref} height="auto" {...props} />
|
|
19
|
+
</InputRightElement>
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
InputRightButton.id = 'InputRightElement'
|
|
25
|
+
|
|
26
|
+
InputRightButton.displayName = 'InputRightButton'
|
package/src/layout.tsx
CHANGED
|
@@ -38,7 +38,7 @@ export const FormLayout = ({ children, ...props }: FormLayoutProps) => {
|
|
|
38
38
|
return (
|
|
39
39
|
<SimpleGrid
|
|
40
40
|
{...gridProps}
|
|
41
|
-
className={cx('
|
|
41
|
+
className={cx('sui-form__layout', props.className)}
|
|
42
42
|
>
|
|
43
43
|
{React.Children.map(children, (child) => {
|
|
44
44
|
if (React.isValidElement(child)) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './number-input'
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Container } from '@chakra-ui/react'
|
|
2
|
+
import * as React from 'react'
|
|
3
|
+
import { Story, Meta } from '@storybook/react'
|
|
4
|
+
|
|
5
|
+
import { NumberInput } from '../src'
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
title: 'Components/Forms/NumberInput',
|
|
9
|
+
decorators: [
|
|
10
|
+
(Story: any) => (
|
|
11
|
+
<Container mt="40px">
|
|
12
|
+
<Story />
|
|
13
|
+
</Container>
|
|
14
|
+
),
|
|
15
|
+
],
|
|
16
|
+
} as Meta
|
|
17
|
+
|
|
18
|
+
const Template: Story = (args) => <NumberInput aria-label="Number" {...args} />
|
|
19
|
+
|
|
20
|
+
export const Basic = Template.bind({})
|
|
21
|
+
Basic.args = {}
|
|
22
|
+
|
|
23
|
+
export const HideStepper = Template.bind({})
|
|
24
|
+
HideStepper.args = {
|
|
25
|
+
hideStepper: true,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const MinMax = Template.bind({})
|
|
29
|
+
MinMax.args = {
|
|
30
|
+
defaultValue: 5,
|
|
31
|
+
min: 0,
|
|
32
|
+
max: 10,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const WithFormatter = Template.bind({})
|
|
36
|
+
WithFormatter.args = {
|
|
37
|
+
format: (value) => `$${value}`, // use any currency formatter here
|
|
38
|
+
parse: (value) => value.replace('$', ''),
|
|
39
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
forwardRef,
|
|
5
|
+
NumberInput as ChakraNumberInput,
|
|
6
|
+
NumberInputField,
|
|
7
|
+
NumberInputStepper,
|
|
8
|
+
NumberIncrementStepper,
|
|
9
|
+
NumberDecrementStepper,
|
|
10
|
+
NumberInputProps as ChakraNumberInputProps,
|
|
11
|
+
} from '@chakra-ui/react'
|
|
12
|
+
import { __DEV__ } from '@chakra-ui/utils'
|
|
13
|
+
|
|
14
|
+
interface NumberInputOptions {
|
|
15
|
+
/**
|
|
16
|
+
* Hide the stepper.
|
|
17
|
+
*/
|
|
18
|
+
hideStepper?: boolean
|
|
19
|
+
/**
|
|
20
|
+
* Render a custom increment icon.
|
|
21
|
+
*/
|
|
22
|
+
incrementIcon?: React.ReactNode
|
|
23
|
+
/**
|
|
24
|
+
* Render a custom decrement icon.
|
|
25
|
+
*/
|
|
26
|
+
decrementIcon?: React.ReactNode
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface NumberInputProps
|
|
30
|
+
extends ChakraNumberInputProps,
|
|
31
|
+
NumberInputOptions {}
|
|
32
|
+
|
|
33
|
+
export const NumberInput = forwardRef<NumberInputProps, 'div'>((props, ref) => {
|
|
34
|
+
const { hideStepper, incrementIcon, decrementIcon, ...rest } = props
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<ChakraNumberInput {...rest} ref={ref}>
|
|
38
|
+
<NumberInputField />
|
|
39
|
+
|
|
40
|
+
{!hideStepper && (
|
|
41
|
+
<NumberInputStepper>
|
|
42
|
+
<NumberIncrementStepper children={incrementIcon} />
|
|
43
|
+
<NumberDecrementStepper children={decrementIcon} />
|
|
44
|
+
</NumberInputStepper>
|
|
45
|
+
)}
|
|
46
|
+
</ChakraNumberInput>
|
|
47
|
+
)
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
NumberInput.defaultProps = {
|
|
51
|
+
hideStepper: false,
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (__DEV__) {
|
|
55
|
+
NumberInput.displayName = 'NumberInput'
|
|
56
|
+
}
|
package/src/object-field.tsx
CHANGED
|
@@ -21,7 +21,7 @@ export interface ObjectFieldProps extends FieldProps {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export const FormLegend = (props: FormLabelProps) => {
|
|
24
|
-
const styles = useStyleConfig('
|
|
24
|
+
const styles = useStyleConfig('SuiFormLegend')
|
|
25
25
|
return <FormLabel as="legend" sx={styles} {...props} />
|
|
26
26
|
}
|
|
27
27
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './password-input'
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Container, FormControl, FormLabel, Icon } from '@chakra-ui/react'
|
|
2
|
+
import { Story } from '@storybook/react'
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
|
|
5
|
+
import { FiEye, FiEyeOff } from 'react-icons/fi'
|
|
6
|
+
|
|
7
|
+
import { PasswordInput } from './password-input'
|
|
8
|
+
|
|
9
|
+
export default {
|
|
10
|
+
title: 'Components/Forms/PasswordInput',
|
|
11
|
+
decorators: [
|
|
12
|
+
(Story: any) => (
|
|
13
|
+
<Container mt="40px">
|
|
14
|
+
<Story />
|
|
15
|
+
</Container>
|
|
16
|
+
),
|
|
17
|
+
],
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const Basic: Story = () => (
|
|
21
|
+
<FormControl>
|
|
22
|
+
<FormLabel>Password</FormLabel>
|
|
23
|
+
<PasswordInput name="password" placeholder="Password" />
|
|
24
|
+
</FormControl>
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
export const ReactIcons: Story = () => (
|
|
28
|
+
<>
|
|
29
|
+
<PasswordInput
|
|
30
|
+
name="password"
|
|
31
|
+
placeholder="Password"
|
|
32
|
+
viewIcon={<Icon as={FiEye} />}
|
|
33
|
+
viewOffIcon={<Icon as={FiEyeOff} />}
|
|
34
|
+
/>
|
|
35
|
+
</>
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
export const Variant: Story = () => (
|
|
39
|
+
<FormControl>
|
|
40
|
+
<FormLabel>Password</FormLabel>
|
|
41
|
+
<PasswordInput name="password" variant="flushed" />
|
|
42
|
+
</FormControl>
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
export const CustomWidth: Story = () => (
|
|
46
|
+
<FormControl>
|
|
47
|
+
<FormLabel>Password</FormLabel>
|
|
48
|
+
<PasswordInput name="password" width="200px" />
|
|
49
|
+
</FormControl>
|
|
50
|
+
)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
|
|
3
|
+
import { render, act, fireEvent, testStories } from '@saas-ui/test-utils'
|
|
4
|
+
import * as stories from './password-input.stories'
|
|
5
|
+
|
|
6
|
+
const { Basic } = testStories<typeof stories>(stories)
|
|
7
|
+
|
|
8
|
+
test('should render correct aria labels', async () => {
|
|
9
|
+
const { getByText, getByLabelText, getByPlaceholderText, getByRole } = render(
|
|
10
|
+
<Basic />
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
const toggle = getByLabelText('Show password')
|
|
14
|
+
|
|
15
|
+
await act(async () => {
|
|
16
|
+
fireEvent.click(toggle)
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
expect(toggle).toHaveAttribute('aria-label', 'Hide password')
|
|
20
|
+
})
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import React, { useState } from 'react'
|
|
2
|
+
|
|
3
|
+
import { forwardRef, InputGroup, Input, InputProps } from '@chakra-ui/react'
|
|
4
|
+
import { __DEV__ } from '@chakra-ui/utils'
|
|
5
|
+
import { ViewIcon, ViewOffIcon } from '@chakra-ui/icons'
|
|
6
|
+
|
|
7
|
+
import { InputRightButton } from '../input-right-button'
|
|
8
|
+
|
|
9
|
+
interface PasswordOptions {
|
|
10
|
+
viewIcon?: React.ReactNode
|
|
11
|
+
viewOffIcon?: React.ReactNode
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface PasswordInputProps extends InputProps, PasswordOptions {}
|
|
15
|
+
|
|
16
|
+
export const PasswordInput = forwardRef<PasswordInputProps, 'div'>(
|
|
17
|
+
(props, ref) => {
|
|
18
|
+
const {
|
|
19
|
+
viewIcon,
|
|
20
|
+
viewOffIcon,
|
|
21
|
+
autoComplete,
|
|
22
|
+
w,
|
|
23
|
+
width,
|
|
24
|
+
size,
|
|
25
|
+
variant,
|
|
26
|
+
...inputProps
|
|
27
|
+
} = props
|
|
28
|
+
const [show, setShow] = useState(false)
|
|
29
|
+
const handleClick = () => setShow(!show)
|
|
30
|
+
|
|
31
|
+
const label = show ? 'Hide password' : 'Show password'
|
|
32
|
+
|
|
33
|
+
let icon
|
|
34
|
+
if (show) {
|
|
35
|
+
icon = viewIcon || <ViewIcon />
|
|
36
|
+
} else {
|
|
37
|
+
icon = viewOffIcon || <ViewOffIcon />
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const groupProps = {
|
|
41
|
+
width: w || width,
|
|
42
|
+
size,
|
|
43
|
+
variant,
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<InputGroup {...groupProps}>
|
|
48
|
+
<Input
|
|
49
|
+
{...inputProps}
|
|
50
|
+
ref={ref}
|
|
51
|
+
type={show ? 'text' : 'password'}
|
|
52
|
+
autoComplete={show ? 'off' : autoComplete}
|
|
53
|
+
/>
|
|
54
|
+
|
|
55
|
+
<InputRightButton
|
|
56
|
+
onClick={handleClick}
|
|
57
|
+
aria-label={label}
|
|
58
|
+
variant="ghost"
|
|
59
|
+
>
|
|
60
|
+
{icon}
|
|
61
|
+
</InputRightButton>
|
|
62
|
+
</InputGroup>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
if (__DEV__) {
|
|
68
|
+
PasswordInput.displayName = 'PasswordInput'
|
|
69
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './pin-input'
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Container } from '@chakra-ui/react'
|
|
2
|
+
import * as React from 'react'
|
|
3
|
+
|
|
4
|
+
import { ComponentStory } from '@storybook/react'
|
|
5
|
+
|
|
6
|
+
import { PinInput } from './pin-input'
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
title: 'Components/Forms/PinInput',
|
|
10
|
+
decorators: [
|
|
11
|
+
(Story: any) => (
|
|
12
|
+
<Container mt="40px">
|
|
13
|
+
<Story />
|
|
14
|
+
</Container>
|
|
15
|
+
),
|
|
16
|
+
],
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const Template: ComponentStory<typeof PinInput> = (args) => (
|
|
20
|
+
<PinInput {...args} />
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
export const Basic = Template.bind({})
|
|
24
|
+
Basic.args = {
|
|
25
|
+
/**
|
|
26
|
+
* Description
|
|
27
|
+
*/
|
|
28
|
+
name: 'pin-input',
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const PinLength = Template.bind({})
|
|
32
|
+
PinLength.args = {
|
|
33
|
+
/**
|
|
34
|
+
* Description
|
|
35
|
+
*/
|
|
36
|
+
name: 'pin-input',
|
|
37
|
+
pinLength: 10,
|
|
38
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import {
|
|
3
|
+
forwardRef,
|
|
4
|
+
PinInput as ChakraPinInput,
|
|
5
|
+
UsePinInputProps,
|
|
6
|
+
PinInputField,
|
|
7
|
+
HStack,
|
|
8
|
+
SystemProps,
|
|
9
|
+
} from '@chakra-ui/react'
|
|
10
|
+
|
|
11
|
+
import { __DEV__ } from '@chakra-ui/utils'
|
|
12
|
+
|
|
13
|
+
interface PinInputOptions {
|
|
14
|
+
/**
|
|
15
|
+
* The pin length.
|
|
16
|
+
*/
|
|
17
|
+
pinLength?: number
|
|
18
|
+
/**
|
|
19
|
+
* Spacing between the inputs.
|
|
20
|
+
*/
|
|
21
|
+
spacing?: SystemProps['margin']
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface PinInputProps extends UsePinInputProps, PinInputOptions {}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @deprecated
|
|
28
|
+
*/
|
|
29
|
+
export const PinInput = forwardRef<PinInputProps, 'div'>((props, ref) => {
|
|
30
|
+
const { pinLength = 4, spacing, ...inputProps } = props
|
|
31
|
+
|
|
32
|
+
const inputs: React.ReactNode[] = []
|
|
33
|
+
for (let i = 0; i < pinLength; i++) {
|
|
34
|
+
inputs.push(<PinInputField key={i} ref={ref} />)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<HStack spacing={spacing}>
|
|
39
|
+
<ChakraPinInput {...inputProps}>{inputs}</ChakraPinInput>
|
|
40
|
+
</HStack>
|
|
41
|
+
)
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
PinInput.defaultProps = {
|
|
45
|
+
pinLength: 4,
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (__DEV__) {
|
|
49
|
+
PinInput.displayName = 'PinInput'
|
|
50
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './radio-input'
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Container } from '@chakra-ui/react'
|
|
2
|
+
import * as React from 'react'
|
|
3
|
+
|
|
4
|
+
import { ComponentStory } from '@storybook/react'
|
|
5
|
+
|
|
6
|
+
import { RadioInput } from './'
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
title: 'Components/Forms/Radio',
|
|
10
|
+
decorators: [
|
|
11
|
+
(Story: any) => (
|
|
12
|
+
<Container mt="40px">
|
|
13
|
+
<Story />
|
|
14
|
+
</Container>
|
|
15
|
+
),
|
|
16
|
+
],
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const options = [
|
|
20
|
+
{
|
|
21
|
+
value: '1',
|
|
22
|
+
label: 'Option 1',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
value: '2',
|
|
26
|
+
label: 'Option 2',
|
|
27
|
+
},
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
const Template: ComponentStory<typeof RadioInput> = (args) => (
|
|
31
|
+
<RadioInput {...args} />
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
export const Basic = Template.bind({})
|
|
35
|
+
Basic.args = {
|
|
36
|
+
name: 'radio',
|
|
37
|
+
options,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const Direction = Template.bind({})
|
|
41
|
+
Direction.args = {
|
|
42
|
+
name: 'radio',
|
|
43
|
+
direction: 'row',
|
|
44
|
+
options,
|
|
45
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
forwardRef,
|
|
5
|
+
Stack,
|
|
6
|
+
RadioGroup,
|
|
7
|
+
RadioGroupProps,
|
|
8
|
+
Radio,
|
|
9
|
+
RadioProps,
|
|
10
|
+
SystemProps,
|
|
11
|
+
StackDirection,
|
|
12
|
+
} from '@chakra-ui/react'
|
|
13
|
+
import { __DEV__ } from '@chakra-ui/utils'
|
|
14
|
+
|
|
15
|
+
interface Option extends RadioProps {
|
|
16
|
+
value: string
|
|
17
|
+
label?: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface RadioInputOptions {
|
|
21
|
+
options: Option[]
|
|
22
|
+
spacing?: SystemProps['margin']
|
|
23
|
+
direction?: StackDirection
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface RadioInputProps
|
|
27
|
+
extends Omit<RadioGroupProps, 'children'>,
|
|
28
|
+
RadioInputOptions {}
|
|
29
|
+
|
|
30
|
+
export const RadioInput = forwardRef<RadioInputProps, 'div'>(
|
|
31
|
+
({ options, spacing, direction, ...props }, ref) => {
|
|
32
|
+
const { onBlur, onChange, ...groupProps } = props
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<RadioGroup onChange={onChange} {...groupProps}>
|
|
36
|
+
<Stack spacing={spacing} direction={direction}>
|
|
37
|
+
{options.map(({ value, label, ...radioProps }, i) => {
|
|
38
|
+
return (
|
|
39
|
+
<Radio
|
|
40
|
+
key={i}
|
|
41
|
+
onBlur={onBlur}
|
|
42
|
+
value={value}
|
|
43
|
+
ref={ref}
|
|
44
|
+
{...radioProps}
|
|
45
|
+
>
|
|
46
|
+
{label || value}
|
|
47
|
+
</Radio>
|
|
48
|
+
)
|
|
49
|
+
})}
|
|
50
|
+
</Stack>
|
|
51
|
+
</RadioGroup>
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
if (__DEV__) {
|
|
57
|
+
RadioInput.displayName = 'RadioInput'
|
|
58
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
forwardRef,
|
|
5
|
+
Select as ChakraSelect,
|
|
6
|
+
SelectProps as ChakraSelectProps,
|
|
7
|
+
} from '@chakra-ui/react'
|
|
8
|
+
import { __DEV__ } from '@chakra-ui/utils'
|
|
9
|
+
|
|
10
|
+
interface Option {
|
|
11
|
+
value: string
|
|
12
|
+
label?: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface NativeSelectOptions {
|
|
16
|
+
options?: Option[]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface NativeSelectProps
|
|
20
|
+
extends ChakraSelectProps,
|
|
21
|
+
NativeSelectOptions {}
|
|
22
|
+
|
|
23
|
+
export const NativeSelect = forwardRef<NativeSelectProps, 'select'>(
|
|
24
|
+
({ options, children, ...props }, ref) => {
|
|
25
|
+
return (
|
|
26
|
+
<ChakraSelect ref={ref} {...props}>
|
|
27
|
+
{children ||
|
|
28
|
+
options?.map(({ value, label }) => {
|
|
29
|
+
return (
|
|
30
|
+
<option key={value} value={value}>
|
|
31
|
+
{label || value}
|
|
32
|
+
</option>
|
|
33
|
+
)
|
|
34
|
+
})}
|
|
35
|
+
</ChakraSelect>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
if (__DEV__) {
|
|
41
|
+
NativeSelect.displayName = 'NativeSelect'
|
|
42
|
+
}
|