@hh.ru/magritte-ui-nav-bar 1.0.2 → 1.1.0

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.
Files changed (43) hide show
  1. package/{NavBar-BM6rmKUJ.js → NavBar-D9oyKBms.js} +10 -12
  2. package/NavBar-D9oyKBms.js.map +1 -0
  3. package/index.css +94 -94
  4. package/index.js +3 -2
  5. package/index.js.map +1 -1
  6. package/index.mock.js +1 -31
  7. package/index.mock.js.map +1 -1
  8. package/internal/PaneStore.js +1 -2
  9. package/internal/PaneStore.js.map +1 -1
  10. package/internal/useAnimationRanges.d.ts +2 -1
  11. package/internal/useAnimationRanges.js +35 -22
  12. package/internal/useAnimationRanges.js.map +1 -1
  13. package/internal/useBindScrollToAnimationProgress.d.ts +1 -1
  14. package/internal/useBindScrollToAnimationProgress.js +25 -20
  15. package/internal/useBindScrollToAnimationProgress.js.map +1 -1
  16. package/internal/useInert.d.ts +2 -0
  17. package/internal/useInert.js +26 -0
  18. package/internal/useInert.js.map +1 -0
  19. package/internal/useResetFocus.d.ts +1 -1
  20. package/internal/useResetFocus.js +3 -3
  21. package/internal/useResetFocus.js.map +1 -1
  22. package/internal/utils.js +8 -1
  23. package/internal/utils.js.map +1 -1
  24. package/package.json +5 -3
  25. package/public/Actions.js +3 -3
  26. package/public/Actions.js.map +1 -1
  27. package/public/LayoutMorph.js +9 -4
  28. package/public/LayoutMorph.js.map +1 -1
  29. package/public/LayoutStage.js +2 -3
  30. package/public/LayoutStage.js.map +1 -1
  31. package/public/Morph.js +2 -2
  32. package/public/NavBar.d.ts +3 -6
  33. package/public/NavBar.js +1 -2
  34. package/public/NavBar.js.map +1 -1
  35. package/public/Pane.js +3 -3
  36. package/public/Pane.js.map +1 -1
  37. package/public/Stage.js +9 -4
  38. package/public/Stage.js.map +1 -1
  39. package/public/TitleContainer.js +2 -2
  40. package/NavBar-BM6rmKUJ.js.map +0 -1
  41. package/internal/ProgressiveBlur.d.ts +0 -7
  42. package/internal/ProgressiveBlur.js +0 -43
  43. package/internal/ProgressiveBlur.js.map +0 -1
package/index.mock.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.mock.js","sources":["../../mock-component/lib/setupMockComponent.js","../src/index.mock.ts"],"sourcesContent":["import { jsxs, jsx } from 'react/jsx-runtime';\nimport { forwardRef } from 'react';\n\nfunction enhance(mockComponent, staticProps) {\n // удаляем внутренний свойства реакта, чтобы не перезаписать ими mockComponent\n delete staticProps.type;\n delete staticProps.$$typeof;\n delete staticProps.render;\n for (const prop in staticProps) {\n mockComponent[prop] = staticProps[prop];\n }\n}\nconst MockComponent = ({ style, ...props }) => (\n// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-ignore\n// eslint-disable-next-line react/jsx-no-undef\njsx(\"magritte-component-mock\", { \"style-data\": style, ...props }));\nconst MockComponentRenderProp = ({ style, ...props }) => (\n// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-ignore\n// eslint-disable-next-line react/jsx-no-undef\njsx(\"magritte-component-mock-render-prop\", { \"style-data\": style, ...props }));\n/**\n * Возвращает mock React компонента\n */\nconst mockComponent = (componentName, staticProps = {}, { withChildren, innerDependence, WrapperComponent } = {}) => {\n const mockWrapper = forwardRef(({ children, ...props }, ref) => {\n const mock = (\n // eslint-disable-next-line react/jsx-no-undef\n jsxs(MockComponent, { ...props, _component: componentName, children: [innerDependence ? innerDependence(props, ref) : null, withChildren ? children : null] }));\n return WrapperComponent ? jsx(WrapperComponent, { ...props, children: mock }) : mock;\n });\n staticProps && enhance(mockWrapper, staticProps);\n mockWrapper.displayName = 'magritte-ref';\n return mockWrapper;\n};\n/**\n * Возвращает mock React компонента, который рендерит переданный в него пропс render\n *\n * @param {String} componentName наименование компонента\n * @param {Object} [staticProps] объект со свойствами, которыми необходимо усилить mock\n * @param {Object} [options] параметры мока\n * @param {boolean} [options.withChildren] флаг отвечает за рендеринг children компонента\n * @param {React.Component} [options.innerDependence] Реакт компонент, который рендерится внутри мока.\n * Необходим, когда mock компоненту требуется иметь\n * дополнительные элементы\n */\nconst mockComponentRenderProp = (componentName, staticProps, { withChildren, innerDependence, renderArguments } = { renderArguments: [] }) => {\n const mockWrapper = ({ render, children, ...props }) => (jsxs(MockComponentRenderProp, { ...props, _component: componentName, children: [innerDependence ? innerDependence(props) : null, withChildren ? children : null, render?.(...(renderArguments || []))] }));\n staticProps && enhance(mockWrapper, staticProps);\n mockWrapper.displayName = 'magritte-component-mock-render-prop';\n return mockWrapper;\n};\n\nexport { mockComponent, mockComponentRenderProp };\n//# sourceMappingURL=setupMockComponent.js.map\n","import { ForwardRefExoticComponent } from 'react';\n\nimport { mockComponent } from '@hh.ru/magritte-ui-mock-component';\n\nexport { type ActionsProps } from '@hh.ru/magritte-ui-nav-bar/public/Actions';\nexport { type NavBarProps } from '@hh.ru/magritte-ui-nav-bar/public/NavBar';\nexport { type ExternalPaneProps as PaneProps } from '@hh.ru/magritte-ui-nav-bar/public/Pane';\nexport { type MorphProps } from '@hh.ru/magritte-ui-nav-bar/public/Morph';\nexport { type TitleContainerProps } from '@hh.ru/magritte-ui-nav-bar/public/TitleContainer';\nexport { type LayoutMorphProps } from '@hh.ru/magritte-ui-nav-bar/public/LayoutMorph';\nexport { type EnvironmentFingerprintNodeProps } from '@hh.ru/magritte-ui-nav-bar/public/EnvironmentFingerprintNode';\n\nexport const NavBar: ForwardRefExoticComponent<Record<string, unknown>> = mockComponent('NavBar', undefined, {\n withChildren: true,\n});\n\nexport const Actions = mockComponent('Action', undefined, { withChildren: true });\nexport const TitleContainer = mockComponent('TitleContainer', undefined, { withChildren: true });\nexport const Pane = mockComponent('Pane', undefined, { withChildren: true });\nexport const Morph = mockComponent('Morph', undefined, { withChildren: true });\nexport const Stage = mockComponent('Stage', undefined, { withChildren: true });\nexport const LayoutMorph = mockComponent('LayoutMorph', undefined, { withChildren: true });\nexport const LayoutStage = mockComponent('LayoutStage', undefined, { withChildren: true });\nexport const EnvironmentFingerprintNode: ForwardRefExoticComponent<Record<string, unknown>> = mockComponent(\n 'EnvironmentFingerprintNode',\n undefined,\n { withChildren: true }\n);\n"],"names":[],"mappings":";;;AAGA,SAAS,OAAO,CAAC,aAAa,EAAE,WAAW,EAAE;AAC7C;AACA,IAAI,OAAO,WAAW,CAAC,IAAI,CAAC;AAC5B,IAAI,OAAO,WAAW,CAAC,QAAQ,CAAC;AAChC,IAAI,OAAO,WAAW,CAAC,MAAM,CAAC;AAC9B,IAAI,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE;AACpC,QAAQ,aAAa,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;AAChD,KAAK;AACL,CAAC;AACD,MAAM,aAAa,GAAG,CAAC,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE;AAC1C;AACA;AACA;AACA,GAAG,CAAC,yBAAyB,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC;AAMnE;AACA;AACA;AACA,MAAM,aAAa,GAAG,CAAC,aAAa,EAAE,WAAW,GAAG,EAAE,EAAE,EAAE,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,GAAG,EAAE,KAAK;AACrH,IAAI,MAAM,WAAW,GAAG,UAAU,CAAC,CAAC,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAE,EAAE,GAAG,KAAK;AACpE,QAAQ,MAAM,IAAI;AAClB;AACA,QAAQ,IAAI,CAAC,aAAa,EAAE,EAAE,GAAG,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC,eAAe,GAAG,eAAe,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG,IAAI,EAAE,YAAY,GAAG,QAAQ,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;AACxK,QAAQ,OAAO,gBAAgB,GAAG,GAAG,CAAC,gBAAgB,EAAE,EAAE,GAAG,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;AAC7F,KAAK,CAAC,CAAC;AACP,IAAI,WAAW,IAAI,OAAO,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;AACrD,IAAI,WAAW,CAAC,WAAW,GAAG,cAAc,CAAC;AAC7C,IAAI,OAAO,WAAW,CAAC;AACvB,CAAC;;MCvBY,MAAM,GAAuD,aAAa,CAAC,QAAQ,EAAE,SAAS,EAAE;AACzG,IAAA,YAAY,EAAE,IAAI;AACrB,CAAA,EAAE;AAEU,MAAA,OAAO,GAAG,aAAa,CAAC,QAAQ,EAAE,SAAS,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,EAAE;AACrE,MAAA,cAAc,GAAG,aAAa,CAAC,gBAAgB,EAAE,SAAS,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,EAAE;AACpF,MAAA,IAAI,GAAG,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,EAAE;AAChE,MAAA,KAAK,GAAG,aAAa,CAAC,OAAO,EAAE,SAAS,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,EAAE;AAClE,MAAA,KAAK,GAAG,aAAa,CAAC,OAAO,EAAE,SAAS,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,EAAE;AAClE,MAAA,WAAW,GAAG,aAAa,CAAC,aAAa,EAAE,SAAS,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,EAAE;AAC9E,MAAA,WAAW,GAAG,aAAa,CAAC,aAAa,EAAE,SAAS,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,EAAE;AAC9E,MAAA,0BAA0B,GAAuD,aAAa,CACvG,4BAA4B,EAC5B,SAAS,EACT,EAAE,YAAY,EAAE,IAAI,EAAE;;;;"}
1
+ {"version":3,"file":"index.mock.js","sources":["../src/index.mock.ts"],"sourcesContent":["import { ForwardRefExoticComponent } from 'react';\n\nimport { mockComponent } from '@hh.ru/magritte-ui-mock-component';\n\nexport { type ActionsProps } from '@hh.ru/magritte-ui-nav-bar/public/Actions';\nexport { type NavBarProps } from '@hh.ru/magritte-ui-nav-bar/public/NavBar';\nexport { type ExternalPaneProps as PaneProps } from '@hh.ru/magritte-ui-nav-bar/public/Pane';\nexport { type MorphProps } from '@hh.ru/magritte-ui-nav-bar/public/Morph';\nexport { type TitleContainerProps } from '@hh.ru/magritte-ui-nav-bar/public/TitleContainer';\nexport { type LayoutMorphProps } from '@hh.ru/magritte-ui-nav-bar/public/LayoutMorph';\nexport { type EnvironmentFingerprintNodeProps } from '@hh.ru/magritte-ui-nav-bar/public/EnvironmentFingerprintNode';\n\nexport const NavBar: ForwardRefExoticComponent<Record<string, unknown>> = mockComponent('NavBar', undefined, {\n withChildren: true,\n});\n\nexport const Actions = mockComponent('Action', undefined, { withChildren: true });\nexport const TitleContainer = mockComponent('TitleContainer', undefined, { withChildren: true });\nexport const Pane = mockComponent('Pane', undefined, { withChildren: true });\nexport const Morph = mockComponent('Morph', undefined, { withChildren: true });\nexport const Stage = mockComponent('Stage', undefined, { withChildren: true });\nexport const LayoutMorph = mockComponent('LayoutMorph', undefined, { withChildren: true });\nexport const LayoutStage = mockComponent('LayoutStage', undefined, { withChildren: true });\nexport const EnvironmentFingerprintNode: ForwardRefExoticComponent<Record<string, unknown>> = mockComponent(\n 'EnvironmentFingerprintNode',\n undefined,\n { withChildren: true }\n);\n"],"names":[],"mappings":";;MAYa,MAAM,GAAuD,aAAa,CAAC,QAAQ,EAAE,SAAS,EAAE;AACzG,IAAA,YAAY,EAAE,IAAI;AACrB,CAAA,EAAE;AAEU,MAAA,OAAO,GAAG,aAAa,CAAC,QAAQ,EAAE,SAAS,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,EAAE;AACrE,MAAA,cAAc,GAAG,aAAa,CAAC,gBAAgB,EAAE,SAAS,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,EAAE;AACpF,MAAA,IAAI,GAAG,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,EAAE;AAChE,MAAA,KAAK,GAAG,aAAa,CAAC,OAAO,EAAE,SAAS,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,EAAE;AAClE,MAAA,KAAK,GAAG,aAAa,CAAC,OAAO,EAAE,SAAS,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,EAAE;AAClE,MAAA,WAAW,GAAG,aAAa,CAAC,aAAa,EAAE,SAAS,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,EAAE;AAC9E,MAAA,WAAW,GAAG,aAAa,CAAC,aAAa,EAAE,SAAS,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,EAAE;AAC9E,MAAA,0BAA0B,GAAuD,aAAa,CACvG,4BAA4B,EAC5B,SAAS,EACT,EAAE,YAAY,EAAE,IAAI,EAAE;;;;"}
@@ -3,14 +3,13 @@ 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-BM6rmKUJ.js';
6
+ import { u as useNavBarContext } from '../NavBar-D9oyKBms.js';
7
7
  import 'react/jsx-runtime';
8
8
  import 'classnames';
9
9
  import 'motion/react';
10
10
  import '@hh.ru/magritte-ui-divider';
11
11
  import '@hh.ru/magritte-ui-layer';
12
12
  import './MetricsProvider.js';
13
- import './ProgressiveBlur.js';
14
13
  import './useAnimationRanges.js';
15
14
  import './useBindScrollToAnimationProgress.js';
16
15
  import './useNavBarMetrics.js';
@@ -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}\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":";;;;;;;;;;;;;;;;;;;;;AAgBA;;;;;;;;;;;;;;;;;;;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}\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":";;;;;;;;;;;;;;;;;;;;AAgBA;;;;;;;;;;;;;;;;;;;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,4 +1,5 @@
1
1
  import { type PaneStore } from '@hh.ru/magritte-ui-nav-bar/internal/PaneStore';
2
+ import { NavBarMetrics } from '@hh.ru/magritte-ui-nav-bar/internal/useNavBarMetrics';
2
3
  /**
3
4
  * Рассчитывает для панелей диапазоны скролла, в течение которого должна проигрываться их анимация.
4
5
  *
@@ -35,4 +36,4 @@ import { type PaneStore } from '@hh.ru/magritte-ui-nav-bar/internal/PaneStore';
35
36
  * Диапазоны скролла для анимации каждой панели в исходном порядке `data`.
36
37
  */
37
38
  export type AnimationRanges = Map<string, [number, number]>;
38
- export declare const useAnimationRanges: (paneRegistry: Set<PaneStore>) => [() => AnimationRanges, VoidFunction];
39
+ export declare const useAnimationRanges: (paneRegistry: Set<PaneStore>, getNavBarMetrics: () => NavBarMetrics) => [() => AnimationRanges, VoidFunction];
@@ -2,7 +2,7 @@ import './../index.css';
2
2
  import { useRef } from 'react';
3
3
  import { useActualRef, useInitOnce } from './utils.js';
4
4
 
5
- const useAnimationRanges = (paneRegistry) => {
5
+ const useAnimationRanges = (paneRegistry, getNavBarMetrics) => {
6
6
  const animationRangesRef = useRef(null);
7
7
  const paneRegistryRef = useActualRef(paneRegistry);
8
8
  const getAnimationRanges = useInitOnce(() => () => {
@@ -11,33 +11,46 @@ const useAnimationRanges = (paneRegistry) => {
11
11
  const panesData = [...paneRegistryRef.current.values()]
12
12
  .map((pane) => pane.data)
13
13
  .sort((a, b) => (b.top ?? 0) - (a.top ?? 0));
14
+ const { heightDelta } = getNavBarMetrics();
14
15
  const scrollRangesById = new Map();
15
16
  let groupStartIdx = 0; // индекс последней встреченной foldable (начало текущей группы)
16
17
  let totalDuration = 0; // суммарная длительность (в пикселях скролла) всех ранее обработанных групп
17
18
  let groupDuration = 0; // суммарная длительность (в пикселях скролла) текущей группы
18
- for (let i = 0; i <= panesData.length; i++) {
19
- const pane = panesData[i] ?? null;
20
- const isLastPane = pane === null;
21
- const isGroupEnd = isLastPane || pane.foldable;
22
- // Новая foldable или последняя панель -> завершаем обработку текущей группы, и сохраняем
23
- // диапазон если известно место ее начала
24
- if (isGroupEnd && groupStartIdx !== null) {
25
- const range = [totalDuration, totalDuration + groupDuration];
26
- for (let j = groupStartIdx; j < i; j++) {
27
- scrollRangesById.set(panesData[j].id, range);
28
- }
29
- totalDuration += groupDuration;
30
- groupDuration = 0;
19
+ // ни одна из панелей не схлопывается и не меняет высоту в процессе анимации
20
+ // в этом случае просто разбиваем стадии анимации на отрезки пропорциональные высоте панелей
21
+ if (heightDelta === 0) {
22
+ for (let i = 0; i < panesData.length; i++) {
23
+ const pane = panesData[i];
24
+ const paneHeight = pane.startHeight ?? 0;
25
+ scrollRangesById.set(pane.id, [totalDuration, totalDuration + paneHeight]);
26
+ totalDuration += paneHeight;
31
27
  }
32
- if (!isLastPane) {
33
- if (pane.foldable) {
34
- groupStartIdx = i;
28
+ }
29
+ else {
30
+ for (let i = 0; i <= panesData.length; i++) {
31
+ const pane = panesData[i] ?? null;
32
+ const isLastPane = pane === null;
33
+ const isGroupEnd = isLastPane || pane.foldable;
34
+ // Новая foldable или последняя панель -> завершаем обработку текущей группы, и сохраняем
35
+ // диапазон если известно место ее начала
36
+ if (isGroupEnd && groupStartIdx !== null) {
37
+ const range = [totalDuration, totalDuration + groupDuration];
38
+ for (let j = groupStartIdx; j < i; j++) {
39
+ scrollRangesById.set(panesData[j].id, range);
40
+ }
41
+ totalDuration += groupDuration;
42
+ groupDuration = 0;
43
+ }
44
+ if (!isLastPane) {
45
+ if (pane.foldable) {
46
+ groupStartIdx = i;
47
+ }
48
+ // Дельта высоты панели: вклад в длительность текущей группы
49
+ const deltaHeight = pane.foldable
50
+ ? (pane.startHeight ?? 0) // foldable схлопывается до 0
51
+ : (pane.startHeight ?? 0) - (pane.endHeight ?? pane.startHeight ?? 0); // иначе разница start -> end
52
+ groupDuration += deltaHeight;
35
53
  }
36
- // Дельта высоты панели: вклад в длительность текущей группы
37
- const deltaHeight = pane.foldable
38
- ? (pane.startHeight ?? 0) // foldable схлопывается до 0
39
- : (pane.startHeight ?? 0) - (pane.endHeight ?? pane.startHeight ?? 0); // иначе разница start -> end
40
- groupDuration += deltaHeight;
41
54
  }
42
55
  }
43
56
  animationRangesRef.current = scrollRangesById;
@@ -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 { 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 = (paneRegistry: Set<PaneStore>): [() => 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\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 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 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":";;;AAyCa,MAAA,kBAAkB,GAAG,CAAC,YAA4B,KAA2C;AACtG,IAAA,MAAM,kBAAkB,GAA6C,MAAM,CAAC,IAAI,CAAC,CAAC;AAClF,IAAA,MAAM,eAAe,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;IAEnD,MAAM,kBAAkB,GAAG,WAAW,CAAC,MAAM,MAAK;AAC9C,QAAA,IAAI,CAAC,kBAAkB,CAAC,OAAO,EAAE;;YAE7B,MAAM,SAAS,GAAG,CAAC,GAAG,eAAe,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;iBAClD,GAAG,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC;iBACxB,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;AAEjD,YAAA,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAA4B,CAAC;AAC7D,YAAA,IAAI,aAAa,GAAkB,CAAC,CAAC;AACrC,YAAA,IAAI,aAAa,GAAG,CAAC,CAAC;AACtB,YAAA,IAAI,aAAa,GAAG,CAAC,CAAC;AAEtB,YAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;gBACxC,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AAClC,gBAAA,MAAM,UAAU,GAAG,IAAI,KAAK,IAAI,CAAC;AACjC,gBAAA,MAAM,UAAU,GAAG,UAAU,IAAI,IAAI,CAAC,QAAQ,CAAC;;;AAG/C,gBAAA,IAAI,UAAU,IAAI,aAAa,KAAK,IAAI,EAAE;oBACtC,MAAM,KAAK,GAAqB,CAAC,aAAa,EAAE,aAAa,GAAG,aAAa,CAAC,CAAC;AAC/E,oBAAA,KAAK,IAAI,CAAC,GAAG,aAAa,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE;AACpC,wBAAA,gBAAgB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;qBAChD;oBACD,aAAa,IAAI,aAAa,CAAC;oBAC/B,aAAa,GAAG,CAAC,CAAC;iBACrB;gBAED,IAAI,CAAC,UAAU,EAAE;AACb,oBAAA,IAAI,IAAI,CAAC,QAAQ,EAAE;wBACf,aAAa,GAAG,CAAC,CAAC;qBACrB;;AAGD,oBAAA,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ;2BAC1B,IAAI,CAAC,WAAW,IAAI,CAAC;0BACtB,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,KAAK,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC;oBAC1E,aAAa,IAAI,WAAW,CAAC;iBAChC;aACJ;AACD,YAAA,kBAAkB,CAAC,OAAO,GAAG,gBAAgB,CAAC;SACjD;QACD,OAAO,kBAAkB,CAAC,OAAO,CAAC;AACtC,KAAC,CAAC,CAAC;AAEH,IAAA,MAAM,8BAA8B,GAAG,WAAW,CAAC,MAAM,OAAO,kBAAkB,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC;AAEpG,IAAA,OAAO,CAAC,kBAAkB,EAAE,8BAA8B,CAAC,CAAC;AAChE;;;;"}
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 const paneHeight = pane.startHeight ?? 0;\n scrollRangesById.set(pane.id, [totalDuration, totalDuration + paneHeight]);\n totalDuration += paneHeight;\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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"}
@@ -3,7 +3,7 @@ import { type MotionStyle, type MotionValue } from 'motion/react';
3
3
  import { type AnimationRanges } from '@hh.ru/magritte-ui-nav-bar/internal/useAnimationRanges';
4
4
  import { type NavBarMetrics } from '@hh.ru/magritte-ui-nav-bar/internal/useNavBarMetrics';
5
5
  import { type ScrollAdapter } from '@hh.ru/magritte-ui-nav-bar/internal/useScrollAdapter';
6
- export declare const useBindScrollToAnimationProgress: (scrollPosition: MotionValue<number>, getNavBarMetrics: () => NavBarMetrics, getAnimationRanges: () => AnimationRanges, scrollAdapter: ScrollAdapter, startTriggerPosition: "start" | "full-area" | RefObject<HTMLElement | null>, endTriggerPosition?: RefObject<HTMLElement | null>) => [VoidFunction, () => {
6
+ export declare const useBindScrollToAnimationProgress: (scrollPosition: MotionValue<number>, getNavBarMetrics: () => NavBarMetrics, getAnimationRanges: () => AnimationRanges, scrollAdapter: ScrollAdapter, startTriggerPosition: "start" | "full-area" | RefObject<HTMLElement | null>, endTriggerPosition?: RefObject<HTMLElement | null> | number) => [VoidFunction, () => {
7
7
  top: number;
8
8
  bottom: number;
9
9
  }, MotionValue<number>, MotionStyle];
@@ -5,20 +5,17 @@ import { useActualRef, useInitOnce, getRelativeOffset, remap, clamp } from './ut
5
5
 
6
6
  const createScrollToAnimationProgressMapFn = (getNavBarMetrics, scrollAdapter, animationRanges, startTriggerPosition, endTriggerPosition) => {
7
7
  const maxScrollTop = scrollAdapter.getMaxScrollTop();
8
- const navBarMetrics = getNavBarMetrics();
8
+ const { heightDelta, top, bottom } = getNavBarMetrics();
9
+ const animationHeight = heightDelta || bottom - top;
9
10
  if (startTriggerPosition === 'full-area') {
10
- const { heightDelta, top } = navBarMetrics;
11
11
  let currentStart = top;
12
- const maxProgress = maxScrollTop >= heightDelta + top
12
+ const maxProgress = maxScrollTop >= animationHeight + top
13
13
  ? 1
14
- : (animationRanges.find((range) => range[0] * heightDelta <= maxScrollTop - top) ?? [0])[0];
14
+ : (animationRanges.find((range) => range[0] * animationHeight <= maxScrollTop - top) ?? [0])[0];
15
15
  const mapScrollToAnimationPropgress = (scrollPosition) => {
16
- if (!heightDelta) {
17
- return 0;
18
- }
19
- const progress = (scrollPosition - currentStart) / heightDelta;
16
+ const progress = (scrollPosition - currentStart) / animationHeight;
20
17
  if (progress > maxProgress) {
21
- currentStart = scrollPosition - heightDelta * maxProgress;
18
+ currentStart = scrollPosition - animationHeight * maxProgress;
22
19
  }
23
20
  else if (progress < 0) {
24
21
  currentStart = scrollPosition;
@@ -28,20 +25,24 @@ const createScrollToAnimationProgressMapFn = (getNavBarMetrics, scrollAdapter, a
28
25
  };
29
26
  const getClosestStops = () => ({
30
27
  bottom: currentStart,
31
- top: currentStart + heightDelta * maxProgress,
28
+ top: currentStart + animationHeight * maxProgress,
32
29
  });
33
30
  return [mapScrollToAnimationPropgress, getClosestStops];
34
31
  }
35
32
  let startPos = startTriggerPosition === 'start' || !startTriggerPosition.current
36
33
  ? 0
37
- : getRelativeOffset(scrollAdapter, startTriggerPosition.current) - navBarMetrics.bottom;
38
- let endPos = endTriggerPosition?.current
39
- ? getRelativeOffset(scrollAdapter, endTriggerPosition.current) +
40
- navBarMetrics.heightDelta -
41
- navBarMetrics.bottom
42
- : startPos + navBarMetrics.heightDelta;
43
- endPos += navBarMetrics.top;
44
- startPos += navBarMetrics.top;
34
+ : getRelativeOffset(scrollAdapter, startTriggerPosition.current) - bottom;
35
+ let endPos;
36
+ if (typeof endTriggerPosition === 'number') {
37
+ endPos = startPos + endTriggerPosition;
38
+ }
39
+ else {
40
+ endPos = endTriggerPosition?.current
41
+ ? getRelativeOffset(scrollAdapter, endTriggerPosition.current) + heightDelta - bottom
42
+ : startPos + animationHeight;
43
+ }
44
+ endPos += top;
45
+ startPos += top;
45
46
  let endProgress = 1;
46
47
  if (endPos > maxScrollTop) {
47
48
  const scrollDelta = endPos - startPos;
@@ -62,9 +63,13 @@ const useBindScrollToAnimationProgress = (scrollPosition, getNavBarMetrics, getA
62
63
  let unsubscribe = null;
63
64
  return () => {
64
65
  unsubscribe?.();
65
- const { heightDelta, top } = getNavBarMetrics();
66
+ const { heightDelta, top, bottom } = getNavBarMetrics();
67
+ const animationHeight = heightDelta || bottom - top;
66
68
  const animationRanges = [
67
- ...new Set([...getAnimationRanges().values()].map((range) => [range[0] / heightDelta, range[1] / heightDelta])),
69
+ ...new Set([...getAnimationRanges().values()].map((range) => [
70
+ range[0] / animationHeight,
71
+ range[1] / animationHeight,
72
+ ])),
68
73
  ].sort((a, b) => b[0] - a[0]);
69
74
  const [mapFn, getClosestStops] = createScrollToAnimationProgressMapFn(getNavBarMetrics, scrollAdapter, animationRanges, startTriggerPositionRef.current, endTriggerPositionRef.current);
70
75
  getClosestStopsRef.current = getClosestStops;
@@ -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>\n) => {\n const maxScrollTop = scrollAdapter.getMaxScrollTop();\n const navBarMetrics = getNavBarMetrics();\n\n if (startTriggerPosition === 'full-area') {\n const { heightDelta, top } = navBarMetrics;\n\n let currentStart = top;\n const maxProgress =\n maxScrollTop >= heightDelta + top\n ? 1\n : (animationRanges.find((range) => range[0] * heightDelta <= maxScrollTop - top) ?? [0])[0];\n\n const mapScrollToAnimationPropgress = (scrollPosition: number) => {\n if (!heightDelta) {\n return 0;\n }\n const progress = (scrollPosition - currentStart) / heightDelta;\n if (progress > maxProgress) {\n currentStart = scrollPosition - heightDelta * 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 + heightDelta * 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) - navBarMetrics.bottom;\n\n let endPos = endTriggerPosition?.current\n ? getRelativeOffset(scrollAdapter, endTriggerPosition.current) +\n navBarMetrics.heightDelta -\n navBarMetrics.bottom\n : startPos + navBarMetrics.heightDelta;\n\n endPos += navBarMetrics.top;\n startPos += navBarMetrics.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>\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 } = getNavBarMetrics();\n\n const animationRanges = [\n ...new Set<[number, number]>(\n [...getAnimationRanges().values()].map((range) => [range[0] / heightDelta, range[1] / heightDelta])\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":";;;;AAQA,MAAM,oCAAoC,GAAG,CACzC,gBAAqC,EACrC,aAA4B,EAC5B,eAAmC,EACnC,oBAA2E,EAC3E,kBAAkD,KAClD;AACA,IAAA,MAAM,YAAY,GAAG,aAAa,CAAC,eAAe,EAAE,CAAC;AACrD,IAAA,MAAM,aAAa,GAAG,gBAAgB,EAAE,CAAC;AAEzC,IAAA,IAAI,oBAAoB,KAAK,WAAW,EAAE;AACtC,QAAA,MAAM,EAAE,WAAW,EAAE,GAAG,EAAE,GAAG,aAAa,CAAC;QAE3C,IAAI,YAAY,GAAG,GAAG,CAAC;AACvB,QAAA,MAAM,WAAW,GACb,YAAY,IAAI,WAAW,GAAG,GAAG;AAC7B,cAAE,CAAC;AACH,cAAE,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC,CAAC,GAAG,WAAW,IAAI,YAAY,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAEpG,QAAA,MAAM,6BAA6B,GAAG,CAAC,cAAsB,KAAI;YAC7D,IAAI,CAAC,WAAW,EAAE;AACd,gBAAA,OAAO,CAAC,CAAC;aACZ;YACD,MAAM,QAAQ,GAAG,CAAC,cAAc,GAAG,YAAY,IAAI,WAAW,CAAC;AAC/D,YAAA,IAAI,QAAQ,GAAG,WAAW,EAAE;AACxB,gBAAA,YAAY,GAAG,cAAc,GAAG,WAAW,GAAG,WAAW,CAAC;aAC7D;AAAM,iBAAA,IAAI,QAAQ,GAAG,CAAC,EAAE;gBACrB,YAAY,GAAG,cAAc,CAAC;aACjC;YACD,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC;YAE3C,OAAO,KAAK,CAAC,QAAQ,EAAE,CAAC,EAAE,WAAW,CAAC,CAAC;AAC3C,SAAC,CAAC;AAEF,QAAA,MAAM,eAAe,GAAG,OAAO;AAC3B,YAAA,MAAM,EAAE,YAAY;AACpB,YAAA,GAAG,EAAE,YAAY,GAAG,WAAW,GAAG,WAAW;AAChD,SAAA,CAAC,CAAC;AAEH,QAAA,OAAO,CAAC,6BAA6B,EAAE,eAAe,CAAU,CAAC;KACpE;IAED,IAAI,QAAQ,GACR,oBAAoB,KAAK,OAAO,IAAI,CAAC,oBAAoB,CAAC,OAAO;AAC7D,UAAE,CAAC;AACH,UAAE,iBAAiB,CAAC,aAAa,EAAE,oBAAoB,CAAC,OAAO,CAAC,GAAG,aAAa,CAAC,MAAM,CAAC;AAEhG,IAAA,IAAI,MAAM,GAAG,kBAAkB,EAAE,OAAO;UAClC,iBAAiB,CAAC,aAAa,EAAE,kBAAkB,CAAC,OAAO,CAAC;AAC5D,YAAA,aAAa,CAAC,WAAW;AACzB,YAAA,aAAa,CAAC,MAAM;AACtB,UAAE,QAAQ,GAAG,aAAa,CAAC,WAAW,CAAC;AAE3C,IAAA,MAAM,IAAI,aAAa,CAAC,GAAG,CAAC;AAC5B,IAAA,QAAQ,IAAI,aAAa,CAAC,GAAG,CAAC;IAE9B,IAAI,WAAW,GAAG,CAAC,CAAC;AACpB,IAAA,IAAI,MAAM,GAAG,YAAY,EAAE;AACvB,QAAA,MAAM,WAAW,GAAG,MAAM,GAAG,QAAQ,CAAC;AACtC,QAAA,WAAW,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC,CAAC,GAAG,WAAW,GAAG,QAAQ,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC5G,QAAA,MAAM,GAAG,QAAQ,GAAG,WAAW,GAAG,WAAW,CAAC;KACjD;AAED,IAAA,OAAO,CAAC,KAAK,CAAC,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAU,CAAC;AAC7G,CAAC,CAAC;AAEF,MAAM,gBAAgB,GAAG,CAAC,SAAiB,EAAE,SAAiB,EAAE,WAAmB,EAAE,iBAAyB,KAC1G,IAAI,CAAC,IAAI,CAAC,SAAS,GAAG,WAAW,GAAG,iBAAiB,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;AAEtE,MAAA,gCAAgC,GAAG,CAC5C,cAAmC,EACnC,gBAAqC,EACrC,kBAAyC,EACzC,aAA4B,EAC5B,oBAA2E,EAC3E,kBAAkD,KACuC;AACzF,IAAA,MAAM,sBAAsB,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC;AACjD,IAAA,MAAM,iBAAiB,GAAG,cAAc,CAAuB,QAAQ,CAAC,CAAC;AACzE,IAAA,MAAM,cAAc,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC;AACzC,IAAA,MAAM,uBAAuB,GAAG,YAAY,CAAC,oBAAoB,CAAC,CAAC;AACnE,IAAA,MAAM,qBAAqB,GAAG,YAAY,CAAC,kBAAkB,CAAC,CAAC;AAC/D,IAAA,MAAM,kBAAkB,GAAqE,MAAM,CAAC,IAAI,CAAC,CAAC;AAE1G,IAAA,MAAM,qBAAqB,GAAG,WAAW,CAAC,MAAK;QAC3C,IAAI,WAAW,GAAwB,IAAI,CAAC;AAC5C,QAAA,OAAO,MAAK;YACR,WAAW,IAAI,CAAC;YAChB,MAAM,EAAE,WAAW,EAAE,GAAG,EAAE,GAAG,gBAAgB,EAAE,CAAC;AAEhD,YAAA,MAAM,eAAe,GAAG;AACpB,gBAAA,GAAG,IAAI,GAAG,CACN,CAAC,GAAG,kBAAkB,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,WAAW,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,CACtG;AACJ,aAAA,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAE9B,MAAM,CAAC,KAAK,EAAE,eAAe,CAAC,GAAG,oCAAoC,CACjE,gBAAgB,EAChB,aAAa,EACb,eAAe,EACf,uBAAuB,CAAC,OAAO,EAC/B,qBAAqB,CAAC,OAAO,CAChC,CAAC;AACF,YAAA,kBAAkB,CAAC,OAAO,GAAG,eAAe,CAAC;AAC7C,YAAA,MAAM,aAAa,GAAG,CAAC,cAAsB,EAAE,sBAA8B,KAAI;gBAC7E,iBAAiB,CAAC,GAAG,CACjB,gBAAgB,CAAC,cAAc,EAAE,GAAG,EAAE,WAAW,EAAE,sBAAsB,CAAC,GAAG,SAAS,GAAG,QAAQ,CACpG,CAAC;gBACF,cAAc,CAAC,GAAG,CAAC,CAAC,WAAW,GAAG,sBAAsB,CAAC,CAAC;AAC9D,aAAC,CAAC;YACF,WAAW,GAAG,cAAc,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,KAAI;gBAChD,sBAAsB,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;gBACzC,aAAa,CAAC,KAAK,EAAE,sBAAsB,CAAC,GAAG,EAAE,CAAC,CAAC;AACvD,aAAC,CAAC,CAAC;YAEH,sBAAsB,CAAC,GAAG,CAAC,KAAK,CAAC,cAAc,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;YACxD,aAAa,CAAC,cAAc,CAAC,GAAG,EAAE,EAAE,sBAAsB,CAAC,GAAG,EAAE,CAAC,CAAC;AACtE,SAAC,CAAC;AACN,KAAC,CAAC,CAAC;IAEH,MAAM,eAAe,GAAG,WAAW,CAAC,MAAM,MAAK;AAC3C,QAAA,IAAI,kBAAkB,CAAC,OAAO,EAAE;AAC5B,YAAA,OAAO,kBAAkB,CAAC,OAAO,EAAE,CAAC;SACvC;AAED,QAAA,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,gBAAgB,EAAE,CAAC,WAAW,EAAE,CAAC;AAC9D,KAAC,CAAC,CAAC;IAEH,OAAO;QACH,qBAAqB;QACrB,eAAe;QACf,sBAAsB;AACtB,QAAA,EAAE,UAAU,EAAE,iBAAiB,EAAE,CAAC,EAAE,cAAc,EAAE;KACvD,CAAC;AACN;;;;"}
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 } = getNavBarMetrics();\n const animationHeight = heightDelta || bottom - top;\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, bottom } = getNavBarMetrics();\n const animationHeight = heightDelta || bottom - top;\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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"}
@@ -0,0 +1,2 @@
1
+ import { RefCallback } from 'react';
2
+ export declare const useInert: () => [RefCallback<HTMLElement | null>, (isInert: boolean) => void];
@@ -0,0 +1,26 @@
1
+ import './../index.css';
2
+ import { useRef } from 'react';
3
+ import { useInitOnce } from './utils.js';
4
+
5
+ const useInert = () => {
6
+ const currentInertRef = useRef(false);
7
+ const elementRef = useRef(null);
8
+ const refCallback = useInitOnce(() => (element) => {
9
+ elementRef.current = element;
10
+ if (element) {
11
+ element.toggleAttribute('inert', currentInertRef.current);
12
+ }
13
+ });
14
+ const setInert = useInitOnce(() => (isInert) => {
15
+ if (isInert !== currentInertRef.current) {
16
+ currentInertRef.current = isInert;
17
+ if (elementRef.current) {
18
+ elementRef.current.toggleAttribute('inert', currentInertRef.current);
19
+ }
20
+ }
21
+ });
22
+ return [refCallback, setInert];
23
+ };
24
+
25
+ export { useInert };
26
+ //# sourceMappingURL=useInert.js.map
@@ -0,0 +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,3 +1,3 @@
1
1
  import { type RefObject } from 'react';
2
2
  import { type MotionValue } from 'motion/react';
3
- export declare const useResetFocus: (rootRef: RefObject<HTMLElement>, scrollPosition: MotionValue<number>) => void;
3
+ export declare const useResetFocus: (rootRef: RefObject<HTMLElement>, animationProgress: MotionValue<number>) => void;
@@ -1,7 +1,7 @@
1
1
  import './../index.css';
2
2
  import { useRef, useEffect } from 'react';
3
3
 
4
- const useResetFocus = (rootRef, scrollPosition) => {
4
+ const useResetFocus = (rootRef, animationProgress) => {
5
5
  const hasFocus = useRef(false);
6
6
  useEffect(() => {
7
7
  if (!rootRef.current) {
@@ -10,7 +10,7 @@ const useResetFocus = (rootRef, scrollPosition) => {
10
10
  const focusHandler = () => (hasFocus.current = true);
11
11
  const element = rootRef.current;
12
12
  element.addEventListener('focusin', focusHandler);
13
- const unsubscribe = scrollPosition.on('change', () => {
13
+ const unsubscribe = animationProgress.on('change', () => {
14
14
  if (!hasFocus.current || !rootRef.current) {
15
15
  return;
16
16
  }
@@ -24,7 +24,7 @@ const useResetFocus = (rootRef, scrollPosition) => {
24
24
  unsubscribe();
25
25
  element.removeEventListener('focusin', focusHandler);
26
26
  };
27
- }, [scrollPosition, rootRef]);
27
+ }, [animationProgress, rootRef]);
28
28
  };
29
29
 
30
30
  export { useResetFocus };
@@ -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>, scrollPosition: 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 = scrollPosition.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 }, [scrollPosition, rootRef]);\n};\n"],"names":[],"mappings":";;MAGa,aAAa,GAAG,CAAC,OAA+B,EAAE,cAAmC,KAAU;AACxG,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,cAAc,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAK;YACjD,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,cAAc,EAAE,OAAO,CAAC,CAAC,CAAC;AAClC;;;;"}
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;;;;"}
package/internal/utils.js CHANGED
@@ -22,7 +22,14 @@ const remap = (domain, image) => {
22
22
  }
23
23
  const a = imageRange / domainRange;
24
24
  const b = image[0] - a * domain[0];
25
- return (value) => clamp(a * value + b, min, max);
25
+ return (value) => {
26
+ // из-за ошибок в числах с плавающей запятой можем не достигать пределов, поэтому дополнительно
27
+ // проверяем вручную
28
+ if (value === domain[0] || value === domain[1]) {
29
+ return value === domain[0] ? image[0] : image[1];
30
+ }
31
+ return clamp(a * value + b, min, max);
32
+ };
26
33
  };
27
34
  const useInitOnce = (fn) => {
28
35
  const ref = useRef(null);
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sources":["../../src/internal/utils.ts"],"sourcesContent":["import { type MutableRefObject, useLayoutEffect, useRef } from 'react';\nimport type { MotionValue } from 'motion';\n\nimport { KeyedSubscriptions } from '@hh.ru/magritte-ui-nav-bar/internal/KeyedSubscriptions';\nimport { ScrollAdapter } from '@hh.ru/magritte-ui-nav-bar/internal/useScrollAdapter';\n\nexport const lerp = (from: number, to: number, progress: number): number => from + (to - from) * progress;\nexport const clamp = (value: number, min: number, max: number): number => Math.min(max, Math.max(min, value));\n\n/**\n * Создаёт функцию преобразования значений из одного числового интервала (домен) в другой (образ).\n *\n * @param domain Интервал входных значений [min, max].\n * @param image Интервал выходных значений [min, max].\n *\n * @returns Функция, принимающая число из диапазона `domain`, преобразующая его в диапазон `image`\n * с линейной интерполяцией.\n */\nexport const remap = (domain: [number, number], image: [number, number]) => {\n const domainRange = domain[1] - domain[0];\n const imageRange = image[1] - image[0];\n const min = Math.min(...image);\n const max = Math.max(...image);\n\n if (domainRange === 0) {\n return () => image[0];\n }\n\n const a = imageRange / domainRange;\n const b = image[0] - a * domain[0];\n\n return (value: number): number => clamp(a * value + b, min, max);\n};\n\nexport const useInitOnce = <T>(fn: () => T): T => {\n const ref = useRef<T | null>(null);\n if (ref.current === null) {\n ref.current = fn();\n }\n return ref.current;\n};\n\n/**\n * Возвращает «одноразовый» планировщик, который выполнит `fn` в следующем\n * тике **микрозадач** (через `Promise.resolve().then(...)`). Повторные вызовы\n * до момента выполнения схлопываются в один запуск.\n *\n * Семантика:\n * - Первый вызов ставит `fn` в очередь микрозадач; последующие вызовы до\n * выполнения игнорируются.\n * - После выполнения `fn` «засов» снимается — можно планировать снова.\n * - Ошибки из `fn` перебрасываются в следующий макротик (через `setTimeout`),\n * чтобы они были видны в DevTools и не превращались в «unhandled rejection».\n *\n * Когда использовать:\n * - Нужно объединить множество синхронных триггеров в одно действие,\n * выполнив его **раньше таймеров/`requestAnimationFrame`**.\n * - Не требуется ожидать стабилизации DOM/лейаута (микрозадачи идут до кадра).\n *\n * @param fn — колбэк, который будет выполнен в следующем тике микрозадач.\n * @returns Функция-триггер: вызов ставит `fn` в очередь 1 раз на тик микрозадач.\n *\n * @example\n * const trigger = scheduleMicro(recompute);\n * trigger(); // запланирован один запуск\n * trigger(); // проигнорирован (уже запланировано)\n * // `recompute` выполнится один раз на следующем тике микрозадач\n */\nexport const scheduleMicro = (fn: VoidFunction) => {\n let scheduled = false;\n return (): void => {\n if (scheduled) {\n return;\n }\n scheduled = true;\n Promise.resolve()\n .then(() => {\n scheduled = false;\n fn();\n })\n .catch((err) =>\n setTimeout(() => {\n throw err;\n }, 0)\n );\n };\n};\n\n/**\n * Возвращает «одноразовый» планировщик, который выполнит `fn` в следующем\n * тике **макрозадач** (через `setTimeout(0)`). Повторные вызовы до момента\n * выполнения схлопываются в один запуск.\n *\n * Семантика:\n * - Первый вызов ставит `fn` в `setTimeout`; последующие вызовы до выполнения\n * игнорируются.\n * - После выполнения `fn` «засов» снимается — можно планировать снова.\n * - Ошибки из `fn` всплывут как непойманные ошибки из обработчика таймера.\n *\n * Когда использовать:\n * - Нужно отложить выполнение **после** опустошения очереди микрозадач\n * (т.е. позже, чем `scheduleMicro`), часто позволяя браузеру между делом\n * подготовить кадр/перерисовку.\n *\n * @param fn — колбэк, который будет выполнен в следующем тике макрозадач.\n * @returns Функция-триггер: вызов ставит `fn` в очередь 1 раз на тик таймера.\n *\n * @example\n * const trigger = scheduleMacro(flushQueue);\n * trigger(); // запланирован один таймер\n * trigger(); // проигнорирован до выполнения `flushQueue`\n */\nexport const scheduleMacro = (fn: VoidFunction): VoidFunction => {\n let scheduled = false;\n const scheduledFn = () => {\n if (scheduled) {\n return;\n }\n scheduled = true;\n setTimeout(() => {\n scheduled = false;\n fn();\n });\n };\n return scheduledFn;\n};\n\n/**\n * Планировщик для «сбора метрик» на основе микрозадач: откладывает выполнение `fn`\n * до момента, когда серия частых вызовов завершится, и запускает `fn` ровно один раз.\n *\n * Идея:\n * - Первый вызов добавляет «запас» из двух тиков микрозадач; каждый следующий\n * в ту же серию добавляет ещё +1.\n * - Единый «дренирующий» цикл (один на серию) уменьшает счётчик на каждом тике\n * микрозадач; когда счётчик достигает нуля — выполняется `fn`.\n * - Повторные вызовы не создают параллельных циклов; `fn` не вызывается несколько раз.\n * - Ошибки из `fn` перебрасываются в макрозадачу (`setTimeout`), чтобы их было видно\n * в DevTools и они не становились unhandled rejection.\n *\n * Когда использовать:\n * - Нужна коалесценция множественных триггеров (Promises, ResizeObserver и т.п.)\n * с запуском «после того, как всё успокоилось», чтобы измерить DOM/пересчитать\n * лейаут/агрегировать состояние один раз.\n *\n * Гарантии и нюансы:\n * - Гарантируется минимум два тика микрозадач после первого вызова (даёт времени\n * промисам/микроочереди завершиться).\n * - Работает как эвристика «дождаться затишья» на уровне микрозадач, без перехода\n * в макрозадачи/таймеры (сам `fn` всё равно выполняется в микрозадаче).\n *\n * @param fn — колбэк, который будет выполнен один раз после завершения серии вызовов.\n * @returns Триггер; его можно вызывать многократно — `fn` запустится один раз,\n * когда внутренний счётчик дойдёт до нуля.\n */\nexport const scheduleGatherMetrics = (fn: VoidFunction): VoidFunction => {\n let callsCount = 0;\n let draining = false;\n\n const step = () =>\n Promise.resolve()\n .then(() => {\n if (callsCount > 0) {\n callsCount -= 1;\n if (callsCount > 0) {\n void step();\n return;\n }\n draining = false;\n fn();\n return;\n }\n draining = false;\n })\n .catch((err) =>\n setTimeout(() => {\n throw err;\n }, 0)\n );\n\n return () => {\n callsCount += callsCount === 0 ? 2 : 1;\n if (!draining) {\n draining = true;\n void step();\n }\n };\n};\n\n/**\n * Хук для получения \"живой\" ссылки (`ref`) на актуальное значение.\n *\n * В отличие от обычного `useRef(initialValue)`, где `.current` инициализируется\n * только один раз и далее меняется вручную, `useActualRef` автоматически\n * обновляет `.current` при каждом рендере на основе переданного `value`.\n *\n * Зачем это нужно:\n * - Когда в коллбэках или подписках нужно иметь доступ к самому свежему значению\n * пропса или состояния, но при этом не хочется пересоздавать замыкания.\n * - Позволяет избежать проблем со \"старыми\" значениями внутри `useCallback`,\n * `useEffect` и обработчиков событий.\n *\n * @param value Актуальное значение, которое должно быть доступно через `.current`.\n *\n * @returns `ref`-объект (`MutableRefObject<T>`), чьё свойство `.current` всегда\n * указывает на последнее переданное `value`.\n */\nexport const useActualRef = <T>(value: T): MutableRefObject<T> => {\n const valueRef = useRef(value);\n valueRef.current = value;\n return valueRef;\n};\n\n/**\n * Подписывает трансформацию на изменения исходного `MotionValue` **и** внешнего хранилища ключевых подписок.\n *\n * В отличие от `useTransform` из motion, этот хук не создает новый `MotionValue`.\n * Вместо этого он вызывает пользовательскую `transformFn(arg, value)` при любом изменении:\n * - исходного `originalValue`, и/или\n * - значений во внешнем хранилище `store` по указанным `keys`.\n *\n * `transformFn` обычно внутри делает `.set(...)` у одного или нескольких целевых `MotionValue`.\n *\n * @template T Тип значения исходного `MotionValue`.\n * @template K Тип ключей внешнего хранилища (строка | число | символ).\n * @template V Тип агрегированного значения, извлекаемого из хранилища и передаваемого в `transformFn`.\n *\n * @param {MotionValue<T>} originalValue\n * Исходный `MotionValue`, изменения которого инициируют трансформацию.\n *\n * @param {KeyedSubscriptions<K> | (() => KeyedSubscriptions<K>)} store\n * Внешнее хранилище с подписками. Может быть самим объектом или фабрикой.\n * Если передана функция, хук пересоздает подписки при событии `onDestroy`.\n *\n * @param {K[]} keys\n * Набор ключей хранилища, на изменения которых нужно реагировать.\n * Равенство `keys` проверяется по количеству и составу; изменение порядка не важно.\n * Изменение этого набора вызывает пересоздание подписок подписок.\n *\n * @param {() => V} valueExtractor\n * Функция, синхронно извлекающая/агрегирующая данные из хранилища\n * для передачи первым аргументом в `transformFn`.\n *\n * @param {(arg: V, value: T) => void} transformFn\n * Функция-трансформация. Вызывается при каждом триггере с\n * `arg = valueExtractor()` и `value = originalValue.get()`. Обычно внутри вызывает `someMotionValue.set(...)`.\n *\n * @example\n * ```tsx\n * import { motion, useMotionValue } from \"motion/react\";\n *\n * const store: KeyedSubscriptions<\"width\" | \"height\"> = createKeyedStore();\n *\n * export function Example() {\n * const x = useMotionValue(0); // исходный драйвер\n * const y = useMotionValue(0); // целевой MotionValue\n *\n * useStoreSyncedTransform(\n * x,\n * store,\n * [\"width\", \"height\"],\n * () => ({\n * w: store.get(\"width\"),\n * h: store.get(\"height\"),\n * }),\n * ({ w, h }, xVal) => {\n * // пример простой зависимости\n * const mapped = (xVal / Math.max(w, 1)) * h;\n * y.set(mapped);\n * }\n * );\n *\n * return <motion.div style={{ x, y }} />;\n * }\n * ```\n */\nexport const useStoreSyncedTransform = <T, K extends PropertyKey, V>(\n originalValue: MotionValue<T>,\n store: KeyedSubscriptions<K>,\n keys: K[],\n valueExtractor: () => V,\n transformFn: (arg: V, value: T) => void\n): void => {\n const valueExtractorRef = useActualRef(valueExtractor);\n const extractedValueRef = useActualRef(valueExtractor());\n const transformFnRef = useActualRef(transformFn);\n const originalValueRef = useActualRef(originalValue);\n const transform = useInitOnce(\n () => () => transformFnRef.current(extractedValueRef.current, originalValueRef.current.get())\n );\n\n const observedKeysRef = useRef<Set<K> | null>(null);\n let observedKeys = observedKeysRef.current;\n const isKeysChanged =\n observedKeys === null || keys.length !== observedKeys.size || keys.some((k) => !observedKeys?.has(k));\n\n if (isKeysChanged) {\n observedKeys = new Set(keys);\n observedKeysRef.current = observedKeys;\n }\n\n useLayoutEffect(() => {\n const unsubscribeStore = store.onChange(observedKeys ? [...observedKeys] : [], () => {\n extractedValueRef.current = valueExtractorRef.current();\n transform();\n });\n const unsubscribeValue = originalValue.on('change', transform);\n\n return () => {\n unsubscribeStore();\n unsubscribeValue();\n };\n }, [store, observedKeys, originalValue, extractedValueRef, valueExtractorRef, transform]);\n\n useLayoutEffect(() => {\n transform();\n }, [valueExtractor, extractedValueRef, transformFn, transform]);\n};\n\nconst SCROLLABLE = ['auto', 'scroll'];\nexport const findScrollContainer = (\n element: HTMLElement\n):\n | { eventsProvider: HTMLElement; infoProvider: HTMLElement; mode: 'element' }\n | { eventsProvider: Window; infoProvider: Element; mode: 'window' } => {\n let parentElement = element.parentElement;\n while (parentElement !== null) {\n const { overflowY } = window.getComputedStyle(parentElement);\n if (SCROLLABLE.includes(overflowY)) {\n return { eventsProvider: parentElement, infoProvider: parentElement, mode: 'element' };\n }\n parentElement = parentElement.parentElement;\n }\n\n return {\n eventsProvider: window,\n infoProvider: document.scrollingElement || document.documentElement,\n mode: 'window',\n };\n};\n\nexport const isDOMRectsEqual = (a: DOMRectReadOnly | null, b: DOMRectReadOnly | null): boolean =>\n a === b || (a !== null && b !== null && a.x === b.x && a.y === b.y && a.height === b.height && a.width === b.width);\n\nexport interface MorphSetup {\n start: DOMRectReadOnly;\n end: DOMRectReadOnly;\n containerStart: DOMRectReadOnly;\n containerEnd: DOMRectReadOnly;\n}\n\nexport type SizeAxis = 'vertical' | 'horizontal' | 'both' | 'auto';\nexport type HorizontalAlign = 'left' | 'right' | 'center';\nexport type VerticalAlign = 'top' | 'bottom' | 'center';\n\nconst MAX_SCALE_RATIO = 2;\nconst AXIS_SIZE_MULTIPLIER = {\n left: 0,\n center: 0.5,\n right: 1,\n top: 0,\n bottom: 1,\n} as const;\n\nexport const calcMorphParams = (\n start: DOMRectReadOnly | null,\n end: DOMRectReadOnly | null,\n sizeAxis: SizeAxis,\n horizontalPositionAlign: HorizontalAlign,\n verticalPositionAlign: VerticalAlign\n): { deltaX: number; deltaY: number; scaleX: number; scaleY: number } => {\n if (start === null || end === null) {\n return {\n deltaX: 0,\n deltaY: 0,\n scaleX: 1,\n scaleY: 1,\n };\n }\n\n let axis = sizeAxis;\n let scaleX = end.width / start.width;\n let scaleY = end.height / start.height;\n\n if (sizeAxis === 'auto') {\n const ratio = Math.max(scaleX, scaleY) / Math.min(scaleX, scaleY);\n if (ratio > MAX_SCALE_RATIO) {\n const absScaleX = scaleX < 1 ? 1 / scaleX : scaleX;\n const absScaleY = scaleY < 1 ? 1 / scaleY : scaleY;\n axis = absScaleX < absScaleY ? 'horizontal' : 'vertical';\n }\n }\n\n scaleX = axis === 'vertical' ? scaleY : scaleX;\n scaleY = axis === 'horizontal' ? scaleX : scaleY;\n\n const startX = start.x + start.width * AXIS_SIZE_MULTIPLIER[horizontalPositionAlign];\n const endX = end.x + end.width * AXIS_SIZE_MULTIPLIER[horizontalPositionAlign];\n const startY = start.y + start.height * AXIS_SIZE_MULTIPLIER[verticalPositionAlign];\n const endY = end.y + end.height * AXIS_SIZE_MULTIPLIER[verticalPositionAlign];\n\n const deltaX = endX - startX;\n const deltaY = endY - startY;\n\n return {\n deltaX,\n deltaY,\n scaleX,\n scaleY,\n };\n};\n\nexport const getRelativeOffset = (scrollAdapter: ScrollAdapter, element: HTMLElement | null): number => {\n if (!scrollAdapter.scrollContainer.current || !element) {\n return 0;\n }\n\n const container = scrollAdapter.scrollContainer.current;\n const containerTop = container === document.documentElement ? 0 : container.getBoundingClientRect().top;\n const scroll = scrollAdapter.getScrollTop();\n scrollAdapter.setScrollTop(0);\n const elementTop = element.getBoundingClientRect().top;\n scrollAdapter.setScrollTop(scroll);\n\n return elementTop - containerTop;\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"utils.js","sources":["../../src/internal/utils.ts"],"sourcesContent":["import { type MutableRefObject, useLayoutEffect, useRef } from 'react';\nimport type { MotionValue } from 'motion';\n\nimport { KeyedSubscriptions } from '@hh.ru/magritte-ui-nav-bar/internal/KeyedSubscriptions';\nimport { ScrollAdapter } from '@hh.ru/magritte-ui-nav-bar/internal/useScrollAdapter';\n\nexport const lerp = (from: number, to: number, progress: number): number => from + (to - from) * progress;\nexport const clamp = (value: number, min: number, max: number): number => Math.min(max, Math.max(min, value));\n\n/**\n * Создаёт функцию преобразования значений из одного числового интервала (домен) в другой (образ).\n *\n * @param domain Интервал входных значений [min, max].\n * @param image Интервал выходных значений [min, max].\n *\n * @returns Функция, принимающая число из диапазона `domain`, преобразующая его в диапазон `image`\n * с линейной интерполяцией.\n */\nexport const remap = (domain: [number, number], image: [number, number]) => {\n const domainRange = domain[1] - domain[0];\n const imageRange = image[1] - image[0];\n const min = Math.min(...image);\n const max = Math.max(...image);\n\n if (domainRange === 0) {\n return () => image[0];\n }\n\n const a = imageRange / domainRange;\n const b = image[0] - a * domain[0];\n\n return (value: number): number => {\n // из-за ошибок в числах с плавающей запятой можем не достигать пределов, поэтому дополнительно\n // проверяем вручную\n if (value === domain[0] || value === domain[1]) {\n return value === domain[0] ? image[0] : image[1];\n }\n return clamp(a * value + b, min, max);\n };\n};\n\nexport const useInitOnce = <T>(fn: () => T): T => {\n const ref = useRef<T | null>(null);\n if (ref.current === null) {\n ref.current = fn();\n }\n return ref.current;\n};\n\n/**\n * Возвращает «одноразовый» планировщик, который выполнит `fn` в следующем\n * тике **микрозадач** (через `Promise.resolve().then(...)`). Повторные вызовы\n * до момента выполнения схлопываются в один запуск.\n *\n * Семантика:\n * - Первый вызов ставит `fn` в очередь микрозадач; последующие вызовы до\n * выполнения игнорируются.\n * - После выполнения `fn` «засов» снимается — можно планировать снова.\n * - Ошибки из `fn` перебрасываются в следующий макротик (через `setTimeout`),\n * чтобы они были видны в DevTools и не превращались в «unhandled rejection».\n *\n * Когда использовать:\n * - Нужно объединить множество синхронных триггеров в одно действие,\n * выполнив его **раньше таймеров/`requestAnimationFrame`**.\n * - Не требуется ожидать стабилизации DOM/лейаута (микрозадачи идут до кадра).\n *\n * @param fn — колбэк, который будет выполнен в следующем тике микрозадач.\n * @returns Функция-триггер: вызов ставит `fn` в очередь 1 раз на тик микрозадач.\n *\n * @example\n * const trigger = scheduleMicro(recompute);\n * trigger(); // запланирован один запуск\n * trigger(); // проигнорирован (уже запланировано)\n * // `recompute` выполнится один раз на следующем тике микрозадач\n */\nexport const scheduleMicro = (fn: VoidFunction) => {\n let scheduled = false;\n return (): void => {\n if (scheduled) {\n return;\n }\n scheduled = true;\n Promise.resolve()\n .then(() => {\n scheduled = false;\n fn();\n })\n .catch((err) =>\n setTimeout(() => {\n throw err;\n }, 0)\n );\n };\n};\n\n/**\n * Возвращает «одноразовый» планировщик, который выполнит `fn` в следующем\n * тике **макрозадач** (через `setTimeout(0)`). Повторные вызовы до момента\n * выполнения схлопываются в один запуск.\n *\n * Семантика:\n * - Первый вызов ставит `fn` в `setTimeout`; последующие вызовы до выполнения\n * игнорируются.\n * - После выполнения `fn` «засов» снимается — можно планировать снова.\n * - Ошибки из `fn` всплывут как непойманные ошибки из обработчика таймера.\n *\n * Когда использовать:\n * - Нужно отложить выполнение **после** опустошения очереди микрозадач\n * (т.е. позже, чем `scheduleMicro`), часто позволяя браузеру между делом\n * подготовить кадр/перерисовку.\n *\n * @param fn — колбэк, который будет выполнен в следующем тике макрозадач.\n * @returns Функция-триггер: вызов ставит `fn` в очередь 1 раз на тик таймера.\n *\n * @example\n * const trigger = scheduleMacro(flushQueue);\n * trigger(); // запланирован один таймер\n * trigger(); // проигнорирован до выполнения `flushQueue`\n */\nexport const scheduleMacro = (fn: VoidFunction): VoidFunction => {\n let scheduled = false;\n const scheduledFn = () => {\n if (scheduled) {\n return;\n }\n scheduled = true;\n setTimeout(() => {\n scheduled = false;\n fn();\n });\n };\n return scheduledFn;\n};\n\n/**\n * Планировщик для «сбора метрик» на основе микрозадач: откладывает выполнение `fn`\n * до момента, когда серия частых вызовов завершится, и запускает `fn` ровно один раз.\n *\n * Идея:\n * - Первый вызов добавляет «запас» из двух тиков микрозадач; каждый следующий\n * в ту же серию добавляет ещё +1.\n * - Единый «дренирующий» цикл (один на серию) уменьшает счётчик на каждом тике\n * микрозадач; когда счётчик достигает нуля — выполняется `fn`.\n * - Повторные вызовы не создают параллельных циклов; `fn` не вызывается несколько раз.\n * - Ошибки из `fn` перебрасываются в макрозадачу (`setTimeout`), чтобы их было видно\n * в DevTools и они не становились unhandled rejection.\n *\n * Когда использовать:\n * - Нужна коалесценция множественных триггеров (Promises, ResizeObserver и т.п.)\n * с запуском «после того, как всё успокоилось», чтобы измерить DOM/пересчитать\n * лейаут/агрегировать состояние один раз.\n *\n * Гарантии и нюансы:\n * - Гарантируется минимум два тика микрозадач после первого вызова (даёт времени\n * промисам/микроочереди завершиться).\n * - Работает как эвристика «дождаться затишья» на уровне микрозадач, без перехода\n * в макрозадачи/таймеры (сам `fn` всё равно выполняется в микрозадаче).\n *\n * @param fn — колбэк, который будет выполнен один раз после завершения серии вызовов.\n * @returns Триггер; его можно вызывать многократно — `fn` запустится один раз,\n * когда внутренний счётчик дойдёт до нуля.\n */\nexport const scheduleGatherMetrics = (fn: VoidFunction): VoidFunction => {\n let callsCount = 0;\n let draining = false;\n\n const step = () =>\n Promise.resolve()\n .then(() => {\n if (callsCount > 0) {\n callsCount -= 1;\n if (callsCount > 0) {\n void step();\n return;\n }\n draining = false;\n fn();\n return;\n }\n draining = false;\n })\n .catch((err) =>\n setTimeout(() => {\n throw err;\n }, 0)\n );\n\n return () => {\n callsCount += callsCount === 0 ? 2 : 1;\n if (!draining) {\n draining = true;\n void step();\n }\n };\n};\n\n/**\n * Хук для получения \"живой\" ссылки (`ref`) на актуальное значение.\n *\n * В отличие от обычного `useRef(initialValue)`, где `.current` инициализируется\n * только один раз и далее меняется вручную, `useActualRef` автоматически\n * обновляет `.current` при каждом рендере на основе переданного `value`.\n *\n * Зачем это нужно:\n * - Когда в коллбэках или подписках нужно иметь доступ к самому свежему значению\n * пропса или состояния, но при этом не хочется пересоздавать замыкания.\n * - Позволяет избежать проблем со \"старыми\" значениями внутри `useCallback`,\n * `useEffect` и обработчиков событий.\n *\n * @param value Актуальное значение, которое должно быть доступно через `.current`.\n *\n * @returns `ref`-объект (`MutableRefObject<T>`), чьё свойство `.current` всегда\n * указывает на последнее переданное `value`.\n */\nexport const useActualRef = <T>(value: T): MutableRefObject<T> => {\n const valueRef = useRef(value);\n valueRef.current = value;\n return valueRef;\n};\n\n/**\n * Подписывает трансформацию на изменения исходного `MotionValue` **и** внешнего хранилища ключевых подписок.\n *\n * В отличие от `useTransform` из motion, этот хук не создает новый `MotionValue`.\n * Вместо этого он вызывает пользовательскую `transformFn(arg, value)` при любом изменении:\n * - исходного `originalValue`, и/или\n * - значений во внешнем хранилище `store` по указанным `keys`.\n *\n * `transformFn` обычно внутри делает `.set(...)` у одного или нескольких целевых `MotionValue`.\n *\n * @template T Тип значения исходного `MotionValue`.\n * @template K Тип ключей внешнего хранилища (строка | число | символ).\n * @template V Тип агрегированного значения, извлекаемого из хранилища и передаваемого в `transformFn`.\n *\n * @param {MotionValue<T>} originalValue\n * Исходный `MotionValue`, изменения которого инициируют трансформацию.\n *\n * @param {KeyedSubscriptions<K> | (() => KeyedSubscriptions<K>)} store\n * Внешнее хранилище с подписками. Может быть самим объектом или фабрикой.\n * Если передана функция, хук пересоздает подписки при событии `onDestroy`.\n *\n * @param {K[]} keys\n * Набор ключей хранилища, на изменения которых нужно реагировать.\n * Равенство `keys` проверяется по количеству и составу; изменение порядка не важно.\n * Изменение этого набора вызывает пересоздание подписок подписок.\n *\n * @param {() => V} valueExtractor\n * Функция, синхронно извлекающая/агрегирующая данные из хранилища\n * для передачи первым аргументом в `transformFn`.\n *\n * @param {(arg: V, value: T) => void} transformFn\n * Функция-трансформация. Вызывается при каждом триггере с\n * `arg = valueExtractor()` и `value = originalValue.get()`. Обычно внутри вызывает `someMotionValue.set(...)`.\n *\n * @example\n * ```tsx\n * import { motion, useMotionValue } from \"motion/react\";\n *\n * const store: KeyedSubscriptions<\"width\" | \"height\"> = createKeyedStore();\n *\n * export function Example() {\n * const x = useMotionValue(0); // исходный драйвер\n * const y = useMotionValue(0); // целевой MotionValue\n *\n * useStoreSyncedTransform(\n * x,\n * store,\n * [\"width\", \"height\"],\n * () => ({\n * w: store.get(\"width\"),\n * h: store.get(\"height\"),\n * }),\n * ({ w, h }, xVal) => {\n * // пример простой зависимости\n * const mapped = (xVal / Math.max(w, 1)) * h;\n * y.set(mapped);\n * }\n * );\n *\n * return <motion.div style={{ x, y }} />;\n * }\n * ```\n */\nexport const useStoreSyncedTransform = <T, K extends PropertyKey, V>(\n originalValue: MotionValue<T>,\n store: KeyedSubscriptions<K>,\n keys: K[],\n valueExtractor: () => V,\n transformFn: (arg: V, value: T) => void\n): void => {\n const valueExtractorRef = useActualRef(valueExtractor);\n const extractedValueRef = useActualRef(valueExtractor());\n const transformFnRef = useActualRef(transformFn);\n const originalValueRef = useActualRef(originalValue);\n const transform = useInitOnce(\n () => () => transformFnRef.current(extractedValueRef.current, originalValueRef.current.get())\n );\n\n const observedKeysRef = useRef<Set<K> | null>(null);\n let observedKeys = observedKeysRef.current;\n const isKeysChanged =\n observedKeys === null || keys.length !== observedKeys.size || keys.some((k) => !observedKeys?.has(k));\n\n if (isKeysChanged) {\n observedKeys = new Set(keys);\n observedKeysRef.current = observedKeys;\n }\n\n useLayoutEffect(() => {\n const unsubscribeStore = store.onChange(observedKeys ? [...observedKeys] : [], () => {\n extractedValueRef.current = valueExtractorRef.current();\n transform();\n });\n const unsubscribeValue = originalValue.on('change', transform);\n\n return () => {\n unsubscribeStore();\n unsubscribeValue();\n };\n }, [store, observedKeys, originalValue, extractedValueRef, valueExtractorRef, transform]);\n\n useLayoutEffect(() => {\n transform();\n }, [valueExtractor, extractedValueRef, transformFn, transform]);\n};\n\nconst SCROLLABLE = ['auto', 'scroll'];\nexport const findScrollContainer = (\n element: HTMLElement\n):\n | { eventsProvider: HTMLElement; infoProvider: HTMLElement; mode: 'element' }\n | { eventsProvider: Window; infoProvider: Element; mode: 'window' } => {\n let parentElement = element.parentElement;\n while (parentElement !== null) {\n const { overflowY } = window.getComputedStyle(parentElement);\n if (SCROLLABLE.includes(overflowY)) {\n return { eventsProvider: parentElement, infoProvider: parentElement, mode: 'element' };\n }\n parentElement = parentElement.parentElement;\n }\n\n return {\n eventsProvider: window,\n infoProvider: document.scrollingElement || document.documentElement,\n mode: 'window',\n };\n};\n\nexport const isDOMRectsEqual = (a: DOMRectReadOnly | null, b: DOMRectReadOnly | null): boolean =>\n a === b || (a !== null && b !== null && a.x === b.x && a.y === b.y && a.height === b.height && a.width === b.width);\n\nexport interface MorphSetup {\n start: DOMRectReadOnly;\n end: DOMRectReadOnly;\n containerStart: DOMRectReadOnly;\n containerEnd: DOMRectReadOnly;\n}\n\nexport type SizeAxis = 'vertical' | 'horizontal' | 'both' | 'auto';\nexport type HorizontalAlign = 'left' | 'right' | 'center';\nexport type VerticalAlign = 'top' | 'bottom' | 'center';\n\nconst MAX_SCALE_RATIO = 2;\nconst AXIS_SIZE_MULTIPLIER = {\n left: 0,\n center: 0.5,\n right: 1,\n top: 0,\n bottom: 1,\n} as const;\n\nexport const calcMorphParams = (\n start: DOMRectReadOnly | null,\n end: DOMRectReadOnly | null,\n sizeAxis: SizeAxis,\n horizontalPositionAlign: HorizontalAlign,\n verticalPositionAlign: VerticalAlign\n): { deltaX: number; deltaY: number; scaleX: number; scaleY: number } => {\n if (start === null || end === null) {\n return {\n deltaX: 0,\n deltaY: 0,\n scaleX: 1,\n scaleY: 1,\n };\n }\n\n let axis = sizeAxis;\n let scaleX = end.width / start.width;\n let scaleY = end.height / start.height;\n\n if (sizeAxis === 'auto') {\n const ratio = Math.max(scaleX, scaleY) / Math.min(scaleX, scaleY);\n if (ratio > MAX_SCALE_RATIO) {\n const absScaleX = scaleX < 1 ? 1 / scaleX : scaleX;\n const absScaleY = scaleY < 1 ? 1 / scaleY : scaleY;\n axis = absScaleX < absScaleY ? 'horizontal' : 'vertical';\n }\n }\n\n scaleX = axis === 'vertical' ? scaleY : scaleX;\n scaleY = axis === 'horizontal' ? scaleX : scaleY;\n\n const startX = start.x + start.width * AXIS_SIZE_MULTIPLIER[horizontalPositionAlign];\n const endX = end.x + end.width * AXIS_SIZE_MULTIPLIER[horizontalPositionAlign];\n const startY = start.y + start.height * AXIS_SIZE_MULTIPLIER[verticalPositionAlign];\n const endY = end.y + end.height * AXIS_SIZE_MULTIPLIER[verticalPositionAlign];\n\n const deltaX = endX - startX;\n const deltaY = endY - startY;\n\n return {\n deltaX,\n deltaY,\n scaleX,\n scaleY,\n };\n};\n\nexport const getRelativeOffset = (scrollAdapter: ScrollAdapter, element: HTMLElement | null): number => {\n if (!scrollAdapter.scrollContainer.current || !element) {\n return 0;\n }\n\n const container = scrollAdapter.scrollContainer.current;\n const containerTop = container === document.documentElement ? 0 : container.getBoundingClientRect().top;\n const scroll = scrollAdapter.getScrollTop();\n scrollAdapter.setScrollTop(0);\n const elementTop = element.getBoundingClientRect().top;\n scrollAdapter.setScrollTop(scroll);\n\n return elementTop - containerTop;\n};\n"],"names":[],"mappings":"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"}