mirror of
https://github.com/dream-num/univer.git
synced 2026-10-02 04:24:34 +08:00
chore: clear code (#654)
* chore: clear code * chore: remove redundant files * fix: revert unintended tsconfig change * fix: eslint
This commit is contained in:
@@ -5,6 +5,8 @@ local
|
||||
.turbo
|
||||
.docusaurus
|
||||
|
||||
apps
|
||||
|
||||
# Coverage
|
||||
coverage-report/
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { createIdentifier } from '@wendellhu/redi';
|
||||
|
||||
import type { Nullable } from '../../shared/types';
|
||||
|
||||
// NOTE: WARNING!! Do not set per unit config here! You can definitely find a better place to do that.
|
||||
// WARNING!!! Do not set per unit config here! You can definitely find a better place to do that.
|
||||
|
||||
/**
|
||||
* IConfig provides universal configuration for the whole application.
|
||||
|
||||
@@ -86,7 +86,6 @@ export class LocalUndoRedoService extends Disposable implements IUndoRedoService
|
||||
// redo stack should be cleared when pushing an undo
|
||||
redoStack.length = 0;
|
||||
|
||||
// TODO: undo element maybe merge-able
|
||||
// TODO: undo redo stack should have a maximum capacity, maybe we should get the config from IConfigService?
|
||||
undoStack.push(item);
|
||||
|
||||
@@ -94,7 +93,6 @@ export class LocalUndoRedoService extends Disposable implements IUndoRedoService
|
||||
undoStack.splice(0, 1);
|
||||
}
|
||||
|
||||
// TODO: update status with unitID, the UI doesn't have to update perhaps
|
||||
this._updateStatus();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
/**
|
||||
* Date Tool
|
||||
*/
|
||||
export class DateTime {}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +0,0 @@
|
||||
/**
|
||||
* Date Tool
|
||||
*/
|
||||
export class ExcelDateTime {}
|
||||
@@ -1,4 +0,0 @@
|
||||
// TODO
|
||||
export interface GroupModel<T> {
|
||||
getGroupModel(): T;
|
||||
}
|
||||
@@ -4,31 +4,20 @@ export * from './color/color-builder';
|
||||
export * from './command-enum';
|
||||
export * from './common';
|
||||
export * from './compare';
|
||||
export * from './date-time';
|
||||
export * from './doc-tool';
|
||||
export * from './excel-date-time';
|
||||
export * from './gen-name';
|
||||
export * from './generate';
|
||||
export * from './group-model';
|
||||
export * from './hash-algorithm';
|
||||
export * from './io-http';
|
||||
export * from './io-socket';
|
||||
export * from './lifecycle';
|
||||
export * from './locale';
|
||||
export * from './logger';
|
||||
export * from './lru/index';
|
||||
export * from './migrate/index';
|
||||
export * from './object-array';
|
||||
export * from './object-matrix';
|
||||
export * from './permission';
|
||||
export * from './random';
|
||||
export * from './rectangle';
|
||||
export { RefAlias } from './ref-alias';
|
||||
export * from './row-col-iter';
|
||||
export * from './sequence';
|
||||
export * from './serialize/index';
|
||||
export * from './sort-rules';
|
||||
export * from './tools';
|
||||
export * from './tuples';
|
||||
export * from './type-store';
|
||||
export * from './types';
|
||||
|
||||
@@ -1,265 +0,0 @@
|
||||
import type { IKeyType, IKeyValue } from './types';
|
||||
|
||||
function deleteEmpty(object: IKeyValue) {
|
||||
if (object) {
|
||||
Object.keys(object).forEach((key) => {
|
||||
const value = object[key];
|
||||
if (unDef(value) || value === '') {
|
||||
delete object[key];
|
||||
}
|
||||
});
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
function extend(src: IKeyValue, ...target: IKeyValue[]) {
|
||||
for (const item of target) {
|
||||
if (item) {
|
||||
for (const key in item) {
|
||||
if (item.hasOwnProperty(key)) {
|
||||
if (isDef(item[key])) {
|
||||
src[key] = item[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return src;
|
||||
}
|
||||
|
||||
function objectToUrlSearch(object: IKeyValue) {
|
||||
if (unDef(object)) return '';
|
||||
let str = '';
|
||||
for (const key in object) {
|
||||
if (object.hasOwnProperty(key) && object[key] !== undefined && object[key] !== null) {
|
||||
str += `${key}=${object[key]}&`;
|
||||
}
|
||||
}
|
||||
str = str.substring(0, str.length - 1);
|
||||
return str ? `?${encodeURI(str)}` : '';
|
||||
}
|
||||
|
||||
function isDef(v: unknown) {
|
||||
return v !== undefined && v !== null;
|
||||
}
|
||||
|
||||
function unDef(v: unknown) {
|
||||
return v === undefined || v === null;
|
||||
}
|
||||
|
||||
function urlSearchToObject(search: string) {
|
||||
if (unDef(search)) return {};
|
||||
search = decodeURI(search);
|
||||
const index = search.indexOf('?');
|
||||
const url = search.substring(0, index);
|
||||
search = search.substring(index);
|
||||
search = search.replace(/\?/, '');
|
||||
const array = search.split('&');
|
||||
const object: IKeyType<string> = {
|
||||
$$url: url,
|
||||
};
|
||||
array.forEach((value) => {
|
||||
const keyValue = value.split('=');
|
||||
object[keyValue[0]] = keyValue[1];
|
||||
});
|
||||
return object;
|
||||
}
|
||||
|
||||
function isString(value?: any): value is string {
|
||||
return Object.prototype.toString.apply(value) === '[object String]';
|
||||
}
|
||||
|
||||
function ispPlain(value: any): value is {} {
|
||||
return value ? Object.getPrototypeOf(value) === Object.getPrototypeOf({}) : false;
|
||||
}
|
||||
|
||||
function isArray(value: any): value is [] {
|
||||
return Object.prototype.toString.apply(value) === '[object Array]';
|
||||
}
|
||||
|
||||
function isFormData(value?: any): value is FormData {
|
||||
return value instanceof FormData;
|
||||
}
|
||||
|
||||
const noop = () => {};
|
||||
|
||||
const defaultFilter = (data: object) => data;
|
||||
|
||||
/**
|
||||
* Types of Io Http Request
|
||||
*/
|
||||
export enum IOHttpRequestType {
|
||||
POST = 'POST',
|
||||
GET = 'GET',
|
||||
PUT = 'PUT',
|
||||
DELETE = 'DELETE',
|
||||
}
|
||||
|
||||
/**
|
||||
* Status of Io Http Ready
|
||||
*/
|
||||
export enum IOHttpReadyState {
|
||||
UNSENT = 0,
|
||||
OPENED = 1,
|
||||
HEADERS_RECEIVED = 2,
|
||||
LOADING = 3,
|
||||
DONE = 4,
|
||||
}
|
||||
|
||||
/**
|
||||
* Types of Io Http Response
|
||||
*/
|
||||
export enum IOHttpResponseType {
|
||||
ARRAY_BUFFER = 'arraybuffer',
|
||||
BLOB = 'blob',
|
||||
TEXT = 'text',
|
||||
DOCUMENT = 'document',
|
||||
JSON = 'json',
|
||||
STREAM = 'stream',
|
||||
}
|
||||
|
||||
/**
|
||||
* IOHTTP header type
|
||||
*/
|
||||
export interface IOHttpHeaderType {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* IOHTTP request body type
|
||||
*/
|
||||
export type IOHttpRequestBodyType = string | {} | [] | FormData;
|
||||
|
||||
/**
|
||||
* Config of http communication
|
||||
*/
|
||||
export interface IOHttpConfig {
|
||||
type: keyof typeof IOHttpRequestType;
|
||||
url: string;
|
||||
dataType?: IOHttpResponseType;
|
||||
async?: boolean;
|
||||
header?: IOHttpHeaderType;
|
||||
contentType?: string;
|
||||
data?: IOHttpRequestBodyType;
|
||||
timeout?: number;
|
||||
username?: string;
|
||||
password?: string;
|
||||
beforeSend?: Function;
|
||||
dataFilter?: Function;
|
||||
error?: Function;
|
||||
success?: Function;
|
||||
complete?: Function;
|
||||
}
|
||||
|
||||
const defaultConfig: IOHttpConfig = {
|
||||
type: IOHttpRequestType.POST,
|
||||
url: '',
|
||||
async: true,
|
||||
header: {},
|
||||
dataType: IOHttpResponseType.JSON,
|
||||
contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
|
||||
data: {},
|
||||
username: '',
|
||||
password: '',
|
||||
timeout: 500,
|
||||
error: noop,
|
||||
beforeSend: defaultFilter,
|
||||
dataFilter: defaultFilter,
|
||||
success: noop,
|
||||
complete: noop,
|
||||
};
|
||||
|
||||
/**
|
||||
* HTTP request
|
||||
*
|
||||
* @param config The available config options for making requests
|
||||
* @returns
|
||||
*/
|
||||
export function IOHttp<T = void>(config: IOHttpConfig): Promise<T> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const setting = extend({}, defaultConfig, config) as Required<IOHttpConfig>;
|
||||
const request = new XMLHttpRequest();
|
||||
setting.beforeSend(setting);
|
||||
|
||||
request.responseType = setting.dataType as XMLHttpRequestResponseType;
|
||||
request.timeout = setting.timeout;
|
||||
request.addEventListener('readystatechange', (event) => {
|
||||
if (request.readyState === IOHttpReadyState.DONE) {
|
||||
if (request.status === 200) {
|
||||
const filter = setting.dataFilter(request.response);
|
||||
setting.success(filter);
|
||||
setting.complete();
|
||||
resolve(filter);
|
||||
} else {
|
||||
setting.error(event);
|
||||
reject(event);
|
||||
}
|
||||
}
|
||||
});
|
||||
request.addEventListener('timeout', (event) => {
|
||||
setting.error(event);
|
||||
});
|
||||
request.open(setting.type, setting.url, setting.async, setting.username, setting.password);
|
||||
|
||||
Object.keys(setting.header).forEach((key) => {
|
||||
const value = setting.header[key];
|
||||
request.setRequestHeader(key, value);
|
||||
});
|
||||
|
||||
if (isFormData(setting.data)) {
|
||||
request.setRequestHeader('Content-Type', 'multipart/form-data');
|
||||
} else if (setting.contentType) {
|
||||
request.setRequestHeader('Content-Type', setting.contentType);
|
||||
}
|
||||
|
||||
switch (setting.type) {
|
||||
case IOHttpRequestType.DELETE:
|
||||
case IOHttpRequestType.GET: {
|
||||
request.send();
|
||||
break;
|
||||
}
|
||||
case IOHttpRequestType.POST:
|
||||
case IOHttpRequestType.PUT: {
|
||||
if (ispPlain(setting.data)) {
|
||||
request.send(JSON.stringify(setting.data));
|
||||
return;
|
||||
}
|
||||
if (isString(setting.data)) {
|
||||
request.send(setting.data);
|
||||
return;
|
||||
}
|
||||
if (isArray(setting.data)) {
|
||||
request.send(JSON.stringify(setting.data));
|
||||
return;
|
||||
}
|
||||
if (isFormData(setting.data)) {
|
||||
request.send(setting.data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a POST request
|
||||
*
|
||||
* @param url The server URL that will be used for the request
|
||||
* @param data The data to be sent as the request body
|
||||
* @param success Successful callback
|
||||
*/
|
||||
export function Post<T = void>(url: string, data: object = {}, success: Function = noop): Promise<T> {
|
||||
return IOHttp({ url, type: IOHttpRequestType.POST, data, success });
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a Get request
|
||||
*
|
||||
* @param url The server URL that will be used for the request
|
||||
* @param data The data to be sent as the request body
|
||||
* @param success Successful callback
|
||||
*/
|
||||
export function Get<T = void>(url: string, data: object = {}, success: Function = noop): Promise<T> {
|
||||
url += objectToUrlSearch(data);
|
||||
return IOHttp({ url, type: IOHttpRequestType.GET, success });
|
||||
}
|
||||
@@ -1,201 +0,0 @@
|
||||
import type { Nullable } from '../common/type-utils';
|
||||
import type { IKeyValue } from './types';
|
||||
|
||||
function isDef(v: any) {
|
||||
return v !== undefined && v !== null;
|
||||
}
|
||||
|
||||
function unDef(v: any) {
|
||||
return v === undefined || v === null;
|
||||
}
|
||||
|
||||
function isBlank(v: string) {
|
||||
return unDef(v) || v.toString() === '';
|
||||
}
|
||||
|
||||
function extend(src: IKeyValue, ...target: IKeyValue[]) {
|
||||
for (const item of target) {
|
||||
if (item) {
|
||||
for (const key in item) {
|
||||
if (item.hasOwnProperty(key)) {
|
||||
if (isDef(item[key])) {
|
||||
src[key] = item[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return src;
|
||||
}
|
||||
|
||||
/**
|
||||
* Properties of socket config
|
||||
*/
|
||||
export interface IOSocketConfig {
|
||||
url: string;
|
||||
heartbeatTime?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Types if Io Socket Listen
|
||||
*/
|
||||
export enum IOSocketListenType {
|
||||
MESSAGE = 'message',
|
||||
OPEN = 'open',
|
||||
CLOSE = 'close',
|
||||
ERROR = 'error',
|
||||
RETRY = 'retry',
|
||||
DESTROY = 'destroy',
|
||||
}
|
||||
|
||||
const defaultConfig = {
|
||||
url: '',
|
||||
heartbeatTime: 1000 * 30,
|
||||
};
|
||||
|
||||
export const HEART_BEAT_MESSAGE = 'heart_beat_message';
|
||||
|
||||
/**
|
||||
* IOSocket send body
|
||||
*/
|
||||
export type IOSocketSendBody = string | ArrayBufferLike | Blob | ArrayBufferView;
|
||||
|
||||
/**
|
||||
* IOSocket type
|
||||
*/
|
||||
export class IOSocket {
|
||||
private _config: IOSocketConfig;
|
||||
|
||||
private _listens: Map<IOSocketListenType, Function>;
|
||||
|
||||
private _timer: number;
|
||||
|
||||
private _socket: Nullable<WebSocket>;
|
||||
|
||||
constructor(config: IOSocketConfig) {
|
||||
const setting = Object.assign(defaultConfig, config);
|
||||
// const setting = extend(
|
||||
// {},
|
||||
// defaultConfig,
|
||||
// config
|
||||
// ) as Required<IOSocketConfig>;
|
||||
if (isBlank(setting.url)) {
|
||||
throw new Error('url must be input');
|
||||
}
|
||||
this._listens = new Map();
|
||||
this._timer = -1;
|
||||
this._config = setting;
|
||||
}
|
||||
|
||||
link(): void {
|
||||
this._create();
|
||||
this._bind();
|
||||
}
|
||||
|
||||
send(body: IOSocketSendBody): void {
|
||||
this._socket?.send(body);
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
const listen = this._listens.get(IOSocketListenType.DESTROY);
|
||||
if (listen) {
|
||||
listen();
|
||||
}
|
||||
this._clear();
|
||||
this._listens.clear();
|
||||
}
|
||||
|
||||
on(type: IOSocketListenType, listen: Function): void {
|
||||
this._listens.set(type, listen);
|
||||
}
|
||||
|
||||
close(): void {
|
||||
this._socket?.close();
|
||||
}
|
||||
|
||||
private _create(): void {
|
||||
const { _config } = this;
|
||||
this._socket = new WebSocket(_config.url);
|
||||
}
|
||||
|
||||
private _bind(): void {
|
||||
this._socket?.addEventListener(IOSocketListenType.MESSAGE, this._message.bind(this));
|
||||
this._socket?.addEventListener(IOSocketListenType.OPEN, this._open.bind(this));
|
||||
this._socket?.addEventListener(IOSocketListenType.CLOSE, this._close.bind(this));
|
||||
this._socket?.addEventListener(IOSocketListenType.ERROR, this._error.bind(this));
|
||||
// this._socket.addEventListener(IOSocketListenType.MESSAGE, (event: Event) => {
|
||||
// this._message(event);
|
||||
// });
|
||||
// this._socket.addEventListener(IOSocketListenType.OPEN, (event: Event) => {
|
||||
// this._open(event);
|
||||
// });
|
||||
// this._socket.addEventListener(IOSocketListenType.CLOSE, (event: Event) => {
|
||||
// this._close(event);
|
||||
// });
|
||||
// this._socket.addEventListener(IOSocketListenType.ERROR, (event: Event) => {
|
||||
// this._error(event);
|
||||
// });
|
||||
}
|
||||
|
||||
private _message(event: Event): void {
|
||||
const listen = this._listens.get(IOSocketListenType.MESSAGE);
|
||||
if (listen) {
|
||||
listen(event);
|
||||
}
|
||||
}
|
||||
|
||||
private _open(event: Event): void {
|
||||
const listen = this._listens.get(IOSocketListenType.OPEN);
|
||||
if (listen) {
|
||||
listen(event);
|
||||
this._heartbeat();
|
||||
}
|
||||
}
|
||||
|
||||
private _error(event: Event): void {
|
||||
const listen = this._listens.get(IOSocketListenType.ERROR);
|
||||
if (listen) {
|
||||
listen(event);
|
||||
this._retry();
|
||||
}
|
||||
}
|
||||
|
||||
private _close(event: Event): void {
|
||||
const listen = this._listens.get(IOSocketListenType.CLOSE);
|
||||
if (listen) {
|
||||
listen(event);
|
||||
this.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
private _clear(): void {
|
||||
if (this._timer > -1) {
|
||||
clearInterval(this._timer);
|
||||
}
|
||||
this._timer = -1;
|
||||
this._socket?.removeEventListener(IOSocketListenType.MESSAGE, this._message.bind(this));
|
||||
this._socket?.removeEventListener(IOSocketListenType.OPEN, this._open.bind(this));
|
||||
this._socket?.removeEventListener(IOSocketListenType.CLOSE, this._close.bind(this));
|
||||
this._socket?.removeEventListener(IOSocketListenType.ERROR, this._error.bind(this));
|
||||
// this._socket.close();
|
||||
}
|
||||
|
||||
private _heartbeat(): void {
|
||||
const { _socket, _config } = this;
|
||||
const { heartbeatTime } = _config;
|
||||
function handle() {
|
||||
_socket?.send(HEART_BEAT_MESSAGE);
|
||||
}
|
||||
this._timer = setInterval(handle, heartbeatTime) as unknown as number;
|
||||
}
|
||||
|
||||
private _retry(): void {
|
||||
const listen = this._listens.get(IOSocketListenType.RETRY);
|
||||
if (listen) {
|
||||
listen();
|
||||
}
|
||||
this._clear();
|
||||
this._create();
|
||||
this._bind();
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/**
|
||||
* Logging tool
|
||||
*/
|
||||
export class Logger {
|
||||
static log(...value: unknown[]): void {
|
||||
if (console) {
|
||||
console.log(...value);
|
||||
}
|
||||
}
|
||||
|
||||
static info(...value: unknown[]): void {
|
||||
if (console) {
|
||||
console.info(...value);
|
||||
}
|
||||
}
|
||||
|
||||
static error(...value: unknown[]): void {
|
||||
if (console) {
|
||||
console.error(...value);
|
||||
}
|
||||
}
|
||||
|
||||
static warn(...value: unknown[]): void {
|
||||
if (console) {
|
||||
console.warn(...value);
|
||||
}
|
||||
}
|
||||
|
||||
static br(): void {
|
||||
Logger.info('\n');
|
||||
}
|
||||
|
||||
static capsule(env: string, version: string, prefix: string = '', suffix = ''): void {
|
||||
if (console) {
|
||||
console.log(
|
||||
`%c${prefix}%c${env}%c${version}%c${suffix}`,
|
||||
'',
|
||||
'padding:3px;color:white;background:#023047',
|
||||
'padding:3px;color:white;background:#219EBC',
|
||||
''
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export * from './migrate';
|
||||
@@ -1,439 +0,0 @@
|
||||
import type { BooleanNumber } from '../../types/enum';
|
||||
import { HorizontalAlign, VerticalAlign, WrapStrategy } from '../../types/enum';
|
||||
import type {
|
||||
ICellData,
|
||||
IColumnData,
|
||||
IRowData,
|
||||
IStyleData,
|
||||
ITextRun,
|
||||
ITextStyle,
|
||||
IWorkbookData,
|
||||
IWorksheetData,
|
||||
} from '../../types/interfaces';
|
||||
import type { ObjectArrayPrimitiveType } from '../object-array';
|
||||
import { Tools } from '../tools';
|
||||
import type { IKeyValue } from '../types';
|
||||
import { border } from './border';
|
||||
|
||||
export function migrate(config: any): Partial<IWorkbookData> {
|
||||
const newConfig: Partial<IWorkbookData> = {};
|
||||
newConfig.styles = {};
|
||||
if (config.hasOwnProperty('lang')) {
|
||||
newConfig.locale = config.lang;
|
||||
}
|
||||
if (config.hasOwnProperty('data')) {
|
||||
newConfig.sheets = {};
|
||||
for (const sheet of config.data) {
|
||||
const newSheet: Partial<IWorksheetData> = {};
|
||||
|
||||
// id
|
||||
// if (sheet.hasOwnProperty('index')) {
|
||||
// newSheet.id = sheet.index as string;
|
||||
// } else {
|
||||
newSheet.id = Tools.generateRandomId(6);
|
||||
// }
|
||||
|
||||
// 缩放比例
|
||||
if (sheet.hasOwnProperty('zoomRatio')) {
|
||||
newSheet.zoomRatio = sheet.zoomRatio;
|
||||
}
|
||||
|
||||
// 最大行号
|
||||
if (sheet.hasOwnProperty('row')) {
|
||||
newSheet.rowCount = sheet.row;
|
||||
}
|
||||
|
||||
// 最大列号
|
||||
if (sheet.hasOwnProperty('column')) {
|
||||
newSheet.columnCount = sheet.column;
|
||||
}
|
||||
|
||||
// 是否激活
|
||||
if (sheet.hasOwnProperty('status')) {
|
||||
newSheet.status = parseInt(sheet.status);
|
||||
}
|
||||
|
||||
// sheet 名称
|
||||
if (sheet.hasOwnProperty('name')) {
|
||||
newSheet.name = sheet.name;
|
||||
}
|
||||
|
||||
// 激活选区
|
||||
if (sheet.hasOwnProperty('universheet_select_save')) {
|
||||
newSheet.selections = [];
|
||||
sheet.universheet_select_save.forEach((selection: IKeyValue) => {
|
||||
newSheet.selections?.push({
|
||||
startRow: selection.row[0],
|
||||
endRow: selection.row[1],
|
||||
startColumn: selection.column[0],
|
||||
endColumn: selection.column[1],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 横向滚动条位置
|
||||
if (sheet.hasOwnProperty('scrollLeft')) {
|
||||
newSheet.scrollLeft = sheet.scrollLeft;
|
||||
}
|
||||
|
||||
// 竖向滚动条位置
|
||||
if (sheet.hasOwnProperty('scrollTop')) {
|
||||
newSheet.scrollTop = sheet.scrollTop;
|
||||
}
|
||||
|
||||
if (sheet.hasOwnProperty('config')) {
|
||||
// 合并单元格
|
||||
if (sheet.config.merge) {
|
||||
newSheet.mergeData = [];
|
||||
for (const key of Object.keys(sheet.config.merge)) {
|
||||
const merge: {
|
||||
r: number;
|
||||
c: number;
|
||||
rs: number;
|
||||
cs: number;
|
||||
} = sheet.config.merge[key];
|
||||
const mergeReact = {
|
||||
startRow: merge.r,
|
||||
endRow: merge.r + merge.rs - 1,
|
||||
startColumn: merge.c,
|
||||
endColumn: merge.c + merge.cs - 1,
|
||||
};
|
||||
newSheet.mergeData.push(mergeReact);
|
||||
}
|
||||
}
|
||||
|
||||
// 边框,先按顺序设置到每一个单元格对象中,设置时需要遍历四个方向的相邻单元格做去重,最后单元格样式确定之后,再统一提取到styles上
|
||||
if (sheet.config.borderInfo) {
|
||||
border(newSheet, sheet);
|
||||
}
|
||||
|
||||
// 行高
|
||||
if (sheet.config.rowlen) {
|
||||
newSheet.rowData = {};
|
||||
for (const [rowIndex, height] of Object.entries(sheet.config.rowlen)) {
|
||||
newSheet.rowData[Number(rowIndex)] = {
|
||||
h: Number(height),
|
||||
hd: 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
// 列宽
|
||||
if (sheet.config.columnlen) {
|
||||
newSheet.columnData = {};
|
||||
for (const [colIndex, width] of Object.entries(sheet.config.columnlen)) {
|
||||
newSheet.columnData[Number(colIndex)] = {
|
||||
w: Number(width),
|
||||
hd: 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
// 隐藏行
|
||||
if (sheet.config.rowhidden) {
|
||||
if (!newSheet.rowData) newSheet.rowData = {};
|
||||
const rowData = newSheet.rowData as ObjectArrayPrimitiveType<Partial<IRowData>>;
|
||||
for (const [rowIndex, isHidden] of Object.entries(sheet.config.rowhidden)) {
|
||||
if (!rowData[Number(rowIndex)]) {
|
||||
rowData[Number(rowIndex)] = {};
|
||||
}
|
||||
|
||||
rowData[Number(rowIndex)].hd = isHidden as BooleanNumber;
|
||||
}
|
||||
}
|
||||
// 隐藏列
|
||||
if (sheet.config.colhidden) {
|
||||
if (!newSheet.columnData) newSheet.columnData = {};
|
||||
const columnData = newSheet.columnData as ObjectArrayPrimitiveType<Partial<IColumnData>>;
|
||||
for (const [colIndex, isHidden] of Object.entries(sheet.config.colhidden)) {
|
||||
if (!columnData[Number(colIndex)]) {
|
||||
columnData[Number(colIndex)] = {};
|
||||
}
|
||||
columnData[Number(colIndex)].hd = isHidden as BooleanNumber;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 单元格数据
|
||||
if (sheet.hasOwnProperty('celldata')) {
|
||||
if (!newSheet.cellData) {
|
||||
newSheet.cellData = {};
|
||||
}
|
||||
|
||||
for (const cellItem of sheet.celldata) {
|
||||
if (!newSheet.cellData[cellItem.r]) {
|
||||
newSheet.cellData[cellItem.r] = {};
|
||||
}
|
||||
if (!newSheet.cellData[cellItem.r][cellItem.c]) {
|
||||
newSheet.cellData[cellItem.r][cellItem.c] = {};
|
||||
}
|
||||
|
||||
const cell = cellItem.v;
|
||||
|
||||
const newCell: ICellData = newSheet.cellData[cellItem.r][cellItem.c];
|
||||
|
||||
if (cell?.ct?.t === 'inlineStr') {
|
||||
const textRuns: ITextRun[] = [];
|
||||
let dataStream = '';
|
||||
|
||||
cell.ct.s.forEach((element: any) => {
|
||||
const textStyle: ITextStyle = {};
|
||||
|
||||
// 背景颜色
|
||||
if (element.hasOwnProperty('bg')) {
|
||||
textStyle.bg = {
|
||||
rgb: element.fc,
|
||||
};
|
||||
}
|
||||
|
||||
// 字体
|
||||
if (element.hasOwnProperty('ff')) {
|
||||
textStyle.ff = element.ff;
|
||||
}
|
||||
|
||||
// 字体颜色
|
||||
if (element.hasOwnProperty('fc')) {
|
||||
textStyle.cl = {
|
||||
rgb: element.fc,
|
||||
};
|
||||
}
|
||||
|
||||
// 字体大小
|
||||
if (element.hasOwnProperty('fs')) {
|
||||
textStyle.fs = element.fs;
|
||||
}
|
||||
|
||||
// 粗体
|
||||
if (element.hasOwnProperty('bl')) {
|
||||
textStyle.bl = element.bl;
|
||||
}
|
||||
|
||||
// 斜体
|
||||
if (element.hasOwnProperty('it')) {
|
||||
textStyle.it = element.it;
|
||||
}
|
||||
|
||||
// 删除线
|
||||
if (element.hasOwnProperty('cl')) {
|
||||
textStyle.st = {
|
||||
s: 1,
|
||||
};
|
||||
}
|
||||
|
||||
// 下划线
|
||||
if (element.hasOwnProperty('un')) {
|
||||
textStyle.ul = {
|
||||
s: element.un,
|
||||
};
|
||||
}
|
||||
|
||||
const sId = Tools.generateRandomId(6);
|
||||
dataStream += element.v;
|
||||
textRuns.push({
|
||||
sId,
|
||||
st: 0,
|
||||
ed: element.v.length - 1,
|
||||
ts: textStyle,
|
||||
});
|
||||
});
|
||||
|
||||
dataStream += '\r\n';
|
||||
|
||||
newCell.p = {
|
||||
id: Tools.generateRandomId(6),
|
||||
body: {
|
||||
dataStream,
|
||||
textRuns,
|
||||
},
|
||||
documentStyle: {},
|
||||
};
|
||||
} else {
|
||||
// 原始值
|
||||
if (cell.hasOwnProperty('v')) {
|
||||
newCell.v = cell.v;
|
||||
}
|
||||
|
||||
const cellStyle: IStyleData = {};
|
||||
|
||||
// 背景颜色
|
||||
if (cell.hasOwnProperty('bg')) {
|
||||
cellStyle.bg = {
|
||||
rgb: cell.bg,
|
||||
};
|
||||
}
|
||||
|
||||
// 字体
|
||||
if (cell.hasOwnProperty('ff')) {
|
||||
cellStyle.ff = cell.ff;
|
||||
}
|
||||
|
||||
// 字体颜色
|
||||
if (cell.hasOwnProperty('fc')) {
|
||||
cellStyle.cl = {
|
||||
rgb: cell.fc,
|
||||
};
|
||||
}
|
||||
|
||||
// 字体大小
|
||||
if (cell.hasOwnProperty('fs')) {
|
||||
cellStyle.fs = cell.fs;
|
||||
}
|
||||
|
||||
// 粗体
|
||||
if (cell.hasOwnProperty('bl')) {
|
||||
cellStyle.bl = cell.bl;
|
||||
}
|
||||
|
||||
// 斜体
|
||||
if (cell.hasOwnProperty('it')) {
|
||||
cellStyle.it = cell.it;
|
||||
}
|
||||
|
||||
// 删除线
|
||||
if (cell.hasOwnProperty('cl')) {
|
||||
cellStyle.st = {
|
||||
s: cell.cl,
|
||||
};
|
||||
}
|
||||
|
||||
// 下划线
|
||||
if (cell.hasOwnProperty('un')) {
|
||||
cellStyle.ul = {
|
||||
s: cell.un,
|
||||
};
|
||||
}
|
||||
|
||||
const sMap = {
|
||||
'0': VerticalAlign.MIDDLE,
|
||||
'1': VerticalAlign.TOP,
|
||||
'2': VerticalAlign.BOTTOM,
|
||||
};
|
||||
// const sMap = {
|
||||
// '0': 2,
|
||||
// '1': 1,
|
||||
// '2': 3,
|
||||
// };
|
||||
|
||||
// 垂直对齐
|
||||
if (cell.hasOwnProperty('vt')) {
|
||||
switch (String(cell.vt)) {
|
||||
case '0':
|
||||
cellStyle.vt = VerticalAlign.MIDDLE;
|
||||
break;
|
||||
case '1':
|
||||
cellStyle.vt = VerticalAlign.TOP;
|
||||
break;
|
||||
case '2':
|
||||
cellStyle.vt = VerticalAlign.BOTTOM;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// cellStyle.vt = sMap[String(cell.vt)];
|
||||
}
|
||||
|
||||
// 水平对齐
|
||||
if (cell.hasOwnProperty('ht')) {
|
||||
switch (String(cell.ht)) {
|
||||
case '0':
|
||||
cellStyle.ht = HorizontalAlign.CENTER;
|
||||
break;
|
||||
case '1':
|
||||
cellStyle.ht = HorizontalAlign.LEFT;
|
||||
break;
|
||||
case '2':
|
||||
cellStyle.ht = HorizontalAlign.RIGHT;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// cellStyle.ht = sMap[String(cell.ht)];
|
||||
}
|
||||
|
||||
// 竖排文字
|
||||
if (cell.hasOwnProperty('tr')) {
|
||||
switch (cell.tr) {
|
||||
case '0':
|
||||
cellStyle.tr = {
|
||||
a: 0,
|
||||
v: 0,
|
||||
};
|
||||
break;
|
||||
|
||||
case '1':
|
||||
cellStyle.tr = {
|
||||
a: 45,
|
||||
v: 0,
|
||||
};
|
||||
break;
|
||||
|
||||
case '2':
|
||||
cellStyle.tr = {
|
||||
a: -45,
|
||||
v: 0,
|
||||
};
|
||||
break;
|
||||
|
||||
case '3':
|
||||
cellStyle.tr = {
|
||||
a: 0,
|
||||
v: 1,
|
||||
};
|
||||
break;
|
||||
|
||||
case '4':
|
||||
cellStyle.tr = {
|
||||
a: 90,
|
||||
v: 0,
|
||||
};
|
||||
break;
|
||||
|
||||
case '5':
|
||||
cellStyle.tr = {
|
||||
a: -90,
|
||||
v: 0,
|
||||
};
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 文字旋转角度
|
||||
if (cell.hasOwnProperty('rt')) {
|
||||
cellStyle.tr = {
|
||||
a: cell.tr,
|
||||
v: 0,
|
||||
};
|
||||
}
|
||||
|
||||
// 文本换行
|
||||
if (cell.hasOwnProperty('tb')) {
|
||||
switch (String(cell.tb)) {
|
||||
case '0':
|
||||
cellStyle.tb = WrapStrategy.CLIP;
|
||||
break;
|
||||
case '1':
|
||||
cellStyle.tb = WrapStrategy.OVERFLOW;
|
||||
break;
|
||||
case '2':
|
||||
cellStyle.tb = WrapStrategy.WRAP;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
//cellStyle.tb = sMap[String(cell.tb)];
|
||||
}
|
||||
|
||||
newCell.s = Object.assign(newCell.s || {}, cellStyle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newConfig.sheets[newSheet.id] = newSheet;
|
||||
}
|
||||
}
|
||||
return newConfig;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
export class Random {
|
||||
protected _max: number;
|
||||
|
||||
protected _min: number;
|
||||
|
||||
constructor(max: number, min: number) {
|
||||
this._max = max;
|
||||
this._min = min;
|
||||
}
|
||||
|
||||
next(): number {
|
||||
return Math.trunc(Math.random() * (this._max - this._min) + this._min);
|
||||
}
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
import { ESRectTree } from './es-rect-tree';
|
||||
import { RectTree } from './rect-tree';
|
||||
|
||||
export class BorderTree {
|
||||
rightTree: ESRectTree;
|
||||
|
||||
leftTree: ESRectTree;
|
||||
|
||||
topTree: ESRectTree;
|
||||
|
||||
bottomTree: ESRectTree;
|
||||
|
||||
constructor() {
|
||||
this.leftTree = new ESRectTree();
|
||||
this.rightTree = new ESRectTree();
|
||||
this.topTree = new ESRectTree();
|
||||
this.bottomTree = new ESRectTree();
|
||||
}
|
||||
|
||||
setLeft(node: BorderTree.Node): void {
|
||||
this.leftTree.destructCrossNode(node);
|
||||
this.leftTree.insert(node);
|
||||
}
|
||||
|
||||
setRight(node: BorderTree.Node): void {
|
||||
this.rightTree.destructCrossNode(node);
|
||||
this.rightTree.insert(node);
|
||||
}
|
||||
|
||||
setTop(node: BorderTree.Node): void {
|
||||
this.topTree.destructCrossNode(node);
|
||||
this.topTree.insert(node);
|
||||
}
|
||||
|
||||
setBottom(node: BorderTree.Node): void {
|
||||
this.bottomTree.destructCrossNode(node);
|
||||
this.bottomTree.insert(node);
|
||||
}
|
||||
|
||||
setWhole(node: BorderTree.Node): void {
|
||||
this.setLeft(node);
|
||||
this.setRight(node);
|
||||
this.setTop(node);
|
||||
this.setBottom(node);
|
||||
}
|
||||
|
||||
clearLeft(node: BorderTree.Node): void {
|
||||
this.leftTree.remove(node, (other) => node.equals(other as BorderTree.Node));
|
||||
}
|
||||
|
||||
clearRight(node: BorderTree.Node): void {
|
||||
this.rightTree.remove(node, (other) => node.equals(other as BorderTree.Node));
|
||||
}
|
||||
|
||||
clearTop(node: BorderTree.Node): void {
|
||||
this.topTree.remove(node, (other) => node.equals(other as BorderTree.Node));
|
||||
}
|
||||
|
||||
clearBottom(node: BorderTree.Node): void {
|
||||
this.bottomTree.remove(node, (other) => node.equals(other as BorderTree.Node));
|
||||
}
|
||||
|
||||
clearWhole(node: BorderTree.Node): void {
|
||||
this.clearLeft(node);
|
||||
this.clearRight(node);
|
||||
this.clearTop(node);
|
||||
this.clearBottom(node);
|
||||
}
|
||||
}
|
||||
|
||||
export namespace BorderTree {
|
||||
export class Node extends RectTree.Node {
|
||||
type: number;
|
||||
|
||||
width: number;
|
||||
|
||||
color: number;
|
||||
|
||||
stamp: number;
|
||||
|
||||
constructor({
|
||||
minY = Infinity,
|
||||
minX = Infinity,
|
||||
maxY = -Infinity,
|
||||
maxX = -Infinity,
|
||||
leaf = true,
|
||||
height = 1,
|
||||
childrenNodes = [],
|
||||
type = 0,
|
||||
width = 0,
|
||||
color = 0,
|
||||
stamp = 0,
|
||||
}: Partial<BorderTree.Node> = {}) {
|
||||
super({
|
||||
minY,
|
||||
minX,
|
||||
maxY,
|
||||
maxX,
|
||||
leaf,
|
||||
height,
|
||||
childrenNodes,
|
||||
});
|
||||
this.type = type;
|
||||
this.width = width;
|
||||
this.color = color;
|
||||
this.stamp = stamp;
|
||||
}
|
||||
|
||||
equals(other: BorderTree.Node) {
|
||||
return (
|
||||
this.minX === other.minX &&
|
||||
this.minY === other.minY &&
|
||||
other.maxX === this.maxX &&
|
||||
other.maxY === this.maxY
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
import { RectTree } from './rect-tree';
|
||||
|
||||
export class ESRectTree extends RectTree {
|
||||
rowExpandSplitAxis(index: number, count: number = 1): void {
|
||||
const baseLineNode = new RectTree.Node({
|
||||
minY: index,
|
||||
minX: 0,
|
||||
maxY: index,
|
||||
maxX: Infinity,
|
||||
});
|
||||
const crossNodes = this.search(baseLineNode);
|
||||
const collectNode = new Array<RectTree.Node>();
|
||||
for (let i = 0; i < crossNodes.length; i++) {
|
||||
const node = crossNodes[i];
|
||||
this.remove(node);
|
||||
if (node.minY >= index) {
|
||||
node.minY += count;
|
||||
}
|
||||
if (node.maxY >= index) {
|
||||
node.maxY += count;
|
||||
}
|
||||
collectNode.push(node);
|
||||
}
|
||||
this.load(collectNode);
|
||||
}
|
||||
|
||||
colExpandSplitAxis(index: number, count: number = 1): void {
|
||||
const baseLineNode = new RectTree.Node({
|
||||
minY: 0,
|
||||
minX: index,
|
||||
maxY: Infinity,
|
||||
maxX: index,
|
||||
});
|
||||
const crossNodes = this.search(baseLineNode);
|
||||
const collectNode = new Array<RectTree.Node>();
|
||||
for (let i = 0; i < crossNodes.length; i++) {
|
||||
const node = crossNodes[i];
|
||||
this.remove(node);
|
||||
if (node.minX >= index) {
|
||||
node.minX += count;
|
||||
}
|
||||
if (node.maxX >= index) {
|
||||
node.maxX += count;
|
||||
}
|
||||
collectNode.push(node);
|
||||
}
|
||||
this.load(collectNode);
|
||||
}
|
||||
|
||||
rowShrinkSplitAxis(index: number, count: number = 1): void {
|
||||
const baseLineNode = new RectTree.Node({
|
||||
minY: index,
|
||||
minX: 0,
|
||||
maxY: index,
|
||||
maxX: Infinity,
|
||||
});
|
||||
const crossNodes = this.search(baseLineNode);
|
||||
const collectNode = new Array<RectTree.Node>();
|
||||
for (let i = 0, len = crossNodes.length; i < len; i++) {
|
||||
const node = crossNodes[i];
|
||||
this.remove(node);
|
||||
if (node.minY >= index) {
|
||||
node.minY -= count;
|
||||
}
|
||||
if (node.maxY >= index) {
|
||||
node.maxY -= count;
|
||||
}
|
||||
collectNode.push(node);
|
||||
}
|
||||
this.load(collectNode);
|
||||
}
|
||||
|
||||
colShrinkSplitAxis(index: number, count: number = 1): void {
|
||||
const baseLineNode = new RectTree.Node({
|
||||
minY: 0,
|
||||
minX: index,
|
||||
maxY: Infinity,
|
||||
maxX: index,
|
||||
});
|
||||
const crossNodes = this.search(baseLineNode);
|
||||
const collectNode = new Array<RectTree.Node>();
|
||||
for (let i = 0; i < crossNodes.length; i++) {
|
||||
const node = crossNodes[i];
|
||||
this.remove(node);
|
||||
if (node.minX >= index) {
|
||||
node.minX -= count;
|
||||
}
|
||||
if (node.maxX >= index) {
|
||||
node.maxX -= count;
|
||||
}
|
||||
collectNode.push(node);
|
||||
}
|
||||
this.load(collectNode);
|
||||
}
|
||||
|
||||
destructCrossNode(master: RectTree.Node): RectTree.Node[] {
|
||||
const crossNodes = this.search(master);
|
||||
const collectNode = new Array<RectTree.Node>();
|
||||
for (const node of crossNodes) {
|
||||
if (!master.contains(node)) {
|
||||
master.difference(node, collectNode);
|
||||
}
|
||||
this.remove(node);
|
||||
}
|
||||
this.load(collectNode);
|
||||
return crossNodes;
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
function QuickSwap<T>(array: T[], i: number, j: number): void {
|
||||
const tmp = array[i];
|
||||
array[i] = array[j];
|
||||
array[j] = tmp;
|
||||
}
|
||||
|
||||
function QuickCompare<T>(a: T, b: T): number {
|
||||
return a < b ? -1 : a > b ? 1 : 0;
|
||||
}
|
||||
|
||||
function QuickStep<T>(array: T[], k: number, left: number, right: number, compare: Compare<T>): void {
|
||||
while (right > left) {
|
||||
if (right - left > 600) {
|
||||
const n = right - left + 1;
|
||||
const m = k - left + 1;
|
||||
const z = Math.log(n);
|
||||
const s = 0.5 * Math.exp((2 * z) / 3);
|
||||
const sd = 0.5 * Math.sqrt((z * s * (n - s)) / n) * (m - n / 2 < 0 ? -1 : 1);
|
||||
const newLeft = Math.max(left, Math.floor(k - (m * s) / n + sd));
|
||||
const newRight = Math.min(right, Math.floor(k + ((n - m) * s) / n + sd));
|
||||
QuickStep(array, k, newLeft, newRight, compare);
|
||||
}
|
||||
|
||||
const t = array[k];
|
||||
let i = left;
|
||||
let j = right;
|
||||
|
||||
QuickSwap(array, left, k);
|
||||
if (compare(array[right], t) > 0) {
|
||||
QuickSwap(array, left, right);
|
||||
}
|
||||
|
||||
while (i < j) {
|
||||
QuickSwap(array, i, j);
|
||||
i++;
|
||||
j--;
|
||||
while (compare(array[i], t) < 0) {
|
||||
i++;
|
||||
}
|
||||
while (compare(array[j], t) > 0) {
|
||||
j--;
|
||||
}
|
||||
}
|
||||
|
||||
if (compare(array[left], t) === 0) {
|
||||
QuickSwap(array, left, j);
|
||||
} else {
|
||||
j++;
|
||||
QuickSwap(array, j, right);
|
||||
}
|
||||
|
||||
if (j <= k) {
|
||||
left = j + 1;
|
||||
}
|
||||
if (k <= j) {
|
||||
right = j - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export type Compare<T> = (a: T, b: T) => number;
|
||||
|
||||
export function QuickSelect<T>(array: T[], k: number, left: number, right: number, compare: Compare<T>) {
|
||||
QuickStep(array, k, left || 0, right || array.length - 1, compare || QuickCompare);
|
||||
}
|
||||
@@ -1,641 +0,0 @@
|
||||
import type { Nullable } from '../types';
|
||||
import type { Compare } from './quick-select';
|
||||
import { QuickSelect } from './quick-select';
|
||||
|
||||
const defaultCompareNodeMinX = (a: RectTree.Node, b: RectTree.Node) => a.minX - b.minX;
|
||||
const defaultCompareNodeMinY = (a: RectTree.Node, b: RectTree.Node) => a.minY - b.minY;
|
||||
|
||||
export class RectTree {
|
||||
private _maxEntries: number;
|
||||
|
||||
private _minEntries: number;
|
||||
|
||||
private _data: RectTree.Node;
|
||||
|
||||
private _compareMinX: Compare<RectTree.Node>;
|
||||
|
||||
private _compareMinY: Compare<RectTree.Node>;
|
||||
|
||||
constructor(max: number = 9) {
|
||||
// 默认情况下,节点中的最大条目数为 9; 最小节点填充为 40% 以获得最佳性能
|
||||
this._maxEntries = Math.max(4, max);
|
||||
this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
|
||||
this._data = null as unknown as RectTree.Node;
|
||||
this._compareMinX = defaultCompareNodeMinX;
|
||||
this._compareMinY = defaultCompareNodeMinY;
|
||||
this.clear();
|
||||
}
|
||||
|
||||
static findItem<T>(item: T, items: T[], equalsFn: Nullable<EqualsFunction<T>>) {
|
||||
if (!equalsFn) {
|
||||
return items.indexOf(item);
|
||||
}
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
if (equalsFn(item, items[i])) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static multiSelect(
|
||||
array: RectTree.Node[],
|
||||
left: number,
|
||||
right: number,
|
||||
n: number,
|
||||
compare: Compare<RectTree.Node>
|
||||
) {
|
||||
// 对数组进行排序,以便项目以 n 个未排序项目为一组,各组在彼此之间排序;
|
||||
// 将选择算法与二元分治法相结合
|
||||
const stack: number[] = [left, right];
|
||||
while (stack.length) {
|
||||
right = stack.pop() as number;
|
||||
left = stack.pop() as number;
|
||||
if (right - left <= n) {
|
||||
continue;
|
||||
}
|
||||
const mid = left + Math.ceil((right - left) / n / 2) * n;
|
||||
QuickSelect(array, mid, left, right, compare);
|
||||
stack.push(left, mid, mid, right);
|
||||
}
|
||||
}
|
||||
|
||||
all(): RectTree.Node[] {
|
||||
return RectTree.Node.expandNode(this._data, []);
|
||||
}
|
||||
|
||||
load(data: RectTree.Node[]): RectTree {
|
||||
if (!(data && data.length)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
if (data.length < this._minEntries) {
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
this.insert(data[i]);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
// 使用 OMT 算法从头开始使用给定数据递归构建树
|
||||
let node = this._build(data.slice(), 0, data.length - 1, 0);
|
||||
|
||||
if (!this._data.childrenNodes.length) {
|
||||
// 如果树为空,则按原样保存
|
||||
this._data = node;
|
||||
} else if (this._data.height === node.height) {
|
||||
// 如果树具有相同的高度,则拆分根
|
||||
this._splitRoot(this._data, node);
|
||||
} else {
|
||||
if (this._data.height < node.height) {
|
||||
// 如果插入的树更大,则交换树
|
||||
const tmpNode = this._data;
|
||||
this._data = node;
|
||||
node = tmpNode;
|
||||
}
|
||||
// 将小树插入大树中适当的层次
|
||||
this._insert(node, this._data.height - node.height - 1);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
clear(): RectTree {
|
||||
this._data = RectTree.Node.makeParentNode([]);
|
||||
return this;
|
||||
}
|
||||
|
||||
remove(item: RectTree.Node, equals: Nullable<EqualsFunction<RectTree.Node>>): RectTree {
|
||||
if (!item) {
|
||||
return this;
|
||||
}
|
||||
|
||||
let node = this._data;
|
||||
const path: RectTree.Node[] = [];
|
||||
const indexes: number[] = [];
|
||||
const bbox = item;
|
||||
let i = 0;
|
||||
let goingUp: boolean = false;
|
||||
let parent: RectTree.Node = null as unknown as RectTree.Node;
|
||||
|
||||
// 深度优先迭代树遍历
|
||||
while (node || path.length) {
|
||||
if (!node) {
|
||||
// 往上
|
||||
node = path.pop() as RectTree.Node;
|
||||
parent = path[path.length - 1];
|
||||
i = indexes.pop() as number;
|
||||
goingUp = true;
|
||||
}
|
||||
|
||||
if (node.leaf) {
|
||||
// 检查当前节点
|
||||
const index = RectTree.findItem(item, node.childrenNodes, equals);
|
||||
if (index !== -1) {
|
||||
// 找到项目,移除项目并向上压缩树
|
||||
node.childrenNodes.splice(index, 1);
|
||||
path.push(node);
|
||||
this._condense(path);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
if (!goingUp && !node.leaf && node.contains(bbox)) {
|
||||
// 往下
|
||||
path.push(node);
|
||||
indexes.push(i as number);
|
||||
i = 0;
|
||||
parent = node;
|
||||
node = node.childrenNodes[0];
|
||||
} else if (parent) {
|
||||
// 向右走
|
||||
i++;
|
||||
node = parent.childrenNodes[i];
|
||||
goingUp = false;
|
||||
} else {
|
||||
// 没有发现
|
||||
node = null as unknown as RectTree.Node;
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
insert(item: RectTree.Node): RectTree {
|
||||
if (item) {
|
||||
this._insert(item, this._data.height - 1);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
collides(bbox: RectTree.Node): boolean {
|
||||
let node: RectTree.Node = this._data;
|
||||
|
||||
if (!bbox.intersects(node)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const nodesToSearch = [];
|
||||
|
||||
while (node) {
|
||||
for (let i = 0; i < node.childrenNodes.length; i++) {
|
||||
const child = node.childrenNodes[i];
|
||||
const childBBox = child;
|
||||
if (bbox.intersects(childBBox)) {
|
||||
if (node.leaf || bbox.contains(childBBox)) {
|
||||
return true;
|
||||
}
|
||||
nodesToSearch.push(child);
|
||||
}
|
||||
}
|
||||
node = nodesToSearch.pop() as RectTree.Node;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
first(bbox: RectTree.Node): RectTree.Node {
|
||||
return this.search(bbox)[0];
|
||||
}
|
||||
|
||||
search(bbox: RectTree.Node): RectTree.Node[] {
|
||||
let node = this._data;
|
||||
const result: RectTree.Node[] = [];
|
||||
|
||||
if (!bbox.intersects(node)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
const nodesToSearch = [];
|
||||
|
||||
while (node) {
|
||||
for (let i = 0; i < node.childrenNodes.length; i++) {
|
||||
const child = node.childrenNodes[i];
|
||||
const childBBox = child;
|
||||
if (bbox.intersects(childBBox)) {
|
||||
if (node.leaf) {
|
||||
result.push(child);
|
||||
} else if (bbox.contains(childBBox)) {
|
||||
RectTree.Node.expandNode(child, result);
|
||||
} else {
|
||||
nodesToSearch.push(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
node = nodesToSearch.pop() as RectTree.Node;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private _chooseSplitAxis(node: RectTree.Node, m: number, M: number) {
|
||||
// 按最佳轴对节点子节点进行排序以进行拆分
|
||||
const compareMinX = node.leaf ? this._compareMinX : defaultCompareNodeMinX;
|
||||
const compareMinY = node.leaf ? this._compareMinY : defaultCompareNodeMinY;
|
||||
const xMargin = this._allDistMargin(node, m, M, compareMinX);
|
||||
const yMargin = this._allDistMargin(node, m, M, compareMinY);
|
||||
// 如果 x 的总分配边际值最小,则按 minX 排序,
|
||||
// 否则它已经按 minY 排序
|
||||
if (xMargin < yMargin) {
|
||||
node.childrenNodes.sort(compareMinX);
|
||||
}
|
||||
}
|
||||
|
||||
private _allDistMargin(node: RectTree.Node, m: number, M: number, compare: Compare<RectTree.Node>) {
|
||||
node.childrenNodes.sort(compare);
|
||||
const leftBBox = node.distBBox(0, m);
|
||||
const rightBBox = node.distBBox(M - m, M);
|
||||
let margin = leftBBox.bboxMargin() + rightBBox.bboxMargin();
|
||||
|
||||
for (let i = m; i < M - m; i++) {
|
||||
const child = node.childrenNodes[i];
|
||||
leftBBox.extend(child);
|
||||
margin += leftBBox.bboxMargin();
|
||||
}
|
||||
for (let i = M - m - 1; i >= m; i--) {
|
||||
const child = node.childrenNodes[i];
|
||||
rightBBox.extend(child);
|
||||
margin += rightBBox.bboxMargin();
|
||||
}
|
||||
|
||||
return margin;
|
||||
}
|
||||
|
||||
private _split(insertPath: RectTree.Node[], level: number) {
|
||||
// 将溢出的节点一分为二
|
||||
const node = insertPath[level];
|
||||
const M = node.childrenNodes.length;
|
||||
const m = this._minEntries;
|
||||
|
||||
this._chooseSplitAxis(node, m, M);
|
||||
|
||||
const splitIndex = this._chooseSplitIndex(node, m, M);
|
||||
|
||||
const newNode = RectTree.Node.makeParentNode(
|
||||
node.childrenNodes.splice(splitIndex, node.childrenNodes.length - splitIndex)
|
||||
);
|
||||
newNode.height = node.height;
|
||||
newNode.leaf = node.leaf;
|
||||
|
||||
node.calcBBox();
|
||||
newNode.calcBBox();
|
||||
|
||||
if (level) {
|
||||
insertPath[level - 1].childrenNodes.push(newNode);
|
||||
} else {
|
||||
this._splitRoot(node, newNode);
|
||||
}
|
||||
}
|
||||
|
||||
private _condense(path: RectTree.Node[]) {
|
||||
// 遍历路径,删除空节点并更新 bboxes
|
||||
for (let i = path.length - 1, siblings; i >= 0; i--) {
|
||||
if (path[i].childrenNodes.length === 0) {
|
||||
if (i > 0) {
|
||||
siblings = path[i - 1].childrenNodes;
|
||||
siblings.splice(siblings.indexOf(path[i]), 1);
|
||||
} else {
|
||||
this.clear();
|
||||
}
|
||||
} else {
|
||||
path[i].calcBBox();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _splitRoot(node: RectTree.Node, newNode: RectTree.Node) {
|
||||
// 分裂根节点
|
||||
this._data = RectTree.Node.makeParentNode([node, newNode]);
|
||||
this._data.height = node.height + 1;
|
||||
this._data.leaf = false;
|
||||
this._data.calcBBox();
|
||||
}
|
||||
|
||||
private _insert(item: RectTree.Node, level: number) {
|
||||
const bbox: RectTree.Node = item;
|
||||
const insertPath: RectTree.Node[] = [];
|
||||
|
||||
// 找到容纳物品的最佳节点,同时保存路径上的所有节点
|
||||
const node = this._chooseSubtree(bbox, this._data, level, insertPath);
|
||||
|
||||
// 将项目放入节点
|
||||
node.childrenNodes.push(item);
|
||||
node.extend(bbox);
|
||||
|
||||
// 节点溢出时分裂; 必要时向上传播
|
||||
while (level >= 0) {
|
||||
if (insertPath[level].childrenNodes.length > this._maxEntries) {
|
||||
this._split(insertPath, level);
|
||||
level--;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 沿插入路径调整 bboxes
|
||||
this._adjustParentBBoxes(bbox, insertPath, level);
|
||||
}
|
||||
|
||||
private _build(items: RectTree.Node[], left: number, right: number, height: number) {
|
||||
const N = right - left + 1;
|
||||
let M = this._maxEntries;
|
||||
let node;
|
||||
|
||||
if (N <= M) {
|
||||
// 达到叶级; 回叶
|
||||
node = RectTree.Node.makeParentNode(items.slice(left, right + 1));
|
||||
node.calcBBox();
|
||||
return node;
|
||||
}
|
||||
|
||||
if (!height) {
|
||||
// 散装树的目标高度
|
||||
height = Math.ceil(Math.log(N) / Math.log(M));
|
||||
// 目标根条目数以最大限度地提高存储利用率
|
||||
M = Math.ceil(N / M ** (height - 1));
|
||||
}
|
||||
|
||||
node = RectTree.Node.makeParentNode([]);
|
||||
node.leaf = false;
|
||||
node.height = height;
|
||||
|
||||
// 将项目分成 M 个主要为方形的元素
|
||||
const N2 = Math.ceil(N / M);
|
||||
const N1 = N2 * Math.ceil(Math.sqrt(M));
|
||||
|
||||
RectTree.multiSelect(items, left, right, N1, defaultCompareNodeMinX);
|
||||
|
||||
for (let i = left; i <= right; i += N1) {
|
||||
const right2 = Math.min(i + N1 - 1, right);
|
||||
RectTree.multiSelect(items, i, right2, N2, defaultCompareNodeMinY);
|
||||
for (let j = i; j <= right2; j += N2) {
|
||||
const right3 = Math.min(j + N2 - 1, right2);
|
||||
// 递归地打包每个条目
|
||||
node.childrenNodes.push(this._build(items, j, right3, height - 1));
|
||||
}
|
||||
}
|
||||
|
||||
node.calcBBox();
|
||||
return node;
|
||||
}
|
||||
|
||||
private _chooseSplitIndex(node: RectTree.Node, m: number, M: number) {
|
||||
let index;
|
||||
let minOverlap = Infinity;
|
||||
let minArea = Infinity;
|
||||
for (let i = m; i <= M - m; i++) {
|
||||
const bbox1 = node.distBBox(0, i);
|
||||
const bbox2 = node.distBBox(i, M);
|
||||
|
||||
const overlap = bbox1.intersectionArea(bbox2);
|
||||
const area = bbox1.bboxArea() + bbox2.bboxArea();
|
||||
|
||||
// 选择重叠最小的分布
|
||||
if (overlap < minOverlap) {
|
||||
minOverlap = overlap;
|
||||
index = i;
|
||||
minArea = area < minArea ? area : minArea;
|
||||
} else if (overlap === minOverlap) {
|
||||
// 否则选择面积最小的分布
|
||||
if (area < minArea) {
|
||||
minArea = area;
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return index || M - m;
|
||||
}
|
||||
|
||||
private _adjustParentBBoxes(bbox: RectTree.Node, path: RectTree.Node[], level: number) {
|
||||
// 沿给定的树路径调整 bbox
|
||||
for (let i = level; i >= 0; i--) {
|
||||
path[i].extend(bbox);
|
||||
}
|
||||
}
|
||||
|
||||
private _chooseSubtree(bbox: RectTree.Node, node: RectTree.Node, level: number, path: RectTree.Node[]) {
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
while (true) {
|
||||
path.push(node);
|
||||
|
||||
if (node.leaf || path.length - 1 === level) {
|
||||
break;
|
||||
}
|
||||
|
||||
let minArea = Infinity;
|
||||
let minEnlargement = Infinity;
|
||||
let targetNode;
|
||||
|
||||
for (let i = 0; i < node.childrenNodes.length; i++) {
|
||||
const child = node.childrenNodes[i];
|
||||
const area = child.bboxArea();
|
||||
const enlargement = bbox.enlargedArea(child) - area;
|
||||
// 选择最小面积扩大的入口
|
||||
if (enlargement < minEnlargement) {
|
||||
minEnlargement = enlargement;
|
||||
minArea = area < minArea ? area : minArea;
|
||||
targetNode = child;
|
||||
} else if (enlargement === minEnlargement) {
|
||||
// 否则选择面积最小的一个
|
||||
if (area < minArea) {
|
||||
minArea = area;
|
||||
targetNode = child;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
node = targetNode || node.childrenNodes[0];
|
||||
}
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
export type EqualsFunction<T> = (a: T, b: T) => boolean;
|
||||
|
||||
export namespace RectTree {
|
||||
export class Node {
|
||||
minX: number;
|
||||
|
||||
minY: number;
|
||||
|
||||
maxX: number;
|
||||
|
||||
maxY: number;
|
||||
|
||||
leaf: boolean;
|
||||
|
||||
height: number;
|
||||
|
||||
childrenNodes: RectTree.Node[];
|
||||
|
||||
constructor({
|
||||
minY = Infinity, // sri
|
||||
minX = Infinity, // sci
|
||||
maxY = -Infinity, // eri
|
||||
maxX = -Infinity, // eci
|
||||
leaf = true,
|
||||
height = 1,
|
||||
childrenNodes = [],
|
||||
}: Partial<RectTree.Node> = {}) {
|
||||
this.minX = minX;
|
||||
this.minY = minY;
|
||||
this.maxX = maxX;
|
||||
this.maxY = maxY;
|
||||
this.leaf = leaf;
|
||||
this.height = height;
|
||||
this.childrenNodes = childrenNodes;
|
||||
}
|
||||
|
||||
static makeParentNode(childrenNodes: RectTree.Node[]) {
|
||||
return new RectTree.Node({ childrenNodes });
|
||||
}
|
||||
|
||||
static expandNode(node: RectTree.Node, result: RectTree.Node[]): RectTree.Node[] {
|
||||
const nodesToSearch: RectTree.Node[] = [];
|
||||
while (node) {
|
||||
if (node.leaf) {
|
||||
result.push(...node.childrenNodes);
|
||||
} else {
|
||||
nodesToSearch.push(...node.childrenNodes);
|
||||
}
|
||||
node = nodesToSearch.pop() as RectTree.Node;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
difference(other: RectTree.Node, collect: Nullable<RectTree.Node[]>) {
|
||||
const ret: RectTree.Node[] = collect || [];
|
||||
const addNode = (minY: number, minX: number, maxY: number, maxX: number) => {
|
||||
ret.push(
|
||||
new RectTree.Node({
|
||||
minY,
|
||||
minX,
|
||||
maxY,
|
||||
maxX,
|
||||
})
|
||||
);
|
||||
};
|
||||
const { minY, minX, maxY, maxX } = this;
|
||||
const diffMinY = other.minY - minY;
|
||||
const diffMinX = other.minX - minX;
|
||||
const diffMaxY = maxY - other.maxY;
|
||||
const diffMaxX = maxX - other.maxX;
|
||||
if (diffMinY > 0) {
|
||||
addNode(minY, minX, other.minY - 1, maxX);
|
||||
if (diffMaxY > 0) {
|
||||
addNode(other.maxY + 1, minX, maxY, maxX);
|
||||
if (diffMinX > 0) {
|
||||
addNode(other.minY, minX, other.maxY, other.minX - 1);
|
||||
}
|
||||
if (diffMaxX > 0) {
|
||||
addNode(other.minY, other.maxX + 1, other.maxY, maxX);
|
||||
}
|
||||
} else {
|
||||
if (diffMinX > 0) {
|
||||
addNode(other.minY, minX, maxY, other.minX - 1);
|
||||
}
|
||||
if (diffMaxX > 0) {
|
||||
addNode(other.minY, other.maxX + 1, maxY, maxX);
|
||||
}
|
||||
}
|
||||
} else if (diffMaxY > 0) {
|
||||
addNode(other.maxY + 1, minX, maxY, maxX);
|
||||
if (diffMinX > 0) {
|
||||
addNode(minY, minX, other.maxY, other.minX - 1);
|
||||
}
|
||||
if (diffMaxX > 0) {
|
||||
addNode(minY, other.maxX + 1, other.maxY, maxX);
|
||||
}
|
||||
}
|
||||
if (diffMinX > 0) {
|
||||
addNode(minY, minX, maxY, other.minX - 1);
|
||||
if (diffMaxX > 0) {
|
||||
addNode(minY, other.maxY + 1, maxY, maxX);
|
||||
if (diffMinY > 0) {
|
||||
addNode(minY, other.minX, other.minY - 1, other.maxX);
|
||||
}
|
||||
if (diffMaxY > 0) {
|
||||
addNode(other.minY + 1, other.minX, maxY, other.maxX);
|
||||
}
|
||||
} else {
|
||||
if (diffMinY > 0) {
|
||||
addNode(minY, other.minX, other.minY - 1, maxX);
|
||||
}
|
||||
if (diffMaxY > 0) {
|
||||
addNode(other.minY + 1, other.minX, maxY, maxX);
|
||||
}
|
||||
}
|
||||
} else if (diffMaxX > 0) {
|
||||
addNode(minY, other.maxX + 1, maxY, maxX);
|
||||
if (diffMinY > 0) {
|
||||
addNode(minY, minX, other.minY - 1, other.maxX);
|
||||
}
|
||||
if (diffMaxY > 0) {
|
||||
addNode(other.maxY + 1, minX, maxY, other.maxX);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bboxArea() {
|
||||
return (this.maxX - this.minX) * (this.maxY - this.minY);
|
||||
}
|
||||
|
||||
bboxMargin() {
|
||||
return this.maxX - this.minX + (this.maxY - this.minY);
|
||||
}
|
||||
|
||||
calcBBox() {
|
||||
// 从其子节点的框计算节点框
|
||||
this.distBBox(0, this.childrenNodes.length, this);
|
||||
}
|
||||
|
||||
distBBox(k: number, p: number, destNode: Nullable<RectTree.Node>) {
|
||||
// 从 k 到 p-1 的节点子节点的最小边界矩形
|
||||
if (!destNode) {
|
||||
destNode = RectTree.Node.makeParentNode(null as unknown as RectTree.Node[]);
|
||||
}
|
||||
destNode.minX = Infinity;
|
||||
destNode.minY = Infinity;
|
||||
destNode.maxX = -Infinity;
|
||||
destNode.maxY = -Infinity;
|
||||
for (let i = k; i < p; i++) {
|
||||
const child = this.childrenNodes[i];
|
||||
destNode.extend(child);
|
||||
}
|
||||
return destNode;
|
||||
}
|
||||
|
||||
contains(other: RectTree.Node) {
|
||||
return (
|
||||
this.minX <= other.minX && this.minY <= other.minY && other.maxX <= this.maxX && other.maxY <= this.maxY
|
||||
);
|
||||
}
|
||||
|
||||
intersects(other: RectTree.Node) {
|
||||
return (
|
||||
other.minX <= this.maxX && other.minY <= this.maxY && other.maxX >= this.minX && other.maxY >= this.minY
|
||||
);
|
||||
}
|
||||
|
||||
extend(other: RectTree.Node): RectTree.Node {
|
||||
this.minX = Math.min(this.minX, other.minX);
|
||||
this.minY = Math.min(this.minY, other.minY);
|
||||
this.maxX = Math.max(this.maxX, other.maxX);
|
||||
this.maxY = Math.max(this.maxY, other.maxY);
|
||||
return this;
|
||||
}
|
||||
|
||||
enlargedArea(other: RectTree.Node) {
|
||||
return (
|
||||
(Math.max(other.maxX, this.maxX) - Math.min(other.minX, this.minX)) *
|
||||
(Math.max(other.maxY, this.maxY) - Math.min(other.minY, this.minY))
|
||||
);
|
||||
}
|
||||
|
||||
intersectionArea(other: RectTree.Node) {
|
||||
const minX = Math.max(this.minX, other.minX);
|
||||
const minY = Math.max(this.minY, other.minY);
|
||||
const maxX = Math.min(this.maxX, other.maxX);
|
||||
const maxY = Math.min(this.maxY, other.maxY);
|
||||
return Math.max(0, maxX - minX) * Math.max(0, maxY - minY);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
export * from './sequence';
|
||||
export * from './serialize';
|
||||
export * from './serialize-manager';
|
||||
export * from './serializer';
|
||||
@@ -1,3 +0,0 @@
|
||||
export declare interface Sequence {
|
||||
className?: string;
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
import { Tools } from '../tools';
|
||||
import type { Sequence } from './sequence';
|
||||
import type { Serialize } from './serialize';
|
||||
|
||||
type SerializeClassType<T extends typeof Serialize = typeof Serialize> = T;
|
||||
type SerializeInstance<I extends Serialize = Serialize> = I;
|
||||
type SequenceInstance<I extends Sequence = Sequence> = I;
|
||||
|
||||
export class SerializeManager {
|
||||
protected _storage = new Map<string, SerializeClassType>();
|
||||
|
||||
addSerializer(clazz: SerializeClassType): void;
|
||||
addSerializer(clazz: SerializeClassType, name: string): void;
|
||||
addSerializer(...parameter: any): void {
|
||||
if (Tools.hasLength(parameter, 1)) {
|
||||
const clazz = parameter[0];
|
||||
this._storage.set(clazz.name, clazz);
|
||||
return;
|
||||
}
|
||||
if (Tools.hasLength(parameter, 2)) {
|
||||
const clazz = parameter[0];
|
||||
const name = parameter[1];
|
||||
this._storage.set(name, clazz);
|
||||
}
|
||||
}
|
||||
|
||||
fromSequence<I extends Serialize>(sequence: object[]): Array<SerializeInstance<I>>;
|
||||
fromSequence<I extends Serialize>(sequence: object): SerializeInstance<I>;
|
||||
fromSequence<I extends Serialize>(sequence: object | object[]): Array<SerializeInstance<I>> | SerializeInstance<I> {
|
||||
if (Tools.isObject<Sequence>(sequence)) {
|
||||
const Clazz = this._storage.get(sequence.className!) as typeof Serialize;
|
||||
return Clazz.fromSequence(sequence) as SerializeInstance<I>;
|
||||
}
|
||||
if (Tools.isArray<Sequence>(sequence)) {
|
||||
const result = new Array<SerializeInstance<I>>();
|
||||
for (const element of sequence) {
|
||||
result.push(this.fromSequence(element) as SerializeInstance<I>);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
toSequence(instance: SerializeInstance[]): SequenceInstance[];
|
||||
toSequence(instance: SerializeInstance): SequenceInstance;
|
||||
toSequence(instance: SerializeInstance | SerializeInstance[]): SequenceInstance[] | SequenceInstance {
|
||||
if (Tools.isObject<SerializeInstance>(instance)) {
|
||||
return instance.toSequence();
|
||||
}
|
||||
if (Tools.isArray<SerializeInstance>(instance)) {
|
||||
const result = new Array<SequenceInstance>();
|
||||
for (const element of instance) {
|
||||
result.push(this.toSequence(element));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
fromStringifyParse(jsonString: string): SerializeInstance[] | SerializeInstance {
|
||||
return this.fromSequence(JSON.parse(jsonString));
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import type { Sequence } from './sequence';
|
||||
|
||||
export declare class Serialize {
|
||||
static newInstance(sequence: Sequence): Serialize;
|
||||
|
||||
static fromSequence(sequence: Sequence[]): Serialize[];
|
||||
static fromSequence(sequence: Sequence): Serialize;
|
||||
|
||||
toSequence(): Sequence;
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
import { Tools } from '../tools';
|
||||
import type { Sequence } from './sequence';
|
||||
|
||||
interface SerializerNewInstanceType {
|
||||
newInstance: (sequence: Sequence) => Serializer;
|
||||
}
|
||||
|
||||
export abstract class Serializer {
|
||||
static fromSequence<T extends Serializer>(sequence: Sequence[]): T[];
|
||||
static fromSequence<T extends Serializer>(sequence: Sequence): T;
|
||||
static fromSequence<T extends Serializer>(sequence: Sequence | Sequence[]): T | T[] {
|
||||
if (Tools.isObject<Sequence>(sequence)) {
|
||||
if (Serializer.requiredNewInstance(this)) {
|
||||
const SerializerClass = this;
|
||||
return SerializerClass.newInstance(sequence) as T;
|
||||
}
|
||||
}
|
||||
if (Tools.isArray<Sequence>(sequence)) {
|
||||
if (Serializer.requiredNewInstance(this)) {
|
||||
const SerializerClass = this;
|
||||
return sequence.map((element: Sequence) => SerializerClass.newInstance(element)) as T[];
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
toSequence(): Sequence {
|
||||
return { className: Tools.getClassName(this) };
|
||||
}
|
||||
}
|
||||
|
||||
export namespace Serializer {
|
||||
export function requiredNewInstance(target: any): target is SerializerNewInstanceType {
|
||||
if (Tools.isDefine(target.newInstance)) {
|
||||
return true;
|
||||
}
|
||||
throw new Error('sequence newInstance need subclass implement!');
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
import { Tools } from './tools';
|
||||
|
||||
/**
|
||||
* Tool library for detecting data types
|
||||
*/
|
||||
export class Tuples {
|
||||
static STRING_TYPE = 1;
|
||||
|
||||
static NUMBER_TYPE = 2;
|
||||
|
||||
static BOOLEAN_TYPE = 3;
|
||||
|
||||
static FUNCTION_TYPE = 4;
|
||||
|
||||
static OBJECT_TYPE = 5;
|
||||
|
||||
static checkup(target: IArguments | unknown[], ...types: any[]): boolean {
|
||||
if (target.length !== types.length) {
|
||||
return false;
|
||||
}
|
||||
const length = target.length;
|
||||
for (let i = 0; i < length; i++) {
|
||||
const element = target[i];
|
||||
const type = types[i];
|
||||
switch (type) {
|
||||
case Tuples.BOOLEAN_TYPE: {
|
||||
if (!Tools.isBoolean(element)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Tuples.NUMBER_TYPE: {
|
||||
if (!Tools.isNumber(element)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Tuples.STRING_TYPE: {
|
||||
if (!Tools.isString(element)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Tuples.FUNCTION_TYPE: {
|
||||
if (!Tools.isFunction(element)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Tuples.OBJECT_TYPE: {
|
||||
if (!Tools.isPlainObject(element)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (!Tools.isAssignableFrom(element, type)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
/**
|
||||
* Register, get type
|
||||
*/
|
||||
export class TypeStoreBase {
|
||||
static RegisteredTypes: { [key: string]: Object } = {};
|
||||
|
||||
static GetClass(fqdn: string): any {
|
||||
if (this.RegisteredTypes && this.RegisteredTypes[fqdn]) {
|
||||
return this.RegisteredTypes[fqdn];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -521,7 +521,7 @@ export class Viewport {
|
||||
|
||||
if (!applyCanvasState && this._renderClipState) {
|
||||
ctx.beginPath();
|
||||
// DEPT: left is set by upper views but width and height is not
|
||||
// DEBT: left is set by upper views but width and height is not
|
||||
const { scaleX, scaleY } = this._getBoundScale(m[0], m[3]);
|
||||
ctx.rect(this.left, this.top, (this.width || 0) * scaleX, (this.height || 0) * scaleY);
|
||||
ctx.clip();
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable no-magic-numbers */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
import { RxDisposable } from '@univerjs/core';
|
||||
@@ -139,8 +138,6 @@ const enum ResponseType {
|
||||
SUBSCRIBE_COMPLETE = 202,
|
||||
}
|
||||
|
||||
// TODO@wzhudev:
|
||||
|
||||
interface IRPCResponse {
|
||||
/** It should be the same as its corresponding requests' `seq`. */
|
||||
seq: number;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import type { IBorderData, IStyleData, IWorksheetData } from '../../types/interfaces';
|
||||
import type { IBorderData, IStyleData, IWorksheetData } from '@univerjs/core/types/interfaces/index.js';
|
||||
|
||||
export function border(newSheet: Partial<IWorksheetData>, sheet: any) {
|
||||
newSheet.cellData = {};
|
||||
@@ -1,8 +1,445 @@
|
||||
import type { IKeyValue } from '@univerjs/core';
|
||||
import { IUniverInstanceService, migrate, Tools } from '@univerjs/core';
|
||||
import type {
|
||||
BooleanNumber,
|
||||
ICellData,
|
||||
IColumnData,
|
||||
IKeyValue,
|
||||
IRowData,
|
||||
IStyleData,
|
||||
ITextRun,
|
||||
ITextStyle,
|
||||
IWorkbookData,
|
||||
IWorksheetData,
|
||||
ObjectArrayPrimitiveType,
|
||||
} from '@univerjs/core';
|
||||
import { HorizontalAlign, IUniverInstanceService, Tools, VerticalAlign, WrapStrategy } from '@univerjs/core';
|
||||
// @ts-ignore
|
||||
import * as LuckyExcel from 'luckyexcel'; // no type definition for package luckyexcel
|
||||
|
||||
import { border } from './border';
|
||||
|
||||
export function migrate(config: any): Partial<IWorkbookData> {
|
||||
const newConfig: Partial<IWorkbookData> = {};
|
||||
newConfig.styles = {};
|
||||
if (config.hasOwnProperty('lang')) {
|
||||
newConfig.locale = config.lang;
|
||||
}
|
||||
if (config.hasOwnProperty('data')) {
|
||||
newConfig.sheets = {};
|
||||
for (const sheet of config.data) {
|
||||
const newSheet: Partial<IWorksheetData> = {};
|
||||
|
||||
// id
|
||||
// if (sheet.hasOwnProperty('index')) {
|
||||
// newSheet.id = sheet.index as string;
|
||||
// } else {
|
||||
newSheet.id = Tools.generateRandomId(6);
|
||||
// }
|
||||
|
||||
// 缩放比例
|
||||
if (sheet.hasOwnProperty('zoomRatio')) {
|
||||
newSheet.zoomRatio = sheet.zoomRatio;
|
||||
}
|
||||
|
||||
// 最大行号
|
||||
if (sheet.hasOwnProperty('row')) {
|
||||
newSheet.rowCount = sheet.row;
|
||||
}
|
||||
|
||||
// 最大列号
|
||||
if (sheet.hasOwnProperty('column')) {
|
||||
newSheet.columnCount = sheet.column;
|
||||
}
|
||||
|
||||
// 是否激活
|
||||
if (sheet.hasOwnProperty('status')) {
|
||||
newSheet.status = parseInt(sheet.status);
|
||||
}
|
||||
|
||||
// sheet 名称
|
||||
if (sheet.hasOwnProperty('name')) {
|
||||
newSheet.name = sheet.name;
|
||||
}
|
||||
|
||||
// 激活选区
|
||||
if (sheet.hasOwnProperty('universheet_select_save')) {
|
||||
newSheet.selections = [];
|
||||
sheet.universheet_select_save.forEach((selection: IKeyValue) => {
|
||||
newSheet.selections?.push({
|
||||
startRow: selection.row[0],
|
||||
endRow: selection.row[1],
|
||||
startColumn: selection.column[0],
|
||||
endColumn: selection.column[1],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 横向滚动条位置
|
||||
if (sheet.hasOwnProperty('scrollLeft')) {
|
||||
newSheet.scrollLeft = sheet.scrollLeft;
|
||||
}
|
||||
|
||||
// 竖向滚动条位置
|
||||
if (sheet.hasOwnProperty('scrollTop')) {
|
||||
newSheet.scrollTop = sheet.scrollTop;
|
||||
}
|
||||
|
||||
if (sheet.hasOwnProperty('config')) {
|
||||
// 合并单元格
|
||||
if (sheet.config.merge) {
|
||||
newSheet.mergeData = [];
|
||||
for (const key of Object.keys(sheet.config.merge)) {
|
||||
const merge: {
|
||||
r: number;
|
||||
c: number;
|
||||
rs: number;
|
||||
cs: number;
|
||||
} = sheet.config.merge[key];
|
||||
const mergeReact = {
|
||||
startRow: merge.r,
|
||||
endRow: merge.r + merge.rs - 1,
|
||||
startColumn: merge.c,
|
||||
endColumn: merge.c + merge.cs - 1,
|
||||
};
|
||||
newSheet.mergeData.push(mergeReact);
|
||||
}
|
||||
}
|
||||
|
||||
// 边框,先按顺序设置到每一个单元格对象中,设置时需要遍历四个方向的相邻单元格做去重,最后单元格样式确定之后,再统一提取到styles上
|
||||
if (sheet.config.borderInfo) {
|
||||
border(newSheet, sheet);
|
||||
}
|
||||
|
||||
// 行高
|
||||
if (sheet.config.rowlen) {
|
||||
newSheet.rowData = {};
|
||||
for (const [rowIndex, height] of Object.entries(sheet.config.rowlen)) {
|
||||
newSheet.rowData[Number(rowIndex)] = {
|
||||
h: Number(height),
|
||||
hd: 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
// 列宽
|
||||
if (sheet.config.columnlen) {
|
||||
newSheet.columnData = {};
|
||||
for (const [colIndex, width] of Object.entries(sheet.config.columnlen)) {
|
||||
newSheet.columnData[Number(colIndex)] = {
|
||||
w: Number(width),
|
||||
hd: 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
// 隐藏行
|
||||
if (sheet.config.rowhidden) {
|
||||
if (!newSheet.rowData) newSheet.rowData = {};
|
||||
const rowData = newSheet.rowData as ObjectArrayPrimitiveType<Partial<IRowData>>;
|
||||
for (const [rowIndex, isHidden] of Object.entries(sheet.config.rowhidden)) {
|
||||
if (!rowData[Number(rowIndex)]) {
|
||||
rowData[Number(rowIndex)] = {};
|
||||
}
|
||||
|
||||
rowData[Number(rowIndex)].hd = isHidden as BooleanNumber;
|
||||
}
|
||||
}
|
||||
// 隐藏列
|
||||
if (sheet.config.colhidden) {
|
||||
if (!newSheet.columnData) newSheet.columnData = {};
|
||||
const columnData = newSheet.columnData as ObjectArrayPrimitiveType<Partial<IColumnData>>;
|
||||
for (const [colIndex, isHidden] of Object.entries(sheet.config.colhidden)) {
|
||||
if (!columnData[Number(colIndex)]) {
|
||||
columnData[Number(colIndex)] = {};
|
||||
}
|
||||
columnData[Number(colIndex)].hd = isHidden as BooleanNumber;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 单元格数据
|
||||
if (sheet.hasOwnProperty('celldata')) {
|
||||
if (!newSheet.cellData) {
|
||||
newSheet.cellData = {};
|
||||
}
|
||||
|
||||
for (const cellItem of sheet.celldata) {
|
||||
if (!newSheet.cellData[cellItem.r]) {
|
||||
newSheet.cellData[cellItem.r] = {};
|
||||
}
|
||||
if (!newSheet.cellData[cellItem.r][cellItem.c]) {
|
||||
newSheet.cellData[cellItem.r][cellItem.c] = {};
|
||||
}
|
||||
|
||||
const cell = cellItem.v;
|
||||
|
||||
const newCell: ICellData = newSheet.cellData[cellItem.r][cellItem.c];
|
||||
|
||||
if (cell?.ct?.t === 'inlineStr') {
|
||||
const textRuns: ITextRun[] = [];
|
||||
let dataStream = '';
|
||||
|
||||
cell.ct.s.forEach((element: any) => {
|
||||
const textStyle: ITextStyle = {};
|
||||
|
||||
// 背景颜色
|
||||
if (element.hasOwnProperty('bg')) {
|
||||
textStyle.bg = {
|
||||
rgb: element.fc,
|
||||
};
|
||||
}
|
||||
|
||||
// 字体
|
||||
if (element.hasOwnProperty('ff')) {
|
||||
textStyle.ff = element.ff;
|
||||
}
|
||||
|
||||
// 字体颜色
|
||||
if (element.hasOwnProperty('fc')) {
|
||||
textStyle.cl = {
|
||||
rgb: element.fc,
|
||||
};
|
||||
}
|
||||
|
||||
// 字体大小
|
||||
if (element.hasOwnProperty('fs')) {
|
||||
textStyle.fs = element.fs;
|
||||
}
|
||||
|
||||
// 粗体
|
||||
if (element.hasOwnProperty('bl')) {
|
||||
textStyle.bl = element.bl;
|
||||
}
|
||||
|
||||
// 斜体
|
||||
if (element.hasOwnProperty('it')) {
|
||||
textStyle.it = element.it;
|
||||
}
|
||||
|
||||
// 删除线
|
||||
if (element.hasOwnProperty('cl')) {
|
||||
textStyle.st = {
|
||||
s: 1,
|
||||
};
|
||||
}
|
||||
|
||||
// 下划线
|
||||
if (element.hasOwnProperty('un')) {
|
||||
textStyle.ul = {
|
||||
s: element.un,
|
||||
};
|
||||
}
|
||||
|
||||
const sId = Tools.generateRandomId(6);
|
||||
dataStream += element.v;
|
||||
textRuns.push({
|
||||
sId,
|
||||
st: 0,
|
||||
ed: element.v.length - 1,
|
||||
ts: textStyle,
|
||||
});
|
||||
});
|
||||
|
||||
dataStream += '\r\n';
|
||||
|
||||
newCell.p = {
|
||||
id: Tools.generateRandomId(6),
|
||||
body: {
|
||||
dataStream,
|
||||
textRuns,
|
||||
},
|
||||
documentStyle: {},
|
||||
};
|
||||
} else {
|
||||
// 原始值
|
||||
if (cell.hasOwnProperty('v')) {
|
||||
newCell.v = cell.v;
|
||||
}
|
||||
|
||||
const cellStyle: IStyleData = {};
|
||||
|
||||
// 背景颜色
|
||||
if (cell.hasOwnProperty('bg')) {
|
||||
cellStyle.bg = {
|
||||
rgb: cell.bg,
|
||||
};
|
||||
}
|
||||
|
||||
// 字体
|
||||
if (cell.hasOwnProperty('ff')) {
|
||||
cellStyle.ff = cell.ff;
|
||||
}
|
||||
|
||||
// 字体颜色
|
||||
if (cell.hasOwnProperty('fc')) {
|
||||
cellStyle.cl = {
|
||||
rgb: cell.fc,
|
||||
};
|
||||
}
|
||||
|
||||
// 字体大小
|
||||
if (cell.hasOwnProperty('fs')) {
|
||||
cellStyle.fs = cell.fs;
|
||||
}
|
||||
|
||||
// 粗体
|
||||
if (cell.hasOwnProperty('bl')) {
|
||||
cellStyle.bl = cell.bl;
|
||||
}
|
||||
|
||||
// 斜体
|
||||
if (cell.hasOwnProperty('it')) {
|
||||
cellStyle.it = cell.it;
|
||||
}
|
||||
|
||||
// 删除线
|
||||
if (cell.hasOwnProperty('cl')) {
|
||||
cellStyle.st = {
|
||||
s: cell.cl,
|
||||
};
|
||||
}
|
||||
|
||||
// 下划线
|
||||
if (cell.hasOwnProperty('un')) {
|
||||
cellStyle.ul = {
|
||||
s: cell.un,
|
||||
};
|
||||
}
|
||||
|
||||
const sMap = {
|
||||
'0': VerticalAlign.MIDDLE,
|
||||
'1': VerticalAlign.TOP,
|
||||
'2': VerticalAlign.BOTTOM,
|
||||
};
|
||||
// const sMap = {
|
||||
// '0': 2,
|
||||
// '1': 1,
|
||||
// '2': 3,
|
||||
// };
|
||||
|
||||
// 垂直对齐
|
||||
if (cell.hasOwnProperty('vt')) {
|
||||
switch (String(cell.vt)) {
|
||||
case '0':
|
||||
cellStyle.vt = VerticalAlign.MIDDLE;
|
||||
break;
|
||||
case '1':
|
||||
cellStyle.vt = VerticalAlign.TOP;
|
||||
break;
|
||||
case '2':
|
||||
cellStyle.vt = VerticalAlign.BOTTOM;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// cellStyle.vt = sMap[String(cell.vt)];
|
||||
}
|
||||
|
||||
// 水平对齐
|
||||
if (cell.hasOwnProperty('ht')) {
|
||||
switch (String(cell.ht)) {
|
||||
case '0':
|
||||
cellStyle.ht = HorizontalAlign.CENTER;
|
||||
break;
|
||||
case '1':
|
||||
cellStyle.ht = HorizontalAlign.LEFT;
|
||||
break;
|
||||
case '2':
|
||||
cellStyle.ht = HorizontalAlign.RIGHT;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// cellStyle.ht = sMap[String(cell.ht)];
|
||||
}
|
||||
|
||||
// 竖排文字
|
||||
if (cell.hasOwnProperty('tr')) {
|
||||
switch (cell.tr) {
|
||||
case '0':
|
||||
cellStyle.tr = {
|
||||
a: 0,
|
||||
v: 0,
|
||||
};
|
||||
break;
|
||||
|
||||
case '1':
|
||||
cellStyle.tr = {
|
||||
a: 45,
|
||||
v: 0,
|
||||
};
|
||||
break;
|
||||
|
||||
case '2':
|
||||
cellStyle.tr = {
|
||||
a: -45,
|
||||
v: 0,
|
||||
};
|
||||
break;
|
||||
|
||||
case '3':
|
||||
cellStyle.tr = {
|
||||
a: 0,
|
||||
v: 1,
|
||||
};
|
||||
break;
|
||||
|
||||
case '4':
|
||||
cellStyle.tr = {
|
||||
a: 90,
|
||||
v: 0,
|
||||
};
|
||||
break;
|
||||
|
||||
case '5':
|
||||
cellStyle.tr = {
|
||||
a: -90,
|
||||
v: 0,
|
||||
};
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 文字旋转角度
|
||||
if (cell.hasOwnProperty('rt')) {
|
||||
cellStyle.tr = {
|
||||
a: cell.tr,
|
||||
v: 0,
|
||||
};
|
||||
}
|
||||
|
||||
// 文本换行
|
||||
if (cell.hasOwnProperty('tb')) {
|
||||
switch (String(cell.tb)) {
|
||||
case '0':
|
||||
cellStyle.tb = WrapStrategy.CLIP;
|
||||
break;
|
||||
case '1':
|
||||
cellStyle.tb = WrapStrategy.OVERFLOW;
|
||||
break;
|
||||
case '2':
|
||||
cellStyle.tb = WrapStrategy.WRAP;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
//cellStyle.tb = sMap[String(cell.tb)];
|
||||
}
|
||||
|
||||
newCell.s = Object.assign(newCell.s || {}, cellStyle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newConfig.sheets[newSheet.id] = newSheet;
|
||||
}
|
||||
}
|
||||
return newConfig;
|
||||
}
|
||||
|
||||
export class UploadService {
|
||||
constructor(@IUniverInstanceService private readonly _currentUniverService: IUniverInstanceService) {}
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// TODO@wzhudev: some logic here should be moved to the controller
|
||||
|
||||
import type { ICommand } from '@univerjs/core';
|
||||
import { CommandType, ICommandService, IUndoRedoService, IUniverInstanceService, RANGE_TYPE } from '@univerjs/core';
|
||||
import type { ISetFrozenMutationParams } from '@univerjs/sheets';
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// TODO@wzhudev: some logic here should be moved to the controller
|
||||
|
||||
import type { ICommand } from '@univerjs/core';
|
||||
import { CommandType, ICommandService, IUndoRedoService, IUniverInstanceService, RANGE_TYPE } from '@univerjs/core';
|
||||
import type { ISetFrozenMutationParams } from '@univerjs/sheets';
|
||||
|
||||
@@ -207,19 +207,17 @@ export class SheetUIController extends Disposable {
|
||||
this._init();
|
||||
}
|
||||
|
||||
private _init(): void {
|
||||
// init custom component
|
||||
private _initCustomComponents(): void {
|
||||
const componentManager = this._componentManager;
|
||||
this.disposeWithMe(componentManager.register(MENU_ITEM_INPUT_COMPONENT, MenuItemInput));
|
||||
this.disposeWithMe(componentManager.register(BORDER_PANEL_COMPONENT, BorderPanel));
|
||||
this.disposeWithMe(componentManager.register(COLOR_PICKER_COMPONENT, ColorPicker));
|
||||
this.disposeWithMe(componentManager.register(FONT_FAMILY_COMPONENT, FontFamily));
|
||||
this.disposeWithMe(componentManager.register(FONT_FAMILY_ITEM_COMPONENT, FontFamilyItem));
|
||||
this.disposeWithMe(componentManager.register(FONT_SIZE_COMPONENT, FontSize));
|
||||
}
|
||||
|
||||
// FIXME: no dispose logic
|
||||
componentManager.register(MENU_ITEM_INPUT_COMPONENT, MenuItemInput);
|
||||
componentManager.register(BORDER_PANEL_COMPONENT, BorderPanel);
|
||||
componentManager.register(COLOR_PICKER_COMPONENT, ColorPicker);
|
||||
componentManager.register(FONT_FAMILY_COMPONENT, FontFamily);
|
||||
componentManager.register(FONT_FAMILY_ITEM_COMPONENT, FontFamilyItem);
|
||||
componentManager.register(FONT_SIZE_COMPONENT, FontSize);
|
||||
|
||||
// init commands
|
||||
private _initCommands(): void {
|
||||
[
|
||||
AddWorksheetMergeAllCommand,
|
||||
AddWorksheetMergeCommand,
|
||||
@@ -268,8 +266,9 @@ export class SheetUIController extends Disposable {
|
||||
].forEach((c) => {
|
||||
this.disposeWithMe(this._commandService.registerCommand(c));
|
||||
});
|
||||
}
|
||||
|
||||
// init menus
|
||||
private _initMenus(): void {
|
||||
(
|
||||
[
|
||||
// context menu
|
||||
@@ -346,8 +345,9 @@ export class SheetUIController extends Disposable {
|
||||
).forEach((factory) => {
|
||||
this.disposeWithMe(this._menuService.addMenuItem(this._injector.invoke(factory)));
|
||||
});
|
||||
}
|
||||
|
||||
// init shortcuts
|
||||
private _initShortcuts(): void {
|
||||
[
|
||||
// selection shortcuts
|
||||
MoveSelectionDownShortcutItem,
|
||||
@@ -400,7 +400,9 @@ export class SheetUIController extends Disposable {
|
||||
].forEach((item) => {
|
||||
this.disposeWithMe(this._shortcutService.registerShortcut(item));
|
||||
});
|
||||
}
|
||||
|
||||
private _initWorkbenchParts(): void {
|
||||
this.disposeWithMe(
|
||||
this._uiController.registerHeaderComponent(() => connectInjector(RenderSheetHeader, this._injector))
|
||||
);
|
||||
@@ -413,4 +415,12 @@ export class SheetUIController extends Disposable {
|
||||
this._uiController.registerContentComponent(() => connectInjector(RenderSheetContent, this._injector))
|
||||
);
|
||||
}
|
||||
|
||||
private _init(): void {
|
||||
this._initCustomComponents();
|
||||
this._initCommands();
|
||||
this._initMenus();
|
||||
this._initShortcuts();
|
||||
this._initWorkbenchParts();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user