@redocly/theme 0.63.0-next.3 → 0.63.0-next.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/Buttons/CopyButton.d.ts +1 -1
- package/lib/components/Buttons/CopyButton.js +1 -1
- package/lib/components/Buttons/DownloadButton.d.ts +6 -0
- package/lib/components/Buttons/DownloadButton.js +20 -0
- package/lib/components/Buttons/EmailButton.js +6 -1
- package/lib/components/Buttons/NewTabButton.js +6 -1
- package/lib/components/Catalog/CatalogEntity/CatalogEntityHistory/CatalogEntityHistorySidebar.js +2 -2
- package/lib/components/Catalog/CatalogEntity/CatalogEntityHistory/CatalogEntityVersionItem.js +6 -13
- package/lib/components/CodeBlock/CodeBlockControls.js +3 -3
- package/lib/components/Menu/MenuItem.js +1 -1
- package/lib/components/PageActions/PageActions.js +1 -1
- package/lib/components/Search/SearchDialog.js +1 -1
- package/lib/components/SidebarActions/SidebarActions.js +1 -1
- package/lib/components/SidebarActions/styled.d.ts +1 -1
- package/lib/components/Tooltip/AnchorTooltip.d.ts +7 -0
- package/lib/components/Tooltip/AnchorTooltip.js +233 -0
- package/lib/components/Tooltip/JsTooltip.d.ts +3 -0
- package/lib/components/Tooltip/JsTooltip.js +277 -0
- package/lib/components/Tooltip/Tooltip.d.ts +2 -13
- package/lib/components/Tooltip/Tooltip.js +14 -190
- package/lib/core/hooks/use-page-actions.js +9 -5
- package/lib/core/hooks/use-theme-hooks.js +1 -0
- package/lib/core/types/hooks.d.ts +4 -0
- package/lib/core/types/index.d.ts +1 -0
- package/lib/core/types/l10n.d.ts +1 -1
- package/lib/core/types/tooltip.d.ts +14 -0
- package/lib/core/types/tooltip.js +3 -0
- package/lib/core/utils/transform-revisions-to-version-history.js +13 -20
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/package.json +3 -3
- package/src/components/Buttons/CopyButton.tsx +2 -1
- package/src/components/Buttons/DownloadButton.tsx +41 -0
- package/src/components/Buttons/EmailButton.tsx +18 -8
- package/src/components/Buttons/NewTabButton.tsx +19 -8
- package/src/components/Catalog/CatalogEntity/CatalogEntityHistory/CatalogEntityHistorySidebar.tsx +2 -2
- package/src/components/Catalog/CatalogEntity/CatalogEntityHistory/CatalogEntityVersionItem.tsx +5 -21
- package/src/components/CodeBlock/CodeBlockControls.tsx +3 -0
- package/src/components/Menu/MenuItem.tsx +1 -0
- package/src/components/PageActions/PageActions.tsx +6 -1
- package/src/components/Search/SearchDialog.tsx +1 -1
- package/src/components/SidebarActions/SidebarActions.tsx +1 -0
- package/src/components/Tooltip/AnchorTooltip.tsx +259 -0
- package/src/components/Tooltip/JsTooltip.tsx +296 -0
- package/src/components/Tooltip/Tooltip.tsx +18 -257
- package/src/core/hooks/__mocks__/use-theme-hooks.ts +3 -0
- package/src/core/hooks/use-page-actions.ts +5 -0
- package/src/core/hooks/use-theme-hooks.ts +1 -0
- package/src/core/types/hooks.ts +2 -1
- package/src/core/types/index.ts +1 -0
- package/src/core/types/l10n.ts +5 -0
- package/src/core/types/tooltip.ts +15 -0
- package/src/core/utils/transform-revisions-to-version-history.ts +13 -21
- package/src/index.ts +1 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
export type TooltipProps = {
|
|
3
|
+
children?: ReactNode;
|
|
4
|
+
tip: string | ReactNode;
|
|
5
|
+
isOpen?: boolean;
|
|
6
|
+
withArrow?: boolean;
|
|
7
|
+
placement?: 'top' | 'bottom' | 'left' | 'right';
|
|
8
|
+
className?: string;
|
|
9
|
+
width?: string;
|
|
10
|
+
dataTestId?: string;
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
arrowPosition?: 'top' | 'bottom' | 'left' | 'right' | 'center';
|
|
13
|
+
onClick?: React.MouseEventHandler<HTMLDivElement>;
|
|
14
|
+
};
|
|
@@ -31,26 +31,19 @@ function transformRevisionsToVersionHistory({ revisions, currentRevisionDate, cu
|
|
|
31
31
|
}
|
|
32
32
|
// Check if any revision in this version group is the default version
|
|
33
33
|
const isDefaultVersion = versionRevisions.some((rev) => rev.isDefaultVersion === true);
|
|
34
|
-
const revisions = versionRevisions.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
date: (0, date_1.toLocalizedShortDateTime)(rev.revision, locale),
|
|
48
|
-
revisionDate: rev.revision,
|
|
49
|
-
isActive: isActiveRevision || isCurrentByDefault,
|
|
50
|
-
isCurrent: (_a = rev.isCurrent) !== null && _a !== void 0 ? _a : false,
|
|
51
|
-
};
|
|
52
|
-
})
|
|
53
|
-
: undefined;
|
|
34
|
+
const revisions = versionRevisions.map((rev, index) => {
|
|
35
|
+
var _a;
|
|
36
|
+
const revisionMatches = currentRevisionDate ? rev.revision === currentRevisionDate : false;
|
|
37
|
+
const isActiveRevision = revisionMatches && versionMatches;
|
|
38
|
+
const isCurrentByDefault = !currentRevisionDate && normalizedCurrentVersion === undefined && index === 0 && isCurrent;
|
|
39
|
+
return {
|
|
40
|
+
name: `r.${versionRevisions.length - index}`,
|
|
41
|
+
date: (0, date_1.toLocalizedShortDateTime)(rev.revision, locale),
|
|
42
|
+
revisionDate: rev.revision,
|
|
43
|
+
isActive: isActiveRevision || isCurrentByDefault,
|
|
44
|
+
isCurrent: (_a = rev.isCurrent) !== null && _a !== void 0 ? _a : false,
|
|
45
|
+
};
|
|
46
|
+
});
|
|
54
47
|
return {
|
|
55
48
|
version,
|
|
56
49
|
date: (0, date_1.toLocalizedShortDate)((latestRevision === null || latestRevision === void 0 ? void 0 : latestRevision.revision) || null, locale),
|
package/lib/index.d.ts
CHANGED
|
@@ -30,6 +30,7 @@ export * from './components/Buttons/CopyButton';
|
|
|
30
30
|
export * from './components/Buttons/EditPageButton';
|
|
31
31
|
export * from './components/Buttons/EmailButton';
|
|
32
32
|
export * from './components/Buttons/NewTabButton';
|
|
33
|
+
export * from './components/Buttons/DownloadButton';
|
|
33
34
|
export * from './components/Buttons/AIAssistantButton';
|
|
34
35
|
export * from './components/Markdown/Markdown';
|
|
35
36
|
export * from './components/Markdown/styles/base-table';
|
package/lib/index.js
CHANGED
|
@@ -70,6 +70,7 @@ __exportStar(require("./components/Buttons/CopyButton"), exports);
|
|
|
70
70
|
__exportStar(require("./components/Buttons/EditPageButton"), exports);
|
|
71
71
|
__exportStar(require("./components/Buttons/EmailButton"), exports);
|
|
72
72
|
__exportStar(require("./components/Buttons/NewTabButton"), exports);
|
|
73
|
+
__exportStar(require("./components/Buttons/DownloadButton"), exports);
|
|
73
74
|
__exportStar(require("./components/Buttons/AIAssistantButton"), exports);
|
|
74
75
|
/* Markdown */
|
|
75
76
|
__exportStar(require("./components/Markdown/Markdown"), exports);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@redocly/theme",
|
|
3
|
-
"version": "0.63.0-next.
|
|
3
|
+
"version": "0.63.0-next.5",
|
|
4
4
|
"description": "Shared UI components lib",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"theme",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"vitest": "4.0.10",
|
|
64
64
|
"vitest-when": "0.6.2",
|
|
65
65
|
"webpack": "5.105.2",
|
|
66
|
-
"@redocly/realm-asyncapi-sdk": "0.9.0-next.
|
|
66
|
+
"@redocly/realm-asyncapi-sdk": "0.9.0-next.4"
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
69
|
"@tanstack/react-query": "5.62.3",
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
"openapi-sampler": "1.7.0",
|
|
82
82
|
"react-calendar": "5.1.0",
|
|
83
83
|
"react-date-picker": "11.0.0",
|
|
84
|
-
"@redocly/config": "0.
|
|
84
|
+
"@redocly/config": "0.44.0"
|
|
85
85
|
},
|
|
86
86
|
"scripts": {
|
|
87
87
|
"watch": "tsc -p tsconfig.build.json && (concurrently \"tsc -w -p tsconfig.build.json\" \"tsc-alias -w -p tsconfig.build.json\")",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { memo } from 'react';
|
|
2
2
|
|
|
3
3
|
import type { JSX } from 'react';
|
|
4
|
-
import type { TooltipProps } from '@redocly/theme/
|
|
4
|
+
import type { TooltipProps } from '@redocly/theme/core/types';
|
|
5
5
|
import type { ButtonProps } from '@redocly/theme/components/Button/Button';
|
|
6
6
|
|
|
7
7
|
import { ClipboardService } from '@redocly/theme/core/utils';
|
|
@@ -88,6 +88,7 @@ function CopyButtonComponent({
|
|
|
88
88
|
tone={tone}
|
|
89
89
|
extraClass={extraClass}
|
|
90
90
|
iconPosition={iconPosition}
|
|
91
|
+
aria-label="Copy"
|
|
91
92
|
>
|
|
92
93
|
{(type === 'text' || type === 'compound') &&
|
|
93
94
|
(buttonText || translate('codeSnippet.copy.toasterText', 'Copy'))}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import type { JSX } from 'react';
|
|
4
|
+
|
|
5
|
+
import { Button } from '@redocly/theme/components/Button/Button';
|
|
6
|
+
import { DownloadIcon } from '@redocly/theme/icons/DownloadIcon/DownloadIcon';
|
|
7
|
+
import { Tooltip } from '@redocly/theme/components/Tooltip/Tooltip';
|
|
8
|
+
import { useThemeHooks } from '@redocly/theme/core/hooks';
|
|
9
|
+
|
|
10
|
+
export type DownloadButtonProps = {
|
|
11
|
+
data: string;
|
|
12
|
+
dataTestId?: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export function DownloadButton({
|
|
16
|
+
data,
|
|
17
|
+
dataTestId = 'download-button',
|
|
18
|
+
}: DownloadButtonProps): JSX.Element {
|
|
19
|
+
const { useTranslate } = useThemeHooks();
|
|
20
|
+
const { translate } = useTranslate();
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<div data-component-name="Buttons/DownloadButton">
|
|
24
|
+
<Tooltip
|
|
25
|
+
tip={translate('button.download.tooltipText', 'Download description')}
|
|
26
|
+
placement="top"
|
|
27
|
+
arrowPosition="right"
|
|
28
|
+
>
|
|
29
|
+
<a href={data} target="_blank" download rel="noreferrer">
|
|
30
|
+
<Button
|
|
31
|
+
variant="text"
|
|
32
|
+
size="small"
|
|
33
|
+
aria-label="Download"
|
|
34
|
+
icon={<DownloadIcon />}
|
|
35
|
+
data-testid={dataTestId}
|
|
36
|
+
/>
|
|
37
|
+
</a>
|
|
38
|
+
</Tooltip>
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
@@ -4,6 +4,8 @@ import type { JSX } from 'react';
|
|
|
4
4
|
|
|
5
5
|
import { Button } from '@redocly/theme/components/Button/Button';
|
|
6
6
|
import { EmailIcon } from '@redocly/theme/icons/EmailIcon/EmailIcon';
|
|
7
|
+
import { Tooltip } from '@redocly/theme/components/Tooltip/Tooltip';
|
|
8
|
+
import { useThemeHooks } from '@redocly/theme/core/hooks';
|
|
7
9
|
|
|
8
10
|
export type EmailButtonProps = {
|
|
9
11
|
data: string;
|
|
@@ -11,20 +13,28 @@ export type EmailButtonProps = {
|
|
|
11
13
|
};
|
|
12
14
|
|
|
13
15
|
export function EmailButton({ data, dataTestId = 'email-button' }: EmailButtonProps): JSX.Element {
|
|
16
|
+
const { useTranslate } = useThemeHooks();
|
|
17
|
+
const { translate } = useTranslate();
|
|
14
18
|
const onClick = () => {
|
|
15
19
|
window.location.href = `mailto:${data}`;
|
|
16
20
|
};
|
|
17
21
|
|
|
18
22
|
return (
|
|
19
23
|
<div data-component-name="Buttons/EmailButton">
|
|
20
|
-
<
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
<Tooltip
|
|
25
|
+
tip={translate('button.email.tooltipText', 'Send email')}
|
|
26
|
+
placement="top"
|
|
27
|
+
arrowPosition="right"
|
|
28
|
+
>
|
|
29
|
+
<Button
|
|
30
|
+
onClick={onClick}
|
|
31
|
+
icon={<EmailIcon />}
|
|
32
|
+
size="small"
|
|
33
|
+
variant="text"
|
|
34
|
+
data-testid={dataTestId}
|
|
35
|
+
aria-label="Email"
|
|
36
|
+
/>
|
|
37
|
+
</Tooltip>
|
|
28
38
|
</div>
|
|
29
39
|
);
|
|
30
40
|
}
|
|
@@ -4,6 +4,8 @@ import type { JSX } from 'react';
|
|
|
4
4
|
|
|
5
5
|
import { Button } from '@redocly/theme/components/Button/Button';
|
|
6
6
|
import { ArrowUpRightIcon } from '@redocly/theme/icons/ArrowUpRightIcon/ArrowUpRightIcon';
|
|
7
|
+
import { Tooltip } from '@redocly/theme/components/Tooltip/Tooltip';
|
|
8
|
+
import { useThemeHooks } from '@redocly/theme/core/hooks';
|
|
7
9
|
|
|
8
10
|
export type NewTabButtonProps = {
|
|
9
11
|
data: string;
|
|
@@ -14,20 +16,29 @@ export function NewTabButton({
|
|
|
14
16
|
data,
|
|
15
17
|
dataTestId = 'new-tab-button',
|
|
16
18
|
}: NewTabButtonProps): JSX.Element {
|
|
19
|
+
const { useTranslate } = useThemeHooks();
|
|
20
|
+
const { translate } = useTranslate();
|
|
21
|
+
|
|
17
22
|
const onClick = () => {
|
|
18
23
|
window.open(data, '_blank');
|
|
19
24
|
};
|
|
20
25
|
|
|
21
26
|
return (
|
|
22
27
|
<div data-component-name="Buttons/NewTabButton">
|
|
23
|
-
<
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
<Tooltip
|
|
29
|
+
tip={translate('button.externalLink.tooltipText', 'Open in new tab')}
|
|
30
|
+
placement="top"
|
|
31
|
+
arrowPosition="right"
|
|
32
|
+
>
|
|
33
|
+
<Button
|
|
34
|
+
onClick={onClick}
|
|
35
|
+
icon={<ArrowUpRightIcon />}
|
|
36
|
+
data-testid={dataTestId}
|
|
37
|
+
aria-label="Open in new tab"
|
|
38
|
+
size="small"
|
|
39
|
+
variant="text"
|
|
40
|
+
/>
|
|
41
|
+
</Tooltip>
|
|
31
42
|
</div>
|
|
32
43
|
);
|
|
33
44
|
}
|
package/src/components/Catalog/CatalogEntity/CatalogEntityHistory/CatalogEntityHistorySidebar.tsx
CHANGED
|
@@ -128,10 +128,10 @@ export function CatalogEntityHistorySidebar({
|
|
|
128
128
|
|
|
129
129
|
const SidebarOverlay = styled.div`
|
|
130
130
|
position: fixed;
|
|
131
|
-
top: var(--navbar-height);
|
|
131
|
+
top: calc(var(--navbar-height) + var(--banner-height, 0));
|
|
132
132
|
left: 0;
|
|
133
133
|
width: var(--sidebar-width);
|
|
134
|
-
height: calc(100vh - var(--navbar-height));
|
|
134
|
+
height: calc(100vh - var(--navbar-height) - var(--banner-height, 0));
|
|
135
135
|
background-color: var(--catalog-history-sidebar-bg-color);
|
|
136
136
|
border-right: 1px solid var(--catalog-history-sidebar-border-color);
|
|
137
137
|
display: flex;
|
package/src/components/Catalog/CatalogEntity/CatalogEntityHistory/CatalogEntityVersionItem.tsx
CHANGED
|
@@ -53,9 +53,6 @@ export function CatalogEntityVersionItem({
|
|
|
53
53
|
|
|
54
54
|
const singleRevisionLink = buildRevisionUrl(basePath, group.singleRevisionDate, group.version);
|
|
55
55
|
|
|
56
|
-
const isClickable = hasRevisions || Boolean(singleRevisionLink);
|
|
57
|
-
const isSingleRevisionActive = Boolean(singleRevisionLink && group.isCurrent);
|
|
58
|
-
|
|
59
56
|
const versionTitle = isNotSpecifiedVersion
|
|
60
57
|
? `${translate('catalog.history.version.label', 'Version')}: ${translate('catalog.history.version.notSpecified', 'not specified')}`
|
|
61
58
|
: `${translate('catalog.history.version.label', 'Version')}: ${group.version}`;
|
|
@@ -64,9 +61,7 @@ export function CatalogEntityVersionItem({
|
|
|
64
61
|
<VersionButton
|
|
65
62
|
type="button"
|
|
66
63
|
onClick={() => hasRevisions && onToggle(group.version)}
|
|
67
|
-
$isClickable={isClickable}
|
|
68
64
|
$isCurrent={group.isCurrent}
|
|
69
|
-
$isActive={isSingleRevisionActive}
|
|
70
65
|
>
|
|
71
66
|
<VersionIcon $isCurrent={group.isCurrent}>
|
|
72
67
|
{group.isCurrent ? (
|
|
@@ -155,34 +150,23 @@ export function CatalogEntityVersionItem({
|
|
|
155
150
|
}
|
|
156
151
|
|
|
157
152
|
const VersionButton = styled.button<{
|
|
158
|
-
$isClickable?: boolean;
|
|
159
153
|
$isCurrent?: boolean;
|
|
160
|
-
$isActive?: boolean;
|
|
161
154
|
}>`
|
|
162
155
|
all: unset;
|
|
163
156
|
display: flex;
|
|
164
157
|
align-items: center;
|
|
165
158
|
width: calc(100% - var(--catalog-history-sidebar-version-icon-margin-right));
|
|
166
159
|
padding: var(--catalog-history-sidebar-version-header-padding);
|
|
167
|
-
cursor:
|
|
160
|
+
cursor: pointer;
|
|
168
161
|
border-radius: var(--catalog-history-sidebar-version-header-border-radius);
|
|
169
162
|
transition: 0.2s ease;
|
|
170
163
|
text-decoration: none;
|
|
171
164
|
color: inherit;
|
|
172
|
-
background-color:
|
|
173
|
-
$isActive ? 'var(--catalog-history-sidebar-revision-item-bg-color-active)' : 'transparent'};
|
|
165
|
+
background-color: 'transparent';
|
|
174
166
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
&:hover {
|
|
179
|
-
background-color: ${
|
|
180
|
-
$isActive
|
|
181
|
-
? 'var(--catalog-history-sidebar-revision-item-bg-color-active)'
|
|
182
|
-
: 'var(--catalog-history-sidebar-version-header-bg-color-hover)'
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
`}
|
|
167
|
+
&:hover {
|
|
168
|
+
background-color: var(--catalog-history-sidebar-version-header-bg-color-hover);
|
|
169
|
+
}
|
|
186
170
|
|
|
187
171
|
${({ $isCurrent }) =>
|
|
188
172
|
!$isCurrent &&
|
|
@@ -96,6 +96,7 @@ export function CodeBlockControls({
|
|
|
96
96
|
size="small"
|
|
97
97
|
data-testid="report-button"
|
|
98
98
|
icon={controlsType === 'icon' ? <WarningSquareIcon size="18px" /> : undefined}
|
|
99
|
+
aria-label="Report a problem"
|
|
99
100
|
{...report.props}
|
|
100
101
|
>
|
|
101
102
|
{controlsType != 'icon' && (report.props?.buttonText || 'Report')}
|
|
@@ -114,6 +115,7 @@ export function CodeBlockControls({
|
|
|
114
115
|
size="small"
|
|
115
116
|
data-testid="expand-all"
|
|
116
117
|
icon={controlsType === 'icon' ? <MaximizeIcon /> : undefined}
|
|
118
|
+
aria-label="Expand all"
|
|
117
119
|
onClick={expand?.onClick}
|
|
118
120
|
>
|
|
119
121
|
{controlsType !== 'icon' && (expand?.label || 'Expand all')}
|
|
@@ -133,6 +135,7 @@ export function CodeBlockControls({
|
|
|
133
135
|
data-testid="collapse-all"
|
|
134
136
|
icon={controlsType === 'icon' ? <MinimizeIcon /> : undefined}
|
|
135
137
|
onClick={collapse?.onClick}
|
|
138
|
+
aria-label="Collapse all"
|
|
136
139
|
>
|
|
137
140
|
{controlsType !== 'icon' && (expand?.label || 'Collapse all')}
|
|
138
141
|
</ControlButton>
|
|
@@ -142,6 +142,7 @@ export function MenuItem(props: React.PropsWithChildren<MenuItemProps>): JSX.Ele
|
|
|
142
142
|
<MenuItemWrapper
|
|
143
143
|
data-component-name="Menu/MenuItem"
|
|
144
144
|
className={generateClassName({ type, item, className })}
|
|
145
|
+
role="listitem"
|
|
145
146
|
>
|
|
146
147
|
{item.link ? (
|
|
147
148
|
<MenuItemLink
|
|
@@ -83,7 +83,12 @@ export function PageActions(props: PageActionProps): JSX.Element | null {
|
|
|
83
83
|
{buttonAction.buttonText}
|
|
84
84
|
</Button>
|
|
85
85
|
{actions.length > 1 ? (
|
|
86
|
-
<StyledDropdown
|
|
86
|
+
<StyledDropdown
|
|
87
|
+
withArrow
|
|
88
|
+
trigger={<Button aria-label="More actions" />}
|
|
89
|
+
placement="bottom"
|
|
90
|
+
alignment="end"
|
|
91
|
+
>
|
|
87
92
|
<StyledDropdownMenu>{menuItems}</StyledDropdownMenu>
|
|
88
93
|
</StyledDropdown>
|
|
89
94
|
) : null}
|
|
@@ -75,7 +75,7 @@ export function SearchDialog({
|
|
|
75
75
|
onQuickFilterReset,
|
|
76
76
|
} = useSearchFilter(filter, setFilter);
|
|
77
77
|
const { addSearchHistoryItem } = useRecentSearches();
|
|
78
|
-
const aiSearch = useAiSearch({ filter });
|
|
78
|
+
const aiSearch = useAiSearch({ filter: filter, product: product?.name });
|
|
79
79
|
useModalScrollLock(true);
|
|
80
80
|
const searchInputRef = useRef<HTMLInputElement>(null);
|
|
81
81
|
const modalRef = useRef<HTMLDivElement>(null);
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import React, { memo, useEffect, useRef, useId } from 'react';
|
|
2
|
+
import styled, { css } from 'styled-components';
|
|
3
|
+
|
|
4
|
+
import type { JSX, PropsWithChildren } from 'react';
|
|
5
|
+
import type { TooltipProps } from '@redocly/theme/core/types';
|
|
6
|
+
|
|
7
|
+
import { useControl, useOutsideClick } from '@redocly/theme/core/hooks';
|
|
8
|
+
import { Portal } from '@redocly/theme/components/Portal/Portal';
|
|
9
|
+
|
|
10
|
+
type Props = Exclude<TooltipProps, 'arrowPosition'> & {
|
|
11
|
+
arrowPosition?: 'left' | 'right' | 'center';
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
function TooltipComponent({
|
|
15
|
+
children,
|
|
16
|
+
isOpen,
|
|
17
|
+
tip,
|
|
18
|
+
withArrow = true,
|
|
19
|
+
placement = 'top',
|
|
20
|
+
className = 'default',
|
|
21
|
+
width,
|
|
22
|
+
dataTestId,
|
|
23
|
+
disabled = false,
|
|
24
|
+
arrowPosition = 'center',
|
|
25
|
+
onClick,
|
|
26
|
+
}: PropsWithChildren<Props>): JSX.Element {
|
|
27
|
+
const tooltipWrapperRef = useRef<HTMLDivElement | null>(null);
|
|
28
|
+
const tooltipBodyRef = useRef<HTMLDivElement | null>(null);
|
|
29
|
+
const { isOpened, handleOpen, handleClose } = useControl(isOpen);
|
|
30
|
+
const anchorName = `--tooltip${useId().replace(/:/g, '')}`;
|
|
31
|
+
|
|
32
|
+
useOutsideClick(isOpened ? [tooltipWrapperRef, tooltipBodyRef] : tooltipWrapperRef, handleClose);
|
|
33
|
+
|
|
34
|
+
const isControlled = isOpen !== undefined;
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
if (!isControlled) return;
|
|
38
|
+
|
|
39
|
+
if (isOpen && !disabled) {
|
|
40
|
+
handleOpen();
|
|
41
|
+
} else {
|
|
42
|
+
handleClose();
|
|
43
|
+
}
|
|
44
|
+
}, [isOpen, disabled, isControlled, handleOpen, handleClose]);
|
|
45
|
+
|
|
46
|
+
const controllers =
|
|
47
|
+
!isControlled && !disabled
|
|
48
|
+
? {
|
|
49
|
+
onMouseEnter: handleOpen,
|
|
50
|
+
onMouseLeave: handleClose,
|
|
51
|
+
onClick: (e: React.MouseEvent<HTMLDivElement>) => {
|
|
52
|
+
onClick?.(e);
|
|
53
|
+
handleClose();
|
|
54
|
+
},
|
|
55
|
+
onFocus: handleOpen,
|
|
56
|
+
onBlur: handleClose,
|
|
57
|
+
}
|
|
58
|
+
: {};
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<TooltipWrapper
|
|
62
|
+
ref={tooltipWrapperRef}
|
|
63
|
+
{...controllers}
|
|
64
|
+
className={`tooltip-${className}`}
|
|
65
|
+
data-component-name="Tooltip/Tooltip"
|
|
66
|
+
anchorName={anchorName}
|
|
67
|
+
>
|
|
68
|
+
{children}
|
|
69
|
+
{isOpened && !disabled && (
|
|
70
|
+
<Portal>
|
|
71
|
+
<TooltipBody
|
|
72
|
+
ref={tooltipBodyRef}
|
|
73
|
+
data-testid={dataTestId || (typeof tip === 'string' ? tip : '')}
|
|
74
|
+
placement={placement}
|
|
75
|
+
width={width}
|
|
76
|
+
withArrow={withArrow}
|
|
77
|
+
arrowPosition={arrowPosition}
|
|
78
|
+
anchorName={anchorName}
|
|
79
|
+
>
|
|
80
|
+
{tip}
|
|
81
|
+
</TooltipBody>
|
|
82
|
+
</Portal>
|
|
83
|
+
)}
|
|
84
|
+
</TooltipWrapper>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export const Tooltip = memo<PropsWithChildren<Props>>(TooltipComponent);
|
|
89
|
+
|
|
90
|
+
const PLACEMENTS = {
|
|
91
|
+
top: css<Pick<Props, 'withArrow' | 'arrowPosition'>>`
|
|
92
|
+
bottom: anchor(top);
|
|
93
|
+
${({ withArrow, arrowPosition }) =>
|
|
94
|
+
withArrow && arrowPosition === 'left'
|
|
95
|
+
? css`
|
|
96
|
+
transform: translate(-32px, -6px);
|
|
97
|
+
left: anchor(center);
|
|
98
|
+
`
|
|
99
|
+
: arrowPosition === 'right'
|
|
100
|
+
? css`
|
|
101
|
+
transform: translate(32px, -6px);
|
|
102
|
+
right: anchor(center);
|
|
103
|
+
`
|
|
104
|
+
: css`
|
|
105
|
+
transform: translate(-50%, -6px);
|
|
106
|
+
left: anchor(center);
|
|
107
|
+
`}
|
|
108
|
+
|
|
109
|
+
${({ withArrow, arrowPosition }) =>
|
|
110
|
+
withArrow &&
|
|
111
|
+
css`
|
|
112
|
+
&::after {
|
|
113
|
+
border-left: 14px solid transparent;
|
|
114
|
+
border-right: 14px solid transparent;
|
|
115
|
+
border-top-width: 8px;
|
|
116
|
+
border-top-style: solid;
|
|
117
|
+
border-radius: 2px;
|
|
118
|
+
bottom: 0;
|
|
119
|
+
${arrowPosition === 'left' && 'left: 16px; transform: translateY(100%);'}
|
|
120
|
+
${arrowPosition === 'center' && 'left: 50%; transform: translate(-50%, 100%);'}
|
|
121
|
+
${arrowPosition === 'right' && 'right: 16px; transform: translateY(100%);'}
|
|
122
|
+
}
|
|
123
|
+
`}
|
|
124
|
+
`,
|
|
125
|
+
bottom: css<Pick<Props, 'withArrow' | 'arrowPosition'>>`
|
|
126
|
+
top: anchor(bottom);
|
|
127
|
+
${({ withArrow, arrowPosition }) =>
|
|
128
|
+
withArrow && arrowPosition === 'left'
|
|
129
|
+
? css`
|
|
130
|
+
transform: translate(-32px, 6px);
|
|
131
|
+
left: anchor(center);
|
|
132
|
+
`
|
|
133
|
+
: arrowPosition === 'right'
|
|
134
|
+
? css`
|
|
135
|
+
transform: translate(32px, 6px);
|
|
136
|
+
right: anchor(center);
|
|
137
|
+
`
|
|
138
|
+
: css`
|
|
139
|
+
transform: translate(-50%, 6px);
|
|
140
|
+
left: anchor(center);
|
|
141
|
+
`}
|
|
142
|
+
|
|
143
|
+
${({ withArrow, arrowPosition }) =>
|
|
144
|
+
withArrow &&
|
|
145
|
+
css`
|
|
146
|
+
&::after {
|
|
147
|
+
border-left: 14px solid transparent;
|
|
148
|
+
border-right: 14px solid transparent;
|
|
149
|
+
border-bottom-width: 8px;
|
|
150
|
+
border-bottom-style: solid;
|
|
151
|
+
border-radius: 0 0 2px 2px;
|
|
152
|
+
top: 0;
|
|
153
|
+
${arrowPosition === 'left' && 'left: 16px; transform: translateY(-100%);'}
|
|
154
|
+
${arrowPosition === 'center' && 'left: 50%; transform: translate(-50%, -100%);'}
|
|
155
|
+
${arrowPosition === 'right' && 'right: 16px; transform: translateY(-100%);'}
|
|
156
|
+
}
|
|
157
|
+
`}
|
|
158
|
+
`,
|
|
159
|
+
left: css<Pick<Props, 'withArrow' | 'arrowPosition'>>`
|
|
160
|
+
transform: translate(-100%, -50%);
|
|
161
|
+
margin-left: -7px;
|
|
162
|
+
top: anchor(center);
|
|
163
|
+
left: anchor(left);
|
|
164
|
+
|
|
165
|
+
${({ withArrow }) =>
|
|
166
|
+
withArrow &&
|
|
167
|
+
css`
|
|
168
|
+
&::after {
|
|
169
|
+
border-top: 14px solid transparent;
|
|
170
|
+
border-bottom: 14px solid transparent;
|
|
171
|
+
border-left-width: 8px;
|
|
172
|
+
border-left-style: solid;
|
|
173
|
+
border-radius: 2px 0 0 2px;
|
|
174
|
+
right: -9px;
|
|
175
|
+
top: 50%;
|
|
176
|
+
transform: translateY(-50%);
|
|
177
|
+
}
|
|
178
|
+
`}
|
|
179
|
+
`,
|
|
180
|
+
right: css<Pick<Props, 'withArrow' | 'arrowPosition'>>`
|
|
181
|
+
transform: translate(0, -50%);
|
|
182
|
+
margin-left: 7px;
|
|
183
|
+
top: anchor(center);
|
|
184
|
+
left: anchor(right);
|
|
185
|
+
|
|
186
|
+
${({ withArrow }) =>
|
|
187
|
+
withArrow &&
|
|
188
|
+
css`
|
|
189
|
+
&::after {
|
|
190
|
+
border-top: 14px solid transparent;
|
|
191
|
+
border-bottom: 14px solid transparent;
|
|
192
|
+
border-right-width: 8px;
|
|
193
|
+
border-right-style: solid;
|
|
194
|
+
border-radius: 0 2px 2px 0;
|
|
195
|
+
left: -9px;
|
|
196
|
+
top: 50%;
|
|
197
|
+
transform: translateY(-50%);
|
|
198
|
+
}
|
|
199
|
+
`}
|
|
200
|
+
`,
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const TooltipWrapper = styled.div.attrs<{ anchorName: string }>(({ anchorName }) => ({
|
|
204
|
+
style: {
|
|
205
|
+
anchorName: anchorName,
|
|
206
|
+
} as React.CSSProperties,
|
|
207
|
+
}))<{ anchorName: string }>`
|
|
208
|
+
display: flex;
|
|
209
|
+
`;
|
|
210
|
+
|
|
211
|
+
const TooltipBody = styled.span.attrs<{ anchorName: string }>(({ anchorName }) => ({
|
|
212
|
+
style: {
|
|
213
|
+
positionAnchor: anchorName,
|
|
214
|
+
} as React.CSSProperties,
|
|
215
|
+
}))<
|
|
216
|
+
Pick<Required<Props>, 'placement' | 'withArrow' | 'arrowPosition'> & {
|
|
217
|
+
width?: string;
|
|
218
|
+
anchorName: string;
|
|
219
|
+
}
|
|
220
|
+
>`
|
|
221
|
+
position: fixed;
|
|
222
|
+
min-width: 64px;
|
|
223
|
+
padding: var(--tooltip-padding);
|
|
224
|
+
max-width: var(--tooltip-max-width);
|
|
225
|
+
white-space: normal;
|
|
226
|
+
word-break: normal;
|
|
227
|
+
overflow-wrap: break-word;
|
|
228
|
+
text-align: left;
|
|
229
|
+
|
|
230
|
+
border-radius: var(--border-radius-md);
|
|
231
|
+
transition: opacity 0.3s ease-out;
|
|
232
|
+
|
|
233
|
+
font-size: var(--font-size-base);
|
|
234
|
+
line-height: var(--line-height-base);
|
|
235
|
+
|
|
236
|
+
z-index: var(--z-index-overlay);
|
|
237
|
+
|
|
238
|
+
&::after {
|
|
239
|
+
position: absolute;
|
|
240
|
+
|
|
241
|
+
content: ' ';
|
|
242
|
+
display: inline-block;
|
|
243
|
+
width: 0;
|
|
244
|
+
height: 0;
|
|
245
|
+
border-color: var(--tooltip-arrow-color, var(--tooltip-bg-color));
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
background: var(--tooltip-bg-color);
|
|
249
|
+
color: var(--tooltip-text-color);
|
|
250
|
+
border: var(--tooltip-border-width, 0) var(--tooltip-border-style, solid)
|
|
251
|
+
var(--tooltip-border-color, transparent);
|
|
252
|
+
box-shadow: var(--bg-raised-shadow);
|
|
253
|
+
|
|
254
|
+
width: ${({ width }) => width || 'max-content'};
|
|
255
|
+
|
|
256
|
+
${({ placement }) => css`
|
|
257
|
+
${PLACEMENTS[placement]};
|
|
258
|
+
`}
|
|
259
|
+
`;
|