lisichatbot 1.1.0 → 1.1.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.js +112 -17
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisichatbot",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "type": "module",
5
5
  "main": "./src/index.js",
6
6
  "exports": {
package/src/index.js CHANGED
@@ -161,6 +161,7 @@ function renderOptions(options, field, isSingleSelect = true) {
161
161
  input.name = field;
162
162
  input.value = valueStr;
163
163
  input.checked = false; // Ensure unchecked initially
164
+ input.onclick = (e) => e.stopPropagation(); // Prevent click bubbling
164
165
  }
165
166
 
166
167
  // Find and ensure tick icon is hidden initially
@@ -186,18 +187,98 @@ function renderOptions(options, field, isSingleSelect = true) {
186
187
  elements.messages.appendChild(optionsWrapper);
187
188
  scrollToBottom();
188
189
 
189
- // Add click handlers
190
+ // Add click handlers with proper event handling
190
191
  const optionElements = optionsWrapper.querySelectorAll('[data-chat-input-element="container"]');
191
192
  optionElements.forEach(el => {
192
- el.onclick = () => handleOptionClick(el, field, isSingleSelect);
193
+ // Clear any existing onclick
194
+ el.onclick = null;
195
+
196
+ // Add new click handler with stopPropagation
197
+ el.onclick = (e) => {
198
+ e.stopPropagation(); // Prevent bubbling
199
+ e.preventDefault(); // Prevent default behavior
200
+ handleOptionClick(el, field, isSingleSelect);
201
+ };
193
202
  });
194
203
  }
195
204
 
205
+ // =============================================================================
206
+ // TEXT/NUMBER INPUT RENDERING
207
+ // =============================================================================
208
+
209
+ function renderTextInput(field, inputType = 'text') {
210
+ if (!elements.messages) return;
211
+
212
+ // Determine input type attribute value
213
+ const inputTypeAttr = inputType === 'number' ? 'number-input' : 'text-input';
214
+
215
+ // Find existing input element in HTML by data-chat-element
216
+ const inputSelector = `[data-chat-element="${inputTypeAttr}"]`;
217
+ const existingInput = document.querySelector(inputSelector);
218
+
219
+ if (!existingInput) {
220
+ console.error(`Element with ${inputSelector} not found in HTML. Please add it to your HTML.`);
221
+ return;
222
+ }
223
+
224
+ // Clone existing input element
225
+ const clone = existingInput.cloneNode(true);
226
+
227
+ // Make clone visible
228
+ clone.style.display = '';
229
+
230
+ // Set data attributes
231
+ clone.setAttribute('data-field', field);
232
+
233
+ // Find the actual input element
234
+ const inputElement = clone.querySelector('[data-chat-input-element="input"]');
235
+ if (inputElement) {
236
+ inputElement.setAttribute('data-field', field);
237
+ inputElement.name = field;
238
+ inputElement.value = '';
239
+ inputElement.type = inputType === 'number' ? 'number' : 'text';
240
+
241
+ // Add input event to enable Next button when user types
242
+ inputElement.oninput = (e) => {
243
+ const value = inputType === 'number' ? parseFloat(e.target.value) : e.target.value;
244
+
245
+ // Save value
246
+ chatState.data[field] = value;
247
+ chatState.currentSelection = { field, value, name: value };
248
+
249
+ // Enable Next button if there's a value
250
+ if (value !== '' && value !== null && !isNaN(value)) {
251
+ enableNextButton();
252
+ } else {
253
+ disableNextButton();
254
+ }
255
+ };
256
+ }
257
+
258
+ // Append to messages
259
+ elements.messages.appendChild(clone);
260
+ scrollToBottom();
261
+
262
+ // Focus the input
263
+ if (inputElement) {
264
+ setTimeout(() => inputElement.focus(), 100);
265
+ }
266
+ }
267
+
196
268
  // =============================================================================
197
269
  // OPTION CLICK HANDLER USING CUSTOM ATTRIBUTES
198
270
  // =============================================================================
199
271
 
200
272
  function handleOptionClick(element, field, isSingleSelect) {
273
+ // Prevent rapid double-clicks
274
+ const now = Date.now();
275
+ const lastClick = element.getAttribute('data-last-click');
276
+ if (lastClick && (now - parseInt(lastClick)) < 300) {
277
+ console.log('Ignored double-click');
278
+ return;
279
+ }
280
+ element.setAttribute('data-last-click', now.toString());
281
+
201
282
  // Find elements using custom attributes
202
283
  const tickIcon = element.querySelector('[data-chat-input-element="tick-icon"]');
203
284
  const input = element.querySelector('[data-chat-input-element="input"]');
@@ -207,6 +288,13 @@ function handleOptionClick(element, field, isSingleSelect) {
207
288
  const optionName = element.getAttribute('data-name');
208
289
  const inputType = element.getAttribute('data-chat-element');
209
290
 
291
+ const mode = isSingleSelect ? 'Single-select' : 'Multi-select';
292
+ console.log(`${mode} clicked:`, {
293
+ field,
294
+ value: valueStr,
295
+ name: optionName
296
+ });
297
+
210
298
  // Parse value
211
299
  let value;
212
300
  try {
@@ -245,19 +333,11 @@ function handleOptionClick(element, field, isSingleSelect) {
245
333
  // Multi-select
246
334
  const isChecked = !element.classList.contains('cf-checked');
247
335
 
248
- console.log('Multi-select clicked:', {
249
- field,
250
- value,
251
- isChecked,
252
- selectedBackground: config.selectedBackground
253
- });
254
-
255
336
  if (isChecked) {
256
337
  element.classList.add('cf-checked');
257
338
  element.style.setProperty('background-color', config.selectedBackground, 'important');
258
339
  if (tickIcon) {
259
340
  tickIcon.style.setProperty('display', 'block', 'important');
260
- console.log('Tick icon shown for:', optionName);
261
341
  }
262
342
  if (input) input.checked = true;
263
343
 
@@ -273,7 +353,7 @@ function handleOptionClick(element, field, isSingleSelect) {
273
353
  chatState.data[field].push(value);
274
354
  }
275
355
 
276
- console.log('Selected:', chatState.data[field]);
356
+ console.log(`✅ Added "${optionName}" - Total selected:`, chatState.data[field]);
277
357
  } else {
278
358
  element.classList.remove('cf-checked');
279
359
  element.style.setProperty('background-color', 'transparent', 'important');
@@ -287,7 +367,7 @@ function handleOptionClick(element, field, isSingleSelect) {
287
367
  );
288
368
  }
289
369
 
290
- console.log('Unselected:', chatState.data[field]);
370
+ console.log(`❌ Removed "${optionName}" - Total selected:`, chatState.data[field]);
291
371
  }
292
372
 
293
373
  // Get all selected names using custom attribute
@@ -399,12 +479,27 @@ async function showNextStep() {
399
479
 
400
480
  // Add options if input exists
401
481
  if (nextStep.input) {
402
- const isSingleSelect = nextStep.inputType !== 'multi';
403
- renderOptions(nextStep.input.options, nextStep.input.field, isSingleSelect);
482
+ const inputType = nextStep.inputType || 'single-select'; // Default to single-select
404
483
 
405
- // Enable Next button if input not required (default behavior)
406
- if (!inputRequired) {
407
- enableNextButton();
484
+ if (inputType === 'text' || inputType === 'number') {
485
+ // Render text or number input
486
+ renderTextInput(nextStep.input.field, inputType);
487
+
488
+ // Disable Next button initially (enabled when user types)
489
+ if (inputRequired) {
490
+ disableNextButton();
491
+ } else {
492
+ enableNextButton();
493
+ }
494
+ } else {
495
+ // Render options (single-select or multi-select)
496
+ const isSingleSelect = inputType === 'single-select';
497
+ renderOptions(nextStep.input.options, nextStep.input.field, isSingleSelect);
498
+
499
+ // Enable Next button if input not required (default behavior)
500
+ if (!inputRequired) {
501
+ enableNextButton();
502
+ }
408
503
  }
409
504
  } else {
410
505
  // Auto-advance for steps without input