lucos_search_component 1.0.49 → 1.0.51
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 +89 -11
- package/example/index.html +3 -0
- package/package.json +1 -1
- package/web-components/lucos-search.js +89 -11
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'];
|
|
5649
|
+
return ['data-api-key','data-types','data-exclude-types','data-no-lang'];
|
|
5650
5650
|
}
|
|
5651
5651
|
constructor() {
|
|
5652
5652
|
super();
|
|
@@ -5794,6 +5794,7 @@ class LucosSearchComponent extends HTMLSpanElement {
|
|
|
5794
5794
|
if (!selector) throw new Error("Can't find select element in lucos-search");
|
|
5795
5795
|
selector.setAttribute("multiple", "multiple");
|
|
5796
5796
|
new TomSelect(selector, {
|
|
5797
|
+
...(component.isLanguageMode ? { optgroupField: 'lang_family', lockOptgroupOrder: true } : {}),
|
|
5797
5798
|
valueField: 'id',
|
|
5798
5799
|
labelField: 'pref_label',
|
|
5799
5800
|
searchField: [],
|
|
@@ -5812,6 +5813,11 @@ class LucosSearchComponent extends HTMLSpanElement {
|
|
|
5812
5813
|
try {
|
|
5813
5814
|
const results = await component.searchRequest(queryParams);
|
|
5814
5815
|
this.clearOptions();
|
|
5816
|
+
if (component.isLanguageMode) {
|
|
5817
|
+
results.forEach(r => { if (!r.lang_family) r.lang_family = 'qli'; });
|
|
5818
|
+
}
|
|
5819
|
+
const noLang = component.noLangOption;
|
|
5820
|
+
if (noLang) results.unshift(noLang);
|
|
5815
5821
|
callback(results);
|
|
5816
5822
|
} catch(err) {
|
|
5817
5823
|
callback([]);
|
|
@@ -5831,20 +5837,42 @@ class LucosSearchComponent extends HTMLSpanElement {
|
|
|
5831
5837
|
},
|
|
5832
5838
|
onFocus: function() {
|
|
5833
5839
|
this.clearOptions();
|
|
5840
|
+
const noLang = component.noLangOption;
|
|
5841
|
+
if (noLang) this.addOption(noLang);
|
|
5834
5842
|
},
|
|
5835
5843
|
// On startup, update any existing options with latest data from search
|
|
5836
5844
|
onInitialize: async function() {
|
|
5837
5845
|
const ids = Object.keys(this.options);
|
|
5846
|
+
const noLang = component.noLangOption;
|
|
5847
|
+
// Always make the no-lang option available for new selections
|
|
5848
|
+
if (noLang) this.addOption(noLang);
|
|
5849
|
+
// In language mode, fetch families and register option groups
|
|
5850
|
+
if (component.isLanguageMode) {
|
|
5851
|
+
const families = await component.getLanguageFamilies();
|
|
5852
|
+
this.addOptionGroup('qli', { label: 'Language Isolate' });
|
|
5853
|
+
families.forEach(family => {
|
|
5854
|
+
this.addOptionGroup(family.code, { label: family.label });
|
|
5855
|
+
});
|
|
5856
|
+
}
|
|
5838
5857
|
if (ids.length < 1) return;
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5858
|
+
// Fetch real options from Typesense, excluding the synthetic no-lang option
|
|
5859
|
+
const idsToFetch = noLang ? ids.filter(id => id !== noLang.id) : ids;
|
|
5860
|
+
if (idsToFetch.length > 0) {
|
|
5861
|
+
const searchParams = new URLSearchParams({
|
|
5862
|
+
q: '*',
|
|
5863
|
+
filter_by: `id:[${idsToFetch.join(",")}]`,
|
|
5864
|
+
per_page: idsToFetch.length,
|
|
5865
|
+
});
|
|
5866
|
+
const results = await component.searchRequest(searchParams);
|
|
5867
|
+
results.forEach(result => {
|
|
5868
|
+
if (component.isLanguageMode && !result.lang_family) result.lang_family = 'qli';
|
|
5869
|
+
this.updateOption(result.id, result);
|
|
5870
|
+
});
|
|
5871
|
+
}
|
|
5872
|
+
// Update any pre-selected no-lang option with the synthetic data
|
|
5873
|
+
if (noLang && ids.includes(noLang.id)) {
|
|
5874
|
+
this.updateOption(noLang.id, noLang);
|
|
5875
|
+
}
|
|
5848
5876
|
},
|
|
5849
5877
|
onItemSelect: function (item) {
|
|
5850
5878
|
// Tom-select prevents clicking on link in an item to work as normal, so force it here
|
|
@@ -5874,6 +5902,56 @@ class LucosSearchComponent extends HTMLSpanElement {
|
|
|
5874
5902
|
shadow.append(selector.nextElementSibling);
|
|
5875
5903
|
}
|
|
5876
5904
|
}
|
|
5905
|
+
get noLangOption() {
|
|
5906
|
+
const label = this.getAttribute("data-no-lang");
|
|
5907
|
+
if (!label) return null;
|
|
5908
|
+
return {
|
|
5909
|
+
id: 'https://eolas.l42.eu/metadata/language/zxx/',
|
|
5910
|
+
pref_label: label,
|
|
5911
|
+
type: 'Language',
|
|
5912
|
+
category: 'Anthropological',
|
|
5913
|
+
labels: [],
|
|
5914
|
+
highlight: {},
|
|
5915
|
+
};
|
|
5916
|
+
}
|
|
5917
|
+
get isLanguageMode() {
|
|
5918
|
+
const types = this.getAttribute("data-types");
|
|
5919
|
+
if (!types) return false;
|
|
5920
|
+
return types.split(",").map(t => t.trim()).includes("Language");
|
|
5921
|
+
}
|
|
5922
|
+
async getLanguageFamilies() {
|
|
5923
|
+
if (this._langFamilies) return this._langFamilies;
|
|
5924
|
+
const key = this.getAttribute("data-api-key");
|
|
5925
|
+
if (!key) { this._langFamilies = []; return []; }
|
|
5926
|
+
const searchParams = new URLSearchParams({
|
|
5927
|
+
q: '*',
|
|
5928
|
+
filter_by: 'type:=Language Family',
|
|
5929
|
+
query_by: 'pref_label',
|
|
5930
|
+
include_fields: 'id,pref_label',
|
|
5931
|
+
sort_by: 'pref_label:asc',
|
|
5932
|
+
enable_highlight_v1: false,
|
|
5933
|
+
per_page: 250,
|
|
5934
|
+
});
|
|
5935
|
+
try {
|
|
5936
|
+
const response = await fetch("https://arachne.l42.eu/search?" + searchParams.toString(), {
|
|
5937
|
+
headers: { 'X-TYPESENSE-API-KEY': key },
|
|
5938
|
+
signal: AbortSignal.timeout(8000),
|
|
5939
|
+
});
|
|
5940
|
+
if (!response.ok) { this._langFamilies = []; return []; }
|
|
5941
|
+
const data = await response.json();
|
|
5942
|
+
this._langFamilies = data.hits.map(hit => ({
|
|
5943
|
+
// The lang_family field on Language documents in Typesense stores the last
|
|
5944
|
+
// path segment of the Language Family URI (e.g. "gmw" for West Germanic at
|
|
5945
|
+
// http://id.loc.gov/vocabulary/iso639-5/gmw). filter(Boolean) strips any
|
|
5946
|
+
// trailing slash. This is consistent with lucos-lang's family code extraction.
|
|
5947
|
+
code: hit.document.id.split("/").filter(Boolean).pop(),
|
|
5948
|
+
label: hit.document.pref_label,
|
|
5949
|
+
}));
|
|
5950
|
+
} catch (_) {
|
|
5951
|
+
this._langFamilies = [];
|
|
5952
|
+
}
|
|
5953
|
+
return this._langFamilies;
|
|
5954
|
+
}
|
|
5877
5955
|
async searchRequest(searchParams) {
|
|
5878
5956
|
const key = this.getAttribute("data-api-key");
|
|
5879
5957
|
if (!key) throw new Error("No `data-api-key` attribute set on `lucos-search` component");
|
|
@@ -5881,7 +5959,7 @@ class LucosSearchComponent extends HTMLSpanElement {
|
|
|
5881
5959
|
searchParams.set('query_by_weights', "10,8,3,1");
|
|
5882
5960
|
searchParams.set('sort_by', "_text_match:desc,pref_label:asc");
|
|
5883
5961
|
searchParams.set('prioritize_num_matching_fields', false);
|
|
5884
|
-
searchParams.set('include_fields', "id,pref_label,type,category,labels");
|
|
5962
|
+
searchParams.set('include_fields', "id,pref_label,type,category,labels,lang_family");
|
|
5885
5963
|
searchParams.set('enable_highlight_v1', false);
|
|
5886
5964
|
searchParams.set('highlight_start_tag', '<span class="highlight">');
|
|
5887
5965
|
searchParams.set('highlight_end_tag', '</span>');
|
package/example/index.html
CHANGED
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
<option selected>https://media-metadata.l42.eu/tracks/13713</option>
|
|
14
14
|
<option selected>https://eolas.l42.eu/metadata/language/ain/</option>
|
|
15
15
|
</select></span>
|
|
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="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>
|
|
18
|
+
<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>
|
|
16
19
|
<label for="search5">More than 10:</label>
|
|
17
20
|
<span is="lucos-search" data-api-key="${KEY_LUCOS_ARACHNE}" data-exclude_types="Track"><select id="search5" multiple>
|
|
18
21
|
<option selected>https://eolas.l42.eu/metadata/place/125/</option>
|
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'];
|
|
6
|
+
return ['data-api-key','data-types','data-exclude-types','data-no-lang'];
|
|
7
7
|
}
|
|
8
8
|
constructor() {
|
|
9
9
|
super();
|
|
@@ -151,6 +151,7 @@ class LucosSearchComponent extends HTMLSpanElement {
|
|
|
151
151
|
if (!selector) throw new Error("Can't find select element in lucos-search");
|
|
152
152
|
selector.setAttribute("multiple", "multiple");
|
|
153
153
|
new TomSelect(selector, {
|
|
154
|
+
...(component.isLanguageMode ? { optgroupField: 'lang_family', lockOptgroupOrder: true } : {}),
|
|
154
155
|
valueField: 'id',
|
|
155
156
|
labelField: 'pref_label',
|
|
156
157
|
searchField: [],
|
|
@@ -169,6 +170,11 @@ class LucosSearchComponent extends HTMLSpanElement {
|
|
|
169
170
|
try {
|
|
170
171
|
const results = await component.searchRequest(queryParams);
|
|
171
172
|
this.clearOptions();
|
|
173
|
+
if (component.isLanguageMode) {
|
|
174
|
+
results.forEach(r => { if (!r.lang_family) r.lang_family = 'qli'; });
|
|
175
|
+
}
|
|
176
|
+
const noLang = component.noLangOption;
|
|
177
|
+
if (noLang) results.unshift(noLang);
|
|
172
178
|
callback(results);
|
|
173
179
|
} catch(err) {
|
|
174
180
|
callback([]);
|
|
@@ -188,20 +194,42 @@ class LucosSearchComponent extends HTMLSpanElement {
|
|
|
188
194
|
},
|
|
189
195
|
onFocus: function() {
|
|
190
196
|
this.clearOptions();
|
|
197
|
+
const noLang = component.noLangOption;
|
|
198
|
+
if (noLang) this.addOption(noLang);
|
|
191
199
|
},
|
|
192
200
|
// On startup, update any existing options with latest data from search
|
|
193
201
|
onInitialize: async function() {
|
|
194
202
|
const ids = Object.keys(this.options);
|
|
203
|
+
const noLang = component.noLangOption;
|
|
204
|
+
// Always make the no-lang option available for new selections
|
|
205
|
+
if (noLang) this.addOption(noLang);
|
|
206
|
+
// In language mode, fetch families and register option groups
|
|
207
|
+
if (component.isLanguageMode) {
|
|
208
|
+
const families = await component.getLanguageFamilies();
|
|
209
|
+
this.addOptionGroup('qli', { label: 'Language Isolate' });
|
|
210
|
+
families.forEach(family => {
|
|
211
|
+
this.addOptionGroup(family.code, { label: family.label });
|
|
212
|
+
});
|
|
213
|
+
}
|
|
195
214
|
if (ids.length < 1) return;
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
215
|
+
// Fetch real options from Typesense, excluding the synthetic no-lang option
|
|
216
|
+
const idsToFetch = noLang ? ids.filter(id => id !== noLang.id) : ids;
|
|
217
|
+
if (idsToFetch.length > 0) {
|
|
218
|
+
const searchParams = new URLSearchParams({
|
|
219
|
+
q: '*',
|
|
220
|
+
filter_by: `id:[${idsToFetch.join(",")}]`,
|
|
221
|
+
per_page: idsToFetch.length,
|
|
222
|
+
});
|
|
223
|
+
const results = await component.searchRequest(searchParams);
|
|
224
|
+
results.forEach(result => {
|
|
225
|
+
if (component.isLanguageMode && !result.lang_family) result.lang_family = 'qli';
|
|
226
|
+
this.updateOption(result.id, result);
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
// Update any pre-selected no-lang option with the synthetic data
|
|
230
|
+
if (noLang && ids.includes(noLang.id)) {
|
|
231
|
+
this.updateOption(noLang.id, noLang);
|
|
232
|
+
}
|
|
205
233
|
},
|
|
206
234
|
onItemSelect: function (item) {
|
|
207
235
|
// Tom-select prevents clicking on link in an item to work as normal, so force it here
|
|
@@ -231,6 +259,56 @@ class LucosSearchComponent extends HTMLSpanElement {
|
|
|
231
259
|
shadow.append(selector.nextElementSibling);
|
|
232
260
|
}
|
|
233
261
|
}
|
|
262
|
+
get noLangOption() {
|
|
263
|
+
const label = this.getAttribute("data-no-lang");
|
|
264
|
+
if (!label) return null;
|
|
265
|
+
return {
|
|
266
|
+
id: 'https://eolas.l42.eu/metadata/language/zxx/',
|
|
267
|
+
pref_label: label,
|
|
268
|
+
type: 'Language',
|
|
269
|
+
category: 'Anthropological',
|
|
270
|
+
labels: [],
|
|
271
|
+
highlight: {},
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
get isLanguageMode() {
|
|
275
|
+
const types = this.getAttribute("data-types");
|
|
276
|
+
if (!types) return false;
|
|
277
|
+
return types.split(",").map(t => t.trim()).includes("Language");
|
|
278
|
+
}
|
|
279
|
+
async getLanguageFamilies() {
|
|
280
|
+
if (this._langFamilies) return this._langFamilies;
|
|
281
|
+
const key = this.getAttribute("data-api-key");
|
|
282
|
+
if (!key) { this._langFamilies = []; return []; }
|
|
283
|
+
const searchParams = new URLSearchParams({
|
|
284
|
+
q: '*',
|
|
285
|
+
filter_by: 'type:=Language Family',
|
|
286
|
+
query_by: 'pref_label',
|
|
287
|
+
include_fields: 'id,pref_label',
|
|
288
|
+
sort_by: 'pref_label:asc',
|
|
289
|
+
enable_highlight_v1: false,
|
|
290
|
+
per_page: 250,
|
|
291
|
+
});
|
|
292
|
+
try {
|
|
293
|
+
const response = await fetch("https://arachne.l42.eu/search?" + searchParams.toString(), {
|
|
294
|
+
headers: { 'X-TYPESENSE-API-KEY': key },
|
|
295
|
+
signal: AbortSignal.timeout(8000),
|
|
296
|
+
});
|
|
297
|
+
if (!response.ok) { this._langFamilies = []; return []; }
|
|
298
|
+
const data = await response.json();
|
|
299
|
+
this._langFamilies = data.hits.map(hit => ({
|
|
300
|
+
// The lang_family field on Language documents in Typesense stores the last
|
|
301
|
+
// path segment of the Language Family URI (e.g. "gmw" for West Germanic at
|
|
302
|
+
// http://id.loc.gov/vocabulary/iso639-5/gmw). filter(Boolean) strips any
|
|
303
|
+
// trailing slash. This is consistent with lucos-lang's family code extraction.
|
|
304
|
+
code: hit.document.id.split("/").filter(Boolean).pop(),
|
|
305
|
+
label: hit.document.pref_label,
|
|
306
|
+
}));
|
|
307
|
+
} catch (_) {
|
|
308
|
+
this._langFamilies = [];
|
|
309
|
+
}
|
|
310
|
+
return this._langFamilies;
|
|
311
|
+
}
|
|
234
312
|
async searchRequest(searchParams) {
|
|
235
313
|
const key = this.getAttribute("data-api-key");
|
|
236
314
|
if (!key) throw new Error("No `data-api-key` attribute set on `lucos-search` component");
|
|
@@ -238,7 +316,7 @@ class LucosSearchComponent extends HTMLSpanElement {
|
|
|
238
316
|
searchParams.set('query_by_weights', "10,8,3,1");
|
|
239
317
|
searchParams.set('sort_by', "_text_match:desc,pref_label:asc");
|
|
240
318
|
searchParams.set('prioritize_num_matching_fields', false);
|
|
241
|
-
searchParams.set('include_fields', "id,pref_label,type,category,labels");
|
|
319
|
+
searchParams.set('include_fields', "id,pref_label,type,category,labels,lang_family");
|
|
242
320
|
searchParams.set('enable_highlight_v1', false);
|
|
243
321
|
searchParams.set('highlight_start_tag', '<span class="highlight">')
|
|
244
322
|
searchParams.set('highlight_end_tag', '</span>');
|