@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.
@@ -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 } value={value} onChange={setValue}/>
18
+ return <TimeField {...props} value={value} onChange={setValue} />
19
19
  }
20
20
 
21
- const setup = ({ hours, minutes, ...props } : TimeProps) => {
22
- const testApp = render(<TestApp {...props } value={[hours, minutes]}/>)
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({ hours: 13, minutes: 42, ignoredKeys: ['ArrowUp', 'ArrowDown'] })
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( { hours: 1, minutes: 42 })
129
+ const { hourInput } = setup({ hours: 1, minutes: 42 })
126
130
 
127
- expect(hourInput).toHaveValue("01")
131
+ expect(hourInput).toHaveValue('01')
128
132
  userEvent.type(hourInput, '{arrowup}')
129
- expect(hourInput).toHaveValue("02")
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( { hours: 1, minutes: 59 })
137
+ const { hourInput, minuteInput } = setup({ hours: 1, minutes: 59 })
134
138
 
135
- expect(hourInput).toHaveValue("01")
139
+ expect(hourInput).toHaveValue('01')
136
140
  userEvent.type(minuteInput, '{arrowup}')
137
- expect(hourInput).toHaveValue("02")
141
+ expect(hourInput).toHaveValue('02')
138
142
  })
139
143
 
140
144
  it('decrement hour on arrow down', () => {
141
- const { hourInput } = setup( { hours: 2, minutes: 42 })
145
+ const { hourInput } = setup({ hours: 2, minutes: 42 })
142
146
 
143
- expect(hourInput).toHaveValue("02")
147
+ expect(hourInput).toHaveValue('02')
144
148
  userEvent.type(hourInput, '{arrowdown}')
145
- expect(hourInput).toHaveValue("01")
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( { hours: 2, minutes: 0 })
153
+ const { hourInput, minuteInput } = setup({ hours: 2, minutes: 0 })
150
154
 
151
- expect(hourInput).toHaveValue("02")
155
+ expect(hourInput).toHaveValue('02')
152
156
  userEvent.type(minuteInput, '{arrowdown}')
153
- expect(hourInput).toHaveValue("01")
157
+ expect(hourInput).toHaveValue('01')
154
158
  })
155
159
 
156
160
  it('increment minute on arrow up', () => {
157
- const { minuteInput } = setup( { hours: 1, minutes: 42 })
161
+ const { minuteInput } = setup({ hours: 1, minutes: 42 })
158
162
 
159
- expect(minuteInput).toHaveValue("42")
163
+ expect(minuteInput).toHaveValue('42')
160
164
  userEvent.type(minuteInput, '{arrowup}')
161
- expect(minuteInput).toHaveValue("43")
165
+ expect(minuteInput).toHaveValue('43')
162
166
  })
163
167
 
164
168
  it('decrement minute on arrow down', () => {
165
- const { minuteInput } = setup( { hours: 2, minutes: 43 })
169
+ const { minuteInput } = setup({ hours: 2, minutes: 43 })
166
170
 
167
- expect(minuteInput).toHaveValue("43")
171
+ expect(minuteInput).toHaveValue('43')
168
172
  userEvent.type(minuteInput, '{arrowdown}')
169
- expect(minuteInput).toHaveValue("42")
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( { hours: 1, minutes: 59 })
177
+ const { minuteInput } = setup({ hours: 1, minutes: 59 })
174
178
 
175
- expect(minuteInput).toHaveValue("59")
179
+ expect(minuteInput).toHaveValue('59')
176
180
  userEvent.type(minuteInput, '{arrowup}')
177
- expect(minuteInput).toHaveValue("00")
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( { hours: 1, minutes: 0 })
185
+ const { minuteInput } = setup({ hours: 1, minutes: 0 })
182
186
 
183
- expect(minuteInput).toHaveValue("00")
187
+ expect(minuteInput).toHaveValue('00')
184
188
  userEvent.type(minuteInput, '{arrowdown}')
185
- expect(minuteInput).toHaveValue("59")
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( { hours: 11 })
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( { hours: 1 })
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( { hours: 12 })
209
+ const { hourInput } = setup({ hours: 12 })
206
210
 
207
- expect(hourInput).toHaveValue("12")
211
+ expect(hourInput).toHaveValue('12')
208
212
  userEvent.type(hourInput, '{arrowup}')
209
- expect(hourInput).toHaveValue("01")
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( { hours: 23, twelveHourClock: false })
217
+ const { hourInput } = setup({ hours: 23, twelveHourClock: false })
214
218
 
215
- expect(hourInput).toHaveValue("23")
219
+ expect(hourInput).toHaveValue('23')
216
220
  userEvent.type(hourInput, '{arrowup}')
217
- expect(hourInput).toHaveValue("00")
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( { hours: 1 })
225
+ const { hourInput } = setup({ hours: 1 })
222
226
 
223
- expect(hourInput).toHaveValue("01")
227
+ expect(hourInput).toHaveValue('01')
224
228
  userEvent.type(hourInput, '{arrowdown}')
225
- expect(hourInput).toHaveValue("12")
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( { hours: 0, twelveHourClock: false })
233
+ const { hourInput } = setup({ hours: 0, twelveHourClock: false })
230
234
 
231
- expect(hourInput).toHaveValue("00")
235
+ expect(hourInput).toHaveValue('00')
232
236
  userEvent.type(hourInput, '{arrowdown}')
233
- expect(hourInput).toHaveValue("23")
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( { hours: 1, minutes: 42, ignoredKeys: ['ArrowUp', 'ArrowDown'] })
241
+ const { hourInput } = setup({
242
+ hours: 1,
243
+ minutes: 42,
244
+ ignoredKeys: ['ArrowUp', 'ArrowDown'],
245
+ })
238
246
 
239
- expect(hourInput).toHaveValue("01")
247
+ expect(hourInput).toHaveValue('01')
240
248
  userEvent.type(hourInput, '{arrowup}')
241
- expect(hourInput).toHaveValue("01")
249
+ expect(hourInput).toHaveValue('01')
242
250
  userEvent.type(hourInput, '{arrowdown}')
243
- expect(hourInput).toHaveValue("01")
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( { hours: 1, minutes: 42, ignoredKeys: ['ArrowUp', 'ArrowDown'] })
255
+ const { minuteInput } = setup({
256
+ hours: 1,
257
+ minutes: 42,
258
+ ignoredKeys: ['ArrowUp', 'ArrowDown'],
259
+ })
248
260
 
249
- expect(minuteInput).toHaveValue("42")
261
+ expect(minuteInput).toHaveValue('42')
250
262
  userEvent.type(minuteInput, '{arrowup}')
251
- expect(minuteInput).toHaveValue("42")
263
+ expect(minuteInput).toHaveValue('42')
252
264
  userEvent.type(minuteInput, '{arrowdown}')
253
- expect(minuteInput).toHaveValue("42")
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({ isIOS: true, hours: 13, minutes: 42 })
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
- if (this.twelveHourClock && hours === 0) {
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}
@@ -12,17 +12,19 @@ export function addHoursToTime(time, hoursToAdd) {
12
12
  }
13
13
 
14
14
  export function addMinutesToTime(time, minutesToAdd) {
15
- let [hours, minutes] = time
16
- const nextMinutes = minutes + minutesToAdd
17
- const remainderMinutes = modulo(nextMinutes, MINUTES_IN_HOUR)
18
- if (remainderMinutes >= 0) {
19
- const nextHours = hours + Math.floor(nextMinutes / MINUTES_IN_HOUR)
20
- hours = modulo(nextHours, HOURS_IN_DAY)
21
- minutes = remainderMinutes
22
- } else {
23
- minutes = nextMinutes
24
- }
25
- return [hours, minutes]
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,