Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
7c35c40
Fix toggling format issue at composition stage for CJK (#3064)
haven2world Jun 11, 2025
3ba879c
Avoid "undefined" in HTML to Markdown generated content (#3069)
Durss Jun 11, 2025
a4e9602
Add focus handling in formatTableWithContentModel and its tests (#3072)
BryanValverdeU Jun 16, 2025
00e00f7
format applier (#3073)
juliaroldi Jun 16, 2025
87b1570
Keep implicit paragraph when pressing Enter at the start of it. (#3075)
BryanValverdeU Jun 18, 2025
184c018
Add beforeLogicalRootChanged event handling and related tests (#3077)
BryanValverdeU Jun 19, 2025
9da5af4
Disable isComposing check in DOM event propagation for Android (#3076)
Rain-Zheng Jun 20, 2025
7fab305
Allow plugins to store state in snapshot (#3079)
florian-msft Jun 26, 2025
7081cdd
Fix #3080 (#3084)
JiuqingSong Jul 1, 2025
fd808bd
Add auto direction to setDirection (#3082)
wisaulni Jul 7, 2025
327d6ab
Ensure image is loaded before creating image wrapper (#3090)
BryanValverdeU Jul 18, 2025
eb3685f
test (#3092)
juliaroldi Jul 22, 2025
3ad7db1
Fix #3085 (#3095)
JiuqingSong Jul 22, 2025
47271ff
Adjust the image wrapper to stay inline (#3093)
juliaroldi Jul 22, 2025
74bc3e4
Fix 309357 (#3097)
JiuqingSong Jul 22, 2025
6307ffb
Fix #3063 (#3096)
JiuqingSong Jul 23, 2025
82f0466
Fix #3087 Delete paragraph styles when there is nothing else to be de…
JiuqingSong Jul 23, 2025
2da7000
Remove karma changes (#3102)
juliaroldi Jul 24, 2025
16ba94c
Adjust Block Indentation (#3099)
juliaroldi Jul 25, 2025
596ed24
Process non-visible elements (#3089)
juliaroldi Jul 25, 2025
eb7cfaa
Fix runtime color for dark mode (#3101)
JiuqingSong Jul 27, 2025
6ed2303
table rtl (#3103)
juliaroldi Jul 28, 2025
bf46671
image-handles (#3104)
juliaroldi Jul 28, 2025
0437f87
Fix superscript rule (#3106)
juliaroldi Jul 30, 2025
7bf4a63
Merge branch 'master' of https://github.com/microsoft/roosterjs into …
BryanValverdeU Jul 31, 2025
2ff30f3
Update main version to 9.33.0 and remove overrides
BryanValverdeU Jul 31, 2025
c4f7f4f
Bump adapter
BryanValverdeU Jul 31, 2025
2d5f689
Fix 380860 (#3107)
JiuqingSong Jul 31, 2025
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
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
"Polski",
"Popout",
"Resizer",
"resizers",
"roosterjs",
"rowspan",
"saturationv",
Expand Down
23 changes: 21 additions & 2 deletions demo/scripts/controlsV2/demoButtons/exportContentButton.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import * as DOMPurify from 'dompurify';
import { exportContent } from 'roosterjs-content-model-core';
import { ModelToTextCallbacks } from 'roosterjs-content-model-types';
import type { RibbonButton } from 'roosterjs-react';
Expand Down Expand Up @@ -33,12 +34,30 @@ export const exportContentButton: RibbonButton<ExportButtonStringKey> = {
let html = '';

if (key == 'menuNameExportHTML') {
html = exportContent(editor);
html =
'<html><head><style>p{margin-top:0;margin-bottom:0;}</style></head><body>' +
exportContent(editor, 'HTML', {
defaultContentModelFormatOverride: {
p: {
marginTop: '0',
marginBottom: '0',
},
},
}) +
'</body></html>';
} else if (key == 'menuNameExportText') {
html = `<pre>${exportContent(editor, 'PlainText', callbacks)}</pre>`;
}

win.document.write(editor.getTrustedHTMLHandler()(html));
win.document.write(
(DOMPurify.sanitize(html, {
ADD_TAGS: ['head', 'meta', 'iframe'],
ADD_ATTR: ['name', 'content'],
WHOLE_DOCUMENT: true,
ALLOW_UNKNOWN_PROTOCOLS: true,
RETURN_TRUSTED_TYPE: true,
}) as any) as string
);
},
commandBarProperties: {
buttonStyles: {
Expand Down
3 changes: 2 additions & 1 deletion demo/scripts/controlsV2/demoButtons/formatPainterButton.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ export function createFormatPainterButton(
key: 'formatPainter',
unlocalizedText: 'Format painter',
iconName: 'Brush',
onClick: () => {
onClick: editor => {
handler.startFormatPainter();
editor.focus();
},
};
}
5 changes: 5 additions & 0 deletions demo/scripts/controlsV2/mainPane/MainPane.scss
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@
top: 0;
right: 0;
bottom: 0;

p {
margin-top: 0;
margin-bottom: 0;
}
}

.resizer {
Expand Down
45 changes: 29 additions & 16 deletions demo/scripts/controlsV2/mainPane/MainPane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ApiPlaygroundPlugin } from '../sidePane/apiPlayground/ApiPlaygroundPlug
import { ContentModelPanePlugin } from '../sidePane/contentModel/ContentModelPanePlugin';
import { darkModeButton } from '../demoButtons/darkModeButton';
import { defaultDomToModelOption } from '../options/defaultDomToModelOption';
import { defaultModelToDomOption } from '../options/defaultModelToDomOption';
import { Editor } from 'roosterjs-content-model-core';
import { EditorOptionsPlugin } from '../sidePane/editorOptions/EditorOptionsPlugin';
import { EventViewPlugin } from '../sidePane/eventViewer/EventViewPlugin';
Expand Down Expand Up @@ -106,7 +107,6 @@ export class MainPane extends React.Component<{}, MainPaneState> {
private formatPainterPlugin: FormatPainterPlugin;
private samplePickerPlugin: SamplePickerPlugin;
private snapshots: Snapshots;
private imageEditPlugin: ImageEditPlugin;
private markdownPanePlugin: MarkdownPanePlugin;

protected sidePane = React.createRef<SidePane>();
Expand Down Expand Up @@ -145,7 +145,6 @@ export class MainPane extends React.Component<{}, MainPaneState> {
this.ribbonPlugin = createRibbonPlugin();
this.formatPainterPlugin = new FormatPainterPlugin();
this.samplePickerPlugin = new SamplePickerPlugin();
this.imageEditPlugin = new ImageEditPlugin();
this.markdownPanePlugin = new MarkdownPanePlugin();

this.state = {
Expand All @@ -167,13 +166,22 @@ export class MainPane extends React.Component<{}, MainPaneState> {

render() {
const theme = getTheme(this.state.isDarkMode);

const imageEditPlugin = this.state.initState.pluginList.imageEditPlugin
? new ImageEditPlugin({
disableSideResize: this.state.initState.disableSideResize,
})
: null;

return (
<ThemeProvider applyTo="body" theme={theme} className={styles.mainPane}>
{this.renderTitleBar()}
{!this.state.popoutWindow && this.renderTabs()}
{!this.state.popoutWindow && this.renderRibbon()}
{!this.state.popoutWindow && this.renderRibbon(imageEditPlugin)}
<div className={styles.body + ' ' + (this.state.isDarkMode ? 'dark' : '')}>
{this.state.popoutWindow ? this.renderPopout() : this.renderMainPane()}
{this.state.popoutWindow
? this.renderPopout(imageEditPlugin)
: this.renderMainPane(imageEditPlugin)}
</div>
</ThemeProvider>
);
Expand Down Expand Up @@ -303,13 +311,13 @@ export class MainPane extends React.Component<{}, MainPaneState> {
</div>
);
}
private renderRibbon() {
private renderRibbon(imageEditPlugin: ImageEditPlugin | undefined) {
return (
<Ribbon
buttons={getButtons(
this.state.activeTab,
this.formatPainterPlugin,
this.imageEditPlugin
imageEditPlugin
)}
plugin={this.ribbonPlugin}
dir={this.state.isRtl ? 'rtl' : 'ltr'}
Expand Down Expand Up @@ -337,7 +345,7 @@ export class MainPane extends React.Component<{}, MainPaneState> {
});
}

private renderEditor() {
private renderEditor(imageEditPlugin: ImageEditPlugin | undefined) {
// Set preset if found
const search = new URLSearchParams(document.location.search);
const hasPreset = search.get('preset');
Expand All @@ -355,7 +363,7 @@ export class MainPane extends React.Component<{}, MainPaneState> {
this.ribbonPlugin,
this.formatPainterPlugin,
this.samplePickerPlugin,
...this.getToggleablePlugins(),
...this.getToggleablePlugins(imageEditPlugin),
this.contentModelPanePlugin.getInnerRibbonPlugin(),
this.updateContentPlugin,
];
Expand Down Expand Up @@ -389,17 +397,18 @@ export class MainPane extends React.Component<{}, MainPaneState> {
this.state.initState.experimentalFeatures
)}
defaultDomToModelOptions={defaultDomToModelOption}
defaultModelToDomOptions={defaultModelToDomOption}
/>
)}
</div>
</div>
);
}

private renderMainPane() {
private renderMainPane(imageEditPlugin: ImageEditPlugin | undefined) {
return (
<>
{this.renderEditor()}
{this.renderEditor(imageEditPlugin)}
{this.state.showSidePane ? (
<>
<div className={styles.resizer} onMouseDown={this.onMouseDown} />
Expand All @@ -425,7 +434,7 @@ export class MainPane extends React.Component<{}, MainPaneState> {
);
}

private renderPopout() {
private renderPopout(imageEditPlugin: ImageEditPlugin | undefined) {
return (
<>
{this.renderSidePane(true /*fullWidth*/)}
Expand All @@ -434,8 +443,10 @@ export class MainPane extends React.Component<{}, MainPaneState> {
<ThemeProvider applyTo="body" theme={getTheme(this.state.isDarkMode)}>
<div className={styles.mainPane}>
{this.renderTabs()}
{this.renderRibbon()}
<div className={styles.body}>{this.renderEditor()}</div>
{this.renderRibbon(imageEditPlugin)}
<div className={styles.body}>
{this.renderEditor(imageEditPlugin)}
</div>
</div>
</ThemeProvider>
</WindowProvider>,
Expand Down Expand Up @@ -501,7 +512,7 @@ export class MainPane extends React.Component<{}, MainPaneState> {
];
}

private getToggleablePlugins(): EditorPlugin[] {
private getToggleablePlugins(imageEditPlugin: ImageEditPlugin | undefined): EditorPlugin[] {
const {
pluginList,
allowExcelNoBorderTable,
Expand All @@ -515,6 +526,7 @@ export class MainPane extends React.Component<{}, MainPaneState> {
customReplacements,
editPluginOptions,
} = this.state.initState;

return [
pluginList.autoFormat && new AutoFormatPlugin(autoFormatOptions),
pluginList.edit && new EditPlugin(editPluginOptions),
Expand All @@ -523,16 +535,17 @@ export class MainPane extends React.Component<{}, MainPaneState> {
pluginList.tableEdit && new TableEditPlugin(),
pluginList.watermark && new WatermarkPlugin(watermarkText),
pluginList.markdown && new MarkdownPlugin(markdownOptions),
pluginList.imageEditPlugin && this.imageEditPlugin,
imageEditPlugin,
pluginList.emoji && createEmojiPlugin(),
pluginList.pasteOption && createPasteOptionPlugin(),
pluginList.sampleEntity && new SampleEntityPlugin(),
pluginList.contextMenu && createContextMenuPlugin(),
pluginList.contextMenu && listMenu && createListEditMenuProvider(),
pluginList.contextMenu && tableMenu && createTableEditMenuProvider(),
pluginList.contextMenu &&
imageEditPlugin &&
imageMenu &&
createImageEditMenuProvider(this.imageEditPlugin),
createImageEditMenuProvider(imageEditPlugin),
pluginList.hyperlink &&
new HyperlinkPlugin(
linkTitle?.indexOf(UrlPlaceholder) >= 0
Expand Down
1 change: 1 addition & 0 deletions demo/scripts/controlsV2/options/defaultDomToModelOption.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ export const defaultDomToModelOption: DomToModelOption = {
additionalFormatParsers: {
link: [demoUndeletableAnchorParser],
},
processNonVisibleElements: true,
};
10 changes: 10 additions & 0 deletions demo/scripts/controlsV2/options/defaultModelToDomOption.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { ModelToDomOption } from 'roosterjs-content-model-types';

export const defaultModelToDomOption: ModelToDomOption = {
defaultContentModelFormatOverride: {
p: {
marginTop: '0',
marginBottom: '0',
},
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ const initialState: OptionState = {
handleTabKey: true,
},
customReplacements: emojiReplacements,
disableSideResize: false,
experimentalFeatures: new Set<ExperimentalFeature>([
'PersistCache',
'HandleEnterKey',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export interface OptionState {
markdownOptions: MarkdownOptions;
customReplacements: CustomReplace[];
editPluginOptions: EditOptions;
disableSideResize: boolean;

// Legacy plugin options
defaultFormat: ContentModelSegmentFormat;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ export class OptionsPane extends React.Component<OptionPaneProps, OptionState> {
customReplacements: this.state.customReplacements,
experimentalFeatures: this.state.experimentalFeatures,
editPluginOptions: { ...this.state.editPluginOptions },
disableSideResize: this.state.disableSideResize,
};

if (callback) {
Expand Down
14 changes: 13 additions & 1 deletion demo/scripts/controlsV2/sidePane/editorOptions/Plugins.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ export class Plugins extends PluginsBase<keyof BuildInPluginList> {
private markdownStrikethrough = React.createRef<HTMLInputElement>();
private markdownCode = React.createRef<HTMLInputElement>();
private linkTitle = React.createRef<HTMLInputElement>();
private disableSideResize = React.createRef<HTMLInputElement>();

render(): JSX.Element {
return (
Expand Down Expand Up @@ -314,7 +315,18 @@ export class Plugins extends PluginsBase<keyof BuildInPluginList> {
)
)}
{this.renderPluginItem('customReplace', 'Custom Replace')}
{this.renderPluginItem('imageEditPlugin', 'ImageEditPlugin')}
{this.renderPluginItem(
'imageEditPlugin',
'ImageEditPlugin',
<>
{this.renderCheckBox(
'Disable side resize',
this.disableSideResize,
this.props.state.disableSideResize,
(state, value) => (state.disableSideResize = value)
)}
</>
)}
{this.renderPluginItem('hiddenProperty', 'Hidden Property')}
</tbody>
</table>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { allTextFormats } from './textPresets';
import { htmlParagraphs, mixedParagraphs } from './paragraphPresets';
import { image64x64Black, image64x64Gradient, image64x64White } from './imagePresets';
import { mergedTableNoText, simpleTable, simpleTableWithHeader } from './tablePresets';
import { mixedParagraphs } from './paragraphPresets';
import { numberedList, simpleList } from './listPresets';
import { Preset } from './Preset';
import { undeleteableText } from './undeleteablePresets';
Expand All @@ -24,6 +24,7 @@ export const allPresets: Preset[] = [
simpleList,
numberedList,
mixedParagraphs,
htmlParagraphs,
allTextFormats,
image64x64Gradient,
image64x64Black,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,69 @@
import { Preset } from './Preset';

export const htmlParagraphs: Preset = {
buttonName: 'HTML Paragraphs with margins',
id: 'htmlParagraphs',
content: {
blockGroupType: 'Document',
blocks: [
{
segments: [
{
text: 'line 1',
segmentType: 'Text',
format: {},
},
],
blockType: 'Paragraph',
format: {
marginTop: '1em',
marginBottom: '1em',
},
decorator: {
tagName: 'p',
format: {},
},
},
{
segments: [
{
text: 'line 2',
segmentType: 'Text',
format: {},
},
],
blockType: 'Paragraph',
format: {
marginTop: '1em',
marginBottom: '1em',
},
decorator: {
tagName: 'p',
format: {},
},
},
{
segments: [
{
text: 'line 3',
segmentType: 'Text',
format: {},
},
],
blockType: 'Paragraph',
format: {
marginTop: '1em',
marginBottom: '1em',
},
decorator: {
tagName: 'p',
format: {},
},
},
],
},
};

export const mixedParagraphs: Preset = {
buttonName: 'Mixed Paragraphs',
id: 'mixedParagraphs',
Expand Down
Loading
Loading