Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Table Borders basic functionality #2146

Merged
merged 22 commits into from
Oct 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
3709847
border type
Andres-CT98 Oct 13, 2023
8f78aa9
add table border to core
Andres-CT98 Oct 13, 2023
25f5cfe
demo changes
Andres-CT98 Oct 13, 2023
791cdd3
add border functions, buttons, and metadata
Andres-CT98 Oct 13, 2023
e1c0afd
Merge branch 'master' of https://github.com/microsoft/roosterjs into …
Andres-CT98 Oct 13, 2023
0d21683
fix tests, eslint, dependencies
Andres-CT98 Oct 13, 2023
f5fd8f4
revert
Andres-CT98 Oct 13, 2023
49232f6
create new table border api, refactor
Andres-CT98 Oct 13, 2023
3e589f7
unify Border
Andres-CT98 Oct 17, 2023
9249364
revert core, move Table border store to UI
Andres-CT98 Oct 17, 2023
427b6c5
button changes
Andres-CT98 Oct 18, 2023
ed903fc
apply border abstracted, rename
Andres-CT98 Oct 18, 2023
68ea31d
cleanup, rename, Border Operations
Andres-CT98 Oct 18, 2023
d4fa45b
Merge branch 'master' of https://github.com/microsoft/roosterjs into …
Andres-CT98 Oct 18, 2023
131e014
Merge branch 'master' of https://github.com/microsoft/roosterjs into …
Andres-CT98 Oct 18, 2023
e81dd89
whitespace cleanup, change enum
Andres-CT98 Oct 18, 2023
eb2b4fd
rename, simplify
Andres-CT98 Oct 18, 2023
4f76c28
Merge branch 'master' of https://github.com/microsoft/roosterjs into …
Andres-CT98 Oct 18, 2023
1a92e63
fix metadata
Andres-CT98 Oct 18, 2023
031f142
Merge branch 'master' of https://github.com/microsoft/roosterjs into …
Andres-CT98 Oct 18, 2023
060ed16
add comment
Andres-CT98 Oct 18, 2023
ea06420
demo changes, delete cache
Andres-CT98 Oct 18, 2023
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
5 changes: 5 additions & 0 deletions demo/scripts/controls/ContentModelEditorMainPane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,11 @@ class ContentModelEditorMainPane extends MainPaneBase {
isDarkMode: this.themeMatch?.matches || false,
editorCreator: null,
isRtl: false,
tableBorderFormat: {
width: '1px',
style: 'solid',
color: '#ABABAB',
},
};
}

Expand Down
26 changes: 25 additions & 1 deletion demo/scripts/controls/MainPaneBase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import * as ReactDOM from 'react-dom';
import BuildInPluginState from './BuildInPluginState';
import SidePane from './sidePane/SidePane';
import SnapshotPlugin from './sidePane/snapshot/SnapshotPlugin';
import { Border } from 'roosterjs-content-model-editor';
import { EditorOptions, EditorPlugin, IEditor } from 'roosterjs-editor-types';
import { getDarkColor } from 'roosterjs-color-utils';
import { PartialTheme, ThemeProvider } from '@fluentui/react/lib/Theme';
import { registerWindowForCss, unregisterWindowForCss } from '../utils/cssMonitor';
import { trustedHTMLHandler } from '../utils/trustedHTMLHandler';
import { WindowProvider } from '@fluentui/react/lib/WindowProvider';
import { EditorOptions, EditorPlugin, IEditor } from 'roosterjs-editor-types';
import {
createUpdateContentPlugin,
Rooster,
Expand All @@ -24,6 +25,7 @@ export interface MainPaneBaseState {
isDarkMode: boolean;
editorCreator: (div: HTMLDivElement, options: EditorOptions) => IEditor;
isRtl: boolean;
tableBorderFormat?: Border;
}

const PopoutRoot = 'mainPane';
Expand Down Expand Up @@ -129,6 +131,28 @@ export default abstract class MainPaneBase extends React.Component<{}, MainPaneB
});
}

getTableBorder(): Border {
return this.state.tableBorderFormat;
}

setTableBorderColor(color: string): void {
this.setState({
tableBorderFormat: { ...this.getTableBorder(), color },
});
}

setTableBorderWidth(width: string): void {
this.setState({
tableBorderFormat: { ...this.getTableBorder(), width },
});
}

setTableBorderStyle(style: string): void {
this.setState({
tableBorderFormat: { ...this.getTableBorder(), style },
});
}

toggleDarkMode(): void {
this.setState({
isDarkMode: !this.state.isDarkMode,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,9 @@ export const TableCellMetadataFormatRenders: FormatRenderer<TableCellMetadataFor
format => format.vAlignOverride,
(format, value) => (format.vAlignOverride = value)
),
createCheckboxFormatRenderer<TableCellMetadataFormat>(
'BorderOverride',
format => format.borderOverride,
(format, value) => (format.borderOverride = value)
),
];
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ import { spacingButton } from './spacingButton';
import { strikethroughButton } from './strikethroughButton';
import { subscriptButton } from './subscriptButton';
import { superscriptButton } from './superscriptButton';
import { tableBorderApplyButton } from './tableBorderApplyButton';
import { tableBorderColorButton } from './tableBorderColorButton';
import { tableBorderStyleButton } from './tableBorderStyleButton';
import { tableBorderWidthButton } from './tableBorderWidthButton';
import { textColorButton } from './textColorButton';
import { underlineButton } from './underlineButton';
import { zoom } from '../zoom';
Expand Down Expand Up @@ -101,6 +105,10 @@ const buttons = [
tableSplitButton,
tableAlignCellButton,
tableAlignTableButton,
tableBorderApplyButton,
tableBorderColorButton,
tableBorderWidthButton,
tableBorderStyleButton,
imageBorderColorButton,
imageBorderWidthButton,
imageBorderStyleButton,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import MainPaneBase from '../../MainPaneBase';
import { applyTableBorderFormat, isContentModelEditor } from 'roosterjs-content-model-editor';
import { RibbonButton } from 'roosterjs-react';

/**
* UNFINISHED
* A map for all Border options and keys in yet to be made dropdown menu.
*/
export const tableBorderApplyButton: RibbonButton<'ribbonButtonTableBorder'> = {
key: 'ribbonButtonTableBorder',
iconName: 'TableComputed',
unlocalizedText: 'Table Border',
isDisabled: formatState => !formatState.isInTable,
dropDownMenu: {
items: {
menuNameTableBorder: 'All Borders',
},
},
onClick: (editor, key) => {
if (isContentModelEditor(editor) && key != 'ribbonButtonTableBorder') {
const border = MainPaneBase.getInstance().getTableBorder();
applyTableBorderFormat(editor, border, 'AllBorders');
}
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import MainPaneBase from '../../MainPaneBase';
import { getButtons, getTextColorValue, KnownRibbonButtonKey } from 'roosterjs-react';
import { isContentModelEditor } from 'roosterjs-content-model-editor';
import { RibbonButton } from 'roosterjs-react';

const originalButton = getButtons([KnownRibbonButtonKey.TextColor])[0] as RibbonButton<
'buttonNameTableBorderColor'
>;

/**
* @internal
* "Table Border Color" button on the format ribbon
*/
export const tableBorderColorButton: RibbonButton<'buttonNameTableBorderColor'> = {
...originalButton,
unlocalizedText: 'Table Border Color',
iconName: 'ColorSolid',
isDisabled: formatState => !formatState.isInTable,
onClick: (editor, key) => {
// This check will always be true, add it here just to satisfy compiler
if (key != 'buttonNameTableBorderColor' && isContentModelEditor(editor)) {
MainPaneBase.getInstance().setTableBorderColor(getTextColorValue(key).lightModeColor);
editor.focus();
}
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import MainPaneBase from '../../MainPaneBase';
import { isContentModelEditor } from 'roosterjs-content-model-editor';
import { RibbonButton } from 'roosterjs-react';

const STYLES: Record<string, string> = {
dashed: 'dashed',
dotted: 'dotted',
solid: 'solid',
double: 'doubled',
groove: 'groove',
ridge: 'ridge',
inset: 'inset',
outset: 'outset',
};

/**
* @internal
* "Table Border Style" button on the format ribbon
*/
export const tableBorderStyleButton: RibbonButton<'buttonNameTableBorderStyle'> = {
key: 'buttonNameTableBorderStyle',
unlocalizedText: 'Table Border Style',
iconName: 'LineStyle',
isDisabled: formatState => !formatState.isInTable,
dropDownMenu: {
items: STYLES,
allowLivePreview: true,
},
onClick: (editor, style) => {
if (isContentModelEditor(editor)) {
MainPaneBase.getInstance().setTableBorderStyle(style);
editor.focus();
}
return true;
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import MainPaneBase from '../../MainPaneBase';
import { isContentModelEditor } from 'roosterjs-content-model-editor';
import { RibbonButton } from 'roosterjs-react';

const WIDTH = [0.25, 0.5, 0.75, 1, 1.5, 2.25, 3, 4.5, 6];

/**
* @internal
* "Table Border Width" button on the format ribbon
*/
export const tableBorderWidthButton: RibbonButton<'buttonNameTableBorderWidth'> = {
key: 'buttonNameTableBorderWidth',
unlocalizedText: 'Table Border Width',
iconName: 'LineThickness',
isDisabled: formatState => !formatState.isInTable,
dropDownMenu: {
items: WIDTH.reduce((map, size) => {
map[size + 'pt'] = size.toString();
return map;
}, <Record<string, string>>{}),
allowLivePreview: true,
},
onClick: (editor, width) => {
if (isContentModelEditor(editor)) {
MainPaneBase.getInstance().setTableBorderWidth(width);
editor.focus();
}
return true;
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const TableCellMetadataFormatDefinition = createObjectDefinition<Required<TableC
{
bgColorOverride: createBooleanDefinition(true /** isOptional */),
vAlignOverride: createBooleanDefinition(true /** isOptional */),
borderOverride: createBooleanDefinition(true /** isOptional */),
},
false /* isOptional */,
true /** allowNull */
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export { ContentModelFormatState } from './publicTypes/format/formatState/ContentModelFormatState';
export { ImageFormatState } from './publicTypes/format/formatState/ImageFormatState';
export { Border } from './publicTypes/interface/Border';
export { BorderOperations } from './publicTypes/enum/BorderOperations';
export {
CreateEditorContext,
ContentModelCoreApiMap,
Expand Down Expand Up @@ -61,6 +62,7 @@ export { default as insertTable } from './publicApi/table/insertTable';
export { default as formatTable } from './publicApi/table/formatTable';
export { default as setTableCellShade } from './publicApi/table/setTableCellShade';
export { default as editTable } from './publicApi/table/editTable';
export { default as applyTableBorderFormat } from './publicApi/table/applyTableBorderFormat';
export { default as toggleBullet } from './publicApi/list/toggleBullet';
export { default as toggleNumbering } from './publicApi/list/toggleNumbering';
export { default as toggleBold } from './publicApi/segment/toggleBold';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const DEFAULT_FORMAT: Required<TableMetadataFormat> = {
type MetaOverrides = {
bgColorOverrides: boolean[][];
vAlignOverrides: boolean[][];
borderOverrides: boolean[][];
};

/**
Expand Down Expand Up @@ -71,14 +72,20 @@ function clearCache(rows: ContentModelTableRow[]) {
}

function updateOverrides(rows: ContentModelTableRow[], removeCellShade: boolean): MetaOverrides {
const overrides: MetaOverrides = { bgColorOverrides: [], vAlignOverrides: [] };
const overrides: MetaOverrides = {
bgColorOverrides: [],
vAlignOverrides: [],
borderOverrides: [],
};

rows.forEach(row => {
const bgColorOverrides: boolean[] = [];
const vAlignOverrides: boolean[] = [];
const borderOverrides: boolean[] = [];

overrides.bgColorOverrides.push(bgColorOverrides);
overrides.vAlignOverrides.push(vAlignOverrides);
overrides.borderOverrides.push(borderOverrides);

row.cells.forEach(cell => {
updateTableCellMetadata(cell, metadata => {
Expand All @@ -89,6 +96,7 @@ function updateOverrides(rows: ContentModelTableRow[], removeCellShade: boolean)
bgColorOverrides.push(!!metadata?.bgColorOverride);
}
vAlignOverrides.push(!!metadata?.vAlignOverride);
borderOverrides.push(!!metadata?.borderOverride);

return metadata;
});
Expand Down Expand Up @@ -170,31 +178,33 @@ function formatCells(
rows.forEach((row, rowIndex) => {
row.cells.forEach((cell, colIndex) => {
// Format Borders
const transparentBorderMatrix = BorderFormatters[
format.tableBorderFormat as TableBorderFormat
]({
firstRow: rowIndex === 0,
lastRow: rowIndex === rows.length - 1,
firstColumn: colIndex === 0,
lastColumn: colIndex === row.cells.length - 1,
});

const formatColor = [
format.topBorderColor,
format.verticalBorderColor,
format.bottomBorderColor,
format.verticalBorderColor,
];

transparentBorderMatrix.forEach((alwaysUseTransparent, i) => {
const borderColor = (!alwaysUseTransparent && formatColor[i]) || '';
if (!metaOverrides.borderOverrides[rowIndex][colIndex]) {
const transparentBorderMatrix = BorderFormatters[
format.tableBorderFormat as TableBorderFormat
]({
firstRow: rowIndex === 0,
lastRow: rowIndex === rows.length - 1,
firstColumn: colIndex === 0,
lastColumn: colIndex === row.cells.length - 1,
});

cell.format[BorderKeys[i]] = combineBorderValue({
style: getBorderStyleFromColor(borderColor),
width: '1px',
color: borderColor,
const formatColor = [
format.topBorderColor,
format.verticalBorderColor,
format.bottomBorderColor,
format.verticalBorderColor,
];

transparentBorderMatrix.forEach((alwaysUseTransparent, i) => {
const borderColor = (!alwaysUseTransparent && formatColor[i]) || '';

cell.format[BorderKeys[i]] = combineBorderValue({
style: getBorderStyleFromColor(borderColor),
width: '1px',
color: borderColor,
});
});
});
}

// Format Background Color
if (!metaOverrides.bgColorOverrides[rowIndex][colIndex]) {
Expand Down
Loading
Loading