smt-select 0.1.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Samet Acar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,99 @@
1
+ # SmtSelect 🚀
2
+
3
+ A high-performance, lightweight, and customizable **Angular Select Component** with built-in Virtual Scroll and Search capabilities.
4
+
5
+ ## ✨ Features
6
+
7
+ - **🔍 Searchable**: Quickly filter through thousands of options.
8
+ - **⚡ Virtual Scroll**: Built-in support for high-performance rendering of large datasets.
9
+ - **✅ Multi-Select**: Support for multiple selection out of the box.
10
+ - **🎨 Custom Styling**: Fully customizable via SCSS tokens.
11
+ - **📱 Responsive**: Works seamlessly across mobile and desktop.
12
+ - **🛡️ Type Safe**: Developed with strict TypeScript.
13
+
14
+ ---
15
+
16
+ ## 🚀 Installation
17
+
18
+ Install the package via npm:
19
+
20
+ ```bash
21
+ npm install smt-select
22
+ ```
23
+
24
+ ---
25
+
26
+ ## 📦 Usage
27
+
28
+ ### 1. Component Logic (app.component.ts)
29
+ ```typescript
30
+ import { SmtSelectComponent, SmtSelectOption, SmtSelectConfig } from 'smt-select';
31
+
32
+ @Component({
33
+ standalone: true,
34
+ imports: [SmtSelectComponent],
35
+ selector: 'app-root',
36
+ templateUrl: './app.component.html'
37
+ })
38
+ export class AppComponent {
39
+ myOptions: SmtSelectOption[] = [
40
+ { value: 1, label: 'Option 1' },
41
+ { value: 2, label: 'Option 2' },
42
+ { value: 3, label: 'Option 3' }
43
+ ];
44
+
45
+ selectConfig: SmtSelectConfig = {
46
+ placeholder: 'Choose an item...',
47
+ virtualScroll: true,
48
+ isMultiSelect: false
49
+ };
50
+
51
+ onSelection(value: any) {
52
+ console.log('Selected value:', value);
53
+ }
54
+ }
55
+ ```
56
+
57
+ ### 2. Template (app.component.html)
58
+ ```html
59
+ <smt-select
60
+ [options]="myOptions"
61
+ [config]="selectConfig"
62
+ [(selectedValue)]="currentValue"
63
+ (selectionChange)="onSelection($event)">
64
+ </smt-select>
65
+ ```
66
+
67
+ ---
68
+
69
+ ## ⚙️ API Reference
70
+
71
+ ### Inputs
72
+ | Property | Type | Default | Description |
73
+ |----------|------|---------|-------------|
74
+ | `options` | `SmtSelectOption[]` | `[]` | Array of options to display. |
75
+ | `config` | `SmtSelectConfig` | `{}` | Configuration object for the component. |
76
+ | `selectedValue` | `any \| any[]` | `null` | The currently selected value(s). Supports two-way binding. |
77
+ | `errorMessage` | `string \| null` | `undefined` | Error message to display below the component. |
78
+ | `isInvalid` | `boolean` | `false` | Manually trigger error state if `errorMessage` is not provided. |
79
+ | `visibility` | `SmtVisibilityType` | `ENABLED` | Controls accessibility (`ENABLED`, `READONLY`, `HIDDEN`). |
80
+
81
+ ### Outputs
82
+ | Event | Payload | Description |
83
+ |-------|---------|-------------|
84
+ | `selectionChange` | `any \| any[]` | Fired when the selected value changes. |
85
+ | `pocketOpen` | `boolean` | Fired when the dropdown is opened or closed. |
86
+
87
+ ### Configuration (`SmtSelectConfig`)
88
+ | Property | Type | Description |
89
+ |----------|------|-------------|
90
+ | `fieldID` | `string \| number` | Unique ID for the wrapper element. |
91
+ | `placeholder` | `string` | Text to show when no value is selected. |
92
+ | `virtualScroll` | `boolean` | Enable/Disable CDK Virtual Scroll for large datasets. |
93
+ | `isMultiSelect` | `boolean` | Enable multiple item selection. |
94
+
95
+ ---
96
+
97
+ ## 📄 License
98
+
99
+ MIT © [Samet Acar](LICENSE)
@@ -0,0 +1,229 @@
1
+ import * as i0 from '@angular/core';
2
+ import { Pipe, EventEmitter, ViewChild, Output, Input, Component } from '@angular/core';
3
+ import * as i1 from '@angular/common';
4
+ import { CommonModule } from '@angular/common';
5
+ import * as i2 from '@angular/forms';
6
+ import { FormsModule } from '@angular/forms';
7
+ import * as i3 from '@angular/cdk/scrolling';
8
+ import { ScrollingModule } from '@angular/cdk/scrolling';
9
+ import { timer, take } from 'rxjs';
10
+
11
+ var SmtVisibilityType;
12
+ (function (SmtVisibilityType) {
13
+ SmtVisibilityType["HIDDEN"] = "HIDDEN";
14
+ SmtVisibilityType["READONLY"] = "READONLY";
15
+ SmtVisibilityType["ENABLED"] = "ENABLED";
16
+ })(SmtVisibilityType || (SmtVisibilityType = {}));
17
+
18
+ class SmtSelectSearchFilterPipe {
19
+ transform(options, searchText) {
20
+ if (!options)
21
+ return [];
22
+ if (!searchText)
23
+ return options;
24
+ searchText = searchText.toLowerCase();
25
+ return options.filter(option => {
26
+ return option.label.toLowerCase().includes(searchText) ||
27
+ (typeof option.value === 'string' && option.value.toLowerCase().includes(searchText));
28
+ });
29
+ }
30
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SmtSelectSearchFilterPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
31
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.0.6", ngImport: i0, type: SmtSelectSearchFilterPipe, isStandalone: true, name: "smtSelectSearchFilter" });
32
+ }
33
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SmtSelectSearchFilterPipe, decorators: [{
34
+ type: Pipe,
35
+ args: [{
36
+ name: 'smtSelectSearchFilter',
37
+ standalone: true,
38
+ pure: true
39
+ }]
40
+ }] });
41
+
42
+ class SmtSelectComponent {
43
+ el;
44
+ renderer;
45
+ options = [];
46
+ config = {};
47
+ selectedValue = null;
48
+ errorMessage;
49
+ isInvalid = false;
50
+ visibility = SmtVisibilityType.ENABLED;
51
+ selectionChange = new EventEmitter();
52
+ pocketOpen = new EventEmitter();
53
+ pocketElement;
54
+ inputElement;
55
+ isPocketOpen = false;
56
+ isOpenReverse = false;
57
+ searchText = '';
58
+ clickListener = null;
59
+ VisibilityType = SmtVisibilityType;
60
+ constructor(el, renderer) {
61
+ this.el = el;
62
+ this.renderer = renderer;
63
+ }
64
+ ngOnDestroy() {
65
+ this.removeOutsideClickListener();
66
+ }
67
+ get hasSelection() {
68
+ if (this.config.isMultiSelect) {
69
+ return Array.isArray(this.selectedValue) && this.selectedValue.length > 0;
70
+ }
71
+ return this.selectedValue !== null && this.selectedValue !== undefined && this.selectedValue !== '';
72
+ }
73
+ displayText() {
74
+ if (!this.hasSelection)
75
+ return this.config.placeholder || 'Select...';
76
+ if (this.config.isMultiSelect) {
77
+ const selectedOptions = this.options.filter(opt => this.selectedValue.includes(opt.value));
78
+ return selectedOptions.map(opt => opt.label).join(', ');
79
+ }
80
+ const selectedOption = this.options.find(opt => opt.value === this.selectedValue);
81
+ return selectedOption ? selectedOption.label : this.selectedValue;
82
+ }
83
+ togglePocket() {
84
+ if (this.visibility === SmtVisibilityType.READONLY) {
85
+ return;
86
+ }
87
+ if (this.isPocketOpen) {
88
+ this.closePocket();
89
+ }
90
+ else {
91
+ this.openPocket();
92
+ }
93
+ }
94
+ openPocket() {
95
+ this.calculateDirection();
96
+ this.isPocketOpen = true;
97
+ this.pocketOpen.emit(true);
98
+ this.searchText = '';
99
+ this.focusInput();
100
+ this.addOutsideClickListener();
101
+ }
102
+ closePocket() {
103
+ if (!this.isPocketOpen)
104
+ return;
105
+ this.isPocketOpen = false;
106
+ this.pocketOpen.emit(false);
107
+ this.removeOutsideClickListener();
108
+ }
109
+ addOutsideClickListener() {
110
+ if (this.clickListener)
111
+ return;
112
+ // Only clicks outside can reach the document, so we can close directly
113
+ this.clickListener = this.renderer.listen('document', 'mousedown', () => {
114
+ this.closePocket();
115
+ });
116
+ }
117
+ removeOutsideClickListener() {
118
+ if (this.clickListener) {
119
+ this.clickListener();
120
+ this.clickListener = null;
121
+ }
122
+ }
123
+ calculateDirection() {
124
+ const rect = this.el.nativeElement.getBoundingClientRect();
125
+ const windowHeight = window.innerHeight;
126
+ const pocketHeight = 200; // max-height
127
+ if (windowHeight - rect.bottom < pocketHeight && rect.top > pocketHeight) {
128
+ this.isOpenReverse = true;
129
+ }
130
+ else {
131
+ this.isOpenReverse = false;
132
+ }
133
+ }
134
+ focusInput() {
135
+ timer(100).pipe(take(1)).subscribe(() => {
136
+ this.inputElement?.nativeElement.focus();
137
+ });
138
+ }
139
+ onBlur() {
140
+ // Keyboard exit scenarios delay
141
+ timer(200).pipe(take(1)).subscribe(() => {
142
+ if (this.isPocketOpen) {
143
+ this.closePocket();
144
+ }
145
+ });
146
+ }
147
+ isSelected(option) {
148
+ if (this.config.isMultiSelect) {
149
+ return Array.isArray(this.selectedValue) && this.selectedValue.includes(option.value);
150
+ }
151
+ return this.selectedValue === option.value;
152
+ }
153
+ onEnterPressed() {
154
+ if (!this.searchText)
155
+ return;
156
+ const filtered = this.options.filter(option => option.label.toLowerCase().includes(this.searchText.toLowerCase()) ||
157
+ (typeof option.value === 'string' && option.value.toLowerCase().includes(this.searchText.toLowerCase())));
158
+ if (filtered.length > 0) {
159
+ this.selectOption(filtered[0]);
160
+ this.closePocket();
161
+ if (this.config.isMultiSelect) {
162
+ this.searchText = '';
163
+ }
164
+ }
165
+ }
166
+ selectOption(option, event) {
167
+ if (event) {
168
+ event.stopPropagation();
169
+ }
170
+ if (this.config.isMultiSelect) {
171
+ if (!Array.isArray(this.selectedValue)) {
172
+ this.selectedValue = [];
173
+ }
174
+ const index = this.selectedValue.indexOf(option.value);
175
+ if (index > -1) {
176
+ this.selectedValue.splice(index, 1);
177
+ }
178
+ else {
179
+ this.selectedValue.push(option.value);
180
+ }
181
+ // View update for spread or slice
182
+ this.selectedValue = [...this.selectedValue];
183
+ }
184
+ else {
185
+ this.selectedValue = option.value;
186
+ }
187
+ this.closePocket();
188
+ this.selectionChange.emit(this.selectedValue);
189
+ }
190
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SmtSelectComponent, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component });
191
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.0.6", type: SmtSelectComponent, isStandalone: true, selector: "smt-select", inputs: { options: "options", config: "config", selectedValue: "selectedValue", errorMessage: "errorMessage", isInvalid: "isInvalid", visibility: "visibility" }, outputs: { selectionChange: "selectionChange", pocketOpen: "pocketOpen" }, viewQueries: [{ propertyName: "pocketElement", first: true, predicate: ["pocket"], descendants: true }, { propertyName: "inputElement", first: true, predicate: ["input"], descendants: true }], ngImport: i0, template: "<div *ngIf=\"visibility !== VisibilityType.HIDDEN\" class=\"smt-select-wrapper\" [id]=\"config.fieldID || ''\"\r\n (mousedown)=\"$event.stopPropagation()\" (click)=\"$event.stopPropagation()\">\r\n <div class=\"smt-select-control\" (click)=\"togglePocket()\" [class.disabled]=\"visibility === VisibilityType.READONLY\"\r\n [class.error]=\"errorMessage ? true : (errorMessage === undefined ? isInvalid : false)\">\r\n\r\n <!-- Search Input -->\r\n <input #input *ngIf=\"isPocketOpen\" [(ngModel)]=\"searchText\" (click)=\"$event.stopPropagation()\"\r\n (keydown.enter)=\"onEnterPressed()\" (blur)=\"onBlur()\" class=\"smt-select-input\"\r\n [placeholder]=\"config.placeholder || 'Select...'\">\r\n\r\n <!-- Selected Value Display -->\r\n <ng-container *ngIf=\"!isPocketOpen\">\r\n <span class=\"select-value\" [class.placeholder]=\"!hasSelection\">\r\n {{ displayText() }}\r\n </span>\r\n </ng-container>\r\n\r\n <div class=\"arrow-icon\" [class.up]=\"isPocketOpen\"></div>\r\n </div>\r\n\r\n <!-- Dropdown (Pocket) -->\r\n <div #pocket *ngIf=\"isPocketOpen\" class=\"smt-select-pocket\" [class.open-reverse]=\"isOpenReverse\">\r\n\r\n <!-- Standard List -->\r\n <ul *ngIf=\"!config.virtualScroll\">\r\n <li *ngFor=\"let option of options | smtSelectSearchFilter: searchText\" (click)=\"selectOption(option, $event)\"\r\n class=\"smt-select-option\" [class.active]=\"isSelected(option)\">\r\n\r\n <div *ngIf=\"config.isMultiSelect\" class=\"checkbox-container\">\r\n <input type=\"checkbox\" [checked]=\"isSelected(option)\" (click)=\"$event.stopPropagation()\">\r\n </div>\r\n\r\n <span>{{ option.label }}</span>\r\n </li>\r\n </ul>\r\n\r\n <!-- Virtual Scroll -->\r\n <cdk-virtual-scroll-viewport *ngIf=\"config.virtualScroll\" itemSize=\"34\" class=\"virtual-scroll-viewport\">\r\n <div *cdkVirtualFor=\"let option of options | smtSelectSearchFilter: searchText\" class=\"smt-select-option\"\r\n (click)=\"selectOption(option, $event)\" [class.active]=\"isSelected(option)\">\r\n\r\n <div *ngIf=\"config.isMultiSelect\" class=\"checkbox-container\">\r\n <input type=\"checkbox\" [checked]=\"isSelected(option)\" (click)=\"$event.stopPropagation()\">\r\n </div>\r\n\r\n <span>{{ option.label }}</span>\r\n </div>\r\n </cdk-virtual-scroll-viewport>\r\n </div>\r\n\r\n <div *ngIf=\"errorMessage\" class=\"error-message\">\r\n {{ errorMessage }}\r\n </div>\r\n</div>", styles: [".smt-select-wrapper{position:relative;width:100%;font-family:Arial,sans-serif;box-sizing:border-box}.smt-select-wrapper *{box-sizing:border-box}.smt-select-control{display:flex;align-items:center;min-height:34px;border:1px solid #ccc;padding:4px 8px;background:#fff;cursor:pointer;position:relative}.smt-select-control.disabled{background:#f5f5f5;cursor:not-allowed;opacity:.7}.smt-select-control.error{border-color:#d9534f}.smt-select-control.error:focus-within{box-shadow:0 0 0 2px #d9534f33}.smt-select-control .select-value{font-size:13px;color:#333;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.smt-select-control .select-value.placeholder{color:#999;font-style:italic}.smt-select-control .smt-select-input{border:none;outline:none;width:100%;font-size:13px;padding:0;background:transparent}.smt-select-control .smt-select-input::placeholder{color:#999;font-style:italic}.smt-select-control .arrow-icon{margin-left:auto;width:0;height:0;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #666;transition:transform .2s}.smt-select-control .arrow-icon.up{transform:rotate(180deg)}.smt-select-pocket{position:absolute;top:100%;left:0;width:100%;background:#fff;border:1px solid #ccc;border-top:none;z-index:1000;max-height:200px;box-shadow:0 4px 6px #0000001a}.smt-select-pocket:has(ul){overflow-y:auto}.smt-select-pocket.open-reverse{top:auto;bottom:100%;border-top:1px solid #ccc;border-bottom:none}.smt-select-pocket .smt-select-option{padding:8px 12px;font-size:13px;cursor:pointer;display:flex;align-items:center}.smt-select-pocket .smt-select-option:hover{background-color:#f0faff}.smt-select-pocket .smt-select-option.active{background-color:#005f87;color:#fff}.smt-select-pocket .smt-select-option .checkbox-container{margin-right:8px;display:flex;align-items:center}.smt-select-pocket ul{list-style:none;padding:0;margin:0}.virtual-scroll-viewport{height:200px;width:100%;outline:none}.error-message{color:#d9534f;font-size:11px;margin-top:4px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i3.CdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "directive", type: i3.CdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "component", type: i3.CdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "pipe", type: SmtSelectSearchFilterPipe, name: "smtSelectSearchFilter" }] });
192
+ }
193
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.6", ngImport: i0, type: SmtSelectComponent, decorators: [{
194
+ type: Component,
195
+ args: [{ selector: 'smt-select', standalone: true, imports: [CommonModule, FormsModule, ScrollingModule, SmtSelectSearchFilterPipe], template: "<div *ngIf=\"visibility !== VisibilityType.HIDDEN\" class=\"smt-select-wrapper\" [id]=\"config.fieldID || ''\"\r\n (mousedown)=\"$event.stopPropagation()\" (click)=\"$event.stopPropagation()\">\r\n <div class=\"smt-select-control\" (click)=\"togglePocket()\" [class.disabled]=\"visibility === VisibilityType.READONLY\"\r\n [class.error]=\"errorMessage ? true : (errorMessage === undefined ? isInvalid : false)\">\r\n\r\n <!-- Search Input -->\r\n <input #input *ngIf=\"isPocketOpen\" [(ngModel)]=\"searchText\" (click)=\"$event.stopPropagation()\"\r\n (keydown.enter)=\"onEnterPressed()\" (blur)=\"onBlur()\" class=\"smt-select-input\"\r\n [placeholder]=\"config.placeholder || 'Select...'\">\r\n\r\n <!-- Selected Value Display -->\r\n <ng-container *ngIf=\"!isPocketOpen\">\r\n <span class=\"select-value\" [class.placeholder]=\"!hasSelection\">\r\n {{ displayText() }}\r\n </span>\r\n </ng-container>\r\n\r\n <div class=\"arrow-icon\" [class.up]=\"isPocketOpen\"></div>\r\n </div>\r\n\r\n <!-- Dropdown (Pocket) -->\r\n <div #pocket *ngIf=\"isPocketOpen\" class=\"smt-select-pocket\" [class.open-reverse]=\"isOpenReverse\">\r\n\r\n <!-- Standard List -->\r\n <ul *ngIf=\"!config.virtualScroll\">\r\n <li *ngFor=\"let option of options | smtSelectSearchFilter: searchText\" (click)=\"selectOption(option, $event)\"\r\n class=\"smt-select-option\" [class.active]=\"isSelected(option)\">\r\n\r\n <div *ngIf=\"config.isMultiSelect\" class=\"checkbox-container\">\r\n <input type=\"checkbox\" [checked]=\"isSelected(option)\" (click)=\"$event.stopPropagation()\">\r\n </div>\r\n\r\n <span>{{ option.label }}</span>\r\n </li>\r\n </ul>\r\n\r\n <!-- Virtual Scroll -->\r\n <cdk-virtual-scroll-viewport *ngIf=\"config.virtualScroll\" itemSize=\"34\" class=\"virtual-scroll-viewport\">\r\n <div *cdkVirtualFor=\"let option of options | smtSelectSearchFilter: searchText\" class=\"smt-select-option\"\r\n (click)=\"selectOption(option, $event)\" [class.active]=\"isSelected(option)\">\r\n\r\n <div *ngIf=\"config.isMultiSelect\" class=\"checkbox-container\">\r\n <input type=\"checkbox\" [checked]=\"isSelected(option)\" (click)=\"$event.stopPropagation()\">\r\n </div>\r\n\r\n <span>{{ option.label }}</span>\r\n </div>\r\n </cdk-virtual-scroll-viewport>\r\n </div>\r\n\r\n <div *ngIf=\"errorMessage\" class=\"error-message\">\r\n {{ errorMessage }}\r\n </div>\r\n</div>", styles: [".smt-select-wrapper{position:relative;width:100%;font-family:Arial,sans-serif;box-sizing:border-box}.smt-select-wrapper *{box-sizing:border-box}.smt-select-control{display:flex;align-items:center;min-height:34px;border:1px solid #ccc;padding:4px 8px;background:#fff;cursor:pointer;position:relative}.smt-select-control.disabled{background:#f5f5f5;cursor:not-allowed;opacity:.7}.smt-select-control.error{border-color:#d9534f}.smt-select-control.error:focus-within{box-shadow:0 0 0 2px #d9534f33}.smt-select-control .select-value{font-size:13px;color:#333;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.smt-select-control .select-value.placeholder{color:#999;font-style:italic}.smt-select-control .smt-select-input{border:none;outline:none;width:100%;font-size:13px;padding:0;background:transparent}.smt-select-control .smt-select-input::placeholder{color:#999;font-style:italic}.smt-select-control .arrow-icon{margin-left:auto;width:0;height:0;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #666;transition:transform .2s}.smt-select-control .arrow-icon.up{transform:rotate(180deg)}.smt-select-pocket{position:absolute;top:100%;left:0;width:100%;background:#fff;border:1px solid #ccc;border-top:none;z-index:1000;max-height:200px;box-shadow:0 4px 6px #0000001a}.smt-select-pocket:has(ul){overflow-y:auto}.smt-select-pocket.open-reverse{top:auto;bottom:100%;border-top:1px solid #ccc;border-bottom:none}.smt-select-pocket .smt-select-option{padding:8px 12px;font-size:13px;cursor:pointer;display:flex;align-items:center}.smt-select-pocket .smt-select-option:hover{background-color:#f0faff}.smt-select-pocket .smt-select-option.active{background-color:#005f87;color:#fff}.smt-select-pocket .smt-select-option .checkbox-container{margin-right:8px;display:flex;align-items:center}.smt-select-pocket ul{list-style:none;padding:0;margin:0}.virtual-scroll-viewport{height:200px;width:100%;outline:none}.error-message{color:#d9534f;font-size:11px;margin-top:4px}\n"] }]
196
+ }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }], propDecorators: { options: [{
197
+ type: Input
198
+ }], config: [{
199
+ type: Input
200
+ }], selectedValue: [{
201
+ type: Input
202
+ }], errorMessage: [{
203
+ type: Input
204
+ }], isInvalid: [{
205
+ type: Input
206
+ }], visibility: [{
207
+ type: Input
208
+ }], selectionChange: [{
209
+ type: Output
210
+ }], pocketOpen: [{
211
+ type: Output
212
+ }], pocketElement: [{
213
+ type: ViewChild,
214
+ args: ['pocket']
215
+ }], inputElement: [{
216
+ type: ViewChild,
217
+ args: ['input']
218
+ }] } });
219
+
220
+ /*
221
+ * Public API Surface of smt-select
222
+ */
223
+
224
+ /**
225
+ * Generated bundle index. Do not edit.
226
+ */
227
+
228
+ export { SmtSelectComponent, SmtVisibilityType };
229
+ //# sourceMappingURL=smt-select.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"smt-select.mjs","sources":["../../../projects/smt-select/src/lib/models/index.ts","../../../projects/smt-select/src/lib/pipes/smt-select-search-filter.pipe.ts","../../../projects/smt-select/src/lib/components/smt-select/smt-select.component.ts","../../../projects/smt-select/src/lib/components/smt-select/smt-select.component.html","../../../projects/smt-select/src/public-api.ts","../../../projects/smt-select/src/smt-select.ts"],"sourcesContent":["export interface SmtSelectOption {\r\n value: any;\r\n label: string;\r\n [key: string]: any;\r\n}\r\n\r\nexport enum SmtVisibilityType {\r\n HIDDEN = 'HIDDEN',\r\n READONLY = 'READONLY',\r\n ENABLED = 'ENABLED'\r\n}\r\n\r\nexport interface SmtSelectConfig {\r\n fieldID?: string | number;\r\n placeholder?: string;\r\n virtualScroll?: boolean;\r\n isMultiSelect?: boolean;\r\n}\r\n","import { Pipe, PipeTransform } from '@angular/core';\r\nimport { SmtSelectOption } from '../models';\r\n\r\n@Pipe({\r\n name: 'smtSelectSearchFilter',\r\n standalone: true,\r\n pure: true\r\n})\r\nexport class SmtSelectSearchFilterPipe implements PipeTransform {\r\n transform(options: SmtSelectOption[], searchText: string): SmtSelectOption[] {\r\n if (!options) return [];\r\n if (!searchText) return options;\r\n\r\n searchText = searchText.toLowerCase();\r\n\r\n return options.filter(option => {\r\n return option.label.toLowerCase().includes(searchText) ||\r\n (typeof option.value === 'string' && option.value.toLowerCase().includes(searchText));\r\n });\r\n }\r\n}\r\n","import {\r\n Component, Input, Output, EventEmitter,\r\n ElementRef, Renderer2, ViewChild, OnDestroy\r\n} from '@angular/core';\r\nimport { CommonModule } from '@angular/common';\r\nimport { FormsModule } from '@angular/forms';\r\nimport { ScrollingModule } from '@angular/cdk/scrolling';\r\nimport {\r\n SmtSelectOption, SmtSelectConfig,\r\n SmtVisibilityType\r\n} from '../../models';\r\nimport { SmtSelectSearchFilterPipe } from '../../pipes/smt-select-search-filter.pipe';\r\nimport { timer, take } from 'rxjs';\r\n\r\n@Component({\r\n selector: 'smt-select',\r\n standalone: true,\r\n imports: [CommonModule, FormsModule, ScrollingModule, SmtSelectSearchFilterPipe],\r\n templateUrl: './smt-select.component.html',\r\n styleUrls: ['./smt-select.component.scss']\r\n})\r\nexport class SmtSelectComponent implements OnDestroy {\r\n @Input() options: SmtSelectOption[] = [];\r\n @Input() config: SmtSelectConfig = {};\r\n @Input() selectedValue: any | any[] = null;\r\n @Input() errorMessage?: string | null;\r\n @Input() isInvalid = false;\r\n @Input() visibility: SmtVisibilityType = SmtVisibilityType.ENABLED;\r\n\r\n @Output() selectionChange = new EventEmitter<any | any[]>();\r\n @Output() pocketOpen = new EventEmitter<boolean>();\r\n\r\n @ViewChild('pocket') pocketElement!: ElementRef;\r\n @ViewChild('input') inputElement!: ElementRef;\r\n\r\n isPocketOpen = false;\r\n isOpenReverse = false;\r\n searchText = '';\r\n\r\n private clickListener: (() => void) | null = null;\r\n public VisibilityType = SmtVisibilityType;\r\n\r\n constructor(private el: ElementRef, private renderer: Renderer2) { }\r\n\r\n ngOnDestroy(): void {\r\n this.removeOutsideClickListener();\r\n }\r\n\r\n get hasSelection(): boolean {\r\n if (this.config.isMultiSelect) {\r\n return Array.isArray(this.selectedValue) && this.selectedValue.length > 0;\r\n }\r\n return this.selectedValue !== null && this.selectedValue !== undefined && this.selectedValue !== '';\r\n }\r\n\r\n displayText(): string {\r\n if (!this.hasSelection) return this.config.placeholder || 'Select...';\r\n\r\n if (this.config.isMultiSelect) {\r\n const selectedOptions = this.options.filter(opt =>\r\n (this.selectedValue as any[]).includes(opt.value)\r\n );\r\n return selectedOptions.map(opt => opt.label).join(', ');\r\n }\r\n\r\n const selectedOption = this.options.find(opt => opt.value === this.selectedValue);\r\n return selectedOption ? selectedOption.label : (this.selectedValue as string);\r\n }\r\n\r\n togglePocket(): void {\r\n if (this.visibility === SmtVisibilityType.READONLY) {\r\n return;\r\n }\r\n\r\n if (this.isPocketOpen) {\r\n this.closePocket();\r\n } else {\r\n this.openPocket();\r\n }\r\n }\r\n\r\n private openPocket(): void {\r\n this.calculateDirection();\r\n this.isPocketOpen = true;\r\n this.pocketOpen.emit(true);\r\n this.searchText = '';\r\n this.focusInput();\r\n this.addOutsideClickListener();\r\n }\r\n\r\n private closePocket(): void {\r\n if (!this.isPocketOpen) return;\r\n this.isPocketOpen = false;\r\n this.pocketOpen.emit(false);\r\n this.removeOutsideClickListener();\r\n }\r\n\r\n private addOutsideClickListener(): void {\r\n if (this.clickListener) return;\r\n\r\n // Only clicks outside can reach the document, so we can close directly\r\n this.clickListener = this.renderer.listen('document', 'mousedown', () => {\r\n this.closePocket();\r\n });\r\n }\r\n\r\n private removeOutsideClickListener(): void {\r\n if (this.clickListener) {\r\n this.clickListener();\r\n this.clickListener = null;\r\n }\r\n }\r\n\r\n private calculateDirection(): void {\r\n const rect = this.el.nativeElement.getBoundingClientRect();\r\n const windowHeight = window.innerHeight;\r\n const pocketHeight = 200; // max-height \r\n\r\n if (windowHeight - rect.bottom < pocketHeight && rect.top > pocketHeight) {\r\n this.isOpenReverse = true;\r\n } else {\r\n this.isOpenReverse = false;\r\n }\r\n }\r\n\r\n private focusInput(): void {\r\n timer(100).pipe(take(1)).subscribe(() => {\r\n this.inputElement?.nativeElement.focus();\r\n });\r\n }\r\n\r\n onBlur(): void {\r\n // Keyboard exit scenarios delay\r\n timer(200).pipe(take(1)).subscribe(() => {\r\n if (this.isPocketOpen) {\r\n this.closePocket();\r\n }\r\n });\r\n }\r\n\r\n isSelected(option: SmtSelectOption): boolean {\r\n if (this.config.isMultiSelect) {\r\n return Array.isArray(this.selectedValue) && this.selectedValue.includes(option.value);\r\n }\r\n return this.selectedValue === option.value;\r\n }\r\n\r\n onEnterPressed(): void {\r\n if (!this.searchText) return;\r\n\r\n const filtered = this.options.filter(option =>\r\n option.label.toLowerCase().includes(this.searchText.toLowerCase()) ||\r\n (typeof option.value === 'string' && option.value.toLowerCase().includes(this.searchText.toLowerCase()))\r\n );\r\n\r\n if (filtered.length > 0) {\r\n this.selectOption(filtered[0]);\r\n this.closePocket();\r\n if (this.config.isMultiSelect) {\r\n this.searchText = '';\r\n }\r\n }\r\n }\r\n\r\n selectOption(option: SmtSelectOption, event?: MouseEvent): void {\r\n if (event) {\r\n event.stopPropagation();\r\n }\r\n\r\n if (this.config.isMultiSelect) {\r\n if (!Array.isArray(this.selectedValue)) {\r\n this.selectedValue = [];\r\n }\r\n\r\n const index = this.selectedValue.indexOf(option.value);\r\n if (index > -1) {\r\n this.selectedValue.splice(index, 1);\r\n } else {\r\n this.selectedValue.push(option.value);\r\n }\r\n\r\n // View update for spread or slice \r\n this.selectedValue = [...this.selectedValue];\r\n } else {\r\n this.selectedValue = option.value;\r\n }\r\n\r\n this.closePocket();\r\n this.selectionChange.emit(this.selectedValue);\r\n }\r\n}\r\n","<div *ngIf=\"visibility !== VisibilityType.HIDDEN\" class=\"smt-select-wrapper\" [id]=\"config.fieldID || ''\"\r\n (mousedown)=\"$event.stopPropagation()\" (click)=\"$event.stopPropagation()\">\r\n <div class=\"smt-select-control\" (click)=\"togglePocket()\" [class.disabled]=\"visibility === VisibilityType.READONLY\"\r\n [class.error]=\"errorMessage ? true : (errorMessage === undefined ? isInvalid : false)\">\r\n\r\n <!-- Search Input -->\r\n <input #input *ngIf=\"isPocketOpen\" [(ngModel)]=\"searchText\" (click)=\"$event.stopPropagation()\"\r\n (keydown.enter)=\"onEnterPressed()\" (blur)=\"onBlur()\" class=\"smt-select-input\"\r\n [placeholder]=\"config.placeholder || 'Select...'\">\r\n\r\n <!-- Selected Value Display -->\r\n <ng-container *ngIf=\"!isPocketOpen\">\r\n <span class=\"select-value\" [class.placeholder]=\"!hasSelection\">\r\n {{ displayText() }}\r\n </span>\r\n </ng-container>\r\n\r\n <div class=\"arrow-icon\" [class.up]=\"isPocketOpen\"></div>\r\n </div>\r\n\r\n <!-- Dropdown (Pocket) -->\r\n <div #pocket *ngIf=\"isPocketOpen\" class=\"smt-select-pocket\" [class.open-reverse]=\"isOpenReverse\">\r\n\r\n <!-- Standard List -->\r\n <ul *ngIf=\"!config.virtualScroll\">\r\n <li *ngFor=\"let option of options | smtSelectSearchFilter: searchText\" (click)=\"selectOption(option, $event)\"\r\n class=\"smt-select-option\" [class.active]=\"isSelected(option)\">\r\n\r\n <div *ngIf=\"config.isMultiSelect\" class=\"checkbox-container\">\r\n <input type=\"checkbox\" [checked]=\"isSelected(option)\" (click)=\"$event.stopPropagation()\">\r\n </div>\r\n\r\n <span>{{ option.label }}</span>\r\n </li>\r\n </ul>\r\n\r\n <!-- Virtual Scroll -->\r\n <cdk-virtual-scroll-viewport *ngIf=\"config.virtualScroll\" itemSize=\"34\" class=\"virtual-scroll-viewport\">\r\n <div *cdkVirtualFor=\"let option of options | smtSelectSearchFilter: searchText\" class=\"smt-select-option\"\r\n (click)=\"selectOption(option, $event)\" [class.active]=\"isSelected(option)\">\r\n\r\n <div *ngIf=\"config.isMultiSelect\" class=\"checkbox-container\">\r\n <input type=\"checkbox\" [checked]=\"isSelected(option)\" (click)=\"$event.stopPropagation()\">\r\n </div>\r\n\r\n <span>{{ option.label }}</span>\r\n </div>\r\n </cdk-virtual-scroll-viewport>\r\n </div>\r\n\r\n <div *ngIf=\"errorMessage\" class=\"error-message\">\r\n {{ errorMessage }}\r\n </div>\r\n</div>","/*\r\n * Public API Surface of smt-select\r\n */\r\n\r\nexport * from './lib/models';\r\nexport * from './lib/components/smt-select/smt-select.component';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;;;;;;;IAMY;AAAZ,CAAA,UAAY,iBAAiB,EAAA;AAC3B,IAAA,iBAAA,CAAA,QAAA,CAAA,GAAA,QAAiB;AACjB,IAAA,iBAAA,CAAA,UAAA,CAAA,GAAA,UAAqB;AACrB,IAAA,iBAAA,CAAA,SAAA,CAAA,GAAA,SAAmB;AACrB,CAAC,EAJW,iBAAiB,KAAjB,iBAAiB,GAAA,EAAA,CAAA,CAAA;;MCEhB,yBAAyB,CAAA;IAClC,SAAS,CAAC,OAA0B,EAAE,UAAkB,EAAA;AACpD,QAAA,IAAI,CAAC,OAAO;AAAE,YAAA,OAAO,EAAE;AACvB,QAAA,IAAI,CAAC,UAAU;AAAE,YAAA,OAAO,OAAO;AAE/B,QAAA,UAAU,GAAG,UAAU,CAAC,WAAW,EAAE;AAErC,QAAA,OAAO,OAAO,CAAC,MAAM,CAAC,MAAM,IAAG;YAC3B,OAAO,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC;AAClD,iBAAC,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;AAC7F,QAAA,CAAC,CAAC;IACN;uGAXS,yBAAyB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,IAAA,EAAA,CAAA;qGAAzB,yBAAyB,EAAA,YAAA,EAAA,IAAA,EAAA,IAAA,EAAA,uBAAA,EAAA,CAAA;;2FAAzB,yBAAyB,EAAA,UAAA,EAAA,CAAA;kBALrC,IAAI;AAAC,YAAA,IAAA,EAAA,CAAA;AACF,oBAAA,IAAI,EAAE,uBAAuB;AAC7B,oBAAA,UAAU,EAAE,IAAI;AAChB,oBAAA,IAAI,EAAE;AACT,iBAAA;;;MCcY,kBAAkB,CAAA;AAqBP,IAAA,EAAA;AAAwB,IAAA,QAAA;IApBnC,OAAO,GAAsB,EAAE;IAC/B,MAAM,GAAoB,EAAE;IAC5B,aAAa,GAAgB,IAAI;AACjC,IAAA,YAAY;IACZ,SAAS,GAAG,KAAK;AACjB,IAAA,UAAU,GAAsB,iBAAiB,CAAC,OAAO;AAExD,IAAA,eAAe,GAAG,IAAI,YAAY,EAAe;AACjD,IAAA,UAAU,GAAG,IAAI,YAAY,EAAW;AAE7B,IAAA,aAAa;AACd,IAAA,YAAY;IAEhC,YAAY,GAAG,KAAK;IACpB,aAAa,GAAG,KAAK;IACrB,UAAU,GAAG,EAAE;IAEP,aAAa,GAAwB,IAAI;IAC1C,cAAc,GAAG,iBAAiB;IAEzC,WAAA,CAAoB,EAAc,EAAU,QAAmB,EAAA;QAA3C,IAAA,CAAA,EAAE,GAAF,EAAE;QAAsB,IAAA,CAAA,QAAQ,GAAR,QAAQ;IAAe;IAEnE,WAAW,GAAA;QACP,IAAI,CAAC,0BAA0B,EAAE;IACrC;AAEA,IAAA,IAAI,YAAY,GAAA;AACZ,QAAA,IAAI,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE;AAC3B,YAAA,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC;QAC7E;AACA,QAAA,OAAO,IAAI,CAAC,aAAa,KAAK,IAAI,IAAI,IAAI,CAAC,aAAa,KAAK,SAAS,IAAI,IAAI,CAAC,aAAa,KAAK,EAAE;IACvG;IAEA,WAAW,GAAA;QACP,IAAI,CAAC,IAAI,CAAC,YAAY;AAAE,YAAA,OAAO,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,WAAW;AAErE,QAAA,IAAI,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE;YAC3B,MAAM,eAAe,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,IAC1C,IAAI,CAAC,aAAuB,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CACpD;AACD,YAAA,OAAO,eAAe,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QAC3D;QAEA,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,KAAK,IAAI,CAAC,aAAa,CAAC;AACjF,QAAA,OAAO,cAAc,GAAG,cAAc,CAAC,KAAK,GAAI,IAAI,CAAC,aAAwB;IACjF;IAEA,YAAY,GAAA;QACR,IAAI,IAAI,CAAC,UAAU,KAAK,iBAAiB,CAAC,QAAQ,EAAE;YAChD;QACJ;AAEA,QAAA,IAAI,IAAI,CAAC,YAAY,EAAE;YACnB,IAAI,CAAC,WAAW,EAAE;QACtB;aAAO;YACH,IAAI,CAAC,UAAU,EAAE;QACrB;IACJ;IAEQ,UAAU,GAAA;QACd,IAAI,CAAC,kBAAkB,EAAE;AACzB,QAAA,IAAI,CAAC,YAAY,GAAG,IAAI;AACxB,QAAA,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC;AAC1B,QAAA,IAAI,CAAC,UAAU,GAAG,EAAE;QACpB,IAAI,CAAC,UAAU,EAAE;QACjB,IAAI,CAAC,uBAAuB,EAAE;IAClC;IAEQ,WAAW,GAAA;QACf,IAAI,CAAC,IAAI,CAAC,YAAY;YAAE;AACxB,QAAA,IAAI,CAAC,YAAY,GAAG,KAAK;AACzB,QAAA,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC;QAC3B,IAAI,CAAC,0BAA0B,EAAE;IACrC;IAEQ,uBAAuB,GAAA;QAC3B,IAAI,IAAI,CAAC,aAAa;YAAE;;AAGxB,QAAA,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,UAAU,EAAE,WAAW,EAAE,MAAK;YACpE,IAAI,CAAC,WAAW,EAAE;AACtB,QAAA,CAAC,CAAC;IACN;IAEQ,0BAA0B,GAAA;AAC9B,QAAA,IAAI,IAAI,CAAC,aAAa,EAAE;YACpB,IAAI,CAAC,aAAa,EAAE;AACpB,YAAA,IAAI,CAAC,aAAa,GAAG,IAAI;QAC7B;IACJ;IAEQ,kBAAkB,GAAA;QACtB,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,qBAAqB,EAAE;AAC1D,QAAA,MAAM,YAAY,GAAG,MAAM,CAAC,WAAW;AACvC,QAAA,MAAM,YAAY,GAAG,GAAG,CAAC;AAEzB,QAAA,IAAI,YAAY,GAAG,IAAI,CAAC,MAAM,GAAG,YAAY,IAAI,IAAI,CAAC,GAAG,GAAG,YAAY,EAAE;AACtE,YAAA,IAAI,CAAC,aAAa,GAAG,IAAI;QAC7B;aAAO;AACH,YAAA,IAAI,CAAC,aAAa,GAAG,KAAK;QAC9B;IACJ;IAEQ,UAAU,GAAA;AACd,QAAA,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,MAAK;AACpC,YAAA,IAAI,CAAC,YAAY,EAAE,aAAa,CAAC,KAAK,EAAE;AAC5C,QAAA,CAAC,CAAC;IACN;IAEA,MAAM,GAAA;;AAEF,QAAA,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,MAAK;AACpC,YAAA,IAAI,IAAI,CAAC,YAAY,EAAE;gBACnB,IAAI,CAAC,WAAW,EAAE;YACtB;AACJ,QAAA,CAAC,CAAC;IACN;AAEA,IAAA,UAAU,CAAC,MAAuB,EAAA;AAC9B,QAAA,IAAI,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE;AAC3B,YAAA,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC;QACzF;AACA,QAAA,OAAO,IAAI,CAAC,aAAa,KAAK,MAAM,CAAC,KAAK;IAC9C;IAEA,cAAc,GAAA;QACV,IAAI,CAAC,IAAI,CAAC,UAAU;YAAE;QAEtB,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,IACvC,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC;aACjE,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC,CAC3G;AAED,QAAA,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE;YACrB,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;YAC9B,IAAI,CAAC,WAAW,EAAE;AAClB,YAAA,IAAI,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE;AAC3B,gBAAA,IAAI,CAAC,UAAU,GAAG,EAAE;YACxB;QACJ;IACJ;IAEA,YAAY,CAAC,MAAuB,EAAE,KAAkB,EAAA;QACpD,IAAI,KAAK,EAAE;YACP,KAAK,CAAC,eAAe,EAAE;QAC3B;AAEA,QAAA,IAAI,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE;YAC3B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE;AACpC,gBAAA,IAAI,CAAC,aAAa,GAAG,EAAE;YAC3B;AAEA,YAAA,MAAM,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC;AACtD,YAAA,IAAI,KAAK,GAAG,CAAC,CAAC,EAAE;gBACZ,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;YACvC;iBAAO;gBACH,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC;YACzC;;YAGA,IAAI,CAAC,aAAa,GAAG,CAAC,GAAG,IAAI,CAAC,aAAa,CAAC;QAChD;aAAO;AACH,YAAA,IAAI,CAAC,aAAa,GAAG,MAAM,CAAC,KAAK;QACrC;QAEA,IAAI,CAAC,WAAW,EAAE;QAClB,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC;IACjD;uGAxKS,kBAAkB,EAAA,IAAA,EAAA,CAAA,EAAA,KAAA,EAAA,EAAA,CAAA,UAAA,EAAA,EAAA,EAAA,KAAA,EAAA,EAAA,CAAA,SAAA,EAAA,CAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAAlB,kBAAkB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,YAAA,EAAA,MAAA,EAAA,EAAA,OAAA,EAAA,SAAA,EAAA,MAAA,EAAA,QAAA,EAAA,aAAA,EAAA,eAAA,EAAA,YAAA,EAAA,cAAA,EAAA,SAAA,EAAA,WAAA,EAAA,UAAA,EAAA,YAAA,EAAA,EAAA,OAAA,EAAA,EAAA,eAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,YAAA,EAAA,EAAA,WAAA,EAAA,CAAA,EAAA,YAAA,EAAA,eAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EAAA,CAAA,QAAA,CAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,EAAA,YAAA,EAAA,cAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EAAA,CAAA,OAAA,CAAA,EAAA,WAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECrB/B,y+EAqDM,EAAA,MAAA,EAAA,CAAA,29DAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EDpCQ,YAAY,+PAAE,WAAW,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,oBAAA,EAAA,QAAA,EAAA,8MAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,eAAA,EAAA,QAAA,EAAA,2CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,OAAA,EAAA,QAAA,EAAA,qDAAA,EAAA,MAAA,EAAA,CAAA,MAAA,EAAA,UAAA,EAAA,SAAA,EAAA,gBAAA,CAAA,EAAA,OAAA,EAAA,CAAA,eAAA,CAAA,EAAA,QAAA,EAAA,CAAA,SAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAE,eAAe,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,yBAAA,EAAA,QAAA,EAAA,uCAAA,EAAA,MAAA,EAAA,CAAA,UAAA,EAAA,aAAA,EAAA,aAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,eAAA,EAAA,QAAA,EAAA,kCAAA,EAAA,MAAA,EAAA,CAAA,iBAAA,EAAA,sBAAA,EAAA,uBAAA,EAAA,gCAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,wBAAA,EAAA,QAAA,EAAA,6BAAA,EAAA,MAAA,EAAA,CAAA,aAAA,EAAA,YAAA,CAAA,EAAA,OAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,MAAA,EAAA,IAAA,EAAE,yBAAyB,EAAA,IAAA,EAAA,uBAAA,EAAA,CAAA,EAAA,CAAA;;2FAItE,kBAAkB,EAAA,UAAA,EAAA,CAAA;kBAP9B,SAAS;+BACI,YAAY,EAAA,UAAA,EACV,IAAI,EAAA,OAAA,EACP,CAAC,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,yBAAyB,CAAC,EAAA,QAAA,EAAA,y+EAAA,EAAA,MAAA,EAAA,CAAA,29DAAA,CAAA,EAAA;;sBAK/E;;sBACA;;sBACA;;sBACA;;sBACA;;sBACA;;sBAEA;;sBACA;;sBAEA,SAAS;uBAAC,QAAQ;;sBAClB,SAAS;uBAAC,OAAO;;;AEjCtB;;AAEG;;ACFH;;AAEG;;;;"}
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "smt-select",
3
+ "version": "0.1.0",
4
+ "license": "MIT",
5
+ "author": "Samet Acar",
6
+ "peerDependencies": {
7
+ "@angular/common": "^21.0.0",
8
+ "@angular/core": "^21.0.0"
9
+ },
10
+ "dependencies": {
11
+ "tslib": "^2.3.0"
12
+ },
13
+ "keywords": [
14
+ "angular-dropdown-select",
15
+ "Angular Dropdown Select",
16
+ "Angular Selectbox",
17
+ "Angular Select",
18
+ "angular select",
19
+ "Angular Dropdown",
20
+ "Dropdown Select",
21
+ "Virtual Scroll",
22
+ "Virtual Scroll Select",
23
+ "Dropdown",
24
+ "angular",
25
+ "ng",
26
+ "Angular",
27
+ "dropdown",
28
+ "select",
29
+ "multi-select",
30
+ "javascript",
31
+ "typescript"
32
+ ],
33
+ "sideEffects": false,
34
+ "module": "fesm2022/smt-select.mjs",
35
+ "typings": "types/smt-select.d.ts",
36
+ "exports": {
37
+ "./package.json": {
38
+ "default": "./package.json"
39
+ },
40
+ ".": {
41
+ "types": "./types/smt-select.d.ts",
42
+ "default": "./fesm2022/smt-select.mjs"
43
+ }
44
+ }
45
+ }
@@ -0,0 +1,59 @@
1
+ import * as i0 from '@angular/core';
2
+ import { OnDestroy, EventEmitter, ElementRef, Renderer2 } from '@angular/core';
3
+
4
+ interface SmtSelectOption {
5
+ value: any;
6
+ label: string;
7
+ [key: string]: any;
8
+ }
9
+ declare enum SmtVisibilityType {
10
+ HIDDEN = "HIDDEN",
11
+ READONLY = "READONLY",
12
+ ENABLED = "ENABLED"
13
+ }
14
+ interface SmtSelectConfig {
15
+ fieldID?: string | number;
16
+ placeholder?: string;
17
+ virtualScroll?: boolean;
18
+ isMultiSelect?: boolean;
19
+ }
20
+
21
+ declare class SmtSelectComponent implements OnDestroy {
22
+ private el;
23
+ private renderer;
24
+ options: SmtSelectOption[];
25
+ config: SmtSelectConfig;
26
+ selectedValue: any | any[];
27
+ errorMessage?: string | null;
28
+ isInvalid: boolean;
29
+ visibility: SmtVisibilityType;
30
+ selectionChange: EventEmitter<any>;
31
+ pocketOpen: EventEmitter<boolean>;
32
+ pocketElement: ElementRef;
33
+ inputElement: ElementRef;
34
+ isPocketOpen: boolean;
35
+ isOpenReverse: boolean;
36
+ searchText: string;
37
+ private clickListener;
38
+ VisibilityType: typeof SmtVisibilityType;
39
+ constructor(el: ElementRef, renderer: Renderer2);
40
+ ngOnDestroy(): void;
41
+ get hasSelection(): boolean;
42
+ displayText(): string;
43
+ togglePocket(): void;
44
+ private openPocket;
45
+ private closePocket;
46
+ private addOutsideClickListener;
47
+ private removeOutsideClickListener;
48
+ private calculateDirection;
49
+ private focusInput;
50
+ onBlur(): void;
51
+ isSelected(option: SmtSelectOption): boolean;
52
+ onEnterPressed(): void;
53
+ selectOption(option: SmtSelectOption, event?: MouseEvent): void;
54
+ static ɵfac: i0.ɵɵFactoryDeclaration<SmtSelectComponent, never>;
55
+ static ɵcmp: i0.ɵɵComponentDeclaration<SmtSelectComponent, "smt-select", never, { "options": { "alias": "options"; "required": false; }; "config": { "alias": "config"; "required": false; }; "selectedValue": { "alias": "selectedValue"; "required": false; }; "errorMessage": { "alias": "errorMessage"; "required": false; }; "isInvalid": { "alias": "isInvalid"; "required": false; }; "visibility": { "alias": "visibility"; "required": false; }; }, { "selectionChange": "selectionChange"; "pocketOpen": "pocketOpen"; }, never, never, true, never>;
56
+ }
57
+
58
+ export { SmtSelectComponent, SmtVisibilityType };
59
+ export type { SmtSelectConfig, SmtSelectOption };