lucos_search_component 2.0.35 → 3.0.0

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 CHANGED
@@ -32,7 +32,7 @@ The following attributes can be added to the lucos-search span:
32
32
  * **data-api-key** \[required\] — a valid API key for the production instance of [lucos_arachne](https://github.com/lucas42/lucos_arachne), as generated by [lucos_creds](https://github.com/lucas42/lucos_creds).
33
33
  * **data-types** — A comma separated list of item types to search for (defaults to all types).
34
34
  * **data-exclude-types** — A comma separated list of item types to exclude from the search (ignored if `data-types` is set).
35
- * **data-no-lang** — The name to give for a "no language" option. If set, a special entry with the URI `https://eolas.l42.eu/metadata/language/zxx/` is added to the top of the list. Only meaningful when `data-types="Language"`.
35
+ * **data-label-override-zxx** — Override the displayed label for the `https://eolas.l42.eu/metadata/language/zxx/` entry in both the dropdown and selected lozenge. Has no effect on whether `zxx` appears in the list — that depends on whether `zxx` is present in the search index. Only meaningful when `data-types="Language"`.
36
36
  * **data-common** — A comma separated list of item URIs to pin in a "Common" group at the top of the list, above the normal results. Only meaningful when `data-types="Language"`.
37
37
  * **data-preload** — If present, all options are loaded upfront rather than fetched on search. Suitable for small, finite datasets such as languages.
38
38
 
@@ -44,7 +44,7 @@ To use `lucos-search` as a language selector (the replacement for the removed `l
44
44
  <span is="lucos-search"
45
45
  data-api-key="..."
46
46
  data-types="Language"
47
- data-no-lang="Instrumental / No Language"
47
+ data-label-override-zxx="Instrumental / No Language"
48
48
  data-common="https://eolas.l42.eu/metadata/language/en/,https://eolas.l42.eu/metadata/language/ga/,https://eolas.l42.eu/metadata/language/zxx/"
49
49
  data-preload>
50
50
  <select multiple>
@@ -62,7 +62,7 @@ Selected options use the language's full URI as their value (e.g. `https://eolas
62
62
  Key differences:
63
63
  * **Values are URIs, not ISO codes.** `lucos-lang` stored ISO 639 codes (e.g. `en`, `ga`). `lucos-search` stores full URIs (e.g. `https://eolas.l42.eu/metadata/language/en/`). Update any stored values and form handling accordingly.
64
64
  * **`data-common` takes URIs, not codes.** Pass full language URIs instead of ISO codes.
65
- * **`data-no-lang` works the same way.** The "no language" entry is added with URI `https://eolas.l42.eu/metadata/language/zxx/`.
65
+ * **Use `data-label-override-zxx` instead of `data-no-lang`.** To show a custom label for the `zxx` (No linguistic content) entry, set `data-label-override-zxx="My Label"`. The `data-no-lang` attribute has been removed in v2.0.0.
66
66
 
67
67
  ## Manual Testing
68
68
 
package/dist/index.js CHANGED
@@ -5657,7 +5657,7 @@ var tomSelectStylesheet = "/**\n * tom-select.css (v2.6.0)\n * Copyright (c) con
5657
5657
 
5658
5658
  class LucosSearchComponent extends HTMLSpanElement {
5659
5659
  static get observedAttributes() {
5660
- return ['data-api-key','data-types','data-exclude-types','data-no-lang','data-common','data-preload'];
5660
+ return ['data-api-key','data-types','data-exclude-types','data-label-override-zxx','data-common','data-preload'];
5661
5661
  }
5662
5662
  constructor() {
5663
5663
  super();
@@ -5821,8 +5821,6 @@ class LucosSearchComponent extends HTMLSpanElement {
5821
5821
 
5822
5822
  errorMessage.setAttribute('hidden', '');
5823
5823
  const commonSet = new Set((component._commonOptions || []).map(o => o.id));
5824
- const noLang = component.noLangOption;
5825
- const noLangIsCommon = noLang && commonSet.has(noLang.id);
5826
5824
  // When preloaded, filter locally instead of hitting Typesense
5827
5825
  if (component._preloadedOptions) {
5828
5826
  const q = query.toLowerCase();
@@ -5843,7 +5841,6 @@ class LucosSearchComponent extends HTMLSpanElement {
5843
5841
  : component._commonOptions;
5844
5842
  filteredCommon.forEach(opt => this.addOption(opt));
5845
5843
  }
5846
- if (noLang && !noLangIsCommon) results.unshift(noLang);
5847
5844
  callback(results);
5848
5845
  return;
5849
5846
  }
@@ -5871,8 +5868,6 @@ class LucosSearchComponent extends HTMLSpanElement {
5871
5868
  : component._commonOptions;
5872
5869
  filteredCommon.forEach(opt => this.addOption(opt));
5873
5870
  }
5874
- // Don't add noLang as standalone if it's already covered by a common item
5875
- if (noLang && !noLangIsCommon) results.unshift(noLang);
5876
5871
  callback(results);
5877
5872
  } catch(err) {
5878
5873
  if (err.name === 'AbortError') return;
@@ -5894,13 +5889,9 @@ class LucosSearchComponent extends HTMLSpanElement {
5894
5889
  onFocus: function() {
5895
5890
  this.clearOptions();
5896
5891
  const commonSet = new Set((component._commonOptions || []).map(o => o.id));
5897
- // Re-add common items first so they own any shared IDs (e.g. zxx in both data-no-lang and data-common)
5898
5892
  if (component._commonOptions) {
5899
5893
  component._commonOptions.forEach(opt => this.addOption(opt));
5900
5894
  }
5901
- const noLang = component.noLangOption;
5902
- // Skip noLang if its ID is already a common item (would be silently discarded as a duplicate)
5903
- if (noLang && !commonSet.has(noLang.id)) this.addOption(noLang);
5904
5895
  // Re-add preloaded options (excluding common items which are shown separately)
5905
5896
  if (component._preloadedOptions) {
5906
5897
  component._preloadedOptions
@@ -5911,7 +5902,6 @@ class LucosSearchComponent extends HTMLSpanElement {
5911
5902
  // On startup, update any existing options with latest data from search
5912
5903
  onInitialize: async function() {
5913
5904
  const ids = Object.keys(this.options);
5914
- const noLang = component.noLangOption;
5915
5905
  // Fetch and register common items (x-common group goes first)
5916
5906
  const commonIds = component.commonIds;
5917
5907
  if (commonIds.length > 0) {
@@ -5923,15 +5913,8 @@ class LucosSearchComponent extends HTMLSpanElement {
5923
5913
  });
5924
5914
  const commonResults = await component.searchRequest(commonParams);
5925
5915
  component._commonOptions = commonResults.map(r => ({...r, lang_family: 'x-common'}));
5926
- // noLang (zxx) doesn't exist in Typesense, so add it synthetically if it's listed in data-common
5927
- if (noLang && component.commonIds.includes(noLang.id)) {
5928
- component._commonOptions.push({...noLang, lang_family: 'x-common'});
5929
- }
5930
5916
  component._commonOptions.forEach(opt => this.addOption(opt));
5931
5917
  }
5932
- // Add noLang option now (after common items) so we can check for overlap
5933
- const noLangIsCommon = noLang && component._commonOptions && component._commonOptions.some(o => o.id === noLang.id);
5934
- if (noLang && !noLangIsCommon) this.addOption(noLang);
5935
5918
  // In language mode, fetch families and register option groups
5936
5919
  if (component.isLanguageMode) {
5937
5920
  const families = await component.getLanguageFamilies();
@@ -5955,9 +5938,9 @@ class LucosSearchComponent extends HTMLSpanElement {
5955
5938
  preloaded.filter(r => !commonSet.has(r.id)).forEach(r => this.addOption(r));
5956
5939
  }
5957
5940
  if (ids.length < 1) return;
5958
- // Fetch real options from Typesense, excluding synthetic/preloaded items
5941
+ // Fetch real options from Typesense, excluding common/preloaded items
5959
5942
  const preloadedIds = component._preloadedOptions ? new Set(component._preloadedOptions.map(r => r.id)) : new Set();
5960
- const excludeIds = new Set([...(noLang ? [noLang.id] : []), ...commonIds, ...preloadedIds]);
5943
+ const excludeIds = new Set([...commonIds, ...preloadedIds]);
5961
5944
  const idsToFetch = ids.filter(id => !excludeIds.has(id));
5962
5945
  if (idsToFetch.length > 0) {
5963
5946
  const searchParams = new URLSearchParams({
@@ -5970,10 +5953,6 @@ class LucosSearchComponent extends HTMLSpanElement {
5970
5953
  this.updateOption(result.id, result);
5971
5954
  });
5972
5955
  }
5973
- // Update any pre-selected no-lang option with the synthetic data
5974
- if (noLang && ids.includes(noLang.id)) {
5975
- this.updateOption(noLang.id, noLang);
5976
- }
5977
5956
  // Update any pre-selected common items with fresh data
5978
5957
  if (component._commonOptions) {
5979
5958
  component._commonOptions.forEach(opt => {
@@ -5993,14 +5972,20 @@ class LucosSearchComponent extends HTMLSpanElement {
5993
5972
  },
5994
5973
  render:{
5995
5974
  option: function(data, escape) {
5996
- let label = escape(data.pref_label);
5975
+ const zxxOverride = component.getAttribute("data-label-override-zxx");
5976
+ const displayLabel = (zxxOverride && data.id === 'https://eolas.l42.eu/metadata/language/zxx/')
5977
+ ? zxxOverride
5978
+ : data.pref_label;
5979
+ let label = escape(displayLabel);
5997
5980
  let alt_label = "";
5998
- if (data.highlight.pref_label) {
5999
- label = data.highlight.pref_label.snippet;
6000
- } else if (data.highlight.labels) {
6001
- const matched_label = data.highlight.labels.find(l => l.matched_tokens.length > 0);
6002
- if (matched_label) {
6003
- alt_label = ` <span class="alt-label">(${matched_label.snippet})</span>`;
5981
+ if (!zxxOverride || data.id !== 'https://eolas.l42.eu/metadata/language/zxx/') {
5982
+ if (data.highlight.pref_label) {
5983
+ label = data.highlight.pref_label.snippet;
5984
+ } else if (data.highlight.labels) {
5985
+ const matched_label = data.highlight.labels.find(l => l.matched_tokens.length > 0);
5986
+ if (matched_label) {
5987
+ alt_label = ` <span class="alt-label">(${matched_label.snippet})</span>`;
5988
+ }
6004
5989
  }
6005
5990
  }
6006
5991
  label = label.replace(` (${data.type})`,""); // No need to include any type disambiguation in label, as the type lozenge is shown when multiple types are configured
@@ -6010,7 +5995,11 @@ class LucosSearchComponent extends HTMLSpanElement {
6010
5995
  return `<div>${label}${alt_label}${typeLozenge}</div>`;
6011
5996
  },
6012
5997
  item: function(data, escape) {
6013
- return `<div class="lozenge" data-type="${escape(data.type)}" data-category="${escape(data.category)}"><a href="${data.id}" target="_blank">${escape(data.pref_label)}</a></div>`;
5998
+ const zxxOverride = component.getAttribute("data-label-override-zxx");
5999
+ const displayLabel = (zxxOverride && data.id === 'https://eolas.l42.eu/metadata/language/zxx/')
6000
+ ? zxxOverride
6001
+ : data.pref_label;
6002
+ return `<div class="lozenge" data-type="${escape(data.type)}" data-category="${escape(data.category)}"><a href="${data.id}" target="_blank">${escape(displayLabel)}</a></div>`;
6014
6003
  },
6015
6004
  },
6016
6005
  });
@@ -6049,18 +6038,6 @@ class LucosSearchComponent extends HTMLSpanElement {
6049
6038
  this._formdataHandler = null;
6050
6039
  }
6051
6040
  }
6052
- get noLangOption() {
6053
- const label = this.getAttribute("data-no-lang");
6054
- if (!label) return null;
6055
- return {
6056
- id: 'https://eolas.l42.eu/metadata/language/zxx/',
6057
- pref_label: label,
6058
- type: 'Language',
6059
- category: 'Anthropological',
6060
- labels: [],
6061
- highlight: {},
6062
- };
6063
- }
6064
6041
  get isLanguageMode() {
6065
6042
  const types = this.getAttribute("data-types");
6066
6043
  if (!types) return false;
@@ -15,10 +15,10 @@
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
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>
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>
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>
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>
21
- <label for="search11">Languages with no-lang, common and preload:</label><span is="lucos-search" data-api-key="${KEY_LUCOS_ARACHNE}" data-types="Language" data-no-lang="Instrumental / 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/" data-preload><select id="search11" multiple ></select></span>
18
+ <label for="search9">Languages with common pinned:</label><span is="lucos-search" data-api-key="${KEY_LUCOS_ARACHNE}" data-types="Language" data-label-override-zxx="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>
19
+ <label for="search6">Languages with label override for zxx:</label><span is="lucos-search" data-api-key="${KEY_LUCOS_ARACHNE}" data-types="Language" data-label-override-zxx="No Language"><select id="search6"></select></span>
20
+ <label for="search7">Languages with zxx pre-selected:</label><span is="lucos-search" data-api-key="${KEY_LUCOS_ARACHNE}" data-types="Language" data-label-override-zxx="No Language"><select id="search7" multiple><option selected>https://eolas.l42.eu/metadata/language/zxx/</option></select></span>
21
+ <label for="search11">Languages with label override, common and preload:</label><span is="lucos-search" data-api-key="${KEY_LUCOS_ARACHNE}" data-types="Language" data-label-override-zxx="Instrumental / 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/" data-preload><select id="search11" multiple ></select></span>
22
22
  <label for="search5">More than 10:</label>
23
23
  <span is="lucos-search" data-api-key="${KEY_LUCOS_ARACHNE}" data-exclude_types="Track"><select id="search5" multiple>
24
24
  <option selected>https://eolas.l42.eu/metadata/place/125/</option>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lucos_search_component",
3
- "version": "2.0.35",
3
+ "version": "3.0.0",
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','data-preload'];
6
+ return ['data-api-key','data-types','data-exclude-types','data-label-override-zxx','data-common','data-preload'];
7
7
  }
8
8
  constructor() {
9
9
  super();
@@ -167,8 +167,6 @@ class LucosSearchComponent extends HTMLSpanElement {
167
167
 
168
168
  errorMessage.setAttribute('hidden', '');
169
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
170
  // When preloaded, filter locally instead of hitting Typesense
173
171
  if (component._preloadedOptions) {
174
172
  const q = query.toLowerCase();
@@ -189,7 +187,6 @@ class LucosSearchComponent extends HTMLSpanElement {
189
187
  : component._commonOptions;
190
188
  filteredCommon.forEach(opt => this.addOption(opt));
191
189
  }
192
- if (noLang && !noLangIsCommon) results.unshift(noLang);
193
190
  callback(results);
194
191
  return;
195
192
  }
@@ -217,8 +214,6 @@ class LucosSearchComponent extends HTMLSpanElement {
217
214
  : component._commonOptions;
218
215
  filteredCommon.forEach(opt => this.addOption(opt));
219
216
  }
220
- // Don't add noLang as standalone if it's already covered by a common item
221
- if (noLang && !noLangIsCommon) results.unshift(noLang);
222
217
  callback(results);
223
218
  } catch(err) {
224
219
  if (err.name === 'AbortError') return;
@@ -240,13 +235,9 @@ class LucosSearchComponent extends HTMLSpanElement {
240
235
  onFocus: function() {
241
236
  this.clearOptions();
242
237
  const commonSet = new Set((component._commonOptions || []).map(o => o.id));
243
- // Re-add common items first so they own any shared IDs (e.g. zxx in both data-no-lang and data-common)
244
238
  if (component._commonOptions) {
245
239
  component._commonOptions.forEach(opt => this.addOption(opt));
246
240
  }
247
- const noLang = component.noLangOption;
248
- // Skip noLang if its ID is already a common item (would be silently discarded as a duplicate)
249
- if (noLang && !commonSet.has(noLang.id)) this.addOption(noLang);
250
241
  // Re-add preloaded options (excluding common items which are shown separately)
251
242
  if (component._preloadedOptions) {
252
243
  component._preloadedOptions
@@ -257,7 +248,6 @@ class LucosSearchComponent extends HTMLSpanElement {
257
248
  // On startup, update any existing options with latest data from search
258
249
  onInitialize: async function() {
259
250
  const ids = Object.keys(this.options);
260
- const noLang = component.noLangOption;
261
251
  // Fetch and register common items (x-common group goes first)
262
252
  const commonIds = component.commonIds;
263
253
  if (commonIds.length > 0) {
@@ -269,15 +259,8 @@ class LucosSearchComponent extends HTMLSpanElement {
269
259
  });
270
260
  const commonResults = await component.searchRequest(commonParams);
271
261
  component._commonOptions = commonResults.map(r => ({...r, lang_family: 'x-common'}));
272
- // noLang (zxx) doesn't exist in Typesense, so add it synthetically if it's listed in data-common
273
- if (noLang && component.commonIds.includes(noLang.id)) {
274
- component._commonOptions.push({...noLang, lang_family: 'x-common'});
275
- }
276
262
  component._commonOptions.forEach(opt => this.addOption(opt));
277
263
  }
278
- // Add noLang option now (after common items) so we can check for overlap
279
- const noLangIsCommon = noLang && component._commonOptions && component._commonOptions.some(o => o.id === noLang.id);
280
- if (noLang && !noLangIsCommon) this.addOption(noLang);
281
264
  // In language mode, fetch families and register option groups
282
265
  if (component.isLanguageMode) {
283
266
  const families = await component.getLanguageFamilies();
@@ -301,9 +284,9 @@ class LucosSearchComponent extends HTMLSpanElement {
301
284
  preloaded.filter(r => !commonSet.has(r.id)).forEach(r => this.addOption(r));
302
285
  }
303
286
  if (ids.length < 1) return;
304
- // Fetch real options from Typesense, excluding synthetic/preloaded items
287
+ // Fetch real options from Typesense, excluding common/preloaded items
305
288
  const preloadedIds = component._preloadedOptions ? new Set(component._preloadedOptions.map(r => r.id)) : new Set();
306
- const excludeIds = new Set([...(noLang ? [noLang.id] : []), ...commonIds, ...preloadedIds]);
289
+ const excludeIds = new Set([...commonIds, ...preloadedIds]);
307
290
  const idsToFetch = ids.filter(id => !excludeIds.has(id));
308
291
  if (idsToFetch.length > 0) {
309
292
  const searchParams = new URLSearchParams({
@@ -316,10 +299,6 @@ class LucosSearchComponent extends HTMLSpanElement {
316
299
  this.updateOption(result.id, result);
317
300
  });
318
301
  }
319
- // Update any pre-selected no-lang option with the synthetic data
320
- if (noLang && ids.includes(noLang.id)) {
321
- this.updateOption(noLang.id, noLang);
322
- }
323
302
  // Update any pre-selected common items with fresh data
324
303
  if (component._commonOptions) {
325
304
  component._commonOptions.forEach(opt => {
@@ -339,14 +318,20 @@ class LucosSearchComponent extends HTMLSpanElement {
339
318
  },
340
319
  render:{
341
320
  option: function(data, escape) {
342
- let label = escape(data.pref_label);
321
+ const zxxOverride = component.getAttribute("data-label-override-zxx");
322
+ const displayLabel = (zxxOverride && data.id === 'https://eolas.l42.eu/metadata/language/zxx/')
323
+ ? zxxOverride
324
+ : data.pref_label;
325
+ let label = escape(displayLabel);
343
326
  let alt_label = "";
344
- if (data.highlight.pref_label) {
345
- label = data.highlight.pref_label.snippet;
346
- } else if (data.highlight.labels) {
347
- const matched_label = data.highlight.labels.find(l => l.matched_tokens.length > 0);
348
- if (matched_label) {
349
- alt_label = ` <span class="alt-label">(${matched_label.snippet})</span>`;
327
+ if (!zxxOverride || data.id !== 'https://eolas.l42.eu/metadata/language/zxx/') {
328
+ if (data.highlight.pref_label) {
329
+ label = data.highlight.pref_label.snippet;
330
+ } else if (data.highlight.labels) {
331
+ const matched_label = data.highlight.labels.find(l => l.matched_tokens.length > 0);
332
+ if (matched_label) {
333
+ alt_label = ` <span class="alt-label">(${matched_label.snippet})</span>`;
334
+ }
350
335
  }
351
336
  }
352
337
  label = label.replace(` (${data.type})`,""); // No need to include any type disambiguation in label, as the type lozenge is shown when multiple types are configured
@@ -356,7 +341,11 @@ class LucosSearchComponent extends HTMLSpanElement {
356
341
  return `<div>${label}${alt_label}${typeLozenge}</div>`;
357
342
  },
358
343
  item: function(data, escape) {
359
- return `<div class="lozenge" data-type="${escape(data.type)}" data-category="${escape(data.category)}"><a href="${data.id}" target="_blank">${escape(data.pref_label)}</a></div>`;
344
+ const zxxOverride = component.getAttribute("data-label-override-zxx");
345
+ const displayLabel = (zxxOverride && data.id === 'https://eolas.l42.eu/metadata/language/zxx/')
346
+ ? zxxOverride
347
+ : data.pref_label;
348
+ return `<div class="lozenge" data-type="${escape(data.type)}" data-category="${escape(data.category)}"><a href="${data.id}" target="_blank">${escape(displayLabel)}</a></div>`;
360
349
  },
361
350
  },
362
351
  });
@@ -395,18 +384,6 @@ class LucosSearchComponent extends HTMLSpanElement {
395
384
  this._formdataHandler = null;
396
385
  }
397
386
  }
398
- get noLangOption() {
399
- const label = this.getAttribute("data-no-lang");
400
- if (!label) return null;
401
- return {
402
- id: 'https://eolas.l42.eu/metadata/language/zxx/',
403
- pref_label: label,
404
- type: 'Language',
405
- category: 'Anthropological',
406
- labels: [],
407
- highlight: {},
408
- };
409
- }
410
387
  get isLanguageMode() {
411
388
  const types = this.getAttribute("data-types");
412
389
  if (!types) return false;