mirror of
https://github.com/dream-num/univer.git
synced 2026-10-02 04:24:34 +08:00
fix(docs-ui): clear solid text fill when setting text color (#7558)
This commit is contained in:
@@ -14,10 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { DocumentDataModel, ICommand, Injector, ITextStyle, Univer } from '@univerjs/core';
|
||||
import type { DocumentDataModel, ICommand, IDocumentData, Injector, ITextStyle, Univer } from '@univerjs/core';
|
||||
import {
|
||||
BaselineOffset,
|
||||
BooleanNumber,
|
||||
createInternalEditorID,
|
||||
DOC_RANGE_TYPE,
|
||||
ICommandService,
|
||||
IUniverInstanceService,
|
||||
@@ -425,5 +426,118 @@ describe('Test inline format commands', () => {
|
||||
rgb: null,
|
||||
});
|
||||
});
|
||||
|
||||
it('clears only solid text fills in a standalone document', async () => {
|
||||
const docsModel = get(IUniverInstanceService)
|
||||
.getUnit<DocumentDataModel>('test-doc', UniverInstanceType.UNIVER_DOC)!;
|
||||
const body = docsModel.getBody()!;
|
||||
const gradientFill = {
|
||||
type: 'gradient' as const,
|
||||
gradient: {
|
||||
type: 'linear' as const,
|
||||
stops: [
|
||||
{ offset: 0, color: '#111111' },
|
||||
{ offset: 1, color: '#eeeeee' },
|
||||
],
|
||||
},
|
||||
};
|
||||
const pictureFill = {
|
||||
type: 'picture' as const,
|
||||
picture: {
|
||||
source: 'https://example.com/fill.png',
|
||||
},
|
||||
};
|
||||
|
||||
body.textRuns = [
|
||||
{
|
||||
st: 0,
|
||||
ed: 2,
|
||||
ts: {
|
||||
bl: BooleanNumber.TRUE,
|
||||
cl: { rgb: '#111111' },
|
||||
textFill: { type: 'solid', color: '#222222' },
|
||||
},
|
||||
},
|
||||
{
|
||||
st: 2,
|
||||
ed: 4,
|
||||
ts: {
|
||||
it: BooleanNumber.TRUE,
|
||||
cl: { rgb: '#111111' },
|
||||
textFill: gradientFill,
|
||||
},
|
||||
},
|
||||
{
|
||||
st: 4,
|
||||
ed: 22,
|
||||
ts: {
|
||||
fs: 24,
|
||||
cl: { rgb: '#111111' },
|
||||
textFill: pictureFill,
|
||||
},
|
||||
},
|
||||
{
|
||||
st: 23,
|
||||
ed: 68,
|
||||
ts: {
|
||||
fs: 24,
|
||||
cl: { rgb: '#111111' },
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
await commandService.executeCommand(SetInlineFormatTextColorCommand.id, { value: '#ff0000' });
|
||||
|
||||
expect(getFormatValueAt('cl', 1)).toStrictEqual({ rgb: '#ff0000' });
|
||||
expect(getFormatValueAt('bl', 1)).toBe(BooleanNumber.TRUE);
|
||||
expect(getFormatValueAt('textFill', 1)).toBeUndefined();
|
||||
expect(getFormatValueAt('textFill', 3)).toStrictEqual(gradientFill);
|
||||
expect(getFormatValueAt('textFill', 4.5)).toStrictEqual(pictureFill);
|
||||
|
||||
await commandService.executeCommand(UndoCommand.id);
|
||||
expect(getFormatValueAt('cl', 1)).toStrictEqual({ rgb: '#111111' });
|
||||
expect(getFormatValueAt('textFill', 1)).toStrictEqual({ type: 'solid', color: '#222222' });
|
||||
|
||||
await commandService.executeCommand(RedoCommand.id);
|
||||
expect(getFormatValueAt('cl', 1)).toStrictEqual({ rgb: '#ff0000' });
|
||||
expect(getFormatValueAt('textFill', 1)).toBeUndefined();
|
||||
});
|
||||
|
||||
it('preserves solid text fills in an internal editor', async () => {
|
||||
const unitId = createInternalEditorID('shape-text');
|
||||
const internalDoc = univer.createUnit<IDocumentData, DocumentDataModel>(UniverInstanceType.UNIVER_DOC, {
|
||||
id: unitId,
|
||||
body: {
|
||||
dataStream: 'abc\r\n',
|
||||
textRuns: [{
|
||||
st: 0,
|
||||
ed: 3,
|
||||
ts: {
|
||||
cl: { rgb: '#111111' },
|
||||
textFill: { type: 'solid', color: '#222222' },
|
||||
},
|
||||
}],
|
||||
paragraphs: [{ startIndex: 3, paragraphId: 'shape-text-paragraph' }],
|
||||
},
|
||||
});
|
||||
const univerInstanceService = get(IUniverInstanceService);
|
||||
univerInstanceService.focusUnit(unitId);
|
||||
const selectionManager = get(DocSelectionManagerService);
|
||||
selectionManager.__TEST_ONLY_setCurrentSelection({ unitId, subUnitId: unitId });
|
||||
selectionManager.__TEST_ONLY_add([{
|
||||
startOffset: 0,
|
||||
endOffset: 3,
|
||||
collapsed: false,
|
||||
isActive: true,
|
||||
}]);
|
||||
|
||||
await commandService.executeCommand(SetInlineFormatTextColorCommand.id, { value: '#ff0000' });
|
||||
|
||||
expect(internalDoc.getBody()?.textRuns?.[0].ts?.cl).toStrictEqual({ rgb: '#ff0000' });
|
||||
expect(internalDoc.getBody()?.textRuns?.[0].ts?.textFill).toStrictEqual({
|
||||
type: 'solid',
|
||||
color: '#222222',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -35,6 +35,7 @@ import {
|
||||
getBodySlice,
|
||||
getRichTextEditPath,
|
||||
ICommandService,
|
||||
isInternalEditorID,
|
||||
IUniverInstanceService,
|
||||
JSONX,
|
||||
MemoryCursor,
|
||||
@@ -42,9 +43,11 @@ import {
|
||||
TextXActionType,
|
||||
Tools,
|
||||
UniverInstanceType,
|
||||
UpdateDocsAttributeType,
|
||||
} from '@univerjs/core';
|
||||
import { DocSelectionManagerService, RichTextEditingMutation } from '@univerjs/docs';
|
||||
import { DocMenuStyleService } from '../../services/doc-menu-style.service';
|
||||
import { IEditorService } from '../../services/editor/editor-manager.service';
|
||||
|
||||
function handleInlineFormat(
|
||||
preCommandId: string,
|
||||
@@ -280,6 +283,7 @@ export const SetInlineFormatCommand: ICommand<ISetInlineFormatCommandParams> = {
|
||||
handler: async (accessor, params: ISetInlineFormatCommandParams) => {
|
||||
const { value, preCommandId } = params;
|
||||
const commandService = accessor.get(ICommandService);
|
||||
const editorService = accessor.has(IEditorService) ? accessor.get(IEditorService) : null;
|
||||
const docSelectionManagerService = accessor.get(DocSelectionManagerService);
|
||||
const univerInstanceService = accessor.get(IUniverInstanceService);
|
||||
const docMenuStyleService = accessor.get(DocMenuStyleService);
|
||||
@@ -377,6 +381,32 @@ export const SetInlineFormatCommand: ICommand<ISetInlineFormatCommandParams> = {
|
||||
|
||||
const textX = new TextX();
|
||||
const jsonX = JSONX.getInstance();
|
||||
const textStylePatch = formatPatch ?? {
|
||||
[COMMAND_ID_TO_FORMAT_KEY_MAP[preCommandId]]: formatValue,
|
||||
};
|
||||
const shouldClearSolidTextFill = preCommandId === SetInlineFormatTextColorCommand.id
|
||||
&& !isInternalEditorID(unitId)
|
||||
&& !editorService?.isEditor(unitId);
|
||||
|
||||
const pushTextStyleUpdate = (len: number, ts: Partial<ITextStyle>, replace = false) => {
|
||||
if (len <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
textX.push({
|
||||
t: TextXActionType.RETAIN,
|
||||
body: {
|
||||
dataStream: '',
|
||||
textRuns: [{
|
||||
st: 0,
|
||||
ed: len,
|
||||
ts,
|
||||
}],
|
||||
},
|
||||
len,
|
||||
...(replace ? { coverType: UpdateDocsAttributeType.REPLACE } : {}),
|
||||
});
|
||||
};
|
||||
|
||||
const memoryCursor = new MemoryCursor();
|
||||
memoryCursor.reset();
|
||||
@@ -416,19 +446,6 @@ export const SetInlineFormatCommand: ICommand<ISetInlineFormatCommandParams> = {
|
||||
continue;
|
||||
}
|
||||
|
||||
const body: IDocumentBody = {
|
||||
dataStream: '',
|
||||
textRuns: [
|
||||
{
|
||||
st: 0,
|
||||
ed: endOffset - startOffset,
|
||||
ts: formatPatch ?? {
|
||||
[COMMAND_ID_TO_FORMAT_KEY_MAP[preCommandId]]: formatValue,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const len = startOffset - memoryCursor.cursor;
|
||||
|
||||
if (len !== 0) {
|
||||
@@ -438,11 +455,37 @@ export const SetInlineFormatCommand: ICommand<ISetInlineFormatCommandParams> = {
|
||||
});
|
||||
}
|
||||
|
||||
textX.push({
|
||||
t: TextXActionType.RETAIN,
|
||||
body,
|
||||
len: endOffset - startOffset,
|
||||
});
|
||||
if (shouldClearSolidTextFill) {
|
||||
let currentOffset = startOffset;
|
||||
|
||||
for (const textRun of body.textRuns ?? []) {
|
||||
if (textRun.ts?.textFill?.type !== 'solid') {
|
||||
continue;
|
||||
}
|
||||
|
||||
const solidStart = Math.max(currentOffset, textRun.st);
|
||||
const solidEnd = Math.min(endOffset, textRun.ed);
|
||||
|
||||
if (solidStart >= solidEnd) {
|
||||
continue;
|
||||
}
|
||||
|
||||
pushTextStyleUpdate(solidStart - currentOffset, textStylePatch);
|
||||
|
||||
const replacementStyle = {
|
||||
...textRun.ts,
|
||||
...textStylePatch,
|
||||
};
|
||||
delete replacementStyle.textFill;
|
||||
pushTextStyleUpdate(solidEnd - solidStart, replacementStyle, true);
|
||||
|
||||
currentOffset = solidEnd;
|
||||
}
|
||||
|
||||
pushTextStyleUpdate(endOffset - currentOffset, textStylePatch);
|
||||
} else {
|
||||
pushTextStyleUpdate(endOffset - startOffset, textStylePatch);
|
||||
}
|
||||
|
||||
memoryCursor.reset();
|
||||
memoryCursor.moveCursor(endOffset);
|
||||
|
||||
Reference in New Issue
Block a user