feat(sheets-ui): support across unit formula (#5002)

This commit is contained in:
ZhangWei
2025-04-14 22:35:06 +08:00
committed by GitHub
parent 453d992900
commit 7f0fa9f7e5
26 changed files with 235 additions and 320 deletions
-2
View File
@@ -123,7 +123,6 @@ import { DocStateChangeManagerService } from './services/doc-state-change-manage
import { DocsRenderService } from './services/docs-render.service';
import { EditorService, IEditorService } from './services/editor/editor-manager.service';
import { DocFloatMenuService } from './services/float-menu.service';
import { IRangeSelectorService, RangeSelectorService } from './services/range-selector/range-selector.service';
import { DocSelectionRenderService } from './services/selection/doc-selection-render.service';
import { BreakLineShortcut, DeleteLeftShortcut, DeleteRightShortcut } from './shortcuts/core-editing.shortcut';
import {
@@ -322,7 +321,6 @@ export class UniverDocsUIPlugin extends Plugin {
[AppUIController],
[DocParagraphSettingController],
[IEditorService, { useClass: EditorService }],
[IRangeSelectorService, { useClass: RangeSelectorService }],
[IDocClipboardService, { useClass: DocClipboardService }],
[DocCanvasPopManagerService],
[DocsRenderService],
-1
View File
@@ -44,7 +44,6 @@ export { DocStateChangeManagerService } from './services/doc-state-change-manage
export { DocsRenderService } from './services/docs-render.service';
export { Editor } from './services/editor/editor';
export { EditorService, IEditorService } from './services/editor/editor-manager.service';
export { type IRangeSelectorRange, IRangeSelectorService, RangeSelectorService } from './services/range-selector/range-selector.service';
export { isInSameTableCell, isValidRectRange, NodePositionConvertToRectRange } from './services/selection/convert-rect-range';
export { NodePositionConvertToCursor } from './services/selection/convert-text-range';
@@ -1,83 +0,0 @@
/**
* Copyright 2023-present DreamNum Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { createIdentifier, Disposable } from '@univerjs/core';
import { BehaviorSubject, Subject } from 'rxjs';
import type { IDisposable, IUnitRange, Nullable } from '@univerjs/core';
import type { Observable } from 'rxjs';
export interface IRangeSelectorRange extends IUnitRange {
sheetName: string;
}
export interface IRangeSelectorService {
selectionChange$: Observable<IRangeSelectorRange[]>;
selectionChange(ranges: IRangeSelectorRange[]): void;
setCurrentSelectorId(id: Nullable<string>): void;
getCurrentSelectorId(): Nullable<string>;
openSelector$: Observable<unknown>;
openSelector(): void;
selectorModalVisible$: Observable<boolean>;
get selectorModalVisible(): boolean;
triggerModalVisibleChange(visible: boolean): void;
}
export class RangeSelectorService extends Disposable implements IRangeSelectorService, IDisposable {
private _currentSelectorId: Nullable<string>;
private readonly _selectionChange$ = new Subject<IRangeSelectorRange[]>();
readonly selectionChange$ = this._selectionChange$.asObservable();
private readonly _openSelector$ = new Subject();
readonly openSelector$ = this._openSelector$.asObservable();
private readonly _selectorModalVisible$ = new BehaviorSubject(false);
readonly selectorModalVisible$ = this._selectorModalVisible$.asObservable();
get selectorModalVisible() {
return this._selectorModalVisible$.getValue();
}
setCurrentSelectorId(id: Nullable<string>) {
this._currentSelectorId = id;
}
getCurrentSelectorId(): Nullable<string> {
return this._currentSelectorId;
}
selectionChange(range: IRangeSelectorRange[]) {
if (!this._currentSelectorId) {
return;
}
this._selectionChange$.next(range);
}
openSelector() {
this._openSelector$.next(null);
}
triggerModalVisibleChange(visible: boolean) {
this._selectorModalVisible$.next(visible);
}
}
export const IRangeSelectorService = createIdentifier<IRangeSelectorService>(
'univer.range-selector.service'
);
@@ -352,7 +352,7 @@ export function getCurrentTypeOfRenderer(
instanceService: IUniverInstanceService,
renderManageService: IRenderManagerService
): Nullable<IRender> {
const currentUnit = instanceService.getCurrentUnitForType(type);
const currentUnit = instanceService.getCurrentUnitOfType(type);
if (currentUnit == null) {
return null;
}
@@ -16,13 +16,14 @@
import type { Workbook } from '@univerjs/core';
import type { IColorScale, IConditionalFormattingRuleConfig } from '@univerjs/sheets-conditional-formatting';
import type { IFormulaEditorRef } from '@univerjs/sheets-formula-ui';
import type { IStyleEditorProps } from './type';
import { IUniverInstanceService, LocaleService, UniverInstanceType } from '@univerjs/core';
import { clsx, InputNumber, Select } from '@univerjs/design';
import { CFRuleType, CFValueType, createDefaultValueByValueType } from '@univerjs/sheets-conditional-formatting';
import { FormulaEditor } from '@univerjs/sheets-formula-ui';
import { useDependency, useSidebarClick } from '@univerjs/ui';
import React, { useEffect, useMemo, useRef, useState } from 'react';
import { useEffect, useMemo, useRef, useState } from 'react';
import { ColorPicker } from '../../color-picker';
import { Preview } from '../../preview';
import stylesBase from '../index.module.less';
@@ -38,6 +39,7 @@ const TextInput = (props: { id: string; type: CFValueType | 'none'; value: numbe
const formulaInitValue = useMemo(() => {
return String(value).startsWith('=') ? String(value) : '=';
}, [value]);
const config = useMemo(() => {
if ([CFValueType.max, CFValueType.min, 'none'].includes(type as CFValueType)) {
return { disabled: true };
@@ -54,12 +56,12 @@ const TextInput = (props: { id: string; type: CFValueType | 'none'; value: numbe
};
}, [type]);
const formulaEditorActionsRef = useRef<Parameters<typeof FormulaEditor>[0]['actions']>({});
const formulaEditorRef = useRef<IFormulaEditorRef>(null);
const [isFocusFormulaEditor, isFocusFormulaEditorSet] = useState(false);
useSidebarClick((e: MouseEvent) => {
const handleOutClick = formulaEditorActionsRef.current?.handleOutClick;
handleOutClick && handleOutClick(e, () => isFocusFormulaEditorSet(false));
const isOutSide = formulaEditorRef.current?.isClickOutSide(e);
isOutSide && isFocusFormulaEditorSet(false);
});
if (type === CFValueType.formula) {
@@ -75,7 +77,7 @@ const TextInput = (props: { id: string; type: CFValueType | 'none'; value: numbe
onChange(formula);
}}
onFocus={() => isFocusFormulaEditorSet(true)}
actions={formulaEditorActionsRef.current}
ref={formulaEditorRef}
/>
</div>
);
@@ -16,14 +16,15 @@
import type { Workbook } from '@univerjs/core';
import type { IConditionalFormattingRuleConfig, IValueConfig } from '@univerjs/sheets-conditional-formatting';
import type { IFormulaEditorRef } from '@univerjs/sheets-formula-ui';
import type { IStyleEditorProps } from './type';
import { IUniverInstanceService, LocaleService, UniverInstanceType } from '@univerjs/core';
import { Checkbox, InputNumber, Radio, RadioGroup, Select } from '@univerjs/design';
import { CFRuleType, CFValueType, createDefaultValueByValueType, defaultDataBarNativeColor, defaultDataBarPositiveColor } from '@univerjs/sheets-conditional-formatting';
import { CFRuleType, CFValueType, createDefaultValueByValueType, defaultDataBarNativeColor, defaultDataBarPositiveColor } from '@univerjs/sheets-conditional-formatting';
import { FormulaEditor } from '@univerjs/sheets-formula-ui';
import { useDependency, useSidebarClick } from '@univerjs/ui';
import React, { useEffect, useMemo, useRef, useState } from 'react';
import { useEffect, useMemo, useRef, useState } from 'react';
import { ColorPicker } from '../../color-picker';
import { Preview } from '../../preview';
import stylesBase from '../index.module.less';
@@ -37,12 +38,12 @@ const InputText = (props: { disabled?: boolean; id: string; className: string; t
const unitId = univerInstanceService.getCurrentUnitForType<Workbook>(UniverInstanceType.UNIVER_SHEET)!.getUnitId();
const subUnitId = univerInstanceService.getCurrentUnitForType<Workbook>(UniverInstanceType.UNIVER_SHEET)!.getActiveSheet()?.getSheetId();
const formulaEditorActionsRef = useRef<Parameters<typeof FormulaEditor>[0]['actions']>({});
const formulaEditorRef = useRef<IFormulaEditorRef>(null);
const [isFocusFormulaEditor, isFocusFormulaEditorSet] = useState(false);
useSidebarClick((e: MouseEvent) => {
const handleOutClick = formulaEditorActionsRef.current?.handleOutClick;
handleOutClick && handleOutClick(e, () => isFocusFormulaEditorSet(false));
const isOutSide = formulaEditorRef.current?.isClickOutSide(e);
isOutSide && isFocusFormulaEditorSet(false);
});
const _value = useRef(value);
@@ -72,7 +73,7 @@ const InputText = (props: { disabled?: boolean; id: string; className: string; t
onChange(formula);
}}
onFocus={() => isFocusFormulaEditorSet(true)}
actions={formulaEditorActionsRef.current}
ref={formulaEditorRef}
/>
</div>
);
@@ -16,12 +16,13 @@
import type { Workbook } from '@univerjs/core';
import type { IAverageHighlightCell, IConditionalFormattingRuleConfig, IHighlightCell, IRankHighlightCell } from '@univerjs/sheets-conditional-formatting';
import type { IFormulaEditorRef } from '@univerjs/sheets-formula-ui';
import type { IStyleEditorProps } from './type';
import { IUniverInstanceService, LocaleService, UniverInstanceType } from '@univerjs/core';
import { CFRuleType, CFSubRuleType } from '@univerjs/sheets-conditional-formatting';
import { FormulaEditor } from '@univerjs/sheets-formula-ui';
import { useDependency, useSidebarClick } from '@univerjs/ui';
import React, { useEffect, useRef, useState } from 'react';
import { useEffect, useRef, useState } from 'react';
import { ConditionalStyleEditor } from '../../conditional-style-editor';
import { Preview } from '../../preview';
import stylesBase from '../index.module.less';
@@ -38,7 +39,7 @@ export const FormulaStyleEditor = (props: IStyleEditorProps) => {
const divEleRef = useRef<HTMLDivElement>(null);
const [isFocusFormulaEditor, isFocusFormulaEditorSet] = useState(false);
const formulaEditorActionsRef = useRef<Parameters<typeof FormulaEditor>[0]['actions']>({});
const formulaEditorRef = useRef<IFormulaEditorRef>(null);
const [style, styleSet] = useState<IHighlightCell['style']>({});
const [formula, formulaSet] = useState(() => {
if (rule?.subType === CFSubRuleType.formula) {
@@ -89,8 +90,8 @@ export const FormulaStyleEditor = (props: IStyleEditorProps) => {
};
useSidebarClick((e: MouseEvent) => {
const handleOutClick = formulaEditorActionsRef.current?.handleOutClick;
handleOutClick && handleOutClick(e, () => isFocusFormulaEditorSet(false));
const isOutSide = formulaEditorRef.current?.isClickOutSide(e);
isOutSide && isFocusFormulaEditorSet(false);
});
return (
@@ -123,11 +124,11 @@ export const FormulaStyleEditor = (props: IStyleEditorProps) => {
}}
errorText={formulaError}
onFocus={() => { isFocusFormulaEditorSet(true); }}
actions={formulaEditorActionsRef.current}
isFocus={isFocusFormulaEditor}
initValue={formula as any}
unitId={workbook.getUnitId()}
subUnitId={worksheet?.getSheetId()}
ref={formulaEditorRef}
/>
</div>
@@ -16,11 +16,13 @@
import type { Workbook } from '@univerjs/core';
import type { IIconSet, IIconType } from '@univerjs/sheets-conditional-formatting';
import type { IStyleEditorProps } from './type';
import type { IFormulaEditorRef } from '@univerjs/sheets-formula-ui';
import type { IStyleEditorProps } from './type';
import { get, IUniverInstanceService, LocaleService, set, Tools, UniverInstanceType } from '@univerjs/core';
import { Checkbox, clsx, Dropdown, InputNumber, Select } from '@univerjs/design';
import { MoreDownSingle, SlashSingle } from '@univerjs/icons';
import {
CFNumberOperator,
CFRuleType,
@@ -33,11 +35,10 @@ import {
iconGroup,
iconMap,
} from '@univerjs/sheets-conditional-formatting';
import { FormulaEditor } from '@univerjs/sheets-formula-ui';
import { ILayoutService, useDependency, useScrollYOverContainer, useSidebarClick } from '@univerjs/ui';
import React, { forwardRef, useEffect, useMemo, useRef, useState } from 'react';
import { forwardRef, useEffect, useMemo, useRef, useState } from 'react';
import stylesBase from '../index.module.less';
import styles from './index.module.less';
@@ -58,12 +59,12 @@ const TextInput = (props: { id: number; type: CFValueType; value: number | strin
return '';
}, [props.error]);
const formulaEditorActionsRef = useRef<Parameters<typeof FormulaEditor>[0]['actions']>({});
const formulaEditorRef = useRef<IFormulaEditorRef>(null);
const [isFocusFormulaEditor, isFocusFormulaEditorSet] = useState(false);
useSidebarClick((e: MouseEvent) => {
const handleOutClick = formulaEditorActionsRef.current?.handleOutClick;
handleOutClick && handleOutClick(e, () => isFocusFormulaEditorSet(false));
const isOutSide = formulaEditorRef.current?.isClickOutSide(e);
isOutSide && isFocusFormulaEditorSet(false);
});
return (
<div className={styles.positionRelative}>
@@ -90,7 +91,7 @@ const TextInput = (props: { id: number; type: CFValueType; value: number | strin
props.onChange(formula);
}}
onFocus={() => isFocusFormulaEditorSet(true)}
actions={formulaEditorActionsRef.current}
ref={formulaEditorRef}
/>
</div>
)}
@@ -15,19 +15,20 @@
*/
import type { IFormulaInputProps } from '@univerjs/data-validation';
import type { IFormulaEditorRef } from '@univerjs/sheets-formula-ui';
import { FormulaEditor } from '@univerjs/sheets-formula-ui';
import { useSidebarClick } from '@univerjs/ui';
import React, { useRef, useState } from 'react';
import { useRef, useState } from 'react';
export function CustomFormulaInput(props: IFormulaInputProps) {
const { unitId, subUnitId, value, onChange, showError, validResult } = props;
const formula1Res = showError ? validResult?.formula1 : undefined;
const formulaEditorActionsRef = useRef<Parameters<typeof FormulaEditor>[0]['actions']>({});
const formulaEditorRef = useRef<IFormulaEditorRef>(null);
const [isFocusFormulaEditor, isFocusFormulaEditorSet] = useState(false);
useSidebarClick((e: MouseEvent) => {
const handleOutClick = formulaEditorActionsRef.current?.handleOutClick;
handleOutClick && handleOutClick(e, () => isFocusFormulaEditorSet(false));
const isOutSide = formulaEditorRef.current?.isClickOutSide(e);
isOutSide && isFocusFormulaEditorSet(false);
});
return (
@@ -49,8 +50,8 @@ export function CustomFormulaInput(props: IFormulaInputProps) {
}}
errorText={formula1Res}
onFocus={() => isFocusFormulaEditorSet(true)}
actions={formulaEditorActionsRef.current}
isSupportAcrossSheet
ref={formulaEditorRef}
/>
);
}
@@ -16,6 +16,7 @@
import type { IFormulaInputProps } from '@univerjs/data-validation';
import type { ListValidator } from '@univerjs/sheets-data-validation';
import type { IFormulaEditorRef } from '@univerjs/sheets-formula-ui';
import type { CSSProperties } from 'react';
import { DataValidationType, isFormulaString, LocaleService, Tools } from '@univerjs/core';
import { DataValidationModel, DataValidatorRegistryService } from '@univerjs/data-validation';
@@ -308,12 +309,12 @@ export function ListFormulaInput(props: IFormulaInputProps) {
}
});
const formulaEditorActionsRef = useRef<Parameters<typeof FormulaEditor>[0]['actions']>({});
const formulaEditorRef = useRef<IFormulaEditorRef>(null);
const [isFocusFormulaEditor, isFocusFormulaEditorSet] = useState(false);
useSidebarClick((e: MouseEvent) => {
const handleOutClick = formulaEditorActionsRef.current?.handleOutClick;
handleOutClick && handleOutClick(e, () => isFocusFormulaEditorSet(false));
const isOutSide = formulaEditorRef.current?.isClickOutSide(e);
isOutSide && isFocusFormulaEditorSet(false);
});
return (
@@ -351,8 +352,8 @@ export function ListFormulaInput(props: IFormulaInputProps) {
}}
errorText={(formula1Res || localError) || undefined}
onFocus={() => isFocusFormulaEditorSet(true)}
actions={formulaEditorActionsRef.current}
isSupportAcrossSheet
ref={formulaEditorRef}
/>
<div ref={containerRef} style={{ marginTop: '12px' }}>
{refFinalList.map((item) => {
+1 -1
View File
@@ -23,7 +23,7 @@ export { InsertFunctionOperation } from './commands/operations/insert-function.o
export { MoreFunctionsOperation } from './commands/operations/more-functions.operation';
export { ReferenceAbsoluteOperation } from './commands/operations/reference-absolute.operation';
export { SearchFunctionOperation } from './commands/operations/search-function.operation';
export { FormulaEditor } from './views/formula-editor/index';
export { FormulaEditor, type IFormulaEditorRef } from './views/formula-editor/index';
export { type IRangeSelectorInstance, type IRangeSelectorProps, RangeSelector } from './views/range-selector/index';
export { RefSelectionsRenderService } from './services/render-services/ref-selections.render-service';
export { GlobalRangeSelectorService, type IShowRangeSelectorDialogOptions } from './services/range-selector.service';
@@ -80,11 +80,21 @@ export class UniverSheetsFormulaUIPlugin extends Plugin {
[FormulaRenderManagerController],
[FormulaReorderController],
]);
this._initUIPart();
}
override onReady(): void {
// render basics
([
[RefSelectionsRenderService],
] as Dependency[]).forEach((dep) => {
this.disposeWithMe(this._renderManagerService.registerRenderModule(UniverInstanceType.UNIVER_SHEET, dep));
});
}
override onRendered(): void {
([
[RefSelectionsRenderService],
[FormulaAlertRenderController],
] as Dependency[]).forEach((dep) => {
this.disposeWithMe(this._renderManagerService.registerRenderModule(UniverInstanceType.UNIVER_SHEET, dep));
@@ -95,10 +105,6 @@ export class UniverSheetsFormulaUIPlugin extends Plugin {
[FormulaClipboardController],
[FormulaRenderManagerController],
]);
// Since component FormulaEditor relies on RefSelectionsRenderService, it should be
// registered after RefSelectionsRenderService is registered.
this._initUIPart();
}
override onSteady(): void {
@@ -40,9 +40,11 @@ export interface IRefSelection {
index: number;
}
// eslint-disable-next-line complexity, max-lines-per-function
export function calcHighlightRanges(opts: {
unitId: string;
subUnitId: string;
currentWorkbook: Workbook;
refSelections: IRefSelection[];
editor: Editor | undefined;
refSelectionsService: SheetsSelectionsService;
@@ -54,6 +56,7 @@ export function calcHighlightRanges(opts: {
const {
unitId,
subUnitId,
currentWorkbook,
refSelections,
editor,
refSelectionsService,
@@ -62,10 +65,12 @@ export function calcHighlightRanges(opts: {
themeService,
univerInstanceService,
} = opts;
const currentUnitId = currentWorkbook.getUnitId();
const workbook = univerInstanceService.getUnit<Workbook>(unitId, UniverInstanceType.UNIVER_SHEET);
const worksheet = workbook?.getActiveSheet();
const selectionWithStyle: ISelectionWithStyle[] = [];
if (!workbook || !worksheet) {
const currentActiveWorksheet = currentWorkbook?.getActiveSheet();
if (!workbook || !worksheet || (currentUnitId !== unitId && !currentActiveWorksheet)) {
refSelectionsService.setSelections(selectionWithStyle);
return;
}
@@ -74,7 +79,6 @@ export function calcHighlightRanges(opts: {
const skeleton = sheetSkeletonManagerService?.getWorksheetSkeleton(currentSheetId)?.skeleton;
if (!skeleton) return;
const endIndexes: number[] = [];
for (let i = 0, len = refSelections.length; i < len; i++) {
const refSelection = refSelections[i];
@@ -82,11 +86,15 @@ export function calcHighlightRanges(opts: {
const unitRangeName = deserializeRangeWithSheet(token);
const { unitId: refUnitId, sheetName, range: rawRange } = unitRangeName;
if (refUnitId && unitId !== refUnitId) {
const refSheetId = getSheetIdByName(sheetName);
if (currentUnitId !== unitId && refUnitId !== currentUnitId) {
continue;
}
const refSheetId = getSheetIdByName(sheetName);
if (refUnitId && refUnitId !== currentUnitId) {
continue;
}
if ((refSheetId && refSheetId !== currentSheetId) || (!refSheetId && currentSheetId !== subUnitId)) {
continue;
@@ -132,10 +140,13 @@ export function useSheetHighlight(unitId: string, subUnitId: string) {
const sheetSkeletonManagerService = render?.with(SheetSkeletonManagerService);
const highlightSheet = useEvent((refSelections: IRefSelection[], editor?: Editor) => {
const currentWorkbook = univerInstanceService.getCurrentUnitOfType<Workbook>(UniverInstanceType.UNIVER_SHEET);
if (!currentWorkbook) return;
if (refSelectionsRenderService?.selectionMoving) return;
const selectionWithStyle = calcHighlightRanges({
unitId,
subUnitId,
currentWorkbook,
refSelections,
editor,
refSelectionsService,
@@ -144,7 +155,6 @@ export function useSheetHighlight(unitId: string, subUnitId: string) {
themeService,
univerInstanceService,
});
if (!selectionWithStyle) return;
const allControls = refSelectionsRenderService?.getSelectionControls() || [];
if (allControls.length === selectionWithStyle.length) {
@@ -14,12 +14,13 @@
* limitations under the License.
*/
import { EDITOR_ACTIVATED, IContextService } from '@univerjs/core';
import type { Workbook } from '@univerjs/core';
import { EDITOR_ACTIVATED, IContextService, IUniverInstanceService, UniverInstanceType } from '@univerjs/core';
import { IRenderManagerService } from '@univerjs/engine-render';
import { IRefSelectionsService, REF_SELECTIONS_ENABLED } from '@univerjs/sheets';
import { IContextMenuService, useDependency } from '@univerjs/ui';
import { IContextMenuService, useDependency, useObservable } from '@univerjs/ui';
import { useEffect, useLayoutEffect } from 'react';
import { useEffect, useLayoutEffect, useMemo } from 'react';
import { RefSelectionsRenderService } from '../../../services/render-services/ref-selections.render-service';
export const useRefactorEffect = (isNeed: boolean, selecting: boolean, unitId: string, disableContextMenu = true) => {
@@ -27,8 +28,9 @@ export const useRefactorEffect = (isNeed: boolean, selecting: boolean, unitId: s
const contextService = useDependency(IContextService);
const contextMenuService = useDependency(IContextMenuService);
const refSelectionsService = useDependency(IRefSelectionsService);
const render = renderManagerService.getRenderById(unitId);
const univerInstanceService = useDependency(IUniverInstanceService);
const currentUnit = useObservable(useMemo(() => univerInstanceService.getCurrentTypeOfUnit$<Workbook>(UniverInstanceType.UNIVER_SHEET), [univerInstanceService]));
const render = renderManagerService.getRenderById(currentUnit?.getUnitId() ?? '');
const refSelectionsRenderService = render?.with(RefSelectionsRenderService);
useLayoutEffect(() => {
@@ -29,6 +29,10 @@ export const useResetSelection = (isNeed: boolean, unitId: string, subUnitId: st
const selections = [...sheetsSelectionsService.getWorkbookSelections(unitId).getSelectionsOfWorksheet(subUnitId)];
const workbook = univerInstanceService.getCurrentUnitForType<Workbook>(UniverInstanceType.UNIVER_SHEET);
const currentSheet = workbook?.getActiveSheet();
if (workbook?.getUnitId() !== unitId) {
univerInstanceService.setCurrentUnitForType(unitId);
}
if (currentSheet && currentSheet.getSheetId() === subUnitId) {
sheetsSelectionsService.setSelections(selections);
}
@@ -21,7 +21,7 @@ import type { Editor } from '@univerjs/docs-ui';
import type { ISelectionWithCoord, ISetSelectionsOperationParams } from '@univerjs/sheets';
import type { RefObject } from 'react';
import type { IRefSelection } from './use-highlight';
import { DisposableCollection, ICommandService, IUniverInstanceService, ThemeService } from '@univerjs/core';
import { DisposableCollection, ICommandService, IUniverInstanceService, ThemeService, UniverInstanceType } from '@univerjs/core';
import { DocSelectionManagerService } from '@univerjs/docs';
import { deserializeRangeWithSheet, generateStringWithSequence, LexerTreeBuilder, sequenceNodeType, serializeRange, serializeRangeWithSheet } from '@univerjs/engine-formula';
import { IRenderManagerService } from '@univerjs/engine-render';
@@ -85,11 +85,12 @@ export const useSheetSelectionChange = (
const sheetName = useMemo(() => getSheetNameById(subUnitId), [getSheetNameById, subUnitId]);
const activeSheet = useObservable(workbook?.activeSheet$);
const contextRef = useStateRef({ activeSheet, sheetName });
const render = renderManagerService.getRenderById(unitId);
const currentUnit = useObservable(useMemo(() => univerInstanceService.getCurrentTypeOfUnit$<Workbook>(UniverInstanceType.UNIVER_SHEET), [univerInstanceService]));
const render = renderManagerService.getRenderById(currentUnit?.getUnitId() ?? '');
const refSelectionsRenderService = render?.with(RefSelectionsRenderService);
const sheetSkeletonManagerService = render?.with(SheetSkeletonManagerService);
const refSelectionsService = useDependency(IRefSelectionsService);
// eslint-disable-next-line complexity
const onSelectionsChange = useEvent((selections: IRange[], isEnd: boolean) => {
const ctx = prepareSelectionChangeContext({ editor, lexerTreeBuilder });
@@ -105,11 +106,12 @@ export const useSheetSelectionChange = (
const rangeSheetId = range.sheetId ?? subUnitId;
const unitRangeName = {
range,
unitId: range.unitId ?? unitId,
unitId: range.unitId ?? currentUnit!.getUnitId(),
sheetName: getSheetNameById(rangeSheetId),
};
const isAcrossSheet = rangeSheetId !== subUnitId;
const refRanges = unitRangesToText([unitRangeName], isSupportAcrossSheet && isAcrossSheet, sheetName);
const isAcrossWorkbook = currentUnit?.getUnitId() !== unitId;
const refRanges = unitRangesToText([unitRangeName], isSupportAcrossSheet && (isAcrossSheet || isAcrossWorkbook), sheetName, isAcrossWorkbook);
sequenceNodes.push({ token: refRanges[0], nodeType: sequenceNodeType.REFERENCE } as any);
const newSequenceNodes = [...sequenceNodes, ...lastNodes];
const result = sequenceNodeToText(newSequenceNodes);
@@ -336,6 +338,7 @@ export const useSheetSelectionChange = (
sheetSkeletonManagerService,
themeService,
univerInstanceService,
currentWorkbook: currentUnit!,
});
});
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { DOCS_NORMAL_EDITOR_UNIT_ID_KEY, ICommandService } from '@univerjs/core';
import { DOCS_NORMAL_EDITOR_UNIT_ID_KEY, ICommandService, IUniverInstanceService, UniverInstanceType } from '@univerjs/core';
import { IEditorService } from '@univerjs/docs-ui';
import { IRenderManagerService } from '@univerjs/engine-render';
import { SetWorksheetActiveOperation } from '@univerjs/sheets';
@@ -22,29 +22,48 @@ import { useDependency } from '@univerjs/ui';
import { useEffect } from 'react';
import { RefSelectionsRenderService } from '../../../services/render-services/ref-selections.render-service';
export const useSwitchSheet = (isNeed: boolean, unitId: string, isSupportAcrossSheet: boolean, isFocusSet: (v: boolean) => void, onBlur: () => void, refresh: () => void) => {
export const useSwitchSheet = (
isNeed: boolean,
unitId: string,
isSupportAcrossSheet: boolean,
isFocusSet: (v: boolean) => void,
onBlur: () => void,
refresh: () => void
) => {
const commandService = useDependency(ICommandService);
const editorService = useDependency(IEditorService);
const renderManagerService = useDependency(IRenderManagerService);
const render = renderManagerService.getRenderById(unitId);
const univerInstanceService = useDependency(IUniverInstanceService);
const refSelectionsRenderService = render?.with(RefSelectionsRenderService);
useEffect(() => {
if (isNeed && refSelectionsRenderService) {
if (isSupportAcrossSheet) {
const handleRefresh = () => {
const length = refSelectionsRenderService.getSelectionControls().length;
for (let index = 1; index <= length; index++) {
refSelectionsRenderService.clearLastSelection();
}
return setTimeout(() => {
refresh();
}, 30);
};
const d = commandService.onCommandExecuted((info) => {
if (info.id === SetWorksheetActiveOperation.id) {
const length = refSelectionsRenderService.getSelectionControls().length;
for (let index = 1; index <= length; index++) {
refSelectionsRenderService.clearLastSelection();
}
setTimeout(() => {
refresh();
}, 30);
handleRefresh();
}
});
const d2 = univerInstanceService.getCurrentTypeOfUnit$(UniverInstanceType.UNIVER_SHEET).subscribe((unit) => {
handleRefresh();
});
return () => {
d.dispose();
d2.unsubscribe();
};
} else {
const d = commandService.beforeCommandExecuted((info) => {
@@ -17,7 +17,7 @@
import type { DocumentDataModel, IDisposable, ITextRange } from '@univerjs/core';
import type { Editor, IKeyboardEventConfig } from '@univerjs/docs-ui';
import type { KeyCode, MetaKeys } from '@univerjs/ui';
import type { CSSProperties, ReactNode } from 'react';
import type { CSSProperties, ReactNode, Ref } from 'react';
import type { FormulaSelectingType } from './hooks/use-formula-selection';
import type { IRefSelection } from './hooks/use-highlight';
import { BuildTextUtils, createInternalEditorID, generateRandomId, IUniverInstanceService, UniverInstanceType } from '@univerjs/core';
@@ -26,7 +26,7 @@ import { DocBackScrollRenderController, DocSelectionRenderService, IEditorServic
import { IRenderManagerService } from '@univerjs/engine-render';
import { EMBEDDING_FORMULA_EDITOR } from '@univerjs/sheets-ui';
import { useDependency, useEvent, useObservable, useUpdateEffect } from '@univerjs/ui';
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
import { forwardRef, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from 'react';
import { findIndexFromSequenceNodes, findRefSequenceIndex } from '../range-selector/utils/find-index-from-sequence-nodes';
import { HelpFunction } from './help-function/HelpFunction';
import { useFocus } from './hooks/use-focus';
@@ -55,9 +55,6 @@ export interface IFormulaEditorProps {
onFocus?: () => void;
onBlur?: () => void;
isSupportAcrossSheet?: boolean;
actions?: {
handleOutClick?: (e: MouseEvent, cb: () => void) => void;
};
className?: string;
editorId?: string;
moveCursor?: boolean;
@@ -76,7 +73,12 @@ export interface IFormulaEditorProps {
}
const noop = () => { };
export function FormulaEditor(props: IFormulaEditorProps) {
export interface IFormulaEditorRef {
isClickOutSide: (e: MouseEvent) => boolean;
}
export const FormulaEditor = forwardRef((props: IFormulaEditorProps, ref: Ref<IFormulaEditorRef>) => {
const {
errorText,
initValue,
@@ -88,7 +90,6 @@ export function FormulaEditor(props: IFormulaEditorProps) {
onBlur = noop,
onChange: propOnChange,
onVerify,
actions,
className,
editorId: propEditorId,
moveCursor = true,
@@ -106,16 +107,14 @@ export function FormulaEditor(props: IFormulaEditorProps) {
const editorService = useDependency(IEditorService);
const sheetEmbeddingRef = useRef<HTMLDivElement>(null);
const onChange = useEvent(propOnChange);
// init actions
if (actions) {
actions.handleOutClick = (e: MouseEvent, cb: () => void) => {
useImperativeHandle(ref, () => ({
isClickOutSide: (e: MouseEvent) => {
if (sheetEmbeddingRef.current) {
const isContain = sheetEmbeddingRef.current.contains(e.target as Node);
!isContain && cb();
return !sheetEmbeddingRef.current.contains(e.target as Node);
}
};
}
return false;
},
}));
const onFormulaSelectingChange = useEvent(propOnFormulaSelectingChange);
const searchFunctionRef = useRef<HTMLElement>(null);
const editorRef = useRef<Editor>(undefined);
@@ -352,4 +351,4 @@ export function FormulaEditor(props: IFormulaEditorProps) {
</div>
)
;
}
});
@@ -15,7 +15,7 @@
*/
import type { IUnitRangeName, IUniverInstanceService, Workbook } from '@univerjs/core';
import { serializeRange, serializeRangeWithSheet } from '@univerjs/engine-formula';
import { serializeRange, serializeRangeToRefString, serializeRangeWithSheet } from '@univerjs/engine-formula';
export function getSheetIdByName(univerInstanceService: IUniverInstanceService, unitId: string, name: string) {
return univerInstanceService.getUnit<Workbook>(unitId)?.getSheetBySheetName(name)?.getSheetId() || '';
@@ -24,11 +24,14 @@ export function getSheetNameById(univerInstanceService: IUniverInstanceService,
return univerInstanceService.getUnit<Workbook>(unitId)?.getSheetBySheetId(sheetId)?.getName() || '';
}
export const unitRangesToText = (ranges: IUnitRangeName[], isNeedSheetName: boolean = false, originSheetName = '') => {
if (!isNeedSheetName) {
export const unitRangesToText = (ranges: IUnitRangeName[], isNeedSheetName: boolean = false, originSheetName = '', isNeedWorkbookName = false) => {
if (!isNeedSheetName && !isNeedWorkbookName) {
return ranges.map((item) => serializeRange(item.range));
} else {
return ranges.map((item) => {
if (isNeedWorkbookName) {
return serializeRangeToRefString(item);
}
if (item.sheetName !== '' && item.sheetName !== originSheetName) {
return serializeRangeWithSheet(item.sheetName, item.range);
}
@@ -18,7 +18,6 @@
import type { DocumentDataModel, ICellData, ICommandInfo, IDisposable, IDocumentBody, IDocumentData, IDocumentStyle, IMutationInfo, IStyleData, Nullable, Styles, Workbook } from '@univerjs/core';
import type { IRichTextEditingMutationParams } from '@univerjs/docs';
import type { IRenderContext, IRenderModule } from '@univerjs/engine-render';
import type { ISetRangeValuesCommandParams, MutationsAffectRange, WorkbookSelectionModel } from '@univerjs/sheets';
import type { IEditorBridgeServiceVisibleParam } from '../../services/editor-bridge.service';
@@ -63,7 +62,7 @@ import {
IRenderManagerService,
} from '@univerjs/engine-render';
import { adjustRangeOnMutation, COMMAND_LISTENER_SKELETON_CHANGE, InsertColMutation, InsertRowMutation, MoveColsMutation, MoveRowsMutation, REF_SELECTIONS_ENABLED, RemoveColMutation, RemoveRowMutation, SetRangeValuesCommand, SetSelectionsOperation, SetWorksheetActivateCommand, SetWorksheetActiveOperation, SheetInterceptorService, SheetsSelectionsService } from '@univerjs/sheets';
import { adjustRangeOnMutation, COMMAND_LISTENER_SKELETON_CHANGE, InsertColMutation, InsertRowMutation, MoveColsMutation, MoveRowsMutation, REF_SELECTIONS_ENABLED, RemoveColMutation, RemoveRowMutation, SetRangeValuesCommand, SetSelectionsOperation, SetWorksheetActivateCommand, SetWorksheetActiveOperation, SheetInterceptorService } from '@univerjs/sheets';
import { KeyCode, MetaKeys } from '@univerjs/ui';
import { distinctUntilChanged, filter } from 'rxjs';
import { getEditorObject } from '../../basics/editor/get-editor-object';
@@ -86,23 +85,20 @@ enum CursorChange {
CursorChange,
}
export class EditingRenderController extends Disposable implements IRenderModule {
export class EditingRenderController extends Disposable {
/**
* It is used to distinguish whether the user has actively moved the cursor in the editor, mainly through mouse clicks.
*/
private _cursorChange: CursorChange = CursorChange.InitialState;
/** If the corresponding unit is active and prepared for editing. */
private _isUnitEditing = false;
private _editingUnit = '';
private _workbookSelections: WorkbookSelectionModel;
private _d: Nullable<IDisposable>;
_cursorTimeout: NodeJS.Timeout;
constructor(
private readonly _context: IRenderContext<Workbook>,
@Inject(SheetsSelectionsService) selectionManagerService: SheetsSelectionsService,
@IUndoRedoService private readonly _undoRedoService: IUndoRedoService,
@IContextService private readonly _contextService: IContextService,
@IRenderManagerService private readonly _renderManagerService: IRenderManagerService,
@@ -114,48 +110,21 @@ export class EditingRenderController extends Disposable implements IRenderModule
@ICommandService private readonly _commandService: ICommandService,
@Inject(LocaleService) protected readonly _localService: LocaleService,
@IEditorService private readonly _editorService: IEditorService,
@Inject(SheetCellEditorResizeService) private readonly _sheetCellEditorResizeService: SheetCellEditorResizeService, // RenderModule
@IUniverInstanceService private readonly _univerInstanceService: IUniverInstanceService,
@Inject(SheetInterceptorService) private readonly _sheetInterceptorService: SheetInterceptorService
@Inject(SheetInterceptorService) private readonly _sheetInterceptorService: SheetInterceptorService,
@Inject(SheetCellEditorResizeService) private readonly _sheetCellEditorResizeService: SheetCellEditorResizeService
) {
super();
this._workbookSelections = selectionManagerService.getWorkbookSelections(this._context.unitId);
// EditingRenderController is per unit. It should only handle keyboard events when the unit is
// the current of its type.
this.disposeWithMe(this._univerInstanceService.getCurrentTypeOfUnit$(UniverInstanceType.UNIVER_SHEET).subscribe((workbook) => {
if (workbook?.getUnitId() === this._context.unitId) {
this._d = this._init();
} else {
this._disposeCurrent();
// Force ending editor when he switch to another workbook.
if (this._isUnitEditing) {
this._handleEditorInvisible({
visible: false,
eventType: DeviceInputEventType.Keyboard,
keycode: KeyCode.ESC,
unitId: this._context.unitId,
});
this._isUnitEditing = false;
}
}
}));
this.disposeWithMe(this._init());
this._initEditorVisibilityListener();
}
override dispose(): void {
super.dispose();
this._disposeCurrent();
}
private _disposeCurrent(): void {
this._d?.dispose();
this._d = null;
}
private _init(): IDisposable {
@@ -180,16 +149,28 @@ export class EditingRenderController extends Disposable implements IRenderModule
}
private _initEditorVisibilityListener(): void {
this.disposeWithMe(this._univerInstanceService.getCurrentTypeOfUnit$(UniverInstanceType.UNIVER_SHEET).subscribe(async (unit) => {
if (this._editingUnit && unit?.getUnitId() !== this._editingUnit && !this._editorBridgeService.isForceKeepVisible()) {
await this._handleEditorInvisible({
visible: false,
eventType: DeviceInputEventType.Keyboard,
keycode: KeyCode.ESC,
unitId: this._editingUnit,
});
this._editingUnit = '';
}
}));
this.disposeWithMe(
this._editorBridgeService.visible$
.pipe(distinctUntilChanged((prev, curr) => prev.visible === curr.visible))
.subscribe((params) => {
if ((params.unitId === DOCS_FORMULA_BAR_EDITOR_UNIT_ID_KEY || params.unitId === this._context.unitId) && params.visible) {
this._isUnitEditing = true;
.subscribe(async (params) => {
if (params.visible) {
this._editingUnit = params.unitId;
this._handleEditorVisible(params);
} else if (this._isUnitEditing) {
this._handleEditorInvisible(params);
this._isUnitEditing = false;
} else if (this._editingUnit) {
await this._handleEditorInvisible(params);
this._editingUnit = '';
}
})
);
@@ -202,7 +183,7 @@ export class EditingRenderController extends Disposable implements IRenderModule
d.add(renderConfig.document.onPointerDown$.subscribeEvent(() => {
// fix https://github.com/dream-num/univer/issues/628, need to recalculate the cell editor size after
// it acquire focus.
if (this._isUnitEditing && this._editorBridgeService.isVisible()) {
if (this._editingUnit && this._editorBridgeService.isVisible()) {
const param = this._editorBridgeService.getEditCellState();
const editorId = this._editorBridgeService.getCurrentEditorId();
@@ -210,7 +191,7 @@ export class EditingRenderController extends Disposable implements IRenderModule
return;
}
this._sheetCellEditorResizeService.fitTextSize();
this._sheetCellEditorResizeService?.fitTextSize();
}
}));
}
@@ -260,7 +241,7 @@ export class EditingRenderController extends Disposable implements IRenderModule
}
}
this._sheetCellEditorResizeService.resizeCellEditor(() => {
this._sheetCellEditorResizeService?.resizeCellEditor(() => {
this._textSelectionManagerService.refreshSelection({
unitId: DOCS_NORMAL_EDITOR_UNIT_ID_KEY,
subUnitId: DOCS_NORMAL_EDITOR_UNIT_ID_KEY,
@@ -341,7 +322,7 @@ export class EditingRenderController extends Disposable implements IRenderModule
const { unitId: commandUnitId } = params;
// Only when the sheet it attached to is focused. Maybe we should change it to the render unit sys.
if (!this._isCurrentSheetFocused() || isRangeSelector(commandUnitId)) {
if (isRangeSelector(commandUnitId)) {
return;
}
@@ -352,7 +333,7 @@ export class EditingRenderController extends Disposable implements IRenderModule
}
if (commandUnitId === DOCS_NORMAL_EDITOR_UNIT_ID_KEY) {
this._sheetCellEditorResizeService.fitTextSize();
this._sheetCellEditorResizeService?.fitTextSize();
}
}
}));
@@ -387,7 +368,7 @@ export class EditingRenderController extends Disposable implements IRenderModule
}
// You can double-click on the cell or input content by keyboard to put the cell into the edit state.
// eslint-disable-next-line complexity
private _handleEditorVisible(param: IEditorBridgeServiceVisibleParam) {
const { eventType, keycode } = param;
// Change `CursorChange` to changed status, when formula bar clicked.
@@ -428,7 +409,7 @@ export class EditingRenderController extends Disposable implements IRenderModule
return;
}
this._sheetCellEditorResizeService.fitTextSize(() => {
this._sheetCellEditorResizeService?.fitTextSize(() => {
const viewMain = scene.getViewport(DOC_VIEWPORT_KEY.VIEW_MAIN);
viewMain?.scrollToViewportPos({
viewportScrollX: Number.POSITIVE_INFINITY,
@@ -508,9 +489,8 @@ export class EditingRenderController extends Disposable implements IRenderModule
const snapshot = Tools.deepClone(documentDataModel?.getSnapshot());
let { keycode } = param;
this._cursorChange = CursorChange.InitialState;
const currentUnitId = editCellState?.unitId ?? '';
this._exitInput(param);
if (editCellState == null) {
return;
}
@@ -522,9 +502,12 @@ export class EditingRenderController extends Disposable implements IRenderModule
keycode = KeyCode.ESC;
}
const workbook = this._context.unit;
const workbook = this._univerInstanceService.getUnit<Workbook>(currentUnitId, UniverInstanceType.UNIVER_SHEET);
if (!workbook) {
return;
}
const worksheet = workbook.getActiveSheet();
const workbookId = this._context.unitId;
const workbookId = workbook.getUnitId();
const worksheetId = worksheet.getSheetId();
const { unitId, sheetId } = editCellState;
@@ -548,7 +531,7 @@ export class EditingRenderController extends Disposable implements IRenderModule
if (selections) {
this._contextService.setContextValue(REF_SELECTIONS_ENABLED, false);
this._commandService.syncExecuteCommand(SetSelectionsOperation.id, {
unitId: this._context.unit.getUnitId(),
unitId: workbookId,
subUnitId: sheetId,
selections,
});
@@ -568,7 +551,7 @@ export class EditingRenderController extends Disposable implements IRenderModule
}
// moveSelection need to put behind of SetRangeValuesCommand, fix https://github.com/dream-num/univer/issues/1155
this._moveSelection(keycode);
this._moveSelection(keycode, currentUnitId);
}
private _getEditorObject() {
@@ -589,10 +572,12 @@ export class EditingRenderController extends Disposable implements IRenderModule
if (editCellState == null) {
return true;
}
const { unitId, sheetId, row, column } = editCellState;
const workbook = this._context.unit;
const workbook = this._univerInstanceService.getUnit<Workbook>(unitId, UniverInstanceType.UNIVER_SHEET);
if (!workbook) {
return true;
}
let worksheet = workbook.getActiveSheet();
// If the target cell does not exist, there is no need to execute setRangeValue
@@ -673,7 +658,7 @@ export class EditingRenderController extends Disposable implements IRenderModule
this._undoRedoService.clearUndoRedo(DOCS_FORMULA_BAR_EDITOR_UNIT_ID_KEY);
}
private _moveSelection(keycode?: KeyCode) {
private _moveSelection(keycode: KeyCode | undefined, currentUnitId: string) {
if (keycode == null || !MOVE_SELECTION_KEYCODE_LIST.includes(keycode)) {
return;
}
@@ -700,6 +685,10 @@ export class EditingRenderController extends Disposable implements IRenderModule
direction = Direction.RIGHT;
break;
}
const currentUnit = this._univerInstanceService.getCurrentUnitOfType(UniverInstanceType.UNIVER_SHEET);
if (currentUnitId && currentUnit?.getUnitId() !== currentUnitId) {
this._univerInstanceService.setCurrentUnitForType(currentUnitId);
}
if (keycode === KeyCode.ENTER || keycode === KeyCode.TAB) {
this._commandService.executeCommand(MoveSelectionEnterAndTabCommand.id, {
@@ -755,12 +744,6 @@ export class EditingRenderController extends Disposable implements IRenderModule
return this._univerInstanceService.getUnit<DocumentDataModel>(DOCS_NORMAL_EDITOR_UNIT_ID_KEY, UniverInstanceType.UNIVER_DOC);
}
// WTF: this is should not exist at all. It is because all editor instances reuse the singleton
// "DocSelectionManagerService" and other modules. Which will be refactored soon in August, 2024.
private _isCurrentSheetFocused(): boolean {
return this._univerInstanceService.getFocusedUnit()?.getUnitId() === this._context.unitId;
}
private _getEditorSkeleton(editorId: string) {
return this._renderManagerService.getRenderById(editorId)?.with(DocSkeletonManagerService).getSkeleton();
}
@@ -769,6 +752,10 @@ export class EditingRenderController extends Disposable implements IRenderModule
return this._renderManagerService.getRenderById(editorId)?.with(DocSkeletonManagerService).getViewModel();
}
private _getEditingUnit() {
return this._editingUnit ? this._univerInstanceService.getUnit<Workbook>(this._editingUnit, UniverInstanceType.UNIVER_SHEET) : null;
}
private _emptyDocumentDataModel(documentStyle: IDocumentStyle, removeStyle: boolean) {
const empty = (documentDataModel: DocumentDataModel, resetDocumentStyle?: boolean) => {
const snapshot = Tools.deepClone(documentDataModel.getSnapshot());
@@ -20,7 +20,7 @@ import type { IRender, IRenderContext, IRenderModule } from '@univerjs/engine-re
import type { ISelectionWithStyle, ISetRangeValuesMutationParams } from '@univerjs/sheets';
import type { ICurrentEditCellParam, IEditorBridgeServiceVisibleParam } from '../../services/editor-bridge.service';
import { DisposableCollection, DOCS_NORMAL_EDITOR_UNIT_ID_KEY, FOCUSING_FX_BAR_EDITOR, FOCUSING_SHEET, ICommandService, IContextService, Inject, IUniverInstanceService, RxDisposable, toDisposable, UniverInstanceType } from '@univerjs/core';
import { DocSelectionRenderService, IEditorService, IRangeSelectorService } from '@univerjs/docs-ui';
import { DocSelectionRenderService } from '@univerjs/docs-ui';
import { DeviceInputEventType, IRenderManagerService } from '@univerjs/engine-render';
import {
ClearSelectionFormatCommand,
@@ -46,14 +46,9 @@ export class EditorBridgeRenderController extends RxDisposable implements IRende
@IUniverInstanceService private readonly _instanceSrv: IUniverInstanceService,
@ICommandService private readonly _commandService: ICommandService,
@IEditorBridgeService private readonly _editorBridgeService: IEditorBridgeService,
// FIXME: should use WorkbookSelectionModel
// FIXME: should check if it is the current sheet, if it becomes the current sheet,
// it should update cell params, otherwise it should do nothing.
@Inject(SheetsSelectionsService) private readonly _selectionManagerService: SheetsSelectionsService,
@IRangeSelectorService private readonly _rangeSelectorService: IRangeSelectorService,
@IContextService private readonly _contextService: IContextService,
@IRenderManagerService private readonly _renderManagerService: IRenderManagerService,
@IEditorService private readonly _editorService: IEditorService,
@Inject(SheetSkeletonManagerService) private readonly _sheetSkeletonManagerService: SheetSkeletonManagerService
) {
super();
@@ -65,8 +60,6 @@ export class EditorBridgeRenderController extends RxDisposable implements IRende
this._disposeCurrent();
}
}));
this._initialRangeSelector();
}
private _init(): IDisposable {
@@ -93,7 +86,6 @@ export class EditorBridgeRenderController extends RxDisposable implements IRende
private _updateEditorPosition(params: Nullable<ISelectionWithStyle[]>) {
if (this._editorBridgeService.isVisible().visible) return;
if (this._rangeSelectorService.selectorModalVisible) return;
const primary = params?.[params.length - 1]?.primary;
if (primary) {
@@ -260,56 +252,6 @@ export class EditorBridgeRenderController extends RxDisposable implements IRende
});
}
private _initialRangeSelector() {
this.disposeWithMe(this._selectionManagerService.selectionMoving$.subscribe(this._rangeSelector.bind(this)));
this.disposeWithMe(this._selectionManagerService.selectionMoveStart$.subscribe(this._rangeSelector.bind(this)));
// TODO: this method shouldn't be here. Fix in when refactor the range selector.
/**
* pro/issues/388
* When the range selector is opened,
* the current selection needs to be synchronized to the range selector.
*/
this.disposeWithMe(
this._rangeSelectorService.openSelector$.subscribe(() => {
const { unitId, sheetId, sheetName } = this._getCurrentUnitIdAndSheetId();
if (!sheetId || !sheetName) return;
const selectionWithStyle = this._selectionManagerService.getCurrentSelections();
const ranges = selectionWithStyle?.map((value) => ({ range: value.range, unitId, sheetId, sheetName }));
if (ranges) {
this._rangeSelectorService.selectionChange(ranges);
}
})
);
}
private _rangeSelector(selectionWithStyle: Nullable<ISelectionWithStyle[]>) {
if (!selectionWithStyle) {
return;
}
const { unitId, sheetId, sheetName } = this._getCurrentUnitIdAndSheetId();
if (!sheetId || !sheetName) return;
const ranges = selectionWithStyle.map((value: ISelectionWithStyle) => {
return { range: value.range, unitId, sheetId, sheetName };
});
this._rangeSelectorService.selectionChange(ranges);
}
private _getCurrentUnitIdAndSheetId() {
const workbook = this._context.unit;
const worksheet = workbook.getActiveSheet();
return {
unitId: workbook.getUnitId(),
sheetId: worksheet?.getSheetId(),
sheetName: worksheet?.getName(),
};
}
private _getSheetObject() {
return getSheetObject(this._context.unit, this._context)!;
}
+3 -2
View File
@@ -139,6 +139,7 @@ export class UniverSheetsUIPlugin extends Plugin {
[SheetCanvasPopManagerService],
[CellAlertManagerService],
[SelectAllService],
[SheetCellEditorResizeService],
// controllers
[ActiveWorksheetController],
@@ -153,6 +154,7 @@ export class UniverSheetsUIPlugin extends Plugin {
[SheetsDefinedNameController],
[EditorDataSyncController],
[SheetCheckboxController],
[EditingRenderController],
// permission
[SheetPermissionPanelModel],
@@ -198,6 +200,7 @@ export class UniverSheetsUIPlugin extends Plugin {
[AutoWidthController],
[EditorDataSyncController],
[SheetCheckboxController],
[EditingRenderController],
]);
}
@@ -232,7 +235,6 @@ export class UniverSheetsUIPlugin extends Plugin {
[SheetsScrollRenderController],
[HeaderFreezeRenderController],
[SheetsZoomRenderController],
[SheetCellEditorResizeService],
[FormatPainterRenderController],
[ClipboardRenderController],
@@ -248,7 +250,6 @@ export class UniverSheetsUIPlugin extends Plugin {
// editor
[EditorBridgeRenderController],
[EditingRenderController],
// permission
[SheetPermissionInterceptorCanvasRenderController],
@@ -14,12 +14,12 @@
* limitations under the License.
*/
import type { DocumentDataModel, IPosition, Nullable, Workbook } from '@univerjs/core';
import type { DocumentSkeleton, IDocumentLayoutObject, IRenderContext, IRenderModule, Scene } from '@univerjs/engine-render';
import { Disposable, DOCS_NORMAL_EDITOR_UNIT_ID_KEY, HorizontalAlign, Inject, IUniverInstanceService, UniverInstanceType, VerticalAlign, WrapStrategy } from '@univerjs/core';
import type { DocumentDataModel, IPosition, Nullable } from '@univerjs/core';
import type { DocumentSkeleton, IDocumentLayoutObject, Scene } from '@univerjs/engine-render';
import { Disposable, DOCS_NORMAL_EDITOR_UNIT_ID_KEY, HorizontalAlign, IUniverInstanceService, UniverInstanceType, VerticalAlign, WrapStrategy } from '@univerjs/core';
import { DocSkeletonManagerService } from '@univerjs/docs';
import { DOCS_COMPONENT_MAIN_LAYER_INDEX, VIEWPORT_KEY } from '@univerjs/docs-ui';
import { convertTextRotation, fixLineWidthByScale, IRenderManagerService, Rect, ScrollBar } from '@univerjs/engine-render';
import { convertTextRotation, fixLineWidthByScale, getCurrentTypeOfRenderer, IRenderManagerService, Rect, ScrollBar } from '@univerjs/engine-render';
import { ILayoutService } from '@univerjs/ui';
import { getEditorObject } from '../../basics/editor/get-editor-object';
import { SHEET_FOOTER_BAR_HEIGHT } from '../../views/sheet-container/SheetContainer';
@@ -36,19 +36,42 @@ interface ICanvasOffset {
top: number;
}
export class SheetCellEditorResizeService extends Disposable implements IRenderModule {
export class SheetCellEditorResizeService extends Disposable {
constructor(
private readonly _context: IRenderContext<Workbook>,
@ILayoutService private readonly _layoutService: ILayoutService,
@ICellEditorManagerService private readonly _cellEditorManagerService: ICellEditorManagerService,
@IEditorBridgeService private readonly _editorBridgeService: IEditorBridgeService,
@IRenderManagerService private readonly _renderManagerService: IRenderManagerService,
@Inject(SheetSkeletonManagerService) private readonly _sheetSkeletonManagerService: SheetSkeletonManagerService,
@IUniverInstanceService private readonly _univerInstanceService: IUniverInstanceService
) {
super();
}
private get _currentRenderer() {
return getCurrentTypeOfRenderer(UniverInstanceType.UNIVER_SHEET, this._univerInstanceService, this._renderManagerService);
}
private get _editingUnitId() {
return this._editorBridgeService.getEditCellState()?.unitId ?? '';
}
private get _editingRenderer() {
return this._renderManagerService.getRenderById(this._editingUnitId);
}
private get _renderer() {
const currentUnitId = this._univerInstanceService.getCurrentUnitOfType(UniverInstanceType.UNIVER_SHEET)?.getUnitId();
return this._editingUnitId === currentUnitId ? this._editingRenderer : this._currentRenderer;
}
private get _sheetSkeletonManagerService() {
return this._renderer?.with(SheetSkeletonManagerService);
}
private get engine() {
return this._renderer?.engine;
}
// eslint-disable-next-line complexity
fitTextSize(callback?: () => void) {
const param = this._editorBridgeService.getEditCellState();
@@ -73,7 +96,7 @@ export class SheetCellEditorResizeService extends Disposable implements IRenderM
scaleX,
scaleY
);
if (!info) return;
if (!info || info.actualWidth <= 0) return;
let { actualWidth, actualHeight } = info;
const { verticalAlign, horizontalAlign, paddingData, fill } = documentLayoutObject;
actualWidth = actualWidth + (paddingData.l ?? 0) + (paddingData.r ?? 0);
@@ -170,7 +193,6 @@ export class SheetCellEditorResizeService extends Disposable implements IRenderM
documentSkeleton.calculate();
const size = documentSkeleton.getActualSize();
let editorWidth = endX - startX;
if (editorWidth < size.actualWidth * scaleX + EDITOR_INPUT_SELF_EXTEND_GAP * scaleX) {
@@ -203,7 +225,8 @@ export class SheetCellEditorResizeService extends Disposable implements IRenderM
return Number.parseInt(width.replace('px', ''));
}
const engine = this._context.engine;
const engine = this.engine;
if (!engine) return;
const canvasElement = engine.getCanvasElement();
const canvasClientRect = canvasElement.getBoundingClientRect();
@@ -212,7 +235,7 @@ export class SheetCellEditorResizeService extends Disposable implements IRenderM
const { width } = canvasClientRect; // real width affected by scale
const scaleAdjust = width / widthOfCanvas;
const { startX, startY, endX } = position;
const enginWidth = this._context.engine.width;
const enginWidth = engine.width;
const clientHeight =
document.body.clientHeight -
@@ -260,7 +283,8 @@ export class SheetCellEditorResizeService extends Disposable implements IRenderM
return;
}
const engine = this._context.engine;
const engine = this.engine;
if (!engine) return;
const canvasElement = engine.getCanvasElement();
// We should take the scale into account when canvas is scaled by CSS.
@@ -394,7 +418,7 @@ export class SheetCellEditorResizeService extends Disposable implements IRenderM
const editCellState = this._editorBridgeService.getEditCellState();
if (!editCellState) return;
const skeleton = this._sheetSkeletonManagerService.getSkeletonParam(editCellState.sheetId)?.skeleton;
const skeleton = this._sheetSkeletonManagerService?.getSkeletonParam(editCellState.sheetId)?.skeleton;
if (!skeleton) return;
const { row, column, scaleX, scaleY, position, canvasOffset, documentLayoutObject } = editCellState;
const { horizontalAlign } = documentLayoutObject;
@@ -201,12 +201,12 @@ export const DefinedNameInput = (props: IDefinedNameInputProps) => {
setTypeValue(type);
};
const formulaEditorActionsRef = useRef<any>({});
const formulaEditorRef = useRef<any>(null);
const [isFocusFormulaEditor, isFocusFormulaEditorSet] = useState(false);
useSidebarClick((e: MouseEvent) => {
const handleOutClick = formulaEditorActionsRef.current?.handleOutClick;
handleOutClick && handleOutClick(e, () => isFocusFormulaEditorSet(false));
const isOutSide = formulaEditorRef.current?.isClickOutSide(e);
isOutSide && isFocusFormulaEditorSet(false);
});
return (
@@ -249,7 +249,7 @@ export const DefinedNameInput = (props: IDefinedNameInputProps) => {
}}
onFocus={() => isFocusFormulaEditorSet(true)}
actions={formulaEditorActionsRef.current}
ref={formulaEditorRef}
/>
))}
<div>
@@ -104,14 +104,12 @@ export const EditorContainer: React.FC<ICellIEditorProps> = () => {
return () => {
sub.unsubscribe();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []); // Empty dependency array means this effect runs once on mount and clean up on unmount
useEffect(() => {
if (!disableAutoFocus) {
cellEditorManagerService.setFocus(true);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [disableAutoFocus, state]);
const handleClickSideBar = useEvent(() => {
@@ -27,7 +27,6 @@ import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from 're
import { EMPTY, merge, of, switchMap } from 'rxjs';
import { SetCellEditVisibleOperation } from '../../commands/operations/cell-edit.operation';
import { EMBEDDING_FORMULA_EDITOR_COMPONENT_KEY } from '../../common/keys';
import { useActiveWorkbook } from '../../components/hook';
import { SheetsUIPart } from '../../consts/ui-name';
import { IEditorBridgeService } from '../../services/editor-bridge.service';
import { IFormulaEditorManagerService } from '../../services/editor/formula-editor-manager.service';
@@ -47,10 +46,8 @@ interface IProps {
export function FormulaBar(props: IProps) {
const { className, disableDefinedName } = props;
const [iconStyle, setIconStyle] = useState<string>(styles.formulaGrey);
const [arrowDirection, setArrowDirection] = useState<ArrowDirection>(ArrowDirection.Down);
const formulaEditorManagerService = useDependency(IFormulaEditorManagerService);
const editorBridgeService = useDependency(IEditorBridgeService);
const worksheetProtectionRuleModel = useDependency(WorksheetProtectionRuleModel);
@@ -65,7 +62,6 @@ export function FormulaBar(props: IProps) {
viewDisable: false,
});
const [imageDisable, setImageDisable] = useState<boolean>(false);
const currentWorkbook = useActiveWorkbook();
const componentManager = useDependency(ComponentManager);
const workbook = useObservable(() => univerInstanceService.getCurrentTypeOfUnit$<Workbook>(UniverInstanceType.UNIVER_SHEET), undefined, undefined, [])!;
const isRefSelecting = useRef<0 | 1 | 2>(0);
@@ -194,7 +190,7 @@ export function FormulaBar(props: IProps) {
visible: false,
eventType: DeviceInputEventType.Keyboard,
keycode: KeyCode.ESC,
unitId: currentWorkbook?.getUnitId() ?? '',
unitId: editState!.unitId,
});
}
}
@@ -206,7 +202,7 @@ export function FormulaBar(props: IProps) {
commandService.executeCommand(SetCellEditVisibleOperation.id, {
visible: false,
eventType: DeviceInputEventType.PointerDown,
unitId: currentWorkbook?.getUnitId() ?? '',
unitId: editState!.unitId,
});
}
}
@@ -230,7 +226,7 @@ export function FormulaBar(props: IProps) {
{
visible: true,
eventType: DeviceInputEventType.PointerDown,
unitId: currentWorkbook?.getUnitId() ?? '',
unitId: editState!.unitId,
} as IEditorBridgeServiceVisibleParam
);
// undoRedoService.clearUndoRedo(DOCS_FORMULA_BAR_EDITOR_UNIT_ID_KEY);