Skip to content

Commit b0f65bc

Browse files
Merge branch 'payloadcms:main' into bkovachev/nested-tabs
2 parents 879a94a + d7ec48f commit b0f65bc

File tree

36 files changed

+1226
-125
lines changed

36 files changed

+1226
-125
lines changed

docs/custom-components/root-components.mdx

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ The following options are available:
4545
| `header` | An array of Custom Components to be injected above the Payload header. [More details](#header). |
4646
| `logout.Button` | The button displayed in the sidebar that logs the user out. [More details](#logoutbutton). |
4747
| `Nav` | Contains the sidebar / mobile menu in its entirety. [More details](#nav). |
48+
| `settingsMenu` | An array of Custom Components to inject into a popup menu accessible via a gear icon above the logout button. [More details](#settingsMenu). |
4849
| `providers` | Custom [React Context](https://react.dev/learn/scaling-up-with-reducer-and-context) providers that will wrap the entire Admin Panel. [More details](./custom-providers). |
4950
| `views` | Override or create new views within the Admin Panel. [More details](./custom-views). |
5051

@@ -271,6 +272,65 @@ export default function MyAfterNavLinksComponent() {
271272
}
272273
```
273274

275+
### settingsMenu
276+
277+
The `settingsMenu` property allows you to inject Custom Components into a popup menu accessible via a gear icon in the navigation controls, positioned above the logout button. This is ideal for adding custom actions, utilities, or settings that are relevant at the admin level.
278+
279+
To add `settingsMenu` components, use the `admin.components.settingsMenu` property in your Payload Config:
280+
281+
```ts
282+
import { buildConfig } from 'payload'
283+
284+
export default buildConfig({
285+
// ...
286+
admin: {
287+
// highlight-start
288+
components: {
289+
settingsMenu: ['/path/to/your/component#ComponentName'],
290+
},
291+
// highlight-end
292+
},
293+
})
294+
```
295+
296+
Here is an example of a simple `settingsMenu` component:
297+
298+
```tsx
299+
'use client'
300+
import { PopupList } from '@payloadcms/ui'
301+
302+
export function MySettingsMenu() {
303+
return (
304+
<PopupList.ButtonGroup>
305+
<PopupList.Button onClick={() => console.log('Action triggered')}>
306+
Custom Action
307+
</PopupList.Button>
308+
<PopupList.Button onClick={() => window.open('/admin/custom-page')}>
309+
Custom Page
310+
</PopupList.Button>
311+
</PopupList.ButtonGroup>
312+
)
313+
}
314+
```
315+
316+
You can pass multiple components to create organized groups of menu items:
317+
318+
```ts
319+
import { buildConfig } from 'payload'
320+
321+
export default buildConfig({
322+
// ...
323+
admin: {
324+
components: {
325+
settingsMenu: [
326+
'/components/SystemActions#SystemActions',
327+
'/components/DataManagement#DataManagement',
328+
],
329+
},
330+
},
331+
})
332+
```
333+
274334
### Nav
275335

276336
The `Nav` property contains the sidebar / mobile menu in its entirety. Use this property to completely replace the built-in Nav with your own custom navigation.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,8 @@
159159
"create-payload-app": "workspace:*",
160160
"cross-env": "7.0.3",
161161
"dotenv": "16.4.7",
162-
"drizzle-kit": "0.31.4",
163-
"drizzle-orm": "0.44.2",
162+
"drizzle-kit": "0.31.5",
163+
"drizzle-orm": "0.44.6",
164164
"escape-html": "^1.0.3",
165165
"execa": "5.1.1",
166166
"form-data": "3.0.1",

packages/db-d1-sqlite/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@
7474
"dependencies": {
7575
"@payloadcms/drizzle": "workspace:*",
7676
"console-table-printer": "2.12.1",
77-
"drizzle-kit": "0.31.4",
78-
"drizzle-orm": "0.44.2",
77+
"drizzle-kit": "0.31.5",
78+
"drizzle-orm": "0.44.6",
7979
"prompts": "2.4.2",
8080
"to-snake-case": "1.0.0",
8181
"uuid": "9.0.0"

packages/db-mongodb/src/utilities/resolveJoins.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,20 @@ export async function resolveJoins({
445445

446446
/**
447447
* Extracts relationTo filter values from a WHERE clause
448+
*
449+
* @purpose When you have a polymorphic join field that can reference multiple collection types (e.g. the documentsAndFolders join field on
450+
* folders that points to all folder-enabled collections), Payload needs to decide which collections to actually query. Without filtering,
451+
* it would query ALL possible collections even when the WHERE clause clearly indicates it only needs specific ones.
452+
*
453+
* extractRelationToFilter analyzes the WHERE clause to extract relationTo conditions and returns only the collection slugs that
454+
* could possibly match, avoiding unnecessary database queries.
455+
*
456+
* @description The function recursively traverses a WHERE clause looking for relationTo conditions in these patterns:
457+
*
458+
* 1. Direct conditions: { relationTo: { equals: 'posts' } }
459+
* 2. IN conditions: { relationTo: { in: ['posts', 'media'] } }
460+
* 3. Nested in AND/OR: Recursively searches through logical operators
461+
448462
* @param where - The WHERE clause to search
449463
* @returns Array of collection slugs if relationTo filter found, null otherwise
450464
*/
@@ -466,21 +480,29 @@ function extractRelationToFilter(where: Record<string, unknown>): null | string[
466480

467481
// Check for relationTo in logical operators
468482
if (where.and && Array.isArray(where.and)) {
483+
const allResults: string[] = []
469484
for (const condition of where.and) {
470485
const result = extractRelationToFilter(condition)
471486
if (result) {
472-
return result
487+
allResults.push(...result)
473488
}
474489
}
490+
if (allResults.length > 0) {
491+
return [...new Set(allResults)] // Remove duplicates
492+
}
475493
}
476494

477495
if (where.or && Array.isArray(where.or)) {
496+
const allResults: string[] = []
478497
for (const condition of where.or) {
479498
const result = extractRelationToFilter(condition)
480499
if (result) {
481-
return result
500+
allResults.push(...result)
482501
}
483502
}
503+
if (allResults.length > 0) {
504+
return [...new Set(allResults)] // Remove duplicates
505+
}
484506
}
485507

486508
return null

packages/db-postgres/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@
7878
"@payloadcms/drizzle": "workspace:*",
7979
"@types/pg": "8.10.2",
8080
"console-table-printer": "2.12.1",
81-
"drizzle-kit": "0.31.4",
82-
"drizzle-orm": "0.44.2",
81+
"drizzle-kit": "0.31.5",
82+
"drizzle-orm": "0.44.6",
8383
"pg": "8.16.3",
8484
"prompts": "2.4.2",
8585
"to-snake-case": "1.0.0",

packages/db-sqlite/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@
7676
"@libsql/client": "0.14.0",
7777
"@payloadcms/drizzle": "workspace:*",
7878
"console-table-printer": "2.12.1",
79-
"drizzle-kit": "0.31.4",
80-
"drizzle-orm": "0.44.2",
79+
"drizzle-kit": "0.31.5",
80+
"drizzle-orm": "0.44.6",
8181
"prompts": "2.4.2",
8282
"to-snake-case": "1.0.0",
8383
"uuid": "9.0.0"

packages/db-vercel-postgres/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@
7878
"@payloadcms/drizzle": "workspace:*",
7979
"@vercel/postgres": "^0.9.0",
8080
"console-table-printer": "2.12.1",
81-
"drizzle-kit": "0.31.4",
82-
"drizzle-orm": "0.44.2",
81+
"drizzle-kit": "0.31.5",
82+
"drizzle-orm": "0.44.6",
8383
"pg": "8.16.3",
8484
"prompts": "2.4.2",
8585
"to-snake-case": "1.0.0",

packages/drizzle/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
"dependencies": {
6161
"console-table-printer": "2.12.1",
6262
"dequal": "2.0.3",
63-
"drizzle-orm": "0.44.2",
63+
"drizzle-orm": "0.44.6",
6464
"prompts": "2.4.2",
6565
"to-snake-case": "1.0.0",
6666
"uuid": "9.0.0"

packages/graphql/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
"graphql-scalars": "1.22.2",
5757
"pluralize": "8.0.0",
5858
"ts-essentials": "10.0.3",
59-
"tsx": "4.19.2"
59+
"tsx": "4.20.6"
6060
},
6161
"devDependencies": {
6262
"@payloadcms/eslint-config": "workspace:*",
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
@import '~@payloadcms/ui/scss';
2+
3+
@layer payload-default {
4+
.settings-menu-button {
5+
&.popup--h-align-left {
6+
.popup__content {
7+
left: calc(var(--nav-padding-inline-start) * -0.5);
8+
}
9+
}
10+
}
11+
}

0 commit comments

Comments
 (0)