@redocly/theme 0.47.0 → 0.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/Buttons/CopyButton.d.ts +8 -3
- package/lib/components/Buttons/CopyButton.js +4 -3
- package/lib/components/Feedback/Mood.d.ts +2 -2
- package/lib/components/Feedback/Mood.js +6 -6
- package/lib/components/Feedback/Rating.d.ts +2 -2
- package/lib/components/Feedback/Rating.js +6 -6
- package/lib/components/Feedback/Scale.d.ts +2 -2
- package/lib/components/Feedback/Scale.js +6 -6
- package/lib/components/Feedback/Sentiment.d.ts +2 -2
- package/lib/components/Feedback/Sentiment.js +6 -6
- package/lib/components/Search/SearchDialog.js +8 -8
- package/lib/components/Search/SearchFilter.d.ts +3 -2
- package/lib/components/Search/SearchFilter.js +2 -2
- package/lib/components/Search/SearchFilterField.d.ts +3 -2
- package/lib/components/Search/SearchFilterField.js +2 -2
- package/lib/components/Search/SearchGroups.d.ts +5 -4
- package/lib/components/Search/SearchGroups.js +3 -3
- package/lib/components/StatusCode/StatusCode.js +11 -14
- package/lib/core/constants/code-walkthrough.d.ts +1 -0
- package/lib/core/constants/code-walkthrough.js +5 -0
- package/lib/core/constants/index.d.ts +1 -0
- package/lib/core/constants/index.js +1 -0
- package/lib/core/contexts/CodeWalkthrough/CodeWalkthroughControlsContext.d.ts +2 -0
- package/lib/core/contexts/CodeWalkthrough/CodeWalkthroughControlsContext.js +18 -0
- package/lib/core/contexts/CodeWalkthrough/CodeWalkthroughStepsContext.d.ts +2 -0
- package/lib/core/contexts/CodeWalkthrough/CodeWalkthroughStepsContext.js +11 -0
- package/lib/core/contexts/index.d.ts +2 -0
- package/lib/core/contexts/index.js +2 -0
- package/lib/core/hooks/__mocks__/search/use-search-filter.d.ts +1 -1
- package/lib/core/hooks/__mocks__/search/use-search-filter.js +1 -1
- package/lib/core/hooks/code-walkthrough/__mocks__/MockIntersectionObserver.d.ts +10 -0
- package/lib/core/hooks/code-walkthrough/__mocks__/MockIntersectionObserver.js +55 -0
- package/lib/core/hooks/code-walkthrough/use-code-panel.d.ts +6 -0
- package/lib/core/hooks/code-walkthrough/use-code-panel.js +93 -0
- package/lib/core/hooks/code-walkthrough/use-code-walkthrough-controls.d.ts +22 -0
- package/lib/core/hooks/code-walkthrough/use-code-walkthrough-controls.js +183 -0
- package/lib/core/hooks/code-walkthrough/use-code-walkthrough-steps.d.ts +16 -0
- package/lib/core/hooks/code-walkthrough/use-code-walkthrough-steps.js +117 -0
- package/lib/core/hooks/code-walkthrough/use-code-walkthrough.d.ts +14 -0
- package/lib/core/hooks/code-walkthrough/use-code-walkthrough.js +22 -0
- package/lib/core/hooks/index.d.ts +4 -0
- package/lib/core/hooks/index.js +4 -0
- package/lib/core/hooks/search/use-search-filter.d.ts +2 -2
- package/lib/core/hooks/search/use-search-filter.js +5 -5
- package/lib/core/styles/global.js +2 -0
- package/lib/core/templates/Markdown.d.ts +8 -2
- package/lib/core/templates/Markdown.js +5 -2
- package/lib/core/types/hooks.d.ts +3 -0
- package/lib/core/types/l10n.d.ts +1 -1
- package/lib/core/types/search.d.ts +1 -2
- package/lib/core/utils/download-code-walkthrough.d.ts +4 -0
- package/lib/core/utils/download-code-walkthrough.js +32 -0
- package/lib/core/utils/get-code-walkthrough-file-text.d.ts +4 -0
- package/lib/core/utils/get-code-walkthrough-file-text.js +22 -0
- package/lib/core/utils/get-file-icon.d.ts +2 -0
- package/lib/core/utils/get-file-icon.js +31 -0
- package/lib/core/utils/index.d.ts +5 -0
- package/lib/core/utils/index.js +5 -0
- package/lib/core/utils/js-utils.d.ts +30 -0
- package/lib/core/utils/js-utils.js +41 -0
- package/lib/core/utils/match-code-walkthrough-conditions.d.ts +4 -0
- package/lib/core/utils/match-code-walkthrough-conditions.js +23 -0
- package/lib/core/utils/replace-inputs-with-value.d.ts +3 -0
- package/lib/core/utils/replace-inputs-with-value.js +16 -0
- package/lib/icons/DocumentCssIcon/DocumentCssIcon.d.ts +9 -0
- package/lib/icons/DocumentCssIcon/DocumentCssIcon.js +23 -0
- package/lib/icons/DocumentCssIcon/index.d.ts +1 -0
- package/lib/icons/DocumentCssIcon/index.js +6 -0
- package/lib/icons/DocumentGraphqlIcon/DocumentGraphqlIcon.d.ts +9 -0
- package/lib/icons/DocumentGraphqlIcon/DocumentGraphqlIcon.js +31 -0
- package/lib/icons/DocumentGraphqlIcon/index.d.ts +1 -0
- package/lib/icons/DocumentGraphqlIcon/index.js +6 -0
- package/lib/icons/DocumentHtmlIcon/DocumentHtmlIcon.d.ts +9 -0
- package/lib/icons/DocumentHtmlIcon/DocumentHtmlIcon.js +23 -0
- package/lib/icons/DocumentHtmlIcon/index.d.ts +1 -0
- package/lib/icons/DocumentHtmlIcon/index.js +6 -0
- package/lib/icons/DocumentJsIcon/DocumentJsIcon.d.ts +9 -0
- package/lib/icons/DocumentJsIcon/DocumentJsIcon.js +23 -0
- package/lib/icons/DocumentJsIcon/index.d.ts +1 -0
- package/lib/icons/DocumentJsIcon/index.js +6 -0
- package/lib/icons/DocumentJsonIcon/DocumentJsonIcon.d.ts +9 -0
- package/lib/icons/DocumentJsonIcon/DocumentJsonIcon.js +23 -0
- package/lib/icons/DocumentJsonIcon/index.d.ts +1 -0
- package/lib/icons/DocumentJsonIcon/index.js +6 -0
- package/lib/icons/DocumentMarkdownIcon/DocumentMarkdownIcon.d.ts +9 -0
- package/lib/icons/DocumentMarkdownIcon/DocumentMarkdownIcon.js +23 -0
- package/lib/icons/DocumentMarkdownIcon/index.d.ts +1 -0
- package/lib/icons/DocumentMarkdownIcon/index.js +6 -0
- package/lib/icons/DocumentReactIcon/DocumentReactIcon.d.ts +9 -0
- package/lib/icons/DocumentReactIcon/DocumentReactIcon.js +23 -0
- package/lib/icons/DocumentReactIcon/index.d.ts +1 -0
- package/lib/icons/DocumentReactIcon/index.js +6 -0
- package/lib/icons/DocumentTsIcon/DocumentTsIcon.d.ts +9 -0
- package/lib/icons/DocumentTsIcon/DocumentTsIcon.js +23 -0
- package/lib/icons/DocumentTsIcon/index.d.ts +1 -0
- package/lib/icons/DocumentTsIcon/index.js +6 -0
- package/lib/icons/DocumentYamlIcon/DocumentYamlIcon.d.ts +9 -0
- package/lib/icons/DocumentYamlIcon/DocumentYamlIcon.js +23 -0
- package/lib/icons/DocumentYamlIcon/index.d.ts +1 -0
- package/lib/icons/DocumentYamlIcon/index.js +6 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/layouts/CodeWalkthroughLayout.d.ts +5 -0
- package/lib/layouts/CodeWalkthroughLayout.js +72 -0
- package/lib/markdoc/attributes/code-walkthrough-filesets.d.ts +5 -0
- package/lib/markdoc/attributes/code-walkthrough-filesets.js +16 -0
- package/lib/markdoc/attributes/code-walkthrough-filters.d.ts +5 -0
- package/lib/markdoc/attributes/code-walkthrough-filters.js +16 -0
- package/lib/markdoc/components/CodeWalkthrough/CodeContainer.d.ts +5 -0
- package/lib/markdoc/components/CodeWalkthrough/CodeContainer.js +81 -0
- package/lib/markdoc/components/CodeWalkthrough/CodeFilters.d.ts +12 -0
- package/lib/markdoc/components/CodeWalkthrough/CodeFilters.js +58 -0
- package/lib/markdoc/components/CodeWalkthrough/CodePanel.d.ts +8 -0
- package/lib/markdoc/components/CodeWalkthrough/CodePanel.js +64 -0
- package/lib/markdoc/components/CodeWalkthrough/CodePanelHeader.d.ts +8 -0
- package/lib/markdoc/components/CodeWalkthrough/CodePanelHeader.js +155 -0
- package/lib/markdoc/components/CodeWalkthrough/CodePanelPreview.d.ts +2 -0
- package/lib/markdoc/components/CodeWalkthrough/CodePanelPreview.js +73 -0
- package/lib/markdoc/components/CodeWalkthrough/CodePanelToolbar.d.ts +5 -0
- package/lib/markdoc/components/CodeWalkthrough/CodePanelToolbar.js +47 -0
- package/lib/markdoc/components/CodeWalkthrough/CodeStep.d.ts +12 -0
- package/lib/markdoc/components/CodeWalkthrough/CodeStep.js +128 -0
- package/lib/markdoc/components/CodeWalkthrough/CodeToggle.d.ts +9 -0
- package/lib/markdoc/components/CodeWalkthrough/CodeToggle.js +69 -0
- package/lib/markdoc/components/CodeWalkthrough/CodeWalkthrough.d.ts +4 -0
- package/lib/markdoc/components/CodeWalkthrough/CodeWalkthrough.js +121 -0
- package/lib/markdoc/components/CodeWalkthrough/Input.d.ts +8 -0
- package/lib/markdoc/components/CodeWalkthrough/Input.js +99 -0
- package/lib/markdoc/components/CodeWalkthrough/variables.d.ts +1 -0
- package/lib/markdoc/components/CodeWalkthrough/variables.js +19 -0
- package/lib/markdoc/components/default.d.ts +5 -0
- package/lib/markdoc/components/default.js +5 -0
- package/lib/markdoc/default.js +8 -0
- package/lib/markdoc/tags/code-step.d.ts +2 -0
- package/lib/markdoc/tags/code-step.js +28 -0
- package/lib/markdoc/tags/code-toggle.d.ts +2 -0
- package/lib/markdoc/tags/code-toggle.js +40 -0
- package/lib/markdoc/tags/code-walkthrough.d.ts +8 -0
- package/lib/markdoc/tags/code-walkthrough.js +242 -0
- package/lib/markdoc/tags/input.d.ts +2 -0
- package/lib/markdoc/tags/input.js +37 -0
- package/lib/plugin.d.ts +2 -1
- package/lib/plugin.js +2 -4
- package/package.json +10 -3
- package/src/components/Buttons/CopyButton.tsx +24 -6
- package/src/components/Feedback/Mood.tsx +9 -10
- package/src/components/Feedback/Rating.tsx +9 -10
- package/src/components/Feedback/Scale.tsx +9 -10
- package/src/components/Feedback/Sentiment.tsx +9 -10
- package/src/components/Search/SearchDialog.tsx +11 -6
- package/src/components/Search/SearchFilter.tsx +6 -3
- package/src/components/Search/SearchFilterField.tsx +4 -2
- package/src/components/Search/SearchGroups.tsx +13 -8
- package/src/components/StatusCode/StatusCode.ts +11 -10
- package/src/core/constants/code-walkthrough.ts +1 -0
- package/src/core/constants/index.ts +1 -0
- package/src/core/contexts/CodeWalkthrough/CodeWalkthroughControlsContext.tsx +21 -0
- package/src/core/contexts/CodeWalkthrough/CodeWalkthroughStepsContext.tsx +10 -0
- package/src/core/contexts/index.ts +2 -0
- package/src/core/hooks/__mocks__/search/use-search-filter.ts +1 -1
- package/src/core/hooks/code-walkthrough/__mocks__/MockIntersectionObserver.ts +22 -0
- package/src/core/hooks/code-walkthrough/use-code-panel.ts +164 -0
- package/src/core/hooks/code-walkthrough/use-code-walkthrough-controls.ts +302 -0
- package/src/core/hooks/code-walkthrough/use-code-walkthrough-steps.ts +165 -0
- package/src/core/hooks/code-walkthrough/use-code-walkthrough.ts +51 -0
- package/src/core/hooks/index.ts +4 -0
- package/src/core/hooks/search/use-search-filter.ts +9 -5
- package/src/core/styles/global.ts +2 -0
- package/src/core/templates/Markdown.tsx +16 -5
- package/src/core/types/hooks.ts +3 -0
- package/src/core/types/l10n.ts +5 -3
- package/src/core/types/search.ts +1 -2
- package/src/core/utils/download-code-walkthrough.ts +25 -0
- package/src/core/utils/get-code-walkthrough-file-text.ts +32 -0
- package/src/core/utils/get-file-icon.ts +35 -0
- package/src/core/utils/index.ts +5 -0
- package/src/core/utils/js-utils.ts +48 -0
- package/src/core/utils/match-code-walkthrough-conditions.ts +29 -0
- package/src/core/utils/replace-inputs-with-value.ts +11 -0
- package/src/icons/DocumentCssIcon/DocumentCssIcon.tsx +36 -0
- package/src/icons/DocumentCssIcon/index.ts +1 -0
- package/src/icons/DocumentGraphqlIcon/DocumentGraphqlIcon.tsx +64 -0
- package/src/icons/DocumentGraphqlIcon/index.ts +1 -0
- package/src/icons/DocumentHtmlIcon/DocumentHtmlIcon.tsx +36 -0
- package/src/icons/DocumentHtmlIcon/index.ts +1 -0
- package/src/icons/DocumentJsIcon/DocumentJsIcon.tsx +36 -0
- package/src/icons/DocumentJsIcon/index.ts +1 -0
- package/src/icons/DocumentJsonIcon/DocumentJsonIcon.tsx +36 -0
- package/src/icons/DocumentJsonIcon/index.ts +1 -0
- package/src/icons/DocumentMarkdownIcon/DocumentMarkdownIcon.tsx +36 -0
- package/src/icons/DocumentMarkdownIcon/index.ts +1 -0
- package/src/icons/DocumentReactIcon/DocumentReactIcon.tsx +36 -0
- package/src/icons/DocumentReactIcon/index.ts +1 -0
- package/src/icons/DocumentTsIcon/DocumentTsIcon.tsx +38 -0
- package/src/icons/DocumentTsIcon/index.ts +1 -0
- package/src/icons/DocumentYamlIcon/DocumentYamlIcon.tsx +36 -0
- package/src/icons/DocumentYamlIcon/index.ts +1 -0
- package/src/index.ts +1 -0
- package/src/layouts/CodeWalkthroughLayout.tsx +78 -0
- package/src/markdoc/attributes/code-walkthrough-filesets.ts +9 -0
- package/src/markdoc/attributes/code-walkthrough-filters.ts +9 -0
- package/src/markdoc/components/CodeWalkthrough/CodeContainer.tsx +76 -0
- package/src/markdoc/components/CodeWalkthrough/CodeFilters.tsx +87 -0
- package/src/markdoc/components/CodeWalkthrough/CodePanel.tsx +68 -0
- package/src/markdoc/components/CodeWalkthrough/CodePanelHeader.tsx +192 -0
- package/src/markdoc/components/CodeWalkthrough/CodePanelPreview.tsx +53 -0
- package/src/markdoc/components/CodeWalkthrough/CodePanelToolbar.tsx +31 -0
- package/src/markdoc/components/CodeWalkthrough/CodeStep.tsx +149 -0
- package/src/markdoc/components/CodeWalkthrough/CodeToggle.tsx +63 -0
- package/src/markdoc/components/CodeWalkthrough/CodeWalkthrough.tsx +111 -0
- package/src/markdoc/components/CodeWalkthrough/Input.tsx +101 -0
- package/src/markdoc/components/CodeWalkthrough/variables.ts +16 -0
- package/src/markdoc/components/default.ts +5 -0
- package/src/markdoc/default.ts +8 -0
- package/src/markdoc/tags/code-step.ts +27 -0
- package/src/markdoc/tags/code-toggle.ts +37 -0
- package/src/markdoc/tags/code-walkthrough.ts +315 -0
- package/src/markdoc/tags/input.ts +35 -0
- package/src/plugin.js +2 -2
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { useContext, useCallback, useEffect, useState, useMemo } from 'react';
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
CodeWalkthroughFile,
|
|
5
|
+
CodeWalkthroughNode,
|
|
6
|
+
CodeWalkthroughConditionsObject,
|
|
7
|
+
} from '@redocly/config';
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
CodeWalkthroughControlsStateContext,
|
|
11
|
+
CodeWalkthroughStepsContext,
|
|
12
|
+
} from '@redocly/theme/core/contexts';
|
|
13
|
+
import { useThemeHooks } from '@redocly/theme/core/hooks';
|
|
14
|
+
|
|
15
|
+
const ACTIVE_FILE_MOCK = {
|
|
16
|
+
content: [],
|
|
17
|
+
path: '',
|
|
18
|
+
basename: '',
|
|
19
|
+
metadata: {},
|
|
20
|
+
language: '',
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export function useCodePanel(files: CodeWalkthroughFile[]) {
|
|
24
|
+
const { activeStep } = useContext(CodeWalkthroughStepsContext);
|
|
25
|
+
|
|
26
|
+
const { areConditionsMet, populateInputsWithValue } = useContext(
|
|
27
|
+
CodeWalkthroughControlsStateContext,
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
const { useCodeHighlight } = useThemeHooks();
|
|
31
|
+
const { highlight } = useCodeHighlight();
|
|
32
|
+
|
|
33
|
+
const findFileIndexByName = useCallback(
|
|
34
|
+
(name: string) => {
|
|
35
|
+
return files.findIndex((file) => file.path === name);
|
|
36
|
+
},
|
|
37
|
+
[files],
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const findFileIndexByStepId = useCallback(
|
|
41
|
+
(id: string) => files.findIndex((file) => file.metadata.steps.includes(id)),
|
|
42
|
+
[files],
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
const activeStepFileIndex = activeStep ? findFileIndexByStepId(activeStep) : 0;
|
|
46
|
+
const initialActiveFileIndex = activeStepFileIndex !== -1 ? activeStepFileIndex : 0;
|
|
47
|
+
|
|
48
|
+
const [activeFileIndex, setActiveFileIndex] = useState(initialActiveFileIndex);
|
|
49
|
+
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
setActiveFileIndex(initialActiveFileIndex);
|
|
52
|
+
}, [initialActiveFileIndex, activeStep, files]);
|
|
53
|
+
|
|
54
|
+
const handleTabSwitch = useCallback(
|
|
55
|
+
(name: string) => {
|
|
56
|
+
const index = findFileIndexByName(name);
|
|
57
|
+
|
|
58
|
+
if (index !== -1) {
|
|
59
|
+
setActiveFileIndex(index);
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
[findFileIndexByName],
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const activeFile =
|
|
66
|
+
files[activeFileIndex] ||
|
|
67
|
+
// Fallback to default. Needed when switching from language with more files to a language with less files
|
|
68
|
+
files[initialActiveFileIndex] ||
|
|
69
|
+
// Final fallback for dev mode when no files were added yet
|
|
70
|
+
ACTIVE_FILE_MOCK;
|
|
71
|
+
const highlightedCode = useMemo(() => {
|
|
72
|
+
const { highlightedLines, code, isWholeFileSelected } = getRenderableCode(
|
|
73
|
+
activeFile,
|
|
74
|
+
activeStep,
|
|
75
|
+
areConditionsMet,
|
|
76
|
+
populateInputsWithValue,
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
return highlight(code, activeFile.language, {
|
|
80
|
+
withLineNumbers: true,
|
|
81
|
+
// Shiki transformerMetaHighlight meta to highlight lines
|
|
82
|
+
// If the whole file is selected for a step, do not apply highlighting
|
|
83
|
+
highlight: isWholeFileSelected ? '' : `{${Array.from(highlightedLines).join(',')}}`,
|
|
84
|
+
customTransformer: {
|
|
85
|
+
// Add greyed-out class to lines that are not highlighted
|
|
86
|
+
line(hast, number) {
|
|
87
|
+
if (!highlightedLines.has(number)) {
|
|
88
|
+
this.addClassToHast(hast, 'greyed-out');
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
}, [activeFile, activeStep, highlight, areConditionsMet, populateInputsWithValue]);
|
|
94
|
+
|
|
95
|
+
return { activeFile, handleTabSwitch, highlightedCode } as const;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function getRenderableCode(
|
|
99
|
+
activeFile: CodeWalkthroughFile,
|
|
100
|
+
activeStep: string | null,
|
|
101
|
+
areConditionsMet: (conditions: CodeWalkthroughConditionsObject) => boolean,
|
|
102
|
+
populateInputsWithValue: (node: string) => string,
|
|
103
|
+
): {
|
|
104
|
+
highlightedLines: Set<number>;
|
|
105
|
+
code: string;
|
|
106
|
+
isWholeFileSelected: boolean;
|
|
107
|
+
} {
|
|
108
|
+
const codeLines = activeFile.content.flatMap((node) =>
|
|
109
|
+
getCodeLinesFromNode(node, activeStep, areConditionsMet, populateInputsWithValue),
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const codeLinesContent: string[] = [];
|
|
113
|
+
const highlightedLines = new Set<number>();
|
|
114
|
+
|
|
115
|
+
codeLines.forEach(({ lineContent, highlighted }, idx) => {
|
|
116
|
+
codeLinesContent.push(lineContent);
|
|
117
|
+
|
|
118
|
+
if (highlighted) {
|
|
119
|
+
highlightedLines.add(idx + 1);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
highlightedLines,
|
|
125
|
+
code: codeLinesContent.join('\n'),
|
|
126
|
+
isWholeFileSelected: highlightedLines.size === codeLinesContent.length,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Convert code node to code line objects with content to render and their highlighted status
|
|
132
|
+
*/
|
|
133
|
+
function getCodeLinesFromNode(
|
|
134
|
+
node: CodeWalkthroughNode,
|
|
135
|
+
activeStep: string | null,
|
|
136
|
+
areConditionsMet: (conditions: CodeWalkthroughConditionsObject) => boolean,
|
|
137
|
+
populateInputsWithValue: (node: string) => string,
|
|
138
|
+
parentHighlighted: boolean = false,
|
|
139
|
+
): { lineContent: string; highlighted: boolean }[] {
|
|
140
|
+
if (typeof node === 'string') {
|
|
141
|
+
const replacedNode = populateInputsWithValue(node);
|
|
142
|
+
|
|
143
|
+
return [{ lineContent: replacedNode, highlighted: parentHighlighted }];
|
|
144
|
+
} else {
|
|
145
|
+
const shouldRenderChunk = areConditionsMet(node.condition);
|
|
146
|
+
|
|
147
|
+
const isHighlighted =
|
|
148
|
+
activeStep != null &&
|
|
149
|
+
node.condition.steps.length > 0 &&
|
|
150
|
+
node.condition.steps.includes(activeStep);
|
|
151
|
+
|
|
152
|
+
return shouldRenderChunk
|
|
153
|
+
? node.children.flatMap((child) =>
|
|
154
|
+
getCodeLinesFromNode(
|
|
155
|
+
child,
|
|
156
|
+
activeStep,
|
|
157
|
+
areConditionsMet,
|
|
158
|
+
populateInputsWithValue,
|
|
159
|
+
isHighlighted,
|
|
160
|
+
),
|
|
161
|
+
)
|
|
162
|
+
: [];
|
|
163
|
+
}
|
|
164
|
+
}
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import { useState, useEffect, useMemo } from 'react';
|
|
2
|
+
import { useLocation, useNavigate } from 'react-router-dom';
|
|
3
|
+
|
|
4
|
+
import type {
|
|
5
|
+
CodeWalkthroughFile,
|
|
6
|
+
CodeWalkthroughFilter,
|
|
7
|
+
InputsMarkdocAttr,
|
|
8
|
+
TogglesMarkdocAttr,
|
|
9
|
+
CodeWalkthroughControls,
|
|
10
|
+
TogglesState,
|
|
11
|
+
InputsState,
|
|
12
|
+
FiltersState,
|
|
13
|
+
CodeWalkthroughFilterItem,
|
|
14
|
+
ControlState,
|
|
15
|
+
ControlTypeValue,
|
|
16
|
+
ControlType,
|
|
17
|
+
CodeWalkthroughConditionsObject,
|
|
18
|
+
} from '@redocly/config';
|
|
19
|
+
|
|
20
|
+
import {
|
|
21
|
+
downloadCodeWalkthrough,
|
|
22
|
+
matchCodeWalkthroughConditions,
|
|
23
|
+
getCodeWalkthroughFileText,
|
|
24
|
+
replaceInputsWithValue,
|
|
25
|
+
} from '@redocly/theme/core/utils';
|
|
26
|
+
|
|
27
|
+
export type getState<T extends ControlType> = (id: string) => Omit<ControlState<T>, 'type'> | null;
|
|
28
|
+
export type changeState<T extends ControlType> = (id: string, value: ControlTypeValue<T>) => void;
|
|
29
|
+
export type ActiveFilter = {
|
|
30
|
+
id: string;
|
|
31
|
+
label?: string;
|
|
32
|
+
items: CodeWalkthroughFilterItem[];
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export type WalkthroughControlsState = {
|
|
36
|
+
activeFilters: ActiveFilter[];
|
|
37
|
+
/* Toggle State Controls */
|
|
38
|
+
getToggleState: getState<'toggle'>;
|
|
39
|
+
changeToggleState: changeState<'toggle'>;
|
|
40
|
+
/* Input State Controls */
|
|
41
|
+
getInputState: getState<'input'>;
|
|
42
|
+
changeInputState: changeState<'input'>;
|
|
43
|
+
/* Filter State Controls */
|
|
44
|
+
getFilterState: getState<'filter'>;
|
|
45
|
+
changeFilterState: changeState<'filter'>;
|
|
46
|
+
/* Utility */
|
|
47
|
+
areConditionsMet: (conditions: CodeWalkthroughConditionsObject) => boolean;
|
|
48
|
+
handleDownloadCode: (files: CodeWalkthroughFile[]) => Promise<void>;
|
|
49
|
+
getFileText: (file: CodeWalkthroughFile) => string;
|
|
50
|
+
populateInputsWithValue: (node: string) => string;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const defaultControlsValues: CodeWalkthroughControls = {
|
|
54
|
+
input: '',
|
|
55
|
+
toggle: false,
|
|
56
|
+
filter: '',
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export function useCodeWalkthroughControls(
|
|
60
|
+
filters: Record<string, CodeWalkthroughFilter>,
|
|
61
|
+
inputs: InputsMarkdocAttr,
|
|
62
|
+
toggles: TogglesMarkdocAttr,
|
|
63
|
+
): WalkthroughControlsState {
|
|
64
|
+
const location = useLocation();
|
|
65
|
+
const navigate = useNavigate();
|
|
66
|
+
const searchParams = useMemo(() => new URLSearchParams(location.search), [location.search]);
|
|
67
|
+
|
|
68
|
+
const [togglesState, setTogglesState] = useState(() => {
|
|
69
|
+
const initialState: TogglesState = {};
|
|
70
|
+
for (const [id, toggle] of Object.entries(toggles)) {
|
|
71
|
+
initialState[id] = {
|
|
72
|
+
...toggle,
|
|
73
|
+
render: true,
|
|
74
|
+
type: 'toggle',
|
|
75
|
+
value: searchParams.get(id) === 'true',
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
return initialState;
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const changeToggleState = (toggleId: string, checked: boolean) => {
|
|
82
|
+
setTogglesState((prev) => {
|
|
83
|
+
const toggle = prev[toggleId];
|
|
84
|
+
if (toggle) {
|
|
85
|
+
return {
|
|
86
|
+
...prev,
|
|
87
|
+
[toggleId]: {
|
|
88
|
+
...toggle,
|
|
89
|
+
value: checked,
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
} else {
|
|
93
|
+
return prev;
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const getToggleState = (toggleId: string) => {
|
|
99
|
+
const toggleState = togglesState[toggleId];
|
|
100
|
+
|
|
101
|
+
if (toggleState) {
|
|
102
|
+
return {
|
|
103
|
+
render: toggleState.render,
|
|
104
|
+
value: toggleState.value,
|
|
105
|
+
};
|
|
106
|
+
} else {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const [inputsState, setInputsState] = useState(() => {
|
|
112
|
+
const initialState: InputsState = {};
|
|
113
|
+
|
|
114
|
+
for (const [id, input] of Object.entries(inputs)) {
|
|
115
|
+
initialState[id] = {
|
|
116
|
+
...input,
|
|
117
|
+
render: true,
|
|
118
|
+
type: 'input',
|
|
119
|
+
value: searchParams.get(id) || input.value,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
return initialState;
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
const changeInputState = (inputId: string, value: string) => {
|
|
126
|
+
setInputsState((prev) => {
|
|
127
|
+
const input = prev[inputId];
|
|
128
|
+
if (input) {
|
|
129
|
+
return {
|
|
130
|
+
...prev,
|
|
131
|
+
[inputId]: {
|
|
132
|
+
...input,
|
|
133
|
+
value,
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
} else {
|
|
137
|
+
return prev;
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const getInputState = (inputId: string) => {
|
|
143
|
+
const inputState = inputsState[inputId];
|
|
144
|
+
|
|
145
|
+
if (inputState) {
|
|
146
|
+
return {
|
|
147
|
+
render: inputState.render,
|
|
148
|
+
value: inputState.value,
|
|
149
|
+
};
|
|
150
|
+
} else {
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const [filtersState, setFiltersState] = useState(() => {
|
|
156
|
+
const initialState: FiltersState = {};
|
|
157
|
+
|
|
158
|
+
for (const [id, filter] of Object.entries(filters)) {
|
|
159
|
+
initialState[id] = {
|
|
160
|
+
...filter,
|
|
161
|
+
render: true,
|
|
162
|
+
type: 'filter',
|
|
163
|
+
value: searchParams.get(id) || filter?.items?.[0]?.value || '',
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return initialState;
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const changeFilterState = (filterId: string, value: string) => {
|
|
171
|
+
setFiltersState((prev) => {
|
|
172
|
+
const filter = prev[filterId];
|
|
173
|
+
if (filter) {
|
|
174
|
+
return {
|
|
175
|
+
...prev,
|
|
176
|
+
[filterId]: {
|
|
177
|
+
...filter,
|
|
178
|
+
value,
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
} else {
|
|
182
|
+
return prev;
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const getFilterState = (filterId: string) => {
|
|
188
|
+
const filterState = filtersState[filterId];
|
|
189
|
+
|
|
190
|
+
if (filterState) {
|
|
191
|
+
return {
|
|
192
|
+
render: filterState.render,
|
|
193
|
+
value: filterState.value,
|
|
194
|
+
};
|
|
195
|
+
} else {
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
const state = {
|
|
201
|
+
...filtersState,
|
|
202
|
+
...togglesState,
|
|
203
|
+
...inputsState,
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
const walkthroughContext = useMemo(() => {
|
|
207
|
+
const areConditionsMet = (conditions: CodeWalkthroughConditionsObject) =>
|
|
208
|
+
matchCodeWalkthroughConditions(conditions, state);
|
|
209
|
+
|
|
210
|
+
for (const [id, element] of Object.entries(state)) {
|
|
211
|
+
if (element && !areConditionsMet(element)) {
|
|
212
|
+
state[id].render = false;
|
|
213
|
+
state[id].value = defaultControlsValues[element.type];
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const activeFilters = [];
|
|
218
|
+
for (const [id, filter] of Object.entries(filters)) {
|
|
219
|
+
if (!filtersState[id].render) {
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// code-walk-todo: need to check if we have a default fallback
|
|
224
|
+
const items = Array.isArray(filter?.items) ? filter.items : [];
|
|
225
|
+
|
|
226
|
+
const activeItems = items.filter((item) => areConditionsMet(item));
|
|
227
|
+
|
|
228
|
+
if (activeItems.length === 0) {
|
|
229
|
+
filtersState[id].render = false;
|
|
230
|
+
filtersState[id].value = defaultControlsValues['filter'];
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const currentValue = filtersState[id].value;
|
|
235
|
+
if (currentValue) {
|
|
236
|
+
const isValueInActiveItems =
|
|
237
|
+
activeItems.findIndex(({ value }) => value === currentValue) !== -1;
|
|
238
|
+
filtersState[id].value = isValueInActiveItems ? currentValue : activeItems[0].value;
|
|
239
|
+
} else {
|
|
240
|
+
filtersState[id].value = activeItems[0].value;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
activeFilters.push({
|
|
244
|
+
id,
|
|
245
|
+
label: filter.label,
|
|
246
|
+
items: activeItems,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const handleDownloadCode = (files: CodeWalkthroughFile[]) =>
|
|
251
|
+
downloadCodeWalkthrough(files, state, inputsState);
|
|
252
|
+
|
|
253
|
+
const getFileText = (file: CodeWalkthroughFile) =>
|
|
254
|
+
getCodeWalkthroughFileText(file, state, inputsState);
|
|
255
|
+
|
|
256
|
+
const populateInputsWithValue = (node: string) => replaceInputsWithValue(node, inputsState);
|
|
257
|
+
|
|
258
|
+
return {
|
|
259
|
+
activeFilters,
|
|
260
|
+
areConditionsMet,
|
|
261
|
+
handleDownloadCode,
|
|
262
|
+
getFileText,
|
|
263
|
+
populateInputsWithValue,
|
|
264
|
+
};
|
|
265
|
+
// Ignore state in dependency array as it's simply a combination of toggles, filters and inputs.
|
|
266
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
267
|
+
}, [filters, filtersState, togglesState, inputsState]);
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Update the URL search params with the current state of the filters and inputs
|
|
271
|
+
*/
|
|
272
|
+
useEffect(() => {
|
|
273
|
+
const newSearchParams = new URLSearchParams(Array.from(searchParams.entries()));
|
|
274
|
+
|
|
275
|
+
for (const [id, { value }] of Object.entries(state)) {
|
|
276
|
+
if (value) {
|
|
277
|
+
newSearchParams.set(id, value.toString());
|
|
278
|
+
} else {
|
|
279
|
+
newSearchParams.delete(id);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const newSearch = newSearchParams.toString();
|
|
284
|
+
if (newSearch === location.search.substring(1)) return;
|
|
285
|
+
navigate({ search: newSearch });
|
|
286
|
+
// Ignore searchParams in dependency array to avoid infinite re-renders
|
|
287
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
288
|
+
}, [filters, filtersState, togglesState, inputsState, navigate, location, state]);
|
|
289
|
+
|
|
290
|
+
return {
|
|
291
|
+
getInputState,
|
|
292
|
+
changeInputState,
|
|
293
|
+
|
|
294
|
+
getToggleState,
|
|
295
|
+
changeToggleState,
|
|
296
|
+
|
|
297
|
+
getFilterState,
|
|
298
|
+
changeFilterState,
|
|
299
|
+
|
|
300
|
+
...walkthroughContext,
|
|
301
|
+
};
|
|
302
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState, useMemo } from 'react';
|
|
2
|
+
import { useLocation, useNavigate } from 'react-router-dom';
|
|
3
|
+
|
|
4
|
+
import type { CodeWalkthroughStepAttr } from '@redocly/config';
|
|
5
|
+
|
|
6
|
+
import { getAdjacentValues } from '@redocly/theme/core/utils';
|
|
7
|
+
import { ACTIVE_STEP_QUERY_PARAM } from '@redocly/theme/core/constants';
|
|
8
|
+
|
|
9
|
+
type ActiveStep = string | null;
|
|
10
|
+
type CodeWalkthroughStep = CodeWalkthroughStepAttr & {
|
|
11
|
+
active?: boolean;
|
|
12
|
+
compRef?: HTMLElement;
|
|
13
|
+
};
|
|
14
|
+
export type WalkthroughStepsState = {
|
|
15
|
+
activeStep: ActiveStep;
|
|
16
|
+
setActiveStep: (stepId: ActiveStep) => void;
|
|
17
|
+
register: (element: HTMLElement) => void;
|
|
18
|
+
unregister: (element: HTMLElement) => void;
|
|
19
|
+
lockObserver?: React.MutableRefObject<boolean>;
|
|
20
|
+
filtersElementRef?: React.RefObject<HTMLDivElement>;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export function useCodeWalkthroughSteps(steps: CodeWalkthroughStep[]): WalkthroughStepsState {
|
|
24
|
+
const location = useLocation();
|
|
25
|
+
const navigate = useNavigate();
|
|
26
|
+
const searchParams = useMemo(() => new URLSearchParams(location.search), [location.search]);
|
|
27
|
+
|
|
28
|
+
const observerRef = useRef<IntersectionObserver | null>(null);
|
|
29
|
+
const filtersElementRef = useRef<HTMLDivElement>(null);
|
|
30
|
+
const lockObserver = useRef<boolean>(false);
|
|
31
|
+
// Track observed elements in case new observer needs to be created
|
|
32
|
+
const observedElementsRef = useRef(new Set<HTMLElement>());
|
|
33
|
+
|
|
34
|
+
const [activeStep, setActiveStep] = useState<ActiveStep>(
|
|
35
|
+
searchParams.get(ACTIVE_STEP_QUERY_PARAM),
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const register = useCallback(
|
|
39
|
+
(element: HTMLElement) => {
|
|
40
|
+
// for some reason, the observer is not ready immediately
|
|
41
|
+
setTimeout(() => {
|
|
42
|
+
if (observerRef.current) {
|
|
43
|
+
const stepKey = Number(element.dataset.stepKey);
|
|
44
|
+
if (Number.isInteger(stepKey) && stepKey >= 0) {
|
|
45
|
+
steps[stepKey].compRef = element;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
observerRef.current.observe(element);
|
|
49
|
+
observedElementsRef.current.add(element);
|
|
50
|
+
}
|
|
51
|
+
}, 10);
|
|
52
|
+
},
|
|
53
|
+
[steps],
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
const unregister = useCallback(
|
|
57
|
+
(element: HTMLElement) => {
|
|
58
|
+
if (observerRef.current) {
|
|
59
|
+
const stepKey = Number(element.dataset.stepKey);
|
|
60
|
+
if (Number.isInteger(stepKey) && stepKey >= 0) {
|
|
61
|
+
steps[stepKey].compRef = undefined;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
observerRef.current.unobserve(element);
|
|
65
|
+
observedElementsRef.current.delete(element);
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
[steps],
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
const observerCallback = useCallback(
|
|
72
|
+
(entries: IntersectionObserverEntry[]) => {
|
|
73
|
+
if (lockObserver.current) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const stepsEntries = [];
|
|
78
|
+
for (const entry of entries) {
|
|
79
|
+
const { target } = entry;
|
|
80
|
+
const stepKey = Number((target as HTMLElement)?.dataset?.stepKey);
|
|
81
|
+
const stepActive = (target as HTMLElement)?.dataset?.stepActive === 'true';
|
|
82
|
+
|
|
83
|
+
if (!Number.isInteger(stepKey) || stepKey < 0) {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const step = steps[stepKey];
|
|
88
|
+
step.active = stepActive;
|
|
89
|
+
stepsEntries.push(entry);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
for (const stepEntry of stepsEntries) {
|
|
93
|
+
const { target, intersectionRatio, boundingClientRect, rootBounds, isIntersecting } =
|
|
94
|
+
stepEntry;
|
|
95
|
+
const stepKey = Number((target as HTMLElement)?.dataset?.stepKey);
|
|
96
|
+
const step = steps[stepKey];
|
|
97
|
+
|
|
98
|
+
const renderedSteps = steps.filter((step) => Boolean(step.compRef));
|
|
99
|
+
const stepIndex = renderedSteps.findIndex(
|
|
100
|
+
(renderedStep) => renderedStep.stepKey === step.stepKey,
|
|
101
|
+
);
|
|
102
|
+
const { next } = getAdjacentValues(renderedSteps, stepIndex);
|
|
103
|
+
|
|
104
|
+
const intersectionAtTop =
|
|
105
|
+
rootBounds?.bottom !== undefined && boundingClientRect.top < rootBounds.top;
|
|
106
|
+
const stepGoesIn = isIntersecting;
|
|
107
|
+
|
|
108
|
+
if (intersectionRatio < 1 && intersectionRatio !== 0 && intersectionAtTop) {
|
|
109
|
+
let newStep: string | null = null;
|
|
110
|
+
|
|
111
|
+
if (stepGoesIn) {
|
|
112
|
+
newStep = step.id;
|
|
113
|
+
} else if (next) {
|
|
114
|
+
newStep = next.id;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (newStep !== activeStep) {
|
|
118
|
+
setActiveStep(newStep);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
[steps, activeStep],
|
|
126
|
+
);
|
|
127
|
+
useEffect(() => {
|
|
128
|
+
const filtersElementHeight = filtersElementRef.current?.clientHeight || 0;
|
|
129
|
+
const navbarHeight = document.querySelector('nav')?.clientHeight || 0;
|
|
130
|
+
const newObserver = new IntersectionObserver(observerCallback, {
|
|
131
|
+
threshold: [0.8],
|
|
132
|
+
rootMargin: `-${filtersElementHeight + navbarHeight}px 0px 0px 0px`,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
for (const observedElement of observedElementsRef.current) {
|
|
136
|
+
newObserver.observe(observedElement);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Unobserve all from the old observer
|
|
140
|
+
observerRef.current?.disconnect();
|
|
141
|
+
observerRef.current = newObserver;
|
|
142
|
+
}, [observerCallback]);
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Update the URL search params with the current state of the filters and inputs
|
|
146
|
+
*/
|
|
147
|
+
useEffect(() => {
|
|
148
|
+
const newSearchParams = new URLSearchParams(Array.from(searchParams.entries()));
|
|
149
|
+
|
|
150
|
+
if (activeStep) {
|
|
151
|
+
newSearchParams.set(ACTIVE_STEP_QUERY_PARAM, activeStep);
|
|
152
|
+
} else {
|
|
153
|
+
newSearchParams.delete(ACTIVE_STEP_QUERY_PARAM);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const newSearch = newSearchParams.toString();
|
|
157
|
+
if (newSearch === location.search.substring(1)) return;
|
|
158
|
+
navigate({ search: newSearch });
|
|
159
|
+
|
|
160
|
+
// Ignore searchParams in dependency array to avoid infinite re-renders
|
|
161
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
162
|
+
}, [activeStep, navigate, location]);
|
|
163
|
+
|
|
164
|
+
return { register, unregister, lockObserver, filtersElementRef, activeStep, setActiveStep };
|
|
165
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
CodeWalkthroughFileset,
|
|
3
|
+
CodeWalkthroughFile,
|
|
4
|
+
CodeWalkthroughStepAttr,
|
|
5
|
+
CodeWalkthroughFilter,
|
|
6
|
+
InputsMarkdocAttr,
|
|
7
|
+
TogglesMarkdocAttr,
|
|
8
|
+
} from '@redocly/config';
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
useCodeWalkthroughSteps,
|
|
12
|
+
useCodeWalkthroughControls,
|
|
13
|
+
type WalkthroughControlsState,
|
|
14
|
+
type WalkthroughStepsState,
|
|
15
|
+
} from '@redocly/theme/core/hooks';
|
|
16
|
+
|
|
17
|
+
export type WalkthroughState = {
|
|
18
|
+
stepsState: WalkthroughStepsState;
|
|
19
|
+
controlsState: WalkthroughControlsState;
|
|
20
|
+
downloadAssociatedFiles: CodeWalkthroughFile[];
|
|
21
|
+
files: CodeWalkthroughFile[];
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export function useCodeWalkthrough(
|
|
25
|
+
steps: CodeWalkthroughStepAttr[],
|
|
26
|
+
attributes: {
|
|
27
|
+
filters: Record<string, CodeWalkthroughFilter>;
|
|
28
|
+
filesets: CodeWalkthroughFileset[];
|
|
29
|
+
inputs: InputsMarkdocAttr;
|
|
30
|
+
toggles: TogglesMarkdocAttr;
|
|
31
|
+
},
|
|
32
|
+
): WalkthroughState {
|
|
33
|
+
const { filters, filesets, inputs, toggles } = attributes;
|
|
34
|
+
const stepsState = useCodeWalkthroughSteps(steps);
|
|
35
|
+
const controlsState = useCodeWalkthroughControls(filters, inputs, toggles);
|
|
36
|
+
|
|
37
|
+
const files: CodeWalkthroughFile[] = filesets
|
|
38
|
+
.filter((fileset) => controlsState.areConditionsMet(fileset))
|
|
39
|
+
.flatMap((fileset) => fileset.files || []);
|
|
40
|
+
|
|
41
|
+
const downloadAssociatedFiles: CodeWalkthroughFile[] = filesets
|
|
42
|
+
.filter((fileset) => controlsState.areConditionsMet(fileset))
|
|
43
|
+
.flatMap((fileset) => fileset.downloadAssociatedFiles || []);
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
stepsState,
|
|
47
|
+
controlsState,
|
|
48
|
+
downloadAssociatedFiles,
|
|
49
|
+
files,
|
|
50
|
+
};
|
|
51
|
+
}
|
package/src/core/hooks/index.ts
CHANGED
|
@@ -28,3 +28,7 @@ export * from '@redocly/theme/core/hooks/search/use-search-dialog';
|
|
|
28
28
|
export * from '@redocly/theme/core/hooks/search/use-search-filter';
|
|
29
29
|
export * from '@redocly/theme/core/hooks/use-controlled-state';
|
|
30
30
|
export * from '@redocly/theme/core/hooks/use-codeblock-tabs-controls';
|
|
31
|
+
export * from '@redocly/theme/core/hooks/code-walkthrough/use-code-walkthrough';
|
|
32
|
+
export * from '@redocly/theme/core/hooks/code-walkthrough/use-code-walkthrough-steps';
|
|
33
|
+
export * from '@redocly/theme/core/hooks/code-walkthrough/use-code-walkthrough-controls';
|
|
34
|
+
export * from '@redocly/theme/core/hooks/code-walkthrough/use-code-panel';
|