@hh.ru/magritte-ui-nav-bar 1.3.26 → 1.3.28
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.
- package/index.css +99 -99
- package/index.d.ts +45 -10
- package/index.js +47 -21
- package/index.js.map +1 -1
- package/index.mock.d.ts +13 -1
- package/index.mock.js +17 -3
- package/index.mock.js.map +1 -1
- package/internal/useBindScrollToAnimationProgress.d.ts +2 -1
- package/internal/useBindScrollToAnimationProgress.js +9 -5
- package/internal/useBindScrollToAnimationProgress.js.map +1 -1
- package/internal/utils.d.ts +2 -1
- package/internal/utils.js +4 -3
- package/internal/utils.js.map +1 -1
- package/nav-bar-Bgo6GHaL.js +5 -0
- package/nav-bar-Bgo6GHaL.js.map +1 -0
- package/package.json +7 -5
- package/public/Actions.js +1 -1
- package/public/LayoutMorph.js +1 -1
- package/public/LayoutStage.js +1 -1
- package/public/Morph.js +1 -1
- package/public/NavBar.d.ts +11 -0
- package/public/NavBar.js +12 -3
- package/public/NavBar.js.map +1 -1
- package/public/Pane.js +1 -1
- package/public/Stage.js +1 -1
- package/public/TitleContainer.js +5 -2
- package/public/TitleContainer.js.map +1 -1
- package/public/Trigger.d.ts +29 -0
- package/public/Trigger.js +37 -0
- package/public/Trigger.js.map +1 -0
- package/public/TriggerContext.d.ts +20 -0
- package/public/TriggerContext.js +7 -0
- package/public/TriggerContext.js.map +1 -0
- package/nav-bar-CmjjkPy6.js +0 -5
- package/nav-bar-CmjjkPy6.js.map +0 -1
package/index.mock.js
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import './index.css';
|
|
2
2
|
import { mockComponent } from '@hh.ru/magritte-ui-mock-component';
|
|
3
3
|
|
|
4
|
-
const NavBar = mockComponent('NavBar', undefined, {
|
|
5
|
-
withChildren: true,
|
|
6
|
-
});
|
|
7
4
|
const Actions = mockComponent('Action', undefined, { withChildren: true });
|
|
8
5
|
const TitleContainer = mockComponent('TitleContainer', undefined, { withChildren: true });
|
|
9
6
|
const Pane = mockComponent('Pane', undefined, { withChildren: true });
|
|
@@ -12,6 +9,23 @@ const Stage = mockComponent('Stage', undefined, { withChildren: true });
|
|
|
12
9
|
const LayoutMorph = mockComponent('LayoutMorph', undefined, { withChildren: true });
|
|
13
10
|
const LayoutStage = mockComponent('LayoutStage', undefined, { withChildren: true });
|
|
14
11
|
const EnvironmentFingerprintNode = mockComponent('EnvironmentFingerprintNode', undefined, { withChildren: true });
|
|
12
|
+
// Доступны только через compound `NavBar.*` (см. ниже), как и в реальном пакете.
|
|
13
|
+
const Trigger = mockComponent('Trigger', undefined, { withChildren: true });
|
|
14
|
+
const TriggerProvider = mockComponent('TriggerProvider', undefined, { withChildren: true });
|
|
15
|
+
const DefaultPropsContext = mockComponent('NavBarDefaultPropsContext', undefined, { withChildren: true });
|
|
16
|
+
const NavBar = Object.assign(mockComponent('NavBar', undefined, { withChildren: true }), {
|
|
17
|
+
Trigger,
|
|
18
|
+
TriggerProvider,
|
|
19
|
+
Pane,
|
|
20
|
+
Stage,
|
|
21
|
+
Morph,
|
|
22
|
+
Actions,
|
|
23
|
+
TitleContainer,
|
|
24
|
+
LayoutStage,
|
|
25
|
+
LayoutMorph,
|
|
26
|
+
EnvironmentFingerprintNode,
|
|
27
|
+
DefaultPropsContext,
|
|
28
|
+
});
|
|
15
29
|
|
|
16
30
|
export { Actions, EnvironmentFingerprintNode, LayoutMorph, LayoutStage, Morph, NavBar, Pane, Stage, TitleContainer };
|
|
17
31
|
//# sourceMappingURL=index.mock.js.map
|
package/index.mock.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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
|
|
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 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\n// Доступны только через compound `NavBar.*` (см. ниже), как и в реальном пакете.\nconst Trigger = mockComponent('Trigger', undefined, { withChildren: true });\nconst TriggerProvider = mockComponent('TriggerProvider', undefined, { withChildren: true });\nconst DefaultPropsContext = mockComponent('NavBarDefaultPropsContext', undefined, { withChildren: true });\n\nexport const NavBar = Object.assign(mockComponent('NavBar', undefined, { withChildren: true }), {\n Trigger,\n TriggerProvider,\n Pane,\n Stage,\n Morph,\n Actions,\n TitleContainer,\n LayoutStage,\n LayoutMorph,\n EnvironmentFingerprintNode,\n DefaultPropsContext,\n});\n"],"names":[],"mappings":";;AAYa,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,EACxB;AAEF;AACA,MAAM,OAAO,GAAG,aAAa,CAAC,SAAS,EAAE,SAAS,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC;AAC5E,MAAM,eAAe,GAAG,aAAa,CAAC,iBAAiB,EAAE,SAAS,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC;AAC5F,MAAM,mBAAmB,GAAG,aAAa,CAAC,2BAA2B,EAAE,SAAS,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC;MAE7F,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,QAAQ,EAAE,SAAS,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,EAAE;IAC5F,OAAO;IACP,eAAe;IACf,IAAI;IACJ,KAAK;IACL,KAAK;IACL,OAAO;IACP,cAAc;IACd,WAAW;IACX,WAAW;IACX,0BAA0B;IAC1B,mBAAmB;AACtB,CAAA;;;;"}
|
|
@@ -3,7 +3,8 @@ 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
|
-
|
|
6
|
+
import { type TriggerEdge } from '@hh.ru/magritte-ui-nav-bar/public/TriggerContext';
|
|
7
|
+
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 | undefined, startTriggerEdge?: TriggerEdge, endTriggerEdge?: TriggerEdge) => [VoidFunction, () => {
|
|
7
8
|
top: number;
|
|
8
9
|
bottom: number;
|
|
9
10
|
}, MotionValue<number>, MotionStyle];
|
|
@@ -4,7 +4,7 @@ import { useMotionValue } from 'motion/react';
|
|
|
4
4
|
import { useActualRef, useInitOnce, getRelativeOffset, remap, clamp } from './utils.js';
|
|
5
5
|
import 'motion';
|
|
6
6
|
|
|
7
|
-
const createScrollToAnimationProgressMapFn = (getNavBarMetrics, scrollAdapter, animationRanges, startTriggerPosition, endTriggerPosition) => {
|
|
7
|
+
const createScrollToAnimationProgressMapFn = (getNavBarMetrics, scrollAdapter, animationRanges, startTriggerPosition, endTriggerPosition, startTriggerEdge, endTriggerEdge) => {
|
|
8
8
|
const maxScrollTop = scrollAdapter.getMaxScrollTop();
|
|
9
9
|
const { heightDelta, top, bottom, animationHeight } = getNavBarMetrics();
|
|
10
10
|
if (startTriggerPosition === 'full-area') {
|
|
@@ -31,14 +31,14 @@ const createScrollToAnimationProgressMapFn = (getNavBarMetrics, scrollAdapter, a
|
|
|
31
31
|
}
|
|
32
32
|
let startPos = startTriggerPosition === 'start' || !startTriggerPosition.current
|
|
33
33
|
? 0
|
|
34
|
-
: getRelativeOffset(scrollAdapter, startTriggerPosition.current) - bottom;
|
|
34
|
+
: getRelativeOffset(scrollAdapter, startTriggerPosition.current, startTriggerEdge) - bottom;
|
|
35
35
|
let endPos;
|
|
36
36
|
if (typeof endTriggerPosition === 'number') {
|
|
37
37
|
endPos = startPos + endTriggerPosition;
|
|
38
38
|
}
|
|
39
39
|
else {
|
|
40
40
|
endPos = endTriggerPosition?.current
|
|
41
|
-
? getRelativeOffset(scrollAdapter, endTriggerPosition.current) + heightDelta - bottom
|
|
41
|
+
? getRelativeOffset(scrollAdapter, endTriggerPosition.current, endTriggerEdge) + heightDelta - bottom
|
|
42
42
|
: startPos + animationHeight;
|
|
43
43
|
}
|
|
44
44
|
endPos += top;
|
|
@@ -52,12 +52,16 @@ const createScrollToAnimationProgressMapFn = (getNavBarMetrics, scrollAdapter, a
|
|
|
52
52
|
return [remap([startPos, endPos], [0, endProgress]), () => ({ bottom: startPos, top: endPos })];
|
|
53
53
|
};
|
|
54
54
|
const isDividerVisible = (scrollTop, navBarTop, heightDelta, animationProgress) => Math.ceil(navBarTop + heightDelta * animationProgress) < Math.floor(scrollTop);
|
|
55
|
-
const useBindScrollToAnimationProgress = (scrollPosition, getNavBarMetrics, getAnimationRanges, scrollAdapter, startTriggerPosition, endTriggerPosition
|
|
55
|
+
const useBindScrollToAnimationProgress = (scrollPosition, getNavBarMetrics, getAnimationRanges, scrollAdapter, startTriggerPosition, endTriggerPosition,
|
|
56
|
+
// Дефолты дублируют продуктовые дефолты `NavBar` (начало — по верхнему краю, конец — по нижнему).
|
|
57
|
+
startTriggerEdge = 'top', endTriggerEdge = 'bottom') => {
|
|
56
58
|
const totalAnimationProgress = useMotionValue(0);
|
|
57
59
|
const dividerVisibility = useMotionValue('hidden');
|
|
58
60
|
const dividerOffsetY = useMotionValue(0);
|
|
59
61
|
const startTriggerPositionRef = useActualRef(startTriggerPosition);
|
|
60
62
|
const endTriggerPositionRef = useActualRef(endTriggerPosition);
|
|
63
|
+
const startTriggerEdgeRef = useActualRef(startTriggerEdge);
|
|
64
|
+
const endTriggerEdgeRef = useActualRef(endTriggerEdge);
|
|
61
65
|
const getClosestStopsRef = useRef(null);
|
|
62
66
|
const bindScrollToAnimation = useInitOnce(() => {
|
|
63
67
|
let unsubscribe = null;
|
|
@@ -70,7 +74,7 @@ const useBindScrollToAnimationProgress = (scrollPosition, getNavBarMetrics, getA
|
|
|
70
74
|
range[1] / animationHeight,
|
|
71
75
|
])),
|
|
72
76
|
].sort((a, b) => b[0] - a[0]);
|
|
73
|
-
const [mapFn, getClosestStops] = createScrollToAnimationProgressMapFn(getNavBarMetrics, scrollAdapter, animationRanges, startTriggerPositionRef.current, endTriggerPositionRef.current);
|
|
77
|
+
const [mapFn, getClosestStops] = createScrollToAnimationProgressMapFn(getNavBarMetrics, scrollAdapter, animationRanges, startTriggerPositionRef.current, endTriggerPositionRef.current, startTriggerEdgeRef.current, endTriggerEdgeRef.current);
|
|
74
78
|
getClosestStopsRef.current = getClosestStops;
|
|
75
79
|
const updateDivider = (scrollPosition, totalAnimationProgress) => {
|
|
76
80
|
dividerVisibility.set(isDividerVisible(scrollPosition, top, heightDelta, totalAnimationProgress) ? 'visible' : 'hidden');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useBindScrollToAnimationProgress.js","sources":["src/internal/useBindScrollToAnimationProgress.ts"],"sourcesContent":["import { type MutableRefObject, type RefObject, useRef } from 'react';\nimport { type MotionStyle, type MotionValue, useMotionValue } from 'motion/react';\n\nimport { type AnimationRanges } from '@hh.ru/magritte-ui-nav-bar/internal/useAnimationRanges';\nimport { type NavBarMetrics } from '@hh.ru/magritte-ui-nav-bar/internal/useNavBarMetrics';\nimport { type ScrollAdapter } from '@hh.ru/magritte-ui-nav-bar/internal/useScrollAdapter';\nimport { remap, clamp, useInitOnce, useActualRef, getRelativeOffset } from '@hh.ru/magritte-ui-nav-bar/internal/utils';\n\nconst createScrollToAnimationProgressMapFn = (\n getNavBarMetrics: () => NavBarMetrics,\n scrollAdapter: ScrollAdapter,\n animationRanges: [number, number][],\n startTriggerPosition: 'start' | 'full-area' | RefObject<HTMLElement | null>,\n endTriggerPosition?: RefObject<HTMLElement | null> | number\n) => {\n const maxScrollTop = scrollAdapter.getMaxScrollTop();\n const { heightDelta, top, bottom, animationHeight } = getNavBarMetrics();\n\n if (startTriggerPosition === 'full-area') {\n let currentStart = top;\n const maxProgress =\n maxScrollTop >= animationHeight + top\n ? 1\n : (animationRanges.find((range) => range[0] * animationHeight <= maxScrollTop - top) ?? [0])[0];\n\n const mapScrollToAnimationPropgress = (scrollPosition: number) => {\n const progress = (scrollPosition - currentStart) / animationHeight;\n if (progress > maxProgress) {\n currentStart = scrollPosition - animationHeight * maxProgress;\n } else if (progress < 0) {\n currentStart = scrollPosition;\n }\n currentStart = Math.max(currentStart, top);\n\n return clamp(progress, 0, maxProgress);\n };\n\n const getClosestStops = () => ({\n bottom: currentStart,\n top: currentStart + animationHeight * maxProgress,\n });\n\n return [mapScrollToAnimationPropgress, getClosestStops] as const;\n }\n\n let startPos =\n startTriggerPosition === 'start' || !startTriggerPosition.current\n ? 0\n : getRelativeOffset(scrollAdapter, startTriggerPosition.current) - bottom;\n\n let endPos: number;\n if (typeof endTriggerPosition === 'number') {\n endPos = startPos + endTriggerPosition;\n } else {\n endPos = endTriggerPosition?.current\n ? getRelativeOffset(scrollAdapter, endTriggerPosition.current) + heightDelta - bottom\n : startPos + animationHeight;\n }\n\n endPos += top;\n startPos += top;\n\n let endProgress = 1;\n if (endPos > maxScrollTop) {\n const scrollDelta = endPos - startPos;\n endProgress = (animationRanges.find((range) => range[0] * scrollDelta + startPos < maxScrollTop) ?? [0])[0];\n endPos = startPos + scrollDelta * endProgress;\n }\n\n return [remap([startPos, endPos], [0, endProgress]), () => ({ bottom: startPos, top: endPos })] as const;\n};\n\nconst isDividerVisible = (scrollTop: number, navBarTop: number, heightDelta: number, animationProgress: number) =>\n Math.ceil(navBarTop + heightDelta * animationProgress) < Math.floor(scrollTop);\n\nexport const useBindScrollToAnimationProgress = (\n scrollPosition: MotionValue<number>,\n getNavBarMetrics: () => NavBarMetrics,\n getAnimationRanges: () => AnimationRanges,\n scrollAdapter: ScrollAdapter,\n startTriggerPosition: 'start' | 'full-area' | RefObject<HTMLElement | null>,\n endTriggerPosition?: RefObject<HTMLElement | null> | number\n): [VoidFunction, () => { top: number; bottom: number }, MotionValue<number>, MotionStyle] => {\n const totalAnimationProgress = useMotionValue(0);\n const dividerVisibility = useMotionValue<'visible' | 'hidden'>('hidden');\n const dividerOffsetY = useMotionValue(0);\n const startTriggerPositionRef = useActualRef(startTriggerPosition);\n const endTriggerPositionRef = useActualRef(endTriggerPosition);\n const getClosestStopsRef: MutableRefObject<(() => { top: number; bottom: number }) | null> = useRef(null);\n\n const bindScrollToAnimation = useInitOnce(() => {\n let unsubscribe: VoidFunction | null = null;\n return () => {\n unsubscribe?.();\n const { heightDelta, top, animationHeight } = getNavBarMetrics();\n\n const animationRanges = [\n ...new Set<[number, number]>(\n [...getAnimationRanges().values()].map((range) => [\n range[0] / animationHeight,\n range[1] / animationHeight,\n ])\n ),\n ].sort((a, b) => b[0] - a[0]);\n\n const [mapFn, getClosestStops] = createScrollToAnimationProgressMapFn(\n getNavBarMetrics,\n scrollAdapter,\n animationRanges,\n startTriggerPositionRef.current,\n endTriggerPositionRef.current\n );\n getClosestStopsRef.current = getClosestStops;\n const updateDivider = (scrollPosition: number, totalAnimationProgress: number) => {\n dividerVisibility.set(\n isDividerVisible(scrollPosition, top, heightDelta, totalAnimationProgress) ? 'visible' : 'hidden'\n );\n dividerOffsetY.set(-heightDelta * totalAnimationProgress);\n };\n unsubscribe = scrollPosition.on('change', (value) => {\n totalAnimationProgress.set(mapFn(value));\n updateDivider(value, totalAnimationProgress.get());\n });\n\n totalAnimationProgress.set(mapFn(scrollPosition.get()));\n updateDivider(scrollPosition.get(), totalAnimationProgress.get());\n };\n });\n\n const getClosestStops = useInitOnce(() => () => {\n if (getClosestStopsRef.current) {\n return getClosestStopsRef.current();\n }\n\n return { bottom: 0, top: getNavBarMetrics().heightDelta };\n });\n\n return [\n bindScrollToAnimation,\n getClosestStops,\n totalAnimationProgress,\n { visibility: dividerVisibility, y: dividerOffsetY },\n ];\n};\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useBindScrollToAnimationProgress.js","sources":["src/internal/useBindScrollToAnimationProgress.ts"],"sourcesContent":["import { type MutableRefObject, type RefObject, useRef } from 'react';\nimport { type MotionStyle, type MotionValue, useMotionValue } from 'motion/react';\n\nimport { type AnimationRanges } from '@hh.ru/magritte-ui-nav-bar/internal/useAnimationRanges';\nimport { type NavBarMetrics } from '@hh.ru/magritte-ui-nav-bar/internal/useNavBarMetrics';\nimport { type ScrollAdapter } from '@hh.ru/magritte-ui-nav-bar/internal/useScrollAdapter';\nimport { remap, clamp, useInitOnce, useActualRef, getRelativeOffset } from '@hh.ru/magritte-ui-nav-bar/internal/utils';\nimport { type TriggerEdge } from '@hh.ru/magritte-ui-nav-bar/public/TriggerContext';\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 | undefined,\n startTriggerEdge: TriggerEdge,\n endTriggerEdge: TriggerEdge\n) => {\n const maxScrollTop = scrollAdapter.getMaxScrollTop();\n const { heightDelta, top, bottom, animationHeight } = getNavBarMetrics();\n\n if (startTriggerPosition === 'full-area') {\n let currentStart = top;\n const maxProgress =\n maxScrollTop >= animationHeight + top\n ? 1\n : (animationRanges.find((range) => range[0] * animationHeight <= maxScrollTop - top) ?? [0])[0];\n\n const mapScrollToAnimationPropgress = (scrollPosition: number) => {\n const progress = (scrollPosition - currentStart) / animationHeight;\n if (progress > maxProgress) {\n currentStart = scrollPosition - animationHeight * maxProgress;\n } else if (progress < 0) {\n currentStart = scrollPosition;\n }\n currentStart = Math.max(currentStart, top);\n\n return clamp(progress, 0, maxProgress);\n };\n\n const getClosestStops = () => ({\n bottom: currentStart,\n top: currentStart + animationHeight * maxProgress,\n });\n\n return [mapScrollToAnimationPropgress, getClosestStops] as const;\n }\n\n let startPos =\n startTriggerPosition === 'start' || !startTriggerPosition.current\n ? 0\n : getRelativeOffset(scrollAdapter, startTriggerPosition.current, startTriggerEdge) - 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, endTriggerEdge) + 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 | undefined,\n // Дефолты дублируют продуктовые дефолты `NavBar` (начало — по верхнему краю, конец — по нижнему).\n startTriggerEdge: TriggerEdge = 'top',\n endTriggerEdge: TriggerEdge = 'bottom'\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 startTriggerEdgeRef = useActualRef(startTriggerEdge);\n const endTriggerEdgeRef = useActualRef(endTriggerEdge);\n const getClosestStopsRef: MutableRefObject<(() => { top: number; bottom: number }) | null> = useRef(null);\n\n const bindScrollToAnimation = useInitOnce(() => {\n let unsubscribe: VoidFunction | null = null;\n return () => {\n unsubscribe?.();\n const { heightDelta, top, animationHeight } = getNavBarMetrics();\n\n const animationRanges = [\n ...new Set<[number, number]>(\n [...getAnimationRanges().values()].map((range) => [\n range[0] / animationHeight,\n range[1] / animationHeight,\n ])\n ),\n ].sort((a, b) => b[0] - a[0]);\n\n const [mapFn, getClosestStops] = createScrollToAnimationProgressMapFn(\n getNavBarMetrics,\n scrollAdapter,\n animationRanges,\n startTriggerPositionRef.current,\n endTriggerPositionRef.current,\n startTriggerEdgeRef.current,\n endTriggerEdgeRef.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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"}
|
package/internal/utils.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { type MutableRefObject } from 'react';
|
|
|
2
2
|
import { type MotionValue } from 'motion';
|
|
3
3
|
import { KeyedSubscriptions } from '@hh.ru/magritte-ui-nav-bar/internal/KeyedSubscriptions';
|
|
4
4
|
import { ScrollAdapter } from '@hh.ru/magritte-ui-nav-bar/internal/useScrollAdapter';
|
|
5
|
+
import { type TriggerEdge } from '@hh.ru/magritte-ui-nav-bar/public/TriggerContext';
|
|
5
6
|
export declare const lerp: (from: number, to: number, progress: number) => number;
|
|
6
7
|
export declare const clamp: (value: number, min: number, max: number) => number;
|
|
7
8
|
/**
|
|
@@ -204,4 +205,4 @@ export declare const calcMorphParams: (start: DOMRectReadOnly | null, end: DOMRe
|
|
|
204
205
|
scaleX: number;
|
|
205
206
|
scaleY: number;
|
|
206
207
|
};
|
|
207
|
-
export declare const getRelativeOffset: (scrollAdapter: ScrollAdapter, element: HTMLElement | null) => number;
|
|
208
|
+
export declare const getRelativeOffset: (scrollAdapter: ScrollAdapter, element: HTMLElement | null, edge?: TriggerEdge) => number;
|
package/internal/utils.js
CHANGED
|
@@ -360,7 +360,7 @@ const calcMorphParams = (start, end, sizeAxis, horizontalPositionAlign, vertical
|
|
|
360
360
|
scaleY,
|
|
361
361
|
};
|
|
362
362
|
};
|
|
363
|
-
const getRelativeOffset = (scrollAdapter, element) => {
|
|
363
|
+
const getRelativeOffset = (scrollAdapter, element, edge = 'top') => {
|
|
364
364
|
if (!scrollAdapter.scrollContainer.current || !element) {
|
|
365
365
|
return 0;
|
|
366
366
|
}
|
|
@@ -368,9 +368,10 @@ const getRelativeOffset = (scrollAdapter, element) => {
|
|
|
368
368
|
const containerTop = container === document.documentElement ? 0 : container.getBoundingClientRect().top;
|
|
369
369
|
const scroll = scrollAdapter.getScrollTop();
|
|
370
370
|
scrollAdapter.setScrollTop(0);
|
|
371
|
-
const
|
|
371
|
+
const rect = element.getBoundingClientRect();
|
|
372
|
+
const elementEdge = edge === 'bottom' ? rect.bottom : rect.top;
|
|
372
373
|
scrollAdapter.setScrollTop(scroll);
|
|
373
|
-
return
|
|
374
|
+
return elementEdge - containerTop;
|
|
374
375
|
};
|
|
375
376
|
|
|
376
377
|
export { calcMorphParams, clamp, findScrollContainer, getRelativeOffset, isDOMRectsEqual, lerp, remap, scheduleGatherMetrics, scheduleMacro, scheduleMicro, useActualRef, useInitOnce, useStoreSyncedTransform };
|
package/internal/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sources":["src/internal/utils.ts"],"sourcesContent":["import { type MutableRefObject, useLayoutEffect, useRef } from 'react';\nimport { frame, 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 scheduled = useRef(false);\n const scheduleTransform = useInitOnce(() => () => {\n if (scheduled.current) {\n return;\n }\n\n scheduled.current = true;\n frame.preRender(() => {\n transformFnRef.current(extractedValueRef.current, originalValueRef.current.get());\n scheduled.current = false;\n });\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 scheduleTransform();\n });\n const unsubscribeValue = originalValue.on('change', scheduleTransform);\n\n return () => {\n unsubscribeStore();\n unsubscribeValue();\n };\n }, [store, observedKeys, originalValue, extractedValueRef, valueExtractorRef, scheduleTransform]);\n\n useLayoutEffect(() => {\n scheduleTransform();\n }, [valueExtractor, extractedValueRef, transformFn, scheduleTransform]);\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 { frame, 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';\nimport { type TriggerEdge } from '@hh.ru/magritte-ui-nav-bar/public/TriggerContext';\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 scheduled = useRef(false);\n const scheduleTransform = useInitOnce(() => () => {\n if (scheduled.current) {\n return;\n }\n\n scheduled.current = true;\n frame.preRender(() => {\n transformFnRef.current(extractedValueRef.current, originalValueRef.current.get());\n scheduled.current = false;\n });\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 scheduleTransform();\n });\n const unsubscribeValue = originalValue.on('change', scheduleTransform);\n\n return () => {\n unsubscribeStore();\n unsubscribeValue();\n };\n }, [store, observedKeys, originalValue, extractedValueRef, valueExtractorRef, scheduleTransform]);\n\n useLayoutEffect(() => {\n scheduleTransform();\n }, [valueExtractor, extractedValueRef, transformFn, scheduleTransform]);\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 = (\n scrollAdapter: ScrollAdapter,\n element: HTMLElement | null,\n edge: TriggerEdge = 'top'\n): 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 rect = element.getBoundingClientRect();\n const elementEdge = edge === 'bottom' ? rect.bottom : rect.top;\n scrollAdapter.setScrollTop(scroll);\n\n return elementEdge - containerTop;\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import './index.css';
|
|
2
|
+
var styles = {"actions-container":"magritte-actions-container___CBgYW_1-3-28","actionsContainer":"magritte-actions-container___CBgYW_1-3-28","actions-no-children":"magritte-actions-no-children___rgJUl_1-3-28","actionsNoChildren":"magritte-actions-no-children___rgJUl_1-3-28","actions-left-slot":"magritte-actions-left-slot___DNOj0_1-3-28","actionsLeftSlot":"magritte-actions-left-slot___DNOj0_1-3-28","actions-right-slot":"magritte-actions-right-slot___aodtj_1-3-28","actionsRightSlot":"magritte-actions-right-slot___aodtj_1-3-28","navbar-transparent-end":"magritte-navbar-transparent-end___6KqLR_1-3-28","navbarTransparentEnd":"magritte-navbar-transparent-end___6KqLR_1-3-28","actions-start-stage":"magritte-actions-start-stage___MJ67a_1-3-28","actionsStartStage":"magritte-actions-start-stage___MJ67a_1-3-28","actions-side-slot-content":"magritte-actions-side-slot-content___TlHrX_1-3-28","actionsSideSlotContent":"magritte-actions-side-slot-content___TlHrX_1-3-28","actions-icon-morph":"magritte-actions-icon-morph___-nNgW_1-3-28","actionsIconMorph":"magritte-actions-icon-morph___-nNgW_1-3-28","navbar-transparent-start":"magritte-navbar-transparent-start___wysv0_1-3-28","navbarTransparentStart":"magritte-navbar-transparent-start___wysv0_1-3-28","actions-end-stage":"magritte-actions-end-stage___qHtTV_1-3-28","actionsEndStage":"magritte-actions-end-stage___qHtTV_1-3-28","navbar-not-transparent":"magritte-navbar-not-transparent___5KZNp_1-3-28","navbarNotTransparent":"magritte-navbar-not-transparent___5KZNp_1-3-28","navbar-transparent":"magritte-navbar-transparent___ULEh8_1-3-28","navbarTransparent":"magritte-navbar-transparent___ULEh8_1-3-28","actions-only-stage":"magritte-actions-only-stage___cg10A_1-3-28","actionsOnlyStage":"magritte-actions-only-stage___cg10A_1-3-28","actions-side-slot-content-clone":"magritte-actions-side-slot-content-clone___jDxVm_1-3-28","actionsSideSlotContentClone":"magritte-actions-side-slot-content-clone___jDxVm_1-3-28","actions-center-slot":"magritte-actions-center-slot___hLAy6_1-3-28","actionsCenterSlot":"magritte-actions-center-slot___hLAy6_1-3-28","actions-center-slot-centered":"magritte-actions-center-slot-centered___merXQ_1-3-28","actionsCenterSlotCentered":"magritte-actions-center-slot-centered___merXQ_1-3-28","align-top":"magritte-align-top___-QSFW_1-3-28","alignTop":"magritte-align-top___-QSFW_1-3-28","align-bottom":"magritte-align-bottom___FliHI_1-3-28","alignBottom":"magritte-align-bottom___FliHI_1-3-28","title-container-wrapper":"magritte-title-container-wrapper___DQUcj_1-3-28","titleContainerWrapper":"magritte-title-container-wrapper___DQUcj_1-3-28","title-main-part":"magritte-title-main-part___npoHN_1-3-28","titleMainPart":"magritte-title-main-part___npoHN_1-3-28","title-left-slot":"magritte-title-left-slot___HTE7h_1-3-28","titleLeftSlot":"magritte-title-left-slot___HTE7h_1-3-28","title-container":"magritte-title-container___y9AIx_1-3-28","titleContainer":"magritte-title-container___y9AIx_1-3-28","subtitle-container":"magritte-subtitle-container___nVUBu_1-3-28","subtitleContainer":"magritte-subtitle-container___nVUBu_1-3-28","text-morph-item":"magritte-text-morph-item___-vXru_1-3-28","textMorphItem":"magritte-text-morph-item___-vXru_1-3-28","centered":"magritte-centered___Y2mlP_1-3-28","title-morph-item":"magritte-title-morph-item___t7Wf3_1-3-28","titleMorphItem":"magritte-title-morph-item___t7Wf3_1-3-28","title":"magritte-title___ZbLgP_1-3-28","size-large":"magritte-size-large___ISXfH_1-3-28","sizeLarge":"magritte-size-large___ISXfH_1-3-28","pane-content":"magritte-pane-content___UVmC6_1-3-28","paneContent":"magritte-pane-content___UVmC6_1-3-28","pane-background":"magritte-pane-background___PDZAX_1-3-28","paneBackground":"magritte-pane-background___PDZAX_1-3-28","morph-item":"magritte-morph-item___8kF46_1-3-28","morphItem":"magritte-morph-item___8kF46_1-3-28","morph-item-top":"magritte-morph-item-top___WPEkn_1-3-28","morphItemTop":"magritte-morph-item-top___WPEkn_1-3-28","morph-item-bottom":"magritte-morph-item-bottom___zNbsF_1-3-28","morphItemBottom":"magritte-morph-item-bottom___zNbsF_1-3-28","morph-item-left":"magritte-morph-item-left___T6AMW_1-3-28","morphItemLeft":"magritte-morph-item-left___T6AMW_1-3-28","morph-item-right":"magritte-morph-item-right___EuT1E_1-3-28","morphItemRight":"magritte-morph-item-right___EuT1E_1-3-28","pane":"magritte-pane___f8eFC_1-3-28","start-state-container":"magritte-start-state-container___giBVb_1-3-28","startStateContainer":"magritte-start-state-container___giBVb_1-3-28","end-state-container":"magritte-end-state-container___uiW8Q_1-3-28","endStateContainer":"magritte-end-state-container___uiW8Q_1-3-28","content-container":"magritte-content-container___7s7vv_1-3-28","contentContainer":"magritte-content-container___7s7vv_1-3-28","next-pane":"magritte-next-pane___H2oxQ_1-3-28","nextPane":"magritte-next-pane___H2oxQ_1-3-28","nav-bar":"magritte-nav-bar___RRGe0_1-3-28","navBar":"magritte-nav-bar___RRGe0_1-3-28","nav-bar-overlay":"magritte-nav-bar-overlay___Mq5ZD_1-3-28","navBarOverlay":"magritte-nav-bar-overlay___Mq5ZD_1-3-28","nav-bar-overlay-wrapper":"magritte-nav-bar-overlay-wrapper___y1VpY_1-3-28","navBarOverlayWrapper":"magritte-nav-bar-overlay-wrapper___y1VpY_1-3-28","nav-bar-content-container":"magritte-nav-bar-content-container___H-WMO_1-3-28","navBarContentContainer":"magritte-nav-bar-content-container___H-WMO_1-3-28","nav-bar-panes-container":"magritte-nav-bar-panes-container___5ZDLa_1-3-28","navBarPanesContainer":"magritte-nav-bar-panes-container___5ZDLa_1-3-28","nav-bar-progressive-blur":"magritte-nav-bar-progressive-blur___qyeUV_1-3-28","navBarProgressiveBlur":"magritte-nav-bar-progressive-blur___qyeUV_1-3-28","nav-bar-stage":"magritte-nav-bar-stage___amDz7_1-3-28","navBarStage":"magritte-nav-bar-stage___amDz7_1-3-28","last-pane":"magritte-last-pane___Hf2No_1-3-28","lastPane":"magritte-last-pane___Hf2No_1-3-28","metrics-mode":"magritte-metrics-mode___h38aX_1-3-28","metricsMode":"magritte-metrics-mode___h38aX_1-3-28","layout-morph":"magritte-layout-morph___I3SPy_1-3-28","layoutMorph":"magritte-layout-morph___I3SPy_1-3-28","animation-stage-progress":"magritte-animation-stage-progress___5FthT_1-3-28","animationStageProgress":"magritte-animation-stage-progress___5FthT_1-3-28","layout-morph-start":"magritte-layout-morph-start___9-krP_1-3-28","layoutMorphStart":"magritte-layout-morph-start___9-krP_1-3-28","layout-morph-end":"magritte-layout-morph-end___LIg4d_1-3-28","layoutMorphEnd":"magritte-layout-morph-end___LIg4d_1-3-28","divider-container":"magritte-divider-container___-NdWi_1-3-28","dividerContainer":"magritte-divider-container___-NdWi_1-3-28"};
|
|
3
|
+
|
|
4
|
+
export { styles as s };
|
|
5
|
+
//# sourceMappingURL=nav-bar-Bgo6GHaL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nav-bar-Bgo6GHaL.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hh.ru/magritte-ui-nav-bar",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.28",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"types": "index.d.ts",
|
|
6
6
|
"sideEffects": [
|
|
@@ -30,13 +30,15 @@
|
|
|
30
30
|
"@hh.ru/magritte-common-use-multiple-refs": "1.1.11",
|
|
31
31
|
"@hh.ru/magritte-common-use-when-font-loaded": "1.0.14",
|
|
32
32
|
"@hh.ru/magritte-design-tokens": "24.7.0",
|
|
33
|
+
"@hh.ru/magritte-internal-accessible-region": "workspace:*",
|
|
33
34
|
"@hh.ru/magritte-internal-custom-scroll": "2.1.1",
|
|
34
|
-
"@hh.ru/magritte-internal-default-props-context": "1.0.
|
|
35
|
+
"@hh.ru/magritte-internal-default-props-context": "1.0.2",
|
|
36
|
+
"@hh.ru/magritte-internal-slot-registry": "1.1.0",
|
|
35
37
|
"@hh.ru/magritte-ui-divider": "3.1.3",
|
|
36
|
-
"@hh.ru/magritte-ui-icon": "14.2.
|
|
38
|
+
"@hh.ru/magritte-ui-icon": "14.2.9",
|
|
37
39
|
"@hh.ru/magritte-ui-layer": "3.2.4",
|
|
38
40
|
"@hh.ru/magritte-ui-mock-component": "1.1.7",
|
|
39
|
-
"@hh.ru/magritte-ui-typography": "5.3.
|
|
41
|
+
"@hh.ru/magritte-ui-typography": "5.3.3"
|
|
40
42
|
},
|
|
41
|
-
"gitHead": "
|
|
43
|
+
"gitHead": "a2837da95a9547b4928c015070823c9039156a5b"
|
|
42
44
|
}
|
package/public/Actions.js
CHANGED
|
@@ -7,7 +7,7 @@ import { EnvironmentFingerprintNode } from './EnvironmentFingerprintNode.js';
|
|
|
7
7
|
import { Morph } from './Morph.js';
|
|
8
8
|
import { useAnimationStage } from './Stage.js';
|
|
9
9
|
import { useActionsDefaultProps, useActionsIconsDefaultProps } from './defaultProps.js';
|
|
10
|
-
import { s as styles } from '../nav-bar-
|
|
10
|
+
import { s as styles } from '../nav-bar-Bgo6GHaL.js';
|
|
11
11
|
import '../internal/MetricsProvider.js';
|
|
12
12
|
import '../internal/utils.js';
|
|
13
13
|
import 'motion';
|
package/public/LayoutMorph.js
CHANGED
|
@@ -10,7 +10,7 @@ import { usePaneStore } from '../internal/PaneStore.js';
|
|
|
10
10
|
import { useInert } from '../internal/useInert.js';
|
|
11
11
|
import { useActualRef, useStoreSyncedTransform, lerp, calcMorphParams } from '../internal/utils.js';
|
|
12
12
|
import { AnimationStageContext } from './LayoutStage.js';
|
|
13
|
-
import { s as styles } from '../nav-bar-
|
|
13
|
+
import { s as styles } from '../nav-bar-Bgo6GHaL.js';
|
|
14
14
|
import '../internal/KeyedSubscriptions.js';
|
|
15
15
|
import 'motion';
|
|
16
16
|
import '../internal/NavBarContext.js';
|
package/public/LayoutStage.js
CHANGED
|
@@ -9,7 +9,7 @@ import { MorphStoreProvider, useMorphStore } from '../internal/MorphStore.js';
|
|
|
9
9
|
import { usePaneStore } from '../internal/PaneStore.js';
|
|
10
10
|
import { useInitOnce } from '../internal/utils.js';
|
|
11
11
|
import { EnvironmentFingerprintNode, useEnvironmentFingerprint } from './EnvironmentFingerprintNode.js';
|
|
12
|
-
import { s as styles } from '../nav-bar-
|
|
12
|
+
import { s as styles } from '../nav-bar-Bgo6GHaL.js';
|
|
13
13
|
import '../internal/KeyedSubscriptions.js';
|
|
14
14
|
import 'motion';
|
|
15
15
|
import '../internal/NavBarContext.js';
|
package/public/Morph.js
CHANGED
|
@@ -9,7 +9,7 @@ import { usePaneStore } from '../internal/PaneStore.js';
|
|
|
9
9
|
import { useStoreSyncedTransform, calcMorphParams, lerp } from '../internal/utils.js';
|
|
10
10
|
import { useEnvironmentFingerprint } from './EnvironmentFingerprintNode.js';
|
|
11
11
|
import { useAnimationStage } from './Stage.js';
|
|
12
|
-
import { s as styles } from '../nav-bar-
|
|
12
|
+
import { s as styles } from '../nav-bar-Bgo6GHaL.js';
|
|
13
13
|
import '../internal/KeyedSubscriptions.js';
|
|
14
14
|
import 'motion';
|
|
15
15
|
import '../internal/NavBarContext.js';
|
package/public/NavBar.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type ReactNode, type RefObject, type FC } from 'react';
|
|
2
2
|
import { MotionValue } from 'motion/react';
|
|
3
3
|
import { type ShowDivider } from '@hh.ru/magritte-ui-divider';
|
|
4
|
+
import { type TriggerEdge } from '@hh.ru/magritte-ui-nav-bar/public/TriggerContext';
|
|
4
5
|
export interface NavBarProps {
|
|
5
6
|
/**
|
|
6
7
|
* В качестве потомков могут передаваться компоненты <Pane /> или произвольные элементы,
|
|
@@ -21,12 +22,22 @@ export interface NavBarProps {
|
|
|
21
22
|
* @default start
|
|
22
23
|
*/
|
|
23
24
|
startTriggerPosition?: 'start' | 'full-area' | RefObject<HTMLElement | null>;
|
|
25
|
+
/**
|
|
26
|
+
* Край элемента `startTriggerPosition`, по которому измеряется позиция начала анимации.
|
|
27
|
+
* @default top
|
|
28
|
+
*/
|
|
29
|
+
startTriggerEdge?: TriggerEdge;
|
|
24
30
|
/**
|
|
25
31
|
* Управляет позицией триггера конца анимации. Если не передан вычисляется автоматически на основе изменения
|
|
26
32
|
* высоты навбара в процессе анимации и позиции триггера начала анмиации. Если передано число, то интерпретируется
|
|
27
33
|
* как отступ в пикселях от позиции триггера старта анимации.
|
|
28
34
|
*/
|
|
29
35
|
endTriggerPosition?: RefObject<HTMLElement | null> | number;
|
|
36
|
+
/**
|
|
37
|
+
* Край элемента `endTriggerPosition`, по которому измеряется позиция конца анимации.
|
|
38
|
+
* @default bottom
|
|
39
|
+
*/
|
|
40
|
+
endTriggerEdge?: TriggerEdge;
|
|
30
41
|
/**
|
|
31
42
|
* Включает режим когда NavBar не занимает место в лейауте и контент располагается под ним.
|
|
32
43
|
* @default false
|
package/public/NavBar.js
CHANGED
|
@@ -16,8 +16,9 @@ import { useSnapScroll } from '../internal/useSnapScroll.js';
|
|
|
16
16
|
import { useSyncMotionValue } from '../internal/useSyncMotionValue.js';
|
|
17
17
|
import { useInitOnce, scheduleMicro, scheduleMacro, remap } from '../internal/utils.js';
|
|
18
18
|
import { isPaneElement, ExternalPane } from './Pane.js';
|
|
19
|
+
import { useTriggerSlot } from './TriggerContext.js';
|
|
19
20
|
import { useNavBarRootDefaultProps } from './defaultProps.js';
|
|
20
|
-
import { s as styles } from '../nav-bar-
|
|
21
|
+
import { s as styles } from '../nav-bar-Bgo6GHaL.js';
|
|
21
22
|
import '@hh.ru/magritte-internal-custom-scroll';
|
|
22
23
|
import 'motion';
|
|
23
24
|
import '../internal/MorphStore.js';
|
|
@@ -28,18 +29,26 @@ import './LayoutStage.js';
|
|
|
28
29
|
import '@hh.ru/magritte-common-use-when-font-loaded';
|
|
29
30
|
import './Stage.js';
|
|
30
31
|
import '../internal/useInert.js';
|
|
32
|
+
import '@hh.ru/magritte-internal-slot-registry';
|
|
31
33
|
import '@hh.ru/magritte-internal-default-props-context';
|
|
32
34
|
|
|
33
35
|
const NavBar = (props) => {
|
|
34
36
|
const defaultProps = useNavBarRootDefaultProps();
|
|
35
|
-
const
|
|
37
|
+
const startSlot = useTriggerSlot('startTrigger');
|
|
38
|
+
const endSlot = useTriggerSlot('endTrigger');
|
|
39
|
+
// Приоритет (от высшего к низшему): явные пропсы > NavBarDefaultPropsContext > слоты `Trigger` > дефолты.
|
|
40
|
+
const slotProps = {
|
|
41
|
+
...(startSlot ? { startTriggerPosition: startSlot.ref, startTriggerEdge: startSlot.edge } : null),
|
|
42
|
+
...(endSlot ? { endTriggerPosition: endSlot.ref, endTriggerEdge: endSlot.edge } : null),
|
|
43
|
+
};
|
|
44
|
+
const { children, transparent = false, startTriggerPosition = 'start', startTriggerEdge = 'top', endTriggerPosition, endTriggerEdge = 'bottom', overlay = false, snapScroll: scrollSnapping = true, showDivider = false, animationProgress, } = { ...slotProps, ...defaultProps, ...props };
|
|
36
45
|
const paneStoreRegistry = useInitOnce(() => new Set());
|
|
37
46
|
const prevFullHeight = useRef(0);
|
|
38
47
|
const rootRef = useRef(null);
|
|
39
48
|
const [scrollAdapter, scrollPosition] = useScrollAdapter(rootRef);
|
|
40
49
|
const [getMetrics, invalidateMetrics] = useNavBarMetrics(paneStoreRegistry, rootRef, scrollAdapter);
|
|
41
50
|
const [getAnimationRanges, invalidateAnimationRanges] = useAnimationRanges(paneStoreRegistry, getMetrics);
|
|
42
|
-
const [bindScrollToAnimation, getClosestStops, totalAnimationProgress, dividerStyle] = useBindScrollToAnimationProgress(scrollPosition, getMetrics, getAnimationRanges, scrollAdapter, startTriggerPosition, endTriggerPosition);
|
|
51
|
+
const [bindScrollToAnimation, getClosestStops, totalAnimationProgress, dividerStyle] = useBindScrollToAnimationProgress(scrollPosition, getMetrics, getAnimationRanges, scrollAdapter, startTriggerPosition, endTriggerPosition, startTriggerEdge, endTriggerEdge);
|
|
43
52
|
const snapScroll = useSnapScroll(scrollPosition, totalAnimationProgress, scrollAdapter, getClosestStops, scrollSnapping);
|
|
44
53
|
// При получении размеров важно дождаться применения MotionValue к DOM, поэтому используем frame.render
|
|
45
54
|
const bindScrollToAnimationScheduled = useInitOnce(() => scheduleMicro(() => frame.render(bindScrollToAnimation)));
|