Skip to content

Commit 18344f0

Browse files
committed
Fix text selection in Search inside panel results
1 parent f910ef7 commit 18344f0

File tree

1 file changed

+120
-73
lines changed

1 file changed

+120
-73
lines changed

src/plugins/search/search-results.js

Lines changed: 120 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
/* eslint-disable class-methods-use-this */
2-
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
3-
import { css, html, LitElement, nothing } from 'lit';
4-
import '@internetarchive/ia-activity-indicator/ia-activity-indicator.js';
5-
import checkmarkIconTemplate from '../../css/icon_checkmark.js';
6-
import closeIconTemplate from '@internetarchive/icon-close/index.js';
7-
import buttonCSS from '../../css/button-base.js';
8-
import { ifDefined } from 'lit/directives/if-defined.js';
9-
import { sharedStyles } from '../../css/sharedStyles.js';
10-
import { svgToDataUrl } from '../../util/lit.js';
2+
import { unsafeHTML } from "lit/directives/unsafe-html.js";
3+
import { css, html, LitElement, nothing } from "lit";
4+
import "@internetarchive/ia-activity-indicator/ia-activity-indicator.js";
5+
import checkmarkIconTemplate from "../../css/icon_checkmark.js";
6+
import closeIconTemplate from "@internetarchive/icon-close/index.js";
7+
import buttonCSS from "../../css/button-base.js";
8+
import { ifDefined } from "lit/directives/if-defined.js";
9+
import { sharedStyles } from "../../css/sharedStyles.js";
10+
import { svgToDataUrl } from "../../util/lit.js";
1111
/** @typedef {import('@/src/plugins/search/plugin.search.js').SearchInsideMatch} SearchInsideMatch */
1212

1313
const checkmarkIconData = svgToDataUrl(checkmarkIconTemplate.strings[0]);
@@ -30,11 +30,11 @@ export class IABookSearchResults extends LitElement {
3030

3131
/** @type {SearchInsideMatch[]} */
3232
this.results = [];
33-
this.query = '';
33+
this.query = "";
3434
this.queryInProgress = false;
3535
this.renderHeader = false;
3636
this.renderSearchAllFiles = false;
37-
this.errorMessage = '';
37+
this.errorMessage = "";
3838

3939
this.bindBookReaderListeners();
4040
}
@@ -45,7 +45,10 @@ export class IABookSearchResults extends LitElement {
4545
}
4646

4747
bindBookReaderListeners() {
48-
document.addEventListener('BookReader:SearchCallback', this.setResults.bind(this));
48+
document.addEventListener(
49+
"BookReader:SearchCallback",
50+
this.setResults.bind(this),
51+
);
4952
}
5053

5154
/**
@@ -55,7 +58,7 @@ export class IABookSearchResults extends LitElement {
5558
if (this.results.length) {
5659
return;
5760
}
58-
const searchInput = this.shadowRoot.querySelector('input[type=\'search\']');
61+
const searchInput = this.shadowRoot.querySelector("input[type='search']");
5962
searchInput.focus();
6063
}
6164

@@ -76,28 +79,34 @@ export class IABookSearchResults extends LitElement {
7679
if (!input || !input.value) {
7780
return;
7881
}
79-
this.dispatchEvent(new CustomEvent('bookSearchInitiated', {
80-
bubbles: true,
81-
composed: true,
82-
detail: {
83-
query: this.query,
84-
},
85-
}));
82+
this.dispatchEvent(
83+
new CustomEvent("bookSearchInitiated", {
84+
bubbles: true,
85+
composed: true,
86+
detail: {
87+
query: this.query,
88+
},
89+
}),
90+
);
8691
}
8792

8893
/**
8994
* @param {SearchInsideMatch} match
9095
*/
9196
selectResult(match) {
92-
this.dispatchEvent(new CustomEvent('resultSelected', {
93-
bubbles: true,
94-
composed: true,
95-
detail: { match },
96-
}));
97-
this.dispatchEvent(new CustomEvent('closeMenu', {
98-
bubbles: true,
99-
composed: true,
100-
}));
97+
this.dispatchEvent(
98+
new CustomEvent("resultSelected", {
99+
bubbles: true,
100+
composed: true,
101+
detail: { match },
102+
}),
103+
);
104+
this.dispatchEvent(
105+
new CustomEvent("closeMenu", {
106+
bubbles: true,
107+
composed: true,
108+
}),
109+
);
101110
}
102111

103112
cancelSearch() {
@@ -106,12 +115,12 @@ export class IABookSearchResults extends LitElement {
106115
}
107116

108117
dispatchSearchCanceled() {
109-
this.dispatchEvent(new Event('bookSearchCanceled'));
118+
this.dispatchEvent(new Event("bookSearchCanceled"));
110119
}
111120

112121
get resultsCount() {
113122
const count = this.results.length;
114-
return count ? `${count} result${count > 1 ? 's' : ''}` : nothing;
123+
return count ? `${count} result${count > 1 ? "s" : ""}` : nothing;
115124
}
116125

117126
get headerSection() {
@@ -133,57 +142,82 @@ export class IABookSearchResults extends LitElement {
133142
get loadingIndicator() {
134143
return html`
135144
<div class="loading">
136-
<ia-activity-indicator mode="processing" aria-hidden="true" role="presentation"></ia-activity-indicator>
145+
<ia-activity-indicator
146+
mode="processing"
147+
aria-hidden="true"
148+
role="presentation"
149+
></ia-activity-indicator>
137150
<p>Searching</p>
138-
<button class="ia-button external cancel-search" @click=${this.cancelSearch}>Cancel</button>
151+
<button
152+
class="ia-button external cancel-search"
153+
@click=${this.cancelSearch}
154+
>
155+
Cancel
156+
</button>
139157
</div>
140158
`;
141159
}
142160

143161
get resultsSet() {
144162
return html`
145-
<nav aria-label="Search results">
146-
<div>
147-
${this.resultsCount}
163+
<nav aria-label="Search results">
164+
<div>
165+
${this.resultsCount}
166+
<a
167+
href="#"
168+
class="skip-link"
169+
@click=${(e) => {
170+
e.preventDefault();
171+
this.shadowRoot
172+
.querySelector(".results li:last-child .result-item")
173+
.focus();
174+
}}
175+
>Skip to last result</a
176+
>
177+
</div>
178+
<ul class="results">
179+
${this.results.map(
180+
(match) => html`
181+
<li>
182+
<button
183+
class="result-item"
184+
@click=${this.selectResult.bind(this, match)}
185+
>
186+
<span class="page-num">Page ${match.displayPageNumber}</span>
187+
188+
<span lang=${ifDefined(match.lang)}
189+
>${unsafeHTML(match.html)}</span
190+
>
191+
</button>
192+
</li>
193+
`,
194+
)}
195+
</ul>
148196
<a
149197
href="#"
150198
class="skip-link"
151199
@click=${(e) => {
152-
e.preventDefault();
153-
this.shadowRoot.querySelector('.results li:last-child .result-item').focus();
154-
}}
155-
>Skip to last result</a>
156-
</div>
157-
<ul class="results">
158-
${this.results.map(match => html`
159-
<li>
160-
<button class="result-item" @click=${this.selectResult.bind(this, match)}>
161-
<span class="page-num">Page ${match.displayPageNumber}</span> — <span lang=${ifDefined(match.lang)}>${unsafeHTML(match.html)}</span>
162-
</button>
163-
</li>
164-
`)}
165-
</ul>
166-
<a
167-
href="#"
168-
class="skip-link"
169-
@click=${(e) => {
170-
e.preventDefault();
171-
this.shadowRoot.querySelector('.results li:first-child .result-item').focus();
172-
}}
173-
>Skip to first result</a>
174-
</nav>
200+
e.preventDefault();
201+
this.shadowRoot
202+
.querySelector(".results li:first-child .result-item")
203+
.focus();
204+
}}
205+
>Skip to first result</a
206+
>
207+
</nav>
175208
`;
176209
}
177210

178211
get setErrorMessage() {
179-
return html`
180-
<p class="error-message">${this.errorMessage}</p>
181-
`;
212+
return html` <p class="error-message">${this.errorMessage}</p> `;
182213
}
183214

184215
render() {
185-
const showSearchCTA = (!this.queryInProgress && !this.errorMessage)
186-
&& (!this.queryInProgress && !this.results.length);
216+
const showSearchCTA =
217+
!this.queryInProgress &&
218+
!this.errorMessage &&
219+
!this.queryInProgress &&
220+
!this.results.length;
187221
return html`
188222
${this.headerSection}
189223
<form action="" method="get" @submit=${this.performSearch}>
@@ -196,7 +230,10 @@ export class IABookSearchResults extends LitElement {
196230
@search=${this.setQuery}
197231
.value=${this.query}
198232
/>
199-
<label class="search-cta ${showSearchCTA ? '' : 'sr-only'}" for="br-search-input">
233+
<label
234+
class="search-cta ${showSearchCTA ? "" : "sr-only"}"
235+
for="br-search-input"
236+
>
200237
Please enter text to search for
201238
</label>
202239
</form>
@@ -234,7 +271,7 @@ export class IABookSearchResults extends LitElement {
234271
}
235272
236273
mark {
237-
padding: 0 .2rem;
274+
padding: 0 0.2rem;
238275
color: ${searchResultText};
239276
background: ${searchResultBg};
240277
border: 1px solid ${searchResultBorder};
@@ -274,7 +311,7 @@ export class IABookSearchResults extends LitElement {
274311
}
275312
276313
label.checkbox {
277-
padding-bottom: .5rem;
314+
padding-bottom: 0.5rem;
278315
font-size: 1.6rem;
279316
line-height: 150%;
280317
vertical-align: middle;
@@ -284,12 +321,12 @@ export class IABookSearchResults extends LitElement {
284321
display: inline-block;
285322
width: 14px;
286323
height: 14px;
287-
margin-left: .7rem;
324+
margin-left: 0.7rem;
288325
content: "";
289326
border-radius: 2px;
290327
}
291328
:checked + label.checkbox:after {
292-
background-image: url('${checkmarkIconData}');
329+
background-image: url("${checkmarkIconData}");
293330
}
294331
295332
label.checkbox[for="all_files"]:after {
@@ -305,7 +342,11 @@ export class IABookSearchResults extends LitElement {
305342
height: 3rem;
306343
padding: 0 10px;
307344
box-sizing: border-box;
308-
font: normal 1.6rem "Helvetica qNeue", Helvetica, Arial, sans-serif;
345+
font:
346+
normal 1.6rem "Helvetica qNeue",
347+
Helvetica,
348+
Arial,
349+
sans-serif;
309350
border-radius: 1.5rem;
310351
background: transparent;
311352
}
@@ -318,8 +359,8 @@ export class IABookSearchResults extends LitElement {
318359
margin-right: -5px;
319360
-webkit-appearance: none;
320361
appearance: none;
321-
-webkit-mask: url('${closeIconData}') 0 0 no-repeat;
322-
mask: url('${closeIconData}') 0 0 no-repeat;
362+
-webkit-mask: url("${closeIconData}") 0 0 no-repeat;
363+
mask: url("${closeIconData}") 0 0 no-repeat;
323364
-webkit-mask-size: 100%;
324365
mask-size: 100%;
325366
background: #fff;
@@ -360,6 +401,12 @@ export class IABookSearchResults extends LitElement {
360401
text-align: left;
361402
font-family: inherit;
362403
transition: background-color 0.2s;
404+
/* Allow users to select and copy search snippet text inside result buttons. */
405+
-webkit-user-select: text;
406+
-moz-user-select: text;
407+
-ms-user-select: text;
408+
-o-user-select: text;
409+
user-select: text;
363410
}
364411
365412
.result-item:hover {
@@ -386,4 +433,4 @@ export class IABookSearchResults extends LitElement {
386433
return [sharedStyles, buttonCSS, mainCSS];
387434
}
388435
}
389-
customElements.define('ia-book-search-results', IABookSearchResults);
436+
customElements.define("ia-book-search-results", IABookSearchResults);

0 commit comments

Comments
 (0)