@fluid-topics/ft-search-bar 0.2.21 → 0.3.1
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/build/ft-search-bar.css.d.ts +17 -20
- package/build/ft-search-bar.css.js +18 -126
- package/build/ft-search-bar.d.ts +36 -49
- package/build/ft-search-bar.js +46 -410
- package/build/ft-search-bar.light.js +396 -393
- package/build/ft-search-bar.min.js +509 -500
- package/build/index.d.ts +1 -0
- package/build/index.js +1 -0
- package/build/managers/DesktopSearchBarManager.d.ts +16 -0
- package/build/managers/DesktopSearchBarManager.js +131 -0
- package/build/managers/FacetsChipsManager.d.ts +10 -0
- package/build/managers/FacetsChipsManager.js +129 -0
- package/build/managers/MobileSearchBarManager.d.ts +14 -0
- package/build/managers/MobileSearchBarManager.js +92 -0
- package/build/managers/SuggestManager.d.ts +20 -0
- package/build/managers/SuggestManager.js +215 -0
- package/package.json +17 -17
package/build/index.d.ts
CHANGED
package/build/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { customElement } from "@fluid-topics/ft-wc-utils";
|
|
2
2
|
import { FtSearchBar } from "./ft-search-bar";
|
|
3
3
|
export * from "./ft-search-bar";
|
|
4
|
+
export { FtSearchBarCssVariables } from "./ft-search-bar.css";
|
|
4
5
|
customElement("ft-search-bar")(FtSearchBar);
|
|
5
6
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { FtSearchBar } from "../ft-search-bar";
|
|
2
|
+
import { FacetsChipsManager } from "./FacetsChipsManager";
|
|
3
|
+
import { SuggestManager } from "./SuggestManager";
|
|
4
|
+
export declare class DesktopSearchBarManager {
|
|
5
|
+
private searchBar;
|
|
6
|
+
private selectedFacetsManager;
|
|
7
|
+
private suggestManager;
|
|
8
|
+
constructor(searchBar: FtSearchBar, selectedFacetsManager?: FacetsChipsManager, suggestManager?: SuggestManager);
|
|
9
|
+
static styles: import("lit").CSSResult;
|
|
10
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
11
|
+
private renderSearchBarLeftAction;
|
|
12
|
+
private renderDesktopSearchBarButtons;
|
|
13
|
+
private onSearchBarKeyDown;
|
|
14
|
+
private onSearchBarKeyUp;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=DesktopSearchBarManager.d.ts.map
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { css, html, nothing } from "lit";
|
|
2
|
+
import { repeat } from "lit/directives/repeat.js";
|
|
3
|
+
import { FacetsChipsManager } from "./FacetsChipsManager";
|
|
4
|
+
import { SuggestManager } from "./SuggestManager";
|
|
5
|
+
import { classMap } from "lit/directives/class-map.js";
|
|
6
|
+
export class DesktopSearchBarManager {
|
|
7
|
+
constructor(searchBar, selectedFacetsManager, suggestManager) {
|
|
8
|
+
this.searchBar = searchBar;
|
|
9
|
+
this.selectedFacetsManager = selectedFacetsManager !== null && selectedFacetsManager !== void 0 ? selectedFacetsManager : new FacetsChipsManager(searchBar);
|
|
10
|
+
this.suggestManager = suggestManager !== null && suggestManager !== void 0 ? suggestManager : new SuggestManager(searchBar);
|
|
11
|
+
}
|
|
12
|
+
render() {
|
|
13
|
+
const rootClasses = {
|
|
14
|
+
"ft-search-bar--container": true,
|
|
15
|
+
"ft-search-bar--dense": this.searchBar.dense,
|
|
16
|
+
"ft-search-bar--desktop": true,
|
|
17
|
+
"ft-search-bar--floating-panel-open": this.searchBar.displayFacets && !this.searchBar.forceMenuOpen,
|
|
18
|
+
"ft-search-bar--forced-open": this.searchBar.forceMenuOpen
|
|
19
|
+
};
|
|
20
|
+
return html `
|
|
21
|
+
<div class="${classMap(rootClasses)}" part="container" tabindex="-1">
|
|
22
|
+
<div class="ft-search-bar" part="search-bar">
|
|
23
|
+
${(this.renderSearchBarLeftAction())}
|
|
24
|
+
<div class="ft-search-bar--input-container" part="input-container" tabindex="-1">
|
|
25
|
+
<div class="ft-search-bar--input-outline" part="input-outline">
|
|
26
|
+
${this.searchBar.dense ? this.selectedFacetsManager.render() : nothing}
|
|
27
|
+
<input class="ft-search-bar--input ft-typography--body2"
|
|
28
|
+
part="input"
|
|
29
|
+
type="text"
|
|
30
|
+
placeholder="${this.searchBar.labelResolver.resolve("inputPlaceHolder")}"
|
|
31
|
+
value="${this.searchBar.query}"
|
|
32
|
+
@keydown=${(e) => this.onSearchBarKeyDown(e)}
|
|
33
|
+
@keyup=${(e) => this.onSearchBarKeyUp(e)}>
|
|
34
|
+
</div>
|
|
35
|
+
${this.suggestManager.render()}
|
|
36
|
+
</div>
|
|
37
|
+
${this.renderDesktopSearchBarButtons()}
|
|
38
|
+
${this.searchBar.renderDesktopFloatingMenu()}
|
|
39
|
+
</div>
|
|
40
|
+
${this.searchBar.dense
|
|
41
|
+
? nothing
|
|
42
|
+
: this.searchBar.forceMenuOpen ? this.searchBar.renderDesktopMenu()
|
|
43
|
+
: this.selectedFacetsManager.render()}
|
|
44
|
+
</div>
|
|
45
|
+
`;
|
|
46
|
+
}
|
|
47
|
+
renderSearchBarLeftAction() {
|
|
48
|
+
if (this.searchBar.hasFacets()) {
|
|
49
|
+
return this.searchBar.forceMenuOpen ? nothing : html `
|
|
50
|
+
<ft-button class="ft-search-bar--filters-opener ft-search-bar--left-action"
|
|
51
|
+
part="filters-opener"
|
|
52
|
+
trailingIcon
|
|
53
|
+
icon="${this.searchBar.displayFacets ? "expand_less" : "expand_more"}"
|
|
54
|
+
@click=${(e) => {
|
|
55
|
+
e.stopPropagation();
|
|
56
|
+
this.searchBar.displayFacets = !this.searchBar.displayFacets;
|
|
57
|
+
}}
|
|
58
|
+
@focusin=${(e) => e.stopPropagation()}>
|
|
59
|
+
${this.searchBar.labelResolver.resolve("filtersButton")}
|
|
60
|
+
</ft-button>
|
|
61
|
+
`;
|
|
62
|
+
}
|
|
63
|
+
else if (this.searchBar.hasLocaleSelector()) {
|
|
64
|
+
return html `
|
|
65
|
+
<ft-select outlined
|
|
66
|
+
class="ft-search-bar--content-locale ft-search-bar--left-action"
|
|
67
|
+
part="content-locale select-ft-locale"
|
|
68
|
+
.exportpartsPrefixes=${["content-locale", "select-ft-locale"]}
|
|
69
|
+
@change=${(e) => this.searchBar.contentLocale = e.detail == null ? undefined : e.detail}>
|
|
70
|
+
${repeat(this.searchBar.availableContentLocales, l => l.lang, l => html `
|
|
71
|
+
<ft-select-option .value=${l.lang}
|
|
72
|
+
label="${l.label}"
|
|
73
|
+
?selected=${l.lang === this.searchBar.contentLocale}>
|
|
74
|
+
</ft-select-option>
|
|
75
|
+
`)}
|
|
76
|
+
</ft-select>
|
|
77
|
+
`;
|
|
78
|
+
}
|
|
79
|
+
return nothing;
|
|
80
|
+
}
|
|
81
|
+
renderDesktopSearchBarButtons() {
|
|
82
|
+
return html `
|
|
83
|
+
<div class="ft-search-bar--actions"
|
|
84
|
+
part="search-bar-actions">
|
|
85
|
+
${this.searchBar.query ? html `
|
|
86
|
+
<ft-button class="ft-search-bar--clear-query"
|
|
87
|
+
part="clear-query"
|
|
88
|
+
icon="close"
|
|
89
|
+
round dense
|
|
90
|
+
label="${this.searchBar.labelResolver.resolve("clearInputButton")}"
|
|
91
|
+
@click=${() => this.searchBar.setQuery("")}
|
|
92
|
+
></ft-button>
|
|
93
|
+
<div class="ft-search-bar--separator"></div>
|
|
94
|
+
` : null}
|
|
95
|
+
<ft-button class="ft-search-bar--launch-search"
|
|
96
|
+
part="launch-search-in-bar"
|
|
97
|
+
icon="search"
|
|
98
|
+
round dense
|
|
99
|
+
label="${this.searchBar.labelResolver.resolve("searchButton")}"
|
|
100
|
+
@click=${() => this.searchBar.launchSearch()}
|
|
101
|
+
></ft-button>
|
|
102
|
+
</div>
|
|
103
|
+
`;
|
|
104
|
+
}
|
|
105
|
+
onSearchBarKeyDown(e) {
|
|
106
|
+
var _a;
|
|
107
|
+
switch (e.key) {
|
|
108
|
+
case "Escape":
|
|
109
|
+
this.searchBar.mobileMenuOpen = false;
|
|
110
|
+
(_a = this.searchBar.input) === null || _a === void 0 ? void 0 : _a.blur();
|
|
111
|
+
break;
|
|
112
|
+
case "ArrowDown":
|
|
113
|
+
e.stopPropagation();
|
|
114
|
+
e.preventDefault();
|
|
115
|
+
this.suggestManager.focusFirstSuggestion();
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
onSearchBarKeyUp(e) {
|
|
120
|
+
const input = e.composedPath()[0];
|
|
121
|
+
this.searchBar.query = input.value;
|
|
122
|
+
if (e.key === "Enter") {
|
|
123
|
+
this.searchBar.launchSearch();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
//language=css
|
|
128
|
+
DesktopSearchBarManager.styles = css `
|
|
129
|
+
|
|
130
|
+
`;
|
|
131
|
+
//# sourceMappingURL=DesktopSearchBarManager.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { FtSearchBar } from "../ft-search-bar";
|
|
2
|
+
export declare class FacetsChipsManager {
|
|
3
|
+
private searchBar;
|
|
4
|
+
constructor(searchBar: FtSearchBar);
|
|
5
|
+
static styles: import("lit").CSSResult;
|
|
6
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
7
|
+
private openMobileFilters;
|
|
8
|
+
getLocaleLabel(locale: string | undefined): string | undefined;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=FacetsChipsManager.d.ts.map
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { css, html, nothing } from "lit";
|
|
2
|
+
import { repeat } from "lit/directives/repeat.js";
|
|
3
|
+
import { getBreadcrumbFromValue, getLabelFromValue, getSelectedValues } from "../converters";
|
|
4
|
+
import { setVariable } from "@fluid-topics/ft-wc-utils";
|
|
5
|
+
import { FtSnapScrollCssVariables } from "@fluid-topics/ft-snap-scroll";
|
|
6
|
+
import { FtChipCssVariables } from "@fluid-topics/ft-chip";
|
|
7
|
+
export class FacetsChipsManager {
|
|
8
|
+
constructor(searchBar) {
|
|
9
|
+
this.searchBar = searchBar;
|
|
10
|
+
}
|
|
11
|
+
render() {
|
|
12
|
+
if (!this.searchBar.hasLocaleSelector() && !this.searchBar.hasFacets()) {
|
|
13
|
+
return html ``;
|
|
14
|
+
}
|
|
15
|
+
const isMobile = this.searchBar.isMobile();
|
|
16
|
+
const useSnapScroll = (!isMobile && this.searchBar.dense) || (isMobile && this.searchBar.isMobileMenuOpen());
|
|
17
|
+
const filters = html `
|
|
18
|
+
${this.searchBar.hasLocaleSelector() && (this.searchBar.hasFacets() || isMobile) ? html `
|
|
19
|
+
<ft-chip part="selected-filters selected-filter-ft-locale"
|
|
20
|
+
?dense=${this.searchBar.dense && !isMobile}
|
|
21
|
+
?clickable=${isMobile}
|
|
22
|
+
@click=${() => this.openMobileFilters("ft:locale")}
|
|
23
|
+
data-key="ft:locale"
|
|
24
|
+
data-value="${this.searchBar.contentLocale}">
|
|
25
|
+
${(this.getLocaleLabel(this.searchBar.contentLocale))}
|
|
26
|
+
</ft-chip>
|
|
27
|
+
` : null}
|
|
28
|
+
${repeat(this.searchBar.facets, facet => facet.key, facet => {
|
|
29
|
+
const values = getSelectedValues(facet);
|
|
30
|
+
return repeat(values, value => {
|
|
31
|
+
let label = facet.label + ": " + getBreadcrumbFromValue(value);
|
|
32
|
+
const keyWithNoColumn = facet.key.replace(":", "-");
|
|
33
|
+
const chip = html `
|
|
34
|
+
<ft-chip
|
|
35
|
+
part="selected-filters selected-filter-${keyWithNoColumn}"
|
|
36
|
+
?dense=${this.searchBar.dense && !isMobile}
|
|
37
|
+
?clickable=${isMobile}
|
|
38
|
+
?removable=${!isMobile}
|
|
39
|
+
icon=${isMobile ? nothing : "close"}
|
|
40
|
+
label="${label}"
|
|
41
|
+
title=${useSnapScroll ? label : nothing}
|
|
42
|
+
@click=${() => this.openMobileFilters(facet.key)}
|
|
43
|
+
@icon-click=${() => this.searchBar.setFilter(facet.key, values.filter(v => v !== value))}
|
|
44
|
+
data-key="${facet.key}"
|
|
45
|
+
data-value="${value}">
|
|
46
|
+
${getLabelFromValue(value)}
|
|
47
|
+
</ft-chip>
|
|
48
|
+
`;
|
|
49
|
+
return useSnapScroll ? chip : html `
|
|
50
|
+
<ft-tooltip inline text="${label}">
|
|
51
|
+
${chip}
|
|
52
|
+
</ft-tooltip>
|
|
53
|
+
`;
|
|
54
|
+
});
|
|
55
|
+
})}
|
|
56
|
+
${isMobile ? html `
|
|
57
|
+
<ft-chip part="selected-filters mobile-filters-opener"
|
|
58
|
+
icon="add"
|
|
59
|
+
clickable
|
|
60
|
+
@click=${() => {
|
|
61
|
+
this.searchBar.mobileMenuOpen = true;
|
|
62
|
+
this.searchBar.displayFacets = true;
|
|
63
|
+
}}>
|
|
64
|
+
${this.searchBar.labelResolver.resolve("filtersButton")}
|
|
65
|
+
</ft-chip>
|
|
66
|
+
` : nothing}
|
|
67
|
+
`;
|
|
68
|
+
return useSnapScroll
|
|
69
|
+
? html `
|
|
70
|
+
<ft-snap-scroll horizontal controls hideScrollbar limitSize
|
|
71
|
+
class="ft-search-bar--selected-filters"
|
|
72
|
+
part="selected-filters-container"
|
|
73
|
+
exportpartsPrefix="selected-filters-container">
|
|
74
|
+
${filters}
|
|
75
|
+
</ft-snap-scroll>
|
|
76
|
+
`
|
|
77
|
+
: html `
|
|
78
|
+
<div class="ft-search-bar--selected-filters" part="selected-filters-container">
|
|
79
|
+
${filters}
|
|
80
|
+
</div>
|
|
81
|
+
`;
|
|
82
|
+
}
|
|
83
|
+
openMobileFilters(filterKey) {
|
|
84
|
+
if (this.searchBar.isMobile()) {
|
|
85
|
+
this.searchBar.mobileMenuOpen = true;
|
|
86
|
+
this.searchBar.displayFacets = true;
|
|
87
|
+
this.searchBar.scrollToFacet = filterKey;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
getLocaleLabel(locale) {
|
|
91
|
+
var _a;
|
|
92
|
+
return (_a = this.searchBar.availableContentLocales.filter(l => { var _a; return ((_a = l.lang) !== null && _a !== void 0 ? _a : "").toLowerCase() === (locale !== null && locale !== void 0 ? locale : "").toLowerCase(); })
|
|
93
|
+
.map(l => l.label)
|
|
94
|
+
.pop()) !== null && _a !== void 0 ? _a : locale;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
//language=css
|
|
98
|
+
FacetsChipsManager.styles = css `
|
|
99
|
+
.ft-search-bar--selected-filters:not(ft-snap-scroll) {
|
|
100
|
+
flex-shrink: 0;
|
|
101
|
+
display: flex;
|
|
102
|
+
flex-direction: row;
|
|
103
|
+
flex-wrap: wrap;
|
|
104
|
+
gap: 8px;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
ft-snap-scroll.ft-search-bar--selected-filters {
|
|
108
|
+
overflow: hidden;
|
|
109
|
+
${setVariable(FtSnapScrollCssVariables.gap, "4px")};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
ft-snap-scroll.ft-search-bar--selected-filters::part(content) {
|
|
113
|
+
align-items: center;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.ft-search-bar--desktop ft-snap-scroll.ft-search-bar--selected-filters {
|
|
117
|
+
${setVariable(FtChipCssVariables.iconSize, "17px")};
|
|
118
|
+
${setVariable(FtChipCssVariables.fontSize, "12px")};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.ft-search-bar--selected-filters * {
|
|
122
|
+
max-width: 100%;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.ft-search-bar--selected-filters ft-chip {
|
|
126
|
+
flex-grow: 0;
|
|
127
|
+
}
|
|
128
|
+
`;
|
|
129
|
+
//# sourceMappingURL=FacetsChipsManager.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { FtSearchBar } from "../ft-search-bar";
|
|
2
|
+
import { FacetsChipsManager } from "./FacetsChipsManager";
|
|
3
|
+
import { SuggestManager } from "./SuggestManager";
|
|
4
|
+
export declare class MobileSearchBarManager {
|
|
5
|
+
private searchBar;
|
|
6
|
+
private selectedFacetsManager;
|
|
7
|
+
private suggestManager;
|
|
8
|
+
constructor(searchBar: FtSearchBar, selectedFacetsManager?: FacetsChipsManager, suggestManager?: SuggestManager);
|
|
9
|
+
static styles: import("lit").CSSResult;
|
|
10
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
11
|
+
private renderMobileSearchBarButtons;
|
|
12
|
+
private onSearchBarKeyUp;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=MobileSearchBarManager.d.ts.map
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { css, html, nothing } from "lit";
|
|
2
|
+
import { FacetsChipsManager } from "./FacetsChipsManager";
|
|
3
|
+
import { SuggestManager } from "./SuggestManager";
|
|
4
|
+
import { classMap } from "lit/directives/class-map.js";
|
|
5
|
+
export class MobileSearchBarManager {
|
|
6
|
+
constructor(searchBar, selectedFacetsManager, suggestManager) {
|
|
7
|
+
this.searchBar = searchBar;
|
|
8
|
+
this.selectedFacetsManager = selectedFacetsManager !== null && selectedFacetsManager !== void 0 ? selectedFacetsManager : new FacetsChipsManager(searchBar);
|
|
9
|
+
this.suggestManager = suggestManager !== null && suggestManager !== void 0 ? suggestManager : new SuggestManager(searchBar);
|
|
10
|
+
}
|
|
11
|
+
render() {
|
|
12
|
+
const rootClasses = {
|
|
13
|
+
"ft-search-bar--container": true,
|
|
14
|
+
"ft-search-bar--mobile": true,
|
|
15
|
+
"ft-search-bar--mobile-menu-open": this.searchBar.isMobileMenuOpen(),
|
|
16
|
+
"ft-search-bar--forced-open": this.searchBar.forceMobileMenuOpen,
|
|
17
|
+
};
|
|
18
|
+
return html `
|
|
19
|
+
<div class="${classMap(rootClasses)}" part="container" tabindex="-1">
|
|
20
|
+
<div class="ft-search-bar">
|
|
21
|
+
<div class="ft-search-bar--input-container" part="input-container">
|
|
22
|
+
<div class="ft-search-bar--input-outline" part="input-outline">
|
|
23
|
+
<input class="ft-search-bar--input ft-typography--body2"
|
|
24
|
+
part="input"
|
|
25
|
+
type="text"
|
|
26
|
+
placeholder="${this.searchBar.labelResolver.resolve("inputPlaceHolder")}"
|
|
27
|
+
value="${this.searchBar.query}"
|
|
28
|
+
@keyup=${(e) => this.onSearchBarKeyUp(e)}
|
|
29
|
+
@focus=${() => {
|
|
30
|
+
this.searchBar.mobileMenuOpen = true;
|
|
31
|
+
this.searchBar.displayFacets = false;
|
|
32
|
+
}}>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
${this.renderMobileSearchBarButtons()}
|
|
36
|
+
</div>
|
|
37
|
+
${this.searchBar.displayFacets ? this.searchBar.renderFacetsActions() : this.selectedFacetsManager.render()}
|
|
38
|
+
${this.searchBar.displayFacets ? this.searchBar.renderMobileFacets() : this.suggestManager.render()}
|
|
39
|
+
${this.searchBar.isMobileMenuOpen() || this.searchBar.displayFacets ? html `
|
|
40
|
+
<ft-button class="ft-search-bar--launch-search"
|
|
41
|
+
part="launch-search-in-panel"
|
|
42
|
+
icon="search"
|
|
43
|
+
@click=${this.searchBar.launchSearch}>
|
|
44
|
+
${this.searchBar.labelResolver.resolve("searchButton")}
|
|
45
|
+
</ft-button>
|
|
46
|
+
` : nothing}
|
|
47
|
+
</div>
|
|
48
|
+
`;
|
|
49
|
+
}
|
|
50
|
+
renderMobileSearchBarButtons() {
|
|
51
|
+
const displayClearButton = this.searchBar.query || (this.searchBar.isMobileMenuOpen() && !this.searchBar.forceMobileMenuOpen);
|
|
52
|
+
return html `
|
|
53
|
+
<div class="ft-search-bar--actions" part="search-bar-actions">
|
|
54
|
+
${displayClearButton ? html `
|
|
55
|
+
<ft-button class="ft-search-bar--clear-query"
|
|
56
|
+
part="clear-query"
|
|
57
|
+
icon="close"
|
|
58
|
+
round
|
|
59
|
+
label="${this.searchBar.labelResolver.resolve("clearInputButton")}"
|
|
60
|
+
tooltipPosition="left"
|
|
61
|
+
@click=${() => {
|
|
62
|
+
this.searchBar.setQuery("");
|
|
63
|
+
this.searchBar.mobileMenuOpen = false;
|
|
64
|
+
this.searchBar.displayFacets = false;
|
|
65
|
+
}}
|
|
66
|
+
></ft-button>
|
|
67
|
+
<div class="ft-search-bar--separator"></div>
|
|
68
|
+
` : nothing}
|
|
69
|
+
<ft-button class="ft-search-bar--launch-search"
|
|
70
|
+
part="launch-search-in-bar"
|
|
71
|
+
icon="search"
|
|
72
|
+
round
|
|
73
|
+
label="${this.searchBar.labelResolver.resolve("searchButton")}"
|
|
74
|
+
tooltipPosition="left"
|
|
75
|
+
@click=${() => { var _a; return this.searchBar.isMobileMenuOpen() ? this.searchBar.launchSearch() : (_a = this.searchBar.input) === null || _a === void 0 ? void 0 : _a.focus(); }}
|
|
76
|
+
></ft-button>
|
|
77
|
+
</div>
|
|
78
|
+
`;
|
|
79
|
+
}
|
|
80
|
+
onSearchBarKeyUp(e) {
|
|
81
|
+
const input = e.composedPath()[0];
|
|
82
|
+
this.searchBar.query = input.value;
|
|
83
|
+
if (e.key === "Enter") {
|
|
84
|
+
this.searchBar.launchSearch();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
//language=css
|
|
89
|
+
MobileSearchBarManager.styles = css `
|
|
90
|
+
|
|
91
|
+
`;
|
|
92
|
+
//# sourceMappingURL=MobileSearchBarManager.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { FtSearchBar } from "../ft-search-bar";
|
|
2
|
+
export declare class SuggestManager {
|
|
3
|
+
private searchBar;
|
|
4
|
+
private updateDebouncer;
|
|
5
|
+
constructor(searchBar: FtSearchBar, debounceTime?: number);
|
|
6
|
+
static styles: import("lit").CSSResult;
|
|
7
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
8
|
+
update(): Promise<void>;
|
|
9
|
+
private onSuggestKeyDown;
|
|
10
|
+
private onSuggestKeyUp;
|
|
11
|
+
private onSuggestClick;
|
|
12
|
+
private onSuggestSelected;
|
|
13
|
+
private removeRecentSearch;
|
|
14
|
+
private getIcon;
|
|
15
|
+
private getFocusedSuggestionElement;
|
|
16
|
+
private getLastSuggestionElement;
|
|
17
|
+
private getFirstSuggestionElement;
|
|
18
|
+
focusFirstSuggestion(): void;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=SuggestManager.d.ts.map
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { css, html } from "lit";
|
|
2
|
+
import { repeat } from "lit/directives/repeat.js";
|
|
3
|
+
import { FtIcons, FtIconVariants, resolveFileFormatIcon } from "@fluid-topics/ft-icon";
|
|
4
|
+
import { Debouncer } from "@fluid-topics/ft-wc-utils";
|
|
5
|
+
import { FtSearchBarCssVariables } from "../ft-search-bar.css";
|
|
6
|
+
export class SuggestManager {
|
|
7
|
+
constructor(searchBar, debounceTime = 300) {
|
|
8
|
+
this.searchBar = searchBar;
|
|
9
|
+
this.updateDebouncer = new Debouncer(debounceTime);
|
|
10
|
+
}
|
|
11
|
+
render() {
|
|
12
|
+
const filteredRecentSearches = this.searchBar.recentSearches.filter(q => q.toLowerCase().includes(this.searchBar.query.toLowerCase()));
|
|
13
|
+
const shouldDisplaySuggestions = this.searchBar.suggestions.length > 0 || filteredRecentSearches.length > 0;
|
|
14
|
+
return html `
|
|
15
|
+
<div class="ft-search-bar--suggestions ${shouldDisplaySuggestions ? "ft-search-bar--suggestions-not-empty" : ""}"
|
|
16
|
+
part="suggestions-container"
|
|
17
|
+
@keydown=${(e) => this.onSuggestKeyDown(e)}>
|
|
18
|
+
${repeat(filteredRecentSearches.slice(0, 5), query => query, query => html `
|
|
19
|
+
<a href="${this.searchBar.searchRequestSerializer({
|
|
20
|
+
...this.searchBar.request,
|
|
21
|
+
query: query
|
|
22
|
+
})}"
|
|
23
|
+
part="suggestions"
|
|
24
|
+
class="ft-search-bar--suggestion ft-search-bar--recent-search"
|
|
25
|
+
@keyup=${(e) => this.onSuggestKeyUp(e, query)}
|
|
26
|
+
@click=${(e) => this.onSuggestClick(e, query)}>
|
|
27
|
+
<ft-ripple></ft-ripple>
|
|
28
|
+
<ft-icon variant="material" part="suggestion-icon">history</ft-icon>
|
|
29
|
+
<ft-typography variant="body1">${query}</ft-typography>
|
|
30
|
+
<ft-button icon="close"
|
|
31
|
+
round
|
|
32
|
+
part="remove-suggestion"
|
|
33
|
+
?dense=${!this.searchBar.isMobile}
|
|
34
|
+
label="${this.searchBar.labelResolver.resolve("removeRecentSearch")}"
|
|
35
|
+
tooltipPosition="left"
|
|
36
|
+
@click=${(e) => this.removeRecentSearch(e, query)}></ft-button>
|
|
37
|
+
</a>
|
|
38
|
+
`)}
|
|
39
|
+
${repeat(this.searchBar.suggestions, suggest => suggest.value, suggest => html `
|
|
40
|
+
<a href="${this.searchBar.searchRequestSerializer({
|
|
41
|
+
...this.searchBar.request,
|
|
42
|
+
query: suggest.value
|
|
43
|
+
})}"
|
|
44
|
+
part="suggestions"
|
|
45
|
+
class="ft-search-bar--suggestion"
|
|
46
|
+
@keyup=${(e) => this.onSuggestKeyUp(e, suggest.value)}
|
|
47
|
+
@click=${(e) => this.onSuggestClick(e, suggest.value)}>
|
|
48
|
+
<ft-ripple></ft-ripple>
|
|
49
|
+
${this.getIcon(suggest)}
|
|
50
|
+
<ft-typography variant="body1">${suggest.value}</ft-typography>
|
|
51
|
+
</a>
|
|
52
|
+
`)}
|
|
53
|
+
</div>
|
|
54
|
+
`;
|
|
55
|
+
}
|
|
56
|
+
update() {
|
|
57
|
+
return new Promise((accept, reject) => {
|
|
58
|
+
this.updateDebouncer.run(async () => {
|
|
59
|
+
this.searchBar.suggestions = this.searchBar.api && this.searchBar.query.length > 2
|
|
60
|
+
? await this.searchBar.api.getSuggestions(this.searchBar.suggestRequest).then(r => r.suggestions).catch(() => [])
|
|
61
|
+
: [];
|
|
62
|
+
accept();
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
onSuggestKeyDown(e) {
|
|
67
|
+
var _a, _b, _c, _d, _e, _f;
|
|
68
|
+
switch (e.key) {
|
|
69
|
+
case "ArrowUp":
|
|
70
|
+
(_c = ((_b = (_a = this.getFocusedSuggestionElement()) === null || _a === void 0 ? void 0 : _a.previousElementSibling) !== null && _b !== void 0 ? _b : this.getLastSuggestionElement())) === null || _c === void 0 ? void 0 : _c.focus();
|
|
71
|
+
e.preventDefault();
|
|
72
|
+
e.stopPropagation();
|
|
73
|
+
break;
|
|
74
|
+
case "ArrowDown":
|
|
75
|
+
(_f = ((_e = (_d = this.getFocusedSuggestionElement()) === null || _d === void 0 ? void 0 : _d.nextElementSibling) !== null && _e !== void 0 ? _e : this.getFirstSuggestionElement())) === null || _f === void 0 ? void 0 : _f.focus();
|
|
76
|
+
e.preventDefault();
|
|
77
|
+
e.stopPropagation();
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
onSuggestKeyUp(e, suggest) {
|
|
82
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
83
|
+
this.onSuggestSelected(e, suggest);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
onSuggestClick(e, suggest) {
|
|
87
|
+
if (!e.ctrlKey && !e.metaKey) {
|
|
88
|
+
this.onSuggestSelected(e, suggest);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
onSuggestSelected(e, suggest) {
|
|
92
|
+
e.preventDefault();
|
|
93
|
+
this.searchBar.setQuery(suggest);
|
|
94
|
+
this.searchBar.launchSearch();
|
|
95
|
+
}
|
|
96
|
+
removeRecentSearch(e, query) {
|
|
97
|
+
var _a, _b;
|
|
98
|
+
e.preventDefault();
|
|
99
|
+
e.stopPropagation();
|
|
100
|
+
let currentFocuseElement = e.target.closest(".ft-search-bar--suggestion");
|
|
101
|
+
const thingToFocus = (_b = (_a = currentFocuseElement === null || currentFocuseElement === void 0 ? void 0 : currentFocuseElement.previousElementSibling) !== null && _a !== void 0 ? _a : currentFocuseElement === null || currentFocuseElement === void 0 ? void 0 : currentFocuseElement.nextElementSibling) !== null && _b !== void 0 ? _b : this.searchBar.input;
|
|
102
|
+
thingToFocus === null || thingToFocus === void 0 ? void 0 : thingToFocus.focus();
|
|
103
|
+
this.searchBar.recentSearches = this.searchBar.recentSearches.filter(q => q.toLowerCase() !== query.toLowerCase());
|
|
104
|
+
this.searchBar.saveRecentSearches();
|
|
105
|
+
}
|
|
106
|
+
getIcon(suggest) {
|
|
107
|
+
const iconVariant = suggest.type === "DOCUMENT" ? FtIconVariants.file_format : FtIconVariants.fluid_topics;
|
|
108
|
+
let icon;
|
|
109
|
+
switch (suggest.type) {
|
|
110
|
+
case "MAP":
|
|
111
|
+
icon = suggest.editorialType === "BOOK" ? FtIcons.BOOK : FtIcons.ARTICLE;
|
|
112
|
+
break;
|
|
113
|
+
case "DOCUMENT":
|
|
114
|
+
icon = resolveFileFormatIcon(suggest.mimeType, suggest.filenameExtension);
|
|
115
|
+
break;
|
|
116
|
+
case "TOPIC":
|
|
117
|
+
icon = FtIcons.TOPICS;
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
return html `
|
|
121
|
+
<ft-icon variant="${iconVariant}" part="suggestion-icon">
|
|
122
|
+
${icon}
|
|
123
|
+
</ft-icon>
|
|
124
|
+
`;
|
|
125
|
+
}
|
|
126
|
+
getFocusedSuggestionElement() {
|
|
127
|
+
return this.searchBar.querySelector(".ft-search-bar--suggestion:focus-within");
|
|
128
|
+
}
|
|
129
|
+
getLastSuggestionElement() {
|
|
130
|
+
let suggestions = this.searchBar.querySelectorAll(".ft-search-bar--suggestion");
|
|
131
|
+
return suggestions.length > 0 ? suggestions[suggestions.length - 1] : null;
|
|
132
|
+
}
|
|
133
|
+
getFirstSuggestionElement() {
|
|
134
|
+
return this.searchBar.querySelector(".ft-search-bar--suggestion");
|
|
135
|
+
}
|
|
136
|
+
focusFirstSuggestion() {
|
|
137
|
+
var _a;
|
|
138
|
+
(_a = this.getFirstSuggestionElement()) === null || _a === void 0 ? void 0 : _a.focus();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
//language=css
|
|
142
|
+
SuggestManager.styles = css `
|
|
143
|
+
.ft-search-bar--mobile .ft-search-bar--suggestions {
|
|
144
|
+
flex-grow: 1;
|
|
145
|
+
flex-shrink: 1;
|
|
146
|
+
overflow-y: auto;
|
|
147
|
+
height: 0;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.ft-search-bar--mobile-menu-open .ft-search-bar--suggestions {
|
|
151
|
+
border-top: 1px solid ${FtSearchBarCssVariables.colorOutline};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.ft-search-bar--mobile-menu-open .ft-search-bar--suggestions {
|
|
155
|
+
height: initial;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.ft-search-bar--floating-panel,
|
|
159
|
+
.ft-search-bar--desktop .ft-search-bar--suggestions {
|
|
160
|
+
position: absolute;
|
|
161
|
+
z-index: ${FtSearchBarCssVariables.floatingZIndex};
|
|
162
|
+
top: 100%;
|
|
163
|
+
left: -1px;
|
|
164
|
+
right: -1px;
|
|
165
|
+
display: none;
|
|
166
|
+
background: ${FtSearchBarCssVariables.colorSurface};
|
|
167
|
+
border: 1px solid ${FtSearchBarCssVariables.colorOutline};
|
|
168
|
+
border-radius: 0 0 ${FtSearchBarCssVariables.borderRadius} ${FtSearchBarCssVariables.borderRadius};
|
|
169
|
+
box-shadow: ${FtSearchBarCssVariables.elevation02};
|
|
170
|
+
outline: none;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.ft-search-bar--desktop .ft-search-bar--suggestions {
|
|
174
|
+
top: calc(100% + 2px);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.ft-search-bar--input-container:focus-within .ft-search-bar--suggestions-not-empty {
|
|
178
|
+
display: block;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.ft-search-bar--suggestion {
|
|
182
|
+
text-decoration: none;
|
|
183
|
+
position: relative;
|
|
184
|
+
display: flex;
|
|
185
|
+
align-items: center;
|
|
186
|
+
padding: 8px;
|
|
187
|
+
gap: 8px;
|
|
188
|
+
cursor: pointer;
|
|
189
|
+
color: ${FtSearchBarCssVariables.colorOnSurface};
|
|
190
|
+
min-height: 52px;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.ft-search-bar--suggestion > *:not(ft-ripple) {
|
|
194
|
+
position: relative;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.ft-search-bar--desktop .ft-search-bar--suggestion {
|
|
198
|
+
min-height: 44px;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.ft-search-bar--suggestion:focus {
|
|
202
|
+
outline: none;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.ft-search-bar--recent-search + .ft-search-bar--suggestion:not(.ft-search-bar--recent-search) {
|
|
206
|
+
border-top: 1px solid ${FtSearchBarCssVariables.colorOutline};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.ft-search-bar--suggestion ft-typography {
|
|
210
|
+
display: block;
|
|
211
|
+
flex-grow: 1;
|
|
212
|
+
flex-shrink: 1;
|
|
213
|
+
}
|
|
214
|
+
`;
|
|
215
|
+
//# sourceMappingURL=SuggestManager.js.map
|