mirror of
https://github.com/apache/superset.git
synced 2024-09-18 19:49:37 -04:00
style: Fancier menus, more SIP-34-ish (#10423)
* style: shinier menus
* fixing mouseover glitch
* unused import
* rm emotion-reset
* restoring default config values
* lint fixes ✨
* a bit more pizzaz to the underlines (max's idea), fading active background
* simplifying navtitle -> label
* RBNavDropdown -> ReactBootstrapNavDropdown
* nixing whitespace
* replacing !importants with better selector specificity
* moving Menu LESS into Emotion
* no more border!
* fixing border issue
* language picker, account dropdown now use new dropdown component
* nixing whitespace in comment
* nixing duplicate styling
* removing borders on FAB navbar
* explicit font coloring
* linting
This commit is contained in:
parent
fdfb7cdfd2
commit
4b40d44b5c
21
superset-frontend/images/icons/dropdown-arrow.svg
Normal file
21
superset-frontend/images/icons/dropdown-arrow.svg
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<!--
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
or more contributor license agreements. See the NOTICE file
|
||||||
|
distributed with this work for additional information
|
||||||
|
regarding copyright ownership. The ASF licenses this file
|
||||||
|
to you under the Apache License, Version 2.0 (the
|
||||||
|
"License"); you may not use this file except in compliance
|
||||||
|
with the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing,
|
||||||
|
software distributed under the License is distributed on an
|
||||||
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
-->
|
||||||
|
<svg width="6" height="4" viewBox="0 0 6 4" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.41421 0C0.523309 0 0.0771424 1.07714 0.707107 1.70711L2.29289 3.29289C2.68342 3.68342 3.31658 3.68342 3.70711 3.29289L5.29289 1.70711C5.92286 1.07714 5.47669 0 4.58579 0H1.41421Z" fill="currentColor"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
@ -30,6 +30,7 @@ import { ReactComponent as CloseIcon } from 'images/icons/close.svg';
|
|||||||
import { ReactComponent as CompassIcon } from 'images/icons/compass.svg';
|
import { ReactComponent as CompassIcon } from 'images/icons/compass.svg';
|
||||||
import { ReactComponent as DatasetPhysicalIcon } from 'images/icons/dataset_physical.svg';
|
import { ReactComponent as DatasetPhysicalIcon } from 'images/icons/dataset_physical.svg';
|
||||||
import { ReactComponent as DatasetVirtualIcon } from 'images/icons/dataset_virtual.svg';
|
import { ReactComponent as DatasetVirtualIcon } from 'images/icons/dataset_virtual.svg';
|
||||||
|
import { ReactComponent as DropdownArrowIcon } from 'images/icons/dropdown-arrow.svg';
|
||||||
import { ReactComponent as ErrorIcon } from 'images/icons/error.svg';
|
import { ReactComponent as ErrorIcon } from 'images/icons/error.svg';
|
||||||
import { ReactComponent as FavoriteSelectedIcon } from 'images/icons/favorite-selected.svg';
|
import { ReactComponent as FavoriteSelectedIcon } from 'images/icons/favorite-selected.svg';
|
||||||
import { ReactComponent as FavoriteUnselectedIcon } from 'images/icons/favorite-unselected.svg';
|
import { ReactComponent as FavoriteUnselectedIcon } from 'images/icons/favorite-unselected.svg';
|
||||||
@ -58,6 +59,7 @@ type IconName =
|
|||||||
| 'compass'
|
| 'compass'
|
||||||
| 'dataset-physical'
|
| 'dataset-physical'
|
||||||
| 'dataset-virtual'
|
| 'dataset-virtual'
|
||||||
|
| 'dropdown-arrow'
|
||||||
| 'error'
|
| 'error'
|
||||||
| 'favorite-selected'
|
| 'favorite-selected'
|
||||||
| 'favorite-unselected'
|
| 'favorite-unselected'
|
||||||
@ -88,6 +90,7 @@ export const iconsRegistry: Record<
|
|||||||
'favorite-selected': FavoriteSelectedIcon,
|
'favorite-selected': FavoriteSelectedIcon,
|
||||||
'favorite-unselected': FavoriteUnselectedIcon,
|
'favorite-unselected': FavoriteUnselectedIcon,
|
||||||
'list-view': ListViewIcon,
|
'list-view': ListViewIcon,
|
||||||
|
'dropdown-arrow': DropdownArrowIcon,
|
||||||
'sort-asc': SortAscIcon,
|
'sort-asc': SortAscIcon,
|
||||||
'sort-desc': SortDescIcon,
|
'sort-desc': SortDescIcon,
|
||||||
certified: CertifiedIcon,
|
certified: CertifiedIcon,
|
||||||
|
@ -17,7 +17,8 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { NavDropdown, MenuItem } from 'react-bootstrap';
|
import { MenuItem } from 'react-bootstrap';
|
||||||
|
import NavDropdown from 'src/components/NavDropdown';
|
||||||
|
|
||||||
export interface Languages {
|
export interface Languages {
|
||||||
[key: string]: {
|
[key: string]: {
|
||||||
|
@ -1,45 +0,0 @@
|
|||||||
/**
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
* or more contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
|
||||||
* to you under the Apache License, Version 2.0 (the
|
|
||||||
* "License"); you may not use this file except in compliance
|
|
||||||
* with the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing,
|
|
||||||
* software distributed under the License is distributed on an
|
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
* KIND, either express or implied. See the License for the
|
|
||||||
* specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*/
|
|
||||||
@import '../../../stylesheets/less/variables.less';
|
|
||||||
|
|
||||||
#main-menu {
|
|
||||||
.navbar .caret {
|
|
||||||
width: 1rem;
|
|
||||||
padding: 0 0 18px 0;
|
|
||||||
|
|
||||||
&:before {
|
|
||||||
position: relative;
|
|
||||||
top: -2px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-inverse {
|
|
||||||
border-bottom: 2px solid @gray-bg;
|
|
||||||
}
|
|
||||||
|
|
||||||
.version-info {
|
|
||||||
padding: 5px 20px;
|
|
||||||
color: @gray-heading;
|
|
||||||
font-size: @font-size-xs;
|
|
||||||
|
|
||||||
div {
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -24,7 +24,6 @@ import MenuObject, { MenuObjectProps } from './MenuObject';
|
|||||||
import NewMenu from './NewMenu';
|
import NewMenu from './NewMenu';
|
||||||
import UserMenu from './UserMenu';
|
import UserMenu from './UserMenu';
|
||||||
import LanguagePicker, { Languages } from './LanguagePicker';
|
import LanguagePicker, { Languages } from './LanguagePicker';
|
||||||
import './Menu.less';
|
|
||||||
|
|
||||||
interface BrandProps {
|
interface BrandProps {
|
||||||
path: string;
|
path: string;
|
||||||
@ -56,32 +55,65 @@ export interface MenuProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const StyledHeader = styled.header`
|
const StyledHeader = styled.header`
|
||||||
|
.caret {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-inverse {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-info {
|
||||||
|
padding: 5px 20px;
|
||||||
|
color: ${({ theme }) => theme.colors.grayscale.base};
|
||||||
|
font-size: ${({ theme }) => theme.typography.sizes.xs}px;
|
||||||
|
|
||||||
|
div {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.navbar-brand {
|
.navbar-brand {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nav > li > a {
|
||||||
|
padding: ${({ theme }) => theme.gridUnit * 4}px;
|
||||||
|
}
|
||||||
|
|
||||||
.navbar-nav > li > a {
|
.navbar-nav > li > a {
|
||||||
|
color: ${({ theme }) => theme.colors.grayscale.dark1};
|
||||||
|
border-bottom: none;
|
||||||
|
&:focus {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
&:after {
|
&:after {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: -3px;
|
bottom: -3px;
|
||||||
left: 0;
|
left: 50%;
|
||||||
width: 100%;
|
width: 0;
|
||||||
height: 3px;
|
height: 3px;
|
||||||
background-color: ${({ theme }) => theme.colors.primary.base};
|
background-color: ${({ theme }) => theme.colors.primary.base};
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity ${({ theme }) => theme.transitionTiming * 2}s;
|
transform: translateX(-50%);
|
||||||
|
transition: all ${({ theme }) => theme.transitionTiming}s;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
color: ${({ theme }) => theme.colors.grayscale.dark1};
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
|
|
||||||
&:after {
|
&:after {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -17,7 +17,8 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { NavItem, NavDropdown, MenuItem } from 'react-bootstrap';
|
import { NavItem, MenuItem } from 'react-bootstrap';
|
||||||
|
import NavDropdown from '../NavDropdown';
|
||||||
|
|
||||||
interface MenuObjectChildProps {
|
interface MenuObjectChildProps {
|
||||||
label: string;
|
label: string;
|
||||||
@ -44,23 +45,13 @@ export default function MenuObject({
|
|||||||
if (url) {
|
if (url) {
|
||||||
return (
|
return (
|
||||||
<NavItem eventKey={index} href={url}>
|
<NavItem eventKey={index} href={url}>
|
||||||
<i className={`fa ${icon}`} /> {label}
|
{label}
|
||||||
</NavItem>
|
</NavItem>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const navTitle = (
|
|
||||||
<>
|
|
||||||
<i className={`fa ${icon}`} />
|
|
||||||
{label}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<NavDropdown
|
<NavDropdown id={`menu-dropdown-${label}`} eventKey={index} title={label}>
|
||||||
id={`menu-dropdown-${label}`}
|
|
||||||
eventKey={index}
|
|
||||||
title={navTitle}
|
|
||||||
>
|
|
||||||
{childs?.map((child: MenuObjectChildProps | string, index1: number) => {
|
{childs?.map((child: MenuObjectChildProps | string, index1: number) => {
|
||||||
if (typeof child === 'string' && child === '-') {
|
if (typeof child === 'string' && child === '-') {
|
||||||
return <MenuItem key={`$${index1}`} divider />;
|
return <MenuItem key={`$${index1}`} divider />;
|
||||||
@ -71,7 +62,6 @@ export default function MenuObject({
|
|||||||
href={child.url}
|
href={child.url}
|
||||||
eventKey={parseFloat(`${index}.${index1}`)}
|
eventKey={parseFloat(`${index}.${index1}`)}
|
||||||
>
|
>
|
||||||
<i className={`fa ${child.icon}`} />
|
|
||||||
{child.label}
|
{child.label}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
);
|
);
|
||||||
|
@ -17,7 +17,8 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { NavDropdown, MenuItem } from 'react-bootstrap';
|
import { MenuItem } from 'react-bootstrap';
|
||||||
|
import NavDropdown from 'src/components/NavDropdown';
|
||||||
import { t } from '@superset-ui/translation';
|
import { t } from '@superset-ui/translation';
|
||||||
|
|
||||||
interface UserMenuProps {
|
interface UserMenuProps {
|
||||||
@ -42,14 +43,8 @@ export default function UserMenu({
|
|||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<MenuItem href={userInfoUrl}>
|
<MenuItem href={userInfoUrl}>{t('Profile')}</MenuItem>
|
||||||
<span className="fa fa-fw fa-user" />
|
<MenuItem href={userLogoutUrl}>{t('Logout')}</MenuItem>
|
||||||
{t('Profile')}
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem href={userLogoutUrl}>
|
|
||||||
<span className="fa fa-fw fa-sign-out" />
|
|
||||||
{t('Logout')}
|
|
||||||
</MenuItem>
|
|
||||||
{(versionString || versionSha) && (
|
{(versionString || versionSha) && (
|
||||||
<li className="version-info">
|
<li className="version-info">
|
||||||
{versionString && <div>Version: {versionString}</div>}
|
{versionString && <div>Version: {versionString}</div>}
|
||||||
|
72
superset-frontend/src/components/NavDropdown/index.tsx
Normal file
72
superset-frontend/src/components/NavDropdown/index.tsx
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import styled from '@superset-ui/style';
|
||||||
|
import { NavDropdown as ReactBootstrapNavDropdown } from 'react-bootstrap';
|
||||||
|
|
||||||
|
const NavDropdown = styled(ReactBootstrapNavDropdown)`
|
||||||
|
&.dropdown > a.dropdown-toggle {
|
||||||
|
padding-right: ${({ theme }) => theme.gridUnit * 6}px;
|
||||||
|
}
|
||||||
|
& > a {
|
||||||
|
transition: background-color ${({ theme }) => theme.transitionTiming}s;
|
||||||
|
}
|
||||||
|
&.dropdown.open > a.dropdown-toggle {
|
||||||
|
background: ${({ theme }) => theme.colors.primary.light4};
|
||||||
|
}
|
||||||
|
|
||||||
|
:after {
|
||||||
|
content: '';
|
||||||
|
height: ${({ theme }) => theme.gridUnit}px;
|
||||||
|
width: ${({ theme }) => theme.gridUnit * 2}px;
|
||||||
|
background: url('/static/assets/images/icons/dropdown-arrow.svg');
|
||||||
|
background-size: contain;
|
||||||
|
background-position: center center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
right: ${({ theme }) => theme.gridUnit * 2}px;
|
||||||
|
transition: opacity ${({ theme }) => theme.transitionTiming}s;
|
||||||
|
opacity: ${({ theme }) => theme.opacity.mediumLight};
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
&:hover,
|
||||||
|
&.active {
|
||||||
|
&:after {
|
||||||
|
opacity: ${({ theme }) => theme.opacity.mediumHeavy};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.dropdown-menu {
|
||||||
|
padding: ${({ theme }) => theme.gridUnit}px 0;
|
||||||
|
top: 100%;
|
||||||
|
border: none;
|
||||||
|
& li a {
|
||||||
|
padding: ${({ theme }) => theme.gridUnit}px
|
||||||
|
${({ theme }) => theme.gridUnit * 4}px;
|
||||||
|
transition: all ${({ theme }) => theme.transitionTiming}s;
|
||||||
|
&:hover {
|
||||||
|
background: ${({ theme }) => theme.colors.primary.light4};
|
||||||
|
color: ${({ theme }) => theme.colors.grayscale.dark1};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default NavDropdown;
|
@ -43,7 +43,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.navbar-inverse {
|
.navbar-inverse {
|
||||||
border: 3px solid @navbar-inverse-bg;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-inverse .navbar-nav > li > a:hover,
|
.navbar-inverse .navbar-nav > li > a:hover,
|
||||||
|
Loading…
Reference in New Issue
Block a user