2017-10-31 12:29:15 +02:00
|
|
|
import React from 'react';
|
|
|
|
import { Broadcast, Subscriber } from 'joy-react-broadcast';
|
|
|
|
import isBoolean from 'lodash.isboolean';
|
|
|
|
import styled, { css } from 'styled-components';
|
2017-12-06 12:34:15 +02:00
|
|
|
import is from 'styled-is';
|
2017-10-31 12:29:15 +02:00
|
|
|
import remcalc from 'remcalc';
|
|
|
|
|
|
|
|
import Baseline from '../baseline';
|
|
|
|
import * as breakpoints from '../breakpoints';
|
2017-12-06 12:34:15 +02:00
|
|
|
import { Arrow as ArrowIcon } from '../icons';
|
2017-10-31 12:29:15 +02:00
|
|
|
|
|
|
|
const { styled: query } = breakpoints;
|
|
|
|
|
|
|
|
const handleBreakpoint = bp => props => {
|
2017-11-23 14:18:38 +02:00
|
|
|
const num = Number(props[bp]);
|
|
|
|
const hidden = (isBoolean(props[bp]) && !props[bp]) || num === 0;
|
2017-10-31 12:29:15 +02:00
|
|
|
const width = remcalc(props[bp]);
|
|
|
|
|
2017-11-23 14:18:38 +02:00
|
|
|
if (!hidden && Number.isNaN(num)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2017-10-31 12:29:15 +02:00
|
|
|
return `
|
|
|
|
width: ${width};
|
2017-11-23 14:18:38 +02:00
|
|
|
display: table-cell;
|
2017-10-31 12:29:15 +02:00
|
|
|
|
|
|
|
${hidden &&
|
|
|
|
`
|
|
|
|
display: none;
|
|
|
|
`};
|
|
|
|
`;
|
|
|
|
};
|
|
|
|
|
|
|
|
const ColumnBorder = css`
|
2017-12-04 20:24:24 +02:00
|
|
|
${is('hasBorder')`
|
|
|
|
border-${props => props.hasBorder}-width: ${remcalc(1)} !important;
|
2017-10-31 12:29:15 +02:00
|
|
|
`};
|
|
|
|
`;
|
|
|
|
|
|
|
|
const Column = css`
|
|
|
|
border-width: ${remcalc(1)};
|
|
|
|
border-style: solid;
|
|
|
|
border-color: ${props => props.theme.grey};
|
|
|
|
border-spacing: 0;
|
|
|
|
|
|
|
|
${is('disabled')`
|
|
|
|
border-color: ${props => props.theme.grey};
|
|
|
|
`};
|
|
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
|
|
box-sizing: border-box;
|
2017-11-20 13:33:50 +02:00
|
|
|
padding: 0 ${remcalc(24)};
|
2017-11-16 12:47:32 +02:00
|
|
|
height: ${remcalc(60)};
|
2017-10-31 12:29:15 +02:00
|
|
|
|
|
|
|
${handleBreakpoint('xs')};
|
|
|
|
|
|
|
|
${query.small`
|
|
|
|
${handleBreakpoint('sm')};
|
|
|
|
`};
|
|
|
|
|
|
|
|
${query.medium`
|
|
|
|
${handleBreakpoint('md')};
|
|
|
|
`};
|
|
|
|
|
|
|
|
${query.xlargeUp`
|
|
|
|
${handleBreakpoint('lg')};
|
|
|
|
`};
|
|
|
|
|
|
|
|
${is('actionable')`
|
|
|
|
cursor: pointer;
|
2017-11-23 14:18:38 +02:00
|
|
|
user-select: none;
|
2017-10-31 12:29:15 +02:00
|
|
|
`};
|
|
|
|
|
|
|
|
${is('baseline')`
|
|
|
|
vertical-align: baseline;
|
|
|
|
`};
|
|
|
|
|
|
|
|
${is('sub')`
|
|
|
|
vertical-align: sub;
|
|
|
|
`};
|
|
|
|
|
|
|
|
${is('text-top')`
|
|
|
|
vertical-align: text-top;
|
|
|
|
`};
|
|
|
|
|
|
|
|
${is('text-bottom')`
|
|
|
|
vertical-align: text-bottom;
|
|
|
|
`};
|
|
|
|
|
|
|
|
${is('middle')`
|
|
|
|
vertical-align: middle;
|
|
|
|
`};
|
|
|
|
|
|
|
|
${is('top')`
|
|
|
|
vertical-align: top;
|
|
|
|
`};
|
|
|
|
|
|
|
|
${is('bottom')`
|
|
|
|
vertical-align: bottom;
|
|
|
|
`};
|
|
|
|
|
|
|
|
${is('center')`
|
|
|
|
text-align: center;
|
|
|
|
`};
|
|
|
|
|
|
|
|
${is('left')`
|
|
|
|
text-align: left;
|
|
|
|
`};
|
|
|
|
|
|
|
|
${is('right')`
|
|
|
|
text-align: right;
|
|
|
|
`};
|
|
|
|
`;
|
|
|
|
|
|
|
|
const BaseTable = styled.table`
|
2017-11-16 12:47:32 +02:00
|
|
|
overflow: hidden;
|
|
|
|
border-spacing: 0;
|
|
|
|
border-collapse: separate;
|
2017-10-31 12:29:15 +02:00
|
|
|
table-layout: fixed;
|
|
|
|
width: 100%;
|
2017-11-16 12:47:32 +02:00
|
|
|
max-width: 100%;
|
2017-10-31 12:29:15 +02:00
|
|
|
`;
|
|
|
|
|
2017-11-23 14:18:38 +02:00
|
|
|
const BaseTfoot = styled.tfoot`
|
2017-10-31 12:29:15 +02:00
|
|
|
width: 100%;
|
2017-12-06 17:03:40 +02:00
|
|
|
background: ${props => props.theme.background};
|
2017-11-16 12:47:32 +02:00
|
|
|
|
|
|
|
th:first-child {
|
2017-11-20 13:33:50 +02:00
|
|
|
border-bottom-left-radius: ${remcalc(4)};
|
2017-11-16 12:47:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
th:last-child {
|
2017-11-20 13:33:50 +02:00
|
|
|
border-bottom-right-radius: ${remcalc(4)};
|
2017-11-16 12:47:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
th {
|
2017-11-20 13:33:50 +02:00
|
|
|
border-top-width: 0;
|
2017-11-16 12:47:32 +02:00
|
|
|
}
|
2017-10-31 12:29:15 +02:00
|
|
|
`;
|
|
|
|
|
2017-11-20 13:33:50 +02:00
|
|
|
const BaseThead = styled.thead`
|
|
|
|
width: 100%;
|
2017-12-06 17:03:40 +02:00
|
|
|
background: ${props => props.theme.background};
|
2017-11-20 13:33:50 +02:00
|
|
|
|
2017-11-23 14:18:38 +02:00
|
|
|
th:first-child {
|
|
|
|
border-top-left-radius: ${remcalc(4)};
|
|
|
|
}
|
2017-11-20 13:33:50 +02:00
|
|
|
|
2017-11-23 14:18:38 +02:00
|
|
|
th:last-child {
|
|
|
|
border-top-right-radius: ${remcalc(4)};
|
|
|
|
}
|
2017-11-20 13:33:50 +02:00
|
|
|
`;
|
|
|
|
|
2017-10-31 12:29:15 +02:00
|
|
|
const BaseTbody = styled.tbody`
|
|
|
|
width: 100%;
|
|
|
|
|
|
|
|
${is('shadow')`
|
2017-12-22 03:08:27 +02:00
|
|
|
box-shadow: ${props => props.theme.shadows.bottomShadow};
|
2017-10-31 12:29:15 +02:00
|
|
|
`};
|
|
|
|
|
|
|
|
${is('actionable')`
|
|
|
|
cursor: pointer;
|
|
|
|
`};
|
|
|
|
|
|
|
|
${is('disabled')`
|
|
|
|
border-color: ${props => props.theme.grey};
|
|
|
|
`};
|
2017-12-06 17:03:40 +02:00
|
|
|
|
|
|
|
tr:first-child td {
|
|
|
|
border-top: none;
|
|
|
|
}
|
2017-10-31 12:29:15 +02:00
|
|
|
`;
|
|
|
|
|
|
|
|
const BaseTh = styled.th`
|
|
|
|
${Column};
|
|
|
|
|
2017-11-16 12:47:32 +02:00
|
|
|
height: ${remcalc(42)};
|
|
|
|
color: ${props => props.theme.greyLight};
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
|
|
${is('selected')`
|
|
|
|
color: ${props => props.theme.text};
|
2017-11-20 13:33:50 +02:00
|
|
|
font-weight: bold;
|
2017-11-16 12:47:32 +02:00
|
|
|
`};
|
|
|
|
|
|
|
|
&:not(:first-child) {
|
|
|
|
border-left-width: 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
&:not(:last-child) {
|
|
|
|
border-right-width: 0;
|
|
|
|
}
|
|
|
|
|
2017-10-31 12:29:15 +02:00
|
|
|
${ColumnBorder};
|
|
|
|
`;
|
|
|
|
|
|
|
|
const BaseTd = styled.td`
|
|
|
|
${Column};
|
2017-11-16 12:47:32 +02:00
|
|
|
|
2017-11-23 14:18:38 +02:00
|
|
|
border-bottom-width: 0;
|
2017-10-31 12:29:15 +02:00
|
|
|
|
2017-11-16 12:47:32 +02:00
|
|
|
&:not(:first-child) {
|
|
|
|
border-left-width: 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
&:not(:last-child) {
|
|
|
|
border-right-width: 0;
|
|
|
|
}
|
|
|
|
|
2017-12-06 17:03:40 +02:00
|
|
|
${is('bold')`
|
|
|
|
font-weight: bold
|
|
|
|
`};
|
|
|
|
|
2017-11-23 14:18:38 +02:00
|
|
|
${is('selected')`
|
|
|
|
border-color: ${props => props.theme.primary};
|
|
|
|
background-color: rgba(59, 70, 204, 0.05);
|
2017-11-16 12:47:32 +02:00
|
|
|
`};
|
2017-10-31 12:29:15 +02:00
|
|
|
|
|
|
|
${ColumnBorder};
|
|
|
|
`;
|
|
|
|
|
|
|
|
const BaseTr = styled.tr`
|
|
|
|
display: table-row;
|
|
|
|
color: ${props => props.theme.text};
|
|
|
|
background-color: ${props => props.theme.white};
|
2017-11-16 12:47:32 +02:00
|
|
|
box-shadow: 0 ${remcalc(2)} 0 rgba(0, 0, 0, 0.05);
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
2017-11-22 14:13:33 +02:00
|
|
|
&:last-child {
|
|
|
|
box-shadow: none;
|
|
|
|
}
|
|
|
|
|
2017-11-16 12:47:32 +02:00
|
|
|
${is('selected')`
|
|
|
|
box-shadow: none;
|
|
|
|
`};
|
|
|
|
|
|
|
|
&:last-child td {
|
|
|
|
border-bottom-width: ${remcalc(1)};
|
|
|
|
}
|
2017-10-31 12:29:15 +02:00
|
|
|
|
|
|
|
${is('actionable')`
|
|
|
|
cursor: pointer;
|
|
|
|
`};
|
|
|
|
|
|
|
|
${is('disabled')`
|
|
|
|
background-color: ${props => props.theme.disabled};
|
|
|
|
color: ${props => props.theme.text};
|
|
|
|
cursor: default;
|
|
|
|
`};
|
|
|
|
|
|
|
|
/* override background when thead > tr */
|
|
|
|
${is('header')`
|
|
|
|
color: ${props => props.theme.text};
|
|
|
|
background-color: transparent;
|
2017-11-16 12:47:32 +02:00
|
|
|
border: none;
|
|
|
|
box-shadow: none;
|
2017-10-31 12:29:15 +02:00
|
|
|
`};
|
|
|
|
`;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @example ./usage.md
|
|
|
|
*/
|
|
|
|
export default Baseline(({ children, ...rest }) => (
|
|
|
|
<Broadcast channel="almost-responsive-table" value={rest}>
|
|
|
|
<BaseTable {...rest}>{children}</BaseTable>
|
|
|
|
</Broadcast>
|
|
|
|
));
|
|
|
|
|
|
|
|
const Propagate = ({ children, ...rest }) => (
|
|
|
|
<Subscriber channel="almost-responsive-table">
|
2017-11-23 14:18:38 +02:00
|
|
|
{({ disabled, header, selected } = {}) => (
|
2017-10-31 12:29:15 +02:00
|
|
|
<Broadcast
|
|
|
|
channel="almost-responsive-table"
|
2017-11-23 14:18:38 +02:00
|
|
|
value={{ disabled, header, selected, ...rest }}
|
2017-10-31 12:29:15 +02:00
|
|
|
>
|
2017-11-23 14:18:38 +02:00
|
|
|
{children({ disabled, header, selected, ...rest })}
|
2017-10-31 12:29:15 +02:00
|
|
|
</Broadcast>
|
|
|
|
)}
|
|
|
|
</Subscriber>
|
|
|
|
);
|
|
|
|
|
|
|
|
export const Thead = Baseline(({ children, ...rest }) => (
|
|
|
|
<Propagate {...rest} header={true}>
|
|
|
|
{value => (
|
|
|
|
<BaseThead {...value} name="thdead">
|
|
|
|
{children}
|
|
|
|
</BaseThead>
|
|
|
|
)}
|
|
|
|
</Propagate>
|
|
|
|
));
|
|
|
|
|
2017-11-23 14:18:38 +02:00
|
|
|
export const Tfoot = Baseline(({ children, ...rest }) => (
|
2017-11-20 13:33:50 +02:00
|
|
|
<Propagate {...rest} header={true}>
|
|
|
|
{value => (
|
2017-12-04 20:24:24 +02:00
|
|
|
<BaseTfoot {...value} name="tfoot">
|
2017-11-20 13:33:50 +02:00
|
|
|
{children}
|
2017-11-23 14:18:38 +02:00
|
|
|
</BaseTfoot>
|
2017-11-20 13:33:50 +02:00
|
|
|
)}
|
|
|
|
</Propagate>
|
|
|
|
));
|
|
|
|
|
2017-10-31 12:29:15 +02:00
|
|
|
export const Tr = Baseline(({ children, ...rest }) => (
|
|
|
|
<Propagate {...rest}>
|
|
|
|
{value => (
|
|
|
|
<BaseTr {...value} name="tr">
|
|
|
|
{children}
|
|
|
|
</BaseTr>
|
|
|
|
)}
|
|
|
|
</Propagate>
|
|
|
|
));
|
|
|
|
|
|
|
|
export const Th = Baseline(({ children, ...rest }) => (
|
|
|
|
<Propagate {...rest}>
|
2017-12-06 12:34:15 +02:00
|
|
|
{({ showSort, sortOrder, header, ...value }) => (
|
|
|
|
<BaseTh {...value} header={header} name="th">
|
2017-10-31 12:29:15 +02:00
|
|
|
{children}
|
2017-12-06 12:34:15 +02:00
|
|
|
{!showSort || !header ? null : (
|
|
|
|
<ArrowIcon
|
|
|
|
marginLeft={remcalc(9)}
|
|
|
|
marginBottom={remcalc(2)}
|
|
|
|
direction={sortOrder === 'asc' ? 'down' : 'up'}
|
|
|
|
/>
|
|
|
|
)}
|
2017-10-31 12:29:15 +02:00
|
|
|
</BaseTh>
|
|
|
|
)}
|
|
|
|
</Propagate>
|
|
|
|
));
|
|
|
|
|
|
|
|
export const Tbody = Baseline(({ children, ...rest }) => (
|
|
|
|
<Propagate {...rest}>
|
|
|
|
{value => (
|
|
|
|
<BaseTbody {...value} name="tbody">
|
|
|
|
{children}
|
|
|
|
</BaseTbody>
|
|
|
|
)}
|
|
|
|
</Propagate>
|
|
|
|
));
|
|
|
|
|
|
|
|
export const Td = Baseline(({ children, ...rest }) => (
|
|
|
|
<Propagate {...rest}>
|
|
|
|
{value => (
|
2017-12-06 17:03:40 +02:00
|
|
|
<BaseTd {...value} {...rest} name="td">
|
2017-10-31 12:29:15 +02:00
|
|
|
{children}
|
|
|
|
</BaseTd>
|
|
|
|
)}
|
|
|
|
</Propagate>
|
|
|
|
));
|