UNPKG

simple-datatables

Version:

A lightweight, dependency-free JavaScript HTML table plugin.

208 lines (171 loc) 6.09 kB
import {DataTable} from "../datatable" import {classNamesToSelector, createElement} from "../helpers" import { defaultConfig } from "./config" import {ColumnFilterOptions} from "./types" class ColumnFilter { addedButtonDOM: boolean menuOpen: boolean buttonDOM: HTMLElement dt: DataTable events: { [key: string]: () => void} initialized: boolean options: ColumnFilterOptions menuDOM: HTMLElement containerDOM: HTMLElement wrapperDOM: HTMLElement limits: {x: number, y: number} rect: {width: number, height: number} event: Event constructor(dataTable: DataTable, options = {}) { this.dt = dataTable this.options = { ...defaultConfig, ...options } } init() { if (this.initialized) { return } const buttonSelector = classNamesToSelector(this.options.classes.button) let buttonDOM : (HTMLElement | null) = this.dt.wrapperDOM.querySelector(buttonSelector) if (!buttonDOM) { buttonDOM = createElement( "button", { class: this.options.classes.button, html: "▦" } ) // filter button not part of template (could be default template. We add it to search.) const searchSelector = classNamesToSelector(this.dt.options.classes.search) const searchWrapper = this.dt.wrapperDOM.querySelector(searchSelector) if (searchWrapper) { searchWrapper.appendChild(buttonDOM) } else { this.dt.wrapperDOM.appendChild(buttonDOM) } this.addedButtonDOM = true } this.buttonDOM = buttonDOM this.containerDOM = createElement("div", { id: this.options.classes.container }) this.wrapperDOM = createElement("div", { class: this.options.classes.wrapper }) this.menuDOM = createElement("ul", { class: this.options.classes.menu, html: this.dt.data.headings.map( (heading, index) => { const settings = this.dt.columns.settings[index] if (this.options.hiddenColumns.includes(index)) { return "" } return `<li data-column="${index}"> <input type="checkbox" value="${heading.text || heading.data}" ${settings.hidden ? "" : "checked=''"}> <label> ${heading.text || heading.data} </label> </li>` } ).join("") }) this.wrapperDOM.appendChild(this.menuDOM) this.containerDOM.appendChild(this.wrapperDOM) this._measureSpace() this._bind() this.initialized = true } dismiss() { if (this.addedButtonDOM && this.buttonDOM.parentElement) { this.buttonDOM.parentElement.removeChild(this.buttonDOM) } document.removeEventListener("click", this.events.click) } _bind() { this.events = { click: this._click.bind(this) } document.addEventListener("click", this.events.click) } _openMenu() { document.body.appendChild(this.containerDOM) this._measureSpace() this.menuOpen = true this.dt.emit("columnFilter.menu.open") } _closeMenu() { if (this.menuOpen) { this.menuOpen = false document.body.removeChild(this.containerDOM) this.dt.emit("columnFilter.menu.close") } } _measureSpace() { const scrollX = window.scrollX || window.pageXOffset const scrollY = window.scrollY || window.pageYOffset this.rect = this.wrapperDOM.getBoundingClientRect() this.limits = { x: window.innerWidth + scrollX - this.rect.width, y: window.innerHeight + scrollY - this.rect.height } } _click(event: MouseEvent) { const target = event.target if (!(target instanceof Element)) { return } this.event = event if (this.buttonDOM.contains(target)) { event.preventDefault() if (this.menuOpen) { this._closeMenu() return } this._openMenu() // get the mouse position let x = event.pageX let y = event.pageY // check if we're near the right edge of window if (x > this.limits.x) { x -= this.rect.width } // check if we're near the bottom edge of window if (y > this.limits.y) { y -= this.rect.height } this.wrapperDOM.style.top = `${y}px` this.wrapperDOM.style.left = `${x}px` } else if (this.menuDOM.contains(target)) { const menuSelector = classNamesToSelector(this.options.classes.menu) const li = target.closest(`${menuSelector} > li`) as HTMLElement if (!li) { return } const checkbox = li.querySelector("input[type=checkbox]") as HTMLInputElement if (!checkbox.contains(target)) { checkbox.checked = !checkbox.checked } const column = Number(li.dataset.column) if (checkbox.checked) { this.dt.columns.show([column]) } else { this.dt.columns.hide([column]) } } else if (this.menuOpen) { this._closeMenu() } } } export const addColumnFilter = function(dataTable: DataTable, options = {}) { const columnFilter = new ColumnFilter(dataTable, options) if (dataTable.initialized) { columnFilter.init() } else { dataTable.on("datatable.init", () => columnFilter.init()) } return columnFilter }