UNPKG

suneditor

Version:

Vanilla JavaScript based WYSIWYG web editor

587 lines (586 loc) 20.3 kB
import type {} from '../../typedef'; export default Figure; /** * Figure information object */ export type FigureInfo = { /** * - Target element (`img`, `iframe`, `video`, `audio`, `table`, etc.) */ target: HTMLElement; /** * - Container element (`div.se-component`|`span.se-component.se-inline-component`) */ container: HTMLElement; /** * - Cover element (`FIGURE`|`null`) */ cover: HTMLElement | null; /** * - Inline cover element (`span.se-inline-component`) */ inlineCover: HTMLElement | null; /** * - Caption element (`FIGCAPTION`) */ caption: HTMLElement | null; /** * - Whether to rotate vertically */ isVertical: boolean; }; /** * Figure target information object (for resize/align operations) */ export type FigureTargetInfo = { /** * - Container element (`div.se-component`|`span.se-component.se-inline-component`) */ container: HTMLElement; /** * - Cover element (`FIGURE`|`null`) */ cover?: HTMLElement | null; /** * - Caption element (`FIGCAPTION`) */ caption?: HTMLElement | null; /** * - Alignment of the element. */ align?: string; /** * - The aspect ratio of the element. */ ratio?: { w: number; h: number; }; /** * - Whether to rotate vertically */ isVertical: boolean; /** * - Width of the element. */ w?: string | number; /** * - Height of the element. */ h?: string | number; /** * - Top position. */ t?: number; /** * - Left position. */ l?: number; /** * - Width, can be a number or `auto`. */ width: string | number; /** * - Height, can be a number or `auto`. */ height: string | number; /** * - Original width from `naturalWidth` or `offsetWidth`. */ originWidth?: number; /** * - Original height from `naturalHeight` or `offsetHeight`. */ originHeight?: number; }; /** * Figure control button type */ export type FigureControlButton = 'mirror_h' | 'mirror_v' | 'rotate_l' | 'rotate_r' | 'caption' | 'revert' | 'edit' | 'copy' | 'remove' | 'as' | 'align' | 'onalign' | 'onresize'; /** * Figure control resize value type (auto, or percentage numbers) */ export type FigureControlResize = `resize_auto,${number}` | `resize_auto,${number},${number}` | `resize_auto,${number},${number},${number}` | `resize_auto,${number},${number},${number},${number}`; /** * Figure control custom action object */ export type ControlCustomAction = { action: (element: Node, value: string, target: Node) => void; command: string; value: string; title: string; icon: string; }; /** * Figure controls configuration * 2D array of control buttons for the figure component toolbar * * **Available control buttons**: * - `"mirror_h"`: Mirror horizontally * - `"mirror_v"`: Mirror vertically * - `"rotate_l"`: Rotate left (-90°) * - `"rotate_r"`: Rotate right (90°) * - `"caption"`: Toggle caption (`FIGCAPTION`) * - `"revert"`: Revert to original size * - `"edit"`: Open edit modal * - `"copy"`: Copy component * - `"remove"`: Remove component * - `"as"`: Format type (`block`/`inline`) - requires `useFormatType` option * - `"align"`: Alignment (`none`/`left`/`center`/`right`) * - `"onalign"`: Alignment button (opens alignment menu) * - `"onresize"`: Resize button (opens resize menu) * - `"resize_auto,50,75,100"`: Auto-resize with percentage values (e.g., `"resize_auto,100,75,50"`) * - Custom action object with action, command, value, title, icon */ export type FigureControls = Array<Array<FigureControlButton | FigureControlResize | ControlCustomAction | string>>; export type FigureParams = { /** * Size unit */ sizeUnit?: string; /** * Auto ratio `{ current: '00%', default: '00%' }` */ autoRatio?: { current: string; default: string; }; }; /** * Figure information object * @typedef {Object} FigureInfo * @property {HTMLElement} target - Target element (`img`, `iframe`, `video`, `audio`, `table`, etc.) * @property {HTMLElement} container - Container element (`div.se-component`|`span.se-component.se-inline-component`) * @property {?HTMLElement} cover - Cover element (`FIGURE`|`null`) * @property {?HTMLElement} inlineCover - Inline cover element (`span.se-inline-component`) * @property {?HTMLElement} caption - Caption element (`FIGCAPTION`) * @property {boolean} isVertical - Whether to rotate vertically */ /** * Figure target information object (for resize/align operations) * @typedef {Object} FigureTargetInfo * @property {HTMLElement} container - Container element (`div.se-component`|`span.se-component.se-inline-component`) * @property {?HTMLElement} [cover] - Cover element (`FIGURE`|`null`) * @property {?HTMLElement} [caption] - Caption element (`FIGCAPTION`) * @property {string} [align] - Alignment of the element. * @property {{w:number, h:number}} [ratio] - The aspect ratio of the element. * @property {boolean} isVertical - Whether to rotate vertically * @property {string|number} [w] - Width of the element. * @property {string|number} [h] - Height of the element. * @property {number} [t] - Top position. * @property {number} [l] - Left position. * @property {string|number} width - Width, can be a number or `auto`. * @property {string|number} height - Height, can be a number or `auto`. * @property {number} [originWidth] - Original width from `naturalWidth` or `offsetWidth`. * @property {number} [originHeight] - Original height from `naturalHeight` or `offsetHeight`. */ /** * Figure control button type * @typedef {"mirror_h" | "mirror_v" | "rotate_l" | "rotate_r" | "caption" | "revert" | "edit" | "copy" | "remove" | "as" | "align" | "onalign" | "onresize"} FigureControlButton */ /** * Figure control resize value type (auto, or percentage numbers) * @typedef {`resize_auto,${number}` | `resize_auto,${number},${number}` | `resize_auto,${number},${number},${number}` | `resize_auto,${number},${number},${number},${number}`} FigureControlResize */ /** * Figure control custom action object * @typedef {{ * action: (element: Node, value: string, target: Node) => void, * command: string, * value: string, * title: string, * icon: string * }} ControlCustomAction */ /** * Figure controls configuration * 2D array of control buttons for the figure component toolbar * * **Available control buttons**: * - `"mirror_h"`: Mirror horizontally * - `"mirror_v"`: Mirror vertically * - `"rotate_l"`: Rotate left (-90°) * - `"rotate_r"`: Rotate right (90°) * - `"caption"`: Toggle caption (`FIGCAPTION`) * - `"revert"`: Revert to original size * - `"edit"`: Open edit modal * - `"copy"`: Copy component * - `"remove"`: Remove component * - `"as"`: Format type (`block`/`inline`) - requires `useFormatType` option * - `"align"`: Alignment (`none`/`left`/`center`/`right`) * - `"onalign"`: Alignment button (opens alignment menu) * - `"onresize"`: Resize button (opens resize menu) * - `"resize_auto,50,75,100"`: Auto-resize with percentage values (e.g., `"resize_auto,100,75,50"`) * - Custom action object with action, command, value, title, icon * * @example * // Basic controls * [['mirror_h', 'mirror_v', 'align', 'caption', 'edit', 'copy', 'remove']] * * @example * // Multi-row controls with resize options * [ * ['as', 'resize_auto,100,75,50', 'rotate_l', 'rotate_r', 'mirror_h', 'mirror_v'], * ['edit', 'align', 'caption', 'revert', 'copy', 'remove'] * ] * * @typedef {Array<Array<FigureControlButton | FigureControlResize | ControlCustomAction | string>>} FigureControls */ /** * @typedef {Object} FigureParams * @property {string} [sizeUnit="px"] Size unit * @property {{ current: string, default: string }} [autoRatio=null] Auto ratio `{ current: '00%', default: '00%' }` */ /** * @class * @description Figure module class for handling resizable/alignable components (images, videos, iframes, etc.) * @see EditorComponent for `inst._element` requirement */ declare class Figure { /** * @description Create a container for the resizing component and insert the element. * @param {Node} element Target element * @param {string} [className] Class name of container (fixed: `se-component`) * @returns {FigureInfo} {target, container, cover, inlineCover, caption} * @example * const imgEl = document.createElement('IMG'); * imgEl.src = imageUrl; * const figureInfo = Figure.CreateContainer(imgEl, 'se-image-container'); * // figureInfo.container → <div class="se-component se-image-container"> * // figureInfo.cover → <figure> wrapping the imgEl */ static CreateContainer(element: Node, className?: string): FigureInfo; /** * @description Create a container for the inline resizing component and insert the element. * @param {Node} element Target element * @param {string} [className] Class name of container (fixed: `se-component` `se-inline-component`) * @returns {FigureInfo} {target, container, cover, inlineCover, caption} * @example * const imgEl = document.createElement('IMG'); * imgEl.src = imageUrl; * const figureInfo = Figure.CreateInlineContainer(imgEl, 'se-image-container'); * // figureInfo.container → <span class="se-component se-inline-component se-image-container"> * // figureInfo.inlineCover → same as container (inline mode) */ static CreateInlineContainer(element: Node, className?: string): FigureInfo; /** * @description Return HTML string of caption(`FIGCAPTION`) element * @param {Node} cover Cover element(`FIGURE`). `CreateContainer().cover` * @returns {HTMLElement} caption element */ static CreateCaption(cover: Node, text: any): HTMLElement; /** * @description Get the element's container(`.se-component`) info. * @param {Node} element Target element * @returns {FigureInfo} {target, container, cover, inlineCover, caption} */ static GetContainer(element: Node): FigureInfo; /** * @description Ratio calculation * @param {string|number} w Width size * @param {string|number} h Height size * @param {?string} [defaultSizeUnit="px"] Default size unit (default: `"px"`) * @return {{w: number, h: number}} * @example * const ratio = Figure.GetRatio(200, 100, 'px'); * // ratio → { w: 2, h: 0.5 } * * // Used with proportion-locked resizing * const ratio = Figure.GetRatio(inputX.value, inputY.value, sizeUnit); * const adjusted = Figure.CalcRatio(newWidth, newHeight, sizeUnit, ratio); */ static GetRatio( w: string | number, h: string | number, defaultSizeUnit?: string | null, ): { w: number; h: number; }; /** * @description Ratio calculation * @param {string|number} w Width size * @param {string|number} h Height size * @param {string} defaultSizeUnit Default size unit (default: `"px"`) * @param {?{w: number, h: number}} [ratio] Ratio size (Figure.GetRatio) * @return {{w: string|number, h: string|number}} * @example * const ratio = Figure.GetRatio(200, 100, 'px'); * // When width changes, recalculate height to maintain aspect ratio * const result = Figure.CalcRatio(inputX.value, inputY.value, 'px', ratio); * inputY.value = result.h; // adjusted height preserving ratio */ static CalcRatio( w: string | number, h: string | number, defaultSizeUnit: string, ratio?: { w: number; h: number; } | null, ): { w: string | number; h: string | number; }; /** * @description It is judged whether it is the component[`img`, `iframe`, `video`, `audio`, `table`] cover(class=`"se-component"`) and `table`, `hr` * @param {Node} element Target element * @returns {boolean} */ static is(element: Node): boolean; /** * @constructor * @param {*} inst The instance object that called the constructor. * @param {SunEditor.Deps} $ Kernel dependencies * @param {FigureControls} controls Controller button array * @param {FigureParams} params Figure options */ constructor(inst: any, $: SunEditor.Deps, controls: FigureControls, params: FigureParams); kind: any; _alignIcons: { none: any; left: any; right: any; center: any; }; /** @type {Object<string, *>} */ _action: { [x: string]: any; }; controller: Controller; alignButton: Element; selectMenu_align: SelectMenu; asButton: Element; selectMenu_as: SelectMenu; resizeButton: Element; selectMenu_resize: SelectMenu; inst: any; sizeUnit: string; autoRatio: { current: string; default: string; }; isVertical: boolean; percentageButtons: any[] | NodeListOf<Element>; captionButton: Element; align: string; as: string; /** @type {{left?: number, top?: number}} */ __offset: { left?: number; top?: number; }; _element: HTMLElement; _cover: HTMLElement; _inlineCover: HTMLElement; _container: HTMLElement; _caption: HTMLElement; _resizeClientX: number; _resizeClientY: number; _resize_direction: string; __containerResizingOff: any; __containerResizing: any; __onContainerEvent: any; __offContainerEvent: any; /** * @description Close the figure's controller */ close(): void; /** * @description Open the figure's controller * @param {Node} targetNode Target element * @param {Object} params params * @param {boolean} [params.nonResizing=false] Do not display the resizing button * @param {boolean} [params.nonSizeInfo=false] Do not display the size information * @param {boolean} [params.nonBorder=false] Do not display the selected style line * @param {boolean} [params.figureTarget=false] If `true`, the target is a figure element * @param {boolean} [params.infoOnly=false] If `true`, returns only the figure target info without opening the controller * @returns {FigureTargetInfo|undefined} figure target info * @example * // Open controller with full UI (resize handles, size info, border) * const info = this.figure.open(imgElement, { * nonResizing: false, nonSizeInfo: false, nonBorder: false, * figureTarget: false, infoOnly: false * }); * * // Get figure info without opening the controller UI * const info = this.figure.open(oFrame, { * nonResizing: false, nonSizeInfo: false, nonBorder: false, * figureTarget: false, infoOnly: true * }); * // info.width, info.height, info.ratio are available */ open( targetNode: Node, { nonResizing, nonSizeInfo, nonBorder, figureTarget, infoOnly, }: { nonResizing?: boolean; nonSizeInfo?: boolean; nonBorder?: boolean; figureTarget?: boolean; infoOnly?: boolean; }, ): FigureTargetInfo | undefined; /** * @description Hide the controller */ controllerHide(): void; /** * @description Hide the controller */ controllerShow(): void; /** * @description Open the figure's controller * @param {Node} target Target element * @param {Object} [params={}] params * @param {boolean} [params.isWWTarget] If the controller is in the WYSIWYG area, set it to `true`. * @param {() => void} [params.initMethod] Method to be called when the controller is closed. * @param {boolean} [params.disabled] If `true`, the controller is disabled. * @param {{left: number, top: number}} [params.addOffset] Additional offset values */ controllerOpen( target: Node, params?: { isWWTarget?: boolean; initMethod?: () => void; disabled?: boolean; addOffset?: { left: number; top: number; }; }, ): void; /** * @description Set the element's container size * @param {string|number} w Width size * @param {string|number} h Height size */ setFigureSize(w: string | number, h: string | number): void; /** * @description Set the element's container size from plugins input value * @param {string|number} w Width size * @param {string|number} h Height size */ setSize(w: string | number, h: string | number): void; /** * @description Gets the Figure size * @param {?Node} [targetNode] Target element, default is the current element * @returns {{w: string, h: string, dw: string, dh: string}} */ getSize(targetNode?: Node | null): { w: string; h: string; dw: string; dh: string; }; /** * @description Align the container. * @param {?Node} targetNode Target element * @param {string} align `"none"`|`"left"`|`"center"`|`"right"` */ setAlign(targetNode: Node | null, align: string): void; /** * @description As style[block, inline] the component * @param {?Node} targetNode Target element * @param {"block"|"inline"} formatStyle Format style * @returns {HTMLElement} New target element after conversion * @example * // Convert a block image to inline format * const newImgEl = this.figure.convertAsFormat(imgElement, 'inline'); * // newImgEl is a cloned element inside a new inline container * * // Convert an inline image back to block format * const newImgEl = this.figure.convertAsFormat(imgElement, 'block'); */ convertAsFormat(targetNode: Node | null, formatStyle: 'block' | 'inline'): HTMLElement; controllerAction(target: HTMLButtonElement): void; /** * @description Inspect the figure component format and change it to the correct format. * @param {Node} container - The container element of the figure component. * @param {Node} originEl - The original element of the figure component. * @param {Node} anchorCover - The anchor cover element of the figure component. * @param {import('../manager/FileManager').default} [fileManagerInst=null] - FileManager module instance, if used. * @example * // Insert a new image figure, replacing the original element in the DOM * const figureInfo = Figure.CreateContainer(imgElement, 'se-image-container'); * this.figure.retainFigureFormat(figureInfo.container, this.#element, null, this.fileManager); * * // Replace with anchor cover (e.g., image wrapped in a link) * this.figure.retainFigureFormat(container, this.#element, anchorEl, this.fileManager); */ retainFigureFormat(container: Node, originEl: Node, anchorCover: Node, fileManagerInst?: import('../manager/FileManager').default): void; /** * @description Initialize the transform style (rotation) of the element. * @param {?Node} [node] Target element, default is the current element */ deleteTransform(node?: Node | null): void; /** * @description Set the transform style (rotation) of the element. * @param {Node} node Target element * @param {?string|number} width Element's width size * @param {?string|number} height Element's height size * @param {?number} deg rotate value * @example * // Rotate element 90 degrees clockwise * this.figure.setTransform(imgElement, 100, 50, 90); * * // Apply size without additional rotation (deg=0 preserves current rotation) * this.figure.setTransform(oFrame, width, height, 0); */ setTransform(node: Node, width: (string | number) | null, height: (string | number) | null, deg: number | null): void; /** * @internal * @description Displays or hides the resize handles of the figure component. * @param {boolean} display Whether to display resize handles. */ _displayResizeHandles(display: boolean): void; /** * @internal * @description Applies rotation transformation to the target element. * @param {HTMLElement} element Target element. * @param {number} r Rotation degree. * @param {number} x X-axis rotation value. * @param {number} y Y-axis rotation value. */ _setRotate(element: HTMLElement, r: number, x: number, y: number): void; /** * @internal * @description Applies size adjustments to the figure element. * @param {string|number} w Width value. * @param {string|number} h Height value. * @param {string} direction Resize direction. */ _applySize(w: string | number, h: string | number, direction: string): void; /** * @internal * @description Sets padding-bottom for cover elements based on width and height. * @param {string} w Width value. * @param {string} h Height value. */ __setCoverPaddingBottom(w: string, h: string): void; /** * @internal * @description Sets the figure element to its auto size. */ _setAutoSize(): void; /** * @internal * @description Sets the figure element's size in percentage. * @param {string|number} w Width percentage. * @param {string|number} h Height percentage. */ _setPercentSize(w: string | number, h: string | number): void; /** * @internal * @description Reverts the figure element to its previously saved size. */ _setRevert(): void; #private; } import SelectMenu from '../ui/SelectMenu'; import Controller from './Controller';