@redocly/theme 0.48.0 → 0.48.2

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 (98) hide show
  1. package/lib/components/DatePicker/variables.js +1 -1
  2. package/lib/components/Feedback/Mood.js +14 -9
  3. package/lib/components/Search/SearchDialog.js +9 -3
  4. package/lib/components/Search/variables.js +4 -0
  5. package/lib/components/Segmented/Segmented.d.ts +4 -4
  6. package/lib/components/Segmented/Segmented.js +4 -7
  7. package/lib/components/Tag/Tag.d.ts +1 -0
  8. package/lib/components/Tag/Tag.js +3 -2
  9. package/lib/core/contexts/CodeWalkthrough/CodeWalkthroughControlsContext.js +2 -6
  10. package/lib/core/hooks/code-walkthrough/use-code-walkthrough-controls.d.ts +7 -10
  11. package/lib/core/hooks/code-walkthrough/use-code-walkthrough-controls.js +63 -97
  12. package/lib/core/hooks/code-walkthrough/use-code-walkthrough-steps.d.ts +1 -2
  13. package/lib/core/hooks/code-walkthrough/use-code-walkthrough-steps.js +20 -15
  14. package/lib/core/hooks/code-walkthrough/use-code-walkthrough.d.ts +2 -7
  15. package/lib/core/hooks/code-walkthrough/use-code-walkthrough.js +10 -3
  16. package/lib/core/hooks/code-walkthrough/use-renderable-files.d.ts +9 -0
  17. package/lib/core/hooks/code-walkthrough/use-renderable-files.js +28 -0
  18. package/lib/core/hooks/index.d.ts +1 -0
  19. package/lib/core/hooks/index.js +1 -0
  20. package/lib/core/styles/global.js +18 -0
  21. package/lib/core/types/l10n.d.ts +1 -1
  22. package/lib/core/utils/download-code-walkthrough.d.ts +4 -2
  23. package/lib/core/utils/download-code-walkthrough.js +9 -1
  24. package/lib/core/utils/find-closest-common-directory.d.ts +6 -0
  25. package/lib/core/utils/find-closest-common-directory.js +51 -0
  26. package/lib/core/utils/get-code-walkthrough-file-text.d.ts +4 -2
  27. package/lib/core/utils/get-file-icon.js +6 -0
  28. package/lib/core/utils/index.d.ts +1 -0
  29. package/lib/core/utils/index.js +1 -0
  30. package/lib/core/utils/replace-inputs-with-value.d.ts +1 -1
  31. package/lib/core/utils/replace-inputs-with-value.js +9 -10
  32. package/lib/icons/DocumentJavaIcon/DocumentJavaIcon.d.ts +9 -0
  33. package/lib/icons/DocumentJavaIcon/DocumentJavaIcon.js +22 -0
  34. package/lib/icons/DocumentJavaIcon/index.d.ts +1 -0
  35. package/lib/icons/DocumentJavaIcon/index.js +6 -0
  36. package/lib/icons/DocumentPythonIcon/DocumentPythonIcon.d.ts +9 -0
  37. package/lib/icons/DocumentPythonIcon/DocumentPythonIcon.js +23 -0
  38. package/lib/icons/DocumentPythonIcon/index.d.ts +1 -0
  39. package/lib/icons/DocumentPythonIcon/index.js +6 -0
  40. package/lib/icons/DocumentShellIcon/DocumentShellIcon.d.ts +9 -0
  41. package/lib/icons/DocumentShellIcon/DocumentShellIcon.js +22 -0
  42. package/lib/icons/DocumentShellIcon/index.d.ts +1 -0
  43. package/lib/icons/DocumentShellIcon/index.js +6 -0
  44. package/lib/icons/__tests__/IconTestUtils.d.ts +7 -0
  45. package/lib/icons/__tests__/IconTestUtils.js +33 -0
  46. package/lib/layouts/CodeWalkthroughLayout.js +4 -1
  47. package/lib/markdoc/components/CodeWalkthrough/CodeContainer.js +25 -4
  48. package/lib/markdoc/components/CodeWalkthrough/CodeFilters.d.ts +5 -4
  49. package/lib/markdoc/components/CodeWalkthrough/CodeFilters.js +15 -2
  50. package/lib/markdoc/components/CodeWalkthrough/CodePanel.js +1 -1
  51. package/lib/markdoc/components/CodeWalkthrough/CodePanelHeader.js +29 -23
  52. package/lib/markdoc/components/CodeWalkthrough/CodePanelPreview.js +1 -1
  53. package/lib/markdoc/components/CodeWalkthrough/CodePanelToolbar.js +1 -1
  54. package/lib/markdoc/components/CodeWalkthrough/CodeStep.js +5 -2
  55. package/lib/markdoc/components/CodeWalkthrough/CodeToggle.js +5 -5
  56. package/lib/markdoc/components/CodeWalkthrough/CodeWalkthrough.js +3 -3
  57. package/lib/markdoc/components/CodeWalkthrough/Input.js +7 -5
  58. package/lib/markdoc/tags/code-walkthrough.js +5 -0
  59. package/package.json +3 -3
  60. package/src/components/DatePicker/variables.ts +1 -1
  61. package/src/components/Feedback/Mood.tsx +16 -7
  62. package/src/components/Search/SearchDialog.tsx +52 -36
  63. package/src/components/Search/variables.ts +4 -0
  64. package/src/components/Segmented/Segmented.tsx +10 -10
  65. package/src/components/Tag/Tag.tsx +1 -1
  66. package/src/core/contexts/CodeWalkthrough/CodeWalkthroughControlsContext.tsx +2 -8
  67. package/src/core/hooks/code-walkthrough/use-code-walkthrough-controls.ts +90 -142
  68. package/src/core/hooks/code-walkthrough/use-code-walkthrough-steps.ts +30 -18
  69. package/src/core/hooks/code-walkthrough/use-code-walkthrough.ts +13 -13
  70. package/src/core/hooks/code-walkthrough/use-renderable-files.ts +51 -0
  71. package/src/core/hooks/index.ts +1 -0
  72. package/src/core/styles/global.ts +18 -0
  73. package/src/core/types/l10n.ts +3 -1
  74. package/src/core/utils/download-code-walkthrough.ts +16 -4
  75. package/src/core/utils/find-closest-common-directory.ts +51 -0
  76. package/src/core/utils/get-code-walkthrough-file-text.ts +3 -3
  77. package/src/core/utils/get-file-icon.ts +7 -0
  78. package/src/core/utils/index.ts +1 -0
  79. package/src/core/utils/replace-inputs-with-value.ts +12 -9
  80. package/src/icons/DocumentJavaIcon/DocumentJavaIcon.tsx +33 -0
  81. package/src/icons/DocumentJavaIcon/index.ts +1 -0
  82. package/src/icons/DocumentPythonIcon/DocumentPythonIcon.tsx +37 -0
  83. package/src/icons/DocumentPythonIcon/index.ts +1 -0
  84. package/src/icons/DocumentShellIcon/DocumentShellIcon.tsx +33 -0
  85. package/src/icons/DocumentShellIcon/index.ts +1 -0
  86. package/src/icons/__tests__/IconTestUtils.tsx +31 -0
  87. package/src/layouts/CodeWalkthroughLayout.tsx +5 -1
  88. package/src/markdoc/components/CodeWalkthrough/CodeContainer.tsx +28 -3
  89. package/src/markdoc/components/CodeWalkthrough/CodeFilters.tsx +21 -4
  90. package/src/markdoc/components/CodeWalkthrough/CodePanel.tsx +1 -1
  91. package/src/markdoc/components/CodeWalkthrough/CodePanelHeader.tsx +64 -47
  92. package/src/markdoc/components/CodeWalkthrough/CodePanelPreview.tsx +1 -1
  93. package/src/markdoc/components/CodeWalkthrough/CodePanelToolbar.tsx +1 -1
  94. package/src/markdoc/components/CodeWalkthrough/CodeStep.tsx +5 -1
  95. package/src/markdoc/components/CodeWalkthrough/CodeToggle.tsx +5 -5
  96. package/src/markdoc/components/CodeWalkthrough/CodeWalkthrough.tsx +11 -5
  97. package/src/markdoc/components/CodeWalkthrough/Input.tsx +8 -6
  98. package/src/markdoc/tags/code-walkthrough.ts +5 -0
@@ -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);
@@ -22,15 +22,12 @@ var __importStar = (this && this.__importStar) || function (mod) {
22
22
  __setModuleDefault(result, mod);
23
23
  return result;
24
24
  };
25
- var __importDefault = (this && this.__importDefault) || function (mod) {
26
- return (mod && mod.__esModule) ? mod : { "default": mod };
27
- };
28
25
  Object.defineProperty(exports, "__esModule", { value: true });
29
26
  exports.MOOD_STATES = void 0;
30
27
  exports.Mood = Mood;
31
28
  const React = __importStar(require("react"));
32
29
  const react_1 = require("react");
33
- const styled_components_1 = __importDefault(require("styled-components"));
30
+ const styled_components_1 = __importStar(require("styled-components"));
34
31
  const Reasons_1 = require("../../components/Feedback/Reasons");
35
32
  const hooks_1 = require("../../core/hooks");
36
33
  const RadioCheckButtonIcon_1 = require("../../icons/RadioCheckButtonIcon/RadioCheckButtonIcon");
@@ -134,14 +131,14 @@ function Mood({ settings, onSubmit, className }) {
134
131
  if (isSubmitted) {
135
132
  return (React.createElement(MoodWrapper, { "data-component-name": "Feedback/Mood" },
136
133
  React.createElement(StyledFormMandatoryFields, null,
137
- React.createElement(Label, { "data-translation-key": "feedback.settings.submitText" }, submitText ||
134
+ React.createElement(Label, { standAlone: true, "data-translation-key": "feedback.settings.submitText" }, submitText ||
138
135
  translate('feedback.settings.submitText', 'Thank you for helping improve our documentation!')),
139
136
  React.createElement(RadioCheckButtonIcon_1.RadioCheckButtonIcon, null))));
140
137
  }
141
138
  return (React.createElement(MoodWrapper, { "data-component-name": "Feedback/Mood", className: className },
142
139
  React.createElement(StyledForm, { onSubmit: onSubmitMoodForm },
143
140
  React.createElement(StyledFormMandatoryFields, null,
144
- React.createElement(Label, { "data-translation-key": "feedback.settings.label" }, label || translate('feedback.settings.label', 'Was this helpful?')),
141
+ React.createElement(Label, { standAlone: true, "data-translation-key": "feedback.settings.label" }, label || translate('feedback.settings.label', 'Was this helpful?')),
145
142
  React.createElement(StyledMandatoryFieldContainer, null,
146
143
  React.createElement(Button_1.Button, { "aria-label": MOOD_STATES.DISSATISFIED, type: "button", size: "medium", variant: score === MOOD_STATES.DISSATISFIED ? 'primary' : 'secondary', tone: score === MOOD_STATES.DISSATISFIED ? 'danger' : 'default', onClick: () => {
147
144
  setScore(MOOD_STATES.DISSATISFIED);
@@ -189,9 +186,17 @@ const MoodWrapper = styled_components_1.default.div `
189
186
  const Label = styled_components_1.default.h4 `
190
187
  font-family: var(--feedback-font-family);
191
188
  font-weight: var(--font-weight-regular);
192
- font-size: var(--feedback-header-font-size);
193
- line-height: var(--feedback-header-line-height);
194
- color: var(--feedback-header-text-color);
189
+ font-size: var(--feedback-font-size);
190
+ line-height: var(--feedback-line-height);
191
+ color: var(--feedback-text-color);
192
+
193
+ ${({ standAlone = false }) => standAlone &&
194
+ (0, styled_components_1.css) `
195
+ font-size: var(--feedback-header-font-size);
196
+ line-height: var(--feedback-header-line-height);
197
+ color: var(--feedback-header-text-color);
198
+ `}
199
+
195
200
  margin: 0;
196
201
  `;
197
202
  const ButtonsContainer = styled_components_1.default.div `
@@ -120,7 +120,7 @@ function SearchDialog({ onClose, className }) {
120
120
  setQuery('');
121
121
  aiSearch.askQuestion(query);
122
122
  }
123
- } }, translate('search.aiButton', 'Search with AI'))) : null,
123
+ } }, translate('search.ai.button', 'Search with AI'))) : null,
124
124
  showSearchFilterButton && (react_1.default.createElement(SearchFilterToggleButton, { icon: react_1.default.createElement(SettingsIcon_1.SettingsIcon, null), onClick: onFilterToggle }))))),
125
125
  react_1.default.createElement(SearchDialogBody, null, mode === 'search' ? (react_1.default.createElement(react_1.default.Fragment, null,
126
126
  react_1.default.createElement(SearchDialogBodyMainView, null,
@@ -139,7 +139,7 @@ function SearchDialog({ onClose, className }) {
139
139
  react_1.default.createElement(SearchSuggestedPages_1.SearchSuggestedPages, null)))),
140
140
  advancedSearch && mode === 'search' && isFilterOpen && (react_1.default.createElement(SearchDialogBodyFilterView, null,
141
141
  react_1.default.createElement(SearchFilter_1.SearchFilter, { facets: facets, filter: filter, query: query, quickFilterFields: [groupField], onFilterChange: onFilterChange, onFilterReset: onFilterReset, onFacetReset: onFacetReset }))))) : (react_1.default.createElement(SearchAiResponse_1.SearchAiResponse, { question: aiSearch.question, isGeneratingResponse: aiSearch.isGeneratingResponse, response: aiSearch.response, resources: aiSearch.resources }))),
142
- mode === 'search' && (react_1.default.createElement(SearchDialogFooter, null,
142
+ react_1.default.createElement(SearchDialogFooter, null, mode === 'ai-dialog' ? (react_1.default.createElement(AiDisclaimer, null, translate('search.ai.disclaimer', 'AI search might provide incomplete or incorrect results. Verify important information.'))) : (react_1.default.createElement(react_1.default.Fragment, null,
143
143
  react_1.default.createElement(SearchShortcuts, null,
144
144
  react_1.default.createElement(SearchShortcut_1.SearchShortcut, { "data-translation-key": "search.keys.navigate", combination: "Tab", text: translate('search.keys.navigate', 'to navigate') }),
145
145
  react_1.default.createElement(SearchShortcut_1.SearchShortcut, { "data-translation-key": "search.keys.select", combination: "\u23CE", text: translate('search.keys.select', 'to select') }),
@@ -147,7 +147,7 @@ function SearchDialog({ onClose, className }) {
147
147
  isSearchLoading && (react_1.default.createElement(SearchLoading, null,
148
148
  react_1.default.createElement(SpinnerLoader_1.SpinnerLoader, { size: "16px", color: "var(--search-input-icon-color)" }),
149
149
  translate('search.loading', 'Loading...'))),
150
- react_1.default.createElement(SearchCancelButton, { "data-translation-key": "search.cancel", variant: "secondary", size: "small", onClick: onClose }, translate('search.cancel', 'Cancel')))))));
150
+ react_1.default.createElement(SearchCancelButton, { "data-translation-key": "search.cancel", variant: "secondary", size: "small", onClick: onClose }, translate('search.cancel', 'Cancel'))))))));
151
151
  }
152
152
  const SearchOverlay = styled_components_1.default.div `
153
153
  position: fixed;
@@ -296,4 +296,10 @@ const SearchHeaderButtons = styled_components_1.default.div `
296
296
  padding-left: var(--search-header-buttons-padding-left);
297
297
  border-left: var(--search-header-buttons-border-left);
298
298
  `;
299
+ const AiDisclaimer = styled_components_1.default.div `
300
+ font-size: var(--search-ai-disclaimer-font-size);
301
+ line-height: var(--search-ai-disclaimer-line-height);
302
+ color: var(--search-ai-disclaimer-text-color);
303
+ margin: 0 auto;
304
+ `;
299
305
  //# sourceMappingURL=SearchDialog.js.map
@@ -178,6 +178,10 @@ exports.search = (0, styled_components_1.css) `
178
178
  --search-ai-resource-tag-text-color: var(--text-color-secondary);
179
179
  --search-ai-resource-tag-icon-color: var(--text-color-secondary);
180
180
 
181
+ --search-ai-disclaimer-font-size: var(--font-size-sm);
182
+ --search-ai-disclaimer-line-height: var(--line-height-sm);
183
+ --search-ai-disclaimer-text-color: var(--text-color-secondary);
184
+
181
185
  // @tokens End
182
186
  `;
183
187
  //# sourceMappingURL=variables.js.map
@@ -1,4 +1,4 @@
1
- import type { ReactElement } from 'react';
1
+ import type { ForwardedRef, ReactElement } from 'react';
2
2
  import type { SelectOption } from '../../core/types/select';
3
3
  export type SegmentedProps<T> = {
4
4
  options: SelectOption<T>[];
@@ -7,6 +7,6 @@ export type SegmentedProps<T> = {
7
7
  className?: string;
8
8
  size?: 'regular' | 'small';
9
9
  };
10
- declare function SegmentedComponent<T>({ options, onChange, value, className, size, }: SegmentedProps<T>): ReactElement;
11
- export declare const Segmented: typeof SegmentedComponent;
12
- export {};
10
+ export declare const Segmented: <T>(props: SegmentedProps<T> & {
11
+ ref?: ForwardedRef<HTMLDivElement>;
12
+ }) => ReactElement;
@@ -22,18 +22,15 @@ var __importStar = (this && this.__importStar) || function (mod) {
22
22
  __setModuleDefault(result, mod);
23
23
  return result;
24
24
  };
25
- var __importDefault = (this && this.__importDefault) || function (mod) {
26
- return (mod && mod.__esModule) ? mod : { "default": mod };
27
- };
28
25
  Object.defineProperty(exports, "__esModule", { value: true });
29
26
  exports.Segmented = void 0;
30
- const react_1 = __importDefault(require("react"));
27
+ const react_1 = __importStar(require("react"));
31
28
  const styled_components_1 = __importStar(require("styled-components"));
32
29
  const typedMemo_1 = require("../../core/hoc/typedMemo");
33
- function SegmentedComponent({ options, onChange, value, className = '', size = 'regular', }) {
34
- return (react_1.default.createElement(SegmentedGroup, { "data-component-name": "Segmented/Segmented", className: `tag-grey ${size} ${className}`, role: "tablist" }, options.map((opt) => (react_1.default.createElement(SegmentedItem, { key: opt.label, role: "tab", title: opt.label, onClick: () => onChange(opt), "$isActive": value == opt.value, "$size": size }, opt.label)))));
30
+ function SegmentedComponent({ options, onChange, value, className = '', size = 'regular' }, ref) {
31
+ return (react_1.default.createElement(SegmentedGroup, { ref: ref, "data-component-name": "Segmented/Segmented", className: `tag-grey ${size} ${className}`, role: "tablist" }, options.map((opt) => (react_1.default.createElement(SegmentedItem, { key: opt.label, role: "tab", title: opt.label, onClick: () => onChange(opt), "$isActive": value == opt.value, "$size": size }, opt.label)))));
35
32
  }
36
- exports.Segmented = (0, typedMemo_1.typedMemo)(SegmentedComponent);
33
+ exports.Segmented = (0, typedMemo_1.typedMemo)((0, react_1.forwardRef)(SegmentedComponent));
37
34
  const SegmentedGroup = styled_components_1.default.div `
38
35
  display: flex;
39
36
  background: var(--segmented-buttons-bg-color-main);
@@ -21,4 +21,5 @@ export type TagProps = {
21
21
  onClose?: (event: React.MouseEvent) => void;
22
22
  };
23
23
  export declare function Tag({ children, color, icon, active, closable, onClick, onClose, size, borderless, withStatusDot, statusDotColor, ...otherProps }: TagProps): JSX.Element;
24
+ export declare const ContentWrapper: import("styled-components").StyledComponent<"div", any, {}, never>;
24
25
  export {};
@@ -14,6 +14,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
14
14
  return (mod && mod.__esModule) ? mod : { "default": mod };
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.ContentWrapper = void 0;
17
18
  exports.Tag = Tag;
18
19
  const react_1 = __importDefault(require("react"));
19
20
  const styled_components_1 = __importDefault(require("styled-components"));
@@ -23,14 +24,14 @@ function Tag(_a) {
23
24
  var { children, color, icon, active, closable, onClick, onClose, size, borderless, withStatusDot, statusDotColor = 'var(--tag-status-dot-color-default)' } = _a, otherProps = __rest(_a, ["children", "color", "icon", "active", "closable", "onClick", "onClose", "size", "borderless", "withStatusDot", "statusDotColor"]);
24
25
  return (react_1.default.createElement(TagWrapper, Object.assign({ "data-component-name": "Tag/Tag", borderless: borderless, color: color, size: size, onClick: onClick, hasCloseButton: closable }, otherProps),
25
26
  withStatusDot ? react_1.default.createElement(StatusDot, { color: statusDotColor }) : icon ? icon : null,
26
- react_1.default.createElement(ContentWrapper, null, children),
27
+ react_1.default.createElement(exports.ContentWrapper, null, children),
27
28
  closable && (react_1.default.createElement(CloseButton, { onClick: (event) => {
28
29
  onClose === null || onClose === void 0 ? void 0 : onClose(event);
29
30
  } },
30
31
  react_1.default.createElement(CloseIcon_1.CloseIcon, null))),
31
32
  active && react_1.default.createElement(ActiveIcon, null)));
32
33
  }
33
- const ContentWrapper = styled_components_1.default.div `
34
+ exports.ContentWrapper = styled_components_1.default.div `
34
35
  display: inline-flex;
35
36
  align-items: center;
36
37
  justify-content: center;
@@ -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
- getToggleState: () => null,
8
- changeToggleState: () => { },
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, ControlState, ControlTypeValue, ControlType, CodeWalkthroughConditionsObject } from '@redocly/config';
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,15 +6,14 @@ export type ActiveFilter = {
8
6
  };
9
7
  export type WalkthroughControlsState = {
10
8
  activeFilters: ActiveFilter[];
11
- getToggleState: getState<'toggle'>;
12
- changeToggleState: changeState<'toggle'>;
13
- getInputState: getState<'input'>;
14
- changeInputState: changeState<'input'>;
15
- getFilterState: getState<'filter'>;
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;
20
17
  populateInputsWithValue: (node: string) => string;
21
18
  };
22
- export declare function useCodeWalkthroughControls(filters: Record<string, CodeWalkthroughFilter>, inputs: InputsMarkdocAttr, toggles: TogglesMarkdocAttr): WalkthroughControlsState;
19
+ export declare function useCodeWalkthroughControls(filters: Record<string, CodeWalkthroughFilter>, inputs: InputsMarkdocAttr, toggles: TogglesMarkdocAttr, enableDeepLink: boolean): WalkthroughControlsState;
@@ -9,130 +9,98 @@ const defaultControlsValues = {
9
9
  toggle: false,
10
10
  filter: '',
11
11
  };
12
- function useCodeWalkthroughControls(filters, inputs, toggles) {
12
+ 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 [togglesState, setTogglesState] = (0, react_1.useState)(() => {
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
- initialState[id] = Object.assign(Object.assign({}, toggle), { render: true, type: 'toggle', value: searchParams.get(id) === 'true' });
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
- const initialState = {};
48
22
  for (const [id, input] of Object.entries(inputs)) {
49
- initialState[id] = Object.assign(Object.assign({}, input), { render: true, type: 'input', value: searchParams.get(id) || input.value });
50
- }
51
- return initialState;
52
- });
53
- const changeInputState = (inputId, value) => {
54
- setInputsState((prev) => {
55
- const input = prev[inputId];
56
- if (input) {
57
- return Object.assign(Object.assign({}, prev), { [inputId]: Object.assign(Object.assign({}, input), { value }) });
58
- }
59
- else {
60
- return prev;
61
- }
62
- });
63
- };
64
- const getInputState = (inputId) => {
65
- const inputState = inputsState[inputId];
66
- if (inputState) {
67
- return {
68
- render: inputState.render,
69
- value: inputState.value,
70
- };
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 });
71
24
  }
72
- else {
73
- return null;
74
- }
75
- };
76
- const [filtersState, setFiltersState] = (0, react_1.useState)(() => {
77
- var _a, _b;
78
- const initialState = {};
79
25
  for (const [id, filter] of Object.entries(filters)) {
80
- initialState[id] = Object.assign(Object.assign({}, filter), { render: true, type: 'filter', value: searchParams.get(id) || ((_b = (_a = filter === null || filter === void 0 ? void 0 : filter.items) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.value) || '' });
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 });
81
28
  }
82
29
  return initialState;
83
30
  });
84
- const changeFilterState = (filterId, value) => {
85
- setFiltersState((prev) => {
86
- const filter = prev[filterId];
87
- if (filter) {
88
- return Object.assign(Object.assign({}, prev), { [filterId]: Object.assign(Object.assign({}, filter), { value }) });
89
- }
90
- 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.`);
91
36
  return prev;
92
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 }) });
93
62
  });
94
63
  };
95
- const getFilterState = (filterId) => {
96
- const filterState = filtersState[filterId];
97
- if (filterState) {
64
+ const getControlState = (id) => {
65
+ const controlState = controlsState[id];
66
+ if (controlState) {
98
67
  return {
99
- render: filterState.render,
100
- value: filterState.value,
68
+ render: controlState.render,
69
+ value: controlState.value,
101
70
  };
102
71
  }
103
72
  else {
104
73
  return null;
105
74
  }
106
75
  };
107
- const state = Object.assign(Object.assign(Object.assign({}, filtersState), togglesState), inputsState);
108
76
  const walkthroughContext = (0, react_1.useMemo)(() => {
109
- const areConditionsMet = (conditions) => (0, utils_1.matchCodeWalkthroughConditions)(conditions, state);
110
- for (const [id, element] of Object.entries(state)) {
111
- if (element && !areConditionsMet(element)) {
112
- state[id].render = false;
113
- state[id].value = defaultControlsValues[element.type];
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];
114
82
  }
115
83
  }
116
84
  const activeFilters = [];
117
85
  for (const [id, filter] of Object.entries(filters)) {
118
- if (!filtersState[id].render) {
86
+ if (!controlsState[id].render) {
119
87
  continue;
120
88
  }
121
89
  // code-walk-todo: need to check if we have a default fallback
122
90
  const items = Array.isArray(filter === null || filter === void 0 ? void 0 : filter.items) ? filter.items : [];
123
91
  const activeItems = items.filter((item) => areConditionsMet(item));
124
92
  if (activeItems.length === 0) {
125
- filtersState[id].render = false;
126
- filtersState[id].value = defaultControlsValues['filter'];
93
+ controlsState[id].render = false;
94
+ controlsState[id].value = defaultControlsValues['filter'];
127
95
  continue;
128
96
  }
129
- const currentValue = filtersState[id].value;
97
+ const currentValue = controlsState[id].value;
130
98
  if (currentValue) {
131
99
  const isValueInActiveItems = activeItems.findIndex(({ value }) => value === currentValue) !== -1;
132
- filtersState[id].value = isValueInActiveItems ? currentValue : activeItems[0].value;
100
+ controlsState[id].value = isValueInActiveItems ? currentValue : activeItems[0].value;
133
101
  }
134
102
  else {
135
- filtersState[id].value = activeItems[0].value;
103
+ controlsState[id].value = activeItems[0].value;
136
104
  }
137
105
  activeFilters.push({
138
106
  id,
@@ -140,8 +108,9 @@ function useCodeWalkthroughControls(filters, inputs, toggles) {
140
108
  items: activeItems,
141
109
  });
142
110
  }
143
- const handleDownloadCode = (files) => (0, utils_1.downloadCodeWalkthrough)(files, state, inputsState);
144
- const getFileText = (file) => (0, utils_1.getCodeWalkthroughFileText)(file, state, inputsState);
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);
145
114
  const populateInputsWithValue = (node) => (0, utils_1.replaceInputsWithValue)(node, inputsState);
146
115
  return {
147
116
  activeFilters,
@@ -150,15 +119,16 @@ function useCodeWalkthroughControls(filters, inputs, toggles) {
150
119
  getFileText,
151
120
  populateInputsWithValue,
152
121
  };
153
- // Ignore state in dependency array as it's simply a combination of toggles, filters and inputs.
154
- // eslint-disable-next-line react-hooks/exhaustive-deps
155
- }, [filters, filtersState, togglesState, inputsState]);
122
+ }, [filters, controlsState]);
156
123
  /**
157
124
  * Update the URL search params with the current state of the filters and inputs
158
125
  */
159
126
  (0, react_1.useEffect)(() => {
127
+ if (!enableDeepLink) {
128
+ return;
129
+ }
160
130
  const newSearchParams = new URLSearchParams(Array.from(searchParams.entries()));
161
- for (const [id, { value }] of Object.entries(state)) {
131
+ for (const [id, { value }] of Object.entries(controlsState)) {
162
132
  if (value) {
163
133
  newSearchParams.set(id, value.toString());
164
134
  }
@@ -172,12 +142,8 @@ function useCodeWalkthroughControls(filters, inputs, toggles) {
172
142
  navigate({ search: newSearch });
173
143
  // Ignore searchParams in dependency array to avoid infinite re-renders
174
144
  // eslint-disable-next-line react-hooks/exhaustive-deps
175
- }, [filters, filtersState, togglesState, inputsState, navigate, location, state]);
176
- return Object.assign({ getInputState,
177
- changeInputState,
178
- getToggleState,
179
- changeToggleState,
180
- getFilterState,
181
- changeFilterState }, walkthroughContext);
145
+ }, [filters, controlsState, navigate, location]);
146
+ return Object.assign({ changeControlState,
147
+ getControlState }, walkthroughContext);
182
148
  }
183
149
  //# sourceMappingURL=use-code-walkthrough-controls.js.map
@@ -1,7 +1,6 @@
1
1
  import type { CodeWalkthroughStepAttr } from '@redocly/config';
2
2
  type ActiveStep = string | null;
3
3
  type CodeWalkthroughStep = CodeWalkthroughStepAttr & {
4
- active?: boolean;
5
4
  compRef?: HTMLElement;
6
5
  };
7
6
  export type WalkthroughStepsState = {
@@ -12,5 +11,5 @@ export type WalkthroughStepsState = {
12
11
  lockObserver?: React.MutableRefObject<boolean>;
13
12
  filtersElementRef?: React.RefObject<HTMLDivElement>;
14
13
  };
15
- export declare function useCodeWalkthroughSteps(steps: CodeWalkthroughStep[]): WalkthroughStepsState;
14
+ export declare function useCodeWalkthroughSteps(steps: CodeWalkthroughStep[], enableDeepLink: boolean): WalkthroughStepsState;
16
15
  export {};
@@ -5,7 +5,7 @@ const react_1 = require("react");
5
5
  const react_router_dom_1 = require("react-router-dom");
6
6
  const utils_1 = require("../../../core/utils");
7
7
  const constants_1 = require("../../../core/constants");
8
- function useCodeWalkthroughSteps(steps) {
8
+ function useCodeWalkthroughSteps(steps, enableDeepLink) {
9
9
  const location = (0, react_router_dom_1.useLocation)();
10
10
  const navigate = (0, react_router_dom_1.useNavigate)();
11
11
  const searchParams = (0, react_1.useMemo)(() => new URLSearchParams(location.search), [location.search]);
@@ -14,7 +14,7 @@ function useCodeWalkthroughSteps(steps) {
14
14
  const lockObserver = (0, react_1.useRef)(false);
15
15
  // Track observed elements in case new observer needs to be created
16
16
  const observedElementsRef = (0, react_1.useRef)(new Set());
17
- const [activeStep, setActiveStep] = (0, react_1.useState)(searchParams.get(constants_1.ACTIVE_STEP_QUERY_PARAM));
17
+ const [activeStep, setActiveStep] = (0, react_1.useState)(enableDeepLink ? searchParams.get(constants_1.ACTIVE_STEP_QUERY_PARAM) : null);
18
18
  const register = (0, react_1.useCallback)((element) => {
19
19
  // for some reason, the observer is not ready immediately
20
20
  setTimeout(() => {
@@ -43,27 +43,29 @@ function useCodeWalkthroughSteps(steps) {
43
43
  if (lockObserver.current) {
44
44
  return;
45
45
  }
46
- const stepsEntries = [];
46
+ const renderedSteps = steps.filter((step) => Boolean(step.compRef));
47
+ if (renderedSteps.length < 2) {
48
+ setActiveStep(((_a = renderedSteps[0]) === null || _a === void 0 ? void 0 : _a.id) || null);
49
+ return;
50
+ }
47
51
  for (const entry of entries) {
48
- const { target } = entry;
49
- const stepKey = Number((_a = target === null || target === void 0 ? void 0 : target.dataset) === null || _a === void 0 ? void 0 : _a.stepKey);
50
- const stepActive = ((_b = target === null || target === void 0 ? void 0 : target.dataset) === null || _b === void 0 ? void 0 : _b.stepActive) === 'true';
52
+ const stepKey = Number((_c = (_b = entry.target) === null || _b === void 0 ? void 0 : _b.dataset) === null || _c === void 0 ? void 0 : _c.stepKey);
51
53
  if (!Number.isInteger(stepKey) || stepKey < 0) {
52
54
  continue;
53
55
  }
56
+ const { intersectionRatio, boundingClientRect, rootBounds, isIntersecting } = entry;
54
57
  const step = steps[stepKey];
55
- step.active = stepActive;
56
- stepsEntries.push(entry);
57
- }
58
- for (const stepEntry of stepsEntries) {
59
- const { target, intersectionRatio, boundingClientRect, rootBounds, isIntersecting } = stepEntry;
60
- const stepKey = Number((_c = target === null || target === void 0 ? void 0 : target.dataset) === null || _c === void 0 ? void 0 : _c.stepKey);
61
- const step = steps[stepKey];
62
- const renderedSteps = steps.filter((step) => Boolean(step.compRef));
63
58
  const stepIndex = renderedSteps.findIndex((renderedStep) => renderedStep.stepKey === step.stepKey);
64
59
  const { next } = (0, utils_1.getAdjacentValues)(renderedSteps, stepIndex);
65
60
  const intersectionAtTop = (rootBounds === null || rootBounds === void 0 ? void 0 : rootBounds.bottom) !== undefined && boundingClientRect.top < rootBounds.top;
66
61
  const stepGoesIn = isIntersecting;
62
+ if (intersectionRatio > 0.8 &&
63
+ intersectionRatio < 1 &&
64
+ intersectionAtTop &&
65
+ activeStep === null) {
66
+ setActiveStep(step.id);
67
+ break;
68
+ }
67
69
  if (intersectionRatio < 1 && intersectionRatio !== 0 && intersectionAtTop) {
68
70
  let newStep = null;
69
71
  if (stepGoesIn) {
@@ -84,7 +86,7 @@ function useCodeWalkthroughSteps(steps) {
84
86
  const filtersElementHeight = ((_a = filtersElementRef.current) === null || _a === void 0 ? void 0 : _a.clientHeight) || 0;
85
87
  const navbarHeight = ((_b = document.querySelector('nav')) === null || _b === void 0 ? void 0 : _b.clientHeight) || 0;
86
88
  const newObserver = new IntersectionObserver(observerCallback, {
87
- threshold: [0.8],
89
+ threshold: [0.8, 0.85, 0.9, 0.95],
88
90
  rootMargin: `-${filtersElementHeight + navbarHeight}px 0px 0px 0px`,
89
91
  });
90
92
  for (const observedElement of observedElementsRef.current) {
@@ -98,6 +100,9 @@ function useCodeWalkthroughSteps(steps) {
98
100
  * Update the URL search params with the current state of the filters and inputs
99
101
  */
100
102
  (0, react_1.useEffect)(() => {
103
+ if (!enableDeepLink) {
104
+ return;
105
+ }
101
106
  const newSearchParams = new URLSearchParams(Array.from(searchParams.entries()));
102
107
  if (activeStep) {
103
108
  newSearchParams.set(constants_1.ACTIVE_STEP_QUERY_PARAM, activeStep);
@@ -1,4 +1,4 @@
1
- import type { CodeWalkthroughFileset, CodeWalkthroughFile, CodeWalkthroughStepAttr, CodeWalkthroughFilter, InputsMarkdocAttr, TogglesMarkdocAttr } from '@redocly/config';
1
+ import type { CodeWalkthroughFile, CodeWalkthroughStepAttr, CodeWalkthroughAttr } from '@redocly/config';
2
2
  import { type WalkthroughControlsState, type WalkthroughStepsState } from '../../../core/hooks';
3
3
  export type WalkthroughState = {
4
4
  stepsState: WalkthroughStepsState;
@@ -6,9 +6,4 @@ export type WalkthroughState = {
6
6
  downloadAssociatedFiles: CodeWalkthroughFile[];
7
7
  files: CodeWalkthroughFile[];
8
8
  };
9
- export declare function useCodeWalkthrough(steps: CodeWalkthroughStepAttr[], attributes: {
10
- filters: Record<string, CodeWalkthroughFilter>;
11
- filesets: CodeWalkthroughFileset[];
12
- inputs: InputsMarkdocAttr;
13
- toggles: TogglesMarkdocAttr;
14
- }): WalkthroughState;
9
+ export declare function useCodeWalkthrough(steps: CodeWalkthroughStepAttr[], attributes: Omit<CodeWalkthroughAttr, 'steps' | 'preview'>): WalkthroughState;
@@ -3,9 +3,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.useCodeWalkthrough = useCodeWalkthrough;
4
4
  const hooks_1 = require("../../../core/hooks");
5
5
  function useCodeWalkthrough(steps, attributes) {
6
- const { filters, filesets, inputs, toggles } = attributes;
7
- const stepsState = (0, hooks_1.useCodeWalkthroughSteps)(steps);
8
- const controlsState = (0, hooks_1.useCodeWalkthroughControls)(filters, inputs, toggles);
6
+ const { filters, filesets, inputs, toggles, __idx } = attributes;
7
+ /*
8
+ We only enable deep linking for the first CodeWalkthrough,
9
+ because we don't expect more than one on the same page.
10
+ Any subsequent walkthroughs have it disabled to avoid
11
+ collisions/conflicts in the URL.
12
+ */
13
+ const enableDeepLink = __idx === 1;
14
+ const stepsState = (0, hooks_1.useCodeWalkthroughSteps)(steps, enableDeepLink);
15
+ const controlsState = (0, hooks_1.useCodeWalkthroughControls)(filters, inputs, toggles, enableDeepLink);
9
16
  const files = filesets
10
17
  .filter((fileset) => controlsState.areConditionsMet(fileset))
11
18
  .flatMap((fileset) => fileset.files || []);
@@ -0,0 +1,9 @@
1
+ import type { CodeWalkthroughFile } from '@redocly/config';
2
+ import type { IconProps } from '../../../icons/types';
3
+ export type RenderableFile = CodeWalkthroughFile & {
4
+ FileIcon: React.FunctionComponent<IconProps>;
5
+ parentFolder: string;
6
+ isNameDuplicate: boolean;
7
+ inRootDir: boolean;
8
+ };
9
+ export declare function useRenderableFiles(files: CodeWalkthroughFile[]): RenderableFile[];