mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-13 00:45:44 +01:00
Add detailed search result ui (#1738)
Add a dedicated search page with more results and different types. Users and groups are now indexed along with repositories. Co-authored-by: René Pfeuffer <rene.pfeuffer@cloudogu.com>
This commit is contained in:
@@ -21,14 +21,13 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
import * as React from "react";
|
||||
import React, { FC } from "react";
|
||||
import classNames from "classnames";
|
||||
import { Link } from "react-router-dom";
|
||||
import { RoutingProps } from "./RoutingProps";
|
||||
import { FC } from "react";
|
||||
import useMenuContext from "./MenuContext";
|
||||
import useActiveMatch from "./useActiveMatch";
|
||||
import {createAttributesForTesting} from "../devBuild";
|
||||
import { createAttributesForTesting } from "../devBuild";
|
||||
|
||||
type Props = RoutingProps & {
|
||||
label: string;
|
||||
@@ -37,21 +36,29 @@ type Props = RoutingProps & {
|
||||
testId?: string;
|
||||
};
|
||||
|
||||
const NavLink: FC<Props> = ({ to, activeWhenMatch, activeOnlyWhenExact, icon, label, title, testId }) => {
|
||||
type NavLinkContentProp = {
|
||||
label: string;
|
||||
icon?: string;
|
||||
collapsed: boolean;
|
||||
};
|
||||
|
||||
const NavLinkContent: FC<NavLinkContentProp> = ({ label, icon, collapsed }) => (
|
||||
<>
|
||||
{icon ? (
|
||||
<>
|
||||
<i className={classNames(icon, "fa-fw")} />{" "}
|
||||
</>
|
||||
) : null}
|
||||
{collapsed ? null : label}
|
||||
</>
|
||||
);
|
||||
|
||||
const NavLink: FC<Props> = ({ to, activeWhenMatch, activeOnlyWhenExact, title, testId, children, ...contentProps }) => {
|
||||
const active = useActiveMatch({ to, activeWhenMatch, activeOnlyWhenExact });
|
||||
|
||||
const context = useMenuContext();
|
||||
const collapsed = context.isCollapsed();
|
||||
|
||||
let showIcon = null;
|
||||
if (icon) {
|
||||
showIcon = (
|
||||
<>
|
||||
<i className={classNames(icon, "fa-fw")} />{" "}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<li title={collapsed ? title : undefined}>
|
||||
<Link
|
||||
@@ -59,15 +66,14 @@ const NavLink: FC<Props> = ({ to, activeWhenMatch, activeOnlyWhenExact, icon, la
|
||||
to={to}
|
||||
{...createAttributesForTesting(testId)}
|
||||
>
|
||||
{showIcon}
|
||||
{collapsed ? null : label}
|
||||
{children ? children : <NavLinkContent {...contentProps} collapsed={collapsed} />}
|
||||
</Link>
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
||||
NavLink.defaultProps = {
|
||||
activeOnlyWhenExact: true
|
||||
activeOnlyWhenExact: true,
|
||||
};
|
||||
|
||||
export default NavLink;
|
||||
|
||||
@@ -25,9 +25,11 @@
|
||||
import React, { FC } from "react";
|
||||
import styled from "styled-components";
|
||||
import useMenuContext from "./MenuContext";
|
||||
import classNames from "classnames";
|
||||
|
||||
type Props = {
|
||||
label: string;
|
||||
collapsible?: boolean;
|
||||
};
|
||||
|
||||
type CollapsedProps = {
|
||||
@@ -38,6 +40,7 @@ const SectionContainer = styled.aside`
|
||||
position: sticky;
|
||||
position: -webkit-sticky; /* Safari */
|
||||
top: 2rem;
|
||||
width: 100%;
|
||||
|
||||
@media (max-height: 900px) {
|
||||
position: relative;
|
||||
@@ -55,19 +58,20 @@ const Icon = styled.i<CollapsedProps>`
|
||||
|
||||
const MenuLabel = styled.p<CollapsedProps>`
|
||||
justify-content: ${(props: CollapsedProps) => (props.collapsed ? "center" : "inherit")};
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
const SecondaryNavigation: FC<Props> = ({ label, children }) => {
|
||||
const SecondaryNavigation: FC<Props> = ({ label, children, collapsible = true }) => {
|
||||
const menuContext = useMenuContext();
|
||||
const isCollapsed = menuContext.isCollapsed();
|
||||
const isCollapsed = collapsible && menuContext.isCollapsed();
|
||||
|
||||
const toggleCollapseState = () => {
|
||||
menuContext.setCollapsed(!isCollapsed);
|
||||
if (collapsible) {
|
||||
menuContext.setCollapsed(!isCollapsed);
|
||||
}
|
||||
};
|
||||
|
||||
const uncollapseMenu = () => {
|
||||
if (isCollapsed) {
|
||||
if (collapsible && isCollapsed) {
|
||||
menuContext.setCollapsed(false);
|
||||
}
|
||||
};
|
||||
@@ -77,10 +81,16 @@ const SecondaryNavigation: FC<Props> = ({ label, children }) => {
|
||||
return (
|
||||
<SectionContainer className="menu">
|
||||
<div>
|
||||
<MenuLabel className="menu-label" collapsed={isCollapsed} onClick={toggleCollapseState}>
|
||||
<Icon color="info" className="is-medium" collapsed={isCollapsed}>
|
||||
{arrowIcon}
|
||||
</Icon>
|
||||
<MenuLabel
|
||||
className={classNames("menu-label", { "has-cursor-pointer": collapsible })}
|
||||
collapsed={isCollapsed}
|
||||
onClick={toggleCollapseState}
|
||||
>
|
||||
{collapsible ? (
|
||||
<Icon color="info" className="is-medium" collapsed={isCollapsed}>
|
||||
{arrowIcon}
|
||||
</Icon>
|
||||
) : null}
|
||||
{isCollapsed ? "" : label}
|
||||
</MenuLabel>
|
||||
<ul className="menu-list" onClick={uncollapseMenu}>
|
||||
|
||||
@@ -26,9 +26,15 @@ import { useLocation, useRouteMatch } from "react-router-dom";
|
||||
import { RoutingProps } from "./RoutingProps";
|
||||
|
||||
const useActiveMatch = ({ to, activeOnlyWhenExact, activeWhenMatch }: RoutingProps) => {
|
||||
let path = to;
|
||||
const index = to.indexOf("?");
|
||||
if (index > 0) {
|
||||
path = to.substr(0, index);
|
||||
}
|
||||
|
||||
const match = useRouteMatch({
|
||||
path: to,
|
||||
exact: activeOnlyWhenExact
|
||||
path,
|
||||
exact: activeOnlyWhenExact,
|
||||
});
|
||||
|
||||
const location = useLocation();
|
||||
@@ -36,7 +42,7 @@ const useActiveMatch = ({ to, activeOnlyWhenExact, activeWhenMatch }: RoutingPro
|
||||
const isActiveWhenMatch = () => {
|
||||
if (activeWhenMatch) {
|
||||
return activeWhenMatch({
|
||||
location
|
||||
location,
|
||||
});
|
||||
}
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user