lisichatbot 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/README.md +0 -0
- package/package.json +15 -0
- package/src/index.js +583 -0
package/README.md
ADDED
|
File without changes
|
package/package.json
ADDED
package/src/index.js
ADDED
|
@@ -0,0 +1,583 @@
|
|
|
1
|
+
|
|
2
|
+
let chatState = {
|
|
3
|
+
step: 0,
|
|
4
|
+
data: {},
|
|
5
|
+
history: [],
|
|
6
|
+
currentSelection: null
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
let elements = {
|
|
10
|
+
container: null,
|
|
11
|
+
messages: null,
|
|
12
|
+
nextBtn: null
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
let config = {
|
|
16
|
+
selectedBackground: '#667eea',
|
|
17
|
+
autoAdvanceDelay: 2000,
|
|
18
|
+
enableAnimations: true
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
let flowData = null;
|
|
22
|
+
|
|
23
|
+
// =============================================================================
|
|
24
|
+
// UTILITY FUNCTIONS
|
|
25
|
+
// =============================================================================
|
|
26
|
+
|
|
27
|
+
function scrollToBottom() {
|
|
28
|
+
if (elements.messages) {
|
|
29
|
+
setTimeout(() => {
|
|
30
|
+
elements.messages.scrollTop = elements.messages.scrollHeight;
|
|
31
|
+
}, 100);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function enableNextButton() {
|
|
36
|
+
if (elements.nextBtn) {
|
|
37
|
+
elements.nextBtn.disabled = false;
|
|
38
|
+
elements.nextBtn.style.opacity = '1';
|
|
39
|
+
elements.nextBtn.style.cursor = 'pointer';
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function disableNextButton() {
|
|
44
|
+
if (elements.nextBtn) {
|
|
45
|
+
elements.nextBtn.disabled = true;
|
|
46
|
+
elements.nextBtn.style.opacity = '0.5';
|
|
47
|
+
elements.nextBtn.style.cursor = 'not-allowed';
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// =============================================================================
|
|
52
|
+
// MESSAGE FUNCTIONS
|
|
53
|
+
// =============================================================================
|
|
54
|
+
|
|
55
|
+
function addMessage(content, type = 'bot') {
|
|
56
|
+
if (!elements.messages) return;
|
|
57
|
+
|
|
58
|
+
const messageDiv = document.createElement('div');
|
|
59
|
+
messageDiv.className = `cf-message cf-${type}`;
|
|
60
|
+
messageDiv.innerHTML = `<div class="cf-message-content">${content}</div>`;
|
|
61
|
+
|
|
62
|
+
elements.messages.appendChild(messageDiv);
|
|
63
|
+
scrollToBottom();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// =============================================================================
|
|
67
|
+
// OPTIONS RENDERING
|
|
68
|
+
// =============================================================================
|
|
69
|
+
|
|
70
|
+
function renderOptions(options, field, isSingleSelect = true) {
|
|
71
|
+
if (!elements.messages) return;
|
|
72
|
+
|
|
73
|
+
const messageDiv = document.createElement('div');
|
|
74
|
+
messageDiv.className = 'cf-message cf-bot';
|
|
75
|
+
|
|
76
|
+
const optionsHtml = options.map((option, index) => {
|
|
77
|
+
const optionName = option.name || option;
|
|
78
|
+
const optionValue = option.value !== undefined ? option.value : option;
|
|
79
|
+
const valueStr = typeof optionValue === 'object' ?
|
|
80
|
+
JSON.stringify(optionValue) : String(optionValue);
|
|
81
|
+
|
|
82
|
+
return `
|
|
83
|
+
<label class="cf-option ${isSingleSelect ? 'cf-single' : 'cf-multi'}"
|
|
84
|
+
data-field="${field}"
|
|
85
|
+
data-value='${valueStr}'
|
|
86
|
+
data-name="${optionName}"
|
|
87
|
+
data-index="${index}">
|
|
88
|
+
<div class="cf-tick" style="display: none;">✓</div>
|
|
89
|
+
<input type="${isSingleSelect ? 'radio' : 'checkbox'}"
|
|
90
|
+
name="${field}"
|
|
91
|
+
value="${valueStr}"
|
|
92
|
+
style="display: none;">
|
|
93
|
+
<span class="cf-option-text">${optionName}</span>
|
|
94
|
+
</label>
|
|
95
|
+
`;
|
|
96
|
+
}).join('');
|
|
97
|
+
|
|
98
|
+
messageDiv.innerHTML = `
|
|
99
|
+
<div class="cf-message-content">
|
|
100
|
+
<div class="cf-options">${optionsHtml}</div>
|
|
101
|
+
</div>
|
|
102
|
+
`;
|
|
103
|
+
|
|
104
|
+
elements.messages.appendChild(messageDiv);
|
|
105
|
+
scrollToBottom();
|
|
106
|
+
|
|
107
|
+
// Add click handlers
|
|
108
|
+
const optionElements = messageDiv.querySelectorAll('.cf-option');
|
|
109
|
+
optionElements.forEach(el => {
|
|
110
|
+
el.onclick = () => handleOptionClick(el, field, isSingleSelect);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// =============================================================================
|
|
115
|
+
// OPTION CLICK HANDLER
|
|
116
|
+
// =============================================================================
|
|
117
|
+
|
|
118
|
+
function handleOptionClick(element, field, isSingleSelect) {
|
|
119
|
+
const tickIcon = element.querySelector('.cf-tick');
|
|
120
|
+
const input = element.querySelector('input');
|
|
121
|
+
const valueStr = element.getAttribute('data-value');
|
|
122
|
+
const optionName = element.getAttribute('data-name');
|
|
123
|
+
|
|
124
|
+
// Parse value
|
|
125
|
+
let value;
|
|
126
|
+
try {
|
|
127
|
+
value = JSON.parse(valueStr);
|
|
128
|
+
} catch (e) {
|
|
129
|
+
value = valueStr;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (isSingleSelect) {
|
|
133
|
+
// Uncheck all others
|
|
134
|
+
const allOptions = document.querySelectorAll(`[data-field="${field}"]`);
|
|
135
|
+
allOptions.forEach(opt => {
|
|
136
|
+
opt.classList.remove('cf-checked');
|
|
137
|
+
opt.style.backgroundColor = 'transparent';
|
|
138
|
+
opt.querySelector('.cf-tick').style.display = 'none';
|
|
139
|
+
opt.querySelector('input').checked = false;
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// Check this one
|
|
143
|
+
element.classList.add('cf-checked');
|
|
144
|
+
element.style.backgroundColor = config.selectedBackground;
|
|
145
|
+
tickIcon.style.display = 'block';
|
|
146
|
+
input.checked = true;
|
|
147
|
+
|
|
148
|
+
// Save value
|
|
149
|
+
chatState.data[field] = value;
|
|
150
|
+
chatState.currentSelection = { field, value, name: optionName };
|
|
151
|
+
|
|
152
|
+
} else {
|
|
153
|
+
// Multi-select
|
|
154
|
+
const isChecked = !element.classList.contains('cf-checked');
|
|
155
|
+
|
|
156
|
+
if (isChecked) {
|
|
157
|
+
element.classList.add('cf-checked');
|
|
158
|
+
element.style.backgroundColor = config.selectedBackground;
|
|
159
|
+
tickIcon.style.display = 'block';
|
|
160
|
+
input.checked = true;
|
|
161
|
+
|
|
162
|
+
// Add to array
|
|
163
|
+
if (!Array.isArray(chatState.data[field])) {
|
|
164
|
+
chatState.data[field] = [];
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const exists = chatState.data[field].some(v =>
|
|
168
|
+
JSON.stringify(v) === JSON.stringify(value)
|
|
169
|
+
);
|
|
170
|
+
if (!exists) {
|
|
171
|
+
chatState.data[field].push(value);
|
|
172
|
+
}
|
|
173
|
+
} else {
|
|
174
|
+
element.classList.remove('cf-checked');
|
|
175
|
+
element.style.backgroundColor = 'transparent';
|
|
176
|
+
tickIcon.style.display = 'none';
|
|
177
|
+
input.checked = false;
|
|
178
|
+
|
|
179
|
+
// Remove from array
|
|
180
|
+
if (Array.isArray(chatState.data[field])) {
|
|
181
|
+
chatState.data[field] = chatState.data[field].filter(v =>
|
|
182
|
+
JSON.stringify(v) !== JSON.stringify(value)
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Get all selected names
|
|
188
|
+
const selectedOptions = document.querySelectorAll(`[data-field="${field}"].cf-checked`);
|
|
189
|
+
const selectedNames = Array.from(selectedOptions).map(opt =>
|
|
190
|
+
opt.getAttribute('data-name')
|
|
191
|
+
).join(', ');
|
|
192
|
+
|
|
193
|
+
chatState.currentSelection = {
|
|
194
|
+
field,
|
|
195
|
+
value: chatState.data[field],
|
|
196
|
+
name: selectedNames || 'None selected'
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Enable Next button
|
|
201
|
+
enableNextButton();
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// =============================================================================
|
|
205
|
+
// NAVIGATION
|
|
206
|
+
// =============================================================================
|
|
207
|
+
|
|
208
|
+
async function handleNext() {
|
|
209
|
+
if (!chatState.currentSelection) return;
|
|
210
|
+
|
|
211
|
+
const currentStep = flowData.flow[chatState.step];
|
|
212
|
+
|
|
213
|
+
// Call onNext validation if exists
|
|
214
|
+
if (currentStep.onNext) {
|
|
215
|
+
try {
|
|
216
|
+
disableNextButton();
|
|
217
|
+
const canProceed = await currentStep.onNext(
|
|
218
|
+
chatState.currentSelection.value,
|
|
219
|
+
chatState.data
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
if (!canProceed) {
|
|
223
|
+
enableNextButton();
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
} catch (error) {
|
|
227
|
+
console.error('Validation error:', error);
|
|
228
|
+
alert('An error occurred. Please try again.');
|
|
229
|
+
enableNextButton();
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Add user message
|
|
235
|
+
addMessage(chatState.currentSelection.name, 'user');
|
|
236
|
+
|
|
237
|
+
// Save to history
|
|
238
|
+
chatState.history.push({
|
|
239
|
+
step: chatState.step,
|
|
240
|
+
field: chatState.currentSelection.field,
|
|
241
|
+
value: chatState.currentSelection.value,
|
|
242
|
+
name: chatState.currentSelection.name
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
// Reset selection
|
|
246
|
+
chatState.currentSelection = null;
|
|
247
|
+
disableNextButton();
|
|
248
|
+
|
|
249
|
+
// Move to next step
|
|
250
|
+
chatState.step++;
|
|
251
|
+
|
|
252
|
+
// Check if finished
|
|
253
|
+
if (chatState.step >= flowData.flow.length) {
|
|
254
|
+
handleCompletion();
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Show next step
|
|
259
|
+
await showNextStep();
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
async function showNextStep() {
|
|
263
|
+
const nextStep = flowData.flow[chatState.step];
|
|
264
|
+
|
|
265
|
+
// Call onStart if exists
|
|
266
|
+
if (nextStep.onStart) {
|
|
267
|
+
try {
|
|
268
|
+
const canStart = await nextStep.onStart(chatState.data);
|
|
269
|
+
if (!canStart) {
|
|
270
|
+
console.log('Step blocked');
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
} catch (error) {
|
|
274
|
+
console.error('Error in onStart:', error);
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Add message
|
|
280
|
+
addMessage(nextStep.message);
|
|
281
|
+
|
|
282
|
+
// Add options if input exists
|
|
283
|
+
if (nextStep.input) {
|
|
284
|
+
const isSingleSelect = nextStep.inputType !== 'multi';
|
|
285
|
+
renderOptions(nextStep.input.options, nextStep.input.field, isSingleSelect);
|
|
286
|
+
} else {
|
|
287
|
+
// Auto-advance for steps without input
|
|
288
|
+
setTimeout(() => {
|
|
289
|
+
chatState.step++;
|
|
290
|
+
if (chatState.step < flowData.flow.length) {
|
|
291
|
+
showNextStep();
|
|
292
|
+
} else {
|
|
293
|
+
handleCompletion();
|
|
294
|
+
}
|
|
295
|
+
}, config.autoAdvanceDelay);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function handleCompletion() {
|
|
300
|
+
addMessage('Thank you! All done.');
|
|
301
|
+
|
|
302
|
+
console.log('✅ Flow completed. Data:', chatState.data);
|
|
303
|
+
|
|
304
|
+
// Hide Next button
|
|
305
|
+
if (elements.nextBtn) {
|
|
306
|
+
elements.nextBtn.style.display = 'none';
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Trigger event
|
|
310
|
+
if (typeof window !== 'undefined') {
|
|
311
|
+
const event = new CustomEvent('conversationalFlowComplete', {
|
|
312
|
+
detail: {
|
|
313
|
+
data: chatState.data,
|
|
314
|
+
history: chatState.history
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
window.dispatchEvent(event);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Call onComplete if provided
|
|
321
|
+
if (flowData.onComplete && typeof flowData.onComplete === 'function') {
|
|
322
|
+
flowData.onComplete(chatState.data);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// =============================================================================
|
|
327
|
+
// INITIALIZATION
|
|
328
|
+
// =============================================================================
|
|
329
|
+
|
|
330
|
+
function init(containerId, flowConfig) {
|
|
331
|
+
// Get container
|
|
332
|
+
elements.container = document.getElementById(containerId);
|
|
333
|
+
if (!elements.container) {
|
|
334
|
+
console.error('Container not found:', containerId);
|
|
335
|
+
return null;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Merge config
|
|
339
|
+
if (flowConfig.config) {
|
|
340
|
+
Object.assign(config, flowConfig.config);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Store flow data
|
|
344
|
+
flowData = flowConfig;
|
|
345
|
+
|
|
346
|
+
// Initialize state
|
|
347
|
+
chatState.step = 0;
|
|
348
|
+
chatState.data = flowConfig.initialData || {};
|
|
349
|
+
chatState.history = [];
|
|
350
|
+
chatState.currentSelection = null;
|
|
351
|
+
|
|
352
|
+
// Create UI
|
|
353
|
+
elements.container.innerHTML = `
|
|
354
|
+
<div class="cf-wrapper">
|
|
355
|
+
<div class="cf-messages" id="cfMessages"></div>
|
|
356
|
+
<div class="cf-bottom">
|
|
357
|
+
<button id="cfNextBtn" class="cf-next-btn" style="opacity: 0.5; cursor: not-allowed;" disabled>
|
|
358
|
+
Next →
|
|
359
|
+
</button>
|
|
360
|
+
</div>
|
|
361
|
+
</div>
|
|
362
|
+
`;
|
|
363
|
+
|
|
364
|
+
// Get elements
|
|
365
|
+
elements.messages = document.getElementById('cfMessages');
|
|
366
|
+
elements.nextBtn = document.getElementById('cfNextBtn');
|
|
367
|
+
|
|
368
|
+
// Add event listener
|
|
369
|
+
if (elements.nextBtn) {
|
|
370
|
+
elements.nextBtn.onclick = handleNext;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Start
|
|
374
|
+
showNextStep();
|
|
375
|
+
|
|
376
|
+
console.log('✅ Conversational Flow Builder initialized');
|
|
377
|
+
|
|
378
|
+
return {
|
|
379
|
+
getState,
|
|
380
|
+
reset,
|
|
381
|
+
goToStep
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function getState() {
|
|
386
|
+
return {
|
|
387
|
+
step: chatState.step,
|
|
388
|
+
data: { ...chatState.data },
|
|
389
|
+
history: [...chatState.history]
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function reset() {
|
|
394
|
+
chatState.step = 0;
|
|
395
|
+
chatState.data = flowData.initialData || {};
|
|
396
|
+
chatState.history = [];
|
|
397
|
+
chatState.currentSelection = null;
|
|
398
|
+
|
|
399
|
+
if (elements.messages) {
|
|
400
|
+
elements.messages.innerHTML = '';
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if (elements.nextBtn) {
|
|
404
|
+
elements.nextBtn.style.display = 'flex';
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
disableNextButton();
|
|
408
|
+
showNextStep();
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
function goToStep(stepNumber) {
|
|
412
|
+
if (stepNumber < 0 || stepNumber >= flowData.flow.length) {
|
|
413
|
+
console.error('Invalid step number:', stepNumber);
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
chatState.step = stepNumber;
|
|
418
|
+
chatState.currentSelection = null;
|
|
419
|
+
|
|
420
|
+
if (elements.messages) {
|
|
421
|
+
elements.messages.innerHTML = '';
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
disableNextButton();
|
|
425
|
+
showNextStep();
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function injectStyles() {
|
|
429
|
+
if (typeof document === 'undefined') return;
|
|
430
|
+
|
|
431
|
+
const styleId = 'cf-styles';
|
|
432
|
+
if (document.getElementById(styleId)) return;
|
|
433
|
+
|
|
434
|
+
const style = document.createElement('style');
|
|
435
|
+
style.id = styleId;
|
|
436
|
+
style.textContent = `
|
|
437
|
+
.cf-wrapper {
|
|
438
|
+
display: flex;
|
|
439
|
+
flex-direction: column;
|
|
440
|
+
height: 100%;
|
|
441
|
+
max-height: 600px;
|
|
442
|
+
border: 1px solid #e5e5e5;
|
|
443
|
+
border-radius: 8px;
|
|
444
|
+
overflow: hidden;
|
|
445
|
+
background: #f9f9f9;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
.cf-messages {
|
|
449
|
+
flex: 1;
|
|
450
|
+
overflow-y: auto;
|
|
451
|
+
padding: 20px;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
.cf-message {
|
|
455
|
+
margin-bottom: 15px;
|
|
456
|
+
animation: cfSlideIn 0.3s ease;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
@keyframes cfSlideIn {
|
|
460
|
+
from { opacity: 0; transform: translateY(10px); }
|
|
461
|
+
to { opacity: 1; transform: translateY(0); }
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
.cf-bot {
|
|
465
|
+
text-align: left;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
.cf-user {
|
|
469
|
+
text-align: right;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
.cf-message-content {
|
|
473
|
+
display: inline-block;
|
|
474
|
+
padding: 12px 16px;
|
|
475
|
+
border-radius: 8px;
|
|
476
|
+
max-width: 80%;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
.cf-bot .cf-message-content {
|
|
480
|
+
background: white;
|
|
481
|
+
border: 1px solid #e5e5e5;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
.cf-user .cf-message-content {
|
|
485
|
+
background: #667eea;
|
|
486
|
+
color: white;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
.cf-options {
|
|
490
|
+
display: flex;
|
|
491
|
+
flex-direction: column;
|
|
492
|
+
gap: 10px;
|
|
493
|
+
margin-top: 10px;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
.cf-option {
|
|
497
|
+
display: flex;
|
|
498
|
+
align-items: center;
|
|
499
|
+
gap: 12px;
|
|
500
|
+
padding: 14px 16px;
|
|
501
|
+
background: white;
|
|
502
|
+
border: 2px solid #e5e5e5;
|
|
503
|
+
border-radius: 8px;
|
|
504
|
+
cursor: pointer;
|
|
505
|
+
transition: all 0.2s ease;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
.cf-option:hover {
|
|
509
|
+
border-color: #667eea;
|
|
510
|
+
transform: translateX(4px);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
.cf-option.cf-checked {
|
|
514
|
+
border-color: #667eea;
|
|
515
|
+
color: white;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
.cf-tick {
|
|
519
|
+
font-weight: bold;
|
|
520
|
+
font-size: 18px;
|
|
521
|
+
min-width: 20px;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
.cf-option-text {
|
|
525
|
+
flex: 1;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
.cf-bottom {
|
|
529
|
+
padding: 16px;
|
|
530
|
+
background: white;
|
|
531
|
+
border-top: 1px solid #e5e5e5;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
.cf-next-btn {
|
|
535
|
+
width: 100%;
|
|
536
|
+
padding: 14px 24px;
|
|
537
|
+
background: #667eea;
|
|
538
|
+
color: white;
|
|
539
|
+
border: none;
|
|
540
|
+
border-radius: 8px;
|
|
541
|
+
font-size: 16px;
|
|
542
|
+
font-weight: 600;
|
|
543
|
+
cursor: pointer;
|
|
544
|
+
transition: all 0.2s ease;
|
|
545
|
+
display: flex;
|
|
546
|
+
align-items: center;
|
|
547
|
+
justify-content: center;
|
|
548
|
+
gap: 8px;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
.cf-next-btn:not(:disabled):hover {
|
|
552
|
+
background: #5568d3;
|
|
553
|
+
transform: translateY(-2px);
|
|
554
|
+
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
.cf-next-btn:disabled {
|
|
558
|
+
cursor: not-allowed;
|
|
559
|
+
}
|
|
560
|
+
`;
|
|
561
|
+
|
|
562
|
+
document.head.appendChild(style);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// Auto-inject styles when imported
|
|
566
|
+
if (typeof document !== 'undefined') {
|
|
567
|
+
if (document.readyState === 'loading') {
|
|
568
|
+
document.addEventListener('DOMContentLoaded', injectStyles);
|
|
569
|
+
} else {
|
|
570
|
+
injectStyles();
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// =============================================================================
|
|
575
|
+
// EXPORTS
|
|
576
|
+
// =============================================================================
|
|
577
|
+
|
|
578
|
+
module.exports = {
|
|
579
|
+
init,
|
|
580
|
+
getState,
|
|
581
|
+
reset,
|
|
582
|
+
goToStep
|
|
583
|
+
};
|