@sparser/au2-data-grid
Version:
A data grid for Aurelia 2
269 lines • 9.85 kB
JavaScript
import { DI, } from '@aurelia/kernel';
import { ViewFactory, } from '@aurelia/runtime-html';
// eslint-disable-next-line @typescript-eslint/naming-convention
export const IGridStateModel = DI.createInterface('IGridStateModel');
/**
* This aggregates the structural metadata for the grid.
* This is meant for internal use.
* @internal
*/
export class GridStateModel {
_activeSortOptions = null;
/**
* @internal
*/
subscribers = [];
/** @internal */
viewFactoriesCreated = false;
columns = [];
get activeSortOptions() {
return this._activeSortOptions;
}
/**
* Exports the grid state.
*/
export() {
return {
columns: this.columns.map((c) => c.export())
};
}
/**
* Applies a previously exported state.
* @param {ExportableGridState} state The state to apply.
*/
applyState(state) {
const columns = this.columns;
const stateColumns = state.columns;
const len = stateColumns.length;
for (let i = 0; i < len; i++) {
const stateColumn = stateColumns[i];
const colIndex = columns.findIndex(c => c.id === stateColumn.id);
if (colIndex === -1)
continue;
const column = columns[colIndex];
if (!column.tryApplyState(stateColumn) || colIndex === i)
continue;
// move column
columns.splice(colIndex, 1);
columns.splice(i, 0, column);
}
}
/**
* Marks the hidden columns as per the given column ids.
* Note that in absence of a `id` attribute in `grid-column`, the `property` is used as the `id`.
* @param {string[]} columnIds The collection of ids of the columns to hide.
*/
hideColumns(columnIds) {
const len = columnIds?.length ?? 0;
if (len === 0)
return;
const columns = this.columns;
for (let i = 0; i < len; i++) {
const id = columnIds[i];
const col = columns.find(c => c.id === id);
if (col === undefined)
continue;
col.hidden = true;
}
}
/**
* Creates the view factories for every column using the given `container`.
*/
createViewFactories(container) {
if (this.viewFactoriesCreated)
return;
const columns = this.columns;
const len = columns.length;
for (let i = 0; i < len; i++) {
columns[i].createViewFactories(container);
}
this.viewFactoriesCreated = true;
}
/**
* Initializes the sort options.
* To this end the first column with non-null direction is used .
*/
initializeActiveSortOptions() {
const column = this.columns.find(c => c.direction !== null);
if (column == null)
return null;
return this._activeSortOptions = {
property: column.property,
direction: column.direction,
};
}
/**
* Adds the given `subscriber` to the collection of subscribers.
* The subscribers will be notified for state changes.
*/
addSubscriber(subscriber) {
this.subscribers.push(subscriber);
}
/**
* Removes the given `subscriber` from the collection of subscribers.
* The subscriber won't be notified for any further state changes.
*/
removeSubscriber(subscriber) {
const subscribers = this.subscribers;
const idx = subscribers.findIndex(s => s === subscriber);
if (idx === -1)
return;
subscribers.splice(idx, 1);
}
notifySubscribers(type, newValue, oldValue) {
const subscribers = this.subscribers;
const len = subscribers.length;
for (let i = 0; i < len; i++) {
subscribers[i].handleGridStateChange(type, newValue, oldValue);
}
}
handleChange(type, columnOrId, destination, location) {
switch (type) {
case 1 /* ChangeType.Sort */: {
const oldSortOptions = this._activeSortOptions;
const oldProperty = oldSortOptions?.property;
const newProperty = columnOrId.property;
if (oldProperty !== newProperty) {
// this is needed so that change to the old sort column can be propagated to the view.
this.columns
.find(c => c.property === oldProperty)
?.setDirection(null, false);
}
const newSortOptions = this._activeSortOptions = { property: newProperty, direction: columnOrId.direction };
this.notifySubscribers(type, newSortOptions, oldSortOptions);
return;
}
case 2 /* ChangeType.Order */: {
const columns = this.columns;
const sourceIndex = columns.findIndex(c => c.id === columnOrId);
const destinationIndex = columns.findIndex(c => c === destination);
const diff = destinationIndex - sourceIndex;
if (diff === 1 && location === 1 /* OrderChangeDropLocation.Before */
|| diff === -1 && location === 2 /* OrderChangeDropLocation.After */)
return;
let $destinationIndex = destinationIndex;
if (sourceIndex < destinationIndex && location === 1 /* OrderChangeDropLocation.Before */) {
$destinationIndex--;
}
else if (sourceIndex > destinationIndex && location === 2 /* OrderChangeDropLocation.After */) {
$destinationIndex++;
}
columns.splice($destinationIndex, 0, columns.splice(sourceIndex, 1)[0]);
this.notifySubscribers(type, { fromIndex: sourceIndex, toIndex: destinationIndex, location }, null);
return;
}
case 3 /* ChangeType.Width */:
this.notifySubscribers(type);
return;
default:
throw new Error(`Unsupported change type: ${String(type)}.`);
}
}
}
/**
* This describes the structural metadata of a column.
* This is meant for internal use.
* @internal
*/
export class Column {
parent;
id;
property;
exportable;
isResizable;
widthPx;
header;
content;
static id = 0;
static generateId() { return `unnamed-column-${++this.id}`; }
/** @internal */
_sortable = false;
/** @internal */
_direction = null;
/** @internal */
_headerViewFactory = null;
/** @internal */
_contentViewFactory = null;
/**
* This is registered from inside the grid-header CE during `binding`.
* @internal
*/
headerElement;
hidden = false;
constructor(parent, id, property, exportable, direction, isResizable, widthPx, header, content) {
this.parent = parent;
this.id = id;
this.property = property;
this.exportable = exportable;
this.isResizable = isResizable;
this.widthPx = widthPx;
this.header = header;
this.content = content;
if (!id)
throw new Error('Cannot instantiate ColumnState; expected non-null, non-undefined, non-empty string for id.');
if (property !== null) {
if (property.length === 0)
throw new Error('Cannot instantiate ColumnState; expected non-empty property.');
this._sortable = true;
}
else {
direction = null;
}
this._direction = direction;
parent.columns.push(this);
}
get direction() { return this._direction; }
get sortable() { return this._sortable; }
get headerViewFactory() { return this._headerViewFactory; }
get contentViewFactory() { return this._contentViewFactory; }
/** @internal */
setDirection(direction, notifyParent) {
if (!this._sortable)
throw new Error(`The column '${this.id}' is not sortable.`);
this._direction = direction;
if (notifyParent) {
this.parent.handleChange(1 /* ChangeType.Sort */, this);
}
}
export() {
if (!this.exportable)
throw new Error(`The column '${this.id}' is not exportable.`);
return {
id: this.id,
property: this.property,
direction: this._direction,
isResizable: this.isResizable,
widthPx: this.widthPx,
};
}
/** @internal */
tryApplyState(state) {
if (this.id !== state.id || this.property !== state.property)
return false;
this._direction = state.direction;
this.widthPx = state.widthPx;
return true;
}
createViewFactories(container) {
// invocation is expected once during pre-binding stage
if (this._headerViewFactory !== null && this._contentViewFactory !== null)
return;
this._headerViewFactory = new ViewFactory(container, this.header);
this._contentViewFactory = new ViewFactory(container, this.content);
}
}
export var ChangeType;
(function (ChangeType) {
/** Content sorting is changed. */
ChangeType[ChangeType["Sort"] = 1] = "Sort";
/** Column is reordered. */
ChangeType[ChangeType["Order"] = 2] = "Order";
/** Width of a column is changed. */
ChangeType[ChangeType["Width"] = 3] = "Width";
})(ChangeType || (ChangeType = {}));
export var OrderChangeDropLocation;
(function (OrderChangeDropLocation) {
OrderChangeDropLocation[OrderChangeDropLocation["Before"] = 1] = "Before";
OrderChangeDropLocation[OrderChangeDropLocation["After"] = 2] = "After";
})(OrderChangeDropLocation || (OrderChangeDropLocation = {}));
//# sourceMappingURL=grid-state.js.map