formshell 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/LICENSE +21 -0
- package/README.md +665 -0
- package/dist/global.d.ts +26 -0
- package/dist/index.d.ts +615 -0
- package/dist/index.js +1070 -0
- package/dist/index.js.map +1 -0
- package/package.json +48 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1070 @@
|
|
|
1
|
+
const i = {
|
|
2
|
+
// Main color palette (CSS styling for console.log)
|
|
3
|
+
colors: {
|
|
4
|
+
primary: "#6366f1",
|
|
5
|
+
// Indigo
|
|
6
|
+
secondary: "#8b5cf6",
|
|
7
|
+
// Purple
|
|
8
|
+
accent: "#ec4899",
|
|
9
|
+
// Pink
|
|
10
|
+
success: "#10b981",
|
|
11
|
+
// Green
|
|
12
|
+
error: "#ef4444",
|
|
13
|
+
// Red
|
|
14
|
+
warning: "#f59e0b",
|
|
15
|
+
// Orange
|
|
16
|
+
info: "#3b82f6",
|
|
17
|
+
// Blue
|
|
18
|
+
text: {
|
|
19
|
+
primary: "#f8fafc",
|
|
20
|
+
// Almost pure white
|
|
21
|
+
secondary: "#cbd5e1",
|
|
22
|
+
// Light gray
|
|
23
|
+
muted: "#64748b",
|
|
24
|
+
// Medium gray
|
|
25
|
+
dark: "#1e293b"
|
|
26
|
+
// Dark gray for background
|
|
27
|
+
},
|
|
28
|
+
background: {
|
|
29
|
+
main: "#0f172a",
|
|
30
|
+
// Dark blue
|
|
31
|
+
secondary: "#1e293b",
|
|
32
|
+
// Lighter dark blue
|
|
33
|
+
accent: "#334155"
|
|
34
|
+
// Blue gray
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
// Unicode icons for UI elements
|
|
38
|
+
icons: {
|
|
39
|
+
checkmark: "✓",
|
|
40
|
+
cross: "✗",
|
|
41
|
+
star: "★",
|
|
42
|
+
starEmpty: "☆",
|
|
43
|
+
arrow: "➤",
|
|
44
|
+
bullet: "◆",
|
|
45
|
+
bulletEmpty: "◇",
|
|
46
|
+
chevron: "▸",
|
|
47
|
+
heart: "♥",
|
|
48
|
+
info: "ℹ",
|
|
49
|
+
warning: "⚠",
|
|
50
|
+
question: "?",
|
|
51
|
+
cursor: "█",
|
|
52
|
+
cursorBlink: "▊",
|
|
53
|
+
spinner: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
|
|
54
|
+
},
|
|
55
|
+
// Box drawing characters (Unicode)
|
|
56
|
+
box: {
|
|
57
|
+
// Single lines
|
|
58
|
+
topLeft: "┌",
|
|
59
|
+
topRight: "┐",
|
|
60
|
+
bottomLeft: "└",
|
|
61
|
+
bottomRight: "┘",
|
|
62
|
+
horizontal: "─",
|
|
63
|
+
vertical: "│",
|
|
64
|
+
// Double lines (for emphasis)
|
|
65
|
+
doubleTopLeft: "╔",
|
|
66
|
+
doubleTopRight: "╗",
|
|
67
|
+
doubleBottomLeft: "╚",
|
|
68
|
+
doubleBottomRight: "╝",
|
|
69
|
+
doubleHorizontal: "═",
|
|
70
|
+
doubleVertical: "║",
|
|
71
|
+
// Progress bar characters
|
|
72
|
+
filled: "▓",
|
|
73
|
+
empty: "░",
|
|
74
|
+
halfFilled: "▒",
|
|
75
|
+
// Shadow/gradient characters
|
|
76
|
+
shadow: ["░", "▒", "▓"],
|
|
77
|
+
fade: ["⣀", "⣄", "⣤", "⣦", "⣶", "⣷", "⣿"]
|
|
78
|
+
},
|
|
79
|
+
// Text styles (using Unicode characters or ANSI)
|
|
80
|
+
text: {
|
|
81
|
+
bold: (n) => `\x1B[1m${n}\x1B[0m`,
|
|
82
|
+
italic: (n) => `\x1B[3m${n}\x1B[0m`,
|
|
83
|
+
underline: (n) => `\x1B[4m${n}\x1B[0m`,
|
|
84
|
+
// Alternative using Unicode mathematical characters (fallback)
|
|
85
|
+
boldUnicode: (n) => {
|
|
86
|
+
const e = {
|
|
87
|
+
a: "𝗮",
|
|
88
|
+
b: "𝗯",
|
|
89
|
+
c: "𝗰",
|
|
90
|
+
d: "𝗱",
|
|
91
|
+
e: "𝗲",
|
|
92
|
+
f: "𝗳",
|
|
93
|
+
g: "𝗴",
|
|
94
|
+
h: "𝗵",
|
|
95
|
+
i: "𝗶",
|
|
96
|
+
j: "𝗷",
|
|
97
|
+
k: "𝗸",
|
|
98
|
+
l: "𝗹",
|
|
99
|
+
m: "𝗺",
|
|
100
|
+
n: "𝗻",
|
|
101
|
+
o: "𝗼",
|
|
102
|
+
p: "𝗽",
|
|
103
|
+
q: "𝗾",
|
|
104
|
+
r: "𝗿",
|
|
105
|
+
s: "𝘀",
|
|
106
|
+
t: "𝘁",
|
|
107
|
+
u: "𝘂",
|
|
108
|
+
v: "𝘃",
|
|
109
|
+
w: "𝘄",
|
|
110
|
+
x: "𝘅",
|
|
111
|
+
y: "𝘆",
|
|
112
|
+
z: "𝘇",
|
|
113
|
+
A: "𝗔",
|
|
114
|
+
B: "𝗕",
|
|
115
|
+
C: "𝗖",
|
|
116
|
+
D: "𝗗",
|
|
117
|
+
E: "𝗘",
|
|
118
|
+
F: "𝗙",
|
|
119
|
+
G: "𝗚",
|
|
120
|
+
H: "𝗛",
|
|
121
|
+
I: "𝗜",
|
|
122
|
+
J: "𝗝",
|
|
123
|
+
K: "𝗞",
|
|
124
|
+
L: "𝗟",
|
|
125
|
+
M: "𝗠",
|
|
126
|
+
N: "𝗡",
|
|
127
|
+
O: "𝗢",
|
|
128
|
+
P: "𝗣",
|
|
129
|
+
Q: "𝗤",
|
|
130
|
+
R: "𝗥",
|
|
131
|
+
S: "𝗦",
|
|
132
|
+
T: "𝗧",
|
|
133
|
+
U: "𝗨",
|
|
134
|
+
V: "𝗩",
|
|
135
|
+
W: "𝗪",
|
|
136
|
+
X: "𝗫",
|
|
137
|
+
Y: "𝗬",
|
|
138
|
+
Z: "𝗭",
|
|
139
|
+
0: "𝟬",
|
|
140
|
+
1: "𝟭",
|
|
141
|
+
2: "𝟮",
|
|
142
|
+
3: "𝟯",
|
|
143
|
+
4: "𝟰",
|
|
144
|
+
5: "𝟱",
|
|
145
|
+
6: "𝟲",
|
|
146
|
+
7: "𝟳",
|
|
147
|
+
8: "𝟴",
|
|
148
|
+
9: "𝟵"
|
|
149
|
+
};
|
|
150
|
+
return n.split("").map((t) => e[t] || t).join("");
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
// Style presets for common elements
|
|
154
|
+
styles: {
|
|
155
|
+
title: {
|
|
156
|
+
color: "#6366f1",
|
|
157
|
+
fontSize: "20px",
|
|
158
|
+
fontWeight: "bold",
|
|
159
|
+
textShadow: "0 0 10px rgba(99, 102, 241, 0.5)"
|
|
160
|
+
},
|
|
161
|
+
subtitle: {
|
|
162
|
+
color: "#8b5cf6",
|
|
163
|
+
fontSize: "16px",
|
|
164
|
+
fontWeight: "600"
|
|
165
|
+
},
|
|
166
|
+
body: {
|
|
167
|
+
color: "#f8fafc",
|
|
168
|
+
fontSize: "14px",
|
|
169
|
+
lineHeight: "1.5"
|
|
170
|
+
},
|
|
171
|
+
success: {
|
|
172
|
+
color: "#10b981",
|
|
173
|
+
fontSize: "14px",
|
|
174
|
+
fontWeight: "bold"
|
|
175
|
+
},
|
|
176
|
+
error: {
|
|
177
|
+
color: "#ef4444",
|
|
178
|
+
fontSize: "14px",
|
|
179
|
+
fontWeight: "bold"
|
|
180
|
+
},
|
|
181
|
+
muted: {
|
|
182
|
+
color: "#64748b",
|
|
183
|
+
fontSize: "13px",
|
|
184
|
+
fontStyle: "italic"
|
|
185
|
+
},
|
|
186
|
+
highlight: {
|
|
187
|
+
color: "#ec4899",
|
|
188
|
+
fontSize: "14px",
|
|
189
|
+
fontWeight: "bold",
|
|
190
|
+
textShadow: "0 0 8px rgba(236, 72, 153, 0.4)"
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
// Helper to create CSS style string for console.log
|
|
194
|
+
createStyle: (n) => Object.entries(n).filter((e) => e[1] !== void 0).map(([e, t]) => `${e.replace(/([A-Z])/g, "-$1").toLowerCase()}: ${t}`).join("; "),
|
|
195
|
+
// Preset messages with styles
|
|
196
|
+
format: {
|
|
197
|
+
title: (n) => [`%c${n}`, i.createStyle(i.styles.title)],
|
|
198
|
+
subtitle: (n) => [`%c${n}`, i.createStyle(i.styles.subtitle)],
|
|
199
|
+
body: (n) => [`%c${n}`, i.createStyle(i.styles.body)],
|
|
200
|
+
success: (n) => [`%c${i.icons.checkmark} ${n}`, i.createStyle(i.styles.success)],
|
|
201
|
+
error: (n) => [`%c${i.icons.cross} ${n}`, i.createStyle(i.styles.error)],
|
|
202
|
+
muted: (n) => [`%c${n}`, i.createStyle(i.styles.muted)],
|
|
203
|
+
highlight: (n) => [`%c${n}`, i.createStyle(i.styles.highlight)],
|
|
204
|
+
// Quick custom colors
|
|
205
|
+
colored: (n, e) => [`%c${n}`, `color: ${e}; font-size: 14px;`]
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
class d {
|
|
209
|
+
constructor(e) {
|
|
210
|
+
this.type = e.type, this.id = e.id, this.label = e.label, this.required = e.required !== !1, this.value = e.defaultValue ?? null, this.error = null, this.condition = "condition" in e ? e.condition : void 0;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Validate the field (to be overridden in subclasses)
|
|
214
|
+
*/
|
|
215
|
+
validate(e) {
|
|
216
|
+
return this.required && (e == null || e === "") ? {
|
|
217
|
+
valid: !1,
|
|
218
|
+
error: "This field is required"
|
|
219
|
+
} : { valid: !0 };
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Format the value for display
|
|
223
|
+
*/
|
|
224
|
+
format(e) {
|
|
225
|
+
return String(e ?? "");
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Get the current value
|
|
229
|
+
*/
|
|
230
|
+
getValue() {
|
|
231
|
+
return this.value;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Set the value
|
|
235
|
+
*/
|
|
236
|
+
setValue(e) {
|
|
237
|
+
const t = this.validate(e);
|
|
238
|
+
return t.valid ? (this.value = e, this.error = null, !0) : (this.error = t.error, !1);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
class a extends d {
|
|
242
|
+
constructor(e) {
|
|
243
|
+
super(e), this.minLength = "minLength" in e ? e.minLength ?? null : null, this.maxLength = "maxLength" in e ? e.maxLength ?? null : null, this.pattern = "pattern" in e ? e.pattern ?? null : null;
|
|
244
|
+
}
|
|
245
|
+
validate(e) {
|
|
246
|
+
const t = super.validate(e);
|
|
247
|
+
if (!t.valid) return t;
|
|
248
|
+
if (!e && !this.required)
|
|
249
|
+
return { valid: !0 };
|
|
250
|
+
const r = String(e);
|
|
251
|
+
return this.minLength && r.length < this.minLength ? {
|
|
252
|
+
valid: !1,
|
|
253
|
+
error: `Minimum ${this.minLength} characters required`
|
|
254
|
+
} : this.maxLength && r.length > this.maxLength ? {
|
|
255
|
+
valid: !1,
|
|
256
|
+
error: `Maximum ${this.maxLength} characters allowed`
|
|
257
|
+
} : this.pattern && !this.pattern.test(r) ? {
|
|
258
|
+
valid: !1,
|
|
259
|
+
error: "Invalid format"
|
|
260
|
+
} : { valid: !0 };
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
class u extends d {
|
|
264
|
+
constructor(e) {
|
|
265
|
+
super(e), this.min = "min" in e && e.min !== void 0 ? e.min : null, this.max = "max" in e && e.max !== void 0 ? e.max : null, this.integer = "integer" in e ? e.integer ?? !1 : !1;
|
|
266
|
+
}
|
|
267
|
+
validate(e) {
|
|
268
|
+
const t = super.validate(e);
|
|
269
|
+
if (!t.valid) return t;
|
|
270
|
+
if (!e && !this.required)
|
|
271
|
+
return { valid: !0 };
|
|
272
|
+
const r = Number(e);
|
|
273
|
+
return isNaN(r) ? {
|
|
274
|
+
valid: !1,
|
|
275
|
+
error: "Must be a valid number"
|
|
276
|
+
} : this.integer && !Number.isInteger(r) ? {
|
|
277
|
+
valid: !1,
|
|
278
|
+
error: "Must be an integer"
|
|
279
|
+
} : this.min !== null && r < this.min ? {
|
|
280
|
+
valid: !1,
|
|
281
|
+
error: `Minimum value: ${this.min}`
|
|
282
|
+
} : this.max !== null && r > this.max ? {
|
|
283
|
+
valid: !1,
|
|
284
|
+
error: `Maximum value: ${this.max}`
|
|
285
|
+
} : { valid: !0 };
|
|
286
|
+
}
|
|
287
|
+
format(e) {
|
|
288
|
+
return e != null ? String(e) : "";
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
class V extends a {
|
|
292
|
+
constructor(e) {
|
|
293
|
+
super(e), this.pattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
294
|
+
}
|
|
295
|
+
validate(e) {
|
|
296
|
+
const t = super.validate(e);
|
|
297
|
+
return t.valid ? t : {
|
|
298
|
+
valid: !1,
|
|
299
|
+
error: t.error === "Invalid format" ? "Invalid email address" : t.error
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
class I extends a {
|
|
304
|
+
constructor(e) {
|
|
305
|
+
super(e), this.pattern = /^https?:\/\/.+\..+/;
|
|
306
|
+
}
|
|
307
|
+
validate(e) {
|
|
308
|
+
const t = super.validate(e);
|
|
309
|
+
return t.valid ? t : {
|
|
310
|
+
valid: !1,
|
|
311
|
+
error: t.error === "Invalid format" ? "Invalid URL (must start with http:// or https://)" : t.error
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
class T extends a {
|
|
316
|
+
constructor(e) {
|
|
317
|
+
super(e), this.pattern = /^(0[1-9]|[12][0-9]|3[01])\/(0[1-9]|1[012])\/(19|20)\d\d$/;
|
|
318
|
+
}
|
|
319
|
+
validate(e) {
|
|
320
|
+
const t = super.validate(e);
|
|
321
|
+
if (!t.valid)
|
|
322
|
+
return {
|
|
323
|
+
valid: !1,
|
|
324
|
+
error: t.error === "Invalid format" ? "Invalid date (format: DD/MM/YYYY)" : t.error
|
|
325
|
+
};
|
|
326
|
+
if (e && typeof e == "string" && this.pattern.test(e)) {
|
|
327
|
+
const [r, s, o] = e.split("/").map(Number), l = new Date(o, s - 1, r);
|
|
328
|
+
if (l.getDate() !== r || l.getMonth() !== s - 1 || l.getFullYear() !== o)
|
|
329
|
+
return {
|
|
330
|
+
valid: !1,
|
|
331
|
+
error: "Invalid date (format: DD/MM/YYYY)"
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
return t;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
class S extends d {
|
|
338
|
+
constructor(e) {
|
|
339
|
+
super(e), this.options = "options" in e ? e.options ?? [] : [];
|
|
340
|
+
}
|
|
341
|
+
validate(e) {
|
|
342
|
+
const t = super.validate(e);
|
|
343
|
+
if (!t.valid) return t;
|
|
344
|
+
if (!e && !this.required)
|
|
345
|
+
return { valid: !0 };
|
|
346
|
+
if (typeof e == "number")
|
|
347
|
+
return e < 1 || e > this.options.length ? {
|
|
348
|
+
valid: !1,
|
|
349
|
+
error: `Choose a number between 1 and ${this.options.length}`
|
|
350
|
+
} : { valid: !0 };
|
|
351
|
+
const r = this.options.map(
|
|
352
|
+
(s) => typeof s == "string" ? s : s.value
|
|
353
|
+
);
|
|
354
|
+
return typeof e == "string" && !r.includes(e) ? {
|
|
355
|
+
valid: !1,
|
|
356
|
+
error: "Invalid choice"
|
|
357
|
+
} : { valid: !0 };
|
|
358
|
+
}
|
|
359
|
+
setValue(e) {
|
|
360
|
+
if (typeof e == "number") {
|
|
361
|
+
const t = e - 1;
|
|
362
|
+
if (t >= 0 && t < this.options.length) {
|
|
363
|
+
const r = this.options[t], s = typeof r == "string" ? r : r.value;
|
|
364
|
+
return super.setValue(s);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
return super.setValue(e);
|
|
368
|
+
}
|
|
369
|
+
format(e) {
|
|
370
|
+
if (!e) return "";
|
|
371
|
+
const t = this.options.find(
|
|
372
|
+
(r) => (typeof r == "string" ? r : r.value) === e
|
|
373
|
+
);
|
|
374
|
+
return typeof t == "string" ? t : (t == null ? void 0 : t.label) || String(e);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
class x extends d {
|
|
378
|
+
constructor(e) {
|
|
379
|
+
super(e), this.options = "options" in e ? e.options ?? [] : [], this.minChoices = "minChoices" in e ? e.minChoices ?? (this.required ? 1 : 0) : this.required ? 1 : 0, this.maxChoices = "maxChoices" in e ? e.maxChoices ?? this.options.length : this.options.length;
|
|
380
|
+
}
|
|
381
|
+
validate(e) {
|
|
382
|
+
if (!Array.isArray(e))
|
|
383
|
+
return this.required ? {
|
|
384
|
+
valid: !1,
|
|
385
|
+
error: "Select at least 1 option(s)"
|
|
386
|
+
} : { valid: !0 };
|
|
387
|
+
if (e.length < this.minChoices)
|
|
388
|
+
return {
|
|
389
|
+
valid: !1,
|
|
390
|
+
error: `Select at least ${this.minChoices} option(s)`
|
|
391
|
+
};
|
|
392
|
+
if (e.length > this.maxChoices)
|
|
393
|
+
return {
|
|
394
|
+
valid: !1,
|
|
395
|
+
error: `Select maximum ${this.maxChoices} option(s)`
|
|
396
|
+
};
|
|
397
|
+
const t = this.options.map(
|
|
398
|
+
(r) => typeof r == "string" ? r : r.value
|
|
399
|
+
);
|
|
400
|
+
for (const r of e)
|
|
401
|
+
if (!t.includes(r))
|
|
402
|
+
return {
|
|
403
|
+
valid: !1,
|
|
404
|
+
error: "One or more choices are invalid"
|
|
405
|
+
};
|
|
406
|
+
return { valid: !0 };
|
|
407
|
+
}
|
|
408
|
+
setValue(e) {
|
|
409
|
+
if (typeof e == "string") {
|
|
410
|
+
const t = e.split(/[,\s]+/).map((s) => s.trim()).filter((s) => s), r = [];
|
|
411
|
+
for (const s of t) {
|
|
412
|
+
const o = Number(s);
|
|
413
|
+
if (!isNaN(o) && o >= 1 && o <= this.options.length) {
|
|
414
|
+
const l = this.options[o - 1];
|
|
415
|
+
r.push(typeof l == "string" ? l : l.value);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return super.setValue(r);
|
|
419
|
+
}
|
|
420
|
+
return super.setValue(e);
|
|
421
|
+
}
|
|
422
|
+
format(e) {
|
|
423
|
+
return !Array.isArray(e) || e.length === 0 ? "No selection" : e.map((r) => {
|
|
424
|
+
const s = this.options.find(
|
|
425
|
+
(o) => (typeof o == "string" ? o : o.value) === r
|
|
426
|
+
);
|
|
427
|
+
return typeof s == "string" ? s : (s == null ? void 0 : s.label) || r;
|
|
428
|
+
}).join(", ");
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
class y extends u {
|
|
432
|
+
constructor(e) {
|
|
433
|
+
super(e), this.min = "min" in e && e.min !== void 0 ? e.min : 1, this.max = "max" in e && e.max !== void 0 ? e.max : 5, this.integer = !0;
|
|
434
|
+
}
|
|
435
|
+
validate(e) {
|
|
436
|
+
const t = super.validate(e);
|
|
437
|
+
return t.valid ? t : {
|
|
438
|
+
valid: !1,
|
|
439
|
+
error: t.error.includes("number") ? `Enter a number from ${this.min} to ${this.max}` : t.error
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
format(e) {
|
|
443
|
+
return !e || typeof e != "number" ? "" : `${i.icons.star.repeat(e) + i.icons.starEmpty.repeat((this.max ?? 5) - e)} (${e}/${this.max})`;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
class C extends d {
|
|
447
|
+
constructor(e) {
|
|
448
|
+
super(e), this.value = e.defaultValue || null;
|
|
449
|
+
}
|
|
450
|
+
validate(e) {
|
|
451
|
+
const t = super.validate(e);
|
|
452
|
+
if (!t.valid) return t;
|
|
453
|
+
if (typeof e == "boolean")
|
|
454
|
+
return { valid: !0 };
|
|
455
|
+
if (typeof e == "string") {
|
|
456
|
+
const r = e.toLowerCase().trim();
|
|
457
|
+
if (["y", "yes", "s", "si", "sì", "n", "no"].includes(r))
|
|
458
|
+
return { valid: !0 };
|
|
459
|
+
}
|
|
460
|
+
return {
|
|
461
|
+
valid: !1,
|
|
462
|
+
error: "Answer with Y (yes) or N (no)"
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
setValue(e) {
|
|
466
|
+
if (typeof e == "string") {
|
|
467
|
+
const t = e.toLowerCase().trim();
|
|
468
|
+
if (["y", "yes", "s", "si", "sì"].includes(t))
|
|
469
|
+
return this.value = !0, this.error = null, !0;
|
|
470
|
+
if (["n", "no"].includes(t))
|
|
471
|
+
return this.value = !1, this.error = null, !0;
|
|
472
|
+
}
|
|
473
|
+
return typeof e == "boolean" ? (this.value = e, this.error = null, !0) : (this.error = "Answer with Y (yes) or N (no)", !1);
|
|
474
|
+
}
|
|
475
|
+
format(e) {
|
|
476
|
+
return e === !0 ? "Yes" : e === !1 ? "No" : "";
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
const L = {
|
|
480
|
+
create(n) {
|
|
481
|
+
const e = (n.type || "text").toLowerCase();
|
|
482
|
+
switch (e) {
|
|
483
|
+
case "text":
|
|
484
|
+
return new a(n);
|
|
485
|
+
case "number":
|
|
486
|
+
return new u(n);
|
|
487
|
+
case "email":
|
|
488
|
+
return new V(n);
|
|
489
|
+
case "url":
|
|
490
|
+
return new I(n);
|
|
491
|
+
case "date":
|
|
492
|
+
return new T(n);
|
|
493
|
+
case "choice":
|
|
494
|
+
return new S(n);
|
|
495
|
+
case "multiple-choice":
|
|
496
|
+
case "multiplechoice":
|
|
497
|
+
return new x(n);
|
|
498
|
+
case "rating":
|
|
499
|
+
return new y(n);
|
|
500
|
+
case "yesno":
|
|
501
|
+
case "yes-no":
|
|
502
|
+
return new C(n);
|
|
503
|
+
default:
|
|
504
|
+
return console.warn(`Unknown field type: ${e}, defaulting to text`), new a(n);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
}, N = "\x1B";
|
|
508
|
+
class A {
|
|
509
|
+
constructor() {
|
|
510
|
+
this.cursorVisible = !0, this.cursorInterval = null;
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Initialize the renderer and start cursor blinking
|
|
514
|
+
*/
|
|
515
|
+
init() {
|
|
516
|
+
this.startCursorBlink();
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* Clear the console
|
|
520
|
+
*/
|
|
521
|
+
clear() {
|
|
522
|
+
console.clear();
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Start the cursor blinking animation
|
|
526
|
+
*/
|
|
527
|
+
startCursorBlink() {
|
|
528
|
+
this.cursorInterval && clearInterval(this.cursorInterval), this.cursorInterval = window.setInterval(() => {
|
|
529
|
+
this.cursorVisible = !this.cursorVisible;
|
|
530
|
+
}, 530);
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* Stop the cursor animation
|
|
534
|
+
*/
|
|
535
|
+
stopCursorBlink() {
|
|
536
|
+
this.cursorInterval && (clearInterval(this.cursorInterval), this.cursorInterval = null);
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Get the current cursor character
|
|
540
|
+
*/
|
|
541
|
+
getCursor() {
|
|
542
|
+
return this.cursorVisible ? i.icons.cursor : " ";
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* Create a decorative horizontal line
|
|
546
|
+
*/
|
|
547
|
+
createHorizontalLine(e = 60, t = i.box.horizontal) {
|
|
548
|
+
return t.repeat(e);
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* Create a box with Unicode borders
|
|
552
|
+
*/
|
|
553
|
+
createBox(e, t = 60, r = "single") {
|
|
554
|
+
const s = [], o = r === "double", l = o ? i.box.doubleTopLeft : i.box.topLeft, h = o ? i.box.doubleTopRight : i.box.topRight, c = o ? i.box.doubleBottomLeft : i.box.bottomLeft, v = o ? i.box.doubleBottomRight : i.box.bottomRight, m = o ? i.box.doubleHorizontal : i.box.horizontal, p = o ? i.box.doubleVertical : i.box.vertical;
|
|
555
|
+
return s.push(l + m.repeat(t - 2) + h), (Array.isArray(e) ? e : [e]).forEach(($) => {
|
|
556
|
+
const f = String($), w = new RegExp(`${N}\\[[0-9;]*m`, "g"), M = f.replace(w, ""), g = t - 2 - M.length, b = Math.floor(g / 2), E = g - b;
|
|
557
|
+
s.push(p + " ".repeat(b) + f + " ".repeat(E) + p);
|
|
558
|
+
}), s.push(c + m.repeat(t - 2) + v), s;
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Create an elegant progress bar
|
|
562
|
+
*/
|
|
563
|
+
createProgressBar(e, t, r = 40) {
|
|
564
|
+
const s = Math.round(e / t * 100), o = Math.round(e / t * r), l = r - o, h = i.box.filled.repeat(o), c = i.box.empty.repeat(l);
|
|
565
|
+
return {
|
|
566
|
+
bar: h + c,
|
|
567
|
+
percentage: s,
|
|
568
|
+
text: `${e}/${t}`
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Render the main title with style
|
|
573
|
+
*/
|
|
574
|
+
renderTitle(e) {
|
|
575
|
+
const [t, r] = i.format.title(e);
|
|
576
|
+
console.log(t, r);
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Render the subtitle with style
|
|
580
|
+
*/
|
|
581
|
+
renderSubtitle(e) {
|
|
582
|
+
const [t, r] = i.format.subtitle(e);
|
|
583
|
+
console.log(t, r);
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* Render normal text
|
|
587
|
+
*/
|
|
588
|
+
renderText(e, t = null) {
|
|
589
|
+
if (t) {
|
|
590
|
+
const [r, s] = i.format.colored(e, t);
|
|
591
|
+
console.log(r, s);
|
|
592
|
+
} else {
|
|
593
|
+
const [r, s] = i.format.body(e);
|
|
594
|
+
console.log(r, s);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* Render a success message
|
|
599
|
+
*/
|
|
600
|
+
renderSuccess(e) {
|
|
601
|
+
const [t, r] = i.format.success(e);
|
|
602
|
+
console.log(t, r);
|
|
603
|
+
}
|
|
604
|
+
/**
|
|
605
|
+
* Render an error message
|
|
606
|
+
*/
|
|
607
|
+
renderError(e) {
|
|
608
|
+
const [t, r] = i.format.error(e);
|
|
609
|
+
console.log(t, r);
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* Render muted/secondary text
|
|
613
|
+
*/
|
|
614
|
+
renderMuted(e) {
|
|
615
|
+
const [t, r] = i.format.muted(e);
|
|
616
|
+
console.log(t, r);
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Render highlighted text
|
|
620
|
+
*/
|
|
621
|
+
renderHighlight(e) {
|
|
622
|
+
const [t, r] = i.format.highlight(e);
|
|
623
|
+
console.log(t, r);
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Render a complete box
|
|
627
|
+
*/
|
|
628
|
+
renderBox(e, t = 60, r = "single") {
|
|
629
|
+
this.createBox(e, t, r).forEach((o) => {
|
|
630
|
+
this.renderText(o, i.colors.text.secondary);
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* Render a separator line
|
|
635
|
+
*/
|
|
636
|
+
renderSeparator(e = 60, t = i.box.horizontal) {
|
|
637
|
+
const r = this.createHorizontalLine(e, t);
|
|
638
|
+
this.renderMuted(r);
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Render the progress bar with info
|
|
642
|
+
*/
|
|
643
|
+
renderProgress(e, t, r = "Progress") {
|
|
644
|
+
const s = this.createProgressBar(e, t);
|
|
645
|
+
console.log(""), this.renderMuted(`${r}: ${s.text} (${s.percentage}%)`), this.renderHighlight(s.bar), console.log("");
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Render a list of options
|
|
649
|
+
*/
|
|
650
|
+
renderOptions(e, t = null) {
|
|
651
|
+
e.forEach((r, s) => {
|
|
652
|
+
const o = s + 1;
|
|
653
|
+
t === s ? this.renderHighlight(` ${o}. ${r}`) : this.renderText(` ${o}. ${r}`, i.colors.text.secondary);
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
/**
|
|
657
|
+
* Render an input field with cursor
|
|
658
|
+
*/
|
|
659
|
+
renderInput(e, t = "", r = !0) {
|
|
660
|
+
const s = r ? this.getCursor() : "", o = t + s;
|
|
661
|
+
console.log(""), this.renderHighlight(`${i.icons.chevron} ${e}`), console.log(""), this.renderText(` ${o}`, i.colors.primary), console.log("");
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Render a yes/no question
|
|
665
|
+
*/
|
|
666
|
+
renderYesNo(e, t = null, r = "Yes", s = "No") {
|
|
667
|
+
console.log(""), this.renderHighlight(`${i.icons.question} ${e}`), console.log("");
|
|
668
|
+
const o = ` ${i.icons.bulletEmpty} Y - ${r}`, l = ` ${i.icons.bulletEmpty} N - ${s}`;
|
|
669
|
+
t === !0 ? (this.renderHighlight(o), this.renderMuted(l)) : t === !1 ? (this.renderMuted(o), this.renderHighlight(l)) : (this.renderMuted(o), this.renderMuted(l)), console.log("");
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Render rating with stars
|
|
673
|
+
*/
|
|
674
|
+
renderRating(e, t = 0, r = 5) {
|
|
675
|
+
console.log(""), this.renderHighlight(`${i.icons.star} ${e}`), console.log("");
|
|
676
|
+
let s = "";
|
|
677
|
+
for (let o = 1; o <= r; o++)
|
|
678
|
+
o <= t ? s += i.icons.star + " " : s += i.icons.starEmpty + " ";
|
|
679
|
+
this.renderText(` ${s}`, i.colors.warning), this.renderMuted(` (${t}/${r})`), console.log("");
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* Render a summary with key-value data
|
|
683
|
+
*/
|
|
684
|
+
renderSummary(e, t) {
|
|
685
|
+
console.log(""), this.renderTitle(e), this.renderSeparator(60), console.log(""), Object.entries(t).forEach(([r, s]) => {
|
|
686
|
+
const o = r.charAt(0).toUpperCase() + r.slice(1);
|
|
687
|
+
this.renderText(`${i.icons.bullet} ${o}:`, i.colors.text.secondary), this.renderHighlight(` ${s}`), console.log("");
|
|
688
|
+
}), this.renderSeparator(60);
|
|
689
|
+
}
|
|
690
|
+
/**
|
|
691
|
+
* Render a help/commands message
|
|
692
|
+
*/
|
|
693
|
+
renderHelp(e, t = "Available Commands") {
|
|
694
|
+
console.log(""), this.renderTitle(`📋 ${t}`), this.renderSeparator(60), console.log(""), e.forEach(({ command: r, description: s }) => {
|
|
695
|
+
this.renderHighlight(` ${i.icons.chevron} ${r}`), this.renderMuted(` ${s}`), console.log("");
|
|
696
|
+
}), this.renderSeparator(60);
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
* Render an animated welcome message
|
|
700
|
+
*/
|
|
701
|
+
renderWelcome(e, t = null) {
|
|
702
|
+
this.clear(), console.log(""), console.log(""), this.renderTitle(`✨ ${e} ✨`), t && (console.log(""), this.renderSubtitle(t)), console.log(""), this.renderSeparator(60, i.box.doubleHorizontal), console.log("");
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
705
|
+
* Render a spinner/loader (static, not animated)
|
|
706
|
+
*/
|
|
707
|
+
renderLoader(e = "Loading...") {
|
|
708
|
+
const t = i.icons.spinner[0];
|
|
709
|
+
this.renderText(`${t} ${e}`, i.colors.info);
|
|
710
|
+
}
|
|
711
|
+
/**
|
|
712
|
+
* Cleanup the renderer
|
|
713
|
+
*/
|
|
714
|
+
destroy() {
|
|
715
|
+
this.stopCursorBlink();
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
class k {
|
|
719
|
+
constructor(e) {
|
|
720
|
+
this.title = e.title || "Form", this.subtitle = e.subtitle || null, this.endpoint = e.endpoint || null, this.onComplete = e.onComplete || null, this.steps = this.initializeSteps(e.steps || []), this.currentStepIndex = -1, this.renderer = new A(), this.started = !1, this.completed = !1, this.startTime = null, this.helpActive = !1, this.renderer.init(), this.showWelcome();
|
|
721
|
+
}
|
|
722
|
+
/**
|
|
723
|
+
* Initialize steps with appropriate field types
|
|
724
|
+
*/
|
|
725
|
+
initializeSteps(e) {
|
|
726
|
+
return e.map((t, r) => {
|
|
727
|
+
const s = L.create(t);
|
|
728
|
+
return {
|
|
729
|
+
id: t.id || `step_${r}`,
|
|
730
|
+
field: s,
|
|
731
|
+
description: t.description ?? null,
|
|
732
|
+
answered: !1
|
|
733
|
+
};
|
|
734
|
+
});
|
|
735
|
+
}
|
|
736
|
+
/**
|
|
737
|
+
* Show welcome screen
|
|
738
|
+
*/
|
|
739
|
+
showWelcome() {
|
|
740
|
+
this.renderer.clear(), console.log(""), this.renderer.renderTitle(this.title), this.subtitle && this.renderer.renderMuted(this.subtitle), console.log(""), this.renderer.renderMuted("Type formShell.start() to begin or formShell.help() for available commands"), console.log("");
|
|
741
|
+
}
|
|
742
|
+
/**
|
|
743
|
+
* Show help screen with commands.
|
|
744
|
+
* Can be called at any time without interrupting the form flow.
|
|
745
|
+
* Use formShell.continue() to resume where you left off.
|
|
746
|
+
*/
|
|
747
|
+
help() {
|
|
748
|
+
this.helpActive = !0, this.renderer.clear(), console.log(""), this.renderer.renderTitle(this.title), this.subtitle && this.renderer.renderMuted(this.subtitle), console.log(""), this.renderer.renderHighlight("Commands:"), this.renderer.renderMuted(" formShell.start() Start the form"), this.renderer.renderMuted(" formShell.continue() Resume the form"), this.renderer.renderMuted(" formShell.answer(...) Answer and proceed"), this.renderer.renderMuted(" formShell.skip() Skip (if optional)"), this.renderer.renderMuted(" formShell.y() / formShell.n() Yes / No"), this.renderer.renderMuted(" formShell.back() Go back"), this.renderer.renderMuted(" formShell.submit() Submit (at the end)"), this.renderer.renderMuted(" formShell.reset() Start over"), this.renderer.renderMuted(" formShell.help() Show this help"), console.log(""), this.renderer.renderMuted("Type formShell.continue() to resume"), console.log("");
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Start the form (only from welcome screen)
|
|
752
|
+
*/
|
|
753
|
+
start() {
|
|
754
|
+
if (this.helpActive) {
|
|
755
|
+
this.renderer.renderError("Use formShell.continue() to resume the form first");
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
758
|
+
if (this.started) {
|
|
759
|
+
this.renderer.renderError("Form already started. Use formShell.reset() to start over");
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
const e = this.getNextVisibleStep(-1);
|
|
763
|
+
if (e >= this.steps.length) {
|
|
764
|
+
this.renderer.renderError("No visible steps in form");
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
this.currentStepIndex = e, this.started = !0, this.startTime = Date.now(), this.renderCurrentStep();
|
|
768
|
+
}
|
|
769
|
+
/**
|
|
770
|
+
* Resume the form after viewing help
|
|
771
|
+
*/
|
|
772
|
+
continue() {
|
|
773
|
+
if (!this.helpActive) {
|
|
774
|
+
this.started ? this.renderer.renderError("Use formShell.answer() to respond to the current question") : this.renderer.renderError("Use formShell.start() to begin the form");
|
|
775
|
+
return;
|
|
776
|
+
}
|
|
777
|
+
this.helpActive = !1, this.started ? this.completed ? this.showSummary() : this.renderCurrentStep() : this.showWelcome();
|
|
778
|
+
}
|
|
779
|
+
/**
|
|
780
|
+
* Check if a step should be shown based on its condition
|
|
781
|
+
*/
|
|
782
|
+
shouldShowStep(e) {
|
|
783
|
+
const t = this.steps[e];
|
|
784
|
+
if (!t.field.condition)
|
|
785
|
+
return !0;
|
|
786
|
+
const r = this.collectData();
|
|
787
|
+
return t.field.condition(r);
|
|
788
|
+
}
|
|
789
|
+
/**
|
|
790
|
+
* Get next visible step index
|
|
791
|
+
*/
|
|
792
|
+
getNextVisibleStep(e) {
|
|
793
|
+
for (let t = e + 1; t < this.steps.length; t++)
|
|
794
|
+
if (this.shouldShowStep(t))
|
|
795
|
+
return t;
|
|
796
|
+
return this.steps.length;
|
|
797
|
+
}
|
|
798
|
+
/**
|
|
799
|
+
* Get previous visible step index
|
|
800
|
+
*/
|
|
801
|
+
getPreviousVisibleStep(e) {
|
|
802
|
+
for (let t = e - 1; t >= 0; t--)
|
|
803
|
+
if (this.shouldShowStep(t))
|
|
804
|
+
return t;
|
|
805
|
+
return -1;
|
|
806
|
+
}
|
|
807
|
+
/**
|
|
808
|
+
* Advance to next step (internal method)
|
|
809
|
+
*/
|
|
810
|
+
_advanceStep() {
|
|
811
|
+
if (this.completed)
|
|
812
|
+
return;
|
|
813
|
+
const e = this.getNextVisibleStep(this.currentStepIndex);
|
|
814
|
+
e < this.steps.length ? (this.currentStepIndex = e, this.renderCurrentStep()) : (this.completed = !0, this.showSummary());
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
817
|
+
* Go back to previous step
|
|
818
|
+
*/
|
|
819
|
+
back() {
|
|
820
|
+
if (this.helpActive) {
|
|
821
|
+
this.renderer.renderError("Use formShell.continue() to resume the form first");
|
|
822
|
+
return;
|
|
823
|
+
}
|
|
824
|
+
const e = this.getPreviousVisibleStep(this.currentStepIndex);
|
|
825
|
+
if (e < 0) {
|
|
826
|
+
this.renderer.renderError("Already at first question. Use formShell.help() for commands");
|
|
827
|
+
return;
|
|
828
|
+
}
|
|
829
|
+
this.currentStepIndex = e, this.completed = !1, this.renderCurrentStep();
|
|
830
|
+
}
|
|
831
|
+
/**
|
|
832
|
+
* Answer the current question
|
|
833
|
+
*/
|
|
834
|
+
answer(e) {
|
|
835
|
+
if (this.helpActive) {
|
|
836
|
+
this.renderer.renderError("Use formShell.continue() to resume the form first");
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
839
|
+
if (!this.started) {
|
|
840
|
+
this.renderer.renderError("Use formShell.start() to begin the form");
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
if (this.completed) {
|
|
844
|
+
this.renderer.renderError("Form already completed! Use formShell.submit() to send or formShell.reset() to start over");
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
const t = this.steps[this.currentStepIndex], r = t.field;
|
|
848
|
+
r.setValue(e) ? (t.answered = !0, this.renderer.renderSuccess(`${r.format(r.getValue())}`), console.log(""), setTimeout(() => {
|
|
849
|
+
this._advanceStep();
|
|
850
|
+
}, 1e3)) : (this.renderer.renderError(r.error ?? "Invalid value"), console.log(""));
|
|
851
|
+
}
|
|
852
|
+
/**
|
|
853
|
+
* Shortcut to answer "Yes"
|
|
854
|
+
*/
|
|
855
|
+
y() {
|
|
856
|
+
this.answer("y");
|
|
857
|
+
}
|
|
858
|
+
/**
|
|
859
|
+
* Shortcut to answer "No"
|
|
860
|
+
*/
|
|
861
|
+
n() {
|
|
862
|
+
this.answer("n");
|
|
863
|
+
}
|
|
864
|
+
/**
|
|
865
|
+
* Skip the current question (only if not required)
|
|
866
|
+
*/
|
|
867
|
+
skip() {
|
|
868
|
+
if (this.helpActive) {
|
|
869
|
+
this.renderer.renderError("Use formShell.continue() to resume the form first");
|
|
870
|
+
return;
|
|
871
|
+
}
|
|
872
|
+
if (!this.started) {
|
|
873
|
+
this.renderer.renderError("Use formShell.start() to begin the form");
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
if (this.completed) {
|
|
877
|
+
this.renderer.renderError("Form already completed! Use formShell.submit() to send or formShell.reset() to start over");
|
|
878
|
+
return;
|
|
879
|
+
}
|
|
880
|
+
const e = this.steps[this.currentStepIndex], t = e.field;
|
|
881
|
+
if (t.required) {
|
|
882
|
+
this.renderer.renderError("This question is required and cannot be skipped"), console.log("");
|
|
883
|
+
return;
|
|
884
|
+
}
|
|
885
|
+
e.answered = !0, t.value = null, this.renderer.renderMuted("Skipped"), console.log(""), setTimeout(() => {
|
|
886
|
+
this._advanceStep();
|
|
887
|
+
}, 1e3);
|
|
888
|
+
}
|
|
889
|
+
/**
|
|
890
|
+
* Reset the form
|
|
891
|
+
*/
|
|
892
|
+
reset() {
|
|
893
|
+
this.currentStepIndex = -1, this.started = !1, this.completed = !1, this.startTime = null, this.helpActive = !1, this.steps.forEach((e) => {
|
|
894
|
+
e.answered = !1, e.field.value = null, e.field.error = null;
|
|
895
|
+
}), this.showWelcome();
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
898
|
+
* Submit data to server
|
|
899
|
+
*/
|
|
900
|
+
async submit() {
|
|
901
|
+
if (this.helpActive) {
|
|
902
|
+
this.renderer.renderError("Use formShell.continue() to resume the form first");
|
|
903
|
+
return;
|
|
904
|
+
}
|
|
905
|
+
if (!this.completed) {
|
|
906
|
+
this.renderer.renderError("Complete all questions first!");
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
909
|
+
const e = this.collectData();
|
|
910
|
+
console.log(""), this.renderer.renderMuted("Submitting...");
|
|
911
|
+
try {
|
|
912
|
+
if (this.endpoint) {
|
|
913
|
+
const t = await fetch(this.endpoint, {
|
|
914
|
+
method: "POST",
|
|
915
|
+
headers: {
|
|
916
|
+
"Content-Type": "application/json"
|
|
917
|
+
},
|
|
918
|
+
body: JSON.stringify(e)
|
|
919
|
+
});
|
|
920
|
+
if (!t.ok)
|
|
921
|
+
throw new Error(`HTTP error! status: ${t.status}`);
|
|
922
|
+
const r = await t.json();
|
|
923
|
+
this.renderer.renderSuccess("Submitted!"), console.log(""), this.onComplete && await this.onComplete(r), console.log("Response:", r);
|
|
924
|
+
} else
|
|
925
|
+
this.renderer.renderSuccess("Completed!"), console.log(""), console.log("Data:", e), this.onComplete && await this.onComplete(e);
|
|
926
|
+
} catch (t) {
|
|
927
|
+
const r = t instanceof Error ? t.message : String(t);
|
|
928
|
+
this.renderer.renderError(`Error: ${r}`), console.log("");
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
/**
|
|
932
|
+
* Collect all form data
|
|
933
|
+
*/
|
|
934
|
+
collectData() {
|
|
935
|
+
const e = {};
|
|
936
|
+
return this.steps.forEach((t) => {
|
|
937
|
+
e[t.field.id] = t.field.getValue();
|
|
938
|
+
}), e;
|
|
939
|
+
}
|
|
940
|
+
/**
|
|
941
|
+
* Calculate completion percentage
|
|
942
|
+
*/
|
|
943
|
+
getProgress() {
|
|
944
|
+
const e = this.steps.filter(
|
|
945
|
+
(r, s) => this.shouldShowStep(s)
|
|
946
|
+
), t = e.filter((r) => r.answered).length;
|
|
947
|
+
return {
|
|
948
|
+
current: t,
|
|
949
|
+
total: e.length,
|
|
950
|
+
percentage: e.length > 0 ? Math.round(t / e.length * 100) : 0
|
|
951
|
+
};
|
|
952
|
+
}
|
|
953
|
+
/**
|
|
954
|
+
* Estimate remaining time
|
|
955
|
+
*/
|
|
956
|
+
getEstimatedTime() {
|
|
957
|
+
if (!this.startTime || this.currentStepIndex < 1)
|
|
958
|
+
return null;
|
|
959
|
+
const t = (Date.now() - this.startTime) / (this.currentStepIndex + 1), r = this.steps.length - this.currentStepIndex - 1, s = t * r, o = Math.round(s / 1e3);
|
|
960
|
+
return o < 60 ? `~${o}s` : `~${Math.round(o / 60)}m`;
|
|
961
|
+
}
|
|
962
|
+
/**
|
|
963
|
+
* Render the current step
|
|
964
|
+
*/
|
|
965
|
+
renderCurrentStep() {
|
|
966
|
+
this.renderer.clear();
|
|
967
|
+
const e = this.steps[this.currentStepIndex], t = e.field;
|
|
968
|
+
console.log("");
|
|
969
|
+
const r = this.getProgress(), s = this.getVisibleStepNumber(this.currentStepIndex), o = this.renderer.createProgressBar(
|
|
970
|
+
s,
|
|
971
|
+
r.total,
|
|
972
|
+
30
|
|
973
|
+
);
|
|
974
|
+
this.renderer.renderMuted(`[${o.bar}] ${o.text}`), console.log(""), this.renderer.renderHighlight(`${s}. ${t.label}`), e.description && this.renderer.renderMuted(` ${e.description}`), console.log(""), this.renderField(t);
|
|
975
|
+
}
|
|
976
|
+
/**
|
|
977
|
+
* Get the visible step number (position among visible steps)
|
|
978
|
+
*/
|
|
979
|
+
getVisibleStepNumber(e) {
|
|
980
|
+
let t = 0;
|
|
981
|
+
for (let r = 0; r <= e; r++)
|
|
982
|
+
this.shouldShowStep(r) && t++;
|
|
983
|
+
return t;
|
|
984
|
+
}
|
|
985
|
+
/**
|
|
986
|
+
* Render a field based on its type
|
|
987
|
+
*/
|
|
988
|
+
renderField(e) {
|
|
989
|
+
if (e instanceof y) {
|
|
990
|
+
this.renderer.renderMuted(` formShell.answer(1-${e.max})`);
|
|
991
|
+
const t = i.icons.starEmpty.repeat(e.max ?? 5);
|
|
992
|
+
this.renderer.renderText(` ${t}`, i.colors.warning);
|
|
993
|
+
} else if (e instanceof S)
|
|
994
|
+
this.renderer.renderOptions(
|
|
995
|
+
e.options.map(
|
|
996
|
+
(t) => typeof t == "string" ? t : t.label
|
|
997
|
+
)
|
|
998
|
+
), this.renderer.renderMuted(" formShell.answer(number)");
|
|
999
|
+
else if (e instanceof x)
|
|
1000
|
+
this.renderer.renderOptions(
|
|
1001
|
+
e.options.map(
|
|
1002
|
+
(t) => typeof t == "string" ? t : t.label
|
|
1003
|
+
)
|
|
1004
|
+
), this.renderer.renderMuted(' formShell.answer("1,2,3")');
|
|
1005
|
+
else if (e.type === "yesno")
|
|
1006
|
+
this.renderer.renderMuted(" formShell.y() / formShell.n()");
|
|
1007
|
+
else {
|
|
1008
|
+
const t = this.getPlaceholder(e);
|
|
1009
|
+
this.renderer.renderMuted(` formShell.answer("${t}")`);
|
|
1010
|
+
}
|
|
1011
|
+
e.required || this.renderer.renderMuted(" formShell.skip() to skip"), console.log("");
|
|
1012
|
+
}
|
|
1013
|
+
/**
|
|
1014
|
+
* Get a placeholder for the field
|
|
1015
|
+
*/
|
|
1016
|
+
getPlaceholder(e) {
|
|
1017
|
+
switch (e.type) {
|
|
1018
|
+
case "email":
|
|
1019
|
+
return "email@example.com";
|
|
1020
|
+
case "url":
|
|
1021
|
+
return "https://...";
|
|
1022
|
+
case "date":
|
|
1023
|
+
return "DD/MM/YYYY";
|
|
1024
|
+
case "number":
|
|
1025
|
+
return e instanceof u && e.min !== null ? `${e.min}-${e.max ?? "..."}` : "number";
|
|
1026
|
+
default:
|
|
1027
|
+
return "your answer";
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
/**
|
|
1031
|
+
* Show final summary
|
|
1032
|
+
*/
|
|
1033
|
+
showSummary() {
|
|
1034
|
+
this.renderer.clear(), console.log(""), this.renderer.renderTitle("Summary"), console.log("");
|
|
1035
|
+
let e = 1;
|
|
1036
|
+
this.steps.forEach((t) => {
|
|
1037
|
+
if (t.field.condition) {
|
|
1038
|
+
const l = this.collectData();
|
|
1039
|
+
if (!t.field.condition(l))
|
|
1040
|
+
return;
|
|
1041
|
+
}
|
|
1042
|
+
const r = t.field, s = r.getValue(), o = r.format(s);
|
|
1043
|
+
this.renderer.renderMuted(`${e}. ${r.label}`), this.renderer.renderHighlight(` ${o}`), e++;
|
|
1044
|
+
}), console.log(""), this.renderer.renderSuccess("Completed!"), this.renderer.renderMuted("formShell.submit() to send | formShell.back() to modify"), console.log("");
|
|
1045
|
+
}
|
|
1046
|
+
/**
|
|
1047
|
+
* Cleanup when form is destroyed
|
|
1048
|
+
*/
|
|
1049
|
+
destroy() {
|
|
1050
|
+
this.renderer.destroy();
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
export {
|
|
1054
|
+
d as BaseField,
|
|
1055
|
+
S as ChoiceField,
|
|
1056
|
+
T as DateField,
|
|
1057
|
+
V as EmailField,
|
|
1058
|
+
L as FieldFactory,
|
|
1059
|
+
k as FormShell,
|
|
1060
|
+
x as MultipleChoiceField,
|
|
1061
|
+
u as NumberField,
|
|
1062
|
+
y as RatingField,
|
|
1063
|
+
A as TUIRenderer,
|
|
1064
|
+
a as TextField,
|
|
1065
|
+
i as Theme,
|
|
1066
|
+
I as URLField,
|
|
1067
|
+
C as YesNoField,
|
|
1068
|
+
k as default
|
|
1069
|
+
};
|
|
1070
|
+
//# sourceMappingURL=index.js.map
|