@fluid-topics/ft-filter 0.0.88

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.
@@ -0,0 +1,398 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { css, html } from "lit";
8
+ import { property, query, queryAll, state } from "lit/decorators.js";
9
+ import { customElement, Debouncer, FtLitElement } from "@fluid-topics/ft-wc-utils";
10
+ import { FtTypography, FtTypographyCaption } from "@fluid-topics/ft-typography";
11
+ import { flatDeep } from "./utils";
12
+ import { FtFilterLevel } from "./ft-filter-level";
13
+ import { FtButton } from "@fluid-topics/ft-button";
14
+ import { FtSnapScroll } from "@fluid-topics/ft-snap-scroll";
15
+ export * from "./ft-filter-option";
16
+ export class FtFilterChangeEvent extends CustomEvent {
17
+ constructor(selectedValues) {
18
+ super("change", { detail: selectedValues });
19
+ }
20
+ }
21
+ let FtFilter = class FtFilter extends FtLitElement {
22
+ constructor() {
23
+ super(...arguments);
24
+ this.id = "";
25
+ this.label = "";
26
+ this.filterPlaceHolder = "Filter {0}";
27
+ this.clearButtonLabel = "Clear";
28
+ this.moreValuesButtonLabel = "More";
29
+ this.options = [];
30
+ this.multivalued = false;
31
+ this.disabled = false;
32
+ this.raiseSelectedOptions = false;
33
+ this.displayedValuesLimit = 0;
34
+ this.withScroll = false;
35
+ this.filter = "";
36
+ this.displayedLevels = [];
37
+ this.scrollResizeObserver = new ResizeObserver(() => this.updateScroll());
38
+ this.levelsScrollDebouncer = new Debouncer(300);
39
+ }
40
+ // language=CSS
41
+ getStyles() {
42
+ return [
43
+ FtTypographyCaption,
44
+ css `
45
+ :host {
46
+ display: flex;
47
+ max-height: 100%;
48
+ max-width: 100%;
49
+ }
50
+
51
+ .ft-filter--container {
52
+ flex-grow: 1;
53
+ display: flex;
54
+ flex-direction: column;
55
+ max-height: 100%;
56
+ max-width: 100%;
57
+ background: var(--ft-color-surface, #FFFFFF);
58
+ }
59
+
60
+ .ft-filter--header {
61
+ display: flex;
62
+ flex-wrap: wrap;
63
+ gap: 4px;
64
+ flex-shrink: 0;
65
+ padding: 0 10px;
66
+ margin-bottom: 8px;
67
+ --ft-typography-font-size: 14px;
68
+ }
69
+
70
+ .ft-filter--header ft-button {
71
+ flex-shrink: 0;
72
+ margin-left: auto;
73
+ --ft-button-icon-size: 18px;
74
+ }
75
+
76
+ .ft-filter--label {
77
+ display: block;
78
+ flex-shrink: 1;
79
+ flex-grow: 1;
80
+ word-break: break-word;
81
+ --ft-typography-line-height: 22px;
82
+ --ft-typography-font-weight: bold;
83
+ }
84
+
85
+ .ft-filter--filter {
86
+ flex-shrink: 0;
87
+ display: flex;
88
+ margin: 0;
89
+ margin-bottom: 8px;
90
+ padding: 0 10px;
91
+ overflow: hidden;
92
+ height: 32px;
93
+ transition: height 250ms ease-in-out, margin 250ms ease-in-out;
94
+ transition-delay: 500ms;
95
+ }
96
+
97
+ .ft-filter--filter[hidden] {
98
+ height: 0;
99
+ margin-bottom: 0;
100
+ }
101
+
102
+ input {
103
+ display: block;
104
+ width: 100%;
105
+ border-radius: 4px;
106
+ border: 1px solid var(--ft-color-outline, rgba(0, 0, 0, 0.14));
107
+ padding: 4px;
108
+ outline-color: var(--ft-color-primary, #2196F3);
109
+ }
110
+
111
+ .ft-filter--values {
112
+ flex-grow: 1;
113
+ flex-shrink: 1;
114
+ max-width: 100%;
115
+ overflow-x: hidden;
116
+ overflow-y: auto;
117
+ }
118
+
119
+ .ft-filter--separator {
120
+ border-top: 1px solid var(--ft-color-outline, rgba(0, 0, 0, 0.14));
121
+ margin: 4px 10px;
122
+ }
123
+
124
+ .ft-filter--levels-container {
125
+ position: relative;
126
+ overflow: hidden;
127
+ }
128
+
129
+ .ft-filter--levels {
130
+ position: relative;
131
+ overflow: hidden;
132
+ }
133
+
134
+ ft-filter-level {
135
+ width: 100%;
136
+ }
137
+
138
+ .ft-filter--level-left {
139
+ height: 0;
140
+ }
141
+
142
+ slot {
143
+ display: none;
144
+ }
145
+ `
146
+ ];
147
+ }
148
+ get flatOptions() {
149
+ return flatDeep(this.options, o => { var _a; return (_a = o.subOptions) !== null && _a !== void 0 ? _a : []; });
150
+ }
151
+ getTemplate() {
152
+ var _a, _b;
153
+ const valuesSelected = this.flatOptions.some(o => o.selected);
154
+ let showFilter = this.withScroll || this.filter || ((_b = (_a = this.lastLevel) === null || _a === void 0 ? void 0 : _a.hasHiddenValues) !== null && _b !== void 0 ? _b : false);
155
+ return html `
156
+ <div class="ft-filter--container ${this.disabled ? "ft-filter--disabled" : ""}"
157
+ part="container">
158
+ ${!this.label && !valuesSelected ? null : html `
159
+ <div class="ft-filter--header" part="header">
160
+ <ft-typography class="ft-filter--label" variant="overline" part="label">${this.label}
161
+ </ft-typography>
162
+ ${valuesSelected ? html `
163
+ <ft-button icon="close" dense @click=${this.clear} part="clear-button">
164
+ ${this.clearButtonLabel}
165
+ </ft-button>
166
+ ` : null}
167
+ </div>
168
+ `}
169
+ <div class="ft-filter--filter" ?hidden=${!showFilter}>
170
+ <input type="search"
171
+ part="input"
172
+ class="ft-typography--caption"
173
+ ?disabled=${!showFilter}
174
+ placeholder="${this.filterPlaceHolder.replace("{0}", this.label)}"
175
+ @keyup=${this.onFilterChange}>
176
+ </div>
177
+ <div class="ft-filter--values">
178
+ ${this.renderLevels()}
179
+ </div>
180
+ <slot @slotchange=${this.updateOptionsFromSlot}></slot>
181
+ </div>
182
+ `;
183
+ }
184
+ update(props) {
185
+ super.update(props);
186
+ if (props.has("options")) {
187
+ const newValues = new Set(this.flatOptions.map(o => o.value));
188
+ this.displayedLevels = this.displayedLevels.filter(value => newValues.has(value));
189
+ }
190
+ }
191
+ contentAvailableCallback(props) {
192
+ var _a, _b;
193
+ super.contentAvailableCallback(props);
194
+ if (this.valuesContainer) {
195
+ this.scrollResizeObserver.observe(this.valuesContainer);
196
+ }
197
+ if (this.container) {
198
+ this.scrollResizeObserver.observe(this.container);
199
+ }
200
+ if (props.has("options")) {
201
+ this.updateScroll();
202
+ (_a = this.levelsContainer) === null || _a === void 0 ? void 0 : _a.scrollToIndex(this.displayedLevels.length);
203
+ }
204
+ if (props.has("slideIn") && this.slideIn) {
205
+ setTimeout(() => {
206
+ var _a;
207
+ (_a = this.levelsContainer) === null || _a === void 0 ? void 0 : _a.next();
208
+ this.slideIn = undefined;
209
+ }, 0);
210
+ }
211
+ if (props.has("slideOut") && this.slideOut) {
212
+ (_b = this.levelsContainer) === null || _b === void 0 ? void 0 : _b.previous();
213
+ setTimeout(() => {
214
+ this.displayedLevels.pop();
215
+ this.slideOut = undefined;
216
+ }, 300);
217
+ }
218
+ }
219
+ renderLevels() {
220
+ const allOptions = this.flatOptions;
221
+ const selectedOptions = allOptions.filter(o => o.selected);
222
+ const currentLevelIndex = (this.slideIn || this.slideOut) ? this.displayedLevels.length - 2 : this.displayedLevels.length - 1;
223
+ const rootOptionsClass = currentLevelIndex < 0 ? "ft-filter--level-center" : "ft-filter--level-left";
224
+ return html `
225
+ ${this.raiseSelectedOptions && selectedOptions.length > 0 ? html `
226
+ <ft-filter-level
227
+ id="${this.id}"
228
+ ?multivalued=${this.multivalued}
229
+ ?disabled=${this.disabled}
230
+ preventNavigation
231
+ .options=${selectedOptions}
232
+ @change=${this.onChange}
233
+ part="selected-values"
234
+ ></ft-filter-level>
235
+ <div class="ft-filter--separator">
236
+ ` : null}
237
+ <ft-snap-scroll horizontal hideScrollbar
238
+ class="ft-filter--levels"
239
+ @current-element-change=${(e) => this.levelsScrollDebouncer.run(() => {
240
+ while (e.detail.index < this.displayedLevels.length) {
241
+ this.displayedLevels.pop();
242
+ }
243
+ this.requestUpdate();
244
+ })}>
245
+ ${this.renderLevel(rootOptionsClass, this.options)}
246
+ ${this.displayedLevels
247
+ .map(optionValue => allOptions.find(o => o.value === optionValue))
248
+ .map((option, index) => {
249
+ var _a;
250
+ const className = (option === null || option === void 0 ? void 0 : option.value) === this.slideIn || (option === null || option === void 0 ? void 0 : option.value) === this.slideOut
251
+ ? "ft-filter--level-right"
252
+ : index === currentLevelIndex
253
+ ? "ft-filter--level-center"
254
+ : "ft-filter--level-left";
255
+ return this.renderLevel(className, (_a = option === null || option === void 0 ? void 0 : option.subOptions) !== null && _a !== void 0 ? _a : [], option);
256
+ })}
257
+ </ft-snap-scroll>
258
+ `;
259
+ }
260
+ renderLevel(className, options, parent) {
261
+ return html `
262
+ <ft-filter-level
263
+ class="${className}"
264
+ id="${this.id}"
265
+ filter="${this.filter}"
266
+ moreValuesButtonLabel="${this.moreValuesButtonLabel}"
267
+ ?multivalued=${this.multivalued}
268
+ ?disabled=${this.disabled || className !== "ft-filter--level-center"}
269
+ ?hideSelectedOptions=${this.raiseSelectedOptions}
270
+ .parent=${parent}
271
+ .options=${options}
272
+ .displayedValuesLimit=${this.displayedValuesLimit}
273
+ @change=${this.onChange}
274
+ @go-back=${this.goBack}
275
+ @display-level=${this.onDisplayLevel}
276
+ part="values"
277
+ ></ft-filter-level>
278
+ `;
279
+ }
280
+ goBack(e) {
281
+ this.slideOut = e.detail.value;
282
+ }
283
+ onDisplayLevel(e) {
284
+ this.displayedLevels.push(e.detail.value);
285
+ this.slideIn = e.detail.value;
286
+ }
287
+ clear() {
288
+ this.flatOptions.forEach(o => o.selected = false);
289
+ if (this.displayedLevels.length > 0) {
290
+ let currentLevel = this.displayedLevels[this.displayedLevels.length - 1];
291
+ this.displayedLevels = [currentLevel];
292
+ this.slideOut = currentLevel;
293
+ }
294
+ this.optionsChanged();
295
+ }
296
+ onChange(e) {
297
+ e.stopPropagation();
298
+ if (!this.multivalued) {
299
+ const clearOption = (o) => {
300
+ var _a;
301
+ if (o.value !== e.detail.value) {
302
+ o.selected = false;
303
+ }
304
+ (_a = o.subOptions) === null || _a === void 0 ? void 0 : _a.forEach(clearOption);
305
+ };
306
+ this.options.forEach(clearOption);
307
+ }
308
+ this.optionsChanged();
309
+ }
310
+ optionsChanged() {
311
+ var _a;
312
+ const selectedValues = this.flatOptions.filter(o => o.selected).map(o => o.value);
313
+ this.dispatchEvent(new FtFilterChangeEvent(selectedValues));
314
+ this.requestUpdate();
315
+ (_a = this.levels) === null || _a === void 0 ? void 0 : _a.forEach(l => l.requestUpdate());
316
+ }
317
+ updateOptionsFromSlot(e) {
318
+ const slot = e.composedPath()[0];
319
+ this.options = slot.assignedElements().map(n => n);
320
+ }
321
+ onFilterChange(e) {
322
+ const input = e.composedPath()[0];
323
+ this.filter = input.value;
324
+ }
325
+ updateScroll() {
326
+ if (this.valuesContainer) {
327
+ this.withScroll = this.valuesContainer.scrollHeight > this.valuesContainer.clientHeight;
328
+ }
329
+ }
330
+ };
331
+ FtFilter.elementDefinitions = {
332
+ "ft-button": FtButton,
333
+ "ft-filter-level": FtFilterLevel,
334
+ "ft-snap-scroll": FtSnapScroll,
335
+ "ft-typography": FtTypography,
336
+ };
337
+ __decorate([
338
+ property({ type: String })
339
+ ], FtFilter.prototype, "id", void 0);
340
+ __decorate([
341
+ property({ type: String })
342
+ ], FtFilter.prototype, "label", void 0);
343
+ __decorate([
344
+ property({ type: String })
345
+ ], FtFilter.prototype, "filterPlaceHolder", void 0);
346
+ __decorate([
347
+ property({ type: String })
348
+ ], FtFilter.prototype, "clearButtonLabel", void 0);
349
+ __decorate([
350
+ property({ type: String })
351
+ ], FtFilter.prototype, "moreValuesButtonLabel", void 0);
352
+ __decorate([
353
+ property({ type: Array })
354
+ ], FtFilter.prototype, "options", void 0);
355
+ __decorate([
356
+ property({ type: Boolean })
357
+ ], FtFilter.prototype, "multivalued", void 0);
358
+ __decorate([
359
+ property({ type: Boolean })
360
+ ], FtFilter.prototype, "disabled", void 0);
361
+ __decorate([
362
+ property({ type: Boolean })
363
+ ], FtFilter.prototype, "raiseSelectedOptions", void 0);
364
+ __decorate([
365
+ property({ type: Number })
366
+ ], FtFilter.prototype, "displayedValuesLimit", void 0);
367
+ __decorate([
368
+ query(".ft-filter--container")
369
+ ], FtFilter.prototype, "container", void 0);
370
+ __decorate([
371
+ query(".ft-filter--values")
372
+ ], FtFilter.prototype, "valuesContainer", void 0);
373
+ __decorate([
374
+ query(".ft-filter--levels")
375
+ ], FtFilter.prototype, "levelsContainer", void 0);
376
+ __decorate([
377
+ query(".ft-filter--levels ft-filter-level:last-child")
378
+ ], FtFilter.prototype, "lastLevel", void 0);
379
+ __decorate([
380
+ queryAll(".ft-filter--levels ft-filter-level")
381
+ ], FtFilter.prototype, "levels", void 0);
382
+ __decorate([
383
+ state()
384
+ ], FtFilter.prototype, "withScroll", void 0);
385
+ __decorate([
386
+ state()
387
+ ], FtFilter.prototype, "filter", void 0);
388
+ __decorate([
389
+ state()
390
+ ], FtFilter.prototype, "slideIn", void 0);
391
+ __decorate([
392
+ state()
393
+ ], FtFilter.prototype, "slideOut", void 0);
394
+ FtFilter = __decorate([
395
+ customElement("ft-filter")
396
+ ], FtFilter);
397
+ export { FtFilter };
398
+ //# sourceMappingURL=ft-filter.js.map