lucos_search_component 3.0.4 → 3.0.6
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 +81 -1
- package/example/index.html +21 -1
- package/package.json +1 -1
- package/web-components/lucos-search.js +81 -1
package/dist/index.js
CHANGED
|
@@ -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
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<label for="search2">No Tracks:</label><span is="lucos-search" data-api-key="${KEY_LUCOS_ARACHNE}" data-exclude_types="Track"><select id="search2"></select></span>
|
|
9
9
|
<label for="search3">Only Cites and Rivers:</label><span is="lucos-search" data-api-key="${KEY_LUCOS_ARACHNE}" data-types="City,River"><select id="search3"></select></span>
|
|
10
10
|
<label for="search4">Load with existing items:</label><span is="lucos-search" data-api-key="${KEY_LUCOS_ARACHNE}"><select id="search4" multiple>
|
|
11
|
-
<option selected>https://
|
|
11
|
+
<option selected>https://eolas.l42.eu/metadata/person/2/</option>
|
|
12
12
|
<option selected>https://eolas.l42.eu/metadata/place/2/</option>
|
|
13
13
|
<option selected>https://media-metadata.l42.eu/tracks/13713</option>
|
|
14
14
|
<option selected>https://eolas.l42.eu/metadata/language/ain/</option>
|
|
@@ -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
|
@@ -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
|
}
|