@tecsinapse/react-core 1.12.8 → 1.14.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/CHANGELOG.md +35 -0
- package/dist/components/atoms/Input/InputElement/InputElement.d.ts +5 -3
- package/dist/components/atoms/Input/InputElement/InputElement.js +22 -8
- package/dist/components/atoms/Input/InputElement/InputElement.js.map +1 -1
- package/dist/components/atoms/Input/hooks/useNumberMask.d.ts +8 -0
- package/dist/components/atoms/Input/hooks/useNumberMask.js +98 -0
- package/dist/components/atoms/Input/hooks/useNumberMask.js.map +1 -0
- package/dist/components/atoms/Input/hooks/useStringMask.d.ts +8 -0
- package/dist/components/atoms/Input/hooks/useStringMask.js +109 -0
- package/dist/components/atoms/Input/hooks/useStringMask.js.map +1 -0
- package/dist/components/atoms/Input/index.d.ts +2 -3
- package/dist/components/atoms/Input/index.js +12 -26
- package/dist/components/atoms/Input/index.js.map +1 -1
- package/dist/components/atoms/ProgressBar/ProgressBar.d.ts +1 -1
- package/dist/components/atoms/ProgressBar/ProgressBar.js +6 -6
- package/dist/components/atoms/ProgressBar/ProgressBar.js.map +1 -1
- package/dist/components/molecules/Calendar/components/MonthWeek.js +5 -10
- package/dist/components/molecules/Calendar/components/MonthWeek.js.map +1 -1
- package/dist/components/molecules/Calendar/index.d.ts +1 -1
- package/dist/components/molecules/Calendar/index.js +6 -0
- package/dist/components/molecules/Calendar/index.js.map +1 -1
- package/dist/components/molecules/Calendar/styled.js +0 -17
- package/dist/components/molecules/Calendar/styled.js.map +1 -1
- package/dist/components/molecules/DatePicker/DatePicker.d.ts +6 -5
- package/dist/components/molecules/DatePicker/DatePicker.js +22 -32
- package/dist/components/molecules/DatePicker/DatePicker.js.map +1 -1
- package/dist/components/molecules/DatePicker/styled.d.ts +0 -14
- package/dist/components/molecules/DatePicker/styled.js +1 -46
- package/dist/components/molecules/DatePicker/styled.js.map +1 -1
- package/dist/components/molecules/DateTimePicker/DateTimePicker.d.ts +5 -4
- package/dist/components/molecules/DateTimePicker/DateTimePicker.js +35 -37
- package/dist/components/molecules/DateTimePicker/DateTimePicker.js.map +1 -1
- package/dist/components/molecules/DateTimePicker/styled.d.ts +0 -21
- package/dist/components/molecules/DateTimePicker/styled.js +3 -51
- package/dist/components/molecules/DateTimePicker/styled.js.map +1 -1
- package/dist/components/molecules/Grid/Item/Item.d.ts +1 -1
- package/dist/components/molecules/Grid/Item/Item.js +7 -5
- package/dist/components/molecules/Grid/Item/Item.js.map +1 -1
- package/dist/components/molecules/HintInputContainer/HintInputContainer.d.ts +3 -2
- package/dist/components/molecules/HintInputContainer/HintInputContainer.js +4 -4
- package/dist/components/molecules/HintInputContainer/HintInputContainer.js.map +1 -1
- package/dist/components/molecules/TextArea/TextArea.d.ts +3 -1
- package/dist/components/molecules/TextArea/TextArea.js +4 -4
- package/dist/components/molecules/TextArea/TextArea.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +15 -29
- package/dist/index.js.map +1 -1
- package/dist/utils/ResponsiveFontSize.js +1 -1
- package/dist/utils/ResponsiveFontSize.js.map +1 -1
- package/dist/utils/formatWithMask.d.ts +3 -0
- package/dist/utils/formatWithMask.js +31 -0
- package/dist/utils/formatWithMask.js.map +1 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.js +28 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/masks.d.ts +10 -0
- package/dist/utils/masks.js +18 -0
- package/dist/utils/masks.js.map +1 -0
- package/package.json +2 -2
- package/src/components/atoms/Input/InputElement/InputElement.tsx +37 -11
- package/src/components/atoms/Input/hooks/useNumberMask.ts +99 -0
- package/src/components/atoms/Input/hooks/useStringMask.ts +126 -0
- package/src/components/atoms/Input/index.ts +2 -3
- package/src/components/atoms/ProgressBar/ProgressBar.tsx +5 -5
- package/src/components/molecules/Calendar/components/MonthWeek.tsx +5 -10
- package/src/components/molecules/Calendar/index.ts +1 -0
- package/src/components/molecules/Calendar/styled.ts +0 -22
- package/src/components/molecules/DatePicker/DatePicker.tsx +32 -41
- package/src/components/molecules/DatePicker/styled.ts +0 -40
- package/src/components/molecules/DateTimePicker/DateTimePicker.tsx +46 -52
- package/src/components/molecules/DateTimePicker/styled.ts +1 -49
- package/src/components/molecules/Grid/Item/Item.tsx +3 -5
- package/src/components/molecules/HintInputContainer/HintInputContainer.tsx +3 -8
- package/src/components/molecules/TextArea/TextArea.tsx +3 -9
- package/src/index.ts +3 -5
- package/src/utils/ResponsiveFontSize.ts +2 -2
- package/src/utils/formatWithMask.ts +25 -0
- package/src/utils/index.ts +4 -2
- package/src/utils/masks.ts +13 -0
- package/dist/components/atoms/Input/hooks/useCurrencyMask.d.ts +0 -3
- package/dist/components/atoms/Input/hooks/useCurrencyMask.js +0 -69
- package/dist/components/atoms/Input/hooks/useCurrencyMask.js.map +0 -1
- package/dist/components/atoms/Input/hooks/useMask.d.ts +0 -9
- package/dist/components/atoms/Input/hooks/useMask.js +0 -61
- package/dist/components/atoms/Input/hooks/useMask.js.map +0 -1
- package/dist/components/atoms/Input/masks/index.d.ts +0 -10
- package/dist/components/atoms/Input/masks/index.js +0 -18
- package/dist/components/atoms/Input/masks/index.js.map +0 -1
- package/dist/components/molecules/DatePicker/Modal.d.ts +0 -8
- package/dist/components/molecules/DatePicker/Modal.js +0 -60
- package/dist/components/molecules/DatePicker/Modal.js.map +0 -1
- package/dist/components/molecules/DateTimePicker/Modal.d.ts +0 -7
- package/dist/components/molecules/DateTimePicker/Modal.js +0 -89
- package/dist/components/molecules/DateTimePicker/Modal.js.map +0 -1
- package/src/components/atoms/Input/hooks/useCurrencyMask.ts +0 -74
- package/src/components/atoms/Input/hooks/useMask.ts +0 -92
- package/src/components/atoms/Input/masks/index.ts +0 -12
- package/src/components/molecules/DatePicker/DatePicker.stories.tsx +0 -44
- package/src/components/molecules/DatePicker/Modal.tsx +0 -51
- package/src/components/molecules/DateTimePicker/DateTimePicker.stories.tsx +0 -26
- package/src/components/molecules/DateTimePicker/Modal.tsx +0 -84
|
@@ -1,20 +1,27 @@
|
|
|
1
1
|
import { useTheme } from '@emotion/react';
|
|
2
2
|
import { ThemeProp } from '@tecsinapse/react-core';
|
|
3
|
-
import React, { FC } from 'react';
|
|
3
|
+
import React, { FC, useEffect, useState } from 'react';
|
|
4
4
|
import { StyleProp, TextInputProps, TextStyle } from 'react-native';
|
|
5
|
-
import { IMask } from '../hooks/useMask';
|
|
6
5
|
import { StyledInputElement } from '../styled';
|
|
6
|
+
import { MaskType, useStringMask } from '../hooks/useStringMask';
|
|
7
|
+
import { CurrencyOptions, useNumberMask } from '../hooks/useNumberMask';
|
|
7
8
|
|
|
8
9
|
export interface InputElementProps
|
|
9
10
|
extends Omit<TextInputProps, 'onChange' | 'value' | 'ref'> {
|
|
10
11
|
style?: StyleProp<TextStyle>;
|
|
11
|
-
|
|
12
|
-
* TODO:
|
|
13
|
-
*/
|
|
14
|
-
value?: string | IMask;
|
|
12
|
+
value: string | number;
|
|
15
13
|
placeholder?: string;
|
|
16
14
|
disabled?: boolean;
|
|
17
|
-
onChange?: (value:
|
|
15
|
+
onChange?: (value: any) => void;
|
|
16
|
+
/**
|
|
17
|
+
To use mask for strings you have to pass a MaskType[] or ((value: string) => MaskType[])
|
|
18
|
+
A MaskType can be a string, RegExp, or Array<RegExp>.
|
|
19
|
+
In case we have a string, '9' represents digits, 'a' represents alphabet characters, and any other character represents fixed characters in the mask.
|
|
20
|
+
For example a phone mask can be represented as ['(99) \\99999-9999'], or ['(99) ', '/[9]/', '9999-9999'], or ['(', /[0-9]/, /[0-9]/, ') ', '/[9]/', '9999-9999'] and many others.
|
|
21
|
+
To use mask for numbers you have to pass a CurrencyOptions object
|
|
22
|
+
A CurrencyOptions object contains symbol, separator, decimal, precision. For example {symbol: 'R$ ', separator: '.', decimal: ',', precision: 2,}.
|
|
23
|
+
**/
|
|
24
|
+
mask?: (MaskType[] | ((value: string) => MaskType[])) | CurrencyOptions;
|
|
18
25
|
onFocus?: () => void;
|
|
19
26
|
onBlur?: () => void;
|
|
20
27
|
ref?: React.Ref<any>;
|
|
@@ -28,21 +35,40 @@ const InputElement: FC<InputElementProps> = React.forwardRef(
|
|
|
28
35
|
value,
|
|
29
36
|
disabled = false,
|
|
30
37
|
placeholderTextColor,
|
|
38
|
+
mask,
|
|
31
39
|
...rest
|
|
32
40
|
},
|
|
33
41
|
ref: React.Ref<any>
|
|
34
42
|
): JSX.Element => {
|
|
35
43
|
const theme = useTheme() as ThemeProp;
|
|
36
|
-
const _value =
|
|
37
|
-
typeof value === 'string' ? value : value?.maskValue?.formatted;
|
|
38
44
|
const _placeholderColor = placeholderTextColor || theme.font.color.dark;
|
|
39
45
|
|
|
46
|
+
const [maskValue, setMaskValue] =
|
|
47
|
+
mask === undefined
|
|
48
|
+
? useState<string>(String(value))
|
|
49
|
+
: Array.isArray(mask) || typeof mask === 'function'
|
|
50
|
+
? useStringMask(mask, String(value))
|
|
51
|
+
: useNumberMask(mask, value);
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
if (onChange) {
|
|
55
|
+
if (typeof maskValue === 'string') onChange(maskValue);
|
|
56
|
+
else onChange(maskValue?.raw);
|
|
57
|
+
}
|
|
58
|
+
}, [maskValue]);
|
|
59
|
+
|
|
60
|
+
const onChangeValue = (value: string) => {
|
|
61
|
+
setMaskValue(value);
|
|
62
|
+
};
|
|
63
|
+
|
|
40
64
|
return (
|
|
41
65
|
<StyledInputElement
|
|
42
66
|
{...rest}
|
|
43
67
|
ref={ref}
|
|
44
|
-
onChangeText={
|
|
45
|
-
value={
|
|
68
|
+
onChangeText={onChangeValue}
|
|
69
|
+
value={
|
|
70
|
+
typeof maskValue === 'string' ? maskValue : maskValue?.formatted ?? ''
|
|
71
|
+
}
|
|
46
72
|
placeholder={placeholder}
|
|
47
73
|
placeholderTextColor={_placeholderColor}
|
|
48
74
|
disabled={disabled}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import currency from 'currency.js';
|
|
2
|
+
import { useCallback, useState } from 'react';
|
|
3
|
+
import { extractNumbersFromString } from '../../../../utils';
|
|
4
|
+
import { MaskValue } from './useStringMask';
|
|
5
|
+
|
|
6
|
+
export type CurrencyOptions = currency.Options;
|
|
7
|
+
|
|
8
|
+
const DEFAULT_OPTIONS: CurrencyOptions = {
|
|
9
|
+
symbol: 'R$ ',
|
|
10
|
+
separator: '.',
|
|
11
|
+
decimal: ',',
|
|
12
|
+
precision: 2,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const getRegex = (precision: number) =>
|
|
16
|
+
new RegExp(`\\B(?=(\\d{${precision}})(?!\\d))`, 'g');
|
|
17
|
+
|
|
18
|
+
export const getInternalNumberAndMask = (
|
|
19
|
+
value: string | number,
|
|
20
|
+
options?: CurrencyOptions
|
|
21
|
+
): { internalNumber: number; mergedOptions: CurrencyOptions } => {
|
|
22
|
+
const mergedOptions = { ...DEFAULT_OPTIONS, ...options };
|
|
23
|
+
const { precision = -1 } = mergedOptions;
|
|
24
|
+
|
|
25
|
+
let internalNumber;
|
|
26
|
+
|
|
27
|
+
if (typeof value === 'number') {
|
|
28
|
+
if (precision) {
|
|
29
|
+
let stringValue = String(value);
|
|
30
|
+
const decimalIndex = stringValue.indexOf('.');
|
|
31
|
+
const currentPrecision = decimalIndex + precision;
|
|
32
|
+
if (decimalIndex !== -1 && currentPrecision <= stringValue.length) {
|
|
33
|
+
const zeros = stringValue.length + 1 - currentPrecision;
|
|
34
|
+
for (let i = 0; i < zeros; i++) stringValue = stringValue + `0`;
|
|
35
|
+
}
|
|
36
|
+
internalNumber = Number(stringValue);
|
|
37
|
+
} else {
|
|
38
|
+
internalNumber = value;
|
|
39
|
+
}
|
|
40
|
+
} else {
|
|
41
|
+
const onlyNumbers = String(extractNumbersFromString(value));
|
|
42
|
+
const padZeros = String(onlyNumbers).padStart(precision + 1, '0');
|
|
43
|
+
internalNumber = Number(padZeros.replace(getRegex(precision), '.'));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (internalNumber > Number.MAX_SAFE_INTEGER) {
|
|
47
|
+
internalNumber = Number.MAX_SAFE_INTEGER;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (internalNumber < Number.MIN_SAFE_INTEGER) {
|
|
51
|
+
internalNumber = Number.MIN_SAFE_INTEGER;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
internalNumber,
|
|
56
|
+
mergedOptions,
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* TODO:
|
|
62
|
+
* @param options
|
|
63
|
+
* @param defaultValue
|
|
64
|
+
* @returns
|
|
65
|
+
*/
|
|
66
|
+
export const useNumberMask = (
|
|
67
|
+
options?: CurrencyOptions,
|
|
68
|
+
defaultValue?: string | number
|
|
69
|
+
): [MaskValue, (value: string) => void] => {
|
|
70
|
+
const applyMask = useCallback(
|
|
71
|
+
(value: string | number = 0): MaskValue => {
|
|
72
|
+
const { internalNumber, mergedOptions } = getInternalNumberAndMask(
|
|
73
|
+
value,
|
|
74
|
+
options
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
raw: internalNumber,
|
|
79
|
+
formatted: currency(internalNumber).format(mergedOptions),
|
|
80
|
+
};
|
|
81
|
+
},
|
|
82
|
+
[options, getRegex]
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
const [value, setValue] = useState<MaskValue>(applyMask(defaultValue));
|
|
86
|
+
|
|
87
|
+
const handleChangeValue = useCallback(
|
|
88
|
+
(formattedValue: string) => {
|
|
89
|
+
const { raw, formatted } = applyMask(formattedValue);
|
|
90
|
+
setValue({
|
|
91
|
+
raw,
|
|
92
|
+
formatted,
|
|
93
|
+
});
|
|
94
|
+
},
|
|
95
|
+
[applyMask, setValue]
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
return [value, handleChangeValue];
|
|
99
|
+
};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { useCallback, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
export interface MaskValue {
|
|
4
|
+
formatted?: string;
|
|
5
|
+
raw?: any;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type MaskType = string | RegExp | Array<RegExp>;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* TODO:
|
|
12
|
+
* @param value
|
|
13
|
+
* @param mask
|
|
14
|
+
* @returns
|
|
15
|
+
*/
|
|
16
|
+
export const mergeMask = (value = '', mask: MaskType[]): MaskValue => {
|
|
17
|
+
let formatted = '';
|
|
18
|
+
let raw = '';
|
|
19
|
+
let iMask = 0;
|
|
20
|
+
let iChars = 0;
|
|
21
|
+
|
|
22
|
+
while (!(iMask === mask.length || iChars === value.length)) {
|
|
23
|
+
const maskChar = mask[iMask];
|
|
24
|
+
const valueChar = value[iChars];
|
|
25
|
+
|
|
26
|
+
if (maskChar === valueChar) {
|
|
27
|
+
formatted += maskChar;
|
|
28
|
+
iChars++;
|
|
29
|
+
iMask++;
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const rawValueChar = value[iChars];
|
|
34
|
+
|
|
35
|
+
if (typeof maskChar === 'object') {
|
|
36
|
+
iChars++;
|
|
37
|
+
|
|
38
|
+
const maskCharRegex = Array.isArray(maskChar) ? maskChar[0] : maskChar;
|
|
39
|
+
const matchRegex = RegExp(maskCharRegex).test(valueChar);
|
|
40
|
+
|
|
41
|
+
if (matchRegex) {
|
|
42
|
+
formatted += valueChar;
|
|
43
|
+
raw += rawValueChar;
|
|
44
|
+
iMask++;
|
|
45
|
+
}
|
|
46
|
+
} else {
|
|
47
|
+
formatted += maskChar;
|
|
48
|
+
iMask++;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return { raw, formatted };
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const getMask = (
|
|
56
|
+
mask: MaskType[] | ((value: string) => MaskType[]),
|
|
57
|
+
newValue: string
|
|
58
|
+
): MaskType[] => {
|
|
59
|
+
let maskArray: MaskType[];
|
|
60
|
+
const regexArray: MaskType[] = [];
|
|
61
|
+
|
|
62
|
+
if (typeof mask === 'function') {
|
|
63
|
+
maskArray = mask(newValue);
|
|
64
|
+
} else {
|
|
65
|
+
maskArray = mask;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
maskArray.forEach(exp => {
|
|
69
|
+
if (typeof exp !== 'string') {
|
|
70
|
+
if (Array.isArray(exp)) regexArray.push(exp);
|
|
71
|
+
else regexArray.push(exp);
|
|
72
|
+
} else {
|
|
73
|
+
for (let i = 0; i < exp.length; i++) {
|
|
74
|
+
if (exp[i] === '\\') {
|
|
75
|
+
regexArray.push(exp[i + 1]);
|
|
76
|
+
i++;
|
|
77
|
+
} else {
|
|
78
|
+
if (exp[i] === '9') regexArray.push(/\d/);
|
|
79
|
+
else if (exp[i] === 'a') regexArray.push(/[a-zA-Z]/);
|
|
80
|
+
else regexArray.push(exp[i]);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
return regexArray;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* TODO:
|
|
91
|
+
* @param mask
|
|
92
|
+
* @param defaultValue
|
|
93
|
+
* @returns
|
|
94
|
+
*/
|
|
95
|
+
export const useStringMask = (
|
|
96
|
+
mask: MaskType[] | ((value: string) => MaskType[]),
|
|
97
|
+
defaultValue?: string
|
|
98
|
+
): [MaskValue, (text: string) => void] => {
|
|
99
|
+
const applyMask = useCallback(
|
|
100
|
+
(value = ''): MaskValue => {
|
|
101
|
+
const selectedMask = getMask(mask, value);
|
|
102
|
+
const { formatted, raw } = mergeMask(value, selectedMask);
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
raw,
|
|
106
|
+
formatted,
|
|
107
|
+
};
|
|
108
|
+
},
|
|
109
|
+
[mask]
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const [value, setValue] = useState<MaskValue>(applyMask(defaultValue));
|
|
113
|
+
|
|
114
|
+
const handleChangeValue = useCallback(
|
|
115
|
+
(formattedValue: string) => {
|
|
116
|
+
const { raw, formatted } = applyMask(formattedValue);
|
|
117
|
+
setValue({
|
|
118
|
+
raw,
|
|
119
|
+
formatted,
|
|
120
|
+
});
|
|
121
|
+
},
|
|
122
|
+
[applyMask, setValue]
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
return [value, handleChangeValue];
|
|
126
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { default as Hint } from './Hint';
|
|
2
|
-
export * from './hooks/useCurrencyMask';
|
|
3
2
|
export * from './hooks/useInputFocus';
|
|
4
|
-
export * from './hooks/
|
|
3
|
+
export * from './hooks/useNumberMask';
|
|
4
|
+
export * from './hooks/useStringMask';
|
|
5
5
|
export {
|
|
6
6
|
default as InputContainer,
|
|
7
7
|
InputContainerProps,
|
|
@@ -11,7 +11,6 @@ export {
|
|
|
11
11
|
default as InputElement,
|
|
12
12
|
InputElementProps,
|
|
13
13
|
} from './InputElement/InputElement';
|
|
14
|
-
export * from './masks';
|
|
15
14
|
export {
|
|
16
15
|
PressableInputContainer,
|
|
17
16
|
PressableInputContainerProps,
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
+
import { useTheme } from '@emotion/react';
|
|
1
2
|
import * as React from 'react';
|
|
2
3
|
import { Animated, ViewProps } from 'react-native';
|
|
3
|
-
import { Container, Progress, Segment } from './styled';
|
|
4
|
-
import { useTheme } from '@emotion/react';
|
|
5
4
|
import {
|
|
6
5
|
ColorGradationType,
|
|
7
6
|
ColorType,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
} from '
|
|
7
|
+
ThemeProp
|
|
8
|
+
} from '../../../types/defaults';
|
|
9
|
+
import { extractNumbersFromString } from '../../../utils';
|
|
10
|
+
import { Container, Progress, Segment } from './styled';
|
|
11
11
|
|
|
12
12
|
export interface ProgressBarProps extends ViewProps {
|
|
13
13
|
/** Number of segments. Defaults to 1. Set to 1 when 0 or less */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { compareAsc as compare,
|
|
2
|
+
import { compareAsc as compare, isSameDay } from 'date-fns';
|
|
3
3
|
import { Cell, Selected, Week } from '../styled';
|
|
4
4
|
import { Value, DateRange, SelectionType } from '../Calendar';
|
|
5
5
|
import { TextProps } from '@tecsinapse/react-core';
|
|
@@ -62,7 +62,7 @@ const MonthWeek = <T extends SelectionType>({
|
|
|
62
62
|
|
|
63
63
|
if (!highest) {
|
|
64
64
|
if (compare(date, lowest) === -1) {
|
|
65
|
-
newValue = { lowest: date, highest:
|
|
65
|
+
newValue = { lowest: date, highest: undefined };
|
|
66
66
|
} else if (compare(date, lowest) === 0) {
|
|
67
67
|
newValue = undefined;
|
|
68
68
|
} else {
|
|
@@ -70,17 +70,12 @@ const MonthWeek = <T extends SelectionType>({
|
|
|
70
70
|
}
|
|
71
71
|
} else {
|
|
72
72
|
if (compare(date, lowest) === -1) {
|
|
73
|
-
newValue = { lowest: date, highest:
|
|
73
|
+
newValue = { lowest: date, highest: undefined };
|
|
74
74
|
} else if (compare(date, lowest) === 0) {
|
|
75
|
-
newValue =
|
|
75
|
+
newValue = undefined;
|
|
76
76
|
} else {
|
|
77
77
|
if (compare(date, highest) === -1) {
|
|
78
|
-
|
|
79
|
-
const highestDiff = Math.abs(differenceInDays(date, highest));
|
|
80
|
-
newValue = {
|
|
81
|
-
lowest: lowestDiff < highestDiff ? date : lowest,
|
|
82
|
-
highest: highestDiff <= lowestDiff ? date : highest,
|
|
83
|
-
};
|
|
78
|
+
newValue = { lowest: lowest, highest: date };
|
|
84
79
|
} else if (compare(date, highest) === 0) {
|
|
85
80
|
newValue = { lowest: lowest, highest: undefined };
|
|
86
81
|
} else {
|
|
@@ -19,26 +19,6 @@ export const TitleContainer = styled.View<Partial<StyleProps>>`
|
|
|
19
19
|
background-color: ${({ theme }) => theme.color.secondary.xlight};
|
|
20
20
|
`;
|
|
21
21
|
|
|
22
|
-
const surfaceBorderRight = ({
|
|
23
|
-
isRright,
|
|
24
|
-
theme,
|
|
25
|
-
}: ButtonBorders & Partial<StyleProps>) =>
|
|
26
|
-
!isWeb &&
|
|
27
|
-
isRright &&
|
|
28
|
-
css`
|
|
29
|
-
border-top-right-radius: ${theme?.borderRadius.deca};
|
|
30
|
-
`;
|
|
31
|
-
|
|
32
|
-
const surfaceBorderLeft = ({
|
|
33
|
-
isLeft,
|
|
34
|
-
theme,
|
|
35
|
-
}: ButtonBorders & Partial<StyleProps>) =>
|
|
36
|
-
!isWeb &&
|
|
37
|
-
isLeft &&
|
|
38
|
-
css`
|
|
39
|
-
border-top-left-radius: ${theme?.borderRadius.deca};
|
|
40
|
-
`;
|
|
41
|
-
|
|
42
22
|
export const Control = styled(PressableSurface)(
|
|
43
23
|
(
|
|
44
24
|
props: Partial<StyleProps> & ButtonBorders & { align: 'start' | 'end' }
|
|
@@ -47,8 +27,6 @@ export const Control = styled(PressableSurface)(
|
|
|
47
27
|
padding: ${props.theme?.spacing.centi};
|
|
48
28
|
border-radius: ${props.theme?.borderRadius.mili};
|
|
49
29
|
margin: ${props.theme?.spacing.mili};
|
|
50
|
-
${surfaceBorderRight(props)}
|
|
51
|
-
${surfaceBorderLeft(props)}
|
|
52
30
|
`
|
|
53
31
|
);
|
|
54
32
|
|
|
@@ -1,29 +1,27 @@
|
|
|
1
1
|
import { format as formatDate } from 'date-fns';
|
|
2
2
|
import * as React from 'react';
|
|
3
|
-
import {
|
|
3
|
+
import { useEffect } from 'react';
|
|
4
4
|
import { InputContainerProps, useInputFocus } from '../../atoms/Input';
|
|
5
5
|
import { Text, TextProps } from '../../atoms/Text';
|
|
6
6
|
import { CalendarProps, DateRange, SelectionType } from '../Calendar';
|
|
7
|
-
import { DatePickerModalProps, Modal } from './Modal';
|
|
8
|
-
import { CalendarIcon, getStyledTextComponent } from './styled';
|
|
9
7
|
import { HintInputContainer } from '../HintInputContainer';
|
|
10
|
-
import {
|
|
8
|
+
import { CalendarIcon, getStyledTextComponent } from './styled';
|
|
11
9
|
|
|
12
|
-
export interface DatePickerProps<T extends SelectionType>
|
|
13
|
-
extends InputContainerProps,
|
|
14
|
-
DatePickerModalProps<T>,
|
|
15
|
-
Omit<CalendarProps<T>, 'style'> {
|
|
10
|
+
export interface DatePickerProps<T extends SelectionType> extends InputContainerProps, Omit<CalendarProps<T>, 'style'> {
|
|
16
11
|
controlComponent?: (
|
|
17
12
|
onPress: () => void,
|
|
18
13
|
displayValue?: string
|
|
19
14
|
) => JSX.Element;
|
|
20
15
|
TextComponent?: React.FC<TextProps>;
|
|
21
|
-
|
|
16
|
+
CalendarComponent: React.FC<CalendarProps<T>>
|
|
22
17
|
placeholder?: string;
|
|
23
18
|
onFocus?: () => void | never;
|
|
24
19
|
onBlur?: () => void | never;
|
|
25
20
|
format?: string;
|
|
26
21
|
closeOnPick?: boolean;
|
|
22
|
+
renderCalendar: (calendar: React.ReactElement, blur?: () => void) => JSX.Element|null
|
|
23
|
+
requestShowCalendar: () => void
|
|
24
|
+
requestCloseCalendar: () => void
|
|
27
25
|
}
|
|
28
26
|
|
|
29
27
|
function DatePicker<T extends SelectionType>({
|
|
@@ -34,7 +32,6 @@ function DatePicker<T extends SelectionType>({
|
|
|
34
32
|
value,
|
|
35
33
|
type,
|
|
36
34
|
format = 'yyyy-MM-dd',
|
|
37
|
-
|
|
38
35
|
placeholder,
|
|
39
36
|
onFocus,
|
|
40
37
|
onBlur,
|
|
@@ -45,31 +42,26 @@ function DatePicker<T extends SelectionType>({
|
|
|
45
42
|
variant,
|
|
46
43
|
TextComponent = Text,
|
|
47
44
|
CalendarComponent,
|
|
48
|
-
bottomOffset,
|
|
49
45
|
rightComponent,
|
|
50
|
-
animationType = 'fade',
|
|
51
46
|
style,
|
|
52
47
|
locale,
|
|
53
48
|
closeOnPick = false,
|
|
49
|
+
renderCalendar,
|
|
50
|
+
requestShowCalendar,
|
|
51
|
+
requestCloseCalendar,
|
|
54
52
|
...rest
|
|
55
53
|
}: DatePickerProps<T>): JSX.Element {
|
|
54
|
+
|
|
56
55
|
const { focused, handleBlur, handleFocus } = useInputFocus(
|
|
57
56
|
onFocus,
|
|
58
57
|
onBlur,
|
|
59
58
|
!disabled
|
|
60
59
|
);
|
|
61
60
|
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
const handlePressInput = React.useCallback(() => {
|
|
65
|
-
setModalVisible(true);
|
|
61
|
+
const handleShowCalendar = React.useCallback(() => {
|
|
62
|
+
requestShowCalendar()
|
|
66
63
|
handleFocus();
|
|
67
|
-
}, [handleFocus,
|
|
68
|
-
|
|
69
|
-
const handleCloseModal = React.useCallback(() => {
|
|
70
|
-
setModalVisible(false);
|
|
71
|
-
handleBlur();
|
|
72
|
-
}, [handleBlur, setModalVisible]);
|
|
64
|
+
}, [handleFocus, requestShowCalendar]);
|
|
73
65
|
|
|
74
66
|
const getDisplayValue = () => {
|
|
75
67
|
if (!value) return placeholder;
|
|
@@ -89,23 +81,35 @@ function DatePicker<T extends SelectionType>({
|
|
|
89
81
|
|
|
90
82
|
useEffect(() => {
|
|
91
83
|
if (closeOnPick && value && type === 'day') {
|
|
92
|
-
setTimeout(
|
|
84
|
+
setTimeout(requestCloseCalendar, 200);
|
|
93
85
|
}
|
|
94
86
|
if (closeOnPick && value && type === 'range') {
|
|
95
87
|
const { lowest, highest } = value as DateRange;
|
|
96
|
-
lowest && highest && setTimeout(
|
|
88
|
+
lowest && highest && setTimeout(requestCloseCalendar, 200);
|
|
97
89
|
}
|
|
98
|
-
}, [value, closeOnPick, type,
|
|
90
|
+
}, [value, closeOnPick, type, requestCloseCalendar]);
|
|
91
|
+
|
|
92
|
+
const calendar = (
|
|
93
|
+
<CalendarComponent
|
|
94
|
+
pointerEvents={'box-none'}
|
|
95
|
+
type={type}
|
|
96
|
+
value={value}
|
|
97
|
+
month={month}
|
|
98
|
+
year={year}
|
|
99
|
+
onChange={onChange}
|
|
100
|
+
locale={locale}
|
|
101
|
+
/>
|
|
102
|
+
)
|
|
99
103
|
|
|
100
104
|
return (
|
|
101
105
|
<>
|
|
102
106
|
{controlComponent ? (
|
|
103
|
-
controlComponent(
|
|
107
|
+
controlComponent(handleShowCalendar, getDisplayValue())
|
|
104
108
|
) : (
|
|
105
109
|
<HintInputContainer
|
|
106
110
|
focused={focused}
|
|
107
111
|
viewStyle={style}
|
|
108
|
-
onPress={
|
|
112
|
+
onPress={handleShowCalendar}
|
|
109
113
|
disabled={disabled}
|
|
110
114
|
hintComponent={hintComponent}
|
|
111
115
|
LabelComponent={TextComponent}
|
|
@@ -124,20 +128,7 @@ function DatePicker<T extends SelectionType>({
|
|
|
124
128
|
</StyledText>
|
|
125
129
|
</HintInputContainer>
|
|
126
130
|
)}
|
|
127
|
-
|
|
128
|
-
CalendarComponent={CalendarComponent}
|
|
129
|
-
bottomOffset={bottomOffset}
|
|
130
|
-
visible={modalVisible}
|
|
131
|
-
onRequestClose={handleCloseModal}
|
|
132
|
-
animated
|
|
133
|
-
animationType={animationType}
|
|
134
|
-
month={month}
|
|
135
|
-
year={year}
|
|
136
|
-
onChange={onChange}
|
|
137
|
-
value={value}
|
|
138
|
-
type={type}
|
|
139
|
-
locale={locale}
|
|
140
|
-
/>
|
|
131
|
+
{renderCalendar(calendar, handleBlur)}
|
|
141
132
|
</>
|
|
142
133
|
);
|
|
143
134
|
}
|
|
@@ -1,50 +1,10 @@
|
|
|
1
1
|
import styled, { css } from '@emotion/native';
|
|
2
2
|
import { FC } from 'react';
|
|
3
|
-
import { Platform } from 'react-native';
|
|
4
|
-
import { hex2rgba } from '../../../styles/definitions';
|
|
5
3
|
import { StyleProps } from '../../../types/defaults';
|
|
6
|
-
import { RFValue } from '../../../utils';
|
|
7
4
|
import { Icon } from '../../atoms/Icon';
|
|
8
5
|
import { disabledInputStyles, InputContainerProps } from '../../atoms/Input';
|
|
9
|
-
import { PressableSurface } from '../../atoms/PressableSurface';
|
|
10
6
|
import { TextProps } from '../../atoms/Text';
|
|
11
7
|
|
|
12
|
-
const isWeb = Platform.OS === 'web';
|
|
13
|
-
|
|
14
|
-
export const Backdrop = styled(PressableSurface)<Partial<StyleProps>>`
|
|
15
|
-
${({ theme }) =>
|
|
16
|
-
isWeb
|
|
17
|
-
? css`
|
|
18
|
-
justify-content: center;
|
|
19
|
-
align-items: center;
|
|
20
|
-
background-color: ${hex2rgba(theme.miscellaneous.overlay, 0.5)};
|
|
21
|
-
height: 100vh;
|
|
22
|
-
`
|
|
23
|
-
: css`
|
|
24
|
-
justify-content: flex-end;
|
|
25
|
-
background-color: ${hex2rgba(theme.miscellaneous.overlay, 0.5)};
|
|
26
|
-
height: 100%;
|
|
27
|
-
`}
|
|
28
|
-
`;
|
|
29
|
-
|
|
30
|
-
export const ModalContent = styled.View<
|
|
31
|
-
{ offset: number } & Partial<StyleProps>
|
|
32
|
-
>`
|
|
33
|
-
background-color: transparent;
|
|
34
|
-
padding-bottom: ${({ offset }) => `${RFValue(offset)}px`};
|
|
35
|
-
${({ theme: { borderRadius } }) => css`
|
|
36
|
-
${isWeb
|
|
37
|
-
? `
|
|
38
|
-
border-radius: ${borderRadius.micro};
|
|
39
|
-
`
|
|
40
|
-
: `
|
|
41
|
-
border-top-left-radius: ${borderRadius.deca};
|
|
42
|
-
border-top-right-radius: ${borderRadius.deca};
|
|
43
|
-
`}
|
|
44
|
-
`}
|
|
45
|
-
overflow: hidden;
|
|
46
|
-
`;
|
|
47
|
-
|
|
48
8
|
export const getStyledTextComponent = (component: FC<TextProps>) => {
|
|
49
9
|
return styled(component)(
|
|
50
10
|
(props: Partial<InputContainerProps> & Partial<StyleProps>) => css`
|