@countriesdb/widget 0.1.25 → 0.1.27

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/dist/index.js CHANGED
@@ -752,27 +752,47 @@
752
752
  select.innerHTML += `<option value="${defaultValue}" disabled>Error: No country select present</option>`;
753
753
  }
754
754
  }
755
+ // Remove old event handlers if they exist (for re-initialization)
756
+ const oldHandlers = state.eventHandlers.get(select);
757
+ if (oldHandlers) {
758
+ if (oldHandlers.update) {
759
+ select.removeEventListener('change', oldHandlers.update);
760
+ }
761
+ if (oldHandlers.followRelated) {
762
+ select.removeEventListener('change', oldHandlers.followRelated);
763
+ }
764
+ if (oldHandlers.followUpward) {
765
+ select.removeEventListener('change', oldHandlers.followUpward);
766
+ }
767
+ }
768
+ // Create new event handlers
769
+ const handlers = {};
755
770
  // Always dispatch an update event for user-initiated subdivision changes
756
- select.addEventListener('change', (event) => {
771
+ handlers.update = (event) => {
757
772
  if (isWidgetInitiatedEvent(event)) {
758
773
  return;
759
774
  }
760
775
  dispatchUpdateEvent(select, { type: 'subdivision', reason: 'regular' });
761
- });
776
+ };
777
+ select.addEventListener('change', handlers.update);
762
778
  // --- follow_related (forward direction) ---
763
- select.addEventListener('change', async (event) => {
779
+ handlers.followRelated = async (event) => {
764
780
  if (isWidgetInitiatedEvent(event)) {
765
781
  return;
766
782
  }
767
783
  await handleFollowRelatedFromSubdivision(select, apiKey, backendUrl, state, config.followRelated, (s, key, code) => updateSubdivisionSelect(s, key, backendUrl, state, config, code));
768
- });
784
+ };
785
+ select.addEventListener('change', handlers.followRelated);
769
786
  // --- follow_upward (reverse direction) ---
770
- select.addEventListener('change', async (event) => {
787
+ handlers.followUpward = async (event) => {
771
788
  if (isWidgetInitiatedEvent(event)) {
772
789
  return;
773
790
  }
774
791
  await handleFollowUpwardFromSubdivision(select, apiKey, backendUrl, state, config.followUpward, (s, key, code) => updateSubdivisionSelect(s, key, backendUrl, state, config, code));
775
- });
792
+ };
793
+ select.addEventListener('change', handlers.followUpward);
794
+ // Store handlers for future cleanup
795
+ state.eventHandlers.set(select, handlers);
776
796
  }
777
797
  }
778
798
  /**
@@ -824,6 +844,11 @@
824
844
  const dataDefaultValue = !isMultiple
825
845
  ? select.dataset.defaultValue
826
846
  : undefined;
847
+ // Preserve user's current selection before clearing innerHTML
848
+ // This prevents preselected values from overwriting user selections
849
+ const currentValue = select.value;
850
+ const defaultValue = select.dataset.defaultValue || '';
851
+ const hasUserSelection = currentValue && currentValue !== defaultValue && currentValue.trim() !== '';
827
852
  select.innerHTML = buildSubdivisionOptionsHTML(subdivisions, select, subdivisionsLanguage, config.showSubdivisionType, config.allowParentSelection, config.subdivisionNameFilter);
828
853
  // Restore data attributes after setting innerHTML
829
854
  if (!isMultiple) {
@@ -834,41 +859,53 @@
834
859
  select.dataset.defaultValue = dataDefaultValue;
835
860
  }
836
861
  }
837
- // Manual preselection wins
838
- // Check if preselected value exists (applyPreselectedValue will read it from select element)
839
- const hasPreselectedValue = (select.getAttribute('data-preselected') || select.dataset.preselected || select.dataset._widgetTempPreselect) !== undefined;
840
- if (hasPreselectedValue) {
841
- applyPreselectedValue(select, apiKey);
842
- dispatchUpdateEvent(select, {
843
- type: 'subdivision',
844
- reason: 'preselected',
845
- });
846
- valueSetByWidget = true;
847
- await triggerFollowLogic(select, apiKey, backendUrl, state, config.followRelated, config.followUpward, (s, key, code) => updateSubdivisionSelect(s, key, backendUrl, state, config, code), (countrySelect, key) => updateSubdivisions(countrySelect, key, backendUrl, state, config));
862
+ // Restore user's selection if it exists in the new options (user selection takes priority)
863
+ let userSelectionRestored = false;
864
+ if (hasUserSelection && !isMultiple) {
865
+ const optionExists = Array.from(select.options).some(opt => opt.value === currentValue);
866
+ if (optionExists) {
867
+ select.value = currentValue;
868
+ userSelectionRestored = true;
869
+ // Don't dispatch event here - user already selected it, no need to notify again
870
+ }
848
871
  }
849
- else {
850
- // Try GeoIP preselect
851
- if (shouldUseGeoIP) {
852
- const preselectedSubdivision = subdivisions.find((subdivision) => subdivision.preselected);
853
- if (preselectedSubdivision) {
854
- const isMultiple = select.hasAttribute('multiple');
855
- if (isMultiple) {
856
- // For multi-select, find and select the option
857
- const option = Array.from(select.options).find((opt) => opt.value === preselectedSubdivision.code);
858
- if (option) {
859
- option.selected = true;
872
+ // Manual preselection only if user hasn't selected anything
873
+ if (!userSelectionRestored) {
874
+ // Check if preselected value exists (applyPreselectedValue will read it from select element)
875
+ const hasPreselectedValue = (select.getAttribute('data-preselected') || select.dataset.preselected || select.dataset._widgetTempPreselect) !== undefined;
876
+ if (hasPreselectedValue) {
877
+ applyPreselectedValue(select, apiKey);
878
+ dispatchUpdateEvent(select, {
879
+ type: 'subdivision',
880
+ reason: 'preselected',
881
+ });
882
+ valueSetByWidget = true;
883
+ await triggerFollowLogic(select, apiKey, backendUrl, state, config.followRelated, config.followUpward, (s, key, code) => updateSubdivisionSelect(s, key, backendUrl, state, config, code), (countrySelect, key) => updateSubdivisions(countrySelect, key, backendUrl, state, config));
884
+ }
885
+ else {
886
+ // Try GeoIP preselect (only if user hasn't selected anything)
887
+ if (shouldUseGeoIP) {
888
+ const preselectedSubdivision = subdivisions.find((subdivision) => subdivision.preselected);
889
+ if (preselectedSubdivision) {
890
+ const isMultiple = select.hasAttribute('multiple');
891
+ if (isMultiple) {
892
+ // For multi-select, find and select the option
893
+ const option = Array.from(select.options).find((opt) => opt.value === preselectedSubdivision.code);
894
+ if (option) {
895
+ option.selected = true;
896
+ }
860
897
  }
898
+ else {
899
+ // Single select: set value directly
900
+ select.value = preselectedSubdivision.code;
901
+ }
902
+ dispatchUpdateEvent(select, {
903
+ type: 'subdivision',
904
+ reason: 'geoip',
905
+ });
906
+ valueSetByWidget = true;
907
+ await triggerFollowLogic(select, apiKey, backendUrl, state, config.followRelated, config.followUpward, (s, key, code) => updateSubdivisionSelect(s, key, backendUrl, state, config, code), (countrySelect, key) => updateSubdivisions(countrySelect, key, backendUrl, state, config));
861
908
  }
862
- else {
863
- // Single select: set value directly
864
- select.value = preselectedSubdivision.code;
865
- }
866
- dispatchUpdateEvent(select, {
867
- type: 'subdivision',
868
- reason: 'geoip',
869
- });
870
- valueSetByWidget = true;
871
- await triggerFollowLogic(select, apiKey, backendUrl, state, config.followRelated, config.followUpward, (s, key, code) => updateSubdivisionSelect(s, key, backendUrl, state, config, code), (countrySelect, key) => updateSubdivisions(countrySelect, key, backendUrl, state, config));
872
909
  }
873
910
  }
874
911
  }
@@ -991,8 +1028,13 @@
991
1028
  // Subdivisions will be loaded by event handler
992
1029
  loadedInitialSubdivisions = true;
993
1030
  }
994
- // On change => update subdivisions
995
- select.addEventListener('change', async (event) => {
1031
+ // Remove old event handlers if they exist (for re-initialization)
1032
+ const oldCountryHandlers = state.eventHandlers.get(select);
1033
+ if (oldCountryHandlers?.countryChange) {
1034
+ select.removeEventListener('change', oldCountryHandlers.countryChange);
1035
+ }
1036
+ // Create new country change handler
1037
+ const countryChangeHandler = async (event) => {
996
1038
  if (isWidgetInitiatedEvent(event)) {
997
1039
  return;
998
1040
  }
@@ -1014,7 +1056,12 @@
1014
1056
  if (config.followUpward && !select.multiple && picked.is_subdivision_of) {
1015
1057
  await handleFollowUpwardFromCountry(select, apiKey, backendUrl, state, config.followUpward, (s, key, code) => updateSubdivisionSelect(s, key, backendUrl, state, subdivisionConfig, code));
1016
1058
  }
1017
- });
1059
+ };
1060
+ // Store and attach the handler
1061
+ const countryHandlers = state.eventHandlers.get(select) || {};
1062
+ countryHandlers.countryChange = countryChangeHandler;
1063
+ state.eventHandlers.set(select, countryHandlers);
1064
+ select.addEventListener('change', countryChangeHandler);
1018
1065
  }
1019
1066
  catch (error) {
1020
1067
  console.error('Failed to fetch countries:', error);
@@ -1089,6 +1136,7 @@
1089
1136
  subdivisionsMap: new WeakMap(),
1090
1137
  subdivisionsLanguageMap: new WeakMap(),
1091
1138
  isInitializing: new Set(),
1139
+ eventHandlers: new WeakMap(),
1092
1140
  };
1093
1141
  // Use empty string if publicKey is missing (will show error when API calls fail)
1094
1142
  const apiKey = config.publicKey || '';