{"version":3,"file":"LayoutManager.min.mjs","names":[],"sources":["../../../src/LayoutManager/LayoutManager.ts"],"sourcesContent":["import { Point } from '../Point';\nimport {\n  CENTER,\n  CHANGED,\n  MODIFIED,\n  MODIFY_PATH,\n  MODIFY_POLY,\n  MOVING,\n  RESIZING,\n  ROTATING,\n  SCALING,\n  SKEWING,\n  iMatrix,\n} from '../constants';\nimport type { Group } from '../shapes/Group';\nimport type { FabricObject } from '../shapes/Object/FabricObject';\nimport { invertTransform } from '../util/misc/matrix';\nimport { resolveOrigin } from '../util/misc/resolveOrigin';\nimport { FitContentLayout } from './LayoutStrategies/FitContentLayout';\nimport type { LayoutStrategy } from './LayoutStrategies/LayoutStrategy';\nimport {\n  LAYOUT_TYPE_INITIALIZATION,\n  LAYOUT_TYPE_ADDED,\n  LAYOUT_TYPE_REMOVED,\n  LAYOUT_TYPE_IMPERATIVE,\n  LAYOUT_TYPE_OBJECT_MODIFIED,\n  LAYOUT_TYPE_OBJECT_MODIFYING,\n} from './constants';\nimport type {\n  LayoutContext,\n  LayoutResult,\n  RegistrationContext,\n  StrictLayoutContext,\n} from './types';\nimport { classRegistry } from '../ClassRegistry';\nimport type { TModificationEvents } from '../EventTypeDefs';\n\nconst LAYOUT_MANAGER = 'layoutManager';\n\nexport type SerializedLayoutManager = {\n  type: string;\n  strategy: string;\n};\n\nexport class LayoutManager {\n  declare private _prevLayoutStrategy?: LayoutStrategy;\n  declare protected _subscriptions: Map<FabricObject, VoidFunction[]>;\n\n  strategy: LayoutStrategy;\n\n  constructor(strategy: LayoutStrategy = new FitContentLayout()) {\n    this.strategy = strategy;\n    this._subscriptions = new Map();\n  }\n\n  public performLayout(context: LayoutContext) {\n    const strictContext: StrictLayoutContext = {\n      bubbles: true,\n      strategy: this.strategy,\n      ...context,\n      prevStrategy: this._prevLayoutStrategy,\n      stopPropagation() {\n        this.bubbles = false;\n      },\n    };\n\n    this.onBeforeLayout(strictContext);\n\n    const layoutResult = this.getLayoutResult(strictContext);\n    if (layoutResult) {\n      this.commitLayout(strictContext, layoutResult);\n    }\n\n    this.onAfterLayout(strictContext, layoutResult);\n    this._prevLayoutStrategy = strictContext.strategy;\n  }\n\n  /**\n   * Attach handlers for events that we know will invalidate the layout when\n   * performed on child objects ( general transforms ).\n   * Returns the disposers for later unsubscribing and cleanup\n   * @param {FabricObject} object\n   * @param {RegistrationContext & Partial<StrictLayoutContext>} context\n   * @returns {VoidFunction[]} disposers remove the handlers\n   */\n  protected attachHandlers(\n    object: FabricObject,\n    context: RegistrationContext & Partial<StrictLayoutContext>,\n  ): VoidFunction[] {\n    const { target } = context;\n    return (\n      [\n        MODIFIED,\n        MOVING,\n        RESIZING,\n        ROTATING,\n        SCALING,\n        SKEWING,\n        CHANGED,\n        MODIFY_POLY,\n        MODIFY_PATH,\n      ] as (TModificationEvents & 'modified')[]\n    ).map((key) =>\n      object.on(key, (e) =>\n        this.performLayout(\n          key === MODIFIED\n            ? {\n                type: LAYOUT_TYPE_OBJECT_MODIFIED,\n                trigger: key,\n                e,\n                target,\n              }\n            : {\n                type: LAYOUT_TYPE_OBJECT_MODIFYING,\n                trigger: key,\n                e,\n                target,\n              },\n        ),\n      ),\n    );\n  }\n\n  /**\n   * Subscribe an object to transform events that will trigger a layout change on the parent\n   * This is important only for interactive groups.\n   * @param object\n   * @param context\n   */\n  protected subscribe(\n    object: FabricObject,\n    context: RegistrationContext & Partial<StrictLayoutContext>,\n  ) {\n    this.unsubscribe(object, context);\n    const disposers = this.attachHandlers(object, context);\n    this._subscriptions.set(object, disposers);\n  }\n\n  /**\n   * unsubscribe object layout triggers\n   */\n  protected unsubscribe(\n    object: FabricObject,\n    _context?: RegistrationContext & Partial<StrictLayoutContext>,\n  ) {\n    (this._subscriptions.get(object) || []).forEach((d) => d());\n    this._subscriptions.delete(object);\n  }\n\n  unsubscribeTargets(\n    context: RegistrationContext & Partial<StrictLayoutContext>,\n  ) {\n    context.targets.forEach((object) => this.unsubscribe(object, context));\n  }\n\n  subscribeTargets(\n    context: RegistrationContext & Partial<StrictLayoutContext>,\n  ) {\n    context.targets.forEach((object) => this.subscribe(object, context));\n  }\n\n  protected onBeforeLayout(context: StrictLayoutContext) {\n    const { target, type } = context;\n    const { canvas } = target;\n    // handle layout triggers subscription\n    // @TODO: gate the registration when the group is interactive\n    if (type === LAYOUT_TYPE_INITIALIZATION || type === LAYOUT_TYPE_ADDED) {\n      this.subscribeTargets(context);\n    } else if (type === LAYOUT_TYPE_REMOVED) {\n      this.unsubscribeTargets(context);\n    }\n    // fire layout event (event will fire only for layouts after initialization layout)\n    target.fire('layout:before', {\n      context,\n    });\n    canvas &&\n      canvas.fire('object:layout:before', {\n        target,\n        context,\n      });\n\n    if (type === LAYOUT_TYPE_IMPERATIVE && context.deep) {\n      const { strategy: _, ...tricklingContext } = context;\n      // traverse the tree\n      target.forEachObject(\n        (object) =>\n          (object as Group).layoutManager &&\n          (object as Group).layoutManager.performLayout({\n            ...tricklingContext,\n            bubbles: false,\n            target: object as Group,\n          }),\n      );\n    }\n  }\n\n  protected getLayoutResult(\n    context: StrictLayoutContext,\n  ): Required<LayoutResult> | undefined {\n    const { target, strategy, type } = context;\n\n    const result = strategy.calcLayoutResult(context, target.getObjects());\n\n    if (!result) {\n      return;\n    }\n\n    const prevCenter =\n      type === LAYOUT_TYPE_INITIALIZATION\n        ? new Point()\n        : target.getRelativeCenterPoint();\n\n    const {\n      center: nextCenter,\n      correction = new Point(),\n      relativeCorrection = new Point(),\n    } = result;\n    const offset = prevCenter\n      .subtract(nextCenter)\n      .add(correction)\n      .transform(\n        // in `initialization` we do not account for target's transformation matrix\n        type === LAYOUT_TYPE_INITIALIZATION\n          ? iMatrix\n          : invertTransform(target.calcOwnMatrix()),\n        true,\n      )\n      .add(relativeCorrection);\n\n    return {\n      result,\n      prevCenter,\n      nextCenter,\n      offset,\n    };\n  }\n\n  protected commitLayout(\n    context: StrictLayoutContext,\n    layoutResult: Required<LayoutResult>,\n  ) {\n    const { target } = context;\n    const {\n      result: { size },\n      nextCenter,\n    } = layoutResult;\n    // set dimensions\n    target.set({ width: size.x, height: size.y });\n    // layout descendants\n    this.layoutObjects(context, layoutResult);\n    //  set position\n    // in `initialization` we do not account for target's transformation matrix\n    if (context.type === LAYOUT_TYPE_INITIALIZATION) {\n      // TODO: what about strokeWidth?\n      target.set({\n        left:\n          context.x ?? nextCenter.x + size.x * resolveOrigin(target.originX),\n        top: context.y ?? nextCenter.y + size.y * resolveOrigin(target.originY),\n      });\n    } else {\n      target.setPositionByOrigin(nextCenter, CENTER, CENTER);\n      // invalidate\n      target.setCoords();\n      target.set('dirty', true);\n    }\n  }\n\n  protected layoutObjects(\n    context: StrictLayoutContext,\n    layoutResult: Required<LayoutResult>,\n  ) {\n    const { target } = context;\n    //  adjust objects to account for new center\n    target.forEachObject((object) => {\n      object.group === target &&\n        this.layoutObject(context, layoutResult, object);\n    });\n    // adjust clip path to account for new center\n    context.strategy.shouldLayoutClipPath(context) &&\n      this.layoutObject(context, layoutResult, target.clipPath as FabricObject);\n  }\n\n  /**\n   * @param {FabricObject} object\n   * @param {Point} offset\n   */\n  protected layoutObject(\n    context: StrictLayoutContext,\n    { offset }: Required<LayoutResult>,\n    object: FabricObject,\n  ) {\n    // TODO: this is here for cache invalidation.\n    // verify if this is necessary since we have explicit\n    // cache invalidation at the end of commitLayout\n    object.set({\n      left: object.left + offset.x,\n      top: object.top + offset.y,\n    });\n  }\n\n  protected onAfterLayout(\n    context: StrictLayoutContext,\n    layoutResult?: LayoutResult,\n  ) {\n    const {\n      target,\n      strategy,\n      bubbles,\n      prevStrategy: _,\n      ...bubblingContext\n    } = context;\n    const { canvas } = target;\n\n    //  fire layout event (event will fire only for layouts after initialization layout)\n    target.fire('layout:after', {\n      context,\n      result: layoutResult,\n    });\n    canvas &&\n      canvas.fire('object:layout:after', {\n        context,\n        result: layoutResult,\n        target,\n      });\n\n    //  bubble\n    const parent = target.parent;\n    if (bubbles && parent?.layoutManager) {\n      //  add target to context#path\n      (bubblingContext.path || (bubblingContext.path = [])).push(target);\n      //  all parents should invalidate their layout\n      parent.layoutManager.performLayout({\n        ...bubblingContext,\n        target: parent,\n      });\n    }\n    target.set('dirty', true);\n  }\n\n  dispose() {\n    const { _subscriptions } = this;\n    _subscriptions.forEach((disposers) => disposers.forEach((d) => d()));\n    _subscriptions.clear();\n  }\n\n  toObject() {\n    return {\n      type: LAYOUT_MANAGER,\n      strategy: (this.strategy.constructor as typeof LayoutStrategy).type,\n    };\n  }\n\n  toJSON() {\n    return this.toObject();\n  }\n}\n\nclassRegistry.setClass(LayoutManager, LAYOUT_MANAGER);\n"],"mappings":"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"}