336 lines
6.5 KiB
TypeScript
336 lines
6.5 KiB
TypeScript
/*
|
||
* Copyright 2018 balena.io
|
||
*
|
||
* Licensed 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 * as React from 'react';
|
||
import type {
|
||
FlexProps,
|
||
ButtonProps,
|
||
TableProps as BaseTableProps,
|
||
} from 'rendition';
|
||
import {
|
||
Alert as AlertBase,
|
||
Flex,
|
||
Button,
|
||
Modal as ModalBase,
|
||
Provider,
|
||
Table as BaseTable,
|
||
Txt,
|
||
} from 'rendition';
|
||
import styled, { css } from 'styled-components';
|
||
|
||
import { colors, theme } from './theme';
|
||
|
||
export const ThemedProvider = (props: any) => (
|
||
<Provider theme={theme} {...props}></Provider>
|
||
);
|
||
|
||
export const BaseButton = styled(Button)`
|
||
width: 200px;
|
||
height: 48px;
|
||
font-size: 16px;
|
||
`;
|
||
|
||
export const IconButton = styled((props) => <Button plain {...props} />)`
|
||
&&& {
|
||
width: 24px;
|
||
height: 24px;
|
||
font-size: 24px;
|
||
color: #fff;
|
||
|
||
> svg {
|
||
font-size: 1em;
|
||
}
|
||
}
|
||
`;
|
||
|
||
export const StepButton = styled((props: ButtonProps) => (
|
||
<BaseButton {...props}></BaseButton>
|
||
))`
|
||
color: #ffffff;
|
||
font-size: 14px;
|
||
`;
|
||
|
||
export const ChangeButton = styled(Button)`
|
||
&& {
|
||
border-radius: 24px;
|
||
color: ${colors.primary.background};
|
||
padding: 0;
|
||
height: 18px;
|
||
font-size: 14px;
|
||
|
||
&:enabled {
|
||
&:hover,
|
||
&:focus,
|
||
&:active {
|
||
color: #8f9297;
|
||
}
|
||
}
|
||
}
|
||
`;
|
||
|
||
export const StepNameButton = styled(BaseButton)`
|
||
display: inline-flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
width: 100%;
|
||
font-weight: normal;
|
||
color: ${colors.dark.foreground};
|
||
|
||
&:enabled {
|
||
&:hover,
|
||
&:focus,
|
||
&:active {
|
||
color: #8f9297;
|
||
}
|
||
}
|
||
`;
|
||
|
||
export const Footer = styled(Txt)`
|
||
margin-top: 10px;
|
||
color: ${colors.dark.disabled.foreground};
|
||
font-size: 10px;
|
||
`;
|
||
|
||
export const DetailsText = (props: FlexProps) => (
|
||
<Flex
|
||
alignItems="center"
|
||
color={colors.dark.disabled.foreground}
|
||
{...props}
|
||
/>
|
||
);
|
||
|
||
const modalFooterShadowCss = css`
|
||
overflow: auto;
|
||
background:
|
||
0,
|
||
linear-gradient(rgba(255, 255, 255, 0), white 70%) 0 100%,
|
||
0,
|
||
linear-gradient(rgba(255, 255, 255, 0), rgba(221, 225, 240, 0.5) 70%) 0 100%;
|
||
background-repeat: no-repeat;
|
||
background-size:
|
||
100% 40px,
|
||
100% 40px,
|
||
100% 8px,
|
||
100% 8px;
|
||
|
||
background-repeat: no-repeat;
|
||
background-color: white;
|
||
background-size:
|
||
100% 40px,
|
||
100% 40px,
|
||
100% 8px,
|
||
100% 8px;
|
||
background-attachment: local, local, scroll, scroll;
|
||
`;
|
||
|
||
export const Modal = styled(({ style, children, ...props }) => {
|
||
return (
|
||
<ModalBase
|
||
position="top"
|
||
width="97vw"
|
||
cancelButtonProps={{
|
||
style: {
|
||
marginRight: '20px',
|
||
border: 'solid 1px #2a506f',
|
||
},
|
||
}}
|
||
style={{
|
||
height: '87.5vh',
|
||
...style,
|
||
}}
|
||
{...props}
|
||
>
|
||
<ScrollableFlex flexDirection="column" width="100%" height="90%">
|
||
{children.length ? children.map((c: any) => <>{c}</>) : children}
|
||
</ScrollableFlex>
|
||
</ModalBase>
|
||
);
|
||
})`
|
||
> div {
|
||
padding: 0;
|
||
height: 99%;
|
||
|
||
> div:first-child {
|
||
height: 81%;
|
||
padding: 24px 30px 0;
|
||
}
|
||
|
||
> h3 {
|
||
margin: 0;
|
||
padding: 24px 30px 0;
|
||
height: 14.3%;
|
||
}
|
||
|
||
> div:first-child {
|
||
height: 81%;
|
||
padding: 24px 30px 0;
|
||
}
|
||
|
||
> div:nth-child(2) {
|
||
height: 61%;
|
||
padding: 0 30px;
|
||
${modalFooterShadowCss}
|
||
}
|
||
|
||
> div:last-child {
|
||
margin: 0;
|
||
flex-direction: ${(props) =>
|
||
props.reverseFooterButtons ? 'row-reverse' : 'row'};
|
||
border-radius: 0 0 7px 7px;
|
||
height: 80px;
|
||
background-color: #fff;
|
||
justify-content: center;
|
||
width: 100%;
|
||
}
|
||
|
||
::-webkit-scrollbar {
|
||
display: none;
|
||
}
|
||
}
|
||
`;
|
||
|
||
export const ScrollableFlex = styled(Flex)`
|
||
overflow: auto;
|
||
|
||
::-webkit-scrollbar {
|
||
display: none;
|
||
}
|
||
|
||
> div > div {
|
||
/* This is required for the sticky table header in TargetsTable */
|
||
overflow-x: visible;
|
||
}
|
||
`;
|
||
|
||
export const Alert = styled((props) => (
|
||
<AlertBase warning emphasized {...props}></AlertBase>
|
||
))`
|
||
position: fixed;
|
||
top: -40px;
|
||
left: 50%;
|
||
transform: translate(-50%, 0px);
|
||
height: 30px;
|
||
min-width: 50%;
|
||
padding: 0px;
|
||
justify-content: center;
|
||
align-items: center;
|
||
font-size: 14px;
|
||
background-color: #fca321;
|
||
text-align: center;
|
||
|
||
* {
|
||
color: #ffffff;
|
||
}
|
||
|
||
> div:first-child {
|
||
display: none;
|
||
}
|
||
`;
|
||
|
||
export interface GenericTableProps<T> extends BaseTableProps<T> {
|
||
refFn: (t: BaseTable<T>) => void;
|
||
data: T[];
|
||
checkedRowsNumber?: number;
|
||
multipleSelection: boolean;
|
||
showWarnings?: boolean;
|
||
}
|
||
|
||
function GenericTable<T>(
|
||
props: GenericTableProps<T>,
|
||
): React.ReactElement<GenericTableProps<T>> {
|
||
return (
|
||
<div>
|
||
<BaseTable<T> ref={props.refFn} {...props} />
|
||
</div>
|
||
);
|
||
}
|
||
|
||
function StyledTable<T>() {
|
||
return styled((props: GenericTableProps<T>) => (
|
||
<GenericTable<T> {...props} />
|
||
))`
|
||
[data-display='table-head']
|
||
> [data-display='table-row']
|
||
> [data-display='table-cell'] {
|
||
position: sticky;
|
||
background-color: #f8f9fd;
|
||
top: 0;
|
||
z-index: 1;
|
||
|
||
input[type='checkbox'] + div {
|
||
display: ${(props) => (props.multipleSelection ? 'flex' : 'none')};
|
||
|
||
${(props) =>
|
||
props.multipleSelection &&
|
||
props.checkedRowsNumber !== 0 &&
|
||
props.checkedRowsNumber !== props.data.length
|
||
? `
|
||
font-weight: 600;
|
||
color: ${colors.primary.foreground};
|
||
background: ${colors.primary.background};
|
||
|
||
::after {
|
||
content: '–';
|
||
}
|
||
`
|
||
: ''}
|
||
}
|
||
}
|
||
}
|
||
|
||
[data-display='table-head'] > [data-display='table-row'],
|
||
[data-display='table-body'] > [data-display='table-row'] {
|
||
> [data-display='table-cell']:first-child {
|
||
padding-left: 15px;
|
||
}
|
||
|
||
> [data-display='table-cell']:last-child {
|
||
padding-right: 0;
|
||
}
|
||
}
|
||
|
||
[data-display='table-body'] > [data-display='table-row'] {
|
||
&:nth-of-type(2n) {
|
||
background: transparent;
|
||
}
|
||
|
||
&[data-highlight='true'] {
|
||
&.system {
|
||
background-color: ${(props) => (props.showWarnings ? '#fff5e6' : '#e8f5fc')};
|
||
}
|
||
|
||
> [data-display='table-cell']:first-child {
|
||
box-shadow: none;
|
||
}
|
||
}
|
||
}
|
||
|
||
&& [data-display='table-row'] > [data-display='table-cell'] {
|
||
padding: 6px 8px;
|
||
color: #2a506f;
|
||
}
|
||
|
||
input[type='checkbox'] + div {
|
||
border-radius: ${(props) => (props.multipleSelection ? '4px' : '50%')};
|
||
}
|
||
`;
|
||
}
|
||
|
||
export const Table = <T extends object>(props: GenericTableProps<T>) => {
|
||
const TypedStyledFunctional = StyledTable<T>();
|
||
return <TypedStyledFunctional {...props} />;
|
||
};
|