lucos_search_component 3.0.3 → 3.0.5
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 +1 -1
- package/dist/index.js +82 -2
- package/example/index.html +20 -0
- package/package.json +1 -1
- package/web-components/lucos-search.js +82 -2
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ Selected options use the item's URI as their value.
|
|
|
31
31
|
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
|
-
* **data-
|
|
34
|
+
* **data-exclude_types** — A comma separated list of item types to exclude from the search (ignored if `data-types` is set).
|
|
35
35
|
* **data-is-contact** — Filter results to contacts only (`"true"`) or non-contacts only (`"false"`). Omitting the attribute applies no filter. Composes with `data-types` when both are set.
|
|
36
36
|
* **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"`.
|
|
37
37
|
* **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"`.
|
package/dist/index.js
CHANGED
|
@@ -5718,7 +5718,7 @@ function buildFilterBy(types, excludeTypes, isContact) {
|
|
|
5718
5718
|
|
|
5719
5719
|
class LucosSearchComponent extends HTMLSpanElement {
|
|
5720
5720
|
static get observedAttributes() {
|
|
5721
|
-
return ['data-api-key','data-types','data-
|
|
5721
|
+
return ['data-api-key','data-types','data-exclude_types','data-is-contact','data-label-override-zxx','data-common','data-preload'];
|
|
5722
5722
|
}
|
|
5723
5723
|
constructor() {
|
|
5724
5724
|
super();
|
|
@@ -5781,8 +5781,30 @@ class LucosSearchComponent extends HTMLSpanElement {
|
|
|
5781
5781
|
margin: 0 3px;
|
|
5782
5782
|
padding: 2px 6px;
|
|
5783
5783
|
}
|
|
5784
|
+
/* Prevent dropdown overflowing into an adjacent column in multi-column layouts.
|
|
5785
|
+
* Consumers may embed lucos-search in a CSS column-count context (e.g. a 2-column
|
|
5786
|
+
* form). Because TomSelect's DOM lives inside this shadow root, page-level CSS
|
|
5787
|
+
* cannot fix column overflow from outside.
|
|
5788
|
+
*
|
|
5789
|
+
* The actual fix is applied in JavaScript (see fixDropdownContentPosition below):
|
|
5790
|
+
* Chromium has a bug where position:absolute on a shadow-DOM descendant inside a
|
|
5791
|
+
* multi-column container resolves the containing block to the column-box boundary
|
|
5792
|
+
* rather than the nearest positioned ancestor (.ts-dropdown). CSS-only approaches
|
|
5793
|
+
* (position:absolute with explicit top/left, contain:layout) do not override this.
|
|
5794
|
+
* The JS fix switches .ts-dropdown-content to position:fixed when the dropdown opens
|
|
5795
|
+
* inside a multi-column layout, bypassing the containing-block resolution entirely. */
|
|
5784
5796
|
.ts-dropdown {
|
|
5785
|
-
|
|
5797
|
+
border: none;
|
|
5798
|
+
margin: -3px 0 0 0;
|
|
5799
|
+
}
|
|
5800
|
+
.ts-dropdown-content {
|
|
5801
|
+
background: #fff;
|
|
5802
|
+
border: 1px solid #d0d0d0;
|
|
5803
|
+
margin: 0.25rem 0 0;
|
|
5804
|
+
border-top: 0 none;
|
|
5805
|
+
box-sizing: border-box;
|
|
5806
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
5807
|
+
border-radius: 0 0 3px 3px;
|
|
5786
5808
|
}
|
|
5787
5809
|
.lozenge a {
|
|
5788
5810
|
color: inherit;
|
|
@@ -5998,6 +6020,64 @@ class LucosSearchComponent extends HTMLSpanElement {
|
|
|
5998
6020
|
},
|
|
5999
6021
|
},
|
|
6000
6022
|
});
|
|
6023
|
+
|
|
6024
|
+
// fixDropdownContentPosition: work around a Chromium bug where, inside a shadow
|
|
6025
|
+
// DOM + CSS multi-column context, Chromium resolves position:absolute on
|
|
6026
|
+
// .ts-dropdown-content to the column-box boundary rather than the .ts-dropdown
|
|
6027
|
+
// containing block. We detect the multi-column context and switch to position:fixed
|
|
6028
|
+
// with explicit viewport coordinates, bypassing containing-block resolution entirely.
|
|
6029
|
+
const ts = selector.tomselect;
|
|
6030
|
+
let dropdownScrollHandler = null;
|
|
6031
|
+
|
|
6032
|
+
function isInMultiColumnLayout() {
|
|
6033
|
+
let el = component.parentElement;
|
|
6034
|
+
while (el && el !== document.body) {
|
|
6035
|
+
if (parseInt(window.getComputedStyle(el).columnCount) > 1) return true;
|
|
6036
|
+
el = el.parentElement;
|
|
6037
|
+
}
|
|
6038
|
+
return false;
|
|
6039
|
+
}
|
|
6040
|
+
|
|
6041
|
+
ts.on('dropdown_open', function (dropdown) {
|
|
6042
|
+
if (!isInMultiColumnLayout()) return;
|
|
6043
|
+
const content = ts.dropdown_content;
|
|
6044
|
+
// Set position:fixed BEFORE reading the bounding rect. Chromium has a bug
|
|
6045
|
+
// where, in shadow DOM + multi-column layouts, getBoundingClientRect() on
|
|
6046
|
+
// .ts-dropdown returns the column-box dimensions rather than the true visual
|
|
6047
|
+
// position while the content is in normal flow. Applying position:fixed first
|
|
6048
|
+
// takes the content out of flow, which forces Chromium to recompute .ts-dropdown's
|
|
6049
|
+
// layout correctly. Subsequent getBoundingClientRect() then returns accurate values.
|
|
6050
|
+
content.style.position = 'fixed';
|
|
6051
|
+
const rect = dropdown.getBoundingClientRect();
|
|
6052
|
+
if (!rect.width) {
|
|
6053
|
+
content.style.position = '';
|
|
6054
|
+
return;
|
|
6055
|
+
}
|
|
6056
|
+
content.style.top = rect.top + 'px';
|
|
6057
|
+
content.style.left = rect.left + 'px';
|
|
6058
|
+
content.style.width = rect.width + 'px';
|
|
6059
|
+
dropdownScrollHandler = function () {
|
|
6060
|
+
const r = dropdown.getBoundingClientRect();
|
|
6061
|
+
content.style.top = r.top + 'px';
|
|
6062
|
+
content.style.left = r.left + 'px';
|
|
6063
|
+
content.style.width = r.width + 'px';
|
|
6064
|
+
};
|
|
6065
|
+
window.addEventListener('scroll', dropdownScrollHandler, { passive: true, capture: true });
|
|
6066
|
+
window.addEventListener('resize', dropdownScrollHandler, { passive: true });
|
|
6067
|
+
});
|
|
6068
|
+
|
|
6069
|
+
ts.on('dropdown_close', function () {
|
|
6070
|
+
if (!dropdownScrollHandler) return;
|
|
6071
|
+
const content = ts.dropdown_content;
|
|
6072
|
+
content.style.position = '';
|
|
6073
|
+
content.style.top = '';
|
|
6074
|
+
content.style.left = '';
|
|
6075
|
+
content.style.width = '';
|
|
6076
|
+
window.removeEventListener('scroll', dropdownScrollHandler, { capture: true });
|
|
6077
|
+
window.removeEventListener('resize', dropdownScrollHandler);
|
|
6078
|
+
dropdownScrollHandler = null;
|
|
6079
|
+
});
|
|
6080
|
+
|
|
6001
6081
|
if (selector.nextElementSibling) {
|
|
6002
6082
|
shadow.append(selector.nextElementSibling);
|
|
6003
6083
|
}
|
package/example/index.html
CHANGED
|
@@ -35,6 +35,26 @@
|
|
|
35
35
|
<option selected>https://eolas.l42.eu/metadata/place/316/</option>
|
|
36
36
|
<option selected>https://eolas.l42.eu/metadata/place/317/</option>
|
|
37
37
|
</select></span>
|
|
38
|
+
<h1>Multi-column layout</h1>
|
|
39
|
+
<p>Dropdowns should open within the same column as their field, not overflow into the adjacent column.</p>
|
|
40
|
+
<div style="column-count: 2; column-gap: 2em; border: 1px solid #ccc; padding: 1em;">
|
|
41
|
+
<div style="break-inside: avoid;">
|
|
42
|
+
<label for="col-search1">Theme tune:</label>
|
|
43
|
+
<span is="lucos-search" data-api-key="${KEY_LUCOS_ARACHNE}" data-types="CreativeWork"><select id="col-search1"></select></span>
|
|
44
|
+
</div>
|
|
45
|
+
<div style="break-inside: avoid;">
|
|
46
|
+
<label for="col-search2">Soundtrack:</label>
|
|
47
|
+
<span is="lucos-search" data-api-key="${KEY_LUCOS_ARACHNE}" data-types="CreativeWork"><select id="col-search2"></select></span>
|
|
48
|
+
</div>
|
|
49
|
+
<div style="break-inside: avoid;">
|
|
50
|
+
<label for="col-search3">Language:</label>
|
|
51
|
+
<span is="lucos-search" data-api-key="${KEY_LUCOS_ARACHNE}" data-types="Language"><select id="col-search3"></select></span>
|
|
52
|
+
</div>
|
|
53
|
+
<div style="break-inside: avoid;">
|
|
54
|
+
<label for="col-search4">About:</label>
|
|
55
|
+
<span is="lucos-search" data-api-key="${KEY_LUCOS_ARACHNE}"><select id="col-search4"></select></span>
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
38
58
|
<h1>Form submission</h1>
|
|
39
59
|
<form id="test-form" onsubmit="handleSubmit(event)">
|
|
40
60
|
<label for="tags">Tags:</label><span is="lucos-search" data-api-key="${KEY_LUCOS_ARACHNE}"><select id="tags" name="tags" multiple></select></span>
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@ import { buildFilterBy } from './filter.js';
|
|
|
5
5
|
|
|
6
6
|
class LucosSearchComponent extends HTMLSpanElement {
|
|
7
7
|
static get observedAttributes() {
|
|
8
|
-
return ['data-api-key','data-types','data-
|
|
8
|
+
return ['data-api-key','data-types','data-exclude_types','data-is-contact','data-label-override-zxx','data-common','data-preload'];
|
|
9
9
|
}
|
|
10
10
|
constructor() {
|
|
11
11
|
super();
|
|
@@ -68,8 +68,30 @@ class LucosSearchComponent extends HTMLSpanElement {
|
|
|
68
68
|
margin: 0 3px;
|
|
69
69
|
padding: 2px 6px;
|
|
70
70
|
}
|
|
71
|
+
/* Prevent dropdown overflowing into an adjacent column in multi-column layouts.
|
|
72
|
+
* Consumers may embed lucos-search in a CSS column-count context (e.g. a 2-column
|
|
73
|
+
* form). Because TomSelect's DOM lives inside this shadow root, page-level CSS
|
|
74
|
+
* cannot fix column overflow from outside.
|
|
75
|
+
*
|
|
76
|
+
* The actual fix is applied in JavaScript (see fixDropdownContentPosition below):
|
|
77
|
+
* Chromium has a bug where position:absolute on a shadow-DOM descendant inside a
|
|
78
|
+
* multi-column container resolves the containing block to the column-box boundary
|
|
79
|
+
* rather than the nearest positioned ancestor (.ts-dropdown). CSS-only approaches
|
|
80
|
+
* (position:absolute with explicit top/left, contain:layout) do not override this.
|
|
81
|
+
* The JS fix switches .ts-dropdown-content to position:fixed when the dropdown opens
|
|
82
|
+
* inside a multi-column layout, bypassing the containing-block resolution entirely. */
|
|
71
83
|
.ts-dropdown {
|
|
72
|
-
|
|
84
|
+
border: none;
|
|
85
|
+
margin: -3px 0 0 0;
|
|
86
|
+
}
|
|
87
|
+
.ts-dropdown-content {
|
|
88
|
+
background: #fff;
|
|
89
|
+
border: 1px solid #d0d0d0;
|
|
90
|
+
margin: 0.25rem 0 0;
|
|
91
|
+
border-top: 0 none;
|
|
92
|
+
box-sizing: border-box;
|
|
93
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
94
|
+
border-radius: 0 0 3px 3px;
|
|
73
95
|
}
|
|
74
96
|
.lozenge a {
|
|
75
97
|
color: inherit;
|
|
@@ -285,6 +307,64 @@ class LucosSearchComponent extends HTMLSpanElement {
|
|
|
285
307
|
},
|
|
286
308
|
},
|
|
287
309
|
});
|
|
310
|
+
|
|
311
|
+
// fixDropdownContentPosition: work around a Chromium bug where, inside a shadow
|
|
312
|
+
// DOM + CSS multi-column context, Chromium resolves position:absolute on
|
|
313
|
+
// .ts-dropdown-content to the column-box boundary rather than the .ts-dropdown
|
|
314
|
+
// containing block. We detect the multi-column context and switch to position:fixed
|
|
315
|
+
// with explicit viewport coordinates, bypassing containing-block resolution entirely.
|
|
316
|
+
const ts = selector.tomselect;
|
|
317
|
+
let dropdownScrollHandler = null;
|
|
318
|
+
|
|
319
|
+
function isInMultiColumnLayout() {
|
|
320
|
+
let el = component.parentElement;
|
|
321
|
+
while (el && el !== document.body) {
|
|
322
|
+
if (parseInt(window.getComputedStyle(el).columnCount) > 1) return true;
|
|
323
|
+
el = el.parentElement;
|
|
324
|
+
}
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
ts.on('dropdown_open', function (dropdown) {
|
|
329
|
+
if (!isInMultiColumnLayout()) return;
|
|
330
|
+
const content = ts.dropdown_content;
|
|
331
|
+
// Set position:fixed BEFORE reading the bounding rect. Chromium has a bug
|
|
332
|
+
// where, in shadow DOM + multi-column layouts, getBoundingClientRect() on
|
|
333
|
+
// .ts-dropdown returns the column-box dimensions rather than the true visual
|
|
334
|
+
// position while the content is in normal flow. Applying position:fixed first
|
|
335
|
+
// takes the content out of flow, which forces Chromium to recompute .ts-dropdown's
|
|
336
|
+
// layout correctly. Subsequent getBoundingClientRect() then returns accurate values.
|
|
337
|
+
content.style.position = 'fixed';
|
|
338
|
+
const rect = dropdown.getBoundingClientRect();
|
|
339
|
+
if (!rect.width) {
|
|
340
|
+
content.style.position = '';
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
content.style.top = rect.top + 'px';
|
|
344
|
+
content.style.left = rect.left + 'px';
|
|
345
|
+
content.style.width = rect.width + 'px';
|
|
346
|
+
dropdownScrollHandler = function () {
|
|
347
|
+
const r = dropdown.getBoundingClientRect();
|
|
348
|
+
content.style.top = r.top + 'px';
|
|
349
|
+
content.style.left = r.left + 'px';
|
|
350
|
+
content.style.width = r.width + 'px';
|
|
351
|
+
};
|
|
352
|
+
window.addEventListener('scroll', dropdownScrollHandler, { passive: true, capture: true });
|
|
353
|
+
window.addEventListener('resize', dropdownScrollHandler, { passive: true });
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
ts.on('dropdown_close', function () {
|
|
357
|
+
if (!dropdownScrollHandler) return;
|
|
358
|
+
const content = ts.dropdown_content;
|
|
359
|
+
content.style.position = '';
|
|
360
|
+
content.style.top = '';
|
|
361
|
+
content.style.left = '';
|
|
362
|
+
content.style.width = '';
|
|
363
|
+
window.removeEventListener('scroll', dropdownScrollHandler, { capture: true });
|
|
364
|
+
window.removeEventListener('resize', dropdownScrollHandler);
|
|
365
|
+
dropdownScrollHandler = null;
|
|
366
|
+
});
|
|
367
|
+
|
|
288
368
|
if (selector.nextElementSibling) {
|
|
289
369
|
shadow.append(selector.nextElementSibling);
|
|
290
370
|
}
|