mirror of
https://github.com/Unleash/unleash.git
synced 2025-03-27 00:19:39 +01:00
Sticky batch actions bar (#3366)
This commit is contained in:
parent
e03307e286
commit
5585a9bed0
@ -1,6 +1,6 @@
|
|||||||
import { FC, useState } from 'react';
|
import { FC, useState } from 'react';
|
||||||
import { Button } from '@mui/material';
|
import { Button } from '@mui/material';
|
||||||
import { Undo } from '@mui/icons-material';
|
import { Delete, Undo } from '@mui/icons-material';
|
||||||
import {
|
import {
|
||||||
DELETE_FEATURE,
|
DELETE_FEATURE,
|
||||||
UPDATE_FEATURE,
|
UPDATE_FEATURE,
|
||||||
@ -69,7 +69,7 @@ export const ArchiveBatchActions: FC<IArchiveBatchActionsProps> = ({
|
|||||||
{({ hasAccess }) => (
|
{({ hasAccess }) => (
|
||||||
<Button
|
<Button
|
||||||
disabled={!hasAccess}
|
disabled={!hasAccess}
|
||||||
startIcon={<Undo />}
|
startIcon={<Delete />}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
size="small"
|
size="small"
|
||||||
onClick={onDelete}
|
onClick={onDelete}
|
||||||
|
@ -294,64 +294,67 @@ export const ArchiveTable = ({
|
|||||||
}, [loading, sortBy, searchValue]); // eslint-disable-line react-hooks/exhaustive-deps
|
}, [loading, sortBy, searchValue]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContent
|
<>
|
||||||
isLoading={loading}
|
<PageContent
|
||||||
header={
|
isLoading={loading}
|
||||||
<PageHeader
|
header={
|
||||||
titleElement={`${title} (${
|
<PageHeader
|
||||||
rows.length < data.length
|
titleElement={`${title} (${
|
||||||
? `${rows.length} of ${data.length}`
|
rows.length < data.length
|
||||||
: data.length
|
? `${rows.length} of ${data.length}`
|
||||||
})`}
|
: data.length
|
||||||
actions={
|
})`}
|
||||||
<Search
|
actions={
|
||||||
initialValue={searchValue}
|
<Search
|
||||||
onChange={setSearchValue}
|
initialValue={searchValue}
|
||||||
hasFilters
|
onChange={setSearchValue}
|
||||||
getSearchContext={getSearchContext}
|
hasFilters
|
||||||
/>
|
getSearchContext={getSearchContext}
|
||||||
}
|
/>
|
||||||
/>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<SearchHighlightProvider value={getSearchText(searchValue)}>
|
|
||||||
<VirtualizedTable
|
|
||||||
rows={rows}
|
|
||||||
headerGroups={headerGroups}
|
|
||||||
prepareRow={prepareRow}
|
|
||||||
/>
|
|
||||||
</SearchHighlightProvider>
|
|
||||||
<ConditionallyRender
|
|
||||||
condition={rows.length === 0}
|
|
||||||
show={() => (
|
|
||||||
<ConditionallyRender
|
|
||||||
condition={searchValue?.length > 0}
|
|
||||||
show={
|
|
||||||
<TablePlaceholder>
|
|
||||||
No feature toggles found matching “
|
|
||||||
{searchValue}”
|
|
||||||
</TablePlaceholder>
|
|
||||||
}
|
|
||||||
elseShow={
|
|
||||||
<TablePlaceholder>
|
|
||||||
None of the feature toggles were archived yet.
|
|
||||||
</TablePlaceholder>
|
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
}
|
||||||
/>
|
>
|
||||||
<ArchivedFeatureDeleteConfirm
|
<SearchHighlightProvider value={getSearchText(searchValue)}>
|
||||||
deletedFeatures={[deletedFeature?.name!]}
|
<VirtualizedTable
|
||||||
projectId={projectId!}
|
rows={rows}
|
||||||
open={deleteModalOpen}
|
headerGroups={headerGroups}
|
||||||
setOpen={setDeleteModalOpen}
|
prepareRow={prepareRow}
|
||||||
refetch={refetch}
|
/>
|
||||||
/>
|
</SearchHighlightProvider>
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={rows.length === 0}
|
||||||
|
show={() => (
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={searchValue?.length > 0}
|
||||||
|
show={
|
||||||
|
<TablePlaceholder>
|
||||||
|
No feature toggles found matching “
|
||||||
|
{searchValue}”
|
||||||
|
</TablePlaceholder>
|
||||||
|
}
|
||||||
|
elseShow={
|
||||||
|
<TablePlaceholder>
|
||||||
|
None of the feature toggles were archived
|
||||||
|
yet.
|
||||||
|
</TablePlaceholder>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<ArchivedFeatureDeleteConfirm
|
||||||
|
deletedFeatures={[deletedFeature?.name!]}
|
||||||
|
projectId={projectId!}
|
||||||
|
open={deleteModalOpen}
|
||||||
|
setOpen={setDeleteModalOpen}
|
||||||
|
refetch={refetch}
|
||||||
|
/>
|
||||||
|
</PageContent>
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={Boolean(projectId)}
|
condition={Boolean(projectId)}
|
||||||
show={
|
show={
|
||||||
<BatchSelectionActionsBar
|
<BatchSelectionActionsBar
|
||||||
selectedIds={Object.keys(selectedRowIds)}
|
count={Object.keys(selectedRowIds).length}
|
||||||
>
|
>
|
||||||
<ArchiveBatchActions
|
<ArchiveBatchActions
|
||||||
selectedIds={Object.keys(selectedRowIds)}
|
selectedIds={Object.keys(selectedRowIds)}
|
||||||
@ -360,6 +363,6 @@ export const ArchiveTable = ({
|
|||||||
</BatchSelectionActionsBar>
|
</BatchSelectionActionsBar>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</PageContent>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -2,14 +2,21 @@ import { FC } from 'react';
|
|||||||
import { Box, Paper, styled, Typography } from '@mui/material';
|
import { Box, Paper, styled, Typography } from '@mui/material';
|
||||||
|
|
||||||
interface IBatchSelectionActionsBarProps {
|
interface IBatchSelectionActionsBarProps {
|
||||||
selectedIds: string[];
|
count: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const StyledContainer = styled(Box)(() => ({
|
const StyledStickyContainer = styled('div')(() => ({
|
||||||
|
position: 'sticky',
|
||||||
|
marginTop: 'auto',
|
||||||
|
bottom: 0,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledContainer = styled(Box)(({ theme }) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
flexWrap: 'wrap',
|
flexWrap: 'wrap',
|
||||||
|
paddingBottom: theme.spacing(2),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const StyledBar = styled(Paper)(({ theme }) => ({
|
const StyledBar = styled(Paper)(({ theme }) => ({
|
||||||
@ -40,22 +47,24 @@ const StyledText = styled(Typography)(({ theme }) => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
export const BatchSelectionActionsBar: FC<IBatchSelectionActionsBarProps> = ({
|
export const BatchSelectionActionsBar: FC<IBatchSelectionActionsBarProps> = ({
|
||||||
selectedIds,
|
count,
|
||||||
children,
|
children,
|
||||||
}) => {
|
}) => {
|
||||||
if (selectedIds.length === 0) {
|
if (count === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledContainer>
|
<StyledStickyContainer>
|
||||||
<StyledBar elevation={4}>
|
<StyledContainer>
|
||||||
<StyledText>
|
<StyledBar elevation={4}>
|
||||||
<StyledCount>{selectedIds.length}</StyledCount>
|
<StyledText>
|
||||||
 selected
|
<StyledCount>{count}</StyledCount>
|
||||||
</StyledText>
|
 selected
|
||||||
{children}
|
</StyledText>
|
||||||
</StyledBar>
|
{children}
|
||||||
</StyledContainer>
|
</StyledBar>
|
||||||
|
</StyledContainer>
|
||||||
|
</StyledStickyContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -91,7 +91,6 @@ export const InstanceStatus: FC = ({ children }) => {
|
|||||||
useInstanceStatus();
|
useInstanceStatus();
|
||||||
const { extendTrial } = useInstanceStatusApi();
|
const { extendTrial } = useInstanceStatusApi();
|
||||||
const { setToastApiError } = useToast();
|
const { setToastApiError } = useToast();
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
const onExtendTrial = async () => {
|
const onExtendTrial = async () => {
|
||||||
try {
|
try {
|
||||||
@ -103,12 +102,7 @@ export const InstanceStatus: FC = ({ children }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<>
|
||||||
style={{
|
|
||||||
height: '100%',
|
|
||||||
backgroundColor: theme.palette.background.paper,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={isBilling && Boolean(instanceStatus)}
|
condition={isBilling && Boolean(instanceStatus)}
|
||||||
show={() => (
|
show={() => (
|
||||||
@ -124,7 +118,7 @@ export const InstanceStatus: FC = ({ children }) => {
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -21,12 +21,15 @@ interface IMainLayoutProps {
|
|||||||
const MainLayoutContainer = styled(Grid)(() => ({
|
const MainLayoutContainer = styled(Grid)(() => ({
|
||||||
height: '100%',
|
height: '100%',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
flexGrow: 1,
|
||||||
|
position: 'relative',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const MainLayoutContentWrapper = styled('main')(({ theme }) => ({
|
const MainLayoutContentWrapper = styled('main')(({ theme }) => ({
|
||||||
margin: theme.spacing(0, 'auto'),
|
margin: theme.spacing(0, 'auto'),
|
||||||
overflow: 'auto', // prevent margin collapsing
|
flexGrow: 1,
|
||||||
flex: 1,
|
|
||||||
width: '100%',
|
width: '100%',
|
||||||
backgroundColor: theme.palette.background.application,
|
backgroundColor: theme.palette.background.application,
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
|
@ -554,174 +554,187 @@ export const ProjectFeatureToggles = ({
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContent
|
<>
|
||||||
isLoading={loading}
|
<PageContent
|
||||||
className={styles.container}
|
isLoading={loading}
|
||||||
header={
|
className={styles.container}
|
||||||
<PageHeader
|
header={
|
||||||
titleElement={`Feature toggles (${rows.length})`}
|
<PageHeader
|
||||||
actions={
|
titleElement={`Feature toggles (${rows.length})`}
|
||||||
<>
|
actions={
|
||||||
<ConditionallyRender
|
<>
|
||||||
condition={!isSmallScreen}
|
<ConditionallyRender
|
||||||
show={
|
condition={!isSmallScreen}
|
||||||
<Search
|
show={
|
||||||
initialValue={searchValue}
|
<Search
|
||||||
onChange={setSearchValue}
|
initialValue={searchValue}
|
||||||
hasFilters
|
onChange={setSearchValue}
|
||||||
getSearchContext={getSearchContext}
|
hasFilters
|
||||||
/>
|
getSearchContext={getSearchContext}
|
||||||
}
|
/>
|
||||||
/>
|
}
|
||||||
<ColumnsMenu
|
/>
|
||||||
allColumns={allColumns}
|
<ColumnsMenu
|
||||||
staticColumns={staticColumns}
|
allColumns={allColumns}
|
||||||
dividerAfter={['createdAt']}
|
staticColumns={staticColumns}
|
||||||
dividerBefore={['Actions']}
|
dividerAfter={['createdAt']}
|
||||||
isCustomized={Boolean(storedParams.columns)}
|
dividerBefore={['Actions']}
|
||||||
setHiddenColumns={setHiddenColumns}
|
isCustomized={Boolean(storedParams.columns)}
|
||||||
/>
|
setHiddenColumns={setHiddenColumns}
|
||||||
<PageHeader.Divider sx={{ marginLeft: 0 }} />
|
/>
|
||||||
<ConditionallyRender
|
<PageHeader.Divider sx={{ marginLeft: 0 }} />
|
||||||
condition={Boolean(
|
<ConditionallyRender
|
||||||
uiConfig?.flags?.featuresExportImport
|
condition={Boolean(
|
||||||
)}
|
uiConfig?.flags?.featuresExportImport
|
||||||
show={
|
)}
|
||||||
<Tooltip
|
show={
|
||||||
title="Export toggles visible in the table below"
|
<Tooltip
|
||||||
arrow
|
title="Export toggles visible in the table below"
|
||||||
>
|
arrow
|
||||||
<IconButton
|
|
||||||
onClick={() =>
|
|
||||||
setShowExportDialog(true)
|
|
||||||
}
|
|
||||||
sx={theme => ({
|
|
||||||
marginRight: theme.spacing(2),
|
|
||||||
})}
|
|
||||||
>
|
>
|
||||||
<FileDownload />
|
<IconButton
|
||||||
</IconButton>
|
onClick={() =>
|
||||||
</Tooltip>
|
setShowExportDialog(true)
|
||||||
}
|
}
|
||||||
/>
|
sx={theme => ({
|
||||||
<StyledResponsiveButton
|
marginRight:
|
||||||
onClick={() =>
|
theme.spacing(2),
|
||||||
navigate(getCreateTogglePath(projectId))
|
})}
|
||||||
}
|
>
|
||||||
maxWidth="960px"
|
<FileDownload />
|
||||||
Icon={Add}
|
</IconButton>
|
||||||
projectId={projectId}
|
</Tooltip>
|
||||||
permission={CREATE_FEATURE}
|
}
|
||||||
data-testid="NAVIGATE_TO_CREATE_FEATURE"
|
/>
|
||||||
>
|
<StyledResponsiveButton
|
||||||
New feature toggle
|
onClick={() =>
|
||||||
</StyledResponsiveButton>
|
navigate(getCreateTogglePath(projectId))
|
||||||
</>
|
}
|
||||||
|
maxWidth="960px"
|
||||||
|
Icon={Add}
|
||||||
|
projectId={projectId}
|
||||||
|
permission={CREATE_FEATURE}
|
||||||
|
data-testid="NAVIGATE_TO_CREATE_FEATURE"
|
||||||
|
>
|
||||||
|
New feature toggle
|
||||||
|
</StyledResponsiveButton>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={isSmallScreen}
|
||||||
|
show={
|
||||||
|
<Search
|
||||||
|
initialValue={searchValue}
|
||||||
|
onChange={setSearchValue}
|
||||||
|
hasFilters
|
||||||
|
getSearchContext={getSearchContext}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</PageHeader>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<SearchHighlightProvider value={getSearchText(searchValue)}>
|
||||||
|
<VirtualizedTable
|
||||||
|
rows={rows}
|
||||||
|
headerGroups={headerGroups}
|
||||||
|
prepareRow={prepareRow}
|
||||||
|
/>
|
||||||
|
</SearchHighlightProvider>
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={rows.length === 0}
|
||||||
|
show={
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={searchValue?.length > 0}
|
||||||
|
show={
|
||||||
|
<TablePlaceholder>
|
||||||
|
No feature toggles found matching “
|
||||||
|
{searchValue}
|
||||||
|
”
|
||||||
|
</TablePlaceholder>
|
||||||
|
}
|
||||||
|
elseShow={
|
||||||
|
<TablePlaceholder>
|
||||||
|
No feature toggles available. Get started by
|
||||||
|
adding a new feature toggle.
|
||||||
|
</TablePlaceholder>
|
||||||
|
}
|
||||||
|
/>
|
||||||
}
|
}
|
||||||
>
|
|
||||||
<ConditionallyRender
|
|
||||||
condition={isSmallScreen}
|
|
||||||
show={
|
|
||||||
<Search
|
|
||||||
initialValue={searchValue}
|
|
||||||
onChange={setSearchValue}
|
|
||||||
hasFilters
|
|
||||||
getSearchContext={getSearchContext}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</PageHeader>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<SearchHighlightProvider value={getSearchText(searchValue)}>
|
|
||||||
<VirtualizedTable
|
|
||||||
rows={rows}
|
|
||||||
headerGroups={headerGroups}
|
|
||||||
prepareRow={prepareRow}
|
|
||||||
/>
|
/>
|
||||||
</SearchHighlightProvider>
|
<EnvironmentStrategyDialog
|
||||||
<ConditionallyRender
|
onClose={() =>
|
||||||
condition={rows.length === 0}
|
setStrategiesDialogState(prev => ({
|
||||||
show={
|
...prev,
|
||||||
<ConditionallyRender
|
open: false,
|
||||||
condition={searchValue?.length > 0}
|
}))
|
||||||
show={
|
}
|
||||||
<TablePlaceholder>
|
projectId={projectId}
|
||||||
No feature toggles found matching “
|
{...strategiesDialogState}
|
||||||
{searchValue}
|
/>
|
||||||
”
|
<FeatureStaleDialog
|
||||||
</TablePlaceholder>
|
isStale={featureStaleDialogState.stale === true}
|
||||||
}
|
isOpen={Boolean(featureStaleDialogState.featureId)}
|
||||||
elseShow={
|
onClose={() => {
|
||||||
<TablePlaceholder>
|
setFeatureStaleDialogState({});
|
||||||
No feature toggles available. Get started by
|
refetch();
|
||||||
adding a new feature toggle.
|
}}
|
||||||
</TablePlaceholder>
|
featureId={featureStaleDialogState.featureId || ''}
|
||||||
}
|
projectId={projectId}
|
||||||
/>
|
/>
|
||||||
}
|
<FeatureArchiveDialog
|
||||||
/>
|
isOpen={Boolean(featureArchiveState)}
|
||||||
<EnvironmentStrategyDialog
|
onConfirm={() => {
|
||||||
onClose={() =>
|
refetch();
|
||||||
setStrategiesDialogState(prev => ({ ...prev, open: false }))
|
}}
|
||||||
}
|
onClose={() => {
|
||||||
projectId={projectId}
|
setFeatureArchiveState(undefined);
|
||||||
{...strategiesDialogState}
|
}}
|
||||||
/>
|
featureIds={[featureArchiveState || '']}
|
||||||
<FeatureStaleDialog
|
projectId={projectId}
|
||||||
isStale={featureStaleDialogState.stale === true}
|
/>{' '}
|
||||||
isOpen={Boolean(featureStaleDialogState.featureId)}
|
<ChangeRequestDialogue
|
||||||
onClose={() => {
|
isOpen={changeRequestDialogDetails.isOpen}
|
||||||
setFeatureStaleDialogState({});
|
onClose={onChangeRequestToggleClose}
|
||||||
refetch();
|
environment={changeRequestDialogDetails?.environment}
|
||||||
}}
|
onConfirm={onChangeRequestToggleConfirm}
|
||||||
featureId={featureStaleDialogState.featureId || ''}
|
messageComponent={
|
||||||
projectId={projectId}
|
<UpdateEnabledMessage
|
||||||
/>
|
featureName={
|
||||||
<FeatureArchiveDialog
|
changeRequestDialogDetails.featureName!
|
||||||
isOpen={Boolean(featureArchiveState)}
|
}
|
||||||
onConfirm={() => {
|
enabled={changeRequestDialogDetails.enabled!}
|
||||||
refetch();
|
environment={
|
||||||
}}
|
changeRequestDialogDetails?.environment!
|
||||||
onClose={() => {
|
}
|
||||||
setFeatureArchiveState(undefined);
|
/>
|
||||||
}}
|
}
|
||||||
featureIds={[featureArchiveState || '']}
|
/>
|
||||||
projectId={projectId}
|
<ConditionallyRender
|
||||||
/>{' '}
|
condition={
|
||||||
<ChangeRequestDialogue
|
Boolean(uiConfig?.flags?.featuresExportImport) &&
|
||||||
isOpen={changeRequestDialogDetails.isOpen}
|
!loading
|
||||||
onClose={onChangeRequestToggleClose}
|
}
|
||||||
environment={changeRequestDialogDetails?.environment}
|
show={
|
||||||
onConfirm={onChangeRequestToggleConfirm}
|
<ExportDialog
|
||||||
messageComponent={
|
showExportDialog={showExportDialog}
|
||||||
<UpdateEnabledMessage
|
data={data}
|
||||||
featureName={changeRequestDialogDetails.featureName!}
|
onClose={() => setShowExportDialog(false)}
|
||||||
enabled={changeRequestDialogDetails.enabled!}
|
environments={environments}
|
||||||
environment={changeRequestDialogDetails?.environment!}
|
/>
|
||||||
/>
|
}
|
||||||
}
|
/>
|
||||||
/>
|
</PageContent>
|
||||||
<ConditionallyRender
|
<BatchSelectionActionsBar
|
||||||
condition={
|
count={Object.keys(selectedRowIds).length}
|
||||||
Boolean(uiConfig?.flags?.featuresExportImport) && !loading
|
>
|
||||||
}
|
|
||||||
show={
|
|
||||||
<ExportDialog
|
|
||||||
showExportDialog={showExportDialog}
|
|
||||||
data={data}
|
|
||||||
onClose={() => setShowExportDialog(false)}
|
|
||||||
environments={environments}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<BatchSelectionActionsBar selectedIds={Object.keys(selectedRowIds)}>
|
|
||||||
<ProjectFeaturesBatchActions
|
<ProjectFeaturesBatchActions
|
||||||
selectedIds={Object.keys(selectedRowIds)}
|
selectedIds={Object.keys(selectedRowIds)}
|
||||||
data={features}
|
data={features}
|
||||||
projectId={projectId}
|
projectId={projectId}
|
||||||
/>
|
/>
|
||||||
</BatchSelectionActionsBar>
|
</BatchSelectionActionsBar>
|
||||||
</PageContent>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -9,10 +9,13 @@ html {
|
|||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
height: 100%;
|
min-height: 100%;
|
||||||
font-family: 'Sen', sans-serif;
|
font-family: 'Sen', sans-serif;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-variant-ligatures: none;
|
font-variant-ligatures: none;
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
@ -139,7 +142,10 @@ a:hover {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#app {
|
#app {
|
||||||
height: 100%;
|
flex-grow: 1;
|
||||||
|
min-height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.MuiCardHeader-title {
|
.MuiCardHeader-title {
|
||||||
|
Loading…
Reference in New Issue
Block a user