@redocly/theme 0.48.1 → 0.49.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/DatePicker/variables.js +1 -1
- package/lib/core/contexts/CodeWalkthrough/CodeWalkthroughControlsContext.js +2 -6
- package/lib/core/hooks/code-walkthrough/use-code-walkthrough-controls.d.ts +6 -9
- package/lib/core/hooks/code-walkthrough/use-code-walkthrough-controls.js +57 -96
- package/lib/core/styles/global.js +18 -0
- package/lib/core/types/search.d.ts +1 -0
- package/lib/core/utils/download-code-walkthrough.d.ts +4 -2
- package/lib/core/utils/get-code-walkthrough-file-text.d.ts +4 -2
- package/lib/ext/configure.d.ts +24 -0
- package/lib/ext/configure.js +18 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/markdoc/components/CodeWalkthrough/CodeContainer.js +24 -3
- package/lib/markdoc/components/CodeWalkthrough/CodeFilters.d.ts +5 -4
- package/lib/markdoc/components/CodeWalkthrough/CodeStep.js +1 -1
- package/lib/markdoc/components/CodeWalkthrough/CodeToggle.js +4 -4
- package/lib/markdoc/components/CodeWalkthrough/CodeWalkthrough.js +2 -2
- package/lib/markdoc/components/CodeWalkthrough/Input.js +3 -3
- package/package.json +3 -2
- package/src/components/DatePicker/variables.ts +1 -1
- package/src/core/contexts/CodeWalkthrough/CodeWalkthroughControlsContext.tsx +2 -8
- package/src/core/hooks/code-walkthrough/use-code-walkthrough-controls.ts +81 -139
- package/src/core/styles/global.ts +18 -0
- package/src/core/types/search.ts +1 -0
- package/src/core/utils/download-code-walkthrough.ts +2 -2
- package/src/core/utils/get-code-walkthrough-file-text.ts +3 -3
- package/src/ext/configure.ts +39 -0
- package/src/index.ts +1 -0
- package/src/markdoc/components/CodeWalkthrough/CodeContainer.tsx +27 -3
- package/src/markdoc/components/CodeWalkthrough/CodeFilters.tsx +2 -1
- package/src/markdoc/components/CodeWalkthrough/CodeStep.tsx +1 -1
- package/src/markdoc/components/CodeWalkthrough/CodeToggle.tsx +4 -4
- package/src/markdoc/components/CodeWalkthrough/CodeWalkthrough.tsx +7 -4
- package/src/markdoc/components/CodeWalkthrough/Input.tsx +4 -4
|
@@ -6,7 +6,7 @@ exports.datePicker = (0, styled_components_1.css) `
|
|
|
6
6
|
--date-picker-nav-color: #000000;
|
|
7
7
|
--date-picker-tile-bg-color: var(--color-blue-2);
|
|
8
8
|
--date-picker-tile-color: var(--text-color-primary);
|
|
9
|
-
--date-picker-tile-bg-color-hover: var(--color-blue-3)
|
|
9
|
+
--date-picker-tile-bg-color-hover: var(--color-blue-3);
|
|
10
10
|
--date-picker-tile-color-hover: var(--text-color-primary);
|
|
11
11
|
--date-picker-input-width: var(--spacing-md);
|
|
12
12
|
--date-picker-invalid-input-bg-color: var(--bg-color-raised);
|
|
@@ -4,12 +4,8 @@ exports.CodeWalkthroughControlsStateContext = void 0;
|
|
|
4
4
|
const react_1 = require("react");
|
|
5
5
|
exports.CodeWalkthroughControlsStateContext = (0, react_1.createContext)({
|
|
6
6
|
activeFilters: [],
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
getInputState: () => null,
|
|
10
|
-
changeInputState: () => { },
|
|
11
|
-
getFilterState: () => null,
|
|
12
|
-
changeFilterState: () => { },
|
|
7
|
+
getControlState: () => null,
|
|
8
|
+
changeControlState: () => { },
|
|
13
9
|
getFileText: () => '',
|
|
14
10
|
areConditionsMet: () => false,
|
|
15
11
|
handleDownloadCode: () => Promise.resolve(),
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import type { CodeWalkthroughFile, CodeWalkthroughFilter, InputsMarkdocAttr, TogglesMarkdocAttr, CodeWalkthroughFilterItem,
|
|
2
|
-
export type getState<T extends ControlType> = (id: string) => Omit<ControlState<T>, 'type'> | null;
|
|
3
|
-
export type changeState<T extends ControlType> = (id: string, value: ControlTypeValue<T>) => void;
|
|
1
|
+
import type { CodeWalkthroughFile, CodeWalkthroughFilter, InputsMarkdocAttr, TogglesMarkdocAttr, CodeWalkthroughFilterItem, CodeWalkthroughConditionsObject } from '@redocly/config';
|
|
4
2
|
export type ActiveFilter = {
|
|
5
3
|
id: string;
|
|
6
4
|
label?: string;
|
|
@@ -8,12 +6,11 @@ export type ActiveFilter = {
|
|
|
8
6
|
};
|
|
9
7
|
export type WalkthroughControlsState = {
|
|
10
8
|
activeFilters: ActiveFilter[];
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
changeFilterState: changeState<'filter'>;
|
|
9
|
+
getControlState: (id: string) => {
|
|
10
|
+
value: string | boolean;
|
|
11
|
+
render: boolean;
|
|
12
|
+
} | null;
|
|
13
|
+
changeControlState: (id: string, value: string | boolean) => void;
|
|
17
14
|
areConditionsMet: (conditions: CodeWalkthroughConditionsObject) => boolean;
|
|
18
15
|
handleDownloadCode: (files: CodeWalkthroughFile[]) => Promise<void>;
|
|
19
16
|
getFileText: (file: CodeWalkthroughFile) => string;
|
|
@@ -13,128 +13,94 @@ function useCodeWalkthroughControls(filters, inputs, toggles, enableDeepLink) {
|
|
|
13
13
|
const location = (0, react_router_dom_1.useLocation)();
|
|
14
14
|
const navigate = (0, react_router_dom_1.useNavigate)();
|
|
15
15
|
const searchParams = (0, react_1.useMemo)(() => new URLSearchParams(location.search), [location.search]);
|
|
16
|
-
const [
|
|
16
|
+
const [controlsState, setControlsState] = (0, react_1.useState)(() => {
|
|
17
|
+
var _a, _b, _c, _d;
|
|
17
18
|
const initialState = {};
|
|
18
19
|
for (const [id, toggle] of Object.entries(toggles)) {
|
|
19
20
|
initialState[id] = Object.assign(Object.assign({}, toggle), { render: true, type: 'toggle', value: enableDeepLink ? searchParams.get(id) === 'true' : false });
|
|
20
21
|
}
|
|
21
|
-
return initialState;
|
|
22
|
-
});
|
|
23
|
-
const changeToggleState = (toggleId, checked) => {
|
|
24
|
-
setTogglesState((prev) => {
|
|
25
|
-
const toggle = prev[toggleId];
|
|
26
|
-
if (toggle) {
|
|
27
|
-
return Object.assign(Object.assign({}, prev), { [toggleId]: Object.assign(Object.assign({}, toggle), { value: checked }) });
|
|
28
|
-
}
|
|
29
|
-
else {
|
|
30
|
-
return prev;
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
};
|
|
34
|
-
const getToggleState = (toggleId) => {
|
|
35
|
-
const toggleState = togglesState[toggleId];
|
|
36
|
-
if (toggleState) {
|
|
37
|
-
return {
|
|
38
|
-
render: toggleState.render,
|
|
39
|
-
value: toggleState.value,
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
return null;
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
const [inputsState, setInputsState] = (0, react_1.useState)(() => {
|
|
47
|
-
var _a;
|
|
48
|
-
const initialState = {};
|
|
49
22
|
for (const [id, input] of Object.entries(inputs)) {
|
|
50
23
|
initialState[id] = Object.assign(Object.assign({}, input), { render: true, type: 'input', value: enableDeepLink ? ((_a = searchParams.get(id)) !== null && _a !== void 0 ? _a : input.value) : input.value });
|
|
51
24
|
}
|
|
52
|
-
return initialState;
|
|
53
|
-
});
|
|
54
|
-
const changeInputState = (inputId, value) => {
|
|
55
|
-
setInputsState((prev) => {
|
|
56
|
-
const input = prev[inputId];
|
|
57
|
-
if (input) {
|
|
58
|
-
return Object.assign(Object.assign({}, prev), { [inputId]: Object.assign(Object.assign({}, input), { value }) });
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
61
|
-
return prev;
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
};
|
|
65
|
-
const getInputState = (inputId) => {
|
|
66
|
-
const inputState = inputsState[inputId];
|
|
67
|
-
if (inputState) {
|
|
68
|
-
return {
|
|
69
|
-
render: inputState.render,
|
|
70
|
-
value: inputState.value,
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
return null;
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
const [filtersState, setFiltersState] = (0, react_1.useState)(() => {
|
|
78
|
-
var _a, _b, _c;
|
|
79
|
-
const initialState = {};
|
|
80
25
|
for (const [id, filter] of Object.entries(filters)) {
|
|
81
|
-
const defaultValue = ((
|
|
82
|
-
initialState[id] = Object.assign(Object.assign({}, filter), { render: true, type: 'filter', value: enableDeepLink ? ((
|
|
26
|
+
const defaultValue = ((_c = (_b = filter === null || filter === void 0 ? void 0 : filter.items) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.value) || '';
|
|
27
|
+
initialState[id] = Object.assign(Object.assign({}, filter), { render: true, type: 'filter', value: enableDeepLink ? ((_d = searchParams.get(id)) !== null && _d !== void 0 ? _d : defaultValue) : defaultValue });
|
|
83
28
|
}
|
|
84
29
|
return initialState;
|
|
85
30
|
});
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
const
|
|
89
|
-
if (
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
else {
|
|
31
|
+
const changeControlState = (id, value) => {
|
|
32
|
+
setControlsState((prev) => {
|
|
33
|
+
const control = prev[id];
|
|
34
|
+
if (!control) {
|
|
35
|
+
console.error(`Control with id "${id}" not found.`);
|
|
93
36
|
return prev;
|
|
94
37
|
}
|
|
38
|
+
switch (control.type) {
|
|
39
|
+
case 'input':
|
|
40
|
+
if (typeof value !== 'string') {
|
|
41
|
+
console.error(`Invalid value type for input "${id}". Input control type requires a string value.`);
|
|
42
|
+
return prev;
|
|
43
|
+
}
|
|
44
|
+
break;
|
|
45
|
+
case 'toggle':
|
|
46
|
+
if (typeof value !== 'boolean') {
|
|
47
|
+
console.error(`Invalid value type for toggle "${id}". Toggle control type requires a boolean value.`);
|
|
48
|
+
return prev;
|
|
49
|
+
}
|
|
50
|
+
break;
|
|
51
|
+
case 'filter':
|
|
52
|
+
if (typeof value !== 'string') {
|
|
53
|
+
console.error(`Invalid value type for filter "${id}". Filter control type requires a string value.`);
|
|
54
|
+
return prev;
|
|
55
|
+
}
|
|
56
|
+
break;
|
|
57
|
+
default:
|
|
58
|
+
console.error(`Invalid control type "${control === null || control === void 0 ? void 0 : control.type}" for control "${id}". Allowed types are "toggle", "input", or "filter".`);
|
|
59
|
+
return prev;
|
|
60
|
+
}
|
|
61
|
+
return Object.assign(Object.assign({}, prev), { [id]: Object.assign(Object.assign({}, control), { value }) });
|
|
95
62
|
});
|
|
96
63
|
};
|
|
97
|
-
const
|
|
98
|
-
const
|
|
99
|
-
if (
|
|
64
|
+
const getControlState = (id) => {
|
|
65
|
+
const controlState = controlsState[id];
|
|
66
|
+
if (controlState) {
|
|
100
67
|
return {
|
|
101
|
-
render:
|
|
102
|
-
value:
|
|
68
|
+
render: controlState.render,
|
|
69
|
+
value: controlState.value,
|
|
103
70
|
};
|
|
104
71
|
}
|
|
105
72
|
else {
|
|
106
73
|
return null;
|
|
107
74
|
}
|
|
108
75
|
};
|
|
109
|
-
const state = Object.assign(Object.assign(Object.assign({}, filtersState), togglesState), inputsState);
|
|
110
76
|
const walkthroughContext = (0, react_1.useMemo)(() => {
|
|
111
|
-
const areConditionsMet = (conditions) => (0, utils_1.matchCodeWalkthroughConditions)(conditions,
|
|
112
|
-
for (const [id,
|
|
113
|
-
if (
|
|
114
|
-
|
|
115
|
-
|
|
77
|
+
const areConditionsMet = (conditions) => (0, utils_1.matchCodeWalkthroughConditions)(conditions, controlsState);
|
|
78
|
+
for (const [id, control] of Object.entries(controlsState)) {
|
|
79
|
+
if (control && !areConditionsMet(control)) {
|
|
80
|
+
controlsState[id].render = false;
|
|
81
|
+
controlsState[id].value = defaultControlsValues[control.type];
|
|
116
82
|
}
|
|
117
83
|
}
|
|
118
84
|
const activeFilters = [];
|
|
119
85
|
for (const [id, filter] of Object.entries(filters)) {
|
|
120
|
-
if (!
|
|
86
|
+
if (!controlsState[id].render) {
|
|
121
87
|
continue;
|
|
122
88
|
}
|
|
123
89
|
// code-walk-todo: need to check if we have a default fallback
|
|
124
90
|
const items = Array.isArray(filter === null || filter === void 0 ? void 0 : filter.items) ? filter.items : [];
|
|
125
91
|
const activeItems = items.filter((item) => areConditionsMet(item));
|
|
126
92
|
if (activeItems.length === 0) {
|
|
127
|
-
|
|
128
|
-
|
|
93
|
+
controlsState[id].render = false;
|
|
94
|
+
controlsState[id].value = defaultControlsValues['filter'];
|
|
129
95
|
continue;
|
|
130
96
|
}
|
|
131
|
-
const currentValue =
|
|
97
|
+
const currentValue = controlsState[id].value;
|
|
132
98
|
if (currentValue) {
|
|
133
99
|
const isValueInActiveItems = activeItems.findIndex(({ value }) => value === currentValue) !== -1;
|
|
134
|
-
|
|
100
|
+
controlsState[id].value = isValueInActiveItems ? currentValue : activeItems[0].value;
|
|
135
101
|
}
|
|
136
102
|
else {
|
|
137
|
-
|
|
103
|
+
controlsState[id].value = activeItems[0].value;
|
|
138
104
|
}
|
|
139
105
|
activeFilters.push({
|
|
140
106
|
id,
|
|
@@ -142,8 +108,9 @@ function useCodeWalkthroughControls(filters, inputs, toggles, enableDeepLink) {
|
|
|
142
108
|
items: activeItems,
|
|
143
109
|
});
|
|
144
110
|
}
|
|
145
|
-
const
|
|
146
|
-
const
|
|
111
|
+
const inputsState = Object.fromEntries(Object.entries(controlsState).filter(([_, controlState]) => controlState.type === 'input'));
|
|
112
|
+
const handleDownloadCode = (files) => (0, utils_1.downloadCodeWalkthrough)(files, controlsState, inputsState);
|
|
113
|
+
const getFileText = (file) => (0, utils_1.getCodeWalkthroughFileText)(file, controlsState, inputsState);
|
|
147
114
|
const populateInputsWithValue = (node) => (0, utils_1.replaceInputsWithValue)(node, inputsState);
|
|
148
115
|
return {
|
|
149
116
|
activeFilters,
|
|
@@ -152,9 +119,7 @@ function useCodeWalkthroughControls(filters, inputs, toggles, enableDeepLink) {
|
|
|
152
119
|
getFileText,
|
|
153
120
|
populateInputsWithValue,
|
|
154
121
|
};
|
|
155
|
-
|
|
156
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
157
|
-
}, [filters, filtersState, togglesState, inputsState]);
|
|
122
|
+
}, [filters, controlsState]);
|
|
158
123
|
/**
|
|
159
124
|
* Update the URL search params with the current state of the filters and inputs
|
|
160
125
|
*/
|
|
@@ -163,7 +128,7 @@ function useCodeWalkthroughControls(filters, inputs, toggles, enableDeepLink) {
|
|
|
163
128
|
return;
|
|
164
129
|
}
|
|
165
130
|
const newSearchParams = new URLSearchParams(Array.from(searchParams.entries()));
|
|
166
|
-
for (const [id, { value }] of Object.entries(
|
|
131
|
+
for (const [id, { value }] of Object.entries(controlsState)) {
|
|
167
132
|
if (value) {
|
|
168
133
|
newSearchParams.set(id, value.toString());
|
|
169
134
|
}
|
|
@@ -177,12 +142,8 @@ function useCodeWalkthroughControls(filters, inputs, toggles, enableDeepLink) {
|
|
|
177
142
|
navigate({ search: newSearch });
|
|
178
143
|
// Ignore searchParams in dependency array to avoid infinite re-renders
|
|
179
144
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
180
|
-
}, [filters,
|
|
181
|
-
return Object.assign({
|
|
182
|
-
|
|
183
|
-
getToggleState,
|
|
184
|
-
changeToggleState,
|
|
185
|
-
getFilterState,
|
|
186
|
-
changeFilterState }, walkthroughContext);
|
|
145
|
+
}, [filters, controlsState, navigate, location]);
|
|
146
|
+
return Object.assign({ changeControlState,
|
|
147
|
+
getControlState }, walkthroughContext);
|
|
187
148
|
}
|
|
188
149
|
//# sourceMappingURL=use-code-walkthrough-controls.js.map
|
|
@@ -1007,6 +1007,24 @@ const error = (0, styled_components_1.css) `
|
|
|
1007
1007
|
--detailed-error-message-width: 100%;
|
|
1008
1008
|
--detailed-error-message-font-size: var(--font-size-base);
|
|
1009
1009
|
--detailed-error-message-font-family: var(--code-block-controls-font-family);
|
|
1010
|
+
|
|
1011
|
+
--compilation-error-description-padding: 0 0 var(--spacing-base);
|
|
1012
|
+
--compilation-error-codeframe-margin: var(--spacing-xs) 0 0 0;
|
|
1013
|
+
--compilation-error-codeframe-padding: var(--spacing-xs);
|
|
1014
|
+
--compilation-error-block-padding: var(--spacing-base);
|
|
1015
|
+
--compilation-error-block-margin: var(--spacing-xl) 0;
|
|
1016
|
+
--compilation-error-title-font-size: var(--font-size-xl);
|
|
1017
|
+
--compilation-error-header-font-size: var(--font-size-lg);
|
|
1018
|
+
--compilation-error-text-font-size: var(--font-size-base);
|
|
1019
|
+
--compilation-error-header-padding: var(--spacing-sm);
|
|
1020
|
+
--compilation-error-container-margin: var(--spacing-xxl) auto;
|
|
1021
|
+
--compilation-error-container-padding: var(--spacing-lg);
|
|
1022
|
+
--compilation-error-page-max-width: 800px;
|
|
1023
|
+
--compilation-error-font-family: var(--font-family-base);
|
|
1024
|
+
--compilation-error-fix-instruction-margin: var(--spacing-sm) 0 0 0;
|
|
1025
|
+
--compilation-error-highlighted-text-padding: 0 var(--spacing-xxs);
|
|
1026
|
+
--compilation-error-divider-margin: var(--spacing-base) 0;
|
|
1027
|
+
--compilation-error-file-header-margin: 0 0 var(--spacing-xs) 0;
|
|
1010
1028
|
`;
|
|
1011
1029
|
const modal = (0, styled_components_1.css) `
|
|
1012
1030
|
body:has(.scroll-lock) {
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import type { CodeWalkthroughFile
|
|
1
|
+
import type { CodeWalkthroughFile } from '@redocly/config';
|
|
2
2
|
export declare function downloadCodeWalkthrough(files: CodeWalkthroughFile[], state: Record<string, {
|
|
3
3
|
value: string | boolean;
|
|
4
|
-
}>, inputsState:
|
|
4
|
+
}>, inputsState: Record<string, {
|
|
5
|
+
value: string;
|
|
6
|
+
}>): Promise<void>;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import type { CodeWalkthroughFile
|
|
1
|
+
import type { CodeWalkthroughFile } from '@redocly/config';
|
|
2
2
|
export declare function getCodeWalkthroughFileText(file: CodeWalkthroughFile, state: Record<string, {
|
|
3
3
|
value: string | boolean;
|
|
4
|
-
}>, inputsState:
|
|
4
|
+
}>, inputsState: Record<string, {
|
|
5
|
+
value: string;
|
|
6
|
+
}>): string;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type SecurityDetails = {
|
|
2
|
+
password?: string;
|
|
3
|
+
username?: string;
|
|
4
|
+
token?: {
|
|
5
|
+
token_type?: string;
|
|
6
|
+
access_token: string;
|
|
7
|
+
};
|
|
8
|
+
client_id?: string;
|
|
9
|
+
client_secret?: string;
|
|
10
|
+
scopes?: string[];
|
|
11
|
+
};
|
|
12
|
+
export type ConfigureRequestValues = {
|
|
13
|
+
headers?: Record<string, string>;
|
|
14
|
+
body?: Record<string, any>;
|
|
15
|
+
query?: Record<string, string>;
|
|
16
|
+
path?: Record<string, string>;
|
|
17
|
+
cookie?: Record<string, string>;
|
|
18
|
+
security?: SecurityDetails;
|
|
19
|
+
};
|
|
20
|
+
type Configure = {
|
|
21
|
+
requestValues?: ConfigureRequestValues;
|
|
22
|
+
};
|
|
23
|
+
export declare function configure(): Configure;
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.configure = configure;
|
|
4
|
+
function configure() {
|
|
5
|
+
// const exampleRequestValues: ConfigureRequestValues = {
|
|
6
|
+
// body: {
|
|
7
|
+
// name: 'John Doe',
|
|
8
|
+
// },
|
|
9
|
+
// security: {
|
|
10
|
+
// token: {
|
|
11
|
+
// access_token: 'John Doe token',
|
|
12
|
+
// },
|
|
13
|
+
// },
|
|
14
|
+
// };
|
|
15
|
+
// return { requestValues: exampleRequestValues } satisfies Configure;
|
|
16
|
+
return {};
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=configure.js.map
|
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
|
@@ -29,6 +29,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
29
29
|
exports.markdoc = void 0;
|
|
30
30
|
__exportStar(require("./core"), exports);
|
|
31
31
|
__exportStar(require("./config"), exports);
|
|
32
|
+
__exportStar(require("./ext/configure"), exports);
|
|
32
33
|
/* Components */
|
|
33
34
|
__exportStar(require("./components/Button/Button"), exports);
|
|
34
35
|
__exportStar(require("./components/JsonViewer/JsonViewer"), exports);
|
|
@@ -36,9 +36,30 @@ function CodeContainer({ highlightedCode, toolbar, }) {
|
|
|
36
36
|
// useEffect executed before DOM is updated due to re-render called before "painting" phase.
|
|
37
37
|
setTimeout(() => {
|
|
38
38
|
if (compRef.current) {
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
const highlightedLines = Array.from(compRef.current.querySelectorAll('span.line.highlighted'));
|
|
40
|
+
const { bottom: panelBottom, top: panelTop } = compRef.current.getBoundingClientRect();
|
|
41
|
+
const linesBelow = [];
|
|
42
|
+
const linesAbove = [];
|
|
43
|
+
for (const highlightedLine of highlightedLines) {
|
|
44
|
+
const { bottom: lineBottom, top: lineTop } = highlightedLine.getBoundingClientRect();
|
|
45
|
+
const below = lineBottom > panelBottom;
|
|
46
|
+
const above = lineTop < panelTop;
|
|
47
|
+
if (below) {
|
|
48
|
+
linesBelow.push(highlightedLine);
|
|
49
|
+
}
|
|
50
|
+
else if (above) {
|
|
51
|
+
linesAbove.push(highlightedLine);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if ((linesBelow.length && linesAbove.length) || linesAbove.length) {
|
|
55
|
+
linesAbove[0].scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' });
|
|
56
|
+
}
|
|
57
|
+
else if (linesBelow.length) {
|
|
58
|
+
linesBelow[linesBelow.length - 1].scrollIntoView({
|
|
59
|
+
behavior: 'smooth',
|
|
60
|
+
block: 'nearest',
|
|
61
|
+
inline: 'start',
|
|
62
|
+
});
|
|
42
63
|
}
|
|
43
64
|
}
|
|
44
65
|
}, 200);
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { CodeWalkthroughFilter } from '@redocly/config';
|
|
3
|
+
export type GetFilterState = (groupId: string) => {
|
|
4
|
+
value: string;
|
|
5
|
+
render: boolean;
|
|
6
|
+
} | null;
|
|
3
7
|
export type CodeFilterProps = {
|
|
4
8
|
filters: CodeWalkthroughFilter[];
|
|
5
|
-
getFilterState:
|
|
6
|
-
value: string;
|
|
7
|
-
render: boolean;
|
|
8
|
-
} | null;
|
|
9
|
+
getFilterState: GetFilterState;
|
|
9
10
|
handleFilterSelect: (groupId: string, id: string) => void;
|
|
10
11
|
filtersElementRef?: React.RefObject<HTMLDivElement>;
|
|
11
12
|
};
|
|
@@ -41,7 +41,7 @@ function CodeStep({ id, heading, stepKey, when, unless, children, }) {
|
|
|
41
41
|
if (lockObserver) {
|
|
42
42
|
lockObserver.current = true;
|
|
43
43
|
if (compRef.current) {
|
|
44
|
-
compRef.current.scrollIntoView({ behavior: 'smooth', block: '
|
|
44
|
+
compRef.current.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
45
45
|
}
|
|
46
46
|
setActiveStep(id);
|
|
47
47
|
setTimeout(() => {
|
|
@@ -35,16 +35,16 @@ const CodeStep_1 = require("../../../markdoc/components/CodeWalkthrough/CodeStep
|
|
|
35
35
|
const Switch_1 = require("../../../components/Switch/Switch");
|
|
36
36
|
function CodeToggle(props) {
|
|
37
37
|
const { id, label, description, children } = props;
|
|
38
|
-
const {
|
|
39
|
-
const toggleState =
|
|
40
|
-
if (!(toggleState && toggleState.render)) {
|
|
38
|
+
const { getControlState, changeControlState } = (0, react_1.useContext)(contexts_1.CodeWalkthroughControlsStateContext);
|
|
39
|
+
const toggleState = getControlState(id);
|
|
40
|
+
if (!(toggleState && toggleState.render && typeof toggleState.value === 'boolean')) {
|
|
41
41
|
return null;
|
|
42
42
|
}
|
|
43
43
|
const checked = toggleState.value;
|
|
44
44
|
return (react_1.default.createElement(exports.ToggleWrapper, { "data-component-name": "Markdoc/CodeWalkthrough/CodeToggle" },
|
|
45
45
|
react_1.default.createElement(ToggleContentWrapper, null,
|
|
46
46
|
react_1.default.createElement(exports.ToggleSubtitle, null,
|
|
47
|
-
react_1.default.createElement(Switch_1.Switch, { value: checked, onChange: (newValue) =>
|
|
47
|
+
react_1.default.createElement(Switch_1.Switch, { value: checked, onChange: (newValue) => changeControlState(id, newValue) }),
|
|
48
48
|
react_1.default.createElement("div", null, label)),
|
|
49
49
|
description ? (react_1.default.createElement("div", null, description.map((paragraph, idx) => (react_1.default.createElement(react_1.default.Fragment, { key: idx }, paragraph))))) : null),
|
|
50
50
|
checked ? children : null));
|
|
@@ -51,13 +51,13 @@ function CodeWalkthrough(_a) {
|
|
|
51
51
|
// This is a workaround to prevent the observer from being recreated
|
|
52
52
|
const [initialSteps] = (0, react_1.useState)([...steps]);
|
|
53
53
|
const { controlsState, stepsState, files, downloadAssociatedFiles } = (0, hooks_1.useCodeWalkthrough)(initialSteps, attributes);
|
|
54
|
-
const { activeFilters,
|
|
54
|
+
const { activeFilters, getControlState, changeControlState } = controlsState;
|
|
55
55
|
const { filtersElementRef } = stepsState;
|
|
56
56
|
return (react_1.default.createElement(contexts_1.CodeWalkthroughStepsContext.Provider, { value: stepsState },
|
|
57
57
|
react_1.default.createElement(contexts_1.CodeWalkthroughControlsStateContext.Provider, { value: controlsState },
|
|
58
58
|
react_1.default.createElement(CodeWalkthroughWrapper, { className: "code-walkthrough", "data-component-name": "Markdoc/CodeWalkthrough/CodeWalkthrough" },
|
|
59
59
|
react_1.default.createElement(DocsPanel, null,
|
|
60
|
-
react_1.default.createElement(CodeFilters_1.CodeFilters, { filters: activeFilters, getFilterState:
|
|
60
|
+
react_1.default.createElement(CodeFilters_1.CodeFilters, { filters: activeFilters, getFilterState: getControlState, handleFilterSelect: changeControlState, filtersElementRef: filtersElementRef }),
|
|
61
61
|
react_1.default.createElement(ContentWrapper, null, children)),
|
|
62
62
|
react_1.default.createElement(CodePanel_1.CodePanel, { files: files, downloadAssociatedFiles: downloadAssociatedFiles, preview: preview })))));
|
|
63
63
|
}
|
|
@@ -34,12 +34,12 @@ const contexts_1 = require("../../../core/contexts");
|
|
|
34
34
|
const DEBOUNCE_TIME = 500;
|
|
35
35
|
function Input(props) {
|
|
36
36
|
const { id, label, placeholder } = props;
|
|
37
|
-
const {
|
|
38
|
-
const inputState =
|
|
37
|
+
const { getControlState, changeControlState } = (0, react_1.useContext)(contexts_1.CodeWalkthroughControlsStateContext);
|
|
38
|
+
const inputState = getControlState(id);
|
|
39
39
|
const [value, setValue] = (0, react_1.useState)((inputState === null || inputState === void 0 ? void 0 : inputState.value) || '');
|
|
40
40
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
41
41
|
const debouncedSave = (0, react_1.useCallback)((0, lodash_debounce_1.default)((id, value) => {
|
|
42
|
-
|
|
42
|
+
changeControlState(id, value);
|
|
43
43
|
}, DEBOUNCE_TIME), []);
|
|
44
44
|
if (!(inputState === null || inputState === void 0 ? void 0 : inputState.render)) {
|
|
45
45
|
return null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@redocly/theme",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.49.0",
|
|
4
4
|
"description": "Shared UI components lib",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"theme",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"./src/": "./src/",
|
|
20
20
|
"./core/*": "./lib/core/*/index.js",
|
|
21
21
|
"./core/templates/*": "./lib/core/templates/*.js",
|
|
22
|
+
"./ext/*": "./lib/ext/*.js",
|
|
22
23
|
"./components/*": "./lib/components/*.js",
|
|
23
24
|
"./layouts/*": "./lib/layouts/*.js",
|
|
24
25
|
"./icons/*": "./lib/icons/*.js",
|
|
@@ -86,7 +87,7 @@
|
|
|
86
87
|
"timeago.js": "4.0.2",
|
|
87
88
|
"i18next": "22.4.15",
|
|
88
89
|
"nprogress": "0.2.0",
|
|
89
|
-
"@redocly/config": "0.20.
|
|
90
|
+
"@redocly/config": "0.20.2"
|
|
90
91
|
},
|
|
91
92
|
"scripts": {
|
|
92
93
|
"watch": "tsc -p tsconfig.build.json && (concurrently \"tsc -w -p tsconfig.build.json\" \"tsc-alias -w -p tsconfig.build.json\")",
|
|
@@ -4,7 +4,7 @@ export const datePicker = css`
|
|
|
4
4
|
--date-picker-nav-color: #000000;
|
|
5
5
|
--date-picker-tile-bg-color: var(--color-blue-2);
|
|
6
6
|
--date-picker-tile-color: var(--text-color-primary);
|
|
7
|
-
--date-picker-tile-bg-color-hover: var(--color-blue-3)
|
|
7
|
+
--date-picker-tile-bg-color-hover: var(--color-blue-3);
|
|
8
8
|
--date-picker-tile-color-hover: var(--text-color-primary);
|
|
9
9
|
--date-picker-input-width: var(--spacing-md);
|
|
10
10
|
--date-picker-invalid-input-bg-color: var(--bg-color-raised);
|
|
@@ -5,14 +5,8 @@ import type { WalkthroughControlsState } from '@redocly/theme/core/hooks';
|
|
|
5
5
|
export const CodeWalkthroughControlsStateContext = createContext<WalkthroughControlsState>({
|
|
6
6
|
activeFilters: [],
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
getInputState: () => null,
|
|
12
|
-
changeInputState: () => {},
|
|
13
|
-
|
|
14
|
-
getFilterState: () => null,
|
|
15
|
-
changeFilterState: () => {},
|
|
8
|
+
getControlState: () => null,
|
|
9
|
+
changeControlState: () => {},
|
|
16
10
|
|
|
17
11
|
getFileText: () => '',
|
|
18
12
|
areConditionsMet: () => false,
|
|
@@ -7,14 +7,9 @@ import type {
|
|
|
7
7
|
InputsMarkdocAttr,
|
|
8
8
|
TogglesMarkdocAttr,
|
|
9
9
|
CodeWalkthroughControls,
|
|
10
|
-
TogglesState,
|
|
11
|
-
InputsState,
|
|
12
|
-
FiltersState,
|
|
13
10
|
CodeWalkthroughFilterItem,
|
|
14
|
-
ControlState,
|
|
15
|
-
ControlTypeValue,
|
|
16
|
-
ControlType,
|
|
17
11
|
CodeWalkthroughConditionsObject,
|
|
12
|
+
CodeWalkthroughControlsState,
|
|
18
13
|
} from '@redocly/config';
|
|
19
14
|
|
|
20
15
|
import {
|
|
@@ -24,8 +19,6 @@ import {
|
|
|
24
19
|
replaceInputsWithValue,
|
|
25
20
|
} from '@redocly/theme/core/utils';
|
|
26
21
|
|
|
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
22
|
export type ActiveFilter = {
|
|
30
23
|
id: string;
|
|
31
24
|
label?: string;
|
|
@@ -34,15 +27,8 @@ export type ActiveFilter = {
|
|
|
34
27
|
|
|
35
28
|
export type WalkthroughControlsState = {
|
|
36
29
|
activeFilters: ActiveFilter[];
|
|
37
|
-
|
|
38
|
-
|
|
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'>;
|
|
30
|
+
getControlState: (id: string) => { value: string | boolean; render: boolean } | null;
|
|
31
|
+
changeControlState: (id: string, value: string | boolean) => void;
|
|
46
32
|
/* Utility */
|
|
47
33
|
areConditionsMet: (conditions: CodeWalkthroughConditionsObject) => boolean;
|
|
48
34
|
handleDownloadCode: (files: CodeWalkthroughFile[]) => Promise<void>;
|
|
@@ -66,8 +52,9 @@ export function useCodeWalkthroughControls(
|
|
|
66
52
|
const navigate = useNavigate();
|
|
67
53
|
const searchParams = useMemo(() => new URLSearchParams(location.search), [location.search]);
|
|
68
54
|
|
|
69
|
-
const [
|
|
70
|
-
const initialState:
|
|
55
|
+
const [controlsState, setControlsState] = useState(() => {
|
|
56
|
+
const initialState: CodeWalkthroughControlsState = {};
|
|
57
|
+
|
|
71
58
|
for (const [id, toggle] of Object.entries(toggles)) {
|
|
72
59
|
initialState[id] = {
|
|
73
60
|
...toggle,
|
|
@@ -76,41 +63,6 @@ export function useCodeWalkthroughControls(
|
|
|
76
63
|
value: enableDeepLink ? searchParams.get(id) === 'true' : false,
|
|
77
64
|
};
|
|
78
65
|
}
|
|
79
|
-
return initialState;
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
const changeToggleState = (toggleId: string, checked: boolean) => {
|
|
83
|
-
setTogglesState((prev) => {
|
|
84
|
-
const toggle = prev[toggleId];
|
|
85
|
-
if (toggle) {
|
|
86
|
-
return {
|
|
87
|
-
...prev,
|
|
88
|
-
[toggleId]: {
|
|
89
|
-
...toggle,
|
|
90
|
-
value: checked,
|
|
91
|
-
},
|
|
92
|
-
};
|
|
93
|
-
} else {
|
|
94
|
-
return prev;
|
|
95
|
-
}
|
|
96
|
-
});
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
const getToggleState = (toggleId: string) => {
|
|
100
|
-
const toggleState = togglesState[toggleId];
|
|
101
|
-
|
|
102
|
-
if (toggleState) {
|
|
103
|
-
return {
|
|
104
|
-
render: toggleState.render,
|
|
105
|
-
value: toggleState.value,
|
|
106
|
-
};
|
|
107
|
-
} else {
|
|
108
|
-
return null;
|
|
109
|
-
}
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
const [inputsState, setInputsState] = useState(() => {
|
|
113
|
-
const initialState: InputsState = {};
|
|
114
66
|
|
|
115
67
|
for (const [id, input] of Object.entries(inputs)) {
|
|
116
68
|
initialState[id] = {
|
|
@@ -120,41 +72,6 @@ export function useCodeWalkthroughControls(
|
|
|
120
72
|
value: enableDeepLink ? (searchParams.get(id) ?? input.value) : input.value,
|
|
121
73
|
};
|
|
122
74
|
}
|
|
123
|
-
return initialState;
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
const changeInputState = (inputId: string, value: string) => {
|
|
127
|
-
setInputsState((prev) => {
|
|
128
|
-
const input = prev[inputId];
|
|
129
|
-
if (input) {
|
|
130
|
-
return {
|
|
131
|
-
...prev,
|
|
132
|
-
[inputId]: {
|
|
133
|
-
...input,
|
|
134
|
-
value,
|
|
135
|
-
},
|
|
136
|
-
};
|
|
137
|
-
} else {
|
|
138
|
-
return prev;
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
const getInputState = (inputId: string) => {
|
|
144
|
-
const inputState = inputsState[inputId];
|
|
145
|
-
|
|
146
|
-
if (inputState) {
|
|
147
|
-
return {
|
|
148
|
-
render: inputState.render,
|
|
149
|
-
value: inputState.value,
|
|
150
|
-
};
|
|
151
|
-
} else {
|
|
152
|
-
return null;
|
|
153
|
-
}
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
const [filtersState, setFiltersState] = useState(() => {
|
|
157
|
-
const initialState: FiltersState = {};
|
|
158
75
|
|
|
159
76
|
for (const [id, filter] of Object.entries(filters)) {
|
|
160
77
|
const defaultValue = filter?.items?.[0]?.value || '';
|
|
@@ -169,56 +86,86 @@ export function useCodeWalkthroughControls(
|
|
|
169
86
|
return initialState;
|
|
170
87
|
});
|
|
171
88
|
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
const
|
|
175
|
-
if (
|
|
176
|
-
|
|
177
|
-
...prev,
|
|
178
|
-
[filterId]: {
|
|
179
|
-
...filter,
|
|
180
|
-
value,
|
|
181
|
-
},
|
|
182
|
-
};
|
|
183
|
-
} else {
|
|
89
|
+
const changeControlState = (id: string, value: string | boolean) => {
|
|
90
|
+
setControlsState((prev) => {
|
|
91
|
+
const control = prev[id];
|
|
92
|
+
if (!control) {
|
|
93
|
+
console.error(`Control with id "${id}" not found.`);
|
|
184
94
|
return prev;
|
|
185
95
|
}
|
|
96
|
+
|
|
97
|
+
switch (control.type) {
|
|
98
|
+
case 'input':
|
|
99
|
+
if (typeof value !== 'string') {
|
|
100
|
+
console.error(
|
|
101
|
+
`Invalid value type for input "${id}". Input control type requires a string value.`,
|
|
102
|
+
);
|
|
103
|
+
return prev;
|
|
104
|
+
}
|
|
105
|
+
break;
|
|
106
|
+
|
|
107
|
+
case 'toggle':
|
|
108
|
+
if (typeof value !== 'boolean') {
|
|
109
|
+
console.error(
|
|
110
|
+
`Invalid value type for toggle "${id}". Toggle control type requires a boolean value.`,
|
|
111
|
+
);
|
|
112
|
+
return prev;
|
|
113
|
+
}
|
|
114
|
+
break;
|
|
115
|
+
|
|
116
|
+
case 'filter':
|
|
117
|
+
if (typeof value !== 'string') {
|
|
118
|
+
console.error(
|
|
119
|
+
`Invalid value type for filter "${id}". Filter control type requires a string value.`,
|
|
120
|
+
);
|
|
121
|
+
return prev;
|
|
122
|
+
}
|
|
123
|
+
break;
|
|
124
|
+
|
|
125
|
+
default:
|
|
126
|
+
console.error(
|
|
127
|
+
`Invalid control type "${(control as { type: string })?.type}" for control "${id}". Allowed types are "toggle", "input", or "filter".`,
|
|
128
|
+
);
|
|
129
|
+
return prev;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
...prev,
|
|
134
|
+
[id]: {
|
|
135
|
+
...control,
|
|
136
|
+
value,
|
|
137
|
+
},
|
|
138
|
+
} as CodeWalkthroughControlsState;
|
|
186
139
|
});
|
|
187
140
|
};
|
|
188
141
|
|
|
189
|
-
const
|
|
190
|
-
const
|
|
142
|
+
const getControlState = (id: string) => {
|
|
143
|
+
const controlState = controlsState[id];
|
|
191
144
|
|
|
192
|
-
if (
|
|
145
|
+
if (controlState) {
|
|
193
146
|
return {
|
|
194
|
-
render:
|
|
195
|
-
value:
|
|
147
|
+
render: controlState.render,
|
|
148
|
+
value: controlState.value,
|
|
196
149
|
};
|
|
197
150
|
} else {
|
|
198
151
|
return null;
|
|
199
152
|
}
|
|
200
153
|
};
|
|
201
154
|
|
|
202
|
-
const state = {
|
|
203
|
-
...filtersState,
|
|
204
|
-
...togglesState,
|
|
205
|
-
...inputsState,
|
|
206
|
-
};
|
|
207
|
-
|
|
208
155
|
const walkthroughContext = useMemo(() => {
|
|
209
156
|
const areConditionsMet = (conditions: CodeWalkthroughConditionsObject) =>
|
|
210
|
-
matchCodeWalkthroughConditions(conditions,
|
|
157
|
+
matchCodeWalkthroughConditions(conditions, controlsState);
|
|
211
158
|
|
|
212
|
-
for (const [id,
|
|
213
|
-
if (
|
|
214
|
-
|
|
215
|
-
|
|
159
|
+
for (const [id, control] of Object.entries(controlsState)) {
|
|
160
|
+
if (control && !areConditionsMet(control)) {
|
|
161
|
+
controlsState[id].render = false;
|
|
162
|
+
controlsState[id].value = defaultControlsValues[control.type];
|
|
216
163
|
}
|
|
217
164
|
}
|
|
218
165
|
|
|
219
166
|
const activeFilters = [];
|
|
220
167
|
for (const [id, filter] of Object.entries(filters)) {
|
|
221
|
-
if (!
|
|
168
|
+
if (!controlsState[id].render) {
|
|
222
169
|
continue;
|
|
223
170
|
}
|
|
224
171
|
|
|
@@ -228,18 +175,18 @@ export function useCodeWalkthroughControls(
|
|
|
228
175
|
const activeItems = items.filter((item) => areConditionsMet(item));
|
|
229
176
|
|
|
230
177
|
if (activeItems.length === 0) {
|
|
231
|
-
|
|
232
|
-
|
|
178
|
+
controlsState[id].render = false;
|
|
179
|
+
controlsState[id].value = defaultControlsValues['filter'];
|
|
233
180
|
continue;
|
|
234
181
|
}
|
|
235
182
|
|
|
236
|
-
const currentValue =
|
|
183
|
+
const currentValue = controlsState[id].value;
|
|
237
184
|
if (currentValue) {
|
|
238
185
|
const isValueInActiveItems =
|
|
239
186
|
activeItems.findIndex(({ value }) => value === currentValue) !== -1;
|
|
240
|
-
|
|
187
|
+
controlsState[id].value = isValueInActiveItems ? currentValue : activeItems[0].value;
|
|
241
188
|
} else {
|
|
242
|
-
|
|
189
|
+
controlsState[id].value = activeItems[0].value;
|
|
243
190
|
}
|
|
244
191
|
|
|
245
192
|
activeFilters.push({
|
|
@@ -249,11 +196,15 @@ export function useCodeWalkthroughControls(
|
|
|
249
196
|
});
|
|
250
197
|
}
|
|
251
198
|
|
|
199
|
+
const inputsState = Object.fromEntries(
|
|
200
|
+
Object.entries(controlsState).filter(([_, controlState]) => controlState.type === 'input'),
|
|
201
|
+
) as Record<string, { value: string }>;
|
|
202
|
+
|
|
252
203
|
const handleDownloadCode = (files: CodeWalkthroughFile[]) =>
|
|
253
|
-
downloadCodeWalkthrough(files,
|
|
204
|
+
downloadCodeWalkthrough(files, controlsState, inputsState);
|
|
254
205
|
|
|
255
206
|
const getFileText = (file: CodeWalkthroughFile) =>
|
|
256
|
-
getCodeWalkthroughFileText(file,
|
|
207
|
+
getCodeWalkthroughFileText(file, controlsState, inputsState);
|
|
257
208
|
|
|
258
209
|
const populateInputsWithValue = (node: string) => replaceInputsWithValue(node, inputsState);
|
|
259
210
|
|
|
@@ -264,9 +215,7 @@ export function useCodeWalkthroughControls(
|
|
|
264
215
|
getFileText,
|
|
265
216
|
populateInputsWithValue,
|
|
266
217
|
};
|
|
267
|
-
|
|
268
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
269
|
-
}, [filters, filtersState, togglesState, inputsState]);
|
|
218
|
+
}, [filters, controlsState]);
|
|
270
219
|
|
|
271
220
|
/**
|
|
272
221
|
* Update the URL search params with the current state of the filters and inputs
|
|
@@ -278,7 +227,7 @@ export function useCodeWalkthroughControls(
|
|
|
278
227
|
|
|
279
228
|
const newSearchParams = new URLSearchParams(Array.from(searchParams.entries()));
|
|
280
229
|
|
|
281
|
-
for (const [id, { value }] of Object.entries(
|
|
230
|
+
for (const [id, { value }] of Object.entries(controlsState)) {
|
|
282
231
|
if (value) {
|
|
283
232
|
newSearchParams.set(id, value.toString());
|
|
284
233
|
} else {
|
|
@@ -291,18 +240,11 @@ export function useCodeWalkthroughControls(
|
|
|
291
240
|
navigate({ search: newSearch });
|
|
292
241
|
// Ignore searchParams in dependency array to avoid infinite re-renders
|
|
293
242
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
294
|
-
}, [filters,
|
|
243
|
+
}, [filters, controlsState, navigate, location]);
|
|
295
244
|
|
|
296
245
|
return {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
getToggleState,
|
|
301
|
-
changeToggleState,
|
|
302
|
-
|
|
303
|
-
getFilterState,
|
|
304
|
-
changeFilterState,
|
|
305
|
-
|
|
246
|
+
changeControlState,
|
|
247
|
+
getControlState,
|
|
306
248
|
...walkthroughContext,
|
|
307
249
|
};
|
|
308
250
|
}
|
|
@@ -1019,6 +1019,24 @@ const error = css`
|
|
|
1019
1019
|
--detailed-error-message-width: 100%;
|
|
1020
1020
|
--detailed-error-message-font-size: var(--font-size-base);
|
|
1021
1021
|
--detailed-error-message-font-family: var(--code-block-controls-font-family);
|
|
1022
|
+
|
|
1023
|
+
--compilation-error-description-padding: 0 0 var(--spacing-base);
|
|
1024
|
+
--compilation-error-codeframe-margin: var(--spacing-xs) 0 0 0;
|
|
1025
|
+
--compilation-error-codeframe-padding: var(--spacing-xs);
|
|
1026
|
+
--compilation-error-block-padding: var(--spacing-base);
|
|
1027
|
+
--compilation-error-block-margin: var(--spacing-xl) 0;
|
|
1028
|
+
--compilation-error-title-font-size: var(--font-size-xl);
|
|
1029
|
+
--compilation-error-header-font-size: var(--font-size-lg);
|
|
1030
|
+
--compilation-error-text-font-size: var(--font-size-base);
|
|
1031
|
+
--compilation-error-header-padding: var(--spacing-sm);
|
|
1032
|
+
--compilation-error-container-margin: var(--spacing-xxl) auto;
|
|
1033
|
+
--compilation-error-container-padding: var(--spacing-lg);
|
|
1034
|
+
--compilation-error-page-max-width: 800px;
|
|
1035
|
+
--compilation-error-font-family: var(--font-family-base);
|
|
1036
|
+
--compilation-error-fix-instruction-margin: var(--spacing-sm) 0 0 0;
|
|
1037
|
+
--compilation-error-highlighted-text-padding: 0 var(--spacing-xxs);
|
|
1038
|
+
--compilation-error-divider-margin: var(--spacing-base) 0;
|
|
1039
|
+
--compilation-error-file-header-margin: 0 0 var(--spacing-xs) 0;
|
|
1022
1040
|
`;
|
|
1023
1041
|
|
|
1024
1042
|
const modal = css`
|
package/src/core/types/search.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { saveAs } from 'file-saver';
|
|
2
2
|
import JSZip from 'jszip';
|
|
3
3
|
|
|
4
|
-
import type { CodeWalkthroughFile
|
|
4
|
+
import type { CodeWalkthroughFile } from '@redocly/config';
|
|
5
5
|
|
|
6
6
|
import {
|
|
7
7
|
findClosestCommonDirectory,
|
|
@@ -15,7 +15,7 @@ JSZip.support.nodebuffer = false;
|
|
|
15
15
|
export async function downloadCodeWalkthrough(
|
|
16
16
|
files: CodeWalkthroughFile[],
|
|
17
17
|
state: Record<string, { value: string | boolean }>,
|
|
18
|
-
inputsState:
|
|
18
|
+
inputsState: Record<string, { value: string }>,
|
|
19
19
|
) {
|
|
20
20
|
const zip = new JSZip();
|
|
21
21
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type { CodeWalkthroughFile, CodeWalkthroughNode
|
|
1
|
+
import type { CodeWalkthroughFile, CodeWalkthroughNode } from '@redocly/config';
|
|
2
2
|
|
|
3
3
|
import { matchCodeWalkthroughConditions, replaceInputsWithValue } from '@redocly/theme/core/utils';
|
|
4
4
|
|
|
5
5
|
export function getCodeWalkthroughFileText(
|
|
6
6
|
file: CodeWalkthroughFile,
|
|
7
7
|
state: Record<string, { value: string | boolean }>,
|
|
8
|
-
inputsState:
|
|
8
|
+
inputsState: Record<string, { value: string }>,
|
|
9
9
|
) {
|
|
10
10
|
const contentChunks: string[] =
|
|
11
11
|
file?.content.flatMap((node) => getChunkText(node, state, inputsState)) ?? [];
|
|
@@ -16,7 +16,7 @@ export function getCodeWalkthroughFileText(
|
|
|
16
16
|
function getChunkText(
|
|
17
17
|
node: CodeWalkthroughNode,
|
|
18
18
|
state: Record<string, { value: string | boolean }>,
|
|
19
|
-
inputsState:
|
|
19
|
+
inputsState: Record<string, { value: string }>,
|
|
20
20
|
): string[] {
|
|
21
21
|
if (typeof node === 'string') {
|
|
22
22
|
const replacedNode = replaceInputsWithValue(node, inputsState);
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export type SecurityDetails = {
|
|
2
|
+
password?: string;
|
|
3
|
+
username?: string;
|
|
4
|
+
token?: {
|
|
5
|
+
token_type?: string;
|
|
6
|
+
access_token: string;
|
|
7
|
+
};
|
|
8
|
+
client_id?: string;
|
|
9
|
+
client_secret?: string;
|
|
10
|
+
scopes?: string[];
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type ConfigureRequestValues = {
|
|
14
|
+
headers?: Record<string, string>;
|
|
15
|
+
body?: Record<string, any>;
|
|
16
|
+
query?: Record<string, string>;
|
|
17
|
+
path?: Record<string, string>;
|
|
18
|
+
cookie?: Record<string, string>;
|
|
19
|
+
security?: SecurityDetails;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
type Configure = {
|
|
23
|
+
requestValues?: ConfigureRequestValues;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export function configure(): Configure {
|
|
27
|
+
// const exampleRequestValues: ConfigureRequestValues = {
|
|
28
|
+
// body: {
|
|
29
|
+
// name: 'John Doe',
|
|
30
|
+
// },
|
|
31
|
+
// security: {
|
|
32
|
+
// token: {
|
|
33
|
+
// access_token: 'John Doe token',
|
|
34
|
+
// },
|
|
35
|
+
// },
|
|
36
|
+
// };
|
|
37
|
+
// return { requestValues: exampleRequestValues } satisfies Configure;
|
|
38
|
+
return {} satisfies Configure;
|
|
39
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -21,9 +21,33 @@ export function CodeContainer({
|
|
|
21
21
|
// useEffect executed before DOM is updated due to re-render called before "painting" phase.
|
|
22
22
|
setTimeout(() => {
|
|
23
23
|
if (compRef.current) {
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
const highlightedLines = Array.from(
|
|
25
|
+
compRef.current.querySelectorAll('span.line.highlighted'),
|
|
26
|
+
);
|
|
27
|
+
const { bottom: panelBottom, top: panelTop } = compRef.current.getBoundingClientRect();
|
|
28
|
+
|
|
29
|
+
const linesBelow: Element[] = [];
|
|
30
|
+
const linesAbove: Element[] = [];
|
|
31
|
+
for (const highlightedLine of highlightedLines) {
|
|
32
|
+
const { bottom: lineBottom, top: lineTop } = highlightedLine.getBoundingClientRect();
|
|
33
|
+
const below = lineBottom > panelBottom;
|
|
34
|
+
const above = lineTop < panelTop;
|
|
35
|
+
|
|
36
|
+
if (below) {
|
|
37
|
+
linesBelow.push(highlightedLine);
|
|
38
|
+
} else if (above) {
|
|
39
|
+
linesAbove.push(highlightedLine);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if ((linesBelow.length && linesAbove.length) || linesAbove.length) {
|
|
44
|
+
linesAbove[0].scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' });
|
|
45
|
+
} else if (linesBelow.length) {
|
|
46
|
+
linesBelow[linesBelow.length - 1].scrollIntoView({
|
|
47
|
+
behavior: 'smooth',
|
|
48
|
+
block: 'nearest',
|
|
49
|
+
inline: 'start',
|
|
50
|
+
});
|
|
27
51
|
}
|
|
28
52
|
}
|
|
29
53
|
}, 200);
|
|
@@ -5,9 +5,10 @@ import type { CodeWalkthroughFilter } from '@redocly/config';
|
|
|
5
5
|
|
|
6
6
|
import { Tag, ContentWrapper as TagContentWrapper } from '@redocly/theme/components/Tag/Tag';
|
|
7
7
|
|
|
8
|
+
export type GetFilterState = (groupId: string) => { value: string; render: boolean } | null;
|
|
8
9
|
export type CodeFilterProps = {
|
|
9
10
|
filters: CodeWalkthroughFilter[];
|
|
10
|
-
getFilterState:
|
|
11
|
+
getFilterState: GetFilterState;
|
|
11
12
|
handleFilterSelect: (groupId: string, id: string) => void;
|
|
12
13
|
filtersElementRef?: React.RefObject<HTMLDivElement>;
|
|
13
14
|
};
|
|
@@ -35,7 +35,7 @@ export function CodeStep({
|
|
|
35
35
|
lockObserver.current = true;
|
|
36
36
|
|
|
37
37
|
if (compRef.current) {
|
|
38
|
-
compRef.current.scrollIntoView({ behavior: 'smooth', block: '
|
|
38
|
+
compRef.current.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
setActiveStep(id);
|
|
@@ -13,11 +13,11 @@ export type CodeToggleProps = React.PropsWithChildren<{
|
|
|
13
13
|
|
|
14
14
|
export function CodeToggle(props: CodeToggleProps) {
|
|
15
15
|
const { id, label, description, children } = props;
|
|
16
|
-
const {
|
|
16
|
+
const { getControlState, changeControlState } = useContext(CodeWalkthroughControlsStateContext);
|
|
17
17
|
|
|
18
|
-
const toggleState =
|
|
18
|
+
const toggleState = getControlState(id);
|
|
19
19
|
|
|
20
|
-
if (!(toggleState && toggleState.render)) {
|
|
20
|
+
if (!(toggleState && toggleState.render && typeof toggleState.value === 'boolean')) {
|
|
21
21
|
return null;
|
|
22
22
|
}
|
|
23
23
|
const checked = toggleState.value;
|
|
@@ -26,7 +26,7 @@ export function CodeToggle(props: CodeToggleProps) {
|
|
|
26
26
|
<ToggleWrapper data-component-name="Markdoc/CodeWalkthrough/CodeToggle">
|
|
27
27
|
<ToggleContentWrapper>
|
|
28
28
|
<ToggleSubtitle>
|
|
29
|
-
<Switch value={checked} onChange={(newValue) =>
|
|
29
|
+
<Switch value={checked} onChange={(newValue) => changeControlState(id, newValue)} />
|
|
30
30
|
<div>{label}</div>
|
|
31
31
|
</ToggleSubtitle>
|
|
32
32
|
{description ? (
|
|
@@ -9,7 +9,10 @@ import {
|
|
|
9
9
|
} from '@redocly/theme/core/contexts';
|
|
10
10
|
import { CodePanel } from '@redocly/theme/markdoc/components/CodeWalkthrough/CodePanel';
|
|
11
11
|
import { useCodeWalkthrough } from '@redocly/theme/core/hooks';
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
CodeFilters,
|
|
14
|
+
GetFilterState,
|
|
15
|
+
} from '@redocly/theme/markdoc/components/CodeWalkthrough/CodeFilters';
|
|
13
16
|
import { StepWrapper } from '@redocly/theme/markdoc/components/CodeWalkthrough/CodeStep';
|
|
14
17
|
|
|
15
18
|
export type CodeWalkthroughProps = PropsWithChildren<CodeWalkthroughAttr>;
|
|
@@ -22,7 +25,7 @@ export function CodeWalkthrough({ children, steps, preview, ...attributes }: Cod
|
|
|
22
25
|
initialSteps,
|
|
23
26
|
attributes,
|
|
24
27
|
);
|
|
25
|
-
const { activeFilters,
|
|
28
|
+
const { activeFilters, getControlState, changeControlState } = controlsState;
|
|
26
29
|
const { filtersElementRef } = stepsState;
|
|
27
30
|
|
|
28
31
|
return (
|
|
@@ -35,8 +38,8 @@ export function CodeWalkthrough({ children, steps, preview, ...attributes }: Cod
|
|
|
35
38
|
<DocsPanel>
|
|
36
39
|
<CodeFilters
|
|
37
40
|
filters={activeFilters}
|
|
38
|
-
getFilterState={
|
|
39
|
-
handleFilterSelect={
|
|
41
|
+
getFilterState={getControlState as GetFilterState}
|
|
42
|
+
handleFilterSelect={changeControlState}
|
|
40
43
|
filtersElementRef={filtersElementRef}
|
|
41
44
|
/>
|
|
42
45
|
<ContentWrapper>{children}</ContentWrapper>
|
|
@@ -15,16 +15,16 @@ const DEBOUNCE_TIME = 500;
|
|
|
15
15
|
|
|
16
16
|
export function Input(props: InputProps) {
|
|
17
17
|
const { id, label, placeholder } = props;
|
|
18
|
-
const {
|
|
18
|
+
const { getControlState, changeControlState } = useContext(CodeWalkthroughControlsStateContext);
|
|
19
19
|
|
|
20
|
-
const inputState =
|
|
20
|
+
const inputState = getControlState(id);
|
|
21
21
|
|
|
22
22
|
const [value, setValue] = useState(inputState?.value || '');
|
|
23
23
|
|
|
24
24
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
25
25
|
const debouncedSave = useCallback(
|
|
26
26
|
debounce((id: string, value: string) => {
|
|
27
|
-
|
|
27
|
+
changeControlState(id, value);
|
|
28
28
|
}, DEBOUNCE_TIME),
|
|
29
29
|
[],
|
|
30
30
|
);
|
|
@@ -45,7 +45,7 @@ export function Input(props: InputProps) {
|
|
|
45
45
|
{label && <Label>{label}</Label>}
|
|
46
46
|
<StyledInput
|
|
47
47
|
id={id}
|
|
48
|
-
value={value}
|
|
48
|
+
value={value as string}
|
|
49
49
|
onChange={handleChange}
|
|
50
50
|
aria-label={label}
|
|
51
51
|
placeholder={placeholder}
|