File

src/table/table.component.ts

Index

Properties

Properties

CHECKBOX_HEADER
CHECKBOX_HEADER: string
Type : string
CHECKBOX_ROW
CHECKBOX_ROW: string
Type : string
END_OF_DATA
END_OF_DATA: string
Type : string
FILTER
FILTER: string
Type : string
SCROLL_TOP
SCROLL_TOP: string
Type : string
import {
	Component,
	ApplicationRef,
	Input,
	Output,
	EventEmitter,
	ElementRef,
	AfterViewInit,
	TemplateRef,
	OnDestroy,
	HostBinding
} from "@angular/core";
import { Subscription, fromEvent, Observable } from "rxjs";

import { TableModel } from "./table-model.class";
import { TableHeaderItem } from "./table-header-item.class";
import { TableItem } from "./table-item.class";

import { getFocusElementList, tabbableSelectorIgnoreTabIndex } from "../common/tab.service";
import { I18n, Overridable } from "./../i18n/i18n.module";
import { merge } from "./../utils/object";
import { DataGridInteractionModel } from "./data-grid-interaction-model.class";
import { TableDomAdapter } from "./table-adapter.class";

export interface TableTranslations {
	FILTER: string;
	END_OF_DATA: string;
	SCROLL_TOP: string;
	CHECKBOX_HEADER: string;
	CHECKBOX_ROW: string;
}

/**
 * Build your table with this component by extending things that differ from default.
 *
 * [See demo](../../?path=/story/table--basic)
 *
 * Instead of the usual write-your-own-html approach you had with `<table>`,
 * carbon table uses model-view-controller approach.
 *
 * Here, you create a view (with built-in controller) and provide it a model.
 * Changes you make to the model are reflected in the view. Provide same model you use
 * in the table to the `<ibm-pagination>` components.
 * They provide a different view over the same data.
 *
 * ## Basic usage
 *
 * ```html
 * <ibm-table [model]="model"></ibm-table>
 * ```
 *
 * ```typescript
 * public model = new TableModel();
 *
 * this.model.data = [
 * 	[new TableItem({data: "asdf"}), new TableItem({data: "qwer"})],
 * 	[new TableItem({data: "csdf"}), new TableItem({data: "zwer"})],
 * 	[new TableItem({data: "bsdf"}), new TableItem({data: "swer"})],
 * 	[new TableItem({data: "csdf"}), new TableItem({data: "twer"})]
 * ];
 * ```
 *
 * ## Customization
 *
 * If you have custom data in your table, you need a way to display it. You can do that
 * by providing a template to `TableItem`.
 *
 * ```html
 * <ng-template #customTableItemTemplate let-data="data">
 * 	<a [routerLink]="data.link">{{data.name}} {{data.surname}}</a>
 * </ng-template>
 * ```
 *
 * ```typescript
 * customTableItemTemplate: TemplateRef<any>;
 *
 * this.customModel.data = [
 * 	[new TableItem({data: "asdf"}), new TableItem({data: {name: "Lessy", link: "/table"}, template: this.customTableItemTemplate})],
 * 	[new TableItem({data: "csdf"}), new TableItem({data: "swer"})],
 * 	[new TableItem({data: "bsdf"}), new TableItem({data: {name: "Alice", surname: "Bob"}, template: this.customTableItemTemplate})],
 * 	[new TableItem({data: "csdf"}), new TableItem({data: "twer"})],
 * ];
 * ```
 *
 * ### Sorting and filtering
 *
 * In case you need custom sorting and/or filtering you should subclass `TableHeaderItem`
 * and override needed functions.
 *
 * ```typescript
 * class FilterableHeaderItem extends TableHeaderItem {
 * 	// custom filter function
 * 	filter(item: TableItem): boolean {
 * 		if (typeof item.data === "string" && item.data.toLowerCase().indexOf(this.filterData.data.toLowerCase()) >= 0 ||
 * 		item.data.name && item.data.name.toLowerCase().indexOf(this.filterData.data.toLowerCase()) >= 0 ||
 * 		item.data.surname && item.data.surname.toLowerCase().indexOf(this.filterData.data.toLowerCase()) >= 0) {
 * 			return false;
 * 		}
 * 		return true;
 * 	}
 *
 * 	set filterCount(n) {}
 * 	get filterCount() {
 * 		return (this.filterData && this.filterData.data && this.filterData.data.length > 0) ? 1 : 0;
 * 	}
 *
 * 	// used for custom sorting
 * 	compare(one: TableItem, two: TableItem) {
 * 		const stringOne = (one.data.name || one.data.surname || one.data).toLowerCase();
 * 		const stringTwo = (two.data.name || two.data.surname || two.data).toLowerCase();
 *
 * 		if (stringOne > stringTwo) {
 * 			return 1;
 * 		} else if (stringOne < stringTwo) {
 * 			return -1;
 * 		} else {
 * 			return 0;
 * 		}
 * 	}
 * }
 * ```
 *
 * See `TableHeaderItem` class for more information.
 *
 * ## No data template
 *
 * When table has no data to show, it can show a message you provide it instead.
 *
 * ```html
 * <ibm-table [model]="model">No data.</ibm-table>
 * ```
 *
 * ... will show `No data.` message, but you can get creative and provide any template you want
 * to replace table's default `tbody`.
 *
 * ## Use pagination as table footer
 *
 * ```html
 * <ibm-pagination [model]="model" (selectPage)="selectPage($event)"></ibm-pagination>
 * ```
 *
 * `selectPage()` function should fetch the data from backend, create new `data`, apply it to `model.data`,
 * and update `model.currentPage`.
 *
 * If the data your server returns is a two dimensional array of objects, it would look something like this:
 *
 * ```typescript
 * selectPage(page) {
 * 	this.getPage(page).then((data: Array<Array<any>>) => {
 * 		// set the data and update page
 * 		this.model.data = this.prepareData(data);
 * 		this.model.currentPage = page;
 * 	});
 * }
 *
 * protected prepareData(data: Array<Array<any>>) {
 * 	// create new data from the service data
 * 	let newData = [];
 * 	data.forEach(dataRow => {
 * 		let row = [];
 * 		dataRow.forEach(dataElement => {
 * 			row.push(new TableItem({
 * 				data: dataElement,
 * 				template: typeof dataElement === "string" ? undefined : this.paginationTableItemTemplate
 * 				// your template can handle all the data types so you don't have to conditionally set it
 * 				// you can also set different templates for different columns based on index
 * 			}));
 * 		});
 * 		newData.push(row);
 * 	});
 * 	return newData;
 * }
 * ```
 *
 * <example-url>../../iframe.html?id=table--basic</example-url>
 */
@Component({
	selector: "ibm-table",
	template: `
	<table
		ibmTable
		[sortable]="sortable"
		[noBorder]="noBorder"
		[ngClass]="{'bx--data-table--sticky-header': stickyHeader}"
		[size]="size"
		[striped]="striped"
		[skeleton]="skeleton">
		<thead
			ibmTableHead
			[sortable]="sortable"
			(deselectAll)="onDeselectAll()"
			(selectAll)="onSelectAll()"
			(sort)="sort.emit($event)"
			[checkboxHeaderLabel]="getCheckboxHeaderLabel()"
			[filterTitle]="getFilterTitle()"
			[model]="model"
			[size]="size"
			[selectAllCheckbox]="selectAllCheckbox"
			[selectAllCheckboxSomeSelected]="selectAllCheckboxSomeSelected"
			[showSelectionColumn]="showSelectionColumn"
			[enableSingleSelect]="enableSingleSelect"
			[skeleton]="skeleton"
			[sortAscendingLabel]="sortAscendingLabel"
			[sortDescendingLabel]="sortDescendingLabel"
			[stickyHeader]="stickyHeader">
		</thead>
		<tbody
			ibmTableBody
			(deselectRow)="onSelectRow($event)"
			(scroll)="onScroll($event)"
			(selectRow)="onSelectRow($event)"
			[checkboxRowLabel]="getCheckboxRowLabel()"
			[enableSingleSelect]="enableSingleSelect"
			(rowClick)="onRowClick($event)"
			[expandButtonAriaLabel]="expandButtonAriaLabel"
			[model]="model"
			[size]="size"
			[ngStyle]="{'overflow-y': 'scroll'}"
			[selectionLabelColumn]="selectionLabelColumn"
			[showSelectionColumn]="showSelectionColumn"
			[skeleton]="skeleton"
			*ngIf="!noData; else noDataTemplate">
		</tbody>
		<ng-template #noDataTemplate><ng-content></ng-content></ng-template>
		<tfoot>
			<ng-template
				[ngTemplateOutlet]="footerTemplate">
			</ng-template>
			<tr *ngIf="this.model.isLoading">
				<td class="table_loading-indicator">
					<div class="bx--loading bx--loading--small">
						<svg class="bx--loading__svg" viewBox="-75 -75 150 150">
							<circle class="bx--loading__stroke" cx="0" cy="0" r="37.5" />
						</svg>
					</div>
				</td>
			</tr>
			<tr *ngIf="this.model.isEnd">
				<td class="table_end-indicator">
					<h5>{{getEndOfDataText() | async}}</h5>
					<button (click)="scrollToTop($event)" class="btn--secondary-sm">
						{{getScrollTopText() | async}}
					</button>
				</td>
			</tr>
		</tfoot>
	</table>
	`
})
export class Table implements AfterViewInit, OnDestroy {
	/**
	 * Creates a skeleton model with a row and column count specified by the user
	 *
	 * Example:
	 *
	 * ```typescript
	 * this.model = Table.skeletonModel(5, 5);
	 * ```
	 */
	static skeletonModel(rowCount: number, columnCount: number) {
		const model = new TableModel();
		let header = new Array<TableHeaderItem>();
		let data = new Array<Array<TableItem>>();
		let row = new Array<TableItem>();

		for (let i = 0; i < columnCount; i++) {
			header.push(new TableHeaderItem());
			row.push(new TableItem());
		}
		for (let i = 0; i < rowCount - 1; i++) {
			data.push(row);
		}

		model.header = header;
		model.data = data;
		return model;
	}

	static setTabIndex(element: HTMLElement, index: -1 | 0) {
		const focusElementList = getFocusElementList(element, tabbableSelectorIgnoreTabIndex);
		if (element.firstElementChild && element.firstElementChild.classList.contains("bx--table-sort")) {
			focusElementList[1].tabIndex = index;
		} else if (focusElementList.length > 0) {
			focusElementList[0].tabIndex = index;
		} else {
			element.tabIndex = index;
		}
	}

	static focus(element: HTMLElement) {
		const focusElementList = getFocusElementList(element, tabbableSelectorIgnoreTabIndex);
		if (element.firstElementChild && element.firstElementChild.classList.contains("bx--table-sort")) {
			focusElementList[1].focus();
		} else if (focusElementList.length > 0) {
			focusElementList[0].focus();
		} else {
			element.focus();
		}
	}

	/**
	 * `TableModel` with data the table is to display.
	 */
	@Input()
	set model(m: TableModel) {
		if (this._model) {
			this.subscriptions.unsubscribe();
			// Need to create a new subscription instance here because unsubscribing prevents any new subscriptions
			// from being added for some reason. When a new model is set, none of the subscriptions would exist.
			this.subscriptions = new Subscription();
		}

		this._model = m;

		const rowsChange = this._model.rowsSelectedChange.subscribe(() => this.updateSelectAllCheckbox());
		const dataChange = this._model.dataChange.subscribe(() => {
			if (this.isDataGrid) {
				this.resetTabIndex();
			}
			this.updateSelectAllCheckbox();
		});

		this.subscriptions.add(rowsChange);
		this.subscriptions.add(dataChange);

		if (this.isDataGrid) {
			const expandedChange = this._model.rowsExpandedChange.subscribe(() => {
				// Allows the expanded row to have a focus state when it exists in the DOM
				setTimeout(() => {
					const expandedRows = this.elementRef.nativeElement.querySelectorAll(".bx--expandable-row:not(.bx--parent-row)");
					Array.from<any>(expandedRows).forEach(row => {
						if (row.firstElementChild.tabIndex === undefined || row.firstElementChild.tabIndex !== -1) {
							row.firstElementChild.tabIndex = -1;
						}
					});
				});
			});
			this.subscriptions.add(expandedChange);
		}
	}

	get model(): TableModel {
		return this._model;
	}

	/**
	 * Size of the table rows.
	 */
	@Input() size: "sm" | "sh" | "md" | "lg" = "md";
	/**
	 * Set to `true` for a loading table.
	 */
	@Input() skeleton = false;
	/**
	 * Set to `true` for a data grid with keyboard interactions.
	 */
	@Input() set isDataGrid(value: boolean) {
		this._isDataGrid = value;
		if (this.isViewReady) {
			if (value) {
				this.enableDataGridInteractions();
			} else {
				this.disableDataGridInteractions();
			}
		}
	}

	/**
	 * Setting sortable to false will disable all headers including headers which are sortable. Is is
	 * possible to set the sortable state on the header item to disable/enable sorting for only some headers.
	 */
	@Input() sortable = true;

	@Input() noBorder = true;

	get isDataGrid(): boolean {
		return this._isDataGrid;
	}

	/**
	 * Controls whether to show the selection checkboxes column or not.
	 *
	 * @deprecated in the next major carbon-components-angular version in favor of
	 * `showSelectionColumn` because of new attribute `enableSingleSelect`
	 *  please use `showSelectionColumn` instead
	 */
	@Input()
	set enableRowSelect(value: boolean) {
		this.showSelectionColumn = value;
	}

	get enableRowSelect () {
		return this.showSelectionColumn;
	}

	/**
	 * Controls whether to show the selection checkboxes column or not.
	 */
	@Input() showSelectionColumn = true;

	/**
	 * Controls whether to enable multiple or single row selection.
	 */
	@Input() enableSingleSelect = false;

	/**
	 * Distance (in px) from the bottom that view has to reach before
	 * `scrollLoad` event is emitted.
	 */
	@Input() scrollLoadDistance = 0;

	/**
	 * Set to `true` to enable users to resize columns.
	 *
	 * Works for columns with width set in pixels.
	 *
	 */
	@Input() columnsResizable = false;

	/**
	 * Set to `true` to enable users to drag and drop columns.
	 *
	 * Changing the column order in table changes table model. Be aware of it when you add additional data
	 * to the model.
	 *
	 */
	@Input() columnsDraggable = false;

	@Input()
	set expandButtonAriaLabel(value: string | Observable<string>) {
		this._expandButtonAriaLabel.override(value);
	}
	get expandButtonAriaLabel() {
		return this._expandButtonAriaLabel.value;
	}
	@Input()
	set sortDescendingLabel(value: string | Observable<string>) {
		this._sortDescendingLabel.override(value);
	}
	get sortDescendingLabel() {
		return this._sortDescendingLabel.value;
	}
	@Input()
	set sortAscendingLabel(value: string | Observable<string>) {
		this._sortAscendingLabel.override(value);
	}
	get sortAscendingLabel() {
		return this._sortAscendingLabel.value;
	}

	/**
	 * Expects an object that contains some or all of:
	 * ```
	 * {
	 *		"FILTER": "Filter",
	 *		"END_OF_DATA": "You've reached the end of your content",
	 *		"SCROLL_TOP": "Scroll to top",
	 *		"CHECKBOX_HEADER": "Select all rows",
	 *		"CHECKBOX_ROW": "Select row"
	 * }
	 * ```
	 */
	@Input()
	set translations (value) {
		const valueWithDefaults = merge(this.i18n.getMultiple("TABLE"), value);
		this._filterTitle.override(valueWithDefaults.FILTER);
		this._endOfDataText.override(valueWithDefaults.END_OF_DATA);
		this._scrollTopText.override(valueWithDefaults.SCROLL_TOP);
		this._checkboxHeaderLabel.override(valueWithDefaults.CHECKBOX_HEADER);
		this._checkboxRowLabel.override(valueWithDefaults.CHECKBOX_ROW);
	}

	/**
	 * Set to `false` to remove table rows (zebra) stripes.
	 */
	@Input() striped = true;

	/**
	 * Set to `true` to stick the header to the top of the table
	 */
	@HostBinding("class.bx--data-table_inner-container") @Input() stickyHeader = false;

	/**
	 * Set footer template to customize what is displayed in the tfoot section of the table
	 */
	@Input() footerTemplate: TemplateRef<any>;

	/**
	 * Used to populate the row selection checkbox label with a useful value if set.
	 *
	 * Example:
	 * ```
	 * <ibm-table [selectionLabelColumn]="0"></ibm-table>
	 * <!-- results in aria-label="Select first column value"
	 * (where "first column value" is the value of the first column in the row -->
	 * ```
	 */
	@Input() selectionLabelColumn: number;

	/**
	 * Emits an index of the column that wants to be sorted.
	 *
	 */
	@Output() sort = new EventEmitter<number>();

	/**
	 * Emits if all rows are selected.
	 *
	 * @param model
	 */
	@Output() selectAll = new EventEmitter<Object>();

	/**
	 * Emits if all rows are deselected.
	 *
	 * @param model
	 */
	@Output() deselectAll = new EventEmitter<Object>();

	/**
	 * Emits if a single row is selected.
	 *
	 * @param ({model: this.model, selectedRowIndex: index})
	 */
	@Output() selectRow = new EventEmitter<Object>();

	/**
	 * Emits if a single row is deselected.
	 *
	 * @param ({model: this.model, deselectedRowIndex: index})
	 */
	@Output() deselectRow = new EventEmitter<Object>();

	/**
	 * Emits if a row item excluding expandButtons, checkboxes, or radios is clicked.
	 */
	@Output() rowClick = new EventEmitter<number>();

	/**
	 * Emits when table requires more data to be loaded.
	 */
	@Output() scrollLoad = new EventEmitter<TableModel>();

	/**
	 * Controls if all checkboxes are viewed as selected.
	 */
	selectAllCheckbox = false;

	/**
	 * Controls the indeterminate state of the header checkbox.
	 */
	selectAllCheckboxSomeSelected = false;

	get noData() {
		return !this.model.data ||
			this.model.data.length === 0 ||
			this.model.data.length === 1 && this.model.data[0].length === 0;
	}

	public isColumnDragging = false;
	public columnDraggedHoverIndex = -1;
	public columnDraggedPosition = "";

	protected _model: TableModel;
	protected _isDataGrid = false;
	// flag to prevent getters/setters from querying the view before it's fully instantiated
	protected isViewReady = false;

	protected subscriptions = new Subscription();
	protected positionSubscription: Subscription;

	protected interactionModel: DataGridInteractionModel;
	protected interactionPositionSubscription: Subscription;

	protected _expandButtonAriaLabel  = this.i18n.getOverridable("TABLE.EXPAND_BUTTON");
	protected _sortDescendingLabel = this.i18n.getOverridable("TABLE.SORT_DESCENDING");
	protected _sortAscendingLabel = this.i18n.getOverridable("TABLE.SORT_ASCENDING");
	protected _checkboxHeaderLabel = this.i18n.getOverridable("TABLE.CHECKBOX_HEADER");
	protected _checkboxRowLabel = this.i18n.getOverridable("TABLE.CHECKBOX_ROW");
	protected _endOfDataText = this.i18n.getOverridable("TABLE.END_OF_DATA");
	protected _scrollTopText = this.i18n.getOverridable("TABLE.SCROLL_TOP");
	protected _filterTitle = this.i18n.getOverridable("TABLE.FILTER");

	protected columnResizeWidth: number;
	protected columnResizeMouseX: number;
	protected mouseMoveSubscription: Subscription;
	protected mouseUpSubscription: Subscription;

	/**
	 * Creates an instance of Table.
	 */
	constructor(
		protected elementRef: ElementRef,
		protected applicationRef: ApplicationRef,
		protected i18n: I18n
	) {}

	ngAfterViewInit() {
		this.isViewReady = true;
		if (this.isDataGrid) {
			this.enableDataGridInteractions();
		}
	}

	ngOnDestroy() {
		this.subscriptions.unsubscribe();
		if (this.positionSubscription) {
			this.positionSubscription.unsubscribe();
		}
	}

	enableDataGridInteractions() {
		// if we have an `interactioModel` we've already enabled datagrid
		if (this.interactionModel) {
			return;
		}
		const table = this.elementRef.nativeElement.querySelector("table") as HTMLTableElement;
		const tableAdapter = new TableDomAdapter(table);
		const keydownEventStream = fromEvent<KeyboardEvent>(table, "keydown");
		const clickEventStream = fromEvent<MouseEvent>(table, "click");
		this.interactionModel = new DataGridInteractionModel(keydownEventStream, clickEventStream, tableAdapter);
		this.positionSubscription = this.interactionModel.position.subscribe(event => {
			const [currentRow, currentColumn] = event.current;
			const [previousRow, previousColumn] = event.previous;

			const currentElement = tableAdapter.getCell(currentRow, currentColumn);
			Table.setTabIndex(currentElement, 0);

			// if the model has just initialized don't focus or reset anything
			if (previousRow === -1 || previousColumn === -1) { return; }

			const previousElement = tableAdapter.getCell(previousRow, previousColumn);
			Table.setTabIndex(previousElement, -1);
			Table.focus(currentElement);
		});
		// call this after assigning `this.interactionModel` since it depends on it
		this.resetTabIndex();
	}

	disableDataGridInteractions() {
		// unsubscribe first so we don't cause the focus to fly around
		if (this.positionSubscription) {
			this.positionSubscription.unsubscribe();
		}
		// undo tab indexing (also resets the model)
		this.resetTabIndex(0);
		// null out the model ref
		this.interactionModel = null;
	}

	onSelectAll() {
		this.model.selectAll(true);
		this.selectAll.emit(this.model);
	}

	onDeselectAll() {
		this.model.selectAll(false);
		this.deselectAll.emit(this.model);
	}

	onSelectRow(event) {
		// check for the existence of the selectedRowIndex property
		if (Object.keys(event).includes("selectedRowIndex")) {
			this.model.selectRow(event.selectedRowIndex, true);
			this.selectRow.emit(event);

			if (this.showSelectionColumn && this.enableSingleSelect) {
				const index = event.selectedRowIndex;
				this.model.selectAll(false);
				this.model.selectRow(index);
			}
		} else {
			this.model.selectRow(event.deselectedRowIndex, false);
			this.deselectRow.emit(event);
		}
	}

	onRowClick(index: number) {
		this.rowClick.emit(index);
	}

	updateSelectAllCheckbox() {
		const selectedRowsCount = this.model.selectedRowsCount();

		if (selectedRowsCount <= 0) {
			// reset select all checkbox if nothing selected
			this.selectAllCheckbox = false;
			this.selectAllCheckboxSomeSelected = false;
		} else if (selectedRowsCount < this.model.data.length) {
			this.selectAllCheckbox = false;
			this.selectAllCheckboxSomeSelected = true;
		} else {
			this.selectAllCheckbox = true;
			this.selectAllCheckboxSomeSelected = false;
		}
	}

	resetTabIndex(newTabIndex = -1) {
		// ensure the view is ready for the reset before we preform the actual reset
		setTimeout(() => {
			// reset all the tabIndexes we can find
			const focusElementList = getFocusElementList(this.elementRef.nativeElement, tabbableSelectorIgnoreTabIndex);
			if (focusElementList) {
				focusElementList.forEach(tabbable => {
					tabbable.tabIndex = newTabIndex;
				});
			}
			// reset interaction model positions and tabIndexes
			if (this.interactionModel) {
				this.interactionModel.resetTabIndexes(newTabIndex);
			}
		});
	}

	columnResizeStart(event, column) {
		this.columnResizeWidth = parseInt(column.style.width, 10);
		this.columnResizeMouseX = event.clientX;
		event.preventDefault();

		this.mouseMoveSubscription = fromEvent(document.body, "mousemove").subscribe(event => {
			this.columnResizeProgress(event, column);
		});
		this.mouseUpSubscription = fromEvent(document.body, "mouseup").subscribe(event => {
			this.columnResizeEnd(event, column);
		});
	}

	columnResizeProgress(event, column) {
		const move = event.clientX - this.columnResizeMouseX;
		column.style.width = `${this.columnResizeWidth + move}px`;
	}

	columnResizeEnd(event, column) {
		this.mouseMoveSubscription.unsubscribe();
		this.mouseUpSubscription.unsubscribe();
	}

	/**
	 * Triggered when the user scrolls on the `<tbody>` element.
	 * Emits the `scrollLoad` event.
	 */
	onScroll(event) {
		const distanceFromBottom = event.target.scrollHeight - event.target.clientHeight - event.target.scrollTop;

		if (distanceFromBottom <= this.scrollLoadDistance) {
			this.scrollLoad.emit(this.model);
		} else {
			this.model.isEnd = false;
		}
	}

	columnDragStart(event, columnIndex) {
		this.isColumnDragging = true;
		this.columnDraggedHoverIndex = columnIndex;
		event.dataTransfer.setData("columnIndex", JSON.stringify(columnIndex));
	}

	columnDragEnd(event, columnIndex) {
		this.isColumnDragging = false;
		this.columnDraggedHoverIndex = -1;
	}

	columnDragEnter(event, position, columnIndex) {
		this.columnDraggedPosition = position;
		this.columnDraggedHoverIndex = columnIndex;
	}

	columnDragLeave(event, position, columnIndex) {
		this.columnDraggedPosition = "";
	}

	columnDragover(event, position, columnIndex) {
		this.columnDraggedHoverIndex = columnIndex;
		this.columnDraggedPosition = position;

		// needed to tell browser to allow dropping
		event.preventDefault();
	}

	columnDrop(event, position, columnIndex) {
		this.isColumnDragging = false;
		this.columnDraggedHoverIndex = -1;
		this.columnDraggedPosition = "";

		this.model.moveColumn(
			parseInt(event.dataTransfer.getData("columnIndex"), 10),
			columnIndex + (position === "right" ? 1 : 0)
		);
	}

	/**
	 * Triggered when the user scrolls on the `<tbody>` element.
	 * Emits the `scrollLoad` event.
	 */
	scrollToTop(event) {
		event.target.parentElement.parentElement.parentElement.parentElement.children[1].scrollTop = 0;
		this.model.isEnd = false;
	}

	getSelectionLabelValue(row: TableItem[]) {
		if (!this.selectionLabelColumn) {
			return { value: this.i18n.get().TABLE.ROW };
		}
		return { value: row[this.selectionLabelColumn].data };
	}

	getExpandButtonAriaLabel() {
		return this._expandButtonAriaLabel.subject;
	}
	getSortDescendingLabel() {
		return this._sortDescendingLabel.subject;
	}
	getSortAscendingLabel() {
		return this._sortAscendingLabel.subject;
	}

	getCheckboxHeaderLabel() {
		return this._checkboxHeaderLabel.subject;
	}

	getCheckboxRowLabel() {
		return this._checkboxRowLabel.subject;
	}

	getEndOfDataText() {
		return this._endOfDataText.subject;
	}

	getScrollTopText() {
		return this._scrollTopText.subject;
	}

	getFilterTitle() {
		return this._filterTitle.subject;
	}

}

result-matching ""

    No results matching ""