mirror of
https://github.com/dream-num/univer.git
synced 2026-09-24 15:32:18 +08:00
feat(sheets-thread-comment): comment support for sheets (#2228)
* feat: add thread-comment pkg * feat: update * feat: update * feat: update * feat: update * feat: update * feat: update * feat: temp * feat: update * feat: update * feat: u * feat: update * feat: update * feat: update * feat: update * fix: #2121 * fix: close #2120 * fix: #2114 * fix: #2112 * feat: temp * feat: update * feat: support at-input * feat: ref-range * feat: update mentions * feat: user * feat: update * feat: update comment * feat: style update * feat: update * feat: update * feat: update * feat: update * feat: rename * feat: scroller * feat: update * feat: update * feat: update * feat: update * fix: #684 univer-pro * feat: auto-height * feat: update * feat: update * fix: eslint * feat: remove useless code * build: fix build error * chore: fix lockfile * build: fix deps * feat: update * feat: update * feat: update * feat: i18n * feat: add sheet name * feat: disable undo on resolve comment operation * feat: update * fix: #2174 * feat: #2162 checkbox ticked and unticked value * feat: update * fix: hover-manager-service * feat: support copy-paste * feat: sidebar visible status * feat: style * feat: update * feat: update init * feat: temp * feat: update * feat: update * fix: checkbox validator * feat: lint * fix: error when delete worksheet * feat: refactor canvas popup service * feat: update * feat: lock * feat: custom-render event handler * feat: update ref * fix: copy-paste * feat: update * feat: update * feat: update max-height * feat: update * feat: fix delete comment * fix: #715 * feat: update * feat: update scroll * feat: style update * feat: bugfix * feat: update * feat: close sidebar * fix: cv undo * feat: update * feat: render z-index * feat: code review issue * feat: transform mentions data * feat: u * feat: bugfix * feat: update * feat: update * feat: style & bugfix * feat: style update * feat: update * feat: update * feat: update * feat: add thread-comment-data-source-service to support async comment crud * feat: rename * feat: build issue * fix: sort order of comments * feat: lint * feat: rename * feat: rebase dev --------- Co-authored-by: 白熱 <sonne@asaki.me>
This commit is contained in:
@@ -34,10 +34,13 @@
|
||||
"@univerjs/sheets-find-replace": "workspace:*",
|
||||
"@univerjs/sheets-formula": "workspace:*",
|
||||
"@univerjs/sheets-numfmt": "workspace:*",
|
||||
"@univerjs/sheets-thread-comment": "workspace:*",
|
||||
"@univerjs/sheets-ui": "workspace:*",
|
||||
"@univerjs/sheets-zen-editor": "workspace:*",
|
||||
"@univerjs/slides": "workspace:*",
|
||||
"@univerjs/slides-ui": "workspace:*",
|
||||
"@univerjs/thread-comment": "workspace:*",
|
||||
"@univerjs/thread-comment-ui": "workspace:*",
|
||||
"@univerjs/ui": "workspace:*",
|
||||
"@univerjs/uniscript": "workspace:*",
|
||||
"@wendellhu/redi": "^0.15.1",
|
||||
|
||||
@@ -23560,6 +23560,10 @@ export const DEFAULT_WORKBOOK_DATA_DEMO: IWorkbookData = {
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: 'SHEET_THREAD_COMMENT_PLUGIN',
|
||||
data: '{"sheet-0011":[{"text":{"textRuns":[],"paragraphs":[{"startIndex":3,"paragraphStyle":{}}],"sectionBreaks":[{"startIndex":4}],"dataStream":"123\\n\\r","customRanges":[]},"dT":"2024/05/17 21:16","id":"jwV0QtHwUbhG3o--iy1qa","ref":"H9","personId":"mockId","unitId":"workbook-01","subUnitId":"sheet-0011"}]}',
|
||||
},
|
||||
],
|
||||
// namedRanges: [
|
||||
// {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { LocaleType, LogLevel, Univer, UniverInstanceType } from '@univerjs/core';
|
||||
import { LocaleType, LogLevel, Univer, UniverInstanceType, UserManagerService } from '@univerjs/core';
|
||||
import { defaultTheme } from '@univerjs/design';
|
||||
import { UniverDocsPlugin } from '@univerjs/docs';
|
||||
import { UniverDocsUIPlugin } from '@univerjs/docs-ui';
|
||||
@@ -34,6 +34,7 @@ import { UniverUIPlugin } from '@univerjs/ui';
|
||||
import { UniverDataValidationPlugin } from '@univerjs/data-validation';
|
||||
import { UniverSheetsDataValidationPlugin } from '@univerjs/sheets-data-validation';
|
||||
import { UniverSheetsConditionalFormattingUIPlugin } from '@univerjs/sheets-conditional-formatting-ui';
|
||||
import { UniverSheetsThreadCommentPlugin } from '@univerjs/sheets-thread-comment';
|
||||
import { UniverDebuggerPlugin } from '@univerjs/debugger';
|
||||
|
||||
import { FUniver } from '@univerjs/facade';
|
||||
@@ -98,9 +99,41 @@ if (!IS_E2E) {
|
||||
univer.createUnit(UniverInstanceType.UNIVER_SHEET, DEFAULT_WORKBOOK_DATA_DEMO);
|
||||
}
|
||||
|
||||
const mockUser = {
|
||||
userID: 'mockId',
|
||||
name: 'MockUser',
|
||||
avatar: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAInSURBVHgBtZU9TxtBEIbfWRzFSIdkikhBSqRQkJqkCKTCFkqVInSUSaT0wC8w/gXxD4gU2nRJkXQWhAZowDUUWKIwEgWWbEEB3mVmx3dn4DA2nB/ppNuPeWd29mMIPXDr+RxwtgRHeW6+guNPRxogqnL7Dwz9psJ27S4NShaeZTH3kwXy6I81dlRKcmRui88swdq9AcSFL7Buz1Vmlns64MiLsCjzwnIYHLH57tbfFbs7KRaXyEU8FVZofqccOfA5l7Q8LPIkGrwnb2RPNEXWFVMUF3L+kDCk0btDDAMzOm5YfAHDwp4tG74wnzAsiOYMnJ3GoDybA7IT98/jm5+JNnfiIzAS6LlqHQBN/i6b2t/cV1Hh6BfwYlHnHP4AXi5q/8kmMMpOs8+BixZw/Fd6xUEHEbnkgclvQP2fGp7uShRKnQ3G32rkjV1th8JhIGG7tR/JyjGteSOZELwGMmNqIIigRCLRh2OZIE6BjItdd7pCW6Uhm1zzkUtungSxwEUzNpQ+GQumtH1ej1MqgmNT6vwmhCq5yuwq56EYTbgeQUz3yvrpV1b4ok3nYJ+eYhgYmjRUqErx2EDq0Fr8FhG++iqVGqxlUJI/70Ar0UgJaWHj6hYVHJrfKssAHot1JfqwE9WVWzXZVd5z2Ws/4PnmtEjkXeKJDvxUecLbWOXH/DP6QQ4J72NS0adedp1aseBfXP8odlZFfPvBF7SN/8hky1TYuPOAXAEipMx15u5ToAAAAABJRU5ErkJggg==',
|
||||
};
|
||||
|
||||
univer.registerPlugin(UniverSheetsThreadCommentPlugin, {
|
||||
mentions: [{
|
||||
trigger: '@',
|
||||
getMentions: async () => {
|
||||
return [
|
||||
{
|
||||
id: mockUser.userID,
|
||||
label: mockUser.name,
|
||||
type: 'user',
|
||||
icon: mockUser.avatar,
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
label: 'User2',
|
||||
type: 'user',
|
||||
icon: mockUser.avatar,
|
||||
},
|
||||
];
|
||||
},
|
||||
}],
|
||||
});
|
||||
|
||||
// debugger plugin
|
||||
univer.registerPlugin(UniverDebuggerPlugin);
|
||||
|
||||
const injector = univer.__getInjector();
|
||||
const userManagerService = injector.get(UserManagerService);
|
||||
userManagerService.setCurrentUser(mockUser);
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
univer?: Univer;
|
||||
|
||||
@@ -15,13 +15,27 @@
|
||||
*/
|
||||
|
||||
import type { IUser } from '@univerjs/protocol';
|
||||
import { Subject } from 'rxjs';
|
||||
import { BehaviorSubject, Subject } from 'rxjs';
|
||||
|
||||
export class UserManagerService {
|
||||
private _model = new Map<string, IUser>();
|
||||
private _userChange$ = new Subject<{ type: 'add' | 'delete'; user: IUser } | { type: 'clear' }>();
|
||||
public userChange$ = this._userChange$.asObservable();
|
||||
|
||||
private _currentUser: IUser | null;
|
||||
private _currentUser$ = new BehaviorSubject<IUser | null>(null);
|
||||
public currentUser$ = this._currentUser$.asObservable();
|
||||
|
||||
getCurrentUser() {
|
||||
return this._currentUser;
|
||||
}
|
||||
|
||||
setCurrentUser(user: IUser) {
|
||||
this._currentUser = user;
|
||||
this.addUser(user);
|
||||
this._currentUser$.next(user);
|
||||
}
|
||||
|
||||
addUser(user: IUser) {
|
||||
this._model.set(user.userID, user);
|
||||
this._userChange$.next({ type: 'add', user });
|
||||
|
||||
@@ -277,6 +277,7 @@ export enum CustomRangeType {
|
||||
BOOKMARK,
|
||||
COMMENT,
|
||||
CUSTOM,
|
||||
MENTION,
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
"dependencies": {
|
||||
"@rc-component/color-picker": "^1.5.3",
|
||||
"@rc-component/trigger": "^1.18.3",
|
||||
"@types/react-mentions": "^4.1.13",
|
||||
"@univerjs/icons": "^0.1.45",
|
||||
"rc-dialog": "^9.4.0",
|
||||
"rc-dropdown": "^4.2.0",
|
||||
@@ -88,6 +89,7 @@
|
||||
"rc-util": "^5.39.3",
|
||||
"react-draggable": "^4.4.6",
|
||||
"react-grid-layout": "^1.4.4",
|
||||
"react-mentions": "^4.4.10",
|
||||
"react-transition-group": "^4.4.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -60,6 +60,8 @@ export interface IButtonProps {
|
||||
|
||||
/** Set the handler to handle `click` event */
|
||||
onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
|
||||
id?: string;
|
||||
}
|
||||
|
||||
export function Button(props: IButtonProps) {
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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 { Meta } from '@storybook/react';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { Mention } from 'react-mentions';
|
||||
import { Mentions } from './Mentions';
|
||||
|
||||
const meta: Meta<typeof Mentions> = {
|
||||
title: 'Components / Mentions',
|
||||
component: Mentions,
|
||||
parameters: {
|
||||
layout: 'centered',
|
||||
},
|
||||
tags: ['autodocs'],
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
export const InputBasic = {
|
||||
|
||||
render() {
|
||||
const [value, onChange] = useState('');
|
||||
|
||||
return (
|
||||
<div style={{ width: 400 }}>
|
||||
<Mentions
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
>
|
||||
<Mention
|
||||
trigger="@"
|
||||
data={[
|
||||
{
|
||||
id: '1',
|
||||
display: 'zhangwei ',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
display: 'zhangwei2 ',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
display: 'zhangwei3 ',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Mentions>
|
||||
</div>
|
||||
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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 React, { forwardRef } from 'react';
|
||||
import type { MentionsInputProps } from 'react-mentions';
|
||||
import { MentionsInput } from 'react-mentions';
|
||||
import styles from './index.module.less';
|
||||
|
||||
export interface IMentionsProps extends MentionsInputProps {}
|
||||
|
||||
export const Mentions = forwardRef<React.Component<MentionsInputProps, any, any>, IMentionsProps>((props, ref) => {
|
||||
return (
|
||||
<MentionsInput
|
||||
ref={ref}
|
||||
{...props}
|
||||
className={styles.mentions}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
.mentions {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.mentions__control {
|
||||
min-height: 32px;
|
||||
}
|
||||
|
||||
.mentions__highlighter {
|
||||
border-radius: 6px;
|
||||
background: rgba(var(--color-white));
|
||||
padding: 6px 10px;
|
||||
font-size: 13px !important;
|
||||
line-height: 20px !important;
|
||||
max-height: 114px;
|
||||
|
||||
strong {
|
||||
color: rgb(var(--blue-500));
|
||||
}
|
||||
}
|
||||
|
||||
.mentions__highlighter__substring {
|
||||
visibility: inherit !important;
|
||||
color: rgb(var(--color-black));
|
||||
}
|
||||
|
||||
.mentions__input {
|
||||
width: 100%;
|
||||
caret-color: red;
|
||||
background-color: transparent;
|
||||
color: transparent;
|
||||
padding: 6px 10px;
|
||||
border: 1px solid rgb(var(--border-color));
|
||||
border-radius: 6px;
|
||||
font-size: 13px !important;
|
||||
line-height: 20px !important;
|
||||
max-height: 114px;
|
||||
}
|
||||
|
||||
.mentions__input:focus {
|
||||
border: 1px solid rgb(var(--blue-500));
|
||||
outline: none !important;
|
||||
}
|
||||
|
||||
.mentions__suggestions {
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
background: rgb(var(--color-white)) !important;
|
||||
border: 1px solid rgb(var(--grey-200)) !important;
|
||||
box-shadow: var(--box-shadow-base) !important;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
margin-top: 20px !important;
|
||||
}
|
||||
|
||||
.mentions__suggestions__list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 8px !important;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.mentions__suggestions__item {
|
||||
padding: 4px 8px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.mentions__suggestions__item--focused {
|
||||
background-color: rgb(var(--grey-50));
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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 { Mentions } from './Mentions';
|
||||
export type { IMentionsProps } from './Mentions';
|
||||
export { Mention, type MentionProps } from 'react-mentions';
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { createContext, useContext, useEffect, useRef, useState } from 'react';
|
||||
import React, { createContext, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useEvent } from 'rc-util';
|
||||
import styles from './index.module.less';
|
||||
|
||||
@@ -93,27 +93,39 @@ function RectPopup(props: IRectPopupProps) {
|
||||
const { children, anchorRect, direction = 'vertical', onClickOutside, excludeOutside } = props;
|
||||
const nodeRef = useRef<HTMLElement>(null);
|
||||
const clickOtherFn = useEvent(onClickOutside ?? (() => { /* empty */ }));
|
||||
|
||||
const [position, setPosition] = useState<Partial<IAbsolutePosition>>({
|
||||
top: -9999,
|
||||
left: -9999,
|
||||
});
|
||||
|
||||
// TODO@weird94: push the content into the visible area.
|
||||
|
||||
const style = useMemo(() => ({ ...position }), [position]);
|
||||
useEffect(() => {
|
||||
const { clientWidth, clientHeight } = nodeRef.current!;
|
||||
const { innerWidth, innerHeight } = window;
|
||||
setPosition(calcPopupPosition({
|
||||
position: anchorRect,
|
||||
width: clientWidth,
|
||||
height: clientHeight,
|
||||
containerWidth: innerWidth,
|
||||
containerHeight: innerHeight,
|
||||
direction,
|
||||
}));
|
||||
requestAnimationFrame(() => {
|
||||
if (!nodeRef.current) {
|
||||
return;
|
||||
}
|
||||
const { clientWidth, clientHeight } = nodeRef.current;
|
||||
const parent = nodeRef.current.parentElement;
|
||||
if (!parent) {
|
||||
return;
|
||||
}
|
||||
const { clientWidth: innerWidth, clientHeight: innerHeight } = parent;
|
||||
|
||||
setPosition(
|
||||
calcPopupPosition(
|
||||
{
|
||||
position: anchorRect,
|
||||
width: clientWidth,
|
||||
height: clientHeight,
|
||||
containerWidth: innerWidth,
|
||||
containerHeight: innerHeight,
|
||||
direction,
|
||||
}
|
||||
)
|
||||
);
|
||||
});
|
||||
},
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[
|
||||
anchorRect.left,
|
||||
anchorRect.top,
|
||||
@@ -124,12 +136,18 @@ function RectPopup(props: IRectPopupProps) {
|
||||
|
||||
useEffect(() => {
|
||||
const handleClickOther = (e: MouseEvent) => {
|
||||
if (excludeOutside && (excludeOutside.indexOf(e.target as any) > -1)) {
|
||||
if (
|
||||
excludeOutside &&
|
||||
(
|
||||
(excludeOutside.indexOf(e.target as any) > -1) ||
|
||||
excludeOutside.some((item) => item.contains(e.target as any)
|
||||
)
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const x = e.clientX;
|
||||
const y = e.clientY;
|
||||
const x = e.offsetX;
|
||||
const y = e.offsetY;
|
||||
if (x <= anchorRect.right && x >= anchorRect.left && y <= anchorRect.bottom && y >= anchorRect.top) {
|
||||
return;
|
||||
}
|
||||
@@ -146,8 +164,8 @@ function RectPopup(props: IRectPopupProps) {
|
||||
return (
|
||||
<section
|
||||
ref={nodeRef}
|
||||
className={styles.popup}
|
||||
style={position}
|
||||
style={style}
|
||||
className={styles.popupAbsolute}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.popup-absolute {
|
||||
position: absolute;
|
||||
z-index: 1070;
|
||||
z-index: 100;
|
||||
top: -9999px;
|
||||
left: -9999px;
|
||||
}
|
||||
|
||||
@@ -61,3 +61,5 @@ export { enUS, zhCN, ruRU } from './locale';
|
||||
export { type ILocale } from './locale/interface';
|
||||
export { defaultTheme, greenTheme, themeInstance } from './themes';
|
||||
export { DraggableList, type IDraggableListProps } from './components/draggable-list';
|
||||
export { Textarea, type ITextareaProps } from './components/textarea';
|
||||
export { Mentions, type IMentionsProps, Mention, type MentionProps } from './components/mentions';
|
||||
|
||||
@@ -186,7 +186,7 @@ export function serializeRangeToRefString(gridRangeName: IUnitRangeName) {
|
||||
return serializeRange(range);
|
||||
}
|
||||
|
||||
function singleReferenceToGrid(refBody: string) {
|
||||
export function singleReferenceToGrid(refBody: string) {
|
||||
const row = Number.parseInt(refBody.replace($ROW_REGEX, '')) - 1;
|
||||
const column = Tools.ABCatNum(refBody.replace($COLUMN_REGEX, ''));
|
||||
|
||||
|
||||
@@ -84,6 +84,7 @@ export {
|
||||
isReferenceStringWithEffectiveColumn,
|
||||
getRangeWithRefsString,
|
||||
isReferenceStrings,
|
||||
singleReferenceToGrid,
|
||||
} from './engine/utils/reference';
|
||||
export { generateStringWithSequence, type ISequenceNode, sequenceNodeType } from './engine/utils/sequence';
|
||||
export { ArrayValueObject, ValueObjectFactory } from './engine/value-object/array-value-object';
|
||||
|
||||
@@ -206,6 +206,7 @@ export class DataValidationRenderController extends RxDisposable {
|
||||
this._sheetInterceptorService.intercept(
|
||||
INTERCEPTOR_POINT.CELL_CONTENT,
|
||||
{
|
||||
priority: 200,
|
||||
// eslint-disable-next-line max-lines-per-function, complexity
|
||||
handler: (cell, pos, next) => {
|
||||
const { row, col, unitId, subUnitId } = pos;
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
# @univerjs/sheets-thread-comment
|
||||
|
||||
[](https://npmjs.org/packages/@univerjs/sheets-thread-comment)
|
||||
[](https://img.shields.io/npm/l/@univerjs/sheets-thread-comment)
|
||||
|
||||
## Introduction
|
||||
|
||||
> TODO: Introduction
|
||||
|
||||
## Usage
|
||||
|
||||
### Installation
|
||||
|
||||
```shell
|
||||
npm i @univerjs/sheets-thread-comment
|
||||
```
|
||||
@@ -0,0 +1,97 @@
|
||||
{
|
||||
"name": "@univerjs/sheets-thread-comment",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"description": "",
|
||||
"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": [],
|
||||
"sideEffects": [
|
||||
"**/*.css"
|
||||
],
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./*": "./src/*"
|
||||
},
|
||||
"main": "./lib/cjs/index.js",
|
||||
"module": "./lib/es/index.js",
|
||||
"types": "./lib/types/index.d.ts",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"main": "./lib/cjs/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"
|
||||
},
|
||||
"./lib/*": "./lib/*"
|
||||
}
|
||||
},
|
||||
"directories": {
|
||||
"lib": "lib"
|
||||
},
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest",
|
||||
"coverage": "vitest run --coverage",
|
||||
"lint:types": "tsc --noEmit",
|
||||
"build": "tsc && vite build"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@univerjs/core": "workspace:*",
|
||||
"@univerjs/engine-formula": "workspace:*",
|
||||
"@univerjs/engine-render": "workspace:*",
|
||||
"@univerjs/sheets": "workspace:*",
|
||||
"@univerjs/sheets-ui": "workspace:*",
|
||||
"@univerjs/thread-comment": "workspace:*",
|
||||
"@univerjs/thread-comment-ui": "workspace:*",
|
||||
"@univerjs/ui": "workspace:*",
|
||||
"@wendellhu/redi": "^0.15.1",
|
||||
"react": "^16.9.0 || ^17.0.0 || ^18.0.0",
|
||||
"rxjs": ">=7.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@univerjs/icons": "^0.1.45",
|
||||
"@univerjs/thread-comment": "workspace:*",
|
||||
"@univerjs/thread-comment-ui": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@univerjs/core": "workspace:*",
|
||||
"@univerjs/design": "workspace:*",
|
||||
"@univerjs/engine-formula": "workspace:*",
|
||||
"@univerjs/shared": "workspace:*",
|
||||
"@univerjs/thread-comment": "workspace:*",
|
||||
"@univerjs/thread-comment-ui": "workspace:*",
|
||||
"@univerjs/ui": "workspace:*",
|
||||
"@wendellhu/redi": "^0.15.1",
|
||||
"clsx": "^2.1.0",
|
||||
"less": "^4.2.0",
|
||||
"react": "^18.2.0",
|
||||
"rxjs": "^7.8.1",
|
||||
"typescript": "^5.3.3",
|
||||
"vite": "^5.1.4",
|
||||
"vitest": "^1.3.1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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 { CommandType, IUniverInstanceService } from '@univerjs/core';
|
||||
import type { ISheetLocation } from '@univerjs/sheets';
|
||||
import { getSheetCommandTarget, SelectionManagerService } from '@univerjs/sheets';
|
||||
import { ThreadCommentPanelService } from '@univerjs/thread-comment-ui';
|
||||
import { SheetsThreadCommentPopupService } from '../../services/sheets-thread-comment-popup.service';
|
||||
import { SheetsThreadCommentModel } from '../../models/sheets-thread-comment.model';
|
||||
|
||||
export const ShowAddSheetCommentModalOperation: ICommand = {
|
||||
type: CommandType.OPERATION,
|
||||
id: 'sheets.operation.show-comment-modal',
|
||||
handler(accessor) {
|
||||
const selectionManagerService = accessor.get(SelectionManagerService);
|
||||
const univerInstanceService = accessor.get(IUniverInstanceService);
|
||||
|
||||
const sheetsThreadCommentPopupService = accessor.get(SheetsThreadCommentPopupService);
|
||||
const threadCommentPanelService = accessor.get(ThreadCommentPanelService);
|
||||
const activeCell = selectionManagerService.getFirst()?.primary;
|
||||
const current = selectionManagerService.getCurrent();
|
||||
const model = accessor.get(SheetsThreadCommentModel);
|
||||
|
||||
if (!current || !activeCell) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const { unitId, sheetId } = current;
|
||||
const result = getSheetCommandTarget(univerInstanceService, { unitId, subUnitId: sheetId });
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
const { workbook, worksheet } = result;
|
||||
const location: ISheetLocation = {
|
||||
workbook,
|
||||
worksheet,
|
||||
unitId,
|
||||
subUnitId: sheetId,
|
||||
row: activeCell.actualRow,
|
||||
col: activeCell.startColumn,
|
||||
};
|
||||
|
||||
sheetsThreadCommentPopupService.showPopup(location);
|
||||
const rootId = model.getByLocation(unitId, sheetId, activeCell.actualRow, activeCell.startColumn);
|
||||
if (rootId) {
|
||||
threadCommentPanelService.setActiveComment({
|
||||
unitId,
|
||||
subUnitId: sheetId,
|
||||
commentId: rootId,
|
||||
trigger: 'context-menu',
|
||||
});
|
||||
}
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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 { IMenuItem, IShortcutItem } from '@univerjs/ui';
|
||||
import { getMenuHiddenObservable, KeyCode, MenuItemType, MenuPosition, MetaKeys } from '@univerjs/ui';
|
||||
import type { IAccessor } from '@wendellhu/redi';
|
||||
import { ToggleSheetCommentPanelOperation } from '@univerjs/thread-comment-ui';
|
||||
import { UniverInstanceType } from '@univerjs/core';
|
||||
import { whenSheetEditorFocused } from '@univerjs/sheets-ui';
|
||||
import { ShowAddSheetCommentModalOperation } from '../commands/operations/comment.operation';
|
||||
import { COMMENT_SINGLE_ICON } from '../types/const';
|
||||
|
||||
export const threadCommentMenuFactory = (accessor: IAccessor) => {
|
||||
return {
|
||||
id: ShowAddSheetCommentModalOperation.id,
|
||||
type: MenuItemType.BUTTON,
|
||||
positions: [MenuPosition.CONTEXT_MENU],
|
||||
icon: COMMENT_SINGLE_ICON,
|
||||
title: 'sheetThreadComment.menu.addComment',
|
||||
hidden$: getMenuHiddenObservable(accessor, UniverInstanceType.UNIVER_SHEET),
|
||||
} as IMenuItem;
|
||||
};
|
||||
|
||||
export const threadPanelMenuFactory = (accessor: IAccessor) => {
|
||||
return {
|
||||
id: ToggleSheetCommentPanelOperation.id,
|
||||
type: MenuItemType.BUTTON,
|
||||
icon: COMMENT_SINGLE_ICON,
|
||||
tooltip: 'sheetThreadComment.menu.commentManagement',
|
||||
positions: MenuPosition.TOOLBAR_START,
|
||||
hidden$: getMenuHiddenObservable(accessor, UniverInstanceType.UNIVER_SHEET),
|
||||
};
|
||||
};
|
||||
|
||||
export const AddCommentShortcut: IShortcutItem = {
|
||||
id: ShowAddSheetCommentModalOperation.id,
|
||||
binding: KeyCode.M | MetaKeys.CTRL_COMMAND | MetaKeys.ALT,
|
||||
preconditions: whenSheetEditorFocused,
|
||||
};
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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, IUniverInstanceService, LifecycleStages, OnLifecycle, UniverInstanceType } from '@univerjs/core';
|
||||
import { INTERCEPTOR_POINT, SheetInterceptorService } from '@univerjs/sheets';
|
||||
import { Inject } from '@wendellhu/redi';
|
||||
import { SheetSkeletonManagerService } from '@univerjs/sheets-ui';
|
||||
import type { Spreadsheet } from '@univerjs/engine-render';
|
||||
import { IRenderManagerService } from '@univerjs/engine-render';
|
||||
import { SheetsThreadCommentModel } from '../../models/sheets-thread-comment.model';
|
||||
|
||||
@OnLifecycle(LifecycleStages.Ready, SheetsThreadCommentRenderController)
|
||||
export class SheetsThreadCommentRenderController extends Disposable {
|
||||
constructor(
|
||||
@Inject(SheetInterceptorService) private readonly _sheetInterceptorService: SheetInterceptorService,
|
||||
@Inject(SheetsThreadCommentModel) private readonly _sheetsThreadCommentModel: SheetsThreadCommentModel,
|
||||
@IUniverInstanceService private readonly _univerInstanceService: IUniverInstanceService,
|
||||
@Inject(SheetSkeletonManagerService) private readonly _sheetSkeletonManagerService: SheetSkeletonManagerService,
|
||||
@IRenderManagerService private readonly _renderManagerService: IRenderManagerService
|
||||
) {
|
||||
super();
|
||||
this._initViewModelIntercept();
|
||||
this._initSkeletonChange();
|
||||
}
|
||||
|
||||
private _initViewModelIntercept() {
|
||||
this.disposeWithMe(
|
||||
this._sheetInterceptorService.intercept(
|
||||
INTERCEPTOR_POINT.CELL_CONTENT,
|
||||
{
|
||||
handler: (cell, pos, next) => {
|
||||
const { row, col, unitId, subUnitId } = pos;
|
||||
if (this._sheetsThreadCommentModel.showCommentMarker(unitId, subUnitId, row, col)) {
|
||||
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 subUnitId = workbook.getActiveSheet().getSheetId();
|
||||
const skeleton = this._sheetSkeletonManagerService.getOrCreateSkeleton({ unitId, sheetId: subUnitId });
|
||||
const currentRender = this._renderManagerService.getRenderById(unitId);
|
||||
|
||||
skeleton?.makeDirty(true);
|
||||
skeleton?.calculate();
|
||||
|
||||
if (currentRender) {
|
||||
(currentRender.mainComponent as Spreadsheet).makeForceDirty();
|
||||
}
|
||||
};
|
||||
|
||||
this.disposeWithMe(this._sheetsThreadCommentModel.commentUpdate$.subscribe(() => {
|
||||
markSkeletonDirty();
|
||||
}));
|
||||
}
|
||||
}
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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 { IMutationInfo, IRange, Nullable } from '@univerjs/core';
|
||||
import { Disposable, LifecycleStages, OnLifecycle, Range } from '@univerjs/core';
|
||||
import { COPY_TYPE, ISheetClipboardService } from '@univerjs/sheets-ui';
|
||||
import { Inject } from '@wendellhu/redi';
|
||||
import { AddCommentMutation, DeleteCommentMutation, type IThreadComment } from '@univerjs/thread-comment';
|
||||
import { serializeRange, singleReferenceToGrid } from '@univerjs/engine-formula';
|
||||
import { SHEETS_THREAD_COMMENT } from '../types/const';
|
||||
import { SheetsThreadCommentModel } from '../models/sheets-thread-comment.model';
|
||||
|
||||
const transformRef = (ref: string, source: { row: number; column: number }, target: { row: number; column: number }) => {
|
||||
const refObj = singleReferenceToGrid(ref);
|
||||
const offsetRow = target.row - source.row;
|
||||
const offsetCol = target.column - source.column;
|
||||
const targetRange = {
|
||||
startColumn: refObj.column + offsetCol,
|
||||
startRow: refObj.row + offsetRow,
|
||||
endColumn: refObj.column + offsetCol,
|
||||
endRow: refObj.row + offsetRow,
|
||||
};
|
||||
return serializeRange(targetRange);
|
||||
};
|
||||
|
||||
@OnLifecycle(LifecycleStages.Rendered, SheetsThreadCommentCopyPasteController)
|
||||
export class SheetsThreadCommentCopyPasteController extends Disposable {
|
||||
private _copyInfo: Nullable<{
|
||||
unitId: string;
|
||||
subUnitId: string;
|
||||
range: IRange;
|
||||
}>;
|
||||
|
||||
constructor(
|
||||
@Inject(ISheetClipboardService) private _sheetClipboardService: ISheetClipboardService,
|
||||
@Inject(SheetsThreadCommentModel) private _sheetsThreadCommentModel: SheetsThreadCommentModel
|
||||
) {
|
||||
super();
|
||||
this._initClipboardHook();
|
||||
}
|
||||
|
||||
// eslint-disable-next-line max-lines-per-function
|
||||
private _initClipboardHook() {
|
||||
this.disposeWithMe(
|
||||
this._sheetClipboardService.addClipboardHook({
|
||||
id: SHEETS_THREAD_COMMENT,
|
||||
onBeforeCopy: (unitId, subUnitId, range) => {
|
||||
this._copyInfo = {
|
||||
unitId,
|
||||
subUnitId,
|
||||
range,
|
||||
};
|
||||
},
|
||||
|
||||
// eslint-disable-next-line max-lines-per-function
|
||||
onPasteCells: (_pasteFrom, pasteTo, _data, payload) => {
|
||||
const { unitId: targetUnitId, subUnitId: targetSubUnitId, range } = pasteTo;
|
||||
const targetPos = {
|
||||
row: range.rows[0],
|
||||
column: range.cols[0],
|
||||
};
|
||||
if (payload.copyType === COPY_TYPE.CUT && this._copyInfo) {
|
||||
const { range, unitId: sourceUnitId, subUnitId: sourceSubUnitId } = this._copyInfo;
|
||||
const sourcePos = {
|
||||
row: range.startRow,
|
||||
column: range.startColumn,
|
||||
};
|
||||
if (!(targetUnitId === sourceUnitId && targetSubUnitId === sourceSubUnitId)) {
|
||||
const roots: { root: IThreadComment; children: IThreadComment[] }[] = [];
|
||||
|
||||
Range.foreach(range, (row, col) => {
|
||||
const root = this._sheetsThreadCommentModel.getCommentWithChildren(sourceUnitId, sourceSubUnitId, row, col);
|
||||
if (root) {
|
||||
roots.push(root);
|
||||
}
|
||||
});
|
||||
|
||||
const sourceRedos: IMutationInfo[] = [];
|
||||
const sourceUndos: IMutationInfo[] = [];
|
||||
const targetRedos: IMutationInfo[] = [];
|
||||
const targetUndos: IMutationInfo[] = [];
|
||||
|
||||
const handleCommentItem = (item: IThreadComment) => {
|
||||
sourceRedos.unshift({
|
||||
id: DeleteCommentMutation.id,
|
||||
params: {
|
||||
unitId: sourceUnitId,
|
||||
subUnitId: sourceSubUnitId,
|
||||
commentId: item.id,
|
||||
},
|
||||
});
|
||||
targetRedos.push({
|
||||
id: AddCommentMutation.id,
|
||||
params: {
|
||||
unitId: targetUnitId,
|
||||
subUnitId: targetSubUnitId,
|
||||
comment: {
|
||||
...item,
|
||||
ref: transformRef(item.ref, sourcePos, targetPos),
|
||||
unitId: targetUnitId,
|
||||
subUnitId: targetSubUnitId,
|
||||
},
|
||||
},
|
||||
});
|
||||
sourceUndos.push({
|
||||
id: AddCommentMutation.id,
|
||||
params: {
|
||||
unitId: sourceUnitId,
|
||||
subUnitId: sourceSubUnitId,
|
||||
comment: item,
|
||||
},
|
||||
});
|
||||
targetUndos.unshift({
|
||||
id: DeleteCommentMutation.id,
|
||||
params: {
|
||||
unitId: targetUnitId,
|
||||
subUnitId: targetSubUnitId,
|
||||
commentId: item.id,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
roots.forEach((root) => {
|
||||
handleCommentItem(root.root);
|
||||
root.children.forEach((child) => {
|
||||
handleCommentItem(child);
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
redos: [...sourceRedos, ...targetRedos],
|
||||
undos: [...targetUndos, ...sourceUndos],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
redos: [],
|
||||
undos: [],
|
||||
};
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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, LifecycleStages, OnLifecycle } from '@univerjs/core';
|
||||
import { HoverManagerService } from '@univerjs/sheets-ui';
|
||||
import { Inject } from '@wendellhu/redi';
|
||||
import { SheetsThreadCommentPopupService } from '../services/sheets-thread-comment-popup.service';
|
||||
import { SheetsThreadCommentModel } from '../models/sheets-thread-comment.model';
|
||||
|
||||
@OnLifecycle(LifecycleStages.Rendered, SheetsThreadCommentHoverController)
|
||||
export class SheetsThreadCommentHoverController extends Disposable {
|
||||
constructor(
|
||||
@Inject(HoverManagerService) private readonly _hoverManagerService: HoverManagerService,
|
||||
@Inject(SheetsThreadCommentPopupService) private readonly _sheetsThreadCommentPopupService: SheetsThreadCommentPopupService,
|
||||
@Inject(SheetsThreadCommentModel) private readonly _sheetsThreadCommentModel: SheetsThreadCommentModel
|
||||
) {
|
||||
super();
|
||||
this._initHoverEvent();
|
||||
}
|
||||
|
||||
private _initHoverEvent() {
|
||||
this._hoverManagerService.currentCell$.subscribe((cell) => {
|
||||
const currentPopup = this._sheetsThreadCommentPopupService.activePopup;
|
||||
if (cell && ((currentPopup && currentPopup.temp) || !currentPopup)) {
|
||||
const { location } = cell;
|
||||
const { unitId, subUnitId, row, col } = location;
|
||||
const commentId = this._sheetsThreadCommentModel.getByLocation(unitId, subUnitId, row, col);
|
||||
|
||||
if (commentId) {
|
||||
const comment = this._sheetsThreadCommentModel.getComment(unitId, subUnitId, commentId);
|
||||
if (comment && !comment.resolved) {
|
||||
this._sheetsThreadCommentPopupService.showPopup({
|
||||
unitId,
|
||||
subUnitId,
|
||||
row,
|
||||
col,
|
||||
commentId,
|
||||
temp: true,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (currentPopup) {
|
||||
this._sheetsThreadCommentPopupService.hidePopup();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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 { IRange } from '@univerjs/core';
|
||||
import { Disposable, LifecycleStages, OnLifecycle, toDisposable } from '@univerjs/core';
|
||||
import type { IDisposable } from '@wendellhu/redi';
|
||||
import { Inject } from '@wendellhu/redi';
|
||||
import type { EffectRefRangeParams } from '@univerjs/sheets';
|
||||
import { handleDefaultRangeChangeWithEffectRefCommands, RefRangeService } from '@univerjs/sheets';
|
||||
import type { IAddCommentMutationParams, IUpdateCommentRefMutationParams } from '@univerjs/thread-comment';
|
||||
import { AddCommentMutation, DeleteCommentMutation, ThreadCommentModel, UpdateCommentRefMutation } from '@univerjs/thread-comment';
|
||||
import { serializeRange, singleReferenceToGrid } from '@univerjs/engine-formula';
|
||||
import type { ISheetThreadComment } from '../types/interfaces/i-sheet-thread-comment';
|
||||
import { SheetsThreadCommentModel } from '../models/sheets-thread-comment.model';
|
||||
|
||||
@OnLifecycle(LifecycleStages.Starting, SheetsThreadCommentRefRangeController)
|
||||
export class SheetsThreadCommentRefRangeController extends Disposable {
|
||||
private _disposableMap = new Map<string, IDisposable>();
|
||||
|
||||
constructor(
|
||||
@Inject(RefRangeService) private readonly _refRangeService: RefRangeService,
|
||||
@Inject(SheetsThreadCommentModel) private readonly _sheetsThreadCommentModel: SheetsThreadCommentModel,
|
||||
@Inject(ThreadCommentModel) private readonly _threadCommentModel: ThreadCommentModel
|
||||
) {
|
||||
super();
|
||||
this._initData();
|
||||
this._initRefRange();
|
||||
}
|
||||
|
||||
private _getIdWithUnitId(unitId: string, subUnitId: string, id: string) {
|
||||
return `${unitId}-${subUnitId}-${id}`;
|
||||
}
|
||||
|
||||
private _register(unitId: string, subUnitId: string, comment: ISheetThreadComment) {
|
||||
const commentId = comment.id;
|
||||
const oldRange: IRange = {
|
||||
startColumn: comment.column,
|
||||
endColumn: comment.column,
|
||||
startRow: comment.row,
|
||||
endRow: comment.row,
|
||||
};
|
||||
|
||||
const handleRangeChange = (commandInfo: EffectRefRangeParams) => {
|
||||
const resultRange = handleDefaultRangeChangeWithEffectRefCommands(oldRange, commandInfo);
|
||||
if (resultRange && resultRange.startColumn === oldRange.startColumn && resultRange.startRow === oldRange.startRow) {
|
||||
return {
|
||||
undos: [],
|
||||
redos: [],
|
||||
};
|
||||
}
|
||||
|
||||
if (!resultRange) {
|
||||
return {
|
||||
redos: [{
|
||||
id: DeleteCommentMutation.id,
|
||||
params: {
|
||||
unitId,
|
||||
subUnitId,
|
||||
commentId,
|
||||
},
|
||||
}],
|
||||
undos: [{
|
||||
id: AddCommentMutation.id,
|
||||
params: {
|
||||
unitId,
|
||||
subUnitId,
|
||||
comment,
|
||||
} as IAddCommentMutationParams,
|
||||
}],
|
||||
};
|
||||
}
|
||||
return {
|
||||
redos: [{
|
||||
id: UpdateCommentRefMutation.id,
|
||||
params: {
|
||||
unitId,
|
||||
subUnitId,
|
||||
payload: {
|
||||
ref: serializeRange(resultRange),
|
||||
commentId,
|
||||
},
|
||||
} as IUpdateCommentRefMutationParams,
|
||||
}],
|
||||
undos: [{
|
||||
id: UpdateCommentRefMutation.id,
|
||||
params: {
|
||||
unitId,
|
||||
subUnitId,
|
||||
payload: {
|
||||
ref: serializeRange(oldRange),
|
||||
commentId,
|
||||
},
|
||||
} as IUpdateCommentRefMutationParams,
|
||||
}],
|
||||
};
|
||||
};
|
||||
|
||||
this._disposableMap.set(
|
||||
this._getIdWithUnitId(unitId, subUnitId, commentId),
|
||||
this._refRangeService.registerRefRange(oldRange, handleRangeChange, unitId, subUnitId)
|
||||
);
|
||||
}
|
||||
|
||||
private _initData() {
|
||||
const data = this._threadCommentModel.getAll();
|
||||
|
||||
for (const unitId in data) {
|
||||
const unitMap = data[unitId];
|
||||
for (const subUnitId in unitMap) {
|
||||
const subUnitMap = unitMap[subUnitId];
|
||||
for (const id in subUnitMap) {
|
||||
const comment = subUnitMap[id];
|
||||
const ref = comment.ref;
|
||||
const pos = singleReferenceToGrid(ref);
|
||||
this._register(unitId, subUnitId, {
|
||||
...comment,
|
||||
...pos,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _initRefRange() {
|
||||
this.disposeWithMe(
|
||||
this._sheetsThreadCommentModel.commentUpdate$.subscribe((option) => {
|
||||
const { unitId, subUnitId } = option;
|
||||
switch (option.type) {
|
||||
case 'add': {
|
||||
const comment = option.payload;
|
||||
this._register(option.unitId, option.subUnitId, {
|
||||
...comment,
|
||||
row: option.row,
|
||||
column: option.column,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'delete': {
|
||||
const disposable = this._disposableMap.get(this._getIdWithUnitId(unitId, subUnitId, option.payload.commentId));
|
||||
disposable?.dispose();
|
||||
break;
|
||||
}
|
||||
case 'updateRef': {
|
||||
const comment = this._sheetsThreadCommentModel.getComment(unitId, subUnitId, option.payload.commentId);
|
||||
if (!comment) {
|
||||
return;
|
||||
}
|
||||
|
||||
const disposable = this._disposableMap.get(this._getIdWithUnitId(unitId, subUnitId, comment.id));
|
||||
disposable?.dispose();
|
||||
this._register(option.unitId, option.subUnitId, {
|
||||
...comment,
|
||||
row: option.row,
|
||||
column: option.column,
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
this.disposeWithMe(toDisposable(() => {
|
||||
this._disposableMap.forEach((item) => {
|
||||
item.dispose();
|
||||
});
|
||||
this._disposableMap.clear();
|
||||
}));
|
||||
}
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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, IUniverInstanceService, LifecycleStages, OnLifecycle, UniverInstanceType } from '@univerjs/core';
|
||||
import { Inject } from '@wendellhu/redi';
|
||||
import type { IRemoveSheetCommandParams } from '@univerjs/sheets';
|
||||
import { RemoveSheetCommand, SheetInterceptorService } from '@univerjs/sheets';
|
||||
import type { IDeleteCommentMutationParams } from '@univerjs/thread-comment';
|
||||
import { AddCommentMutation, DeleteCommentMutation, ThreadCommentModel } from '@univerjs/thread-comment';
|
||||
|
||||
@OnLifecycle(LifecycleStages.Ready, ThreadCommentRemoveSheetsController)
|
||||
export class ThreadCommentRemoveSheetsController extends Disposable {
|
||||
constructor(
|
||||
@Inject(SheetInterceptorService) private _sheetInterceptorService: SheetInterceptorService,
|
||||
@IUniverInstanceService private _univerInstanceService: IUniverInstanceService,
|
||||
@Inject(ThreadCommentModel) private _threadCommentModel: ThreadCommentModel
|
||||
) {
|
||||
super();
|
||||
this._initSheetChange();
|
||||
}
|
||||
|
||||
private _initSheetChange() {
|
||||
this.disposeWithMe(
|
||||
this._sheetInterceptorService.interceptCommand({
|
||||
getMutations: (commandInfo) => {
|
||||
if (commandInfo.id === RemoveSheetCommand.id) {
|
||||
const params = commandInfo.params as IRemoveSheetCommandParams;
|
||||
const workbook = params.unitId ? this._univerInstanceService.getUnit<Workbook>(params.unitId) : this._univerInstanceService.getCurrentUnitForType<Workbook>(UniverInstanceType.UNIVER_SHEET);
|
||||
if (!workbook) {
|
||||
return { redos: [], undos: [] };
|
||||
}
|
||||
const unitId = workbook.getUnitId();
|
||||
const subUnitId = params.subUnitId || workbook.getActiveSheet().getSheetId();
|
||||
const { commentMap } = this._threadCommentModel.ensureMap(unitId, subUnitId);
|
||||
|
||||
const ids = Array.from(Object.keys(commentMap));
|
||||
const comments = Array.from(Object.values(commentMap));
|
||||
const redos = ids.map((id) => ({
|
||||
id: DeleteCommentMutation.id,
|
||||
params: {
|
||||
unitId,
|
||||
subUnitId,
|
||||
commentId: id,
|
||||
} as IDeleteCommentMutationParams,
|
||||
}));
|
||||
|
||||
const undos = comments.map((comment) => ({
|
||||
id: AddCommentMutation.id,
|
||||
params: {
|
||||
unitId,
|
||||
subUnitId,
|
||||
comment,
|
||||
},
|
||||
}));
|
||||
|
||||
return { redos, undos };
|
||||
}
|
||||
return { redos: [], undos: [] };
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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, ICommandService, IUniverInstanceService, LifecycleStages, LocaleService, OnLifecycle, UniverInstanceType } from '@univerjs/core';
|
||||
import { ComponentManager, IMenuService, IShortcutService } from '@univerjs/ui';
|
||||
import { Inject, Injector } from '@wendellhu/redi';
|
||||
import { CommentSingle } from '@univerjs/icons';
|
||||
import { SetActiveCommentOperation, THREAD_COMMENT_PANEL, ThreadCommentPanelService } from '@univerjs/thread-comment-ui';
|
||||
import type { ISetSelectionsOperationParams } from '@univerjs/sheets';
|
||||
import { SelectionMoveType, SetSelectionsOperation, SetWorksheetActiveOperation } from '@univerjs/sheets';
|
||||
import { singleReferenceToGrid } from '@univerjs/engine-formula';
|
||||
import { ScrollToCellCommand } from '@univerjs/sheets-ui';
|
||||
import type { IDeleteCommentMutationParams } from '@univerjs/thread-comment';
|
||||
import { DeleteCommentMutation } from '@univerjs/thread-comment';
|
||||
import { SheetsThreadCommentCell } from '../views/sheets-thread-comment-cell';
|
||||
import { COMMENT_SINGLE_ICON, SHEETS_THREAD_COMMENT_MODAL } from '../types/const';
|
||||
import { SheetsThreadCommentPanel } from '../views/sheets-thread-comment-panel';
|
||||
import { enUS, zhCN } from '../locales';
|
||||
import { SheetsThreadCommentPopupService } from '../services/sheets-thread-comment-popup.service';
|
||||
import { SheetsThreadCommentModel } from '../models/sheets-thread-comment.model';
|
||||
import { AddCommentShortcut, threadCommentMenuFactory, threadPanelMenuFactory } from './menu';
|
||||
|
||||
@OnLifecycle(LifecycleStages.Starting, SheetsThreadCommentController)
|
||||
export class SheetsThreadCommentController extends Disposable {
|
||||
constructor(
|
||||
@IMenuService private readonly _menuService: IMenuService,
|
||||
@Inject(Injector) private readonly _injector: Injector,
|
||||
@Inject(ComponentManager) private readonly _componentManager: ComponentManager,
|
||||
@Inject(LocaleService) private readonly _localeService: LocaleService,
|
||||
@ICommandService private readonly _commandService: ICommandService,
|
||||
@Inject(SheetsThreadCommentPopupService) private readonly _sheetsThreadCommentPopupService: SheetsThreadCommentPopupService,
|
||||
@Inject(SheetsThreadCommentModel) private readonly _sheetsThreadCommentModel: SheetsThreadCommentModel,
|
||||
@Inject(ThreadCommentPanelService) private readonly _threadCommentPanelService: ThreadCommentPanelService,
|
||||
@IShortcutService private readonly _shortcutService: IShortcutService,
|
||||
@IUniverInstanceService private readonly _univerInstanceService: IUniverInstanceService
|
||||
) {
|
||||
super();
|
||||
this._initMenu();
|
||||
this._initShortcut();
|
||||
this._initComponent();
|
||||
this._initLocale();
|
||||
this._initCommandListener();
|
||||
this._initPanelListener();
|
||||
}
|
||||
|
||||
private _initShortcut() {
|
||||
this._shortcutService.registerShortcut(AddCommentShortcut);
|
||||
}
|
||||
|
||||
private _initCommandListener() {
|
||||
this._commandService.onCommandExecuted((commandInfo) => {
|
||||
if (commandInfo.id === SetSelectionsOperation.id) {
|
||||
const params = commandInfo.params as ISetSelectionsOperationParams;
|
||||
const { unitId, subUnitId, selections, type } = params;
|
||||
if ((type === SelectionMoveType.MOVE_END || type === undefined) && selections[0].primary) {
|
||||
const row = selections[0].primary.actualRow;
|
||||
const col = selections[0].primary.actualColumn;
|
||||
if (!this._sheetsThreadCommentModel.showCommentMarker(unitId, subUnitId, row, col)) {
|
||||
if (this._threadCommentPanelService.activeCommentId) {
|
||||
this._commandService.executeCommand(SetActiveCommentOperation.id);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const commentId = this._sheetsThreadCommentModel.getByLocation(unitId, subUnitId, row, col);
|
||||
if (commentId) {
|
||||
this._commandService.executeCommand(SetActiveCommentOperation.id, {
|
||||
unitId,
|
||||
subUnitId,
|
||||
commentId,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (commandInfo.id === DeleteCommentMutation.id) {
|
||||
const params = commandInfo.params as IDeleteCommentMutationParams;
|
||||
const active = this._sheetsThreadCommentPopupService.activePopup;
|
||||
if (!active) {
|
||||
return;
|
||||
}
|
||||
const { unitId, subUnitId, commentId } = active;
|
||||
if (params.unitId === unitId && params.subUnitId === subUnitId && params.commentId === commentId) {
|
||||
this._sheetsThreadCommentPopupService.hidePopup();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _initMenu() {
|
||||
[
|
||||
threadCommentMenuFactory,
|
||||
threadPanelMenuFactory,
|
||||
].forEach((menuFactory) => {
|
||||
this._menuService.addMenuItem(menuFactory(this._injector));
|
||||
});
|
||||
}
|
||||
|
||||
private _initComponent() {
|
||||
([
|
||||
[SHEETS_THREAD_COMMENT_MODAL, SheetsThreadCommentCell],
|
||||
[THREAD_COMMENT_PANEL, SheetsThreadCommentPanel],
|
||||
[COMMENT_SINGLE_ICON, CommentSingle],
|
||||
] as const).forEach(([key, comp]) => {
|
||||
this._componentManager.register(key, comp);
|
||||
});
|
||||
}
|
||||
|
||||
private _initLocale() {
|
||||
this._localeService.load({
|
||||
zhCN,
|
||||
enUS,
|
||||
});
|
||||
}
|
||||
|
||||
private _initPanelListener() {
|
||||
this.disposeWithMe(this._threadCommentPanelService.activeCommentId$.subscribe(async (commentInfo) => {
|
||||
if (commentInfo) {
|
||||
const { unitId, subUnitId, commentId, trigger } = commentInfo;
|
||||
const comment = this._sheetsThreadCommentModel.getComment(unitId, subUnitId, commentId);
|
||||
if (!comment || comment.resolved) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentUnit = this._univerInstanceService.getCurrentUnitForType<Workbook>(UniverInstanceType.UNIVER_SHEET);
|
||||
if (!currentUnit) {
|
||||
return;
|
||||
}
|
||||
const currentUnitId = currentUnit.getUnitId();
|
||||
if (currentUnitId !== unitId) {
|
||||
return;
|
||||
}
|
||||
const currentSheetId = currentUnit.getActiveSheet().getSheetId();
|
||||
if (currentSheetId !== subUnitId) {
|
||||
await this._commandService.executeCommand(SetWorksheetActiveOperation.id, {
|
||||
unitId,
|
||||
subUnitId,
|
||||
});
|
||||
}
|
||||
|
||||
const location = singleReferenceToGrid(comment.ref);
|
||||
await this._commandService.executeCommand(ScrollToCellCommand.id, {
|
||||
range: {
|
||||
startColumn: location.column,
|
||||
endColumn: location.column,
|
||||
startRow: location.row,
|
||||
endRow: location.row,
|
||||
},
|
||||
});
|
||||
this._sheetsThreadCommentPopupService.showPopup({
|
||||
unitId,
|
||||
subUnitId,
|
||||
row: location.row,
|
||||
col: location.column,
|
||||
commentId: comment.id,
|
||||
trigger,
|
||||
});
|
||||
} else {
|
||||
this._sheetsThreadCommentPopupService.hidePopup();
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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 type { Dependency } from '@wendellhu/redi';
|
||||
export { Inject, Injector } from '@wendellhu/redi';
|
||||
export { ICommandService, Plugin, UniverInstanceType } from '@univerjs/core';
|
||||
export { UniverThreadCommentUIPlugin } from '@univerjs/thread-comment-ui';
|
||||
export { SheetsThreadCommentController } from './controllers/sheets-thread-comment.controller';
|
||||
export { SheetsThreadCommentRefRangeController } from './controllers/sheets-thread-comment-ref-range.controller';
|
||||
export { SheetsThreadCommentModel } from './models/sheets-thread-comment.model';
|
||||
export { SheetsThreadCommentPopupService } from './services/sheets-thread-comment-popup.service';
|
||||
export { UniverSheetsThreadCommentPlugin } from './plugin';
|
||||
export { SHEETS_THREAD_COMMENT } from './types/const';
|
||||
export { SheetsThreadCommentCopyPasteController } from './controllers/sheets-thread-comment-copy-paste.controller';
|
||||
export { SheetsThreadCommentHoverController } from './controllers/sheets-thread-comment-hover.controller';
|
||||
export { ThreadCommentRemoveSheetsController } from './controllers/sheets-thread-comment-remove.controller';
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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 {
|
||||
sheetThreadComment: {
|
||||
menu: {
|
||||
addComment: 'Add Comment',
|
||||
commentManagement: 'Comment Management',
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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 zhCN } from './zhCN';
|
||||
export { default as enUS } from './enUS';
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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 {
|
||||
sheetThreadComment: {
|
||||
menu: {
|
||||
addComment: '添加评论',
|
||||
commentManagement: '评论管理',
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,234 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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, IUniverInstanceService, ObjectMatrix, UniverInstanceType } from '@univerjs/core';
|
||||
import type { CommentUpdate, IThreadComment } from '@univerjs/thread-comment';
|
||||
import { ThreadCommentModel } from '@univerjs/thread-comment';
|
||||
import { Inject } from '@wendellhu/redi';
|
||||
import { singleReferenceToGrid } from '@univerjs/engine-formula';
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
export type SheetCommentUpdate = CommentUpdate & {
|
||||
row: number;
|
||||
column: number;
|
||||
};
|
||||
|
||||
export class SheetsThreadCommentModel extends Disposable {
|
||||
private _matrixMap: Map<string, Map<string, ObjectMatrix<string>>> = new Map();
|
||||
private _locationMap: Map<string, Map<string, Map<string, { row: number; column: number }>>> = new Map();
|
||||
private _commentUpdate$ = new Subject<SheetCommentUpdate>();
|
||||
|
||||
commentUpdate$ = this._commentUpdate$.asObservable();
|
||||
|
||||
constructor(
|
||||
@Inject(ThreadCommentModel) private readonly _threadCommentModel: ThreadCommentModel,
|
||||
@IUniverInstanceService private readonly _univerInstanceService: IUniverInstanceService
|
||||
) {
|
||||
super();
|
||||
this._init();
|
||||
}
|
||||
|
||||
private _init() {
|
||||
this._initData();
|
||||
this._initUpdateTransform();
|
||||
}
|
||||
|
||||
private _ensureCommentMatrix(unitId: string, subUnitId: string) {
|
||||
let unitMap = this._matrixMap.get(unitId);
|
||||
|
||||
if (!unitMap) {
|
||||
unitMap = new Map();
|
||||
this._matrixMap.set(unitId, unitMap);
|
||||
}
|
||||
|
||||
let subUnitMap = unitMap.get(subUnitId);
|
||||
if (!subUnitMap) {
|
||||
subUnitMap = new ObjectMatrix();
|
||||
unitMap.set(subUnitId, subUnitMap);
|
||||
}
|
||||
|
||||
return subUnitMap;
|
||||
}
|
||||
|
||||
private _ensureCommentLocationMap(unitId: string, subUnitId: string) {
|
||||
let unitMap = this._locationMap.get(unitId);
|
||||
|
||||
if (!unitMap) {
|
||||
unitMap = new Map();
|
||||
this._locationMap.set(unitId, unitMap);
|
||||
}
|
||||
|
||||
let subUnitMap = unitMap.get(subUnitId);
|
||||
if (!subUnitMap) {
|
||||
subUnitMap = new Map();
|
||||
unitMap.set(subUnitId, subUnitMap);
|
||||
}
|
||||
|
||||
return subUnitMap;
|
||||
}
|
||||
|
||||
private _ensure(unitId: string, subUnitId: string) {
|
||||
const matrix = this._ensureCommentMatrix(unitId, subUnitId);
|
||||
const locationMap = this._ensureCommentLocationMap(unitId, subUnitId);
|
||||
return { matrix, locationMap };
|
||||
}
|
||||
|
||||
private _initData() {
|
||||
const data = this._threadCommentModel.getAll();
|
||||
|
||||
for (const unitId in data) {
|
||||
const unitMap = data[unitId];
|
||||
for (const subUnitId in unitMap) {
|
||||
const subUnitMap = unitMap[subUnitId];
|
||||
for (const id in subUnitMap) {
|
||||
const comment = subUnitMap[id];
|
||||
this._addComment(unitId, subUnitId, comment);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _addComment(unitId: string, subUnitId: string, comment: IThreadComment) {
|
||||
const location = singleReferenceToGrid(comment.ref);
|
||||
const parentId = comment.parentId;
|
||||
const { row, column } = location;
|
||||
const commentId = comment.id;
|
||||
const { matrix, locationMap } = this._ensure(unitId, subUnitId);
|
||||
|
||||
if (!parentId && row >= 0 && column >= 0) {
|
||||
matrix.setValue(row, column, commentId);
|
||||
locationMap.set(commentId, { row, column });
|
||||
}
|
||||
|
||||
this._commentUpdate$.next({
|
||||
unitId,
|
||||
subUnitId,
|
||||
payload: comment,
|
||||
type: 'add',
|
||||
...location,
|
||||
});
|
||||
}
|
||||
|
||||
private _initUpdateTransform() {
|
||||
this.disposeWithMe(this._threadCommentModel.commentUpdate$.subscribe((update) => {
|
||||
const { unitId, subUnitId } = update;
|
||||
|
||||
try {
|
||||
const type = this._univerInstanceService.getUnitType(unitId);
|
||||
if (type !== UniverInstanceType.UNIVER_SHEET) {
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
const { matrix, locationMap } = this._ensure(unitId, subUnitId);
|
||||
switch (update.type) {
|
||||
case 'add': {
|
||||
this._addComment(update.unitId, update.subUnitId, update.payload);
|
||||
break;
|
||||
}
|
||||
case 'delete': {
|
||||
const { isRoot, comment } = update.payload;
|
||||
const location = singleReferenceToGrid(comment.ref);
|
||||
if (isRoot) {
|
||||
const { row, column } = location;
|
||||
if (row >= 0 && column >= 0) {
|
||||
matrix.realDeleteValue(row, column);
|
||||
}
|
||||
}
|
||||
this._commentUpdate$.next({
|
||||
...update,
|
||||
...location,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'update': {
|
||||
const { commentId } = update.payload;
|
||||
const comment = this._threadCommentModel.getComment(unitId, subUnitId, commentId);
|
||||
if (!comment) {
|
||||
return;
|
||||
}
|
||||
const location = singleReferenceToGrid(comment.ref);
|
||||
this._commentUpdate$.next({
|
||||
...update,
|
||||
...location,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'updateRef': {
|
||||
const location = singleReferenceToGrid(update.payload.ref);
|
||||
const { commentId } = update.payload;
|
||||
const currentLoc = locationMap.get(commentId);
|
||||
if (!currentLoc) {
|
||||
return;
|
||||
}
|
||||
const { row, column } = currentLoc;
|
||||
const currentCommentId = matrix.getValue(row, column);
|
||||
if (currentCommentId === commentId) {
|
||||
matrix.realDeleteValue(row, column);
|
||||
locationMap.delete(commentId);
|
||||
}
|
||||
if (location.row >= 0 && location.column >= 0) {
|
||||
matrix.setValue(location.row, location.column, commentId);
|
||||
locationMap.set(commentId, { row: location.row, column: location.column });
|
||||
}
|
||||
|
||||
this._commentUpdate$.next({
|
||||
...update,
|
||||
...location,
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
getByLocation(unitId: string, subUnitId: string, row: number, column: number): string | undefined {
|
||||
const matrix = this._ensureCommentMatrix(unitId, subUnitId);
|
||||
return matrix.getValue(row, column);
|
||||
}
|
||||
|
||||
getComment(unitId: string, subUnitId: string, commentId: string) {
|
||||
return this._threadCommentModel.getComment(unitId, subUnitId, commentId);
|
||||
}
|
||||
|
||||
getCommentWithChildren(unitId: string, subUnitId: string, row: number, column: number) {
|
||||
const matrix = this._ensureCommentMatrix(unitId, subUnitId);
|
||||
const commentId = matrix.getValue(row, column);
|
||||
if (!commentId) {
|
||||
return undefined;
|
||||
}
|
||||
return this._threadCommentModel.getCommentWithChildren(unitId, subUnitId, commentId);
|
||||
}
|
||||
|
||||
showCommentMarker(unitId: string, subUnitId: string, row: number, column: number) {
|
||||
const commentId = this.getByLocation(unitId, subUnitId, row, column);
|
||||
if (!commentId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const comment = this.getComment(unitId, subUnitId, commentId);
|
||||
if (comment && !comment.resolved) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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 '@wendellhu/redi';
|
||||
import { Inject, Injector } from '@wendellhu/redi';
|
||||
import { ICommandService, IConfigService, UniverInstanceType } from '@univerjs/core';
|
||||
import type { IThreadCommentUIConfig } from '@univerjs/thread-comment-ui';
|
||||
import { UniverThreadCommentUIPlugin } from '@univerjs/thread-comment-ui';
|
||||
import { SheetsThreadCommentController } from './controllers/sheets-thread-comment.controller';
|
||||
import { SheetsThreadCommentRefRangeController } from './controllers/sheets-thread-comment-ref-range.controller';
|
||||
import { SheetsThreadCommentModel } from './models/sheets-thread-comment.model';
|
||||
import { SheetsThreadCommentPopupService } from './services/sheets-thread-comment-popup.service';
|
||||
import { ShowAddSheetCommentModalOperation } from './commands/operations/comment.operation';
|
||||
import { SheetsThreadCommentRenderController } from './controllers/render-controllers/render.controller';
|
||||
import { SHEETS_THREAD_COMMENT } from './types/const';
|
||||
import { SheetsThreadCommentCopyPasteController } from './controllers/sheets-thread-comment-copy-paste.controller';
|
||||
import { SheetsThreadCommentHoverController } from './controllers/sheets-thread-comment-hover.controller';
|
||||
import { ThreadCommentRemoveSheetsController } from './controllers/sheets-thread-comment-remove.controller';
|
||||
|
||||
const defaultConfig: IThreadCommentUIConfig = {
|
||||
mentions: [{
|
||||
trigger: '@',
|
||||
async getMentions() {
|
||||
return [{
|
||||
id: 'mock',
|
||||
label: 'MockUser',
|
||||
type: 'user',
|
||||
}];
|
||||
},
|
||||
}],
|
||||
};
|
||||
|
||||
export class UniverSheetsThreadCommentPlugin extends UniverThreadCommentUIPlugin {
|
||||
static override pluginName = SHEETS_THREAD_COMMENT;
|
||||
static override type = UniverInstanceType.UNIVER_SHEET;
|
||||
|
||||
constructor(
|
||||
_config: IThreadCommentUIConfig = defaultConfig,
|
||||
@Inject(Injector) protected override _injector: Injector,
|
||||
@Inject(ICommandService) protected override _commandService: ICommandService,
|
||||
@Inject(IConfigService) protected override _configService: IConfigService
|
||||
) {
|
||||
super(_config, _injector, _commandService, _configService);
|
||||
}
|
||||
|
||||
override onStarting(injector: Injector): void {
|
||||
super.onStarting(injector);
|
||||
([
|
||||
[SheetsThreadCommentModel],
|
||||
[SheetsThreadCommentController],
|
||||
[SheetsThreadCommentRefRangeController],
|
||||
[SheetsThreadCommentRenderController],
|
||||
[SheetsThreadCommentCopyPasteController],
|
||||
[SheetsThreadCommentHoverController],
|
||||
[ThreadCommentRemoveSheetsController],
|
||||
|
||||
[SheetsThreadCommentPopupService],
|
||||
] as Dependency[]).forEach((dep) => {
|
||||
this._injector.add(dep);
|
||||
});
|
||||
|
||||
[ShowAddSheetCommentModalOperation].forEach((command) => {
|
||||
this._commandService.registerCommand(command);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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 { Disposable, DisposableCollection } from '@univerjs/core';
|
||||
import type { ISheetLocationBase } from '@univerjs/sheets';
|
||||
import { SheetCanvasPopManagerService } from '@univerjs/sheets-ui';
|
||||
import { IZenZoneService } from '@univerjs/ui';
|
||||
import { type IDisposable, Inject } from '@wendellhu/redi';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { SHEETS_THREAD_COMMENT_MODAL } from '../types/const';
|
||||
|
||||
export interface IThreadCommentPopup extends ISheetLocationBase {
|
||||
commentId?: string;
|
||||
// when triggered by hover, temp is set to be `true`
|
||||
temp?: boolean;
|
||||
trigger?: string;
|
||||
}
|
||||
|
||||
export class SheetsThreadCommentPopupService extends Disposable {
|
||||
private _lastPopup: Nullable<IDisposable> = null;
|
||||
private _activePopup: Nullable<IThreadCommentPopup>;
|
||||
private _activePopup$ = new BehaviorSubject<Nullable<IThreadCommentPopup>>(null);
|
||||
|
||||
activePopup$ = this._activePopup$.asObservable();
|
||||
|
||||
get activePopup() {
|
||||
return this._activePopup;
|
||||
}
|
||||
|
||||
constructor(
|
||||
@Inject(SheetCanvasPopManagerService) private readonly _canvasPopupManagerService: SheetCanvasPopManagerService,
|
||||
@IZenZoneService private readonly _zenZoneService: IZenZoneService
|
||||
) {
|
||||
super();
|
||||
this._initZenVisible();
|
||||
}
|
||||
|
||||
private _initZenVisible() {
|
||||
this.disposeWithMe(this._zenZoneService.visible$.subscribe((visible) => {
|
||||
if (visible) {
|
||||
this.hidePopup();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
showPopup(location: IThreadCommentPopup, 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;
|
||||
}
|
||||
this._lastPopup && this._lastPopup.dispose();
|
||||
if (this._zenZoneService.visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._activePopup = location;
|
||||
this._activePopup$.next(location);
|
||||
|
||||
const popupDisposable = this._canvasPopupManagerService.attachPopupToCell(
|
||||
row,
|
||||
col,
|
||||
{
|
||||
componentKey: SHEETS_THREAD_COMMENT_MODAL,
|
||||
onClickOutside: () => {
|
||||
this.hidePopup();
|
||||
},
|
||||
direction: 'horizontal',
|
||||
excludeOutside: [
|
||||
...Array.from(document.querySelectorAll('.univer-thread-comment')),
|
||||
document.getElementById('thread-comment-add'),
|
||||
].filter(Boolean) as HTMLElement[],
|
||||
}
|
||||
);
|
||||
|
||||
if (!popupDisposable) {
|
||||
throw new Error('[SheetsThreadCommentPopupService]: cannot show popup!');
|
||||
}
|
||||
|
||||
const disposableCollection = new DisposableCollection();
|
||||
disposableCollection.add(popupDisposable);
|
||||
disposableCollection.add({
|
||||
dispose: () => {
|
||||
onHide?.();
|
||||
},
|
||||
});
|
||||
|
||||
this._lastPopup = disposableCollection;
|
||||
}
|
||||
|
||||
hidePopup() {
|
||||
if (!this._activePopup) {
|
||||
return;
|
||||
}
|
||||
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,19 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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 SHEETS_THREAD_COMMENT_MODAL = 'univer.sheet.thread-comment-modal';
|
||||
export const COMMENT_SINGLE_ICON = 'comment-single';
|
||||
export const SHEETS_THREAD_COMMENT = 'univer.sheet.thread-comment';
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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 { IThreadComment } from '@univerjs/thread-comment';
|
||||
|
||||
export interface ISheetThreadComment extends IThreadComment {
|
||||
row: number;
|
||||
column: number;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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 { useDependency } from '@wendellhu/redi/react-bindings';
|
||||
import React from 'react';
|
||||
import { ThreadCommentTree } from '@univerjs/thread-comment-ui';
|
||||
import type { Workbook } from '@univerjs/core';
|
||||
import { IUniverInstanceService, Tools, UniverInstanceType } from '@univerjs/core';
|
||||
import { useObservable } from '@univerjs/ui';
|
||||
import { SheetsThreadCommentModel } from '../../models/sheets-thread-comment.model';
|
||||
import { SheetsThreadCommentPopupService } from '../../services/sheets-thread-comment-popup.service';
|
||||
|
||||
export const SheetsThreadCommentCell = () => {
|
||||
const univerInstanceService = useDependency(IUniverInstanceService);
|
||||
const sheetsThreadCommentPopupService = useDependency(SheetsThreadCommentPopupService);
|
||||
const activePopup = useObservable(sheetsThreadCommentPopupService.activePopup$);
|
||||
const sheetThreadCommentModel = useDependency(SheetsThreadCommentModel);
|
||||
useObservable(sheetThreadCommentModel.commentUpdate$);
|
||||
if (!activePopup) {
|
||||
return null;
|
||||
}
|
||||
const { row, col, unitId, subUnitId, trigger } = activePopup;
|
||||
const rootId = sheetThreadCommentModel.getByLocation(unitId, subUnitId, row, col);
|
||||
const ref = `${Tools.chatAtABC(col)}${row + 1}`;
|
||||
const onClose = () => {
|
||||
sheetsThreadCommentPopupService.hidePopup();
|
||||
};
|
||||
|
||||
const getSubUnitName = (id: string) => {
|
||||
return univerInstanceService.getCurrentUnitForType<Workbook>(UniverInstanceType.UNIVER_SHEET)?.getSheetBySheetId(id)?.getName() ?? '';
|
||||
};
|
||||
|
||||
return (
|
||||
<ThreadCommentTree
|
||||
onClick={() => {
|
||||
sheetsThreadCommentPopupService.persistPopup();
|
||||
}}
|
||||
prefix="cell"
|
||||
id={rootId}
|
||||
unitId={unitId}
|
||||
subUnitId={subUnitId}
|
||||
type={UniverInstanceType.UNIVER_SHEET}
|
||||
refStr={ref}
|
||||
onClose={onClose}
|
||||
getSubUnitName={getSubUnitName}
|
||||
autoFocus={trigger === 'context-menu'}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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 { ICommandService, IUniverInstanceService, UniverInstanceType } from '@univerjs/core';
|
||||
import { ThreadCommentPanel } from '@univerjs/thread-comment-ui';
|
||||
import { useDependency } from '@wendellhu/redi/react-bindings';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { map } from 'rxjs';
|
||||
import type { IThreadComment } from '@univerjs/thread-comment';
|
||||
import { singleReferenceToGrid } from '@univerjs/engine-formula';
|
||||
import { ShowAddSheetCommentModalOperation } from '../../commands/operations/comment.operation';
|
||||
import { SheetsThreadCommentPopupService } from '../../services/sheets-thread-comment-popup.service';
|
||||
|
||||
export const SheetsThreadCommentPanel = () => {
|
||||
const univerInstanceService = useDependency(IUniverInstanceService);
|
||||
const sheetsThreadCommentPopupService = useDependency(SheetsThreadCommentPopupService);
|
||||
const workbook = univerInstanceService.getCurrentUnitForType<Workbook>(UniverInstanceType.UNIVER_SHEET);
|
||||
if (!workbook) {
|
||||
return null;
|
||||
}
|
||||
const unitId = workbook.getUnitId();
|
||||
const commandService = useDependency(ICommandService);
|
||||
const subUnitId$ = useMemo(() => workbook.activeSheet$.pipe(map((i) => i?.getSheetId())), [workbook.activeSheet$]);
|
||||
|
||||
const sortComments = useCallback((comments: IThreadComment[]) => {
|
||||
const worksheets = workbook.getSheets();
|
||||
const sheetIndex: Record<string, number> = {};
|
||||
worksheets.forEach((sheet, i) => {
|
||||
sheetIndex[sheet.getSheetId()] = i;
|
||||
});
|
||||
|
||||
return comments.map((comment) => {
|
||||
const ref = singleReferenceToGrid(comment.ref);
|
||||
const p = [sheetIndex[comment.subUnitId] ?? 0, ref.row, ref.column];
|
||||
return { ...comment, p };
|
||||
}).sort((pre, aft) => {
|
||||
if (pre.p[0] === aft.p[0]) {
|
||||
if (pre.p[1] === aft.p[1]) {
|
||||
return pre.p[2] - aft.p[2];
|
||||
}
|
||||
return pre.p[1] - aft.p[1];
|
||||
}
|
||||
|
||||
return pre.p[0] - aft.p[0];
|
||||
});
|
||||
}, [workbook]);
|
||||
|
||||
const getSubUnitName = (id: string) => {
|
||||
return workbook.getSheetBySheetId(id)?.getName() ?? '';
|
||||
};
|
||||
|
||||
const handleAdd = () => {
|
||||
commandService.executeCommand(ShowAddSheetCommentModalOperation.id);
|
||||
};
|
||||
|
||||
const handleResolve = () => {
|
||||
sheetsThreadCommentPopupService.hidePopup();
|
||||
};
|
||||
|
||||
return (
|
||||
<ThreadCommentPanel
|
||||
unitId={unitId}
|
||||
subUnitId$={subUnitId$}
|
||||
type={UniverInstanceType.UNIVER_SHEET}
|
||||
onAdd={handleAdd}
|
||||
getSubUnitName={getSubUnitName}
|
||||
onResolve={handleResolve}
|
||||
sortComments={sortComments}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "@univerjs/shared/tsconfigs/base",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"outDir": "lib/types"
|
||||
},
|
||||
"references": [{ "path": "./tsconfig.node.json" }],
|
||||
"include": ["src"]
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "@univerjs/shared/tsconfigs/node",
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import createViteConfig from '@univerjs/shared/vite';
|
||||
import pkg from './package.json';
|
||||
|
||||
export default ({ mode }) => createViteConfig({}, {
|
||||
mode,
|
||||
pkg,
|
||||
features: {
|
||||
react: false,
|
||||
css: true,
|
||||
dom: true,
|
||||
},
|
||||
});
|
||||
@@ -81,7 +81,7 @@
|
||||
"rxjs": ">=7.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@univerjs/icons": "^0.1.44"
|
||||
"@univerjs/icons": "^0.1.45"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.79",
|
||||
|
||||
@@ -69,13 +69,12 @@ export class SheetCanvasPopManagerService extends Disposable {
|
||||
};
|
||||
|
||||
const offsetBound = transformBound2OffsetBound(bound, scene, skeleton, worksheet);
|
||||
const bounding = currentRender.engine.getCanvasElement().getBoundingClientRect();
|
||||
|
||||
const position = {
|
||||
left: offsetBound.left,
|
||||
right: offsetBound.right,
|
||||
top: offsetBound.top + bounding.top,
|
||||
bottom: offsetBound.bottom + bounding.top,
|
||||
top: offsetBound.top,
|
||||
bottom: offsetBound.bottom,
|
||||
};
|
||||
return position;
|
||||
};
|
||||
@@ -256,7 +255,7 @@ export class SheetCanvasPopManagerService extends Disposable {
|
||||
skeleton: SpreadsheetSkeleton,
|
||||
activeViewport: Viewport
|
||||
): IBoundRectNoAngle {
|
||||
const { scene, engine } = currentRender;
|
||||
const { scene } = currentRender;
|
||||
|
||||
const primaryWithCoord = skeleton.getCellByIndex(row, col);
|
||||
const cellInfo = primaryWithCoord.isMergedMainCell ? primaryWithCoord.mergeInfo : primaryWithCoord;
|
||||
@@ -267,12 +266,11 @@ export class SheetCanvasPopManagerService extends Disposable {
|
||||
y: activeViewport.actualScrollY,
|
||||
};
|
||||
|
||||
const bounding = engine.getCanvasElement().getBoundingClientRect();
|
||||
return {
|
||||
left: ((cellInfo.startX - scrollXY.x) * scaleX) + bounding.left,
|
||||
right: (cellInfo.endX - scrollXY.x) * scaleX + bounding.left,
|
||||
top: ((cellInfo.startY - scrollXY.y) * scaleY) + bounding.top,
|
||||
bottom: ((cellInfo.endY - scrollXY.y) * scaleY) + bounding.top,
|
||||
left: ((cellInfo.startX - scrollXY.x) * scaleX),
|
||||
right: (cellInfo.endX - scrollXY.x) * scaleX,
|
||||
top: ((cellInfo.startY - scrollXY.y) * scaleY),
|
||||
bottom: ((cellInfo.endY - scrollXY.y) * scaleY),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -287,7 +287,7 @@ export {
|
||||
} from './services/ref-range/util';
|
||||
export { INTERCEPTOR_POINT } from './services/sheet-interceptor/interceptor-const';
|
||||
export { SheetInterceptorService } from './services/sheet-interceptor/sheet-interceptor.service';
|
||||
export type { ISheetLocation } from './services/sheet-interceptor/utils/interceptor';
|
||||
export type { ISheetLocation, ISheetLocationBase, ISheetRowLocation } from './services/sheet-interceptor/utils/interceptor';
|
||||
export { MergeCellController } from './controllers/merge-cell.controller';
|
||||
export { AddMergeRedoSelectionsOperationFactory, AddMergeUndoSelectionsOperationFactory } from './commands/utils/handle-merge-operation';
|
||||
|
||||
|
||||
@@ -16,15 +16,18 @@
|
||||
|
||||
import type { Workbook, Worksheet } from '@univerjs/core';
|
||||
|
||||
export interface ISheetLocation {
|
||||
workbook: Workbook;
|
||||
worksheet: Worksheet;
|
||||
export interface ISheetLocationBase {
|
||||
unitId: string;
|
||||
subUnitId: string;
|
||||
row: number;
|
||||
col: number;
|
||||
}
|
||||
|
||||
export interface ISheetLocation extends ISheetLocationBase {
|
||||
workbook: Workbook;
|
||||
worksheet: Worksheet;
|
||||
}
|
||||
|
||||
export interface ISheetRowLocation {
|
||||
workbook: Workbook;
|
||||
worksheet: Worksheet;
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
# @univerjs/thread-comment
|
||||
|
||||
[](https://npmjs.org/packages/@univerjs/thread-comment)
|
||||
[](https://img.shields.io/npm/l/@univerjs/thread-comment)
|
||||
|
||||
## Introduction
|
||||
|
||||
> TODO: Introduction
|
||||
|
||||
## Usage
|
||||
|
||||
### Installation
|
||||
|
||||
```shell
|
||||
npm i @univerjs/thread-comment
|
||||
```
|
||||
@@ -0,0 +1,92 @@
|
||||
{
|
||||
"name": "@univerjs/thread-comment-ui",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"description": "",
|
||||
"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": [],
|
||||
"sideEffects": [
|
||||
"**/*.css"
|
||||
],
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./*": "./src/*"
|
||||
},
|
||||
"main": "./lib/cjs/index.js",
|
||||
"module": "./lib/es/index.js",
|
||||
"types": "./lib/types/index.d.ts",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"main": "./lib/cjs/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"
|
||||
},
|
||||
"./lib/*": "./lib/*"
|
||||
}
|
||||
},
|
||||
"directories": {
|
||||
"lib": "lib"
|
||||
},
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest",
|
||||
"coverage": "vitest run --coverage",
|
||||
"lint:types": "tsc --noEmit",
|
||||
"build": "tsc && vite build"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@univerjs/core": "workspace:*",
|
||||
"@univerjs/design": "workspace:*",
|
||||
"@univerjs/thread-comment": "workspace:*",
|
||||
"@univerjs/ui": "workspace:*",
|
||||
"@wendellhu/redi": "^0.15.1",
|
||||
"dayjs": ">=1.11.0",
|
||||
"react": "^16.9.0 || ^17.0.0 || ^18.0.0",
|
||||
"rxjs": ">=7.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@univerjs/icons": "^0.1.45",
|
||||
"@univerjs/protocol": "^0.1.20",
|
||||
"@univerjs/thread-comment": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@univerjs/core": "workspace:*",
|
||||
"@univerjs/design": "workspace:*",
|
||||
"@univerjs/shared": "workspace:*",
|
||||
"@univerjs/ui": "workspace:*",
|
||||
"@wendellhu/redi": "^0.15.1",
|
||||
"clsx": "^2.1.1",
|
||||
"dayjs": "^1.11.10",
|
||||
"less": "^4.2.0",
|
||||
"react": "^18.2.0",
|
||||
"rxjs": "^7.8.1",
|
||||
"typescript": "^5.4.5",
|
||||
"vite": "^5.2.10",
|
||||
"vitest": "^1.5.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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 { CommandType } from '@univerjs/core';
|
||||
import { ISidebarService } from '@univerjs/ui';
|
||||
import type { IAccessor } from '@wendellhu/redi';
|
||||
import { ThreadCommentPanelService } from '../../services/thread-comment-panel.service';
|
||||
import { THREAD_COMMENT_PANEL } from '../../types/const';
|
||||
|
||||
export const ToggleSheetCommentPanelOperation: ICommand = {
|
||||
id: 'thread-comment-ui.operation.toggle-panel',
|
||||
type: CommandType.OPERATION,
|
||||
handler(accessor: IAccessor) {
|
||||
const sidebarService = accessor.get(ISidebarService);
|
||||
const panelService = accessor.get(ThreadCommentPanelService);
|
||||
|
||||
if (panelService.panelVisible) {
|
||||
sidebarService.close();
|
||||
panelService.setPanelVisible(false);
|
||||
} else {
|
||||
sidebarService.open({
|
||||
header: { title: 'threadCommentUI.panel.title' },
|
||||
children: { label: THREAD_COMMENT_PANEL },
|
||||
width: 320,
|
||||
});
|
||||
panelService.setPanelVisible(true);
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
||||
export interface ISetActiveCommentOperationParams {
|
||||
unitId: string;
|
||||
subUnitId: string;
|
||||
commentId: string;
|
||||
}
|
||||
|
||||
export const SetActiveCommentOperation: ICommand<ISetActiveCommentOperationParams> = {
|
||||
id: 'thread-comment-ui.operation.set-active-comment',
|
||||
type: CommandType.OPERATION,
|
||||
handler(accessor, params) {
|
||||
const panelService = accessor.get(ThreadCommentPanelService);
|
||||
panelService.setActiveComment(params);
|
||||
return true;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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, LifecycleStages, LocaleService, OnLifecycle } from '@univerjs/core';
|
||||
import { Inject } from '@wendellhu/redi';
|
||||
import { enUS, zhCN } from '../locales';
|
||||
|
||||
@OnLifecycle(LifecycleStages.Starting, ThreadCommentUIController)
|
||||
export class ThreadCommentUIController extends Disposable {
|
||||
constructor(
|
||||
@Inject(LocaleService) private readonly _localeService: LocaleService
|
||||
) {
|
||||
super();
|
||||
|
||||
this._initLocales();
|
||||
}
|
||||
|
||||
private _initLocales() {
|
||||
this._localeService.load({
|
||||
zhCN,
|
||||
enUS,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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 { UniverThreadCommentUIPlugin } from './plugin';
|
||||
export { ToggleSheetCommentPanelOperation, SetActiveCommentOperation, type ISetActiveCommentOperationParams } from './commands/operations/comment.operations';
|
||||
export { ThreadCommentPanelService } from './services/thread-comment-panel.service';
|
||||
export { ThreadCommentPanel } from './views/thread-comment-panel';
|
||||
export type { IThreadCommentPanelProps } from './views/thread-comment-panel';
|
||||
export { ThreadCommentTree } from './views/thread-comment-tree';
|
||||
export type { IThreadCommentTreeProps } from './views/thread-comment-tree';
|
||||
export { THREAD_COMMENT_PANEL } from './types/const';
|
||||
export type { IThreadCommentUIConfig } from './types/interfaces/i-thread-comment-mention';
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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 {
|
||||
threadCommentUI: {
|
||||
panel: {
|
||||
title: 'Comment Management',
|
||||
empty: 'No comments yet',
|
||||
filterEmpty: 'No match result',
|
||||
reset: 'Reset Filter',
|
||||
addComment: 'Add Comment',
|
||||
},
|
||||
editor: {
|
||||
placeholder: 'Reply or add others with @',
|
||||
reply: 'Comment',
|
||||
cancel: 'Cancel',
|
||||
save: 'Save',
|
||||
},
|
||||
item: {
|
||||
edit: 'Edit',
|
||||
delete: 'Delete This Comment',
|
||||
},
|
||||
filter: {
|
||||
sheet: {
|
||||
all: 'All sheet',
|
||||
current: 'Current sheet',
|
||||
},
|
||||
status: {
|
||||
all: 'All comments',
|
||||
resolved: 'Resolved',
|
||||
unsolved: 'Not resolved',
|
||||
concernMe: 'Concern me',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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 zhCN } from './zhCN';
|
||||
export { default as enUS } from './enUS';
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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 {
|
||||
threadCommentUI: {
|
||||
panel: {
|
||||
title: '评论管理',
|
||||
empty: '暂无评论',
|
||||
filterEmpty: '没有匹配的结果',
|
||||
reset: '重置',
|
||||
addComment: '新建评论',
|
||||
},
|
||||
editor: {
|
||||
placeholder: '回复',
|
||||
reply: '回复',
|
||||
cancel: '取消',
|
||||
save: '保存',
|
||||
},
|
||||
item: {
|
||||
edit: '编辑',
|
||||
delete: '删除',
|
||||
},
|
||||
filter: {
|
||||
sheet: {
|
||||
all: '所有表格',
|
||||
current: '当前表格',
|
||||
},
|
||||
status: {
|
||||
all: '所有评论',
|
||||
resolved: '已解决',
|
||||
unsolved: '未解决',
|
||||
concernMe: '与我有关',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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 { UniverThreadCommentPlugin } from '@univerjs/thread-comment';
|
||||
import { ICommandService, IConfigService, UniverInstanceType } from '@univerjs/core';
|
||||
import type { Dependency } from '@wendellhu/redi';
|
||||
import { Inject, Injector } from '@wendellhu/redi';
|
||||
import { PLUGIN_NAME } from './types/const';
|
||||
import { ThreadCommentPanelService } from './services/thread-comment-panel.service';
|
||||
import { SetActiveCommentOperation, ToggleSheetCommentPanelOperation } from './commands/operations/comment.operations';
|
||||
import { ThreadCommentUIController } from './controllers/thread-comment-ui.controller';
|
||||
import type { IThreadCommentUIConfig } from './types/interfaces/i-thread-comment-mention';
|
||||
|
||||
export class UniverThreadCommentUIPlugin extends UniverThreadCommentPlugin {
|
||||
static override pluginName = PLUGIN_NAME;
|
||||
static override type = UniverInstanceType.UNIVER_UNKNOWN;
|
||||
|
||||
constructor(
|
||||
_config: IThreadCommentUIConfig,
|
||||
@Inject(Injector) protected override _injector: Injector,
|
||||
@ICommandService protected override _commandService: ICommandService,
|
||||
@IConfigService protected _configService: IConfigService
|
||||
) {
|
||||
super(
|
||||
_config,
|
||||
_injector,
|
||||
_commandService
|
||||
);
|
||||
|
||||
this._configService.setConfig(PLUGIN_NAME, _config);
|
||||
}
|
||||
|
||||
override onStarting(injector: Injector): void {
|
||||
super.onStarting(injector);
|
||||
([
|
||||
[ThreadCommentUIController],
|
||||
[ThreadCommentPanelService],
|
||||
] as Dependency[]).forEach((dep) => {
|
||||
injector.add(dep);
|
||||
});
|
||||
|
||||
[ToggleSheetCommentPanelOperation, SetActiveCommentOperation].forEach((command) => {
|
||||
this._commandService.registerCommand(command);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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, IUniverInstanceService, UniverInstanceType } from '@univerjs/core';
|
||||
import { ISidebarService } from '@univerjs/ui';
|
||||
import { Inject } from '@wendellhu/redi';
|
||||
import { BehaviorSubject, filter } from 'rxjs';
|
||||
|
||||
type ActiveCommentInfo = { unitId: string; subUnitId: string; commentId: string; trigger?: string } | undefined;
|
||||
|
||||
export class ThreadCommentPanelService extends Disposable {
|
||||
private _panelVisible = false;
|
||||
private _panelVisible$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
private _activeCommentId: ActiveCommentInfo;
|
||||
private _activeCommentId$ = new BehaviorSubject<ActiveCommentInfo>(undefined);
|
||||
|
||||
panelVisible$ = this._panelVisible$.asObservable();
|
||||
activeCommentId$ = this._activeCommentId$.asObservable();
|
||||
|
||||
constructor(
|
||||
@Inject(ISidebarService) private readonly _sidebarService: ISidebarService,
|
||||
@IUniverInstanceService private readonly _univerInstanceService: IUniverInstanceService
|
||||
) {
|
||||
super();
|
||||
this._init();
|
||||
}
|
||||
|
||||
private _init() {
|
||||
this.disposeWithMe(
|
||||
this._sidebarService.sidebarOptions$.subscribe((opt) => {
|
||||
if (!opt.visible) {
|
||||
this.setPanelVisible(false);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
this.disposeWithMe(
|
||||
this._univerInstanceService.getCurrentTypeOfUnit$(UniverInstanceType.UNIVER_SHEET)
|
||||
.pipe(filter((sheet) => !sheet)).subscribe(() => {
|
||||
this._sidebarService.close();
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
get panelVisible() {
|
||||
return this._panelVisible;
|
||||
}
|
||||
|
||||
get activeCommentId() {
|
||||
return this._activeCommentId;
|
||||
}
|
||||
|
||||
setPanelVisible(visible: boolean) {
|
||||
this._panelVisible = visible;
|
||||
this._panelVisible$.next(visible);
|
||||
}
|
||||
|
||||
setActiveComment(commentInfo: ActiveCommentInfo) {
|
||||
this._activeCommentId = commentInfo;
|
||||
this._activeCommentId$.next(commentInfo);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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 THREAD_COMMENT_POPUP = 'thread-comment-popup';
|
||||
|
||||
export const THREAD_COMMENT_PANEL = 'thread-comment-panel';
|
||||
|
||||
export const PLUGIN_NAME = 'thread-comment-ui-plugin';
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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 { IThreadCommentMention } from '@univerjs/thread-comment';
|
||||
import type { MentionProps } from '@univerjs/design';
|
||||
|
||||
export interface IThreadCommentMentionConfig {
|
||||
getMentions: (search: string) => Promise<IThreadCommentMention[]>;
|
||||
trigger: string;
|
||||
renderSuggestion?: MentionProps['renderSuggestion'];
|
||||
}
|
||||
|
||||
export interface IThreadCommentUIConfig {
|
||||
mentions?: IThreadCommentMentionConfig[];
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
.thread-comment-editor-buttons {
|
||||
margin-top: 12px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.thread-comment-editor-suggestion {
|
||||
display: flex;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 13px;
|
||||
color: rgb(var(--color-black));
|
||||
}
|
||||
|
||||
.thread-comment-editor-suggestionActive {
|
||||
background-color: rgba(var(--grey-50));
|
||||
}
|
||||
|
||||
.thread-comment-editor-suggestion-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 12px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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 { IThreadComment } from '@univerjs/thread-comment';
|
||||
import type { MentionProps } from '@univerjs/design';
|
||||
import { Button, Mention, Mentions } from '@univerjs/design';
|
||||
import React, { forwardRef, useImperativeHandle, useRef, useState } from 'react';
|
||||
import { useDependency } from '@wendellhu/redi/react-bindings';
|
||||
import type { IDocumentBody } from '@univerjs/core';
|
||||
import { IConfigService, LocaleService } from '@univerjs/core';
|
||||
import { PLUGIN_NAME } from '../../types/const';
|
||||
import type { IThreadCommentUIConfig } from '../../types/interfaces/i-thread-comment-mention';
|
||||
import styles from './index.module.less';
|
||||
import { parseMentions, transformDocument2TextNodes, transformMention, transformTextNode2Text, transformTextNodes2Document } from './util';
|
||||
|
||||
export interface IThreadCommentEditorProps {
|
||||
id?: string;
|
||||
comment?: Pick<IThreadComment, 'attachments' | 'text' | 'mentions'>;
|
||||
onSave?: (comment: Pick<IThreadComment, 'attachments' | 'text'>) => void;
|
||||
onCancel?: () => void;
|
||||
autoFocus?: boolean;
|
||||
}
|
||||
|
||||
export interface IThreadCommentEditorInstance {
|
||||
reply: (text: IDocumentBody) => void;
|
||||
}
|
||||
|
||||
const defaultRenderSuggestion: MentionProps['renderSuggestion'] = (mention, search, highlightedDisplay, index, focused) => {
|
||||
const icon = (mention as any).raw?.icon;
|
||||
return (
|
||||
<div className={styles.threadCommentEditorSuggestion}>
|
||||
{icon ? <img className={styles.threadCommentEditorSuggestionIcon} src={icon} /> : null}
|
||||
<div>
|
||||
{mention.display ?? mention.id}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const ThreadCommentEditor = forwardRef<IThreadCommentEditorInstance, IThreadCommentEditorProps>((props, ref) => {
|
||||
const { comment, onSave, id, onCancel, autoFocus } = props;
|
||||
const configService = useDependency(IConfigService);
|
||||
const localeService = useDependency(LocaleService);
|
||||
const [localComment, setLocalComment] = useState({ ...comment });
|
||||
const [editing, setEditing] = useState(false);
|
||||
const mentions = configService.getConfig<IThreadCommentUIConfig>(PLUGIN_NAME)?.mentions ?? [];
|
||||
const inputRef = useRef(null);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
reply(text) {
|
||||
setLocalComment({
|
||||
...comment,
|
||||
text,
|
||||
attachments: [],
|
||||
});
|
||||
(inputRef.current as any)?.inputElement.focus();
|
||||
},
|
||||
}));
|
||||
|
||||
return (
|
||||
<div className={styles.threadCommentEditor} onClick={(e) => e.preventDefault()}>
|
||||
<Mentions
|
||||
ref={inputRef}
|
||||
autoFocus={autoFocus}
|
||||
style={{ width: '100%' }}
|
||||
placeholder={localeService.t('threadCommentUI.editor.placeholder')}
|
||||
value={localComment?.text ? transformTextNode2Text(transformDocument2TextNodes(localComment.text)) : ''}
|
||||
onChange={(e) => {
|
||||
const text = e.target.value;
|
||||
if (!text) {
|
||||
setLocalComment({ ...comment, text: undefined });
|
||||
}
|
||||
setLocalComment?.({ ...comment, text: transformTextNodes2Document(parseMentions(e.target.value)) });
|
||||
}}
|
||||
onFocus={() => {
|
||||
setEditing(true);
|
||||
}}
|
||||
>
|
||||
{mentions.map((mention) => (
|
||||
<Mention
|
||||
key={mention.trigger}
|
||||
trigger={mention.trigger}
|
||||
data={(query, callback) => mention.getMentions!(query)
|
||||
.then((res) => res.map(transformMention)).then(callback) as any}
|
||||
displayTransform={(id, label) => `@${label} `}
|
||||
renderSuggestion={mention.renderSuggestion ?? defaultRenderSuggestion}
|
||||
/>
|
||||
))}
|
||||
</Mentions>
|
||||
{editing
|
||||
? (
|
||||
<div className={styles.threadCommentEditorButtons}>
|
||||
<Button
|
||||
style={{ marginRight: 12 }}
|
||||
onClick={() => {
|
||||
onCancel?.();
|
||||
setEditing(false);
|
||||
setLocalComment({ text: undefined });
|
||||
}}
|
||||
>
|
||||
{localeService.t('threadCommentUI.editor.cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
disabled={!localComment.text}
|
||||
onClick={() => {
|
||||
if (localComment.text) {
|
||||
onSave?.({
|
||||
...localComment,
|
||||
text: localComment.text,
|
||||
});
|
||||
setEditing(false);
|
||||
setLocalComment({ text: undefined });
|
||||
}
|
||||
}}
|
||||
>
|
||||
{localeService.t(id ? 'threadCommentUI.editor.save' : 'threadCommentUI.editor.reply')}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
: null}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,164 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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 { CustomRangeType, type IDocumentBody } from '@univerjs/core';
|
||||
import type { IThreadCommentMention } from '@univerjs/thread-comment';
|
||||
|
||||
export type TextNode = {
|
||||
type: 'text';
|
||||
content: string;
|
||||
} | {
|
||||
type: 'mention';
|
||||
content: IThreadCommentMention;
|
||||
};
|
||||
|
||||
export const parseMentions = (text: string): TextNode[] => {
|
||||
const regex = /@\[(.*?)\]\((.*?)\)|(\w+)/g;
|
||||
let match;
|
||||
let lastIndex = 0;
|
||||
const result: TextNode[] = [];
|
||||
|
||||
while ((match = regex.exec(text)) !== null) {
|
||||
if (match.index > lastIndex) {
|
||||
// Add the text between two user mentions or before the first user mention
|
||||
result.push({
|
||||
type: 'text',
|
||||
content: text.substring(lastIndex, match.index),
|
||||
});
|
||||
}
|
||||
|
||||
if (match[1] && match[2]) {
|
||||
// User mention found
|
||||
result.push({
|
||||
type: 'mention',
|
||||
content: {
|
||||
label: match[1],
|
||||
id: match[2],
|
||||
},
|
||||
});
|
||||
} else if (match[3]) {
|
||||
// Text (numbers) found
|
||||
result.push({
|
||||
type: 'text',
|
||||
content: match[3],
|
||||
});
|
||||
}
|
||||
lastIndex = regex.lastIndex;
|
||||
}
|
||||
|
||||
// Add any remaining text after the last mention (if any)
|
||||
if (lastIndex < text.length) {
|
||||
result.push({
|
||||
type: 'text',
|
||||
content: text.substring(lastIndex),
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export const transformTextNode2Text = (nodes: TextNode[]) => {
|
||||
return nodes.map((item) => {
|
||||
switch (item.type) {
|
||||
case 'mention':
|
||||
return `@[${item.content.label}](${item.content.id})`;
|
||||
default:
|
||||
return item.content;
|
||||
}
|
||||
}).join('');
|
||||
};
|
||||
|
||||
export const transformDocument2TextNodes = (doc: IDocumentBody) => {
|
||||
const { dataStream, customRanges } = doc;
|
||||
const end = dataStream.length - 2;
|
||||
const textNodes: TextNode[] = [];
|
||||
let lastIndex = 0;
|
||||
customRanges?.forEach((range) => {
|
||||
if (lastIndex < range.startIndex) {
|
||||
textNodes.push({
|
||||
type: 'text',
|
||||
content: dataStream.slice(lastIndex, range.startIndex),
|
||||
});
|
||||
}
|
||||
textNodes.push({
|
||||
type: 'mention',
|
||||
content: {
|
||||
label: dataStream.slice(range.startIndex, range.endIndex).slice(1, -1),
|
||||
id: range.rangeId,
|
||||
},
|
||||
});
|
||||
lastIndex = range.endIndex;
|
||||
});
|
||||
|
||||
textNodes.push({
|
||||
type: 'text',
|
||||
content: dataStream.slice(lastIndex, end),
|
||||
});
|
||||
return textNodes;
|
||||
};
|
||||
|
||||
export const transformTextNodes2Document = (nodes: TextNode[]): IDocumentBody => {
|
||||
let str = '';
|
||||
const customRanges: Required<IDocumentBody['customRanges']> = [];
|
||||
|
||||
nodes.forEach((node) => {
|
||||
switch (node.type) {
|
||||
case 'text':
|
||||
str += node.content;
|
||||
break;
|
||||
case 'mention': {
|
||||
const start = str.length;
|
||||
str += `\x1F${node.content.label}\x1E`;
|
||||
const end = str.length;
|
||||
customRanges.push({
|
||||
rangeId: node.content.id,
|
||||
rangeType: CustomRangeType.MENTION,
|
||||
startIndex: start,
|
||||
endIndex: end,
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
str += '\n\r';
|
||||
|
||||
return {
|
||||
textRuns: [],
|
||||
paragraphs: [
|
||||
{
|
||||
startIndex: str.length - 2,
|
||||
paragraphStyle: {},
|
||||
},
|
||||
],
|
||||
sectionBreaks: [
|
||||
{
|
||||
startIndex: str.length - 1,
|
||||
},
|
||||
],
|
||||
dataStream: str,
|
||||
customRanges,
|
||||
};
|
||||
};
|
||||
|
||||
export const transformMention = (mention: IThreadCommentMention) => ({
|
||||
display: mention.label,
|
||||
id: `${mention.id}`,
|
||||
raw: mention,
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
.thread-comment-panel {
|
||||
min-height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.thread-comment {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
&-forms {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
margin-top: 12px;
|
||||
|
||||
.select {
|
||||
width: 120px;
|
||||
}
|
||||
}
|
||||
|
||||
&-empty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: rgb(var(--grey-600));
|
||||
font-size: 13px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
&-add {
|
||||
margin-top: 8px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
svg {
|
||||
margin-right: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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 React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useDependency } from '@wendellhu/redi/react-bindings';
|
||||
import type { IThreadComment } from '@univerjs/thread-comment';
|
||||
import { ThreadCommentModel } from '@univerjs/thread-comment';
|
||||
import { ICommandService, LocaleService, type UniverInstanceType, UserManagerService } from '@univerjs/core';
|
||||
import { useObservable } from '@univerjs/ui';
|
||||
import { Button, Select } from '@univerjs/design';
|
||||
import { IncreaseSingle } from '@univerjs/icons';
|
||||
import type { Observable } from 'rxjs';
|
||||
import { ThreadCommentTree } from '../thread-comment-tree';
|
||||
import { ThreadCommentPanelService } from '../../services/thread-comment-panel.service';
|
||||
import { SetActiveCommentOperation } from '../../commands/operations/comment.operations';
|
||||
import styles from './index.module.less';
|
||||
|
||||
export interface IThreadCommentPanelProps {
|
||||
unitId: string;
|
||||
subUnitId$: Observable<string | undefined>;
|
||||
type: UniverInstanceType;
|
||||
onAdd: () => void;
|
||||
getSubUnitName: (subUnitId: string) => string;
|
||||
onResolve?: (id: string) => void;
|
||||
sortComments?: (comments: IThreadComment[]) => IThreadComment[];
|
||||
}
|
||||
|
||||
export const ThreadCommentPanel = (props: IThreadCommentPanelProps) => {
|
||||
const { unitId, subUnitId$, type, onAdd, getSubUnitName, onResolve, sortComments } = props;
|
||||
const [unit, setUnit] = useState('all');
|
||||
const [status, setStatus] = useState('all');
|
||||
const localeService = useDependency(LocaleService);
|
||||
const userService = useDependency(UserManagerService);
|
||||
const threadCommentModel = useDependency(ThreadCommentModel);
|
||||
const [unitComments, setUnitComments] = useState(() => threadCommentModel.getUnit(unitId));
|
||||
const panelService = useDependency(ThreadCommentPanelService);
|
||||
const activeCommentId = useObservable(panelService.activeCommentId$);
|
||||
const update = useObservable(threadCommentModel.commentUpdate$);
|
||||
const commandService = useDependency(ICommandService);
|
||||
const subUnitId = useObservable(subUnitId$);
|
||||
const currentUser = userService.getCurrentUser();
|
||||
const shouldScroll = useRef(true);
|
||||
const prefix = 'panel';
|
||||
const comments = useMemo(() => {
|
||||
if (unit === 'all') {
|
||||
const filteredComments = unitComments.map((i) => i[1]).flat().filter((i) => !i.parentId);
|
||||
return sortComments ? sortComments(filteredComments) : filteredComments;
|
||||
} else {
|
||||
return unitComments.find((i) => i[0] === subUnitId)?.[1] ?? [];
|
||||
}
|
||||
}, [unit, unitComments, subUnitId, sortComments]);
|
||||
|
||||
const statuedComments = useMemo(() => {
|
||||
if (status === 'resolved') {
|
||||
return comments.filter((comment) => comment.resolved);
|
||||
}
|
||||
|
||||
if (status === 'unsolved') {
|
||||
return comments.filter((comment) => !comment.resolved);
|
||||
}
|
||||
if (status === 'concern_me') {
|
||||
if (!currentUser?.userID) {
|
||||
return comments;
|
||||
}
|
||||
|
||||
return comments.map((comment) => threadCommentModel.getCommentWithChildren(comment.unitId, comment.subUnitId, comment.id)).map((comment) => {
|
||||
if (comment?.relativeUsers.has(currentUser.userID)) {
|
||||
return comment.root;
|
||||
}
|
||||
return null;
|
||||
}).filter(Boolean) as IThreadComment[];
|
||||
}
|
||||
|
||||
return comments;
|
||||
}, [comments, currentUser?.userID, status, threadCommentModel]);
|
||||
|
||||
const isFiltering = status !== 'all' || unit !== 'all';
|
||||
|
||||
const onReset = () => {
|
||||
setStatus('all');
|
||||
setUnit('all');
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (unitId) {
|
||||
setUnitComments(
|
||||
threadCommentModel.getUnit(unitId)
|
||||
);
|
||||
}
|
||||
}, [unitId, threadCommentModel, update]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!activeCommentId) {
|
||||
return;
|
||||
}
|
||||
if (!shouldScroll.current) {
|
||||
shouldScroll.current = true;
|
||||
return;
|
||||
}
|
||||
const { unitId, subUnitId, commentId } = activeCommentId;
|
||||
const id = `${prefix}-${unitId}-${subUnitId}-${commentId}`;
|
||||
document.getElementById(id)?.scrollIntoView({ block: 'center' });
|
||||
}, [activeCommentId]);
|
||||
|
||||
return (
|
||||
<div className={styles.threadCommentPanel}>
|
||||
<div className={styles.threadCommentPanelForms}>
|
||||
<Select
|
||||
borderless
|
||||
value={unit}
|
||||
onChange={(e) => setUnit(e)}
|
||||
options={[
|
||||
{
|
||||
value: 'current',
|
||||
label: localeService.t('threadCommentUI.filter.sheet.current'),
|
||||
}, {
|
||||
value: 'all',
|
||||
label: localeService.t('threadCommentUI.filter.sheet.all'),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<Select
|
||||
borderless
|
||||
value={status}
|
||||
onChange={(e) => setStatus(e)}
|
||||
options={[
|
||||
{
|
||||
value: 'all',
|
||||
label: localeService.t('threadCommentUI.filter.status.all'),
|
||||
}, {
|
||||
value: 'resolved',
|
||||
label: localeService.t('threadCommentUI.filter.status.resolved'),
|
||||
},
|
||||
{
|
||||
value: 'unsolved',
|
||||
label: localeService.t('threadCommentUI.filter.status.unsolved'),
|
||||
},
|
||||
{
|
||||
value: 'concern_me',
|
||||
label: localeService.t('threadCommentUI.filter.status.concernMe'),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
{statuedComments?.map((comment) => (
|
||||
<ThreadCommentTree
|
||||
prefix={prefix}
|
||||
getSubUnitName={getSubUnitName}
|
||||
key={comment.id}
|
||||
id={comment.id}
|
||||
unitId={comment.unitId}
|
||||
subUnitId={comment.subUnitId}
|
||||
type={type}
|
||||
showEdit={activeCommentId?.commentId === comment.id}
|
||||
showHighlight={activeCommentId?.commentId === comment.id}
|
||||
onClick={() => {
|
||||
shouldScroll.current = false;
|
||||
commandService.executeCommand(SetActiveCommentOperation.id, {
|
||||
unitId: comment.unitId,
|
||||
subUnitId: comment.subUnitId,
|
||||
commentId: comment.id,
|
||||
temp: true,
|
||||
});
|
||||
}}
|
||||
onClose={() => onResolve?.(comment.id)}
|
||||
/>
|
||||
))}
|
||||
{statuedComments.length
|
||||
? null
|
||||
: (
|
||||
<div className={styles.threadCommentPanelEmpty}>
|
||||
{isFiltering ?
|
||||
localeService.t('threadCommentUI.panel.filterEmpty')
|
||||
: localeService.t('threadCommentUI.panel.empty')}
|
||||
{isFiltering
|
||||
? (
|
||||
<Button
|
||||
onClick={onReset}
|
||||
type="link"
|
||||
>
|
||||
{localeService.t('threadCommentUI.panel.reset')}
|
||||
</Button>
|
||||
)
|
||||
: (
|
||||
<Button
|
||||
id="thread-comment-add"
|
||||
className={styles.threadCommentPanelAdd}
|
||||
type="primary"
|
||||
onClick={onAdd}
|
||||
>
|
||||
<IncreaseSingle />
|
||||
{localeService.t('threadCommentUI.panel.addComment')}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,120 @@
|
||||
.thread-comment {
|
||||
padding: 16px;
|
||||
background: rgba(var(--color-white));
|
||||
border: 1px solid rgba(var(--grey-200));
|
||||
width: 279px;
|
||||
border-radius: 8px;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
box-shadow: var(--box-shadow-base);
|
||||
}
|
||||
|
||||
.thread-comment-content {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
scrollbar-color: rgba(var(--scrollbar-color), 0.7) transparent;
|
||||
scrollbar-gutter: auto;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
.thread-comment-highlight {
|
||||
background-color: rgb(var(--gold-400));
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 6px;
|
||||
border-top-left-radius: 6px;
|
||||
border-top-right-radius: 6px;
|
||||
}
|
||||
|
||||
.thread-comment-icon-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.thread-comment-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 3px;
|
||||
margin-left: 4px;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.thread-comment-icon:hover {
|
||||
background-color: rgba(var(--grey-50));
|
||||
}
|
||||
|
||||
.thread-comment-title {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.thread-comment-username {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.thread-comment-item {
|
||||
margin-bottom: 12px;
|
||||
padding-left: 30px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.thread-comment-item-head {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 12px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.thread-comment-item-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
height: 24px;
|
||||
align-items: center;
|
||||
margin-bottom: 4px;
|
||||
|
||||
&-position {
|
||||
color: rgba(var(--color-black));
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
&-highlight {
|
||||
width: 3px;
|
||||
height: 14px;
|
||||
border-radius: 2px;
|
||||
margin-right: 8px;
|
||||
margin-top: 3px;
|
||||
background-color: rgba(var(--gold-400));
|
||||
}
|
||||
}
|
||||
|
||||
.thread-comment-item-time {
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 4px;
|
||||
color: rgba(var(--grey-600));
|
||||
}
|
||||
|
||||
.thread-comment-item-content {
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
word-break: break-all;
|
||||
color: rgba(var(--color-black));
|
||||
}
|
||||
|
||||
.thread-comment-item-at {
|
||||
color: rgba(var(--blue-400));
|
||||
}
|
||||
@@ -0,0 +1,337 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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 { useDependency } from '@wendellhu/redi/react-bindings';
|
||||
import type { IAddCommentCommandParams, IThreadComment, IUpdateCommentCommandParams } from '@univerjs/thread-comment';
|
||||
import { AddCommentCommand, DeleteCommentCommand, DeleteCommentTreeCommand, ResolveCommentCommand, ThreadCommentModel, UpdateCommentCommand } from '@univerjs/thread-comment';
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { DeleteSingle, MoreHorizontalSingle, ReplyToCommentSingle, ResolvedSingle, SolveSingle } from '@univerjs/icons';
|
||||
import { ICommandService, LocaleService, Tools, type UniverInstanceType, UserManagerService } from '@univerjs/core';
|
||||
import { useObservable } from '@univerjs/ui';
|
||||
import dayjs from 'dayjs';
|
||||
import { Dropdown, Menu, MenuItem } from '@univerjs/design';
|
||||
import type { IUser } from '@univerjs/protocol';
|
||||
import type { IThreadCommentEditorInstance } from '../thread-comment-editor';
|
||||
import { ThreadCommentEditor } from '../thread-comment-editor';
|
||||
import { transformDocument2TextNodes, transformTextNodes2Document } from '../thread-comment-editor/util';
|
||||
import styles from './index.module.less';
|
||||
|
||||
export interface IThreadCommentTreeProps {
|
||||
id?: string;
|
||||
unitId: string;
|
||||
subUnitId: string;
|
||||
type: UniverInstanceType;
|
||||
refStr?: string;
|
||||
showEdit?: boolean;
|
||||
onClick?: () => void;
|
||||
showHighlight?: boolean;
|
||||
onClose?: () => void;
|
||||
getSubUnitName: (subUnitId: string) => string;
|
||||
prefix?: string;
|
||||
autoFocus?: boolean;
|
||||
}
|
||||
|
||||
export interface IThreadCommentItemProps {
|
||||
item: IThreadComment;
|
||||
unitId: string;
|
||||
subUnitId: string;
|
||||
onEditingChange?: (editing: boolean) => void;
|
||||
editing?: boolean;
|
||||
onClick?: () => void;
|
||||
resolved?: boolean;
|
||||
onReply: (user: IUser | undefined) => void;
|
||||
isRoot?: boolean;
|
||||
onClose?: () => void;
|
||||
}
|
||||
|
||||
const MOCK_ID = '__mock__';
|
||||
|
||||
const ThreadCommentItem = (props: IThreadCommentItemProps) => {
|
||||
const { item, unitId, subUnitId, editing, onEditingChange, onReply, resolved, isRoot, onClose } = props;
|
||||
const commandService = useDependency(ICommandService);
|
||||
const localeService = useDependency(LocaleService);
|
||||
const userManagerService = useDependency(UserManagerService);
|
||||
const user = userManagerService.getUser(item.personId);
|
||||
const currentUser = useObservable(userManagerService.currentUser$);
|
||||
const isCommentBySelf = currentUser?.userID === item.personId;
|
||||
const isMock = item.id === MOCK_ID;
|
||||
const [showReply, setShowReply] = useState(false);
|
||||
const handleDeleteItem = () => {
|
||||
commandService.executeCommand(
|
||||
isRoot ? DeleteCommentTreeCommand.id : DeleteCommentCommand.id,
|
||||
{
|
||||
unitId,
|
||||
subUnitId,
|
||||
commentId: item.id,
|
||||
}
|
||||
);
|
||||
if (isRoot) {
|
||||
onClose?.();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.threadCommentItem} onMouseLeave={() => setShowReply(false)} onMouseEnter={() => setShowReply(true)}>
|
||||
<img className={styles.threadCommentItemHead} src={user?.avatar} />
|
||||
<div className={styles.threadCommentItemTitle}>
|
||||
<div className={styles.threadCommentUsername}>
|
||||
{user?.name || ' '}
|
||||
</div>
|
||||
<div>
|
||||
{(isMock || resolved)
|
||||
? null
|
||||
: (
|
||||
showReply
|
||||
? (
|
||||
<div className={styles.threadCommentIcon} onClick={() => onReply(user)}>
|
||||
<ReplyToCommentSingle />
|
||||
</div>
|
||||
)
|
||||
: null
|
||||
)}
|
||||
{isCommentBySelf && !isMock && !resolved
|
||||
? (
|
||||
<Dropdown
|
||||
overlay={(
|
||||
<Menu>
|
||||
<MenuItem onClick={() => onEditingChange?.(true)}>{localeService.t('threadCommentUI.item.edit')}</MenuItem>
|
||||
<MenuItem onClick={handleDeleteItem}>{localeService.t('threadCommentUI.item.delete')}</MenuItem>
|
||||
</Menu>
|
||||
)}
|
||||
>
|
||||
<div className={styles.threadCommentIcon}>
|
||||
<MoreHorizontalSingle />
|
||||
</div>
|
||||
</Dropdown>
|
||||
)
|
||||
: null}
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.threadCommentItemTime}>{item.dT}</div>
|
||||
{editing
|
||||
? (
|
||||
<ThreadCommentEditor
|
||||
id={item.id}
|
||||
comment={item}
|
||||
onCancel={() => onEditingChange?.(false)}
|
||||
autoFocus
|
||||
onSave={({ text, attachments }) => {
|
||||
onEditingChange?.(false);
|
||||
commandService.executeCommand(
|
||||
UpdateCommentCommand.id,
|
||||
{
|
||||
unitId,
|
||||
subUnitId,
|
||||
payload: {
|
||||
commentId: item.id,
|
||||
text,
|
||||
attachments,
|
||||
},
|
||||
} as IUpdateCommentCommandParams
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)
|
||||
: (
|
||||
<div className={styles.threadCommentItemContent}>
|
||||
{transformDocument2TextNodes(item.text).map((item, i) => {
|
||||
switch (item.type) {
|
||||
case 'mention':
|
||||
return (
|
||||
<a className={styles.threadCommentItemAt} key={i}>
|
||||
@
|
||||
{item.content.label}
|
||||
{' '}
|
||||
</a>
|
||||
);
|
||||
default:
|
||||
return item.content;
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const ThreadCommentTree = (props: IThreadCommentTreeProps) => {
|
||||
const {
|
||||
id,
|
||||
unitId,
|
||||
subUnitId,
|
||||
refStr,
|
||||
showEdit = true,
|
||||
onClick,
|
||||
showHighlight,
|
||||
onClose,
|
||||
getSubUnitName,
|
||||
prefix,
|
||||
autoFocus,
|
||||
} = props;
|
||||
const threadCommentModel = useDependency(ThreadCommentModel);
|
||||
|
||||
const [editingId, setEditingId] = useState('');
|
||||
useObservable(threadCommentModel.commentMap$);
|
||||
const comments = id ? threadCommentModel.getCommentWithChildren(unitId, subUnitId, id) : null;
|
||||
const commandService = useDependency(ICommandService);
|
||||
const userManagerService = useDependency(UserManagerService);
|
||||
const resolved = comments?.root.resolved;
|
||||
const currentUser = useObservable(userManagerService.currentUser$);
|
||||
const editorRef = useRef<IThreadCommentEditorInstance>(null);
|
||||
const renderComments = [
|
||||
...comments ?
|
||||
[comments.root] :
|
||||
// mock empty comment
|
||||
[{
|
||||
id: MOCK_ID,
|
||||
text: {
|
||||
dataStream: '\n\r',
|
||||
},
|
||||
personId: currentUser?.userID ?? '',
|
||||
ref: refStr ?? '',
|
||||
dT: '',
|
||||
unitId,
|
||||
subUnitId,
|
||||
}],
|
||||
...comments?.children ?? [],
|
||||
];
|
||||
const handleResolve = () => {
|
||||
commandService.executeCommand(ResolveCommentCommand.id, {
|
||||
unitId,
|
||||
subUnitId,
|
||||
commentId: id,
|
||||
resolved: !resolved,
|
||||
});
|
||||
onClose?.();
|
||||
};
|
||||
|
||||
const handleDeleteRoot = () => {
|
||||
commandService.executeCommand(
|
||||
DeleteCommentTreeCommand.id,
|
||||
{
|
||||
unitId,
|
||||
subUnitId,
|
||||
commentId: id,
|
||||
}
|
||||
);
|
||||
onClose?.();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.threadComment} onClick={onClick} id={`${prefix}-${unitId}-${subUnitId}-${id}`}>
|
||||
{showHighlight ? <div className={styles.threadCommentHighlight} /> : null}
|
||||
<div className={styles.threadCommentTitle}>
|
||||
<div className={styles.threadCommentTitlePosition}>
|
||||
<div className={styles.threadCommentTitleHighlight} />
|
||||
{refStr || comments?.root.ref}
|
||||
{' · '}
|
||||
{getSubUnitName(comments?.root.subUnitId ?? subUnitId)}
|
||||
</div>
|
||||
{comments
|
||||
? (
|
||||
<div className={styles.threadCommentIconContainer}>
|
||||
<div
|
||||
onClick={handleResolve}
|
||||
className={styles.threadCommentIcon}
|
||||
style={{ color: resolved ? 'rgb(var(--green-500))' : '' }}
|
||||
>
|
||||
{resolved ? <ResolvedSingle /> : <SolveSingle />}
|
||||
</div>
|
||||
{currentUser?.userID === comments.root.personId
|
||||
? (
|
||||
<div className={styles.threadCommentIcon} onClick={handleDeleteRoot}>
|
||||
<DeleteSingle />
|
||||
</div>
|
||||
)
|
||||
: null}
|
||||
</div>
|
||||
)
|
||||
: null}
|
||||
</div>
|
||||
<div className={styles.threadCommentContent}>
|
||||
{renderComments.map(
|
||||
(item) => (
|
||||
<ThreadCommentItem
|
||||
onClose={onClose}
|
||||
unitId={unitId}
|
||||
subUnitId={subUnitId}
|
||||
item={item}
|
||||
key={item.id}
|
||||
isRoot={item.id === comments?.root.id}
|
||||
editing={editingId === item.id}
|
||||
resolved={comments?.root.resolved}
|
||||
onEditingChange={(editing) => {
|
||||
if (editing) {
|
||||
setEditingId(item.id);
|
||||
} else {
|
||||
setEditingId('');
|
||||
}
|
||||
}}
|
||||
onReply={(user) => {
|
||||
if (!user) {
|
||||
return;
|
||||
}
|
||||
editorRef.current?.reply(transformTextNodes2Document([{
|
||||
type: 'mention',
|
||||
content: {
|
||||
id: user.userID,
|
||||
label: user.name,
|
||||
},
|
||||
}]));
|
||||
}}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
{showEdit && !editingId && !resolved
|
||||
? (
|
||||
<div>
|
||||
<ThreadCommentEditor
|
||||
key={`${autoFocus}`}
|
||||
ref={editorRef}
|
||||
onSave={({ text, attachments }) => {
|
||||
commandService.executeCommand(
|
||||
AddCommentCommand.id,
|
||||
{
|
||||
unitId,
|
||||
subUnitId,
|
||||
comment: {
|
||||
text,
|
||||
attachments,
|
||||
dT: dayjs().format('YYYY/MM/DD HH:mm'),
|
||||
id: Tools.generateRandomId(),
|
||||
ref: refStr,
|
||||
personId: currentUser?.userID,
|
||||
parentId: comments?.root.id,
|
||||
unitId,
|
||||
subUnitId,
|
||||
},
|
||||
} as IAddCommentCommandParams
|
||||
);
|
||||
}}
|
||||
autoFocus={autoFocus || (!comments)}
|
||||
onCancel={() => {
|
||||
if (!comments) {
|
||||
onClose?.();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
: null}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "@univerjs/shared/tsconfigs/base",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"outDir": "lib/types"
|
||||
},
|
||||
"references": [{ "path": "./tsconfig.node.json" }],
|
||||
"include": ["src"]
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "@univerjs/shared/tsconfigs/node",
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import createViteConfig from '@univerjs/shared/vite';
|
||||
import pkg from './package.json';
|
||||
|
||||
export default ({ mode }) => createViteConfig({}, {
|
||||
mode,
|
||||
pkg,
|
||||
features: {
|
||||
react: false,
|
||||
css: true,
|
||||
dom: true,
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
# @univerjs/thread-comment
|
||||
|
||||
[](https://npmjs.org/packages/@univerjs/thread-comment)
|
||||
[](https://img.shields.io/npm/l/@univerjs/thread-comment)
|
||||
|
||||
## Introduction
|
||||
|
||||
> TODO: Introduction
|
||||
|
||||
## Usage
|
||||
|
||||
### Installation
|
||||
|
||||
```shell
|
||||
npm i @univerjs/thread-comment
|
||||
```
|
||||
@@ -0,0 +1,84 @@
|
||||
{
|
||||
"name": "@univerjs/thread-comment",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"description": "",
|
||||
"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": [],
|
||||
"sideEffects": [
|
||||
"**/*.css"
|
||||
],
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./*": "./src/*"
|
||||
},
|
||||
"main": "./lib/cjs/index.js",
|
||||
"module": "./lib/es/index.js",
|
||||
"types": "./lib/types/index.d.ts",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"main": "./lib/cjs/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"
|
||||
},
|
||||
"./lib/*": "./lib/*"
|
||||
}
|
||||
},
|
||||
"directories": {
|
||||
"lib": "lib"
|
||||
},
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest",
|
||||
"coverage": "vitest run --coverage",
|
||||
"lint:types": "tsc --noEmit",
|
||||
"build": "tsc && vite build"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@univerjs/core": "workspace:*",
|
||||
"@wendellhu/redi": "^0.15.1",
|
||||
"rxjs": ">=7.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@univerjs/protocol": "^0.1.20"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@univerjs/core": "workspace:*",
|
||||
"@univerjs/design": "workspace:*",
|
||||
"@univerjs/shared": "workspace:*",
|
||||
"@univerjs/ui": "workspace:*",
|
||||
"@wendellhu/redi": "^0.15.1",
|
||||
"clsx": "^2.1.0",
|
||||
"less": "^4.2.0",
|
||||
"react": "^18.2.0",
|
||||
"rxjs": "^7.8.1",
|
||||
"typescript": "^5.3.3",
|
||||
"vite": "^5.1.4",
|
||||
"vitest": "^1.3.1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,337 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { CommandType, type ICommand, ICommandService, IUndoRedoService, sequenceExecute } from '@univerjs/core';
|
||||
import type { IThreadComment } from '../../types/interfaces/i-thread-comment';
|
||||
import { ThreadCommentModel } from '../../models/thread-comment.model';
|
||||
import { AddCommentMutation, DeleteCommentMutation, type IUpdateCommentPayload, ResolveCommentMutation, UpdateCommentMutation } from '../mutations/comment.mutation';
|
||||
import { IThreadCommentDataSourceService } from '../../services/tc-datasource.service';
|
||||
|
||||
export interface IAddCommentCommandParams {
|
||||
unitId: string;
|
||||
subUnitId: string;
|
||||
comment: IThreadComment;
|
||||
}
|
||||
|
||||
export const AddCommentCommand: ICommand<IAddCommentCommandParams> = {
|
||||
id: 'thread-comment.command.add-comment',
|
||||
type: CommandType.COMMAND,
|
||||
async handler(accessor, params) {
|
||||
if (!params) {
|
||||
return false;
|
||||
}
|
||||
const commandService = accessor.get(ICommandService);
|
||||
const undoRedoService = accessor.get(IUndoRedoService);
|
||||
const dataSourceService = accessor.get(IThreadCommentDataSourceService);
|
||||
const { unitId, subUnitId, comment: originComment } = params;
|
||||
const comment = await dataSourceService.addComment(originComment);
|
||||
const redo = {
|
||||
id: AddCommentMutation.id,
|
||||
params,
|
||||
};
|
||||
const undo = {
|
||||
id: DeleteCommentMutation.id,
|
||||
params: {
|
||||
unitId,
|
||||
subUnitId,
|
||||
commentId: comment.id,
|
||||
},
|
||||
};
|
||||
undoRedoService.pushUndoRedo({
|
||||
undoMutations: [undo],
|
||||
redoMutations: [redo],
|
||||
unitID: unitId,
|
||||
});
|
||||
commandService.executeCommand(redo.id, redo.params);
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
||||
export interface IUpdateCommentCommandParams {
|
||||
unitId: string;
|
||||
subUnitId: string;
|
||||
payload: IUpdateCommentPayload;
|
||||
}
|
||||
|
||||
export const UpdateCommentCommand: ICommand<IUpdateCommentCommandParams> = {
|
||||
id: 'thread-comment.command.update-comment',
|
||||
type: CommandType.COMMAND,
|
||||
async handler(accessor, params) {
|
||||
if (!params) {
|
||||
return false;
|
||||
}
|
||||
const { unitId, subUnitId, payload } = params;
|
||||
const commandService = accessor.get(ICommandService);
|
||||
const undoRedoService = accessor.get(IUndoRedoService);
|
||||
const threadCommentModel = accessor.get(ThreadCommentModel);
|
||||
const dataSourceService = accessor.get(IThreadCommentDataSourceService);
|
||||
const currentComment = threadCommentModel.getComment(
|
||||
unitId,
|
||||
subUnitId,
|
||||
payload.commentId
|
||||
);
|
||||
|
||||
if (!currentComment) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const success = await dataSourceService.updateComment({
|
||||
...currentComment,
|
||||
...payload,
|
||||
});
|
||||
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const redo = {
|
||||
id: UpdateCommentMutation.id,
|
||||
params,
|
||||
};
|
||||
const undo = {
|
||||
id: UpdateCommentMutation.id,
|
||||
params: {
|
||||
unitId,
|
||||
subUnitId,
|
||||
payload: {
|
||||
commentId: payload.commentId,
|
||||
text: currentComment.text,
|
||||
attachments: currentComment.attachments,
|
||||
updateT: currentComment.updateT,
|
||||
updated: currentComment.updated,
|
||||
},
|
||||
},
|
||||
};
|
||||
undoRedoService.pushUndoRedo({
|
||||
undoMutations: [undo],
|
||||
redoMutations: [redo],
|
||||
unitID: unitId,
|
||||
});
|
||||
commandService.executeCommand(redo.id, redo.params);
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
||||
export interface IUpdateCommentRefPayload {
|
||||
commentId: string;
|
||||
ref: string;
|
||||
}
|
||||
|
||||
export interface IUpdateCommentRefCommandParams {
|
||||
unitId: string;
|
||||
subUnitId: string;
|
||||
payload: IUpdateCommentRefPayload;
|
||||
}
|
||||
|
||||
export const UpdateCommentRefCommand: ICommand<IUpdateCommentRefCommandParams> = {
|
||||
id: 'thread-comment.command.update-comment-ref',
|
||||
type: CommandType.COMMAND,
|
||||
async handler(accessor, params) {
|
||||
if (!params) {
|
||||
return false;
|
||||
}
|
||||
const dataSourceService = accessor.get(IThreadCommentDataSourceService);
|
||||
const threadCommentModel = accessor.get(ThreadCommentModel);
|
||||
const commandService = accessor.get(ICommandService);
|
||||
const undoRedoService = accessor.get(IUndoRedoService);
|
||||
const { unitId, subUnitId, payload } = params;
|
||||
const currentComment = threadCommentModel.getComment(unitId, subUnitId, payload.commentId);
|
||||
|
||||
if (!currentComment) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const success = await dataSourceService.updateComment({
|
||||
...currentComment,
|
||||
ref: payload.ref,
|
||||
});
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
const redo = {
|
||||
id: UpdateCommentMutation.id,
|
||||
params,
|
||||
};
|
||||
const undo = {
|
||||
id: UpdateCommentMutation.id,
|
||||
params: {
|
||||
unitId,
|
||||
subUnitId,
|
||||
payload: {
|
||||
commentId: payload.commentId,
|
||||
ref: currentComment.ref,
|
||||
},
|
||||
},
|
||||
};
|
||||
undoRedoService.pushUndoRedo({
|
||||
undoMutations: [undo],
|
||||
redoMutations: [redo],
|
||||
unitID: unitId,
|
||||
});
|
||||
commandService.executeCommand(redo.id, redo.params);
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
||||
export interface IResolveCommentCommandParams {
|
||||
unitId: string;
|
||||
subUnitId: string;
|
||||
commentId: string;
|
||||
resolved: boolean;
|
||||
}
|
||||
|
||||
export const ResolveCommentCommand: ICommand<IResolveCommentCommandParams> = {
|
||||
id: 'thread-comment.command.resolve-comment',
|
||||
type: CommandType.COMMAND,
|
||||
async handler(accessor, params) {
|
||||
if (!params) {
|
||||
return false;
|
||||
}
|
||||
const { unitId, subUnitId, resolved, commentId } = params;
|
||||
const dataSourceService = accessor.get(IThreadCommentDataSourceService);
|
||||
const threadCommentModel = accessor.get(ThreadCommentModel);
|
||||
const currentComment = threadCommentModel.getComment(unitId, subUnitId, commentId);
|
||||
|
||||
if (!currentComment) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const success = await dataSourceService.updateComment({
|
||||
...currentComment,
|
||||
resolved,
|
||||
});
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
const commandService = accessor.get(ICommandService);
|
||||
|
||||
commandService.executeCommand(
|
||||
ResolveCommentMutation.id,
|
||||
params
|
||||
);
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
||||
export interface IDeleteCommentCommandParams {
|
||||
unitId: string;
|
||||
subUnitId: string;
|
||||
commentId: string;
|
||||
}
|
||||
|
||||
export const DeleteCommentCommand: ICommand<IDeleteCommentCommandParams> = {
|
||||
id: 'thread-comment.command.delete-comment',
|
||||
type: CommandType.COMMAND,
|
||||
async handler(accessor, params) {
|
||||
if (!params) {
|
||||
return false;
|
||||
}
|
||||
const threadCommentModel = accessor.get(ThreadCommentModel);
|
||||
const dataSourceService = accessor.get(IThreadCommentDataSourceService);
|
||||
const commandService = accessor.get(ICommandService);
|
||||
const undoRedoService = accessor.get(IUndoRedoService);
|
||||
const { unitId, subUnitId, commentId } = params;
|
||||
|
||||
const comment = threadCommentModel.getComment(unitId, subUnitId, commentId);
|
||||
if (!comment) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(await dataSourceService.deleteComment(commentId))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const redo = {
|
||||
id: DeleteCommentMutation.id,
|
||||
params,
|
||||
};
|
||||
const undo = {
|
||||
id: AddCommentMutation.id,
|
||||
params: {
|
||||
unitId,
|
||||
subUnitId,
|
||||
comment,
|
||||
},
|
||||
};
|
||||
undoRedoService.pushUndoRedo({
|
||||
undoMutations: [undo],
|
||||
redoMutations: [redo],
|
||||
unitID: unitId,
|
||||
});
|
||||
return commandService.executeCommand(redo.id, redo.params);
|
||||
},
|
||||
};
|
||||
|
||||
export interface IDeleteCommentTreeCommandParams {
|
||||
unitId: string;
|
||||
subUnitId: string;
|
||||
commentId: string;
|
||||
}
|
||||
|
||||
export const DeleteCommentTreeCommand: ICommand<IDeleteCommentCommandParams> = {
|
||||
id: 'thread-comment.command.delete-comment-tree',
|
||||
type: CommandType.COMMAND,
|
||||
async handler(accessor, params) {
|
||||
if (!params) {
|
||||
return false;
|
||||
}
|
||||
const threadCommentModel = accessor.get(ThreadCommentModel);
|
||||
const commandService = accessor.get(ICommandService);
|
||||
const dataSourceService = accessor.get(IThreadCommentDataSourceService);
|
||||
const undoRedoService = accessor.get(IUndoRedoService);
|
||||
const { unitId, subUnitId, commentId } = params;
|
||||
|
||||
const commentWithChildren = threadCommentModel.getCommentWithChildren(unitId, subUnitId, commentId);
|
||||
if (!commentWithChildren) {
|
||||
return false;
|
||||
}
|
||||
const comments = [commentWithChildren.root, ...commentWithChildren.children];
|
||||
|
||||
if (!(await dataSourceService.deleteCommentBatch(comments.map((comment) => comment.id)))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const redos = comments.map((item) => ({
|
||||
id: DeleteCommentMutation.id,
|
||||
params: {
|
||||
unitId,
|
||||
subUnitId,
|
||||
commentId: item.id,
|
||||
},
|
||||
}));
|
||||
|
||||
const undos = comments.map((item) => ({
|
||||
id: AddCommentMutation.id,
|
||||
params: {
|
||||
unitId,
|
||||
subUnitId,
|
||||
comment: item,
|
||||
},
|
||||
}));
|
||||
|
||||
const result = sequenceExecute(redos, commandService);
|
||||
|
||||
if (result.result) {
|
||||
undoRedoService.pushUndoRedo({
|
||||
undoMutations: undos,
|
||||
redoMutations: redos,
|
||||
unitID: unitId,
|
||||
});
|
||||
}
|
||||
|
||||
return result.result;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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, IDocumentBody } from '@univerjs/core';
|
||||
import { CommandType } from '@univerjs/core';
|
||||
import type { IThreadComment } from '../../types/interfaces/i-thread-comment';
|
||||
import { ThreadCommentModel } from '../../models/thread-comment.model';
|
||||
|
||||
export interface IAddCommentMutationParams {
|
||||
unitId: string;
|
||||
subUnitId: string;
|
||||
comment: IThreadComment;
|
||||
}
|
||||
|
||||
export const AddCommentMutation: ICommand<IAddCommentMutationParams> = {
|
||||
id: 'thread-comment.mutation.add-comment',
|
||||
type: CommandType.MUTATION,
|
||||
handler(accessor, params) {
|
||||
if (!params) {
|
||||
return false;
|
||||
}
|
||||
const threadCommentModel = accessor.get(ThreadCommentModel);
|
||||
const { unitId, subUnitId, comment } = params;
|
||||
return threadCommentModel.addComment(unitId, subUnitId, comment);
|
||||
},
|
||||
};
|
||||
|
||||
export interface IUpdateCommentPayload {
|
||||
commentId: string;
|
||||
text: IDocumentBody;
|
||||
attachments?: string[];
|
||||
updated?: boolean;
|
||||
updateT?: string;
|
||||
}
|
||||
|
||||
export interface IUpdateCommentMutationParams {
|
||||
unitId: string;
|
||||
subUnitId: string;
|
||||
payload: IUpdateCommentPayload;
|
||||
}
|
||||
|
||||
export const UpdateCommentMutation: ICommand<IUpdateCommentMutationParams> = {
|
||||
id: 'thread-comment.mutation.update-comment',
|
||||
type: CommandType.MUTATION,
|
||||
handler(accessor, params) {
|
||||
if (!params) {
|
||||
return false;
|
||||
}
|
||||
const threadCommentModel = accessor.get(ThreadCommentModel);
|
||||
const { unitId, subUnitId, payload } = params;
|
||||
return threadCommentModel.updateComment(unitId, subUnitId, payload);
|
||||
},
|
||||
};
|
||||
|
||||
export interface IUpdateCommentRefPayload {
|
||||
commentId: string;
|
||||
ref: string;
|
||||
}
|
||||
|
||||
export interface IUpdateCommentRefMutationParams {
|
||||
unitId: string;
|
||||
subUnitId: string;
|
||||
payload: IUpdateCommentRefPayload;
|
||||
}
|
||||
|
||||
export const UpdateCommentRefMutation: ICommand<IUpdateCommentRefMutationParams> = {
|
||||
id: 'thread-comment.mutation.update-comment-ref',
|
||||
type: CommandType.MUTATION,
|
||||
handler(accessor, params) {
|
||||
if (!params) {
|
||||
return false;
|
||||
}
|
||||
const threadCommentModel = accessor.get(ThreadCommentModel);
|
||||
const { unitId, subUnitId, payload } = params;
|
||||
return threadCommentModel.updateCommentRef(unitId, subUnitId, payload);
|
||||
},
|
||||
};
|
||||
|
||||
export interface IResolveCommentMutationParams {
|
||||
unitId: string;
|
||||
subUnitId: string;
|
||||
commentId: string;
|
||||
resolved: boolean;
|
||||
}
|
||||
|
||||
export const ResolveCommentMutation: ICommand<IResolveCommentMutationParams> = {
|
||||
id: 'thread-comment.mutation.resolve-comment',
|
||||
type: CommandType.MUTATION,
|
||||
handler(accessor, params) {
|
||||
if (!params) {
|
||||
return false;
|
||||
}
|
||||
const threadCommentModel = accessor.get(ThreadCommentModel);
|
||||
const { unitId, subUnitId, resolved, commentId } = params;
|
||||
return threadCommentModel.resolveComment(unitId, subUnitId, commentId, resolved);
|
||||
},
|
||||
};
|
||||
|
||||
export interface IDeleteCommentMutationParams {
|
||||
unitId: string;
|
||||
subUnitId: string;
|
||||
commentId: string;
|
||||
}
|
||||
|
||||
export const DeleteCommentMutation: ICommand<IDeleteCommentMutationParams> = {
|
||||
id: 'thread-comment.mutation.delete-comment',
|
||||
type: CommandType.MUTATION,
|
||||
handler(accessor, params) {
|
||||
if (!params) {
|
||||
return false;
|
||||
}
|
||||
const threadCommentModel = accessor.get(ThreadCommentModel);
|
||||
const { unitId, subUnitId, commentId } = params;
|
||||
return threadCommentModel.deleteComment(unitId, subUnitId, commentId);
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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, IResourceManagerService, LifecycleStages, OnLifecycle } from '@univerjs/core';
|
||||
import { Inject } from '@wendellhu/redi';
|
||||
import { UniverType } from '@univerjs/protocol';
|
||||
import { ThreadCommentModel } from '../models/thread-comment.model';
|
||||
import { TC_PLUGIN_NAME } from '../types/const';
|
||||
import type { IThreadComment } from '../types/interfaces/i-thread-comment';
|
||||
import { IThreadCommentDataSourceService } from '../services/tc-datasource.service';
|
||||
|
||||
export type UnitThreadCommentJSON = Record<string, IThreadComment[]>;
|
||||
|
||||
@OnLifecycle(LifecycleStages.Starting, ThreadCommentResourceController)
|
||||
export class ThreadCommentResourceController extends Disposable {
|
||||
constructor(
|
||||
@IResourceManagerService private readonly _resourceManagerService: IResourceManagerService,
|
||||
@Inject(ThreadCommentModel) private readonly _threadCommentModel: ThreadCommentModel,
|
||||
@IThreadCommentDataSourceService private readonly _threadCommentDataSourceService: IThreadCommentDataSourceService
|
||||
) {
|
||||
super();
|
||||
this._initSnapshot();
|
||||
}
|
||||
|
||||
private _initSnapshot() {
|
||||
const toJson = (unitID: string) => {
|
||||
const map = this._threadCommentModel.getUnit(unitID);
|
||||
const resultMap: UnitThreadCommentJSON = {};
|
||||
if (map) {
|
||||
map.forEach(([key, v]) => {
|
||||
resultMap[key] = v;
|
||||
});
|
||||
|
||||
return JSON.stringify(this._threadCommentDataSourceService.saveToSnapshot(resultMap));
|
||||
}
|
||||
return '';
|
||||
};
|
||||
const parseJson = (json: string): UnitThreadCommentJSON => {
|
||||
if (!json) {
|
||||
return {};
|
||||
}
|
||||
try {
|
||||
return JSON.parse(json);
|
||||
} catch (err) {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
this.disposeWithMe(
|
||||
this._resourceManagerService.registerPluginResource({
|
||||
pluginName: `SHEET_${TC_PLUGIN_NAME}`,
|
||||
businesses: [UniverType.UNIVER_SHEET],
|
||||
toJson: (unitID) => toJson(unitID),
|
||||
parseJson: (json) => parseJson(json),
|
||||
onUnLoad: (unitID) => {
|
||||
this._threadCommentModel.deleteUnit(unitID);
|
||||
},
|
||||
onLoad: async (unitID, value) => {
|
||||
const unitComments = await this._threadCommentDataSourceService.loadFormSnapshot(value);
|
||||
Object.keys(unitComments).forEach((subunitId) => {
|
||||
const commentList = value[subunitId];
|
||||
commentList.forEach((comment) => {
|
||||
this._threadCommentModel.addComment(unitID, subunitId, comment);
|
||||
});
|
||||
});
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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 { ThreadCommentModel, type CommentUpdate } from './models/thread-comment.model';
|
||||
export { ThreadCommentResourceController } from './controllers/tc-resource.controller';
|
||||
export { TC_PLUGIN_NAME } from './types/const';
|
||||
export {
|
||||
AddCommentMutation,
|
||||
DeleteCommentMutation,
|
||||
ResolveCommentMutation,
|
||||
UpdateCommentMutation,
|
||||
UpdateCommentRefMutation,
|
||||
} from './commands/mutations/comment.mutation';
|
||||
export type {
|
||||
IAddCommentMutationParams,
|
||||
IDeleteCommentMutationParams,
|
||||
IResolveCommentMutationParams,
|
||||
IUpdateCommentMutationParams,
|
||||
IUpdateCommentPayload,
|
||||
IUpdateCommentRefMutationParams,
|
||||
} from './commands/mutations/comment.mutation';
|
||||
export type { IThreadComment, IThreadCommentMention } from './types/interfaces/i-thread-comment';
|
||||
export {
|
||||
AddCommentCommand,
|
||||
DeleteCommentCommand,
|
||||
ResolveCommentCommand,
|
||||
UpdateCommentCommand,
|
||||
DeleteCommentTreeCommand,
|
||||
} from './commands/commands/comment.command';
|
||||
export type {
|
||||
IAddCommentCommandParams,
|
||||
IDeleteCommentCommandParams,
|
||||
IResolveCommentCommandParams,
|
||||
IUpdateCommentCommandParams,
|
||||
IDeleteCommentTreeCommandParams,
|
||||
IUpdateCommentRefCommandParams,
|
||||
IUpdateCommentRefPayload,
|
||||
} from './commands/commands/comment.command';
|
||||
export { UniverThreadCommentPlugin } from './plugin';
|
||||
@@ -0,0 +1,339 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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 { BehaviorSubject, map, Subject } from 'rxjs';
|
||||
import { CustomRangeType } from '@univerjs/core';
|
||||
import type { IThreadComment } from '../types/interfaces/i-thread-comment';
|
||||
import type { IUpdateCommentPayload, IUpdateCommentRefPayload } from '../commands/mutations/comment.mutation';
|
||||
|
||||
export type CommentUpdate = {
|
||||
unitId: string;
|
||||
subUnitId: string;
|
||||
type: 'add';
|
||||
payload: IThreadComment;
|
||||
} | {
|
||||
unitId: string;
|
||||
subUnitId: string;
|
||||
type: 'update';
|
||||
payload: IUpdateCommentPayload;
|
||||
} | {
|
||||
unitId: string;
|
||||
subUnitId: string;
|
||||
type: 'delete';
|
||||
payload: {
|
||||
commentId: string;
|
||||
isRoot: boolean;
|
||||
comment: IThreadComment;
|
||||
};
|
||||
} | {
|
||||
unitId: string;
|
||||
subUnitId: string;
|
||||
type: 'updateRef';
|
||||
payload: IUpdateCommentRefPayload;
|
||||
} | {
|
||||
unitId: string;
|
||||
subUnitId: string;
|
||||
type: 'resolve';
|
||||
payload: {
|
||||
commentId: string;
|
||||
resolved: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
export class ThreadCommentModel {
|
||||
private _commentsMap: Record<string, Record<string, Record<string, IThreadComment>>> = {};
|
||||
private _commentsTreeMap: Map<string, Map<string, Map<string, string[]>>> = new Map();
|
||||
private _commentUpdate$ = new Subject<CommentUpdate>();
|
||||
private _commentsTreeMap$ = new BehaviorSubject<Record<string, Record<string, Record<string, string[]>>>>({});
|
||||
private _commentsMap$ = new BehaviorSubject<Record<string, Record<string, Record<string, IThreadComment>>>>({});
|
||||
|
||||
commentUpdate$ = this._commentUpdate$.asObservable();
|
||||
commentTreeMap$ = this._commentsTreeMap$.asObservable();
|
||||
commentMap$ = this._commentsMap$.asObservable();
|
||||
|
||||
private _ensureCommentMap(unitId: string, subUnitId: string) {
|
||||
let unitMap = this._commentsMap[unitId];
|
||||
|
||||
if (!unitMap) {
|
||||
unitMap = {};
|
||||
this._commentsMap[unitId] = unitMap;
|
||||
}
|
||||
|
||||
let subUnitMap = unitMap[subUnitId];
|
||||
if (!subUnitMap) {
|
||||
subUnitMap = {};
|
||||
unitMap[subUnitId] = subUnitMap;
|
||||
}
|
||||
|
||||
return subUnitMap;
|
||||
}
|
||||
|
||||
private _ensureCommentChildrenMap(unitId: string, subUnitId: string) {
|
||||
let unitMap = this._commentsTreeMap.get(unitId);
|
||||
|
||||
if (!unitMap) {
|
||||
unitMap = new Map();
|
||||
this._commentsTreeMap.set(unitId, unitMap);
|
||||
}
|
||||
|
||||
let subUnitMap = unitMap.get(subUnitId);
|
||||
if (!subUnitMap) {
|
||||
subUnitMap = new Map();
|
||||
unitMap.set(subUnitId, subUnitMap);
|
||||
}
|
||||
|
||||
return subUnitMap;
|
||||
}
|
||||
|
||||
private _refreshCommentsMap$() {
|
||||
this._commentsMap$.next({
|
||||
...this._commentsMap,
|
||||
});
|
||||
}
|
||||
|
||||
private _refreshCommentsTreeMap$() {
|
||||
const map: Record<string, Record<string, Record<string, string[]>>> = {};
|
||||
this._commentsTreeMap.forEach((unit, unitId) => {
|
||||
map[unitId] = {};
|
||||
const unitRecord = map[unitId];
|
||||
unit.forEach((subUnit, subUnitId) => {
|
||||
unitRecord[subUnitId] = {};
|
||||
const subUnitRecord = unitRecord[subUnitId];
|
||||
subUnit.forEach((children, key) => {
|
||||
subUnitRecord[key] = children;
|
||||
});
|
||||
});
|
||||
});
|
||||
this._commentsTreeMap$.next(map);
|
||||
}
|
||||
|
||||
ensureMap(unitId: string, subUnitId: string) {
|
||||
const commentMap = this._ensureCommentMap(unitId, subUnitId);
|
||||
const commentChildrenMap = this._ensureCommentChildrenMap(unitId, subUnitId);
|
||||
|
||||
return {
|
||||
commentMap,
|
||||
commentChildrenMap,
|
||||
};
|
||||
}
|
||||
|
||||
addComment(unitId: string, subUnitId: string, comment: IThreadComment) {
|
||||
const { commentMap, commentChildrenMap } = this.ensureMap(unitId, subUnitId);
|
||||
|
||||
let parentId = comment.parentId;
|
||||
if (parentId) {
|
||||
let parent = commentMap[parentId];
|
||||
// find the top root
|
||||
while (parent?.parentId) {
|
||||
parent = commentMap[parent.parentId];
|
||||
parentId = parent.parentId!;
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
let children = commentChildrenMap.get(parentId);
|
||||
if (!children) {
|
||||
children = [];
|
||||
}
|
||||
children.push(comment.id);
|
||||
commentChildrenMap.set(parentId, children);
|
||||
}
|
||||
} else {
|
||||
commentChildrenMap.set(comment.id, []);
|
||||
}
|
||||
|
||||
commentMap[comment.id] = comment;
|
||||
this._commentUpdate$.next({
|
||||
unitId,
|
||||
subUnitId,
|
||||
type: 'add',
|
||||
payload: comment,
|
||||
});
|
||||
this._refreshCommentsMap$();
|
||||
this._refreshCommentsTreeMap$();
|
||||
return true;
|
||||
}
|
||||
|
||||
updateComment(unitId: string, subUnitId: string, payload: IUpdateCommentPayload) {
|
||||
const { commentMap } = this.ensureMap(unitId, subUnitId);
|
||||
const oldComment = commentMap[payload.commentId];
|
||||
if (!oldComment) {
|
||||
return false;
|
||||
}
|
||||
oldComment.updated = true;
|
||||
oldComment.text = payload.text;
|
||||
oldComment.attachments = payload.attachments;
|
||||
oldComment.updateT = payload.updateT;
|
||||
|
||||
this._commentUpdate$.next({
|
||||
unitId,
|
||||
subUnitId,
|
||||
type: 'update',
|
||||
payload,
|
||||
});
|
||||
this._refreshCommentsMap$();
|
||||
this._refreshCommentsTreeMap$();
|
||||
return true;
|
||||
}
|
||||
|
||||
updateCommentRef(unitId: string, subUnitId: string, payload: IUpdateCommentRefPayload) {
|
||||
const { commentMap } = this.ensureMap(unitId, subUnitId);
|
||||
const oldComment = commentMap[payload.commentId];
|
||||
if (!oldComment) {
|
||||
return false;
|
||||
}
|
||||
|
||||
oldComment.ref = payload.ref;
|
||||
|
||||
this._commentUpdate$.next({
|
||||
unitId,
|
||||
subUnitId,
|
||||
type: 'updateRef',
|
||||
payload,
|
||||
});
|
||||
this._refreshCommentsMap$();
|
||||
this._refreshCommentsTreeMap$();
|
||||
return true;
|
||||
}
|
||||
|
||||
resolveComment(unitId: string, subUnitId: string, commentId: string, resolved: boolean) {
|
||||
const { commentMap } = this.ensureMap(unitId, subUnitId);
|
||||
const oldComment = commentMap[commentId];
|
||||
if (!oldComment) {
|
||||
return false;
|
||||
}
|
||||
|
||||
oldComment.resolved = resolved;
|
||||
this._commentUpdate$.next({
|
||||
unitId,
|
||||
subUnitId,
|
||||
type: 'resolve',
|
||||
payload: {
|
||||
commentId,
|
||||
resolved,
|
||||
},
|
||||
});
|
||||
this._refreshCommentsMap$();
|
||||
this._refreshCommentsTreeMap$();
|
||||
return true;
|
||||
}
|
||||
|
||||
getComment(unitId: string, subUnitId: string, commentId: string) {
|
||||
const { commentMap } = this.ensureMap(unitId, subUnitId);
|
||||
return commentMap[commentId] as IThreadComment | undefined;
|
||||
}
|
||||
|
||||
getComment$(unitId: string, subUnitId: string, commentId: string) {
|
||||
return this._commentsMap$.pipe(map((records) => records[unitId][subUnitId][commentId]));
|
||||
}
|
||||
|
||||
getCommentWithChildren(unitId: string, subUnitId: string, commentId: string) {
|
||||
const { commentMap, commentChildrenMap } = this.ensureMap(unitId, subUnitId);
|
||||
const current = commentMap[commentId];
|
||||
if (!current) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const relativeUsers = new Set<string>();
|
||||
const children = commentChildrenMap.get(commentId) ?? [];
|
||||
const childrenComments = children?.map((childId) => commentMap[childId]!);
|
||||
|
||||
[current, ...childrenComments].forEach((comment) => {
|
||||
relativeUsers.add(comment.personId);
|
||||
comment.text.customRanges?.forEach((range) => {
|
||||
if (range.rangeType === CustomRangeType.MENTION) {
|
||||
relativeUsers.add(range.rangeId);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
root: current,
|
||||
children: childrenComments,
|
||||
relativeUsers,
|
||||
};
|
||||
}
|
||||
|
||||
deleteComment(unitId: string, subUnitId: string, commentId: string) {
|
||||
const { commentMap, commentChildrenMap } = this.ensureMap(unitId, subUnitId);
|
||||
const current = commentMap[commentId];
|
||||
if (!current) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (current.parentId) {
|
||||
const children = commentChildrenMap.get(current.parentId);
|
||||
if (children) {
|
||||
const index = children.indexOf(commentId);
|
||||
children.splice(index, 1);
|
||||
}
|
||||
delete commentMap[commentId];
|
||||
} else {
|
||||
delete commentMap[commentId];
|
||||
commentChildrenMap.delete(commentId);
|
||||
}
|
||||
|
||||
this._commentUpdate$.next({
|
||||
unitId,
|
||||
subUnitId,
|
||||
type: 'delete',
|
||||
payload: {
|
||||
commentId,
|
||||
isRoot: !current.parentId,
|
||||
comment: current,
|
||||
},
|
||||
});
|
||||
this._refreshCommentsMap$();
|
||||
this._refreshCommentsTreeMap$();
|
||||
return true;
|
||||
}
|
||||
|
||||
getUnit(unitId: string) {
|
||||
const unitMap = this._commentsMap[unitId];
|
||||
if (!unitMap) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Array.from(Object.entries(unitMap)).map(([subUnitId, subUnitMap]) => [subUnitId, Array.from(Object.values(subUnitMap))] as const);
|
||||
}
|
||||
|
||||
deleteUnit(unitId: string) {
|
||||
const unitMap = this._commentsMap[unitId];
|
||||
if (!unitMap) {
|
||||
return;
|
||||
}
|
||||
|
||||
Object.entries(unitMap).forEach(([subUnitId, subUnitMap]) => {
|
||||
Object.values(subUnitMap).forEach((comment) => {
|
||||
this.deleteComment(unitId, subUnitId, comment.id);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getRootCommentIds(unitId: string, subUnitId: string) {
|
||||
const commentChildrenMap = this._ensureCommentChildrenMap(unitId, subUnitId);
|
||||
return Array.from(commentChildrenMap.keys());
|
||||
}
|
||||
|
||||
getRootCommentIds$(unitId: string, subUnitId: string) {
|
||||
return this._commentsTreeMap$.pipe(map(
|
||||
(tree) => Object.keys(tree[unitId]?.[subUnitId])
|
||||
));
|
||||
}
|
||||
|
||||
getAll() {
|
||||
return this._commentsMap;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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 { ICommandService, Plugin, UniverInstanceType } from '@univerjs/core';
|
||||
import { type Dependency, Inject, Injector } from '@wendellhu/redi';
|
||||
import { ThreadCommentModel } from './models/thread-comment.model';
|
||||
import { ThreadCommentResourceController } from './controllers/tc-resource.controller';
|
||||
import { TC_PLUGIN_NAME } from './types/const';
|
||||
import { AddCommentMutation, DeleteCommentMutation, ResolveCommentMutation, UpdateCommentMutation, UpdateCommentRefMutation } from './commands/mutations/comment.mutation';
|
||||
import { AddCommentCommand, DeleteCommentCommand, DeleteCommentTreeCommand, ResolveCommentCommand, UpdateCommentCommand } from './commands/commands/comment.command';
|
||||
import { IThreadCommentDataSourceService, ThreadCommentDataSourceService } from './services/tc-datasource.service';
|
||||
|
||||
export class UniverThreadCommentPlugin extends Plugin {
|
||||
static override pluginName = TC_PLUGIN_NAME;
|
||||
static override type = UniverInstanceType.UNIVER_UNKNOWN;
|
||||
|
||||
constructor(
|
||||
_config: unknown,
|
||||
@Inject(Injector) protected _injector: Injector,
|
||||
@ICommandService protected _commandService: ICommandService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
override onStarting(injector: Injector): void {
|
||||
([
|
||||
[ThreadCommentModel],
|
||||
[ThreadCommentResourceController],
|
||||
[IThreadCommentDataSourceService, { useClass: ThreadCommentDataSourceService }],
|
||||
] as Dependency[]).forEach(
|
||||
(d) => {
|
||||
injector.add(d);
|
||||
}
|
||||
);
|
||||
|
||||
[
|
||||
AddCommentCommand,
|
||||
UpdateCommentCommand,
|
||||
DeleteCommentCommand,
|
||||
ResolveCommentCommand,
|
||||
DeleteCommentTreeCommand,
|
||||
|
||||
AddCommentMutation,
|
||||
UpdateCommentMutation,
|
||||
UpdateCommentRefMutation,
|
||||
DeleteCommentMutation,
|
||||
ResolveCommentMutation,
|
||||
].forEach((command) => {
|
||||
this._commandService.registerCommand(command);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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 { createIdentifier } from '@wendellhu/redi';
|
||||
import type { IThreadComment } from '../types/interfaces/i-thread-comment';
|
||||
|
||||
export type ThreadCommentJSON = { id: string } & Partial<Omit<IThreadComment, 'id'>>;
|
||||
|
||||
export interface IThreadCommentDataSourceService {
|
||||
addComment: (comment: IThreadComment) => Promise<IThreadComment>;
|
||||
updateComment: (comment: IThreadComment) => Promise<boolean>;
|
||||
deleteComment: (commentId: string) => Promise<boolean>;
|
||||
deleteCommentBatch: (commentIds: string[]) => Promise<boolean>;
|
||||
loadFormSnapshot: (unitComments: Record<string, ThreadCommentJSON[]>) => Promise<Record<string, IThreadComment[]>>;
|
||||
saveToSnapshot: (unitComments: Record<string, IThreadComment[]>) => Record<string, ThreadCommentJSON[]>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Preserve for import async comment system
|
||||
*/
|
||||
export class ThreadCommentDataSourceService implements IThreadCommentDataSourceService {
|
||||
async addComment(comment: IThreadComment) {
|
||||
return comment;
|
||||
}
|
||||
|
||||
async updateComment(_comment: IThreadComment) {
|
||||
return true;
|
||||
}
|
||||
|
||||
async deleteComment(_commentId: string) {
|
||||
return true;
|
||||
}
|
||||
|
||||
async deleteCommentBatch(_commentIds: string[]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
async loadFormSnapshot(unitComments: Record<string, ThreadCommentJSON[]>) {
|
||||
return unitComments as Record<string, IThreadComment[]>;
|
||||
}
|
||||
|
||||
saveToSnapshot(unitComments: Record<string, IThreadComment[]>) {
|
||||
return unitComments;
|
||||
}
|
||||
}
|
||||
|
||||
export const IThreadCommentDataSourceService = createIdentifier<IThreadCommentDataSourceService>('univer.thread-comment.data-source-service');
|
||||
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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 TC_PLUGIN_NAME = 'THREAD_COMMENT_PLUGIN';
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright 2023-present DreamNum Inc.
|
||||
*
|
||||
* 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 { IDocumentBody } from '@univerjs/core';
|
||||
|
||||
export interface IThreadCommentMention {
|
||||
label: string;
|
||||
id: string;
|
||||
icon?: string;
|
||||
}
|
||||
|
||||
export interface IThreadComment {
|
||||
id: string;
|
||||
ref: string;
|
||||
dT: string;
|
||||
updateT?: string;
|
||||
personId: string;
|
||||
parentId?: string;
|
||||
text: IDocumentBody;
|
||||
attachments?: string[];
|
||||
resolved?: boolean;
|
||||
updated?: boolean;
|
||||
unitId: string;
|
||||
subUnitId: string;
|
||||
mentions?: string[];
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "@univerjs/shared/tsconfigs/base",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"outDir": "lib/types"
|
||||
},
|
||||
"references": [{ "path": "./tsconfig.node.json" }],
|
||||
"include": ["src"]
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "@univerjs/shared/tsconfigs/node",
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import createViteConfig from '@univerjs/shared/vite';
|
||||
import pkg from './package.json';
|
||||
|
||||
export default ({ mode }) => createViteConfig({}, {
|
||||
mode,
|
||||
pkg,
|
||||
features: {
|
||||
react: false,
|
||||
css: true,
|
||||
dom: true,
|
||||
},
|
||||
});
|
||||
@@ -25,6 +25,11 @@ import type { IZenZoneService } from './zen-zone.service';
|
||||
export class DesktopZenZoneService implements IZenZoneService {
|
||||
readonly visible$ = new Subject<boolean>();
|
||||
readonly componentKey$ = new Subject<string>();
|
||||
private _visible = false;
|
||||
|
||||
get visible() {
|
||||
return this._visible;
|
||||
}
|
||||
|
||||
constructor(@Inject(ComponentManager) private readonly _componentManager: ComponentManager) {
|
||||
// super
|
||||
@@ -42,10 +47,12 @@ export class DesktopZenZoneService implements IZenZoneService {
|
||||
}
|
||||
|
||||
open(): void {
|
||||
this._visible = true;
|
||||
this.visible$.next(true);
|
||||
}
|
||||
|
||||
close() {
|
||||
this._visible = false;
|
||||
this.visible$.next(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ export interface IZenZoneService {
|
||||
readonly visible$: Subject<boolean>;
|
||||
readonly componentKey$: Subject<string>;
|
||||
|
||||
readonly visible: boolean;
|
||||
|
||||
set(key: string, component: any): IDisposable;
|
||||
|
||||
open(): void;
|
||||
|
||||
@@ -150,6 +150,7 @@ export function App(props: IUniverAppProps) {
|
||||
data-range-selector
|
||||
onContextMenu={(e) => e.preventDefault()}
|
||||
>
|
||||
<CanvasPopup />
|
||||
<ComponentContainer components={contentComponents} />
|
||||
</section>
|
||||
</section>
|
||||
@@ -167,11 +168,8 @@ export function App(props: IUniverAppProps) {
|
||||
)}
|
||||
|
||||
<ZenZone />
|
||||
|
||||
</section>
|
||||
<CanvasPopup />
|
||||
</div>
|
||||
|
||||
<ComponentContainer components={globalComponents} />
|
||||
<ComponentContainer components={builtInGlobalComponents} />
|
||||
{contextMenu && <ContextMenu />}
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
|
||||
&-canvas {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&-left-sidebar {
|
||||
|
||||
@@ -56,7 +56,6 @@ export function CanvasPopup() {
|
||||
const popupService = useDependency(ICanvasPopupService);
|
||||
const popups = useObservable(popupService.popups$, undefined, true);
|
||||
const componentManager = useDependency(ComponentManager);
|
||||
|
||||
return popups.map((item) => {
|
||||
const [key, popup] = item;
|
||||
const Component = componentManager.get(popup.componentKey);
|
||||
|
||||
Generated
+445
-51
@@ -264,6 +264,9 @@ importers:
|
||||
'@univerjs/sheets-numfmt':
|
||||
specifier: workspace:*
|
||||
version: link:../packages/sheets-numfmt
|
||||
'@univerjs/sheets-thread-comment':
|
||||
specifier: workspace:*
|
||||
version: link:../packages/sheets-thread-comment
|
||||
'@univerjs/sheets-ui':
|
||||
specifier: workspace:*
|
||||
version: link:../packages/sheets-ui
|
||||
@@ -276,6 +279,12 @@ importers:
|
||||
'@univerjs/slides-ui':
|
||||
specifier: workspace:*
|
||||
version: link:../packages/slides-ui
|
||||
'@univerjs/thread-comment':
|
||||
specifier: workspace:*
|
||||
version: link:../packages/thread-comment
|
||||
'@univerjs/thread-comment-ui':
|
||||
specifier: workspace:*
|
||||
version: link:../packages/thread-comment-ui
|
||||
'@univerjs/ui':
|
||||
specifier: workspace:*
|
||||
version: link:../packages/ui
|
||||
@@ -457,6 +466,9 @@ importers:
|
||||
'@rc-component/trigger':
|
||||
specifier: ^1.18.3
|
||||
version: 1.18.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@types/react-mentions':
|
||||
specifier: ^4.1.13
|
||||
version: 4.1.13
|
||||
'@univerjs/icons':
|
||||
specifier: ^0.1.45
|
||||
version: 0.1.45(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
@@ -499,6 +511,9 @@ importers:
|
||||
react-grid-layout:
|
||||
specifier: ^1.4.4
|
||||
version: 1.4.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
react-mentions:
|
||||
specifier: ^4.4.10
|
||||
version: 4.4.10(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
react-transition-group:
|
||||
specifier: ^4.4.5
|
||||
version: 4.4.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
@@ -1384,14 +1399,75 @@ importers:
|
||||
specifier: ^1.6.0
|
||||
version: 1.6.0(@types/node@20.12.12)(happy-dom@13.3.8)(jsdom@24.0.0)(less@4.2.0)(sass@1.72.0)(terser@5.30.0)
|
||||
|
||||
packages/sheets-thread-comment:
|
||||
dependencies:
|
||||
'@univerjs/engine-render':
|
||||
specifier: workspace:*
|
||||
version: link:../engine-render
|
||||
'@univerjs/icons':
|
||||
specifier: ^0.1.45
|
||||
version: 0.1.45(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@univerjs/sheets':
|
||||
specifier: workspace:*
|
||||
version: link:../sheets
|
||||
'@univerjs/sheets-ui':
|
||||
specifier: workspace:*
|
||||
version: link:../sheets-ui
|
||||
'@univerjs/thread-comment':
|
||||
specifier: workspace:*
|
||||
version: link:../thread-comment
|
||||
'@univerjs/thread-comment-ui':
|
||||
specifier: workspace:*
|
||||
version: link:../thread-comment-ui
|
||||
devDependencies:
|
||||
'@univerjs/core':
|
||||
specifier: workspace:*
|
||||
version: link:../core
|
||||
'@univerjs/design':
|
||||
specifier: workspace:*
|
||||
version: link:../design
|
||||
'@univerjs/engine-formula':
|
||||
specifier: workspace:*
|
||||
version: link:../engine-formula
|
||||
'@univerjs/shared':
|
||||
specifier: workspace:*
|
||||
version: link:../../common/shared
|
||||
'@univerjs/ui':
|
||||
specifier: workspace:*
|
||||
version: link:../ui
|
||||
'@wendellhu/redi':
|
||||
specifier: ^0.15.1
|
||||
version: 0.15.1
|
||||
clsx:
|
||||
specifier: ^2.1.0
|
||||
version: 2.1.1
|
||||
less:
|
||||
specifier: ^4.2.0
|
||||
version: 4.2.0
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.2.0
|
||||
rxjs:
|
||||
specifier: ^7.8.1
|
||||
version: 7.8.1
|
||||
typescript:
|
||||
specifier: ^5.3.3
|
||||
version: 5.4.5
|
||||
vite:
|
||||
specifier: ^5.1.4
|
||||
version: 5.2.10(@types/node@20.12.12)(less@4.2.0)(sass@1.72.0)(terser@5.30.0)
|
||||
vitest:
|
||||
specifier: ^1.3.1
|
||||
version: 1.5.2(@types/node@20.12.12)(happy-dom@13.3.8)(jsdom@24.0.0)(less@4.2.0)(sass@1.72.0)(terser@5.30.0)
|
||||
|
||||
packages/sheets-ui:
|
||||
dependencies:
|
||||
'@univerjs/docs-ui':
|
||||
specifier: workspace:*
|
||||
version: link:../docs-ui
|
||||
'@univerjs/icons':
|
||||
specifier: ^0.1.44
|
||||
version: 0.1.44(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
specifier: ^0.1.45
|
||||
version: 0.1.45(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
devDependencies:
|
||||
'@types/react':
|
||||
specifier: ^18.2.79
|
||||
@@ -1575,6 +1651,101 @@ importers:
|
||||
specifier: ^1.6.0
|
||||
version: 1.6.0(@types/node@20.12.12)(happy-dom@13.3.8)(jsdom@24.0.0)(less@4.2.0)(sass@1.72.0)(terser@5.30.0)
|
||||
|
||||
packages/thread-comment:
|
||||
dependencies:
|
||||
'@univerjs/protocol':
|
||||
specifier: ^0.1.20
|
||||
version: 0.1.23(@grpc/grpc-js@1.10.4)(rxjs@7.8.1)
|
||||
devDependencies:
|
||||
'@univerjs/core':
|
||||
specifier: workspace:*
|
||||
version: link:../core
|
||||
'@univerjs/design':
|
||||
specifier: workspace:*
|
||||
version: link:../design
|
||||
'@univerjs/shared':
|
||||
specifier: workspace:*
|
||||
version: link:../../common/shared
|
||||
'@univerjs/ui':
|
||||
specifier: workspace:*
|
||||
version: link:../ui
|
||||
'@wendellhu/redi':
|
||||
specifier: ^0.15.1
|
||||
version: 0.15.1
|
||||
clsx:
|
||||
specifier: ^2.1.0
|
||||
version: 2.1.1
|
||||
less:
|
||||
specifier: ^4.2.0
|
||||
version: 4.2.0
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.2.0
|
||||
rxjs:
|
||||
specifier: ^7.8.1
|
||||
version: 7.8.1
|
||||
typescript:
|
||||
specifier: ^5.3.3
|
||||
version: 5.4.5
|
||||
vite:
|
||||
specifier: ^5.1.4
|
||||
version: 5.2.10(@types/node@20.12.12)(less@4.2.0)(sass@1.72.0)(terser@5.30.0)
|
||||
vitest:
|
||||
specifier: ^1.3.1
|
||||
version: 1.5.2(@types/node@20.12.12)(happy-dom@13.3.8)(jsdom@24.0.0)(less@4.2.0)(sass@1.72.0)(terser@5.30.0)
|
||||
|
||||
packages/thread-comment-ui:
|
||||
dependencies:
|
||||
'@univerjs/icons':
|
||||
specifier: ^0.1.45
|
||||
version: 0.1.45(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
'@univerjs/protocol':
|
||||
specifier: ^0.1.20
|
||||
version: 0.1.23(@grpc/grpc-js@1.10.4)(rxjs@7.8.1)
|
||||
'@univerjs/thread-comment':
|
||||
specifier: workspace:*
|
||||
version: link:../thread-comment
|
||||
devDependencies:
|
||||
'@univerjs/core':
|
||||
specifier: workspace:*
|
||||
version: link:../core
|
||||
'@univerjs/design':
|
||||
specifier: workspace:*
|
||||
version: link:../design
|
||||
'@univerjs/shared':
|
||||
specifier: workspace:*
|
||||
version: link:../../common/shared
|
||||
'@univerjs/ui':
|
||||
specifier: workspace:*
|
||||
version: link:../ui
|
||||
'@wendellhu/redi':
|
||||
specifier: ^0.15.1
|
||||
version: 0.15.1
|
||||
clsx:
|
||||
specifier: ^2.1.1
|
||||
version: 2.1.1
|
||||
dayjs:
|
||||
specifier: ^1.11.10
|
||||
version: 1.11.10
|
||||
less:
|
||||
specifier: ^4.2.0
|
||||
version: 4.2.0
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.2.0
|
||||
rxjs:
|
||||
specifier: ^7.8.1
|
||||
version: 7.8.1
|
||||
typescript:
|
||||
specifier: ^5.4.5
|
||||
version: 5.4.5
|
||||
vite:
|
||||
specifier: ^5.2.10
|
||||
version: 5.2.10(@types/node@20.12.12)(less@4.2.0)(sass@1.72.0)(terser@5.30.0)
|
||||
vitest:
|
||||
specifier: ^1.5.0
|
||||
version: 1.5.2(@types/node@20.12.12)(happy-dom@13.3.8)(jsdom@24.0.0)(less@4.2.0)(sass@1.72.0)(terser@5.30.0)
|
||||
|
||||
packages/ui:
|
||||
dependencies:
|
||||
'@univerjs/icons':
|
||||
@@ -2561,6 +2732,9 @@ packages:
|
||||
resolution: {integrity: sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@babel/runtime@7.4.5':
|
||||
resolution: {integrity: sha512-TuI4qpWZP6lGOGIuGWtp9sPluqYICmbk8T/1vpSysqJxRPkudh/ofFWyqdcMsDf2s7KvDL4/YHgKyvcS3g9CJQ==}
|
||||
|
||||
'@babel/template@7.24.0':
|
||||
resolution: {integrity: sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
@@ -4259,6 +4433,9 @@ packages:
|
||||
'@types/react-grid-layout@1.3.5':
|
||||
resolution: {integrity: sha512-WH/po1gcEcoR6y857yAnPGug+ZhkF4PaTUxgAbwfeSH/QOgVSakKHBXoPGad/sEznmkiaK3pqHk+etdWisoeBQ==}
|
||||
|
||||
'@types/react-mentions@4.1.13':
|
||||
resolution: {integrity: sha512-kRulAAjlmhCtsJ9bapO0foocknaE/rEuFKpmFEU81fBfnXZmZNBaJ9J/DBjwigT3WDHjQVUmYoi5sxEXrcdzAw==}
|
||||
|
||||
'@types/react-transition-group@4.4.10':
|
||||
resolution: {integrity: sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==}
|
||||
|
||||
@@ -4393,18 +4570,19 @@ packages:
|
||||
'@univerjs/icons-svg@0.1.45':
|
||||
resolution: {integrity: sha512-v1oETviQ6RI9whV0HyUGlnQLEHyatZssaMJBY/ixIfBEZn4lZeMjPYszWM+Ymm2YilhHWA6+oCj4f5isT0ovIg==}
|
||||
|
||||
'@univerjs/icons@0.1.44':
|
||||
resolution: {integrity: sha512-emPPhk9aNGIl/wF/z+9KLAq5EG/zmUoSwj2UaHHssnM/mv96Ieob4MpkhTfrVtvtiAUI2zZpUXrbsRX3PShrXg==}
|
||||
peerDependencies:
|
||||
react: '*'
|
||||
react-dom: '*'
|
||||
|
||||
'@univerjs/icons@0.1.45':
|
||||
resolution: {integrity: sha512-Yw7YDDs2lvcn9i5uyaaZmmQh0Z6N523RGhymduvHauckX5ool3RLc+tFMgkKxHfEX90qEYSbfRXNHAG+Ftd29w==}
|
||||
peerDependencies:
|
||||
react: '*'
|
||||
react-dom: '*'
|
||||
|
||||
'@univerjs/protocol@0.1.23':
|
||||
resolution: {integrity: sha512-xOilOZjr+qA3RtKsgcVhRXp5Bo45JryPutrcVNeM06PoGmljWBfbQZhHZ0HjHxN1iYZNl9AO4LPuouLEeB4IZQ==}
|
||||
engines: {node: '>=16.0.0', npm: '>=8.0.0'}
|
||||
peerDependencies:
|
||||
'@grpc/grpc-js': ^1.9.14
|
||||
rxjs: '>=7.0.0'
|
||||
|
||||
'@univerjs/protocol@0.1.29':
|
||||
resolution: {integrity: sha512-B3/OmxK54sME2qFvsih6uH7NMwTpjOcIFsalZcDA9y7ZisKzZDQpqNoc58HQO2+cARVO/WXKoCxpWiG7ziAg1w==}
|
||||
engines: {node: '>=16.0.0', npm: '>=8.0.0'}
|
||||
@@ -4433,24 +4611,39 @@ packages:
|
||||
'@vitest/expect@1.3.1':
|
||||
resolution: {integrity: sha512-xofQFwIzfdmLLlHa6ag0dPV8YsnKOCP1KdAeVVh34vSjN2dcUiXYCD9htu/9eM7t8Xln4v03U9HLxLpPlsXdZw==}
|
||||
|
||||
'@vitest/expect@1.5.2':
|
||||
resolution: {integrity: sha512-rf7MTD1WCoDlN3FfYJ9Llfp0PbdtOMZ3FIF0AVkDnKbp3oiMW1c8AmvRZBcqbAhDUAvF52e9zx4WQM1r3oraVA==}
|
||||
|
||||
'@vitest/expect@1.6.0':
|
||||
resolution: {integrity: sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==}
|
||||
|
||||
'@vitest/runner@1.5.2':
|
||||
resolution: {integrity: sha512-7IJ7sJhMZrqx7HIEpv3WrMYcq8ZNz9L6alo81Y6f8hV5mIE6yVZsFoivLZmr0D777klm1ReqonE9LyChdcmw6g==}
|
||||
|
||||
'@vitest/runner@1.6.0':
|
||||
resolution: {integrity: sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==}
|
||||
|
||||
'@vitest/snapshot@1.5.2':
|
||||
resolution: {integrity: sha512-CTEp/lTYos8fuCc9+Z55Ga5NVPKUgExritjF5VY7heRFUfheoAqBneUlvXSUJHUZPjnPmyZA96yLRJDP1QATFQ==}
|
||||
|
||||
'@vitest/snapshot@1.6.0':
|
||||
resolution: {integrity: sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==}
|
||||
|
||||
'@vitest/spy@1.3.1':
|
||||
resolution: {integrity: sha512-xAcW+S099ylC9VLU7eZfdT9myV67Nor9w9zhf0mGCYJSO+zM2839tOeROTdikOi/8Qeusffvxb/MyBSOja1Uig==}
|
||||
|
||||
'@vitest/spy@1.5.2':
|
||||
resolution: {integrity: sha512-xCcPvI8JpCtgikT9nLpHPL1/81AYqZy1GCy4+MCHBE7xi8jgsYkULpW5hrx5PGLgOQjUpb6fd15lqcriJ40tfQ==}
|
||||
|
||||
'@vitest/spy@1.6.0':
|
||||
resolution: {integrity: sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==}
|
||||
|
||||
'@vitest/utils@1.3.1':
|
||||
resolution: {integrity: sha512-d3Waie/299qqRyHTm2DjADeTaNdNSVsnwHPWrs20JMpjh6eiVq7ggggweO8rc4arhf6rRkWuHKwvxGvejUXZZQ==}
|
||||
|
||||
'@vitest/utils@1.5.2':
|
||||
resolution: {integrity: sha512-sWOmyofuXLJ85VvXNsroZur7mOJGiQeM0JN3/0D1uU8U9bGFM69X1iqHaRXl6R8BwaLY6yPCogP257zxTzkUdA==}
|
||||
|
||||
'@vitest/utils@1.6.0':
|
||||
resolution: {integrity: sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==}
|
||||
|
||||
@@ -5049,8 +5242,8 @@ packages:
|
||||
resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
chromatic@11.3.2:
|
||||
resolution: {integrity: sha512-0PuHl49VvBMoDHEfmNjC/bim9YYNhWF3axTZlFuatC0avwr2Xw4GDqJDG9fArEWN8oM8VtYHkE9D7qc87dmz2w==}
|
||||
chromatic@11.3.3:
|
||||
resolution: {integrity: sha512-XZMa/9HJpTZLnBh5yGolbiRVagtSr3gK815rFicSuQpXr82ppOsqstrOoeu0IBpgtt/F6YvN3/JqHe6k00nXGA==}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@chromatic-com/cypress': ^0.*.* || ^1.0.0
|
||||
@@ -5447,6 +5640,9 @@ packages:
|
||||
resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
dayjs@1.11.10:
|
||||
resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==}
|
||||
|
||||
dayjs@1.11.11:
|
||||
resolution: {integrity: sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==}
|
||||
|
||||
@@ -8564,6 +8760,12 @@ packages:
|
||||
react-is@18.2.0:
|
||||
resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==}
|
||||
|
||||
react-mentions@4.4.10:
|
||||
resolution: {integrity: sha512-JHiQlgF1oSZR7VYPjq32wy97z1w1oE4x10EuhKjPr4WUKhVzG1uFQhQjKqjQkbVqJrmahf+ldgBTv36NrkpKpA==}
|
||||
peerDependencies:
|
||||
react: '>=16.8.3'
|
||||
react-dom: '>=16.8.3'
|
||||
|
||||
react-mosaic-component@6.1.0:
|
||||
resolution: {integrity: sha512-iWrNUSdW6HK9SB6kaj7/auvIGZWlyEFR8ulQKC9lskY047uluo5ur4fiuZTNroUTZvGqL02AiLzBBj1+et8RZA==}
|
||||
peerDependencies:
|
||||
@@ -8678,6 +8880,9 @@ packages:
|
||||
regenerate@1.4.2:
|
||||
resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==}
|
||||
|
||||
regenerator-runtime@0.13.11:
|
||||
resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
|
||||
|
||||
regenerator-runtime@0.14.1:
|
||||
resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
|
||||
|
||||
@@ -9194,6 +9399,11 @@ packages:
|
||||
peerDependencies:
|
||||
webpack: ^5.0.0
|
||||
|
||||
substyle@9.4.1:
|
||||
resolution: {integrity: sha512-VOngeq/W1/UkxiGzeqVvDbGDPM8XgUyJVWjrqeh+GgKqspEPiLYndK+XRcsKUHM5Muz/++1ctJ1QCF/OqRiKWA==}
|
||||
peerDependencies:
|
||||
react: '>=16.8.3'
|
||||
|
||||
supports-color@5.5.0:
|
||||
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
|
||||
engines: {node: '>=4'}
|
||||
@@ -9676,6 +9886,11 @@ packages:
|
||||
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
vite-node@1.5.2:
|
||||
resolution: {integrity: sha512-Y8p91kz9zU+bWtF7HGt6DVw2JbhyuB2RlZix3FPYAYmUyZ3n7iTp8eSyLyY6sxtPegvxQtmlTMhfPhUfCUF93A==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
hasBin: true
|
||||
|
||||
vite-node@1.6.0:
|
||||
resolution: {integrity: sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
@@ -9691,6 +9906,34 @@ packages:
|
||||
vite:
|
||||
optional: true
|
||||
|
||||
vite@5.2.10:
|
||||
resolution: {integrity: sha512-PAzgUZbP7msvQvqdSD+ErD5qGnSFiGOoWmV5yAKUEI0kdhjbH6nMWVyZQC/hSc4aXwc0oJ9aEdIiF9Oje0JFCw==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@types/node': ^18.0.0 || >=20.0.0
|
||||
less: '*'
|
||||
lightningcss: ^1.21.0
|
||||
sass: '*'
|
||||
stylus: '*'
|
||||
sugarss: '*'
|
||||
terser: ^5.4.0
|
||||
peerDependenciesMeta:
|
||||
'@types/node':
|
||||
optional: true
|
||||
less:
|
||||
optional: true
|
||||
lightningcss:
|
||||
optional: true
|
||||
sass:
|
||||
optional: true
|
||||
stylus:
|
||||
optional: true
|
||||
sugarss:
|
||||
optional: true
|
||||
terser:
|
||||
optional: true
|
||||
|
||||
vite@5.2.11:
|
||||
resolution: {integrity: sha512-HndV31LWW05i1BLPMUCE1B9E9GFbOu1MbenhS58FuK6owSO5qHm7GiCotrNY1YE5rMeQSFBGmT5ZaLEjFizgiQ==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
@@ -9724,6 +9967,31 @@ packages:
|
||||
peerDependencies:
|
||||
vitest: '*'
|
||||
|
||||
vitest@1.5.2:
|
||||
resolution: {integrity: sha512-l9gwIkq16ug3xY7BxHwcBQovLZG75zZL0PlsiYQbf76Rz6QGs54416UWMtC0jXeihvHvcHrf2ROEjkQRVpoZYw==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@edge-runtime/vm': '*'
|
||||
'@types/node': ^18.0.0 || >=20.0.0
|
||||
'@vitest/browser': 1.5.2
|
||||
'@vitest/ui': 1.5.2
|
||||
happy-dom: '*'
|
||||
jsdom: '*'
|
||||
peerDependenciesMeta:
|
||||
'@edge-runtime/vm':
|
||||
optional: true
|
||||
'@types/node':
|
||||
optional: true
|
||||
'@vitest/browser':
|
||||
optional: true
|
||||
'@vitest/ui':
|
||||
optional: true
|
||||
happy-dom:
|
||||
optional: true
|
||||
jsdom:
|
||||
optional: true
|
||||
|
||||
vitest@1.6.0:
|
||||
resolution: {integrity: sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
@@ -10144,11 +10412,11 @@ snapshots:
|
||||
|
||||
'@babel/helper-annotate-as-pure@7.22.5':
|
||||
dependencies:
|
||||
'@babel/types': 7.24.5
|
||||
'@babel/types': 7.24.0
|
||||
|
||||
'@babel/helper-builder-binary-assignment-operator-visitor@7.22.15':
|
||||
dependencies:
|
||||
'@babel/types': 7.24.5
|
||||
'@babel/types': 7.24.0
|
||||
|
||||
'@babel/helper-compilation-targets@7.23.6':
|
||||
dependencies:
|
||||
@@ -10168,7 +10436,7 @@ snapshots:
|
||||
'@babel/helper-optimise-call-expression': 7.22.5
|
||||
'@babel/helper-replace-supers': 7.24.1(@babel/core@7.24.5)
|
||||
'@babel/helper-skip-transparent-expression-wrappers': 7.22.5
|
||||
'@babel/helper-split-export-declaration': 7.24.5
|
||||
'@babel/helper-split-export-declaration': 7.22.6
|
||||
semver: 6.3.1
|
||||
|
||||
'@babel/helper-create-class-features-plugin@7.24.5(@babel/core@7.24.5)':
|
||||
@@ -10215,7 +10483,7 @@ snapshots:
|
||||
|
||||
'@babel/helper-member-expression-to-functions@7.23.0':
|
||||
dependencies:
|
||||
'@babel/types': 7.24.5
|
||||
'@babel/types': 7.24.0
|
||||
|
||||
'@babel/helper-member-expression-to-functions@7.24.5':
|
||||
dependencies:
|
||||
@@ -10234,6 +10502,15 @@ snapshots:
|
||||
'@babel/helper-split-export-declaration': 7.22.6
|
||||
'@babel/helper-validator-identifier': 7.22.20
|
||||
|
||||
'@babel/helper-module-transforms@7.23.3(@babel/core@7.24.5)':
|
||||
dependencies:
|
||||
'@babel/core': 7.24.5
|
||||
'@babel/helper-environment-visitor': 7.22.20
|
||||
'@babel/helper-module-imports': 7.24.3
|
||||
'@babel/helper-simple-access': 7.22.5
|
||||
'@babel/helper-split-export-declaration': 7.22.6
|
||||
'@babel/helper-validator-identifier': 7.22.20
|
||||
|
||||
'@babel/helper-module-transforms@7.24.5(@babel/core@7.24.5)':
|
||||
dependencies:
|
||||
'@babel/core': 7.24.5
|
||||
@@ -10245,7 +10522,7 @@ snapshots:
|
||||
|
||||
'@babel/helper-optimise-call-expression@7.22.5':
|
||||
dependencies:
|
||||
'@babel/types': 7.24.5
|
||||
'@babel/types': 7.24.0
|
||||
|
||||
'@babel/helper-plugin-utils@7.24.0': {}
|
||||
|
||||
@@ -10275,7 +10552,7 @@ snapshots:
|
||||
|
||||
'@babel/helper-skip-transparent-expression-wrappers@7.22.5':
|
||||
dependencies:
|
||||
'@babel/types': 7.24.5
|
||||
'@babel/types': 7.24.0
|
||||
|
||||
'@babel/helper-split-export-declaration@7.22.6':
|
||||
dependencies:
|
||||
@@ -10297,7 +10574,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@babel/helper-function-name': 7.23.0
|
||||
'@babel/template': 7.24.0
|
||||
'@babel/types': 7.24.5
|
||||
'@babel/types': 7.24.0
|
||||
|
||||
'@babel/helpers@7.24.1':
|
||||
dependencies:
|
||||
@@ -10603,28 +10880,28 @@ snapshots:
|
||||
'@babel/plugin-transform-modules-amd@7.24.1(@babel/core@7.24.5)':
|
||||
dependencies:
|
||||
'@babel/core': 7.24.5
|
||||
'@babel/helper-module-transforms': 7.24.5(@babel/core@7.24.5)
|
||||
'@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.5)
|
||||
'@babel/helper-plugin-utils': 7.24.5
|
||||
|
||||
'@babel/plugin-transform-modules-commonjs@7.24.1(@babel/core@7.24.5)':
|
||||
dependencies:
|
||||
'@babel/core': 7.24.5
|
||||
'@babel/helper-module-transforms': 7.24.5(@babel/core@7.24.5)
|
||||
'@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.5)
|
||||
'@babel/helper-plugin-utils': 7.24.0
|
||||
'@babel/helper-simple-access': 7.24.5
|
||||
'@babel/helper-simple-access': 7.22.5
|
||||
|
||||
'@babel/plugin-transform-modules-systemjs@7.24.1(@babel/core@7.24.5)':
|
||||
dependencies:
|
||||
'@babel/core': 7.24.5
|
||||
'@babel/helper-hoist-variables': 7.22.5
|
||||
'@babel/helper-module-transforms': 7.24.5(@babel/core@7.24.5)
|
||||
'@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.5)
|
||||
'@babel/helper-plugin-utils': 7.24.5
|
||||
'@babel/helper-validator-identifier': 7.24.5
|
||||
'@babel/helper-validator-identifier': 7.22.20
|
||||
|
||||
'@babel/plugin-transform-modules-umd@7.24.1(@babel/core@7.24.5)':
|
||||
dependencies:
|
||||
'@babel/core': 7.24.5
|
||||
'@babel/helper-module-transforms': 7.24.5(@babel/core@7.24.5)
|
||||
'@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.5)
|
||||
'@babel/helper-plugin-utils': 7.24.5
|
||||
|
||||
'@babel/plugin-transform-named-capturing-groups-regex@7.22.5(@babel/core@7.24.5)':
|
||||
@@ -10884,7 +11161,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@babel/core': 7.24.5
|
||||
'@babel/helper-plugin-utils': 7.24.5
|
||||
'@babel/types': 7.24.5
|
||||
'@babel/types': 7.24.0
|
||||
esutils: 2.0.3
|
||||
|
||||
'@babel/preset-typescript@7.24.1(@babel/core@7.24.5)':
|
||||
@@ -10911,6 +11188,10 @@ snapshots:
|
||||
dependencies:
|
||||
regenerator-runtime: 0.14.1
|
||||
|
||||
'@babel/runtime@7.4.5':
|
||||
dependencies:
|
||||
regenerator-runtime: 0.13.11
|
||||
|
||||
'@babel/template@7.24.0':
|
||||
dependencies:
|
||||
'@babel/code-frame': 7.24.2
|
||||
@@ -10963,7 +11244,7 @@ snapshots:
|
||||
|
||||
'@chromatic-com/storybook@1.4.0(react@18.2.0)':
|
||||
dependencies:
|
||||
chromatic: 11.3.2
|
||||
chromatic: 11.3.3
|
||||
filesize: 10.1.1
|
||||
jsonfile: 6.1.0
|
||||
react-confetti: 6.1.0(react@18.2.0)
|
||||
@@ -11121,7 +11402,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/eslint': 8.56.10
|
||||
'@types/estree': 1.0.5
|
||||
'@typescript-eslint/types': 7.9.0
|
||||
'@typescript-eslint/types': 7.7.1
|
||||
comment-parser: 1.4.1
|
||||
esquery: 1.5.0
|
||||
jsdoc-type-pratt-parser: 4.0.0
|
||||
@@ -12267,7 +12548,7 @@ snapshots:
|
||||
fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.4.5)(webpack@5.91.0(@swc/core@1.4.11)(esbuild@0.20.2))
|
||||
fs-extra: 11.2.0
|
||||
html-webpack-plugin: 5.6.0(webpack@5.91.0(@swc/core@1.4.11)(esbuild@0.20.2))
|
||||
magic-string: 0.30.10
|
||||
magic-string: 0.30.8
|
||||
path-browserify: 1.0.1
|
||||
process: 0.11.10
|
||||
semver: 7.6.0
|
||||
@@ -12304,7 +12585,7 @@ snapshots:
|
||||
'@storybook/cli@8.1.1(@babel/preset-env@7.24.5(@babel/core@7.24.5))(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
|
||||
dependencies:
|
||||
'@babel/core': 7.24.5
|
||||
'@babel/types': 7.24.5
|
||||
'@babel/types': 7.24.0
|
||||
'@ndelangen/get-tarball': 3.0.9
|
||||
'@storybook/codemod': 8.1.1
|
||||
'@storybook/core-common': 8.1.1(prettier@3.2.5)
|
||||
@@ -12356,7 +12637,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@babel/core': 7.24.5
|
||||
'@babel/preset-env': 7.24.5(@babel/core@7.24.5)
|
||||
'@babel/types': 7.24.5
|
||||
'@babel/types': 7.24.0
|
||||
'@storybook/csf': 0.1.7
|
||||
'@storybook/csf-tools': 8.1.1
|
||||
'@storybook/node-logger': 8.1.1
|
||||
@@ -12513,7 +12794,7 @@ snapshots:
|
||||
'@babel/generator': 7.24.5
|
||||
'@babel/parser': 7.24.5
|
||||
'@babel/traverse': 7.24.1
|
||||
'@babel/types': 7.24.5
|
||||
'@babel/types': 7.24.0
|
||||
'@storybook/csf': 0.1.7
|
||||
'@storybook/types': 8.1.1
|
||||
fs-extra: 11.2.0
|
||||
@@ -12557,7 +12838,7 @@ snapshots:
|
||||
'@storybook/core-events': 8.1.1
|
||||
'@storybook/global': 5.0.0
|
||||
'@storybook/preview-api': 8.1.1
|
||||
'@vitest/utils': 1.6.0
|
||||
'@vitest/utils': 1.5.2
|
||||
util: 0.12.5
|
||||
|
||||
'@storybook/manager-api@8.1.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
|
||||
@@ -12598,7 +12879,7 @@ snapshots:
|
||||
'@types/semver': 7.5.8
|
||||
find-up: 5.0.0
|
||||
fs-extra: 11.2.0
|
||||
magic-string: 0.30.10
|
||||
magic-string: 0.30.8
|
||||
react: 18.2.0
|
||||
react-docgen: 7.0.3
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
@@ -12739,7 +13020,7 @@ snapshots:
|
||||
'@testing-library/jest-dom': 6.4.2(vitest@1.6.0(@types/node@20.12.12)(happy-dom@13.3.8)(jsdom@24.0.0)(less@4.2.0)(sass@1.72.0)(terser@5.30.0))
|
||||
'@testing-library/user-event': 14.5.2(@testing-library/dom@9.3.4)
|
||||
'@vitest/expect': 1.3.1
|
||||
'@vitest/spy': 1.6.0
|
||||
'@vitest/spy': 1.5.2
|
||||
util: 0.12.5
|
||||
transitivePeerDependencies:
|
||||
- '@jest/globals'
|
||||
@@ -13070,6 +13351,10 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/react': 18.3.1
|
||||
|
||||
'@types/react-mentions@4.1.13':
|
||||
dependencies:
|
||||
'@types/react': 18.3.1
|
||||
|
||||
'@types/react-transition-group@4.4.10':
|
||||
dependencies:
|
||||
'@types/react': 18.3.1
|
||||
@@ -13243,16 +13528,16 @@ snapshots:
|
||||
|
||||
'@univerjs/icons-svg@0.1.45': {}
|
||||
|
||||
'@univerjs/icons@0.1.44(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
|
||||
'@univerjs/icons@0.1.45(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
|
||||
'@univerjs/protocol@0.1.23(@grpc/grpc-js@1.10.4)(rxjs@7.8.1)':
|
||||
dependencies:
|
||||
'@grpc/grpc-js': 1.10.4
|
||||
rxjs: 7.8.1
|
||||
|
||||
'@univerjs/protocol@0.1.29(@grpc/grpc-js@1.10.4)(rxjs@7.8.1)':
|
||||
dependencies:
|
||||
'@grpc/grpc-js': 1.10.4
|
||||
@@ -13295,18 +13580,36 @@ snapshots:
|
||||
'@vitest/utils': 1.3.1
|
||||
chai: 4.4.1
|
||||
|
||||
'@vitest/expect@1.5.2':
|
||||
dependencies:
|
||||
'@vitest/spy': 1.5.2
|
||||
'@vitest/utils': 1.5.2
|
||||
chai: 4.4.1
|
||||
|
||||
'@vitest/expect@1.6.0':
|
||||
dependencies:
|
||||
'@vitest/spy': 1.6.0
|
||||
'@vitest/utils': 1.6.0
|
||||
chai: 4.4.1
|
||||
|
||||
'@vitest/runner@1.5.2':
|
||||
dependencies:
|
||||
'@vitest/utils': 1.5.2
|
||||
p-limit: 5.0.0
|
||||
pathe: 1.1.2
|
||||
|
||||
'@vitest/runner@1.6.0':
|
||||
dependencies:
|
||||
'@vitest/utils': 1.6.0
|
||||
p-limit: 5.0.0
|
||||
pathe: 1.1.2
|
||||
|
||||
'@vitest/snapshot@1.5.2':
|
||||
dependencies:
|
||||
magic-string: 0.30.8
|
||||
pathe: 1.1.2
|
||||
pretty-format: 29.7.0
|
||||
|
||||
'@vitest/snapshot@1.6.0':
|
||||
dependencies:
|
||||
magic-string: 0.30.8
|
||||
@@ -13317,6 +13620,10 @@ snapshots:
|
||||
dependencies:
|
||||
tinyspy: 2.2.1
|
||||
|
||||
'@vitest/spy@1.5.2':
|
||||
dependencies:
|
||||
tinyspy: 2.2.1
|
||||
|
||||
'@vitest/spy@1.6.0':
|
||||
dependencies:
|
||||
tinyspy: 2.2.1
|
||||
@@ -13328,6 +13635,13 @@ snapshots:
|
||||
loupe: 2.3.7
|
||||
pretty-format: 29.7.0
|
||||
|
||||
'@vitest/utils@1.5.2':
|
||||
dependencies:
|
||||
diff-sequences: 29.6.3
|
||||
estree-walker: 3.0.3
|
||||
loupe: 2.3.7
|
||||
pretty-format: 29.7.0
|
||||
|
||||
'@vitest/utils@1.6.0':
|
||||
dependencies:
|
||||
diff-sequences: 29.6.3
|
||||
@@ -13355,7 +13669,6 @@ snapshots:
|
||||
entities: 4.5.0
|
||||
estree-walker: 2.0.2
|
||||
source-map-js: 1.2.0
|
||||
optional: true
|
||||
|
||||
'@vue/compiler-core@3.4.27':
|
||||
dependencies:
|
||||
@@ -13369,7 +13682,6 @@ snapshots:
|
||||
dependencies:
|
||||
'@vue/compiler-core': 3.4.21
|
||||
'@vue/shared': 3.4.21
|
||||
optional: true
|
||||
|
||||
'@vue/compiler-dom@3.4.27':
|
||||
dependencies:
|
||||
@@ -13416,8 +13728,8 @@ snapshots:
|
||||
dependencies:
|
||||
'@volar/language-core': 1.11.1
|
||||
'@volar/source-map': 1.11.1
|
||||
'@vue/compiler-dom': 3.4.27
|
||||
'@vue/shared': 3.4.27
|
||||
'@vue/compiler-dom': 3.4.21
|
||||
'@vue/shared': 3.4.21
|
||||
computeds: 0.0.1
|
||||
minimatch: 9.0.4
|
||||
muggle-string: 0.3.1
|
||||
@@ -13472,8 +13784,7 @@ snapshots:
|
||||
'@vue/shared': 3.4.27
|
||||
vue: 3.4.27(typescript@5.4.5)
|
||||
|
||||
'@vue/shared@3.4.21':
|
||||
optional: true
|
||||
'@vue/shared@3.4.21': {}
|
||||
|
||||
'@vue/shared@3.4.27': {}
|
||||
|
||||
@@ -14073,7 +14384,7 @@ snapshots:
|
||||
|
||||
chownr@2.0.0: {}
|
||||
|
||||
chromatic@11.3.2: {}
|
||||
chromatic@11.3.3: {}
|
||||
|
||||
chrome-trace-event@1.0.3: {}
|
||||
|
||||
@@ -14461,6 +14772,8 @@ snapshots:
|
||||
es-errors: 1.3.0
|
||||
is-data-view: 1.0.1
|
||||
|
||||
dayjs@1.11.10: {}
|
||||
|
||||
dayjs@1.11.11: {}
|
||||
|
||||
de-indent@1.0.2: {}
|
||||
@@ -16421,7 +16734,7 @@ snapshots:
|
||||
istanbul-lib-instrument@6.0.2:
|
||||
dependencies:
|
||||
'@babel/core': 7.24.3
|
||||
'@babel/parser': 7.24.5
|
||||
'@babel/parser': 7.24.1
|
||||
'@istanbuljs/schema': 0.1.3
|
||||
istanbul-lib-coverage: 3.2.2
|
||||
semver: 7.6.0
|
||||
@@ -16531,7 +16844,7 @@ snapshots:
|
||||
jscodeshift@0.15.2(@babel/preset-env@7.24.5(@babel/core@7.24.5)):
|
||||
dependencies:
|
||||
'@babel/core': 7.24.5
|
||||
'@babel/parser': 7.24.5
|
||||
'@babel/parser': 7.24.1
|
||||
'@babel/plugin-transform-class-properties': 7.24.1(@babel/core@7.24.5)
|
||||
'@babel/plugin-transform-modules-commonjs': 7.24.1(@babel/core@7.24.5)
|
||||
'@babel/plugin-transform-nullish-coalescing-operator': 7.24.1(@babel/core@7.24.5)
|
||||
@@ -16855,8 +17168,8 @@ snapshots:
|
||||
|
||||
magicast@0.3.3:
|
||||
dependencies:
|
||||
'@babel/parser': 7.24.5
|
||||
'@babel/types': 7.24.5
|
||||
'@babel/parser': 7.24.1
|
||||
'@babel/types': 7.24.0
|
||||
source-map-js: 1.2.0
|
||||
|
||||
make-dir@2.1.0:
|
||||
@@ -17895,7 +18208,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@babel/core': 7.24.3
|
||||
'@babel/traverse': 7.24.1
|
||||
'@babel/types': 7.24.5
|
||||
'@babel/types': 7.24.0
|
||||
'@types/babel__core': 7.20.5
|
||||
'@types/babel__traverse': 7.20.5
|
||||
'@types/doctrine': 0.0.9
|
||||
@@ -17951,6 +18264,15 @@ snapshots:
|
||||
|
||||
react-is@18.2.0: {}
|
||||
|
||||
react-mentions@4.4.10(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.4.5
|
||||
invariant: 2.2.4
|
||||
prop-types: 15.8.1
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
substyle: 9.4.1(react@18.2.0)
|
||||
|
||||
react-mosaic-component@6.1.0(@types/node@20.12.12)(@types/react@18.3.1)(dnd-core@16.0.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
|
||||
dependencies:
|
||||
classnames: 2.5.1
|
||||
@@ -18111,6 +18433,8 @@ snapshots:
|
||||
|
||||
regenerate@1.4.2: {}
|
||||
|
||||
regenerator-runtime@0.13.11: {}
|
||||
|
||||
regenerator-runtime@0.14.1: {}
|
||||
|
||||
regenerator-transform@0.15.2:
|
||||
@@ -18755,6 +19079,12 @@ snapshots:
|
||||
dependencies:
|
||||
webpack: 5.91.0(@swc/core@1.4.11)(esbuild@0.20.2)
|
||||
|
||||
substyle@9.4.1(react@18.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.1
|
||||
invariant: 2.2.4
|
||||
react: 18.2.0
|
||||
|
||||
supports-color@5.5.0:
|
||||
dependencies:
|
||||
has-flag: 3.0.0
|
||||
@@ -19215,6 +19545,23 @@ snapshots:
|
||||
|
||||
vary@1.1.2: {}
|
||||
|
||||
vite-node@1.5.2(@types/node@20.12.12)(less@4.2.0)(sass@1.72.0)(terser@5.30.0):
|
||||
dependencies:
|
||||
cac: 6.7.14
|
||||
debug: 4.3.4
|
||||
pathe: 1.1.2
|
||||
picocolors: 1.0.0
|
||||
vite: 5.2.11(@types/node@20.12.12)(less@4.2.0)(sass@1.72.0)(terser@5.30.0)
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
- less
|
||||
- lightningcss
|
||||
- sass
|
||||
- stylus
|
||||
- sugarss
|
||||
- supports-color
|
||||
- terser
|
||||
|
||||
vite-node@1.6.0(@types/node@20.12.12)(less@4.2.0)(sass@1.72.0)(terser@5.30.0):
|
||||
dependencies:
|
||||
cac: 6.7.14
|
||||
@@ -19239,7 +19586,7 @@ snapshots:
|
||||
'@vue/language-core': 1.8.27(typescript@5.4.5)
|
||||
debug: 4.3.4
|
||||
kolorist: 1.8.0
|
||||
magic-string: 0.30.10
|
||||
magic-string: 0.30.8
|
||||
typescript: 5.4.5
|
||||
vue-tsc: 1.8.27(typescript@5.4.5)
|
||||
optionalDependencies:
|
||||
@@ -19249,6 +19596,18 @@ snapshots:
|
||||
- rollup
|
||||
- supports-color
|
||||
|
||||
vite@5.2.10(@types/node@20.12.12)(less@4.2.0)(sass@1.72.0)(terser@5.30.0):
|
||||
dependencies:
|
||||
esbuild: 0.20.2
|
||||
postcss: 8.4.38
|
||||
rollup: 4.13.2
|
||||
optionalDependencies:
|
||||
'@types/node': 20.12.12
|
||||
fsevents: 2.3.3
|
||||
less: 4.2.0
|
||||
sass: 1.72.0
|
||||
terser: 5.30.0
|
||||
|
||||
vite@5.2.11(@types/node@20.12.12)(less@4.2.0)(sass@1.72.0)(terser@5.30.0):
|
||||
dependencies:
|
||||
esbuild: 0.20.2
|
||||
@@ -19266,6 +19625,41 @@ snapshots:
|
||||
jest-canvas-mock: 2.5.2
|
||||
vitest: 1.6.0(@types/node@20.12.12)(happy-dom@13.3.8)(jsdom@24.0.0)(less@4.2.0)(sass@1.72.0)(terser@5.30.0)
|
||||
|
||||
vitest@1.5.2(@types/node@20.12.12)(happy-dom@13.3.8)(jsdom@24.0.0)(less@4.2.0)(sass@1.72.0)(terser@5.30.0):
|
||||
dependencies:
|
||||
'@vitest/expect': 1.5.2
|
||||
'@vitest/runner': 1.5.2
|
||||
'@vitest/snapshot': 1.5.2
|
||||
'@vitest/spy': 1.5.2
|
||||
'@vitest/utils': 1.5.2
|
||||
acorn-walk: 8.3.2
|
||||
chai: 4.4.1
|
||||
debug: 4.3.4
|
||||
execa: 8.0.1
|
||||
local-pkg: 0.5.0
|
||||
magic-string: 0.30.8
|
||||
pathe: 1.1.2
|
||||
picocolors: 1.0.0
|
||||
std-env: 3.7.0
|
||||
strip-literal: 2.1.0
|
||||
tinybench: 2.6.0
|
||||
tinypool: 0.8.3
|
||||
vite: 5.2.11(@types/node@20.12.12)(less@4.2.0)(sass@1.72.0)(terser@5.30.0)
|
||||
vite-node: 1.5.2(@types/node@20.12.12)(less@4.2.0)(sass@1.72.0)(terser@5.30.0)
|
||||
why-is-node-running: 2.2.2
|
||||
optionalDependencies:
|
||||
'@types/node': 20.12.12
|
||||
happy-dom: 13.3.8
|
||||
jsdom: 24.0.0
|
||||
transitivePeerDependencies:
|
||||
- less
|
||||
- lightningcss
|
||||
- sass
|
||||
- stylus
|
||||
- sugarss
|
||||
- supports-color
|
||||
- terser
|
||||
|
||||
vitest@1.6.0(@types/node@20.12.12)(happy-dom@13.3.8)(jsdom@24.0.0)(less@4.2.0)(sass@1.72.0)(terser@5.30.0):
|
||||
dependencies:
|
||||
'@vitest/expect': 1.6.0
|
||||
|
||||
Reference in New Issue
Block a user