@charcoal-ui/react 2.0.0-alpha.8 → 2.0.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -0
- package/dist/components/Checkbox/index.d.ts +21 -0
- package/dist/components/Checkbox/index.d.ts.map +1 -0
- package/dist/components/Checkbox/index.story.d.ts +16 -0
- package/dist/components/Checkbox/index.story.d.ts.map +1 -0
- package/dist/components/DropdownSelector/Listbox.d.ts +10 -0
- package/dist/components/DropdownSelector/Listbox.d.ts.map +1 -0
- package/dist/components/DropdownSelector/Popover.d.ts +10 -0
- package/dist/components/DropdownSelector/Popover.d.ts.map +1 -0
- package/dist/components/DropdownSelector/index.d.ts +32 -0
- package/dist/components/DropdownSelector/index.d.ts.map +1 -0
- package/dist/components/DropdownSelector/index.story.d.ts +22 -0
- package/dist/components/DropdownSelector/index.story.d.ts.map +1 -0
- package/dist/components/Icon/index.story.d.ts +1 -1
- package/dist/components/LoadingSpinner/index.d.ts +15 -0
- package/dist/components/LoadingSpinner/index.d.ts.map +1 -0
- package/dist/components/LoadingSpinner/index.story.d.ts +10 -0
- package/dist/components/LoadingSpinner/index.story.d.ts.map +1 -0
- package/dist/components/Modal/ModalPlumbing.d.ts +5 -0
- package/dist/components/Modal/ModalPlumbing.d.ts.map +1 -0
- package/dist/components/Modal/index.d.ts +16 -0
- package/dist/components/Modal/index.d.ts.map +1 -0
- package/dist/components/Modal/index.story.d.ts +33 -0
- package/dist/components/Modal/index.story.d.ts.map +1 -0
- package/dist/components/{Select → MultiSelect}/context.d.ts +2 -2
- package/dist/components/MultiSelect/context.d.ts.map +1 -0
- package/dist/components/MultiSelect/index.d.ts +24 -0
- package/dist/components/MultiSelect/index.d.ts.map +1 -0
- package/dist/components/{Select → MultiSelect}/index.story.d.ts +2 -2
- package/dist/components/MultiSelect/index.story.d.ts.map +1 -0
- package/dist/components/{Select → MultiSelect}/index.test.d.ts +0 -0
- package/dist/components/MultiSelect/index.test.d.ts.map +1 -0
- package/dist/components/SegmentedControl/RadioGroupContext.d.ts +9 -0
- package/dist/components/SegmentedControl/RadioGroupContext.d.ts.map +1 -0
- package/dist/components/SegmentedControl/index.d.ts +20 -0
- package/dist/components/SegmentedControl/index.d.ts.map +1 -0
- package/dist/components/SegmentedControl/index.story.d.ts +11 -0
- package/dist/components/SegmentedControl/index.story.d.ts.map +1 -0
- package/dist/components/TextField/index.d.ts +6 -3
- package/dist/components/TextField/index.d.ts.map +1 -1
- package/dist/components/TextField/index.story.d.ts +1 -0
- package/dist/components/TextField/index.story.d.ts.map +1 -1
- package/dist/core/SSRProvider.d.ts +2 -0
- package/dist/core/SSRProvider.d.ts.map +1 -0
- package/dist/index.cjs +22686 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +8 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.modern.js +21612 -45
- package/dist/index.modern.js.map +1 -1
- package/dist/index.module.js +22641 -1
- package/dist/index.module.js.map +1 -1
- package/package.json +15 -8
- package/src/components/Checkbox/index.story.tsx +64 -0
- package/src/components/Checkbox/index.tsx +122 -0
- package/src/components/DropdownSelector/Listbox.tsx +127 -0
- package/src/components/DropdownSelector/Popover.tsx +46 -0
- package/src/components/DropdownSelector/index.story.tsx +134 -0
- package/src/components/DropdownSelector/index.tsx +214 -0
- package/src/components/FieldLabel/index.tsx +1 -1
- package/src/components/LoadingSpinner/index.story.tsx +52 -0
- package/src/components/LoadingSpinner/index.tsx +87 -0
- package/src/components/Modal/ModalPlumbing.tsx +47 -0
- package/src/components/Modal/index.story.tsx +195 -0
- package/src/components/Modal/index.tsx +226 -0
- package/src/components/{Select → MultiSelect}/context.ts +3 -3
- package/src/components/{Select → MultiSelect}/index.story.tsx +16 -12
- package/src/components/{Select → MultiSelect}/index.test.tsx +13 -13
- package/src/components/{Select → MultiSelect}/index.tsx +24 -21
- package/src/components/SegmentedControl/RadioGroupContext.tsx +22 -0
- package/src/components/SegmentedControl/index.story.tsx +36 -0
- package/src/components/SegmentedControl/index.tsx +157 -0
- package/src/components/TextField/index.story.tsx +31 -16
- package/src/components/TextField/index.tsx +53 -34
- package/src/components/a11y.test.tsx +11 -0
- package/src/core/SSRProvider.tsx +1 -0
- package/src/index.ts +22 -5
- package/src/styled.ts +1 -1
- package/dist/components/Select/context.d.ts.map +0 -1
- package/dist/components/Select/index.d.ts +0 -24
- package/dist/components/Select/index.d.ts.map +0 -1
- package/dist/components/Select/index.story.d.ts.map +0 -1
- package/dist/components/Select/index.test.d.ts.map +0 -1
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import React, { Key, useMemo, useRef } from 'react'
|
|
2
|
+
import styled from 'styled-components'
|
|
3
|
+
import { Item, useSelectState } from 'react-stately'
|
|
4
|
+
import { disabledSelector } from '@charcoal-ui/utils'
|
|
5
|
+
import { useVisuallyHidden } from '@react-aria/visually-hidden'
|
|
6
|
+
import { useSelect, HiddenSelect } from '@react-aria/select'
|
|
7
|
+
import { useButton } from '@react-aria/button'
|
|
8
|
+
import { SelectProps } from '@react-types/select'
|
|
9
|
+
import Listbox, { ListboxProps } from './Listbox'
|
|
10
|
+
import Popover from './Popover'
|
|
11
|
+
import Icon from '../Icon'
|
|
12
|
+
import FieldLabel from '../FieldLabel'
|
|
13
|
+
import { theme } from '../../styled'
|
|
14
|
+
|
|
15
|
+
import type { CollectionBase } from '@react-types/shared'
|
|
16
|
+
import type { ReactNode } from 'react'
|
|
17
|
+
|
|
18
|
+
type LabelProps = {
|
|
19
|
+
readonly showLabel?: boolean
|
|
20
|
+
readonly label: string
|
|
21
|
+
readonly subLabel?: ReactNode
|
|
22
|
+
readonly requiredText?: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
type Empty = Record<string, unknown>
|
|
26
|
+
export type DropdownSelectorProps<T extends Empty = Empty> = LabelProps &
|
|
27
|
+
Readonly<CollectionBase<T>> & {
|
|
28
|
+
readonly id?: string
|
|
29
|
+
readonly name?: string
|
|
30
|
+
readonly autoComplete?: string
|
|
31
|
+
readonly placeholder?: string
|
|
32
|
+
readonly className?: string
|
|
33
|
+
readonly disabled?: boolean
|
|
34
|
+
readonly required?: boolean
|
|
35
|
+
readonly invalid?: boolean
|
|
36
|
+
readonly assertiveText?: string
|
|
37
|
+
readonly value?: Key
|
|
38
|
+
readonly defaultValue?: Key
|
|
39
|
+
readonly open?: boolean
|
|
40
|
+
readonly onOpenChange?: (isOpen?: boolean) => void
|
|
41
|
+
readonly onChange?: (key: Key) => void
|
|
42
|
+
readonly mode?: ListboxProps<T>['mode']
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const DropdownSelector = <T extends Record<string, unknown>>({
|
|
46
|
+
open,
|
|
47
|
+
className,
|
|
48
|
+
label = '',
|
|
49
|
+
requiredText = '',
|
|
50
|
+
subLabel,
|
|
51
|
+
assertiveText,
|
|
52
|
+
autoComplete,
|
|
53
|
+
invalid = false,
|
|
54
|
+
disabled = false,
|
|
55
|
+
required = false,
|
|
56
|
+
showLabel = false,
|
|
57
|
+
mode = 'default',
|
|
58
|
+
...props
|
|
59
|
+
}: DropdownSelectorProps<T>) => {
|
|
60
|
+
const { visuallyHiddenProps } = useVisuallyHidden()
|
|
61
|
+
const triggerRef = useRef<HTMLButtonElement>(null)
|
|
62
|
+
const selectProps = useMemo<SelectProps<T>>(
|
|
63
|
+
() => ({
|
|
64
|
+
...props,
|
|
65
|
+
label,
|
|
66
|
+
isOpen: open,
|
|
67
|
+
isDisabled: disabled,
|
|
68
|
+
isRequired: required,
|
|
69
|
+
errorMessage: invalid && assertiveText,
|
|
70
|
+
validationState: invalid ? 'invalid' : 'valid',
|
|
71
|
+
onSelectionChange: props.onChange,
|
|
72
|
+
selectedKey: props.value,
|
|
73
|
+
defaultSelectedKey: props.defaultValue,
|
|
74
|
+
}),
|
|
75
|
+
[assertiveText, disabled, invalid, label, open, props, required]
|
|
76
|
+
)
|
|
77
|
+
const state = useSelectState<T>(selectProps)
|
|
78
|
+
|
|
79
|
+
const {
|
|
80
|
+
labelProps,
|
|
81
|
+
triggerProps,
|
|
82
|
+
valueProps,
|
|
83
|
+
menuProps,
|
|
84
|
+
errorMessageProps,
|
|
85
|
+
descriptionProps,
|
|
86
|
+
} = useSelect<T>(selectProps, state, triggerRef)
|
|
87
|
+
|
|
88
|
+
const { buttonProps } = useButton(triggerProps, triggerRef)
|
|
89
|
+
|
|
90
|
+
const hasAssertiveText =
|
|
91
|
+
assertiveText !== undefined && assertiveText.length > 0
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<DropdownSelectorRoot aria-disabled={disabled} className={className}>
|
|
95
|
+
<DropdownFieldLabel
|
|
96
|
+
label={label}
|
|
97
|
+
required={required}
|
|
98
|
+
requiredText={requiredText}
|
|
99
|
+
subLabel={subLabel}
|
|
100
|
+
{...labelProps}
|
|
101
|
+
{...(!showLabel ? visuallyHiddenProps : {})}
|
|
102
|
+
/>
|
|
103
|
+
<HiddenSelect
|
|
104
|
+
state={state}
|
|
105
|
+
triggerRef={triggerRef}
|
|
106
|
+
label={label}
|
|
107
|
+
name={props.name}
|
|
108
|
+
isDisabled={disabled}
|
|
109
|
+
autoComplete={autoComplete}
|
|
110
|
+
/>
|
|
111
|
+
<DropdownButtonWrapper>
|
|
112
|
+
<DropdownButton {...buttonProps} ref={triggerRef} invalid={invalid}>
|
|
113
|
+
<DropdownButtonText {...valueProps}>
|
|
114
|
+
{/*
|
|
115
|
+
* react-stately の useSelectState から取得される selectedItem の型が常に
|
|
116
|
+
* Node<T> であるが runtime では null が帰ってくることがある
|
|
117
|
+
*/}
|
|
118
|
+
{/* eslint-disable-next-line @typescript-eslint/strict-boolean-expressions,@typescript-eslint/no-unnecessary-condition*/}
|
|
119
|
+
{state.selectedItem
|
|
120
|
+
? state.selectedItem.rendered
|
|
121
|
+
: props.placeholder}
|
|
122
|
+
</DropdownButtonText>
|
|
123
|
+
|
|
124
|
+
<Icon name="16/Menu" />
|
|
125
|
+
</DropdownButton>
|
|
126
|
+
{state.isOpen && (
|
|
127
|
+
<DropdownPopover open={state.isOpen} onClose={() => state.close()}>
|
|
128
|
+
<Listbox {...menuProps} state={state} mode={mode} />
|
|
129
|
+
</DropdownPopover>
|
|
130
|
+
)}
|
|
131
|
+
</DropdownButtonWrapper>
|
|
132
|
+
|
|
133
|
+
{hasAssertiveText && (
|
|
134
|
+
<AssertiveText
|
|
135
|
+
invalid={invalid}
|
|
136
|
+
{...(invalid ? errorMessageProps : descriptionProps)}
|
|
137
|
+
>
|
|
138
|
+
{assertiveText}
|
|
139
|
+
</AssertiveText>
|
|
140
|
+
)}
|
|
141
|
+
</DropdownSelectorRoot>
|
|
142
|
+
)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export default DropdownSelector
|
|
146
|
+
export const DropdownSelectorItem = Item
|
|
147
|
+
|
|
148
|
+
const DropdownSelectorRoot = styled.div`
|
|
149
|
+
position: relative;
|
|
150
|
+
display: inline-block;
|
|
151
|
+
|
|
152
|
+
${disabledSelector} {
|
|
153
|
+
cursor: default;
|
|
154
|
+
${theme((o) => o.disabled)}
|
|
155
|
+
}
|
|
156
|
+
`
|
|
157
|
+
|
|
158
|
+
const DropdownFieldLabel = styled(FieldLabel)`
|
|
159
|
+
width: 100%;
|
|
160
|
+
|
|
161
|
+
${theme((o) => o.margin.bottom(8))}
|
|
162
|
+
`
|
|
163
|
+
|
|
164
|
+
const DropdownButtonWrapper = styled.div`
|
|
165
|
+
position: relative;
|
|
166
|
+
`
|
|
167
|
+
|
|
168
|
+
const DropdownButton = styled.button<{ invalid: boolean }>`
|
|
169
|
+
display: flex;
|
|
170
|
+
justify-content: space-between;
|
|
171
|
+
align-items: center;
|
|
172
|
+
|
|
173
|
+
height: 40px;
|
|
174
|
+
width: 288px;
|
|
175
|
+
box-sizing: border-box;
|
|
176
|
+
cursor: pointer;
|
|
177
|
+
|
|
178
|
+
${disabledSelector} {
|
|
179
|
+
cursor: default;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
${({ invalid }) =>
|
|
183
|
+
theme((o) => [
|
|
184
|
+
o.border.default,
|
|
185
|
+
o.padding.horizontal(8),
|
|
186
|
+
o.outline.default.focus,
|
|
187
|
+
o.bg.surface3,
|
|
188
|
+
o.borderRadius(4),
|
|
189
|
+
invalid && o.outline.assertive,
|
|
190
|
+
])}
|
|
191
|
+
`
|
|
192
|
+
|
|
193
|
+
const DropdownButtonText = styled.span`
|
|
194
|
+
text-align: left;
|
|
195
|
+
|
|
196
|
+
${theme((o) => [o.typography(14), o.font.text2])}
|
|
197
|
+
`
|
|
198
|
+
|
|
199
|
+
const AssertiveText = styled.div<{ invalid: boolean }>`
|
|
200
|
+
${({ invalid }) =>
|
|
201
|
+
theme((o) => [
|
|
202
|
+
o.typography(14),
|
|
203
|
+
o.margin.top(8),
|
|
204
|
+
invalid && o.font.assertive,
|
|
205
|
+
])}
|
|
206
|
+
`
|
|
207
|
+
|
|
208
|
+
const DropdownPopover = styled(Popover)`
|
|
209
|
+
position: absolute;
|
|
210
|
+
width: 100%;
|
|
211
|
+
|
|
212
|
+
top: 100%;
|
|
213
|
+
margin-top: 2px;
|
|
214
|
+
`
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import {
|
|
2
|
+
boolean,
|
|
3
|
+
button,
|
|
4
|
+
number,
|
|
5
|
+
text,
|
|
6
|
+
withKnobs,
|
|
7
|
+
} from '@storybook/addon-knobs'
|
|
8
|
+
import React, { useRef } from 'react'
|
|
9
|
+
import LoadingSpinner, {
|
|
10
|
+
LoadingSpinnerIcon,
|
|
11
|
+
LoadingSpinnerIconHandler,
|
|
12
|
+
} from '.'
|
|
13
|
+
|
|
14
|
+
export default {
|
|
15
|
+
title: 'LoadingSpinner',
|
|
16
|
+
component: LoadingSpinner,
|
|
17
|
+
decorators: [withKnobs],
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function Basic() {
|
|
21
|
+
const size = number('size', 48)
|
|
22
|
+
const padding = number('padding', 16)
|
|
23
|
+
const transparent = boolean('transparent', false)
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<LoadingSpinner size={size} padding={padding} transparent={transparent} />
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function Icon() {
|
|
31
|
+
return <IconComponent />
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function IconComponent() {
|
|
35
|
+
const size = number('size', 12)
|
|
36
|
+
const color = text('color', '#B1CC29')
|
|
37
|
+
const once = boolean('once', false)
|
|
38
|
+
button('restart', () => ref.current?.restart())
|
|
39
|
+
|
|
40
|
+
const ref = useRef<LoadingSpinnerIconHandler>(null)
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<div
|
|
44
|
+
css={`
|
|
45
|
+
font-size: ${size}px;
|
|
46
|
+
color: ${color};
|
|
47
|
+
`}
|
|
48
|
+
>
|
|
49
|
+
<LoadingSpinnerIcon once={once} ref={ref} />
|
|
50
|
+
</div>
|
|
51
|
+
)
|
|
52
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { transparentize } from 'polished'
|
|
2
|
+
import React, { useImperativeHandle, useRef } from 'react'
|
|
3
|
+
import styled, { keyframes } from 'styled-components'
|
|
4
|
+
|
|
5
|
+
export default function LoadingSpinner({
|
|
6
|
+
size = 48,
|
|
7
|
+
padding = 16,
|
|
8
|
+
transparent = false,
|
|
9
|
+
}) {
|
|
10
|
+
return (
|
|
11
|
+
<LoadingSpinnerRoot size={size} padding={padding} transparent={transparent}>
|
|
12
|
+
<LoadingSpinnerIcon />
|
|
13
|
+
</LoadingSpinnerRoot>
|
|
14
|
+
)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const LoadingSpinnerRoot = styled.div.attrs({ role: 'progressbar' })<{
|
|
18
|
+
size: number
|
|
19
|
+
padding: number
|
|
20
|
+
transparent: boolean
|
|
21
|
+
}>`
|
|
22
|
+
margin: auto;
|
|
23
|
+
padding: ${(props) => props.padding}px;
|
|
24
|
+
border-radius: 8px;
|
|
25
|
+
font-size: ${(props) => props.size}px;
|
|
26
|
+
width: ${(props) => props.size}px;
|
|
27
|
+
height: ${(props) => props.size}px;
|
|
28
|
+
background-color: ${({ theme, transparent }) =>
|
|
29
|
+
transparent
|
|
30
|
+
? 'transparent'
|
|
31
|
+
: transparentize(0.32, theme.color.background1)};
|
|
32
|
+
color: ${({ theme }) => theme.color.text4};
|
|
33
|
+
`
|
|
34
|
+
|
|
35
|
+
const scaleout = keyframes`
|
|
36
|
+
from {
|
|
37
|
+
transform: scale(0);
|
|
38
|
+
opacity: 1;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
to {
|
|
42
|
+
transform: scale(1);
|
|
43
|
+
opacity: 0;
|
|
44
|
+
}
|
|
45
|
+
`
|
|
46
|
+
|
|
47
|
+
const Icon = styled.div.attrs({ role: 'presentation' })<{ once: boolean }>`
|
|
48
|
+
width: 1em;
|
|
49
|
+
height: 1em;
|
|
50
|
+
border-radius: 1em;
|
|
51
|
+
background-color: currentColor;
|
|
52
|
+
animation: ${scaleout} 1s both ease-out;
|
|
53
|
+
animation-iteration-count: ${(p) => (p.once ? 1 : 'infinite')};
|
|
54
|
+
|
|
55
|
+
&[data-reset-animation] {
|
|
56
|
+
animation: none;
|
|
57
|
+
}
|
|
58
|
+
`
|
|
59
|
+
|
|
60
|
+
interface Props {
|
|
61
|
+
once?: boolean
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface LoadingSpinnerIconHandler {
|
|
65
|
+
restart(): void
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const LoadingSpinnerIcon = React.forwardRef<
|
|
69
|
+
LoadingSpinnerIconHandler,
|
|
70
|
+
Props
|
|
71
|
+
>(function LoadingSpinnerIcon({ once = false }, ref) {
|
|
72
|
+
const iconRef = useRef<HTMLDivElement>(null)
|
|
73
|
+
|
|
74
|
+
useImperativeHandle(ref, () => ({
|
|
75
|
+
restart: () => {
|
|
76
|
+
if (!iconRef.current) {
|
|
77
|
+
return
|
|
78
|
+
}
|
|
79
|
+
iconRef.current.dataset.resetAnimation = 'true'
|
|
80
|
+
// Force reflow hack!
|
|
81
|
+
void iconRef.current.offsetWidth
|
|
82
|
+
delete iconRef.current.dataset.resetAnimation
|
|
83
|
+
},
|
|
84
|
+
}))
|
|
85
|
+
|
|
86
|
+
return <Icon ref={iconRef} once={once} />
|
|
87
|
+
})
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { ModalTitle } from '.'
|
|
3
|
+
import styled from 'styled-components'
|
|
4
|
+
import { theme } from '../../styled'
|
|
5
|
+
import { maxWidth } from '@charcoal-ui/utils'
|
|
6
|
+
|
|
7
|
+
export function ModalHeader() {
|
|
8
|
+
return (
|
|
9
|
+
<ModalHeaderRoot>
|
|
10
|
+
<StyledModalTitle />
|
|
11
|
+
</ModalHeaderRoot>
|
|
12
|
+
)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const ModalHeaderRoot = styled.div`
|
|
16
|
+
height: 64px;
|
|
17
|
+
display: grid;
|
|
18
|
+
align-content: center;
|
|
19
|
+
justify-content: center;
|
|
20
|
+
`
|
|
21
|
+
|
|
22
|
+
const StyledModalTitle = styled(ModalTitle)`
|
|
23
|
+
${theme((o) => [o.font.text1, o.typography(16).bold])}
|
|
24
|
+
`
|
|
25
|
+
|
|
26
|
+
export const ModalAlign = styled.div`
|
|
27
|
+
${theme((o) => [o.padding.horizontal(24)])}
|
|
28
|
+
|
|
29
|
+
@media ${({ theme }) => maxWidth(theme.breakpoint.screen1)} {
|
|
30
|
+
${theme((o) => [o.padding.horizontal(16)])}
|
|
31
|
+
}
|
|
32
|
+
`
|
|
33
|
+
|
|
34
|
+
export const ModalBody = styled.div`
|
|
35
|
+
${theme((o) => [o.padding.bottom(40)])}
|
|
36
|
+
`
|
|
37
|
+
|
|
38
|
+
export const ModalButtons = styled.div`
|
|
39
|
+
display: grid;
|
|
40
|
+
grid-auto-flow: row;
|
|
41
|
+
grid-row-gap: 8px;
|
|
42
|
+
${theme((o) => [o.padding.horizontal(24).top(16)])}
|
|
43
|
+
|
|
44
|
+
@media ${({ theme }) => maxWidth(theme.breakpoint.screen1)} {
|
|
45
|
+
${theme((o) => [o.padding.horizontal(16)])}
|
|
46
|
+
}
|
|
47
|
+
`
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Story } from '../../_lib/compat'
|
|
3
|
+
import Modal, { ModalDismissButton, ModalProps } from '.'
|
|
4
|
+
import { OverlayProvider } from '@react-aria/overlays'
|
|
5
|
+
import { useOverlayTriggerState } from 'react-stately'
|
|
6
|
+
import Button from '../Button'
|
|
7
|
+
import {
|
|
8
|
+
ModalAlign,
|
|
9
|
+
ModalBody,
|
|
10
|
+
ModalButtons,
|
|
11
|
+
ModalHeader,
|
|
12
|
+
} from './ModalPlumbing'
|
|
13
|
+
import styled from 'styled-components'
|
|
14
|
+
import { theme } from '../../styled'
|
|
15
|
+
import TextField from '../TextField'
|
|
16
|
+
|
|
17
|
+
export default {
|
|
18
|
+
title: 'Modal',
|
|
19
|
+
component: Modal,
|
|
20
|
+
args: {
|
|
21
|
+
title: 'Title',
|
|
22
|
+
},
|
|
23
|
+
argTypes: {
|
|
24
|
+
size: {
|
|
25
|
+
options: ['S', 'M', 'L'],
|
|
26
|
+
control: {
|
|
27
|
+
type: 'inline-radio',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
bottomSheet: {
|
|
31
|
+
options: ['full', 'true', 'false'],
|
|
32
|
+
mapping: { full: 'full', true: true, false: false },
|
|
33
|
+
control: {
|
|
34
|
+
type: 'inline-radio',
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const DefaultStory = (args: ModalProps) => {
|
|
41
|
+
const state = useOverlayTriggerState({})
|
|
42
|
+
return (
|
|
43
|
+
// Application must be wrapped in an OverlayProvider so that it can be
|
|
44
|
+
// hidden from screen readers when a modal opens.
|
|
45
|
+
<OverlayProvider>
|
|
46
|
+
<Button onClick={() => state.open()}>Open Modal</Button>
|
|
47
|
+
|
|
48
|
+
<Modal
|
|
49
|
+
isOpen={state.isOpen}
|
|
50
|
+
onClose={() => state.close()}
|
|
51
|
+
isDismissable
|
|
52
|
+
{...args}
|
|
53
|
+
>
|
|
54
|
+
<ModalHeader />
|
|
55
|
+
<ModalBody>
|
|
56
|
+
<ModalVStack>
|
|
57
|
+
<StyledModalText>
|
|
58
|
+
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quod
|
|
59
|
+
placeat tenetur, necessitatibus laudantium cumque exercitationem
|
|
60
|
+
provident. Quaerat iure enim, eveniet dolores earum odio quo
|
|
61
|
+
possimus fugiat aspernatur, numquam, commodi repellat.
|
|
62
|
+
</StyledModalText>
|
|
63
|
+
<ModalAlign>
|
|
64
|
+
<TextField
|
|
65
|
+
showLabel
|
|
66
|
+
label="Name"
|
|
67
|
+
placeholder="Nagisa"
|
|
68
|
+
></TextField>
|
|
69
|
+
</ModalAlign>
|
|
70
|
+
<ModalAlign>
|
|
71
|
+
<TextField
|
|
72
|
+
showLabel
|
|
73
|
+
label="Country"
|
|
74
|
+
placeholder="Tokyo"
|
|
75
|
+
></TextField>
|
|
76
|
+
</ModalAlign>
|
|
77
|
+
</ModalVStack>
|
|
78
|
+
<ModalButtons>
|
|
79
|
+
<Button variant="Primary" onClick={() => state.close()} fixed>
|
|
80
|
+
Apply
|
|
81
|
+
</Button>
|
|
82
|
+
<Button onClick={() => state.close()} fixed>
|
|
83
|
+
Cancel
|
|
84
|
+
</Button>
|
|
85
|
+
</ModalButtons>
|
|
86
|
+
</ModalBody>
|
|
87
|
+
</Modal>
|
|
88
|
+
</OverlayProvider>
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const ModalVStack = styled.div`
|
|
93
|
+
display: grid;
|
|
94
|
+
gap: 24px;
|
|
95
|
+
`
|
|
96
|
+
|
|
97
|
+
const StyledModalText = styled(ModalAlign)`
|
|
98
|
+
${theme((o) => [o.font.text2, o.typography(14)])}
|
|
99
|
+
`
|
|
100
|
+
|
|
101
|
+
export const Default: Story<ModalProps> = DefaultStory.bind({})
|
|
102
|
+
|
|
103
|
+
const FullBottomSheetStory = (args: ModalProps) => {
|
|
104
|
+
const state = useOverlayTriggerState({})
|
|
105
|
+
return (
|
|
106
|
+
// Application must be wrapped in an OverlayProvider so that it can be
|
|
107
|
+
// hidden from screen readers when a modal opens.
|
|
108
|
+
<OverlayProvider>
|
|
109
|
+
<Button onClick={() => state.open()}>Open Modal</Button>
|
|
110
|
+
|
|
111
|
+
<Modal
|
|
112
|
+
isOpen={state.isOpen}
|
|
113
|
+
onClose={() => state.close()}
|
|
114
|
+
isDismissable
|
|
115
|
+
bottomSheet="full"
|
|
116
|
+
{...args}
|
|
117
|
+
>
|
|
118
|
+
<ModalHeader />
|
|
119
|
+
<ModalBody>
|
|
120
|
+
<ModalVStack>
|
|
121
|
+
<StyledModalText>
|
|
122
|
+
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quod
|
|
123
|
+
placeat tenetur, necessitatibus laudantium cumque exercitationem
|
|
124
|
+
provident. Quaerat iure enim, eveniet dolores earum odio quo
|
|
125
|
+
possimus fugiat aspernatur, numquam, commodi repellat.
|
|
126
|
+
</StyledModalText>
|
|
127
|
+
<ModalAlign>
|
|
128
|
+
<TextField
|
|
129
|
+
showLabel
|
|
130
|
+
label="Name"
|
|
131
|
+
placeholder="Nagisa"
|
|
132
|
+
></TextField>
|
|
133
|
+
</ModalAlign>
|
|
134
|
+
<ModalAlign>
|
|
135
|
+
<TextField
|
|
136
|
+
showLabel
|
|
137
|
+
label="Country"
|
|
138
|
+
placeholder="Tokyo"
|
|
139
|
+
></TextField>
|
|
140
|
+
</ModalAlign>
|
|
141
|
+
</ModalVStack>
|
|
142
|
+
<ModalButtons>
|
|
143
|
+
<Button variant="Primary" onClick={() => state.close()} fixed>
|
|
144
|
+
Apply
|
|
145
|
+
</Button>
|
|
146
|
+
<Button onClick={() => state.close()} fixed>
|
|
147
|
+
Cancel
|
|
148
|
+
</Button>
|
|
149
|
+
</ModalButtons>
|
|
150
|
+
</ModalBody>
|
|
151
|
+
</Modal>
|
|
152
|
+
</OverlayProvider>
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export const FullBottomSheet: Story<ModalProps> = FullBottomSheetStory.bind({})
|
|
157
|
+
|
|
158
|
+
const BottomSheetStory = (args: ModalProps) => {
|
|
159
|
+
const state = useOverlayTriggerState({})
|
|
160
|
+
return (
|
|
161
|
+
// Application must be wrapped in an OverlayProvider so that it can be
|
|
162
|
+
// hidden from screen readers when a modal opens.
|
|
163
|
+
<OverlayProvider>
|
|
164
|
+
<Button onClick={() => state.open()}>Open Modal</Button>
|
|
165
|
+
|
|
166
|
+
<Modal
|
|
167
|
+
isOpen={state.isOpen}
|
|
168
|
+
onClose={() => state.close()}
|
|
169
|
+
bottomSheet
|
|
170
|
+
isDismissable
|
|
171
|
+
{...args}
|
|
172
|
+
>
|
|
173
|
+
<ModalHeader />
|
|
174
|
+
<ModalBody>
|
|
175
|
+
<ModalVStack>
|
|
176
|
+
<StyledModalText>
|
|
177
|
+
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quod
|
|
178
|
+
placeat tenetur, necessitatibus laudantium cumque exercitationem
|
|
179
|
+
provident. Quaerat iure enim, eveniet dolores earum odio quo
|
|
180
|
+
possimus fugiat aspernatur, numquam, commodi repellat.
|
|
181
|
+
</StyledModalText>
|
|
182
|
+
</ModalVStack>
|
|
183
|
+
<ModalButtons>
|
|
184
|
+
<Button variant="Danger" onClick={() => state.close()} fixed>
|
|
185
|
+
削除する
|
|
186
|
+
</Button>
|
|
187
|
+
<ModalDismissButton>キャンセル</ModalDismissButton>
|
|
188
|
+
</ModalButtons>
|
|
189
|
+
</ModalBody>
|
|
190
|
+
</Modal>
|
|
191
|
+
</OverlayProvider>
|
|
192
|
+
)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export const BottomSheet: Story<ModalProps> = BottomSheetStory.bind({})
|