@sixbell-telco/sdk
Version:
A collection of reusable components designed for use in Sixbell Telco Angular projects
194 lines (190 loc) • 19.4 kB
JavaScript
import * as i0 from '@angular/core';
import { input, signal, computed, output, effect, Component } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
import { TranslatePipe } from '@ngx-translate/core';
import { ButtonComponent } from '@sixbell-telco/sdk/components/button';
import { CheckboxComponent } from '@sixbell-telco/sdk/components/forms/checkbox';
import { InputComponent, InputContentComponent } from '@sixbell-telco/sdk/components/forms/input';
import { IconComponent } from '@sixbell-telco/sdk/components/icon';
import { matSearch } from '@sixbell-telco/sdk/components/icon/material/baseline';
import { matChevronLeftOutline, matChevronRightOutline } from '@sixbell-telco/sdk/components/icon/material/outline';
import { TableComponent } from '@sixbell-telco/sdk/components/table';
import { TypographyDirective } from '@sixbell-telco/sdk/directives/typography';
import * as i1 from 'ngx-pagination';
import { NgxPaginationModule } from 'ngx-pagination';
class DualListComponent {
// Styles for tables
size = input('sm');
height = input('sm');
// Styles for buttons
chevronLeftIcon = matChevronLeftOutline;
chevronRightIcon = matChevronRightOutline;
searchIcon = matSearch;
// Key and label inputs and getters
key = input('id');
value = input('item');
label = input('');
titleLeft = input('');
titleRight = input('');
getKey(item) {
return item[this.key()];
}
getValue(item) {
return item[this.value()];
}
getLabel() {
return this.label()
.replace(/([A-Z])/g, ' $1')
.replace(/^./, function (str) {
return str.toUpperCase();
});
}
// Signals for dual list management
items = input.required();
target = signal([]);
selectedItems = signal([]);
itemsToRemove = signal([]);
totalItems = input(0);
addedItems = input.required({
// Input used when there are items added on dual list initialization
transform: (items) => {
this.selectedItems.set(items);
this.handleAddItems();
return items;
},
});
computedItems = computed(() => {
return this.items().map((item) => {
const selected = this.selectedItems().some((addedItem) => addedItem[this.key()] === item[this.key()]);
return { ...item, selected };
});
});
allToAdd = computed(() => {
return this.computedItems().filter((item) => item.selected).length === this.items().length;
});
allToRemove = computed(() => {
return this.target().length === this.itemsToRemove().length;
});
// Output for dual list change
itemsAdded = output();
itemsAddedEffect = effect(() => {
this.itemsAdded.emit(this.target());
});
// Left side search (managed by parent)
searchQuery = output();
handleSearch(search) {
this.searchQuery.emit(search);
}
// Right side search (local)
searchTarget = signal('');
searchTargetComputed = computed(() => {
if (this.searchTarget() === '')
return this.target();
return this.target().filter((item) => `${item[this.value()]}`.toLowerCase().includes(this.searchTarget().toLowerCase()));
});
handleTargetListSearch(search) {
this.searchTarget.set(search);
}
// Pagination
pagination = input.required();
pageChanged = output();
pageSizeChanged = output();
handlePageChange(page) {
this.pageChanged.emit(page);
}
handlePageSizeChange(size) {
this.pageSizeChanged.emit(size);
}
// Select item handlers
handleSelectSourceItem(checked, item) {
if (checked) {
const exists = this.selectedItems().some((selectedItem) => selectedItem[this.key()] === item[this.key()]);
if (exists)
return;
this.selectedItems.update((items) => [...items, { ...item }]);
}
else {
const selectedItems = this.selectedItems().filter((selectedItem) => {
if (selectedItem && selectedItem[this.key()] === item[this.key()]) {
return false;
}
return true;
});
this.selectedItems.set(selectedItems);
}
}
handleSelectTargetItem(checked, item) {
if (checked) {
item.selected = true;
this.itemsToRemove.update((items) => [...items, { ...item, selected: true }]);
}
else {
item.selected = false;
const itemsToRemove = this.itemsToRemove().filter((selectedItem) => {
if (selectedItem && selectedItem[this.key()] === item[this.key()]) {
return false;
}
return true;
});
this.itemsToRemove.set(itemsToRemove);
}
}
// Select ALL handlers
handleSelectAll(all) {
const itemsList = this.items();
itemsList.forEach((item) => {
this.handleSelectSourceItem(all, item);
});
}
handleSelectAllToRemove(all) {
const targetList = this.target();
targetList.forEach((item) => {
this.handleSelectTargetItem(all, item);
});
}
// Add and remove handlers
handleAddItems() {
const items = this.selectedItems().map((selectedItem) => {
return { ...selectedItem, selected: false };
});
this.target.set(items);
this.itemsToRemove.set([]);
}
handleRemoveItems() {
if (this.itemsToRemove().length === 0)
return;
const selectedItems = this.selectedItems().filter((selectedItem) => this.itemsToRemove().every((itemToRemove) => selectedItem[this.key()] !== itemToRemove[this.key()]));
this.selectedItems.set(selectedItems);
this.updateTargetList(this.itemsToRemove());
this.itemsToRemove.set([]);
this.handlePageChange(this.pagination().currentPage ?? 1);
}
updateTargetList(itemsToRemove) {
const items = this.target().filter((addedItem) => {
return itemsToRemove.every((itemToRemove) => addedItem[this.key()] !== itemToRemove[this.key()]);
});
this.target.set(items);
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: DualListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.0", type: DualListComponent, isStandalone: true, selector: "st-dual-list", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, titleLeft: { classPropertyName: "titleLeft", publicName: "titleLeft", isSignal: true, isRequired: false, transformFunction: null }, titleRight: { classPropertyName: "titleRight", publicName: "titleRight", isSignal: true, isRequired: false, transformFunction: null }, items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, totalItems: { classPropertyName: "totalItems", publicName: "totalItems", isSignal: true, isRequired: false, transformFunction: null }, addedItems: { classPropertyName: "addedItems", publicName: "addedItems", isSignal: true, isRequired: true, transformFunction: null }, pagination: { classPropertyName: "pagination", publicName: "pagination", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { itemsAdded: "itemsAdded", searchQuery: "searchQuery", pageChanged: "pageChanged", pageSizeChanged: "pageSizeChanged" }, ngImport: i0, template: "@let id = pagination().id;\n@let currentPage = pagination().currentPage;\n@let itemsPerPage = pagination().itemsPerPage;\n@let titleLeftVar = titleLeft();\n@let titleRightVar = titleRight();\n@let heightVar = height();\n@let sizeVar = size();\n<div class=\"grid flex-1 grid-cols-[3fr_min-content_3fr] gap-6\">\n\t<div class=\"rounded-box border-neutral flex flex-col gap-6 border p-6\">\n\t\t@if (titleLeftVar) {\n\t\t\t<div>\n\t\t\t\t<h3 typography [tyVariant]=\"'h3'\">{{ titleLeftVar }}</h3>\n\t\t\t</div>\n\t\t}\n\t\t<div class=\"flex h-full flex-col gap-4\">\n\t\t\t<st-input\n\t\t\t\t[placeholder]=\"'sdk.dualList.searchPlaceHolder' | translate\"\n\t\t\t\t(enterPressed)=\"handleSearch($event)\"\n\t\t\t\t(valueDebounced)=\"handleSearch($event)\"\n\t\t\t>\n\t\t\t\t<st-input-content alignment=\"start\">\n\t\t\t\t\t<st-icon [icon]=\"searchIcon\"></st-icon>\n\t\t\t\t</st-input-content>\n\t\t\t</st-input>\n\t\t\t<st-table\n\t\t\t\tclass=\"h-full\"\n\t\t\t\t[pagination]=\"pagination()\"\n\t\t\t\t[totalItems]=\"totalItems()\"\n\t\t\t\t(pageChanged)=\"handlePageChange($event)\"\n\t\t\t\t(pageSizeChanged)=\"handlePageSizeChange($event)\"\n\t\t\t\t[showItemsPerPage]=\"false\"\n\t\t\t\t[showEntries]=\"false\"\n\t\t\t\t[maxSize]=\"4\"\n\t\t\t\t[size]=\"sizeVar\"\n\t\t\t\t[height]=\"heightVar\"\n\t\t\t\t[pinRows]=\"true\"\n\t\t\t\t[showEntries]=\"false\"\n\t\t\t\t[showItemsPerPage]=\"false\"\n\t\t\t>\n\t\t\t\t<thead>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th class=\"w-6\">\n\t\t\t\t\t\t\t<st-checkbox [value]=\"allToAdd()\" (valueUpdated)=\"handleSelectAll($event)\"></st-checkbox>\n\t\t\t\t\t\t</th>\n\t\t\t\t\t\t<th>{{ getLabel() }}</th>\n\t\t\t\t\t</tr>\n\t\t\t\t</thead>\n\t\t\t\t<tbody>\n\t\t\t\t\t@for (item of computedItems() | paginate: { id, currentPage, itemsPerPage, totalItems: totalItems() }; track getKey(item)) {\n\t\t\t\t\t\t<tr class=\"hover:bg-neutral\">\n\t\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t\t<st-checkbox [(value)]=\"item.selected\" (valueUpdated)=\"handleSelectSourceItem($event, item)\"></st-checkbox>\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t<td>{{ getValue(item) }}</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t}\n\t\t\t\t</tbody>\n\t\t\t</st-table>\n\t\t</div>\n\t</div>\n\n\t<!-- Buttons -->\n\t<div class=\"grid place-content-center gap-4\">\n\t\t<st-button variant=\"secondary\" [circle]=\"true\" [icon]=\"chevronRightIcon\" (click)=\"handleAddItems()\" />\n\t\t<st-button variant=\"secondary\" [circle]=\"true\" [icon]=\"chevronLeftIcon\" (click)=\"handleRemoveItems()\" />\n\t</div>\n\n\t<!-- END Buttons -->\n\n\t<div class=\"rounded-box border-neutral flex flex-1 flex-col gap-6 border p-6\">\n\t\t@if (titleRightVar) {\n\t\t\t<div>\n\t\t\t\t<h3 typography [tyVariant]=\"'h3'\">{{ titleRightVar }}</h3>\n\t\t\t</div>\n\t\t}\n\t\t<div class=\"flex h-full flex-col gap-4\">\n\t\t\t<st-input\n\t\t\t\t[placeholder]=\"'sdk.dualList.searchPlaceHolder' | translate\"\n\t\t\t\t(enterPressed)=\"handleTargetListSearch($event)\"\n\t\t\t\t(valueDebounced)=\"handleTargetListSearch($event)\"\n\t\t\t>\n\t\t\t\t<st-input-content alignment=\"start\">\n\t\t\t\t\t<st-icon [icon]=\"searchIcon\"></st-icon>\n\t\t\t\t</st-input-content>\n\t\t\t</st-input>\n\t\t\t@let added = searchTargetComputed();\n\t\t\t@if (added) {\n\t\t\t\t<st-table\n\t\t\t\t\t[data]=\"added\"\n\t\t\t\t\t[size]=\"sizeVar\"\n\t\t\t\t\t[height]=\"heightVar\"\n\t\t\t\t\t[pinRows]=\"true\"\n\t\t\t\t\t[showItemsPerPage]=\"false\"\n\t\t\t\t\t[showPagination]=\"false\"\n\t\t\t\t\t[totalItems]=\"added.length\"\n\t\t\t\t\tclass=\"h-full\"\n\t\t\t\t>\n\t\t\t\t\t<thead>\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<th class=\"w-6\">\n\t\t\t\t\t\t\t\t@if (searchTarget() === '') {\n\t\t\t\t\t\t\t\t\t<st-checkbox [value]=\"allToRemove()\" (valueUpdated)=\"handleSelectAllToRemove($event)\"></st-checkbox>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th>{{ getLabel() }}</th>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t</thead>\n\t\t\t\t\t<tbody>\n\t\t\t\t\t\t@for (item of added; track getKey(item)) {\n\t\t\t\t\t\t\t<tr class=\"hover:bg-neutral\">\n\t\t\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t\t\t<st-checkbox [(value)]=\"item.selected\" (valueUpdated)=\"handleSelectTargetItem($event, item)\"></st-checkbox>\n\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t<td>{{ getValue(item) }}</td>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t}\n\t\t\t\t\t</tbody>\n\t\t\t\t</st-table>\n\t\t\t}\n\t\t</div>\n\t</div>\n</div>\n", dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "component", type: TableComponent, selector: "st-table", inputs: ["size", "height", "pinRows", "pinColumns", "zebra", "paginatorType", "data", "totalItems", "pagination", "maxSize", "showItemsPerPage", "showEntries", "showPagination", "showFooter"], outputs: ["pageChanged", "pageSizeChanged", "checkedItems"] }, { kind: "component", type: CheckboxComponent, selector: "st-checkbox", inputs: ["variant", "size", "label", "name", "value", "disabled"], outputs: ["valueChange", "valueUpdated", "disabledChange"] }, { 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: "ngmodule", type: NgxPaginationModule }, { kind: "pipe", type: i1.PaginatePipe, name: "paginate" }, { kind: "component", type: ButtonComponent, selector: "st-button", inputs: ["variant", "ghost", "outline", "link", "soft", "dash", "wide", "circle", "square", "glass", "block", "loader", "size", "shadow", "focusable", "icon", "iconPosition", "type", "disabled"] }, { kind: "directive", type: TypographyDirective, selector: "[typography]", inputs: ["tyVariant", "tyColor", "tyFontWeight", "tyTextOverflow"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "component", type: InputContentComponent, selector: "st-input-content", inputs: ["alignment"] }, { kind: "component", type: IconComponent, selector: "st-icon", inputs: ["color", "size", "icon"] }] });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: DualListComponent, decorators: [{
type: Component,
args: [{ selector: 'st-dual-list', imports: [
ReactiveFormsModule,
TableComponent,
CheckboxComponent,
InputComponent,
NgxPaginationModule,
ButtonComponent,
TypographyDirective,
TranslatePipe,
InputContentComponent,
IconComponent,
], template: "@let id = pagination().id;\n@let currentPage = pagination().currentPage;\n@let itemsPerPage = pagination().itemsPerPage;\n@let titleLeftVar = titleLeft();\n@let titleRightVar = titleRight();\n@let heightVar = height();\n@let sizeVar = size();\n<div class=\"grid flex-1 grid-cols-[3fr_min-content_3fr] gap-6\">\n\t<div class=\"rounded-box border-neutral flex flex-col gap-6 border p-6\">\n\t\t@if (titleLeftVar) {\n\t\t\t<div>\n\t\t\t\t<h3 typography [tyVariant]=\"'h3'\">{{ titleLeftVar }}</h3>\n\t\t\t</div>\n\t\t}\n\t\t<div class=\"flex h-full flex-col gap-4\">\n\t\t\t<st-input\n\t\t\t\t[placeholder]=\"'sdk.dualList.searchPlaceHolder' | translate\"\n\t\t\t\t(enterPressed)=\"handleSearch($event)\"\n\t\t\t\t(valueDebounced)=\"handleSearch($event)\"\n\t\t\t>\n\t\t\t\t<st-input-content alignment=\"start\">\n\t\t\t\t\t<st-icon [icon]=\"searchIcon\"></st-icon>\n\t\t\t\t</st-input-content>\n\t\t\t</st-input>\n\t\t\t<st-table\n\t\t\t\tclass=\"h-full\"\n\t\t\t\t[pagination]=\"pagination()\"\n\t\t\t\t[totalItems]=\"totalItems()\"\n\t\t\t\t(pageChanged)=\"handlePageChange($event)\"\n\t\t\t\t(pageSizeChanged)=\"handlePageSizeChange($event)\"\n\t\t\t\t[showItemsPerPage]=\"false\"\n\t\t\t\t[showEntries]=\"false\"\n\t\t\t\t[maxSize]=\"4\"\n\t\t\t\t[size]=\"sizeVar\"\n\t\t\t\t[height]=\"heightVar\"\n\t\t\t\t[pinRows]=\"true\"\n\t\t\t\t[showEntries]=\"false\"\n\t\t\t\t[showItemsPerPage]=\"false\"\n\t\t\t>\n\t\t\t\t<thead>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th class=\"w-6\">\n\t\t\t\t\t\t\t<st-checkbox [value]=\"allToAdd()\" (valueUpdated)=\"handleSelectAll($event)\"></st-checkbox>\n\t\t\t\t\t\t</th>\n\t\t\t\t\t\t<th>{{ getLabel() }}</th>\n\t\t\t\t\t</tr>\n\t\t\t\t</thead>\n\t\t\t\t<tbody>\n\t\t\t\t\t@for (item of computedItems() | paginate: { id, currentPage, itemsPerPage, totalItems: totalItems() }; track getKey(item)) {\n\t\t\t\t\t\t<tr class=\"hover:bg-neutral\">\n\t\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t\t<st-checkbox [(value)]=\"item.selected\" (valueUpdated)=\"handleSelectSourceItem($event, item)\"></st-checkbox>\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t<td>{{ getValue(item) }}</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t}\n\t\t\t\t</tbody>\n\t\t\t</st-table>\n\t\t</div>\n\t</div>\n\n\t<!-- Buttons -->\n\t<div class=\"grid place-content-center gap-4\">\n\t\t<st-button variant=\"secondary\" [circle]=\"true\" [icon]=\"chevronRightIcon\" (click)=\"handleAddItems()\" />\n\t\t<st-button variant=\"secondary\" [circle]=\"true\" [icon]=\"chevronLeftIcon\" (click)=\"handleRemoveItems()\" />\n\t</div>\n\n\t<!-- END Buttons -->\n\n\t<div class=\"rounded-box border-neutral flex flex-1 flex-col gap-6 border p-6\">\n\t\t@if (titleRightVar) {\n\t\t\t<div>\n\t\t\t\t<h3 typography [tyVariant]=\"'h3'\">{{ titleRightVar }}</h3>\n\t\t\t</div>\n\t\t}\n\t\t<div class=\"flex h-full flex-col gap-4\">\n\t\t\t<st-input\n\t\t\t\t[placeholder]=\"'sdk.dualList.searchPlaceHolder' | translate\"\n\t\t\t\t(enterPressed)=\"handleTargetListSearch($event)\"\n\t\t\t\t(valueDebounced)=\"handleTargetListSearch($event)\"\n\t\t\t>\n\t\t\t\t<st-input-content alignment=\"start\">\n\t\t\t\t\t<st-icon [icon]=\"searchIcon\"></st-icon>\n\t\t\t\t</st-input-content>\n\t\t\t</st-input>\n\t\t\t@let added = searchTargetComputed();\n\t\t\t@if (added) {\n\t\t\t\t<st-table\n\t\t\t\t\t[data]=\"added\"\n\t\t\t\t\t[size]=\"sizeVar\"\n\t\t\t\t\t[height]=\"heightVar\"\n\t\t\t\t\t[pinRows]=\"true\"\n\t\t\t\t\t[showItemsPerPage]=\"false\"\n\t\t\t\t\t[showPagination]=\"false\"\n\t\t\t\t\t[totalItems]=\"added.length\"\n\t\t\t\t\tclass=\"h-full\"\n\t\t\t\t>\n\t\t\t\t\t<thead>\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<th class=\"w-6\">\n\t\t\t\t\t\t\t\t@if (searchTarget() === '') {\n\t\t\t\t\t\t\t\t\t<st-checkbox [value]=\"allToRemove()\" (valueUpdated)=\"handleSelectAllToRemove($event)\"></st-checkbox>\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th>{{ getLabel() }}</th>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t</thead>\n\t\t\t\t\t<tbody>\n\t\t\t\t\t\t@for (item of added; track getKey(item)) {\n\t\t\t\t\t\t\t<tr class=\"hover:bg-neutral\">\n\t\t\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t\t\t<st-checkbox [(value)]=\"item.selected\" (valueUpdated)=\"handleSelectTargetItem($event, item)\"></st-checkbox>\n\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t<td>{{ getValue(item) }}</td>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t}\n\t\t\t\t\t</tbody>\n\t\t\t\t</st-table>\n\t\t\t}\n\t\t</div>\n\t</div>\n</div>\n" }]
}] });
/**
* Generated bundle index. Do not edit.
*/
export { DualListComponent };
//# sourceMappingURL=sixbell-telco-sdk-components-dual-list.mjs.map