diff --git a/demo/scripts/controls/contentModel/components/format/formatPart/FloatFormatRenderer.ts b/demo/scripts/controls/contentModel/components/format/formatPart/FloatFormatRenderer.ts new file mode 100644 index 00000000000..0b29f44b958 --- /dev/null +++ b/demo/scripts/controls/contentModel/components/format/formatPart/FloatFormatRenderer.ts @@ -0,0 +1,11 @@ +import { createTextFormatRenderer } from '../utils/createTextFormatRenderer'; +import { FloatFormat } from 'roosterjs-content-model-types'; +import { FormatRenderer } from '../utils/FormatRenderer'; + +export const FloatFormatRenderer: FormatRenderer = createTextFormatRenderer< + FloatFormat +>( + 'Float', + format => format.float, + (format, value) => (format.float = value) +); diff --git a/demo/scripts/controls/contentModel/components/model/ContentModelImageView.tsx b/demo/scripts/controls/contentModel/components/model/ContentModelImageView.tsx index e00fd88d057..948a6390a1a 100644 --- a/demo/scripts/controls/contentModel/components/model/ContentModelImageView.tsx +++ b/demo/scripts/controls/contentModel/components/model/ContentModelImageView.tsx @@ -3,6 +3,7 @@ import { ContentModelCodeView } from './ContentModelCodeView'; import { ContentModelImage, ContentModelImageFormat } from 'roosterjs-content-model-types'; import { ContentModelLinkView } from './ContentModelLinkView'; import { ContentModelView } from '../ContentModelView'; +import { FloatFormatRenderer } from '../format/formatPart/FloatFormatRenderer'; import { FormatRenderer } from '../format/utils/FormatRenderer'; import { FormatView } from '../format/FormatView'; import { IdFormatRenderer } from '../format/formatPart/IdFormatRenderer'; @@ -22,6 +23,7 @@ const ImageFormatRenderers: FormatRenderer[] = [ ...SizeFormatRenderers, MarginFormatRenderer, PaddingFormatRenderer, + FloatFormatRenderer, ]; export function ContentModelImageView(props: { image: ContentModelImage }) { diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/floatFormatHandler.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/floatFormatHandler.ts new file mode 100644 index 00000000000..538e174ab89 --- /dev/null +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/common/floatFormatHandler.ts @@ -0,0 +1,20 @@ +import { FloatFormat } from 'roosterjs-content-model-types'; +import { FormatHandler } from '../FormatHandler'; + +/** + * @internal + */ +export const floatFormatHandler: FormatHandler = { + parse: (format, element) => { + const float = element.style.float || element.getAttribute('align'); + + if (float) { + format.float = float; + } + }, + apply: (format, element) => { + if (format.float) { + element.style.float = format.float; + } + }, +}; diff --git a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/defaultFormatHandlers.ts b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/defaultFormatHandlers.ts index 9748ea9a197..b793af848d2 100644 --- a/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/defaultFormatHandlers.ts +++ b/packages-content-model/roosterjs-content-model-dom/lib/formatHandlers/defaultFormatHandlers.ts @@ -6,6 +6,7 @@ import { boxShadowFormatHandler } from './common/boxShadowFormatHandler'; import { datasetFormatHandler } from './common/datasetFormatHandler'; import { directionFormatHandler } from './block/directionFormatHandler'; import { displayFormatHandler } from './block/displayFormatHandler'; +import { floatFormatHandler } from './common/floatFormatHandler'; import { fontFamilyFormatHandler } from './segment/fontFamilyFormatHandler'; import { fontSizeFormatHandler } from './segment/fontSizeFormatHandler'; import { FormatHandler } from './FormatHandler'; @@ -58,6 +59,7 @@ const defaultFormatHandlerMap: FormatHandlers = { dataset: datasetFormatHandler, direction: directionFormatHandler, display: displayFormatHandler, + float: floatFormatHandler, fontFamily: fontFamilyFormatHandler, fontSize: fontSizeFormatHandler, htmlAlign: htmlAlignFormatHandler, @@ -164,7 +166,17 @@ const defaultFormatKeysPerCategory: { ], tableBorder: ['borderBox', 'tableSpacing'], tableCellBorder: ['borderBox'], - image: ['id', 'size', 'margin', 'padding', 'borderBox', 'border', 'boxShadow', 'display'], + image: [ + 'id', + 'size', + 'margin', + 'padding', + 'borderBox', + 'border', + 'boxShadow', + 'display', + 'float', + ], link: [ 'link', 'textColor', diff --git a/packages-content-model/roosterjs-content-model-dom/test/formatHandlers/common/floatFormatHandlerTest.ts b/packages-content-model/roosterjs-content-model-dom/test/formatHandlers/common/floatFormatHandlerTest.ts new file mode 100644 index 00000000000..1ea9466f24e --- /dev/null +++ b/packages-content-model/roosterjs-content-model-dom/test/formatHandlers/common/floatFormatHandlerTest.ts @@ -0,0 +1,60 @@ +import { createDomToModelContext } from '../../../lib/domToModel/context/createDomToModelContext'; +import { createModelToDomContext } from '../../../lib/modelToDom/context/createModelToDomContext'; +import { DomToModelContext, FloatFormat, ModelToDomContext } from 'roosterjs-content-model-types'; +import { floatFormatHandler } from '../../../lib/formatHandlers/common/floatFormatHandler'; + +describe('floatFormatHandler.parse', () => { + let div: HTMLElement; + let format: FloatFormat; + let context: DomToModelContext; + + beforeEach(() => { + div = document.createElement('div'); + format = {}; + context = createDomToModelContext(); + }); + + it('No float', () => { + floatFormatHandler.parse(format, div, context, {}); + expect(format).toEqual({}); + }); + + it('Float left', () => { + div.style.float = 'left'; + floatFormatHandler.parse(format, div, context, {}); + expect(format).toEqual({ + float: 'left', + }); + }); + + it('Float left from attribute', () => { + div.setAttribute('align', 'left'); + floatFormatHandler.parse(format, div, context, {}); + expect(format).toEqual({ + float: 'left', + }); + }); +}); + +describe('floatFormatHandler.apply', () => { + let div: HTMLElement; + let format: FloatFormat; + let context: ModelToDomContext; + + beforeEach(() => { + div = document.createElement('div'); + format = {}; + context = createModelToDomContext(); + }); + + it('No float', () => { + floatFormatHandler.apply(format, div, context); + expect(div.outerHTML).toBe('
'); + }); + + it('Float: left', () => { + format.float = 'left'; + floatFormatHandler.apply(format, div, context); + expect(div.outerHTML).toBe('
'); + }); +}); diff --git a/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelImageFormat.ts b/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelImageFormat.ts index 5693df671ae..d4f70c76492 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelImageFormat.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/format/ContentModelImageFormat.ts @@ -2,6 +2,7 @@ import { BorderFormat } from './formatParts/BorderFormat'; import { BoxShadowFormat } from './formatParts/BoxShadowFormat'; import { ContentModelSegmentFormat } from './ContentModelSegmentFormat'; import { DisplayFormat } from './formatParts/DisplayFormat'; +import { FloatFormat } from './formatParts/FloatFormat'; import { IdFormat } from './formatParts/IdFormat'; import { MarginFormat } from './formatParts/MarginFormat'; import { PaddingFormat } from './formatParts/PaddingFormat'; @@ -17,4 +18,5 @@ export type ContentModelImageFormat = ContentModelSegmentFormat & PaddingFormat & BorderFormat & BoxShadowFormat & - DisplayFormat; + DisplayFormat & + FloatFormat; diff --git a/packages-content-model/roosterjs-content-model-types/lib/format/FormatHandlerTypeMap.ts b/packages-content-model/roosterjs-content-model-types/lib/format/FormatHandlerTypeMap.ts index 483fe79386d..8936c886c75 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/format/FormatHandlerTypeMap.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/format/FormatHandlerTypeMap.ts @@ -6,6 +6,7 @@ import { BoxShadowFormat } from './formatParts/BoxShadowFormat'; import { DatasetFormat } from './metadata/DatasetFormat'; import { DirectionFormat } from './formatParts/DirectionFormat'; import { DisplayFormat } from './formatParts/DisplayFormat'; +import { FloatFormat } from './formatParts/FloatFormat'; import { FontFamilyFormat } from './formatParts/FontFamilyFormat'; import { FontSizeFormat } from './formatParts/FontSizeFormat'; import { HtmlAlignFormat } from './formatParts/HtmlAlignFormat'; @@ -74,6 +75,11 @@ export interface FormatHandlerTypeMap { */ display: DisplayFormat; + /** + * Format for FloatFormat + */ + float: FloatFormat; + /** * Format for FontFamilyFormat */ diff --git a/packages-content-model/roosterjs-content-model-types/lib/format/formatParts/FloatFormat.ts b/packages-content-model/roosterjs-content-model-types/lib/format/formatParts/FloatFormat.ts new file mode 100644 index 00000000000..ce59dc748c3 --- /dev/null +++ b/packages-content-model/roosterjs-content-model-types/lib/format/formatParts/FloatFormat.ts @@ -0,0 +1,9 @@ +/** + * Format of float + */ +export type FloatFormat = { + /** + * Float style + */ + float?: string; +}; diff --git a/packages-content-model/roosterjs-content-model-types/lib/index.ts b/packages-content-model/roosterjs-content-model-types/lib/index.ts index 51657498387..22e154bf4a5 100644 --- a/packages-content-model/roosterjs-content-model-types/lib/index.ts +++ b/packages-content-model/roosterjs-content-model-types/lib/index.ts @@ -45,6 +45,7 @@ export { SizeFormat } from './format/formatParts/SizeFormat'; export { BoxShadowFormat } from './format/formatParts/BoxShadowFormat'; export { ListThreadFormat } from './format/formatParts/ListThreadFormat'; export { ListStylePositionFormat } from './format/formatParts/ListStylePositionFormat'; +export { FloatFormat } from './format/formatParts/FloatFormat'; export { DatasetFormat } from './format/metadata/DatasetFormat'; export { TableMetadataFormat } from './format/metadata/TableMetadataFormat';