mirror of
https://github.com/strapi/strapi.git
synced 2025-12-14 00:29:32 +00:00
DS: moving the left menu to use the DS MainNav (#10689)
This commit is contained in:
parent
7f5ca6b479
commit
b7cd466d27
@ -1,5 +1,7 @@
|
||||
import React from 'react';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import { ThemeProvider } from '@strapi/parts/ThemeProvider';
|
||||
import { lightTheme } from '@strapi/parts/themes';
|
||||
import merge from 'lodash/merge';
|
||||
import pick from 'lodash/pick';
|
||||
import isFunction from 'lodash/isFunction';
|
||||
@ -10,7 +12,7 @@ import configureStore from './core/store/configureStore';
|
||||
import { Plugin } from './core/apis';
|
||||
import App from './pages/App';
|
||||
import AuthLogo from './assets/images/logo_strapi_auth.png';
|
||||
import MenuLogo from './assets/images/logo_strapi_menu.png';
|
||||
import MenuLogo from './assets/images/strapi-img.png';
|
||||
import Providers from './components/Providers';
|
||||
import Theme from './components/Theme';
|
||||
import languageNativeNames from './translations/languageNativeNames';
|
||||
@ -423,44 +425,46 @@ class StrapiApp {
|
||||
} = this.library;
|
||||
|
||||
return (
|
||||
<Theme theme={this.configurations.theme}>
|
||||
<Providers
|
||||
authLogo={this.configurations.authLogo}
|
||||
components={components}
|
||||
fields={fields}
|
||||
localeNames={localeNames}
|
||||
getAdminInjectedComponents={this.getAdminInjectedComponents}
|
||||
getPlugin={this.getPlugin}
|
||||
messages={this.configurations.translations}
|
||||
menu={this.menu}
|
||||
menuLogo={this.configurations.menuLogo}
|
||||
plugins={this.plugins}
|
||||
runHookParallel={this.runHookParallel}
|
||||
runHookWaterfall={(name, initialValue, async = false) => {
|
||||
return this.runHookWaterfall(name, initialValue, async, store);
|
||||
}}
|
||||
runHookSeries={this.runHookSeries}
|
||||
settings={this.settings}
|
||||
showTutorials={this.configurations.tutorials}
|
||||
showReleaseNotification={this.configurations.notifications.releases}
|
||||
store={store}
|
||||
>
|
||||
<>
|
||||
<Helmet
|
||||
link={[
|
||||
{
|
||||
rel: 'icon',
|
||||
type: 'image/png',
|
||||
href: this.configurations.head.favicon,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<BrowserRouter basename={basename}>
|
||||
<App store={store} />
|
||||
</BrowserRouter>
|
||||
</>
|
||||
</Providers>
|
||||
</Theme>
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<Theme theme={this.configurations.theme}>
|
||||
<Providers
|
||||
authLogo={this.configurations.authLogo}
|
||||
components={components}
|
||||
fields={fields}
|
||||
localeNames={localeNames}
|
||||
getAdminInjectedComponents={this.getAdminInjectedComponents}
|
||||
getPlugin={this.getPlugin}
|
||||
messages={this.configurations.translations}
|
||||
menu={this.menu}
|
||||
menuLogo={this.configurations.menuLogo}
|
||||
plugins={this.plugins}
|
||||
runHookParallel={this.runHookParallel}
|
||||
runHookWaterfall={(name, initialValue, async = false) => {
|
||||
return this.runHookWaterfall(name, initialValue, async, store);
|
||||
}}
|
||||
runHookSeries={this.runHookSeries}
|
||||
settings={this.settings}
|
||||
showTutorials={this.configurations.tutorials}
|
||||
showReleaseNotification={this.configurations.notifications.releases}
|
||||
store={store}
|
||||
>
|
||||
<>
|
||||
<Helmet
|
||||
link={[
|
||||
{
|
||||
rel: 'icon',
|
||||
type: 'image/png',
|
||||
href: this.configurations.head.favicon,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<BrowserRouter basename={basename}>
|
||||
<App store={store} />
|
||||
</BrowserRouter>
|
||||
</>
|
||||
</Providers>
|
||||
</Theme>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 24 KiB |
BIN
packages/core/admin/admin/src/assets/images/strapi-img.png
Normal file
BIN
packages/core/admin/admin/src/assets/images/strapi-img.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.5 KiB |
@ -6,176 +6,6 @@ import '@fortawesome/fontawesome-free/css/all.css';
|
||||
import '@fortawesome/fontawesome-free/js/all.min.js';
|
||||
import { createGlobalStyle } from 'styled-components';
|
||||
|
||||
const GlobalStyle = createGlobalStyle`
|
||||
html {
|
||||
font-size: 62.5%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Lato';
|
||||
font-size: 1.4rem;
|
||||
line-height: 1.5;
|
||||
color: #292b2c;
|
||||
}
|
||||
|
||||
* {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
box-sizing: border-box;
|
||||
font-family: 'Lato';
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 {
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.btn {
|
||||
font-size: 1.4rem;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.cursor-pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/*
|
||||
* Override
|
||||
*/
|
||||
|
||||
.modal {
|
||||
background: transparent;
|
||||
.modal-dialog {
|
||||
max-width: 74.5rem;
|
||||
margin: 16rem auto 3rem calc(50% - #{$left - menu - width});
|
||||
position: relative;
|
||||
z-index: 999;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-backdrop.show {
|
||||
opacity: 0.15;
|
||||
}
|
||||
|
||||
|
||||
.modal-content {
|
||||
border-radius: .2rem;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
border: none;
|
||||
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
button {
|
||||
&.close {
|
||||
margin: 0;
|
||||
padding: 2rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
.video-react {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
form .row {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.form-check {
|
||||
padding-left: 0;
|
||||
.form-check-label {
|
||||
padding-left: 1.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
textarea.form-control {
|
||||
height: 10.6rem;
|
||||
}
|
||||
|
||||
.input-group-addon {
|
||||
padding-left: 0.75rem;
|
||||
padding-right: 0.75rem;
|
||||
}
|
||||
|
||||
.btn-secondary:not(:disabled):not(.disabled):active:focus,
|
||||
.btn-secondary:not(:disabled):not(.disabled).active:focus,
|
||||
.btn-secondary,
|
||||
.show > .btn-secondary.dropdown-toggle:focus {
|
||||
&:focus, &:active, &:hover, &.focus {
|
||||
box-shadow: 0 0 0 0px rgba(134,142,150,0.5);
|
||||
color: rgb(51, 55, 64);
|
||||
background-color: rgb(250, 250, 251) !important;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Notifications animation
|
||||
*/
|
||||
|
||||
.notification-enter {
|
||||
opacity: 0;
|
||||
top: -70px;
|
||||
}
|
||||
|
||||
.notification-enter.notification-enter-active {
|
||||
opacity: 1;
|
||||
transition: all 400ms ease-in;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.notification-exit {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.notification-exit.notification-exit-active {
|
||||
opacity: 0;
|
||||
transition: all 400ms ease-in;
|
||||
}
|
||||
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 9px;
|
||||
height: 5px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track:hover {
|
||||
background-color: #ddd;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: #ccc;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background-color: #bbb;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* firefox scrollbar */
|
||||
/* stylelint-disable */
|
||||
* {
|
||||
scrollbar-color: #bbb #eee;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
/* stylelint-enable */
|
||||
|
||||
a::-moz-focus-inner {
|
||||
border: 0;
|
||||
}
|
||||
`;
|
||||
const GlobalStyle = createGlobalStyle``;
|
||||
|
||||
export default GlobalStyle;
|
||||
|
||||
@ -1,61 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* Wrapper
|
||||
*
|
||||
*/
|
||||
|
||||
import styled from 'styled-components';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
position: fixed;
|
||||
float: left;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100vh;
|
||||
width: ${props => props.theme.main.sizes.leftMenu.width};
|
||||
background: ${props => props.theme.main.colors.strapi['blue-darker']};
|
||||
|
||||
/* scrollbar overrides */
|
||||
* {
|
||||
::-webkit-scrollbar {
|
||||
width: 7px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track,
|
||||
::-webkit-scrollbar-track:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: ${props => props.theme.main.colors.leftMenu['title-color']};
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background-color: ${props => props.theme.main.colors.leftMenu['link-color']};
|
||||
}
|
||||
|
||||
/* firefox */
|
||||
scrollbar-color: ${props => props.theme.main.colors.leftMenu['title-color']} transparent;
|
||||
}
|
||||
`;
|
||||
|
||||
Wrapper.defaultProps = {
|
||||
theme: {
|
||||
main: {
|
||||
colors: {
|
||||
strapi: {},
|
||||
},
|
||||
sizes: {
|
||||
header: {},
|
||||
leftMenu: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
Wrapper.propTypes = {
|
||||
theme: PropTypes.object,
|
||||
};
|
||||
|
||||
export default Wrapper;
|
||||
@ -1,51 +0,0 @@
|
||||
import styled from 'styled-components';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
background: ${props => props.theme.main.colors.strapi['blue-darker']};
|
||||
bottom: 0;
|
||||
.poweredBy {
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
height: 3rem;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
line-height: 3rem;
|
||||
background-color: rgba(255, 255, 255, 0.02);
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
letter-spacing: 0.05rem;
|
||||
vertical-align: middle;
|
||||
color: ${({ theme }) => theme.main.colors.strapi['gray-light']};
|
||||
}
|
||||
`;
|
||||
|
||||
const A = styled.a`
|
||||
&:hover {
|
||||
color: #007bff;
|
||||
text-decoration: underline;
|
||||
}
|
||||
`;
|
||||
|
||||
Wrapper.defaultProps = {
|
||||
theme: {
|
||||
main: {
|
||||
colors: {
|
||||
strapi: {},
|
||||
},
|
||||
sizes: {
|
||||
header: {},
|
||||
leftMenu: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
Wrapper.propTypes = {
|
||||
theme: PropTypes.object,
|
||||
};
|
||||
|
||||
export default Wrapper;
|
||||
export { A };
|
||||
@ -1,40 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* LeftMenuFooter
|
||||
*
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { useAppInfos } from '@strapi/helper-plugin';
|
||||
import Wrapper, { A } from './Wrapper';
|
||||
|
||||
function LeftMenuFooter() {
|
||||
const projectType = strapi.projectType;
|
||||
const { strapiVersion } = useAppInfos();
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<div className="poweredBy">
|
||||
<A key="website" href="https://strapi.io" target="_blank" rel="noopener noreferrer">
|
||||
Strapi
|
||||
</A>
|
||||
|
||||
<A
|
||||
href={`https://github.com/strapi/strapi/releases/tag/v${strapiVersion}`}
|
||||
key="github"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
v{strapiVersion}
|
||||
</A>
|
||||
|
||||
<A href="https://strapi.io" target="_blank" rel="noopener noreferrer">
|
||||
— {projectType} Edition
|
||||
</A>
|
||||
</div>
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default LeftMenuFooter;
|
||||
@ -1,48 +0,0 @@
|
||||
import styled from 'styled-components';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
background-color: ${props => props.theme.main.colors.leftMenu['background-header-link']};
|
||||
padding-left: 2rem;
|
||||
height: ${props => props.theme.main.sizes.leftMenu.height};
|
||||
|
||||
.leftMenuHeaderLink {
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.projectName {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: ${props => props.theme.main.sizes.leftMenu.height};
|
||||
font-size: 2rem;
|
||||
letter-spacing: 0.2rem;
|
||||
color: $white;
|
||||
|
||||
background-image: url(${props => props.logo});
|
||||
background-repeat: no-repeat;
|
||||
background-position: left center;
|
||||
background-size: auto 2.5rem;
|
||||
}
|
||||
`;
|
||||
|
||||
Wrapper.defaultProps = {
|
||||
theme: {
|
||||
main: {
|
||||
colors: {
|
||||
leftMenu: {},
|
||||
},
|
||||
sizes: {
|
||||
header: {},
|
||||
leftMenu: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
Wrapper.propTypes = {
|
||||
theme: PropTypes.object,
|
||||
};
|
||||
|
||||
export default Wrapper;
|
||||
@ -1,18 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useConfigurations } from '../../../../hooks';
|
||||
import Wrapper from './Wrapper';
|
||||
|
||||
const LeftMenuHeader = () => {
|
||||
const { menuLogo } = useConfigurations();
|
||||
|
||||
return (
|
||||
<Wrapper logo={menuLogo}>
|
||||
<Link to="/" className="leftMenuHeaderLink">
|
||||
<span className="projectName" />
|
||||
</Link>
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default LeftMenuHeader;
|
||||
@ -1,23 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styled from 'styled-components';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
|
||||
const FaIcon = styled(({ small, ...props }) => <FontAwesomeIcon {...props} />)`
|
||||
position: absolute;
|
||||
top: ${({ small }) => (small ? 'calc(50% - 0.3rem)' : 'calc(50% - 0.9rem + 0.3rem)')};
|
||||
left: ${({ small }) => (small ? '2.2rem' : '1.6rem')};
|
||||
width: ${({ small }) => (small ? 'auto' : '1.3rem')} !important;
|
||||
font-size: ${({ small }) => (small ? '.5rem' : '1.3rem')};
|
||||
`;
|
||||
|
||||
const LeftMenuIcon = ({ icon }) => <FaIcon small={icon === 'circle'} icon={icon} />;
|
||||
|
||||
LeftMenuIcon.propTypes = {
|
||||
icon: PropTypes.string,
|
||||
};
|
||||
LeftMenuIcon.defaultProps = {
|
||||
icon: 'circle',
|
||||
};
|
||||
|
||||
export default LeftMenuIcon;
|
||||
@ -1,40 +0,0 @@
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const Link = styled(NavLink)`
|
||||
display: flex;
|
||||
position: relative;
|
||||
padding-top: 1rem;
|
||||
padding-bottom: 0.2rem;
|
||||
padding-left: 1.6rem;
|
||||
min-height: 3.6rem;
|
||||
line-height: 1.8rem;
|
||||
border-left: 0.3rem solid transparent;
|
||||
cursor: pointer;
|
||||
color: ${props => props.theme.main.colors.leftMenu['link-color']};
|
||||
text-decoration: none;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
|
||||
&:hover {
|
||||
color: ${props => props.theme.main.colors.white};
|
||||
background: ${props => props.theme.main.colors.leftMenu['link-hover']};
|
||||
border-left: 0.3rem solid ${props => props.theme.main.colors.strapi.blue};
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
color: ${props => props.theme.main.colors.white};
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&:visited {
|
||||
color: ${props => props.theme.main.colors.leftMenu['link-color']};
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: white !important;
|
||||
border-left: 0.3rem solid ${props => props.theme.main.colors.strapi.blue};
|
||||
}
|
||||
`;
|
||||
|
||||
export default Link;
|
||||
@ -1,10 +0,0 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const LinkLabel = styled.span`
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
padding-right: 1rem;
|
||||
padding-left: 2.5rem;
|
||||
`;
|
||||
|
||||
export default LinkLabel;
|
||||
@ -1,32 +0,0 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Text } from '@buffetjs/core';
|
||||
|
||||
const NotificationWrapper = styled.div`
|
||||
height: 14px;
|
||||
margin-top: 4px;
|
||||
margin-right: 20px;
|
||||
padding: 0px 4px;
|
||||
background-color: #383d49;
|
||||
border-radius: 2px;
|
||||
font-size: 11px;
|
||||
`;
|
||||
|
||||
const NotificationCount = ({ count }) => (
|
||||
<NotificationWrapper>
|
||||
<Text fontWeight="bold" fontSize="xs" lineHeight="14px" color="#919bae">
|
||||
{count}
|
||||
</Text>
|
||||
</NotificationWrapper>
|
||||
);
|
||||
|
||||
NotificationCount.defaultProps = {
|
||||
count: 0,
|
||||
};
|
||||
|
||||
NotificationCount.propTypes = {
|
||||
count: PropTypes.number,
|
||||
};
|
||||
|
||||
export default NotificationCount;
|
||||
@ -1,40 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* LeftMenuLink
|
||||
*
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import LinkLabel from './LinkLabel';
|
||||
import Link from './Link';
|
||||
import LeftMenuIcon from './LeftMenuIcon';
|
||||
import NotificationCount from './NotificationCount';
|
||||
|
||||
const LeftMenuLink = ({ to, icon, intlLabel, notificationsCount }) => {
|
||||
return (
|
||||
<Link to={to}>
|
||||
<LeftMenuIcon icon={icon} />
|
||||
{/* TODO change with new DS */}
|
||||
<FormattedMessage {...intlLabel}>
|
||||
{message => <LinkLabel>{message}</LinkLabel>}
|
||||
</FormattedMessage>
|
||||
{notificationsCount > 0 && <NotificationCount count={notificationsCount} />}
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
LeftMenuLink.propTypes = {
|
||||
to: PropTypes.string.isRequired,
|
||||
icon: PropTypes.string,
|
||||
intlLabel: PropTypes.object.isRequired,
|
||||
notificationsCount: PropTypes.number,
|
||||
};
|
||||
|
||||
LeftMenuLink.defaultProps = {
|
||||
icon: 'circle',
|
||||
notificationsCount: 0,
|
||||
};
|
||||
|
||||
export default LeftMenuLink;
|
||||
@ -1,31 +0,0 @@
|
||||
import styled from 'styled-components';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const LinksContainer = styled.div`
|
||||
padding-top: 0.7rem;
|
||||
position: absolute;
|
||||
top: ${props => props.theme.main.sizes.leftMenu.height};
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
overflow-y: auto;
|
||||
height: calc(100vh - (${props => props.theme.main.sizes.leftMenu.height} + 3rem));
|
||||
box-sizing: border-box;
|
||||
`;
|
||||
|
||||
LinksContainer.defaultProps = {
|
||||
theme: {
|
||||
main: {
|
||||
sizes: {
|
||||
header: {},
|
||||
leftMenu: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
LinksContainer.propTypes = {
|
||||
theme: PropTypes.object,
|
||||
};
|
||||
|
||||
export default LinksContainer;
|
||||
@ -1,10 +0,0 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const LeftMenuListLink = styled.div`
|
||||
max-height: 180px;
|
||||
margin-bottom: 19px;
|
||||
margin-right: 25px;
|
||||
overflow: auto;
|
||||
`;
|
||||
|
||||
export default LeftMenuListLink;
|
||||
@ -1,22 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import LeftMenuLink from '../Link';
|
||||
import LeftMenuListLink from './LeftMenuListLink';
|
||||
|
||||
const LeftMenuLinksSection = ({ links }) => {
|
||||
return (
|
||||
<>
|
||||
<LeftMenuListLink>
|
||||
{links.map(link => (
|
||||
<LeftMenuLink {...link} key={link.to} />
|
||||
))}
|
||||
</LeftMenuListLink>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
LeftMenuLinksSection.propTypes = {
|
||||
links: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
};
|
||||
|
||||
export default LeftMenuLinksSection;
|
||||
@ -1,30 +0,0 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const SectionTitle = styled.div`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding-left: 2rem;
|
||||
padding-right: 1.6rem;
|
||||
padding-top: 1rem;
|
||||
margin-bottom: 0.9rem;
|
||||
color: ${props => props.theme.main.colors.leftMenu['title-color']};
|
||||
text-transform: uppercase;
|
||||
font-size: 1.1rem;
|
||||
letter-spacing: 0.1rem;
|
||||
font-weight: 800;
|
||||
max-height: 26px;
|
||||
`;
|
||||
|
||||
SectionTitle.defaultProps = {
|
||||
theme: {
|
||||
main: {
|
||||
colors: {
|
||||
leftMenu: {
|
||||
'title-color': '#5b626f',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default SectionTitle;
|
||||
@ -1,5 +0,0 @@
|
||||
export { default as Footer } from './Footer';
|
||||
export { default as Header } from './Header';
|
||||
export { default as LinksContainer } from './Links';
|
||||
export { default as LinksSection } from './LinksSection';
|
||||
export { default as SectionTitle } from './SectionTitle';
|
||||
@ -1,58 +1,84 @@
|
||||
import React, { memo } from 'react';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { BaselineAlignment } from '@strapi/helper-plugin';
|
||||
import { Footer, Header, LinksContainer, LinksSection, SectionTitle } from './compos';
|
||||
import LeftMenuLink from './compos/Link';
|
||||
|
||||
import Wrapper from './Wrapper';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import get from 'lodash/get';
|
||||
import {
|
||||
MainNav,
|
||||
NavBrand,
|
||||
NavSections,
|
||||
NavLink,
|
||||
NavSection,
|
||||
NavUser,
|
||||
NavCondense,
|
||||
Divider,
|
||||
} from '@strapi/parts';
|
||||
import ContentIcon from '@strapi/icons/ContentIcon';
|
||||
import { auth, usePersistentState } from '@strapi/helper-plugin';
|
||||
import useConfigurations from '../../hooks/useConfigurations';
|
||||
|
||||
const LeftMenu = ({ generalSectionLinks, pluginsSectionLinks }) => {
|
||||
const { menuLogo } = useConfigurations();
|
||||
const [condensed, setCondensed] = usePersistentState('navbar-condensed', false);
|
||||
|
||||
const userInfo = auth.getUserInfo();
|
||||
|
||||
let displayName;
|
||||
|
||||
if (userInfo && userInfo.firstname && userInfo.lastname) {
|
||||
displayName = `${userInfo.firstname} ${userInfo.lastname}`;
|
||||
} else {
|
||||
displayName = get(userInfo, 'username', '');
|
||||
}
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<Header />
|
||||
<MainNav condensed={condensed}>
|
||||
<NavBrand
|
||||
workplace="Workplace"
|
||||
title="Strapi Dashboard"
|
||||
icon={<img src={menuLogo} alt="" />}
|
||||
/>
|
||||
|
||||
<LinksContainer>
|
||||
<BaselineAlignment top size="16px" />
|
||||
<LeftMenuLink
|
||||
to="/content-manager"
|
||||
icon="book-open"
|
||||
intlLabel={{
|
||||
id: `content-manager.plugin.name`,
|
||||
defaultMessage: 'Content manager',
|
||||
}}
|
||||
/>
|
||||
<BaselineAlignment bottom size="2px" />
|
||||
<Divider />
|
||||
|
||||
{pluginsSectionLinks.length > 0 && (
|
||||
<>
|
||||
<SectionTitle>
|
||||
<FormattedMessage
|
||||
id="app.components.LeftMenuLinkContainer.listPlugins"
|
||||
defaultMessage="Plugins"
|
||||
/>
|
||||
</SectionTitle>
|
||||
<LinksSection
|
||||
links={pluginsSectionLinks}
|
||||
searchable={false}
|
||||
emptyLinksListMessage="app.components.LeftMenuLinkContainer.noPluginsInstalled"
|
||||
/>
|
||||
</>
|
||||
<NavSections>
|
||||
<NavLink to="/content-manager" icon={<ContentIcon />}>
|
||||
<FormattedMessage id="content-manager.plugin.name" defaultMessage="Content manager" />
|
||||
</NavLink>
|
||||
|
||||
{pluginsSectionLinks.length > 0 ? (
|
||||
<NavSection label="Plugins">
|
||||
{pluginsSectionLinks.map(link => (
|
||||
<NavLink to={link.to} key={link.to} icon={<FontAwesomeIcon icon={link.icon} />}>
|
||||
<FormattedMessage {...link.intlLabel} />
|
||||
</NavLink>
|
||||
))}
|
||||
</NavSection>
|
||||
) : null}
|
||||
|
||||
{generalSectionLinks.length > 0 ? (
|
||||
<NavSection label="General">
|
||||
{generalSectionLinks.map(link => (
|
||||
<NavLink to={link.to} key={link.to} icon={<FontAwesomeIcon icon={link.icon} />}>
|
||||
<FormattedMessage {...link.intlLabel} />
|
||||
</NavLink>
|
||||
))}
|
||||
</NavSection>
|
||||
) : null}
|
||||
</NavSections>
|
||||
|
||||
<NavUser src="https://avatars.githubusercontent.com/u/3874873?v=4" to="/me">
|
||||
{displayName}
|
||||
</NavUser>
|
||||
|
||||
<NavCondense onClick={() => setCondensed(s => !s)}>
|
||||
{condensed ? (
|
||||
<FormattedMessage id="app.components.LeftMenu.expand" />
|
||||
) : (
|
||||
<FormattedMessage id="app.components.LeftMenu.collapse" />
|
||||
)}
|
||||
{generalSectionLinks.length > 0 && (
|
||||
<>
|
||||
<SectionTitle>
|
||||
<FormattedMessage
|
||||
id="app.components.LeftMenuLinkContainer.general"
|
||||
defaultMessage="General"
|
||||
/>
|
||||
</SectionTitle>
|
||||
<LinksSection links={generalSectionLinks} searchable={false} />
|
||||
</>
|
||||
)}
|
||||
</LinksContainer>
|
||||
<Footer key="footer" />
|
||||
</Wrapper>
|
||||
</NavCondense>
|
||||
</MainNav>
|
||||
);
|
||||
};
|
||||
|
||||
@ -61,4 +87,4 @@ LeftMenu.propTypes = {
|
||||
pluginsSectionLinks: PropTypes.array.isRequired,
|
||||
};
|
||||
|
||||
export default memo(LeftMenu);
|
||||
export default LeftMenu;
|
||||
|
||||
30
packages/core/admin/admin/src/layouts/AppLayout.js
Normal file
30
packages/core/admin/admin/src/layouts/AppLayout.js
Normal file
@ -0,0 +1,30 @@
|
||||
import React from 'react';
|
||||
import { Row, Box, SkipToContent } from '@strapi/parts';
|
||||
import PropTypes from 'prop-types';
|
||||
import styled from 'styled-components';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
const FlexBox = styled(Box)`
|
||||
flex: 1;
|
||||
`;
|
||||
|
||||
const AppLayout = ({ children, sideNav }) => {
|
||||
return (
|
||||
<Box background="neutral100">
|
||||
<SkipToContent>
|
||||
<FormattedMessage id="skipToContent" />
|
||||
</SkipToContent>
|
||||
<Row alignItems="flex-start">
|
||||
{sideNav}
|
||||
<FlexBox>{children}</FlexBox>
|
||||
</Row>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
AppLayout.propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
sideNav: PropTypes.node.isRequired,
|
||||
};
|
||||
|
||||
export default AppLayout;
|
||||
@ -16,15 +16,11 @@ import {
|
||||
import { DndProvider } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
import adminPermissions from '../../permissions';
|
||||
import Header from '../../components/Header/index';
|
||||
import NavTopRightWrapper from '../../components/NavTopRightWrapper';
|
||||
import LeftMenu from '../../components/LeftMenu';
|
||||
import Onboarding from '../../components/Onboarding';
|
||||
import { useMenu, useReleaseNotification } from '../../hooks';
|
||||
import Logout from './Logout';
|
||||
import Wrapper from './Wrapper';
|
||||
import Content from './Content';
|
||||
import { createRoute } from '../../utils';
|
||||
import AppLayout from '../../layouts/AppLayout';
|
||||
|
||||
const CM = lazy(() =>
|
||||
import(/* webpackChunkName: "content-manager" */ '../../content-manager/pages/App')
|
||||
@ -83,46 +79,42 @@ const Admin = () => {
|
||||
|
||||
return (
|
||||
<DndProvider backend={HTML5Backend}>
|
||||
<Wrapper>
|
||||
<LeftMenu
|
||||
generalSectionLinks={generalSectionLinks}
|
||||
pluginsSectionLinks={pluginsSectionLinks}
|
||||
/>
|
||||
<NavTopRightWrapper>
|
||||
<Logout />
|
||||
</NavTopRightWrapper>
|
||||
<div className="adminPageRightWrapper">
|
||||
<Header />
|
||||
<Content>
|
||||
<Suspense fallback={<LoadingIndicatorPage />}>
|
||||
<Switch>
|
||||
<Route path="/" component={HomePage} exact />
|
||||
<Route path="/me" component={ProfilePage} exact />
|
||||
<AppLayout
|
||||
sideNav={
|
||||
// eslint-disable-next-line react/jsx-wrap-multilines
|
||||
<LeftMenu
|
||||
generalSectionLinks={generalSectionLinks}
|
||||
pluginsSectionLinks={pluginsSectionLinks}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Suspense fallback={<LoadingIndicatorPage />}>
|
||||
<Switch>
|
||||
<Route path="/" component={HomePage} exact />
|
||||
<Route path="/me" component={ProfilePage} exact />
|
||||
|
||||
<Route path="/content-manager" component={CM} />
|
||||
<Route path="/plugins/content-type-builder" component={CTB} />
|
||||
<Route path="/plugins/upload" component={Upload} />
|
||||
{routes}
|
||||
<Route path="/settings/:settingId" component={SettingsPage} />
|
||||
<Route path="/settings" component={SettingsPage} exact />
|
||||
<Route path="/marketplace">
|
||||
<CheckPagePermissions permissions={adminPermissions.marketplace.main}>
|
||||
<MarketplacePage />
|
||||
</CheckPagePermissions>
|
||||
</Route>
|
||||
<Route path="/list-plugins" exact>
|
||||
<CheckPagePermissions permissions={adminPermissions.marketplace.main}>
|
||||
<InstalledPluginsPage />
|
||||
</CheckPagePermissions>
|
||||
</Route>
|
||||
<Route path="/404" component={NotFoundPage} />
|
||||
<Route path="" component={NotFoundPage} />
|
||||
</Switch>
|
||||
</Suspense>
|
||||
</Content>
|
||||
</div>
|
||||
<Route path="/content-manager" component={CM} />
|
||||
<Route path="/plugins/content-type-builder" component={CTB} />
|
||||
<Route path="/plugins/upload" component={Upload} />
|
||||
{routes}
|
||||
<Route path="/settings/:settingId" component={SettingsPage} />
|
||||
<Route path="/settings" component={SettingsPage} exact />
|
||||
<Route path="/marketplace">
|
||||
<CheckPagePermissions permissions={adminPermissions.marketplace.main}>
|
||||
<MarketplacePage />
|
||||
</CheckPagePermissions>
|
||||
</Route>
|
||||
<Route path="/list-plugins" exact>
|
||||
<CheckPagePermissions permissions={adminPermissions.marketplace.main}>
|
||||
<InstalledPluginsPage />
|
||||
</CheckPagePermissions>
|
||||
</Route>
|
||||
<Route path="/404" component={NotFoundPage} />
|
||||
<Route path="" component={NotFoundPage} />
|
||||
</Switch>
|
||||
</Suspense>
|
||||
<Onboarding />
|
||||
</Wrapper>
|
||||
</AppLayout>
|
||||
</DndProvider>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,15 +0,0 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
font-family: 'Lato';
|
||||
font-size: 1.4rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
color: #292b2c;
|
||||
`;
|
||||
|
||||
const Content = styled.div`
|
||||
display: block;
|
||||
`;
|
||||
|
||||
export { Content, Wrapper };
|
||||
@ -18,7 +18,6 @@ import { createRoute, makeUniqueRoutes } from '../../utils';
|
||||
import AuthPage from '../AuthPage';
|
||||
import NotFoundPage from '../NotFoundPage';
|
||||
import { getUID } from './utils';
|
||||
import { Content, Wrapper } from './components';
|
||||
import routes from './utils/routes';
|
||||
|
||||
const AuthenticatedApp = lazy(() =>
|
||||
@ -107,22 +106,18 @@ function App() {
|
||||
return (
|
||||
<Suspense fallback={<LoadingIndicatorPage />}>
|
||||
<TrackingContext.Provider value={uuid}>
|
||||
<Wrapper>
|
||||
<Content>
|
||||
<Switch>
|
||||
{authRoutes}
|
||||
<Route
|
||||
path="/auth/:authType"
|
||||
render={routerProps => (
|
||||
<AuthPage {...routerProps} setHasAdmin={setHasAdmin} hasAdmin={hasAdmin} />
|
||||
)}
|
||||
exact
|
||||
/>
|
||||
<PrivateRoute path="/" component={AuthenticatedApp} />
|
||||
<Route path="" component={NotFoundPage} />
|
||||
</Switch>
|
||||
</Content>
|
||||
</Wrapper>
|
||||
<Switch>
|
||||
{authRoutes}
|
||||
<Route
|
||||
path="/auth/:authType"
|
||||
render={routerProps => (
|
||||
<AuthPage {...routerProps} setHasAdmin={setHasAdmin} hasAdmin={hasAdmin} />
|
||||
)}
|
||||
exact
|
||||
/>
|
||||
<PrivateRoute path="/" component={AuthenticatedApp} />
|
||||
<Route path="" component={NotFoundPage} />
|
||||
</Switch>
|
||||
</TrackingContext.Provider>
|
||||
</Suspense>
|
||||
);
|
||||
|
||||
@ -219,6 +219,8 @@
|
||||
"app.components.LeftMenuLinkContainer.plugins": "Plugins",
|
||||
"app.components.LeftMenuLinkContainer.settings": "Settings",
|
||||
"app.components.LeftMenuLinkContainer.singleTypes": "Single Types",
|
||||
"app.components.LeftMenu.expand": "Expanded the navbar",
|
||||
"app.components.LeftMenu.collapse": "Collapse the navbar",
|
||||
"app.components.ListPluginsPage.deletePlugin.description": "It might take a few seconds to uninstall the plugin.",
|
||||
"app.components.ListPluginsPage.deletePlugin.title": "Uninstalling",
|
||||
"app.components.ListPluginsPage.description": "List of the installed plugins in the project.",
|
||||
@ -553,5 +555,6 @@
|
||||
"notification.version.update.link": "See more",
|
||||
"notification.version.update.message": "A new version of Strapi is available!",
|
||||
"or": "OR",
|
||||
"request.error.model.unknown": "This model doesn't exist"
|
||||
"request.error.model.unknown": "This model doesn't exist",
|
||||
"skipToContent": "Skip to content"
|
||||
}
|
||||
|
||||
@ -41,6 +41,8 @@
|
||||
"@strapi/babel-plugin-switch-ee-ce": "1.0.0",
|
||||
"@strapi/helper-plugin": "3.6.6",
|
||||
"@strapi/utils": "3.6.6",
|
||||
"@strapi/icons": "0.0.1-alpha.4",
|
||||
"@strapi/parts": "0.0.1-alpha.4",
|
||||
"axios": "^0.21.1",
|
||||
"babel-loader": "8.2.2",
|
||||
"babel-plugin-styled-components": "1.12.0",
|
||||
|
||||
@ -0,0 +1,17 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
const usePersistentState = (key, defaultValue) => {
|
||||
const [value, setValue] = useState(() => {
|
||||
const stickyValue = window.localStorage.getItem(key);
|
||||
|
||||
return stickyValue !== null ? JSON.parse(stickyValue) : defaultValue;
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
window.localStorage.setItem(key, JSON.stringify(value));
|
||||
}, [key, value]);
|
||||
|
||||
return [value, setValue];
|
||||
};
|
||||
|
||||
export default usePersistentState;
|
||||
@ -128,6 +128,7 @@ export { default as useOverlayBlocker } from './hooks/useOverlayBlocker';
|
||||
export { default as useAutoReloadOverlayBlocker } from './hooks/useAutoReloadOverlayBlocker';
|
||||
export { default as useRBACProvider } from './hooks/useRBACProvider';
|
||||
export { default as useRBAC } from './hooks/useRBAC';
|
||||
export { default as usePersistentState } from './hooks/usePersistentState';
|
||||
|
||||
// Providers
|
||||
export { default as LibraryProvider } from './providers/LibraryProvider';
|
||||
|
||||
18
yarn.lock
18
yarn.lock
@ -3490,6 +3490,19 @@
|
||||
tslib "^2.0.0"
|
||||
upath "2.0.1"
|
||||
|
||||
"@strapi/icons@0.0.1-alpha.4":
|
||||
version "0.0.1-alpha.4"
|
||||
resolved "https://registry.yarnpkg.com/@strapi/icons/-/icons-0.0.1-alpha.4.tgz#f3e66f05b8aa0ae1615380e68cc0125fe405777e"
|
||||
integrity sha512-Lnfam0vCD2PWNdvnyG9ZHtvTKgDdSc7AvqzqlxC3BlbvrNBMov3XtvCZFfW1TgXZ4iwGbCmuN+JDmiFtiAWygA==
|
||||
|
||||
"@strapi/parts@0.0.1-alpha.4":
|
||||
version "0.0.1-alpha.4"
|
||||
resolved "https://registry.yarnpkg.com/@strapi/parts/-/parts-0.0.1-alpha.4.tgz#ffeeb856d2e4b0b6f5ae84f28e071e73e1c3d07e"
|
||||
integrity sha512-1LBipdmZiCEdO6RC+YSsVEkHV3XxStTFxTKPz3bsorb5Rt52FchOB2CkHTjIK4+WmNvHdDJh1b98ZC0M0gJCEQ==
|
||||
dependencies:
|
||||
compute-scroll-into-view "^1.0.17"
|
||||
prop-types "^15.7.2"
|
||||
|
||||
"@stylelint/postcss-css-in-js@^0.37.2":
|
||||
version "0.37.2"
|
||||
resolved "https://registry.yarnpkg.com/@stylelint/postcss-css-in-js/-/postcss-css-in-js-0.37.2.tgz#7e5a84ad181f4234a2480803422a47b8749af3d2"
|
||||
@ -6853,6 +6866,11 @@ compression@^1.7.4:
|
||||
safe-buffer "5.1.2"
|
||||
vary "~1.1.2"
|
||||
|
||||
compute-scroll-into-view@^1.0.17:
|
||||
version "1.0.17"
|
||||
resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.17.tgz#6a88f18acd9d42e9cf4baa6bec7e0522607ab7ab"
|
||||
integrity sha512-j4dx+Fb0URmzbwwMUrhqWM2BEWHdFGx+qZ9qqASHRPqvTYdqvWnHg0H1hIbcyLnvgnoNAVMlwkepyqM3DaIFUg==
|
||||
|
||||
concat-map@0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user