diff --git a/docs/configuration/collections.mdx b/docs/configuration/collections.mdx index 8416e77292..cc58c77188 100644 --- a/docs/configuration/collections.mdx +++ b/docs/configuration/collections.mdx @@ -158,6 +158,7 @@ The following options are available: | **`beforeListTable`** | An array of components to inject _before_ the built-in List View's table | | **`afterList`** | An array of components to inject _after_ the built-in List View | | **`afterListTable`** | An array of components to inject _after_ the built-in List View's table | +| **`listControlsMenu`** | An array of components to render as buttons within a menu next to the List Controls (after the Columns and Filters options) | | **`Description`** | A component to render below the Collection label in the List View. An alternative to the `admin.description` property. | | **`edit.SaveButton`** | Replace the default Save Button with a Custom Component. [Drafts](../versions/drafts) must be disabled. | | **`edit.SaveDraftButton`** | Replace the default Save Draft Button with a Custom Component. [Drafts](../versions/drafts) must be enabled and autosave must be disabled. | diff --git a/packages/next/src/views/List/renderListViewSlots.tsx b/packages/next/src/views/List/renderListViewSlots.tsx index 48419a1333..5761b33605 100644 --- a/packages/next/src/views/List/renderListViewSlots.tsx +++ b/packages/next/src/views/List/renderListViewSlots.tsx @@ -33,6 +33,15 @@ export const renderListViewSlots = ({ }) } + if (collectionConfig.admin.components?.listControlsMenu) { + result.ListControlsMenu = RenderServerComponent({ + clientProps, + Component: collectionConfig.admin.components.listControlsMenu, + importMap: payload.importMap, + serverProps, + }) + } + if (collectionConfig.admin.components?.afterListTable) { result.AfterListTable = RenderServerComponent({ clientProps, diff --git a/packages/payload/src/bin/generateImportMap/iterateCollections.ts b/packages/payload/src/bin/generateImportMap/iterateCollections.ts index f6a879d037..9b337f266d 100644 --- a/packages/payload/src/bin/generateImportMap/iterateCollections.ts +++ b/packages/payload/src/bin/generateImportMap/iterateCollections.ts @@ -30,6 +30,7 @@ export function iterateCollections({ }) addToImportMap(collection.admin?.components?.afterList) + addToImportMap(collection.admin?.components?.listControlsMenu) addToImportMap(collection.admin?.components?.afterListTable) addToImportMap(collection.admin?.components?.beforeList) addToImportMap(collection.admin?.components?.beforeListTable) diff --git a/packages/payload/src/collections/config/types.ts b/packages/payload/src/collections/config/types.ts index eafa2be926..a3f4c722f8 100644 --- a/packages/payload/src/collections/config/types.ts +++ b/packages/payload/src/collections/config/types.ts @@ -310,6 +310,7 @@ export type CollectionAdminOptions = { */ Upload?: CustomUpload } + listControlsMenu?: CustomComponent[] views?: { /** * Set to a React component to replace the entire Edit View, including all nested routes. diff --git a/packages/translations/src/clientKeys.ts b/packages/translations/src/clientKeys.ts index c87869f26d..49e63c31c6 100644 --- a/packages/translations/src/clientKeys.ts +++ b/packages/translations/src/clientKeys.ts @@ -209,6 +209,7 @@ export const clientTranslationKeys = createClientTranslationKeys([ 'general:loading', 'general:locale', 'general:menu', + 'general:listControlMenu', 'general:moveDown', 'general:moveUp', 'general:next', diff --git a/packages/translations/src/languages/ar.ts b/packages/translations/src/languages/ar.ts index e4f787688d..13624f601e 100644 --- a/packages/translations/src/languages/ar.ts +++ b/packages/translations/src/languages/ar.ts @@ -261,6 +261,7 @@ export const arTranslations: DefaultTranslationsObject = { leaveAnyway: 'المغادرة على أي حال', leaveWithoutSaving: 'المغادرة بدون حفظ', light: 'فاتح', + listControlMenu: 'قائمة التحكم', livePreview: 'معاينة مباشرة', loading: 'يتمّ التّحميل', locale: 'اللّغة', diff --git a/packages/translations/src/languages/az.ts b/packages/translations/src/languages/az.ts index be67f1e3fd..60de39ee57 100644 --- a/packages/translations/src/languages/az.ts +++ b/packages/translations/src/languages/az.ts @@ -265,6 +265,7 @@ export const azTranslations: DefaultTranslationsObject = { leaveAnyway: 'Heç olmasa çıx', leaveWithoutSaving: 'Saxlamadan çıx', light: 'Açıq', + listControlMenu: 'Siyahı nəzarət menyusu', livePreview: 'Öncədən baxış', loading: 'Yüklənir', locale: 'Lokal', diff --git a/packages/translations/src/languages/bg.ts b/packages/translations/src/languages/bg.ts index 3f7b9fc86d..670dd0d3db 100644 --- a/packages/translations/src/languages/bg.ts +++ b/packages/translations/src/languages/bg.ts @@ -264,6 +264,7 @@ export const bgTranslations: DefaultTranslationsObject = { leaveAnyway: 'Напусни въпреки това', leaveWithoutSaving: 'Напусни без да запазиш', light: 'Светла', + listControlMenu: 'Меню за контрол на списъка', livePreview: 'Предварителен преглед', loading: 'Зарежда се', locale: 'Локализация', diff --git a/packages/translations/src/languages/ca.ts b/packages/translations/src/languages/ca.ts index 8598ad19cc..d1b32e2c7e 100644 --- a/packages/translations/src/languages/ca.ts +++ b/packages/translations/src/languages/ca.ts @@ -265,6 +265,7 @@ export const caTranslations: DefaultTranslationsObject = { leaveAnyway: 'Deixa-ho de totes maneres', leaveWithoutSaving: 'Deixa sense desar', light: 'Clar', + listControlMenu: 'Menú de control de llista', livePreview: 'Previsualització en viu', loading: 'Carregant', locale: 'Idioma', diff --git a/packages/translations/src/languages/cs.ts b/packages/translations/src/languages/cs.ts index 82ebcf0daa..2e3365b283 100644 --- a/packages/translations/src/languages/cs.ts +++ b/packages/translations/src/languages/cs.ts @@ -263,6 +263,7 @@ export const csTranslations: DefaultTranslationsObject = { leaveAnyway: 'Přesto odejít', leaveWithoutSaving: 'Odejít bez uložení', light: 'Světlé', + listControlMenu: 'Nabídka ovládání seznamu', livePreview: 'Náhled', loading: 'Načítání', locale: 'Místní verze', diff --git a/packages/translations/src/languages/da.ts b/packages/translations/src/languages/da.ts index e7fccda3d3..3232f7c385 100644 --- a/packages/translations/src/languages/da.ts +++ b/packages/translations/src/languages/da.ts @@ -263,6 +263,7 @@ export const daTranslations: DefaultTranslationsObject = { leaveAnyway: 'Forlad alligevel', leaveWithoutSaving: 'Forlad uden at gemme', light: 'Lys', + listControlMenu: 'Kontrolmenu for liste', livePreview: 'Live-forhåndsvisning', loading: 'Loader', locale: 'Lokalitet', diff --git a/packages/translations/src/languages/de.ts b/packages/translations/src/languages/de.ts index eb19ed4bf4..ad68d0f6cd 100644 --- a/packages/translations/src/languages/de.ts +++ b/packages/translations/src/languages/de.ts @@ -269,6 +269,7 @@ export const deTranslations: DefaultTranslationsObject = { leaveAnyway: 'Trotzdem verlassen', leaveWithoutSaving: 'Ohne speichern verlassen', light: 'Hell', + listControlMenu: 'Kontrollmenü auflisten', livePreview: 'Vorschau', loading: 'Lädt', locale: 'Sprache', diff --git a/packages/translations/src/languages/en.ts b/packages/translations/src/languages/en.ts index 3a0c987691..df1a811af9 100644 --- a/packages/translations/src/languages/en.ts +++ b/packages/translations/src/languages/en.ts @@ -265,6 +265,7 @@ export const enTranslations = { leaveAnyway: 'Leave anyway', leaveWithoutSaving: 'Leave without saving', light: 'Light', + listControlMenu: 'List control menu', livePreview: 'Live Preview', loading: 'Loading', locale: 'Locale', diff --git a/packages/translations/src/languages/es.ts b/packages/translations/src/languages/es.ts index 051e074384..e09c7edeb6 100644 --- a/packages/translations/src/languages/es.ts +++ b/packages/translations/src/languages/es.ts @@ -269,6 +269,7 @@ export const esTranslations: DefaultTranslationsObject = { leaveAnyway: 'Salir de todos modos', leaveWithoutSaving: 'Salir sin guardar', light: 'Claro', + listControlMenu: 'Menú de control de lista', livePreview: 'Previsualizar', loading: 'Cargando', locale: 'Regional', diff --git a/packages/translations/src/languages/et.ts b/packages/translations/src/languages/et.ts index 8c34605bc6..5da5b1615c 100644 --- a/packages/translations/src/languages/et.ts +++ b/packages/translations/src/languages/et.ts @@ -262,6 +262,7 @@ export const etTranslations: DefaultTranslationsObject = { leaveAnyway: 'Lahku ikkagi', leaveWithoutSaving: 'Lahku ilma salvestamata', light: 'Hele', + listControlMenu: 'Loendikontrolli menüü', livePreview: 'Reaalajas eelvaade', loading: 'Laadimine', locale: 'Keel', diff --git a/packages/translations/src/languages/fa.ts b/packages/translations/src/languages/fa.ts index 32c91e6a20..12d4e3fb57 100644 --- a/packages/translations/src/languages/fa.ts +++ b/packages/translations/src/languages/fa.ts @@ -263,6 +263,7 @@ export const faTranslations: DefaultTranslationsObject = { leaveAnyway: 'به هر حال ترک کن', leaveWithoutSaving: 'ترک کردن بدون ذخیره', light: 'روشن', + listControlMenu: 'منوی کنترل لیست', livePreview: 'پیش‌نمایش', loading: 'در حال بارگذاری', locale: 'زبان', diff --git a/packages/translations/src/languages/fr.ts b/packages/translations/src/languages/fr.ts index 8e27d914e3..12e2b697d5 100644 --- a/packages/translations/src/languages/fr.ts +++ b/packages/translations/src/languages/fr.ts @@ -272,6 +272,7 @@ export const frTranslations: DefaultTranslationsObject = { leaveAnyway: 'Quitter quand même', leaveWithoutSaving: 'Quitter sans sauvegarder', light: 'Clair', + listControlMenu: 'Menu de contrôle de liste', livePreview: 'Aperçu', loading: 'Chargement en cours', locale: 'Paramètres régionaux', diff --git a/packages/translations/src/languages/he.ts b/packages/translations/src/languages/he.ts index 43b6dc3649..ff48978ae8 100644 --- a/packages/translations/src/languages/he.ts +++ b/packages/translations/src/languages/he.ts @@ -259,6 +259,7 @@ export const heTranslations: DefaultTranslationsObject = { leaveAnyway: 'צא בכל זאת', leaveWithoutSaving: 'צא מבלי לשמור', light: 'בהיר', + listControlMenu: 'תפריט בקרת רשימה', livePreview: 'תצוגה מקדימה חיה', loading: 'טוען', locale: 'שפה', diff --git a/packages/translations/src/languages/hr.ts b/packages/translations/src/languages/hr.ts index c261a87b0d..8c9a44732a 100644 --- a/packages/translations/src/languages/hr.ts +++ b/packages/translations/src/languages/hr.ts @@ -265,6 +265,7 @@ export const hrTranslations: DefaultTranslationsObject = { leaveAnyway: 'Svejedno napusti', leaveWithoutSaving: 'Napusti bez spremanja', light: 'Svijetlo', + listControlMenu: 'Izbornik za kontrolu popisa', livePreview: 'Pregled', loading: 'Učitavanje', locale: 'Jezik', diff --git a/packages/translations/src/languages/hu.ts b/packages/translations/src/languages/hu.ts index 3fff9dde93..28c0e449f6 100644 --- a/packages/translations/src/languages/hu.ts +++ b/packages/translations/src/languages/hu.ts @@ -267,6 +267,7 @@ export const huTranslations: DefaultTranslationsObject = { leaveAnyway: 'Távozás mindenképp', leaveWithoutSaving: 'Távozás mentés nélkül', light: 'Világos', + listControlMenu: 'Lista vezérlő menü', livePreview: 'Előnézet', loading: 'Betöltés', locale: 'Nyelv', diff --git a/packages/translations/src/languages/it.ts b/packages/translations/src/languages/it.ts index 793253a53a..c5f47c13b1 100644 --- a/packages/translations/src/languages/it.ts +++ b/packages/translations/src/languages/it.ts @@ -268,6 +268,7 @@ export const itTranslations: DefaultTranslationsObject = { leaveAnyway: 'Esci comunque', leaveWithoutSaving: 'Esci senza salvare', light: 'Chiaro', + listControlMenu: 'Menu di controllo elenco', livePreview: 'Anteprima dal vivo', loading: 'Caricamento', locale: 'Locale', diff --git a/packages/translations/src/languages/ja.ts b/packages/translations/src/languages/ja.ts index e030b98a28..6b2112088d 100644 --- a/packages/translations/src/languages/ja.ts +++ b/packages/translations/src/languages/ja.ts @@ -265,6 +265,7 @@ export const jaTranslations: DefaultTranslationsObject = { leaveAnyway: 'すぐに画面を離れる', leaveWithoutSaving: '内容が保存されていません', light: 'ライトモード', + listControlMenu: 'リスト制御メニュー', livePreview: 'プレビュー', loading: 'ローディング中', locale: 'ロケール', diff --git a/packages/translations/src/languages/ko.ts b/packages/translations/src/languages/ko.ts index 1dfea76687..e982e459e9 100644 --- a/packages/translations/src/languages/ko.ts +++ b/packages/translations/src/languages/ko.ts @@ -263,6 +263,7 @@ export const koTranslations: DefaultTranslationsObject = { leaveAnyway: '그래도 나가시겠습니까?', leaveWithoutSaving: '저장하지 않고 나가기', light: '라이트', + listControlMenu: '목록 제어 메뉴', livePreview: '실시간 미리보기', loading: '불러오는 중', locale: 'locale', diff --git a/packages/translations/src/languages/my.ts b/packages/translations/src/languages/my.ts index 3512b45c73..37c2e3d5b4 100644 --- a/packages/translations/src/languages/my.ts +++ b/packages/translations/src/languages/my.ts @@ -267,6 +267,7 @@ export const myTranslations: DefaultTranslationsObject = { leaveAnyway: 'ဘာဖြစ်ဖြစ် ထွက်မည်။', leaveWithoutSaving: 'မသိမ်းဘဲ ထွက်မည်။', light: 'အလင်း', + listControlMenu: 'စာရင်းထိန်းချုပ် မီနူး', livePreview: 'အစမ်းကြည့်ရန်', loading: 'ဖွင့်နေသည်', locale: 'ဒေသ', diff --git a/packages/translations/src/languages/nb.ts b/packages/translations/src/languages/nb.ts index f3c7076d92..b56324af05 100644 --- a/packages/translations/src/languages/nb.ts +++ b/packages/translations/src/languages/nb.ts @@ -265,6 +265,7 @@ export const nbTranslations: DefaultTranslationsObject = { leaveAnyway: 'Forlat likevel', leaveWithoutSaving: 'Forlat uten å lagre', light: 'Lys', + listControlMenu: 'Kontrollmeny for liste', livePreview: 'Forhåndsvisning', loading: 'Laster', locale: 'Lokalitet', diff --git a/packages/translations/src/languages/nl.ts b/packages/translations/src/languages/nl.ts index 57aebf53b4..de050ec925 100644 --- a/packages/translations/src/languages/nl.ts +++ b/packages/translations/src/languages/nl.ts @@ -268,6 +268,7 @@ export const nlTranslations: DefaultTranslationsObject = { leaveAnyway: 'Toch weggaan', leaveWithoutSaving: 'Verlaten zonder op te slaan', light: 'Licht', + listControlMenu: 'Controlelijstmenu', livePreview: 'Voorbeeld', loading: 'Laden', locale: 'Taal', diff --git a/packages/translations/src/languages/pl.ts b/packages/translations/src/languages/pl.ts index 835f558d06..d3276430d5 100644 --- a/packages/translations/src/languages/pl.ts +++ b/packages/translations/src/languages/pl.ts @@ -265,6 +265,7 @@ export const plTranslations: DefaultTranslationsObject = { leaveAnyway: 'Wyjdź mimo to', leaveWithoutSaving: 'Wyjdź bez zapisywania', light: 'Jasny', + listControlMenu: 'Menu kontroli listy', livePreview: 'Podgląd', loading: 'Ładowanie', locale: 'Ustawienia regionalne', diff --git a/packages/translations/src/languages/pt.ts b/packages/translations/src/languages/pt.ts index 9fc707fceb..512065abb1 100644 --- a/packages/translations/src/languages/pt.ts +++ b/packages/translations/src/languages/pt.ts @@ -265,6 +265,7 @@ export const ptTranslations: DefaultTranslationsObject = { leaveAnyway: 'Sair mesmo assim', leaveWithoutSaving: 'Sair sem salvar', light: 'Claro', + listControlMenu: 'Menu de controle de lista', livePreview: 'Pré-visualização', loading: 'Carregando', locale: 'Local', diff --git a/packages/translations/src/languages/ro.ts b/packages/translations/src/languages/ro.ts index 511287757f..d41edee33e 100644 --- a/packages/translations/src/languages/ro.ts +++ b/packages/translations/src/languages/ro.ts @@ -269,6 +269,7 @@ export const roTranslations: DefaultTranslationsObject = { leaveAnyway: 'Pleacă oricum', leaveWithoutSaving: 'Plecare fără a salva', light: 'Light', + listControlMenu: 'Meniu de control al listei', livePreview: 'Previzualizare', loading: 'Încărcare', locale: 'Localitate', diff --git a/packages/translations/src/languages/rs.ts b/packages/translations/src/languages/rs.ts index cca29ebdbb..f17a516c44 100644 --- a/packages/translations/src/languages/rs.ts +++ b/packages/translations/src/languages/rs.ts @@ -265,6 +265,7 @@ export const rsTranslations: DefaultTranslationsObject = { leaveAnyway: 'Свеједно напусти', leaveWithoutSaving: 'Напусти без чувања', light: 'Светло', + listControlMenu: 'Meni za kontrolu liste', livePreview: 'Преглед', loading: 'Учитавање', locale: 'Језик', diff --git a/packages/translations/src/languages/rsLatin.ts b/packages/translations/src/languages/rsLatin.ts index 98135aa646..c9eb87ca50 100644 --- a/packages/translations/src/languages/rsLatin.ts +++ b/packages/translations/src/languages/rsLatin.ts @@ -265,6 +265,7 @@ export const rsLatinTranslations: DefaultTranslationsObject = { leaveAnyway: 'Svejedno napusti', leaveWithoutSaving: 'Napusti bez čuvanja', light: 'Svetlo', + listControlMenu: 'Kontrolni meni liste', livePreview: 'Pregled', loading: 'Učitavanje', locale: 'Jezik', diff --git a/packages/translations/src/languages/ru.ts b/packages/translations/src/languages/ru.ts index dc5fa77cc0..1dabacbc12 100644 --- a/packages/translations/src/languages/ru.ts +++ b/packages/translations/src/languages/ru.ts @@ -267,6 +267,7 @@ export const ruTranslations: DefaultTranslationsObject = { leaveAnyway: 'Все равно уйти', leaveWithoutSaving: 'Выход без сохранения', light: 'Светлая', + listControlMenu: 'Меню управления списком', livePreview: 'Предпросмотр', loading: 'Загрузка', locale: 'Локаль', diff --git a/packages/translations/src/languages/sk.ts b/packages/translations/src/languages/sk.ts index 994bfc8364..c1349f95b3 100644 --- a/packages/translations/src/languages/sk.ts +++ b/packages/translations/src/languages/sk.ts @@ -266,6 +266,7 @@ export const skTranslations: DefaultTranslationsObject = { leaveAnyway: 'Presto odísť', leaveWithoutSaving: 'Odísť bez uloženia', light: 'Svetlý', + listControlMenu: 'Menu ovládania zoznamu', livePreview: 'Náhľad', loading: 'Načítavanie', locale: 'Jazyk', diff --git a/packages/translations/src/languages/sl.ts b/packages/translations/src/languages/sl.ts index e355170978..a0c7ecd6cc 100644 --- a/packages/translations/src/languages/sl.ts +++ b/packages/translations/src/languages/sl.ts @@ -264,6 +264,7 @@ export const slTranslations: DefaultTranslationsObject = { leaveAnyway: 'Vseeno zapusti', leaveWithoutSaving: 'Zapusti brez shranjevanja', light: 'Svetlo', + listControlMenu: 'Meni za nadzor seznama', livePreview: 'Predogled', loading: 'Nalaganje', locale: 'Jezik', diff --git a/packages/translations/src/languages/sv.ts b/packages/translations/src/languages/sv.ts index eb372add21..5e47711794 100644 --- a/packages/translations/src/languages/sv.ts +++ b/packages/translations/src/languages/sv.ts @@ -265,6 +265,7 @@ export const svTranslations: DefaultTranslationsObject = { leaveAnyway: 'Lämna ändå', leaveWithoutSaving: 'Lämna utan att spara', light: 'Ljus', + listControlMenu: 'Kontrollmeny för lista', livePreview: 'Förhandsvisa', loading: 'Läser in', locale: 'Lokal', diff --git a/packages/translations/src/languages/th.ts b/packages/translations/src/languages/th.ts index 9405595a0f..43d703e700 100644 --- a/packages/translations/src/languages/th.ts +++ b/packages/translations/src/languages/th.ts @@ -261,6 +261,7 @@ export const thTranslations: DefaultTranslationsObject = { leaveAnyway: 'ออกจากหน้านี้', leaveWithoutSaving: 'ออกโดยไม่บันทึก', light: 'สว่าง', + listControlMenu: 'เมนูควบคุมรายการ', livePreview: 'แสดงตัวอย่าง', loading: 'กำลังโหลด', locale: 'ตำแหน่งที่ตั้ง', diff --git a/packages/translations/src/languages/tr.ts b/packages/translations/src/languages/tr.ts index 6acb0bf003..969dfa0a28 100644 --- a/packages/translations/src/languages/tr.ts +++ b/packages/translations/src/languages/tr.ts @@ -268,6 +268,7 @@ export const trTranslations: DefaultTranslationsObject = { leaveAnyway: 'Yine de ayrıl', leaveWithoutSaving: 'Kaydetmeden ayrıl', light: 'Aydınlık', + listControlMenu: 'Liste kontrol menüsü', livePreview: 'Önizleme', loading: 'Yükleniyor', locale: 'Yerel ayar', diff --git a/packages/translations/src/languages/uk.ts b/packages/translations/src/languages/uk.ts index 17e379f177..f6a00894bb 100644 --- a/packages/translations/src/languages/uk.ts +++ b/packages/translations/src/languages/uk.ts @@ -264,6 +264,7 @@ export const ukTranslations: DefaultTranslationsObject = { leaveAnyway: 'Все одно вийти', leaveWithoutSaving: 'Вийти без збереження', light: 'Світла', + listControlMenu: 'Меню контролю списку', livePreview: 'Попередній перегляд', loading: 'Завантаження', locale: 'Локаль', diff --git a/packages/translations/src/languages/vi.ts b/packages/translations/src/languages/vi.ts index a9e86a6354..1dc95042e8 100644 --- a/packages/translations/src/languages/vi.ts +++ b/packages/translations/src/languages/vi.ts @@ -264,6 +264,7 @@ export const viTranslations: DefaultTranslationsObject = { leaveAnyway: 'Tiếp tục thoát', leaveWithoutSaving: 'Thay đổi chưa được lưu', light: 'Nền sáng', + listControlMenu: 'Menu điều khiển danh sách', livePreview: 'Xem trước', loading: 'Đang tải', locale: 'Ngôn ngữ', diff --git a/packages/translations/src/languages/zh.ts b/packages/translations/src/languages/zh.ts index dd42c1bbac..ef3ef7f4d9 100644 --- a/packages/translations/src/languages/zh.ts +++ b/packages/translations/src/languages/zh.ts @@ -255,6 +255,7 @@ export const zhTranslations: DefaultTranslationsObject = { leaveAnyway: '无论如何都要离开', leaveWithoutSaving: '离开而不保存', light: '亮色', + listControlMenu: '列表控制菜单', livePreview: '预览', loading: '加载中...', locale: '语言环境', diff --git a/packages/translations/src/languages/zhTw.ts b/packages/translations/src/languages/zhTw.ts index 409680532d..7dedd1443a 100644 --- a/packages/translations/src/languages/zhTw.ts +++ b/packages/translations/src/languages/zhTw.ts @@ -255,6 +255,7 @@ export const zhTwTranslations: DefaultTranslationsObject = { leaveAnyway: '無論如何都要離開', leaveWithoutSaving: '不儲存直接離開', light: '亮色', + listControlMenu: '列表控制菜單', livePreview: '預覽', loading: '載入中...', locale: '語言環境', diff --git a/packages/ui/src/elements/ListControls/index.tsx b/packages/ui/src/elements/ListControls/index.tsx index 5011cf8062..faf3bb7432 100644 --- a/packages/ui/src/elements/ListControls/index.tsx +++ b/packages/ui/src/elements/ListControls/index.tsx @@ -5,8 +5,10 @@ import { useWindowInfo } from '@faceless-ui/window-info' import { getTranslation } from '@payloadcms/translations' import React, { Fragment, useEffect, useRef, useState } from 'react' +import { Popup, PopupList } from '../../elements/Popup/index.js' import { useUseTitleField } from '../../hooks/useUseAsTitle.js' import { ChevronIcon } from '../../icons/Chevron/index.js' +import { Dots } from '../../icons/Dots/index.js' import { SearchIcon } from '../../icons/Search/index.js' import { useListQuery } from '../../providers/ListQuery/index.js' import { useTranslation } from '../../providers/Translation/index.js' @@ -19,8 +21,8 @@ import { PublishMany } from '../PublishMany/index.js' import { SearchFilter } from '../SearchFilter/index.js' import { UnpublishMany } from '../UnpublishMany/index.js' import { WhereBuilder } from '../WhereBuilder/index.js' -import validateWhereQuery from '../WhereBuilder/validateWhereQuery.js' import './index.scss' +import validateWhereQuery from '../WhereBuilder/validateWhereQuery.js' import { getTextFieldsToBeSearched } from './getTextFieldsToBeSearched.js' const baseClass = 'list-controls' @@ -36,6 +38,7 @@ export type ListControlsProps = { readonly handleSearchChange?: (search: string) => void readonly handleSortChange?: (sort: string) => void readonly handleWhereChange?: (where: Where) => void + readonly listControlsMenu?: React.ReactNode | React.ReactNode[] readonly renderedFilters?: Map } @@ -53,6 +56,7 @@ export const ListControls: React.FC = (props) => { disableBulkEdit, enableColumns = true, enableSort = false, + listControlsMenu, renderedFilters, } = props @@ -193,6 +197,21 @@ export const ListControls: React.FC = (props) => { {t('general:sort')} )} + {listControlsMenu && Array.isArray(listControlsMenu) && ( + } + className={`${baseClass}__popup`} + horizontalAlign="right" + size="large" + verticalAlign="bottom" + > + + {listControlsMenu.map((control, index) => ( + {control} + ))} + + + )} diff --git a/packages/ui/src/icons/Dots/index.scss b/packages/ui/src/icons/Dots/index.scss new file mode 100644 index 0000000000..c33d39f9da --- /dev/null +++ b/packages/ui/src/icons/Dots/index.scss @@ -0,0 +1,27 @@ +@import '../../scss/styles'; + +@layer payload-default { + .dots { + margin: 0; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + gap: 2px; + background-color: var(--theme-elevation-150); + border-radius: $style-radius-m; + height: calc(var(--base) * 1.2); + width: calc(var(--base) * 1.2); + + &:hover { + background-color: var(--theme-elevation-100); + } + + > div { + width: 2.5px; + height: 2.5px; + border-radius: 100%; + background-color: currentColor; + } + } +} diff --git a/packages/ui/src/icons/Dots/index.tsx b/packages/ui/src/icons/Dots/index.tsx new file mode 100644 index 0000000000..9bea6bf69e --- /dev/null +++ b/packages/ui/src/icons/Dots/index.tsx @@ -0,0 +1,17 @@ +import React from 'react' + +import './index.scss' + +export const Dots: React.FC<{ ariaLabel?: string; className?: string }> = ({ + ariaLabel, + className, +}) => ( +
+
+
+
+
+) diff --git a/packages/ui/src/views/List/index.tsx b/packages/ui/src/views/List/index.tsx index 4ef4fda9b9..bbb13c9877 100644 --- a/packages/ui/src/views/List/index.tsx +++ b/packages/ui/src/views/List/index.tsx @@ -48,6 +48,7 @@ export type ListViewSlots = { BeforeList?: React.ReactNode BeforeListTable?: React.ReactNode Description?: React.ReactNode + ListControlsMenu?: React.ReactNode | React.ReactNode[] Table: React.ReactNode } @@ -79,6 +80,7 @@ export const DefaultListView: React.FC = (props) => { disableBulkEdit, enableRowSelections, hasCreatePermission, + ListControlsMenu, listPreferences, newDocumentURL, preferenceKey, @@ -218,6 +220,7 @@ export const DefaultListView: React.FC = (props) => { collectionSlug={collectionSlug} disableBulkDelete={disableBulkDelete} disableBulkEdit={disableBulkEdit} + listControlsMenu={ListControlsMenu} renderedFilters={renderedFilters} /> {BeforeListTable} diff --git a/test/admin/collections/Posts.ts b/test/admin/collections/Posts.ts index 3de7b281dc..6e94f753c1 100644 --- a/test/admin/collections/Posts.ts +++ b/test/admin/collections/Posts.ts @@ -33,6 +33,26 @@ export const Posts: CollectionConfig = { }, }, ], + listControlsMenu: [ + { + path: '/components/Banner/index.js#Banner', + clientProps: { + message: 'ListControlsMenu', + }, + }, + { + path: '/components/Banner/index.js#Banner', + clientProps: { + message: 'Many of them', + }, + }, + { + path: '/components/Banner/index.js#Banner', + clientProps: { + message: 'Ok last one', + }, + }, + ], afterList: [ { path: '/components/Banner/index.js#Banner', diff --git a/test/admin/e2e/list-view/e2e.spec.ts b/test/admin/e2e/list-view/e2e.spec.ts index 2ecc2333b7..ca1b39c2d1 100644 --- a/test/admin/e2e/list-view/e2e.spec.ts +++ b/test/admin/e2e/list-view/e2e.spec.ts @@ -198,6 +198,19 @@ describe('List View', () => { ).toBeVisible() }) + test('should render custom listControlsMenu component', async () => { + await page.goto(postsUrl.list) + const kebabMenu = page.locator('.list-controls__popup') + await expect(kebabMenu).toBeVisible() + await kebabMenu.click() + + await expect( + page.locator('.popup-button-list__button').locator('div', { + hasText: 'ListControlsMenu', + }), + ).toBeVisible() + }) + test('should render custom afterListTable component', async () => { await page.goto(postsUrl.list) await expect(