From 0f272e7b976eb0c514a33bceea78a37e8a161606 Mon Sep 17 00:00:00 2001 From: "Julia Roldi (from Dev Box)" Date: Fri, 13 Dec 2024 19:08:49 -0300 Subject: [PATCH 01/10] image dataset extract --- .../lib/imageEdit/ImageEditPlugin.ts | 15 ++++++++++++ .../test/imageEdit/ImageEditPluginTest.ts | 23 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/packages/roosterjs-content-model-plugins/lib/imageEdit/ImageEditPlugin.ts b/packages/roosterjs-content-model-plugins/lib/imageEdit/ImageEditPlugin.ts index ec6dfe6047f..2c14e9898dd 100644 --- a/packages/roosterjs-content-model-plugins/lib/imageEdit/ImageEditPlugin.ts +++ b/packages/roosterjs-content-model-plugins/lib/imageEdit/ImageEditPlugin.ts @@ -174,9 +174,24 @@ export class ImageEditPlugin implements ImageEditor, EditorPlugin { this.onDropHandler(this.editor); } break; + case 'extractContentWithDom': + this.removeImageEditing(event.clonedRoot); + break; } } + private removeImageEditing(clonedRoot: HTMLElement) { + const images = clonedRoot.querySelectorAll('img'); + images.forEach(image => { + if (image.dataset.isEditing) { + delete image.dataset.isEditing; + } + if (image.dataset.editingInfo) { + delete image.dataset.editingInfo; + } + }); + } + private isImageSelection(target: Node): target is HTMLElement { return ( isNodeOfType(target, 'ELEMENT_NODE') && diff --git a/packages/roosterjs-content-model-plugins/test/imageEdit/ImageEditPluginTest.ts b/packages/roosterjs-content-model-plugins/test/imageEdit/ImageEditPluginTest.ts index 9fa5393fe0d..2bcea8bec9c 100644 --- a/packages/roosterjs-content-model-plugins/test/imageEdit/ImageEditPluginTest.ts +++ b/packages/roosterjs-content-model-plugins/test/imageEdit/ImageEditPluginTest.ts @@ -3,6 +3,7 @@ import * as findImage from '../../lib/imageEdit/utils/findEditingImage'; import * as getSelectedImage from '../../lib/imageEdit/utils/getSelectedImage'; import * as normalizeImageSelection from '../../lib/imageEdit/utils/normalizeImageSelection'; import { ChangeSource, createImage, createParagraph } from 'roosterjs-content-model-dom'; +import { ExtractContentWithDomEvent } from 'roosterjs-editor-types'; import { getSelectedImageMetadata } from '../../lib/imageEdit/utils/updateImageEditInfo'; import { ImageEditPlugin } from '../../lib/imageEdit/ImageEditPlugin'; import { initEditor } from '../TestHelper'; @@ -648,6 +649,28 @@ describe('ImageEditPlugin', () => { expect(editor.setEditorStyle).toHaveBeenCalledWith('imageEdit', null); expect(editor.setEditorStyle).toHaveBeenCalledWith('imageEditCaretColor', null); }); + + it('extractContentWithDom', () => { + const plugin = new ImageEditPlugin(); + plugin.initialize(editor); + const clonedRoot = document.createElement('div'); + const image = document.createElement('img'); + clonedRoot.appendChild(image); + image.dataset['editingInfo'] = JSON.stringify({ + src: 'test', + }); + image.dataset['isEditing'] = 'true'; + const event = { + eventType: 'extractContentWithDom', + clonedRoot, + } as any; + plugin.onPluginEvent(event); + const expectedClonedRoot = document.createElement('div'); + const expectedImage = document.createElement('img'); + expectedClonedRoot.appendChild(expectedImage); + expect(event.clonedRoot).toEqual(expectedClonedRoot); + plugin.dispose(); + }); }); class TestPlugin extends ImageEditPlugin { From d78bba2895598ba0666e1ec41c178902c7bf7cb2 Mon Sep 17 00:00:00 2001 From: "Julia Roldi (from Dev Box)" Date: Fri, 13 Dec 2024 19:15:07 -0300 Subject: [PATCH 02/10] refactor --- .../test/imageEdit/ImageEditPluginTest.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/roosterjs-content-model-plugins/test/imageEdit/ImageEditPluginTest.ts b/packages/roosterjs-content-model-plugins/test/imageEdit/ImageEditPluginTest.ts index 2bcea8bec9c..1bc97e1075c 100644 --- a/packages/roosterjs-content-model-plugins/test/imageEdit/ImageEditPluginTest.ts +++ b/packages/roosterjs-content-model-plugins/test/imageEdit/ImageEditPluginTest.ts @@ -3,7 +3,6 @@ import * as findImage from '../../lib/imageEdit/utils/findEditingImage'; import * as getSelectedImage from '../../lib/imageEdit/utils/getSelectedImage'; import * as normalizeImageSelection from '../../lib/imageEdit/utils/normalizeImageSelection'; import { ChangeSource, createImage, createParagraph } from 'roosterjs-content-model-dom'; -import { ExtractContentWithDomEvent } from 'roosterjs-editor-types'; import { getSelectedImageMetadata } from '../../lib/imageEdit/utils/updateImageEditInfo'; import { ImageEditPlugin } from '../../lib/imageEdit/ImageEditPlugin'; import { initEditor } from '../TestHelper'; From 61f4923d86fc10510a2aad764344e0da4921bb18 Mon Sep 17 00:00:00 2001 From: Bryan Valverde U Date: Mon, 16 Dec 2024 11:24:12 -0600 Subject: [PATCH 03/10] Resume triggering the BeforePasteEvent when Paste Type is equal to PlainText (#2911) * Trigger BeforePaste on PlainText * Fix tests --- .../paste/generatePasteOptionFromPlugins.ts | 4 +-- .../generatePasteOptionFromPluginsTest.ts | 26 +++++++------------ .../test/command/paste/pasteTest.ts | 20 +++++++------- 3 files changed, 21 insertions(+), 29 deletions(-) diff --git a/packages/roosterjs-content-model-core/lib/command/paste/generatePasteOptionFromPlugins.ts b/packages/roosterjs-content-model-core/lib/command/paste/generatePasteOptionFromPlugins.ts index 3cd712671cc..6549f0b5446 100644 --- a/packages/roosterjs-content-model-core/lib/command/paste/generatePasteOptionFromPlugins.ts +++ b/packages/roosterjs-content-model-core/lib/command/paste/generatePasteOptionFromPlugins.ts @@ -39,7 +39,5 @@ export function generatePasteOptionFromPlugins( containsBlockElements: !!htmlFromClipboard.containsBlockElements, }; - return pasteType == 'asPlainText' - ? event - : editor.triggerEvent('beforePaste', event, true /* broadcast */); + return editor.triggerEvent('beforePaste', event, true /* broadcast */); } diff --git a/packages/roosterjs-content-model-core/test/command/paste/generatePasteOptionFromPluginsTest.ts b/packages/roosterjs-content-model-core/test/command/paste/generatePasteOptionFromPluginsTest.ts index 7c434d19259..6377bb3e13a 100644 --- a/packages/roosterjs-content-model-core/test/command/paste/generatePasteOptionFromPluginsTest.ts +++ b/packages/roosterjs-content-model-core/test/command/paste/generatePasteOptionFromPluginsTest.ts @@ -221,6 +221,8 @@ describe('generatePasteOptionFromPlugins', () => { it('PasteType=asPlainText', () => { triggerPluginEventSpy.and.callFake((core, event) => { Object.assign(event, mockedResult); + + return event; }); const result = generatePasteOptionFromPlugins( editor, @@ -236,24 +238,16 @@ describe('generatePasteOptionFromPlugins', () => { ); expect(result).toEqual({ - fragment: mockedFragment, - domToModelOption: { - additionalAllowedTags: [], - additionalDisallowedTags: [], - additionalFormatParsers: {}, - formatParserOverride: {}, - processorOverride: {}, - styleSanitizers: {}, - attributeSanitizers: {}, - }, - pasteType: 'asPlainText', eventType: 'beforePaste', - clipboardData: mockedClipboardData, - htmlBefore, - htmlAfter, - htmlAttributes: mockedMetadata, + clipboardData: 'CLIPBOARDDATA' as any, + fragment: 'FragmentResult' as any, + htmlBefore: 'HTMLBEFORE', + htmlAfter: 'HTMLAFTER', + htmlAttributes: 'METADATA' as any, + pasteType: 'TypeResult' as any, + domToModelOption: 'OptionResult' as any, containsBlockElements: false, }); - expect(triggerPluginEventSpy).toHaveBeenCalledTimes(0); + expect(triggerPluginEventSpy).toHaveBeenCalledTimes(1); }); }); diff --git a/packages/roosterjs-content-model-core/test/command/paste/pasteTest.ts b/packages/roosterjs-content-model-core/test/command/paste/pasteTest.ts index 2230d485c73..4e0cf9cf5f4 100644 --- a/packages/roosterjs-content-model-core/test/command/paste/pasteTest.ts +++ b/packages/roosterjs-content-model-core/test/command/paste/pasteTest.ts @@ -223,9 +223,9 @@ describe('paste with content model & paste plugin', () => { paste(editor!, clipboardData, 'asPlainText'); - expect(setProcessorF.setProcessor).toHaveBeenCalledTimes(0); - expect(addParserF.addParser).toHaveBeenCalledTimes(0); - expect(WordDesktopFile.processPastedContentFromWordDesktop).toHaveBeenCalledTimes(0); + expect(setProcessorF.setProcessor).toHaveBeenCalledTimes(1); + expect(addParserF.addParser).toHaveBeenCalledTimes(9); + expect(WordDesktopFile.processPastedContentFromWordDesktop).toHaveBeenCalledTimes(1); }); it('Word Online | Plain Text', () => { @@ -234,9 +234,9 @@ describe('paste with content model & paste plugin', () => { paste(editor!, clipboardData, 'asPlainText'); - expect(setProcessorF.setProcessor).toHaveBeenCalledTimes(0); - expect(addParserF.addParser).toHaveBeenCalledTimes(0); - expect(WacComponents.processPastedContentWacComponents).toHaveBeenCalledTimes(0); + expect(setProcessorF.setProcessor).toHaveBeenCalledTimes(2); + expect(addParserF.addParser).toHaveBeenCalledTimes(11); + expect(WacComponents.processPastedContentWacComponents).toHaveBeenCalledTimes(1); }); it('Excel Online | Plain Text', () => { @@ -246,7 +246,7 @@ describe('paste with content model & paste plugin', () => { paste(editor!, clipboardData, 'asPlainText'); expect(setProcessorF.setProcessor).toHaveBeenCalledTimes(0); - expect(addParserF.addParser).toHaveBeenCalledTimes(0); + expect(addParserF.addParser).toHaveBeenCalledTimes(4); expect(ExcelF.processPastedContentFromExcel).toHaveBeenCalledTimes(0); }); @@ -257,7 +257,7 @@ describe('paste with content model & paste plugin', () => { paste(editor!, clipboardData, 'asPlainText'); expect(setProcessorF.setProcessor).toHaveBeenCalledTimes(0); - expect(addParserF.addParser).toHaveBeenCalledTimes(0); + expect(addParserF.addParser).toHaveBeenCalledTimes(4); expect(ExcelF.processPastedContentFromExcel).toHaveBeenCalledTimes(0); }); @@ -268,8 +268,8 @@ describe('paste with content model & paste plugin', () => { paste(editor!, clipboardData, 'asPlainText'); expect(setProcessorF.setProcessor).toHaveBeenCalledTimes(0); - expect(addParserF.addParser).toHaveBeenCalledTimes(0); - expect(PPT.processPastedContentFromPowerPoint).toHaveBeenCalledTimes(0); + expect(addParserF.addParser).toHaveBeenCalledTimes(4); + expect(PPT.processPastedContentFromPowerPoint).toHaveBeenCalledTimes(1); }); it('Verify the event data is not lost', () => { From 9415c682d5ff2a20a456b939ff91c4a4e3eb4015 Mon Sep 17 00:00:00 2001 From: "Julia Roldi (from Dev Box)" Date: Fri, 20 Dec 2024 17:37:11 -0300 Subject: [PATCH 04/10] table aria --- .../demoButtons/tableTitleButton.ts | 56 +++++++++++++ demo/scripts/controlsV2/tabs/ribbonButtons.ts | 3 + .../common/ariaFormatHandler.ts | 26 ++++++ .../formatHandlers/defaultFormatHandlers.ts | 3 + .../common/ariaFormatHandlerTest.ts | 81 +++++++++++++++++++ .../format/ContentModelTableFormat.ts | 2 + .../format/FormatHandlerTypeMap.ts | 6 ++ .../format/formatParts/AriaFormat.ts | 14 ++++ .../lib/index.ts | 1 + 9 files changed, 192 insertions(+) create mode 100644 demo/scripts/controlsV2/demoButtons/tableTitleButton.ts create mode 100644 packages/roosterjs-content-model-dom/lib/formatHandlers/common/ariaFormatHandler.ts create mode 100644 packages/roosterjs-content-model-dom/test/formatHandlers/common/ariaFormatHandlerTest.ts create mode 100644 packages/roosterjs-content-model-types/lib/contentModel/format/formatParts/AriaFormat.ts diff --git a/demo/scripts/controlsV2/demoButtons/tableTitleButton.ts b/demo/scripts/controlsV2/demoButtons/tableTitleButton.ts new file mode 100644 index 00000000000..8632bb1fcc2 --- /dev/null +++ b/demo/scripts/controlsV2/demoButtons/tableTitleButton.ts @@ -0,0 +1,56 @@ +import { getFirstSelectedTable, mutateBlock } from 'roosterjs-content-model-dom'; +import { IEditor } from 'roosterjs-content-model-types'; +import { RibbonButton, showInputDialog } from 'roosterjs-react'; + +/** + * @internal + * "Image Border Style" button on the format ribbon + */ +export const tableTitleButton: RibbonButton<'buttonNameTableTitle'> = { + key: 'buttonNameTableTitle', + unlocalizedText: 'Table Title', + iconName: 'TableComputed', + isDisabled: formatState => !formatState.isInTable, + onClick: (editor, _, strings, uiUtilities) => { + const items = { + title: { + autoFocus: true, + labelKey: 'buttonNameTableTitle' as const, + unlocalizedLabel: 'Title', + initValue: '', + }, + }; + + showInputDialog( + uiUtilities, + 'buttonNameTableTitle', + 'Insert Table', + items, + strings, + (itemName, newValue, values) => { + if (itemName == 'title') { + values.title = newValue; + return values; + } else { + return null; + } + } + ).then(result => { + editor.focus(); + if (result && result.title) { + insertTableTitle(editor, result.title); + } + }); + }, +}; + +const insertTableTitle = (editor: IEditor, title: string) => { + editor.formatContentModel(model => { + const table = getFirstSelectedTable(model)[0]; + if (table) { + mutateBlock(table).format.title = title; + return true; + } + return false; + }); +}; diff --git a/demo/scripts/controlsV2/tabs/ribbonButtons.ts b/demo/scripts/controlsV2/tabs/ribbonButtons.ts index 6911f0aa9c5..af669dffd00 100644 --- a/demo/scripts/controlsV2/tabs/ribbonButtons.ts +++ b/demo/scripts/controlsV2/tabs/ribbonButtons.ts @@ -21,6 +21,7 @@ import { tableBorderColorButton } from '../demoButtons/tableBorderColorButton'; import { tableBorderStyleButton } from '../demoButtons/tableBorderStyleButton'; import { tableBorderWidthButton } from '../demoButtons/tableBorderWidthButton'; import { tableOptionsButton } from '../demoButtons/tableOptionsButton'; +import { tableTitleButton } from '../demoButtons/tableTitleButton'; import { tabNames } from './getTabs'; import { tableAlignCellButton, @@ -83,6 +84,7 @@ const tableButtons: RibbonButton[] = [ insertTableButton, formatTableButton, setTableCellShadeButton, + tableTitleButton, tableOptionsButton, tableInsertButton, tableDeleteButton, @@ -178,6 +180,7 @@ const allButtons: RibbonButton[] = [ tableDeleteButton, tableMergeButton, tableSplitButton, + tableTitleButton, tableAlignCellButton, tableAlignTableButton, tableBorderApplyButton, diff --git a/packages/roosterjs-content-model-dom/lib/formatHandlers/common/ariaFormatHandler.ts b/packages/roosterjs-content-model-dom/lib/formatHandlers/common/ariaFormatHandler.ts new file mode 100644 index 00000000000..a612375bbdc --- /dev/null +++ b/packages/roosterjs-content-model-dom/lib/formatHandlers/common/ariaFormatHandler.ts @@ -0,0 +1,26 @@ +import type { AriaFormat } from 'roosterjs-content-model-types'; +import type { FormatHandler } from '../FormatHandler'; + +/** + * @internal + */ +export const ariaFormatHandler: FormatHandler = { + parse: (format, element) => { + const ariaDescribedBy = element.getAttribute('aria-describedby'); + const title = element.getAttribute('title'); + if (ariaDescribedBy) { + format.ariaDescribedBy = ariaDescribedBy; + } + if (title) { + format.title = title; + } + }, + apply: (format, element) => { + if (format.ariaDescribedBy) { + element.setAttribute('aria-describedby', format.ariaDescribedBy); + } + if (format.title) { + element.setAttribute('title', format.title); + } + }, +}; diff --git a/packages/roosterjs-content-model-dom/lib/formatHandlers/defaultFormatHandlers.ts b/packages/roosterjs-content-model-dom/lib/formatHandlers/defaultFormatHandlers.ts index 4d2ac993d31..d90edb198cf 100644 --- a/packages/roosterjs-content-model-dom/lib/formatHandlers/defaultFormatHandlers.ts +++ b/packages/roosterjs-content-model-dom/lib/formatHandlers/defaultFormatHandlers.ts @@ -1,3 +1,4 @@ +import { ariaFormatHandler } from './common/ariaFormatHandler'; import { backgroundColorFormatHandler } from './common/backgroundColorFormatHandler'; import { boldFormatHandler } from './segment/boldFormatHandler'; import { borderBoxFormatHandler } from './common/borderBoxFormatHandler'; @@ -51,6 +52,7 @@ type FormatHandlers = { }; const defaultFormatHandlerMap: FormatHandlers = { + aria: ariaFormatHandler, backgroundColor: backgroundColorFormatHandler, bold: boldFormatHandler, border: borderFormatHandler, @@ -162,6 +164,7 @@ export const defaultFormatKeysPerCategory: { tableRow: ['backgroundColor'], tableColumn: ['size'], table: [ + 'aria', 'id', 'border', 'backgroundColor', diff --git a/packages/roosterjs-content-model-dom/test/formatHandlers/common/ariaFormatHandlerTest.ts b/packages/roosterjs-content-model-dom/test/formatHandlers/common/ariaFormatHandlerTest.ts new file mode 100644 index 00000000000..e7412e82601 --- /dev/null +++ b/packages/roosterjs-content-model-dom/test/formatHandlers/common/ariaFormatHandlerTest.ts @@ -0,0 +1,81 @@ +import { AriaFormat, DomToModelContext, ModelToDomContext } from 'roosterjs-content-model-types'; +import { ariaFormatHandler } from '../../../lib/formatHandlers/common/ariaFormatHandler'; +import { createDomToModelContext } from '../../../lib/domToModel/context/createDomToModelContext'; +import { createModelToDomContext } from '../../../lib/modelToDom/context/createModelToDomContext'; + +describe('ariaFormatHandler.parse', () => { + let div: HTMLElement; + let format: AriaFormat; + let context: DomToModelContext; + + beforeEach(() => { + div = document.createElement('div'); + format = {}; + context = createDomToModelContext(); + }); + + it('No title and describedby', () => { + ariaFormatHandler.parse(format, div, context, {}); + expect(format).toEqual({}); + }); + + it('has title and describedby', () => { + div.setAttribute('title', 'test'); + div.setAttribute('aria-describedby', 'test'); + ariaFormatHandler.parse(format, div, context, {}); + expect(format).toEqual({ + title: 'test', + ariaDescribedBy: 'test', + }); + }); + + it('has title and no describedby', () => { + div.setAttribute('title', 'test'); + ariaFormatHandler.parse(format, div, context, {}); + expect(format).toEqual({ + title: 'test', + }); + }); + + it('no title and has describedby', () => { + div.setAttribute('aria-describedby', 'test'); + ariaFormatHandler.parse(format, div, context, {}); + expect(format).toEqual({ ariaDescribedBy: 'test' }); + }); +}); + +describe('idFormatHandler.apply', () => { + let div: HTMLElement; + let format: AriaFormat; + let context: ModelToDomContext; + + beforeEach(() => { + div = document.createElement('div'); + format = {}; + context = createModelToDomContext(); + }); + + it('No title and no describedby', () => { + ariaFormatHandler.apply(format, div, context); + expect(div.outerHTML).toBe('
'); + }); + + it('Has title and has describedby', () => { + format.title = 'test'; + format.ariaDescribedBy = 'test'; + ariaFormatHandler.apply(format, div, context); + expect(div.outerHTML).toBe('
'); + }); + + it('No title and has describedby', () => { + format.ariaDescribedBy = 'test'; + ariaFormatHandler.apply(format, div, context); + expect(div.outerHTML).toBe('
'); + }); + + it('Has title and no describedby', () => { + format.title = 'test'; + ariaFormatHandler.apply(format, div, context); + expect(div.outerHTML).toBe('
'); + }); +}); diff --git a/packages/roosterjs-content-model-types/lib/contentModel/format/ContentModelTableFormat.ts b/packages/roosterjs-content-model-types/lib/contentModel/format/ContentModelTableFormat.ts index 7d84b3fe376..fe9a27187e3 100644 --- a/packages/roosterjs-content-model-types/lib/contentModel/format/ContentModelTableFormat.ts +++ b/packages/roosterjs-content-model-types/lib/contentModel/format/ContentModelTableFormat.ts @@ -1,3 +1,4 @@ +import type { AriaFormat } from './formatParts/AriaFormat'; import type { BorderBoxFormat } from './formatParts/BorderBoxFormat'; import type { BorderFormat } from './formatParts/BorderFormat'; import type { ContentModelBlockFormat } from './ContentModelBlockFormat'; @@ -13,6 +14,7 @@ import type { SizeFormat } from './formatParts/SizeFormat'; */ export type ContentModelTableFormat = ContentModelBlockFormat & IdFormat & + AriaFormat & BorderFormat & BorderBoxFormat & SpacingFormat & diff --git a/packages/roosterjs-content-model-types/lib/contentModel/format/FormatHandlerTypeMap.ts b/packages/roosterjs-content-model-types/lib/contentModel/format/FormatHandlerTypeMap.ts index d8c851be670..463b5001a36 100644 --- a/packages/roosterjs-content-model-types/lib/contentModel/format/FormatHandlerTypeMap.ts +++ b/packages/roosterjs-content-model-types/lib/contentModel/format/FormatHandlerTypeMap.ts @@ -1,3 +1,4 @@ +import type { AriaFormat } from './formatParts/AriaFormat'; import type { BackgroundColorFormat } from './formatParts/BackgroundColorFormat'; import type { BoldFormat } from './formatParts/BoldFormat'; import type { BorderBoxFormat } from './formatParts/BorderBoxFormat'; @@ -37,6 +38,11 @@ import type { WordBreakFormat } from './formatParts/WordBreakFormat'; * Represents a record of all format handlers */ export interface FormatHandlerTypeMap { + /** + * Format for AriaFormat + */ + aria: AriaFormat; + /** * Format for BackgroundColorFormat */ diff --git a/packages/roosterjs-content-model-types/lib/contentModel/format/formatParts/AriaFormat.ts b/packages/roosterjs-content-model-types/lib/contentModel/format/formatParts/AriaFormat.ts new file mode 100644 index 00000000000..b30c08e0243 --- /dev/null +++ b/packages/roosterjs-content-model-types/lib/contentModel/format/formatParts/AriaFormat.ts @@ -0,0 +1,14 @@ +/** + * Format of background color + */ +export type AriaFormat = { + /** + * Aria-describedby attribute + */ + ariaDescribedBy?: string; + + /** + * Title attribute + */ + title?: string; +}; diff --git a/packages/roosterjs-content-model-types/lib/index.ts b/packages/roosterjs-content-model-types/lib/index.ts index 06720540376..a2ddd2d4fe3 100644 --- a/packages/roosterjs-content-model-types/lib/index.ts +++ b/packages/roosterjs-content-model-types/lib/index.ts @@ -23,6 +23,7 @@ export { ContentModelImageFormat } from './contentModel/format/ContentModelImage export { ContentModelEntityFormat } from './contentModel/format/ContentModelEntityFormat'; export { FormatHandlerTypeMap, FormatKey } from './contentModel/format/FormatHandlerTypeMap'; +export { AriaFormat } from './contentModel/format/formatParts/AriaFormat'; export { BackgroundColorFormat } from './contentModel/format/formatParts/BackgroundColorFormat'; export { BoldFormat } from './contentModel/format/formatParts/BoldFormat'; export { FontFamilyFormat } from './contentModel/format/formatParts/FontFamilyFormat'; From 0bc2578ad74d42f39b42c1ba6c1403e635fa1901 Mon Sep 17 00:00:00 2001 From: Jiuqing Song Date: Tue, 7 Jan 2025 10:35:36 -0800 Subject: [PATCH 05/10] Support MixedPlugin (#2917) --- .../lib/corePlugins/BridgePlugin.ts | 34 ++++++- .../roosterjs-editor-adapter/lib/index.ts | 1 + .../lib/publicTypes/MixedPlugin.ts | 35 +++++++ .../test/corePlugins/BridgePluginTest.ts | 97 +++++++++++++++++++ 4 files changed, 164 insertions(+), 3 deletions(-) create mode 100644 packages/roosterjs-editor-adapter/lib/publicTypes/MixedPlugin.ts diff --git a/packages/roosterjs-editor-adapter/lib/corePlugins/BridgePlugin.ts b/packages/roosterjs-editor-adapter/lib/corePlugins/BridgePlugin.ts index 0eb1d7a106a..5ddc1f36732 100644 --- a/packages/roosterjs-editor-adapter/lib/corePlugins/BridgePlugin.ts +++ b/packages/roosterjs-editor-adapter/lib/corePlugins/BridgePlugin.ts @@ -5,6 +5,7 @@ import { IgnoredPluginNames } from '../editor/IgnoredPluginNames'; import { newEventToOldEvent, oldEventToNewEvent } from '../editor/utils/eventConverter'; import type { EditorPlugin as LegacyEditorPlugin, + PluginEvent as LegacyPluginEvent, ContextMenuProvider as LegacyContextMenuProvider, IEditor as ILegacyEditor, ExperimentalFeatures, @@ -14,6 +15,7 @@ import type { DarkColorHandler, } from 'roosterjs-editor-types'; import type { ContextMenuProvider, IEditor, PluginEvent } from 'roosterjs-content-model-types'; +import type { MixedPlugin } from '../publicTypes/MixedPlugin'; const ExclusivelyHandleEventPluginKey = '__ExclusivelyHandleEventPlugin'; const OldEventKey = '__OldEventFromNewEvent'; @@ -97,7 +99,13 @@ export class BridgePlugin implements ContextMenuProvider { initialize(editor: IEditor) { const outerEditor = this.onInitialize(this.createEditorCore(editor)); - this.legacyPlugins.forEach(plugin => plugin.initialize(outerEditor)); + this.legacyPlugins.forEach(plugin => { + plugin.initialize(outerEditor); + + if (isMixedPlugin(plugin)) { + plugin.initializeV9(editor); + } + }); } /** @@ -122,9 +130,9 @@ export class BridgePlugin implements ContextMenuProvider { const exclusivelyHandleEventPlugin = this.cacheGetExclusivelyHandlePlugin(event); if (exclusivelyHandleEventPlugin) { - exclusivelyHandleEventPlugin.onPluginEvent?.(oldEvent); + this.handleEvent(exclusivelyHandleEventPlugin, oldEvent, event); } else { - this.legacyPlugins.forEach(plugin => plugin.onPluginEvent?.(oldEvent)); + this.legacyPlugins.forEach(plugin => this.handleEvent(plugin, oldEvent, event)); } Object.assign(event, oldEventToNewEvent(oldEvent, event)); @@ -164,6 +172,10 @@ export class BridgePlugin implements ContextMenuProvider { if (plugin.willHandleEventExclusively?.(oldEvent)) { return plugin; } + + if (isMixedPlugin(plugin) && plugin.willHandleEventExclusivelyV9?.(event)) { + return plugin; + } } } @@ -185,6 +197,18 @@ export class BridgePlugin implements ContextMenuProvider { contextMenuProviders: this.contextMenuProviders, }; } + + private handleEvent( + plugin: LegacyEditorPlugin, + oldEvent: LegacyPluginEvent, + newEvent: PluginEvent + ) { + plugin.onPluginEvent?.(oldEvent); + + if (isMixedPlugin(plugin)) { + plugin.onPluginEventV9?.(newEvent); + } + } } /** @@ -200,3 +224,7 @@ function isContextMenuProvider( ): source is LegacyContextMenuProvider { return !!(>source)?.getContextMenuItems; } + +function isMixedPlugin(plugin: LegacyEditorPlugin): plugin is MixedPlugin { + return !!(plugin as MixedPlugin).initializeV9; +} diff --git a/packages/roosterjs-editor-adapter/lib/index.ts b/packages/roosterjs-editor-adapter/lib/index.ts index 3646c7e890c..96ced6346a6 100644 --- a/packages/roosterjs-editor-adapter/lib/index.ts +++ b/packages/roosterjs-editor-adapter/lib/index.ts @@ -1,5 +1,6 @@ export { EditorAdapterOptions } from './publicTypes/EditorAdapterOptions'; export { BeforePasteAdapterEvent } from './publicTypes/BeforePasteAdapterEvent'; +export { MixedPlugin } from './publicTypes/MixedPlugin'; export { EditorAdapter } from './editor/EditorAdapter'; diff --git a/packages/roosterjs-editor-adapter/lib/publicTypes/MixedPlugin.ts b/packages/roosterjs-editor-adapter/lib/publicTypes/MixedPlugin.ts new file mode 100644 index 00000000000..5618b180058 --- /dev/null +++ b/packages/roosterjs-editor-adapter/lib/publicTypes/MixedPlugin.ts @@ -0,0 +1,35 @@ +import type { PluginEvent, IEditor } from 'roosterjs-content-model-types'; +import type { EditorPlugin } from 'roosterjs-editor-types'; + +/** + * Represents a mixed version plugin that can handle both v8 and v9 events. + * This is not commonly used, but just for transitioning from v8 to v9 plugins + */ +export interface MixedPlugin extends EditorPlugin { + /** + * The first method that editor will call to a plugin when editor is initializing. + * It will pass in the editor instance, plugin should take this chance to save the + * editor reference so that it can call to any editor method or format API later. + * @param editor The editor object + */ + + initializeV9: (editor: IEditor) => void; + + /** + * Check if the plugin should handle the given event exclusively. + * Handle an event exclusively means other plugin will not receive this event in + * onPluginEvent method. + * If two plugins will return true in willHandleEventExclusively() for the same event, + * the final result depends on the order of the plugins are added into editor + * @param event The event to check: + */ + willHandleEventExclusivelyV9?: (event: PluginEvent) => boolean; + + /** + * Core method for a plugin. Once an event happens in editor, editor will call this + * method of each plugin to handle the event as long as the event is not handled + * exclusively by another plugin. + * @param event The event to handle: + */ + onPluginEventV9?: (event: PluginEvent) => void; +} diff --git a/packages/roosterjs-editor-adapter/test/corePlugins/BridgePluginTest.ts b/packages/roosterjs-editor-adapter/test/corePlugins/BridgePluginTest.ts index 7011ce96419..a0db7f68882 100644 --- a/packages/roosterjs-editor-adapter/test/corePlugins/BridgePluginTest.ts +++ b/packages/roosterjs-editor-adapter/test/corePlugins/BridgePluginTest.ts @@ -511,4 +511,101 @@ describe('BridgePlugin', () => { expect(disposeSpy).toHaveBeenCalledTimes(2); }); + + it('MixedPlugin', () => { + const initializeV8Spy = jasmine.createSpy('initializeV8'); + const initializeV9Spy = jasmine.createSpy('initializeV9'); + const onPluginEventV8Spy = jasmine.createSpy('onPluginEventV8'); + const onPluginEventV9Spy = jasmine.createSpy('onPluginEventV9'); + const willHandleEventExclusivelyV8Spy = jasmine.createSpy('willHandleEventExclusivelyV8'); + const willHandleEventExclusivelyV9Spy = jasmine.createSpy('willHandleEventExclusivelyV9'); + const disposeSpy = jasmine.createSpy('dispose'); + + const mockedPlugin = { + initialize: initializeV8Spy, + initializeV9: initializeV9Spy, + onPluginEvent: onPluginEventV8Spy, + onPluginEventV9: onPluginEventV9Spy, + willHandleEventExclusively: willHandleEventExclusivelyV8Spy, + willHandleEventExclusivelyV9: willHandleEventExclusivelyV9Spy, + dispose: disposeSpy, + getName: () => '', + } as any; + const mockedEditor = {} as any; + const onInitializeSpy = jasmine.createSpy('onInitialize').and.returnValue(mockedEditor); + const plugin = new BridgePlugin.BridgePlugin(onInitializeSpy, [mockedPlugin]); + + expect(initializeV8Spy).not.toHaveBeenCalled(); + expect(initializeV9Spy).not.toHaveBeenCalled(); + expect(onPluginEventV8Spy).not.toHaveBeenCalled(); + expect(onPluginEventV9Spy).not.toHaveBeenCalled(); + expect(disposeSpy).not.toHaveBeenCalled(); + expect(onInitializeSpy).not.toHaveBeenCalled(); + expect(willHandleEventExclusivelyV8Spy).not.toHaveBeenCalled(); + expect(willHandleEventExclusivelyV9Spy).not.toHaveBeenCalled(); + + const mockedZoomScale = 'ZOOM' as any; + const calculateZoomScaleSpy = jasmine + .createSpy('calculateZoomScale') + .and.returnValue(mockedZoomScale); + const mockedColorManager = 'COLOR' as any; + const mockedInnerDarkColorHandler = 'INNERCOLOR' as any; + const mockedInnerEditor = { + getDOMHelper: () => ({ + calculateZoomScale: calculateZoomScaleSpy, + }), + getColorManager: () => mockedInnerDarkColorHandler, + } as any; + + const createDarkColorHandlerSpy = spyOn( + DarkColorHandler, + 'createDarkColorHandler' + ).and.returnValue(mockedColorManager); + + plugin.initialize(mockedInnerEditor); + + expect(onInitializeSpy).toHaveBeenCalledWith({ + customData: {}, + experimentalFeatures: [], + sizeTransformer: jasmine.anything(), + darkColorHandler: mockedColorManager, + edit: 'edit', + contextMenuProviders: [], + } as any); + expect(createDarkColorHandlerSpy).toHaveBeenCalledWith(mockedInnerDarkColorHandler); + expect(initializeV8Spy).toHaveBeenCalledTimes(1); + expect(initializeV9Spy).toHaveBeenCalledTimes(1); + expect(disposeSpy).not.toHaveBeenCalled(); + expect(initializeV8Spy).toHaveBeenCalledWith(mockedEditor); + expect(initializeV9Spy).toHaveBeenCalledWith(mockedInnerEditor); + expect(onPluginEventV8Spy).not.toHaveBeenCalled(); + expect(onPluginEventV9Spy).not.toHaveBeenCalled(); + expect(willHandleEventExclusivelyV8Spy).not.toHaveBeenCalled(); + expect(willHandleEventExclusivelyV9Spy).not.toHaveBeenCalled(); + + plugin.onPluginEvent({ + eventType: 'editorReady', + addedBlockElements: [], + removedBlockElements: [], + }); + + expect(onPluginEventV8Spy).toHaveBeenCalledTimes(1); + expect(onPluginEventV9Spy).toHaveBeenCalledTimes(1); + expect(onPluginEventV8Spy).toHaveBeenCalledWith({ + eventType: PluginEventType.EditorReady, + eventDataCache: undefined, + }); + expect(onPluginEventV9Spy).toHaveBeenCalledWith({ + eventType: 'editorReady', + addedBlockElements: [], + removedBlockElements: [], + eventDataCache: undefined, + }); + expect(willHandleEventExclusivelyV8Spy).toHaveBeenCalledTimes(1); + expect(willHandleEventExclusivelyV9Spy).toHaveBeenCalledTimes(1); + + plugin.dispose(); + + expect(disposeSpy).toHaveBeenCalledTimes(1); + }); }); From 93b0cb2f3704c2139d22a342943e1f434811efa9 Mon Sep 17 00:00:00 2001 From: Jiuqing Song Date: Tue, 7 Jan 2025 13:35:23 -0800 Subject: [PATCH 06/10] Fix #2903 Handle watermark for IME (#2913) * Fix #2903 * improve * fix test --- .../lib/watermark/WatermarkPlugin.ts | 39 +++++++++++++------ .../test/watermark/WatermarkPluginTest.ts | 13 +++++++ 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/packages/roosterjs-content-model-plugins/lib/watermark/WatermarkPlugin.ts b/packages/roosterjs-content-model-plugins/lib/watermark/WatermarkPlugin.ts index c64f3cfe3a8..1ab7d186a7f 100644 --- a/packages/roosterjs-content-model-plugins/lib/watermark/WatermarkPlugin.ts +++ b/packages/roosterjs-content-model-plugins/lib/watermark/WatermarkPlugin.ts @@ -18,6 +18,7 @@ export class WatermarkPlugin implements EditorPlugin { private format: WatermarkFormat; private isShowing = false; private darkTextColor: string | null = null; + private disposer: (() => void) | null = null; /** * Create an instance of Watermark plugin @@ -43,12 +44,20 @@ export class WatermarkPlugin implements EditorPlugin { */ initialize(editor: IEditor) { this.editor = editor; + this.disposer = this.editor.attachDomEvent({ + compositionstart: { + beforeDispatch: this.onCompositionStart, + }, + }); } /** * Dispose this plugin */ dispose() { + this.disposer?.(); + this.disposer = null; + this.editor = null; } @@ -63,10 +72,7 @@ export class WatermarkPlugin implements EditorPlugin { return; } - if ( - (event.eventType == 'input' && event.rawEvent.inputType == 'insertText') || - event.eventType == 'compositionEnd' - ) { + if (event.eventType == 'input' && event.rawEvent.inputType == 'insertText') { // When input text, editor must not be empty, so we can do hide watermark now without checking content model this.showHide(editor, false /*isEmpty*/); } else if ( @@ -92,16 +98,27 @@ export class WatermarkPlugin implements EditorPlugin { event.eventType == 'editorReady' || event.eventType == 'contentChanged' || event.eventType == 'input' || - event.eventType == 'beforeDispose' + event.eventType == 'beforeDispose' || + event.eventType == 'compositionEnd' ) { - editor.formatContentModel(model => { - const isEmpty = isModelEmptyFast(model); - - this.showHide(editor, isEmpty); + this.update(editor); + } + } - return false; - }); + private onCompositionStart = () => { + if (this.editor) { + this.showHide(this.editor, false /*isEmpty*/); } + }; + + private update(editor: IEditor) { + editor.formatContentModel(model => { + const isEmpty = isModelEmptyFast(model); + + this.showHide(editor, isEmpty); + + return false; + }); } private showHide(editor: IEditor, isEmpty: boolean) { diff --git a/packages/roosterjs-content-model-plugins/test/watermark/WatermarkPluginTest.ts b/packages/roosterjs-content-model-plugins/test/watermark/WatermarkPluginTest.ts index 94b3775f614..47c32f38872 100644 --- a/packages/roosterjs-content-model-plugins/test/watermark/WatermarkPluginTest.ts +++ b/packages/roosterjs-content-model-plugins/test/watermark/WatermarkPluginTest.ts @@ -8,12 +8,14 @@ describe('WatermarkPlugin', () => { let formatContentModelSpy: jasmine.Spy; let isModelEmptyFastSpy: jasmine.Spy; let setEditorStyleSpy: jasmine.Spy; + let attachDomEventSpy: jasmine.Spy; const mockedModel = 'Model' as any; beforeEach(() => { isModelEmptyFastSpy = spyOn(isModelEmptyFast, 'isModelEmptyFast'); setEditorStyleSpy = jasmine.createSpy('setEditorStyle'); + attachDomEventSpy = jasmine.createSpy('attachDomEvent'); formatContentModelSpy = jasmine .createSpy('formatContentModel') @@ -26,6 +28,7 @@ describe('WatermarkPlugin', () => { formatContentModel: formatContentModelSpy, setEditorStyle: setEditorStyleSpy, isDarkMode: () => false, + attachDomEvent: attachDomEventSpy, } as any; }); @@ -36,6 +39,12 @@ describe('WatermarkPlugin', () => { plugin.initialize(editor); + expect(attachDomEventSpy).toHaveBeenCalledWith({ + compositionstart: { + beforeDispatch: jasmine.anything(), + }, + }); + plugin.onPluginEvent({ eventType: 'editorReady', addedBlockElements: [], @@ -169,6 +178,8 @@ describe('WatermarkPlugin dark mode', () => { let setEditorStyleSpy: jasmine.Spy; let getDarkColorSpy: jasmine.Spy; let isDarkModeSpy: jasmine.Spy; + let attachDomEventSpy: jasmine.Spy; + const DEFAULT_DARK_COLOR_SUFFIX_COLOR = 'DarkColorMock-'; const mockedModel = 'Model' as any; @@ -178,6 +189,7 @@ describe('WatermarkPlugin dark mode', () => { setEditorStyleSpy = jasmine.createSpy('setEditorStyle'); getDarkColorSpy = jasmine.createSpy('getDarkColor'); isDarkModeSpy = jasmine.createSpy('isDarkMode'); + attachDomEventSpy = jasmine.createSpy('attachDomEvent'); formatContentModelSpy = jasmine .createSpy('formatContentModel') @@ -191,6 +203,7 @@ describe('WatermarkPlugin dark mode', () => { formatContentModel: formatContentModelSpy, setEditorStyle: setEditorStyleSpy, isDarkMode: isDarkModeSpy, + attachDomEvent: attachDomEventSpy, getColorManager: () => ({ getDarkColor: getDarkColorSpy.and.callFake((color: string) => { return `${DEFAULT_DARK_COLOR_SUFFIX_COLOR}${color}`; From 2099b7bace2ff8080631c7eff37e4493899a3a86 Mon Sep 17 00:00:00 2001 From: Jiuqing Song Date: Wed, 8 Jan 2025 13:15:35 -0800 Subject: [PATCH 07/10] Fix demo site icons (#2918) --- demo/index.html | 2 +- demo/scripts/utils/cssMonitor.ts | 2 +- index.html | 2 +- package.json | 2 +- .../lib/emoji/components/EmojiIcon.tsx | 4 +- .../lib/emoji/components/EmojiNavBar.tsx | 4 +- .../lib/emoji/components/EmojiPane.tsx | 257 +++++++++--------- .../lib/emoji/components/EmojiStatusBar.tsx | 4 +- .../lib/emoji/type/EmojiPaneStyles.ts | 39 ++- .../lib/ribbon/component/Ribbon.tsx | 8 + tools/buildTools/checkDependency.js | 1 + yarn.lock | 214 ++++++++------- 12 files changed, 287 insertions(+), 252 deletions(-) diff --git a/demo/index.html b/demo/index.html index f655db7f2d4..559202c138e 100644 --- a/demo/index.html +++ b/demo/index.html @@ -20,7 +20,7 @@
- + diff --git a/demo/scripts/utils/cssMonitor.ts b/demo/scripts/utils/cssMonitor.ts index bd0cf25b0f3..dcb09c25dae 100644 --- a/demo/scripts/utils/cssMonitor.ts +++ b/demo/scripts/utils/cssMonitor.ts @@ -1,4 +1,4 @@ -import { Stylesheet } from '@fluentui/merge-styles/lib/Stylesheet'; +import { Stylesheet } from '@fluentui/merge-styles'; let isCssMonitorStarted: boolean = false; const activeWindows: Window[] = []; diff --git a/index.html b/index.html index daf62dcaad3..8befb4fc0e5 100644 --- a/index.html +++ b/index.html @@ -20,7 +20,7 @@
- + diff --git a/package.json b/package.json index 9dab173dd0d..8db4657e41e 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "publish": "node tools/build.js clean normalize buildcommonjs buildamd buildmjs dts pack packprod builddemo builddoc publish" }, "devDependencies": { - "@fluentui/react": "^8.0.0", + "@fluentui/react": "8.122.3", "@microsoft/loader-load-themed-styles": "1.8.11", "@types/color": "3.0.0", "@types/dompurify": "2.2.3", diff --git a/packages/roosterjs-react/lib/emoji/components/EmojiIcon.tsx b/packages/roosterjs-react/lib/emoji/components/EmojiIcon.tsx index 8b2c4d4cc57..a907ff8257f 100644 --- a/packages/roosterjs-react/lib/emoji/components/EmojiIcon.tsx +++ b/packages/roosterjs-react/lib/emoji/components/EmojiIcon.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { css } from '@fluentui/react/lib/Utilities'; import type { Emoji } from '../type/Emoji'; import type { EmojiPaneStyle } from '../type/EmojiPaneStyles'; -import type { IProcessedStyleSet, IStyleSet } from '@fluentui/react/lib/Styling'; +import type { IProcessedStyleSet } from '@fluentui/react/lib/Styling'; /** * @internal * Emoji icon data @@ -11,7 +11,7 @@ export interface EmojiIconProps { id: string; emoji: Emoji; strings: Record; - classNames: IProcessedStyleSet>; + classNames: IProcessedStyleSet; onClick?: (e: React.MouseEvent) => void; onMouseOver?: (e: React.MouseEvent) => void; onFocus?: React.FocusEventHandler; diff --git a/packages/roosterjs-react/lib/emoji/components/EmojiNavBar.tsx b/packages/roosterjs-react/lib/emoji/components/EmojiNavBar.tsx index 3c81f8f397b..e09bcd2d944 100644 --- a/packages/roosterjs-react/lib/emoji/components/EmojiNavBar.tsx +++ b/packages/roosterjs-react/lib/emoji/components/EmojiNavBar.tsx @@ -7,7 +7,7 @@ import { Icon } from '@fluentui/react/lib/Icon'; import { TooltipHost } from '@fluentui/react/lib/Tooltip'; import type { EmojiFamilyKeys } from '../utils/emojiList'; import type { EmojiPaneStyle } from '../type/EmojiPaneStyles'; -import type { IProcessedStyleSet, IStyleSet } from '@fluentui/react/lib/Styling'; +import type { IProcessedStyleSet } from '@fluentui/react/lib/Styling'; /** * @internal @@ -18,7 +18,7 @@ export interface EmojiNavBarProps { currentSelected?: string; getTabId?: (selected: EmojiFamilyKeys) => string; strings: Record; - classNames: IProcessedStyleSet>; + classNames: IProcessedStyleSet; } /** diff --git a/packages/roosterjs-react/lib/emoji/components/EmojiPane.tsx b/packages/roosterjs-react/lib/emoji/components/EmojiPane.tsx index 50c855cd67a..157dec79444 100644 --- a/packages/roosterjs-react/lib/emoji/components/EmojiPane.tsx +++ b/packages/roosterjs-react/lib/emoji/components/EmojiPane.tsx @@ -11,6 +11,8 @@ import { mergeStyleSets } from '@fluentui/react/lib/Styling'; import { searchEmojis } from '../utils/searchEmojis'; import { TextField } from '@fluentui/react/lib/TextField'; import { useTheme } from '@fluentui/react/lib/Theme'; +import type { EmojiPaneStyle } from '../type/EmojiPaneStyles'; +import type { IProcessedStyleSet } from '@fluentui/react/lib/Styling'; import type { EmojiFamilyKeys } from '../utils/emojiList'; import type { EmojiIconProps } from './EmojiIcon'; import type { EmojiNavBarProps } from './EmojiNavBar'; @@ -607,149 +609,152 @@ const calcPaneWidth = () => { return paneWidth.toString() + 'px'; }; -const getEmojiPaneClassName = memoizeFunction((theme: Theme) => { - const palette = theme.palette; - return mergeStyleSets({ - quickPicker: { - overflowY: 'hidden', - ':after': { - content: '', - position: 'absolute', - left: '0px', - top: '0px', - bottom: '0px', - right: '0px', - zIndex: 1, - borderWidth: '1px', - borderStyle: 'solid', - borderColor: 'rgb(255, 255, 255)', - borderImage: 'initial', - outline: 'rgb(102, 102, 102) solid 1px', +const getEmojiPaneClassName = memoizeFunction( + (theme: Theme): IProcessedStyleSet => { + const palette = theme.palette; + const styles: EmojiPaneStyle = { + quickPicker: { + overflowY: 'hidden', + ':after': { + content: '', + position: 'absolute', + left: '0px', + top: '0px', + bottom: '0px', + right: '0px', + zIndex: 1, + borderWidth: '1px', + borderStyle: 'solid', + borderColor: 'rgb(255, 255, 255)', + borderImage: 'initial', + outline: 'rgb(102, 102, 102) solid 1px', + }, }, - }, - tooltip: { - padding: '8px', - }, + tooltip: { + padding: '8px', + }, - emojiTextInput: { - padding: '6px', - }, + emojiTextInput: { + padding: '6px', + }, - partialList: { - maxHeight: calcMaxHeight(), - overflow: 'hidden', - overflowY: 'scroll', - }, + partialList: { + maxHeight: calcMaxHeight(), + overflow: 'hidden', + overflowY: 'scroll', + }, - fullListContent: { - width: calcPaneWidth(), - }, + fullListContent: { + width: calcPaneWidth(), + }, - fullListBody: { - maxHeight: calcMaxHeight(), - overflow: 'hidden', - overflowY: 'scroll', - height: calcMaxHeight(), - }, + fullListBody: { + maxHeight: calcMaxHeight(), + overflow: 'hidden', + overflowY: 'scroll', + height: calcMaxHeight(), + }, - fullList: { - position: 'relative', - }, + fullList: { + position: 'relative', + }, - roosterEmojiPane: { - padding: '1px', - background: palette.white, - }, + roosterEmojiPane: { + padding: '1px', + background: palette.white, + }, - emoji: { - fontSize: '18px', - width: '40px', - height: '40px', - border: '0', - position: 'relative', - background: palette.white, - transition: 'backgorund 0.5s ease-in-out', - }, + emoji: { + fontSize: '18px', + width: '40px', + height: '40px', + border: '0', + position: 'relative', + background: palette.white, + transition: 'backgorund 0.5s ease-in-out', + }, - emojiSelected: { - background: palette.neutralLight, - }, + emojiSelected: { + background: palette.neutralLight, + }, - navBar: { - top: '-1px', - zIndex: 10, - position: 'sticky', - }, + navBar: { + top: '-1px', + zIndex: 10, + position: 'sticky', + }, - navBarTooltip: { - display: 'inline-block', - }, + navBarTooltip: { + display: 'inline-block', + }, - navBarButton: { - height: '40px', - width: '40px', - border: '0', - borderBottom: 'solid 1px', - padding: 0, - marginBottom: 0, - display: 'inline-block', - color: palette.themeDark, - background: palette.white, - '&:hover': { - cursor: 'default', + navBarButton: { + height: '40px', + width: '40px', + border: '0', + borderBottom: 'solid 1px', + padding: 0, + marginBottom: 0, + display: 'inline-block', + color: palette.themeDark, + background: palette.white, + '&:hover': { + cursor: 'default', + }, }, - }, - selected: { - borderBottom: '2px solid', - borderBottomColor: palette.themeDark, - }, + selected: { + borderBottom: '2px solid', + borderBottomColor: palette.themeDark, + }, - statusBar: { - borderTop: 'solid 1px', - height: '50px', - overflow: 'hidden', - position: 'relative', - background: palette.white, - }, + statusBar: { + borderTop: 'solid 1px', + height: '50px', + overflow: 'hidden', + position: 'relative', + background: palette.white, + }, - statusBarIcon: { - padding: '4px', - fontSize: '25px', - display: 'inline-block', - fontStyle: 'normal', - fontWeight: 'normal', - lineHeight: '40px', - }, + statusBarIcon: { + padding: '4px', + fontSize: '25px', + display: 'inline-block', + fontStyle: 'normal', + fontWeight: 'normal', + lineHeight: '40px', + }, - statusBarDetailsContainer: { - padding: '0 4px', - lineHeight: '50px', - position: 'absolute', - display: 'inline-block', - left: '40px', - right: '0', - top: '0', - }, + statusBarDetailsContainer: { + padding: '0 4px', + lineHeight: '50px', + position: 'absolute', + display: 'inline-block', + left: '40px', + right: '0', + top: '0', + }, - statusBarDetails: { - fontWeight: 'bold', - whiteSpace: 'nowrap', - overflow: 'hidden', - textOverflow: 'ellipsis', - }, + statusBarDetails: { + fontWeight: 'bold', + whiteSpace: 'nowrap', + overflow: 'hidden', + textOverflow: 'ellipsis', + }, - statusBarNoResultDetailsContainer: { - lineHeight: '50px', - position: 'absolute', - display: 'inline-block', - top: '0', - whiteSpace: 'nowrap', - overflow: 'hidden', - textOverflow: 'ellipsis', - left: '0', - padding: '0 8px', - }, - }); -}); + statusBarNoResultDetailsContainer: { + lineHeight: '50px', + position: 'absolute', + display: 'inline-block', + top: '0', + whiteSpace: 'nowrap', + overflow: 'hidden', + textOverflow: 'ellipsis', + left: '0', + padding: '0 8px', + }, + }; + return mergeStyleSets(styles); + } +); diff --git a/packages/roosterjs-react/lib/emoji/components/EmojiStatusBar.tsx b/packages/roosterjs-react/lib/emoji/components/EmojiStatusBar.tsx index 22b95434c3a..043a144978b 100644 --- a/packages/roosterjs-react/lib/emoji/components/EmojiStatusBar.tsx +++ b/packages/roosterjs-react/lib/emoji/components/EmojiStatusBar.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { TooltipHost, TooltipOverflowMode } from '@fluentui/react/lib/Tooltip'; import type { Emoji } from '../type/Emoji'; import type { EmojiPaneStyle } from '../type/EmojiPaneStyles'; -import type { IProcessedStyleSet, IStyleSet } from '@fluentui/react/lib/Styling'; +import type { IProcessedStyleSet } from '@fluentui/react/lib/Styling'; /** * @internal * Emoji Status Bar data @@ -11,7 +11,7 @@ export interface EmojiStatusBarProps { emoji: Emoji; strings: Record; hasResult: boolean; - classNames: IProcessedStyleSet>; + classNames: IProcessedStyleSet; } const NO_SUGGESTIONS = 'emjDNoSuggetions'; diff --git a/packages/roosterjs-react/lib/emoji/type/EmojiPaneStyles.ts b/packages/roosterjs-react/lib/emoji/type/EmojiPaneStyles.ts index 0b4c32f9606..d74596aee05 100644 --- a/packages/roosterjs-react/lib/emoji/type/EmojiPaneStyles.ts +++ b/packages/roosterjs-react/lib/emoji/type/EmojiPaneStyles.ts @@ -1,26 +1,25 @@ -import type { IStyleSet } from '@fluentui/react/lib/Styling'; /** * @internal * EmojiPane Style classes */ export interface EmojiPaneStyle { - quickPicker: IStyleSet; - tooltip: IStyleSet; - emojiTextInput: IStyleSet; - partialList: IStyleSet; - fullListContent: IStyleSet; - fullListBody: IStyleSet; - fullList: IStyleSet; - roosterEmojiPane: IStyleSet; - emoji: IStyleSet; - navBarButton: IStyleSet; - navBarTooltip: IStyleSet; - selected: IStyleSet; - navBar: IStyleSet; - statusBarDetails: IStyleSet; - statusBarDetailsContainer: IStyleSet; - statusBar: IStyleSet; - statusBarIcon: IStyleSet; - statusBarNoResultDetailsContainer: IStyleSet; - emojiSelected: IStyleSet; + quickPicker: Object; + tooltip: Object; + emojiTextInput: Object; + partialList: Object; + fullListContent: Object; + fullListBody: Object; + fullList: Object; + roosterEmojiPane: Object; + emoji: Object; + navBarButton: Object; + navBarTooltip: Object; + selected: Object; + navBar: Object; + statusBarDetails: Object; + statusBarDetailsContainer: Object; + statusBar: Object; + statusBarIcon: Object; + statusBarNoResultDetailsContainer: Object; + emojiSelected: Object; } diff --git a/packages/roosterjs-react/lib/ribbon/component/Ribbon.tsx b/packages/roosterjs-react/lib/ribbon/component/Ribbon.tsx index 0de2e7d2d44..8ccc33adfc5 100644 --- a/packages/roosterjs-react/lib/ribbon/component/Ribbon.tsx +++ b/packages/roosterjs-react/lib/ribbon/component/Ribbon.tsx @@ -3,6 +3,7 @@ import { CommandBar } from '@fluentui/react/lib/CommandBar'; import { FocusZoneDirection } from '@fluentui/react/lib/FocusZone'; import { getLocalizedString } from '../../common/utils/getLocalizedString'; import { getObjectKeys } from 'roosterjs-content-model-dom'; +import { initializeIcons } from '@fluentui/font-icons-mdl2'; import { mergeStyles } from '@fluentui/react/lib/Styling'; import { moreCommands } from '../buttons/moreCommands'; import type { @@ -25,6 +26,8 @@ const rtlIcon = mergeStyles({ transform: 'scaleX(-1)', }); +let iconInitialized = false; + /** * The format ribbon component of roosterjs-react * @param props Properties of format ribbon component @@ -35,6 +38,11 @@ export function Ribbon(props: RibbonProps) { const [formatState, setFormatState] = React.useState(null); const isRtl = dir == 'rtl'; + if (!iconInitialized) { + iconInitialized = true; + initializeIcons(); + } + const onClick = React.useCallback( (_, item?: IContextualMenuItem) => { if (item) { diff --git a/tools/buildTools/checkDependency.js b/tools/buildTools/checkDependency.js index 60e7a7e05d3..e73a41b81f5 100644 --- a/tools/buildTools/checkDependency.js +++ b/tools/buildTools/checkDependency.js @@ -85,6 +85,7 @@ function findPackageName(filename) { const GlobalAllowedCrossPackageDependency = [ 'roosterjs-editor-types/lib/compatibleTypes', /@fluentui\/react(\/.*)?/, + /@fluentui\/font-icons-mdl2(\/.*)?/, ]; function checkDependency() { diff --git a/yarn.lock b/yarn.lock index b003e590332..9e319cd6783 100644 --- a/yarn.lock +++ b/yarn.lock @@ -308,143 +308,153 @@ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.50.0.tgz#9e93b850f0f3fa35f5fa59adfd03adae8488e484" integrity sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ== -"@fluentui/date-time-utilities@^8.3.0": - version "8.3.0" - resolved "https://registry.yarnpkg.com/@fluentui/date-time-utilities/-/date-time-utilities-8.3.0.tgz#a51cd59ea327b948bdb76a083d9c42d95a71c98a" - integrity sha512-shSWwarh+ueDF22hIRL1T9bGA4epsP8QAIfYhpoK5ySr0Zg5RjqdNMM5IXzlTiXqE/LGe16CVrvyW2ksqfvQPg== +"@fluentui/date-time-utilities@^8.6.9": + version "8.6.9" + resolved "https://registry.yarnpkg.com/@fluentui/date-time-utilities/-/date-time-utilities-8.6.9.tgz#97ff9b164aa45bf81fc145dd2e37751c5635c77f" + integrity sha512-dgOlVm4nXBWDLqijmvn4iAtyv1hVpQZjN6p0So74BW+7ASUTkQGe3lf8PHV/OjBiXfZa4qwONvmTQBGCheNU0w== dependencies: - "@fluentui/set-version" "^8.1.5" + "@fluentui/set-version" "^8.2.23" tslib "^2.1.0" -"@fluentui/dom-utilities@^2.1.5": - version "2.1.5" - resolved "https://registry.yarnpkg.com/@fluentui/dom-utilities/-/dom-utilities-2.1.5.tgz#21ba77c8bfe64d15ffc16a8e055255bbb23600b2" - integrity sha512-OLDYV5ZGIiK/JXx/DFFib4vSa7PELvznbdAujDcX2wjt3V3Lt2N5ucv59JsVxk5LlwXjasUHJI2NZadagmnM6A== +"@fluentui/dom-utilities@^2.3.9": + version "2.3.9" + resolved "https://registry.yarnpkg.com/@fluentui/dom-utilities/-/dom-utilities-2.3.9.tgz#59b357cd9c20904b60f0867687002223b2e83a8f" + integrity sha512-8PPzv31VXnyMvZrzK7iSGPRx8piJjas0xV+qaNQ1tzAXHuTaLXPeADJK/gEDH1XA/e9Vaakb3lPUpRVa8tal+w== dependencies: - "@fluentui/set-version" "^8.1.5" + "@fluentui/set-version" "^8.2.23" tslib "^2.1.0" -"@fluentui/font-icons-mdl2@^8.1.26": - version "8.1.26" - resolved "https://registry.yarnpkg.com/@fluentui/font-icons-mdl2/-/font-icons-mdl2-8.1.26.tgz#e74070e8ab91302539ccb23a05e0a6c792333e42" - integrity sha512-CmdyZ3QP5UCdyHrYwwtA2AlFyG2oG2aOav4A7quVZZ60gX/63tPUskcVK+UvB8f+zecOLBvaHFJIkTWL13fvvA== +"@fluentui/font-icons-mdl2@^8.5.57": + version "8.5.57" + resolved "https://registry.yarnpkg.com/@fluentui/font-icons-mdl2/-/font-icons-mdl2-8.5.57.tgz#fc541e43d749baeb71a9a958b828fa49ecefa7ea" + integrity sha512-HYB+deey6wt6qHtTKdrhPhTZi7ZZVI2IwlguabK+22LzixgSdeJ0sg5Hhau5IKFwrn8ExEFOwfoaZ6KCSbcMwQ== dependencies: - "@fluentui/set-version" "^8.1.5" - "@fluentui/style-utilities" "^8.5.8" + "@fluentui/set-version" "^8.2.23" + "@fluentui/style-utilities" "^8.11.6" + "@fluentui/utilities" "^8.15.19" tslib "^2.1.0" -"@fluentui/foundation-legacy@^8.1.25": - version "8.1.25" - resolved "https://registry.yarnpkg.com/@fluentui/foundation-legacy/-/foundation-legacy-8.1.25.tgz#2c0c149c2ab037419458f71632504ad87a4b340e" - integrity sha512-keV/jdvUJAPFiMARrruvBGvXgix0kMbo+pfrdsbEFnQHs2o9m7Ha1ChsQCAkVmhv3DYrkb7shDG9R1eerPLJiw== +"@fluentui/foundation-legacy@^8.4.23": + version "8.4.23" + resolved "https://registry.yarnpkg.com/@fluentui/foundation-legacy/-/foundation-legacy-8.4.23.tgz#18f06a119eb523a8334b987ad06428bf9c202232" + integrity sha512-lWFouH1+vku2LgKaZUhuBNyoXJ7DByUIMXHF7Osgq/miN8ewHt5uez8LuuSHDgCytxksCY4usCMIIL2zJD0I6w== dependencies: - "@fluentui/merge-styles" "^8.3.0" - "@fluentui/set-version" "^8.1.5" - "@fluentui/style-utilities" "^8.5.8" - "@fluentui/utilities" "^8.5.0" + "@fluentui/merge-styles" "^8.6.13" + "@fluentui/set-version" "^8.2.23" + "@fluentui/style-utilities" "^8.11.6" + "@fluentui/utilities" "^8.15.19" tslib "^2.1.0" -"@fluentui/keyboard-key@^0.3.5": - version "0.3.5" - resolved "https://registry.yarnpkg.com/@fluentui/keyboard-key/-/keyboard-key-0.3.5.tgz#af1273bbd8db3e7e08bf8ce8a303890d33d4d8b2" - integrity sha512-qPNPnRtkC92b8Zjx3mJ6+vRX+pdmbDYcXP8zXb2NJ/briAQXYmyqdjJLUl2riVBcAC4H3cL6dTKLR9VAyqhdYQ== +"@fluentui/keyboard-key@^0.4.23": + version "0.4.23" + resolved "https://registry.yarnpkg.com/@fluentui/keyboard-key/-/keyboard-key-0.4.23.tgz#ebaa87b1dcdfb2a9ac805c678f9520e9d9194c3c" + integrity sha512-9GXeyUqNJUdg5JiQUZeGPiKnRzMRi9YEUn1l9zq6X/imYdMhxHrxpVZS12129cBfgvPyxt9ceJpywSfmLWqlKA== dependencies: tslib "^2.1.0" -"@fluentui/merge-styles@^8.3.0": - version "8.3.0" - resolved "https://registry.yarnpkg.com/@fluentui/merge-styles/-/merge-styles-8.3.0.tgz#e27581dd43cbe1b5d54d7325e619924883dd0fd5" - integrity sha512-qI61vWnmSuZYopMfWbEkTuDmnedmOYmsphTwflG6zif1EPV6h5u3dDUfHnmHMahywNOLOv5MOa+zVU0SCEHSAg== +"@fluentui/merge-styles@^8.6.13": + version "8.6.13" + resolved "https://registry.yarnpkg.com/@fluentui/merge-styles/-/merge-styles-8.6.13.tgz#c9168b39aa6952ef36318e4e5273f50a5529b023" + integrity sha512-IWgvi2CC+mcQ7/YlCvRjsmHL2+PUz7q+Pa2Rqk3a+QHN0V1uBvgIbKk5y/Y/awwDXy1yJHiqMCcDHjBNmS1d4A== dependencies: - "@fluentui/set-version" "^8.1.5" + "@fluentui/set-version" "^8.2.23" tslib "^2.1.0" -"@fluentui/react-focus@^8.3.21": - version "8.3.21" - resolved "https://registry.yarnpkg.com/@fluentui/react-focus/-/react-focus-8.3.21.tgz#01f3f9398719496fa1158c3f227cf855e4938e4e" - integrity sha512-MCPKvPiyifwAErjXyAAu9NWlhLEJOohcGf4y6GVKWlXi44oslSIfKQatUVrstFW83hbkcXHigDuBZmhdyTThVg== +"@fluentui/react-focus@^8.9.20": + version "8.9.20" + resolved "https://registry.yarnpkg.com/@fluentui/react-focus/-/react-focus-8.9.20.tgz#65ea82af728657147db75c46853197de9aa961e9" + integrity sha512-eOYKohP5v82jUAeEj7Mscqy5Tt4DhgTsVwf+cejj3AGhvLfFfmUbJFmVClooqXFdMgm1vvPGdub8SHA02REVkg== dependencies: - "@fluentui/keyboard-key" "^0.3.5" - "@fluentui/merge-styles" "^8.3.0" - "@fluentui/set-version" "^8.1.5" - "@fluentui/style-utilities" "^8.5.8" - "@fluentui/utilities" "^8.5.0" + "@fluentui/keyboard-key" "^0.4.23" + "@fluentui/merge-styles" "^8.6.13" + "@fluentui/set-version" "^8.2.23" + "@fluentui/style-utilities" "^8.11.6" + "@fluentui/utilities" "^8.15.19" tslib "^2.1.0" -"@fluentui/react-hooks@^8.4.0": - version "8.4.0" - resolved "https://registry.yarnpkg.com/@fluentui/react-hooks/-/react-hooks-8.4.0.tgz#a4c75f97badc7d758abb36138c4e928d27842229" - integrity sha512-BI0lp+5rlabvXpV6uCEP+cCvCJy5srlrPZ2J63Jr1T+nWnn7CKJ5mgE2Uc+VYW9EoHRLJAFbrD0a9WYkXKZ3xA== +"@fluentui/react-hooks@^8.8.16": + version "8.8.16" + resolved "https://registry.yarnpkg.com/@fluentui/react-hooks/-/react-hooks-8.8.16.tgz#1843c44e51d332bbd464959967dbb9be92584b97" + integrity sha512-PQ1BeOp+99mdO0g7j6QLtChfXG1LxXeHG0q5CtUeD1OUGR+vUDK84h60sw7e7qU9sSmvPmHO7jn69Lg3CS+DXw== dependencies: - "@fluentui/react-window-provider" "^2.1.6" - "@fluentui/set-version" "^8.1.5" - "@fluentui/utilities" "^8.5.0" + "@fluentui/react-window-provider" "^2.2.28" + "@fluentui/set-version" "^8.2.23" + "@fluentui/utilities" "^8.15.19" tslib "^2.1.0" -"@fluentui/react-window-provider@^2.1.6": - version "2.1.6" - resolved "https://registry.yarnpkg.com/@fluentui/react-window-provider/-/react-window-provider-2.1.6.tgz#1e3d66842a6b1c724dcc3ee13d2bc0aa0d7028a8" - integrity sha512-Fr1THmtn/Kx95/WVXI1qIELX2VHpuGYVKWSpdgbpcmKukvk30h4JG6kKMGYPz3r7UeaTzpaeIw537A9CJ2hVMg== +"@fluentui/react-portal-compat-context@^9.0.13": + version "9.0.13" + resolved "https://registry.yarnpkg.com/@fluentui/react-portal-compat-context/-/react-portal-compat-context-9.0.13.tgz#98a8c78e214acc7bb25e5e750df378be42567d2d" + integrity sha512-N+c6Qs775jnr/4WIzsQuNaRu4v16fa+gGsOCzzU1bqxX0IR9BSjjO2oLGC6luaAOqlQP+JIwn/aumOIJICKXkA== dependencies: - "@fluentui/set-version" "^8.1.5" + "@swc/helpers" "^0.5.1" + +"@fluentui/react-window-provider@^2.2.28": + version "2.2.28" + resolved "https://registry.yarnpkg.com/@fluentui/react-window-provider/-/react-window-provider-2.2.28.tgz#6f1109a37f888ffba78cdf009772c642222132ed" + integrity sha512-YdZ74HTaoDwlvLDzoBST80/17ExIl93tLJpTxnqK5jlJOAUVQ+mxLPF2HQEJq+SZr5IMXHsQ56w/KaZVRn72YA== + dependencies: + "@fluentui/set-version" "^8.2.23" tslib "^2.1.0" -"@fluentui/react@^8.0.0": - version "8.56.2" - resolved "https://registry.yarnpkg.com/@fluentui/react/-/react-8.56.2.tgz#6c6246f5c1a9a20cc2121e7db6bcdbf2da5f7d19" - integrity sha512-RMmto4/zExtfQf3r9ooQyqdC2lWSpNWQ9apvK464pW314BXWNMFO6E3mb8vDPjmfBzZ8yDYjrB0L2y6OO4ORDA== - dependencies: - "@fluentui/date-time-utilities" "^8.3.0" - "@fluentui/font-icons-mdl2" "^8.1.26" - "@fluentui/foundation-legacy" "^8.1.25" - "@fluentui/merge-styles" "^8.3.0" - "@fluentui/react-focus" "^8.3.21" - "@fluentui/react-hooks" "^8.4.0" - "@fluentui/react-window-provider" "^2.1.6" - "@fluentui/set-version" "^8.1.5" - "@fluentui/style-utilities" "^8.5.8" - "@fluentui/theme" "^2.4.12" - "@fluentui/utilities" "^8.5.0" +"@fluentui/react@8.122.3": + version "8.122.3" + resolved "https://registry.yarnpkg.com/@fluentui/react/-/react-8.122.3.tgz#b7d7c95b190a44d3c54340203290be4dac582c5d" + integrity sha512-XAffQFevT5+PI1fvhAO+2G7ozB5OLYIFEJGXzzpIIkIOPEbZwUMgDh/J93ajVdsBc3OOLaR+mEqaO8ed5Klutw== + dependencies: + "@fluentui/date-time-utilities" "^8.6.9" + "@fluentui/font-icons-mdl2" "^8.5.57" + "@fluentui/foundation-legacy" "^8.4.23" + "@fluentui/merge-styles" "^8.6.13" + "@fluentui/react-focus" "^8.9.20" + "@fluentui/react-hooks" "^8.8.16" + "@fluentui/react-portal-compat-context" "^9.0.13" + "@fluentui/react-window-provider" "^2.2.28" + "@fluentui/set-version" "^8.2.23" + "@fluentui/style-utilities" "^8.11.6" + "@fluentui/theme" "^2.6.64" + "@fluentui/utilities" "^8.15.19" "@microsoft/load-themed-styles" "^1.10.26" tslib "^2.1.0" -"@fluentui/set-version@^8.1.5": - version "8.1.5" - resolved "https://registry.yarnpkg.com/@fluentui/set-version/-/set-version-8.1.5.tgz#68d3d8c7fbefba20b3d1aef71fcc730ca46dd353" - integrity sha512-AfaycaduWd/aErqEmrAUWpr2gpZrkaSe6D9noXhtVH3JlreRuFM78Ji1oE4f8cpWxSA/K5qb7BT6x4z4I2Bs+A== +"@fluentui/set-version@^8.2.23": + version "8.2.23" + resolved "https://registry.yarnpkg.com/@fluentui/set-version/-/set-version-8.2.23.tgz#14032bc9a222a6e50a5cb166e1a39a527cfc69fd" + integrity sha512-VPXaBsiaa3Xn/AY40nLU9bvDQ62lpMVnFzFTlQ8CbpdwrjxNlRxDUY5vRToNzp1+Zu5gD/+CgsXqIZGcry5L5w== dependencies: tslib "^2.1.0" -"@fluentui/style-utilities@^8.5.8": - version "8.5.8" - resolved "https://registry.yarnpkg.com/@fluentui/style-utilities/-/style-utilities-8.5.8.tgz#198512c3a710c71b814c5a0eba1e9f885f91a4e1" - integrity sha512-g6jUptGmkKrh0mDQBjKi+1wUNlJeRjpemavXmSBG+i+ZTn8PT/XjshLB9T9Q7FBriil0ccr6wBkPr11ElZC5gg== +"@fluentui/style-utilities@^8.11.6": + version "8.11.6" + resolved "https://registry.yarnpkg.com/@fluentui/style-utilities/-/style-utilities-8.11.6.tgz#0a7c505d7b13b9671ca7b28ee7bd562789cf96a1" + integrity sha512-bVFu/ONP2+GZ/JzR6NhN7+1fuMHvi+LjOfgo21HQoDakY/KwFaitLiQBQFlRpbRUVcZXQDqe4Ur6EDFAlb2I7Q== dependencies: - "@fluentui/merge-styles" "^8.3.0" - "@fluentui/set-version" "^8.1.5" - "@fluentui/theme" "^2.4.12" - "@fluentui/utilities" "^8.5.0" + "@fluentui/merge-styles" "^8.6.13" + "@fluentui/set-version" "^8.2.23" + "@fluentui/theme" "^2.6.64" + "@fluentui/utilities" "^8.15.19" "@microsoft/load-themed-styles" "^1.10.26" tslib "^2.1.0" -"@fluentui/theme@^2.4.12": - version "2.4.12" - resolved "https://registry.yarnpkg.com/@fluentui/theme/-/theme-2.4.12.tgz#82181b786922aab142816af300f6e3f54ac83357" - integrity sha512-dlBmGOp3Ib08iP1F2rU8gb/DX/tsbdzNNFT0WqlO+1bjAVwnmn46xuDpsvGgMz0ZRYN4ZXfpvWkWqXpDnsHS9g== +"@fluentui/theme@^2.6.64": + version "2.6.64" + resolved "https://registry.yarnpkg.com/@fluentui/theme/-/theme-2.6.64.tgz#6b8f1620ba1f217461441d9fab9ffd44325deb9c" + integrity sha512-cjzwPgq3Zsw4F6Xy7A7yN8WCeEXKTwk9lfJzEr5b00euJRuPMxkxesBbAWW43+/1l1eWVYmSm4GcEMDVD4BfXQ== dependencies: - "@fluentui/merge-styles" "^8.3.0" - "@fluentui/set-version" "^8.1.5" - "@fluentui/utilities" "^8.5.0" + "@fluentui/merge-styles" "^8.6.13" + "@fluentui/set-version" "^8.2.23" + "@fluentui/utilities" "^8.15.19" tslib "^2.1.0" -"@fluentui/utilities@^8.5.0": - version "8.5.0" - resolved "https://registry.yarnpkg.com/@fluentui/utilities/-/utilities-8.5.0.tgz#07a2d9c1f41ca294e89a5a4f454479d51f86ff59" - integrity sha512-npuYmwQfCx2WggW0MHwKgpaSPdk62FtQLmw0zoCNdSNDDQ5u3lWKScwy7FR3Nn1jjL0CDaC9PnD8ults2iIeSg== +"@fluentui/utilities@^8.15.19": + version "8.15.19" + resolved "https://registry.yarnpkg.com/@fluentui/utilities/-/utilities-8.15.19.tgz#2229d4c294d4d6b1eb618a8ea6d6ec05392c5962" + integrity sha512-20WoYz0wW7pkmur+7qxTwRfvkdAnHfylLdCYSm91WLupb0cwQ1wWZWIuyo+e0cjcvem1T9TC1+NjWs0kavTWBg== dependencies: - "@fluentui/dom-utilities" "^2.1.5" - "@fluentui/merge-styles" "^8.3.0" - "@fluentui/set-version" "^8.1.5" + "@fluentui/dom-utilities" "^2.3.9" + "@fluentui/merge-styles" "^8.6.13" + "@fluentui/react-window-provider" "^2.2.28" + "@fluentui/set-version" "^8.2.23" tslib "^2.1.0" "@humanwhocodes/config-array@^0.11.11": @@ -610,6 +620,13 @@ resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz#821f8442f4175d8f0467b9daf26e3a18e2d02af2" integrity sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA== +"@swc/helpers@^0.5.1": + version "0.5.15" + resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.15.tgz#79efab344c5819ecf83a43f3f9f811fc84b516d7" + integrity sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g== + dependencies: + tslib "^2.8.0" + "@types/color-convert@*": version "2.0.0" resolved "https://registry.yarnpkg.com/@types/color-convert/-/color-convert-2.0.0.tgz#8f5ee6b9e863dcbee5703f5a517ffb13d3ea4e22" @@ -6726,6 +6743,11 @@ tslib@^2.3.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.3.tgz#24944ba2d990940e6e982c4bea147aba80209913" integrity sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w== +tslib@^2.8.0: + version "2.8.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== + tslint-eslint-rules@5.4.0: version "5.4.0" resolved "https://registry.yarnpkg.com/tslint-eslint-rules/-/tslint-eslint-rules-5.4.0.tgz#e488cc9181bf193fe5cd7bfca213a7695f1737b5" From 50e31fa6e27aa5aa4414e4364775cb08112834d8 Mon Sep 17 00:00:00 2001 From: Haowen Chen Date: Sat, 11 Jan 2025 01:20:29 +0800 Subject: [PATCH 08/10] Add parameter `fontSizes` back to `changeFontSize` (#2920) --- .../lib/publicApi/segment/changeFontSize.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/roosterjs-content-model-api/lib/publicApi/segment/changeFontSize.ts b/packages/roosterjs-content-model-api/lib/publicApi/segment/changeFontSize.ts index cb97c07022d..1a059d573a3 100644 --- a/packages/roosterjs-content-model-api/lib/publicApi/segment/changeFontSize.ts +++ b/packages/roosterjs-content-model-api/lib/publicApi/segment/changeFontSize.ts @@ -21,13 +21,17 @@ const MAX_FONT_SIZE = 1000; * @param change Whether increase or decrease font size * @param fontSizes A sorted font size array, in pt. Default value is FONT_SIZES */ -export function changeFontSize(editor: IEditor, change: 'increase' | 'decrease') { +export function changeFontSize( + editor: IEditor, + change: 'increase' | 'decrease', + fontSizes: number[] = FONT_SIZES +) { editor.focus(); formatSegmentWithContentModel( editor, 'changeFontSize', - (format, _, __, paragraph) => changeFontSizeInternal(change, format, paragraph), + (format, _, __, paragraph) => changeFontSizeInternal(change, format, paragraph, fontSizes), undefined /* segmentHasStyleCallback*/, true /*includingFormatHandler*/ ); @@ -36,13 +40,14 @@ export function changeFontSize(editor: IEditor, change: 'increase' | 'decrease') function changeFontSizeInternal( change: 'increase' | 'decrease', format: ContentModelSegmentFormat, - paragraph: ShallowMutableContentModelParagraph | null + paragraph: ShallowMutableContentModelParagraph | null, + fontSizes: number[] ) { if (format.fontSize) { const sizeInPt = parseValueWithUnit(format.fontSize, undefined /*element*/, 'pt'); if (sizeInPt > 0) { - const newSize = getNewFontSize(sizeInPt, change == 'increase' ? 1 : -1, FONT_SIZES); + const newSize = getNewFontSize(sizeInPt, change == 'increase' ? 1 : -1, fontSizes); setFontSizeInternal(newSize + 'pt', format, paragraph); } From 2b41c7bf480be228705e69e5b4dbf7b46615163b Mon Sep 17 00:00:00 2001 From: "Julia Roldi (from Dev Box)" Date: Fri, 10 Jan 2025 15:29:45 -0300 Subject: [PATCH 09/10] bump version --- versions.json | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/versions.json b/versions.json index 9d5c07fbede..de9a22e33d0 100644 --- a/versions.json +++ b/versions.json @@ -1,8 +1,6 @@ { "react": "9.0.0", - "main": "9.16.0", + "main": "9.17.0", "legacyAdapter": "8.62.4", - "overrides": { - "roosterjs-content-model-plugins": "9.16.1" - } + "overrides": {} } From efa2131043add9679fc864a2e71179663da9e4e5 Mon Sep 17 00:00:00 2001 From: "Julia Roldi (from Dev Box)" Date: Fri, 10 Jan 2025 15:49:13 -0300 Subject: [PATCH 10/10] upgrade --- versions.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/versions.json b/versions.json index de9a22e33d0..45c49bb7916 100644 --- a/versions.json +++ b/versions.json @@ -1,6 +1,6 @@ { - "react": "9.0.0", + "react": "9.0.1", "main": "9.17.0", - "legacyAdapter": "8.62.4", + "legacyAdapter": "8.63.0", "overrides": {} }