torch-glare 2.4.1 → 2.4.2
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/apps/lib/components/Button.tsx +1 -1
- package/apps/lib/components/Card.tsx +47 -20
- package/apps/lib/components/ColorPicker.tsx +441 -0
- package/apps/lib/components/ConclusionHeader.tsx +148 -0
- package/apps/lib/components/DatePicker.tsx +2 -0
- package/apps/lib/components/Drawer.tsx +66 -24
- package/apps/lib/components/FormBuilder/DisplayField.tsx +40 -0
- package/apps/lib/components/FormBuilder/context.ts +71 -0
- package/apps/lib/components/FormBuilder/fields/ChoiceFields.tsx +65 -0
- package/apps/lib/components/FormBuilder/fields/ColorField.tsx +76 -0
- package/apps/lib/components/FormBuilder/fields/CustomField.tsx +16 -0
- package/apps/lib/components/FormBuilder/fields/DateField.tsx +34 -0
- package/apps/lib/components/FormBuilder/fields/FieldArray.tsx +79 -0
- package/apps/lib/components/FormBuilder/fields/FieldShell.tsx +174 -0
- package/apps/lib/components/FormBuilder/fields/FileField.tsx +40 -0
- package/apps/lib/components/FormBuilder/fields/OptionListFields.tsx +139 -0
- package/apps/lib/components/FormBuilder/fields/OtpField.tsx +32 -0
- package/apps/lib/components/FormBuilder/fields/PhoneField.tsx +82 -0
- package/apps/lib/components/FormBuilder/fields/RichTextEditorField.tsx +37 -0
- package/apps/lib/components/FormBuilder/fields/SelectField.tsx +101 -0
- package/apps/lib/components/FormBuilder/fields/SignatureField.tsx +175 -0
- package/apps/lib/components/FormBuilder/fields/SliderField.tsx +72 -0
- package/apps/lib/components/FormBuilder/fields/SwitchBoxField.tsx +42 -0
- package/apps/lib/components/FormBuilder/fields/TableField.tsx +312 -0
- package/apps/lib/components/FormBuilder/fields/TextField.tsx +222 -0
- package/apps/lib/components/FormBuilder/fields/TreeSelectField.tsx +49 -0
- package/apps/lib/components/FormBuilder/fields/countries.ts +303 -0
- package/apps/lib/components/FormBuilder/fields/index.ts +25 -0
- package/apps/lib/components/FormBuilder/form-builder.tsx +292 -0
- package/apps/lib/components/FormBuilder/header.tsx +105 -0
- package/apps/lib/components/FormBuilder/index.ts +37 -0
- package/apps/lib/components/FormBuilder/numberFormat.ts +16 -0
- package/apps/lib/components/FormBuilder/stepper.tsx +217 -0
- package/apps/lib/components/FormBuilder/submit.tsx +41 -0
- package/apps/lib/components/FormBuilder/types.ts +264 -0
- package/apps/lib/components/FormBuilder/viewFormat.tsx +137 -0
- package/apps/lib/components/FormRenderer/FormDrawer.tsx +121 -0
- package/apps/lib/components/FormRenderer/form-renderer.tsx +113 -0
- package/apps/lib/components/FormRenderer/index.ts +9 -0
- package/apps/lib/components/FormRenderer/types.ts +79 -0
- package/apps/lib/components/FormSummary.tsx +282 -0
- package/apps/lib/components/ImageAttachment.tsx +36 -61
- package/apps/lib/components/Label.tsx +49 -42
- package/apps/lib/components/RadioCard.tsx +2 -0
- package/apps/lib/components/SearchableSelect.tsx +9 -3
- package/apps/lib/components/SectionBlock.tsx +16 -8
- package/apps/lib/components/Select.tsx +41 -120
- package/apps/lib/components/TextEditor/RichTextField.tsx +46 -0
- package/apps/lib/components/{TextEditor.tsx → TextEditor/TextEditor.tsx} +63 -9
- package/apps/lib/components/TextEditor/TextEditorToolbar.tsx +429 -0
- package/apps/lib/components/TextEditor/editor-tools/AlignmentTune.ts +70 -0
- package/apps/lib/components/TextEditor/editor-tools/ColorInlineTool.ts +50 -0
- package/apps/lib/components/TextEditor/editor-tools/StrikethroughInlineTool.ts +48 -0
- package/apps/lib/components/TextEditor/editor-tools/inlineFormat.ts +98 -0
- package/apps/lib/{types → components/TextEditor}/editorjs.d.ts +19 -0
- package/apps/lib/components/TextEditor/index.ts +7 -0
- package/apps/lib/components/Textarea.tsx +1 -1
- package/apps/lib/registry.json +51 -58
- package/apps/lib/tsconfig.tsbuildinfo +1 -0
- package/apps/lib/utils/color.ts +175 -0
- package/docs/components/card.md +4 -2
- package/docs/components/chart-block-tool.md +5 -4
- package/docs/components/color-picker.md +101 -0
- package/docs/components/conclusion-header.md +80 -0
- package/docs/components/drawer.md +153 -102
- package/docs/components/form-builder.md +270 -0
- package/docs/components/form-renderer.md +228 -0
- package/docs/components/form-summary.md +123 -0
- package/docs/components/image-attachment.md +10 -4
- package/docs/components/table-dnd-wrapper.md +5 -3
- package/docs/components/text-editor.md +18 -0
- package/docs/how-to/form-and-list-recipes.md +26 -19
- package/docs/how-to/forms-with-form-builder.md +408 -0
- package/docs/how-to/guides.md +153 -179
- package/docs/tutorials/building-first-form.md +150 -159
- package/package.json +1 -1
- /package/apps/lib/components/{ChartBlockTool.ts → TextEditor/ChartBlockTool.ts} +0 -0
- /package/apps/lib/components/{TableDnDWrapper.ts → TextEditor/TableDnDWrapper.ts} +0 -0
- /package/apps/lib/{utils → components/TextEditor}/markdownParser.ts +0 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { ReactNode, useEffect, useLayoutEffect, useRef, useState, type ChangeEvent } from "react";
|
|
4
|
+
import type { ControllerRenderProps, FieldValues } from "react-hook-form";
|
|
5
|
+
|
|
6
|
+
import { InputField } from "../../InputField";
|
|
7
|
+
import { Textarea } from "../../Textarea";
|
|
8
|
+
import { PasswordLevel } from "../../PasswordLevel";
|
|
9
|
+
import { useLoading, useCell } from "../context";
|
|
10
|
+
import { formatFieldView } from "../viewFormat";
|
|
11
|
+
import { formatNumber } from "../numberFormat";
|
|
12
|
+
import type { BaseFieldProps, CurrencyFieldProps, PasswordFieldProps } from "../types";
|
|
13
|
+
import { FieldShell } from "./FieldShell";
|
|
14
|
+
|
|
15
|
+
// Run before paint on the client; fall back to useEffect on the server (no SSR warning).
|
|
16
|
+
const useIsoLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* A number input that shows a **live** thousands-separated value (e.g. "1,299")
|
|
20
|
+
* while storing the raw `number` in the form. Commas are inserted as you type and
|
|
21
|
+
* the caret is preserved; decimals (and a trailing ".") are kept intact.
|
|
22
|
+
*/
|
|
23
|
+
function NumberInput({
|
|
24
|
+
field,
|
|
25
|
+
icon,
|
|
26
|
+
placeholder,
|
|
27
|
+
disabled,
|
|
28
|
+
onTable,
|
|
29
|
+
}: {
|
|
30
|
+
field: ControllerRenderProps<FieldValues, string>;
|
|
31
|
+
icon?: ReactNode;
|
|
32
|
+
placeholder?: string;
|
|
33
|
+
disabled?: boolean;
|
|
34
|
+
onTable?: boolean;
|
|
35
|
+
}) {
|
|
36
|
+
const inputRef = useRef<HTMLInputElement | null>(null);
|
|
37
|
+
const caretRef = useRef<number | null>(null);
|
|
38
|
+
const [text, setText] = useState<string>(() => formatNumber(field.value));
|
|
39
|
+
|
|
40
|
+
// Reflect external value changes (e.g. edit data loading) while not editing.
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (document.activeElement === inputRef.current) return;
|
|
43
|
+
setText(formatNumber(field.value));
|
|
44
|
+
}, [field.value]);
|
|
45
|
+
|
|
46
|
+
// Restore the caret after a live reformat (commas shift positions).
|
|
47
|
+
useIsoLayoutEffect(() => {
|
|
48
|
+
if (caretRef.current != null && inputRef.current) {
|
|
49
|
+
inputRef.current.setSelectionRange(caretRef.current, caretRef.current);
|
|
50
|
+
caretRef.current = null;
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const setRefs = (el: HTMLInputElement | null) => {
|
|
55
|
+
inputRef.current = el;
|
|
56
|
+
if (typeof field.ref === "function") field.ref(el);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
|
|
60
|
+
const raw = e.target.value;
|
|
61
|
+
const caret = e.target.selectionStart ?? raw.length;
|
|
62
|
+
const digitsBeforeCaret = raw.slice(0, caret).replace(/\D/g, "").length;
|
|
63
|
+
|
|
64
|
+
// Keep digits and a single decimal point.
|
|
65
|
+
let cleaned = raw.replace(/[^\d.]/g, "");
|
|
66
|
+
const dot = cleaned.indexOf(".");
|
|
67
|
+
if (dot !== -1) {
|
|
68
|
+
cleaned = cleaned.slice(0, dot + 1) + cleaned.slice(dot + 1).replace(/\./g, "");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
let formatted = "";
|
|
72
|
+
if (cleaned !== "") {
|
|
73
|
+
const [intPart, decPart] = cleaned.split(".");
|
|
74
|
+
const intFmt = intPart === "" ? "" : Number(intPart).toLocaleString("en-US");
|
|
75
|
+
formatted = decPart !== undefined ? `${intFmt}.${decPart}` : intFmt;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Caret goes after the same count of digits, skipping the inserted commas.
|
|
79
|
+
let pos = 0;
|
|
80
|
+
let seen = 0;
|
|
81
|
+
while (pos < formatted.length && seen < digitsBeforeCaret) {
|
|
82
|
+
if (/\d/.test(formatted[pos])) seen++;
|
|
83
|
+
pos++;
|
|
84
|
+
}
|
|
85
|
+
caretRef.current = pos;
|
|
86
|
+
|
|
87
|
+
setText(formatted);
|
|
88
|
+
field.onChange(cleaned === "" || cleaned === "." ? undefined : Number(cleaned));
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
return (
|
|
92
|
+
<InputField
|
|
93
|
+
name={field.name}
|
|
94
|
+
ref={setRefs}
|
|
95
|
+
type="text"
|
|
96
|
+
inputMode="decimal"
|
|
97
|
+
icon={icon}
|
|
98
|
+
value={text}
|
|
99
|
+
placeholder={placeholder}
|
|
100
|
+
disabled={disabled}
|
|
101
|
+
onTable={onTable}
|
|
102
|
+
className="w-full"
|
|
103
|
+
onChange={handleChange}
|
|
104
|
+
onBlur={() => field.onBlur()}
|
|
105
|
+
/>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function TextLike({ props, type }: { props: BaseFieldProps; type: "text" | "email" | "password" }) {
|
|
110
|
+
const loading = useLoading();
|
|
111
|
+
const cell = useCell();
|
|
112
|
+
return (
|
|
113
|
+
<FieldShell {...props} view={(v) => formatFieldView({ kind: "text", value: v })}>
|
|
114
|
+
{(field) => (
|
|
115
|
+
<InputField
|
|
116
|
+
{...field}
|
|
117
|
+
type={type}
|
|
118
|
+
value={field.value ?? ""}
|
|
119
|
+
placeholder={props.placeholder}
|
|
120
|
+
disabled={props.disabled || loading}
|
|
121
|
+
onTable={cell}
|
|
122
|
+
className="w-full"
|
|
123
|
+
/>
|
|
124
|
+
)}
|
|
125
|
+
</FieldShell>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function TextField(props: BaseFieldProps) {
|
|
130
|
+
return <TextLike props={props} type="text" />;
|
|
131
|
+
}
|
|
132
|
+
export function EmailField(props: BaseFieldProps) {
|
|
133
|
+
return <TextLike props={props} type="email" />;
|
|
134
|
+
}
|
|
135
|
+
export function PasswordField(props: PasswordFieldProps) {
|
|
136
|
+
const loading = useLoading();
|
|
137
|
+
const cell = useCell();
|
|
138
|
+
return (
|
|
139
|
+
<FieldShell
|
|
140
|
+
{...props}
|
|
141
|
+
view={(v) => formatFieldView({ kind: "text", value: v ? "••••••••" : "" })}
|
|
142
|
+
>
|
|
143
|
+
{(field) => (
|
|
144
|
+
<div className="flex w-full flex-col gap-2">
|
|
145
|
+
<InputField
|
|
146
|
+
{...field}
|
|
147
|
+
type="password"
|
|
148
|
+
value={field.value ?? ""}
|
|
149
|
+
placeholder={props.placeholder}
|
|
150
|
+
disabled={props.disabled || loading}
|
|
151
|
+
onTable={cell}
|
|
152
|
+
className="w-full"
|
|
153
|
+
/>
|
|
154
|
+
{props.strengthMeter && <PasswordLevel value={field.value ?? ""} />}
|
|
155
|
+
</div>
|
|
156
|
+
)}
|
|
157
|
+
</FieldShell>
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function NumberField(props: BaseFieldProps) {
|
|
162
|
+
const loading = useLoading();
|
|
163
|
+
const cell = useCell();
|
|
164
|
+
return (
|
|
165
|
+
<FieldShell {...props} view={(v) => formatFieldView({ kind: "number", value: v })}>
|
|
166
|
+
{(field) => (
|
|
167
|
+
<NumberInput
|
|
168
|
+
field={field}
|
|
169
|
+
placeholder={props.placeholder}
|
|
170
|
+
disabled={props.disabled || loading}
|
|
171
|
+
onTable={cell}
|
|
172
|
+
/>
|
|
173
|
+
)}
|
|
174
|
+
</FieldShell>
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function CurrencyField(props: CurrencyFieldProps) {
|
|
179
|
+
const loading = useLoading();
|
|
180
|
+
const cell = useCell();
|
|
181
|
+
return (
|
|
182
|
+
<FieldShell
|
|
183
|
+
{...props}
|
|
184
|
+
view={(v) =>
|
|
185
|
+
formatFieldView({ kind: "currency", value: v, currencySymbol: props.currencySymbol })
|
|
186
|
+
}
|
|
187
|
+
>
|
|
188
|
+
{(field) => (
|
|
189
|
+
<NumberInput
|
|
190
|
+
field={field}
|
|
191
|
+
icon={
|
|
192
|
+
props.currencySymbol ? (
|
|
193
|
+
<span className="typography-body-small-medium">{props.currencySymbol}</span>
|
|
194
|
+
) : undefined
|
|
195
|
+
}
|
|
196
|
+
placeholder={props.placeholder}
|
|
197
|
+
disabled={props.disabled || loading}
|
|
198
|
+
onTable={cell}
|
|
199
|
+
/>
|
|
200
|
+
)}
|
|
201
|
+
</FieldShell>
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export function TextareaField(props: BaseFieldProps & { rows?: number }) {
|
|
206
|
+
const loading = useLoading();
|
|
207
|
+
return (
|
|
208
|
+
<FieldShell {...props} view={(v) => formatFieldView({ kind: "text", value: v })}>
|
|
209
|
+
{(field, fieldState) => (
|
|
210
|
+
<Textarea
|
|
211
|
+
{...field}
|
|
212
|
+
value={field.value ?? ""}
|
|
213
|
+
rows={props.rows}
|
|
214
|
+
placeholder={props.placeholder}
|
|
215
|
+
disabled={props.disabled || loading}
|
|
216
|
+
state={fieldState.error ? "negative" : undefined}
|
|
217
|
+
className="w-full"
|
|
218
|
+
/>
|
|
219
|
+
)}
|
|
220
|
+
</FieldShell>
|
|
221
|
+
);
|
|
222
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { SearchableTree } from "../../SearchableTree";
|
|
4
|
+
import type { TreeSelectFieldProps } from "../types";
|
|
5
|
+
import { FieldShell } from "./FieldShell";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* `FormBuilder.TreeSelect` — hierarchical single-select (Glare `SearchableTree`).
|
|
9
|
+
* Stores the selected node's id; seeds the picker + view label by finding the
|
|
10
|
+
* node back from that id.
|
|
11
|
+
*/
|
|
12
|
+
export function TreeSelectField<T>(props: TreeSelectFieldProps<T>) {
|
|
13
|
+
const findById = (id: unknown): T | null => {
|
|
14
|
+
const target = String(id ?? "");
|
|
15
|
+
if (!target) return null;
|
|
16
|
+
// Works for both nested (getNodeChildren) and flat node lists.
|
|
17
|
+
const stack: T[] = [...props.nodes];
|
|
18
|
+
while (stack.length) {
|
|
19
|
+
const node = stack.pop() as T;
|
|
20
|
+
if (props.getNodeId(node) === target) return node;
|
|
21
|
+
const kids = props.getNodeChildren?.(node);
|
|
22
|
+
if (kids && kids.length) stack.push(...kids);
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<FieldShell
|
|
29
|
+
{...props}
|
|
30
|
+
view={(v) => {
|
|
31
|
+
const node = findById(v);
|
|
32
|
+
return { value: node ? String(props.getNodeLabel(node)) : "" };
|
|
33
|
+
}}
|
|
34
|
+
>
|
|
35
|
+
{(field) => (
|
|
36
|
+
<SearchableTree
|
|
37
|
+
nodes={props.nodes}
|
|
38
|
+
getNodeId={props.getNodeId}
|
|
39
|
+
getNodeLabel={props.getNodeLabel}
|
|
40
|
+
getNodeChildren={props.getNodeChildren}
|
|
41
|
+
parentIdKey={props.parentIdKey as (keyof T & string) | undefined}
|
|
42
|
+
selectableFolders={props.selectableFolders}
|
|
43
|
+
value={findById(field.value)}
|
|
44
|
+
onSelect={(node) => field.onChange(props.getNodeId(node))}
|
|
45
|
+
/>
|
|
46
|
+
)}
|
|
47
|
+
</FieldShell>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ISO 3166-1 countries + their E.164 calling codes, for `FormBuilder.Phone`.
|
|
3
|
+
*
|
|
4
|
+
* Data is stored compactly as `[isoCode, name, dial]`; the flag emoji is derived from the ISO code
|
|
5
|
+
* (regional-indicator letters) rather than stored. Dial codes are NOT unique (US/Canada share +1,
|
|
6
|
+
* etc.) — that's why the picker keys options by the unique ISO `code`, and `countryByDial` resolves
|
|
7
|
+
* a stored dial back to a representative country.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export interface Country {
|
|
11
|
+
/** ISO 3166-1 alpha-2 code, unique — e.g. "US". */
|
|
12
|
+
code: string;
|
|
13
|
+
name: string;
|
|
14
|
+
/** E.164 calling code with leading "+", e.g. "+1". */
|
|
15
|
+
dial: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// [ISO code, name, dial(no +)]
|
|
19
|
+
const DATA: ReadonlyArray<readonly [string, string, string]> = [
|
|
20
|
+
["AF", "Afghanistan", "93"],
|
|
21
|
+
["AX", "Åland Islands", "358"],
|
|
22
|
+
["AL", "Albania", "355"],
|
|
23
|
+
["DZ", "Algeria", "213"],
|
|
24
|
+
["AS", "American Samoa", "1"],
|
|
25
|
+
["AD", "Andorra", "376"],
|
|
26
|
+
["AO", "Angola", "244"],
|
|
27
|
+
["AI", "Anguilla", "1"],
|
|
28
|
+
["AG", "Antigua and Barbuda", "1"],
|
|
29
|
+
["AR", "Argentina", "54"],
|
|
30
|
+
["AM", "Armenia", "374"],
|
|
31
|
+
["AW", "Aruba", "297"],
|
|
32
|
+
["AU", "Australia", "61"],
|
|
33
|
+
["AT", "Austria", "43"],
|
|
34
|
+
["AZ", "Azerbaijan", "994"],
|
|
35
|
+
["BS", "Bahamas", "1"],
|
|
36
|
+
["BH", "Bahrain", "973"],
|
|
37
|
+
["BD", "Bangladesh", "880"],
|
|
38
|
+
["BB", "Barbados", "1"],
|
|
39
|
+
["BY", "Belarus", "375"],
|
|
40
|
+
["BE", "Belgium", "32"],
|
|
41
|
+
["BZ", "Belize", "501"],
|
|
42
|
+
["BJ", "Benin", "229"],
|
|
43
|
+
["BM", "Bermuda", "1"],
|
|
44
|
+
["BT", "Bhutan", "975"],
|
|
45
|
+
["BO", "Bolivia", "591"],
|
|
46
|
+
["BA", "Bosnia and Herzegovina", "387"],
|
|
47
|
+
["BW", "Botswana", "267"],
|
|
48
|
+
["BR", "Brazil", "55"],
|
|
49
|
+
["IO", "British Indian Ocean Territory", "246"],
|
|
50
|
+
["VG", "British Virgin Islands", "1"],
|
|
51
|
+
["BN", "Brunei", "673"],
|
|
52
|
+
["BG", "Bulgaria", "359"],
|
|
53
|
+
["BF", "Burkina Faso", "226"],
|
|
54
|
+
["BI", "Burundi", "257"],
|
|
55
|
+
["KH", "Cambodia", "855"],
|
|
56
|
+
["CM", "Cameroon", "237"],
|
|
57
|
+
["CA", "Canada", "1"],
|
|
58
|
+
["CV", "Cape Verde", "238"],
|
|
59
|
+
["KY", "Cayman Islands", "1"],
|
|
60
|
+
["CF", "Central African Republic", "236"],
|
|
61
|
+
["TD", "Chad", "235"],
|
|
62
|
+
["CL", "Chile", "56"],
|
|
63
|
+
["CN", "China", "86"],
|
|
64
|
+
["CX", "Christmas Island", "61"],
|
|
65
|
+
["CC", "Cocos (Keeling) Islands", "61"],
|
|
66
|
+
["CO", "Colombia", "57"],
|
|
67
|
+
["KM", "Comoros", "269"],
|
|
68
|
+
["CG", "Congo - Brazzaville", "242"],
|
|
69
|
+
["CD", "Congo - Kinshasa", "243"],
|
|
70
|
+
["CK", "Cook Islands", "682"],
|
|
71
|
+
["CR", "Costa Rica", "506"],
|
|
72
|
+
["CI", "Côte d’Ivoire", "225"],
|
|
73
|
+
["HR", "Croatia", "385"],
|
|
74
|
+
["CU", "Cuba", "53"],
|
|
75
|
+
["CW", "Curaçao", "599"],
|
|
76
|
+
["CY", "Cyprus", "357"],
|
|
77
|
+
["CZ", "Czechia", "420"],
|
|
78
|
+
["DK", "Denmark", "45"],
|
|
79
|
+
["DJ", "Djibouti", "253"],
|
|
80
|
+
["DM", "Dominica", "1"],
|
|
81
|
+
["DO", "Dominican Republic", "1"],
|
|
82
|
+
["EC", "Ecuador", "593"],
|
|
83
|
+
["EG", "Egypt", "20"],
|
|
84
|
+
["SV", "El Salvador", "503"],
|
|
85
|
+
["GQ", "Equatorial Guinea", "240"],
|
|
86
|
+
["ER", "Eritrea", "291"],
|
|
87
|
+
["EE", "Estonia", "372"],
|
|
88
|
+
["SZ", "Eswatini", "268"],
|
|
89
|
+
["ET", "Ethiopia", "251"],
|
|
90
|
+
["FK", "Falkland Islands", "500"],
|
|
91
|
+
["FO", "Faroe Islands", "298"],
|
|
92
|
+
["FJ", "Fiji", "679"],
|
|
93
|
+
["FI", "Finland", "358"],
|
|
94
|
+
["FR", "France", "33"],
|
|
95
|
+
["GF", "French Guiana", "594"],
|
|
96
|
+
["PF", "French Polynesia", "689"],
|
|
97
|
+
["GA", "Gabon", "241"],
|
|
98
|
+
["GM", "Gambia", "220"],
|
|
99
|
+
["GE", "Georgia", "995"],
|
|
100
|
+
["DE", "Germany", "49"],
|
|
101
|
+
["GH", "Ghana", "233"],
|
|
102
|
+
["GI", "Gibraltar", "350"],
|
|
103
|
+
["GR", "Greece", "30"],
|
|
104
|
+
["GL", "Greenland", "299"],
|
|
105
|
+
["GD", "Grenada", "1"],
|
|
106
|
+
["GP", "Guadeloupe", "590"],
|
|
107
|
+
["GU", "Guam", "1"],
|
|
108
|
+
["GT", "Guatemala", "502"],
|
|
109
|
+
["GG", "Guernsey", "44"],
|
|
110
|
+
["GN", "Guinea", "224"],
|
|
111
|
+
["GW", "Guinea-Bissau", "245"],
|
|
112
|
+
["GY", "Guyana", "592"],
|
|
113
|
+
["HT", "Haiti", "509"],
|
|
114
|
+
["HN", "Honduras", "504"],
|
|
115
|
+
["HK", "Hong Kong", "852"],
|
|
116
|
+
["HU", "Hungary", "36"],
|
|
117
|
+
["IS", "Iceland", "354"],
|
|
118
|
+
["IN", "India", "91"],
|
|
119
|
+
["ID", "Indonesia", "62"],
|
|
120
|
+
["IR", "Iran", "98"],
|
|
121
|
+
["IQ", "Iraq", "964"],
|
|
122
|
+
["IE", "Ireland", "353"],
|
|
123
|
+
["IM", "Isle of Man", "44"],
|
|
124
|
+
["IT", "Italy", "39"],
|
|
125
|
+
["JM", "Jamaica", "1"],
|
|
126
|
+
["JP", "Japan", "81"],
|
|
127
|
+
["JE", "Jersey", "44"],
|
|
128
|
+
["JO", "Jordan", "962"],
|
|
129
|
+
["KZ", "Kazakhstan", "7"],
|
|
130
|
+
["KE", "Kenya", "254"],
|
|
131
|
+
["KI", "Kiribati", "686"],
|
|
132
|
+
["XK", "Kosovo", "383"],
|
|
133
|
+
["KW", "Kuwait", "965"],
|
|
134
|
+
["KG", "Kyrgyzstan", "996"],
|
|
135
|
+
["LA", "Laos", "856"],
|
|
136
|
+
["LV", "Latvia", "371"],
|
|
137
|
+
["LB", "Lebanon", "961"],
|
|
138
|
+
["LS", "Lesotho", "266"],
|
|
139
|
+
["LR", "Liberia", "231"],
|
|
140
|
+
["LY", "Libya", "218"],
|
|
141
|
+
["LI", "Liechtenstein", "423"],
|
|
142
|
+
["LT", "Lithuania", "370"],
|
|
143
|
+
["LU", "Luxembourg", "352"],
|
|
144
|
+
["MO", "Macau", "853"],
|
|
145
|
+
["MG", "Madagascar", "261"],
|
|
146
|
+
["MW", "Malawi", "265"],
|
|
147
|
+
["MY", "Malaysia", "60"],
|
|
148
|
+
["MV", "Maldives", "960"],
|
|
149
|
+
["ML", "Mali", "223"],
|
|
150
|
+
["MT", "Malta", "356"],
|
|
151
|
+
["MH", "Marshall Islands", "692"],
|
|
152
|
+
["MQ", "Martinique", "596"],
|
|
153
|
+
["MR", "Mauritania", "222"],
|
|
154
|
+
["MU", "Mauritius", "230"],
|
|
155
|
+
["YT", "Mayotte", "262"],
|
|
156
|
+
["MX", "Mexico", "52"],
|
|
157
|
+
["FM", "Micronesia", "691"],
|
|
158
|
+
["MD", "Moldova", "373"],
|
|
159
|
+
["MC", "Monaco", "377"],
|
|
160
|
+
["MN", "Mongolia", "976"],
|
|
161
|
+
["ME", "Montenegro", "382"],
|
|
162
|
+
["MS", "Montserrat", "1"],
|
|
163
|
+
["MA", "Morocco", "212"],
|
|
164
|
+
["MZ", "Mozambique", "258"],
|
|
165
|
+
["MM", "Myanmar (Burma)", "95"],
|
|
166
|
+
["NA", "Namibia", "264"],
|
|
167
|
+
["NR", "Nauru", "674"],
|
|
168
|
+
["NP", "Nepal", "977"],
|
|
169
|
+
["NL", "Netherlands", "31"],
|
|
170
|
+
["NC", "New Caledonia", "687"],
|
|
171
|
+
["NZ", "New Zealand", "64"],
|
|
172
|
+
["NI", "Nicaragua", "505"],
|
|
173
|
+
["NE", "Niger", "227"],
|
|
174
|
+
["NG", "Nigeria", "234"],
|
|
175
|
+
["NU", "Niue", "683"],
|
|
176
|
+
["NF", "Norfolk Island", "672"],
|
|
177
|
+
["KP", "North Korea", "850"],
|
|
178
|
+
["MK", "North Macedonia", "389"],
|
|
179
|
+
["MP", "Northern Mariana Islands", "1"],
|
|
180
|
+
["NO", "Norway", "47"],
|
|
181
|
+
["OM", "Oman", "968"],
|
|
182
|
+
["PK", "Pakistan", "92"],
|
|
183
|
+
["PW", "Palau", "680"],
|
|
184
|
+
["PS", "Palestine", "970"],
|
|
185
|
+
["PA", "Panama", "507"],
|
|
186
|
+
["PG", "Papua New Guinea", "675"],
|
|
187
|
+
["PY", "Paraguay", "595"],
|
|
188
|
+
["PE", "Peru", "51"],
|
|
189
|
+
["PH", "Philippines", "63"],
|
|
190
|
+
["PL", "Poland", "48"],
|
|
191
|
+
["PT", "Portugal", "351"],
|
|
192
|
+
["PR", "Puerto Rico", "1"],
|
|
193
|
+
["QA", "Qatar", "974"],
|
|
194
|
+
["RE", "Réunion", "262"],
|
|
195
|
+
["RO", "Romania", "40"],
|
|
196
|
+
["RU", "Russia", "7"],
|
|
197
|
+
["RW", "Rwanda", "250"],
|
|
198
|
+
["WS", "Samoa", "685"],
|
|
199
|
+
["SM", "San Marino", "378"],
|
|
200
|
+
["ST", "São Tomé and Príncipe", "239"],
|
|
201
|
+
["SA", "Saudi Arabia", "966"],
|
|
202
|
+
["SN", "Senegal", "221"],
|
|
203
|
+
["RS", "Serbia", "381"],
|
|
204
|
+
["SC", "Seychelles", "248"],
|
|
205
|
+
["SL", "Sierra Leone", "232"],
|
|
206
|
+
["SG", "Singapore", "65"],
|
|
207
|
+
["SX", "Sint Maarten", "1"],
|
|
208
|
+
["SK", "Slovakia", "421"],
|
|
209
|
+
["SI", "Slovenia", "386"],
|
|
210
|
+
["SB", "Solomon Islands", "677"],
|
|
211
|
+
["SO", "Somalia", "252"],
|
|
212
|
+
["ZA", "South Africa", "27"],
|
|
213
|
+
["KR", "South Korea", "82"],
|
|
214
|
+
["SS", "South Sudan", "211"],
|
|
215
|
+
["ES", "Spain", "34"],
|
|
216
|
+
["LK", "Sri Lanka", "94"],
|
|
217
|
+
["BL", "St. Barthélemy", "590"],
|
|
218
|
+
["SH", "St. Helena", "290"],
|
|
219
|
+
["KN", "St. Kitts and Nevis", "1"],
|
|
220
|
+
["LC", "St. Lucia", "1"],
|
|
221
|
+
["MF", "St. Martin", "590"],
|
|
222
|
+
["PM", "St. Pierre and Miquelon", "508"],
|
|
223
|
+
["VC", "St. Vincent and the Grenadines", "1"],
|
|
224
|
+
["SD", "Sudan", "249"],
|
|
225
|
+
["SR", "Suriname", "597"],
|
|
226
|
+
["SE", "Sweden", "46"],
|
|
227
|
+
["CH", "Switzerland", "41"],
|
|
228
|
+
["SY", "Syria", "963"],
|
|
229
|
+
["TW", "Taiwan", "886"],
|
|
230
|
+
["TJ", "Tajikistan", "992"],
|
|
231
|
+
["TZ", "Tanzania", "255"],
|
|
232
|
+
["TH", "Thailand", "66"],
|
|
233
|
+
["TL", "Timor-Leste", "670"],
|
|
234
|
+
["TG", "Togo", "228"],
|
|
235
|
+
["TK", "Tokelau", "690"],
|
|
236
|
+
["TO", "Tonga", "676"],
|
|
237
|
+
["TT", "Trinidad and Tobago", "1"],
|
|
238
|
+
["TN", "Tunisia", "216"],
|
|
239
|
+
["TR", "Turkey", "90"],
|
|
240
|
+
["TM", "Turkmenistan", "993"],
|
|
241
|
+
["TC", "Turks and Caicos Islands", "1"],
|
|
242
|
+
["TV", "Tuvalu", "688"],
|
|
243
|
+
["UG", "Uganda", "256"],
|
|
244
|
+
["UA", "Ukraine", "380"],
|
|
245
|
+
["AE", "United Arab Emirates", "971"],
|
|
246
|
+
["GB", "United Kingdom", "44"],
|
|
247
|
+
["US", "United States", "1"],
|
|
248
|
+
["UY", "Uruguay", "598"],
|
|
249
|
+
["UZ", "Uzbekistan", "998"],
|
|
250
|
+
["VU", "Vanuatu", "678"],
|
|
251
|
+
["VA", "Vatican City", "39"],
|
|
252
|
+
["VE", "Venezuela", "58"],
|
|
253
|
+
["VN", "Vietnam", "84"],
|
|
254
|
+
["VI", "U.S. Virgin Islands", "1"],
|
|
255
|
+
["WF", "Wallis and Futuna", "681"],
|
|
256
|
+
["YE", "Yemen", "967"],
|
|
257
|
+
["ZM", "Zambia", "260"],
|
|
258
|
+
["ZW", "Zimbabwe", "263"],
|
|
259
|
+
];
|
|
260
|
+
|
|
261
|
+
/** The full list, sorted by name. */
|
|
262
|
+
export const COUNTRIES: Country[] = DATA.map(([code, name, dial]) => ({
|
|
263
|
+
code,
|
|
264
|
+
name,
|
|
265
|
+
dial: `+${dial}`,
|
|
266
|
+
})).sort((a, b) => a.name.localeCompare(b.name));
|
|
267
|
+
|
|
268
|
+
const BY_CODE = new Map(COUNTRIES.map((c) => [c.code, c]));
|
|
269
|
+
|
|
270
|
+
/** Flag emoji from an ISO 3166-1 alpha-2 code (regional-indicator letters). */
|
|
271
|
+
export function flagEmoji(code: string): string {
|
|
272
|
+
return code
|
|
273
|
+
.toUpperCase()
|
|
274
|
+
.replace(/[^A-Z]/g, "")
|
|
275
|
+
.replace(/./g, (c) => String.fromCodePoint(127397 + c.charCodeAt(0)));
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export function countryByCode(code: string): Country | undefined {
|
|
279
|
+
return BY_CODE.get(code);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// For dial codes shared by several countries, the one to show when resolving a stored dial back.
|
|
283
|
+
const PREFERRED_BY_DIAL: Record<string, string> = {
|
|
284
|
+
"+964": "IQ",
|
|
285
|
+
"+1": "US",
|
|
286
|
+
"+7": "RU",
|
|
287
|
+
"+44": "GB",
|
|
288
|
+
"+61": "AU",
|
|
289
|
+
"+39": "IT",
|
|
290
|
+
"+212": "MA",
|
|
291
|
+
"+262": "RE",
|
|
292
|
+
"+358": "FI",
|
|
293
|
+
"+590": "GP",
|
|
294
|
+
"+596": "MQ",
|
|
295
|
+
"+599": "CW",
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
/** A representative country for a dial code (dial codes aren't unique). */
|
|
299
|
+
export function countryByDial(dial: string): Country | undefined {
|
|
300
|
+
const preferred = PREFERRED_BY_DIAL[dial];
|
|
301
|
+
if (preferred) return BY_CODE.get(preferred);
|
|
302
|
+
return COUNTRIES.find((c) => c.dial === dial);
|
|
303
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export {
|
|
2
|
+
TextField,
|
|
3
|
+
EmailField,
|
|
4
|
+
PasswordField,
|
|
5
|
+
NumberField,
|
|
6
|
+
CurrencyField,
|
|
7
|
+
TextareaField,
|
|
8
|
+
} from "./TextField";
|
|
9
|
+
export { SelectField, SearchableSelectField, MultiSelectField } from "./SelectField";
|
|
10
|
+
export { CheckboxField, RadioCardsField } from "./ChoiceFields";
|
|
11
|
+
export { RadioListField, CheckboxGroupField } from "./OptionListFields";
|
|
12
|
+
export { SwitchBoxField } from "./SwitchBoxField";
|
|
13
|
+
export { DateField } from "./DateField";
|
|
14
|
+
export { OtpField } from "./OtpField";
|
|
15
|
+
export { TreeSelectField } from "./TreeSelectField";
|
|
16
|
+
export { SliderField } from "./SliderField";
|
|
17
|
+
export { ColorField } from "./ColorField";
|
|
18
|
+
export { SignatureField } from "./SignatureField";
|
|
19
|
+
export { PhoneField } from "./PhoneField";
|
|
20
|
+
export { FieldArray } from "./FieldArray";
|
|
21
|
+
export { TableField } from "./TableField";
|
|
22
|
+
export { FileField } from "./FileField";
|
|
23
|
+
export { RichTextField } from "./RichTextEditorField";
|
|
24
|
+
export { CustomField } from "./CustomField";
|
|
25
|
+
export { FieldShell } from "./FieldShell";
|