Skip to content

Commit 32ad112

Browse files
feat(components): type definitions
- Added type definition for generic component (base class); - Added type definition for static and virtual class methods; - Added type definitions for object options; - Added overload definitions to "init" methods; - Added initial version for code docs (props); - Dropped support to jQuery selector in "getInstance" method; - Fixed "Collapsible" destructor.
1 parent f5057d6 commit 32ad112

24 files changed

+2230
-628
lines changed

src/autocomplete.ts

+139-27
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,74 @@
1-
import { Component } from "./component";
21
import { M } from "./global";
3-
import { Dropdown } from "./dropdown";
2+
import { Dropdown, DropdownOptions } from "./dropdown";
3+
import { Component, BaseOptions, InitElements } from "./component";
4+
5+
export interface AutocompleteData {
6+
/**
7+
* A primitive value that can be converted to string.
8+
* If "text" is not provided, it will also be used as "option text" as well
9+
*/
10+
id: string | number;
11+
/**
12+
* This optional attribute is used as "display value" for the current entry.
13+
* When provided, it will also be taken into consideration by the standard search function.
14+
*/
15+
text?: string;
16+
/**
17+
* This optional attribute is used to provide a valid image URL to the current option.
18+
*/
19+
image?: string;
20+
/**
21+
* Optional attributes which describes the option.
22+
*/
23+
description?: string;
24+
}
25+
26+
export interface AutocompleteOptions extends BaseOptions {
27+
/**
28+
* Data object defining autocomplete options with
29+
* optional icon strings.
30+
*/
31+
data: AutocompleteData[];
32+
/**
33+
* Flag which can be set if multiple values can be selected. The Result will be an Array.
34+
* @default false
35+
*/
36+
isMultiSelect: boolean;
37+
/**
38+
* Callback for when autocompleted.
39+
*/
40+
onAutocomplete: (entries: AutocompleteData[]) => void;
41+
/**
42+
* Minimum number of characters before autocomplete starts.
43+
* @default 1
44+
*/
45+
minLength: number;
46+
/**
47+
* The height of the Menu which can be set via css-property.
48+
* @default '300px'
49+
*/
50+
maxDropDownHeight: string;
51+
/**
52+
* Function is called when the input text is altered and data can also be loaded asynchronously.
53+
* If the results are collected the items in the list can be updated via the function setMenuItems(collectedItems).
54+
* @param text Searched text.
55+
* @param autocomplete Current autocomplete instance.
56+
*/
57+
onSearch: (text: string, autocomplete: Autocomplete) => void;
58+
/**
59+
* If true will render the key from each item directly as HTML.
60+
* User input MUST be properly sanitized first.
61+
* @default false
62+
*/
63+
allowUnsafeHTML: boolean;
64+
/**
65+
* Pass options object to select dropdown initialization.
66+
* @default {}
67+
*/
68+
dropdownOptions: Partial<DropdownOptions>;
69+
};
470

5-
let _defaults = {
71+
let _defaults: AutocompleteOptions = {
672
data: [], // Autocomplete data set
773
onAutocomplete: null, // Callback for when autocompleted
874
dropdownOptions: {
@@ -26,45 +92,73 @@ let _defaults = {
2692
};
2793

2894

29-
export class Autocomplete extends Component {
30-
el: HTMLInputElement;
95+
export class Autocomplete extends Component<AutocompleteOptions> {
96+
declare el: HTMLInputElement;
97+
/** If the autocomplete is open. */
3198
isOpen: boolean;
99+
/** Number of matching autocomplete options. */
32100
count: number;
101+
/** Index of the current selected option. */
33102
activeIndex: number;
34-
private oldVal: any;
103+
private oldVal: string;
35104
private $active: HTMLElement|null;
36105
private _mousedown: boolean;
37106
container: HTMLElement;
107+
/** Instance of the dropdown plugin for this autocomplete. */
38108
dropdown: Dropdown;
39109
static _keydown: boolean;
40-
selectedValues: any[];
41-
menuItems: any[];
110+
selectedValues: AutocompleteData[];
111+
menuItems: AutocompleteData[];
42112

43113

44-
constructor(el, options) {
45-
super(Autocomplete, el, options);
114+
constructor(el: HTMLElement, options: Partial<AutocompleteOptions>) {
115+
super(el, options, Autocomplete);
46116
(this.el as any).M_Autocomplete = this;
47-
this.options = {...Autocomplete.defaults, ...options};
117+
118+
this.options = {
119+
...Autocomplete.defaults,
120+
...options
121+
};
122+
48123
this.isOpen = false;
49124
this.count = 0;
50125
this.activeIndex = -1;
51-
this.oldVal;
126+
this.oldVal = "";
52127
this.selectedValues = [];
53128
this.menuItems = [];
54129
this.$active = null;
55130
this._mousedown = false;
56131
this._setupDropdown();
57132
this._setupEventHandlers();
58133
}
59-
static get defaults() {
134+
135+
static get defaults(): AutocompleteOptions {
60136
return _defaults;
61137
}
62-
static init(els, options) {
63-
return super.init(this, els, options);
138+
139+
/**
140+
* Initializes instance of Autocomplete.
141+
* @param el HTML element.
142+
* @param options Component options.
143+
*/
144+
static init(el: HTMLElement, options: Partial<AutocompleteOptions>): Autocomplete;
145+
/**
146+
* Initializes instances of Autocomplete.
147+
* @param els HTML elements.
148+
* @param options Component options.
149+
*/
150+
static init(els: InitElements<HTMLElement>, options: Partial<AutocompleteOptions>): Autocomplete[];
151+
/**
152+
* Initializes instances of Autocomplete.
153+
* @param els HTML elements.
154+
* @param options Component options.
155+
*/
156+
static init(els: HTMLElement | InitElements<HTMLElement>, options: Partial<AutocompleteOptions>): Autocomplete | Autocomplete[] {
157+
return super.init(els, options, Autocomplete);
64158
}
65-
static getInstance(el) {
66-
let domElem = el.jquery ? el[0] : el;
67-
return domElem.M_Autocomplete;
159+
160+
static getInstance(el: HTMLElement): Autocomplete {
161+
return (el as any).M_Autocomplete;
68162
}
69163

70164
destroy() {
@@ -253,7 +347,7 @@ export class Autocomplete extends Component {
253347
this._mousedown = false;
254348
}
255349

256-
_highlightPartialText(input, label) {
350+
_highlightPartialText(input: string, label: string) {
257351
const start = label.toLowerCase().indexOf('' + input.toLowerCase() + '');
258352
const end = start + input.length - 1;
259353
//custom filters may return results where the string does not match any part
@@ -263,9 +357,9 @@ export class Autocomplete extends Component {
263357
return [label.slice(0, start), label.slice(start, end + 1), label.slice(end + 1)];
264358
}
265359

266-
_createDropdownItem(entry) {
360+
_createDropdownItem(entry: AutocompleteData) {
267361
const item = document.createElement('li');
268-
item.setAttribute('data-id', entry.id);
362+
item.setAttribute('data-id', <string>entry.id);
269363
item.setAttribute(
270364
'style',
271365
'display:grid; grid-auto-flow: column; user-select: none; align-items: center;'
@@ -366,7 +460,7 @@ export class Autocomplete extends Component {
366460
_refreshInputText() {
367461
if (this.selectedValues.length === 1) {
368462
const entry = this.selectedValues[0];
369-
this.el.value = entry.text || entry.id; // Write Text to Input
463+
this.el.value = entry.text || <string>entry.id; // Write Text to Input
370464
}
371465
}
372466

@@ -377,7 +471,10 @@ export class Autocomplete extends Component {
377471
this.options.onAutocomplete.call(this, this.selectedValues);
378472
}
379473

380-
open() {
474+
/**
475+
* Show autocomplete.
476+
*/
477+
open = () => {
381478
const inputText = this.el.value.toLowerCase();
382479
this._resetAutocomplete();
383480
if (inputText.length >= this.options.minLength) {
@@ -393,17 +490,28 @@ export class Autocomplete extends Component {
393490
else this.dropdown.recalculateDimensions(); // Recalculate dropdown when its already open
394491
}
395492

396-
close() {
493+
/**
494+
* Hide autocomplete.
495+
*/
496+
close = () => {
397497
this.dropdown.close();
398498
}
399499

400-
setMenuItems(menuItems) {
500+
/**
501+
* Updates the visible or selectable items shown in the menu.
502+
* @param menuItems Items to be available.
503+
*/
504+
setMenuItems(menuItems: AutocompleteData[]) {
401505
this.menuItems = menuItems;
402506
this.open();
403507
this._updateSelectedInfo();
404508
}
405509

406-
setValues(entries) {
510+
/**
511+
* Sets selected values.
512+
* @param entries
513+
*/
514+
setValues(entries: AutocompleteData[]) {
407515
this.selectedValues = entries;
408516
this._updateSelectedInfo();
409517
if (!this.options.isMultiSelect) {
@@ -412,7 +520,11 @@ export class Autocomplete extends Component {
412520
this._triggerChanged();
413521
}
414522

415-
selectOption(id) {
523+
/**
524+
* Select a specific autocomplete option via id-property.
525+
* @param id The id of a data-entry.
526+
*/
527+
selectOption(id: number | string) {
416528
const entry = this.menuItems.find((item) => item.id == id);
417529
if (!entry) return;
418530
// Toggle Checkbox

0 commit comments

Comments
 (0)