{"version":3,"file":"LayoutManager.min.mjs","sources":["../../../src/LayoutManager/LayoutManager.ts"],"sourcesContent":["import { Point } from '../Point';\nimport { CENTER, iMatrix } 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  private declare _prevLayoutStrategy?: LayoutStrategy;\n  protected declare _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        'modifyPoly',\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 } = context;\n\n    const result = context.strategy.calcLayoutResult(\n      context,\n      target.getObjects()\n    );\n\n    if (!result) {\n      return;\n    }\n\n    const prevCenter =\n      context.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        context.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    this._subscriptions.forEach((disposers) => disposers.forEach((d) => d()));\n    this._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"],"names":["LAYOUT_MANAGER","LayoutManager","constructor","strategy","arguments","length","undefined","FitContentLayout","_defineProperty","this","_subscriptions","Map","performLayout","context","strictContext","_objectSpread","bubbles","prevStrategy","_prevLayoutStrategy","stopPropagation","onBeforeLayout","layoutResult","getLayoutResult","commitLayout","onAfterLayout","attachHandlers","object","target","map","key","on","e","type","LAYOUT_TYPE_OBJECT_MODIFIED","trigger","LAYOUT_TYPE_OBJECT_MODIFYING","subscribe","unsubscribe","disposers","set","get","forEach","d","delete","unsubscribeTargets","targets","subscribeTargets","canvas","LAYOUT_TYPE_INITIALIZATION","LAYOUT_TYPE_ADDED","LAYOUT_TYPE_REMOVED","fire","LAYOUT_TYPE_IMPERATIVE","deep","tricklingContext","_objectWithoutProperties","_excluded","forEachObject","layoutManager","result","calcLayoutResult","getObjects","prevCenter","Point","getRelativeCenterPoint","center","nextCenter","correction","relativeCorrection","offset","subtract","add","transform","iMatrix","invertTransform","calcOwnMatrix","size","_context$x","_context$y","width","x","height","y","layoutObjects","left","resolveOrigin","originX","top","originY","setPositionByOrigin","CENTER","setCoords","group","layoutObject","shouldLayoutClipPath","clipPath","_ref","_","bubblingContext","_excluded2","parent","path","push","dispose","clear","toObject","toJSON","classRegistry","setClass"],"mappings":"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"}