@witchcraft/layout
    Preparing search index...

    Interface IAction

    A drag action describes when and how to handle a drag event.

    For example, there are the default split/close actions that can be triggered in certain situations. This could be when holding down a modifier or key, or some other condition (e.g. the user is dragging a specific edge).

    Each action should handle it's configuration and saving/caching any state it needs. See SplitAction and CloseAction for examples.

    interface IAction {
        debug: string | boolean;
        name: string;
        onMoveApply: (
            state: MoveState,
            forceRecalculateEdges: () => void,
        ) => ActionApplyResult;
        onMoveChange: <T extends "start" | "move" | "end">(
            type: T,
            e: T extends "start" | "end" ? PointerEvent | undefined : PointerEvent,
            state: MoveState,
            forceRecalculateEdges: () => void,
            cancel: (
                e: PointerEvent | KeyboardEvent | undefined,
                state: MoveState,
            ) => void,
            resolve: T extends "end"
                ? undefined
                : (opts: ActionHandlerApplyResult) => void,
        ) => ActionChangeResult;
        annotateEdge?(
            edge: {
                endX: number;
                endY: number;
                error?:
                    | KnownError<
                        LayoutError,
                        | {}
                        | { id: string
                        | undefined }
                        | { id: string | undefined }
                        | { size: { height: number; width: number } }
                        | Record<string, never>
                        | { frame: LayoutFrame }
                        | {
                            frame: LayoutFrame;
                            minSize: { height: number; width: number };
                            nearbyFrames: LayoutFrame[];
                        }
                        | { frame: LayoutFrame }
                        | { frame: LayoutFrame; minSize: number; newSize: number }
                        | { frame: LayoutFrame; framesRequiredToBeDeleted: LayoutFrame[] }
                        | { frame: LayoutFrame }
                        | {
                            hoveredFrameId: string;
                            movingFrameId: string;
                            zoneSide: "left" | "right" | "top" | "bottom" | "center";
                        }
                        | {
                            hoveredFrameId: string;
                            movingFrameId: string;
                            zoneSide: "left"
                            | "right"
                            | "top"
                            | "bottom"
                            | "center";
                        }
                        | {
                            hoveredFrameId: string;
                            movingFrameId: string;
                            zoneSide: "left"
                            | "right"
                            | "top"
                            | "bottom"
                            | "center";
                        }
                        | { frame: LayoutFrame }
                        | { frameSizeNeeded: number; minFrameSize: number }
                        | { max: number; min: number; wanted: number }
                        | { problemEdgeCoordinates: number[] }
                        | { id: string; side: "left" | "right" | "top" | "bottom" }
                        | { frame: string }
                        | { frame: LayoutFrame }
                        | { frame: LayoutFrame }
                        | { frame: LayoutFrame }
                        | { frame: LayoutFrame },
                    >
                    | { code: string; message: string };
                startX: number;
                startY: number;
            },
            frames: LayoutFrame[],
        ): void;
        cancel(e: PointerEvent | KeyboardEvent | undefined, state: MoveState): void;
        canHandleRequest(
            e: PointerEvent | KeyboardEvent | undefined,
            state: MoveState,
            forceRecalculateEdges: () => void,
        ): boolean;
        getTextHints(
            type: "start" | "move" | "end",
        ): { actions?: string[]; errors?: string[] };
    }

    Implemented by

    Index

    Properties

    debug: string | boolean

    Plugins should implement some basic debug logs by calling ActionHandler.debugState at least before and after applying actions in onMoveApply. Debug can be a string because it can be an object key to filter on (see the debugState function).

    Calls look like this usually, where this.state is the plugin state:

    ActionHandler.debugState(this.name, "before", state, this.state, this.debug)
    
    name: string

    A unique name for your action.

    onMoveApply: (
        state: MoveState,
        forceRecalculateEdges: () => void,
    ) => ActionApplyResult

    Is called after onMoveChange("end") with the same event. Will not be called if the request was cancelled.

    You should apply your action if possible and return whether it was applied wasApplied as well as updateEdges and result (optional), see ActionHandler.onMoveApply, which this is the extended version of.

    Do not reset state here, use onMoveChange ("end").

    onMoveChange: <T extends "start" | "move" | "end">(
        type: T,
        e: T extends "start" | "end" ? PointerEvent | undefined : PointerEvent,
        state: MoveState,
        forceRecalculateEdges: () => void,
        cancel: (
            e: PointerEvent | KeyboardEvent | undefined,
            state: MoveState,
        ) => void,
        resolve: T extends "end"
            ? undefined
            : (opts: ActionHandlerApplyResult) => void,
    ) => ActionChangeResult

    Called when the drag coordinates change (during any event).

    Should return { allowed: true/false, shapes: LayoutShape[] } to control whether the action is allowed and edges update and what deco shapes to render.

    Note that the allowed return type only affect the move event but is also typed as boolean for other events for ease of use.

    Use also to cleanup your action when type is "end".

    See also ActionHandler.onMoveChange to understand it's lifecycle as it is the extended version of.

    Type Declaration

      • <T extends "start" | "move" | "end">(
            type: T,
            e: T extends "start" | "end" ? PointerEvent | undefined : PointerEvent,
            state: MoveState,
            forceRecalculateEdges: () => void,
            cancel: (
                e: PointerEvent | KeyboardEvent | undefined,
                state: MoveState,
            ) => void,
            resolve: T extends "end"
                ? undefined
                : (opts: ActionHandlerApplyResult) => void,
        ): ActionChangeResult
      • Type Parameters

        • T extends "start" | "move" | "end"

        Parameters

        • type: T
        • e: T extends "start" | "end" ? PointerEvent | undefined : PointerEvent
        • state: MoveState
        • forceRecalculateEdges: () => void
        • cancel: (e: PointerEvent | KeyboardEvent | undefined, state: MoveState) => void

          Calls moveEnd with updateEdges: false. This can technically be called from "end", it should still work.

        • resolve: T extends "end" ? undefined : (opts: ActionHandlerApplyResult) => void

          Saves result to resolve moveStart promise, updates edges if you passed updateEdges then aborts the handler (only onMoveEnded will fire after) . Not available during "end" event. It's designed for resolving from other external evente (e.g. key events).

        Returns ActionChangeResult

    Methods

    • Called after visual edges are recalculated, once per edge. Action handlers can annotate edges with error info here (Edge.error).

      When LayoutEdges sees an error it adds the message as the title and the following classes:

      • data-error attribute with the error message
      • class drag-edge-errored on the thick drag handle
      • class edge-errored on the thin visual edge

      Tailwind example to style errored edges:

      [&_.drag-edge-errored]:cursor-not-allowed
      [&_.drag-edge-errored]:bg-red-500/30
      [&_.edge-errored]:bg-red-500/20

      Regarding the title, this is a temporary solution until lib is refactored to more composable components.

      NOTE: Annotation does NOT prevent dragging/events on edges.

      Parameters

      • edge: {
            endX: number;
            endY: number;
            error?:
                | KnownError<
                    LayoutError,
                    | {}
                    | { id: string
                    | undefined }
                    | { id: string | undefined }
                    | { size: { height: number; width: number } }
                    | Record<string, never>
                    | { frame: LayoutFrame }
                    | {
                        frame: LayoutFrame;
                        minSize: { height: number; width: number };
                        nearbyFrames: LayoutFrame[];
                    }
                    | { frame: LayoutFrame }
                    | { frame: LayoutFrame; minSize: number; newSize: number }
                    | { frame: LayoutFrame; framesRequiredToBeDeleted: LayoutFrame[] }
                    | { frame: LayoutFrame }
                    | {
                        hoveredFrameId: string;
                        movingFrameId: string;
                        zoneSide: "left" | "right" | "top" | "bottom" | "center";
                    }
                    | {
                        hoveredFrameId: string;
                        movingFrameId: string;
                        zoneSide: "left"
                        | "right"
                        | "top"
                        | "bottom"
                        | "center";
                    }
                    | {
                        hoveredFrameId: string;
                        movingFrameId: string;
                        zoneSide: "left"
                        | "right"
                        | "top"
                        | "bottom"
                        | "center";
                    }
                    | { frame: LayoutFrame }
                    | { frameSizeNeeded: number; minFrameSize: number }
                    | { max: number; min: number; wanted: number }
                    | { problemEdgeCoordinates: number[] }
                    | { id: string; side: "left" | "right" | "top" | "bottom" }
                    | { frame: string }
                    | { frame: LayoutFrame }
                    | { frame: LayoutFrame }
                    | { frame: LayoutFrame }
                    | { frame: LayoutFrame },
                >
                | { code: string; message: string };
            startX: number;
            startY: number;
        }
      • frames: LayoutFrame[]

      Returns void

    • Called when an action is cancelled. Call action specific onCancel hooks here.

      Parameters

      • e: PointerEvent | KeyboardEvent | undefined
      • state: MoveState

      Returns void

    • Should return true if it should handle the "request"/event (e.g. some modifier is being pressed => user is requesting x action).

      The user is not necessarily dragging at this point, though they might also change actions mid drag. So it does not necessarily mean the event is allowed.

      Here is where you should initiate your state. Don't allow the action by default unless it can always be allowed.

      Event might be undefined if it was initiated manually via moveStart without an event.

      Parameters

      • e: PointerEvent | KeyboardEvent | undefined
      • state: MoveState
      • forceRecalculateEdges: () => void

      Returns boolean

    • The action handler will call this regardless of whether the action is active or not.

      Can be used by actions to return display hints.

      Actions should keep the state of the hints locally and update them in canHandlerRequest/onDrag*, etc. and only use this to return the state of the actions, not update them as that could become expensive.

      Parameters

      • type: "start" | "move" | "end"

      Returns { actions?: string[]; errors?: string[] }

      • Optionalactions?: string[]

        Hint texts to display regarding the action state/usage. Undefined means no hint.

      • Optionalerrors?: string[]

        Error texts/hints to display when the action produces an error.