2020-04-06 14:28:15 +00:00
|
|
|
import * as React from "react";
|
2020-04-18 17:37:30 +00:00
|
|
|
import * as ReactDOM from "react-dom";
|
|
|
|
|
|
|
|
export enum BatchUpdateType {
|
|
|
|
UNSET = -1,
|
|
|
|
GENERAL = 0,
|
|
|
|
CHANNEL_TREE = 1
|
|
|
|
}
|
|
|
|
|
|
|
|
interface UpdateBatch {
|
|
|
|
enabled: boolean;
|
|
|
|
enable_count: number;
|
|
|
|
|
|
|
|
update: {
|
|
|
|
c: any;
|
|
|
|
s: any;
|
|
|
|
b: () => void;
|
|
|
|
}[];
|
|
|
|
|
|
|
|
force: {
|
|
|
|
c: any;
|
|
|
|
b: () => void;
|
|
|
|
}[];
|
|
|
|
}
|
|
|
|
|
|
|
|
const generate_batch = () => { return { enabled: false, enable_count: 0, update: [], force: [] }};
|
|
|
|
let update_batches: {[key: number]:UpdateBatch} = {
|
|
|
|
0: generate_batch(),
|
|
|
|
1: generate_batch()
|
|
|
|
};
|
2020-04-21 14:17:21 +00:00
|
|
|
(window as any).update_batches = update_batches;
|
2020-04-18 17:37:30 +00:00
|
|
|
|
|
|
|
export function BatchUpdateAssignment(type: BatchUpdateType) {
|
|
|
|
return function (constructor: Function) {
|
|
|
|
if(!ReactComponentBase.prototype.isPrototypeOf(constructor.prototype))
|
|
|
|
throw "Class/object isn't an instance of ReactComponentBase";
|
|
|
|
|
|
|
|
const didMount = constructor.prototype.componentDidMount;
|
|
|
|
constructor.prototype.componentDidMount = function() {
|
|
|
|
if(typeof this.update_batch === "undefined")
|
|
|
|
this.update_batch = update_batches[type];
|
|
|
|
|
|
|
|
if(typeof didMount === "function")
|
|
|
|
didMount.call(this, arguments);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
2020-04-06 14:28:15 +00:00
|
|
|
|
|
|
|
export abstract class ReactComponentBase<Properties, State> extends React.Component<Properties, State> {
|
2020-04-18 17:37:30 +00:00
|
|
|
private update_batch: UpdateBatch;
|
|
|
|
|
|
|
|
private batch_component_id: number;
|
|
|
|
private batch_component_force_id: number;
|
|
|
|
|
2020-04-06 14:28:15 +00:00
|
|
|
constructor(props: Properties) {
|
|
|
|
super(props);
|
2020-04-18 17:37:30 +00:00
|
|
|
this.batch_component_id = -1;
|
|
|
|
this.batch_component_force_id = -1;
|
2020-04-06 14:28:15 +00:00
|
|
|
|
2020-04-18 17:37:30 +00:00
|
|
|
this.state = this.defaultState();
|
2020-04-06 14:28:15 +00:00
|
|
|
this.initialize();
|
|
|
|
}
|
|
|
|
|
|
|
|
protected initialize() { }
|
2020-04-18 17:37:30 +00:00
|
|
|
protected defaultState() : State { return {} as State; }
|
2020-04-06 14:28:15 +00:00
|
|
|
|
2020-04-18 17:37:30 +00:00
|
|
|
setState<K extends keyof State>(
|
|
|
|
state: ((prevState: Readonly<State>, props: Readonly<Properties>) => (Pick<State, K> | State | null)) | (Pick<State, K> | State | null),
|
|
|
|
callback?: () => void
|
|
|
|
): void {
|
|
|
|
if(typeof this.update_batch !== "undefined" && this.update_batch.enabled) {
|
|
|
|
const obj = {
|
|
|
|
c: this,
|
|
|
|
s: Object.assign(this.update_batch.update[this.batch_component_id]?.s || {}, state),
|
|
|
|
b: callback
|
|
|
|
};
|
|
|
|
if(this.batch_component_id === -1)
|
|
|
|
this.batch_component_id = this.update_batch.update.push(obj) - 1;
|
|
|
|
else
|
|
|
|
this.update_batch.update[this.batch_component_id] = obj;
|
|
|
|
} else {
|
|
|
|
super.setState(state, callback);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
forceUpdate(callback?: () => void): void {
|
|
|
|
if(typeof this.update_batch !== "undefined" && this.update_batch.enabled) {
|
|
|
|
const obj = {
|
|
|
|
c: this,
|
|
|
|
b: callback
|
|
|
|
};
|
|
|
|
if(this.batch_component_force_id === -1)
|
|
|
|
this.batch_component_force_id = this.update_batch.force.push(obj) - 1;
|
|
|
|
else
|
|
|
|
this.update_batch.force[this.batch_component_force_id] = obj;
|
|
|
|
} else {
|
|
|
|
super.forceUpdate(callback);
|
2020-04-10 18:57:50 +00:00
|
|
|
}
|
2020-04-06 14:28:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
protected classList(...classes: (string | undefined)[]) {
|
|
|
|
return [...classes].filter(e => typeof e === "string" && e.length > 0).join(" ");
|
|
|
|
}
|
|
|
|
|
|
|
|
protected hasChildren() {
|
|
|
|
const type = typeof this.props.children;
|
|
|
|
if(type === "undefined") return false;
|
|
|
|
|
|
|
|
return Array.isArray(this.props.children) ? this.props.children.length > 0 : true;
|
|
|
|
}
|
2020-04-18 17:37:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export function batch_updates(type: BatchUpdateType) {
|
|
|
|
const batch = update_batches[type];
|
|
|
|
if(typeof batch === "undefined") throw "unknown batch type";
|
|
|
|
|
|
|
|
batch.enabled = true;
|
|
|
|
batch.enable_count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function flush_batched_updates(type: BatchUpdateType, force?: boolean) {
|
|
|
|
const batch = update_batches[type];
|
|
|
|
if(typeof batch === "undefined") throw "unknown batch type";
|
|
|
|
if(--batch.enable_count > 0 && !force) return;
|
|
|
|
if(batch.enable_count < 0) throw "flush_batched_updates called more than batch_updates!";
|
|
|
|
|
|
|
|
const updates = batch.update;
|
|
|
|
const forces = batch.force;
|
|
|
|
|
|
|
|
batch.update = [];
|
|
|
|
batch.force = [];
|
|
|
|
batch.enabled = batch.enable_count > 0;
|
|
|
|
|
|
|
|
ReactDOM.unstable_batchedUpdates(() => {
|
|
|
|
{
|
|
|
|
let index = updates.length;
|
|
|
|
while(index--) { /* fastest way to iterate */
|
|
|
|
const update = updates[index];
|
|
|
|
update.c.batch_component_id = -1;
|
|
|
|
update.c.setState(update.s, update.b);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
{
|
|
|
|
let index = forces.length;
|
|
|
|
while(index--) { /* fastest way to iterate */
|
|
|
|
const update = forces[index];
|
|
|
|
update.c.batch_component_force_id = -1;
|
|
|
|
update.c.forceUpdate(update.b);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2020-04-06 14:28:15 +00:00
|
|
|
}
|