diff --git a/packages/sheets/src/basics/interfaces/mutation-interface.ts b/packages/sheets/src/basics/interfaces/mutation-interface.ts index 956f971d63b..c848801c181 100644 --- a/packages/sheets/src/basics/interfaces/mutation-interface.ts +++ b/packages/sheets/src/basics/interfaces/mutation-interface.ts @@ -100,3 +100,10 @@ export interface IAddWorksheetMergeMutationParams { subUnitId: string; ranges: IRange[]; } +/** Params of AddWorksheetMergeMutation */ +export interface IWorksheetRangeThemeStyleMutationParams { + unitId: string; + subUnitId: string; + range: IRange; + themeName: string; +} diff --git a/packages/sheets/src/commands/commands/add-worksheet-range-theme.command.ts b/packages/sheets/src/commands/commands/add-worksheet-range-theme.command.ts new file mode 100644 index 00000000000..512ae823754 --- /dev/null +++ b/packages/sheets/src/commands/commands/add-worksheet-range-theme.command.ts @@ -0,0 +1,69 @@ +/** + * 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. + */ + +/** + * 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 { IAccessor, ICommand } from '@univerjs/core'; + +import type { IWorksheetRangeThemeStyleMutationParams } from '../../basics/interfaces/mutation-interface'; +import { + CommandType, + ICommandService, + IUndoRedoService, +} from '@univerjs/core'; +import { SetWorksheetRangeThemeStyleMutation, SetWorksheetRangeThemeStyleMutationFactory } from '../mutations/add-worksheet-range-theme.mutation'; +import { DeleteWorksheetRangeThemeStyleMutation } from '../mutations/delete-worksheet-range-theme.mutation'; + +export const SetWorksheetRangeThemeStyleCommand: ICommand = { + type: CommandType.COMMAND, + id: 'sheet.command.set-worksheet-range-theme-style', + handler: (accessor: IAccessor, params: IWorksheetRangeThemeStyleMutationParams) => { + const commandService = accessor.get(ICommandService); + const undoRedoService = accessor.get(IUndoRedoService); + + const { unitId } = params; + + const undoMutationParams = SetWorksheetRangeThemeStyleMutationFactory(accessor, params); + + const result = commandService.syncExecuteCommand(SetWorksheetRangeThemeStyleMutation.id, params); + + if (result) { + undoRedoService.pushUndoRedo({ + unitID: unitId, + undoMutations: [{ id: DeleteWorksheetRangeThemeStyleMutation.id, params: undoMutationParams }], + redoMutations: [{ id: SetWorksheetRangeThemeStyleMutation.id, params }], + }); + + return true; + } + + return false; + }, +}; diff --git a/packages/sheets/src/commands/commands/delete-worksheet-range-theme.command.ts b/packages/sheets/src/commands/commands/delete-worksheet-range-theme.command.ts new file mode 100644 index 00000000000..8a3db3e7f40 --- /dev/null +++ b/packages/sheets/src/commands/commands/delete-worksheet-range-theme.command.ts @@ -0,0 +1,69 @@ +/** + * 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. + */ + +/** + * 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 { IAccessor, ICommand } from '@univerjs/core'; + +import type { IWorksheetRangeThemeStyleMutationParams } from '../../basics/interfaces/mutation-interface'; +import { + CommandType, + ICommandService, + IUndoRedoService, +} from '@univerjs/core'; +import { SetWorksheetRangeThemeStyleMutation } from '../mutations/add-worksheet-range-theme.mutation'; +import { DeleteWorksheetRangeThemeStyleMutation, DeleteWorksheetRangeThemeStyleMutationFactory } from '../mutations/delete-worksheet-range-theme.mutation'; + +export const DeleteWorksheetRangeThemeStyleCommand: ICommand = { + type: CommandType.COMMAND, + id: 'sheet.command.remove-worksheet-range-theme-style', + handler: (accessor: IAccessor, params: IWorksheetRangeThemeStyleMutationParams) => { + const commandService = accessor.get(ICommandService); + const undoRedoService = accessor.get(IUndoRedoService); + + const { unitId } = params; + + const undoMutationParams = DeleteWorksheetRangeThemeStyleMutationFactory(accessor, params); + + const result = commandService.syncExecuteCommand(DeleteWorksheetRangeThemeStyleMutation.id, params); + + if (result) { + undoRedoService.pushUndoRedo({ + unitID: unitId, + undoMutations: [{ id: SetWorksheetRangeThemeStyleMutation.id, params: undoMutationParams }], + redoMutations: [{ id: DeleteWorksheetRangeThemeStyleMutation.id, params }], + }); + + return true; + } + + return false; + }, +}; diff --git a/packages/sheets/src/commands/mutations/add-worksheet-range-theme.mutation.ts b/packages/sheets/src/commands/mutations/add-worksheet-range-theme.mutation.ts new file mode 100644 index 00000000000..9f9869a0f44 --- /dev/null +++ b/packages/sheets/src/commands/mutations/add-worksheet-range-theme.mutation.ts @@ -0,0 +1,55 @@ +/** + * 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 { IAccessor, IMutation } from '@univerjs/core'; +import type { IWorksheetRangeThemeStyleMutationParams } from '../../basics/interfaces/mutation-interface'; +import { CommandType, IUniverInstanceService } from '@univerjs/core'; +import { SheetRangeThemeModel } from '../../model/range-theme-model'; +import { getSheetCommandTarget, getSheetMutationTarget } from '../commands/utils/target-util'; + +export const SetWorksheetRangeThemeStyleMutation: IMutation = { + id: 'sheet.mutation.set-worksheet-range-theme-style', + type: CommandType.MUTATION, + handler: (accessor, params) => { + const { unitId, subUnitId, range, themeName } = params; + const univerInstanceService = accessor.get(IUniverInstanceService); + const target = getSheetCommandTarget(univerInstanceService); + const sheetRangeThemeModel = accessor.get(SheetRangeThemeModel); + if (!target) return false; + + const { worksheet } = target; + if (!worksheet) { + return false; + } + sheetRangeThemeModel.registerRangeThemeRule(themeName, { range, unitId, subUnitId }); + return true; + }, +}; + +export const SetWorksheetRangeThemeStyleMutationFactory = (accessor: IAccessor, params: IWorksheetRangeThemeStyleMutationParams) => { + const target = getSheetMutationTarget(accessor.get(IUniverInstanceService), params); + if (!target) { + throw new Error('[SetWorksheetDefaultStyleMutationFactory]: worksheet is null error!'); + } + + const { worksheet } = target; + return { + unitId: params.unitId, + subUnitId: worksheet.getSheetId(), + range: params.range, + themeName: params.themeName, + }; +}; diff --git a/packages/sheets/src/commands/mutations/delete-worksheet-range-theme.mutation.ts b/packages/sheets/src/commands/mutations/delete-worksheet-range-theme.mutation.ts new file mode 100644 index 00000000000..73a80fbcc6d --- /dev/null +++ b/packages/sheets/src/commands/mutations/delete-worksheet-range-theme.mutation.ts @@ -0,0 +1,55 @@ +/** + * 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 { IAccessor, IMutation } from '@univerjs/core'; +import type { IWorksheetRangeThemeStyleMutationParams } from '../../basics/interfaces/mutation-interface'; +import { CommandType, IUniverInstanceService } from '@univerjs/core'; +import { SheetRangeThemeModel } from '../../model/range-theme-model'; +import { getSheetCommandTarget, getSheetMutationTarget } from '../commands/utils/target-util'; + +export const DeleteWorksheetRangeThemeStyleMutation: IMutation = { + id: 'sheet.mutation.remove-worksheet-range-theme-style', + type: CommandType.MUTATION, + handler: (accessor, params) => { + const { unitId, subUnitId, range, themeName } = params; + const univerInstanceService = accessor.get(IUniverInstanceService); + const target = getSheetCommandTarget(univerInstanceService); + const sheetRangeThemeModel = accessor.get(SheetRangeThemeModel); + if (!target) return false; + + const { worksheet } = target; + if (!worksheet) { + return false; + } + sheetRangeThemeModel.removeRangeThemeRule(themeName, { range, unitId, subUnitId }); + return true; + }, +}; + +export const DeleteWorksheetRangeThemeStyleMutationFactory = (accessor: IAccessor, params: IWorksheetRangeThemeStyleMutationParams) => { + const target = getSheetMutationTarget(accessor.get(IUniverInstanceService), params); + if (!target) { + throw new Error('[SetWorksheetDefaultStyleMutationFactory]: worksheet is null error!'); + } + + const { worksheet } = target; + return { + unitId: params.unitId, + subUnitId: worksheet.getSheetId(), + range: params.range, + themeName: params.themeName, + }; +}; diff --git a/packages/sheets/src/controllers/basic-worksheet.controller.ts b/packages/sheets/src/controllers/basic-worksheet.controller.ts index c4b37a7053b..c674e96c892 100644 --- a/packages/sheets/src/controllers/basic-worksheet.controller.ts +++ b/packages/sheets/src/controllers/basic-worksheet.controller.ts @@ -20,6 +20,7 @@ import { DataSyncPrimaryController } from '@univerjs/rpc'; import { AddRangeProtectionCommand } from '../commands/commands/add-range-protection.command'; import { AddWorksheetProtectionCommand } from '../commands/commands/add-worksheet-protection.command'; +import { SetWorksheetRangeThemeStyleCommand } from '../commands/commands/add-worksheet-range-theme.command'; import { ClearSelectionAllCommand } from '../commands/commands/clear-selection-all.command'; import { ClearSelectionContentCommand } from '../commands/commands/clear-selection-content.command'; import { ClearSelectionFormatCommand } from '../commands/commands/clear-selection-format.command'; @@ -28,6 +29,7 @@ import { DeleteRangeMoveLeftCommand } from '../commands/commands/delete-range-mo import { DeleteRangeMoveUpCommand } from '../commands/commands/delete-range-move-up.command'; import { DeleteRangeProtectionCommand } from '../commands/commands/delete-range-protection.command'; import { DeleteWorksheetProtectionCommand } from '../commands/commands/delete-worksheet-protection.command'; +import { DeleteWorksheetRangeThemeStyleCommand } from '../commands/commands/delete-worksheet-range-theme.command'; import { InsertDefinedNameCommand } from '../commands/commands/insert-defined-name.command'; import { InsertRangeMoveDownCommand } from '../commands/commands/insert-range-move-down.command'; import { InsertRangeMoveRightCommand } from '../commands/commands/insert-range-move-right.command'; @@ -104,20 +106,22 @@ import { ToggleGridlinesCommand } from '../commands/commands/toggle-gridlines.co import { AddRangeProtectionMutation } from '../commands/mutations/add-range-protection.mutation'; import { AddWorksheetMergeMutation } from '../commands/mutations/add-worksheet-merge.mutation'; import { AddWorksheetProtectionMutation } from '../commands/mutations/add-worksheet-protection.mutation'; +import { SetWorksheetRangeThemeStyleMutation } from '../commands/mutations/add-worksheet-range-theme.mutation'; import { DeleteRangeProtectionMutation } from '../commands/mutations/delete-range-protection.mutation'; import { DeleteWorksheetProtectionMutation } from '../commands/mutations/delete-worksheet-protection.mutation'; +import { DeleteWorksheetRangeThemeStyleMutation } from '../commands/mutations/delete-worksheet-range-theme.mutation'; import { EmptyMutation } from '../commands/mutations/empty.mutation'; import { InsertColMutation, InsertRowMutation } from '../commands/mutations/insert-row-col.mutation'; import { InsertSheetMutation } from '../commands/mutations/insert-sheet.mutation'; + import { MoveRangeMutation } from '../commands/mutations/move-range.mutation'; import { MoveColsMutation, MoveRowsMutation } from '../commands/mutations/move-rows-cols.mutation'; import { RemoveNumfmtMutation, SetNumfmtMutation } from '../commands/mutations/numfmt-mutation'; -import { RemoveColMutation, RemoveRowMutation } from '../commands/mutations/remove-row-col.mutation'; +import { RemoveColMutation, RemoveRowMutation } from '../commands/mutations/remove-row-col.mutation'; import { RemoveSheetMutation } from '../commands/mutations/remove-sheet.mutation'; import { RemoveWorksheetMergeMutation } from '../commands/mutations/remove-worksheet-merge.mutation'; import { ReorderRangeMutation } from '../commands/mutations/reorder-range.mutation'; - import { SetColDataMutation } from '../commands/mutations/set-col-data.mutation'; import { SetColHiddenMutation, SetColVisibleMutation } from '../commands/mutations/set-col-visible.mutation'; import { SetFrozenMutation } from '../commands/mutations/set-frozen.mutation'; @@ -303,6 +307,12 @@ export class BasicWorksheetController extends Disposable implements IDisposable SetWorksheetDefaultStyleCommand, SplitTextToColumnsCommand, + + SetWorksheetRangeThemeStyleCommand, + DeleteWorksheetRangeThemeStyleCommand, + DeleteWorksheetRangeThemeStyleMutation, + SetWorksheetRangeThemeStyleMutation, + ].forEach((command) => this.disposeWithMe(this._commandService.registerCommand(command))); } diff --git a/packages/sheets/src/facade/f-range.ts b/packages/sheets/src/facade/f-range.ts index 7d98c72947e..96ba880832c 100644 --- a/packages/sheets/src/facade/f-range.ts +++ b/packages/sheets/src/facade/f-range.ts @@ -19,7 +19,7 @@ import type { ISetHorizontalTextAlignCommandParams, ISetStyleCommandParams, ISet import type { FHorizontalAlignment, FVerticalAlignment } from './utils'; import { BooleanNumber, Dimension, FBase, ICommandService, Inject, Injector, Rectangle, WrapStrategy } from '@univerjs/core'; import { FormulaDataModel, serializeRange, serializeRangeWithSheet } from '@univerjs/engine-formula'; -import { addMergeCellsUtil, getAddMergeMutationRangeByType, RemoveWorksheetMergeCommand, SetHorizontalTextAlignCommand, SetRangeValuesCommand, SetStyleCommand, SetTextWrapCommand, SetVerticalTextAlignCommand, SheetRangeThemeService, SplitTextToColumnsCommand } from '@univerjs/sheets'; +import { addMergeCellsUtil, DeleteWorksheetRangeThemeStyleCommand, getAddMergeMutationRangeByType, RemoveWorksheetMergeCommand, SetHorizontalTextAlignCommand, SetRangeValuesCommand, SetStyleCommand, SetTextWrapCommand, SetVerticalTextAlignCommand, SetWorksheetRangeThemeStyleCommand, SheetRangeThemeService, SplitTextToColumnsCommand } from '@univerjs/sheets'; import { FWorkbook } from './f-workbook'; import { covertCellValue, covertCellValues, transformCoreHorizontalAlignment, transformCoreVerticalAlignment, transformFacadeHorizontalAlignment, transformFacadeVerticalAlignment } from './utils'; @@ -760,12 +760,64 @@ export class FRange extends FBase { }); } + /** + * Set the theme style for the range. + * @param {string} themeName The name of the theme style to apply. + * @example + * ```ts + * const fWorkbook = univerAPI.getActiveWorkbook(); + * const fWorksheet = fWorkbook.getActiveSheet(); + * const fRange = fWorksheet.getRange('A1:E20'); + * fRange.useThemeStyle('default'); + * ``` + */ useThemeStyle(themeName: string): void { - const rangeInfo = { + this._commandService.executeCommand(SetWorksheetRangeThemeStyleCommand.id, { + unitId: this._workbook.getUnitId(), + subUnitId: this._worksheet.getSheetId(), + range: this._range, + themeName, + }); + } + + /** + * Remove the theme style for the range. + * @param {string} themeName The name of the theme style to remove. + * @example + * ```ts + * const fWorkbook = univerAPI.getActiveWorkbook(); + * const fWorksheet = fWorkbook.getActiveSheet(); + * const fRange = fWorksheet.getRange('A1:E20'); + * fRange.removeThemeStyle('default'); + * ``` + */ + removeThemeStyle(themeName: string): void { + this._commandService.executeCommand(DeleteWorksheetRangeThemeStyleCommand.id, { + unitId: this._workbook.getUnitId(), + subUnitId: this._worksheet.getSheetId(), range: this._range, - unitId: this.getUnitId(), + themeName, + }); + } + + /** + * Gets the theme style applied to the range. + * @returns {string | undefined} The name of the theme style applied to the range or not exist. + * @example + * ```ts + * const fWorkbook = univerAPI.getActiveWorkbook(); + * const fWorksheet = fWorkbook.getActiveSheet(); + * const fRange = fWorksheet.getRange('A1:E20'); + * fRange.useThemeStyle('default'); + * const themeStyle = fRange.getUsedThemeStyle(); + * console.log(themeStyle); // 'default' + * ``` + */ + getUsedThemeStyle(): string | undefined { + return this._injector.get(SheetRangeThemeService).getRegisteredRangeThemeStyle({ + unitId: this._workbook.getUnitId(), subUnitId: this._worksheet.getSheetId(), - }; - this._injector.get(SheetRangeThemeService).registerRangeThemeStyle(themeName, rangeInfo); + range: this._range, + }); } } diff --git a/packages/sheets/src/index.ts b/packages/sheets/src/index.ts index 67a32201462..9607e2fa4ed 100644 --- a/packages/sheets/src/index.ts +++ b/packages/sheets/src/index.ts @@ -29,6 +29,7 @@ export { type IRemoveRowsMutationParams, type IRemoveSheetMutationParams, type IRemoveWorksheetMergeMutationParams, + type IWorksheetRangeThemeStyleMutationParams, } from './basics/interfaces/mutation-interface'; export { convertPrimaryWithCoordToPrimary, @@ -191,6 +192,8 @@ export { AddWorksheetMergeHorizontalCommand, AddWorksheetMergeVerticalCommand, } from './commands/commands/add-worksheet-merge.command'; +export { SetWorksheetRangeThemeStyleCommand } from './commands/commands/add-worksheet-range-theme.command'; +export { DeleteWorksheetRangeThemeStyleCommand } from './commands/commands/delete-worksheet-range-theme.command'; export { ClearSelectionAllCommand } from './commands/commands/clear-selection-all.command'; export { ClearSelectionContentCommand } from './commands/commands/clear-selection-content.command'; export { ClearSelectionFormatCommand } from './commands/commands/clear-selection-format.command'; @@ -395,6 +398,8 @@ export { export { type ISetWorksheetHideMutationParams, SetWorksheetHideMutation } from './commands/mutations/set-worksheet-hide.mutation'; export { type ISetWorksheetNameMutationParams, SetWorksheetNameMutation } from './commands/mutations/set-worksheet-name.mutation'; export { type ISetWorksheetOrderMutationParams, SetWorksheetOrderMutation } from './commands/mutations/set-worksheet-order.mutation'; +export { SetWorksheetRangeThemeStyleMutation, SetWorksheetRangeThemeStyleMutationFactory } from './commands/mutations/add-worksheet-range-theme.mutation'; +export { DeleteWorksheetRangeThemeStyleMutation, DeleteWorksheetRangeThemeStyleMutationFactory } from './commands/mutations/delete-worksheet-range-theme.mutation'; export { SetWorksheetPermissionPointsMutation } from './commands/mutations/set-worksheet-permission-points.mutation'; export type { ISetWorksheetPermissionPointsMutationParams } from './commands/mutations/set-worksheet-permission-points.mutation'; export { SetWorksheetProtectionMutation } from './commands/mutations/set-worksheet-protection.mutation'; diff --git a/packages/sheets/src/model/range-theme-model.ts b/packages/sheets/src/model/range-theme-model.ts index d37630b10a0..da82e08f0ad 100644 --- a/packages/sheets/src/model/range-theme-model.ts +++ b/packages/sheets/src/model/range-theme-model.ts @@ -15,7 +15,7 @@ */ import type { ICellDataForSheetInterceptor, IRange, Nullable } from '@univerjs/core'; -import type { IRangeThemeStyleItem } from './range-theme-util'; +import type { IRangeThemeStyleItem, IRangeThemeStyleJSON } from './range-theme-util'; import { Disposable, generateRandomId, Inject, InterceptorEffectEnum, IResourceManagerService, RTree, UniverInstanceType } from '@univerjs/core'; import { INTERCEPTOR_POINT } from '../services/sheet-interceptor/interceptor-const'; import { SheetInterceptorService } from '../services/sheet-interceptor/sheet-interceptor.service'; @@ -85,6 +85,34 @@ export class SheetRangeThemeModel extends Disposable { rTreeCollection.insert({ unitId, sheetId: subUnitId, range, id }); } + getRegisteredRangeThemeStyle(rangeInfo: IRangeThemeRangeInfo): string | undefined { + const { unitId, subUnitId, range } = rangeInfo; + const rTreeCollection = this._ensureRTreeCollection(unitId); + const themes = Array.from(rTreeCollection.bulkSearch([{ unitId, sheetId: subUnitId, range }])); + if (themes[0]) { + const themeRuleMap = this._ensureRangeThemeStyleRuleMap(unitId); + const themeRule = themeRuleMap.get(themes[0] as string); + if (themeRule) { + return themeRule.themeName; + } + } + return undefined; + } + + removeRangeThemeRule(themeName: string, rangeInfo: IRangeThemeRangeInfo): void { + const { unitId, subUnitId, range } = rangeInfo; + const rTreeCollection = this._ensureRTreeCollection(unitId); + const themes = Array.from(rTreeCollection.bulkSearch([{ unitId, sheetId: subUnitId, range }])); + if (themes[0]) { + const themeRuleMap = this._ensureRangeThemeStyleRuleMap(unitId); + const themeRule = themeRuleMap.get(themes[0] as string); + if (themeRule && themeRule.themeName === themeName) { + themeRuleMap.delete(themes[0] as string); + rTreeCollection.remove({ unitId, sheetId: subUnitId, range, id: themes[0] as string }); + } + } + } + registerDefaultRangeTheme(rangeThemeStyle: RangeThemeStyle): void { this._defaultRangeThemeMap.set(rangeThemeStyle.getName(), rangeThemeStyle); } @@ -116,7 +144,7 @@ export class SheetRangeThemeModel extends Disposable { const offsetRow = row - rangeInfo.range.startRow; const offsetCol = col - rangeInfo.range.startColumn; const theme = this.getRangeThemeStyle(unitId, themeName); - return theme.getStyle(offsetRow, offsetCol); + return theme.getStyle(offsetRow, offsetCol, row === rangeInfo.range.endRow, col === rangeInfo.range.endColumn); } } return undefined; @@ -141,16 +169,18 @@ export class SheetRangeThemeModel extends Disposable { } toJson(unitId: string) { + // deserialize registered range theme style rule const ruleMap = this._ensureRangeThemeStyleRuleMap(unitId); const rangeThemeStyleRuleMap: Record = {}; ruleMap.forEach((value, key) => { rangeThemeStyleRuleMap[key] = value; }); + // deserialize custom range theme style const rangeThemeStyleMap = this._ensureRangeThemeStyleMap(unitId); - const rangeThemeStyleMapJson: Record> = {}; + const rangeThemeStyleMapJson: Record = {}; rangeThemeStyleMap.forEach((value, key) => { - rangeThemeStyleMapJson[key] = value.toJSON(); + rangeThemeStyleMapJson[key] = value.toJson(); }); return JSON.stringify({ @@ -176,7 +206,7 @@ export class SheetRangeThemeModel extends Disposable { Object.keys(rangeThemeStyleMapJson).forEach((key) => { const styleMap = rangeThemeStyleMapJson[key]; const style = new RangeThemeStyle(styleMap.name); - style.fromJSON(styleMap); + style.fromJson(styleMap); this._ensureRangeThemeStyleMap(key).set(style.getName(), style); }); } diff --git a/packages/sheets/src/model/range-theme-util.ts b/packages/sheets/src/model/range-theme-util.ts index 4d989201c4f..bccfa4715aa 100644 --- a/packages/sheets/src/model/range-theme-util.ts +++ b/packages/sheets/src/model/range-theme-util.ts @@ -18,24 +18,118 @@ import type { IStyleData, Nullable } from '@univerjs/core'; export type IRangeThemeStyleItem = Pick; +export interface IRangeThemeStyleJSON { + name: string; + wholeStyle?: IRangeThemeStyleItem; + headerRowStyle?: IRangeThemeStyleItem; + headerColumnStyle?: IRangeThemeStyleItem; + firstRowStyle?: IRangeThemeStyleItem; + secondRowStyle?: IRangeThemeStyleItem; + lastRowStyle?: IRangeThemeStyleItem; + firstColumnStyle?: IRangeThemeStyleItem; + secondColumnStyle?: IRangeThemeStyleItem; + lastColumnStyle?: IRangeThemeStyleItem; +} + +const serializeRangeStyle = (style: IRangeThemeStyleItem) => { + const result = {} as IRangeThemeStyleItem; + if (style.bg) { + result.bg = { ...style.bg }; + } + if (style.ol) { + result.ol = { ...style.ol }; + } + if (style.bd) { + result.bd = { ...style.bd }; + } + if (style.cl) { + result.cl = { ...style.cl }; + } + return result; +}; + +function composeStyles(styles: IStyleData[]): IRangeThemeStyleItem { + const composedStyle: IRangeThemeStyleItem = {}; + + for (const style of styles) { + if (style.bg) { + composedStyle.bg = style.bg; + } + if (style.ol) { + composedStyle.ol = style.ol; + } + if (style.bd) { + composedStyle.bd = { ...composedStyle.bd, ...style.bd }; + } + if (style.cl) { + composedStyle.cl = style.cl; + } + } + + return composedStyle; +} + +const STYLE_MAP = { + wholeStyle: 1, + headerRowStyle: 2, + headerColumnStyle: 4, + firstRowStyle: 8, + secondRowStyle: 16, + lastRowStyle: 32, + firstColumnStyle: 128, + secondColumnStyle: 256, + lastColumnStyle: 512, +}; + /** * Range theme style * @description The range theme style is used to set the style of the range. */ export class RangeThemeStyle { private readonly _name: string; + /** + * @property {Nullable} wholeStyle effect for the whole range. + */ wholeStyle: Nullable = null; + /** + * @property {Nullable} headerRowStyle effect for the header row. + */ headerRowStyle: Nullable = null; + /** + * @property {Nullable} headerColumnStyle effect for the header column. + */ headerColumnStyle: Nullable = null; + /** + * @property {Nullable} firstRowStyle effect for the first row. + */ firstRowStyle: Nullable = null; + /** + * @property {Nullable} secondRowStyle effect for the second row. + */ secondRowStyle: Nullable = null; + /** + * @property {Nullable} lastRowStyle effect for the last row. + */ lastRowStyle: Nullable = null; - + /** + * @property {Nullable} effect for the first column. + */ firstColumnStyle: Nullable = null; + /** + * @property {Nullable} effect for the second column. + */ secondColumnStyle: Nullable = null; + /** + * @property {Nullable} effect for the last column. + */ lastColumnStyle: Nullable = null; + + /** + * @property {Nullable} quickly get merge style + */ + private _mergeCacheMap: Map = new Map(); /** * @constructor * @param {string} name The name of the range theme style, it used to identify the range theme style. @@ -44,6 +138,10 @@ export class RangeThemeStyle { this._name = name; } + /** + * Gets the name of the range theme style.The name is read only, and use to identifier the range theme style. + * @returns {string} The name of the range theme style. + */ getName(): string { return this._name; } @@ -120,49 +218,177 @@ export class RangeThemeStyle { this.headerColumnStyle = style; } - public getStyle(offsetRow: number, offsetCol: number) { - if (offsetCol === 0) { - return this.headerColumnStyle; + public getStyle(offsetRow: number, offsetCol: number, isLastRow: boolean, isLastCol: boolean) { + let mergeNumber = 0; + + if (isLastRow) { + mergeNumber = mergeNumber | STYLE_MAP.lastRowStyle; } - if (offsetRow === 0) { - return this.headerRowStyle; + + if (isLastCol) { + mergeNumber = mergeNumber | STYLE_MAP.lastColumnStyle; } - if (offsetRow % 2 === 0) { - return this.firstRowStyle; + + if (offsetRow >= 0 && offsetCol >= 0) { + mergeNumber = mergeNumber | STYLE_MAP.wholeStyle; } + if (offsetRow % 2 === 1) { - return this.secondRowStyle; + mergeNumber = mergeNumber | STYLE_MAP.firstRowStyle; + } + + if (offsetRow % 2 === 0 && offsetRow !== 0) { + mergeNumber = mergeNumber | STYLE_MAP.secondRowStyle; + } + + if (offsetRow === 0) { + mergeNumber = mergeNumber | STYLE_MAP.headerRowStyle; + } + + if (offsetCol === 0) { + mergeNumber = mergeNumber | STYLE_MAP.headerColumnStyle; + } + + if (offsetCol % 2 === 1) { + mergeNumber = mergeNumber | STYLE_MAP.firstColumnStyle; + } + + if (offsetCol % 2 === 0 && offsetCol !== 0) { + mergeNumber = mergeNumber | STYLE_MAP.secondColumnStyle; + } + + // it means no style should be merged + if (mergeNumber === 0) { + return null; + } + return this._getMergeStyle(mergeNumber); + } + + private _getMergeStyle(mergeNumber: number) { + let style = this._mergeCacheMap.get(mergeNumber); + if (!style) { + style = this._mergeStyle(mergeNumber); + this._mergeCacheMap.set(mergeNumber, style); + } + return style; + } + + private _mergeStyle(mergeNumber: number): IRangeThemeStyleItem { + const rs: IRangeThemeStyleItem[] = []; + if (this.wholeStyle && (mergeNumber & STYLE_MAP.wholeStyle)) { + rs.push(this.wholeStyle); + } + if (this.headerRowStyle && (mergeNumber & STYLE_MAP.headerRowStyle)) { + rs.push(this.headerRowStyle); + } + if (this.headerColumnStyle && (mergeNumber & STYLE_MAP.headerColumnStyle) && !(mergeNumber & STYLE_MAP.headerRowStyle)) { + rs.push(this.headerColumnStyle); + } + if (this.firstRowStyle && (mergeNumber & STYLE_MAP.firstRowStyle)) { + rs.push(this.firstRowStyle); } - if (this.wholeStyle && offsetRow >= 0 && offsetCol >= 0) { - return this.wholeStyle; + if (this.secondRowStyle && (mergeNumber & STYLE_MAP.secondRowStyle)) { + rs.push(this.secondRowStyle); } + if (this.lastRowStyle && (mergeNumber & STYLE_MAP.lastRowStyle)) { + rs.push(this.lastRowStyle); + } + if (this.firstColumnStyle && (mergeNumber & STYLE_MAP.firstColumnStyle)) { + rs.push(this.firstColumnStyle); + } + if (this.secondColumnStyle && (mergeNumber & STYLE_MAP.secondColumnStyle)) { + rs.push(this.secondColumnStyle); + } + if (this.lastColumnStyle && (mergeNumber & STYLE_MAP.lastColumnStyle)) { + rs.push(this.lastColumnStyle); + } + + return composeStyles(rs as IStyleData[]); } - toJSON(): Record { - return { + toJson(): IRangeThemeStyleJSON { + const jsonData: IRangeThemeStyleJSON = { name: this._name, - wholeStyle: JSON.stringify(this.wholeStyle), - headerRowStyle: JSON.stringify(this.headerRowStyle), - headerColumnStyle: JSON.stringify(this.headerColumnStyle), - firstRowStyle: JSON.stringify(this.firstRowStyle), - secondRowStyle: JSON.stringify(this.secondRowStyle), - lastRowStyle: JSON.stringify(this.lastRowStyle), - firstColumnStyle: JSON.stringify(this.firstColumnStyle), - secondColumnStyle: JSON.stringify(this.secondColumnStyle), - lastColumnStyle: JSON.stringify(this.lastColumnStyle), }; + + if (this.wholeStyle) { + jsonData.wholeStyle = serializeRangeStyle(this.wholeStyle)!; + } + + if (this.headerRowStyle) { + jsonData.headerRowStyle = serializeRangeStyle(this.headerRowStyle)!; + } + + if (this.headerColumnStyle) { + jsonData.headerColumnStyle = serializeRangeStyle(this.headerColumnStyle)!; + } + + if (this.firstRowStyle) { + jsonData.firstRowStyle = serializeRangeStyle(this.firstRowStyle)!; + } + + if (this.secondRowStyle) { + jsonData.secondRowStyle = serializeRangeStyle(this.secondRowStyle)!; + } + + if (this.lastRowStyle) { + jsonData.lastRowStyle = serializeRangeStyle(this.lastRowStyle)!; + } + + if (this.firstColumnStyle) { + jsonData.firstColumnStyle = serializeRangeStyle(this.firstColumnStyle)!; + } + + if (this.secondColumnStyle) { + jsonData.secondColumnStyle = serializeRangeStyle(this.secondColumnStyle)!; + } + + if (this.lastColumnStyle) { + jsonData.lastColumnStyle = serializeRangeStyle(this.lastColumnStyle)!; + } + return jsonData; + } + + fromJson(json: IRangeThemeStyleJSON): void { + if (json.wholeStyle) { + this.wholeStyle = serializeRangeStyle(json.wholeStyle); + } + + if (json.headerRowStyle) { + this.headerRowStyle = serializeRangeStyle(json.headerRowStyle); + } + + if (json.headerColumnStyle) { + this.headerColumnStyle = serializeRangeStyle(json.headerColumnStyle); + } + + if (json.firstRowStyle) { + this.firstRowStyle = serializeRangeStyle(json.firstRowStyle); + } + + if (json.secondRowStyle) { + this.secondRowStyle = serializeRangeStyle(json.secondRowStyle); + } + + if (json.lastRowStyle) { + this.lastRowStyle = serializeRangeStyle(json.lastRowStyle); + } + + if (json.firstColumnStyle) { + this.firstColumnStyle = serializeRangeStyle(json.firstColumnStyle); + } + + if (json.secondColumnStyle) { + this.secondColumnStyle = serializeRangeStyle(json.secondColumnStyle); + } + + if (json.lastColumnStyle) { + this.lastColumnStyle = serializeRangeStyle(json.lastColumnStyle); + } } - fromJSON(json: Record): void { - this.wholeStyle = JSON.parse(json.wholeStyle); - this.headerRowStyle = JSON.parse(json.headerRowStyle); - this.headerColumnStyle = JSON.parse(json.headerColumnStyle); - this.firstRowStyle = JSON.parse(json.firstRowStyle); - this.secondRowStyle = JSON.parse(json.secondRowStyle); - this.lastRowStyle = JSON.parse(json.lastRowStyle); - this.firstColumnStyle = JSON.parse(json.firstColumnStyle); - this.secondColumnStyle = JSON.parse(json.secondColumnStyle); - this.lastColumnStyle = JSON.parse(json.lastColumnStyle); + dispose(): void { + this._mergeCacheMap.clear(); } } diff --git a/packages/sheets/src/services/range-theme-service.ts b/packages/sheets/src/services/range-theme-service.ts index ffd1f8764f5..ba4e1f25eec 100644 --- a/packages/sheets/src/services/range-theme-service.ts +++ b/packages/sheets/src/services/range-theme-service.ts @@ -37,4 +37,8 @@ export class SheetRangeThemeService extends Disposable { registerRangeThemeStyle(themeName: string, rangeInfo: IRangeThemeRangeInfo): void { this._sheetRangeThemeModel.registerRangeThemeRule(themeName, rangeInfo); } + + getRegisteredRangeThemeStyle(rangeInfo: IRangeThemeRangeInfo): string | undefined { + return this._sheetRangeThemeModel.getRegisteredRangeThemeStyle(rangeInfo); + } }