mirror of
				https://github.com/scm-manager/scm-manager.git
				synced 2025-11-03 20:15:52 +01:00 
			
		
		
		
	Small header (#1721)
Co-authored-by: René Pfeuffer <rene.pfeuffer@cloudogu.com>
This commit is contained in:
		@@ -32,8 +32,14 @@ const Wrapper = styled.div`
 | 
			
		||||
  height: 100%;
 | 
			
		||||
`;
 | 
			
		||||
 | 
			
		||||
storiesOf("Logo", module).add("Default", () => (
 | 
			
		||||
  <Wrapper>
 | 
			
		||||
    <Logo />
 | 
			
		||||
  </Wrapper>
 | 
			
		||||
));
 | 
			
		||||
storiesOf("Logo", module)
 | 
			
		||||
  .add("Default", () => (
 | 
			
		||||
    <Wrapper>
 | 
			
		||||
      <Logo />
 | 
			
		||||
    </Wrapper>
 | 
			
		||||
  ))
 | 
			
		||||
  .add("WithoutText", () => (
 | 
			
		||||
    <Wrapper>
 | 
			
		||||
      <Logo withText={false} />
 | 
			
		||||
    </Wrapper>
 | 
			
		||||
  ));
 | 
			
		||||
 
 | 
			
		||||
@@ -21,15 +21,22 @@
 | 
			
		||||
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
			
		||||
 * SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
import React from "react";
 | 
			
		||||
import { WithTranslation, withTranslation } from "react-i18next";
 | 
			
		||||
import React, { FC } from "react";
 | 
			
		||||
import { useTranslation } from "react-i18next";
 | 
			
		||||
import Image from "./Image";
 | 
			
		||||
 | 
			
		||||
class Logo extends React.Component<WithTranslation> {
 | 
			
		||||
  render() {
 | 
			
		||||
    const { t } = this.props;
 | 
			
		||||
    return <Image src="/images/logo.png" alt={t("logo.alt")} />;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
type Props = {
 | 
			
		||||
  withText?: boolean;
 | 
			
		||||
  className?: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default withTranslation("commons")(Logo);
 | 
			
		||||
const Logo: FC<Props> = ({ withText = true, className }) => {
 | 
			
		||||
  const [t] = useTranslation("commons");
 | 
			
		||||
 | 
			
		||||
  if (withText) {
 | 
			
		||||
    return <Image src="/images/logo.png" alt={t("logo.alt")} className={className} />;
 | 
			
		||||
  }
 | 
			
		||||
  return <Image src="/images/scmLogo.svg" alt={t("logo.alt")} className={className} />;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default Logo;
 | 
			
		||||
 
 | 
			
		||||
@@ -53430,6 +53430,17 @@ exports[`Storyshots Logo Default 1`] = `
 | 
			
		||||
</div>
 | 
			
		||||
`;
 | 
			
		||||
 | 
			
		||||
exports[`Storyshots Logo WithoutText 1`] = `
 | 
			
		||||
<div
 | 
			
		||||
  className="Logostories__Wrapper-sc-14nnt4j-0 brMuIC"
 | 
			
		||||
>
 | 
			
		||||
  <img
 | 
			
		||||
    alt="logo.alt"
 | 
			
		||||
    src="/images/scmLogo.svg"
 | 
			
		||||
  />
 | 
			
		||||
</div>
 | 
			
		||||
`;
 | 
			
		||||
 | 
			
		||||
exports[`Storyshots MarkdownView Code without Lang 1`] = `
 | 
			
		||||
<div
 | 
			
		||||
  className="MarkdownViewstories__Spacing-sc-1lofakk-0 iOWSJJ"
 | 
			
		||||
 
 | 
			
		||||
@@ -30,20 +30,20 @@ export type Device = {
 | 
			
		||||
 | 
			
		||||
export const devices = {
 | 
			
		||||
  mobile: {
 | 
			
		||||
    width: 768
 | 
			
		||||
    width: 768,
 | 
			
		||||
  },
 | 
			
		||||
  tablet: {
 | 
			
		||||
    width: 769
 | 
			
		||||
    width: 769,
 | 
			
		||||
  },
 | 
			
		||||
  desktop: {
 | 
			
		||||
    width: 1024
 | 
			
		||||
    width: 1024,
 | 
			
		||||
  },
 | 
			
		||||
  widescreen: {
 | 
			
		||||
    width: 1216
 | 
			
		||||
    width: 1216,
 | 
			
		||||
  },
 | 
			
		||||
  fullhd: {
 | 
			
		||||
    width: 1408
 | 
			
		||||
  }
 | 
			
		||||
    width: 1408,
 | 
			
		||||
  },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type DeviceType = keyof typeof devices;
 | 
			
		||||
 
 | 
			
		||||
@@ -21,33 +21,42 @@
 | 
			
		||||
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
			
		||||
 * SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
import React, { ReactNode } from "react";
 | 
			
		||||
import React, { FC, ReactNode } from "react";
 | 
			
		||||
import Logo from "./../Logo";
 | 
			
		||||
import { Links } from "@scm-manager/ui-types";
 | 
			
		||||
 | 
			
		||||
type Props = {
 | 
			
		||||
  children?: ReactNode;
 | 
			
		||||
  links: Links;
 | 
			
		||||
  authenticated: boolean;
 | 
			
		||||
  children: ReactNode;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class Header extends React.Component<Props> {
 | 
			
		||||
  render() {
 | 
			
		||||
    const { children } = this.props;
 | 
			
		||||
    return (
 | 
			
		||||
      <section className="hero is-dark is-small">
 | 
			
		||||
        <div className="hero-body">
 | 
			
		||||
          <div className="container">
 | 
			
		||||
            <div className="columns is-vcentered">
 | 
			
		||||
              <div className="column">
 | 
			
		||||
                <Logo />
 | 
			
		||||
              </div>
 | 
			
		||||
const SmallHeader: FC<{ children: ReactNode }> = ({ children }) => {
 | 
			
		||||
  return <div className="has-scm-background">{children}</div>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const LargeHeader: FC = () => {
 | 
			
		||||
  return (
 | 
			
		||||
    <section className="hero has-scm-background is-small">
 | 
			
		||||
      <div className="hero-body">
 | 
			
		||||
        <div className="container">
 | 
			
		||||
          <div className="columns is-vcentered">
 | 
			
		||||
            <div className="column">
 | 
			
		||||
              <Logo />
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div className="hero-foot">
 | 
			
		||||
          <div className="container">{children}</div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </section>
 | 
			
		||||
    );
 | 
			
		||||
      </div>
 | 
			
		||||
    </section>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const Header: FC<Props> = ({ authenticated, children, links }) => {
 | 
			
		||||
  if (authenticated) {
 | 
			
		||||
    return <SmallHeader>{children}</SmallHeader>;
 | 
			
		||||
  } else {
 | 
			
		||||
    return <LargeHeader />;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default Header;
 | 
			
		||||
 
 | 
			
		||||
@@ -21,86 +21,59 @@
 | 
			
		||||
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
			
		||||
 * SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
import React, { ReactNode } from "react";
 | 
			
		||||
import { WithTranslation, withTranslation } from "react-i18next";
 | 
			
		||||
import React, { FC, ReactNode } from "react";
 | 
			
		||||
import PrimaryNavigationLink from "./PrimaryNavigationLink";
 | 
			
		||||
import { Links } from "@scm-manager/ui-types";
 | 
			
		||||
import { binder, ExtensionPoint } from "@scm-manager/ui-extensions";
 | 
			
		||||
import { urls } from "@scm-manager/ui-api";
 | 
			
		||||
import { withRouter, RouteComponentProps } from "react-router-dom";
 | 
			
		||||
import { useLocation } from "react-router-dom";
 | 
			
		||||
import { useTranslation } from "react-i18next";
 | 
			
		||||
 | 
			
		||||
type Props = RouteComponentProps & WithTranslation & {
 | 
			
		||||
type Props = {
 | 
			
		||||
  links: Links;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
type Appender = (to: string, match: string, label: string, linkName: string) => void;
 | 
			
		||||
 | 
			
		||||
class PrimaryNavigation extends React.Component<Props> {
 | 
			
		||||
  createNavigationAppender = (navigationItems: ReactNode[]): Appender => {
 | 
			
		||||
    const { t, links } = this.props;
 | 
			
		||||
const PrimaryNavigation: FC<Props> = ({ links }) => {
 | 
			
		||||
  const [t] = useTranslation("commons");
 | 
			
		||||
  const location = useLocation();
 | 
			
		||||
 | 
			
		||||
  const createNavigationAppender = (navItems: ReactNode[]): Appender => {
 | 
			
		||||
    return (to: string, match: string, label: string, linkName: string) => {
 | 
			
		||||
      const link = links[linkName];
 | 
			
		||||
      if (link) {
 | 
			
		||||
        const navigationItem = <PrimaryNavigationLink testId={label.replace(".", "-")} to={to} match={match} label={t(label)} key={linkName} />;
 | 
			
		||||
        navigationItems.push(navigationItem);
 | 
			
		||||
        const navigationItem = (
 | 
			
		||||
          <PrimaryNavigationLink
 | 
			
		||||
            testId={label.replace(".", "-")}
 | 
			
		||||
            to={to}
 | 
			
		||||
            match={match}
 | 
			
		||||
            label={t(label)}
 | 
			
		||||
            key={linkName}
 | 
			
		||||
            className="navbar-item"
 | 
			
		||||
          />
 | 
			
		||||
        );
 | 
			
		||||
        navItems.push(navigationItem);
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  appendLogout = (navigationItems: ReactNode[], append: Appender) => {
 | 
			
		||||
    const { t, links } = this.props;
 | 
			
		||||
  const createNavigationItems = () => {
 | 
			
		||||
    const navItems: ReactNode[] = [];
 | 
			
		||||
 | 
			
		||||
    const props = {
 | 
			
		||||
    const extensionProps = {
 | 
			
		||||
      links,
 | 
			
		||||
      label: t("primary-navigation.logout")
 | 
			
		||||
      label: t("primary-navigation.first-menu"),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if (binder.hasExtension("primary-navigation.logout", props)) {
 | 
			
		||||
      navigationItems.push(
 | 
			
		||||
        <ExtensionPoint key="primary-navigation.logout" name="primary-navigation.logout" props={props} />
 | 
			
		||||
      );
 | 
			
		||||
    } else {
 | 
			
		||||
      append("/logout", "/logout", "primary-navigation.logout", "logout");
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  appendLogin = (navigationItems: ReactNode[], append: Appender) => {
 | 
			
		||||
    const { t, links, location } = this.props;
 | 
			
		||||
 | 
			
		||||
    const from = location.pathname;
 | 
			
		||||
    const loginPath = "/login";
 | 
			
		||||
    const to = `${loginPath}?from=${encodeURIComponent(from)}`;
 | 
			
		||||
 | 
			
		||||
    const props = {
 | 
			
		||||
      links,
 | 
			
		||||
      label: t("primary-navigation.login"),
 | 
			
		||||
      loginUrl: urls.withContextPath(loginPath),
 | 
			
		||||
      from
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if (binder.hasExtension("primary-navigation.login", props)) {
 | 
			
		||||
      navigationItems.push(
 | 
			
		||||
        <ExtensionPoint key="primary-navigation.login" name="primary-navigation.login" props={props} />
 | 
			
		||||
      );
 | 
			
		||||
    } else {
 | 
			
		||||
      append(to, "/login", "primary-navigation.login", "login");
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  createNavigationItems = () => {
 | 
			
		||||
    const navigationItems: ReactNode[] = [];
 | 
			
		||||
    const { t, links } = this.props;
 | 
			
		||||
 | 
			
		||||
    const props = {
 | 
			
		||||
      links,
 | 
			
		||||
      label: t("primary-navigation.first-menu")
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const append = this.createNavigationAppender(navigationItems);
 | 
			
		||||
    if (binder.hasExtension("primary-navigation.first-menu", props)) {
 | 
			
		||||
      navigationItems.push(
 | 
			
		||||
        <ExtensionPoint key="primary-navigation.first-menu" name="primary-navigation.first-menu" props={props} />
 | 
			
		||||
    const append = createNavigationAppender(navItems);
 | 
			
		||||
    if (binder.hasExtension("primary-navigation.first-menu", extensionProps)) {
 | 
			
		||||
      navItems.push(
 | 
			
		||||
        <ExtensionPoint
 | 
			
		||||
          key="primary-navigation.first-menu"
 | 
			
		||||
          name="primary-navigation.first-menu"
 | 
			
		||||
          props={extensionProps}
 | 
			
		||||
        />
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
    append("/repos/", "/(repo|repos)", "primary-navigation.repositories", "repositories");
 | 
			
		||||
@@ -108,32 +81,21 @@ class PrimaryNavigation extends React.Component<Props> {
 | 
			
		||||
    append("/groups/", "/(group|groups)", "primary-navigation.groups", "groups");
 | 
			
		||||
    append("/admin", "/admin", "primary-navigation.admin", "config");
 | 
			
		||||
 | 
			
		||||
    navigationItems.push(
 | 
			
		||||
    navItems.push(
 | 
			
		||||
      <ExtensionPoint
 | 
			
		||||
        key="primary-navigation"
 | 
			
		||||
        name="primary-navigation"
 | 
			
		||||
        renderAll={true}
 | 
			
		||||
        props={{
 | 
			
		||||
          links: this.props.links
 | 
			
		||||
          links,
 | 
			
		||||
        }}
 | 
			
		||||
      />
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    this.appendLogout(navigationItems, append);
 | 
			
		||||
    this.appendLogin(navigationItems, append);
 | 
			
		||||
 | 
			
		||||
    return navigationItems;
 | 
			
		||||
    return navItems;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    const navigationItems = this.createNavigationItems();
 | 
			
		||||
  return <>{createNavigationItems()}</>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <nav className="tabs is-boxed mb-0">
 | 
			
		||||
        <ul>{navigationItems}</ul>
 | 
			
		||||
      </nav>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default withTranslation("commons")(withRouter(PrimaryNavigation));
 | 
			
		||||
export default PrimaryNavigation;
 | 
			
		||||
 
 | 
			
		||||
@@ -21,42 +21,31 @@
 | 
			
		||||
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
			
		||||
 * SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
import * as React from "react";
 | 
			
		||||
import { Route, Link } from "react-router-dom";
 | 
			
		||||
import React, { FC } from "react";
 | 
			
		||||
import { useRouteMatch, Link } from "react-router-dom";
 | 
			
		||||
import { createAttributesForTesting } from "../devBuild";
 | 
			
		||||
import classNames from "classnames";
 | 
			
		||||
 | 
			
		||||
type Props = {
 | 
			
		||||
  to: string;
 | 
			
		||||
  match: string;
 | 
			
		||||
  label: string;
 | 
			
		||||
  match?: string;
 | 
			
		||||
  activeOnlyWhenExact?: boolean;
 | 
			
		||||
  testId?: string;
 | 
			
		||||
  className?: string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class PrimaryNavigationLink extends React.Component<Props> {
 | 
			
		||||
  renderLink = (route: any) => {
 | 
			
		||||
    const { to, label, testId } = this.props;
 | 
			
		||||
    return (
 | 
			
		||||
      <li className={route.match ? "is-active" : ""}>
 | 
			
		||||
        <Link to={to} {...createAttributesForTesting(testId)}>
 | 
			
		||||
          {label}
 | 
			
		||||
        </Link>
 | 
			
		||||
      </li>
 | 
			
		||||
    );
 | 
			
		||||
  };
 | 
			
		||||
const PrimaryNavigationLink: FC<Props> = ({ to, match, testId, label, className }) => {
 | 
			
		||||
  const routeMatch = useRouteMatch({ path: match });
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    const { to, match, activeOnlyWhenExact, testId } = this.props;
 | 
			
		||||
    const path = match ? match : to;
 | 
			
		||||
    return (
 | 
			
		||||
      <Route
 | 
			
		||||
        path={path}
 | 
			
		||||
        exact={activeOnlyWhenExact}
 | 
			
		||||
        children={this.renderLink}
 | 
			
		||||
        {...createAttributesForTesting(testId)}
 | 
			
		||||
      />
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
  return (
 | 
			
		||||
    <Link
 | 
			
		||||
      to={to}
 | 
			
		||||
      className={classNames(className, "navbar-item", { "is-active": routeMatch })}
 | 
			
		||||
      {...createAttributesForTesting(testId)}
 | 
			
		||||
    >
 | 
			
		||||
      {label}
 | 
			
		||||
    </Link>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default PrimaryNavigationLink;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user