@redocly/theme 0.55.0-next.1 → 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 (47) hide show
  1. package/lib/components/JsonViewer/JsonViewer.d.ts +2 -0
  2. package/lib/components/JsonViewer/JsonViewer.js +3 -1
  3. package/lib/components/Marker/Marker.d.ts +10 -0
  4. package/lib/components/Marker/Marker.js +62 -0
  5. package/lib/core/contexts/CodeWalkthrough/CodeWalkthroughStepsContext.d.ts +1 -1
  6. package/lib/core/contexts/CodeWalkthrough/CodeWalkthroughStepsContext.js +5 -2
  7. package/lib/core/hooks/code-walkthrough/use-code-walkthrough-steps.d.ts +17 -9
  8. package/lib/core/hooks/code-walkthrough/use-code-walkthrough-steps.js +242 -47
  9. package/lib/core/hooks/code-walkthrough/use-code-walkthrough.d.ts +9 -2
  10. package/lib/core/hooks/code-walkthrough/use-code-walkthrough.js +2 -2
  11. package/lib/core/hooks/index.d.ts +1 -1
  12. package/lib/core/hooks/index.js +1 -1
  13. package/lib/core/hooks/use-active-page-version.d.ts +1 -0
  14. package/lib/core/hooks/{use-page-active-version.js → use-active-page-version.js} +3 -3
  15. package/lib/core/types/code-walkthrough.d.ts +13 -0
  16. package/lib/core/types/code-walkthrough.js +3 -0
  17. package/lib/core/types/index.d.ts +2 -0
  18. package/lib/core/types/index.js +2 -0
  19. package/lib/core/types/marker.d.ts +4 -0
  20. package/lib/core/types/marker.js +3 -0
  21. package/lib/core/utils/js-utils.d.ts +18 -0
  22. package/lib/core/utils/js-utils.js +31 -0
  23. package/lib/index.d.ts +1 -0
  24. package/lib/index.js +1 -0
  25. package/lib/markdoc/components/CodeWalkthrough/CodeStep.d.ts +1 -2
  26. package/lib/markdoc/components/CodeWalkthrough/CodeStep.js +26 -20
  27. package/lib/markdoc/components/CodeWalkthrough/CodeWalkthrough.js +32 -3
  28. package/lib/markdoc/tags/code-step.js +0 -3
  29. package/lib/markdoc/tags/code-walkthrough.js +0 -1
  30. package/package.json +1 -1
  31. package/src/components/JsonViewer/JsonViewer.tsx +6 -0
  32. package/src/components/Marker/Marker.tsx +53 -0
  33. package/src/core/contexts/CodeWalkthrough/CodeWalkthroughStepsContext.tsx +6 -3
  34. package/src/core/hooks/code-walkthrough/use-code-walkthrough-steps.ts +326 -65
  35. package/src/core/hooks/code-walkthrough/use-code-walkthrough.ts +9 -6
  36. package/src/core/hooks/index.ts +1 -1
  37. package/src/core/hooks/{use-page-active-version.ts → use-active-page-version.ts} +1 -1
  38. package/src/core/types/code-walkthrough.ts +15 -0
  39. package/src/core/types/index.ts +2 -0
  40. package/src/core/types/marker.ts +4 -0
  41. package/src/core/utils/js-utils.ts +31 -0
  42. package/src/index.ts +1 -0
  43. package/src/markdoc/components/CodeWalkthrough/CodeStep.tsx +76 -36
  44. package/src/markdoc/components/CodeWalkthrough/CodeWalkthrough.tsx +8 -3
  45. package/src/markdoc/tags/code-step.ts +0 -3
  46. package/src/markdoc/tags/code-walkthrough.ts +0 -1
  47. package/lib/core/hooks/use-page-active-version.d.ts +0 -1
@@ -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[];
@@ -5,6 +5,8 @@ exports.toStringIfDefined = toStringIfDefined;
5
5
  exports.isBrowser = isBrowser;
6
6
  exports.isPlainObject = isPlainObject;
7
7
  exports.getAdjacentValues = getAdjacentValues;
8
+ exports.insertAt = insertAt;
9
+ exports.removeElement = removeElement;
8
10
  function findDeepFirst(list, cb) {
9
11
  for (const item of list) {
10
12
  if (cb(item) === true) {
@@ -65,4 +67,33 @@ function getAdjacentValues(array, index) {
65
67
  const nextValue = index < array.length - 1 ? array[index + 1] : null;
66
68
  return { prev: prevValue, next: nextValue };
67
69
  }
70
+ /**
71
+ * Inserts an element at a given index in an array. Returns a new array with the element inserted.
72
+ *
73
+ * @example
74
+ * const array = [10, 20, 30, 40];
75
+ * insertAt(array, 2, 25);
76
+ * // returns: [10, 20, 25, 30, 40]
77
+ */
78
+ function insertAt(array, index, newElement) {
79
+ const result = array.concat();
80
+ result.splice(index, 0, newElement);
81
+ return result;
82
+ }
83
+ /**
84
+ * Removes an element from an array. Returns a new array with the element removed.
85
+ *
86
+ * @example
87
+ * const array = [10, 20, 30, 40];
88
+ * removeElement(array, 20);
89
+ * // returns: [10, 30, 40]
90
+ */
91
+ function removeElement(array, item) {
92
+ const index = array.indexOf(item);
93
+ if (index === -1)
94
+ return array;
95
+ const result = array.slice();
96
+ result.splice(index, 1);
97
+ return result;
98
+ }
68
99
  //# sourceMappingURL=js-utils.js.map
package/lib/index.d.ts CHANGED
@@ -21,6 +21,7 @@ export * from './components/Tooltip/Tooltip';
21
21
  export * from './components/Tags/HttpTag';
22
22
  export * from './components/Tags/CounterTag';
23
23
  export * from './components/VersionPicker/VersionPicker';
24
+ export * from './components/Marker/Marker';
24
25
  export * from './components/Buttons/CopyButton';
25
26
  export * from './components/Buttons/EditPageButton';
26
27
  export * from './components/Buttons/EmailButton';
package/lib/index.js CHANGED
@@ -51,6 +51,7 @@ __exportStar(require("./components/Tooltip/Tooltip"), exports);
51
51
  __exportStar(require("./components/Tags/HttpTag"), exports);
52
52
  __exportStar(require("./components/Tags/CounterTag"), exports);
53
53
  __exportStar(require("./components/VersionPicker/VersionPicker"), exports);
54
+ __exportStar(require("./components/Marker/Marker"), exports);
54
55
  /* Buttons */
55
56
  __exportStar(require("./components/Buttons/CopyButton"), exports);
56
57
  __exportStar(require("./components/Buttons/EditPageButton"), exports);
@@ -2,10 +2,9 @@ import React, { type PropsWithChildren } from 'react';
2
2
  import type { WithConditions } from '@redocly/config';
3
3
  export type CodeStepProps = WithConditions<{
4
4
  id: string;
5
- stepKey: number;
6
5
  heading?: string;
7
6
  }>;
8
- export declare function CodeStep({ id, heading, stepKey, when, unless, children, }: PropsWithChildren<CodeStepProps>): React.JSX.Element | null;
7
+ export declare function CodeStep({ id, heading, when, unless, children, }: PropsWithChildren<CodeStepProps>): React.JSX.Element | null;
9
8
  export declare const StepWrapper: import("styled-components").StyledComponent<"div", any, {
10
9
  isActive: boolean;
11
10
  scrollMarginTop: number;
@@ -30,25 +30,31 @@ exports.StepWrapper = void 0;
30
30
  exports.CodeStep = CodeStep;
31
31
  const react_1 = __importStar(require("react"));
32
32
  const styled_components_1 = __importDefault(require("styled-components"));
33
+ const Marker_1 = require("../../../components/Marker/Marker");
33
34
  const contexts_1 = require("../../../core/contexts");
34
- function CodeStep({ id, heading, stepKey, when, unless, children, }) {
35
+ function CodeStep({ id, heading, when, unless, children, }) {
35
36
  const compRef = (0, react_1.useRef)(null);
37
+ const markerRef = (0, react_1.useRef)(null);
36
38
  const { areConditionsMet } = (0, react_1.useContext)(contexts_1.CodeWalkthroughControlsStateContext);
37
- const { activeStep, setActiveStep, register, unregister, lockObserver, filtersElementRef } = (0, react_1.useContext)(contexts_1.CodeWalkthroughStepsContext);
39
+ const { activeStep, setActiveStep, markers, registerStep, removeStep, registerMarker, removeMarker, lockObserver, filtersElementRef, } = (0, react_1.useContext)(contexts_1.CodeWalkthroughStepsContext);
38
40
  const isActive = activeStep === id;
39
41
  const [scrollMarginTop, setScrollMarginTop] = (0, react_1.useState)(0);
40
- const handleActivateStep = () => {
42
+ const marker = (0, react_1.useMemo)(() => markers[id], [markers, id]);
43
+ const isVisible = (0, react_1.useMemo)(() => areConditionsMet({ when, unless }), [areConditionsMet, when, unless]);
44
+ const handleActivateStep = (0, react_1.useCallback)(() => {
41
45
  if (lockObserver) {
42
46
  lockObserver.current = true;
43
- if (compRef.current) {
44
- compRef.current.scrollIntoView({ behavior: 'smooth', block: 'start' });
47
+ if (markerRef.current) {
48
+ markerRef.current.scrollIntoView({ behavior: 'smooth', block: 'start' });
45
49
  }
46
50
  setActiveStep(id);
47
51
  setTimeout(() => {
48
52
  lockObserver.current = false;
49
53
  }, 1000);
50
54
  }
51
- };
55
+ }, [setActiveStep, lockObserver, id]);
56
+ const handleRegisterMarker = (0, react_1.useCallback)((element) => registerMarker(id, element), [registerMarker, id]);
57
+ const handleRemoveMarker = (0, react_1.useCallback)((element) => removeMarker(id, element), [removeMarker, id]);
52
58
  (0, react_1.useEffect)(() => {
53
59
  // If the step is active during first render, scroll to it
54
60
  // This is to ensure that the step is visible when the page is loaded
@@ -62,33 +68,33 @@ function CodeStep({ id, heading, stepKey, when, unless, children, }) {
62
68
  (0, react_1.useEffect)(() => {
63
69
  var _a, _b;
64
70
  const currentCompRef = compRef.current;
65
- if (currentCompRef) {
71
+ if (currentCompRef && isVisible) {
66
72
  currentCompRef
67
73
  .querySelectorAll('a, button, input, textarea, select, [tabindex]')
68
74
  .forEach((el) => {
69
75
  el.setAttribute('tabindex', '-1');
70
76
  });
71
- register(currentCompRef);
77
+ registerStep(id, currentCompRef);
72
78
  }
73
79
  const filtersElementHeight = ((_a = filtersElementRef === null || filtersElementRef === void 0 ? void 0 : filtersElementRef.current) === null || _a === void 0 ? void 0 : _a.clientHeight) || 0;
74
80
  const navbarHeight = ((_b = document.querySelector('nav')) === null || _b === void 0 ? void 0 : _b.clientHeight) || 0;
75
81
  setScrollMarginTop(filtersElementHeight + navbarHeight + 10);
76
82
  return () => {
77
- if (currentCompRef) {
78
- unregister(currentCompRef);
79
- }
83
+ removeStep(id);
80
84
  };
81
- }, [activeStep, register, unregister, filtersElementRef]);
82
- if (!areConditionsMet({ when, unless })) {
83
- if (isActive) {
84
- setActiveStep(null);
85
- }
85
+ }, [filtersElementRef, registerStep, removeStep, id, isVisible]);
86
+ if (!isVisible) {
86
87
  return null;
87
88
  }
88
- return (react_1.default.createElement(exports.StepWrapper, { "data-component-name": "Markdoc/CodeWalkthrough/CodeStep", ref: compRef, isActive: isActive, scrollMarginTop: scrollMarginTop, "data-step-key": stepKey, "data-step-active": isActive, onClick: handleActivateStep, onFocus: handleActivateStep, tabIndex: 0, className: "code-step-wrapper" },
89
- react_1.default.createElement(StepContent, { isActive: isActive },
90
- heading ? react_1.default.createElement(StepHeading, null, heading) : null,
91
- children)));
89
+ return (react_1.default.createElement(react_1.default.Fragment, null,
90
+ marker && (react_1.default.createElement(Marker_1.Marker, { ref: markerRef, marker: marker, registerMarker: handleRegisterMarker, removeMarker: handleRemoveMarker, dataAttribures: {
91
+ 'data-step-id': id,
92
+ 'data-step-active': isActive,
93
+ } })),
94
+ react_1.default.createElement(exports.StepWrapper, { "data-component-name": "Markdoc/CodeWalkthrough/CodeStep", ref: compRef, isActive: isActive, scrollMarginTop: scrollMarginTop, onClick: handleActivateStep, onFocus: handleActivateStep, tabIndex: 0, className: "code-step-wrapper" },
95
+ react_1.default.createElement(StepContent, { isActive: isActive },
96
+ heading ? react_1.default.createElement(StepHeading, null, heading) : null,
97
+ children))));
92
98
  }
93
99
  const StepContent = styled_components_1.default.div `
94
100
  margin: var(--spacing-xs) 0px var(--spacing-xs) calc(var(--spacing-unit) * 3.5);
@@ -1,4 +1,27 @@
1
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
+ };
2
25
  var __rest = (this && this.__rest) || function (s, e) {
3
26
  var t = {};
4
27
  for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
@@ -15,7 +38,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
15
38
  };
16
39
  Object.defineProperty(exports, "__esModule", { value: true });
17
40
  exports.CodeWalkthrough = CodeWalkthrough;
18
- const react_1 = __importDefault(require("react"));
41
+ const react_1 = __importStar(require("react"));
19
42
  const styled_components_1 = __importDefault(require("styled-components"));
20
43
  const contexts_1 = require("../../../core/contexts");
21
44
  const CodePanel_1 = require("../../../markdoc/components/CodeWalkthrough/CodePanel");
@@ -23,18 +46,24 @@ const hooks_1 = require("../../../core/hooks");
23
46
  const CodeFilters_1 = require("../../../markdoc/components/CodeWalkthrough/CodeFilters");
24
47
  function CodeWalkthrough(_a) {
25
48
  var { children, steps, preview } = _a, attributes = __rest(_a, ["children", "steps", "preview"]);
26
- const { controlsState, stepsState, files, downloadAssociatedFiles } = (0, hooks_1.useCodeWalkthrough)(steps, attributes);
49
+ const root = (0, react_1.useRef)(null);
50
+ const { controlsState, stepsState, files, downloadAssociatedFiles } = (0, hooks_1.useCodeWalkthrough)({
51
+ steps,
52
+ attributes,
53
+ root,
54
+ });
27
55
  const { activeFilters, getControlState, changeControlState } = controlsState;
28
56
  const { filtersElementRef } = stepsState;
29
57
  return (react_1.default.createElement(contexts_1.CodeWalkthroughStepsContext.Provider, { value: stepsState },
30
58
  react_1.default.createElement(contexts_1.CodeWalkthroughControlsStateContext.Provider, { value: controlsState },
31
- react_1.default.createElement(CodeWalkthroughWrapper, { className: "code-walkthrough", "data-component-name": "Markdoc/CodeWalkthrough/CodeWalkthrough" },
59
+ react_1.default.createElement(CodeWalkthroughWrapper, { ref: root, className: "code-walkthrough", "data-component-name": "Markdoc/CodeWalkthrough/CodeWalkthrough" },
32
60
  react_1.default.createElement(DocsPanel, null,
33
61
  react_1.default.createElement(CodeFilters_1.CodeFilters, { filters: activeFilters, getFilterState: getControlState, handleFilterSelect: changeControlState, filtersElementRef: filtersElementRef }),
34
62
  react_1.default.createElement(ContentWrapper, null, children)),
35
63
  react_1.default.createElement(CodePanel_1.CodePanel, { files: files, downloadAssociatedFiles: downloadAssociatedFiles, preview: preview })))));
36
64
  }
37
65
  const CodeWalkthroughWrapper = styled_components_1.default.div `
66
+ position: relative;
38
67
  display: grid;
39
68
  grid-template-columns: 4fr 6fr;
40
69
 
@@ -18,9 +18,6 @@ exports.codeStep = {
18
18
  unless: {
19
19
  type: Object,
20
20
  },
21
- stepKey: {
22
- type: Number, // internal
23
- },
24
21
  },
25
22
  render: 'CodeStep',
26
23
  },
@@ -149,7 +149,6 @@ exports.codeWalkthrough = {
149
149
  function collectStepsFromChildren(children, idx = 0) {
150
150
  return children.flatMap((child) => {
151
151
  if (child instanceof markdoc_1.default.Tag && child.name === 'CodeStep') {
152
- child.attributes.stepKey = idx++;
153
152
  return [child];
154
153
  }
155
154
  if (child instanceof markdoc_1.default.Tag) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/theme",
3
- "version": "0.55.0-next.1",
3
+ "version": "0.55.0-next.3",
4
4
  "description": "Shared UI components lib",
5
5
  "keywords": [
6
6
  "theme",
@@ -8,6 +8,8 @@ import { CodeBlock } from '@redocly/theme/components/CodeBlock/CodeBlock';
8
8
 
9
9
  import { JsonValue } from './helpers';
10
10
 
11
+ export type PanelType = 'request' | 'responses' | 'request-samples' | 'response-samples';
12
+
11
13
  export type JsonProps = {
12
14
  title?: CodeBlockControlsProps['title'];
13
15
  data: any;
@@ -16,6 +18,7 @@ export type JsonProps = {
16
18
  startLineNumber?: number;
17
19
  hideHeader?: boolean;
18
20
  onCopyClick?: () => void;
21
+ onPanelToggle?: (isExpanded: boolean, panelType?: PanelType) => void;
19
22
  };
20
23
 
21
24
  function JsonComponent({
@@ -23,6 +26,7 @@ function JsonComponent({
23
26
  expandLevel = 1,
24
27
  className,
25
28
  onCopyClick,
29
+ onPanelToggle,
26
30
  title,
27
31
  hideHeader,
28
32
  }: JsonProps): JSX.Element {
@@ -33,6 +37,7 @@ function JsonComponent({
33
37
 
34
38
  const expandAll = () => {
35
39
  setExpandAllSignal(true);
40
+ onPanelToggle?.(true);
36
41
  setTimeout(() => {
37
42
  setExpandAllSignal(undefined);
38
43
  });
@@ -40,6 +45,7 @@ function JsonComponent({
40
45
 
41
46
  const collapseAll = () => {
42
47
  setExpandAllSignal(false);
48
+ onPanelToggle?.(false);
43
49
  setTimeout(() => {
44
50
  setExpandAllSignal(undefined);
45
51
  });
@@ -0,0 +1,53 @@
1
+ import React, { useEffect, useRef, forwardRef, useLayoutEffect } from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ import type { MarkerArea } from '@redocly/theme/core/types';
5
+
6
+ type MarkerProps = {
7
+ marker: MarkerArea;
8
+ registerMarker: (element: HTMLElement) => void;
9
+ removeMarker: (element: HTMLElement) => void;
10
+ dataAttribures?: Record<string, string | boolean | number>;
11
+ };
12
+
13
+ export const Marker = forwardRef<HTMLDivElement, MarkerProps>(
14
+ ({ marker, dataAttribures, registerMarker, removeMarker }, forwardedRef) => {
15
+ const internalRef = useRef<HTMLDivElement | null>(null);
16
+
17
+ // Reference should be available before paint
18
+ useLayoutEffect(() => {
19
+ if (!forwardedRef) return;
20
+ if (typeof forwardedRef === 'function') {
21
+ forwardedRef(internalRef.current);
22
+ } else {
23
+ forwardedRef.current = internalRef.current;
24
+ }
25
+ }, [forwardedRef]);
26
+
27
+ useEffect(() => {
28
+ const currentElement = internalRef.current;
29
+ if (!currentElement) return;
30
+
31
+ registerMarker(currentElement);
32
+ return () => removeMarker(currentElement);
33
+ }, [registerMarker, removeMarker, marker]);
34
+
35
+ return (
36
+ <StyledMarker
37
+ data-component-name="Marker/Marker"
38
+ ref={internalRef}
39
+ marker={marker}
40
+ {...(dataAttribures ?? {})}
41
+ />
42
+ );
43
+ },
44
+ );
45
+
46
+ const StyledMarker = styled.div<{ marker: MarkerArea }>`
47
+ position: absolute;
48
+ z-index: -1;
49
+ top: ${({ marker }) => marker.offset}px;
50
+ height: ${({ marker }) => marker.height}px;
51
+ width: 100%;
52
+ left: 0;
53
+ `;
@@ -1,10 +1,13 @@
1
1
  import { createContext } from 'react';
2
2
 
3
- import type { WalkthroughStepsState } from '@redocly/theme/core/hooks';
3
+ import type { WalkthroughStepsState } from '@redocly/theme/core/types';
4
4
 
5
5
  export const CodeWalkthroughStepsContext = createContext<WalkthroughStepsState>({
6
+ markers: {},
6
7
  activeStep: null,
7
8
  setActiveStep: () => {},
8
- register: () => {},
9
- unregister: () => {},
9
+ registerStep: () => {},
10
+ registerMarker: () => {},
11
+ removeMarker: () => {},
12
+ removeStep: () => {},
10
13
  });