fabric
Version:
Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.
1 lines • 15.3 kB
Source Map (JSON)
{"version":3,"file":"LayoutManager.min.mjs","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 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 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"],"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","MODIFIED","MOVING","RESIZING","ROTATING","SCALING","SKEWING","CHANGED","MODIFY_POLY","MODIFY_PATH","map","key","on","e","type","LAYOUT_TYPE_OBJECT_MODIFIED","trigger","LAYOUT_TYPE_OBJECT_MODIFYING","subscribe","unsubscribe","disposers","set","_context","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":"o4BAqCMA,EAAiB,gBAOhB,MAAMC,EAMXC,WAAAA,GAA+D,IAAnDC,EAAwBC,UAAAC,OAAAD,QAAAE,IAAAF,UAAAE,GAAAF,UAAA,GAAG,IAAIG,EAAkBC,EAAAC,KAAA,gBAAA,GAC3DA,KAAKN,SAAWA,EAChBM,KAAKC,eAAiB,IAAIC,GAC5B,CAEOC,aAAAA,CAAcC,GACnB,MAAMC,EAAkCC,EAAAA,EAAA,CACtCC,SAAS,EACTb,SAAUM,KAAKN,UACZU,GAAO,GAAA,CACVI,aAAcR,KAAKS,oBACnBC,eAAAA,GACEV,KAAKO,SAAU,CACjB,IAGFP,KAAKW,eAAeN,GAEpB,MAAMO,EAAeZ,KAAKa,gBAAgBR,GACtCO,GACFZ,KAAKc,aAAaT,EAAeO,GAGnCZ,KAAKe,cAAcV,EAAeO,GAClCZ,KAAKS,oBAAsBJ,EAAcX,QAC3C,CAUUsB,cAAAA,CACRC,EACAb,GAEA,MAAMc,OAAEA,GAAWd,EACnB,MACE,CACEe,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,EACAC,GAEFC,KAAKC,GACLZ,EAAOa,GAAGD,GAAME,GACd/B,KAAKG,cACH0B,IAAQV,EACJ,CACEa,KAAMC,EACNC,QAASL,EACTE,IACAb,UAEF,CACEc,KAAMG,EACND,QAASL,EACTE,IACAb,cAKd,CAQUkB,SAAAA,CACRnB,EACAb,GAEAJ,KAAKqC,YAAYpB,EAAQb,GACzB,MAAMkC,EAAYtC,KAAKgB,eAAeC,EAAQb,GAC9CJ,KAAKC,eAAesC,IAAItB,EAAQqB,EAClC,CAKUD,WAAAA,CACRpB,EACAuB,IAECxC,KAAKC,eAAewC,IAAIxB,IAAW,IAAIyB,SAASC,GAAMA,MACvD3C,KAAKC,eAAe2C,OAAO3B,EAC7B,CAEA4B,kBAAAA,CACEzC,GAEAA,EAAQ0C,QAAQJ,SAASzB,GAAWjB,KAAKqC,YAAYpB,EAAQb,IAC/D,CAEA2C,gBAAAA,CACE3C,GAEAA,EAAQ0C,QAAQJ,SAASzB,GAAWjB,KAAKoC,UAAUnB,EAAQb,IAC7D,CAEUO,cAAAA,CAAeP,GACvB,MAAMc,OAAEA,EAAMc,KAAEA,GAAS5B,GACnB4C,OAAEA,GAAW9B,EAkBnB,GAfIc,IAASiB,GAA8BjB,IAASkB,EAClDlD,KAAK+C,iBAAiB3C,GACb4B,IAASmB,GAClBnD,KAAK6C,mBAAmBzC,GAG1Bc,EAAOkC,KAAK,gBAAiB,CAC3BhD,YAEF4C,GACEA,EAAOI,KAAK,uBAAwB,CAClClC,SACAd,YAGA4B,IAASqB,GAA0BjD,EAAQkD,KAAM,CAC7C,MAAkBC,EAAgBC,EAAKpD,EAAOqD,GAEpDvC,EAAOwC,eACJzC,GACEA,EAAiB0C,eACjB1C,EAAiB0C,cAAcxD,cAAaG,EAAAA,KACxCiD,GAAgB,CAAA,EAAA,CACnBhD,SAAS,EACTW,OAAQD,MAGhB,CACF,CAEUJ,eAAAA,CACRT,GAEA,MAAMc,OAAEA,EAAMxB,SAAEA,EAAQsC,KAAEA,GAAS5B,EAE7BwD,EAASlE,EAASmE,iBAAiBzD,EAASc,EAAO4C,cAEzD,IAAKF,EACH,OAGF,MAAMG,EACJ/B,IAASiB,EACL,IAAIe,EACJ9C,EAAO+C,0BAGXC,OAAQC,EAAUC,WAClBA,EAAa,IAAIJ,EAAOK,mBACxBA,EAAqB,IAAIL,GACvBJ,EACEU,EAASP,EACZQ,SAASJ,GACTK,IAAIJ,GACJK,UAECzC,IAASiB,EACLyB,EACAC,EAAgBzD,EAAO0D,kBAC3B,GAEDJ,IAAIH,GAEP,MAAO,CACLT,SACAG,aACAI,aACAG,SAEJ,CAEUxD,YAAAA,CACRV,EACAQ,GAEA,MAAMM,OAAEA,GAAWd,GAEjBwD,QAAQiB,KAAEA,GAAMV,WAChBA,GACEvD,EAO6C,IAAAkE,EAAAC,GALjD7D,EAAOqB,IAAI,CAAEyC,MAAOH,EAAKI,EAAGC,OAAQL,EAAKM,IAEzCnF,KAAKoF,cAAchF,EAASQ,GAGxBR,EAAQ4B,OAASiB,GAEnB/B,EAAOqB,IAAI,CACT8C,KACWP,QADPA,EACF1E,EAAQ6E,SAACH,IAAAA,EAAAA,EAAIX,EAAWc,EAAIJ,EAAKI,EAAIK,EAAcpE,EAAOqE,SAC5DC,IAAc,QAAXT,EAAE3E,EAAQ+E,SAAC,IAAAJ,EAAAA,EAAIZ,EAAWgB,EAAIN,EAAKM,EAAIG,EAAcpE,EAAOuE,YAGjEvE,EAAOwE,oBAAoBvB,EAAYwB,EAAQA,GAE/CzE,EAAO0E,YACP1E,EAAOqB,IAAI,SAAS,GAExB,CAEU6C,aAAAA,CACRhF,EACAQ,GAEA,MAAMM,OAAEA,GAAWd,EAEnBc,EAAOwC,eAAezC,IACpBA,EAAO4E,QAAU3E,GACflB,KAAK8F,aAAa1F,EAASQ,EAAcK,EAAO,IAGpDb,EAAQV,SAASqG,qBAAqB3F,IACpCJ,KAAK8F,aAAa1F,EAASQ,EAAcM,EAAO8E,SACpD,CAMUF,YAAAA,CACR1F,EAA4B6F,EAE5BhF,GACA,IAFAqD,OAAEA,GAAgC2B,EAMlChF,EAAOsB,IAAI,CACT8C,KAAMpE,EAAOoE,KAAOf,EAAOW,EAC3BO,IAAKvE,EAAOuE,IAAMlB,EAAOa,GAE7B,CAEUpE,aAAAA,CACRX,EACAQ,GAEA,MAAMM,OACJA,EAAMxB,SACNA,EAAQa,QACRA,EACAC,aAAc0F,GAEZ9F,EADC+F,EAAe3C,EAChBpD,EAAOgG,IACLpD,OAAEA,GAAW9B,EAGnBA,EAAOkC,KAAK,eAAgB,CAC1BhD,UACAwD,OAAQhD,IAEVoC,GACEA,EAAOI,KAAK,sBAAuB,CACjChD,UACAwD,OAAQhD,EACRM,WAIJ,MAAMmF,EAASnF,EAAOmF,OAClB9F,SAAW8F,GAAAA,EAAQ1C,iBAEpBwC,EAAgBG,OAASH,EAAgBG,KAAO,KAAKC,KAAKrF,GAE3DmF,EAAO1C,cAAcxD,cAAaG,EAAAA,EAAA,GAC7B6F,GAAe,GAAA,CAClBjF,OAAQmF,MAGZnF,EAAOqB,IAAI,SAAS,EACtB,CAEAiE,OAAAA,GACE,MAAMvG,eAAEA,GAAmBD,KAC3BC,EAAeyC,SAASJ,GAAcA,EAAUI,SAASC,GAAMA,QAC/D1C,EAAewG,OACjB,CAEAC,QAAAA,GACE,MAAO,CACL1E,KAAMzC,EACNG,SAAWM,KAAKN,SAASD,YAAsCuC,KAEnE,CAEA2E,MAAAA,GACE,OAAO3G,KAAK0G,UACd,EAGFE,EAAcC,SAASrH,EAAeD"}