chore: Migrate .less styles to Emotion (#22474)
This commit is contained in:
parent
5026da50e1
commit
39c96d0568
|
|
@ -127,10 +127,11 @@ export const databasesPage = {
|
|||
|
||||
export const sqlLabView = {
|
||||
sqlEditorLeftBar: {
|
||||
sqlEditorLeftBar: '[class="SqlEditorLeftBar"]',
|
||||
databaseSchemaTableSection: '[class="SqlEditorLeftBar"] > :nth-child(1)',
|
||||
sqlEditorLeftBar: '[data-test="sql-editor-left-bar"]',
|
||||
databaseSchemaTableSection:
|
||||
'[data-test="sql-editor-left-bar"] > :nth-child(1)',
|
||||
tableSchemaSection:
|
||||
'[class="SqlEditorLeftBar"] > :nth-child(1) > :nth-child(3) > :nth-child(1)',
|
||||
'[data-test="sql-editor-left-bar"] > :nth-child(1) > :nth-child(3) > :nth-child(1)',
|
||||
tableSchemaInputEmpty: '[aria-label="Select table or type table name"]',
|
||||
},
|
||||
databaseInput: '[data-test=DatabaseSelector] > :nth-child(1)',
|
||||
|
|
|
|||
|
|
@ -42,9 +42,9 @@ import {
|
|||
import { BYTES_PER_CHAR, KB_STORAGE } from './constants';
|
||||
import setupApp from '../setup/setupApp';
|
||||
|
||||
import './main.less';
|
||||
import '../assets/stylesheets/reactable-pagination.less';
|
||||
import { theme } from '../preamble';
|
||||
import { SqlLabGlobalStyles } from './SqlLabGlobalStyles';
|
||||
|
||||
setupApp();
|
||||
setupExtensions();
|
||||
|
|
@ -141,6 +141,7 @@ const Application = () => (
|
|||
<Provider store={store}>
|
||||
<ThemeProvider theme={theme}>
|
||||
<GlobalStyles />
|
||||
<SqlLabGlobalStyles />
|
||||
<App />
|
||||
</ThemeProvider>
|
||||
</Provider>
|
||||
|
|
|
|||
|
|
@ -16,13 +16,21 @@
|
|||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
@import '../../assets/stylesheets/less/variables.less';
|
||||
|
||||
@import './builder.less';
|
||||
@import './dashboard.less';
|
||||
@import './dnd.less';
|
||||
@import './filter-scope-selector.less';
|
||||
@import './grid.less';
|
||||
@import './popover-menu.less';
|
||||
@import './resizable.less';
|
||||
@import './components/index.less';
|
||||
import React from 'react';
|
||||
import { Global } from '@emotion/react';
|
||||
import { css } from '@superset-ui/core';
|
||||
|
||||
export const SqlLabGlobalStyles = () => (
|
||||
<Global
|
||||
styles={theme => css`
|
||||
body {
|
||||
min-height: max(
|
||||
100vh,
|
||||
${theme.gridUnit * 125}px
|
||||
); // Set a min height so the gutter is always visible when resizing
|
||||
overflow: hidden;
|
||||
}
|
||||
`}
|
||||
/>
|
||||
);
|
||||
|
|
@ -18,6 +18,8 @@
|
|||
*/
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { css, styled } from '@superset-ui/core';
|
||||
|
||||
import { usePrevious } from 'src/hooks/usePrevious';
|
||||
import { areArraysShallowEqual } from 'src/reduxUtils';
|
||||
import sqlKeywords from 'src/SqlLab/utils/sqlKeywords';
|
||||
|
|
@ -57,6 +59,28 @@ type AceEditorWrapperProps = {
|
|||
hotkeys: HotKey[];
|
||||
};
|
||||
|
||||
const StyledAceEditor = styled(AceEditor)`
|
||||
${({ theme }) => css`
|
||||
&& {
|
||||
// double class is better than !important
|
||||
border: 1px solid ${theme.colors.grayscale.light2};
|
||||
font-feature-settings: 'liga' off, 'calt' off;
|
||||
// Fira Code causes problem with Ace under Firefox
|
||||
font-family: 'Menlo', 'Consolas', 'Courier New', 'Ubuntu Mono',
|
||||
'source-code-pro', 'Lucida Console', monospace;
|
||||
|
||||
&.ace_autocomplete {
|
||||
// Use !important because Ace Editor applies extra CSS at the last second
|
||||
// when opening the autocomplete.
|
||||
width: ${theme.gridUnit * 130}px !important;
|
||||
}
|
||||
|
||||
.ace_scroller {
|
||||
background-color: ${theme.colors.grayscale.light4};
|
||||
}
|
||||
}
|
||||
`}
|
||||
`;
|
||||
const AceEditorWrapper = ({
|
||||
autocomplete,
|
||||
onBlur = () => {},
|
||||
|
|
@ -258,7 +282,7 @@ const AceEditorWrapper = ({
|
|||
};
|
||||
|
||||
return (
|
||||
<AceEditor
|
||||
<StyledAceEditor
|
||||
keywords={words}
|
||||
onLoad={onEditorLoad}
|
||||
onBlur={onBlurSql}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import React from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import { t } from '@superset-ui/core';
|
||||
import { css, styled, t } from '@superset-ui/core';
|
||||
import throttle from 'lodash/throttle';
|
||||
import ToastContainer from 'src/components/MessageToasts/ToastContainer';
|
||||
import {
|
||||
|
|
@ -32,6 +32,69 @@ import * as Actions from 'src/SqlLab/actions/sqlLab';
|
|||
import TabbedSqlEditors from '../TabbedSqlEditors';
|
||||
import QueryAutoRefresh from '../QueryAutoRefresh';
|
||||
|
||||
const SqlLabStyles = styled.div`
|
||||
${({ theme }) => css`
|
||||
&.SqlLab {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
padding: 0 ${theme.gridUnit * 2}px;
|
||||
|
||||
pre {
|
||||
padding: 0 !important;
|
||||
margin: 0;
|
||||
border: none;
|
||||
font-size: ${theme.typography.sizes.s}px;
|
||||
background: transparent !important;
|
||||
}
|
||||
|
||||
.north-pane {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.ace_editor {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.ace_content {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.ant-tabs-content-holder {
|
||||
/* This is needed for Safari */
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.ant-tabs-content {
|
||||
height: 100%;
|
||||
position: relative;
|
||||
background-color: ${theme.colors.grayscale.light5};
|
||||
overflow-x: auto;
|
||||
overflow-y: auto;
|
||||
|
||||
> .ant-tabs-tabpane {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ResultsModal .ant-modal-body {
|
||||
min-height: ${theme.gridUnit * 140}px;
|
||||
}
|
||||
|
||||
.ant-modal-body {
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
`};
|
||||
`;
|
||||
|
||||
class App extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
|
@ -99,7 +162,7 @@ class App extends React.PureComponent {
|
|||
return window.location.replace('/superset/sqllab/history/');
|
||||
}
|
||||
return (
|
||||
<div className="App SqlLab">
|
||||
<SqlLabStyles className="App SqlLab">
|
||||
<QueryAutoRefresh
|
||||
queries={queries}
|
||||
refreshQueries={actions?.refreshQueries}
|
||||
|
|
@ -107,7 +170,7 @@ class App extends React.PureComponent {
|
|||
/>
|
||||
<TabbedSqlEditors />
|
||||
<ToastContainer />
|
||||
</div>
|
||||
</SqlLabStyles>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
import React, { useMemo } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { t } from '@superset-ui/core';
|
||||
import { css, styled, t } from '@superset-ui/core';
|
||||
|
||||
import Alert from 'src/components/Alert';
|
||||
import TableView from 'src/components/TableView';
|
||||
|
|
@ -36,6 +36,12 @@ export interface EstimateQueryCostButtonProps {
|
|||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const CostEstimateModalStyles = styled.div`
|
||||
${({ theme }) => css`
|
||||
font-size: ${theme.typography.sizes.s};
|
||||
`}
|
||||
`;
|
||||
|
||||
const EstimateQueryCostButton = ({
|
||||
getEstimate,
|
||||
queryEditorId,
|
||||
|
|
@ -76,13 +82,14 @@ const EstimateQueryCostButton = ({
|
|||
}
|
||||
if (queryCostEstimate?.completed) {
|
||||
return (
|
||||
<TableView
|
||||
columns={columns}
|
||||
data={tableData}
|
||||
withPagination={false}
|
||||
emptyWrapperType={EmptyWrapperType.Small}
|
||||
className="cost-estimate"
|
||||
/>
|
||||
<CostEstimateModalStyles>
|
||||
<TableView
|
||||
columns={columns}
|
||||
data={tableData}
|
||||
withPagination={false}
|
||||
emptyWrapperType={EmptyWrapperType.Small}
|
||||
/>
|
||||
</CostEstimateModalStyles>
|
||||
);
|
||||
}
|
||||
return <Loading position="normal" />;
|
||||
|
|
|
|||
|
|
@ -17,8 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import { styledMount as mount } from 'spec/helpers/theming';
|
||||
import Label from 'src/components/Label';
|
||||
import QueryStateLabel from 'src/SqlLab/components/QueryStateLabel';
|
||||
|
||||
|
|
@ -34,7 +33,7 @@ describe('SavedQuery', () => {
|
|||
);
|
||||
});
|
||||
it('has an Overlay and a Popover', () => {
|
||||
const wrapper = shallow(<QueryStateLabel {...mockedProps} />);
|
||||
const wrapper = mount(<QueryStateLabel {...mockedProps} />);
|
||||
expect(wrapper.find(Label)).toExist();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -19,16 +19,18 @@
|
|||
import React from 'react';
|
||||
import Label from 'src/components/Label';
|
||||
import { STATE_TYPE_MAP } from 'src/SqlLab/constants';
|
||||
import { Query } from '@superset-ui/core';
|
||||
import { styled, Query } from '@superset-ui/core';
|
||||
|
||||
interface QueryStateLabelProps {
|
||||
query: Query;
|
||||
}
|
||||
|
||||
const StyledLabel = styled(Label)`
|
||||
margin-right: ${({ theme }) => theme.gridUnit}px;
|
||||
`;
|
||||
|
||||
export default function QueryStateLabel({ query }: QueryStateLabelProps) {
|
||||
return (
|
||||
<Label className="m-r-3" type={STATE_TYPE_MAP[query.state]}>
|
||||
{query.state}
|
||||
</Label>
|
||||
<StyledLabel type={STATE_TYPE_MAP[query.state]}>{query.state}</StyledLabel>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,13 @@ import ButtonGroup from 'src/components/ButtonGroup';
|
|||
import Alert from 'src/components/Alert';
|
||||
import Button from 'src/components/Button';
|
||||
import shortid from 'shortid';
|
||||
import { QueryResponse, QueryState, styled, t } from '@superset-ui/core';
|
||||
import {
|
||||
QueryResponse,
|
||||
QueryState,
|
||||
styled,
|
||||
t,
|
||||
useTheme,
|
||||
} from '@superset-ui/core';
|
||||
import { usePrevious } from 'src/hooks/usePrevious';
|
||||
import ErrorMessageWithStackTrace from 'src/components/ErrorMessage/ErrorMessageWithStackTrace';
|
||||
import {
|
||||
|
|
@ -133,6 +139,7 @@ const ResultSet = ({
|
|||
user,
|
||||
defaultQueryLimit,
|
||||
}: ResultSetProps) => {
|
||||
const theme = useTheme();
|
||||
const [searchText, setSearchText] = useState('');
|
||||
const [cachedData, setCachedData] = useState<Record<string, unknown>[]>([]);
|
||||
const [showSaveDatasetModal, setShowSaveDatasetModal] = useState(false);
|
||||
|
|
@ -449,7 +456,7 @@ const ResultSet = ({
|
|||
<ButtonGroup>
|
||||
<Button
|
||||
buttonSize="small"
|
||||
className="m-r-5"
|
||||
css={{ marginRight: theme.gridUnit }}
|
||||
onClick={() => popSelectStar(tempSchema, tempTable)}
|
||||
>
|
||||
{t('Query in a new tab')}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import { CSSTransition } from 'react-transition-group';
|
|||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import Split from 'react-split';
|
||||
import { t, styled, useTheme } from '@superset-ui/core';
|
||||
import { css, t, styled, useTheme } from '@superset-ui/core';
|
||||
import debounce from 'lodash/debounce';
|
||||
import throttle from 'lodash/throttle';
|
||||
import Modal from 'src/components/Modal';
|
||||
|
|
@ -132,6 +132,62 @@ const StyledSidebar = styled.div`
|
|||
hide ? 'transparent' : theme.colors.grayscale.light2};
|
||||
`;
|
||||
|
||||
const StyledSqlEditor = styled.div`
|
||||
${({ theme }) => css`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 100%;
|
||||
|
||||
.schemaPane {
|
||||
transition: transform ${theme.transitionTiming}s ease-in-out;
|
||||
}
|
||||
|
||||
.queryPane {
|
||||
flex: 1 1 auto;
|
||||
padding: ${theme.gridUnit * 2}px;
|
||||
overflow-y: auto;
|
||||
overflow-x: scroll;
|
||||
}
|
||||
|
||||
.schemaPane-enter-done,
|
||||
.schemaPane-exit {
|
||||
transform: translateX(0);
|
||||
z-index: 7;
|
||||
}
|
||||
|
||||
.schemaPane-exit-active {
|
||||
transform: translateX(-120%);
|
||||
}
|
||||
|
||||
.schemaPane-enter-active {
|
||||
transform: translateX(0);
|
||||
max-width: ${theme.gridUnit * 75}px;
|
||||
}
|
||||
|
||||
.schemaPane-enter,
|
||||
.schemaPane-exit-done {
|
||||
max-width: 0;
|
||||
transform: translateX(-120%);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.schemaPane-exit-done + .queryPane {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.gutter {
|
||||
border-top: 1px solid ${theme.colors.grayscale.light2};
|
||||
border-bottom: 1px solid ${theme.colors.grayscale.light2};
|
||||
width: 3%;
|
||||
margin: ${theme.gridUnit}px 47%;
|
||||
}
|
||||
|
||||
.gutter.gutter-vertical {
|
||||
cursor: row-resize;
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
const propTypes = {
|
||||
tables: PropTypes.array.isRequired,
|
||||
queryEditor: PropTypes.object.isRequired,
|
||||
|
|
@ -636,7 +692,7 @@ const SqlEditor = ({
|
|||
? 'schemaPane-exit-done'
|
||||
: 'schemaPane-enter-done';
|
||||
return (
|
||||
<div ref={sqlEditorRef} className="SqlEditor">
|
||||
<StyledSqlEditor ref={sqlEditorRef} className="SqlEditor">
|
||||
<CSSTransition classNames="schemaPane" in={!hideLeftBar} timeout={300}>
|
||||
<ResizableSidebar
|
||||
id={`sqllab:${queryEditor.id}`}
|
||||
|
|
@ -704,7 +760,7 @@ const SqlEditor = ({
|
|||
<span>{t('Name')}</span>
|
||||
<Input placeholder={createModalPlaceHolder} onChange={ctasChanged} />
|
||||
</Modal>
|
||||
</div>
|
||||
</StyledSqlEditor>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -89,12 +89,25 @@ const collapseStyles = (theme: SupersetTheme) => css`
|
|||
.ant-collapse-arrow {
|
||||
top: ${theme.gridUnit * 2}px !important;
|
||||
color: ${theme.colors.primary.dark1} !important;
|
||||
&: hover {
|
||||
&:hover {
|
||||
color: ${theme.colors.primary.dark2} !important;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const LeftBarStyles = styled.div`
|
||||
${({ theme }) => css`
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.divider {
|
||||
border-bottom: 1px solid ${theme.colors.grayscale.light4};
|
||||
margin: ${theme.gridUnit * 4}px 0;
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
const SqlEditorLeftBar = ({
|
||||
database,
|
||||
queryEditorId,
|
||||
|
|
@ -228,7 +241,7 @@ const SqlEditorLeftBar = ({
|
|||
}, []);
|
||||
|
||||
return (
|
||||
<div data-test="sql-editor-left-bar" className="SqlEditorLeftBar">
|
||||
<LeftBarStyles data-test="sql-editor-left-bar">
|
||||
<TableSelectorMultiple
|
||||
onEmptyResults={onEmptyResults}
|
||||
emptyState={emptyStateComponent(emptyResultsWithSearch)}
|
||||
|
|
@ -276,7 +289,7 @@ const SqlEditorLeftBar = ({
|
|||
<i className="fa fa-bomb" /> {t('Reset state')}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</LeftBarStyles>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,12 @@ const TabTitle = styled.span`
|
|||
text-transform: none;
|
||||
`;
|
||||
|
||||
const IconContainer = styled.div`
|
||||
display: inline-block;
|
||||
width: ${({ theme }) => theme.gridUnit * 8}px;
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
queryEditor: QueryEditor;
|
||||
}
|
||||
|
|
@ -91,9 +97,9 @@ const SqlEditorTabHeader: React.FC<Props> = ({ queryEditor }) => {
|
|||
onClick={() => actions.removeQueryEditor(qe)}
|
||||
data-test="close-tab-menu-option"
|
||||
>
|
||||
<div className="icon-container">
|
||||
<IconContainer>
|
||||
<i className="fa fa-close" />
|
||||
</div>
|
||||
</IconContainer>
|
||||
{t('Close tab')}
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
|
|
@ -101,9 +107,9 @@ const SqlEditorTabHeader: React.FC<Props> = ({ queryEditor }) => {
|
|||
onClick={renameTab}
|
||||
data-test="rename-tab-menu-option"
|
||||
>
|
||||
<div className="icon-container">
|
||||
<IconContainer>
|
||||
<i className="fa fa-i-cursor" />
|
||||
</div>
|
||||
</IconContainer>
|
||||
{t('Rename tab')}
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
|
|
@ -111,9 +117,9 @@ const SqlEditorTabHeader: React.FC<Props> = ({ queryEditor }) => {
|
|||
onClick={() => actions.toggleLeftBar(qe)}
|
||||
data-test="toggle-menu-option"
|
||||
>
|
||||
<div className="icon-container">
|
||||
<IconContainer>
|
||||
<i className="fa fa-cogs" />
|
||||
</div>
|
||||
</IconContainer>
|
||||
{qe.hideLeftBar ? t('Expand tool bar') : t('Hide tool bar')}
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
|
|
@ -121,9 +127,9 @@ const SqlEditorTabHeader: React.FC<Props> = ({ queryEditor }) => {
|
|||
onClick={() => actions.removeAllOtherQueryEditors(qe)}
|
||||
data-test="close-all-other-menu-option"
|
||||
>
|
||||
<div className="icon-container">
|
||||
<IconContainer>
|
||||
<i className="fa fa-times-circle-o" />
|
||||
</div>
|
||||
</IconContainer>
|
||||
{t('Close all other tabs')}
|
||||
</Menu.Item>
|
||||
<Menu.Item
|
||||
|
|
@ -131,9 +137,9 @@ const SqlEditorTabHeader: React.FC<Props> = ({ queryEditor }) => {
|
|||
onClick={() => actions.cloneQueryToNewTab(qe, false)}
|
||||
data-test="clone-tab-menu-option"
|
||||
>
|
||||
<div className="icon-container">
|
||||
<IconContainer>
|
||||
<i className="fa fa-files-o" />
|
||||
</div>
|
||||
</IconContainer>
|
||||
{t('Duplicate tab')}
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
|
|
|
|||
|
|
@ -17,13 +17,42 @@
|
|||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { QueryState, styled } from '@superset-ui/core';
|
||||
import { css, QueryState, styled } from '@superset-ui/core';
|
||||
import Icons, { IconType } from 'src/components/Icons';
|
||||
|
||||
const IconContainer = styled.span`
|
||||
position: absolute;
|
||||
top: -7px;
|
||||
left: 0px;
|
||||
top: -6px;
|
||||
left: 1px;
|
||||
`;
|
||||
|
||||
const Circle = styled.div`
|
||||
${({ theme }) => css`
|
||||
border-radius: 50%;
|
||||
width: ${theme.gridUnit * 3}px;
|
||||
height: ${theme.gridUnit * 3}px;
|
||||
|
||||
display: inline-block;
|
||||
background-color: ${theme.colors.grayscale.light2};
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
font-size: ${theme.typography.sizes.m}px;
|
||||
font-weight: ${theme.typography.weights.bold};
|
||||
color: ${theme.colors.grayscale.light5};
|
||||
position: relative;
|
||||
|
||||
&.running {
|
||||
background-color: ${theme.colors.info.base};
|
||||
}
|
||||
|
||||
&.success {
|
||||
background-color: ${theme.colors.success.base};
|
||||
}
|
||||
|
||||
&.failed {
|
||||
background-color: ${theme.colors.error.base};
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
interface TabStatusIconProps {
|
||||
|
|
@ -38,12 +67,12 @@ const STATE_ICONS: Record<string, React.FC<IconType>> = {
|
|||
export default function TabStatusIcon({ tabState }: TabStatusIconProps) {
|
||||
const StatusIcon = STATE_ICONS[tabState];
|
||||
return (
|
||||
<div className={`circle ${tabState}`}>
|
||||
<Circle className={`circle ${tabState}`}>
|
||||
{StatusIcon && (
|
||||
<IconContainer>
|
||||
<StatusIcon iconSize="xs" />
|
||||
</IconContainer>
|
||||
)}
|
||||
</div>
|
||||
</Circle>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -212,9 +212,9 @@ describe('TabbedSqlEditors', () => {
|
|||
});
|
||||
it('should disable new tab when offline', () => {
|
||||
wrapper = getWrapper();
|
||||
expect(wrapper.find(EditableTabs).props().hideAdd).toBe(false);
|
||||
expect(wrapper.find('#a11y-query-editor-tabs').props().hideAdd).toBe(false);
|
||||
wrapper.setProps({ offline: true });
|
||||
expect(wrapper.find(EditableTabs).props().hideAdd).toBe(true);
|
||||
expect(wrapper.find('#a11y-query-editor-tabs').props().hideAdd).toBe(true);
|
||||
});
|
||||
it('should have an empty state when query editors is empty', () => {
|
||||
wrapper = getWrapper();
|
||||
|
|
|
|||
|
|
@ -54,6 +54,12 @@ const defaultProps = {
|
|||
scheduleQueryWarning: null,
|
||||
};
|
||||
|
||||
const StyledEditableTabs = styled(EditableTabs)`
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
`;
|
||||
|
||||
const StyledTab = styled.span`
|
||||
line-height: 24px;
|
||||
`;
|
||||
|
|
@ -303,7 +309,7 @@ class TabbedSqlEditors extends React.PureComponent {
|
|||
);
|
||||
|
||||
return (
|
||||
<EditableTabs
|
||||
<StyledEditableTabs
|
||||
destroyInactiveTabPane
|
||||
activeKey={this.props.tabHistory[this.props.tabHistory.length - 1]}
|
||||
id="a11y-query-editor-tabs"
|
||||
|
|
@ -331,7 +337,7 @@ class TabbedSqlEditors extends React.PureComponent {
|
|||
>
|
||||
{editors}
|
||||
{noQueryEditors && emptyTabState}
|
||||
</EditableTabs>
|
||||
</StyledEditableTabs>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import { useDispatch } from 'react-redux';
|
|||
import Collapse from 'src/components/Collapse';
|
||||
import Card from 'src/components/Card';
|
||||
import ButtonGroup from 'src/components/ButtonGroup';
|
||||
import { t, styled } from '@superset-ui/core';
|
||||
import { css, t, styled } from '@superset-ui/core';
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
import { removeDataPreview, removeTables } from 'src/SqlLab/actions/sqlLab';
|
||||
|
|
@ -61,7 +61,7 @@ export interface TableElementProps {
|
|||
|
||||
const StyledSpan = styled.span`
|
||||
color: ${({ theme }) => theme.colors.primary.dark1};
|
||||
&: hover {
|
||||
&:hover {
|
||||
color: ${({ theme }) => theme.colors.primary.dark2};
|
||||
}
|
||||
cursor: pointer;
|
||||
|
|
@ -72,6 +72,39 @@ const Fade = styled.div`
|
|||
opacity: ${(props: { hovered: boolean }) => (props.hovered ? 1 : 0)};
|
||||
`;
|
||||
|
||||
const StyledCollapsePanel = styled(Collapse.Panel)`
|
||||
${({ theme }) => css`
|
||||
& {
|
||||
.ws-el-controls {
|
||||
margin-right: ${-theme.gridUnit}px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.header-container {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
|
||||
.table-name {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-size: ${theme.typography.sizes.l}px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.header-right-side {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: ${theme.gridUnit * 8}px;
|
||||
}
|
||||
}
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
const TableElement = ({ table, ...props }: TableElementProps) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
|
|
@ -287,7 +320,7 @@ const TableElement = ({ table, ...props }: TableElementProps) => {
|
|||
};
|
||||
|
||||
return (
|
||||
<Collapse.Panel
|
||||
<StyledCollapsePanel
|
||||
{...props}
|
||||
key={table.id}
|
||||
header={renderHeader()}
|
||||
|
|
@ -295,7 +328,7 @@ const TableElement = ({ table, ...props }: TableElementProps) => {
|
|||
forceRender
|
||||
>
|
||||
{renderBody()}
|
||||
</Collapse.Panel>
|
||||
</StyledCollapsePanel>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,491 +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 '../assets/stylesheets/less/variables.less';
|
||||
|
||||
body {
|
||||
min-height: ~'max(100vh, 500px)'; // Set a min height so the gutter is always visible when resizing
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.inlineBlock {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.valignTop {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.inline {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.nopadding {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.pane-cell {
|
||||
padding: 10px;
|
||||
overflow: auto;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.ant-tabs-content-holder {
|
||||
/* This is needed for Safari */
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.ant-tabs-content {
|
||||
height: 100%;
|
||||
position: relative;
|
||||
background-color: @lightest;
|
||||
overflow-x: auto;
|
||||
overflow-y: auto;
|
||||
|
||||
> .ant-tabs-tabpane {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.Workspace .btn-sm {
|
||||
box-shadow: 1px 1px 2px fade(@darkest, @opacity-light);
|
||||
margin-top: 2px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.Workspace hr {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
div.Workspace {
|
||||
height: 100%;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.padded {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.p-t-10 {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.p-t-5 {
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
.m-r-5 {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.m-r-3 {
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
.m-l-1 {
|
||||
margin-left: 1px;
|
||||
}
|
||||
|
||||
.m-l-2 {
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.m-r-10 {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.m-l-10 {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.m-l-5 {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.m-b-10 {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.m-t-5 {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.m-t-10 {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.p-t-10 {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.no-shadow {
|
||||
box-shadow: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.pane-west {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.circle {
|
||||
@circle-diameter: 10px;
|
||||
border-radius: (@circle-diameter / 2);
|
||||
width: @circle-diameter;
|
||||
height: @circle-diameter;
|
||||
|
||||
display: inline-block;
|
||||
background-color: @gray-light;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
font-size: @font-size-m;
|
||||
font-weight: @font-weight-bold;
|
||||
color: @lightest;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.running {
|
||||
background-color: @info;
|
||||
}
|
||||
|
||||
.success {
|
||||
background-color: @success;
|
||||
}
|
||||
|
||||
.failed {
|
||||
background-color: @danger;
|
||||
}
|
||||
|
||||
.handle {
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
#a11y-query-editor-tabs {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.SqlLab {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
padding: 0 10px;
|
||||
|
||||
pre {
|
||||
padding: 0px !important;
|
||||
margin: 0px;
|
||||
border: none;
|
||||
font-size: @font-size-s;
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.north-pane {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#ace-editor {
|
||||
height: calc(100% - 51px);
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.ace_content {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.SqlEditorTabs li {
|
||||
a:focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.ddbtn-tab {
|
||||
font-size: inherit;
|
||||
color: black;
|
||||
|
||||
&:active {
|
||||
background: none;
|
||||
}
|
||||
|
||||
svg {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown.btn-group.btn-group-sm {
|
||||
width: 3px;
|
||||
height: 3px;
|
||||
border-radius: 1.5px;
|
||||
background: #bababa;
|
||||
margin-right: 8px;
|
||||
font-weight: @font-weight-normal;
|
||||
display: inline-flex;
|
||||
|
||||
&:hover {
|
||||
background-color: @primary-color;
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
background-color: @primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
position: absolute;
|
||||
content: ' ';
|
||||
width: 3px;
|
||||
height: 3px;
|
||||
border-radius: 1.5px;
|
||||
background-color: #bababa;
|
||||
}
|
||||
&:before {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
&:after {
|
||||
transform: translateY(5px);
|
||||
}
|
||||
}
|
||||
|
||||
ul.dropdown-menu {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.dropdown-toggle {
|
||||
padding-top: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.SqlEditor {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 100%;
|
||||
|
||||
.schemaPane {
|
||||
transition: transform @timing-normal ease-in-out;
|
||||
}
|
||||
|
||||
.queryPane {
|
||||
flex: 1 1 auto;
|
||||
padding: 10px;
|
||||
overflow-y: none;
|
||||
overflow-x: scroll;
|
||||
}
|
||||
|
||||
.schemaPane-enter-done,
|
||||
.schemaPane-exit {
|
||||
transform: translateX(0);
|
||||
z-index: 7;
|
||||
}
|
||||
|
||||
.schemaPane-exit-active {
|
||||
transform: translateX(-120%);
|
||||
}
|
||||
|
||||
.schemaPane-enter-active {
|
||||
transform: translateX(0);
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.schemaPane-enter,
|
||||
.schemaPane-exit-done {
|
||||
max-width: 0;
|
||||
transform: translateX(-120%);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.schemaPane-exit-done + .queryPane {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.gutter {
|
||||
border-top: 1px solid @gray-light;
|
||||
border-bottom: 1px solid @gray-light;
|
||||
width: 3%;
|
||||
margin: 3px 47%;
|
||||
}
|
||||
|
||||
.gutter.gutter-vertical {
|
||||
cursor: row-resize;
|
||||
}
|
||||
}
|
||||
|
||||
.SqlEditorLeftBar {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.divider {
|
||||
border-bottom: 1px solid @gray-bg;
|
||||
margin: 15px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.popover {
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.table-label {
|
||||
margin-top: 5px;
|
||||
margin-right: 10px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
div.tablePopover {
|
||||
opacity: 0.7 !important;
|
||||
|
||||
&:hover {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.ace_editor.ace_editor {
|
||||
//double class is better than !important
|
||||
border: 1px solid @gray-light;
|
||||
font-feature-settings: @font-feature-settings;
|
||||
// Fira Code causes problem with Ace under Firefox
|
||||
font-family: 'Menlo', 'Consolas', 'Courier New', 'Ubuntu Mono',
|
||||
'source-code-pro', 'Lucida Console', monospace;
|
||||
|
||||
&.ace_autocomplete {
|
||||
// Use !important because Ace Editor applies extra CSS at the last second
|
||||
// when opening the autocomplete.
|
||||
width: 520px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.Select__menu-outer {
|
||||
min-width: 100%;
|
||||
width: inherit;
|
||||
z-index: @z-index-dropdown;
|
||||
}
|
||||
|
||||
.Select__clear-indicator {
|
||||
margin-top: -2px;
|
||||
}
|
||||
|
||||
.Select__arrow {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.ace_scroller {
|
||||
background-color: @gray-bg;
|
||||
}
|
||||
|
||||
.TableElement {
|
||||
.well {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
.ws-el-controls {
|
||||
margin-right: -0.3em;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.header-container {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
|
||||
.table-name {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 16px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.header-right-side {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 33px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.QueryTable .label {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.QueryTable .ant-btn {
|
||||
position: static;
|
||||
}
|
||||
|
||||
.ResultsModal .ant-modal-body {
|
||||
min-height: 560px;
|
||||
}
|
||||
|
||||
.ant-modal-body {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
a.Link {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.QueryTable .well {
|
||||
padding: 3px 5px;
|
||||
margin: 3px 5px;
|
||||
}
|
||||
|
||||
.nav-tabs .ddbtn-tab {
|
||||
padding: 0;
|
||||
border: none;
|
||||
background: none;
|
||||
position: relative;
|
||||
top: 2px;
|
||||
|
||||
&:focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
&:active {
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-container {
|
||||
display: inline-block;
|
||||
width: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.search-date-filter-container {
|
||||
display: flex;
|
||||
|
||||
.Select {
|
||||
margin-right: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
.cost-estimate {
|
||||
font-size: @font-size-s;
|
||||
}
|
||||
|
|
@ -42,10 +42,6 @@ input.form-control {
|
|||
background-color: @lightest;
|
||||
}
|
||||
|
||||
.chart-header a.danger {
|
||||
color: @danger;
|
||||
}
|
||||
|
||||
.disabledButton {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
|
@ -165,16 +161,6 @@ img.viz-thumb-option {
|
|||
max-height: 700px;
|
||||
}
|
||||
|
||||
.chart-header .header-text {
|
||||
font-size: @font-size-xl;
|
||||
line-height: 22px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid @gray;
|
||||
margin-top: 10px;
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
#is_cached {
|
||||
display: none;
|
||||
}
|
||||
|
|
@ -327,6 +313,10 @@ table.table-no-hover tr:hover {
|
|||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.m-l-2 {
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.m-l-4 {
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -113,6 +113,10 @@ const Styles = styled.div`
|
|||
.pivot_table tbody tr {
|
||||
font-feature-settings: 'tnum' 1;
|
||||
}
|
||||
|
||||
.alert {
|
||||
margin: ${({ theme }) => theme.gridUnit * 2}px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ import {
|
|||
} from '../../logger/LogUtils';
|
||||
import { areObjectsEqual } from '../../reduxUtils';
|
||||
|
||||
import '../stylesheets/index.less';
|
||||
import getLocationHash from '../util/getLocationHash';
|
||||
import isDashboardEmpty from '../util/isDashboardEmpty';
|
||||
import { getAffectedOwnDataCharts } from '../util/charts/getOwnDataCharts';
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ describe('DashboardBuilder', () => {
|
|||
|
||||
it('should render a StickyContainer with class "dashboard"', () => {
|
||||
const { getByTestId } = setup();
|
||||
const stickyContainer = getByTestId('dashboard-content');
|
||||
const stickyContainer = getByTestId('dashboard-content-wrapper');
|
||||
expect(stickyContainer).toHaveClass('dashboard');
|
||||
});
|
||||
|
||||
|
|
@ -133,7 +133,7 @@ describe('DashboardBuilder', () => {
|
|||
const { getByTestId } = setup({
|
||||
dashboardState: { ...mockState.dashboardState, editMode: true },
|
||||
});
|
||||
const stickyContainer = getByTestId('dashboard-content');
|
||||
const stickyContainer = getByTestId('dashboard-content-wrapper');
|
||||
expect(stickyContainer).toHaveClass('dashboard dashboard--editing');
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,14 @@ import React, {
|
|||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { css, JsonObject, styled, t } from '@superset-ui/core';
|
||||
import {
|
||||
addAlpha,
|
||||
css,
|
||||
JsonObject,
|
||||
styled,
|
||||
t,
|
||||
useTheme,
|
||||
} from '@superset-ui/core';
|
||||
import { Global } from '@emotion/react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import ErrorBoundary from 'src/components/ErrorBoundary';
|
||||
|
|
@ -82,52 +89,66 @@ import { useNativeFilters } from './state';
|
|||
type DashboardBuilderProps = {};
|
||||
|
||||
const StyledDiv = styled.div`
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
grid-template-rows: auto 1fr;
|
||||
flex: 1;
|
||||
/* Special cases */
|
||||
${({ theme }) => css`
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
grid-template-rows: auto 1fr;
|
||||
flex: 1;
|
||||
/* Special cases */
|
||||
|
||||
/* A row within a column has inset hover menu */
|
||||
.dragdroppable-column .dragdroppable-row .hover-menu--left {
|
||||
left: -12px;
|
||||
background: ${({ theme }) => theme.colors.grayscale.light5};
|
||||
border: 1px solid ${({ theme }) => theme.colors.grayscale.light2};
|
||||
}
|
||||
/* A row within a column has inset hover menu */
|
||||
.dragdroppable-column .dragdroppable-row .hover-menu--left {
|
||||
left: ${theme.gridUnit * -3}px;
|
||||
background: ${theme.colors.grayscale.light5};
|
||||
border: 1px solid ${theme.colors.grayscale.light2};
|
||||
}
|
||||
|
||||
.dashboard-component-tabs {
|
||||
position: relative;
|
||||
}
|
||||
.dashboard-component-tabs {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* A column within a column or tabs has inset hover menu */
|
||||
.dragdroppable-column .dragdroppable-column .hover-menu--top,
|
||||
.dashboard-component-tabs .dragdroppable-column .hover-menu--top {
|
||||
top: -12px;
|
||||
background: ${({ theme }) => theme.colors.grayscale.light5};
|
||||
border: 1px solid ${({ theme }) => theme.colors.grayscale.light2};
|
||||
}
|
||||
/* A column within a column or tabs has inset hover menu */
|
||||
.dragdroppable-column .dragdroppable-column .hover-menu--top,
|
||||
.dashboard-component-tabs .dragdroppable-column .hover-menu--top {
|
||||
top: ${theme.gridUnit * -3}px;
|
||||
background: ${theme.colors.grayscale.light5};
|
||||
border: 1px solid ${theme.colors.grayscale.light2};
|
||||
}
|
||||
|
||||
/* move Tabs hover menu to top near actual Tabs */
|
||||
.dashboard-component-tabs > .hover-menu-container > .hover-menu--left {
|
||||
top: 0;
|
||||
transform: unset;
|
||||
background: transparent;
|
||||
}
|
||||
/* move Tabs hover menu to top near actual Tabs */
|
||||
.dashboard-component-tabs > .hover-menu-container > .hover-menu--left {
|
||||
top: 0;
|
||||
transform: unset;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* push Chart actions to upper right */
|
||||
.dragdroppable-column .dashboard-component-chart-holder .hover-menu--top,
|
||||
.dragdroppable .dashboard-component-header .hover-menu--top {
|
||||
right: 8px;
|
||||
top: 8px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
transform: unset;
|
||||
left: unset;
|
||||
}
|
||||
div:hover > .hover-menu-container .hover-menu,
|
||||
.hover-menu-container .hover-menu:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
/* push Chart actions to upper right */
|
||||
.dragdroppable-column .dashboard-component-chart-holder .hover-menu--top,
|
||||
.dragdroppable .dashboard-component-header .hover-menu--top {
|
||||
right: ${theme.gridUnit * 2}px;
|
||||
top: ${theme.gridUnit * 2}px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
transform: unset;
|
||||
left: unset;
|
||||
}
|
||||
div:hover > .hover-menu-container .hover-menu,
|
||||
.hover-menu-container .hover-menu:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0 0 ${theme.gridUnit * 2}px 0;
|
||||
}
|
||||
|
||||
i.danger {
|
||||
color: ${theme.colors.error.base};
|
||||
}
|
||||
|
||||
i.warning {
|
||||
color: ${theme.colors.alert.base};
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
// @z-index-above-dashboard-charts + 1 = 11
|
||||
|
|
@ -164,75 +185,247 @@ const StyledContent = styled.div<{
|
|||
${({ fullSizeChartId }) => fullSizeChartId && `z-index: 101;`}
|
||||
`;
|
||||
|
||||
const StyledDashboardContent = styled.div<{
|
||||
dashboardFiltersOpen: boolean;
|
||||
editMode: boolean;
|
||||
nativeFiltersEnabled: boolean;
|
||||
filterBarOrientation: FilterBarOrientation;
|
||||
}>`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
height: auto;
|
||||
flex: 1;
|
||||
const DashboardContentWrapper = styled.div`
|
||||
${({ theme }) => css`
|
||||
&.dashboard {
|
||||
position: relative;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
|
||||
.grid-container .dashboard-component-tabs {
|
||||
box-shadow: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.grid-container {
|
||||
/* without this, the grid will not get smaller upon toggling the builder panel on */
|
||||
width: 0;
|
||||
flex: 1;
|
||||
position: relative;
|
||||
margin-top: ${({ theme }) => theme.gridUnit * 6}px;
|
||||
margin-right: ${({ theme }) => theme.gridUnit * 8}px;
|
||||
margin-bottom: ${({ theme }) => theme.gridUnit * 6}px;
|
||||
margin-left: ${({
|
||||
theme,
|
||||
dashboardFiltersOpen,
|
||||
editMode,
|
||||
nativeFiltersEnabled,
|
||||
filterBarOrientation,
|
||||
}) => {
|
||||
if (
|
||||
!dashboardFiltersOpen &&
|
||||
!editMode &&
|
||||
nativeFiltersEnabled &&
|
||||
filterBarOrientation !== FilterBarOrientation.HORIZONTAL
|
||||
) {
|
||||
return 0;
|
||||
/* drop shadow for top-level tabs only */
|
||||
& .dashboard-component-tabs {
|
||||
box-shadow: 0 ${theme.gridUnit}px ${theme.gridUnit}px 0
|
||||
${addAlpha(
|
||||
theme.colors.grayscale.dark2,
|
||||
parseFloat(theme.opacity.light) / 100,
|
||||
)};
|
||||
padding-left: ${theme.gridUnit *
|
||||
2}px; /* note this is added to tab-level padding, to match header */
|
||||
}
|
||||
return theme.gridUnit * 8;
|
||||
}}px;
|
||||
|
||||
${({ editMode, theme }) =>
|
||||
editMode &&
|
||||
.dropdown-toggle.btn.btn-primary .caret {
|
||||
color: ${theme.colors.grayscale.light5};
|
||||
}
|
||||
|
||||
.background--transparent {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.background--white {
|
||||
background-color: ${theme.colors.grayscale.light5};
|
||||
}
|
||||
}
|
||||
&.dashboard--editing {
|
||||
.grid-row:after,
|
||||
.dashboard-component-tabs > .hover-menu:hover + div:after {
|
||||
border: 1px dashed transparent;
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.resizable-container {
|
||||
& .dashboard-component-chart-holder {
|
||||
.dashboard-chart {
|
||||
.chart-container {
|
||||
cursor: move;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.slice_container {
|
||||
/* disable chart interactions in edit mode */
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover .dashboard-chart .chart-container {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&.resizable-container--resizing:hover {
|
||||
& > .dashboard-component-chart-holder:after {
|
||||
border: 1px dashed ${theme.colors.primary.base};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.resizable-container--resizing:hover > .grid-row:after,
|
||||
.hover-menu:hover + .grid-row:after,
|
||||
.dashboard-component-tabs > .hover-menu:hover + div:after {
|
||||
border: 1px dashed ${theme.colors.primary.base};
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.grid-row:after,
|
||||
.dashboard-component-tabs > .hover-menu + div:after {
|
||||
border: 1px dashed ${theme.colors.grayscale.light2};
|
||||
}
|
||||
|
||||
/* provide hit area in case row contents is edge to edge */
|
||||
.dashboard-component-tabs-content {
|
||||
.dragdroppable-row {
|
||||
padding-top: ${theme.gridUnit * 4}px;
|
||||
}
|
||||
|
||||
& > div:not(:last-child):not(.empty-droptarget) {
|
||||
margin-bottom: ${theme.gridUnit * 4}px;
|
||||
}
|
||||
}
|
||||
|
||||
.dashboard-component-chart-holder {
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
&:hover:after {
|
||||
border: 1px dashed ${theme.colors.primary.base};
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
|
||||
.contract-trigger:before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
& .dashboard-component-tabs-content {
|
||||
& > div:not(:last-child):not(.empty-droptarget) {
|
||||
margin-bottom: ${theme.gridUnit * 4}px;
|
||||
}
|
||||
|
||||
& > .empty-droptarget {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
& > .empty-droptarget:first-child {
|
||||
height: ${theme.gridUnit * 4}px;
|
||||
top: -2px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
& > .empty-droptarget:last-child {
|
||||
height: ${theme.gridUnit * 3}px;
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.empty-droptarget:first-child .drop-indicator--bottom {
|
||||
top: ${theme.gridUnit * 6}px;
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
const StyledDashboardContent = styled.div<{
|
||||
editMode: boolean;
|
||||
marginLeft: number;
|
||||
}>`
|
||||
${({ theme, editMode, marginLeft }) => css`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
height: auto;
|
||||
flex: 1;
|
||||
|
||||
.grid-container .dashboard-component-tabs {
|
||||
box-shadow: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.grid-container {
|
||||
/* without this, the grid will not get smaller upon toggling the builder panel on */
|
||||
width: 0;
|
||||
flex: 1;
|
||||
position: relative;
|
||||
margin-top: ${theme.gridUnit * 6}px;
|
||||
margin-right: ${theme.gridUnit * 8}px;
|
||||
margin-bottom: ${theme.gridUnit * 6}px;
|
||||
margin-left: ${marginLeft}px;
|
||||
|
||||
${editMode &&
|
||||
`
|
||||
max-width: calc(100% - ${
|
||||
BUILDER_SIDEPANEL_WIDTH + theme.gridUnit * 16
|
||||
}px);
|
||||
`}
|
||||
}
|
||||
|
||||
.dashboard-builder-sidepane {
|
||||
width: ${BUILDER_SIDEPANEL_WIDTH}px;
|
||||
z-index: 1;
|
||||
}
|
||||
/* this is the ParentSize wrapper */
|
||||
& > div:first-child {
|
||||
height: inherit !important;
|
||||
}
|
||||
}
|
||||
|
||||
.dashboard-component-chart-holder {
|
||||
// transitionable traits to show filter relevance
|
||||
transition: opacity ${({ theme }) => theme.transitionTiming}s,
|
||||
border-color ${({ theme }) => theme.transitionTiming}s,
|
||||
box-shadow ${({ theme }) => theme.transitionTiming}s;
|
||||
border: 0 solid transparent;
|
||||
}
|
||||
.dashboard-builder-sidepane {
|
||||
width: ${BUILDER_SIDEPANEL_WIDTH}px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.dashboard-component-chart-holder {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: ${theme.colors.grayscale.light5};
|
||||
position: relative;
|
||||
padding: ${theme.gridUnit * 4}px;
|
||||
overflow-y: visible;
|
||||
|
||||
// transitionable traits to show filter relevance
|
||||
transition: opacity ${theme.transitionTiming}s ease-in-out,
|
||||
border-color ${theme.transitionTiming}s ease-in-out,
|
||||
box-shadow ${theme.transitionTiming}s ease-in-out;
|
||||
|
||||
&.fade-in {
|
||||
border-radius: ${theme.borderRadius}px;
|
||||
box-shadow: inset 0 0 0 2px ${theme.colors.primary.base},
|
||||
0 0 0 3px
|
||||
${addAlpha(
|
||||
theme.colors.primary.base,
|
||||
parseFloat(theme.opacity.light) / 100,
|
||||
)};
|
||||
}
|
||||
|
||||
&.fade-out {
|
||||
border-radius: ${theme.borderRadius}px;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
& .missing-chart-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
overflow-y: auto;
|
||||
justify-content: center;
|
||||
|
||||
.missing-chart-body {
|
||||
font-size: ${theme.typography.sizes.s}px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
const DashboardBuilder: FC<DashboardBuilderProps> = () => {
|
||||
const dispatch = useDispatch();
|
||||
const uiConfig = useUiConfig();
|
||||
const theme = useTheme();
|
||||
|
||||
const dashboardId = useSelector<RootState, string>(
|
||||
({ dashboardInfo }) => `${dashboardInfo.id}`,
|
||||
|
|
@ -433,6 +626,14 @@ const DashboardBuilder: FC<DashboardBuilderProps> = () => {
|
|||
],
|
||||
);
|
||||
|
||||
const dashboardContentMarginLeft =
|
||||
!dashboardFiltersOpen &&
|
||||
!editMode &&
|
||||
nativeFiltersEnabled &&
|
||||
filterBarOrientation !== FilterBarOrientation.HORIZONTAL
|
||||
? 0
|
||||
: theme.gridUnit * 8;
|
||||
|
||||
return (
|
||||
<StyledDiv>
|
||||
{showFilterBar && filterBarOrientation === FilterBarOrientation.VERTICAL && (
|
||||
|
|
@ -518,16 +719,14 @@ const DashboardBuilder: FC<DashboardBuilderProps> = () => {
|
|||
image="dashboard.svg"
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
data-test="dashboard-content"
|
||||
<DashboardContentWrapper
|
||||
data-test="dashboard-content-wrapper"
|
||||
className={cx('dashboard', editMode && 'dashboard--editing')}
|
||||
>
|
||||
<StyledDashboardContent
|
||||
className="dashboard-content"
|
||||
dashboardFiltersOpen={dashboardFiltersOpen}
|
||||
editMode={editMode}
|
||||
nativeFiltersEnabled={nativeFiltersEnabled}
|
||||
filterBarOrientation={filterBarOrientation}
|
||||
marginLeft={dashboardContentMarginLeft}
|
||||
>
|
||||
{showDashboard ? (
|
||||
<DashboardContainer topLevelTabs={topLevelTabs} />
|
||||
|
|
@ -536,7 +735,7 @@ const DashboardBuilder: FC<DashboardBuilderProps> = () => {
|
|||
)}
|
||||
{editMode && <BuilderComponentPane topOffset={barTopOffset} />}
|
||||
</StyledDashboardContent>
|
||||
</div>
|
||||
</DashboardContentWrapper>
|
||||
</StyledContent>
|
||||
{dashboardIsSaving && (
|
||||
<Loading
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { styled, t } from '@superset-ui/core';
|
||||
import { addAlpha, css, styled, t } from '@superset-ui/core';
|
||||
import { EmptyStateBig } from 'src/components/EmptyState';
|
||||
import { componentShape } from '../util/propShapes';
|
||||
import DashboardComponent from '../containers/DashboardComponent';
|
||||
|
|
@ -58,16 +58,62 @@ const DashboardEmptyStateContainer = styled.div`
|
|||
right: 0;
|
||||
`;
|
||||
|
||||
const GridContent = styled.div`
|
||||
${({ theme }) => css`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
/* gutters between rows */
|
||||
& > div:not(:last-child):not(.empty-droptarget) {
|
||||
margin-bottom: ${theme.gridUnit * 4}px;
|
||||
}
|
||||
|
||||
& > .empty-droptarget {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
& > .empty-droptarget:first-child {
|
||||
height: ${theme.gridUnit * 12}px;
|
||||
margin-top: ${theme.gridUnit * -6}px;
|
||||
margin-bottom: ${theme.gridUnit * -6}px;
|
||||
}
|
||||
|
||||
& > .empty-droptarget:only-child {
|
||||
height: 80vh;
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
const GridColumnGuide = styled.div`
|
||||
${({ theme }) => css`
|
||||
// /* Editing guides */
|
||||
&.grid-column-guide {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
min-height: 100%;
|
||||
background-color: ${addAlpha(
|
||||
theme.colors.primary.base,
|
||||
parseFloat(theme.opacity.light) / 100,
|
||||
)};
|
||||
pointer-events: none;
|
||||
box-shadow: inset 0 0 0 1px
|
||||
${addAlpha(
|
||||
theme.colors.primary.base,
|
||||
parseFloat(theme.opacity.mediumHeavy) / 100,
|
||||
)};
|
||||
}
|
||||
`};
|
||||
`;
|
||||
|
||||
class DashboardGrid extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isResizing: false,
|
||||
rowGuideTop: null,
|
||||
};
|
||||
|
||||
this.handleResizeStart = this.handleResizeStart.bind(this);
|
||||
this.handleResize = this.handleResize.bind(this);
|
||||
this.handleResizeStop = this.handleResizeStop.bind(this);
|
||||
this.handleTopDropTargetDrop = this.handleTopDropTargetDrop.bind(this);
|
||||
this.getRowGuidePosition = this.getRowGuidePosition.bind(this);
|
||||
|
|
@ -90,30 +136,17 @@ class DashboardGrid extends React.PureComponent {
|
|||
this.grid = ref;
|
||||
}
|
||||
|
||||
handleResizeStart({ ref, direction }) {
|
||||
let rowGuideTop = null;
|
||||
if (direction === 'bottom' || direction === 'bottomRight') {
|
||||
rowGuideTop = this.getRowGuidePosition(ref);
|
||||
}
|
||||
|
||||
handleResizeStart() {
|
||||
this.setState(() => ({
|
||||
isResizing: true,
|
||||
rowGuideTop,
|
||||
}));
|
||||
}
|
||||
|
||||
handleResize({ ref, direction }) {
|
||||
if (direction === 'bottom' || direction === 'bottomRight') {
|
||||
this.setState(() => ({ rowGuideTop: this.getRowGuidePosition(ref) }));
|
||||
}
|
||||
}
|
||||
|
||||
handleResizeStop({ id, widthMultiple: width, heightMultiple: height }) {
|
||||
this.props.resizeComponent({ id, width, height });
|
||||
|
||||
this.setState(() => ({
|
||||
isResizing: false,
|
||||
rowGuideTop: null,
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
@ -150,7 +183,7 @@ class DashboardGrid extends React.PureComponent {
|
|||
(width + GRID_GUTTER_SIZE) / GRID_COLUMN_COUNT;
|
||||
|
||||
const columnWidth = columnPlusGutterWidth - GRID_GUTTER_SIZE;
|
||||
const { isResizing, rowGuideTop } = this.state;
|
||||
const { isResizing } = this.state;
|
||||
|
||||
const shouldDisplayEmptyState = gridComponent?.children?.length === 0;
|
||||
const shouldDisplayTopLevelTabEmptyState =
|
||||
|
|
@ -227,7 +260,7 @@ class DashboardGrid extends React.PureComponent {
|
|||
</DashboardEmptyStateContainer>
|
||||
)}
|
||||
<div className="dashboard-grid" ref={this.setGridRef}>
|
||||
<div className="grid-content" data-test="grid-content">
|
||||
<GridContent className="grid-content" data-test="grid-content">
|
||||
{/* make the area above components droppable */}
|
||||
{editMode && (
|
||||
<DragDroppable
|
||||
|
|
@ -278,7 +311,7 @@ class DashboardGrid extends React.PureComponent {
|
|||
Array(GRID_COLUMN_COUNT)
|
||||
.fill(null)
|
||||
.map((_, i) => (
|
||||
<div
|
||||
<GridColumnGuide
|
||||
key={`grid-column-${i}`}
|
||||
className="grid-column-guide"
|
||||
style={{
|
||||
|
|
@ -287,16 +320,7 @@ class DashboardGrid extends React.PureComponent {
|
|||
}}
|
||||
/>
|
||||
))}
|
||||
{isResizing && rowGuideTop && (
|
||||
<div
|
||||
className="grid-row-guide"
|
||||
style={{
|
||||
top: rowGuideTop,
|
||||
width,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</GridContent>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -76,13 +76,6 @@ describe('DashboardGrid', () => {
|
|||
expect(wrapper.find('.grid-column-guide')).toHaveLength(GRID_COLUMN_COUNT);
|
||||
});
|
||||
|
||||
it('should render a grid row guide when resizing', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find('.grid-row-guide')).not.toExist();
|
||||
wrapper.setState({ isResizing: true, rowGuideTop: 10 });
|
||||
expect(wrapper.find('.grid-row-guide')).toExist();
|
||||
});
|
||||
|
||||
it('should call resizeComponent when a child DashboardComponent calls resizeStop', () => {
|
||||
const resizeComponent = sinon.spy();
|
||||
const args = { id: 'id', widthMultiple: 1, heightMultiple: 3 };
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import React, {
|
|||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { styled, t } from '@superset-ui/core';
|
||||
import { css, styled, t } from '@superset-ui/core';
|
||||
import { useUiConfig } from 'src/components/UiConfigContext';
|
||||
import { Tooltip } from 'src/components/Tooltip';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
|
@ -64,6 +64,69 @@ const CrossFilterIcon = styled(Icons.CursorTarget)`
|
|||
width: 22px;
|
||||
`;
|
||||
|
||||
const ChartHeaderStyles = styled.div`
|
||||
${({ theme }) => css`
|
||||
font-size: ${theme.typography.sizes.l}px;
|
||||
font-weight: ${theme.typography.weights.bold};
|
||||
margin-bottom: ${theme.gridUnit}px;
|
||||
display: flex;
|
||||
max-width: 100%;
|
||||
align-items: flex-start;
|
||||
min-height: 0;
|
||||
|
||||
& > .header-title {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 100%;
|
||||
flex-grow: 1;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
|
||||
& > span.ant-tooltip-open {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
|
||||
& > .header-controls {
|
||||
display: flex;
|
||||
|
||||
& > * {
|
||||
margin-left: ${theme.gridUnit * 2}px;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown.btn-group {
|
||||
pointer-events: none;
|
||||
vertical-align: top;
|
||||
& > * {
|
||||
pointer-events: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-toggle.btn.btn-default {
|
||||
background: none;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.dropdown-menu.dropdown-menu-right {
|
||||
top: ${theme.gridUnit * 5}px;
|
||||
}
|
||||
|
||||
.divider {
|
||||
margin: ${theme.gridUnit}px 0;
|
||||
}
|
||||
|
||||
.refresh-tooltip {
|
||||
display: block;
|
||||
height: ${theme.gridUnit * 4}px;
|
||||
margin: ${theme.gridUnit}px 0;
|
||||
color: ${theme.colors.text.label};
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
const SliceHeader: FC<SliceHeaderProps> = ({
|
||||
innerRef = null,
|
||||
forceRefresh = () => ({}),
|
||||
|
|
@ -134,7 +197,7 @@ const SliceHeader: FC<SliceHeaderProps> = ({
|
|||
const exploreUrl = `/explore/?dashboard_page_id=${dashboardPageId}&slice_id=${slice.slice_id}`;
|
||||
|
||||
return (
|
||||
<div className="chart-header" data-test="slice-header" ref={innerRef}>
|
||||
<ChartHeaderStyles data-test="slice-header" ref={innerRef}>
|
||||
<div className="header-title" ref={headerRef}>
|
||||
<Tooltip title={headerTooltip}>
|
||||
<EditableTitle
|
||||
|
|
@ -229,7 +292,7 @@ const SliceHeader: FC<SliceHeaderProps> = ({
|
|||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</ChartHeaderStyles>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -69,12 +69,20 @@ const MENU_KEYS = {
|
|||
DRILL_TO_DETAIL: 'drill_to_detail',
|
||||
};
|
||||
|
||||
// TODO: replace 3 dots with an icon
|
||||
const VerticalDotsContainer = styled.div`
|
||||
padding: ${({ theme }) => theme.gridUnit / 4}px
|
||||
${({ theme }) => theme.gridUnit * 1.5}px;
|
||||
|
||||
.dot {
|
||||
display: block;
|
||||
|
||||
height: ${({ theme }) => theme.gridUnit}px;
|
||||
width: ${({ theme }) => theme.gridUnit}px;
|
||||
border-radius: 50%;
|
||||
margin: ${({ theme }) => theme.gridUnit / 2}px 0;
|
||||
|
||||
background-color: ${({ theme }) => theme.colors.text.label};
|
||||
}
|
||||
|
||||
&:hover {
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import React from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import { DragSource, DropTarget } from 'react-dnd';
|
||||
import cx from 'classnames';
|
||||
import { css, styled } from '@superset-ui/core';
|
||||
|
||||
import { componentShape } from '../../util/propShapes';
|
||||
import { dragConfig, dropConfig } from './dragDroppableConfig';
|
||||
|
|
@ -73,6 +74,64 @@ const defaultProps = {
|
|||
dragPreviewRef() {},
|
||||
};
|
||||
|
||||
const DragDroppableStyles = styled.div`
|
||||
${({ theme }) => css`
|
||||
position: relative;
|
||||
|
||||
&.dragdroppable--dragging {
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
&.dragdroppable-row {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&.dragdroppable-column .resizable-container span div {
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
& {
|
||||
.drop-indicator {
|
||||
display: block;
|
||||
background-color: ${theme.colors.primary.base};
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.drop-indicator--top {
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: ${theme.gridUnit}px;
|
||||
width: 100%;
|
||||
min-width: ${theme.gridUnit * 4}px;
|
||||
}
|
||||
|
||||
.drop-indicator--bottom {
|
||||
top: 100%;
|
||||
left: 0;
|
||||
height: ${theme.gridUnit}px;
|
||||
width: 100%;
|
||||
min-width: ${theme.gridUnit * 4}px;
|
||||
}
|
||||
|
||||
.drop-indicator--right {
|
||||
top: 0;
|
||||
left: 100%;
|
||||
height: 100%;
|
||||
width: ${theme.gridUnit}px;
|
||||
min-height: ${theme.gridUnit * 4}px;
|
||||
}
|
||||
|
||||
.drop-indicator--left {
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: ${theme.gridUnit}px;
|
||||
min-height: ${theme.gridUnit * 4}px;
|
||||
}
|
||||
}
|
||||
`};
|
||||
`;
|
||||
// export unwrapped component for testing
|
||||
export class UnwrappedDragDroppable extends React.PureComponent {
|
||||
constructor(props) {
|
||||
|
|
@ -141,7 +200,7 @@ export class UnwrappedDragDroppable extends React.PureComponent {
|
|||
: {};
|
||||
|
||||
return (
|
||||
<div
|
||||
<DragDroppableStyles
|
||||
style={style}
|
||||
ref={this.setRef}
|
||||
data-test="dragdroppable-object"
|
||||
|
|
@ -154,7 +213,7 @@ export class UnwrappedDragDroppable extends React.PureComponent {
|
|||
)}
|
||||
>
|
||||
{children(childProps)}
|
||||
</div>
|
||||
</DragDroppableStyles>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,10 @@
|
|||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { shallow, mount } from 'enzyme';
|
||||
import {
|
||||
styledMount as mount,
|
||||
styledShallow as shallow,
|
||||
} from 'spec/helpers/theming';
|
||||
import sinon from 'sinon';
|
||||
|
||||
import newComponentFactory from 'src/dashboard/util/newComponentFactory';
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import React from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import cx from 'classnames';
|
||||
import Button from 'src/components/Button';
|
||||
import { t, styled } from '@superset-ui/core';
|
||||
import { css, t, styled } from '@superset-ui/core';
|
||||
|
||||
import buildFilterScopeTreeEntry from 'src/dashboard/util/buildFilterScopeTreeEntry';
|
||||
import getFilterScopeNodesTree from 'src/dashboard/util/getFilterScopeNodesTree';
|
||||
|
|
@ -49,6 +49,268 @@ const propTypes = {
|
|||
onCloseModal: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
const ScopeContainer = styled.div`
|
||||
${({ theme }) => css`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 80%;
|
||||
margin-right: ${theme.gridUnit * -6}px;
|
||||
font-size: ${theme.typography.sizes.m}px;
|
||||
|
||||
& .nav.nav-tabs {
|
||||
border: none;
|
||||
}
|
||||
|
||||
& .filter-scope-body {
|
||||
flex: 1;
|
||||
max-height: calc(100% - ${theme.gridUnit * 32}px);
|
||||
|
||||
.filter-field-pane,
|
||||
.filter-scope-pane {
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
& .warning-message {
|
||||
padding: ${theme.gridUnit * 6}px;
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
const ScopeBody = styled.div`
|
||||
${({ theme }) => css`
|
||||
&.filter-scope-body {
|
||||
flex: 1;
|
||||
max-height: calc(100% - ${theme.gridUnit * 32}px);
|
||||
|
||||
.filter-field-pane,
|
||||
.filter-scope-pane {
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
const ScopeHeader = styled.div`
|
||||
${({ theme }) => css`
|
||||
height: ${theme.gridUnit * 16}px;
|
||||
border-bottom: 1px solid ${theme.colors.grayscale.light2};
|
||||
padding-left: ${theme.gridUnit * 6}px;
|
||||
margin-left: ${theme.gridUnit * -6}px;
|
||||
|
||||
h4 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.selected-fields {
|
||||
margin: ${theme.gridUnit * 3}px 0 ${theme.gridUnit * 4}px;
|
||||
visibility: hidden;
|
||||
|
||||
&.multi-edit-mode {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.selected-scopes {
|
||||
padding-left: ${theme.gridUnit}px;
|
||||
}
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
const ScopeSelector = styled.div`
|
||||
${({ theme }) => css`
|
||||
&.filters-scope-selector {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
|
||||
a,
|
||||
a:active,
|
||||
a:hover {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.react-checkbox-tree .rct-icon.rct-icon-expand-all,
|
||||
.react-checkbox-tree .rct-icon.rct-icon-collapse-all {
|
||||
font-family: ${theme.typography.families.sansSerif};
|
||||
font-size: ${theme.typography.sizes.m}px;
|
||||
color: ${theme.colors.primary.base};
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
}
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
.filter-field-pane {
|
||||
position: relative;
|
||||
width: 40%;
|
||||
padding: ${theme.gridUnit * 4}px;
|
||||
padding-left: 0;
|
||||
border-right: 1px solid ${theme.colors.grayscale.light2};
|
||||
|
||||
.filter-container label {
|
||||
font-weight: ${theme.typography.weights.normal};
|
||||
margin: 0 0 0 ${theme.gridUnit * 4}px;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.filter-field-item {
|
||||
height: ${theme.gridUnit * 9}px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 ${theme.gridUnit * 6}px;
|
||||
margin-left: ${theme.gridUnit * -6}px;
|
||||
|
||||
&.is-selected {
|
||||
border: 1px solid ${theme.colors.text.label};
|
||||
border-radius: ${theme.borderRadius}px;
|
||||
background-color: ${theme.colors.grayscale.light4};
|
||||
margin-left: ${theme.gridUnit * -6}px;
|
||||
}
|
||||
}
|
||||
|
||||
.react-checkbox-tree {
|
||||
.rct-title .root {
|
||||
font-weight: ${theme.typography.weights.bold};
|
||||
}
|
||||
|
||||
.rct-text {
|
||||
height: ${theme.gridUnit * 10}px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filter-scope-pane {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
padding: ${theme.gridUnit * 4}px;
|
||||
padding-right: ${theme.gridUnit * 6}px;
|
||||
}
|
||||
|
||||
.react-checkbox-tree {
|
||||
flex-direction: column;
|
||||
color: ${theme.colors.grayscale.dark1};
|
||||
font-size: ${theme.typography.sizes.m}px;
|
||||
|
||||
.filter-scope-type {
|
||||
padding: ${theme.gridUnit * 2}px 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&.chart {
|
||||
font-weight: ${theme.typography.weights.normal};
|
||||
}
|
||||
|
||||
&.selected-filter {
|
||||
padding-left: ${theme.gridUnit * 7}px;
|
||||
position: relative;
|
||||
color: ${theme.colors.text.label};
|
||||
|
||||
&::before {
|
||||
content: ' ';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
width: ${theme.gridUnit * 4}px;
|
||||
height: ${theme.gridUnit * 4}px;
|
||||
border-radius: ${theme.borderRadius}px;
|
||||
margin-top: ${theme.gridUnit * -2}px;
|
||||
box-shadow: inset 0 0 0 2px ${theme.colors.grayscale.light2};
|
||||
background: ${theme.colors.grayscale.light3};
|
||||
}
|
||||
}
|
||||
|
||||
&.root {
|
||||
font-weight: ${theme.typography.weights.bold};
|
||||
}
|
||||
}
|
||||
|
||||
.rct-checkbox {
|
||||
svg {
|
||||
position: relative;
|
||||
top: 3px;
|
||||
width: ${theme.gridUnit * 4.5}px;
|
||||
}
|
||||
}
|
||||
|
||||
.rct-node-leaf {
|
||||
.rct-bare-label {
|
||||
&::before {
|
||||
padding-left: ${theme.gridUnit}px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.rct-options {
|
||||
text-align: left;
|
||||
margin-left: 0;
|
||||
margin-bottom: ${theme.gridUnit * 2}px;
|
||||
}
|
||||
|
||||
.rct-text {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.rct-title {
|
||||
display: block;
|
||||
}
|
||||
|
||||
// disable style from react-checkbox-trees.css
|
||||
.rct-node-clickable:hover,
|
||||
.rct-node-clickable:focus,
|
||||
label:hover,
|
||||
label:active {
|
||||
background: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.multi-edit-mode {
|
||||
&.filter-scope-pane {
|
||||
.rct-node.rct-node-leaf .filter-scope-type.filter_box {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.filter-field-item {
|
||||
padding: 0 ${theme.gridUnit * 4}px 0 ${theme.gridUnit * 12}px;
|
||||
margin-left: ${theme.gridUnit * -12}px;
|
||||
|
||||
&.is-selected {
|
||||
margin-left: ${theme.gridUnit * -13}px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.scope-search {
|
||||
position: absolute;
|
||||
right: ${theme.gridUnit * 4}px;
|
||||
top: ${theme.gridUnit * 4}px;
|
||||
border-radius: ${theme.borderRadius}px;
|
||||
border: 1px solid ${theme.colors.grayscale.light2};
|
||||
padding: ${theme.gridUnit}px ${theme.gridUnit * 2}px;
|
||||
font-size: ${theme.typography.sizes.m}px;
|
||||
outline: none;
|
||||
|
||||
&:focus {
|
||||
border: 1px solid ${theme.colors.primary.base};
|
||||
}
|
||||
}
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
const ActionsContainer = styled.div`
|
||||
${({ theme }) => `
|
||||
height: ${theme.gridUnit * 16}px;
|
||||
|
|
@ -496,28 +758,28 @@ export default class FilterScopeSelector extends React.PureComponent {
|
|||
const { showSelector } = this.state;
|
||||
|
||||
return (
|
||||
<div className="filter-scope-container">
|
||||
<div className="filter-scope-header">
|
||||
<ScopeContainer>
|
||||
<ScopeHeader>
|
||||
<h4>{t('Configure filter scopes')}</h4>
|
||||
{showSelector && this.renderEditingFiltersName()}
|
||||
</div>
|
||||
</ScopeHeader>
|
||||
|
||||
<div className="filter-scope-body">
|
||||
<ScopeBody className="filter-scope-body">
|
||||
{!showSelector ? (
|
||||
<div className="warning-message">
|
||||
{t('There are no filters in this dashboard.')}
|
||||
</div>
|
||||
) : (
|
||||
<div className="filters-scope-selector">
|
||||
<ScopeSelector className="filters-scope-selector">
|
||||
<div className={cx('filter-field-pane multi-edit-mode')}>
|
||||
{this.renderFilterFieldList()}
|
||||
</div>
|
||||
<div className="filter-scope-pane multi-edit-mode">
|
||||
{this.renderFilterScopeTree()}
|
||||
</div>
|
||||
</div>
|
||||
</ScopeSelector>
|
||||
)}
|
||||
</div>
|
||||
</ScopeBody>
|
||||
|
||||
<ActionsContainer>
|
||||
<Button buttonSize="small" onClick={this.onClose}>
|
||||
|
|
@ -533,7 +795,7 @@ export default class FilterScopeSelector extends React.PureComponent {
|
|||
</Button>
|
||||
)}
|
||||
</ActionsContainer>
|
||||
</div>
|
||||
</ScopeContainer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -114,6 +114,15 @@ const SHOULD_UPDATE_ON_PROP_CHANGES = Object.keys(propTypes).filter(
|
|||
const OVERFLOWABLE_VIZ_TYPES = new Set(['filter_box']);
|
||||
const DEFAULT_HEADER_HEIGHT = 22;
|
||||
|
||||
const ChartWrapper = styled.div`
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
&.dashboard-chart--overflowable {
|
||||
overflow: visible;
|
||||
}
|
||||
`;
|
||||
|
||||
const ChartOverlay = styled.div`
|
||||
position: absolute;
|
||||
top: 0;
|
||||
|
|
@ -486,7 +495,7 @@ class Chart extends React.Component {
|
|||
/>
|
||||
)}
|
||||
|
||||
<div
|
||||
<ChartWrapper
|
||||
className={cx(
|
||||
'dashboard-chart',
|
||||
isOverflowable && 'dashboard-chart--overflowable',
|
||||
|
|
@ -530,7 +539,7 @@ class Chart extends React.Component {
|
|||
datasetsStatus={datasetsStatus}
|
||||
isInView={isInView}
|
||||
/>
|
||||
</div>
|
||||
</ChartWrapper>
|
||||
</SliceContainer>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import React, { useState, useMemo, useCallback, useEffect } from 'react';
|
|||
import { ResizeCallback, ResizeStartCallback } from 're-resizable';
|
||||
import cx from 'classnames';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { css } from '@superset-ui/core';
|
||||
import { LayoutItem, RootState } from 'src/dashboard/types';
|
||||
import AnchorLink from 'src/dashboard/components/AnchorLink';
|
||||
import Chart from 'src/dashboard/containers/Chart';
|
||||
|
|
@ -69,6 +69,15 @@ interface ChartHolderProps {
|
|||
isInView: boolean;
|
||||
}
|
||||
|
||||
const fullSizeStyle = css`
|
||||
&& {
|
||||
position: fixed;
|
||||
z-index: 3000;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const ChartHolder: React.FC<ChartHolderProps> = ({
|
||||
id,
|
||||
parentId,
|
||||
|
|
@ -265,13 +274,13 @@ const ChartHolder: React.FC<ChartHolderProps> = ({
|
|||
ref={dragSourceRef}
|
||||
data-test="dashboard-component-chart-holder"
|
||||
style={focusHighlightStyles}
|
||||
css={isFullSize ? fullSizeStyle : undefined}
|
||||
className={cx(
|
||||
'dashboard-component',
|
||||
'dashboard-component-chart-holder',
|
||||
// The following class is added to support custom dashboard styling via the CSS editor
|
||||
`dashboard-chart-id-${chartId}`,
|
||||
outlinedComponentId ? 'fade-in' : 'fade-out',
|
||||
isFullSize && 'full-size',
|
||||
)}
|
||||
>
|
||||
{!editMode && (
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import cx from 'classnames';
|
||||
import { css, styled, t } from '@superset-ui/core';
|
||||
import Icons from 'src/components/Icons';
|
||||
import DashboardComponent from 'src/dashboard/containers/DashboardComponent';
|
||||
import DeleteComponentButton from 'src/dashboard/components/DeleteComponentButton';
|
||||
|
|
@ -58,6 +59,46 @@ const propTypes = {
|
|||
|
||||
const defaultProps = {};
|
||||
|
||||
const ColumnStyles = styled.div`
|
||||
${({ theme }) => css`
|
||||
&.grid-column {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
|
||||
& > :not(.hover-menu):not(:last-child) {
|
||||
margin-bottom: ${theme.gridUnit * 4}px;
|
||||
}
|
||||
}
|
||||
|
||||
.dashboard--editing &:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
border: 1px dashed ${theme.colors.grayscale.light2};
|
||||
}
|
||||
.dashboard--editing .resizable-container--resizing:hover > &:after,
|
||||
.dashboard--editing .hover-menu:hover + &:after {
|
||||
border: 1px dashed ${theme.colors.primary.base};
|
||||
z-index: 2;
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
const emptyColumnContentStyles = theme => css`
|
||||
min-height: ${theme.gridUnit * 25}px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: ${theme.colors.text.label};
|
||||
`;
|
||||
|
||||
class Column extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
|
@ -172,32 +213,32 @@ class Column extends React.PureComponent {
|
|||
/>
|
||||
</HoverMenu>
|
||||
)}
|
||||
<div
|
||||
className={cx(
|
||||
'grid-column',
|
||||
columnItems.length === 0 && 'grid-column--empty',
|
||||
backgroundStyle.className,
|
||||
)}
|
||||
<ColumnStyles
|
||||
className={cx('grid-column', backgroundStyle.className)}
|
||||
>
|
||||
{columnItems.map((componentId, itemIndex) => (
|
||||
<DashboardComponent
|
||||
key={componentId}
|
||||
id={componentId}
|
||||
parentId={columnComponent.id}
|
||||
depth={depth + 1}
|
||||
index={itemIndex}
|
||||
availableColumnCount={columnComponent.meta.width}
|
||||
columnWidth={columnWidth}
|
||||
onResizeStart={onResizeStart}
|
||||
onResize={onResize}
|
||||
onResizeStop={onResizeStop}
|
||||
isComponentVisible={isComponentVisible}
|
||||
onChangeTab={onChangeTab}
|
||||
/>
|
||||
))}
|
||||
{columnItems.length === 0 ? (
|
||||
<div css={emptyColumnContentStyles}>{t('Empty column')}</div>
|
||||
) : (
|
||||
columnItems.map((componentId, itemIndex) => (
|
||||
<DashboardComponent
|
||||
key={componentId}
|
||||
id={componentId}
|
||||
parentId={columnComponent.id}
|
||||
depth={depth + 1}
|
||||
index={itemIndex}
|
||||
availableColumnCount={columnComponent.meta.width}
|
||||
columnWidth={columnWidth}
|
||||
onResizeStart={onResizeStart}
|
||||
onResize={onResize}
|
||||
onResizeStop={onResizeStop}
|
||||
isComponentVisible={isComponentVisible}
|
||||
onChangeTab={onChangeTab}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
|
||||
{dropIndicatorProps && <div {...dropIndicatorProps} />}
|
||||
</div>
|
||||
</ColumnStyles>
|
||||
</WithPopoverMenu>
|
||||
</ResizableContainer>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { css, styled } from '@superset-ui/core';
|
||||
|
||||
import DragDroppable from '../dnd/DragDroppable';
|
||||
import HoverMenu from '../menu/HoverMenu';
|
||||
|
|
@ -36,6 +37,31 @@ const propTypes = {
|
|||
deleteComponent: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
const DividerLine = styled.div`
|
||||
${({ theme }) => css`
|
||||
width: 100%;
|
||||
padding: ${theme.gridUnit * 2}px 0; /* this is padding not margin to enable a larger mouse target */
|
||||
background-color: transparent;
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
background-color: ${theme.colors.grayscale.light2};
|
||||
display: block;
|
||||
}
|
||||
|
||||
div[draggable='true'] & {
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.dashboard-component-tabs & {
|
||||
padding-left: ${theme.gridUnit * 4}px;
|
||||
padding-right: ${theme.gridUnit * 4}px;
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
class Divider extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
|
@ -75,7 +101,7 @@ class Divider extends React.PureComponent {
|
|||
</HoverMenu>
|
||||
)}
|
||||
|
||||
<div className="dashboard-component dashboard-component-divider" />
|
||||
<DividerLine className="dashboard-component dashboard-component-divider" />
|
||||
|
||||
{dropIndicatorProps && <div {...dropIndicatorProps} />}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import cx from 'classnames';
|
||||
import { css, styled } from '@superset-ui/core';
|
||||
|
||||
import PopoverDropdown from 'src/components/PopoverDropdown';
|
||||
import EditableTitle from 'src/components/EditableTitle';
|
||||
|
|
@ -55,6 +56,64 @@ const propTypes = {
|
|||
|
||||
const defaultProps = {};
|
||||
|
||||
const HeaderStyles = styled.div`
|
||||
${({ theme }) => css`
|
||||
font-weight: ${theme.typography.weights.bold};
|
||||
width: 100%;
|
||||
padding: ${theme.gridUnit * 4}px 0;
|
||||
|
||||
&.header-small {
|
||||
font-size: ${theme.typography.sizes.l}px;
|
||||
}
|
||||
|
||||
&.header-medium {
|
||||
font-size: ${theme.typography.sizes.xl}px;
|
||||
}
|
||||
|
||||
&.header-large {
|
||||
font-size: ${theme.typography.sizes.xxl}px;
|
||||
}
|
||||
|
||||
.dashboard--editing .dashboard-grid & {
|
||||
&:after {
|
||||
border: 1px dashed transparent;
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&:hover:after {
|
||||
border: 1px dashed ${theme.colors.primary.base};
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
|
||||
.dashboard--editing .dragdroppable-row & {
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
/**
|
||||
* grids add margin between items, so don't double pad within columns
|
||||
* we'll not worry about double padding on top as it can serve as a visual separator
|
||||
*/
|
||||
.grid-column > :not(:last-child) & {
|
||||
margin-bottom: ${theme.gridUnit * -4}px;
|
||||
}
|
||||
|
||||
.background--white &,
|
||||
&.background--white,
|
||||
.dashboard-component-tabs & {
|
||||
padding-left: ${theme.gridUnit * 4}px;
|
||||
padding-right: ${theme.gridUnit * 4}px;
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
class Header extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
|
@ -154,7 +213,7 @@ class Header extends React.PureComponent {
|
|||
]}
|
||||
editMode={editMode}
|
||||
>
|
||||
<div
|
||||
<HeaderStyles
|
||||
className={cx(
|
||||
'dashboard-component',
|
||||
'dashboard-component-header',
|
||||
|
|
@ -178,7 +237,7 @@ class Header extends React.PureComponent {
|
|||
{!editMode && (
|
||||
<AnchorLink id={component.id} dashboardId={dashboardId} />
|
||||
)}
|
||||
</div>
|
||||
</HeaderStyles>
|
||||
</WithPopoverMenu>
|
||||
|
||||
{dropIndicatorProps && <div {...dropIndicatorProps} />}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import PropTypes from 'prop-types';
|
|||
import { connect } from 'react-redux';
|
||||
import cx from 'classnames';
|
||||
|
||||
import { t, SafeMarkdown } from '@superset-ui/core';
|
||||
import { css, styled, t, SafeMarkdown } from '@superset-ui/core';
|
||||
import { Logger, LOG_ACTIONS_RENDER_CHART } from 'src/logger/LogUtils';
|
||||
import { MarkdownEditor } from 'src/components/AsyncAceEditor';
|
||||
|
||||
|
|
@ -83,6 +83,37 @@ Click here to learn more about [markdown formatting](https://bit.ly/1dQOfRK)`;
|
|||
|
||||
const MARKDOWN_ERROR_MESSAGE = t('This markdown component has an error.');
|
||||
|
||||
const MarkdownStyles = styled.div`
|
||||
${({ theme }) => css`
|
||||
&.dashboard-markdown {
|
||||
overflow: hidden;
|
||||
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-weight: ${theme.typography.weights.normal};
|
||||
}
|
||||
|
||||
h5 {
|
||||
color: ${theme.colors.grayscale.base};
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: ${theme.typography.sizes.s}px;
|
||||
}
|
||||
|
||||
.dashboard-component-chart-holder {
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.dashboard--editing & {
|
||||
cursor: move;
|
||||
}
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
class Markdown extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
|
@ -322,7 +353,7 @@ class Markdown extends React.PureComponent {
|
|||
]}
|
||||
editMode={editMode}
|
||||
>
|
||||
<div
|
||||
<MarkdownStyles
|
||||
data-test="dashboard-markdown-editor"
|
||||
className={cx(
|
||||
'dashboard-markdown',
|
||||
|
|
@ -363,7 +394,7 @@ class Markdown extends React.PureComponent {
|
|||
: this.renderPreviewMode()}
|
||||
</div>
|
||||
</ResizableContainer>
|
||||
</div>
|
||||
</MarkdownStyles>
|
||||
{dropIndicatorProps && <div {...dropIndicatorProps} />}
|
||||
</WithPopoverMenu>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,13 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import cx from 'classnames';
|
||||
import { FeatureFlag, isFeatureEnabled } from '@superset-ui/core';
|
||||
import {
|
||||
css,
|
||||
FeatureFlag,
|
||||
isFeatureEnabled,
|
||||
styled,
|
||||
t,
|
||||
} from '@superset-ui/core';
|
||||
|
||||
import DragDroppable from 'src/dashboard/components/dnd/DragDroppable';
|
||||
import DragHandle from 'src/dashboard/components/dnd/DragHandle';
|
||||
|
|
@ -58,6 +64,36 @@ const propTypes = {
|
|||
updateComponents: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
const GridRow = styled.div`
|
||||
${({ theme }) => css`
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
align-items: flex-start;
|
||||
width: 100%;
|
||||
height: fit-content;
|
||||
|
||||
& > :not(:last-child):not(.hover-menu) {
|
||||
margin-right: ${theme.gridUnit * 4}px;
|
||||
}
|
||||
|
||||
&.grid-row--empty {
|
||||
min-height: ${theme.gridUnit * 25}px;
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
const emptyRowContentStyles = theme => css`
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: ${theme.colors.text.label};
|
||||
`;
|
||||
|
||||
class Row extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
|
@ -201,7 +237,7 @@ class Row extends React.PureComponent {
|
|||
/>
|
||||
</HoverMenu>
|
||||
)}
|
||||
<div
|
||||
<GridRow
|
||||
className={cx(
|
||||
'grid-row',
|
||||
rowItems.length === 0 && 'grid-row--empty',
|
||||
|
|
@ -210,28 +246,32 @@ class Row extends React.PureComponent {
|
|||
data-test={`grid-row-${backgroundStyle.className}`}
|
||||
ref={this.containerRef}
|
||||
>
|
||||
{rowItems.map((componentId, itemIndex) => (
|
||||
<DashboardComponent
|
||||
key={componentId}
|
||||
id={componentId}
|
||||
parentId={rowComponent.id}
|
||||
depth={depth + 1}
|
||||
index={itemIndex}
|
||||
availableColumnCount={
|
||||
availableColumnCount - occupiedColumnCount
|
||||
}
|
||||
columnWidth={columnWidth}
|
||||
onResizeStart={onResizeStart}
|
||||
onResize={onResize}
|
||||
onResizeStop={onResizeStop}
|
||||
isComponentVisible={isComponentVisible}
|
||||
onChangeTab={onChangeTab}
|
||||
isInView={this.state.isInView}
|
||||
/>
|
||||
))}
|
||||
{rowItems.length === 0 ? (
|
||||
<div css={emptyRowContentStyles}>{t('Empty row')}</div>
|
||||
) : (
|
||||
rowItems.map((componentId, itemIndex) => (
|
||||
<DashboardComponent
|
||||
key={componentId}
|
||||
id={componentId}
|
||||
parentId={rowComponent.id}
|
||||
depth={depth + 1}
|
||||
index={itemIndex}
|
||||
availableColumnCount={
|
||||
availableColumnCount - occupiedColumnCount
|
||||
}
|
||||
columnWidth={columnWidth}
|
||||
onResizeStart={onResizeStart}
|
||||
onResize={onResize}
|
||||
onResizeStop={onResizeStop}
|
||||
isComponentVisible={isComponentVisible}
|
||||
onChangeTab={onChangeTab}
|
||||
isInView={this.state.isInView}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
|
||||
{dropIndicatorProps && <div {...dropIndicatorProps} />}
|
||||
</div>
|
||||
</GridRow>
|
||||
</WithPopoverMenu>
|
||||
)}
|
||||
</DragDroppable>
|
||||
|
|
|
|||
|
|
@ -19,10 +19,11 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import cx from 'classnames';
|
||||
import { css, styled } from '@superset-ui/core';
|
||||
|
||||
import DragDroppable from '../../dnd/DragDroppable';
|
||||
import { NEW_COMPONENTS_SOURCE_ID } from '../../../util/constants';
|
||||
import { NEW_COMPONENT_SOURCE_TYPE } from '../../../util/componentTypes';
|
||||
import DragDroppable from 'src/dashboard/components/dnd/DragDroppable';
|
||||
import { NEW_COMPONENTS_SOURCE_ID } from 'src/dashboard/util/constants';
|
||||
import { NEW_COMPONENT_SOURCE_TYPE } from 'src/dashboard/util/componentTypes';
|
||||
|
||||
const propTypes = {
|
||||
id: PropTypes.string.isRequired,
|
||||
|
|
@ -35,6 +36,53 @@ const defaultProps = {
|
|||
className: null,
|
||||
};
|
||||
|
||||
const NewComponent = styled.div`
|
||||
${({ theme }) => css`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
padding: ${theme.gridUnit * 4}px;
|
||||
background: ${theme.colors.grayscale.light5};
|
||||
cursor: move;
|
||||
|
||||
&:not(.static):hover {
|
||||
background: ${theme.colors.grayscale.light4};
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
const NewComponentPlaceholder = styled.div`
|
||||
${({ theme }) => css`
|
||||
position: relative;
|
||||
background: ${theme.colors.grayscale.light4};
|
||||
width: ${theme.gridUnit * 10}px;
|
||||
height: ${theme.gridUnit * 10}px;
|
||||
margin-right: ${theme.gridUnit * 4}px;
|
||||
border: 1px solid ${theme.colors.grayscale.light5};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: ${theme.colors.text.label};
|
||||
font-size: ${theme.typography.sizes.xxl}px;
|
||||
|
||||
&.fa-window-restore {
|
||||
font-size: ${theme.typography.sizes.l}px;
|
||||
}
|
||||
|
||||
&.fa-area-chart {
|
||||
font-size: ${theme.typography.sizes.xl}px;
|
||||
}
|
||||
|
||||
&.divider-placeholder:after {
|
||||
content: '';
|
||||
height: 2px;
|
||||
width: 100%;
|
||||
background-color: ${theme.colors.grayscale.light2};
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
export default class DraggableNewComponent extends React.PureComponent {
|
||||
render() {
|
||||
const { label, id, type, className, meta } = this.props;
|
||||
|
|
@ -50,14 +98,12 @@ export default class DraggableNewComponent extends React.PureComponent {
|
|||
editMode
|
||||
>
|
||||
{({ dragSourceRef }) => (
|
||||
<div
|
||||
ref={dragSourceRef}
|
||||
className="new-component"
|
||||
data-test="new-component"
|
||||
>
|
||||
<div className={cx('new-component-placeholder', className)} />
|
||||
<NewComponent ref={dragSourceRef} data-test="new-component">
|
||||
<NewComponentPlaceholder
|
||||
className={cx('new-component-placeholder', className)}
|
||||
/>
|
||||
{label}
|
||||
</div>
|
||||
</NewComponent>
|
||||
)}
|
||||
</DragDroppable>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import { styledMount as mount } from 'spec/helpers/theming';
|
||||
import { DndProvider } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
|
||||
|
|
@ -73,7 +73,9 @@ describe('DraggableNewComponent', () => {
|
|||
|
||||
it('should render the passed label', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find('.new-component').text()).toBe(props.label);
|
||||
expect(
|
||||
wrapper.find('[data-test="new-component"]').at(0).childAt(0).text(),
|
||||
).toBe(props.label);
|
||||
});
|
||||
|
||||
it('should add the passed className', () => {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
import React from 'react';
|
||||
import cx from 'classnames';
|
||||
import { css, styled } from '@superset-ui/core';
|
||||
|
||||
import backgroundStyleOptions from 'src/dashboard/util/backgroundStyleOptions';
|
||||
import PopoverDropdown, {
|
||||
|
|
@ -31,19 +32,63 @@ interface BackgroundStyleDropdownProps {
|
|||
onChange: OnChangeHandler;
|
||||
}
|
||||
|
||||
const BackgroundStyleOption = styled.div`
|
||||
${({ theme }) => css`
|
||||
display: inline-block;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
margin-right: ${theme.gridUnit * 2}px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
&.background--white {
|
||||
padding-left: 0;
|
||||
background: transparent;
|
||||
|
||||
&:before {
|
||||
background: ${theme.colors.grayscale.light5};
|
||||
border: 1px solid ${theme.colors.grayscale.light2};
|
||||
}
|
||||
}
|
||||
|
||||
/* Create the transparent rect icon */
|
||||
&.background--transparent:before {
|
||||
background-image: linear-gradient(
|
||||
45deg,
|
||||
${theme.colors.text.label} 25%,
|
||||
transparent 25%
|
||||
),
|
||||
linear-gradient(-45deg, ${theme.colors.text.label} 25%, transparent 25%),
|
||||
linear-gradient(45deg, transparent 75%, ${theme.colors.text.label} 75%),
|
||||
linear-gradient(-45deg, transparent 75%, ${theme.colors.text.label} 75%);
|
||||
background-size: ${theme.gridUnit * 2}px ${theme.gridUnit * 2}px;
|
||||
background-position: 0 0, 0 ${theme.gridUnit}px,
|
||||
${theme.gridUnit}px ${-theme.gridUnit}px, ${-theme.gridUnit}px 0px;
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
function renderButton(option: OptionProps) {
|
||||
return (
|
||||
<div className={cx('background-style-option', option.className)}>
|
||||
<BackgroundStyleOption
|
||||
className={cx('background-style-option', option.className)}
|
||||
>
|
||||
{`${option.label} background`}
|
||||
</div>
|
||||
</BackgroundStyleOption>
|
||||
);
|
||||
}
|
||||
|
||||
function renderOption(option: OptionProps) {
|
||||
return (
|
||||
<div className={cx('background-style-option', option.className)}>
|
||||
<BackgroundStyleOption
|
||||
className={cx('background-style-option', option.className)}
|
||||
>
|
||||
{option.label}
|
||||
</div>
|
||||
</BackgroundStyleOption>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
import React from 'react';
|
||||
import cx from 'classnames';
|
||||
import { addAlpha, css, styled } from '@superset-ui/core';
|
||||
|
||||
type ShouldFocusContainer = HTMLDivElement & {
|
||||
contains: (event_target: EventTarget & HTMLElement) => Boolean;
|
||||
|
|
@ -41,6 +42,67 @@ interface WithPopoverMenuState {
|
|||
isFocused: Boolean;
|
||||
}
|
||||
|
||||
const WithPopoverMenuStyles = styled.div`
|
||||
${({ theme }) => css`
|
||||
position: relative;
|
||||
outline: none;
|
||||
|
||||
&.with-popover-menu--focused:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 2px solid ${theme.colors.primary.base};
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.dashboard-component-tabs li &.with-popover-menu--focused:after {
|
||||
top: ${theme.gridUnit * -3}px;
|
||||
left: ${theme.gridUnit * -2}px;
|
||||
width: calc(100% + ${theme.gridUnit * 4}px);
|
||||
height: calc(100% + ${theme.gridUnit * 7}px);
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
const PopoverMenuStyles = styled.div`
|
||||
${({ theme }) => css`
|
||||
position: absolute;
|
||||
flex-wrap: nowrap;
|
||||
left: 1px;
|
||||
top: -42px;
|
||||
height: ${theme.gridUnit * 10}px;
|
||||
padding: 0 ${theme.gridUnit * 4}px;
|
||||
background: ${theme.colors.grayscale.light5};
|
||||
box-shadow: 0 1px 2px 1px
|
||||
${addAlpha(
|
||||
theme.colors.grayscale.dark2,
|
||||
parseFloat(theme.opacity.mediumLight) / 100,
|
||||
)};
|
||||
font-size: ${theme.typography.sizes.m}px;
|
||||
cursor: default;
|
||||
z-index: 3000;
|
||||
|
||||
&,
|
||||
.menu-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* vertical spacer after each menu item */
|
||||
.menu-item:not(:last-child):after {
|
||||
content: '';
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
background: ${theme.colors.grayscale.light2};
|
||||
margin: 0 ${theme.gridUnit * 4}px;
|
||||
}
|
||||
`}
|
||||
`;
|
||||
|
||||
export default class WithPopoverMenu extends React.PureComponent<
|
||||
WithPopoverMenuProps,
|
||||
WithPopoverMenuState
|
||||
|
|
@ -126,7 +188,7 @@ export default class WithPopoverMenu extends React.PureComponent<
|
|||
const { isFocused } = this.state;
|
||||
|
||||
return (
|
||||
<div
|
||||
<WithPopoverMenuStyles
|
||||
ref={this.setRef}
|
||||
onClick={this.handleClick}
|
||||
role="none"
|
||||
|
|
@ -138,15 +200,15 @@ export default class WithPopoverMenu extends React.PureComponent<
|
|||
>
|
||||
{children}
|
||||
{editMode && isFocused && (menuItems?.length ?? 0) > 0 && (
|
||||
<div className="popover-menu">
|
||||
<PopoverMenuStyles>
|
||||
{menuItems.map((node: React.ReactNode, i: Number) => (
|
||||
<div className="menu-item" key={`menu-item-${i}`}>
|
||||
{node}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</PopoverMenuStyles>
|
||||
)}
|
||||
</div>
|
||||
</WithPopoverMenuStyles>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ import React from 'react';
|
|||
import { css } from '@emotion/react';
|
||||
import { FilterBarOrientation } from 'src/dashboard/types';
|
||||
import FilterDivider from './FilterDivider';
|
||||
import 'src/dashboard/stylesheets/index.less';
|
||||
import { FilterDividerProps } from './types';
|
||||
|
||||
export default {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import React from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import { Resizable } from 're-resizable';
|
||||
import cx from 'classnames';
|
||||
import { css, styled } from '@superset-ui/core';
|
||||
|
||||
import ResizableHandle from './ResizableHandle';
|
||||
import resizableConfig from '../../util/resizableConfig';
|
||||
|
|
@ -80,6 +81,93 @@ const HANDLE_CLASSES = {
|
|||
right: 'resizable-container-handle--right',
|
||||
bottom: 'resizable-container-handle--bottom',
|
||||
};
|
||||
|
||||
const StyledResizable = styled(Resizable)`
|
||||
${({ theme }) => css`
|
||||
&.resizable-container {
|
||||
background-color: transparent;
|
||||
position: relative;
|
||||
|
||||
/* re-resizable sets an empty div to 100% width and height, which doesn't
|
||||
play well with many 100% height containers we need */
|
||||
|
||||
& ~ div {
|
||||
width: auto !important;
|
||||
height: auto !important;
|
||||
}
|
||||
}
|
||||
|
||||
&.resizable-container--resizing {
|
||||
/* after ensures border visibility on top of any children */
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-shadow: inset 0 0 0 2px ${theme.colors.primary.base};
|
||||
}
|
||||
|
||||
& > span .resize-handle {
|
||||
border-color: ${theme.colors.primary.base};
|
||||
}
|
||||
}
|
||||
|
||||
.resize-handle {
|
||||
opacity: 0;
|
||||
z-index: 10;
|
||||
|
||||
&--bottom-right {
|
||||
position: absolute;
|
||||
border-right: 1px solid ${theme.colors.text.label};
|
||||
border-bottom: 1px solid ${theme.colors.text.label};
|
||||
right: ${theme.gridUnit * 4}px;
|
||||
bottom: ${theme.gridUnit * 4}px;
|
||||
width: ${theme.gridUnit * 2}px;
|
||||
height: ${theme.gridUnit * 2}px;
|
||||
}
|
||||
|
||||
&--right {
|
||||
width: ${theme.gridUnit / 2}px;
|
||||
height: ${theme.gridUnit * 5}px;
|
||||
right: ${theme.gridUnit}px;
|
||||
top: 50%;
|
||||
transform: translate(0, -50%);
|
||||
position: absolute;
|
||||
border-left: 1px solid ${theme.colors.text.label};
|
||||
border-right: 1px solid ${theme.colors.text.label};
|
||||
}
|
||||
|
||||
&--bottom {
|
||||
height: ${theme.gridUnit / 2}px;
|
||||
width: ${theme.gridUnit * 5}px;
|
||||
bottom: ${theme.gridUnit}px;
|
||||
left: 50%;
|
||||
transform: translate(-50%);
|
||||
position: absolute;
|
||||
border-top: 1px solid ${theme.colors.text.label};
|
||||
border-bottom: 1px solid ${theme.colors.text.label};
|
||||
}
|
||||
}
|
||||
`}
|
||||
|
||||
&.resizable-container:hover .resize-handle,
|
||||
&.resizable-container--resizing .resize-handle {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.dragdroppable-column & .resizable-container-handle--right {
|
||||
/* override the default because the inner column's handle's mouse target is very small */
|
||||
right: 0 !important;
|
||||
}
|
||||
|
||||
& .resizable-container-handle--bottom {
|
||||
bottom: 0 !important;
|
||||
}
|
||||
`;
|
||||
|
||||
class ResizableContainer extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
|
@ -186,7 +274,7 @@ class ResizableContainer extends React.PureComponent {
|
|||
const { isResizing } = this.state;
|
||||
|
||||
return (
|
||||
<Resizable
|
||||
<StyledResizable
|
||||
enable={enableConfig}
|
||||
grid={SNAP_TO_GRID}
|
||||
minWidth={
|
||||
|
|
@ -228,7 +316,7 @@ class ResizableContainer extends React.PureComponent {
|
|||
handleClasses={HANDLE_CLASSES}
|
||||
>
|
||||
{children}
|
||||
</Resizable>
|
||||
</StyledResizable>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ import {
|
|||
getFilterValue,
|
||||
getPermalinkValue,
|
||||
} from 'src/dashboard/components/nativeFilters/FilterBar/keyValue';
|
||||
import { filterCardPopoverStyle } from 'src/dashboard/styles';
|
||||
import { filterCardPopoverStyle, headerStyles } from 'src/dashboard/styles';
|
||||
import { DashboardContextForExplore } from 'src/types/DashboardContextForExplore';
|
||||
import shortid from 'shortid';
|
||||
import { RootState } from '../types';
|
||||
|
|
@ -365,7 +365,7 @@ export const DashboardPage: FC<PageProps> = ({ idOrSlug }: PageProps) => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<Global styles={filterCardPopoverStyle(theme)} />
|
||||
<Global styles={[filterCardPopoverStyle(theme), headerStyles(theme)]} />
|
||||
<FilterBoxMigrationModal
|
||||
show={filterboxMigrationState === FILTER_BOX_MIGRATION_STATES.UNDECIDED}
|
||||
hideFooter={!isMigrationEnabled}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,39 @@
|
|||
*/
|
||||
import { css, SupersetTheme } from '@superset-ui/core';
|
||||
|
||||
export const headerStyles = (theme: SupersetTheme) => css`
|
||||
body {
|
||||
h1 {
|
||||
font-weight: ${theme.typography.weights.bold};
|
||||
line-height: 1.4;
|
||||
font-size: ${theme.typography.sizes.xxl}px;
|
||||
letter-spacing: -0.2px;
|
||||
margin-top: ${theme.gridUnit * 3}px;
|
||||
margin-bottom: ${theme.gridUnit * 3}px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-weight: ${theme.typography.weights.bold};
|
||||
line-height: 1.4;
|
||||
font-size: ${theme.typography.sizes.xl}px;
|
||||
margin-top: ${theme.gridUnit * 3}px;
|
||||
margin-bottom: ${theme.gridUnit * 2}px;
|
||||
}
|
||||
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-weight: ${theme.typography.weights.bold};
|
||||
line-height: 1.4;
|
||||
font-size: ${theme.typography.sizes.l}px;
|
||||
letter-spacing: 0.2px;
|
||||
margin-top: ${theme.gridUnit * 2}px;
|
||||
margin-bottom: ${theme.gridUnit}px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const filterCardPopoverStyle = (theme: SupersetTheme) => css`
|
||||
.filter-card-popover {
|
||||
width: 240px;
|
||||
|
|
|
|||
|
|
@ -1,49 +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.
|
||||
*/
|
||||
.dashboard {
|
||||
position: relative;
|
||||
color: @almost-black;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* only top-level tabs have popover, give it more padding to match header + tabs */
|
||||
.dashboard > .with-popover-menu > .popover-menu {
|
||||
left: 24px;
|
||||
}
|
||||
|
||||
/* drop shadow for top-level tabs only */
|
||||
.dashboard .dashboard-component-tabs {
|
||||
box-shadow: 0 4px 4px 0 fade(@darkest, @opacity-light);
|
||||
padding-left: 8px; /* note this is added to tab-level padding, to match header */
|
||||
}
|
||||
|
||||
.dropdown-toggle.btn.btn-primary .caret {
|
||||
color: @lightest;
|
||||
}
|
||||
|
||||
.background--transparent {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.background--white {
|
||||
background-color: @lightest;
|
||||
}
|
||||
|
|
@ -1,150 +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.
|
||||
*/
|
||||
.dashboard-component-chart-holder {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
color: @gray-dark;
|
||||
background-color: @lightest;
|
||||
position: relative;
|
||||
padding: 16px;
|
||||
overflow-y: visible;
|
||||
|
||||
// transitionable traits for when a filter is being actively focused
|
||||
transition: opacity 0.2s, border-color 0.2s, box-shadow 0.2s;
|
||||
border: 2px solid transparent;
|
||||
|
||||
.missing-chart-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
overflow-y: auto;
|
||||
justify-content: center;
|
||||
|
||||
.missing-chart-body {
|
||||
font-size: @font-size-s;
|
||||
position: relative;
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
&.fade-in {
|
||||
border-radius: @border-radius-large;
|
||||
box-shadow: inset 0 0 0 2px @shadow-highlight,
|
||||
0 0 0 3px fade(@shadow-highlight, @opacity-light);
|
||||
transition: box-shadow 0.2s ease-in-out, opacity 0.2s ease-in-out,
|
||||
border-color 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
&.fade-out {
|
||||
border-radius: @border-radius-large;
|
||||
box-shadow: none;
|
||||
transition: box-shadow 0.2s ease-in-out, opacity 0.2s ease-in-out,
|
||||
border-color 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
.dashboard-chart {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dashboard-chart.dashboard-chart--overflowable {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.dashboard--editing {
|
||||
.dashboard-component-chart-holder {
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
z-index: @z-index-chart;
|
||||
pointer-events: none;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
&:hover:after {
|
||||
border: 1px dashed @indicator-color;
|
||||
z-index: @z-index-chart--dragging;
|
||||
}
|
||||
}
|
||||
|
||||
.resizable-container {
|
||||
&:hover,
|
||||
&.resizable-container--resizing:hover {
|
||||
& > .dashboard-component-chart-holder:after {
|
||||
border: 1px dashed @indicator-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.resizable-container .dashboard-component-chart-holder {
|
||||
.dashboard-chart {
|
||||
.chart-container {
|
||||
cursor: move;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.slice_container {
|
||||
/* disable chart interactions in edit mode */
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover .dashboard-chart .chart-container {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dot {
|
||||
@dot-diameter: 4px;
|
||||
|
||||
height: @dot-diameter;
|
||||
width: @dot-diameter;
|
||||
border-radius: @dot-diameter / 2;
|
||||
margin: @dot-diameter / 2 0;
|
||||
|
||||
background-color: @gray;
|
||||
display: inline-block;
|
||||
|
||||
a[role='menuitem'] & {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.time-filter-tabs > .nav-tabs {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.time-filter-tabs > .nav-tabs > li > a {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.full-size {
|
||||
position: fixed;
|
||||
z-index: @z-index-max;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
|
@ -1,64 +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.
|
||||
*/
|
||||
.grid-column {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* gutters between elements in a column */
|
||||
.grid-column > :not(:only-child):not(.hover-menu):not(:last-child) {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.dashboard--editing {
|
||||
.grid-column:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: @z-index-chart;
|
||||
pointer-events: none;
|
||||
border: 1px dashed @gray-light;
|
||||
}
|
||||
|
||||
.resizable-container.resizable-container--resizing:hover > .grid-column:after,
|
||||
.hover-menu:hover + .grid-column:after {
|
||||
border: 1px dashed @indicator-color;
|
||||
z-index: @z-index-chart--dragging;
|
||||
}
|
||||
}
|
||||
|
||||
.grid-column--empty {
|
||||
min-height: 100px;
|
||||
|
||||
&:before {
|
||||
content: 'Empty column';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: @gray-light;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,42 +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.
|
||||
*/
|
||||
.dashboard-component-divider {
|
||||
width: 100%;
|
||||
padding: 8px 0; /* this is padding not margin to enable a larger mouse target */
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.dashboard-component-divider:after {
|
||||
content: '';
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
background-color: @gray-light;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.new-component-placeholder.divider-placeholder:after {
|
||||
content: '';
|
||||
height: 2px;
|
||||
width: 100%;
|
||||
background-color: @gray-light;
|
||||
}
|
||||
|
||||
.dragdroppable .dashboard-component-divider {
|
||||
cursor: move;
|
||||
}
|
||||
|
|
@ -1,87 +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.
|
||||
*/
|
||||
.dashboard-component-header {
|
||||
width: 100%;
|
||||
font-weight: @font-weight-bold;
|
||||
padding: 16px 0;
|
||||
color: @almost-black;
|
||||
}
|
||||
|
||||
.dashboard--editing {
|
||||
.dashboard-grid {
|
||||
.dashboard-component-header {
|
||||
&:after {
|
||||
border: 1px dashed transparent;
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: @z-index-chart;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&:hover:after {
|
||||
border: 1px dashed @indicator-color;
|
||||
z-index: @z-index-chart--dragging;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dragdroppable-row .dashboard-component-header {
|
||||
cursor: move;
|
||||
}
|
||||
}
|
||||
|
||||
.header-style-option {
|
||||
font-weight: @font-weight-bold;
|
||||
color: @almost-black;
|
||||
}
|
||||
|
||||
.dashboard--editing
|
||||
|
||||
/* note: sizes should be a multiple of the 8px grid unit so that rows in the grid align */
|
||||
.header-small {
|
||||
font-size: @font-size-l;
|
||||
}
|
||||
|
||||
.header-medium {
|
||||
font-size: @font-size-xl;
|
||||
}
|
||||
|
||||
.header-large {
|
||||
font-size: @font-size-xxl;
|
||||
}
|
||||
|
||||
.background--white .dashboard-component-header,
|
||||
.dashboard-component-header.background--white,
|
||||
.dashboard-component-tabs .dashboard-component-header,
|
||||
.dashboard-component-tabs .dashboard-component-divider {
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
}
|
||||
|
||||
/*
|
||||
* grids add margin between items, so don't double pad within columns
|
||||
* we'll not worry about double padding on top as it can serve as a visual separator
|
||||
*/
|
||||
.grid-column > :not(:only-child):not(:last-child) .dashboard-component-header {
|
||||
margin-bottom: -16px;
|
||||
}
|
||||
|
|
@ -1,25 +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 './chart.less';
|
||||
@import './column.less';
|
||||
@import './divider.less';
|
||||
@import './header.less';
|
||||
@import './new-component.less';
|
||||
@import './row.less';
|
||||
@import './markdown.less';
|
||||
|
|
@ -1,57 +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.
|
||||
*/
|
||||
.dashboard-markdown {
|
||||
overflow: hidden;
|
||||
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-weight: @font-weight-normal;
|
||||
}
|
||||
|
||||
h5 {
|
||||
color: @gray-heading;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-size: @font-size-s;
|
||||
}
|
||||
|
||||
.dashboard-component-chart-holder {
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.dashboard--editing & {
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
#ace-editor {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* maximize editing space */
|
||||
.dashboard-markdown--editing {
|
||||
.dashboard-component-chart-holder {
|
||||
.with-popover-menu--focused & {
|
||||
padding: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,55 +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 '../../../assets/stylesheets/less/variables.less';
|
||||
|
||||
.new-component {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
align-items: center;
|
||||
padding: 16px;
|
||||
background: @lightest;
|
||||
cursor: move;
|
||||
|
||||
&:not(.static):hover {
|
||||
background: @gray-bg;
|
||||
}
|
||||
}
|
||||
|
||||
.new-component-placeholder {
|
||||
position: relative;
|
||||
background: @gray-bg;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin-right: 16px;
|
||||
border: 1px solid @lightest;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: @gray;
|
||||
font-size: @font-size-xxl;
|
||||
|
||||
&.fa-window-restore {
|
||||
font-size: @font-size-l;
|
||||
}
|
||||
|
||||
&.fa-area-chart {
|
||||
font-size: @font-size-xl;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,92 +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.
|
||||
*/
|
||||
.grid-row {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
align-items: flex-start;
|
||||
width: 100%;
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
/* gutters between elements in a row */
|
||||
.grid-row > :not(:only-child):not(:last-child):not(.hover-menu) {
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
/* hover indicator */
|
||||
.dashboard--editing {
|
||||
.grid-row:after,
|
||||
.dashboard-component-tabs > .hover-menu:hover + div:after {
|
||||
border: 1px dashed transparent;
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: @z-index-chart;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.resizable-container.resizable-container--resizing:hover > .grid-row:after,
|
||||
.hover-menu:hover + .grid-row:after,
|
||||
.dashboard-component-tabs > .hover-menu:hover + div:after {
|
||||
border: 1px dashed @indicator-color;
|
||||
z-index: @z-index-chart--dragging;
|
||||
}
|
||||
|
||||
.grid-row:after,
|
||||
.dashboard-component-tabs > .hover-menu + div:after {
|
||||
border: 1px dashed @gray-light;
|
||||
}
|
||||
|
||||
/* provide hit area in case row contents is edge to edge */
|
||||
.dashboard-component-tabs-content {
|
||||
.dragdroppable-row {
|
||||
padding-top: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* gutters between rows within tab */
|
||||
.dashboard-component-tabs-content
|
||||
> div:not(:only-child):not(:last-child):not(.empty-droptarget) {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.grid-row.grid-row--empty {
|
||||
/* this centers the empty note content */
|
||||
align-items: center;
|
||||
height: 100px;
|
||||
|
||||
&:before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
content: 'Empty row';
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
color: @gray;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,164 +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.
|
||||
*/
|
||||
/* header has mysterious extra margin */
|
||||
header.top {
|
||||
margin-bottom: 2px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
body {
|
||||
h1 {
|
||||
font-weight: @font-weight-bold;
|
||||
line-height: @line-height-base;
|
||||
font-size: @font-size-xxl;
|
||||
letter-spacing: -0.2px;
|
||||
margin-top: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-weight: @font-weight-bold;
|
||||
line-height: @line-height-base;
|
||||
font-size: @font-size-xl;
|
||||
margin-top: 12px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-weight: @font-weight-bold;
|
||||
line-height: @line-height-base;
|
||||
font-size: @font-size-l;
|
||||
letter-spacing: 0.2px;
|
||||
margin-top: 8px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0 0 8px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.dashboard .chart-header {
|
||||
font-size: @font-size-l;
|
||||
font-weight: @font-weight-bold;
|
||||
margin-bottom: 4px;
|
||||
display: flex;
|
||||
max-width: 100%;
|
||||
align-items: flex-start;
|
||||
min-height: 0;
|
||||
|
||||
& > .header-title {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 100%;
|
||||
flex-grow: 1;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
|
||||
& > span.ant-tooltip-open {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
|
||||
& > .header-controls {
|
||||
display: flex;
|
||||
|
||||
& > * {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown.btn-group {
|
||||
pointer-events: none;
|
||||
vertical-align: top;
|
||||
& > * {
|
||||
pointer-events: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-toggle.btn.btn-default {
|
||||
background: none;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.dropdown-menu.dropdown-menu-right {
|
||||
top: 20px;
|
||||
}
|
||||
|
||||
.divider {
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.refresh-tooltip {
|
||||
display: block;
|
||||
height: 16px;
|
||||
margin: 3px 0;
|
||||
color: @gray;
|
||||
}
|
||||
}
|
||||
|
||||
.dashboard .chart-header,
|
||||
.dashboard .dashboard-header {
|
||||
.dropdown-menu {
|
||||
padding: 9px 0;
|
||||
}
|
||||
|
||||
.dropdown-menu li {
|
||||
font-weight: @font-weight-normal;
|
||||
}
|
||||
}
|
||||
|
||||
.react-bs-container-body {
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.hidden,
|
||||
#pageDropDown {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.separator .chart-container {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.dashboard .title {
|
||||
margin: 0 20px;
|
||||
}
|
||||
|
||||
.slice_container .alert {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
i.danger {
|
||||
color: @danger;
|
||||
}
|
||||
|
||||
i.warning {
|
||||
color: @warning;
|
||||
}
|
||||
|
|
@ -1,130 +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.
|
||||
*/
|
||||
.dragdroppable {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
// Fixes ISSUE-12181 - before in chart's contract-trigger breaks drag and drop mode
|
||||
.dashboard--editing {
|
||||
.contract-trigger:before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.dragdroppable--dragging {
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.dragdroppable-row {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dragdroppable-column {
|
||||
.resizable-container {
|
||||
span {
|
||||
div {
|
||||
z-index: @z-index-above-dashboard-charts;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* drop indicators */
|
||||
.drop-indicator {
|
||||
display: block;
|
||||
background-color: @indicator-color;
|
||||
position: absolute;
|
||||
z-index: @z-index-above-dashboard-charts;
|
||||
}
|
||||
|
||||
.drop-indicator--top {
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 4px;
|
||||
width: 100%;
|
||||
min-width: 16px;
|
||||
}
|
||||
|
||||
.drop-indicator--bottom {
|
||||
top: 100%;
|
||||
left: 0;
|
||||
height: 4px;
|
||||
width: 100%;
|
||||
min-width: 16px;
|
||||
}
|
||||
|
||||
.empty-droptarget:first-child {
|
||||
.drop-indicator--bottom {
|
||||
top: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.drop-indicator--right {
|
||||
top: 0;
|
||||
left: 100%;
|
||||
height: 100%;
|
||||
width: 4px;
|
||||
min-height: 16px;
|
||||
}
|
||||
|
||||
.drop-indicator--left {
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 4px;
|
||||
min-height: 16px;
|
||||
}
|
||||
|
||||
/* empty drop targets */
|
||||
.dashboard-component-tabs-content {
|
||||
& > .empty-droptarget {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
& > .empty-droptarget:first-child {
|
||||
height: 14px;
|
||||
top: -2px;
|
||||
z-index: @z-index-above-dashboard-charts;
|
||||
}
|
||||
|
||||
& > .empty-droptarget:last-child {
|
||||
height: 12px;
|
||||
bottom: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.grid-content {
|
||||
/* note we don't do a :last-child selection because
|
||||
assuming bottom empty-droptarget is last child is fragile */
|
||||
& > .empty-droptarget {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
& > .empty-droptarget:first-child {
|
||||
height: 48px;
|
||||
margin-top: -24px;
|
||||
margin-bottom: -24px;
|
||||
}
|
||||
|
||||
& > .empty-droptarget:only-child {
|
||||
height: 80vh;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,259 +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 '../../assets/stylesheets/less/variables.less';
|
||||
|
||||
.filter-scope-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 80%;
|
||||
margin-right: -24px;
|
||||
font-size: @font-size-m;
|
||||
|
||||
.nav.nav-tabs {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.filter-scope-body {
|
||||
flex: 1;
|
||||
max-height: calc(100% - 128px);
|
||||
|
||||
.filter-field-pane,
|
||||
.filter-scope-pane {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
}
|
||||
|
||||
.warning-message {
|
||||
padding: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.filter-scope-header {
|
||||
height: 64px;
|
||||
border-bottom: 1px solid @gray-light;
|
||||
padding-left: 24px;
|
||||
margin-left: -24px;
|
||||
|
||||
h4 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.selected-fields {
|
||||
margin: 12px 0 16px;
|
||||
visibility: hidden;
|
||||
|
||||
&.multi-edit-mode {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.selected-scopes {
|
||||
padding-left: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filters-scope-selector {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
|
||||
a,
|
||||
a:active,
|
||||
a:hover {
|
||||
color: @almost-black;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.react-checkbox-tree .rct-icon.rct-icon-expand-all,
|
||||
.react-checkbox-tree .rct-icon.rct-icon-collapse-all {
|
||||
font-size: @font-size-m;
|
||||
font-family: @font-family-sans-serif;
|
||||
color: @brand-primary;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
}
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
.filter-field-pane {
|
||||
position: relative;
|
||||
width: 40%;
|
||||
padding: 16px 16px 16px 0;
|
||||
border-right: 1px solid @gray-light;
|
||||
|
||||
.filter-container {
|
||||
label {
|
||||
font-weight: @font-weight-normal;
|
||||
margin: 0 0 0 16px;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
|
||||
.filter-field-item {
|
||||
height: 35px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 24px;
|
||||
margin-left: -24px;
|
||||
|
||||
&.is-selected {
|
||||
border: 1px solid @gray-heading;
|
||||
border-radius: @border-radius-large;
|
||||
background-color: @gray-bg;
|
||||
margin-left: -25px;
|
||||
}
|
||||
}
|
||||
|
||||
.react-checkbox-tree {
|
||||
.rct-title .root {
|
||||
font-weight: @font-weight-bold;
|
||||
}
|
||||
|
||||
.rct-text {
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filter-scope-pane {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
padding: 16px 24px 16px 16px;
|
||||
}
|
||||
|
||||
.react-checkbox-tree {
|
||||
flex-direction: column;
|
||||
color: @almost-black;
|
||||
font-size: @font-size-m;
|
||||
|
||||
.filter-scope-type {
|
||||
padding: 8px 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&.chart {
|
||||
font-weight: @font-weight-normal;
|
||||
}
|
||||
|
||||
&.selected-filter {
|
||||
padding-left: 28px;
|
||||
position: relative;
|
||||
color: @gray-heading;
|
||||
|
||||
&::before {
|
||||
content: ' ';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: @border-radius-normal;
|
||||
margin-top: -9px;
|
||||
box-shadow: inset 0 0 0 2px @gray-light;
|
||||
background: #f2f2f2;
|
||||
}
|
||||
}
|
||||
|
||||
&.root {
|
||||
font-weight: @font-weight-bold;
|
||||
}
|
||||
}
|
||||
|
||||
.rct-checkbox {
|
||||
svg {
|
||||
position: relative;
|
||||
top: 3px;
|
||||
width: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
.rct-node-leaf {
|
||||
.rct-bare-label {
|
||||
&::before {
|
||||
padding-left: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.rct-options {
|
||||
text-align: left;
|
||||
margin-left: 0;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.rct-text {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.rct-title {
|
||||
display: block;
|
||||
}
|
||||
|
||||
// disable style from react-checkbox-trees.css
|
||||
.rct-node-clickable:hover,
|
||||
.rct-node-clickable:focus,
|
||||
label:hover,
|
||||
label:active {
|
||||
background: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.multi-edit-mode {
|
||||
&.filter-scope-pane {
|
||||
.rct-node.rct-node-leaf .filter-scope-type.filter_box {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.filter-field-item {
|
||||
padding: 0 16px 0 50px;
|
||||
margin-left: -50px;
|
||||
|
||||
&.is-selected {
|
||||
margin-left: -51px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.scope-search {
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
top: 16px;
|
||||
border-radius: @border-radius-large;
|
||||
border: 1px solid @gray-light;
|
||||
padding: 4px 8px 4px 8px;
|
||||
font-size: @font-size-m;
|
||||
outline: none;
|
||||
|
||||
&:focus {
|
||||
border: 1px solid @brand-primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,53 +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.
|
||||
*/
|
||||
|
||||
/* this is the ParentSize wrapper */
|
||||
.grid-container > div:first-child {
|
||||
height: inherit !important;
|
||||
}
|
||||
|
||||
.grid-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* gutters between rows */
|
||||
.grid-content > div:not(:only-child):not(:last-child):not(.empty-droptarget) {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
/* Editing guides */
|
||||
.grid-column-guide {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
min-height: 100%;
|
||||
background-color: fade(@indicator-color, @opacity-light);
|
||||
pointer-events: none;
|
||||
box-shadow: inset 0 0 0 1px fade(@indicator-color, @opacity-medium-heavy);
|
||||
}
|
||||
|
||||
.grid-row-guide {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 2;
|
||||
height: 2;
|
||||
background-color: @indicator-color;
|
||||
pointer-events: none;
|
||||
z-index: @z-index-above-dashboard-charts;
|
||||
}
|
||||
|
|
@ -1,140 +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.
|
||||
*/
|
||||
.with-popover-menu {
|
||||
position: relative;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.grid-row.grid-row--empty .with-popover-menu {
|
||||
/* drop indicator doesn't show up without this */
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.with-popover-menu--focused:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 2px solid @indicator-color;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.popover-menu {
|
||||
position: absolute;
|
||||
flex-wrap: nowrap;
|
||||
left: 1px;
|
||||
top: -42px;
|
||||
height: 40px;
|
||||
padding: 0 16px;
|
||||
background: @lightest;
|
||||
box-shadow: 0 1px 2px 1px fade(@darkest, @opacity-medium-light);
|
||||
font-size: @font-size-m;
|
||||
cursor: default;
|
||||
z-index: @z-index-max;
|
||||
|
||||
&,
|
||||
.menu-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* vertical spacer after each menu item */
|
||||
.menu-item:not(:only-child):not(:last-child):after {
|
||||
content: '';
|
||||
width: 1;
|
||||
height: 100%;
|
||||
background: @gray-light;
|
||||
margin: 0 16px;
|
||||
}
|
||||
}
|
||||
|
||||
/* the focus menu doesn't account for parent padding */
|
||||
.dashboard-component-tabs li .with-popover-menu--focused:after {
|
||||
top: -12px;
|
||||
left: -8px;
|
||||
width: ~'calc(100% + 16px)'; /* escape for .less */
|
||||
height: ~'calc(100% + 28px)';
|
||||
}
|
||||
|
||||
.dashboard-component-tabs li .popover-menu {
|
||||
top: -56px;
|
||||
left: -7px;
|
||||
}
|
||||
|
||||
.hover-dropdown .btn {
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
background: initial;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.hover-dropdown,
|
||||
.popover-menu {
|
||||
li.dropdown-item {
|
||||
&:hover a {
|
||||
background: @menu-hover;
|
||||
}
|
||||
|
||||
&.active a {
|
||||
background: @gray-light;
|
||||
font-weight: @font-weight-bold;
|
||||
color: @almost-black;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* background style menu */
|
||||
.background-style-option {
|
||||
display: inline-block;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
margin-right: 8px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
&.background--white {
|
||||
padding-left: 0;
|
||||
background: transparent;
|
||||
|
||||
&:before {
|
||||
background: @lightest;
|
||||
border: 1px solid @gray-light;
|
||||
}
|
||||
}
|
||||
|
||||
/* Create the transparent rect icon */
|
||||
&.background--transparent:before {
|
||||
background-image: linear-gradient(45deg, @gray 25%, transparent 25%),
|
||||
linear-gradient(-45deg, @gray 25%, transparent 25%),
|
||||
linear-gradient(45deg, transparent 75%, @gray 75%),
|
||||
linear-gradient(-45deg, transparent 75%, @gray 75%);
|
||||
background-size: 8px 8px;
|
||||
background-position: 0 0, 0 4px, 4px -4px, -4px 0px;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,105 +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.
|
||||
*/
|
||||
.resizable-container {
|
||||
background-color: transparent;
|
||||
position: relative;
|
||||
|
||||
/* re-resizable sets an empty div to 100% width and height, which doesn't
|
||||
play well with many 100% height containers we need
|
||||
*/
|
||||
& ~ div {
|
||||
width: auto !important;
|
||||
height: auto !important;
|
||||
}
|
||||
}
|
||||
|
||||
.resizable-container--resizing {
|
||||
/* after ensures border visibility on top of any children */
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-shadow: inset 0 0 0 2px @indicator-color;
|
||||
}
|
||||
|
||||
& > span .resize-handle {
|
||||
border-color: @indicator-color;
|
||||
}
|
||||
}
|
||||
|
||||
.resize-handle {
|
||||
opacity: 0;
|
||||
z-index: @z-index-above-dashboard-charts;
|
||||
|
||||
&--bottom-right {
|
||||
position: absolute;
|
||||
border: solid;
|
||||
border-width: 0 1.5px 1.5px 0;
|
||||
border-right-color: @gray;
|
||||
border-bottom-color: @gray;
|
||||
right: 16px;
|
||||
bottom: 16px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
&--right {
|
||||
width: 2px;
|
||||
height: 20px;
|
||||
right: 4px;
|
||||
top: 50%;
|
||||
transform: translate(0, -50%);
|
||||
position: absolute;
|
||||
border-left: 1px solid @gray;
|
||||
border-right: 1px solid @gray;
|
||||
}
|
||||
|
||||
&--bottom {
|
||||
height: 2px;
|
||||
width: 20px;
|
||||
bottom: 4px;
|
||||
left: 50%;
|
||||
transform: translate(-50%);
|
||||
position: absolute;
|
||||
border-top: 1px solid @gray;
|
||||
border-bottom: 1px solid @gray;
|
||||
}
|
||||
}
|
||||
|
||||
.resizable-container:hover .resize-handle,
|
||||
.resizable-container--resizing .resize-handle {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.dragdroppable-column .resizable-container-handle--right {
|
||||
/* override the default because the inner column's handle's mouse target is very small */
|
||||
right: 0 !important;
|
||||
}
|
||||
|
||||
.dragdroppable-column .dragdroppable-column .resizable-container-handle--right {
|
||||
/* override the default because the inner column's handle's mouse target is very small */
|
||||
right: 0 !important;
|
||||
}
|
||||
|
||||
.resizable-container-handle--bottom {
|
||||
bottom: 0 !important;
|
||||
}
|
||||
|
|
@ -1,137 +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 '../assets/stylesheets/less/variables.less';
|
||||
|
||||
.scrollbar-container {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.scrollbar-content {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
overflow-y: auto;
|
||||
margin-right: 0px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.edit-desc-icon {
|
||||
padding: 0 0 0 0.5em;
|
||||
font-size: @font-size-m;
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
float: left;
|
||||
margin-top: 0px;
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
.background-transparent {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.fa.expander {
|
||||
width: 15px;
|
||||
}
|
||||
|
||||
.list-group {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.color-popover.popover {
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.color-popover .popover-content {
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.column-option {
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
.datasource-container {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.adhoc-metric-edit-tabs > .nav-tabs {
|
||||
margin-bottom: 6px;
|
||||
|
||||
& > li > a {
|
||||
padding: 4px 4px 4px 4px;
|
||||
}
|
||||
}
|
||||
|
||||
#metrics-edit-popover {
|
||||
max-width: none;
|
||||
|
||||
.inline-editable {
|
||||
line-height: 30px; // hand-tweaked to match the height of the input
|
||||
}
|
||||
}
|
||||
|
||||
.adhoc-option {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.label-dropdown ul.dropdown-menu {
|
||||
position: fixed;
|
||||
top: auto;
|
||||
left: auto;
|
||||
margin: 20px 0 0;
|
||||
}
|
||||
|
||||
.label-btn:hover,
|
||||
.label-btn-label:hover {
|
||||
background-color: @gray-dark;
|
||||
}
|
||||
|
||||
.label-btn-label {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.adhoc-label-arrow {
|
||||
font-size: @font-size-s;
|
||||
margin-left: 3px;
|
||||
position: static;
|
||||
}
|
||||
|
||||
.time-filter-tabs > .nav-tabs {
|
||||
margin-bottom: 8px;
|
||||
|
||||
& > li > a {
|
||||
padding: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
div.section-header {
|
||||
font-size: @font-size-s;
|
||||
font-weight: @font-weight-bold;
|
||||
color: @gray-light5;
|
||||
margin-bottom: 0;
|
||||
margin-top: 0;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
|
@ -45,6 +45,8 @@ const StyledHeader = styled.header`
|
|||
${({ theme }) => `
|
||||
background-color: ${theme.colors.grayscale.light5};
|
||||
margin-bottom: 2px;
|
||||
z-index: 10;
|
||||
|
||||
&:nth-last-of-type(2) nav {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue