@sparser/au2-data-grid
Version:
A data grid for Aurelia 2
280 lines • 14.4 kB
JavaScript
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
var _, done = false;
for (var i = decorators.length - 1; i >= 0; i--) {
var context = {};
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
if (kind === "accessor") {
if (result === void 0) continue;
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
if (_ = accept(result.get)) descriptor.get = _;
if (_ = accept(result.set)) descriptor.set = _;
if (_ = accept(result.init)) initializers.unshift(_);
}
else if (_ = accept(result)) {
if (kind === "field") initializers.unshift(_);
else descriptor[key] = _;
}
}
if (target) Object.defineProperty(target, contextIn.name, descriptor);
done = true;
};
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
var useValue = arguments.length > 2;
for (var i = 0; i < initializers.length; i++) {
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
}
return useValue ? value : void 0;
};
import { ILogger, resolve, } from '@aurelia/kernel';
import { bindable, CustomElement, CustomElementDefinition, IHydrationContext, INode, BindingMode, ISignaler, } from '@aurelia/runtime-html';
import { ItemSelectionMode, } from './content-model.js';
import template from './data-grid.html';
import { DefaultGridHeader, } from './grid-header.js';
import { Column, GridStateModel, } from './grid-state.js';
import { SortDirection, } from './sorting-options.js';
import { GridContent, GridHeaders, } from './template-controllers.js';
const ascPattern = /^asc$|^ascending$/i;
const descPattern = /^desc$|^descending$/i;
const stateLookup = new Map();
/**
* Default implementation of the data-grid.
*/
let DataGrid = (() => {
let _model_decorators;
let _model_initializers = [];
let _model_extraInitializers = [];
let _state_decorators;
let _state_initializers = [];
let _state_extraInitializers = [];
let _itemClicked_decorators;
let _itemClicked_initializers = [];
let _itemClicked_extraInitializers = [];
let _hiddenColumns_decorators;
let _hiddenColumns_initializers = [];
let _hiddenColumns_extraInitializers = [];
return class DataGrid {
static {
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
_model_decorators = [bindable];
_state_decorators = [bindable];
_itemClicked_decorators = [bindable];
_hiddenColumns_decorators = [bindable({ mode: BindingMode.oneTime })];
__esDecorate(null, null, _model_decorators, { kind: "field", name: "model", static: false, private: false, access: { has: obj => "model" in obj, get: obj => obj.model, set: (obj, value) => { obj.model = value; } }, metadata: _metadata }, _model_initializers, _model_extraInitializers);
__esDecorate(null, null, _state_decorators, { kind: "field", name: "state", static: false, private: false, access: { has: obj => "state" in obj, get: obj => obj.state, set: (obj, value) => { obj.state = value; } }, metadata: _metadata }, _state_initializers, _state_extraInitializers);
__esDecorate(null, null, _itemClicked_decorators, { kind: "field", name: "itemClicked", static: false, private: false, access: { has: obj => "itemClicked" in obj, get: obj => obj.itemClicked, set: (obj, value) => { obj.itemClicked = value; } }, metadata: _metadata }, _itemClicked_initializers, _itemClicked_extraInitializers);
__esDecorate(null, null, _hiddenColumns_decorators, { kind: "field", name: "hiddenColumns", static: false, private: false, access: { has: obj => "hiddenColumns" in obj, get: obj => obj.hiddenColumns, set: (obj, value) => { obj.hiddenColumns = value; } }, metadata: _metadata }, _hiddenColumns_initializers, _hiddenColumns_extraInitializers);
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
}
static id = 0;
/**
* The content model (data).
*/
model = __runInitializers(this, _model_initializers, void 0);
/**
* Any bound state is read only once in the binding stage.
* Any 'incoming' changes from the consumer side thereafter is disregarded.
* In the post-binding phase this property is treated as a write-only property to provide the consumer side with any changes in the exportable grid state.
*/
state = (__runInitializers(this, _model_extraInitializers), __runInitializers(this, _state_initializers, void 0));
/**
* Callback when a item is
* - clicked with the 'None' selection mode, or
* - double-clicked with 'Single' or 'Multiple' selection mode.
*/
itemClicked = (__runInitializers(this, _state_extraInitializers), __runInitializers(this, _itemClicked_initializers, void 0));
/**
* This is a one-time bindable array of string columnIds that needs to be hidden from the current instance of the grid.
*/
hiddenColumns = (__runInitializers(this, _itemClicked_extraInitializers), __runInitializers(this, _hiddenColumns_initializers, []));
stateModel = __runInitializers(this, _hiddenColumns_extraInitializers);
$controller; // This is set by the controller after this instance is constructed
containerEl;
lastClickedRow = null;
selectionUpdateSignal = '';
hydrationContext = resolve(IHydrationContext);
node = resolve(INode);
signaler = resolve(ISignaler);
logger = resolve(ILogger).scopeTo('DataGrid');
created(controller) {
const instanceIdStr = this.node.dataset.instanceId;
const instanceId = Number(instanceIdStr);
if (!Number.isInteger(instanceId))
throw new Error(`Invalid data grid instanceId: ${instanceIdStr}; expected integer.`);
this.selectionUpdateSignal = `update-selection-${instanceIdStr}`;
const state = stateLookup.get(Number(instanceId));
if (state === undefined)
throw new Error(`Cannot find the model for the instance #${instanceIdStr}`);
this.stateModel = state;
const container = this.hydrationContext
.controller
.container
.createChild({ inheritParentResources: true })
.register(controller.definition.dependencies);
state.createViewFactories(container);
this.node.style.setProperty('--num-columns', state.columns.length.toString());
}
binding() {
const stateModel = this.stateModel;
const state = this.state;
if (state != null) {
stateModel.applyState(state);
}
stateModel.hideColumns(this.hiddenColumns);
const sortingOptions = stateModel.initializeActiveSortOptions();
if (sortingOptions !== null) {
this.model.applySorting(sortingOptions);
}
stateModel.addSubscriber(this);
}
attaching() {
this.adjustColumnWidth();
}
unbinding() {
this.stateModel.removeSubscriber(this);
}
exportState() {
try {
return this.state = this.stateModel.export();
}
catch (e) {
this.logger.warn(e.message);
}
}
handleGridStateChange(type, newValue, _oldValue) {
switch (type) {
case 1 /* ChangeType.Sort */:
this.model.applySorting(newValue);
break;
case 3 /* ChangeType.Width */:
case 2 /* ChangeType.Order */:
this.adjustColumnWidth();
break;
}
this.exportState();
}
adjustColumnWidth() {
const columns = this.stateModel.columns;
const fallback = columns.some(c => c.widthPx != null) ? 'auto' : '1fr';
this.containerEl.style.gridTemplateColumns = columns.map(c => `minmax(0px, ${c.widthPx ?? fallback})`).join(' ');
}
handleDblClick(item, index) {
getSelection()?.empty();
this.itemClicked?.({ item, index });
this.lastClickedRow = index;
}
handleClick(event, item, index) {
getSelection()?.empty();
const model = this.model;
switch (model.selectionMode) {
case ItemSelectionMode.None:
this.itemClicked?.({ item, index });
break;
case ItemSelectionMode.Single:
model.selectItem(item);
break;
case ItemSelectionMode.Multiple:
if (event.shiftKey) {
const lastClickedRow = this.lastClickedRow;
if (lastClickedRow !== null) {
model.selectRange(lastClickedRow, index);
}
else {
model.selectItem(item);
}
}
else if (event.ctrlKey) {
model.toggleSelection(item);
}
else {
model.clearSelections();
model.selectItem(item);
}
break;
}
this.lastClickedRow = index;
this.signaler.dispatchSignal(this.selectionUpdateSignal);
}
static processContent(content, platform) {
const columns = content.querySelectorAll('grid-column');
const numColumns = columns.length;
const state = new GridStateModel();
const doc = platform.document;
for (let i = 0; i < numColumns; i++) {
const col = columns[i];
// extract metadata
let isExportable = true;
const property = col.getAttribute('property');
const id = col.getAttribute('id') ?? property ?? (isExportable = false, Column.generateId());
const directionRaw = col.getAttribute('sort-direction');
let direction = null;
if (directionRaw !== null) {
if (ascPattern.test(directionRaw)) {
direction = SortDirection.Ascending;
}
else if (descPattern.test(directionRaw)) {
direction = SortDirection.Descending;
}
}
const isResizable = !col.hasAttribute('non-resizable');
let width = null;
if (isResizable) {
width = col.getAttribute('width');
width = width === null || Number.isNaN(width) ? null : `${width}px`;
}
// extract header
let container = doc.createElement('grid-header');
container.setAttribute('state.bind', '');
const header = col.querySelector('header');
const headerContent = header?.childNodes;
const projection = doc.createElement('template');
projection.setAttribute('au-slot', 'default');
projection.content.append(...(headerContent !== undefined
? Array.from(headerContent)
: [doc.createTextNode(`Column ${i + 1}`)]));
container.append(projection);
const headerDfn = CustomElementDefinition.create({ name: CustomElement.generateName(), template: container });
header?.remove();
// extract content
container = doc.createElement('div');
container.setAttribute('role', 'cell');
container.append(...Array.from(col.childNodes));
const contentDfn = CustomElementDefinition.create({ name: CustomElement.generateName(), template: container });
void new Column(state, id, property, isExportable, direction, isResizable, width, headerDfn, contentDfn);
col.remove();
}
const id = ++this.id;
stateLookup.set(id, state);
content.setAttribute('data-instance-id', id.toString());
}
};
})();
export { DataGrid };
// eslint-disable-next-line @typescript-eslint/naming-convention
export const DefaultDataGrid = defineDataGridCustomElement(DefaultGridHeader);
/**
* Creates data-grid custom element registration.
* @param {CustomElementType<THeader>} header The grid-header custom element registration.
* @returns {CustomElementType<Constructable<DataGrid>>} Data grid custom element registration.
* @template THeader
*/
export function defineDataGridCustomElement(header) {
return CustomElement.define({
name: 'data-grid',
template,
dependencies: [
// TCs
GridHeaders,
GridContent,
//CEs
header,
]
}, DataGrid);
}
//# sourceMappingURL=data-grid.js.map