revert: "fix(docs): follow theme colors on canvas surfaces" (#7515)

This commit is contained in:
白熱
2026-08-15 09:57:15 +08:00
committed by GitHub
parent e0b1e2781d
commit 702a455883
8 changed files with 85 additions and 25 deletions
@@ -22,7 +22,7 @@ import {
LocaleService,
} from '@univerjs/core';
import { DocSkeletonManagerService, RichTextEditingMutation } from '@univerjs/docs';
import { DocumentEditArea, IRenderManagerService, Path } from '@univerjs/engine-render';
import { DocumentEditArea, IRenderManagerService, Path, Rect } from '@univerjs/engine-render';
import { BehaviorSubject, Subject } from 'rxjs';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { CloseHeaderFooterCommand } from '../../commands/commands/doc-header-footer.command';
@@ -151,8 +151,46 @@ describe('DocHeaderFooterController', () => {
controller.dispose();
});
it('draws header/footer guides while editing header or footer', () => {
it('covers header and footer areas while editing the document body', () => {
const { controller, pageRender$ } = createController({ editArea: DocumentEditArea.BODY });
const rectSpy = vi.spyOn(Rect, 'drawWith').mockImplementation(() => undefined);
const pathSpy = vi.spyOn(Path, 'drawWith').mockImplementation(() => undefined);
const textSpy = vi.spyOn(TextBubbleShape, 'drawWith').mockImplementation(() => undefined);
const ctx = createCtx();
pageRender$.next({
ctx,
pageLeft: 12,
pageTop: 24,
page: {
pageWidth: 200,
pageHeight: 300,
marginTop: 30,
marginBottom: 40,
},
});
expect(ctx.translate).toHaveBeenCalledWith(11.5, 23.5);
expect(rectSpy).toHaveBeenCalledTimes(2);
expect(rectSpy.mock.calls[0][1]).toMatchObject({
width: 200,
height: 30,
fill: 'alpha(white, 0.5)',
});
expect(rectSpy.mock.calls[1][1]).toMatchObject({
width: 200,
height: 40,
fill: 'alpha(white, 0.5)',
});
expect(pathSpy).not.toHaveBeenCalled();
expect(textSpy).not.toHaveBeenCalled();
controller.dispose();
});
it('covers the body and draws header/footer guides while editing header or footer', () => {
const { controller, pageRender$ } = createController({ editArea: DocumentEditArea.HEADER });
const rectSpy = vi.spyOn(Rect, 'drawWith').mockImplementation(() => undefined);
const pathSpy = vi.spyOn(Path, 'drawWith').mockImplementation(() => undefined);
const textSpy = vi.spyOn(TextBubbleShape, 'drawWith').mockImplementation(() => undefined);
const ctx = createCtx();
@@ -169,6 +207,11 @@ describe('DocHeaderFooterController', () => {
},
});
expect(rectSpy).toHaveBeenCalledWith(ctx, expect.objectContaining({
top: 30,
width: 200,
height: 230,
}));
expect(pathSpy).toHaveBeenCalledTimes(2);
expect(pathSpy).toHaveBeenCalledWith(ctx, expect.objectContaining({ stroke: 'primary.600' }));
expect(textSpy).toHaveBeenCalledWith(ctx, expect.objectContaining({
@@ -235,7 +235,7 @@ function createControllerFixture(options?: {
describe('doc render controller', () => {
it.each([
[DocumentFlavor.TRADITIONAL, 'gray.100'],
[DocumentFlavor.MODERN, 'token(white)'],
[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}`);
@@ -41,7 +41,7 @@ import {
UniverInstanceType,
} from '@univerjs/core';
import { DocSkeletonManagerService, HeaderFooterType, RichTextEditingMutation } from '@univerjs/docs';
import { DocumentEditArea, IRenderManagerService, PageLayoutType, Path, Vector2 } from '@univerjs/engine-render';
import { DocumentEditArea, IRenderManagerService, PageLayoutType, Path, Rect, Vector2 } from '@univerjs/engine-render';
import { neoGetDocObject } from '../basics/component-tools';
import { CloseHeaderFooterCommand, CoreHeaderFooterCommand } from '../commands/commands/doc-header-footer.command';
import { IEditorService } from '../services/editor/editor-manager.service';
@@ -49,6 +49,7 @@ import { DocSelectionRenderService } from '../services/selection/doc-selection-r
import { getDocPageSectionContext } from '../utils/section-header-footer';
import { TextBubbleShape } from '../views/header-footer/text-bubble';
const HEADER_FOOTER_COVER_COLOR = 'alpha(white, 0.5)';
const HEADER_FOOTER_STROKE_COLOR = 'primary.600';
const HEADER_FOOTER_LABEL_COLOR = 'alpha(primary.600, 0.08)';
@@ -306,6 +307,7 @@ export class DocHeaderFooterController extends Disposable implements IRenderModu
this.disposeWithMe(
toDisposable(
// eslint-disable-next-line max-lines-per-function
docsComponent.pageRender$.subscribe((config: IPageRenderConfig) => {
if (this._editorService.isEditor(unitId)) {
return;
@@ -325,6 +327,38 @@ export class DocHeaderFooterController extends Disposable implements IRenderModu
ctx.save();
ctx.translate(pageLeft - 0.5, pageTop - 0.5);
// Cover header and footer.
if (isEditBody) {
Rect.drawWith(ctx, {
left: 0,
top: 0,
width: pageWidth,
height: marginTop,
fill: HEADER_FOOTER_COVER_COLOR,
});
ctx.save();
ctx.translate(0, pageHeight - marginBottom);
Rect.drawWith(ctx, {
left: 0,
top: 0,
width: pageWidth,
height: marginBottom,
fill: HEADER_FOOTER_COVER_COLOR,
});
ctx.restore();
} else { // Cover body.
ctx.save();
ctx.translate(0, marginTop);
Rect.drawWith(ctx, {
left: 0,
top: marginTop,
width: pageWidth,
height: pageHeight - marginTop - marginBottom,
fill: HEADER_FOOTER_COVER_COLOR,
});
ctx.restore();
}
if (!isEditBody) {
const headerPathConfigIPathProps = {
dataArray: [{
@@ -22,7 +22,7 @@ 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 = 'token(white)';
const DOC_MODERN_WORKSPACE_BACKGROUND_TOKEN = 'white';
export interface IResolveDocRenderBackgroundOptions {
documentFlavor?: DocumentFlavor;
@@ -75,7 +75,7 @@ describe('DocBackground', () => {
background.draw(createCtx());
expect(rectDraw.mock.calls[0][1]).toMatchObject({ fill: 'gray.100' });
expect(rectDraw.mock.calls[1][1]).toMatchObject({ fill: 'token(white)' });
expect(rectDraw.mock.calls[1][1]).toMatchObject({ fill: 'white' });
background.dispose();
});
@@ -91,7 +91,7 @@ describe('DocBackground', () => {
background.draw(createCtx());
expect(rectDraw).toHaveBeenCalledTimes(1);
expect(rectDraw.mock.calls[0][1]).toMatchObject({ fill: 'token(white)' });
expect(rectDraw.mock.calls[0][1]).toMatchObject({ fill: 'white' });
background.dispose();
});
@@ -25,7 +25,7 @@ import { DocComponent } from './doc-component';
import { Liquid } from './liquid';
const PAGE_STROKE_COLOR = 'gray.200';
const PAGE_FILL_COLOR = 'token(white)';
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';
@@ -53,11 +53,9 @@ describe('CanvasColorService', () => {
expect(service.getRenderColor('gray.50')).toBe('#0d1422');
expect(service.getRenderColor('white')).toBe('white');
expect(service.getRenderColor('token(white)')).toBe('#07111f');
themeService.setTheme({
...theme,
white: '#050914',
gray: {
...theme.gray,
50: '#101827',
@@ -65,7 +63,6 @@ describe('CanvasColorService', () => {
});
expect(service.getRenderColor('gray.50')).toBe('#101827');
expect(service.getRenderColor('token(white)')).toBe('#050914');
});
it('applies dark rendering to resolved design tokens', () => {
@@ -164,7 +161,6 @@ describe('CanvasColorService', () => {
expect(() => service.getRenderColor('alpha(gray.50, 1.1)')).toThrow('[CanvasColorService]: illegal color');
expect(() => service.getRenderColor('alpha(not-a-color, 0.5)')).toThrow('[CanvasColorService]: illegal color');
expect(() => service.getRenderColor('token(not-a-color)')).toThrow('[CanvasColorService]: illegal color');
});
it('maps render colors for dark mode rendering', () => {
@@ -71,7 +71,6 @@ const DARK_RENDER_COLOR_OVERRIDES: Record<string, string> = {
const COLOR_MIX_REGEXP = /^mix\(\s*([^,()]+)\s*,\s*([^,()]+)\s*,\s*(0(?:\.\d+)?|1(?:\.0+)?)\s*\)$/;
const COLOR_ALPHA_REGEXP = /^alpha\(\s*([^,()]+)\s*,\s*(0(?:\.\d+)?|1(?:\.0+)?)\s*\)$/;
const THEME_TOKEN_REGEXP = /^token\(\s*([^,()]+)\s*\)$/;
/**
* This service inverts a color for dark mode. This service is exposed
@@ -152,18 +151,6 @@ export class CanvasColorService extends Disposable implements ICanvasColorServic
return cachedColor;
}
const tokenMatch = inputColor.match(THEME_TOKEN_REGEXP);
if (tokenMatch) {
const token = tokenMatch[1].trim();
const color = this._themeService.getColorFromTheme<unknown>(token);
if (typeof color !== 'string' || !this._themeService.isValidThemeColor(token)) {
throw new Error(`[CanvasColorService]: illegal color "${inputColor}"`);
}
this._resolvedColorCache.set(inputColor, color);
return color;
}
const mixMatch = inputColor.match(COLOR_MIX_REGEXP);
if (mixMatch) {
const color1 = this._resolveThemeColor(mixMatch[1].trim());