@swan-io/shared-business 13.9.2 → 13.10.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 +3 -3
- package/src/components/InputPhoneNumber.d.ts +27 -0
- package/src/components/InputPhoneNumber.js +47 -0
- package/src/components/PhoneCountryPicker.d.ts +14 -0
- package/src/components/PhoneCountryPicker.js +110 -0
- package/src/constants/countries.d.ts +1 -0
- package/src/constants/countries.js +2 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@swan-io/shared-business",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.10.0",
|
|
4
4
|
"engines": {
|
|
5
5
|
"node": ">22.12.0"
|
|
6
6
|
},
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
],
|
|
26
26
|
"license": "MIT",
|
|
27
27
|
"peerDependencies": {
|
|
28
|
-
"@swan-io/lake": "13.
|
|
28
|
+
"@swan-io/lake": "13.10.0"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@formatjs/intl": "^4.1.2",
|
|
@@ -50,6 +50,6 @@
|
|
|
50
50
|
"@types/react-dom": "^19.2.3",
|
|
51
51
|
"@types/react-native": "^0.72.8",
|
|
52
52
|
"type-fest": "^5.4.3",
|
|
53
|
-
"@swan-io/lake": "13.
|
|
53
|
+
"@swan-io/lake": "13.10.0"
|
|
54
54
|
}
|
|
55
55
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Ref } from "react";
|
|
2
|
+
import { Country, CountryCCA3 } from "../constants/countries";
|
|
3
|
+
export type InputPhoneNumberRef = {
|
|
4
|
+
focus: () => void;
|
|
5
|
+
blur: () => void;
|
|
6
|
+
};
|
|
7
|
+
type Props = {
|
|
8
|
+
ref?: Ref<InputPhoneNumberRef>;
|
|
9
|
+
id?: string;
|
|
10
|
+
country: Country;
|
|
11
|
+
value: string;
|
|
12
|
+
countries?: CountryCCA3[];
|
|
13
|
+
error?: string;
|
|
14
|
+
help?: string;
|
|
15
|
+
valid?: boolean;
|
|
16
|
+
autofocus?: boolean;
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
readOnly?: boolean;
|
|
19
|
+
countryAriaLabel?: string;
|
|
20
|
+
hideErrors?: boolean;
|
|
21
|
+
onCountryChange: (country: Country) => void;
|
|
22
|
+
onChangeText: (text: string) => void;
|
|
23
|
+
onSubmitEditing?: () => void;
|
|
24
|
+
onBlur?: () => void;
|
|
25
|
+
};
|
|
26
|
+
export declare const InputPhoneNumber: ({ ref, id, country, value, countries, error, help, valid, autofocus, disabled, readOnly, countryAriaLabel, hideErrors, onCountryChange, onChangeText, onSubmitEditing, onBlur, }: Props) => import("react/jsx-runtime").JSX.Element;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Box } from "@swan-io/lake/src/components/Box";
|
|
3
|
+
import { LakeText } from "@swan-io/lake/src/components/LakeText";
|
|
4
|
+
import { LakeTextInput } from "@swan-io/lake/src/components/LakeTextInput";
|
|
5
|
+
import { Space } from "@swan-io/lake/src/components/Space";
|
|
6
|
+
import { colors } from "@swan-io/lake/src/constants/design";
|
|
7
|
+
import { isNotNullishOrEmpty } from "@swan-io/lake/src/utils/nullish";
|
|
8
|
+
import { useEffect, useImperativeHandle, useRef } from "react";
|
|
9
|
+
import { StyleSheet } from "react-native";
|
|
10
|
+
import { phoneCountries } from "../constants/countries";
|
|
11
|
+
import { PhoneCountryPicker } from "./PhoneCountryPicker";
|
|
12
|
+
const styles = StyleSheet.create({
|
|
13
|
+
picker: {
|
|
14
|
+
borderTopRightRadius: 0,
|
|
15
|
+
borderBottomRightRadius: 0,
|
|
16
|
+
borderRightWidth: 0,
|
|
17
|
+
},
|
|
18
|
+
pickerDisabled: {
|
|
19
|
+
borderRightWidth: 1,
|
|
20
|
+
borderRightColor: colors.gray[100],
|
|
21
|
+
},
|
|
22
|
+
input: {
|
|
23
|
+
borderTopLeftRadius: 0,
|
|
24
|
+
borderBottomLeftRadius: 0,
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
export const InputPhoneNumber = ({ ref, id, country, value, countries, error, help, valid = false, autofocus = false, disabled = false, readOnly = false, countryAriaLabel, hideErrors = false, onCountryChange, onChangeText, onSubmitEditing, onBlur, }) => {
|
|
28
|
+
var _a;
|
|
29
|
+
const inputRef = useRef(null);
|
|
30
|
+
const handleChangeText = (text) => {
|
|
31
|
+
const clean = text.replace(/[^ +0-9-()]/g, "");
|
|
32
|
+
if (text === clean) {
|
|
33
|
+
onChangeText(text);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
if (autofocus && inputRef.current) {
|
|
38
|
+
setTimeout(() => { var _a; return (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus(); }, 250);
|
|
39
|
+
}
|
|
40
|
+
}, [autofocus]);
|
|
41
|
+
useImperativeHandle(ref, () => ({
|
|
42
|
+
focus: () => { var _a; return (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus(); },
|
|
43
|
+
blur: () => { var _a; return (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.blur(); },
|
|
44
|
+
}), []);
|
|
45
|
+
const pickerStyle = [styles.picker, (disabled || readOnly) && styles.pickerDisabled];
|
|
46
|
+
return (_jsxs(_Fragment, { children: [_jsxs(Box, { direction: "row", children: [_jsx(PhoneCountryPicker, { value: country, countries: countries !== null && countries !== void 0 ? countries : phoneCountries, onValueChange: onCountryChange, disabled: disabled, readOnly: readOnly, error: error, ariaLabel: countryAriaLabel, style: pickerStyle }), _jsx(LakeTextInput, { id: id, ref: inputRef, autoComplete: "tel", inputMode: "tel", rows: 1, hideErrors: true, value: value, valid: valid, error: error, disabled: disabled, readOnly: readOnly, onChangeText: handleChangeText, onSubmitEditing: onSubmitEditing, onBlur: onBlur, style: styles.input })] }), !hideErrors && (_jsxs(_Fragment, { children: [_jsx(Space, { height: 4 }), _jsx(LakeText, { variant: "smallRegular", color: isNotNullishOrEmpty(error) ? colors.negative[500] : colors.gray[500], children: (_a = error !== null && error !== void 0 ? error : help) !== null && _a !== void 0 ? _a : " " })] }))] }));
|
|
47
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { StyleProp, ViewStyle } from "react-native";
|
|
2
|
+
import { Country, CountryCCA3 } from "../constants/countries";
|
|
3
|
+
type Props = {
|
|
4
|
+
value: Country;
|
|
5
|
+
onValueChange: (country: Country) => void;
|
|
6
|
+
countries: CountryCCA3[];
|
|
7
|
+
style?: StyleProp<ViewStyle>;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
readOnly?: boolean;
|
|
10
|
+
error?: string;
|
|
11
|
+
ariaLabel?: string;
|
|
12
|
+
};
|
|
13
|
+
export declare const PhoneCountryPicker: ({ value, onValueChange, countries, style, disabled, readOnly, error, ariaLabel, }: Props) => import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Box } from "@swan-io/lake/src/components/Box";
|
|
3
|
+
import { FlatList } from "@swan-io/lake/src/components/FlatList";
|
|
4
|
+
import { Icon } from "@swan-io/lake/src/components/Icon";
|
|
5
|
+
import { LakeText } from "@swan-io/lake/src/components/LakeText";
|
|
6
|
+
import { Popover } from "@swan-io/lake/src/components/Popover";
|
|
7
|
+
import { Pressable } from "@swan-io/lake/src/components/Pressable";
|
|
8
|
+
import { Separator } from "@swan-io/lake/src/components/Separator";
|
|
9
|
+
import { Space } from "@swan-io/lake/src/components/Space";
|
|
10
|
+
import { colors, radii, spacings, texts } from "@swan-io/lake/src/constants/design";
|
|
11
|
+
import { useDebounce } from "@swan-io/lake/src/hooks/useDebounce";
|
|
12
|
+
import { useDisclosure } from "@swan-io/lake/src/hooks/useDisclosure";
|
|
13
|
+
import { isNotNullish, isNotNullishOrEmpty } from "@swan-io/lake/src/utils/nullish";
|
|
14
|
+
import { deburr } from "@swan-io/lake/src/utils/string";
|
|
15
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
16
|
+
import { StyleSheet, TextInput, View } from "react-native";
|
|
17
|
+
import { getCountryByCCA3 } from "../constants/countries";
|
|
18
|
+
import { Flag } from "./Flag";
|
|
19
|
+
const ROW_HEIGHT = 48;
|
|
20
|
+
const styles = StyleSheet.create({
|
|
21
|
+
trigger: {
|
|
22
|
+
alignItems: "center",
|
|
23
|
+
backgroundColor: colors.gray[50],
|
|
24
|
+
borderColor: colors.gray[100],
|
|
25
|
+
borderTopLeftRadius: radii[4],
|
|
26
|
+
borderTopRightRadius: radii[4],
|
|
27
|
+
borderBottomLeftRadius: radii[4],
|
|
28
|
+
borderBottomRightRadius: radii[4],
|
|
29
|
+
borderWidth: 1,
|
|
30
|
+
flexDirection: "row",
|
|
31
|
+
height: 40,
|
|
32
|
+
justifyContent: "center",
|
|
33
|
+
outlineStyle: "none",
|
|
34
|
+
paddingLeft: spacings[16],
|
|
35
|
+
paddingRight: spacings[12],
|
|
36
|
+
transitionDuration: "150ms",
|
|
37
|
+
transitionProperty: "background-color",
|
|
38
|
+
},
|
|
39
|
+
triggerPressed: { backgroundColor: colors.gray[100] },
|
|
40
|
+
triggerErrored: { borderColor: colors.negative[500] },
|
|
41
|
+
triggerDisabled: { borderColor: colors.gray[50] },
|
|
42
|
+
list: { height: 230 },
|
|
43
|
+
searchIcon: { position: "absolute", left: 16 },
|
|
44
|
+
searchInput: {
|
|
45
|
+
...texts.regular,
|
|
46
|
+
color: colors.gray[700],
|
|
47
|
+
flexGrow: 1,
|
|
48
|
+
height: ROW_HEIGHT,
|
|
49
|
+
outlineStyle: "none",
|
|
50
|
+
paddingLeft: 44,
|
|
51
|
+
},
|
|
52
|
+
row: {
|
|
53
|
+
flexDirection: "row",
|
|
54
|
+
alignItems: "center",
|
|
55
|
+
height: ROW_HEIGHT,
|
|
56
|
+
paddingHorizontal: 16,
|
|
57
|
+
transitionProperty: "background-color",
|
|
58
|
+
transitionDuration: "150ms",
|
|
59
|
+
},
|
|
60
|
+
rowHovered: { backgroundColor: colors.gray[50] },
|
|
61
|
+
rowPressed: { backgroundColor: colors.gray[100] },
|
|
62
|
+
rowName: { flexGrow: 1 },
|
|
63
|
+
});
|
|
64
|
+
export const PhoneCountryPicker = ({ value, onValueChange, countries, style, disabled = false, readOnly = false, error, ariaLabel = "Select country", }) => {
|
|
65
|
+
const referenceRef = useRef(null);
|
|
66
|
+
const inputRef = useRef(null);
|
|
67
|
+
const [visible, { open, close }] = useDisclosure(false);
|
|
68
|
+
const allowedCountries = useMemo(() => countries
|
|
69
|
+
.filter((cca3, index, array) => array.indexOf(cca3) === index)
|
|
70
|
+
.map(cca3 => getCountryByCCA3(cca3)), [countries]);
|
|
71
|
+
const [filteredCountries, setFilteredCountries] = useState(allowedCountries);
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
setFilteredCountries(allowedCountries);
|
|
74
|
+
}, [allowedCountries]);
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
if (visible) {
|
|
77
|
+
setTimeout(() => { var _a; return (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus(); }, 250);
|
|
78
|
+
}
|
|
79
|
+
}, [visible]);
|
|
80
|
+
const handleOnChangeText = useDebounce(useCallback((text) => {
|
|
81
|
+
const cleaned = deburr(text.trim().toLowerCase());
|
|
82
|
+
if (cleaned === "") {
|
|
83
|
+
setFilteredCountries(allowedCountries);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const filtered = allowedCountries.filter(country => country.deburr.includes(cleaned) ||
|
|
87
|
+
country.idd.includes(cleaned) ||
|
|
88
|
+
`+${country.idd}`.includes(cleaned));
|
|
89
|
+
setFilteredCountries(filtered);
|
|
90
|
+
}, [allowedCountries]), 200);
|
|
91
|
+
return (_jsxs(_Fragment, { children: [_jsxs(Pressable, { ref: referenceRef, role: "button", "aria-label": ariaLabel, disabled: visible || disabled || readOnly, onPress: open, style: ({ pressed }) => [
|
|
92
|
+
styles.trigger,
|
|
93
|
+
style,
|
|
94
|
+
!visible && pressed && styles.triggerPressed,
|
|
95
|
+
(disabled || readOnly) && styles.triggerDisabled,
|
|
96
|
+
isNotNullishOrEmpty(error) && styles.triggerErrored,
|
|
97
|
+
], children: [_jsx(Flag, { code: value.cca2, width: 16 }), _jsx(Space, { width: 8 }), _jsxs(LakeText, { color: colors.gray[600], numberOfLines: 1, userSelect: "none", variant: "smallSemibold", children: ["+", value.idd] }), _jsx(Space, { width: 8 }), _jsx(Icon, { name: "chevron-down-filled", color: colors.gray[600], size: 16 })] }), _jsx(Popover, { referenceRef: referenceRef, visible: visible, onDismiss: close, matchReferenceWidth: false, children: _jsxs(View, { style: styles.list, children: [_jsxs(Box, { direction: "row", alignItems: "center", children: [_jsx(Icon, { name: "search-filled", color: colors.gray[300], size: 18, style: styles.searchIcon }), _jsx(TextInput, { ref: inputRef, inputMode: "search", style: styles.searchInput, onChangeText: handleOnChangeText, onSubmitEditing: () => {
|
|
98
|
+
if (isNotNullish(filteredCountries[0])) {
|
|
99
|
+
onValueChange(filteredCountries[0]);
|
|
100
|
+
close();
|
|
101
|
+
}
|
|
102
|
+
} })] }), _jsx(Separator, {}), _jsx(FlatList, { data: filteredCountries, ItemSeparatorComponent: _jsx(Separator, {}), keyExtractor: item => item.uid, renderItem: ({ item }) => (_jsxs(Pressable, { role: "button", "aria-label": item.name, style: ({ hovered, pressed }) => [
|
|
103
|
+
styles.row,
|
|
104
|
+
hovered && styles.rowHovered,
|
|
105
|
+
pressed && styles.rowPressed,
|
|
106
|
+
], onPress: () => {
|
|
107
|
+
onValueChange(item);
|
|
108
|
+
close();
|
|
109
|
+
}, children: [_jsx(Flag, { code: item.cca2, width: 16 }), _jsx(Space, { width: 12 }), _jsx(LakeText, { numberOfLines: 1, style: styles.rowName, userSelect: "none", variant: "smallRegular", children: item.name }), item.uid === value.uid && (_jsxs(_Fragment, { children: [_jsx(Space, { width: 12 }), _jsx(Icon, { name: "checkmark-filled", color: colors.positive[500], size: 16 })] })), _jsx(Space, { width: 12 }), _jsxs(LakeText, { userSelect: "none", variant: "smallRegular", children: ["+", item.idd] })] })) })] }) })] }));
|
|
110
|
+
};
|
|
@@ -1502,6 +1502,7 @@ export declare const france: {
|
|
|
1502
1502
|
uid: string;
|
|
1503
1503
|
flag: string;
|
|
1504
1504
|
};
|
|
1505
|
+
export declare const phoneCountries: ("ALA" | "ASM" | "AND" | "AGO" | "AIA" | "ATA" | "ATG" | "ARG" | "ABW" | "AUS" | "AZE" | "BHS" | "BGD" | "BRB" | "BEL" | "BLZ" | "BEN" | "BMU" | "BOL" | "BES" | "BIH" | "BWA" | "BVT" | "BRA" | "VGB" | "BFA" | "BDI" | "CPV" | "CMR" | "CAN" | "CYM" | "CZE" | "CHL" | "CXR" | "CCK" | "COL" | "COK" | "CRI" | "CIV" | "CUB" | "CUW" | "DNK" | "DEU" | "DJI" | "DMA" | "ECU" | "EST" | "IRL" | "SLV" | "ESP" | "FLK" | "FJI" | "FRO" | "FRA" | "GAB" | "GMB" | "GHA" | "GIB" | "GRD" | "GLP" | "GUM" | "GTM" | "GGY" | "GNB" | "GNQ" | "GIN" | "GUY" | "GUF" | "HTI" | "HMD" | "HND" | "HRV" | "IDN" | "ISL" | "IMN" | "ITA" | "JAM" | "JEY" | "GRL" | "KHM" | "KAZ" | "KEN" | "KIR" | "CAF" | "COM" | "REU" | "LVA" | "LSO" | "LBR" | "LIE" | "LTU" | "LUX" | "MDG" | "HUN" | "MHL" | "MWI" | "MYS" | "MDV" | "MLI" | "MLT" | "MTQ" | "MUS" | "MYT" | "MEX" | "FSM" | "MOZ" | "MDA" | "MCO" | "MSR" | "NAM" | "NLD" | "BRN" | "NZL" | "NIC" | "NER" | "NGA" | "NIU" | "NFK" | "NOR" | "MNP" | "NCL" | "AUT" | "UZB" | "PAK" | "PLW" | "PAN" | "PNG" | "PRY" | "PER" | "PHL" | "PCN" | "POL" | "PYF" | "PRT" | "PRI" | "DOM" | "XKX" | "COD" | "COG" | "ROU" | "RWA" | "KNA" | "LCA" | "VCT" | "BLM" | "MAF" | "SPM" | "WSM" | "SMR" | "STP" | "CHE" | "SEN" | "SYC" | "ALB" | "SLE" | "SGP" | "SVN" | "SVK" | "SLB" | "SOM" | "ZAF" | "SGS" | "SSD" | "LKA" | "FIN" | "SUR" | "SWE" | "SWZ" | "TZA" | "TCD" | "ATF" | "TLS" | "TGO" | "TON" | "TTO" | "TUR" | "TKM" | "TCA" | "TUV" | "UGA" | "GBR" | "USA" | "UMI" | "VIR" | "URY" | "VUT" | "VAT" | "VEN" | "VNM" | "WLF" | "ZMB" | "ZWE" | "BTN" | "GRC" | "CYP" | "BLR" | "BGR" | "KGZ" | "MKD" | "MNG" | "RUS" | "SRB" | "TJK" | "UKR" | "MNE" | "ARM" | "ISR" | "AFG" | "JOR" | "DZA" | "SDN" | "ESH" | "IRQ" | "SAU" | "KWT" | "MAR" | "YEM" | "IRN" | "TUN" | "ARE" | "SYR" | "OMN" | "PSE" | "QAT" | "LBN" | "EGY" | "MRT" | "NPL" | "IND" | "THA" | "LAO" | "MMR" | "GEO" | "ETH" | "ERI" | "BHR" | "LBY" | "CHN" | "JPN" | "MAC" | "TWN" | "HKG" | "KOR" | "PRK")[];
|
|
1505
1506
|
export declare const individualCountries: ("BEL" | "CZE" | "DNK" | "DEU" | "EST" | "IRL" | "ESP" | "FRA" | "HRV" | "ISL" | "ITA" | "LVA" | "LIE" | "LTU" | "LUX" | "HUN" | "MLT" | "NLD" | "NOR" | "AUT" | "POL" | "PRT" | "ROU" | "SVN" | "SVK" | "FIN" | "SWE" | "GRC" | "CYP" | "BGR")[];
|
|
1506
1507
|
export type IndividualCountryCCA3 = (typeof individualCountries)[number];
|
|
1507
1508
|
export declare const companyCountries: ("BEL" | "CZE" | "DNK" | "DEU" | "EST" | "IRL" | "ESP" | "FRA" | "HRV" | "ISL" | "ITA" | "LVA" | "LIE" | "LTU" | "LUX" | "HUN" | "MLT" | "NLD" | "NOR" | "AUT" | "POL" | "PRT" | "ROU" | "SVN" | "SVK" | "FIN" | "SWE" | "GRC" | "CYP" | "BGR")[];
|
|
@@ -1768,6 +1768,8 @@ export const getCCA3forCCA2 = (cca2) => countriesByCCA2[cca2].cca3;
|
|
|
1768
1768
|
export const companyWithUboCountries = ["BEL", "DEU", "FRA", "ITA", "LTU", "LUX", "NLD"];
|
|
1769
1769
|
export const allCountries = countries.map(country => country.cca3);
|
|
1770
1770
|
export const france = countriesByCCA3["FRA"];
|
|
1771
|
+
const blockedPhoneCountries = ["CUB", "SYR", "IRN", "PRK", "RUS"];
|
|
1772
|
+
export const phoneCountries = allCountries.filter(cca3 => !blockedPhoneCountries.includes(cca3));
|
|
1771
1773
|
export const individualCountries = [
|
|
1772
1774
|
"AUT",
|
|
1773
1775
|
"BEL",
|