@alfalab/core-components-date-range-input 1.1.0
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/Component.d.ts +190 -0
- package/Component.js +220 -0
- package/cssm/Component.d.ts +190 -0
- package/cssm/Component.js +219 -0
- package/cssm/index.d.ts +1 -0
- package/cssm/index.js +22 -0
- package/cssm/index.module.css +63 -0
- package/cssm/utils/format.d.ts +8 -0
- package/cssm/utils/format.js +98 -0
- package/cssm/utils/index.d.ts +1 -0
- package/cssm/utils/index.js +17 -0
- package/esm/Component.d.ts +190 -0
- package/esm/Component.js +209 -0
- package/esm/index.css +64 -0
- package/esm/index.d.ts +1 -0
- package/esm/index.js +13 -0
- package/esm/utils/format.d.ts +8 -0
- package/esm/utils/format.js +83 -0
- package/esm/utils/index.d.ts +1 -0
- package/esm/utils/index.js +3 -0
- package/index.css +64 -0
- package/index.d.ts +1 -0
- package/index.js +21 -0
- package/modern/Component.d.ts +190 -0
- package/modern/Component.js +166 -0
- package/modern/index.css +64 -0
- package/modern/index.d.ts +1 -0
- package/modern/index.js +13 -0
- package/modern/utils/format.d.ts +8 -0
- package/modern/utils/format.js +76 -0
- package/modern/utils/index.d.ts +1 -0
- package/modern/utils/index.js +3 -0
- package/package.json +24 -0
- package/send-stats.js +82 -0
- package/utils/format.d.ts +8 -0
- package/utils/format.js +98 -0
- package/utils/index.d.ts +1 -0
- package/utils/index.js +17 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { ChangeEvent, ElementType } from "react";
|
|
4
|
+
import { InputProps } from "@alfalab/core-components-input";
|
|
5
|
+
import { CalendarProps, CalendarMobileProps } from "@alfalab/core-components-calendar";
|
|
6
|
+
import { PopoverProps } from "@alfalab/core-components-popover";
|
|
7
|
+
type DateRangeInputProps = Omit<InputProps, 'onChange'> & {
|
|
8
|
+
/**
|
|
9
|
+
* Дополнительный класс
|
|
10
|
+
*/
|
|
11
|
+
className?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Дополнительный класс для инпута
|
|
14
|
+
*/
|
|
15
|
+
inputClassName?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Дополнительный класс для поповера
|
|
18
|
+
*/
|
|
19
|
+
popoverClassName?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Обработчик изменения значения
|
|
22
|
+
*/
|
|
23
|
+
picker?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Обработчик изменения значения
|
|
26
|
+
*/
|
|
27
|
+
onChange?: (payload: {
|
|
28
|
+
dateFrom?: Date;
|
|
29
|
+
dateTo?: Date;
|
|
30
|
+
value: string;
|
|
31
|
+
}, event?: ChangeEvent<HTMLInputElement>) => void;
|
|
32
|
+
/**
|
|
33
|
+
* Обработчик окончания ввода
|
|
34
|
+
*/
|
|
35
|
+
onComplete?: (payload: {
|
|
36
|
+
dateFrom: Date;
|
|
37
|
+
dateTo: Date;
|
|
38
|
+
value: string;
|
|
39
|
+
}, event?: ChangeEvent<HTMLInputElement>) => void;
|
|
40
|
+
/**
|
|
41
|
+
* Компонент календаря
|
|
42
|
+
*/
|
|
43
|
+
Calendar?: ElementType<CalendarProps>;
|
|
44
|
+
/**
|
|
45
|
+
* Доп. пропсы для календаря
|
|
46
|
+
*/
|
|
47
|
+
calendarProps?: (CalendarProps & Record<string, unknown>) | (CalendarMobileProps & Record<string, unknown>);
|
|
48
|
+
/**
|
|
49
|
+
* Месяц в календаре по умолчанию (timestamp)
|
|
50
|
+
*/
|
|
51
|
+
defaultMonth?: number;
|
|
52
|
+
/**
|
|
53
|
+
* Минимальная дата, доступная для выбора (timestamp)
|
|
54
|
+
*/
|
|
55
|
+
minDate?: number;
|
|
56
|
+
/**
|
|
57
|
+
* Максимальная дата, доступная для выбора (timestamp)
|
|
58
|
+
*/
|
|
59
|
+
maxDate?: number;
|
|
60
|
+
/**
|
|
61
|
+
* Список событий
|
|
62
|
+
*/
|
|
63
|
+
events?: Array<Date | number>;
|
|
64
|
+
/**
|
|
65
|
+
* Список выходных
|
|
66
|
+
*/
|
|
67
|
+
offDays?: Array<Date | number>;
|
|
68
|
+
/**
|
|
69
|
+
* Состояние открытия по умолчанию
|
|
70
|
+
*/
|
|
71
|
+
defaultOpen?: boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Позиционирование поповера с календарем
|
|
74
|
+
*/
|
|
75
|
+
popoverPosition?: PopoverProps['position'];
|
|
76
|
+
/**
|
|
77
|
+
* z-index Popover
|
|
78
|
+
*/
|
|
79
|
+
zIndexPopover?: PopoverProps['zIndex'];
|
|
80
|
+
/**
|
|
81
|
+
* Запрещает поповеру менять свою позицию.
|
|
82
|
+
* Например, если места снизу недостаточно,то он все равно будет показан снизу
|
|
83
|
+
*/
|
|
84
|
+
preventFlip?: boolean;
|
|
85
|
+
/**
|
|
86
|
+
* Календарь будет принимать ширину инпута
|
|
87
|
+
*/
|
|
88
|
+
useAnchorWidth?: boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Растягивает компонент на ширину контейнера
|
|
91
|
+
*/
|
|
92
|
+
block?: boolean;
|
|
93
|
+
};
|
|
94
|
+
declare const DateRangeInput: React.ForwardRefExoticComponent<Pick<InputProps, "className" | "dataTestId" | "form" | "label" | "slot" | "style" | "title" | "pattern" | "leftAddons" | "rightAddons" | "size" | "block" | "colors" | "children" | "type" | "defaultChecked" | "defaultValue" | "suppressContentEditableWarning" | "suppressHydrationWarning" | "accessKey" | "contentEditable" | "contextMenu" | "dir" | "draggable" | "hidden" | "id" | "lang" | "placeholder" | "spellCheck" | "tabIndex" | "translate" | "radioGroup" | "role" | "about" | "datatype" | "inlist" | "prefix" | "property" | "resource" | "typeof" | "vocab" | "autoCapitalize" | "autoCorrect" | "autoSave" | "color" | "itemProp" | "itemScope" | "itemType" | "itemID" | "itemRef" | "results" | "security" | "unselectable" | "inputMode" | "is" | "aria-activedescendant" | "aria-atomic" | "aria-autocomplete" | "aria-busy" | "aria-checked" | "aria-colcount" | "aria-colindex" | "aria-colspan" | "aria-controls" | "aria-current" | "aria-describedby" | "aria-details" | "aria-disabled" | "aria-dropeffect" | "aria-errormessage" | "aria-expanded" | "aria-flowto" | "aria-grabbed" | "aria-haspopup" | "aria-hidden" | "aria-invalid" | "aria-keyshortcuts" | "aria-label" | "aria-labelledby" | "aria-level" | "aria-live" | "aria-modal" | "aria-multiline" | "aria-multiselectable" | "aria-orientation" | "aria-owns" | "aria-placeholder" | "aria-posinset" | "aria-pressed" | "aria-readonly" | "aria-relevant" | "aria-required" | "aria-roledescription" | "aria-rowcount" | "aria-rowindex" | "aria-rowspan" | "aria-selected" | "aria-setsize" | "aria-sort" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "dangerouslySetInnerHTML" | "onCopy" | "onCopyCapture" | "onCut" | "onCutCapture" | "onPaste" | "onPasteCapture" | "onCompositionEnd" | "onCompositionEndCapture" | "onCompositionStart" | "onCompositionStartCapture" | "onCompositionUpdate" | "onCompositionUpdateCapture" | "onFocus" | "onFocusCapture" | "onBlur" | "onBlurCapture" | "onChangeCapture" | "onBeforeInput" | "onBeforeInputCapture" | "onInput" | "onInputCapture" | "onReset" | "onResetCapture" | "onSubmit" | "onSubmitCapture" | "onInvalid" | "onInvalidCapture" | "onLoad" | "onLoadCapture" | "onError" | "onErrorCapture" | "onKeyDown" | "onKeyDownCapture" | "onKeyPress" | "onKeyPressCapture" | "onKeyUp" | "onKeyUpCapture" | "onAbort" | "onAbortCapture" | "onCanPlay" | "onCanPlayCapture" | "onCanPlayThrough" | "onCanPlayThroughCapture" | "onDurationChange" | "onDurationChangeCapture" | "onEmptied" | "onEmptiedCapture" | "onEncrypted" | "onEncryptedCapture" | "onEnded" | "onEndedCapture" | "onLoadedData" | "onLoadedDataCapture" | "onLoadedMetadata" | "onLoadedMetadataCapture" | "onLoadStart" | "onLoadStartCapture" | "onPause" | "onPauseCapture" | "onPlay" | "onPlayCapture" | "onPlaying" | "onPlayingCapture" | "onProgress" | "onProgressCapture" | "onRateChange" | "onRateChangeCapture" | "onSeeked" | "onSeekedCapture" | "onSeeking" | "onSeekingCapture" | "onStalled" | "onStalledCapture" | "onSuspend" | "onSuspendCapture" | "onTimeUpdate" | "onTimeUpdateCapture" | "onVolumeChange" | "onVolumeChangeCapture" | "onWaiting" | "onWaitingCapture" | "onAuxClick" | "onAuxClickCapture" | "onClick" | "onClickCapture" | "onContextMenu" | "onContextMenuCapture" | "onDoubleClick" | "onDoubleClickCapture" | "onDrag" | "onDragCapture" | "onDragEnd" | "onDragEndCapture" | "onDragEnter" | "onDragEnterCapture" | "onDragExit" | "onDragExitCapture" | "onDragLeave" | "onDragLeaveCapture" | "onDragOver" | "onDragOverCapture" | "onDragStart" | "onDragStartCapture" | "onDrop" | "onDropCapture" | "onMouseDown" | "onMouseDownCapture" | "onMouseEnter" | "onMouseLeave" | "onMouseMove" | "onMouseMoveCapture" | "onMouseOut" | "onMouseOutCapture" | "onMouseOver" | "onMouseOverCapture" | "onMouseUp" | "onMouseUpCapture" | "onSelect" | "onSelectCapture" | "onTouchCancel" | "onTouchCancelCapture" | "onTouchEnd" | "onTouchEndCapture" | "onTouchMove" | "onTouchMoveCapture" | "onTouchStart" | "onTouchStartCapture" | "onPointerDown" | "onPointerDownCapture" | "onPointerMove" | "onPointerMoveCapture" | "onPointerUp" | "onPointerUpCapture" | "onPointerCancel" | "onPointerCancelCapture" | "onPointerEnter" | "onPointerEnterCapture" | "onPointerLeave" | "onPointerLeaveCapture" | "onPointerOver" | "onPointerOverCapture" | "onPointerOut" | "onPointerOutCapture" | "onGotPointerCapture" | "onGotPointerCaptureCapture" | "onLostPointerCapture" | "onLostPointerCaptureCapture" | "onScroll" | "onScrollCapture" | "onWheel" | "onWheelCapture" | "onAnimationStart" | "onAnimationStartCapture" | "onAnimationEnd" | "onAnimationEndCapture" | "onAnimationIteration" | "onAnimationIterationCapture" | "onTransitionEnd" | "onTransitionEndCapture" | "list" | "step" | "autoFocus" | "disabled" | "formAction" | "formEncType" | "formMethod" | "formNoValidate" | "formTarget" | "name" | "value" | "fieldClassName" | "labelClassName" | "addonsClassName" | "error" | "hint" | "labelView" | "bottomAddons" | "accept" | "alt" | "autoComplete" | "capture" | "checked" | "crossOrigin" | "height" | "max" | "maxLength" | "min" | "minLength" | "multiple" | "readOnly" | "required" | "src" | "width" | "clear" | "success" | "inputClassName" | "focusedClassName" | "filledClassName" | "onClear" | "wrapperRef"> & {
|
|
95
|
+
/**
|
|
96
|
+
* Дополнительный класс
|
|
97
|
+
*/
|
|
98
|
+
className?: string | undefined;
|
|
99
|
+
/**
|
|
100
|
+
* Дополнительный класс для инпута
|
|
101
|
+
*/
|
|
102
|
+
inputClassName?: string | undefined;
|
|
103
|
+
/**
|
|
104
|
+
* Дополнительный класс для поповера
|
|
105
|
+
*/
|
|
106
|
+
popoverClassName?: string | undefined;
|
|
107
|
+
/**
|
|
108
|
+
* Обработчик изменения значения
|
|
109
|
+
*/
|
|
110
|
+
picker?: boolean | undefined;
|
|
111
|
+
/**
|
|
112
|
+
* Обработчик изменения значения
|
|
113
|
+
*/
|
|
114
|
+
onChange?: ((payload: {
|
|
115
|
+
dateFrom?: Date | undefined;
|
|
116
|
+
dateTo?: Date | undefined;
|
|
117
|
+
value: string;
|
|
118
|
+
}, event?: React.ChangeEvent<HTMLInputElement> | undefined) => void) | undefined;
|
|
119
|
+
/**
|
|
120
|
+
* Обработчик окончания ввода
|
|
121
|
+
*/
|
|
122
|
+
onComplete?: ((payload: {
|
|
123
|
+
dateFrom: Date;
|
|
124
|
+
dateTo: Date;
|
|
125
|
+
value: string;
|
|
126
|
+
}, event?: React.ChangeEvent<HTMLInputElement> | undefined) => void) | undefined;
|
|
127
|
+
/**
|
|
128
|
+
* Компонент календаря
|
|
129
|
+
*/
|
|
130
|
+
Calendar?: React.ComponentClass<CalendarProps, any> | React.FunctionComponent<CalendarProps> | undefined;
|
|
131
|
+
/**
|
|
132
|
+
* Доп. пропсы для календаря
|
|
133
|
+
*/
|
|
134
|
+
calendarProps?: (CalendarProps & Record<string, unknown>) | (CalendarProps & {
|
|
135
|
+
open: boolean;
|
|
136
|
+
title?: string | undefined;
|
|
137
|
+
onClose?: (() => void) | undefined;
|
|
138
|
+
yearsAmount?: number | undefined;
|
|
139
|
+
hasHeader?: boolean | undefined; /**
|
|
140
|
+
* Дополнительный класс
|
|
141
|
+
*/
|
|
142
|
+
allowSelectionFromEmptyRange?: boolean | undefined;
|
|
143
|
+
} & Record<string, unknown>) | undefined;
|
|
144
|
+
/**
|
|
145
|
+
* Месяц в календаре по умолчанию (timestamp)
|
|
146
|
+
*/
|
|
147
|
+
defaultMonth?: number | undefined;
|
|
148
|
+
/**
|
|
149
|
+
* Минимальная дата, доступная для выбора (timestamp)
|
|
150
|
+
*/
|
|
151
|
+
minDate?: number | undefined;
|
|
152
|
+
/**
|
|
153
|
+
* Максимальная дата, доступная для выбора (timestamp)
|
|
154
|
+
*/
|
|
155
|
+
maxDate?: number | undefined;
|
|
156
|
+
/**
|
|
157
|
+
* Список событий
|
|
158
|
+
*/
|
|
159
|
+
events?: (number | Date)[] | undefined;
|
|
160
|
+
/**
|
|
161
|
+
* Список выходных
|
|
162
|
+
*/
|
|
163
|
+
offDays?: (number | Date)[] | undefined;
|
|
164
|
+
/**
|
|
165
|
+
* Состояние открытия по умолчанию
|
|
166
|
+
*/
|
|
167
|
+
defaultOpen?: boolean | undefined;
|
|
168
|
+
/**
|
|
169
|
+
* Позиционирование поповера с календарем
|
|
170
|
+
*/
|
|
171
|
+
popoverPosition?: "right" | "left" | "top" | "bottom" | "top-start" | "top-end" | "bottom-start" | "bottom-end" | "right-start" | "right-end" | "left-start" | "left-end" | undefined;
|
|
172
|
+
/**
|
|
173
|
+
* z-index Popover
|
|
174
|
+
*/
|
|
175
|
+
zIndexPopover?: number | undefined;
|
|
176
|
+
/**
|
|
177
|
+
* Запрещает поповеру менять свою позицию.
|
|
178
|
+
* Например, если места снизу недостаточно,то он все равно будет показан снизу
|
|
179
|
+
*/
|
|
180
|
+
preventFlip?: boolean | undefined;
|
|
181
|
+
/**
|
|
182
|
+
* Календарь будет принимать ширину инпута
|
|
183
|
+
*/
|
|
184
|
+
useAnchorWidth?: boolean | undefined;
|
|
185
|
+
/**
|
|
186
|
+
* Растягивает компонент на ширину контейнера
|
|
187
|
+
*/
|
|
188
|
+
block?: boolean | undefined;
|
|
189
|
+
} & React.RefAttributes<HTMLInputElement>>;
|
|
190
|
+
export { DateRangeInputProps, DateRangeInput };
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import React, { useRef, useState, useEffect } from 'react';
|
|
2
|
+
import cn from 'classnames';
|
|
3
|
+
import mergeRefs from 'react-merge-refs';
|
|
4
|
+
import dateFnsIsValid from 'date-fns/isValid';
|
|
5
|
+
import { useMedia } from '@alfalab/hooks';
|
|
6
|
+
import { Input } from '@alfalab/core-components-input/modern';
|
|
7
|
+
import { Calendar, usePeriod, CalendarMobile } from '@alfalab/core-components-calendar/modern';
|
|
8
|
+
import { Popover } from '@alfalab/core-components-popover/modern';
|
|
9
|
+
import { IconButton } from '@alfalab/core-components-icon-button/modern';
|
|
10
|
+
import { CalendarMIcon } from '@alfalab/icons-glyph/CalendarMIcon';
|
|
11
|
+
import 'date-fns/parse';
|
|
12
|
+
import { parseTimestampToDate, DATE_FORMAT, DATE_MASK, format, parseDateString, isCompleteDateInput, isValid } from './utils/format.js';
|
|
13
|
+
|
|
14
|
+
var styles = {"component":"date-range-input__component_kztbp","calendarContainer":"date-range-input__calendarContainer_kztbp","calendarResponsive":"date-range-input__calendarResponsive_kztbp","block":"date-range-input__block_kztbp"};
|
|
15
|
+
require('./index.css')
|
|
16
|
+
|
|
17
|
+
/* eslint-disable no-useless-escape, jsx-a11y/click-events-have-key-events */
|
|
18
|
+
const DateRangeInput = React.forwardRef(({ className, inputClassName, popoverClassName, disabled, readOnly, picker, defaultValue = '', value: propValue, onChange, onComplete, rightAddons, useAnchorWidth, block, popoverPosition = 'bottom-start', zIndexPopover, preventFlip, Calendar: Calendar$1 = Calendar, calendarProps = {}, defaultMonth, minDate = calendarProps.minDate, maxDate = calendarProps.maxDate, offDays = calendarProps.offDays || [], events = calendarProps.events || [], defaultOpen = false, ...restProps }, ref) => {
|
|
19
|
+
const [view] = useMedia([
|
|
20
|
+
['mobile', '(max-width: 1023px)'],
|
|
21
|
+
['desktop', '(min-width: 1024px)'],
|
|
22
|
+
], 'desktop');
|
|
23
|
+
const inputRef = useRef(null);
|
|
24
|
+
const calendarRef = useRef(null);
|
|
25
|
+
const [value, setValue] = useState(propValue || defaultValue);
|
|
26
|
+
const [open, setOpen] = useState(false);
|
|
27
|
+
const inputDisabled = disabled || readOnly;
|
|
28
|
+
const CalendarComponent = view === 'desktop' ? Calendar$1 : CalendarMobile;
|
|
29
|
+
const calendarResponsive = calendarProps?.responsive ?? true;
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
setOpen(defaultOpen);
|
|
32
|
+
}, [defaultOpen]);
|
|
33
|
+
const handlePeriodChange = (selectedFrom, selectedTo) => {
|
|
34
|
+
if (selectedFrom && !selectedTo && value.length === DATE_MASK.length) {
|
|
35
|
+
setValue(parseTimestampToDate(selectedFrom));
|
|
36
|
+
}
|
|
37
|
+
else if ((!selectedFrom && !selectedTo && value.length === DATE_FORMAT.length) ||
|
|
38
|
+
(selectedFrom === selectedTo && value.length === DATE_MASK.length)) {
|
|
39
|
+
setValue('');
|
|
40
|
+
}
|
|
41
|
+
if (onChange) {
|
|
42
|
+
onChange({
|
|
43
|
+
dateFrom: selectedFrom ? new Date(selectedFrom) : undefined,
|
|
44
|
+
dateTo: selectedTo ? new Date(selectedTo) : undefined,
|
|
45
|
+
value,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
if (onComplete && selectedFrom && selectedTo) {
|
|
49
|
+
onComplete({
|
|
50
|
+
dateFrom: new Date(selectedFrom),
|
|
51
|
+
dateTo: new Date(selectedTo),
|
|
52
|
+
value,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
const { selectedFrom, selectedTo, updatePeriod, resetPeriod, setStart, setEnd, } = usePeriod({ onPeriodChange: handlePeriodChange });
|
|
57
|
+
const handleInputWrapperFocus = (event) => {
|
|
58
|
+
if (view === 'desktop') {
|
|
59
|
+
if (!open && event.target.tagName !== 'INPUT' && calendarRef.current) {
|
|
60
|
+
calendarRef.current.focus();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
const handleBlur = (event) => {
|
|
65
|
+
if (view === 'desktop') {
|
|
66
|
+
const target = (event.relatedTarget || document.activeElement);
|
|
67
|
+
if (calendarRef.current && calendarRef.current.contains(target) === false) {
|
|
68
|
+
setOpen(false);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
const handleChange = (event) => {
|
|
73
|
+
const { value: newValue } = event.target;
|
|
74
|
+
if (newValue.length > DATE_MASK.length)
|
|
75
|
+
return;
|
|
76
|
+
// Позволяем вводить только цифры, точки, дефис и пробелы
|
|
77
|
+
if (/[^\d. \- \d.]/.test(newValue)) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const dots = newValue.match(/\./g);
|
|
81
|
+
const hyphen = newValue.match(/\-/g);
|
|
82
|
+
// Не даем вводить больше, чем 4 точки и 1 дефис
|
|
83
|
+
if ((dots && dots.length > 4) || (hyphen && hyphen.length > 1)) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const formattedValue = format(newValue);
|
|
87
|
+
const dateArr = formattedValue.split(' - ');
|
|
88
|
+
const dateFrom = parseDateString(dateArr[0]);
|
|
89
|
+
const dateTo = parseDateString(dateArr[1]);
|
|
90
|
+
if (selectedFrom && formattedValue.length < DATE_FORMAT.length) {
|
|
91
|
+
setStart();
|
|
92
|
+
}
|
|
93
|
+
else if (selectedFrom && selectedTo) {
|
|
94
|
+
setEnd();
|
|
95
|
+
}
|
|
96
|
+
else if (dateFnsIsValid(dateFrom) &&
|
|
97
|
+
dateArr[0]?.length === DATE_FORMAT.length &&
|
|
98
|
+
dateFrom.getTime() !== selectedFrom) {
|
|
99
|
+
setStart(dateFrom.getTime());
|
|
100
|
+
}
|
|
101
|
+
else if (dateFnsIsValid(dateTo) &&
|
|
102
|
+
dateArr[1]?.length === DATE_FORMAT.length &&
|
|
103
|
+
dateTo.getTime() !== selectedTo) {
|
|
104
|
+
setEnd(dateTo.getTime());
|
|
105
|
+
}
|
|
106
|
+
setValue(formattedValue);
|
|
107
|
+
if (onChange)
|
|
108
|
+
onChange({ dateFrom, dateTo, value: formattedValue }, event);
|
|
109
|
+
if (isCompleteDateInput(formattedValue)) {
|
|
110
|
+
const valid = isValid(formattedValue, dateArr[0], dateArr[1]);
|
|
111
|
+
if (!valid)
|
|
112
|
+
return;
|
|
113
|
+
if (onComplete) {
|
|
114
|
+
onComplete({ dateFrom, dateTo, value: formattedValue }, event);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
const handleCalendarClose = () => {
|
|
119
|
+
setOpen(false);
|
|
120
|
+
};
|
|
121
|
+
const handleClear = () => {
|
|
122
|
+
setValue('');
|
|
123
|
+
resetPeriod();
|
|
124
|
+
};
|
|
125
|
+
const handleCalendarChange = (date) => {
|
|
126
|
+
if (date) {
|
|
127
|
+
updatePeriod(date);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
useEffect(() => {
|
|
131
|
+
if (selectedFrom && selectedTo) {
|
|
132
|
+
setValue(`${parseTimestampToDate(selectedFrom)} - ${parseTimestampToDate(selectedTo)}`);
|
|
133
|
+
}
|
|
134
|
+
else if (selectedFrom && value.length < DATE_FORMAT.length) {
|
|
135
|
+
setValue(parseTimestampToDate(selectedFrom));
|
|
136
|
+
}
|
|
137
|
+
}, [selectedFrom, selectedTo, value]);
|
|
138
|
+
const handleCalendarWrapperMouseDown = (event) => {
|
|
139
|
+
// Не дает инпуту терять фокус при выборе даты
|
|
140
|
+
event.preventDefault();
|
|
141
|
+
};
|
|
142
|
+
const handleInputWrapperClick = () => {
|
|
143
|
+
if (view === 'desktop' && !open) {
|
|
144
|
+
setOpen(true);
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
const handleIconButtonClick = () => {
|
|
148
|
+
if (!open)
|
|
149
|
+
setOpen(true);
|
|
150
|
+
};
|
|
151
|
+
const renderCalendar = () => (
|
|
152
|
+
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
|
|
153
|
+
React.createElement("div", { onMouseDown: handleCalendarWrapperMouseDown },
|
|
154
|
+
React.createElement(CalendarComponent, Object.assign({}, calendarProps, { responsive: calendarResponsive, open: open, onClose: handleCalendarClose, ref: calendarRef, defaultMonth: defaultMonth, selectedFrom: selectedFrom, selectedTo: selectedTo, onChange: handleCalendarChange, minDate: minDate, maxDate: maxDate, offDays: offDays, events: events }))));
|
|
155
|
+
return (React.createElement("div", { className: cn(styles.component, className, {
|
|
156
|
+
[styles.block]: block,
|
|
157
|
+
}), onClick: inputDisabled ? undefined : handleInputWrapperClick, onFocus: inputDisabled ? undefined : handleInputWrapperFocus, onBlur: handleBlur },
|
|
158
|
+
React.createElement(Input, Object.assign({}, restProps, { block: block, ref: mergeRefs([ref, inputRef]), value: value, onChange: handleChange, disabled: disabled, readOnly: readOnly, className: inputClassName, onClear: handleClear, rightAddons: React.createElement(React.Fragment, null,
|
|
159
|
+
rightAddons,
|
|
160
|
+
picker && (React.createElement(IconButton, { onClick: inputDisabled ? undefined : handleIconButtonClick, icon: CalendarMIcon, size: 'xxs' }))) })),
|
|
161
|
+
picker && (React.createElement(Popover, { open: open, useAnchorWidth: useAnchorWidth, anchorElement: inputRef.current, popperClassName: cn(styles.calendarContainer, {
|
|
162
|
+
[styles.calendarResponsive]: calendarResponsive,
|
|
163
|
+
}), className: popoverClassName, position: popoverPosition, offset: [0, 8], withTransition: false, preventFlip: preventFlip, zIndex: zIndexPopover }, renderCalendar()))));
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
export { DateRangeInput };
|
package/modern/index.css
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/* hash: in3xf */
|
|
2
|
+
:root {
|
|
3
|
+
|
|
4
|
+
/* Hard */
|
|
5
|
+
|
|
6
|
+
/* Up */
|
|
7
|
+
|
|
8
|
+
/* Hard up */
|
|
9
|
+
}
|
|
10
|
+
:root {
|
|
11
|
+
--gap-m: 16px;
|
|
12
|
+
}
|
|
13
|
+
:root {
|
|
14
|
+
--border-radius-s: 4px;
|
|
15
|
+
}
|
|
16
|
+
:root {
|
|
17
|
+
--calendar-width: 344px;
|
|
18
|
+
|
|
19
|
+
/* Кнопки выбора месяцев и годов */
|
|
20
|
+
|
|
21
|
+
/* Шапка */
|
|
22
|
+
|
|
23
|
+
/* День */
|
|
24
|
+
|
|
25
|
+
/* today */
|
|
26
|
+
|
|
27
|
+
/* highlighted */
|
|
28
|
+
|
|
29
|
+
/* holidays */
|
|
30
|
+
|
|
31
|
+
/* range */
|
|
32
|
+
|
|
33
|
+
/* selected */
|
|
34
|
+
|
|
35
|
+
/* disabled */
|
|
36
|
+
|
|
37
|
+
/* marker */
|
|
38
|
+
}
|
|
39
|
+
:root {
|
|
40
|
+
--calendar-popover-border-radius: 0 0 var(--border-radius-s) var(--border-radius-s);
|
|
41
|
+
}
|
|
42
|
+
.date-range-input__component_kztbp {
|
|
43
|
+
display: inline-block;
|
|
44
|
+
outline: none;
|
|
45
|
+
position: relative;
|
|
46
|
+
}
|
|
47
|
+
.date-range-input__calendarContainer_kztbp {
|
|
48
|
+
display: inline-block;
|
|
49
|
+
box-sizing: border-box;
|
|
50
|
+
border-radius: var(--calendar-popover-border-radius)
|
|
51
|
+
}
|
|
52
|
+
@media (max-width: 374px) {
|
|
53
|
+
.date-range-input__calendarContainer_kztbp {
|
|
54
|
+
width: 100%;
|
|
55
|
+
min-width: 288px
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
.date-range-input__calendarResponsive_kztbp {
|
|
59
|
+
width: var(--calendar-width);
|
|
60
|
+
padding: 0 var(--gap-m);
|
|
61
|
+
}
|
|
62
|
+
.date-range-input__block_kztbp {
|
|
63
|
+
width: 100%;
|
|
64
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./Component";
|
package/modern/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import 'react';
|
|
2
|
+
import 'classnames';
|
|
3
|
+
import 'react-merge-refs';
|
|
4
|
+
import 'date-fns/isValid';
|
|
5
|
+
import '@alfalab/hooks';
|
|
6
|
+
import '@alfalab/core-components-input/modern';
|
|
7
|
+
import '@alfalab/core-components-calendar/modern';
|
|
8
|
+
import '@alfalab/core-components-popover/modern';
|
|
9
|
+
import '@alfalab/core-components-icon-button/modern';
|
|
10
|
+
import '@alfalab/icons-glyph/CalendarMIcon';
|
|
11
|
+
import 'date-fns/parse';
|
|
12
|
+
import './utils/format.js';
|
|
13
|
+
export { DateRangeInput } from './Component.js';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
declare const DATE_FORMAT = "dd.MM.yyyy";
|
|
2
|
+
declare const DATE_MASK: (string | RegExp)[];
|
|
3
|
+
declare const isCompleteDateInput: (input: string) => boolean;
|
|
4
|
+
declare const parseDateString: (value: string, dateFormat?: string) => Date;
|
|
5
|
+
declare const isValid: (inputValue: string, dateFrom: string, dateTo: string) => boolean;
|
|
6
|
+
declare const format: (value: string) => string;
|
|
7
|
+
declare const parseTimestampToDate: (timestamp: number) => string;
|
|
8
|
+
export { DATE_FORMAT, DATE_MASK, isCompleteDateInput, parseDateString, isValid, format, parseTimestampToDate };
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import dateFnsIsValid from 'date-fns/isValid';
|
|
2
|
+
import parse from 'date-fns/parse';
|
|
3
|
+
|
|
4
|
+
/* eslint-disable no-useless-escape */
|
|
5
|
+
const DATE_FORMAT = 'dd.MM.yyyy';
|
|
6
|
+
const DATE_MASK = [
|
|
7
|
+
/\d/,
|
|
8
|
+
/\d/,
|
|
9
|
+
'.',
|
|
10
|
+
/\d/,
|
|
11
|
+
/\d/,
|
|
12
|
+
'.',
|
|
13
|
+
/\d/,
|
|
14
|
+
/\d/,
|
|
15
|
+
/\d/,
|
|
16
|
+
/\d/,
|
|
17
|
+
' ',
|
|
18
|
+
'-',
|
|
19
|
+
' ',
|
|
20
|
+
/\d/,
|
|
21
|
+
/\d/,
|
|
22
|
+
'.',
|
|
23
|
+
/\d/,
|
|
24
|
+
/\d/,
|
|
25
|
+
'.',
|
|
26
|
+
/\d/,
|
|
27
|
+
/\d/,
|
|
28
|
+
/\d/,
|
|
29
|
+
/\d/,
|
|
30
|
+
];
|
|
31
|
+
const isCompleteDateInput = (input) => input.length === DATE_MASK.length;
|
|
32
|
+
const parseDateString = (value, dateFormat = DATE_FORMAT) => parse(value, dateFormat, new Date());
|
|
33
|
+
const isValid = (inputValue, dateFrom, dateTo) => !inputValue ||
|
|
34
|
+
(isCompleteDateInput(inputValue) &&
|
|
35
|
+
dateFnsIsValid(parseDateString(dateFrom)) &&
|
|
36
|
+
dateFnsIsValid(parseDateString(dateTo)));
|
|
37
|
+
const format = (value) => value
|
|
38
|
+
.replace(/^(\d\d)(\d)$/, '$1.$2') // 121 => 12.1
|
|
39
|
+
.replace(/^(\d\d)\.(\d\d)(\d)$/, '$1.$2.$3') // 12.122 => 12.12.2
|
|
40
|
+
.replace(/^(\d\d)\d\.(.*)/, '$1.$2') // 123.12.2005 => 12.12.2005
|
|
41
|
+
.replace(/^(\d\d\.\d\d)\d\.(.*)/, '$1.$2') // 12.123.2005 => 12.12.2005
|
|
42
|
+
.replace(/\.$/, '') // 12. => 12
|
|
43
|
+
.replace(/\ $/, '') // 1 2 => 12
|
|
44
|
+
.replace(/^(\d\d\.\d\d\.\d\d\d\d)(\d) - (\d.*)/, '$1 - $3') // 12.12.20051 - 12.12.200 => 12.12.2005 - 12.12.200
|
|
45
|
+
.replace(/^(\d\d\.\d\d\.\d\d\d\d) (\d)- (\d.*)/, '$1 - $3') // 12.12.2005 1- 12.12.200 => 12.12.2005 - 12.12.200
|
|
46
|
+
.replace(/^(\d\d\.\d\d\.\d\d\d\d) -(\d) (\d.*)/, '$1 - $3') // 12.12.2005 -1 12.12.200 => 12.12.2005 - 12.12.200
|
|
47
|
+
.replace(/^(\d\d\.\d\d\.\d\d\d\d) - (\d)(\d\d.\d\d.\d\d\d)/, '$1 - $3') // 12.12.2005 - 112.12.200 => 12.12.2005 - 12.12.200
|
|
48
|
+
.replace(/^(\d\d\.\d\d\.\d\d\d\d) - (\d)(\d\d.\d.\d\d\d\d)/, '$1 - $3') // 12.12.2005 - 112.1.2001 => 12.12.2005 - 12.1.2001
|
|
49
|
+
.replace(/^(\d\d\.\d\d)(\d\d\d\d)/, '$1.$2') // 12.122005 => 12.12.2005
|
|
50
|
+
.replace(/^(\d\d)(\d\d\.\d\d\d\d)/, '$1.$2') // 1212.2005 => 12.12.2005
|
|
51
|
+
.replace(/^(\d\d)(\d.*)/, '$1.$2') // 1212 => 12.12
|
|
52
|
+
.replace(/^(\d\d.\d\d)(\d.*)/, '$1.$2') // 12.122 => 12.12.2
|
|
53
|
+
.replace(/^(\d\d\.\d\d\.\d\d\d\d)(\d)/, '$1 - $2') // 12.12.20056 => 12.12.2005 - 6
|
|
54
|
+
.replace(/^(\d\d\.\d\d\.\d\d\d\d) - (\d\d)(\d)/, '$1 - $2.$3') // 12.12.2005 - 123 => 12.12.2005 - 12.3
|
|
55
|
+
.replace(/^(\d\d\.\d\d\.\d\d\d\d) - (\d\d).(\d\d)(\d)/, '$1 - $2.$3.$4') // 12.12.2005 - 12.123 => 12.12.2005 - 12.12.3
|
|
56
|
+
.replace(/^(\d\d\.\d\d\.\d\d\d\d)- (\d.*)/, '$1 - $2') // 12.12.2005- 12.12.2005 => 12.12.2005 - 12.12.2005
|
|
57
|
+
.replace(/^(\d\d\.\d\d\.\d\d\d\d) -(\d.*)/, '$1 - $2') // 12.12.2005 -12.12.2005 => 12.12.2005 - 12.12.2005
|
|
58
|
+
.replace(/^(\d\d\.\d\d\.\d\d\d\d) -/, '$1') // 12.12.2005 - => 12.12.2005
|
|
59
|
+
.replace(/^(\d\d\.\d\d\.\d\d\d\d) (\d.*)/, '$1 - $2') // 12.12.2005 12.12.2005 => 12.12.2005 - 12.12.2005
|
|
60
|
+
.replace(/^(\d\d\.\d\d\.\d\d\d\d) {2}(\d.*)/, '$1 - $2') // 12.12.2005 12.12.2005 => 12.12.2005 - 12.12.2005
|
|
61
|
+
.replace(/^(\d\d\.\d\d\.\d\d\d\d)-/, '$1'); // 12.12.2005- => 12.12.2005
|
|
62
|
+
const parseTimestampToDate = (timestamp) => {
|
|
63
|
+
const date = new Date(timestamp);
|
|
64
|
+
const year = date.getFullYear();
|
|
65
|
+
let month = date.getMonth() + 1;
|
|
66
|
+
let day = date.getDate();
|
|
67
|
+
if (month < 10) {
|
|
68
|
+
month = `0${month}`;
|
|
69
|
+
}
|
|
70
|
+
if (day < 10) {
|
|
71
|
+
day = `0${day}`;
|
|
72
|
+
}
|
|
73
|
+
return `${day}.${month}.${year}`;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export { DATE_FORMAT, DATE_MASK, format, isCompleteDateInput, isValid, parseDateString, parseTimestampToDate };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./format";
|
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@alfalab/core-components-date-range-input",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"keywords": [],
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "index.js",
|
|
8
|
+
"module": "./esm/index.js",
|
|
9
|
+
"publishConfig": {
|
|
10
|
+
"access": "public",
|
|
11
|
+
"directory": "dist"
|
|
12
|
+
},
|
|
13
|
+
"peerDependencies": {
|
|
14
|
+
"react": "^16.9.0 || ^17.0.1 || ^18.0.0",
|
|
15
|
+
"react-dom": "^16.9.0 || ^17.0.1 || ^18.0.0"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@alfalab/core-components-input": "^10.1.0",
|
|
19
|
+
"@alfalab/core-components-icon-button": "^5.0.3",
|
|
20
|
+
"@alfalab/core-components-calendar": "^6.1.4",
|
|
21
|
+
"@alfalab/core-components-popover": "^6.0.1",
|
|
22
|
+
"classnames": "^2.3.1"
|
|
23
|
+
}
|
|
24
|
+
}
|
package/send-stats.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
const http = require('http');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const { promisify } = require('util');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
const readFile = promisify(fs.readFile);
|
|
7
|
+
|
|
8
|
+
async function main() {
|
|
9
|
+
const remoteHost = process.env.NIS_HOST || 'digital';
|
|
10
|
+
const remotePort = process.env.NIS_PORT || 80;
|
|
11
|
+
const remotePath = process.env.NIS_PATH || '/npm-install-stats/api/install-stats';
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
const [_, node, os, arch] =
|
|
15
|
+
/node\/v(\d+\.\d+\.\d+) (\w+) (\w+)/.exec(process.env.npm_config_user_agent) || [];
|
|
16
|
+
const [__, npm] = /npm\/(\d+\.\d+\.\d+)/.exec(process.env.npm_config_user_agent) || [];
|
|
17
|
+
const [___, yarn] = /yarn\/(\d+\.\d+\.\d+)/.exec(process.env.npm_config_user_agent) || [];
|
|
18
|
+
|
|
19
|
+
let ownPackageJson, packageJson;
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
const result = await Promise.all([
|
|
23
|
+
readFile(path.join(process.cwd(), 'package.json'), 'utf-8'),
|
|
24
|
+
readFile(path.join(process.cwd(), '../../../package.json'), 'utf-8'),
|
|
25
|
+
]);
|
|
26
|
+
|
|
27
|
+
ownPackageJson = JSON.parse(result[0]);
|
|
28
|
+
packageJson = JSON.parse(result[1]);
|
|
29
|
+
} catch (err) {
|
|
30
|
+
ownPackageJson = '';
|
|
31
|
+
packageJson = '';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const data = {
|
|
35
|
+
node,
|
|
36
|
+
npm,
|
|
37
|
+
yarn,
|
|
38
|
+
os,
|
|
39
|
+
arch,
|
|
40
|
+
ownPackageJson: JSON.stringify(ownPackageJson),
|
|
41
|
+
packageJson: JSON.stringify(packageJson),
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const body = JSON.stringify(data);
|
|
45
|
+
|
|
46
|
+
const options = {
|
|
47
|
+
host: remoteHost,
|
|
48
|
+
port: remotePort,
|
|
49
|
+
path: remotePath,
|
|
50
|
+
method: 'POST',
|
|
51
|
+
headers: {
|
|
52
|
+
'Content-Type': 'application/json',
|
|
53
|
+
'Content-Length': body.length,
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
return new Promise((resolve, reject) => {
|
|
58
|
+
const req = http.request(options, res => {
|
|
59
|
+
res.on('end', () => {
|
|
60
|
+
resolve();
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
req.on('error', () => {
|
|
65
|
+
reject();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
req.write(body);
|
|
69
|
+
req.end();
|
|
70
|
+
});
|
|
71
|
+
} catch (error) {
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
main()
|
|
77
|
+
.then(() => {
|
|
78
|
+
process.exit(0);
|
|
79
|
+
})
|
|
80
|
+
.catch(() => {
|
|
81
|
+
process.exit(0);
|
|
82
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
declare const DATE_FORMAT = "dd.MM.yyyy";
|
|
2
|
+
declare const DATE_MASK: (string | RegExp)[];
|
|
3
|
+
declare const isCompleteDateInput: (input: string) => boolean;
|
|
4
|
+
declare const parseDateString: (value: string, dateFormat?: string) => Date;
|
|
5
|
+
declare const isValid: (inputValue: string, dateFrom: string, dateTo: string) => boolean;
|
|
6
|
+
declare const format: (value: string) => string;
|
|
7
|
+
declare const parseTimestampToDate: (timestamp: number) => string;
|
|
8
|
+
export { DATE_FORMAT, DATE_MASK, isCompleteDateInput, parseDateString, isValid, format, parseTimestampToDate };
|