import { LightningElement, api } from "lwc"; import cx from "classnames"; import { GridVariant, LightningSlotElement } from "typings/custom"; const DEFAULT_COLUMNS = "three"; interface SlottedItem { originalDiplayValue: string; htmlElement: HTMLElement; } export default class Grid extends LightningElement { @api get columns() { if (this._columns.includes("dynamic") && this._itemCount) { return this.dynamicColumns; } return this._columns || DEFAULT_COLUMNS; } set columns(value: GridVariant) { this._columns = value; } @api searchPlaceholder?: string; /* The data passed in must be an array of elements equal to the number of items slotted into this grid. Each item in the array can either be a string, or an object. In the second case, we will search on all the string values of these objects. */ @api set searchData(value: string) { const data = JSON.parse(value); this._searchData = data.map((item: any) => typeof item === "string" ? item.toLowerCase() : Object.values(item) .filter((e) => typeof e === "string") .join("") .toLowerCase() ); } get searchData(): string[] { return this._searchData; } searchValue: string = ""; private get isSearchable(): boolean { return ( Boolean(this.searchData) && this._itemCount > 1 && this.searchData.length === this._itemCount ); } private get getSearchPlaceholder(): string { return this.searchPlaceholder ? this.searchPlaceholder : "Search"; } private onInputChange(e: CustomEvent) { this.searchValue = e.detail.toLowerCase(); this.displaySearchResults(); } private displaySearchResults() { if (this.searchValue) { this._slottedItems?.forEach((item, index) => { const searchDatum = this._searchData[index]; if (searchDatum && searchDatum.indexOf(this.searchValue) > -1) { item.htmlElement.style.display = item.originalDiplayValue; } else { item.htmlElement.style.display = "none"; } }); } else { // show all items this._slottedItems?.forEach((item) => { item.htmlElement.style.display = item.originalDiplayValue; }); } } _columns!: GridVariant; _itemCount: number = 0; _slottedItems?: SlottedItem[]; _searchData: string[] = []; private get className(): string { return cx("container", `col-${this.columns}`); } private isMoreIdyllicColumnNum = (a: number, b: number): boolean => { if (!this._itemCount) { return false; } const remA = this._itemCount % a; const remB = this._itemCount % b; const relRemA = a - remA; const relRemB = b - remB; if (remA === 0 && remB === 0) { return b > a; } if (remA === 0) { return false; } if (remB === 0) { return true; } return relRemB < relRemA; }; private get dynamicColumns(): GridVariant { if (!this._itemCount) { return DEFAULT_COLUMNS; } const possibleColumns = [4, 3]; let ideal = possibleColumns[0]; for (let i = 1; i < possibleColumns.length; i++) { if (this.isMoreIdyllicColumnNum(ideal, possibleColumns[i])) { ideal = possibleColumns[i]; } } return ({ 4: "four", 3: "three" }[ideal] || DEFAULT_COLUMNS) as GridVariant; } onSlotChange(e: Event) { const slottedItems = ( e.target as LightningSlotElement ).assignedElements(); this._itemCount = slottedItems.length; this._slottedItems = slottedItems.map((item) => { const htmlElement = item as HTMLElement; return { originalDiplayValue: htmlElement.style.display, htmlElement: htmlElement }; }); } }