suneditor
Version:
Vanilla JavaScript based WYSIWYG web editor
841 lines (753 loc) • 29.7 kB
JavaScript
import { dom, numbers, converter, env } from '../../../../helper';
import { SelectMenu } from '../../../../modules/ui';
import * as Constants from '../shared/table.constants';
import { CreateBorderMenu, CreateBorderFormatMenu } from '../render/table.menu';
import { CreateHTML_controller_properties } from '../render/table.html';
import { ColorPicker, Controller } from '../../../../modules/contract';
import { CreateCellsString, InvalidateTableCache } from '../shared/table.utils';
const { _w } = env;
/**
* @description Manages table and cell styling including borders, colors, alignment, and property controllers.
*/
export class TableStyleService {
#main;
#state;
#$;
/**
* @constructor
* @param {import('../index').default} main Table index
* @param {Object} options - Options
* @param {import('../index').TablePluginOptions} options.pluginOptions - Plugin options
* @param {HTMLElement} options.controller_table - Controller table element
*/
constructor(main, { pluginOptions, controller_table }) {
this.#main = main;
this.#state = main.state;
this.#$ = main.$;
this.sliderType = '';
/** @type {HTMLButtonElement} */
this.resizeButton = controller_table.querySelector('._se_table_resize');
/** @type {HTMLSpanElement} */
this.resizeText = controller_table.querySelector('._se_table_resize > span > span');
/** @type {HTMLButtonElement} */
this.columnFixedButton = controller_table.querySelector('._se_table_fixed_column');
/** @type {HTMLButtonElement} */
this.headerButton = controller_table.querySelector('._se_table_header');
/** @type {HTMLButtonElement} */
this.captionButton = controller_table.querySelector('._se_table_caption');
// props
const controller_props = CreateHTML_controller_properties(this.#$);
const propsTargets = [this.#main.controller_table, this.#main.controller_cell];
this.controller_props = new Controller(this, this.#$, controller_props.html, { position: 'bottom', parents: propsTargets, isInsideForm: true });
this.controller_props_title = controller_props.controller_props_title;
// color picker
const colorForm = dom.utils.createElement('DIV', { class: 'se-controller se-list-layer' }, null);
this.controller_colorPicker = new Controller(this, this.#$, colorForm, {
position: 'bottom',
parents: [this.controller_props].concat(propsTargets),
isInsideForm: true,
isWWTarget: false,
initMethod: () => {
this.colorPicker.hueSlider.close();
dom.utils.removeClass(this.controller_colorPicker.currentTarget, 'on');
},
});
this.colorPicker = new ColorPicker(this, this.#$, '', {
form: colorForm,
colorList: pluginOptions.colorList || Constants.DEFAULT_COLOR_LIST,
splitNum: 5,
disableRemove: true,
hueSliderOptions: { controllerOptions: { isOutsideForm: true, parents: [this.controller_colorPicker], parentsHide: true } },
});
// members - SelectMenu - properties - border style
const borderMenu = CreateBorderMenu();
const borderButton = controller_props.borderButton;
this.selectMenu_props_border = new SelectMenu(this.#$, { checkList: false, position: 'bottom-center' });
this.selectMenu_props_border.on(borderButton, this.#OnPropsBorderEdit.bind(this));
this.selectMenu_props_border.create(borderMenu.items, borderMenu.menus);
// members - SelectMenu - properties - border format
const borderFormatMenu = CreateBorderFormatMenu(this.#$.lang, this.#$.icons, []);
const borderFormatButton = controller_props.borderFormatButton;
this.selectMenu_props_border_format = new SelectMenu(this.#$, { checkList: false, position: 'bottom-left', dir: 'ltr', splitNum: 5 });
this.selectMenu_props_border_format.on(borderFormatButton, this.#OnPropsBorderFormatEdit.bind(this, 'all'));
this.selectMenu_props_border_format.create(borderFormatMenu.items, borderFormatMenu.menus);
const borderFormatMenu_oneCell = CreateBorderFormatMenu(this.#$.lang, this.#$.icons, Constants.BORDER_FORMAT_INSIDE);
this.selectMenu_props_border_format_oneCell = new SelectMenu(this.#$, { checkList: false, position: 'bottom-left', dir: 'ltr', splitNum: 6 });
this.selectMenu_props_border_format_oneCell.on(borderFormatButton, this.#OnPropsBorderFormatEdit.bind(this, 'outside'));
this.selectMenu_props_border_format_oneCell.create(borderFormatMenu_oneCell.items, borderFormatMenu_oneCell.menus);
// memberts - elements..
this.maxText = this.#$.lang.maxSize;
this.minText = this.#$.lang.minSize;
this.propTargets = {
cell_alignment: controller_props.cell_alignment,
cell_alignment_vertical: controller_props.cell_alignment_vertical,
cell_alignment_table_text: controller_props.cell_alignment_table_text,
border_format: borderFormatButton,
border_style: controller_props.border_style,
border_color: controller_props.border_color,
border_width: controller_props.border_width,
back_color: controller_props.back_color,
font_color: controller_props.font_color,
palette_border_button: controller_props.palette_border_button,
font_bold: controller_props.font_bold,
font_underline: controller_props.font_underline,
font_italic: controller_props.font_italic,
font_strike: controller_props.font_strike,
};
this._propsCache = [];
this._currentFontStyles = [];
this._propsAlignCache = '';
this._propsVerticalAlignCache = '';
this._typeCache = '';
}
get #selectionService() {
return this.#main.selectionService;
}
/**
* @hook Modules.ColorPicker
* @type {SunEditor.Hook.ColorPicker.Action}
*/
colorPickerAction(color) {
this.applyColorPicker(color);
}
/**
* @hook Modules.Controller
* @type {SunEditor.Hook.Controller.Action}
*/
controllerAction(target) {
const command = target.getAttribute('data-command');
if (!command) return;
const value = target.getAttribute('data-value');
switch (command) {
case 'props_onborder_format':
this.openBorderFormatMenu();
break;
case 'props_onborder_style':
this.openBorderStyleMenu();
break;
case 'props_onpalette':
this.openColorPalette(target, value);
break;
case 'props_font_style':
this.toggleFontStyle(value);
break;
case 'props_submit':
this.submitProps(target);
break;
case 'props_align':
this.setAlignProps(target.getAttribute('data-value'));
break;
case 'props_align_vertical':
this.setVerticalAlignProps(target.getAttribute('data-value'));
break;
}
}
/**
* @description Opens the table properties dialog.
* @param {HTMLElement} target - The target element (usually the table).
*/
openTableProps(target) {
if (this.controller_props.currentTarget === target && this.controller_props.form?.style.display === 'block') {
this.controller_props.close();
} else {
this.controller_props_title.textContent = this.#$.lang.tableProperties;
this.#setCtrlProps('table');
this.controller_props.open(target, this.#main.controller_table.form, { isWWTarget: false, initMethod: null, addOffset: null });
}
}
/**
* @description Opens the cell properties dialog.
* @param {HTMLElement} target - The target element (usually the table cell).
*/
openCellProps(target) {
if (this.controller_props.currentTarget === target && this.controller_props.form?.style.display === 'block') {
this.controller_props.close();
} else {
this.controller_props_title.textContent = this.#$.lang.cellProperties;
this.#setCtrlProps('cell');
this.controller_props.open(target, this.#main.controller_cell.form, { isWWTarget: false, initMethod: null, addOffset: null });
}
}
/**
* @description Opens the border format menu.
*/
openBorderFormatMenu() {
if (this._propsCache.length === 1) {
this.selectMenu_props_border_format_oneCell.open();
} else {
this.selectMenu_props_border_format.open();
}
}
/**
* @description Opens the border style menu.
*/
openBorderStyleMenu() {
this.selectMenu_props_border.open();
}
/**
* @description Handles color selection from the color palette.
* @param {Node} button The button triggering the color palette.
* @param {string} type The type of color selection.
*/
openColorPalette(button, type) {
const { back_color, font_color, border_color } = this.propTargets;
const color = type === 'border' ? border_color : type === 'back' ? back_color : font_color;
if (this.controller_colorPicker.isOpen && type === this.sliderType) {
this.controller_colorPicker.close();
} else {
this.sliderType = type;
dom.utils.addClass(button, 'on');
this.colorPicker.init(color?.value || '', button);
this.controller_colorPicker.open(button, null, { isWWTarget: false, initMethod: null, addOffset: null });
}
}
/**
* @description Toggles the font style.
* @param {string} value - The style to toggle (`bold`|`underline`|`italic`|`strike`).
*/
toggleFontStyle(value) {
dom.utils.toggleClass(this.propTargets[`font_${value}`], 'on');
}
/**
* @description Toggles the visibility of the table header (`<thead>`). If the header is present, it is removed; if absent, it is added.
*/
toggleHeader() {
const btn = this.headerButton;
const active = dom.utils.hasClass(btn, 'active');
const table = this.#main._element;
InvalidateTableCache(table);
if (!active) {
const header = dom.utils.createElement('THEAD');
header.innerHTML = '<tr>' + CreateCellsString('th', this.#state.logical_cellCnt) + '</tr>';
table.insertBefore(header, table.querySelector('tbody'));
} else {
dom.utils.removeItem(table.querySelector('thead'));
}
dom.utils.toggleClass(btn, 'active');
if (/TH/i.test(this.#state.tdElement.nodeName)) {
this.#main._closeController();
} else {
this.#main._setCellControllerPosition(this.#state.tdElement, false);
}
}
/**
* @description Toggles the visibility of the table caption (`<caption>`). If the caption is present, it is removed; if absent, it is added.
*/
toggleCaption() {
const btn = this.captionButton;
const active = dom.utils.hasClass(btn, 'active');
const table = this.#main._element;
if (!active) {
const caption = dom.utils.createElement('CAPTION', { class: `se-table-caption-${this.#main.captionPosition}` });
caption.innerHTML = '<div><br></div>';
table.insertBefore(caption, table.firstElementChild);
} else {
dom.utils.removeItem(table.querySelector('caption'));
}
dom.utils.toggleClass(btn, 'active');
this.#main._setCellControllerPosition(this.#state.tdElement, false);
}
/**
* @description Resets the header button state.
* @param {HTMLTableElement} table - The table element.
*/
resetHeaderButton(table) {
if (table.querySelector('thead')) dom.utils.addClass(this.headerButton, 'active');
else dom.utils.removeClass(this.headerButton, 'active');
}
/**
* @description Resets the caption button state.
* @param {HTMLTableElement} table - The table element.
*/
resetCaptionButton(table) {
if (table.querySelector('caption')) dom.utils.addClass(this.captionButton, 'active');
else dom.utils.removeClass(this.captionButton, 'active');
}
/**
* @description Resets the alignment properties for table cells.
*/
resetPropsAlign() {
const { cell_alignment } = this.propTargets;
const left = cell_alignment.querySelector('[data-value="left"]');
const right = cell_alignment.querySelector('[data-value="right"]');
const l_parent = left.parentElement;
const r_parent = right.parentElement;
l_parent.appendChild(right);
r_parent.appendChild(left);
}
/**
* @description Reverts the properties to their previous state.
*/
revertProps() {
const propsCache = this._propsCache;
for (let i = 0, len = propsCache.length; i < len; i++) {
propsCache[i][0].style.cssText = propsCache[i][1];
}
// alignment
this.#setAlignProps(this.propTargets.cell_alignment, this._propsAlignCache, true);
this.#setAlignProps(this.propTargets.cell_alignment_vertical, this._propsVerticalAlignCache, true);
if (dom.check.isTable(propsCache[0][0]) && this.#state.figureElement) {
this.#state.figureElement.style.float = this._propsAlignCache;
}
}
/**
* @description Applies the color from the color picker.
* @param {any} color - The color string or object.
*/
applyColorPicker(color) {
const target = this.propTargets[`${this.sliderType}_color`];
target.style.borderColor = target.value = color;
this.controller_colorPicker.close();
}
/**
* @description Sets the alignment properties.
* @param {string} value - The alignment value.
*/
setAlignProps(value) {
this.#setAlignProps(this.propTargets.cell_alignment, value, false);
}
/**
* @description Sets the vertical alignment properties.
* @param {string} value - The vertical alignment value.
*/
setVerticalAlignProps(value) {
this.#setAlignProps(this.propTargets.cell_alignment_vertical, value, false);
}
/**
* @description Updates table layout styles.
* @param {string} styles - Styles to update.
* @param {boolean} isMaxWidth - Whether the table is set to maximum width.
* @param {boolean} isFixedColumn - Whether the table has fixed column width.
* @param {boolean} ondisplay - Whether to update display.
*/
setTableLayout(styles, isMaxWidth, isFixedColumn, ondisplay) {
if (styles.includes('width')) {
const targets = this.#state.figureElement;
if (!targets) return;
let sizeIcon, text;
if (!isMaxWidth) {
sizeIcon = this.#$.icons.expansion;
text = this.maxText;
if (!ondisplay) targets.style.width = 'max-content';
} else {
sizeIcon = this.#$.icons.reduction;
text = this.minText;
if (!ondisplay) targets.style.width = '100%';
}
dom.utils.changeElement(this.resizeButton.firstElementChild, sizeIcon);
dom.utils.changeTxt(this.resizeText, text);
}
if (styles.includes('column')) {
if (!isFixedColumn) {
dom.utils.removeClass(this.#main._element, 'se-table-layout-fixed');
dom.utils.addClass(this.#main._element, 'se-table-layout-auto');
dom.utils.removeClass(this.columnFixedButton, 'active');
} else {
dom.utils.removeClass(this.#main._element, 'se-table-layout-auto');
dom.utils.addClass(this.#main._element, 'se-table-layout-fixed');
dom.utils.addClass(this.columnFixedButton, 'active');
}
}
}
/**
* @description Applies properties to table cells.
* @param {HTMLButtonElement} target The target element.
*/
submitProps(target) {
try {
target.disabled = true;
const isTable = this.#main.controller_table.form.contains(this.controller_props.currentTarget);
const targets = isTable ? [this.#main._element] : this.#state.selectedCells;
const tr = /** @type {HTMLTableCellElement} */ (targets[0]);
const trStyles = _w.getComputedStyle(tr);
const { border_format, border_color, border_style, border_width, back_color, font_color, cell_alignment, cell_alignment_vertical } = this.propTargets;
const borderFormat = border_format.getAttribute('se-border-format') || '';
const hasFormat = borderFormat !== 'all';
const borderStyle = (border_style.textContent === 'none' ? '' : border_style.textContent) || '';
const isNoneFormat = borderFormat === 'none' || !borderStyle;
const cellAlignment = cell_alignment.getAttribute('se-cell-align') || '';
const cellAlignmentVertical = cell_alignment_vertical.getAttribute('se-cell-align') || '';
const borderColor = isNoneFormat ? '' : border_color.value.trim() || trStyles.borderColor;
let borderWidth = isNoneFormat ? '' : border_width.value.trim() || trStyles.borderWidth;
borderWidth = borderWidth + (numbers.is(borderWidth) ? Constants.DEFAULT_BORDER_UNIT : '');
const backColor = back_color.value.trim();
const fontColor = font_color.value.trim();
const hasBorder = hasFormat && !isNoneFormat && borderWidth;
const borderCss = `${borderWidth} ${borderStyle} ${borderColor}`;
const cells = {
left: [],
top: [],
right: [],
bottom: [],
middle: [],
all: null,
};
if (!isTable) {
const trRow = /** @type {HTMLTableRowElement} */ (tr.parentElement);
// --- target cells roof
let { rs, re, cs, ce } = this.#state.ref || {
rs: trRow.rowIndex || 0,
re: trRow.rowIndex || 0,
cs: tr.cellIndex || 0,
ce: tr.cellIndex || 0,
};
const mergeInfo = new Array(re - rs + 1).fill(0).map(() => new Array(ce - cs + 1).fill(0));
const cellStartIndex = cs;
re -= rs;
rs -= rs;
ce -= cs;
cs -= cs;
let prevRow = /** @type {HTMLElement} */ (trRow);
for (let i = 0, cellCnt = 0, len = targets.length, e, es, rowIndex = 0, cellIndex, colspan, rowspan; i < len; i++, cellCnt++) {
e = /** @type {HTMLTableCellElement} */ (targets[i]);
colspan = e.colSpan;
rowspan = e.rowSpan;
cellIndex = e.cellIndex - cellStartIndex;
if (prevRow !== e.parentElement) {
rowIndex++;
cellCnt = 0;
prevRow = e.parentElement;
}
let c = 0;
while (c <= cellIndex) {
cellIndex += mergeInfo[rowIndex][c] || 0;
c++;
}
try {
if (rowspan > 1) {
const rowspanNum = rowspan - 1;
for (let r = rowIndex; r <= rowIndex + rowspanNum; r++) {
mergeInfo[r][cellIndex] += colspan - (rowIndex === r ? 1 : 0);
}
} else if (colspan > 1) {
mergeInfo[rowIndex][cellIndex] += colspan - 1;
}
} catch {
// ignore error
}
const isBottom = rowIndex + rowspan - 1 === re;
if (rowIndex === rs) cells.top.push(e);
if (rowIndex === re || isBottom) cells.bottom.push(e);
if (cellIndex === cs) cells.left.push(e);
if (cellIndex === ce || cellIndex + colspan - 1 === ce) cells.right.push(e);
if (!isBottom && rowIndex !== rs && rowIndex !== re && cellIndex !== cs && cellIndex !== ce) cells.middle.push(e);
// --- set styles
es = e.style;
// alignment
es.textAlign = cellAlignment;
es.verticalAlign = cellAlignmentVertical;
// back
es.backgroundColor = backColor;
// font
es.color = fontColor;
// font style
this.#setFontStyle(es);
// border
if (hasBorder) continue;
// border - all || none
if (isNoneFormat) {
es.border = es.borderLeft = es.borderTop = es.borderRight = es.borderBottom = '';
} else {
es.border = borderCss;
}
}
if (cells.middle.length === 0) {
cells.middle = targets;
}
} else {
// -- table styles
const es = tr.style;
// alignment
if (this.#state.figureElement) {
this.#state.figureElement.style.float = cellAlignment;
this.#state.figureElement.style.verticalAlign = cellAlignmentVertical;
}
// back
es.backgroundColor = backColor;
// font
es.color = fontColor;
// font style
this.#setFontStyle(es);
// border
if (!hasBorder) {
// border - all || none
if (isNoneFormat) {
es.border = es.borderLeft = es.borderTop = es.borderRight = es.borderBottom = '';
} else {
es.border = borderCss;
}
}
cells.left = cells.top = cells.right = cells.bottom = targets;
}
cells.all = targets;
// border format
if (hasBorder) {
this.#setBorderStyles(cells, borderFormat, borderCss);
}
this.#main.historyPush();
// set cells style
this.controller_props.close();
if (this.#state.tdElement) {
this.#selectionService.recallStyleSelectedCells();
this.#main.setCellInfo(this.#state.tdElement, true);
dom.utils.addClass(this.#state.tdElement, 'se-selected-cell-focus');
}
} catch (err) {
console.warn('[SUNEDITOR.plugins.table.setProps.error]', err);
} finally {
target.disabled = false;
}
}
/**
* @description Closes the properties dialog.
*/
closeProps() {
this.controller_props.close();
this.controller_colorPicker.close();
}
/**
* @description Updates control properties.
* @param {string} type The type of control property.
*/
#setCtrlProps(type) {
this._typeCache = type;
const isTable = type === 'table';
const targets = isTable ? [this.#main._element] : this.#state.selectedCells;
if (!targets?.[0]) return;
const { border_format, border_color, border_style, border_width, back_color, font_color, cell_alignment, cell_alignment_vertical, cell_alignment_table_text, font_bold, font_underline, font_italic, font_strike } = this.propTargets;
const { border, backgroundColor, color, textAlign, verticalAlign, fontWeight, textDecoration, fontStyle } = _w.getComputedStyle(targets[0]);
const cellBorder = this.#getBorderStyle(border);
/** @type {HTMLElement} */ (cell_alignment.querySelector('[data-value="justify"]')).style.display = isTable ? 'none' : '';
cell_alignment_table_text.style.display = isTable ? '' : 'none';
if (isTable) cell_alignment_vertical.style.display = 'none';
else cell_alignment_vertical.style.display = '';
let b_color = converter.rgb2hex(cellBorder.c),
b_style = cellBorder.s,
b_width = cellBorder.w,
backColor = converter.rgb2hex(backgroundColor),
fontColor = converter.rgb2hex(color),
bold = /.+/.test(fontWeight),
underline = /underline/i.test(textDecoration),
strike = /line-through/i.test(textDecoration),
italic = /italic/i.test(fontStyle),
align = isTable ? this.#state.figureElement?.style.float : textAlign,
align_v = verticalAlign;
this._propsCache = [];
for (let i = 0, t, isBreak; (t = targets[i]); i++) {
// eslint-disable-next-line no-shadow
const { cssText, border, backgroundColor, color, textAlign, verticalAlign, fontWeight, textDecoration, fontStyle } = t.style;
this._propsCache.push([t, cssText]);
if (isBreak) continue;
const { c, s, w } = this.#getBorderStyle(border);
// use getComputedStyle to normalize any CSS color format to rgb
let hexBackColor = backgroundColor;
let hexColor = color;
if (hexBackColor || hexColor) {
const computed = _w.getComputedStyle(t);
if (hexBackColor) hexBackColor = computed.backgroundColor;
if (hexColor) hexColor = computed.color;
}
if (b_color && cellBorder.c !== c) b_color = '';
if (b_style && cellBorder.s !== s) b_style = '';
if (b_width && cellBorder.w !== w) b_width = '';
if (backColor !== converter.rgb2hex(hexBackColor)) backColor = '';
if (fontColor !== converter.rgb2hex(hexColor)) fontColor = '';
if (align !== (isTable ? this.#state.figureElement?.style.float : textAlign)) align = '';
if (align_v && align_v !== verticalAlign) align_v = '';
if (bold && bold !== /.+/.test(fontWeight)) bold = false;
if (underline && underline !== /underline/i.test(textDecoration)) underline = false;
if (strike && strike !== /line-through/i.test(textDecoration)) strike = false;
if (italic && italic !== /italic/i.test(fontStyle)) italic = false;
if (!b_color || !b_style || !b_width || !backColor || !fontColor) {
isBreak = true;
}
}
// border - format
border_format.firstElementChild.innerHTML = this.#$.icons[Constants.BORDER_FORMATS[targets.length === 1 ? 'outside' : 'all']];
border_format.setAttribute('se-border-format', 'all');
dom.utils.removeClass(border_format, 'active');
// border - styles
b_style ||= Constants.BORDER_LIST[0];
border_style.textContent = b_style;
border_color.style.borderColor = border_color.value = b_color;
border_width.value = b_width;
this.#disableBorderProps(b_style === Constants.BORDER_LIST[0]);
// back, font color
back_color.value = back_color.style.borderColor = backColor;
font_color.value = font_color.style.borderColor = fontColor;
// font style
if (bold) dom.utils.addClass(font_bold, 'on');
if (underline) dom.utils.addClass(font_underline, 'on');
if (strike) dom.utils.addClass(font_strike, 'on');
if (italic) dom.utils.addClass(font_italic, 'on');
// align
this.#setAlignProps(cell_alignment, (this._propsAlignCache = align), true);
this.#setAlignProps(cell_alignment_vertical, (this._propsVerticalAlignCache = align_v), true);
}
/**
* @description Sets font styles.
* @param {CSSStyleDeclaration} styles The style object to modify.
*/
#setFontStyle(styles) {
const { font_bold, font_italic, font_strike, font_underline } = this.propTargets;
styles.fontWeight = dom.utils.hasClass(font_bold, 'on') ? 'bold' : '';
styles.fontStyle = dom.utils.hasClass(font_italic, 'on') ? 'italic' : '';
styles.textDecoration = ((dom.utils.hasClass(font_strike, 'on') ? 'line-through ' : '') + (dom.utils.hasClass(font_underline, 'on') ? 'underline' : '')).trim();
}
/**
* @description Gets the border style.
* @param {string} borderStyle The border style string.
* @returns {{w: string, s: string, c: string}} The parsed border style object.
* - `w`: The border width.
* - `s`: The border style.
* - `c`: The border color.
*/
#getBorderStyle(borderStyle) {
const parts = borderStyle.split(/\s(?![^()]*\))/);
let w = '',
s = '',
c = '';
if (parts.length === 3) {
w = parts[0];
s = parts[1];
c = parts[2];
} else if (parts.length === 2) {
if (/\d/.test(parts[0])) {
w = parts[0];
s = parts[1];
} else {
s = parts[0];
c = parts[1];
}
} else if (parts.length === 1) {
if (/\d/.test(parts[0])) {
w = parts[0];
} else {
s = parts[0];
}
}
return { w, s, c: converter.rgb2hex(c) };
}
/**
* @description Sets border format and styles.
* @param {{left: Node[], top: Node[], right: Node[], bottom: Node[], all: Node[]}} cells The table cells categorized by border positions.
* @param {string} borderKey Border style (`all`|`inside`|`horizon`|`vertical`|`outside`|`left`|`top`|`right`|`bottom`)
* @param {string} s The border style value.
*/
#setBorderStyles(cells, borderKey, s) {
const { left, top, right, bottom, all } = cells;
switch (borderKey) {
case 'inside':
if (all.length === 1) return;
dom.utils.setStyle(
all.filter((c) => !bottom.includes(c)),
Constants.BORDER_NS.b,
s,
);
dom.utils.setStyle(
all.filter((c) => !right.includes(c)),
Constants.BORDER_NS.r,
s,
);
break;
case 'horizon':
if (all.length === 1) return;
dom.utils.setStyle(
all.filter((c) => !bottom.includes(c)),
Constants.BORDER_NS.b,
s,
);
break;
case 'vertical':
if (all.length === 1) return;
dom.utils.setStyle(
all.filter((c) => !right.includes(c)),
Constants.BORDER_NS.r,
s,
);
break;
case 'outside':
dom.utils.setStyle(left, Constants.BORDER_NS.l, s);
dom.utils.setStyle(top, Constants.BORDER_NS.t, s);
dom.utils.setStyle(right, Constants.BORDER_NS.r, s);
dom.utils.setStyle(bottom, Constants.BORDER_NS.b, s);
break;
case 'left':
dom.utils.setStyle(left, Constants.BORDER_NS.l, s);
break;
case 'top':
dom.utils.setStyle(top, Constants.BORDER_NS.t, s);
break;
case 'right':
dom.utils.setStyle(right, Constants.BORDER_NS.r, s);
break;
case 'bottom':
dom.utils.setStyle(bottom, Constants.BORDER_NS.b, s);
break;
}
}
/**
* @description Disables or enables border properties.
* @param {boolean} disabled Whether to disable or enable border properties.
*/
#disableBorderProps(disabled) {
const { border_color, border_width, palette_border_button } = this.propTargets;
if (disabled) {
border_color.disabled = true;
border_width.disabled = true;
palette_border_button.disabled = true;
border_width.disabled = true;
} else {
border_color.disabled = false;
border_width.disabled = false;
palette_border_button.disabled = false;
border_width.disabled = false;
}
}
/**
* @description Sets text alignment properties.
* @param {Element} el The element to apply alignment to.
* @param {string} align The alignment value.
* @param {boolean} reset Whether to reset the alignment.
*/
#setAlignProps(el, align, reset) {
dom.utils.removeClass(el.querySelectorAll('button'), 'on');
if (!reset && el.getAttribute('se-cell-align') === align) {
el.setAttribute('se-cell-align', '');
return;
}
dom.utils.addClass(el.querySelector(`[data-value="${align}"]`), 'on');
el.setAttribute('se-cell-align', align);
}
/**
* @description Handles border style changes in table properties.
* @param {string} command The border style command.
*/
#OnPropsBorderEdit(command) {
this.propTargets.border_style.textContent = command;
this.#disableBorderProps(command === Constants.BORDER_LIST[0]);
this.selectMenu_props_border.close();
}
/**
* @description Handles border format changes in table properties.
* @param {string} defaultCommand The default border format command.
* @param {string} command The new border format command.
*/
#OnPropsBorderFormatEdit(defaultCommand, command) {
const { border_format } = this.propTargets;
border_format.setAttribute('se-border-format', command);
border_format.firstElementChild.innerHTML = this.#$.icons[Constants.BORDER_FORMATS[command]];
if (command !== defaultCommand) dom.utils.addClass(border_format, 'active');
else dom.utils.removeClass(border_format, 'active');
this.selectMenu_props_border_format.close();
this.selectMenu_props_border_format_oneCell.close();
}
/**
* @description Initialize the style service (resets properties).
*/
init() {
const { border_format, border_color, border_style, border_width, back_color, font_color, cell_alignment, cell_alignment_vertical, font_bold, font_underline, font_italic, font_strike } = this.propTargets;
dom.utils.removeClass([border_format, border_color, border_style, border_width, back_color, font_color, cell_alignment, cell_alignment_vertical, font_bold, font_underline, font_italic, font_strike], 'on');
}
}
export default TableStyleService;