@countriesdb/widget 0.1.19 → 0.1.21

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.
@@ -23,7 +23,7 @@ export declare function applyPreselectedValue(select: SelectElement, apiKey?: st
23
23
  /**
24
24
  * Handle API error by showing error message in select
25
25
  */
26
- export declare function handleApiError(select: SelectElement, errorMessage: string | Error): void;
26
+ export declare function handleApiError(select: SelectElement, errorMessage: string | Error, replace?: boolean): void;
27
27
  /**
28
28
  * Parse boolean from string value
29
29
  */
@@ -142,12 +142,19 @@ export function applyPreselectedValue(select, apiKey) {
142
142
  /**
143
143
  * Handle API error by showing error message in select
144
144
  */
145
- export function handleApiError(select, errorMessage) {
145
+ export function handleApiError(select, errorMessage, replace = false) {
146
146
  const message = errorMessage instanceof Error ? errorMessage.message : errorMessage;
147
147
  const defaultValue = select.dataset.defaultValue ?? '';
148
148
  // Add "Error: " prefix to match old widget behavior and test expectations
149
149
  const formattedMessage = message.startsWith('Error: ') ? message : `Error: ${message}`;
150
- select.innerHTML += `<option value="${defaultValue}" disabled>${formattedMessage}</option>`;
150
+ if (replace) {
151
+ select.innerHTML = `<option value="${defaultValue}" disabled>${formattedMessage}</option>`;
152
+ }
153
+ else {
154
+ select.innerHTML += `<option value="${defaultValue}" disabled>${formattedMessage}</option>`;
155
+ }
156
+ // Ensure select is enabled so users can see the error
157
+ select.disabled = false;
151
158
  }
152
159
  /**
153
160
  * Parse boolean from string value
package/dist/index.esm.js CHANGED
@@ -143,12 +143,19 @@ function applyPreselectedValue(select, apiKey) {
143
143
  /**
144
144
  * Handle API error by showing error message in select
145
145
  */
146
- function handleApiError(select, errorMessage) {
146
+ function handleApiError(select, errorMessage, replace = false) {
147
147
  const message = errorMessage instanceof Error ? errorMessage.message : errorMessage;
148
148
  const defaultValue = select.dataset.defaultValue ?? '';
149
149
  // Add "Error: " prefix to match old widget behavior and test expectations
150
150
  const formattedMessage = message.startsWith('Error: ') ? message : `Error: ${message}`;
151
- select.innerHTML += `<option value="${defaultValue}" disabled>${formattedMessage}</option>`;
151
+ if (replace) {
152
+ select.innerHTML = `<option value="${defaultValue}" disabled>${formattedMessage}</option>`;
153
+ }
154
+ else {
155
+ select.innerHTML += `<option value="${defaultValue}" disabled>${formattedMessage}</option>`;
156
+ }
157
+ // Ensure select is enabled so users can see the error
158
+ select.disabled = false;
152
159
  }
153
160
 
154
161
  /**
@@ -458,8 +465,7 @@ async function setupSubdivisionSelection(apiKey, backendUrl, state, config) {
458
465
  : null;
459
466
  // Check if linked country select is multi-select (not allowed)
460
467
  if (linkedCountrySelect && linkedCountrySelect.hasAttribute('multiple')) {
461
- const defaultValue = select.dataset.defaultValue ?? '';
462
- select.innerHTML = `<option value="${defaultValue}" disabled>Error: Cannot link to multi-select country. Use data-country-code instead.</option>`;
468
+ handleApiError(select, 'Cannot link to multi-select country. Use data-country-code instead.', true);
463
469
  continue;
464
470
  }
465
471
  // No direct link → maybe data-country-code
@@ -469,8 +475,7 @@ async function setupSubdivisionSelection(apiKey, backendUrl, state, config) {
469
475
  await updateSubdivisionSelect(select, apiKey, backendUrl, state, config, select.dataset.countryCode);
470
476
  }
471
477
  else {
472
- const defaultValue = select.dataset.defaultValue ?? '';
473
- select.innerHTML += `<option value="${defaultValue}" disabled>Error: No country select present</option>`;
478
+ handleApiError(select, 'No country select present');
474
479
  }
475
480
  }
476
481
  // Always dispatch an update event for user-initiated subdivision changes
@@ -516,7 +521,8 @@ async function updateSubdivisionSelect(select, apiKey, backendUrl, state, config
516
521
  // Use GeoIP only if data-preselected attribute is not set at all
517
522
  const shouldUseGeoIP = preselectedValue === undefined || preselectedValue === null;
518
523
  // Check if this subdivision select prefers official subdivisions
519
- const preferOfficial = select.hasAttribute('data-prefer-official');
524
+ const preferOfficial = select.hasAttribute('data-prefer-official') ||
525
+ config.preferOfficialSubdivisions;
520
526
  const languageHeaders = CountriesDBClient.getLanguageHeaders(config.forcedLanguage, config.defaultLanguage);
521
527
  const subdivisionsResult = await CountriesDBClient.fetchSubdivisions({
522
528
  apiKey,
@@ -613,8 +619,7 @@ async function updateSubdivisionSelect(select, apiKey, backendUrl, state, config
613
619
  }
614
620
  else if (!select.dataset.country ||
615
621
  !document.querySelector(`.country-selection[data-name="${select.dataset.country}"]`)) {
616
- const defaultValue = select.dataset.defaultValue ?? '';
617
- select.innerHTML += `<option value="${defaultValue}" disabled>Error: No country select present</option>`;
622
+ handleApiError(select, 'No country select present');
618
623
  }
619
624
  }
620
625
  /**
@@ -643,8 +648,7 @@ async function setupCountrySelection(apiKey, backendUrl, state, config, subdivis
643
648
  if (name && seenNames[name]) {
644
649
  select.removeAttribute('data-name');
645
650
  initializeSelect(select, '&mdash;');
646
- const defaultValue = select.dataset.defaultValue ?? '';
647
- select.innerHTML += `<option value="${defaultValue}" disabled>Error: Duplicate field</option>`;
651
+ handleApiError(select, 'Duplicate field');
648
652
  continue;
649
653
  }
650
654
  if (name) {
@@ -808,6 +812,7 @@ async function CountriesWidgetLoad(options = {}) {
808
812
  followUpward: config.followUpward || false,
809
813
  showSubdivisionType: config.showSubdivisionType !== false,
810
814
  allowParentSelection: config.allowParentSelection || false,
815
+ preferOfficialSubdivisions: config.preferOfficialSubdivisions || false,
811
816
  subdivisionRomanizationPreference: config.subdivisionRomanizationPreference,
812
817
  preferLocalVariant: config.preferLocalVariant || false,
813
818
  subdivisionNameFilter: config.subdivisionNameFilter,
@@ -817,6 +822,7 @@ async function CountriesWidgetLoad(options = {}) {
817
822
  followUpward: config.followUpward || false,
818
823
  showSubdivisionType: config.showSubdivisionType !== false,
819
824
  allowParentSelection: config.allowParentSelection || false,
825
+ preferOfficialSubdivisions: config.preferOfficialSubdivisions || false,
820
826
  subdivisionRomanizationPreference: config.subdivisionRomanizationPreference,
821
827
  preferLocalVariant: config.preferLocalVariant || false,
822
828
  forcedLanguage: config.forcedLanguage,
@@ -849,6 +855,10 @@ async function CountriesWidgetLoad(options = {}) {
849
855
  * Get configuration from options or script URL parameters
850
856
  */
851
857
  function getConfigFromOptionsOrScript(options) {
858
+ // Check for global config first (for bundled widgets that need config before auto-init)
859
+ const globalConfig = typeof window !== 'undefined' && window.CountriesDBConfig
860
+ ? window.CountriesDBConfig
861
+ : null;
852
862
  // Try to get config from script URL (for backward compatibility with widget.blade.php)
853
863
  let scriptUrl = null;
854
864
  try {
@@ -877,36 +887,57 @@ function getConfigFromOptionsOrScript(options) {
877
887
  // Ignore errors
878
888
  }
879
889
  const config = {
880
- publicKey: options.publicKey || scriptUrl?.searchParams.get('public_key') || '',
881
- backendUrl: options.backendUrl ?? scriptUrl?.searchParams.get('backend_url') ?? getBackendUrlFromScript(),
882
- defaultLanguage: options.defaultLanguage || scriptUrl?.searchParams.get('default_language') || undefined,
883
- forcedLanguage: options.forcedLanguage || scriptUrl?.searchParams.get('forced_language') || undefined,
890
+ // Priority: options > globalConfig > scriptUrl params > defaults
891
+ publicKey: options.publicKey ?? globalConfig?.publicKey ?? scriptUrl?.searchParams.get('public_key') ?? '',
892
+ backendUrl: options.backendUrl ?? globalConfig?.backendUrl ?? scriptUrl?.searchParams.get('backend_url') ?? getBackendUrlFromScript(),
893
+ defaultLanguage: options.defaultLanguage ?? globalConfig?.defaultLanguage ?? scriptUrl?.searchParams.get('default_language') ?? undefined,
894
+ forcedLanguage: options.forcedLanguage ?? globalConfig?.forcedLanguage ?? scriptUrl?.searchParams.get('forced_language') ?? undefined,
884
895
  showSubdivisionType: options.showSubdivisionType !== undefined
885
896
  ? options.showSubdivisionType
886
- : parseBoolean(scriptUrl?.searchParams.get('show_subdivision_type') ?? '1'),
897
+ : globalConfig?.showSubdivisionType !== undefined
898
+ ? globalConfig.showSubdivisionType
899
+ : parseBoolean(scriptUrl?.searchParams.get('show_subdivision_type') ?? '1'),
887
900
  followRelated: options.followRelated !== undefined
888
901
  ? options.followRelated
889
- : parseBoolean(scriptUrl?.searchParams.get('follow_related') ?? 'false'),
902
+ : globalConfig?.followRelated !== undefined
903
+ ? globalConfig.followRelated
904
+ : parseBoolean(scriptUrl?.searchParams.get('follow_related') ?? 'false'),
890
905
  followUpward: options.followUpward !== undefined
891
906
  ? options.followUpward
892
- : parseBoolean(scriptUrl?.searchParams.get('follow_upward') ?? 'false'),
907
+ : globalConfig?.followUpward !== undefined
908
+ ? globalConfig.followUpward
909
+ : parseBoolean(scriptUrl?.searchParams.get('follow_upward') ?? 'false'),
893
910
  allowParentSelection: options.allowParentSelection !== undefined
894
911
  ? options.allowParentSelection
895
- : parseBoolean(scriptUrl?.searchParams.get('allow_parent_selection') ?? 'false'),
912
+ : globalConfig?.allowParentSelection !== undefined
913
+ ? globalConfig.allowParentSelection
914
+ : parseBoolean(scriptUrl?.searchParams.get('allow_parent_selection') ?? 'false'),
896
915
  isoCountryNames: options.isoCountryNames !== undefined
897
916
  ? options.isoCountryNames
898
- : parseBoolean(scriptUrl?.searchParams.get('iso_country_names') ?? 'false'),
917
+ : globalConfig?.isoCountryNames !== undefined
918
+ ? globalConfig.isoCountryNames
919
+ : parseBoolean(scriptUrl?.searchParams.get('iso_country_names') ?? 'false'),
920
+ preferOfficialSubdivisions: options.preferOfficialSubdivisions !== undefined
921
+ ? options.preferOfficialSubdivisions
922
+ : globalConfig?.preferOfficialSubdivisions !== undefined
923
+ ? globalConfig.preferOfficialSubdivisions
924
+ : parseBoolean(scriptUrl?.searchParams.get('prefer_official') ?? 'false'),
899
925
  subdivisionRomanizationPreference: options.subdivisionRomanizationPreference ||
926
+ globalConfig?.subdivisionRomanizationPreference ||
900
927
  scriptUrl?.searchParams.get('subdivision_romanization_preference') ||
901
928
  undefined,
902
929
  preferLocalVariant: options.preferLocalVariant !== undefined
903
930
  ? options.preferLocalVariant
904
- : parseBoolean(scriptUrl?.searchParams.get('prefer_local_variant') ?? 'false'),
905
- countryNameFilter: options.countryNameFilter,
906
- subdivisionNameFilter: options.subdivisionNameFilter,
931
+ : globalConfig?.preferLocalVariant !== undefined
932
+ ? globalConfig.preferLocalVariant
933
+ : parseBoolean(scriptUrl?.searchParams.get('prefer_local_variant') ?? 'false'),
934
+ countryNameFilter: options.countryNameFilter ?? globalConfig?.countryNameFilter,
935
+ subdivisionNameFilter: options.subdivisionNameFilter ?? globalConfig?.subdivisionNameFilter,
907
936
  autoInit: options.autoInit !== undefined
908
937
  ? options.autoInit
909
- : parseBoolean(scriptUrl?.searchParams.get('auto_init') ?? 'true'),
938
+ : globalConfig?.autoInit !== undefined
939
+ ? globalConfig.autoInit
940
+ : parseBoolean(scriptUrl?.searchParams.get('auto_init') ?? 'true'),
910
941
  };
911
942
  // Resolve filter functions from global scope if specified by name
912
943
  if (scriptUrl) {
@@ -1006,14 +1037,14 @@ function parseBoolean(value) {
1006
1037
  * Show error when both follow_related and follow_upward are enabled
1007
1038
  */
1008
1039
  function showParamConflictError() {
1009
- const errorMessage = 'Error: Cannot enable both follow_related and follow_upward';
1040
+ const errorMessage = 'Cannot enable both follow_related and follow_upward';
1010
1041
  const countrySelects = Array.from(document.querySelectorAll('.country-selection'));
1011
1042
  for (const select of countrySelects) {
1012
- select.innerHTML = `<option value="${select.dataset.defaultValue ?? ''}" disabled>${errorMessage}</option>`;
1043
+ handleApiError(select, errorMessage, true);
1013
1044
  }
1014
1045
  const subdivisionSelects = Array.from(document.querySelectorAll('.subdivision-selection'));
1015
1046
  for (const select of subdivisionSelects) {
1016
- select.innerHTML = `<option value="${select.dataset.defaultValue ?? ''}" disabled>${errorMessage}</option>`;
1047
+ handleApiError(select, errorMessage, true);
1017
1048
  }
1018
1049
  }
1019
1050
  // Expose public loader