@planningcenter/tapestry-react 2.9.0-rc.1 → 2.9.0-rc.3
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/cjs/TimeField/TimeField.js +270 -222
- package/dist/cjs/TimeField/TimeField.test.js +31 -31
- package/dist/cjs/TimeField/legacy_TimeField.js +274 -0
- package/dist/cjs/TimeField/utils.js +6 -12
- package/dist/cjs/utils.js +9 -0
- package/dist/esm/TimeField/TimeField.js +259 -225
- package/dist/esm/TimeField/TimeField.test.js +31 -31
- package/dist/esm/TimeField/legacy_TimeField.js +271 -0
- package/dist/esm/TimeField/utils.js +6 -12
- package/dist/esm/utils.js +9 -1
- package/dist/types/TimeField/TimeField.d.ts +35 -0
- package/package.json +2 -2
- package/src/TimeField/TimeField.mdx +1 -1
- package/src/TimeField/TimeField.test.tsx +68 -52
- package/src/TimeField/TimeField.tsx +339 -0
- package/src/TimeField/{TimeField.js → legacy_TimeField.js} +2 -6
- package/src/TimeField/utils.js +13 -11
- package/src/utils.js +8 -0
|
@@ -15,11 +15,11 @@ interface TimeProps {
|
|
|
15
15
|
|
|
16
16
|
const TestApp = ({ value: initialValue, ...props }: TimeProps) => {
|
|
17
17
|
const [value, setValue] = useState(initialValue)
|
|
18
|
-
return <TimeField {...props
|
|
18
|
+
return <TimeField {...props} value={value} onChange={setValue} />
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
const setup = ({ hours, minutes, ...props }
|
|
22
|
-
const testApp = render(<TestApp {...props
|
|
21
|
+
const setup = ({ hours, minutes, ...props }: TimeProps) => {
|
|
22
|
+
const testApp = render(<TestApp {...props} value={[hours, minutes]} />)
|
|
23
23
|
const hourInput = testApp.queryByLabelText('Hours')
|
|
24
24
|
const minuteInput = testApp.queryByLabelText('Minutes')
|
|
25
25
|
const meridiemInput = testApp.queryByLabelText('AM/PM')
|
|
@@ -110,7 +110,11 @@ it('changes the meridiem on arrow key', () => {
|
|
|
110
110
|
})
|
|
111
111
|
|
|
112
112
|
it('does not change the meridiem on arrow key if arrow keys are ignored', () => {
|
|
113
|
-
const { meridiemInput } = setup({
|
|
113
|
+
const { meridiemInput } = setup({
|
|
114
|
+
hours: 13,
|
|
115
|
+
minutes: 42,
|
|
116
|
+
ignoredKeys: ['ArrowUp', 'ArrowDown'],
|
|
117
|
+
})
|
|
114
118
|
|
|
115
119
|
expect(meridiemInput).toHaveValue('PM')
|
|
116
120
|
|
|
@@ -122,79 +126,79 @@ it('does not change the meridiem on arrow key if arrow keys are ignored', () =>
|
|
|
122
126
|
})
|
|
123
127
|
|
|
124
128
|
it('increment hour on arrow up', () => {
|
|
125
|
-
const { hourInput } = setup(
|
|
129
|
+
const { hourInput } = setup({ hours: 1, minutes: 42 })
|
|
126
130
|
|
|
127
|
-
expect(hourInput).toHaveValue(
|
|
131
|
+
expect(hourInput).toHaveValue('01')
|
|
128
132
|
userEvent.type(hourInput, '{arrowup}')
|
|
129
|
-
expect(hourInput).toHaveValue(
|
|
133
|
+
expect(hourInput).toHaveValue('02')
|
|
130
134
|
})
|
|
131
135
|
|
|
132
136
|
it('increment hour if arrow up on minutes exceeds max value', () => {
|
|
133
|
-
const { hourInput, minuteInput } = setup(
|
|
137
|
+
const { hourInput, minuteInput } = setup({ hours: 1, minutes: 59 })
|
|
134
138
|
|
|
135
|
-
expect(hourInput).toHaveValue(
|
|
139
|
+
expect(hourInput).toHaveValue('01')
|
|
136
140
|
userEvent.type(minuteInput, '{arrowup}')
|
|
137
|
-
expect(hourInput).toHaveValue(
|
|
141
|
+
expect(hourInput).toHaveValue('02')
|
|
138
142
|
})
|
|
139
143
|
|
|
140
144
|
it('decrement hour on arrow down', () => {
|
|
141
|
-
const { hourInput } = setup(
|
|
145
|
+
const { hourInput } = setup({ hours: 2, minutes: 42 })
|
|
142
146
|
|
|
143
|
-
expect(hourInput).toHaveValue(
|
|
147
|
+
expect(hourInput).toHaveValue('02')
|
|
144
148
|
userEvent.type(hourInput, '{arrowdown}')
|
|
145
|
-
expect(hourInput).toHaveValue(
|
|
149
|
+
expect(hourInput).toHaveValue('01')
|
|
146
150
|
})
|
|
147
151
|
|
|
148
152
|
it('decrement hour if arrow down on minutes exceeds min value', () => {
|
|
149
|
-
const { hourInput, minuteInput } = setup(
|
|
153
|
+
const { hourInput, minuteInput } = setup({ hours: 2, minutes: 0 })
|
|
150
154
|
|
|
151
|
-
expect(hourInput).toHaveValue(
|
|
155
|
+
expect(hourInput).toHaveValue('02')
|
|
152
156
|
userEvent.type(minuteInput, '{arrowdown}')
|
|
153
|
-
expect(hourInput).toHaveValue(
|
|
157
|
+
expect(hourInput).toHaveValue('01')
|
|
154
158
|
})
|
|
155
159
|
|
|
156
160
|
it('increment minute on arrow up', () => {
|
|
157
|
-
const { minuteInput } = setup(
|
|
161
|
+
const { minuteInput } = setup({ hours: 1, minutes: 42 })
|
|
158
162
|
|
|
159
|
-
expect(minuteInput).toHaveValue(
|
|
163
|
+
expect(minuteInput).toHaveValue('42')
|
|
160
164
|
userEvent.type(minuteInput, '{arrowup}')
|
|
161
|
-
expect(minuteInput).toHaveValue(
|
|
165
|
+
expect(minuteInput).toHaveValue('43')
|
|
162
166
|
})
|
|
163
167
|
|
|
164
168
|
it('decrement minute on arrow down', () => {
|
|
165
|
-
const { minuteInput } = setup(
|
|
169
|
+
const { minuteInput } = setup({ hours: 2, minutes: 43 })
|
|
166
170
|
|
|
167
|
-
expect(minuteInput).toHaveValue(
|
|
171
|
+
expect(minuteInput).toHaveValue('43')
|
|
168
172
|
userEvent.type(minuteInput, '{arrowdown}')
|
|
169
|
-
expect(minuteInput).toHaveValue(
|
|
173
|
+
expect(minuteInput).toHaveValue('42')
|
|
170
174
|
})
|
|
171
175
|
|
|
172
176
|
it('set minute to min value if arrow up on minutes exceeds max value', () => {
|
|
173
|
-
const { minuteInput } = setup(
|
|
177
|
+
const { minuteInput } = setup({ hours: 1, minutes: 59 })
|
|
174
178
|
|
|
175
|
-
expect(minuteInput).toHaveValue(
|
|
179
|
+
expect(minuteInput).toHaveValue('59')
|
|
176
180
|
userEvent.type(minuteInput, '{arrowup}')
|
|
177
|
-
expect(minuteInput).toHaveValue(
|
|
181
|
+
expect(minuteInput).toHaveValue('00')
|
|
178
182
|
})
|
|
179
183
|
|
|
180
184
|
it('set minute to max value if arrow down on minutes exceeds min value', () => {
|
|
181
|
-
const { minuteInput } = setup(
|
|
185
|
+
const { minuteInput } = setup({ hours: 1, minutes: 0 })
|
|
182
186
|
|
|
183
|
-
expect(minuteInput).toHaveValue(
|
|
187
|
+
expect(minuteInput).toHaveValue('00')
|
|
184
188
|
userEvent.type(minuteInput, '{arrowdown}')
|
|
185
|
-
expect(minuteInput).toHaveValue(
|
|
189
|
+
expect(minuteInput).toHaveValue('59')
|
|
186
190
|
})
|
|
187
191
|
|
|
188
192
|
it('toggle meridiem if arrow up on hour exceeds eleven', () => {
|
|
189
|
-
const { hourInput, meridiemInput } = setup(
|
|
190
|
-
|
|
193
|
+
const { hourInput, meridiemInput } = setup({ hours: 11 })
|
|
194
|
+
|
|
191
195
|
expect(meridiemInput).toHaveValue('AM')
|
|
192
196
|
userEvent.type(hourInput, '{arrowup}')
|
|
193
197
|
expect(meridiemInput).toHaveValue('PM')
|
|
194
198
|
})
|
|
195
199
|
|
|
196
200
|
it('toggle meridiem if arrow down on hour exceeds min value', () => {
|
|
197
|
-
const { hourInput, meridiemInput } = setup(
|
|
201
|
+
const { hourInput, meridiemInput } = setup({ hours: 0 })
|
|
198
202
|
|
|
199
203
|
expect(meridiemInput).toHaveValue('AM')
|
|
200
204
|
userEvent.type(hourInput, '{arrowdown}')
|
|
@@ -202,60 +206,72 @@ it('toggle meridiem if arrow down on hour exceeds min value', () => {
|
|
|
202
206
|
})
|
|
203
207
|
|
|
204
208
|
it('set 12 hour clock to min value if arrow up on hour exceeds max value', () => {
|
|
205
|
-
const { hourInput } = setup(
|
|
209
|
+
const { hourInput } = setup({ hours: 12 })
|
|
206
210
|
|
|
207
|
-
expect(hourInput).toHaveValue(
|
|
211
|
+
expect(hourInput).toHaveValue('12')
|
|
208
212
|
userEvent.type(hourInput, '{arrowup}')
|
|
209
|
-
expect(hourInput).toHaveValue(
|
|
213
|
+
expect(hourInput).toHaveValue('01')
|
|
210
214
|
})
|
|
211
215
|
|
|
212
216
|
it('set 24 hour clock to min value if arrow up on hour exceeds max value', () => {
|
|
213
|
-
const { hourInput } = setup(
|
|
217
|
+
const { hourInput } = setup({ hours: 23, twelveHourClock: false })
|
|
214
218
|
|
|
215
|
-
expect(hourInput).toHaveValue(
|
|
219
|
+
expect(hourInput).toHaveValue('23')
|
|
216
220
|
userEvent.type(hourInput, '{arrowup}')
|
|
217
|
-
expect(hourInput).toHaveValue(
|
|
221
|
+
expect(hourInput).toHaveValue('00')
|
|
218
222
|
})
|
|
219
223
|
|
|
220
224
|
it('set 12 hour clock to max value if arrow down on hour exceeds min value', () => {
|
|
221
|
-
const { hourInput } = setup(
|
|
225
|
+
const { hourInput } = setup({ hours: 1 })
|
|
222
226
|
|
|
223
|
-
expect(hourInput).toHaveValue(
|
|
227
|
+
expect(hourInput).toHaveValue('01')
|
|
224
228
|
userEvent.type(hourInput, '{arrowdown}')
|
|
225
|
-
expect(hourInput).toHaveValue(
|
|
229
|
+
expect(hourInput).toHaveValue('12')
|
|
226
230
|
})
|
|
227
231
|
|
|
228
232
|
it('set 24 hour clock to max value if arrow down on hour exceeds min value', () => {
|
|
229
|
-
const { hourInput } = setup(
|
|
233
|
+
const { hourInput } = setup({ hours: 0, twelveHourClock: false })
|
|
230
234
|
|
|
231
|
-
expect(hourInput).toHaveValue(
|
|
235
|
+
expect(hourInput).toHaveValue('00')
|
|
232
236
|
userEvent.type(hourInput, '{arrowdown}')
|
|
233
|
-
expect(hourInput).toHaveValue(
|
|
237
|
+
expect(hourInput).toHaveValue('23')
|
|
234
238
|
})
|
|
235
239
|
|
|
236
240
|
it('does not change the value of hours on arrow up or arrow down if ignored', () => {
|
|
237
|
-
const { hourInput } = setup(
|
|
241
|
+
const { hourInput } = setup({
|
|
242
|
+
hours: 1,
|
|
243
|
+
minutes: 42,
|
|
244
|
+
ignoredKeys: ['ArrowUp', 'ArrowDown'],
|
|
245
|
+
})
|
|
238
246
|
|
|
239
|
-
expect(hourInput).toHaveValue(
|
|
247
|
+
expect(hourInput).toHaveValue('01')
|
|
240
248
|
userEvent.type(hourInput, '{arrowup}')
|
|
241
|
-
expect(hourInput).toHaveValue(
|
|
249
|
+
expect(hourInput).toHaveValue('01')
|
|
242
250
|
userEvent.type(hourInput, '{arrowdown}')
|
|
243
|
-
expect(hourInput).toHaveValue(
|
|
251
|
+
expect(hourInput).toHaveValue('01')
|
|
244
252
|
})
|
|
245
253
|
|
|
246
254
|
it('does not change the value of minutes on arrow up or arrow down if ignored', () => {
|
|
247
|
-
const { minuteInput } = setup(
|
|
255
|
+
const { minuteInput } = setup({
|
|
256
|
+
hours: 1,
|
|
257
|
+
minutes: 42,
|
|
258
|
+
ignoredKeys: ['ArrowUp', 'ArrowDown'],
|
|
259
|
+
})
|
|
248
260
|
|
|
249
|
-
expect(minuteInput).toHaveValue(
|
|
261
|
+
expect(minuteInput).toHaveValue('42')
|
|
250
262
|
userEvent.type(minuteInput, '{arrowup}')
|
|
251
|
-
expect(minuteInput).toHaveValue(
|
|
263
|
+
expect(minuteInput).toHaveValue('42')
|
|
252
264
|
userEvent.type(minuteInput, '{arrowdown}')
|
|
253
|
-
expect(minuteInput).toHaveValue(
|
|
265
|
+
expect(minuteInput).toHaveValue('42')
|
|
254
266
|
})
|
|
255
267
|
|
|
256
268
|
// ios
|
|
257
269
|
it('renders iOS as a single input', () => {
|
|
258
|
-
const { container, hourInput, minuteInput, meridiemInput } = setup({
|
|
270
|
+
const { container, hourInput, minuteInput, meridiemInput } = setup({
|
|
271
|
+
isIOS: true,
|
|
272
|
+
hours: 13,
|
|
273
|
+
minutes: 42,
|
|
274
|
+
})
|
|
259
275
|
const singleTimeInput = container.querySelector('[type=time]')
|
|
260
276
|
|
|
261
277
|
expect(singleTimeInput).toHaveValue('13:42')
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
import React, { useEffect, useRef, useState, KeyboardEvent } from 'react'
|
|
2
|
+
|
|
3
|
+
import Box from '../Box'
|
|
4
|
+
import Input from '../Input/Input'
|
|
5
|
+
import InputBox from '../Input/InputBox'
|
|
6
|
+
import InputField from '../Input/InputField'
|
|
7
|
+
import NumberField from '../NumberField'
|
|
8
|
+
import { noop, padNumber } from '../utils'
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
addHoursToTime,
|
|
12
|
+
addMinutesToTime,
|
|
13
|
+
addTimeToDate,
|
|
14
|
+
getTimeFromDate,
|
|
15
|
+
} from './utils'
|
|
16
|
+
|
|
17
|
+
type TimeFieldProps = {
|
|
18
|
+
/**
|
|
19
|
+
* An array of keys to ignore when pushed.
|
|
20
|
+
* ex: ["ArrowUp", "ArrowDown"].
|
|
21
|
+
*/
|
|
22
|
+
ignoredKeys?: string[]
|
|
23
|
+
|
|
24
|
+
interval?: number
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Render native time input, defaults to `true` if user platform is an iOS device, otherwise `false`.
|
|
28
|
+
*/
|
|
29
|
+
isIOS?: boolean
|
|
30
|
+
|
|
31
|
+
max?: number
|
|
32
|
+
|
|
33
|
+
min?: number
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Receives new time from typing or navigating with up and down arrow keys.
|
|
37
|
+
*/
|
|
38
|
+
onChange: (event: any) => void
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Displays time based on a 12 hour clock, `true` by default.
|
|
42
|
+
*/
|
|
43
|
+
twelveHourClock?: boolean
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The controlled value passed to the internal input.
|
|
47
|
+
* Accepts an array of [hours, minutes] based on a 24 hour clock.
|
|
48
|
+
*/
|
|
49
|
+
value: [number, number]
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const isMobile =
|
|
53
|
+
typeof window !== 'undefined'
|
|
54
|
+
? /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
|
|
55
|
+
navigator.userAgent
|
|
56
|
+
)
|
|
57
|
+
: false
|
|
58
|
+
|
|
59
|
+
const TimeField = ({
|
|
60
|
+
ignoredKeys,
|
|
61
|
+
interval,
|
|
62
|
+
isIOS = false,
|
|
63
|
+
max,
|
|
64
|
+
min,
|
|
65
|
+
onChange,
|
|
66
|
+
twelveHourClock = true,
|
|
67
|
+
value,
|
|
68
|
+
...restProps
|
|
69
|
+
}: TimeFieldProps) => {
|
|
70
|
+
const ref = useRef(null)
|
|
71
|
+
const [hours, setHours] = useState(value[0])
|
|
72
|
+
const [minutes, setMinutes] = useState(value[1])
|
|
73
|
+
const [meridiem, setMeridiem] = useState(hours < 12 ? 'AM' : 'PM')
|
|
74
|
+
const [hoursDisplay, setHoursDisplay] = useState(
|
|
75
|
+
twelveHourClock && hours > 12 ? hours - 12 : hours
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
const decrementHoursBy = (amount: number) => {
|
|
79
|
+
const updatedHours = hours - amount
|
|
80
|
+
|
|
81
|
+
if (twelveHourClock) {
|
|
82
|
+
if (updatedHours > 12) {
|
|
83
|
+
setHours(updatedHours)
|
|
84
|
+
setHoursDisplay(updatedHours - 12)
|
|
85
|
+
} else if (updatedHours <= 12 && updatedHours > 0) {
|
|
86
|
+
setHours(updatedHours)
|
|
87
|
+
setHoursDisplay(updatedHours)
|
|
88
|
+
} else if (updatedHours === 0) {
|
|
89
|
+
setHours(updatedHours)
|
|
90
|
+
setHoursDisplay(12)
|
|
91
|
+
} else {
|
|
92
|
+
setHours(23)
|
|
93
|
+
setHoursDisplay(11)
|
|
94
|
+
}
|
|
95
|
+
} else {
|
|
96
|
+
if (updatedHours < 0) {
|
|
97
|
+
setHours(23)
|
|
98
|
+
setHoursDisplay(23)
|
|
99
|
+
} else {
|
|
100
|
+
setHours(updatedHours)
|
|
101
|
+
setHoursDisplay(updatedHours)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const incrementHoursBy = (amount: number) => {
|
|
107
|
+
const updatedHours = hours + amount
|
|
108
|
+
|
|
109
|
+
if (twelveHourClock) {
|
|
110
|
+
if (updatedHours > 0 && updatedHours <= 12) {
|
|
111
|
+
setHours(updatedHours)
|
|
112
|
+
setHoursDisplay(updatedHours)
|
|
113
|
+
} else if (updatedHours > 12 && updatedHours < 24) {
|
|
114
|
+
setHours(updatedHours)
|
|
115
|
+
setHoursDisplay(updatedHours - 12)
|
|
116
|
+
} else {
|
|
117
|
+
setHours(0)
|
|
118
|
+
setHoursDisplay(12)
|
|
119
|
+
}
|
|
120
|
+
} else {
|
|
121
|
+
if (updatedHours < 24) {
|
|
122
|
+
setHours(updatedHours)
|
|
123
|
+
setHoursDisplay(updatedHours)
|
|
124
|
+
} else {
|
|
125
|
+
setHours(0)
|
|
126
|
+
setHoursDisplay(0)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const decrementMinutesBy = (amount: number) => {
|
|
132
|
+
const updatedMinutes = minutes - amount
|
|
133
|
+
|
|
134
|
+
if (updatedMinutes >= 0) {
|
|
135
|
+
setMinutes(updatedMinutes)
|
|
136
|
+
} else {
|
|
137
|
+
setMinutes(59)
|
|
138
|
+
decrementHoursBy(1)
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const incrementMinutesBy = (amount: number) => {
|
|
143
|
+
const updatedMinutes = minutes + amount
|
|
144
|
+
|
|
145
|
+
if (updatedMinutes <= 59) {
|
|
146
|
+
setMinutes(updatedMinutes)
|
|
147
|
+
} else {
|
|
148
|
+
setMinutes(0)
|
|
149
|
+
incrementHoursBy(1)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const toggleMeridiem = () => {
|
|
154
|
+
if (meridiem === 'AM') {
|
|
155
|
+
setMeridiem('PM')
|
|
156
|
+
if (hours >= 0 && hours <= 12) {
|
|
157
|
+
setHours(hours + 12)
|
|
158
|
+
}
|
|
159
|
+
} else {
|
|
160
|
+
setMeridiem('AM')
|
|
161
|
+
if (hours >= 12 && hours < 24) {
|
|
162
|
+
setHours(hours - 12)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const handleHoursKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
|
|
168
|
+
if (ignoredKeys && ignoredKeys.includes(event.key)) {
|
|
169
|
+
return
|
|
170
|
+
}
|
|
171
|
+
if (event.key === 'Backspace') {
|
|
172
|
+
event.preventDefault()
|
|
173
|
+
}
|
|
174
|
+
if (event.key === 'ArrowRight') {
|
|
175
|
+
ref.current.node.children[2].focus()
|
|
176
|
+
}
|
|
177
|
+
if (event.key === 'ArrowUp') {
|
|
178
|
+
incrementHoursBy(1)
|
|
179
|
+
}
|
|
180
|
+
if (event.key === 'ArrowDown') {
|
|
181
|
+
decrementHoursBy(1)
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const handleHoursChange = (e: any) => {
|
|
186
|
+
setHoursDisplay(e)
|
|
187
|
+
!twelveHourClock && setHours(e)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const handleHoursBlur = (e: any) => {
|
|
191
|
+
const updatedHoursValue = parseInt(e.target.value)
|
|
192
|
+
|
|
193
|
+
if (twelveHourClock) {
|
|
194
|
+
if (updatedHoursValue === 0) {
|
|
195
|
+
setHours(0)
|
|
196
|
+
setHoursDisplay(12)
|
|
197
|
+
} else if (hours <= 12) {
|
|
198
|
+
setHours(updatedHoursValue)
|
|
199
|
+
setHoursDisplay(updatedHoursValue)
|
|
200
|
+
} else {
|
|
201
|
+
setHours(updatedHoursValue + 12)
|
|
202
|
+
setHoursDisplay(updatedHoursValue)
|
|
203
|
+
}
|
|
204
|
+
} else {
|
|
205
|
+
setHours(updatedHoursValue)
|
|
206
|
+
setHoursDisplay(updatedHoursValue)
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const handleMinutesKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
|
|
211
|
+
if (event.key === 'ArrowLeft') {
|
|
212
|
+
ref.current.node.children[0].focus()
|
|
213
|
+
}
|
|
214
|
+
if (event.key === 'ArrowRight') {
|
|
215
|
+
ref.current.node.children[3].focus()
|
|
216
|
+
}
|
|
217
|
+
if (event.key === 'ArrowUp') {
|
|
218
|
+
incrementMinutesBy(1)
|
|
219
|
+
}
|
|
220
|
+
if (event.key === 'ArrowDown') {
|
|
221
|
+
decrementMinutesBy(1)
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const handleAMPMKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
|
|
226
|
+
if (ignoredKeys && ignoredKeys.includes(event.key)) {
|
|
227
|
+
return
|
|
228
|
+
}
|
|
229
|
+
if (event.key !== 'Tab' && !(event.metaKey && event.key === 'r')) {
|
|
230
|
+
event.preventDefault()
|
|
231
|
+
}
|
|
232
|
+
if (event.key === 'ArrowLeft') {
|
|
233
|
+
ref.current.node.children[2].focus()
|
|
234
|
+
}
|
|
235
|
+
if (
|
|
236
|
+
(event.key.toLowerCase() === 'a' && meridiem === 'PM') ||
|
|
237
|
+
(event.key.toLowerCase() === 'p' && meridiem === 'AM') ||
|
|
238
|
+
event.key === 'ArrowUp' ||
|
|
239
|
+
event.key === 'ArrowDown'
|
|
240
|
+
) {
|
|
241
|
+
toggleMeridiem()
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const updateTime = (time: [number, number]) => {
|
|
246
|
+
const [hours, minutes] = time
|
|
247
|
+
|
|
248
|
+
return onChange([hours, minutes])
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const handleMobileChange = (event: any) => {
|
|
252
|
+
const [hours, minutes] = event.target.value.split(':')
|
|
253
|
+
|
|
254
|
+
if (hours || minutes) {
|
|
255
|
+
setHours(hours)
|
|
256
|
+
setMinutes(minutes)
|
|
257
|
+
} else {
|
|
258
|
+
setHours(0)
|
|
259
|
+
setMinutes(0)
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
useEffect(() => {
|
|
264
|
+
if (hours < 12) {
|
|
265
|
+
setMeridiem('AM')
|
|
266
|
+
} else {
|
|
267
|
+
setMeridiem('PM')
|
|
268
|
+
}
|
|
269
|
+
updateTime([hours, minutes])
|
|
270
|
+
}, [hours, minutes])
|
|
271
|
+
|
|
272
|
+
return isMobile || isIOS ? (
|
|
273
|
+
<Input
|
|
274
|
+
{...restProps}
|
|
275
|
+
type="time"
|
|
276
|
+
autoWidth={`${hours}:${padNumber(minutes, 2)} ${meridiem}`}
|
|
277
|
+
value={`${padNumber(hours, 2)}:${padNumber(minutes, 2)}`}
|
|
278
|
+
onChange={handleMobileChange}
|
|
279
|
+
/>
|
|
280
|
+
) : (
|
|
281
|
+
<InputBox ref={ref} inline {...restProps}>
|
|
282
|
+
<NumberField
|
|
283
|
+
autoWidth={false}
|
|
284
|
+
fontVariantNumeric="tabular-nums"
|
|
285
|
+
moveFocusOnMax
|
|
286
|
+
value={hoursDisplay}
|
|
287
|
+
min={0}
|
|
288
|
+
max={twelveHourClock ? 12 : 23}
|
|
289
|
+
pad={2}
|
|
290
|
+
grow={0}
|
|
291
|
+
width="2ch"
|
|
292
|
+
textAlign="center"
|
|
293
|
+
aria-label="Hours"
|
|
294
|
+
onChange={handleHoursChange}
|
|
295
|
+
onKeyDown={handleHoursKeyDown}
|
|
296
|
+
onBlur={handleHoursBlur}
|
|
297
|
+
{...{ ignoredKeys }}
|
|
298
|
+
/>
|
|
299
|
+
<Box as="span" width={0.5} textAlign="center" userSelect="none">
|
|
300
|
+
:
|
|
301
|
+
</Box>
|
|
302
|
+
<NumberField
|
|
303
|
+
autoWidth={false}
|
|
304
|
+
fontVariantNumeric="tabular-nums"
|
|
305
|
+
moveFocusOnMax
|
|
306
|
+
value={minutes}
|
|
307
|
+
min={0}
|
|
308
|
+
max={59}
|
|
309
|
+
pad={2}
|
|
310
|
+
grow={0}
|
|
311
|
+
width="2ch"
|
|
312
|
+
textAlign="center"
|
|
313
|
+
aria-label="Minutes"
|
|
314
|
+
onChange={(e) => setMinutes(e)}
|
|
315
|
+
onKeyDown={handleMinutesKeyDown}
|
|
316
|
+
{...{ ignoredKeys }}
|
|
317
|
+
/>
|
|
318
|
+
{twelveHourClock && (
|
|
319
|
+
<InputField
|
|
320
|
+
highlightOnInteraction
|
|
321
|
+
value={meridiem}
|
|
322
|
+
grow={0}
|
|
323
|
+
width="2em"
|
|
324
|
+
textAlign="center"
|
|
325
|
+
aria-label="AM/PM"
|
|
326
|
+
onChange={noop}
|
|
327
|
+
onKeyDown={handleAMPMKeyDown}
|
|
328
|
+
/>
|
|
329
|
+
)}
|
|
330
|
+
</InputBox>
|
|
331
|
+
)
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
TimeField.addHoursToTime = addHoursToTime
|
|
335
|
+
TimeField.addMinutesToTime = addMinutesToTime
|
|
336
|
+
TimeField.addTimeToDate = addTimeToDate
|
|
337
|
+
TimeField.getTimeFromDate = getTimeFromDate
|
|
338
|
+
|
|
339
|
+
export default TimeField
|
|
@@ -118,11 +118,7 @@ class TimeField extends Component<Props> {
|
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
handleHoursChange = (hours) => {
|
|
121
|
-
|
|
122
|
-
this.updateTime({ hours: 12 })
|
|
123
|
-
} else {
|
|
124
|
-
this.updateTime({ hours })
|
|
125
|
-
}
|
|
121
|
+
this.updateTime({ hours })
|
|
126
122
|
}
|
|
127
123
|
|
|
128
124
|
handleHoursKeyDown = (event) => {
|
|
@@ -232,7 +228,7 @@ class TimeField extends Component<Props> {
|
|
|
232
228
|
value={
|
|
233
229
|
twelveHourClock ? hours % HOURS_TO_NOON || HOURS_TO_NOON : hours
|
|
234
230
|
}
|
|
235
|
-
min={0}
|
|
231
|
+
min={twelveHourClock ? 1 : 0}
|
|
236
232
|
max={twelveHourClock ? HOURS_TO_NOON : HOURS_IN_DAY - 1}
|
|
237
233
|
pad={2}
|
|
238
234
|
grow={0}
|
package/src/TimeField/utils.js
CHANGED
|
@@ -12,17 +12,19 @@ export function addHoursToTime(time, hoursToAdd) {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export function addMinutesToTime(time, minutesToAdd) {
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
15
|
+
const [hours, minutes] = time
|
|
16
|
+
const totalTimeInMinutes = hours * 60 + minutes
|
|
17
|
+
const totalMinutesInDay = 24 * 60
|
|
18
|
+
const newTime = parseInt(totalTimeInMinutes) + parseInt(minutesToAdd)
|
|
19
|
+
|
|
20
|
+
const newHours =
|
|
21
|
+
newTime > totalMinutesInDay
|
|
22
|
+
? Math.floor(newTime / 60) - 24
|
|
23
|
+
: Math.floor(newTime / 60)
|
|
24
|
+
|
|
25
|
+
const newMinutes = newTime % 60
|
|
26
|
+
|
|
27
|
+
return [newHours, newMinutes]
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
export function addTimeToDate(date, time) {
|
package/src/utils.js
CHANGED
|
@@ -353,6 +353,13 @@ function isFunction(value) {
|
|
|
353
353
|
return value && value.constructor === Function
|
|
354
354
|
}
|
|
355
355
|
|
|
356
|
+
/**
|
|
357
|
+
* Determines if value is a number
|
|
358
|
+
*/
|
|
359
|
+
function isNumber(value) {
|
|
360
|
+
return /^[0-9]$/i.test(value)
|
|
361
|
+
}
|
|
362
|
+
|
|
356
363
|
/**
|
|
357
364
|
* Determines if a value is present in an array by
|
|
358
365
|
* performing a shallow equal check on each item.
|
|
@@ -787,6 +794,7 @@ export {
|
|
|
787
794
|
isElementInteractive,
|
|
788
795
|
isFunction,
|
|
789
796
|
isIOS,
|
|
797
|
+
isNumber,
|
|
790
798
|
isValueInArray,
|
|
791
799
|
joinChildren,
|
|
792
800
|
lockScroll,
|