@charcoal-ui/react 3.0.0-beta.3 → 3.0.0-beta.4
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/_lib/compat.d.ts +18 -0
- package/dist/_lib/compat.d.ts.map +1 -1
- package/dist/_lib/index.d.ts +7 -0
- package/dist/_lib/index.d.ts.map +1 -1
- package/dist/components/Checkbox/index.d.ts +1 -0
- package/dist/components/Checkbox/index.d.ts.map +1 -1
- package/dist/components/Checkbox/index.story.d.ts +1 -0
- package/dist/components/Checkbox/index.story.d.ts.map +1 -1
- package/dist/components/DropdownSelector/index.d.ts.map +1 -1
- package/dist/components/LoadingSpinner/index.d.ts +8 -6
- package/dist/components/LoadingSpinner/index.d.ts.map +1 -1
- package/dist/components/LoadingSpinner/index.story.d.ts +1 -2
- package/dist/components/LoadingSpinner/index.story.d.ts.map +1 -1
- package/dist/components/Modal/index.d.ts +17 -26
- package/dist/components/Modal/index.d.ts.map +1 -1
- package/dist/components/Modal/index.story.d.ts +12 -2
- package/dist/components/Modal/index.story.d.ts.map +1 -1
- package/dist/components/MultiSelect/index.d.ts +14 -1
- package/dist/components/MultiSelect/index.d.ts.map +1 -1
- package/dist/components/MultiSelect/index.story.d.ts +14 -2
- package/dist/components/MultiSelect/index.story.d.ts.map +1 -1
- package/dist/components/Radio/index.d.ts +12 -5
- package/dist/components/Radio/index.d.ts.map +1 -1
- package/dist/components/Radio/index.story.d.ts +10 -6
- package/dist/components/Radio/index.story.d.ts.map +1 -1
- package/dist/components/SegmentedControl/index.d.ts +1 -0
- package/dist/components/SegmentedControl/index.d.ts.map +1 -1
- package/dist/components/Switch/index.d.ts +2 -1
- package/dist/components/Switch/index.d.ts.map +1 -1
- package/dist/components/Switch/index.story.d.ts +1 -2
- package/dist/components/Switch/index.story.d.ts.map +1 -1
- package/dist/components/TextArea/index.d.ts +3 -10
- package/dist/components/TextArea/index.d.ts.map +1 -1
- package/dist/components/TextField/TextField.story.d.ts +4 -5
- package/dist/components/TextField/TextField.story.d.ts.map +1 -1
- package/dist/components/TextField/index.d.ts +6 -29
- package/dist/components/TextField/index.d.ts.map +1 -1
- package/dist/components/TextField/index.story.d.ts +5 -4
- package/dist/components/TextField/index.story.d.ts.map +1 -1
- package/dist/index.cjs.js +636 -594
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +604 -563
- package/dist/index.esm.js.map +1 -1
- package/package.json +6 -6
- package/src/_lib/compat.ts +19 -0
- package/src/_lib/index.ts +23 -0
- package/src/components/Checkbox/index.story.tsx +1 -0
- package/src/components/Checkbox/index.tsx +2 -1
- package/src/components/DropdownSelector/DropdownMenuItem.tsx +1 -1
- package/src/components/DropdownSelector/ListItem/index.story.tsx +1 -1
- package/src/components/DropdownSelector/ListItem/index.tsx +1 -1
- package/src/components/DropdownSelector/MenuItem/index.tsx +0 -1
- package/src/components/DropdownSelector/MenuItem/internals/useMenuItemHandleKeyDown.tsx +1 -1
- package/src/components/DropdownSelector/MenuItemGroup/index.tsx +0 -1
- package/src/components/DropdownSelector/MenuList/index.story.tsx +0 -1
- package/src/components/DropdownSelector/MenuList/index.tsx +1 -1
- package/src/components/DropdownSelector/MenuList/internals/getValuesRecursive.tsx +1 -1
- package/src/components/DropdownSelector/Popover/index.story.tsx +1 -1
- package/src/components/DropdownSelector/Popover/index.tsx +1 -1
- package/src/components/DropdownSelector/index.tsx +16 -14
- package/src/components/DropdownSelector/utils/findPreviewRecursive.tsx +2 -1
- package/src/components/LoadingSpinner/index.story.tsx +7 -1
- package/src/components/LoadingSpinner/index.tsx +27 -11
- package/src/components/Modal/index.tsx +18 -12
- package/src/components/MultiSelect/index.story.tsx +16 -4
- package/src/components/MultiSelect/index.tsx +70 -60
- package/src/components/Radio/index.story.tsx +7 -8
- package/src/components/Radio/index.test.tsx +3 -3
- package/src/components/Radio/index.tsx +23 -23
- package/src/components/SegmentedControl/index.tsx +6 -1
- package/src/components/Switch/index.tsx +37 -32
- package/src/components/TextArea/TextArea.story.tsx +61 -0
- package/src/components/TextArea/index.tsx +246 -0
- package/src/components/TextField/{index.story.tsx → TextField.story.tsx} +6 -28
- package/src/components/TextField/index.tsx +146 -371
- package/src/index.ts +1 -2
- package/dist/components/DropdownSelector/OptionItem.d.ts +0 -7
- package/dist/components/DropdownSelector/OptionItem.d.ts.map +0 -1
- package/dist/components/DropdownSelector/utils/focusIfHTMLLIElement.d.ts +0 -6
- package/dist/components/DropdownSelector/utils/focusIfHTMLLIElement.d.ts.map +0 -1
- package/dist/components/DropdownSelector/utils/handleFocusByKeyBoard.d.ts +0 -6
- package/dist/components/DropdownSelector/utils/handleFocusByKeyBoard.d.ts.map +0 -1
- package/dist/types/CustomJSXElement.d.ts +0 -3
- package/dist/types/CustomJSXElement.d.ts.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@charcoal-ui/react",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.4",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "./dist/index.cjs.js",
|
|
6
6
|
"module": "./dist/index.esm.js",
|
|
@@ -49,10 +49,10 @@
|
|
|
49
49
|
"typescript": "^4.9.5"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@charcoal-ui/icons": "^3.0.0-beta.
|
|
53
|
-
"@charcoal-ui/styled": "^3.0.0-beta.
|
|
54
|
-
"@charcoal-ui/theme": "^3.0.0-beta.
|
|
55
|
-
"@charcoal-ui/utils": "^3.0.0-beta.
|
|
52
|
+
"@charcoal-ui/icons": "^3.0.0-beta.4",
|
|
53
|
+
"@charcoal-ui/styled": "^3.0.0-beta.4",
|
|
54
|
+
"@charcoal-ui/theme": "^3.0.0-beta.4",
|
|
55
|
+
"@charcoal-ui/utils": "^3.0.0-beta.4",
|
|
56
56
|
"@react-aria/button": "^3.7.0",
|
|
57
57
|
"@react-aria/checkbox": "^3.8.0",
|
|
58
58
|
"@react-aria/dialog": "^3.5.0",
|
|
@@ -88,5 +88,5 @@
|
|
|
88
88
|
"url": "https://github.com/pixiv/charcoal.git",
|
|
89
89
|
"directory": "packages/react"
|
|
90
90
|
},
|
|
91
|
-
"gitHead": "
|
|
91
|
+
"gitHead": "47ec80abac2b87e78417a06db4943b3cb0408b78"
|
|
92
92
|
}
|
package/src/_lib/compat.ts
CHANGED
|
@@ -10,3 +10,22 @@ import * as React from 'react'
|
|
|
10
10
|
* `Type alias 'Interpolation' circularly references itself. ts(2456)`
|
|
11
11
|
*/
|
|
12
12
|
export type Story<P> = React.ComponentType<P> & { args?: P }
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* react-ariaの`useTextField()`は、<textarea>をサポートするにも関わらず、
|
|
16
|
+
* `React.KeyboardEvent<HTMLInputElement>`しか想定していないイベントハンドラがいくつかある
|
|
17
|
+
* ↓ が直るまで、以下のイベントハンドラの型は信用しない(本当は`Element`ではなく`HTMLTextAreaElement`とかにしたい)
|
|
18
|
+
*
|
|
19
|
+
* @see https://github.com/adobe/react-spectrum/issues/4662
|
|
20
|
+
*/
|
|
21
|
+
export interface ReactAreaUseTextFieldCompat<E = Element> {
|
|
22
|
+
readonly onCopy?: React.ClipboardEventHandler<E>
|
|
23
|
+
readonly onPaste?: React.ClipboardEventHandler<E>
|
|
24
|
+
readonly onCut?: React.ClipboardEventHandler<E>
|
|
25
|
+
readonly onCompositionStart?: React.CompositionEventHandler<E>
|
|
26
|
+
readonly onCompositionEnd?: React.CompositionEventHandler<E>
|
|
27
|
+
readonly onCompositionUpdate?: React.CompositionEventHandler<E>
|
|
28
|
+
readonly onSelect?: React.ReactEventHandler<E>
|
|
29
|
+
readonly onBeforeInput?: React.FormEventHandler<E>
|
|
30
|
+
readonly onInput?: React.FormEventHandler<E>
|
|
31
|
+
}
|
package/src/_lib/index.ts
CHANGED
|
@@ -33,3 +33,26 @@ export function unreachable(value?: never): never {
|
|
|
33
33
|
: `unreachable (${JSON.stringify(value)})`
|
|
34
34
|
)
|
|
35
35
|
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 複数のrefをマージする。
|
|
39
|
+
*
|
|
40
|
+
* forwardRefで受け取ったrefと、コンポーネント内で定義したrefを同じ要素につけたいケースなどで使う
|
|
41
|
+
*/
|
|
42
|
+
export function mergeRefs<T>(...refs: React.Ref<T>[]): React.RefCallback<T> {
|
|
43
|
+
return (value) => {
|
|
44
|
+
for (const ref of refs) {
|
|
45
|
+
if (typeof ref === 'function') {
|
|
46
|
+
ref(value)
|
|
47
|
+
} else if (ref !== null) {
|
|
48
|
+
;(ref as React.MutableRefObject<T | null>).current = value
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function countCodePointsInString(string: string) {
|
|
55
|
+
// [...string] とするとproduction buildで動かなくなる
|
|
56
|
+
// cf. https://twitter.com/f_subal/status/1497214727511891972
|
|
57
|
+
return Array.from(string).length
|
|
58
|
+
}
|
|
@@ -21,6 +21,7 @@ type CheckboxLabelProps =
|
|
|
21
21
|
export type CheckboxProps = CheckboxLabelProps & {
|
|
22
22
|
readonly id?: string
|
|
23
23
|
readonly name?: string
|
|
24
|
+
readonly className?: string
|
|
24
25
|
|
|
25
26
|
readonly checked?: boolean
|
|
26
27
|
readonly defaultChecked?: boolean
|
|
@@ -53,7 +54,7 @@ const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
|
|
|
53
54
|
const isDisabled = (props.disabled ?? false) || (props.readonly ?? false)
|
|
54
55
|
|
|
55
56
|
return (
|
|
56
|
-
<InputRoot aria-disabled={isDisabled}>
|
|
57
|
+
<InputRoot aria-disabled={isDisabled} className={props.className}>
|
|
57
58
|
<CheckboxRoot>
|
|
58
59
|
<CheckboxInput type="checkbox" {...inputProps} />
|
|
59
60
|
<CheckboxInputOverlay aria-hidden={true} checked={inputProps.checked}>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import styled from 'styled-components'
|
|
2
2
|
import MenuItem, { MenuItemProps } from './MenuItem'
|
|
3
3
|
import { MenuListContext } from './MenuList/MenuListContext'
|
|
4
|
-
import
|
|
4
|
+
import { useContext } from 'react'
|
|
5
5
|
import { theme } from '../../styled'
|
|
6
6
|
import Icon from '../Icon'
|
|
7
7
|
|
|
@@ -56,22 +56,24 @@ export default function DropdownSelector(props: DropdownSelectorProps) {
|
|
|
56
56
|
</DropdownButtonText>
|
|
57
57
|
<DropdownButtonIcon name="16/Menu" />
|
|
58
58
|
</DropdownButton>
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
>
|
|
65
|
-
<MenuList
|
|
59
|
+
{isOpen && (
|
|
60
|
+
<DropdownPopover
|
|
61
|
+
isOpen={isOpen}
|
|
62
|
+
onClose={() => setIsOpen(false)}
|
|
63
|
+
triggerRef={triggerRef}
|
|
66
64
|
value={props.value}
|
|
67
|
-
onChange={(v) => {
|
|
68
|
-
props.onChange(v)
|
|
69
|
-
setIsOpen(false)
|
|
70
|
-
}}
|
|
71
65
|
>
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
66
|
+
<MenuList
|
|
67
|
+
value={props.value}
|
|
68
|
+
onChange={(v) => {
|
|
69
|
+
props.onChange(v)
|
|
70
|
+
setIsOpen(false)
|
|
71
|
+
}}
|
|
72
|
+
>
|
|
73
|
+
{props.children}
|
|
74
|
+
</MenuList>
|
|
75
|
+
</DropdownPopover>
|
|
76
|
+
)}
|
|
75
77
|
{props.assistiveText !== undefined && (
|
|
76
78
|
<AssertiveText invalid={props.invalid}>
|
|
77
79
|
{props.assistiveText}
|
|
@@ -21,9 +21,15 @@ export function Basic() {
|
|
|
21
21
|
const size = number('size', 48)
|
|
22
22
|
const padding = number('padding', 16)
|
|
23
23
|
const transparent = boolean('transparent', false)
|
|
24
|
+
const className = text('className', 'basic')
|
|
24
25
|
|
|
25
26
|
return (
|
|
26
|
-
<LoadingSpinner
|
|
27
|
+
<LoadingSpinner
|
|
28
|
+
size={size}
|
|
29
|
+
padding={padding}
|
|
30
|
+
transparent={transparent}
|
|
31
|
+
className={className}
|
|
32
|
+
/>
|
|
27
33
|
)
|
|
28
34
|
}
|
|
29
35
|
|
|
@@ -1,19 +1,35 @@
|
|
|
1
|
-
import { forwardRef, useImperativeHandle, useRef } from 'react'
|
|
1
|
+
import { forwardRef, useImperativeHandle, useRef, memo } from 'react'
|
|
2
2
|
import styled, { keyframes } from 'styled-components'
|
|
3
3
|
import { theme } from '../../styled'
|
|
4
4
|
|
|
5
|
-
export
|
|
6
|
-
size
|
|
7
|
-
padding
|
|
8
|
-
transparent
|
|
9
|
-
|
|
10
|
-
return (
|
|
11
|
-
<LoadingSpinnerRoot size={size} padding={padding} transparent={transparent}>
|
|
12
|
-
<LoadingSpinnerIcon />
|
|
13
|
-
</LoadingSpinnerRoot>
|
|
14
|
-
)
|
|
5
|
+
export type LoadingSpinnerProps = {
|
|
6
|
+
readonly size?: number
|
|
7
|
+
readonly padding?: number
|
|
8
|
+
readonly transparent?: boolean
|
|
9
|
+
readonly className?: string
|
|
15
10
|
}
|
|
16
11
|
|
|
12
|
+
const LoadingSpinner = forwardRef<HTMLDivElement, LoadingSpinnerProps>(
|
|
13
|
+
function LoadingSpinnerInner(
|
|
14
|
+
{ size = 48, padding = 16, transparent = false, className },
|
|
15
|
+
ref
|
|
16
|
+
) {
|
|
17
|
+
return (
|
|
18
|
+
<LoadingSpinnerRoot
|
|
19
|
+
size={size}
|
|
20
|
+
padding={padding}
|
|
21
|
+
transparent={transparent}
|
|
22
|
+
className={className}
|
|
23
|
+
ref={ref}
|
|
24
|
+
>
|
|
25
|
+
<LoadingSpinnerIcon />
|
|
26
|
+
</LoadingSpinnerRoot>
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
export default memo(LoadingSpinner)
|
|
32
|
+
|
|
17
33
|
const LoadingSpinnerRoot = styled.div.attrs({ role: 'progressbar' })<{
|
|
18
34
|
size: number
|
|
19
35
|
padding: number
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useContext,
|
|
1
|
+
import { useContext, forwardRef, memo } from 'react'
|
|
2
2
|
import * as React from 'react'
|
|
3
3
|
import {
|
|
4
4
|
AriaModalOverlayProps,
|
|
@@ -18,6 +18,7 @@ import { useMedia } from '@charcoal-ui/styled'
|
|
|
18
18
|
import { animated, useTransition, easings } from 'react-spring'
|
|
19
19
|
import Button, { ButtonProps } from '../Button'
|
|
20
20
|
import IconButton from '../IconButton'
|
|
21
|
+
import { useObjectRef } from '@react-aria/utils'
|
|
21
22
|
|
|
22
23
|
type BottomSheet = boolean | 'full'
|
|
23
24
|
type Size = 'S' | 'M' | 'L'
|
|
@@ -31,6 +32,7 @@ export type ModalProps = AriaModalOverlayProps &
|
|
|
31
32
|
bottomSheet?: BottomSheet
|
|
32
33
|
isOpen: boolean
|
|
33
34
|
onClose: () => void
|
|
35
|
+
className?: string
|
|
34
36
|
|
|
35
37
|
/**
|
|
36
38
|
* https://github.com/adobe/react-spectrum/issues/3787
|
|
@@ -56,31 +58,32 @@ const DEFAULT_Z_INDEX = 10
|
|
|
56
58
|
*
|
|
57
59
|
* <OverlayProvider>
|
|
58
60
|
* <App>
|
|
59
|
-
* <Modal isOpen={state.isOpen} onClose={() => state.close()} isDismissable>
|
|
61
|
+
* <Modal title="Title" isOpen={state.isOpen} onClose={() => state.close()} isDismissable>
|
|
60
62
|
* <ModalHeader />
|
|
61
|
-
* <ModalBody
|
|
62
|
-
*
|
|
63
|
+
* <ModalBody>
|
|
64
|
+
* ...
|
|
65
|
+
* <ModalButtons>...</ModalButtons>
|
|
66
|
+
* </ModalBody>
|
|
63
67
|
* </Modal>
|
|
64
68
|
* </App>
|
|
65
69
|
* </OverlayProvider>
|
|
66
70
|
* ```
|
|
67
71
|
*/
|
|
68
|
-
|
|
69
|
-
children,
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
...props
|
|
73
|
-
}: ModalProps) {
|
|
72
|
+
const Modal = forwardRef<HTMLDivElement, ModalProps>(function ModalInner(
|
|
73
|
+
{ children, zIndex = DEFAULT_Z_INDEX, portalContainer, ...props },
|
|
74
|
+
external
|
|
75
|
+
) {
|
|
74
76
|
const {
|
|
75
77
|
title,
|
|
76
78
|
size = 'M',
|
|
77
79
|
bottomSheet = false,
|
|
78
80
|
isDismissable,
|
|
79
81
|
onClose,
|
|
82
|
+
className,
|
|
80
83
|
isOpen = false,
|
|
81
84
|
} = props
|
|
82
85
|
|
|
83
|
-
const ref =
|
|
86
|
+
const ref = useObjectRef<HTMLDivElement>(external)
|
|
84
87
|
const { overlayProps, underlayProps } = useOverlay(props, ref)
|
|
85
88
|
|
|
86
89
|
const { modalProps } = useModalOverlay(
|
|
@@ -146,6 +149,7 @@ export default function Modal({
|
|
|
146
149
|
style={transitionEnabled ? { transform } : {}}
|
|
147
150
|
size={size}
|
|
148
151
|
bottomSheet={bottomSheet}
|
|
152
|
+
className={className}
|
|
149
153
|
>
|
|
150
154
|
<ModalContext.Provider
|
|
151
155
|
value={{ titleProps, title, close: onClose, showDismiss }}
|
|
@@ -166,7 +170,9 @@ export default function Modal({
|
|
|
166
170
|
</Overlay>
|
|
167
171
|
)
|
|
168
172
|
)
|
|
169
|
-
}
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
export default memo(Modal)
|
|
170
176
|
|
|
171
177
|
const ModalContext = React.createContext<{
|
|
172
178
|
titleProps: React.HTMLAttributes<HTMLElement>
|
|
@@ -55,6 +55,7 @@ type Props = {
|
|
|
55
55
|
readonly?: boolean
|
|
56
56
|
invalid?: boolean
|
|
57
57
|
variant?: 'default' | 'overlay'
|
|
58
|
+
className?: string
|
|
58
59
|
}
|
|
59
60
|
|
|
60
61
|
const StyledMultiSelectGroup = styled(MultiSelectGroup)`
|
|
@@ -72,6 +73,7 @@ const Template: Story<Props> = ({
|
|
|
72
73
|
readonly,
|
|
73
74
|
invalid,
|
|
74
75
|
variant,
|
|
76
|
+
className,
|
|
75
77
|
}) => {
|
|
76
78
|
return (
|
|
77
79
|
<StyledMultiSelectGroup
|
|
@@ -83,11 +85,15 @@ const Template: Story<Props> = ({
|
|
|
83
85
|
readonly,
|
|
84
86
|
invalid,
|
|
85
87
|
}}
|
|
86
|
-
className={''}
|
|
87
88
|
selected={selected ? ['選択肢1', '選択肢3'] : []}
|
|
88
89
|
>
|
|
89
90
|
{[1, 2, 3, 4].map((idx) => (
|
|
90
|
-
<MultiSelect
|
|
91
|
+
<MultiSelect
|
|
92
|
+
value={`選択肢${idx}`}
|
|
93
|
+
variant={variant}
|
|
94
|
+
key={idx}
|
|
95
|
+
className={className}
|
|
96
|
+
>
|
|
91
97
|
選択肢{idx}
|
|
92
98
|
</MultiSelect>
|
|
93
99
|
))}
|
|
@@ -114,10 +120,11 @@ type PlaygroundProps = {
|
|
|
114
120
|
disabled?: boolean
|
|
115
121
|
readonly?: boolean
|
|
116
122
|
invalid?: boolean
|
|
123
|
+
className?: string
|
|
117
124
|
variant?: 'default' | 'overlay'
|
|
118
125
|
}
|
|
119
126
|
|
|
120
|
-
export const Playground: Story<PlaygroundProps> = (props) => {
|
|
127
|
+
export const Playground: Story<PlaygroundProps> = ({ className, ...props }) => {
|
|
121
128
|
const [selected, setSelected] = useState<string[]>([])
|
|
122
129
|
|
|
123
130
|
return (
|
|
@@ -127,7 +134,12 @@ export const Playground: Story<PlaygroundProps> = (props) => {
|
|
|
127
134
|
onChange={setSelected}
|
|
128
135
|
>
|
|
129
136
|
{[1, 2, 3, 4].map((idx) => (
|
|
130
|
-
<MultiSelect
|
|
137
|
+
<MultiSelect
|
|
138
|
+
value={`選択肢${idx}`}
|
|
139
|
+
variant={props.variant}
|
|
140
|
+
key={idx}
|
|
141
|
+
className={className}
|
|
142
|
+
>
|
|
131
143
|
選択肢{idx}
|
|
132
144
|
</MultiSelect>
|
|
133
145
|
))}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ChangeEvent, useCallback, useContext } from 'react'
|
|
1
|
+
import { ChangeEvent, useCallback, useContext, forwardRef, memo } from 'react'
|
|
2
2
|
import * as React from 'react'
|
|
3
3
|
import styled, { css } from 'styled-components'
|
|
4
4
|
import warning from 'warning'
|
|
@@ -11,70 +11,80 @@ export type MultiSelectProps = React.PropsWithChildren<{
|
|
|
11
11
|
value: string
|
|
12
12
|
disabled?: boolean
|
|
13
13
|
variant?: 'default' | 'overlay'
|
|
14
|
+
className?: string
|
|
14
15
|
onChange?: (payload: { value: string; selected: boolean }) => void
|
|
15
16
|
}>
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
selected,
|
|
27
|
-
disabled: parentDisabled,
|
|
28
|
-
readonly,
|
|
29
|
-
invalid,
|
|
30
|
-
onChange: parentOnChange,
|
|
31
|
-
} = useContext(MultiSelectGroupContext)
|
|
32
|
-
|
|
33
|
-
warning(
|
|
34
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
35
|
-
name !== undefined,
|
|
36
|
-
`"name" is not Provided for <MultiSelect>. Perhaps you forgot to wrap with <MultiSelectGroup> ?`
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
const isSelected = selected.includes(value)
|
|
40
|
-
const isDisabled = disabled || parentDisabled || readonly
|
|
41
|
-
|
|
42
|
-
const handleChange = useCallback(
|
|
43
|
-
(event: ChangeEvent<HTMLInputElement>) => {
|
|
44
|
-
if (!(event.currentTarget instanceof HTMLInputElement)) {
|
|
45
|
-
return
|
|
46
|
-
}
|
|
47
|
-
if (onChange) onChange({ value, selected: event.currentTarget.checked })
|
|
48
|
-
parentOnChange({ value, selected: event.currentTarget.checked })
|
|
18
|
+
const MultiSelect = forwardRef<HTMLInputElement, MultiSelectProps>(
|
|
19
|
+
function MultiSelectInner(
|
|
20
|
+
{
|
|
21
|
+
value,
|
|
22
|
+
disabled = false,
|
|
23
|
+
onChange,
|
|
24
|
+
variant = 'default',
|
|
25
|
+
className,
|
|
26
|
+
children,
|
|
49
27
|
},
|
|
50
|
-
|
|
51
|
-
)
|
|
28
|
+
ref
|
|
29
|
+
) {
|
|
30
|
+
const {
|
|
31
|
+
name,
|
|
32
|
+
selected,
|
|
33
|
+
disabled: parentDisabled,
|
|
34
|
+
readonly,
|
|
35
|
+
invalid,
|
|
36
|
+
onChange: parentOnChange,
|
|
37
|
+
} = useContext(MultiSelectGroupContext)
|
|
38
|
+
|
|
39
|
+
warning(
|
|
40
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
41
|
+
name !== undefined,
|
|
42
|
+
`"name" is not Provided for <MultiSelect>. Perhaps you forgot to wrap with <MultiSelectGroup> ?`
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
const isSelected = selected.includes(value)
|
|
46
|
+
const isDisabled = disabled || parentDisabled || readonly
|
|
47
|
+
|
|
48
|
+
const handleChange = useCallback(
|
|
49
|
+
(event: ChangeEvent<HTMLInputElement>) => {
|
|
50
|
+
if (!(event.currentTarget instanceof HTMLInputElement)) {
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
if (onChange) onChange({ value, selected: event.currentTarget.checked })
|
|
54
|
+
parentOnChange({ value, selected: event.currentTarget.checked })
|
|
55
|
+
},
|
|
56
|
+
[onChange, parentOnChange, value]
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<MultiSelectRoot aria-disabled={isDisabled} className={className}>
|
|
61
|
+
<MultiSelectInput
|
|
62
|
+
{...{
|
|
63
|
+
name,
|
|
64
|
+
value,
|
|
65
|
+
invalid,
|
|
66
|
+
}}
|
|
67
|
+
checked={isSelected}
|
|
68
|
+
disabled={isDisabled}
|
|
69
|
+
onChange={handleChange}
|
|
70
|
+
overlay={variant === 'overlay'}
|
|
71
|
+
aria-invalid={invalid}
|
|
72
|
+
ref={ref}
|
|
73
|
+
/>
|
|
74
|
+
<MultiSelectInputOverlay
|
|
75
|
+
overlay={variant === 'overlay'}
|
|
76
|
+
invalid={invalid}
|
|
77
|
+
aria-hidden={true}
|
|
78
|
+
>
|
|
79
|
+
<pixiv-icon name="24/Check" unsafe-non-guideline-scale={16 / 24} />
|
|
80
|
+
</MultiSelectInputOverlay>
|
|
81
|
+
{Boolean(children) && <MultiSelectLabel>{children}</MultiSelectLabel>}
|
|
82
|
+
</MultiSelectRoot>
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
)
|
|
52
86
|
|
|
53
|
-
|
|
54
|
-
<MultiSelectRoot aria-disabled={isDisabled}>
|
|
55
|
-
<MultiSelectInput
|
|
56
|
-
{...{
|
|
57
|
-
name,
|
|
58
|
-
value,
|
|
59
|
-
invalid,
|
|
60
|
-
}}
|
|
61
|
-
checked={isSelected}
|
|
62
|
-
disabled={isDisabled}
|
|
63
|
-
onChange={handleChange}
|
|
64
|
-
overlay={variant === 'overlay'}
|
|
65
|
-
aria-invalid={invalid}
|
|
66
|
-
/>
|
|
67
|
-
<MultiSelectInputOverlay
|
|
68
|
-
overlay={variant === 'overlay'}
|
|
69
|
-
invalid={invalid}
|
|
70
|
-
aria-hidden={true}
|
|
71
|
-
>
|
|
72
|
-
<pixiv-icon name="24/Check" unsafe-non-guideline-scale={16 / 24} />
|
|
73
|
-
</MultiSelectInputOverlay>
|
|
74
|
-
{Boolean(children) && <MultiSelectLabel>{children}</MultiSelectLabel>}
|
|
75
|
-
</MultiSelectRoot>
|
|
76
|
-
)
|
|
77
|
-
}
|
|
87
|
+
export default memo(MultiSelect)
|
|
78
88
|
|
|
79
89
|
const MultiSelectRoot = styled.label`
|
|
80
90
|
display: grid;
|
|
@@ -16,30 +16,29 @@ export default {
|
|
|
16
16
|
},
|
|
17
17
|
},
|
|
18
18
|
args: {
|
|
19
|
-
|
|
19
|
+
invalid: false,
|
|
20
20
|
parentDisabled: false,
|
|
21
21
|
childDisabled: false,
|
|
22
|
-
forceChecked: false,
|
|
23
22
|
readonly: false,
|
|
24
23
|
},
|
|
25
24
|
}
|
|
26
25
|
|
|
27
26
|
interface Props {
|
|
28
27
|
value?: string
|
|
29
|
-
|
|
28
|
+
invalid: boolean
|
|
30
29
|
parentDisabled: boolean
|
|
31
30
|
childDisabled: boolean
|
|
32
|
-
forceChecked: boolean
|
|
33
31
|
readonly: boolean
|
|
32
|
+
className?: string
|
|
34
33
|
}
|
|
35
34
|
|
|
36
35
|
const Template: Story<Partial<Props>> = ({
|
|
37
36
|
value,
|
|
38
|
-
|
|
39
|
-
hasError,
|
|
37
|
+
invalid,
|
|
40
38
|
parentDisabled,
|
|
41
39
|
childDisabled,
|
|
42
40
|
readonly,
|
|
41
|
+
className,
|
|
43
42
|
}) => (
|
|
44
43
|
<div
|
|
45
44
|
css={css`
|
|
@@ -57,14 +56,14 @@ const Template: Story<Partial<Props>> = ({
|
|
|
57
56
|
onChange={action('onChange')}
|
|
58
57
|
disabled={parentDisabled}
|
|
59
58
|
readonly={readonly}
|
|
60
|
-
|
|
59
|
+
invalid={invalid}
|
|
61
60
|
>
|
|
62
61
|
{options.map((option) => (
|
|
63
62
|
<Radio
|
|
64
63
|
key={option}
|
|
65
64
|
value={option}
|
|
66
65
|
disabled={childDisabled}
|
|
67
|
-
|
|
66
|
+
className={className}
|
|
68
67
|
>
|
|
69
68
|
{name}({option})を選ぶ
|
|
70
69
|
</Radio>
|
|
@@ -111,7 +111,7 @@ function TestComponent({
|
|
|
111
111
|
onChange = jest.fn(),
|
|
112
112
|
radioGroupDisabled = false,
|
|
113
113
|
readonly = false,
|
|
114
|
-
|
|
114
|
+
invalid = false,
|
|
115
115
|
option1Disabled = false,
|
|
116
116
|
option2Disabled = false,
|
|
117
117
|
}: {
|
|
@@ -119,7 +119,7 @@ function TestComponent({
|
|
|
119
119
|
onChange?: () => void
|
|
120
120
|
radioGroupDisabled?: boolean
|
|
121
121
|
readonly?: boolean
|
|
122
|
-
|
|
122
|
+
invalid?: boolean
|
|
123
123
|
option1Disabled?: boolean
|
|
124
124
|
option2Disabled?: boolean
|
|
125
125
|
}) {
|
|
@@ -132,7 +132,7 @@ function TestComponent({
|
|
|
132
132
|
onChange={onChange}
|
|
133
133
|
disabled={radioGroupDisabled}
|
|
134
134
|
readonly={readonly}
|
|
135
|
-
|
|
135
|
+
invalid={invalid}
|
|
136
136
|
>
|
|
137
137
|
<Radio value="option1" disabled={option1Disabled}>
|
|
138
138
|
option1を選ぶ
|