Compare commits

..

28 Commits

Author SHA1 Message Date
Elliot DeNolf
2d6e7f8a37 chore(release): v3.0.0-beta.84 [skip ci] 2024-08-16 13:56:50 -04:00
James Mikrut
3d37d74c6e fix: adds default drizzle package exports (#7728)
Default exports were missing for Drizzle package.
2024-08-16 13:40:41 -04:00
James Mikrut
de19822ed4 fix: ensures user is accurate in useAuth (#7727)
## Description

Fixes an issue where the `user` could be out of date after logging in.
2024-08-16 17:10:32 +00:00
Tylan Davis
2b2bcb5264 fix: corrects logout icon styling (#7726)
## Description

before: 
<img width="89" alt="Screenshot 2024-08-16 at 12 43 56 PM"
src="https://github.com/user-attachments/assets/1052cfdb-6dde-4b65-a4c0-e37a909dac34">

after:
<img width="48" alt="Screenshot 2024-08-16 at 12 43 35 PM"
src="https://github.com/user-attachments/assets/aa4d6aed-4a78-4b17-a209-df3618b273a1">

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

<!-- Please delete options that are not relevant. -->

- [x] Bug fix (non-breaking change which fixes an issue)

## Checklist:

- [ ] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
- [ ] I have made corresponding changes to the documentation
2024-08-16 17:01:42 +00:00
Elliot DeNolf
e9b01e6d9f chore(release): v3.0.0-beta.83 [skip ci] 2024-08-16 12:36:30 -04:00
Alessio Gravili
b0a760193e fix: type RelationshipFieldClient typed incorrectly (#7725) 2024-08-16 12:35:05 -04:00
Jarrod Flesch
95569e44e4 fix: login with username server validations (#7719) 2024-08-16 12:07:53 -04:00
Paul
11816080a6 fix: bin script error when running on linux (#7721)
Fixes https://github.com/payloadcms/payload/issues/7717
2024-08-16 10:07:03 -06:00
Paul
3a86822f0a fix(ui): ensure that aborting Autosave always has a valid reason for the controller - fixes uncaught error (#7723) 2024-08-16 16:04:32 +00:00
Jarrod Flesch
6f8604e18c fix: ensures users cannot be created without confirming pw (#7583) 2024-08-16 11:44:27 -04:00
Tylan Davis
aec3f5e308 chore: admin panel style updates (#7720)
## Description

Minor admin panel style updates:
- Adjusts document header title spacing.
- Makes toast notifications more apparent.
- Adjusts alignment of create new button.
- Improves chevron icon.

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

<!-- Please delete options that are not relevant. -->

- [x] Chore (non-breaking change which does not add functionality)

## Checklist:

- [ ] I have added tests that prove my fix is effective or that my
feature works
- [x] Existing test suite passes locally with my changes
- [ ] I have made corresponding changes to the documentation
2024-08-16 15:23:08 +00:00
Jarrod Flesch
e0a5de6730 chore: extends dropzone and upload field (#7713)
## Description

Tweaks to Upload and Dropzone components, making them more extendable.

- Dropzone adds prop to allow multiple files
- Upload correctly sets url if state is initialized with a File

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [x] Chore (non-breaking change which does not add functionality)

## Checklist:

- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] Existing test suite passes locally with my changes
- [ ] I have made corresponding changes to the documentation
2024-08-16 09:38:41 -04:00
Paul
5eee49da9a feat(plugin-seo): pass req through to generate functions (#7711)
Closes https://github.com/payloadcms/payload/issues/7708
2024-08-15 23:09:08 +00:00
dependabot[bot]
b7d01dec70 chore(deps): bump pnpm/action-setup from 3 to 4 in /.github/actions/setup in the github_actions group across 1 directory (#7687)
Bumps the github_actions group with 1 update in the
/.github/actions/setup directory:
[pnpm/action-setup](https://github.com/pnpm/action-setup).

Updates `pnpm/action-setup` from 3 to 4
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/pnpm/action-setup/releases">pnpm/action-setup's
releases</a>.</em></p>
<blockquote>
<h2>v4.0.0</h2>
<p>An error is thrown if one version of pnpm is specified in the
<code>packageManager</code> field of <code>package.json</code> and a
different version is specified in the action's settings <a
href="https://redirect.github.com/pnpm/action-setup/pull/122">#122</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="fe02b34f77"><code>fe02b34</code></a>
docs: bump action-setup version in README</li>
<li><a
href="bee1f099e5"><code>bee1f09</code></a>
feat: throw error when multiple versions specified (<a
href="https://redirect.github.com/pnpm/action-setup/issues/122">#122</a>)</li>
<li><a
href="ce859e384f"><code>ce859e3</code></a>
refactor: replace <code>fs-extra</code> with Node.js built-in fs methods
(<a
href="https://redirect.github.com/pnpm/action-setup/issues/120">#120</a>)</li>
<li><a
href="2ab6dce4f5"><code>2ab6dce</code></a>
docs(README): fix link to LICENSE</li>
<li><a
href="e280758d01"><code>e280758</code></a>
docs(README): update dependency versions (<a
href="https://redirect.github.com/pnpm/action-setup/issues/117">#117</a>)</li>
<li><a
href="129abb77bf"><code>129abb7</code></a>
Bump undici from 5.28.2 to 5.28.3 (<a
href="https://redirect.github.com/pnpm/action-setup/issues/115">#115</a>)</li>
<li>See full diff in <a
href="https://github.com/pnpm/action-setup/compare/v3...v4">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=pnpm/action-setup&package-manager=github_actions&previous-version=3&new-version=4)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-15 15:57:33 -04:00
Elliot DeNolf
0618130fe3 chore(release): v3.0.0-beta.82 [skip ci] 2024-08-15 15:46:12 -04:00
Jacob Fletcher
cd245793fc chore(ui): resolves self-referencing imports (#7707) 2024-08-15 14:27:19 -04:00
Dan Ribbens
3a6c75a1a3 fix: importMap windows paths (#7706)
Fix windows compatibility for importMap generation
2024-08-15 17:57:48 +00:00
Alessio Gravili
5a683b6947 chore: fix issues running postgres in our dev test suites (#7704) 2024-08-15 16:58:00 +00:00
Elliot DeNolf
9b27f03e61 feat(eslint): no-imports-from-self rule (#7691)
New rule to prevent a package from importing from itself.
2024-08-15 09:02:29 -04:00
Elliot DeNolf
89746ebe09 chore(eslint): update relative import regex to handle more scenarios (#7690)
Updates no-relative-monorepo-import regex to handle more scenarios:

 Scenarios that will violate the rule:
```ts
import { something } from '../../payload/src/utilities/some-util.js'
import { something } from '../../../packages/payload/src/utilities/some-util.js'
import { something } from 'packages/payload/src/utilities/some-util.js'
```
2024-08-14 23:57:22 -04:00
dependabot[bot]
eacf2030cd chore(deps): bump the github_actions group with 2 updates (#7686)
Bumps the github_actions group with 2 updates:
[pnpm/action-setup](https://github.com/pnpm/action-setup) and
[supercharge/mongodb-github-action](https://github.com/supercharge/mongodb-github-action).

Updates `pnpm/action-setup` from 3 to 4
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/pnpm/action-setup/releases">pnpm/action-setup's
releases</a>.</em></p>
<blockquote>
<h2>v4.0.0</h2>
<p>An error is thrown if one version of pnpm is specified in the
<code>packageManager</code> field of <code>package.json</code> and a
different version is specified in the action's settings <a
href="https://redirect.github.com/pnpm/action-setup/pull/122">#122</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="fe02b34f77"><code>fe02b34</code></a>
docs: bump action-setup version in README</li>
<li><a
href="bee1f099e5"><code>bee1f09</code></a>
feat: throw error when multiple versions specified (<a
href="https://redirect.github.com/pnpm/action-setup/issues/122">#122</a>)</li>
<li><a
href="ce859e384f"><code>ce859e3</code></a>
refactor: replace <code>fs-extra</code> with Node.js built-in fs methods
(<a
href="https://redirect.github.com/pnpm/action-setup/issues/120">#120</a>)</li>
<li><a
href="2ab6dce4f5"><code>2ab6dce</code></a>
docs(README): fix link to LICENSE</li>
<li><a
href="e280758d01"><code>e280758</code></a>
docs(README): update dependency versions (<a
href="https://redirect.github.com/pnpm/action-setup/issues/117">#117</a>)</li>
<li><a
href="129abb77bf"><code>129abb7</code></a>
Bump undici from 5.28.2 to 5.28.3 (<a
href="https://redirect.github.com/pnpm/action-setup/issues/115">#115</a>)</li>
<li>See full diff in <a
href="https://github.com/pnpm/action-setup/compare/v3...v4">compare
view</a></li>
</ul>
</details>
<br />

Updates `supercharge/mongodb-github-action` from 1.10.0 to 1.11.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/supercharge/mongodb-github-action/releases">supercharge/mongodb-github-action's
releases</a>.</em></p>
<blockquote>
<p>Release 1.11.0</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/supercharge/mongodb-github-action/blob/main/CHANGELOG.md">supercharge/mongodb-github-action's
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/superchargejs/mongodb-github-action/compare/v1.10.0...v1.11.0">1.11.0</a>
- 2024-05-22</h2>
<h3>Added</h3>
<ul>
<li>added <code>mongodb-container-name</code> input: this option allows
you to define the Docker container name</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>use the <code>mongo</code> command to interact with MongoDB versions
4.x or lower. Previously, we only checked for MongoDB 4 and would use
<code>mongosh</code> for MongoDB 3 (and lower). <a
href="https://redirect.github.com/supercharge/mongodb-github-action/pull/61">Thanks
to Aravind!</a></li>
</ul>
<h3>Updated</h3>
<ul>
<li>bump dependencies</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="5a87bd81f8"><code>5a87bd8</code></a>
prepare changelog for 1.11.0</li>
<li><a
href="7c12fc679c"><code>7c12fc6</code></a>
update readme</li>
<li><a
href="ad73029553"><code>ad73029</code></a>
bump mongoose dependency</li>
<li><a
href="268fb2c93c"><code>268fb2c</code></a>
Merge pull request <a
href="https://redirect.github.com/supercharge/mongodb-github-action/issues/61">#61</a>
from aravindnc/main</li>
<li><a
href="12b898a9c8"><code>12b898a</code></a>
Fix to use mongo client if MongoDB verison is less than or equal to
4.</li>
<li><a
href="b8277548e0"><code>b827754</code></a>
wait 20 seconds</li>
<li><a
href="5f37c5fb42"><code>5f37c5f</code></a>
revert ESLint to 8.x</li>
<li><a
href="fcc7443a6b"><code>fcc7443</code></a>
bump verions</li>
<li><a
href="fde299bc70"><code>fde299b</code></a>
bump deps</li>
<li><a
href="9ceda80ede"><code>9ceda80</code></a>
bump versions of GitHub Actions</li>
<li>Additional commits viewable in <a
href="https://github.com/supercharge/mongodb-github-action/compare/1.10.0...1.11.0">compare
view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-14 21:56:24 -04:00
Elliot DeNolf
86428539f5 chore: add packageManager property for dependabot 2024-08-14 21:30:53 -04:00
Elliot DeNolf
a7f519c53a chore(release): v3.0.0-beta.81 [skip ci] 2024-08-14 19:53:07 -04:00
Alessio Gravili
49a2d70fbb feat: allow passing false as PayloadComponent which signals that the component should not be rendered (#7682)
If it's undefined/null => Fallback Component may be rendered
If it's false => No component should be rendered - as if an empty
component was passed in

This ensures that the user does not have to install `@payloadcms/ui`
anymore, which previously exported an empty component to be used in
component paths
2024-08-14 22:31:58 +00:00
Alessio Gravili
cb7fa00a6f fix: use tsx instead of swc as default bin script transpiler, as swc errors when it encounters 'next/cache' (#7681)
Fixes https://github.com/payloadcms/payload/issues/7677

- Payload bin scripts were not properly working on windows
- Use tsx by default instead of swc, as swc does not handle next/cache
imports without the .js at the end
- Support other node runtimes through --disable-transpile flag
2024-08-14 16:40:31 -04:00
Jacob Fletcher
a212cdef3f fix(next): supports root document view overrides as separate from default edit view (#7673)
## Description

We've since lost the ability to override the document view at the
root-level. This was a feature that made it possible to override _the
entire document routing/view structure_, including the document
header/tabs and all nested routes within, i.e. the API route/view, the
Live Preview route/view, etc. This is distinct from the "default" edit
view, which _only_ targets the component rendered within the "edit" tab.
This regression was introduced when types were simplified down to better
support "component paths" here: #7620. The `default` key was incorrectly
used as the "root" view override. To continue to support stricter types
_and_ root view overrides, a new `root` key has been added to the
`views` config.

You were previously able to do this:

```tsx
import { MyComponent } from './MyComponent.js'

export const MyCollection = {
  // ...
  admin: {
    views: {
      Edit: MyComponent
    }
  }
}
```

This is now done like this:

```tsx
export const MyCollection = {
  // ...
  admin: {
    views: {
      edit: {
        root: {
          Component: './path-to-my-component.js'
        }
      }
    }
  }
}
```

Some of the documentation was also incorrect according to the new
component paths API.

- [x] I have read and understand the
[CONTRIBUTING.md](https://github.com/payloadcms/payload/blob/main/CONTRIBUTING.md)
document in this repository.

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)
- [x] This change requires a documentation update

## Checklist:

- [x] Existing test suite passes locally with my changes
- [x] I have made corresponding changes to the documentation
2024-08-14 16:02:14 -04:00
Paul
4f323a3754 fix(ui): issue with checking for undefined json when autosave and validate is enabled (#7678) 2024-08-14 18:47:27 +00:00
Paul
f5e7578b41 chore: add command to run inside the importMap error (#7674) 2024-08-14 17:39:12 +00:00
106 changed files with 830 additions and 556 deletions

View File

@@ -25,7 +25,7 @@ runs:
node-version: ${{ inputs.node-version }}
- name: Install pnpm
uses: pnpm/action-setup@v3
uses: pnpm/action-setup@v4
with:
version: ${{ inputs.pnpm-version }}
run_install: false

View File

@@ -74,7 +74,7 @@ jobs:
node-version: ${{ env.NODE_VERSION }}
- name: Install pnpm
uses: pnpm/action-setup@v3
uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
run_install: false
@@ -120,7 +120,7 @@ jobs:
node-version: ${{ env.NODE_VERSION }}
- name: Install pnpm
uses: pnpm/action-setup@v3
uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
run_install: false
@@ -167,7 +167,7 @@ jobs:
node-version: ${{ env.NODE_VERSION }}
- name: Install pnpm
uses: pnpm/action-setup@v3
uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
run_install: false
@@ -217,7 +217,7 @@ jobs:
node-version: ${{ env.NODE_VERSION }}
- name: Install pnpm
uses: pnpm/action-setup@v3
uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
run_install: false
@@ -332,7 +332,7 @@ jobs:
node-version: ${{ env.NODE_VERSION }}
- name: Install pnpm
uses: pnpm/action-setup@v3
uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
run_install: false
@@ -407,7 +407,7 @@ jobs:
node-version: ${{ env.NODE_VERSION }}
- name: Install pnpm
uses: pnpm/action-setup@v3
uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
run_install: false
@@ -420,7 +420,7 @@ jobs:
key: ${{ github.sha }}-${{ github.run_number }}
- name: Start MongoDB
uses: supercharge/mongodb-github-action@1.10.0
uses: supercharge/mongodb-github-action@1.11.0
with:
mongodb-version: 6.0
@@ -451,7 +451,7 @@ jobs:
node-version: ${{ env.NODE_VERSION }}
- name: Install pnpm
uses: pnpm/action-setup@v3
uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
run_install: false
@@ -492,7 +492,7 @@ jobs:
node-version: ${{ env.NODE_VERSION }}
- name: Start MongoDB
uses: supercharge/mongodb-github-action@1.10.0
uses: supercharge/mongodb-github-action@1.11.0
with:
mongodb-version: 6.0
@@ -520,7 +520,7 @@ jobs:
node-version: ${{ env.NODE_VERSION }}
- name: Install pnpm
uses: pnpm/action-setup@v3
uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
run_install: false

View File

@@ -145,7 +145,7 @@ Instead, we utilize component paths to reference React Components. This method e
When constructing the `ClientConfig`, Payload uses the component paths as keys to fetch the corresponding React Component imports from the Import Map. It then substitutes the `PayloadComponent` with a `MappedComponent`. A `MappedComponent` includes the React Component and additional metadata, such as whether it's a server or a client component and which props it should receive. These components are then rendered through the `<RenderComponent />` component within the Payload Admin Panel.
Import maps are regenerated whenever you modify any element related to component paths. This regeneration occurs at startup and whenever Hot Module Replacement (HMR) runs. If the import maps fail to regenerate during HMR, you can restart your application and execute the `payload generate:importmap` command to manually create a new import map.
Import maps are regenerated whenever you modify any element related to component paths. This regeneration occurs at startup and whenever Hot Module Replacement (HMR) runs. If the import maps fail to regenerate during HMR, you can restart your application and execute the `payload generate:importmap` command to manually create a new import map. If you encounter any errors running this command, see the [Troubleshooting](/docs/beta/local-api/outside-nextjs#troubleshooting) section.
### Component paths in external packages

View File

@@ -53,7 +53,7 @@ For more granular control, pass a configuration object instead. Payload exposes
| Property | Description |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **`Component`** \* | Pass in the component that should be rendered when a user navigates to this route. |
| **`Component`** \* | Pass in the component path that should be rendered when a user navigates to this route. |
| **`path`** \* | Any valid URL path or array of paths that [`path-to-regexp`](https://www.npmjs.com/package/path-to-regex) understands. |
| **`exact`** | Boolean. When true, will only match if the path matches the `usePathname()` exactly. |
| **`strict`** | When true, a path that has a trailing slash will only match a `location.pathname` with a trailing slash. This has no effect when there are additional URL segments in the pathname. |
@@ -111,7 +111,20 @@ export const MyCollectionConfig: SanitizedCollectionConfig = {
components: {
views: {
edit: {
Component: '/path/to/MyCustomEditView', // highlight-line
root: {
Component: '/path/to/MyCustomEditView', // highlight-line
}
// other options include:
// default
// versions
// version
// api
// livePreview
// [key: string]
// See "Document Views" for more details
},
list: {
Component: '/path/to/MyCustomListView',
}
},
},
@@ -123,7 +136,7 @@ _For details on how to build Custom Views, see [Building Custom Views](#building
<Banner type="warning">
<strong>Note:</strong>
The `Edit` property will replace the _entire_ Edit View, including the title, tabs, etc., _as well as all nested [Document Views](#document-views)_, such as the API, Live Preview, and Version views. To replace only the Edit View precisely, use the `Edit.Default` key instead.
The `root` property will replace the _entire_ Edit View, including the title, tabs, etc., _as well as all nested [Document Views](#document-views)_, such as the API, Live Preview, and Version views. To replace only the Edit View precisely, use the `edit.default` key instead.
</Banner>
The following options are available:
@@ -152,18 +165,29 @@ export const MyGlobalConfig: SanitizedGlobalConfig = {
admin: {
components: {
views: {
edit: '/path/to/MyCustomEditView', // highlight-line
edit: {
root: {
Component: '/path/to/MyCustomEditView', // highlight-line
}
// other options include:
// default
// versions
// version
// api
// livePreview
// [key: string]
},
},
},
},
})
}
```
_For details on how to build Custom Views, see [Building Custom Views](#building-custom-views)._
<Banner type="warning">
<strong>Note:</strong>
The `Edit` property will replace the _entire_ Edit View, including the title, tabs, etc., _as well as all nested [Document Views](#document-views)_, such as the API, Live Preview, and Version views. To replace only the Edit View precisely, use the `Edit.Default` key instead.
The `root` property will replace the _entire_ Edit View, including the title, tabs, etc., _as well as all nested [Document Views](#document-views)_, such as the API, Live Preview, and Version views. To replace only the Edit View precisely, use the `edit.default` key instead.
</Banner>
The following options are available:
@@ -199,25 +223,26 @@ export const MyCollectionOrGlobalConfig: SanitizedCollectionConfig = {
},
},
},
})
}
```
_For details on how to build Custom Views, see [Building Custom Views](#building-custom-views)._
<Banner type="warning">
<strong>Note:</strong>
If you need to replace the _entire_ Edit View, including _all_ nested Document Views, use the `Edit` key itself. See [Custom Collection Views](#collection-views) or [Custom Global Views](#global-views) for more information.
If you need to replace the _entire_ Edit View, including _all_ nested Document Views, use the `root` key. See [Custom Collection Views](#collection-views) or [Custom Global Views](#global-views) for more information.
</Banner>
The following options are available:
| Property | Description |
| ----------------- | --------------------------------------------------------------------------------------------------------------------------- |
| **`default`** | The Default view is the primary view in which your document is edited. |
| **`versions`** | The Versions view is used to view the version history of a single document. [More details](../versions). |
| **`version`** | The Version view is used to view a single version of a single document for a given collection. [More details](../versions). |
| **`api`** | The API view is used to display the REST API JSON response for a given document. |
| **`livePreview`** | The LivePreview view is used to display the Live Preview interface. [More details](../live-preview). |
| **`root`** | The Root View overrides all other nested views and routes. No document controls or tabs are rendered when this key is set. |
| **`default`** | The Default View is the primary view in which your document is edited. It is rendered within the "Edit" tab. |
| **`versions`** | The Versions View is used to navigate the version history of a single document. It is rendered within the "Versions" tab. [More details](../versions). |
| **`version`** | The Version View is used to edit a single version of a document. It is rendered within the "Version" tab. [More details](../versions). |
| **`api`** | The API View is used to display the REST API JSON response for a given document. It is rendered within the "API" tab. |
| **`livePreview`** | The LivePreview view is used to display the Live Preview interface. It is rendered within the "Live Preview" tab. [More details](../live-preview). |
### Document Tabs

View File

@@ -61,14 +61,27 @@ payload run src/seed.ts
The `payload run` command does two things for you:
1. It loads the environment variables the same way Next.js loads them, eliminating the need for additional dependencies like `dotenv`. The usage of `dotenv` is not recommended, as Next.js loads environment variables differently. By using `payload run`, you ensure consistent environment variable handling across your Payload and Next.js setup.
2. It initializes swc, allowing direct execution of TypeScript files without requiring tools like tsx or ts-node.
2. It initializes tsx, allowing direct execution of TypeScript files manually installing tools like tsx or ts-node.
### Troubleshooting
If you encounter import-related errors, try running the script in TSX mode:
If you encounter import-related errors, you have 2 options:
#### Option 1: enable swc mode by appending `--use-swc` to the `payload` command:
Example:
```sh
payload run src/seed.ts --use-tsx
payload run src/seed.ts --use-swc
```
Note: Install tsx in your project first. Be aware that TSX mode is slower than the default swc mode, so only use it if necessary.
Note: Install @swc-node/register in your project first. While swc mode is faster than the default tsx mode, it might break for some imports.
#### Option 2: use an alternative runtime like bun
While we do not guarantee support for alternative runtimes, you are free to use them and disable payloads own transpilation by appending the `--disable-transpilation` flag to the `payload` command:
```sh
bunx --bun payload run src/seed.ts --disable-transpile
```
You will need to have bun installed on your system for this to work.

View File

@@ -119,7 +119,7 @@ A function that allows you to return any meta title, including from document's c
{
// ...
seoPlugin({
generateTitle: ({ ...docInfo, doc, locale }) => `Website.com — ${doc?.title}`,
generateTitle: ({ ...docInfo, doc, locale, req }) => `Website.com — ${doc?.title}`,
})
}
```
@@ -133,7 +133,7 @@ A function that allows you to return any meta description, including from docume
{
// ...
seoPlugin({
generateDescription: ({ ...docInfo, doc, locale }) => doc?.excerpt,
generateDescription: ({ ...docInfo, doc, locale, req }) => doc?.excerpt,
})
}
```
@@ -147,7 +147,7 @@ A function that allows you to return any meta image, including from document's c
{
// ...
seoPlugin({
generateImage: ({ ...docInfo, doc, locale }) => doc?.featuredImage,
generateImage: ({ ...docInfo, doc, locale, req }) => doc?.featuredImage,
})
}
```
@@ -161,7 +161,7 @@ A function called by the search preview component to display the actual URL of y
{
// ...
seoPlugin({
generateURL: ({ ...docInfo, doc, locale }) =>
generateURL: ({ ...docInfo, doc, locale, req }) =>
`https://yoursite.com/${collection?.slug}/${doc?.slug}`,
})
}

View File

@@ -62,6 +62,7 @@ export const rootEslintConfig = [
'payload/no-jsx-import-statements': 'warn',
'payload/no-relative-monorepo-imports': 'error',
'payload/no-imports-from-exports-dir': 'error',
'payload/no-imports-from-self': 'error',
},
},
{

View File

@@ -1,6 +1,6 @@
{
"name": "payload-monorepo",
"version": "3.0.0-beta.80",
"version": "3.0.0-beta.84",
"private": true,
"type": "module",
"scripts": {
@@ -103,6 +103,8 @@
"@payloadcms/eslint-config": "workspace:*",
"@payloadcms/eslint-plugin": "workspace:*",
"@payloadcms/live-preview-react": "workspace:*",
"@payloadcms/db-postgres": "workspace:*",
"drizzle-kit": "0.23.2-df9e596",
"@playwright/test": "1.46.0",
"@swc-node/register": "1.10.9",
"@swc/cli": "0.4.0",
@@ -164,6 +166,7 @@
"node": "^18.20.2 || >=20.9.0",
"pnpm": "^9.7.0"
},
"packageManager": "pnpm@9.7.0",
"pnpm": {
"allowedDeprecatedVersions": {
"abab": "2",

View File

@@ -1,6 +1,6 @@
{
"name": "create-payload-app",
"version": "3.0.0-beta.80",
"version": "3.0.0-beta.84",
"homepage": "https://payloadcms.com",
"repository": {
"type": "git",

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/db-mongodb",
"version": "3.0.0-beta.80",
"version": "3.0.0-beta.84",
"description": "The officially supported MongoDB database adapter for Payload",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/db-postgres",
"version": "3.0.0-beta.80",
"version": "3.0.0-beta.84",
"description": "The officially supported Postgres database adapter for Payload",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/db-sqlite",
"version": "3.0.0-beta.80",
"version": "3.0.0-beta.84",
"description": "The officially supported SQLite database adapter for Payload",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/drizzle",
"version": "3.0.0-beta.80",
"version": "3.0.0-beta.84",
"description": "A library of shared functions used by different payload database adapters",
"homepage": "https://payloadcms.com",
"repository": {
@@ -58,11 +58,13 @@
"exports": {
".": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"./types": {
"import": "./dist/types.js",
"types": "./dist/types.d.ts"
"types": "./dist/types.d.ts",
"default": "./dist/types.js"
}
},
"main": "./dist/index.js",

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/email-nodemailer",
"version": "3.0.0-beta.80",
"version": "3.0.0-beta.84",
"description": "Payload Nodemailer Email Adapter",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/email-resend",
"version": "3.0.0-beta.80",
"version": "3.0.0-beta.84",
"description": "Payload Resend Email Adapter",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -0,0 +1,67 @@
import fs from 'fs'
import path from 'path'
/** @type {import('eslint').Rule.RuleModule} */
export const rule = {
meta: {
docs: {
description: 'Disallow a package from importing from itself',
category: 'Best Practices',
recommended: true,
},
fixable: 'code',
schema: [],
},
create(context) {
let packageName = null
return {
ImportDeclaration(node) {
const importPath = node.source.value
const pkgName = getPackageName(context, packageName)
if (pkgName && importPath.startsWith(pkgName)) {
context.report({
node,
message: `Package "${pkgName}" should not import from itself. Use relative instead.`,
})
}
},
}
},
}
export default rule
/**
* @param {import('eslint').Rule.RuleContext} context
* @param {string|undefined} packageName
*/
function getPackageName(context, packageName) {
if (packageName) {
return packageName
}
const fileName = context.getFilename()
const pkg = findNearestPackageJson(path.dirname(fileName))
if (pkg) {
return pkg.name
}
}
/**
* @param {string} startDir
*/
function findNearestPackageJson(startDir) {
let currentDir = startDir
while (currentDir !== path.dirname(currentDir)) {
// Root directory check
const pkgPath = path.join(currentDir, 'package.json')
if (fs.existsSync(pkgPath)) {
const pkgContent = JSON.parse(fs.readFileSync(pkgPath, 'utf8'))
return pkgContent
}
currentDir = path.dirname(currentDir)
}
return null
}

View File

@@ -21,7 +21,7 @@ export const rule = {
const importPath = node.source.value
// Match imports starting with any number of "../" followed by "packages/"
const regex = /^(\.\.\/)*packages\/[^/]+\/src/
const regex = /^(\.\.\/)*((?!src\b)\w+\/)+src\//
if (regex.test(importPath)) {
context.report({

View File

@@ -3,6 +3,7 @@ import noNonRetryableAssertions from './customRules/no-non-retryable-assertions.
import noRelativeMonorepoImports from './customRules/no-relative-monorepo-imports.js'
import noImportsFromExportsDir from './customRules/no-imports-from-exports-dir.js'
import noFlakyAssertions from './customRules/no-flaky-assertions.js'
import noImportsFromSelf from './customRules/no-imports-from-self.js'
/**
* @type {import('eslint').ESLint.Plugin}
@@ -13,6 +14,7 @@ const index = {
'no-non-retryable-assertions': noNonRetryableAssertions,
'no-relative-monorepo-imports': noRelativeMonorepoImports,
'no-imports-from-exports-dir': noImportsFromExportsDir,
'no-imports-from-self': noImportsFromSelf,
'no-flaky-assertions': noFlakyAssertions,
'no-wait-function': {
create: function (context) {

View File

@@ -1,21 +1,54 @@
#!/usr/bin/env node
#!/usr/bin/env node --no-deprecation
import { register } from 'node:module'
import path from 'node:path'
import { fileURLToPath, pathToFileURL } from 'node:url'
// Allow disabling SWC for debugging
if (process.env.DISABLE_SWC !== 'true') {
const useSwc = process.argv.includes('--use-swc')
const disableTranspile = process.argv.includes('--disable-transpile')
if (disableTranspile) {
// Remove --disable-transpile from arguments
process.argv = process.argv.filter((arg) => arg !== '--disable-transpile')
const start = async () => {
const { bin } = await import('./dist/bin/index.js')
await bin()
}
void start()
} else {
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
const url = pathToFileURL(dirname).toString() + '/'
register('@swc-node/register/esm', url)
}
if (!useSwc) {
const start = async () => {
// Use tsx
let tsImport = (await import('tsx/esm/api')).tsImport
const start = async () => {
const { bin } = await import('./dist/bin/index.js')
await bin()
}
const { bin } = await tsImport('./dist/bin/index.js', url)
await bin()
}
void start()
void start()
} else if (useSwc) {
const { register } = await import('node:module')
// Remove --use-swc from arguments
process.argv = process.argv.filter((arg) => arg !== '--use-swc')
try {
register('@swc-node/register/esm', url)
} catch (_) {
console.error(
'@swc-node/register is not installed. Please install @swc-node/register in your project, if you want to use swc in payload run.',
)
}
const start = async () => {
const { bin } = await import('./dist/bin/index.js')
await bin()
}
void start()
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/graphql",
"version": "3.0.0-beta.80",
"version": "3.0.0-beta.84",
"homepage": "https://payloadcms.com",
"repository": {
"type": "git",
@@ -43,7 +43,7 @@
"dependencies": {
"graphql-scalars": "1.22.2",
"pluralize": "8.0.0",
"@swc-node/register": "1.10.9",
"tsx": "4.17.0",
"ts-essentials": "7.0.3"
},
"devDependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/live-preview-react",
"version": "3.0.0-beta.80",
"version": "3.0.0-beta.84",
"description": "The official React SDK for Payload Live Preview",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/live-preview-vue",
"version": "3.0.0-beta.80",
"version": "3.0.0-beta.84",
"description": "The official Vue SDK for Payload Live Preview",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/live-preview",
"version": "3.0.0-beta.80",
"version": "3.0.0-beta.84",
"description": "The official live preview JavaScript SDK for Payload",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/next",
"version": "3.0.0-beta.80",
"version": "3.0.0-beta.84",
"homepage": "https://payloadcms.com",
"repository": {
"type": "git",

View File

@@ -2,8 +2,8 @@
.doc-header {
width: 100%;
margin-top: base(0.5);
padding-bottom: calc(var(--base) * 1.5);
margin-top: base(0.4);
padding-bottom: calc(var(--base) * 1.2);
display: flex;
align-items: center;
position: relative;
@@ -27,6 +27,9 @@
overflow: hidden;
text-overflow: ellipsis;
margin: 0;
padding-bottom: base(0.2);
line-height: 1;
vertical-align: top;
}
@include mid-break {

View File

@@ -8,7 +8,7 @@
position: absolute;
order: 3;
left: unset;
inset-inline-end: base(0.5);
inset-inline-end: base(0.8);
top: 50%;
transform: translateY(-50%);
color: var(--theme-elevation-600);
@@ -16,8 +16,8 @@
border: none;
svg {
width: base(0.75);
height: base(0.75);
width: base(0.8);
height: base(0.8);
}
&:hover {
@@ -33,10 +33,11 @@
.toast-title {
line-height: base(1);
margin-right: base(1);
}
.payload-toast-item {
padding: base(0.5);
padding: base(0.8);
color: var(--theme-elevation-800);
font-style: normal;
font-weight: 600;
@@ -69,8 +70,8 @@
}
.toast-icon {
width: base(1);
height: base(1);
width: base(0.8);
height: base(0.8);
margin: 0;
display: flex;
align-items: center;
@@ -84,8 +85,8 @@
&.toast-warning {
color: var(--theme-warning-800);
border-color: var(--theme-warning-150);
background-color: var(--theme-warning-50);
border-color: var(--theme-warning-250);
background-color: var(--theme-warning-100);
.payload-toast-close-button {
color: var(--theme-warning-600);
@@ -98,8 +99,8 @@
&.toast-error {
color: var(--theme-error-800);
border-color: var(--theme-error-150);
background-color: var(--theme-error-50);
border-color: var(--theme-error-250);
background-color: var(--theme-error-100);
.payload-toast-close-button {
color: var(--theme-error-600);
@@ -112,8 +113,8 @@
&.toast-success {
color: var(--theme-success-800);
border-color: var(--theme-success-150);
background-color: var(--theme-success-50);
border-color: var(--theme-success-250);
background-color: var(--theme-success-100);
.payload-toast-close-button {
color: var(--theme-success-600);
@@ -126,8 +127,8 @@
&.toast-info {
color: var(--theme-elevation-800);
border-color: var(--theme-elevation-150);
background-color: var(--theme-elevation-50);
border-color: var(--theme-elevation-250);
background-color: var(--theme-elevation-100);
.payload-toast-close-button {
color: var(--theme-elevation-600);

View File

@@ -66,139 +66,89 @@ export const getViewsFromConfig = ({
config?.admin?.livePreview?.globals?.includes(globalConfig?.slug)
if (collectionConfig) {
const editConfig = collectionConfig?.admin?.components?.views?.edit
const EditOverride = typeof editConfig === 'function' ? editConfig : null
const [collectionEntity, collectionSlug, segment3, segment4, segment5, ...remainingSegments] =
routeSegments
if (EditOverride) {
CustomView = EditOverride
}
if (!EditOverride) {
const [collectionEntity, collectionSlug, segment3, segment4, segment5, ...remainingSegments] =
routeSegments
if (!docPermissions?.read?.permission) {
notFound()
} else {
// `../:id`, or `../create`
switch (routeSegments.length) {
case 3: {
switch (segment3) {
case 'create': {
if ('create' in docPermissions && docPermissions?.create?.permission) {
CustomView = {
payloadComponent: getCustomViewByKey(views, 'default'),
}
DefaultView = {
Component: DefaultEditView,
}
} else {
ErrorView = {
Component: UnauthorizedView,
}
}
break
}
default: {
if (!docPermissions?.read?.permission) {
notFound()
} else {
// `../:id`, or `../create`
switch (routeSegments.length) {
case 3: {
switch (segment3) {
case 'create': {
if ('create' in docPermissions && docPermissions?.create?.permission) {
CustomView = {
payloadComponent: getCustomViewByKey(views, 'default'),
}
DefaultView = {
Component: DefaultEditView,
}
break
} else {
ErrorView = {
Component: UnauthorizedView,
}
}
break
}
break
}
// `../:id/api`, `../:id/preview`, `../:id/versions`, etc
case 4: {
switch (segment4) {
case 'api': {
if (collectionConfig?.admin?.hideAPIURL !== true) {
CustomView = {
payloadComponent: getCustomViewByKey(views, 'api'),
}
DefaultView = {
Component: DefaultAPIView,
}
}
break
default: {
CustomView = {
payloadComponent: getCustomViewByKey(views, 'default'),
}
case 'preview': {
if (livePreviewEnabled) {
DefaultView = {
Component: DefaultLivePreviewView,
}
}
break
}
case 'versions': {
if (docPermissions?.readVersions?.permission) {
CustomView = {
payloadComponent: getCustomViewByKey(views, 'versions'),
}
DefaultView = {
Component: DefaultVersionsView,
}
} else {
ErrorView = {
Component: UnauthorizedView,
}
}
break
}
default: {
const baseRoute = [
adminRoute !== '/' && adminRoute,
'collections',
collectionSlug,
segment3,
]
.filter(Boolean)
.join('/')
const currentRoute = [baseRoute, segment4, segment5, ...remainingSegments]
.filter(Boolean)
.join('/')
CustomView = {
payloadComponent: getCustomViewByRoute({
baseRoute,
currentRoute,
views,
}),
}
break
DefaultView = {
Component: DefaultEditView,
}
break
}
break
}
break
}
// `../:id/versions/:version`, etc
default: {
if (segment4 === 'versions') {
if (docPermissions?.readVersions?.permission) {
// `../:id/api`, `../:id/preview`, `../:id/versions`, etc
case 4: {
switch (segment4) {
case 'api': {
if (collectionConfig?.admin?.hideAPIURL !== true) {
CustomView = {
payloadComponent: getCustomViewByKey(views, 'version'),
payloadComponent: getCustomViewByKey(views, 'api'),
}
DefaultView = {
Component: DefaultVersionView,
Component: DefaultAPIView,
}
}
break
}
case 'preview': {
if (livePreviewEnabled) {
DefaultView = {
Component: DefaultLivePreviewView,
}
}
break
}
case 'versions': {
if (docPermissions?.readVersions?.permission) {
CustomView = {
payloadComponent: getCustomViewByKey(views, 'versions'),
}
DefaultView = {
Component: DefaultVersionsView,
}
} else {
ErrorView = {
Component: UnauthorizedView,
}
}
} else {
break
}
default: {
const baseRoute = [
adminRoute !== '/' && adminRoute,
collectionEntity,
'collections',
collectionSlug,
segment3,
]
@@ -216,144 +166,176 @@ export const getViewsFromConfig = ({
views,
}),
}
break
}
break
}
break
}
// `../:id/versions/:version`, etc
default: {
if (segment4 === 'versions') {
if (docPermissions?.readVersions?.permission) {
CustomView = {
payloadComponent: getCustomViewByKey(views, 'version'),
}
DefaultView = {
Component: DefaultVersionView,
}
} else {
ErrorView = {
Component: UnauthorizedView,
}
}
} else {
const baseRoute = [
adminRoute !== '/' && adminRoute,
collectionEntity,
collectionSlug,
segment3,
]
.filter(Boolean)
.join('/')
const currentRoute = [baseRoute, segment4, segment5, ...remainingSegments]
.filter(Boolean)
.join('/')
CustomView = {
payloadComponent: getCustomViewByRoute({
baseRoute,
currentRoute,
views,
}),
}
}
break
}
}
}
}
if (globalConfig) {
const editConfig = globalConfig?.admin?.components?.views?.edit
const EditOverride = typeof editConfig === 'function' ? editConfig : null
const [globalEntity, globalSlug, segment3, ...remainingSegments] = routeSegments
if (EditOverride) {
CustomView = EditOverride
}
if (!EditOverride) {
const [globalEntity, globalSlug, segment3, ...remainingSegments] = routeSegments
if (!docPermissions?.read?.permission) {
notFound()
} else {
switch (routeSegments.length) {
case 2: {
CustomView = {
payloadComponent: getCustomViewByKey(views, 'default'),
}
DefaultView = {
Component: DefaultEditView,
}
break
if (!docPermissions?.read?.permission) {
notFound()
} else {
switch (routeSegments.length) {
case 2: {
CustomView = {
payloadComponent: getCustomViewByKey(views, 'default'),
}
case 3: {
// `../:slug/api`, `../:slug/preview`, `../:slug/versions`, etc
switch (segment3) {
case 'api': {
if (globalConfig?.admin?.hideAPIURL !== true) {
CustomView = {
payloadComponent: getCustomViewByKey(views, 'api'),
}
DefaultView = {
Component: DefaultAPIView,
}
}
break
}
case 'preview': {
if (livePreviewEnabled) {
DefaultView = {
Component: DefaultLivePreviewView,
}
}
break
}
case 'versions': {
if (docPermissions?.readVersions?.permission) {
CustomView = {
payloadComponent: getCustomViewByKey(views, 'versions'),
}
DefaultView = {
Component: DefaultVersionsView,
}
} else {
ErrorView = {
Component: UnauthorizedView,
}
}
break
}
default: {
if (docPermissions?.read?.permission) {
const baseRoute = [adminRoute, globalEntity, globalSlug, segment3]
.filter(Boolean)
.join('/')
const currentRoute = [baseRoute, segment3, ...remainingSegments]
.filter(Boolean)
.join('/')
CustomView = {
payloadComponent: getCustomViewByRoute({
baseRoute,
currentRoute,
views,
}),
}
DefaultView = {
Component: DefaultEditView,
}
} else {
ErrorView = {
Component: UnauthorizedView,
}
}
break
}
}
break
DefaultView = {
Component: DefaultEditView,
}
break
}
default: {
// `../:slug/versions/:version`, etc
if (segment3 === 'versions') {
if (docPermissions?.readVersions?.permission) {
case 3: {
// `../:slug/api`, `../:slug/preview`, `../:slug/versions`, etc
switch (segment3) {
case 'api': {
if (globalConfig?.admin?.hideAPIURL !== true) {
CustomView = {
payloadComponent: getCustomViewByKey(views, 'version'),
payloadComponent: getCustomViewByKey(views, 'api'),
}
DefaultView = {
Component: DefaultVersionView,
Component: DefaultAPIView,
}
}
break
}
case 'preview': {
if (livePreviewEnabled) {
DefaultView = {
Component: DefaultLivePreviewView,
}
}
break
}
case 'versions': {
if (docPermissions?.readVersions?.permission) {
CustomView = {
payloadComponent: getCustomViewByKey(views, 'versions'),
}
DefaultView = {
Component: DefaultVersionsView,
}
} else {
ErrorView = {
Component: UnauthorizedView,
}
}
} else {
const baseRoute = [adminRoute !== '/' && adminRoute, 'globals', globalSlug]
.filter(Boolean)
.join('/')
break
}
const currentRoute = [baseRoute, segment3, ...remainingSegments]
.filter(Boolean)
.join('/')
default: {
if (docPermissions?.read?.permission) {
const baseRoute = [adminRoute, globalEntity, globalSlug, segment3]
.filter(Boolean)
.join('/')
const currentRoute = [baseRoute, segment3, ...remainingSegments]
.filter(Boolean)
.join('/')
CustomView = {
payloadComponent: getCustomViewByRoute({
baseRoute,
currentRoute,
views,
}),
}
DefaultView = {
Component: DefaultEditView,
}
} else {
ErrorView = {
Component: UnauthorizedView,
}
}
break
}
}
break
}
default: {
// `../:slug/versions/:version`, etc
if (segment3 === 'versions') {
if (docPermissions?.readVersions?.permission) {
CustomView = {
payloadComponent: getCustomViewByRoute({
baseRoute,
currentRoute,
views,
}),
payloadComponent: getCustomViewByKey(views, 'version'),
}
DefaultView = {
Component: DefaultVersionView,
}
} else {
ErrorView = {
Component: UnauthorizedView,
}
}
break
} else {
const baseRoute = [adminRoute !== '/' && adminRoute, 'globals', globalSlug]
.filter(Boolean)
.join('/')
const currentRoute = [baseRoute, segment3, ...remainingSegments]
.filter(Boolean)
.join('/')
CustomView = {
payloadComponent: getCustomViewByRoute({
baseRoute,
currentRoute,
views,
}),
}
}
break
}
}
}

View File

@@ -60,7 +60,7 @@ export const Document: React.FC<AdminViewProps> = async ({
const isEditing = getIsEditing({ id, collectionSlug, globalSlug })
let ViewOverride: MappedComponent<ServerSideEditViewProps>
let RootViewOverride: MappedComponent<ServerSideEditViewProps>
let CustomView: MappedComponent<ServerSideEditViewProps>
let DefaultView: MappedComponent<ServerSideEditViewProps>
let ErrorView: MappedComponent<AdminViewProps>
@@ -115,19 +115,18 @@ export const Document: React.FC<AdminViewProps> = async ({
apiURL = `${serverURL}${apiRoute}/${collectionSlug}/${id}${apiQueryParams}`
ViewOverride =
collectionConfig?.admin?.components?.views?.edit?.default &&
'Component' in collectionConfig.admin.components.views.edit.default
RootViewOverride =
collectionConfig?.admin?.components?.views?.edit?.root &&
'Component' in collectionConfig.admin.components.views.edit.root
? createMappedComponent(
collectionConfig?.admin?.components?.views?.edit?.default
?.Component as EditViewComponent, // some type info gets lost from Config => SanitizedConfig due to our usage of Deep type operations from ts-essentials. Despite .Component being defined as EditViewComponent, this info is lost and we need cast it here.
collectionConfig?.admin?.components?.views?.edit?.root?.Component as EditViewComponent, // some type info gets lost from Config => SanitizedConfig due to our usage of Deep type operations from ts-essentials. Despite .Component being defined as EditViewComponent, this info is lost and we need cast it here.
undefined,
undefined,
'collectionConfig?.admin?.components?.views?.edit?.default',
'collectionConfig?.admin?.components?.views?.edit?.root',
)
: null
if (!ViewOverride) {
if (!RootViewOverride) {
const collectionViews = getViewsFromConfig({
collectionConfig,
config,
@@ -157,7 +156,7 @@ export const Document: React.FC<AdminViewProps> = async ({
)
}
if (!CustomView && !DefaultView && !ViewOverride && !ErrorView) {
if (!CustomView && !DefaultView && !RootViewOverride && !ErrorView) {
ErrorView = createMappedComponent(undefined, undefined, NotFoundView, 'NotFoundView')
}
}
@@ -170,9 +169,11 @@ export const Document: React.FC<AdminViewProps> = async ({
const params = new URLSearchParams({
locale: locale?.code,
})
if (globalConfig.versions?.drafts) {
params.append('draft', 'true')
}
if (locale?.code) {
params.append('locale', locale.code)
}
@@ -181,10 +182,18 @@ export const Document: React.FC<AdminViewProps> = async ({
apiURL = `${serverURL}${apiRoute}/${globalSlug}${apiQueryParams}`
const editConfig = globalConfig?.admin?.components?.views?.edit
ViewOverride = typeof editConfig === 'function' ? editConfig : null
RootViewOverride =
globalConfig?.admin?.components?.views?.edit?.root &&
'Component' in globalConfig.admin.components.views.edit.root
? createMappedComponent(
globalConfig?.admin?.components?.views?.edit?.root?.Component as EditViewComponent, // some type info gets lost from Config => SanitizedConfig due to our usage of Deep type operations from ts-essentials. Despite .Component being defined as EditViewComponent, this info is lost and we need cast it here.
undefined,
undefined,
'globalConfig?.admin?.components?.views?.edit?.root',
)
: null
if (!ViewOverride) {
if (!RootViewOverride) {
const globalViews = getViewsFromConfig({
config,
docPermissions,
@@ -213,7 +222,7 @@ export const Document: React.FC<AdminViewProps> = async ({
'globalViews?.ErrorView.payloadComponent',
)
if (!CustomView && !DefaultView && !ViewOverride && !ErrorView) {
if (!CustomView && !DefaultView && !RootViewOverride && !ErrorView) {
ErrorView = createMappedComponent(undefined, undefined, NotFoundView, 'NotFoundView')
}
}
@@ -268,7 +277,7 @@ export const Document: React.FC<AdminViewProps> = async ({
initialState={formState}
isEditing={isEditing}
>
{!ViewOverride && (
{!RootViewOverride && (
<DocumentHeader
collectionConfig={collectionConfig}
globalConfig={globalConfig}
@@ -294,7 +303,9 @@ export const Document: React.FC<AdminViewProps> = async ({
<RenderComponent mappedComponent={ErrorView} />
) : (
<RenderComponent
mappedComponent={ViewOverride ? ViewOverride : CustomView ? CustomView : DefaultView}
mappedComponent={
RootViewOverride ? RootViewOverride : CustomView ? CustomView : DefaultView
}
/>
)}
</EditDepthProvider>

View File

@@ -149,8 +149,10 @@ export const Auth: React.FC<Props> = (props) => {
{(showPasswordFields || requirePassword) && (
<div className={`${baseClass}__changing-password`}>
<PasswordField
autoComplete="new-password"
field={{
name: 'password',
_path: 'password',
admin: {
disabled,
},

View File

@@ -98,14 +98,18 @@ export const DefaultEditView: React.FC = () => {
if (globalSlug) classes.push(`global-edit--${globalSlug}`)
if (collectionSlug) classes.push(`collection-edit--${collectionSlug}`)
const [schemaPath, setSchemaPath] = React.useState(entitySlug)
const [schemaPath, setSchemaPath] = React.useState(() => {
if (operation === 'create' && auth && !auth.disableLocalStrategy) {
return `_${entitySlug}.auth`
}
return entitySlug
})
const [validateBeforeSubmit, setValidateBeforeSubmit] = useState(() => {
if (
operation === 'create' &&
collectionConfig.auth &&
!collectionConfig.auth.disableLocalStrategy
)
if (operation === 'create' && auth && !auth.disableLocalStrategy) {
return true
}
return false
})

View File

@@ -13,7 +13,7 @@
&__header {
display: flex;
align-items: center;
align-items: flex-end;
flex-wrap: wrap;
gap: base(0.8);
@@ -27,7 +27,7 @@
.pill {
position: relative;
margin: 0;
margin: 0 0 base(0.2);
}
}

View File

@@ -1,18 +1,14 @@
#!/usr/bin/env node --no-deprecation
#!/usr/bin/env -S node --no-deprecation
import { register } from 'node:module'
import path from 'node:path'
import { fileURLToPath, pathToFileURL } from 'node:url'
const useTsx = process.argv.includes('--use-tsx')
const useSwc = process.argv.includes('--use-swc')
const disableTranspile = process.argv.includes('--disable-transpile')
// Allow disabling SWC/TSX for debugging
if (process.env.DISABLE_SWC !== 'true' && !useTsx) {
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
const url = pathToFileURL(dirname).toString() + '/'
register('@swc-node/register/esm', url)
if (disableTranspile) {
// Remove --disable-transpile from arguments
process.argv = process.argv.filter((arg) => arg !== '--disable-transpile')
const start = async () => {
const { bin } = await import('./dist/bin/index.js')
@@ -20,25 +16,39 @@ if (process.env.DISABLE_SWC !== 'true' && !useTsx) {
}
void start()
} else if (useTsx) {
// Remove --use-tsx from arguments
process.argv = process.argv.filter((arg) => arg !== '--use-tsx')
} else {
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
const url = pathToFileURL(dirname).toString() + '/'
const start = async () => {
// Use tsx
let tsImport
try {
tsImport = (await import('tsx/esm/api')).tsImport
} catch (_) {
console.error(
'tsx is not installed. Please install tsx in your project, if you want to use tsx in payload run.',
)
return
if (!useSwc) {
const start = async () => {
// Use tsx
let tsImport = (await import('tsx/esm/api')).tsImport
const { bin } = await tsImport('./dist/bin/index.js', url)
await bin()
}
const { bin } = await tsImport('./dist/bin/index.js', import.meta.url)
await bin()
}
void start()
} else if (useSwc) {
const { register } = await import('node:module')
// Remove --use-swc from arguments
process.argv = process.argv.filter((arg) => arg !== '--use-swc')
void start()
try {
register('@swc-node/register/esm', url)
} catch (_) {
console.error(
'@swc-node/register is not installed. Please install @swc-node/register in your project, if you want to use swc in payload run.',
)
}
const start = async () => {
const { bin } = await import('./dist/bin/index.js')
await bin()
}
void start()
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "payload",
"version": "3.0.0-beta.80",
"version": "3.0.0-beta.84",
"description": "Node, React, Headless CMS and Application Framework built on Next.js",
"keywords": [
"admin panel",
@@ -86,7 +86,7 @@
"dependencies": {
"@next/env": "^15.0.0-canary.104",
"@payloadcms/translations": "workspace:*",
"@swc-node/register": "1.10.9",
"tsx": "4.17.0",
"ajv": "8.14.0",
"bson-objectid": "2.0.4",
"ci-info": "^4.0.0",

View File

@@ -227,8 +227,13 @@ export type MappedClientComponent<TComponentClientProps extends JsonObject = Jso
type: 'client'
}
export type MappedEmptyComponent = {
type: 'empty'
}
export type MappedComponent<TComponentClientProps extends JsonObject = JsonObject> =
| MappedClientComponent<TComponentClientProps>
| MappedEmptyComponent
| MappedServerComponent<TComponentClientProps>
| undefined

View File

@@ -13,7 +13,7 @@ export const apiKeyFields = [
type: 'checkbox',
admin: {
components: {
Field: '@payloadcms/ui/shared#emptyComponent',
Field: false,
},
},
label: ({ t }) => t('authentication:enableAPIKey'),
@@ -23,7 +23,7 @@ export const apiKeyFields = [
type: 'text',
admin: {
components: {
Field: '@payloadcms/ui/shared#emptyComponent',
Field: false,
},
},
hooks: {

View File

@@ -7,7 +7,7 @@ export const emailFieldConfig: EmailField = {
type: 'email',
admin: {
components: {
Field: '@payloadcms/ui/shared#emptyComponent',
Field: false,
},
},
hooks: {

View File

@@ -7,7 +7,7 @@ export const usernameFieldConfig: TextField = {
type: 'text',
admin: {
components: {
Field: '@payloadcms/ui/shared#emptyComponent',
Field: false,
},
},
hooks: {

View File

@@ -26,7 +26,7 @@ export const verificationFields: Field[] = [
},
admin: {
components: {
Field: '@payloadcms/ui/shared#emptyComponent',
Field: false,
},
},
label: ({ t }) => t('authentication:verified'),

View File

@@ -44,11 +44,11 @@ export const ensureUsernameOrEmail = <TSlug extends CollectionSlug>({
missingFields = true
}
// prevent clearing email if no username
if ('email' in data && !data.email && !originalDoc.username) {
if ('email' in data && !data.email && !originalDoc.username && !data?.username) {
missingFields = true
}
// prevent clearing username if no email
if ('username' in data && !data.username && !originalDoc.email) {
if ('username' in data && !data.username && !originalDoc.email && !data?.email) {
missingFields = true
}
}

View File

@@ -67,7 +67,7 @@ export function addPayloadComponentToImportMap({
imports[importIdentifier] = {
path:
componentPath.startsWith('.') || componentPath.startsWith('/')
? path.join(baseDir, componentPath.slice(1))
? path.posix.join(baseDir.replace(/\\/g, '/'), componentPath.slice(1))
: componentPath,
specifier: exportName,
}

View File

@@ -4,6 +4,9 @@ export function parsePayloadComponent(payloadComponent: PayloadComponent): {
exportName: string
path: string
} {
if (!payloadComponent) {
return null
}
const pathAndMaybeExport =
typeof payloadComponent === 'string' ? payloadComponent : payloadComponent.path

View File

@@ -1,4 +1,5 @@
import minimist from 'minimist'
import { pathToFileURL } from 'node:url'
import path from 'path'
import type { BinScript } from '../config/types.js'
@@ -29,7 +30,7 @@ export const bin = async () => {
process.argv = [process.argv[0], process.argv[1], ...args._.slice(2)]
try {
await import(absoluteScriptPath)
await import(pathToFileURL(absoluteScriptPath).toString())
} catch (error) {
console.error(`Error running script: ${absoluteScriptPath}`)
console.error(error)
@@ -42,7 +43,7 @@ export const bin = async () => {
}
const configPath = findConfig()
const configPromise = await import(configPath)
const configPromise = await import(pathToFileURL(configPath).toString())
let config = await configPromise
if (config.default) config = await config.default
@@ -52,7 +53,7 @@ export const bin = async () => {
if (userBinScript) {
try {
const script: BinScript = await import(userBinScript.scriptPath)
const script: BinScript = await import(pathToFileURL(userBinScript.scriptPath).toString())
await script(config)
} catch (err) {
console.log(`Could not find associated bin script for the ${userBinScript.key} command`)

View File

@@ -22,10 +22,7 @@ const getTSConfigPaths = (): {
const rootConfigDir = path.resolve(tsConfigDir, tsConfig.compilerOptions.baseUrl || '')
const srcPath = tsConfig.compilerOptions?.rootDir || path.resolve(process.cwd(), 'src')
const outPath = tsConfig.compilerOptions?.outDir || path.resolve(process.cwd(), 'dist')
let configPath = path.resolve(
rootConfigDir,
tsConfig.compilerOptions?.paths?.['@payload-config']?.[0],
)
let configPath = tsConfig.compilerOptions?.paths?.['@payload-config']?.[0]
if (configPath) {
configPath = path.resolve(rootConfigDir, configPath)

View File

@@ -13,7 +13,7 @@ import type { default as sharp } from 'sharp'
import type { DeepRequired } from 'ts-essentials'
import type { RichTextAdapterProvider } from '../admin/RichText.js'
import type { DocumentTabConfig, MappedComponent, RichTextAdapter } from '../admin/types.js'
import type { DocumentTabConfig, RichTextAdapter } from '../admin/types.js'
import type { AdminViewConfig, ServerSideEditViewProps } from '../admin/views/types.js'
import type { Permissions } from '../auth/index.js'
import type {
@@ -38,12 +38,12 @@ import type { PayloadLogger } from '../utilities/logger.js'
/**
* The string path pointing to the React component. If one of the generics is `never`, you effectively mark it as a server-only or client-only component.
*
* If the path is an empty string, it will be treated as () => null
* If it is `false` an empty component will be rendered.
*/
export type PayloadComponent<
TComponentServerProps extends never | object = Record<string, any>,
TComponentClientProps extends never | object = Record<string, any>,
> = RawPayloadComponent<TComponentServerProps, TComponentClientProps> | string
> = RawPayloadComponent<TComponentServerProps, TComponentClientProps> | false | string
// We need the actual object as its own type, otherwise the infers for the PayloadClientReactComponent / PayloadServerReactComponent will not work due to the string union.
// We also NEED to actually use those generics for this to work, thus they are part of the props.
@@ -912,28 +912,44 @@ export type SanitizedConfig = {
'collections' | 'editor' | 'endpoint' | 'globals' | 'i18n' | 'localization' | 'upload'
>
export type EditConfig = {
[key: string]: Partial<EditViewConfig>
/**
* Replace or modify individual nested routes, or add new ones:
* + `default` - `/admin/collections/:collection/:id`
* + `api` - `/admin/collections/:collection/:id/api`
* + `livePreview` - `/admin/collections/:collection/:id/preview`
* + `references` - `/admin/collections/:collection/:id/references`
* + `relationships` - `/admin/collections/:collection/:id/relationships`
* + `versions` - `/admin/collections/:collection/:id/versions`
* + `version` - `/admin/collections/:collection/:id/versions/:version`
* + `customView` - `/admin/collections/:collection/:id/:path`
*/
api?: Partial<EditViewConfig>
default?: Partial<EditViewConfig>
livePreview?: Partial<EditViewConfig>
version?: Partial<EditViewConfig>
versions?: Partial<EditViewConfig>
// TODO: uncomment these as they are built
// references?: EditView
// relationships?: EditView
}
export type EditConfig =
| {
[key: string]: EditViewConfig
/**
* Replace or modify individual nested routes, or add new ones:
* + `default` - `/admin/collections/:collection/:id`
* + `api` - `/admin/collections/:collection/:id/api`
* + `livePreview` - `/admin/collections/:collection/:id/preview`
* + `references` - `/admin/collections/:collection/:id/references`
* + `relationships` - `/admin/collections/:collection/:id/relationships`
* + `versions` - `/admin/collections/:collection/:id/versions`
* + `version` - `/admin/collections/:collection/:id/versions/:version`
* + `customView` - `/admin/collections/:collection/:id/:path`
*
* To override the entire Edit View including all nested views, use the `root` key.
*/
api?: Partial<EditViewConfig>
default?: Partial<EditViewConfig>
livePreview?: Partial<EditViewConfig>
root?: never
version?: Partial<EditViewConfig>
versions?: Partial<EditViewConfig>
// TODO: uncomment these as they are built
// references?: EditView
// relationships?: EditView
}
| {
api?: never
default?: never
livePreview?: never
/**
* Replace or modify _all_ nested document views and routes, including the document header, controls, and tabs. This cannot be used in conjunction with other nested views.
* + `root` - `/admin/collections/:collection/:id/**\/*`
*/
root: Partial<EditViewConfig>
version?: never
versions?: never
}
export type EntityDescriptionComponent = CustomComponent

View File

@@ -61,6 +61,8 @@ export { setsAreEqual } from '../utilities/setsAreEqual.js'
export { default as toKebabCase } from '../utilities/toKebabCase.js'
export { unflatten } from '../utilities/unflatten.js'
export { wait } from '../utilities/wait.js'
export { default as wordBoundariesRegex } from '../utilities/wordBoundariesRegex.js'

View File

@@ -962,7 +962,9 @@ export type SingleRelationshipFieldClient = {
export type RelationshipField = PolymorphicRelationshipField | SingleRelationshipField
export type RelationshipFieldClient = PolymorphicRelationshipFieldClient
export type RelationshipFieldClient =
| PolymorphicRelationshipFieldClient
| SingleRelationshipFieldClient
export type ValueWithRelation = {
relationTo: CollectionSlug

View File

@@ -17,7 +17,7 @@ const baseVersionFields: Field[] = [
type: 'select',
admin: {
components: {
Field: '@payloadcms/ui/shared#emptyComponent',
Field: false,
},
disableBulkEdit: true,
},

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/plugin-cloud-storage",
"version": "3.0.0-beta.80",
"version": "3.0.0-beta.84",
"description": "The official cloud storage plugin for Payload CMS",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/plugin-cloud",
"version": "3.0.0-beta.80",
"version": "3.0.0-beta.84",
"description": "The official Payload Cloud plugin",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/plugin-form-builder",
"version": "3.0.0-beta.80",
"version": "3.0.0-beta.84",
"description": "Form builder plugin for Payload CMS",
"keywords": [
"payload",

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/plugin-nested-docs",
"version": "3.0.0-beta.80",
"version": "3.0.0-beta.84",
"description": "The official Nested Docs plugin for Payload",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/plugin-redirects",
"version": "3.0.0-beta.80",
"version": "3.0.0-beta.84",
"description": "Redirects plugin for Payload",
"keywords": [
"payload",

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/plugin-relationship-object-ids",
"version": "3.0.0-beta.80",
"version": "3.0.0-beta.84",
"description": "A Payload plugin to store all relationship IDs as ObjectIDs",
"repository": {
"type": "git",

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/plugin-search",
"version": "3.0.0-beta.80",
"version": "3.0.0-beta.84",
"description": "Search plugin for Payload",
"keywords": [
"payload",

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/plugin-seo",
"version": "3.0.0-beta.80",
"version": "3.0.0-beta.84",
"description": "SEO plugin for Payload",
"keywords": [
"payload",

View File

@@ -61,7 +61,7 @@ export const MetaDescriptionComponent: React.FC<MetaDescriptionProps> = (props)
...docInfo,
doc: { ...getData() },
locale: typeof locale === 'object' ? locale?.code : locale,
} satisfies Parameters<GenerateDescription>[0]),
} satisfies Omit<Parameters<GenerateDescription>[0], 'req'>),
credentials: 'include',
headers: {
'Content-Type': 'application/json',

View File

@@ -57,7 +57,7 @@ export const MetaImageComponent: React.FC<MetaImageProps> = (props) => {
...docInfo,
doc: { ...getData() },
locale: typeof locale === 'object' ? locale?.code : locale,
} satisfies Parameters<GenerateImage>[0]),
} satisfies Omit<Parameters<GenerateImage>[0], 'req'>),
credentials: 'include',
headers: {
'Content-Type': 'application/json',

View File

@@ -62,7 +62,7 @@ export const MetaTitleComponent: React.FC<MetaTitleProps> = (props) => {
...docInfo,
doc: { ...getData() },
locale: typeof locale === 'object' ? locale?.code : locale,
} satisfies Parameters<GenerateTitle>[0]),
} satisfies Omit<Parameters<GenerateTitle>[0], 'req'>),
credentials: 'include',
headers: {
'Content-Type': 'application/json',

View File

@@ -49,7 +49,7 @@ export const PreviewComponent: React.FC<PreviewProps> = ({
...docInfo,
doc: { ...getData() },
locale: typeof locale === 'object' ? locale?.code : locale,
} satisfies Parameters<GenerateURL>[0]),
} satisfies Omit<Parameters<GenerateURL>[0], 'req'>),
credentials: 'include',
headers: {
'Content-Type': 'application/json',

View File

@@ -134,11 +134,12 @@ export const seoPlugin =
{
handler: async (req) => {
await addDataAndFileToRequest(req)
req.t
const result = pluginConfig.generateTitle
? await pluginConfig.generateTitle(
req.data as unknown as Parameters<GenerateTitle>[0],
)
? await pluginConfig.generateTitle({
...req.data,
req,
} as unknown as Parameters<GenerateTitle>[0])
: ''
return new Response(JSON.stringify({ result }), { status: 200 })
},
@@ -148,10 +149,12 @@ export const seoPlugin =
{
handler: async (req) => {
await addDataAndFileToRequest(req)
const result = pluginConfig.generateDescription
? await pluginConfig.generateDescription(
req.data as unknown as Parameters<GenerateDescription>[0],
)
? await pluginConfig.generateDescription({
...req.data,
req,
} as unknown as Parameters<GenerateDescription>[0])
: ''
return new Response(JSON.stringify({ result }), { status: 200 })
},
@@ -161,8 +164,12 @@ export const seoPlugin =
{
handler: async (req) => {
await addDataAndFileToRequest(req)
const result = pluginConfig.generateURL
? await pluginConfig.generateURL(req.data as unknown as Parameters<GenerateURL>[0])
? await pluginConfig.generateURL({
...req.data,
req,
} as unknown as Parameters<GenerateURL>[0])
: ''
return new Response(JSON.stringify({ result }), { status: 200 })
},
@@ -172,10 +179,12 @@ export const seoPlugin =
{
handler: async (req) => {
await addDataAndFileToRequest(req)
const result = pluginConfig.generateImage
? await pluginConfig.generateImage(
req.data as unknown as Parameters<GenerateImage>[0],
)
? await pluginConfig.generateImage({
...req.data,
req,
} as unknown as Parameters<GenerateImage>[0])
: ''
return new Response(result, { status: 200 })
},

View File

@@ -1,23 +1,24 @@
import type { DocumentInfoContext } from '@payloadcms/ui'
import type { Field, TextField, TextareaField, UploadField } from 'payload'
import type { Field, PayloadRequest, TextField, TextareaField, UploadField } from 'payload'
export type GenerateTitle<T = any> = (
args: { doc: T; locale?: string } & DocumentInfoContext,
args: { doc: T; locale?: string; req: PayloadRequest } & DocumentInfoContext,
) => Promise<string> | string
export type GenerateDescription<T = any> = (
args: {
doc: T
locale?: string
req: PayloadRequest
} & DocumentInfoContext,
) => Promise<string> | string
export type GenerateImage<T = any> = (
args: { doc: T; locale?: string } & DocumentInfoContext,
args: { doc: T; locale?: string; req: PayloadRequest } & DocumentInfoContext,
) => Promise<string> | string
export type GenerateURL<T = any> = (
args: { doc: T; locale?: string } & DocumentInfoContext,
args: { doc: T; locale?: string; req: PayloadRequest } & DocumentInfoContext,
) => Promise<string> | string
export type SEOPluginConfig = {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/plugin-stripe",
"version": "3.0.0-beta.80",
"version": "3.0.0-beta.84",
"description": "Stripe plugin for Payload",
"keywords": [
"payload",

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/richtext-lexical",
"version": "3.0.0-beta.80",
"version": "3.0.0-beta.84",
"description": "The officially supported Lexical richtext adapter for Payload",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -8,7 +8,7 @@
position: absolute;
order: 3;
left: unset;
inset-inline-end: base(0.5);
inset-inline-end: base(0.8);
top: 50%;
transform: translateY(-50%);
color: var(--theme-elevation-600);
@@ -16,8 +16,8 @@
border: none;
svg {
width: base(0.75);
height: base(0.75);
width: base(0.8);
height: base(0.8);
}
&:hover {
@@ -33,10 +33,11 @@
.toast-title {
line-height: base(1);
margin-right: base(1);
}
.payload-toast-item {
padding: base(0.5);
padding: base(0.8);
color: var(--theme-elevation-800);
font-style: normal;
font-weight: 600;
@@ -69,8 +70,8 @@
}
.toast-icon {
width: base(1);
height: base(1);
width: base(0.8);
height: base(0.8);
margin: 0;
display: flex;
align-items: center;
@@ -84,8 +85,8 @@
&.toast-warning {
color: var(--theme-warning-800);
border-color: var(--theme-warning-150);
background-color: var(--theme-warning-50);
border-color: var(--theme-warning-250);
background-color: var(--theme-warning-100);
.payload-toast-close-button {
color: var(--theme-warning-600);
@@ -98,8 +99,8 @@
&.toast-error {
color: var(--theme-error-800);
border-color: var(--theme-error-150);
background-color: var(--theme-error-50);
border-color: var(--theme-error-250);
background-color: var(--theme-error-100);
.payload-toast-close-button {
color: var(--theme-error-600);
@@ -112,8 +113,8 @@
&.toast-success {
color: var(--theme-success-800);
border-color: var(--theme-success-150);
background-color: var(--theme-success-50);
border-color: var(--theme-success-250);
background-color: var(--theme-success-100);
.payload-toast-close-button {
color: var(--theme-success-600);
@@ -126,8 +127,8 @@
&.toast-info {
color: var(--theme-elevation-800);
border-color: var(--theme-elevation-150);
background-color: var(--theme-elevation-50);
border-color: var(--theme-elevation-250);
background-color: var(--theme-elevation-100);
.payload-toast-close-button {
color: var(--theme-elevation-600);

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/richtext-slate",
"version": "3.0.0-beta.80",
"version": "3.0.0-beta.84",
"description": "The officially supported Slate richtext adapter for Payload",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -3,5 +3,5 @@ import type { RichTextCustomElement } from '../../../types.js'
export const textAlign: RichTextCustomElement = {
name: 'alignment',
Button: '@payloadcms/richtext-slate/client#TextAlignElementButton',
Element: '@payloadcms/ui/shared#emptyComponent',
Element: false,
}

View File

@@ -8,7 +8,7 @@
position: absolute;
order: 3;
left: unset;
inset-inline-end: base(0.5);
inset-inline-end: base(0.8);
top: 50%;
transform: translateY(-50%);
color: var(--theme-elevation-600);
@@ -16,8 +16,8 @@
border: none;
svg {
width: base(0.75);
height: base(0.75);
width: base(0.8);
height: base(0.8);
}
&:hover {
@@ -33,10 +33,11 @@
.toast-title {
line-height: base(1);
margin-right: base(1);
}
.payload-toast-item {
padding: base(0.5);
padding: base(0.8);
color: var(--theme-elevation-800);
font-style: normal;
font-weight: 600;
@@ -69,8 +70,8 @@
}
.toast-icon {
width: base(1);
height: base(1);
width: base(0.8);
height: base(0.8);
margin: 0;
display: flex;
align-items: center;
@@ -84,8 +85,8 @@
&.toast-warning {
color: var(--theme-warning-800);
border-color: var(--theme-warning-150);
background-color: var(--theme-warning-50);
border-color: var(--theme-warning-250);
background-color: var(--theme-warning-100);
.payload-toast-close-button {
color: var(--theme-warning-600);
@@ -98,8 +99,8 @@
&.toast-error {
color: var(--theme-error-800);
border-color: var(--theme-error-150);
background-color: var(--theme-error-50);
border-color: var(--theme-error-250);
background-color: var(--theme-error-100);
.payload-toast-close-button {
color: var(--theme-error-600);
@@ -112,8 +113,8 @@
&.toast-success {
color: var(--theme-success-800);
border-color: var(--theme-success-150);
background-color: var(--theme-success-50);
border-color: var(--theme-success-250);
background-color: var(--theme-success-100);
.payload-toast-close-button {
color: var(--theme-success-600);
@@ -126,8 +127,8 @@
&.toast-info {
color: var(--theme-elevation-800);
border-color: var(--theme-elevation-150);
background-color: var(--theme-elevation-50);
border-color: var(--theme-elevation-250);
background-color: var(--theme-elevation-100);
.payload-toast-close-button {
color: var(--theme-elevation-600);

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/storage-azure",
"version": "3.0.0-beta.80",
"version": "3.0.0-beta.84",
"description": "Payload storage adapter for Azure Blob Storage",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/storage-gcs",
"version": "3.0.0-beta.80",
"version": "3.0.0-beta.84",
"description": "Payload storage adapter for Google Cloud Storage",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/storage-s3",
"version": "3.0.0-beta.80",
"version": "3.0.0-beta.84",
"description": "Payload storage adapter for Amazon S3",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/storage-uploadthing",
"version": "3.0.0-beta.80",
"version": "3.0.0-beta.84",
"description": "Payload storage adapter for uploadthing",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/storage-vercel-blob",
"version": "3.0.0-beta.80",
"version": "3.0.0-beta.84",
"description": "Payload storage adapter for Vercel Blob Storage",
"homepage": "https://payloadcms.com",
"repository": {

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/translations",
"version": "3.0.0-beta.80",
"version": "3.0.0-beta.84",
"homepage": "https://payloadcms.com",
"repository": {
"type": "git",

View File

@@ -1,6 +1,6 @@
{
"name": "@payloadcms/ui",
"version": "3.0.0-beta.80",
"version": "3.0.0-beta.84",
"homepage": "https://payloadcms.com",
"repository": {
"type": "git",

View File

@@ -155,7 +155,7 @@ export const Autosave: React.FC<Props> = ({
}
})
.then((json) => {
if (versionsConfig?.drafts && versionsConfig?.drafts?.validate && json.errors) {
if (versionsConfig?.drafts && versionsConfig?.drafts?.validate && json?.errors) {
if (Array.isArray(json.errors)) {
const [fieldErrors, nonFieldErrors] = json.errors.reduce(
([fieldErrs, nonFieldErrs], err) => {
@@ -224,7 +224,7 @@ export const Autosave: React.FC<Props> = ({
if (autosaveTimeout) clearTimeout(autosaveTimeout)
if (abortController.signal) {
try {
abortController.abort()
abortController.abort('Autosave closed early.')
} catch (error) {
// swallow error
}

View File

@@ -13,13 +13,20 @@ const handleDragOver = (e: DragEvent) => {
const baseClass = 'dropzone'
export type Props = {
className?: string
mimeTypes?: string[]
onChange: (e: FileList) => void
onPasteUrlClick?: () => void
readonly className?: string
readonly mimeTypes?: string[]
readonly multipleFiles?: boolean
readonly onChange: (e: FileList) => void
readonly onPasteUrlClick?: () => void
}
export const Dropzone: React.FC<Props> = ({ className, mimeTypes, onChange, onPasteUrlClick }) => {
export const Dropzone: React.FC<Props> = ({
className,
mimeTypes,
multipleFiles,
onChange,
onPasteUrlClick,
}) => {
const dropRef = React.useRef<HTMLDivElement>(null)
const [dragging, setDragging] = React.useState(false)
const inputRef = React.useRef(null)
@@ -111,17 +118,21 @@ export const Dropzone: React.FC<Props> = ({ className, mimeTypes, onChange, onPa
>
{t('upload:selectFile')}
</Button>
<Button
buttonStyle="secondary"
className={`${baseClass}__file-button`}
onClick={onPasteUrlClick}
size="medium"
>
{t('upload:pasteURL')}
</Button>
{typeof onPasteUrlClick === 'function' && (
<Button
buttonStyle="secondary"
className={`${baseClass}__file-button`}
onClick={onPasteUrlClick}
size="medium"
>
{t('upload:pasteURL')}
</Button>
)}
<input
accept={mimeTypes?.join(',')}
aria-hidden="true"
className={`${baseClass}__hidden-input`}
multiple={multipleFiles}
onChange={handleFileSelection}
ref={inputRef}
type="file"

View File

@@ -1,16 +1,17 @@
'use client'
import { CloseMenuIcon, MenuIcon } from '@payloadcms/ui'
import React from 'react'
import { ChevronIcon } from '../../icons/Chevron/index.js'
import { CloseMenuIcon } from '../../icons/CloseMenu/index.js'
import { MenuIcon } from '../../icons/Menu/index.js'
import { useTranslation } from '../../providers/Translation/index.js'
import './index.scss'
const baseClass = 'hamburger'
export const Hamburger: React.FC<{
closeIcon?: 'collapse' | 'x'
isActive?: boolean
readonly closeIcon?: 'collapse' | 'x'
readonly isActive?: boolean
}> = (props) => {
const { t } = useTranslation()
const { closeIcon = 'x', isActive = false } = props

View File

@@ -1,11 +1,12 @@
@import '../../scss/styles';
.id-label {
font-size: base(0.75);
font-size: base(0.8);
line-height: base(1.2);
font-weight: normal;
color: var(--theme-elevation-600);
background: var(--theme-elevation-100);
padding: 0 base(0.6);
padding: base(0.2) base(0.4);
border-radius: $style-radius-m;
display: inline-flex;
}

View File

@@ -9,11 +9,10 @@ import AnimateHeightImport from 'react-animate-height'
const AnimateHeight = (AnimateHeightImport.default ||
AnimateHeightImport) as typeof AnimateHeightImport.default
import { useListInfo } from '@payloadcms/ui'
import { useUseTitleField } from '../../hooks/useUseAsTitle.js'
import { ChevronIcon } from '../../icons/Chevron/index.js'
import { SearchIcon } from '../../icons/Search/index.js'
import { useListInfo } from '../../providers/ListInfo/index.js'
import { useListQuery } from '../../providers/ListQuery/index.js'
import { useSearchParams } from '../../providers/SearchParams/index.js'
import { useTranslation } from '../../providers/Translation/index.js'
@@ -32,13 +31,13 @@ import './index.scss'
const baseClass = 'list-controls'
export type ListControlsProps = {
collectionConfig: ClientCollectionConfig
enableColumns?: boolean
enableSort?: boolean
fields: ClientField[]
handleSearchChange?: (search: string) => void
handleSortChange?: (sort: string) => void
handleWhereChange?: (where: Where) => void
readonly collectionConfig: ClientCollectionConfig
readonly enableColumns?: boolean
readonly enableSort?: boolean
readonly fields: ClientField[]
readonly handleSearchChange?: (search: string) => void
readonly handleSortChange?: (sort: string) => void
readonly handleWhereChange?: (where: Where) => void
}
/**

View File

@@ -1,11 +1,11 @@
'use client'
import type { DropdownIndicatorProps } from 'react-select'
import { ChevronIcon } from '@payloadcms/ui'
import React, { type JSX } from 'react'
import type { Option as OptionType } from '../types.js'
import { ChevronIcon } from '../../../icons/Chevron/index.js'
import './index.scss'
const baseClass = 'dropdown-indicator'

View File

@@ -1,7 +1,7 @@
@import '../../scss/styles.scss';
.render-title {
display: inline-flex;
display: inline-block;
&__id {
vertical-align: middle;
position: relative;

View File

@@ -8,10 +8,9 @@ import { useTranslation } from '../../providers/Translation/index.js'
import { StepNavProvider, useStepNav } from './context.js'
import './index.scss'
export { SetStepNav } from './SetStepNav.js'
import { PayloadIcon } from '@payloadcms/ui'
import type { StepNavItem } from './types.js'
import { PayloadIcon } from '../../graphics/Icon/index.js'
import { RenderComponent } from '../../providers/Config/RenderComponent.js'
const baseClass = 'step-nav'

View File

@@ -1,9 +1,9 @@
'use client'
import type { DateFieldClient, DefaultCellComponentProps } from 'payload'
import { useConfig } from '@payloadcms/ui'
import React from 'react'
import { useConfig } from '../../../../../providers/Config/index.js'
import { useTranslation } from '../../../../../providers/Translation/index.js'
import { formatDate } from '../../../../../utilities/formatDate.js'

View File

@@ -6,12 +6,12 @@ import type {
StaticLabel,
} from 'payload'
import { DefaultCell } from '@payloadcms/ui'
import React from 'react'
import type { ColumnPreferences } from '../../providers/ListInfo/index.js'
import type { Column } from '../Table/index.js'
import { DefaultCell } from '../../elements/Table/DefaultCell/index.js'
import { FieldLabel } from '../../fields/FieldLabel/index.js'
import { flattenFieldMap } from '../../utilities/flattenFieldMap.js'
import { SelectAll } from '../SelectAll/index.js'

View File

@@ -1,16 +1,17 @@
'use client'
import type { FormState, SanitizedCollectionConfig, UploadEdits } from 'payload'
import { useForm, useUploadEdits } from '@payloadcms/ui'
import { isImage, reduceFieldsToValues } from 'payload/shared'
import React, { useCallback, useEffect, useRef, useState } from 'react'
import { toast } from 'sonner'
import { FieldError } from '../../fields/FieldError/index.js'
import { fieldBaseClass } from '../../fields/shared/index.js'
import { useForm } from '../../forms/Form/index.js'
import { useField } from '../../forms/useField/index.js'
import { useDocumentInfo } from '../../providers/DocumentInfo/index.js'
import { useTranslation } from '../../providers/Translation/index.js'
import { useUploadEdits } from '../../providers/UploadEdits/index.js'
import { Button } from '../Button/index.js'
import { Drawer, DrawerToggler } from '../Drawer/index.js'
import { Dropzone } from '../Dropzone/index.js'
@@ -33,10 +34,10 @@ const validate = (value) => {
}
type UploadActionsArgs = {
customActions?: React.ReactNode[]
enableAdjustments: boolean
enablePreviewSizes: boolean
mimeType: string
readonly customActions?: React.ReactNode[]
readonly enableAdjustments: boolean
readonly enablePreviewSizes: boolean
readonly mimeType: string
}
export const UploadActions = ({
@@ -77,11 +78,11 @@ export const UploadActions = ({
}
export type UploadProps = {
collectionSlug: string
customActions?: React.ReactNode[]
initialState?: FormState
onChange?: (file?: File) => void
uploadConfig: SanitizedCollectionConfig['upload']
readonly collectionSlug: string
readonly customActions?: React.ReactNode[]
readonly initialState?: FormState
readonly onChange?: (file?: File) => void
readonly uploadConfig: SanitizedCollectionConfig['upload']
}
export const Upload: React.FC<UploadProps> = (props) => {
@@ -108,15 +109,7 @@ export const Upload: React.FC<UploadProps> = (props) => {
const handleFileChange = useCallback(
(newFile: File) => {
if (newFile instanceof File) {
const fileReader = new FileReader()
fileReader.onload = (e) => {
const imgSrc = e.target?.result
if (typeof imgSrc === 'string') {
setFileSrc(imgSrc)
}
}
fileReader.readAsDataURL(newFile)
setFileSrc(URL.createObjectURL(newFile))
}
setValue(newFile)
@@ -201,6 +194,9 @@ export const Upload: React.FC<UploadProps> = (props) => {
useEffect(() => {
setDoc(reduceFieldsToValues(initialState || {}, true))
if (initialState?.file?.value instanceof File) {
setFileSrc(URL.createObjectURL(initialState.file.value))
}
setReplacingFile(false)
}, [initialState])
@@ -265,7 +261,9 @@ export const Upload: React.FC<UploadProps> = (props) => {
<div className={`${baseClass}__add-file-wrap`}>
<button
className={`${baseClass}__add-file`}
onClick={handleUrlSubmit}
onClick={() => {
void handleUrlSubmit()
}}
type="button"
>
{t('upload:addFile')}

View File

@@ -32,6 +32,7 @@ export { Collapsible } from '../../elements/Collapsible/index.js'
export { CopyToClipboard } from '../../elements/CopyToClipboard/index.js'
export { DeleteMany } from '../../elements/DeleteMany/index.js'
export { DocumentControls } from '../../elements/DocumentControls/index.js'
export { Dropzone } from '../../elements/Dropzone/index.js'
export { useDocumentDrawer } from '../../elements/DocumentDrawer/index.js'
export { DocumentFields } from '../../elements/DocumentFields/index.js'
export { Drawer, DrawerToggler, formatDrawerSlug } from '../../elements/Drawer/index.js'

View File

@@ -21,5 +21,3 @@ export {
} from '../../utilities/groupNavItems.js'
export { hasSavePermission } from '../../utilities/hasSavePermission.js'
export { isEditing } from '../../utilities/isEditing.js'
export const emptyComponent = () => null

View File

@@ -1,7 +1,6 @@
'use client'
import type { PasswordFieldValidation, PayloadRequest } from 'payload'
import { useConfig, useLocale, useTranslation } from '@payloadcms/ui'
import { password } from 'payload/shared'
import React, { useCallback } from 'react'
@@ -9,6 +8,9 @@ import type { PasswordFieldProps } from './types.js'
import { useField } from '../../forms/useField/index.js'
import { withCondition } from '../../forms/withCondition/index.js'
import { useConfig } from '../../providers/Config/index.js'
import { useLocale } from '../../providers/Locale/index.js'
import { useTranslation } from '../../providers/Translation/index.js'
import { isFieldRTL } from '../shared/index.js'
import './index.scss'
import { PasswordInput } from './input.js'

View File

@@ -2,10 +2,11 @@
import type { ClientField, FieldPermissions } from 'payload'
import { HiddenField, useFieldComponents } from '@payloadcms/ui'
import React from 'react'
import { HiddenField } from '../../fields/Hidden/index.js'
import { RenderComponent } from '../../providers/Config/RenderComponent.js'
import { useFieldComponents } from '../../providers/FieldComponents/index.js'
import { useOperation } from '../../providers/Operation/index.js'
import { FieldPropsProvider, useFieldProps } from '../FieldPropsProvider/index.js'

View File

@@ -2,20 +2,20 @@
import { usePathname } from 'next/navigation.js'
import React from 'react'
import { useAuth } from '../../providers/Auth/index.js'
import { RenderComponent } from '../../providers/Config/RenderComponent.js'
import { useConfig } from '../../providers/Config/index.js'
import { formatAdminURL } from '../../utilities/formatAdminURL.js'
import { GravatarAccountIcon } from './Gravatar/index.js'
import { useAuth } from '@payloadcms/ui'
import { DefaultAccountIcon } from './Default/index.js'
import { GravatarAccountIcon } from './Gravatar/index.js'
export const Account = () => {
const {
config: {
admin: {
avatar,
routes: { account: accountRoute },
components: { Avatar: CustomAvatar },
routes: { account: accountRoute },
},
routes: { admin: adminRoute },
},

View File

@@ -26,6 +26,6 @@ export const ChevronIcon: React.FC<{
width={20}
xmlns="http://www.w3.org/2000/svg"
>
<path className="stroke" d="M6 9L10 13L14 9" strokeLinecap="square" />
<path className="stroke" d="M6 8L10 12.5L14 8" strokeLinecap="square" />
</svg>
)

View File

@@ -13,7 +13,8 @@ export const LogOutIcon: React.FC = () => (
>
<path
className="stroke"
d="M8 16H5.33333C4.97971 16 4.64057 15.8595 4.39052 15.6095C4.14048 15.3594 4 15.0203 4 14.6667V5.33333C4 4.97971 4.14048 4.64057 4.39052 4.39052C4.64057 4.14048 4.97971 4 5.33333 4H8M12.6667 13.3333L16 10M16 10L12.6667 6.66667M16 10H8"
d="M12 16H14.6667C15.0203 16 15.3594 15.8595 15.6095 15.6095C15.8595 15.3594 16 15.0203 16 14.6667V5.33333C16 4.97971 15.8595 4.64057 15.6095 4.39052C15.3594 4.14048 15.0203 4 14.6667 4H12M7.33333 13.3333L4 10M4 10L7.33333 6.66667M4 10H12"
strokeLinecap="square"
/>
</svg>
)

View File

@@ -1,9 +1,10 @@
'use client'
import type { MappedComponent } from 'payload'
import { useConfig } from '@payloadcms/ui'
import React, { createContext, useContext, useEffect, useState } from 'react'
import { useConfig } from '../../providers/Config/index.js'
export { SetViewActions } from './SetViewActions/index.js'
type ActionsContextType = {
@@ -18,7 +19,9 @@ const ActionsContext = createContext<ActionsContextType>({
export const useActions = () => useContext(ActionsContext)
export const ActionsProvider = ({ children }) => {
export const ActionsProvider: React.FC<{
readonly children: React.ReactNode
}> = ({ children }) => {
const [viewActions, setViewActions] = useState([])
const [adminActions, setAdminActions] = useState([])

View File

@@ -263,6 +263,11 @@ export function AuthProvider({
}
}, [debouncedLocationChange, refreshCookie, id])
// When initialUser changes, reset in state
useEffect(() => {
setUser(initialUser)
}, [initialUser])
useEffect(() => {
setLastLocationChange(Date.now())
}, [pathname])

View File

@@ -58,6 +58,10 @@ export const RenderComponent: React.FC<{
))
}
if (mappedComponent.type === 'empty') {
return null
}
if (mappedComponent.RenderedComponent) {
return mappedComponent.RenderedComponent
}

View File

@@ -257,7 +257,9 @@ export const createClientCollectionConfig = ({
Component: createMappedComponent(
hasEditView &&
'Component' in collection.admin.components.views.edit.default &&
collection.admin.components.views.edit.default.Component,
collection.admin.components.views.edit.default.Component
? collection.admin.components.views.edit.default.Component
: null,
{
collectionSlug: collection.slug,
},
@@ -312,7 +314,9 @@ export const createClientCollectionConfig = ({
clientCollection.admin.components.views.list.Component = createMappedComponent(
hasListView &&
'Component' in collection.admin.components.views.list &&
collection.admin.components.views.list.Component,
collection.admin.components.views.list.Component
? collection.admin.components.views.list.Component
: null,
{
collectionSlug: collection.slug,
},

View File

@@ -3,7 +3,7 @@ import type { ImportMap, PayloadComponent, ResolvedComponent } from 'payload'
import { parsePayloadComponent } from 'payload/shared'
/**
* Gets th resolved React component from `PayloadComponent` from the importMap
* Gets the resolved React component from `PayloadComponent` from the importMap
*/
export const getComponent = <
TComponentServerProps extends object,
@@ -23,6 +23,7 @@ export const getComponent = <
silent?: boolean
}): ResolvedComponent<TComponentServerProps, TComponentClientProps> => {
if (!payloadComponent) {
// undefined, null or false
return {
Component: undefined,
clientProps: undefined,
@@ -37,11 +38,15 @@ export const getComponent = <
const Component = importMap[key]
if (!Component && !silent) {
console.error(`getComponent: Component not found in importMap`, {
identifier,
key,
payloadComponent,
})
console.error(
`getComponent: Component not found in importMap`,
{
identifier,
key,
payloadComponent,
},
'You may need to run the `payload generate:importmap` command to generate the importMap ahead of runtime.',
)
}
return {

View File

@@ -31,7 +31,7 @@ export function getCreateMappedComponent({
Fallback: React.FC<any>,
identifier: string,
): MappedComponent => {
if (!payloadComponent) {
if (payloadComponent === undefined || payloadComponent === null) {
if (!Fallback) {
return undefined
}
@@ -55,6 +55,12 @@ export function getCreateMappedComponent({
}
}
if (payloadComponent === false) {
return {
type: 'empty',
}
}
const resolvedComponent =
payloadComponent &&
typeof payloadComponent === 'object' &&
@@ -107,7 +113,7 @@ export function getCreateMappedComponent({
fallback,
identifier,
) => {
if (!payloadComponent && !fallback) {
if ((payloadComponent === undefined || payloadComponent === null) && !fallback) {
return undefined as any
}

View File

@@ -138,7 +138,9 @@ export const createClientGlobalConfig = ({
Component: createMappedComponent(
hasEditView &&
'Component' in global.admin.components.views.edit.default &&
global.admin.components.views.edit.default.Component,
global.admin.components.views.edit.default.Component
? global.admin.components.views.edit.default.Component
: null,
{
globalSlug: global.slug,
},

View File

@@ -8,7 +8,7 @@
position: absolute;
order: 3;
left: unset;
inset-inline-end: base(0.5);
inset-inline-end: base(0.8);
top: 50%;
transform: translateY(-50%);
color: var(--theme-elevation-600);
@@ -16,8 +16,8 @@
border: none;
svg {
width: base(0.75);
height: base(0.75);
width: base(0.8);
height: base(0.8);
}
&:hover {
@@ -33,10 +33,11 @@
.toast-title {
line-height: base(1);
margin-right: base(1);
}
.payload-toast-item {
padding: base(0.5);
padding: base(0.8);
color: var(--theme-elevation-800);
font-style: normal;
font-weight: 600;
@@ -69,8 +70,8 @@
}
.toast-icon {
width: base(1);
height: base(1);
width: base(0.8);
height: base(0.8);
margin: 0;
display: flex;
align-items: center;
@@ -84,8 +85,8 @@
&.toast-warning {
color: var(--theme-warning-800);
border-color: var(--theme-warning-150);
background-color: var(--theme-warning-50);
border-color: var(--theme-warning-250);
background-color: var(--theme-warning-100);
.payload-toast-close-button {
color: var(--theme-warning-600);
@@ -98,8 +99,8 @@
&.toast-error {
color: var(--theme-error-800);
border-color: var(--theme-error-150);
background-color: var(--theme-error-50);
border-color: var(--theme-error-250);
background-color: var(--theme-error-100);
.payload-toast-close-button {
color: var(--theme-error-600);
@@ -112,8 +113,8 @@
&.toast-success {
color: var(--theme-success-800);
border-color: var(--theme-success-150);
background-color: var(--theme-success-50);
border-color: var(--theme-success-250);
background-color: var(--theme-success-100);
.payload-toast-close-button {
color: var(--theme-success-600);
@@ -126,8 +127,8 @@
&.toast-info {
color: var(--theme-elevation-800);
border-color: var(--theme-elevation-150);
background-color: var(--theme-elevation-50);
border-color: var(--theme-elevation-250);
background-color: var(--theme-elevation-100);
.payload-toast-close-button {
color: var(--theme-elevation-600);

Some files were not shown because too many files have changed in this diff Show More