@fluid-topics/ft-text-field 1.2.28 → 1.2.29

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,2 @@
1
+ import { FtTextFieldSuggestionDefinition } from "./models";
2
+ export declare function basicSuggestionsProvider(values: Array<string | FtTextFieldSuggestionDefinition>, maxSuggest?: number): (query: string) => FtTextFieldSuggestionDefinition[];
@@ -0,0 +1,18 @@
1
+ export function basicSuggestionsProvider(values, maxSuggest = 20) {
2
+ return (query) => values.map(value => typeof value == "string" ? ({ value: value, label: value }) : value)
3
+ .filter(v => v.label.toLowerCase().includes(query.toLowerCase()))
4
+ .sort((a, b) => alphabeticalSortWithPriorToStartWithQuery(a.label, b.label, query))
5
+ .slice(0, maxSuggest);
6
+ }
7
+ function alphabeticalSortWithPriorToStartWithQuery(a, b, query) {
8
+ const lowerA = a.toLowerCase();
9
+ const lowerB = b.toLowerCase();
10
+ const lowerQuery = query.toLowerCase();
11
+ if (lowerA.startsWith(lowerQuery) && !lowerB.includes(lowerQuery)) {
12
+ return -1;
13
+ }
14
+ if (lowerB.startsWith(lowerQuery) && !lowerA.startsWith(lowerQuery)) {
15
+ return 1;
16
+ }
17
+ return lowerA.localeCompare(lowerB);
18
+ }
@@ -2,6 +2,7 @@ import { PropertyValues } from "lit";
2
2
  import { ElementDefinitionsMap, FtLitElement } from "@fluid-topics/ft-wc-utils";
3
3
  import { FtTextFieldProperties } from "./ft-text-field.properties";
4
4
  import { FtTextFieldSuggestion } from "./ft-text-field-suggestion";
5
+ import { FtTextFieldSuggestionDefinition } from "./models";
5
6
  declare const FtTextField_base: import("@fluid-topics/ft-wc-utils").FtFormComponentType<typeof FtLitElement>;
6
7
  export declare class FtTextField extends FtTextField_base implements FtTextFieldProperties {
7
8
  static elementDefinitions: ElementDefinitionsMap;
@@ -29,22 +30,26 @@ export declare class FtTextField extends FtTextField_base implements FtTextField
29
30
  maxLength?: number;
30
31
  password: boolean;
31
32
  autocomplete?: string;
33
+ suggestionsProvider?: (query: string) => FtTextFieldSuggestionDefinition[] | Promise<FtTextFieldSuggestionDefinition[]>;
32
34
  focused: boolean;
33
35
  hidePassword: boolean;
34
36
  hideSuggestions: boolean;
35
37
  visibleSuggestions: FtTextFieldSuggestion[];
38
+ providedSuggestions: FtTextFieldSuggestionDefinition[];
36
39
  mainPanel?: HTMLElement;
37
40
  input?: HTMLInputElement;
38
41
  newValueSuggestion?: FtTextFieldSuggestion;
39
42
  suggestionsContainer?: HTMLElement;
40
- suggestions: FtTextFieldSuggestion[];
43
+ providedSuggestionsInDom: FtTextFieldSuggestion[];
44
+ slottedSuggestions: FtTextFieldSuggestion[];
41
45
  focus(): void;
42
- constructor();
43
46
  protected render(): import("lit").TemplateResult<1>;
44
47
  private resolveInputType;
48
+ private renderSuggestions;
45
49
  private renderPasswordIcon;
46
50
  private renderIcon;
47
51
  protected update(props: PropertyValues): void;
52
+ private updateSuggestions;
48
53
  private filterSuggestionsIfNeeded;
49
54
  protected contentAvailableCallback(props: PropertyValues): void;
50
55
  private watchAutofillInterval?;
@@ -62,5 +67,6 @@ export declare class FtTextField extends FtTextField_base implements FtTextField
62
67
  private togglePasswordVisibility;
63
68
  private isPasswordField;
64
69
  private setSuggestionPosition;
70
+ private suggestionsShouldBeDisplayed;
65
71
  }
66
72
  export {};
@@ -5,7 +5,8 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
7
  import { html, nothing } from "lit";
8
- import { property, query, queryAssignedElements, state } from "lit/decorators.js";
8
+ import { property, query, queryAll, queryAssignedElements, state } from "lit/decorators.js";
9
+ import { repeat } from "lit/directives/repeat.js";
9
10
  import { classMap } from "lit/directives/class-map.js";
10
11
  import { ifDefined } from "lit/directives/if-defined.js";
11
12
  import { computeFlipOffsetPosition, FtLitElement, noTextInputDefaultClearButton, toFtFormComponent } from "@fluid-topics/ft-wc-utils";
@@ -16,6 +17,25 @@ import { FtIcon, FtIcons } from "@fluid-topics/ft-icon";
16
17
  import { FtTextFieldCssVariables, styles } from "./ft-text-field.styles";
17
18
  import { FtTextFieldSuggestion } from "./ft-text-field-suggestion";
18
19
  class FtTextField extends toFtFormComponent(FtLitElement, "textbox") {
20
+ constructor() {
21
+ super(...arguments);
22
+ this._value = "";
23
+ this.dispatchedValue = "";
24
+ this.outlined = false;
25
+ this.disabled = false;
26
+ this.error = false;
27
+ this.fixedMenuPosition = false;
28
+ this.prefix = null;
29
+ this.passwordHiddenIcon = FtIcons.EYE_SLASH;
30
+ this.passwordRevealedIcon = FtIcons.EYE;
31
+ this.filterSuggestions = false;
32
+ this.password = false;
33
+ this.focused = false;
34
+ this.hidePassword = true;
35
+ this.hideSuggestions = false;
36
+ this.visibleSuggestions = [];
37
+ this.providedSuggestions = [];
38
+ }
19
39
  /*
20
40
  * We prevent lit from creating setter and getter to differentiate
21
41
  * when the value change comes from inside or outside the component
@@ -40,24 +60,6 @@ class FtTextField extends toFtFormComponent(FtLitElement, "textbox") {
40
60
  var _a;
41
61
  (_a = this.input) === null || _a === void 0 ? void 0 : _a.focus();
42
62
  }
43
- constructor() {
44
- super();
45
- this._value = "";
46
- this.dispatchedValue = "";
47
- this.outlined = false;
48
- this.disabled = false;
49
- this.error = false;
50
- this.fixedMenuPosition = false;
51
- this.prefix = null;
52
- this.passwordHiddenIcon = FtIcons.EYE_SLASH;
53
- this.passwordRevealedIcon = FtIcons.EYE;
54
- this.filterSuggestions = false;
55
- this.password = false;
56
- this.focused = false;
57
- this.hidePassword = true;
58
- this.hideSuggestions = false;
59
- this.visibleSuggestions = [];
60
- }
61
63
  render() {
62
64
  const classes = {
63
65
  "ft-text-field": true,
@@ -69,11 +71,14 @@ class FtTextField extends toFtFormComponent(FtLitElement, "textbox") {
69
71
  "ft-text-field--in-error": this.error,
70
72
  "ft-text-field--fixed": this.fixedMenuPosition,
71
73
  "ft-text-field--with-prefix": !!this.prefix,
72
- "ft-text-field--hide-suggestions": this.hideSuggestions || this.visibleSuggestions.length == 0,
74
+ "ft-text-field--hide-suggestions": !this.suggestionsShouldBeDisplayed(),
73
75
  "ft-text-field--raised-label": this.focused || this.value != "",
74
76
  "ft-text-field--with-icon": !!this.icon,
75
77
  "ft-text-field--with-password": this.isPasswordField()
76
78
  };
79
+ const suggestionsAreProvidedAndQueryNotInPossiblesValues = this.suggestionsProvider && this.value && this.value != "" && !this.providedSuggestions.map(p => p.label).includes(this.value);
80
+ const suggestionsFromSlotAndQueryNotEmpty = !this.suggestionsProvider && this.filterSuggestions && this.value && this.value != "";
81
+ const shouldDisplayQueryAsNewSuggestion = suggestionsFromSlotAndQueryNotEmpty || suggestionsAreProvidedAndQueryNotInPossiblesValues;
77
82
  return html `
78
83
  <div class="${classMap(classes)}">
79
84
  <div class="ft-text-field--main-panel"
@@ -109,12 +114,14 @@ class FtTextField extends toFtFormComponent(FtLitElement, "textbox") {
109
114
  </div>
110
115
  <div class="ft-text-field--suggestions"
111
116
  @suggestion-selected=${this.onSuggestionSelected}>
112
- <slot @slotchange=${() => this.filterSuggestionsIfNeeded()}></slot>
113
- ${this.filterSuggestions && (this.value && this.value != "") ? html `
114
- <ft-text-field-suggestion class="ft-text-field-suggestion--new-value" helper="${this.suggestionsHelper}">
115
- ${this.value}
116
- </ft-text-field-suggestion>
117
- ` : nothing}
117
+ ${this.renderSuggestions()}
118
+ ${shouldDisplayQueryAsNewSuggestion
119
+ ? html `
120
+ <ft-text-field-suggestion class="ft-text-field-suggestion--new-value" helper="${this.suggestionsHelper}">
121
+ ${this.value}
122
+ </ft-text-field-suggestion>
123
+ `
124
+ : nothing}
118
125
  </div>
119
126
  </div>
120
127
  ${this.helper ? html `
@@ -132,6 +139,25 @@ class FtTextField extends toFtFormComponent(FtLitElement, "textbox") {
132
139
  }
133
140
  return (_a = this.type) !== null && _a !== void 0 ? _a : "text";
134
141
  }
142
+ renderSuggestions() {
143
+ if (this.suggestionsProvider) {
144
+ return html `
145
+ ${repeat(this.providedSuggestions, suggestion => html `
146
+ <ft-text-field-suggestion value=${suggestion.value} @click=${suggestion.clickHandler}>
147
+ ${suggestion.icon ? html `
148
+ <ft-icon .value=${suggestion.icon}></ft-icon>
149
+ `
150
+ : nothing}
151
+ ${suggestion.label}
152
+ </ft-text-field-suggestion>`)}
153
+ `;
154
+ }
155
+ else {
156
+ return html `
157
+ <slot @slotchange=${() => this.filterSuggestionsIfNeeded()}></slot>
158
+ `;
159
+ }
160
+ }
135
161
  renderPasswordIcon() {
136
162
  return html `
137
163
  <ft-icon class="ft-text-field--icon"
@@ -153,8 +179,11 @@ class FtTextField extends toFtFormComponent(FtLitElement, "textbox") {
153
179
  }
154
180
  update(props) {
155
181
  super.update(props);
156
- if (props.has("value") || props.has("filterSuggestions")) {
157
- this.filterSuggestionsIfNeeded();
182
+ if (props.has("value")
183
+ || props.has("filterSuggestions")
184
+ || props.has("suggestionsProvider")
185
+ || props.has("hideSuggestions")) {
186
+ this.updateSuggestions();
158
187
  }
159
188
  if (props.has("value") && props.get("value") != null) {
160
189
  this.hideSuggestions = false;
@@ -162,17 +191,22 @@ class FtTextField extends toFtFormComponent(FtLitElement, "textbox") {
162
191
  if (props.has("dispatchedValue") && props.get("dispatchedValue") != null) {
163
192
  this.hideSuggestions = true;
164
193
  }
165
- if (props.has("visibleSuggestions") && this.visibleSuggestions.length) {
166
- this.setSuggestionPosition();
194
+ }
195
+ async updateSuggestions() {
196
+ if (this.suggestionsProvider) {
197
+ this.providedSuggestions = await this.suggestionsProvider(this.value);
198
+ }
199
+ else {
200
+ this.filterSuggestionsIfNeeded();
167
201
  }
168
202
  }
169
203
  filterSuggestionsIfNeeded() {
170
204
  if (this.filterSuggestions) {
171
- this.suggestions.forEach(s => s.hidden = !s.getValue().toLowerCase().includes(this.value.toLowerCase()));
172
- this.visibleSuggestions = this.suggestions.filter(s => !s.hidden);
205
+ this.slottedSuggestions.forEach(s => s.hidden = !s.getValue().toLowerCase().includes(this.value.toLowerCase()));
206
+ this.visibleSuggestions = this.slottedSuggestions.filter(s => !s.hidden);
173
207
  }
174
208
  else {
175
- this.visibleSuggestions = this.suggestions;
209
+ this.visibleSuggestions = this.slottedSuggestions;
176
210
  }
177
211
  if (this.newValueSuggestion) {
178
212
  this.visibleSuggestions = [...this.visibleSuggestions, this.newValueSuggestion];
@@ -184,9 +218,15 @@ class FtTextField extends toFtFormComponent(FtLitElement, "textbox") {
184
218
  if (props.has("focused") && !this.hideSuggestions && this.visibleSuggestions.length > 0) {
185
219
  this.setSuggestionPosition();
186
220
  }
221
+ if (props.has("value") && this.suggestionsProvider) {
222
+ this.visibleSuggestions = [...this.providedSuggestionsInDom];
223
+ }
187
224
  if (props.has("autocomplete")) {
188
225
  this.setupWatchAutofill();
189
226
  }
227
+ if (this.suggestionsShouldBeDisplayed()) {
228
+ this.setSuggestionPosition();
229
+ }
190
230
  }
191
231
  setupWatchAutofill() {
192
232
  var _a;
@@ -283,6 +323,9 @@ class FtTextField extends toFtFormComponent(FtLitElement, "textbox") {
283
323
  this.suggestionsContainer.style.top = `${y}px`;
284
324
  });
285
325
  }
326
+ suggestionsShouldBeDisplayed() {
327
+ return !this.hideSuggestions && (this.visibleSuggestions.length || this.providedSuggestions.length);
328
+ }
286
329
  }
287
330
  FtTextField.elementDefinitions = {
288
331
  "ft-input-label": FtInputLabel,
@@ -357,6 +400,9 @@ __decorate([
357
400
  __decorate([
358
401
  property({ type: String })
359
402
  ], FtTextField.prototype, "autocomplete", void 0);
403
+ __decorate([
404
+ property({ attribute: false })
405
+ ], FtTextField.prototype, "suggestionsProvider", void 0);
360
406
  __decorate([
361
407
  state()
362
408
  ], FtTextField.prototype, "focused", void 0);
@@ -369,6 +415,9 @@ __decorate([
369
415
  __decorate([
370
416
  state()
371
417
  ], FtTextField.prototype, "visibleSuggestions", void 0);
418
+ __decorate([
419
+ state()
420
+ ], FtTextField.prototype, "providedSuggestions", void 0);
372
421
  __decorate([
373
422
  query(".ft-text-field--main-panel")
374
423
  ], FtTextField.prototype, "mainPanel", void 0);
@@ -381,7 +430,10 @@ __decorate([
381
430
  __decorate([
382
431
  query(".ft-text-field--suggestions")
383
432
  ], FtTextField.prototype, "suggestionsContainer", void 0);
433
+ __decorate([
434
+ queryAll("ft-text-field-suggestion")
435
+ ], FtTextField.prototype, "providedSuggestionsInDom", void 0);
384
436
  __decorate([
385
437
  queryAssignedElements({ selector: "ft-text-field-suggestion" })
386
- ], FtTextField.prototype, "suggestions", void 0);
438
+ ], FtTextField.prototype, "slottedSuggestions", void 0);
387
439
  export { FtTextField };