mirror of
https://github.com/dream-num/univer.git
synced 2026-08-29 07:13:59 +08:00
fix(docs): align canvas backgrounds with document flavors (#7491)
This commit is contained in:
@@ -17,7 +17,7 @@
|
||||
import type { ICommandInfo, IExecutionOptions } from '@univerjs/core';
|
||||
import { DOCS_NORMAL_EDITOR_UNIT_ID_KEY, DocumentFlavor } from '@univerjs/core';
|
||||
import { RichTextEditingMutation } from '@univerjs/docs';
|
||||
import { Subject } from 'rxjs';
|
||||
import { BehaviorSubject, Subject } from 'rxjs';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { DOCS_VIEW_KEY } from '../../basics/docs-view-key';
|
||||
import { DocRenderController } from '../render-controllers/doc.render-controller';
|
||||
@@ -107,7 +107,8 @@ function createControllerFixture(options?: {
|
||||
}) {
|
||||
mockScrollBarProps.length = 0;
|
||||
const commandCallbacks: Array<(command: ICommandInfo, options?: IExecutionOptions) => void> = [];
|
||||
const darkMode$ = new Subject<boolean>();
|
||||
const darkMode$ = new BehaviorSubject<boolean>(false);
|
||||
const currentTheme$ = new BehaviorSubject<unknown>({});
|
||||
const canvasElement = { style: {} as Record<string, string> };
|
||||
const canvasColorService = {
|
||||
getRenderColor: vi.fn((color: string) => color),
|
||||
@@ -214,7 +215,7 @@ function createControllerFixture(options?: {
|
||||
align: options?.fitToWidth?.align ?? 'center',
|
||||
})),
|
||||
},
|
||||
{ darkMode$ }
|
||||
{ currentTheme$, darkMode$ }
|
||||
);
|
||||
|
||||
return {
|
||||
@@ -223,6 +224,8 @@ function createControllerFixture(options?: {
|
||||
context,
|
||||
canvasElement,
|
||||
canvasColorService,
|
||||
currentTheme$,
|
||||
darkMode$,
|
||||
skeletonManager,
|
||||
pageLayoutService,
|
||||
selectionManager,
|
||||
@@ -230,6 +233,43 @@ function createControllerFixture(options?: {
|
||||
}
|
||||
|
||||
describe('doc render controller', () => {
|
||||
it.each([
|
||||
[DocumentFlavor.TRADITIONAL, 'gray.100'],
|
||||
[DocumentFlavor.MODERN, 'white'],
|
||||
])('resolves the %s workspace background again when dark mode changes', (documentFlavor, backgroundToken) => {
|
||||
const { canvasColorService, canvasElement, darkMode$ } = createControllerFixture({ documentFlavor });
|
||||
canvasColorService.getRenderColor.mockImplementation((color: string) => `dark:${color}`);
|
||||
|
||||
darkMode$.next(true);
|
||||
|
||||
expect(canvasColorService.getRenderColor).toHaveBeenLastCalledWith(backgroundToken);
|
||||
expect(canvasElement.style.backgroundColor).toBe(`dark:${backgroundToken}`);
|
||||
});
|
||||
|
||||
it('keeps the unspecified workspace background unchanged when dark mode changes', () => {
|
||||
const { canvasColorService, canvasElement, darkMode$ } = createControllerFixture({
|
||||
documentFlavor: DocumentFlavor.UNSPECIFIED,
|
||||
});
|
||||
canvasColorService.getRenderColor.mockImplementation((color: string) => `dark:${color}`);
|
||||
|
||||
darkMode$.next(true);
|
||||
|
||||
expect(canvasColorService.getRenderColor).not.toHaveBeenCalled();
|
||||
expect(canvasElement.style.backgroundColor).toBe('var(--univer-gray-100)');
|
||||
});
|
||||
|
||||
it('resolves the workspace background again when the theme changes', () => {
|
||||
const { canvasColorService, canvasElement, currentTheme$ } = createControllerFixture({
|
||||
documentFlavor: DocumentFlavor.TRADITIONAL,
|
||||
});
|
||||
canvasColorService.getRenderColor.mockImplementation((color: string) => `theme:${color}`);
|
||||
|
||||
currentTheme$.next({});
|
||||
|
||||
expect(canvasColorService.getRenderColor).toHaveBeenLastCalledWith('gray.100');
|
||||
expect(canvasElement.style.backgroundColor).toBe('theme:gray.100');
|
||||
});
|
||||
|
||||
it('disables only the horizontal scrollbar for container-fitted embedded docs', () => {
|
||||
createControllerFixture({
|
||||
fitToWidth: {
|
||||
|
||||
@@ -20,7 +20,7 @@ import type { DocumentSkeleton, IDocumentSkeletonPage, IRenderContext, IRenderMo
|
||||
import { DocumentFlavor, ICommandService, Inject, isInternalEditorID, IUniverInstanceService, RxDisposable, ThemeService, UniverInstanceType } from '@univerjs/core';
|
||||
import { DocSelectionManagerService, DocSkeletonManagerService, RichTextEditingMutation } from '@univerjs/docs';
|
||||
import { DocBackground, Documents, IRenderManagerService, Layer, PageLayoutType, ScrollBar, Viewport } from '@univerjs/engine-render';
|
||||
import { takeUntil } from 'rxjs';
|
||||
import { combineLatest, takeUntil } from 'rxjs';
|
||||
import { DOCS_COMPONENT_BACKGROUND_LAYER_INDEX, DOCS_COMPONENT_DEFAULT_Z_INDEX, DOCS_COMPONENT_HEADER_LAYER_INDEX, DOCS_COMPONENT_MAIN_LAYER_INDEX, DOCS_VIEW_KEY, VIEWPORT_KEY } from '../../basics/docs-view-key';
|
||||
import { DocPageLayoutService } from '../../services/doc-page-layout.service';
|
||||
import { resolveDocRenderBackground } from '../../services/doc-render-background';
|
||||
@@ -259,7 +259,7 @@ export class DocRenderController extends RxDisposable implements IRenderModule {
|
||||
}
|
||||
|
||||
private _initThemeListener() {
|
||||
this.disposeWithMe(this._themeService.darkMode$.pipe(takeUntil(this.dispose$)).subscribe(() => {
|
||||
this.disposeWithMe(combineLatest([this._themeService.currentTheme$, this._themeService.darkMode$]).pipe(takeUntil(this.dispose$)).subscribe(() => {
|
||||
this._syncCanvasBackground();
|
||||
this._context.mainComponent?.makeDirty(true);
|
||||
this._context.components.get(DOCS_VIEW_KEY.BACKGROUND)?.makeDirty(true);
|
||||
|
||||
@@ -162,6 +162,7 @@ describe('DocsRenderService', () => {
|
||||
expect(renderManagerService.createdUnitIds).toEqual(['doc-existing']);
|
||||
expect(renderManagerService.canvases.get('doc-existing')?.id).toBe('univer-doc-main-canvas');
|
||||
expect(renderManagerService.canvases.get('doc-existing')?.contextId).toBe('univer-doc-main-canvas');
|
||||
expect(renderManagerService.canvases.get('doc-existing')?.style.backgroundColor).toBe('var(--univer-white)');
|
||||
const editorDoc = new DocumentDataModel({
|
||||
id: DOCS_NORMAL_EDITOR_UNIT_ID_KEY,
|
||||
documentStyle: { documentFlavor: DocumentFlavor.TRADITIONAL },
|
||||
|
||||
@@ -18,8 +18,11 @@ import type { ICanvasColorService } from '@univerjs/engine-render';
|
||||
import { DocumentFlavor } from '@univerjs/core';
|
||||
|
||||
const DOC_TRADITIONAL_WORKSPACE_BACKGROUND_COLOR = 'var(--univer-gray-100)';
|
||||
const DOC_MODERN_WORKSPACE_BACKGROUND_COLOR = 'var(--univer-gray-50)';
|
||||
const DOC_MODERN_WORKSPACE_BACKGROUND_COLOR = 'var(--univer-white)';
|
||||
const DOC_UNSPECIFIED_WORKSPACE_BACKGROUND_COLOR = 'var(--univer-gray-100)';
|
||||
const DOC_EDITOR_INTERNAL_BACKGROUND_COLOR = 'transparent';
|
||||
const DOC_TRADITIONAL_WORKSPACE_BACKGROUND_TOKEN = 'gray.100';
|
||||
const DOC_MODERN_WORKSPACE_BACKGROUND_TOKEN = 'white';
|
||||
|
||||
export interface IResolveDocRenderBackgroundOptions {
|
||||
documentFlavor?: DocumentFlavor;
|
||||
@@ -44,10 +47,24 @@ export function resolveDocRenderBackground(options: IResolveDocRenderBackgroundO
|
||||
};
|
||||
}
|
||||
|
||||
if (editorBackgroundColor != null) {
|
||||
return {
|
||||
canvasElementBackgroundColor: canvasColorService?.getRenderColor(editorBackgroundColor) ?? editorBackgroundColor,
|
||||
docBackgroundFillColor: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
if (documentFlavor !== DocumentFlavor.TRADITIONAL && documentFlavor !== DocumentFlavor.MODERN) {
|
||||
return {
|
||||
canvasElementBackgroundColor: backgroundColor,
|
||||
docBackgroundFillColor: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
const renderBackgroundColor = getDefaultDocCanvasBackgroundToken(documentFlavor);
|
||||
|
||||
return {
|
||||
canvasElementBackgroundColor: editorBackgroundColor == null
|
||||
? backgroundColor
|
||||
: canvasColorService?.getRenderColor(backgroundColor) ?? backgroundColor,
|
||||
canvasElementBackgroundColor: canvasColorService?.getRenderColor(renderBackgroundColor) ?? backgroundColor,
|
||||
docBackgroundFillColor: undefined,
|
||||
};
|
||||
}
|
||||
@@ -57,7 +74,18 @@ export function getDefaultDocCanvasBackgroundColor(documentFlavor?: DocumentFlav
|
||||
return DOC_EDITOR_INTERNAL_BACKGROUND_COLOR;
|
||||
}
|
||||
|
||||
return documentFlavor === DocumentFlavor.MODERN
|
||||
? DOC_MODERN_WORKSPACE_BACKGROUND_COLOR
|
||||
: DOC_TRADITIONAL_WORKSPACE_BACKGROUND_COLOR;
|
||||
switch (documentFlavor) {
|
||||
case DocumentFlavor.MODERN:
|
||||
return DOC_MODERN_WORKSPACE_BACKGROUND_COLOR;
|
||||
case DocumentFlavor.TRADITIONAL:
|
||||
return DOC_TRADITIONAL_WORKSPACE_BACKGROUND_COLOR;
|
||||
default:
|
||||
return DOC_UNSPECIFIED_WORKSPACE_BACKGROUND_COLOR;
|
||||
}
|
||||
}
|
||||
|
||||
function getDefaultDocCanvasBackgroundToken(documentFlavor: DocumentFlavor.TRADITIONAL | DocumentFlavor.MODERN) {
|
||||
return documentFlavor === DocumentFlavor.MODERN
|
||||
? DOC_MODERN_WORKSPACE_BACKGROUND_TOKEN
|
||||
: DOC_TRADITIONAL_WORKSPACE_BACKGROUND_TOKEN;
|
||||
}
|
||||
|
||||
@@ -66,6 +66,53 @@ describe('DocBackground', () => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('draws traditional workspaces with gray 100 and pages with white', () => {
|
||||
const background = DocBackground.create('traditional-background', createSkeleton([createPage()]));
|
||||
background.resize(320, 180);
|
||||
const rectDraw = vi.spyOn(Rect, 'drawWith').mockImplementation(() => {});
|
||||
vi.spyOn(Path, 'drawWith').mockImplementation(() => {});
|
||||
|
||||
background.draw(createCtx());
|
||||
|
||||
expect(rectDraw.mock.calls[0][1]).toMatchObject({ fill: 'gray.100' });
|
||||
expect(rectDraw.mock.calls[1][1]).toMatchObject({ fill: 'white' });
|
||||
|
||||
background.dispose();
|
||||
});
|
||||
|
||||
it('draws modern workspaces with white', () => {
|
||||
const background = DocBackground.create(
|
||||
'modern-background',
|
||||
createSkeleton([createPage()], DocumentFlavor.MODERN)
|
||||
);
|
||||
background.resize(320, 180);
|
||||
const rectDraw = vi.spyOn(Rect, 'drawWith').mockImplementation(() => {});
|
||||
|
||||
background.draw(createCtx());
|
||||
|
||||
expect(rectDraw).toHaveBeenCalledTimes(1);
|
||||
expect(rectDraw.mock.calls[0][1]).toMatchObject({ fill: 'white' });
|
||||
|
||||
background.dispose();
|
||||
});
|
||||
|
||||
it('keeps unspecified documents on the legacy gray workspace and page fills', () => {
|
||||
const background = DocBackground.create(
|
||||
'unspecified-background',
|
||||
createSkeleton([createPage()], DocumentFlavor.UNSPECIFIED)
|
||||
);
|
||||
background.resize(320, 180);
|
||||
const rectDraw = vi.spyOn(Rect, 'drawWith').mockImplementation(() => {});
|
||||
vi.spyOn(Path, 'drawWith').mockImplementation(() => {});
|
||||
|
||||
background.draw(createCtx());
|
||||
|
||||
expect(rectDraw.mock.calls[0][1]).toMatchObject({ fill: 'gray.100' });
|
||||
expect(rectDraw.mock.calls[1][1]).toMatchObject({ fill: 'gray.50' });
|
||||
|
||||
background.dispose();
|
||||
});
|
||||
|
||||
it('positions multiple traditional pages horizontally and draws their margin identifiers', () => {
|
||||
const background = DocBackground.create(
|
||||
'horizontal-background',
|
||||
|
||||
@@ -25,7 +25,8 @@ import { DocComponent } from './doc-component';
|
||||
import { Liquid } from './liquid';
|
||||
|
||||
const PAGE_STROKE_COLOR = 'gray.200';
|
||||
const PAGE_FILL_COLOR = 'gray.50';
|
||||
const PAGE_FILL_COLOR = 'white';
|
||||
const UNSPECIFIED_PAGE_FILL_COLOR = 'gray.50';
|
||||
const DOCS_WORKSPACE_FILL_COLOR = 'gray.100';
|
||||
const MARGIN_STROKE_COLOR = 'gray.300';
|
||||
|
||||
@@ -122,7 +123,7 @@ export class DocBackground extends DocComponent {
|
||||
height: pageHeight ?? height,
|
||||
strokeWidth: 1,
|
||||
stroke: this._pageStrokeColor ?? PAGE_STROKE_COLOR,
|
||||
fill: this._pageFillColor ?? PAGE_FILL_COLOR,
|
||||
fill: this._pageFillColor ?? (documentFlavor === DocumentFlavor.TRADITIONAL ? PAGE_FILL_COLOR : UNSPECIFIED_PAGE_FILL_COLOR),
|
||||
zIndex: 3,
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user