@redocly/theme 0.55.0-next.2 → 0.55.0-next.3

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 (44) hide show
  1. package/lib/components/Marker/Marker.d.ts +10 -0
  2. package/lib/components/Marker/Marker.js +62 -0
  3. package/lib/core/contexts/CodeWalkthrough/CodeWalkthroughStepsContext.d.ts +1 -1
  4. package/lib/core/contexts/CodeWalkthrough/CodeWalkthroughStepsContext.js +5 -2
  5. package/lib/core/hooks/code-walkthrough/use-code-walkthrough-steps.d.ts +17 -9
  6. package/lib/core/hooks/code-walkthrough/use-code-walkthrough-steps.js +242 -47
  7. package/lib/core/hooks/code-walkthrough/use-code-walkthrough.d.ts +9 -2
  8. package/lib/core/hooks/code-walkthrough/use-code-walkthrough.js +2 -2
  9. package/lib/core/hooks/index.d.ts +1 -1
  10. package/lib/core/hooks/index.js +1 -1
  11. package/lib/core/hooks/use-active-page-version.d.ts +1 -0
  12. package/lib/core/hooks/{use-page-active-version.js → use-active-page-version.js} +3 -3
  13. package/lib/core/types/code-walkthrough.d.ts +13 -0
  14. package/lib/core/types/code-walkthrough.js +3 -0
  15. package/lib/core/types/index.d.ts +2 -0
  16. package/lib/core/types/index.js +2 -0
  17. package/lib/core/types/marker.d.ts +4 -0
  18. package/lib/core/types/marker.js +3 -0
  19. package/lib/core/utils/js-utils.d.ts +18 -0
  20. package/lib/core/utils/js-utils.js +31 -0
  21. package/lib/index.d.ts +1 -0
  22. package/lib/index.js +1 -0
  23. package/lib/markdoc/components/CodeWalkthrough/CodeStep.d.ts +1 -2
  24. package/lib/markdoc/components/CodeWalkthrough/CodeStep.js +26 -20
  25. package/lib/markdoc/components/CodeWalkthrough/CodeWalkthrough.js +32 -3
  26. package/lib/markdoc/tags/code-step.js +0 -3
  27. package/lib/markdoc/tags/code-walkthrough.js +0 -1
  28. package/package.json +1 -1
  29. package/src/components/Marker/Marker.tsx +53 -0
  30. package/src/core/contexts/CodeWalkthrough/CodeWalkthroughStepsContext.tsx +6 -3
  31. package/src/core/hooks/code-walkthrough/use-code-walkthrough-steps.ts +326 -65
  32. package/src/core/hooks/code-walkthrough/use-code-walkthrough.ts +9 -6
  33. package/src/core/hooks/index.ts +1 -1
  34. package/src/core/hooks/{use-page-active-version.ts → use-active-page-version.ts} +1 -1
  35. package/src/core/types/code-walkthrough.ts +15 -0
  36. package/src/core/types/index.ts +2 -0
  37. package/src/core/types/marker.ts +4 -0
  38. package/src/core/utils/js-utils.ts +31 -0
  39. package/src/index.ts +1 -0
  40. package/src/markdoc/components/CodeWalkthrough/CodeStep.tsx +76 -36
  41. package/src/markdoc/components/CodeWalkthrough/CodeWalkthrough.tsx +8 -3
  42. package/src/markdoc/tags/code-step.ts +0 -3
  43. package/src/markdoc/tags/code-walkthrough.ts +0 -1
  44. package/lib/core/hooks/use-page-active-version.d.ts +0 -1
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import type { MarkerArea } from '../../core/types';
3
+ type MarkerProps = {
4
+ marker: MarkerArea;
5
+ registerMarker: (element: HTMLElement) => void;
6
+ removeMarker: (element: HTMLElement) => void;
7
+ dataAttribures?: Record<string, string | boolean | number>;
8
+ };
9
+ export declare const Marker: React.ForwardRefExoticComponent<MarkerProps & React.RefAttributes<HTMLDivElement>>;
10
+ export {};
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.Marker = void 0;
30
+ const react_1 = __importStar(require("react"));
31
+ const styled_components_1 = __importDefault(require("styled-components"));
32
+ exports.Marker = (0, react_1.forwardRef)(({ marker, dataAttribures, registerMarker, removeMarker }, forwardedRef) => {
33
+ const internalRef = (0, react_1.useRef)(null);
34
+ // Reference should be available before paint
35
+ (0, react_1.useLayoutEffect)(() => {
36
+ if (!forwardedRef)
37
+ return;
38
+ if (typeof forwardedRef === 'function') {
39
+ forwardedRef(internalRef.current);
40
+ }
41
+ else {
42
+ forwardedRef.current = internalRef.current;
43
+ }
44
+ }, [forwardedRef]);
45
+ (0, react_1.useEffect)(() => {
46
+ const currentElement = internalRef.current;
47
+ if (!currentElement)
48
+ return;
49
+ registerMarker(currentElement);
50
+ return () => removeMarker(currentElement);
51
+ }, [registerMarker, removeMarker, marker]);
52
+ return (react_1.default.createElement(StyledMarker, Object.assign({ "data-component-name": "Marker/Marker", ref: internalRef, marker: marker }, (dataAttribures !== null && dataAttribures !== void 0 ? dataAttribures : {}))));
53
+ });
54
+ const StyledMarker = styled_components_1.default.div `
55
+ position: absolute;
56
+ z-index: -1;
57
+ top: ${({ marker }) => marker.offset}px;
58
+ height: ${({ marker }) => marker.height}px;
59
+ width: 100%;
60
+ left: 0;
61
+ `;
62
+ //# sourceMappingURL=Marker.js.map
@@ -1,2 +1,2 @@
1
- import type { WalkthroughStepsState } from '../../../core/hooks';
1
+ import type { WalkthroughStepsState } from '../../../core/types';
2
2
  export declare const CodeWalkthroughStepsContext: import("react").Context<WalkthroughStepsState>;
@@ -3,9 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CodeWalkthroughStepsContext = void 0;
4
4
  const react_1 = require("react");
5
5
  exports.CodeWalkthroughStepsContext = (0, react_1.createContext)({
6
+ markers: {},
6
7
  activeStep: null,
7
8
  setActiveStep: () => { },
8
- register: () => { },
9
- unregister: () => { },
9
+ registerStep: () => { },
10
+ registerMarker: () => { },
11
+ removeMarker: () => { },
12
+ removeStep: () => { },
10
13
  });
11
14
  //# sourceMappingURL=CodeWalkthroughStepsContext.js.map
@@ -1,15 +1,23 @@
1
1
  import type { CodeWalkthroughStepAttr } from '@redocly/config';
2
- type ActiveStep = string | null;
2
+ import type { WalkthroughStepsState } from '../../../core/types';
3
3
  type CodeWalkthroughStep = CodeWalkthroughStepAttr & {
4
4
  compRef?: HTMLElement;
5
+ markerRef?: HTMLElement;
5
6
  };
6
- export type WalkthroughStepsState = {
7
- activeStep: ActiveStep;
8
- setActiveStep: (stepId: ActiveStep) => void;
9
- register: (element: HTMLElement) => void;
10
- unregister: (element: HTMLElement) => void;
11
- lockObserver?: React.RefObject<boolean>;
12
- filtersElementRef?: React.RefObject<HTMLDivElement | null>;
7
+ type Params = {
8
+ steps: CodeWalkthroughStep[];
9
+ enableDeepLink: boolean;
10
+ root: React.RefObject<HTMLDivElement | null>;
13
11
  };
14
- export declare function useCodeWalkthroughSteps(steps: CodeWalkthroughStep[], enableDeepLink: boolean): WalkthroughStepsState;
12
+ export declare function useCodeWalkthroughSteps({ steps, enableDeepLink, root, }: Params): WalkthroughStepsState;
13
+ type StepsGroup = {
14
+ freeSpace: number;
15
+ usedSpace: number;
16
+ offset: number;
17
+ steps: {
18
+ offset: number;
19
+ height: number;
20
+ }[];
21
+ };
22
+ export declare function getGroupMarkers(group: StepsGroup): number[];
15
23
  export {};
@@ -1,11 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.useCodeWalkthroughSteps = useCodeWalkthroughSteps;
4
+ exports.getGroupMarkers = getGroupMarkers;
4
5
  const react_1 = require("react");
5
6
  const react_router_dom_1 = require("react-router-dom");
6
7
  const utils_1 = require("../../../core/utils");
7
8
  const constants_1 = require("../../../core/constants");
8
- function useCodeWalkthroughSteps(steps, enableDeepLink) {
9
+ function useCodeWalkthroughSteps({ steps, enableDeepLink, root, }) {
9
10
  const location = (0, react_router_dom_1.useLocation)();
10
11
  const navigate = (0, react_router_dom_1.useNavigate)();
11
12
  const searchParams = (0, react_1.useMemo)(() => new URLSearchParams(location.search), [location.search]);
@@ -15,56 +16,122 @@ function useCodeWalkthroughSteps(steps, enableDeepLink) {
15
16
  // Track observed elements in case new observer needs to be created
16
17
  const observedElementsRef = (0, react_1.useRef)(new Set());
17
18
  const [activeStep, setActiveStep] = (0, react_1.useState)(enableDeepLink ? searchParams.get(constants_1.ACTIVE_STEP_QUERY_PARAM) : null);
18
- // eslint-disable-next-line react-hooks/exhaustive-deps
19
- const _steps = (0, react_1.useMemo)(() => steps, [JSON.stringify(steps)]);
20
- const register = (0, react_1.useCallback)((element) => {
21
- // for some reason, the observer is not ready immediately
22
- setTimeout(() => {
23
- if (observerRef.current) {
24
- const stepKey = Number(element.dataset.stepKey);
25
- if (Number.isInteger(stepKey) && stepKey >= 0 && _steps[stepKey]) {
26
- _steps[stepKey].compRef = element;
27
- }
28
- observerRef.current.observe(element);
29
- observedElementsRef.current.add(element);
19
+ const stepsMap = (0, react_1.useMemo)(() => {
20
+ const map = new Map();
21
+ steps.forEach((step, index) => {
22
+ map.set(step.id, Object.assign(Object.assign({}, step), { index }));
23
+ });
24
+ return map;
25
+ // eslint-disable-next-line react-hooks/exhaustive-deps
26
+ }, [JSON.stringify(steps)]);
27
+ const options = (0, react_1.useMemo)(() => {
28
+ var _a, _b;
29
+ if (!(0, utils_1.isBrowser)()) {
30
+ return null;
31
+ }
32
+ const filtersElementHeight = ((_a = filtersElementRef.current) === null || _a === void 0 ? void 0 : _a.clientHeight) || 0;
33
+ const navbarHeight = ((_b = document.querySelector('nav')) === null || _b === void 0 ? void 0 : _b.getBoundingClientRect().height) || 0;
34
+ return {
35
+ filtersElementHeight,
36
+ navbarHeight,
37
+ };
38
+ }, []);
39
+ const [visibleSteps, setVisibleSteps] = (0, react_1.useState)([]);
40
+ const [markers, setMarkers] = (0, react_1.useState)({});
41
+ (0, react_1.useEffect)(() => {
42
+ var _a, _b, _c, _d, _e;
43
+ if (!root.current || !visibleSteps.length || !options) {
44
+ return;
45
+ }
46
+ const markersMinTopOffset = options.filtersElementHeight + options.navbarHeight;
47
+ const rootHeight = (_b = (_a = root.current) === null || _a === void 0 ? void 0 : _a.clientHeight) !== null && _b !== void 0 ? _b : 0;
48
+ const lastStepOffset = (_e = (_d = (_c = visibleSteps[visibleSteps.length - 1]) === null || _c === void 0 ? void 0 : _c.compRef) === null || _d === void 0 ? void 0 : _d.offsetTop) !== null && _e !== void 0 ? _e : 0;
49
+ const deficit = Math.max(lastStepOffset - (rootHeight - window.innerHeight), 0);
50
+ const groups = getGroups(visibleSteps);
51
+ let markers = groups.flatMap((group) => getGroupMarkers(group));
52
+ if (deficit) {
53
+ const startOffset = markersMinTopOffset;
54
+ const endOffset = Math.max(rootHeight - window.innerHeight, 0);
55
+ markers = distributeMarkers({
56
+ endOffset,
57
+ markers,
58
+ startOffset: markersMinTopOffset < endOffset ? startOffset : 0,
59
+ });
60
+ }
61
+ setMarkers(markers.reduce((acc, marker, index) => {
62
+ var _a;
63
+ const step = visibleSteps[index];
64
+ acc[step.id] = {
65
+ offset: marker,
66
+ height: markers[index + 1] || !step.compRef
67
+ ? ((_a = markers[index + 1]) !== null && _a !== void 0 ? _a : rootHeight) - marker
68
+ : step.compRef.clientHeight,
69
+ };
70
+ return acc;
71
+ }, {}));
72
+ // eslint-disable-next-line react-hooks/exhaustive-deps
73
+ }, [visibleSteps, root.current, options]);
74
+ const registerMarker = (0, react_1.useCallback)((stepId, element) => {
75
+ if (observerRef.current) {
76
+ const step = stepsMap.get(stepId);
77
+ if (step) {
78
+ step.markerRef = element;
30
79
  }
31
- }, 10);
32
- }, [_steps]);
33
- const unregister = (0, react_1.useCallback)((element) => {
80
+ observerRef.current.observe(element);
81
+ observedElementsRef.current.add(element);
82
+ }
83
+ }, [stepsMap]);
84
+ const removeMarker = (0, react_1.useCallback)((stepId, element) => {
34
85
  if (observerRef.current) {
35
- const stepKey = Number(element.dataset.stepKey);
36
- if (Number.isInteger(stepKey) && stepKey >= 0 && _steps[stepKey]) {
37
- _steps[stepKey].compRef = undefined;
86
+ const step = stepsMap.get(stepId);
87
+ if (step) {
88
+ step.markerRef = undefined;
38
89
  }
39
90
  observerRef.current.unobserve(element);
40
91
  observedElementsRef.current.delete(element);
41
92
  }
42
- }, [_steps]);
93
+ }, [stepsMap]);
94
+ const registerStep = (0, react_1.useCallback)((stepId, element) => {
95
+ const step = stepsMap.get(stepId);
96
+ if (!step) {
97
+ return;
98
+ }
99
+ step.compRef = element;
100
+ setVisibleSteps((prevSteps) => (0, utils_1.insertAt)(prevSteps, step.index, step));
101
+ }, [stepsMap]);
102
+ const removeStep = (0, react_1.useCallback)((stepId) => {
103
+ const step = stepsMap.get(stepId);
104
+ if (!step) {
105
+ return;
106
+ }
107
+ step.compRef = undefined;
108
+ setVisibleSteps((prevSteps) => (0, utils_1.removeElement)(prevSteps, step));
109
+ setActiveStep((prevStep) => (prevStep === stepId ? null : prevStep));
110
+ }, [stepsMap]);
43
111
  const observerCallback = (0, react_1.useCallback)((entries) => {
44
112
  var _a, _b, _c;
45
- if (lockObserver.current) {
113
+ if (lockObserver.current || !visibleSteps.length) {
46
114
  return;
47
115
  }
48
- const renderedSteps = _steps.filter((step) => Boolean(step.compRef));
49
- if (renderedSteps.length < 2) {
50
- setActiveStep(((_a = renderedSteps[0]) === null || _a === void 0 ? void 0 : _a.id) || null);
116
+ if (visibleSteps.length < 2) {
117
+ setActiveStep(((_a = visibleSteps[0]) === null || _a === void 0 ? void 0 : _a.id) || null);
51
118
  return;
52
119
  }
53
120
  for (const entry of entries) {
54
- const stepKey = Number((_c = (_b = entry.target) === null || _b === void 0 ? void 0 : _b.dataset) === null || _c === void 0 ? void 0 : _c.stepKey);
55
- if (!Number.isInteger(stepKey) || stepKey < 0) {
121
+ const stepId = (_c = (_b = entry.target) === null || _b === void 0 ? void 0 : _b.dataset) === null || _c === void 0 ? void 0 : _c.stepId;
122
+ if (!stepId) {
56
123
  continue;
57
124
  }
58
125
  const { intersectionRatio, boundingClientRect, rootBounds, isIntersecting } = entry;
59
- const step = _steps[stepKey];
60
- const stepIndex = renderedSteps.findIndex((renderedStep) => renderedStep.stepKey === step.stepKey);
61
- const { next } = (0, utils_1.getAdjacentValues)(renderedSteps, stepIndex);
126
+ const step = stepsMap.get(stepId);
127
+ if (!step) {
128
+ continue;
129
+ }
130
+ const stepIndex = visibleSteps.findIndex((renderedStep) => renderedStep.id === stepId);
131
+ const { next } = (0, utils_1.getAdjacentValues)(visibleSteps, stepIndex);
62
132
  const intersectionAtTop = (rootBounds === null || rootBounds === void 0 ? void 0 : rootBounds.bottom) !== undefined && boundingClientRect.top < rootBounds.top;
63
133
  const stepGoesIn = isIntersecting;
64
- if (intersectionRatio > 0.8 &&
65
- intersectionRatio < 1 &&
66
- intersectionAtTop &&
67
- activeStep === null) {
134
+ if (intersectionRatio > 0.8 && intersectionRatio < 1 && intersectionAtTop) {
68
135
  setActiveStep(step.id);
69
136
  break;
70
137
  }
@@ -76,28 +143,37 @@ function useCodeWalkthroughSteps(steps, enableDeepLink) {
76
143
  else if (next) {
77
144
  newStep = next.id;
78
145
  }
79
- if (newStep !== activeStep) {
80
- setActiveStep(newStep);
81
- }
146
+ setActiveStep((prevStep) => newStep || prevStep);
82
147
  break;
83
148
  }
84
149
  }
85
- }, [_steps, activeStep]);
150
+ }, [stepsMap, visibleSteps]);
86
151
  (0, react_1.useEffect)(() => {
87
- var _a, _b, _c;
88
- const filtersElementHeight = ((_a = filtersElementRef.current) === null || _a === void 0 ? void 0 : _a.clientHeight) || 0;
89
- const navbarHeight = ((_b = document.querySelector('nav')) === null || _b === void 0 ? void 0 : _b.clientHeight) || 0;
152
+ var _a;
153
+ if (!options) {
154
+ return;
155
+ }
90
156
  const newObserver = new IntersectionObserver(observerCallback, {
91
- threshold: [0.8, 0.85, 0.9, 0.95],
92
- rootMargin: `-${filtersElementHeight + navbarHeight}px 0px 0px 0px`,
157
+ threshold: [0.3, 0.8, 0.9, 0.95],
158
+ rootMargin: `-${options.filtersElementHeight + options.navbarHeight}px 0px 0px 0px`,
93
159
  });
94
- for (const observedElement of observedElementsRef.current) {
160
+ for (const observedElement of observedElementsRef.current.values()) {
95
161
  newObserver.observe(observedElement);
96
162
  }
97
- // Unobserve all from the old observer
98
- (_c = observerRef.current) === null || _c === void 0 ? void 0 : _c.disconnect();
163
+ (_a = observerRef.current) === null || _a === void 0 ? void 0 : _a.disconnect();
99
164
  observerRef.current = newObserver;
100
- }, [observerCallback]);
165
+ }, [observerCallback, markers, options]);
166
+ (0, react_1.useEffect)(() => {
167
+ var _a, _b, _c;
168
+ if (!options) {
169
+ return;
170
+ }
171
+ const rootTopOffset = (_b = (_a = root.current) === null || _a === void 0 ? void 0 : _a.offsetTop) !== null && _b !== void 0 ? _b : 0;
172
+ if (!activeStep && rootTopOffset <= options.navbarHeight) {
173
+ setActiveStep(((_c = visibleSteps[0]) === null || _c === void 0 ? void 0 : _c.id) || null);
174
+ }
175
+ // eslint-disable-next-line react-hooks/exhaustive-deps
176
+ }, [activeStep, root.current, options, visibleSteps]);
101
177
  /**
102
178
  * Update the URL search params with the current state of the filters and inputs
103
179
  */
@@ -118,6 +194,125 @@ function useCodeWalkthroughSteps(steps, enableDeepLink) {
118
194
  navigate({ search: newSearch }, { replace: true });
119
195
  // eslint-disable-next-line react-hooks/exhaustive-deps
120
196
  }, [activeStep]);
121
- return { register, unregister, lockObserver, filtersElementRef, activeStep, setActiveStep };
197
+ return {
198
+ registerStep,
199
+ removeStep,
200
+ markers,
201
+ registerMarker,
202
+ removeMarker,
203
+ lockObserver,
204
+ filtersElementRef,
205
+ activeStep,
206
+ setActiveStep,
207
+ };
208
+ }
209
+ /**
210
+ * This function analyzes the offset and height of each step to determine
211
+ * when a new group should be created. A new group is started when there is a free space
212
+ * between the two steps, treating it as the content of the next group header.
213
+ *
214
+ * @param steps - An array of `CodeWalkthroughStep` objects
215
+ *
216
+ * @returns An array of `StepsGroup` objects, each containing the offset from the top of the relative
217
+ * block, the free space at the top of the group, the total space used by the steps within the group
218
+ * and the steps themselves with relative offset and height.
219
+ */
220
+ function getGroups(steps) {
221
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
222
+ if (!steps.length) {
223
+ return [];
224
+ }
225
+ const firstStepOffset = (_c = (_b = (_a = steps[0]) === null || _a === void 0 ? void 0 : _a.compRef) === null || _b === void 0 ? void 0 : _b.offsetTop) !== null && _c !== void 0 ? _c : 0;
226
+ const firstStepHeight = (_f = (_e = (_d = steps[0]) === null || _d === void 0 ? void 0 : _d.compRef) === null || _e === void 0 ? void 0 : _e.clientHeight) !== null && _f !== void 0 ? _f : 0;
227
+ const secondStepOffset = (_j = (_h = (_g = steps[1]) === null || _g === void 0 ? void 0 : _g.compRef) === null || _h === void 0 ? void 0 : _h.offsetTop) !== null && _j !== void 0 ? _j : 0;
228
+ const margin = Math.max(secondStepOffset - firstStepOffset - firstStepHeight, 0);
229
+ let groupIndex = 0;
230
+ const groups = [
231
+ {
232
+ offset: 0,
233
+ freeSpace: firstStepOffset,
234
+ usedSpace: 0,
235
+ steps: [],
236
+ },
237
+ ];
238
+ for (let i = 0; i < steps.length; i++) {
239
+ let currentGroup = groups[groupIndex];
240
+ const step = steps[i];
241
+ const stepHeight = (_l = (_k = step.compRef) === null || _k === void 0 ? void 0 : _k.clientHeight) !== null && _l !== void 0 ? _l : 0;
242
+ const stepOffset = (_o = (_m = step.compRef) === null || _m === void 0 ? void 0 : _m.offsetTop) !== null && _o !== void 0 ? _o : 0;
243
+ const prevStepOffset = currentGroup.freeSpace + currentGroup.usedSpace;
244
+ if (prevStepOffset !== Math.max(stepOffset - currentGroup.offset, 0)) {
245
+ const offset = currentGroup.offset + currentGroup.freeSpace + currentGroup.usedSpace;
246
+ groupIndex++;
247
+ groups[groupIndex] = {
248
+ offset,
249
+ freeSpace: Math.max(stepOffset - offset, 0),
250
+ usedSpace: 0,
251
+ steps: [],
252
+ };
253
+ currentGroup = groups[groupIndex];
254
+ }
255
+ currentGroup.steps.push({
256
+ offset: stepOffset - currentGroup.offset,
257
+ height: stepHeight,
258
+ ref: step.compRef,
259
+ });
260
+ currentGroup.usedSpace += stepHeight + margin;
261
+ }
262
+ return groups;
263
+ }
264
+ function getGroupMarkers(group) {
265
+ if (!group.steps.length) {
266
+ return [];
267
+ }
268
+ if (group.steps.length === 1) {
269
+ return [group.offset + group.steps[0].offset - group.freeSpace];
270
+ }
271
+ const availableFreeSpace = group.freeSpace > 0.3 * window.innerHeight ? 0.3 * window.innerHeight : group.freeSpace;
272
+ const unusedFreeSpace = group.freeSpace - availableFreeSpace;
273
+ const lastStepOffset = group.steps[group.steps.length - 1].offset;
274
+ // distribute group free space between steps
275
+ return distributeMarkers({
276
+ startOffset: 0,
277
+ endOffset: lastStepOffset - unusedFreeSpace,
278
+ markers: group.steps.map((step) => step.offset),
279
+ additionalSteps: [(marker) => group.offset + unusedFreeSpace + marker],
280
+ });
281
+ }
282
+ /**
283
+ * Distribute markers preserving the relationship throughout the available space
284
+ * @param startOffset - the starting point of the available space
285
+ * @param endOffset - the end point of the available space
286
+ * @param markers - the markers to distribute
287
+ * @param additionalSteps - additional steps to apply to the markers
288
+ *
289
+ * @returns array of markers positions
290
+ */
291
+ function distributeMarkers({ endOffset, markers, startOffset, additionalSteps = [], }) {
292
+ return markers.map((marker) => {
293
+ const normalizedOffset = getNormalizedNumber({
294
+ min: markers[0],
295
+ max: markers[markers.length - 1],
296
+ value: marker,
297
+ });
298
+ const availableSpace = endOffset - startOffset;
299
+ let result = startOffset + normalizedOffset * availableSpace;
300
+ for (const additionalStep of additionalSteps) {
301
+ result = additionalStep(result);
302
+ }
303
+ return result;
304
+ });
305
+ }
306
+ /**
307
+ * Normalize a number between a min and max value
308
+ * @param min - the minimum value of the distribution
309
+ * @param max - the maximum value of the distribution
310
+ * @param value - the value to normalize
311
+ *
312
+ * @returns normalized number between 0 and 1
313
+ */
314
+ function getNormalizedNumber(options) {
315
+ const { min, max, value } = options;
316
+ return (value - min) / (max - min);
122
317
  }
123
318
  //# sourceMappingURL=use-code-walkthrough-steps.js.map
@@ -1,9 +1,16 @@
1
1
  import type { CodeWalkthroughFile, CodeWalkthroughStepAttr, CodeWalkthroughAttr } from '@redocly/config';
2
- import { type WalkthroughControlsState, type WalkthroughStepsState } from '../../../core/hooks';
2
+ import type { WalkthroughStepsState } from '../../../core/types';
3
+ import { type WalkthroughControlsState } from '../../../core/hooks';
3
4
  export type WalkthroughState = {
4
5
  stepsState: WalkthroughStepsState;
5
6
  controlsState: WalkthroughControlsState;
6
7
  downloadAssociatedFiles: CodeWalkthroughFile[];
7
8
  files: CodeWalkthroughFile[];
8
9
  };
9
- export declare function useCodeWalkthrough(steps: CodeWalkthroughStepAttr[], attributes: Omit<CodeWalkthroughAttr, 'steps' | 'preview'>): WalkthroughState;
10
+ type Params = {
11
+ steps: CodeWalkthroughStepAttr[];
12
+ attributes: Omit<CodeWalkthroughAttr, 'steps' | 'preview'>;
13
+ root: React.RefObject<HTMLDivElement | null>;
14
+ };
15
+ export declare function useCodeWalkthrough({ steps, attributes, root }: Params): WalkthroughState;
16
+ export {};
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.useCodeWalkthrough = useCodeWalkthrough;
4
4
  const hooks_1 = require("../../../core/hooks");
5
- function useCodeWalkthrough(steps, attributes) {
5
+ function useCodeWalkthrough({ steps, attributes, root }) {
6
6
  const { filters, filesets, inputs, toggles, __idx } = attributes;
7
7
  /*
8
8
  We only enable deep linking for the first CodeWalkthrough,
@@ -11,7 +11,7 @@ function useCodeWalkthrough(steps, attributes) {
11
11
  collisions/conflicts in the URL.
12
12
  */
13
13
  const enableDeepLink = __idx === 1;
14
- const stepsState = (0, hooks_1.useCodeWalkthroughSteps)(steps, enableDeepLink);
14
+ const stepsState = (0, hooks_1.useCodeWalkthroughSteps)({ steps, enableDeepLink, root });
15
15
  const controlsState = (0, hooks_1.useCodeWalkthroughControls)(filters, inputs, toggles, enableDeepLink);
16
16
  const files = filesets
17
17
  .filter((fileset) => controlsState.areConditionsMet(fileset))
@@ -36,5 +36,5 @@ export * from '../../core/hooks/code-walkthrough/use-renderable-files';
36
36
  export * from '../../core/hooks/use-element-size';
37
37
  export * from '../../core/hooks/use-time-ago';
38
38
  export * from '../../core/hooks/use-input-key-commands';
39
- export * from '../../core/hooks/use-page-active-version';
39
+ export * from '../../core/hooks/use-active-page-version';
40
40
  export * from '../../core/hooks/use-page-versions';
@@ -52,6 +52,6 @@ __exportStar(require("../../core/hooks/code-walkthrough/use-renderable-files"),
52
52
  __exportStar(require("../../core/hooks/use-element-size"), exports);
53
53
  __exportStar(require("../../core/hooks/use-time-ago"), exports);
54
54
  __exportStar(require("../../core/hooks/use-input-key-commands"), exports);
55
- __exportStar(require("../../core/hooks/use-page-active-version"), exports);
55
+ __exportStar(require("../../core/hooks/use-active-page-version"), exports);
56
56
  __exportStar(require("../../core/hooks/use-page-versions"), exports);
57
57
  //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ export declare function useActivePageVersion(): string | undefined;
@@ -1,11 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.usePageActiveVersion = usePageActiveVersion;
3
+ exports.useActivePageVersion = useActivePageVersion;
4
4
  const hooks_1 = require("../../core/hooks");
5
- function usePageActiveVersion() {
5
+ function useActivePageVersion() {
6
6
  const { usePageVersions } = (0, hooks_1.useThemeHooks)();
7
7
  const { versions } = usePageVersions();
8
8
  const activeVersion = versions.find((version) => version.active);
9
9
  return activeVersion === null || activeVersion === void 0 ? void 0 : activeVersion.version;
10
10
  }
11
- //# sourceMappingURL=use-page-active-version.js.map
11
+ //# sourceMappingURL=use-active-page-version.js.map
@@ -0,0 +1,13 @@
1
+ import type { MarkerArea } from '../../core/types';
2
+ export type ActiveStep = string | null;
3
+ export type WalkthroughStepsState = {
4
+ activeStep: ActiveStep;
5
+ setActiveStep: (stepId: ActiveStep) => void;
6
+ markers: Record<string, MarkerArea>;
7
+ registerMarker: (stepId: string, element: HTMLElement) => void;
8
+ removeMarker: (stepId: string, element: HTMLElement) => void;
9
+ registerStep: (stepId: string, element: HTMLElement) => void;
10
+ removeStep: (stepId: string) => void;
11
+ lockObserver?: React.RefObject<boolean>;
12
+ filtersElementRef?: React.RefObject<HTMLDivElement | null>;
13
+ };
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=code-walkthrough.js.map
@@ -13,3 +13,5 @@ export * from '../../core/types/user-menu';
13
13
  export * from '../../core/types/user-claims';
14
14
  export * from '../../core/types/common';
15
15
  export * from '../../core/types/open-api-server';
16
+ export * from '../../core/types/marker';
17
+ export * from '../../core/types/code-walkthrough';
@@ -29,4 +29,6 @@ __exportStar(require("../../core/types/user-menu"), exports);
29
29
  __exportStar(require("../../core/types/user-claims"), exports);
30
30
  __exportStar(require("../../core/types/common"), exports);
31
31
  __exportStar(require("../../core/types/open-api-server"), exports);
32
+ __exportStar(require("../../core/types/marker"), exports);
33
+ __exportStar(require("../../core/types/code-walkthrough"), exports);
32
34
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,4 @@
1
+ export type MarkerArea = {
2
+ offset: number;
3
+ height: number;
4
+ };
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=marker.js.map
@@ -36,3 +36,21 @@ export declare function getAdjacentValues<T>(array: T[], index: number): {
36
36
  prev: T | null;
37
37
  next: T | null;
38
38
  };
39
+ /**
40
+ * Inserts an element at a given index in an array. Returns a new array with the element inserted.
41
+ *
42
+ * @example
43
+ * const array = [10, 20, 30, 40];
44
+ * insertAt(array, 2, 25);
45
+ * // returns: [10, 20, 25, 30, 40]
46
+ */
47
+ export declare function insertAt<T>(array: T[], index: number, newElement: T): T[];
48
+ /**
49
+ * Removes an element from an array. Returns a new array with the element removed.
50
+ *
51
+ * @example
52
+ * const array = [10, 20, 30, 40];
53
+ * removeElement(array, 20);
54
+ * // returns: [10, 30, 40]
55
+ */
56
+ export declare function removeElement<T>(array: T[], item: T): T[];