Delete old table

Signed-off-by: soupette <cyril@strapi.io>
This commit is contained in:
soupette 2021-09-13 16:12:38 +02:00
parent 5a7dbe742a
commit 5611a8b0f8
12 changed files with 0 additions and 659 deletions

View File

@ -1,14 +0,0 @@
import styled from 'styled-components';
const Delete = styled.span`
font-weight: 600;
-webkit-font-smoothing: antialiased;
&:after {
content: '—';
margin: 0 7px;
font-size: 13px;
font-weight: 600;
}
`;
export default Delete;

View File

@ -1,18 +0,0 @@
import styled from 'styled-components';
const DeleteAll = styled.span`
position: absolute;
color: #f64d0a;
font-weight: 500;
cursor: pointer;
&:after {
position: relative;
top: -1px;
content: '\f2ed';
margin-left: 7px;
font-size: 10px;
font-family: FontAwesome;
-webkit-font-smoothing: antialiased;
}
`;
export default DeleteAll;

View File

@ -1,20 +0,0 @@
import styled from 'styled-components';
const Wrapper = styled.tr`
width: 100%;
height: 36px;
background: #f7f8f8;
td {
height: 36px;
line-height: 36px;
font-size: 1.3rem;
font-weight: 400;
color: #333740;
text-align: left;
border-collapse: collapse;
border-top: 1px solid #f1f1f2 !important;
}
`;
export default Wrapper;

View File

@ -1,38 +0,0 @@
import React, { memo } from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import { getTrad } from '../../../utils';
import useListView from '../../../hooks/useListView';
import DeleteAll from './DeleteAll';
import Delete from './Delete';
import Wrapper from './Wrapper';
function ActionCollapse({ colSpan }) {
const { data, entriesToDelete, toggleModalDeleteAll } = useListView();
const number = entriesToDelete.length;
const suffix = number > 1 ? 'plural' : 'singular';
const deleteMessageId = number === data.length ? 'delete' : 'deleteSelected';
return (
<Wrapper colSpan={colSpan}>
<td colSpan={colSpan}>
<FormattedMessage
id={getTrad(`components.TableDelete.entries.${suffix}`)}
values={{ number }}
>
{message => <Delete>{message}</Delete>}
</FormattedMessage>
<FormattedMessage id={getTrad(`components.TableDelete.${deleteMessageId}`)}>
{message => <DeleteAll onClick={toggleModalDeleteAll}>{message}</DeleteAll>}
</FormattedMessage>
</td>
</Wrapper>
);
}
ActionCollapse.propTypes = {
colSpan: PropTypes.number.isRequired,
};
export default memo(ActionCollapse);

View File

@ -1,59 +0,0 @@
import React, { memo } from 'react';
import PropTypes from 'prop-types';
import { Carret, useTracking } from '@strapi/helper-plugin';
import { useListView } from '../../../hooks';
const Header = ({ fieldSchema: { type }, metadatas: { label, sortable, mainField }, name }) => {
const { sort, firstSortableHeader, setQuery } = useListView();
const { trackUsage } = useTracking();
const [sortBy, sortOrder] = sort.split(':');
let sortField = name;
let useRelation = false;
if (type === 'relation') {
useRelation = true;
sortField = `${name}.${mainField.name}`;
}
const handleClick = () => {
if (sortable) {
trackUsage('didSortEntries', { useRelation });
const isCurrentSort = sortField === sortBy;
const nextOrder = isCurrentSort && sortOrder === 'ASC' ? 'DESC' : 'ASC';
let value = `${sortField}:${nextOrder}`;
if (isCurrentSort && sortOrder === 'DESC') {
value = `${firstSortableHeader}:ASC`;
}
setQuery({
sort: value,
});
}
};
return (
<th onClick={handleClick}>
<span className={sortable ? 'sortable' : ''}>
{label}
{sortBy === sortField && <Carret fill="#212529" isUp={sortOrder === 'ASC' && 'isAsc'} />}
</span>
</th>
);
};
Header.propTypes = {
fieldSchema: PropTypes.shape({
type: PropTypes.string.isRequired,
}).isRequired,
metadatas: PropTypes.shape({
label: PropTypes.string.isRequired,
sortable: PropTypes.bool.isRequired,
mainField: PropTypes.object,
}).isRequired,
name: PropTypes.string.isRequired,
};
export default memo(Header);

View File

@ -1,35 +0,0 @@
import styled, { css } from 'styled-components';
/* eslint-disable consistent-return */
const Thead = styled.thead`
background: #f3f3f3;
height: 43px;
overflow: hidden;
th {
height: 43px;
border: none !important;
font-size: 1.3rem;
vertical-align: middle !important;
> span {
position: relative;
&.sortable {
cursor: pointer;
}
}
}
${({ isBulkable }) => {
if (isBulkable) {
return css`
> tr {
th:first-child {
width: 50px;
}
}
`;
}
}}
`;
export default Thead;

View File

@ -1,46 +0,0 @@
/* eslint-disable jsx-a11y/control-has-associated-label */
import React, { memo } from 'react';
import PropTypes from 'prop-types';
import { useListView } from '../../../hooks';
import CustomInputCheckbox from '../../CustomInputCheckbox';
import Thead from './Thead';
import Header from './Header';
function Headers({ headers, isBulkable }) {
const { data, entriesToDelete, onChangeBulkSelectall } = useListView();
return (
<Thead isBulkable={isBulkable}>
<tr>
{isBulkable && (
<th>
<CustomInputCheckbox
entriesToDelete={entriesToDelete}
isAll
name="all"
onChange={onChangeBulkSelectall}
value={data.length === entriesToDelete.length && entriesToDelete.length > 0}
/>
</th>
)}
{headers.map(({ key, name, fieldSchema, metadatas }) => {
return <Header key={key} name={name} fieldSchema={fieldSchema} metadatas={metadatas} />;
})}
<th />
</tr>
</Thead>
);
}
Headers.defaultProps = {
isBulkable: true,
headers: [],
};
Headers.propTypes = {
headers: PropTypes.array,
isBulkable: PropTypes.bool,
};
export default memo(Headers);

View File

@ -1,19 +0,0 @@
import styled from 'styled-components';
const ActionContainer = styled.td`
text-align: right;
i,
svg {
margin-left: 15px;
font-size: 1rem;
color: #333740;
&:first-of-type {
margin-left: 0px;
}
}
`;
export default ActionContainer;

View File

@ -1,55 +0,0 @@
import React, { memo, useState } from 'react';
import PropTypes from 'prop-types';
import { Tooltip } from '@buffetjs/styles';
import MediaPreviewList from '../../MediaPreviewList';
import RelationPreviewList from '../../RelationPreviewList';
import Truncate from '../../Truncate';
import Truncated from '../../Truncated';
const Cell = ({ options }) => {
const [tooltipIsDisplayed, setDisplayTooltip] = useState(false);
const handleTooltipToggle = () => {
setDisplayTooltip(prev => !prev);
};
const { type, cellId, value } = options;
if (type === 'media') {
return <MediaPreviewList files={value} />;
}
if (type === 'relation') {
return <RelationPreviewList options={options} />;
}
return (
<Truncate onMouseEnter={handleTooltipToggle} onMouseLeave={handleTooltipToggle}>
<Truncated>
<span data-for={cellId} data-tip={value}>
{value}
</span>
</Truncated>
{tooltipIsDisplayed && <Tooltip id={cellId} />}
</Truncate>
);
};
Cell.propTypes = {
options: PropTypes.shape({
cellId: PropTypes.string.isRequired,
metadatas: PropTypes.shape({
mainField: PropTypes.object,
}).isRequired,
name: PropTypes.string.isRequired,
relationType: PropTypes.string,
rowId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
type: PropTypes.string,
queryInfos: PropTypes.shape({
endPoint: PropTypes.string.isRequired,
}),
value: PropTypes.any,
}).isRequired,
};
export default memo(Cell);

View File

@ -1,111 +0,0 @@
import React, { memo, useCallback } from 'react';
import PropTypes from 'prop-types';
import { toString } from 'lodash';
import { useTracking } from '@strapi/helper-plugin';
import { IconLinks } from '@buffetjs/core';
import { Duplicate } from '@buffetjs/icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { useListView } from '../../../hooks';
import { getDisplayedValue } from '../../../utils';
import CustomInputCheckbox from '../../CustomInputCheckbox';
import ActionContainer from './ActionContainer';
import Cell from './Cell';
/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
function Row({ canCreate, canDelete, canUpdate, isBulkable, row, headers, goTo }) {
const { entriesToDelete, onChangeBulk, onClickDelete } = useListView();
const { trackUsage } = useTracking();
const memoizedDisplayedValue = useCallback(
(name, type) => {
return getDisplayedValue(type, row[name], name);
},
[row]
);
const links = [
{
icon: canCreate ? <Duplicate fill="black" /> : null,
onClick: e => {
e.stopPropagation();
goTo(`create/clone/${row.id}`);
},
},
{
icon: canUpdate ? <FontAwesomeIcon icon="pencil-alt" /> : null,
onClick: e => {
e.stopPropagation();
trackUsage('willEditEntryFromList');
goTo(row.id);
},
},
{
icon: canDelete ? <FontAwesomeIcon icon="trash-alt" /> : null,
onClick: e => {
e.stopPropagation();
trackUsage('willDeleteEntryFromList');
onClickDelete(row.id);
},
},
].filter(icon => icon);
return (
<>
{isBulkable && (
// eslint-disable-next-line jsx-a11y/click-events-have-key-events
<td key="i" onClick={e => e.stopPropagation()}>
<CustomInputCheckbox
name={row.id}
onChange={onChangeBulk}
value={entriesToDelete.filter(id => toString(id) === toString(row.id)).length > 0}
/>
</td>
)}
{headers.map(
({
key,
name,
fieldSchema: { type, relationType },
cellFormatter,
metadatas,
queryInfos,
}) => (
<td key={key}>
{cellFormatter ? (
cellFormatter(row)
) : (
<Cell
options={{
rowId: row.id,
relationType,
type,
name,
value: memoizedDisplayedValue(name, type),
cellId: key,
metadatas,
queryInfos,
}}
/>
)}
</td>
)
)}
<ActionContainer>
<IconLinks links={links} />
</ActionContainer>
</>
);
}
Row.propTypes = {
canCreate: PropTypes.bool.isRequired,
canDelete: PropTypes.bool.isRequired,
canUpdate: PropTypes.bool.isRequired,
headers: PropTypes.array.isRequired,
isBulkable: PropTypes.bool.isRequired,
row: PropTypes.object.isRequired,
goTo: PropTypes.func.isRequired,
};
export default memo(Row);

View File

@ -1,159 +0,0 @@
import React, { memo, useMemo } from 'react';
import PropTypes from 'prop-types';
import { useLocation, useHistory } from 'react-router-dom';
import { FormattedMessage, useIntl } from 'react-intl';
import { upperFirst, isEmpty } from 'lodash';
import { LoadingIndicator, useTracking } from '@strapi/helper-plugin';
import useListView from '../../hooks/useListView';
import { getTrad } from '../../utils';
import State from '../State';
import { LoadingContainer, LoadingWrapper, Table, TableEmpty, TableRow } from './styledComponents';
import ActionCollapse from './ActionCollapse';
import Headers from './Headers';
import Row from './Row';
import { usePluginsQueryParams } from '../../hooks';
const CustomTable = ({
canCreate,
canUpdate,
canDelete,
data,
displayedHeaders,
hasDraftAndPublish,
isBulkable,
showLoader,
}) => {
const { formatMessage } = useIntl();
const { entriesToDelete, label, filters, _q } = useListView();
const { trackUsage } = useTracking();
const { pathname } = useLocation();
const { push } = useHistory();
const pluginsQueryParams = usePluginsQueryParams();
const headers = useMemo(() => {
if (hasDraftAndPublish) {
return [
...displayedHeaders,
{
key: '__published_at_temp_key__',
name: 'published_at',
fieldSchema: {
type: 'custom',
},
metadatas: {
label: formatMessage({ id: getTrad('containers.ListPage.table-headers.published_at') }),
searchable: false,
sortable: true,
},
cellFormatter: cellData => {
const isPublished = !isEmpty(cellData.published_at);
return <State isPublished={isPublished} />;
},
},
];
}
return displayedHeaders;
}, [formatMessage, hasDraftAndPublish, displayedHeaders]);
const colSpanLength = isBulkable && canDelete ? headers.length + 2 : headers.length + 1;
const handleRowGoTo = id => {
trackUsage('willEditEntryFromList');
push({
pathname: `${pathname}/${id}`,
state: { from: pathname },
search: pluginsQueryParams,
});
};
const handleEditGoTo = id => {
trackUsage('willEditEntryFromButton');
push({
pathname: `${pathname}/${id}`,
state: { from: pathname },
search: pluginsQueryParams,
});
};
const values = { contentType: upperFirst(label), search: _q };
let tableEmptyMsgId = filters.length > 0 ? 'withFilters' : 'withoutFilter';
if (_q !== '') {
tableEmptyMsgId = 'withSearch';
}
const content =
data.length === 0 ? (
<TableEmpty>
<td colSpan={colSpanLength}>
<FormattedMessage
id={`content-manager.components.TableEmpty.${tableEmptyMsgId}`}
values={values}
/>
</td>
</TableEmpty>
) : (
data.map(row => {
return (
<TableRow
key={row.id}
onClick={e => {
e.preventDefault();
e.stopPropagation();
handleRowGoTo(row.id);
}}
>
<Row
canCreate={canCreate}
canDelete={canDelete}
canUpdate={canUpdate}
isBulkable={isBulkable && canDelete}
headers={headers}
row={row}
goTo={handleEditGoTo}
/>
</TableRow>
);
})
);
if (showLoader) {
return (
<>
<Table className="table">
<Headers headers={headers} isBulkable={isBulkable && canDelete} />
</Table>
<LoadingWrapper>
<LoadingContainer>
<LoadingIndicator />
</LoadingContainer>
</LoadingWrapper>
</>
);
}
return (
<Table className="table">
<Headers headers={headers} isBulkable={isBulkable && canDelete} />
<tbody>
{entriesToDelete.length > 0 && <ActionCollapse colSpan={colSpanLength} />}
{content}
</tbody>
</Table>
);
};
CustomTable.propTypes = {
canCreate: PropTypes.bool.isRequired,
canDelete: PropTypes.bool.isRequired,
canUpdate: PropTypes.bool.isRequired,
data: PropTypes.array.isRequired,
displayedHeaders: PropTypes.array.isRequired,
hasDraftAndPublish: PropTypes.bool.isRequired,
isBulkable: PropTypes.bool.isRequired,
showLoader: PropTypes.bool.isRequired,
};
export default memo(CustomTable);

View File

@ -1,85 +0,0 @@
import styled from 'styled-components';
import { themePropTypes } from '@strapi/helper-plugin';
const Table = styled.table`
border-radius: 3px;
border-collapse: initial;
box-shadow: 0 2px 4px #e3e9f3;
table-layout: fixed;
margin-bottom: 0;
tr,
th,
td {
border: none;
padding: 0;
white-space: nowrap;
}
th,
td {
padding: 0 25px;
label {
display: inline;
}
}
`;
const TableEmpty = styled.tr`
width: 100%;
height: 108px;
background: #ffffff;
td {
height: 106px;
line-height: 90px;
font-size: 1.3rem;
font-weight: 400;
color: #333740;
text-align: center;
border-collapse: collapse;
border-top: 1px solid #f1f1f2 !important;
}
`;
const TableRow = styled.tr`
height: 54px;
background: #ffffff;
&:hover {
cursor: pointer;
background: #f7f8f8;
}
td {
height: 53px;
font-size: 1.3rem;
line-height: 1.8rem;
font-weight: 400;
color: #333740;
vertical-align: middle;
border-collapse: collapse;
border-top: 1px solid #f1f1f2 !important;
}
`;
const LoadingContainer = styled.div`
display: block;
margin: auto;
`;
const LoadingWrapper = styled.div`
width: 100%;
height: 108px;
display: flex;
background: ${props => props.theme.main.colors.white};
box-shadow: 0 2px 4px ${props => props.theme.main.colors.darkGrey};
clip-path: inset(0px -5px -5px -5px);
`;
LoadingWrapper.propTypes = {
...themePropTypes,
};
export { LoadingContainer, LoadingWrapper, Table, TableEmpty, TableRow };