grapesjs
Version:
Free and Open Source Web Builder Framework
1,485 lines (1,483 loc) • 429 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 const DataCollectionVariableType = "data-collection-variable";
declare const DataVariableType: "data-variable";
export interface DataVariableProps {
type: typeof DataVariableType;
path: string;
defaultValue?: string;
}
declare class DataVariable extends Model<DataVariableProps> {
em?: EditorModel;
defaults(): {
type: "data-variable";
defaultValue: string;
path: string;
};
constructor(props: DataVariableProps, options: {
em?: EditorModel;
});
getDataValue(): any;
}
declare enum DataCollectionStateVariableType {
currentIndex = "currentIndex",
startIndex = "startIndex",
currentItem = "currentItem",
endIndex = "endIndex",
collectionId = "collectionId",
totalItems = "totalItems",
remainingItems = "remainingItems"
}
export interface DataCollectionState {
[DataCollectionStateVariableType.currentIndex]: number;
[DataCollectionStateVariableType.startIndex]: number;
[DataCollectionStateVariableType.currentItem]: DataVariableProps;
[DataCollectionStateVariableType.endIndex]: number;
[DataCollectionStateVariableType.collectionId]: string;
[DataCollectionStateVariableType.totalItems]: number;
[DataCollectionStateVariableType.remainingItems]: number;
}
export interface DataCollectionStateMap {
[key: string]: DataCollectionState;
}
export interface DataCollectionVariableProps {
type: typeof DataCollectionVariableType;
variableType: DataCollectionStateVariableType;
collectionId: string;
path?: string;
}
export interface DataResolverListenerProps {
em: EditorModel;
resolver: DataResolver;
onUpdate: (value: any) => void;
}
declare class DataResolverListener {
private listeners;
private em;
private onUpdate;
private model;
resolver: DataResolver;
constructor(props: DataResolverListenerProps);
private onChange;
private createListener;
listenToResolver(): void;
private listenToConditionalVariable;
private listenToDataVariable;
private listenToDataCollectionVariable;
private removeListeners;
destroy(): void;
}
export interface DataCollectionVariablePropsDefined extends DataCollectionVariableProps {
value?: any;
}
declare class DataCollectionVariable extends Model<DataCollectionVariablePropsDefined> {
em: EditorModel;
collectionsStateMap?: DataCollectionStateMap;
dataVariable?: DataVariable;
resolverListener?: DataResolverListener;
defaults(): Partial<DataCollectionVariablePropsDefined>;
constructor(props: DataCollectionVariablePropsDefined, options: {
em: EditorModel;
collectionsStateMap?: DataCollectionStateMap;
});
hasDynamicValue(): boolean;
getDataValue(): any;
private updateDataVariable;
updateCollectionsStateMap(collectionsStateMap: DataCollectionStateMap): void;
destroy(): false | JQueryXHR;
toJSON(options?: any): any;
}
export interface DataSourceOptions extends CombinedModelConstructorOptions<{
em: EditorModel;
}, DataSource> {
}
export declare class DataSource<DRProps extends DataRecordProps = DataRecordProps> extends Model<DataSourceType<DRProps>> {
transformers: DataSourceTransformers;
/**
* Returns the default properties for the data source.
* These include an empty array of records and an empty object of transformers.
*
* @returns {Object} The default attributes for the data source.
* @name defaults
*/
defaults(): DataSourceType<DRProps>;
/**
* Initializes a new instance of the `DataSource` class.
* It sets up the transformers and initializes the collection of records.
* If the `records` property is not an instance of `DataRecords`, it will be converted into one.
*
* @param {DataSourceProps<DRProps>} props - Properties to initialize the data source.
* @param {DataSourceOptions} opts - Options to initialize the data source.
* @name constructor
*/
constructor(props: DataSourceProps<DRProps>, opts: DataSourceOptions);
/**
* Retrieves the collection of records associated with this data source.
*
* @returns {DataRecords<DRProps>} The collection of data records.
* @name records
*/
get records(): NonNullable<DataRecords<DRProps>>;
/**
* Retrieves the editor model associated with this data source.
*
* @returns {EditorModel} The editor model.
* @name em
*/
get em(): EditorModel;
/**
* Handles the `add` event for records in the data source.
* This method triggers a change event on the newly added record.
*
* @param {DataRecord<DRProps>} dr - The data record that was added.
* @private
* @name onAdd
*/
onAdd(dr: DataRecord<DRProps>): void;
/**
* Adds a new record to the data source.
*
* @param {DRProps} record - The properties of the record to add.
* @param {AddOptions} [opts] - Options to apply when adding the record.
* @returns {DataRecord} The added data record.
* @name addRecord
*/
addRecord(record: DRProps, opts?: AddOptions): DataRecord<DRProps>;
/**
* Retrieves a record from the data source by its ID.
*
* @param {string | number} id - The ID of the record to retrieve.
* @returns {DataRecord<DRProps> | undefined} The data record, or `undefined` if no record is found with the given ID.
* @name getRecord
*/
getRecord(id: string | number): DataRecord | undefined;
/**
* Retrieves all records from the data source.
* Each record is processed with the `getRecord` method to apply any read transformers.
*
* @returns {Array<DataRecord<DRProps> | undefined>} An array of data records.
* @name getRecords
*/
getRecords(): DataRecord<DataRecordProps>[];
/**
* Removes a record from the data source by its ID.
*
* @param {string | number} id - The ID of the record to remove.
* @param {RemoveOptions} [opts] - Options to apply when removing the record.
* @returns {DataRecord<DRProps> | undefined} The removed data record, or `undefined` if no record is found with the given ID.
* @name removeRecord
*/
removeRecord(id: string | number, opts?: RemoveOptions): DataRecord<DRProps>;
/**
* Replaces the existing records in the data source with a new set of records.
*
* @param {Array<DRProps>} records - An array of data record properties to set.
* @returns {Array<DataRecord>} An array of the added data records.
* @name setRecords
*/
setRecords(records: DRProps[]): void;
private handleChanges;
}
export declare class DataRecords<T extends DataRecordProps = DataRecordProps> extends Collection<DataRecord<T>> {
dataSource: DataSource;
constructor(models: DataRecord[] | DataRecordProps[], options: {
dataSource: DataSource;
});
}
export declare class DataRecord<T extends DataRecordProps = DataRecordProps> extends Model<T> {
mutable: boolean;
constructor(props: T, opts?: {});
get cl(): DataRecords;
get dataSource(): DataSource;
get em(): EditorModel;
get index(): number;
/**
* Handles changes to the record's attributes.
* This method triggers a change event for each property that has been altered.
*
* @private
* @name handleChange
*/
handleChange(): void;
/**
* Get the path of the record.
* The path is a string that represents the location of the record within the data source.
* Optionally, include a property name to create a more specific path.
*
* @param {String} [prop] - Optional property name to include in the path.
* @param {Object} [opts] - Options for path generation.
* @param {Boolean} [opts.useIndex] - Whether to use the index of the record in the path.
* @returns {String} - The path of the record.
* @name getPath
* @example
* const pathRecord = record.getPath();
* // e.g., 'SOURCE_ID.record1'
* const pathRecord2 = record.getPath('myProp');
* // e.g., 'SOURCE_ID.record1.myProp'
*/
getPath(prop?: string, opts?: {
useIndex?: boolean;
}): string;
/**
* Get both ID-based and index-based paths of the record.
* Returns an array containing the paths using both ID and index.
*
* @param {String} [prop] - Optional property name to include in the paths.
* @returns {Array<String>} - An array of paths.
* @name getPaths
* @example
* const paths = record.getPaths();
* // e.g., ['SOURCE_ID.record1', 'SOURCE_ID.0']
*/
getPaths(prop?: string): string[];
/**
* Trigger a change event for the record.
* Optionally, include a property name to trigger a change event for a specific property.
*
* @param {String} [prop] - Optional property name to trigger a change event for a specific property.
* @name triggerChange
*/
triggerChange(prop?: string): void;
/**
* Set a property on the record, optionally using transformers.
* If transformers are defined for the record, they will be applied to the value before setting it.
*
* @param {String|Object} attributeName - The name of the attribute to set, or an object of key-value pairs.
* @param {any} [value] - The value to set for the attribute.
* @param {Object} [options] - Options to apply when setting the attribute.
* @param {Boolean} [options.avoidTransformers] - If true, transformers will not be applied.
* @returns {DataRecord} - The instance of the DataRecord.
* @name set
* @example
* record.set('name', 'newValue');
* // Sets 'name' property to 'newValue'
*/
set<A extends _StringKey<T>>(attributeName: DeepPartialDot<T> | A, value?: SetOptions | T[A] | undefined, options?: SetOptions | undefined): this;
}
export type ConditionProps = ExpressionProps | LogicGroupProps | boolean;
export interface DataConditionEvaluatorProps {
condition: ConditionProps;
}
declare class DataConditionEvaluator extends Model<DataConditionEvaluatorProps> {
private em;
constructor(props: DataConditionEvaluatorProps, opts: {
em: EditorModel;
});
evaluate(): boolean;
/**
* Factory method for creating operators based on the data type.
*/
private getOperator;
getDependentDataVariables(): DataVariableProps[];
private extractDataVariables;
private isLogicGroup;
private isExpression;
private isOperatorInEnum;
toJSON(options?: any): any;
}
declare enum BooleanOperation {
and = "and",
or = "or",
xor = "xor"
}
declare enum NumberOperation {
greaterThan = ">",
lessThan = "<",
greaterThanOrEqual = ">=",
lessThanOrEqual = "<=",
equals = "=",
notEquals = "!="
}
declare enum StringOperation {
contains = "contains",
startsWith = "startsWith",
endsWith = "endsWith",
matchesRegex = "matchesRegex",
equalsIgnoreCase = "equalsIgnoreCase",
trimEquals = "trimEquals"
}
declare enum AnyTypeOperation {
equals = "equals",
isTruthy = "isTruthy",
isFalsy = "isFalsy",
isDefined = "isDefined",
isNull = "isNull",
isUndefined = "isUndefined",
isArray = "isArray",
isObject = "isObject",
isString = "isString",
isNumber = "isNumber",
isBoolean = "isBoolean",
isDefaultValue = "isDefaultValue"
}
declare const DataConditionType = "data-condition";
export interface ExpressionProps {
left: any;
operator: AnyTypeOperation | StringOperation | NumberOperation;
right: any;
}
export interface LogicGroupProps {
logicalOperator: BooleanOperation;
statements: ConditionProps[];
}
export interface DataConditionProps {
type: typeof DataConditionType;
condition: ConditionProps;
ifTrue: any;
ifFalse: any;
}
export interface DataConditionPropsDefined extends Omit<DataConditionProps, "condition"> {
condition: DataConditionEvaluator;
}
declare class DataCondition extends Model<DataConditionPropsDefined> {
private em;
private resolverListeners;
private _onValueChange?;
constructor(props: {
condition: ConditionProps;
ifTrue: any;
ifFalse: any;
}, opts: {
em: EditorModel;
onValueChange?: () => void;
});
private get conditionEvaluator();
getCondition(): ConditionProps;
getIfTrue(): any;
getIfFalse(): any;
isTrue(): boolean;
getDataValue(skipDynamicValueResolution?: boolean): any;
set onValueChange(newFunction: () => void);
setCondition(newCondition: ConditionProps): void;
setIfTrue(newIfTrue: any): void;
setIfFalse(newIfFalse: any): void;
private listenToDataVariables;
getDependentDataVariables(): DataVariableProps[];
private cleanupListeners;
toJSON(): {
type: string;
condition: DataConditionEvaluator;
ifTrue: any;
ifFalse: any;
};
}
export type DataResolver = DataVariable | DataCondition | DataCollectionVariable;
export interface DataRecordProps extends ObjectAny {
/**
* Record id.
*/
id: string;
/**
* Specifies if the record is mutable. Defaults to `true`.
*/
mutable?: boolean;
[key: string]: any;
}
export interface BaseDataSource {
/**
* DataSource id.
*/
id: string;
/**
* DataSource validation and transformation factories.
*/
transformers?: DataSourceTransformers;
/**
* If true will store the data source in the GrapesJS project.json file.
*/
skipFromStorage?: boolean;
}
export interface DataSourceType<DR extends DataRecordProps> extends BaseDataSource {
records: DataRecords<DR>;
}
export interface DataSourceProps<DR extends DataRecordProps> extends BaseDataSource {
records?: DataRecords<DR> | DataRecord<DR>[] | DR[];
}
export interface DataSourceTransformers {
onRecordSetValue?: (args: {
id: string | number;
key: string;
value: any;
}) => any;
}
declare enum DataSourcesEvents {
/**
* @event `data:add` Added new data source.
* @example
* editor.on('data:add', (dataSource) => { ... });
*/
add = "data:add",
addBefore = "data:add:before",
/**
* @event `data:remove` Data source removed.
* @example
* editor.on('data:remove', (dataSource) => { ... });
*/
remove = "data:remove",
removeBefore = "data:remove:before",
/**
* @event `data:update` Data source updated.
* @example
* editor.on('data:update', (dataSource, changes) => { ... });
*/
update = "data:update",
/**
* @event `data:path` Data record path update.
* @example
* editor.on('data:path:SOURCE_ID:RECORD_ID:PROP_NAME', ({ dataSource, dataRecord, path }) => { ... });
*/
path = "data:path",
/**
* @event `data` Catch-all event for all the events mentioned above.
* @example
* editor.on('data', ({ event, model, ... }) => { ... });
*/
all = "data"
}
/**{END_EVENTS}*/
export type DotSeparatedKeys<T> = T extends object ? {
[K in keyof T]: K extends string ? T[K] extends object ? `${K}` | `${K}.${DotSeparatedKeys<T[K]>}` : `${K}` : never;
}[keyof T] : never;
export type DeepPartialDot<T> = {
[P in DotSeparatedKeys<T>]?: P extends `${infer K}.${infer Rest}` ? K extends keyof T ? Rest extends DotSeparatedKeys<T[K]> ? DeepPartialDot<T[K]>[Rest] : never : never : P extends keyof T ? T[P] : never;
};
export interface DynamicWatchersOptions {
skipWatcherUpdates?: boolean;
fromDataSource?: boolean;
}
export interface ComponentResolverWatcherOptions {
em: EditorModel;
collectionsStateMap?: DataCollectionStateMap;
}
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 Definition](/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 bein