@redocly/theme 0.70.0-next.1 → 0.70.0-next.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/lib/components/CodeBlock/CodeBlockControls.js +3 -5
  2. package/lib/components/Consent/Consent.d.ts +2 -0
  3. package/lib/components/Consent/Consent.js +17 -0
  4. package/lib/components/Consent/ConsentBanner.d.ts +2 -0
  5. package/lib/components/Consent/ConsentBanner.js +120 -0
  6. package/lib/components/Consent/ConsentDialog.d.ts +2 -0
  7. package/lib/components/Consent/ConsentDialog.js +128 -0
  8. package/lib/components/Consent/ConsentSettingsLink.d.ts +2 -0
  9. package/lib/components/Consent/ConsentSettingsLink.js +35 -0
  10. package/lib/components/Consent/variables.d.ts +1 -0
  11. package/lib/components/Consent/variables.js +31 -0
  12. package/lib/components/Footer/Footer.js +4 -1
  13. package/lib/components/JsonViewer/Helpers.d.ts +10 -2
  14. package/lib/components/JsonViewer/Helpers.js +29 -33
  15. package/lib/components/JsonViewer/JsonViewer.js +15 -18
  16. package/lib/components/Markdown/styles/base-table.js +14 -1
  17. package/lib/components/Markdown/variables.js +1 -0
  18. package/lib/components/Product/utils.js +1 -1
  19. package/lib/components/Search/SearchAiConversationInput.js +10 -6
  20. package/lib/components/Search/SearchAiDialog.js +8 -1
  21. package/lib/components/Search/SearchAiMessage.js +1 -1
  22. package/lib/components/Search/SearchAiResponse.js +1 -0
  23. package/lib/components/Search/SearchItem.js +9 -1
  24. package/lib/core/constants/consent.d.ts +2 -0
  25. package/lib/core/constants/consent.js +30 -0
  26. package/lib/core/constants/index.d.ts +1 -0
  27. package/lib/core/constants/index.js +1 -0
  28. package/lib/core/constants/search.d.ts +1 -1
  29. package/lib/core/constants/search.js +1 -1
  30. package/lib/core/hooks/index.d.ts +2 -0
  31. package/lib/core/hooks/index.js +2 -0
  32. package/lib/core/hooks/use-consent-dialog.d.ts +12 -0
  33. package/lib/core/hooks/use-consent-dialog.js +36 -0
  34. package/lib/core/hooks/use-consent-telemetry.d.ts +1 -0
  35. package/lib/core/hooks/use-consent-telemetry.js +42 -0
  36. package/lib/core/hooks/use-dialog-hotkeys.js +4 -2
  37. package/lib/core/hooks/use-theme-hooks.js +14 -0
  38. package/lib/core/styles/global.js +42 -40
  39. package/lib/core/types/consent.d.ts +54 -0
  40. package/lib/core/types/consent.js +3 -0
  41. package/lib/core/types/hooks.d.ts +2 -0
  42. package/lib/core/types/index.d.ts +1 -0
  43. package/lib/core/types/l10n.d.ts +1 -1
  44. package/lib/core/types/search.d.ts +2 -0
  45. package/lib/core/utils/consent-settings-link-placement.d.ts +3 -0
  46. package/lib/core/utils/consent-settings-link-placement.js +15 -0
  47. package/lib/core/utils/index.d.ts +1 -0
  48. package/lib/core/utils/index.js +1 -0
  49. package/lib/core/utils/mcp.js +1 -1
  50. package/lib/core/utils/menu.js +6 -5
  51. package/lib/core/utils/urls.js +3 -1
  52. package/lib/icons/ExpandCollapseIcon/ExpandCollapseIcon.d.ts +29 -0
  53. package/lib/icons/ExpandCollapseIcon/ExpandCollapseIcon.js +81 -0
  54. package/lib/index.d.ts +1 -0
  55. package/lib/index.js +1 -0
  56. package/package.json +3 -3
  57. package/src/components/CodeBlock/CodeBlockControls.tsx +4 -7
  58. package/src/components/Consent/Consent.tsx +18 -0
  59. package/src/components/Consent/ConsentBanner.tsx +110 -0
  60. package/src/components/Consent/ConsentDialog.tsx +210 -0
  61. package/src/components/Consent/ConsentSettingsLink.tsx +44 -0
  62. package/src/components/Consent/variables.ts +28 -0
  63. package/src/components/Footer/Footer.tsx +2 -0
  64. package/src/components/JsonViewer/Helpers.tsx +46 -51
  65. package/src/components/JsonViewer/JsonViewer.tsx +23 -25
  66. package/src/components/Markdown/styles/base-table.ts +14 -1
  67. package/src/components/Markdown/variables.ts +1 -0
  68. package/src/components/Product/utils.ts +1 -1
  69. package/src/components/Search/SearchAiConversationInput.tsx +13 -7
  70. package/src/components/Search/SearchAiDialog.tsx +16 -1
  71. package/src/components/Search/SearchAiMessage.tsx +1 -1
  72. package/src/components/Search/SearchAiResponse.tsx +1 -0
  73. package/src/components/Search/SearchItem.tsx +14 -0
  74. package/src/core/constants/consent.ts +29 -0
  75. package/src/core/constants/index.ts +1 -0
  76. package/src/core/constants/search.ts +1 -1
  77. package/src/core/hooks/__mocks__/use-theme-hooks.ts +15 -0
  78. package/src/core/hooks/code-walkthrough/__mocks__/MockIntersectionObserver.ts +1 -2
  79. package/src/core/hooks/code-walkthrough/__mocks__/mock-intersection-observer.ts +1 -2
  80. package/src/core/hooks/index.ts +2 -0
  81. package/src/core/hooks/use-consent-dialog.ts +51 -0
  82. package/src/core/hooks/use-consent-telemetry.ts +56 -0
  83. package/src/core/hooks/use-dialog-hotkeys.ts +11 -5
  84. package/src/core/hooks/use-theme-hooks.ts +14 -0
  85. package/src/core/styles/global.ts +2 -0
  86. package/src/core/types/consent.ts +51 -0
  87. package/src/core/types/hooks.ts +2 -0
  88. package/src/core/types/index.ts +1 -0
  89. package/src/core/types/l10n.ts +25 -1
  90. package/src/core/types/search.ts +2 -0
  91. package/src/core/utils/consent-settings-link-placement.ts +18 -0
  92. package/src/core/utils/index.ts +1 -0
  93. package/src/core/utils/mcp.ts +1 -1
  94. package/src/core/utils/menu.ts +5 -6
  95. package/src/core/utils/urls.ts +3 -1
  96. package/src/icons/ExpandCollapseIcon/ExpandCollapseIcon.tsx +56 -0
  97. package/src/index.ts +1 -0
@@ -5,6 +5,6 @@ function getProductClassName(productName) {
5
5
  return `product-${productName
6
6
  .toLowerCase()
7
7
  .replace(/[^a-z0-9]+/g, '-')
8
- .replace(/^-+|-+$/g, '')}`;
8
+ .replace(/^-+|(?<!-)-+$/g, '')}`;
9
9
  }
10
10
  //# sourceMappingURL=utils.js.map
@@ -48,17 +48,21 @@ function SearchAiConversationInput({ isGeneratingResponse, onMessageSent, classN
48
48
  const { useTranslate } = (0, hooks_1.useThemeHooks)();
49
49
  const { translate } = useTranslate();
50
50
  const inputRef = (0, react_1.useRef)(null);
51
+ const wasGeneratingResponseRef = (0, react_1.useRef)(isGeneratingResponse);
51
52
  const [query, setQuery] = (0, react_1.useState)('');
52
53
  (0, react_1.useEffect)(() => {
53
- if (!isGeneratingResponse && inputRef.current) {
54
+ const wasGeneratingResponse = wasGeneratingResponseRef.current;
55
+ wasGeneratingResponseRef.current = isGeneratingResponse;
56
+ if (wasGeneratingResponse && !isGeneratingResponse) {
54
57
  requestAnimationFrame(() => {
55
58
  var _a;
56
59
  (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
57
60
  });
58
61
  }
59
62
  }, [isGeneratingResponse]);
63
+ const isDisabled = disabled || isGeneratingResponse || query.trim().length === 0;
60
64
  const handleSendMessage = () => {
61
- if (disabled)
65
+ if (isDisabled)
62
66
  return;
63
67
  setQuery('');
64
68
  onMessageSent(query);
@@ -72,7 +76,6 @@ function SearchAiConversationInput({ isGeneratingResponse, onMessageSent, classN
72
76
  handleSendMessage();
73
77
  }
74
78
  };
75
- const isDisabled = disabled || isGeneratingResponse || query.trim().length === 0;
76
79
  const commonProps = {
77
80
  placeholder: placeholder || translate('search.ai.followUpQuestion', 'Ask a follow up question?'),
78
81
  onChange: (e) => setQuery(e.target.value),
@@ -83,7 +86,7 @@ function SearchAiConversationInput({ isGeneratingResponse, onMessageSent, classN
83
86
  };
84
87
  return (react_1.default.createElement(SearchAiConversationInputWrapper, { "data-component-name": "Search/SearchAiConversationInput", className: className },
85
88
  multiline ? (react_1.default.createElement(ConversationTextarea, Object.assign({ ref: inputRef }, commonProps))) : (react_1.default.createElement(ConversationInput, Object.assign({ ref: inputRef, type: "text" }, commonProps))),
86
- react_1.default.createElement(SendButton, { disabled: isDisabled, size: "small", icon: react_1.default.createElement(SendIcon_1.SendIcon, { color: isDisabled
89
+ react_1.default.createElement(SendButton, { "aria-label": translate('search.ai.send', 'Send question'), "aria-disabled": isDisabled, size: "small", icon: react_1.default.createElement(SendIcon_1.SendIcon, { color: isDisabled
87
90
  ? '--button-content-color-disabled'
88
91
  : 'var(--search-ai-conversation-input-send-button-icon-color)' }), onClick: handleSendMessage })));
89
92
  }
@@ -151,11 +154,12 @@ const SendButton = (0, styled_components_1.default)(Button_1.Button) `
151
154
  border-radius: var(--search-ai-conversation-input-send-button-border-radius);
152
155
  padding: var(--search-ai-conversation-input-send-button-padding);
153
156
 
154
- &:hover {
157
+ &:hover:not([aria-disabled='true']) {
155
158
  background-color: var(--search-ai-conversation-input-send-button-bg-color-hover);
156
159
  }
157
160
 
158
- &:disabled {
161
+ &[aria-disabled='true'] {
162
+ cursor: default;
159
163
  background-color: var(--search-ai-conversation-input-send-button-bg-color-disabled);
160
164
  border: var(--search-ai-conversation-input-send-button-border-disabled);
161
165
  }
@@ -52,6 +52,13 @@ function SearchAiDialog({ isGeneratingResponse, response, initialMessage, error,
52
52
  const { aiAssistant } = (0, hooks_1.useThemeConfig)();
53
53
  const { translate } = useTranslate();
54
54
  const conversationEndRef = react_1.default.useRef(null);
55
+ const wrapperRef = react_1.default.useRef(null);
56
+ (0, react_1.useEffect)(() => {
57
+ requestAnimationFrame(() => {
58
+ var _a, _b;
59
+ (_b = (_a = wrapperRef.current) === null || _a === void 0 ? void 0 : _a.querySelector('[data-component-name="Search/SearchAiConversationInput"] :is(input, textarea):not(:disabled)')) === null || _b === void 0 ? void 0 : _b.focus();
60
+ });
61
+ }, []);
55
62
  const suggestions = (_a = aiAssistant === null || aiAssistant === void 0 ? void 0 : aiAssistant.suggestions) !== null && _a !== void 0 ? _a : [];
56
63
  const placeholder = isGeneratingResponse
57
64
  ? translate('search.ai.generatingResponse', 'Generating response...')
@@ -139,7 +146,7 @@ function SearchAiDialog({ isGeneratingResponse, response, initialMessage, error,
139
146
  const handleFeedbackChange = (0, react_1.useCallback)((messageId, feedback) => {
140
147
  setConversation((prev) => prev.map((item) => (item.messageId === messageId ? Object.assign(Object.assign({}, item), { feedback }) : item)));
141
148
  }, [setConversation]);
142
- return (react_1.default.createElement(SearchAiDialogWrapper, { "data-component-name": "Search/SearchAiDialog", className: className },
149
+ return (react_1.default.createElement(SearchAiDialogWrapper, { ref: wrapperRef, "data-component-name": "Search/SearchAiDialog", className: className },
143
150
  !conversation.length && (react_1.default.createElement(WelcomeWrapper, null,
144
151
  react_1.default.createElement(AiStarsIcon_1.AiStarsIcon, { color: "var(--search-ai-icon-color)", size: "32px", background: "var(--search-ai-icon-bg-color)", borderRadius: "var(--border-radius-lg)", margin: "0 var(--spacing-xs) 0 0" }),
145
152
  translate('search.ai.welcomeText', 'Welcome to AI assistant! Feel free to ask me anything. How can I help you? '))),
@@ -251,7 +251,7 @@ const ResourceTagsWrapper = styled_components_1.default.div `
251
251
  flex-wrap: wrap;
252
252
  gap: var(--search-ai-resource-tags-gap);
253
253
  `;
254
- const ResourceTag = (0, styled_components_1.default)(Tag_1.Tag) `
254
+ const ResourceTag = (0, styled_components_1.default)(Tag_1.Tag).attrs({ textTransform: 'none' }) `
255
255
  .tag-default {
256
256
  --tag-color: var(--search-ai-resource-tag-text-color);
257
257
  max-width: 100%;
@@ -141,6 +141,7 @@ const ResourceTags = styled_components_1.default.div `
141
141
  `;
142
142
  const ResourceTag = (0, styled_components_1.default)(Tag_1.Tag).attrs({
143
143
  className: 'tag-resource',
144
+ textTransform: 'none',
144
145
  }) `
145
146
  .tag-default {
146
147
  --tag-color: --search-ai-resource-tag-text-color;
@@ -99,7 +99,15 @@ function SearchItem({ onClick, item, className, product, innerRef, }) {
99
99
  (0, SearchHighlight_1.searchHighlight)(parameter.path.join(' → ')),
100
100
  ` → `)) : null,
101
101
  (0, SearchHighlight_1.searchHighlight)(parameter.name)),
102
- react_1.default.createElement("div", null, (0, SearchHighlight_1.searchHighlight)(parameter.description)))) : (shouldShowPath && (react_1.default.createElement(SearchItemPath, null, highlight.path && highlight.path.length
102
+ react_1.default.createElement("div", null, (0, SearchHighlight_1.searchHighlight)(parameter.description)),
103
+ parameter.enum ? (react_1.default.createElement("div", null,
104
+ translate('search.searchItem.enum', 'Enum'),
105
+ ': ',
106
+ (0, SearchHighlight_1.searchHighlight)(parameter.enum))) : null,
107
+ parameter.example ? (react_1.default.createElement("div", null,
108
+ translate('search.searchItem.example', 'Example'),
109
+ ': ',
110
+ (0, SearchHighlight_1.searchHighlight)(parameter.example))) : null)) : (shouldShowPath && (react_1.default.createElement(SearchItemPath, null, highlight.path && highlight.path.length
103
111
  ? (0, SearchHighlight_1.searchHighlight)(highlight.path.join(' → '))
104
112
  : (_c = document.path) === null || _c === void 0 ? void 0 : _c.join(' → '))))),
105
113
  react_1.default.createElement(ReturnKeyIcon_1.ReturnKeyIcon, { color: "var(--search-item-text-color)" })));
@@ -0,0 +1,2 @@
1
+ import type { ConsentCategory } from '../types/consent';
2
+ export declare const CONSENT_CATEGORIES: readonly ConsentCategory[];
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CONSENT_CATEGORIES = void 0;
4
+ exports.CONSENT_CATEGORIES = [
5
+ {
6
+ id: 'necessary',
7
+ locked: true,
8
+ titleTranslationKey: 'consent.category.necessary.title',
9
+ descriptionTranslationKey: 'consent.category.necessary.description',
10
+ titleDefault: 'Necessary',
11
+ descriptionDefault: 'Needed for the site to work: your consent choice, your color mode, and anonymous usage data.',
12
+ },
13
+ {
14
+ id: 'analytics',
15
+ locked: false,
16
+ titleTranslationKey: 'consent.category.analytics.title',
17
+ descriptionTranslationKey: 'consent.category.analytics.description',
18
+ titleDefault: 'Analytics',
19
+ descriptionDefault: 'Helps us understand which pages are useful.',
20
+ },
21
+ {
22
+ id: 'marketing',
23
+ locked: false,
24
+ titleTranslationKey: 'consent.category.marketing.title',
25
+ descriptionTranslationKey: 'consent.category.marketing.description',
26
+ titleDefault: 'Marketing',
27
+ descriptionDefault: 'Lets us measure which campaigns bring visitors to this site.',
28
+ },
29
+ ];
30
+ //# sourceMappingURL=consent.js.map
@@ -10,3 +10,4 @@ export * from './mcp';
10
10
  export * from './feedback';
11
11
  export * from './heading';
12
12
  export * from './markdown';
13
+ export * from './consent';
@@ -26,4 +26,5 @@ __exportStar(require("./mcp"), exports);
26
26
  __exportStar(require("./feedback"), exports);
27
27
  __exportStar(require("./heading"), exports);
28
28
  __exportStar(require("./markdown"), exports);
29
+ __exportStar(require("./consent"), exports);
29
30
  //# sourceMappingURL=index.js.map
@@ -13,7 +13,7 @@ export declare const AiSearchConversationRole: {
13
13
  export type AiSearchConversationRole = (typeof AiSearchConversationRole)[keyof typeof AiSearchConversationRole];
14
14
  export declare const AI_SEARCH_ERROR_CONFIG: Record<AiSearchError, AiSearchErrorConfig>;
15
15
  export declare const AI_SEARCH_MAX_MESSAGE_LENGTH = 45000;
16
- export declare const SEARCH_DEBOUNCE_TIME_MS = 300;
16
+ export declare const SEARCH_DEBOUNCE_TIME_MS = 500;
17
17
  export declare const DEFAULT_KEYBOARD_SHORTCUTS = "\u2318+K,CTRL+K";
18
18
  export declare const TOOL_CALL_DISPLAY_TEXT: Partial<Record<string, {
19
19
  inProgressText: string;
@@ -38,7 +38,7 @@ exports.AI_SEARCH_ERROR_CONFIG = {
38
38
  [AiSearchError.ErrorProcessingResponse]: defaultErrorConfig,
39
39
  };
40
40
  exports.AI_SEARCH_MAX_MESSAGE_LENGTH = 45000;
41
- exports.SEARCH_DEBOUNCE_TIME_MS = 300;
41
+ exports.SEARCH_DEBOUNCE_TIME_MS = 500;
42
42
  exports.DEFAULT_KEYBOARD_SHORTCUTS = '⌘+K,CTRL+K';
43
43
  exports.TOOL_CALL_DISPLAY_TEXT = {
44
44
  [search_1.ToolCallName.SearchDocumentation]: {
@@ -22,6 +22,8 @@ export * from './menu/use-collapse';
22
22
  export * from './feedback/use-report-dialog';
23
23
  export * from './use-tabs';
24
24
  export * from './use-focus-trap';
25
+ export * from './use-consent-dialog';
26
+ export * from './use-consent-telemetry';
25
27
  export * from './use-language-picker';
26
28
  export * from './use-product-picker';
27
29
  export * from './search/use-search-dialog';
@@ -38,6 +38,8 @@ __exportStar(require("./menu/use-collapse"), exports);
38
38
  __exportStar(require("./feedback/use-report-dialog"), exports);
39
39
  __exportStar(require("./use-tabs"), exports);
40
40
  __exportStar(require("./use-focus-trap"), exports);
41
+ __exportStar(require("./use-consent-dialog"), exports);
42
+ __exportStar(require("./use-consent-telemetry"), exports);
41
43
  __exportStar(require("./use-language-picker"), exports);
42
44
  __exportStar(require("./use-product-picker"), exports);
43
45
  __exportStar(require("./search/use-search-dialog"), exports);
@@ -0,0 +1,12 @@
1
+ import type { RefObject } from 'react';
2
+ import type { ConsentCategory, ConsentCategoryId, UseConsentResult } from '../types/consent';
3
+ export type ConsentDraft = Record<Exclude<ConsentCategoryId, 'necessary'>, boolean>;
4
+ export type UseConsentDialogResult = {
5
+ draft: ConsentDraft;
6
+ setCategory: (id: keyof ConsentDraft, value: boolean) => void;
7
+ dialogRef: RefObject<HTMLDivElement | null>;
8
+ close: () => void;
9
+ save: () => void;
10
+ categories: readonly ConsentCategory[];
11
+ };
12
+ export declare function useConsentDialog(consent: UseConsentResult): UseConsentDialogResult;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useConsentDialog = useConsentDialog;
4
+ const react_1 = require("react");
5
+ const consent_1 = require("../constants/consent");
6
+ const use_focus_trap_1 = require("./use-focus-trap");
7
+ function useConsentDialog(consent) {
8
+ const [draft, setDraft] = (0, react_1.useState)(() => { var _a, _b; var _c, _d; return ({
9
+ analytics: (_c = (_a = consent.state) === null || _a === void 0 ? void 0 : _a.analytics) !== null && _c !== void 0 ? _c : consent.mode === 'opt-out',
10
+ marketing: ((_d = (_b = consent.state) === null || _b === void 0 ? void 0 : _b.marketing) !== null && _d !== void 0 ? _d : consent.mode === 'opt-out') && !consent.gpc,
11
+ }); });
12
+ const dialogRef = (0, react_1.useRef)(null);
13
+ const close = consent.closeDialog;
14
+ const closeRef = (0, react_1.useRef)(close);
15
+ closeRef.current = close;
16
+ (0, react_1.useEffect)(() => {
17
+ const previous = document.activeElement;
18
+ const onKeyDown = (event) => {
19
+ if (event.key === 'Escape')
20
+ closeRef.current();
21
+ };
22
+ document.addEventListener('keydown', onKeyDown);
23
+ return () => {
24
+ var _a;
25
+ document.removeEventListener('keydown', onKeyDown);
26
+ (_a = previous === null || previous === void 0 ? void 0 : previous.focus) === null || _a === void 0 ? void 0 : _a.call(previous);
27
+ };
28
+ }, []);
29
+ (0, use_focus_trap_1.useFocusTrap)(dialogRef);
30
+ const setCategory = (id, value) => {
31
+ setDraft((current) => (Object.assign(Object.assign({}, current), { [id]: value })));
32
+ };
33
+ const save = () => consent.save(draft);
34
+ return { draft, setCategory, dialogRef, close, save, categories: consent_1.CONSENT_CATEGORIES };
35
+ }
36
+ //# sourceMappingURL=use-consent-dialog.js.map
@@ -0,0 +1 @@
1
+ export declare function useConsentTelemetry(): void;
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useConsentTelemetry = useConsentTelemetry;
4
+ const react_1 = require("react");
5
+ const use_theme_hooks_1 = require("./use-theme-hooks");
6
+ const get_base_data_attributes_1 = require("../utils/telemetry/get-base-data-attributes");
7
+ function isRealmOwned(runtime) {
8
+ return runtime.region.source !== 'provider';
9
+ }
10
+ function getConsentRuntime() {
11
+ var _a;
12
+ if (typeof window === 'undefined')
13
+ return undefined;
14
+ return (_a = window.redocly) === null || _a === void 0 ? void 0 : _a.consent;
15
+ }
16
+ // Sends one consent.changed event per visitor choice in the modes Realm owns.
17
+ function useConsentTelemetry() {
18
+ const { useTelemetry } = (0, use_theme_hooks_1.useThemeHooks)();
19
+ const telemetry = useTelemetry();
20
+ const telemetryRef = (0, react_1.useRef)(telemetry);
21
+ telemetryRef.current = telemetry;
22
+ (0, react_1.useEffect)(() => {
23
+ const found = getConsentRuntime();
24
+ const runtime = found && isRealmOwned(found) ? found : null;
25
+ if (!runtime)
26
+ return;
27
+ return runtime.on('change', (state) => {
28
+ const choice = state.analytics && state.marketing
29
+ ? 'accept'
30
+ : !state.analytics && !state.marketing
31
+ ? 'reject'
32
+ : 'partial';
33
+ const { origin, pathname, search, hash } = window.location;
34
+ const uri = `${origin}${pathname}${search}${hash}`;
35
+ telemetryRef.current.sendConsentChangedMessage([
36
+ Object.assign(Object.assign({}, (0, get_base_data_attributes_1.getBaseDataAttributes)('consent', 'consent')), { choice, mode: runtime.mode, configuredMode: runtime.configuredMode, regionSource: runtime.region.source, gpc: runtime.gpc }),
37
+ (0, get_base_data_attributes_1.getBaseDataAttributes)(uri, 'page', uri),
38
+ ]);
39
+ });
40
+ }, []);
41
+ }
42
+ //# sourceMappingURL=use-consent-telemetry.js.map
@@ -23,8 +23,10 @@ function useDialogHotKeys(ref, onClose) {
23
23
  };
24
24
  const onFocusableElementsHandler = () => {
25
25
  var _a;
26
- const focusableElements = (_a = ref.current) === null || _a === void 0 ? void 0 : _a.querySelectorAll('input, [href], [tabindex]:not([tabindex="-1"])');
27
- if (focusableElements && focusableElements.length > 0) {
26
+ var _b;
27
+ const focusableElements = Array.from((_b = (_a = ref.current) === null || _a === void 0 ? void 0 : _a.querySelectorAll('input, textarea, select, button, [href], [tabindex]:not([tabindex="-1"])')) !== null && _b !== void 0 ? _b : []).filter((element) => !element.hasAttribute('disabled') &&
28
+ (typeof element.checkVisibility !== 'function' || element.checkVisibility()));
29
+ if (focusableElements.length > 0) {
28
30
  firstFocusableRef.current = focusableElements[0];
29
31
  lastFocusableRef.current = focusableElements[focusableElements.length - 1];
30
32
  }
@@ -17,6 +17,20 @@ const fallbacks = {
17
17
  useOtelTelemetry: () => ({ send: () => { } }),
18
18
  useBreadcrumbs: () => ({ breadcrumbs: [], siblings: undefined }),
19
19
  useBanner: () => ({ banner: undefined, dismissBanner: () => { } }),
20
+ useConsent: () => ({
21
+ enabled: false,
22
+ config: undefined,
23
+ mode: undefined,
24
+ state: undefined,
25
+ decided: false,
26
+ gpc: false,
27
+ isDialogOpen: false,
28
+ openDialog: () => { },
29
+ closeDialog: () => { },
30
+ accept: () => { },
31
+ reject: () => { },
32
+ save: () => { },
33
+ }),
20
34
  useCodeHighlight: () => ({ highlight: (rawContent) => rawContent }),
21
35
  useUserMenu: () => ({}),
22
36
  usePageData: () => null,
@@ -30,26 +30,27 @@ const variables_25 = require("../../components/CodeBlock/variables");
30
30
  const variables_26 = require("../../components/Product/variables");
31
31
  const variables_27 = require("../../components/Markdown/variables");
32
32
  const variables_28 = require("../../components/Banner/variables");
33
- const variables_29 = require("../../markdoc/components/Accordion/variables");
34
- const variables_30 = require("../../markdoc/components/Tabs/variables");
35
- const variables_31 = require("../../markdoc/components/NumberedList/variables");
36
- const variables_32 = require("../../markdoc/components/Diagram/variables");
37
- const variables_33 = require("../../components/LastUpdated/variables");
38
- const variables_34 = require("../../components/Logo/variables");
39
- const variables_35 = require("../../components/StatusCode/variables");
40
- const variables_36 = require("../../components/Segmented/variables");
41
- const variables_37 = require("../../components/UserMenu/variables");
42
- const variables_38 = require("../../components/Tags/variables");
43
- const variables_39 = require("../../components/VersionPicker/variables");
44
- const variables_40 = require("../../components/DatePicker/variables");
45
- const variables_41 = require("../../components/Switch/variables");
46
- const variables_42 = require("../../markdoc/components/Cards/variables");
47
- const variables_43 = require("../../markdoc/components/CodeWalkthrough/variables");
48
- const variables_44 = require("../../components/SkipContent/variables");
49
- const variables_45 = require("../../components/PageActions/variables");
50
- const variables_46 = require("../../components/SvgViewer/variables");
51
- const variables_47 = require("../../components/Toast/variables");
52
- const variables_48 = require("../../components/PageNavigation/variables");
33
+ const variables_29 = require("../../components/Consent/variables");
34
+ const variables_30 = require("../../markdoc/components/Accordion/variables");
35
+ const variables_31 = require("../../markdoc/components/Tabs/variables");
36
+ const variables_32 = require("../../markdoc/components/NumberedList/variables");
37
+ const variables_33 = require("../../markdoc/components/Diagram/variables");
38
+ const variables_34 = require("../../components/LastUpdated/variables");
39
+ const variables_35 = require("../../components/Logo/variables");
40
+ const variables_36 = require("../../components/StatusCode/variables");
41
+ const variables_37 = require("../../components/Segmented/variables");
42
+ const variables_38 = require("../../components/UserMenu/variables");
43
+ const variables_39 = require("../../components/Tags/variables");
44
+ const variables_40 = require("../../components/VersionPicker/variables");
45
+ const variables_41 = require("../../components/DatePicker/variables");
46
+ const variables_42 = require("../../components/Switch/variables");
47
+ const variables_43 = require("../../markdoc/components/Cards/variables");
48
+ const variables_44 = require("../../markdoc/components/CodeWalkthrough/variables");
49
+ const variables_45 = require("../../components/SkipContent/variables");
50
+ const variables_46 = require("../../components/PageActions/variables");
51
+ const variables_47 = require("../../components/SvgViewer/variables");
52
+ const variables_48 = require("../../components/Toast/variables");
53
+ const variables_49 = require("../../components/PageNavigation/variables");
53
54
  const palette_1 = require("./palette");
54
55
  const dark_1 = require("./dark");
55
56
  const themeColors = (0, styled_components_1.css) `
@@ -1313,33 +1314,34 @@ exports.styles = (0, styled_components_1.css) `
1313
1314
  ${badges}
1314
1315
  ${variables_28.banner}
1315
1316
  ${borders}
1317
+ ${variables_29.consent}
1316
1318
  ${variables_5.breadcrumbs}
1317
1319
  ${variables_20.button}
1318
1320
  ${variables_21.aiAssistantButton}
1319
- ${variables_42.cards}
1321
+ ${variables_43.cards}
1320
1322
  ${variables_8.catalog}
1321
1323
  ${variables_11.catalogClassic}
1322
1324
  ${variables_25.code}
1323
- ${variables_43.codeWalkthrough}
1325
+ ${variables_44.codeWalkthrough}
1324
1326
  ${docsDropdown}
1325
1327
  ${variables_15.dropdown}
1326
1328
  ${error}
1327
1329
  ${variables_10.filter}
1328
1330
  ${variables_19.footer}
1329
1331
  ${headingsTypography}
1330
- ${variables_38.httpTag}
1332
+ ${variables_39.httpTag}
1331
1333
  ${variables_9.image}
1332
1334
  ${inputs}
1333
1335
  ${variables_1.languagePicker}
1334
- ${variables_33.lastUpdated}
1336
+ ${variables_34.lastUpdated}
1335
1337
  ${links}
1336
1338
  ${loadProgressBar}
1337
- ${variables_34.logo}
1339
+ ${variables_35.logo}
1338
1340
  ${variables_27.markdown}
1339
- ${variables_29.markdownAccordion}
1340
- ${variables_30.markdownTabs}
1341
- ${variables_31.markdownNumberedList}
1342
- ${variables_32.diagram}
1341
+ ${variables_30.markdownAccordion}
1342
+ ${variables_31.markdownTabs}
1343
+ ${variables_32.markdownNumberedList}
1344
+ ${variables_33.diagram}
1343
1345
  ${variables_24.menu}
1344
1346
  ${variables_24.mobileMenu}
1345
1347
  ${modal}
@@ -1358,26 +1360,26 @@ exports.styles = (0, styled_components_1.css) `
1358
1360
  ${variables_7.toc}
1359
1361
  ${variables_16.tooltip}
1360
1362
  ${typography}
1361
- ${variables_37.userMenu}
1362
- ${variables_39.versionPicker}
1363
+ ${variables_38.userMenu}
1364
+ ${variables_40.versionPicker}
1363
1365
  ${zIndexDepth}
1364
1366
  ${scorecardColors}
1365
- ${variables_35.statusCode}
1367
+ ${variables_36.statusCode}
1366
1368
  ${tab}
1367
1369
  ${icon}
1368
1370
  ${tree}
1369
- ${variables_36.segmented}
1370
- ${variables_41.switcher}
1371
+ ${variables_37.segmented}
1372
+ ${variables_42.switcher}
1371
1373
  ${variables_17.checkbox}
1372
1374
  ${variables_3.feedback}
1373
1375
  ${variables_2.scorecard}
1374
- ${variables_40.datePicker}
1376
+ ${variables_41.datePicker}
1375
1377
  ${replay}
1376
- ${variables_44.skipContent}
1377
- ${variables_45.pageActions}
1378
- ${variables_46.svgViewer}
1379
- ${variables_47.toast}
1380
- ${variables_48.pageNavigation}
1378
+ ${variables_45.skipContent}
1379
+ ${variables_46.pageActions}
1380
+ ${variables_47.svgViewer}
1381
+ ${variables_48.toast}
1382
+ ${variables_49.pageNavigation}
1381
1383
 
1382
1384
  background-color: var(--bg-color);
1383
1385
  color: var(--text-color-primary);
@@ -0,0 +1,54 @@
1
+ import type { ConsentConfig } from '@redocly/config';
2
+ import type { TranslationKey } from './l10n';
3
+ export type ConsentCategoryId = 'necessary' | 'analytics' | 'marketing';
4
+ export type ConsentCategory = {
5
+ id: ConsentCategoryId;
6
+ locked: boolean;
7
+ titleTranslationKey: TranslationKey;
8
+ descriptionTranslationKey: TranslationKey;
9
+ titleDefault: string;
10
+ descriptionDefault: string;
11
+ };
12
+ export type ConsentState = {
13
+ necessary: true;
14
+ analytics: boolean;
15
+ marketing: boolean;
16
+ };
17
+ export type ConsentChoice = {
18
+ analytics?: boolean;
19
+ marketing?: boolean;
20
+ };
21
+ export type ConsentProvider = 'onetrust' | 'cookiebot' | 'trustarc' | 'tcf';
22
+ export type ConsentRuntime = {
23
+ configuredMode: 'auto' | 'opt-in' | 'opt-out' | ConsentProvider;
24
+ mode: 'opt-in' | 'opt-out' | ConsentProvider;
25
+ state: ConsentState;
26
+ decided: boolean;
27
+ gpc: boolean;
28
+ dialogOpen: boolean;
29
+ region: {
30
+ source: 'timezone' | 'config' | 'none' | 'provider';
31
+ timeZone: string | null;
32
+ };
33
+ accept: () => void;
34
+ reject: () => void;
35
+ save: (next: ConsentChoice) => void;
36
+ open: () => void;
37
+ close: () => void;
38
+ reload: () => void;
39
+ on: (event: 'change' | 'open' | 'close', fn: (state: ConsentState) => void) => () => void;
40
+ };
41
+ export type UseConsentResult = {
42
+ enabled: boolean;
43
+ config: ConsentConfig | undefined;
44
+ mode: ConsentRuntime['mode'] | undefined;
45
+ state: ConsentState | undefined;
46
+ decided: boolean;
47
+ gpc: boolean;
48
+ isDialogOpen: boolean;
49
+ openDialog: () => void;
50
+ closeDialog: () => void;
51
+ accept: () => void;
52
+ reject: () => void;
53
+ save: (next: ConsentChoice) => void;
54
+ };
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=consent.js.map
@@ -12,6 +12,7 @@ import type { SearchItemData, SearchFacet, SearchFilterItem, SearchFacetQuery, A
12
12
  import type { SubmitFeedbackParams } from './feedback';
13
13
  import type { TFunction } from './l10n';
14
14
  import type { BreadcrumbItem } from './breadcrumb';
15
+ import type { UseConsentResult } from './consent';
15
16
  import type { AiSearchError } from '../constants/search';
16
17
  export type ThemeHooks = {
17
18
  useTranslate: () => {
@@ -64,6 +65,7 @@ export type ThemeHooks = {
64
65
  banner: ResolvedBannerConfig | undefined;
65
66
  dismissBanner: (content: string) => void;
66
67
  };
68
+ useConsent: () => UseConsentResult;
67
69
  useSearch: (product?: string, autoSearchDisabled?: boolean) => {
68
70
  query: string;
69
71
  setQuery: React.Dispatch<React.SetStateAction<string>>;
@@ -1,5 +1,6 @@
1
1
  export type * from './breadcrumb';
2
2
  export type * from './catalog';
3
+ export type * from './consent';
3
4
  export type * from './l10n';
4
5
  export type * from './mcp';
5
6
  export type * from './feedback';