@redocly/theme 0.62.0-next.4 → 0.62.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/SvgViewer/SvgViewer.d.ts +15 -0
- package/lib/components/SvgViewer/SvgViewer.js +312 -0
- package/lib/components/SvgViewer/variables.d.ts +1 -0
- package/lib/components/SvgViewer/variables.dark.d.ts +1 -0
- package/lib/components/SvgViewer/variables.dark.js +8 -0
- package/lib/components/SvgViewer/variables.js +17 -0
- package/lib/components/Tag/variables.dark.js +6 -0
- package/lib/components/Tag/variables.js +6 -0
- package/lib/core/styles/dark.js +2 -0
- package/lib/core/styles/global.js +2 -0
- package/lib/core/types/catalog.d.ts +1 -1
- package/lib/core/types/l10n.d.ts +1 -1
- package/lib/core/utils/transform-revisions-to-version-history.js +8 -51
- package/lib/icons/FitToViewIcon/FitToViewIcon.d.ts +9 -0
- package/lib/icons/FitToViewIcon/FitToViewIcon.js +25 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +2 -0
- package/lib/markdoc/components/Mermaid/Mermaid.js +70 -2
- package/package.json +4 -4
- package/src/components/SvgViewer/SvgViewer.tsx +405 -0
- package/src/components/SvgViewer/variables.dark.ts +5 -0
- package/src/components/SvgViewer/variables.ts +14 -0
- package/src/components/Tag/variables.dark.ts +6 -0
- package/src/components/Tag/variables.ts +6 -0
- package/src/core/styles/dark.ts +2 -0
- package/src/core/styles/global.ts +2 -0
- package/src/core/types/catalog.ts +1 -1
- package/src/core/types/l10n.ts +7 -1
- package/src/core/utils/transform-revisions-to-version-history.ts +8 -80
- package/src/icons/FitToViewIcon/FitToViewIcon.tsx +26 -0
- package/src/index.ts +2 -0
- package/src/markdoc/components/Mermaid/Mermaid.tsx +57 -8
|
@@ -8,72 +8,6 @@ import { VERSION_NOT_SPECIFIED } from '@redocly/theme/core/constants';
|
|
|
8
8
|
|
|
9
9
|
import { toLocalizedShortDate, toLocalizedShortDateTime } from './date';
|
|
10
10
|
|
|
11
|
-
function compareVersionsDescending(versionA: string, versionB: string): number {
|
|
12
|
-
return versionB.localeCompare(versionA, undefined, { numeric: true });
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function extractGroupComparisonData(group: CatalogEntityVersionHistoryGroup): {
|
|
16
|
-
version: string;
|
|
17
|
-
isNotSpecified: boolean;
|
|
18
|
-
time: number;
|
|
19
|
-
hasCurrent: boolean;
|
|
20
|
-
} {
|
|
21
|
-
const version = group.version;
|
|
22
|
-
const isNotSpecified = version === VERSION_NOT_SPECIFIED;
|
|
23
|
-
|
|
24
|
-
// Get version dates (oldest revision date for each group - the version's creation date)
|
|
25
|
-
// This ensures versions stay in their original order even when new revisions are added
|
|
26
|
-
const date =
|
|
27
|
-
group.singleRevisionDate ||
|
|
28
|
-
(group.revisions && group.revisions.length > 0
|
|
29
|
-
? group.revisions[group.revisions.length - 1]?.revisionDate
|
|
30
|
-
: undefined) ||
|
|
31
|
-
'';
|
|
32
|
-
const time = date ? new Date(date).getTime() : 0;
|
|
33
|
-
|
|
34
|
-
const hasCurrent = group.hasCurrentRevisionFromBackend ?? false;
|
|
35
|
-
|
|
36
|
-
return { version, isNotSpecified, time, hasCurrent };
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function compareVersionGroupsDescending(
|
|
40
|
-
groupA: CatalogEntityVersionHistoryGroup,
|
|
41
|
-
groupB: CatalogEntityVersionHistoryGroup,
|
|
42
|
-
): number {
|
|
43
|
-
const {
|
|
44
|
-
version: versionA,
|
|
45
|
-
isNotSpecified: isNotSpecifiedA,
|
|
46
|
-
time: timeA,
|
|
47
|
-
hasCurrent: hasCurrentA,
|
|
48
|
-
} = extractGroupComparisonData(groupA);
|
|
49
|
-
const {
|
|
50
|
-
version: versionB,
|
|
51
|
-
isNotSpecified: isNotSpecifiedB,
|
|
52
|
-
time: timeB,
|
|
53
|
-
hasCurrent: hasCurrentB,
|
|
54
|
-
} = extractGroupComparisonData(groupB);
|
|
55
|
-
|
|
56
|
-
// First, compare by version date (oldest first, so most recent versions come first)
|
|
57
|
-
const dateComparison = timeB - timeA;
|
|
58
|
-
if (dateComparison !== 0) {
|
|
59
|
-
return dateComparison;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// If dates are equal, prioritize the one with isCurrent from backend
|
|
63
|
-
if (hasCurrentA !== hasCurrentB) {
|
|
64
|
-
return hasCurrentB ? 1 : -1; // hasCurrentB comes first if true
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// If both have same isCurrent status, compare by version
|
|
68
|
-
// If both are unknown or both are real versions, compare by version
|
|
69
|
-
if (isNotSpecifiedA === isNotSpecifiedB) {
|
|
70
|
-
return compareVersionsDescending(versionA, versionB);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// If one is unknown and one is a real version, unknown comes last
|
|
74
|
-
return isNotSpecifiedA ? 1 : -1;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
11
|
export type TransformRevisionsToVersionHistoryParams = {
|
|
78
12
|
revisions: BffCatalogEntityRevision[];
|
|
79
13
|
currentRevisionDate?: string;
|
|
@@ -100,13 +34,7 @@ export function transformRevisionsToVersionHistory({
|
|
|
100
34
|
});
|
|
101
35
|
|
|
102
36
|
const versionGroups = Array.from(versionMap.entries()).map(([version, versionRevisions]) => {
|
|
103
|
-
const
|
|
104
|
-
const dateA = a.revision ? new Date(a.revision).getTime() : 0;
|
|
105
|
-
const dateB = b.revision ? new Date(b.revision).getTime() : 0;
|
|
106
|
-
return dateB - dateA;
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
const latestRevision = sortedRevisions[0];
|
|
37
|
+
const latestRevision = versionRevisions[0];
|
|
110
38
|
|
|
111
39
|
const versionMatches =
|
|
112
40
|
normalizedCurrentVersion === undefined || normalizedCurrentVersion === version;
|
|
@@ -114,7 +42,7 @@ export function transformRevisionsToVersionHistory({
|
|
|
114
42
|
let isCurrent: boolean;
|
|
115
43
|
if (currentRevisionDate !== undefined) {
|
|
116
44
|
// Check if revision matches AND version matches
|
|
117
|
-
const revisionMatches =
|
|
45
|
+
const revisionMatches = versionRevisions.some((rev) => rev.revision === currentRevisionDate);
|
|
118
46
|
isCurrent = revisionMatches && versionMatches;
|
|
119
47
|
} else {
|
|
120
48
|
// When no revision is specified, use isCurrent flag from the latest revision
|
|
@@ -123,11 +51,11 @@ export function transformRevisionsToVersionHistory({
|
|
|
123
51
|
}
|
|
124
52
|
|
|
125
53
|
// Check if any revision in this version group is the default version
|
|
126
|
-
const isDefaultVersion =
|
|
54
|
+
const isDefaultVersion = versionRevisions.some((rev) => rev.isDefaultVersion === true);
|
|
127
55
|
|
|
128
56
|
const revisions =
|
|
129
|
-
|
|
130
|
-
?
|
|
57
|
+
versionRevisions.length > 1
|
|
58
|
+
? versionRevisions.map((rev, index): CatalogEntityRevision => {
|
|
131
59
|
const revisionMatches = currentRevisionDate
|
|
132
60
|
? rev.revision === currentRevisionDate
|
|
133
61
|
: false;
|
|
@@ -138,7 +66,7 @@ export function transformRevisionsToVersionHistory({
|
|
|
138
66
|
index === 0 &&
|
|
139
67
|
isCurrent;
|
|
140
68
|
return {
|
|
141
|
-
name: `r.${
|
|
69
|
+
name: `r.${versionRevisions.length - index}`,
|
|
142
70
|
date: toLocalizedShortDateTime(rev.revision, locale),
|
|
143
71
|
revisionDate: rev.revision,
|
|
144
72
|
isActive: isActiveRevision || isCurrentByDefault,
|
|
@@ -155,9 +83,9 @@ export function transformRevisionsToVersionHistory({
|
|
|
155
83
|
hasCurrentRevisionFromBackend: latestRevision?.isCurrent ?? false,
|
|
156
84
|
isDefaultVersion,
|
|
157
85
|
revisions,
|
|
158
|
-
singleRevisionDate:
|
|
86
|
+
singleRevisionDate: versionRevisions.length === 1 ? versionRevisions[0]?.revision : undefined,
|
|
159
87
|
};
|
|
160
88
|
});
|
|
161
89
|
|
|
162
|
-
return versionGroups
|
|
90
|
+
return versionGroups;
|
|
163
91
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
|
|
4
|
+
import type { IconProps } from '@redocly/theme/icons/types';
|
|
5
|
+
|
|
6
|
+
import { getCssColorVariable } from '@redocly/theme/core/utils';
|
|
7
|
+
|
|
8
|
+
const Icon = (props: IconProps) => (
|
|
9
|
+
<svg viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
|
10
|
+
<path d="M1 1H5V2.5H2.5V5H1V1Z" />
|
|
11
|
+
<path d="M15 1H11V2.5H13.5V5H15V1Z" />
|
|
12
|
+
<path d="M1 15H5V13.5H2.5V11H1V15Z" />
|
|
13
|
+
<path d="M15 15H11V13.5H13.5V11H15V15Z" />
|
|
14
|
+
</svg>
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
export const FitToViewIcon = styled(Icon).attrs(() => ({
|
|
18
|
+
'data-component-name': 'icons/FitToViewIcon/FitToViewIcon',
|
|
19
|
+
}))<IconProps>`
|
|
20
|
+
path {
|
|
21
|
+
fill: ${({ color }) => getCssColorVariable(color)};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
height: ${({ size }) => size || '16px'};
|
|
25
|
+
width: ${({ size }) => size || '16px'};
|
|
26
|
+
`;
|
package/src/index.ts
CHANGED
|
@@ -27,6 +27,7 @@ export * from '@redocly/theme/components/Tags/CounterTag';
|
|
|
27
27
|
export * from '@redocly/theme/components/VersionPicker/VersionPicker';
|
|
28
28
|
export * from '@redocly/theme/components/Marker/Marker';
|
|
29
29
|
export * from '@redocly/theme/components/PageActions/PageActions';
|
|
30
|
+
export * from '@redocly/theme/components/SvgViewer/SvgViewer';
|
|
30
31
|
/* Buttons */
|
|
31
32
|
export * from '@redocly/theme/components/Buttons/CopyButton';
|
|
32
33
|
export * from '@redocly/theme/components/Buttons/EditPageButton';
|
|
@@ -293,6 +294,7 @@ export * from '@redocly/theme/icons/WorkflowHierarchyIcon/WorkflowHierarchyIcon'
|
|
|
293
294
|
export * from '@redocly/theme/icons/GenericIcon/GenericIcon';
|
|
294
295
|
export * from '@redocly/theme/icons/ShareIcon/ShareIcon';
|
|
295
296
|
export * from '@redocly/theme/icons/HashtagIcon/HashtagIcon';
|
|
297
|
+
export * from '@redocly/theme/icons/FitToViewIcon/FitToViewIcon';
|
|
296
298
|
/* Layouts */
|
|
297
299
|
export * from '@redocly/theme/layouts/RootLayout';
|
|
298
300
|
export * from '@redocly/theme/layouts/PageLayout';
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
2
|
import styled from 'styled-components';
|
|
3
3
|
|
|
4
4
|
import type { JSX } from 'react';
|
|
5
5
|
|
|
6
6
|
import { concatClassNames } from '@redocly/theme/core/utils';
|
|
7
|
+
import { useThemeHooks } from '@redocly/theme/core/hooks';
|
|
8
|
+
import { SvgViewer } from '@redocly/theme/components/SvgViewer/SvgViewer';
|
|
7
9
|
|
|
8
10
|
type MermaidProps = {
|
|
9
11
|
diagramHtml: string;
|
|
@@ -18,20 +20,55 @@ export function Mermaid({
|
|
|
18
20
|
'data-hash': dataHash,
|
|
19
21
|
className,
|
|
20
22
|
}: MermaidProps): JSX.Element {
|
|
23
|
+
const { useTranslate } = useThemeHooks();
|
|
24
|
+
const { translate } = useTranslate();
|
|
25
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
26
|
+
|
|
27
|
+
const open = () => setIsOpen(true);
|
|
28
|
+
const close = () => setIsOpen(false);
|
|
29
|
+
|
|
21
30
|
return (
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
31
|
+
<>
|
|
32
|
+
<Wrapper
|
|
33
|
+
className={concatClassNames('mermaid-wrapper', className)}
|
|
34
|
+
dangerouslySetInnerHTML={{ __html: diagramHtml }}
|
|
35
|
+
data-component-name="Markdoc/Mermaid/Mermaid"
|
|
36
|
+
data-source={dataSource}
|
|
37
|
+
data-hash={dataHash}
|
|
38
|
+
onClick={open}
|
|
39
|
+
onKeyDown={(e) => e.key === 'Enter' || (e.key === ' ' && open())}
|
|
40
|
+
role="button"
|
|
41
|
+
tabIndex={0}
|
|
42
|
+
aria-label={translate('mermaid.openFullscreen', 'Click to open diagram in fullscreen')}
|
|
43
|
+
/>
|
|
44
|
+
<SvgViewer
|
|
45
|
+
isOpen={isOpen}
|
|
46
|
+
onClose={close}
|
|
47
|
+
labels={{
|
|
48
|
+
zoomIn: translate('mermaid.zoomIn', 'Zoom in'),
|
|
49
|
+
zoomOut: translate('mermaid.zoomOut', 'Zoom out'),
|
|
50
|
+
fitToView: translate('mermaid.reset', 'Fit to view'),
|
|
51
|
+
close: translate('mermaid.close', 'Close'),
|
|
52
|
+
dialogLabel: translate('mermaid.viewer', 'Mermaid diagram viewer'),
|
|
53
|
+
}}
|
|
54
|
+
>
|
|
55
|
+
<ViewerContent dangerouslySetInnerHTML={{ __html: diagramHtml }} />
|
|
56
|
+
</SvgViewer>
|
|
57
|
+
</>
|
|
29
58
|
);
|
|
30
59
|
}
|
|
31
60
|
|
|
32
61
|
const Wrapper = styled.div`
|
|
33
62
|
background-color: var(--mermaid-bg-color);
|
|
34
63
|
border-radius: var(--mermaid-border-radius);
|
|
64
|
+
cursor: pointer;
|
|
65
|
+
transition: box-shadow 0.2s ease;
|
|
66
|
+
|
|
67
|
+
&:hover,
|
|
68
|
+
&:focus {
|
|
69
|
+
outline: none;
|
|
70
|
+
box-shadow: 0 0 0 2px var(--border-color-input-focus);
|
|
71
|
+
}
|
|
35
72
|
|
|
36
73
|
* {
|
|
37
74
|
font-family: var(--mermaid-font-family) !important;
|
|
@@ -42,3 +79,15 @@ const Wrapper = styled.div`
|
|
|
42
79
|
max-width: 100%;
|
|
43
80
|
}
|
|
44
81
|
`;
|
|
82
|
+
|
|
83
|
+
const ViewerContent = styled.div`
|
|
84
|
+
* {
|
|
85
|
+
font-family: var(--mermaid-font-family) !important;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.mermaid > svg {
|
|
89
|
+
font-size: var(--font-size-base) !important;
|
|
90
|
+
display: block;
|
|
91
|
+
max-width: none !important;
|
|
92
|
+
}
|
|
93
|
+
`;
|