mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-08 14:35:45 +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;
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^5.11.2",
|
||||
"bulma": "^0.9.0",
|
||||
"bulma": "^0.9.3",
|
||||
"bulma-popover": "^1.0.0",
|
||||
"bulma-tooltip": "^3.0.0",
|
||||
"react-diff-view": "^2.4.1"
|
||||
|
||||
@@ -60,10 +60,6 @@ $family-monospace: "Courier New", Monaco, Menlo, "Ubuntu Mono", "source-code-pro
|
||||
padding: 0 0 0 3.8em !important;
|
||||
}
|
||||
|
||||
.main {
|
||||
min-height: calc(100vh - 300px);
|
||||
}
|
||||
|
||||
// shown in top section when pageactions set
|
||||
hr.header-with-actions {
|
||||
margin-top: -10px;
|
||||
@@ -92,7 +88,6 @@ hr.header-with-actions {
|
||||
}
|
||||
|
||||
footer.footer {
|
||||
//height: 100px;
|
||||
background-color: $white-ter;
|
||||
padding: inherit;
|
||||
|
||||
@@ -716,6 +711,13 @@ form .field:not(.is-grouped) {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.has-scm-background {
|
||||
background-image: url(images/scmManagerHero.jpg) !important;
|
||||
background-size: cover;
|
||||
background-position: top center;
|
||||
background-color: #002e4b;
|
||||
}
|
||||
|
||||
// hero
|
||||
.hero.is-dark {
|
||||
background-color: #002e4b;
|
||||
|
||||
@@ -22,10 +22,11 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
import { Links } from "./hal";
|
||||
import { Embedded, Links } from "./hal";
|
||||
|
||||
export type IndexResources = {
|
||||
version: string;
|
||||
initialization?: string;
|
||||
_links: Links;
|
||||
_embedded?: Embedded;
|
||||
};
|
||||
|
||||
104
scm-ui/ui-webapp/public/images/scmLogo.svg
Normal file
104
scm-ui/ui-webapp/public/images/scmLogo.svg
Normal file
@@ -0,0 +1,104 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 25.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 213.8 181.8" style="enable-background:new 0 0 213.8 181.8;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#C7C7C6;}
|
||||
.st1{clip-path:url(#SVGID_2_);fill:url(#SVGID_3_);}
|
||||
.st2{clip-path:url(#SVGID_5_);fill:url(#SVGID_6_);}
|
||||
.st3{clip-path:url(#SVGID_8_);fill:url(#SVGID_9_);}
|
||||
.st4{clip-path:url(#SVGID_11_);fill:url(#SVGID_12_);}
|
||||
.st5{clip-path:url(#SVGID_14_);fill:url(#SVGID_15_);}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M0.1,0L0,0.1C25,30,61.8,97.2,41,160.3c-0.4,3.2-1.4,6.1-2.8,8.7C76.6,99.9,25.3,19.6,14.6,4.2
|
||||
C9.8,2.4,4.9,1,0.1,0"/>
|
||||
<path class="st0" d="M40.5,165.2C75.9,104.5,41.6,35.4,25.2,8.5c4.8,2.3,9.6,4.9,14.1,7.7C54.8,47.3,76.1,107,45.6,157
|
||||
C43.9,159.7,42.2,162.5,40.5,165.2"/>
|
||||
<path class="st0" d="M49.3,151c7.7-13.3,31.3-53.6-0.4-128.3C53.2,26,57.3,29.4,61,33c13.8,44.1,11.6,80-6.6,109.8L49.3,151z"/>
|
||||
<path class="st0" d="M58.8,135.6c7-12,23.4-40.1,11.3-93.2c3.2,3.8,6.1,7.7,8.5,11.7c2,21.9,1.5,45.9-14.7,73.2
|
||||
C62.2,130.1,60.5,132.9,58.8,135.6"/>
|
||||
<path class="st0" d="M67.6,121.5c5.1-8.8,15.8-27.1,15.6-58.7c0.6,1.3,1.1,2.7,1.6,4c1,2.7,1.7,5.3,2.1,7.7
|
||||
c-2.5,13.8-5.6,24.1-14.2,38.6L67.6,121.5z"/>
|
||||
<g>
|
||||
<defs>
|
||||
<path id="SVGID_1_" d="M75.7,181.8L75.7,181.8c0.6-2.7,1.7-5.2,3.3-7.7c24.8-61.6,96.4-89.3,134.8-96l0-0.2
|
||||
c-4.3-2.4-9-4.4-13.8-6.2C182,76.6,91.3,104.8,75.7,181.8"/>
|
||||
</defs>
|
||||
<clipPath id="SVGID_2_">
|
||||
<use xlink:href="#SVGID_1_" style="overflow:visible;"/>
|
||||
</clipPath>
|
||||
|
||||
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="-306.7593" y1="394.7581" x2="-304.2159" y2="394.7581" gradientTransform="matrix(54.3221 0 0 -54.3221 16739.459 21570.8125)">
|
||||
<stop offset="0" style="stop-color:#45BFEA"/>
|
||||
<stop offset="0.5" style="stop-color:#2FA9E0"/>
|
||||
<stop offset="1" style="stop-color:#2893D1"/>
|
||||
</linearGradient>
|
||||
<rect x="75.7" y="71.7" class="st1" width="138.2" height="110.1"/>
|
||||
</g>
|
||||
<g>
|
||||
<defs>
|
||||
<path id="SVGID_4_" d="M77.6,168.5c-0.5,3.2-1,6.4-1.4,9.6c12.2-69.2,83.1-99.9,113-109.9c-5.2-1.4-10.5-2.4-15.8-3.2
|
||||
C141.4,78.9,86.6,110.7,77.6,168.5"/>
|
||||
</defs>
|
||||
<clipPath id="SVGID_5_">
|
||||
<use xlink:href="#SVGID_4_" style="overflow:visible;"/>
|
||||
</clipPath>
|
||||
|
||||
<linearGradient id="SVGID_6_" gradientUnits="userSpaceOnUse" x1="-305.2708" y1="395.9162" x2="-302.7246" y2="395.9162" gradientTransform="matrix(44.3853 0 0 -44.3853 13625.6709 17694.4355)">
|
||||
<stop offset="0" style="stop-color:#45BFEA"/>
|
||||
<stop offset="0.5" style="stop-color:#2FA9E0"/>
|
||||
<stop offset="1" style="stop-color:#2893D1"/>
|
||||
</linearGradient>
|
||||
<rect x="76.1" y="65" class="st2" width="113" height="113.1"/>
|
||||
</g>
|
||||
<g>
|
||||
<defs>
|
||||
<path id="SVGID_7_" d="M145.9,63.8c-39,24.8-60.6,53.7-65.9,88.2l-1.4,9.6c2.7-15.1,10.8-61.1,83.2-97.8
|
||||
c-2.8-0.2-5.5-0.2-8.3-0.2C151,63.6,148.5,63.7,145.9,63.8"/>
|
||||
</defs>
|
||||
<clipPath id="SVGID_8_">
|
||||
<use xlink:href="#SVGID_7_" style="overflow:visible;"/>
|
||||
</clipPath>
|
||||
|
||||
<linearGradient id="SVGID_9_" gradientUnits="userSpaceOnUse" x1="-302.3176" y1="398.1338" x2="-299.7714" y2="398.1338" gradientTransform="matrix(32.6903 0 0 -32.6903 9961.4385 13127.7422)">
|
||||
<stop offset="0" style="stop-color:#45BFEA"/>
|
||||
<stop offset="0.5" style="stop-color:#2FA9E0"/>
|
||||
<stop offset="1" style="stop-color:#2893D1"/>
|
||||
</linearGradient>
|
||||
<rect x="78.6" y="63.6" class="st3" width="83.2" height="98"/>
|
||||
</g>
|
||||
<g>
|
||||
<defs>
|
||||
<path id="SVGID_10_" d="M118.8,68.7c-15.6,15.4-30.8,34.1-36.1,65.4c-0.5,3.2-1,6.4-1.4,9.6c2.4-13.7,8-45.7,51.6-78.5
|
||||
C128,66.1,123.2,67.3,118.8,68.7"/>
|
||||
</defs>
|
||||
<clipPath id="SVGID_11_">
|
||||
<use xlink:href="#SVGID_10_" style="overflow:visible;"/>
|
||||
</clipPath>
|
||||
|
||||
<linearGradient id="SVGID_12_" gradientUnits="userSpaceOnUse" x1="-295.4908" y1="403.173" x2="-292.9446" y2="403.173" gradientTransform="matrix(20.2781 0 0 -20.2781 6073.2446 8280.0889)">
|
||||
<stop offset="0" style="stop-color:#45BFEA"/>
|
||||
<stop offset="0.5" style="stop-color:#2FA9E0"/>
|
||||
<stop offset="1" style="stop-color:#2893D1"/>
|
||||
</linearGradient>
|
||||
<rect x="81.3" y="65.3" class="st4" width="51.6" height="78.5"/>
|
||||
</g>
|
||||
<g>
|
||||
<defs>
|
||||
<path id="SVGID_13_" d="M105.9,74.4c-2.5,1.4-4.7,2.9-6.6,4.5c-7,12.2-11.3,22-14.1,38.7l-1.4,9.6c1.8-10,5.4-30.9,26-54.9
|
||||
C108.4,73,107.1,73.7,105.9,74.4"/>
|
||||
</defs>
|
||||
<clipPath id="SVGID_14_">
|
||||
<use xlink:href="#SVGID_13_" style="overflow:visible;"/>
|
||||
</clipPath>
|
||||
|
||||
<linearGradient id="SVGID_15_" gradientUnits="userSpaceOnUse" x1="-277.7022" y1="416.0602" x2="-275.1562" y2="416.0602" gradientTransform="matrix(10.2218 0 0 -10.2218 2922.3333 4352.7075)">
|
||||
<stop offset="0" style="stop-color:#45BFEA"/>
|
||||
<stop offset="0.5" style="stop-color:#2FA9E0"/>
|
||||
<stop offset="1" style="stop-color:#2893D1"/>
|
||||
</linearGradient>
|
||||
<rect x="83.7" y="72.4" class="st5" width="26" height="54.9"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.1 KiB |
@@ -24,11 +24,11 @@
|
||||
import React, { FC } from "react";
|
||||
import Main from "./Main";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { ErrorPage, Footer, Header, Loading, PrimaryNavigation } from "@scm-manager/ui-components";
|
||||
import { ErrorPage, Footer, Header, Loading } from "@scm-manager/ui-components";
|
||||
import { binder } from "@scm-manager/ui-extensions";
|
||||
import Login from "./Login";
|
||||
import { useIndex, useSubject } from "@scm-manager/ui-api";
|
||||
import Notifications from "./Notifications";
|
||||
import NavigationBar from "./NavigationBar";
|
||||
|
||||
const App: FC = () => {
|
||||
const { data: index } = useIndex();
|
||||
@@ -46,7 +46,7 @@ const App: FC = () => {
|
||||
|
||||
if (index?.initialization) {
|
||||
const Extension = binder.getExtension(`initialization.step.${index.initialization}`);
|
||||
content = <Extension data={index._embedded[index.initialization]} />;
|
||||
content = <Extension data={index?._embedded ? index._embedded[index.initialization] : undefined} />;
|
||||
} else if (!authenticated && !isLoading) {
|
||||
content = <Login />;
|
||||
} else if (isLoading) {
|
||||
@@ -59,13 +59,8 @@ const App: FC = () => {
|
||||
|
||||
return (
|
||||
<div className="App">
|
||||
<Header>
|
||||
{authenticated ? (
|
||||
<div className="is-flex is-justify-content-space-between is-flex-wrap-nowrap ">
|
||||
<PrimaryNavigation links={index._links} />
|
||||
<Notifications />
|
||||
</div>
|
||||
) : null}
|
||||
<Header authenticated={authenticated} links={index._links}>
|
||||
<NavigationBar links={index._links} />
|
||||
</Header>
|
||||
{content}
|
||||
{authenticated ? <Footer me={me} version={index.version} links={index._links} /> : null}
|
||||
|
||||
46
scm-ui/ui-webapp/src/containers/HeaderActions.tsx
Normal file
46
scm-ui/ui-webapp/src/containers/HeaderActions.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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 React, { FC } from "react";
|
||||
import Notifications from "./Notifications";
|
||||
import LogoutButton from "./LogoutButton";
|
||||
import { Links } from "@scm-manager/ui-types";
|
||||
import LoginButton from "./LoginButton";
|
||||
|
||||
type Props = {
|
||||
burgerMode: boolean;
|
||||
links: Links;
|
||||
};
|
||||
|
||||
const HeaderActions: FC<Props> = ({ burgerMode, links }) => {
|
||||
return (
|
||||
<>
|
||||
{!burgerMode ? <Notifications className="navbar-item" /> : null}
|
||||
<LogoutButton burgerMode={burgerMode} links={links} />
|
||||
<LoginButton burgerMode={burgerMode} links={links} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default HeaderActions;
|
||||
86
scm-ui/ui-webapp/src/containers/LoginButton.tsx
Normal file
86
scm-ui/ui-webapp/src/containers/LoginButton.tsx
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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 React, { FC } from "react";
|
||||
import { devices, Icon } from "@scm-manager/ui-components";
|
||||
import { binder, ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Links } from "@scm-manager/ui-types";
|
||||
import classNames from "classnames";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import styled from "styled-components";
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
links?: Links;
|
||||
burgerMode: boolean;
|
||||
};
|
||||
|
||||
const StyledLogoutButton = styled.div`
|
||||
@media screen and (max-width: ${devices.desktop.width}px) {
|
||||
border-top: 1px solid white;
|
||||
margin-top: 1rem;
|
||||
padding-top: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
@media screen and (min-width: ${devices.desktop.width}px) {
|
||||
margin-left: 2rem;
|
||||
}
|
||||
`;
|
||||
|
||||
const LoginButton: FC<Props> = ({ burgerMode, links, className }) => {
|
||||
const [t] = useTranslation("commons");
|
||||
const history = useHistory();
|
||||
|
||||
const extensionProps = {
|
||||
links,
|
||||
label: t("primary-navigation.login"),
|
||||
};
|
||||
|
||||
if (links?.login) {
|
||||
if (binder.hasExtension("primary-navigation.login", extensionProps)) {
|
||||
return <ExtensionPoint key="primary-navigation.login" name="primary-navigation.login" props={extensionProps} />;
|
||||
} else {
|
||||
return (
|
||||
<StyledLogoutButton
|
||||
data-testid="primary-navigation-login"
|
||||
onClick={() => history.push({ pathname: "/login" })}
|
||||
className={classNames("is-align-items-center", "navbar-item", className)}
|
||||
>
|
||||
<Icon
|
||||
title={t("primary-navigation.login")}
|
||||
name="sign-in-alt"
|
||||
color="white"
|
||||
className={burgerMode ? "is-size-5" : "is-size-4"}
|
||||
/>
|
||||
{" " + t("primary-navigation.login")}
|
||||
</StyledLogoutButton>
|
||||
);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export default LoginButton;
|
||||
86
scm-ui/ui-webapp/src/containers/LogoutButton.tsx
Normal file
86
scm-ui/ui-webapp/src/containers/LogoutButton.tsx
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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 React, { FC } from "react";
|
||||
import { devices, Icon } from "@scm-manager/ui-components";
|
||||
import { binder, ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Links } from "@scm-manager/ui-types";
|
||||
import classNames from "classnames";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import styled from "styled-components";
|
||||
|
||||
type Props = {
|
||||
className?: string;
|
||||
links?: Links;
|
||||
burgerMode: boolean;
|
||||
};
|
||||
|
||||
const StyledLogoutButton = styled.div`
|
||||
@media screen and (max-width: ${devices.desktop.width}px) {
|
||||
border-top: 1px solid white;
|
||||
margin-top: 1rem;
|
||||
padding-top: 1rem;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
@media screen and (min-width: ${devices.desktop.width}px) {
|
||||
margin-left: 2rem;
|
||||
}
|
||||
`;
|
||||
|
||||
const LogoutButton: FC<Props> = ({ burgerMode, links, className }) => {
|
||||
const [t] = useTranslation("commons");
|
||||
const history = useHistory();
|
||||
|
||||
const extensionProps = {
|
||||
links,
|
||||
label: t("primary-navigation.logout"),
|
||||
};
|
||||
|
||||
if (links?.logout) {
|
||||
if (binder.hasExtension("primary-navigation.logout", extensionProps)) {
|
||||
return <ExtensionPoint key="primary-navigation.logout" name="primary-navigation.logout" props={extensionProps} />;
|
||||
} else {
|
||||
return (
|
||||
<StyledLogoutButton
|
||||
data-testid="primary-navigation-logout"
|
||||
onClick={() => history.push({ pathname: "/logout" })}
|
||||
className={classNames("is-align-items-center", "navbar-item", className)}
|
||||
>
|
||||
<Icon
|
||||
title={t("primary-navigation.logout")}
|
||||
name="sign-out-alt"
|
||||
color="white"
|
||||
className={burgerMode ? "is-size-5" : "is-size-4"}
|
||||
/>
|
||||
{" " + t("primary-navigation.logout")}
|
||||
</StyledLogoutButton>
|
||||
);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export default LogoutButton;
|
||||
@@ -48,6 +48,7 @@ import Profile from "./Profile";
|
||||
import NamespaceRoot from "../repos/namespaces/containers/NamespaceRoot";
|
||||
import ImportLog from "../repos/importlog/ImportLog";
|
||||
import CreateRepositoryRoot from "../repos/containers/CreateRepositoryRoot";
|
||||
import styled from "styled-components";
|
||||
|
||||
type Props = {
|
||||
me: Me;
|
||||
@@ -55,6 +56,14 @@ type Props = {
|
||||
links: Links;
|
||||
};
|
||||
|
||||
type StyledMainProps = {
|
||||
isSmallHeader: boolean;
|
||||
};
|
||||
|
||||
const StyledMain = styled.div.attrs((props) => ({}))<StyledMainProps>`
|
||||
min-height: calc(100vh - ${(props) => (props.isSmallHeader ? 250 : 210)}px);
|
||||
`;
|
||||
|
||||
class Main extends React.Component<Props> {
|
||||
render() {
|
||||
const { authenticated, me, links } = this.props;
|
||||
@@ -71,7 +80,7 @@ class Main extends React.Component<Props> {
|
||||
}
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<div className="main">
|
||||
<StyledMain className="main" isSmallHeader={!!links.logout}>
|
||||
<Switch>
|
||||
<Redirect exact from="/" to={url} />
|
||||
<Route exact path="/login" component={Login} />
|
||||
@@ -103,11 +112,11 @@ class Main extends React.Component<Props> {
|
||||
props={{
|
||||
me,
|
||||
links,
|
||||
authenticated
|
||||
authenticated,
|
||||
}}
|
||||
/>
|
||||
</Switch>
|
||||
</div>
|
||||
</StyledMain>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
}
|
||||
|
||||
144
scm-ui/ui-webapp/src/containers/NavigationBar.tsx
Normal file
144
scm-ui/ui-webapp/src/containers/NavigationBar.tsx
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* 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 React, { FC, useEffect, useState } from "react";
|
||||
import { Links } from "@scm-manager/ui-types";
|
||||
import classNames from "classnames";
|
||||
import styled from "styled-components";
|
||||
import { devices, Logo, PrimaryNavigation } from "@scm-manager/ui-components";
|
||||
import HeaderActions from "./HeaderActions";
|
||||
import Notifications from "./Notifications";
|
||||
|
||||
const StyledMenuBar = styled.div`
|
||||
background-color: transparent !important;
|
||||
`;
|
||||
|
||||
const LogoItem = styled.a`
|
||||
cursor: default !important;
|
||||
`;
|
||||
|
||||
const StyledNavBar = styled.nav`
|
||||
@media screen and (min-width: ${devices.desktop.width - 1}px) {
|
||||
.navbar-burger-actions {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-start .navbar-item {
|
||||
border-bottom: solid 5px transparent;
|
||||
&.is-active {
|
||||
border-bottom: solid 5px #28b1e8;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-menu.is-active .navbar-start .navbar-item {
|
||||
border-bottom: none;
|
||||
border-left: solid 5px transparent;
|
||||
&.is-active {
|
||||
border-left: solid 5px #28b1e8;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-menu {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
@media screen and (max-width: ${devices.desktop.width - 1}px) {
|
||||
border-bottom: 1px solid white;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-menu.is-active .navbar-end .navbar-item {
|
||||
border-left: solid 5px transparent;
|
||||
}
|
||||
|
||||
.navbar-burger {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.navbar-item {
|
||||
:hover:not(.logo) {
|
||||
background-color: rgba(10, 10, 10, 0.1) !important;
|
||||
color: #fff;
|
||||
}
|
||||
color: #fff !important;
|
||||
background-color: transparent !important;
|
||||
}
|
||||
color: #fff;
|
||||
background-color: transparent !important;
|
||||
`;
|
||||
|
||||
type Props = {
|
||||
links: Links;
|
||||
};
|
||||
|
||||
const BurgerActionBar: FC = () => (
|
||||
<div className="navbar-burger-actions">
|
||||
<Notifications className="navbar-item" direction="left" />
|
||||
</div>
|
||||
);
|
||||
|
||||
const NavigationBar: FC<Props> = ({ links }) => {
|
||||
const [burgerActive, setBurgerActive] = useState(false);
|
||||
useEffect(() => {
|
||||
const close = () => {
|
||||
if (burgerActive) {
|
||||
setBurgerActive(false);
|
||||
}
|
||||
};
|
||||
window.addEventListener("click", close);
|
||||
return () => window.removeEventListener("click", close);
|
||||
}, [burgerActive]);
|
||||
|
||||
return (
|
||||
<StyledNavBar className="navbar mb-0 container" role="navigation" aria-label="main navigation">
|
||||
<div className="navbar-brand">
|
||||
<LogoItem className="navbar-item logo">
|
||||
<Logo withText={false} className="image is-32x32" />
|
||||
</LogoItem>
|
||||
<BurgerActionBar />
|
||||
<button
|
||||
role="button"
|
||||
className={classNames("navbar-burger", { "is-active": burgerActive })}
|
||||
aria-expanded="true"
|
||||
onClick={() => setBurgerActive((active) => !active)}
|
||||
>
|
||||
<span aria-hidden="true" />
|
||||
<span aria-hidden="true" />
|
||||
<span aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
<StyledMenuBar className={classNames("navbar-menu", { "is-active": burgerActive })}>
|
||||
<div className="navbar-start">
|
||||
<PrimaryNavigation links={links} />
|
||||
</div>
|
||||
<div className="navbar-end">
|
||||
<HeaderActions burgerMode={burgerActive} links={links} />
|
||||
</div>
|
||||
</StyledMenuBar>
|
||||
</StyledNavBar>
|
||||
);
|
||||
};
|
||||
|
||||
export default NavigationBar;
|
||||
@@ -33,14 +33,14 @@ import {
|
||||
ToastType,
|
||||
Loading,
|
||||
DateFromNow,
|
||||
devices
|
||||
devices,
|
||||
} from "@scm-manager/ui-components";
|
||||
import styled from "styled-components";
|
||||
import {
|
||||
useClearNotifications,
|
||||
useDismissNotification,
|
||||
useNotifications,
|
||||
useNotificationSubscription
|
||||
useNotificationSubscription,
|
||||
} from "@scm-manager/ui-api";
|
||||
import { Notification, NotificationCollection } from "@scm-manager/ui-types";
|
||||
import { useHistory, Link } from "react-router-dom";
|
||||
@@ -54,13 +54,14 @@ const Bell = styled(Icon)`
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
|
||||
@media screen and (max-width: ${devices.desktop.width}px) {
|
||||
padding-right: 1rem;
|
||||
}
|
||||
`;
|
||||
|
||||
const DropDownMenu = styled.div`
|
||||
type DropDownProps = {
|
||||
direction: "left" | "right";
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
const DropDownMenu = styled.div.attrs((props) => {})<DropDownProps>`
|
||||
min-width: 35rem;
|
||||
|
||||
@media screen and (max-width: ${devices.mobile.width}px) {
|
||||
@@ -79,7 +80,7 @@ const DropDownMenu = styled.div`
|
||||
height: 0;
|
||||
width: 0;
|
||||
top: 0;
|
||||
right: 0.9rem;
|
||||
${(props) => props.direction}: 1.25rem;
|
||||
border-color: transparent;
|
||||
border-bottom-color: white;
|
||||
border-left-color: white;
|
||||
@@ -273,6 +274,9 @@ const BellNotificationContainer = styled.div`
|
||||
position: relative;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
`;
|
||||
|
||||
type NotificationCounterProps = {
|
||||
@@ -282,7 +286,7 @@ type NotificationCounterProps = {
|
||||
const NotificationCounter = styled.span<NotificationCounterProps>`
|
||||
position: absolute;
|
||||
top: -0.5rem;
|
||||
right: ${props => (props.count < 10 ? "0" : "-0.25")}rem;
|
||||
right: ${(props) => (props.count < 10 ? "0" : "-0.25")}rem;
|
||||
`;
|
||||
|
||||
type BellNotificationIconProps = {
|
||||
@@ -317,7 +321,12 @@ const ErrorBox: FC<{ error: Error | null }> = ({ error }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const Notifications: FC = () => {
|
||||
type NotificationProps = {
|
||||
className?: string;
|
||||
direction?: "left" | "right";
|
||||
};
|
||||
|
||||
const Notifications: FC<NotificationProps> = ({ className, direction = "right" }) => {
|
||||
const { data, isLoading, error, refetch } = useNotifications();
|
||||
const { notifications, remove, clear } = useNotificationSubscription(refetch, data);
|
||||
|
||||
@@ -332,15 +341,21 @@ const Notifications: FC = () => {
|
||||
<>
|
||||
<NotificationSubscription notifications={notifications} remove={remove} />
|
||||
<div
|
||||
className={classNames("is-align-self-flex-end", "dropdown", "is-right", "is-hoverable", {
|
||||
"is-active": open
|
||||
})}
|
||||
onClick={e => e.stopPropagation()}
|
||||
className={classNames(
|
||||
"dropdown",
|
||||
`is-${direction}`,
|
||||
"is-hoverable",
|
||||
{
|
||||
"is-active": open,
|
||||
},
|
||||
className
|
||||
)}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<Container className="dropdown-trigger">
|
||||
<BellNotificationIcon data={data} onClick={() => setOpen(o => !o)} />
|
||||
<BellNotificationIcon data={data} onClick={() => setOpen((o) => !o)} />
|
||||
</Container>
|
||||
<DropDownMenu className="dropdown-menu" id="dropdown-menu" role="menu">
|
||||
<DropDownMenu className="dropdown-menu" id="dropdown-menu" role="menu" direction={direction}>
|
||||
<ErrorBox error={error} />
|
||||
{isLoading ? <LoadingBox /> : null}
|
||||
{data ? <NotificationDropDown data={data} remove={remove} clear={clear} /> : null}
|
||||
|
||||
Reference in New Issue
Block a user