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 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
- const commonIds = new Set(component._commonOptions.map(o => o.id));
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
- const noLangIsCommon = noLang && component._commonOptions && component._commonOptions.some(o => o.id === noLang.id);
5864
- if (noLang && !noLangIsCommon) this.addOption(noLang);
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 the synthetic no-lang option and common items
5896
- const excludeIds = new Set([...(noLang ? [noLang.id] : []), ...commonIds]);
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
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lucos_search_component",
3
- "version": "1.0.55",
3
+ "version": "1.0.57",
4
4
  "description": "Web Components for searching lucOS data",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -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
- const commonIds = new Set(component._commonOptions.map(o => o.id));
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
- const noLangIsCommon = noLang && component._commonOptions && component._commonOptions.some(o => o.id === noLang.id);
221
- if (noLang && !noLangIsCommon) this.addOption(noLang);
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 the synthetic no-lang option and common items
253
- const excludeIds = new Set([...(noLang ? [noLang.id] : []), ...commonIds]);
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