UNPKG

@sixbell-telco/sdk

Version:

A collection of reusable components designed for use in Sixbell Telco Angular projects

196 lines (192 loc) 15.8 kB
import * as i0 from '@angular/core'; import { input, model, output, computed, ChangeDetectionStrategy, Component } from '@angular/core'; import * as i2 from '@angular/forms'; import { FormsModule, ReactiveFormsModule, NG_VALUE_ACCESSOR } from '@angular/forms'; import * as i1 from '@ng-select/ng-select'; import { NgSelectModule } from '@ng-select/ng-select'; import { TranslatePipe } from '@ngx-translate/core'; import { InputComponent } from '@sixbell-telco/sdk/components/forms/input'; import { TypographyDirective } from '@sixbell-telco/sdk/directives/typography'; import { cn } from '@sixbell-telco/sdk/utils/cn'; import { cva } from 'class-variance-authority'; /* eslint-disable @typescript-eslint/no-explicit-any */ const comboboxComponent = cva(['*:w-full', 'font-body', 'custom-ng-select'], { variants: { variant: { primary: ['combobox-primary first:'], secondary: ['combobox-secondary'], success: ['combobox-success'], error: ['combobox-error'], warning: ['combobox-warning'], }, shape: { 'round-none': ['*:rounded-[0px_!important]'], 'round-sm': ['*:rounded-[var(--rounded-btn-sm)_!important]'], 'round-md': ['*:rounded-[var(--rounded-btn-md)_!important]'], 'round-lg': ['*:rounded-[var(--rounded-btn-lg)_!important]'], }, size: { sm: ['[&>*:first-child]:px-2!', '[&>*:first-child]:py-1!', '[&>*:first-child]:h-auto!', '[&>*:first-child]:min-h-[auto]!'], md: ['[&>*:first-child]:px-3!', '[&>*:first-child]:py-2!', '[&>*:first-child]:h-auto!', '[&>*:first-child]:min-h-[auto]!'], lg: ['[&>*:first-child]:px-4!', '[&>*:first-child]:py-3!', '[&>*:first-child]:h-auto!', '[&>*:first-child]:min-h-[auto]!'], }, }, compoundVariants: [], defaultVariants: { variant: 'secondary', shape: 'round-sm', size: 'md', }, }); /** * Search input component * A search input component that allows the user to search for an option from a list of options * This component was implemented using the ng-select library * Docs: https://github.com/ng-select/ng-select */ class ComboboxComponent { // Basic input properties variant = input('secondary'); shape = input('round-sm'); size = input('md'); classes = input(''); name = input(null); label = input(''); value = model(); options = input([]); emptyOptionText = input(); loading = input(false); search = ''; // Options properties trackBy = input('id'); key = input('name'); // Form control properties parentForm = input(null); formControlName = input(''); get formField() { if (!this.parentForm()) return null; return this.parentForm()?.get(this.formControlName()); } // ControlValueAccessor attributes onControlChange = () => { }; onControlTouch = () => { }; disabled = model(false); // ControlValueAccessor methods writeValue(obj) { this.value.set(obj); } registerOnChange(fn) { this.onControlChange = fn; } registerOnTouched(fn) { this.onControlTouch = fn; } setDisabledState(isDisabled) { this.disabled.set(isDisabled); } // Select handler valueUpdated = output(); handleSelect() { this.valueUpdated.emit(this.value()); this.onControlChange(this.value()); this.handleSearch(''); } // Search handler searchQuery = output(); handleSearch(search) { this.searchQuery.emit(search); this.search = search; } // Blur handler handleBlur() { this.onControlTouch(); } // Checks if options are primitives or not simpleOption = computed(() => { return this.options().every((option) => typeof option !== 'object'); }); get componentClass() { return cn(comboboxComponent({ variant: this.variant(), shape: this.shape(), size: this.size(), class: this.classes(), })); } get errorClass() { return cn(comboboxComponent({ variant: 'error', shape: this.shape(), size: this.size(), class: this.classes(), })); } get successClass() { return cn(comboboxComponent({ variant: 'success', shape: this.shape(), size: this.size(), class: this.classes(), })); } get validationClass() { if (!this.formField) return this.componentClass; if (this.formField.dirty || this.formField.touched) { if (this.formField.invalid) { return this.errorClass; } else if (this.formField.valid) { return this.successClass; } } return this.componentClass; } get isValidTrackAndKey() { const opts = this.options(); if (!opts || opts.length === 0) { // Nothing to validate – treat as valid. return true; } const trackProp = this.trackBy(); // e.g. 'id' const keyProp = this.key(); // e.g. 'name' // Validate that each option has a non-empty value for both properties. const allHaveValues = opts.every((o) => o && o[trackProp] != null && o[trackProp] !== '' && o[keyProp] != null && o[keyProp] !== ''); if (!allHaveValues) { console.warn('[st-combobox] Provided trackBy ("' + this.trackBy() + '") and/or key ("' + this.key() + '") property is missing or not unique in the options. ' + 'Falling back to tracking by identity. For better performance, please ensure each option has a unique property for tracking and a valid property for display.'); return false; } // Check that the tracking property values are unique. const trackValues = opts.map((o) => o[trackProp]); return trackValues.length === new Set(trackValues).size; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: ComboboxComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.0", type: ComboboxComponent, isStandalone: true, selector: "st-combobox", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, shape: { classPropertyName: "shape", publicName: "shape", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, classes: { classPropertyName: "classes", publicName: "classes", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, emptyOptionText: { classPropertyName: "emptyOptionText", publicName: "emptyOptionText", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, trackBy: { classPropertyName: "trackBy", publicName: "trackBy", isSignal: true, isRequired: false, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: false, transformFunction: null }, parentForm: { classPropertyName: "parentForm", publicName: "parentForm", isSignal: true, isRequired: false, transformFunction: null }, formControlName: { classPropertyName: "formControlName", publicName: "formControlName", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", disabled: "disabledChange", valueUpdated: "valueUpdated", searchQuery: "searchQuery" }, providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: ComboboxComponent, multi: true, }, ], ngImport: i0, template: "<div class=\"flex flex-col gap-1\">\n\t@if (label()) {\n\t\t<label [for]=\"this.name()\" class=\"block\"\n\t\t\t><span typography [tyVariant]=\"'body'\" [tyColor]=\"'base'\" [tyFontWeight]=\"'normal'\">{{ label() }}</span></label\n\t\t>\n\t}\n\t<ng-select\n\t\t#select\n\t\t[notFoundText]=\"'sdk.combobox.noResultsFound' | translate\"\n\t\t[loadingText]=\"'sdk.combobox.loading' | translate\"\n\t\t[searchable]=\"false\"\n\t\t[clearable]=\"false\"\n\t\t[(ngModel)]=\"value\"\n\t\t[bindLabel]=\"key()\"\n\t\t[disabled]=\"disabled()\"\n\t\t(change)=\"handleSelect()\"\n\t\t(blur)=\"handleBlur()\"\n\t\t[class]=\"validationClass\"\n\t>\n\t\t@if (emptyOptionText() && !search) {\n\t\t\t<ng-option value=\"\">\n\t\t\t\t<span typography [tyVariant]=\"'body-sm'\" [tyColor]=\"'base'\" [tyFontWeight]=\"'normal'\">{{ emptyOptionText() }}</span></ng-option\n\t\t\t>\n\t\t}\n\t\t@if (simpleOption()) {\n\t\t\t@for (option of options(); track option) {\n\t\t\t\t<ng-option [value]=\"option\">\n\t\t\t\t\t<span typography [tyVariant]=\"'body-sm'\" [tyColor]=\"'base'\" [tyFontWeight]=\"'normal'\">{{ option }}</span>\n\t\t\t\t</ng-option>\n\t\t\t}\n\t\t} @else {\n\t\t\t@if (isValidTrackAndKey) {\n\t\t\t\t@for (option of options(); track option[trackBy()]) {\n\t\t\t\t\t<ng-option [value]=\"option\">\n\t\t\t\t\t\t<span typography [tyVariant]=\"'body-sm'\" [tyColor]=\"'base'\" [tyFontWeight]=\"'normal'\">{{ option[key()] }}</span>\n\t\t\t\t\t</ng-option>\n\t\t\t\t}\n\t\t\t} @else {\n\t\t\t\t@for (option of options(); track option) {\n\t\t\t\t\t<ng-option [value]=\"option\">\n\t\t\t\t\t\t<span typography [tyVariant]=\"'body-sm'\" [tyColor]=\"'base'\" [tyFontWeight]=\"'normal'\">{{ option }}</span>\n\t\t\t\t\t</ng-option>\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t<ng-template ng-header-tmp>\n\t\t\t<st-input placeholder=\"Buscar...\" (valueDebounced)=\"handleSearch($event)\" variant=\"secondary\" [ghost]=\"true\" />\n\t\t\t<!-- TODO: loader -->\n\t\t</ng-template>\n\t</ng-select>\n\t<ng-content></ng-content>\n</div>\n", dependencies: [{ kind: "ngmodule", type: NgSelectModule }, { kind: "component", type: i1.NgSelectComponent, selector: "ng-select", inputs: ["ariaLabelDropdown", "bindLabel", "bindValue", "ariaLabel", "markFirst", "placeholder", "fixedPlaceholder", "notFoundText", "typeToSearchText", "preventToggleOnRightClick", "addTagText", "loadingText", "clearAllText", "appearance", "dropdownPosition", "appendTo", "loading", "closeOnSelect", "hideSelected", "selectOnTab", "openOnEnter", "maxSelectedItems", "groupBy", "groupValue", "bufferAmount", "virtualScroll", "selectableGroup", "selectableGroupAsModel", "searchFn", "trackByFn", "clearOnBackspace", "labelForId", "inputAttrs", "tabIndex", "readonly", "searchWhileComposing", "minTermLength", "editableSearchTerm", "ngClass", "typeahead", "multiple", "addTag", "searchable", "clearable", "isOpen", "items", "compareWith", "clearSearchOnAdd", "deselectOnClick", "keyDownFn"], outputs: ["blur", "focus", "change", "open", "close", "search", "clear", "add", "remove", "scroll", "scrollToEnd"] }, { kind: "component", type: i1.NgOptionComponent, selector: "ng-option", inputs: ["value", "disabled"] }, { kind: "directive", type: i1.NgHeaderTemplateDirective, selector: "[ng-header-tmp]" }, { kind: "ngmodule", type: FormsModule }, { 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: "component", type: InputComponent, selector: "st-input", inputs: ["variant", "size", "label", "type", "name", "placeholder", "readonly", "focus", "onlyNumbers", "ghost", "value", "parentForm", "formControlName", "disabled", "debounceTime"], outputs: ["valueChange", "disabledChange", "enterPressed", "blurred", "valueDebounced"] }, { kind: "directive", type: TypographyDirective, selector: "[typography]", inputs: ["tyVariant", "tyColor", "tyFontWeight", "tyTextOverflow"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: ComboboxComponent, decorators: [{ type: Component, args: [{ selector: 'st-combobox', imports: [NgSelectModule, FormsModule, InputComponent, TypographyDirective, ReactiveFormsModule, TranslatePipe], providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: ComboboxComponent, multi: true, }, ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"flex flex-col gap-1\">\n\t@if (label()) {\n\t\t<label [for]=\"this.name()\" class=\"block\"\n\t\t\t><span typography [tyVariant]=\"'body'\" [tyColor]=\"'base'\" [tyFontWeight]=\"'normal'\">{{ label() }}</span></label\n\t\t>\n\t}\n\t<ng-select\n\t\t#select\n\t\t[notFoundText]=\"'sdk.combobox.noResultsFound' | translate\"\n\t\t[loadingText]=\"'sdk.combobox.loading' | translate\"\n\t\t[searchable]=\"false\"\n\t\t[clearable]=\"false\"\n\t\t[(ngModel)]=\"value\"\n\t\t[bindLabel]=\"key()\"\n\t\t[disabled]=\"disabled()\"\n\t\t(change)=\"handleSelect()\"\n\t\t(blur)=\"handleBlur()\"\n\t\t[class]=\"validationClass\"\n\t>\n\t\t@if (emptyOptionText() && !search) {\n\t\t\t<ng-option value=\"\">\n\t\t\t\t<span typography [tyVariant]=\"'body-sm'\" [tyColor]=\"'base'\" [tyFontWeight]=\"'normal'\">{{ emptyOptionText() }}</span></ng-option\n\t\t\t>\n\t\t}\n\t\t@if (simpleOption()) {\n\t\t\t@for (option of options(); track option) {\n\t\t\t\t<ng-option [value]=\"option\">\n\t\t\t\t\t<span typography [tyVariant]=\"'body-sm'\" [tyColor]=\"'base'\" [tyFontWeight]=\"'normal'\">{{ option }}</span>\n\t\t\t\t</ng-option>\n\t\t\t}\n\t\t} @else {\n\t\t\t@if (isValidTrackAndKey) {\n\t\t\t\t@for (option of options(); track option[trackBy()]) {\n\t\t\t\t\t<ng-option [value]=\"option\">\n\t\t\t\t\t\t<span typography [tyVariant]=\"'body-sm'\" [tyColor]=\"'base'\" [tyFontWeight]=\"'normal'\">{{ option[key()] }}</span>\n\t\t\t\t\t</ng-option>\n\t\t\t\t}\n\t\t\t} @else {\n\t\t\t\t@for (option of options(); track option) {\n\t\t\t\t\t<ng-option [value]=\"option\">\n\t\t\t\t\t\t<span typography [tyVariant]=\"'body-sm'\" [tyColor]=\"'base'\" [tyFontWeight]=\"'normal'\">{{ option }}</span>\n\t\t\t\t\t</ng-option>\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t<ng-template ng-header-tmp>\n\t\t\t<st-input placeholder=\"Buscar...\" (valueDebounced)=\"handleSearch($event)\" variant=\"secondary\" [ghost]=\"true\" />\n\t\t\t<!-- TODO: loader -->\n\t\t</ng-template>\n\t</ng-select>\n\t<ng-content></ng-content>\n</div>\n" }] }] }); /** * Generated bundle index. Do not edit. */ export { ComboboxComponent }; //# sourceMappingURL=sixbell-telco-sdk-components-forms-combobox.mjs.map