Skip to content

Commit

Permalink
Fix #1443: Support CSS style word-break in ContentModel for table cell (
Browse files Browse the repository at this point in the history
#1451)

* Fix #1443

* fix build
  • Loading branch information
JiuqingSong authored Dec 2, 2022
1 parent 086e797 commit 87a393d
Show file tree
Hide file tree
Showing 9 changed files with 763 additions and 653 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { createTextFormatRenderer } from '../utils/createTextFormatRenderer';
import { WordBreakFormat } from 'roosterjs-content-model';

export const WordBreakFormatRenderer = createTextFormatRenderer<WordBreakFormat>(
'Word break',
format => format.wordBreak,
(format, value) => (format.wordBreak = value)
);
Original file line number Diff line number Diff line change
@@ -1,125 +1,127 @@
import * as React from 'react';
import { BackgroundColorFormatRenderer } from '../format/formatPart/BackgroundColorFormatRenderer';
import { BlockGroupContentView } from './BlockGroupContentView';
import { BorderBoxFormatRenderer } from '../format/formatPart/BorderBoxFormatRenderer';
import { BorderFormatRenderers } from '../format/formatPart/BorderFormatRenderers';
import { ContentModelView } from '../ContentModelView';
import { DirectionFormatRenderers } from '../format/formatPart/DirectionFormatRenderers';
import { FormatRenderer } from '../format/utils/FormatRenderer';
import { FormatView } from '../format/FormatView';
import { MetadataView } from '../format/MetadataView';
import { PaddingFormatRenderer } from '../format/formatPart/PaddingFormatRenderer';
import { TableCellMetadataFormatRender } from '../format/formatPart/TableCellMetadataFormatRender';
import { updateTableCellMetadata } from 'roosterjs-content-model/lib/modelApi/metadata/updateTableCellMetadata';
import { useProperty } from '../../hooks/useProperty';
import { VerticalAlignFormatRenderer } from '../format/formatPart/VerticalAlignFormatRenderer';
import {
ContentModelTableCell,
ContentModelTableCellFormat,
hasSelectionInBlockGroup,
} from 'roosterjs-content-model';

const styles = require('./ContentModelTableCellView.scss');

const TableCellFormatRenderers: FormatRenderer<ContentModelTableCellFormat>[] = [
...BorderFormatRenderers,
...DirectionFormatRenderers,
BorderBoxFormatRenderer,
BackgroundColorFormatRenderer,
PaddingFormatRenderer,
VerticalAlignFormatRenderer,
];

export function ContentModelTableCellView(props: { cell: ContentModelTableCell }) {
const { cell } = props;
const checkboxHeader = React.useRef<HTMLInputElement>(null);
const checkboxSpanLeft = React.useRef<HTMLInputElement>(null);
const checkboxSpanAbove = React.useRef<HTMLInputElement>(null);
const [isHeader, setIsHeader] = useProperty(cell.isHeader);
const [spanLeft, setSpanLeft] = useProperty(cell.spanLeft);
const [spanAbove, setSpanAbove] = useProperty(cell.spanAbove);

const onHeaderChanged = React.useCallback(() => {
const value = checkboxHeader.current.checked;
cell.isHeader = value;
setIsHeader(value);
}, [cell, setIsHeader]);

const onSpanLeftChanged = React.useCallback(() => {
const value = checkboxSpanLeft.current.checked;
cell.spanLeft = value;
setSpanLeft(value);
}, [cell, setSpanLeft]);

const onSpanAboveChanged = React.useCallback(() => {
const value = checkboxSpanAbove.current.checked;
cell.spanAbove = value;
setSpanAbove(value);
}, [cell, setSpanAbove]);

const getContent = React.useCallback(() => {
return (
<>
<div>
<input
type="checkbox"
checked={isHeader}
ref={checkboxHeader}
onChange={onHeaderChanged}
/>
Header
</div>
<div>
<input
type="checkbox"
checked={spanLeft}
ref={checkboxSpanLeft}
onChange={onSpanLeftChanged}
/>
Span Left
</div>
<div>
<input
type="checkbox"
checked={spanAbove}
ref={checkboxSpanAbove}
onChange={onSpanAboveChanged}
/>
Span Above
</div>
<BlockGroupContentView group={cell} />
</>
);
}, [cell, isHeader, spanAbove, spanLeft]);

const getMetadata = React.useCallback(() => {
return (
<MetadataView
model={cell}
renderers={[TableCellMetadataFormatRender]}
updater={updateTableCellMetadata}
/>
);
}, [cell]);

const getFormat = React.useCallback(() => {
return <FormatView format={cell.format} renderers={TableCellFormatRenderers} />;
}, [cell.format]);

const subTitle =
cell.spanAbove && cell.spanLeft ? '↖' : cell.spanLeft ? '←' : cell.spanAbove ? '↑' : '';

return (
<ContentModelView
title={isHeader ? 'TableCellHeader' : 'TableCell'}
subTitle={subTitle}
className={styles.modelTableCell}
hasSelection={hasSelectionInBlockGroup(cell)}
isSelected={cell.isSelected}
jsonSource={cell}
getContent={getContent}
getFormat={getFormat}
getMetadata={getMetadata}
/>
);
}
import * as React from 'react';
import { BackgroundColorFormatRenderer } from '../format/formatPart/BackgroundColorFormatRenderer';
import { BlockGroupContentView } from './BlockGroupContentView';
import { BorderBoxFormatRenderer } from '../format/formatPart/BorderBoxFormatRenderer';
import { BorderFormatRenderers } from '../format/formatPart/BorderFormatRenderers';
import { ContentModelView } from '../ContentModelView';
import { DirectionFormatRenderers } from '../format/formatPart/DirectionFormatRenderers';
import { FormatRenderer } from '../format/utils/FormatRenderer';
import { FormatView } from '../format/FormatView';
import { MetadataView } from '../format/MetadataView';
import { PaddingFormatRenderer } from '../format/formatPart/PaddingFormatRenderer';
import { TableCellMetadataFormatRender } from '../format/formatPart/TableCellMetadataFormatRender';
import { updateTableCellMetadata } from 'roosterjs-content-model/lib/modelApi/metadata/updateTableCellMetadata';
import { useProperty } from '../../hooks/useProperty';
import { VerticalAlignFormatRenderer } from '../format/formatPart/VerticalAlignFormatRenderer';
import { WordBreakFormatRenderer } from '../format/formatPart/WordBreakFormatRenderer';
import {
ContentModelTableCell,
ContentModelTableCellFormat,
hasSelectionInBlockGroup,
} from 'roosterjs-content-model';

const styles = require('./ContentModelTableCellView.scss');

const TableCellFormatRenderers: FormatRenderer<ContentModelTableCellFormat>[] = [
...BorderFormatRenderers,
...DirectionFormatRenderers,
BorderBoxFormatRenderer,
BackgroundColorFormatRenderer,
PaddingFormatRenderer,
VerticalAlignFormatRenderer,
WordBreakFormatRenderer,
];

export function ContentModelTableCellView(props: { cell: ContentModelTableCell }) {
const { cell } = props;
const checkboxHeader = React.useRef<HTMLInputElement>(null);
const checkboxSpanLeft = React.useRef<HTMLInputElement>(null);
const checkboxSpanAbove = React.useRef<HTMLInputElement>(null);
const [isHeader, setIsHeader] = useProperty(cell.isHeader);
const [spanLeft, setSpanLeft] = useProperty(cell.spanLeft);
const [spanAbove, setSpanAbove] = useProperty(cell.spanAbove);

const onHeaderChanged = React.useCallback(() => {
const value = checkboxHeader.current.checked;
cell.isHeader = value;
setIsHeader(value);
}, [cell, setIsHeader]);

const onSpanLeftChanged = React.useCallback(() => {
const value = checkboxSpanLeft.current.checked;
cell.spanLeft = value;
setSpanLeft(value);
}, [cell, setSpanLeft]);

const onSpanAboveChanged = React.useCallback(() => {
const value = checkboxSpanAbove.current.checked;
cell.spanAbove = value;
setSpanAbove(value);
}, [cell, setSpanAbove]);

const getContent = React.useCallback(() => {
return (
<>
<div>
<input
type="checkbox"
checked={isHeader}
ref={checkboxHeader}
onChange={onHeaderChanged}
/>
Header
</div>
<div>
<input
type="checkbox"
checked={spanLeft}
ref={checkboxSpanLeft}
onChange={onSpanLeftChanged}
/>
Span Left
</div>
<div>
<input
type="checkbox"
checked={spanAbove}
ref={checkboxSpanAbove}
onChange={onSpanAboveChanged}
/>
Span Above
</div>
<BlockGroupContentView group={cell} />
</>
);
}, [cell, isHeader, spanAbove, spanLeft]);

const getMetadata = React.useCallback(() => {
return (
<MetadataView
model={cell}
renderers={[TableCellMetadataFormatRender]}
updater={updateTableCellMetadata}
/>
);
}, [cell]);

const getFormat = React.useCallback(() => {
return <FormatView format={cell.format} renderers={TableCellFormatRenderers} />;
}, [cell.format]);

const subTitle =
cell.spanAbove && cell.spanLeft ? '↖' : cell.spanLeft ? '←' : cell.spanAbove ? '↑' : '';

return (
<ContentModelView
title={isHeader ? 'TableCellHeader' : 'TableCell'}
subTitle={subTitle}
className={styles.modelTableCell}
hasSelection={hasSelectionInBlockGroup(cell)}
isSelected={cell.isSelected}
jsonSource={cell}
getContent={getContent}
getFormat={getFormat}
getMetadata={getMetadata}
/>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { FormatHandler } from '../FormatHandler';
import { WordBreakFormat } from '../../publicTypes/format/formatParts/WordBreakFormat';

/**
* @internal
*/
export const wordBreakFormatHandler: FormatHandler<WordBreakFormat> = {
parse: (format, element, _, defaultStyle) => {
const wordBreak = element.style.wordBreak || defaultStyle.wordBreak;

if (wordBreak) {
format.wordBreak = wordBreak;
}
},
apply: (format, element) => {
if (format.wordBreak) {
element.style.wordBreak = format.wordBreak;
}
},
};
Loading

0 comments on commit 87a393d

Please sign in to comment.