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 7 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,43 @@
import { RibbonButton } from 'roosterjs-react';
import { isContentModelEditor, toggleMargins } 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)) {
toggleMargins(editor, {
marginBottom: '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)) {
toggleMargins(editor, {
marginTop: '12pt',
});
}
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,29 @@
import { getObjectKeys } from 'roosterjs-editor-dom';
import { createParagraphDecorator } from '../../modelApi/creators/createParagraphDecorator';
import { MarginFormat } from '../../publicTypes/format/formatParts/MarginFormat';
import { IContentModelEditor } from '../../publicTypes/IContentModelEditor';
import { formatParagraphWithContentModel } from '../utils/formatParagraphWithContentModel';

/**
* Toggles the current block(s) margin properties.
* @param editor The editor to operate on
* @param marginFormat Object containing margin props to be changed.
* If a margin property is not in this object, it won't be changed.
*/
export default function toggleMargins(editor: IContentModelEditor, marginFormat: MarginFormat) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we want to support margin left and right here? If not, you can make the parameter for margin top and bottom only

ianeli1 marked this conversation as resolved.
Show resolved Hide resolved
formatParagraphWithContentModel(editor, 'toggleMargins', para => {
if (!para.decorator || para.decorator.tagName !== 'p') {
para.decorator = createParagraphDecorator('p');
JiuqingSong marked this conversation as resolved.
Show resolved Hide resolved
ianeli1 marked this conversation as resolved.
Show resolved Hide resolved
}
getObjectKeys(marginFormat).forEach(key => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

what if marginFormat only contain marginTop but existing format contains marginBottom? Do we want to delete it?

getObjectKeys() can only return keys that exists in marginFormat

Copy link
Member Author

Choose a reason for hiding this comment

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

I was going for a function that let us toggle marginTop and/or marginBottom in one implementation. So if a key isn't passed, it will be ignored

Copy link
Collaborator

Choose a reason for hiding this comment

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

Let' just have separate parameters and handle them one by one.

Using getObjectKeys() may accidentally overwrite properties that we don't want to if user pass in a larger object then expected parameter type as sub class.

if (
para.format[key] &&
(para.format[key] === marginFormat[key] || parseInt(para.format[key]!) > 0)
Copy link
Collaborator

Choose a reason for hiding this comment

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

why delete para.format[key] when format[key] == marginFormat[key]?

Copy link
Member Author

Choose a reason for hiding this comment

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

To answer this and "I think name it "setMargins" is better", if we make it delete the margin format when format is already set, we can toggle the value, no need to get formatState to apply margin. Also removes a positive margin (coming from copy paste for example)

Copy link
Collaborator

Choose a reason for hiding this comment

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

Hmm, I think that make cause some confusion to user. If user doesn't want to change the margin, they need to be very careful to make sure not passing in a value that is the same with existing value. So user first need to get the existing value to check if it is the same with the one they want to check.

When a function is named "toggle", it means user doesn't need to know the original value, but just call it to turn on or off.

So still suggest:

  1. name the function "setMargin" or "setParagraphMargin"
  2. have two separate parameters for top and bottom
  3. when pass in a valide value for top/bottom, apply it, no matter if it is the same with existing value
  4. when pass in undefined, don't change
  5. when pass in null or 0, delete the margin
    so that is easier to understand and use.

Copy link
Collaborator

Choose a reason for hiding this comment

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

We have an API "toggleHeader" in original roosterjs code which is a bad example. In content model I have renamed it to be "setHeaderLevel"

Copy link
Member Author

Choose a reason for hiding this comment

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

Got it, went ahead with all of your fixes
Had to add support for passing formatState in onClick callback for the spaceBeforeAfter buttons to remain simple

) {
delete para.format[key];
} else {
para.format[key] = marginFormat[key];
}
});
});
}
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 @@ -40,3 +40,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 toggleMargins } from './block/toggleMargins';
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 @@ -331,6 +333,8 @@ describe('retrieveModelFormatState', () => {
canUnlink: false,
canAddImageAltText: false,
lineHeight: undefined,
marginTop: undefined,
marginBottom: undefined,
});
});

Expand Down Expand Up @@ -443,6 +447,8 @@ describe('retrieveModelFormatState', () => {
isUnderline: undefined,
isStrikeThrough: undefined,
lineHeight: undefined,
marginTop: undefined,
marginBottom: undefined,
ianeli1 marked this conversation as resolved.
Show resolved Hide resolved
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
import { ContentModelDocument } from '../../../lib/publicTypes/group/ContentModelDocument';
import { paragraphTestCommon } from './paragraphTestCommon';
import toggleMargins from '../../../lib/publicApi/block/toggleMargins';
import { MarginFormat } from '../../../lib/publicTypes/format/formatParts/MarginFormat';

describe('setMargins', () => {
function runTest(
model: ContentModelDocument,
result: ContentModelDocument,
marginFormat: MarginFormat,
calledTimes: number = 1
) {
paragraphTestCommon(
'toggleMargins',
editor => toggleMargins(editor, marginFormat),
model,
result,
calledTimes
);
}

it('empty content', () => {
runTest(
{
blockGroupType: 'Document',
blocks: [],
},
{
blockGroupType: 'Document',
blocks: [],
},
{
marginTop: '8px',
},
0
);
});

it('no selection', () => {
runTest(
{
blockGroupType: 'Document',
blocks: [
{
blockType: 'Paragraph',
format: {},
segments: [
{
segmentType: 'Text',
text: 'test',
format: {},
},
],
},
],
},
{
blockGroupType: 'Document',
blocks: [
{
blockType: 'Paragraph',
format: {},
segments: [
{
segmentType: 'Text',
text: 'test',
format: {},
},
],
},
],
},
{
marginTop: '8px',
},
0
);
});

it('Collapsed selection', () => {
runTest(
{
blockGroupType: 'Document',
blocks: [
{
blockType: 'Paragraph',
format: {},
segments: [
{
segmentType: 'Text',
text: 'test',
format: {},
},
{
segmentType: 'SelectionMarker',
format: {},
isSelected: true,
},
],
},
],
},
{
blockGroupType: 'Document',
blocks: [
{
blockType: 'Paragraph',
format: {
marginTop: '8px',
},
decorator: {
tagName: 'p',
format: {},
},
segments: [
{
segmentType: 'Text',
text: 'test',
format: {},
},
{
segmentType: 'SelectionMarker',
format: {},
isSelected: true,
},
],
},
],
},
{
marginTop: '8px',
},
1
);
});

it('With selection', () => {
runTest(
{
blockGroupType: 'Document',
blocks: [
{
blockType: 'Paragraph',
format: {},
segments: [
{
segmentType: 'Text',
text: 'test',
format: {},
isSelected: true,
},
],
},
],
},
{
blockGroupType: 'Document',
blocks: [
{
blockType: 'Paragraph',
format: {
marginTop: '8px',
},
decorator: {
tagName: 'p',
format: {},
},
segments: [
{
segmentType: 'Text',
text: 'test',
format: {},
isSelected: true,
},
],
},
],
},
{ marginTop: '8px' },
1
);
});

it('Replaces properties correctly', () => {
runTest(
{
blockGroupType: 'Document',
blocks: [
{
blockType: 'Paragraph',
format: {
marginTop: '8px',
marginRight: '12px',
},
segments: [
{
segmentType: 'Text',
text: 'test',
format: {},
},
{
segmentType: 'SelectionMarker',
format: {},
isSelected: true,
},
],
},
],
},
{
blockGroupType: 'Document',
blocks: [
{
blockType: 'Paragraph',
format: {
marginRight: '12px',
},
decorator: {
tagName: 'p',
format: {},
},
segments: [
{
segmentType: 'Text',
text: 'test',
format: {},
},
{
segmentType: 'SelectionMarker',
format: {},
isSelected: true,
},
],
},
],
},
{ marginTop: '8px' },
1
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ export const getStyleBasedFormatState: GetStyleBasedFormatState = (
'color',
'background-color',
'line-height',
'margin-top',
'margin-bottom',
])
: [];
const {
Expand Down Expand Up @@ -95,6 +97,8 @@ export const getStyleBasedFormatState: GetStyleBasedFormatState = (
}
: undefined,
lineHeight: styles[4],
marginTop: styles[5],
marginBottom: styles[6],
};
} else {
const ogTextColorNode =
Expand Down
Loading