torch-glare 2.4.1 → 2.4.3
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/context.ts +68 -0
- package/apps/lib/components/FormBuilder/fields/ChoiceFields.tsx +60 -0
- package/apps/lib/components/FormBuilder/fields/ColorField.tsx +59 -0
- package/apps/lib/components/FormBuilder/fields/CustomField.tsx +11 -0
- package/apps/lib/components/FormBuilder/fields/DateField.tsx +32 -0
- package/apps/lib/components/FormBuilder/fields/FieldArray.tsx +74 -0
- package/apps/lib/components/FormBuilder/fields/FieldShell.tsx +148 -0
- package/apps/lib/components/FormBuilder/fields/FileField.tsx +39 -0
- package/apps/lib/components/FormBuilder/fields/OptionListFields.tsx +132 -0
- package/apps/lib/components/FormBuilder/fields/OtpField.tsx +31 -0
- package/apps/lib/components/FormBuilder/fields/PhoneField.tsx +83 -0
- package/apps/lib/components/FormBuilder/fields/RichTextEditorField.tsx +31 -0
- package/apps/lib/components/FormBuilder/fields/SelectField.tsx +91 -0
- package/apps/lib/components/FormBuilder/fields/SignatureField.tsx +157 -0
- package/apps/lib/components/FormBuilder/fields/SliderField.tsx +67 -0
- package/apps/lib/components/FormBuilder/fields/SwitchBoxField.tsx +41 -0
- package/apps/lib/components/FormBuilder/fields/TableField.tsx +308 -0
- package/apps/lib/components/FormBuilder/fields/TextField.tsx +213 -0
- package/apps/lib/components/FormBuilder/fields/TreeSelectField.tsx +43 -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 +279 -0
- package/apps/lib/components/FormBuilder/header.tsx +101 -0
- package/apps/lib/components/FormBuilder/index.ts +33 -0
- package/apps/lib/components/FormBuilder/numberFormat.ts +16 -0
- package/apps/lib/components/FormBuilder/stepper.tsx +290 -0
- package/apps/lib/components/FormBuilder/submit.tsx +38 -0
- package/apps/lib/components/FormBuilder/types.ts +261 -0
- package/apps/lib/components/FormRenderer/FormDrawer.tsx +128 -0
- package/apps/lib/components/FormRenderer/form-renderer.tsx +111 -0
- package/apps/lib/components/FormRenderer/index.ts +4 -0
- package/apps/lib/components/FormRenderer/types.ts +77 -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/Popover.tsx +6 -2
- package/apps/lib/components/RadioCard.tsx +2 -0
- package/apps/lib/components/SearchableSelect.tsx +16 -5
- 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/layouts/FieldSection.tsx +25 -22
- package/apps/lib/registry.json +51 -58
- package/apps/lib/tsconfig.tsbuildinfo +1 -0
- package/apps/lib/utils/color.ts +175 -0
- package/dist/src/shared/tailwindInit.d.ts.map +1 -1
- package/dist/src/shared/tailwindInit.js +3 -0
- package/dist/src/shared/tailwindInit.js.map +1 -1
- 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 +260 -0
- package/docs/components/form-renderer.md +234 -0
- package/docs/components/form-summary.md +123 -0
- package/docs/components/image-attachment.md +10 -4
- package/docs/components/searchable-select.md +50 -46
- 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 +411 -0
- package/docs/how-to/guides.md +153 -179
- package/docs/reference/tailwind-plugins.md +11 -1
- package/docs/tutorials/building-first-form.md +150 -159
- package/docs/tutorials/getting-started.md +9 -0
- 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,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";
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { ReactNode } from "react";
|
|
5
|
+
import { useForm, type FieldValues } from "react-hook-form";
|
|
6
|
+
|
|
7
|
+
import { cn } from "../../utils/cn";
|
|
8
|
+
import { Form } from "../Form";
|
|
9
|
+
import { SectionBlock, type SectionColor } from "../SectionBlock";
|
|
10
|
+
import { LoadingContext, DirectionContext, StepperContext, FormIdContext } from "./context";
|
|
11
|
+
import { Header } from "./header";
|
|
12
|
+
import type { FormBuilderRootProps } from "./types";
|
|
13
|
+
import {
|
|
14
|
+
TextField,
|
|
15
|
+
EmailField,
|
|
16
|
+
PasswordField,
|
|
17
|
+
NumberField,
|
|
18
|
+
CurrencyField,
|
|
19
|
+
TextareaField,
|
|
20
|
+
SelectField,
|
|
21
|
+
SearchableSelectField,
|
|
22
|
+
MultiSelectField,
|
|
23
|
+
CheckboxField,
|
|
24
|
+
RadioCardsField,
|
|
25
|
+
RadioListField,
|
|
26
|
+
CheckboxGroupField,
|
|
27
|
+
SwitchBoxField,
|
|
28
|
+
DateField,
|
|
29
|
+
OtpField,
|
|
30
|
+
TreeSelectField,
|
|
31
|
+
SliderField,
|
|
32
|
+
ColorField,
|
|
33
|
+
SignatureField,
|
|
34
|
+
PhoneField,
|
|
35
|
+
FieldArray,
|
|
36
|
+
TableField,
|
|
37
|
+
FileField,
|
|
38
|
+
RichTextField,
|
|
39
|
+
CustomField,
|
|
40
|
+
} from "./fields";
|
|
41
|
+
import {
|
|
42
|
+
Stepper,
|
|
43
|
+
Step,
|
|
44
|
+
Back,
|
|
45
|
+
Next,
|
|
46
|
+
StepperNav,
|
|
47
|
+
StepSlot,
|
|
48
|
+
useStepperState,
|
|
49
|
+
isStepElement,
|
|
50
|
+
isStepperElement,
|
|
51
|
+
} from "./stepper";
|
|
52
|
+
import { SubmitButton } from "./submit";
|
|
53
|
+
|
|
54
|
+
// ─── Section ─────────────────────────────────────────────────────────────────
|
|
55
|
+
|
|
56
|
+
export interface SectionProps {
|
|
57
|
+
title?: ReactNode;
|
|
58
|
+
color?: SectionColor;
|
|
59
|
+
icon?: ReactNode;
|
|
60
|
+
children: ReactNode;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** `FormBuilder.Section` — a titled Glare SectionBlock grouping fields. */
|
|
64
|
+
function Section({ title, color, icon, children }: SectionProps) {
|
|
65
|
+
return (
|
|
66
|
+
<SectionBlock title={title} color={color} icon={icon}>
|
|
67
|
+
{children}
|
|
68
|
+
</SectionBlock>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function isHeaderElement(node: React.ReactNode): node is React.ReactElement {
|
|
73
|
+
return (
|
|
74
|
+
React.isValidElement(node) &&
|
|
75
|
+
(node.type as { __isFormHeader?: boolean })?.__isFormHeader === true
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ─── Root ────────────────────────────────────────────────────────────────────
|
|
80
|
+
|
|
81
|
+
function FormBuilderRoot<T extends FieldValues = FieldValues>({
|
|
82
|
+
children,
|
|
83
|
+
id,
|
|
84
|
+
form: formProp,
|
|
85
|
+
onSubmit,
|
|
86
|
+
onInvalid,
|
|
87
|
+
resolver,
|
|
88
|
+
defaultValues,
|
|
89
|
+
values,
|
|
90
|
+
loading = false,
|
|
91
|
+
fieldDirection,
|
|
92
|
+
resetOnSuccess,
|
|
93
|
+
conclusion,
|
|
94
|
+
className,
|
|
95
|
+
}: FormBuilderRootProps<T>) {
|
|
96
|
+
// Hooks can't be conditional, so always create one; `formProp` wins when given
|
|
97
|
+
// (the caller hoisted `useForm` to share values with something outside the form).
|
|
98
|
+
const ownForm = useForm<T>({ resolver, defaultValues, values });
|
|
99
|
+
const form = formProp ?? ownForm;
|
|
100
|
+
// Don't force a direction — left undefined, FieldSection uses its responsive `flexible`
|
|
101
|
+
// layout (stacked on small screens, two-column at `lg`). Only a caller-set `fieldDirection`
|
|
102
|
+
// (e.g. vertical inside a drawer) pins it.
|
|
103
|
+
const direction = fieldDirection;
|
|
104
|
+
|
|
105
|
+
const handleValid = async (v: T) => {
|
|
106
|
+
await onSubmit(v);
|
|
107
|
+
if (resetOnSuccess) form.reset();
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// Split out a FormBuilder.Header (renders absolutely) and detect a Stepper child.
|
|
111
|
+
const childArray = React.Children.toArray(children);
|
|
112
|
+
const header = childArray.find(isHeaderElement);
|
|
113
|
+
const rest = childArray.filter((n) => !isHeaderElement(n));
|
|
114
|
+
|
|
115
|
+
const stepperEl = rest.find(isStepperElement);
|
|
116
|
+
const stepChildren = stepperEl ? React.Children.toArray(stepperEl.props.children) : [];
|
|
117
|
+
const steps = stepChildren.filter(isStepElement);
|
|
118
|
+
const stepExtras = stepChildren.filter((n) => !isStepElement(n));
|
|
119
|
+
const isStepper = !!stepperEl;
|
|
120
|
+
|
|
121
|
+
// Stepper state is lifted HERE so the nav can live in its own grid column, outside the
|
|
122
|
+
// `<form>`. Called unconditionally (inert when there are no steps) to keep hooks order stable.
|
|
123
|
+
const stepper = useStepperState(steps, form.trigger as Parameters<typeof useStepperState>[1]);
|
|
124
|
+
|
|
125
|
+
// The stepper nav is its own column beside the fields, inside the form surface.
|
|
126
|
+
const nav = isStepper ? <StepperNav /> : null;
|
|
127
|
+
|
|
128
|
+
// The fields the `<form>` wraps: the stepper's steps (+ any custom footer extras like
|
|
129
|
+
// Back/Next), or the plain children. The Submit itself lives outside the form (see FormRenderer).
|
|
130
|
+
const fields = isStepper ? (
|
|
131
|
+
<>
|
|
132
|
+
{steps.map((step, i) => (
|
|
133
|
+
<StepSlot key={i} index={i} active={i === stepper.currentStep}>
|
|
134
|
+
{step.props.children}
|
|
135
|
+
</StepSlot>
|
|
136
|
+
))}
|
|
137
|
+
{stepExtras}
|
|
138
|
+
</>
|
|
139
|
+
) : (
|
|
140
|
+
rest
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
// The fields column caps at 1200px and centers — as the middle column of the grid, and
|
|
144
|
+
// standalone.
|
|
145
|
+
const fieldsInner = (
|
|
146
|
+
<div className="mx-auto flex w-full max-w-[1100px] flex-col gap-4 px-[48px]">{fields}</div>
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
const formEl = (
|
|
150
|
+
<form id={id} className="w-full min-w-0" onSubmit={form.handleSubmit(handleValid, onInvalid)}>
|
|
151
|
+
{fieldsInner}
|
|
152
|
+
</form>
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
// Inside the form surface: the stepper rail beside the fields. Columns never wrap — the
|
|
156
|
+
// layout stays side-by-side at every screen size (the fields column shrinks instead).
|
|
157
|
+
const bodyInner = nav ? (
|
|
158
|
+
<div className="grid w-full grid-cols-[1fr_minmax(0,1100px)_1fr] gap-8">
|
|
159
|
+
{nav}
|
|
160
|
+
{formEl}
|
|
161
|
+
{/* Empty third column — balances the rail's gutter so the middle column is centred. */}
|
|
162
|
+
<div />
|
|
163
|
+
</div>
|
|
164
|
+
) : (
|
|
165
|
+
formEl
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
const surface = header ? (
|
|
169
|
+
// Scroll shell: the absolute header floats over the scrollable body. No fixed height — it
|
|
170
|
+
// fills whatever the parent gives it (like the drawer's panel fills its tray) and the body
|
|
171
|
+
// scrolls internally.
|
|
172
|
+
<div className="relative isolate flex h-full w-full flex-col overflow-hidden rounded-2xl bg-background-presentation-body-primary">
|
|
173
|
+
{header}
|
|
174
|
+
<div className="relative z-[1] flex min-h-0 w-full flex-1 flex-col overflow-y-auto px-6 py-6 pt-[72px] scrollbar-hide">
|
|
175
|
+
{bodyInner}
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
) : (
|
|
179
|
+
bodyInner
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
// The conclusion lives OUTSIDE the form surface — its own panel beside it, exactly like the
|
|
183
|
+
// drawer's tray (`FormDrawer` puts the conclusion next to the form panel with a 6px gutter).
|
|
184
|
+
// It never wraps below the form: the two stay side-by-side at every screen size.
|
|
185
|
+
const body = conclusion ? (
|
|
186
|
+
<div className="flex h-full flex-row items-stretch gap-[6px]">
|
|
187
|
+
<div className="min-h-0 min-w-0 flex-1">{surface}</div>
|
|
188
|
+
<div className="flex min-h-0">{conclusion}</div>
|
|
189
|
+
</div>
|
|
190
|
+
) : (
|
|
191
|
+
surface
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
// `className` lands on the OUTERMOST element — the one a parent lays out (e.g. `flex-1 min-h-0`
|
|
195
|
+
// to fill a flex column). `h-full` fills a parent that has a definite height.
|
|
196
|
+
const outerClassName = cn("h-full w-full @container", className);
|
|
197
|
+
|
|
198
|
+
const tree = (
|
|
199
|
+
<Form {...form}>
|
|
200
|
+
<div className={outerClassName}>{body}</div>
|
|
201
|
+
</Form>
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
return (
|
|
205
|
+
<FormIdContext.Provider value={id}>
|
|
206
|
+
<LoadingContext.Provider value={loading}>
|
|
207
|
+
<DirectionContext.Provider value={direction}>
|
|
208
|
+
{isStepper ? (
|
|
209
|
+
<StepperContext.Provider value={stepper}>{tree}</StepperContext.Provider>
|
|
210
|
+
) : (
|
|
211
|
+
tree
|
|
212
|
+
)}
|
|
213
|
+
</DirectionContext.Provider>
|
|
214
|
+
</LoadingContext.Provider>
|
|
215
|
+
</FormIdContext.Provider>
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* FormBuilder — a compound, composition-based form. Author a form as JSX:
|
|
221
|
+
*
|
|
222
|
+
* ```tsx
|
|
223
|
+
* <FormBuilder onSubmit={fn} resolver={r} defaultValues={d}>
|
|
224
|
+
* <FormBuilder.Section title="Identity" color="Blue">
|
|
225
|
+
* <FormBuilder.Text name="name" label="Name" required />
|
|
226
|
+
* </FormBuilder.Section>
|
|
227
|
+
* <FormBuilder.Submit>Save</FormBuilder.Submit>
|
|
228
|
+
* </FormBuilder>
|
|
229
|
+
* ```
|
|
230
|
+
*
|
|
231
|
+
* Steps are components (`FormBuilder.Stepper` + `FormBuilder.Step`). FormBuilder is
|
|
232
|
+
* drawer-unaware — to show a form in a drawer, wrap it in `FormRenderer`'s `FormDrawer`
|
|
233
|
+
* (or use `FormRenderer` with `display: "drawer"`).
|
|
234
|
+
*/
|
|
235
|
+
export const FormBuilder = Object.assign(FormBuilderRoot, {
|
|
236
|
+
// fields
|
|
237
|
+
Text: TextField,
|
|
238
|
+
Email: EmailField,
|
|
239
|
+
Password: PasswordField,
|
|
240
|
+
Number: NumberField,
|
|
241
|
+
Currency: CurrencyField,
|
|
242
|
+
Textarea: TextareaField,
|
|
243
|
+
Select: SelectField,
|
|
244
|
+
SearchableSelect: SearchableSelectField,
|
|
245
|
+
MultiSelect: MultiSelectField,
|
|
246
|
+
Tags: MultiSelectField,
|
|
247
|
+
RadioList: RadioListField,
|
|
248
|
+
RadioCards: RadioCardsField,
|
|
249
|
+
Checkbox: CheckboxField,
|
|
250
|
+
CheckboxGroup: CheckboxGroupField,
|
|
251
|
+
SwitchBox: SwitchBoxField,
|
|
252
|
+
Otp: OtpField,
|
|
253
|
+
TreeSelect: TreeSelectField,
|
|
254
|
+
Slider: SliderField,
|
|
255
|
+
Color: ColorField,
|
|
256
|
+
Signature: SignatureField,
|
|
257
|
+
Phone: PhoneField,
|
|
258
|
+
FieldArray: FieldArray,
|
|
259
|
+
Table: TableField,
|
|
260
|
+
Date: DateField,
|
|
261
|
+
DateRange: (props: Parameters<typeof DateField>[0]) => <DateField {...props} mode="range" />,
|
|
262
|
+
DateMultiple: (props: Parameters<typeof DateField>[0]) => (
|
|
263
|
+
<DateField {...props} mode="multiple" />
|
|
264
|
+
),
|
|
265
|
+
DateTime: (props: Parameters<typeof DateField>[0]) => <DateField {...props} timePicker />,
|
|
266
|
+
File: FileField,
|
|
267
|
+
Image: (props: Parameters<typeof FileField>[0]) => <FileField {...props} image />,
|
|
268
|
+
RichText: RichTextField,
|
|
269
|
+
Custom: CustomField,
|
|
270
|
+
// layout
|
|
271
|
+
Section,
|
|
272
|
+
Header,
|
|
273
|
+
Submit: SubmitButton,
|
|
274
|
+
// stepper
|
|
275
|
+
Stepper,
|
|
276
|
+
Step,
|
|
277
|
+
Back,
|
|
278
|
+
Next,
|
|
279
|
+
});
|