@dvrd/dvr-controls 1.1.33 → 1.1.34

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dvrd/dvr-controls",
3
- "version": "1.1.33",
3
+ "version": "1.1.34",
4
4
  "description": "Custom web controls",
5
5
  "main": "index.ts",
6
6
  "files": [
@@ -5,15 +5,7 @@ import './style/dvrdDatePicker.scss';
5
5
 
6
6
  import classNames from 'classnames';
7
7
  import React, {
8
- ForwardedRef,
9
- forwardRef,
10
- Fragment,
11
- MouseEventHandler,
12
- useEffect,
13
- useImperativeHandle,
14
- useMemo,
15
- useRef,
16
- useState
8
+ ForwardedRef, forwardRef, Fragment, MouseEventHandler, useEffect, useImperativeHandle, useMemo, useRef, useState
17
9
  } from 'react';
18
10
  import {ChangeFunction, DatePickerTimeMode, ErrorType} from '../util/interfaces';
19
11
  import AwesomeIcon from '../icon/awesomeIcon';
@@ -21,7 +13,7 @@ import WithBackground from '../popup/withBackground';
21
13
  import DvrdButton from '../button/dvrdButton';
22
14
  import {generateComponentId} from '../util/componentUtil';
23
15
  import IDate from '@dvrd/idate';
24
- import {pad} from '../util/controlUtil';
16
+ import {pad, padNum} from '../util/controlUtil';
25
17
  import {Swiper, SwiperClass, SwiperSlide} from 'swiper/react';
26
18
  import {FreeMode, Mousewheel} from 'swiper/modules';
27
19
  import 'swiper/css';
@@ -45,7 +37,7 @@ interface Props {
45
37
  pickersOnly?: boolean;
46
38
  }
47
39
 
48
- export type DVRDDatePickerRef = { onOpen: VoidFunction; onClose: VoidFunction; }
40
+ export type DVRDDatePickerRef = {onOpen: VoidFunction; onClose: VoidFunction;}
49
41
 
50
42
  function DvrdDatePicker(props: Props, ref: ForwardedRef<DVRDDatePickerRef>) {
51
43
  const {
@@ -138,38 +130,72 @@ interface DatePickerProps {
138
130
  min?: IDate | string;
139
131
  }
140
132
 
133
+ type Day = {
134
+ number: number;
135
+ value: IDate;
136
+ currentMonth: boolean;
137
+ }
138
+
139
+ const beforePadDays = [6, 0, 1, 2, 3, 4, 5];
140
+
141
+ function buildFirstDaysRow(firstDay: IDate): [Array<Day> | null, IDate] {
142
+ const padBeforeDays = beforePadDays[firstDay.weekday()];
143
+ if (!padBeforeDays) return [null, firstDay];
144
+ const firstRow: Array<Day> = [];
145
+ for (let offset = padBeforeDays; offset >= 1; offset -= 1) {
146
+ const dayValue = firstDay.subtract(offset, 'days');
147
+ firstRow.push({
148
+ number: dayValue.day(),
149
+ value: dayValue,
150
+ currentMonth: false
151
+ });
152
+ }
153
+ let secondRowFirstDay = firstDay.clone();
154
+ while (firstRow.length < 7) {
155
+ firstRow.push({
156
+ number: secondRowFirstDay.day(),
157
+ value: secondRowFirstDay,
158
+ currentMonth: true
159
+ });
160
+ secondRowFirstDay = secondRowFirstDay.add(1, 'day');
161
+ }
162
+ return [firstRow, secondRowFirstDay];
163
+ }
164
+
165
+ function buildDay(value: IDate, year: number, month: number): Day {
166
+ return {
167
+ number: value.day(),
168
+ value,
169
+ currentMonth: value.year() === year && value.month() === month
170
+ };
171
+ }
172
+
173
+ const daysToRender = 42; // Always render 6 rows of days
174
+
141
175
  function DatePicker(props: DatePickerProps) {
142
176
  const {min, max, alwaysShowArrows, open, onClose, value, onChange} = props;
143
177
  const divRef = useRef<HTMLDivElement>(null);
144
178
  const pickerValue = useMemo(() => value?.clone() ?? IDate.now(), [value]);
145
- const [padBeforeDays, days, padAfterDays] = useMemo(() => {
146
- const daysInMonth = pickerValue.daysInMonth();
147
- const days: number[] = [];
148
- const padBeforeDays: number[] = [];
149
- const padAfterDays: number[] = [];
150
- for (let i = 1; i <= daysInMonth; i++) days.push(i);
151
- let firstMonthDay = pickerValue.day(1).weekday();
152
- if (!firstMonthDay) firstMonthDay = 7;
153
- const padDaysBefore = firstMonthDay - 1;
154
- let dayBefore = pickerValue.add(-1, 'month').daysInMonth();
155
- for (let i = 0; i < padDaysBefore; i++)
156
- padBeforeDays.splice(0, 0, dayBefore--);
157
-
158
- const lastWeekDay = pickerValue.day(daysInMonth).weekday();
159
- const padDaysAfter = 7 - lastWeekDay;
160
- for (let i = 0; i < padDaysAfter; i++)
161
- padAfterDays.push(i + 1);
162
-
163
- if (Math.ceil(padAfterDays.concat(days).concat(padBeforeDays).length / 7) < 6) {
164
- let dayAfter = padAfterDays[padAfterDays.length - 1] + 1;
165
- for (let i = 0; i < 7; i++)
166
- padAfterDays.push(dayAfter++);
179
+ const days: Array<Day> = useMemo(() => {
180
+ const month = pickerValue.month();
181
+ const year = pickerValue.year();
182
+ const days: Array<Day> = [];
183
+ let firstDay = new IDate(`${year}-${padNum(month)}-01`);
184
+ const [firstRow, nextFirstDay] = buildFirstDaysRow(firstDay);
185
+ if (firstRow) days.push(...firstRow);
186
+ firstDay = nextFirstDay;
187
+ while (days.length < daysToRender) {
188
+ for (let ix = 0; ix < 7; ix++) {
189
+ days.push(buildDay(firstDay, year, month));
190
+ firstDay = firstDay.add(1, 'day');
191
+ }
167
192
  }
168
- return [padBeforeDays, days, padAfterDays];
193
+
194
+ return days;
169
195
  }, [pickerValue]);
170
196
 
171
197
  function onReduce(key: 'year' | 'month') {
172
- return function () {
198
+ return function() {
173
199
  let value = pickerValue.clone();
174
200
  value = value.add(-1, key);
175
201
  value = value.day(Math.min(value.daysInMonth(), value.day()));
@@ -178,7 +204,7 @@ function DatePicker(props: DatePickerProps) {
178
204
  }
179
205
 
180
206
  function onAdd(key: 'year' | 'month') {
181
- return function () {
207
+ return function() {
182
208
  let value = pickerValue.clone();
183
209
  value = value.add(1, key);
184
210
  value = value.day(Math.min(value.daysInMonth(), value.day()));
@@ -187,7 +213,7 @@ function DatePicker(props: DatePickerProps) {
187
213
  }
188
214
 
189
215
  function onResetSwitcher(key: 'year' | 'month') {
190
- return function () {
216
+ return function() {
191
217
  let value = pickerValue.clone();
192
218
  if (key === 'year') value = value.year(IDate.now().year());
193
219
  else value = value.month(IDate.now().month());
@@ -200,25 +226,9 @@ function DatePicker(props: DatePickerProps) {
200
226
  onChange(true)(IDate.now());
201
227
  }
202
228
 
203
- function onSelectDay(day: number, month?: number, year?: number) {
204
- return function () {
205
- let pickValue = pickerValue.clone();
206
- if (day || value === null) {
207
- pickValue = pickValue.day(day);
208
- if (month !== undefined) pickValue = pickValue.month(month);
209
- if (year !== undefined) pickValue = pickValue.year(year);
210
- onChange(true)(pickValue);
211
- }
212
- };
213
- }
214
-
215
- function onSelectPadDay(day: number, kind: 'prev' | 'next') {
216
- return function () {
217
- let value = pickerValue.clone();
218
- if (kind === 'prev') value = value.add(-1, 'month');
219
- else value = value.add(1, 'month');
220
- value = value.day(day);
221
- onChange(true)(value);
229
+ function onSelectDay(day: Day) {
230
+ return function() {
231
+ onChange(true)(day.value);
222
232
  };
223
233
  }
224
234
 
@@ -267,14 +277,10 @@ function DatePicker(props: DatePickerProps) {
267
277
  divRef.current?.classList.remove('switch-mount');
268
278
  }
269
279
 
270
- function dateIsDisabled(day: number, month?: number, year?: number) {
280
+ function dateIsDisabled(day: Day) {
271
281
  if (!min && !max) return false;
272
- let pickValue = pickerValue.clone();
273
- pickValue = pickValue.day(day);
274
- if (month !== undefined) pickValue = pickValue.month(month);
275
- if (year !== undefined) pickValue = pickValue.year(year);
276
- if (min && new IDate(min).isAfter(pickValue, 'day')) return true;
277
- else if (max && new IDate(max).isBefore(pickValue, 'day')) return true;
282
+ if (min && new IDate(min).isAfter(day.value, 'day')) return true;
283
+ else if (max && new IDate(max).isBefore(day.value, 'day')) return true;
278
284
  return false;
279
285
  }
280
286
 
@@ -356,25 +362,17 @@ function DatePicker(props: DatePickerProps) {
356
362
  } else {
357
363
  document.removeEventListener('keydown', keyListener);
358
364
  }
359
- return function () {
365
+ return function() {
360
366
  document.removeEventListener('keydown', keyListener);
361
367
  };
362
368
  }, [open]);
363
369
 
364
- function renderDay(day: number, index: number, onClick: MouseEventHandler, className?: string,
365
- type?: 'prev' | 'next') {
366
- let pickValue = pickerValue.clone();
367
- if (type === 'prev')
368
- pickValue = pickValue.subtract(1, 'month');
369
- else if (type === 'next')
370
- pickValue = pickValue.add(1, 'month');
371
- const month = pickValue.month();
372
- const year = pickValue.year();
373
- const disabled = dateIsDisabled(day, month, year);
370
+ function renderDay(day: Day, index: number, onClick: MouseEventHandler, className?: string) {
371
+ const disabled = dateIsDisabled(day);
374
372
  return (
375
373
  <div className={classNames('day', disabled && 'disabled', className)}
376
374
  onClick={disabled ? undefined : onClick} key={index}>
377
- <span className='day-label'>{day}</span>
375
+ <span className='day-label'>{day.number}</span>
378
376
  </div>
379
377
  );
380
378
  }
@@ -420,16 +418,11 @@ function DatePicker(props: DatePickerProps) {
420
418
  <div className='day no-select'>
421
419
  <span className='day-label'>Zo</span>
422
420
  </div>
423
- {padBeforeDays.map((day: number, index: number) => {
424
- return renderDay(day, index, onSelectPadDay(day, 'prev'), 'pad', 'prev');
425
- })}
426
- {days.map((day: number, index: number) => {
427
- const isSelected = day === pickerValue.day();
421
+ {days.map((day: Day, index: number) => {
422
+ const isSelected = day.value.isAt(pickerValue, 'day');
428
423
  return renderDay(day, index, onSelectDay(day),
429
- classNames(isSelected && 'selected', day === 0 && 'hide'));
430
- })}
431
- {padAfterDays.map((day: number, index: number) => {
432
- return renderDay(day, index, onSelectPadDay(day, 'next'), 'pad', 'next');
424
+ classNames(isSelected && 'selected', day.number === 0 && 'hide',
425
+ !day.currentMonth && 'pad'));
433
426
  })}
434
427
  </div>
435
428
  <div className='actions-container'>
@@ -461,7 +454,7 @@ function TimePicker(props: TimePickerProps) {
461
454
  }
462
455
 
463
456
  function onSlideChange(key: 'hour' | 'minute' | 'second') {
464
- return function (swiper: SwiperClass) {
457
+ return function(swiper: SwiperClass) {
465
458
  if (key === 'hour') internalValue.current = internalValue.current.hours(swiper.realIndex);
466
459
  if (key === 'minute') internalValue.current = internalValue.current.minutes(swiper.realIndex);
467
460
  if (key === 'second') internalValue.current = internalValue.current.seconds(swiper.realIndex);