@platform/ui.datagrid
Version:
Isolated tabular DataGrid.
138 lines (137 loc) • 5 kB
JavaScript
import '../../styles';
import * as React from 'react';
import { Subject } from 'rxjs';
import { debounceTime, filter, takeUntil } from 'rxjs/operators';
import { FactoryManager } from '../../factory';
import * as render from '../../render';
import { constants, containsFocus, css, defaultValue, events, Handsontable as TableLib, } from '../common';
import { getSettings } from '../settings';
import { DataGridOverlay } from './DataGrid.Overlay';
const { CSS } = constants;
export class DataGrid extends React.PureComponent {
constructor() {
super(...arguments);
this.state = {};
this.unmounted$ = new Subject();
this.state$ = new Subject();
this.elRef = (ref) => (this.el = ref);
}
componentDidMount() {
const grid = (this.grid = this.props.grid);
this.unmounted$.subscribe(() => grid.dispose());
this.state$.pipe(takeUntil(this.unmounted$)).subscribe((e) => this.setState(e));
const settings = getSettings({ grid });
const Table = this.props.Handsontable || TableLib;
const table = (this.table = new Table(this.el, settings));
grid.initialize({ table });
const factory = (this.factory = new FactoryManager({ grid, factory: this.props.factory }));
render.registerAll(Table, grid, factory);
const refs = {
grid,
editorEvents$: new Subject(),
factory: this.factory,
};
table.__gridRefs = refs;
const { events$, keyboard$ } = grid;
const editor$ = refs.editorEvents$.pipe(takeUntil(this.unmounted$));
const bubble$ = this.props.events$;
if (bubble$) {
events$.subscribe((e) => bubble$.next(e));
}
editor$.subscribe((e) => this.grid.fire(e));
events$
.pipe(filter((e) => e.type === 'GRID/redraw'), debounceTime(0))
.subscribe(() => this.redraw());
keyboard$
.pipe(filter((e) => e.metaKey && e.key === 'a'), filter((e) => this.props.canSelectAll !== true), filter((e) => !grid.isEditing))
.subscribe((e) => e.cancel());
this.updateSize();
events.resize$.pipe(takeUntil(this.unmounted$)).subscribe(() => this.redraw());
const hot = module.hot;
if (hot) {
hot.dispose(() => this.dispose());
}
events.focus$
.pipe(takeUntil(this.unmounted$), debounceTime(0), filter((e) => e.to !== document.body), filter((e) => !containsFocus(this)))
.subscribe((e) => this.grid.deselect());
this.init();
}
async init() {
if (this.isDisposed) {
return;
}
const { initial = {} } = this.props;
const grid = this.grid;
if (initial.selection) {
const selection = typeof initial.selection === 'string' ? { cell: initial.selection } : initial.selection;
const { cell, ranges } = selection;
grid.select({ cell, ranges });
}
await grid.calc.update();
const cells = grid.data.cells;
grid.changeCells(cells, { init: true, silent: true });
grid.mergeCells({ cells, init: true });
grid.fire({ type: 'GRID/ready', payload: { grid } });
this.forceUpdate();
}
componentWillUnmount() {
this.dispose();
}
dispose() {
this.unmounted$.next();
this.unmounted$.complete();
}
get isDisposed() {
return this.unmounted$.isStopped || this.grid.isDisposed;
}
get isReady() {
return this.grid ? this.grid.isReady : false;
}
get events$() {
return this.grid.events$;
}
focus(isFocused) {
if (defaultValue(isFocused, true)) {
this.grid.focus();
}
else {
this.grid.blur();
}
return this;
}
redraw() {
this.updateSize();
if (this.table) {
this.table.render();
}
return this;
}
updateSize() {
const el = this.el;
if (!el || this.isDisposed) {
return;
}
const { offsetWidth: width, offsetHeight: height } = el;
const size = { width, height };
this.state$.next({ size });
return this;
}
render() {
const { factory } = this.props;
const grid = this.grid;
const styles = {
base: css({
position: 'relative',
overflow: 'hidden',
}),
grid: css({
Absolute: 0,
userSelect: 'none',
visibility: this.isReady ? 'visible' : 'hidden',
}),
};
return (React.createElement("div", Object.assign({}, css(styles.base, this.props.style)),
React.createElement("div", Object.assign({ ref: this.elRef, className: CSS.CLASS.GRID.BASE }, styles.grid)),
grid && React.createElement(DataGridOverlay, { grid: grid, factory: factory })));
}
}