May 19, 2023
In this blog post, we'll explore how to create a dynamic, tree grid component in Salesforce Lightning Web Components (LWC) that have sortable feature, row actions.
The implemention consists of two main components:
1. Main container Component(mainTreeGrid)
The component handles:
<template><lightning-card title="Custom Tree Grid"><lightning-layout multiple-rows="true"><lightning-layout-item size="12"><p class="slds-m-around_x-small">ActionItem: {actionItem}</p><div class="slds-m-around_x-small slds-scrollable"><c-custom-tree-grid table-columns={columns} table-data={threadList} hide-checkbox-column="true" onrowaction={handleRowAction} onsort={onSortList}></c-custom-tree-grid></div></lightning-layout-item></lightning-layout></lightning-card></template>
import { LightningElement } from 'lwc';
const gridColumns= [{type: 'text', fieldName: 'col1', label: 'Name', sortable:true, link:true},{type: 'text', fieldName: 'col2', label: 'Email', sortable:true},{type: 'text', fieldName: 'col3', label: 'Phone', sortable:true},{type: 'text', fieldName: 'col4', label: 'Type', sortable:true},{type: 'text', fieldName: 'col5', label: 'Industry', sortable:true},{type: 'text', fieldName: 'col6', label: 'Ownership', sortable:true},{type: 'text', fieldName: 'col7', label: 'Rating', sortable:true},{type: 'text', fieldName: 'col8', label: 'CreatedDate', sortable:true},{type: 'action', label:'Action', typeAttributes: { rowActions: [{ label: 'Create', name: 'create' },{ label: 'Edit', name: 'edit' }]}}];
export default class MainTreeGrid extends LightningElement {columns = gridColumns;threadList = [];threadSortDirection;threadSortedBy;actionItem;
connectedCallback() {
this.threadList = [{"Id": "1","col1": "GenePoint","col2": "test1@gmail.com","col3": "(415) 555-1212","col4": "Customer - Channel","col5": "Banking","col6": "Private","col7": "Hot","col8": "03/02/2020, 12:00 AM","_children": [{"Id": "1-A","col1": "Name","col2": "Title","col3": "Email","col4": "Phone","col5": "Lead Source","col6": "Level","col7": "Report To","col8": "CreatedDate","_children": [{"Id": "1-A-A","col1": "Doglas","col2": "SVP, Technology","col3": "test0005@gmail.com","col4": "(212)-555-83838","col5": "Public Relations","col6": "Secondary","col7": "Lauren Boyle","col8": "03/02/2020, 12:00 AM"},{"Id": "1-A-B","col1": "Rogers","col2": "VP, Facilities","col3": "test0004@gmail.com","col4": "(212)-555-83838","col5": "Partner Referral","col6": "Primary","col7": "Scane Boyle","col8": "05/03/2020, 11:00 AM"}]}],},{"Id": "2","col1": "United Oil P& Gas, UAE","col2": "test2@gmail.com","col3": "(415) 842-5500","col4": "Customer - Direct","col5": "Energy","col6": "Private","col7": "Warm","col8": "10/11/2019, 03:00 PM","_children": [{"Id": "2-A","col1": "Name","col2": "Title","col3": "Email","col4": "Phone","col5": "Lead Source","col6": "Level","col7": "Report To","col8": "CreatedDate","_children": [{"Id": "2-A-A","col1": "Jane gray","col2": "Dean of Administration","col3": "test007@gmail.com","col4": "(212)-125-83838","col5": "Phone Inquiry","col6": "Primary","col7": "Jane gray","col8": "05/03/2020, 11:00 AM"},{"Id": "2-A-B","col1": "Atom smith","col2": "SVP, Technology","col3": "test008@gmail.com","col4": "(212)-555-83838","col5": "Web","col6": "Tertiary","col7": "Atom smith","col8": "05/05/2021, 03:00 AM"}]}],}]}
handleRowAction(event) {this.actionItem = JSON.stringify(event.detail.row);}
handleRowSelect(event) {this.actionItem = JSON.stringify(event.detail.row);}
onSortList(event) {let fieldName = event.detail.sortedBy;let sortDirection = event.detail.sortDirection;this.sortData(fieldName, sortDirection);this.sortBy = fieldName;this.sortDirection = sortDirection;}
sortData(fieldName, sortDirection) {let sortResult = this.threadList;let sorted = sortResult.sort(function (a, b) {if (a[fieldName] < b[fieldName])return sortDirection === 'desc' ? 1 : -1;else if (a[fieldName] > b[fieldName])return sortDirection === 'desc' ? -1 : 1;elsereturn 0;})this.threadList = JSON.parse(JSON.stringify(sorted));}}
<?xml version="1.0" encoding="UTF-8"?><LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata"><apiVersion>63.0</apiVersion><isExposed>true</isExposed><targets><target>lightning__HomePage</target><target>lightning__RecordPage</target></targets></LightningComponentBundle>
2. Custom Tree Grid Component (customTreeGrid)
The custom tree grid component implements the actual grid functionality:
<template>
<div class="slds-table_header-fixed_container slds-scrollable_x" onmousemove={handlemousemove} onmouseup={handlemouseup} ondblclick={handledblclickresizable}><table aria-multiselectable="true" class="slds-table slds-table_header-fixed slds-table_bordered slds-table_edit slds-table_resizable-cols slds-tree slds-table_tree" role="treegrid" aria-label="Example default tree grid" style="table-layout: fixed;"><thead><tr class="slds-line-height_reset"><template lwc:if={hideColumn}><th class="slds-text-align_right" scope="col" style="width:2rem"></th></template><template for:each={labelList} for:item="col"><template lwc:if={col.sortable}><th key={col.fieldName} aria-sort="none" style={fixedWidth} class="slds-is-resizable dv-dynamic-width slds-has-button-menu slds-is-resizable slds-is-sortable" scope="col"><div class="slds-cell-fixed" style={fixedWidth}><a class="slds-th__action slds-text-link_reset" href="#" role="button" tabindex="-1" onclick={sortByColumn}><span class="slds-assistive-text">Sort by: </span><div class="slds-grid slds-grid_vertical-align-center slds-has-flexi-truncate" data-icon-name={col.sortIconTitle} onmouseenter={mouseEnter} onmouseout={mouseOut} onmouseleave={mouseLeave} data-label={col.fieldName}><span class="slds-truncate" data-label={col.fieldName} data-icon-name={col.sortIconTitle} title={col.label}>{col.label}</span><span class="slds-m-left_xx-small slds-icon_container slds-icon-utility-arrowdown" ><lightning-icon class={col.hoverClass} icon-name={col.sortIconName} alternative-text={col.sortIconTitle} size='xx-small' data-label={col.fieldName} data-icon-name={col.sortIconTitle} title={col.sortIconTitle}></lightning-icon></span></div></a><button class="slds-button slds-button_icon slds-th__action-button slds-button_icon-x-small" aria-haspopup="true" tabindex="-1"><lightning-icon icon-name='utility:chevrondown' alternative-text='chevrondown' size='xx-small' title='chevrondown'></lightning-icon></button><div class="slds-resizable"><span class="slds-resizable__handle" onmousedown={handlemousedown}><span class="slds-resizable__divider"></span></span></div></div></th></template><template lwc:else><th key={col.fieldName} aria-sort="none" style={fixedWidth} class="slds-is-resizable dv-dynamic-width slds-has-button-menu slds-is-resizable slds-is-sortable" scope="col"><div class="slds-cell-fixed" style={fixedWidth}><a class="slds-th__action slds-text-link_reset" href="#" role="button" tabindex="-1" ><span class="slds-assistive-text">Sort by: </span><div class="slds-grid slds-grid_vertical-align-center slds-has-flexi-truncate" ><span class="slds-truncate" data-label={col.fieldName} title={col.label}>{col.label}</span></div></a><button class="slds-button slds-button_icon slds-th__action-button slds-button_icon-x-small" aria-haspopup="true" tabindex="-1" title="Show column actions"><lightning-icon icon-name='utility:chevrondown' alternative-text='chevrondown' size='xx-small' title='chevrondown'></lightning-icon></button><div class="slds-resizable"><span class="slds-resizable__handle" onmousedown={handlemousedown}><span class="slds-resizable__divider"></span></span></div></div></th></template></template></tr></thead><tbody><template for:each={dataList} for:item="data" for:index="index"><tr class="slds-hint-parent" key={data.Id} id={data.Id} aria-expanded={data.isExpand} aria-level="1" aria-posinset="2" aria-selected="false" aria-setsize="4" data-row-key-value={data.Id} data-row-number={data.rowNumber}><template lwc:if={hideColumn}><td class="slds-text-align_right" role="gridcell" style="width:3.25rem"><div class="slds-checkbox"><lightning-input type="checkbox" checked={data.isSelected} onchange={singleSelection} data-id={data.Id} name={data.Id}></lightning-input></div></td></template><td class="slds-tree__item" data-label={data.col1} scope="row"><div class="slds-truncate dv-dynamic-width" style={fixedWidth} title={data.col1}><button class="slds-button slds-button_icon slds-button_icon-xx-small slds-m-right_x-small" aria-hidden="false" tabindex="-1" title={data.col1} onclick={toggleExpand}><lightning-icon icon-name={data.expandIconName} alternative-text={data.expandIcontitle} size='xx-small' title={data.expandIcontitle} data-row-title={data.col1} data-row-key-value={data.Id} data-row-number={data.rowNumber} data-aria-expanded={data.isExpand}></lightning-icon><span class="slds-assistive-text">{data.col1}</span></button><a href={data.smturl} tabindex="-1">{data.col1}</a></div></td><td data-label={data.col2} role="gridcell"><div class="slds-truncate dv-dynamic-width" style={fixedWidth} title={data.col2}>{data.col2}</div></td><td data-label={data.col3} role="gridcell"><div class="slds-truncate dv-dynamic-width" style={fixedWidth} title={data.col3}>{data.col3}</div></td><td data-label={data.col4} role="gridcell"><div class="slds-truncate dv-dynamic-width" style={fixedWidth} title={data.col4}>{data.col4}</div></td><td data-label={data.col5} role="gridcell"><div class="slds-truncate dv-dynamic-width" style={fixedWidth} title={data.col5}>{data.col5}</div></td><td data-label={data.col6} role="gridcell"><div class="slds-truncate dv-dynamic-width" style={fixedWidth} title={data.col6}>{data.col6}</div></td><td data-label={data.col7} role="gridcell"><div class="slds-truncate dv-dynamic-width" style={fixedWidth} title={data.col7}>{data.col7}</div></td><td data-label={data.col8} role="gridcell"><div class="slds-truncate dv-dynamic-width" style={fixedWidth} title={data.col8}>{data.col8}</div></td><td role="gridcell" class="slds-cell_action-mode"><lightning-button-menu menu-alignment="auto" alternative-text="Show menu" variant="border-filled" icon-size="x-small" ><template for:each={data.rowAction} for:item="action" for:index="index"><lightning-menu-item key={data.Id} value={data} label={action.label} data-menu-label={action.label} data-menu-name={action.name} data-menu-id={data.Id} onclick={clickmenuitem}></lightning-menu-item></template></lightning-button-menu></td></tr>
<template lwc:if={data._children}><template lwc:if={data.isExpand}><template for:each={data._children} for:item="child1"><tr key={data.Id} id={child1.Id} aria-expanded={child1.isExpand} aria-level="2" aria-posinset="1" aria-selected="false" aria-setsize="1" class="slds-hint-parent" data-row-key-value={child1.Id} data-row-number={child1.rowNumber}><template lwc:if={hideColumn}><th class="slds-text-align_right" role="gridcell" style="width:3.25rem"><!-- <template lwc:if={showChildHeader}><div class="slds-checkbox"><lightning-input type="checkbox" checked={child1.isSelected} id={child1.Id} name={child1.Id}></lightning-input></div></template> --></th></template><td class="slds-tree__item" data-label={child1.col1} scope="row"><div class="slds-truncate dv-dynamic-width" style={fixedWidth} title={child1.col1}><button class="slds-button slds-button_icon slds-button_icon-xx-small slds-m-right_x-small" aria-hidden="false" tabindex="-1" title={child1.col1} onclick={toggleExpand}><lightning-icon icon-name={child1.expandIconName} alternative-text={child1.expandIcontitle} size='xx-small' title={child1.expandIcontitle} data-row-title={child1.col1} data-row-key-value={child1.Id} data-row-number={child1.rowNumber} data-aria-expanded={data.isExpand}></lightning-icon><span class="slds-assistive-text">{child1.col1}</span></button><span tabindex="-1" class="slds-text-title_bold">{child1.col1}</span></div></td><td data-label={child1.col2} role="gridcell"><div class="slds-truncate dv-dynamic-width slds-text-title_bold" style={fixedWidth} title={child1.col2}>{child1.col2}</div></td><td data-label={child1.col3} role="gridcell"><div class="slds-truncate dv-dynamic-width slds-text-title_bold" style={fixedWidth} title={child1.col3}>{child1.col3}</div></td><td data-label={child1.col4} role="gridcell"><div class="slds-truncate dv-dynamic-width slds-text-title_bold" style={fixedWidth} title={child1.col4}>{child1.col4}</div></td><td data-label={child1.col5} role="gridcell"><div class="slds-truncate dv-dynamic-width slds-text-title_bold" style={fixedWidth} title={child1.col5}>{child1.col5}</div></td><td data-label={child1.col6} role="gridcell"><div class="slds-truncate dv-dynamic-width slds-text-title_bold" style={fixedWidth} title={child1.col6}>{child1.col6}</div></td><td data-label={child1.col7} role="gridcell"><div class="slds-truncate dv-dynamic-width slds-text-title_bold" style={fixedWidth} title={child1.col7}>{child1.col7}</div></td><td data-label={child1.col8} role="gridcell"><div class="slds-truncate dv-dynamic-width slds-text-title_bold" style={fixedWidth} title={child1.col8}>{child1.col8}</div></td><td role="gridcell" style="width:3.25rem"></td></tr><template lwc:if={child1._children}><template lwc:if={child1.isExpand}><template for:each={child1._children} for:item="child2" for:index="index"><tr key={child2.Id} id={child2.Id} aria-expanded={child2.isExpand} aria-level="3" aria-posinset="1" aria-selected="false" aria-setsize="1" class="slds-hint-parent" data-row-key-value={child2.Id} data-row-number={child2.rowNumber}><template lwc:if={hideColumn}><td class="slds-text-align_right" role="gridcell" style="width:3.25rem"><div class="slds-checkbox"><lightning-input type="checkbox" checked={child2.isSelected} onchange={singleSelection} data-id={child2.Id} name={child2.Id}></lightning-input></div></td></template><td class="slds-tree__item" data-label={child2.col1} scope="row"><button class="slds-button slds-button_icon slds-button_icon-xx-small slds-m-right_x-small" aria-hidden="false" tabindex="-1" title={child2.col1} onclick={toggleExpand}><lightning-icon icon-name={child2.expandIconName} alternative-text={child2.expandIcontitle} size='xx-small' title={child2.expandIcontitle} data-row-title={child2.col1} data-row-key-value={child2.Id} data-row-number={child2.rowNumber}></lightning-icon><span class="slds-assistive-text">{child2.col1}</span></button><div class="slds-truncate dv-dynamic-width" style={fixedWidth} title={child2.col1}><a href={child2.smturl} tabindex="-1">{child2.col1}</a></div></td><td data-label={child2.col2} role="gridcell"><div class="slds-truncate dv-dynamic-width" style={fixedWidth} title={child2.col2}>{child2.col2}</div></td><td data-label={child2.col3} role="gridcell"><div class="slds-truncate" title={child2.col3}>{child2.col3}</div></td><td data-label={child2.col4} role="gridcell"><div class="slds-truncate dv-dynamic-width" style={fixedWidth} title={child2.col4}>{child2.col4}</div></td><td data-label={child2.col5} role="gridcell"><div class="slds-truncate dv-dynamic-width" style={fixedWidth} title={child2.col5}>{child2.col5}</div></td><td data-label={child2.col6} role="gridcell"><div class="slds-truncate dv-dynamic-width" style={fixedWidth} title={child2.col6}>{child2.col6}</div></td><td data-label={child2.col7} role="gridcell"><div class="slds-truncate dv-dynamic-width" style={fixedWidth} title={child2.col7}>{child2.col7}</div></td><td data-label={child2.col8} role="gridcell"><div class="slds-truncate dv-dynamic-width" style={fixedWidth} title={child2.col8}>{child2.col8}</div></td><td role="gridcell" class="slds-cell_action-mode"><lightning-button-menu menu-alignment="auto" alternative-text="Show menu" variant="border-filled" icon-size="x-small"><template for:each={child2.rowAction} for:item="action" for:index="index"><lightning-menu-item key={child2.Id} value={child2} label={action.label} data-menu-label={action.label} data-menu-name={action.name} data-menu-id={child2.Id} onclick={clickmenuitem}></lightning-menu-item></template></lightning-button-menu>
</td></tr></template></template></template>
</template></template></template>
</template>
</tbody></table></div></template>
import { LightningElement, api } from 'lwc';
export default class CustomTreeGrid extends LightningElement {@api hideCheckboxColumn;labelList=[];dataList=[];sortIconName = 'utility:arrowdown';iconTitle = 'arrowdown';expandIconName = 'utility:chevronright';expandIcontitle = 'chevronright';label=[];rowAction;rowActionItem={};fixedWidth = "width:200px";
@apiset tableColumns(value) {this.labelList = value.map(column=>{let labelObj = {...column}labelObj.hoverClass = 'slds-hide';labelObj.sortIconName = 'utility:arrowdown';labelObj.sortIconTitle = 'arrowdown';if(labelObj.hasOwnProperty('typeAttributes')) {this.rowAction = labelObj.typeAttributes.rowActions;}return labelObj;});}
get tableColumns() {return this.labelList;}
@apiset tableData(value) {if(value != undefined) {this.dataList = this.iterateNestedData(value);}}
get tableData() {return this.dataList;}
get hideColumn() {if(this.hideCheckboxColumn=="true") {return false;} else {return true;}}
iterateNestedData(data) {const dataList = data.map((item, index) => {let obj = {...item}if(obj.hasOwnProperty('_children')) {obj.isExpand = false;obj.rowNumber = index+1;obj.expandIconName = 'utility:chevronright';obj.expandIcontitle = 'chevronright';}obj.isSelected = false;obj.menuClass = 'slds-dropdown-trigger slds-dropdown-trigger_click';obj.rowAction = this.rowAction;if(item.hasOwnProperty('_children')) {obj._children = this.iterateNestedData(obj._children); // Recursive call for children}return obj;});return dataList;}
mouseEnter(event) {this.labelList.map(column=>{if(event.target.dataset.label == column.fieldName){column.hoverClass = 'slds-show';}});this.labelList = [...this.labelList];}
mouseOut(event) {this.labelList.map(column=>{if(event.target.dataset.label != column.fieldName){column.hoverClass = 'slds-hide';}});this.labelList = [...this.labelList];}
mouseLeave(event) {this.labelList.map(column=>{if(event.target.dataset.label == column.fieldName){column.hoverClass = 'slds-hide';}});this.labelList = [...this.labelList];}
sortByColumn(event) {const columnname = event.target.dataset.labelconst iconName = event.target.dataset.iconName;let sortDirection = 'asc';this.labelList.map(column=>{if(event.target.dataset.label == column.fieldName && column.sortIconName == 'utility:arrowdown'){column.sortIconName = 'utility:arrowup';column.sortIconTitle = 'arrowup';} else {column.sortIconName = 'utility:arrowdown';column.sortIconTitle = 'arrowdown';}});this.labelList = [...this.labelList];if(iconName === 'arrowdown') {sortDirection = 'asc';} else {sortDirection = 'desc';}const columnEvent = new CustomEvent("sort", { detail:{sortedBy:columnname, sortDirection:sortDirection }});this.dispatchEvent(columnEvent);}
selectedrow=null;singleSelection(event) {let singleselection = event.target.checked;let rowid = event.target.dataset.Id;this.dataList = this.selecteSingleRow(this.dataList, rowid);if(this.selectedrow != null && singleselection) {const rowselect = new CustomEvent("rowselect", { detail:this.selectedrow});this.dispatchEvent(rowselect);}}
selecteSingleRow(data, rowid) {const dataList = data.map((item, index) => {if(rowid==item.Id)item.isSelected = trueelse {item.isSelected = false;}if(item.hasOwnProperty('_children')) {item._children = this.selecteSingleRow(item._children, rowid); // Recursive call for children}if(item.Id==rowid) {this.selectedrow = {...this.selectedrow, Id:item.Id, col1:item.col1, col2:item.col2, col3:item.col3, col4:item.col4, col5:item.col5, col6:item.col6, col7:item.col7, col8:item.col8, col9:item.col9, col10:item.col10, smtId:item.smtId};}return item;});return dataList;}
toggleExpand(event) {const rowKeyValue = event.target.dataset.rowKeyValue;this.dataList = this.nestedData(this.dataList, rowKeyValue);}nestedData(data, rowKeyValue) {const dataList = data.map((item, index) => {if(item.hasOwnProperty('_children')) {if((item.Id === rowKeyValue) && (item.isExpand === false)) {item.isExpand = true;item.expandIconName = 'utility:chevrondown';item.expandIconTitle = 'chevrondown';}else if((item.Id === rowKeyValue) && (item.isExpand === true)) {item.isExpand = false;item.expandIconName = 'utility:chevronright';item.expandIconTitle = 'chevronright';}}if(item.hasOwnProperty('_children')) {item._children = this.nestedData(item._children, rowKeyValue); // Recursive call for children}return item;});return dataList;}
clickmenuitem(event) {const rowId = event.target.dataset.menuId;this.menuRowAction(this.dataList, rowId);const selectedItem = {action:{label:event.target.dataset.menuLabel, name:event.target.dataset.menuName}, row:this.rowActionItem}if(this.rowActionItem != null) {const actionEvent = new CustomEvent("rowaction", { detail:selectedItem});this.dispatchEvent(actionEvent);}}
menuRowAction(data, rowid) {const dataList = data.map((item, index) => {if(item.hasOwnProperty('_children')) {item._children = this.menuRowAction(item._children, rowid); // Recursive call for children}if(item.Id==rowid) {this.rowActionItem = {...this.rowActionItem, Id:item.Id, col1:item.col1, col2:item.col2, col3:item.col3, col4:item.col4, col5:item.col5, col6:item.col6, col7:item.col7, col8:item.col8, col9:item.col9, col10:item.col10, smtId:item.smtId};}return item;});return dataList;}
handlemouseup(e) {this._tableThColumn = undefined;this._tableThInnerDiv = undefined;this._pageX = undefined;this._tableThWidth = undefined;}handlemousedown(e) {if (!this._initWidths) {this._initWidths = [];let tableThs = this.template.querySelectorAll("table thead .dv-dynamic-width");}this._tableThColumn = e.target.parentElement;this._tableThInnerDiv = e.target.parentElement;while (this._tableThColumn.tagName !== "TH") {this._tableThColumn = this._tableThColumn.parentNode;}while (!this._tableThInnerDiv.className.includes("slds-cell-fixed")) {this._tableThInnerDiv = this._tableThInnerDiv.parentNode;}this._pageX = e.pageX;this._padding = this.paddingDiff(this._tableThColumn);this._tableThWidth = this._tableThColumn.offsetWidth - this._padding;}handlemousemove(e) {if (this._tableThColumn && this._tableThColumn.tagName === "TH") {this._diffX = e.pageX - this._pageX;this.template.querySelector("table").style.width = (this.template.querySelector("table") - (this._diffX)) + 'px';this._tableThColumn.style.width = (this._tableThWidth + this._diffX) + 'px';this._tableThInnerDiv.style.width = this._tableThColumn.style.width;let tableThs = this.template.querySelectorAll("table thead .dv-dynamic-width");let tableBodyRows = this.template.querySelectorAll("table tbody tr");let tableBodyTds = this.template.querySelectorAll("table tbody .dv-dynamic-width");tableBodyRows.forEach(row => {let rowTds = row.querySelectorAll(".dv-dynamic-width");rowTds.forEach((td, ind) => {rowTds[ind].style.width = tableThs[ind].style.width;});});}}handledblclickresizable() {let tableThs = this.template.querySelectorAll("table thead .dv-dynamic-width");let tableBodyRows = this.template.querySelectorAll("table tbody tr");tableThs.forEach((th, ind) => {th.style.width = this._initWidths[ind];th.querySelector(".slds-cell-fixed").style.width = this._initWidths[ind];});tableBodyRows.forEach(row => {let rowTds = row.querySelectorAll(".dv-dynamic-width");rowTds.forEach((td, ind) => {rowTds[ind].style.width = this._initWidths[ind];});});}paddingDiff(col) {if (this.getStyleVal(col, 'box-sizing') === 'border-box') {return 0;}this._padLeft = this.getStyleVal(col, 'padding-left');this._padRight = this.getStyleVal(col, 'padding-right');return (parseInt(this._padLeft, 10) + parseInt(this._padRight, 10));}getStyleVal(elm, css) {return (window.getComputedStyle(elm, null).getPropertyValue(css))}}
<?xml version="1.0" encoding="UTF-8"?><LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata"><apiVersion>63.0</apiVersion><isExposed>false</isExposed></LightningComponentBundle>
Output:
This implementation provides a robust, flexible tree grid component that can be easily integrated into any Salesforce LWC application. The component offers a rich set of features while maintaining good performance and user experience.