feat(ui): sidePanel expandedWidth (#3728)
This commit is contained in:
parent
3d98548072
commit
61bf22c6f8
@ -8,6 +8,7 @@ export type SidePanelWithServicesProps = {
|
|||||||
className: string;
|
className: string;
|
||||||
activeTabIndex: number;
|
activeTabIndex: number;
|
||||||
tabs: any;
|
tabs: any;
|
||||||
|
expandedWidth?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SidePanelWithServices = ({
|
const SidePanelWithServices = ({
|
||||||
@ -16,7 +17,8 @@ const SidePanelWithServices = ({
|
|||||||
className,
|
className,
|
||||||
activeTabIndex: activeTabIndexProp,
|
activeTabIndex: activeTabIndexProp,
|
||||||
tabs,
|
tabs,
|
||||||
}) => {
|
expandedWidth,
|
||||||
|
}: SidePanelWithServicesProps) => {
|
||||||
const panelService: PanelService = servicesManager?.services?.panelService;
|
const panelService: PanelService = servicesManager?.services?.panelService;
|
||||||
|
|
||||||
// Tracks whether this SidePanel has been opened at least once since this SidePanel was inserted into the DOM.
|
// Tracks whether this SidePanel has been opened at least once since this SidePanel was inserted into the DOM.
|
||||||
@ -53,6 +55,7 @@ const SidePanelWithServices = ({
|
|||||||
onOpen={() => {
|
onOpen={() => {
|
||||||
setHasBeenOpened(true);
|
setHasBeenOpened(true);
|
||||||
}}
|
}}
|
||||||
|
expandedWidth={expandedWidth}
|
||||||
></SidePanel>
|
></SidePanel>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,39 +1,26 @@
|
|||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { CSSProperties, useCallback, useEffect, useState } from 'react';
|
import React, { CSSProperties, useCallback, useEffect, useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import Icon from '../Icon';
|
import Icon from '../Icon';
|
||||||
import Tooltip from '../Tooltip';
|
import Tooltip from '../Tooltip';
|
||||||
|
|
||||||
|
type StyleMap = {
|
||||||
|
open: {
|
||||||
|
left: { marginLeft: string };
|
||||||
|
right: { marginRight: string };
|
||||||
|
};
|
||||||
|
closed: {
|
||||||
|
left: { marginLeft: string };
|
||||||
|
right: { marginRight: string };
|
||||||
|
};
|
||||||
|
};
|
||||||
const borderSize = 4;
|
const borderSize = 4;
|
||||||
const expandedWidth = 248;
|
|
||||||
const collapsedWidth = 25;
|
const collapsedWidth = 25;
|
||||||
const closeIconWidth = 30;
|
const closeIconWidth = 30;
|
||||||
const gridHorizontalPadding = 10;
|
const gridHorizontalPadding = 10;
|
||||||
const tabSpacerWidth = 2;
|
const tabSpacerWidth = 2;
|
||||||
const gridAvailableWidth = expandedWidth - closeIconWidth - gridHorizontalPadding;
|
|
||||||
|
|
||||||
const baseStyle = {
|
|
||||||
maxWidth: `${expandedWidth}px`,
|
|
||||||
width: `${expandedWidth}px`,
|
|
||||||
// To align the top of the side panel with the top of the viewport grid, use position relative and offset the
|
|
||||||
// top by the same top offset as the viewport grid. Also adjust the height so that there is no overflow.
|
|
||||||
position: 'relative',
|
|
||||||
top: '0.2%',
|
|
||||||
height: '99.8%',
|
|
||||||
};
|
|
||||||
|
|
||||||
const collapsedHideWidth = expandedWidth - collapsedWidth - borderSize;
|
|
||||||
const styleMap = {
|
|
||||||
open: {
|
|
||||||
left: { marginLeft: '0px' },
|
|
||||||
right: { marginRight: '0px' },
|
|
||||||
},
|
|
||||||
closed: {
|
|
||||||
left: { marginLeft: `-${collapsedHideWidth}px` },
|
|
||||||
right: { marginRight: `-${collapsedHideWidth}px` },
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const baseClasses =
|
const baseClasses =
|
||||||
'transition-all duration-300 ease-in-out bg-black border-black justify-start box-content flex flex-col';
|
'transition-all duration-300 ease-in-out bg-black border-black justify-start box-content flex flex-col';
|
||||||
@ -62,7 +49,7 @@ const getTabWidth = (numTabs: number) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getGridWidth = (numTabs: number) => {
|
const getGridWidth = (numTabs: number, gridAvailableWidth: number) => {
|
||||||
const spacersWidth = (numTabs - 1) * tabSpacerWidth;
|
const spacersWidth = (numTabs - 1) * tabSpacerWidth;
|
||||||
const tabsWidth = getTabWidth(numTabs) * numTabs;
|
const tabsWidth = getTabWidth(numTabs) * numTabs;
|
||||||
|
|
||||||
@ -73,14 +60,13 @@ const getGridWidth = (numTabs: number) => {
|
|||||||
return gridAvailableWidth;
|
return gridAvailableWidth;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getNumGridColumns = (numTabs: number) => {
|
const getNumGridColumns = (numTabs: number, gridWidth: number) => {
|
||||||
if (numTabs === 1) {
|
if (numTabs === 1) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start by calculating the number of tabs assuming each tab was accompanied by a spacer.
|
// Start by calculating the number of tabs assuming each tab was accompanied by a spacer.
|
||||||
const tabWidth = getTabWidth(numTabs);
|
const tabWidth = getTabWidth(numTabs);
|
||||||
const gridWidth = getGridWidth(numTabs);
|
|
||||||
const numTabsWithOneSpacerEach = Math.floor(gridWidth / (tabWidth + tabSpacerWidth));
|
const numTabsWithOneSpacerEach = Math.floor(gridWidth / (tabWidth + tabSpacerWidth));
|
||||||
|
|
||||||
// But there is always one less spacer than tabs, so now check if an extra tab with one less spacer fits.
|
// But there is always one less spacer than tabs, so now check if an extra tab with one less spacer fits.
|
||||||
@ -94,8 +80,12 @@ const getNumGridColumns = (numTabs: number) => {
|
|||||||
return numTabsWithOneSpacerEach;
|
return numTabsWithOneSpacerEach;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getGridStyle = (side: string, numTabs: number = 0): CSSProperties => {
|
const getGridStyle = (
|
||||||
const gridWidth = getGridWidth(numTabs);
|
side: string,
|
||||||
|
numTabs: number = 0,
|
||||||
|
gridWidth: number,
|
||||||
|
expandedWidth: number
|
||||||
|
): CSSProperties => {
|
||||||
const relativePosition = Math.max(0, Math.floor(expandedWidth - gridWidth) / 2 - closeIconWidth);
|
const relativePosition = Math.max(0, Math.floor(expandedWidth - gridWidth) / 2 - closeIconWidth);
|
||||||
return {
|
return {
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
@ -128,11 +118,53 @@ const getTabIconClassNames = (numTabs: number, isActiveTab: boolean) => {
|
|||||||
rounded: isActiveTab,
|
rounded: isActiveTab,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
const createStyleMap = (
|
||||||
|
expandedWidth: number,
|
||||||
|
borderSize: number,
|
||||||
|
collapsedWidth: number
|
||||||
|
): StyleMap => {
|
||||||
|
const collapsedHideWidth = expandedWidth - collapsedWidth - borderSize;
|
||||||
|
|
||||||
|
return {
|
||||||
|
open: {
|
||||||
|
left: { marginLeft: '0px' },
|
||||||
|
right: { marginRight: '0px' },
|
||||||
|
},
|
||||||
|
closed: {
|
||||||
|
left: { marginLeft: `-${collapsedHideWidth}px` },
|
||||||
|
right: { marginRight: `-${collapsedHideWidth}px` },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const createBaseStyle = (expandedWidth: number) => {
|
||||||
|
return {
|
||||||
|
maxWidth: `${expandedWidth}px`,
|
||||||
|
width: `${expandedWidth}px`,
|
||||||
|
// To align the top of the side panel with the top of the viewport grid, use position relative and offset the
|
||||||
|
// top by the same top offset as the viewport grid. Also adjust the height so that there is no overflow.
|
||||||
|
position: 'relative',
|
||||||
|
top: '0.2%',
|
||||||
|
height: '99.8%',
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const SidePanel = ({
|
||||||
|
side,
|
||||||
|
className,
|
||||||
|
activeTabIndex: activeTabIndexProp,
|
||||||
|
tabs,
|
||||||
|
onOpen,
|
||||||
|
expandedWidth = 248,
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation('SidePanel');
|
||||||
|
|
||||||
const SidePanel = ({ side, className, activeTabIndex: activeTabIndexProp, tabs, onOpen }) => {
|
|
||||||
const [panelOpen, setPanelOpen] = useState(activeTabIndexProp !== null);
|
const [panelOpen, setPanelOpen] = useState(activeTabIndexProp !== null);
|
||||||
const [activeTabIndex, setActiveTabIndex] = useState(0);
|
const [activeTabIndex, setActiveTabIndex] = useState(0);
|
||||||
|
|
||||||
|
const styleMap = createStyleMap(expandedWidth, borderSize, collapsedWidth);
|
||||||
|
const baseStyle = createBaseStyle(expandedWidth);
|
||||||
|
const gridAvailableWidth = expandedWidth - closeIconWidth - gridHorizontalPadding;
|
||||||
|
const gridWidth = getGridWidth(tabs.length, gridAvailableWidth);
|
||||||
const openStatus = panelOpen ? 'open' : 'closed';
|
const openStatus = panelOpen ? 'open' : 'closed';
|
||||||
const style = Object.assign({}, styleMap[openStatus][side], baseStyle);
|
const style = Object.assign({}, styleMap[openStatus][side], baseStyle);
|
||||||
|
|
||||||
@ -238,13 +270,13 @@ const SidePanel = ({ side, className, activeTabIndex: activeTabIndexProp, tabs,
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getTabGridComponent = () => {
|
const getTabGridComponent = () => {
|
||||||
const numCols = getNumGridColumns(tabs.length);
|
const numCols = getNumGridColumns(tabs.length, gridWidth);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classnames('flex grow ', side === 'right' ? 'justify-start' : 'justify-end')}>
|
<div className={classnames('flex grow ', side === 'right' ? 'justify-start' : 'justify-end')}>
|
||||||
<div
|
<div
|
||||||
className={classnames('bg-primary-dark text-primary-active flex flex-wrap')}
|
className={classnames('bg-primary-dark text-primary-active flex flex-wrap')}
|
||||||
style={getGridStyle(side, tabs.length)}
|
style={getGridStyle(side, tabs.length, gridWidth, expandedWidth)}
|
||||||
>
|
>
|
||||||
{tabs.map((tab, tabIndex) => {
|
{tabs.map((tab, tabIndex) => {
|
||||||
return (
|
return (
|
||||||
@ -360,6 +392,7 @@ SidePanel.propTypes = {
|
|||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
onOpen: PropTypes.func,
|
onOpen: PropTypes.func,
|
||||||
|
expandedWidth: PropTypes.number,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SidePanel;
|
export default SidePanel;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user