Skip to content

Commit

Permalink
Add parameter to Disable table edit features (#2624)
Browse files Browse the repository at this point in the history
* disableFeatures, renaming

* Export Table Feature Ids

* Modify and create tests

* move Id to respective feature, add tests, cleanup

* export feature names

* TableEditFeatureName
  • Loading branch information
Andres-CT98 authored May 10, 2024
1 parent 6c69ef9 commit b1d4bab
Show file tree
Hide file tree
Showing 14 changed files with 254 additions and 114 deletions.
1 change: 1 addition & 0 deletions packages/roosterjs-content-model-plugins/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export { TableEditPlugin } from './tableEdit/TableEditPlugin';
export { OnTableEditorCreatedCallback } from './tableEdit/OnTableEditorCreatedCallback';
export { TableEditFeatureName } from './tableEdit/editors/features/TableEditFeatureName';
export { PastePlugin } from './paste/PastePlugin';
export { EditPlugin } from './edit/EditPlugin';
export { AutoFormatPlugin, AutoFormatOptions } from './autoFormat/AutoFormatPlugin';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type { TableEditFeatureName } from './editors/features/TableEditFeatureName';

/**
* Optional callback when creating a TableEditPlugin, allows to customize the Selectors element as required.
*/
export type OnTableEditorCreatedCallback = (
editorType: 'HorizontalTableInserter' | 'VerticalTableInserter' | 'TableMover' | 'TableResizer',
featureType: TableEditFeatureName,
element: HTMLElement
) => () => void;
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { isNodeOfType, normalizeRect } from 'roosterjs-content-model-dom';
import { TableEditor } from './editors/TableEditor';
import type { TableEditFeatureName } from './editors/features/TableEditFeatureName';
import type { OnTableEditorCreatedCallback } from './OnTableEditorCreatedCallback';
import type { EditorPlugin, IEditor, PluginEvent, Rect } from 'roosterjs-content-model-types';

Expand All @@ -20,10 +21,12 @@ export class TableEditPlugin implements EditorPlugin {
* The container must not be affected by transform: scale(), otherwise the position calculation will be wrong.
* If not specified, the plugin will be inserted in document.body
* @param onTableEditorCreated An optional callback to customize the Table Editors elements when created.
* @param disableFeatures An optional array of TableEditFeatures to disable
*/
constructor(
private anchorContainerSelector?: string,
private onTableEditorCreated?: OnTableEditorCreatedCallback
private onTableEditorCreated?: OnTableEditorCreatedCallback,
private disableFeatures?: TableEditFeatureName[]
) {}

/**
Expand Down Expand Up @@ -147,7 +150,8 @@ export class TableEditPlugin implements EditorPlugin {
this.invalidateTableRects,
isNodeOfType(container, 'ELEMENT_NODE') ? container : undefined,
event?.currentTarget,
this.onTableEditorCreated
this.onTableEditorCreated,
this.disableFeatures
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { isNodeOfType, normalizeRect, parseTableCells } from 'roosterjs-content-
import type { OnTableEditorCreatedCallback } from '../OnTableEditorCreatedCallback';
import type { TableEditFeature } from './features/TableEditFeature';
import type { IEditor, TableSelection } from 'roosterjs-content-model-types';
import type { TableEditFeatureName } from './features/TableEditFeatureName';

const INSERTER_HOVER_OFFSET = 6;
const enum TOP_OR_SIDE {
Expand Down Expand Up @@ -67,7 +68,8 @@ export class TableEditor {
private onChanged: () => void,
private anchorContainer?: HTMLElement,
private contentDiv?: EventTarget | null,
private onTableEditorCreated?: OnTableEditorCreatedCallback
private onTableEditorCreated?: OnTableEditorCreatedCallback,
private disableFeatures?: TableEditFeatureName[]
) {
this.isRTL = editor.getDocument().defaultView?.getComputedStyle(table).direction == 'rtl';
this.setEditorFeatures();
Expand Down Expand Up @@ -145,10 +147,11 @@ export class TableEditor {
if (i === 0 && topOrSide == TOP_OR_SIDE.top) {
const center = (tdRect.left + tdRect.right) / 2;
const isOnRightHalf = this.isRTL ? x < center : x > center;
this.setInserterTd(
isOnRightHalf ? td : tr.cells[j - 1],
false /*isHorizontal*/
);
!this.disableFeatures?.includes('VerticalTableInserter') &&
this.setInserterTd(
isOnRightHalf ? td : tr.cells[j - 1],
false /*isHorizontal*/
);
} else if (j === 0 && topOrSide == TOP_OR_SIDE.side) {
const tdAbove = this.table.rows[i - 1]?.cells[0];
const tdAboveRect = tdAbove
Expand All @@ -161,17 +164,18 @@ export class TableEditor {
? tdAboveRect.right === tdRect.right
: tdAboveRect.left === tdRect.left;

this.setInserterTd(
y < (tdRect.top + tdRect.bottom) / 2 && isTdNotAboveMerged
? tdAbove
: td,
true /*isHorizontal*/
);
!this.disableFeatures?.includes('HorizontalTableInserter') &&
this.setInserterTd(
y < (tdRect.top + tdRect.bottom) / 2 && isTdNotAboveMerged
? tdAbove
: td,
true /*isHorizontal*/
);
} else {
this.setInserterTd(null);
}

this.setResizingTd(td);
!this.disableFeatures?.includes('CellResizer') && this.setResizingTd(td);

//Cell found
break;
Expand All @@ -188,19 +192,22 @@ export class TableEditor {
}

private setEditorFeatures() {
if (!this.tableMover) {
const disableSelector = this.disableFeatures?.includes('TableSelector');
const disableMovement = this.disableFeatures?.includes('TableMover');
if (!this.tableMover && !(disableSelector && disableMovement)) {
this.tableMover = createTableMover(
this.table,
this.editor,
this.isRTL,
this.onSelect,
disableSelector ? () => {} : this.onSelect,
this.contentDiv,
this.anchorContainer,
this.onEditorCreated
this.onEditorCreated,
disableMovement
);
}

if (!this.tableResizer) {
if (!this.tableResizer && !this.disableFeatures?.includes('TableResizer')) {
this.tableResizer = createTableResizer(
this.table,
this.editor,
Expand All @@ -214,15 +221,8 @@ export class TableEditor {
}
}

private onEditorCreated = (
editorType:
| 'HorizontalTableInserter'
| 'VerticalTableInserter'
| 'TableMover'
| 'TableResizer',
element: HTMLElement
) => {
const disposer = this.onTableEditorCreated?.(editorType, element);
private onEditorCreated = (featureType: TableEditFeatureName, element: HTMLElement) => {
const disposer = this.onTableEditorCreated?.(featureType, element);
const onMouseOut = element && this.getOnMouseOut(element);
if (onMouseOut) {
element.addEventListener('mouseout', onMouseOut);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createElement } from '../../../pluginUtils/CreateElement/createElement';
import { DragAndDropHelper } from '../../../pluginUtils/DragAndDrop/DragAndDropHelper';
import type { TableEditFeature } from './TableEditFeature';
import {
isElementOfType,
normalizeRect,
Expand All @@ -9,9 +10,16 @@ import {
} from 'roosterjs-content-model-dom';
import type { DragAndDropHandler } from '../../../pluginUtils/DragAndDrop/DragAndDropHandler';
import type { ContentModelTable, IEditor } from 'roosterjs-content-model-types';
import type { TableEditFeature } from './TableEditFeature';

const CELL_RESIZER_WIDTH = 4;
/**
* @internal
*/
export const HORIZONTAL_RESIZER_ID = 'horizontalResizer';
/**
* @internal
*/
export const VERTICAL_RESIZER_ID = 'verticalResizer';

/**
* @internal
Expand Down Expand Up @@ -223,7 +231,7 @@ function setHorizontalPosition(context: DragAndDropContext, trigger: HTMLElement
const { td } = context;
const rect = normalizeRect(td.getBoundingClientRect());
if (rect) {
trigger.id = 'horizontalResizer';
trigger.id = HORIZONTAL_RESIZER_ID;
trigger.style.top = rect.bottom - CELL_RESIZER_WIDTH + 'px';
trigger.style.left = rect.left + 'px';
trigger.style.width = rect.right - rect.left + 'px';
Expand All @@ -235,7 +243,7 @@ function setVerticalPosition(context: DragAndDropContext, trigger: HTMLElement)
const { td, isRTL } = context;
const rect = normalizeRect(td.getBoundingClientRect());
if (rect) {
trigger.id = 'verticalResizer';
trigger.id = VERTICAL_RESIZER_ID;
trigger.style.top = rect.top + 'px';
trigger.style.left = (isRTL ? rect.left : rect.right) - CELL_RESIZER_WIDTH + 1 + 'px';
trigger.style.width = CELL_RESIZER_WIDTH + 'px';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ export interface TableEditFeature {
/**
* @internal
*/
export function disposeTableEditFeature(resizer: TableEditFeature | null) {
if (resizer) {
resizer.featureHandler?.dispose();
resizer.featureHandler = null;
resizer.div?.parentNode?.removeChild(resizer.div);
resizer.div = null;
export function disposeTableEditFeature(feature: TableEditFeature | null) {
if (feature) {
feature.featureHandler?.dispose();
feature.featureHandler = null;
feature.div?.parentNode?.removeChild(feature.div);
feature.div = null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* Names of table edit features
*/
export type TableEditFeatureName =
| 'HorizontalTableInserter'
| 'VerticalTableInserter'
| 'TableMover'
| 'TableResizer'
| 'TableSelector'
| 'CellResizer';
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createElement } from '../../../pluginUtils/CreateElement/createElement';
import { getIntersectedRect } from '../../../pluginUtils/Rect/getIntersectedRect';
import { isElementOfType, normalizeRect } from 'roosterjs-content-model-dom';
import type { TableEditFeature } from './TableEditFeature';
import type { OnTableEditorCreatedCallback } from '../../OnTableEditorCreatedCallback';
import {
formatTableWithContentModel,
Expand All @@ -9,13 +10,20 @@ import {
} from 'roosterjs-content-model-api';
import type { CreateElementData } from '../../../pluginUtils/CreateElement/CreateElementData';
import type { Disposable } from '../../../pluginUtils/Disposable';
import type { TableEditFeature } from './TableEditFeature';
import type { IEditor } from 'roosterjs-content-model-types';

const INSERTER_COLOR = '#4A4A4A';
const INSERTER_COLOR_DARK_MODE = 'white';
const INSERTER_SIDE_LENGTH = 12;
const INSERTER_BORDER_SIZE = 1;
/**
* @internal
*/
export const HORIZONTAL_INSERTER_ID = 'horizontalInserter';
/**
* @internal
*/
export const VERTICAL_INSERTER_ID = 'verticalInserter';

/**
* @internal
Expand Down Expand Up @@ -48,7 +56,7 @@ export function createTableInserter(

if (isHorizontal) {
// tableRect.left/right is used because the Inserter is always intended to be on the side
div.id = 'horizontalInserter';
div.id = HORIZONTAL_INSERTER_ID;
div.style.left = `${
isRTL
? tableRect.right
Expand All @@ -57,7 +65,7 @@ export function createTableInserter(
div.style.top = `${tdRect.bottom - 8}px`;
(div.firstChild as HTMLElement).style.width = `${tableRect.right - tableRect.left}px`;
} else {
div.id = 'verticalInserter';
div.id = VERTICAL_INSERTER_ID;
div.style.left = `${isRTL ? tdRect.left - 8 : tdRect.right - 8}px`;
// tableRect.top is used because the Inserter is always intended to be on top
div.style.top = `${
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { createElement } from '../../../pluginUtils/CreateElement/createElement';
import { DragAndDropHelper } from '../../../pluginUtils/DragAndDrop/DragAndDropHelper';
import { isNodeOfType, normalizeRect } from 'roosterjs-content-model-dom';
import type { TableEditFeature } from './TableEditFeature';
import type { OnTableEditorCreatedCallback } from '../../OnTableEditorCreatedCallback';
import type { DragAndDropHandler } from '../../../pluginUtils/DragAndDrop/DragAndDropHandler';
import type { IEditor, Rect } from 'roosterjs-content-model-types';
import type { TableEditFeature } from './TableEditFeature';

const TABLE_MOVER_LENGTH = 12;
const TABLE_MOVER_ID = '_Table_Mover';
/**
* @internal
*/
export const TABLE_MOVER_ID = '_Table_Mover';

/**
* @internal
Expand All @@ -21,7 +24,8 @@ export function createTableMover(
onFinishDragging: (table: HTMLTableElement) => void,
contentDiv?: EventTarget | null,
anchorContainer?: HTMLElement,
onTableEditorCreated?: OnTableEditorCreatedCallback
onTableEditorCreated?: OnTableEditorCreatedCallback,
disableMovement?: boolean
): TableEditFeature | null {
const rect = normalizeRect(table.getBoundingClientRect());

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createElement } from '../../../pluginUtils/CreateElement/createElement';
import { DragAndDropHelper } from '../../../pluginUtils/DragAndDrop/DragAndDropHelper';

import type { TableEditFeature } from './TableEditFeature';
import type { OnTableEditorCreatedCallback } from '../../OnTableEditorCreatedCallback';
import {
getFirstSelectedTable,
Expand All @@ -9,11 +9,13 @@ import {
normalizeTable,
} from 'roosterjs-content-model-dom';
import type { ContentModelTable, IEditor, Rect } from 'roosterjs-content-model-types';
import type { TableEditFeature } from './TableEditFeature';
import type { DragAndDropHandler } from '../../../pluginUtils/DragAndDrop/DragAndDropHandler';

const TABLE_RESIZER_LENGTH = 12;
const TABLE_RESIZER_ID = '_Table_Resizer';
/**
* @internal
*/
export const TABLE_RESIZER_ID = '_Table_Resizer';

/**
* @internal
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as TestHelper from '../TestHelper';
import { DOMEventHandlerFunction } from 'roosterjs-editor-types';
import { getObjectKeys, normalizeTable } from 'roosterjs-content-model-dom';
import { TableEditFeatureName } from '../../lib/tableEdit/editors/features/TableEditFeatureName';
import { TableEditPlugin } from '../../lib/tableEdit/TableEditPlugin';
import {
ContentModelTable,
Expand All @@ -15,8 +16,12 @@ import {
* @param anchorContainerSelector The selector for the anchor container
* @returns The editor, plugin, and handler to be used in the test
*/
export function beforeTableTest(TEST_ID: string, anchorContainerSelector?: string) {
const plugin = new TableEditPlugin('.' + anchorContainerSelector);
export function beforeTableTest(
TEST_ID: string,
anchorContainerSelector?: string,
disabledFeatures?: TableEditFeatureName[]
) {
const plugin = new TableEditPlugin('.' + anchorContainerSelector, undefined, disabledFeatures);

let handler: Record<string, DOMEventHandlerFunction> = {};
const attachDomEvent = jasmine
Expand Down
Loading

0 comments on commit b1d4bab

Please sign in to comment.