{"version":3,"file":"useTimelineTrackGeometry.mjs","names":[],"sources":["../../../src/hooks/tracks/useTimelineTrackGeometry.ts"],"sourcesContent":["import { useTimelineEngine } from '#react/hooks/core/useTimelineEngine';\nimport { defaultTimelineInteractionGeometry } from '@techsquidtv/canvas-timeline-core';\nimport type {\n  TimelineEngine,\n  TimelineStateSnapshot,\n  TimelineTrackGeometryOptions,\n  TimelineTrackRect,\n} from '@techsquidtv/canvas-timeline-core';\nimport { useCallback, useMemo, useSyncExternalStore } from 'react';\n\nconst stores = new WeakMap<TimelineEngine, Map<string, ReturnType<typeof createStore>>>();\n\nfunction createStore(\n  engine: TimelineEngine,\n  options: TimelineTrackGeometryOptions,\n  release: () => void,\n  retain: () => void\n) {\n  let previous: TimelineStateSnapshot | undefined;\n  let rectangles = new Map<string, Readonly<TimelineTrackRect>>();\n  const listeners = new Set<() => void>();\n  let unsubscribe: (() => void)[] = [];\n  const update = () => {\n    const state = engine.getState();\n    if (\n      previous &&\n      state.scrollTop === previous.scrollTop &&\n      state.viewportWidth === previous.viewportWidth &&\n      state.tracks.length === previous.tracks.length &&\n      state.tracks.every((track, index) => {\n        const prior = previous?.tracks[index];\n        return (\n          track.id === prior?.id &&\n          track.height === prior.height &&\n          track.collapsed === prior.collapsed\n        );\n      })\n    ) {\n      previous = state;\n      return;\n    }\n    previous = state;\n    rectangles = new Map(\n      engine.geometry.getTrackRects(options).map((rect) => {\n        const prior = rectangles.get(rect.trackId);\n        return [\n          rect.trackId,\n          prior &&\n          (Object.keys(rect) as (keyof TimelineTrackRect)[]).every(\n            (key) => rect[key] === prior[key]\n          )\n            ? prior\n            : Object.freeze(rect),\n        ];\n      })\n    );\n    listeners.forEach((listener) => listener());\n  };\n  update();\n  return {\n    get: (trackId: string) => rectangles.get(trackId) ?? null,\n    subscribe: (listener: () => void) => {\n      if (listeners.size === 0) {\n        retain();\n        unsubscribe = [engine.on('state:settled', update), engine.on('content:change', update)];\n        update();\n      }\n      listeners.add(listener);\n      return () => {\n        listeners.delete(listener);\n        if (listeners.size === 0) {\n          unsubscribe.forEach((dispose) => dispose());\n          release();\n        }\n      };\n    },\n  };\n}\n\n/** Shares row geometry across consumers using the same normalized layout options. */\nexport function useTimelineTrackGeometry(trackId: string, options: TimelineTrackGeometryOptions) {\n  const engine = useTimelineEngine();\n  const rulerHeight = options.rulerHeight ?? defaultTimelineInteractionGeometry.rulerHeight;\n  const trackHeight = options.trackHeight ?? defaultTimelineInteractionGeometry.trackHeight;\n  const collapsedTrackHeight =\n    options.collapsedTrackHeight ?? defaultTimelineInteractionGeometry.collapsedTrackHeight;\n  const viewportWidth = options.viewportWidth;\n  const store = useMemo(() => {\n    let engineStores = stores.get(engine);\n    if (!engineStores) {\n      engineStores = new Map();\n      stores.set(engine, engineStores);\n    }\n    const key = [rulerHeight, trackHeight, collapsedTrackHeight, viewportWidth ?? 'auto'].join(':');\n    let result = engineStores.get(key);\n    if (!result) {\n      result = createStore(\n        engine,\n        { rulerHeight, trackHeight, collapsedTrackHeight, viewportWidth },\n        () => {\n          if (engineStores.get(key) === result) {\n            engineStores.delete(key);\n          }\n        },\n        () => {\n          if (result) {\n            engineStores.set(key, result);\n          }\n        }\n      );\n      engineStores.set(key, result);\n    }\n    return result;\n  }, [engine, rulerHeight, trackHeight, collapsedTrackHeight, viewportWidth]);\n  const getSnapshot = useCallback(() => store.get(trackId), [store, trackId]);\n  return useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);\n}\n"],"mappings":";;;;AAUA,MAAM,yBAAS,IAAI,QAAqE;AAExF,SAAS,YACP,QACA,SACA,SACA,QACA;CACA,IAAI;CACJ,IAAI,6BAAa,IAAI,IAAyC;CAC9D,MAAM,4BAAY,IAAI,IAAgB;CACtC,IAAI,cAA8B,CAAC;CACnC,MAAM,eAAe;EACnB,MAAM,QAAQ,OAAO,SAAS;EAC9B,IACE,YACA,MAAM,cAAc,SAAS,aAC7B,MAAM,kBAAkB,SAAS,iBACjC,MAAM,OAAO,WAAW,SAAS,OAAO,UACxC,MAAM,OAAO,OAAO,OAAO,UAAU;GACnC,MAAM,QAAQ,UAAU,OAAO;GAC/B,OACE,MAAM,OAAO,OAAO,MACpB,MAAM,WAAW,MAAM,UACvB,MAAM,cAAc,MAAM;EAE9B,CAAC,GACD;GACA,WAAW;GACX;EACF;EACA,WAAW;EACX,aAAa,IAAI,IACf,OAAO,SAAS,cAAc,OAAO,CAAC,CAAC,KAAK,SAAS;GACnD,MAAM,QAAQ,WAAW,IAAI,KAAK,OAAO;GACzC,OAAO,CACL,KAAK,SACL,SACC,OAAO,KAAK,IAAI,CAAC,CAAiC,OAChD,QAAQ,KAAK,SAAS,MAAM,IAC/B,IACI,QACA,OAAO,OAAO,IAAI,CACxB;EACF,CAAC,CACH;EACA,UAAU,SAAS,aAAa,SAAS,CAAC;CAC5C;CACA,OAAO;CACP,OAAO;EACL,MAAM,YAAoB,WAAW,IAAI,OAAO,KAAK;EACrD,YAAY,aAAyB;GACnC,IAAI,UAAU,SAAS,GAAG;IACxB,OAAO;IACP,cAAc,CAAC,OAAO,GAAG,iBAAiB,MAAM,GAAG,OAAO,GAAG,kBAAkB,MAAM,CAAC;IACtF,OAAO;GACT;GACA,UAAU,IAAI,QAAQ;GACtB,aAAa;IACX,UAAU,OAAO,QAAQ;IACzB,IAAI,UAAU,SAAS,GAAG;KACxB,YAAY,SAAS,YAAY,QAAQ,CAAC;KAC1C,QAAQ;IACV;GACF;EACF;CACF;AACF;;AAGA,SAAgB,yBAAyB,SAAiB,SAAuC;CAC/F,MAAM,SAAS,kBAAkB;CACjC,MAAM,cAAc,QAAQ,eAAe,mCAAmC;CAC9E,MAAM,cAAc,QAAQ,eAAe,mCAAmC;CAC9E,MAAM,uBACJ,QAAQ,wBAAwB,mCAAmC;CACrE,MAAM,gBAAgB,QAAQ;CAC9B,MAAM,QAAQ,cAAc;EAC1B,IAAI,eAAe,OAAO,IAAI,MAAM;EACpC,IAAI,CAAC,cAAc;GACjB,+BAAe,IAAI,IAAI;GACvB,OAAO,IAAI,QAAQ,YAAY;EACjC;EACA,MAAM,MAAM;GAAC;GAAa;GAAa;GAAsB,iBAAiB;EAAM,CAAC,CAAC,KAAK,GAAG;EAC9F,IAAI,SAAS,aAAa,IAAI,GAAG;EACjC,IAAI,CAAC,QAAQ;GACX,SAAS,YACP,QACA;IAAE;IAAa;IAAa;IAAsB;GAAc,SAC1D;IACJ,IAAI,aAAa,IAAI,GAAG,MAAM,QAC5B,aAAa,OAAO,GAAG;GAE3B,SACM;IACJ,IAAI,QACF,aAAa,IAAI,KAAK,MAAM;GAEhC,CACF;GACA,aAAa,IAAI,KAAK,MAAM;EAC9B;EACA,OAAO;CACT,GAAG;EAAC;EAAQ;EAAa;EAAa;EAAsB;CAAa,CAAC;CAC1E,MAAM,cAAc,kBAAkB,MAAM,IAAI,OAAO,GAAG,CAAC,OAAO,OAAO,CAAC;CAC1E,OAAO,qBAAqB,MAAM,WAAW,aAAa,WAAW;AACvE"}