@superleapai/flow-ui 1.0.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 +65 -0
- package/LICENSE +21 -0
- package/README.md +451 -0
- package/components/alert.js +282 -0
- package/components/avatar.js +195 -0
- package/components/badge.js +135 -0
- package/components/button.js +201 -0
- package/components/checkbox.js +254 -0
- package/components/currency.js +227 -0
- package/components/date-time-picker/date-time-picker-utils.js +253 -0
- package/components/date-time-picker/date-time-picker.js +532 -0
- package/components/duration/duration-constants.js +46 -0
- package/components/duration/duration-utils.js +164 -0
- package/components/duration/duration.js +448 -0
- package/components/enum-multiselect.js +869 -0
- package/components/enum-select.js +831 -0
- package/components/enumeration.js +213 -0
- package/components/file-input.js +533 -0
- package/components/icon.js +200 -0
- package/components/input.js +259 -0
- package/components/label.js +111 -0
- package/components/multiselect.js +351 -0
- package/components/phone-input/phone-input.js +392 -0
- package/components/phone-input/phone-utils.js +157 -0
- package/components/popover.js +240 -0
- package/components/radio-group.js +435 -0
- package/components/record-multiselect.js +956 -0
- package/components/record-select.js +930 -0
- package/components/select.js +544 -0
- package/components/spinner.js +136 -0
- package/components/table.js +335 -0
- package/components/textarea.js +114 -0
- package/components/time-picker.js +357 -0
- package/components/toast.js +343 -0
- package/core/flow.js +1729 -0
- package/core/superleapClient.js +146 -0
- package/dist/output.css +2 -0
- package/index.d.ts +458 -0
- package/index.js +253 -0
- package/package.json +70 -0
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phone Input Component
|
|
3
|
+
* International phone number input with country selector
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
(function (global) {
|
|
7
|
+
"use strict";
|
|
8
|
+
|
|
9
|
+
if (!global.PhoneInputUtils) {
|
|
10
|
+
console.error("[PhoneInput] PhoneInputUtils is required");
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const utils = global.PhoneInputUtils;
|
|
15
|
+
|
|
16
|
+
/** Resolve component by name (works after index.js captures and removes globals). */
|
|
17
|
+
function getComponent(name) {
|
|
18
|
+
if (typeof global.FlowUI !== "undefined" && typeof global.FlowUI._getComponent === "function") {
|
|
19
|
+
const c = global.FlowUI._getComponent(name);
|
|
20
|
+
if (c) return c;
|
|
21
|
+
}
|
|
22
|
+
return global[name];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Selector icon SVG
|
|
26
|
+
const SELECTOR_ICON =
|
|
27
|
+
'<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m7 15 5 5 5-5"/><path d="m7 9 5-5 5 5"/></svg>';
|
|
28
|
+
|
|
29
|
+
const SEARCH_ICON =
|
|
30
|
+
'<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>';
|
|
31
|
+
|
|
32
|
+
const CHECK_ICON =
|
|
33
|
+
'<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>';
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Create a phone input component
|
|
37
|
+
* @param {Object} config
|
|
38
|
+
* @param {string} [config.variant] - 'default' | 'error' | 'warning' | 'borderless' | 'inline'
|
|
39
|
+
* @param {string} [config.inputSize] - 'default' | 'large' | 'small'
|
|
40
|
+
* @param {string} [config.defaultCountryCode] - Default country code (e.g., 'US', 'IN')
|
|
41
|
+
* @param {string} [config.defaultPhoneNumber] - Default phone number in format "callingCode-nationalNumber"
|
|
42
|
+
* @param {boolean} [config.disabled]
|
|
43
|
+
* @param {boolean} [config.disableCountrySelect] - Disable country selector
|
|
44
|
+
* @param {boolean} [config.hideCountrySelect] - Hide country selector completely
|
|
45
|
+
* @param {string} [config.placeholder] - Custom placeholder (overrides auto-generated)
|
|
46
|
+
* @param {string} [config.className] - Extra class for wrapper
|
|
47
|
+
* @param {string} [config.countryCodeClassName] - Extra class for country selector
|
|
48
|
+
* @param {Function} [config.onCountryChange] - Called when country changes (country)
|
|
49
|
+
* @param {Function} [config.onPhoneNumberChange] - Called when phone number changes (fullValue)
|
|
50
|
+
* @param {Function} [config.onChange] - Called when either changes (fullValue, country)
|
|
51
|
+
* @returns {HTMLElement} Phone input wrapper
|
|
52
|
+
*/
|
|
53
|
+
function create(config = {}) {
|
|
54
|
+
const variant = config.variant || "default";
|
|
55
|
+
const inputSize = config.inputSize || "default";
|
|
56
|
+
const disabled = !!config.disabled;
|
|
57
|
+
const disableCountrySelect = !!config.disableCountrySelect;
|
|
58
|
+
const hideCountrySelect = !!config.hideCountrySelect;
|
|
59
|
+
|
|
60
|
+
const options = utils.getCountriesOptions();
|
|
61
|
+
|
|
62
|
+
// Determine initial country
|
|
63
|
+
let initialCountry = null;
|
|
64
|
+
if (config.defaultPhoneNumber) {
|
|
65
|
+
const parsed = utils.parsePhoneNumber(config.defaultPhoneNumber);
|
|
66
|
+
if (parsed.callingCode) {
|
|
67
|
+
const countryCode = utils.getCountryCodeFromCallingCode(
|
|
68
|
+
parsed.callingCode
|
|
69
|
+
);
|
|
70
|
+
if (countryCode) {
|
|
71
|
+
initialCountry = options.find((opt) => opt.value === countryCode);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (!initialCountry && config.defaultCountryCode) {
|
|
76
|
+
initialCountry = options.find(
|
|
77
|
+
(opt) => opt.value === config.defaultCountryCode
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
// Default to India if no country specified
|
|
81
|
+
if (!initialCountry) {
|
|
82
|
+
initialCountry = options.find((opt) => opt.value === "IN") || options[0];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
let currentCountry = initialCountry;
|
|
86
|
+
let phoneNumber = "";
|
|
87
|
+
|
|
88
|
+
// Parse initial phone number
|
|
89
|
+
if (config.defaultPhoneNumber) {
|
|
90
|
+
const parsed = utils.parsePhoneNumber(config.defaultPhoneNumber);
|
|
91
|
+
phoneNumber = parsed.nationalNumber;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Main wrapper
|
|
95
|
+
const wrapper = document.createElement("div");
|
|
96
|
+
wrapper.className = "flex w-full flex-col gap-4";
|
|
97
|
+
|
|
98
|
+
// Row container
|
|
99
|
+
const row = document.createElement("div");
|
|
100
|
+
row.className = "flex items-center gap-2";
|
|
101
|
+
|
|
102
|
+
// Country selector (combobox-style with popover)
|
|
103
|
+
let countrySelector = null;
|
|
104
|
+
if (!hideCountrySelect) {
|
|
105
|
+
countrySelector = createCountrySelector({
|
|
106
|
+
variant,
|
|
107
|
+
inputSize,
|
|
108
|
+
disabled: disabled || disableCountrySelect,
|
|
109
|
+
options,
|
|
110
|
+
value: currentCountry,
|
|
111
|
+
getValue: () => currentCountry,
|
|
112
|
+
className: config.countryCodeClassName,
|
|
113
|
+
onChange: (country) => {
|
|
114
|
+
currentCountry = country;
|
|
115
|
+
updatePlaceholder();
|
|
116
|
+
if (config.onCountryChange) config.onCountryChange(country);
|
|
117
|
+
const fullValue = utils.formatPhoneNumber(
|
|
118
|
+
country.indicatif,
|
|
119
|
+
phoneNumber
|
|
120
|
+
);
|
|
121
|
+
if (config.onPhoneNumberChange)
|
|
122
|
+
config.onPhoneNumberChange(fullValue);
|
|
123
|
+
if (config.onChange) config.onChange(fullValue, country);
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
row.appendChild(countrySelector);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Phone number input
|
|
130
|
+
const InputComponent = getComponent("InputComponent");
|
|
131
|
+
if (!InputComponent || typeof InputComponent.create !== "function") {
|
|
132
|
+
console.error("[PhoneInput] InputComponent is required");
|
|
133
|
+
return wrapper;
|
|
134
|
+
}
|
|
135
|
+
const phoneInputWrapper = InputComponent.create({
|
|
136
|
+
variant,
|
|
137
|
+
inputSize,
|
|
138
|
+
type: "tel",
|
|
139
|
+
placeholder:
|
|
140
|
+
config.placeholder || utils.getExampleFormat(currentCountry.value),
|
|
141
|
+
value: phoneNumber,
|
|
142
|
+
disabled,
|
|
143
|
+
className: config.className || "",
|
|
144
|
+
onInput: (e) => {
|
|
145
|
+
const input = e.target;
|
|
146
|
+
const digitsOnly = (input.value || "").replace(/\D/g, "");
|
|
147
|
+
if (input.value !== digitsOnly) {
|
|
148
|
+
input.value = digitsOnly;
|
|
149
|
+
}
|
|
150
|
+
phoneNumber = digitsOnly;
|
|
151
|
+
const fullValue = utils.formatPhoneNumber(
|
|
152
|
+
currentCountry.indicatif,
|
|
153
|
+
phoneNumber
|
|
154
|
+
);
|
|
155
|
+
if (config.onPhoneNumberChange) config.onPhoneNumberChange(fullValue);
|
|
156
|
+
if (config.onChange) config.onChange(fullValue, currentCountry);
|
|
157
|
+
},
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
row.appendChild(phoneInputWrapper);
|
|
161
|
+
wrapper.appendChild(row);
|
|
162
|
+
|
|
163
|
+
function updatePlaceholder() {
|
|
164
|
+
const input = phoneInputWrapper.getInput();
|
|
165
|
+
if (input) {
|
|
166
|
+
input.placeholder =
|
|
167
|
+
config.placeholder || utils.getExampleFormat(currentCountry.value);
|
|
168
|
+
}
|
|
169
|
+
// Update prefix
|
|
170
|
+
if (!hideCountrySelect) {
|
|
171
|
+
const prefix = phoneInputWrapper.querySelector(
|
|
172
|
+
"span.text-typography-quaternary-text"
|
|
173
|
+
);
|
|
174
|
+
if (prefix) {
|
|
175
|
+
prefix.textContent = `+${currentCountry.indicatif}`;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Public API
|
|
181
|
+
wrapper.getValue = function () {
|
|
182
|
+
return utils.formatPhoneNumber(currentCountry.indicatif, phoneNumber);
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
wrapper.setValue = function (value) {
|
|
186
|
+
const parsed = utils.parsePhoneNumber(value);
|
|
187
|
+
if (parsed.callingCode) {
|
|
188
|
+
const countryCode = utils.getCountryCodeFromCallingCode(
|
|
189
|
+
parsed.callingCode
|
|
190
|
+
);
|
|
191
|
+
if (countryCode) {
|
|
192
|
+
const country = options.find((opt) => opt.value === countryCode);
|
|
193
|
+
if (country) {
|
|
194
|
+
currentCountry = country;
|
|
195
|
+
if (countrySelector) {
|
|
196
|
+
// Update country selector display
|
|
197
|
+
const triggerInput = countrySelector.querySelector("input");
|
|
198
|
+
if (triggerInput) {
|
|
199
|
+
triggerInput.value = utils.isoToEmoji(country.value) + " " + country.indicatif;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
phoneNumber = parsed.nationalNumber;
|
|
206
|
+
phoneInputWrapper.setValue(phoneNumber);
|
|
207
|
+
updatePlaceholder();
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
wrapper.getCountry = function () {
|
|
211
|
+
return currentCountry;
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
wrapper.setCountry = function (countryCode) {
|
|
215
|
+
const country = options.find((opt) => opt.value === countryCode);
|
|
216
|
+
if (country) {
|
|
217
|
+
currentCountry = country;
|
|
218
|
+
if (countrySelector) {
|
|
219
|
+
const triggerInput = countrySelector.querySelector("input");
|
|
220
|
+
if (triggerInput) {
|
|
221
|
+
triggerInput.value = utils.isoToEmoji(country.value) + " " + country.indicatif;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
updatePlaceholder();
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
wrapper.setDisabled = function (d) {
|
|
229
|
+
phoneInputWrapper.setDisabled(d);
|
|
230
|
+
if (countrySelector) {
|
|
231
|
+
const triggerInput = countrySelector.querySelector("input");
|
|
232
|
+
if (triggerInput) {
|
|
233
|
+
triggerInput.disabled = d;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
return wrapper;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Create country selector (combobox with popover)
|
|
243
|
+
*/
|
|
244
|
+
function createCountrySelector(config) {
|
|
245
|
+
const { variant, inputSize, disabled, options, value, getValue, className, onChange } =
|
|
246
|
+
config;
|
|
247
|
+
const getSelected = getValue && typeof getValue === "function" ? getValue : () => value;
|
|
248
|
+
|
|
249
|
+
const wrapper = document.createElement("div");
|
|
250
|
+
wrapper.className = "relative";
|
|
251
|
+
|
|
252
|
+
// Trigger (input with flag emoji)
|
|
253
|
+
const InputComponent = getComponent("InputComponent");
|
|
254
|
+
if (!InputComponent || typeof InputComponent.create !== "function") return wrapper;
|
|
255
|
+
const trigger = InputComponent.create({
|
|
256
|
+
variant,
|
|
257
|
+
inputSize,
|
|
258
|
+
type: "button",
|
|
259
|
+
value: utils.isoToEmoji(value.value) + " " + value.indicatif,
|
|
260
|
+
disabled,
|
|
261
|
+
readOnly: true,
|
|
262
|
+
showReadOnlyIcon: false,
|
|
263
|
+
className: "w-fit !gap-x-2 cursor-pointer " + (className || ""),
|
|
264
|
+
endIcon: SELECTOR_ICON,
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
const triggerInput = trigger.getInput();
|
|
268
|
+
triggerInput.style.cursor = "pointer";
|
|
269
|
+
triggerInput.style.caretColor = "transparent";
|
|
270
|
+
|
|
271
|
+
wrapper.appendChild(trigger);
|
|
272
|
+
|
|
273
|
+
// Create popover content (using Select component styles)
|
|
274
|
+
const popoverContent = document.createElement("div");
|
|
275
|
+
popoverContent.className = "w-[280px] max-w-full";
|
|
276
|
+
|
|
277
|
+
// Search input
|
|
278
|
+
const searchWrapper = document.createElement("div");
|
|
279
|
+
searchWrapper.className = "py-2 border-b-1/2 border-border-primary";
|
|
280
|
+
|
|
281
|
+
const searchInput = InputComponent.create({
|
|
282
|
+
variant: "borderless",
|
|
283
|
+
inputSize: "small",
|
|
284
|
+
type: "text",
|
|
285
|
+
placeholder: "Find your country...",
|
|
286
|
+
startIcon: SEARCH_ICON,
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
searchWrapper.appendChild(searchInput);
|
|
290
|
+
popoverContent.appendChild(searchWrapper);
|
|
291
|
+
|
|
292
|
+
// Options list (using Select component's option styles)
|
|
293
|
+
const listWrapper = document.createElement("div");
|
|
294
|
+
listWrapper.className =
|
|
295
|
+
"overflow-y-auto max-h-[200px] p-2 w-full rounded-4 bg-fill-quarternary-fill-white";
|
|
296
|
+
|
|
297
|
+
function renderOptions(filter = "") {
|
|
298
|
+
listWrapper.innerHTML = "";
|
|
299
|
+
const filtered = options.filter((opt) =>
|
|
300
|
+
opt.label.toLowerCase().includes(filter.toLowerCase())
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
if (filtered.length === 0) {
|
|
304
|
+
const empty = document.createElement("div");
|
|
305
|
+
empty.className =
|
|
306
|
+
"flex h-full min-h-[100px] w-full items-center justify-center p-4 !text-reg-13 text-typography-quaternary-text";
|
|
307
|
+
empty.textContent = "No country found.";
|
|
308
|
+
listWrapper.appendChild(empty);
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
filtered.forEach((opt) => {
|
|
313
|
+
const selected = getSelected();
|
|
314
|
+
const isSelected = selected && selected.value === opt.value;
|
|
315
|
+
|
|
316
|
+
// Using Select component's option classes
|
|
317
|
+
const item = document.createElement("div");
|
|
318
|
+
item.setAttribute("role", "option");
|
|
319
|
+
item.setAttribute("data-value", opt.value);
|
|
320
|
+
item.setAttribute("aria-selected", isSelected);
|
|
321
|
+
item.className = [
|
|
322
|
+
"relative flex w-full cursor-pointer select-none items-center gap-8 rounded-2 px-12 py-6 text-reg-13 outline-none first:rounded-t-4 last:rounded-b-4",
|
|
323
|
+
"hover:bg-fill-tertiary-fill-light-gray focus:bg-fill-tertiary-fill-light-gray",
|
|
324
|
+
isSelected ? "bg-primary-surface hover:!bg-primary-surface-hover" : ""
|
|
325
|
+
].filter(Boolean).join(" ");
|
|
326
|
+
|
|
327
|
+
const label = document.createElement("span");
|
|
328
|
+
label.className = "flex items-center gap-8 flex-1 truncate";
|
|
329
|
+
label.textContent = `${utils.isoToEmoji(opt.value)} ${opt.label}`;
|
|
330
|
+
item.appendChild(label);
|
|
331
|
+
|
|
332
|
+
if (isSelected) {
|
|
333
|
+
const check = document.createElement("span");
|
|
334
|
+
check.className = "ml-auto text-primary-base size-16";
|
|
335
|
+
check.innerHTML = CHECK_ICON;
|
|
336
|
+
item.appendChild(check);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
item.addEventListener("click", () => {
|
|
340
|
+
triggerInput.value = utils.isoToEmoji(opt.value) + " " + opt.indicatif;
|
|
341
|
+
onChange(opt);
|
|
342
|
+
popover.hide();
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
listWrapper.appendChild(item);
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
renderOptions();
|
|
350
|
+
popoverContent.appendChild(listWrapper);
|
|
351
|
+
|
|
352
|
+
// Search handler
|
|
353
|
+
const searchInputEl = searchInput.getInput();
|
|
354
|
+
searchInputEl.addEventListener("input", (e) => {
|
|
355
|
+
renderOptions(e.target.value);
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
// Create popover
|
|
359
|
+
const Popover = getComponent("Popover");
|
|
360
|
+
if (!Popover || typeof Popover.create !== "function") return wrapper;
|
|
361
|
+
const popover = Popover.create({
|
|
362
|
+
trigger,
|
|
363
|
+
content: popoverContent,
|
|
364
|
+
placement: "bottom",
|
|
365
|
+
align: "start",
|
|
366
|
+
closeOnClickOutside: true,
|
|
367
|
+
bodyClassName: "p-0",
|
|
368
|
+
panelClassName: "w-[280px]",
|
|
369
|
+
onOpen: () => {
|
|
370
|
+
searchInputEl.value = "";
|
|
371
|
+
renderOptions();
|
|
372
|
+
// Focus search input after popover opens
|
|
373
|
+
setTimeout(() => searchInputEl.focus(), 100);
|
|
374
|
+
},
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
if (!disabled) {
|
|
378
|
+
trigger.addEventListener("click", (e) => {
|
|
379
|
+
e.preventDefault();
|
|
380
|
+
e.stopPropagation();
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return wrapper;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
global.PhoneInput = {
|
|
388
|
+
create,
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
console.log("[PhoneInput] Component loaded successfully");
|
|
392
|
+
})(typeof window !== "undefined" ? window : this);
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phone Input Utilities
|
|
3
|
+
* Country data and helper functions for phone number formatting
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
(function (global) {
|
|
7
|
+
"use strict";
|
|
8
|
+
|
|
9
|
+
// Country codes with calling codes and names
|
|
10
|
+
const COUNTRIES_DATA = [
|
|
11
|
+
{ code: "US", name: "United States", callingCode: "1" },
|
|
12
|
+
{ code: "GB", name: "United Kingdom", callingCode: "44" },
|
|
13
|
+
{ code: "CA", name: "Canada", callingCode: "1" },
|
|
14
|
+
{ code: "AU", name: "Australia", callingCode: "61" },
|
|
15
|
+
{ code: "IN", name: "India", callingCode: "91" },
|
|
16
|
+
{ code: "DE", name: "Germany", callingCode: "49" },
|
|
17
|
+
{ code: "FR", name: "France", callingCode: "33" },
|
|
18
|
+
{ code: "IT", name: "Italy", callingCode: "39" },
|
|
19
|
+
{ code: "ES", name: "Spain", callingCode: "34" },
|
|
20
|
+
{ code: "BR", name: "Brazil", callingCode: "55" },
|
|
21
|
+
{ code: "MX", name: "Mexico", callingCode: "52" },
|
|
22
|
+
{ code: "AR", name: "Argentina", callingCode: "54" },
|
|
23
|
+
{ code: "CN", name: "China", callingCode: "86" },
|
|
24
|
+
{ code: "JP", name: "Japan", callingCode: "81" },
|
|
25
|
+
{ code: "KR", name: "South Korea", callingCode: "82" },
|
|
26
|
+
{ code: "RU", name: "Russia", callingCode: "7" },
|
|
27
|
+
{ code: "ZA", name: "South Africa", callingCode: "27" },
|
|
28
|
+
{ code: "NG", name: "Nigeria", callingCode: "234" },
|
|
29
|
+
{ code: "EG", name: "Egypt", callingCode: "20" },
|
|
30
|
+
{ code: "AE", name: "United Arab Emirates", callingCode: "971" },
|
|
31
|
+
{ code: "SA", name: "Saudi Arabia", callingCode: "966" },
|
|
32
|
+
{ code: "SG", name: "Singapore", callingCode: "65" },
|
|
33
|
+
{ code: "MY", name: "Malaysia", callingCode: "60" },
|
|
34
|
+
{ code: "TH", name: "Thailand", callingCode: "66" },
|
|
35
|
+
{ code: "ID", name: "Indonesia", callingCode: "62" },
|
|
36
|
+
{ code: "PH", name: "Philippines", callingCode: "63" },
|
|
37
|
+
{ code: "VN", name: "Vietnam", callingCode: "84" },
|
|
38
|
+
{ code: "PK", name: "Pakistan", callingCode: "92" },
|
|
39
|
+
{ code: "BD", name: "Bangladesh", callingCode: "880" },
|
|
40
|
+
{ code: "NZ", name: "New Zealand", callingCode: "64" },
|
|
41
|
+
{ code: "IE", name: "Ireland", callingCode: "353" },
|
|
42
|
+
{ code: "NL", name: "Netherlands", callingCode: "31" },
|
|
43
|
+
{ code: "BE", name: "Belgium", callingCode: "32" },
|
|
44
|
+
{ code: "CH", name: "Switzerland", callingCode: "41" },
|
|
45
|
+
{ code: "AT", name: "Austria", callingCode: "43" },
|
|
46
|
+
{ code: "SE", name: "Sweden", callingCode: "46" },
|
|
47
|
+
{ code: "NO", name: "Norway", callingCode: "47" },
|
|
48
|
+
{ code: "DK", name: "Denmark", callingCode: "45" },
|
|
49
|
+
{ code: "FI", name: "Finland", callingCode: "358" },
|
|
50
|
+
{ code: "PL", name: "Poland", callingCode: "48" },
|
|
51
|
+
{ code: "PT", name: "Portugal", callingCode: "351" },
|
|
52
|
+
{ code: "GR", name: "Greece", callingCode: "30" },
|
|
53
|
+
{ code: "TR", name: "Turkey", callingCode: "90" },
|
|
54
|
+
{ code: "IL", name: "Israel", callingCode: "972" },
|
|
55
|
+
{ code: "CL", name: "Chile", callingCode: "56" },
|
|
56
|
+
{ code: "CO", name: "Colombia", callingCode: "57" },
|
|
57
|
+
{ code: "PE", name: "Peru", callingCode: "51" },
|
|
58
|
+
{ code: "VE", name: "Venezuela", callingCode: "58" },
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
// ISO 3166-1 alpha-2 to emoji flag
|
|
62
|
+
function isoToEmoji(code) {
|
|
63
|
+
if (!code || code.length !== 2) return "";
|
|
64
|
+
const codePoints = code
|
|
65
|
+
.toUpperCase()
|
|
66
|
+
.split("")
|
|
67
|
+
.map((char) => 127397 + char.charCodeAt(0));
|
|
68
|
+
return String.fromCodePoint(...codePoints);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Get all countries as options for select/combobox
|
|
72
|
+
function getCountriesOptions() {
|
|
73
|
+
return COUNTRIES_DATA.map((country) => ({
|
|
74
|
+
value: country.code,
|
|
75
|
+
label: country.name,
|
|
76
|
+
indicatif: country.callingCode,
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Find country by code
|
|
81
|
+
function getCountryByCode(code) {
|
|
82
|
+
return COUNTRIES_DATA.find((c) => c.code === code);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Find country by calling code
|
|
86
|
+
function getCountryCodeFromCallingCode(callingCode) {
|
|
87
|
+
const normalized = callingCode.replace(/^\+/, "");
|
|
88
|
+
const country = COUNTRIES_DATA.find((c) => c.callingCode === normalized);
|
|
89
|
+
return country ? country.code : null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Replace numbers with zeros for placeholder (e.g., "+1 (555) 123-4567" -> "+1 (000) 000-0000")
|
|
93
|
+
function replaceNumbersWithZeros(str) {
|
|
94
|
+
return str.replace(/\d/g, "0");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Get example phone number format for a country
|
|
98
|
+
function getExampleFormat(countryCode) {
|
|
99
|
+
const formats = {
|
|
100
|
+
US: "(000) 000-0000",
|
|
101
|
+
GB: "00000 000000",
|
|
102
|
+
IN: "00000 00000",
|
|
103
|
+
CA: "(000) 000-0000",
|
|
104
|
+
AU: "0000 000 000",
|
|
105
|
+
DE: "0000 0000000",
|
|
106
|
+
FR: "00 00 00 00 00",
|
|
107
|
+
IT: "000 000 0000",
|
|
108
|
+
ES: "000 00 00 00",
|
|
109
|
+
BR: "(00) 00000-0000",
|
|
110
|
+
MX: "00 0000 0000",
|
|
111
|
+
CN: "000 0000 0000",
|
|
112
|
+
JP: "00-0000-0000",
|
|
113
|
+
KR: "00-0000-0000",
|
|
114
|
+
RU: "(000) 000-00-00",
|
|
115
|
+
};
|
|
116
|
+
return formats[countryCode] || "000000000000";
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Parse phone number from format "callingCode-nationalNumber"
|
|
120
|
+
function parsePhoneNumber(value) {
|
|
121
|
+
if (!value) return { callingCode: "", nationalNumber: "" };
|
|
122
|
+
const parts = value.split("-");
|
|
123
|
+
if (parts.length === 2) {
|
|
124
|
+
return {
|
|
125
|
+
callingCode: parts[0].replace(/^\+/, ""),
|
|
126
|
+
nationalNumber: parts[1],
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
// Try to extract calling code from beginning
|
|
130
|
+
const match = value.match(/^\+?(\d{1,4})/);
|
|
131
|
+
if (match) {
|
|
132
|
+
return {
|
|
133
|
+
callingCode: match[1],
|
|
134
|
+
nationalNumber: value.substring(match[0].length),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
return { callingCode: "", nationalNumber: value };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Format phone number as "callingCode-nationalNumber"
|
|
141
|
+
function formatPhoneNumber(callingCode, nationalNumber) {
|
|
142
|
+
if (!nationalNumber) return callingCode || "";
|
|
143
|
+
return callingCode ? `${callingCode}-${nationalNumber}` : nationalNumber;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
global.PhoneInputUtils = {
|
|
147
|
+
COUNTRIES_DATA,
|
|
148
|
+
isoToEmoji,
|
|
149
|
+
getCountriesOptions,
|
|
150
|
+
getCountryByCode,
|
|
151
|
+
getCountryCodeFromCallingCode,
|
|
152
|
+
replaceNumbersWithZeros,
|
|
153
|
+
getExampleFormat,
|
|
154
|
+
parsePhoneNumber,
|
|
155
|
+
formatPhoneNumber,
|
|
156
|
+
};
|
|
157
|
+
})(typeof window !== "undefined" ? window : this);
|