@korsolutions/ui 0.0.53 → 0.0.54
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/babel.config.js +1 -11
- package/dist/module/components/alert/components/alert-body.js.map +1 -1
- package/dist/module/components/alert/components/alert-description.js.map +1 -1
- package/dist/module/components/alert/components/alert-icon.js.map +1 -1
- package/dist/module/components/alert/components/alert-root.js.map +1 -1
- package/dist/module/components/alert/components/alert-title.js.map +1 -1
- package/dist/module/components/alert/index.js.map +1 -1
- package/dist/module/components/alert/variants/default.js.map +1 -1
- package/dist/module/components/alert/variants/destructive.js.map +1 -1
- package/dist/module/components/alert/variants/index.js.map +1 -1
- package/dist/module/components/alert-dialog/async-alert-dialog.js.map +1 -1
- package/dist/module/components/alert-dialog/components/alert-dialog-action.js.map +1 -1
- package/dist/module/components/alert-dialog/components/alert-dialog-cancel.js.map +1 -1
- package/dist/module/components/alert-dialog/components/alert-dialog-content.js.map +1 -1
- package/dist/module/components/alert-dialog/components/alert-dialog-description.js.map +1 -1
- package/dist/module/components/alert-dialog/components/alert-dialog-footer.js.map +1 -1
- package/dist/module/components/alert-dialog/components/alert-dialog-overlay.js.map +1 -1
- package/dist/module/components/alert-dialog/components/alert-dialog-portal.js.map +1 -1
- package/dist/module/components/alert-dialog/components/alert-dialog-root.js.map +1 -1
- package/dist/module/components/alert-dialog/components/alert-dialog-title.js.map +1 -1
- package/dist/module/components/alert-dialog/components/alert-dialog-trigger.js.map +1 -1
- package/dist/module/components/alert-dialog/index.js.map +1 -1
- package/dist/module/components/alert-dialog/variants/default.js.map +1 -1
- package/dist/module/components/alert-dialog/variants/index.js.map +1 -1
- package/dist/module/components/avatar/components/avatar-fallback.js.map +1 -1
- package/dist/module/components/avatar/components/avatar-image.js.map +1 -1
- package/dist/module/components/avatar/components/avatar-root.js.map +1 -1
- package/dist/module/components/avatar/index.js.map +1 -1
- package/dist/module/components/avatar/variants/default.js.map +1 -1
- package/dist/module/components/avatar/variants/index.js.map +1 -1
- package/dist/module/components/badge/components/badge-label.js.map +1 -1
- package/dist/module/components/badge/components/badge-root.js.map +1 -1
- package/dist/module/components/badge/index.js.map +1 -1
- package/dist/module/components/badge/variants/default.js.map +1 -1
- package/dist/module/components/badge/variants/index.js.map +1 -1
- package/dist/module/components/badge/variants/secondary.js.map +1 -1
- package/dist/module/components/button/components/button-label.js.map +1 -1
- package/dist/module/components/button/components/button-root.js.map +1 -1
- package/dist/module/components/button/components/button-spinner.js.map +1 -1
- package/dist/module/components/button/index.js.map +1 -1
- package/dist/module/components/button/variants/default.js.map +1 -1
- package/dist/module/components/button/variants/index.js.map +1 -1
- package/dist/module/components/button/variants/secondary.js.map +1 -1
- package/dist/module/components/calendar/components/calendar-day.js.map +1 -1
- package/dist/module/components/calendar/components/calendar-header.js.map +1 -1
- package/dist/module/components/calendar/components/calendar-nav-button.js.map +1 -1
- package/dist/module/components/calendar/components/calendar-root.js.map +1 -1
- package/dist/module/components/calendar/components/calendar-title.js.map +1 -1
- package/dist/module/components/calendar/components/calendar-week-labels.js.map +1 -1
- package/dist/module/components/calendar/components/calendar-weeks.js.map +1 -1
- package/dist/module/components/calendar/index.js.map +1 -1
- package/dist/module/components/calendar/variants/default.js.map +1 -1
- package/dist/module/components/calendar/variants/index.js.map +1 -1
- package/dist/module/components/card/card-body.js.map +1 -1
- package/dist/module/components/card/card-footer.js.map +1 -1
- package/dist/module/components/card/card-header.js.map +1 -1
- package/dist/module/components/card/card-root.js.map +1 -1
- package/dist/module/components/card/card-title.js.map +1 -1
- package/dist/module/components/card/index.js.map +1 -1
- package/dist/module/components/card/variants/default.js.map +1 -1
- package/dist/module/components/card/variants/index.js.map +1 -1
- package/dist/module/components/checkbox/components/checkbox-content.js.map +1 -1
- package/dist/module/components/checkbox/components/checkbox-description.js.map +1 -1
- package/dist/module/components/checkbox/components/checkbox-indicator.js.map +1 -1
- package/dist/module/components/checkbox/components/checkbox-root.js.map +1 -1
- package/dist/module/components/checkbox/components/checkbox-title.js.map +1 -1
- package/dist/module/components/checkbox/index.js.map +1 -1
- package/dist/module/components/checkbox/variants/default.js.map +1 -1
- package/dist/module/components/checkbox/variants/index.js.map +1 -1
- package/dist/module/components/checkbox/variants/outlined.js.map +1 -1
- package/dist/module/components/empty/components/empty-description.js.map +1 -1
- package/dist/module/components/empty/components/empty-media.js.map +1 -1
- package/dist/module/components/empty/components/empty-root.js.map +1 -1
- package/dist/module/components/empty/components/empty-title.js.map +1 -1
- package/dist/module/components/empty/index.js.map +1 -1
- package/dist/module/components/empty/variants/default.js.map +1 -1
- package/dist/module/components/empty/variants/index.js.map +1 -1
- package/dist/module/components/field/components/field-description.js.map +1 -1
- package/dist/module/components/field/components/field-error.js.map +1 -1
- package/dist/module/components/field/components/field-label.js.map +1 -1
- package/dist/module/components/field/components/field-root.js.map +1 -1
- package/dist/module/components/field/index.js.map +1 -1
- package/dist/module/components/field/variants/default.js.map +1 -1
- package/dist/module/components/field/variants/index.js.map +1 -1
- package/dist/module/components/icon/icon.js.map +1 -1
- package/dist/module/components/icon/index.js.map +1 -1
- package/dist/module/components/icon/variants/default.js.map +1 -1
- package/dist/module/components/icon/variants/index.js.map +1 -1
- package/dist/module/components/index.js.map +1 -1
- package/dist/module/components/input/index.js.map +1 -1
- package/dist/module/components/input/input.js.map +1 -1
- package/dist/module/components/input/numeric-input.js.map +1 -1
- package/dist/module/components/input/variants/default.js.map +1 -1
- package/dist/module/components/input/variants/index.js.map +1 -1
- package/dist/module/components/input/variants/secondary.js.map +1 -1
- package/dist/module/components/link/index.js.map +1 -1
- package/dist/module/components/link/link.js.map +1 -1
- package/dist/module/components/link/variants/default.js.map +1 -1
- package/dist/module/components/link/variants/index.js.map +1 -1
- package/dist/module/components/list/index.js.map +1 -1
- package/dist/module/components/menu/components/menu-content.js.map +1 -1
- package/dist/module/components/menu/components/menu-item.js.map +1 -1
- package/dist/module/components/menu/components/menu-overlay.js.map +1 -1
- package/dist/module/components/menu/components/menu-portal.js.map +1 -1
- package/dist/module/components/menu/components/menu-root.js.map +1 -1
- package/dist/module/components/menu/components/menu-trigger.js.map +1 -1
- package/dist/module/components/menu/index.js.map +1 -1
- package/dist/module/components/menu/variants/default.js.map +1 -1
- package/dist/module/components/menu/variants/index.js.map +1 -1
- package/dist/module/components/phone-input/components/country-picker.js +101 -0
- package/dist/module/components/phone-input/components/country-picker.js.map +1 -0
- package/dist/module/components/phone-input/components/phone-input-root.js +57 -0
- package/dist/module/components/phone-input/components/phone-input-root.js.map +1 -0
- package/dist/module/components/phone-input/components/phone-input.js +31 -0
- package/dist/module/components/phone-input/components/phone-input.js.map +1 -0
- package/dist/module/components/phone-input/context.js +12 -0
- package/dist/module/components/phone-input/context.js.map +1 -0
- package/dist/module/components/phone-input/index.js +8 -1
- package/dist/module/components/phone-input/index.js.map +1 -1
- package/dist/module/components/phone-input/variants/default.js +1 -1
- package/dist/module/components/phone-input/variants/default.js.map +1 -1
- package/dist/module/components/phone-input/variants/index.js.map +1 -1
- package/dist/module/components/popover/components/popover-close.js.map +1 -1
- package/dist/module/components/popover/components/popover-content.js.map +1 -1
- package/dist/module/components/popover/components/popover-overlay.js.map +1 -1
- package/dist/module/components/popover/components/popover-portal.js.map +1 -1
- package/dist/module/components/popover/components/popover-root.js.map +1 -1
- package/dist/module/components/popover/components/popover-trigger.js.map +1 -1
- package/dist/module/components/popover/index.js.map +1 -1
- package/dist/module/components/popover/variants/default.js.map +1 -1
- package/dist/module/components/popover/variants/index.js.map +1 -1
- package/dist/module/components/popover/variants/unstyled.js.map +1 -1
- package/dist/module/components/portal/index.js.map +1 -1
- package/dist/module/components/portal/portal.js.map +1 -1
- package/dist/module/components/portal/portal.web.js.map +1 -1
- package/dist/module/components/progress/components/progress-indicator.js.map +1 -1
- package/dist/module/components/progress/components/progress-root.js.map +1 -1
- package/dist/module/components/progress/index.js.map +1 -1
- package/dist/module/components/progress/variants/default.js.map +1 -1
- package/dist/module/components/progress/variants/index.js.map +1 -1
- package/dist/module/components/scroll-bar/index.js.map +1 -1
- package/dist/module/components/scroll-bar/scroll-bar.js.map +1 -1
- package/dist/module/components/select/components/select-content.js.map +1 -1
- package/dist/module/components/select/components/select-option.js.map +1 -1
- package/dist/module/components/select/components/select-overlay.js.map +1 -1
- package/dist/module/components/select/components/select-portal.js.map +1 -1
- package/dist/module/components/select/components/select-root.js.map +1 -1
- package/dist/module/components/select/components/select-trigger.js.map +1 -1
- package/dist/module/components/select/index.js.map +1 -1
- package/dist/module/components/select/variants/default.js.map +1 -1
- package/dist/module/components/select/variants/index.js.map +1 -1
- package/dist/module/components/tabs/components/tabs-list.js.map +1 -1
- package/dist/module/components/tabs/components/tabs-root.js.map +1 -1
- package/dist/module/components/tabs/components/tabs-trigger-text.js.map +1 -1
- package/dist/module/components/tabs/components/tabs-trigger.js.map +1 -1
- package/dist/module/components/tabs/index.js.map +1 -1
- package/dist/module/components/tabs/variants/default.js.map +1 -1
- package/dist/module/components/tabs/variants/index.js.map +1 -1
- package/dist/module/components/tabs/variants/line.js.map +1 -1
- package/dist/module/components/textarea/index.js.map +1 -1
- package/dist/module/components/textarea/textarea.js.map +1 -1
- package/dist/module/components/textarea/variants/default.js.map +1 -1
- package/dist/module/components/textarea/variants/index.js.map +1 -1
- package/dist/module/components/toast/components/toast-description.js.map +1 -1
- package/dist/module/components/toast/components/toast-icon.js.map +1 -1
- package/dist/module/components/toast/components/toast-root.js.map +1 -1
- package/dist/module/components/toast/components/toast-title.js.map +1 -1
- package/dist/module/components/toast/index.js.map +1 -1
- package/dist/module/components/toast/manager.js.map +1 -1
- package/dist/module/components/toast/variants/danger.js.map +1 -1
- package/dist/module/components/toast/variants/default.js.map +1 -1
- package/dist/module/components/toast/variants/index.js.map +1 -1
- package/dist/module/components/toast/variants/success.js.map +1 -1
- package/dist/module/components/typography/index.js.map +1 -1
- package/dist/module/components/typography/typography.js.map +1 -1
- package/dist/module/components/typography/variants/body-lg.js.map +1 -1
- package/dist/module/components/typography/variants/body-md.js.map +1 -1
- package/dist/module/components/typography/variants/body-sm.js.map +1 -1
- package/dist/module/components/typography/variants/heading-lg.js.map +1 -1
- package/dist/module/components/typography/variants/heading-md.js.map +1 -1
- package/dist/module/components/typography/variants/heading-sm.js.map +1 -1
- package/dist/module/components/typography/variants/index.js.map +1 -1
- package/dist/module/data/countries.js.map +1 -1
- package/dist/module/hooks/index.js.map +1 -1
- package/dist/module/hooks/use-phone-mask.js.map +1 -1
- package/dist/module/index.js.map +1 -1
- package/dist/module/safe-area/index.js.map +1 -1
- package/dist/module/safe-area/provider.js.map +1 -1
- package/dist/module/themes/adapters/index.js.map +1 -1
- package/dist/module/themes/adapters/react-navigation.js.map +1 -1
- package/dist/module/themes/default/index.js.map +1 -1
- package/dist/module/themes/index.js.map +1 -1
- package/dist/module/themes/provider.js.map +1 -1
- package/dist/module/utils/use-themed-styles.js.map +1 -1
- package/dist/typescript/babel.config.d.ts +1 -6
- package/dist/typescript/babel.config.d.ts.map +1 -1
- package/dist/typescript/src/components/phone-input/components/country-picker.d.ts +3 -0
- package/dist/typescript/src/components/phone-input/components/country-picker.d.ts.map +1 -0
- package/dist/typescript/src/components/phone-input/components/phone-input-root.d.ts +17 -0
- package/dist/typescript/src/components/phone-input/components/phone-input-root.d.ts.map +1 -0
- package/dist/typescript/src/components/phone-input/components/phone-input.d.ts +8 -0
- package/dist/typescript/src/components/phone-input/components/phone-input.d.ts.map +1 -0
- package/dist/typescript/src/components/phone-input/context.d.ts +17 -0
- package/dist/typescript/src/components/phone-input/context.d.ts.map +1 -0
- package/dist/typescript/src/components/phone-input/index.d.ts +8 -1
- package/dist/typescript/src/components/phone-input/index.d.ts.map +1 -1
- package/dist/typescript/src/data/countries.d.ts +2 -1
- package/dist/typescript/src/data/countries.d.ts.map +1 -1
- package/dist/typescript/src/hooks/use-phone-mask.d.ts +2 -2
- package/dist/typescript/src/hooks/use-phone-mask.d.ts.map +1 -1
- package/package.json +2 -7
- package/src/components/phone-input/components/country-picker.tsx +155 -0
- package/src/components/phone-input/components/phone-input-root.tsx +85 -0
- package/src/components/phone-input/components/phone-input.tsx +43 -0
- package/src/components/phone-input/context.ts +32 -0
- package/src/components/phone-input/index.ts +10 -1
- package/src/components/phone-input/variants/default.tsx +1 -1
- package/src/data/countries.ts +606 -68
- package/src/hooks/use-phone-mask.ts +2 -2
- package/dist/module/components/phone-input/phone-input.js +0 -165
- package/dist/module/components/phone-input/phone-input.js.map +0 -1
- package/dist/typescript/src/components/phone-input/phone-input.d.ts +0 -16
- package/dist/typescript/src/components/phone-input/phone-input.d.ts.map +0 -1
- package/scripts/build.sh +0 -2
- package/src/components/phone-input/phone-input.tsx +0 -220
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import React, { useState } from "react";
|
|
2
|
+
import {
|
|
3
|
+
Keyboard,
|
|
4
|
+
type LayoutRectangle,
|
|
5
|
+
Platform,
|
|
6
|
+
Pressable,
|
|
7
|
+
ScrollView,
|
|
8
|
+
StyleSheet,
|
|
9
|
+
Text,
|
|
10
|
+
TextInput,
|
|
11
|
+
View,
|
|
12
|
+
} from "react-native";
|
|
13
|
+
import type { CountryData } from "../../../data/countries";
|
|
14
|
+
import {
|
|
15
|
+
DEFAULT_LAYOUT,
|
|
16
|
+
DEFAULT_POSITION,
|
|
17
|
+
type LayoutPosition,
|
|
18
|
+
useRelativePosition,
|
|
19
|
+
} from "../../../hooks/use-relative-position";
|
|
20
|
+
import { measureLayoutPosition } from "../../../utils/normalize-layout";
|
|
21
|
+
import { Portal } from "../../portal";
|
|
22
|
+
import { usePhoneInput } from "../context";
|
|
23
|
+
|
|
24
|
+
export function CountryPicker() {
|
|
25
|
+
const { styles, state, phoneMask, isDisabled, triggerRef } = usePhoneInput();
|
|
26
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
27
|
+
const [searchQuery, setSearchQuery] = useState("");
|
|
28
|
+
const [triggerPosition, setTriggerPosition] =
|
|
29
|
+
useState<LayoutPosition>(DEFAULT_POSITION);
|
|
30
|
+
const [contentLayout, setContentLayout] =
|
|
31
|
+
useState<LayoutRectangle>(DEFAULT_LAYOUT);
|
|
32
|
+
|
|
33
|
+
const filteredCountries = searchQuery
|
|
34
|
+
? phoneMask.countries.filter((c) =>
|
|
35
|
+
c.name.toLowerCase().includes(searchQuery.toLowerCase()),
|
|
36
|
+
)
|
|
37
|
+
: phoneMask.countries;
|
|
38
|
+
|
|
39
|
+
const onOpen = () => {
|
|
40
|
+
if (isDisabled) return;
|
|
41
|
+
if (Platform.OS !== "web") {
|
|
42
|
+
Keyboard.dismiss();
|
|
43
|
+
}
|
|
44
|
+
measureLayoutPosition(triggerRef.current, (layout) => {
|
|
45
|
+
setTriggerPosition(layout);
|
|
46
|
+
setSearchQuery("");
|
|
47
|
+
setIsOpen(true);
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const onClose = () => {
|
|
52
|
+
setIsOpen(false);
|
|
53
|
+
setSearchQuery("");
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const onSelectCountry = (country: CountryData) => {
|
|
57
|
+
phoneMask.setCountry(country.code);
|
|
58
|
+
onClose();
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const positionStyle = useRelativePosition({
|
|
62
|
+
align: "start",
|
|
63
|
+
triggerPosition,
|
|
64
|
+
contentLayout,
|
|
65
|
+
alignOffset: 0,
|
|
66
|
+
preferredSide: "bottom",
|
|
67
|
+
sideOffset: 4,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const countryButtonStyles = StyleSheet.flatten([
|
|
71
|
+
styles.countryButton?.default,
|
|
72
|
+
styles.countryButton?.[state],
|
|
73
|
+
]);
|
|
74
|
+
const countryButtonTextStyles = StyleSheet.flatten([
|
|
75
|
+
styles.countryButtonText?.default,
|
|
76
|
+
styles.countryButtonText?.[state],
|
|
77
|
+
]);
|
|
78
|
+
|
|
79
|
+
const overlayStyles = StyleSheet.flatten([
|
|
80
|
+
styles.pickerOverlay?.default,
|
|
81
|
+
styles.pickerOverlay?.[state],
|
|
82
|
+
]);
|
|
83
|
+
const contentStyles = StyleSheet.flatten([
|
|
84
|
+
styles.pickerContent?.default,
|
|
85
|
+
styles.pickerContent?.[state],
|
|
86
|
+
]);
|
|
87
|
+
const searchStyles = StyleSheet.flatten([
|
|
88
|
+
styles.pickerSearch?.default,
|
|
89
|
+
styles.pickerSearch?.[state],
|
|
90
|
+
]);
|
|
91
|
+
|
|
92
|
+
return (
|
|
93
|
+
<>
|
|
94
|
+
<Pressable
|
|
95
|
+
onPress={onOpen}
|
|
96
|
+
disabled={isDisabled}
|
|
97
|
+
style={countryButtonStyles}
|
|
98
|
+
>
|
|
99
|
+
<Text style={countryButtonTextStyles}>
|
|
100
|
+
{phoneMask.country.flag} +{phoneMask.country.dialCode}
|
|
101
|
+
</Text>
|
|
102
|
+
</Pressable>
|
|
103
|
+
{isOpen && (
|
|
104
|
+
<Portal name="phone-input-country-picker">
|
|
105
|
+
<Pressable
|
|
106
|
+
onPress={onClose}
|
|
107
|
+
style={[StyleSheet.absoluteFill, overlayStyles]}
|
|
108
|
+
/>
|
|
109
|
+
<View
|
|
110
|
+
style={[
|
|
111
|
+
positionStyle,
|
|
112
|
+
contentStyles,
|
|
113
|
+
{ width: triggerPosition.width },
|
|
114
|
+
]}
|
|
115
|
+
onLayout={(e) => setContentLayout(e.nativeEvent.layout)}
|
|
116
|
+
pointerEvents="box-none"
|
|
117
|
+
>
|
|
118
|
+
<TextInput
|
|
119
|
+
value={searchQuery}
|
|
120
|
+
onChangeText={setSearchQuery}
|
|
121
|
+
placeholder="Search countries..."
|
|
122
|
+
autoFocus
|
|
123
|
+
style={searchStyles}
|
|
124
|
+
/>
|
|
125
|
+
<ScrollView keyboardShouldPersistTaps="handled">
|
|
126
|
+
{filteredCountries.map((country) => {
|
|
127
|
+
const isSelected = country.code === phoneMask.country.code;
|
|
128
|
+
const optionStyles = StyleSheet.flatten([
|
|
129
|
+
styles.pickerOption?.default,
|
|
130
|
+
isSelected && styles.pickerOption?.selected,
|
|
131
|
+
]);
|
|
132
|
+
const optionTextStyles = StyleSheet.flatten([
|
|
133
|
+
styles.pickerOptionText?.default,
|
|
134
|
+
isSelected && styles.pickerOptionText?.selected,
|
|
135
|
+
]);
|
|
136
|
+
|
|
137
|
+
return (
|
|
138
|
+
<Pressable
|
|
139
|
+
key={country.code}
|
|
140
|
+
onPress={() => onSelectCountry(country)}
|
|
141
|
+
style={optionStyles}
|
|
142
|
+
>
|
|
143
|
+
<Text style={optionTextStyles}>
|
|
144
|
+
{country.flag} {country.name} +{country.dialCode}
|
|
145
|
+
</Text>
|
|
146
|
+
</Pressable>
|
|
147
|
+
);
|
|
148
|
+
})}
|
|
149
|
+
</ScrollView>
|
|
150
|
+
</View>
|
|
151
|
+
</Portal>
|
|
152
|
+
)}
|
|
153
|
+
</>
|
|
154
|
+
);
|
|
155
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import React, { useMemo, useRef, useState } from "react";
|
|
2
|
+
import { StyleSheet, View, type StyleProp, type ViewStyle } from "react-native";
|
|
3
|
+
import { COUNTRIES, type CountryCode } from "../../../data/countries";
|
|
4
|
+
import { usePhoneMask } from "../../../hooks/use-phone-mask";
|
|
5
|
+
import type { ViewRef } from "../../../types/element.types";
|
|
6
|
+
import { PhoneInputContext } from "../context";
|
|
7
|
+
import type { PhoneInputState } from "../types";
|
|
8
|
+
import { PhoneInputVariants } from "../variants";
|
|
9
|
+
|
|
10
|
+
export interface PhoneInputRootProps {
|
|
11
|
+
children?: React.ReactNode;
|
|
12
|
+
value?: string;
|
|
13
|
+
onChange?: (value: string) => void;
|
|
14
|
+
defaultCountry?: string;
|
|
15
|
+
countryCodes?: CountryCode[];
|
|
16
|
+
isDisabled?: boolean;
|
|
17
|
+
variant?: keyof typeof PhoneInputVariants;
|
|
18
|
+
placeholder?: string;
|
|
19
|
+
style?: StyleProp<ViewStyle>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const calculateState = (
|
|
23
|
+
isDisabled: boolean,
|
|
24
|
+
isFocused: boolean,
|
|
25
|
+
): PhoneInputState => {
|
|
26
|
+
if (isDisabled) return "disabled";
|
|
27
|
+
if (isFocused) return "focused";
|
|
28
|
+
return "default";
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export function PhoneInputRoot({
|
|
32
|
+
children,
|
|
33
|
+
value,
|
|
34
|
+
onChange,
|
|
35
|
+
defaultCountry = "US",
|
|
36
|
+
countryCodes = ["US"],
|
|
37
|
+
isDisabled = false,
|
|
38
|
+
variant = "default",
|
|
39
|
+
style,
|
|
40
|
+
}: PhoneInputRootProps) {
|
|
41
|
+
const variantStyles = PhoneInputVariants[variant]();
|
|
42
|
+
const [isFocused, setIsFocused] = useState(false);
|
|
43
|
+
const triggerRef = useRef<ViewRef>(null);
|
|
44
|
+
|
|
45
|
+
const state = calculateState(isDisabled, isFocused);
|
|
46
|
+
|
|
47
|
+
const countries = COUNTRIES.filter((c) => countryCodes.includes(c.code));
|
|
48
|
+
|
|
49
|
+
const phoneMask = usePhoneMask({
|
|
50
|
+
value,
|
|
51
|
+
defaultCountry,
|
|
52
|
+
onChange,
|
|
53
|
+
countries,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const rootStyles = StyleSheet.flatten([
|
|
57
|
+
variantStyles.root?.default,
|
|
58
|
+
variantStyles.root?.[state],
|
|
59
|
+
style,
|
|
60
|
+
]);
|
|
61
|
+
|
|
62
|
+
const context = useMemo(
|
|
63
|
+
() =>
|
|
64
|
+
({
|
|
65
|
+
value,
|
|
66
|
+
onChange,
|
|
67
|
+
isFocused,
|
|
68
|
+
setIsFocused,
|
|
69
|
+
state,
|
|
70
|
+
phoneMask,
|
|
71
|
+
isDisabled,
|
|
72
|
+
styles: variantStyles,
|
|
73
|
+
triggerRef,
|
|
74
|
+
}) satisfies PhoneInputContext,
|
|
75
|
+
[variantStyles, state, phoneMask, isDisabled, value, isFocused],
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<PhoneInputContext.Provider value={context}>
|
|
80
|
+
<View ref={triggerRef} style={rootStyles}>
|
|
81
|
+
{children}
|
|
82
|
+
</View>
|
|
83
|
+
</PhoneInputContext.Provider>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import {
|
|
3
|
+
type StyleProp,
|
|
4
|
+
StyleSheet,
|
|
5
|
+
TextInput,
|
|
6
|
+
type TextStyle,
|
|
7
|
+
} from "react-native";
|
|
8
|
+
import { usePhoneInput } from "../context";
|
|
9
|
+
|
|
10
|
+
export interface PhoneInputProps {
|
|
11
|
+
placeholder?: string;
|
|
12
|
+
style?: StyleProp<TextStyle>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function PhoneInput({
|
|
16
|
+
placeholder = "Enter phone number",
|
|
17
|
+
style,
|
|
18
|
+
}: PhoneInputProps) {
|
|
19
|
+
const { styles, state, setIsFocused, phoneMask, isDisabled } =
|
|
20
|
+
usePhoneInput();
|
|
21
|
+
|
|
22
|
+
const inputStyles = StyleSheet.flatten([
|
|
23
|
+
styles.input?.default,
|
|
24
|
+
styles.input?.[state],
|
|
25
|
+
style,
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<TextInput
|
|
30
|
+
value={phoneMask.displayValue}
|
|
31
|
+
onChangeText={phoneMask.onChangeText}
|
|
32
|
+
keyboardType={phoneMask.keyboardType}
|
|
33
|
+
placeholder={placeholder}
|
|
34
|
+
placeholderTextColor={
|
|
35
|
+
StyleSheet.flatten(styles.countryButtonText?.disabled)?.color
|
|
36
|
+
}
|
|
37
|
+
readOnly={isDisabled}
|
|
38
|
+
onFocus={() => setIsFocused(true)}
|
|
39
|
+
onBlur={() => setIsFocused(false)}
|
|
40
|
+
style={inputStyles}
|
|
41
|
+
/>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { createContext, useContext } from "react";
|
|
2
|
+
import type { PhoneMask } from "../../hooks";
|
|
3
|
+
import type { ViewRef } from "../../types/element.types";
|
|
4
|
+
import type { PhoneInputState, PhoneInputStyles } from "./types";
|
|
5
|
+
|
|
6
|
+
export interface PhoneInputContext {
|
|
7
|
+
value: string | undefined;
|
|
8
|
+
onChange: ((value: string) => void) | undefined;
|
|
9
|
+
|
|
10
|
+
isFocused: boolean;
|
|
11
|
+
setIsFocused: (focused: boolean) => void;
|
|
12
|
+
isDisabled: boolean;
|
|
13
|
+
|
|
14
|
+
triggerRef: React.RefObject<ViewRef | null>;
|
|
15
|
+
|
|
16
|
+
phoneMask: PhoneMask;
|
|
17
|
+
|
|
18
|
+
state: PhoneInputState;
|
|
19
|
+
styles: PhoneInputStyles;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const PhoneInputContext = createContext<PhoneInputContext | undefined>(
|
|
23
|
+
undefined,
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
export const usePhoneInput = () => {
|
|
27
|
+
const context = useContext(PhoneInputContext);
|
|
28
|
+
if (!context) {
|
|
29
|
+
throw new Error("usePhoneInput must be used within a PhoneInputRoot");
|
|
30
|
+
}
|
|
31
|
+
return context;
|
|
32
|
+
};
|
|
@@ -1,2 +1,11 @@
|
|
|
1
|
-
export { PhoneInput, type PhoneInputProps } from "./phone-input";
|
|
2
1
|
export type { PhoneInputState, PhoneInputStyles } from "./types";
|
|
2
|
+
|
|
3
|
+
import { CountryPicker } from "./components/country-picker";
|
|
4
|
+
import { PhoneInput as PhoneInputComponent } from "./components/phone-input";
|
|
5
|
+
import { PhoneInputRoot } from "./components/phone-input-root";
|
|
6
|
+
|
|
7
|
+
export const PhoneInput = {
|
|
8
|
+
Root: PhoneInputRoot,
|
|
9
|
+
CountryPicker,
|
|
10
|
+
Input: PhoneInputComponent,
|
|
11
|
+
};
|