lisichatbot 1.2.5 → 1.2.7

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 +199 -33
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisichatbot",
3
- "version": "1.2.5",
3
+ "version": "1.2.7",
4
4
  "type": "module",
5
5
  "main": "./src/index.js",
6
6
  "exports": {
package/src/index.js CHANGED
@@ -145,7 +145,7 @@ function addMessage(content, type = 'bot', hasInput = false, stepNumber = null)
145
145
  console.log(` Calling editStep(${stepNumber})...`);
146
146
  editStep(stepNumber);
147
147
  };
148
- editIcon.style.setProperty('display', 'block', 'important');
148
+ editIcon.style.setProperty('display', 'flex', 'important');
149
149
  editIcon.style.setProperty('margin-left', '8px', 'important');
150
150
  editIcon.style.setProperty('cursor', 'pointer', 'important');
151
151
  console.log(` ✏️ Edit icon SHOWN (step ${stepNumber} is latest previous step with input)`);
@@ -184,7 +184,7 @@ function addMessage(content, type = 'bot', hasInput = false, stepNumber = null)
184
184
 
185
185
  // Only show if has input AND it's a previous step AND it's latest instance
186
186
  if (hasInput && stepNumber !== null && stepNumber < chatState.step && isLatest) {
187
- editIconAfterAppend.style.setProperty('display', 'block', 'important');
187
+ editIconAfterAppend.style.setProperty('display', 'flex', 'important');
188
188
 
189
189
  // Debug: Check spacing
190
190
  setTimeout(() => {
@@ -238,7 +238,7 @@ function updateEditIcons() {
238
238
  editStep(stepNumber);
239
239
  };
240
240
  editIcon.setAttribute('data-chat-step', stepNumber);
241
- editIcon.style.setProperty('display', 'block', 'important');
241
+ editIcon.style.setProperty('display', 'flex', 'important');
242
242
  editIcon.style.setProperty('cursor', 'pointer', 'important');
243
243
  editIcon.style.setProperty('margin-left', '8px', 'important');
244
244
  console.log(` ✏️ Step ${stepNumber}: Icon SHOWN (latest previous step)`);
@@ -282,6 +282,10 @@ function renderOptions(options, field, isSingleSelect = true) {
282
282
  // Create wrapper to hold all options
283
283
  const optionsWrapper = document.createElement('div');
284
284
  optionsWrapper.setAttribute('data-chat-element', 'options-wrapper');
285
+ optionsWrapper.style.display = 'flex';
286
+ optionsWrapper.style.flexDirection = 'column';
287
+ optionsWrapper.style.alignItems = 'flex-start'; // Don't stretch options
288
+ optionsWrapper.style.gap = '8px';
285
289
 
286
290
  // Clone and fill option element for each option
287
291
  options.forEach((option, index) => {
@@ -396,6 +400,10 @@ function renderColorOptions(options, field) {
396
400
  // Create wrapper to hold all options
397
401
  const optionsWrapper = document.createElement('div');
398
402
  optionsWrapper.setAttribute('data-chat-element', 'options-wrapper');
403
+ optionsWrapper.style.display = 'flex';
404
+ optionsWrapper.style.flexDirection = 'column';
405
+ optionsWrapper.style.alignItems = 'flex-start'; // Don't stretch options
406
+ optionsWrapper.style.gap = '8px';
399
407
 
400
408
  // Clone and fill option element for each option
401
409
  options.forEach((option, index) => {
@@ -514,6 +522,10 @@ function renderCustomSelectOptions(options, field, customConfig) {
514
522
  // Create wrapper to hold all options
515
523
  const optionsWrapper = document.createElement('div');
516
524
  optionsWrapper.setAttribute('data-chat-element', 'options-wrapper');
525
+ optionsWrapper.style.display = 'flex';
526
+ optionsWrapper.style.flexDirection = 'column';
527
+ optionsWrapper.style.alignItems = 'flex-start'; // Don't stretch options
528
+ optionsWrapper.style.gap = '8px';
517
529
 
518
530
  // Render regular options
519
531
  options.forEach((option, index) => {
@@ -648,6 +660,10 @@ function renderMinMaxInputs(field, customConfig, existingData) {
648
660
  const rangeWrapper = document.createElement('div');
649
661
  rangeWrapper.setAttribute('data-chat-element', 'range-wrapper');
650
662
  rangeWrapper.setAttribute('data-field', field);
663
+ rangeWrapper.style.marginBottom = '16px'; // Add space below wrapper
664
+ rangeWrapper.style.display = 'flex';
665
+ rangeWrapper.style.gap = '16px'; // Gap between min and max
666
+ rangeWrapper.style.flexWrap = 'wrap';
651
667
 
652
668
  // Check if should show min/max (custom option selected)
653
669
  // Data is stored as array [min, max]
@@ -712,14 +728,48 @@ function renderMinMaxInputs(field, customConfig, existingData) {
712
728
 
713
729
  elements.messages.appendChild(rangeWrapper);
714
730
 
715
- // Add focus handlers to select custom option when editing
716
- if (minInput) {
717
- minInput.onfocus = () => selectCustomOption(field);
718
- minInput.oninput = () => validateMinMax(field, customConfig);
719
- }
720
- if (maxInput) {
721
- maxInput.onfocus = () => selectCustomOption(field);
722
- maxInput.oninput = () => validateMinMax(field, customConfig);
731
+ // Add handlers for both inputs
732
+ if (minInput && maxInput) {
733
+ const updateVisualFeedback = () => {
734
+ const minFilled = minInput.value.trim() !== '';
735
+ const maxFilled = maxInput.value.trim() !== '';
736
+ const anyFilled = minFilled || maxFilled;
737
+
738
+ // Get tick icons
739
+ const minTick = minClone.querySelector('[data-chat-input-element="tick-icon"]');
740
+ const maxTick = maxClone.querySelector('[data-chat-input-element="tick-icon"]');
741
+
742
+ // Update background and tick for BOTH inputs
743
+ if (anyFilled) {
744
+ minClone.style.backgroundColor = config.selectedBackground;
745
+ maxClone.style.backgroundColor = config.selectedBackground;
746
+ if (minTick) minTick.style.display = 'block';
747
+ if (maxTick) maxTick.style.display = 'block';
748
+ } else {
749
+ minClone.style.backgroundColor = 'transparent';
750
+ maxClone.style.backgroundColor = 'transparent';
751
+ if (minTick) minTick.style.display = 'none';
752
+ if (maxTick) maxTick.style.display = 'none';
753
+ }
754
+ };
755
+
756
+ minInput.onfocus = () => {
757
+ selectCustomOption(field);
758
+ updateVisualFeedback();
759
+ };
760
+ minInput.oninput = () => {
761
+ updateVisualFeedback();
762
+ validateMinMax(field, customConfig);
763
+ };
764
+
765
+ maxInput.onfocus = () => {
766
+ selectCustomOption(field);
767
+ updateVisualFeedback();
768
+ };
769
+ maxInput.oninput = () => {
770
+ updateVisualFeedback();
771
+ validateMinMax(field, customConfig);
772
+ };
723
773
  }
724
774
  }
725
775
 
@@ -783,10 +833,39 @@ function handleCustomSelectClick(element, field, customConfig) {
783
833
  chatState.currentSelection = null;
784
834
  console.log(' ℹ️ Custom selected - waiting for min/max input');
785
835
  } else {
786
- // Hide min/max inputs
836
+ // Hide min/max inputs and clear them
787
837
  const rangeWrapper = document.querySelector(`[data-chat-element="range-wrapper"][data-field="${field}"]`);
788
838
  if (rangeWrapper) {
789
839
  rangeWrapper.style.display = 'none';
840
+
841
+ // Clear input values
842
+ const minInput = rangeWrapper.querySelector('[data-input-type="min"] [data-chat-input-element="input"]');
843
+ const maxInput = rangeWrapper.querySelector('[data-input-type="max"] [data-chat-input-element="input"]');
844
+
845
+ if (minInput) minInput.value = '';
846
+ if (maxInput) maxInput.value = '';
847
+
848
+ // Reset visual feedback (background and ticks)
849
+ const minContainer = rangeWrapper.querySelector('[data-chat-element="single-select-custom-min"]');
850
+ const maxContainer = rangeWrapper.querySelector('[data-chat-element="single-select-custom-max"]');
851
+
852
+ if (minContainer) {
853
+ minContainer.style.backgroundColor = 'transparent';
854
+ const minTick = minContainer.querySelector('[data-chat-input-element="tick-icon"]');
855
+ if (minTick) minTick.style.display = 'none';
856
+ }
857
+
858
+ if (maxContainer) {
859
+ maxContainer.style.backgroundColor = 'transparent';
860
+ const maxTick = maxContainer.querySelector('[data-chat-input-element="tick-icon"]');
861
+ if (maxTick) maxTick.style.display = 'none';
862
+ }
863
+
864
+ // Hide error message
865
+ const errorDiv = rangeWrapper.querySelector('[data-chat-element="range-error"]');
866
+ if (errorDiv) errorDiv.style.display = 'none';
867
+
868
+ console.log(' 🧹 Min/max inputs cleared and hidden');
790
869
  }
791
870
 
792
871
  // Parse and save regular value
@@ -809,7 +888,7 @@ function validateMinMax(field, customConfig) {
809
888
  const maxInput = document.querySelector(`[data-field="${field}"][data-input-type="max"] [data-chat-input-element="input"]`);
810
889
  const errorDiv = document.querySelector(`[data-chat-element="range-error"][data-field="${field}"]`);
811
890
 
812
- if (!minInput || !maxInput) return false;
891
+ if (!minInput || !maxInput) return { valid: false, error: null };
813
892
 
814
893
  const minValue = parseFloat(minInput.value);
815
894
  const maxValue = parseFloat(maxInput.value);
@@ -819,66 +898,91 @@ function validateMinMax(field, customConfig) {
819
898
  const minConstraint = customConfig.min !== undefined ? customConfig.min : 0;
820
899
  const maxConstraint = customConfig.max !== undefined ? customConfig.max : 100;
821
900
 
822
- // Helper to show error
823
- const showError = (message) => {
901
+ // Helper to show error in div and clear data
902
+ const showErrorDiv = (message) => {
824
903
  if (errorDiv) {
825
904
  errorDiv.textContent = message;
826
905
  errorDiv.style.display = 'block';
827
906
  }
828
- console.log(' ❌ Validation error:', message);
829
- disableNextButton();
830
- return false;
907
+ // Clear the data when validation fails
908
+ delete chatState.data[field];
909
+ chatState.currentSelection = null;
831
910
  };
832
911
 
833
- // Helper to hide error
834
- const hideError = () => {
912
+ // Helper to hide error div
913
+ const hideErrorDiv = () => {
835
914
  if (errorDiv) {
836
915
  errorDiv.style.display = 'none';
837
916
  }
838
917
  };
839
918
 
840
- // Validation rules
919
+ // Validation rules - return error message if invalid
841
920
 
842
921
  // 1. Check if only one is filled
843
922
  if (minFilled && !maxFilled) {
844
- return showError(config.customRangeErrors.maxRequired);
923
+ const error = config.customRangeErrors.maxRequired;
924
+ showErrorDiv(error);
925
+ console.log(' ❌ Validation error:', error);
926
+ disableNextButton();
927
+ return { valid: false, error };
845
928
  }
846
929
 
847
930
  if (!minFilled && maxFilled) {
848
- return showError(config.customRangeErrors.minRequired);
931
+ const error = config.customRangeErrors.minRequired;
932
+ showErrorDiv(error);
933
+ console.log(' ❌ Validation error:', error);
934
+ disableNextButton();
935
+ return { valid: false, error };
849
936
  }
850
937
 
851
938
  // 2. Check if both are empty
852
939
  if (!minFilled && !maxFilled) {
853
- hideError();
940
+ hideErrorDiv();
941
+ // Clear data when both empty
942
+ delete chatState.data[field];
943
+ chatState.currentSelection = null;
854
944
  disableNextButton();
855
- return false;
945
+ return { valid: false, error: null };
856
946
  }
857
947
 
858
948
  // 3. Check if values are valid numbers
859
949
  if (isNaN(minValue) || isNaN(maxValue)) {
860
- return showError(config.customRangeErrors.bothRequired);
950
+ const error = config.customRangeErrors.bothRequired;
951
+ showErrorDiv(error);
952
+ console.log(' ❌ Validation error:', error);
953
+ disableNextButton();
954
+ return { valid: false, error };
861
955
  }
862
956
 
863
957
  // 4. Check min constraint
864
958
  if (minValue < minConstraint) {
865
- const msg = config.customRangeErrors.minBelowConstraint.replace('{min}', minConstraint);
866
- return showError(msg);
959
+ const error = config.customRangeErrors.minBelowConstraint.replace('{min}', minConstraint);
960
+ showErrorDiv(error);
961
+ console.log(' ❌ Validation error:', error);
962
+ disableNextButton();
963
+ return { valid: false, error };
867
964
  }
868
965
 
869
966
  // 5. Check max constraint
870
967
  if (maxValue > maxConstraint) {
871
- const msg = config.customRangeErrors.maxAboveConstraint.replace('{max}', maxConstraint);
872
- return showError(msg);
968
+ const error = config.customRangeErrors.maxAboveConstraint.replace('{max}', maxConstraint);
969
+ showErrorDiv(error);
970
+ console.log(' ❌ Validation error:', error);
971
+ disableNextButton();
972
+ return { valid: false, error };
873
973
  }
874
974
 
875
975
  // 6. Check min < max
876
976
  if (minValue >= maxValue) {
877
- return showError(config.customRangeErrors.minGreaterThanMax);
977
+ const error = config.customRangeErrors.minGreaterThanMax;
978
+ showErrorDiv(error);
979
+ console.log(' ❌ Validation error:', error);
980
+ disableNextButton();
981
+ return { valid: false, error };
878
982
  }
879
983
 
880
984
  // All valid! Hide error and save data as array
881
- hideError();
985
+ hideErrorDiv();
882
986
 
883
987
  // Store as array [min, max]
884
988
  chatState.data[field] = [minValue, maxValue];
@@ -890,7 +994,7 @@ function validateMinMax(field, customConfig) {
890
994
 
891
995
  console.log(' ✅ Range valid - stored as array:', [minValue, maxValue]);
892
996
  enableNextButton();
893
- return true;
997
+ return { valid: true, error: null };
894
998
  }
895
999
 
896
1000
  // =============================================================================
@@ -1106,6 +1210,54 @@ async function handleNext() {
1106
1210
  return;
1107
1211
  }
1108
1212
 
1213
+ // VALIDATION: Check for single-select-custom validation before proceeding
1214
+ if (currentStep.inputType === 'single-select-custom' && currentStep.input) {
1215
+ const field = currentStep.input.field;
1216
+ const customConfig = currentStep.input.custom;
1217
+
1218
+ // Check if custom range was selected (data is array)
1219
+ const selectedValue = chatState.data[field];
1220
+ const isCustomRange = Array.isArray(selectedValue);
1221
+
1222
+ if (isCustomRange && customConfig) {
1223
+ // Validate the custom range one more time before proceeding
1224
+ const validation = validateMinMax(field, customConfig);
1225
+
1226
+ if (!validation.valid) {
1227
+ // If there's an error message, show it
1228
+ if (validation.error) {
1229
+ console.log(' ⚠️ Validation failed in handleNext, showing error message');
1230
+
1231
+ // Hide current inputs (options and range wrapper)
1232
+ const optionsWrapper = document.querySelector('[data-chat-element="options-wrapper"]');
1233
+ if (optionsWrapper) {
1234
+ optionsWrapper.style.display = 'none';
1235
+ }
1236
+
1237
+ const rangeWrapper = document.querySelector(`[data-chat-element="range-wrapper"][data-field="${field}"]`);
1238
+ if (rangeWrapper) {
1239
+ rangeWrapper.style.display = 'none';
1240
+ }
1241
+
1242
+ // Add error message as bot message
1243
+ addMessage(validation.error, 'bot', false, null);
1244
+
1245
+ // Re-display the same step with fresh inputs
1246
+ await showNextStep();
1247
+ } else {
1248
+ // No error message, just incomplete data - do nothing
1249
+ console.log(' ⚠️ Validation incomplete, waiting for user input');
1250
+ }
1251
+
1252
+ return; // Stop here, don't proceed
1253
+ }
1254
+ } else if (customConfig && chatState.currentSelection && chatState.currentSelection.value === customConfig.value) {
1255
+ // User selected the custom option but hasn't entered valid min/max yet
1256
+ console.log(' ⚠️ Custom option selected but no valid range entered');
1257
+ return; // Stop here
1258
+ }
1259
+ }
1260
+
1109
1261
  // Call onNext validation if exists
1110
1262
  if (currentStep.onNext) {
1111
1263
  try {
@@ -1155,6 +1307,13 @@ async function handleNext() {
1155
1307
  chatState.step = targetStep;
1156
1308
  console.log(` ✅ Jumped to step ${targetStep}`);
1157
1309
 
1310
+ // Hide all range-wrappers
1311
+ const allRangeWrappers = document.querySelectorAll('[data-chat-element="range-wrapper"]');
1312
+ allRangeWrappers.forEach(wrapper => {
1313
+ wrapper.style.display = 'none';
1314
+ });
1315
+ console.log(' 🙈 Hidden all range-wrappers');
1316
+
1158
1317
  // Update edit icons for the new position
1159
1318
  updateEditIcons();
1160
1319
 
@@ -1169,6 +1328,13 @@ async function handleNext() {
1169
1328
  // Normal flow: Move to next step
1170
1329
  chatState.step++;
1171
1330
 
1331
+ // Hide all range-wrappers (custom min/max inputs)
1332
+ const allRangeWrappers = document.querySelectorAll('[data-chat-element="range-wrapper"]');
1333
+ allRangeWrappers.forEach(wrapper => {
1334
+ wrapper.style.display = 'none';
1335
+ });
1336
+ console.log(' 🙈 Hidden all range-wrappers');
1337
+
1172
1338
  // Update edit icons for all previous steps
1173
1339
  updateEditIcons();
1174
1340