@scm-manager/ui-components 4.0.0-REACT19-20250913-143258 → 4.0.0-REACT19-20250915-143710

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scm-manager/ui-components",
3
- "version": "4.0.0-REACT19-20250913-143258",
3
+ "version": "4.0.0-REACT19-20250915-143710",
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-20250913-143258",
41
- "@scm-manager/ui-syntaxhighlighting": "4.0.0-REACT19-20250913-143258",
42
- "@scm-manager/ui-text": "4.0.0-REACT19-20250913-143258",
43
- "@scm-manager/ui-types": "4.0.0-REACT19-20250913-143258",
40
+ "@scm-manager/ui-shortcuts": "4.0.0-REACT19-20250915-143710",
41
+ "@scm-manager/ui-syntaxhighlighting": "4.0.0-REACT19-20250915-143710",
42
+ "@scm-manager/ui-text": "4.0.0-REACT19-20250915-143710",
43
+ "@scm-manager/ui-types": "4.0.0-REACT19-20250915-143710",
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-20250913-143258",
71
- "@scm-manager/ui-buttons": "4.0.0-REACT19-20250913-143258",
72
- "@scm-manager/ui-core": "4.0.0-REACT19-20250913-143258",
73
- "@scm-manager/ui-extensions": "4.0.0-REACT19-20250913-143258",
74
- "@scm-manager/ui-layout": "4.0.0-REACT19-20250913-143258",
75
- "@scm-manager/ui-overlays": "4.0.0-REACT19-20250913-143258",
70
+ "@scm-manager/ui-api": "4.0.0-REACT19-20250915-143710",
71
+ "@scm-manager/ui-buttons": "4.0.0-REACT19-20250915-143710",
72
+ "@scm-manager/ui-core": "4.0.0-REACT19-20250915-143710",
73
+ "@scm-manager/ui-extensions": "4.0.0-REACT19-20250915-143710",
74
+ "@scm-manager/ui-layout": "4.0.0-REACT19-20250915-143710",
75
+ "@scm-manager/ui-overlays": "4.0.0-REACT19-20250915-143710",
76
76
  "deepmerge": "^4.2.2",
77
77
  "hast-util-sanitize": "^3.0.2",
78
- "react-diff-view": "^2.4.10",
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",
@@ -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, Namespace, Repository } from "@scm-manager/ui-types";
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, useNamespaceFromParams } from "@scm-manager/ui-core";
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 & { url: string };
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, url }: RepositoryNavProps) => {
123
- return this.navLink(url + to, labelI18nKey, t);
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: any,
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, url }: RepositoryNavProps) => {
158
- return this.navLink(url + "/settings" + to, labelI18nKey, t, { activeOnlyWhenExact: false });
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 = ({ repository, ...additionalProps }: RepositoryRouteProps) => {
166
- const link = repository._links[linkName];
167
-
168
- if (link) {
169
- return this.route(
170
- "settings" + to,
171
- <TitledRepositorySettingComponent
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, repoPredicate);
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<{ link: string; namespace: Namespace }>,
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
- const namespace = useNamespaceFromParams();
204
- if (namespace) {
205
- const link = namespace._links[linkName];
206
-
207
- if (link) {
208
- return this.route(
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 { useDocumentTitle, useDocumentTitleForRepository, useNamespaceFromParams } from "@scm-manager/ui-core";
20
- import { Repository } from "@scm-manager/ui-types";
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
- children,
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"));
@@ -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-ignore react-diff-view does not provide types
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
  }
@@ -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 (