Skip to content

Instantly share code, notes, and snippets.

@ahmetskilinc
Created May 5, 2024 12:25
Show Gist options
  • Save ahmetskilinc/9f568f32839dc16b0c5df94217c9e0c9 to your computer and use it in GitHub Desktop.
Save ahmetskilinc/9f568f32839dc16b0c5df94217c9e0c9 to your computer and use it in GitHub Desktop.
Payload Media Grid
.SortingHeader {
display: flex;
overflow-x: scroll;
gap: 18px;
align-items: center;
}
.MediaGrid {
display: grid;
gap: 1.5rem;
grid-template-columns: repeat(2, minmax(0, 1fr));
@media (min-width: 520px) {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
@media (min-width: 1024px) {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
@media (min-width: 1280px) {
grid-template-columns: repeat(6, minmax(0, 1fr));
}
}
.MediaGridCard {
outline-style: solid;
outline-width: 2px;
border-radius: 4px !important;
outline-color: rgb(47, 47, 47);
text-decoration: none;
text-align: left;
position: relative;
> a > span > span {
display: flex !important;
flex-direction: column;
justify-content: space-between;
}
}
.MediaGridCard:hover {
.MediaCardMeta {
background-color: rgb(47, 47, 47);
}
}
.MediaGridCardSelect {
position: absolute;
top: 8px;
left: 8px;
z-index: 10;
}
.MediaGridCardMedia {
object-fit: contain;
aspect-ratio: 1/1;
width: 100%;
}
.MediaCardMeta {
padding: 12px;
background-color: rgb(34, 34, 34);
display: flex;
flex-direction: column;
flex-grow: 1;
flex-shrink: 0;
}
.MediaGridCardTitle {
width: 100%;
font-weight: 500;
overflow-wrap: anywhere;
text-decoration: underline;
margin-bottom: 0;
}
.MediaGridCardAlt {
width: 100%;
font-weight: 300;
overflow-wrap: anywhere;
text-decoration: none;
margin-bottom: 0;
opacity: 0.5;
}
import { useWindowInfo } from "@faceless-ui/window-info";
import DeleteMany from "payload/dist/admin/components/elements/DeleteMany";
import EditMany from "payload/dist/admin/components/elements/EditMany";
import { Gutter } from "payload/dist/admin/components/elements/Gutter";
import { ListControls } from "payload/dist/admin/components/elements/ListControls";
import ListSelection from "payload/dist/admin/components/elements/ListSelection";
import SortColumn from "payload/dist/admin/components/elements/SortColumn";
import Paginator from "payload/dist/admin/components/elements/Paginator";
import PerPage from "payload/dist/admin/components/elements/PerPage";
import Pill from "payload/dist/admin/components/elements/Pill";
import PublishMany from "payload/dist/admin/components/elements/PublishMany";
import UnpublishMany from "payload/dist/admin/components/elements/UnpublishMany";
import ViewDescription from "payload/dist/admin/components/elements/ViewDescription";
import { SelectionProvider } from "payload/dist/admin/components/views/collections/List/SelectionProvider";
import SelectRow from "payload/dist/admin/components/views/collections/List/SelectRow";
import SelectAll from "payload/dist/admin/components/views/collections/List/SelectAll";
import { getTranslation } from "payload/dist/utilities/getTranslation";
import { formatDate } from "payload/dist/admin/utilities/formatDate";
import { useConfig } from "payload/dist/admin/components/utilities/Config";
import Meta from "payload/dist/admin/components/utilities/Meta";
import { Button } from "payload/components/elements";
import React, { Fragment } from "react";
import { useTranslation } from "react-i18next";
import type { Props } from "payload/dist/admin/components/views/collections/List/types";
import classes from "./MediaList.module.scss";
const baseClass = "collection-list";
const MediaList = (props: Props) => {
const {
data,
collection: {
admin: { description } = {},
labels: { plural: pluralLabel, singular: singularLabel },
},
limit,
collection,
handlePageChange,
handlePerPageChange,
handleSearchChange,
handleSortChange,
handleWhereChange,
hasCreatePermission,
modifySearchParams,
newDocumentURL,
resetParams,
titleField,
customHeader,
} = props;
const {
breakpoints: { s: smallBreak },
} = useWindowInfo();
const { i18n, t } = useTranslation("general");
const {
admin: { dateFormat },
routes: { admin: adminRoute },
} = useConfig();
return (
<div className="collection-list">
<Meta title={getTranslation(collection.labels.plural, i18n)} />
<SelectionProvider docs={data.docs} totalDocs={data.totalDocs}>
<Gutter className={`${baseClass}__wrap`}>
<header className={`${baseClass}__header`}>
{customHeader && customHeader}
{!customHeader && (
<Fragment>
<h1>Media</h1>
{hasCreatePermission && (
<Pill
aria-label={t("createNewLabel", { label: getTranslation(singularLabel, i18n) })}
to={newDocumentURL}
>
{t("createNew")}
</Pill>
)}
{!smallBreak && <ListSelection label={getTranslation(pluralLabel, i18n)} />}
{description && (
<div className={`${baseClass}__sub-header`}>
<ViewDescription description={description} />
</div>
)}
</Fragment>
)}
</header>
<ListControls
collection={collection}
handleSearchChange={handleSearchChange}
handleSortChange={handleSortChange}
handleWhereChange={handleWhereChange}
modifySearchQuery={modifySearchParams}
resetParams={resetParams}
titleField={titleField}
enableColumns={false}
/>
<div className={classes.SortingHeader}>
<SelectAll />
<SortColumn label="File Name" name="filename" />
<SortColumn label="Alt Tag" name="alt" />
<SortColumn label="Created At" name="createdAt" />
<SortColumn label="Updated At" name="updatedAt" />
</div>
<div className={classes.MediaGrid}>
{props.data.docs
? props.data.docs.map((doc) => (
<Button
key={doc.id}
className={classes.MediaGridCard}
el={"link"}
buttonStyle="none"
to={`${adminRoute}/collections/media/${doc.id}`}
>
<div className={classes.MediaGridCardSelect}>
<SelectRow id={doc.id} />
</div>
{doc.mimeType.includes("image") ? (
doc.mimeType.includes("svg") ? (
<img src={doc.url} className={classes.MediaGridCardMedia} />
) : (
<img src={doc.sizes.optimised.url} className={classes.MediaGridCardMedia} />
)
) : (
<video
src={doc.url}
autoPlay
muted
loop
className={classes.MediaGridCardMedia}
/>
)}
<div className={classes.MediaCardMeta}>
<p className={classes.MediaGridCardTitle}>{doc.filename}</p>
<p className={classes.MediaGridCardAlt}>{doc.alt || "<No Alt Text>"}</p>
{/* <p className={classes.MediaGridCardAlt}>
{formatDate(doc.updatedAt, dateFormat, i18n?.language)}
</p> */}
<p className={classes.MediaGridCardAlt}>
{formatDate(doc.createdAt, dateFormat, i18n?.language)}
</p>
</div>
</Button>
))
: null}
</div>
{data.docs && data.docs.length > 0 && (
<div className={`${baseClass}__page-controls`}>
<Paginator
disableHistoryChange={modifySearchParams === false}
hasNextPage={data.hasNextPage}
hasPrevPage={data.hasPrevPage}
limit={data.limit}
nextPage={data.nextPage}
numberOfNeighbors={1}
onChange={handlePageChange}
page={data.page}
prevPage={data.prevPage}
totalPages={data.totalPages}
/>
{data?.totalDocs > 0 && (
<Fragment>
<div className={`${baseClass}__page-info`}>
{data.page * data.limit - (data.limit - 1)}-
{data.totalPages > 1 && data.totalPages !== data.page
? data.limit * data.page
: data.totalDocs}{" "}
{t("of")} {data.totalDocs}
</div>
<PerPage
handleChange={handlePerPageChange}
limit={limit}
limits={collection?.admin?.pagination?.limits}
modifySearchParams={modifySearchParams}
resetPage={data.totalDocs <= data.pagingCounter}
/>
{smallBreak && (
<div className={`${baseClass}__list-selection`}>
<Fragment>
<ListSelection label={getTranslation(collection.labels.plural, i18n)} />
<div className={`${baseClass}__list-selection-actions`}>
<EditMany collection={collection} resetParams={resetParams} />
<PublishMany collection={collection} resetParams={resetParams} />
<UnpublishMany collection={collection} resetParams={resetParams} />
<DeleteMany collection={collection} resetParams={resetParams} />
</div>
</Fragment>
</div>
)}
</Fragment>
)}
</div>
)}
</Gutter>
</SelectionProvider>
</div>
);
};
export default MediaList;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment