UNPKG

ractive-ez-table

Version:
302 lines (242 loc) 9.39 kB
/* ----------------------------------------------- | Header 1 | Header 2 | Header 3 | Header 4 | | [ ] | [ ] | [filter] | [ ] | |-----------------------------------------------| | Grouping 1a | | Grouping 2a | | Cell 1 | Cell 2 | Cell 3 | Cell 4 | | Cell 1 | Cell 2 | Cell 3 | Cell 4 | | Grouping 2b | | Cell 1 | Cell 2 | Cell 3 | Cell 4 | | Cell 1 | Cell 2 | Cell 3 | Cell 4 | ----------------------------------------------- Interactions: 1. Header => drag/drop column to change column order 2. Header => click column to change sort direction 3. Header => enter filter text to filter on a property (search type depends on first character? < | > | ^ | $ | = ) 4. Header => drag handle to resize 4. Grouping => click to collapse 5. Row => drag/drop selections! editing? Components: EzTableHeader EzTableGroup EzTableRow */ import Ractive from 'ractive'; import 'ractive-ez-core'; import 'ractive-ez-resize'; import utils from './utils.js'; import settings from './settings.js'; import EzTableHeader from './EzTableHeader.js'; import EzTableContent from './EzTableContent.js'; import groupBy from './groupBy.js'; import Virtualization from './Virtualization.js'; import NoCursor from './cursors/SelectionCursor.None.js'; import SingleCursor from './cursors/SelectionCursor.Single.js'; import MultiCursor from './cursors/SelectionCursor.Multi.js'; import './EzTable.less'; const EzTable = Ractive.components.EzTable = Ractive.extend({ template: require("./EzTable.html"), _scrollHandler: null, _isRendered: false, components: { EzTableHeader, EzTableContent }, data() { return { items: null, selectedItems: [], columns: null, groups: null, sortColumn: null, itemHeight: 24, labelHeight: 24, virtualBuffer: 144, _root: null, _selectionCursor: null, _isValidDropTarget: false, _viewport: { top: 0, height: 0 }, visible(columns, groups = []) { return columns.filter(column => column.isVisible && groups.indexOf(column) == -1); }, columnContentType: settings.columnContentType, selectionMode: settings.selectionMode, enableDrag: settings.enableDrag, enableDrop: settings.enableDrop, enableGrouping: settings.enableGrouping, enableSorting: settings.enableSorting, enableVirtualization: settings.enableVirtualization, showHeaderTop: settings.showHeaderTop, showHeaderBottom: settings.showHeaderBottom, dataMapper: settings.dataMapper } }, oninit() { // TODO: this seems to trigger a re-render more than necessary? this.observe("items groups columns.*.sortDirection _viewport", () => this._refreshView()); this.observe("items", () => { const items = this.get("items"); const selectedItems = this.get("selectedItems"); selectedItems .filter(item => items.indexOf(item) == -1) .forEach(item => this.splice("selectedItems", selectedItems.indexOf(item), 1)); }); this.observe("selectionMode", () => { switch (this.getSelectionMode()) { case "single": return this.set("_selectionCursor", new SingleCursor(this)); case "multi": return this.set("_selectionCursor", new MultiCursor(this)); default: return this.set("_selectionCursor", new NoCursor(this)); } }); // TODO: handle resize this._scrollHandler = utils.createScrollHandler(() => { if (this._isRendered) { const virtualBuffer = this.get("virtualBuffer"); this.set("_viewport.top", this.find(".body").scrollTop - virtualBuffer); this.set("_viewport.height", this.find(".body").offsetHeight + virtualBuffer * 2); } }); }, onrender() { this._isRendered = true; this._scrollHandler(); }, onunrender() { this._isRendered = false; }, _refreshView() { const groupings = groupBy(this.get("items"), this.get("groups"), this.get("sortColumn")); const virtualizationEnabled = this.get("enableVirtualization"); const virtualization = new Virtualization( this.get("_viewport"), this.get("labelHeight"), this.get("itemHeight")); const root = virtualization.virtualize(groupings, virtualizationEnabled); this.set("_root", root); }, getConfiguration() { const columns = this.get("columns") || []; const groups = this.get("groups") || []; const sortColumn = this.get("sortColumn") || {}; const colData = Object.create(null); const grpData = groups.map(column => column.name); columns.forEach((column, index) => { colData[column.name] = { isVisible: column.isVisible, sortDirection: !!column.sortDirection, sequence: index }; }); return { groups: grpData, columns: colData, sortColumn: sortColumn.name }; }, setConfiguration(config) { const columns = this.get("columns") || []; // Load columns if (config.columns) { columns.forEach(col => { const setting = config.columns[col.name] || {}; if ("isVisible" in setting) col.isVisible = setting.isVisible; if ("sortDirection" in setting) col.sortDirection = setting.sortDirection; }); columns.sort((a, b) => config.columns[a.name].sequence - config.columns[b.name].sequence); this.set("columns", columns); } // Load sort column if (config.sortColumn) { const sortColumn = columns.find(col => col.name == config.sortColumn); this.set("sortColumn", sortColumn); } // Load groups if (config.groups && config.groups.length) { const groups = config.groups .map(name => columns.find(col => col.name == name)) .filter(col => !!col); this.set("groups", groups); } }, getSelectionMode() { return this.get("selectionMode").toLowerCase(); }, isItemSelected(item) { return this.get("selectedItems").indexOf(item) != -1; }, selectItem(item) { if (!this.isItemSelected(item)) { this.push("selectedItems", item); } }, unselectItem(item) { const index = this.get("selectedItems").indexOf(item); if (index != -1) { this.splice("selectedItems", index, 1); } }, toggleItem(item) { this.isItemSelected(item) ? this.unselectItem(item) : this.selectItem(item); }, clearSelection() { this.splice("selectedItems", 0, this.get("selectedItems").length); }, preventSelection(event) { event.preventDefault(); return false; }, handleScroll(event) { this._scrollHandler(event); }, _handleDragStart(event, items) { const mapping = this.get("dataMapper"); const contentTypes = Object.keys(mapping); contentTypes.forEach(contentType => { const map = mapping[contentType].stringify; if (map) { const data = map(items); event.dataTransfer.setData(contentType, data); } }); this.fire("dragitem", items); }, _handleDragOver(event) { const mapping = this.get("dataMapper"); if (!mapping) { console.warn("EzTable - dataMapper is undefined"); return; } const contentTypes = Object.keys(mapping); const allowDrop = event.dataTransfer.types.some(type => mapping[type] && mapping[type].parse); this.set("_isValidDropTarget", allowDrop); if (allowDrop) { event.preventDefault(); event.dropEffect = "move"; } }, _handleDrop(event, targetItem) { const mapping = this.get("dataMapper"); const contentTypes = Object.keys(mapping); const data = Object.create(null); if (event.cancelBubble) return; event.cancelBubble = true; event.dataTransfer.types.forEach(type => { if (contentTypes.indexOf(type) != -1) { const map = mapping[type].parse; data[type] = map(event.dataTransfer.getData(type)); } }); this.fire("dropitem", data, targetItem); } }); EzTable.settings = settings; export default EzTable;