lucos_search_component 1.0.55 → 1.0.57
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 +53 -14
- package/example/index.html +1 -0
- package/package.json +1 -1
- package/web-components/lucos-search.js +53 -14
package/dist/index.js
CHANGED
|
@@ -5646,7 +5646,7 @@ var tomSelectStylesheet = "/**\n * tom-select.css (v2.5.2)\n * Copyright (c) con
|
|
|
5646
5646
|
|
|
5647
5647
|
class LucosSearchComponent extends HTMLSpanElement {
|
|
5648
5648
|
static get observedAttributes() {
|
|
5649
|
-
return ['data-api-key','data-types','data-exclude-types','data-no-lang','data-common'];
|
|
5649
|
+
return ['data-api-key','data-types','data-exclude-types','data-no-lang','data-common','data-preload'];
|
|
5650
5650
|
}
|
|
5651
5651
|
constructor() {
|
|
5652
5652
|
super();
|
|
@@ -5809,6 +5809,25 @@ class LucosSearchComponent extends HTMLSpanElement {
|
|
|
5809
5809
|
component._searchAbortController = abortController;
|
|
5810
5810
|
|
|
5811
5811
|
errorMessage.setAttribute('hidden', '');
|
|
5812
|
+
const commonSet = new Set((component._commonOptions || []).map(o => o.id));
|
|
5813
|
+
const noLang = component.noLangOption;
|
|
5814
|
+
const noLangIsCommon = noLang && commonSet.has(noLang.id);
|
|
5815
|
+
// When preloaded, filter locally instead of hitting Typesense
|
|
5816
|
+
if (component._preloadedOptions) {
|
|
5817
|
+
const q = query.toLowerCase();
|
|
5818
|
+
let results = q
|
|
5819
|
+
? component._preloadedOptions.filter(r =>
|
|
5820
|
+
r.pref_label.toLowerCase().includes(q) ||
|
|
5821
|
+
(r.labels && r.labels.some(l => l.toLowerCase().includes(q)))
|
|
5822
|
+
)
|
|
5823
|
+
: [...component._preloadedOptions];
|
|
5824
|
+
results = results.filter(r => !commonSet.has(r.id));
|
|
5825
|
+
this.clearOptions();
|
|
5826
|
+
if (component._commonOptions) component._commonOptions.forEach(opt => this.addOption(opt));
|
|
5827
|
+
if (noLang && !noLangIsCommon) results.unshift(noLang);
|
|
5828
|
+
callback(results);
|
|
5829
|
+
return;
|
|
5830
|
+
}
|
|
5812
5831
|
const queryParams = new URLSearchParams({
|
|
5813
5832
|
q: query,
|
|
5814
5833
|
});
|
|
@@ -5821,18 +5840,12 @@ class LucosSearchComponent extends HTMLSpanElement {
|
|
|
5821
5840
|
let results = await component.searchRequest(queryParams, abortController.signal);
|
|
5822
5841
|
if (abortController.signal.aborted) return;
|
|
5823
5842
|
this.clearOptions();
|
|
5824
|
-
if (component.isLanguageMode) {
|
|
5825
|
-
results.forEach(r => { if (!r.lang_family) r.lang_family = 'qli'; });
|
|
5826
|
-
}
|
|
5827
5843
|
// Remove common items from results to avoid duplication (they're always shown separately)
|
|
5828
5844
|
if (component._commonOptions) {
|
|
5829
|
-
|
|
5830
|
-
results = results.filter(r => !commonIds.has(r.id));
|
|
5845
|
+
results = results.filter(r => !commonSet.has(r.id));
|
|
5831
5846
|
component._commonOptions.forEach(opt => this.addOption(opt));
|
|
5832
5847
|
}
|
|
5833
|
-
const noLang = component.noLangOption;
|
|
5834
5848
|
// Don't add noLang as standalone if it's already covered by a common item
|
|
5835
|
-
const noLangIsCommon = noLang && component._commonOptions && component._commonOptions.some(o => o.id === noLang.id);
|
|
5836
5849
|
if (noLang && !noLangIsCommon) results.unshift(noLang);
|
|
5837
5850
|
callback(results);
|
|
5838
5851
|
} catch(err) {
|
|
@@ -5854,14 +5867,20 @@ class LucosSearchComponent extends HTMLSpanElement {
|
|
|
5854
5867
|
},
|
|
5855
5868
|
onFocus: function() {
|
|
5856
5869
|
this.clearOptions();
|
|
5870
|
+
const commonSet = new Set((component._commonOptions || []).map(o => o.id));
|
|
5857
5871
|
// Re-add common items first so they own any shared IDs (e.g. zxx in both data-no-lang and data-common)
|
|
5858
5872
|
if (component._commonOptions) {
|
|
5859
5873
|
component._commonOptions.forEach(opt => this.addOption(opt));
|
|
5860
5874
|
}
|
|
5861
5875
|
const noLang = component.noLangOption;
|
|
5862
5876
|
// Skip noLang if its ID is already a common item (would be silently discarded as a duplicate)
|
|
5863
|
-
|
|
5864
|
-
|
|
5877
|
+
if (noLang && !commonSet.has(noLang.id)) this.addOption(noLang);
|
|
5878
|
+
// Re-add preloaded options (excluding common items which are shown separately)
|
|
5879
|
+
if (component._preloadedOptions) {
|
|
5880
|
+
component._preloadedOptions
|
|
5881
|
+
.filter(opt => !commonSet.has(opt.id))
|
|
5882
|
+
.forEach(opt => this.addOption(opt));
|
|
5883
|
+
}
|
|
5865
5884
|
},
|
|
5866
5885
|
// On startup, update any existing options with latest data from search
|
|
5867
5886
|
onInitialize: async function() {
|
|
@@ -5886,14 +5905,29 @@ class LucosSearchComponent extends HTMLSpanElement {
|
|
|
5886
5905
|
// In language mode, fetch families and register option groups
|
|
5887
5906
|
if (component.isLanguageMode) {
|
|
5888
5907
|
const families = await component.getLanguageFamilies();
|
|
5889
|
-
this.addOptionGroup('qli', { label: 'Language Isolate' });
|
|
5890
5908
|
families.forEach(family => {
|
|
5891
5909
|
this.addOptionGroup(family.code, { label: family.label });
|
|
5892
5910
|
});
|
|
5893
5911
|
}
|
|
5912
|
+
// Preload all matching options (after groups are registered so options slot correctly)
|
|
5913
|
+
if (component.hasAttribute("data-preload")) {
|
|
5914
|
+
const filterValue = component.getAttribute("data-types")
|
|
5915
|
+
? `type:=[${component.getAttribute("data-types")}]`
|
|
5916
|
+
: component.getAttribute("data-exclude_types")
|
|
5917
|
+
? `type:!=[${component.getAttribute("data-exclude_types")}]`
|
|
5918
|
+
: null;
|
|
5919
|
+
// per_page: 250 acts as an upper bound — data-preload is intended for finite datasets
|
|
5920
|
+
const preloadParams = new URLSearchParams({ q: '*', per_page: 250 });
|
|
5921
|
+
if (filterValue) preloadParams.set("filter_by", filterValue);
|
|
5922
|
+
const preloaded = await component.searchRequest(preloadParams);
|
|
5923
|
+
component._preloadedOptions = preloaded;
|
|
5924
|
+
const commonSet = new Set((component._commonOptions || []).map(o => o.id));
|
|
5925
|
+
preloaded.filter(r => !commonSet.has(r.id)).forEach(r => this.addOption(r));
|
|
5926
|
+
}
|
|
5894
5927
|
if (ids.length < 1) return;
|
|
5895
|
-
// Fetch real options from Typesense, excluding
|
|
5896
|
-
const
|
|
5928
|
+
// Fetch real options from Typesense, excluding synthetic/preloaded items
|
|
5929
|
+
const preloadedIds = component._preloadedOptions ? new Set(component._preloadedOptions.map(r => r.id)) : new Set();
|
|
5930
|
+
const excludeIds = new Set([...(noLang ? [noLang.id] : []), ...commonIds, ...preloadedIds]);
|
|
5897
5931
|
const idsToFetch = ids.filter(id => !excludeIds.has(id));
|
|
5898
5932
|
if (idsToFetch.length > 0) {
|
|
5899
5933
|
const searchParams = new URLSearchParams({
|
|
@@ -5903,7 +5937,6 @@ class LucosSearchComponent extends HTMLSpanElement {
|
|
|
5903
5937
|
});
|
|
5904
5938
|
const results = await component.searchRequest(searchParams);
|
|
5905
5939
|
results.forEach(result => {
|
|
5906
|
-
if (component.isLanguageMode && !result.lang_family) result.lang_family = 'qli';
|
|
5907
5940
|
this.updateOption(result.id, result);
|
|
5908
5941
|
});
|
|
5909
5942
|
}
|
|
@@ -5917,6 +5950,12 @@ class LucosSearchComponent extends HTMLSpanElement {
|
|
|
5917
5950
|
if (ids.includes(opt.id)) this.updateOption(opt.id, opt);
|
|
5918
5951
|
});
|
|
5919
5952
|
}
|
|
5953
|
+
// Update any pre-selected preloaded items with fresh data
|
|
5954
|
+
if (component._preloadedOptions) {
|
|
5955
|
+
component._preloadedOptions.forEach(opt => {
|
|
5956
|
+
if (ids.includes(opt.id)) this.updateOption(opt.id, opt);
|
|
5957
|
+
});
|
|
5958
|
+
}
|
|
5920
5959
|
},
|
|
5921
5960
|
onItemSelect: function (item) {
|
|
5922
5961
|
// Tom-select prevents clicking on link in an item to work as normal, so force it here
|
package/example/index.html
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
<option selected>https://eolas.l42.eu/metadata/language/ain/</option>
|
|
15
15
|
</select></span>
|
|
16
16
|
<label for="search8">Languages grouped by family:</label><span is="lucos-search" data-api-key="${KEY_LUCOS_ARACHNE}" data-types="Language"><select id="search8"></select></span>
|
|
17
|
+
<label for="search10">Languages preloaded:</label><span is="lucos-search" data-api-key="${KEY_LUCOS_ARACHNE}" data-types="Language" data-preload><select id="search10"></select></span>
|
|
17
18
|
<label for="search9">Languages with common pinned:</label><span is="lucos-search" data-api-key="${KEY_LUCOS_ARACHNE}" data-types="Language" data-no-lang="No Language" data-common="https://eolas.l42.eu/metadata/language/en/,https://eolas.l42.eu/metadata/language/ga/,https://eolas.l42.eu/metadata/language/zxx/"><select id="search9"></select></span>
|
|
18
19
|
<label for="search6">Languages with no-lang option:</label><span is="lucos-search" data-api-key="${KEY_LUCOS_ARACHNE}" data-types="Language" data-no-lang="No Language"><select id="search6"></select></span>
|
|
19
20
|
<label for="search7">Languages with no-lang pre-selected:</label><span is="lucos-search" data-api-key="${KEY_LUCOS_ARACHNE}" data-types="Language" data-no-lang="No Language"><select id="search7" multiple><option selected>https://eolas.l42.eu/metadata/language/zxx/</option></select></span>
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@ import tomSelectStylesheet from 'tom-select/dist/css/tom-select.default.css';
|
|
|
3
3
|
|
|
4
4
|
class LucosSearchComponent extends HTMLSpanElement {
|
|
5
5
|
static get observedAttributes() {
|
|
6
|
-
return ['data-api-key','data-types','data-exclude-types','data-no-lang','data-common'];
|
|
6
|
+
return ['data-api-key','data-types','data-exclude-types','data-no-lang','data-common','data-preload'];
|
|
7
7
|
}
|
|
8
8
|
constructor() {
|
|
9
9
|
super();
|
|
@@ -166,6 +166,25 @@ class LucosSearchComponent extends HTMLSpanElement {
|
|
|
166
166
|
component._searchAbortController = abortController;
|
|
167
167
|
|
|
168
168
|
errorMessage.setAttribute('hidden', '');
|
|
169
|
+
const commonSet = new Set((component._commonOptions || []).map(o => o.id));
|
|
170
|
+
const noLang = component.noLangOption;
|
|
171
|
+
const noLangIsCommon = noLang && commonSet.has(noLang.id);
|
|
172
|
+
// When preloaded, filter locally instead of hitting Typesense
|
|
173
|
+
if (component._preloadedOptions) {
|
|
174
|
+
const q = query.toLowerCase();
|
|
175
|
+
let results = q
|
|
176
|
+
? component._preloadedOptions.filter(r =>
|
|
177
|
+
r.pref_label.toLowerCase().includes(q) ||
|
|
178
|
+
(r.labels && r.labels.some(l => l.toLowerCase().includes(q)))
|
|
179
|
+
)
|
|
180
|
+
: [...component._preloadedOptions];
|
|
181
|
+
results = results.filter(r => !commonSet.has(r.id));
|
|
182
|
+
this.clearOptions();
|
|
183
|
+
if (component._commonOptions) component._commonOptions.forEach(opt => this.addOption(opt));
|
|
184
|
+
if (noLang && !noLangIsCommon) results.unshift(noLang);
|
|
185
|
+
callback(results);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
169
188
|
const queryParams = new URLSearchParams({
|
|
170
189
|
q: query,
|
|
171
190
|
});
|
|
@@ -178,18 +197,12 @@ class LucosSearchComponent extends HTMLSpanElement {
|
|
|
178
197
|
let results = await component.searchRequest(queryParams, abortController.signal);
|
|
179
198
|
if (abortController.signal.aborted) return;
|
|
180
199
|
this.clearOptions();
|
|
181
|
-
if (component.isLanguageMode) {
|
|
182
|
-
results.forEach(r => { if (!r.lang_family) r.lang_family = 'qli'; });
|
|
183
|
-
}
|
|
184
200
|
// Remove common items from results to avoid duplication (they're always shown separately)
|
|
185
201
|
if (component._commonOptions) {
|
|
186
|
-
|
|
187
|
-
results = results.filter(r => !commonIds.has(r.id));
|
|
202
|
+
results = results.filter(r => !commonSet.has(r.id));
|
|
188
203
|
component._commonOptions.forEach(opt => this.addOption(opt));
|
|
189
204
|
}
|
|
190
|
-
const noLang = component.noLangOption;
|
|
191
205
|
// Don't add noLang as standalone if it's already covered by a common item
|
|
192
|
-
const noLangIsCommon = noLang && component._commonOptions && component._commonOptions.some(o => o.id === noLang.id);
|
|
193
206
|
if (noLang && !noLangIsCommon) results.unshift(noLang);
|
|
194
207
|
callback(results);
|
|
195
208
|
} catch(err) {
|
|
@@ -211,14 +224,20 @@ class LucosSearchComponent extends HTMLSpanElement {
|
|
|
211
224
|
},
|
|
212
225
|
onFocus: function() {
|
|
213
226
|
this.clearOptions();
|
|
227
|
+
const commonSet = new Set((component._commonOptions || []).map(o => o.id));
|
|
214
228
|
// Re-add common items first so they own any shared IDs (e.g. zxx in both data-no-lang and data-common)
|
|
215
229
|
if (component._commonOptions) {
|
|
216
230
|
component._commonOptions.forEach(opt => this.addOption(opt));
|
|
217
231
|
}
|
|
218
232
|
const noLang = component.noLangOption;
|
|
219
233
|
// Skip noLang if its ID is already a common item (would be silently discarded as a duplicate)
|
|
220
|
-
|
|
221
|
-
|
|
234
|
+
if (noLang && !commonSet.has(noLang.id)) this.addOption(noLang);
|
|
235
|
+
// Re-add preloaded options (excluding common items which are shown separately)
|
|
236
|
+
if (component._preloadedOptions) {
|
|
237
|
+
component._preloadedOptions
|
|
238
|
+
.filter(opt => !commonSet.has(opt.id))
|
|
239
|
+
.forEach(opt => this.addOption(opt));
|
|
240
|
+
}
|
|
222
241
|
},
|
|
223
242
|
// On startup, update any existing options with latest data from search
|
|
224
243
|
onInitialize: async function() {
|
|
@@ -243,14 +262,29 @@ class LucosSearchComponent extends HTMLSpanElement {
|
|
|
243
262
|
// In language mode, fetch families and register option groups
|
|
244
263
|
if (component.isLanguageMode) {
|
|
245
264
|
const families = await component.getLanguageFamilies();
|
|
246
|
-
this.addOptionGroup('qli', { label: 'Language Isolate' });
|
|
247
265
|
families.forEach(family => {
|
|
248
266
|
this.addOptionGroup(family.code, { label: family.label });
|
|
249
267
|
});
|
|
250
268
|
}
|
|
269
|
+
// Preload all matching options (after groups are registered so options slot correctly)
|
|
270
|
+
if (component.hasAttribute("data-preload")) {
|
|
271
|
+
const filterValue = component.getAttribute("data-types")
|
|
272
|
+
? `type:=[${component.getAttribute("data-types")}]`
|
|
273
|
+
: component.getAttribute("data-exclude_types")
|
|
274
|
+
? `type:!=[${component.getAttribute("data-exclude_types")}]`
|
|
275
|
+
: null;
|
|
276
|
+
// per_page: 250 acts as an upper bound — data-preload is intended for finite datasets
|
|
277
|
+
const preloadParams = new URLSearchParams({ q: '*', per_page: 250 });
|
|
278
|
+
if (filterValue) preloadParams.set("filter_by", filterValue);
|
|
279
|
+
const preloaded = await component.searchRequest(preloadParams);
|
|
280
|
+
component._preloadedOptions = preloaded;
|
|
281
|
+
const commonSet = new Set((component._commonOptions || []).map(o => o.id));
|
|
282
|
+
preloaded.filter(r => !commonSet.has(r.id)).forEach(r => this.addOption(r));
|
|
283
|
+
}
|
|
251
284
|
if (ids.length < 1) return;
|
|
252
|
-
// Fetch real options from Typesense, excluding
|
|
253
|
-
const
|
|
285
|
+
// Fetch real options from Typesense, excluding synthetic/preloaded items
|
|
286
|
+
const preloadedIds = component._preloadedOptions ? new Set(component._preloadedOptions.map(r => r.id)) : new Set();
|
|
287
|
+
const excludeIds = new Set([...(noLang ? [noLang.id] : []), ...commonIds, ...preloadedIds]);
|
|
254
288
|
const idsToFetch = ids.filter(id => !excludeIds.has(id));
|
|
255
289
|
if (idsToFetch.length > 0) {
|
|
256
290
|
const searchParams = new URLSearchParams({
|
|
@@ -260,7 +294,6 @@ class LucosSearchComponent extends HTMLSpanElement {
|
|
|
260
294
|
});
|
|
261
295
|
const results = await component.searchRequest(searchParams);
|
|
262
296
|
results.forEach(result => {
|
|
263
|
-
if (component.isLanguageMode && !result.lang_family) result.lang_family = 'qli';
|
|
264
297
|
this.updateOption(result.id, result);
|
|
265
298
|
});
|
|
266
299
|
}
|
|
@@ -274,6 +307,12 @@ class LucosSearchComponent extends HTMLSpanElement {
|
|
|
274
307
|
if (ids.includes(opt.id)) this.updateOption(opt.id, opt);
|
|
275
308
|
});
|
|
276
309
|
}
|
|
310
|
+
// Update any pre-selected preloaded items with fresh data
|
|
311
|
+
if (component._preloadedOptions) {
|
|
312
|
+
component._preloadedOptions.forEach(opt => {
|
|
313
|
+
if (ids.includes(opt.id)) this.updateOption(opt.id, opt);
|
|
314
|
+
});
|
|
315
|
+
}
|
|
277
316
|
},
|
|
278
317
|
onItemSelect: function (item) {
|
|
279
318
|
// Tom-select prevents clicking on link in an item to work as normal, so force it here
|