@swan-io/shared-business 8.13.0 → 8.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/package.json +1 -1
- package/src/components/ChoicePicker.js +34 -22
- package/src/components/InlineDatePicker.d.ts +10 -0
- package/src/components/InlineDatePicker.js +62 -0
- package/src/locales/en.json +6 -1
- package/src/utils/date.d.ts +7 -0
- package/src/utils/date.js +18 -2
- package/src/utils/validation.d.ts +2 -0
- package/src/utils/validation.js +13 -0
package/package.json
CHANGED
|
@@ -112,11 +112,14 @@ export const ChoicePicker = ({ tile = true, items, getId = identity, large = fal
|
|
|
112
112
|
return;
|
|
113
113
|
}
|
|
114
114
|
// auto scroll to selected value on mobile
|
|
115
|
-
const
|
|
115
|
+
const container = containerRef.current;
|
|
116
|
+
if (container == null) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
116
119
|
const index = items.findIndex(item => value === item);
|
|
117
|
-
if (index !== -1 &&
|
|
118
|
-
const width =
|
|
119
|
-
|
|
120
|
+
if (index !== -1 && container.element != null) {
|
|
121
|
+
const width = container.element.offsetWidth;
|
|
122
|
+
container.scrollTo({ x: index * width, animated: false });
|
|
120
123
|
}
|
|
121
124
|
// if no value is selected, select first item
|
|
122
125
|
if (value == null && items[0] != null) {
|
|
@@ -129,10 +132,13 @@ export const ChoicePicker = ({ tile = true, items, getId = identity, large = fal
|
|
|
129
132
|
if (desktop) {
|
|
130
133
|
return;
|
|
131
134
|
}
|
|
132
|
-
const
|
|
133
|
-
if (
|
|
134
|
-
|
|
135
|
-
|
|
135
|
+
const container = containerRef.current;
|
|
136
|
+
if (container == null) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (container.element != null) {
|
|
140
|
+
const scrollLeft = container.element.scrollLeft;
|
|
141
|
+
const width = container.element.offsetWidth;
|
|
136
142
|
const index = clampValue(0, items.length - 1)(Math.round(scrollLeft / width));
|
|
137
143
|
const item = items[index];
|
|
138
144
|
if (item != null) {
|
|
@@ -146,37 +152,43 @@ export const ChoicePicker = ({ tile = true, items, getId = identity, large = fal
|
|
|
146
152
|
};
|
|
147
153
|
const onPressPrevious = () => {
|
|
148
154
|
var _a;
|
|
149
|
-
const
|
|
150
|
-
if (
|
|
151
|
-
|
|
152
|
-
|
|
155
|
+
const container = containerRef.current;
|
|
156
|
+
if (container == null) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
if (container.element != null) {
|
|
160
|
+
const scrollLeft = container.element.scrollLeft;
|
|
161
|
+
const width = container.element.offsetWidth;
|
|
153
162
|
const index = Math.round(scrollLeft / width);
|
|
154
163
|
const previousIndex = Math.max(0, index - 1);
|
|
155
164
|
// remove scroll snap during scroll animation to avoid weird behavior on older browsers
|
|
156
|
-
|
|
165
|
+
container.element.style.scrollSnapType = "none";
|
|
157
166
|
(_a = containerRef.current) === null || _a === void 0 ? void 0 : _a.scrollTo({ x: previousIndex * width, animated: true });
|
|
158
|
-
detectScrollAnimationEnd(
|
|
167
|
+
detectScrollAnimationEnd(container.element).onResolve(() => {
|
|
159
168
|
// set back scroll snap
|
|
160
169
|
// @ts-expect-error
|
|
161
|
-
|
|
170
|
+
container.element.style.scrollSnapType = null;
|
|
162
171
|
});
|
|
163
172
|
}
|
|
164
173
|
};
|
|
165
174
|
const onPressNext = () => {
|
|
166
175
|
var _a;
|
|
167
|
-
const
|
|
168
|
-
if (
|
|
169
|
-
|
|
170
|
-
|
|
176
|
+
const container = containerRef.current;
|
|
177
|
+
if (container == null) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
if (container.element != null) {
|
|
181
|
+
const scrollLeft = container.element.scrollLeft;
|
|
182
|
+
const width = container.element.offsetWidth;
|
|
171
183
|
const index = Math.round(scrollLeft / width);
|
|
172
184
|
const nextIndex = Math.min(items.length - 1, index + 1);
|
|
173
185
|
// remove scroll snap during scroll animation to avoid weird behavior on older browsers
|
|
174
|
-
|
|
186
|
+
container.element.style.scrollSnapType = "none";
|
|
175
187
|
(_a = containerRef.current) === null || _a === void 0 ? void 0 : _a.scrollTo({ x: nextIndex * width, animated: true });
|
|
176
|
-
detectScrollAnimationEnd(
|
|
188
|
+
detectScrollAnimationEnd(container.element).onResolve(() => {
|
|
177
189
|
// set back scroll snap
|
|
178
190
|
// @ts-expect-error
|
|
179
|
-
|
|
191
|
+
container.element.style.scrollSnapType = null;
|
|
180
192
|
});
|
|
181
193
|
}
|
|
182
194
|
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ExtractedDate } from "../utils/date";
|
|
2
|
+
export type InlineDatePickerProps = {
|
|
3
|
+
label: string;
|
|
4
|
+
value: ExtractedDate | undefined;
|
|
5
|
+
onValueChange: (value: ExtractedDate) => void;
|
|
6
|
+
error?: string;
|
|
7
|
+
onBlur?: () => void;
|
|
8
|
+
order: "day-month-year" | "month-day-year";
|
|
9
|
+
};
|
|
10
|
+
export declare const InlineDatePicker: ({ value, label, onValueChange, error, onBlur, order, }: InlineDatePickerProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box } from "@swan-io/lake/src/components/Box";
|
|
3
|
+
import { InputError } from "@swan-io/lake/src/components/InputError";
|
|
4
|
+
import { LakeLabel } from "@swan-io/lake/src/components/LakeLabel";
|
|
5
|
+
import { LakeSelect } from "@swan-io/lake/src/components/LakeSelect";
|
|
6
|
+
import { LakeTextInput } from "@swan-io/lake/src/components/LakeTextInput";
|
|
7
|
+
import { Stack } from "@swan-io/lake/src/components/Stack";
|
|
8
|
+
import { colors } from "@swan-io/lake/src/constants/design";
|
|
9
|
+
import { isNotNullish } from "@swan-io/lake/src/utils/nullish";
|
|
10
|
+
import { StyleSheet, View } from "react-native";
|
|
11
|
+
import { t } from "../utils/i18n";
|
|
12
|
+
const months = [
|
|
13
|
+
{ value: "01", name: t("datePicker.month.january") },
|
|
14
|
+
{ value: "02", name: t("datePicker.month.february") },
|
|
15
|
+
{ value: "03", name: t("datePicker.month.march") },
|
|
16
|
+
{ value: "04", name: t("datePicker.month.april") },
|
|
17
|
+
{ value: "05", name: t("datePicker.month.may") },
|
|
18
|
+
{ value: "06", name: t("datePicker.month.june") },
|
|
19
|
+
{ value: "07", name: t("datePicker.month.july") },
|
|
20
|
+
{ value: "08", name: t("datePicker.month.august") },
|
|
21
|
+
{ value: "09", name: t("datePicker.month.september") },
|
|
22
|
+
{ value: "10", name: t("datePicker.month.october") },
|
|
23
|
+
{ value: "11", name: t("datePicker.month.november") },
|
|
24
|
+
{ value: "12", name: t("datePicker.month.december") },
|
|
25
|
+
];
|
|
26
|
+
const styles = StyleSheet.create({
|
|
27
|
+
day: {
|
|
28
|
+
maxWidth: 90,
|
|
29
|
+
flexGrow: 0,
|
|
30
|
+
},
|
|
31
|
+
year: {
|
|
32
|
+
maxWidth: 120,
|
|
33
|
+
flexGrow: 0,
|
|
34
|
+
},
|
|
35
|
+
error: {
|
|
36
|
+
borderColor: colors.negative[400],
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
export const InlineDatePicker = ({ value = { day: "", month: "", year: "" }, label, onValueChange, error, onBlur, order, }) => {
|
|
40
|
+
return (_jsx(LakeLabel, { label: label, render: id => {
|
|
41
|
+
const day = (_jsx(View, { style: styles.day, children: _jsx(LakeTextInput, { id: id, style: isNotNullish(error) && styles.error, placeholder: t("datePicker.day"), value: value.day, onBlur: onBlur, hideErrors: true, onChangeText: day => {
|
|
42
|
+
onValueChange({
|
|
43
|
+
day,
|
|
44
|
+
month: value.month,
|
|
45
|
+
year: value.year,
|
|
46
|
+
});
|
|
47
|
+
}, pattern: "[0-9]", maxLength: 2, autoComplete: "bday-day" }) }));
|
|
48
|
+
const month = (_jsx(LakeSelect, { value: value.month === "" ? undefined : value.month, style: isNotNullish(error) && styles.error, placeholder: t("datePicker.month"), hideErrors: true, items: months, onValueChange: month => {
|
|
49
|
+
onValueChange({
|
|
50
|
+
day: value.day,
|
|
51
|
+
month,
|
|
52
|
+
year: value.year,
|
|
53
|
+
});
|
|
54
|
+
} }));
|
|
55
|
+
const year = (_jsx(View, { style: styles.year, children: _jsx(LakeTextInput, { value: value.year, style: isNotNullish(error) && styles.error, placeholder: t("datePicker.year"), onBlur: onBlur, hideErrors: true, onChangeText: year => onValueChange({
|
|
56
|
+
day: value.day,
|
|
57
|
+
month: value.month,
|
|
58
|
+
year,
|
|
59
|
+
}), pattern: "[0-9]", maxLength: 4, autoComplete: "bday-year" }) }));
|
|
60
|
+
return (_jsxs(Box, { children: [order === "day-month-year" ? (_jsxs(Stack, { direction: "row", space: 12, children: [day, " ", month, " ", year] })) : (_jsxs(Stack, { direction: "row", space: 12, children: [month, " ", day, " ", year] })), _jsx(InputError, { message: error })] }));
|
|
61
|
+
} }));
|
|
62
|
+
};
|
package/src/locales/en.json
CHANGED
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"common.skipToContent": "Skip to content",
|
|
41
41
|
"copyButton.copiedTooltip": "Copied to clipboard",
|
|
42
42
|
"copyButton.copyTooltip": "Click to copy",
|
|
43
|
+
"datePicker.day": "Day",
|
|
43
44
|
"datePicker.day.friday": "Friday",
|
|
44
45
|
"datePicker.day.monday": "Monday",
|
|
45
46
|
"datePicker.day.saturday": "Saturday",
|
|
@@ -47,6 +48,7 @@
|
|
|
47
48
|
"datePicker.day.thursday": "Thursday",
|
|
48
49
|
"datePicker.day.tuesday": "Tuesday",
|
|
49
50
|
"datePicker.day.wednesday": "Wednesday",
|
|
51
|
+
"datePicker.month": "Month",
|
|
50
52
|
"datePicker.month.april": "April",
|
|
51
53
|
"datePicker.month.august": "August",
|
|
52
54
|
"datePicker.month.december": "December",
|
|
@@ -61,6 +63,7 @@
|
|
|
61
63
|
"datePicker.month.october": "October",
|
|
62
64
|
"datePicker.month.previous": "Previous month",
|
|
63
65
|
"datePicker.month.september": "September",
|
|
66
|
+
"datePicker.year": "Year",
|
|
64
67
|
"error.generic": "An error occurred",
|
|
65
68
|
"error.iban.invalid": "This IBAN doesn't look right. Trying entering it again.",
|
|
66
69
|
"error.network.500": "Internal server error",
|
|
@@ -293,5 +296,7 @@
|
|
|
293
296
|
"transactionStatement.title.creditor": "Creditor information",
|
|
294
297
|
"transactionStatement.title.debtor": "Debtor information",
|
|
295
298
|
"transactionStatement.title.document": "Transaction confirmation",
|
|
296
|
-
"transactionStatement.title.information": "Information"
|
|
299
|
+
"transactionStatement.title.information": "Information",
|
|
300
|
+
"validation.invalidBirthDate": "Invalid birthdate",
|
|
301
|
+
"validation.birthdateCannotBeFuture": "Birthdate cannot be in the future"
|
|
297
302
|
}
|
package/src/utils/date.d.ts
CHANGED
|
@@ -1,2 +1,9 @@
|
|
|
1
1
|
export declare const decodeBirthDate: (value: string) => string;
|
|
2
2
|
export declare const encodeBirthDate: (value: string) => string;
|
|
3
|
+
export type ExtractedDate = {
|
|
4
|
+
day: string;
|
|
5
|
+
month: string;
|
|
6
|
+
year: string;
|
|
7
|
+
};
|
|
8
|
+
export declare const extractDate: (value: string) => ExtractedDate | undefined;
|
|
9
|
+
export declare const formatExtractedDate: (date: ExtractedDate) => string;
|
package/src/utils/date.js
CHANGED
|
@@ -1,9 +1,25 @@
|
|
|
1
1
|
import dayjs from "dayjs";
|
|
2
2
|
export const decodeBirthDate = (value) => {
|
|
3
|
-
const date = dayjs.utc(value, "YYYY-MM-DD");
|
|
3
|
+
const date = dayjs.utc(value, "YYYY-MM-DD", true);
|
|
4
4
|
return date.isValid() ? date.format("DD/MM/YYYY") : "";
|
|
5
5
|
};
|
|
6
6
|
export const encodeBirthDate = (value) => {
|
|
7
|
-
const date = dayjs.utc(value, "DD/MM/YYYY");
|
|
7
|
+
const date = dayjs.utc(value, "DD/MM/YYYY", true);
|
|
8
8
|
return date.isValid() ? date.format("YYYY-MM-DD") : "";
|
|
9
9
|
};
|
|
10
|
+
export const extractDate = (value) => {
|
|
11
|
+
const date = dayjs.utc(value, "YYYY-MM-DD", true);
|
|
12
|
+
if (date.isValid()) {
|
|
13
|
+
return {
|
|
14
|
+
day: date.format("DD"),
|
|
15
|
+
month: date.format("MM"),
|
|
16
|
+
year: date.format("YYYY"),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
export const formatExtractedDate = (date) => {
|
|
21
|
+
const day = date.day.trim().padStart(2, "0");
|
|
22
|
+
const month = date.month.trim().padStart(2, "0");
|
|
23
|
+
const year = date.year.trim().padStart(4, "0");
|
|
24
|
+
return `${year}-${month}-${day}`;
|
|
25
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Validator } from "@swan-io/use-form";
|
|
2
|
+
import { ExtractedDate } from "./date";
|
|
2
3
|
import { AccountCountry } from "./templateTranslations";
|
|
3
4
|
export declare const isValidVatNumber: (maybeVat: string) => boolean;
|
|
4
5
|
export declare const isValidEmail: (maybeEmail: string) => boolean;
|
|
@@ -9,3 +10,4 @@ export declare const validateIndividualTaxNumber: (accountCountry: AccountCountr
|
|
|
9
10
|
export declare const validateCompanyTaxNumber: (accountCountry: AccountCountry) => Validator<string | undefined>;
|
|
10
11
|
export { printFormat as printIbanFormat } from "iban";
|
|
11
12
|
export declare const validateIban: (iban: string) => string | undefined;
|
|
13
|
+
export declare const validateBirthdate: (value: ExtractedDate) => string | undefined;
|
package/src/utils/validation.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { noop } from "@swan-io/lake/src/utils/function";
|
|
2
|
+
import dayjs from "dayjs";
|
|
2
3
|
import { isValid as isValidIban } from "iban";
|
|
3
4
|
import { match } from "ts-pattern";
|
|
5
|
+
import { formatExtractedDate } from "./date";
|
|
4
6
|
import { t } from "./i18n";
|
|
5
7
|
const EMAIL_REGEX = /^[A-Z0-9_+.-]*[A-Z0-9_+-]@([A-Z0-9][A-Z0-9-]*\.)+[A-Z]{2,}$/i;
|
|
6
8
|
const VAT_NUMBER_REGEX = /^((AT)?U[0-9]{8}|(BE)?0[0-9]{9}|(BG)?[0-9]{9,10}|(CY)?[0-9]{8}L|(CZ)?[0-9]{8,10}|(DE)?[0-9]{9}|(DK)?[0-9]{8}|(EE)?[0-9]{9}|(EL|GR)?[0-9]{9}|(ES)?[0-9A-Z][0-9]{7}[0-9A-Z]|(FI)?[0-9]{8}|(FR)?[0-9A-Z]{2}[0-9]{9}|(GB)?([0-9]{9}([0-9]{3})?|[A-Z]{2}[0-9]{3})|(HU)?[0-9]{8}|(IE)?[0-9]S[0-9]{5}L|(IT)?[0-9]{11}|(LT)?([0-9]{9}|[0-9]{12})|(LU)?[0-9]{8}|(LV)?[0-9]{11}|(MT)?[0-9]{8}|(NL)?[0-9]{9}B[0-9]{2}|(PL)?[0-9]{10}|(PT)?[0-9]{9}|(RO)?[0-9]{2,10}|(SE)?[0-9]{12}|(SI)?[0-9]{8}|(SK)?[0-9]{10})$/;
|
|
@@ -102,3 +104,14 @@ export const validateIban = (iban) => {
|
|
|
102
104
|
return t("error.iban.invalid");
|
|
103
105
|
}
|
|
104
106
|
};
|
|
107
|
+
export const validateBirthdate = (value) => {
|
|
108
|
+
const date = dayjs.utc(formatExtractedDate(value), "YYYY-MM-DD", true);
|
|
109
|
+
const isBirthdateOver150years = date.isBefore(dayjs.utc().subtract(150, "years"));
|
|
110
|
+
const isBirthdateWithin4years = date.isAfter(dayjs.utc().subtract(4, "years"));
|
|
111
|
+
if (!date.isValid() || isBirthdateOver150years || isBirthdateWithin4years) {
|
|
112
|
+
return t("validation.invalidBirthDate");
|
|
113
|
+
}
|
|
114
|
+
if (date.isAfter(dayjs.utc().add(1, "day"))) {
|
|
115
|
+
return t("validation.birthdateCannotBeFuture");
|
|
116
|
+
}
|
|
117
|
+
};
|