refactor(debugger): refactor debugger plugin (#5213)

This commit is contained in:
白熱
2025-05-17 00:20:54 +08:00
committed by GitHub
parent f53819050f
commit e21dd65082
57 changed files with 935 additions and 1294 deletions
+8 -2
View File
@@ -72,10 +72,16 @@ univer.registerPlugin(UniverDocsQuickInsertUIPlugin);
if (!IS_E2E) {
univer.createUnit(UniverInstanceType.UNIVER_DOC, DEFAULT_DOCUMENT_DATA_SIMPLE);
univer.registerPlugin(UniverDebuggerPlugin);
} else {
univer.registerPlugin(UniverDebuggerPlugin, {
fab: false,
performanceMonitor: {
enabled: false,
},
});
}
univer.registerPlugin(UniverDebuggerPlugin);
// use for console test
declare global {
// eslint-disable-next-line ts/naming-convention
+1
View File
@@ -138,6 +138,7 @@ function createNewInstance() {
// If we are running in e2e platform, we should immediately register the debugger plugin.
if (IS_E2E) {
univer.registerPlugin(UniverDebuggerPlugin, {
fab: false,
performanceMonitor: {
enabled: false,
},
-1
View File
@@ -29,7 +29,6 @@ const IS_E2E: boolean = !!process.env.IS_E2E;
export default function getVeryLazyPlugins() {
const plugins: Array<[PluginCtor<Plugin>] | [PluginCtor<Plugin>, unknown]> = [
[UniverActionRecorderPlugin],
[UniverSheetsHyperLinkUIPlugin],
[UniverSheetsSortUIPlugin],
+1 -6
View File
@@ -66,7 +66,6 @@
"@univerjs/core": "workspace:*",
"@univerjs/design": "workspace:*",
"@univerjs/docs-drawing-ui": "workspace:*",
"@univerjs/docs-ui": "workspace:*",
"@univerjs/engine-render": "workspace:*",
"@univerjs/icons": "^0.3.24",
"@univerjs/mockdata": "workspace:*",
@@ -76,9 +75,6 @@
"@univerjs/ui": "workspace:*",
"@univerjs/watermark": "workspace:*"
},
"optionalDependencies": {
"vue": ">=3.0.0"
},
"devDependencies": {
"@univerjs-infra/shared": "workspace:*",
"postcss": "^8.5.3",
@@ -87,7 +83,6 @@
"tailwindcss": "^3.4.17",
"typescript": "^5.8.3",
"vite": "^6.3.5",
"vitest": "^3.1.3",
"vue": "^3.5.13"
"vitest": "^3.1.3"
}
}
@@ -1,76 +0,0 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { ICommand, IWorkbookData, Univer, Workbook } from '@univerjs/core';
import type { FUniver } from '@univerjs/core/facade';
import { CommandType, IUniverInstanceService, UniverInstanceType } from '@univerjs/core';
import { ILocalFileService } from '@univerjs/ui';
declare global {
// eslint-disable-next-line ts/naming-convention
interface Window {
univer?: Univer;
univerAPI?: ReturnType<typeof FUniver.newAPI>;
}
}
export const DisposeUniverCommand: ICommand = {
id: 'debugger.command.dispose-unit',
type: CommandType.COMMAND,
handler: () => {
window.univer?.dispose();
window.univer = undefined;
window.univerAPI = undefined;
return true;
},
};
export const DisposeCurrentUnitCommand: ICommand = {
id: 'debugger.command.dispose-current-unit',
type: CommandType.COMMAND,
handler: async (accessor) => {
const univerInstanceService = accessor.get(IUniverInstanceService);
const focused = univerInstanceService.getFocusedUnit();
if (!focused) return false;
return univerInstanceService.disposeUnit(focused.getUnitId());
},
};
export const CreateEmptySheetCommand: ICommand = {
id: 'debugger.command.create-empty-sheet',
type: CommandType.COMMAND,
handler: async (accessor) => {
const univerInstanceService = accessor.get(IUniverInstanceService);
univerInstanceService.createUnit(UniverInstanceType.UNIVER_SHEET, {});
return true;
},
};
export const LoadSheetSnapshotCommand: ICommand = {
id: 'debugger.command.load-sheet-snapshot',
type: CommandType.COMMAND,
handler: async (accessor) => {
const fileOpenerService = accessor.get(ILocalFileService);
const snapshotFile = await fileOpenerService.openFile({ multiple: false, accept: '.json' });
if (snapshotFile.length !== 1) return false;
const text = await snapshotFile[0].text();
const instanceService = accessor.get(IUniverInstanceService);
instanceService.createUnit<IWorkbookData, Workbook>(UniverInstanceType.UNIVER_SHEET, JSON.parse(text));
return true;
},
};
@@ -1,50 +0,0 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { CommandType, createDefaultUser, IPermissionService, IUniverInstanceService, UniverInstanceType, UserManagerService } from '@univerjs/core';
import { WorkbookManageCollaboratorPermission } from '@univerjs/sheets';
import type { IAccessor, ICommand, Workbook } from '@univerjs/core';
export enum UnitRole {
Reader = 0,
Editor = 1,
Owner = 2,
UNRECOGNIZED = -1,
}
interface IChangeUserParams {
value: UnitRole.Reader | UnitRole.Owner;
}
export const ChangeUserCommand: ICommand = {
id: 'debugger.operation.changeUser',
type: CommandType.OPERATION,
handler: async (accessor: IAccessor, params: IChangeUserParams) => {
const userManagerService = accessor.get(UserManagerService);
const univerInstanceService = accessor.get(IUniverInstanceService);
userManagerService.setCurrentUser(createDefaultUser(params.value));
const workbook = univerInstanceService.getCurrentUnitForType<Workbook>(UniverInstanceType.UNIVER_SHEET)!;
const permissionService = accessor.get(IPermissionService);
const unitId = workbook.getUnitId();
if (params.value === UnitRole.Owner) {
permissionService.updatePermissionPoint(new WorkbookManageCollaboratorPermission(unitId).id, true);
} else {
permissionService.updatePermissionPoint(new WorkbookManageCollaboratorPermission(unitId).id, false);
}
return true;
},
};
@@ -1,53 +0,0 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { IAccessor, ICommand } from '@univerjs/core';
import { CommandType } from '@univerjs/core';
import { IConfirmService } from '@univerjs/ui';
export interface IUIComponentCommandParams {
value: string;
}
export const ConfirmOperation: ICommand = {
id: 'debugger.operation.confirm',
type: CommandType.COMMAND,
handler: async (accessor: IAccessor, params: IUIComponentCommandParams) => {
const confirmService = accessor.get(IConfirmService);
confirmService.open({
id: 'confirm1',
children: { title: 'Confirm Content' },
title: { title: 'Confirm Title' },
confirmText: 'hello',
cancelText: 'world',
onClose() {
confirmService.close('confirm1');
},
});
confirmService.open({
id: 'confirm2',
children: { title: 'Confirm2 Content' },
title: { title: 'Confirm2 Title' },
onClose() {
confirmService.close('confirm2');
},
});
return true;
},
};
@@ -1,34 +0,0 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { IAccessor, ICommand } from '@univerjs/core';
import { CommandType, ThemeService } from '@univerjs/core';
export interface IDarkModeCommandParams {
value?: 'dark' | 'light';
}
export const DarkModeOperation: ICommand = {
id: 'debugger.operation.dark-mode',
type: CommandType.COMMAND,
handler: async (accessor: IAccessor, params: IDarkModeCommandParams) => {
const themeService = accessor.get(ThemeService);
themeService.setDarkMode(params.value === 'dark');
localStorage.setItem('local.darkMode', params.value ?? 'light');
return true;
},
};
@@ -1,32 +0,0 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { CommandType, LocaleService } from '@univerjs/core';
import type { IAccessor, ICommand, LocaleType } from '@univerjs/core';
export interface ILocaleCommandParams {
value: LocaleType;
}
export const LocaleOperation: ICommand = {
id: 'debugger.operation.locale',
type: CommandType.COMMAND,
handler: async (accessor: IAccessor, params: ILocaleCommandParams) => {
const localeService = accessor.get(LocaleService);
localeService.setLocale(params.value);
return true;
},
};
@@ -1,47 +0,0 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { IAccessor, ICommand } from '@univerjs/core';
import { CommandType } from '@univerjs/core';
import { INotificationService } from '@univerjs/ui';
export interface IUIComponentCommandParams {
value: string;
}
export const NotificationOperation: ICommand = {
id: 'debugger.operation.notification',
type: CommandType.COMMAND,
handler: async (accessor: IAccessor, params: IUIComponentCommandParams) => {
const notificationService = accessor.get(INotificationService);
const { value = '' } = params;
const type =
value.indexOf('Success') > -1
? 'success'
: value.indexOf('Info') > -1
? 'info'
: value.indexOf('Warning') > -1
? 'warning'
: 'error';
notificationService.show({
type,
content: value || 'Notification Content',
title: 'Notification Title',
});
return true;
},
};
@@ -1,36 +0,0 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { CommandType, ICommandService } from '@univerjs/core';
import type { IAccessor, ICommand } from '@univerjs/core';
export interface INumfmtOperationCommandParams {
value: 'close' | 'open';
}
export const NumfmtOperation: ICommand = {
id: 'debugger.operation.numfmt',
type: CommandType.COMMAND,
handler: async (accessor: IAccessor, params: INumfmtOperationCommandParams) => {
const confirmService = accessor.get(ICommandService);
if (params.value === 'open') {
confirmService.executeCommand('sheet.operation.open.numfmt.panel');
} else {
confirmService.executeCommand('sheet.operation.close.numfmt.panel');
}
return true;
},
};
@@ -1,120 +0,0 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable node/prefer-global/process */
import { CommandType, IResourceLoaderService, IUniverInstanceService, ObjectMatrix, UniverInstanceType } from '@univerjs/core';
import { ILocalFileService } from '@univerjs/ui';
import type { DocumentDataModel, IAccessor, ICommand, IStyleData, IWorkbookData, Workbook } from '@univerjs/core';
import { RecordController } from '../../controllers/local-save/record.controller';
export interface ISaveSnapshotParams {
value: 'sheet' | 'workbook' | 'record';
}
const filterStyle = (workbookData: IWorkbookData) => {
const sheets = workbookData.sheets;
const cacheStyle: Record<string, IStyleData> = {};
Object.keys(sheets).forEach((sheetId) => {
const sheet = sheets[sheetId];
new ObjectMatrix(sheet.cellData).forValue((_r, _c, value) => {
const s = value?.s;
if (s && typeof s === 'string') {
const style = workbookData.styles[s];
if (style) {
cacheStyle[s] = style;
}
}
});
});
workbookData.styles = cacheStyle;
return workbookData;
};
export const SaveSnapshotOptions: ICommand = {
id: 'debugger.operation.saveSnapshot',
type: CommandType.OPERATION,
handler: async (accessor: IAccessor, params: ISaveSnapshotParams) => {
const univerInstanceService = accessor.get(IUniverInstanceService);
const resourceLoaderService = accessor.get(IResourceLoaderService);
const localFileService = accessor.get(ILocalFileService);
const recordController = accessor.get(RecordController);
const preName = new Date().toLocaleString();
const workbook = univerInstanceService.getCurrentUnitForType<Workbook>(UniverInstanceType.UNIVER_SHEET)!;
if (!workbook) {
const doc = univerInstanceService.getCurrentUnitForType<DocumentDataModel>(UniverInstanceType.UNIVER_DOC)!;
const snapshot = resourceLoaderService.saveUnit(doc.getUnitId());
if (process.env.NODE_ENV === 'production') {
const gitHash = process.env.GIT_COMMIT_HASH;
const gitBranch = process.env.GIT_REF_NAME;
const buildTime = process.env.BUILD_TIME;
(snapshot as any).__env__ = { gitHash, gitBranch, buildTime };
}
const text = JSON.stringify(snapshot, null, 2);
localFileService.downloadFile(new Blob([text]), `${preName} snapshot.json`);
return true;
}
const worksheet = workbook.getActiveSheet();
if (!worksheet) {
return false;
}
const snapshot = resourceLoaderService.saveUnit<ReturnType<typeof workbook.getSnapshot>>(workbook.getUnitId())!;
if (process.env.NODE_ENV === 'production') {
const gitHash = process.env.GIT_COMMIT_HASH;
const gitBranch = process.env.GIT_REF_NAME;
const buildTime = process.env.BUILD_TIME;
(snapshot as any).__env__ = { gitHash, gitBranch, buildTime };
}
switch (params.value) {
case 'sheet': {
const sheetId = worksheet.getSheetId();
const sheet = snapshot.sheets[sheetId];
snapshot.sheets = { [sheetId]: sheet };
snapshot.sheetOrder = [sheetId];
const text = JSON.stringify(filterStyle(snapshot), null, 2);
localFileService.downloadFile(new Blob([text]), `${preName} snapshot.json`);
break;
}
case 'workbook': {
const text = JSON.stringify(filterStyle(snapshot), null, 2);
localFileService.downloadFile(new Blob([text]), `${preName} snapshot.json`);
break;
}
case 'record': {
let endCommands = () => [];
recordController.record().subscribe((v) => {
if (v.type === 'start') {
endCommands = recordController.startSaveCommands() as any;
}
if (v.type === 'finish') {
const commands = endCommands();
localFileService.downloadFile(v.data, `${preName} video.webm`);
localFileService.downloadFile(new Blob([JSON.stringify(commands, null, 2)]), `${preName} commands.json`);
}
});
}
}
return true;
},
};
@@ -1,53 +0,0 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { IAccessor, ICommand, Workbook } from '@univerjs/core';
import { CommandType, IUniverInstanceService, UniverInstanceType } from '@univerjs/core';
import { IEditorService } from '@univerjs/docs-ui';
import { ISidebarService } from '@univerjs/ui';
import { TEST_EDITOR_CONTAINER_COMPONENT } from '../../views/test-editor/component-name';
export interface IUIComponentCommandParams {
value: string;
}
export const SidebarOperation: ICommand = {
id: 'debugger.operation.sidebar',
type: CommandType.COMMAND,
handler: async (accessor: IAccessor, params: IUIComponentCommandParams) => {
const sidebarService = accessor.get(ISidebarService);
const editorService = accessor.get(IEditorService);
const univerInstanceService = accessor.get(IUniverInstanceService);
const unit = univerInstanceService.getCurrentUnitForType<Workbook>(UniverInstanceType.UNIVER_SHEET)!;
switch (params.value) {
case 'open':
sidebarService.open({
header: { title: 'Sidebar title' },
children: { label: TEST_EDITOR_CONTAINER_COMPONENT },
footer: { title: 'Sidebar Footer' },
onClose: () => {
},
});
break;
case 'close':
default:
sidebarService.close();
break;
}
return true;
},
};
@@ -1,34 +0,0 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { IAccessor, ICommand } from '@univerjs/core';
import type { Theme } from '@univerjs/themes';
import { CommandType, ThemeService } from '@univerjs/core';
export interface IThemeCommandParams {
value?: Theme;
}
export const ThemeOperation: ICommand = {
id: 'debugger.operation.theme',
type: CommandType.COMMAND,
handler: async (accessor: IAccessor, params: IThemeCommandParams) => {
const themeService = accessor.get(ThemeService);
params.value && themeService.setTheme(params.value);
return true;
},
};
@@ -1,5 +0,0 @@
<template>
<section>
🌍
</section>
</template>
@@ -22,9 +22,15 @@ export const configSymbol = Symbol(DEBUGGER_PLUGIN_CONFIG_KEY);
export interface IUniverDebuggerConfig {
menu?: MenuConfig;
fab?: boolean;
performanceMonitor?: {
enabled: boolean;
};
}
export const defaultPluginConfig: IUniverDebuggerConfig = {};
export const defaultPluginConfig: IUniverDebuggerConfig = {
fab: true,
performanceMonitor: {
enabled: true,
},
};
@@ -14,79 +14,41 @@
* limitations under the License.
*/
import { Disposable, ICommandService, Inject, Injector } from '@univerjs/core';
import { ComponentManager, IMenuManagerService } from '@univerjs/ui';
import { CreateFloatDomCommand } from '../commands/commands/float-dom.command';
import { CreateEmptySheetCommand, DisposeCurrentUnitCommand, DisposeUniverCommand, LoadSheetSnapshotCommand } from '../commands/commands/unit.command';
import { ShowCellContentOperation } from '../commands/operations/cell.operation';
import { ChangeUserCommand } from '../commands/operations/change-user.operation';
import { ConfirmOperation } from '../commands/operations/confirm.operation';
import { DarkModeOperation } from '../commands/operations/dark-mode.operation';
import { DialogOperation } from '../commands/operations/dialog.operation';
import { LocaleOperation } from '../commands/operations/locale.operation';
import { MessageOperation } from '../commands/operations/message.operation';
import { NotificationOperation } from '../commands/operations/notification.operation';
import { OpenWatermarkPanelOperation } from '../commands/operations/open-watermark-panel.operation';
import { SaveSnapshotOptions } from '../commands/operations/save-snapshot.operations';
import { SetEditable } from '../commands/operations/set.editable.operation';
import { SidebarOperation } from '../commands/operations/sidebar.operation';
import { ThemeOperation } from '../commands/operations/theme.operation';
import type { IUniverDebuggerConfig } from './config.schema';
import { Disposable, IConfigService, Inject, Injector } from '@univerjs/core';
import { BuiltInUIPart, ComponentManager, connectInjector, IUIPartsService } from '@univerjs/ui';
import { AIButton, FloatButton } from '../components/FloatButton';
import { ImageDemo } from '../components/Image';
import { RangeLoading } from '../components/RangeLoading';
// @ts-ignore
import VueI18nIcon from '../components/VueI18nIcon.vue';
import { Fab } from '../views/Fab';
import { DEBUGGER_PLUGIN_CONFIG_KEY } from './config.schema';
import { RecordController } from './local-save/record.controller';
import { menuSchema } from './menu.schema';
export class DebuggerController extends Disposable {
constructor(
@Inject(Injector) private readonly _injector: Injector,
@IMenuManagerService private readonly _menuManagerService: IMenuManagerService,
@ICommandService private readonly _commandService: ICommandService,
@IConfigService private readonly _configService: IConfigService,
@IUIPartsService protected readonly _uiPartsService: IUIPartsService,
@Inject(ComponentManager) private readonly _componentManager: ComponentManager
) {
super();
this._initializeMenu();
this._initCustomComponents();
[
LocaleOperation,
DarkModeOperation,
ThemeOperation,
NotificationOperation,
DialogOperation,
ConfirmOperation,
MessageOperation,
SidebarOperation,
SetEditable,
SaveSnapshotOptions,
DisposeUniverCommand,
DisposeCurrentUnitCommand,
CreateEmptySheetCommand,
LoadSheetSnapshotCommand,
CreateFloatDomCommand,
ChangeUserCommand,
ShowCellContentOperation,
OpenWatermarkPanelOperation,
].forEach((command) => this.disposeWithMe(this._commandService.registerCommand(command)));
this._injector.add([RecordController]);
}
private _initializeMenu() {
this._menuManagerService.mergeMenu(menuSchema);
}
private _initCustomComponents(): void {
const componentManager = this._componentManager;
this.disposeWithMe(componentManager.register('VueI18nIcon', VueI18nIcon, {
framework: 'vue3',
}));
this.disposeWithMe(componentManager.register('ImageDemo', ImageDemo));
this.disposeWithMe(componentManager.register('RangeLoading', RangeLoading));
this.disposeWithMe(componentManager.register('FloatButton', FloatButton));
this.disposeWithMe(componentManager.register('AIButton', AIButton));
const configs = this._configService.getConfig<IUniverDebuggerConfig>(DEBUGGER_PLUGIN_CONFIG_KEY);
if (configs?.fab) {
this._uiPartsService.registerComponent(BuiltInUIPart.GLOBAL, () => connectInjector(Fab, this._injector));
}
}
}
@@ -14,10 +14,10 @@
* limitations under the License.
*/
import { awaitTime, Disposable, ICommandService, Inject, IUniverInstanceService, ThemeService, UniverInstanceType } from '@univerjs/core';
import type { Univer } from '@univerjs/core';
import type { FUniver } from '@univerjs/core/facade';
import { awaitTime, Disposable, Inject, IUniverInstanceService, ThemeService, UniverInstanceType } from '@univerjs/core';
import { DEFAULT_WORKBOOK_DATA_DEMO, DEFAULT_WORKBOOK_DATA_DEMO_DEFAULT_STYLE } from '@univerjs/mockdata';
import { DisposeUniverCommand } from '../../commands/commands/unit.command';
import { getDefaultDocData } from './data/default-doc';
import { getDefaultWorkbookData } from './data/default-sheet';
@@ -42,6 +42,8 @@ declare global {
// eslint-disable-next-line ts/naming-convention
interface Window {
E2EControllerAPI: IE2EControllerAPI;
univer?: Univer;
univerAPI?: FUniver;
}
}
@@ -51,7 +53,6 @@ declare global {
export class E2EController extends Disposable {
constructor(
@IUniverInstanceService private readonly _univerInstanceService: IUniverInstanceService,
@ICommandService private readonly _commandService: ICommandService,
@Inject(ThemeService) private readonly _themeService: ThemeService
) {
super();
@@ -126,7 +127,9 @@ export class E2EController extends Disposable {
}
private async _disposeUniver(): Promise<void> {
await this._commandService.executeCommand(DisposeUniverCommand.id);
window.univer?.dispose();
window.univer = undefined;
window.univerAPI = undefined;
}
private async _disposeDefaultSheetUnit(disposingTimeout: number = AWAIT_DISPOSING_TIMEOUT): Promise<void> {
@@ -1,142 +0,0 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { MenuSchemaType } from '@univerjs/ui';
import { RibbonOthersGroup } from '@univerjs/ui';
import { CreateFloatDomCommand } from '../commands/commands/float-dom.command';
import { CreateEmptySheetCommand, DisposeCurrentUnitCommand, DisposeUniverCommand, LoadSheetSnapshotCommand } from '../commands/commands/unit.command';
import { ShowCellContentOperation } from '../commands/operations/cell.operation';
import { ChangeUserCommand } from '../commands/operations/change-user.operation';
import { ConfirmOperation } from '../commands/operations/confirm.operation';
import { DarkModeOperation } from '../commands/operations/dark-mode.operation';
import { DialogOperation } from '../commands/operations/dialog.operation';
import { LocaleOperation } from '../commands/operations/locale.operation';
import { MessageOperation } from '../commands/operations/message.operation';
import { NotificationOperation } from '../commands/operations/notification.operation';
import { OpenWatermarkPanelOperation } from '../commands/operations/open-watermark-panel.operation';
import { SaveSnapshotOptions } from '../commands/operations/save-snapshot.operations';
import { SetEditable } from '../commands/operations/set.editable.operation';
import { SidebarOperation } from '../commands/operations/sidebar.operation';
import { ThemeOperation } from '../commands/operations/theme.operation';
import {
ChangeUserMenuItemFactory,
ConfirmMenuItemFactory,
CreateEmptySheetMenuItemFactory,
CreateFloatDOMMenuItemFactory,
DarkModeMenuItemFactory,
DialogMenuItemFactory,
DisposeCurrentUnitMenuItemFactory,
DisposeUniverItemFactory,
FLOAT_DOM_ITEM_MENU_ID,
FloatDomMenuItemFactory,
LoadSheetSnapshotMenuItemFactory,
LocaleMenuItemFactory,
MessageMenuItemFactory,
NotificationMenuItemFactory,
SaveSnapshotSetEditableMenuItemFactory,
SetEditableMenuItemFactory,
ShowCellContentMenuItemFactory,
SidebarMenuItemFactory,
ThemeMenuItemFactory,
UNIT_ITEM_MENU_ID,
UnitMenuItemFactory,
WatermarkMenuItemFactory,
} from '../controllers/menu';
export const menuSchema: MenuSchemaType = {
[RibbonOthersGroup.OTHERS]: {
[LocaleOperation.id]: {
order: 0,
menuItemFactory: LocaleMenuItemFactory,
},
[DarkModeOperation.id]: {
order: 0.9,
menuItemFactory: DarkModeMenuItemFactory,
},
[ThemeOperation.id]: {
order: 1,
menuItemFactory: ThemeMenuItemFactory,
},
[NotificationOperation.id]: {
order: 2,
menuItemFactory: NotificationMenuItemFactory,
},
[DialogOperation.id]: {
order: 3,
menuItemFactory: DialogMenuItemFactory,
},
[ConfirmOperation.id]: {
order: 4,
menuItemFactory: ConfirmMenuItemFactory,
},
[MessageOperation.id]: {
order: 5,
menuItemFactory: MessageMenuItemFactory,
},
[SidebarOperation.id]: {
order: 6,
menuItemFactory: SidebarMenuItemFactory,
},
[SetEditable.id]: {
order: 7,
menuItemFactory: SetEditableMenuItemFactory,
},
[SaveSnapshotOptions.id]: {
order: 8,
menuItemFactory: SaveSnapshotSetEditableMenuItemFactory,
[LoadSheetSnapshotCommand.id]: {
order: 3,
menuItemFactory: LoadSheetSnapshotMenuItemFactory,
},
},
[UNIT_ITEM_MENU_ID]: {
order: 9,
menuItemFactory: UnitMenuItemFactory,
[DisposeUniverCommand.id]: {
order: 0,
menuItemFactory: DisposeUniverItemFactory,
},
[DisposeCurrentUnitCommand.id]: {
order: 1,
menuItemFactory: DisposeCurrentUnitMenuItemFactory,
},
[CreateEmptySheetCommand.id]: {
order: 2,
menuItemFactory: CreateEmptySheetMenuItemFactory,
},
},
[FLOAT_DOM_ITEM_MENU_ID]: {
order: 10,
menuItemFactory: FloatDomMenuItemFactory,
[CreateFloatDomCommand.id]: {
order: 0,
menuItemFactory: CreateFloatDOMMenuItemFactory,
},
[ShowCellContentOperation.id]: {
order: 1,
menuItemFactory: ShowCellContentMenuItemFactory,
},
},
[ChangeUserCommand.id]: {
order: 11,
menuItemFactory: ChangeUserMenuItemFactory,
},
[OpenWatermarkPanelOperation.id]: {
order: 12,
menuItemFactory: WatermarkMenuItemFactory,
},
},
};
@@ -1,365 +0,0 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { IAccessor } from '@univerjs/core';
import type { IMenuButtonItem, IMenuSelectorItem } from '@univerjs/ui';
import { LocaleType } from '@univerjs/core';
import { defaultTheme, greenTheme } from '@univerjs/themes';
// import { defaultTheme } from '@univerjs/themes';
import { MenuItemType } from '@univerjs/ui';
import { CreateFloatDomCommand } from '../commands/commands/float-dom.command';
import { CreateEmptySheetCommand, DisposeCurrentUnitCommand, DisposeUniverCommand, LoadSheetSnapshotCommand } from '../commands/commands/unit.command';
import { ShowCellContentOperation } from '../commands/operations/cell.operation';
import { ChangeUserCommand, UnitRole } from '../commands/operations/change-user.operation';
import { ConfirmOperation } from '../commands/operations/confirm.operation';
import { DarkModeOperation } from '../commands/operations/dark-mode.operation';
import { DialogOperation } from '../commands/operations/dialog.operation';
import { LocaleOperation } from '../commands/operations/locale.operation';
import { MessageOperation } from '../commands/operations/message.operation';
import { NotificationOperation } from '../commands/operations/notification.operation';
import { OpenWatermarkPanelOperation } from '../commands/operations/open-watermark-panel.operation';
import { SaveSnapshotOptions } from '../commands/operations/save-snapshot.operations';
import { SetEditable } from '../commands/operations/set.editable.operation';
import { SidebarOperation } from '../commands/operations/sidebar.operation';
import { ThemeOperation } from '../commands/operations/theme.operation';
import { UNIVER_WATERMARK_MENU } from './watermark.menu.controller';
export function LocaleMenuItemFactory(): IMenuSelectorItem {
return {
id: LocaleOperation.id,
icon: 'VueI18nIcon',
tooltip: 'i18n',
type: MenuItemType.SELECTOR,
selections: [
{
label: 'English',
value: LocaleType.EN_US,
},
{
label: 'Français',
value: LocaleType.FR_FR,
},
{
label: '简体中文',
value: LocaleType.ZH_CN,
},
{
label: 'Русский',
value: LocaleType.RU_RU,
},
{
label: '繁體中文',
value: LocaleType.ZH_TW,
},
{
label: 'Tiếng Việt',
value: LocaleType.VI_VN,
},
],
};
}
export function DarkModeMenuItemFactory(): IMenuSelectorItem {
return {
id: DarkModeOperation.id,
title: '🌓',
tooltip: 'Dark Mode',
type: MenuItemType.SELECTOR,
selections: [
{
label: 'Light',
value: 'light',
},
{
label: 'Dark',
value: 'dark',
},
],
};
}
export function ThemeMenuItemFactory(): IMenuSelectorItem {
return {
id: ThemeOperation.id,
title: 'Theme',
tooltip: 'Theme',
type: MenuItemType.SELECTOR,
selections: [
{
label: '🟢',
value: greenTheme as any,
},
{
label: '🔵',
value: defaultTheme as any,
},
],
};
}
export function NotificationMenuItemFactory(): IMenuSelectorItem {
return {
id: NotificationOperation.id,
title: 'Notification',
tooltip: 'Notification',
type: MenuItemType.SELECTOR,
selections: [
{
label: 'Notification Success',
value: 'Notification Success random string to test Notification Success random string to test Notification Success random string to test Notification Success random string to test Notification Success random string to test',
},
{
label: 'Notification Info',
value: 'Notification Info',
},
{
label: 'Notification Warning',
value: 'Notification Warning',
},
{
label: 'Notification Error',
value: 'Notification Error',
},
],
};
}
export function DialogMenuItemFactory(): IMenuSelectorItem {
return {
id: DialogOperation.id,
title: 'Dialog',
tooltip: 'Dialog',
type: MenuItemType.SELECTOR,
selections: [
{
label: 'Open Dialog',
value: 'dialog',
},
{
label: 'Draggable Dialog',
value: 'draggable',
},
],
};
}
export function ConfirmMenuItemFactory(): IMenuSelectorItem {
return {
id: ConfirmOperation.id,
title: 'Confirm',
tooltip: 'Confirm',
type: MenuItemType.SELECTOR,
selections: [
{
label: 'Open confirm',
value: 'confirm',
},
],
};
}
export function MessageMenuItemFactory(): IMenuSelectorItem {
return {
id: MessageOperation.id,
title: 'Message',
tooltip: 'Message',
type: MenuItemType.SELECTOR,
selections: [
{
label: 'Open message',
value: '',
},
{
label: 'Open loading message',
value: 'loading',
},
],
};
}
export function SidebarMenuItemFactory(accessor: IAccessor): IMenuSelectorItem {
return {
id: SidebarOperation.id,
title: 'Sidebar',
tooltip: 'Sidebar',
type: MenuItemType.SELECTOR,
selections: [
{
label: 'Open sidebar',
value: 'open',
},
{
label: 'Close sidebar',
value: 'close',
},
],
};
}
export function SetEditableMenuItemFactory(accessor: IAccessor): IMenuSelectorItem {
return {
id: SetEditable.id,
title: 'Editable',
tooltip: 'Editable',
type: MenuItemType.SELECTOR,
selections: [
{
label: 'changeUniverEditable',
value: 'univer',
},
{
label: 'changeSheetEditable',
value: 'sheet',
},
],
};
}
export function SaveSnapshotSetEditableMenuItemFactory(accessor: IAccessor): IMenuSelectorItem {
return {
id: SaveSnapshotOptions.id,
type: MenuItemType.SELECTOR,
title: 'Snapshot',
selections: [
{
label: 'saveWorkbook',
value: 'workbook',
},
{
label: 'saveSheet',
value: 'sheet',
},
{
label: 'record',
value: 'record',
},
],
};
}
export const UNIT_ITEM_MENU_ID = 'debugger.unit-menu-item';
export function UnitMenuItemFactory(): IMenuSelectorItem {
return {
id: UNIT_ITEM_MENU_ID,
title: 'Dispose',
tooltip: 'Lifecycle Related Commands',
type: MenuItemType.SUBITEMS,
};
}
export function DisposeUniverItemFactory(): IMenuButtonItem {
return {
id: DisposeUniverCommand.id,
title: 'Dispose Univer',
tooltip: 'Dispose the Univer instance',
icon: 'DS',
type: MenuItemType.BUTTON,
};
}
export function DisposeCurrentUnitMenuItemFactory(): IMenuButtonItem {
return {
id: DisposeCurrentUnitCommand.id,
title: 'Dispose Current Unit',
tooltip: 'Dispose Current Unit',
icon: 'DS',
type: MenuItemType.BUTTON,
};
}
export function CreateEmptySheetMenuItemFactory(): IMenuButtonItem {
return {
id: CreateEmptySheetCommand.id,
title: 'Create Another Sheet',
tooltip: 'Create Another Sheet',
icon: 'CR',
type: MenuItemType.BUTTON,
};
}
export function LoadSheetSnapshotMenuItemFactory(): IMenuButtonItem {
return {
id: LoadSheetSnapshotCommand.id,
title: 'Load Snapshot',
tooltip: 'Load Snapshot',
icon: 'CR',
type: MenuItemType.BUTTON,
};
}
export const FLOAT_DOM_ITEM_MENU_ID = 'debugger.float-dom-menu-item';
export function FloatDomMenuItemFactory(): IMenuSelectorItem {
return {
id: FLOAT_DOM_ITEM_MENU_ID,
title: 'FloatDom',
tooltip: 'Float Dom Commands',
type: MenuItemType.SUBITEMS,
};
}
export function CreateFloatDOMMenuItemFactory(): IMenuButtonItem {
return {
id: CreateFloatDomCommand.id,
title: 'Create Float Dom',
tooltip: 'Create Float Dom',
icon: 'DS',
type: MenuItemType.BUTTON,
};
}
export function ShowCellContentMenuItemFactory(): IMenuButtonItem {
return {
id: ShowCellContentOperation.id,
type: MenuItemType.BUTTON,
title: 'Cell',
icon: 'DS',
};
}
export function ChangeUserMenuItemFactory(): IMenuSelectorItem {
return {
id: ChangeUserCommand.id,
type: MenuItemType.SELECTOR,
title: 'Change User',
selections: [
{
label: 'Owner',
value: UnitRole.Owner,
},
{
label: 'Editor',
value: UnitRole.Editor,
},
{
label: 'Reader',
value: UnitRole.Reader,
},
],
};
}
export function WatermarkMenuItemFactory(): IMenuButtonItem {
return {
id: OpenWatermarkPanelOperation.id,
title: 'univer-watermark.title',
tooltip: 'univer-watermark.title',
icon: UNIVER_WATERMARK_MENU,
type: MenuItemType.BUTTON,
};
}
@@ -21,9 +21,7 @@ import { IRenderManagerService } from '@univerjs/engine-render';
import { distinctUntilChanged, filter, take, takeUntil } from 'rxjs';
export class PerformanceMonitorController extends RxDisposable {
private _initListener = false;
private _containerElement!: HTMLDivElement;
private _styleElement!: HTMLStyleElement;
private _containerElement: HTMLDivElement | null;
private _currentUnitSub: Nullable<Subscription>;
@@ -45,9 +43,6 @@ export class PerformanceMonitorController extends RxDisposable {
override dispose(): void {
super.dispose();
document.body.removeChild(this._containerElement);
document.head.removeChild(this._styleElement);
this._disposeCurrentObserver();
}
@@ -66,46 +61,16 @@ export class PerformanceMonitorController extends RxDisposable {
}
private _listenToRenderer(unitId: string) {
this._tryInit();
const renderer = this._renderManagerService.getRenderById(unitId);
if (renderer) {
const { engine } = renderer;
this._currentUnitSub = engine.endFrame$.subscribe(() => {
this._containerElement.textContent = `FPS: ${Math.round(engine.getFps()).toString()}`;
if (!this._containerElement) {
this._containerElement = document.querySelector('[data-u-comp=debugger-fps]');
} else {
this._containerElement.textContent = `FPS: ${Math.round(engine.getFps()).toString()}`;
}
});
}
}
private _tryInit(): void {
if (this._initListener) {
return;
}
this._initListener = true;
const container = (this._containerElement = document.createElement('div'));
this._containerElement = container;
container.classList.add('fps-monitor');
document.body.appendChild(container);
const style = `
.fps-monitor {
position: absolute;
top: 0;
left: 10px;
width: 100px;
height: 32px;
line-height: 32px;
color: rgba(32, 32, 32, .8);
font-size: 14px;
font-family: sans-serif;
z-index: 1000;
pointer-events: none;
}
`;
this._styleElement = document.createElement('style');
document.head.appendChild(this._styleElement).textContent = style;
}
}
@@ -15,12 +15,10 @@
*/
import { Disposable, Inject } from '@univerjs/core';
import { WatermarkSingle } from '@univerjs/icons';
import { ComponentManager, IMenuManagerService } from '@univerjs/ui';
import { WatermarkPanel } from '../views/watermark/WatermarkPanel';
import { WatermarkPanelFooter } from '../views/watermark/WatermarkPanelFooter';
export const UNIVER_WATERMARK_MENU = 'UNIVER_WATERMARK_MENU';
export const WATERMARK_PANEL = 'WATERMARK_PANEL';
export const WATERMARK_PANEL_FOOTER = 'WATERMARK_PANEL_FOOTER';
@@ -36,7 +34,6 @@ export class UniverWatermarkMenuController extends Disposable {
private _initComponents() {
([
[UNIVER_WATERMARK_MENU, WatermarkSingle],
[WATERMARK_PANEL, WatermarkPanel],
[WATERMARK_PANEL_FOOTER, WatermarkPanelFooter],
] as const).forEach(([key, component]) => {
@@ -18,7 +18,6 @@ import type { Dependency } from '@univerjs/core';
import type { IUniverDebuggerConfig } from './controllers/config.schema';
import { IConfigService, Inject, Injector, merge, Plugin, registerDependencies, touchDependencies } from '@univerjs/core';
import { DEBUGGER_PLUGIN_CONFIG_KEY, defaultPluginConfig } from './controllers/config.schema';
import { DarkModeController } from './controllers/dark-mode.controller';
import { DebuggerController } from './controllers/debugger.controller';
import { E2EController } from './controllers/e2e/e2e.controller';
import { MultiUnitsController } from './controllers/multi-units/multi-units.controller';
@@ -51,7 +50,6 @@ export class UniverDebuggerPlugin extends Plugin {
override onStarting(): void {
const dependencies: Dependency[] = [
[DarkModeController],
[DebuggerController],
[MultiUnitsController],
[E2EController],
@@ -66,7 +64,6 @@ export class UniverDebuggerPlugin extends Plugin {
touchDependencies(this._injector, [
[E2EController],
[DarkModeController],
]);
}
@@ -0,0 +1,92 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { IDropdownMenuProps } from '@univerjs/design';
import type { IUniverDebuggerConfig } from '../controllers/config.schema';
import { IConfigService, IUniverInstanceService, UniverInstanceType } from '@univerjs/core';
import { borderClassName, clsx, DropdownMenu } from '@univerjs/design';
import { useDependency } from '@univerjs/ui';
import { DEBUGGER_PLUGIN_CONFIG_KEY } from '../controllers/config.schema';
import { useCellContent } from './use-cell-content';
import { useDarkMode } from './use-dark-mode';
import { useDialog } from './use-dialog';
import { useDispose } from './use-dispose';
import { useEditable } from './use-editable';
import { useFloatingDom } from './use-floating-dom';
import { useLocale } from './use-locale';
import { useMessage } from './use-message';
import { useNotification } from './use-notification';
import { useSidebar } from './use-sidebar';
import { useSnapshot } from './use-snapshot';
import { useTheme } from './use-theme';
import { useUser } from './use-user';
import { useWatermark } from './use-watermark';
export function Fab() {
const configService = useDependency(IConfigService);
const univerInstanceService = useDependency(IUniverInstanceService);
const unitType = univerInstanceService.getFocusedUnit()?.type;
const items: IDropdownMenuProps['items'] = [
useLocale(),
useDarkMode(),
useTheme(),
useWatermark(),
{ type: 'separator' },
useNotification(),
useMessage(),
useDialog(),
useSidebar(),
{ type: 'separator' },
unitType === UniverInstanceType.UNIVER_SHEET && useFloatingDom(),
unitType === UniverInstanceType.UNIVER_SHEET && useCellContent(),
useSnapshot(),
useEditable(),
useUser(),
useDispose(),
].filter((item) => item !== null) as IDropdownMenuProps['items'];
const configs = configService.getConfig<IUniverDebuggerConfig>(DEBUGGER_PLUGIN_CONFIG_KEY);
const performanceMonitor = configs?.performanceMonitor;
return (
<div
className={`
univer-fixed univer-bottom-12 univer-right-8 univer-z-[9999] univer-flex univer-select-none
univer-flex-col univer-items-center univer-gap-1
`}
>
<DropdownMenu align="end" items={items}>
<button
className={clsx(`
univer-flex univer-size-9 univer-cursor-pointer univer-items-center univer-justify-center
univer-rounded-full univer-bg-white univer-text-base univer-text-gray-900 univer-shadow
univer-outline-none univer-transition-shadow
dark:!univer-bg-gray-900 dark:!univer-text-gray-200
hover:univer-ring-1 hover:univer-ring-primary-400
`, borderClassName)}
type="button"
>
🏗️
</button>
</DropdownMenu>
{performanceMonitor?.enabled && (
<span data-u-comp="debugger-fps" className="univer-text-xs univer-text-gray-400" />
)}
</div>
);
}
@@ -14,17 +14,17 @@
* limitations under the License.
*/
import { CommandType, type ICommand, ILogService, IUniverInstanceService, ObjectMatrix, Range } from '@univerjs/core';
import { ILogService, IUniverInstanceService, ObjectMatrix, Range } from '@univerjs/core';
import { getSheetCommandTarget, SheetsSelectionsService } from '@univerjs/sheets';
import { useDependency } from '@univerjs/ui';
export const ShowCellContentOperation: ICommand = {
id: 'debugger.operation.show-cell-content',
type: CommandType.OPERATION,
handler(accessor) {
const logService = accessor.get(ILogService);
const selectionManagerService = accessor.get(SheetsSelectionsService);
export function useCellContent() {
const logService = useDependency(ILogService);
const selectionManagerService = useDependency(SheetsSelectionsService);
const univerInstanceService = useDependency(IUniverInstanceService);
const onSelect = () => {
const selections = selectionManagerService.getCurrentSelections();
const univerInstanceService = accessor.get(IUniverInstanceService);
const target = getSheetCommandTarget(univerInstanceService);
const matrix = new ObjectMatrix();
selections.forEach((selection) => {
@@ -33,6 +33,11 @@ export const ShowCellContentOperation: ICommand = {
});
});
logService.debug('cell-content', matrix);
return true;
},
};
};
return {
type: 'item' as const,
children: '🗒️ Console cell content',
onSelect,
};
}
@@ -14,31 +14,34 @@
* limitations under the License.
*/
import { ILocalStorageService, Inject, RxDisposable, ThemeService } from '@univerjs/core';
import { ThemeService } from '@univerjs/core';
import { useDependency } from '@univerjs/ui';
import { useEffect } from 'react';
export class DarkModeController extends RxDisposable {
constructor(
@ILocalStorageService private _localStorageService: ILocalStorageService,
@Inject(ThemeService) themeService: ThemeService
) {
super();
export function useDarkMode() {
const themeService = useDependency(ThemeService);
useEffect(() => {
const darkMode = localStorage.getItem('local.darkMode');
if (darkMode === 'dark') {
document.documentElement.classList.add('univer-dark');
themeService.setDarkMode(true);
} else if (darkMode === 'system') {
const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
darkModeMediaQuery.addEventListener('change', (e) => {
if (e.matches) {
document.documentElement.classList.add('univer-dark');
themeService.setDarkMode(true);
} else {
document.documentElement.classList.remove('univer-dark');
themeService.setDarkMode(false);
}
});
themeService.setDarkMode(false);
}
}
}, []);
const onSelect = () => {
const darkMode = themeService.darkMode ? 'light' : 'dark';
themeService.setDarkMode(darkMode === 'dark');
localStorage.setItem('local.darkMode', darkMode);
};
return {
type: 'item' as const,
children: '🌓 Toggle dark mode',
onSelect,
};
}
@@ -14,21 +14,28 @@
* limitations under the License.
*/
import type { IAccessor, ICommand } from '@univerjs/core';
import { CommandType } from '@univerjs/core';
import { IDialogService } from '@univerjs/ui';
import { IConfirmService, IDialogService, useDependency } from '@univerjs/ui';
export interface IUIComponentCommandParams {
value: string;
}
const menu = [
{
label: 'Open dialog',
value: 'dialog',
},
{
label: 'Draggable dialog',
value: 'draggable',
},
{
label: 'Open confirm',
value: 'confirm',
},
];
export const DialogOperation: ICommand = {
id: 'debugger.operation.dialog',
type: CommandType.COMMAND,
handler: async (accessor: IAccessor, params: IUIComponentCommandParams) => {
const dialogService = accessor.get(IDialogService);
const { value } = params;
export function useDialog() {
const dialogService = useDependency(IDialogService);
const confirmService = useDependency(IConfirmService);
const onSelect = (value: string) => {
if (value === 'draggable') {
dialogService.open({
id: 'draggable',
@@ -47,7 +54,7 @@ export const DialogOperation: ICommand = {
}
},
});
} else {
} else if (value === 'dialog') {
dialogService.open({
id: 'dialog1',
children: { title: 'Dialog Content' },
@@ -63,8 +70,36 @@ export const DialogOperation: ICommand = {
}
},
});
}
} else if (value === 'confirm') {
confirmService.open({
id: 'confirm1',
children: { title: 'Confirm Content' },
title: { title: 'Confirm Title' },
confirmText: 'hello',
cancelText: 'world',
onClose() {
confirmService.close('confirm1');
},
});
return true;
},
};
confirmService.open({
id: 'confirm2',
children: { title: 'Confirm2 Content' },
title: { title: 'Confirm2 Title' },
onClose() {
confirmService.close('confirm2');
},
});
}
};
return {
type: 'subItem' as const,
children: '💬 Dialog & Confirm',
options: menu.map((item) => ({
type: 'item' as const,
children: item.label,
onSelect: () => onSelect(item.value),
})),
};
}
@@ -0,0 +1,62 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { IUniverInstanceService, UniverInstanceType } from '@univerjs/core';
import { useDependency } from '@univerjs/ui';
const menu = [
{
label: 'Dispose Univer',
value: 'univer',
},
{
label: 'Dispose current unit',
value: 'unit',
},
{
label: 'Create another sheet',
value: 'create',
},
];
export function useDispose() {
const univerInstanceService = useDependency(IUniverInstanceService);
const onSelect = (value: string) => {
if (value === 'univer') {
window.univer?.dispose();
window.univer = undefined;
window.univerAPI = undefined;
} else if (value === 'unit') {
const focused = univerInstanceService.getFocusedUnit();
if (!focused) return false;
return univerInstanceService.disposeUnit(focused.getUnitId());
} else if (value === 'create') {
univerInstanceService.createUnit(UniverInstanceType.UNIVER_SHEET, {});
}
};
return {
type: 'subItem' as const,
children: '🗑️ Dispose',
options: menu.map((item) => ({
type: 'item' as const,
children: item.label,
onSelect: () => onSelect(item.value),
})),
};
}
@@ -14,29 +14,35 @@
* limitations under the License.
*/
import { CommandType, IPermissionService, IUniverInstanceService } from '@univerjs/core';
import { IPermissionService, IUniverInstanceService } from '@univerjs/core';
import { getSheetCommandTarget, WorkbookEditablePermission, WorksheetEditPermission } from '@univerjs/sheets';
import type { IAccessor, ICommand } from '@univerjs/core';
import { useDependency } from '@univerjs/ui';
export interface ISetEditableCommandParams {
value: 'sheet' | 'univer';
}
const menu = [
{
label: 'Change workbook editable',
value: 'univer',
},
{
label: 'Change worksheet editable',
value: 'sheet',
},
];
export const SetEditable: ICommand = {
id: 'debugger.operation.set.editable',
type: CommandType.OPERATION,
handler: (accessor: IAccessor, params: ISetEditableCommandParams) => {
const univerInstanceService = accessor.get(IUniverInstanceService);
export function useEditable() {
const univerInstanceService = useDependency(IUniverInstanceService);
const permissionService = useDependency(IPermissionService);
const onSelect = (value: string) => {
const target = getSheetCommandTarget(univerInstanceService);
if (!target) {
return false;
}
const { workbook, worksheet, unitId, subUnitId } = target;
const permissionService = accessor.get(IPermissionService);
if (!workbook || !worksheet) {
return false;
}
if (params.value === 'sheet') {
if (value === 'sheet') {
const editable = permissionService.getPermissionPoint(new WorksheetEditPermission(unitId, subUnitId).id);
permissionService.updatePermissionPoint(new WorksheetEditPermission(unitId, subUnitId).id, !editable);
} else {
@@ -44,6 +50,15 @@ export const SetEditable: ICommand = {
const editable = permissionService.getPermissionPoint(new WorkbookEditablePermission(unitId).id);
permissionService.updatePermissionPoint(new WorkbookEditablePermission(unitId).id, !editable);
}
return true;
},
};
};
return {
type: 'subItem' as const,
children: '✍️ Editable',
options: menu.map((item) => ({
type: 'item' as const,
children: item.label,
onSelect: () => onSelect(item.value),
})),
};
}
@@ -14,19 +14,19 @@
* limitations under the License.
*/
import type { ICommand } from '@univerjs/core';
import { CommandType, IUniverInstanceService, UniverInstanceType } from '@univerjs/core';
import { IUniverInstanceService, UniverInstanceType } from '@univerjs/core';
import { DocFloatDomController } from '@univerjs/docs-drawing-ui';
import { SheetCanvasFloatDomManagerService } from '@univerjs/sheets-drawing-ui';
import { useDependency } from '@univerjs/ui';
export const CreateFloatDomCommand: ICommand = {
id: 'debugger.command.create-float-dom',
type: CommandType.COMMAND,
handler: (accessor) => {
const univerInstanceService = accessor.get(IUniverInstanceService);
export function useFloatingDom() {
const univerInstanceService = useDependency(IUniverInstanceService);
const floatDomService = useDependency(SheetCanvasFloatDomManagerService);
const floatDomController = useDependency(DocFloatDomController);
const onSelect = () => {
const currentSheet = univerInstanceService.getCurrentUnitOfType(UniverInstanceType.UNIVER_SHEET);
if (currentSheet) {
const floatDomService = accessor.get(SheetCanvasFloatDomManagerService);
floatDomService.addFloatDomToPosition({
allowTransform: true,
initPosition: {
@@ -41,7 +41,6 @@ export const CreateFloatDomCommand: ICommand = {
},
});
} else {
const floatDomController = accessor.get(DocFloatDomController);
floatDomController.insertFloatDom({
allowTransform: true,
componentKey: 'ImageDemo',
@@ -52,7 +51,11 @@ export const CreateFloatDomCommand: ICommand = {
height: 300,
});
}
};
return true;
},
};
return {
type: 'item' as const,
children: '☁️ Create floating DOM',
onSelect,
};
}
@@ -0,0 +1,76 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { LocaleService, LocaleType } from '@univerjs/core';
import { useDependency } from '@univerjs/ui';
import { useEffect } from 'react';
const locales = [
{
label: 'English',
value: LocaleType.EN_US,
},
{
label: 'Français',
value: LocaleType.FR_FR,
},
{
label: '简体中文',
value: LocaleType.ZH_CN,
},
{
label: 'Русский',
value: LocaleType.RU_RU,
},
{
label: '繁體中文',
value: LocaleType.ZH_TW,
},
{
label: 'Tiếng Việt',
value: LocaleType.VI_VN,
},
];
export function useLocale() {
const localeService = useDependency(LocaleService);
useEffect(() => {
const locale = localStorage.getItem('local.locale');
if (locale) {
localeService.setLocale(locale as LocaleType);
}
}, []);
const onSelect = (value: string) => {
localeService.setLocale(value as LocaleType);
localStorage.setItem('local.locale', value);
};
return {
type: 'subItem' as const,
children: '🌐 Languages',
options: locales.map((lang) => ({
type: 'radio' as const,
value: localeService.getCurrentLocale(),
options: [{
label: lang.label,
value: lang.value,
}],
onSelect,
})),
};
}
@@ -14,33 +14,46 @@
* limitations under the License.
*/
import type { IAccessor, ICommand } from '@univerjs/core';
import { CommandType } from '@univerjs/core';
import { MessageType } from '@univerjs/design';
import { IMessageService } from '@univerjs/ui';
import { IMessageService, useDependency } from '@univerjs/ui';
export const MessageOperation: ICommand = {
id: 'debugger.operation.message',
type: CommandType.OPERATION,
handler: async (accessor: IAccessor, params: { value: string }) => {
const messageService = accessor.get(IMessageService);
const menu = [
{
label: 'Open message',
value: '',
},
{
label: 'Open loading message',
value: 'loading',
},
];
if (params.value === 'loading') {
export function useMessage() {
const messageService = useDependency(IMessageService);
const onSelect = (value: string) => {
if (value === 'loading') {
messageService.show({
type: MessageType.Loading,
content: 'Loading message',
duration: 3000,
});
return true;
} else {
messageService.show({
type: MessageType.Success,
content: 'Demo message',
duration: 1500,
});
}
};
messageService.show({
type: MessageType.Success,
content: 'Demo message',
duration: 1500,
});
return true;
},
};
return {
type: 'subItem' as const,
children: '✉️ Message',
options: menu.map((item) => ({
type: 'item' as const,
children: item.label,
onSelect: () => onSelect(item.value),
})),
};
}
@@ -0,0 +1,59 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { INotificationOptions } from '@univerjs/ui';
import { INotificationService, useDependency } from '@univerjs/ui';
const menu = [
{
label: 'Notification success',
value: 'success',
},
{
label: 'Notification info',
value: 'info',
},
{
label: 'Notification warning',
value: 'warning',
},
{
label: 'Notification error',
value: 'error',
},
];
export function useNotification() {
const notificationService = useDependency(INotificationService);
const onSelect = (value: string) => {
notificationService.show({
type: value as INotificationOptions['type'],
content: 'Lorem Ipusm dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
title: 'Notification Title',
});
};
return {
type: 'subItem' as const,
children: '🔔 Notification',
options: menu.map((item) => ({
type: 'item' as const,
children: item.label,
onSelect: () => onSelect(item.value),
})),
};
}
@@ -0,0 +1,57 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ISidebarService, useDependency } from '@univerjs/ui';
import { TEST_EDITOR_CONTAINER_COMPONENT } from './test-editor/component-name';
const menu = [
{
label: 'Open sidebar',
value: 'open',
},
{
label: 'Close sidebar',
value: 'close',
},
];
export function useSidebar() {
const sidebarService = useDependency(ISidebarService);
const onSelect = (value: string) => {
if (value === 'open') {
sidebarService.open({
header: { title: 'Sidebar title' },
children: { label: TEST_EDITOR_CONTAINER_COMPONENT },
footer: { title: 'Sidebar Footer' },
onClose: () => {
},
});
} else if (value === 'close') {
sidebarService.close();
}
};
return {
type: 'subItem' as const,
children: '🧩 Sidebar',
options: menu.map((item) => ({
type: 'item' as const,
children: item.label,
onSelect: () => onSelect(item.value),
})),
};
}
@@ -0,0 +1,144 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable node/prefer-global/process */
import type { DocumentDataModel, IStyleData, IWorkbookData, Workbook } from '@univerjs/core';
import { IResourceLoaderService, IUniverInstanceService, ObjectMatrix, UniverInstanceType } from '@univerjs/core';
import { ILocalFileService, useDependency } from '@univerjs/ui';
import { RecordController } from '../controllers/local-save/record.controller';
const menu = [
{
label: 'Save workbook',
value: 'workbook',
},
{
label: 'Save worksheet',
value: 'sheet',
},
{
label: 'Record',
value: 'record',
},
{
label: 'Load snapshot',
value: 'load',
},
];
export interface ISaveSnapshotParams {
value: 'sheet' | 'workbook' | 'record';
}
const filterStyle = (workbookData: IWorkbookData) => {
const sheets = workbookData.sheets;
const cacheStyle: Record<string, IStyleData> = {};
Object.keys(sheets).forEach((sheetId) => {
const sheet = sheets[sheetId];
new ObjectMatrix(sheet.cellData).forValue((_r, _c, value) => {
const s = value?.s;
if (s && typeof s === 'string') {
const style = workbookData.styles[s];
if (style) {
cacheStyle[s] = style;
}
}
});
});
workbookData.styles = cacheStyle;
return workbookData;
};
export function useSnapshot() {
const univerInstanceService = useDependency(IUniverInstanceService);
const resourceLoaderService = useDependency(IResourceLoaderService);
const localFileService = useDependency(ILocalFileService);
const recordController = useDependency(RecordController);
const onSelect = async (value: string) => {
const preName = new Date().toLocaleString();
const workbook = univerInstanceService.getCurrentUnitOfType<Workbook>(UniverInstanceType.UNIVER_SHEET)!;
if (!workbook) {
const doc = univerInstanceService.getCurrentUnitOfType<DocumentDataModel>(UniverInstanceType.UNIVER_DOC)!;
const snapshot = resourceLoaderService.saveUnit(doc.getUnitId());
if (process.env.NODE_ENV === 'production') {
const gitHash = process.env.GIT_COMMIT_HASH;
const gitBranch = process.env.GIT_REF_NAME;
const buildTime = process.env.BUILD_TIME;
(snapshot as any).__env__ = { gitHash, gitBranch, buildTime };
}
const text = JSON.stringify(snapshot, null, 2);
localFileService.downloadFile(new Blob([text]), `${preName} snapshot.json`);
return true;
}
const worksheet = workbook.getActiveSheet();
if (!worksheet) {
return false;
}
const snapshot = resourceLoaderService.saveUnit<ReturnType<typeof workbook.getSnapshot>>(workbook.getUnitId())!;
if (process.env.NODE_ENV === 'production') {
const gitHash = process.env.GIT_COMMIT_HASH;
const gitBranch = process.env.GIT_REF_NAME;
const buildTime = process.env.BUILD_TIME;
(snapshot as any).__env__ = { gitHash, gitBranch, buildTime };
}
if (value === 'sheet') {
const sheetId = worksheet.getSheetId();
const sheet = snapshot.sheets[sheetId];
snapshot.sheets = { [sheetId]: sheet };
snapshot.sheetOrder = [sheetId];
const text = JSON.stringify(filterStyle(snapshot), null, 2);
localFileService.downloadFile(new Blob([text]), `${preName} snapshot.json`);
} else if (value === 'workbook') {
const text = JSON.stringify(filterStyle(snapshot), null, 2);
localFileService.downloadFile(new Blob([text]), `${preName} snapshot.json`);
} else if (value === 'record') {
let endCommands = () => [];
recordController.record().subscribe((v) => {
if (v.type === 'start') {
endCommands = recordController.startSaveCommands() as any;
}
if (v.type === 'finish') {
const commands = endCommands();
localFileService.downloadFile(v.data, `${preName} video.webm`);
localFileService.downloadFile(new Blob([JSON.stringify(commands, null, 2)]), `${preName} commands.json`);
}
});
} else if (value === 'load') {
const snapshotFile = await localFileService.openFile({ multiple: false, accept: '.json' });
if (snapshotFile.length !== 1) return false;
const text = await snapshotFile[0].text();
univerInstanceService.createUnit<IWorkbookData, Workbook>(UniverInstanceType.UNIVER_SHEET, JSON.parse(text));
return true;
}
};
return {
type: 'subItem' as const,
children: '📷 Snapshot',
options: menu.map((item) => ({
type: 'item' as const,
children: item.label,
onSelect: () => onSelect(item.value),
})),
};
}
@@ -0,0 +1,65 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ThemeService } from '@univerjs/core';
import { defaultTheme, greenTheme } from '@univerjs/themes';
import { useDependency } from '@univerjs/ui';
import { useEffect } from 'react';
const themes = [
{
label: '🟢',
value: greenTheme,
},
{
label: '🔵',
value: defaultTheme,
},
];
export function useTheme() {
const themeService = useDependency(ThemeService);
useEffect(() => {
const themeKey = localStorage.getItem('local.theme');
const theme = themes.find((theme) => theme.label === themeKey);
if (theme) {
themeService.setTheme(theme.value);
}
}, []);
const onSelect = (value: string) => {
localStorage.setItem('local.theme', value);
const theme = themes.find((theme) => theme.label === value);
if (theme) {
themeService.setTheme(theme.value);
}
};
return {
type: 'subItem' as const,
children: '🎨 Themes',
options: themes.map((theme) => ({
type: 'item' as const,
children: theme.label,
onSelect: () => onSelect(theme.label),
})),
};
}
@@ -0,0 +1,70 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Workbook } from '@univerjs/core';
import { createDefaultUser, IPermissionService, IUniverInstanceService, UniverInstanceType, UserManagerService } from '@univerjs/core';
import { WorkbookManageCollaboratorPermission } from '@univerjs/sheets';
import { useDependency } from '@univerjs/ui';
enum UnitRole {
Reader = 0,
Editor = 1,
Owner = 2,
UNRECOGNIZED = -1,
}
const menu = [
{
label: 'Owner',
value: UnitRole.Owner,
},
{
label: 'Editor',
value: UnitRole.Editor,
},
{
label: 'Reader',
value: UnitRole.Reader,
},
];
export function useUser() {
const univerInstanceService = useDependency(IUniverInstanceService);
const userManagerService = useDependency(UserManagerService);
const permissionService = useDependency(IPermissionService);
const onSelect = (value: UnitRole) => {
userManagerService.setCurrentUser(createDefaultUser(value));
const workbook = univerInstanceService.getCurrentUnitOfType<Workbook>(UniverInstanceType.UNIVER_SHEET)!;
const unitId = workbook.getUnitId();
if (value === UnitRole.Owner) {
permissionService.updatePermissionPoint(new WorkbookManageCollaboratorPermission(unitId).id, true);
} else {
permissionService.updatePermissionPoint(new WorkbookManageCollaboratorPermission(unitId).id, false);
}
};
return {
type: 'subItem' as const,
children: '👥 Change user\'s role',
options: menu.map((item) => ({
type: 'item' as const,
children: item.label,
onSelect: () => onSelect(item.value),
})),
};
}
@@ -14,18 +14,15 @@
* limitations under the License.
*/
import type { ICommand } from '@univerjs/core';
import { CommandType, LocaleService } from '@univerjs/core';
import { ISidebarService } from '@univerjs/ui';
import { WATERMARK_PANEL, WATERMARK_PANEL_FOOTER } from '../../controllers/watermark.menu.controller';
import { LocaleService } from '@univerjs/core';
import { ISidebarService, useDependency } from '@univerjs/ui';
import { WATERMARK_PANEL, WATERMARK_PANEL_FOOTER } from '../controllers/watermark.menu.controller';
export const OpenWatermarkPanelOperation: ICommand = {
type: CommandType.OPERATION,
id: 'univer.operation.open-watermark-panel',
handler(accessor) {
const sidebarService = accessor.get(ISidebarService);
const localeService = accessor.get(LocaleService);
export function useWatermark() {
const sidebarService = useDependency(ISidebarService);
const localeService = useDependency(LocaleService);
const onSelect = () => {
sidebarService.open({
header: { title: localeService.t('univer-watermark.title') },
children: { label: WATERMARK_PANEL },
@@ -33,7 +30,11 @@ export const OpenWatermarkPanelOperation: ICommand = {
onClose: () => { },
width: 330,
});
};
return true;
},
};
return {
type: 'item' as const,
children: '🌊 Watermark Settings',
onSelect,
};
}
@@ -102,21 +102,32 @@ export function WatermarkTextSetting(props: IWatermarkTextSettingProps) {
</div>
)}
>
<a className="hover:univer-bg-gray-100">
<a
className={`
dark:hover:!univer-bg-gray-700
hover:univer-bg-gray-100
`}
>
<FontColor extend={{ colorChannel1: config.color ?? '#2c53f1' }} />
</a>
</Dropdown>
<a
className={clsx('hover:univer-bg-gray-100', {
'univer-bg-gray-200': config.bold,
className={clsx(`
dark:hover:!univer-bg-gray-700
hover:univer-bg-gray-100
`, {
'univer-bg-gray-200 dark:!univer-bg-gray-600': config.bold,
})}
onClick={() => { onChange({ ...config, bold: !config.bold }); }}
>
<BoldSingle />
</a>
<a
className={clsx('hover:univer-bg-gray-100', {
'univer-bg-gray-200': config.italic,
className={clsx(`
dark:hover:!univer-bg-gray-700
hover:univer-bg-gray-100
`, {
'univer-bg-gray-200 dark:!univer-bg-gray-600': config.italic,
})}
onClick={() => { onChange({ ...config, italic: !config.italic }); }}
>
@@ -102,6 +102,7 @@ export function UniSheetBar() {
className={clsx(`
univer-flex univer-h-8 univer-cursor-pointer univer-items-center univer-rounded-lg
univer-px-1 univer-py-0
dark:hover:!univer-bg-gray-700
hover:univer-bg-gray-100
`, activeKey === item.sheetId ? 'univer-text-primary-500' : '')}
onClick={() => handleSheetActiveChange(item.sheetId)}
@@ -125,6 +126,7 @@ export function UniSheetBar() {
univer-rounded-lg univer-border-0 univer-bg-transparent univer-text-primary-600
before:absolute before:left-0 before:top-0 before:h-full before:w-full before:bg-gray-100
before:content-['']
dark:hover:!univer-bg-gray-700
hover:univer-cursor-pointer hover:univer-bg-gray-100
`}
@@ -132,6 +132,7 @@ export function UniSlideSideBar() {
univer-justify-center univer-rounded-lg univer-border-0 univer-bg-transparent univer-px-1
univer-text-primary-500
before:absolute before:left-0 before:top-0 before:h-px before:content-[''] before:univer-bg-gray-200
dark:hover:!univer-bg-gray-700
hover:univer-bg-gray-100
`}
onClick={handleAppendSlide}
@@ -155,6 +155,7 @@ export const UniControls = ({ zoom, onItemClick }: { zoom: number; onItemClick?:
`,
`
univer-rounded univer-no-underline univer-transition-colors univer-duration-200
dark:hover:!univer-bg-gray-700
hover:univer-bg-gray-100
`,
item === zoomPercent ? 'univer-bg-gray-100' : ''
@@ -20,10 +20,14 @@ import {
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuPortal,
DropdownMenuPrimitive,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuSeparator,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuTrigger,
} from './DropdownMenuPrimitive';
@@ -35,6 +39,15 @@ interface IDropdownMenuNormalItem {
onSelect?: (item: DropdownMenuType) => void;
}
interface IDropdownMenuNormalSubItem {
type: 'subItem';
className?: string;
children: ReactNode;
options?: DropdownMenuType[];
disabled?: boolean;
onSelect?: (item: DropdownMenuType) => void;
}
interface IDropdownMenuSeparatorItem {
type: 'separator';
className?: string;
@@ -65,7 +78,7 @@ interface IDropdownMenuCheckItem {
onSelect?: (item: string) => void;
}
type DropdownMenuType = IDropdownMenuNormalItem | IDropdownMenuSeparatorItem | IDropdownMenuRadioItem | IDropdownMenuCheckItem;
type DropdownMenuType = IDropdownMenuNormalItem | IDropdownMenuNormalSubItem | IDropdownMenuSeparatorItem | IDropdownMenuRadioItem | IDropdownMenuCheckItem;
export interface IDropdownMenuProps extends ComponentProps<typeof DropdownMenuContent> {
children: ReactNode;
@@ -164,6 +177,19 @@ export function DropdownMenu(props: IDropdownMenuProps) {
{item.children}
</DropdownMenuItem>
);
} else if (type === 'subItem') {
return (
<DropdownMenuSub key={index}>
<DropdownMenuSubTrigger>{item.children}</DropdownMenuSubTrigger>
<DropdownMenuPortal>
<DropdownMenuSubContent sideOffset={12}>
{item.options?.map((subItem, subIndex) => (
renderMenuItem(subItem, subIndex)
))}
</DropdownMenuSubContent>
</DropdownMenuPortal>
</DropdownMenuSub>
);
}
}
@@ -103,7 +103,7 @@ function DropdownMenuSubTrigger({
univer-flex univer-cursor-default univer-select-none univer-items-center univer-justify-between
univer-gap-2 univer-rounded univer-px-2 univer-py-1.5 univer-text-sm univer-outline-none
[&_svg]:univer-pointer-events-none [&_svg]:univer-size-4 [&_svg]:univer-shrink-0
dark:focus:!univer-bg-gray-600
dark:focus:!univer-bg-gray-600 dark:data-[state=open]:!univer-bg-gray-600
data-[state=open]:univer-bg-gray-100
focus:univer-bg-gray-100
`,
@@ -330,7 +330,10 @@ function DropdownMenuSeparator({
}: ComponentProps<typeof Separator>) {
return (
<Separator
className={clsx('-univer-mx-1 univer-my-1 univer-h-px univer-bg-gray-200', className)}
className={clsx(`
-univer-mx-1 univer-my-1 univer-h-px univer-bg-gray-200
dark:!univer-bg-gray-600
`, className)}
{...props}
/>
);
@@ -57,6 +57,7 @@ export const Playground = {
<a
className={clsx(`
univer-cursor-pointer univer-rounded-lg univer-px-4 univer-py-2 univer-transition-all
dark:hover:!univer-bg-gray-700
hover:univer-bg-gray-100
`, borderClassName)}
>
@@ -58,6 +58,7 @@ export const Playground = {
<a
className={clsx(`
univer-cursor-pointer univer-rounded-lg univer-px-4 univer-py-2 univer-transition-all
dark:hover:!univer-bg-gray-700
hover:univer-bg-gray-100
`, borderClassName)}
>
@@ -16,7 +16,7 @@
import type { ComponentProps, ReactNode } from 'react';
import { useState } from 'react';
import { HoverCardContent, HoverCardPrimitive, HoverCardTrigger } from './HoverCardPrimitive';
import { HoverCardContent, HoverCardPortal, HoverCardPrimitive, HoverCardTrigger } from './HoverCardPrimitive';
export interface IHoverCardProps extends ComponentProps<typeof HoverCardContent> {
children: ReactNode;
@@ -60,9 +60,11 @@ export function HoverCard(props: IHoverCardProps) {
return (
<HoverCardPrimitive open={open} onOpenChange={handleChangeOpen} openDelay={openDelay}>
<HoverCardTrigger asChild>{children}</HoverCardTrigger>
<HoverCardContent {...restProps}>
{overlay}
</HoverCardContent>
<HoverCardPortal>
<HoverCardContent {...restProps}>
{overlay}
</HoverCardContent>
</HoverCardPortal>
</HoverCardPrimitive>
);
}
@@ -15,13 +15,15 @@
*/
import type { ComponentPropsWithoutRef, ElementRef } from 'react';
import { Content, Root, Trigger } from '@radix-ui/react-hover-card';
import { Content, Portal, Root, Trigger } from '@radix-ui/react-hover-card';
import { forwardRef } from 'react';
import { borderClassName } from '../../helper/class-utilities';
import { clsx } from '../../helper/clsx';
const HoverCardPrimitive = Root;
const HoverCardPortal = Portal;
const HoverCardTrigger = Trigger;
const HoverCardContent = forwardRef<
@@ -36,7 +38,7 @@ const HoverCardContent = forwardRef<
`
univer-z-[1080] univer-w-64 univer-origin-[--radix-hover-card-content-transform-origin] univer-rounded-md
univer-bg-white univer-text-gray-900 univer-shadow-md univer-outline-none
dark:univer-bg-gray-900 dark:univer-text-white
dark:!univer-bg-gray-900 dark:!univer-text-white
data-[side=bottom]:univer-slide-in-from-top-2
data-[side=left]:univer-slide-in-from-right-2
data-[side=right]:univer-slide-in-from-left-2
@@ -54,4 +56,4 @@ const HoverCardContent = forwardRef<
HoverCardContent.displayName = Content.displayName;
export { HoverCardContent, HoverCardPrimitive, HoverCardTrigger };
export { HoverCardContent, HoverCardPortal, HoverCardPrimitive, HoverCardTrigger };
@@ -79,10 +79,11 @@ export const QuickInsertButton = ({ className = '' }: IQuickInsertButtonProps) =
className={clsx(`
univer-mr-1 univer-flex univer-cursor-pointer univer-items-center univer-gap-2.5 univer-rounded-full
univer-p-1.5 univer-shadow-sm
dark:hover:!univer-bg-gray-700
hover:univer-bg-gray-100
`, borderClassName, {
'univer-bg-gray-100': editPopup,
'univer-bg-white': !editPopup,
'univer-bg-gray-100 dark:!univer-bg-gray-700': editPopup,
'univer-bg-white dark:univer-bg-gray-900': !editPopup,
}, className)}
role="button"
tabIndex={0}
@@ -50,6 +50,7 @@ export const ColorPicker = (props: IColorPickerProps) => {
<span
className={clsx(`
univer-flex univer-cursor-pointer univer-items-center univer-rounded univer-p-1
dark:hover:!univer-bg-gray-700
hover:univer-bg-gray-100
`, className)}
>
@@ -129,6 +129,7 @@ const IconGroupList = forwardRef<HTMLDivElement, IIconGroupListProps>((props, re
<a
className={`
univer-cursor-pointer univer-rounded
dark:hover:!univer-bg-gray-700
hover:univer-bg-gray-100
`}
>
@@ -192,6 +193,7 @@ const IconItemList = (props: { onClick: (iconType: IIconType, iconId: string) =>
className={`
univer-mb-2 univer-mr-2 univer-flex univer-cursor-pointer univer-items-center
univer-justify-center univer-rounded
dark:hover:!univer-bg-gray-700
hover:univer-bg-gray-100
`}
>
@@ -381,9 +381,10 @@ export const RuleList = (props: IRuleListProps) => {
className={clsx(`
univer-group univer-relative univer-flex univer-items-center
univer-justify-between univer-rounded univer-py-2 univer-pl-5 univer-pr-8
dark:hover:!univer-bg-gray-700
hover:univer-bg-gray-100
`, {
'univer-bg-gray-100': draggingId === index,
'univer-bg-gray-100 dark:!univer-bg-gray-700': draggingId === index,
})}
onMouseMove={() => {
rule.ranges !== currentRuleRanges && currentRuleRangesSet(rule.ranges);
@@ -417,6 +417,7 @@ export function ListFormulaInput(props: IFormulaInputProps) {
className={`
univer-flex univer-w-fit univer-cursor-pointer univer-flex-row univer-items-center
univer-rounded univer-p-1 univer-px-2 univer-text-sm univer-text-primary
dark:hover:!univer-bg-gray-800
hover:univer-bg-primary-50
`}
onClick={handleAdd}
@@ -130,6 +130,7 @@ export function HelpFunction(props: IHelpFunctionProps) {
className={clsx(`
univer-m-0 univer-box-border univer-w-[250px] univer-select-none univer-list-none
univer-rounded-lg univer-bg-white univer-leading-5 univer-shadow-md univer-outline-none
dark:univer-bg-gray-900
`, borderClassName)}
>
<div
@@ -256,6 +256,7 @@ export function SortOptionItem(props: ISortOptionItemProps) {
univer-relative univer-box-border univer-flex univer-h-7 univer-cursor-pointer
univer-list-none univer-items-center univer-justify-between univer-rounded
univer-px-2 univer-text-sm univer-transition-all
dark:hover:!univer-bg-gray-700
hover:univer-bg-gray-100
`}
>
@@ -116,8 +116,8 @@ export function Sidebar() {
<section
data-u-comp="sidebar"
className={clsx(`
univer-relative univer-h-full univer-bg-white
dark:!univer-bg-gray-900
univer-relative univer-h-full univer-bg-white univer-text-gray-900
dark:!univer-bg-gray-900 dark:!univer-text-white
`, {
'univer-w-96 univer-translate-x-0': options?.visible,
'univer-w-0 univer-translate-x-full': !options?.visible,
@@ -141,7 +141,13 @@ export function Sidebar() {
>
{options?.header}
<a className="univer-cursor-pointer univer-text-gray-500" onClick={handleClose}>
<a
className={`
univer-cursor-pointer univer-text-gray-500
dark:!univer-text-gray-300
`}
onClick={handleClose}
>
<CloseSingle />
</a>
</header>
-7
View File
@@ -612,9 +612,6 @@ importers:
'@univerjs/docs-drawing-ui':
specifier: workspace:*
version: link:../../packages/docs-drawing-ui
'@univerjs/docs-ui':
specifier: workspace:*
version: link:../../packages/docs-ui
'@univerjs/engine-render':
specifier: workspace:*
version: link:../../packages/engine-render
@@ -664,10 +661,6 @@ importers:
vitest:
specifier: ^3.1.3
version: 3.1.3(@types/debug@4.1.12)(@types/node@22.15.18)(happy-dom@17.4.7)(jiti@2.4.2)(jsdom@26.1.0)(sass@1.87.0)(terser@5.39.0)(tsx@4.19.4)(yaml@2.7.1)
optionalDependencies:
vue:
specifier: '>=3.0.0'
version: 3.5.13(typescript@5.8.3)
packages-experimental/uni-docs-ui:
dependencies: