diginet-core-ui 1.4.24 → 1.4.25

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.
@@ -0,0 +1,189 @@
1
+ import { isValid } from 'date-fns';
2
+ import { useReducer } from 'react';
3
+ import { modifyDate } from "./utils";
4
+ export const patternMap = {
5
+ Y: 'year',
6
+ y: 'year',
7
+ M: 'month',
8
+ D: 'day',
9
+ d: 'day',
10
+ H: 'hour',
11
+ h: 'hour',
12
+ m: 'minute',
13
+ s: 'second',
14
+ a: 'meridian'
15
+ };
16
+ export class DateField {
17
+ constructor(format, value) {
18
+ this.format = format;
19
+ this.patternArray = [];
20
+ this.year = null;
21
+ this.month = null;
22
+ this.day = null;
23
+ this.hour = null;
24
+ this.minute = null;
25
+ this.second = null;
26
+ const formatArray = format.match(new RegExp('([Y|y|D|d|M|H|h|m|s|a])+', 'ig')) || [];
27
+ this.patternArray = formatArray.map(pattern => {
28
+ return {
29
+ pattern,
30
+ key: patternMap[pattern[0]]
31
+ };
32
+ });
33
+ if (value && isValid(value)) {
34
+ this.year = value.getFullYear();
35
+ this.month = value.getMonth() + 1;
36
+ this.day = value.getDate();
37
+ this.hour = value.getHours();
38
+ this.minute = value.getMinutes();
39
+ this.second = value.getSeconds();
40
+ }
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Pad a number with zeros to the left.
46
+ */
47
+ function padNumber(number, length) {
48
+ let numberString = String(number);
49
+ if (numberString.length >= length) {
50
+ return numberString;
51
+ }
52
+ const paddingCount = length - numberString.length;
53
+ for (let i = 0; i < paddingCount; i++) {
54
+ numberString = '0' + numberString;
55
+ }
56
+ return numberString;
57
+ }
58
+ export const useDateField = (format, localize, date) => {
59
+ const [dateField, dispatch] = useReducer((state, action) => {
60
+ switch (action.type) {
61
+ case 'setYear':
62
+ return {
63
+ ...state,
64
+ year: action.value
65
+ };
66
+ case 'setMonth':
67
+ return {
68
+ ...state,
69
+ month: action.value
70
+ };
71
+ case 'setDay':
72
+ return {
73
+ ...state,
74
+ day: action.value
75
+ };
76
+ case 'setHour':
77
+ return {
78
+ ...state,
79
+ hour: action.value
80
+ };
81
+ case 'setMinute':
82
+ return {
83
+ ...state,
84
+ minute: action.value
85
+ };
86
+ case 'setSecond':
87
+ return {
88
+ ...state,
89
+ second: action.value
90
+ };
91
+ case 'setNewDate':
92
+ return new DateField(format, action.value);
93
+ default:
94
+ return state;
95
+ }
96
+ }, new DateField(format, date));
97
+ const toDateString = () => {
98
+ let str = format;
99
+ dateField.patternArray.forEach(item => {
100
+ const {
101
+ key,
102
+ pattern
103
+ } = item;
104
+ const hour = dateField.hour;
105
+ let value = dateField[key];
106
+ if (value !== null) {
107
+ if (pattern === 'MMM' && typeof value === 'number') {
108
+ // value = localize?.month(value - 1, { width: 'abbreviated' });
109
+ value = localize === null || localize === void 0 ? void 0 : localize.months[['notFull']][value - 1];
110
+ } else if (pattern === 'MMMM' && typeof value === 'number') {
111
+ // value = localize?.month(value - 1, { width: 'wide' });
112
+ value = localize === null || localize === void 0 ? void 0 : localize.months[['full']][value - 1];
113
+ } else if (pattern === 'aa') {
114
+ if (typeof hour === 'number') {
115
+ value = hour > 12 ? 'PM' : 'AM';
116
+ } else {
117
+ value = 'aa';
118
+ }
119
+ } else if (pattern === 'hh' && typeof value === 'number') {
120
+ value = value === 0 ? 12 : value > 12 ? value - 12 : value;
121
+ }
122
+ if (typeof value === 'number') {
123
+ value = padNumber(value, pattern.length);
124
+ }
125
+ str = str.replace(pattern, value);
126
+ }
127
+ });
128
+ return str;
129
+ };
130
+
131
+ // Check if the field value is valid.
132
+ const validFieldValue = (type, value) => {
133
+ var _format$match;
134
+ let isValid = true;
135
+ (_format$match = format.match(new RegExp('([y|d|M|H|h|m|s])+', 'ig'))) === null || _format$match === void 0 ? void 0 : _format$match.forEach(pattern => {
136
+ const key = patternMap[pattern[0]];
137
+ const fieldValue = type === key ? value : dateField[key];
138
+ if (fieldValue === null) {
139
+ isValid = false;
140
+ return;
141
+ }
142
+ });
143
+ return isValid;
144
+ };
145
+ const isEmptyValue = (type, value) => {
146
+ var _format$match2;
147
+ const checkValueArray = (_format$match2 = format.match(new RegExp('([y|d|M|H|h|m|s])+', 'ig'))) === null || _format$match2 === void 0 ? void 0 : _format$match2.map(pattern => {
148
+ const key = patternMap[pattern[0]];
149
+ const fieldValue = type === key ? value : dateField[key];
150
+ return fieldValue !== null;
151
+ });
152
+ return checkValueArray === null || checkValueArray === void 0 ? void 0 : checkValueArray.every(item => item === false);
153
+ };
154
+ const toDate = (type, value) => {
155
+ const {
156
+ year,
157
+ month,
158
+ day,
159
+ hour,
160
+ minute,
161
+ second
162
+ } = dateField;
163
+ const date = new Date(year || 0, typeof month === 'number' ? month - 1 : 0,
164
+ // The default day is 1 when the value is null, otherwise it becomes the last day of the month.
165
+ day || 1, hour || 0, minute || 0, second || 0);
166
+ if (typeof type === 'undefined' || typeof value === 'undefined') {
167
+ return date;
168
+ }
169
+ if (value === null || !validFieldValue(type, value)) {
170
+ if (isEmptyValue(type, value)) {
171
+ return null;
172
+ }
173
+ return new Date('');
174
+ }
175
+ if (type === 'meridian' && typeof hour === 'number') {
176
+ const newHour = hour > 12 ? hour - 12 : hour + 12;
177
+ type = 'hour';
178
+ value = newHour;
179
+ }
180
+ return modifyDate(date, type, value);
181
+ };
182
+ return {
183
+ dateField,
184
+ dispatch,
185
+ toDate,
186
+ toDateString,
187
+ isEmptyValue
188
+ };
189
+ };
@@ -0,0 +1,300 @@
1
+ /** @jsxRuntime classic */
2
+ /** @jsx jsx */
3
+ import { css, jsx } from '@emotion/core';
4
+ import { HelperText, Label } from "../..";
5
+ import PropTypes from 'prop-types';
6
+ import { forwardRef, memo, useImperativeHandle, useMemo, useRef, useState } from 'react';
7
+ import { cursorNotAllowed, displayBlock, positionRelative } from "../../../styles/general";
8
+ import useThemeProps from "../../../theme/utils/useThemeProps";
9
+ import { classNames, refType as ref } from "../../../utils";
10
+ import useIsFocused from "./useIsFocused";
11
+ import useKeyboardInputEvent from "./useKeyboardInputEvent";
12
+ import { getInputSelectedState, isFieldFullValue, useEventCallback, useInputSelection, validateDateTime } from "./utils";
13
+ import { getGlobal } from "../../../global";
14
+ import locale from "../../../locale";
15
+ import useDateInputState from "./useDateInputState";
16
+ import useControlled from "./useControlled";
17
+ import UncontrolledInputBase from "../input-base/UncontrolledInputBase";
18
+ const DateInput = /*#__PURE__*/memo( /*#__PURE__*/forwardRef((inProps, reference) => {
19
+ if (!reference) reference = useRef(null);
20
+
21
+ // props priority: `inProps` > `themeDefaultProps`
22
+ const props = useThemeProps({
23
+ props: inProps,
24
+ name: 'DateInput'
25
+ });
26
+ const {
27
+ action = {},
28
+ className,
29
+ defaultValue,
30
+ disabled,
31
+ endIcon,
32
+ endIconProps,
33
+ error,
34
+ format: formatStr,
35
+ inputProps,
36
+ helperTextProps,
37
+ inputRef = useRef(null),
38
+ inputStyle,
39
+ label,
40
+ labelProps,
41
+ onBlur,
42
+ onChange,
43
+ onFocus,
44
+ onKeyDown,
45
+ placeholder,
46
+ readOnly,
47
+ required,
48
+ startIcon,
49
+ startIconProps,
50
+ style,
51
+ value: valueProp,
52
+ viewType,
53
+ ...other
54
+ } = props;
55
+ const ref = useRef(null);
56
+ const [selectedState, setSelectedState] = useState({
57
+ selectedPattern: 'y',
58
+ selectionStart: 0,
59
+ selectionEnd: 0
60
+ });
61
+ const isError = !!error;
62
+ const isEnabled = !readOnly && !disabled;
63
+ const _DateInputRoot = DateInputRoot();
64
+ useImperativeHandle(reference, () => {
65
+ const currentRef = ref.current || {};
66
+ currentRef.element = ref.current;
67
+ const _instance = {
68
+ ...action
69
+ }; // methods
70
+ _instance.__proto__ = {}; // hidden methods
71
+ currentRef.instance = _instance;
72
+ return currentRef;
73
+ });
74
+ const dateLocale = locale.get();
75
+ const [value, setValue, isControlled] = useControlled(valueProp, defaultValue);
76
+ const {
77
+ dateField,
78
+ setDateOffset,
79
+ setDateField,
80
+ getDateField,
81
+ toDateString,
82
+ isEmptyValue
83
+ } = useDateInputState({
84
+ formatStr,
85
+ locale: dateLocale,
86
+ date: value,
87
+ isControlledDate: isControlled
88
+ });
89
+ const dateString = toDateString();
90
+ const keyPressOptions = useMemo(() => ({
91
+ formatStr,
92
+ localize: getGlobal(dateLocale),
93
+ selectedMonth: dateField.month,
94
+ dateString
95
+ }), [dateField, dateString, formatStr, dateLocale]);
96
+ const handleChange = useEventCallback((value, event) => {
97
+ if (onChange) {
98
+ onChange(value, event);
99
+ }
100
+ setValue(value);
101
+ });
102
+ const setSelectionRange = useInputSelection(inputRef);
103
+ const onSegmentChange = useEventCallback((event, nextDirection) => {
104
+ const input = event.target;
105
+ const key = event.key;
106
+ const direction = nextDirection || (key === 'ArrowRight' ? 'right' : 'left');
107
+ const state = getInputSelectedState({
108
+ ...keyPressOptions,
109
+ input,
110
+ direction
111
+ });
112
+ setSelectionRange(state.selectionStart, state.selectionEnd);
113
+ setSelectedState(state);
114
+ });
115
+ const onSegmentValueChange = useEventCallback(event => {
116
+ const input = event.target;
117
+ const key = event.key;
118
+ const offset = key === 'ArrowUp' ? 1 : -1;
119
+ const state = getInputSelectedState({
120
+ ...keyPressOptions,
121
+ input,
122
+ valueOffset: offset
123
+ });
124
+ setSelectedState(state);
125
+ setDateOffset(state.selectedPattern, offset, date => handleChange(date, event));
126
+ setSelectionRange(state.selectionStart, state.selectionEnd);
127
+ });
128
+ const onSegmentValueChangeWithNumericKeys = useEventCallback(event => {
129
+ const input = event.target;
130
+ const key = event.key;
131
+ const pattern = selectedState.selectedPattern;
132
+ if (!pattern) {
133
+ return;
134
+ }
135
+ const field = getDateField(pattern);
136
+ const value = parseInt(key, 10);
137
+ const padValue = parseInt(`${field.value || ''}${key}`, 10);
138
+ let newValue = value;
139
+
140
+ // Check if the value entered by the user is a valid date
141
+ if (validateDateTime(field.name, padValue)) {
142
+ newValue = padValue;
143
+ }
144
+
145
+ // if (pattern === 'M') {
146
+ // // Month cannot be less than 1.
147
+ // newValue = Math.max(1, newValue);
148
+ // }
149
+
150
+ setDateField(pattern, newValue, date => handleChange(date, event));
151
+
152
+ // The currently selected month will be retained as a parameter of getInputSelectedState,
153
+ // but if the user enters a month, the month value will be replaced with the value entered by the user.
154
+ const selectedMonth = pattern === 'M' ? newValue : dateField.month;
155
+ const nextState = getInputSelectedState({
156
+ ...keyPressOptions,
157
+ input,
158
+ selectedMonth
159
+ });
160
+ setSelectedState(nextState);
161
+ setSelectionRange(nextState.selectionStart, nextState.selectionEnd);
162
+ // If the field is full value, move the cursor to the next field
163
+ if ((isFieldFullValue(formatStr, newValue, pattern) || field.value === 0) && input.selectionEnd !== input.value.length) {
164
+ onSegmentChange(event, 'right');
165
+ }
166
+ });
167
+ const onSegmentValueRemove = useEventCallback(event => {
168
+ const input = event.target;
169
+ if (selectedState.selectedPattern) {
170
+ const nextState = getInputSelectedState({
171
+ ...keyPressOptions,
172
+ input,
173
+ valueOffset: null
174
+ });
175
+ setSelectedState(nextState);
176
+ setSelectionRange(nextState.selectionStart, nextState.selectionEnd);
177
+ setDateField(selectedState.selectedPattern, null, date => handleChange(date, event));
178
+ }
179
+ });
180
+ const handleClick = useEventCallback(event => {
181
+ const input = event.target;
182
+ const state = getInputSelectedState({
183
+ ...keyPressOptions,
184
+ input
185
+ });
186
+ setSelectedState(state);
187
+ setSelectionRange(state.selectionStart, state.selectionEnd);
188
+ });
189
+ const onKeyboardInput = useKeyboardInputEvent({
190
+ onSegmentChange,
191
+ onSegmentValueChange,
192
+ onSegmentValueChangeWithNumericKeys,
193
+ onSegmentValueRemove,
194
+ onKeyDown
195
+ });
196
+ const [focused, focusEventProps] = useIsFocused({
197
+ onBlur,
198
+ onFocus
199
+ });
200
+ const renderedValue = useMemo(() => {
201
+ if (!isEmptyValue()) {
202
+ return dateString;
203
+ }
204
+ return !focused ? '' : dateString;
205
+ }, [dateString, focused, isEmptyValue]);
206
+ return jsx("div", {
207
+ ref: ref,
208
+ css: _DateInputRoot,
209
+ className: classNames('DGN-UI-DateInput', className, disabled && 'disabled'),
210
+ style: style,
211
+ ...other
212
+ }, !!label && jsx(Label, {
213
+ required: required,
214
+ ...labelProps,
215
+ disabled: disabled
216
+ }, label), jsx(UncontrolledInputBase, {
217
+ autoComplete: "off",
218
+ autoCorrect: "off",
219
+ disabled: disabled,
220
+ endIcon: endIcon,
221
+ endIconProps: endIconProps,
222
+ inputMode: focused ? 'numeric' : 'text',
223
+ inputProps: inputProps,
224
+ inputRef: inputRef,
225
+ inputStyle: inputStyle,
226
+ onClick: handleClick,
227
+ onKeyDown: isEnabled ? onKeyboardInput : null,
228
+ placeholder: placeholder || formatStr,
229
+ readOnly: readOnly,
230
+ spellCheck: false,
231
+ startIcon: startIcon,
232
+ startIconProps: startIconProps,
233
+ value: renderedValue,
234
+ viewType: viewType,
235
+ ...focusEventProps
236
+ }), isError && jsx(HelperText, {
237
+ ...helperTextProps,
238
+ disabled: disabled
239
+ }, error));
240
+ }));
241
+ const DateInputRoot = () => css`
242
+ ${displayBlock};
243
+ ${positionRelative};
244
+ &.disabled {
245
+ ${cursorNotAllowed};
246
+ }
247
+ `;
248
+ DateInput.propTypes = {
249
+ /** Class for component. */
250
+ className: PropTypes.string,
251
+ /** The default value. Use when the component is not controlled. */
252
+ defaultValue: PropTypes.instanceOf(Date),
253
+ /** If `true`, the component is disabled.*/
254
+ disabled: PropTypes.bool,
255
+ /** [Icon](https://core.diginet.com.vn/ui/?path=/story/icon-basic--basic) or element placed to the right of input. */
256
+ endIcon: PropTypes.any,
257
+ /** Props of end icon in InputBase component. */
258
+ endIconProps: PropTypes.object,
259
+ /** Error displayed below input. */
260
+ error: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]),
261
+ /** Format of the date when rendered in the input. */
262
+ format: PropTypes.string,
263
+ /** [Props](https://core.diginet.com.vn/ui/?path=/story/form-control-text-helpertext) of helper text. */
264
+ helperTextProps: PropTypes.object,
265
+ /** [Attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attributes) applied to the input element. */
266
+ inputProps: PropTypes.object,
267
+ /** Pass a ref to the input element. */
268
+ inputRef: ref,
269
+ /** Style inline of input element. */
270
+ inputStyle: PropTypes.object,
271
+ /** The label of the component. */
272
+ label: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
273
+ /** [Props](https://core.diginet.com.vn/ui/?path=/docs/form-control-text-label--simple) of label. */
274
+ labelProps: PropTypes.object,
275
+ /** Callback fired when the input is blurred. */
276
+ onBlur: PropTypes.func,
277
+ /** Callback fired when the value is changed. */
278
+ onChange: PropTypes.func,
279
+ /** Callback fired when the input is focused. */
280
+ onFocus: PropTypes.func,
281
+ /** Callback fired when pressing a key. */
282
+ onKeyDown: PropTypes.func,
283
+ /** The short hint displayed in the input before the user enters a value. */
284
+ placeholder: PropTypes.string,
285
+ /** If `true`, the component is readOnly. */
286
+ readOnly: PropTypes.bool,
287
+ /** If `true`, the input element is required. */
288
+ required: PropTypes.bool,
289
+ /** [Icon](https://core.diginet.com.vn/ui/?path=/story/icon-basic--basic) or element placed to the left of input. */
290
+ startIcon: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
291
+ /** Props of start icon in InputBase component. */
292
+ startIconProps: PropTypes.object,
293
+ /** Style inline of component. */
294
+ style: PropTypes.object,
295
+ /** The value of the input element, required for a controlled component. */
296
+ value: PropTypes.instanceOf(Date),
297
+ /** The variant to use. */
298
+ viewType: PropTypes.oneOf(['underlined', 'outlined', 'none'])
299
+ };
300
+ export default DateInput;
@@ -0,0 +1,14 @@
1
+ import { useRef, useState, useCallback } from 'react';
2
+ function useControlled(controlledValue, defaultValue) {
3
+ const controlledRef = useRef(false);
4
+ controlledRef.current = controlledValue !== undefined;
5
+ const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);
6
+ const value = controlledRef.current ? controlledValue : uncontrolledValue;
7
+ const setValue = useCallback(nextValue => {
8
+ if (!controlledRef.current) {
9
+ setUncontrolledValue(nextValue);
10
+ }
11
+ }, [controlledRef]);
12
+ return [value, setValue, controlledRef.current];
13
+ }
14
+ export default useControlled;
@@ -0,0 +1,132 @@
1
+ import { useEffect } from 'react';
2
+ // import startCase from 'lodash/startCase';
3
+ import { addDays, addHours, addMinutes, addMonths, addSeconds, addYears, format, isLastDayOfMonth, isValid, lastDayOfMonth } from 'date-fns';
4
+ import { getGlobal } from "../../../global";
5
+ import { capitalize } from "../../../utils";
6
+ import { patternMap, useDateField } from "./DateField";
7
+ import { lowerCaseDayYear } from "./utils";
8
+ import { enUS, vi } from 'date-fns/locale';
9
+ export function useDateInputState({
10
+ formatStr,
11
+ locale,
12
+ date,
13
+ isControlledDate
14
+ }) {
15
+ const {
16
+ dateField,
17
+ dispatch,
18
+ toDateString,
19
+ toDate,
20
+ isEmptyValue
21
+ } = useDateField(formatStr, getGlobal(locale), date);
22
+ const setDateOffset = (pattern, offset, callback) => {
23
+ const currentDate = new Date();
24
+ const year = dateField.year || currentDate.getFullYear();
25
+ const month = dateField.month ? dateField.month - 1 : currentDate.getMonth();
26
+ const day = dateField.day || 0;
27
+ const hour = dateField.hour || 0;
28
+ const minute = dateField.minute || 0;
29
+ const second = dateField.second || 0;
30
+ let actionName;
31
+ let value;
32
+ switch (pattern) {
33
+ case 'Y':
34
+ case 'y':
35
+ actionName = 'setYear';
36
+ value = addYears(new Date(year, 0), offset).getFullYear();
37
+ break;
38
+ case 'M':
39
+ actionName = 'setMonth';
40
+ value = addMonths(new Date(year, month), offset).getMonth() + 1;
41
+ break;
42
+ case 'D':
43
+ case 'd':
44
+ actionName = 'setDay';
45
+ // eslint-disable-next-line no-case-declarations
46
+ const prevDate = new Date(year, month, day);
47
+ value = addDays(prevDate, offset).getDate();
48
+ if (offset > 0) {
49
+ value = isLastDayOfMonth(prevDate) ? 1 : value;
50
+ } else {
51
+ value = prevDate.getDate() === 1 ? lastDayOfMonth(prevDate).getDate() : value;
52
+ }
53
+ break;
54
+ case 'H':
55
+ case 'h':
56
+ actionName = 'setHour';
57
+ value = addHours(new Date(year, month, day, hour), offset).getHours();
58
+ break;
59
+ case 'm':
60
+ actionName = 'setMinute';
61
+ value = addMinutes(new Date(year, month, day, hour, minute), offset).getMinutes();
62
+ break;
63
+ case 's':
64
+ actionName = 'setSecond';
65
+ value = addSeconds(new Date(year, month, day, hour, minute, second), offset).getSeconds();
66
+ break;
67
+ case 'a':
68
+ actionName = 'setHour';
69
+ value = hour >= 12 ? hour - 12 : hour + 12;
70
+ break;
71
+ }
72
+ if (actionName && value) {
73
+ dispatch({
74
+ type: actionName,
75
+ value
76
+ });
77
+ const field = patternMap[pattern];
78
+ callback === null || callback === void 0 ? void 0 : callback(toDate(field, value));
79
+ }
80
+ };
81
+ const setDateField = (pattern, value, callback) => {
82
+ const field = patternMap[pattern];
83
+ // const actionName = `set${startCase(field)}`;
84
+ const actionName = `set${capitalize(field)}`;
85
+ dispatch({
86
+ type: actionName,
87
+ value
88
+ });
89
+ callback === null || callback === void 0 ? void 0 : callback(toDate(field, value));
90
+ };
91
+ const getDateField = pattern => {
92
+ const fieldName = patternMap[pattern];
93
+ return {
94
+ name: fieldName,
95
+ value: dateField[fieldName]
96
+ };
97
+ };
98
+ const toControlledDateString = () => {
99
+ if (date && isValid(date)) {
100
+ // return format(date, formatStr, { locale });
101
+ return format(date, lowerCaseDayYear(formatStr), {
102
+ locale: locale === 'vi' ? vi : enUS
103
+ });
104
+ }
105
+ // if date is not valid, return uncontrolled date string
106
+ return toDateString();
107
+ };
108
+ useEffect(() => {
109
+ if (isControlledDate) {
110
+ if (date && isValid(date)) {
111
+ dispatch({
112
+ type: 'setNewDate',
113
+ value: date
114
+ });
115
+ } else if (date === null) {
116
+ dispatch({
117
+ type: 'setNewDate',
118
+ value: null
119
+ });
120
+ }
121
+ }
122
+ }, [date, dispatch, isControlledDate]);
123
+ return {
124
+ dateField,
125
+ setDateOffset,
126
+ setDateField,
127
+ getDateField,
128
+ toDateString: isControlledDate ? toControlledDateString : toDateString,
129
+ isEmptyValue
130
+ };
131
+ }
132
+ export default useDateInputState;
@@ -0,0 +1,20 @@
1
+ import { useState, useCallback } from 'react';
2
+ function useIsFocused({
3
+ onFocus: onFocusProp,
4
+ onBlur: onBlurProp
5
+ }) {
6
+ const [isFocused, setIsFocused] = useState(false);
7
+ const onFocus = useCallback(event => {
8
+ setIsFocused(true);
9
+ onFocusProp === null || onFocusProp === void 0 ? void 0 : onFocusProp(event);
10
+ }, [onFocusProp]);
11
+ const onBlur = useCallback(event => {
12
+ setIsFocused(false);
13
+ onBlurProp === null || onBlurProp === void 0 ? void 0 : onBlurProp(event);
14
+ }, [onBlurProp]);
15
+ return [isFocused, {
16
+ onFocus,
17
+ onBlur
18
+ }];
19
+ }
20
+ export default useIsFocused;
@@ -0,0 +1,45 @@
1
+ function useKeyboardInputEvent({
2
+ onSegmentChange,
3
+ onSegmentValueChange,
4
+ onSegmentValueChangeWithNumericKeys,
5
+ onSegmentValueRemove,
6
+ onKeyDown
7
+ }) {
8
+ return event => {
9
+ var _key$match;
10
+ const {
11
+ key,
12
+ ctrlKey,
13
+ metaKey
14
+ } = event;
15
+ switch (key) {
16
+ case 'ArrowRight':
17
+ case 'ArrowLeft':
18
+ onSegmentChange === null || onSegmentChange === void 0 ? void 0 : onSegmentChange(event);
19
+ event.preventDefault();
20
+ break;
21
+ case 'ArrowUp':
22
+ case 'ArrowDown':
23
+ onSegmentValueChange === null || onSegmentValueChange === void 0 ? void 0 : onSegmentValueChange(event);
24
+ event.preventDefault();
25
+ break;
26
+ case 'Backspace':
27
+ case 'Delete':
28
+ onSegmentValueRemove === null || onSegmentValueRemove === void 0 ? void 0 : onSegmentValueRemove(event);
29
+ event.preventDefault();
30
+ break;
31
+ case (_key$match = key.match(/\d/)) === null || _key$match === void 0 ? void 0 : _key$match.input:
32
+ // Allow numeric keys to be entered
33
+ onSegmentValueChangeWithNumericKeys === null || onSegmentValueChangeWithNumericKeys === void 0 ? void 0 : onSegmentValueChangeWithNumericKeys(event);
34
+ event.preventDefault();
35
+ break;
36
+ default:
37
+ // case key.match(/[a-z]/)?.[0]:
38
+ // Prevent letters from being entered
39
+ if (!ctrlKey && !metaKey) event.preventDefault();
40
+ break;
41
+ }
42
+ onKeyDown === null || onKeyDown === void 0 ? void 0 : onKeyDown(event);
43
+ };
44
+ }
45
+ export default useKeyboardInputEvent;