@dsfr-builder/grapesjs
Version:
Free and Open Source Web Builder Framework
1,577 lines (1,575 loc) • 424 kB
TypeScript
import Backbone from 'backbone';
import { ModelDestroyOptions, _StringKey } from 'backbone';
export interface DomComponentsConfig {
stylePrefix?: string;
/**
* Could be used for default components.
*/
components?: Record<string, any>[];
/**
* If the component is draggable you can drag the component itself (not only from the toolbar).
* @default true
*/
draggableComponents?: boolean;
/**
* Experimental: Disable text inner components.
* With this option, you're able to decide which inner component inside text should be
* disabled (eg. no select, no hover, no layer visibility) once edited.
* @default false
* @example
* // disable all inner childs
* disableTextInnerChilds: true,
* // disable all except link components
* disableTextInnerChilds: (child) => !child.is('link'),
*/
disableTextInnerChilds?: boolean | ((cmp: Component) => boolean | void);
/**
* You can setup a custom component definition processor before adding it into the editor.
* It might be useful to transform custom objects (es. some framework specific JSX) to GrapesJS component one.
* This custom function will be executed on ANY new added component to the editor so make smart checks/conditions
* to avoid doing useless executions
* By default, GrapesJS supports already elements generated from React JSX preset
* @example
* processor: (obj) => {
* if (obj.$$typeof) { // eg. this is a React Element
* const gjsComponent = {
* type: obj.type,
* components: obj.props.children,
* ...
* };
* ...
* return gjsComponent;
* }
* }
*/
processor?: (obj: any) => Record<string, any> | undefined;
/**
* List of HTML void elements.
* https://www.w3.org/TR/2011/WD-html-markup-20110113/syntax.html#void-elements
*/
voidElements?: string[];
/**
* Experimental: Use the frame document for DOM element creation.
* This option might be useful when elements require the local document context to
* work properly (eg. Web Components).
*/
useFrameDoc?: boolean;
}
declare class ModuleModel<TModule extends IBaseModule<any> = Module, T extends ObjectHash = any, S = SetOptions, E = any> extends Model<T, S, E> {
private _module;
constructor(module: TModule, attributes?: T, options?: CombinedModelConstructorOptions<E>);
get module(): TModule;
get config(): TModule extends IBaseModule<infer C> ? C : unknown;
get em(): EditorModel;
}
export type ModuleExt<TModel extends ModuleModel> = TModel extends ModuleModel<infer M> ? M : unknown;
export type ModelConstructor<TModel extends ModuleModel> = {
new (mod: ModuleExt<TModel>, attr: any): TModel;
};
declare class ModuleCollection<TModel extends ModuleModel = ModuleModel> extends Collection<TModel> {
module: ModuleExt<TModel>;
private newModel;
add(model: Array<Record<string, any>> | TModel, options?: AddOptions): TModel;
add(models: Array<Array<Record<string, any>> | TModel>, options?: AddOptions): TModel[];
constructor(module: ModuleExt<TModel>, models: TModel[] | Array<Record<string, any>>, modelConstructor: ModelConstructor<TModel>);
preinitialize(models?: TModel[] | Array<Record<string, any>>, options?: any): void;
}
export type ModuleFromModel<TModel extends ModuleModel> = TModel extends ModuleModel<infer M> ? M : unknown;
export type ModuleModelExt<TItem extends ModuleModel | ModuleCollection> = TItem extends ModuleCollection<infer M> ? ModuleFromModel<M> : TItem extends ModuleModel<infer M> ? M : unknown;
declare class ModuleView<TModel extends ModuleModel | ModuleCollection = ModuleModel, TElement extends Element = HTMLElement> extends View<TModel extends ModuleModel ? TModel : undefined, TElement> {
protected get pfx(): string;
protected get ppfx(): string;
collection: TModel extends ModuleModel ? ModuleCollection<ModuleModel> : TModel;
protected get module(): ModuleModelExt<TModel>;
protected get em(): EditorModel;
protected get config(): ModuleModelExt<TModel> extends IBaseModule<infer C> ? C : unknown;
className: string;
preinitialize(options?: any): void;
}
export type ContentElement = string | ComponentDefinition;
export type ContentType = ContentElement | ContentElement[];
export interface DraggableContent {
/**
* Determines if a block can be moved inside a given component when the content is a function.
*
* This property is used to determine the validity of the drag operation.
* @type {ComponentDefinition | undefined}
*/
dragDef?: ComponentDefinition;
/**
* The content being dragged. Might be an HTML string or a [Component Defintion](/modules/Components.html#component-definition)
*/
content?: ContentType | (() => ContentType);
}
export type DragSource<T> = DraggableContent & {
model?: T;
};
export type Placement = "inside" | "before" | "after";
export type DroppableZoneConfig = {
ratio: number;
minUndroppableDimension: number;
maxUndroppableDimension: number;
};
declare enum DragDirection {
Vertical = "Vertical",
Horizontal = "Horizontal",
BothDirections = "BothDirections"
}
export type CustomTarget = ({ event }: {
event: MouseEvent;
}) => HTMLElement | null;
export interface SorterContainerContext {
container: HTMLElement;
containerSel: string;
itemSel: string;
pfx: string;
document: Document;
placeholderElement: HTMLElement;
customTarget?: CustomTarget;
}
export interface PositionOptions {
windowMargin?: number;
borderOffset?: number;
offsetTop?: number;
offsetLeft?: number;
canvasRelative?: boolean;
relative?: boolean;
}
/**
* Represents an event handler for the `onStartSort` event.
*
* @param sourceNodes The source nodes being sorted.
* @param container The container element where the sorting is taking place.
*/
export type OnStartSortHandler<NodeType> = (sourceNodes: NodeType[], container?: HTMLElement) => void;
/**
* Represents an event handler for the `onDragStart` event.
*
* @param mouseEvent The mouse event associated with the drag start.
*/
export type OnDragStartHandler = (mouseEvent: MouseEvent) => void;
export type OnMouseMoveHandler = (mouseEvent: MouseEvent) => void;
export type OnDropHandler<NodeType> = (targetNode: NodeType | undefined, sourceNodes: NodeType[], index: number | undefined) => void;
export type OnTargetChangeHandler<NodeType> = (oldTargetNode: NodeType | undefined, newTargetNode: NodeType | undefined) => void;
export type OnPlaceholderPositionChangeHandler = (targetDimension: Dimension, placement: Placement) => void;
export type OnEndHandler = () => void;
/**
* Represents a collection of event handlers for sortable tree node events.
*/
export interface SorterEventHandlers<NodeType> {
onStartSort?: OnStartSortHandler<NodeType>;
onDragStart?: OnDragStartHandler;
onMouseMove?: OnMouseMoveHandler;
onDrop?: OnDropHandler<NodeType>;
onTargetChange?: OnTargetChangeHandler<NodeType>;
onPlaceholderPositionChange?: OnPlaceholderPositionChangeHandler;
onEnd?: OnEndHandler;
legacyOnMoveClb?: Function;
legacyOnStartSort?: Function;
legacyOnEndMove?: Function;
legacyOnEnd?: Function;
}
export interface SorterDragBehaviorOptions {
dragDirection: DragDirection;
nested?: boolean;
selectOnEnd?: boolean;
}
export interface SorterOptions<T, NodeType extends SortableTreeNode<T>> {
em: EditorModel;
treeClass: new (model: T, dragSource?: DragSource<T>) => NodeType;
containerContext: SorterContainerContext;
positionOptions: PositionOptions;
dragBehavior: SorterDragBehaviorOptions;
eventHandlers: SorterEventHandlers<NodeType>;
}
declare class Dimension {
top: number;
left: number;
height: number;
width: number;
offsets: ReturnType<CanvasModule["getElementOffsets"]>;
dir?: boolean;
/**
* Initializes the DimensionCalculator with the given initial dimensions.
*
* @param initialDimensions - The initial dimensions containing `top`, `left`, `height`, `width`, and other properties.
*/
constructor(initialDimensions: {
top: number;
left: number;
height: number;
width: number;
offsets: ReturnType<CanvasModule["getElementOffsets"]>;
dir?: boolean;
el?: HTMLElement;
indexEl?: number;
});
/**
* Calculates the difference between the current and previous dimensions.
* If there are no previous dimensions, it will return zero differences.
*
* @returns An object containing the differences in `top` and `left` positions.
*/
calculateDimensionDifference(dimension: Dimension): {
topDifference: number;
leftDifference: number;
};
/**
* Updates the current dimensions by adding the given differences to the `top` and `left` values.
*
* @param topDifference - The difference to add to the current `top` value.
* @param leftDifference - The difference to add to the current `left` value.
*/
adjustDimensions(difference: {
topDifference: number;
leftDifference: number;
}): Dimension;
/**
* Determines the placement ('before' or 'after') based on the X and Y coordinates and center points.
*
* @param {number} mouseX X coordinate of the mouse
* @param {number} mouseY Y coordinate of the mouse
* @return {Placement} 'before' or 'after'
*/
determinePlacement(mouseX: number, mouseY: number): Placement;
/**
* Compares the current dimension object with another dimension to check equality.
*
* @param {Dimension} dimension - The dimension to compare against.
* @returns {boolean} True if the dimensions are equal, otherwise false.
*/
equals(dimension: Dimension | undefined): boolean;
/**
* Creates a clone of the current Dimension object.
*
* @returns {Dimension} A new Dimension object with the same properties.
*/
clone(): Dimension;
getDropArea(config: DroppableZoneConfig): Dimension;
private adjustDropDimension;
/**
* Checks if the given coordinates are within the bounds of this dimension instance.
*
* @param {number} x - The X coordinate to check.
* @param {number} y - The Y coordinate to check.
* @returns {boolean} - True if the coordinates are within bounds, otherwise false.
*/
isWithinBounds(x: number, y: number): boolean;
}
declare abstract class SortableTreeNode<T> {
protected _model: T;
protected _dragSource: DragSource<T>;
protected _dropAreaConfig: DroppableZoneConfig;
/** The dimensions of the node. */
nodeDimensions?: Dimension;
/** The dimensions of the child elements within the target node. */
childrenDimensions?: Dimension[];
constructor(model: T, dragSource?: DragSource<T>);
/**
* Get the list of children of this node.
*
* @returns {SortableTreeNode<T>[] | null} - List of children or null if no children exist.
*/
abstract getChildren(): SortableTreeNode<T>[] | null;
/**
* Get the parent node of this node, or null if it has no parent.
*
* @returns {SortableTreeNode<T> | null} - Parent node or null if it has no parent.
*/
abstract getParent(): SortableTreeNode<T> | null;
/**
* Add a child node at a particular index.
*
* @param {SortableTreeNode<T>} node - The node to add.
* @param {number} index - The position to insert the child node at.
* @returns {SortableTreeNode<T>} - The added node.
*/
abstract addChildAt(node: SortableTreeNode<T>, index: number): SortableTreeNode<T>;
/**
* Remove a child node at a particular index.
*
* @param {number} index - The index to remove the child node from.
*/
abstract removeChildAt(index: number): void;
/**
* Get the index of a child node in the current node's list of children.
*
* @param {SortableTreeNode<T>} node - The node whose index is to be found.
* @returns {number} - The index of the node, or -1 if the node is not a child.
*/
abstract indexOfChild(node: SortableTreeNode<T>): number;
/**
* Determine if a node can be moved to a specific index in another node's children list.
*
* @param {SortableTreeNode<T>} source - The node to be moved.
* @param {number} index - The index at which the node will be inserted.
* @returns {boolean} - True if the move is allowed, false otherwise.
*/
abstract canMove(source: SortableTreeNode<T>, index: number): boolean;
/**
* Get the view associated with this node, if any.
*
* @returns {View | undefined} - The view associated with this node, or undefined if none.
*/
abstract get view(): View | undefined;
/**
* Get the HTML element associated with this node.
*
* @returns {HTMLElement} - The associated HTML element.
*/
abstract get element(): HTMLElement | undefined;
/**
* Get the model associated with this node.
*
* @returns {T} - The associated model.
*/
get model(): T;
get dragSource(): DragSource<T>;
get dropArea(): Dimension | undefined;
/**
* Checks if the given coordinates are within the bounds of this node.
*
* @param {number} x - The X coordinate to check.
* @param {number} y - The Y coordinate to check.
* @returns {boolean} - True if the coordinates are within bounds, otherwise false.
*/
isWithinDropBounds(x: number, y: number): boolean;
equals(node?: SortableTreeNode<T>): node is SortableTreeNode<T>;
adjustDimensions(diff: {
topDifference: number;
leftDifference: number;
}): void;
}
declare abstract class BaseComponentNode extends SortableTreeNode<Component> {
private displayCache;
/**
* Get the list of child components.
* @returns {BaseComponentNode[] | null} - The list of children wrapped in
* BaseComponentNode, or null if there are no children.
*/
getChildren(): BaseComponentNode[] | null;
/**
* Get the list of displayed children, i.e., components that have a valid HTML element.
* Cached values are used to avoid recalculating the display status unnecessarily.
* @returns {BaseComponentNode[] | null} - The list of displayed children wrapped in
* BaseComponentNode, or null if there are no displayed children.
*/
private getDisplayedChildren;
/**
* Check if a child is displayed, using cached value if available.
* @param {Component} child - The child component to check.
* @returns {boolean} - Whether the child is displayed.
*/
private isChildDisplayed;
/**
* Get the parent component of this node.
* @returns {BaseComponentNode | null} - The parent wrapped in BaseComponentNode,
* or null if no parent exists.
*/
getParent(): BaseComponentNode | null;
/**
* Add a child component to this node at the specified index.
* @param {BaseComponentNode} node - The child node to add.
* @param {number} displayIndex - The visual index at which to insert the child.
* @param {{ action: string }} options - Options for the operation, with the default action being 'add-component'.
* @returns {BaseComponentNode} - The newly added child node wrapped in BaseComponentNode.
*/
addChildAt(node: BaseComponentNode, displayIndex: number, options?: {
action: string;
}): BaseComponentNode;
/**
* Remove a child component at the specified index.
* @param {number} displayIndex - The visual index of the child to remove.
* @param {{ temporary: boolean }} options - Whether to temporarily remove the child.
*/
removeChildAt(displayIndex: number, options?: {
temporary: boolean;
}): void;
/**
* Get the visual index of a child node within the displayed children.
* @param {BaseComponentNode} node - The child node to locate.
* @returns {number} - The index of the child node, or -1 if not found.
*/
indexOfChild(node: BaseComponentNode): number;
/**
* Get the index of the given node within the displayed children.
* @param {BaseComponentNode} node - The node to find.
* @returns {number} - The display index of the node, or -1 if not found.
*/
private getDisplayIndex;
/**
* Convert a display index to the actual index within the component's children array.
* @param {number} index - The display index to convert.
* @returns {number} - The corresponding real index, or -1 if not found.
*/
getRealIndex(index: number): number;
/**
* Check if a source node can be moved to a specified index within this component.
* @param {BaseComponentNode} source - The source node to move.
* @param {number} index - The display index to move the source to.
* @returns {boolean} - True if the move is allowed, false otherwise.
*/
canMove(source: BaseComponentNode, index: number): boolean;
/**
* Abstract method to get the view associated with this component.
* Subclasses must implement this method.
* @abstract
*/
abstract get view(): any;
/**
* Abstract method to get the DOM element associated with this component.
* Subclasses must implement this method.
* @abstract
*/
abstract get element(): HTMLElement | undefined;
/**
* Reset the state of the node by clearing its status and disabling editing.
*/
restNodeState(): void;
/**
* Set the contentEditable property of the node's DOM element.
* @param {boolean} value - True to make the content editable, false to disable editing.
*/
setContentEditable(value: boolean): void;
/**
* Disable editing capabilities for the component's view.
* This method depends on the presence of the `disableEditing` method in the view.
*/
private disableEditing;
/**
* Clear the current state of the node by resetting its status.
*/
private clearState;
/**
* Set the state of the node to 'selected-parent'.
*/
setSelectedParentState(): void;
/**
* Determine if the component is a text node.
* @returns {boolean} - True if the component is a text node, false otherwise.
*/
isTextNode(): boolean;
/**
* Determine if the component is textable.
* @returns {boolean} - True if the component is textable, false otherwise.
*/
isTextable(): boolean;
}
declare class CanvasComponentNode extends BaseComponentNode {
protected _dropAreaConfig: {
ratio: number;
minUndroppableDimension: number;
maxUndroppableDimension: number;
};
/**
* Get the associated view of this component.
* @returns The view associated with the component, or undefined if none.
*/
get view(): ComponentView<Component> | undefined;
/**
* Get the associated element of this component.
* @returns The Element associated with the component, or undefined if none.
*/
get element(): HTMLElement | undefined;
}
declare class CanvasNewComponentNode extends CanvasComponentNode {
canMove(source: CanvasNewComponentNode, index: number): boolean;
private canMoveSingleContent;
addChildAt(node: CanvasNewComponentNode, index: number): CanvasNewComponentNode;
/**
* Adds a single content item to the current node.
* @param {ContentType} content - The content to add.
* @param {number} index - The index where the content is to be added.
* @param {boolean} insertingTextableIntoText - Whether the operation involves textable content.
* @returns {CanvasNewComponentNode} - The newly added node.
*/
private addSingleChild;
/**
* Adds multiple content items as children, looping through the array.
* @param {any[]} contentArray - Array of content items
* @param {number} index - Index to start adding children
* @param {boolean} insertingTextableIntoText - Whether inserting textable content
* @returns {CanvasNewComponentNode} The last added node
*/
private addMultipleChildren;
/**
* Checks if the source component belongs to the same symbol model as the current component.
* @param {Component | undefined} symbolModel - Symbol model to compare
* @returns {boolean} Whether the source is the same symbol
*/
private isSourceSameSymbol;
set content(content: ContentType | (() => ContentType));
}
export type ContainerContext = {
container: HTMLElement;
itemSel: string;
customTarget?: CustomTarget;
document: Document;
};
export interface DropLocationDeterminerOptions<T, NodeType extends SortableTreeNode<T>> {
em: EditorModel;
treeClass: new (model: T, dragSource?: DragSource<T>) => NodeType;
containerContext: ContainerContext;
positionOptions: PositionOptions;
dragDirection: DragDirection;
eventHandlers: SorterEventHandlers<NodeType>;
}
/**
* Represents the data related to the last move event during drag-and-drop sorting.
* This type is discriminated by the presence or absence of a valid target node.
*/
export type lastMoveData<NodeType> = {
/** The target node under the mouse pointer during the last move. */
targetNode?: NodeType;
/** The node under the mouse pointer during this move*/
hoveredNode?: NodeType;
/** The index where the placeholder or dragged element should be inserted. */
index?: number;
/** Placement relative to the target ('before' or 'after'). */
placement?: Placement;
/** The mouse event, used if we want to move placeholder with scrolling. */
mouseEvent?: MouseEvent;
placeholderDimensions?: Dimension;
};
declare class DropLocationDeterminer<T, NodeType extends SortableTreeNode<T>> extends View {
em: EditorModel;
treeClass: new (model: any, dragSource?: DragSource<T>) => NodeType;
positionOptions: PositionOptions;
containerContext: ContainerContext;
dragDirection: DragDirection;
eventHandlers: SorterEventHandlers<NodeType>;
sourceNodes: NodeType[];
lastMoveData: lastMoveData<NodeType>;
containerOffset: {
top: number;
left: number;
};
private moveThreshold;
private rateLimiter;
constructor(options: DropLocationDeterminerOptions<T, NodeType>);
/**
* Picking components to move
* @param {HTMLElement[]} sourceElements
* */
startSort(sourceNodes: NodeType[]): void;
private bindDragEventHandlers;
/**
* Triggers the `onMove` event.
*
* This method is should be called when the user scrolls within the container, using the last recorded mouse event
* to determine the new target.
*/
recalculateTargetOnScroll(): void;
private onMove;
private handleMove;
private adjustForScroll;
private restLastMoveData;
private triggerLegacyOnMoveCallback;
private triggerMoveEvent;
/**
* Handles the movement of the dragged element over a target node.
* Updates the placeholder position and triggers relevant events when necessary.
*
* @param node - The node currently being hovered over.
* @param mouseX - The x-coordinate of the mouse relative to the container.
* @param mouseY - The y-coordinate of the mouse relative to the container.
*/
private getDropPosition;
/**
* Retrieves the target node based on the mouse event.
* Determines the element being hovered, its corresponding model, and
* calculates the valid parent node to use as the target node.
*
* @param mouseEvent - The mouse event containing the cursor position and target element.
* @returns The target node if a valid one is found, otherwise undefined.
*/
private getTargetNode;
/**
* Creates a new hovered node or reuses the last hovered node if it is the same.
*
* @param hoveredModel - The model corresponding to the hovered element.
* @returns The new or reused hovered node.
*/
private getOrCreateHoveredNode;
/**
* Checks if the target node has changed and returns the last one if they are identical.
*
* @param targetNode - The newly calculated target node.
* @returns The new or reused target node.
*/
private getOrReuseTargetNode;
private getMouseTargetElement;
private onDragStart;
endDrag(): void;
cancelDrag(): void;
private finalizeMove;
private dropDragged;
private triggerOnDragEndEvent;
/**
* Retrieves the first element that has a data model associated with it.
* Traverses up the DOM tree from the given element until it reaches the container
* or an element with a data model.
*
* @param mouseTargetEl - The element to start searching from.
* @returns The first element with a data model, or null if not found.
*/
private getFirstElementWithAModel;
private getValidParent;
private handleParentTraversal;
private getIndexInParent;
private triggerDragValidation;
/**
* Clean up event listeners that were attached during the move.
*
* @param {HTMLElement} container - The container element.
* @param {Document[]} docs - List of documents.
* @private
*/
private cleanupEventListeners;
/**
* Determines if an element is in the normal flow of the document.
* This checks whether the element is not floated or positioned in a way that removes it from the flow.
*
* @param {HTMLElement} el - The element to check.
* @param {HTMLElement} [parent=document.body] - The parent element for additional checks (defaults to `document.body`).
* @return {boolean} Returns `true` if the element is in flow, otherwise `false`.
* @private
*/
private getDirection;
/**
* Get children dimensions
* @param {NodeType} el Element root
* @return {Array}
* */
private getChildrenDim;
/**
* Gets the mouse position relative to the container, adjusting for scroll and canvas relative options.
*
* @return {{ mouseXRelativeToContainer: number, mouseYRelativeToContainer: number }} - The mouse X and Y positions relative to the container.
* @private
*/
private getMousePositionRelativeToContainer;
/**
* Caches the container position and updates relevant variables for position calculation.
*
* @private
*/
private cacheContainerPosition;
/**
* Returns dimensions and positions about the element
* @param {HTMLElement} el
* @return {Dimension}
*/
private getDim;
}
declare class PlaceholderClass extends View {
pfx: string;
allowNesting: boolean;
container: HTMLElement;
el: HTMLElement;
offset: {
top: number;
left: number;
};
private moveLimiter;
constructor(options: {
container: HTMLElement;
pfx?: string;
allowNesting?: boolean;
el: HTMLElement;
offset: {
top: number;
left: number;
};
});
show(): void;
hide(): void;
/**
* Updates the position of the placeholder with a movement threshold.
* @param {Dimension} elementDimension element dimensions.
* @param {Placement} placement either before or after the target.
*/
move(elementDimension: Dimension, placement: Placement): void;
private _move;
/**
* Sets the orientation of the placeholder based on the element dimensions.
* @param {Dimension} elementDimension Dimensions of the element at the index.
*/
private setOrientationForDimension;
/**
* Sets the placeholder's class to vertical.
*/
private setOrientation;
/**
* Updates the CSS styles of the placeholder element.
* @param {number} top Top position of the placeholder.
* @param {number} left Left position of the placeholder.
* @param {string} width Width of the placeholder.
* @param {string} height Height of the placeholder.
*/
private updateStyles;
private adjustOffset;
}
declare class Sorter<T, NodeType extends SortableTreeNode<T>> {
em: EditorModel;
treeClass: new (model: T, dragSource?: DragSource<T>) => NodeType;
placeholder: PlaceholderClass;
dropLocationDeterminer: DropLocationDeterminer<T, NodeType>;
positionOptions: PositionOptions;
containerContext: SorterContainerContext;
dragBehavior: SorterDragBehaviorOptions;
eventHandlers: SorterEventHandlers<NodeType>;
sourceNodes?: NodeType[];
constructor(sorterOptions: SorterOptions<T, NodeType>);
/**
* Picking components to move
* @param {HTMLElement[]} sources[]
* */
startSort(sources: {
element?: HTMLElement;
dragSource?: DragSource<T>;
}[]): void;
validTarget(targetEl: HTMLElement | undefined, sources: {
element?: HTMLElement;
dragSource?: DragSource<T>;
}[], index: number): boolean;
private getSourceNodes;
/**
* This method is should be called when the user scrolls within the container.
*/
protected recalculateTargetOnScroll(): void;
/**
* Called when the drag operation should be cancelled
*/
cancelDrag(): void;
/**
* Called to drop an item onto a valid target.
*/
endDrag(): void;
private handlePlaceholderMove;
/**
* Creates a new placeholder element for the drag-and-drop operation.
*
* @returns {PlaceholderClass} The newly created placeholder instance.
*/
private createPlaceholder;
private ensurePlaceholderElement;
/**
* Triggered when the offset of the editor is changed
*/
private updateOffset;
/**
* Finds the closest valid source element within the container context.
* @param sourceElement - The initial source element to check.
* @returns The closest valid source element, or null if none is found.
*/
private findValidSourceElement;
protected bindDragEventHandlers(): void;
private updatePlaceholderPosition;
/**
* Clean up event listeners that were attached during the move.
*
* @private
*/
protected cleanupEventListeners(): void;
/**
* Finalize the move.
*
* @private
*/
protected finalizeMove(): void;
/**
* Cancels the drag on Escape press ( nothing is dropped or moved )
* @param {KeyboardEvent} e - The keyboard event object.
*/
private rollback;
protected triggerNullOnEndMove(dragIsCancelled: boolean): void;
}
export interface CategoryViewConfig {
em: EditorModel;
pStylePrefix?: string;
stylePrefix?: string;
}
declare class CategoryView extends View<Category> {
em: EditorModel;
config: CategoryViewConfig;
pfx: string;
caretR: string;
caretD: string;
iconClass: string;
activeClass: string;
iconEl?: HTMLElement;
typeEl?: HTMLElement;
catName: string;
events(): {
"click [data-title]": string;
};
template({ pfx, label, catName }: {
pfx: string;
label: string;
catName: string;
}): string;
/** @ts-ignore */
attributes(): Record<string, any>;
constructor(o: any, config: CategoryViewConfig, catName: string);
updateVisibility(): void;
open(): void;
close(): void;
toggle(): void;
getIconEl(): HTMLElement;
getTypeEl(): HTMLElement;
append(el: HTMLElement): void;
render(): this;
}
interface CategoryProperties {
/**
* Category id.
*/
id: string;
/**
* Category label.
*/
label: string;
/**
* Category open state.
* @default true
*/
open?: boolean;
/**
* Category order.
*/
order?: string | number;
/**
* Category attributes.
* @default {}
*/
attributes?: Record<string, any>;
}
export interface ItemsByCategory<T> {
category?: Category;
items: T[];
}
export declare class Category extends Model<CategoryProperties> {
view?: CategoryView;
defaults(): {
id: string;
label: string;
open: boolean;
attributes: {};
};
getId(): string;
getLabel(): string;
}
export type CategoryCollectionParams = ConstructorParameters<typeof Collection<Category>>;
export interface CategoryOptions {
events?: {
update?: string;
};
em?: EditorModel;
}
export declare class Categories extends Collection<Category> {
constructor(models?: CategoryCollectionParams[0], opts?: CategoryOptions);
/** @ts-ignore */
add(model: (CategoryProperties | Category)[] | CategoryProperties | Category, opts?: AddOptions): Category;
get(id: string | Category): Category;
}
export interface ModelWithCategoryProps {
category?: string | CategoryProperties;
}
declare abstract class CollectionWithCategories<T extends Model<ModelWithCategoryProps>> extends Collection<T> {
abstract getCategories(): Categories;
initCategory(model: T): Category | undefined;
}
export declare class Blocks extends CollectionWithCategories<Block> {
em: EditorModel;
constructor(coll: any[], options: {
em: EditorModel;
});
getCategories(): Categories;
handleAdd(model: Block): void;
}
/** @private */
export interface BlockProperties extends DraggableContent {
/**
* Block label, eg. `My block`
*/
label: string;
/**
* HTML string for the media/icon of the block, eg. `<svg ...`, `<img ...`, etc.
* @default ''
*/
media?: string;
/**
* Block category, eg. `Basic blocks`
* @default ''
*/
category?: string | CategoryProperties;
/**
* If true, triggers the `active` event on the dropped component.
* @default false
*/
activate?: boolean;
/**
* If true, the dropped component will be selected.
* @default false
*/
select?: boolean;
/**
* If true, all IDs of dropped components and their styles will be changed.
* @default false
*/
resetId?: boolean;
/**
* Disable the block from being interacted.
* @default false
*/
disable?: boolean;
/**
* Custom behavior on click.
* @example
* onClick: (block, editor) => editor.getWrapper().append(block.get('content'))
*/
onClick?: (block: Block, editor: Editor) => void;
/**
* Block attributes
*/
attributes?: Record<string, any>;
id?: string;
/**
* @deprecated
*/
activeOnRender?: boolean;
}
/**
* @property {String} label Block label, eg. `My block`
* @property {String|Object} content The content of the block. Might be an HTML string or a [Component Defintion](/modules/Components.html#component-definition)
* @property {String} [media=''] HTML string for the media/icon of the block, eg. `<svg ...`, `<img ...`, etc.
* @property {String} [category=''] Block category, eg. `Basic blocks`
* @property {Boolean} [activate=false] If true, triggers the `active` event on the dropped component.
* @property {Boolean} [select=false] If true, the dropped component will be selected.
* @property {Boolean} [resetId=false] If true, all IDs of dropped components and their styles will be changed.
* @property {Boolean} [disable=false] Disable the block from being interacted
* @property {Function} [onClick] Custom behavior on click, eg. `(block, editor) => editor.getWrapper().append(block.get('content'))`
* @property {Object} [attributes={}] Block attributes to apply in the view element
*
* @module docsjs.Block
*/
export declare class Block extends Model<BlockProperties> {
defaults(): {
label: string;
content: string;
media: string;
category: string;
activate: boolean;
select: undefined;
resetId: boolean;
disable: boolean;
onClick: undefined;
attributes: {};
dragDef: {};
};
get category(): Category | undefined;
get parent(): Blocks;
/**
* Get block id
* @returns {String}
*/
getId(): string;
/**
* Get block label
* @returns {String}
*/
getLabel(): string;
/**
* Get block media
* @returns {String}
*/
getMedia(): string | undefined;
/**
* Get block content
* @returns {Object|String|Array<Object|String>}
*/
getContent(): ContentType | (() => ContentType) | undefined;
/**
* Get block component dragDef
* @returns {ComponentDefinition}
*/
getDragDef(): ComponentDefinition | undefined;
/**
* Get block category label
* @returns {String}
*/
getCategoryLabel(): string;
}
declare class ComponentSorter<NodeType extends BaseComponentNode> extends Sorter<Component, NodeType> {
targetIsText: boolean;
__currentBlock?: Block;
constructor({ em, treeClass, containerContext, dragBehavior, positionOptions, eventHandlers, }: {
em: EditorModel;
treeClass: new (model: Component, dragSource?: DragSource<Component>) => NodeType;
containerContext: SorterContainerContext;
dragBehavior: SorterDragBehaviorOptions;
positionOptions?: PositionOptions;
eventHandlers?: SorterEventHandlers<NodeType>;
});
private onStartSort;
protected bindDragEventHandlers(): void;
protected cleanupEventListeners(): void;
handleScrollEvent(...agrs: any[]): void;
private onMouseMove;
/**
* Handles the drop action by moving the source nodes to the target node.
* Calls appropriate handlers based on whether the move was successful or not.
*
* @param targetNode - The node where the source nodes will be dropped.
* @param sourceNodes - The nodes being dropped.
* @param index - The index at which to drop the source nodes.
*/
private onDrop;
/**
* Handles the addition of multiple source nodes to the target node.
* If the move is valid, adds the nodes at the specified index and increments the index.
*
* @param targetNode - The target node where source nodes will be added.
* @param sourceNodes - The nodes being added.
* @param index - The initial index at which to add the source nodes.
* @returns The list of successfully added nodes.
*/
private handleNodeAddition;
/**
* Determines if a source node position has changed.
*
* @param targetNode - The node where the source node will be moved.
* @param sourceNode - The node being moved.
* @param index - The index at which to move the source node.
* @returns Whether the node can be moved.
*/
private isPositionChanged;
/**
* Moves a source node to the target node at the specified index, handling edge cases.
*
* @param targetNode - The node where the source node will be moved.
* @param sourceNode - The node being moved.
* @param index - The index at which to move the source node.
* @returns An object containing the added node and its new index, or null if it couldn't be moved.
*/
private moveNode;
/**
* Triggers the end move event for a node that was added to the target.
*
* @param addedNode - The node that was moved and added to the target.
*/
private triggerEndMoveEvent;
/**
* Finalize the move by removing any helpers and selecting the target model.
*
* @private
*/
protected finalizeMove(): void;
private onTargetChange;
private updateTextViewCursorPosition;
/**
* Change Autoscroll while sorting
* @param {Boolean} active
*/
private setAutoCanvasScroll;
}
export type DragStop = (cancel?: boolean) => void;
declare class Droppable {
em: EditorModel;
canvas: CanvasModule;
el: HTMLElement;
counter: number;
getSorterOptions?: (sorter: any) => Record<string, any> | null;
over?: boolean;
dragStop?: DragStop;
draggedNode?: CanvasNewComponentNode;
sorter: ComponentSorter<CanvasNewComponentNode>;
setAbsoluteDragContent?: (cnt: any) => any;
constructor(em: EditorModel, rootEl?: HTMLElement);
toggleEffects(el: HTMLElement, enable: boolean): void;
__customTglEff(enable: boolean): void;
startCustom(): void;
endCustom(cancel?: boolean): void;
/**
* This function is expected to be always executed at the end of d&d.
*/
endDrop(cancel?: boolean, ev?: Event): void;
handleDragLeave(ev: Event): void;
updateCounter(value: number, ev: Event): void;
handleDragEnter(ev: DragEvent | Event): void;
/**
* Generates a temporary model of the content being dragged for use with the sorter.
* @returns The temporary model representing the dragged content.
*/
private getTempDropModel;
handleDragEnd(model: any, dt: any): void;
/**
* Always need to have this handler active for enabling the drop
* @param {Event} ev
*/
handleDragOver(ev: Event): void;
/**
* WARNING: This function might fail to run on drop, for example, when the
* drop, accidentally, happens on some external element (DOM not inside the iframe)
*/
handleDrop(ev: Event | DragEvent): void;
getContentByData(dt: any): {
content: any;
};
}
export interface PageManagerConfig extends ModuleConfig {
/**
* Default pages.
*/
pages?: PageProperties[];
/**
* ID of the page to select on editor load.
*/
selected?: string;
}
export interface SelectableOption {
/**
* Select the page.
*/
select?: boolean;
}
export interface AbortOption {
abort?: boolean;
}
declare enum PagesEvents {
/**
* @event `page:add` Added new page. The page is passed as an argument to the callback.
* @example
* editor.on('page:add', (page) => { ... });
*/
add = "page:add",
addBefore = "page:add:before",
/**
* @event `page:remove` Page removed. The page is passed as an argument to the callback.
* @example
* editor.on('page:remove', (page) => { ... });
*/
remove = "page:remove",
removeBefore = "page:remove:before",
/**
* @event `page:select` New page selected. The newly selected page and the previous one, are passed as arguments to the callback.
* @example
* editor.on('page:select', (page, previousPage) => { ... });
*/
select = "page:select",
selectBefore = "page:select:before",
/**
* @event `page:update` Page updated. The updated page and the object containing changes are passed as arguments to the callback.
* @example
* editor.on('page:update', (page, changes) => { ... });
*/
update = "page:update",
/**
* @event `page` Catch-all event for all the events mentioned above. An object containing all the available data about the triggered event is passed as an argument to the callback.
* @example
* editor.on('page', ({ event, model, ... }) => { ... });
*/
all = "page"
}
export declare class Frames extends ModuleCollection<Frame> {
loadedItems: number;
itemsToLoad: number;
page?: Page;
constructor(module: CanvasModule, models?: Frame[] | Array<Record<string, any>>);
onAdd(frame: Frame): void;
onReset(m: Frame, opts?: {
previousModels?: Frame[];
}): void;
onRemove(frame: Frame): void;
initRefs(): void;
itemLoaded(): void;
listenToLoad(): void;
listenToLoadItems(on: boolean): void;
}
/** @private */
export interface PageProperties {
/**
* Panel id.
*/
id?: string;
/**
* Page name.
*/
name?: string;
/**
* HTML to load as page content.
*/
component?: string | ComponentDefinition | ComponentDefinition[];
/**
* CSS to load with the page.
*/
styles?: string | CssRuleJSON[];
[key: string]: unknown;
}
export interface PagePropertiesDefined extends Pick<PageProperties, "id" | "name"> {
frames: Frames;
[key: string]: unknown;
}
export declare class Page extends Model<PagePropertiesDefined> {
defaults(): {
name: string;
frames: Frames;
_undo: boolean;
};
em: EditorModel;
constructor(props: any, opts?: {
em?: EditorModel;
config?: PageManagerConfig;
});
onRemove(): void;
getFrames(): Frames;
/**
* Get page id
* @returns {String}
*/
getId(): string;
/**
* Get page name
* @returns {String}
*/
getName(): string;
/**
* Update page name
* @param {String} name New page name
* @example
* page.setName('New name');
*/
setName(name: string): this;
/**
* Get all frames
* @returns {Array<Frame>}
* @example
* const arrayOfFrames = page.getAllFrames();
*/
getAllFrames(): Frame[];
/**
* Get the first frame of the page (identified always as the main one)
* @returns {Frame}
* @example
* const mainFrame = page.getMainFrame();
*/
getMainFrame(): Frame;
/**
* Get the root component (usually is the `wrapper` component) from the main frame
* @returns {Component}
* @example
* const rootComponent = page.getMainComponent();
* console.log(rootComponent.toHTML());
*/
getMainComponent(): ComponentWrapper;
toJSON(opts?: {}): any;
}
export declare class Canvas extends ModuleModel<CanvasModule> {
defaults(): {
frame: string;
frames: never[];
rulers: boolean;
zoom: number;
x: number;
y: number;
scripts: never[];
styles: never[];
pointer: Coordinates;
pointerScreen: Coordinates;
};
constructor(module: CanvasModule);
get frames(): Frames;
init(): void;
_pageUpdated(page: Page, prev?: Page): void;
updateDevice(opts?: any): void;
onZoomChange(): void;
onCoordsChange(): void;
onPointerChange(): void;
getPointerCoords(type?: CoordinatesTypes): Coordinates;
}
export type DraggerPosition = Position & {
end?: boolean;
};
export type Guide = {
x: number;
y: number;
lock?: number;
active?: boolean;
};
export interface DraggerOptions {
/**
* Element on which the drag will be executed. By default, the document will be used
*/
container?: HTMLElement;
/**
* Callback on drag start.
* @example
* onStart(ev, dragger) {
* console.log('pointer start', dragger.startPointer, 'position start', dragger.startPosition);
* }
*/
onStart?: (ev: Event, dragger: Dragger) => void;
/**
* Callback on drag.
* @example
* onDrag(ev, dragger) {
* console.log('pointer', dragger.currentPointer, 'position', dragger.position, 'delta', dragger.delta);
* }
*/
onDrag?: (ev: Event, dragger: Dragger) => void;
/**
* Callback on drag end.
* @example
* onEnd(ev, dragger) {
* console.log('pointer', dragger.currentPointer, 'position', dragger.position, 'delta', dragger.delta);
* }
*/
onEnd?: (ev: Event, dragger: Dragger, opts: {
cancelled: boolean;
}) => void;
/**
* Indicate a callback where to pass an object with new coordinates
*/
setPosition?: (position: DraggerPosition) => void;
/**
* Indicate a callback where to get initial coordinates.
* @example
* getPosition: () => {
* // ...
* return { x: 10, y: 100 }
* }
*/
getPosition?: () => DraggerPosition;
/**
* Indicate a callback where to get pointer coordinates.
*/
getPointerPosition?: (ev: Event) => DraggerPosition;
/**
* Static guides to be snapped.
*/
guidesStatic?: () => Guide[];
/**
* Target guides that will snap to static one.
*/
guidesTarget?: () => Guide[];
/**
* Offset before snap to guides.
* @default 5
*/
snapOffset?: number;
/**
* Document on which listen to pointer events.
*/
doc?: Document;
/**
* Scale result points, can also be a function.
* @default 1
*/
scale?: number | (() => number);
}
declare class Dragger {
opts: DraggerOptions;
startPointer: DraggerPosition;
delta: DraggerPosition;
lastScroll: DraggerPosition;
lastScrollDiff: DraggerPosition;
startPosition: DraggerPosition;
globScrollDiff: DraggerPosition;
currentPointer: DraggerPosition;
position: DraggerPosition;
el?: HTMLElement;
guidesStatic: Guide[];
guidesTarget: Guide[];
lockedAxis?: any;
docs: Document[];
trgX?: Guide;
trgY?: Guide;
/**
* Init the dragger
* @param {Object} opts
*/
constructor(opts?: DraggerOptions);
/**
* Update options
* @param {Object} options
*/
setOptions(opts?: Partial<DraggerOptions>): void;
toggleDrag(enable?: boolean): void;
handleScroll(): void;
/**
* Start dragging
* @param {Event} e
*/
start(ev: Event): void;
/**
* Drag event
* @param {Event} event
*/
drag(ev: Event): void;
/**
* Check if the delta hits some guide
*/
snapGuides(delta: DraggerPosition): {
newDelta: DraggerPosition;
trgX: Guide | undefined;
trgY: Guide | undefined;
};
isPointIn(src: number, trg: number, { offset }?: {
offset?: number;
}): boolean;
setGuideLock(guide: Guide, value: any): Guide;
/**
* Stop dragging
*/
stop(ev: Event, opts?: {
cancel?: boolean;
}): void;
keyHandle(ev: Event): void;
/**
* Move the element
* @param {integer} x
* @param {integer} y
*/
move(x: number, y: number, end?: boolean): void;
getContainerEl(): Document[] | HTMLElement[];
getWindowEl(): any[];
/**
* Returns documents
*/
getDocumentEl(el?: HTMLElement): Document[];
/**
* Get mouse coordinates
* @param {Event} event
* @return {Object}
*/
getPointerPos(ev: Event): DraggerPosition;
getStartPosition(): {
x: number;
y: number;
};
getScrollInfo(): {
y: number;
x: number;
};
detectAxisLock(x: number, y: number): "x" | "y" | undefined;
}
export interface ToScreenOption {
toScreen?: boolean;
}
export interface ToWorldOption {
toWorld?: boolean;
}
export interface GetBoxRectOptions extends ToScreenOption {
local?: boolean;
}
export interface CanvasRefreshOptions {
/**
* Refresh canvas spots.
*/
spots?: boolean;
all?: boolean;
}
declare enum CanvasEvents {
/**
* @event `canvas:dragenter` Something is dragged inside the canvas, `DataTransfer` instance passed as an argument.
*/
dragEnter = "canvas:dragenter",
/**
* @event `canvas:dragover` Something is dragging on the canvas, `DataTransfer` instance passed as an argument.
*/
dragOver = "canvas:dragover",
/**
* @event `canvas:dragend` When a drag operation is ended, `DataTransfer` instance passed as an argument.
*/
dragEnd = "canvas:dragend",
/**
* @event `canvas:dragdata` On any dataTransfer parse, `DataTransfer` instance and the `result` are passed as arguments. By changing `result.content` you're able to customize what is dropped.
*/
dragData = "canvas:dragdata",
/**
* @event `canvas:drop` Something is dropped in canvas, `DataTransfer` instance and the dropped model are passed as arguments.
*/
drop = "canvas:drop",
/**
* @event `canvas:spot` Spots updated.
* @example
* editor.on('canvas:spot', () => {
* console.log('Spots', editor.Canvas.getSpots());
* });
*/
spot = "canvas:spot",
/**
* @event `canvas:spot:add` New canvas spot added.
* @example
* editor.on('canvas:spot:add', ({ spot }) => {
* console.log('Spot added', spot);
* });
*/
spotAdd = "canvas:spot:add",
/**
* @event `canvas:spot:update` Canvas spot updated.
* @example
* editor.on('canvas:spot:update', ({ spot }) => {
* console.log('Spot updated', spot);
* });
*/
spotUpdate = "canvas:spot:update",
/**
* @event `canvas:spot:remove` Canvas spot removed.
* @example
* editor.on('canvas:spot:remove', ({ spot }) => {
* console.log('Spot removed', spot);
* });
*/
spotRemove = "canvas:spot:remove",
/**
* @event `canvas:coords` Canvas coordinates updated.
* @example
* editor.on('canvas:coords', () => {
* console.log('Canvas coordinates updated:', editor.Canvas.getCoords());
* });
*/
coords = "canvas:coords",
/**
* @event `canvas:zoom` Canvas zoom updated.
* @example
* editor.on('canvas:zoom', () => {
* console.log('Canvas zoom updated:', editor.Canvas.getZoom());
* });
*/
zoom = "canvas:zoom",
/**
* @event `canvas:pointer` Canvas pointer updated.
* @example
* editor.on('canvas:pointer', () => {
* console.log('Canvas pointer updated:', editor.Canvas.getPointer());
* });
*/
pointer = "canvas:pointer",
/**
* @event `canvas:refresh` Canvas was refreshed to update elements on top, like spots/tools (eg. via `editor.Canvas.refresh()` or on frame resize).
* @example
* editor.on('canvas:refresh', (canvasRefreshOptions) => {
* console.log('Canvas refreshed with options:', canvasRefreshOptions);
* });
*/
refresh = "canvas:refresh",
/**
* @event `canvas:frame:load` Frame loaded in canvas. The event is triggered right after iframe's `onload`.
* @example
* editor.on('canvas:frame:load', ({ window }) => {
* console.log('Frame loaded', window);
* });
*/
frameLoad = "canvas:frame:load",
/**
* @event `canvas:frame:load:head` Frame head loaded in canvas. The event is triggered right after iframe's finished to load the head elemenets (eg. scripts)
* @example
* editor.on('canvas:frame:load:head', ({ window }) => {
* console.log('Frame head loaded', window);
* });
*/
frameLoadHead = "canvas:frame:load:head",
/**
* @event `canvas:frame:load:body` Frame body loaded in canvas. The event is triggered when the body i