@platform/cell.typesystem
Version:
The 'strongly typed sheets' system of the CellOS.
176 lines (175 loc) • 5.57 kB
JavaScript
import { coord, Uri } from './common';
import { TypedSheetRow } from './TypedSheetRow';
export class TypedSheetData {
constructor(args) {
this._rows = [];
this._status = 'INIT';
this._total = -1;
this._loading = [];
this._isLoaded = false;
this._sheet = args.sheet;
this.typename = args.typename;
this.types = args.types;
this._ctx = args.ctx;
this._range = TypedSheetData.formatRange(args.range);
}
static formatRange(input) {
const text = (input || '').trim();
const DEFAULT = TypedSheetData.DEFAULT;
if (!text) {
return DEFAULT.RANGE;
}
const range = coord.range.fromKey(text);
if (!range.isValid) {
return DEFAULT.RANGE;
}
const left = {
key: range.left.key,
index: range.left.row,
isInfinity: isInfinity(range.left.key),
};
const right = {
key: range.right.key,
index: range.right.row,
isInfinity: isInfinity(range.right.key),
};
if (left.isInfinity && right.isInfinity) {
return DEFAULT.RANGE;
}
if (left.isInfinity) {
left.index = DEFAULT.PAGE - 1;
}
if (right.isInfinity) {
right.index = DEFAULT.PAGE - 1;
}
const edges = [Math.max(0, left.index) + 1, Math.max(0, right.index) + 1].sort(diff);
return `${edges[0]}:${edges[1]}`;
}
get uri() {
return this._sheet.uri;
}
get rows() {
return this._rows;
}
get range() {
return this._range;
}
get status() {
return this._status;
}
get isLoaded() {
return this._isLoaded;
}
get total() {
return this._total;
}
toString() {
return this.uri.toString();
}
exists(index) {
return Boolean(this._rows[index]);
}
row(index) {
if (index < 0) {
throw new Error(`Row index must be >=0`);
}
if (!this.exists(index)) {
this._rows[index] = this.createRow(index);
}
return this._rows[index];
}
async load(args) {
var _a;
let argRange = typeof args === 'string' ? args : (_a = args) === null || _a === void 0 ? void 0 : _a.range;
if (argRange) {
argRange = this.expandRange(argRange);
}
const query = argRange || this.range;
const alreadyLoading = this._loading.find(item => item.query === query);
if (alreadyLoading) {
return alreadyLoading.promise;
}
const ns = this.uri.toString();
const promise = new Promise(async (resolve, reject) => {
this._status = 'LOADING';
const sheet = this._sheet;
this.fire({
type: 'SHEET/loading',
payload: { sheet, range: query },
});
const { total, error } = await this._ctx.fetch.getCells({ ns, query });
if (error) {
reject(new Error(error.message));
}
const range = coord.range.fromKey(query);
const min = Math.max(0, range.left.row);
const max = Math.min(total.rows - 1, range.right.row);
const wait = Array.from({ length: max - min + 1 }).map((v, i) => {
const index = i + min;
return this.row(index).load();
});
await Promise.all(wait);
this._total = total.rows;
this._status = 'LOADED';
this._isLoaded = true;
this._loading = this._loading.filter(item => item.query !== query);
this.fire({
type: 'SHEET/loaded',
payload: {
sheet,
range: this.range,
total: this.total,
},
});
return resolve(this);
});
this._loading = [...this._loading, { query, promise }];
return promise;
}
filter(fn) {
return this.rows.filter((row, i) => fn(row.props, i));
}
find(fn) {
return this.rows.find((row, i) => fn(row.props, i));
}
map(fn) {
return this.rows.map((row, i) => fn(row.props, i));
}
forEach(fn) {
this.rows.forEach((row, i) => fn(row.props, i));
}
expandRange(range) {
range = TypedSheetData.formatRange(range);
this._range = this.isLoaded
? mergeRanges(range, this._range)
: range;
return this._range;
}
fire(e) {
this._ctx.event$.next(e);
}
createRow(index) {
const uri = Uri.create.row(this.uri.toString(), (index + 1).toString());
const columns = this.types;
const ctx = this._ctx;
const typename = this.typename;
const sheet = this._sheet;
return TypedSheetRow.create({ sheet, typename, uri, columns, ctx });
}
}
TypedSheetData.create = (args) => {
return new TypedSheetData(args);
};
TypedSheetData.DEFAULT = {
RANGE: '1:500',
PAGE: 500,
};
const diff = (a, b) => a - b;
const isInfinity = (input) => input === '*' || input === '**';
const mergeRanges = (input1, input2) => {
const range1 = coord.range.fromKey(input1).square;
const range2 = coord.range.fromKey(input2).square;
const min = Math.min(0, range1.left.row, range2.left.row) + 1;
const max = Math.max(range1.right.row, range2.right.row) + 1;
return `${min}:${max}`;
};