@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swan-io/shared-business",
3
- "version": "13.9.2",
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.9.2"
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.9.2"
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",