初始化模版工程
This commit is contained in:
281
components/html-editor/lib/core/styleManager/index.ts
Normal file
281
components/html-editor/lib/core/styleManager/index.ts
Normal file
@@ -0,0 +1,281 @@
|
||||
/**
|
||||
* Style Manager
|
||||
* 样式管理器,处理元素样式的应用和获取
|
||||
*/
|
||||
|
||||
import { type HTMLEditor } from '../editor';
|
||||
import type { ElementStyles } from '../../types';
|
||||
import { createStyleChangeCommand, createElementTagChangeCommand, createContentChangeCommand } from '../historyManager/commands';
|
||||
|
||||
export class StyleManager {
|
||||
private editor: HTMLEditor;
|
||||
|
||||
constructor(editor: HTMLEditor) {
|
||||
this.editor = editor;
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用样式并记录历史
|
||||
*/
|
||||
private applyStyleWithHistory(element: HTMLElement, property: string, value: string): void {
|
||||
const oldValue = element.style.getPropertyValue(property) || window.getComputedStyle(element).getPropertyValue(property);
|
||||
|
||||
// 创建命令并执行
|
||||
if (this.editor.historyManager) {
|
||||
const command = createStyleChangeCommand(element, property, oldValue, value);
|
||||
command.execute();
|
||||
this.editor.historyManager.push(command);
|
||||
} else {
|
||||
// 如果没有历史管理器,直接应用样式
|
||||
element.style.setProperty(property, value);
|
||||
}
|
||||
}
|
||||
|
||||
// 字体相关方法
|
||||
changeFont(element: HTMLElement | null, fontFamily: string, triggerContentChange = true): boolean {
|
||||
if (!element) element = this.editor.selectedElement;
|
||||
if (!element) return false;
|
||||
this.applyStyleWithHistory(element, 'font-family', fontFamily);
|
||||
this.editor.emit('styleChange', element, { fontFamily });
|
||||
if (triggerContentChange) {
|
||||
this.editor.emit('contentChange');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
changeFontSize(element: HTMLElement | null, fontSize: string, triggerContentChange = true): boolean {
|
||||
if (!element) element = this.editor.selectedElement;
|
||||
if (!element) return false;
|
||||
this.applyStyleWithHistory(element, 'font-size', fontSize);
|
||||
this.editor.emit('styleChange', element, { fontSize });
|
||||
if (triggerContentChange) {
|
||||
this.editor.emit('contentChange');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
changeFontWeight(element: HTMLElement | null, fontWeight: string, triggerContentChange = true): boolean {
|
||||
if (!element) element = this.editor.selectedElement;
|
||||
if (!element) return false;
|
||||
this.applyStyleWithHistory(element, 'font-weight', fontWeight);
|
||||
this.editor.emit('styleChange', element, { fontWeight });
|
||||
if (triggerContentChange) {
|
||||
this.editor.emit('contentChange');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
changeFontStyle(element: HTMLElement | null, fontStyle: string, triggerContentChange = true): boolean {
|
||||
if (!element) element = this.editor.selectedElement;
|
||||
if (!element) return false;
|
||||
this.applyStyleWithHistory(element, 'font-style', fontStyle);
|
||||
this.editor.emit('styleChange', element, { fontStyle });
|
||||
if (triggerContentChange) {
|
||||
this.editor.emit('contentChange');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
changeTextDecoration(element: HTMLElement | null, textDecoration: string, triggerContentChange = true): boolean {
|
||||
if (!element) element = this.editor.selectedElement;
|
||||
if (!element) return false;
|
||||
this.applyStyleWithHistory(element, 'text-decoration', textDecoration);
|
||||
this.editor.emit('styleChange', element, { textDecoration });
|
||||
if (triggerContentChange) {
|
||||
this.editor.emit('contentChange');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
changeTextAlign(element: HTMLElement | null, textAlign: string, triggerContentChange = true): boolean {
|
||||
if (!element) element = this.editor.selectedElement;
|
||||
if (!element) return false;
|
||||
this.applyStyleWithHistory(element, 'text-align', textAlign);
|
||||
this.editor.emit('styleChange', element, { textAlign });
|
||||
if (triggerContentChange) {
|
||||
this.editor.emit('contentChange');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 边距相关方法
|
||||
changeMargin(element: HTMLElement | null, margin: string, triggerContentChange = true): boolean {
|
||||
if (!element) element = this.editor.selectedElement;
|
||||
if (!element) return false;
|
||||
this.applyStyleWithHistory(element, 'margin', margin);
|
||||
this.editor.emit('styleChange', element, { margin });
|
||||
if (triggerContentChange) {
|
||||
this.editor.emit('contentChange');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
changePadding(element: HTMLElement | null, padding: string,triggerContentChange = true): boolean {
|
||||
if (!element) element = this.editor.selectedElement;
|
||||
if (!element) return false;
|
||||
this.applyStyleWithHistory(element, 'padding', padding);
|
||||
this.editor.emit('styleChange', element, { padding });
|
||||
if (triggerContentChange) {
|
||||
this.editor.emit('contentChange');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 颜色相关方法
|
||||
changeColor(element: HTMLElement | null, color: string, triggerContentChange = true): boolean {
|
||||
if (!element) element = this.editor.selectedElement;
|
||||
if (!element) return false;
|
||||
this.applyStyleWithHistory(element, 'color', color);
|
||||
this.editor.emit('styleChange', element, { color });
|
||||
if (triggerContentChange) {
|
||||
this.editor.emit('contentChange');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
changeBackground(element: HTMLElement | null, backgroundColor: string, triggerContentChange = true): boolean {
|
||||
if (!element) element = this.editor.selectedElement;
|
||||
if (!element) return false;
|
||||
// 使用批量操作记录 background 和 backgroundColor
|
||||
// this.editor.beginBatch();
|
||||
this.applyStyleWithHistory(element, 'background', backgroundColor);
|
||||
// this.editor.endBatch();
|
||||
this.editor.emit('styleChange', element, { backgroundColor, background: backgroundColor });
|
||||
if (triggerContentChange) {
|
||||
this.editor.emit('contentChange');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 边框相关方法
|
||||
changeBorder(element: HTMLElement | null, border: string, triggerContentChange = true): boolean {
|
||||
if (!element) element = this.editor.selectedElement;
|
||||
if (!element) return false;
|
||||
this.applyStyleWithHistory(element, 'border', border);
|
||||
this.editor.emit('styleChange', element, { border });
|
||||
if (triggerContentChange) {
|
||||
this.editor.emit('contentChange');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
changeBorderRadius(element: HTMLElement | null, borderRadius: string, triggerContentChange = true): boolean {
|
||||
if (!element) element = this.editor.selectedElement;
|
||||
if (!element) return false;
|
||||
this.applyStyleWithHistory(element, 'border-radius', borderRadius);
|
||||
this.editor.emit('styleChange', element, { borderRadius });
|
||||
if (triggerContentChange) {
|
||||
this.editor.emit('contentChange');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
applyTextStyle(property: string, value: string, triggerContentChange = true): boolean {
|
||||
if (!this.editor.selectedElement) return false;
|
||||
this.applyStyleWithHistory(this.editor.selectedElement, property, value);
|
||||
this.editor.emit('styleChange', this.editor.selectedElement, { [property]: value });
|
||||
if (triggerContentChange) {
|
||||
this.editor.emit('contentChange');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
applyBlockStyle(property: string, value: string, triggerContentChange = true): boolean {
|
||||
if (!this.editor.selectedElement) return false;
|
||||
|
||||
const el = this.editor.selectedElement;
|
||||
|
||||
// 兼容传入 'background-color' 字符串
|
||||
const prop = property === 'background-color' ? 'background-color' : property;
|
||||
|
||||
// 当设置背景相关属性时,同时更新 background 与 backgroundColor
|
||||
if (prop === 'background' || prop === 'background-color') {
|
||||
this.editor.beginBatch();
|
||||
this.applyStyleWithHistory(el, 'background-color', value);
|
||||
this.applyStyleWithHistory(el, 'background', value);
|
||||
this.editor.endBatch();
|
||||
this.editor.emit('styleChange', el, { backgroundColor: value, background: value });
|
||||
return true;
|
||||
}
|
||||
|
||||
this.applyStyleWithHistory(el, prop, value);
|
||||
this.editor.emit('styleChange', el, { [prop]: value });
|
||||
if (triggerContentChange) {
|
||||
this.editor.emit('contentChange');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
getComputedStyle(element: HTMLElement, property: string): string {
|
||||
return window.getComputedStyle(element)[property as any];
|
||||
}
|
||||
|
||||
getElementStyles(element: HTMLElement): ElementStyles {
|
||||
const computedStyle = window.getComputedStyle(element);
|
||||
return {
|
||||
fontSize: computedStyle.fontSize,
|
||||
color: computedStyle.color,
|
||||
fontWeight: computedStyle.fontWeight,
|
||||
backgroundColor: computedStyle.backgroundColor,
|
||||
borderWidth: computedStyle.borderWidth,
|
||||
padding: computedStyle.padding,
|
||||
margin: computedStyle.margin,
|
||||
borderRadius: computedStyle.borderRadius
|
||||
};
|
||||
}
|
||||
|
||||
changeElementTag(element: HTMLElement, newTag: string, triggerContentChange = true): HTMLElement | null {
|
||||
if (!element || !element.parentNode || !newTag) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const command = createElementTagChangeCommand(element, newTag);
|
||||
command.execute();
|
||||
|
||||
const newElement = (command as any).newElement as HTMLElement;
|
||||
if (!newElement) return null;
|
||||
|
||||
if (this.editor.historyManager) {
|
||||
this.editor.historyManager.push(command);
|
||||
}
|
||||
|
||||
this.editor.selectElement(newElement);
|
||||
|
||||
this.editor.emit('contentChange');
|
||||
if (triggerContentChange) {
|
||||
this.editor.emit('contentChange');
|
||||
}
|
||||
|
||||
return newElement;
|
||||
}
|
||||
|
||||
rgbToHex(rgb: string): string {
|
||||
if (!rgb || rgb === 'rgba(0, 0, 0, 0)' || rgb === 'transparent') {
|
||||
return '#ffffff';
|
||||
}
|
||||
|
||||
const result = rgb.match(/\d+/g);
|
||||
if (!result) return '#000000';
|
||||
|
||||
const r = parseInt(result[0]);
|
||||
const g = parseInt(result[1]);
|
||||
const b = parseInt(result[2]);
|
||||
|
||||
return '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
|
||||
}
|
||||
|
||||
// =============================
|
||||
// 选区样式操作(全局contenteditable模式)
|
||||
// =============================
|
||||
applySelectionBold(): boolean { return this.editor.globalEditable?.applySelectionBold() ?? false; }
|
||||
applySelectionItalic(): boolean { return this.editor.globalEditable?.applySelectionItalic() ?? false; }
|
||||
applySelectionUnderline(): boolean { return this.editor.globalEditable?.applySelectionUnderline() ?? false; }
|
||||
applySelectionStrikeThrough(): boolean { return this.editor.globalEditable?.applySelectionStrikeThrough() ?? false; }
|
||||
applySelectionFontSize(px: string): boolean { return this.editor.globalEditable?.applySelectionFontSize(px) ?? false; }
|
||||
applySelectionFontFamily(name: string): boolean { return this.editor.globalEditable?.applySelectionFontFamily(name) ?? false; }
|
||||
applySelectionColor(color: string): boolean { return this.editor.globalEditable?.applySelectionColor(color) ?? false; }
|
||||
applySelectionBackground(color: string): boolean { return this.editor.globalEditable?.applySelectionBackground(color) ?? false; }
|
||||
applySelectionAlign(align: 'left' | 'center' | 'right'): boolean { return this.editor.globalEditable?.applySelectionAlign(align as any) ?? false; }
|
||||
}
|
||||
|
||||
export default StyleManager;
|
||||
Reference in New Issue
Block a user