@hh.ru/magritte-ui-nav-bar 1.3.5 → 1.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"MetricsProvider.js","sources":["../../src/internal/MetricsProvider.tsx"],"sourcesContent":["import {\n createContext,\n forwardRef,\n useCallback,\n useContext,\n useLayoutEffect,\n useRef,\n type MutableRefObject,\n type DependencyList,\n type HTMLAttributes,\n type PropsWithChildren,\n type RefCallback,\n type RefObject,\n} from 'react';\n\nimport {\n scheduleGatherMetrics,\n scheduleMicro,\n useActualRef,\n useInitOnce,\n} from '@hh.ru/magritte-ui-nav-bar/internal/utils';\n\n/**\n * Функция-подписчик, которая получает массив измерений для заранее\n * зарегистрированного набора элементов (порядок соответствует зарегистрированному).\n */\ntype MeasureFn = (elementMetrics: DOMRect) => void;\n\n/**\n * Хук измерения: подписывает callback на изменения размеров указанных элементов\n * и возвращает функцию ручного «принудительного» переизмерения (remeasure).\n *\n * @param elements Список RefObject’ов элементов, размеры которых нужно измерять.\n * @param measureFn Коллбек, получающий массив DOMRect при измерении.\n * @param deps Зависимости для эффекта подписки (аналогичны deps useEffect).\n * @returns Функция remeasure(), которая помечает элементы «грязными» и запускает измерение.\n */\nexport interface UseMeasure {\n (\n elements: RefObject<Element | null> | MutableRefObject<Element | null | undefined>,\n measureFn: MeasureFn,\n deps?: DependencyList\n ): VoidFunction;\n}\n\n/**\n * Реестр подписок на измерения размеров элементов.\n * Хранит двунаправные сопоставления «элемент → подписчики» и «подписчик → элементы»,\n * кеширует DOMRect для элементов в текущем тике и поддерживает список «грязных» элементов.\n *\n * Внешний код вызывает:\n * - register/unregister для подписки/отписки,\n * - markDirty/consumeDirty для управления очередью измерений,\n * - subscribersFor/elementFor для получения связей.\n */\nexport class MeasureRegistry {\n private elementsToSubs = new Map<Element, MeasureFn>();\n private subsToElements = new Map<MeasureFn, Element>();\n private dirty = new Set<Element>();\n isDirty = false;\n\n register(element: Element, fn: MeasureFn): boolean {\n this.subsToElements.set(fn, element);\n const isFirstTimeSub = !this.elementsToSubs.has(element);\n const prevSub = this.elementsToSubs.get(element);\n this.elementsToSubs.set(element, fn);\n if (prevSub && prevSub !== fn) {\n this.subsToElements.delete(prevSub);\n }\n return isFirstTimeSub;\n }\n\n unregister(element: Element): void {\n const fn = this.elementsToSubs.get(element);\n if (fn) {\n this.subsToElements.delete(fn);\n this.elementsToSubs.delete(element);\n }\n }\n markDirty(element: Element): void {\n if (this.elementsToSubs.has(element)) {\n this.dirty.add(element);\n this.isDirty = true;\n }\n }\n\n markAllDirty(): void {\n for (const element of this.elementsToSubs.keys()) {\n this.dirty.add(element);\n }\n this.isDirty = true;\n }\n\n consumeDirty(): Element[] {\n const list = [...this.dirty];\n this.dirty.clear();\n this.isDirty = false;\n return list;\n }\n\n elementFor(fn: MeasureFn): Element | null {\n return this.subsToElements.get(fn) ?? null;\n }\n\n subscribersFor(elements: Element[]): Set<MeasureFn> {\n const subs = new Set<MeasureFn>();\n for (const el of elements) {\n const fn = this.elementsToSubs.get(el);\n if (fn) {\n subs.add(fn);\n }\n }\n return subs;\n }\n\n listAllElements(): Element[] {\n return [...this.elementsToSubs.keys()];\n }\n}\n\nexport interface MetricsContextValue {\n useMeasureAuto: UseMeasure;\n useMeasureManual: UseMeasure;\n useMeasureFingerprint: UseMeasure;\n measureAllManual: VoidFunction;\n}\n\n/**\n * Контекст с API для измерений:\n * - useMeasureAuto: авто-режим (измерения по ResizeObserver + ручные remeasure);\n * - useMeasureManual: ручной режим (только по вызову remeasure);\n * - measureAllManual: принудительное переизмерение всех элементов ручного реестра.\n */\nconst MetricsContext = createContext<MetricsContextValue | null>(null);\n\n/**\n * Провайдер измерений. Оборачивает subtree и предоставляет через контекст:\n * - два хука подписки (авто/ручной),\n * - функцию «переизмерить всё» для ручного реестра.\n *\n * Для корректных измерений при необходимости временно навешивает CSS-класс\n * (measureClassName) на корневой div — полезно для временного отключения\n * transform/overflow и т.п., чтобы getBoundingClientRect() отдавал «чистые» размеры.\n *\n * @example\n * <MetricsProvider measureClassName=\"no-transform\">\n * ...\n * </MetricsProvider>\n */\nexport const MetricsProvider = forwardRef<\n HTMLDivElement,\n PropsWithChildren<{ measureClassName?: string } & HTMLAttributes<HTMLDivElement>>\n>(({ children, measureClassName, ...rest }, ref) => {\n const classNameRef = useActualRef(measureClassName);\n const rootRef = useRef<HTMLDivElement | null>();\n const inMeasureCycleRef = useRef(false);\n const callbacksCallCache = useInitOnce(() => new Set<MeasureFn>());\n const observerInstanceRef = useRef<ResizeObserver | null>(null);\n\n // Пробрасываем внешний ref и локально храним DOM-узел.\n const rootRefCallback = useCallback<RefCallback<HTMLDivElement>>(\n (element: HTMLDivElement | null) => {\n rootRef.current = element;\n if (typeof ref === 'function') {\n ref(element);\n } else if (ref) {\n ref.current = element;\n }\n },\n [ref]\n );\n // Текущее состояние «включён ли класс для корректного измерения».\n const classStateRef = useRef(false);\n\n // Реестры: отдельный для авто-подписок (RO) и для ручных подписок.\n const manualSubs = useInitOnce(() => new MeasureRegistry());\n const autoSubs = useInitOnce(() => new MeasureRegistry());\n const fingerprintSubs = useInitOnce(() => new MeasureRegistry());\n\n /**\n * Включает/выключает CSS-класс для контейнера (например, чтобы на время измерения отключить transform).\n * Если включили — автоматически планируем выключение в конец тика (см. disableLater).\n */\n const toggle = (on?: boolean) => {\n if (classStateRef.current === !!on || !rootRef.current || !classNameRef.current) {\n return;\n }\n rootRef.current.classList.toggle(classNameRef.current, !!on);\n classStateRef.current = !!on;\n\n // если навесили класс отключающий трансформации - планируем удаление этого класса на конец тика\n // eslint-disable-next-line @typescript-eslint/no-use-before-define\n !!on && disableLater();\n };\n\n /**\n * Планировщик «снять класс позже»: schedule(toggle) вернёт функцию,\n * которая при вызове поставит toggle(false) в конец очереди микротасок.\n */\n const disableLater = useInitOnce(() => scheduleMicro(toggle));\n const measureDirtyRegistry = useInitOnce(() => (registry: MeasureRegistry) => {\n if (!registry.isDirty) {\n return false;\n }\n\n const dirty = registry.consumeDirty();\n const measureFns = registry.subscribersFor(dirty);\n measureFns.forEach((fn) => {\n if (callbacksCallCache.has(fn)) {\n return;\n }\n callbacksCallCache.add(fn);\n const element = registry.elementFor(fn);\n if (!element) {\n return;\n }\n fn(element.getBoundingClientRect());\n });\n\n return true;\n });\n\n const measureSync = useInitOnce(() => () => {\n /**\n * Во время вызова коллбеков может произойти либо синхронный вызов measureSync, либо асинхронный.\n * Синхронный нужно вызвать сразу, а для асинхронного обычно создается задача в очереди микротасок.\n * Но в случае установки флага inMeasureCycle таска не будет создана, т.к. она не нужна, потому что измерения\n * произойдут в этом же цикле.\n * Чтобы синхронно запущенный вызов не сбросил этот флаг раньше времени мы при вызове функции запоминаем\n * его состояние, и при выходе из него возвращаем его к исходному значению.\n * Получается что при рекурсивном вызове measureSync только самая нижняя в стеке вызовов функция сбросит этот\n * флаг в false.\n */\n const inCycle = inMeasureCycleRef.current;\n inMeasureCycleRef.current = true;\n toggle(true);\n\n // фингерпринты обрабатываем только один раз за цикл, т.к. единственный вариант для этих элементов попасть в\n // список на измерение через ResizeObserver, а он не может добавить новые элементы во время цикла измерения\n measureDirtyRegistry(fingerprintSubs);\n\n // в ручную и авто очередь элементы можно добавить руками с помощью вызова функции, а значит элементы могут\n // добавляться в цикле измерений в коллбеках изменения размера, поэтому обрабатываем их до тех пор пока не\n // перестанут появляться новые элементы\n while (autoSubs.isDirty || manualSubs.isDirty) {\n measureDirtyRegistry(autoSubs);\n measureDirtyRegistry(manualSubs);\n }\n\n if (!inCycle) {\n callbacksCallCache.clear();\n }\n inMeasureCycleRef.current = inCycle;\n });\n\n /**\n * Если вызвано асинхронное измерение, то откладываем его до обработки микротасок, чтобы таска на измерения\n * от ResizeObserver и вызванная вручную схлопнулись в одну.\n * Функция не должна вызываться напрямую, только через runMeasure\n */\n const _scheduledMeasure = useInitOnce(() => scheduleGatherMetrics(measureSync));\n /**\n * Кроме того ручной вызов может произойти в цикле измерений, в этом случае не нужно делать новую таску, достаточно\n * просто пометить инвалидированные размеры, они будут получены (или прочитаны из кеша) в текущем цикле измерений\n */\n const runMeasure = useInitOnce(() => () => {\n if (inMeasureCycleRef.current) {\n return;\n }\n _scheduledMeasure();\n });\n\n const observedElements = useInitOnce(() => new Set<Element>());\n\n /**\n * Общий ResizeObserver: помечает элементы грязными и планирует измерение.\n */\n useLayoutEffect(() => {\n const observer = new ResizeObserver((entries) => {\n const dirtyEntries = entries.map((entry) => entry.target);\n for (const entry of dirtyEntries) {\n autoSubs.markDirty(entry);\n fingerprintSubs.markDirty(entry);\n }\n runMeasure();\n });\n observerInstanceRef.current = observer;\n for (const element of observedElements) {\n observer.observe(element);\n }\n return () => {\n observerInstanceRef.current = null;\n observer.disconnect();\n };\n }, [runMeasure, autoSubs, fingerprintSubs, observedElements]);\n\n /** Отписывает подписчика от авто-реестра и снимает наблюдение, если подписчиков больше нет. */\n const unregister = (registry: MeasureRegistry, element: Element, observe: boolean = true) => {\n registry.unregister(element);\n observedElements.delete(element);\n observe && observerInstanceRef.current?.unobserve(element);\n };\n\n /** Регистрирует подписчика в реестре и опционально подключает наблюдение через ResizeObserver. */\n const register = (registry: MeasureRegistry, element: Element, fn: MeasureFn, observe: boolean = true) => {\n const isFirstSub = registry.register(element, fn);\n observe && observedElements.add(element);\n observe && isFirstSub && observerInstanceRef.current?.observe(element);\n return () => unregister(registry, element, observe);\n };\n\n const createUseMeasure = useInitOnce(\n () =>\n (registry: MeasureRegistry, observe: boolean): UseMeasure =>\n (ref, callback, deps = []) => {\n // eslint-disable-next-line react-hooks/rules-of-hooks\n useLayoutEffect(\n () => (ref.current ? register(registry, ref.current, callback, observe) : void 0),\n // eslint-disable-next-line disable-autofix/react-hooks/exhaustive-deps\n deps\n );\n\n // eslint-disable-next-line react-hooks/rules-of-hooks\n const remeasure: VoidFunction = useCallback(() => {\n if (ref.current) {\n registry.markDirty(ref.current);\n runMeasure();\n }\n }, [ref]);\n\n return remeasure;\n }\n );\n\n /**\n * Возвращает функцию, которая пометит «грязными» все элементы ручного реестра\n * и выполнит синхронное измерение сразу (без ожидания микротасок).\n */\n const measureAllManual = useInitOnce(() => () => {\n manualSubs.markAllDirty();\n measureSync();\n });\n\n const contextValue = useInitOnce(() => ({\n useMeasureAuto: createUseMeasure(autoSubs, true),\n useMeasureManual: createUseMeasure(manualSubs, false),\n useMeasureFingerprint: createUseMeasure(fingerprintSubs, true),\n measureAllManual,\n })) satisfies MetricsContextValue;\n\n return (\n <MetricsContext.Provider value={contextValue}>\n <div {...rest} ref={rootRefCallback}>\n {children}\n </div>\n </MetricsContext.Provider>\n );\n});\n\nMetricsProvider.displayName = 'MetricsProvider';\n\nexport const useMetricsContext = (): MetricsContextValue | null => useContext(MetricsContext);\n\ntype UseMeasureKeys = {\n [K in keyof MetricsContextValue]-?: MetricsContextValue[K] extends UseMeasure ? K : never;\n}[keyof MetricsContextValue];\nconst createHook =\n (name: UseMeasureKeys): UseMeasure =>\n (...args) => {\n const ctx = useContext(MetricsContext);\n if (!ctx) {\n throw new Error('useMeasure* must be used within <MetricsProvider/>');\n }\n return ctx[name](...args);\n };\n\nexport const useMeasureAuto = createHook('useMeasureAuto');\nexport const useMeasureManual = createHook('useMeasureManual');\nexport const useMeasureFingerprint = createHook('useMeasureFingerprint');\n\n/**\n * Хук, возвращающий функцию «переизмерить всё» для подписок зарегистрированных с помощью {@link useMeasureManual}.\n * Полезно, когда нужно одномоментно обновить измерения всех зарегистрированных вручную элементов.\n */\nexport const useRemeasureAllManual: () => VoidFunction = () => {\n const ctx = useContext(MetricsContext);\n if (!ctx) {\n throw new Error('useMeasure* must be used within <MetricsProvider/>');\n }\n return ctx.measureAllManual;\n};\n"],"names":["_jsx"],"mappings":"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"}
1
+ {"version":3,"file":"MetricsProvider.js","sources":["src/internal/MetricsProvider.tsx"],"sourcesContent":["import {\n createContext,\n forwardRef,\n useCallback,\n useContext,\n useLayoutEffect,\n useRef,\n type MutableRefObject,\n type DependencyList,\n type HTMLAttributes,\n type PropsWithChildren,\n type RefCallback,\n type RefObject,\n} from 'react';\n\nimport {\n scheduleGatherMetrics,\n scheduleMicro,\n useActualRef,\n useInitOnce,\n} from '@hh.ru/magritte-ui-nav-bar/internal/utils';\n\n/**\n * Функция-подписчик, которая получает массив измерений для заранее\n * зарегистрированного набора элементов (порядок соответствует зарегистрированному).\n */\ntype MeasureFn = (elementMetrics: DOMRect) => void;\n\n/**\n * Хук измерения: подписывает callback на изменения размеров указанных элементов\n * и возвращает функцию ручного «принудительного» переизмерения (remeasure).\n *\n * @param elements Список RefObject’ов элементов, размеры которых нужно измерять.\n * @param measureFn Коллбек, получающий массив DOMRect при измерении.\n * @param deps Зависимости для эффекта подписки (аналогичны deps useEffect).\n * @returns Функция remeasure(), которая помечает элементы «грязными» и запускает измерение.\n */\nexport interface UseMeasure {\n (\n elements: RefObject<Element | null> | MutableRefObject<Element | null | undefined>,\n measureFn: MeasureFn,\n deps?: DependencyList\n ): VoidFunction;\n}\n\n/**\n * Реестр подписок на измерения размеров элементов.\n * Хранит двунаправные сопоставления «элемент → подписчики» и «подписчик → элементы»,\n * кеширует DOMRect для элементов в текущем тике и поддерживает список «грязных» элементов.\n *\n * Внешний код вызывает:\n * - register/unregister для подписки/отписки,\n * - markDirty/consumeDirty для управления очередью измерений,\n * - subscribersFor/elementFor для получения связей.\n */\nexport class MeasureRegistry {\n private elementsToSubs = new Map<Element, MeasureFn>();\n private subsToElements = new Map<MeasureFn, Element>();\n private dirty = new Set<Element>();\n isDirty = false;\n\n register(element: Element, fn: MeasureFn): boolean {\n this.subsToElements.set(fn, element);\n const isFirstTimeSub = !this.elementsToSubs.has(element);\n const prevSub = this.elementsToSubs.get(element);\n this.elementsToSubs.set(element, fn);\n if (prevSub && prevSub !== fn) {\n this.subsToElements.delete(prevSub);\n }\n return isFirstTimeSub;\n }\n\n unregister(element: Element): void {\n const fn = this.elementsToSubs.get(element);\n if (fn) {\n this.subsToElements.delete(fn);\n this.elementsToSubs.delete(element);\n }\n }\n markDirty(element: Element): void {\n if (this.elementsToSubs.has(element)) {\n this.dirty.add(element);\n this.isDirty = true;\n }\n }\n\n markAllDirty(): void {\n for (const element of this.elementsToSubs.keys()) {\n this.dirty.add(element);\n }\n this.isDirty = true;\n }\n\n consumeDirty(): Element[] {\n const list = [...this.dirty];\n this.dirty.clear();\n this.isDirty = false;\n return list;\n }\n\n elementFor(fn: MeasureFn): Element | null {\n return this.subsToElements.get(fn) ?? null;\n }\n\n subscribersFor(elements: Element[]): Set<MeasureFn> {\n const subs = new Set<MeasureFn>();\n for (const el of elements) {\n const fn = this.elementsToSubs.get(el);\n if (fn) {\n subs.add(fn);\n }\n }\n return subs;\n }\n\n listAllElements(): Element[] {\n return [...this.elementsToSubs.keys()];\n }\n}\n\nexport interface MetricsContextValue {\n useMeasureAuto: UseMeasure;\n useMeasureManual: UseMeasure;\n useMeasureFingerprint: UseMeasure;\n measureAllManual: VoidFunction;\n}\n\n/**\n * Контекст с API для измерений:\n * - useMeasureAuto: авто-режим (измерения по ResizeObserver + ручные remeasure);\n * - useMeasureManual: ручной режим (только по вызову remeasure);\n * - measureAllManual: принудительное переизмерение всех элементов ручного реестра.\n */\nconst MetricsContext = createContext<MetricsContextValue | null>(null);\n\n/**\n * Провайдер измерений. Оборачивает subtree и предоставляет через контекст:\n * - два хука подписки (авто/ручной),\n * - функцию «переизмерить всё» для ручного реестра.\n *\n * Для корректных измерений при необходимости временно навешивает CSS-класс\n * (measureClassName) на корневой div — полезно для временного отключения\n * transform/overflow и т.п., чтобы getBoundingClientRect() отдавал «чистые» размеры.\n *\n * @example\n * <MetricsProvider measureClassName=\"no-transform\">\n * ...\n * </MetricsProvider>\n */\nexport const MetricsProvider = forwardRef<\n HTMLDivElement,\n PropsWithChildren<{ measureClassName?: string } & HTMLAttributes<HTMLDivElement>>\n>(({ children, measureClassName, ...rest }, ref) => {\n const classNameRef = useActualRef(measureClassName);\n const rootRef = useRef<HTMLDivElement | null>();\n const inMeasureCycleRef = useRef(false);\n const callbacksCallCache = useInitOnce(() => new Set<MeasureFn>());\n const observerInstanceRef = useRef<ResizeObserver | null>(null);\n\n // Пробрасываем внешний ref и локально храним DOM-узел.\n const rootRefCallback = useCallback<RefCallback<HTMLDivElement>>(\n (element: HTMLDivElement | null) => {\n rootRef.current = element;\n if (typeof ref === 'function') {\n ref(element);\n } else if (ref) {\n ref.current = element;\n }\n },\n [ref]\n );\n // Текущее состояние «включён ли класс для корректного измерения».\n const classStateRef = useRef(false);\n\n // Реестры: отдельный для авто-подписок (RO) и для ручных подписок.\n const manualSubs = useInitOnce(() => new MeasureRegistry());\n const autoSubs = useInitOnce(() => new MeasureRegistry());\n const fingerprintSubs = useInitOnce(() => new MeasureRegistry());\n\n /**\n * Включает/выключает CSS-класс для контейнера (например, чтобы на время измерения отключить transform).\n * Если включили — автоматически планируем выключение в конец тика (см. disableLater).\n */\n const toggle = (on?: boolean) => {\n if (classStateRef.current === !!on || !rootRef.current || !classNameRef.current) {\n return;\n }\n rootRef.current.classList.toggle(classNameRef.current, !!on);\n classStateRef.current = !!on;\n\n // если навесили класс отключающий трансформации - планируем удаление этого класса на конец тика\n // eslint-disable-next-line @typescript-eslint/no-use-before-define\n !!on && disableLater();\n };\n\n /**\n * Планировщик «снять класс позже»: schedule(toggle) вернёт функцию,\n * которая при вызове поставит toggle(false) в конец очереди микротасок.\n */\n const disableLater = useInitOnce(() => scheduleMicro(toggle));\n const measureDirtyRegistry = useInitOnce(() => (registry: MeasureRegistry) => {\n if (!registry.isDirty) {\n return false;\n }\n\n const dirty = registry.consumeDirty();\n const measureFns = registry.subscribersFor(dirty);\n measureFns.forEach((fn) => {\n if (callbacksCallCache.has(fn)) {\n return;\n }\n callbacksCallCache.add(fn);\n const element = registry.elementFor(fn);\n if (!element) {\n return;\n }\n fn(element.getBoundingClientRect());\n });\n\n return true;\n });\n\n const measureSync = useInitOnce(() => () => {\n /**\n * Во время вызова коллбеков может произойти либо синхронный вызов measureSync, либо асинхронный.\n * Синхронный нужно вызвать сразу, а для асинхронного обычно создается задача в очереди микротасок.\n * Но в случае установки флага inMeasureCycle таска не будет создана, т.к. она не нужна, потому что измерения\n * произойдут в этом же цикле.\n * Чтобы синхронно запущенный вызов не сбросил этот флаг раньше времени мы при вызове функции запоминаем\n * его состояние, и при выходе из него возвращаем его к исходному значению.\n * Получается что при рекурсивном вызове measureSync только самая нижняя в стеке вызовов функция сбросит этот\n * флаг в false.\n */\n const inCycle = inMeasureCycleRef.current;\n inMeasureCycleRef.current = true;\n toggle(true);\n\n // фингерпринты обрабатываем только один раз за цикл, т.к. единственный вариант для этих элементов попасть в\n // список на измерение через ResizeObserver, а он не может добавить новые элементы во время цикла измерения\n measureDirtyRegistry(fingerprintSubs);\n\n // в ручную и авто очередь элементы можно добавить руками с помощью вызова функции, а значит элементы могут\n // добавляться в цикле измерений в коллбеках изменения размера, поэтому обрабатываем их до тех пор пока не\n // перестанут появляться новые элементы\n while (autoSubs.isDirty || manualSubs.isDirty) {\n measureDirtyRegistry(autoSubs);\n measureDirtyRegistry(manualSubs);\n }\n\n if (!inCycle) {\n callbacksCallCache.clear();\n }\n inMeasureCycleRef.current = inCycle;\n });\n\n /**\n * Если вызвано асинхронное измерение, то откладываем его до обработки микротасок, чтобы таска на измерения\n * от ResizeObserver и вызванная вручную схлопнулись в одну.\n * Функция не должна вызываться напрямую, только через runMeasure\n */\n const _scheduledMeasure = useInitOnce(() => scheduleGatherMetrics(measureSync));\n /**\n * Кроме того ручной вызов может произойти в цикле измерений, в этом случае не нужно делать новую таску, достаточно\n * просто пометить инвалидированные размеры, они будут получены (или прочитаны из кеша) в текущем цикле измерений\n */\n const runMeasure = useInitOnce(() => () => {\n if (inMeasureCycleRef.current) {\n return;\n }\n _scheduledMeasure();\n });\n\n const observedElements = useInitOnce(() => new Set<Element>());\n\n /**\n * Общий ResizeObserver: помечает элементы грязными и планирует измерение.\n */\n useLayoutEffect(() => {\n const observer = new ResizeObserver((entries) => {\n const dirtyEntries = entries.map((entry) => entry.target);\n for (const entry of dirtyEntries) {\n autoSubs.markDirty(entry);\n fingerprintSubs.markDirty(entry);\n }\n runMeasure();\n });\n observerInstanceRef.current = observer;\n for (const element of observedElements) {\n observer.observe(element);\n }\n return () => {\n observerInstanceRef.current = null;\n observer.disconnect();\n };\n }, [runMeasure, autoSubs, fingerprintSubs, observedElements]);\n\n /** Отписывает подписчика от авто-реестра и снимает наблюдение, если подписчиков больше нет. */\n const unregister = (registry: MeasureRegistry, element: Element, observe: boolean = true) => {\n registry.unregister(element);\n observedElements.delete(element);\n observe && observerInstanceRef.current?.unobserve(element);\n };\n\n /** Регистрирует подписчика в реестре и опционально подключает наблюдение через ResizeObserver. */\n const register = (registry: MeasureRegistry, element: Element, fn: MeasureFn, observe: boolean = true) => {\n const isFirstSub = registry.register(element, fn);\n observe && observedElements.add(element);\n observe && isFirstSub && observerInstanceRef.current?.observe(element);\n return () => unregister(registry, element, observe);\n };\n\n const createUseMeasure = useInitOnce(\n () =>\n (registry: MeasureRegistry, observe: boolean): UseMeasure =>\n (ref, callback, deps = []) => {\n // eslint-disable-next-line react-hooks/rules-of-hooks\n useLayoutEffect(\n () => (ref.current ? register(registry, ref.current, callback, observe) : void 0),\n // eslint-disable-next-line disable-autofix/react-hooks/exhaustive-deps\n deps\n );\n\n // eslint-disable-next-line react-hooks/rules-of-hooks\n const remeasure: VoidFunction = useCallback(() => {\n if (ref.current) {\n registry.markDirty(ref.current);\n runMeasure();\n }\n }, [ref]);\n\n return remeasure;\n }\n );\n\n /**\n * Возвращает функцию, которая пометит «грязными» все элементы ручного реестра\n * и выполнит синхронное измерение сразу (без ожидания микротасок).\n */\n const measureAllManual = useInitOnce(() => () => {\n manualSubs.markAllDirty();\n measureSync();\n });\n\n const contextValue = useInitOnce(() => ({\n useMeasureAuto: createUseMeasure(autoSubs, true),\n useMeasureManual: createUseMeasure(manualSubs, false),\n useMeasureFingerprint: createUseMeasure(fingerprintSubs, true),\n measureAllManual,\n })) satisfies MetricsContextValue;\n\n return (\n <MetricsContext.Provider value={contextValue}>\n <div {...rest} ref={rootRefCallback}>\n {children}\n </div>\n </MetricsContext.Provider>\n );\n});\n\nMetricsProvider.displayName = 'MetricsProvider';\n\nexport const useMetricsContext = (): MetricsContextValue | null => useContext(MetricsContext);\n\ntype UseMeasureKeys = {\n [K in keyof MetricsContextValue]-?: MetricsContextValue[K] extends UseMeasure ? K : never;\n}[keyof MetricsContextValue];\nconst createHook =\n (name: UseMeasureKeys): UseMeasure =>\n (...args) => {\n const ctx = useContext(MetricsContext);\n if (!ctx) {\n throw new Error('useMeasure* must be used within <MetricsProvider/>');\n }\n return ctx[name](...args);\n };\n\nexport const useMeasureAuto = createHook('useMeasureAuto');\nexport const useMeasureManual = createHook('useMeasureManual');\nexport const useMeasureFingerprint = createHook('useMeasureFingerprint');\n\n/**\n * Хук, возвращающий функцию «переизмерить всё» для подписок зарегистрированных с помощью {@link useMeasureManual}.\n * Полезно, когда нужно одномоментно обновить измерения всех зарегистрированных вручную элементов.\n */\nexport const useRemeasureAllManual: () => VoidFunction = () => {\n const ctx = useContext(MetricsContext);\n if (!ctx) {\n throw new Error('useMeasure* must be used within <MetricsProvider/>');\n }\n return ctx.measureAllManual;\n};\n"],"names":["_jsx"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"MorphStore.js","sources":["../../src/internal/MorphStore.tsx"],"sourcesContent":["import { createContext, useContext, type FC, type PropsWithChildren } from 'react';\n\nimport { KeyedSubscriptions } from '@hh.ru/magritte-ui-nav-bar/internal/KeyedSubscriptions';\nimport { isDOMRectsEqual, useInitOnce } from '@hh.ru/magritte-ui-nav-bar/internal/utils';\n\nexport class MorphStore extends KeyedSubscriptions<string> {\n private morphRectsStore = new Map<string, DOMRectReadOnly>();\n\n set(key: string, rect: DOMRectReadOnly | null): void {\n const isEqual = isDOMRectsEqual(rect, this.morphRectsStore.get(key) ?? null);\n // eslint-disable-next-line @typescript-eslint/no-unused-expressions\n rect === null ? this.morphRectsStore.delete(key) : this.morphRectsStore.set(key, rect);\n if (!isEqual) {\n this.markChanged(key);\n this.notify();\n }\n }\n\n get(key: string): DOMRectReadOnly | null {\n return this.morphRectsStore.get(key) ?? null;\n }\n}\n\nexport const MorphContext = createContext<MorphStore | null>(null);\nexport const MorphStoreProvider: FC<PropsWithChildren> = ({ children }) => {\n const store = useInitOnce(() => new MorphStore());\n\n return <MorphContext.Provider value={store}>{children}</MorphContext.Provider>;\n};\n\nexport const useMorphStore = (): MorphStore => {\n const morphStore = useContext(MorphContext);\n if (!morphStore) {\n throw new Error('useMorphStore must be used within <MorphStoreProvider/>');\n }\n return morphStore;\n};\n"],"names":["_jsx"],"mappings":";;;;;;AAKM,MAAO,UAAW,SAAQ,kBAA0B,CAAA;AAC9C,IAAA,eAAe,GAAG,IAAI,GAAG,EAA2B,CAAC;IAE7D,GAAG,CAAC,GAAW,EAAE,IAA4B,EAAA;AACzC,QAAA,MAAM,OAAO,GAAG,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC;;QAE7E,IAAI,KAAK,IAAI,GAAG,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACvF,IAAI,CAAC,OAAO,EAAE;AACV,YAAA,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;YACtB,IAAI,CAAC,MAAM,EAAE,CAAC;SACjB;KACJ;AAED,IAAA,GAAG,CAAC,GAAW,EAAA;QACX,OAAO,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;KAChD;AACJ,CAAA;MAEY,YAAY,GAAG,aAAa,CAAoB,IAAI,EAAE;MACtD,kBAAkB,GAA0B,CAAC,EAAE,QAAQ,EAAE,KAAI;IACtE,MAAM,KAAK,GAAG,WAAW,CAAC,MAAM,IAAI,UAAU,EAAE,CAAC,CAAC;IAElD,OAAOA,GAAA,CAAC,YAAY,CAAC,QAAQ,EAAA,EAAC,KAAK,EAAE,KAAK,EAAA,QAAA,EAAG,QAAQ,EAAA,CAAyB,CAAC;AACnF,EAAE;AAEK,MAAM,aAAa,GAAG,MAAiB;AAC1C,IAAA,MAAM,UAAU,GAAG,UAAU,CAAC,YAAY,CAAC,CAAC;IAC5C,IAAI,CAAC,UAAU,EAAE;AACb,QAAA,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;KAC9E;AACD,IAAA,OAAO,UAAU,CAAC;AACtB;;;;"}
1
+ {"version":3,"file":"MorphStore.js","sources":["src/internal/MorphStore.tsx"],"sourcesContent":["import { createContext, useContext, type FC, type PropsWithChildren } from 'react';\n\nimport { KeyedSubscriptions } from '@hh.ru/magritte-ui-nav-bar/internal/KeyedSubscriptions';\nimport { isDOMRectsEqual, useInitOnce } from '@hh.ru/magritte-ui-nav-bar/internal/utils';\n\nexport class MorphStore extends KeyedSubscriptions<string> {\n private morphRectsStore = new Map<string, DOMRectReadOnly>();\n\n set(key: string, rect: DOMRectReadOnly | null): void {\n const isEqual = isDOMRectsEqual(rect, this.morphRectsStore.get(key) ?? null);\n // eslint-disable-next-line @typescript-eslint/no-unused-expressions\n rect === null ? this.morphRectsStore.delete(key) : this.morphRectsStore.set(key, rect);\n if (!isEqual) {\n this.markChanged(key);\n this.notify();\n }\n }\n\n get(key: string): DOMRectReadOnly | null {\n return this.morphRectsStore.get(key) ?? null;\n }\n}\n\nexport const MorphContext = createContext<MorphStore | null>(null);\nexport const MorphStoreProvider: FC<PropsWithChildren> = ({ children }) => {\n const store = useInitOnce(() => new MorphStore());\n\n return <MorphContext.Provider value={store}>{children}</MorphContext.Provider>;\n};\n\nexport const useMorphStore = (): MorphStore => {\n const morphStore = useContext(MorphContext);\n if (!morphStore) {\n throw new Error('useMorphStore must be used within <MorphStoreProvider/>');\n }\n return morphStore;\n};\n"],"names":["_jsx"],"mappings":";;;;;;AAKM,MAAO,UAAW,SAAQ,kBAA0B,CAAA;AAC9C,IAAA,eAAe,GAAG,IAAI,GAAG,EAA2B,CAAC;IAE7D,GAAG,CAAC,GAAW,EAAE,IAA4B,EAAA;AACzC,QAAA,MAAM,OAAO,GAAG,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC;;QAE7E,IAAI,KAAK,IAAI,GAAG,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACvF,IAAI,CAAC,OAAO,EAAE;AACV,YAAA,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;YACtB,IAAI,CAAC,MAAM,EAAE,CAAC;SACjB;KACJ;AAED,IAAA,GAAG,CAAC,GAAW,EAAA;QACX,OAAO,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;KAChD;AACJ,CAAA;MAEY,YAAY,GAAG,aAAa,CAAoB,IAAI,EAAE;MACtD,kBAAkB,GAA0B,CAAC,EAAE,QAAQ,EAAE,KAAI;IACtE,MAAM,KAAK,GAAG,WAAW,CAAC,MAAM,IAAI,UAAU,EAAE,CAAC,CAAC;IAElD,OAAOA,GAAA,CAAC,YAAY,CAAC,QAAQ,EAAA,EAAC,KAAK,EAAE,KAAK,EAAA,QAAA,EAAG,QAAQ,EAAA,CAAyB,CAAC;AACnF,EAAE;AAEK,MAAM,aAAa,GAAG,MAAiB;AAC1C,IAAA,MAAM,UAAU,GAAG,UAAU,CAAC,YAAY,CAAC,CAAC;IAC5C,IAAI,CAAC,UAAU,EAAE;AACb,QAAA,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;KAC9E;AACD,IAAA,OAAO,UAAU,CAAC;AACtB;;;;"}
@@ -3,7 +3,7 @@ import { createContext, useContext, useId, useLayoutEffect } from 'react';
3
3
  import { motionValue } from 'motion';
4
4
  import { KeyedSubscriptions } from './KeyedSubscriptions.js';
5
5
  import { useInitOnce } from './utils.js';
6
- import { u as useNavBarContext } from '../NavBar-B-hISzmq.js';
6
+ import { u as useNavBarContext } from '../NavBar-D7kMx-Hi.js';
7
7
  import 'react/jsx-runtime';
8
8
  import 'classnames';
9
9
  import 'motion/react';
@@ -1 +1 @@
1
- {"version":3,"file":"PaneStore.js","sources":["../../src/internal/PaneStore.ts"],"sourcesContent":["import { createContext, useContext, useId, useLayoutEffect } from 'react';\nimport { motionValue, type MotionValue } from 'motion';\n\nimport { KeyedSubscriptions } from '@hh.ru/magritte-ui-nav-bar/internal/KeyedSubscriptions';\nimport { useInitOnce } from '@hh.ru/magritte-ui-nav-bar/internal/utils';\nimport { useNavBarContext } from '@hh.ru/magritte-ui-nav-bar/public/NavBar';\n\nexport interface PaneData {\n id: string;\n foldable?: boolean;\n motionValue: MotionValue<number>;\n startHeight?: number;\n endHeight?: number;\n top?: number;\n animated?: boolean;\n}\n\n/**\n * Хранилище состояния панели с поддержкой подписок на изменения.\n *\n * Наследует всю подписочную логику от {@link KeyedSubscriptions}, а именно:\n * - `onChange(key, cb)` — подписка на изменения конкретного ключа состояния;\n * - `onDestroy(cb)` — подписка на уничтожение;\n * - батчинг уведомлений (несколько изменений за один цикл объединяются);\n * - дедупликация коллбеков (один коллбек, подписанный на разные ключи, вызовется один раз).\n *\n * Собственная ответственность класса:\n * - Хранение состояния панели в `PaneData`.\n * - Применение частичных обновлений через метод `set(...)`:\n * - сравнивает новые значения с текущими;\n * - обновляет только изменившиеся поля;\n * - помечает изменённые ключи (`markChanged`) и инициирует рассылку (`notify`).\n * - Получение текущего значения по ключу через `get(...)`.\n *\n * Таким образом, `PaneStore` объединяет модель данных панели\n * с реактивными подписками на её изменения.\n */\nexport class PaneStore extends KeyedSubscriptions<keyof PaneData> {\n data: PaneData;\n\n /**\n * @param data Начальное состояние панели.\n */\n constructor(data: PaneData) {\n super();\n this.data = data;\n }\n\n /**\n * Частичное обновление состояния панели.\n * Для каждого ключа из `patch` выполняется сравнение и,\n * если значение изменилось, оно записывается в `data`,\n * а ключ помечается как изменённый.\n * После всех обновлений вызывается `notify()` для рассылки подписчикам.\n *\n * @example\n * controller.set({ foldable: true, startHeight: 200 });\n */\n set<K extends keyof PaneData>(patch: Partial<Pick<PaneData, K>>): void {\n (Object.keys(patch) as K[]).forEach((key) => {\n const next = patch[key];\n if (next !== this.data[key]) {\n this.data[key] = next as PaneData[K];\n this.markChanged(key);\n }\n });\n this.notify();\n }\n\n /**\n * Получить текущее значение поля состояния.\n *\n * @param key Ключ из {@link PaneData}.\n * @returns Текущее значение по указанному ключу.\n *\n * @example\n * const height = controller.get(\"startHeight\");\n */\n get<K extends keyof PaneData>(key: K): PaneData[K] {\n return this.data[key];\n }\n}\n\nexport const PaneContext = createContext<PaneStore | null>(null);\nexport const usePaneStore = (): PaneStore => {\n const contextStore = useContext(PaneContext);\n const id = useId();\n const registerPaneStore = useNavBarContext();\n const paneStore = useInitOnce(() => contextStore ?? new PaneStore({ motionValue: motionValue(0), id }));\n useLayoutEffect(() => {\n if (!registerPaneStore || contextStore !== null) {\n return void 0;\n }\n\n return registerPaneStore(contextStore ?? paneStore);\n }, [contextStore, paneStore, registerPaneStore]);\n return contextStore ?? paneStore;\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAiBA;;;;;;;;;;;;;;;;;;;AAmBG;AACG,MAAO,SAAU,SAAQ,kBAAkC,CAAA;AAC7D,IAAA,IAAI,CAAW;AAEf;;AAEG;AACH,IAAA,WAAA,CAAY,IAAc,EAAA;AACtB,QAAA,KAAK,EAAE,CAAC;AACR,QAAA,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;KACpB;AAED;;;;;;;;;AASG;AACH,IAAA,GAAG,CAA2B,KAAiC,EAAA;QAC1D,MAAM,CAAC,IAAI,CAAC,KAAK,CAAS,CAAC,OAAO,CAAC,CAAC,GAAG,KAAI;AACxC,YAAA,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;YACxB,IAAI,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;AACzB,gBAAA,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAmB,CAAC;AACrC,gBAAA,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;aACzB;AACL,SAAC,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,EAAE,CAAC;KACjB;AAED;;;;;;;;AAQG;AACH,IAAA,GAAG,CAA2B,GAAM,EAAA;AAChC,QAAA,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;KACzB;AACJ,CAAA;MAEY,WAAW,GAAG,aAAa,CAAmB,IAAI,EAAE;AAC1D,MAAM,YAAY,GAAG,MAAgB;AACxC,IAAA,MAAM,YAAY,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC;AAC7C,IAAA,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC;AACnB,IAAA,MAAM,iBAAiB,GAAG,gBAAgB,EAAE,CAAC;IAC7C,MAAM,SAAS,GAAG,WAAW,CAAC,MAAM,YAAY,IAAI,IAAI,SAAS,CAAC,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;IACxG,eAAe,CAAC,MAAK;AACjB,QAAA,IAAI,CAAC,iBAAiB,IAAI,YAAY,KAAK,IAAI,EAAE;YAC7C,OAAO,KAAK,CAAC,CAAC;SACjB;AAED,QAAA,OAAO,iBAAiB,CAAC,YAAY,IAAI,SAAS,CAAC,CAAC;KACvD,EAAE,CAAC,YAAY,EAAE,SAAS,EAAE,iBAAiB,CAAC,CAAC,CAAC;IACjD,OAAO,YAAY,IAAI,SAAS,CAAC;AACrC;;;;"}
1
+ {"version":3,"file":"PaneStore.js","sources":["src/internal/PaneStore.ts"],"sourcesContent":["import { createContext, useContext, useId, useLayoutEffect } from 'react';\nimport { motionValue, type MotionValue } from 'motion';\n\nimport { KeyedSubscriptions } from '@hh.ru/magritte-ui-nav-bar/internal/KeyedSubscriptions';\nimport { useInitOnce } from '@hh.ru/magritte-ui-nav-bar/internal/utils';\nimport { useNavBarContext } from '@hh.ru/magritte-ui-nav-bar/public/NavBar';\n\nexport interface PaneData {\n id: string;\n foldable?: boolean;\n motionValue: MotionValue<number>;\n startHeight?: number;\n endHeight?: number;\n top?: number;\n animated?: boolean;\n}\n\n/**\n * Хранилище состояния панели с поддержкой подписок на изменения.\n *\n * Наследует всю подписочную логику от {@link KeyedSubscriptions}, а именно:\n * - `onChange(key, cb)` — подписка на изменения конкретного ключа состояния;\n * - `onDestroy(cb)` — подписка на уничтожение;\n * - батчинг уведомлений (несколько изменений за один цикл объединяются);\n * - дедупликация коллбеков (один коллбек, подписанный на разные ключи, вызовется один раз).\n *\n * Собственная ответственность класса:\n * - Хранение состояния панели в `PaneData`.\n * - Применение частичных обновлений через метод `set(...)`:\n * - сравнивает новые значения с текущими;\n * - обновляет только изменившиеся поля;\n * - помечает изменённые ключи (`markChanged`) и инициирует рассылку (`notify`).\n * - Получение текущего значения по ключу через `get(...)`.\n *\n * Таким образом, `PaneStore` объединяет модель данных панели\n * с реактивными подписками на её изменения.\n */\nexport class PaneStore extends KeyedSubscriptions<keyof PaneData> {\n data: PaneData;\n\n /**\n * @param data Начальное состояние панели.\n */\n constructor(data: PaneData) {\n super();\n this.data = data;\n }\n\n /**\n * Частичное обновление состояния панели.\n * Для каждого ключа из `patch` выполняется сравнение и,\n * если значение изменилось, оно записывается в `data`,\n * а ключ помечается как изменённый.\n * После всех обновлений вызывается `notify()` для рассылки подписчикам.\n *\n * @example\n * controller.set({ foldable: true, startHeight: 200 });\n */\n set<K extends keyof PaneData>(patch: Partial<Pick<PaneData, K>>): void {\n (Object.keys(patch) as K[]).forEach((key) => {\n const next = patch[key];\n if (next !== this.data[key]) {\n this.data[key] = next as PaneData[K];\n this.markChanged(key);\n }\n });\n this.notify();\n }\n\n /**\n * Получить текущее значение поля состояния.\n *\n * @param key Ключ из {@link PaneData}.\n * @returns Текущее значение по указанному ключу.\n *\n * @example\n * const height = controller.get(\"startHeight\");\n */\n get<K extends keyof PaneData>(key: K): PaneData[K] {\n return this.data[key];\n }\n}\n\nexport const PaneContext = createContext<PaneStore | null>(null);\nexport const usePaneStore = (): PaneStore => {\n const contextStore = useContext(PaneContext);\n const id = useId();\n const registerPaneStore = useNavBarContext();\n const paneStore = useInitOnce(() => contextStore ?? new PaneStore({ motionValue: motionValue(0), id }));\n useLayoutEffect(() => {\n if (!registerPaneStore || contextStore !== null) {\n return void 0;\n }\n\n return registerPaneStore(contextStore ?? paneStore);\n }, [contextStore, paneStore, registerPaneStore]);\n return contextStore ?? paneStore;\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAiBA;;;;;;;;;;;;;;;;;;;AAmBG;AACG,MAAO,SAAU,SAAQ,kBAAkC,CAAA;AAC7D,IAAA,IAAI,CAAW;AAEf;;AAEG;AACH,IAAA,WAAA,CAAY,IAAc,EAAA;AACtB,QAAA,KAAK,EAAE,CAAC;AACR,QAAA,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;KACpB;AAED;;;;;;;;;AASG;AACH,IAAA,GAAG,CAA2B,KAAiC,EAAA;QAC1D,MAAM,CAAC,IAAI,CAAC,KAAK,CAAS,CAAC,OAAO,CAAC,CAAC,GAAG,KAAI;AACxC,YAAA,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;YACxB,IAAI,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;AACzB,gBAAA,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAmB,CAAC;AACrC,gBAAA,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;aACzB;AACL,SAAC,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,EAAE,CAAC;KACjB;AAED;;;;;;;;AAQG;AACH,IAAA,GAAG,CAA2B,GAAM,EAAA;AAChC,QAAA,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;KACzB;AACJ,CAAA;MAEY,WAAW,GAAG,aAAa,CAAmB,IAAI,EAAE;AAC1D,MAAM,YAAY,GAAG,MAAgB;AACxC,IAAA,MAAM,YAAY,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC;AAC7C,IAAA,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC;AACnB,IAAA,MAAM,iBAAiB,GAAG,gBAAgB,EAAE,CAAC;IAC7C,MAAM,SAAS,GAAG,WAAW,CAAC,MAAM,YAAY,IAAI,IAAI,SAAS,CAAC,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;IACxG,eAAe,CAAC,MAAK;AACjB,QAAA,IAAI,CAAC,iBAAiB,IAAI,YAAY,KAAK,IAAI,EAAE;YAC7C,OAAO,KAAK,CAAC,CAAC;SACjB;AAED,QAAA,OAAO,iBAAiB,CAAC,YAAY,IAAI,SAAS,CAAC,CAAC;KACvD,EAAE,CAAC,YAAY,EAAE,SAAS,EAAE,iBAAiB,CAAC,CAAC,CAAC;IACjD,OAAO,YAAY,IAAI,SAAS,CAAC;AACrC;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"useAnimationRanges.js","sources":["../../src/internal/useAnimationRanges.ts"],"sourcesContent":["import { type MutableRefObject, useRef } from 'react';\n\nimport { type PaneStore } from '@hh.ru/magritte-ui-nav-bar/internal/PaneStore';\nimport { NavBarMetrics } from '@hh.ru/magritte-ui-nav-bar/internal/useNavBarMetrics';\nimport { useActualRef, useInitOnce } from '@hh.ru/magritte-ui-nav-bar/internal/utils';\n\n/**\n * Рассчитывает для панелей диапазоны скролла, в течение которого должна проигрываться их анимация.\n *\n * - Каждая панель принадлежит группе, каждая новая группа начинается с foldable панели, и включет её\n * - Для группы суммируется изменение высот её панелей:\n * - для foldable панелей: diff = startHeight (они схлопываются до 0),\n * - для остальных: diff = startHeight − (endHeight ?? startHeight).\n * - Диапазон скролла группы равен [cum, cum + sumDiff], где `cum` — суммарное изменение высот всех предыдущих групп.\n * - Всем панелям группы присваивается один и тот же диапазон.\n *\n * Вход:\n * - Массив панелей в произвольном порядке; внутри функция сортирует их по `top` по убыванию,\n * чтобы идти от нижних к верхним.\n *\n * Выход:\n * - Массив той же длины и в том же порядке, что и `data`.\n * Для каждой панели — кортеж `[start, end]` в пикселях, либо `null`, если диапазон не вычислен.\n *\n * Ожидания по данным:\n * - `id` уникален (используется как ключ).\n * - `startHeight` задан у всех панелей.\n * - `endHeight` опционален (если не задан, изменение высоты для не-foldable панелей считается 0).\n * - `top` задан для корректной сортировки.\n *\n * @param {PaneData[]} data\n * Список панелей. Минимально используемые поля:\n * - id: string\n * - top?: number\n * - foldable?: boolean\n * - startHeight?: number\n * - endHeight?: number\n *\n * @returns {Array<[number, number] | null>}\n * Диапазоны скролла для анимации каждой панели в исходном порядке `data`.\n */\nexport type AnimationRanges = Map<string, [number, number]>;\nexport const useAnimationRanges = (\n paneRegistry: Set<PaneStore>,\n getNavBarMetrics: () => NavBarMetrics\n): [() => AnimationRanges, VoidFunction] => {\n const animationRangesRef: MutableRefObject<AnimationRanges | null> = useRef(null);\n const paneRegistryRef = useActualRef(paneRegistry);\n\n const getAnimationRanges = useInitOnce(() => () => {\n if (!animationRangesRef.current) {\n // Идём снизу вверх: сортируем по top убыванием.\n const panesData = [...paneRegistryRef.current.values()]\n .map((pane) => pane.data)\n .sort((a, b) => (b.top ?? 0) - (a.top ?? 0));\n const { heightDelta } = getNavBarMetrics();\n\n const scrollRangesById = new Map<string, [number, number]>();\n let groupStartIdx: number | null = 0; // индекс последней встреченной foldable (начало текущей группы)\n let totalDuration = 0; // суммарная длительность (в пикселях скролла) всех ранее обработанных групп\n let groupDuration = 0; // суммарная длительность (в пикселях скролла) текущей группы\n\n // ни одна из панелей не схлопывается и не меняет высоту в процессе анимации\n // в этом случае просто разбиваем стадии анимации на отрезки пропорциональные высоте панелей\n if (heightDelta === 0) {\n for (let i = 0; i < panesData.length; i++) {\n const pane = panesData[i];\n if (pane.animated) {\n const paneHeight = pane.startHeight ?? 0;\n scrollRangesById.set(pane.id, [totalDuration, totalDuration + paneHeight]);\n totalDuration += paneHeight;\n }\n }\n } else {\n for (let i = 0; i <= panesData.length; i++) {\n const pane = panesData[i] ?? null;\n const isLastPane = pane === null;\n const isGroupEnd = isLastPane || pane.foldable;\n // Новая foldable или последняя панель -> завершаем обработку текущей группы, и сохраняем\n // диапазон если известно место ее начала\n if (isGroupEnd && groupStartIdx !== null) {\n const range: [number, number] = [totalDuration, totalDuration + groupDuration];\n for (let j = groupStartIdx; j < i; j++) {\n scrollRangesById.set(panesData[j].id, range);\n }\n totalDuration += groupDuration;\n groupDuration = 0;\n }\n\n if (!isLastPane) {\n if (pane.foldable) {\n groupStartIdx = i;\n }\n\n // Дельта высоты панели: вклад в длительность текущей группы\n const deltaHeight = pane.foldable\n ? (pane.startHeight ?? 0) // foldable схлопывается до 0\n : (pane.startHeight ?? 0) - (pane.endHeight ?? pane.startHeight ?? 0); // иначе разница start -> end\n groupDuration += deltaHeight;\n }\n }\n }\n\n animationRangesRef.current = scrollRangesById;\n }\n return animationRangesRef.current;\n });\n\n const invalidateAnimationRangesCache = useInitOnce(() => () => (animationRangesRef.current = null));\n\n return [getAnimationRanges, invalidateAnimationRangesCache];\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useAnimationRanges.js","sources":["src/internal/useAnimationRanges.ts"],"sourcesContent":["import { type MutableRefObject, useRef } from 'react';\n\nimport { type PaneStore } from '@hh.ru/magritte-ui-nav-bar/internal/PaneStore';\nimport { NavBarMetrics } from '@hh.ru/magritte-ui-nav-bar/internal/useNavBarMetrics';\nimport { useActualRef, useInitOnce } from '@hh.ru/magritte-ui-nav-bar/internal/utils';\n\n/**\n * Рассчитывает для панелей диапазоны скролла, в течение которого должна проигрываться их анимация.\n *\n * - Каждая панель принадлежит группе, каждая новая группа начинается с foldable панели, и включет её\n * - Для группы суммируется изменение высот её панелей:\n * - для foldable панелей: diff = startHeight (они схлопываются до 0),\n * - для остальных: diff = startHeight − (endHeight ?? startHeight).\n * - Диапазон скролла группы равен [cum, cum + sumDiff], где `cum` — суммарное изменение высот всех предыдущих групп.\n * - Всем панелям группы присваивается один и тот же диапазон.\n *\n * Вход:\n * - Массив панелей в произвольном порядке; внутри функция сортирует их по `top` по убыванию,\n * чтобы идти от нижних к верхним.\n *\n * Выход:\n * - Массив той же длины и в том же порядке, что и `data`.\n * Для каждой панели — кортеж `[start, end]` в пикселях, либо `null`, если диапазон не вычислен.\n *\n * Ожидания по данным:\n * - `id` уникален (используется как ключ).\n * - `startHeight` задан у всех панелей.\n * - `endHeight` опционален (если не задан, изменение высоты для не-foldable панелей считается 0).\n * - `top` задан для корректной сортировки.\n *\n * @param {PaneData[]} data\n * Список панелей. Минимально используемые поля:\n * - id: string\n * - top?: number\n * - foldable?: boolean\n * - startHeight?: number\n * - endHeight?: number\n *\n * @returns {Array<[number, number] | null>}\n * Диапазоны скролла для анимации каждой панели в исходном порядке `data`.\n */\nexport type AnimationRanges = Map<string, [number, number]>;\nexport const useAnimationRanges = (\n paneRegistry: Set<PaneStore>,\n getNavBarMetrics: () => NavBarMetrics\n): [() => AnimationRanges, VoidFunction] => {\n const animationRangesRef: MutableRefObject<AnimationRanges | null> = useRef(null);\n const paneRegistryRef = useActualRef(paneRegistry);\n\n const getAnimationRanges = useInitOnce(() => () => {\n if (!animationRangesRef.current) {\n // Идём снизу вверх: сортируем по top убыванием.\n const panesData = [...paneRegistryRef.current.values()]\n .map((pane) => pane.data)\n .sort((a, b) => (b.top ?? 0) - (a.top ?? 0));\n const { heightDelta } = getNavBarMetrics();\n\n const scrollRangesById = new Map<string, [number, number]>();\n let groupStartIdx: number | null = 0; // индекс последней встреченной foldable (начало текущей группы)\n let totalDuration = 0; // суммарная длительность (в пикселях скролла) всех ранее обработанных групп\n let groupDuration = 0; // суммарная длительность (в пикселях скролла) текущей группы\n\n // ни одна из панелей не схлопывается и не меняет высоту в процессе анимации\n // в этом случае просто разбиваем стадии анимации на отрезки пропорциональные высоте панелей\n if (heightDelta === 0) {\n for (let i = 0; i < panesData.length; i++) {\n const pane = panesData[i];\n if (pane.animated) {\n const paneHeight = pane.startHeight ?? 0;\n scrollRangesById.set(pane.id, [totalDuration, totalDuration + paneHeight]);\n totalDuration += paneHeight;\n }\n }\n } else {\n for (let i = 0; i <= panesData.length; i++) {\n const pane = panesData[i] ?? null;\n const isLastPane = pane === null;\n const isGroupEnd = isLastPane || pane.foldable;\n // Новая foldable или последняя панель -> завершаем обработку текущей группы, и сохраняем\n // диапазон если известно место ее начала\n if (isGroupEnd && groupStartIdx !== null) {\n const range: [number, number] = [totalDuration, totalDuration + groupDuration];\n for (let j = groupStartIdx; j < i; j++) {\n scrollRangesById.set(panesData[j].id, range);\n }\n totalDuration += groupDuration;\n groupDuration = 0;\n }\n\n if (!isLastPane) {\n if (pane.foldable) {\n groupStartIdx = i;\n }\n\n // Дельта высоты панели: вклад в длительность текущей группы\n const deltaHeight = pane.foldable\n ? (pane.startHeight ?? 0) // foldable схлопывается до 0\n : (pane.startHeight ?? 0) - (pane.endHeight ?? pane.startHeight ?? 0); // иначе разница start -> end\n groupDuration += deltaHeight;\n }\n }\n }\n\n animationRangesRef.current = scrollRangesById;\n }\n return animationRangesRef.current;\n });\n\n const invalidateAnimationRangesCache = useInitOnce(() => () => (animationRangesRef.current = null));\n\n return [getAnimationRanges, invalidateAnimationRangesCache];\n};\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"useBindScrollToAnimationProgress.js","sources":["../../src/internal/useBindScrollToAnimationProgress.ts"],"sourcesContent":["import { type MutableRefObject, type RefObject, useRef } from 'react';\nimport { type MotionStyle, type MotionValue, useMotionValue } from 'motion/react';\n\nimport { type AnimationRanges } from '@hh.ru/magritte-ui-nav-bar/internal/useAnimationRanges';\nimport { type NavBarMetrics } from '@hh.ru/magritte-ui-nav-bar/internal/useNavBarMetrics';\nimport { type ScrollAdapter } from '@hh.ru/magritte-ui-nav-bar/internal/useScrollAdapter';\nimport { remap, clamp, useInitOnce, useActualRef, getRelativeOffset } from '@hh.ru/magritte-ui-nav-bar/internal/utils';\n\nconst createScrollToAnimationProgressMapFn = (\n getNavBarMetrics: () => NavBarMetrics,\n scrollAdapter: ScrollAdapter,\n animationRanges: [number, number][],\n startTriggerPosition: 'start' | 'full-area' | RefObject<HTMLElement | null>,\n endTriggerPosition?: RefObject<HTMLElement | null> | number\n) => {\n const maxScrollTop = scrollAdapter.getMaxScrollTop();\n const { heightDelta, top, bottom, animationHeight } = getNavBarMetrics();\n\n if (startTriggerPosition === 'full-area') {\n let currentStart = top;\n const maxProgress =\n maxScrollTop >= animationHeight + top\n ? 1\n : (animationRanges.find((range) => range[0] * animationHeight <= maxScrollTop - top) ?? [0])[0];\n\n const mapScrollToAnimationPropgress = (scrollPosition: number) => {\n const progress = (scrollPosition - currentStart) / animationHeight;\n if (progress > maxProgress) {\n currentStart = scrollPosition - animationHeight * maxProgress;\n } else if (progress < 0) {\n currentStart = scrollPosition;\n }\n currentStart = Math.max(currentStart, top);\n\n return clamp(progress, 0, maxProgress);\n };\n\n const getClosestStops = () => ({\n bottom: currentStart,\n top: currentStart + animationHeight * maxProgress,\n });\n\n return [mapScrollToAnimationPropgress, getClosestStops] as const;\n }\n\n let startPos =\n startTriggerPosition === 'start' || !startTriggerPosition.current\n ? 0\n : getRelativeOffset(scrollAdapter, startTriggerPosition.current) - bottom;\n\n let endPos: number;\n if (typeof endTriggerPosition === 'number') {\n endPos = startPos + endTriggerPosition;\n } else {\n endPos = endTriggerPosition?.current\n ? getRelativeOffset(scrollAdapter, endTriggerPosition.current) + heightDelta - bottom\n : startPos + animationHeight;\n }\n\n endPos += top;\n startPos += top;\n\n let endProgress = 1;\n if (endPos > maxScrollTop) {\n const scrollDelta = endPos - startPos;\n endProgress = (animationRanges.find((range) => range[0] * scrollDelta + startPos < maxScrollTop) ?? [0])[0];\n endPos = startPos + scrollDelta * endProgress;\n }\n\n return [remap([startPos, endPos], [0, endProgress]), () => ({ bottom: startPos, top: endPos })] as const;\n};\n\nconst isDividerVisible = (scrollTop: number, navBarTop: number, heightDelta: number, animationProgress: number) =>\n Math.ceil(navBarTop + heightDelta * animationProgress) < Math.floor(scrollTop);\n\nexport const useBindScrollToAnimationProgress = (\n scrollPosition: MotionValue<number>,\n getNavBarMetrics: () => NavBarMetrics,\n getAnimationRanges: () => AnimationRanges,\n scrollAdapter: ScrollAdapter,\n startTriggerPosition: 'start' | 'full-area' | RefObject<HTMLElement | null>,\n endTriggerPosition?: RefObject<HTMLElement | null> | number\n): [VoidFunction, () => { top: number; bottom: number }, MotionValue<number>, MotionStyle] => {\n const totalAnimationProgress = useMotionValue(0);\n const dividerVisibility = useMotionValue<'visible' | 'hidden'>('hidden');\n const dividerOffsetY = useMotionValue(0);\n const startTriggerPositionRef = useActualRef(startTriggerPosition);\n const endTriggerPositionRef = useActualRef(endTriggerPosition);\n const getClosestStopsRef: MutableRefObject<(() => { top: number; bottom: number }) | null> = useRef(null);\n\n const bindScrollToAnimation = useInitOnce(() => {\n let unsubscribe: VoidFunction | null = null;\n return () => {\n unsubscribe?.();\n const { heightDelta, top, animationHeight } = getNavBarMetrics();\n\n const animationRanges = [\n ...new Set<[number, number]>(\n [...getAnimationRanges().values()].map((range) => [\n range[0] / animationHeight,\n range[1] / animationHeight,\n ])\n ),\n ].sort((a, b) => b[0] - a[0]);\n\n const [mapFn, getClosestStops] = createScrollToAnimationProgressMapFn(\n getNavBarMetrics,\n scrollAdapter,\n animationRanges,\n startTriggerPositionRef.current,\n endTriggerPositionRef.current\n );\n getClosestStopsRef.current = getClosestStops;\n const updateDivider = (scrollPosition: number, totalAnimationProgress: number) => {\n dividerVisibility.set(\n isDividerVisible(scrollPosition, top, heightDelta, totalAnimationProgress) ? 'visible' : 'hidden'\n );\n dividerOffsetY.set(-heightDelta * totalAnimationProgress);\n };\n unsubscribe = scrollPosition.on('change', (value) => {\n totalAnimationProgress.set(mapFn(value));\n updateDivider(value, totalAnimationProgress.get());\n });\n\n totalAnimationProgress.set(mapFn(scrollPosition.get()));\n updateDivider(scrollPosition.get(), totalAnimationProgress.get());\n };\n });\n\n const getClosestStops = useInitOnce(() => () => {\n if (getClosestStopsRef.current) {\n return getClosestStopsRef.current();\n }\n\n return { bottom: 0, top: getNavBarMetrics().heightDelta };\n });\n\n return [\n bindScrollToAnimation,\n getClosestStops,\n totalAnimationProgress,\n { visibility: dividerVisibility, y: dividerOffsetY },\n ];\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useBindScrollToAnimationProgress.js","sources":["src/internal/useBindScrollToAnimationProgress.ts"],"sourcesContent":["import { type MutableRefObject, type RefObject, useRef } from 'react';\nimport { type MotionStyle, type MotionValue, useMotionValue } from 'motion/react';\n\nimport { type AnimationRanges } from '@hh.ru/magritte-ui-nav-bar/internal/useAnimationRanges';\nimport { type NavBarMetrics } from '@hh.ru/magritte-ui-nav-bar/internal/useNavBarMetrics';\nimport { type ScrollAdapter } from '@hh.ru/magritte-ui-nav-bar/internal/useScrollAdapter';\nimport { remap, clamp, useInitOnce, useActualRef, getRelativeOffset } from '@hh.ru/magritte-ui-nav-bar/internal/utils';\n\nconst createScrollToAnimationProgressMapFn = (\n getNavBarMetrics: () => NavBarMetrics,\n scrollAdapter: ScrollAdapter,\n animationRanges: [number, number][],\n startTriggerPosition: 'start' | 'full-area' | RefObject<HTMLElement | null>,\n endTriggerPosition?: RefObject<HTMLElement | null> | number\n) => {\n const maxScrollTop = scrollAdapter.getMaxScrollTop();\n const { heightDelta, top, bottom, animationHeight } = getNavBarMetrics();\n\n if (startTriggerPosition === 'full-area') {\n let currentStart = top;\n const maxProgress =\n maxScrollTop >= animationHeight + top\n ? 1\n : (animationRanges.find((range) => range[0] * animationHeight <= maxScrollTop - top) ?? [0])[0];\n\n const mapScrollToAnimationPropgress = (scrollPosition: number) => {\n const progress = (scrollPosition - currentStart) / animationHeight;\n if (progress > maxProgress) {\n currentStart = scrollPosition - animationHeight * maxProgress;\n } else if (progress < 0) {\n currentStart = scrollPosition;\n }\n currentStart = Math.max(currentStart, top);\n\n return clamp(progress, 0, maxProgress);\n };\n\n const getClosestStops = () => ({\n bottom: currentStart,\n top: currentStart + animationHeight * maxProgress,\n });\n\n return [mapScrollToAnimationPropgress, getClosestStops] as const;\n }\n\n let startPos =\n startTriggerPosition === 'start' || !startTriggerPosition.current\n ? 0\n : getRelativeOffset(scrollAdapter, startTriggerPosition.current) - bottom;\n\n let endPos: number;\n if (typeof endTriggerPosition === 'number') {\n endPos = startPos + endTriggerPosition;\n } else {\n endPos = endTriggerPosition?.current\n ? getRelativeOffset(scrollAdapter, endTriggerPosition.current) + heightDelta - bottom\n : startPos + animationHeight;\n }\n\n endPos += top;\n startPos += top;\n\n let endProgress = 1;\n if (endPos > maxScrollTop) {\n const scrollDelta = endPos - startPos;\n endProgress = (animationRanges.find((range) => range[0] * scrollDelta + startPos < maxScrollTop) ?? [0])[0];\n endPos = startPos + scrollDelta * endProgress;\n }\n\n return [remap([startPos, endPos], [0, endProgress]), () => ({ bottom: startPos, top: endPos })] as const;\n};\n\nconst isDividerVisible = (scrollTop: number, navBarTop: number, heightDelta: number, animationProgress: number) =>\n Math.ceil(navBarTop + heightDelta * animationProgress) < Math.floor(scrollTop);\n\nexport const useBindScrollToAnimationProgress = (\n scrollPosition: MotionValue<number>,\n getNavBarMetrics: () => NavBarMetrics,\n getAnimationRanges: () => AnimationRanges,\n scrollAdapter: ScrollAdapter,\n startTriggerPosition: 'start' | 'full-area' | RefObject<HTMLElement | null>,\n endTriggerPosition?: RefObject<HTMLElement | null> | number\n): [VoidFunction, () => { top: number; bottom: number }, MotionValue<number>, MotionStyle] => {\n const totalAnimationProgress = useMotionValue(0);\n const dividerVisibility = useMotionValue<'visible' | 'hidden'>('hidden');\n const dividerOffsetY = useMotionValue(0);\n const startTriggerPositionRef = useActualRef(startTriggerPosition);\n const endTriggerPositionRef = useActualRef(endTriggerPosition);\n const getClosestStopsRef: MutableRefObject<(() => { top: number; bottom: number }) | null> = useRef(null);\n\n const bindScrollToAnimation = useInitOnce(() => {\n let unsubscribe: VoidFunction | null = null;\n return () => {\n unsubscribe?.();\n const { heightDelta, top, animationHeight } = getNavBarMetrics();\n\n const animationRanges = [\n ...new Set<[number, number]>(\n [...getAnimationRanges().values()].map((range) => [\n range[0] / animationHeight,\n range[1] / animationHeight,\n ])\n ),\n ].sort((a, b) => b[0] - a[0]);\n\n const [mapFn, getClosestStops] = createScrollToAnimationProgressMapFn(\n getNavBarMetrics,\n scrollAdapter,\n animationRanges,\n startTriggerPositionRef.current,\n endTriggerPositionRef.current\n );\n getClosestStopsRef.current = getClosestStops;\n const updateDivider = (scrollPosition: number, totalAnimationProgress: number) => {\n dividerVisibility.set(\n isDividerVisible(scrollPosition, top, heightDelta, totalAnimationProgress) ? 'visible' : 'hidden'\n );\n dividerOffsetY.set(-heightDelta * totalAnimationProgress);\n };\n unsubscribe = scrollPosition.on('change', (value) => {\n totalAnimationProgress.set(mapFn(value));\n updateDivider(value, totalAnimationProgress.get());\n });\n\n totalAnimationProgress.set(mapFn(scrollPosition.get()));\n updateDivider(scrollPosition.get(), totalAnimationProgress.get());\n };\n });\n\n const getClosestStops = useInitOnce(() => () => {\n if (getClosestStopsRef.current) {\n return getClosestStopsRef.current();\n }\n\n return { bottom: 0, top: getNavBarMetrics().heightDelta };\n });\n\n return [\n bindScrollToAnimation,\n getClosestStops,\n totalAnimationProgress,\n { visibility: dividerVisibility, y: dividerOffsetY },\n ];\n};\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"useInert.js","sources":["../../src/internal/useInert.ts"],"sourcesContent":["import { RefCallback, useRef } from 'react';\n\nimport { useInitOnce } from '@hh.ru/magritte-ui-nav-bar/internal/utils';\n\nexport const useInert = (): [RefCallback<HTMLElement | null>, (isInert: boolean) => void] => {\n const currentInertRef = useRef(false);\n const elementRef = useRef<HTMLElement | null>(null);\n const refCallback: RefCallback<HTMLElement | null> = useInitOnce(() => (element) => {\n elementRef.current = element;\n if (element) {\n element.toggleAttribute('inert', currentInertRef.current);\n }\n });\n const setInert = useInitOnce(() => (isInert: boolean) => {\n if (isInert !== currentInertRef.current) {\n currentInertRef.current = isInert;\n if (elementRef.current) {\n elementRef.current.toggleAttribute('inert', currentInertRef.current);\n }\n }\n });\n\n return [refCallback, setInert];\n};\n"],"names":[],"mappings":";;;;AAIO,MAAM,QAAQ,GAAG,MAAoE;AACxF,IAAA,MAAM,eAAe,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;AACtC,IAAA,MAAM,UAAU,GAAG,MAAM,CAAqB,IAAI,CAAC,CAAC;IACpD,MAAM,WAAW,GAAoC,WAAW,CAAC,MAAM,CAAC,OAAO,KAAI;AAC/E,QAAA,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC;QAC7B,IAAI,OAAO,EAAE;YACT,OAAO,CAAC,eAAe,CAAC,OAAO,EAAE,eAAe,CAAC,OAAO,CAAC,CAAC;SAC7D;AACL,KAAC,CAAC,CAAC;IACH,MAAM,QAAQ,GAAG,WAAW,CAAC,MAAM,CAAC,OAAgB,KAAI;AACpD,QAAA,IAAI,OAAO,KAAK,eAAe,CAAC,OAAO,EAAE;AACrC,YAAA,eAAe,CAAC,OAAO,GAAG,OAAO,CAAC;AAClC,YAAA,IAAI,UAAU,CAAC,OAAO,EAAE;gBACpB,UAAU,CAAC,OAAO,CAAC,eAAe,CAAC,OAAO,EAAE,eAAe,CAAC,OAAO,CAAC,CAAC;aACxE;SACJ;AACL,KAAC,CAAC,CAAC;AAEH,IAAA,OAAO,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;AACnC;;;;"}
1
+ {"version":3,"file":"useInert.js","sources":["src/internal/useInert.ts"],"sourcesContent":["import { RefCallback, useRef } from 'react';\n\nimport { useInitOnce } from '@hh.ru/magritte-ui-nav-bar/internal/utils';\n\nexport const useInert = (): [RefCallback<HTMLElement | null>, (isInert: boolean) => void] => {\n const currentInertRef = useRef(false);\n const elementRef = useRef<HTMLElement | null>(null);\n const refCallback: RefCallback<HTMLElement | null> = useInitOnce(() => (element) => {\n elementRef.current = element;\n if (element) {\n element.toggleAttribute('inert', currentInertRef.current);\n }\n });\n const setInert = useInitOnce(() => (isInert: boolean) => {\n if (isInert !== currentInertRef.current) {\n currentInertRef.current = isInert;\n if (elementRef.current) {\n elementRef.current.toggleAttribute('inert', currentInertRef.current);\n }\n }\n });\n\n return [refCallback, setInert];\n};\n"],"names":[],"mappings":";;;;AAIO,MAAM,QAAQ,GAAG,MAAoE;AACxF,IAAA,MAAM,eAAe,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;AACtC,IAAA,MAAM,UAAU,GAAG,MAAM,CAAqB,IAAI,CAAC,CAAC;IACpD,MAAM,WAAW,GAAoC,WAAW,CAAC,MAAM,CAAC,OAAO,KAAI;AAC/E,QAAA,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC;QAC7B,IAAI,OAAO,EAAE;YACT,OAAO,CAAC,eAAe,CAAC,OAAO,EAAE,eAAe,CAAC,OAAO,CAAC,CAAC;SAC7D;AACL,KAAC,CAAC,CAAC;IACH,MAAM,QAAQ,GAAG,WAAW,CAAC,MAAM,CAAC,OAAgB,KAAI;AACpD,QAAA,IAAI,OAAO,KAAK,eAAe,CAAC,OAAO,EAAE;AACrC,YAAA,eAAe,CAAC,OAAO,GAAG,OAAO,CAAC;AAClC,YAAA,IAAI,UAAU,CAAC,OAAO,EAAE;gBACpB,UAAU,CAAC,OAAO,CAAC,eAAe,CAAC,OAAO,EAAE,eAAe,CAAC,OAAO,CAAC,CAAC;aACxE;SACJ;AACL,KAAC,CAAC,CAAC;AAEH,IAAA,OAAO,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;AACnC;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"useNavBarMetrics.js","sources":["../../src/internal/useNavBarMetrics.ts"],"sourcesContent":["import { type MutableRefObject, RefObject, useRef } from 'react';\n\nimport { type PaneStore } from '@hh.ru/magritte-ui-nav-bar/internal/PaneStore';\nimport { ScrollAdapter } from '@hh.ru/magritte-ui-nav-bar/internal/useScrollAdapter';\nimport { getRelativeOffset, useActualRef, useInitOnce } from '@hh.ru/magritte-ui-nav-bar/internal/utils';\n\nexport interface NavBarMetrics {\n heightDelta: number;\n top: number;\n bottom: number;\n animationHeight: number;\n}\n\nexport const useNavBarMetrics = (\n panesRegistry: Set<PaneStore>,\n rootRef: RefObject<HTMLElement>,\n scrollAdapter: ScrollAdapter\n): [() => NavBarMetrics, VoidFunction] => {\n const metricsRef: MutableRefObject<NavBarMetrics | null> = useRef(null);\n const panesRegistryRef = useActualRef(panesRegistry);\n\n const getNavBarMetrics = useInitOnce(() => () => {\n if (!metricsRef.current) {\n // расстояние от верхнего края NavBar до верхнего края контейнера со скроллом\n const top = getRelativeOffset(scrollAdapter, rootRef.current);\n const panes = [...panesRegistryRef.current.values()].map((pane) => pane.data);\n const heightDelta = panes.reduce(\n (delta, pane) =>\n delta +\n (pane.foldable\n ? (pane.startHeight ?? 0)\n : (pane.startHeight ?? 0) - (pane.endHeight ?? pane.startHeight ?? 0)),\n 0\n );\n const positions = panes\n .map((pane) => ({ top: pane.top ?? 0, bottom: (pane.top ?? 0) + (pane.startHeight ?? 0) }))\n .sort((a, b) => a.top - b.top);\n const height = (positions[positions.length - 1]?.bottom ?? 0) - (positions[0]?.top ?? 0);\n\n metricsRef.current = {\n heightDelta,\n top,\n bottom: top + height,\n animationHeight:\n heightDelta ||\n panes.filter((p) => p.animated).reduce((height, p) => height + (p.startHeight ?? 0), 0),\n };\n }\n\n return metricsRef.current;\n });\n\n const invalidateMetricsCache = useInitOnce(() => () => (metricsRef.current = null));\n\n return [getNavBarMetrics, invalidateMetricsCache];\n};\n"],"names":[],"mappings":";;;;AAaa,MAAA,gBAAgB,GAAG,CAC5B,aAA6B,EAC7B,OAA+B,EAC/B,aAA4B,KACS;AACrC,IAAA,MAAM,UAAU,GAA2C,MAAM,CAAC,IAAI,CAAC,CAAC;AACxE,IAAA,MAAM,gBAAgB,GAAG,YAAY,CAAC,aAAa,CAAC,CAAC;IAErD,MAAM,gBAAgB,GAAG,WAAW,CAAC,MAAM,MAAK;AAC5C,QAAA,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE;;YAErB,MAAM,GAAG,GAAG,iBAAiB,CAAC,aAAa,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;YAC9D,MAAM,KAAK,GAAG,CAAC,GAAG,gBAAgB,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,CAAC;AAC9E,YAAA,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,CAC5B,CAAC,KAAK,EAAE,IAAI,KACR,KAAK;iBACJ,IAAI,CAAC,QAAQ;AACV,uBAAG,IAAI,CAAC,WAAW,IAAI,CAAC;sBACtB,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,KAAK,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC,EAC9E,CAAC,CACJ,CAAC;YACF,MAAM,SAAS,GAAG,KAAK;AAClB,iBAAA,GAAG,CAAC,CAAC,IAAI,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,IAAI,CAAC,WAAW,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;AAC1F,iBAAA,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;YACnC,MAAM,MAAM,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,MAAM,IAAI,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;YAEzF,UAAU,CAAC,OAAO,GAAG;gBACjB,WAAW;gBACX,GAAG;gBACH,MAAM,EAAE,GAAG,GAAG,MAAM;AACpB,gBAAA,eAAe,EACX,WAAW;AACX,oBAAA,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,MAAM,IAAI,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;aAC9F,CAAC;SACL;QAED,OAAO,UAAU,CAAC,OAAO,CAAC;AAC9B,KAAC,CAAC,CAAC;AAEH,IAAA,MAAM,sBAAsB,GAAG,WAAW,CAAC,MAAM,OAAO,UAAU,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC;AAEpF,IAAA,OAAO,CAAC,gBAAgB,EAAE,sBAAsB,CAAC,CAAC;AACtD;;;;"}
1
+ {"version":3,"file":"useNavBarMetrics.js","sources":["src/internal/useNavBarMetrics.ts"],"sourcesContent":["import { type MutableRefObject, RefObject, useRef } from 'react';\n\nimport { type PaneStore } from '@hh.ru/magritte-ui-nav-bar/internal/PaneStore';\nimport { ScrollAdapter } from '@hh.ru/magritte-ui-nav-bar/internal/useScrollAdapter';\nimport { getRelativeOffset, useActualRef, useInitOnce } from '@hh.ru/magritte-ui-nav-bar/internal/utils';\n\nexport interface NavBarMetrics {\n heightDelta: number;\n top: number;\n bottom: number;\n animationHeight: number;\n}\n\nexport const useNavBarMetrics = (\n panesRegistry: Set<PaneStore>,\n rootRef: RefObject<HTMLElement>,\n scrollAdapter: ScrollAdapter\n): [() => NavBarMetrics, VoidFunction] => {\n const metricsRef: MutableRefObject<NavBarMetrics | null> = useRef(null);\n const panesRegistryRef = useActualRef(panesRegistry);\n\n const getNavBarMetrics = useInitOnce(() => () => {\n if (!metricsRef.current) {\n // расстояние от верхнего края NavBar до верхнего края контейнера со скроллом\n const top = getRelativeOffset(scrollAdapter, rootRef.current);\n const panes = [...panesRegistryRef.current.values()].map((pane) => pane.data);\n const heightDelta = panes.reduce(\n (delta, pane) =>\n delta +\n (pane.foldable\n ? (pane.startHeight ?? 0)\n : (pane.startHeight ?? 0) - (pane.endHeight ?? pane.startHeight ?? 0)),\n 0\n );\n const positions = panes\n .map((pane) => ({ top: pane.top ?? 0, bottom: (pane.top ?? 0) + (pane.startHeight ?? 0) }))\n .sort((a, b) => a.top - b.top);\n const height = (positions[positions.length - 1]?.bottom ?? 0) - (positions[0]?.top ?? 0);\n\n metricsRef.current = {\n heightDelta,\n top,\n bottom: top + height,\n animationHeight:\n heightDelta ||\n panes.filter((p) => p.animated).reduce((height, p) => height + (p.startHeight ?? 0), 0),\n };\n }\n\n return metricsRef.current;\n });\n\n const invalidateMetricsCache = useInitOnce(() => () => (metricsRef.current = null));\n\n return [getNavBarMetrics, invalidateMetricsCache];\n};\n"],"names":[],"mappings":";;;;AAaa,MAAA,gBAAgB,GAAG,CAC5B,aAA6B,EAC7B,OAA+B,EAC/B,aAA4B,KACS;AACrC,IAAA,MAAM,UAAU,GAA2C,MAAM,CAAC,IAAI,CAAC,CAAC;AACxE,IAAA,MAAM,gBAAgB,GAAG,YAAY,CAAC,aAAa,CAAC,CAAC;IAErD,MAAM,gBAAgB,GAAG,WAAW,CAAC,MAAM,MAAK;AAC5C,QAAA,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE;;YAErB,MAAM,GAAG,GAAG,iBAAiB,CAAC,aAAa,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;YAC9D,MAAM,KAAK,GAAG,CAAC,GAAG,gBAAgB,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,CAAC;AAC9E,YAAA,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,CAC5B,CAAC,KAAK,EAAE,IAAI,KACR,KAAK;iBACJ,IAAI,CAAC,QAAQ;AACV,uBAAG,IAAI,CAAC,WAAW,IAAI,CAAC;sBACtB,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,KAAK,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC,EAC9E,CAAC,CACJ,CAAC;YACF,MAAM,SAAS,GAAG,KAAK;AAClB,iBAAA,GAAG,CAAC,CAAC,IAAI,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,IAAI,CAAC,WAAW,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;AAC1F,iBAAA,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;YACnC,MAAM,MAAM,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,MAAM,IAAI,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;YAEzF,UAAU,CAAC,OAAO,GAAG;gBACjB,WAAW;gBACX,GAAG;gBACH,MAAM,EAAE,GAAG,GAAG,MAAM;AACpB,gBAAA,eAAe,EACX,WAAW;AACX,oBAAA,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,MAAM,IAAI,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;aAC9F,CAAC;SACL;QAED,OAAO,UAAU,CAAC,OAAO,CAAC;AAC9B,KAAC,CAAC,CAAC;AAEH,IAAA,MAAM,sBAAsB,GAAG,WAAW,CAAC,MAAM,OAAO,UAAU,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC;AAEpF,IAAA,OAAO,CAAC,gBAAgB,EAAE,sBAAsB,CAAC,CAAC;AACtD;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"useResetFocus.js","sources":["../../src/internal/useResetFocus.ts"],"sourcesContent":["import { type MutableRefObject, useRef, type RefObject, useEffect } from 'react';\nimport { type MotionValue } from 'motion/react';\n\nexport const useResetFocus = (rootRef: RefObject<HTMLElement>, animationProgress: MotionValue<number>): void => {\n const hasFocus: MutableRefObject<boolean> = useRef(false);\n\n useEffect(() => {\n if (!rootRef.current) {\n return void 0;\n }\n\n const focusHandler = () => (hasFocus.current = true);\n const element = rootRef.current;\n element.addEventListener('focusin', focusHandler);\n const unsubscribe = animationProgress.on('change', () => {\n if (!hasFocus.current || !rootRef.current) {\n return;\n }\n\n const activeElement = document.activeElement as HTMLElement;\n hasFocus.current = false;\n if (activeElement && rootRef.current.contains(activeElement) && typeof activeElement.blur === 'function') {\n activeElement.blur();\n }\n });\n\n return () => {\n unsubscribe();\n element.removeEventListener('focusin', focusHandler);\n };\n }, [animationProgress, rootRef]);\n};\n"],"names":[],"mappings":";;MAGa,aAAa,GAAG,CAAC,OAA+B,EAAE,iBAAsC,KAAU;AAC3G,IAAA,MAAM,QAAQ,GAA8B,MAAM,CAAC,KAAK,CAAC,CAAC;IAE1D,SAAS,CAAC,MAAK;AACX,QAAA,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE;YAClB,OAAO,KAAK,CAAC,CAAC;SACjB;AAED,QAAA,MAAM,YAAY,GAAG,OAAO,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;AACrD,QAAA,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;AAChC,QAAA,OAAO,CAAC,gBAAgB,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;QAClD,MAAM,WAAW,GAAG,iBAAiB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAK;YACpD,IAAI,CAAC,QAAQ,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE;gBACvC,OAAO;aACV;AAED,YAAA,MAAM,aAAa,GAAG,QAAQ,CAAC,aAA4B,CAAC;AAC5D,YAAA,QAAQ,CAAC,OAAO,GAAG,KAAK,CAAC;AACzB,YAAA,IAAI,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,OAAO,aAAa,CAAC,IAAI,KAAK,UAAU,EAAE;gBACtG,aAAa,CAAC,IAAI,EAAE,CAAC;aACxB;AACL,SAAC,CAAC,CAAC;AAEH,QAAA,OAAO,MAAK;AACR,YAAA,WAAW,EAAE,CAAC;AACd,YAAA,OAAO,CAAC,mBAAmB,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;AACzD,SAAC,CAAC;AACN,KAAC,EAAE,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAAC,CAAC;AACrC;;;;"}
1
+ {"version":3,"file":"useResetFocus.js","sources":["src/internal/useResetFocus.ts"],"sourcesContent":["import { type MutableRefObject, useRef, type RefObject, useEffect } from 'react';\nimport { type MotionValue } from 'motion/react';\n\nexport const useResetFocus = (rootRef: RefObject<HTMLElement>, animationProgress: MotionValue<number>): void => {\n const hasFocus: MutableRefObject<boolean> = useRef(false);\n\n useEffect(() => {\n if (!rootRef.current) {\n return void 0;\n }\n\n const focusHandler = () => (hasFocus.current = true);\n const element = rootRef.current;\n element.addEventListener('focusin', focusHandler);\n const unsubscribe = animationProgress.on('change', () => {\n if (!hasFocus.current || !rootRef.current) {\n return;\n }\n\n const activeElement = document.activeElement as HTMLElement;\n hasFocus.current = false;\n if (activeElement && rootRef.current.contains(activeElement) && typeof activeElement.blur === 'function') {\n activeElement.blur();\n }\n });\n\n return () => {\n unsubscribe();\n element.removeEventListener('focusin', focusHandler);\n };\n }, [animationProgress, rootRef]);\n};\n"],"names":[],"mappings":";;MAGa,aAAa,GAAG,CAAC,OAA+B,EAAE,iBAAsC,KAAU;AAC3G,IAAA,MAAM,QAAQ,GAA8B,MAAM,CAAC,KAAK,CAAC,CAAC;IAE1D,SAAS,CAAC,MAAK;AACX,QAAA,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE;YAClB,OAAO,KAAK,CAAC,CAAC;SACjB;AAED,QAAA,MAAM,YAAY,GAAG,OAAO,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;AACrD,QAAA,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;AAChC,QAAA,OAAO,CAAC,gBAAgB,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;QAClD,MAAM,WAAW,GAAG,iBAAiB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAK;YACpD,IAAI,CAAC,QAAQ,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE;gBACvC,OAAO;aACV;AAED,YAAA,MAAM,aAAa,GAAG,QAAQ,CAAC,aAA4B,CAAC;AAC5D,YAAA,QAAQ,CAAC,OAAO,GAAG,KAAK,CAAC;AACzB,YAAA,IAAI,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,OAAO,aAAa,CAAC,IAAI,KAAK,UAAU,EAAE;gBACtG,aAAa,CAAC,IAAI,EAAE,CAAC;aACxB;AACL,SAAC,CAAC,CAAC;AAEH,QAAA,OAAO,MAAK;AACR,YAAA,WAAW,EAAE,CAAC;AACd,YAAA,OAAO,CAAC,mBAAmB,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;AACzD,SAAC,CAAC;AACN,KAAC,EAAE,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAAC,CAAC;AACrC;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"useScrollAdapter.js","sources":["../../src/internal/useScrollAdapter.ts"],"sourcesContent":["import { MutableRefObject, useEffect, useLayoutEffect, useRef, type RefObject } from 'react';\nimport { type MotionValue, useMotionValue } from 'motion/react';\n\nimport { useScrollContext } from '@hh.ru/magritte-internal-custom-scroll';\nimport { findScrollContainer, useInitOnce } from '@hh.ru/magritte-ui-nav-bar/internal/utils';\n\nconst createSubFn = (store: Set<VoidFunction>) => (handler: VoidFunction) => {\n store.add(handler);\n return () => store.delete(handler);\n};\n\nexport interface ScrollAdapter {\n getMaxScrollTop: () => number;\n getScrollTop: () => number;\n setScrollTop: (pos: number) => void;\n onScroll: (cb: VoidFunction) => VoidFunction;\n onTouchStart: (cb: VoidFunction) => VoidFunction;\n onTouchEnd: (cb: VoidFunction) => VoidFunction;\n onResize: (cb: VoidFunction) => VoidFunction;\n scrollContainer: RefObject<Element>;\n hasTouchSupport: () => boolean;\n breakScrollSession: VoidFunction;\n}\n\nexport const useScrollAdapter = (elementRef: RefObject<HTMLElement>): [ScrollAdapter, MotionValue<number>] => {\n const scrollContext = useScrollContext();\n const scrollContainerRef = useRef<ReturnType<typeof findScrollContainer>>();\n const scrollPosition = useMotionValue(0);\n const mediaQueryRef: MutableRefObject<MediaQueryList | null> = useRef(null);\n const scrollContainerElRef: MutableRefObject<null | Element> = useRef(null);\n\n const [subscriptionsManager, notifier] = useInitOnce(() => {\n const touchStartHandlers = new Set<VoidFunction>();\n const touchEndHandlers = new Set<VoidFunction>();\n const scrollHandlers = new Set<VoidFunction>();\n const resizeHandlers = new Set<VoidFunction>();\n const subscriptionsManager = {\n onTouchStart: createSubFn(touchStartHandlers),\n onTouchEnd: createSubFn(touchEndHandlers),\n onScroll: createSubFn(scrollHandlers),\n onResize: createSubFn(resizeHandlers),\n };\n\n const notifier = {\n notifyTouchStart: () => touchStartHandlers.forEach((fn) => fn()),\n notifyTouchEnd: () => touchEndHandlers.forEach((fn) => fn()),\n notifyScroll: () => scrollHandlers.forEach((fn) => fn()),\n notifyResize: () => resizeHandlers.forEach((fn) => fn()),\n };\n\n return [subscriptionsManager, notifier];\n });\n\n useEffect(() => {\n if (window && 'matchMedia' in window) {\n mediaQueryRef.current = window.matchMedia('(pointer:coarse)');\n }\n }, []);\n\n const scrollAdapter = useInitOnce(() => {\n const hasTouchSupport = () => mediaQueryRef.current?.matches ?? false;\n if (scrollContext) {\n const subscriptions = [\n scrollContext.onTouchStart(notifier.notifyTouchStart),\n scrollContext.onTouchEnd(notifier.notifyTouchEnd),\n scrollContext.onScroll(notifier.notifyScroll),\n ];\n return {\n ...scrollContext,\n scrollContainer: scrollContext.wrapperRef,\n ...subscriptionsManager,\n unsubscribe: () => subscriptions.forEach((fn) => fn()),\n hasTouchSupport,\n // не нужно для ненативного скролла\n breakScrollSession: (): void => void 0,\n };\n }\n return {\n getMaxScrollTop: () => {\n if (!scrollContainerRef.current) {\n return 0;\n }\n\n const height =\n scrollContainerRef.current.mode === 'window'\n ? scrollContainerRef.current.infoProvider.clientHeight\n : scrollContainerRef.current.infoProvider.offsetHeight;\n return scrollContainerRef.current.infoProvider.scrollHeight - height;\n },\n getScrollTop: () =>\n scrollContainerRef.current ? Math.max(scrollContainerRef.current.infoProvider.scrollTop, 0) : 0,\n setScrollTop: (pos: number) => {\n if (scrollContainerRef.current) {\n scrollContainerRef.current.infoProvider.scrollTop = pos;\n }\n },\n scrollContainer: scrollContainerElRef,\n ...subscriptionsManager,\n unsubscribe: (): void => void 0,\n breakScrollSession: () => {\n if (scrollContainerRef.current?.mode !== 'element' || !scrollContainerElRef.current) {\n return;\n }\n\n const element = scrollContainerElRef.current as HTMLElement;\n const overflowY = element.style.overflowY;\n element.style.overflowY = 'hidden';\n // force layout\n void element.offsetHeight;\n element.style.overflowY = overflowY;\n },\n hasTouchSupport,\n };\n }) satisfies ScrollAdapter;\n\n useLayoutEffect(() => {\n const subscriptions: VoidFunction[] = [];\n if (!scrollContext && elementRef.current) {\n scrollContainerRef.current = findScrollContainer(elementRef.current);\n const subscriptionsController = new AbortController();\n const signal = subscriptionsController.signal;\n const eventsProvider = scrollContainerRef.current.eventsProvider;\n eventsProvider.addEventListener('scroll', notifier.notifyScroll, { passive: true, signal });\n eventsProvider.addEventListener('touchstart', notifier.notifyTouchStart, { passive: true, signal });\n eventsProvider.addEventListener('touchend', notifier.notifyTouchEnd, { passive: true, signal });\n scrollContainerElRef.current = scrollContainerRef.current.infoProvider;\n subscriptions.push(() => subscriptionsController.abort());\n }\n\n if (scrollAdapter.scrollContainer.current) {\n const observer = new ResizeObserver(notifier.notifyResize);\n observer.observe(scrollAdapter.scrollContainer.current);\n subscriptions.push(() => observer.disconnect());\n }\n\n return () => subscriptions.forEach((fn) => fn());\n }, [notifier, elementRef, scrollContext, scrollAdapter, scrollPosition]);\n\n return [scrollAdapter, scrollPosition];\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useScrollAdapter.js","sources":["src/internal/useScrollAdapter.ts"],"sourcesContent":["import { MutableRefObject, useEffect, useLayoutEffect, useRef, type RefObject } from 'react';\nimport { type MotionValue, useMotionValue } from 'motion/react';\n\nimport { useScrollContext } from '@hh.ru/magritte-internal-custom-scroll';\nimport { findScrollContainer, useInitOnce } from '@hh.ru/magritte-ui-nav-bar/internal/utils';\n\nconst createSubFn = (store: Set<VoidFunction>) => (handler: VoidFunction) => {\n store.add(handler);\n return () => store.delete(handler);\n};\n\nexport interface ScrollAdapter {\n getMaxScrollTop: () => number;\n getScrollTop: () => number;\n setScrollTop: (pos: number) => void;\n onScroll: (cb: VoidFunction) => VoidFunction;\n onTouchStart: (cb: VoidFunction) => VoidFunction;\n onTouchEnd: (cb: VoidFunction) => VoidFunction;\n onResize: (cb: VoidFunction) => VoidFunction;\n scrollContainer: RefObject<Element>;\n hasTouchSupport: () => boolean;\n breakScrollSession: VoidFunction;\n}\n\nexport const useScrollAdapter = (elementRef: RefObject<HTMLElement>): [ScrollAdapter, MotionValue<number>] => {\n const scrollContext = useScrollContext();\n const scrollContainerRef = useRef<ReturnType<typeof findScrollContainer>>();\n const scrollPosition = useMotionValue(0);\n const mediaQueryRef: MutableRefObject<MediaQueryList | null> = useRef(null);\n const scrollContainerElRef: MutableRefObject<null | Element> = useRef(null);\n\n const [subscriptionsManager, notifier] = useInitOnce(() => {\n const touchStartHandlers = new Set<VoidFunction>();\n const touchEndHandlers = new Set<VoidFunction>();\n const scrollHandlers = new Set<VoidFunction>();\n const resizeHandlers = new Set<VoidFunction>();\n const subscriptionsManager = {\n onTouchStart: createSubFn(touchStartHandlers),\n onTouchEnd: createSubFn(touchEndHandlers),\n onScroll: createSubFn(scrollHandlers),\n onResize: createSubFn(resizeHandlers),\n };\n\n const notifier = {\n notifyTouchStart: () => touchStartHandlers.forEach((fn) => fn()),\n notifyTouchEnd: () => touchEndHandlers.forEach((fn) => fn()),\n notifyScroll: () => scrollHandlers.forEach((fn) => fn()),\n notifyResize: () => resizeHandlers.forEach((fn) => fn()),\n };\n\n return [subscriptionsManager, notifier];\n });\n\n useEffect(() => {\n if (window && 'matchMedia' in window) {\n mediaQueryRef.current = window.matchMedia('(pointer:coarse)');\n }\n }, []);\n\n const scrollAdapter = useInitOnce(() => {\n const hasTouchSupport = () => mediaQueryRef.current?.matches ?? false;\n if (scrollContext) {\n const subscriptions = [\n scrollContext.onTouchStart(notifier.notifyTouchStart),\n scrollContext.onTouchEnd(notifier.notifyTouchEnd),\n scrollContext.onScroll(notifier.notifyScroll),\n ];\n return {\n ...scrollContext,\n scrollContainer: scrollContext.wrapperRef,\n ...subscriptionsManager,\n unsubscribe: () => subscriptions.forEach((fn) => fn()),\n hasTouchSupport,\n // не нужно для ненативного скролла\n breakScrollSession: (): void => void 0,\n };\n }\n return {\n getMaxScrollTop: () => {\n if (!scrollContainerRef.current) {\n return 0;\n }\n\n const height =\n scrollContainerRef.current.mode === 'window'\n ? scrollContainerRef.current.infoProvider.clientHeight\n : scrollContainerRef.current.infoProvider.offsetHeight;\n return scrollContainerRef.current.infoProvider.scrollHeight - height;\n },\n getScrollTop: () =>\n scrollContainerRef.current ? Math.max(scrollContainerRef.current.infoProvider.scrollTop, 0) : 0,\n setScrollTop: (pos: number) => {\n if (scrollContainerRef.current) {\n scrollContainerRef.current.infoProvider.scrollTop = pos;\n }\n },\n scrollContainer: scrollContainerElRef,\n ...subscriptionsManager,\n unsubscribe: (): void => void 0,\n breakScrollSession: () => {\n if (scrollContainerRef.current?.mode !== 'element' || !scrollContainerElRef.current) {\n return;\n }\n\n const element = scrollContainerElRef.current as HTMLElement;\n const overflowY = element.style.overflowY;\n element.style.overflowY = 'hidden';\n // force layout\n void element.offsetHeight;\n element.style.overflowY = overflowY;\n },\n hasTouchSupport,\n };\n }) satisfies ScrollAdapter;\n\n useLayoutEffect(() => {\n const subscriptions: VoidFunction[] = [];\n if (!scrollContext && elementRef.current) {\n scrollContainerRef.current = findScrollContainer(elementRef.current);\n const subscriptionsController = new AbortController();\n const signal = subscriptionsController.signal;\n const eventsProvider = scrollContainerRef.current.eventsProvider;\n eventsProvider.addEventListener('scroll', notifier.notifyScroll, { passive: true, signal });\n eventsProvider.addEventListener('touchstart', notifier.notifyTouchStart, { passive: true, signal });\n eventsProvider.addEventListener('touchend', notifier.notifyTouchEnd, { passive: true, signal });\n scrollContainerElRef.current = scrollContainerRef.current.infoProvider;\n subscriptions.push(() => subscriptionsController.abort());\n }\n\n if (scrollAdapter.scrollContainer.current) {\n const observer = new ResizeObserver(notifier.notifyResize);\n observer.observe(scrollAdapter.scrollContainer.current);\n subscriptions.push(() => observer.disconnect());\n }\n\n return () => subscriptions.forEach((fn) => fn());\n }, [notifier, elementRef, scrollContext, scrollAdapter, scrollPosition]);\n\n return [scrollAdapter, scrollPosition];\n};\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"useSnapScroll.js","sources":["../../src/internal/useSnapScroll.ts"],"sourcesContent":["import { type MutableRefObject, useEffect, useRef } from 'react';\nimport { useMotionValue, animate, type MotionValue, AnimationPlaybackControlsWithThen } from 'motion/react';\n\nimport { type ScrollAdapter } from '@hh.ru/magritte-ui-nav-bar/internal/useScrollAdapter';\nimport { useActualRef, useInitOnce } from '@hh.ru/magritte-ui-nav-bar/internal/utils';\n\nconst AUTOSCROLL_SPEED = 1600; // px/sec\nconst SETTLE_FRAMES_COUNT = 10;\n\nexport const useSnapScroll = (\n scrollPosition: MotionValue<number>,\n totalAnimationProgress: MotionValue<number>,\n scrollAdapter: ScrollAdapter,\n getAnimationStops: () => { top: number; bottom: number },\n enableScrollSnap: boolean\n): VoidFunction => {\n const scrollAnimationController = useMotionValue(scrollPosition.get());\n const prevStopRef = useRef<'top' | 'bottom'>(\n scrollAdapter.getScrollTop() === getAnimationStops().top ? 'top' : 'bottom'\n );\n const touchedRef = useRef(false);\n const lastDirectionRef: MutableRefObject<'top' | 'bottom' | null> = useRef(null);\n const animationControlsRef: MutableRefObject<AnimationPlaybackControlsWithThen | null> = useRef(null);\n const enableScrollSnapRef = useActualRef(enableScrollSnap);\n const prevScrollPositionRef = useRef(0);\n\n const stopAnimation = useInitOnce(() => () => {\n if (animationControlsRef.current) {\n animationControlsRef.current.stop();\n animationControlsRef.current = null;\n }\n });\n\n const snapScroll = useInitOnce(() => () => {\n if (touchedRef.current || animationControlsRef.current || !enableScrollSnapRef.current) {\n return;\n }\n\n const stops = getAnimationStops();\n const target = lastDirectionRef.current === null ? prevStopRef.current : lastDirectionRef.current;\n const targetPos = stops[target];\n const currentPos = scrollAdapter.getScrollTop();\n\n // за границами анимации навбара, т.е. он или полностью раскрыт или схлопнут\n if (currentPos >= stops.top || currentPos <= stops.bottom) {\n return;\n }\n\n // ios safari не предоставляет достаточно данных чтобы точно определить что пользователь перестал\n // взаимодействовать со страницей, поэтому мы принимаем решение о запуске анимации скролла по косвенному\n // признаку - отсутствие события скролл в течение SETTLE_FRAMES_COUNT.\n // Однако в случае если пользователь поскроллил страницу и задержал палец на экране анимация тоже будет\n // запущена. Если после этого положение пальца на экране изменится скролл моментально вернется к положению\n // которое было до анимации, после чего снова будет запущена анимация и так по кругу.\n // Единственное что тут можно сделать - после запуска анимации прекратить \"скролл сессию\", чтобы при движении\n // пальца позиция скролла не менялась до начала слежующей скролл сессии. К сожалению это работает только\n // если скролл происходит в каком то контейнере. Если же скроллится вся страница, то скролл сессию нельзя\n // разорвать без видимых для пользователя изменений в лейауте.\n scrollAdapter.breakScrollSession();\n scrollAnimationController.jump(currentPos);\n prevScrollPositionRef.current = currentPos;\n const controls = animate(scrollAnimationController, targetPos, {\n duration: Math.abs(scrollAnimationController.get() - targetPos) / AUTOSCROLL_SPEED,\n ease: 'easeOut',\n });\n animationControlsRef.current = controls;\n controls\n .then(() => {\n lastDirectionRef.current = null;\n prevStopRef.current = target;\n animationControlsRef.current = null;\n })\n .catch(() => {\n prevStopRef.current = target;\n animationControlsRef.current = null;\n });\n });\n\n const checkSettled = useInitOnce(() => {\n let frames = 0;\n let run = false;\n return () => {\n frames = SETTLE_FRAMES_COUNT;\n if (run) {\n return;\n }\n\n run = true;\n const step = () => {\n frames -= 1;\n if (frames === 0) {\n run = false;\n snapScroll();\n return;\n }\n\n requestAnimationFrame(step);\n };\n\n requestAnimationFrame(step);\n };\n });\n\n const onTouchEnd = useInitOnce(() => () => {\n touchedRef.current = false;\n stopAnimation();\n checkSettled();\n });\n\n useEffect(() => {\n const subscriptions = [\n scrollAdapter.onTouchStart(() => {\n stopAnimation();\n touchedRef.current = true;\n }),\n scrollAdapter.onTouchEnd(onTouchEnd),\n scrollAdapter.onScroll(() => {\n const scrollTop = scrollAdapter.getScrollTop();\n const dir = Math.sign(scrollTop - prevScrollPositionRef.current);\n if (animationControlsRef.current) {\n // ios safari шлет только первый touchstart/touchend в случае скролла который происходит\n // в несколько жестов. Т.е. на ios мы не можем положиться на touchedRef для прерывания анимации.\n // Вместо этого мы проверяем не изменилось ли направление скролла и не \"перескочил\" ли скролл\n // значение анимируемой переменной в направлении скролла. Оба этих факта говорят о вмешательстве\n // пользователя в процесс скролла, а значит надо прервать анимацию. Можно было бы сделать\n // проще и проверять расстояние между значением анимируемой переменной и фактическим положением\n // скролла, но сафари после установки scrollTop может прислать событие со старым значением\n // scrollTop, и только потом с новым, что делает такой подход неприменимым.\n const isDirChanged =\n dir !== 0 &&\n lastDirectionRef.current &&\n (lastDirectionRef.current === 'top' ? dir < 0 : dir > 0);\n const isOutOfRange =\n lastDirectionRef.current &&\n (lastDirectionRef.current === 'top'\n ? scrollTop > scrollAnimationController.get() + 1\n : scrollTop < scrollAnimationController.get() - 1);\n if (isDirChanged || isOutOfRange) {\n stopAnimation();\n }\n }\n prevScrollPositionRef.current = scrollTop;\n // Не обновляем MotionValue вместе со скроллом если происходит анимация скролла\n // потому что ios safari шлет события скролла с низкой частотой, из-за чего вся анимация может\n // выглядеть дерганно. Вместо этого ниже есть прямая связь scrollAnimationController -> scrollPosition\n !animationControlsRef.current && scrollPosition.set(scrollTop);\n if (dir !== 0) {\n lastDirectionRef.current = dir > 0 ? 'top' : 'bottom';\n }\n }),\n totalAnimationProgress.on('change', checkSettled),\n scrollAnimationController.on('change', (value) => {\n scrollAdapter.setScrollTop(value);\n scrollPosition.set(value);\n }),\n ];\n\n return () => subscriptions.forEach((unsubscribe) => unsubscribe());\n }, [\n checkSettled,\n scrollAdapter,\n scrollPosition,\n onTouchEnd,\n scrollAnimationController,\n stopAnimation,\n totalAnimationProgress,\n ]);\n\n return snapScroll;\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useSnapScroll.js","sources":["src/internal/useSnapScroll.ts"],"sourcesContent":["import { type MutableRefObject, useEffect, useRef } from 'react';\nimport { useMotionValue, animate, type MotionValue, AnimationPlaybackControlsWithThen } from 'motion/react';\n\nimport { type ScrollAdapter } from '@hh.ru/magritte-ui-nav-bar/internal/useScrollAdapter';\nimport { useActualRef, useInitOnce } from '@hh.ru/magritte-ui-nav-bar/internal/utils';\n\nconst AUTOSCROLL_SPEED = 1600; // px/sec\nconst SETTLE_FRAMES_COUNT = 10;\n\nexport const useSnapScroll = (\n scrollPosition: MotionValue<number>,\n totalAnimationProgress: MotionValue<number>,\n scrollAdapter: ScrollAdapter,\n getAnimationStops: () => { top: number; bottom: number },\n enableScrollSnap: boolean\n): VoidFunction => {\n const scrollAnimationController = useMotionValue(scrollPosition.get());\n const prevStopRef = useRef<'top' | 'bottom'>(\n scrollAdapter.getScrollTop() === getAnimationStops().top ? 'top' : 'bottom'\n );\n const touchedRef = useRef(false);\n const lastDirectionRef: MutableRefObject<'top' | 'bottom' | null> = useRef(null);\n const animationControlsRef: MutableRefObject<AnimationPlaybackControlsWithThen | null> = useRef(null);\n const enableScrollSnapRef = useActualRef(enableScrollSnap);\n const prevScrollPositionRef = useRef(0);\n\n const stopAnimation = useInitOnce(() => () => {\n if (animationControlsRef.current) {\n animationControlsRef.current.stop();\n animationControlsRef.current = null;\n }\n });\n\n const snapScroll = useInitOnce(() => () => {\n if (touchedRef.current || animationControlsRef.current || !enableScrollSnapRef.current) {\n return;\n }\n\n const stops = getAnimationStops();\n const target = lastDirectionRef.current === null ? prevStopRef.current : lastDirectionRef.current;\n const targetPos = stops[target];\n const currentPos = scrollAdapter.getScrollTop();\n\n // за границами анимации навбара, т.е. он или полностью раскрыт или схлопнут\n if (currentPos >= stops.top || currentPos <= stops.bottom) {\n return;\n }\n\n // ios safari не предоставляет достаточно данных чтобы точно определить что пользователь перестал\n // взаимодействовать со страницей, поэтому мы принимаем решение о запуске анимации скролла по косвенному\n // признаку - отсутствие события скролл в течение SETTLE_FRAMES_COUNT.\n // Однако в случае если пользователь поскроллил страницу и задержал палец на экране анимация тоже будет\n // запущена. Если после этого положение пальца на экране изменится скролл моментально вернется к положению\n // которое было до анимации, после чего снова будет запущена анимация и так по кругу.\n // Единственное что тут можно сделать - после запуска анимации прекратить \"скролл сессию\", чтобы при движении\n // пальца позиция скролла не менялась до начала слежующей скролл сессии. К сожалению это работает только\n // если скролл происходит в каком то контейнере. Если же скроллится вся страница, то скролл сессию нельзя\n // разорвать без видимых для пользователя изменений в лейауте.\n scrollAdapter.breakScrollSession();\n scrollAnimationController.jump(currentPos);\n prevScrollPositionRef.current = currentPos;\n const controls = animate(scrollAnimationController, targetPos, {\n duration: Math.abs(scrollAnimationController.get() - targetPos) / AUTOSCROLL_SPEED,\n ease: 'easeOut',\n });\n animationControlsRef.current = controls;\n controls\n .then(() => {\n lastDirectionRef.current = null;\n prevStopRef.current = target;\n animationControlsRef.current = null;\n })\n .catch(() => {\n prevStopRef.current = target;\n animationControlsRef.current = null;\n });\n });\n\n const checkSettled = useInitOnce(() => {\n let frames = 0;\n let run = false;\n return () => {\n frames = SETTLE_FRAMES_COUNT;\n if (run) {\n return;\n }\n\n run = true;\n const step = () => {\n frames -= 1;\n if (frames === 0) {\n run = false;\n snapScroll();\n return;\n }\n\n requestAnimationFrame(step);\n };\n\n requestAnimationFrame(step);\n };\n });\n\n const onTouchEnd = useInitOnce(() => () => {\n touchedRef.current = false;\n stopAnimation();\n checkSettled();\n });\n\n useEffect(() => {\n const subscriptions = [\n scrollAdapter.onTouchStart(() => {\n stopAnimation();\n touchedRef.current = true;\n }),\n scrollAdapter.onTouchEnd(onTouchEnd),\n scrollAdapter.onScroll(() => {\n const scrollTop = scrollAdapter.getScrollTop();\n const dir = Math.sign(scrollTop - prevScrollPositionRef.current);\n if (animationControlsRef.current) {\n // ios safari шлет только первый touchstart/touchend в случае скролла который происходит\n // в несколько жестов. Т.е. на ios мы не можем положиться на touchedRef для прерывания анимации.\n // Вместо этого мы проверяем не изменилось ли направление скролла и не \"перескочил\" ли скролл\n // значение анимируемой переменной в направлении скролла. Оба этих факта говорят о вмешательстве\n // пользователя в процесс скролла, а значит надо прервать анимацию. Можно было бы сделать\n // проще и проверять расстояние между значением анимируемой переменной и фактическим положением\n // скролла, но сафари после установки scrollTop может прислать событие со старым значением\n // scrollTop, и только потом с новым, что делает такой подход неприменимым.\n const isDirChanged =\n dir !== 0 &&\n lastDirectionRef.current &&\n (lastDirectionRef.current === 'top' ? dir < 0 : dir > 0);\n const isOutOfRange =\n lastDirectionRef.current &&\n (lastDirectionRef.current === 'top'\n ? scrollTop > scrollAnimationController.get() + 1\n : scrollTop < scrollAnimationController.get() - 1);\n if (isDirChanged || isOutOfRange) {\n stopAnimation();\n }\n }\n prevScrollPositionRef.current = scrollTop;\n // Не обновляем MotionValue вместе со скроллом если происходит анимация скролла\n // потому что ios safari шлет события скролла с низкой частотой, из-за чего вся анимация может\n // выглядеть дерганно. Вместо этого ниже есть прямая связь scrollAnimationController -> scrollPosition\n !animationControlsRef.current && scrollPosition.set(scrollTop);\n if (dir !== 0) {\n lastDirectionRef.current = dir > 0 ? 'top' : 'bottom';\n }\n }),\n totalAnimationProgress.on('change', checkSettled),\n scrollAnimationController.on('change', (value) => {\n scrollAdapter.setScrollTop(value);\n scrollPosition.set(value);\n }),\n ];\n\n return () => subscriptions.forEach((unsubscribe) => unsubscribe());\n }, [\n checkSettled,\n scrollAdapter,\n scrollPosition,\n onTouchEnd,\n scrollAnimationController,\n stopAnimation,\n totalAnimationProgress,\n ]);\n\n return snapScroll;\n};\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"useSyncMotionValue.js","sources":["../../src/internal/useSyncMotionValue.ts"],"sourcesContent":["import { useEffect } from 'react';\nimport { type MotionValue } from 'motion/react';\n\nexport const useSyncMotionValue = (source: MotionValue<number>, destination?: MotionValue<number>): void => {\n useEffect(\n () => (destination ? source.on('change', (value) => destination.set(value)) : void 0),\n [source, destination]\n );\n};\n"],"names":[],"mappings":";;MAGa,kBAAkB,GAAG,CAAC,MAA2B,EAAE,WAAiC,KAAU;AACvG,IAAA,SAAS,CACL,OAAO,WAAW,GAAG,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,KAAK,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,EACrF,CAAC,MAAM,EAAE,WAAW,CAAC,CACxB,CAAC;AACN;;;;"}
1
+ {"version":3,"file":"useSyncMotionValue.js","sources":["src/internal/useSyncMotionValue.ts"],"sourcesContent":["import { useEffect } from 'react';\nimport { type MotionValue } from 'motion/react';\n\nexport const useSyncMotionValue = (source: MotionValue<number>, destination?: MotionValue<number>): void => {\n useEffect(\n () => (destination ? source.on('change', (value) => destination.set(value)) : void 0),\n [source, destination]\n );\n};\n"],"names":[],"mappings":";;MAGa,kBAAkB,GAAG,CAAC,MAA2B,EAAE,WAAiC,KAAU;AACvG,IAAA,SAAS,CACL,OAAO,WAAW,GAAG,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,KAAK,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,EACrF,CAAC,MAAM,EAAE,WAAW,CAAC,CACxB,CAAC;AACN;;;;"}