@royallondon/design-system-component-search-keyword 0.0.0-stage → 2.3.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/LICENSE.md +54 -0
- package/README.md +86 -2
- package/dist/search-keyword.css +1 -0
- package/dist/search-keyword.d.ts +33 -0
- package/dist/search-keyword.js +242 -0
- package/dist/utils.js +13 -0
- package/package.json +41 -3
package/LICENSE.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
Authorised Use Licence v1.0
|
|
2
|
+
|
|
3
|
+
Copyright © 2026 Royal London.
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
1. Grant of Licence
|
|
7
|
+
|
|
8
|
+
The Licensor grants a non-exclusive, non-transferable,
|
|
9
|
+
revocable, royalty-free licence to use the Software only where:
|
|
10
|
+
|
|
11
|
+
(a) the individual, organisation, or business has been
|
|
12
|
+
expressly authorised in writing by the Licensor; and
|
|
13
|
+
|
|
14
|
+
(b) such authorisation remains in force.
|
|
15
|
+
|
|
16
|
+
2. Restrictions
|
|
17
|
+
|
|
18
|
+
The Software may not be:
|
|
19
|
+
|
|
20
|
+
(a) used by any unauthorised person or organisation;
|
|
21
|
+
|
|
22
|
+
(b) redistributed, sublicensed, sold, rented or made
|
|
23
|
+
available to third parties;
|
|
24
|
+
|
|
25
|
+
(c) incorporated into another product or service without
|
|
26
|
+
written approval;
|
|
27
|
+
|
|
28
|
+
(d) reverse engineered except where prohibited by law.
|
|
29
|
+
|
|
30
|
+
3. Ownership
|
|
31
|
+
|
|
32
|
+
The Software remains the exclusive property of the Licensor.
|
|
33
|
+
No ownership rights are transferred under this Licence.
|
|
34
|
+
|
|
35
|
+
4. Termination
|
|
36
|
+
|
|
37
|
+
The Licensor may revoke authorisation at any time by
|
|
38
|
+
written notice.
|
|
39
|
+
|
|
40
|
+
Upon termination the Licensee must immediately cease use
|
|
41
|
+
of the Software and destroy all copies.
|
|
42
|
+
|
|
43
|
+
5. No Warranty
|
|
44
|
+
|
|
45
|
+
The Software is provided "as is" without warranty of any kind.
|
|
46
|
+
|
|
47
|
+
6. Limitation of Liability
|
|
48
|
+
|
|
49
|
+
The Licensor shall not be liable for any damages arising
|
|
50
|
+
from use of the Software.
|
|
51
|
+
|
|
52
|
+
7. Governing Law
|
|
53
|
+
|
|
54
|
+
This Licence shall be governed by the laws of England and Wales.
|
package/README.md
CHANGED
|
@@ -1,3 +1,87 @@
|
|
|
1
|
-
|
|
1
|
+
### Installation
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Install the Search keyword package from npm
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install @royallondon/design-system-component-search-keyword
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Import the required CSS file
|
|
10
|
+
|
|
11
|
+
```css
|
|
12
|
+
@import '@royallondon/design-system-component-search-keyword/dist/search-keyword';
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Add the html code for the search keyword component
|
|
16
|
+
|
|
17
|
+
```html
|
|
18
|
+
<form method="" class="rl-c-search--ice">
|
|
19
|
+
<label class="rl-u__visuallyhidden" for="search-input">Search</label>
|
|
20
|
+
<input
|
|
21
|
+
type="text"
|
|
22
|
+
class="rl-c-search__input rl--bg-color-white"
|
|
23
|
+
id="search-input"
|
|
24
|
+
aria-describedby="search-input"
|
|
25
|
+
placeholder="Search Royal London"
|
|
26
|
+
/>
|
|
27
|
+
<button type="submit" class="rl-c-search__submit">
|
|
28
|
+
<svg class="rl-f-icon rl-f-icon--md rl-f-icon--primary">
|
|
29
|
+
<title>Search</title>
|
|
30
|
+
<use href="/assets/icons.svg#search"></use>
|
|
31
|
+
</svg>
|
|
32
|
+
</button>
|
|
33
|
+
</form>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Import the js for the component, json data to be used for the search results and initialise the new searchBar class
|
|
37
|
+
|
|
38
|
+
```js
|
|
39
|
+
import SearchBar from '@royallondon/design-system-component-search-keyword/dist/search-keyword.js';
|
|
40
|
+
import data from 'PathToJsonFile.json';
|
|
41
|
+
|
|
42
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
43
|
+
new SearchBar(data);
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
1. You can pass in the following options to searchBar js class
|
|
48
|
+
|
|
49
|
+
```js
|
|
50
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
51
|
+
const searchBar = new SearchBar(searchData, {
|
|
52
|
+
formSelector: '#my-search-form',
|
|
53
|
+
inputSelector: '#my-search-input',
|
|
54
|
+
submitButtonSelector: '#my-search-submit',
|
|
55
|
+
minCharacters: 2, // Set to 2 if you want search to start after 2 characters are typed
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Support
|
|
61
|
+
|
|
62
|
+
Issues and questions are handled through the [Royal London Design System support page](https://design.royallondon.com/support/).
|
|
63
|
+
|
|
64
|
+
### Licence
|
|
65
|
+
|
|
66
|
+
#### @royallondon/design-system-component-search-keyword
|
|
67
|
+
|
|
68
|
+
This package is published primarily for use in Royal London products. It is made available publicly so that partners and suppliers building Royal London experiences can consume it directly.
|
|
69
|
+
|
|
70
|
+
Use of this package is subject to the Royal London Authorised Use Licence. See the `LICENSE.md` file included in this package.
|
|
71
|
+
|
|
72
|
+
The Search keyword component for the [Royal London Design System](https://design.royallondon.com). For design guidance, examples and accessibility notes, see the [Search keyword page](https://design.royallondon.com/components/search-keyword/).
|
|
73
|
+
|
|
74
|
+
### Release log
|
|
75
|
+
|
|
76
|
+
| Version | Release notes |
|
|
77
|
+
| ------- | --------------------------------------------------------------------------------------------------------------------------------- |
|
|
78
|
+
| 2.3.0 | Prepare for public npm release: add licence, publish `dist` only (`src` removed), remove source maps; add TypeScript declarations |
|
|
79
|
+
| 2.2.5 | Move to new feed |
|
|
80
|
+
| 2.2.4 | Update border for compatibility with Forms 2.x and 3.x |
|
|
81
|
+
| 2.2.0 | Apply new icons and latest tokens added to foundations |
|
|
82
|
+
| 2.1.1 | Add custom font weight to search result titles |
|
|
83
|
+
| 2.1.0 | Update search keyword to use type tokens |
|
|
84
|
+
| 2.0.1 | Fix for width not collapsing on small screens |
|
|
85
|
+
| 2.0.0 | Upgrade for compatibility with foundations v2 |
|
|
86
|
+
| 1.1.0 | Fixes for screen reader announcing results |
|
|
87
|
+
| 1.0.0 | First release of search keyword component |
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[class*=rl-c-search--]{background:var(--search-keyword-fill-input-on);display:flex;gap:.375rem;justify-content:space-between;max-width:600px;position:relative;width:100%;z-index:1}.rl-c-search__input{all:unset;border:2px solid var(--search-keyword-fill-input-on);color:var(--search-keyword-text-input-on);flex:1 1 auto;font-family:var(--typography-font-family-title-medium);font-size:var(--typography-size-title-medium);font-weight:var(--typography-weight-title-medium);line-height:var(--type-line-height-single);min-width:0;padding:12px 0;text-indent:10px}@media (--mq-sm ){.rl-c-search__input:-moz-placeholder{padding-right:3.625rem}.rl-c-search__input:placeholder-shown{padding-right:3.625rem}}.rl-c-search__input::-moz-placeholder{color:var(--search-keyword-text-input-on);text-indent:calc(1.5rem - var(--spacing-01))}.rl-c-search__input::placeholder{color:var(--search-keyword-text-input-on);text-indent:calc(1.5rem - var(--spacing-01))}.rl-c-search:hover{border-bottom:1px solid var(--search-keyword-stroke-input-on-hover)}.rl-c-search__input:hover{border-color:var(--search-keyword-fill-input-on)}.rl-c-search__input:focus{outline:.125rem solid var(--color-watermelon);outline-offset:.125rem}.rl-c-search__submit{border:2px solid var(--color-ice);padding:0 10px}.rl-c-search__submit [class*=rl-icon]{cursor:pointer;font-size:2rem}.rl-c-search__clear{border:2px solid var(--color-ice);padding:0 18px}.rl-c-search__clear [class*=rl-icon]{cursor:pointer}.rl-c-search__results-list{background:var(--search-keyword-fill-result);list-style-type:none;margin:0;padding:0;width:100%}.rl-c-search__result-item{border-bottom:1px solid var(--search-keyword-stroke-result);cursor:pointer;padding:12px}.rl-c-search__result-item:hover{background-color:var(--search-keyword-fill-result-hover)}.rl-c-search__results{position:absolute;top:59px;width:100%;z-index:1002}.rl-c-search__results-summary{background:var(--search-keyword-fill-counter);border-bottom:1px solid var(--search-keyword-stroke-result);font-weight:700;padding:var(--spacing-02) 12px}.rl-c-search__result-title{color:var(--search-keyword-text-result-title);font-family:var(--typography-font-family-title-small);font-size:var(--typography-size-title-small);font-weight:700;line-height:var(--type-line-height-body);margin-bottom:var(--spacing-02)}.rl-c-search__result-title:last-child{margin-bottom:0}.rl-c-search__view-all{background:var(--color-teal-dark);color:var(--color-white);font-family:var(--typography-font-family-label-medium);font-size:var(--typography-size-label-medium);font-weight:var(--typography-weight-label-medium);gap:var(--spacing-02);justify-content:flex-start;line-height:var(--type-line-height-single);margin-top:var(--spacing-01);padding:var(--spacing-02) 12px var(--spacing-01)}.rl-c-search__no-results{background:var(--color-cloud);border-bottom:1px solid var(--color-fog);padding:var(--spacing-02) 12px}.rl-c-search__overlay{background:color-mix(in srgb,var(--search-keyword-fill-overlay),transparent 80%);bottom:0;left:0;position:fixed;right:0;top:0;z-index:1000}.rl-c-search--white{background:var(--color-white)}.rl-c-search--white .rl-c-search__input{border:2px solid var(--color-white)}.rl-c-search--white .rl-c-search__input:hover{border-color:var(--color-white)}.rl-c-search--white .rl-c-search__clear,.rl-c-search--white .rl-c-search__submit{border:2px solid var(--color-white)}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export interface SearchItem {
|
|
2
|
+
title: string;
|
|
3
|
+
description: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface SearchBarOptions {
|
|
7
|
+
formSelector?: string;
|
|
8
|
+
inputSelector?: string;
|
|
9
|
+
submitButtonSelector?: string;
|
|
10
|
+
preventDefault?: boolean;
|
|
11
|
+
minCharacters?: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default class SearchBar {
|
|
15
|
+
constructor(searchData: SearchItem[], options?: SearchBarOptions);
|
|
16
|
+
|
|
17
|
+
data: SearchItem[];
|
|
18
|
+
options: Required<SearchBarOptions>;
|
|
19
|
+
form: HTMLFormElement | null;
|
|
20
|
+
input: HTMLInputElement | null;
|
|
21
|
+
submitButton: HTMLButtonElement | null;
|
|
22
|
+
resultsContainer: HTMLDivElement;
|
|
23
|
+
overlay: HTMLDivElement;
|
|
24
|
+
|
|
25
|
+
performSearch(): void;
|
|
26
|
+
displayResults(matches: SearchItem[]): void;
|
|
27
|
+
showResults(): void;
|
|
28
|
+
hideResults(): void;
|
|
29
|
+
clearResults(): void;
|
|
30
|
+
clearInput(): void;
|
|
31
|
+
selectResult(result: string): void;
|
|
32
|
+
closeSearchAndReset(): void;
|
|
33
|
+
}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _utils = require("./utils.js");
|
|
8
|
+
class SearchBar {
|
|
9
|
+
constructor(searchData, options = {}) {
|
|
10
|
+
this.data = searchData;
|
|
11
|
+
this.options = {
|
|
12
|
+
formSelector: '.rl-c-search--ice',
|
|
13
|
+
inputSelector: '#search-input',
|
|
14
|
+
submitButtonSelector: '.rl-c-search__submit',
|
|
15
|
+
preventDefault: false,
|
|
16
|
+
minCharacters: 3,
|
|
17
|
+
...options
|
|
18
|
+
};
|
|
19
|
+
this.form = document.querySelector(this.options.formSelector);
|
|
20
|
+
if (!this.form) {
|
|
21
|
+
console.error(`Form element not found with selector: ${this.options.formSelector}`);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
this.input = this.form.querySelector(this.options.inputSelector);
|
|
25
|
+
this.submitButton = this.form.querySelector(this.options.submitButtonSelector);
|
|
26
|
+
if (!this.input) {
|
|
27
|
+
console.error(`Input element not found with selector: ${this.options.inputSelector}`);
|
|
28
|
+
}
|
|
29
|
+
if (!this.submitButton) {
|
|
30
|
+
console.error(`Submit button not found with selector: ${this.options.submitButtonSelector}`);
|
|
31
|
+
}
|
|
32
|
+
this.createResultsContainer();
|
|
33
|
+
this.createOverlay();
|
|
34
|
+
this.input.addEventListener('input', this.handleInput.bind(this));
|
|
35
|
+
this.input.addEventListener('keydown', this.handleKeydown.bind(this));
|
|
36
|
+
this.form.addEventListener('focusin', this.handleFocusIn.bind(this));
|
|
37
|
+
document.addEventListener('focusin', this.handleFocusOut.bind(this));
|
|
38
|
+
document.addEventListener('click', this.handleClickOutside.bind(this));
|
|
39
|
+
this.form.addEventListener('submit', this.handleSubmit.bind(this));
|
|
40
|
+
document.addEventListener('keydown', this.handleEscapeKey.bind(this));
|
|
41
|
+
this.overlay.addEventListener('click', this.handleOverlayClick.bind(this));
|
|
42
|
+
this.debouncedSearch = (0, _utils.debounce)(this.performSearch.bind(this), 300);
|
|
43
|
+
}
|
|
44
|
+
createResultsContainer() {
|
|
45
|
+
this.resultsContainer = document.createElement('div');
|
|
46
|
+
this.resultsContainer.className = 'rl-c-search__results';
|
|
47
|
+
this.resultsContainer.innerHTML = `
|
|
48
|
+
<div id="results-summary" aria-live="polite" class="rl-c-search__results-summary"></div>
|
|
49
|
+
<ul id="rl-c-search-results" class="rl-c-search__results-list" role="listbox" aria-label="Search results"></ul>
|
|
50
|
+
<div id="no-results" class="rl-c-search__no-results" style="display: none;" aria-live="polite">No results found</div>
|
|
51
|
+
<a href="#" id="view-all" class="rl-c-search__view-all rl-c-button">View all results <svg class="rl-f-icon rl-f-icon--xs" aria-hidden="true"><use xlink:href="/assets/icons.svg#arrow-next"></use></svg></a>
|
|
52
|
+
`;
|
|
53
|
+
this.resultsContainer.addEventListener('focusin', this.handleFocusIn.bind(this));
|
|
54
|
+
this.form.appendChild(this.resultsContainer);
|
|
55
|
+
this.resultsSummary = this.resultsContainer.querySelector('#results-summary');
|
|
56
|
+
this.resultsList = this.resultsContainer.querySelector('#rl-c-search-results');
|
|
57
|
+
this.noResults = this.resultsContainer.querySelector('#no-results');
|
|
58
|
+
this.viewAllLink = this.resultsContainer.querySelector('#view-all');
|
|
59
|
+
this.hideResults();
|
|
60
|
+
}
|
|
61
|
+
createOverlay() {
|
|
62
|
+
this.overlay = document.createElement('div');
|
|
63
|
+
this.overlay.className = 'rl-c-search__overlay';
|
|
64
|
+
document.body.appendChild(this.overlay);
|
|
65
|
+
this.overlay.style.display = 'none';
|
|
66
|
+
}
|
|
67
|
+
handleInput() {
|
|
68
|
+
const query = this.input.value;
|
|
69
|
+
this.toggleClearButton(query);
|
|
70
|
+
this.debouncedSearch();
|
|
71
|
+
}
|
|
72
|
+
handleFocusIn() {
|
|
73
|
+
this.showOverlay();
|
|
74
|
+
this.form.classList.add('rl-c-search--focused');
|
|
75
|
+
this.form.style.zIndex = '1002';
|
|
76
|
+
}
|
|
77
|
+
handleFocusOut(event) {
|
|
78
|
+
if (!this.form.contains(event.target) && !this.resultsContainer.contains(event.target)) {
|
|
79
|
+
this.closeSearchAndReset();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
showOverlay() {
|
|
83
|
+
this.overlay.style.display = 'block';
|
|
84
|
+
}
|
|
85
|
+
hideOverlay() {
|
|
86
|
+
this.overlay.style.display = 'none';
|
|
87
|
+
}
|
|
88
|
+
toggleClearButton(query) {
|
|
89
|
+
if (query.length > 0) {
|
|
90
|
+
if (!this.clearButton) {
|
|
91
|
+
this.clearButton = document.createElement('button');
|
|
92
|
+
this.clearButton.type = 'button';
|
|
93
|
+
this.clearButton.className = 'rl-c-search__clear';
|
|
94
|
+
this.clearButton.innerHTML = '<svg class="rl-f-icon rl-f-icon--xs rl-f-icon--secondary"><title>Clear search</title><use xlink:href="/assets/icons.svg#no"></use></svg>';
|
|
95
|
+
this.clearButton.addEventListener('click', this.clearInput.bind(this));
|
|
96
|
+
this.input.parentNode.insertBefore(this.clearButton, this.input.nextSibling);
|
|
97
|
+
}
|
|
98
|
+
this.clearButton.style.display = 'inline';
|
|
99
|
+
} else if (this.clearButton) {
|
|
100
|
+
this.clearButton.style.display = 'none';
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
clearInput() {
|
|
104
|
+
this.input.value = '';
|
|
105
|
+
this.toggleClearButton('');
|
|
106
|
+
this.clearResults();
|
|
107
|
+
this.input.focus();
|
|
108
|
+
}
|
|
109
|
+
performSearch() {
|
|
110
|
+
const query = this.input.value.toLowerCase();
|
|
111
|
+
if (query.length < this.options.minCharacters) {
|
|
112
|
+
this.clearResults();
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const matches = this.data.filter(item => item.title.toLowerCase().includes(query) || item.description.toLowerCase().includes(query));
|
|
116
|
+
this.displayResults(matches);
|
|
117
|
+
}
|
|
118
|
+
displayResults(matches) {
|
|
119
|
+
this.clearResults();
|
|
120
|
+
if (matches.length === 0) {
|
|
121
|
+
this.noResults.style.display = 'block';
|
|
122
|
+
this.showResults();
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const displayLimit = 6;
|
|
126
|
+
const displayedMatches = matches.slice(0, displayLimit);
|
|
127
|
+
const summaryText = `Showing ${displayedMatches.length} of ${matches.length} results`;
|
|
128
|
+
this.resultsSummary.textContent = summaryText;
|
|
129
|
+
this.resultsSummary.setAttribute("tabindex", "0");
|
|
130
|
+
this.resultsSummary.style.display = 'block';
|
|
131
|
+
displayedMatches.forEach((match, index) => {
|
|
132
|
+
const li = document.createElement('li');
|
|
133
|
+
li.className = 'rl-c-search__result-item';
|
|
134
|
+
li.setAttribute('role', 'option');
|
|
135
|
+
li.setAttribute('id', `search-result-${index}`);
|
|
136
|
+
li.setAttribute('aria-selected', 'false');
|
|
137
|
+
li.tabIndex = 0;
|
|
138
|
+
li.innerHTML = `
|
|
139
|
+
<div class="rl-c-search__result-title">${match.title}</div>
|
|
140
|
+
<div class="rl-c-search__result-description">${match.description}</div>
|
|
141
|
+
`;
|
|
142
|
+
li.addEventListener('click', () => this.selectResult(match.title));
|
|
143
|
+
this.resultsList.appendChild(li);
|
|
144
|
+
});
|
|
145
|
+
this.viewAllLink.style.display = matches.length > displayLimit ? 'flex' : 'none';
|
|
146
|
+
this.showResults();
|
|
147
|
+
|
|
148
|
+
// Update ARIA attributes
|
|
149
|
+
this.input.setAttribute('aria-expanded', 'true');
|
|
150
|
+
this.resultsList.setAttribute('aria-busy', 'false');
|
|
151
|
+
|
|
152
|
+
// Set focus to the first result
|
|
153
|
+
if (displayedMatches.length > 0) {
|
|
154
|
+
const firstResult = this.resultsList.querySelector('.rl-c-search__result-item');
|
|
155
|
+
firstResult.setAttribute('aria-selected', 'true');
|
|
156
|
+
this.input.setAttribute('aria-activedescendant', firstResult.id);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
showResults() {
|
|
160
|
+
this.resultsContainer.style.display = 'block';
|
|
161
|
+
}
|
|
162
|
+
hideResults() {
|
|
163
|
+
this.resultsContainer.style.display = 'none';
|
|
164
|
+
}
|
|
165
|
+
clearResults() {
|
|
166
|
+
this.resultsList.innerHTML = '';
|
|
167
|
+
this.resultsSummary.style.display = 'none';
|
|
168
|
+
this.noResults.style.display = 'none';
|
|
169
|
+
this.viewAllLink.style.display = 'none';
|
|
170
|
+
this.hideResults();
|
|
171
|
+
this.input.removeAttribute('aria-activedescendant');
|
|
172
|
+
this.input.setAttribute('aria-expanded', 'false');
|
|
173
|
+
}
|
|
174
|
+
selectResult(result) {
|
|
175
|
+
this.input.value = result;
|
|
176
|
+
this.toggleClearButton(result);
|
|
177
|
+
this.clearResults();
|
|
178
|
+
this.hideOverlay();
|
|
179
|
+
this.input.focus();
|
|
180
|
+
}
|
|
181
|
+
handleKeydown(e) {
|
|
182
|
+
const items = this.resultsList.querySelectorAll('.rl-c-search__result-item');
|
|
183
|
+
const currentIndex = Array.from(items).findIndex(item => item.getAttribute('aria-selected') === 'true');
|
|
184
|
+
switch (e.key) {
|
|
185
|
+
case 'ArrowDown':
|
|
186
|
+
case 'ArrowUp':
|
|
187
|
+
e.preventDefault();
|
|
188
|
+
let nextIndex;
|
|
189
|
+
if (e.key === 'ArrowDown') {
|
|
190
|
+
nextIndex = currentIndex < items.length - 1 ? currentIndex + 1 : 0;
|
|
191
|
+
} else {
|
|
192
|
+
nextIndex = currentIndex > 0 ? currentIndex - 1 : items.length - 1;
|
|
193
|
+
}
|
|
194
|
+
items.forEach(item => item.setAttribute('aria-selected', 'false'));
|
|
195
|
+
items[nextIndex].setAttribute('aria-selected', 'true');
|
|
196
|
+
this.input.setAttribute('aria-activedescendant', `search-result-${nextIndex}`);
|
|
197
|
+
break;
|
|
198
|
+
case 'Enter':
|
|
199
|
+
if (document.activeElement === this.input && currentIndex !== -1) {
|
|
200
|
+
e.preventDefault();
|
|
201
|
+
this.selectResult(items[currentIndex].querySelector('.rl-c-search__result-title').textContent);
|
|
202
|
+
}
|
|
203
|
+
break;
|
|
204
|
+
case 'Escape':
|
|
205
|
+
this.closeSearchAndReset();
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
handleEscapeKey(e) {
|
|
210
|
+
if (e.key === 'Escape') {
|
|
211
|
+
this.closeSearchAndReset();
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
handleOverlayClick(e) {
|
|
215
|
+
if (e.target === this.overlay) {
|
|
216
|
+
this.closeSearchAndReset();
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
closeSearchAndReset() {
|
|
220
|
+
this.clearResults();
|
|
221
|
+
this.hideOverlay();
|
|
222
|
+
this.form.classList.remove('rl-c-search--focused');
|
|
223
|
+
this.input.value = '';
|
|
224
|
+
this.toggleClearButton('');
|
|
225
|
+
this.input.setAttribute('aria-expanded', 'false');
|
|
226
|
+
this.input.removeAttribute('aria-activedescendant');
|
|
227
|
+
this.input.blur();
|
|
228
|
+
this.form.style.zIndex = '1';
|
|
229
|
+
}
|
|
230
|
+
handleClickOutside(e) {
|
|
231
|
+
if (!this.form.contains(e.target) && !this.resultsContainer.contains(e.target) && e.target !== this.overlay) {
|
|
232
|
+
this.closeSearchAndReset();
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
handleSubmit(e) {
|
|
236
|
+
if (this.options.preventDefault) {
|
|
237
|
+
e.preventDefault();
|
|
238
|
+
}
|
|
239
|
+
this.performSearch();
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
exports.default = SearchBar;
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.debounce = debounce;
|
|
7
|
+
function debounce(func, delay) {
|
|
8
|
+
let timeoutId;
|
|
9
|
+
return function (...args) {
|
|
10
|
+
clearTimeout(timeoutId);
|
|
11
|
+
timeoutId = setTimeout(() => func.apply(this, args), delay);
|
|
12
|
+
};
|
|
13
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,44 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@royallondon/design-system-component-search-keyword",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "2.3.0",
|
|
4
|
+
"description": "Royal London Design System - Search keyword package",
|
|
5
|
+
"homepage": "https://design.royallondon.com/components/search-keyword/",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://dev.azure.com/RLG-UK/GTC/_git/design-system",
|
|
9
|
+
"directory": "ds-packages/packages/design-system-component-search-keyword"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://design.royallondon.com/support/"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"royal-london-design-system-component-search-keyword",
|
|
16
|
+
"rlds-search-keyword",
|
|
17
|
+
"royal-london"
|
|
18
|
+
],
|
|
19
|
+
"author": "Design System Squad <DesignSystem@royallondon.com>",
|
|
20
|
+
"license": "SEE LICENSE IN LICENSE.md",
|
|
21
|
+
"main": "dist/search-keyword.css",
|
|
22
|
+
"style": "dist/search-keyword.css",
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build:clean": "npx rimraf dist",
|
|
25
|
+
"build:lint": "stylelint src/**/*.{css,pcss} --fix",
|
|
26
|
+
"build:css": "postcss src/**/search-keyword.pcss --base src --dir dist/ --ext css",
|
|
27
|
+
"build:js": "npm-run-all --serial build:babel build:types",
|
|
28
|
+
"build:babel": "babel src/ --out-dir dist/ --config-file ../../babel.config",
|
|
29
|
+
"build:types": "npx copyfiles -u 1 \"src/**/*.d.ts\" dist",
|
|
30
|
+
"build": "npm-run-all --serial build:clean build:css build:js"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@royallondon/design-system-foundations": "^2.23.0"
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"@royallondon/design-system-foundations": "^2.19.2"
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"dist"
|
|
40
|
+
],
|
|
41
|
+
"publishConfig": {
|
|
42
|
+
"access": "public"
|
|
43
|
+
}
|
|
6
44
|
}
|