mirror of
https://github.com/strapi/strapi.git
synced 2025-09-02 21:32:43 +00:00
Delete old table
Signed-off-by: soupette <cyril@strapi.io>
This commit is contained in:
parent
5a7dbe742a
commit
5611a8b0f8
@ -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;
|
@ -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;
|
@ -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;
|
@ -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);
|
@ -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);
|
@ -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;
|
@ -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);
|
@ -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;
|
@ -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);
|
@ -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);
|
@ -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);
|
@ -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 };
|
Loading…
x
Reference in New Issue
Block a user