@sixbell-telco/sdk
Version:
A collection of reusable components designed for use in Sixbell Telco Angular projects
196 lines (192 loc) • 15.8 kB
JavaScript
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