@countriesdb/widget 0.1.22 → 0.1.24
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 +21 -2
- package/dist/dom-manipulation.js +0 -2
- package/dist/event-system.d.ts +5 -1
- package/dist/event-system.js +28 -0
- package/dist/index.esm.js +84 -82
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +84 -82
- package/dist/index.js.map +1 -1
- package/dist/initialization.js +21 -7
- package/dist/types.d.ts +3 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -400,8 +400,6 @@
|
|
|
400
400
|
else {
|
|
401
401
|
select.innerHTML += `<option value="${defaultValue}" disabled>${formattedMessage}</option>`;
|
|
402
402
|
}
|
|
403
|
-
// Ensure select is enabled so users can see the error
|
|
404
|
-
select.disabled = false;
|
|
405
403
|
}
|
|
406
404
|
|
|
407
405
|
/**
|
|
@@ -435,6 +433,34 @@
|
|
|
435
433
|
});
|
|
436
434
|
select.dispatchEvent(evt);
|
|
437
435
|
}
|
|
436
|
+
/**
|
|
437
|
+
* Dispatch a custom ready event once a select has been populated.
|
|
438
|
+
*/
|
|
439
|
+
function dispatchReadyEvent(select, detail = {}) {
|
|
440
|
+
const selectedValues = select.multiple
|
|
441
|
+
? Array.from(select.selectedOptions || [])
|
|
442
|
+
.map((opt) => opt.value)
|
|
443
|
+
.filter((v) => v !== '')
|
|
444
|
+
: select.value
|
|
445
|
+
? [select.value]
|
|
446
|
+
: [];
|
|
447
|
+
const evt = new CustomEvent('countriesWidget:ready', {
|
|
448
|
+
bubbles: true,
|
|
449
|
+
detail: {
|
|
450
|
+
value: select.value || '',
|
|
451
|
+
selectedValues,
|
|
452
|
+
name: select.dataset.name || null,
|
|
453
|
+
country: select.dataset.country || null,
|
|
454
|
+
isSubdivision: select.classList.contains('subdivision-selection'),
|
|
455
|
+
type: select.classList.contains('subdivision-selection')
|
|
456
|
+
? 'subdivision'
|
|
457
|
+
: 'country',
|
|
458
|
+
phase: 'initial',
|
|
459
|
+
...detail,
|
|
460
|
+
},
|
|
461
|
+
});
|
|
462
|
+
select.dispatchEvent(evt);
|
|
463
|
+
}
|
|
438
464
|
/**
|
|
439
465
|
* Check if an event was initiated by the widget (not user)
|
|
440
466
|
*/
|
|
@@ -711,7 +737,8 @@
|
|
|
711
737
|
: null;
|
|
712
738
|
// Check if linked country select is multi-select (not allowed)
|
|
713
739
|
if (linkedCountrySelect && linkedCountrySelect.hasAttribute('multiple')) {
|
|
714
|
-
|
|
740
|
+
const defaultValue = select.dataset.defaultValue ?? '';
|
|
741
|
+
select.innerHTML = `<option value="${defaultValue}" disabled>Error: Cannot link to multi-select country. Use data-country-code instead.</option>`;
|
|
715
742
|
continue;
|
|
716
743
|
}
|
|
717
744
|
// No direct link → maybe data-country-code
|
|
@@ -721,7 +748,8 @@
|
|
|
721
748
|
await updateSubdivisionSelect(select, apiKey, backendUrl, state, config, select.dataset.countryCode);
|
|
722
749
|
}
|
|
723
750
|
else {
|
|
724
|
-
|
|
751
|
+
const defaultValue = select.dataset.defaultValue ?? '';
|
|
752
|
+
select.innerHTML += `<option value="${defaultValue}" disabled>Error: No country select present</option>`;
|
|
725
753
|
}
|
|
726
754
|
}
|
|
727
755
|
// Always dispatch an update event for user-initiated subdivision changes
|
|
@@ -767,8 +795,10 @@
|
|
|
767
795
|
// Use GeoIP only if data-preselected attribute is not set at all
|
|
768
796
|
const shouldUseGeoIP = preselectedValue === undefined || preselectedValue === null;
|
|
769
797
|
// Check if this subdivision select prefers official subdivisions
|
|
770
|
-
|
|
771
|
-
|
|
798
|
+
// Use data attribute if present, otherwise use config
|
|
799
|
+
const preferOfficial = select.hasAttribute('data-prefer-official')
|
|
800
|
+
? true
|
|
801
|
+
: config.preferOfficialSubdivisions;
|
|
772
802
|
const languageHeaders = CountriesDBClient.getLanguageHeaders(config.forcedLanguage, config.defaultLanguage);
|
|
773
803
|
const subdivisionsResult = await CountriesDBClient.fetchSubdivisions({
|
|
774
804
|
apiKey,
|
|
@@ -854,6 +884,10 @@
|
|
|
854
884
|
finally {
|
|
855
885
|
// Mark initialization as complete
|
|
856
886
|
state.isInitializing.delete(select);
|
|
887
|
+
dispatchReadyEvent(select, {
|
|
888
|
+
type: 'subdivision',
|
|
889
|
+
phase: isReload ? 'reload' : 'initial',
|
|
890
|
+
});
|
|
857
891
|
// Only fire 'reload' if this is a reload, not initial load
|
|
858
892
|
if (isReload && !valueSetByWidget) {
|
|
859
893
|
dispatchUpdateEvent(select, {
|
|
@@ -865,7 +899,8 @@
|
|
|
865
899
|
}
|
|
866
900
|
else if (!select.dataset.country ||
|
|
867
901
|
!document.querySelector(`.country-selection[data-name="${select.dataset.country}"]`)) {
|
|
868
|
-
|
|
902
|
+
const defaultValue = select.dataset.defaultValue ?? '';
|
|
903
|
+
select.innerHTML += `<option value="${defaultValue}" disabled>Error: No country select present</option>`;
|
|
869
904
|
}
|
|
870
905
|
}
|
|
871
906
|
/**
|
|
@@ -894,7 +929,8 @@
|
|
|
894
929
|
if (name && seenNames[name]) {
|
|
895
930
|
select.removeAttribute('data-name');
|
|
896
931
|
initializeSelect(select, '—');
|
|
897
|
-
|
|
932
|
+
const defaultValue = select.dataset.defaultValue ?? '';
|
|
933
|
+
select.innerHTML += `<option value="${defaultValue}" disabled>Error: Duplicate field</option>`;
|
|
898
934
|
continue;
|
|
899
935
|
}
|
|
900
936
|
if (name) {
|
|
@@ -995,6 +1031,10 @@
|
|
|
995
1031
|
finally {
|
|
996
1032
|
// Mark initialization as complete
|
|
997
1033
|
state.isInitializing.delete(select);
|
|
1034
|
+
dispatchReadyEvent(select, {
|
|
1035
|
+
type: 'country',
|
|
1036
|
+
phase: 'initial',
|
|
1037
|
+
});
|
|
998
1038
|
// If no preselected and no geoip selection happened, emit a regular update
|
|
999
1039
|
if (!valueSetByWidget) {
|
|
1000
1040
|
dispatchUpdateEvent(select, { type: 'country', reason: 'regular' });
|
|
@@ -1101,29 +1141,23 @@
|
|
|
1101
1141
|
* Get configuration from options or script URL parameters
|
|
1102
1142
|
*/
|
|
1103
1143
|
function getConfigFromOptionsOrScript(options) {
|
|
1104
|
-
// Check for global config first (for bundled widgets that need config before auto-init)
|
|
1105
|
-
const globalConfig = typeof window !== 'undefined' && window.CountriesDBConfig
|
|
1106
|
-
? window.CountriesDBConfig
|
|
1107
|
-
: null;
|
|
1108
1144
|
// Try to get config from script URL (for backward compatibility with widget.blade.php)
|
|
1109
1145
|
let scriptUrl = null;
|
|
1110
1146
|
try {
|
|
1111
1147
|
let loaderScript = null;
|
|
1112
1148
|
// First try document.currentScript (works during script execution)
|
|
1113
|
-
// But only if it matches the widget pattern (not a bundled file)
|
|
1114
1149
|
if (document.currentScript && document.currentScript instanceof HTMLScriptElement) {
|
|
1115
|
-
|
|
1116
|
-
if (src && (src.includes('@countriesdb/widget') ||
|
|
1117
|
-
src.includes('widget/dist/index.js'))) {
|
|
1118
|
-
loaderScript = document.currentScript;
|
|
1119
|
-
}
|
|
1150
|
+
loaderScript = document.currentScript;
|
|
1120
1151
|
}
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
// Only consider script tags that loaded the widget bundle
|
|
1152
|
+
else {
|
|
1153
|
+
// Fallback: find script tag with @countriesdb/widget in src
|
|
1124
1154
|
const scripts = Array.from(document.getElementsByTagName('script'));
|
|
1125
1155
|
loaderScript = scripts.find((s) => s.src && (s.src.includes('@countriesdb/widget') ||
|
|
1126
1156
|
s.src.includes('widget/dist/index.js'))) || null;
|
|
1157
|
+
// If still not found, try the last script with src
|
|
1158
|
+
if (!loaderScript) {
|
|
1159
|
+
loaderScript = scripts.filter((s) => s.src).pop() || null;
|
|
1160
|
+
}
|
|
1127
1161
|
}
|
|
1128
1162
|
if (loaderScript && loaderScript.src) {
|
|
1129
1163
|
scriptUrl = new URL(loaderScript.src);
|
|
@@ -1133,57 +1167,39 @@
|
|
|
1133
1167
|
// Ignore errors
|
|
1134
1168
|
}
|
|
1135
1169
|
const config = {
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
forcedLanguage: options.forcedLanguage ?? globalConfig?.forcedLanguage ?? scriptUrl?.searchParams.get('forced_language') ?? undefined,
|
|
1170
|
+
publicKey: options.publicKey || scriptUrl?.searchParams.get('public_key') || '',
|
|
1171
|
+
backendUrl: options.backendUrl || scriptUrl?.searchParams.get('backend_url') || getBackendUrlFromScript(),
|
|
1172
|
+
defaultLanguage: options.defaultLanguage || scriptUrl?.searchParams.get('default_language') || undefined,
|
|
1173
|
+
forcedLanguage: options.forcedLanguage || scriptUrl?.searchParams.get('forced_language') || undefined,
|
|
1141
1174
|
showSubdivisionType: options.showSubdivisionType !== undefined
|
|
1142
1175
|
? options.showSubdivisionType
|
|
1143
|
-
:
|
|
1144
|
-
? globalConfig.showSubdivisionType
|
|
1145
|
-
: parseBoolean(scriptUrl?.searchParams.get('show_subdivision_type') ?? '1'),
|
|
1176
|
+
: parseBoolean(scriptUrl?.searchParams.get('show_subdivision_type') ?? '1'),
|
|
1146
1177
|
followRelated: options.followRelated !== undefined
|
|
1147
1178
|
? options.followRelated
|
|
1148
|
-
:
|
|
1149
|
-
? globalConfig.followRelated
|
|
1150
|
-
: parseBoolean(scriptUrl?.searchParams.get('follow_related') ?? 'false'),
|
|
1179
|
+
: parseBoolean(scriptUrl?.searchParams.get('follow_related') ?? 'false'),
|
|
1151
1180
|
followUpward: options.followUpward !== undefined
|
|
1152
1181
|
? options.followUpward
|
|
1153
|
-
:
|
|
1154
|
-
? globalConfig.followUpward
|
|
1155
|
-
: parseBoolean(scriptUrl?.searchParams.get('follow_upward') ?? 'false'),
|
|
1182
|
+
: parseBoolean(scriptUrl?.searchParams.get('follow_upward') ?? 'false'),
|
|
1156
1183
|
allowParentSelection: options.allowParentSelection !== undefined
|
|
1157
1184
|
? options.allowParentSelection
|
|
1158
|
-
:
|
|
1159
|
-
? globalConfig.allowParentSelection
|
|
1160
|
-
: parseBoolean(scriptUrl?.searchParams.get('allow_parent_selection') ?? 'false'),
|
|
1185
|
+
: parseBoolean(scriptUrl?.searchParams.get('allow_parent_selection') ?? 'false'),
|
|
1161
1186
|
isoCountryNames: options.isoCountryNames !== undefined
|
|
1162
1187
|
? options.isoCountryNames
|
|
1163
|
-
:
|
|
1164
|
-
? globalConfig.isoCountryNames
|
|
1165
|
-
: parseBoolean(scriptUrl?.searchParams.get('iso_country_names') ?? 'false'),
|
|
1166
|
-
preferOfficialSubdivisions: options.preferOfficialSubdivisions !== undefined
|
|
1167
|
-
? options.preferOfficialSubdivisions
|
|
1168
|
-
: globalConfig?.preferOfficialSubdivisions !== undefined
|
|
1169
|
-
? globalConfig.preferOfficialSubdivisions
|
|
1170
|
-
: parseBoolean(scriptUrl?.searchParams.get('prefer_official') ?? 'false'),
|
|
1188
|
+
: parseBoolean(scriptUrl?.searchParams.get('iso_country_names') ?? 'false'),
|
|
1171
1189
|
subdivisionRomanizationPreference: options.subdivisionRomanizationPreference ||
|
|
1172
|
-
globalConfig?.subdivisionRomanizationPreference ||
|
|
1173
1190
|
scriptUrl?.searchParams.get('subdivision_romanization_preference') ||
|
|
1174
1191
|
undefined,
|
|
1175
1192
|
preferLocalVariant: options.preferLocalVariant !== undefined
|
|
1176
1193
|
? options.preferLocalVariant
|
|
1177
|
-
:
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1194
|
+
: parseBoolean(scriptUrl?.searchParams.get('prefer_local_variant') ?? 'false'),
|
|
1195
|
+
preferOfficialSubdivisions: options.preferOfficialSubdivisions !== undefined
|
|
1196
|
+
? options.preferOfficialSubdivisions
|
|
1197
|
+
: parseBoolean(scriptUrl?.searchParams.get('prefer_official') ?? 'false'),
|
|
1198
|
+
countryNameFilter: options.countryNameFilter,
|
|
1199
|
+
subdivisionNameFilter: options.subdivisionNameFilter,
|
|
1182
1200
|
autoInit: options.autoInit !== undefined
|
|
1183
1201
|
? options.autoInit
|
|
1184
|
-
:
|
|
1185
|
-
? globalConfig.autoInit
|
|
1186
|
-
: parseBoolean(scriptUrl?.searchParams.get('auto_init') ?? 'true'),
|
|
1202
|
+
: parseBoolean(scriptUrl?.searchParams.get('auto_init') ?? 'true'),
|
|
1187
1203
|
};
|
|
1188
1204
|
// Resolve filter functions from global scope if specified by name
|
|
1189
1205
|
if (scriptUrl) {
|
|
@@ -1211,20 +1227,18 @@
|
|
|
1211
1227
|
try {
|
|
1212
1228
|
let loaderScript = null;
|
|
1213
1229
|
// First try document.currentScript (works during script execution)
|
|
1214
|
-
// But only if it matches the widget pattern (not a bundled file)
|
|
1215
1230
|
if (document.currentScript && document.currentScript instanceof HTMLScriptElement) {
|
|
1216
|
-
|
|
1217
|
-
if (src && (src.includes('@countriesdb/widget') ||
|
|
1218
|
-
src.includes('widget/dist/index.js'))) {
|
|
1219
|
-
loaderScript = document.currentScript;
|
|
1220
|
-
}
|
|
1231
|
+
loaderScript = document.currentScript;
|
|
1221
1232
|
}
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
// Only consider script tags that loaded the widget bundle
|
|
1233
|
+
else {
|
|
1234
|
+
// Fallback: find script tag with @countriesdb/widget in src
|
|
1225
1235
|
const scripts = Array.from(document.getElementsByTagName('script'));
|
|
1226
1236
|
loaderScript = scripts.find((s) => s.src && (s.src.includes('@countriesdb/widget') ||
|
|
1227
1237
|
s.src.includes('widget/dist/index.js'))) || null;
|
|
1238
|
+
// If still not found, try the last script with src
|
|
1239
|
+
if (!loaderScript) {
|
|
1240
|
+
loaderScript = scripts.filter((s) => s.src).pop() || null;
|
|
1241
|
+
}
|
|
1228
1242
|
}
|
|
1229
1243
|
if (loaderScript && loaderScript.src) {
|
|
1230
1244
|
const scriptUrl = new URL(loaderScript.src);
|
|
@@ -1283,14 +1297,14 @@
|
|
|
1283
1297
|
* Show error when both follow_related and follow_upward are enabled
|
|
1284
1298
|
*/
|
|
1285
1299
|
function showParamConflictError() {
|
|
1286
|
-
const errorMessage = 'Cannot enable both follow_related and follow_upward';
|
|
1300
|
+
const errorMessage = 'Error: Cannot enable both follow_related and follow_upward';
|
|
1287
1301
|
const countrySelects = Array.from(document.querySelectorAll('.country-selection'));
|
|
1288
1302
|
for (const select of countrySelects) {
|
|
1289
|
-
|
|
1303
|
+
select.innerHTML = `<option value="${select.dataset.defaultValue ?? ''}" disabled>${errorMessage}</option>`;
|
|
1290
1304
|
}
|
|
1291
1305
|
const subdivisionSelects = Array.from(document.querySelectorAll('.subdivision-selection'));
|
|
1292
1306
|
for (const select of subdivisionSelects) {
|
|
1293
|
-
|
|
1307
|
+
select.innerHTML = `<option value="${select.dataset.defaultValue ?? ''}" disabled>${errorMessage}</option>`;
|
|
1294
1308
|
}
|
|
1295
1309
|
}
|
|
1296
1310
|
// Expose public loader
|
|
@@ -1307,16 +1321,10 @@
|
|
|
1307
1321
|
// Find the script tag that loaded this widget
|
|
1308
1322
|
let loaderScript = null;
|
|
1309
1323
|
// First try document.currentScript (works during script execution)
|
|
1310
|
-
// But only if it matches the widget pattern (not a bundled file)
|
|
1311
1324
|
if (document.currentScript && document.currentScript instanceof HTMLScriptElement) {
|
|
1312
|
-
|
|
1313
|
-
if (src && (src.includes('@countriesdb/widget') ||
|
|
1314
|
-
src.includes('widget/dist/index.js'))) {
|
|
1315
|
-
loaderScript = document.currentScript;
|
|
1316
|
-
}
|
|
1325
|
+
loaderScript = document.currentScript;
|
|
1317
1326
|
}
|
|
1318
|
-
|
|
1319
|
-
if (!loaderScript) {
|
|
1327
|
+
else {
|
|
1320
1328
|
// Fallback: find script tag with @countriesdb/widget in src
|
|
1321
1329
|
const scripts = Array.from(document.getElementsByTagName('script'));
|
|
1322
1330
|
loaderScript = scripts.find((s) => s.src && (s.src.includes('@countriesdb/widget') ||
|
|
@@ -1324,13 +1332,7 @@
|
|
|
1324
1332
|
}
|
|
1325
1333
|
// Default to auto-init = true (only disable if explicitly set to false)
|
|
1326
1334
|
let shouldAutoInit = true;
|
|
1327
|
-
|
|
1328
|
-
? window.CountriesDBConfig || null
|
|
1329
|
-
: null;
|
|
1330
|
-
if (globalConfig && typeof globalConfig.autoInit !== 'undefined') {
|
|
1331
|
-
shouldAutoInit = !!globalConfig.autoInit;
|
|
1332
|
-
}
|
|
1333
|
-
else if (loaderScript && loaderScript.src) {
|
|
1335
|
+
if (loaderScript && loaderScript.src) {
|
|
1334
1336
|
try {
|
|
1335
1337
|
const scriptUrl = new URL(loaderScript.src);
|
|
1336
1338
|
const autoInit = scriptUrl.searchParams.get('auto_init');
|