@scm-manager/ui-components 4.0.0-REACT19-20250910-124634 → 4.0.0-REACT19-20250915-121549
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/package.json +12 -12
- package/src/Image.tsx +3 -2
- package/src/__snapshots__/storyshots.test.ts.snap +32 -15
- package/src/avatar/AvatarImage.tsx +2 -2
- package/src/config/ConfigurationBinder.tsx +27 -39
- package/src/config/TitledSettings.tsx +8 -9
- package/src/layout/Footer.tsx +7 -11
- package/src/repos/DiffTypes.ts +16 -2
- package/src/repos/changesets/ChangesetAuthor.tsx +2 -2
- package/src/repos/changesets/Contributor.tsx +1 -1
- package/src/repos/changesets/SingleChangeset.tsx +1 -0
- package/src/repos/diff/DiffFileHunk.tsx +12 -8
- package/src/repos/diff/DiffFileTree.tsx +89 -37
- package/src/repos/diff/LazyDiffFile.tsx +9 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scm-manager/ui-components",
|
|
3
|
-
"version": "4.0.0-REACT19-
|
|
3
|
+
"version": "4.0.0-REACT19-20250915-121549",
|
|
4
4
|
"description": "UI Components for SCM-Manager and its plugins",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"files": [
|
|
@@ -37,10 +37,10 @@
|
|
|
37
37
|
"@scm-manager/jest-preset": "^2.15.0-alpha1",
|
|
38
38
|
"@scm-manager/prettier-config": "^2.12.0",
|
|
39
39
|
"@scm-manager/tsconfig": "^2.13.0",
|
|
40
|
-
"@scm-manager/ui-shortcuts": "4.0.0-REACT19-
|
|
41
|
-
"@scm-manager/ui-syntaxhighlighting": "4.0.0-REACT19-
|
|
42
|
-
"@scm-manager/ui-text": "4.0.0-REACT19-
|
|
43
|
-
"@scm-manager/ui-types": "4.0.0-REACT19-
|
|
40
|
+
"@scm-manager/ui-shortcuts": "4.0.0-REACT19-20250915-121549",
|
|
41
|
+
"@scm-manager/ui-syntaxhighlighting": "4.0.0-REACT19-20250915-121549",
|
|
42
|
+
"@scm-manager/ui-text": "4.0.0-REACT19-20250915-121549",
|
|
43
|
+
"@scm-manager/ui-types": "4.0.0-REACT19-20250915-121549",
|
|
44
44
|
"@storybook/addon-actions": "^9.0.8",
|
|
45
45
|
"@storybook/addon-docs": "^9.1.5",
|
|
46
46
|
"@storybook/addon-essentials": "^9.0.0-alpha.12",
|
|
@@ -67,15 +67,15 @@
|
|
|
67
67
|
"storybook-addon-themes": "^6.1.0"
|
|
68
68
|
},
|
|
69
69
|
"dependencies": {
|
|
70
|
-
"@scm-manager/ui-api": "4.0.0-REACT19-
|
|
71
|
-
"@scm-manager/ui-buttons": "4.0.0-REACT19-
|
|
72
|
-
"@scm-manager/ui-core": "4.0.0-REACT19-
|
|
73
|
-
"@scm-manager/ui-extensions": "4.0.0-REACT19-
|
|
74
|
-
"@scm-manager/ui-layout": "4.0.0-REACT19-
|
|
75
|
-
"@scm-manager/ui-overlays": "4.0.0-REACT19-
|
|
70
|
+
"@scm-manager/ui-api": "4.0.0-REACT19-20250915-121549",
|
|
71
|
+
"@scm-manager/ui-buttons": "4.0.0-REACT19-20250915-121549",
|
|
72
|
+
"@scm-manager/ui-core": "4.0.0-REACT19-20250915-121549",
|
|
73
|
+
"@scm-manager/ui-extensions": "4.0.0-REACT19-20250915-121549",
|
|
74
|
+
"@scm-manager/ui-layout": "4.0.0-REACT19-20250915-121549",
|
|
75
|
+
"@scm-manager/ui-overlays": "4.0.0-REACT19-20250915-121549",
|
|
76
76
|
"deepmerge": "^4.2.2",
|
|
77
77
|
"hast-util-sanitize": "^3.0.2",
|
|
78
|
-
"react-diff-view": "
|
|
78
|
+
"react-diff-view": "2.6.0",
|
|
79
79
|
"react-error-boundary": "^6.0.0",
|
|
80
80
|
"react-hook-form": "^7.33.1",
|
|
81
81
|
"react-select": "^2.1.2",
|
package/src/Image.tsx
CHANGED
|
@@ -22,6 +22,7 @@ type Props = {
|
|
|
22
22
|
alt: string;
|
|
23
23
|
title?: string;
|
|
24
24
|
className?: string;
|
|
25
|
+
crossOrigin?: "anonymous" | "";
|
|
25
26
|
};
|
|
26
27
|
|
|
27
28
|
/**
|
|
@@ -37,8 +38,8 @@ class Image extends React.Component<Props> {
|
|
|
37
38
|
};
|
|
38
39
|
|
|
39
40
|
render() {
|
|
40
|
-
const { alt, title, className } = this.props;
|
|
41
|
-
return <img className={className} src={this.createImageSrc()} alt={alt} title={title} />;
|
|
41
|
+
const { alt, title, className, crossOrigin } = this.props;
|
|
42
|
+
return <img className={className} src={this.createImageSrc()} alt={alt} title={title} crossOrigin={crossOrigin} />;
|
|
42
43
|
}
|
|
43
44
|
}
|
|
44
45
|
|
|
@@ -1825,18 +1825,20 @@ exports[`Storyshots Footer Full 1`] = `
|
|
|
1825
1825
|
className="has-text-weight-bold mb-2"
|
|
1826
1826
|
>
|
|
1827
1827
|
<div
|
|
1828
|
+
className="is-flex"
|
|
1828
1829
|
data-testid="trillian-mcmillian"
|
|
1829
1830
|
>
|
|
1830
|
-
<
|
|
1831
|
-
className="Footer__AvatarContainer-sc-k70cxq-1 kckyuz image is-rounded"
|
|
1832
|
-
>
|
|
1831
|
+
<div>
|
|
1833
1832
|
<img
|
|
1834
1833
|
alt="trillian"
|
|
1835
|
-
className="is-rounded Footer__VCenteredAvatar-sc-k70cxq-0
|
|
1834
|
+
className="is-rounded Footer__VCenteredAvatar-sc-k70cxq-0 gNPbnb"
|
|
1835
|
+
crossOrigin="anonymous"
|
|
1836
1836
|
src="test-file-stub"
|
|
1837
1837
|
/>
|
|
1838
|
-
</
|
|
1839
|
-
|
|
1838
|
+
</div>
|
|
1839
|
+
<div>
|
|
1840
|
+
Trillian McMillian
|
|
1841
|
+
</div>
|
|
1840
1842
|
</div>
|
|
1841
1843
|
</h2>
|
|
1842
1844
|
<ul
|
|
@@ -2031,18 +2033,20 @@ exports[`Storyshots Footer With Avatar 1`] = `
|
|
|
2031
2033
|
className="has-text-weight-bold mb-2"
|
|
2032
2034
|
>
|
|
2033
2035
|
<div
|
|
2036
|
+
className="is-flex"
|
|
2034
2037
|
data-testid="trillian-mcmillian"
|
|
2035
2038
|
>
|
|
2036
|
-
<
|
|
2037
|
-
className="Footer__AvatarContainer-sc-k70cxq-1 kckyuz image is-rounded"
|
|
2038
|
-
>
|
|
2039
|
+
<div>
|
|
2039
2040
|
<img
|
|
2040
2041
|
alt="trillian"
|
|
2041
|
-
className="is-rounded Footer__VCenteredAvatar-sc-k70cxq-0
|
|
2042
|
+
className="is-rounded Footer__VCenteredAvatar-sc-k70cxq-0 gNPbnb"
|
|
2043
|
+
crossOrigin="anonymous"
|
|
2042
2044
|
src="test-file-stub"
|
|
2043
2045
|
/>
|
|
2044
|
-
</
|
|
2045
|
-
|
|
2046
|
+
</div>
|
|
2047
|
+
<div>
|
|
2048
|
+
Trillian McMillian
|
|
2049
|
+
</div>
|
|
2046
2050
|
</div>
|
|
2047
2051
|
</h2>
|
|
2048
2052
|
<ul
|
|
@@ -20073,6 +20077,7 @@ exports[`Storyshots Repositories/Annotate With Avatars 1`] = `
|
|
|
20073
20077
|
<img
|
|
20074
20078
|
alt="Arthur Dent"
|
|
20075
20079
|
className="has-rounded-border AuthorImage-sc-ygvjd8-0 dTapGF"
|
|
20080
|
+
crossOrigin="anonymous"
|
|
20076
20081
|
src="https://robohash.org/arthur.dent@hitchhiker.com.png"
|
|
20077
20082
|
/>
|
|
20078
20083
|
Arthur Dent
|
|
@@ -20135,6 +20140,7 @@ exports[`Storyshots Repositories/Annotate With Avatars 1`] = `
|
|
|
20135
20140
|
<img
|
|
20136
20141
|
alt="Tricia Marie McMillan"
|
|
20137
20142
|
className="has-rounded-border AuthorImage-sc-ygvjd8-0 dTapGF"
|
|
20143
|
+
crossOrigin="anonymous"
|
|
20138
20144
|
src="https://robohash.org/trillian@hitchhiker.com.png"
|
|
20139
20145
|
/>
|
|
20140
20146
|
Tricia Marie McMillan
|
|
@@ -20197,6 +20203,7 @@ exports[`Storyshots Repositories/Annotate With Avatars 1`] = `
|
|
|
20197
20203
|
<img
|
|
20198
20204
|
alt="Arthur Dent"
|
|
20199
20205
|
className="has-rounded-border AuthorImage-sc-ygvjd8-0 dTapGF"
|
|
20206
|
+
crossOrigin="anonymous"
|
|
20200
20207
|
src="https://robohash.org/arthur.dent@hitchhiker.com.png"
|
|
20201
20208
|
/>
|
|
20202
20209
|
Arthur Dent
|
|
@@ -20241,6 +20248,7 @@ exports[`Storyshots Repositories/Annotate With Avatars 1`] = `
|
|
|
20241
20248
|
<img
|
|
20242
20249
|
alt="Ford Prefect"
|
|
20243
20250
|
className="has-rounded-border AuthorImage-sc-ygvjd8-0 dTapGF"
|
|
20251
|
+
crossOrigin="anonymous"
|
|
20244
20252
|
src="https://robohash.org/ford.prefect@hitchhiker.com.png"
|
|
20245
20253
|
/>
|
|
20246
20254
|
Ford Prefect
|
|
@@ -20285,6 +20293,7 @@ exports[`Storyshots Repositories/Annotate With Avatars 1`] = `
|
|
|
20285
20293
|
<img
|
|
20286
20294
|
alt="Arthur Dent"
|
|
20287
20295
|
className="has-rounded-border AuthorImage-sc-ygvjd8-0 dTapGF"
|
|
20296
|
+
crossOrigin="anonymous"
|
|
20288
20297
|
src="https://robohash.org/arthur.dent@hitchhiker.com.png"
|
|
20289
20298
|
/>
|
|
20290
20299
|
Arthur Dent
|
|
@@ -20367,11 +20376,12 @@ exports[`Storyshots Repositories/Changesets Co-Authors with avatar 1`] = `
|
|
|
20367
20376
|
className="media-left mt-2 mr-2"
|
|
20368
20377
|
>
|
|
20369
20378
|
<div
|
|
20370
|
-
className="SingleChangeset__FixedSizedAvatar-sc-ytpqp9-0
|
|
20379
|
+
className="SingleChangeset__FixedSizedAvatar-sc-ytpqp9-0 gwbxYG image"
|
|
20371
20380
|
>
|
|
20372
20381
|
<img
|
|
20373
20382
|
alt="SCM Administrator"
|
|
20374
20383
|
className="has-rounded-border"
|
|
20384
|
+
crossOrigin="anonymous"
|
|
20375
20385
|
src="https://robohash.org/scm-admin@scm-manager.org"
|
|
20376
20386
|
/>
|
|
20377
20387
|
</div>
|
|
@@ -20425,6 +20435,7 @@ exports[`Storyshots Repositories/Changesets Co-Authors with avatar 1`] = `
|
|
|
20425
20435
|
<img
|
|
20426
20436
|
alt="Ford Prefect"
|
|
20427
20437
|
className="ContributorAvatar-sc-1yz8zn-0 lhgGHS"
|
|
20438
|
+
crossOrigin="anonymous"
|
|
20428
20439
|
src="https://robohash.org/ford.prefect@hitchhiker.com"
|
|
20429
20440
|
/>
|
|
20430
20441
|
</a>
|
|
@@ -20435,6 +20446,7 @@ exports[`Storyshots Repositories/Changesets Co-Authors with avatar 1`] = `
|
|
|
20435
20446
|
<img
|
|
20436
20447
|
alt="Zaphod Beeblebrox"
|
|
20437
20448
|
className="ContributorAvatar-sc-1yz8zn-0 lhgGHS"
|
|
20449
|
+
crossOrigin="anonymous"
|
|
20438
20450
|
src="https://robohash.org/zaphod.beeblebrox@hitchhiker.cm"
|
|
20439
20451
|
/>
|
|
20440
20452
|
</a>
|
|
@@ -20445,6 +20457,7 @@ exports[`Storyshots Repositories/Changesets Co-Authors with avatar 1`] = `
|
|
|
20445
20457
|
<img
|
|
20446
20458
|
alt="Tricia Marie McMillan"
|
|
20447
20459
|
className="ContributorAvatar-sc-1yz8zn-0 lhgGHS"
|
|
20460
|
+
crossOrigin="anonymous"
|
|
20448
20461
|
src="https://robohash.org/trillian@hitchhiker.cm"
|
|
20449
20462
|
/>
|
|
20450
20463
|
</a>
|
|
@@ -20541,11 +20554,12 @@ exports[`Storyshots Repositories/Changesets Commiter and Co-Authors with avatar
|
|
|
20541
20554
|
className="media-left mt-2 mr-2"
|
|
20542
20555
|
>
|
|
20543
20556
|
<div
|
|
20544
|
-
className="SingleChangeset__FixedSizedAvatar-sc-ytpqp9-0
|
|
20557
|
+
className="SingleChangeset__FixedSizedAvatar-sc-ytpqp9-0 gwbxYG image"
|
|
20545
20558
|
>
|
|
20546
20559
|
<img
|
|
20547
20560
|
alt="SCM Administrator"
|
|
20548
20561
|
className="has-rounded-border"
|
|
20562
|
+
crossOrigin="anonymous"
|
|
20549
20563
|
src="https://robohash.org/scm-admin@scm-manager.org"
|
|
20550
20564
|
/>
|
|
20551
20565
|
</div>
|
|
@@ -20594,6 +20608,7 @@ exports[`Storyshots Repositories/Changesets Commiter and Co-Authors with avatar
|
|
|
20594
20608
|
<img
|
|
20595
20609
|
alt="Zaphod Beeblebrox"
|
|
20596
20610
|
className="ContributorAvatar-sc-1yz8zn-0 lhgGHS"
|
|
20611
|
+
crossOrigin="anonymous"
|
|
20597
20612
|
src="https://robohash.org/zaphod.beeblebrox@hitchhiker.cm"
|
|
20598
20613
|
/>
|
|
20599
20614
|
</a>
|
|
@@ -20609,6 +20624,7 @@ exports[`Storyshots Repositories/Changesets Commiter and Co-Authors with avatar
|
|
|
20609
20624
|
<img
|
|
20610
20625
|
alt="Ford Prefect"
|
|
20611
20626
|
className="ContributorAvatar-sc-1yz8zn-0 lhgGHS"
|
|
20627
|
+
crossOrigin="anonymous"
|
|
20612
20628
|
src="https://robohash.org/ford.prefect@hitchhiker.com"
|
|
20613
20629
|
/>
|
|
20614
20630
|
</a>
|
|
@@ -21613,11 +21629,12 @@ exports[`Storyshots Repositories/Changesets With avatar 1`] = `
|
|
|
21613
21629
|
className="media-left mt-2 mr-2"
|
|
21614
21630
|
>
|
|
21615
21631
|
<div
|
|
21616
|
-
className="SingleChangeset__FixedSizedAvatar-sc-ytpqp9-0
|
|
21632
|
+
className="SingleChangeset__FixedSizedAvatar-sc-ytpqp9-0 gwbxYG image"
|
|
21617
21633
|
>
|
|
21618
21634
|
<img
|
|
21619
21635
|
alt="SCM Administrator"
|
|
21620
21636
|
className="has-rounded-border"
|
|
21637
|
+
crossOrigin="anonymous"
|
|
21621
21638
|
src="test-file-stub"
|
|
21622
21639
|
/>
|
|
21623
21640
|
</div>
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import React, { FC } from "react";
|
|
18
|
-
import Image from "
|
|
18
|
+
import { Image } from "@scm-manager/ui-core";
|
|
19
19
|
import { EXTENSION_POINT, Person } from "./Avatar";
|
|
20
20
|
import { useBinder } from "@scm-manager/ui-extensions";
|
|
21
21
|
|
|
@@ -36,7 +36,7 @@ const AvatarImage: FC<Props> = ({ person, representation = "rounded-border", cla
|
|
|
36
36
|
classes += " " + className;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
return <Image className={classes} src={avatar} alt={person.name} />;
|
|
39
|
+
return <Image className={classes} src={avatar} alt={person.name} crossOrigin="anonymous" />;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
return null;
|
|
@@ -19,13 +19,13 @@ import { binder, extensionPoints } from "@scm-manager/ui-extensions";
|
|
|
19
19
|
import { NavLink } from "../navigation";
|
|
20
20
|
import { Route } from "react-router-dom";
|
|
21
21
|
import { useTranslation, WithTranslation, withTranslation } from "react-i18next";
|
|
22
|
-
import { Link, Links,
|
|
22
|
+
import { Link, Links, Repository } from "@scm-manager/ui-types";
|
|
23
23
|
import {
|
|
24
24
|
TitledGlobalSettingComponent,
|
|
25
25
|
TitledNamespaceSettingComponent,
|
|
26
26
|
TitledRepositorySettingComponent,
|
|
27
27
|
} from "./TitledSettings";
|
|
28
|
-
import { NAMESPACE_SUB_ROUTES,
|
|
28
|
+
import { NAMESPACE_SUB_ROUTES, REPOSITORY_SUB_ROUTES } from "@scm-manager/ui-core";
|
|
29
29
|
|
|
30
30
|
type GlobalRouteProps = {
|
|
31
31
|
url: string;
|
|
@@ -37,7 +37,7 @@ type RepositoryRouteProps = {
|
|
|
37
37
|
repository: Repository;
|
|
38
38
|
};
|
|
39
39
|
|
|
40
|
-
type RepositoryNavProps = WithTranslation & {
|
|
40
|
+
type RepositoryNavProps = WithTranslation & { absoluteRepoUrl: string };
|
|
41
41
|
|
|
42
42
|
type NamespaceNavProps = WithTranslation & { url: string };
|
|
43
43
|
|
|
@@ -119,8 +119,8 @@ class ConfigurationBinder {
|
|
|
119
119
|
};
|
|
120
120
|
|
|
121
121
|
// create NavigationLink with translated label
|
|
122
|
-
const RepoNavLink = withTranslation(this.i18nNamespace)(({ t,
|
|
123
|
-
return this.navLink(
|
|
122
|
+
const RepoNavLink = withTranslation(this.i18nNamespace)(({ t, absoluteRepoUrl }: RepositoryNavProps) => {
|
|
123
|
+
return this.navLink(absoluteRepoUrl + to, labelI18nKey, t);
|
|
124
124
|
});
|
|
125
125
|
|
|
126
126
|
// bind navigation link to extension point
|
|
@@ -144,7 +144,7 @@ class ConfigurationBinder {
|
|
|
144
144
|
to: string,
|
|
145
145
|
labelI18nKey: string,
|
|
146
146
|
linkName: string,
|
|
147
|
-
RepositoryComponent:
|
|
147
|
+
RepositoryComponent: ComponentType<object>,
|
|
148
148
|
sortKey?: string,
|
|
149
149
|
) {
|
|
150
150
|
// create predicate based on the link name of the current repository route
|
|
@@ -153,41 +153,37 @@ class ConfigurationBinder {
|
|
|
153
153
|
return props.repository && props.repository._links && props.repository._links[linkName];
|
|
154
154
|
};
|
|
155
155
|
|
|
156
|
+
const sanitizedTo = to.startsWith("/") ? to.substring(1) : to;
|
|
157
|
+
|
|
156
158
|
// create NavigationLink with translated label
|
|
157
|
-
const RepoNavLink = withTranslation(this.i18nNamespace)(({ t,
|
|
158
|
-
return this.navLink(
|
|
159
|
+
const RepoNavLink = withTranslation(this.i18nNamespace)(({ t, absoluteRepoUrl }: RepositoryNavProps) => {
|
|
160
|
+
return this.navLink(`${absoluteRepoUrl}/${REPOSITORY_SUB_ROUTES.SETTINGS}/${sanitizedTo}`, labelI18nKey, t, {
|
|
161
|
+
activeOnlyWhenExact: false,
|
|
162
|
+
});
|
|
159
163
|
});
|
|
160
164
|
|
|
161
165
|
// bind navigation link to extension point
|
|
162
166
|
binder.bind("repository.setting", RepoNavLink, repoPredicate, sortKey ?? linkName);
|
|
163
167
|
|
|
164
168
|
// route for global configuration, passes the current repository to component
|
|
165
|
-
const RepoRoute = ({
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
i18nNamespace={this.i18nNamespace}
|
|
173
|
-
label={labelI18nKey}
|
|
174
|
-
repository={repository}
|
|
175
|
-
>
|
|
176
|
-
<RepositoryComponent repository={repository} link={(link as Link).href} {...additionalProps} />
|
|
177
|
-
</TitledRepositorySettingComponent>,
|
|
178
|
-
);
|
|
179
|
-
}
|
|
169
|
+
const RepoRoute = ({ ...additionalProps }: RepositoryRouteProps) => {
|
|
170
|
+
return this.route(
|
|
171
|
+
"settings" + to,
|
|
172
|
+
<TitledRepositorySettingComponent i18nNamespace={this.i18nNamespace} label={labelI18nKey}>
|
|
173
|
+
<RepositoryComponent {...additionalProps} />
|
|
174
|
+
</TitledRepositorySettingComponent>,
|
|
175
|
+
);
|
|
180
176
|
};
|
|
181
177
|
|
|
182
178
|
// bind config route to extension point
|
|
183
|
-
binder.bind("repository.route", RepoRoute
|
|
179
|
+
binder.bind("repository.route", RepoRoute);
|
|
184
180
|
}
|
|
185
181
|
|
|
186
182
|
bindNamespaceSetting(
|
|
187
183
|
to: string,
|
|
188
184
|
labelI18nKey: string,
|
|
189
185
|
linkName: string,
|
|
190
|
-
ConfigurationComponent: ComponentType<
|
|
186
|
+
ConfigurationComponent: ComponentType<object>,
|
|
191
187
|
) {
|
|
192
188
|
const namespacePredicate = (props: extensionPoints.NamespaceSetting["props"]) =>
|
|
193
189
|
props.namespace && props.namespace._links && props.namespace._links[linkName];
|
|
@@ -200,20 +196,12 @@ class ConfigurationBinder {
|
|
|
200
196
|
binder.bind<extensionPoints.NamespaceSetting>("namespace.setting", NamespaceNavLink, namespacePredicate);
|
|
201
197
|
|
|
202
198
|
const NamespaceRoute = ({ ...additionalProps }: NamespaceNavProps) => {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
NAMESPACE_SUB_ROUTES.SETTINGS + to,
|
|
210
|
-
<TitledNamespaceSettingComponent i18nNamespace={this.i18nNamespace} label={labelI18nKey}>
|
|
211
|
-
<ConfigurationComponent namespace={namespace} link={(link as Link).href} {...additionalProps} />
|
|
212
|
-
</TitledNamespaceSettingComponent>,
|
|
213
|
-
);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
return null;
|
|
199
|
+
return this.route(
|
|
200
|
+
NAMESPACE_SUB_ROUTES.SETTINGS + to,
|
|
201
|
+
<TitledNamespaceSettingComponent i18nNamespace={this.i18nNamespace} label={labelI18nKey}>
|
|
202
|
+
<ConfigurationComponent {...additionalProps} />
|
|
203
|
+
</TitledNamespaceSettingComponent>,
|
|
204
|
+
);
|
|
217
205
|
};
|
|
218
206
|
|
|
219
207
|
binder.bind("namespace.route", NamespaceRoute);
|
|
@@ -16,8 +16,12 @@
|
|
|
16
16
|
|
|
17
17
|
import React, { FC, ReactNode } from "react";
|
|
18
18
|
import { useTranslation } from "react-i18next";
|
|
19
|
-
import {
|
|
20
|
-
|
|
19
|
+
import {
|
|
20
|
+
useDocumentTitle,
|
|
21
|
+
useDocumentTitleForRepository,
|
|
22
|
+
useNamespaceFromParams,
|
|
23
|
+
useRepositoryFromParams,
|
|
24
|
+
} from "@scm-manager/ui-core";
|
|
21
25
|
|
|
22
26
|
type GlobalProps = {
|
|
23
27
|
i18nNamespace: string;
|
|
@@ -35,16 +39,11 @@ export const TitledGlobalSettingComponent: FC<GlobalProps> = ({ children, i18nNa
|
|
|
35
39
|
type RepositoryProps = {
|
|
36
40
|
i18nNamespace: string;
|
|
37
41
|
label: string;
|
|
38
|
-
repository: Repository;
|
|
39
42
|
children?: ReactNode;
|
|
40
43
|
};
|
|
41
44
|
|
|
42
|
-
export const TitledRepositorySettingComponent: FC<RepositoryProps> = ({
|
|
43
|
-
|
|
44
|
-
i18nNamespace,
|
|
45
|
-
label,
|
|
46
|
-
repository,
|
|
47
|
-
}) => {
|
|
45
|
+
export const TitledRepositorySettingComponent: FC<RepositoryProps> = ({ children, i18nNamespace, label }) => {
|
|
46
|
+
const repository = useRepositoryFromParams();
|
|
48
47
|
const [t] = useTranslation(i18nNamespace);
|
|
49
48
|
const [commonTranslation] = useTranslation("commons");
|
|
50
49
|
useDocumentTitleForRepository(repository, t(label), commonTranslation("documentTitle.repositoryConfiguration"));
|
package/src/layout/Footer.tsx
CHANGED
|
@@ -51,23 +51,19 @@ type TitleWithAvatarProps = {
|
|
|
51
51
|
};
|
|
52
52
|
|
|
53
53
|
const VCenteredAvatar = styled(AvatarImage)`
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const AvatarContainer = styled.span`
|
|
58
|
-
float: left;
|
|
54
|
+
width: 1rem !important;
|
|
55
|
+
height: 1rem !important;
|
|
59
56
|
margin-right: 0.3em;
|
|
60
57
|
padding-top: 0.2em;
|
|
61
|
-
|
|
62
|
-
height: 1em;
|
|
58
|
+
border-radius: 100%;
|
|
63
59
|
`;
|
|
64
60
|
|
|
65
61
|
const TitleWithAvatar: FC<TitleWithAvatarProps> = ({ me }) => (
|
|
66
|
-
<div {...createAttributesForTesting(me.displayName)}>
|
|
67
|
-
<
|
|
62
|
+
<div {...createAttributesForTesting(me.displayName)} className="is-flex">
|
|
63
|
+
<div>
|
|
68
64
|
<VCenteredAvatar person={me} representation="rounded" />
|
|
69
|
-
</
|
|
70
|
-
{me.displayName}
|
|
65
|
+
</div>
|
|
66
|
+
<div>{me.displayName}</div>
|
|
71
67
|
</div>
|
|
72
68
|
);
|
|
73
69
|
|
package/src/repos/DiffTypes.ts
CHANGED
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
* along with this program. If not, see https://www.gnu.org/licenses/.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import { ReactNode } from "react";
|
|
17
|
+
import { FC, PropsWithChildren, ReactNode } from "react";
|
|
18
18
|
import { DefaultCollapsed } from "./defaultCollapsed";
|
|
19
|
-
import { Change, Hunk, FileDiff as File } from "@scm-manager/ui-types";
|
|
19
|
+
import { Change, Hunk, FileDiff as File, FileChangeType } from "@scm-manager/ui-types";
|
|
20
20
|
|
|
21
21
|
export type ChangeEvent = {
|
|
22
22
|
change: Change;
|
|
@@ -31,6 +31,20 @@ export type AnnotationFactoryContext = BaseContext;
|
|
|
31
31
|
|
|
32
32
|
export type FileAnnotationFactory = (file: File) => ReactNode[];
|
|
33
33
|
|
|
34
|
+
export type FileTreeNodeWrapper = FC<
|
|
35
|
+
PropsWithChildren & {
|
|
36
|
+
name: string;
|
|
37
|
+
path: string;
|
|
38
|
+
changeType?: FileChangeType;
|
|
39
|
+
iconName: string;
|
|
40
|
+
iconColor: string;
|
|
41
|
+
isFile: boolean;
|
|
42
|
+
originalIcon: ReactNode;
|
|
43
|
+
originalLabel: ReactNode;
|
|
44
|
+
isCurrentFile: boolean;
|
|
45
|
+
}
|
|
46
|
+
>;
|
|
47
|
+
|
|
34
48
|
// key = change id, value = react component
|
|
35
49
|
export type AnnotationFactory = (context: AnnotationFactoryContext) => {
|
|
36
50
|
[key: string]: any;
|
|
@@ -57,11 +57,11 @@ const ContributorWithAvatar: FC<PersonAvatarProps> = ({ person, avatar }) => {
|
|
|
57
57
|
if (person.mail) {
|
|
58
58
|
return (
|
|
59
59
|
<a href={"mailto:" + person.mail} title={t("changeset.contributors.mailto") + " " + person.mail}>
|
|
60
|
-
<ContributorAvatar src={avatar} alt={person.name} />
|
|
60
|
+
<ContributorAvatar src={avatar} alt={person.name} crossOrigin="anonymous" />
|
|
61
61
|
</a>
|
|
62
62
|
);
|
|
63
63
|
}
|
|
64
|
-
return <ContributorAvatar src={avatar} alt={person.name} title={person.name} />;
|
|
64
|
+
return <ContributorAvatar src={avatar} alt={person.name} title={person.name} crossOrigin="anonymous" />;
|
|
65
65
|
};
|
|
66
66
|
|
|
67
67
|
export const SingleContributor: FC<PersonProps> = ({ person, className, displayTextOnly }) => {
|
|
@@ -30,7 +30,7 @@ const Contributor: FC<{ person: Person }> = ({ person }) => {
|
|
|
30
30
|
if (avatar) {
|
|
31
31
|
prefix = (
|
|
32
32
|
<>
|
|
33
|
-
<ContributorAvatar src={avatar} alt={person.name} />{" "}
|
|
33
|
+
<ContributorAvatar src={avatar} alt={person.name} crossOrigin="anonymous" />{" "}
|
|
34
34
|
</>
|
|
35
35
|
);
|
|
36
36
|
}
|
|
@@ -21,7 +21,7 @@ import { StyledHunk } from "./styledElements";
|
|
|
21
21
|
import LastHunkFooter from "./LastHunkFooter";
|
|
22
22
|
import { DiffExpandedCallback, DiffFileProps, ErrorHandler } from "./types";
|
|
23
23
|
import HunkFooter from "./HunkFooter";
|
|
24
|
-
// @ts-
|
|
24
|
+
// @ts-expect-error react-diff-view does not provide types with v2
|
|
25
25
|
import { Decoration, getChangeKey } from "react-diff-view";
|
|
26
26
|
import { collectHunkAnnotations, markConflicts } from "./helpers";
|
|
27
27
|
import HunkHeader from "./HunkHeader";
|
|
@@ -70,7 +70,7 @@ const DiffFileHunk: FC<Props> = ({
|
|
|
70
70
|
onClick(context);
|
|
71
71
|
}
|
|
72
72
|
},
|
|
73
|
-
[file, onClick]
|
|
73
|
+
[file, onClick],
|
|
74
74
|
);
|
|
75
75
|
|
|
76
76
|
const gutterEvents = useMemo(
|
|
@@ -78,7 +78,7 @@ const DiffFileHunk: FC<Props> = ({
|
|
|
78
78
|
onClick && {
|
|
79
79
|
onClick: (event: ChangeEvent) => handleClickEvent(event.change, hunk),
|
|
80
80
|
},
|
|
81
|
-
[handleClickEvent, hunk, onClick]
|
|
81
|
+
[handleClickEvent, hunk, onClick],
|
|
82
82
|
);
|
|
83
83
|
|
|
84
84
|
if (shouldMarkConflicts && hunk.changes) {
|
|
@@ -92,13 +92,13 @@ const DiffFileHunk: FC<Props> = ({
|
|
|
92
92
|
expandableHunk={expandableHunk}
|
|
93
93
|
diffExpanded={diffExpanded}
|
|
94
94
|
diffExpansionFailed={diffExpansionFailed}
|
|
95
|
-
|
|
95
|
+
/>,
|
|
96
96
|
);
|
|
97
97
|
} else if (i > 0) {
|
|
98
98
|
items.push(
|
|
99
99
|
<Decoration key={"decoration-" + hunk.content}>
|
|
100
100
|
<hr className="my-2" />
|
|
101
|
-
</Decoration
|
|
101
|
+
</Decoration>,
|
|
102
102
|
);
|
|
103
103
|
}
|
|
104
104
|
|
|
@@ -112,7 +112,11 @@ const DiffFileHunk: FC<Props> = ({
|
|
|
112
112
|
icon={hunkGutterHoverIcon}
|
|
113
113
|
actionable={!!gutterEvents}
|
|
114
114
|
highlightLineOnHover={highlightLineOnHover}
|
|
115
|
-
|
|
115
|
+
selectedChanges={[]}
|
|
116
|
+
generateAnchorID={() => undefined}
|
|
117
|
+
// @ts-expect-error react-diff-view does not provide types with v2
|
|
118
|
+
renderGutter={({ renderDefault, wrapInAnchor }) => wrapInAnchor(renderDefault())}
|
|
119
|
+
/>,
|
|
116
120
|
);
|
|
117
121
|
if (file._links?.lines) {
|
|
118
122
|
if (i === (file.hunks ?? []).length - 1) {
|
|
@@ -122,7 +126,7 @@ const DiffFileHunk: FC<Props> = ({
|
|
|
122
126
|
expandableHunk={expandableHunk}
|
|
123
127
|
diffExpanded={diffExpanded}
|
|
124
128
|
diffExpansionFailed={diffExpansionFailed}
|
|
125
|
-
|
|
129
|
+
/>,
|
|
126
130
|
);
|
|
127
131
|
} else {
|
|
128
132
|
items.push(
|
|
@@ -131,7 +135,7 @@ const DiffFileHunk: FC<Props> = ({
|
|
|
131
135
|
expandableHunk={expandableHunk}
|
|
132
136
|
diffExpanded={diffExpanded}
|
|
133
137
|
diffExpansionFailed={diffExpansionFailed}
|
|
134
|
-
|
|
138
|
+
/>,
|
|
135
139
|
);
|
|
136
140
|
}
|
|
137
141
|
}
|
|
@@ -18,12 +18,19 @@ import React, { FC } from "react";
|
|
|
18
18
|
import { Link } from "react-router-dom";
|
|
19
19
|
import { useTranslation } from "react-i18next";
|
|
20
20
|
import classNames from "classnames";
|
|
21
|
-
import { FileTree } from "@scm-manager/ui-types";
|
|
21
|
+
import { FileChangeType, FileTree } from "@scm-manager/ui-types";
|
|
22
22
|
import { FileDiffContent, StackedSpan, StyledIcon } from "./styledElements";
|
|
23
|
+
import { FileTreeNodeWrapper } from "../DiffTypes";
|
|
23
24
|
|
|
24
|
-
type Props = {
|
|
25
|
+
type Props = {
|
|
26
|
+
tree: FileTree;
|
|
27
|
+
currentFile: string;
|
|
28
|
+
setCurrentFile: (path: string) => void;
|
|
29
|
+
gap?: number;
|
|
30
|
+
FileTreeNodeWrapper?: FileTreeNodeWrapper;
|
|
31
|
+
};
|
|
25
32
|
|
|
26
|
-
const DiffFileTree: FC<Props> = ({ tree, currentFile, setCurrentFile, gap = 15 }) => {
|
|
33
|
+
const DiffFileTree: FC<Props> = ({ tree, currentFile, setCurrentFile, gap = 15, FileTreeNodeWrapper }) => {
|
|
27
34
|
return (
|
|
28
35
|
<FileDiffContent gap={gap}>
|
|
29
36
|
{Object.keys(tree.children).map((key) => (
|
|
@@ -33,6 +40,7 @@ const DiffFileTree: FC<Props> = ({ tree, currentFile, setCurrentFile, gap = 15 }
|
|
|
33
40
|
parentPath=""
|
|
34
41
|
currentFile={currentFile}
|
|
35
42
|
setCurrentFile={setCurrentFile}
|
|
43
|
+
FileTreeNodeWrapper={FileTreeNodeWrapper}
|
|
36
44
|
/>
|
|
37
45
|
))}
|
|
38
46
|
</FileDiffContent>
|
|
@@ -41,9 +49,13 @@ const DiffFileTree: FC<Props> = ({ tree, currentFile, setCurrentFile, gap = 15 }
|
|
|
41
49
|
|
|
42
50
|
export default DiffFileTree;
|
|
43
51
|
|
|
44
|
-
type
|
|
45
|
-
|
|
46
|
-
|
|
52
|
+
type NodeProps = {
|
|
53
|
+
node: FileTree;
|
|
54
|
+
parentPath: string;
|
|
55
|
+
currentFile: string;
|
|
56
|
+
setCurrentFile: (path: string) => void;
|
|
57
|
+
FileTreeNodeWrapper?: FileTreeNodeWrapper;
|
|
58
|
+
};
|
|
47
59
|
|
|
48
60
|
const addPath = (parentPath: string, path: string) => {
|
|
49
61
|
if ("" === parentPath) {
|
|
@@ -52,16 +64,31 @@ const addPath = (parentPath: string, path: string) => {
|
|
|
52
64
|
return parentPath + "/" + path;
|
|
53
65
|
};
|
|
54
66
|
|
|
55
|
-
const TreeNode: FC<NodeProps> = ({ node, parentPath, currentFile, setCurrentFile }) => {
|
|
67
|
+
const TreeNode: FC<NodeProps> = ({ node, parentPath, currentFile, setCurrentFile, FileTreeNodeWrapper }) => {
|
|
56
68
|
const [t] = useTranslation("repos");
|
|
57
69
|
|
|
70
|
+
FileTreeNodeWrapper = FileTreeNodeWrapper || (({ children }) => <>{children}</>);
|
|
71
|
+
|
|
72
|
+
const label = <div className="ml-1">{node.nodeName}</div>;
|
|
73
|
+
const icon = <StyledIcon alt={t("diff.showContent")}>folder</StyledIcon>;
|
|
58
74
|
return (
|
|
59
75
|
<li>
|
|
60
76
|
{Object.keys(node.children).length > 0 ? (
|
|
61
77
|
<ul className="py-1 pl-3">
|
|
62
78
|
<li className="is-flex has-text-grey">
|
|
63
|
-
<
|
|
64
|
-
|
|
79
|
+
<FileTreeNodeWrapper
|
|
80
|
+
path={addPath(parentPath, node.nodeName)}
|
|
81
|
+
isFile={false}
|
|
82
|
+
isCurrentFile={false}
|
|
83
|
+
name={node.nodeName}
|
|
84
|
+
iconName={"folder"}
|
|
85
|
+
iconColor={"grey"}
|
|
86
|
+
originalIcon={icon}
|
|
87
|
+
originalLabel={label}
|
|
88
|
+
>
|
|
89
|
+
{icon}
|
|
90
|
+
{label}
|
|
91
|
+
</FileTreeNodeWrapper>
|
|
65
92
|
</li>
|
|
66
93
|
{Object.keys(node.children).map((key) => (
|
|
67
94
|
<TreeNode
|
|
@@ -70,23 +97,25 @@ const TreeNode: FC<NodeProps> = ({ node, parentPath, currentFile, setCurrentFile
|
|
|
70
97
|
parentPath={addPath(parentPath, node.nodeName)}
|
|
71
98
|
currentFile={currentFile}
|
|
72
99
|
setCurrentFile={setCurrentFile}
|
|
100
|
+
FileTreeNodeWrapper={FileTreeNodeWrapper}
|
|
73
101
|
/>
|
|
74
102
|
))}
|
|
75
103
|
</ul>
|
|
76
104
|
) : (
|
|
77
105
|
<TreeFile
|
|
78
|
-
changeType={node.changeType.toLowerCase() as
|
|
106
|
+
changeType={node.changeType.toLowerCase() as FileChangeType}
|
|
79
107
|
path={node.nodeName}
|
|
80
108
|
parentPath={parentPath}
|
|
81
109
|
currentFile={currentFile}
|
|
82
110
|
setCurrentFile={setCurrentFile}
|
|
111
|
+
FileTreeNodeWrapper={FileTreeNodeWrapper}
|
|
83
112
|
/>
|
|
84
113
|
)}
|
|
85
114
|
</li>
|
|
86
115
|
);
|
|
87
116
|
};
|
|
88
117
|
|
|
89
|
-
const getColor = (changeType:
|
|
118
|
+
const getColor = (changeType: FileChangeType) => {
|
|
90
119
|
switch (changeType) {
|
|
91
120
|
case "add":
|
|
92
121
|
return "success";
|
|
@@ -99,7 +128,7 @@ const getColor = (changeType: ChangeType) => {
|
|
|
99
128
|
}
|
|
100
129
|
};
|
|
101
130
|
|
|
102
|
-
const getIcon = (changeType:
|
|
131
|
+
const getIcon = (changeType: FileChangeType) => {
|
|
103
132
|
switch (changeType) {
|
|
104
133
|
case "add":
|
|
105
134
|
case "copy":
|
|
@@ -113,14 +142,22 @@ const getIcon = (changeType: ChangeType) => {
|
|
|
113
142
|
};
|
|
114
143
|
|
|
115
144
|
type FileProps = {
|
|
116
|
-
changeType:
|
|
145
|
+
changeType: FileChangeType;
|
|
117
146
|
path: string;
|
|
118
147
|
parentPath: string;
|
|
119
148
|
currentFile: string;
|
|
120
149
|
setCurrentFile: (path: string) => void;
|
|
150
|
+
FileTreeNodeWrapper: FileTreeNodeWrapper;
|
|
121
151
|
};
|
|
122
152
|
|
|
123
|
-
const TreeFile: FC<FileProps> = ({
|
|
153
|
+
const TreeFile: FC<FileProps> = ({
|
|
154
|
+
changeType,
|
|
155
|
+
path,
|
|
156
|
+
parentPath,
|
|
157
|
+
currentFile,
|
|
158
|
+
setCurrentFile,
|
|
159
|
+
FileTreeNodeWrapper,
|
|
160
|
+
}) => {
|
|
124
161
|
const [t] = useTranslation("repos");
|
|
125
162
|
const completePath = addPath(parentPath, path);
|
|
126
163
|
|
|
@@ -128,35 +165,50 @@ const TreeFile: FC<FileProps> = ({ changeType, path, parentPath, currentFile, se
|
|
|
128
165
|
return currentFile === completePath;
|
|
129
166
|
};
|
|
130
167
|
|
|
168
|
+
const iconName = getIcon(changeType);
|
|
169
|
+
|
|
170
|
+
const icon = (
|
|
171
|
+
<StackedSpan className="fa-stack">
|
|
172
|
+
<StyledIcon
|
|
173
|
+
className={classNames("fa-stack-2x", `has-text-${getColor(changeType)}`)}
|
|
174
|
+
key={completePath + "file"}
|
|
175
|
+
type="fas"
|
|
176
|
+
alt={t("diff.showContent")}
|
|
177
|
+
>
|
|
178
|
+
file
|
|
179
|
+
</StyledIcon>
|
|
180
|
+
<StyledIcon
|
|
181
|
+
className={classNames("fa-stack-1x", "is-relative", "has-text-secondary-least")}
|
|
182
|
+
issmaller={iconName === "circle" ? "true" : "false"}
|
|
183
|
+
key={changeType}
|
|
184
|
+
alt={t(`diff.changes.${changeType}`)}
|
|
185
|
+
>
|
|
186
|
+
{iconName}
|
|
187
|
+
</StyledIcon>
|
|
188
|
+
</StackedSpan>
|
|
189
|
+
);
|
|
190
|
+
const label = <div className={classNames("ml-1", isCurrentFile() ? "has-text-weight-bold" : "")}>{path}</div>;
|
|
191
|
+
|
|
131
192
|
return (
|
|
132
193
|
<Link
|
|
133
194
|
className="is-flex py-1 pl-3 has-cursor-pointer"
|
|
134
195
|
onClick={() => setCurrentFile(completePath)}
|
|
135
196
|
to={`#diff-${encodeURIComponent(completePath)}`}
|
|
136
197
|
>
|
|
137
|
-
<
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
)}
|
|
152
|
-
issmaller={getIcon(changeType) === "circle" ? "true" : "false"}
|
|
153
|
-
key={changeType}
|
|
154
|
-
alt={t(`diff.changes.${changeType}`)}
|
|
155
|
-
>
|
|
156
|
-
{getIcon(changeType)}
|
|
157
|
-
</StyledIcon>
|
|
158
|
-
</StackedSpan>
|
|
159
|
-
<div className="ml-1">{path}</div>
|
|
198
|
+
<FileTreeNodeWrapper
|
|
199
|
+
name={path}
|
|
200
|
+
path={completePath}
|
|
201
|
+
changeType={changeType}
|
|
202
|
+
isFile={true}
|
|
203
|
+
iconName={iconName}
|
|
204
|
+
iconColor={getColor(changeType)}
|
|
205
|
+
originalIcon={icon}
|
|
206
|
+
originalLabel={label}
|
|
207
|
+
isCurrentFile={isCurrentFile()}
|
|
208
|
+
>
|
|
209
|
+
{icon}
|
|
210
|
+
{label}
|
|
211
|
+
</FileTreeNodeWrapper>
|
|
160
212
|
</Link>
|
|
161
213
|
);
|
|
162
214
|
};
|
|
@@ -77,7 +77,7 @@ const DiffFile: FC<Props> = ({
|
|
|
77
77
|
() =>
|
|
78
78
|
hasContent ||
|
|
79
79
|
(["add", "modify", "delete"].includes(file.type) && canDisplayBinaryFile(oldContentType, newContentType)),
|
|
80
|
-
[file, hasContent, newContentType, oldContentType]
|
|
80
|
+
[file, hasContent, newContentType, oldContentType],
|
|
81
81
|
);
|
|
82
82
|
const canRenderSideBySide = useMemo(() => hasContent && file.type === "modify", [file, hasContent]);
|
|
83
83
|
|
|
@@ -126,7 +126,7 @@ const DiffFile: FC<Props> = ({
|
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
},
|
|
129
|
-
[collapsed, file, canRenderContent, isCollapsedProp, onCollapseStateChange, stickyHeader]
|
|
129
|
+
[collapsed, file, canRenderContent, isCollapsedProp, onCollapseStateChange, stickyHeader],
|
|
130
130
|
);
|
|
131
131
|
|
|
132
132
|
const toggleSideBySide = useCallback((callback: () => void) => {
|
|
@@ -139,7 +139,7 @@ const DiffFile: FC<Props> = ({
|
|
|
139
139
|
setWhitespace(!whitespace);
|
|
140
140
|
callback();
|
|
141
141
|
},
|
|
142
|
-
[whitespace]
|
|
142
|
+
[whitespace],
|
|
143
143
|
);
|
|
144
144
|
|
|
145
145
|
const sideBySideToggle = useMemo(
|
|
@@ -161,7 +161,7 @@ const DiffFile: FC<Props> = ({
|
|
|
161
161
|
)}
|
|
162
162
|
</MenuContext.Consumer>
|
|
163
163
|
),
|
|
164
|
-
[canRenderSideBySide, sideBySide, t, toggleSideBySide]
|
|
164
|
+
[canRenderSideBySide, sideBySide, t, toggleSideBySide],
|
|
165
165
|
);
|
|
166
166
|
|
|
167
167
|
const whitespaceToggle = useMemo(
|
|
@@ -183,7 +183,7 @@ const DiffFile: FC<Props> = ({
|
|
|
183
183
|
)}
|
|
184
184
|
</MenuContext.Consumer>
|
|
185
185
|
),
|
|
186
|
-
[hasContent, t, toggleWhiteSpace, whitespace]
|
|
186
|
+
[hasContent, t, toggleWhiteSpace, whitespace],
|
|
187
187
|
);
|
|
188
188
|
|
|
189
189
|
const errorModal = useMemo(
|
|
@@ -196,7 +196,7 @@ const DiffFile: FC<Props> = ({
|
|
|
196
196
|
active={true}
|
|
197
197
|
/>
|
|
198
198
|
) : null,
|
|
199
|
-
[expansionError, t]
|
|
199
|
+
[expansionError, t],
|
|
200
200
|
);
|
|
201
201
|
|
|
202
202
|
const innerContent = useMemo(
|
|
@@ -253,7 +253,7 @@ const DiffFile: FC<Props> = ({
|
|
|
253
253
|
sideBySide,
|
|
254
254
|
whitespace,
|
|
255
255
|
viewType,
|
|
256
|
-
]
|
|
256
|
+
],
|
|
257
257
|
);
|
|
258
258
|
|
|
259
259
|
const body = useMemo(() => (!isCollapsed ? innerContent : null), [innerContent, isCollapsed]);
|
|
@@ -266,7 +266,7 @@ const DiffFile: FC<Props> = ({
|
|
|
266
266
|
modalBody={<MarginlessModalContent>{innerContent}</MarginlessModalContent>}
|
|
267
267
|
/>
|
|
268
268
|
) : null,
|
|
269
|
-
[canRenderContent, file, innerContent]
|
|
269
|
+
[canRenderContent, file, innerContent],
|
|
270
270
|
);
|
|
271
271
|
|
|
272
272
|
const collapseIcon = useMemo(
|
|
@@ -276,7 +276,7 @@ const DiffFile: FC<Props> = ({
|
|
|
276
276
|
) : (
|
|
277
277
|
<Icon alt={t("diff.hideContent")}>angle-down</Icon>
|
|
278
278
|
),
|
|
279
|
-
[isCollapsed, t]
|
|
279
|
+
[isCollapsed, t],
|
|
280
280
|
);
|
|
281
281
|
|
|
282
282
|
return (
|