@dvrd/dvr-controls 1.0.78 → 1.0.81

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.0.78",
3
+ "version": "1.0.81",
4
4
  "description": "Custom web controls",
5
5
  "main": "index.ts",
6
6
  "files": [
@@ -30,7 +30,7 @@
30
30
  "react-router-dom": "6.15.0"
31
31
  },
32
32
  "dependencies": {
33
- "@dvrd/idate": "^1.8.4",
33
+ "@dvrd/idate": "^1.8.6",
34
34
  "@fortawesome/fontawesome-svg-core": "6.5.2",
35
35
  "@fortawesome/free-brands-svg-icons": "6.5.2",
36
36
  "@fortawesome/free-regular-svg-icons": "6.5.2",
@@ -47,6 +47,7 @@
47
47
  "@types/react": "^18.2.28",
48
48
  "@types/react-color": "2.13.5",
49
49
  "@types/react-dom": "^18.0.11",
50
+ "@types/swiper": "^6.0.0",
50
51
  "@types/uuid": "9.0.0",
51
52
  "classnames": "2.5.1",
52
53
  "dompurify": "3.0.0",
@@ -58,6 +59,7 @@
58
59
  "lodash.mergewith": "^4.6.2",
59
60
  "react-color": "2.19.3",
60
61
  "react-rnd": "10.4.1",
62
+ "swiper": "^11.1.4",
61
63
  "typescript": "4.9.5",
62
64
  "uuid": "9.0.0"
63
65
  }
@@ -21,15 +21,17 @@ import WithBackground from '../popup/withBackground';
21
21
  import DvrdButton from "../button/dvrdButton";
22
22
  import {generateComponentId} from "../util/componentUtil";
23
23
  import IDate from '@dvrd/idate';
24
- import {pad, padNum, voidFunction} from "../util/controlUtil";
25
- import {DvrdNumberInput} from "../../../index";
24
+ import {pad} from "../util/controlUtil";
26
25
  import defer from 'lodash.defer';
26
+ import {Swiper, SwiperClass, SwiperSlide} from 'swiper/react';
27
+ import {FreeMode, Mousewheel} from 'swiper/modules';
28
+ import 'swiper/css';
27
29
 
28
30
  interface Props {
29
31
  onChange: ChangeFunction<IDate>;
30
32
  closeOnChange?: boolean;
31
33
  value: IDate | null;
32
- label: string;
34
+ label?: string;
33
35
  timeMode?: DatePickerTimeMode;
34
36
  error?: ErrorType;
35
37
  className?: string;
@@ -109,7 +111,7 @@ function DvrdDatePicker(props: Props, ref: ForwardedRef<DVRDDatePickerRef>) {
109
111
  return (
110
112
  <div className={classNames('dvrd-date-picker', className, error && 'error', disabled && 'disabled')}>
111
113
  {!pickersOnly && <>
112
- <label className='field-label'>{label}</label>
114
+ {!!label && <label className='field-label'>{label}</label>}
113
115
  {renderMobilePicker()}
114
116
  <div className={classNames('value-container', useMobileNative && 'no-mob')} onClick={onClickContainer}>
115
117
  <label className={classNames('value', !value && 'placeholder')}>{value?.format(dateFormat) ??
@@ -453,85 +455,40 @@ interface TimePickerProps {
453
455
  closeOnChange?: boolean;
454
456
  }
455
457
 
456
- type TimePartValues = [string, string, string, string, string, string];
457
-
458
- function dateToTimeParts(value: IDate | null): TimePartValues {
459
- if (!value) return ['', '', '', '', '', ''];
460
- const hours = padNum(value.hours());
461
- const minutes = padNum(value.minutes());
462
- const seconds = padNum(value.seconds());
463
- return [...hours.split(''), ...minutes.split(''), ...seconds.split('')] as TimePartValues;
464
- }
465
-
466
- function timePartsToNumber(part1: string, part2: string): number {
467
- const num = Number(part1 + part2);
468
- return Number.isNaN(num) ? 0 : num;
469
- }
470
-
471
458
  function TimePicker(props: TimePickerProps) {
472
459
  const {onChange, value, open, onClose, timeMode, closeOnChange} = props;
473
- const [timeParts, setTimeParts] = useState<TimePartValues>(dateToTimeParts(value));
474
- const secondHourMax = useMemo(() => {
475
- if (Number(timeParts[0]) === 2) return 3;
476
- return 9;
477
- }, timeParts);
478
- const compID = useRef(generateComponentId());
460
+ const internalValue = useRef<IDate>(value ?? new IDate('00:00:00', 'HH:mm:ss'));
479
461
 
480
462
  function onClickNow() {
481
- setTimeParts(dateToTimeParts(new IDate()));
463
+ internalValue.current = IDate.now();
482
464
  if (closeOnChange)
483
465
  onSubmit();
484
466
  }
485
467
 
486
- function onSubmit() {
487
- const hours = timePartsToNumber(timeParts[0], timeParts[1]);
488
- const minutes = timePartsToNumber(timeParts[2], timeParts[3]);
489
- const seconds = timePartsToNumber(timeParts[4], timeParts[5]);
490
- const _value = value?.clone() ?? new IDate();
491
- onChange(_value.set({hours, minutes, seconds}));
492
- onClose();
493
- }
494
-
495
- function onKeyDown(index: number) {
496
- return function (evt: React.KeyboardEvent) {
497
- const {key} = evt;
498
- if (/\d/.test(key) && index < 5) {
499
- onChangePickerPart(index)(key);
500
- defer(() => onFocusElement(index + 1));
501
- }
502
- if (key.toLowerCase() === 'backspace')
503
- if ((evt.target as HTMLInputElement).value) {
504
- onChangePickerPart(index)('');
505
- } else if (index > 0) {
506
- onChangePickerPart(index - 1)('');
507
- defer(() => onFocusElement(index - 1));
508
- }
468
+ function onSlideChange(key: 'hour' | 'minute' | 'second') {
469
+ return function (swiper: SwiperClass) {
470
+ if (key === 'hour') internalValue.current = internalValue.current.hours(swiper.realIndex);
471
+ if (key === 'minute') internalValue.current = internalValue.current.minutes(swiper.realIndex);
472
+ if (key === 'second') internalValue.current = internalValue.current.seconds(swiper.realIndex);
509
473
  }
510
474
  }
511
475
 
512
- function onChangePickerPart(index: number) {
513
- return function (value: string) {
514
- const parts = [...timeParts];
515
- parts[index] = value;
516
- setTimeParts(parts as TimePartValues);
517
- }
518
- }
519
-
520
- function onFocusElement(index: number) {
521
- const input = document.getElementById(`${compID.current}-${index}-input`);
522
- input?.focus();
476
+ function onSubmit() {
477
+ onChange(internalValue.current.clone());
478
+ onClose();
523
479
  }
524
480
 
525
- function renderTimeControls() {
481
+ function renderTimeContainer() {
526
482
  const renderHours = [DatePickerTimeMode.FULL, DatePickerTimeMode.HOURS_MINUTES, DatePickerTimeMode.HOURS_ONLY].includes(timeMode);
527
483
  const renderMinutes = [DatePickerTimeMode.FULL, DatePickerTimeMode.HOURS_MINUTES, DatePickerTimeMode.MINUTES_ONLY].includes(timeMode);
528
484
  const renderSeconds = [DatePickerTimeMode.FULL, DatePickerTimeMode.SECONDS_ONLY].includes(timeMode);
529
485
  const timeControls: Array<React.ReactElement> = [];
530
- if (renderHours) timeControls.push(renderTimeControl([0, 1], 2, secondHourMax, true));
531
- if (renderMinutes) timeControls.push(renderTimeControl([2, 3], 5, 9, !renderHours));
532
- if (renderSeconds) timeControls.push(renderTimeControl([4, 5], 5, 9, !renderHours && !renderMinutes));
486
+ if (renderHours) timeControls.push(renderTimeSwiper('hour'));
487
+ if (renderMinutes) timeControls.push(renderTimeSwiper('minute'));
488
+ if (renderSeconds) timeControls.push(renderTimeSwiper('second'));
533
489
  return (
534
- <div className='time-controls'>
490
+ <div className='time-swiper-container'>
491
+ <div className='vizor top'/>
535
492
  {timeControls.map((control: React.ReactElement, index: number) => {
536
493
  return (
537
494
  <Fragment key={index}>
@@ -540,30 +497,42 @@ function TimePicker(props: TimePickerProps) {
540
497
  </Fragment>
541
498
  )
542
499
  })}
500
+ <div className='vizor bottom'/>
543
501
  </div>
544
502
  )
545
503
  }
546
504
 
547
- function renderTimeControl(timePartIndexes: [number, number], firstMax: number, secondMax: number = 9,
548
- autoFocus: boolean = false) {
505
+ function renderTimeSwiper(key: 'hour' | 'minute' | 'second') {
506
+ const _value = value ?? new IDate('00:00:00', 'HH:mm:ss');
507
+ let timeValue: number, numSlides: number;
508
+ if (key === 'hour') {
509
+ timeValue = _value.hours();
510
+ numSlides = 24;
511
+ } else if (key === 'minute') {
512
+ timeValue = _value.minutes();
513
+ numSlides = 60;
514
+ } else {
515
+ timeValue = _value.seconds();
516
+ numSlides = 60;
517
+ }
518
+ const slideValues: Array<string> = [];
519
+ for (let i = 0; i < numSlides; i++) slideValues.push(pad(i));
549
520
  return (
550
- <div className='time-control'>
551
- <DvrdNumberInput value={timeParts[timePartIndexes[0]]} onChange={voidFunction}
552
- inputProps={{min: 0, max: firstMax, tabIndex: timePartIndexes[0] + 1, maxLength: 1}}
553
- className='time-control-part' id={`${compID.current}-${timePartIndexes[0]}`}
554
- onKeyDown={onKeyDown(timePartIndexes[0])} placeholder='0' autoFocus={autoFocus}
555
- autoSelect wholeNumbers/>
556
- <DvrdNumberInput value={timeParts[timePartIndexes[1]]} onChange={voidFunction}
557
- inputProps={{min: 0, max: secondMax, tabIndex: timePartIndexes[1] + 1, maxLength: 1}}
558
- onKeyDown={onKeyDown(timePartIndexes[1])} className='time-control-part'
559
- id={`${compID.current}-${timePartIndexes[1]}`} placeholder='0' autoSelect
560
- wholeNumbers/>
561
- </div>
521
+ <Swiper modules={[Mousewheel, FreeMode]} slidesPerView={3} direction='vertical' initialSlide={timeValue}
522
+ onTransitionEnd={onSlideChange(key)} mousewheel={{enabled: true, sensitivity: .5}} freeMode={{
523
+ enabled: true,
524
+ sticky: true,
525
+ minimumVelocity: .1,
526
+ momentumVelocityRatio: .25,
527
+ momentumRatio: .25
528
+ }} loop slideToClickedSlide centeredSlides>
529
+ {slideValues.map((value: string, index: number) => <SwiperSlide key={index}>{value}</SwiperSlide>)}
530
+ </Swiper>
562
531
  )
563
532
  }
564
533
 
565
534
  useEffect(() => {
566
- setTimeParts(dateToTimeParts(value));
535
+ if (value) internalValue.current = value;
567
536
  }, [value]);
568
537
 
569
538
  return (
@@ -572,7 +541,7 @@ function TimePicker(props: TimePickerProps) {
572
541
  <div className='switcher'>
573
542
  <label className='switcher-label'>Tijdstip</label>
574
543
  </div>
575
- {renderTimeControls()}
544
+ {renderTimeContainer()}
576
545
  <div className='actions-container'>
577
546
  <DvrdButton onClick={onClickNow} label='Huidig tijdstip' secondary className='action'/>
578
547
  <DvrdButton onClick={onSubmit} label='Oké' className='action'/>
@@ -296,6 +296,48 @@
296
296
  .picker.time {
297
297
  width: 20rem;
298
298
 
299
+ .time-swiper-container {
300
+ height: 10rem;
301
+ display: flex;
302
+ align-items: center;
303
+ column-gap: 1rem;
304
+ position: relative;
305
+ justify-content: center;
306
+
307
+ .swiper {
308
+ max-height: 100%;
309
+ margin: 0;
310
+
311
+ .swiper-slide {
312
+ display: flex;
313
+ align-items: center;
314
+ font-size: 1.2rem;
315
+ font-weight: 500;
316
+ }
317
+ }
318
+
319
+ .sep {
320
+ font-size: 1.2rem;
321
+ height: 1.2rem;
322
+ }
323
+
324
+ .vizor {
325
+ position: absolute;
326
+ background-color: $color-gray-4;
327
+ width: 100%;
328
+ height: 1px;
329
+ z-index: 1;
330
+
331
+ &.top {
332
+ top: 35%;
333
+ }
334
+
335
+ &.bottom {
336
+ top: 65%;
337
+ }
338
+ }
339
+ }
340
+
299
341
  .time-controls {
300
342
  display: flex;
301
343
  column-gap: .5rem;