jodit
Version:
Jodit is an awesome and useful wysiwyg editor with filebrowser
312 lines (311 loc) • 11.5 kB
JavaScript
/*!
* Jodit Editor (https://xdsoft.net/jodit/)
* Released under MIT see LICENSE.txt in the project root for license information.
* Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net
*/
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
r = Reflect.decorate(decorators, target, key, desc);
else
for (var i = decorators.length - 1; i >= 0; i--)
if (d = decorators[i])
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import * as consts from "../../core/constants.js";
import { autobind } from "../../core/decorators/index.js";
import { Dom } from "../../core/dom/dom.js";
import { pluginSystem } from "../../core/global.js";
import { $$, call, dataBind, getContentWidth, offset } from "../../core/helpers/index.js";
import { Plugin, Table } from "../../modules/index.js";
import "./config.js";
const key = 'table_processor_observer-resize';
/**
* Process tables in editor
*/
export class resizeCells extends Plugin {
constructor() {
super(...arguments);
this.selectMode = false;
this.resizeDelta = 0;
this.createResizeHandle = () => {
if (!this.resizeHandler) {
this.resizeHandler = this.j.c.div('jodit-table-resizer');
this.j.e
.on(this.resizeHandler, 'mousedown.table touchstart.table', this.onHandleMouseDown)
.on(this.resizeHandler, 'mouseenter.table', () => {
this.j.async.clearTimeout(this.hideTimeout);
});
}
};
this.hideTimeout = 0;
this.drag = false;
this.minX = 0;
this.maxX = 0;
this.startX = 0;
}
/**
* Shortcut for Table module
*/
get module() {
return this.j.getInstance('Table', this.j.o);
}
/**
* Now editor has rtl direction
*/
get isRTL() {
return this.j.o.direction === 'rtl';
}
showResizeHandle() {
this.j.async.clearTimeout(this.hideTimeout);
this.j.workplace.appendChild(this.resizeHandler);
}
hideResizeHandle() {
this.hideTimeout = this.j.async.setTimeout(() => {
Dom.safeRemove(this.resizeHandler);
}, {
timeout: this.j.defaultTimeout,
label: 'hideResizer'
});
}
/**
* Click on resize handle
*/
onHandleMouseDown(event) {
if (this.j.isLocked) {
return;
}
this.drag = true;
this.j.e
.on(this.j.ow, 'mouseup.resize-cells touchend.resize-cells', this.onMouseUp)
.on(this.j.ew, 'mousemove.table touchmove.table', this.onMouseMove);
this.startX = event.clientX;
this.j.lock(key);
this.resizeHandler.classList.add('jodit-table-resizer_moved');
let box, tableBox = this.workTable.getBoundingClientRect();
this.minX = 0;
this.maxX = 1000000;
if (this.wholeTable != null) {
tableBox = this.workTable.parentNode.getBoundingClientRect();
this.minX = tableBox.left;
this.maxX = this.minX + tableBox.width;
}
else {
// find maximum columns
const coordinate = this.module.formalCoordinate(this.workTable, this.workCell, true);
this.module.formalMatrix(this.workTable, (td, i, j) => {
if (coordinate[1] === j) {
box = td.getBoundingClientRect();
this.minX = Math.max(box.left + consts.NEARBY / 2, this.minX);
}
if (coordinate[1] + (this.isRTL ? -1 : 1) === j) {
box = td.getBoundingClientRect();
this.maxX = Math.min(box.left + box.width - consts.NEARBY / 2, this.maxX);
}
});
}
return false;
}
/**
* Mouse move after click on resize handle
*/
onMouseMove(event) {
if (!this.drag) {
return;
}
this.j.e.fire('closeAllPopups');
let x = event.clientX;
const workplacePosition = offset((this.resizeHandler.parentNode ||
this.j.od.documentElement), this.j, this.j.od, true);
if (x < this.minX) {
x = this.minX;
}
if (x > this.maxX) {
x = this.maxX;
}
this.resizeDelta =
x - this.startX + (!this.j.o.iframe ? 0 : workplacePosition.left);
this.resizeHandler.style.left =
x - (this.j.o.iframe ? 0 : workplacePosition.left) + 'px';
const sel = this.j.s.sel;
sel && sel.removeAllRanges();
}
/**
* Mouse up every where after move and click
*/
onMouseUp(e) {
if (this.selectMode || this.drag) {
this.selectMode = false;
this.j.unlock();
}
if (!this.resizeHandler || !this.drag) {
return;
}
this.drag = false;
this.j.e.off(this.j.ew, 'mousemove.table touchmove.table', this.onMouseMove);
this.resizeHandler.classList.remove('jodit-table-resizer_moved');
if (this.startX !== e.clientX) {
// resize column
if (this.wholeTable == null) {
this.resizeColumns();
}
else {
this.resizeTable();
}
}
this.j.synchronizeValues();
this.j.s.focus();
}
/**
* Resize only one column
*/
resizeColumns() {
const delta = this.resizeDelta;
const marked = [];
const tableModule = this.module;
tableModule.setColumnWidthByDelta(this.workTable, tableModule.formalCoordinate(this.workTable, this.workCell, true)[1], delta, true, marked);
const nextTD = call(this.isRTL ? Dom.prev : Dom.next, this.workCell, Dom.isCell, this.workCell.parentNode);
tableModule.setColumnWidthByDelta(this.workTable, tableModule.formalCoordinate(this.workTable, nextTD)[1], -delta, false, marked);
}
/**
* Resize whole table
*/
resizeTable() {
const delta = this.resizeDelta * (this.isRTL ? -1 : 1);
const width = this.workTable.offsetWidth, parentWidth = getContentWidth(this.workTable.parentNode, this.j.ew);
// for RTL use mirror logic
const rightSide = !this.wholeTable;
const needChangeWidth = this.isRTL ? !rightSide : rightSide;
// right side
if (needChangeWidth) {
this.workTable.style.width =
((width + delta) / parentWidth) * 100 + '%';
}
else {
const side = this.isRTL ? 'marginRight' : 'marginLeft';
const margin = parseInt(this.j.ew.getComputedStyle(this.workTable)[side] || '0', 10);
this.workTable.style.width =
((width - delta) / parentWidth) * 100 + '%';
this.workTable.style[side] =
((margin + delta) / parentWidth) * 100 + '%';
}
}
/**
* Memoize current cell
*
* @param wholeTable - resize whole table by left side,
* false - resize whole table by right side, null - resize column
*/
setWorkCell(cell, wholeTable = null) {
this.wholeTable = wholeTable;
this.workCell = cell;
this.workTable = Dom.up(cell, (elm) => Dom.isTag(elm, 'table'), this.j.editor);
}
/**
* Calc helper resize handle position
*/
calcHandlePosition(table, cell, offsetX = 0, delta = 0) {
const box = offset(cell, this.j, this.j.ed);
if (offsetX > consts.NEARBY && offsetX < box.width - consts.NEARBY) {
this.hideResizeHandle();
return;
}
const workplacePosition = offset(this.j.workplace, this.j, this.j.od, true), parentBox = offset(table, this.j, this.j.ed);
this.resizeHandler.style.left =
(offsetX <= consts.NEARBY ? box.left : box.left + box.width) -
workplacePosition.left +
delta +
'px';
Object.assign(this.resizeHandler.style, {
height: parentBox.height + 'px',
top: parentBox.top - workplacePosition.top + 'px'
});
this.showResizeHandle();
if (offsetX <= consts.NEARBY) {
const prevTD = call(this.isRTL ? Dom.next : Dom.prev, cell, Dom.isCell, cell.parentNode);
this.setWorkCell(prevTD || cell, prevTD ? null : true);
}
else {
const nextTD = call(!this.isRTL ? Dom.next : Dom.prev, cell, Dom.isCell, cell.parentNode);
this.setWorkCell(cell, !nextTD ? false : null);
}
}
/** @override */
afterInit(editor) {
if (!editor.o.tableAllowCellResize) {
return;
}
editor.e
.off(this.j.ow, '.resize-cells')
.off('.resize-cells')
.on('change.resize-cells afterCommand.resize-cells afterSetMode.resize-cells', () => {
$$('table', editor.editor).forEach(this.observe);
})
.on(this.j.ow, 'scroll.resize-cells', () => {
if (!this.drag) {
return;
}
const parent = Dom.up(this.workCell, (elm) => Dom.isTag(elm, 'table'), editor.editor);
if (parent) {
const parentBox = parent.getBoundingClientRect();
this.resizeHandler.style.top = parentBox.top + 'px';
}
})
.on('beforeSetMode.resize-cells', () => {
const tableModule = this.module;
tableModule.getAllSelectedCells().forEach(td => {
tableModule.removeSelection(td);
tableModule.normalizeTable(Dom.closest(td, 'table', editor.editor));
});
});
}
/**
* Add to every Table listeners
*/
observe(table) {
if (dataBind(table, key)) {
return;
}
dataBind(table, key, true);
this.j.e
.on(table, 'mouseleave.resize-cells', (e) => {
if (this.resizeHandler &&
this.resizeHandler !== e.relatedTarget) {
this.hideResizeHandle();
}
})
.on(table, 'mousemove.resize-cells touchmove.resize-cells', this.j.async.throttle((event) => {
if (this.j.isLocked) {
return;
}
const cell = Dom.up(event.target, Dom.isCell, table);
if (!cell) {
return;
}
this.calcHandlePosition(table, cell, event.offsetX);
}, {
timeout: this.j.defaultTimeout
}));
this.createResizeHandle();
}
beforeDestruct(jodit) {
if (jodit.events) {
jodit.e.off(this.j.ow, '.resize-cells');
jodit.e.off('.resize-cells');
}
}
}
__decorate([
autobind
], resizeCells.prototype, "onHandleMouseDown", null);
__decorate([
autobind
], resizeCells.prototype, "onMouseMove", null);
__decorate([
autobind
], resizeCells.prototype, "onMouseUp", null);
__decorate([
autobind
], resizeCells.prototype, "observe", null);
pluginSystem.add('resizeCells', resizeCells);