mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-10 23:45:44 +01:00
fixed active state of sub navigation items, which are using activeWhenMatch
This commit is contained in:
committed by
René Pfeuffer
parent
91aca71591
commit
dcac6b3f22
@@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Close file lists in migration ([#1191](https://github.com/scm-manager/scm-manager/pull/1191))
|
- Close file lists in migration ([#1191](https://github.com/scm-manager/scm-manager/pull/1191))
|
||||||
- Use command in javahg.py from registrar (Upgrade to newer javahg version) ([#1192](https://github.com/scm-manager/scm-manager/pull/1192))
|
- Use command in javahg.py from registrar (Upgrade to newer javahg version) ([#1192](https://github.com/scm-manager/scm-manager/pull/1192))
|
||||||
- Fixed wrong e-tag format ([sdorra/web-resource #1](https://github.com/sdorra/web-resources/pull/1))
|
- Fixed wrong e-tag format ([sdorra/web-resource #1](https://github.com/sdorra/web-resources/pull/1))
|
||||||
|
- Fixed active state of sub navigation items, which are using activeWhenMatch
|
||||||
- Handles repositories in custom directories correctly in migration from 1.x ([#1201](https://github.com/scm-manager/scm-manager/pull/1201))
|
- Handles repositories in custom directories correctly in migration from 1.x ([#1201](https://github.com/scm-manager/scm-manager/pull/1201))
|
||||||
- Usage of short git commit ids in changeset urls ([#1200](https://github.com/scm-manager/scm-manager/pull/1200))
|
- Usage of short git commit ids in changeset urls ([#1200](https://github.com/scm-manager/scm-manager/pull/1200))
|
||||||
|
|
||||||
|
|||||||
@@ -40777,6 +40777,68 @@ exports[`Storyshots Modal|Modal Default 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`Storyshots Navigation|Secondary Active when match 1`] = `
|
||||||
|
<div
|
||||||
|
className="SecondaryNavigationstories__Columns-fdxo4w-0 iEPunq columns"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="column is-3"
|
||||||
|
>
|
||||||
|
<aside
|
||||||
|
className="SecondaryNavigation__SectionContainer-sc-8p1rgi-0 cpLxdt menu"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<p
|
||||||
|
className="SecondaryNavigation__MenuLabel-sc-8p1rgi-2 bzrEDi menu-label"
|
||||||
|
onClick={[Function]}
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
className="SecondaryNavigation__Icon-sc-8p1rgi-1 djTcfn is-medium"
|
||||||
|
color="info"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
className="fas fa-caret-right"
|
||||||
|
/>
|
||||||
|
</i>
|
||||||
|
Hitchhiker
|
||||||
|
</p>
|
||||||
|
<ul
|
||||||
|
className="menu-list"
|
||||||
|
onClick={[Function]}
|
||||||
|
>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
className=""
|
||||||
|
href="/42"
|
||||||
|
onClick={[Function]}
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
className="fas fa-puzzle-piece fa-fw"
|
||||||
|
/>
|
||||||
|
|
||||||
|
Puzzle 42
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
className="is-active"
|
||||||
|
href="/heart-of-gold"
|
||||||
|
onClick={[Function]}
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
className="fas fa-star fa-fw"
|
||||||
|
/>
|
||||||
|
|
||||||
|
Heart Of Gold
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`Storyshots Navigation|Secondary Default 1`] = `
|
exports[`Storyshots Navigation|Secondary Default 1`] = `
|
||||||
<div
|
<div
|
||||||
className="SecondaryNavigationstories__Columns-fdxo4w-0 iEPunq columns"
|
className="SecondaryNavigationstories__Columns-fdxo4w-0 iEPunq columns"
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import { Link, useRouteMatch } from "react-router-dom";
|
|||||||
import { RoutingProps } from "./RoutingProps";
|
import { RoutingProps } from "./RoutingProps";
|
||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import useMenuContext from "./MenuContext";
|
import useMenuContext from "./MenuContext";
|
||||||
|
import useActiveMatch from "./useActiveMatch";
|
||||||
|
|
||||||
type Props = RoutingProps & {
|
type Props = RoutingProps & {
|
||||||
label: string;
|
label: string;
|
||||||
@@ -34,11 +35,8 @@ type Props = RoutingProps & {
|
|||||||
icon?: string;
|
icon?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const NavLink: FC<Props> = ({ to, activeOnlyWhenExact, icon, label, title }) => {
|
const NavLink: FC<Props> = ({ to, activeWhenMatch, activeOnlyWhenExact, icon, label, title }) => {
|
||||||
const match = useRouteMatch({
|
const active = useActiveMatch({to, activeWhenMatch, activeOnlyWhenExact});
|
||||||
path: to,
|
|
||||||
exact: activeOnlyWhenExact
|
|
||||||
});
|
|
||||||
|
|
||||||
const context = useMenuContext();
|
const context = useMenuContext();
|
||||||
const collapsed = context.isCollapsed();
|
const collapsed = context.isCollapsed();
|
||||||
@@ -54,7 +52,7 @@ const NavLink: FC<Props> = ({ to, activeOnlyWhenExact, icon, label, title }) =>
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<li title={collapsed ? title : undefined}>
|
<li title={collapsed ? title : undefined}>
|
||||||
<Link className={classNames(!!match ? "is-active" : "", collapsed ? "has-text-centered" : "")} to={to}>
|
<Link className={classNames(active ? "is-active" : "", collapsed ? "has-text-centered" : "")} to={to}>
|
||||||
{showIcon}
|
{showIcon}
|
||||||
{collapsed ? null : label}
|
{collapsed ? null : label}
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -86,4 +86,18 @@ storiesOf("Navigation|Secondary", module)
|
|||||||
</SecondaryNavigation>
|
</SecondaryNavigation>
|
||||||
</BinderContext.Provider>
|
</BinderContext.Provider>
|
||||||
);
|
);
|
||||||
});
|
})
|
||||||
|
.add("Active when match", () =>
|
||||||
|
withRoute("/hog")(
|
||||||
|
<SecondaryNavigation label="Hitchhiker">
|
||||||
|
<SecondaryNavigationItem to="/42" icon="fas fa-puzzle-piece" label="Puzzle 42" title="Puzzle 42" />
|
||||||
|
<SecondaryNavigationItem
|
||||||
|
activeWhenMatch={route => route.location.pathname === "/hog"}
|
||||||
|
to="/heart-of-gold"
|
||||||
|
icon="fas fa-star"
|
||||||
|
label="Heart Of Gold"
|
||||||
|
title="Heart Of Gold"
|
||||||
|
/>
|
||||||
|
</SecondaryNavigation>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|||||||
@@ -22,10 +22,11 @@
|
|||||||
* SOFTWARE.
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
import React, { FC, useContext } from "react";
|
import React, { FC, useContext } from "react";
|
||||||
import { Link, useRouteMatch } from "react-router-dom";
|
import { Link, useRouteMatch, useLocation } from "react-router-dom";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import useMenuContext, { MenuContext } from "./MenuContext";
|
import useMenuContext, { MenuContext } from "./MenuContext";
|
||||||
import { RoutingProps } from "./RoutingProps";
|
import { RoutingProps } from "./RoutingProps";
|
||||||
|
import useActiveMatch from "./useActiveMatch";
|
||||||
|
|
||||||
type Props = RoutingProps & {
|
type Props = RoutingProps & {
|
||||||
label: string;
|
label: string;
|
||||||
@@ -33,18 +34,19 @@ type Props = RoutingProps & {
|
|||||||
icon?: string;
|
icon?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SubNavigation: FC<Props> = ({ to, activeOnlyWhenExact, icon, title, label, children }) => {
|
const SubNavigation: FC<Props> = ({ to, activeOnlyWhenExact, activeWhenMatch, icon, title, label, children }) => {
|
||||||
|
const context = useMenuContext();
|
||||||
|
const collapsed = context.isCollapsed();
|
||||||
|
|
||||||
const parents = to.split("/");
|
const parents = to.split("/");
|
||||||
parents.splice(-1, 1);
|
parents.splice(-1, 1);
|
||||||
const parent = parents.join("/");
|
const parent = parents.join("/");
|
||||||
|
|
||||||
const match = useRouteMatch({
|
const active = useActiveMatch({
|
||||||
path: parent,
|
to: parent,
|
||||||
exact: activeOnlyWhenExact
|
activeOnlyWhenExact,
|
||||||
});
|
activeWhenMatch
|
||||||
|
})
|
||||||
const context = useMenuContext();
|
|
||||||
const collapsed = context.isCollapsed();
|
|
||||||
|
|
||||||
let defaultIcon = "fas fa-cog";
|
let defaultIcon = "fas fa-cog";
|
||||||
if (icon) {
|
if (icon) {
|
||||||
@@ -52,13 +54,13 @@ const SubNavigation: FC<Props> = ({ to, activeOnlyWhenExact, icon, title, label,
|
|||||||
}
|
}
|
||||||
|
|
||||||
let childrenList = null;
|
let childrenList = null;
|
||||||
if (match && !collapsed) {
|
if (active && !collapsed) {
|
||||||
childrenList = <ul className="sub-menu">{children}</ul>;
|
childrenList = <ul className="sub-menu">{children}</ul>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li title={collapsed ? title : undefined}>
|
<li title={collapsed ? title : undefined}>
|
||||||
<Link className={classNames(match != null ? "is-active" : "", collapsed ? "has-text-centered" : "")} to={to}>
|
<Link className={classNames(active ? "is-active" : "", collapsed ? "has-text-centered" : "")} to={to}>
|
||||||
<i className={classNames(defaultIcon, "fa-fw")} /> {collapsed ? "" : label}
|
<i className={classNames(defaultIcon, "fa-fw")} /> {collapsed ? "" : label}
|
||||||
</Link>
|
</Link>
|
||||||
{childrenList}
|
{childrenList}
|
||||||
|
|||||||
48
scm-ui/ui-components/src/navigation/useActiveMatch.ts
Normal file
48
scm-ui/ui-components/src/navigation/useActiveMatch.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {useLocation, useRouteMatch} from "react-router-dom";
|
||||||
|
import {RoutingProps} from "./RoutingProps";
|
||||||
|
|
||||||
|
const useActiveMatch = ({to, activeOnlyWhenExact, activeWhenMatch}: RoutingProps) => {
|
||||||
|
const match = useRouteMatch({
|
||||||
|
path: to,
|
||||||
|
exact: activeOnlyWhenExact
|
||||||
|
});
|
||||||
|
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
|
const isActiveWhenMatch = () => {
|
||||||
|
if (activeWhenMatch) {
|
||||||
|
return activeWhenMatch({
|
||||||
|
location
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
return !!match || isActiveWhenMatch();
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useActiveMatch;
|
||||||
Reference in New Issue
Block a user