@ltht-react/input 2.0.190 → 2.0.191
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 +15 -15
- package/package.json +7 -7
- package/src/atoms/checkbox.tsx +92 -92
- package/src/atoms/daypicker.tsx +229 -229
- package/src/atoms/index.tsx +5 -5
- package/src/atoms/radio.tsx +47 -47
- package/src/atoms/text.tsx +57 -57
- package/src/index.tsx +2 -2
- package/src/molecules/daypicker-range.tsx +89 -89
- package/src/molecules/index.tsx +1 -1
package/README.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
# Input
|
|
2
|
-
|
|
3
|
-
<!-- STORY -->
|
|
4
|
-
|
|
5
|
-
### Import
|
|
6
|
-
|
|
7
|
-
```js
|
|
8
|
-
import Input from '@ltht-react/input'
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
### Usage
|
|
12
|
-
|
|
13
|
-
```jsx
|
|
14
|
-
<Input />
|
|
15
|
-
```
|
|
1
|
+
# Input
|
|
2
|
+
|
|
3
|
+
<!-- STORY -->
|
|
4
|
+
|
|
5
|
+
### Import
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
import Input from '@ltht-react/input'
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
### Usage
|
|
12
|
+
|
|
13
|
+
```jsx
|
|
14
|
+
<Input />
|
|
15
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ltht-react/input",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.191",
|
|
4
4
|
"description": "ltht-react styled Input component.",
|
|
5
5
|
"author": "LTHT",
|
|
6
6
|
"homepage": "",
|
|
@@ -28,11 +28,11 @@
|
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@emotion/react": "^11.0.0",
|
|
30
30
|
"@emotion/styled": "^11.0.0",
|
|
31
|
-
"@ltht-react/button": "^2.0.
|
|
32
|
-
"@ltht-react/icon": "^2.0.
|
|
33
|
-
"@ltht-react/styles": "^2.0.
|
|
34
|
-
"@ltht-react/types": "^2.0.
|
|
35
|
-
"@ltht-react/utils": "^2.0.
|
|
31
|
+
"@ltht-react/button": "^2.0.191",
|
|
32
|
+
"@ltht-react/icon": "^2.0.191",
|
|
33
|
+
"@ltht-react/styles": "^2.0.191",
|
|
34
|
+
"@ltht-react/types": "^2.0.191",
|
|
35
|
+
"@ltht-react/utils": "^2.0.191",
|
|
36
36
|
"@popperjs/core": "^2.11.5",
|
|
37
37
|
"date-fns": "^3.6.0",
|
|
38
38
|
"focus-trap-react": "^9.0.2",
|
|
@@ -41,5 +41,5 @@
|
|
|
41
41
|
"react-day-picker": "^8.9.1",
|
|
42
42
|
"react-popper": "^2.3.0"
|
|
43
43
|
},
|
|
44
|
-
"gitHead": "
|
|
44
|
+
"gitHead": "c5113953a19f4ed9ae82b545f073b5feb7d5a20b"
|
|
45
45
|
}
|
package/src/atoms/checkbox.tsx
CHANGED
|
@@ -1,92 +1,92 @@
|
|
|
1
|
-
import { FC, HTMLAttributes, InputHTMLAttributes } from 'react'
|
|
2
|
-
import styled from '@emotion/styled'
|
|
3
|
-
import { BADGE_COLOURS, INPUT_COLOURS } from '@ltht-react/styles'
|
|
4
|
-
import { css } from '@emotion/react'
|
|
5
|
-
|
|
6
|
-
const StyledCheckbox = styled.div<ConditionalStyles>`
|
|
7
|
-
display: flex;
|
|
8
|
-
align-items: center;
|
|
9
|
-
${(props) =>
|
|
10
|
-
props.checked &&
|
|
11
|
-
css`
|
|
12
|
-
background-color: ${INPUT_COLOURS.RADIO_SELECTED};
|
|
13
|
-
`}
|
|
14
|
-
`
|
|
15
|
-
|
|
16
|
-
const StyledInput = styled.input`
|
|
17
|
-
appearance: none;
|
|
18
|
-
height: 1.2em;
|
|
19
|
-
margin: 0;
|
|
20
|
-
margin-right: 0.5em;
|
|
21
|
-
position: relative;
|
|
22
|
-
width: 1em;
|
|
23
|
-
|
|
24
|
-
&:before {
|
|
25
|
-
border: solid 1px grey;
|
|
26
|
-
border-radius: 3px;
|
|
27
|
-
content: '';
|
|
28
|
-
display: inline-block;
|
|
29
|
-
height: 1em;
|
|
30
|
-
width: 1em;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
&:checked:before {
|
|
34
|
-
background-color: ${BADGE_COLOURS.PRIMARY};
|
|
35
|
-
content: '';
|
|
36
|
-
color: white;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
&:checked:after {
|
|
40
|
-
color: white;
|
|
41
|
-
content: '\\2714';
|
|
42
|
-
font-size: 0.8em;
|
|
43
|
-
height: 1em;
|
|
44
|
-
left: 0.25em;
|
|
45
|
-
position: absolute;
|
|
46
|
-
top: 0;
|
|
47
|
-
width: 1em;
|
|
48
|
-
}
|
|
49
|
-
`
|
|
50
|
-
|
|
51
|
-
const StyledLabel = styled.label`
|
|
52
|
-
display: inline-block;
|
|
53
|
-
margin-bottom: 0;
|
|
54
|
-
`
|
|
55
|
-
|
|
56
|
-
const Checkbox: FC<Props> = ({
|
|
57
|
-
id,
|
|
58
|
-
checked = false,
|
|
59
|
-
onChange,
|
|
60
|
-
children,
|
|
61
|
-
name,
|
|
62
|
-
parentDivAttributes = {},
|
|
63
|
-
labelAttributes = {},
|
|
64
|
-
...rest
|
|
65
|
-
}) => (
|
|
66
|
-
<StyledCheckbox checked={checked} {...parentDivAttributes}>
|
|
67
|
-
<StyledInput
|
|
68
|
-
id={id}
|
|
69
|
-
onChange={onChange}
|
|
70
|
-
type="checkbox"
|
|
71
|
-
checked={checked}
|
|
72
|
-
aria-checked={checked}
|
|
73
|
-
role="checkbox"
|
|
74
|
-
name={name}
|
|
75
|
-
{...rest}
|
|
76
|
-
/>
|
|
77
|
-
<StyledLabel htmlFor={id} {...labelAttributes}>
|
|
78
|
-
{children}
|
|
79
|
-
</StyledLabel>
|
|
80
|
-
</StyledCheckbox>
|
|
81
|
-
)
|
|
82
|
-
|
|
83
|
-
interface ConditionalStyles extends InputHTMLAttributes<HTMLInputElement> {
|
|
84
|
-
checked: boolean
|
|
85
|
-
}
|
|
86
|
-
interface Props extends InputHTMLAttributes<HTMLInputElement> {
|
|
87
|
-
children: React.ReactNode
|
|
88
|
-
parentDivAttributes?: HTMLAttributes<HTMLDivElement>
|
|
89
|
-
labelAttributes?: HTMLAttributes<HTMLLabelElement>
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export default Checkbox
|
|
1
|
+
import { FC, HTMLAttributes, InputHTMLAttributes } from 'react'
|
|
2
|
+
import styled from '@emotion/styled'
|
|
3
|
+
import { BADGE_COLOURS, INPUT_COLOURS } from '@ltht-react/styles'
|
|
4
|
+
import { css } from '@emotion/react'
|
|
5
|
+
|
|
6
|
+
const StyledCheckbox = styled.div<ConditionalStyles>`
|
|
7
|
+
display: flex;
|
|
8
|
+
align-items: center;
|
|
9
|
+
${(props) =>
|
|
10
|
+
props.checked &&
|
|
11
|
+
css`
|
|
12
|
+
background-color: ${INPUT_COLOURS.RADIO_SELECTED};
|
|
13
|
+
`}
|
|
14
|
+
`
|
|
15
|
+
|
|
16
|
+
const StyledInput = styled.input`
|
|
17
|
+
appearance: none;
|
|
18
|
+
height: 1.2em;
|
|
19
|
+
margin: 0;
|
|
20
|
+
margin-right: 0.5em;
|
|
21
|
+
position: relative;
|
|
22
|
+
width: 1em;
|
|
23
|
+
|
|
24
|
+
&:before {
|
|
25
|
+
border: solid 1px grey;
|
|
26
|
+
border-radius: 3px;
|
|
27
|
+
content: '';
|
|
28
|
+
display: inline-block;
|
|
29
|
+
height: 1em;
|
|
30
|
+
width: 1em;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
&:checked:before {
|
|
34
|
+
background-color: ${BADGE_COLOURS.PRIMARY};
|
|
35
|
+
content: '';
|
|
36
|
+
color: white;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
&:checked:after {
|
|
40
|
+
color: white;
|
|
41
|
+
content: '\\2714';
|
|
42
|
+
font-size: 0.8em;
|
|
43
|
+
height: 1em;
|
|
44
|
+
left: 0.25em;
|
|
45
|
+
position: absolute;
|
|
46
|
+
top: 0;
|
|
47
|
+
width: 1em;
|
|
48
|
+
}
|
|
49
|
+
`
|
|
50
|
+
|
|
51
|
+
const StyledLabel = styled.label`
|
|
52
|
+
display: inline-block;
|
|
53
|
+
margin-bottom: 0;
|
|
54
|
+
`
|
|
55
|
+
|
|
56
|
+
const Checkbox: FC<Props> = ({
|
|
57
|
+
id,
|
|
58
|
+
checked = false,
|
|
59
|
+
onChange,
|
|
60
|
+
children,
|
|
61
|
+
name,
|
|
62
|
+
parentDivAttributes = {},
|
|
63
|
+
labelAttributes = {},
|
|
64
|
+
...rest
|
|
65
|
+
}) => (
|
|
66
|
+
<StyledCheckbox checked={checked} {...parentDivAttributes}>
|
|
67
|
+
<StyledInput
|
|
68
|
+
id={id}
|
|
69
|
+
onChange={onChange}
|
|
70
|
+
type="checkbox"
|
|
71
|
+
checked={checked}
|
|
72
|
+
aria-checked={checked}
|
|
73
|
+
role="checkbox"
|
|
74
|
+
name={name}
|
|
75
|
+
{...rest}
|
|
76
|
+
/>
|
|
77
|
+
<StyledLabel htmlFor={id} {...labelAttributes}>
|
|
78
|
+
{children}
|
|
79
|
+
</StyledLabel>
|
|
80
|
+
</StyledCheckbox>
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
interface ConditionalStyles extends InputHTMLAttributes<HTMLInputElement> {
|
|
84
|
+
checked: boolean
|
|
85
|
+
}
|
|
86
|
+
interface Props extends InputHTMLAttributes<HTMLInputElement> {
|
|
87
|
+
children: React.ReactNode
|
|
88
|
+
parentDivAttributes?: HTMLAttributes<HTMLDivElement>
|
|
89
|
+
labelAttributes?: HTMLAttributes<HTMLLabelElement>
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export default Checkbox
|
package/src/atoms/daypicker.tsx
CHANGED
|
@@ -1,229 +1,229 @@
|
|
|
1
|
-
import { ChangeEventHandler, FC, HTMLAttributes, MouseEventHandler, useRef, useState } from 'react'
|
|
2
|
-
import styled from '@emotion/styled'
|
|
3
|
-
import { DayPicker, SelectSingleEventHandler, DateBefore, Matcher, DateAfter } from 'react-day-picker'
|
|
4
|
-
import { BTN_COLOURS, TEXT_COLOURS, TRANSLUCENT_DARK_BLUE } from '@ltht-react/styles'
|
|
5
|
-
import { Button } from '@ltht-react/button'
|
|
6
|
-
import { usePopper } from 'react-popper'
|
|
7
|
-
import { format, isValid, parse } from 'date-fns'
|
|
8
|
-
import FocusTrap from 'focus-trap-react'
|
|
9
|
-
import Icon from '@ltht-react/icon'
|
|
10
|
-
|
|
11
|
-
const StyledDialogSheet = styled.div`
|
|
12
|
-
z-index: 1;
|
|
13
|
-
background: white;
|
|
14
|
-
border-radius: 4px;
|
|
15
|
-
box-shadow: 0px 1px 10px rgba(0, 0, 0, 0.04), 0px 4px 5px rgba(0, 0, 0, 0.06), 0px 2px 4px -1px rgba(0, 0, 0, 0.09);
|
|
16
|
-
`
|
|
17
|
-
|
|
18
|
-
const DayPickerLabel = styled.small`
|
|
19
|
-
color: ${TEXT_COLOURS.SECONDARY.LIGHTER25};
|
|
20
|
-
`
|
|
21
|
-
|
|
22
|
-
const DayPickerInput = styled.input`
|
|
23
|
-
:focus-visible {
|
|
24
|
-
outline-color: ${BTN_COLOURS.PRIMARY.VALUE};
|
|
25
|
-
outline-style: auto;
|
|
26
|
-
outline-width: 1px;
|
|
27
|
-
}
|
|
28
|
-
`
|
|
29
|
-
|
|
30
|
-
const InputContainer = styled.div<InputProps>`
|
|
31
|
-
display: flex;
|
|
32
|
-
margin-right: ${({ showIcon }) => (showIcon ? '0rem;' : '2rem;')};
|
|
33
|
-
height: 1.5rem;
|
|
34
|
-
`
|
|
35
|
-
|
|
36
|
-
const StyledButton = styled(Button)`
|
|
37
|
-
position: relative;
|
|
38
|
-
right: 2rem;
|
|
39
|
-
width: 2rem !important;
|
|
40
|
-
`
|
|
41
|
-
|
|
42
|
-
const StyledDayPicker = styled(DayPicker)`
|
|
43
|
-
button.rdp-button:hover:not([aria-disabled='true']) {
|
|
44
|
-
background-color: ${TRANSLUCENT_DARK_BLUE};
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
button.rdp-day_selected: not([aria-disabled= 'true' ]), button.rdp-day_selected:not([aria-disabled='true']),
|
|
48
|
-
button.rdp-day_selected:focus:not([aria-disabled='true']),
|
|
49
|
-
button.rdp-day_selected:active:not([aria-disabled='true']),
|
|
50
|
-
button.rdp-day_selected:hover:not([aria-disabled='true']) {
|
|
51
|
-
color: white;
|
|
52
|
-
background-color: ${BTN_COLOURS.PRIMARY.VALUE};
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
button.rdp-button:focus, button.rdp-button:active {
|
|
56
|
-
border-color: ${BTN_COLOURS.PRIMARY.VALUE};
|
|
57
|
-
}
|
|
58
|
-
`
|
|
59
|
-
|
|
60
|
-
const Daypicker: FC<DaypickerProps> = ({
|
|
61
|
-
initialDate,
|
|
62
|
-
pickerOpen,
|
|
63
|
-
showIcon,
|
|
64
|
-
dayFormat,
|
|
65
|
-
label,
|
|
66
|
-
minDate,
|
|
67
|
-
maxDate,
|
|
68
|
-
navigationNumberOfMonths = 1,
|
|
69
|
-
changeHandler,
|
|
70
|
-
buttonHandler,
|
|
71
|
-
}) => {
|
|
72
|
-
const [selected, setSelected] = useState<Date | undefined>(initialDate)
|
|
73
|
-
const [inputValue, setInputValue] = useState<string>(format(initialDate ?? new Date(), dayFormat))
|
|
74
|
-
const [isPopperOpen, setIsPopperOpen] = useState(pickerOpen ?? false)
|
|
75
|
-
|
|
76
|
-
const disabledDays: Matcher[] = []
|
|
77
|
-
|
|
78
|
-
if (minDate) {
|
|
79
|
-
const disabledBefore: DateBefore = {
|
|
80
|
-
before: minDate,
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
disabledDays.push(disabledBefore)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (maxDate) {
|
|
87
|
-
const disabledAfter: DateAfter = {
|
|
88
|
-
after: maxDate,
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
disabledDays.push(disabledAfter)
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const popperRef = useRef<HTMLDivElement>(null)
|
|
95
|
-
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null)
|
|
96
|
-
|
|
97
|
-
const popper = usePopper(popperRef.current, popperElement, {
|
|
98
|
-
placement: 'bottom-start',
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
const closePopper = () => {
|
|
102
|
-
setIsPopperOpen(false)
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const selectDate = (date: Date | undefined) => {
|
|
106
|
-
setSelected(date)
|
|
107
|
-
changeHandler && changeHandler(date)
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const handleInputChange: ChangeEventHandler<HTMLInputElement> = (e) => {
|
|
111
|
-
setInputValue(e.currentTarget.value)
|
|
112
|
-
const date = parse(e.currentTarget.value, dayFormat, new Date())
|
|
113
|
-
if (isValid(date)) {
|
|
114
|
-
selectDate(date)
|
|
115
|
-
} else {
|
|
116
|
-
selectDate(undefined)
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const handleSelect: SelectSingleEventHandler = (_day, selectedDay, _activeModifiers, _e) => {
|
|
121
|
-
if (_activeModifiers.disabled) {
|
|
122
|
-
return
|
|
123
|
-
}
|
|
124
|
-
selectDate(selectedDay)
|
|
125
|
-
if (selectedDay) {
|
|
126
|
-
setInputValue(format(selectedDay, dayFormat))
|
|
127
|
-
closePopper()
|
|
128
|
-
} else {
|
|
129
|
-
setInputValue('')
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const onInputClick: MouseEventHandler<HTMLInputElement> = () => {
|
|
134
|
-
setIsPopperOpen(true)
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const onButtonClick: MouseEventHandler<HTMLButtonElement> = () => {
|
|
138
|
-
const popperOpen = !isPopperOpen
|
|
139
|
-
setIsPopperOpen(popperOpen)
|
|
140
|
-
buttonHandler && buttonHandler(popperOpen)
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
return (
|
|
144
|
-
<FocusTrap
|
|
145
|
-
active={isPopperOpen}
|
|
146
|
-
focusTrapOptions={{
|
|
147
|
-
tabbableOptions: {
|
|
148
|
-
displayCheck: 'none',
|
|
149
|
-
},
|
|
150
|
-
initialFocus: false,
|
|
151
|
-
allowOutsideClick: false,
|
|
152
|
-
clickOutsideDeactivates: true,
|
|
153
|
-
onDeactivate: closePopper,
|
|
154
|
-
}}
|
|
155
|
-
>
|
|
156
|
-
<div>
|
|
157
|
-
{label && <DayPickerLabel>{label}</DayPickerLabel>}
|
|
158
|
-
<InputContainer ref={popperRef} showIcon={showIcon}>
|
|
159
|
-
<DayPickerInput
|
|
160
|
-
type="text"
|
|
161
|
-
readOnly
|
|
162
|
-
placeholder={format(initialDate ?? new Date(), dayFormat)}
|
|
163
|
-
value={inputValue}
|
|
164
|
-
onChange={handleInputChange}
|
|
165
|
-
onClick={!showIcon ? onInputClick : undefined}
|
|
166
|
-
/>
|
|
167
|
-
{showIcon && (
|
|
168
|
-
<StyledButton
|
|
169
|
-
type="button"
|
|
170
|
-
icon={<Icon type="calendar" size="medium" />}
|
|
171
|
-
iconPlacement="center"
|
|
172
|
-
onClick={onButtonClick}
|
|
173
|
-
/>
|
|
174
|
-
)}
|
|
175
|
-
</InputContainer>
|
|
176
|
-
{isPopperOpen && (
|
|
177
|
-
<StyledDialogSheet
|
|
178
|
-
tabIndex={-1}
|
|
179
|
-
style={popper.styles.popper}
|
|
180
|
-
{...popper.attributes.popper}
|
|
181
|
-
ref={setPopperElement}
|
|
182
|
-
role="dialog"
|
|
183
|
-
>
|
|
184
|
-
<StyledDayPicker
|
|
185
|
-
initialFocus={isPopperOpen}
|
|
186
|
-
mode="single"
|
|
187
|
-
defaultMonth={selected}
|
|
188
|
-
selected={selected}
|
|
189
|
-
onSelect={handleSelect}
|
|
190
|
-
fromMonth={minDate}
|
|
191
|
-
toMonth={maxDate}
|
|
192
|
-
disabled={disabledDays}
|
|
193
|
-
numberOfMonths={navigationNumberOfMonths}
|
|
194
|
-
pagedNavigation
|
|
195
|
-
/>
|
|
196
|
-
</StyledDialogSheet>
|
|
197
|
-
)}
|
|
198
|
-
</div>
|
|
199
|
-
</FocusTrap>
|
|
200
|
-
)
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
interface InputProps extends HTMLAttributes<HTMLDivElement> {
|
|
204
|
-
showIcon: boolean
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
export interface DaypickerProps extends HTMLAttributes<HTMLDivElement> {
|
|
208
|
-
initialDate?: Date
|
|
209
|
-
pickerOpen?: boolean | undefined
|
|
210
|
-
showIcon: boolean
|
|
211
|
-
dayFormat: string
|
|
212
|
-
label?: string
|
|
213
|
-
minDate?: Date | undefined
|
|
214
|
-
maxDate?: Date | undefined
|
|
215
|
-
/**
|
|
216
|
-
* The number of displayed months in navigation. Defaults to `1`.
|
|
217
|
-
*/
|
|
218
|
-
navigationNumberOfMonths?: number | undefined
|
|
219
|
-
/**
|
|
220
|
-
* Executes whenever a day is selected from picker
|
|
221
|
-
*/
|
|
222
|
-
changeHandler?: (day: Date | undefined) => void | undefined
|
|
223
|
-
/**
|
|
224
|
-
* Executes when the button icon clicked
|
|
225
|
-
*/
|
|
226
|
-
buttonHandler?: (datepickerOpen: boolean) => void | undefined
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
export default Daypicker
|
|
1
|
+
import { ChangeEventHandler, FC, HTMLAttributes, MouseEventHandler, useRef, useState } from 'react'
|
|
2
|
+
import styled from '@emotion/styled'
|
|
3
|
+
import { DayPicker, SelectSingleEventHandler, DateBefore, Matcher, DateAfter } from 'react-day-picker'
|
|
4
|
+
import { BTN_COLOURS, TEXT_COLOURS, TRANSLUCENT_DARK_BLUE } from '@ltht-react/styles'
|
|
5
|
+
import { Button } from '@ltht-react/button'
|
|
6
|
+
import { usePopper } from 'react-popper'
|
|
7
|
+
import { format, isValid, parse } from 'date-fns'
|
|
8
|
+
import FocusTrap from 'focus-trap-react'
|
|
9
|
+
import Icon from '@ltht-react/icon'
|
|
10
|
+
|
|
11
|
+
const StyledDialogSheet = styled.div`
|
|
12
|
+
z-index: 1;
|
|
13
|
+
background: white;
|
|
14
|
+
border-radius: 4px;
|
|
15
|
+
box-shadow: 0px 1px 10px rgba(0, 0, 0, 0.04), 0px 4px 5px rgba(0, 0, 0, 0.06), 0px 2px 4px -1px rgba(0, 0, 0, 0.09);
|
|
16
|
+
`
|
|
17
|
+
|
|
18
|
+
const DayPickerLabel = styled.small`
|
|
19
|
+
color: ${TEXT_COLOURS.SECONDARY.LIGHTER25};
|
|
20
|
+
`
|
|
21
|
+
|
|
22
|
+
const DayPickerInput = styled.input`
|
|
23
|
+
:focus-visible {
|
|
24
|
+
outline-color: ${BTN_COLOURS.PRIMARY.VALUE};
|
|
25
|
+
outline-style: auto;
|
|
26
|
+
outline-width: 1px;
|
|
27
|
+
}
|
|
28
|
+
`
|
|
29
|
+
|
|
30
|
+
const InputContainer = styled.div<InputProps>`
|
|
31
|
+
display: flex;
|
|
32
|
+
margin-right: ${({ showIcon }) => (showIcon ? '0rem;' : '2rem;')};
|
|
33
|
+
height: 1.5rem;
|
|
34
|
+
`
|
|
35
|
+
|
|
36
|
+
const StyledButton = styled(Button)`
|
|
37
|
+
position: relative;
|
|
38
|
+
right: 2rem;
|
|
39
|
+
width: 2rem !important;
|
|
40
|
+
`
|
|
41
|
+
|
|
42
|
+
const StyledDayPicker = styled(DayPicker)`
|
|
43
|
+
button.rdp-button:hover:not([aria-disabled='true']) {
|
|
44
|
+
background-color: ${TRANSLUCENT_DARK_BLUE};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
button.rdp-day_selected: not([aria-disabled= 'true' ]), button.rdp-day_selected:not([aria-disabled='true']),
|
|
48
|
+
button.rdp-day_selected:focus:not([aria-disabled='true']),
|
|
49
|
+
button.rdp-day_selected:active:not([aria-disabled='true']),
|
|
50
|
+
button.rdp-day_selected:hover:not([aria-disabled='true']) {
|
|
51
|
+
color: white;
|
|
52
|
+
background-color: ${BTN_COLOURS.PRIMARY.VALUE};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
button.rdp-button:focus, button.rdp-button:active {
|
|
56
|
+
border-color: ${BTN_COLOURS.PRIMARY.VALUE};
|
|
57
|
+
}
|
|
58
|
+
`
|
|
59
|
+
|
|
60
|
+
const Daypicker: FC<DaypickerProps> = ({
|
|
61
|
+
initialDate,
|
|
62
|
+
pickerOpen,
|
|
63
|
+
showIcon,
|
|
64
|
+
dayFormat,
|
|
65
|
+
label,
|
|
66
|
+
minDate,
|
|
67
|
+
maxDate,
|
|
68
|
+
navigationNumberOfMonths = 1,
|
|
69
|
+
changeHandler,
|
|
70
|
+
buttonHandler,
|
|
71
|
+
}) => {
|
|
72
|
+
const [selected, setSelected] = useState<Date | undefined>(initialDate)
|
|
73
|
+
const [inputValue, setInputValue] = useState<string>(format(initialDate ?? new Date(), dayFormat))
|
|
74
|
+
const [isPopperOpen, setIsPopperOpen] = useState(pickerOpen ?? false)
|
|
75
|
+
|
|
76
|
+
const disabledDays: Matcher[] = []
|
|
77
|
+
|
|
78
|
+
if (minDate) {
|
|
79
|
+
const disabledBefore: DateBefore = {
|
|
80
|
+
before: minDate,
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
disabledDays.push(disabledBefore)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (maxDate) {
|
|
87
|
+
const disabledAfter: DateAfter = {
|
|
88
|
+
after: maxDate,
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
disabledDays.push(disabledAfter)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const popperRef = useRef<HTMLDivElement>(null)
|
|
95
|
+
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null)
|
|
96
|
+
|
|
97
|
+
const popper = usePopper(popperRef.current, popperElement, {
|
|
98
|
+
placement: 'bottom-start',
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
const closePopper = () => {
|
|
102
|
+
setIsPopperOpen(false)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const selectDate = (date: Date | undefined) => {
|
|
106
|
+
setSelected(date)
|
|
107
|
+
changeHandler && changeHandler(date)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const handleInputChange: ChangeEventHandler<HTMLInputElement> = (e) => {
|
|
111
|
+
setInputValue(e.currentTarget.value)
|
|
112
|
+
const date = parse(e.currentTarget.value, dayFormat, new Date())
|
|
113
|
+
if (isValid(date)) {
|
|
114
|
+
selectDate(date)
|
|
115
|
+
} else {
|
|
116
|
+
selectDate(undefined)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const handleSelect: SelectSingleEventHandler = (_day, selectedDay, _activeModifiers, _e) => {
|
|
121
|
+
if (_activeModifiers.disabled) {
|
|
122
|
+
return
|
|
123
|
+
}
|
|
124
|
+
selectDate(selectedDay)
|
|
125
|
+
if (selectedDay) {
|
|
126
|
+
setInputValue(format(selectedDay, dayFormat))
|
|
127
|
+
closePopper()
|
|
128
|
+
} else {
|
|
129
|
+
setInputValue('')
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const onInputClick: MouseEventHandler<HTMLInputElement> = () => {
|
|
134
|
+
setIsPopperOpen(true)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const onButtonClick: MouseEventHandler<HTMLButtonElement> = () => {
|
|
138
|
+
const popperOpen = !isPopperOpen
|
|
139
|
+
setIsPopperOpen(popperOpen)
|
|
140
|
+
buttonHandler && buttonHandler(popperOpen)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return (
|
|
144
|
+
<FocusTrap
|
|
145
|
+
active={isPopperOpen}
|
|
146
|
+
focusTrapOptions={{
|
|
147
|
+
tabbableOptions: {
|
|
148
|
+
displayCheck: 'none',
|
|
149
|
+
},
|
|
150
|
+
initialFocus: false,
|
|
151
|
+
allowOutsideClick: false,
|
|
152
|
+
clickOutsideDeactivates: true,
|
|
153
|
+
onDeactivate: closePopper,
|
|
154
|
+
}}
|
|
155
|
+
>
|
|
156
|
+
<div>
|
|
157
|
+
{label && <DayPickerLabel>{label}</DayPickerLabel>}
|
|
158
|
+
<InputContainer ref={popperRef} showIcon={showIcon}>
|
|
159
|
+
<DayPickerInput
|
|
160
|
+
type="text"
|
|
161
|
+
readOnly
|
|
162
|
+
placeholder={format(initialDate ?? new Date(), dayFormat)}
|
|
163
|
+
value={inputValue}
|
|
164
|
+
onChange={handleInputChange}
|
|
165
|
+
onClick={!showIcon ? onInputClick : undefined}
|
|
166
|
+
/>
|
|
167
|
+
{showIcon && (
|
|
168
|
+
<StyledButton
|
|
169
|
+
type="button"
|
|
170
|
+
icon={<Icon type="calendar" size="medium" />}
|
|
171
|
+
iconPlacement="center"
|
|
172
|
+
onClick={onButtonClick}
|
|
173
|
+
/>
|
|
174
|
+
)}
|
|
175
|
+
</InputContainer>
|
|
176
|
+
{isPopperOpen && (
|
|
177
|
+
<StyledDialogSheet
|
|
178
|
+
tabIndex={-1}
|
|
179
|
+
style={popper.styles.popper}
|
|
180
|
+
{...popper.attributes.popper}
|
|
181
|
+
ref={setPopperElement}
|
|
182
|
+
role="dialog"
|
|
183
|
+
>
|
|
184
|
+
<StyledDayPicker
|
|
185
|
+
initialFocus={isPopperOpen}
|
|
186
|
+
mode="single"
|
|
187
|
+
defaultMonth={selected}
|
|
188
|
+
selected={selected}
|
|
189
|
+
onSelect={handleSelect}
|
|
190
|
+
fromMonth={minDate}
|
|
191
|
+
toMonth={maxDate}
|
|
192
|
+
disabled={disabledDays}
|
|
193
|
+
numberOfMonths={navigationNumberOfMonths}
|
|
194
|
+
pagedNavigation
|
|
195
|
+
/>
|
|
196
|
+
</StyledDialogSheet>
|
|
197
|
+
)}
|
|
198
|
+
</div>
|
|
199
|
+
</FocusTrap>
|
|
200
|
+
)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
interface InputProps extends HTMLAttributes<HTMLDivElement> {
|
|
204
|
+
showIcon: boolean
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export interface DaypickerProps extends HTMLAttributes<HTMLDivElement> {
|
|
208
|
+
initialDate?: Date
|
|
209
|
+
pickerOpen?: boolean | undefined
|
|
210
|
+
showIcon: boolean
|
|
211
|
+
dayFormat: string
|
|
212
|
+
label?: string
|
|
213
|
+
minDate?: Date | undefined
|
|
214
|
+
maxDate?: Date | undefined
|
|
215
|
+
/**
|
|
216
|
+
* The number of displayed months in navigation. Defaults to `1`.
|
|
217
|
+
*/
|
|
218
|
+
navigationNumberOfMonths?: number | undefined
|
|
219
|
+
/**
|
|
220
|
+
* Executes whenever a day is selected from picker
|
|
221
|
+
*/
|
|
222
|
+
changeHandler?: (day: Date | undefined) => void | undefined
|
|
223
|
+
/**
|
|
224
|
+
* Executes when the button icon clicked
|
|
225
|
+
*/
|
|
226
|
+
buttonHandler?: (datepickerOpen: boolean) => void | undefined
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export default Daypicker
|
package/src/atoms/index.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { default as RadioButton } from './radio'
|
|
2
|
-
export { default as Checkbox } from './checkbox'
|
|
3
|
-
export { default as TextInput } from './text'
|
|
4
|
-
export { default as Daypicker, DaypickerProps } from './daypicker'
|
|
5
|
-
export { default as Toggle } from './toggle'
|
|
1
|
+
export { default as RadioButton } from './radio'
|
|
2
|
+
export { default as Checkbox } from './checkbox'
|
|
3
|
+
export { default as TextInput } from './text'
|
|
4
|
+
export { default as Daypicker, DaypickerProps } from './daypicker'
|
|
5
|
+
export { default as Toggle } from './toggle'
|
package/src/atoms/radio.tsx
CHANGED
|
@@ -1,47 +1,47 @@
|
|
|
1
|
-
import { ChangeEvent, FC, HTMLAttributes } from 'react'
|
|
2
|
-
import styled from '@emotion/styled'
|
|
3
|
-
import { css, SerializedStyles } from '@emotion/react'
|
|
4
|
-
import { INPUT_COLOURS } from '@ltht-react/styles'
|
|
5
|
-
|
|
6
|
-
const StyledRadioButton = styled.div<StyledRadioButtonProps>`
|
|
7
|
-
padding-left: 1.25rem;
|
|
8
|
-
|
|
9
|
-
${({ checked }): ConditionalStyles =>
|
|
10
|
-
checked &&
|
|
11
|
-
css`
|
|
12
|
-
background-color: ${INPUT_COLOURS.RADIO_SELECTED};
|
|
13
|
-
`}
|
|
14
|
-
`
|
|
15
|
-
|
|
16
|
-
const StyledInput = styled.input`
|
|
17
|
-
position: absolute;
|
|
18
|
-
margin-top: 0.15rem;
|
|
19
|
-
margin-left: -1.25rem;
|
|
20
|
-
`
|
|
21
|
-
|
|
22
|
-
const StyledLabel = styled.label`
|
|
23
|
-
display: inline-block;
|
|
24
|
-
margin-bottom: 0;
|
|
25
|
-
`
|
|
26
|
-
|
|
27
|
-
const RadioButton: FC<Props> = ({ id, value, checked = false, label, changeHandler, ...rest }) => (
|
|
28
|
-
<StyledRadioButton checked={checked} {...rest}>
|
|
29
|
-
<StyledInput id={id} onChange={changeHandler} value={value} type="radio" checked={checked} />
|
|
30
|
-
<StyledLabel htmlFor={id}>{label}</StyledLabel>
|
|
31
|
-
</StyledRadioButton>
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
type ConditionalStyles = SerializedStyles | false
|
|
35
|
-
|
|
36
|
-
interface StyledRadioButtonProps {
|
|
37
|
-
checked: boolean
|
|
38
|
-
}
|
|
39
|
-
interface Props extends HTMLAttributes<HTMLDivElement> {
|
|
40
|
-
id: string
|
|
41
|
-
value: string
|
|
42
|
-
checked?: boolean
|
|
43
|
-
label: string
|
|
44
|
-
changeHandler(e: ChangeEvent<HTMLInputElement>): void
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export default RadioButton
|
|
1
|
+
import { ChangeEvent, FC, HTMLAttributes } from 'react'
|
|
2
|
+
import styled from '@emotion/styled'
|
|
3
|
+
import { css, SerializedStyles } from '@emotion/react'
|
|
4
|
+
import { INPUT_COLOURS } from '@ltht-react/styles'
|
|
5
|
+
|
|
6
|
+
const StyledRadioButton = styled.div<StyledRadioButtonProps>`
|
|
7
|
+
padding-left: 1.25rem;
|
|
8
|
+
|
|
9
|
+
${({ checked }): ConditionalStyles =>
|
|
10
|
+
checked &&
|
|
11
|
+
css`
|
|
12
|
+
background-color: ${INPUT_COLOURS.RADIO_SELECTED};
|
|
13
|
+
`}
|
|
14
|
+
`
|
|
15
|
+
|
|
16
|
+
const StyledInput = styled.input`
|
|
17
|
+
position: absolute;
|
|
18
|
+
margin-top: 0.15rem;
|
|
19
|
+
margin-left: -1.25rem;
|
|
20
|
+
`
|
|
21
|
+
|
|
22
|
+
const StyledLabel = styled.label`
|
|
23
|
+
display: inline-block;
|
|
24
|
+
margin-bottom: 0;
|
|
25
|
+
`
|
|
26
|
+
|
|
27
|
+
const RadioButton: FC<Props> = ({ id, value, checked = false, label, changeHandler, ...rest }) => (
|
|
28
|
+
<StyledRadioButton checked={checked} {...rest}>
|
|
29
|
+
<StyledInput id={id} onChange={changeHandler} value={value} type="radio" checked={checked} />
|
|
30
|
+
<StyledLabel htmlFor={id}>{label}</StyledLabel>
|
|
31
|
+
</StyledRadioButton>
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
type ConditionalStyles = SerializedStyles | false
|
|
35
|
+
|
|
36
|
+
interface StyledRadioButtonProps {
|
|
37
|
+
checked: boolean
|
|
38
|
+
}
|
|
39
|
+
interface Props extends HTMLAttributes<HTMLDivElement> {
|
|
40
|
+
id: string
|
|
41
|
+
value: string
|
|
42
|
+
checked?: boolean
|
|
43
|
+
label: string
|
|
44
|
+
changeHandler(e: ChangeEvent<HTMLInputElement>): void
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export default RadioButton
|
package/src/atoms/text.tsx
CHANGED
|
@@ -1,57 +1,57 @@
|
|
|
1
|
-
import { FC, InputHTMLAttributes } from 'react'
|
|
2
|
-
import styled from '@emotion/styled'
|
|
3
|
-
import { DESKTOP_MINIMUM_MEDIA_QUERY, inputBaseStyles } from '@ltht-react/styles'
|
|
4
|
-
import Icon, { IconProps } from '@ltht-react/icon'
|
|
5
|
-
|
|
6
|
-
const TextInputContainer = styled.div`
|
|
7
|
-
width: 100%;
|
|
8
|
-
display: inline-block;
|
|
9
|
-
position: relative;
|
|
10
|
-
|
|
11
|
-
${DESKTOP_MINIMUM_MEDIA_QUERY} {
|
|
12
|
-
width: auto;
|
|
13
|
-
}
|
|
14
|
-
`
|
|
15
|
-
|
|
16
|
-
const StyledTextInput: FC<IStyledTextInputProps> = styled.input`
|
|
17
|
-
width: 100%;
|
|
18
|
-
${inputBaseStyles}
|
|
19
|
-
padding: 0.5rem 1rem 0.5rem 1.75rem;
|
|
20
|
-
padding-left: ${({ hasIcon }: IStyledTextInputProps) => (hasIcon ? '1.75rem' : '0.5rem')};
|
|
21
|
-
&::placeholder {
|
|
22
|
-
color: #98a4ad;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
${DESKTOP_MINIMUM_MEDIA_QUERY} {
|
|
26
|
-
width: auto;
|
|
27
|
-
}
|
|
28
|
-
`
|
|
29
|
-
|
|
30
|
-
const StyledIcon = styled(Icon)`
|
|
31
|
-
position: absolute;
|
|
32
|
-
left: 8px;
|
|
33
|
-
top: 50%;
|
|
34
|
-
transform: translateY(-50%);
|
|
35
|
-
color: #98a4ad;
|
|
36
|
-
|
|
37
|
-
${DESKTOP_MINIMUM_MEDIA_QUERY} {
|
|
38
|
-
width: auto;
|
|
39
|
-
}
|
|
40
|
-
`
|
|
41
|
-
|
|
42
|
-
const TextInput: FC<ITextInputProps> = ({ placeholder, icon, ...rest }) => (
|
|
43
|
-
<TextInputContainer>
|
|
44
|
-
{icon && <StyledIcon {...icon} />}
|
|
45
|
-
<StyledTextInput type="text" placeholder={placeholder} {...rest} hasIcon={icon !== undefined} />
|
|
46
|
-
</TextInputContainer>
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
interface ITextInputProps extends InputHTMLAttributes<HTMLInputElement> {
|
|
50
|
-
icon?: IconProps
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
interface IStyledTextInputProps extends ITextInputProps {
|
|
54
|
-
hasIcon: boolean
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export default TextInput
|
|
1
|
+
import { FC, InputHTMLAttributes } from 'react'
|
|
2
|
+
import styled from '@emotion/styled'
|
|
3
|
+
import { DESKTOP_MINIMUM_MEDIA_QUERY, inputBaseStyles } from '@ltht-react/styles'
|
|
4
|
+
import Icon, { IconProps } from '@ltht-react/icon'
|
|
5
|
+
|
|
6
|
+
const TextInputContainer = styled.div`
|
|
7
|
+
width: 100%;
|
|
8
|
+
display: inline-block;
|
|
9
|
+
position: relative;
|
|
10
|
+
|
|
11
|
+
${DESKTOP_MINIMUM_MEDIA_QUERY} {
|
|
12
|
+
width: auto;
|
|
13
|
+
}
|
|
14
|
+
`
|
|
15
|
+
|
|
16
|
+
const StyledTextInput: FC<IStyledTextInputProps> = styled.input`
|
|
17
|
+
width: 100%;
|
|
18
|
+
${inputBaseStyles}
|
|
19
|
+
padding: 0.5rem 1rem 0.5rem 1.75rem;
|
|
20
|
+
padding-left: ${({ hasIcon }: IStyledTextInputProps) => (hasIcon ? '1.75rem' : '0.5rem')};
|
|
21
|
+
&::placeholder {
|
|
22
|
+
color: #98a4ad;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
${DESKTOP_MINIMUM_MEDIA_QUERY} {
|
|
26
|
+
width: auto;
|
|
27
|
+
}
|
|
28
|
+
`
|
|
29
|
+
|
|
30
|
+
const StyledIcon = styled(Icon)`
|
|
31
|
+
position: absolute;
|
|
32
|
+
left: 8px;
|
|
33
|
+
top: 50%;
|
|
34
|
+
transform: translateY(-50%);
|
|
35
|
+
color: #98a4ad;
|
|
36
|
+
|
|
37
|
+
${DESKTOP_MINIMUM_MEDIA_QUERY} {
|
|
38
|
+
width: auto;
|
|
39
|
+
}
|
|
40
|
+
`
|
|
41
|
+
|
|
42
|
+
const TextInput: FC<ITextInputProps> = ({ placeholder, icon, ...rest }) => (
|
|
43
|
+
<TextInputContainer>
|
|
44
|
+
{icon && <StyledIcon {...icon} />}
|
|
45
|
+
<StyledTextInput type="text" placeholder={placeholder} {...rest} hasIcon={icon !== undefined} />
|
|
46
|
+
</TextInputContainer>
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
interface ITextInputProps extends InputHTMLAttributes<HTMLInputElement> {
|
|
50
|
+
icon?: IconProps
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface IStyledTextInputProps extends ITextInputProps {
|
|
54
|
+
hasIcon: boolean
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export default TextInput
|
package/src/index.tsx
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from './atoms'
|
|
2
|
-
export * from './molecules'
|
|
1
|
+
export * from './atoms'
|
|
2
|
+
export * from './molecules'
|
|
@@ -1,89 +1,89 @@
|
|
|
1
|
-
import styled from '@emotion/styled'
|
|
2
|
-
import { FC, HTMLAttributes, useState } from 'react'
|
|
3
|
-
import { Daypicker, DaypickerProps } from '../atoms'
|
|
4
|
-
|
|
5
|
-
const RangeContainer = styled.div`
|
|
6
|
-
display: flex;
|
|
7
|
-
`
|
|
8
|
-
|
|
9
|
-
const PickerContainer = styled.div`
|
|
10
|
-
flex: 1;
|
|
11
|
-
`
|
|
12
|
-
|
|
13
|
-
const DaypickerRange: FC<Props> = ({ fromProps, toProps }) => {
|
|
14
|
-
if (fromProps.minDate && toProps.maxDate && fromProps.minDate > toProps.maxDate) {
|
|
15
|
-
throw new Error(
|
|
16
|
-
`Cannot initialise DaypickerRange where the minDate: ${fromProps.minDate?.toDateString()} > maxDate: ${toProps.maxDate?.toDateString()} `
|
|
17
|
-
)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const [toMinDate, setToMinDate] = useState(fromProps.minDate)
|
|
21
|
-
const [fromMaxDate, setFromMaxDate] = useState(toProps.maxDate)
|
|
22
|
-
const [fromPickerOpen, setFromPickerOpen] = useState(fromProps.pickerOpen ?? false)
|
|
23
|
-
const [toPickerOpen, setToPickerOpen] = useState(toProps.pickerOpen ?? false)
|
|
24
|
-
|
|
25
|
-
const fromChangeHandler = (date: Date | undefined) => {
|
|
26
|
-
fromProps.changeHandler && fromProps.changeHandler(date)
|
|
27
|
-
setToMinDate(date)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const toChangeHandler = (date: Date | undefined) => {
|
|
31
|
-
toProps.changeHandler && toProps.changeHandler(date)
|
|
32
|
-
setFromMaxDate(date)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const toButtonHandler = (toPickerOpen: boolean) => {
|
|
36
|
-
if (toPickerOpen) {
|
|
37
|
-
setFromPickerOpen(false)
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const fromButtonHandler = (fromPickerOpen: boolean) => {
|
|
42
|
-
if (fromPickerOpen) {
|
|
43
|
-
setToPickerOpen(false)
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return (
|
|
48
|
-
<RangeContainer>
|
|
49
|
-
<PickerContainer>
|
|
50
|
-
<Daypicker
|
|
51
|
-
initialDate={fromProps.initialDate}
|
|
52
|
-
showIcon={fromProps.showIcon}
|
|
53
|
-
pickerOpen={fromPickerOpen}
|
|
54
|
-
dayFormat={fromProps.dayFormat}
|
|
55
|
-
label={fromProps.label}
|
|
56
|
-
minDate={fromProps.minDate}
|
|
57
|
-
maxDate={fromMaxDate}
|
|
58
|
-
navigationNumberOfMonths={fromProps.navigationNumberOfMonths}
|
|
59
|
-
changeHandler={fromChangeHandler}
|
|
60
|
-
buttonHandler={fromButtonHandler}
|
|
61
|
-
/>
|
|
62
|
-
</PickerContainer>
|
|
63
|
-
<PickerContainer>
|
|
64
|
-
<Daypicker
|
|
65
|
-
initialDate={toProps.initialDate}
|
|
66
|
-
pickerOpen={toPickerOpen}
|
|
67
|
-
showIcon={toProps.showIcon}
|
|
68
|
-
dayFormat={toProps.dayFormat}
|
|
69
|
-
label={toProps.label}
|
|
70
|
-
minDate={toMinDate}
|
|
71
|
-
maxDate={toProps.maxDate}
|
|
72
|
-
navigationNumberOfMonths={toProps.navigationNumberOfMonths}
|
|
73
|
-
changeHandler={toChangeHandler}
|
|
74
|
-
buttonHandler={toButtonHandler}
|
|
75
|
-
/>
|
|
76
|
-
</PickerContainer>
|
|
77
|
-
</RangeContainer>
|
|
78
|
-
)
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export type fromDaypickerRangeProps = Omit<DaypickerProps, 'maxDate'>
|
|
82
|
-
export type toDaypickerRangeProps = Omit<DaypickerProps, 'minDate'>
|
|
83
|
-
|
|
84
|
-
interface Props extends HTMLAttributes<HTMLDivElement> {
|
|
85
|
-
fromProps: fromDaypickerRangeProps
|
|
86
|
-
toProps: toDaypickerRangeProps
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export default DaypickerRange
|
|
1
|
+
import styled from '@emotion/styled'
|
|
2
|
+
import { FC, HTMLAttributes, useState } from 'react'
|
|
3
|
+
import { Daypicker, DaypickerProps } from '../atoms'
|
|
4
|
+
|
|
5
|
+
const RangeContainer = styled.div`
|
|
6
|
+
display: flex;
|
|
7
|
+
`
|
|
8
|
+
|
|
9
|
+
const PickerContainer = styled.div`
|
|
10
|
+
flex: 1;
|
|
11
|
+
`
|
|
12
|
+
|
|
13
|
+
const DaypickerRange: FC<Props> = ({ fromProps, toProps }) => {
|
|
14
|
+
if (fromProps.minDate && toProps.maxDate && fromProps.minDate > toProps.maxDate) {
|
|
15
|
+
throw new Error(
|
|
16
|
+
`Cannot initialise DaypickerRange where the minDate: ${fromProps.minDate?.toDateString()} > maxDate: ${toProps.maxDate?.toDateString()} `
|
|
17
|
+
)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const [toMinDate, setToMinDate] = useState(fromProps.minDate)
|
|
21
|
+
const [fromMaxDate, setFromMaxDate] = useState(toProps.maxDate)
|
|
22
|
+
const [fromPickerOpen, setFromPickerOpen] = useState(fromProps.pickerOpen ?? false)
|
|
23
|
+
const [toPickerOpen, setToPickerOpen] = useState(toProps.pickerOpen ?? false)
|
|
24
|
+
|
|
25
|
+
const fromChangeHandler = (date: Date | undefined) => {
|
|
26
|
+
fromProps.changeHandler && fromProps.changeHandler(date)
|
|
27
|
+
setToMinDate(date)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const toChangeHandler = (date: Date | undefined) => {
|
|
31
|
+
toProps.changeHandler && toProps.changeHandler(date)
|
|
32
|
+
setFromMaxDate(date)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const toButtonHandler = (toPickerOpen: boolean) => {
|
|
36
|
+
if (toPickerOpen) {
|
|
37
|
+
setFromPickerOpen(false)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const fromButtonHandler = (fromPickerOpen: boolean) => {
|
|
42
|
+
if (fromPickerOpen) {
|
|
43
|
+
setToPickerOpen(false)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<RangeContainer>
|
|
49
|
+
<PickerContainer>
|
|
50
|
+
<Daypicker
|
|
51
|
+
initialDate={fromProps.initialDate}
|
|
52
|
+
showIcon={fromProps.showIcon}
|
|
53
|
+
pickerOpen={fromPickerOpen}
|
|
54
|
+
dayFormat={fromProps.dayFormat}
|
|
55
|
+
label={fromProps.label}
|
|
56
|
+
minDate={fromProps.minDate}
|
|
57
|
+
maxDate={fromMaxDate}
|
|
58
|
+
navigationNumberOfMonths={fromProps.navigationNumberOfMonths}
|
|
59
|
+
changeHandler={fromChangeHandler}
|
|
60
|
+
buttonHandler={fromButtonHandler}
|
|
61
|
+
/>
|
|
62
|
+
</PickerContainer>
|
|
63
|
+
<PickerContainer>
|
|
64
|
+
<Daypicker
|
|
65
|
+
initialDate={toProps.initialDate}
|
|
66
|
+
pickerOpen={toPickerOpen}
|
|
67
|
+
showIcon={toProps.showIcon}
|
|
68
|
+
dayFormat={toProps.dayFormat}
|
|
69
|
+
label={toProps.label}
|
|
70
|
+
minDate={toMinDate}
|
|
71
|
+
maxDate={toProps.maxDate}
|
|
72
|
+
navigationNumberOfMonths={toProps.navigationNumberOfMonths}
|
|
73
|
+
changeHandler={toChangeHandler}
|
|
74
|
+
buttonHandler={toButtonHandler}
|
|
75
|
+
/>
|
|
76
|
+
</PickerContainer>
|
|
77
|
+
</RangeContainer>
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export type fromDaypickerRangeProps = Omit<DaypickerProps, 'maxDate'>
|
|
82
|
+
export type toDaypickerRangeProps = Omit<DaypickerProps, 'minDate'>
|
|
83
|
+
|
|
84
|
+
interface Props extends HTMLAttributes<HTMLDivElement> {
|
|
85
|
+
fromProps: fromDaypickerRangeProps
|
|
86
|
+
toProps: toDaypickerRangeProps
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export default DaypickerRange
|
package/src/molecules/index.tsx
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { default as DaypickerRange, fromDaypickerRangeProps, toDaypickerRangeProps } from './daypicker-range'
|
|
1
|
+
export { default as DaypickerRange, fromDaypickerRangeProps, toDaypickerRangeProps } from './daypicker-range'
|