@alfalab/core-components-date-input 1.2.10 → 1.3.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.
Files changed (49) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/dist/Component.d.ts +16 -18
  3. package/dist/Component.js +65 -28
  4. package/dist/cssm/Component.d.ts +16 -18
  5. package/dist/cssm/Component.js +64 -27
  6. package/dist/cssm/index.js +4 -6
  7. package/dist/cssm/utils/format.d.ts +3 -1
  8. package/dist/cssm/utils/format.js +16 -0
  9. package/dist/cssm/utils/index.d.ts +0 -1
  10. package/dist/cssm/utils/index.js +2 -5
  11. package/dist/cssm/utils/native-supports.d.ts +1 -3
  12. package/dist/cssm/utils/native-supports.js +0 -4
  13. package/dist/esm/Component.d.ts +16 -18
  14. package/dist/esm/Component.js +67 -31
  15. package/dist/esm/index.css +4 -4
  16. package/dist/esm/index.js +4 -4
  17. package/dist/esm/utils/format.d.ts +3 -1
  18. package/dist/esm/utils/format.js +17 -3
  19. package/dist/esm/utils/index.d.ts +0 -1
  20. package/dist/esm/utils/index.js +2 -3
  21. package/dist/esm/utils/native-supports.d.ts +1 -3
  22. package/dist/esm/utils/native-supports.js +1 -3
  23. package/dist/index.css +4 -4
  24. package/dist/index.js +4 -6
  25. package/dist/modern/Component.d.ts +16 -18
  26. package/dist/modern/Component.js +67 -29
  27. package/dist/modern/index.css +4 -4
  28. package/dist/modern/index.js +4 -4
  29. package/dist/modern/utils/format.d.ts +3 -1
  30. package/dist/modern/utils/format.js +13 -3
  31. package/dist/modern/utils/index.d.ts +0 -1
  32. package/dist/modern/utils/index.js +2 -3
  33. package/dist/modern/utils/native-supports.d.ts +1 -3
  34. package/dist/modern/utils/native-supports.js +1 -3
  35. package/dist/utils/format.d.ts +3 -1
  36. package/dist/utils/format.js +16 -0
  37. package/dist/utils/index.d.ts +0 -1
  38. package/dist/utils/index.js +2 -5
  39. package/dist/utils/native-supports.d.ts +1 -3
  40. package/dist/utils/native-supports.js +0 -4
  41. package/package.json +5 -4
  42. package/dist/cssm/utils/date-correction-pipe.d.ts +0 -9
  43. package/dist/cssm/utils/date-correction-pipe.js +0 -59
  44. package/dist/esm/utils/date-correction-pipe.d.ts +0 -9
  45. package/dist/esm/utils/date-correction-pipe.js +0 -54
  46. package/dist/modern/utils/date-correction-pipe.d.ts +0 -9
  47. package/dist/modern/utils/date-correction-pipe.js +0 -52
  48. package/dist/utils/date-correction-pipe.d.ts +0 -9
  49. package/dist/utils/date-correction-pipe.js +0 -59
@@ -1,16 +1,8 @@
1
1
  /// <reference types="react" />
2
2
  import React from 'react';
3
3
  import { ChangeEvent } from "react";
4
- import { MaskedInputProps } from "@alfalab/core-components-masked-input";
5
- type DateInputProps = Omit<MaskedInputProps, 'onBeforeDisplay' | 'mask' | 'onChange'> & {
6
- /**
7
- * Минимальный год, доступный для ввода
8
- */
9
- minYear?: number;
10
- /**
11
- * Максимальный год, доступный для ввода
12
- */
13
- maxYear?: number;
4
+ import { InputProps } from "@alfalab/core-components-input";
5
+ type DateInputProps = Omit<InputProps, 'onChange'> & {
14
6
  /**
15
7
  * Управление нативным режимом на мобильных устройствах
16
8
  */
@@ -22,16 +14,15 @@ type DateInputProps = Omit<MaskedInputProps, 'onBeforeDisplay' | 'mask' | 'onCha
22
14
  date: Date;
23
15
  value: string;
24
16
  }) => void;
25
- };
26
- declare const DateInput: React.ForwardRefExoticComponent<Pick<MaskedInputProps, "className" | "dataTestId" | "form" | "label" | "slot" | "style" | "title" | "pattern" | "children" | "leftAddons" | "rightAddons" | "size" | "block" | "colors" | "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" | "bottomAddons" | "accept" | "alt" | "autoComplete" | "capture" | "checked" | "crossOrigin" | "height" | "max" | "maxLength" | "min" | "minLength" | "multiple" | "readOnly" | "required" | "src" | "width" | "clear" | "success" | "inputClassName" | "focusedClassName" | "filledClassName" | "onClear" | "wrapperRef" | "keepCharPositions"> & {
27
- /**
28
- * Минимальный год, доступный для ввода
29
- */
30
- minYear?: number | undefined;
31
17
  /**
32
- * Максимальный год, доступный для ввода
18
+ * Обработчик окончания ввода
33
19
  */
34
- maxYear?: number | undefined;
20
+ onComplete?: (event: ChangeEvent<HTMLInputElement>, payload: {
21
+ date: Date;
22
+ value: string;
23
+ }) => void;
24
+ };
25
+ declare const DateInput: React.ForwardRefExoticComponent<Pick<InputProps, "className" | "dataTestId" | "form" | "label" | "slot" | "style" | "title" | "pattern" | "children" | "leftAddons" | "rightAddons" | "size" | "block" | "colors" | "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" | "bottomAddons" | "accept" | "alt" | "autoComplete" | "capture" | "checked" | "crossOrigin" | "height" | "max" | "maxLength" | "min" | "minLength" | "multiple" | "readOnly" | "required" | "src" | "width" | "clear" | "success" | "inputClassName" | "focusedClassName" | "filledClassName" | "onClear" | "wrapperRef"> & {
35
26
  /**
36
27
  * Управление нативным режимом на мобильных устройствах
37
28
  */
@@ -43,5 +34,12 @@ declare const DateInput: React.ForwardRefExoticComponent<Pick<MaskedInputProps,
43
34
  date: Date;
44
35
  value: string;
45
36
  }) => void) | undefined;
37
+ /**
38
+ * Обработчик окончания ввода
39
+ */
40
+ onComplete?: ((event: React.ChangeEvent<HTMLInputElement>, payload: {
41
+ date: Date;
42
+ value: string;
43
+ }) => void) | undefined;
46
44
  } & React.RefAttributes<HTMLInputElement>>;
47
45
  export { DateInputProps, DateInput };
@@ -1,9 +1,9 @@
1
- import React, { useState, useMemo, useCallback } from 'react';
2
- import { MaskedInput } from '@alfalab/core-components-masked-input/dist/esm';
1
+ import React, { forwardRef, useRef, useState, useCallback, useEffect } from 'react';
2
+ import { Input } from '@alfalab/core-components-input/dist/esm';
3
+ import mergeRefs from 'react-merge-refs';
3
4
  import 'date-fns';
4
- import { parseDateString, NATIVE_DATE_FORMAT, formatDate } from './utils/format.js';
5
- import { createAutoCorrectedDatePipe, mask } from './utils/date-correction-pipe.js';
6
- import { SUPPORTS_INPUT_TYPE_DATE } from './utils/native-supports.js';
5
+ import { isValid, DATE_MASK, format, parseDateString, isCompleteDateInput, NATIVE_DATE_FORMAT, formatDate } from './utils/format.js';
6
+ import { isInputDateSupported } from './utils/native-supports.js';
7
7
 
8
8
  /*! *****************************************************************************
9
9
  Copyright (c) Microsoft Corporation.
@@ -44,40 +44,76 @@ function __rest(s, e) {
44
44
  return t;
45
45
  }
46
46
 
47
- var styles = {"nativeInput":"date-input__nativeInput_qb4yv"};
47
+ var styles = {"nativeInput":"date-input__nativeInput_1692t"};
48
48
  require('./index.css')
49
49
 
50
- var DateInput = React.forwardRef(function (_a, ref) {
51
- var maxYear = _a.maxYear, minYear = _a.minYear, _b = _a.mobileMode, mobileMode = _b === void 0 ? 'input' : _b, value = _a.value, defaultValue = _a.defaultValue, rightAddons = _a.rightAddons, onChange = _a.onChange, restProps = __rest(_a, ["maxYear", "minYear", "mobileMode", "value", "defaultValue", "rightAddons", "onChange"]);
52
- var uncontrolled = value === undefined;
53
- var shouldRenderNative = SUPPORTS_INPUT_TYPE_DATE && mobileMode === 'native';
54
- var _c = useState(defaultValue), stateValue = _c[0], setStateValue = _c[1];
55
- var inputValue = uncontrolled ? stateValue : value;
56
- var pipe = useMemo(function () {
57
- return createAutoCorrectedDatePipe({
58
- maxYear: maxYear,
59
- minYear: minYear,
60
- });
61
- }, [maxYear, minYear]);
62
- var changeHandler = useCallback(function (event, newValue, newDate) {
63
- if (uncontrolled) {
64
- setStateValue(newValue);
50
+ var DateInput = forwardRef(function (_a, ref) {
51
+ var _b = _a.mobileMode, mobileMode = _b === void 0 ? 'input' : _b, _c = _a.defaultValue, defaultValue = _c === void 0 ? '' : _c, rightAddons = _a.rightAddons, error = _a.error, propValue = _a.value, onBlur = _a.onBlur, onChange = _a.onChange, onComplete = _a.onComplete, restProps = __rest(_a, ["mobileMode", "defaultValue", "rightAddons", "error", "value", "onBlur", "onChange", "onComplete"]);
52
+ var inputRef = useRef(null);
53
+ var _d = useState(false), shouldRenderNative = _d[0], setShouldRenderNative = _d[1];
54
+ var _e = useState(propValue || defaultValue), value = _e[0], setValue = _e[1];
55
+ var _f = useState(!isValid(propValue)), stateError = _f[0], setStateError = _f[1];
56
+ var handleValueValidity = useCallback(function (inputValue) {
57
+ // Валидируем незаполненное значение только если инпут не в фокусе (блюр, либо установка значения снаружи)
58
+ var validateIncomplete = inputRef.current && document.activeElement !== inputRef.current;
59
+ if (!inputValue || validateIncomplete || inputValue.length >= DATE_MASK.length) {
60
+ setStateError(!isValid(inputValue));
65
61
  }
66
- if (onChange) {
67
- onChange(event, { date: newDate, value: newValue });
68
- }
69
- }, [onChange, uncontrolled]);
62
+ }, []);
70
63
  var handleChange = useCallback(function (event) {
71
64
  var newValue = event.target.value;
72
- var newDate = parseDateString(newValue);
73
- changeHandler(event, newValue, newDate);
74
- }, [changeHandler]);
65
+ // Позволяем вводить только цифры и точки
66
+ if (/[^\d.]/.test(newValue)) {
67
+ return;
68
+ }
69
+ var dots = newValue.match(/\./g);
70
+ // Не даем вводить больше, чем 2 точки
71
+ if (dots && dots.length > 2) {
72
+ return;
73
+ }
74
+ // Форматируем введенное значение (добавляем точки)
75
+ var formattedValue = format(newValue);
76
+ var date = parseDateString(formattedValue);
77
+ setValue(formattedValue);
78
+ if (onChange)
79
+ onChange(event, { date: date, value: formattedValue });
80
+ if (isCompleteDateInput(formattedValue)) {
81
+ var valid = formattedValue.length > 0 && isValid(formattedValue);
82
+ if (!valid)
83
+ return;
84
+ if (onComplete)
85
+ onComplete(event, { date: date, value: formattedValue });
86
+ }
87
+ }, [onChange, onComplete]);
75
88
  var handleNativeInputChange = useCallback(function (event) {
76
89
  var newDate = parseDateString(event.target.value, NATIVE_DATE_FORMAT);
77
90
  var newValue = event.target.value === '' ? '' : formatDate(newDate);
78
- changeHandler(event, newValue, newDate);
79
- }, [changeHandler]);
80
- return (React.createElement(MaskedInput, __assign({}, restProps, { ref: ref, mask: mask, keepCharPositions: true, defaultValue: defaultValue, value: inputValue, onBeforeDisplay: pipe, onChange: handleChange, rightAddons: React.createElement(React.Fragment, null,
91
+ setValue(newValue);
92
+ if (onComplete)
93
+ onComplete(event, { date: newDate, value: newValue });
94
+ if (onChange)
95
+ onChange(event, { date: newDate, value: newValue });
96
+ }, [onComplete, onChange]);
97
+ var handleBlur = useCallback(function (event) {
98
+ handleValueValidity(value);
99
+ if (onBlur)
100
+ onBlur(event);
101
+ }, [handleValueValidity, onBlur, value]);
102
+ useEffect(function () {
103
+ if (mobileMode === 'native' && isInputDateSupported()) {
104
+ setShouldRenderNative(true);
105
+ }
106
+ }, [mobileMode]);
107
+ useEffect(function () {
108
+ if (typeof propValue !== 'undefined') {
109
+ setValue(propValue);
110
+ }
111
+ // eslint-disable-next-line react-hooks/exhaustive-deps
112
+ }, [propValue]);
113
+ useEffect(function () {
114
+ handleValueValidity(value);
115
+ }, [handleValueValidity, value]);
116
+ return (React.createElement(Input, __assign({}, restProps, { ref: mergeRefs([ref, inputRef]), value: value, inputMode: 'decimal', pattern: '[0-9\\.]*', onChange: handleChange, onBlur: handleBlur, placeholder: '\u0414\u0414.\u041C\u041C.\u0413\u0413\u0413\u0413', error: error || stateError, rightAddons: React.createElement(React.Fragment, null,
81
117
  rightAddons,
82
118
  shouldRenderNative && (React.createElement("input", { type: 'date', ref: ref, defaultValue: defaultValue, onChange: handleNativeInputChange, className: styles.nativeInput }))) })));
83
119
  });
@@ -1,4 +1,4 @@
1
- /* hash: 1z123 */
1
+ /* hash: 1yoqs */
2
2
  :root {
3
3
 
4
4
  /* Hard */
@@ -7,7 +7,7 @@
7
7
 
8
8
  /* Hard up */
9
9
  }
10
- .date-input__nativeInput_qb4yv {
10
+ .date-input__nativeInput_1692t {
11
11
  opacity: 0;
12
12
  position: absolute;
13
13
  top: 0;
@@ -18,9 +18,9 @@
18
18
  appearance: none;
19
19
  z-index: 1
20
20
  }
21
- .date-input__nativeInput_qb4yv::-webkit-calendar-picker-indicator {
21
+ .date-input__nativeInput_1692t::-webkit-calendar-picker-indicator {
22
22
  display: none;
23
23
  }
24
- .date-input__nativeInput_qb4yv::-webkit-inner-spin-button {
24
+ .date-input__nativeInput_1692t::-webkit-inner-spin-button {
25
25
  display: none;
26
26
  }
package/dist/esm/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  export { DateInput } from './Component.js';
2
2
  import 'react';
3
- import '@alfalab/core-components-masked-input/dist/esm';
3
+ import '@alfalab/core-components-input/dist/esm';
4
+ import 'react-merge-refs';
4
5
  import 'date-fns';
5
- export { DATE_FORMAT, DATE_MASK, NATIVE_DATE_FORMAT, formatDate, isCompleteDateInput, parseDateString } from './utils/format.js';
6
- export { createAutoCorrectedDatePipe, mask } from './utils/date-correction-pipe.js';
7
- export { IS_BROWSER, SUPPORTS_INPUT_TYPE_DATE, isInputDateSupported } from './utils/native-supports.js';
6
+ export { DATE_FORMAT, DATE_MASK, NATIVE_DATE_FORMAT, format, formatDate, isCompleteDateInput, isValid, parseDateString } from './utils/format.js';
7
+ export { isInputDateSupported } from './utils/native-supports.js';
@@ -4,4 +4,6 @@ declare const DATE_MASK: (string | RegExp)[];
4
4
  declare const isCompleteDateInput: (input: string) => boolean;
5
5
  declare const formatDate: (date: number | Date, dateFormat?: string) => string;
6
6
  declare const parseDateString: (value: string, dateFormat?: string) => Date;
7
- export { DATE_FORMAT, NATIVE_DATE_FORMAT, DATE_MASK, isCompleteDateInput, formatDate, parseDateString };
7
+ declare const isValid: (inputValue?: string | undefined) => boolean;
8
+ declare const format: (value: string) => string;
9
+ export { DATE_FORMAT, NATIVE_DATE_FORMAT, DATE_MASK, isCompleteDateInput, formatDate, parseDateString, isValid, format };
@@ -1,4 +1,4 @@
1
- import { format, parse } from 'date-fns';
1
+ import { format as format$1, parse, isValid as isValid$1 } from 'date-fns';
2
2
 
3
3
  var DATE_FORMAT = 'dd.MM.yyyy';
4
4
  var NATIVE_DATE_FORMAT = 'yyyy-MM-dd';
@@ -6,11 +6,25 @@ var DATE_MASK = [/\d/, /\d/, '.', /\d/, /\d/, '.', /\d/, /\d/, /\d/, /\d/];
6
6
  var isCompleteDateInput = function (input) { return input.length === DATE_MASK.length; };
7
7
  var formatDate = function (date, dateFormat) {
8
8
  if (dateFormat === void 0) { dateFormat = DATE_FORMAT; }
9
- return format(date, dateFormat);
9
+ return format$1(date, dateFormat);
10
10
  };
11
11
  var parseDateString = function (value, dateFormat) {
12
12
  if (dateFormat === void 0) { dateFormat = DATE_FORMAT; }
13
13
  return parse(value, dateFormat, new Date());
14
14
  };
15
+ var isValid = function (inputValue) {
16
+ return !inputValue || (isCompleteDateInput(inputValue) && isValid$1(parseDateString(inputValue)));
17
+ };
18
+ var format = function (value) {
19
+ return value
20
+ .replace(/^(\d\d)(\d)$/, '$1.$2') // 121 => 12.1
21
+ .replace(/^(\d\d)\.(\d\d)(\d)$/, '$1.$2.$3') // 12.122 => 12.12.2
22
+ .replace(/^(\d\d)\d\.(.*)/, '$1.$2') // 123.12.2005 => 12.12.2005
23
+ .replace(/^(\d\d\.\d\d)\d\.(.*)/, '$1.$2') // 12.123.2005 => 12.12.2005
24
+ .replace(/^(\d\d\.\d\d\.\d\d\d\d).*/, '$1') // 12.12.20056 => 12.12.2005
25
+ .replace(/\.$/, '') // 12. => 12
26
+ .replace(/^(\d\d\.\d\d)(\d\d\d\d)/, '$1.$2') // 12.122005 => 12.12.2005
27
+ .replace(/^(\d\d)(\d\d\.\d\d\d\d)/, '$1.$2');
28
+ }; // 1212.2005 => 12.12.2005
15
29
 
16
- export { DATE_FORMAT, DATE_MASK, NATIVE_DATE_FORMAT, formatDate, isCompleteDateInput, parseDateString };
30
+ export { DATE_FORMAT, DATE_MASK, NATIVE_DATE_FORMAT, format, formatDate, isCompleteDateInput, isValid, parseDateString };
@@ -1,3 +1,2 @@
1
1
  export * from "./format";
2
- export * from "./date-correction-pipe";
3
2
  export * from "./native-supports";
@@ -1,4 +1,3 @@
1
1
  import 'date-fns';
2
- export { DATE_FORMAT, DATE_MASK, NATIVE_DATE_FORMAT, formatDate, isCompleteDateInput, parseDateString } from './format.js';
3
- export { createAutoCorrectedDatePipe, mask } from './date-correction-pipe.js';
4
- export { IS_BROWSER, SUPPORTS_INPUT_TYPE_DATE, isInputDateSupported } from './native-supports.js';
2
+ export { DATE_FORMAT, DATE_MASK, NATIVE_DATE_FORMAT, format, formatDate, isCompleteDateInput, isValid, parseDateString } from './format.js';
3
+ export { isInputDateSupported } from './native-supports.js';
@@ -1,7 +1,5 @@
1
- declare const IS_BROWSER: boolean;
2
- declare const SUPPORTS_INPUT_TYPE_DATE: boolean;
3
1
  /**
4
2
  * Возвращает `true`, если поддерживается `input[type="date"]`
5
3
  */
6
4
  declare function isInputDateSupported(): boolean;
7
- export { IS_BROWSER, SUPPORTS_INPUT_TYPE_DATE, isInputDateSupported };
5
+ export { isInputDateSupported };
@@ -1,5 +1,3 @@
1
- var IS_BROWSER = typeof window !== 'undefined';
2
- var SUPPORTS_INPUT_TYPE_DATE = IS_BROWSER && isInputDateSupported();
3
1
  /**
4
2
  * Возвращает `true`, если поддерживается `input[type="date"]`
5
3
  */
@@ -11,4 +9,4 @@ function isInputDateSupported() {
11
9
  return input.value !== value;
12
10
  }
13
11
 
14
- export { IS_BROWSER, SUPPORTS_INPUT_TYPE_DATE, isInputDateSupported };
12
+ export { isInputDateSupported };
package/dist/index.css CHANGED
@@ -1,4 +1,4 @@
1
- /* hash: 1z123 */
1
+ /* hash: 1yoqs */
2
2
  :root {
3
3
 
4
4
  /* Hard */
@@ -7,7 +7,7 @@
7
7
 
8
8
  /* Hard up */
9
9
  }
10
- .date-input__nativeInput_qb4yv {
10
+ .date-input__nativeInput_1692t {
11
11
  opacity: 0;
12
12
  position: absolute;
13
13
  top: 0;
@@ -18,9 +18,9 @@
18
18
  appearance: none;
19
19
  z-index: 1
20
20
  }
21
- .date-input__nativeInput_qb4yv::-webkit-calendar-picker-indicator {
21
+ .date-input__nativeInput_1692t::-webkit-calendar-picker-indicator {
22
22
  display: none;
23
23
  }
24
- .date-input__nativeInput_qb4yv::-webkit-inner-spin-button {
24
+ .date-input__nativeInput_1692t::-webkit-inner-spin-button {
25
25
  display: none;
26
26
  }
package/dist/index.js CHANGED
@@ -4,10 +4,10 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var Component = require('./Component.js');
6
6
  require('react');
7
- require('@alfalab/core-components-masked-input');
7
+ require('@alfalab/core-components-input');
8
+ require('react-merge-refs');
8
9
  require('date-fns');
9
10
  var utils_format = require('./utils/format.js');
10
- var utils_dateCorrectionPipe = require('./utils/date-correction-pipe.js');
11
11
  var utils_nativeSupports = require('./utils/native-supports.js');
12
12
 
13
13
 
@@ -16,11 +16,9 @@ exports.DateInput = Component.DateInput;
16
16
  exports.DATE_FORMAT = utils_format.DATE_FORMAT;
17
17
  exports.DATE_MASK = utils_format.DATE_MASK;
18
18
  exports.NATIVE_DATE_FORMAT = utils_format.NATIVE_DATE_FORMAT;
19
+ exports.format = utils_format.format;
19
20
  exports.formatDate = utils_format.formatDate;
20
21
  exports.isCompleteDateInput = utils_format.isCompleteDateInput;
22
+ exports.isValid = utils_format.isValid;
21
23
  exports.parseDateString = utils_format.parseDateString;
22
- exports.createAutoCorrectedDatePipe = utils_dateCorrectionPipe.createAutoCorrectedDatePipe;
23
- exports.mask = utils_dateCorrectionPipe.mask;
24
- exports.IS_BROWSER = utils_nativeSupports.IS_BROWSER;
25
- exports.SUPPORTS_INPUT_TYPE_DATE = utils_nativeSupports.SUPPORTS_INPUT_TYPE_DATE;
26
24
  exports.isInputDateSupported = utils_nativeSupports.isInputDateSupported;
@@ -1,16 +1,8 @@
1
1
  /// <reference types="react" />
2
2
  import React from 'react';
3
3
  import { ChangeEvent } from "react";
4
- import { MaskedInputProps } from "@alfalab/core-components-masked-input";
5
- type DateInputProps = Omit<MaskedInputProps, 'onBeforeDisplay' | 'mask' | 'onChange'> & {
6
- /**
7
- * Минимальный год, доступный для ввода
8
- */
9
- minYear?: number;
10
- /**
11
- * Максимальный год, доступный для ввода
12
- */
13
- maxYear?: number;
4
+ import { InputProps } from "@alfalab/core-components-input";
5
+ type DateInputProps = Omit<InputProps, 'onChange'> & {
14
6
  /**
15
7
  * Управление нативным режимом на мобильных устройствах
16
8
  */
@@ -22,16 +14,15 @@ type DateInputProps = Omit<MaskedInputProps, 'onBeforeDisplay' | 'mask' | 'onCha
22
14
  date: Date;
23
15
  value: string;
24
16
  }) => void;
25
- };
26
- declare const DateInput: React.ForwardRefExoticComponent<Pick<MaskedInputProps, "className" | "dataTestId" | "form" | "label" | "slot" | "style" | "title" | "pattern" | "children" | "leftAddons" | "rightAddons" | "size" | "block" | "colors" | "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" | "bottomAddons" | "accept" | "alt" | "autoComplete" | "capture" | "checked" | "crossOrigin" | "height" | "max" | "maxLength" | "min" | "minLength" | "multiple" | "readOnly" | "required" | "src" | "width" | "clear" | "success" | "inputClassName" | "focusedClassName" | "filledClassName" | "onClear" | "wrapperRef" | "keepCharPositions"> & {
27
- /**
28
- * Минимальный год, доступный для ввода
29
- */
30
- minYear?: number | undefined;
31
17
  /**
32
- * Максимальный год, доступный для ввода
18
+ * Обработчик окончания ввода
33
19
  */
34
- maxYear?: number | undefined;
20
+ onComplete?: (event: ChangeEvent<HTMLInputElement>, payload: {
21
+ date: Date;
22
+ value: string;
23
+ }) => void;
24
+ };
25
+ declare const DateInput: React.ForwardRefExoticComponent<Pick<InputProps, "className" | "dataTestId" | "form" | "label" | "slot" | "style" | "title" | "pattern" | "children" | "leftAddons" | "rightAddons" | "size" | "block" | "colors" | "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" | "bottomAddons" | "accept" | "alt" | "autoComplete" | "capture" | "checked" | "crossOrigin" | "height" | "max" | "maxLength" | "min" | "minLength" | "multiple" | "readOnly" | "required" | "src" | "width" | "clear" | "success" | "inputClassName" | "focusedClassName" | "filledClassName" | "onClear" | "wrapperRef"> & {
35
26
  /**
36
27
  * Управление нативным режимом на мобильных устройствах
37
28
  */
@@ -43,5 +34,12 @@ declare const DateInput: React.ForwardRefExoticComponent<Pick<MaskedInputProps,
43
34
  date: Date;
44
35
  value: string;
45
36
  }) => void) | undefined;
37
+ /**
38
+ * Обработчик окончания ввода
39
+ */
40
+ onComplete?: ((event: React.ChangeEvent<HTMLInputElement>, payload: {
41
+ date: Date;
42
+ value: string;
43
+ }) => void) | undefined;
46
44
  } & React.RefAttributes<HTMLInputElement>>;
47
45
  export { DateInputProps, DateInput };
@@ -1,41 +1,79 @@
1
- import React, { useState, useMemo, useCallback } from 'react';
2
- import { MaskedInput } from '@alfalab/core-components-masked-input/dist/modern';
1
+ import React, { forwardRef, useRef, useState, useCallback, useEffect } from 'react';
2
+ import { Input } from '@alfalab/core-components-input/dist/modern';
3
+ import mergeRefs from 'react-merge-refs';
3
4
  import 'date-fns';
4
- import { parseDateString, NATIVE_DATE_FORMAT, formatDate } from './utils/format.js';
5
- import { createAutoCorrectedDatePipe, mask } from './utils/date-correction-pipe.js';
6
- import { SUPPORTS_INPUT_TYPE_DATE } from './utils/native-supports.js';
5
+ import { isValid, DATE_MASK, format, parseDateString, isCompleteDateInput, NATIVE_DATE_FORMAT, formatDate } from './utils/format.js';
6
+ import { isInputDateSupported } from './utils/native-supports.js';
7
7
 
8
- var styles = {"nativeInput":"date-input__nativeInput_qb4yv"};
8
+ var styles = {"nativeInput":"date-input__nativeInput_1692t"};
9
9
  require('./index.css')
10
10
 
11
- const DateInput = React.forwardRef(({ maxYear, minYear, mobileMode = 'input', value, defaultValue, rightAddons, onChange, ...restProps }, ref) => {
12
- const uncontrolled = value === undefined;
13
- const shouldRenderNative = SUPPORTS_INPUT_TYPE_DATE && mobileMode === 'native';
14
- const [stateValue, setStateValue] = useState(defaultValue);
15
- const inputValue = uncontrolled ? stateValue : value;
16
- const pipe = useMemo(() => createAutoCorrectedDatePipe({
17
- maxYear,
18
- minYear,
19
- }), [maxYear, minYear]);
20
- const changeHandler = useCallback((event, newValue, newDate) => {
21
- if (uncontrolled) {
22
- setStateValue(newValue);
11
+ const DateInput = forwardRef(({ mobileMode = 'input', defaultValue = '', rightAddons, error, value: propValue, onBlur, onChange, onComplete, ...restProps }, ref) => {
12
+ const inputRef = useRef(null);
13
+ const [shouldRenderNative, setShouldRenderNative] = useState(false);
14
+ const [value, setValue] = useState(propValue || defaultValue);
15
+ const [stateError, setStateError] = useState(!isValid(propValue));
16
+ const handleValueValidity = useCallback((inputValue) => {
17
+ // Валидируем незаполненное значение только если инпут не в фокусе (блюр, либо установка значения снаружи)
18
+ const validateIncomplete = inputRef.current && document.activeElement !== inputRef.current;
19
+ if (!inputValue || validateIncomplete || inputValue.length >= DATE_MASK.length) {
20
+ setStateError(!isValid(inputValue));
23
21
  }
24
- if (onChange) {
25
- onChange(event, { date: newDate, value: newValue });
26
- }
27
- }, [onChange, uncontrolled]);
22
+ }, []);
28
23
  const handleChange = useCallback((event) => {
29
- const newValue = event.target.value;
30
- const newDate = parseDateString(newValue);
31
- changeHandler(event, newValue, newDate);
32
- }, [changeHandler]);
24
+ const { value: newValue } = event.target;
25
+ // Позволяем вводить только цифры и точки
26
+ if (/[^\d.]/.test(newValue)) {
27
+ return;
28
+ }
29
+ const dots = newValue.match(/\./g);
30
+ // Не даем вводить больше, чем 2 точки
31
+ if (dots && dots.length > 2) {
32
+ return;
33
+ }
34
+ // Форматируем введенное значение (добавляем точки)
35
+ const formattedValue = format(newValue);
36
+ const date = parseDateString(formattedValue);
37
+ setValue(formattedValue);
38
+ if (onChange)
39
+ onChange(event, { date, value: formattedValue });
40
+ if (isCompleteDateInput(formattedValue)) {
41
+ const valid = formattedValue.length > 0 && isValid(formattedValue);
42
+ if (!valid)
43
+ return;
44
+ if (onComplete)
45
+ onComplete(event, { date, value: formattedValue });
46
+ }
47
+ }, [onChange, onComplete]);
33
48
  const handleNativeInputChange = useCallback((event) => {
34
49
  const newDate = parseDateString(event.target.value, NATIVE_DATE_FORMAT);
35
50
  const newValue = event.target.value === '' ? '' : formatDate(newDate);
36
- changeHandler(event, newValue, newDate);
37
- }, [changeHandler]);
38
- return (React.createElement(MaskedInput, Object.assign({}, restProps, { ref: ref, mask: mask, keepCharPositions: true, defaultValue: defaultValue, value: inputValue, onBeforeDisplay: pipe, onChange: handleChange, rightAddons: React.createElement(React.Fragment, null,
51
+ setValue(newValue);
52
+ if (onComplete)
53
+ onComplete(event, { date: newDate, value: newValue });
54
+ if (onChange)
55
+ onChange(event, { date: newDate, value: newValue });
56
+ }, [onComplete, onChange]);
57
+ const handleBlur = useCallback((event) => {
58
+ handleValueValidity(value);
59
+ if (onBlur)
60
+ onBlur(event);
61
+ }, [handleValueValidity, onBlur, value]);
62
+ useEffect(() => {
63
+ if (mobileMode === 'native' && isInputDateSupported()) {
64
+ setShouldRenderNative(true);
65
+ }
66
+ }, [mobileMode]);
67
+ useEffect(() => {
68
+ if (typeof propValue !== 'undefined') {
69
+ setValue(propValue);
70
+ }
71
+ // eslint-disable-next-line react-hooks/exhaustive-deps
72
+ }, [propValue]);
73
+ useEffect(() => {
74
+ handleValueValidity(value);
75
+ }, [handleValueValidity, value]);
76
+ return (React.createElement(Input, Object.assign({}, restProps, { ref: mergeRefs([ref, inputRef]), value: value, inputMode: 'decimal', pattern: '[0-9\\.]*', onChange: handleChange, onBlur: handleBlur, placeholder: '\u0414\u0414.\u041C\u041C.\u0413\u0413\u0413\u0413', error: error || stateError, rightAddons: React.createElement(React.Fragment, null,
39
77
  rightAddons,
40
78
  shouldRenderNative && (React.createElement("input", { type: 'date', ref: ref, defaultValue: defaultValue, onChange: handleNativeInputChange, className: styles.nativeInput }))) })));
41
79
  });
@@ -1,4 +1,4 @@
1
- /* hash: 1z123 */
1
+ /* hash: 1yoqs */
2
2
  :root {
3
3
 
4
4
  /* Hard */
@@ -7,7 +7,7 @@
7
7
 
8
8
  /* Hard up */
9
9
  }
10
- .date-input__nativeInput_qb4yv {
10
+ .date-input__nativeInput_1692t {
11
11
  opacity: 0;
12
12
  position: absolute;
13
13
  top: 0;
@@ -18,9 +18,9 @@
18
18
  appearance: none;
19
19
  z-index: 1
20
20
  }
21
- .date-input__nativeInput_qb4yv::-webkit-calendar-picker-indicator {
21
+ .date-input__nativeInput_1692t::-webkit-calendar-picker-indicator {
22
22
  display: none;
23
23
  }
24
- .date-input__nativeInput_qb4yv::-webkit-inner-spin-button {
24
+ .date-input__nativeInput_1692t::-webkit-inner-spin-button {
25
25
  display: none;
26
26
  }
@@ -1,7 +1,7 @@
1
1
  import 'react';
2
- import '@alfalab/core-components-masked-input/dist/modern';
2
+ import '@alfalab/core-components-input/dist/modern';
3
+ import 'react-merge-refs';
3
4
  import 'date-fns';
4
- export { DATE_FORMAT, DATE_MASK, NATIVE_DATE_FORMAT, formatDate, isCompleteDateInput, parseDateString } from './utils/format.js';
5
- export { createAutoCorrectedDatePipe, mask } from './utils/date-correction-pipe.js';
6
- export { IS_BROWSER, SUPPORTS_INPUT_TYPE_DATE, isInputDateSupported } from './utils/native-supports.js';
5
+ export { DATE_FORMAT, DATE_MASK, NATIVE_DATE_FORMAT, format, formatDate, isCompleteDateInput, isValid, parseDateString } from './utils/format.js';
6
+ export { isInputDateSupported } from './utils/native-supports.js';
7
7
  export { DateInput } from './Component.js';