Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for adding/removing space before/after paragraphs in content model #1565

Merged
merged 19 commits into from
Feb 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import {
tableSplitButton,
} from './tableEditButtons';
import { spacingButton } from './spacingButton';
import { spaceAfterButton, spaceBeforeButton } from './spaceBeforeAfterButtons';

const buttons = [
formatPainterButton,
Expand Down Expand Up @@ -98,6 +99,8 @@ const buttons = [
changeImageButton,
imageBoxShadowButton,
spacingButton,
spaceBeforeButton,
spaceAfterButton,
];

export default function ContentModelRibbon(props: { ribbonPlugin: RibbonPlugin; isRtl: boolean }) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { RibbonButton } from 'roosterjs-react';
import { getFormatState, isContentModelEditor, setParagraphMargin } from 'roosterjs-content-model';

const spaceAfterButtonKey = 'buttonNameSpaceAfter';
const spaceBeforeButtonKey = 'buttonNameSpaceBefore';

/**
* @internal
* "Add space after" button on the format ribbon
*/
export const spaceAfterButton: RibbonButton<typeof spaceAfterButtonKey> = {
key: spaceAfterButtonKey,
unlocalizedText: 'Remove space after',
iconName: 'CaretDown8',
isChecked: formatState => !formatState.marginBottom || parseInt(formatState.marginBottom) <= 0,
onClick: editor => {
if (isContentModelEditor(editor)) {
const marginBottom = getFormatState(editor).marginBottom;
setParagraphMargin(
editor,
undefined /* marginTop */,
parseInt(marginBottom) ? null : '8pt'
);
}
return true;
},
};

/**
* @internal
* "Add space before" button on the format ribbon
*/
export const spaceBeforeButton: RibbonButton<typeof spaceBeforeButtonKey> = {
key: spaceBeforeButtonKey,
unlocalizedText: 'Add space before',
iconName: 'CaretUp8',
isChecked: formatState => parseInt(formatState.marginTop) > 0,
onClick: editor => {
if (isContentModelEditor(editor)) {
const marginTop = getFormatState(editor).marginTop;
setParagraphMargin(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want to do a toggle, you can get format state using getFormatState() here. Do not let plugin pass the format to you. Or if you really want, make it an optional parameter so that we know it can be null.

editor,
parseInt(marginTop) ? null : '12pt',
undefined /* marginBottom */
);
}
return true;
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ function retrieveFormatStateInternal(
result.textColor = format.textColor;
//TODO: handle block owning segments with different line-heights
result.lineHeight = paragraph.format.lineHeight || format.lineHeight;
result.marginBottom = paragraph.format.marginBottom;
result.marginTop = paragraph.format.marginTop;

result.isBold = isBold(format.fontWeight);
result.isItalic = format.italic;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { createParagraphDecorator } from '../../modelApi/creators/createParagraphDecorator';
import { IContentModelEditor } from '../../publicTypes/IContentModelEditor';
import { formatParagraphWithContentModel } from '../utils/formatParagraphWithContentModel';

/**
* Toggles the current block(s) margin properties.
* null deletes any existing value, undefined is ignored
* @param editor The editor to operate on
* @param marginTop value for top margin
* @param marginBottom value for bottom margin
*/
export default function setParagraphMargin(
editor: IContentModelEditor,
marginTop?: string | null,
marginBottom?: string | null
) {
formatParagraphWithContentModel(editor, 'setParagraphMargin', para => {
if (!para.decorator) {
para.decorator = createParagraphDecorator('p');
}

if (marginTop) {
para.format.marginTop = marginTop;
} else if (marginTop === null) {
delete para.format.marginTop;
}

if (marginBottom) {
para.format.marginBottom = marginBottom;
} else if (marginBottom === null) {
delete para.format.marginBottom;
}
});
}
1 change: 1 addition & 0 deletions packages/roosterjs-content-model/lib/publicApi/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,4 @@ export { default as removeLink } from './link/removeLink';
export { default as adjustLinkSelection } from './link/adjustLinkSelection';
export { default as setImageAltText } from './image/setImageAltText';
export { default as adjustImageSelection } from './image/adjustImageSelection';
export { default as setParagraphMargin } from './block/setParagraphMargin';
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ describe('retrieveModelFormatState', () => {
canUnlink: false,
canAddImageAltText: false,
lineHeight: undefined,
marginTop: undefined,
marginBottom: undefined,
};

it('Empty model', () => {
Expand Down Expand Up @@ -131,6 +133,29 @@ describe('retrieveModelFormatState', () => {
});
});

it('Single selection with margin format', () => {
const model = createContentModelDocument();
const result: FormatState = {};
const paraFormat = {
marginTop: '2px',
marginBottom: '5px',
};
const para = createParagraph(false, paraFormat);
const marker = createSelectionMarker(segmentFormat);

spyOn(iterateSelections, 'iterateSelections').and.callFake((path, callback) => {
callback(path, undefined, para, [marker]);
return false;
});

retrieveModelFormatState(model, null, result);

expect(result).toEqual({
...baseFormatResult,
...paraFormat,
});
});

it('Single selection with table', () => {
const model = createContentModelDocument();
const result: FormatState = {};
Expand Down Expand Up @@ -331,6 +356,8 @@ describe('retrieveModelFormatState', () => {
canUnlink: false,
canAddImageAltText: false,
lineHeight: undefined,
marginTop: undefined,
marginBottom: undefined,
});
});

Expand Down Expand Up @@ -443,6 +470,8 @@ describe('retrieveModelFormatState', () => {
isUnderline: undefined,
isStrikeThrough: undefined,
lineHeight: undefined,
marginTop: undefined,
marginBottom: undefined,
ianeli1 marked this conversation as resolved.
Show resolved Hide resolved
});
});
});
Loading