mirror of
https://github.com/dream-num/univer.git
synced 2026-09-01 15:29:43 +08:00
feat(sheets-note): support attach note to cells (#5125)
This commit is contained in:
@@ -45,7 +45,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/fs-extra": "^11.0.4",
|
||||
"@univerjs/icons": "^0.3.22",
|
||||
"@univerjs/icons": "^0.3.24",
|
||||
"@univerjs/icons-svg": "^0.3.22",
|
||||
"@univerjs/protocol": "0.1.45"
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"@univerjs/docs-drawing-ui": "workspace:*",
|
||||
"@univerjs/docs-hyper-link-ui": "workspace:*",
|
||||
"@univerjs/docs-mention-ui": "workspace:*",
|
||||
|
||||
"@univerjs/docs-quick-insert-ui": "workspace:*",
|
||||
"@univerjs/docs-thread-comment-ui": "workspace:*",
|
||||
"@univerjs/docs-ui": "workspace:*",
|
||||
@@ -55,6 +56,8 @@
|
||||
"@univerjs/sheets-formula-ui": "workspace:*",
|
||||
"@univerjs/sheets-hyper-link": "workspace:*",
|
||||
"@univerjs/sheets-hyper-link-ui": "workspace:*",
|
||||
"@univerjs/sheets-note": "workspace:*",
|
||||
"@univerjs/sheets-note-ui": "workspace:*",
|
||||
"@univerjs/sheets-numfmt": "workspace:*",
|
||||
"@univerjs/sheets-numfmt-ui": "workspace:*",
|
||||
"@univerjs/sheets-sort": "workspace:*",
|
||||
|
||||
@@ -31,6 +31,8 @@ import { UniverSheetsDataValidationPlugin } from '@univerjs/sheets-data-validati
|
||||
import { UniverSheetsFilterPlugin } from '@univerjs/sheets-filter';
|
||||
import { UniverSheetsFormulaPlugin } from '@univerjs/sheets-formula';
|
||||
import { UniverSheetsHyperLinkPlugin } from '@univerjs/sheets-hyper-link';
|
||||
import { SheetsNotePlugin } from '@univerjs/sheets-note';
|
||||
import { SheetsNoteUIPlugin } from '@univerjs/sheets-note-ui';
|
||||
import { UniverSheetsNumfmtPlugin } from '@univerjs/sheets-numfmt';
|
||||
import { UniverSheetsSortPlugin } from '@univerjs/sheets-sort';
|
||||
import { UniverSheetsBindingSourcePlugin } from '@univerjs/sheets-source-binding';
|
||||
@@ -38,13 +40,13 @@ import { UniverSheetTablePlugin } from '@univerjs/sheets-table';
|
||||
import { UniverSheetsThreadCommentPlugin } from '@univerjs/sheets-thread-comment';
|
||||
import { UniverSheetsUIPlugin } from '@univerjs/sheets-ui';
|
||||
import { UniverSheetsZenEditorPlugin } from '@univerjs/sheets-zen-editor';
|
||||
|
||||
import { UniverUIPlugin } from '@univerjs/ui';
|
||||
|
||||
import { enUS, faIR, frFR, ruRU, viVN, zhCN, zhTW } from '../locales';
|
||||
|
||||
import { UniverSheetsCustomMenuPlugin } from './custom-menu';
|
||||
import ImportCSVButtonPlugin from './custom-plugin/import-csv-button';
|
||||
|
||||
import ImportCSVButtonPlugin from './custom-plugin/import-csv-button';
|
||||
import '@univerjs/sheets/facade';
|
||||
import '@univerjs/ui/facade';
|
||||
import '@univerjs/docs-ui/facade';
|
||||
@@ -131,7 +133,8 @@ function createNewInstance() {
|
||||
|
||||
univer.registerPlugin(UniverSheetTablePlugin);
|
||||
univer.registerPlugin(UniverNetworkPlugin);
|
||||
|
||||
univer.registerPlugin(SheetsNotePlugin);
|
||||
univer.registerPlugin(SheetsNoteUIPlugin);
|
||||
univer.registerPlugin(ImportCSVButtonPlugin);
|
||||
|
||||
// If we are running in e2e platform, we should immediately register the debugger plugin.
|
||||
|
||||
@@ -24108,6 +24108,10 @@ export const DEFAULT_WORKBOOK_DATA_DEMO: IWorkbookData = {
|
||||
'dv-test': dv2,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'SHEET_NOTE_PLUGIN',
|
||||
data: '{"sheet-0011":{"8":{"7":{"width":160,"height":60,"note":"overlap"}},"10":{"9":{"width":160,"height":60,"note":"This is a note of sheet","show":true}},"26":{"9":{"width":160,"height":60,"note":"This is note 2"}}}}',
|
||||
},
|
||||
{
|
||||
name: 'SHEET_FILTER_PLUGIN',
|
||||
data: JSON.stringify({
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
"dependencies": {
|
||||
"@univerjs/core": "workspace:*",
|
||||
"@univerjs/design": "workspace:*",
|
||||
"@univerjs/icons": "^0.3.22",
|
||||
"@univerjs/icons": "^0.3.24",
|
||||
"@univerjs/sheets": "workspace:*",
|
||||
"@univerjs/sheets-filter": "workspace:*",
|
||||
"@univerjs/sheets-ui": "workspace:*",
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
"@univerjs/docs-drawing-ui": "workspace:*",
|
||||
"@univerjs/docs-ui": "workspace:*",
|
||||
"@univerjs/engine-render": "workspace:*",
|
||||
"@univerjs/icons": "^0.3.22",
|
||||
"@univerjs/icons": "^0.3.24",
|
||||
"@univerjs/mockdata": "workspace:*",
|
||||
"@univerjs/sheets": "workspace:*",
|
||||
"@univerjs/sheets-drawing-ui": "workspace:*",
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
"@univerjs/docs": "workspace:*",
|
||||
"@univerjs/docs-ui": "workspace:*",
|
||||
"@univerjs/engine-render": "workspace:*",
|
||||
"@univerjs/icons": "^0.3.22",
|
||||
"@univerjs/icons": "^0.3.24",
|
||||
"@univerjs/rpc": "workspace:*",
|
||||
"@univerjs/sheets-formula": "workspace:*",
|
||||
"@univerjs/sheets-formula-ui": "workspace:*",
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
"@univerjs/design": "workspace:*",
|
||||
"@univerjs/docs": "workspace:*",
|
||||
"@univerjs/docs-ui": "workspace:*",
|
||||
"@univerjs/icons": "^0.3.22",
|
||||
"@univerjs/icons": "^0.3.24",
|
||||
"@univerjs/sheets": "workspace:*",
|
||||
"@univerjs/sheets-drawing-ui": "workspace:*",
|
||||
"@univerjs/sheets-ui": "workspace:*",
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
"@univerjs/core": "workspace:*",
|
||||
"@univerjs/design": "workspace:*",
|
||||
"@univerjs/engine-render": "workspace:*",
|
||||
"@univerjs/icons": "^0.3.22",
|
||||
"@univerjs/icons": "^0.3.24",
|
||||
"@univerjs/slides-ui": "workspace:*",
|
||||
"@univerjs/ui": "workspace:*",
|
||||
"@univerjs/uniui": "workspace:*"
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
"@univerjs/core": "workspace:*",
|
||||
"@univerjs/design": "workspace:*",
|
||||
"@univerjs/engine-render": "workspace:*",
|
||||
"@univerjs/icons": "^0.3.22",
|
||||
"@univerjs/icons": "^0.3.24",
|
||||
"@univerjs/sheets": "workspace:*",
|
||||
"@univerjs/sheets-ui": "workspace:*",
|
||||
"@univerjs/ui": "workspace:*",
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
"@radix-ui/react-slot": "^1.2.0",
|
||||
"@radix-ui/react-tooltip": "^1.2.4",
|
||||
"@rc-component/trigger": "^2.2.6",
|
||||
"@univerjs/icons": "^0.3.22",
|
||||
"@univerjs/icons": "^0.3.24",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"dayjs": "^1.11.13",
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
"@univerjs/drawing": "workspace:*",
|
||||
"@univerjs/drawing-ui": "workspace:*",
|
||||
"@univerjs/engine-render": "workspace:*",
|
||||
"@univerjs/icons": "^0.3.22",
|
||||
"@univerjs/icons": "^0.3.24",
|
||||
"@univerjs/ui": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
"@univerjs/docs-hyper-link": "workspace:*",
|
||||
"@univerjs/docs-ui": "workspace:*",
|
||||
"@univerjs/engine-render": "workspace:*",
|
||||
"@univerjs/icons": "^0.3.22",
|
||||
"@univerjs/icons": "^0.3.24",
|
||||
"@univerjs/ui": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
"@univerjs/drawing": "workspace:*",
|
||||
"@univerjs/drawing-ui": "workspace:*",
|
||||
"@univerjs/engine-render": "workspace:*",
|
||||
"@univerjs/icons": "^0.3.22",
|
||||
"@univerjs/icons": "^0.3.24",
|
||||
"@univerjs/ui": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
"@univerjs/docs": "workspace:*",
|
||||
"@univerjs/docs-ui": "workspace:*",
|
||||
"@univerjs/engine-render": "workspace:*",
|
||||
"@univerjs/icons": "^0.3.22",
|
||||
"@univerjs/icons": "^0.3.24",
|
||||
"@univerjs/thread-comment": "workspace:*",
|
||||
"@univerjs/thread-comment-ui": "workspace:*",
|
||||
"@univerjs/ui": "workspace:*"
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
"@univerjs/docs": "workspace:*",
|
||||
"@univerjs/drawing": "workspace:*",
|
||||
"@univerjs/engine-render": "workspace:*",
|
||||
"@univerjs/icons": "^0.3.22",
|
||||
"@univerjs/icons": "^0.3.24",
|
||||
"@univerjs/ui": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
"@univerjs/design": "workspace:*",
|
||||
"@univerjs/drawing": "workspace:*",
|
||||
"@univerjs/engine-render": "workspace:*",
|
||||
"@univerjs/icons": "^0.3.22",
|
||||
"@univerjs/icons": "^0.3.24",
|
||||
"@univerjs/ui": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
"@univerjs/core": "workspace:*",
|
||||
"@univerjs/design": "workspace:*",
|
||||
"@univerjs/engine-render": "workspace:*",
|
||||
"@univerjs/icons": "^0.3.22",
|
||||
"@univerjs/icons": "^0.3.24",
|
||||
"@univerjs/ui": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
"@univerjs/design": "workspace:*",
|
||||
"@univerjs/engine-formula": "workspace:*",
|
||||
"@univerjs/engine-render": "workspace:*",
|
||||
"@univerjs/icons": "^0.3.22",
|
||||
"@univerjs/icons": "^0.3.24",
|
||||
"@univerjs/sheets": "workspace:*",
|
||||
"@univerjs/sheets-conditional-formatting": "workspace:*",
|
||||
"@univerjs/sheets-formula-ui": "workspace:*",
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
"@univerjs/core": "workspace:*",
|
||||
"@univerjs/design": "workspace:*",
|
||||
"@univerjs/engine-render": "workspace:*",
|
||||
"@univerjs/icons": "^0.3.22",
|
||||
"@univerjs/icons": "^0.3.24",
|
||||
"@univerjs/sheets": "workspace:*",
|
||||
"@univerjs/sheets-ui": "workspace:*",
|
||||
"@univerjs/ui": "workspace:*"
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
"@univerjs/design": "workspace:*",
|
||||
"@univerjs/engine-formula": "workspace:*",
|
||||
"@univerjs/engine-render": "workspace:*",
|
||||
"@univerjs/icons": "^0.3.22",
|
||||
"@univerjs/icons": "^0.3.24",
|
||||
"@univerjs/sheets": "workspace:*",
|
||||
"@univerjs/sheets-data-validation": "workspace:*",
|
||||
"@univerjs/sheets-formula-ui": "workspace:*",
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
"@univerjs/drawing": "workspace:*",
|
||||
"@univerjs/drawing-ui": "workspace:*",
|
||||
"@univerjs/engine-render": "workspace:*",
|
||||
"@univerjs/icons": "^0.3.22",
|
||||
"@univerjs/icons": "^0.3.24",
|
||||
"@univerjs/sheets": "workspace:*",
|
||||
"@univerjs/sheets-drawing": "workspace:*",
|
||||
"@univerjs/sheets-ui": "workspace:*",
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
"@univerjs/core": "workspace:*",
|
||||
"@univerjs/design": "workspace:*",
|
||||
"@univerjs/engine-render": "workspace:*",
|
||||
"@univerjs/icons": "^0.3.22",
|
||||
"@univerjs/icons": "^0.3.24",
|
||||
"@univerjs/rpc": "workspace:*",
|
||||
"@univerjs/sheets": "workspace:*",
|
||||
"@univerjs/sheets-filter": "workspace:*",
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
"@univerjs/docs-ui": "workspace:*",
|
||||
"@univerjs/engine-formula": "workspace:*",
|
||||
"@univerjs/engine-render": "workspace:*",
|
||||
"@univerjs/icons": "^0.3.22",
|
||||
"@univerjs/icons": "^0.3.24",
|
||||
"@univerjs/sheets": "workspace:*",
|
||||
"@univerjs/sheets-formula": "workspace:*",
|
||||
"@univerjs/sheets-ui": "workspace:*",
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
"@univerjs/docs-ui": "workspace:*",
|
||||
"@univerjs/engine-formula": "workspace:*",
|
||||
"@univerjs/engine-render": "workspace:*",
|
||||
"@univerjs/icons": "^0.3.22",
|
||||
"@univerjs/icons": "^0.3.24",
|
||||
"@univerjs/sheets": "workspace:*",
|
||||
"@univerjs/sheets-data-validation": "workspace:*",
|
||||
"@univerjs/sheets-formula-ui": "workspace:*",
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
# @univerjs/sheets-note-ui
|
||||
|
||||
| Package | Description | Version | License | Downloads | Continuous Integration | Coverage |
|
||||
|---------|-------------|---------|----------|-----------|----------------------|----------|
|
||||
| `@univerjs/sheets-note-ui` | `UniverSheetsNote` | [![][npm-version-shield]][npm-version-link] | ![][npm-license-shield] | ![][npm-downloads-shield] | ⭕️ | ⭕️ |
|
||||
|
||||
## Introduction
|
||||
|
||||
`@univerjs/sheets-note-ui` provides the note/annotation function of Univer Sheets.
|
||||
|
||||
## Usage
|
||||
|
||||
### Installation
|
||||
|
||||
```shell
|
||||
npm install @univerjs/sheets-note-ui
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```shell
|
||||
pnpm add @univerjs/sheets-note-ui
|
||||
```
|
||||
|
||||
### Example
|
||||
|
||||
```ts
|
||||
import { UniverSheetsNotePlugin, INoteMentionDataService} from '@univerjs/sheets-note-ui';
|
||||
|
||||
// Register the plugin
|
||||
univer.registerPlugin(UniverSheetsNotePlugin);
|
||||
```
|
||||
|
||||
[npm-version-shield]: https://img.shields.io/npm/v/@univerjs/sheets-note-ui?style=flat-square
|
||||
[npm-version-link]: https://npmjs.com/package/@univerjs/sheets-note-ui
|
||||
[npm-license-shield]: https://img.shields.io/npm/l/@univerjs/sheets-note-ui?style=flat-square
|
||||
[npm-downloads-shield]: https://img.shields.io/npm/dm/@univerjs/sheets-note-ui?style=flat-square
|
||||
@@ -0,0 +1,83 @@
|
||||
{
|
||||
"name": "@univerjs/sheets-note-ui",
|
||||
"version": "0.6.10",
|
||||
"private": false,
|
||||
"description": "Univer sheets note UI plugin",
|
||||
"author": "DreamNum <developer@univer.ai>",
|
||||
"license": "Apache-2.0",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/univer"
|
||||
},
|
||||
"homepage": "https://univer.ai",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/dream-num/univer"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/dream-num/univer/issues"
|
||||
},
|
||||
"keywords": [],
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./*": "./src/*",
|
||||
"./locale/*": "./src/locale/*.ts",
|
||||
"./facade": "./src/facade/index.ts"
|
||||
},
|
||||
"main": "./lib/index.js",
|
||||
"module": "./lib/index.js",
|
||||
"types": "./lib/index.d.ts",
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"main": "./lib/es/index.js",
|
||||
"module": "./lib/es/index.js",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./lib/es/index.js",
|
||||
"require": "./lib/cjs/index.js",
|
||||
"types": "./lib/types/index.d.ts"
|
||||
},
|
||||
"./*": {
|
||||
"import": "./lib/es/*",
|
||||
"require": "./lib/cjs/*",
|
||||
"types": "./lib/types/index.d.ts"
|
||||
},
|
||||
"./locale/*": {
|
||||
"import": "./lib/es/locale/*.js",
|
||||
"require": "./lib/cjs/locale/*.js",
|
||||
"types": "./lib/types/locale/*.d.ts"
|
||||
},
|
||||
"./lib/*": "./lib/*"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest",
|
||||
"build": "univer-cli build"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.9.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc",
|
||||
"rxjs": ">=7.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@univerjs/core": "workspace:*",
|
||||
"@univerjs/engine-render": "workspace:*",
|
||||
"@univerjs/icons": "^0.3.24",
|
||||
"@univerjs/sheets": "workspace:*",
|
||||
"@univerjs/sheets-note": "workspace:*",
|
||||
"@univerjs/sheets-ui": "workspace:*",
|
||||
"@univerjs/ui": "workspace:*",
|
||||
"rxjs": "^7.8.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.0.15",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"less": "^4.1.3",
|
||||
"typescript": "^5.4.3",
|
||||
"vite": "^4.5.2",
|
||||
"vitest": "^1.3.1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from '@univerjs-infra/shared/postcss';
|
||||
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* 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 { SheetsSelectionsService } from '@univerjs/sheets';
|
||||
import { SheetsNotePopupService } from '../../services/sheets-note-popup.service';
|
||||
|
||||
export interface IAddNotePopupOperationParams {
|
||||
trigger?: string;
|
||||
}
|
||||
|
||||
export const AddNotePopupOperation: ICommand = {
|
||||
id: 'sheet.operation.add-note-popup',
|
||||
type: CommandType.OPERATION,
|
||||
handler: async (accessor: IAccessor, params?: IAddNotePopupOperationParams) => {
|
||||
const selectionService = accessor.get(SheetsSelectionsService);
|
||||
const notePopupService = accessor.get(SheetsNotePopupService);
|
||||
const univerInstanceService = accessor.get(IUniverInstanceService);
|
||||
|
||||
const workbook = univerInstanceService.getCurrentUnitForType<Workbook>(UniverInstanceType.UNIVER_SHEET);
|
||||
if (!workbook) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const worksheet = workbook.getActiveSheet();
|
||||
const lastSelection = selectionService.getCurrentLastSelection();
|
||||
if (!lastSelection?.primary) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const { primary } = lastSelection;
|
||||
|
||||
notePopupService.showPopup({
|
||||
unitId: workbook.getUnitId(),
|
||||
subUnitId: worksheet.getSheetId(),
|
||||
row: primary.actualRow,
|
||||
col: primary.actualColumn,
|
||||
temp: false,
|
||||
trigger: params?.trigger,
|
||||
});
|
||||
|
||||
return true;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* 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 { SheetDeleteNoteCommand, SheetToggleNotePopupCommand } from '@univerjs/sheets-note';
|
||||
import { ContextMenuGroup, ContextMenuPosition } from '@univerjs/ui';
|
||||
import { AddNotePopupOperation } from '../commands/operations/add-note-popup.operation';
|
||||
import { sheetDeleteNoteMenuFactory, sheetNoteContextMenuFactory, sheetNoteToggleMenuFactory } from './note.menu';
|
||||
|
||||
export const menuSchema: MenuSchemaType = {
|
||||
[ContextMenuPosition.MAIN_AREA]: {
|
||||
[ContextMenuGroup.OTHERS]: {
|
||||
order: 0,
|
||||
[AddNotePopupOperation.id]: {
|
||||
order: 0,
|
||||
menuItemFactory: sheetNoteContextMenuFactory,
|
||||
},
|
||||
[SheetDeleteNoteCommand.id]: {
|
||||
order: 0,
|
||||
menuItemFactory: sheetDeleteNoteMenuFactory,
|
||||
},
|
||||
[SheetToggleNotePopupCommand.id]: {
|
||||
order: 0,
|
||||
menuItemFactory: sheetNoteToggleMenuFactory,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* 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 { IMenuItem } from '@univerjs/ui';
|
||||
import type { Observable } from 'rxjs';
|
||||
import { IUniverInstanceService, UniverInstanceType } from '@univerjs/core';
|
||||
import { getSheetCommandTarget, SheetsSelectionsService, WorkbookEditablePermission, WorksheetEditPermission } from '@univerjs/sheets';
|
||||
import { SheetDeleteNoteCommand, SheetsNoteModel, SheetToggleNotePopupCommand } from '@univerjs/sheets-note';
|
||||
import { getCurrentRangeDisable$ } from '@univerjs/sheets-ui';
|
||||
import { getMenuHiddenObservable, MenuItemType } from '@univerjs/ui';
|
||||
import { combineLatest, map } from 'rxjs';
|
||||
import { AddNotePopupOperation } from '../commands/operations/add-note-popup.operation';
|
||||
|
||||
export const SHEET_NOTE_CONTEXT_MENU_ID = 'sheet.menu.note';
|
||||
|
||||
function getHasNote$(accessor: IAccessor): Observable<boolean> {
|
||||
const sheetsSelectionsService = accessor.get(SheetsSelectionsService);
|
||||
const univerInstanceService = accessor.get(IUniverInstanceService);
|
||||
return sheetsSelectionsService.selectionMoveEnd$.pipe(map(() => {
|
||||
const selection = sheetsSelectionsService.getCurrentLastSelection();
|
||||
if (!selection?.primary) return false;
|
||||
const target = getSheetCommandTarget(univerInstanceService);
|
||||
if (!target) return false;
|
||||
const { actualColumn, actualRow } = selection.primary;
|
||||
const noteModel = accessor.get(SheetsNoteModel);
|
||||
return Boolean(noteModel.getNote(target.unitId, target.subUnitId, actualRow, actualColumn));
|
||||
}));
|
||||
}
|
||||
|
||||
export function sheetNoteContextMenuFactory(accessor: IAccessor): IMenuItem {
|
||||
return {
|
||||
id: AddNotePopupOperation.id,
|
||||
type: MenuItemType.BUTTON,
|
||||
title: 'rightClick.addNote',
|
||||
icon: 'AddNoteSingle',
|
||||
hidden$: combineLatest([getMenuHiddenObservable(accessor, UniverInstanceType.UNIVER_SHEET), getHasNote$(accessor)])
|
||||
.pipe(map(([hidden, hasNote]) => hidden || hasNote)),
|
||||
disabled$: getCurrentRangeDisable$(accessor, { workbookTypes: [WorkbookEditablePermission], worksheetTypes: [WorksheetEditPermission] }),
|
||||
commandId: AddNotePopupOperation.id,
|
||||
};
|
||||
}
|
||||
|
||||
export function sheetDeleteNoteMenuFactory(accessor: IAccessor): IMenuItem {
|
||||
return {
|
||||
id: SheetDeleteNoteCommand.id,
|
||||
type: MenuItemType.BUTTON,
|
||||
title: 'rightClick.deleteNote',
|
||||
icon: 'DeleteNoteSingle',
|
||||
hidden$: getHasNote$(accessor).pipe(map((hasNote) => !hasNote)),
|
||||
disabled$: getCurrentRangeDisable$(accessor, { workbookTypes: [WorkbookEditablePermission], worksheetTypes: [WorksheetEditPermission] }),
|
||||
};
|
||||
}
|
||||
export function sheetNoteToggleMenuFactory(accessor: IAccessor): IMenuItem {
|
||||
return {
|
||||
id: SheetToggleNotePopupCommand.id,
|
||||
type: MenuItemType.BUTTON,
|
||||
title: 'rightClick.toggleNote',
|
||||
icon: 'HideNoteSingle',
|
||||
hidden$: getHasNote$(accessor).pipe(map((hasNote) => !hasNote)),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* 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 { Disposable, Inject, InterceptorEffectEnum, IUniverInstanceService, UniverInstanceType } from '@univerjs/core';
|
||||
import { IRenderManagerService } from '@univerjs/engine-render';
|
||||
import { INTERCEPTOR_POINT, SheetInterceptorService } from '@univerjs/sheets';
|
||||
import { SheetsNoteModel } from '@univerjs/sheets-note';
|
||||
import { debounceTime } from 'rxjs';
|
||||
|
||||
export class SheetsCellContentController extends Disposable {
|
||||
constructor(
|
||||
@Inject(SheetInterceptorService) private readonly _sheetInterceptorService: SheetInterceptorService,
|
||||
@Inject(SheetsNoteModel) private readonly _sheetsNoteModel: SheetsNoteModel,
|
||||
@IRenderManagerService private readonly _renderManagerService: IRenderManagerService,
|
||||
@IUniverInstanceService private readonly _univerInstanceService: IUniverInstanceService
|
||||
) {
|
||||
super();
|
||||
this._initViewModelIntercept();
|
||||
this._initSkeletonChange();
|
||||
}
|
||||
|
||||
private _initViewModelIntercept() {
|
||||
this.disposeWithMe(
|
||||
this._sheetInterceptorService.intercept(
|
||||
INTERCEPTOR_POINT.CELL_CONTENT,
|
||||
{
|
||||
effect: InterceptorEffectEnum.Style,
|
||||
handler: (cell, pos, next) => {
|
||||
const { row, col, unitId, subUnitId } = pos;
|
||||
const note = this._sheetsNoteModel.getNote(unitId, subUnitId, row, col);
|
||||
if (note) {
|
||||
return next({
|
||||
...cell,
|
||||
markers: {
|
||||
...cell?.markers,
|
||||
tr: {
|
||||
color: '#FFBD37',
|
||||
size: 6,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return next(cell);
|
||||
},
|
||||
priority: 100,
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private _initSkeletonChange() {
|
||||
const markSkeletonDirty = () => {
|
||||
const workbook = this._univerInstanceService.getCurrentUnitForType<Workbook>(UniverInstanceType.UNIVER_SHEET);
|
||||
if (!workbook) return;
|
||||
const unitId = workbook.getUnitId();
|
||||
const currentRender = this._renderManagerService.getRenderById(unitId);
|
||||
currentRender?.mainComponent?.makeForceDirty();
|
||||
};
|
||||
|
||||
this.disposeWithMe(this._sheetsNoteModel.change$.pipe(debounceTime(16)).subscribe(() => {
|
||||
markSkeletonDirty();
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
/**
|
||||
* 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 { IDisposable, Nullable, Workbook } from '@univerjs/core';
|
||||
import type { ISheetLocationBase } from '@univerjs/sheets';
|
||||
import type { ISheetNote } from '@univerjs/sheets-note';
|
||||
import { Disposable, Inject, IUniverInstanceService, ObjectMatrix, UniverInstanceType } from '@univerjs/core';
|
||||
import { SheetsNoteModel } from '@univerjs/sheets-note';
|
||||
import { CellPopupManagerService } from '@univerjs/sheets-ui';
|
||||
import { of, switchMap } from 'rxjs';
|
||||
import { SheetsNotePopupService } from '../services/sheets-note-popup.service';
|
||||
import { SHEET_NOTE_COMPONENT } from '../views/config';
|
||||
|
||||
export class SheetsNoteAttachmentController extends Disposable {
|
||||
private _noteMatrix = new ObjectMatrix<IDisposable>();
|
||||
|
||||
constructor(
|
||||
@Inject(SheetsNoteModel) private readonly _sheetsNoteModel: SheetsNoteModel,
|
||||
@Inject(IUniverInstanceService) private readonly _univerInstanceService: IUniverInstanceService,
|
||||
@Inject(CellPopupManagerService) private readonly _cellPopupManagerService: CellPopupManagerService,
|
||||
@Inject(SheetsNotePopupService) private readonly _sheetsNotePopupService: SheetsNotePopupService
|
||||
) {
|
||||
super();
|
||||
|
||||
this._initNoteChangeListener();
|
||||
}
|
||||
|
||||
private _showPopup(unitId: string, sheetId: string, row: number, col: number) {
|
||||
this._sheetsNotePopupService.hidePopup(true);
|
||||
|
||||
return this._cellPopupManagerService.showPopup(
|
||||
{
|
||||
unitId,
|
||||
subUnitId: sheetId,
|
||||
row,
|
||||
col,
|
||||
},
|
||||
{
|
||||
componentKey: SHEET_NOTE_COMPONENT,
|
||||
direction: 'horizontal',
|
||||
extraProps: {
|
||||
location: {
|
||||
unitId,
|
||||
subUnitId: sheetId,
|
||||
row,
|
||||
col,
|
||||
} as ISheetLocationBase,
|
||||
},
|
||||
priority: 3,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private _initSheet(targetUnitId: string, targetSheetId: string) {
|
||||
const oldMatrix = this._noteMatrix;
|
||||
oldMatrix.forValue((_, __, disposable) => {
|
||||
disposable.dispose();
|
||||
});
|
||||
|
||||
this._noteMatrix = new ObjectMatrix();
|
||||
const handleNote = (unitId: string, sheetId: string, row: number, col: number, note: Nullable<ISheetNote>) => {
|
||||
const matrix = this._noteMatrix;
|
||||
const disposable = matrix.getValue(row, col);
|
||||
if (note?.show) {
|
||||
if (!disposable) {
|
||||
const newDisposable = this._showPopup(unitId, sheetId, row, col);
|
||||
if (newDisposable) {
|
||||
matrix.setValue(row, col, newDisposable);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (disposable) {
|
||||
disposable.dispose();
|
||||
matrix.realDeleteValue(row, col);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this._sheetsNoteModel.getSheetNotes(targetUnitId, targetSheetId)?.forValue((row, col, note) => {
|
||||
handleNote(targetUnitId, targetSheetId, row, col, note);
|
||||
});
|
||||
|
||||
return this._sheetsNoteModel.change$.subscribe((change) => {
|
||||
if (change.unitId !== targetUnitId || change.sheetId !== targetSheetId) {
|
||||
return;
|
||||
}
|
||||
switch (change.type) {
|
||||
case 'ref': {
|
||||
const { unitId, sheetId, row, col, newPosition, note } = change;
|
||||
const matrix = this._noteMatrix;
|
||||
if (!note.show) return;
|
||||
|
||||
const disposable = matrix.getValue(row, col);
|
||||
if (disposable) {
|
||||
disposable.dispose();
|
||||
matrix.realDeleteValue(row, col);
|
||||
}
|
||||
const newDisposable = this._showPopup(unitId, sheetId, newPosition.row, newPosition.col);
|
||||
if (newDisposable) {
|
||||
matrix.setValue(newPosition.row, newPosition.col, newDisposable);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'update': {
|
||||
const { unitId, sheetId, row, col, note } = change;
|
||||
handleNote(unitId, sheetId, row, col, note);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _initNoteChangeListener() {
|
||||
this.disposeWithMe(
|
||||
this._univerInstanceService.getCurrentTypeOfUnit$<Workbook>(UniverInstanceType.UNIVER_SHEET).pipe(
|
||||
switchMap((workbook) => workbook?.activeSheet$ ?? of(null))
|
||||
).subscribe((sheet) => {
|
||||
if (sheet) {
|
||||
const disposable = this._initSheet(sheet.getUnitId(), sheet.getSheetId());
|
||||
return () => {
|
||||
disposable.unsubscribe();
|
||||
};
|
||||
} else {
|
||||
this._noteMatrix.forValue((_, __, disposable) => {
|
||||
disposable.dispose();
|
||||
});
|
||||
this._noteMatrix = new ObjectMatrix();
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/**
|
||||
* 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 { Nullable } from '@univerjs/core';
|
||||
import type { ISelectionWithStyle } from '@univerjs/sheets';
|
||||
import type { IHoverCellPosition } from '@univerjs/sheets-ui';
|
||||
import { Disposable, Inject, RANGE_TYPE, Rectangle } from '@univerjs/core';
|
||||
import { IRenderManagerService } from '@univerjs/engine-render';
|
||||
import { SheetsSelectionsService } from '@univerjs/sheets';
|
||||
import { SheetsNoteModel } from '@univerjs/sheets-note';
|
||||
import { HoverManagerService, IEditorBridgeService, SheetSkeletonManagerService } from '@univerjs/sheets-ui';
|
||||
import { debounceTime } from 'rxjs/operators';
|
||||
import { SheetsNotePopupService } from '../services/sheets-note-popup.service';
|
||||
|
||||
export class SheetsNotePopupController extends Disposable {
|
||||
private _isSwitchingSheet = false;
|
||||
|
||||
constructor(
|
||||
@Inject(SheetsNotePopupService) private readonly _sheetsNotePopupService: SheetsNotePopupService,
|
||||
@Inject(SheetsNoteModel) private readonly _sheetsNoteModel: SheetsNoteModel,
|
||||
@Inject(SheetsSelectionsService) private readonly _sheetSelectionService: SheetsSelectionsService,
|
||||
@IEditorBridgeService private readonly _editorBridgeService: IEditorBridgeService,
|
||||
@IRenderManagerService private readonly _renderManagerService: IRenderManagerService,
|
||||
@Inject(HoverManagerService) private readonly _hoverManagerService: HoverManagerService
|
||||
) {
|
||||
super();
|
||||
|
||||
this._initSelectionUpdateListener();
|
||||
this._initEditorBridge();
|
||||
this._initHoverEvent();
|
||||
}
|
||||
|
||||
private _handleSelectionChange(selections: ISelectionWithStyle[], unitId: string, subUnitId: string) {
|
||||
const range = selections[0]?.range;
|
||||
const render = this._renderManagerService.getRenderById(unitId);
|
||||
const skeleton = render?.with(SheetSkeletonManagerService).getSkeletonParam(subUnitId)?.skeleton;
|
||||
if (!skeleton) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!range) {
|
||||
return;
|
||||
}
|
||||
const actualCell = skeleton.getCellWithCoordByIndex(range.startRow, range.startColumn);
|
||||
|
||||
const rangeType = range.rangeType ?? RANGE_TYPE.NORMAL;
|
||||
if ((rangeType !== RANGE_TYPE.NORMAL || range.endColumn - range.startColumn > 0 || range.endRow - range.startRow > 0) && !((actualCell.isMerged || actualCell.isMergedMainCell) && Rectangle.equals(actualCell.mergeInfo, range))) {
|
||||
this._sheetsNotePopupService.hidePopup();
|
||||
return;
|
||||
}
|
||||
|
||||
const row = actualCell.actualRow;
|
||||
const col = actualCell.actualColumn;
|
||||
const note = this._sheetsNoteModel.getNote(unitId, subUnitId, row, col);
|
||||
if (note?.show) return;
|
||||
|
||||
if (note) {
|
||||
this._sheetsNotePopupService.showPopup({
|
||||
unitId,
|
||||
subUnitId,
|
||||
row,
|
||||
col,
|
||||
});
|
||||
} else {
|
||||
this._sheetsNotePopupService.hidePopup(true);
|
||||
}
|
||||
}
|
||||
|
||||
private _initSelectionUpdateListener() {
|
||||
this.disposeWithMe(
|
||||
this._sheetSelectionService.selectionMoveEnd$.subscribe((selections) => {
|
||||
if (this._isSwitchingSheet) {
|
||||
return;
|
||||
}
|
||||
const current = this._sheetSelectionService.currentSelectionParam;
|
||||
if (!current) {
|
||||
return;
|
||||
}
|
||||
this._handleSelectionChange(selections, current.unitId, current.sheetId);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private _initEditorBridge() {
|
||||
this.disposeWithMe(
|
||||
this._editorBridgeService.visible$.subscribe((visible) => {
|
||||
if (visible.visible) {
|
||||
this._sheetsNotePopupService.hidePopup(true);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private _initHoverEvent() {
|
||||
this.disposeWithMe(
|
||||
this._hoverManagerService.currentCell$.pipe(debounceTime(100)).subscribe((cell: Nullable<IHoverCellPosition>) => {
|
||||
if (!cell?.location) return;
|
||||
|
||||
const { unitId, subUnitId, row, col } = cell.location;
|
||||
|
||||
const note = this._sheetsNoteModel.getNote(unitId, subUnitId, row, col);
|
||||
if (note?.show) return;
|
||||
if (note) {
|
||||
this._sheetsNotePopupService.showPopup({
|
||||
unitId,
|
||||
subUnitId,
|
||||
row,
|
||||
col,
|
||||
temp: true,
|
||||
});
|
||||
} else {
|
||||
this._sheetsNotePopupService.hidePopup();
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* 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 { Disposable, ICommandService, Inject } from '@univerjs/core';
|
||||
import { AddNoteSingle, DeleteNoteSingle, HideNoteSingle } from '@univerjs/icons';
|
||||
import { ComponentManager, IMenuManagerService } from '@univerjs/ui';
|
||||
import { AddNotePopupOperation } from '../commands/operations/add-note-popup.operation';
|
||||
import { SHEET_NOTE_COMPONENT } from '../views/config';
|
||||
import { SheetsNote } from '../views/note';
|
||||
import { menuSchema } from './menu.schema';
|
||||
|
||||
export class SheetsNoteUIController extends Disposable {
|
||||
constructor(
|
||||
@Inject(ComponentManager) private readonly _componentManager: ComponentManager,
|
||||
@Inject(IMenuManagerService) private readonly _menuManagerService: IMenuManagerService,
|
||||
@ICommandService private readonly _commandService: ICommandService
|
||||
) {
|
||||
super();
|
||||
|
||||
this._initComponents();
|
||||
this._initMenu();
|
||||
this._initCommands();
|
||||
}
|
||||
|
||||
private _initComponents() {
|
||||
this.disposeWithMe(this._componentManager.register(SHEET_NOTE_COMPONENT, SheetsNote));
|
||||
this.disposeWithMe(this._componentManager.register('AddNoteSingle', AddNoteSingle));
|
||||
this.disposeWithMe(this._componentManager.register('DeleteNoteSingle', DeleteNoteSingle));
|
||||
this.disposeWithMe(this._componentManager.register('HideNoteSingle', HideNoteSingle));
|
||||
}
|
||||
|
||||
private _initMenu() {
|
||||
this._menuManagerService.mergeMenu(menuSchema);
|
||||
}
|
||||
|
||||
private _initCommands() {
|
||||
this._commandService.registerCommand(AddNotePopupOperation);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export * from './plugin';
|
||||
export * from './services/sheets-note-popup.service';
|
||||
export * from './views/note';
|
||||
export * from './controllers/sheets-note-popup.controller';
|
||||
export * from './controllers/sheets-cell-content.controller';
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* 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 zhCN from './zh-CN';
|
||||
|
||||
const locale: typeof zhCN = {
|
||||
rightClick: {
|
||||
addNote: 'Add Note',
|
||||
deleteNote: 'Delete Note',
|
||||
toggleNote: 'Show/Hide Note',
|
||||
},
|
||||
note: {
|
||||
placeholder: 'Type here',
|
||||
},
|
||||
};
|
||||
|
||||
export default locale;
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* 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 zhCN from './zh-CN';
|
||||
|
||||
const locale: typeof zhCN = {
|
||||
rightClick: {
|
||||
addNote: 'افزودن یادداشت',
|
||||
deleteNote: 'حذف یادداشت',
|
||||
toggleNote: 'نمایش/پنهان کردن یادداشت',
|
||||
},
|
||||
note: {
|
||||
placeholder: 'اینجا تایپ کنید',
|
||||
},
|
||||
};
|
||||
|
||||
export default locale;
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* 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 zhCN from './zh-CN';
|
||||
|
||||
const locale: typeof zhCN = {
|
||||
rightClick: {
|
||||
addNote: 'Ajouter une note',
|
||||
deleteNote: 'Supprimer la note',
|
||||
toggleNote: 'Afficher/Masquer la note',
|
||||
},
|
||||
note: {
|
||||
placeholder: 'Écrivez ici',
|
||||
},
|
||||
};
|
||||
|
||||
export default locale;
|
||||
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { default as enUS } from './en-US';
|
||||
export { default as faIR } from './fa-IR';
|
||||
export { default as frFR } from './fr-FR';
|
||||
export { default as ruRU } from './ru-RU';
|
||||
export { default as viVN } from './vi-VN';
|
||||
export { default as zhCN } from './zh-CN';
|
||||
export { default as zhTW } from './zh-TW';
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* 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 zhCN from './zh-CN';
|
||||
|
||||
const locale: typeof zhCN = {
|
||||
rightClick: {
|
||||
addNote: 'Добавить примечание',
|
||||
deleteNote: 'Удалить примечание',
|
||||
toggleNote: 'Показать/Скрыть примечание',
|
||||
},
|
||||
note: {
|
||||
placeholder: 'Введите текст здесь',
|
||||
},
|
||||
};
|
||||
|
||||
export default locale;
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* 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 zhCN from './zh-CN';
|
||||
|
||||
const locale: typeof zhCN = {
|
||||
rightClick: {
|
||||
addNote: 'Thêm ghi chú',
|
||||
deleteNote: 'Xóa ghi chú',
|
||||
toggleNote: 'Hiện/Ẩn ghi chú',
|
||||
},
|
||||
note: {
|
||||
placeholder: 'Nhập tại đây',
|
||||
},
|
||||
};
|
||||
|
||||
export default locale;
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
const locale = {
|
||||
rightClick: {
|
||||
addNote: '添加批注',
|
||||
deleteNote: '删除批注',
|
||||
toggleNote: '显示/隐藏批注',
|
||||
},
|
||||
note: {
|
||||
placeholder: '在此输入',
|
||||
},
|
||||
};
|
||||
|
||||
export default locale;
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* 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 zhCN from './zh-CN';
|
||||
|
||||
const locale: typeof zhCN = {
|
||||
rightClick: {
|
||||
addNote: '添加批註',
|
||||
deleteNote: '刪除批註',
|
||||
toggleNote: '顯示/隱藏批註',
|
||||
},
|
||||
note: {
|
||||
placeholder: '在此輸入',
|
||||
},
|
||||
};
|
||||
|
||||
export default locale;
|
||||
@@ -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 { Dependency } from '@univerjs/core';
|
||||
import { DependentOn, IConfigService, Inject, Injector, merge, Plugin, touchDependencies, UniverInstanceType } from '@univerjs/core';
|
||||
import { SheetsNotePlugin } from '@univerjs/sheets-note';
|
||||
import { menuSchema } from './controllers/menu.schema';
|
||||
import { SheetsCellContentController } from './controllers/sheets-cell-content.controller';
|
||||
import { SheetsNoteAttachmentController } from './controllers/sheets-note-attachment.controller';
|
||||
import { SheetsNotePopupController } from './controllers/sheets-note-popup.controller';
|
||||
import { SheetsNoteUIController } from './controllers/sheets-note-ui.controller';
|
||||
import { SheetsNotePopupService } from './services/sheets-note-popup.service';
|
||||
|
||||
export const PLUGIN_NAME = 'SHEET_NOTE_UI_PLUGIN';
|
||||
export const SHEETS_NOTE_UI_PLUGIN_CONFIG_KEY = 'sheets-note-ui';
|
||||
|
||||
export interface IUniverSheetsNoteUIPluginConfig {
|
||||
menu?: {
|
||||
[key: string]: unknown;
|
||||
};
|
||||
defaultNoteSize?: {
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
}
|
||||
|
||||
const defaultPluginConfig: IUniverSheetsNoteUIPluginConfig = {
|
||||
menu: menuSchema,
|
||||
};
|
||||
|
||||
@DependentOn(SheetsNotePlugin)
|
||||
export class SheetsNoteUIPlugin extends Plugin {
|
||||
static override pluginName = PLUGIN_NAME;
|
||||
static override type = UniverInstanceType.UNIVER_SHEET;
|
||||
|
||||
constructor(
|
||||
private readonly _config: Partial<IUniverSheetsNoteUIPluginConfig> = defaultPluginConfig,
|
||||
@Inject(Injector) protected override readonly _injector: Injector,
|
||||
@IConfigService private readonly _configService: IConfigService
|
||||
) {
|
||||
super();
|
||||
// Manage the plugin configuration.
|
||||
const { menu, ...rest } = merge(
|
||||
{},
|
||||
defaultPluginConfig,
|
||||
this._config
|
||||
);
|
||||
if (menu) {
|
||||
this._configService.setConfig('menu', menu, { merge: true });
|
||||
}
|
||||
this._configService.setConfig(SHEETS_NOTE_UI_PLUGIN_CONFIG_KEY, rest);
|
||||
}
|
||||
|
||||
override onStarting(): void {
|
||||
([
|
||||
[SheetsNotePopupService],
|
||||
[SheetsCellContentController],
|
||||
[SheetsNotePopupController],
|
||||
[SheetsNoteUIController],
|
||||
[SheetsNoteAttachmentController],
|
||||
] as Dependency[]).forEach((dependency) => {
|
||||
this._injector.add(dependency);
|
||||
});
|
||||
}
|
||||
|
||||
override onReady(): void {
|
||||
touchDependencies(this._injector, [
|
||||
[SheetsNoteUIController],
|
||||
[SheetsCellContentController],
|
||||
]);
|
||||
}
|
||||
|
||||
override onRendered(): void {
|
||||
touchDependencies(this._injector, [
|
||||
[SheetsNotePopupController],
|
||||
[SheetsNoteAttachmentController],
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/**
|
||||
* 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 { IDisposable, Nullable } from '@univerjs/core';
|
||||
import type { ISheetLocationBase } from '@univerjs/sheets';
|
||||
import { Disposable, DisposableCollection, Inject } from '@univerjs/core';
|
||||
import { CellPopupManagerService } from '@univerjs/sheets-ui';
|
||||
import { IZenZoneService } from '@univerjs/ui';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { SHEET_NOTE_COMPONENT } from '../views/config';
|
||||
|
||||
export interface INotePopup extends ISheetLocationBase {
|
||||
noteId?: string;
|
||||
temp?: boolean;
|
||||
trigger?: string;
|
||||
}
|
||||
|
||||
export class SheetsNotePopupService extends Disposable {
|
||||
private _lastPopup: Nullable<IDisposable> = null;
|
||||
private _activePopup: Nullable<INotePopup>;
|
||||
private _activePopup$ = new BehaviorSubject<Nullable<INotePopup>>(null);
|
||||
|
||||
activePopup$ = this._activePopup$.asObservable();
|
||||
|
||||
get activePopup() {
|
||||
return this._activePopup;
|
||||
}
|
||||
|
||||
constructor(
|
||||
@IZenZoneService private readonly _zenZoneService: IZenZoneService,
|
||||
@Inject(CellPopupManagerService) private readonly _cellPopupManagerService: CellPopupManagerService
|
||||
) {
|
||||
super();
|
||||
this._initZenVisible();
|
||||
|
||||
this.disposeWithMe(() => {
|
||||
this._activePopup$.complete();
|
||||
});
|
||||
}
|
||||
|
||||
private _initZenVisible() {
|
||||
this.disposeWithMe(this._zenZoneService.visible$.subscribe((visible) => {
|
||||
if (visible) {
|
||||
this.hidePopup();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
showPopup(location: INotePopup, onHide?: () => void) {
|
||||
const { row, col, unitId, subUnitId } = location;
|
||||
if (
|
||||
this._activePopup &&
|
||||
row === this._activePopup.row &&
|
||||
col === this._activePopup.col &&
|
||||
unitId === this._activePopup.unitId &&
|
||||
subUnitId === this.activePopup?.subUnitId
|
||||
) {
|
||||
this._activePopup = location;
|
||||
this._activePopup$.next(location);
|
||||
return;
|
||||
}
|
||||
if (this._lastPopup) {
|
||||
this._lastPopup.dispose();
|
||||
};
|
||||
if (this._zenZoneService.visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._activePopup = location;
|
||||
this._activePopup$.next(location);
|
||||
|
||||
const popupDisposable = this._cellPopupManagerService.showPopup(
|
||||
{
|
||||
unitId,
|
||||
subUnitId,
|
||||
row,
|
||||
col,
|
||||
},
|
||||
{
|
||||
componentKey: SHEET_NOTE_COMPONENT,
|
||||
onClickOutside: () => {
|
||||
this.hidePopup();
|
||||
},
|
||||
direction: 'horizontal',
|
||||
extraProps: {
|
||||
location,
|
||||
},
|
||||
priority: 3,
|
||||
}
|
||||
);
|
||||
|
||||
if (!popupDisposable) {
|
||||
throw new Error('[SheetsNotePopupService]: cannot show popup!');
|
||||
}
|
||||
|
||||
const disposableCollection = new DisposableCollection();
|
||||
disposableCollection.add(popupDisposable);
|
||||
disposableCollection.add({
|
||||
dispose: () => {
|
||||
onHide?.();
|
||||
},
|
||||
});
|
||||
|
||||
this._lastPopup = disposableCollection;
|
||||
}
|
||||
|
||||
hidePopup(force?: boolean) {
|
||||
if (!this._activePopup) {
|
||||
return;
|
||||
}
|
||||
if (!force && !this._activePopup.temp) return;
|
||||
if (this._lastPopup) {
|
||||
this._lastPopup.dispose();
|
||||
}
|
||||
this._lastPopup = null;
|
||||
|
||||
this._activePopup = null;
|
||||
this._activePopup$.next(null);
|
||||
}
|
||||
|
||||
persistPopup() {
|
||||
if (!this._activePopup || !this._activePopup.temp) {
|
||||
return;
|
||||
}
|
||||
this._activePopup = {
|
||||
...this._activePopup,
|
||||
temp: false,
|
||||
};
|
||||
|
||||
this._activePopup$.next(this._activePopup);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export const SHEET_NOTE_COMPONENT = 'SHEET_NOTE_COMPONENT';
|
||||
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* 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 { ISheetLocationBase } from '@univerjs/sheets';
|
||||
import type { ISheetNote } from '@univerjs/sheets-note';
|
||||
import type { IPopup } from '@univerjs/ui';
|
||||
import type { IUniverSheetsNoteUIPluginConfig } from '../plugin';
|
||||
import { ICommandService, LocaleService } from '@univerjs/core';
|
||||
import { SheetDeleteNoteCommand, SheetsNoteModel, SheetUpdateNoteCommand } from '@univerjs/sheets-note';
|
||||
import { useConfigValue, useDebounceFn, useDependency } from '@univerjs/ui';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { of } from 'rxjs';
|
||||
import { SHEETS_NOTE_UI_PLUGIN_CONFIG_KEY } from '../plugin';
|
||||
|
||||
export const SheetsNote = (props: { popup: IPopup<{ location: ISheetLocationBase }> }) => {
|
||||
const noteModel = useDependency(SheetsNoteModel);
|
||||
const localeService = useDependency(LocaleService);
|
||||
const config = useConfigValue<IUniverSheetsNoteUIPluginConfig>(SHEETS_NOTE_UI_PLUGIN_CONFIG_KEY);
|
||||
const [note, setNote] = useState<ISheetNote>({ width: config?.defaultNoteSize?.width || 216, height: config?.defaultNoteSize?.height || 92, note: '' });
|
||||
const activePopup = props.popup.extraProps?.location;
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
const commandService = useDependency(ICommandService);
|
||||
const cellNoteChange$ = useMemo(() => activePopup ? noteModel.getCellNoteChange$(activePopup.unitId, activePopup.subUnitId!, activePopup.row, activePopup.col) : of(null), [activePopup]);
|
||||
|
||||
const updateNote = useDebounceFn((newNote: ISheetNote) => {
|
||||
if (!activePopup) return;
|
||||
|
||||
if (newNote.note) {
|
||||
commandService.executeCommand(SheetUpdateNoteCommand.id, {
|
||||
unitId: activePopup.unitId,
|
||||
sheetId: activePopup.subUnitId!,
|
||||
row: activePopup.row,
|
||||
col: activePopup.col,
|
||||
note: newNote,
|
||||
});
|
||||
} else {
|
||||
commandService.executeCommand(SheetDeleteNoteCommand.id, {
|
||||
unitId: activePopup.unitId,
|
||||
sheetId: activePopup.subUnitId!,
|
||||
row: activePopup.row,
|
||||
col: activePopup.col,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (activePopup) {
|
||||
const existingNote = noteModel.getNote(activePopup.unitId, activePopup.subUnitId!, activePopup.row, activePopup.col);
|
||||
if (existingNote) {
|
||||
setNote(existingNote);
|
||||
} else {
|
||||
textareaRef.current?.focus();
|
||||
}
|
||||
}
|
||||
}, [activePopup, noteModel]);
|
||||
|
||||
useEffect(() => {
|
||||
const sub = cellNoteChange$.subscribe(() => {
|
||||
if (!activePopup) return;
|
||||
const existingNote = noteModel.getNote(activePopup.unitId, activePopup.subUnitId!, activePopup.row, activePopup.col);
|
||||
if (existingNote) {
|
||||
setNote(existingNote);
|
||||
}
|
||||
});
|
||||
|
||||
return () => sub.unsubscribe();
|
||||
}, [cellNoteChange$]);
|
||||
|
||||
useEffect(() => {
|
||||
const textarea = textareaRef.current;
|
||||
if (!textarea) return;
|
||||
|
||||
const resizeObserver = new ResizeObserver((entries) => {
|
||||
const entry = entries[0];
|
||||
if (!entry) return;
|
||||
|
||||
const { width, height } = entry.contentRect;
|
||||
const newNote = { ...note, width, height };
|
||||
setNote(newNote);
|
||||
updateNote(newNote);
|
||||
});
|
||||
|
||||
resizeObserver.observe(textarea);
|
||||
|
||||
return () => {
|
||||
resizeObserver.disconnect();
|
||||
};
|
||||
}, [note.note]); // Only re-run when note content changes
|
||||
|
||||
const handleNoteChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||
const noteString = e.target.value;
|
||||
const newNote = { ...note, note: noteString };
|
||||
setNote(newNote);
|
||||
updateNote(newNote);
|
||||
};
|
||||
|
||||
if (!activePopup) return null;
|
||||
|
||||
return (
|
||||
<textarea
|
||||
style={{ width: note.width, height: note.height }}
|
||||
ref={textareaRef}
|
||||
className={`
|
||||
univer-note-textarea univer-resize-both univer-ml-[1px] univer-rounded univer-border univer-border-solid
|
||||
univer-border-gray-200 univer-p-2 univer-shadow
|
||||
focus:univer-outline-none
|
||||
`}
|
||||
value={note.note}
|
||||
onChange={handleNoteChange}
|
||||
placeholder={localeService.t('note.placeholder')}
|
||||
/>
|
||||
);
|
||||
};
|
||||
+1
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* 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 { Config } from 'tailwindcss';
|
||||
import preset from '@univerjs-infra/shared/tailwind';
|
||||
|
||||
const config: Config = {
|
||||
presets: [preset],
|
||||
content: [
|
||||
'./src/**/*.{js,ts,jsx,tsx}',
|
||||
],
|
||||
};
|
||||
|
||||
export default config;
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "@univerjs-infra/shared/tsconfigs/base",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"outDir": "lib/types"
|
||||
},
|
||||
"references": [{ "path": "./tsconfig.node.json" }],
|
||||
"include": ["src"]
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "@univerjs-infra/shared/tsconfigs/node",
|
||||
"include": ["vitest.config.ts"]
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import createConfig from '@univerjs-infra/shared/vitest';
|
||||
|
||||
export default createConfig();
|
||||
@@ -0,0 +1,28 @@
|
||||
# @univerjs/sheets-note
|
||||
|
||||
| Package | Description | Version | License | Downloads | Continuous Integration | Coverage |
|
||||
|---------|-------------|---------|----------|-----------|----------------------|----------|
|
||||
| `@univerjs/sheets-note` | `UniverSheetsNote` | [![][npm-version-shield]][npm-version-link] | ![][npm-license-shield] | ![][npm-downloads-shield] | ❌ | ❌ |
|
||||
|
||||
## Introduction
|
||||
|
||||
`@univerjs/sheets-note` core package for sheet note. For using, please refer to [@univerjs/sheets-note](../sheets-note/README.md)
|
||||
|
||||
## Usage
|
||||
|
||||
### Installation
|
||||
|
||||
```shell
|
||||
npm install @univerjs/sheets-note
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```shell
|
||||
pnpm add @univerjs/sheets-note
|
||||
```
|
||||
|
||||
[npm-version-shield]: https://img.shields.io/npm/v/@univerjs/sheets-note?style=flat-square
|
||||
[npm-version-link]: https://npmjs.com/package/@univerjs/sheets-note
|
||||
[npm-license-shield]: https://img.shields.io/npm/l/@univerjs/sheets-note?style=flat-square
|
||||
[npm-downloads-shield]: https://img.shields.io/npm/dm/@univerjs/sheets-note?style=flat-square
|
||||
@@ -0,0 +1,83 @@
|
||||
{
|
||||
"name": "@univerjs/sheets-note",
|
||||
"version": "0.6.10",
|
||||
"private": false,
|
||||
"description": "Univer sheets note base plugin",
|
||||
"author": "DreamNum <developer@univer.ai>",
|
||||
"license": "Apache-2.0",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/univer"
|
||||
},
|
||||
"homepage": "https://univer.ai",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/dream-num/univer"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/dream-num/univer/issues"
|
||||
},
|
||||
"keywords": [],
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./*": "./src/*",
|
||||
"./facade": "./src/facade/index.ts"
|
||||
},
|
||||
"main": "./src/index.ts",
|
||||
"types": "./lib/types/index.d.ts",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"main": "./lib/es/index.js",
|
||||
"module": "./lib/es/index.js",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./lib/es/index.js",
|
||||
"require": "./lib/cjs/index.js",
|
||||
"types": "./lib/types/index.d.ts"
|
||||
},
|
||||
"./*": {
|
||||
"import": "./lib/es/*",
|
||||
"require": "./lib/cjs/*",
|
||||
"types": "./lib/types/index.d.ts"
|
||||
},
|
||||
"./facade": {
|
||||
"import": "./lib/es/facade.js",
|
||||
"require": "./lib/cjs/facade.js",
|
||||
"types": "./lib/types/facade/index.d.ts"
|
||||
},
|
||||
"./lib/facade": {
|
||||
"import": "./lib/es/facade.js",
|
||||
"require": "./lib/cjs/facade.js",
|
||||
"types": "./lib/types/facade/index.d.ts"
|
||||
},
|
||||
"./lib/*": "./lib/*"
|
||||
}
|
||||
},
|
||||
"directories": {
|
||||
"lib": "lib"
|
||||
},
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest",
|
||||
"coverage": "vitest run --coverage",
|
||||
"lint:types": "tsc --noEmit",
|
||||
"build": "univer-cli build"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"rxjs": ">=7.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@univerjs/core": "workspace:*",
|
||||
"@univerjs/sheets": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@univerjs-infra/shared": "workspace:*",
|
||||
"rxjs": "^7.8.1",
|
||||
"typescript": "^5.8.3",
|
||||
"vite": "^6.3.3",
|
||||
"vitest": "^3.1.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* 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 } from '@univerjs/core';
|
||||
import type { IUpdateNoteMutationParams } from '../mutations/note.mutation';
|
||||
import { CommandType, ICommandService, IUniverInstanceService } from '@univerjs/core';
|
||||
import { getSheetCommandTarget, SheetsSelectionsService } from '@univerjs/sheets';
|
||||
import { RemoveNoteMutation, ToggleNotePopupMutation, UpdateNoteMutation } from '../mutations/note.mutation';
|
||||
|
||||
export const SheetDeleteNoteCommand: ICommand = {
|
||||
id: 'sheet.command.delete-note',
|
||||
type: CommandType.COMMAND,
|
||||
handler: (accessor, params) => {
|
||||
const univerInstanceService = accessor.get(IUniverInstanceService);
|
||||
const target = getSheetCommandTarget(univerInstanceService);
|
||||
if (!target) return false;
|
||||
const sheetsSelectionsService = accessor.get(SheetsSelectionsService);
|
||||
const selection = sheetsSelectionsService.getCurrentLastSelection();
|
||||
if (!selection?.primary) return false;
|
||||
const { actualColumn, actualRow } = selection.primary;
|
||||
const commandService = accessor.get(ICommandService);
|
||||
|
||||
return commandService.executeCommand(RemoveNoteMutation.id, {
|
||||
unitId: target.unitId,
|
||||
sheetId: target.subUnitId,
|
||||
row: actualRow,
|
||||
col: actualColumn,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export const SheetToggleNotePopupCommand: ICommand = {
|
||||
id: 'sheet.command.toggle-note-popup',
|
||||
type: CommandType.COMMAND,
|
||||
handler: (accessor, params) => {
|
||||
const univerInstanceService = accessor.get(IUniverInstanceService);
|
||||
const target = getSheetCommandTarget(univerInstanceService);
|
||||
if (!target) return false;
|
||||
const sheetsSelectionsService = accessor.get(SheetsSelectionsService);
|
||||
const selection = sheetsSelectionsService.getCurrentLastSelection();
|
||||
if (!selection?.primary) return false;
|
||||
const { actualColumn, actualRow } = selection.primary;
|
||||
const commandService = accessor.get(ICommandService);
|
||||
return commandService.executeCommand(ToggleNotePopupMutation.id, {
|
||||
unitId: target.unitId,
|
||||
sheetId: target.subUnitId,
|
||||
row: actualRow,
|
||||
col: actualColumn,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export const SheetUpdateNoteCommand: ICommand<IUpdateNoteMutationParams> = {
|
||||
id: 'sheet.command.update-note',
|
||||
type: CommandType.COMMAND,
|
||||
handler: (accessor, params) => {
|
||||
const commandService = accessor.get(ICommandService);
|
||||
return commandService.syncExecuteCommand(UpdateNoteMutation.id, params);
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* 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, IMutation } from '@univerjs/core';
|
||||
import type { ISheetNote } from '../../models/sheets-note.model';
|
||||
import { CommandType } from '@univerjs/core';
|
||||
import { SheetsNoteModel } from '../../models/sheets-note.model';
|
||||
|
||||
export interface IUpdateNoteMutationParams {
|
||||
unitId: string;
|
||||
sheetId: string;
|
||||
row: number;
|
||||
col: number;
|
||||
note: ISheetNote;
|
||||
silent?: boolean;
|
||||
}
|
||||
|
||||
export const UpdateNoteMutation: IMutation<IUpdateNoteMutationParams> = {
|
||||
id: 'sheet.mutation.update-note',
|
||||
type: CommandType.MUTATION,
|
||||
handler: (accessor: IAccessor, params: IUpdateNoteMutationParams) => {
|
||||
const { unitId, sheetId, row, col, note, silent } = params;
|
||||
const sheetsNoteModel = accessor.get(SheetsNoteModel);
|
||||
sheetsNoteModel.updateNote(unitId, sheetId, row, col, note, silent);
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
||||
export interface IRemoveNoteMutationParams {
|
||||
unitId: string;
|
||||
sheetId: string;
|
||||
row: number;
|
||||
col: number;
|
||||
silent?: boolean;
|
||||
}
|
||||
|
||||
export const RemoveNoteMutation: IMutation<IRemoveNoteMutationParams> = {
|
||||
id: 'sheet.mutation.remove-note',
|
||||
type: CommandType.MUTATION,
|
||||
handler: (accessor: IAccessor, params: IRemoveNoteMutationParams) => {
|
||||
const { unitId, sheetId, row, col, silent } = params;
|
||||
const sheetsNoteModel = accessor.get(SheetsNoteModel);
|
||||
sheetsNoteModel.removeNote(unitId, sheetId, row, col, silent);
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
||||
export interface IToggleNotePopupMutationParams {
|
||||
unitId: string;
|
||||
sheetId: string;
|
||||
row: number;
|
||||
col: number;
|
||||
silent?: boolean;
|
||||
}
|
||||
|
||||
export const ToggleNotePopupMutation: IMutation<IToggleNotePopupMutationParams> = {
|
||||
id: 'sheet.mutation.toggle-note-popup',
|
||||
type: CommandType.MUTATION,
|
||||
handler: (accessor: IAccessor, params: IToggleNotePopupMutationParams) => {
|
||||
const { unitId, sheetId, row, col, silent } = params;
|
||||
const sheetsNoteModel = accessor.get(SheetsNoteModel);
|
||||
sheetsNoteModel.toggleNotePopup(unitId, sheetId, row, col, silent);
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
||||
export interface IUpdateNotePositionMutationParams {
|
||||
unitId: string;
|
||||
sheetId: string;
|
||||
row: number;
|
||||
col: number;
|
||||
newPosition: {
|
||||
row: number;
|
||||
col: number;
|
||||
};
|
||||
silent?: boolean;
|
||||
}
|
||||
|
||||
export const UpdateNotePositionMutation: IMutation<IUpdateNotePositionMutationParams> = {
|
||||
id: 'sheet.mutation.update-note-position',
|
||||
type: CommandType.MUTATION,
|
||||
handler: (accessor: IAccessor, params: IUpdateNotePositionMutationParams) => {
|
||||
const { unitId, sheetId, row, col, newPosition, silent } = params;
|
||||
const sheetsNoteModel = accessor.get(SheetsNoteModel);
|
||||
sheetsNoteModel.updateNotePosition(unitId, sheetId, row, col, newPosition.row, newPosition.col, silent);
|
||||
return true;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,205 @@
|
||||
/**
|
||||
* 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 { IDisposable, IRange, Nullable } from '@univerjs/core';
|
||||
import type { EffectRefRangeParams } from '@univerjs/sheets';
|
||||
import type { ISheetNote } from '../models/sheets-note.model';
|
||||
import { Disposable, ICommandService, Inject, sequenceExecuteAsync } from '@univerjs/core';
|
||||
import { handleCommonRangeChangeWithEffectRefCommandsSkipNoInterests, RefRangeService, SheetsSelectionsService } from '@univerjs/sheets';
|
||||
import { RemoveNoteMutation, UpdateNoteMutation, UpdateNotePositionMutation } from '../commands/mutations/note.mutation';
|
||||
import { SheetsNoteModel } from '../models/sheets-note.model';
|
||||
|
||||
export class SheetsNoteRefRangeController extends Disposable {
|
||||
private _disposableMap = new Map<string, IDisposable>();
|
||||
private _watcherMap = new Map<string, IDisposable>();
|
||||
|
||||
constructor(
|
||||
@Inject(RefRangeService) private readonly _refRangeService: RefRangeService,
|
||||
@Inject(SheetsNoteModel) private readonly _sheetsNoteModel: SheetsNoteModel,
|
||||
@Inject(SheetsSelectionsService) private readonly _selectionManagerService: SheetsSelectionsService,
|
||||
@ICommandService private readonly _commandService: ICommandService
|
||||
) {
|
||||
super();
|
||||
this._initData();
|
||||
this._initRefRange();
|
||||
}
|
||||
|
||||
private _getIdWithUnitId(unitId: string, subUnitId: string, row: number, col: number) {
|
||||
return `${unitId}-${subUnitId}-${row}-${col}`;
|
||||
}
|
||||
|
||||
private _handleRangeChange = (unitId: string, subUnitId: string, note: ISheetNote, row: number, col: number, resultRange: Nullable<IRange>, silent?: boolean) => {
|
||||
if (!resultRange) {
|
||||
return {
|
||||
redos: [{
|
||||
id: RemoveNoteMutation.id,
|
||||
params: {
|
||||
unitId,
|
||||
sheetId: subUnitId,
|
||||
row,
|
||||
col,
|
||||
},
|
||||
}],
|
||||
undos: [{
|
||||
id: UpdateNoteMutation.id,
|
||||
params: {
|
||||
unitId,
|
||||
sheetId: subUnitId,
|
||||
row,
|
||||
col,
|
||||
note,
|
||||
},
|
||||
}],
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
redos: [{
|
||||
id: UpdateNotePositionMutation.id,
|
||||
params: {
|
||||
unitId,
|
||||
sheetId: subUnitId,
|
||||
row,
|
||||
col,
|
||||
newPosition: {
|
||||
row: resultRange.startRow,
|
||||
col: resultRange.startColumn,
|
||||
},
|
||||
silent,
|
||||
},
|
||||
}],
|
||||
undos: [{
|
||||
id: UpdateNotePositionMutation.id,
|
||||
params: {
|
||||
unitId,
|
||||
sheetId: subUnitId,
|
||||
row: resultRange.startRow,
|
||||
col: resultRange.startColumn,
|
||||
newPosition: {
|
||||
row,
|
||||
col,
|
||||
},
|
||||
note,
|
||||
silent,
|
||||
},
|
||||
}],
|
||||
};
|
||||
};
|
||||
|
||||
private _register(unitId: string, subUnitId: string, note: ISheetNote, row: number, col: number) {
|
||||
const oldRange: IRange = {
|
||||
startColumn: col,
|
||||
endColumn: col,
|
||||
startRow: row,
|
||||
endRow: row,
|
||||
};
|
||||
|
||||
this._disposableMap.set(
|
||||
this._getIdWithUnitId(unitId, subUnitId, row, col),
|
||||
this._refRangeService.registerRefRange(oldRange, (commandInfo: EffectRefRangeParams) => {
|
||||
const resultRanges = handleCommonRangeChangeWithEffectRefCommandsSkipNoInterests(oldRange, commandInfo, { selectionManagerService: this._selectionManagerService });
|
||||
const resultRange = Array.isArray(resultRanges) ? resultRanges[0] : resultRanges;
|
||||
if (resultRange && resultRange.startColumn === oldRange.startColumn && resultRange.startRow === oldRange.startRow) {
|
||||
return {
|
||||
undos: [],
|
||||
redos: [],
|
||||
};
|
||||
}
|
||||
const res = this._handleRangeChange(unitId, subUnitId, note, row, col, resultRange, false);
|
||||
return res;
|
||||
}, unitId, subUnitId)
|
||||
);
|
||||
}
|
||||
|
||||
private _watch(unitId: string, subUnitId: string, note: ISheetNote, row: number, col: number) {
|
||||
const oldRange: IRange = {
|
||||
startColumn: col,
|
||||
endColumn: col,
|
||||
startRow: row,
|
||||
endRow: row,
|
||||
};
|
||||
|
||||
this._watcherMap.set(
|
||||
this._getIdWithUnitId(unitId, subUnitId, row, col),
|
||||
this._refRangeService.watchRange(unitId, subUnitId, oldRange, (before, after) => {
|
||||
const { redos } = this._handleRangeChange(unitId, subUnitId, note, before.startRow, before.startColumn, after, true);
|
||||
sequenceExecuteAsync(redos, this._commandService, { onlyLocal: true });
|
||||
}, true)
|
||||
);
|
||||
}
|
||||
|
||||
private _unwatch(unitId: string, subUnitId: string, row: number, col: number) {
|
||||
const id = this._getIdWithUnitId(unitId, subUnitId, row, col);
|
||||
this._watcherMap.get(id)?.dispose();
|
||||
this._watcherMap.delete(id);
|
||||
}
|
||||
|
||||
private _unregister(unitId: string, subUnitId: string, row: number, col: number) {
|
||||
const id = this._getIdWithUnitId(unitId, subUnitId, row, col);
|
||||
this._disposableMap.get(id)?.dispose();
|
||||
this._disposableMap.delete(id);
|
||||
}
|
||||
|
||||
private _initData() {
|
||||
const unitNotes = this._sheetsNoteModel.getNotes();
|
||||
for (const [unitId, unitNote] of unitNotes) {
|
||||
for (const [sheetId, matrix] of unitNote) {
|
||||
matrix.forValue((row: number, col: number, value: ISheetNote) => {
|
||||
if (value) {
|
||||
this._register(unitId, sheetId, value, row, col);
|
||||
this._watch(unitId, sheetId, value, row, col);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _initRefRange() {
|
||||
this.disposeWithMe(
|
||||
this._sheetsNoteModel.change$.subscribe((option) => {
|
||||
switch (option.type) {
|
||||
case 'update': {
|
||||
const { unitId, sheetId, row, col, note } = option;
|
||||
const id = this._getIdWithUnitId(unitId, sheetId, row, col);
|
||||
if (note) {
|
||||
if (!this._disposableMap.has(id)) {
|
||||
this._register(unitId, sheetId, note, row, col);
|
||||
this._watch(unitId, sheetId, note, row, col);
|
||||
}
|
||||
} else {
|
||||
this._unregister(unitId, sheetId, row, col);
|
||||
this._unwatch(unitId, sheetId, row, col);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'ref': {
|
||||
const { unitId, sheetId, row, col, newPosition, note, silent } = option;
|
||||
this._unregister(unitId, sheetId, row, col);
|
||||
|
||||
if (!silent) {
|
||||
this._unwatch(unitId, sheetId, row, col);
|
||||
this._watch(unitId, sheetId, note, newPosition.row, newPosition.col);
|
||||
}
|
||||
|
||||
this._register(unitId, sheetId, note, newPosition.row, newPosition.col);
|
||||
break;
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/**
|
||||
* 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 { ISheetNote } from '../models/sheets-note.model';
|
||||
import { Disposable, Inject, IResourceManagerService, IUniverInstanceService, UniverInstanceType } from '@univerjs/core';
|
||||
import { SheetsNoteModel } from '../models/sheets-note.model';
|
||||
import { PLUGIN_NAME } from '../plugin';
|
||||
|
||||
interface INoteData {
|
||||
[sheetId: string]: {
|
||||
[row: number]: {
|
||||
[col: number]: ISheetNote;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export class SheetsNoteResourceController extends Disposable {
|
||||
constructor(
|
||||
@IResourceManagerService private readonly _resourceManagerService: IResourceManagerService,
|
||||
@IUniverInstanceService private readonly _univerInstanceService: IUniverInstanceService,
|
||||
@Inject(SheetsNoteModel) private readonly _sheetsNoteModel: SheetsNoteModel
|
||||
) {
|
||||
super();
|
||||
this._initSnapshot();
|
||||
}
|
||||
|
||||
private _initSnapshot() {
|
||||
const toJson = (unitId: string) => {
|
||||
const unitMap = this._sheetsNoteModel.getUnitNotes(unitId);
|
||||
if (!unitMap) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const result: INoteData = {};
|
||||
unitMap.forEach((matrix, sheetId) => {
|
||||
const sheetNotes: INoteData[string] = {};
|
||||
matrix.forValue((row, col, note) => {
|
||||
if (!sheetNotes[row]) {
|
||||
sheetNotes[row] = {};
|
||||
}
|
||||
sheetNotes[row][col] = note;
|
||||
});
|
||||
if (Object.keys(sheetNotes).length > 0) {
|
||||
result[sheetId] = sheetNotes;
|
||||
}
|
||||
});
|
||||
|
||||
return JSON.stringify(result);
|
||||
};
|
||||
|
||||
const parseJson = (json: string): INoteData => {
|
||||
if (!json) {
|
||||
return {};
|
||||
}
|
||||
try {
|
||||
return JSON.parse(json);
|
||||
} catch (err) {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
this.disposeWithMe(
|
||||
this._resourceManagerService.registerPluginResource<INoteData>({
|
||||
pluginName: PLUGIN_NAME,
|
||||
businesses: [UniverInstanceType.UNIVER_SHEET],
|
||||
toJson: (unitId) => toJson(unitId),
|
||||
parseJson: (json) => parseJson(json),
|
||||
onUnLoad: (unitId) => {
|
||||
// Clear all notes for this unit
|
||||
this._sheetsNoteModel.deleteUnitNotes(unitId);
|
||||
},
|
||||
onLoad: (unitId, value) => {
|
||||
// Load notes from the parsed data
|
||||
Object.entries(value).forEach(([sheetId, sheetNotes]) => {
|
||||
Object.entries(sheetNotes).forEach(([row, colNotes]) => {
|
||||
Object.entries(colNotes).forEach(([col, note]) => {
|
||||
this._sheetsNoteModel.updateNote(
|
||||
unitId,
|
||||
sheetId,
|
||||
Number(row),
|
||||
Number(col),
|
||||
note
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* 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 { Disposable, ICommandService } from '@univerjs/core';
|
||||
import { SheetDeleteNoteCommand, SheetToggleNotePopupCommand, SheetUpdateNoteCommand } from '../commands/commands/note.command';
|
||||
import { RemoveNoteMutation, ToggleNotePopupMutation, UpdateNoteMutation, UpdateNotePositionMutation } from '../commands/mutations/note.mutation';
|
||||
|
||||
export class SheetsNoteController extends Disposable {
|
||||
constructor(
|
||||
@ICommandService private readonly _commandService: ICommandService
|
||||
) {
|
||||
super();
|
||||
|
||||
this._initialize();
|
||||
}
|
||||
|
||||
private _initialize(): void {
|
||||
[
|
||||
UpdateNotePositionMutation,
|
||||
ToggleNotePopupMutation,
|
||||
UpdateNoteMutation,
|
||||
RemoveNoteMutation,
|
||||
SheetDeleteNoteCommand,
|
||||
SheetToggleNotePopupCommand,
|
||||
SheetUpdateNoteCommand,
|
||||
].forEach((command) => {
|
||||
this.disposeWithMe(
|
||||
this._commandService.registerCommand(command)
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
/**
|
||||
* 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 { ISheetNote } from '@univerjs/sheets-note';
|
||||
import type { FWorkbook, FWorksheet } from '@univerjs/sheets/facade';
|
||||
import { FEventName } from '@univerjs/core/facade';
|
||||
|
||||
export interface ISheetNoteAddEventParmas {
|
||||
workbook: FWorkbook;
|
||||
worksheet: FWorksheet;
|
||||
row: number;
|
||||
col: number;
|
||||
note: ISheetNote;
|
||||
cancel?: boolean;
|
||||
}
|
||||
|
||||
export interface ISheetNoteDeleteEventParmas {
|
||||
workbook: FWorkbook;
|
||||
worksheet: FWorksheet;
|
||||
row: number;
|
||||
col: number;
|
||||
oldNote: ISheetNote;
|
||||
cancel?: boolean;
|
||||
}
|
||||
|
||||
export interface ISheetNoteUpdateEventParmas {
|
||||
workbook: FWorkbook;
|
||||
worksheet: FWorksheet;
|
||||
row: number;
|
||||
col: number;
|
||||
note: ISheetNote;
|
||||
oldNote: ISheetNote;
|
||||
cancel?: boolean;
|
||||
}
|
||||
|
||||
export interface ISheetNoteShowEventParmas {
|
||||
workbook: FWorkbook;
|
||||
worksheet: FWorksheet;
|
||||
row: number;
|
||||
col: number;
|
||||
cancel?: boolean;
|
||||
}
|
||||
|
||||
export interface ISheetNoteHideEventParmas {
|
||||
workbook: FWorkbook;
|
||||
worksheet: FWorksheet;
|
||||
row: number;
|
||||
col: number;
|
||||
cancel?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
export interface ISheetsNoteEventParamConfig {
|
||||
SheetNoteAdd: ISheetNoteAddEventParmas;
|
||||
SheetNoteDelete: ISheetNoteDeleteEventParmas;
|
||||
SheetNoteUpdate: ISheetNoteUpdateEventParmas;
|
||||
SheetNoteShow: ISheetNoteShowEventParmas;
|
||||
SheetNoteHide: ISheetNoteHideEventParmas;
|
||||
|
||||
BeforeSheetNoteAdd: ISheetNoteAddEventParmas;
|
||||
BeforeSheetNoteDelete: ISheetNoteDeleteEventParmas;
|
||||
BeforeSheetNoteUpdate: ISheetNoteUpdateEventParmas;
|
||||
BeforeSheetNoteShow: ISheetNoteShowEventParmas;
|
||||
BeforeSheetNoteHide: ISheetNoteHideEventParmas;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
interface ISheetNoteEvent {
|
||||
/**
|
||||
* Event fired when a note is added
|
||||
* @see {@link ISheetNoteAddEventParmas}
|
||||
* @example
|
||||
* ```ts
|
||||
* const disposable = univerAPI.addEvent(univerAPI.Event.SheetNoteAdd, (params) => {
|
||||
* const { workbook, worksheet, row, col, note } = params;
|
||||
* console.log(params);
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
SheetNoteAdd: 'SheetNoteAdd';
|
||||
/**
|
||||
* Event fired when a note is deleted
|
||||
* @see {@link ISheetNoteDeleteEventParmas}
|
||||
* @example
|
||||
* ```ts
|
||||
* const disposable = univerAPI.addEvent(univerAPI.Event.SheetNoteDelete, (params) => {
|
||||
* const { workbook, worksheet, row, col, oldNote } = params;
|
||||
* console.log(params);
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
SheetNoteDelete: 'SheetNoteDelete';
|
||||
/**
|
||||
* Event fired when a note is updated
|
||||
* @see {@link ISheetNoteUpdateEventParmas}
|
||||
* @example
|
||||
* ```ts
|
||||
* const disposable = univerAPI.addEvent(univerAPI.Event.SheetNoteUpdate, (params) => {
|
||||
* const { workbook, worksheet, row, col, note, oldNote } = params;
|
||||
* console.log(params);
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
SheetNoteUpdate: 'SheetNoteUpdate';
|
||||
/**
|
||||
* Event fired when a note is shown
|
||||
* @see {@link ISheetNoteShowEventParmas}
|
||||
* @example
|
||||
* ```ts
|
||||
* const disposable = univerAPI.addEvent(univerAPI.Event.SheetNoteShow, (params) => {
|
||||
* const { workbook, worksheet, row, col } = params;
|
||||
* console.log(params);
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
SheetNoteShow: 'SheetNoteShow';
|
||||
/**
|
||||
* Event fired when a note is hidden
|
||||
* @see {@link ISheetNoteHideEventParmas}
|
||||
* @example
|
||||
* ```ts
|
||||
* const disposable = univerAPI.addEvent(univerAPI.Event.SheetNoteHide, (params) => {
|
||||
* const { workbook, worksheet, row, col } = params;
|
||||
* console.log(params);
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
SheetNoteHide: 'SheetNoteHide';
|
||||
|
||||
/**
|
||||
* Event fired before a note is added
|
||||
* @see {@link ISheetNoteAddEventParmas}
|
||||
* @example
|
||||
* ```ts
|
||||
* const disposable = univerAPI.addEvent(univerAPI.Event.BeforeSheetNoteAdd, (params) => {
|
||||
* const { workbook, worksheet, row, col, note } = params;
|
||||
* console.log(params);
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
BeforeSheetNoteAdd: 'BeforeSheetNoteAdd';
|
||||
/**
|
||||
* Event fired before a note is deleted
|
||||
* @see {@link ISheetNoteDeleteEventParmas}
|
||||
* @example
|
||||
* ```ts
|
||||
* const disposable = univerAPI.addEvent(univerAPI.Event.BeforeSheetNoteDelete, (params) => {
|
||||
* const { workbook, worksheet, row, col, oldNote } = params;
|
||||
* console.log(params);
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
BeforeSheetNoteDelete: 'BeforeSheetNoteDelete';
|
||||
/**
|
||||
* Event fired before a note is updated
|
||||
* @see {@link ISheetNoteUpdateEventParmas}
|
||||
* @example
|
||||
* ```ts
|
||||
* const disposable = univerAPI.addEvent(univerAPI.Event.BeforeSheetNoteUpdate, (params) => {
|
||||
* const { workbook, worksheet, row, col, note, oldNote } = params;
|
||||
* console.log(params);
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
BeforeSheetNoteUpdate: 'BeforeSheetNoteUpdate';
|
||||
/**
|
||||
* Event fired before a note is shown
|
||||
* @see {@link ISheetNoteShowEventParmas}
|
||||
* @example
|
||||
* ```ts
|
||||
* const disposable = univerAPI.addEvent(univerAPI.Event.BeforeSheetNoteShow, (params) => {
|
||||
* const { workbook, worksheet, row, col } = params;
|
||||
* console.log(params);
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
BeforeSheetNoteShow: 'BeforeSheetNoteShow';
|
||||
/**
|
||||
* Event fired before a note is hidden
|
||||
* @see {@link ISheetNoteHideEventParmas}
|
||||
* @example
|
||||
* ```ts
|
||||
* const disposable = univerAPI.addEvent(univerAPI.Event.BeforeSheetNoteHide, (params) => {
|
||||
* const { workbook, worksheet, row, col } = params;
|
||||
* console.log(params);
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
BeforeSheetNoteHide: 'BeforeSheetNoteHide';
|
||||
}
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
export class FSheetNoteEvent implements ISheetNoteEvent {
|
||||
get SheetNoteAdd(): 'SheetNoteAdd' {
|
||||
return 'SheetNoteAdd';
|
||||
}
|
||||
|
||||
get SheetNoteDelete(): 'SheetNoteDelete' {
|
||||
return 'SheetNoteDelete';
|
||||
}
|
||||
|
||||
get SheetNoteUpdate(): 'SheetNoteUpdate' {
|
||||
return 'SheetNoteUpdate';
|
||||
}
|
||||
|
||||
get SheetNoteShow(): 'SheetNoteShow' {
|
||||
return 'SheetNoteShow';
|
||||
}
|
||||
|
||||
get SheetNoteHide(): 'SheetNoteHide' {
|
||||
return 'SheetNoteHide';
|
||||
}
|
||||
|
||||
get BeforeSheetNoteAdd(): 'BeforeSheetNoteAdd' {
|
||||
return 'BeforeSheetNoteAdd';
|
||||
}
|
||||
|
||||
get BeforeSheetNoteDelete(): 'BeforeSheetNoteDelete' {
|
||||
return 'BeforeSheetNoteDelete';
|
||||
}
|
||||
|
||||
get BeforeSheetNoteUpdate(): 'BeforeSheetNoteUpdate' {
|
||||
return 'BeforeSheetNoteUpdate';
|
||||
}
|
||||
|
||||
get BeforeSheetNoteShow(): 'BeforeSheetNoteShow' {
|
||||
return 'BeforeSheetNoteShow';
|
||||
}
|
||||
|
||||
get BeforeSheetNoteHide(): 'BeforeSheetNoteHide' {
|
||||
return 'BeforeSheetNoteHide';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
export interface ISheetNoteEventConfig {
|
||||
SheetNoteAdd: ISheetNoteAddEventParmas;
|
||||
SheetNoteDelete: ISheetNoteDeleteEventParmas;
|
||||
SheetNoteUpdate: ISheetNoteUpdateEventParmas;
|
||||
SheetNoteShow: ISheetNoteShowEventParmas;
|
||||
SheetNoteHide: ISheetNoteHideEventParmas;
|
||||
|
||||
BeforeSheetNoteAdd: ISheetNoteAddEventParmas;
|
||||
BeforeSheetNoteDelete: ISheetNoteDeleteEventParmas;
|
||||
BeforeSheetNoteUpdate: ISheetNoteUpdateEventParmas;
|
||||
BeforeSheetNoteShow: ISheetNoteShowEventParmas;
|
||||
BeforeSheetNoteHide: ISheetNoteHideEventParmas;
|
||||
}
|
||||
|
||||
FEventName.extend(FSheetNoteEvent);
|
||||
declare module '@univerjs/core/facade' {
|
||||
// eslint-disable-next-line ts/naming-convention
|
||||
interface FEventName extends ISheetNoteEvent {
|
||||
}
|
||||
|
||||
interface IEventParamConfig extends ISheetNoteEventConfig {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* 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 { Nullable } from '@univerjs/core';
|
||||
import type { ISheetNote } from '@univerjs/sheets-note';
|
||||
import { RemoveNoteMutation, SheetsNoteModel, UpdateNoteMutation } from '@univerjs/sheets-note';
|
||||
import { FRange } from '@univerjs/sheets/facade';
|
||||
|
||||
export interface IFSheetsNoteRange {
|
||||
/**
|
||||
* get the note of the primary cell
|
||||
* @returns {Nullable<ISheetNote>} the note of the primary cell
|
||||
* @example
|
||||
* ```ts
|
||||
* const fWorkbook = univerAPI.getWorkbook();
|
||||
* const fWorksheet = fWorkbook.getWorksheet();
|
||||
* const fRange = fWorksheet.getRange(0, 0, 1, 1);
|
||||
* const note = fRange.getNote();
|
||||
* ```
|
||||
*/
|
||||
getNote(): Nullable<ISheetNote>;
|
||||
/**
|
||||
* create a new note and attach to the primary cell
|
||||
* @param {ISheetNote} note the note to create
|
||||
* @returns {FRange} the range
|
||||
* @example
|
||||
* ```ts
|
||||
* const fWorkbook = univerAPI.getWorkbook();
|
||||
* const fWorksheet = fWorkbook.getWorksheet();
|
||||
* const fRange = fWorksheet.getRange(0, 0, 1, 1);
|
||||
* const note = fRange.createOrUpdateNote({});
|
||||
* ```
|
||||
*/
|
||||
createOrUpdateNote(note: ISheetNote): FRange;
|
||||
/**
|
||||
* delete the note of the primary cell
|
||||
* @returns {FRange} the range
|
||||
* @example
|
||||
* ```ts
|
||||
* const fWorkbook = univerAPI.getWorkbook();
|
||||
* const fWorksheet = fWorkbook.getWorksheet();
|
||||
* const fRange = fWorksheet.getRange(0, 0, 1, 1);
|
||||
* fRange.deleteNote();
|
||||
* ```
|
||||
*/
|
||||
deleteNote(): FRange;
|
||||
}
|
||||
|
||||
export class FSheetsNoteRange extends FRange implements IFSheetsNoteRange {
|
||||
createOrUpdateNote(note: ISheetNote): FRange {
|
||||
this._commandService.syncExecuteCommand(
|
||||
UpdateNoteMutation.id,
|
||||
{
|
||||
unitId: this.getUnitId(),
|
||||
sheetId: this.getSheetId(),
|
||||
row: this.getRow(),
|
||||
col: this.getColumn(),
|
||||
note,
|
||||
}
|
||||
);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
deleteNote(): FRange {
|
||||
const model = this._injector.get(SheetsNoteModel);
|
||||
this._commandService.syncExecuteCommand(
|
||||
RemoveNoteMutation.id,
|
||||
{
|
||||
unitId: this.getUnitId(),
|
||||
sheetId: this.getSheetId(),
|
||||
row: this.getRow(),
|
||||
col: this.getColumn(),
|
||||
}
|
||||
);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
getNote(): Nullable<ISheetNote> {
|
||||
const model = this._injector.get(SheetsNoteModel);
|
||||
return model.getNote(this.getUnitId(), this.getSheetId(), this.getRow(), this.getColumn());
|
||||
}
|
||||
}
|
||||
|
||||
declare module '@univerjs/sheets/facade' {
|
||||
interface IRange extends FSheetsNoteRange { }
|
||||
}
|
||||
|
||||
FRange.extend(FSheetsNoteRange);
|
||||
@@ -0,0 +1,311 @@
|
||||
/**
|
||||
* 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 { Injector } from '@univerjs/core';
|
||||
import type { IRemoveNoteMutationParams, IUpdateNoteMutationParams } from '@univerjs/sheets-note';
|
||||
import { CanceledError, ICommandService } from '@univerjs/core';
|
||||
import { FUniver } from '@univerjs/core/facade';
|
||||
import { SheetDeleteNoteCommand, SheetsNoteModel, SheetToggleNotePopupCommand, SheetUpdateNoteCommand } from '@univerjs/sheets-note';
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
export interface IFUniverSheetNoteMixin {
|
||||
// Add any note-specific methods here if needed
|
||||
}
|
||||
|
||||
export class FUniverSheetNoteMixin extends FUniver implements IFUniverSheetNoteMixin {
|
||||
override _initialize(injector: Injector): void {
|
||||
this.registerEventHandler(
|
||||
this.Event.SheetNoteAdd,
|
||||
() => {
|
||||
const model = injector.get(SheetsNoteModel);
|
||||
return model.change$.subscribe((change) => {
|
||||
if (change.type === 'update' && !change.oldNote && change.note) {
|
||||
const { unitId, sheetId, row, col, note, oldNote } = change;
|
||||
const target = this.getSheetTarget(unitId, sheetId);
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
const { workbook, worksheet } = target;
|
||||
|
||||
this.fireEvent(this.Event.SheetNoteAdd, {
|
||||
workbook,
|
||||
worksheet,
|
||||
row,
|
||||
col,
|
||||
note,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
this.registerEventHandler(
|
||||
this.Event.SheetNoteDelete,
|
||||
() => {
|
||||
const model = injector.get(SheetsNoteModel);
|
||||
return model.change$.subscribe((change) => {
|
||||
if (change.type === 'update' && change.oldNote && !change.note) {
|
||||
const { unitId, sheetId, row, col, note, oldNote } = change;
|
||||
const target = this.getSheetTarget(unitId, sheetId);
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
const { workbook, worksheet } = target;
|
||||
|
||||
this.fireEvent(this.Event.SheetNoteDelete, {
|
||||
workbook,
|
||||
worksheet,
|
||||
row,
|
||||
col,
|
||||
oldNote,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
this.registerEventHandler(
|
||||
this.Event.SheetNoteUpdate,
|
||||
() => {
|
||||
const model = injector.get(SheetsNoteModel);
|
||||
return model.change$.subscribe((change) => {
|
||||
if (change.type === 'update' && change.oldNote && change.note) {
|
||||
const { unitId, sheetId, row, col, note, oldNote } = change;
|
||||
const target = this.getSheetTarget(unitId, sheetId);
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
const { workbook, worksheet } = target;
|
||||
|
||||
this.fireEvent(this.Event.SheetNoteUpdate, {
|
||||
workbook,
|
||||
worksheet,
|
||||
row,
|
||||
col,
|
||||
note,
|
||||
oldNote,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
this.registerEventHandler(
|
||||
this.Event.SheetNoteShow,
|
||||
() => {
|
||||
const model = injector.get(SheetsNoteModel);
|
||||
return model.change$.subscribe((change) => {
|
||||
if (change.type === 'update' && change.oldNote && change.note && !change.oldNote.show && change.note.show) {
|
||||
const { unitId, sheetId, row, col } = change;
|
||||
const target = this.getSheetTarget(unitId, sheetId);
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
const { workbook, worksheet } = target;
|
||||
|
||||
this.fireEvent(this.Event.SheetNoteShow, {
|
||||
workbook,
|
||||
worksheet,
|
||||
row,
|
||||
col,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
this.registerEventHandler(
|
||||
this.Event.SheetNoteHide,
|
||||
() => {
|
||||
const model = injector.get(SheetsNoteModel);
|
||||
return model.change$.subscribe((change) => {
|
||||
if (change.type === 'update' && change.oldNote && change.note && change.oldNote.show && !change.note.show) {
|
||||
const { unitId, sheetId, row, col } = change;
|
||||
const target = this.getSheetTarget(unitId, sheetId);
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
const { workbook, worksheet } = target;
|
||||
|
||||
this.fireEvent(this.Event.SheetNoteHide, {
|
||||
workbook,
|
||||
worksheet,
|
||||
row,
|
||||
col,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
this.registerEventHandler(
|
||||
this.Event.BeforeSheetNoteAdd,
|
||||
() => {
|
||||
const commandService = injector.get(ICommandService);
|
||||
return commandService.beforeCommandExecuted((command) => {
|
||||
if (command.id === SheetUpdateNoteCommand.id) {
|
||||
const model = injector.get(SheetsNoteModel);
|
||||
const { unitId, sheetId, row, col, note } = command.params as IUpdateNoteMutationParams;
|
||||
const oldNote = model.getNote(unitId, sheetId, row, col);
|
||||
if (oldNote) return;
|
||||
const target = this.getSheetTarget(unitId, sheetId);
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
const { workbook, worksheet } = target;
|
||||
const cancel = this.fireEvent(this.Event.BeforeSheetNoteAdd, {
|
||||
workbook,
|
||||
worksheet,
|
||||
row,
|
||||
col,
|
||||
note,
|
||||
});
|
||||
if (cancel) {
|
||||
throw new CanceledError();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
this.registerEventHandler(
|
||||
this.Event.BeforeSheetNoteDelete,
|
||||
() => {
|
||||
const commandService = injector.get(ICommandService);
|
||||
return commandService.beforeCommandExecuted((command) => {
|
||||
if (command.id === SheetDeleteNoteCommand.id) {
|
||||
const model = injector.get(SheetsNoteModel);
|
||||
const { unitId, sheetId, row, col } = command.params as IRemoveNoteMutationParams;
|
||||
const oldNote = model.getNote(unitId, sheetId, row, col);
|
||||
if (!oldNote) return;
|
||||
const target = this.getSheetTarget(unitId, sheetId);
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
const { workbook, worksheet } = target;
|
||||
const cancel = this.fireEvent(this.Event.BeforeSheetNoteDelete, {
|
||||
workbook,
|
||||
worksheet,
|
||||
row,
|
||||
col,
|
||||
oldNote,
|
||||
});
|
||||
if (cancel) {
|
||||
throw new CanceledError();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
this.registerEventHandler(
|
||||
this.Event.BeforeSheetNoteUpdate,
|
||||
() => {
|
||||
const commandService = injector.get(ICommandService);
|
||||
return commandService.beforeCommandExecuted((command) => {
|
||||
if (command.id === SheetUpdateNoteCommand.id) {
|
||||
const model = injector.get(SheetsNoteModel);
|
||||
const { unitId, sheetId, row, col, note } = command.params as IUpdateNoteMutationParams;
|
||||
const oldNote = model.getNote(unitId, sheetId, row, col);
|
||||
if (!oldNote) return;
|
||||
const target = this.getSheetTarget(unitId, sheetId);
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
const { workbook, worksheet } = target;
|
||||
const cancel = this.fireEvent(this.Event.BeforeSheetNoteUpdate, {
|
||||
workbook,
|
||||
worksheet,
|
||||
row,
|
||||
col,
|
||||
note,
|
||||
oldNote,
|
||||
});
|
||||
if (cancel) {
|
||||
throw new CanceledError();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
this.registerEventHandler(
|
||||
this.Event.BeforeSheetNoteShow,
|
||||
() => {
|
||||
const commandService = injector.get(ICommandService);
|
||||
return commandService.beforeCommandExecuted((command) => {
|
||||
if (command.id === SheetToggleNotePopupCommand.id) {
|
||||
const model = injector.get(SheetsNoteModel);
|
||||
const { unitId, sheetId, row, col } = command.params as IUpdateNoteMutationParams;
|
||||
const oldNote = model.getNote(unitId, sheetId, row, col);
|
||||
if (oldNote?.show) return;
|
||||
const target = this.getSheetTarget(unitId, sheetId);
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
const { workbook, worksheet } = target;
|
||||
const cancel = this.fireEvent(this.Event.BeforeSheetNoteShow, {
|
||||
workbook,
|
||||
worksheet,
|
||||
row,
|
||||
col,
|
||||
});
|
||||
if (cancel) {
|
||||
throw new CanceledError();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
this.registerEventHandler(
|
||||
this.Event.BeforeSheetNoteHide,
|
||||
() => {
|
||||
const commandService = injector.get(ICommandService);
|
||||
return commandService.beforeCommandExecuted((command) => {
|
||||
if (command.id === SheetToggleNotePopupCommand.id) {
|
||||
const model = injector.get(SheetsNoteModel);
|
||||
const { unitId, sheetId, row, col } = command.params as IUpdateNoteMutationParams;
|
||||
const oldNote = model.getNote(unitId, sheetId, row, col);
|
||||
if (!oldNote?.show) return;
|
||||
const target = this.getSheetTarget(unitId, sheetId);
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
const { workbook, worksheet } = target;
|
||||
const cancel = this.fireEvent(this.Event.BeforeSheetNoteHide, {
|
||||
workbook,
|
||||
worksheet,
|
||||
row,
|
||||
col,
|
||||
});
|
||||
if (cancel) {
|
||||
throw new CanceledError();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
FUniver.extend(FUniverSheetNoteMixin);
|
||||
declare module '@univerjs/core/facade' {
|
||||
// eslint-disable-next-line ts/naming-convention
|
||||
interface FUniver extends IFUniverSheetNoteMixin {}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* 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 { ISheetNote } from '@univerjs/sheets-note';
|
||||
import { SheetsNoteModel } from '@univerjs/sheets-note';
|
||||
import { FWorksheet } from '@univerjs/sheets/facade';
|
||||
|
||||
export interface ISheetNoteInfo extends ISheetNote {
|
||||
row: number;
|
||||
col: number;
|
||||
}
|
||||
|
||||
export interface IFSheetsNoteWorksheet {
|
||||
/**
|
||||
* get the note of the primary cell
|
||||
*/
|
||||
getNotes(): ISheetNoteInfo[];
|
||||
}
|
||||
|
||||
export class FSheetsNoteWorksheet extends FWorksheet implements IFSheetsNoteWorksheet {
|
||||
getNotes(): ISheetNoteInfo[] {
|
||||
const model = this._injector.get(SheetsNoteModel);
|
||||
const notes = model.getSheetNotes(this.getWorkbook().getUnitId(), this.getSheetId());
|
||||
const arr: ISheetNoteInfo[] = [];
|
||||
|
||||
notes?.forValue((row, col, note) => {
|
||||
arr.push({
|
||||
...note,
|
||||
row,
|
||||
col,
|
||||
});
|
||||
});
|
||||
|
||||
return arr;
|
||||
}
|
||||
}
|
||||
|
||||
declare module '@univerjs/sheets/facade' {
|
||||
interface IWorksheet extends FSheetsNoteWorksheet { }
|
||||
}
|
||||
|
||||
FWorksheet.extend(FSheetsNoteWorksheet);
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* 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 './f-event';
|
||||
import './f-range';
|
||||
import './f-univer';
|
||||
import './f-worksheet';
|
||||
|
||||
export * from './f-event';
|
||||
export * from './f-range';
|
||||
export * from './f-univer';
|
||||
export * from './f-worksheet';
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export * from './plugin';
|
||||
export * from './models/sheets-note.model';
|
||||
export * from './controllers/sheets-note-resource.controller';
|
||||
export * from './commands/mutations/note.mutation';
|
||||
export * from './commands/commands/note.command';
|
||||
@@ -0,0 +1,171 @@
|
||||
/**
|
||||
* 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 { Nullable } from '@univerjs/core';
|
||||
import type { ISheetLocationBase } from '@univerjs/sheets';
|
||||
import { Disposable, ObjectMatrix } from '@univerjs/core';
|
||||
import { filter, map, Subject } from 'rxjs';
|
||||
|
||||
export interface ISheetNote {
|
||||
width: number;
|
||||
height: number;
|
||||
note: string;
|
||||
show?: boolean;
|
||||
}
|
||||
|
||||
export type ISheetNoteChange = {
|
||||
unitId: string;
|
||||
sheetId: string;
|
||||
row: number;
|
||||
col: number;
|
||||
silent?: boolean;
|
||||
} & ({
|
||||
type: 'update';
|
||||
note: Nullable<ISheetNote>;
|
||||
oldNote: Nullable<ISheetNote>;
|
||||
} | {
|
||||
type: 'ref';
|
||||
newPosition: {
|
||||
row: number;
|
||||
col: number;
|
||||
};
|
||||
note: ISheetNote;
|
||||
});
|
||||
|
||||
export class SheetsNoteModel extends Disposable {
|
||||
private _noteMatrix = new Map<string, Map<string, ObjectMatrix<ISheetNote>>>();
|
||||
private readonly _change$ = new Subject<ISheetNoteChange>();
|
||||
readonly change$ = this._change$.asObservable();
|
||||
|
||||
private _ensureNoteMatrix(unitId: string, sheetId: string): ObjectMatrix<ISheetNote> {
|
||||
let unitMap = this._noteMatrix.get(unitId);
|
||||
if (!unitMap) {
|
||||
unitMap = new Map();
|
||||
this._noteMatrix.set(unitId, unitMap);
|
||||
}
|
||||
|
||||
let matrix = unitMap.get(sheetId);
|
||||
if (!matrix) {
|
||||
matrix = new ObjectMatrix<ISheetNote>();
|
||||
unitMap.set(sheetId, matrix);
|
||||
}
|
||||
|
||||
return matrix;
|
||||
}
|
||||
|
||||
getSheetShowNotes$(unitId: string, sheetId: string) {
|
||||
return this._change$.pipe(
|
||||
filter(({ unitId: u, sheetId: s }) => u === unitId && s === sheetId),
|
||||
map(() => {
|
||||
const matrix = this._ensureNoteMatrix(unitId, sheetId);
|
||||
const notes: { loc: ISheetLocationBase; note: ISheetNote }[] = [];
|
||||
|
||||
matrix.forValue((row, col, note) => {
|
||||
if (note.show) {
|
||||
notes.push({ loc: { row, col, unitId, subUnitId: sheetId }, note });
|
||||
}
|
||||
});
|
||||
|
||||
return notes;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
getCellNoteChange$(unitId: string, sheetId: string, row: number, col: number) {
|
||||
return this._change$.pipe(
|
||||
filter(({ unitId: u, sheetId: s, row: r, col: c }) => u === unitId && s === sheetId && r === row && c === col),
|
||||
map(({ note }) => note)
|
||||
);
|
||||
}
|
||||
|
||||
updateNote(unitId: string, sheetId: string, row: number, col: number, note: ISheetNote, silent?: boolean): void {
|
||||
const matrix = this._ensureNoteMatrix(unitId, sheetId);
|
||||
const oldNote = matrix.getValue(row, col);
|
||||
matrix.setValue(row, col, note);
|
||||
this._change$.next({ unitId, sheetId, row, col, type: 'update', note, oldNote, silent });
|
||||
}
|
||||
|
||||
removeNote(unitId: string, sheetId: string, row: number, col: number, silent?: boolean): void {
|
||||
const matrix = this._ensureNoteMatrix(unitId, sheetId);
|
||||
const oldNote = matrix.getValue(row, col);
|
||||
matrix.realDeleteValue(row, col);
|
||||
this._change$.next({ unitId, sheetId, row, col, type: 'update', note: null, oldNote, silent });
|
||||
}
|
||||
|
||||
toggleNotePopup(unitId: string, sheetId: string, row: number, col: number, silent?: boolean): void {
|
||||
const matrix = this._ensureNoteMatrix(unitId, sheetId);
|
||||
const note = matrix.getValue(row, col);
|
||||
if (note) {
|
||||
note.show = !note.show;
|
||||
const newNote = { ...note, show: note.show };
|
||||
matrix.setValue(row, col, newNote);
|
||||
this._change$.next({
|
||||
unitId,
|
||||
sheetId,
|
||||
row,
|
||||
col,
|
||||
type: 'update',
|
||||
note: newNote,
|
||||
oldNote: note,
|
||||
silent,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
updateNotePosition(unitId: string, sheetId: string, row: number, col: number, newRow: number, newCol: number, silent?: boolean): void {
|
||||
const matrix = this._ensureNoteMatrix(unitId, sheetId);
|
||||
const note = matrix.getValue(row, col);
|
||||
if (note) {
|
||||
matrix.realDeleteValue(row, col);
|
||||
matrix.setValue(newRow, newCol, note);
|
||||
this._change$.next({
|
||||
unitId,
|
||||
sheetId,
|
||||
row,
|
||||
col,
|
||||
type: 'ref',
|
||||
newPosition: { row: newRow, col: newCol },
|
||||
note,
|
||||
silent,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getNote(unitId: string, sheetId: string, row: number, col: number): Nullable<ISheetNote> {
|
||||
const matrix = this._ensureNoteMatrix(unitId, sheetId);
|
||||
return matrix.getValue(row, col);
|
||||
}
|
||||
|
||||
getUnitNotes(unitId: string): Map<string, ObjectMatrix<ISheetNote>> | undefined {
|
||||
return this._noteMatrix.get(unitId);
|
||||
}
|
||||
|
||||
getSheetNotes(unitId: string, sheetId: string): ObjectMatrix<ISheetNote> | undefined {
|
||||
const unitMap = this._noteMatrix.get(unitId);
|
||||
if (!unitMap) {
|
||||
return undefined;
|
||||
}
|
||||
return unitMap.get(sheetId);
|
||||
}
|
||||
|
||||
getNotes() {
|
||||
return this._noteMatrix;
|
||||
}
|
||||
|
||||
deleteUnitNotes(unitId: string): void {
|
||||
this._noteMatrix.delete(unitId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* 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 { Dependency } from '@univerjs/core';
|
||||
import { DependentOn, IConfigService, Inject, Injector, Plugin, touchDependencies, UniverInstanceType } from '@univerjs/core';
|
||||
import { UniverSheetsPlugin } from '@univerjs/sheets';
|
||||
import { SheetsNoteRefRangeController } from './controllers/sheets-note-ref-range.controller';
|
||||
import { SheetsNoteResourceController } from './controllers/sheets-note-resource.controller';
|
||||
import { SheetsNoteController } from './controllers/sheets.note.controller';
|
||||
import { SheetsNoteModel } from './models/sheets-note.model';
|
||||
|
||||
export const PLUGIN_NAME = 'SHEET_NOTE_PLUGIN';
|
||||
|
||||
@DependentOn(UniverSheetsPlugin)
|
||||
export class SheetsNotePlugin extends Plugin {
|
||||
static override pluginName = PLUGIN_NAME;
|
||||
static override type = UniverInstanceType.UNIVER_SHEET;
|
||||
|
||||
constructor(
|
||||
@Inject(Injector) protected override readonly _injector: Injector,
|
||||
@IConfigService private readonly _configService: IConfigService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
override onStarting(): void {
|
||||
([
|
||||
[SheetsNoteModel],
|
||||
[SheetsNoteController],
|
||||
[SheetsNoteResourceController],
|
||||
[SheetsNoteRefRangeController],
|
||||
] as Dependency[]).forEach((dependency) => {
|
||||
this._injector.add(dependency);
|
||||
});
|
||||
|
||||
touchDependencies(this._injector, [
|
||||
[SheetsNoteModel],
|
||||
[SheetsNoteController],
|
||||
[SheetsNoteResourceController],
|
||||
]);
|
||||
}
|
||||
|
||||
override onReady(): void {
|
||||
touchDependencies(this._injector, [
|
||||
[SheetsNoteRefRangeController],
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "@univerjs-infra/shared/tsconfigs/base",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"outDir": "lib/types"
|
||||
},
|
||||
"references": [{ "path": "./tsconfig.node.json" }],
|
||||
"include": ["src"]
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "@univerjs-infra/shared/tsconfigs/node",
|
||||
"include": ["vitest.config.ts"]
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import createConfig from '@univerjs-infra/shared/vitest';
|
||||
|
||||
export default createConfig();
|
||||
@@ -73,7 +73,7 @@
|
||||
"@univerjs/engine-formula": "workspace:*",
|
||||
"@univerjs/engine-numfmt": "workspace:*",
|
||||
"@univerjs/engine-render": "workspace:*",
|
||||
"@univerjs/icons": "^0.3.22",
|
||||
"@univerjs/icons": "^0.3.24",
|
||||
"@univerjs/sheets": "workspace:*",
|
||||
"@univerjs/sheets-numfmt": "workspace:*",
|
||||
"@univerjs/sheets-ui": "workspace:*",
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
"@univerjs/core": "workspace:*",
|
||||
"@univerjs/design": "workspace:*",
|
||||
"@univerjs/engine-formula": "workspace:*",
|
||||
"@univerjs/icons": "^0.3.22",
|
||||
"@univerjs/icons": "^0.3.24",
|
||||
"@univerjs/sheets": "workspace:*",
|
||||
"@univerjs/sheets-sort": "workspace:*",
|
||||
"@univerjs/sheets-ui": "workspace:*",
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
"@univerjs/design": "workspace:*",
|
||||
"@univerjs/engine-formula": "workspace:*",
|
||||
"@univerjs/engine-render": "workspace:*",
|
||||
"@univerjs/icons": "^0.3.22",
|
||||
"@univerjs/icons": "^0.3.24",
|
||||
"@univerjs/sheets": "workspace:*",
|
||||
"@univerjs/sheets-formula-ui": "workspace:*",
|
||||
"@univerjs/sheets-sort": "workspace:*",
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
"@univerjs/core": "workspace:*",
|
||||
"@univerjs/engine-formula": "workspace:*",
|
||||
"@univerjs/engine-render": "workspace:*",
|
||||
"@univerjs/icons": "^0.3.22",
|
||||
"@univerjs/icons": "^0.3.24",
|
||||
"@univerjs/sheets": "workspace:*",
|
||||
"@univerjs/sheets-thread-comment": "workspace:*",
|
||||
"@univerjs/sheets-ui": "workspace:*",
|
||||
|
||||
+11
-5
@@ -17,7 +17,7 @@
|
||||
import type { IDisposable, Nullable } from '@univerjs/core';
|
||||
import type { ISheetLocationBase } from '@univerjs/sheets';
|
||||
import { Disposable, DisposableCollection, Inject } from '@univerjs/core';
|
||||
import { SheetCanvasPopManagerService } from '@univerjs/sheets-ui';
|
||||
import { CellPopupManagerService, SheetCanvasPopManagerService } from '@univerjs/sheets-ui';
|
||||
import { IZenZoneService } from '@univerjs/ui';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { SHEETS_THREAD_COMMENT_MODAL } from '../types/const';
|
||||
@@ -42,7 +42,8 @@ export class SheetsThreadCommentPopupService extends Disposable {
|
||||
|
||||
constructor(
|
||||
@Inject(SheetCanvasPopManagerService) private readonly _canvasPopupManagerService: SheetCanvasPopManagerService,
|
||||
@IZenZoneService private readonly _zenZoneService: IZenZoneService
|
||||
@IZenZoneService private readonly _zenZoneService: IZenZoneService,
|
||||
@Inject(CellPopupManagerService) private readonly _cellPopupManagerService: CellPopupManagerService
|
||||
) {
|
||||
super();
|
||||
this._initZenVisible();
|
||||
@@ -83,9 +84,13 @@ export class SheetsThreadCommentPopupService extends Disposable {
|
||||
this._activePopup = location;
|
||||
this._activePopup$.next(location);
|
||||
|
||||
const popupDisposable = this._canvasPopupManagerService.attachPopupToCell(
|
||||
row,
|
||||
col,
|
||||
const popupDisposable = this._cellPopupManagerService.showPopup(
|
||||
{
|
||||
row,
|
||||
col,
|
||||
unitId,
|
||||
subUnitId,
|
||||
},
|
||||
{
|
||||
componentKey: SHEETS_THREAD_COMMENT_MODAL,
|
||||
onClickOutside: () => {
|
||||
@@ -96,6 +101,7 @@ export class SheetsThreadCommentPopupService extends Disposable {
|
||||
...Array.from(document.querySelectorAll('.univer-thread-comment')),
|
||||
document.getElementById('thread-comment-add'),
|
||||
].filter(Boolean) as HTMLElement[],
|
||||
priority: 2,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
"@univerjs/engine-formula": "workspace:*",
|
||||
"@univerjs/engine-numfmt": "workspace:*",
|
||||
"@univerjs/engine-render": "workspace:*",
|
||||
"@univerjs/icons": "^0.3.22",
|
||||
"@univerjs/icons": "^0.3.24",
|
||||
"@univerjs/protocol": "0.1.45",
|
||||
"@univerjs/sheets": "workspace:*",
|
||||
"@univerjs/telemetry": "workspace:*",
|
||||
|
||||
@@ -104,6 +104,8 @@ import {
|
||||
} from '../components/font-family';
|
||||
import { FONT_SIZE_COMPONENT, FontSize } from '../components/font-size';
|
||||
import { MENU_ITEM_INPUT_COMPONENT, MenuItemInput } from '../components/menu-item-input';
|
||||
import { CellPopup } from '../views/cell-popup';
|
||||
import { CELL_POPUP_COMPONENT_KEY } from '../views/cell-popup/config';
|
||||
import { DEFINED_NAME_CONTAINER } from '../views/defined-name/component-name';
|
||||
import { DefinedNameContainer } from '../views/defined-name/DefinedNameContainer';
|
||||
import { RenderSheetContent, RenderSheetFooter, RenderSheetHeader } from '../views/sheet-container/SheetContainer';
|
||||
@@ -198,6 +200,7 @@ export class SheetUIController extends Disposable {
|
||||
this.disposeWithMe(componentManager.register(FONT_FAMILY_ITEM_COMPONENT, FontFamilyItem));
|
||||
this.disposeWithMe(componentManager.register(FONT_SIZE_COMPONENT, FontSize));
|
||||
this.disposeWithMe(componentManager.register(DEFINED_NAME_CONTAINER, DefinedNameContainer));
|
||||
this.disposeWithMe(componentManager.register(CELL_POPUP_COMPONENT_KEY, CellPopup));
|
||||
|
||||
// init icons
|
||||
this.disposeWithMe(componentManager.register('HideGridlines', HideGridlines));
|
||||
|
||||
@@ -99,6 +99,7 @@ export { useKeyEventConfig } from './views/editor-container';
|
||||
export { type IDropdownParam, ISheetCellDropdownManagerService, SheetCellDropdownManagerService } from './services/cell-dropdown-manager.service';
|
||||
export { FormulaBar } from './views/formula-bar/FormulaBar';
|
||||
export { type IPermissionDetailUserPartProps } from './views/permission/panel-detail/PermissionDetailUserPart';
|
||||
export { CellPopupManagerService } from './services/cell-popup-manager.service';
|
||||
// #region - all commands
|
||||
export { SetWorksheetColAutoWidthCommand } from './commands/commands/set-worksheet-auto-col-width.command';
|
||||
export { AutoClearContentCommand, AutoFillCommand } from './commands/commands/auto-fill.command';
|
||||
|
||||
@@ -53,6 +53,7 @@ import { AutoFillService, IAutoFillService } from './services/auto-fill/auto-fil
|
||||
import { SheetCanvasPopManagerService } from './services/canvas-pop-manager.service';
|
||||
import { CellAlertManagerService } from './services/cell-alert-manager.service';
|
||||
import { ISheetCellDropdownManagerService, SheetCellDropdownManagerService } from './services/cell-dropdown-manager.service';
|
||||
import { CellPopupManagerService } from './services/cell-popup-manager.service';
|
||||
import { ISheetClipboardService, SheetClipboardService } from './services/clipboard/clipboard.service';
|
||||
import { DragManagerService } from './services/drag-manager.service';
|
||||
import { EditorBridgeService, IEditorBridgeService } from './services/editor-bridge.service';
|
||||
@@ -105,6 +106,7 @@ export class UniverSheetsMobileUIPlugin extends Plugin {
|
||||
[HoverManagerService],
|
||||
[DragManagerService],
|
||||
[SheetCanvasPopManagerService],
|
||||
[CellPopupManagerService],
|
||||
[CellAlertManagerService],
|
||||
[SelectAllService],
|
||||
[ISheetCellDropdownManagerService, { useClass: SheetCellDropdownManagerService }],
|
||||
|
||||
@@ -68,6 +68,7 @@ import { AutoFillService, IAutoFillService } from './services/auto-fill/auto-fil
|
||||
import { SheetCanvasPopManagerService } from './services/canvas-pop-manager.service';
|
||||
import { CellAlertManagerService } from './services/cell-alert-manager.service';
|
||||
import { ISheetCellDropdownManagerService, SheetCellDropdownManagerService } from './services/cell-dropdown-manager.service';
|
||||
import { CellPopupManagerService } from './services/cell-popup-manager.service';
|
||||
import { ISheetClipboardService, SheetClipboardService } from './services/clipboard/clipboard.service';
|
||||
import { DragManagerService } from './services/drag-manager.service';
|
||||
import { EditorBridgeService, IEditorBridgeService } from './services/editor-bridge.service';
|
||||
@@ -156,6 +157,7 @@ export class UniverSheetsUIPlugin extends Plugin {
|
||||
[HoverManagerService],
|
||||
[DragManagerService],
|
||||
[SheetCanvasPopManagerService],
|
||||
[CellPopupManagerService],
|
||||
[CellAlertManagerService],
|
||||
[SelectAllService],
|
||||
[ISheetCellDropdownManagerService, { useClass: SheetCellDropdownManagerService }],
|
||||
|
||||
@@ -18,11 +18,11 @@ import type { DrawingTypeEnum, ICommandInfo, INeedCheckDisposable, IRange, Nulla
|
||||
import type { BaseObject, IBoundRectNoAngle, IRender, SpreadsheetSkeleton, Viewport } from '@univerjs/engine-render';
|
||||
import type { ISetWorksheetRowAutoHeightMutationParams, ISheetLocationBase } from '@univerjs/sheets';
|
||||
import type { IPopup } from '@univerjs/ui';
|
||||
import { Disposable, DisposableCollection, ICommandService, Inject, IUniverInstanceService, toDisposable, UniverInstanceType } from '@univerjs/core';
|
||||
import { Disposable, DisposableCollection, fromEventSubject, ICommandService, Inject, IUniverInstanceService, toDisposable, UniverInstanceType } from '@univerjs/core';
|
||||
import { IRenderManagerService } from '@univerjs/engine-render';
|
||||
import { COMMAND_LISTENER_SKELETON_CHANGE, IRefSelectionsService, RefRangeService, SetFrozenMutation, SetWorksheetRowAutoHeightMutation, SheetsSelectionsService } from '@univerjs/sheets';
|
||||
import { ICanvasPopupService } from '@univerjs/ui';
|
||||
import { BehaviorSubject, map } from 'rxjs';
|
||||
import { BehaviorSubject, map, throttleTime } from 'rxjs';
|
||||
import { SetScrollOperation } from '../commands/operations/scroll.operation';
|
||||
import { SetZoomRatioOperation } from '../commands/operations/set-zoom-ratio.operation';
|
||||
import { getViewportByCell, transformBound2OffsetBound } from '../common/utils';
|
||||
@@ -181,11 +181,14 @@ export class SheetCanvasPopManagerService extends Disposable {
|
||||
|
||||
disposable.add(this._commandService.onCommandExecuted((commandInfo) => {
|
||||
if (commandInfo.id === SetFrozenMutation.id) {
|
||||
const newRects = calc();
|
||||
rects$.next(newRects);
|
||||
rects$.next(calc());
|
||||
}
|
||||
}));
|
||||
|
||||
disposable.add(fromEventSubject(currentRender.engine.onTransformChange$).pipe(throttleTime(16)).subscribe(() => {
|
||||
rects$.next(calc());
|
||||
}));
|
||||
|
||||
return {
|
||||
rects$,
|
||||
disposable,
|
||||
@@ -597,6 +600,7 @@ export class SheetCanvasPopManagerService extends Disposable {
|
||||
|
||||
const disposable = new DisposableCollection();
|
||||
disposable.add(currentRender.engine.clientRect$.subscribe(() => updatePosition()));
|
||||
disposable.add(fromEventSubject(currentRender.engine.onTransformChange$).pipe(throttleTime(16)).subscribe(() => updatePosition()));
|
||||
disposable.add(this._commandService.onCommandExecuted((commandInfo) => {
|
||||
if (commandInfo.id === SetWorksheetRowAutoHeightMutation.id) {
|
||||
const params = commandInfo.params as ISetWorksheetRowAutoHeightMutationParams;
|
||||
|
||||
@@ -21,6 +21,7 @@ import { IRenderManagerService } from '@univerjs/engine-render';
|
||||
import { Subject } from 'rxjs';
|
||||
import { CELL_ALERT_KEY } from '../views/cell-alert';
|
||||
import { SheetCanvasPopManagerService } from './canvas-pop-manager.service';
|
||||
import { CellPopupManagerService } from './cell-popup-manager.service';
|
||||
|
||||
export enum CellAlertType {
|
||||
INFO,
|
||||
@@ -50,7 +51,8 @@ export class CellAlertManagerService extends Disposable {
|
||||
|
||||
constructor(
|
||||
@IRenderManagerService private readonly _renderManagerService: IRenderManagerService,
|
||||
@Inject(SheetCanvasPopManagerService) private readonly _canvasPopManagerService: SheetCanvasPopManagerService
|
||||
@Inject(SheetCanvasPopManagerService) private readonly _canvasPopManagerService: SheetCanvasPopManagerService,
|
||||
@Inject(CellPopupManagerService) private readonly _cellPopupManagerService: CellPopupManagerService
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -79,21 +81,29 @@ export class CellAlertManagerService extends Disposable {
|
||||
}
|
||||
|
||||
const { location } = alert;
|
||||
const { row, col, unitId } = location;
|
||||
const { row, col, unitId, subUnitId } = location;
|
||||
|
||||
const currentRender = this._renderManagerService.getRenderById(unitId);
|
||||
if (!currentRender) {
|
||||
return;
|
||||
}
|
||||
|
||||
const disposable = this._canvasPopManagerService.attachPopupToCell(row, col, {
|
||||
componentKey: CELL_ALERT_KEY,
|
||||
direction: 'horizontal',
|
||||
offset: [2, 0],
|
||||
extraProps: {
|
||||
alert,
|
||||
const disposable = this._cellPopupManagerService.showPopup(
|
||||
{
|
||||
unitId,
|
||||
subUnitId,
|
||||
row,
|
||||
col,
|
||||
},
|
||||
});
|
||||
{
|
||||
componentKey: CELL_ALERT_KEY,
|
||||
direction: 'horizontal',
|
||||
extraProps: {
|
||||
alert,
|
||||
},
|
||||
priority: 1,
|
||||
}
|
||||
);
|
||||
if (disposable) {
|
||||
lastPopup.dispose = disposable;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
import type { IDisposable } from '@univerjs/core';
|
||||
import type { ISheetLocationBase } from '@univerjs/sheets';
|
||||
import type { ICanvasPopup } from './canvas-pop-manager.service';
|
||||
import { Disposable, generateRandomId, Inject, ObjectMatrix, toDisposable } from '@univerjs/core';
|
||||
import { Subject } from 'rxjs';
|
||||
import { CELL_POPUP_COMPONENT_KEY } from '../views/cell-popup/config';
|
||||
import { SheetCanvasPopManagerService } from './canvas-pop-manager.service';
|
||||
|
||||
export interface ICellPopup extends Omit<ICanvasPopup, 'direction' | 'offset' | 'mask'> {
|
||||
direction?: 'horizontal' | 'vertical';
|
||||
id?: string;
|
||||
priority: number;
|
||||
}
|
||||
|
||||
export interface ICellPopupDirectionCache {
|
||||
popups: ICellPopup[];
|
||||
disposable: IDisposable;
|
||||
}
|
||||
|
||||
export interface ICellPopupCache {
|
||||
horizontal?: ICellPopupDirectionCache;
|
||||
vertical?: ICellPopupDirectionCache;
|
||||
}
|
||||
|
||||
export interface ICellPopupChange extends ISheetLocationBase {
|
||||
direction: 'horizontal' | 'vertical';
|
||||
}
|
||||
|
||||
export class CellPopupManagerService extends Disposable {
|
||||
private readonly _cellPopupMap = new Map<string, Map<string, ObjectMatrix<ICellPopupCache>>>();
|
||||
private readonly _change$ = new Subject<ICellPopupChange>();
|
||||
readonly change$ = this._change$.asObservable();
|
||||
|
||||
constructor(
|
||||
@Inject(SheetCanvasPopManagerService) private readonly _sheetCanvasPopManagerService: SheetCanvasPopManagerService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
private _ensureCellPopupMap(unitId: string, subUnitId: string) {
|
||||
let unitMap = this._cellPopupMap.get(unitId);
|
||||
if (!unitMap) {
|
||||
unitMap = new Map();
|
||||
this._cellPopupMap.set(unitId, unitMap);
|
||||
}
|
||||
let subUnitMap = unitMap.get(subUnitId);
|
||||
if (!subUnitMap) {
|
||||
subUnitMap = new ObjectMatrix<ICellPopupCache>();
|
||||
unitMap.set(subUnitId, subUnitMap);
|
||||
}
|
||||
return subUnitMap;
|
||||
}
|
||||
|
||||
showPopup(location: ISheetLocationBase, popup: ICellPopup) {
|
||||
const { direction = 'horizontal' } = popup;
|
||||
const { unitId, subUnitId, row, col } = location;
|
||||
const subUnitMap = this._ensureCellPopupMap(unitId, subUnitId);
|
||||
const directionCache = subUnitMap.getValue(row, col)?.[direction];
|
||||
|
||||
popup.id = popup.id ?? generateRandomId();
|
||||
|
||||
const hidePopup = () => {
|
||||
const root = subUnitMap.getValue(row, col);
|
||||
if (!root) {
|
||||
return;
|
||||
}
|
||||
const directionCache = root[direction];
|
||||
if (!directionCache) {
|
||||
return;
|
||||
}
|
||||
|
||||
const findIndex = directionCache?.popups.findIndex((p) => p.id === popup.id && p.componentKey === popup.componentKey);
|
||||
directionCache?.popups.splice(findIndex, 1);
|
||||
|
||||
if (directionCache.popups.length === 0) {
|
||||
directionCache.disposable.dispose();
|
||||
root[direction] = undefined;
|
||||
}
|
||||
|
||||
this._change$.next({
|
||||
...location,
|
||||
direction,
|
||||
});
|
||||
};
|
||||
|
||||
if (!directionCache) {
|
||||
const disposable = this._sheetCanvasPopManagerService.attachPopupToCell(
|
||||
row,
|
||||
col,
|
||||
{
|
||||
...popup,
|
||||
direction,
|
||||
componentKey: CELL_POPUP_COMPONENT_KEY,
|
||||
extraProps: {
|
||||
...location,
|
||||
direction,
|
||||
},
|
||||
noPushMinimumGap: true,
|
||||
},
|
||||
unitId,
|
||||
subUnitId
|
||||
);
|
||||
|
||||
subUnitMap.setValue(row, col, {
|
||||
...subUnitMap.getValue(row, col),
|
||||
[direction]: {
|
||||
disposable,
|
||||
popups: [popup],
|
||||
},
|
||||
});
|
||||
} else {
|
||||
directionCache.popups.push(popup);
|
||||
directionCache.popups.sort((a, b) => a.priority - b.priority);
|
||||
}
|
||||
|
||||
this._change$.next({
|
||||
...location,
|
||||
direction,
|
||||
});
|
||||
|
||||
return toDisposable(hidePopup);
|
||||
}
|
||||
|
||||
getPopups(unitId: string, subUnitId: string, row: number, col: number, direction: 'horizontal' | 'vertical') {
|
||||
const subUnitMap = this._ensureCellPopupMap(unitId, subUnitId);
|
||||
return subUnitMap.getValue(row, col)?.[direction]?.popups || [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export const CELL_POPUP_COMPONENT_KEY = 'sheets.component.cell-popup';
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* 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 { ISheetLocationBase } from '@univerjs/sheets';
|
||||
import type { IPopup } from '@univerjs/ui';
|
||||
import { ComponentManager, useDependency, useObservable } from '@univerjs/ui';
|
||||
import { useMemo } from 'react';
|
||||
import { filter } from 'rxjs';
|
||||
import { CellPopupManagerService } from '../../services/cell-popup-manager.service';
|
||||
|
||||
export const CellPopup = (props: { popup: IPopup<ISheetLocationBase & { direction: 'horizontal' | 'vertical' }> }) => {
|
||||
const { popup } = props;
|
||||
const location = popup.extraProps!;
|
||||
const { row, col, direction, unitId, subUnitId } = location;
|
||||
const cellPopupManagerService = useDependency(CellPopupManagerService);
|
||||
useObservable(useMemo(() => cellPopupManagerService.change$.pipe(filter((change) => change.row === row && change.col === col && change.direction === direction)), [cellPopupManagerService, row, col, direction]));
|
||||
const popups = cellPopupManagerService.getPopups(unitId, subUnitId, row, col, direction);
|
||||
const componentManager = useDependency(ComponentManager);
|
||||
|
||||
return (
|
||||
<div className="univer-cell-popup univer-flex univer-flex-col univer-ml-[1px]">
|
||||
{popups.map((item) => {
|
||||
const popup = item;
|
||||
const Component = componentManager.get(popup.componentKey);
|
||||
return Component ? <Component popup={popup} /> : null;
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -84,7 +84,7 @@
|
||||
"@univerjs/docs": "workspace:*",
|
||||
"@univerjs/docs-ui": "workspace:*",
|
||||
"@univerjs/engine-render": "workspace:*",
|
||||
"@univerjs/icons": "^0.3.22",
|
||||
"@univerjs/icons": "^0.3.24",
|
||||
"@univerjs/sheets": "workspace:*",
|
||||
"@univerjs/sheets-ui": "workspace:*",
|
||||
"@univerjs/ui": "workspace:*"
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
"@univerjs/docs-ui": "workspace:*",
|
||||
"@univerjs/drawing": "workspace:*",
|
||||
"@univerjs/engine-render": "workspace:*",
|
||||
"@univerjs/icons": "^0.3.22",
|
||||
"@univerjs/icons": "^0.3.24",
|
||||
"@univerjs/slides": "workspace:*",
|
||||
"@univerjs/ui": "workspace:*"
|
||||
},
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
"@univerjs/core": "workspace:*",
|
||||
"@univerjs/design": "workspace:*",
|
||||
"@univerjs/docs-ui": "workspace:*",
|
||||
"@univerjs/icons": "^0.3.22",
|
||||
"@univerjs/icons": "^0.3.24",
|
||||
"@univerjs/thread-comment": "workspace:*",
|
||||
"@univerjs/ui": "workspace:*"
|
||||
},
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
"@univerjs/core": "workspace:*",
|
||||
"@univerjs/design": "workspace:*",
|
||||
"@univerjs/engine-render": "workspace:*",
|
||||
"@univerjs/icons": "^0.3.22",
|
||||
"@univerjs/icons": "^0.3.24",
|
||||
"@wendellhu/redi": "0.18.0",
|
||||
"localforage": "^1.10.0"
|
||||
},
|
||||
|
||||
@@ -18,4 +18,5 @@ export { useEvent } from './event';
|
||||
export { useConfigValue } from './layout';
|
||||
export { useUpdateEffect } from './update-effect';
|
||||
export { useClickOutSide } from './use-click-out-side';
|
||||
export { useDebounceFn } from './use-debounce';
|
||||
export { useVirtualList } from './virtual-list';
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 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 { useCallback, useRef } from 'react';
|
||||
|
||||
export function useDebounceFn<T extends (...args: any[]) => void>(fn: T, delay = 300) {
|
||||
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||
|
||||
return useCallback((...args: Parameters<T>) => {
|
||||
if (timeoutRef.current) {
|
||||
clearTimeout(timeoutRef.current);
|
||||
}
|
||||
|
||||
timeoutRef.current = setTimeout(() => {
|
||||
fn(...args);
|
||||
}, delay);
|
||||
}, [fn, delay]) as T;
|
||||
}
|
||||
@@ -127,7 +127,7 @@ export { MobileContextMenu } from './views/components/context-menu/MobileContext
|
||||
export { type IDialogPartMethodOptions } from './views/components/dialog-part/interface';
|
||||
export { FloatDom } from './views/components/dom/FloatDom';
|
||||
export { GlobalZone } from './views/components/global-zone/GlobalZone';
|
||||
export { CanvasPopup } from './views/components/popup/CanvasPopup';
|
||||
export { CanvasPopup, SingleCanvasPopup } from './views/components/popup/CanvasPopup';
|
||||
export { ToolbarButton } from './views/components/ribbon/Button/ToolbarButton';
|
||||
export { useToolbarItemStatus } from './views/components/ribbon/hook';
|
||||
export { Ribbon } from './views/components/ribbon/Ribbon';
|
||||
|
||||
@@ -27,7 +27,7 @@ interface ISingleCanvasPopupProps {
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
const SingleCanvasPopup = ({ popup, children }: ISingleCanvasPopupProps) => {
|
||||
export const SingleCanvasPopup = ({ popup, children }: ISingleCanvasPopupProps) => {
|
||||
const [hidden, setHidden] = useState(false);
|
||||
const anchorRect$ = useMemo(() => popup.anchorRect$.pipe(
|
||||
throttleTime(0, animationFrameScheduler),
|
||||
|
||||
@@ -85,6 +85,7 @@ export interface IRectPopupProps {
|
||||
zIndex?: number;
|
||||
maskZIndex?: number;
|
||||
onMaskClick?: () => void;
|
||||
noPushMinimumGap?: boolean;
|
||||
}
|
||||
|
||||
export interface IPopupLayoutInfo extends Pick<IRectPopupProps, 'direction'> {
|
||||
@@ -93,33 +94,36 @@ export interface IPopupLayoutInfo extends Pick<IRectPopupProps, 'direction'> {
|
||||
height: number;
|
||||
containerWidth: number;
|
||||
containerHeight: number;
|
||||
noPushMinimumGap?: boolean;
|
||||
}
|
||||
|
||||
/** The popup should have a minimum edge to the boundary. */
|
||||
const PUSHING_MINIMUM_GAP = 8;
|
||||
|
||||
function calcPopupPosition(layout: IPopupLayoutInfo): { top: number; left: number } {
|
||||
const { position, width, height, containerHeight, containerWidth, direction = 'vertical' } = layout;
|
||||
const { position, width, height, containerHeight, containerWidth, direction = 'vertical', noPushMinimumGap = false } = layout;
|
||||
|
||||
const minTop = noPushMinimumGap ? -Infinity : PUSHING_MINIMUM_GAP;
|
||||
const maxTop = noPushMinimumGap ? Infinity : containerHeight - height - PUSHING_MINIMUM_GAP;
|
||||
const minLeft = noPushMinimumGap ? -Infinity : PUSHING_MINIMUM_GAP;
|
||||
const maxLeft = noPushMinimumGap ? Infinity : containerWidth - width - PUSHING_MINIMUM_GAP;
|
||||
|
||||
// In y-axis
|
||||
if (direction === 'vertical' || direction.indexOf('top') === 0 || direction.indexOf('bottom') === 0) {
|
||||
const { left: startX, top: startY, right: endX, bottom: endY } = position;
|
||||
const verticalStyle = (direction === 'vertical' && endY > containerHeight - height - PUSHING_MINIMUM_GAP) || direction.indexOf('top') > -1
|
||||
const verticalStyle = (direction === 'vertical' && endY > maxTop) || direction.indexOf('top') > -1
|
||||
// top
|
||||
? { top: Math.max(startY - height, PUSHING_MINIMUM_GAP) }
|
||||
? { top: Math.max(startY - height, minTop) }
|
||||
// bottom
|
||||
: { top: Math.min(endY, containerHeight - height - PUSHING_MINIMUM_GAP) };
|
||||
: { top: Math.min(endY, maxTop) };
|
||||
|
||||
let horizontalStyle;
|
||||
|
||||
const minLeft = PUSHING_MINIMUM_GAP;
|
||||
const maxLeft = containerWidth - width - PUSHING_MINIMUM_GAP;
|
||||
|
||||
if (direction.includes('center')) {
|
||||
const rectWidth = endX - startX;
|
||||
const offsetX = (rectWidth - width) / 2;
|
||||
|
||||
horizontalStyle = (Math.max(startX + offsetX, PUSHING_MINIMUM_GAP) + width) > containerWidth
|
||||
horizontalStyle = (Math.max(startX + offsetX, minLeft) + width) > containerWidth
|
||||
? { left: Math.max(Math.min(maxLeft, endX - width - offsetX), minLeft) }
|
||||
: { left: Math.max(minLeft, Math.min(startX + offsetX, maxLeft)) };
|
||||
} else if (direction.includes('left')) {
|
||||
@@ -140,33 +144,31 @@ function calcPopupPosition(layout: IPopupLayoutInfo): { top: number; left: numbe
|
||||
const { left: startX, top: startY, right: endX, bottom: endY } = position;
|
||||
// const horizontalStyle = ((endX + width) > containerWidth || direction === 'left')
|
||||
const horizontalStyle = direction.includes('left')
|
||||
? { left: Math.max(startX - width, PUSHING_MINIMUM_GAP) } // on left
|
||||
: { left: Math.min(endX, containerWidth - width - PUSHING_MINIMUM_GAP) }; // on right
|
||||
? { left: Math.max(startX - width, minLeft) } // on left
|
||||
: { left: Math.min(endX, maxLeft) }; // on right
|
||||
|
||||
let verticalStyle;
|
||||
const minTop = PUSHING_MINIMUM_GAP;
|
||||
const maxTop = containerHeight - height - PUSHING_MINIMUM_GAP;
|
||||
|
||||
if (direction.includes('center')) {
|
||||
const rectHeight = endY - startY;
|
||||
const offsetY = (rectHeight - height) / 2;
|
||||
|
||||
verticalStyle = (Math.max(startY + offsetY, PUSHING_MINIMUM_GAP) + height) > containerHeight
|
||||
verticalStyle = (Math.max(startY + offsetY, minTop) + height) > containerHeight
|
||||
? { top: Math.max(Math.min(maxTop, endY - height - offsetY), minTop) }
|
||||
: { top: Math.max(minTop, Math.min(startY + offsetY, maxTop)) };
|
||||
} else if (direction.includes('top')) {
|
||||
verticalStyle = {
|
||||
top: Math.min(startY, containerHeight - height - PUSHING_MINIMUM_GAP),
|
||||
top: Math.min(startY, maxTop),
|
||||
};
|
||||
} else if (direction.includes('bottom')) {
|
||||
verticalStyle = {
|
||||
top: Math.max(endY - height, PUSHING_MINIMUM_GAP),
|
||||
top: Math.max(endY - height, minTop),
|
||||
};
|
||||
} else {
|
||||
// If the popup element exceed the visible area. We should "push" it back.
|
||||
verticalStyle = ((startY + height) > containerHeight)
|
||||
? { top: Math.max(endY - height, PUSHING_MINIMUM_GAP) } // on top
|
||||
: { top: Math.min(startY, containerHeight - height - PUSHING_MINIMUM_GAP) }; // on bottom
|
||||
? { top: Math.max(endY - height, minTop) } // on top
|
||||
: { top: Math.min(startY, maxTop) }; // on bottom
|
||||
}
|
||||
|
||||
return { ...verticalStyle, ...horizontalStyle };
|
||||
@@ -190,6 +192,7 @@ function RectPopup(props: IRectPopupProps) {
|
||||
zIndex = 1020,
|
||||
maskZIndex = 100,
|
||||
onMaskClick,
|
||||
noPushMinimumGap,
|
||||
} = props;
|
||||
const nodeRef = useRef<HTMLElement>(null);
|
||||
const clickOtherFn = useEvent(onClickOutside ?? (() => { /* empty */ }));
|
||||
@@ -220,6 +223,7 @@ function RectPopup(props: IRectPopupProps) {
|
||||
containerWidth: innerWidth,
|
||||
containerHeight: innerHeight,
|
||||
direction,
|
||||
noPushMinimumGap,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
Generated
+974
-68
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user