@redocly/theme 0.70.0-next.2 → 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.
- package/lib/components/CodeBlock/CodeBlockControls.js +3 -5
- package/lib/components/Consent/Consent.d.ts +2 -0
- package/lib/components/Consent/Consent.js +17 -0
- package/lib/components/Consent/ConsentBanner.d.ts +2 -0
- package/lib/components/Consent/ConsentBanner.js +120 -0
- package/lib/components/Consent/ConsentDialog.d.ts +2 -0
- package/lib/components/Consent/ConsentDialog.js +128 -0
- package/lib/components/Consent/ConsentSettingsLink.d.ts +2 -0
- package/lib/components/Consent/ConsentSettingsLink.js +35 -0
- package/lib/components/Consent/variables.d.ts +1 -0
- package/lib/components/Consent/variables.js +31 -0
- package/lib/components/Footer/Footer.js +4 -1
- package/lib/components/JsonViewer/Helpers.d.ts +10 -2
- package/lib/components/JsonViewer/Helpers.js +29 -33
- package/lib/components/JsonViewer/JsonViewer.js +15 -18
- package/lib/components/Markdown/styles/base-table.js +14 -1
- package/lib/components/Markdown/variables.js +1 -0
- package/lib/components/Product/utils.js +1 -1
- package/lib/components/Search/SearchAiMessage.js +1 -1
- package/lib/components/Search/SearchAiResponse.js +1 -0
- package/lib/core/constants/consent.d.ts +2 -0
- package/lib/core/constants/consent.js +30 -0
- package/lib/core/constants/index.d.ts +1 -0
- package/lib/core/constants/index.js +1 -0
- package/lib/core/hooks/index.d.ts +2 -0
- package/lib/core/hooks/index.js +2 -0
- package/lib/core/hooks/use-consent-dialog.d.ts +12 -0
- package/lib/core/hooks/use-consent-dialog.js +36 -0
- package/lib/core/hooks/use-consent-telemetry.d.ts +1 -0
- package/lib/core/hooks/use-consent-telemetry.js +42 -0
- package/lib/core/hooks/use-theme-hooks.js +14 -0
- package/lib/core/styles/global.js +42 -40
- package/lib/core/types/consent.d.ts +54 -0
- package/lib/core/types/consent.js +3 -0
- package/lib/core/types/hooks.d.ts +2 -0
- package/lib/core/types/index.d.ts +1 -0
- package/lib/core/types/l10n.d.ts +1 -1
- package/lib/core/utils/consent-settings-link-placement.d.ts +3 -0
- package/lib/core/utils/consent-settings-link-placement.js +15 -0
- package/lib/core/utils/index.d.ts +1 -0
- package/lib/core/utils/index.js +1 -0
- package/lib/core/utils/mcp.js +1 -1
- package/lib/core/utils/menu.js +6 -5
- package/lib/core/utils/urls.js +3 -1
- package/lib/icons/ExpandCollapseIcon/ExpandCollapseIcon.d.ts +29 -0
- package/lib/icons/ExpandCollapseIcon/ExpandCollapseIcon.js +81 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/package.json +3 -3
- package/src/components/CodeBlock/CodeBlockControls.tsx +4 -7
- package/src/components/Consent/Consent.tsx +18 -0
- package/src/components/Consent/ConsentBanner.tsx +110 -0
- package/src/components/Consent/ConsentDialog.tsx +210 -0
- package/src/components/Consent/ConsentSettingsLink.tsx +44 -0
- package/src/components/Consent/variables.ts +28 -0
- package/src/components/Footer/Footer.tsx +2 -0
- package/src/components/JsonViewer/Helpers.tsx +46 -51
- package/src/components/JsonViewer/JsonViewer.tsx +23 -25
- package/src/components/Markdown/styles/base-table.ts +14 -1
- package/src/components/Markdown/variables.ts +1 -0
- package/src/components/Product/utils.ts +1 -1
- package/src/components/Search/SearchAiMessage.tsx +1 -1
- package/src/components/Search/SearchAiResponse.tsx +1 -0
- package/src/core/constants/consent.ts +29 -0
- package/src/core/constants/index.ts +1 -0
- package/src/core/hooks/__mocks__/use-theme-hooks.ts +15 -0
- package/src/core/hooks/index.ts +2 -0
- package/src/core/hooks/use-consent-dialog.ts +51 -0
- package/src/core/hooks/use-consent-telemetry.ts +56 -0
- package/src/core/hooks/use-theme-hooks.ts +14 -0
- package/src/core/styles/global.ts +2 -0
- package/src/core/types/consent.ts +51 -0
- package/src/core/types/hooks.ts +2 -0
- package/src/core/types/index.ts +1 -0
- package/src/core/types/l10n.ts +22 -1
- package/src/core/utils/consent-settings-link-placement.ts +18 -0
- package/src/core/utils/index.ts +1 -0
- package/src/core/utils/mcp.ts +1 -1
- package/src/core/utils/menu.ts +5 -6
- package/src/core/utils/urls.ts +3 -1
- package/src/icons/ExpandCollapseIcon/ExpandCollapseIcon.tsx +56 -0
- package/src/index.ts +1 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { IconProps } from '../../icons/types';
|
|
3
|
+
declare const Icon: (props: IconProps) => React.JSX.Element;
|
|
4
|
+
export declare const ExpandCollapseIcon: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("styled-components").FastOmit<import("styled-components").FastOmit<{
|
|
5
|
+
color?: string;
|
|
6
|
+
size?: string;
|
|
7
|
+
className?: string;
|
|
8
|
+
'data-component-name'?: string;
|
|
9
|
+
} & React.SVGProps<SVGSVGElement>, "data-component-name" | "size" | keyof React.SVGProps<SVGSVGElement>> & {
|
|
10
|
+
color?: string;
|
|
11
|
+
size?: string;
|
|
12
|
+
className?: string;
|
|
13
|
+
'data-component-name'?: string;
|
|
14
|
+
} & React.SVGProps<SVGSVGElement>, "$collapse"> & {
|
|
15
|
+
$collapse?: boolean;
|
|
16
|
+
}, "data-component-name"> & Partial<Pick<import("styled-components").FastOmit<import("styled-components").FastOmit<{
|
|
17
|
+
color?: string;
|
|
18
|
+
size?: string;
|
|
19
|
+
className?: string;
|
|
20
|
+
'data-component-name'?: string;
|
|
21
|
+
} & React.SVGProps<SVGSVGElement>, "data-component-name" | "size" | keyof React.SVGProps<SVGSVGElement>> & {
|
|
22
|
+
color?: string;
|
|
23
|
+
size?: string;
|
|
24
|
+
className?: string;
|
|
25
|
+
'data-component-name'?: string;
|
|
26
|
+
} & React.SVGProps<SVGSVGElement>, "$collapse"> & {
|
|
27
|
+
$collapse?: boolean;
|
|
28
|
+
}, "data-component-name">>> & string & Omit<typeof Icon, keyof React.Component<any, {}, any>>;
|
|
29
|
+
export {};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.ExpandCollapseIcon = void 0;
|
|
40
|
+
const react_1 = __importDefault(require("react"));
|
|
41
|
+
const styled_components_1 = __importStar(require("styled-components"));
|
|
42
|
+
const utils_1 = require("../../core/utils");
|
|
43
|
+
const Icon = (props) => (react_1.default.createElement("svg", Object.assign({ viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, props),
|
|
44
|
+
react_1.default.createElement("path", { className: "corner-top", d: "M12.2432 8.70703H11.2461L11.2422 4.75781L7.29297 4.75391V3.75781H12.2432V8.70703Z" }),
|
|
45
|
+
react_1.default.createElement("path", { className: "corner-bottom", d: "M4.75781 11.2422L8.70703 11.2461V12.2432H3.75781V7.29297H4.75391L4.75781 11.2422Z" })));
|
|
46
|
+
exports.ExpandCollapseIcon = (0, styled_components_1.default)(Icon).attrs({
|
|
47
|
+
'data-component-name': 'icons/ExpandCollapseIcon/ExpandCollapseIcon',
|
|
48
|
+
}) `
|
|
49
|
+
path {
|
|
50
|
+
fill: ${({ color }) => (0, utils_1.getCssColorVariable)(color)};
|
|
51
|
+
transform-box: view-box;
|
|
52
|
+
transition: transform 0.3s ease-in-out;
|
|
53
|
+
|
|
54
|
+
@media (prefers-reduced-motion: reduce) {
|
|
55
|
+
transition: none;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.corner-top {
|
|
60
|
+
transform-origin: 10.756px 5.244px;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.corner-bottom {
|
|
64
|
+
transform-origin: 5.244px 10.756px;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
${({ $collapse }) => $collapse &&
|
|
68
|
+
(0, styled_components_1.css) `
|
|
69
|
+
.corner-top {
|
|
70
|
+
transform: rotate(180deg);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.corner-bottom {
|
|
74
|
+
transform: rotate(-180deg);
|
|
75
|
+
}
|
|
76
|
+
`}
|
|
77
|
+
|
|
78
|
+
height: ${({ size }) => size || '16px'};
|
|
79
|
+
width: ${({ size }) => size || '16px'};
|
|
80
|
+
`;
|
|
81
|
+
//# sourceMappingURL=ExpandCollapseIcon.js.map
|
package/lib/index.d.ts
CHANGED
|
@@ -226,6 +226,7 @@ export * from './icons/LinkIcon/LinkIcon';
|
|
|
226
226
|
export * from './icons/LaunchIcon/LaunchIcon';
|
|
227
227
|
export * from './icons/MinimizeIcon/MinimizeIcon';
|
|
228
228
|
export * from './icons/MaximizeIcon/MaximizeIcon';
|
|
229
|
+
export * from './icons/ExpandCollapseIcon/ExpandCollapseIcon';
|
|
229
230
|
export * from './icons/MarkdownFullIcon/MarkdownFullIcon';
|
|
230
231
|
export * from './icons/DocumentBlankIcon/DocumentBlankIcon';
|
|
231
232
|
export * from './icons/WarningSquareIcon/WarningSquareIcon';
|
package/lib/index.js
CHANGED
|
@@ -289,6 +289,7 @@ __exportStar(require("./icons/LinkIcon/LinkIcon"), exports);
|
|
|
289
289
|
__exportStar(require("./icons/LaunchIcon/LaunchIcon"), exports);
|
|
290
290
|
__exportStar(require("./icons/MinimizeIcon/MinimizeIcon"), exports);
|
|
291
291
|
__exportStar(require("./icons/MaximizeIcon/MaximizeIcon"), exports);
|
|
292
|
+
__exportStar(require("./icons/ExpandCollapseIcon/ExpandCollapseIcon"), exports);
|
|
292
293
|
__exportStar(require("./icons/MarkdownFullIcon/MarkdownFullIcon"), exports);
|
|
293
294
|
__exportStar(require("./icons/DocumentBlankIcon/DocumentBlankIcon"), exports);
|
|
294
295
|
__exportStar(require("./icons/WarningSquareIcon/WarningSquareIcon"), exports);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@redocly/theme",
|
|
3
|
-
"version": "0.70.0-next.
|
|
3
|
+
"version": "0.70.0-next.3",
|
|
4
4
|
"description": "Shared UI components lib",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"theme",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@babel/core": "7.29.7",
|
|
44
44
|
"@markdoc/markdoc": "0.5.2",
|
|
45
|
-
"@redocly/realm-asyncapi-sdk": "0.16.0-next.
|
|
45
|
+
"@redocly/realm-asyncapi-sdk": "0.16.0-next.1",
|
|
46
46
|
"@shikijs/types": "3.21.0",
|
|
47
47
|
"@testing-library/react": "16.3.0",
|
|
48
48
|
"@testing-library/user-event": "14.6.1",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"webpack": "5.105.2"
|
|
72
72
|
},
|
|
73
73
|
"dependencies": {
|
|
74
|
-
"@redocly/config": "0.
|
|
74
|
+
"@redocly/config": "0.58.0",
|
|
75
75
|
"@tanstack/react-query": "5.62.3",
|
|
76
76
|
"@tanstack/react-virtual": "3.13.0",
|
|
77
77
|
"@xyflow/react": "12.8.3",
|
|
@@ -10,8 +10,7 @@ import { Tooltip } from '@redocly/theme/components/Tooltip/Tooltip';
|
|
|
10
10
|
import type { ReportButtonProps } from '@redocly/theme/core/hooks';
|
|
11
11
|
import { useThemeHooks, useThemeConfig } from '@redocly/theme/core/hooks';
|
|
12
12
|
import { DeselectIcon } from '@redocly/theme/icons/DeselectIcon/DeselectIcon';
|
|
13
|
-
import {
|
|
14
|
-
import { MinimizeIcon } from '@redocly/theme/icons/MinimizeIcon/MinimizeIcon';
|
|
13
|
+
import { ExpandCollapseIcon } from '@redocly/theme/icons/ExpandCollapseIcon/ExpandCollapseIcon';
|
|
15
14
|
import { SelectIcon } from '@redocly/theme/icons/SelectIcon/SelectIcon';
|
|
16
15
|
import { WarningSquareIcon } from '@redocly/theme/icons/WarningSquareIcon/WarningSquareIcon';
|
|
17
16
|
import { Button } from '@redocly/theme/components/Button/Button';
|
|
@@ -116,16 +115,14 @@ export function CodeBlockControls({
|
|
|
116
115
|
variant="text"
|
|
117
116
|
size="small"
|
|
118
117
|
data-testid="expand-all"
|
|
119
|
-
icon={controlsType === 'icon' ? <
|
|
118
|
+
icon={controlsType === 'icon' ? <ExpandCollapseIcon /> : undefined}
|
|
120
119
|
aria-label="Expand all"
|
|
121
120
|
onClick={expand?.onClick}
|
|
122
121
|
>
|
|
123
122
|
{controlsType !== 'icon' && (expand?.label || 'Expand all')}
|
|
124
123
|
</ControlButton>
|
|
125
124
|
</Tooltip>
|
|
126
|
-
) :
|
|
127
|
-
|
|
128
|
-
{collapse && !codeSnippet?.collapse?.hide ? (
|
|
125
|
+
) : collapse && !codeSnippet?.collapse?.hide ? (
|
|
129
126
|
<Tooltip
|
|
130
127
|
tip={
|
|
131
128
|
collapse.props?.tooltip ||
|
|
@@ -138,7 +135,7 @@ export function CodeBlockControls({
|
|
|
138
135
|
variant="text"
|
|
139
136
|
size="small"
|
|
140
137
|
data-testid="collapse-all"
|
|
141
|
-
icon={controlsType === 'icon' ? <
|
|
138
|
+
icon={controlsType === 'icon' ? <ExpandCollapseIcon $collapse /> : undefined}
|
|
142
139
|
onClick={collapse?.onClick}
|
|
143
140
|
aria-label="Collapse all"
|
|
144
141
|
>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import type { JSX } from 'react';
|
|
4
|
+
|
|
5
|
+
import { ConsentBanner } from '@redocly/theme/components/Consent/ConsentBanner';
|
|
6
|
+
import { ConsentDialog } from '@redocly/theme/components/Consent/ConsentDialog';
|
|
7
|
+
import { useConsentTelemetry } from '@redocly/theme/core/hooks';
|
|
8
|
+
|
|
9
|
+
export function Consent(): JSX.Element {
|
|
10
|
+
useConsentTelemetry();
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<>
|
|
14
|
+
<ConsentBanner />
|
|
15
|
+
<ConsentDialog />
|
|
16
|
+
</>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styled, { keyframes } from 'styled-components';
|
|
3
|
+
|
|
4
|
+
import type { JSX } from 'react';
|
|
5
|
+
|
|
6
|
+
import { Button } from '@redocly/theme/components/Button/Button';
|
|
7
|
+
import { Link } from '@redocly/theme/components/Link/Link';
|
|
8
|
+
import { useThemeHooks } from '@redocly/theme/core/hooks';
|
|
9
|
+
|
|
10
|
+
export function ConsentBanner(): JSX.Element | null {
|
|
11
|
+
const { useConsent, useTranslate } = useThemeHooks();
|
|
12
|
+
const consent = useConsent();
|
|
13
|
+
const { translate } = useTranslate();
|
|
14
|
+
|
|
15
|
+
if (!consent.enabled || consent.mode !== 'opt-in' || consent.decided || consent.isDialogOpen) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const title = translate('consent.title', 'We value your privacy');
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<BannerWrapper data-component-name="Consent/ConsentBanner" role="region" aria-label={title}>
|
|
23
|
+
<Title>{title}</Title>
|
|
24
|
+
<Message>
|
|
25
|
+
{translate(
|
|
26
|
+
'consent.message',
|
|
27
|
+
'By selecting "Accept all cookies", you agree to the use of cookies to improve site navigation, understand how our site is used, and support our marketing efforts. Select "Customize" to choose which types of cookies you allow.',
|
|
28
|
+
)}{' '}
|
|
29
|
+
{consent.config?.privacyPolicyUrl ? (
|
|
30
|
+
<Link to={consent.config.privacyPolicyUrl} external>
|
|
31
|
+
{translate('consent.privacyPolicy', 'Privacy policy')}
|
|
32
|
+
</Link>
|
|
33
|
+
) : null}
|
|
34
|
+
</Message>
|
|
35
|
+
<Actions>
|
|
36
|
+
<MainActions>
|
|
37
|
+
<Button variant="outlined" onClick={consent.reject}>
|
|
38
|
+
{translate('consent.rejectAll', 'Reject all')}
|
|
39
|
+
</Button>
|
|
40
|
+
<Button variant="primary" onClick={consent.accept}>
|
|
41
|
+
{translate('consent.acceptAllCookies', 'Accept all cookies')}
|
|
42
|
+
</Button>
|
|
43
|
+
</MainActions>
|
|
44
|
+
<Button variant="text" onClick={consent.openDialog}>
|
|
45
|
+
{translate('consent.customize', 'Customize')}
|
|
46
|
+
</Button>
|
|
47
|
+
</Actions>
|
|
48
|
+
</BannerWrapper>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const slideUp = keyframes`
|
|
53
|
+
from {
|
|
54
|
+
opacity: 0;
|
|
55
|
+
transform: translateY(16px);
|
|
56
|
+
}
|
|
57
|
+
to {
|
|
58
|
+
opacity: 1;
|
|
59
|
+
transform: translateY(0);
|
|
60
|
+
}
|
|
61
|
+
`;
|
|
62
|
+
|
|
63
|
+
const BannerWrapper = styled.div`
|
|
64
|
+
position: fixed;
|
|
65
|
+
left: var(--consent-banner-offset);
|
|
66
|
+
bottom: var(--consent-banner-offset);
|
|
67
|
+
z-index: var(--consent-banner-z-index);
|
|
68
|
+
display: flex;
|
|
69
|
+
flex-direction: column;
|
|
70
|
+
gap: var(--consent-banner-gap);
|
|
71
|
+
width: var(--consent-banner-width);
|
|
72
|
+
max-width: calc(100vw - 2 * var(--consent-banner-offset));
|
|
73
|
+
padding: var(--consent-banner-padding);
|
|
74
|
+
border: 1px solid var(--consent-banner-border-color);
|
|
75
|
+
border-radius: var(--consent-banner-border-radius);
|
|
76
|
+
background: var(--consent-banner-bg-color);
|
|
77
|
+
color: var(--consent-banner-text-color);
|
|
78
|
+
box-shadow: var(--consent-banner-box-shadow);
|
|
79
|
+
font-family: var(--font-family-base);
|
|
80
|
+
|
|
81
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
82
|
+
animation: ${slideUp} 240ms ease-out;
|
|
83
|
+
}
|
|
84
|
+
`;
|
|
85
|
+
|
|
86
|
+
const Title = styled.h2`
|
|
87
|
+
margin: 0;
|
|
88
|
+
font-size: var(--consent-banner-title-font-size);
|
|
89
|
+
font-weight: var(--font-weight-bold);
|
|
90
|
+
`;
|
|
91
|
+
|
|
92
|
+
const Message = styled.p`
|
|
93
|
+
margin: 0;
|
|
94
|
+
color: var(--text-color-secondary);
|
|
95
|
+
font-size: var(--consent-banner-font-size);
|
|
96
|
+
`;
|
|
97
|
+
|
|
98
|
+
const Actions = styled.div`
|
|
99
|
+
display: flex;
|
|
100
|
+
flex-wrap: wrap;
|
|
101
|
+
gap: var(--consent-banner-gap);
|
|
102
|
+
align-items: center;
|
|
103
|
+
justify-content: space-between;
|
|
104
|
+
`;
|
|
105
|
+
|
|
106
|
+
const MainActions = styled.div`
|
|
107
|
+
display: flex;
|
|
108
|
+
flex-wrap: wrap;
|
|
109
|
+
gap: var(--consent-banner-gap);
|
|
110
|
+
`;
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
|
|
4
|
+
import type { JSX } from 'react';
|
|
5
|
+
import type { UseConsentResult } from '@redocly/theme/core/types';
|
|
6
|
+
import type { ConsentDraft } from '@redocly/theme/core/hooks';
|
|
7
|
+
|
|
8
|
+
import { Button } from '@redocly/theme/components/Button/Button';
|
|
9
|
+
import { Link } from '@redocly/theme/components/Link/Link';
|
|
10
|
+
import { Portal } from '@redocly/theme/components/Portal/Portal';
|
|
11
|
+
import { Switch } from '@redocly/theme/components/Switch/Switch';
|
|
12
|
+
import { useConsentDialog, useThemeHooks } from '@redocly/theme/core/hooks';
|
|
13
|
+
import { CloseIcon } from '@redocly/theme/icons/CloseIcon/CloseIcon';
|
|
14
|
+
|
|
15
|
+
const noop = () => {};
|
|
16
|
+
|
|
17
|
+
export function ConsentDialog(): JSX.Element | null {
|
|
18
|
+
const { useConsent } = useThemeHooks();
|
|
19
|
+
const consent = useConsent();
|
|
20
|
+
|
|
21
|
+
if (!consent.enabled || !consent.isDialogOpen) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return <ConsentDialogContent consent={consent} />;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function ConsentDialogContent({ consent }: { consent: UseConsentResult }): JSX.Element {
|
|
29
|
+
const { useTranslate } = useThemeHooks();
|
|
30
|
+
const { translate } = useTranslate();
|
|
31
|
+
const { draft, setCategory, dialogRef, close, save, categories } = useConsentDialog(consent);
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<Portal>
|
|
35
|
+
<Overlay
|
|
36
|
+
className="scroll-lock"
|
|
37
|
+
data-component-name="Consent/ConsentDialog"
|
|
38
|
+
onMouseDown={(event) => {
|
|
39
|
+
if (event.target === event.currentTarget) close();
|
|
40
|
+
}}
|
|
41
|
+
>
|
|
42
|
+
<Dialog
|
|
43
|
+
ref={dialogRef}
|
|
44
|
+
role="dialog"
|
|
45
|
+
aria-modal="true"
|
|
46
|
+
aria-labelledby="redocly-consent-dialog-title"
|
|
47
|
+
tabIndex={-1}
|
|
48
|
+
>
|
|
49
|
+
<TitleRow>
|
|
50
|
+
<Title id="redocly-consent-dialog-title">
|
|
51
|
+
{translate('consent.dialog.title', 'Cookie preferences')}
|
|
52
|
+
</Title>
|
|
53
|
+
<Button
|
|
54
|
+
variant="ghost"
|
|
55
|
+
size="small"
|
|
56
|
+
icon={<CloseIcon />}
|
|
57
|
+
aria-label={translate('consent.dialog.close', 'Close')}
|
|
58
|
+
onClick={close}
|
|
59
|
+
/>
|
|
60
|
+
</TitleRow>
|
|
61
|
+
<Description>
|
|
62
|
+
{translate(
|
|
63
|
+
'consent.dialog.description',
|
|
64
|
+
'Choose which cookies this site may use. You can change your choice at any time.',
|
|
65
|
+
)}{' '}
|
|
66
|
+
{consent.config?.privacyPolicyUrl ? (
|
|
67
|
+
<Link to={consent.config.privacyPolicyUrl} external>
|
|
68
|
+
{translate('consent.privacyPolicy', 'Privacy policy')}
|
|
69
|
+
</Link>
|
|
70
|
+
) : null}
|
|
71
|
+
</Description>
|
|
72
|
+
{categories.map((category) => {
|
|
73
|
+
const gpcLocked = category.id === 'marketing' && consent.gpc;
|
|
74
|
+
return (
|
|
75
|
+
<Row key={category.id}>
|
|
76
|
+
<RowText>
|
|
77
|
+
<RowTitle>
|
|
78
|
+
{translate(category.titleTranslationKey, category.titleDefault)}
|
|
79
|
+
</RowTitle>
|
|
80
|
+
<RowDescription>
|
|
81
|
+
{translate(category.descriptionTranslationKey, category.descriptionDefault)}
|
|
82
|
+
</RowDescription>
|
|
83
|
+
{gpcLocked ? (
|
|
84
|
+
<GpcNotice>
|
|
85
|
+
{translate(
|
|
86
|
+
'consent.gpcNotice',
|
|
87
|
+
'Your browser asked us not to share your data, so this stays off.',
|
|
88
|
+
)}
|
|
89
|
+
</GpcNotice>
|
|
90
|
+
) : null}
|
|
91
|
+
</RowText>
|
|
92
|
+
{category.locked ? (
|
|
93
|
+
<SwitchGroup role="group" aria-label={translate('consent.alwaysOn', 'Always on')}>
|
|
94
|
+
<Switch value={true} disabled onChange={noop} />
|
|
95
|
+
</SwitchGroup>
|
|
96
|
+
) : gpcLocked ? (
|
|
97
|
+
<SwitchGroup
|
|
98
|
+
role="group"
|
|
99
|
+
aria-label={translate('consent.alwaysOff', 'Always off')}
|
|
100
|
+
>
|
|
101
|
+
<Switch value={false} disabled onChange={noop} />
|
|
102
|
+
</SwitchGroup>
|
|
103
|
+
) : (
|
|
104
|
+
<Switch
|
|
105
|
+
value={draft[category.id as keyof ConsentDraft]}
|
|
106
|
+
onChange={(value) => setCategory(category.id as keyof ConsentDraft, value)}
|
|
107
|
+
/>
|
|
108
|
+
)}
|
|
109
|
+
</Row>
|
|
110
|
+
);
|
|
111
|
+
})}
|
|
112
|
+
<Actions>
|
|
113
|
+
<Button variant="outlined" onClick={consent.reject}>
|
|
114
|
+
{translate('consent.rejectAll', 'Reject all')}
|
|
115
|
+
</Button>
|
|
116
|
+
<Spacer />
|
|
117
|
+
<Button variant="outlined" onClick={consent.accept}>
|
|
118
|
+
{translate('consent.acceptAll', 'Accept all')}
|
|
119
|
+
</Button>
|
|
120
|
+
<Button variant="primary" onClick={save}>
|
|
121
|
+
{translate('consent.savePreferences', 'Save preferences')}
|
|
122
|
+
</Button>
|
|
123
|
+
</Actions>
|
|
124
|
+
</Dialog>
|
|
125
|
+
</Overlay>
|
|
126
|
+
</Portal>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const Overlay = styled.div`
|
|
131
|
+
position: fixed;
|
|
132
|
+
inset: 0;
|
|
133
|
+
z-index: var(--z-index-overlay);
|
|
134
|
+
display: flex;
|
|
135
|
+
align-items: center;
|
|
136
|
+
justify-content: center;
|
|
137
|
+
background: var(--bg-color-modal-overlay);
|
|
138
|
+
font-family: var(--font-family-base);
|
|
139
|
+
`;
|
|
140
|
+
|
|
141
|
+
const Dialog = styled.div`
|
|
142
|
+
width: var(--consent-dialog-width);
|
|
143
|
+
max-width: var(--consent-dialog-max-width);
|
|
144
|
+
padding: var(--consent-dialog-padding);
|
|
145
|
+
border-radius: var(--consent-dialog-border-radius);
|
|
146
|
+
background: var(--consent-dialog-bg-color);
|
|
147
|
+
box-shadow: var(--consent-dialog-box-shadow);
|
|
148
|
+
`;
|
|
149
|
+
|
|
150
|
+
const TitleRow = styled.div`
|
|
151
|
+
display: flex;
|
|
152
|
+
gap: var(--spacing-xs);
|
|
153
|
+
align-items: flex-start;
|
|
154
|
+
justify-content: space-between;
|
|
155
|
+
`;
|
|
156
|
+
|
|
157
|
+
const Title = styled.h2`
|
|
158
|
+
margin: 0 0 var(--spacing-xs);
|
|
159
|
+
font-size: var(--font-size-lg);
|
|
160
|
+
`;
|
|
161
|
+
|
|
162
|
+
const Description = styled.p`
|
|
163
|
+
margin: 0 0 var(--spacing-base);
|
|
164
|
+
color: var(--consent-dialog-description-color);
|
|
165
|
+
font-size: var(--font-size-sm);
|
|
166
|
+
`;
|
|
167
|
+
|
|
168
|
+
const Row = styled.div`
|
|
169
|
+
display: flex;
|
|
170
|
+
gap: var(--spacing-base);
|
|
171
|
+
align-items: flex-start;
|
|
172
|
+
padding: var(--spacing-sm) 0;
|
|
173
|
+
border-top: 1px solid var(--consent-dialog-row-border-color);
|
|
174
|
+
`;
|
|
175
|
+
|
|
176
|
+
const RowText = styled.div`
|
|
177
|
+
flex: 1;
|
|
178
|
+
`;
|
|
179
|
+
|
|
180
|
+
const RowTitle = styled.div`
|
|
181
|
+
font-size: var(--font-size-base);
|
|
182
|
+
font-weight: var(--font-weight-bold);
|
|
183
|
+
`;
|
|
184
|
+
|
|
185
|
+
const RowDescription = styled.div`
|
|
186
|
+
color: var(--consent-dialog-description-color);
|
|
187
|
+
font-size: var(--font-size-sm);
|
|
188
|
+
`;
|
|
189
|
+
|
|
190
|
+
const GpcNotice = styled.div`
|
|
191
|
+
color: var(--consent-dialog-description-color);
|
|
192
|
+
font-size: var(--font-size-sm);
|
|
193
|
+
`;
|
|
194
|
+
|
|
195
|
+
const SwitchGroup = styled.div`
|
|
196
|
+
flex: none;
|
|
197
|
+
`;
|
|
198
|
+
|
|
199
|
+
const Actions = styled.div`
|
|
200
|
+
display: flex;
|
|
201
|
+
flex-wrap: wrap;
|
|
202
|
+
gap: var(--spacing-xs);
|
|
203
|
+
margin-top: var(--spacing-base);
|
|
204
|
+
padding-top: var(--spacing-base);
|
|
205
|
+
border-top: 1px solid var(--consent-dialog-row-border-color);
|
|
206
|
+
`;
|
|
207
|
+
|
|
208
|
+
const Spacer = styled.span`
|
|
209
|
+
flex: 1;
|
|
210
|
+
`;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
|
|
4
|
+
import type { JSX } from 'react';
|
|
5
|
+
|
|
6
|
+
import { Button } from '@redocly/theme/components/Button/Button';
|
|
7
|
+
import { useThemeConfig, useThemeHooks } from '@redocly/theme/core/hooks';
|
|
8
|
+
import { resolveSettingsLinkPlacement } from '@redocly/theme/core/utils';
|
|
9
|
+
|
|
10
|
+
export function ConsentSettingsLink(): JSX.Element | null {
|
|
11
|
+
const { useConsent, useTranslate } = useThemeHooks();
|
|
12
|
+
const { consent: consentConfig, footer } = useThemeConfig() || {};
|
|
13
|
+
const consent = useConsent();
|
|
14
|
+
const { translate } = useTranslate();
|
|
15
|
+
|
|
16
|
+
if (!consent.enabled || resolveSettingsLinkPlacement(consentConfig, footer) !== 'footer') {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<SettingsLinkButton
|
|
22
|
+
variant="link"
|
|
23
|
+
size="small"
|
|
24
|
+
data-component-name="Consent/ConsentSettingsLink"
|
|
25
|
+
onClick={consent.openDialog}
|
|
26
|
+
>
|
|
27
|
+
{translate('consent.settingsLink', 'Cookie preferences')}
|
|
28
|
+
</SettingsLinkButton>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const SettingsLinkButton = styled(Button)`
|
|
33
|
+
margin: var(--footer-link-padding-vertical) 0 var(--footer-link-padding-vertical) var(--spacing-sm);
|
|
34
|
+
padding: 0;
|
|
35
|
+
color: var(--footer-link-text-color);
|
|
36
|
+
font: inherit;
|
|
37
|
+
line-height: inherit;
|
|
38
|
+
|
|
39
|
+
&:hover,
|
|
40
|
+
&:focus-visible {
|
|
41
|
+
color: var(--footer-link-color-hover);
|
|
42
|
+
background: none;
|
|
43
|
+
}
|
|
44
|
+
`;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { css } from 'styled-components';
|
|
2
|
+
|
|
3
|
+
export const consent = css`
|
|
4
|
+
/**
|
|
5
|
+
* @tokens Consent
|
|
6
|
+
*/
|
|
7
|
+
--consent-banner-width: 380px;
|
|
8
|
+
--consent-banner-offset: var(--spacing-base); // @presenter Spacing
|
|
9
|
+
--consent-banner-bg-color: var(--bg-color); // @presenter Color
|
|
10
|
+
--consent-banner-text-color: var(--text-color-primary); // @presenter Color
|
|
11
|
+
--consent-banner-border-color: var(--border-color-primary); // @presenter Color
|
|
12
|
+
--consent-banner-border-radius: var(--border-radius-lg);
|
|
13
|
+
--consent-banner-box-shadow: var(--bg-raised-shadow);
|
|
14
|
+
--consent-banner-padding: var(--spacing-base); // @presenter Spacing
|
|
15
|
+
--consent-banner-gap: var(--spacing-sm); // @presenter Spacing
|
|
16
|
+
--consent-banner-title-font-size: var(--font-size-base);
|
|
17
|
+
--consent-banner-font-size: var(--font-size-sm);
|
|
18
|
+
--consent-banner-z-index: var(--z-index-overlay);
|
|
19
|
+
|
|
20
|
+
--consent-dialog-width: 560px;
|
|
21
|
+
--consent-dialog-max-width: calc(100vw - 2 * var(--spacing-base));
|
|
22
|
+
--consent-dialog-padding: var(--spacing-lg); // @presenter Spacing
|
|
23
|
+
--consent-dialog-border-radius: var(--border-radius-lg);
|
|
24
|
+
--consent-dialog-bg-color: var(--modal-bg-color); // @presenter Color
|
|
25
|
+
--consent-dialog-box-shadow: var(--modal-box-shadow);
|
|
26
|
+
--consent-dialog-row-border-color: var(--border-color-primary); // @presenter Color
|
|
27
|
+
--consent-dialog-description-color: var(--text-color-secondary); // @presenter Color
|
|
28
|
+
`;
|
|
@@ -9,6 +9,7 @@ import { breakpoints, isEmptyArray } from '@redocly/theme/core/utils';
|
|
|
9
9
|
import { useThemeConfig } from '@redocly/theme/core/hooks';
|
|
10
10
|
import { FooterCopyright } from '@redocly/theme/components/Footer/FooterCopyright';
|
|
11
11
|
import { FooterColumn } from '@redocly/theme/components/Footer/FooterColumn';
|
|
12
|
+
import { ConsentSettingsLink } from '@redocly/theme/components/Consent/ConsentSettingsLink';
|
|
12
13
|
|
|
13
14
|
export type FooterProps = {
|
|
14
15
|
className?: string;
|
|
@@ -43,6 +44,7 @@ export function Footer({ className }: FooterProps): JSX.Element | null {
|
|
|
43
44
|
)}
|
|
44
45
|
<FooterCopyrightWrapper>
|
|
45
46
|
{copyrightText && <FooterCopyright copyrightText={copyrightText} />}
|
|
47
|
+
<ConsentSettingsLink />
|
|
46
48
|
</FooterCopyrightWrapper>
|
|
47
49
|
</FooterWrapper>
|
|
48
50
|
);
|