Compare commits
71 Commits
db-mongodb
...
db-postgre
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
82bd5c656f | ||
|
|
afe8992ca6 | ||
|
|
48a410e294 | ||
|
|
82b88a315f | ||
|
|
cc94078607 | ||
|
|
1c30ad73b6 | ||
|
|
d9442dcce3 | ||
|
|
de92c50847 | ||
|
|
b7f5f932f6 | ||
|
|
bc9e591e37 | ||
|
|
ad38011348 | ||
|
|
d02b1fb084 | ||
|
|
51efe4f39b | ||
|
|
30e535b5b9 | ||
|
|
4bd3bb9400 | ||
|
|
69c93d3c62 | ||
|
|
78aa957043 | ||
|
|
cd06c022c0 | ||
|
|
8299e9fc33 | ||
|
|
9df5ab8a10 | ||
|
|
6979f5a1b1 | ||
|
|
723791b94f | ||
|
|
aa3833ec83 | ||
|
|
2972af2af1 | ||
|
|
857b9a4ac3 | ||
|
|
f829b084ba | ||
|
|
6ae682698d | ||
|
|
622cdb0440 | ||
|
|
cac52da638 | ||
|
|
3cb3c1aceb | ||
|
|
db4aacebb8 | ||
|
|
b735d6aa16 | ||
|
|
fbec3a33e0 | ||
|
|
034be89bdb | ||
|
|
1f2af0963b | ||
|
|
20f1ece2d7 | ||
|
|
2be5ad0eba | ||
|
|
5c58bd322d | ||
|
|
23f3eb1cf0 | ||
|
|
af67749e49 | ||
|
|
43dab5c705 | ||
|
|
9b7e62dc20 | ||
|
|
6e38cc2bcf | ||
|
|
83551bfcaa | ||
|
|
7b44d9d28a | ||
|
|
182d5db6de | ||
|
|
93109ec84a | ||
|
|
4d9e0f35f0 | ||
|
|
19327c8d6d | ||
|
|
831f1ff5be | ||
|
|
a8ac8b4633 | ||
|
|
36b1f5a763 | ||
|
|
24f697219b | ||
|
|
3fccd34abe | ||
|
|
a38f8e93a6 | ||
|
|
84570e6e3b | ||
|
|
5ad8e0edcb | ||
|
|
91bac9c0aa | ||
|
|
33f6edc9d5 | ||
|
|
e1f91f5170 | ||
|
|
0e75dfb5c1 | ||
|
|
1300e264be | ||
|
|
5600125de7 | ||
|
|
22e270f89c | ||
|
|
593f82bcba | ||
|
|
cbf3da1144 | ||
|
|
1a2aab4126 | ||
|
|
d0ba694c80 | ||
|
|
d78df36d9b | ||
|
|
02572d945a | ||
|
|
f05a433320 |
@@ -1,4 +1,4 @@
|
||||
name: pr-title
|
||||
name: release-canary
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -8,4 +8,4 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Echo
|
||||
run: echo "Register pr-title workflow"
|
||||
run: echo "Register release-canary workflow"
|
||||
7
.vscode/launch.json
vendored
7
.vscode/launch.json
vendored
@@ -19,6 +19,13 @@
|
||||
"PAYLOAD_PUBLIC_CLOUD_STORAGE_ADAPTER": "s3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "pnpm run dev collections-graphql",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"name": "Run Dev GraphQL",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
},
|
||||
{
|
||||
"command": "pnpm run dev fields",
|
||||
"cwd": "${workspaceFolder}",
|
||||
|
||||
90
CHANGELOG.md
90
CHANGELOG.md
@@ -1,3 +1,93 @@
|
||||
## [2.18.0](https://github.com/payloadcms/payload/compare/v2.17.0...v2.18.0) (2024-05-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* store focal point on uploads ([#6364](https://github.com/payloadcms/payload/issues/6364)) ([82b88a3](https://github.com/payloadcms/payload/commit/82b88a315ff1d52f0b19a70224d5c600a3a97eb5))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **db-postgres:** filter with ID not_in AND queries - postgres ([#6358](https://github.com/payloadcms/payload/issues/6358)) ([cc94078](https://github.com/payloadcms/payload/commit/cc940786072c0065f10fdd2893050bddc4595a21)), closes [#5151](https://github.com/payloadcms/payload/issues/5151)
|
||||
* **richtext-lexical:** upload, relationship and block node insertion fails sometimes ([#6390](https://github.com/payloadcms/payload/issues/6390)) ([48a410e](https://github.com/payloadcms/payload/commit/48a410e294598af9c73577a04f86466248f93da0))
|
||||
|
||||
## [2.17.0](https://github.com/payloadcms/payload/compare/v2.16.1...v2.17.0) (2024-05-15)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* adds misc translations to API view and react select ([#6138](https://github.com/payloadcms/payload/issues/6138)) ([30e535b](https://github.com/payloadcms/payload/commit/30e535b5b929dddead007d8a9adca62808595e2c))
|
||||
|
||||
* **richtext-lexical:** remove LexicalBlock, RichTextFieldRequiredEditor and FieldWithRichTextRequiredEditor types ([#6279](https://github.com/payloadcms/payload/issues/6279)) ([9df5ab8](https://github.com/payloadcms/payload/commit/9df5ab8a10a35ad34615d7e4da024f59ff037e0e))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* appends `editDepth` value to `radio` & `checkbox` IDs when inside drawer ([#6181](https://github.com/payloadcms/payload/issues/6181)) ([69c93d3](https://github.com/payloadcms/payload/commit/69c93d3c62394a5cf995a2eaec9a3ab30e0f77af))
|
||||
* collection labels with locales not working when creating new doc ([#5995](https://github.com/payloadcms/payload/issues/5995)) ([51efe4f](https://github.com/payloadcms/payload/commit/51efe4f39bcaadccb109a2a02a690ca65041ee57))
|
||||
* safely access cookie header for uploads ([#6367](https://github.com/payloadcms/payload/issues/6367)) ([de92c50](https://github.com/payloadcms/payload/commit/de92c50847640661f915455f8db0029873ddc7ab))
|
||||
* step-nav breadcrumbs ellipsis ([#6345](https://github.com/payloadcms/payload/issues/6345)) ([d02b1fb](https://github.com/payloadcms/payload/commit/d02b1fb084e636e49122ad55b25b9c49eb761f1c))
|
||||
*
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* **richtext-lexical:** remove LexicalBlock, RichTextFieldRequiredEditor and FieldWithRichTextRequiredEditor types (#6279)
|
||||
|
||||
## [2.16.1](https://github.com/payloadcms/payload/compare/v2.16.0...v2.16.1) (2024-05-07)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **richtext-lexical:** add maxDepth property to various lexical features ([#6250](https://github.com/payloadcms/payload/issues/6250)) ([857b9a4](https://github.com/payloadcms/payload/commit/857b9a4ac3236c740458750f156a3a4274eda210)), closes [#6242](https://github.com/payloadcms/payload/issues/6242)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **richtext-lexical:** export missing HorizontalRuleFeature ([#6236](https://github.com/payloadcms/payload/issues/6236)) ([f829b08](https://github.com/payloadcms/payload/commit/f829b084ba9649ef596cce4a7bf6ae8c7ccf57e3))
|
||||
|
||||
## [2.16.0](https://github.com/payloadcms/payload/compare/v2.15.0...v2.16.0) (2024-05-06)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* adds disableListColumn, disableListFilter to fields admin props ([#6188](https://github.com/payloadcms/payload/issues/6188)) ([db4aace](https://github.com/payloadcms/payload/commit/db4aacebb801f1cc11ef8732f9f3b78475256641))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* graphql upload relations returning null ([#6233](https://github.com/payloadcms/payload/issues/6233)) ([cac52da](https://github.com/payloadcms/payload/commit/cac52da638a0df4356120a2f61c6aaf25641a5ad))
|
||||
* hide drag handles when `admin.isSortable: false` ([#6225](https://github.com/payloadcms/payload/issues/6225)) ([622cdb0](https://github.com/payloadcms/payload/commit/622cdb044002b2c3182c3b0432b51befbfb9b979))
|
||||
* **plugin-form-builder:** hook overrides not working as intended ([#6203](https://github.com/payloadcms/payload/issues/6203)) ([b735d6a](https://github.com/payloadcms/payload/commit/b735d6aa169acca8cb638859d2c8ba43e315f02c))
|
||||
|
||||
## [2.15.0](https://github.com/payloadcms/payload/compare/v2.14.2...v2.15.0) (2024-05-03)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add isSortable to arrays and blocks ([#5962](https://github.com/payloadcms/payload/issues/5962)) ([5c58bd3](https://github.com/payloadcms/payload/commit/5c58bd322da966fe610959df13dfd49add35a2ef))
|
||||
* use filterOptions in list relationship filter ([#6156](https://github.com/payloadcms/payload/issues/6156)) ([23f3eb1](https://github.com/payloadcms/payload/commit/23f3eb1cf0b75a4044319d7cd3e5000d5b4e42c4))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* bulk publish from collection list ([#6063](https://github.com/payloadcms/payload/issues/6063)) ([84570e6](https://github.com/payloadcms/payload/commit/84570e6e3bbb81fcae80da92b01bc56d09906072))
|
||||
* cascade draft arg in nested GraphQL relationship queries ([#6141](https://github.com/payloadcms/payload/issues/6141)) ([a8ac8b4](https://github.com/payloadcms/payload/commit/a8ac8b463349664f3188ae77217f037da72f796b))
|
||||
* GraphQL nested relationships not respecting req locale ([#6117](https://github.com/payloadcms/payload/issues/6117)) ([3fccd34](https://github.com/payloadcms/payload/commit/3fccd34abe5a332f88f5e950b755cd1d21441fb6))
|
||||
* hide unusable fields from collection filter select ([#6135](https://github.com/payloadcms/payload/issues/6135)) ([2be5ad0](https://github.com/payloadcms/payload/commit/2be5ad0ebafd1d3c1c0567e2085ccfd593f18271))
|
||||
* incorrect `localesNotSaved` translation ([#5996](https://github.com/payloadcms/payload/issues/5996)) ([af67749](https://github.com/payloadcms/payload/commit/af67749e49db92e675b63b52190e562468894706))
|
||||
* **plugin-cloud:** purge cache for all sizes ([#5301](https://github.com/payloadcms/payload/issues/5301)) ([831f1ff](https://github.com/payloadcms/payload/commit/831f1ff5bed7e083cc076e9eb5ff9a2b2f1ed710))
|
||||
* properly adds `readonly` styles to disabled `radio` fields ([#6176](https://github.com/payloadcms/payload/issues/6176)) ([9b7e62d](https://github.com/payloadcms/payload/commit/9b7e62dc20dca7402c6c68dfb8a5995c211993af))
|
||||
* resets filter state when param state change within route ([#6169](https://github.com/payloadcms/payload/issues/6169)) ([6e38cc2](https://github.com/payloadcms/payload/commit/6e38cc2bcfb08b608abcb6aac4b4c1f6eea63428))
|
||||
* **richtext-lexical:** drag and add block handles disappear too quickly for smaller screen sizes. ([#6145](https://github.com/payloadcms/payload/issues/6145)) ([24f6972](https://github.com/payloadcms/payload/commit/24f697219b5071d91a5c37aafb50e2d823b68d4c))
|
||||
* **richtext-lexical:** floating toolbar caret positioned incorrectly for some line heights ([#6151](https://github.com/payloadcms/payload/issues/6151)) ([36b1f5a](https://github.com/payloadcms/payload/commit/36b1f5a763f782c140e62aa062b4077d6efd0738))
|
||||
* sanitizes fields in default edit view for drawer content ([#6175](https://github.com/payloadcms/payload/issues/6175)) ([43dab5c](https://github.com/payloadcms/payload/commit/43dab5c7053831a0c71f3a6860113f653cab674f))
|
||||
* version restoration ([#6039](https://github.com/payloadcms/payload/issues/6039)) ([91bac9c](https://github.com/payloadcms/payload/commit/91bac9c0aa1ff3da052b9c2ad83fa5ac23a16d1d))
|
||||
|
||||
## [2.14.2](https://github.com/payloadcms/payload/compare/v2.14.1...v2.14.2) (2024-04-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **deps:** dedupes react ([#6058](https://github.com/payloadcms/payload/issues/6058)) ([d0ba694](https://github.com/payloadcms/payload/commit/d0ba694c80a1b699c4f2cad98b1f0bde1f0d43ca))
|
||||
|
||||
## [2.14.1](https://github.com/payloadcms/payload/compare/v2.14.0...v2.14.1) (2024-04-25)
|
||||
|
||||
|
||||
|
||||
@@ -216,7 +216,7 @@ Example:
|
||||
{
|
||||
slug: 'customers',
|
||||
auth: {
|
||||
forgotPassword: {
|
||||
verify: {
|
||||
// highlight-start
|
||||
generateEmailSubject: ({ req, user }) => {
|
||||
return `Hey ${user.email}, reset your password!`;
|
||||
|
||||
@@ -49,7 +49,8 @@ export default buildConfig({
|
||||
{
|
||||
label: 'Arabic',
|
||||
code: 'ar',
|
||||
// opt-in to setting default text-alignment on Input fields to rtl (right-to-left) when current locale is rtl
|
||||
// opt-in to setting default text-alignment on Input fields to rtl (right-to-left)
|
||||
// when current locale is rtl
|
||||
rtl: true,
|
||||
},
|
||||
],
|
||||
@@ -134,13 +135,9 @@ to support localization, you need to specify each field that you would like to l
|
||||
```js
|
||||
{
|
||||
name: 'title',
|
||||
type
|
||||
:
|
||||
'text',
|
||||
// highlight-start
|
||||
localized
|
||||
:
|
||||
true,
|
||||
type: 'text',
|
||||
// highlight-start
|
||||
localized: true,
|
||||
// highlight-end
|
||||
}
|
||||
```
|
||||
|
||||
@@ -20,7 +20,8 @@ The initial request made to Payload will begin a new transaction and attach it t
|
||||
|
||||
```ts
|
||||
const afterChange: CollectionAfterChangeHook = async ({ req }) => {
|
||||
// because req.transactionID is assigned from Payload and passed through, my-slug will only persist if the entire request is successful
|
||||
// because req.transactionID is assigned from Payload and passed through,
|
||||
// my-slug will only persist if the entire request is successful
|
||||
await req.payload.create({
|
||||
req,
|
||||
collection: 'my-slug',
|
||||
@@ -60,10 +61,44 @@ const afterChange: CollectionAfterChangeHook = async ({ req }) => {
|
||||
|
||||
### Direct Transaction Access
|
||||
|
||||
When writing your own scripts or custom endpoints, you may wish to have direct control over transactions. This is useful for interacting with your database outside of Payload's local API.
|
||||
When writing your own scripts or custom endpoints, you may wish to have direct control over transactions. This is useful for interacting with your database in something like a background job, outside the normal request-response flow.
|
||||
|
||||
The following functions can be used for managing transactions:
|
||||
|
||||
`payload.db.beginTransaction` - Starts a new session and returns a transaction ID for use in other Payload Local API calls.
|
||||
`payload.db.commitTransaction` - Takes the identifier for the transaction, finalizes any changes.
|
||||
`payload.db.rollbackTransaction` - Takes the identifier for the transaction, discards any changes.
|
||||
`payload.db.beginTransaction` - Starts a new session and returns a transaction ID for use in other Payload Local API calls. Note that if your database does not support transactions, this will return `null`.\
|
||||
`payload.db.commitTransaction` - Takes the identifier for the transaction, finalizes any changes.\
|
||||
`payload.db.rollbackTransaction` - Takes the identifier for the transaction, discards any changes.
|
||||
|
||||
You can then use the transaction ID with Payload's local API by passing it inside the `PayloadRequest` object.
|
||||
|
||||
Here is an example for a "background job" function, which utilizes the direct transaction API to make sure it either succeeds completely or gets rolled back in case of an error.
|
||||
|
||||
```ts
|
||||
async function allOrNothingJob() {
|
||||
const req = {} as PayloadRequest;
|
||||
req.transactionID = await payload.db.beginTransaction();
|
||||
try {
|
||||
await payload.create({
|
||||
req, // use our manual transaction
|
||||
collection: 'my-slug',
|
||||
data: {
|
||||
some: 'data'
|
||||
}
|
||||
});
|
||||
|
||||
await payload.create({
|
||||
req, // use our manual transaction
|
||||
collection: 'something-else',
|
||||
data: {
|
||||
some: 'data'
|
||||
}
|
||||
});
|
||||
console.log('Everything done.');
|
||||
if (req.transactionID) await payload.db.commitTransaction(req.transactionID);
|
||||
} catch (e) {
|
||||
console.error('Oh no, something went wrong!');
|
||||
if (req.transactionID) await payload.db.rollbackTransaction(req.transactionID);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
39
docs/examples/overview.mdx
Normal file
39
docs/examples/overview.mdx
Normal file
@@ -0,0 +1,39 @@
|
||||
---
|
||||
title: Examples
|
||||
label: Overview
|
||||
order: 10
|
||||
desc:
|
||||
keywords: example, examples, starter, boilerplate, template, templates
|
||||
---
|
||||
|
||||
Payload provides a vast array of examples to help you get started with your project no matter what you are working on. These examples are designed to be easy to get up and running, and to be easy to understand. They showcase nothing more than the specific features being demonstrated, so you can easily decipher what is going on.
|
||||
|
||||
Examples are changing every day, so be sure to check back often to see what new examples have been added. If you have a specific example you would like to see, please feel free to start a new [Discussion](https://github.com/payloadcms/payload/discussions) or open a new [PR](https://github.com/payloadcms/payload/pulls) to add it yourself.
|
||||
|
||||
- [Auth](https://github.com/payloadcms/payload/tree/main/examples/auth)
|
||||
- [Custom Server](https://github.com/payloadcms/payload/tree/main/examples/custom-server)
|
||||
- [Draft Preview](https://github.com/payloadcms/payload/tree/main/examples/draft-preview)
|
||||
- [Email](https://github.com/payloadcms/payload/tree/main/examples/email)
|
||||
- [Form Builder](https://github.com/payloadcms/payload/tree/main/examples/form-builder)
|
||||
- [Hierarchy](https://github.com/payloadcms/payload/tree/main/examples/hierarchy)
|
||||
- [Live Preview](https://github.com/payloadcms/payload/tree/main/examples/live-preview)
|
||||
- [Multi-tenant](https://github.com/payloadcms/payload/tree/main/examples/multi-tenant)
|
||||
- [Nested Docs](https://github.com/payloadcms/payload/tree/main/examples/nested-docs)
|
||||
- [Redirects](https://github.com/payloadcms/payload/tree/main/examples/redirects)
|
||||
- [Tests](https://github.com/payloadcms/payload/tree/main/examples/testing)
|
||||
- [Virtual Fields](https://github.com/payloadcms/payload/tree/main/examples/virtual-fields)
|
||||
- [White-label Admin UI](https://github.com/payloadcms/payload/tree/main/examples/whitelabel)
|
||||
|
||||
Where necessary, some examples include a front-end. Examples that require a front-end share this folder structure:
|
||||
|
||||
```plaintext
|
||||
example/
|
||||
├── payload/
|
||||
├── next-app/
|
||||
├── next-pages/
|
||||
├── react-router/
|
||||
├── vue/
|
||||
├── svelte/
|
||||
```
|
||||
|
||||
Where `payload` is your Payload project, and the other directories are dedicated to their respective front-end framework. We are adding new examples every day, so if your framework of choice is not yet supported in any particular example, please feel free to start a new [Discussion](https://github.com/payloadcms/payload/discussions) or open a new [PR](https://github.com/payloadcms/payload/pulls) to add it yourself.
|
||||
@@ -59,6 +59,7 @@ properties:
|
||||
| Option | Description |
|
||||
|---------------------------|----------------------------------------------------------------------------------------------------------------------|
|
||||
| **`initCollapsed`** | Set the initial collapsed state |
|
||||
| **`isSortable`** | Disable array order sorting by setting this value to `false` |
|
||||
| **`components.RowLabel`** | Function or React component to be rendered as the label on the array row. Receives `({ data, index, path })` as args |
|
||||
|
||||
### Example
|
||||
@@ -67,6 +68,7 @@ properties:
|
||||
|
||||
```ts
|
||||
import { CollectionConfig } from 'payload/types'
|
||||
import { RowLabelArgs } from 'payload/dist/admin/components/forms/RowLabel/types'
|
||||
|
||||
export const ExampleCollection: CollectionConfig = {
|
||||
slug: 'example-collection',
|
||||
@@ -101,7 +103,7 @@ export const ExampleCollection: CollectionConfig = {
|
||||
],
|
||||
admin: {
|
||||
components: {
|
||||
RowLabel: ({ data, index }) => {
|
||||
RowLabel: ({ data, index }: RowLabelArgs) => {
|
||||
return data?.title || `Slide ${String(index).padStart(2, '0')}`
|
||||
},
|
||||
},
|
||||
|
||||
@@ -58,6 +58,7 @@ properties:
|
||||
| Option | Description |
|
||||
|---------------------|---------------------------------|
|
||||
| **`initCollapsed`** | Set the initial collapsed state |
|
||||
| **`isSortable`** | Disable block order sorting by setting this value to `false` |
|
||||
|
||||
### Block configs
|
||||
|
||||
|
||||
@@ -163,19 +163,21 @@ Example:
|
||||
|
||||
In addition to each field's base configuration, you can define specific traits and properties for fields that only have effect on how they are rendered in the Admin panel. The following properties are available for all fields within the `admin` property:
|
||||
|
||||
| Option | Description |
|
||||
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `condition` | You can programmatically show / hide fields based on what other fields are doing. [Click here](#conditional-logic) for more info. |
|
||||
| `components` | All field components can be completely and easily swapped out for custom components that you define. [Click here](#custom-components) for more info. |
|
||||
| `description` | Helper text to display with the field to provide more information for the editor user. [Click here](#description) for more info. |
|
||||
| `position` | Specify if the field should be rendered in the sidebar by defining `position: 'sidebar'`. |
|
||||
| `width` | Restrict the width of a field. you can pass any string-based value here, be it pixels, percentages, etc. This property is especially useful when fields are nested within a `Row` type where they can be organized horizontally. |
|
||||
| `style` | Attach raw CSS style properties to the root DOM element of a field. |
|
||||
| `className` | Attach a CSS class name to the root DOM element of a field. |
|
||||
| `readOnly` | Setting a field to `readOnly` has no effect on the API whatsoever but disables the admin component's editability to prevent editors from modifying the field's value. |
|
||||
| `disabled` | If a field is `disabled`, it is completely omitted from the Admin panel. |
|
||||
| `disableBulkEdit` | Set `disableBulkEdit` to `true` to prevent fields from appearing in the select options when making edits for multiple documents. |
|
||||
| `hidden` | Setting a field's `hidden` property on its `admin` config will transform it into a `hidden` input type. Its value will still submit with the Admin panel's requests, but the field itself will not be visible to editors. |
|
||||
| Option | Description |
|
||||
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `condition` | You can programmatically show / hide fields based on what other fields are doing. [Click here](#conditional-logic) for more info. |
|
||||
| `components` | All field components can be completely and easily swapped out for custom components that you define. [Click here](#custom-components) for more info. |
|
||||
| `description` | Helper text to display with the field to provide more information for the editor user. [Click here](#description) for more info. |
|
||||
| `position` | Specify if the field should be rendered in the sidebar by defining `position: 'sidebar'`. |
|
||||
| `width` | Restrict the width of a field. you can pass any string-based value here, be it pixels, percentages, etc. This property is especially useful when fields are nested within a `Row` type where they can be organized horizontally. |
|
||||
| `style` | Attach raw CSS style properties to the root DOM element of a field. |
|
||||
| `className` | Attach a CSS class name to the root DOM element of a field. |
|
||||
| `readOnly` | Setting a field to `readOnly` has no effect on the API whatsoever but disables the admin component's editability to prevent editors from modifying the field's value. |
|
||||
| `disabled` | If a field is `disabled`, it is completely omitted from the Admin panel. |
|
||||
| `disableBulkEdit` | Set `disableBulkEdit` to `true` to prevent fields from appearing in the select options when making edits for multiple documents. |
|
||||
| `disableListColumn` | Set `disableListColumn` to `true` to prevent fields from appearing in the list view column selector. |
|
||||
| `disableListFilter` | Set `disableListFilter` to `true` to prevent fields from appearing in the list view filter options. |
|
||||
| `hidden` | Setting a field's `hidden` property on its `admin` config will transform it into a `hidden` input type. Its value will still submit with the Admin panel's requests, but the field itself will not be visible to editors. |
|
||||
|
||||
### Custom components
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ If your Hook simply performs a side-effect, such as updating a CRM, it might be
|
||||
|
||||
#### Server-only execution
|
||||
|
||||
Payload Hooks are only triggered on the server. You can safely [remove your hooks](/docs/admin/webpack#aliasing-server-only-modules) from your Admin panel's client-side code by customizing the Webpack config, which not only keeps your Admin bundles' filesize small but also ensures that any server-side only code does not cause problems within browser environments.
|
||||
Payload Hooks are only triggered on the server. You can safely [remove your hooks](/docs/admin/excluding-server-code#aliasing-server-only-modules) from your Admin panel's client-side code by customizing the Webpack config, which not only keeps your Admin bundles' filesize small but also ensures that any server-side only code does not cause problems within browser environments.
|
||||
|
||||
## Hook Types
|
||||
|
||||
|
||||
@@ -247,7 +247,7 @@ In the template, we have stubbed out a basic `onInitExtension` file that you can
|
||||
|
||||
### Webpack
|
||||
|
||||
If any of your files use server only packages such as fs, stripe, nodemailer, etc, they will need to be removed from the browser bundle. To do that, you can [alias the file imports with webpack](https://payloadcms.com/docs/admin/webpack#aliasing-server-only-modules).
|
||||
If any of your files use server only packages such as fs, stripe, nodemailer, etc, they will need to be removed from the browser bundle. To do that, you can [alias the file imports with webpack](https://payloadcms.com/docs/admin/excluding-server-code#aliasing-server-only-modules).
|
||||
|
||||
When files are bundled for the browser, the import paths are essentially crawled to determine what files to include in the bundle. To prevent the server only files from making it into the bundle, we can alias their import paths to a file that can be included in the browser. This will short-circuit the import path crawling and ensure browser only code is bundled.
|
||||
|
||||
|
||||
@@ -203,7 +203,8 @@ const Pages: CollectionConfig = {
|
||||
editor: lexicalEditor({
|
||||
features: ({ defaultFeatures }) => [
|
||||
...defaultFeatures,
|
||||
// The HTMLConverter Feature is the feature which manages the HTML serializers. If you do not pass any arguments to it, it will use the default serializers.
|
||||
// The HTMLConverter Feature is the feature which manages the HTML serializers.
|
||||
// If you do not pass any arguments to it, it will use the default serializers.
|
||||
HTMLConverterFeature({}),
|
||||
],
|
||||
}),
|
||||
|
||||
@@ -270,6 +270,10 @@ const serialize = (children) =>
|
||||
text = <em key={i}>{text}</em>;
|
||||
}
|
||||
|
||||
if (node.text === '') {
|
||||
text = <br />;
|
||||
}
|
||||
|
||||
// Handle other leaf types here...
|
||||
|
||||
return <Fragment key={i}>{text}</Fragment>;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/bundler-vite",
|
||||
"version": "0.1.6",
|
||||
"version": "0.1.7",
|
||||
"description": "The officially supported Vite bundler adapter for Payload",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -41,7 +41,7 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"payload": "^2.0.0",
|
||||
"react-dom": "18.2.0"
|
||||
"react-dom": "^18.0.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"main": "./dist/index.js",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-postgres",
|
||||
"version": "0.8.1",
|
||||
"version": "0.8.3",
|
||||
"description": "The officially supported Postgres database adapter for Payload",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -10,8 +10,8 @@ import type { GenericColumn, PostgresAdapter } from '../types'
|
||||
import type { BuildQueryJoinAliases, BuildQueryJoins } from './buildQuery'
|
||||
|
||||
import { buildAndOrConditions } from './buildAndOrConditions'
|
||||
import { convertPathToJSONTraversal } from './createJSONQuery/convertPathToJSONTraversal'
|
||||
import { createJSONQuery } from './createJSONQuery'
|
||||
import { convertPathToJSONTraversal } from './createJSONQuery/convertPathToJSONTraversal'
|
||||
import { getTableColumnFromPath } from './getTableColumnFromPath'
|
||||
import { operatorMap } from './operatorMap'
|
||||
import { sanitizeQueryValue } from './sanitizeQueryValue'
|
||||
@@ -195,10 +195,10 @@ export async function parseParams({
|
||||
operator === 'not_in'
|
||||
) {
|
||||
constraints.push(
|
||||
sql`${notInArray(table[columnName], queryValue)} OR
|
||||
sql`(${notInArray(table[columnName], queryValue)} OR
|
||||
${table[columnName]}
|
||||
IS
|
||||
NULL`,
|
||||
NULL)`,
|
||||
)
|
||||
|
||||
break
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "payload",
|
||||
"version": "2.14.1",
|
||||
"version": "2.18.0",
|
||||
"description": "Node, React and MongoDB Headless CMS and Application Framework",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
@@ -118,11 +118,11 @@
|
||||
"process": "0.11.10",
|
||||
"qs": "6.11.2",
|
||||
"qs-middleware": "1.0.3",
|
||||
"react": "18.2.0",
|
||||
"react": "^18.0.0",
|
||||
"react-animate-height": "2.1.2",
|
||||
"react-datepicker": "4.16.0",
|
||||
"react-diff-viewer-continued": "3.2.6",
|
||||
"react-dom": "18.2.0",
|
||||
"react-dom": "^18.0.0",
|
||||
"react-helmet": "6.1.0",
|
||||
"react-i18next": "11.18.6",
|
||||
"react-image-crop": "10.1.8",
|
||||
|
||||
@@ -19,6 +19,7 @@ import { ConfigProvider } from './components/utilities/Config'
|
||||
import { CustomProvider } from './components/utilities/CustomProvider'
|
||||
import { DocumentEventsProvider } from './components/utilities/DocumentEvents'
|
||||
import { I18n } from './components/utilities/I18n'
|
||||
import { LanguageWrap } from './components/utilities/LanguageWrap'
|
||||
import { LoadingOverlayProvider } from './components/utilities/LoadingOverlay'
|
||||
import { LocaleProvider } from './components/utilities/Locale'
|
||||
import { PreferencesProvider } from './components/utilities/Preferences'
|
||||
@@ -46,21 +47,23 @@ const Root = ({ config: incomingConfig }: { config?: SanitizedConfig }) => {
|
||||
<AuthProvider>
|
||||
<PreferencesProvider>
|
||||
<ThemeProvider>
|
||||
<SearchParamsProvider>
|
||||
<LocaleProvider>
|
||||
<StepNavProvider>
|
||||
<LoadingOverlayProvider>
|
||||
<DocumentEventsProvider>
|
||||
<NavProvider>
|
||||
<CustomProvider>
|
||||
<Routes />
|
||||
</CustomProvider>
|
||||
</NavProvider>
|
||||
</DocumentEventsProvider>
|
||||
</LoadingOverlayProvider>
|
||||
</StepNavProvider>
|
||||
</LocaleProvider>
|
||||
</SearchParamsProvider>
|
||||
<LanguageWrap>
|
||||
<SearchParamsProvider>
|
||||
<LocaleProvider>
|
||||
<StepNavProvider>
|
||||
<LoadingOverlayProvider>
|
||||
<DocumentEventsProvider>
|
||||
<NavProvider>
|
||||
<CustomProvider>
|
||||
<Routes />
|
||||
</CustomProvider>
|
||||
</NavProvider>
|
||||
</DocumentEventsProvider>
|
||||
</LoadingOverlayProvider>
|
||||
</StepNavProvider>
|
||||
</LocaleProvider>
|
||||
</SearchParamsProvider>
|
||||
</LanguageWrap>
|
||||
</ThemeProvider>
|
||||
<ModalContainer />
|
||||
</PreferencesProvider>
|
||||
|
||||
@@ -19,6 +19,7 @@ export const ArrayAction: React.FC<Props> = ({
|
||||
duplicateRow,
|
||||
hasMaxRows,
|
||||
index,
|
||||
isSortable,
|
||||
moveRow,
|
||||
removeRow,
|
||||
rowCount,
|
||||
@@ -33,7 +34,7 @@ export const ArrayAction: React.FC<Props> = ({
|
||||
render={({ close }) => {
|
||||
return (
|
||||
<PopupList.ButtonGroup buttonSize="small">
|
||||
{index !== 0 && (
|
||||
{isSortable && index !== 0 && (
|
||||
<PopupList.Button
|
||||
className={`${baseClass}__action ${baseClass}__move-up`}
|
||||
onClick={() => {
|
||||
@@ -47,7 +48,7 @@ export const ArrayAction: React.FC<Props> = ({
|
||||
{t('moveUp')}
|
||||
</PopupList.Button>
|
||||
)}
|
||||
{index < rowCount - 1 && (
|
||||
{isSortable && index < rowCount - 1 && (
|
||||
<PopupList.Button
|
||||
className={`${baseClass}__action`}
|
||||
onClick={() => {
|
||||
|
||||
@@ -3,6 +3,7 @@ export type Props = {
|
||||
duplicateRow: (current: number) => void
|
||||
hasMaxRows: boolean
|
||||
index: number
|
||||
isSortable: boolean
|
||||
moveRow: (from: number, to: number) => void
|
||||
removeRow: (index: number) => void
|
||||
rowCount: number
|
||||
|
||||
@@ -15,7 +15,7 @@ import './index.scss'
|
||||
const baseClass = 'column-selector'
|
||||
|
||||
const ColumnSelector: React.FC<Props> = (props) => {
|
||||
const { collection } = props
|
||||
const { slug } = props
|
||||
|
||||
const { columns, moveColumn, toggleColumn } = useTableColumns()
|
||||
|
||||
@@ -53,7 +53,7 @@ const ColumnSelector: React.FC<Props> = (props) => {
|
||||
draggable
|
||||
icon={active ? <X /> : <Plus />}
|
||||
id={accessor}
|
||||
key={`${collection.slug}-${col.name || i}${editDepth ? `-${editDepth}-` : ''}${uuid}`}
|
||||
key={`${slug}-${col.name || i}${editDepth ? `-${editDepth}-` : ''}${uuid}`}
|
||||
onClick={() => {
|
||||
toggleColumn(accessor)
|
||||
}}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { SanitizedCollectionConfig } from '../../../../collections/config/types'
|
||||
|
||||
export type Props = {
|
||||
collection: SanitizedCollectionConfig
|
||||
slug: SanitizedCollectionConfig['slug']
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { CollectionPermission, GlobalPermission } from '../../../../auth'
|
||||
import type { SanitizedCollectionConfig } from '../../../../collections/config/types'
|
||||
import type { SanitizedGlobalConfig } from '../../../../globals/config/types'
|
||||
|
||||
import { getTranslation } from '../../../../utilities/getTranslation'
|
||||
import { formatDate } from '../../../utilities/formatDate'
|
||||
import { useConfig } from '../../utilities/Config'
|
||||
import { useDocumentInfo } from '../../utilities/DocumentInfo'
|
||||
@@ -63,6 +64,12 @@ export const DocumentControls: React.FC<{
|
||||
collection && id && !disableActions && (hasCreatePermission || hasDeletePermission),
|
||||
)
|
||||
|
||||
const collectionLabel = () => {
|
||||
const label = collection?.labels?.singular
|
||||
if (!label) return t('document')
|
||||
return typeof label === 'string' ? label : getTranslation(label, i18n)
|
||||
}
|
||||
|
||||
return (
|
||||
<Gutter className={baseClass}>
|
||||
<div className={`${baseClass}__wrapper`}>
|
||||
@@ -71,12 +78,7 @@ export const DocumentControls: React.FC<{
|
||||
{collection && !isEditing && !isAccountView && (
|
||||
<li className={`${baseClass}__list-item`}>
|
||||
<p className={`${baseClass}__value`}>
|
||||
{t('creatingNewLabel', {
|
||||
label:
|
||||
typeof collection?.labels?.singular === 'string'
|
||||
? collection.labels.singular
|
||||
: t('document'),
|
||||
})}
|
||||
{t('creatingNewLabel', { label: collectionLabel() })}
|
||||
</p>
|
||||
</li>
|
||||
)}
|
||||
|
||||
@@ -139,7 +139,7 @@ const Duplicate: React.FC<Props> = ({ id, slug, collection }) => {
|
||||
if (localeErrors.length > 0) {
|
||||
toast.error(
|
||||
`
|
||||
${t('error:localesNotSaved', { count: localeErrors.length })}
|
||||
${t('error:localesNotSaved_other', { count: localeErrors.length })}
|
||||
${localeErrors.join(', ')}
|
||||
`,
|
||||
{ autoClose: 5000 },
|
||||
|
||||
@@ -32,7 +32,7 @@ export const EditUpload: React.FC<{
|
||||
imageCacheTag?: string
|
||||
showCrop?: boolean
|
||||
showFocalPoint?: boolean
|
||||
}> = ({ fileName, fileSrc, imageCacheTag, showCrop, showFocalPoint }) => {
|
||||
}> = ({ doc, fileName, fileSrc, imageCacheTag, showCrop, showFocalPoint }) => {
|
||||
const { closeModal } = useModal()
|
||||
const { t } = useTranslation(['general', 'upload'])
|
||||
const { formQueryParams, setFormQueryParams } = useFormQueryParams()
|
||||
@@ -41,14 +41,13 @@ export const EditUpload: React.FC<{
|
||||
height: uploadEdits?.crop?.height || 100,
|
||||
unit: '%',
|
||||
width: uploadEdits?.crop?.width || 100,
|
||||
x: uploadEdits?.crop?.x || 0,
|
||||
y: uploadEdits?.crop?.y || 0,
|
||||
})
|
||||
|
||||
const [pointPosition, setPointPosition] = useState<{ x: number; y: number }>({
|
||||
x: uploadEdits?.focalPoint?.x || 50,
|
||||
y: uploadEdits?.focalPoint?.y || 50,
|
||||
const [focalPosition, setFocalPosition] = useState<{ x: number; y: number }>({
|
||||
x: uploadEdits?.focalPoint?.x || doc.focalX || 50,
|
||||
y: uploadEdits?.focalPoint?.y || doc.focalY || 50,
|
||||
})
|
||||
|
||||
const [checkBounds, setCheckBounds] = useState<boolean>(false)
|
||||
const [originalHeight, setOriginalHeight] = useState<number>(0)
|
||||
const [originalWidth, setOriginalWidth] = useState<number>(0)
|
||||
@@ -72,10 +71,16 @@ export const EditUpload: React.FC<{
|
||||
})
|
||||
}
|
||||
|
||||
const fineTuneFocalPoint = ({ coordinate, value }: { coordinate: 'x' | 'y'; value: string }) => {
|
||||
const fineTuneFocalPosition = ({
|
||||
coordinate,
|
||||
value,
|
||||
}: {
|
||||
coordinate: 'x' | 'y'
|
||||
value: string
|
||||
}) => {
|
||||
const intValue = parseInt(value)
|
||||
if (intValue >= 0 && intValue <= 100) {
|
||||
setPointPosition((prevPosition) => ({ ...prevPosition, [coordinate]: intValue }))
|
||||
setFocalPosition((prevPosition) => ({ ...prevPosition, [coordinate]: intValue }))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,14 +89,14 @@ export const EditUpload: React.FC<{
|
||||
...formQueryParams,
|
||||
uploadEdits: {
|
||||
crop: crop || undefined,
|
||||
focalPoint: pointPosition ? pointPosition : undefined,
|
||||
focalPoint: focalPosition ? focalPosition : undefined,
|
||||
},
|
||||
})
|
||||
closeModal(editDrawerSlug)
|
||||
}
|
||||
|
||||
const onDragEnd = React.useCallback(({ x, y }) => {
|
||||
setPointPosition({ x, y })
|
||||
setFocalPosition({ x, y })
|
||||
setCheckBounds(false)
|
||||
}, [])
|
||||
|
||||
@@ -104,7 +109,7 @@ export const EditUpload: React.FC<{
|
||||
((boundsRect.left - containerRect.left + boundsRect.width / 2) / containerRect.width) * 100
|
||||
const yCenter =
|
||||
((boundsRect.top - containerRect.top + boundsRect.height / 2) / containerRect.height) * 100
|
||||
setPointPosition({ x: xCenter, y: yCenter })
|
||||
setFocalPosition({ x: xCenter, y: yCenter })
|
||||
}
|
||||
|
||||
const fileSrcToUse = imageCacheTag ? `${fileSrc}?${imageCacheTag}` : fileSrc
|
||||
@@ -180,7 +185,7 @@ export const EditUpload: React.FC<{
|
||||
checkBounds={showCrop ? checkBounds : false}
|
||||
className={`${baseClass}__focalPoint`}
|
||||
containerRef={focalWrapRef}
|
||||
initialPosition={pointPosition}
|
||||
initialPosition={focalPosition}
|
||||
onDragEnd={onDragEnd}
|
||||
setCheckBounds={showCrop ? setCheckBounds : false}
|
||||
>
|
||||
@@ -251,13 +256,13 @@ export const EditUpload: React.FC<{
|
||||
<div className={`${baseClass}__inputsWrap`}>
|
||||
<Input
|
||||
name="X %"
|
||||
onChange={(value) => fineTuneFocalPoint({ coordinate: 'x', value })}
|
||||
value={pointPosition.x.toFixed(0)}
|
||||
onChange={(value) => fineTuneFocalPosition({ coordinate: 'x', value })}
|
||||
value={focalPosition.x.toFixed(0)}
|
||||
/>
|
||||
<Input
|
||||
name="Y %"
|
||||
onChange={(value) => fineTuneFocalPoint({ coordinate: 'y', value })}
|
||||
value={pointPosition.y.toFixed(0)}
|
||||
onChange={(value) => fineTuneFocalPosition({ coordinate: 'y', value })}
|
||||
value={focalPosition.y.toFixed(0)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -9,7 +9,6 @@ import { fieldAffectsData } from '../../../../fields/config/types'
|
||||
import { getTranslation } from '../../../../utilities/getTranslation'
|
||||
import Chevron from '../../icons/Chevron'
|
||||
import { useSearchParams } from '../../utilities/SearchParams'
|
||||
import Button from '../Button'
|
||||
import ColumnSelector from '../ColumnSelector'
|
||||
import DeleteMany from '../DeleteMany'
|
||||
import EditMany from '../EditMany'
|
||||
@@ -50,6 +49,8 @@ export const ListControls: React.FC<Props> = (props) => {
|
||||
const params = useSearchParams()
|
||||
const shouldInitializeWhereOpened = validateWhereQuery(params?.where)
|
||||
|
||||
const hasWhereParam = React.useRef(Boolean(params?.where))
|
||||
|
||||
const [textFieldsToBeSearched, setFieldsToBeSearched] = useState(
|
||||
getTextFieldsToBeSearched(listSearchableFields, fields),
|
||||
)
|
||||
@@ -65,6 +66,15 @@ export const ListControls: React.FC<Props> = (props) => {
|
||||
setFieldsToBeSearched(getTextFieldsToBeSearched(listSearchableFields, fields))
|
||||
}, [listSearchableFields, fields])
|
||||
|
||||
React.useEffect(() => {
|
||||
if (hasWhereParam.current && !params?.where) {
|
||||
setVisibleDrawer(undefined)
|
||||
hasWhereParam.current = false
|
||||
} else if (params?.where) {
|
||||
hasWhereParam.current = true
|
||||
}
|
||||
}, [setVisibleDrawer, params?.where])
|
||||
|
||||
return (
|
||||
<div className={baseClass}>
|
||||
<div className={`${baseClass}__wrap`}>
|
||||
@@ -136,7 +146,7 @@ export const ListControls: React.FC<Props> = (props) => {
|
||||
height={visibleDrawer === 'columns' ? 'auto' : 0}
|
||||
id={`${baseClass}-columns`}
|
||||
>
|
||||
<ColumnSelector collection={collection} />
|
||||
<ColumnSelector slug={collection.slug} />
|
||||
</AnimateHeight>
|
||||
)}
|
||||
<AnimateHeight
|
||||
@@ -147,6 +157,7 @@ export const ListControls: React.FC<Props> = (props) => {
|
||||
<WhereBuilder
|
||||
collection={collection}
|
||||
handleChange={handleWhereChange}
|
||||
key={String(hasWhereParam.current && !params?.where)}
|
||||
modifySearchQuery={modifySearchQuery}
|
||||
/>
|
||||
</AnimateHeight>
|
||||
|
||||
@@ -24,7 +24,6 @@ export const LoadingOverlay: React.FC<Props> = ({
|
||||
show = true,
|
||||
}) => {
|
||||
const { t } = useTranslation('general')
|
||||
|
||||
return (
|
||||
<div
|
||||
className={[
|
||||
@@ -59,25 +58,25 @@ type UseLoadingOverlayToggleT = {
|
||||
}
|
||||
export const LoadingOverlayToggle: React.FC<UseLoadingOverlayToggleT> = ({
|
||||
name: key,
|
||||
type = 'fullscreen',
|
||||
loadingText,
|
||||
show,
|
||||
type = 'fullscreen',
|
||||
}) => {
|
||||
const { toggleLoadingOverlay } = useLoadingOverlay()
|
||||
|
||||
React.useEffect(() => {
|
||||
toggleLoadingOverlay({
|
||||
type,
|
||||
isLoading: show,
|
||||
key,
|
||||
loadingText: loadingText || undefined,
|
||||
type,
|
||||
})
|
||||
|
||||
return () => {
|
||||
toggleLoadingOverlay({
|
||||
type,
|
||||
isLoading: false,
|
||||
key,
|
||||
type,
|
||||
})
|
||||
}
|
||||
}, [show, toggleLoadingOverlay, key, type, loadingText])
|
||||
@@ -94,10 +93,10 @@ type FormLoadingOverlayToggleT = {
|
||||
}
|
||||
export const FormLoadingOverlayToggle: React.FC<FormLoadingOverlayToggleT> = ({
|
||||
name,
|
||||
type = 'fullscreen',
|
||||
action,
|
||||
formIsLoading = false,
|
||||
loadingSuffix,
|
||||
type = 'fullscreen',
|
||||
}) => {
|
||||
const isProcessing = useFormProcessing()
|
||||
const { i18n, t } = useTranslation('general')
|
||||
|
||||
@@ -18,7 +18,7 @@ import './index.scss'
|
||||
const baseClass = 'publish-many'
|
||||
|
||||
const PublishMany: React.FC<Props> = (props) => {
|
||||
const { collection: { labels: { plural }, slug, versions } = {}, resetParams } = props
|
||||
const { collection: { slug, labels: { plural }, versions } = {}, resetParams } = props
|
||||
|
||||
const {
|
||||
routes: { api },
|
||||
@@ -27,7 +27,7 @@ const PublishMany: React.FC<Props> = (props) => {
|
||||
const { permissions } = useAuth()
|
||||
const { toggleModal } = useModal()
|
||||
const { i18n, t } = useTranslation('version')
|
||||
const { count, getQueryParams, selectAll } = useSelection()
|
||||
const { getQueryParams, selectAll } = useSelection()
|
||||
const [submitted, setSubmitted] = useState(false)
|
||||
|
||||
const collectionPermissions = permissions?.collections?.[slug]
|
||||
@@ -41,9 +41,11 @@ const PublishMany: React.FC<Props> = (props) => {
|
||||
|
||||
const handlePublish = useCallback(() => {
|
||||
setSubmitted(true)
|
||||
requests
|
||||
void requests
|
||||
.patch(
|
||||
`${serverURL}${api}/${slug}${getQueryParams({ _status: { not_equals: 'published' } })}`,
|
||||
`${serverURL}${api}/${slug}${getQueryParams({
|
||||
_status: { not_equals: 'published' },
|
||||
})}&draft=true`,
|
||||
{
|
||||
body: JSON.stringify({
|
||||
_status: 'published',
|
||||
|
||||
@@ -40,7 +40,7 @@ const SelectAdapter: React.FC<ReactSelectAdapterProps> = (props) => {
|
||||
isCreatable,
|
||||
isLoading,
|
||||
isSearchable = true,
|
||||
noOptionsMessage,
|
||||
noOptionsMessage = () => t('general:noOptions'),
|
||||
numberOnly = false,
|
||||
onChange,
|
||||
onMenuOpen,
|
||||
@@ -50,6 +50,8 @@ const SelectAdapter: React.FC<ReactSelectAdapterProps> = (props) => {
|
||||
value,
|
||||
} = props
|
||||
|
||||
const loadingMessage = () => t('general:loading') + '...'
|
||||
|
||||
const classes = [className, 'react-select', showError && 'react-select--error']
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
@@ -79,6 +81,7 @@ const SelectAdapter: React.FC<ReactSelectAdapterProps> = (props) => {
|
||||
isClearable={isClearable}
|
||||
isDisabled={disabled}
|
||||
isSearchable={isSearchable}
|
||||
loadingMessage={loadingMessage}
|
||||
menuPlacement="auto"
|
||||
noOptionsMessage={noOptionsMessage}
|
||||
onChange={onChange}
|
||||
@@ -148,6 +151,7 @@ const SelectAdapter: React.FC<ReactSelectAdapterProps> = (props) => {
|
||||
isClearable={isClearable}
|
||||
isDisabled={disabled}
|
||||
isSearchable={isSearchable}
|
||||
loadingMessage={loadingMessage}
|
||||
menuPlacement="auto"
|
||||
noOptionsMessage={noOptionsMessage}
|
||||
onChange={onChange}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
.step-nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: calc(var(--base) / 2);
|
||||
|
||||
&::after {
|
||||
@@ -56,8 +57,6 @@
|
||||
}
|
||||
|
||||
span {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
max-width: base(8);
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
|
||||
@@ -33,6 +33,13 @@ export const TableColumnContext = createContext<ITableColumns>({} as ITableColum
|
||||
|
||||
export const useTableColumns = (): ITableColumns => useContext(TableColumnContext)
|
||||
|
||||
const filterTableFields = (fields: Field[]): Field[] => {
|
||||
return fields.reduce((acc, field) => {
|
||||
if (!field.admin?.disableListColumn) acc.push(field)
|
||||
return acc
|
||||
}, [])
|
||||
}
|
||||
|
||||
export const TableColumnsProvider: React.FC<{
|
||||
cellProps?: Partial<CellProps>[]
|
||||
children: React.ReactNode
|
||||
@@ -50,9 +57,10 @@ export const TableColumnsProvider: React.FC<{
|
||||
const hasInitialized = useRef(false)
|
||||
const { getPreference, setPreference } = usePreferences()
|
||||
const [formattedFields] = useState<Field[]>(() => formatFields(collection))
|
||||
const filteredFields = filterTableFields(formattedFields)
|
||||
|
||||
const [tableColumns, dispatchTableColumns] = useReducer(columnReducer, {}, () => {
|
||||
const initialColumns = getInitialColumnState(formattedFields, useAsTitle, defaultColumns)
|
||||
const initialColumns = getInitialColumnState(filteredFields, useAsTitle, defaultColumns)
|
||||
|
||||
return buildColumns({
|
||||
cellProps,
|
||||
@@ -77,13 +85,14 @@ export const TableColumnsProvider: React.FC<{
|
||||
|
||||
const currentPreferences = await getPreference<ListPreferences>(preferenceKey)
|
||||
prevCollection.current = collection.slug
|
||||
const initialColumns = getInitialColumnState(formattedFields, useAsTitle, defaultColumns)
|
||||
const initialColumns = getInitialColumnState(filteredFields, useAsTitle, defaultColumns)
|
||||
const newCols = currentPreferences?.columns || initialColumns
|
||||
|
||||
dispatchTableColumns({
|
||||
type: 'set',
|
||||
payload: {
|
||||
cellProps,
|
||||
collection: { ...collection, fields: formatFields(collection) },
|
||||
collection: { ...collection, fields: filteredFields },
|
||||
columns: newCols.map((column) => {
|
||||
// 'string' is for backwards compatibility
|
||||
// the preference used to be stored as an array of strings
|
||||
@@ -96,14 +105,13 @@ export const TableColumnsProvider: React.FC<{
|
||||
return column
|
||||
}),
|
||||
},
|
||||
type: 'set',
|
||||
})
|
||||
|
||||
hasInitialized.current = true
|
||||
}
|
||||
}
|
||||
|
||||
sync()
|
||||
void sync()
|
||||
}, [
|
||||
preferenceKey,
|
||||
setPreference,
|
||||
@@ -113,7 +121,7 @@ export const TableColumnsProvider: React.FC<{
|
||||
defaultColumns,
|
||||
collection,
|
||||
cellProps,
|
||||
formattedFields,
|
||||
filteredFields,
|
||||
])
|
||||
|
||||
// /////////////////////////////////////
|
||||
@@ -133,6 +141,7 @@ export const TableColumnsProvider: React.FC<{
|
||||
const setActiveColumns = useCallback(
|
||||
(columns: string[]) => {
|
||||
dispatchTableColumns({
|
||||
type: 'set',
|
||||
payload: {
|
||||
// onSelect,
|
||||
cellProps,
|
||||
@@ -142,7 +151,6 @@ export const TableColumnsProvider: React.FC<{
|
||||
active: true,
|
||||
})),
|
||||
},
|
||||
type: 'set',
|
||||
})
|
||||
},
|
||||
[collection, cellProps],
|
||||
@@ -153,13 +161,13 @@ export const TableColumnsProvider: React.FC<{
|
||||
const { fromIndex, toIndex } = args
|
||||
|
||||
dispatchTableColumns({
|
||||
type: 'move',
|
||||
payload: {
|
||||
cellProps,
|
||||
collection: { ...collection, fields: formatFields(collection) },
|
||||
fromIndex,
|
||||
toIndex,
|
||||
},
|
||||
type: 'move',
|
||||
})
|
||||
},
|
||||
[collection, cellProps],
|
||||
@@ -168,12 +176,12 @@ export const TableColumnsProvider: React.FC<{
|
||||
const toggleColumn = useCallback(
|
||||
(column: string) => {
|
||||
dispatchTableColumns({
|
||||
type: 'toggle',
|
||||
payload: {
|
||||
cellProps,
|
||||
collection: { ...collection, fields: formatFields(collection) },
|
||||
column,
|
||||
},
|
||||
type: 'toggle',
|
||||
})
|
||||
},
|
||||
[collection, cellProps],
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import type { Where } from 'payload/types'
|
||||
|
||||
import qs from 'qs'
|
||||
import React, { useCallback, useEffect, useReducer, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
@@ -6,6 +9,7 @@ import type { Option } from '../../../ReactSelect/types'
|
||||
import type { GetResults, Props, ValueWithRelation } from './types'
|
||||
|
||||
import useDebounce from '../../../../../hooks/useDebounce'
|
||||
import { useAuth } from '../../../../utilities/Auth'
|
||||
import { useConfig } from '../../../../utilities/Config'
|
||||
import ReactSelect from '../../../ReactSelect'
|
||||
import './index.scss'
|
||||
@@ -16,7 +20,15 @@ const baseClass = 'condition-value-relationship'
|
||||
const maxResultsPerRequest = 10
|
||||
|
||||
const RelationshipField: React.FC<Props> = (props) => {
|
||||
const { admin: { isSortable } = {}, disabled, hasMany, onChange, relationTo, value } = props
|
||||
const {
|
||||
admin: { isSortable } = {},
|
||||
disabled,
|
||||
filterOptions,
|
||||
hasMany,
|
||||
onChange,
|
||||
relationTo,
|
||||
value,
|
||||
} = props
|
||||
|
||||
const {
|
||||
collections,
|
||||
@@ -33,11 +45,12 @@ const RelationshipField: React.FC<Props> = (props) => {
|
||||
const [hasLoadedFirstOptions, setHasLoadedFirstOptions] = useState(false)
|
||||
const debouncedSearch = useDebounce(search, 300)
|
||||
const { i18n, t } = useTranslation('general')
|
||||
const { user } = useAuth()
|
||||
|
||||
const addOptions = useCallback(
|
||||
(data, relation) => {
|
||||
const collection = collections.find((coll) => coll.slug === relation)
|
||||
dispatchOptions({ collection, data, hasMultipleRelations, i18n, relation, type: 'ADD' })
|
||||
dispatchOptions({ type: 'ADD', collection, data, hasMultipleRelations, i18n, relation })
|
||||
},
|
||||
[collections, hasMultipleRelations, i18n],
|
||||
)
|
||||
@@ -61,23 +74,66 @@ const RelationshipField: React.FC<Props> = (props) => {
|
||||
let resultsFetched = 0
|
||||
|
||||
if (!errorLoading) {
|
||||
relationsToFetch.reduce(async (priorRelation, relation) => {
|
||||
void relationsToFetch.reduce(async (priorRelation, relation) => {
|
||||
await priorRelation
|
||||
|
||||
if (resultsFetched < 10) {
|
||||
const search: Record<string, unknown> & { where: Where } = {
|
||||
depth: 0,
|
||||
limit: maxResultsPerRequest,
|
||||
page: lastLoadedPageToUse,
|
||||
where: { and: [] },
|
||||
}
|
||||
const collection = collections.find((coll) => coll.slug === relation)
|
||||
const fieldToSearch = collection?.admin?.useAsTitle || 'id'
|
||||
const searchParam = searchArg ? `&where[${fieldToSearch}][like]=${searchArg}` : ''
|
||||
|
||||
const response = await fetch(
|
||||
`${serverURL}${api}/${relation}?limit=${maxResultsPerRequest}&page=${lastLoadedPageToUse}&depth=0${searchParam}`,
|
||||
{
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Accept-Language': i18n.language,
|
||||
// add search arg to where object
|
||||
if (searchArg) {
|
||||
search.where.and.push({
|
||||
[fieldToSearch]: {
|
||||
like: searchArg,
|
||||
},
|
||||
})
|
||||
}
|
||||
// call the filterOptions function if it exists passing in the collection
|
||||
if (filterOptions) {
|
||||
const optionFilter =
|
||||
typeof filterOptions === 'function'
|
||||
? await filterOptions({
|
||||
// data and siblingData are empty since we cannot fetch with the values covering the
|
||||
// entire list this limitation means that filterOptions functions using a document's
|
||||
// data are unsupported in the whereBuilder
|
||||
id: undefined,
|
||||
data: {},
|
||||
relationTo: collection.slug,
|
||||
siblingData: {},
|
||||
user,
|
||||
})
|
||||
: filterOptions
|
||||
if (typeof optionFilter === 'object') {
|
||||
search.where.and.push(optionFilter)
|
||||
}
|
||||
if (optionFilter === false) {
|
||||
// no options will be returned
|
||||
setLastFullyLoadedRelation(relations.indexOf(relation))
|
||||
|
||||
// If there are more relations to search, need to reset lastLoadedPage to 1
|
||||
// both locally within function and state
|
||||
if (relations.indexOf(relation) + 1 < relations.length) {
|
||||
lastLoadedPageToUse = 1
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (search.where.and.length === 0) {
|
||||
delete search.where
|
||||
}
|
||||
|
||||
const response = await fetch(`${serverURL}${api}/${relation}?${qs.stringify(search)}`, {
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Accept-Language': i18n.language,
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
if (response.ok) {
|
||||
const data: PaginatedDocs = await response.json()
|
||||
@@ -103,7 +159,18 @@ const RelationshipField: React.FC<Props> = (props) => {
|
||||
}, Promise.resolve())
|
||||
}
|
||||
},
|
||||
[i18n, relationTo, errorLoading, collections, serverURL, api, addOptions, t],
|
||||
[
|
||||
relationTo,
|
||||
errorLoading,
|
||||
collections,
|
||||
filterOptions,
|
||||
serverURL,
|
||||
api,
|
||||
i18n.language,
|
||||
user,
|
||||
addOptions,
|
||||
t,
|
||||
],
|
||||
)
|
||||
|
||||
const findOptionsByValue = useCallback((): Option | Option[] => {
|
||||
|
||||
@@ -52,9 +52,9 @@ const Condition: React.FC<Props> = (props) => {
|
||||
|
||||
useEffect(() => {
|
||||
dispatch({
|
||||
type: 'update',
|
||||
andIndex,
|
||||
orIndex,
|
||||
type: 'update',
|
||||
value: debouncedValue || '',
|
||||
})
|
||||
}, [debouncedValue, dispatch, orIndex, andIndex])
|
||||
@@ -80,10 +80,10 @@ const Condition: React.FC<Props> = (props) => {
|
||||
isClearable={false}
|
||||
onChange={(field) => {
|
||||
dispatch({
|
||||
andIndex: andIndex,
|
||||
field: field?.value,
|
||||
orIndex: orIndex,
|
||||
type: 'update',
|
||||
andIndex,
|
||||
field: field?.value,
|
||||
orIndex,
|
||||
})
|
||||
}}
|
||||
options={fields}
|
||||
@@ -96,10 +96,10 @@ const Condition: React.FC<Props> = (props) => {
|
||||
isClearable={false}
|
||||
onChange={(operator) => {
|
||||
dispatch({
|
||||
type: 'update',
|
||||
andIndex,
|
||||
operator: operator.value,
|
||||
orIndex,
|
||||
type: 'update',
|
||||
})
|
||||
setInternalOperatorField(operator.value)
|
||||
}}
|
||||
@@ -134,9 +134,9 @@ const Condition: React.FC<Props> = (props) => {
|
||||
iconStyle="with-border"
|
||||
onClick={() =>
|
||||
dispatch({
|
||||
type: 'remove',
|
||||
andIndex,
|
||||
orIndex,
|
||||
type: 'remove',
|
||||
})
|
||||
}
|
||||
round
|
||||
@@ -148,11 +148,11 @@ const Condition: React.FC<Props> = (props) => {
|
||||
iconStyle="with-border"
|
||||
onClick={() =>
|
||||
dispatch({
|
||||
type: 'add',
|
||||
andIndex: andIndex + 1,
|
||||
field: fields[0].value,
|
||||
orIndex,
|
||||
relation: 'and',
|
||||
type: 'add',
|
||||
})
|
||||
}
|
||||
round
|
||||
|
||||
@@ -32,34 +32,38 @@ const reduceFields = (fields, i18n) =>
|
||||
} else {
|
||||
operators = fieldTypes[field.type].operators
|
||||
}
|
||||
}
|
||||
|
||||
const operatorKeys = new Set()
|
||||
const filteredOperators = operators.reduce((acc, operator) => {
|
||||
if (!operatorKeys.has(operator.value)) {
|
||||
operatorKeys.add(operator.value)
|
||||
return [
|
||||
...acc,
|
||||
{
|
||||
...operator,
|
||||
label: i18n.t(`operators:${operator.label}`),
|
||||
},
|
||||
]
|
||||
const operatorKeys = new Set()
|
||||
const filteredOperators = operators.reduce((acc, operator) => {
|
||||
if (!operatorKeys.has(operator.value)) {
|
||||
operatorKeys.add(operator.value)
|
||||
return [
|
||||
...acc,
|
||||
{
|
||||
...operator,
|
||||
label: i18n.t(`operators:${operator.label}`),
|
||||
},
|
||||
]
|
||||
}
|
||||
return acc
|
||||
}, [])
|
||||
|
||||
const formattedField = {
|
||||
label: getTranslation(field.label || field.name, i18n),
|
||||
value: field.name,
|
||||
...fieldTypes[field.type],
|
||||
operators: filteredOperators,
|
||||
props: {
|
||||
...field,
|
||||
},
|
||||
}
|
||||
return acc
|
||||
}, [])
|
||||
|
||||
const formattedField = {
|
||||
label: getTranslation(field.label || field.name, i18n),
|
||||
value: field.name,
|
||||
...fieldTypes[field.type],
|
||||
operators: filteredOperators,
|
||||
props: {
|
||||
...field,
|
||||
},
|
||||
if (field.admin?.disableListFilter) return reduced
|
||||
|
||||
return [...reduced, formattedField]
|
||||
}
|
||||
|
||||
return [...reduced, formattedField]
|
||||
return reduced
|
||||
}, [])
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,6 +26,7 @@ type ArrayRowProps = UseDraggableSortableReturn &
|
||||
duplicateRow: (rowIndex: number) => void
|
||||
forceRender?: boolean
|
||||
hasMaxRows?: boolean
|
||||
isSortable: boolean
|
||||
moveRow: (fromIndex: number, toIndex: number) => void
|
||||
readOnly?: boolean
|
||||
removeRow: (rowIndex: number) => void
|
||||
@@ -44,6 +45,7 @@ export const ArrayRow: React.FC<ArrayRowProps> = ({
|
||||
forceRender = false,
|
||||
hasMaxRows,
|
||||
indexPath,
|
||||
isSortable,
|
||||
labels,
|
||||
listeners,
|
||||
moveRow,
|
||||
@@ -94,6 +96,7 @@ export const ArrayRow: React.FC<ArrayRowProps> = ({
|
||||
duplicateRow={duplicateRow}
|
||||
hasMaxRows={hasMaxRows}
|
||||
index={rowIndex}
|
||||
isSortable={isSortable}
|
||||
moveRow={moveRow}
|
||||
removeRow={removeRow}
|
||||
rowCount={rowCount}
|
||||
@@ -103,11 +106,15 @@ export const ArrayRow: React.FC<ArrayRowProps> = ({
|
||||
className={classNames}
|
||||
collapsed={row.collapsed}
|
||||
collapsibleStyle={fieldHasErrors ? 'error' : 'default'}
|
||||
dragHandleProps={{
|
||||
id: row.id,
|
||||
attributes,
|
||||
listeners,
|
||||
}}
|
||||
dragHandleProps={
|
||||
isSortable
|
||||
? {
|
||||
id: row.id,
|
||||
attributes,
|
||||
listeners,
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
header={
|
||||
<div className={`${baseClass}__row-header`}>
|
||||
<RowLabel
|
||||
|
||||
@@ -29,7 +29,7 @@ const baseClass = 'array-field'
|
||||
const ArrayFieldType: React.FC<Props> = (props) => {
|
||||
const {
|
||||
name,
|
||||
admin: { className, components, condition, description, readOnly },
|
||||
admin: { className, components, condition, description, isSortable = true, readOnly },
|
||||
fieldTypes,
|
||||
fields,
|
||||
forceRender = false,
|
||||
@@ -113,7 +113,7 @@ const ArrayFieldType: React.FC<Props> = (props) => {
|
||||
|
||||
const duplicateRow = useCallback(
|
||||
(rowIndex: number) => {
|
||||
dispatchFields({ path, rowIndex, type: 'DUPLICATE_ROW' })
|
||||
dispatchFields({ type: 'DUPLICATE_ROW', path, rowIndex })
|
||||
setModified(true)
|
||||
|
||||
setTimeout(() => {
|
||||
@@ -133,7 +133,7 @@ const ArrayFieldType: React.FC<Props> = (props) => {
|
||||
|
||||
const moveRow = useCallback(
|
||||
(moveFromIndex: number, moveToIndex: number) => {
|
||||
dispatchFields({ moveFromIndex, moveToIndex, path, type: 'MOVE_ROW' })
|
||||
dispatchFields({ type: 'MOVE_ROW', moveFromIndex, moveToIndex, path })
|
||||
setModified(true)
|
||||
},
|
||||
[dispatchFields, path, setModified],
|
||||
@@ -141,14 +141,14 @@ const ArrayFieldType: React.FC<Props> = (props) => {
|
||||
|
||||
const toggleCollapseAll = useCallback(
|
||||
(collapsed: boolean) => {
|
||||
dispatchFields({ collapsed, path, setDocFieldPreferences, type: 'SET_ALL_ROWS_COLLAPSED' })
|
||||
dispatchFields({ type: 'SET_ALL_ROWS_COLLAPSED', collapsed, path, setDocFieldPreferences })
|
||||
},
|
||||
[dispatchFields, path, setDocFieldPreferences],
|
||||
)
|
||||
|
||||
const setCollapse = useCallback(
|
||||
(rowID: string, collapsed: boolean) => {
|
||||
dispatchFields({ collapsed, path, rowID, setDocFieldPreferences, type: 'SET_ROW_COLLAPSED' })
|
||||
dispatchFields({ type: 'SET_ROW_COLLAPSED', collapsed, path, rowID, setDocFieldPreferences })
|
||||
},
|
||||
[dispatchFields, path, setDocFieldPreferences],
|
||||
)
|
||||
@@ -227,7 +227,7 @@ const ArrayFieldType: React.FC<Props> = (props) => {
|
||||
onDragEnd={({ moveFromIndex, moveToIndex }) => moveRow(moveFromIndex, moveToIndex)}
|
||||
>
|
||||
{rows.map((row, i) => (
|
||||
<DraggableSortableItem disabled={readOnly} id={row.id} key={row.id}>
|
||||
<DraggableSortableItem disabled={readOnly || !isSortable} id={row.id} key={row.id}>
|
||||
{(draggableSortableItemProps) => (
|
||||
<ArrayRow
|
||||
{...draggableSortableItemProps}
|
||||
@@ -239,6 +239,7 @@ const ArrayFieldType: React.FC<Props> = (props) => {
|
||||
forceRender={forceRender}
|
||||
hasMaxRows={hasMaxRows}
|
||||
indexPath={indexPath}
|
||||
isSortable={isSortable}
|
||||
labels={labels}
|
||||
moveRow={moveRow}
|
||||
path={path}
|
||||
|
||||
@@ -26,6 +26,7 @@ type BlockFieldProps = UseDraggableSortableReturn &
|
||||
duplicateRow: (rowIndex: number) => void
|
||||
forceRender?: boolean
|
||||
hasMaxRows?: boolean
|
||||
isSortable?: boolean
|
||||
moveRow: (fromIndex: number, toIndex: number) => void
|
||||
readOnly: boolean
|
||||
removeRow: (rowIndex: number) => void
|
||||
@@ -44,6 +45,7 @@ export const BlockRow: React.FC<BlockFieldProps> = ({
|
||||
forceRender,
|
||||
hasMaxRows,
|
||||
indexPath,
|
||||
isSortable,
|
||||
labels,
|
||||
listeners,
|
||||
moveRow,
|
||||
@@ -90,6 +92,7 @@ export const BlockRow: React.FC<BlockFieldProps> = ({
|
||||
blocks={blocks}
|
||||
duplicateRow={duplicateRow}
|
||||
hasMaxRows={hasMaxRows}
|
||||
isSortable={isSortable}
|
||||
labels={labels}
|
||||
moveRow={moveRow}
|
||||
removeRow={removeRow}
|
||||
@@ -101,11 +104,15 @@ export const BlockRow: React.FC<BlockFieldProps> = ({
|
||||
className={classNames}
|
||||
collapsed={row.collapsed}
|
||||
collapsibleStyle={fieldHasErrors ? 'error' : 'default'}
|
||||
dragHandleProps={{
|
||||
id: row.id,
|
||||
attributes,
|
||||
listeners,
|
||||
}}
|
||||
dragHandleProps={
|
||||
isSortable
|
||||
? {
|
||||
id: row.id,
|
||||
attributes,
|
||||
listeners,
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
header={
|
||||
<div className={`${baseClass}__block-header`}>
|
||||
<span className={`${baseClass}__block-number`}>
|
||||
|
||||
@@ -13,6 +13,7 @@ export const RowActions: React.FC<{
|
||||
blocks: Block[]
|
||||
duplicateRow: (rowIndex: number, blockType: string) => void
|
||||
hasMaxRows?: boolean
|
||||
isSortable?: boolean
|
||||
labels: Labels
|
||||
moveRow: (fromIndex: number, toIndex: number) => void
|
||||
removeRow: (rowIndex: number) => void
|
||||
@@ -25,6 +26,7 @@ export const RowActions: React.FC<{
|
||||
blocks,
|
||||
duplicateRow,
|
||||
hasMaxRows,
|
||||
isSortable,
|
||||
labels,
|
||||
moveRow,
|
||||
removeRow,
|
||||
@@ -59,6 +61,7 @@ export const RowActions: React.FC<{
|
||||
duplicateRow={() => duplicateRow(rowIndex, blockType)}
|
||||
hasMaxRows={hasMaxRows}
|
||||
index={rowIndex}
|
||||
isSortable={isSortable}
|
||||
moveRow={moveRow}
|
||||
removeRow={removeRow}
|
||||
rowCount={rowCount}
|
||||
|
||||
@@ -34,7 +34,7 @@ const BlocksField: React.FC<Props> = (props) => {
|
||||
|
||||
const {
|
||||
name,
|
||||
admin: { className, condition, description, readOnly },
|
||||
admin: { className, condition, description, isSortable = true, readOnly },
|
||||
blocks,
|
||||
fieldTypes,
|
||||
forceRender = false,
|
||||
@@ -230,7 +230,7 @@ const BlocksField: React.FC<Props> = (props) => {
|
||||
|
||||
if (blockToRender) {
|
||||
return (
|
||||
<DraggableSortableItem disabled={readOnly} id={row.id} key={row.id}>
|
||||
<DraggableSortableItem disabled={readOnly || !isSortable} id={row.id} key={row.id}>
|
||||
{(draggableSortableItemProps) => (
|
||||
<BlockRow
|
||||
{...draggableSortableItemProps}
|
||||
@@ -242,6 +242,7 @@ const BlocksField: React.FC<Props> = (props) => {
|
||||
forceRender={forceRender}
|
||||
hasMaxRows={hasMaxRows}
|
||||
indexPath={indexPath}
|
||||
isSortable={isSortable}
|
||||
labels={labels}
|
||||
moveRow={moveRow}
|
||||
path={path}
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { Props } from './types'
|
||||
|
||||
import { checkbox } from '../../../../../fields/validations'
|
||||
import { getTranslation } from '../../../../../utilities/getTranslation'
|
||||
import { useEditDepth } from '../../../utilities/EditDepth'
|
||||
import DefaultError from '../../Error'
|
||||
import FieldDescription from '../../FieldDescription'
|
||||
import useField from '../../useField'
|
||||
@@ -41,6 +42,8 @@ const Checkbox: React.FC<Props> = (props) => {
|
||||
|
||||
const path = pathFromProps || name
|
||||
|
||||
const editDepth = useEditDepth()
|
||||
|
||||
const memoizedValidate = useCallback(
|
||||
(value, options) => {
|
||||
return validate(value, { ...options, required })
|
||||
@@ -62,7 +65,7 @@ const Checkbox: React.FC<Props> = (props) => {
|
||||
}
|
||||
}, [onChange, readOnly, setValue, value])
|
||||
|
||||
const fieldID = `field-${path.replace(/\./g, '__')}`
|
||||
const fieldID = `field-${path.replace(/\./g, '__')}${editDepth > 1 ? `-${editDepth}` : ''}`
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
@@ -98,6 +98,7 @@ const RadioGroupInput: React.FC<RadioGroupInputProps> = (props) => {
|
||||
onChange={readOnly ? undefined : onChange}
|
||||
option={optionIsObject(option) ? option : { label: option, value: option }}
|
||||
path={path}
|
||||
readOnly={readOnly}
|
||||
/>
|
||||
</li>
|
||||
)
|
||||
|
||||
@@ -70,31 +70,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.radio-group--read-only {
|
||||
.radio-input {
|
||||
cursor: default;
|
||||
|
||||
&__label {
|
||||
color: var(--theme-elevation-800);
|
||||
}
|
||||
|
||||
&--is-selected {
|
||||
.radio-input__styled-radio {
|
||||
&:before {
|
||||
background-color: var(--theme-elevation-800);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.radio-input--is-selected) {
|
||||
&:hover {
|
||||
.radio-input__styled-radio {
|
||||
&:before {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,23 +4,27 @@ import { useTranslation } from 'react-i18next'
|
||||
import type { Props } from './types'
|
||||
|
||||
import { getTranslation } from '../../../../../../utilities/getTranslation'
|
||||
import { useEditDepth } from '../../../../utilities/EditDepth'
|
||||
import './index.scss'
|
||||
|
||||
const baseClass = 'radio-input'
|
||||
|
||||
const RadioInput: React.FC<Props> = (props) => {
|
||||
const { isSelected, onChange, option, path } = props
|
||||
const { isSelected, onChange, option, path, readOnly } = props
|
||||
const { i18n } = useTranslation()
|
||||
|
||||
const editDepth = useEditDepth()
|
||||
|
||||
const classes = [baseClass, isSelected && `${baseClass}--is-selected`].filter(Boolean).join(' ')
|
||||
|
||||
const id = `field-${path}-${option.value}`
|
||||
const id = `field-${path}-${option.value}${editDepth > 1 ? `-${editDepth}` : ''}`
|
||||
|
||||
return (
|
||||
<label htmlFor={id}>
|
||||
<div className={classes}>
|
||||
<input
|
||||
checked={isSelected}
|
||||
disabled={readOnly}
|
||||
id={id}
|
||||
onChange={() => (typeof onChange === 'function' ? onChange(option.value) : null)}
|
||||
type="radio"
|
||||
|
||||
@@ -8,4 +8,5 @@ export type Props = {
|
||||
value: string
|
||||
}
|
||||
path: string
|
||||
readOnly?: boolean
|
||||
}
|
||||
|
||||
@@ -29,6 +29,34 @@
|
||||
}
|
||||
}
|
||||
|
||||
.radio-group--read-only {
|
||||
.radio-input {
|
||||
cursor: default;
|
||||
|
||||
&__label {
|
||||
color: var(--theme-elevation-400);
|
||||
}
|
||||
|
||||
&--is-selected {
|
||||
.radio-input__styled-radio {
|
||||
&:before {
|
||||
background-color: var(--theme-elevation-100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.radio-input--is-selected) {
|
||||
&:hover {
|
||||
.radio-input__styled-radio {
|
||||
&:before {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
html[data-theme='light'] {
|
||||
.radio-group {
|
||||
&.error {
|
||||
|
||||
@@ -55,6 +55,7 @@ const RadioGroup: React.FC<Props> = (props) => {
|
||||
onChange={readOnly ? undefined : setValue}
|
||||
options={options}
|
||||
path={path}
|
||||
readOnly={readOnly}
|
||||
required={required}
|
||||
showError={showError}
|
||||
style={style}
|
||||
|
||||
@@ -51,6 +51,7 @@ type RichTextAdapterBase<
|
||||
context: RequestContext
|
||||
currentDepth?: number
|
||||
depth: number
|
||||
draft: boolean
|
||||
field: RichTextField<Value, AdapterProps, ExtraFieldProperties>
|
||||
findMany: boolean
|
||||
flattenLocales: boolean
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import '../../../scss/app.scss'
|
||||
|
||||
const scriptLanguages = ['ar', 'fa']
|
||||
|
||||
export const LanguageWrap: React.FC<{ children?: React.ReactNode }> = ({ children }) => {
|
||||
const { i18n } = useTranslation()
|
||||
const currentLanguage = i18n?.language
|
||||
const isScriptLanguage = currentLanguage && scriptLanguages.includes(currentLanguage)
|
||||
|
||||
return <div className={isScriptLanguage ? `script-language` : ''}>{children}</div>
|
||||
}
|
||||
@@ -27,9 +27,9 @@ const chars = {
|
||||
const baseClass = 'query-inspector'
|
||||
|
||||
const Bracket = ({
|
||||
type,
|
||||
comma = false,
|
||||
position,
|
||||
type,
|
||||
}: {
|
||||
comma?: boolean
|
||||
position: 'end' | 'start'
|
||||
@@ -64,9 +64,9 @@ const RecursivelyRenderObjectData = ({
|
||||
const objectKeys = Object.keys(object)
|
||||
const objectLength = objectKeys.length
|
||||
const [isOpen, setIsOpen] = React.useState<boolean>(true)
|
||||
|
||||
const isNestedAndEmpty = isEmpty && (parentType === 'object' || parentType === 'array')
|
||||
return (
|
||||
<li>
|
||||
<li className={isNestedAndEmpty ? `${baseClass}__row-line--nested` : ''}>
|
||||
<button
|
||||
aria-label="toggle"
|
||||
className={`${baseClass}__list-toggle ${isEmpty ? `${baseClass}__list-toggle--empty` : ''}`}
|
||||
@@ -173,7 +173,7 @@ function createURL(url: string) {
|
||||
|
||||
export const API: React.FC<EditViewProps> = (props) => {
|
||||
const { apiURL } = props
|
||||
const { i18n } = useTranslation()
|
||||
const { i18n, t } = useTranslation()
|
||||
const {
|
||||
localization,
|
||||
routes: { api },
|
||||
@@ -262,14 +262,14 @@ export const API: React.FC<EditViewProps> = (props) => {
|
||||
<CheckboxInput
|
||||
checked={draft}
|
||||
id="draft-checkbox"
|
||||
label="Draft"
|
||||
label={t('version:draft')}
|
||||
onToggle={() => setDraft(!draft)}
|
||||
/>
|
||||
)}
|
||||
<CheckboxInput
|
||||
checked={authenticated}
|
||||
id="auth-checkbox"
|
||||
label="Authenticated"
|
||||
label={t('authentication:authenticated')}
|
||||
onToggle={() => setAuthenticated(!authenticated)}
|
||||
/>
|
||||
</div>
|
||||
@@ -280,7 +280,7 @@ export const API: React.FC<EditViewProps> = (props) => {
|
||||
label: locale,
|
||||
value: locale,
|
||||
}}
|
||||
label="Locale"
|
||||
label={t('general:locale')}
|
||||
name="locale"
|
||||
onChange={(e) => setLocale(e.value as string)}
|
||||
options={localeOptions}
|
||||
@@ -292,7 +292,7 @@ export const API: React.FC<EditViewProps> = (props) => {
|
||||
label: depth,
|
||||
value: depth,
|
||||
}}
|
||||
label="Depth"
|
||||
label={t('general:depth')}
|
||||
name="depth"
|
||||
onChange={(e) => setDepth(e.value as string)}
|
||||
options={[
|
||||
|
||||
@@ -66,7 +66,9 @@ const Restore: React.FC<Props> = ({
|
||||
if (res.status === 200) {
|
||||
const json = await res.json()
|
||||
toast.success(json.message)
|
||||
history.push(redirectURL)
|
||||
history.push(redirectURL, {
|
||||
refetchDocumentData: true,
|
||||
})
|
||||
} else {
|
||||
toast.error(t('problemRestoringVersion'))
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { Fragment } from 'react'
|
||||
import React, { Fragment, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import type { FieldTypes } from '../../../../forms/field-types'
|
||||
@@ -12,6 +12,7 @@ import Meta from '../../../../utilities/Meta'
|
||||
import Auth from '../Auth'
|
||||
import { SetStepNav } from '../SetStepNav'
|
||||
import { Upload } from '../Upload'
|
||||
import formatFields from '../formatFields'
|
||||
import './index.scss'
|
||||
|
||||
const baseClass = 'collection-default-edit'
|
||||
@@ -37,7 +38,9 @@ export const DefaultCollectionEdit: React.FC<
|
||||
permissions,
|
||||
} = props
|
||||
|
||||
const { auth, fields, upload } = collection
|
||||
const { auth, upload } = collection
|
||||
|
||||
const [fields] = useState(() => formatFields(collection, isEditing))
|
||||
|
||||
const operation = isEditing ? 'update' : 'create'
|
||||
|
||||
|
||||
@@ -50,7 +50,8 @@ const EditView: React.FC<IndexProps> = (props) => {
|
||||
} = config
|
||||
|
||||
const { params: { id } = {} } = useRouteMatch<Record<string, string>>()
|
||||
const history = useHistory()
|
||||
const history = useHistory<{ refetchDocumentData?: boolean }>()
|
||||
|
||||
const [internalState, setInternalState] = useState<Fields>()
|
||||
const [updatedAt, setUpdatedAt] = useState<string>()
|
||||
const { permissions, user } = useAuth()
|
||||
@@ -58,7 +59,7 @@ const EditView: React.FC<IndexProps> = (props) => {
|
||||
const { docPermissions, getDocPermissions, getDocPreferences, getVersions } = useDocumentInfo()
|
||||
const { t } = useTranslation('general')
|
||||
|
||||
const [{ data, isError, isLoading: isLoadingData }] = usePayloadAPI(
|
||||
const [{ data, isError, isLoading: isLoadingData }, { refetchData }] = usePayloadAPI(
|
||||
isEditing ? `${serverURL}${api}/${collectionSlug}/${id}` : '',
|
||||
{ initialData: null, initialParams: { depth: 0, draft: 'true', 'fallback-locale': 'null' } },
|
||||
)
|
||||
@@ -128,10 +129,16 @@ const EditView: React.FC<IndexProps> = (props) => {
|
||||
useEffect(() => {
|
||||
setFormQueryParams((params) => ({
|
||||
...params,
|
||||
locale: locale,
|
||||
locale,
|
||||
}))
|
||||
}, [locale])
|
||||
|
||||
useEffect(() => {
|
||||
if (history.location.state?.refetchDocumentData) {
|
||||
void refetchData()
|
||||
}
|
||||
}, [history.location.state?.refetchDocumentData, refetchData])
|
||||
|
||||
if (isError) {
|
||||
return <NotFound marginTop="large" />
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import type { CheckboxField } from '../../../../../../../../exports/types'
|
||||
import type { CellComponentProps } from '../../types'
|
||||
@@ -6,9 +7,13 @@ import type { CellComponentProps } from '../../types'
|
||||
import './index.scss'
|
||||
|
||||
// Handles boolean values
|
||||
const Checkbox: React.FC<CellComponentProps<CheckboxField>> = ({ data }) => (
|
||||
<code className="bool-cell">
|
||||
<span>{JSON.stringify(data)}</span>
|
||||
</code>
|
||||
)
|
||||
const Checkbox: React.FC<CellComponentProps<CheckboxField>> = ({ data }) => {
|
||||
const { t } = useTranslation('general')
|
||||
if (typeof data !== 'boolean') return null
|
||||
return (
|
||||
<code className="bool-cell">
|
||||
<span>{t(`${data}`).toLowerCase()}</span>
|
||||
</code>
|
||||
)
|
||||
}
|
||||
export default Checkbox
|
||||
|
||||
@@ -9,11 +9,11 @@ const formatFields = (config: SanitizedCollectionConfig): Field[] => {
|
||||
|
||||
const defaultIDField: Field = {
|
||||
name: 'id',
|
||||
type: 'text',
|
||||
admin: {
|
||||
disableBulkEdit: true,
|
||||
},
|
||||
label: 'ID',
|
||||
type: 'text',
|
||||
}
|
||||
|
||||
const shouldSkipField = (field: Field): boolean =>
|
||||
|
||||
@@ -47,13 +47,13 @@ const ListView: React.FC<ListIndexProps> = (props) => {
|
||||
const {
|
||||
collection,
|
||||
collection: {
|
||||
slug,
|
||||
admin: {
|
||||
components: { views: { List: CustomList } = {} } = {},
|
||||
listSearchableFields,
|
||||
pagination: { defaultLimit },
|
||||
},
|
||||
labels: { plural },
|
||||
slug,
|
||||
},
|
||||
} = props
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import queryString from 'qs'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import { requests } from '../api'
|
||||
@@ -12,6 +12,7 @@ type Result = [
|
||||
isLoading: boolean
|
||||
},
|
||||
{
|
||||
refetchData: (abortController?: AbortController) => Promise<void>
|
||||
setParams: React.Dispatch<unknown>
|
||||
},
|
||||
]
|
||||
@@ -43,49 +44,53 @@ const usePayloadAPI: UsePayloadAPI = (url, options = {}) => {
|
||||
},
|
||||
)
|
||||
|
||||
const fetchData = useCallback(
|
||||
async (abortController?: AbortController) => {
|
||||
if (url) {
|
||||
setIsError(false)
|
||||
setIsLoading(true)
|
||||
try {
|
||||
const response = await requests.get(`${url}${search}`, {
|
||||
headers: {
|
||||
'Accept-Language': i18n.language,
|
||||
},
|
||||
signal: abortController ? abortController.signal : undefined,
|
||||
})
|
||||
|
||||
if (response.status > 201) {
|
||||
setIsError(true)
|
||||
}
|
||||
|
||||
const json = await response.json()
|
||||
setData(json)
|
||||
setIsLoading(false)
|
||||
} catch (error) {
|
||||
if (!abortController || !abortController.signal.aborted) {
|
||||
setIsError(true)
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setIsError(false)
|
||||
setIsLoading(false)
|
||||
}
|
||||
},
|
||||
[url, search, i18n.language],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
const abortController = new AbortController()
|
||||
|
||||
const fetchData = async () => {
|
||||
setIsError(false)
|
||||
setIsLoading(true)
|
||||
|
||||
try {
|
||||
const response = await requests.get(`${url}${search}`, {
|
||||
headers: {
|
||||
'Accept-Language': i18n.language,
|
||||
},
|
||||
signal: abortController.signal,
|
||||
})
|
||||
|
||||
if (response.status > 201) {
|
||||
setIsError(true)
|
||||
}
|
||||
|
||||
const json = await response.json()
|
||||
setData(json)
|
||||
setIsLoading(false)
|
||||
} catch (error) {
|
||||
if (!abortController.signal.aborted) {
|
||||
setIsError(true)
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (url) {
|
||||
fetchData()
|
||||
} else {
|
||||
setIsError(false)
|
||||
setIsLoading(false)
|
||||
}
|
||||
void fetchData(abortController)
|
||||
|
||||
return () => {
|
||||
abortController.abort()
|
||||
}
|
||||
}, [url, locale, search, i18n.language])
|
||||
}, [url, search, fetchData])
|
||||
|
||||
return [{ data, isError, isLoading }, { setParams }]
|
||||
return [
|
||||
{ data, isError, isLoading },
|
||||
{ refetchData: fetchData, setParams },
|
||||
]
|
||||
}
|
||||
|
||||
export default usePayloadAPI
|
||||
|
||||
@@ -200,4 +200,11 @@ dialog {
|
||||
z-index: var(--z-modal);
|
||||
}
|
||||
|
||||
.script-language {
|
||||
& > *,
|
||||
& > * > * {
|
||||
letter-spacing: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
@import '~payload-user-css';
|
||||
|
||||
@@ -205,6 +205,7 @@ async function login<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
context: req.context,
|
||||
depth,
|
||||
doc: user,
|
||||
draft: undefined,
|
||||
fallbackLocale,
|
||||
global: null,
|
||||
locale,
|
||||
|
||||
@@ -54,6 +54,7 @@ const batchAndLoadDocs =
|
||||
fallbackLocale,
|
||||
overrideAccess,
|
||||
showHiddenFields,
|
||||
draft,
|
||||
] = JSON.parse(key)
|
||||
|
||||
const batchKeyArray = [
|
||||
@@ -65,6 +66,7 @@ const batchAndLoadDocs =
|
||||
fallbackLocale,
|
||||
overrideAccess,
|
||||
showHiddenFields,
|
||||
draft,
|
||||
]
|
||||
|
||||
const batchKey = JSON.stringify(batchKeyArray)
|
||||
@@ -100,6 +102,7 @@ const batchAndLoadDocs =
|
||||
fallbackLocale,
|
||||
overrideAccess,
|
||||
showHiddenFields,
|
||||
draft,
|
||||
] = JSON.parse(batchKey)
|
||||
|
||||
req.transactionID = transactionID
|
||||
@@ -109,6 +112,7 @@ const batchAndLoadDocs =
|
||||
currentDepth,
|
||||
depth,
|
||||
disableErrors: true,
|
||||
draft,
|
||||
fallbackLocale,
|
||||
locale,
|
||||
overrideAccess: Boolean(overrideAccess),
|
||||
@@ -136,6 +140,7 @@ const batchAndLoadDocs =
|
||||
fallbackLocale,
|
||||
overrideAccess,
|
||||
showHiddenFields,
|
||||
draft,
|
||||
])
|
||||
const docsIndex = keys.findIndex((key) => key === docKey)
|
||||
|
||||
|
||||
@@ -54,6 +54,8 @@ function initCollectionsGraphQL(payload: Payload): void {
|
||||
|
||||
if (!graphQL) return
|
||||
|
||||
const draftsEnabled = collection.config.versions?.drafts
|
||||
|
||||
let singularName
|
||||
let pluralName
|
||||
const fromSlug = formatNames(collection.config.slug)
|
||||
@@ -150,7 +152,11 @@ function initCollectionsGraphQL(payload: Payload): void {
|
||||
type: collection.graphQL.type,
|
||||
args: {
|
||||
id: { type: new GraphQLNonNull(idType) },
|
||||
draft: { type: GraphQLBoolean },
|
||||
...(draftsEnabled
|
||||
? {
|
||||
draft: { type: GraphQLBoolean },
|
||||
}
|
||||
: {}),
|
||||
...(payload.config.localization
|
||||
? {
|
||||
fallbackLocale: { type: payload.types.fallbackLocaleInputType },
|
||||
@@ -164,7 +170,11 @@ function initCollectionsGraphQL(payload: Payload): void {
|
||||
payload.Query.fields[pluralName] = {
|
||||
type: buildPaginatedListType(pluralName, collection.graphQL.type),
|
||||
args: {
|
||||
draft: { type: GraphQLBoolean },
|
||||
...(draftsEnabled
|
||||
? {
|
||||
draft: { type: GraphQLBoolean },
|
||||
}
|
||||
: {}),
|
||||
where: { type: collection.graphQL.whereInputType },
|
||||
...(payload.config.localization
|
||||
? {
|
||||
@@ -187,7 +197,11 @@ function initCollectionsGraphQL(payload: Payload): void {
|
||||
},
|
||||
}),
|
||||
args: {
|
||||
draft: { type: GraphQLBoolean },
|
||||
...(draftsEnabled
|
||||
? {
|
||||
draft: { type: GraphQLBoolean },
|
||||
}
|
||||
: {}),
|
||||
where: { type: collection.graphQL.whereInputType },
|
||||
...(payload.config.localization
|
||||
? {
|
||||
@@ -217,7 +231,11 @@ function initCollectionsGraphQL(payload: Payload): void {
|
||||
...(createMutationInputType
|
||||
? { data: { type: collection.graphQL.mutationInputType } }
|
||||
: {}),
|
||||
draft: { type: GraphQLBoolean },
|
||||
...(draftsEnabled
|
||||
? {
|
||||
draft: { type: GraphQLBoolean },
|
||||
}
|
||||
: {}),
|
||||
...(payload.config.localization
|
||||
? {
|
||||
locale: { type: payload.types.localeInputType },
|
||||
@@ -235,7 +253,11 @@ function initCollectionsGraphQL(payload: Payload): void {
|
||||
...(updateMutationInputType
|
||||
? { data: { type: collection.graphQL.updateMutationInputType } }
|
||||
: {}),
|
||||
draft: { type: GraphQLBoolean },
|
||||
...(draftsEnabled
|
||||
? {
|
||||
draft: { type: GraphQLBoolean },
|
||||
}
|
||||
: {}),
|
||||
...(payload.config.localization
|
||||
? {
|
||||
locale: { type: payload.types.localeInputType },
|
||||
|
||||
@@ -28,6 +28,8 @@ export default function findResolver(collection: Collection): Resolver {
|
||||
req.locale = args.locale || locale
|
||||
req.fallbackLocale = fallbackLocale
|
||||
|
||||
context.req = req
|
||||
|
||||
const options = {
|
||||
collection,
|
||||
req: isolateObjectProperty<PayloadRequest>(req, 'transactionID'),
|
||||
|
||||
@@ -33,6 +33,17 @@ export default function createResolver<TSlug extends keyof GeneratedTypes['colle
|
||||
const locale = req.locale
|
||||
req = isolateObjectProperty(req, 'locale')
|
||||
req.locale = args.locale || locale
|
||||
if (!req.query) req.query = {}
|
||||
|
||||
const draft: boolean =
|
||||
args.draft ?? req.query?.draft === 'false'
|
||||
? false
|
||||
: req.query?.draft === 'true'
|
||||
? true
|
||||
: undefined
|
||||
if (typeof draft === 'boolean') req.query.draft = String(draft)
|
||||
|
||||
context.req = req
|
||||
|
||||
const options = {
|
||||
collection,
|
||||
|
||||
@@ -31,6 +31,17 @@ export default function getDeleteResolver<TSlug extends keyof GeneratedTypes['co
|
||||
req = isolateObjectProperty(req, 'fallbackLocale')
|
||||
req.locale = args.locale || locale
|
||||
req.fallbackLocale = args.fallbackLocale || fallbackLocale
|
||||
if (!req.query) req.query = {}
|
||||
|
||||
const draft: boolean =
|
||||
args.draft ?? req.query?.draft === 'false'
|
||||
? false
|
||||
: req.query?.draft === 'true'
|
||||
? true
|
||||
: undefined
|
||||
if (typeof draft === 'boolean') req.query.draft = String(draft)
|
||||
|
||||
context.req = req
|
||||
|
||||
const options = {
|
||||
id: args.id,
|
||||
|
||||
@@ -35,6 +35,17 @@ export default function findResolver(collection: Collection): Resolver {
|
||||
req = isolateObjectProperty(req, 'fallbackLocale')
|
||||
req.locale = args.locale || locale
|
||||
req.fallbackLocale = args.fallbackLocale || fallbackLocale
|
||||
if (!req.query) req.query = {}
|
||||
|
||||
const draft: boolean =
|
||||
args.draft ?? req.query?.draft === 'false'
|
||||
? false
|
||||
: req.query?.draft === 'true'
|
||||
? true
|
||||
: undefined
|
||||
if (typeof draft === 'boolean') req.query.draft = String(draft)
|
||||
|
||||
context.req = req
|
||||
|
||||
const options = {
|
||||
collection,
|
||||
|
||||
@@ -30,6 +30,17 @@ export default function findByIDResolver<T extends keyof GeneratedTypes['collect
|
||||
req = isolateObjectProperty(req, 'fallbackLocale')
|
||||
req.locale = args.locale || locale
|
||||
req.fallbackLocale = args.fallbackLocale || fallbackLocale
|
||||
if (!req.query) req.query = {}
|
||||
|
||||
const draft: boolean =
|
||||
args.draft ?? req.query?.draft === 'false'
|
||||
? false
|
||||
: req.query?.draft === 'true'
|
||||
? true
|
||||
: undefined
|
||||
if (typeof draft === 'boolean') req.query.draft = String(draft)
|
||||
|
||||
context.req = req
|
||||
|
||||
const options = {
|
||||
id: args.id,
|
||||
|
||||
@@ -11,7 +11,6 @@ import findVersionByID from '../../operations/findVersionByID'
|
||||
export type Resolver<T extends TypeWithID = any> = (
|
||||
_: unknown,
|
||||
args: {
|
||||
draft: boolean
|
||||
fallbackLocale?: string
|
||||
id: number | string
|
||||
locale?: string
|
||||
@@ -32,11 +31,12 @@ export default function findVersionByIDResolver(collection: Collection): Resolve
|
||||
req.locale = args.locale || locale
|
||||
req.fallbackLocale = args.fallbackLocale || fallbackLocale
|
||||
|
||||
context.req = req
|
||||
|
||||
const options = {
|
||||
id: args.id,
|
||||
collection,
|
||||
depth: 0,
|
||||
draft: args.draft,
|
||||
req: isolateObjectProperty<PayloadRequest>(req, 'transactionID'),
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,17 @@ export default function findVersionsResolver(collection: Collection): Resolver {
|
||||
req = isolateObjectProperty(req, 'fallbackLocale')
|
||||
req.locale = args.locale || locale
|
||||
req.fallbackLocale = args.fallbackLocale || fallbackLocale
|
||||
if (!req.query) req.query = {}
|
||||
|
||||
const draft: boolean =
|
||||
args.draft ?? req.query?.draft === 'false'
|
||||
? false
|
||||
: req.query?.draft === 'true'
|
||||
? true
|
||||
: undefined
|
||||
if (typeof draft === 'boolean') req.query.draft = String(draft)
|
||||
|
||||
context.req = req
|
||||
|
||||
const options = {
|
||||
collection,
|
||||
|
||||
@@ -34,6 +34,17 @@ export default function updateResolver<TSlug extends keyof GeneratedTypes['colle
|
||||
req = isolateObjectProperty(req, 'fallbackLocale')
|
||||
req.locale = args.locale || locale
|
||||
req.fallbackLocale = args.fallbackLocale || fallbackLocale
|
||||
if (!req.query) req.query = {}
|
||||
|
||||
const draft: boolean =
|
||||
args.draft ?? req.query?.draft === 'false'
|
||||
? false
|
||||
: req.query?.draft === 'true'
|
||||
? true
|
||||
: undefined
|
||||
if (typeof draft === 'boolean') req.query.draft = String(draft)
|
||||
|
||||
context.req = req
|
||||
|
||||
const options = {
|
||||
id: args.id,
|
||||
|
||||
@@ -130,6 +130,7 @@ async function create<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
collection,
|
||||
config,
|
||||
data,
|
||||
operation: 'create',
|
||||
overwriteExistingFiles,
|
||||
req,
|
||||
throwOnMissingFile:
|
||||
@@ -290,6 +291,7 @@ async function create<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
context: req.context,
|
||||
depth,
|
||||
doc: result,
|
||||
draft,
|
||||
fallbackLocale,
|
||||
global: null,
|
||||
locale,
|
||||
|
||||
@@ -179,6 +179,7 @@ async function deleteOperation<TSlug extends keyof GeneratedTypes['collections']
|
||||
context: req.context,
|
||||
depth,
|
||||
doc: result || doc,
|
||||
draft: undefined,
|
||||
fallbackLocale,
|
||||
global: null,
|
||||
locale,
|
||||
|
||||
@@ -158,6 +158,7 @@ async function deleteByID<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
context: req.context,
|
||||
depth,
|
||||
doc: result,
|
||||
draft: undefined,
|
||||
fallbackLocale,
|
||||
global: null,
|
||||
locale,
|
||||
|
||||
@@ -196,6 +196,7 @@ async function find<T extends TypeWithID & Record<string, unknown>>(
|
||||
currentDepth,
|
||||
depth,
|
||||
doc,
|
||||
draft: draftsEnabled,
|
||||
fallbackLocale,
|
||||
findMany: true,
|
||||
global: null,
|
||||
|
||||
@@ -139,6 +139,7 @@ async function findByID<T extends TypeWithID>(incomingArgs: Arguments): Promise<
|
||||
currentDepth,
|
||||
depth,
|
||||
doc: result,
|
||||
draft: draftEnabled,
|
||||
fallbackLocale,
|
||||
global: null,
|
||||
locale,
|
||||
|
||||
@@ -112,6 +112,7 @@ async function findVersionByID<T extends TypeWithID = any>(
|
||||
currentDepth,
|
||||
depth,
|
||||
doc: result.version,
|
||||
draft: undefined,
|
||||
fallbackLocale,
|
||||
global: null,
|
||||
locale,
|
||||
|
||||
@@ -125,6 +125,7 @@ async function findVersions<T extends TypeWithVersion<T>>(
|
||||
context: req.context,
|
||||
depth,
|
||||
doc: data.version,
|
||||
draft: undefined,
|
||||
fallbackLocale,
|
||||
findMany: true,
|
||||
global: null,
|
||||
|
||||
@@ -140,6 +140,7 @@ async function restoreVersion<T extends TypeWithID = any>(args: Arguments): Prom
|
||||
context: req.context,
|
||||
depth,
|
||||
doc: result,
|
||||
draft: undefined,
|
||||
fallbackLocale,
|
||||
global: null,
|
||||
locale,
|
||||
|
||||
@@ -157,6 +157,7 @@ async function update<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
collection,
|
||||
config,
|
||||
data: bulkUpdateData,
|
||||
operation: 'update',
|
||||
overwriteExistingFiles,
|
||||
req,
|
||||
throwOnMissingFile: false,
|
||||
@@ -177,6 +178,7 @@ async function update<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
context: req.context,
|
||||
depth: 0,
|
||||
doc,
|
||||
draft: draftArg,
|
||||
fallbackLocale,
|
||||
global: null,
|
||||
locale,
|
||||
@@ -312,6 +314,7 @@ async function update<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
context: req.context,
|
||||
depth,
|
||||
doc: result,
|
||||
draft: draftArg,
|
||||
fallbackLocale: null,
|
||||
global: null,
|
||||
locale,
|
||||
|
||||
@@ -131,6 +131,7 @@ async function updateByID<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
context: req.context,
|
||||
depth: 0,
|
||||
doc: docWithLocales,
|
||||
draft: draftArg,
|
||||
fallbackLocale: null,
|
||||
global: null,
|
||||
locale,
|
||||
@@ -147,6 +148,8 @@ async function updateByID<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
collection,
|
||||
config,
|
||||
data,
|
||||
operation: 'update',
|
||||
originalDoc,
|
||||
overwriteExistingFiles,
|
||||
req,
|
||||
throwOnMissingFile: false,
|
||||
@@ -300,6 +303,7 @@ async function updateByID<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
context: req.context,
|
||||
depth,
|
||||
doc: result,
|
||||
draft: draftArg,
|
||||
fallbackLocale,
|
||||
global: null,
|
||||
locale,
|
||||
|
||||
@@ -68,7 +68,6 @@ export type {
|
||||
FieldWithMany,
|
||||
FieldWithMaxDepth,
|
||||
FieldWithPath,
|
||||
FieldWithRichTextRequiredEditor,
|
||||
FieldWithSubFields,
|
||||
FilterOptions,
|
||||
FilterOptionsProps,
|
||||
@@ -87,7 +86,6 @@ export type {
|
||||
RelationshipField,
|
||||
RelationshipValue,
|
||||
RichTextField,
|
||||
RichTextFieldRequiredEditor,
|
||||
RowAdmin,
|
||||
RowField,
|
||||
SelectField,
|
||||
|
||||
@@ -19,6 +19,8 @@ export const baseAdminFields = joi.object().keys({
|
||||
.alternatives()
|
||||
.try(joi.string(), joi.object().pattern(joi.string(), [joi.string()]), componentSchema),
|
||||
disableBulkEdit: joi.boolean().default(false),
|
||||
disableListColumn: joi.boolean().default(false),
|
||||
disableListFilter: joi.boolean().default(false),
|
||||
disabled: joi.boolean().default(false),
|
||||
hidden: joi.boolean().default(false),
|
||||
initCollapsed: joi.boolean().default(false),
|
||||
@@ -311,6 +313,7 @@ export const array = baseField.keys({
|
||||
RowLabel: componentSchema,
|
||||
})
|
||||
.default({}),
|
||||
isSortable: joi.boolean(),
|
||||
})
|
||||
.default({}),
|
||||
dbName: joi.alternatives().try(joi.string(), joi.func()),
|
||||
@@ -408,6 +411,11 @@ export const relationship = baseField.keys({
|
||||
export const blocks = baseField.keys({
|
||||
name: joi.string().required(),
|
||||
type: joi.string().valid('blocks').required(),
|
||||
admin: baseAdminFields
|
||||
.keys({
|
||||
isSortable: joi.boolean(),
|
||||
})
|
||||
.default({}),
|
||||
blocks: joi
|
||||
.array()
|
||||
.items(
|
||||
@@ -469,6 +477,7 @@ export const richText = baseField.keys({
|
||||
validate: joi.func().required(),
|
||||
})
|
||||
.unknown(),
|
||||
maxDepth: joi.number(),
|
||||
})
|
||||
|
||||
export const date = baseField.keys({
|
||||
|
||||
@@ -113,6 +113,8 @@ type Admin = {
|
||||
condition?: Condition
|
||||
description?: Description
|
||||
disableBulkEdit?: boolean
|
||||
disableListColumn?: boolean
|
||||
disableListFilter?: boolean
|
||||
disabled?: boolean
|
||||
hidden?: boolean
|
||||
position?: 'sidebar'
|
||||
@@ -387,6 +389,8 @@ export type UIField = {
|
||||
}
|
||||
condition?: Condition
|
||||
disableBulkEdit?: boolean
|
||||
disableListColumn?: boolean
|
||||
disableListFilter?: boolean
|
||||
position?: string
|
||||
width?: string
|
||||
}
|
||||
@@ -547,23 +551,23 @@ export type RichTextField<
|
||||
}
|
||||
}
|
||||
editor?: RichTextAdapter<Value, AdapterProps, AdapterProps>
|
||||
/**
|
||||
* Sets a maximum population depth for this field, regardless of the remaining depth when this field is reached.
|
||||
*/
|
||||
maxDepth?: number
|
||||
type: 'richText'
|
||||
} & ExtraProperties
|
||||
|
||||
export type RichTextFieldRequiredEditor<
|
||||
Value extends object = any,
|
||||
AdapterProps = any,
|
||||
ExtraProperties = object,
|
||||
> = Omit<RichTextField<Value, AdapterProps, ExtraProperties>, 'editor'> & {
|
||||
editor: RichTextAdapter<Value, AdapterProps, ExtraProperties>
|
||||
}
|
||||
|
||||
export type ArrayField = FieldBase & {
|
||||
admin?: Admin & {
|
||||
components?: {
|
||||
RowLabel?: RowLabel
|
||||
} & Admin['components']
|
||||
initCollapsed?: boolean | false
|
||||
/**
|
||||
* Disable drag and drop sorting
|
||||
*/
|
||||
isSortable?: boolean
|
||||
}
|
||||
/**
|
||||
* Customize the SQL table name
|
||||
@@ -631,6 +635,10 @@ export type Block = {
|
||||
export type BlockField = FieldBase & {
|
||||
admin?: Admin & {
|
||||
initCollapsed?: boolean | false
|
||||
/**
|
||||
* Disable drag and drop sorting
|
||||
*/
|
||||
isSortable?: boolean
|
||||
}
|
||||
blocks: Block[]
|
||||
defaultValue?: unknown
|
||||
@@ -667,10 +675,6 @@ export type Field =
|
||||
| UIField
|
||||
| UploadField
|
||||
|
||||
export type FieldWithRichTextRequiredEditor =
|
||||
| Exclude<Field, RichTextField>
|
||||
| RichTextFieldRequiredEditor
|
||||
|
||||
export type FieldAffectingData =
|
||||
| ArrayField
|
||||
| BlockField
|
||||
|
||||
@@ -11,6 +11,7 @@ type Args = {
|
||||
currentDepth?: number
|
||||
depth: number
|
||||
doc: Record<string, unknown>
|
||||
draft: boolean
|
||||
fallbackLocale: null | string
|
||||
findMany?: boolean
|
||||
flattenLocales?: boolean
|
||||
@@ -28,6 +29,7 @@ export async function afterRead<T = any>(args: Args): Promise<T> {
|
||||
currentDepth: incomingCurrentDepth,
|
||||
depth: incomingDepth,
|
||||
doc: incomingDoc,
|
||||
draft,
|
||||
fallbackLocale,
|
||||
findMany,
|
||||
flattenLocales = true,
|
||||
@@ -56,6 +58,7 @@ export async function afterRead<T = any>(args: Args): Promise<T> {
|
||||
currentDepth,
|
||||
depth,
|
||||
doc,
|
||||
draft,
|
||||
fallbackLocale,
|
||||
fieldPromises,
|
||||
fields: collection?.fields || global?.fields,
|
||||
|
||||
@@ -16,6 +16,7 @@ type Args = {
|
||||
currentDepth: number
|
||||
depth: number
|
||||
doc: Record<string, unknown>
|
||||
draft: boolean
|
||||
fallbackLocale: null | string
|
||||
field: Field | TabAsField
|
||||
fieldPromises: Promise<void>[]
|
||||
@@ -46,6 +47,7 @@ export const promise = async ({
|
||||
currentDepth,
|
||||
depth,
|
||||
doc,
|
||||
draft,
|
||||
fallbackLocale,
|
||||
field,
|
||||
fieldPromises,
|
||||
@@ -141,10 +143,14 @@ export const promise = async ({
|
||||
const editor: RichTextAdapter = field?.editor
|
||||
// This is run here AND in the GraphQL Resolver
|
||||
if (editor?.populationPromise) {
|
||||
const populateDepth =
|
||||
field?.maxDepth !== undefined && field?.maxDepth < depth ? field?.maxDepth : depth
|
||||
|
||||
const populationPromise = editor.populationPromise({
|
||||
context,
|
||||
currentDepth,
|
||||
depth,
|
||||
depth: populateDepth,
|
||||
draft,
|
||||
field,
|
||||
findMany,
|
||||
flattenLocales,
|
||||
@@ -287,6 +293,7 @@ export const promise = async ({
|
||||
relationshipPopulationPromise({
|
||||
currentDepth,
|
||||
depth,
|
||||
draft,
|
||||
fallbackLocale,
|
||||
field,
|
||||
locale,
|
||||
@@ -310,6 +317,7 @@ export const promise = async ({
|
||||
currentDepth,
|
||||
depth,
|
||||
doc,
|
||||
draft,
|
||||
fallbackLocale,
|
||||
fieldPromises,
|
||||
fields: field.fields,
|
||||
@@ -340,6 +348,7 @@ export const promise = async ({
|
||||
currentDepth,
|
||||
depth,
|
||||
doc,
|
||||
draft,
|
||||
fallbackLocale,
|
||||
fieldPromises,
|
||||
fields: field.fields,
|
||||
@@ -366,6 +375,7 @@ export const promise = async ({
|
||||
currentDepth,
|
||||
depth,
|
||||
doc,
|
||||
draft,
|
||||
fallbackLocale,
|
||||
fieldPromises,
|
||||
fields: field.fields,
|
||||
@@ -404,6 +414,7 @@ export const promise = async ({
|
||||
currentDepth,
|
||||
depth,
|
||||
doc,
|
||||
draft,
|
||||
fallbackLocale,
|
||||
fieldPromises,
|
||||
fields: block.fields,
|
||||
@@ -434,6 +445,7 @@ export const promise = async ({
|
||||
currentDepth,
|
||||
depth,
|
||||
doc,
|
||||
draft,
|
||||
fallbackLocale,
|
||||
fieldPromises,
|
||||
fields: block.fields,
|
||||
@@ -468,6 +480,7 @@ export const promise = async ({
|
||||
currentDepth,
|
||||
depth,
|
||||
doc,
|
||||
draft,
|
||||
fallbackLocale,
|
||||
fieldPromises,
|
||||
fields: field.fields,
|
||||
@@ -500,6 +513,7 @@ export const promise = async ({
|
||||
currentDepth,
|
||||
depth,
|
||||
doc,
|
||||
draft,
|
||||
fallbackLocale,
|
||||
fieldPromises,
|
||||
fields: field.fields,
|
||||
@@ -526,6 +540,7 @@ export const promise = async ({
|
||||
currentDepth,
|
||||
depth,
|
||||
doc,
|
||||
draft,
|
||||
fallbackLocale,
|
||||
fieldPromises,
|
||||
fields: field.tabs.map((tab) => ({ ...tab, type: 'tab' })),
|
||||
|
||||
@@ -8,6 +8,7 @@ type PopulateArgs = {
|
||||
data: Record<string, unknown>
|
||||
dataReference: Record<string, any>
|
||||
depth: number
|
||||
draft: boolean
|
||||
fallbackLocale: null | string
|
||||
field: RelationshipField | UploadField
|
||||
index?: number
|
||||
@@ -23,6 +24,7 @@ const populate = async ({
|
||||
data,
|
||||
dataReference,
|
||||
depth,
|
||||
draft = false,
|
||||
fallbackLocale,
|
||||
field,
|
||||
index,
|
||||
@@ -62,6 +64,7 @@ const populate = async ({
|
||||
fallbackLocale,
|
||||
overrideAccess,
|
||||
showHiddenFields,
|
||||
draft,
|
||||
]),
|
||||
)
|
||||
}
|
||||
@@ -94,6 +97,7 @@ const populate = async ({
|
||||
type PromiseArgs = {
|
||||
currentDepth: number
|
||||
depth: number
|
||||
draft: boolean
|
||||
fallbackLocale: null | string
|
||||
field: RelationshipField | UploadField
|
||||
locale: null | string
|
||||
@@ -106,6 +110,7 @@ type PromiseArgs = {
|
||||
const relationshipPopulationPromise = async ({
|
||||
currentDepth,
|
||||
depth,
|
||||
draft,
|
||||
fallbackLocale,
|
||||
field,
|
||||
locale,
|
||||
@@ -133,6 +138,7 @@ const relationshipPopulationPromise = async ({
|
||||
data: siblingDoc[field.name][key][index],
|
||||
dataReference: resultingDoc,
|
||||
depth: populateDepth,
|
||||
draft,
|
||||
fallbackLocale,
|
||||
field,
|
||||
index,
|
||||
@@ -156,6 +162,7 @@ const relationshipPopulationPromise = async ({
|
||||
data: relatedDoc,
|
||||
dataReference: resultingDoc,
|
||||
depth: populateDepth,
|
||||
draft,
|
||||
fallbackLocale,
|
||||
field,
|
||||
index,
|
||||
@@ -182,6 +189,7 @@ const relationshipPopulationPromise = async ({
|
||||
data: siblingDoc[field.name][key],
|
||||
dataReference: resultingDoc,
|
||||
depth: populateDepth,
|
||||
draft,
|
||||
fallbackLocale,
|
||||
field,
|
||||
key,
|
||||
@@ -201,6 +209,7 @@ const relationshipPopulationPromise = async ({
|
||||
data: siblingDoc[field.name],
|
||||
dataReference: resultingDoc,
|
||||
depth: populateDepth,
|
||||
draft,
|
||||
fallbackLocale,
|
||||
field,
|
||||
locale,
|
||||
|
||||
@@ -11,6 +11,7 @@ type Args = {
|
||||
currentDepth: number
|
||||
depth: number
|
||||
doc: Record<string, unknown>
|
||||
draft: boolean
|
||||
fallbackLocale: null | string
|
||||
fieldPromises: Promise<void>[]
|
||||
fields: (Field | TabAsField)[]
|
||||
@@ -33,6 +34,7 @@ export const traverseFields = ({
|
||||
currentDepth,
|
||||
depth,
|
||||
doc,
|
||||
draft,
|
||||
fallbackLocale,
|
||||
fieldPromises,
|
||||
fields,
|
||||
@@ -56,6 +58,7 @@ export const traverseFields = ({
|
||||
currentDepth,
|
||||
depth,
|
||||
doc,
|
||||
draft,
|
||||
fallbackLocale,
|
||||
field,
|
||||
fieldPromises,
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import Ajv from 'ajv'
|
||||
import ObjectID from 'bson-objectid'
|
||||
|
||||
import type { RichTextAdapter } from '../exports/types'
|
||||
import type {
|
||||
ArrayField,
|
||||
@@ -159,7 +161,7 @@ export const json: Validate<unknown, unknown, JSONField & { jsonError?: string }
|
||||
return true
|
||||
}
|
||||
|
||||
const fetchSchema = ({ uri, schema }) => {
|
||||
const fetchSchema = ({ schema, uri }) => {
|
||||
if (uri && schema) return schema
|
||||
return fetch(uri)
|
||||
.then((response) => {
|
||||
@@ -344,8 +346,12 @@ const validateFilterOptions: Validate = async (
|
||||
const valueIDs: (number | string)[] = []
|
||||
|
||||
values.forEach((val) => {
|
||||
if (typeof val === 'object' && val?.value) {
|
||||
valueIDs.push(val.value)
|
||||
if (typeof val === 'object') {
|
||||
if (val?.value) {
|
||||
valueIDs.push(val.value)
|
||||
} else if (ObjectID.isValid(val)) {
|
||||
valueIDs.push(new ObjectID(val).toHexString())
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof val === 'string' || typeof val === 'number') {
|
||||
@@ -397,6 +403,10 @@ const validateFilterOptions: Validate = async (
|
||||
if (typeof val === 'string' || typeof val === 'number') {
|
||||
requestedID = val
|
||||
}
|
||||
|
||||
if (typeof val === 'object' && ObjectID.isValid(val)) {
|
||||
requestedID = new ObjectID(val).toHexString()
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(relationTo) && typeof val === 'object' && val?.relationTo) {
|
||||
|
||||
@@ -100,6 +100,7 @@ async function findOne<T extends Record<string, unknown>>(args: Args): Promise<T
|
||||
context: req.context,
|
||||
depth,
|
||||
doc,
|
||||
draft: draftEnabled,
|
||||
fallbackLocale,
|
||||
global: globalConfig,
|
||||
locale,
|
||||
|
||||
@@ -108,6 +108,7 @@ async function findVersionByID<T extends TypeWithVersion<T> = any>(args: Argumen
|
||||
currentDepth,
|
||||
depth,
|
||||
doc: result.version,
|
||||
draft: undefined,
|
||||
fallbackLocale,
|
||||
global: globalConfig,
|
||||
locale,
|
||||
|
||||
@@ -97,6 +97,7 @@ async function findVersions<T extends TypeWithVersion<T>>(
|
||||
// Patch globalType onto version doc
|
||||
globalType: globalConfig.slug,
|
||||
},
|
||||
draft: undefined,
|
||||
fallbackLocale,
|
||||
findMany: true,
|
||||
global: globalConfig,
|
||||
|
||||
@@ -105,6 +105,7 @@ async function restoreVersion<T extends TypeWithVersion<T> = any>(args: Argument
|
||||
context: req.context,
|
||||
depth,
|
||||
doc: result,
|
||||
draft: undefined,
|
||||
fallbackLocale,
|
||||
global: globalConfig,
|
||||
locale,
|
||||
|
||||
@@ -97,6 +97,7 @@ async function update<TSlug extends keyof GeneratedTypes['globals']>(
|
||||
context: req.context,
|
||||
depth: 0,
|
||||
doc: globalJSON,
|
||||
draft: draftArg,
|
||||
fallbackLocale,
|
||||
global: globalConfig,
|
||||
locale,
|
||||
@@ -220,6 +221,7 @@ async function update<TSlug extends keyof GeneratedTypes['globals']>(
|
||||
context: req.context,
|
||||
depth,
|
||||
doc: result,
|
||||
draft: draftArg,
|
||||
fallbackLocale: null,
|
||||
global: globalConfig,
|
||||
locale,
|
||||
|
||||
@@ -312,6 +312,7 @@ function buildObjectType({
|
||||
type = type || newlyCreatedBlockType
|
||||
|
||||
const relationshipArgs: {
|
||||
draft?: unknown
|
||||
fallbackLocale?: unknown
|
||||
limit?: unknown
|
||||
locale?: unknown
|
||||
@@ -319,6 +320,16 @@ function buildObjectType({
|
||||
where?: unknown
|
||||
} = {}
|
||||
|
||||
const relationsUseDrafts = (Array.isArray(relationTo) ? relationTo : [relationTo]).some(
|
||||
(relation) => payload.collections[relation].config.versions?.drafts,
|
||||
)
|
||||
|
||||
if (relationsUseDrafts) {
|
||||
relationshipArgs.draft = {
|
||||
type: GraphQLBoolean,
|
||||
}
|
||||
}
|
||||
|
||||
if (payload.config.localization) {
|
||||
relationshipArgs.locale = {
|
||||
type: payload.types.localeInputType,
|
||||
@@ -330,6 +341,11 @@ function buildObjectType({
|
||||
}
|
||||
|
||||
const relationship = {
|
||||
type: withNullableType(
|
||||
field,
|
||||
hasManyValues ? new GraphQLList(new GraphQLNonNull(type)) : type,
|
||||
forceNullable,
|
||||
),
|
||||
args: relationshipArgs,
|
||||
extensions: { complexity: 10 },
|
||||
async resolve(parent, args, context) {
|
||||
@@ -337,6 +353,7 @@ function buildObjectType({
|
||||
const locale = args.locale || context.req.locale
|
||||
const fallbackLocale = args.fallbackLocale || context.req.fallbackLocale
|
||||
let relatedCollectionSlug = field.relationTo
|
||||
const draft = args.draft ?? context.req.query?.draft
|
||||
|
||||
if (hasManyValues) {
|
||||
const results = []
|
||||
@@ -362,6 +379,7 @@ function buildObjectType({
|
||||
fallbackLocale,
|
||||
false,
|
||||
false,
|
||||
draft,
|
||||
]),
|
||||
)
|
||||
|
||||
@@ -408,6 +426,7 @@ function buildObjectType({
|
||||
fallbackLocale,
|
||||
false,
|
||||
false,
|
||||
draft,
|
||||
]),
|
||||
)
|
||||
|
||||
@@ -430,11 +449,6 @@ function buildObjectType({
|
||||
|
||||
return null
|
||||
},
|
||||
type: withNullableType(
|
||||
field,
|
||||
hasManyValues ? new GraphQLList(new GraphQLNonNull(type)) : type,
|
||||
forceNullable,
|
||||
),
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -445,6 +459,7 @@ function buildObjectType({
|
||||
richText: (objectTypeConfig: ObjectTypeConfig, field: RichTextField) => ({
|
||||
...objectTypeConfig,
|
||||
[field.name]: {
|
||||
type: withNullableType(field, GraphQLJSON, forceNullable),
|
||||
args: {
|
||||
depth: {
|
||||
type: GraphQLInt,
|
||||
@@ -461,9 +476,13 @@ function buildObjectType({
|
||||
// In the graphql find.ts resolver, the depth is then hard-coded to 0.
|
||||
// Effectively, this means that the populationPromise for GraphQL is only run here, and not in the find.ts resolver / normal population promise.
|
||||
if (editor?.populationPromise) {
|
||||
const populateDepth =
|
||||
field?.maxDepth !== undefined && field?.maxDepth < depth ? field?.maxDepth : depth
|
||||
|
||||
await editor?.populationPromise({
|
||||
context,
|
||||
depth,
|
||||
depth: populateDepth,
|
||||
draft: args.draft,
|
||||
field,
|
||||
findMany: false,
|
||||
flattenLocales: false,
|
||||
@@ -477,7 +496,6 @@ function buildObjectType({
|
||||
|
||||
return parent[field.name]
|
||||
},
|
||||
type: withNullableType(field, GraphQLJSON, forceNullable),
|
||||
},
|
||||
}),
|
||||
row: (objectTypeConfig: ObjectTypeConfig, field: RowField) =>
|
||||
@@ -586,6 +604,7 @@ function buildObjectType({
|
||||
const relatedCollectionSlug = field.relationTo
|
||||
|
||||
const upload = {
|
||||
type,
|
||||
args: uploadArgs,
|
||||
extensions: { complexity: 20 },
|
||||
async resolve(parent, args, context) {
|
||||
@@ -593,6 +612,7 @@ function buildObjectType({
|
||||
const locale = args.locale || context.req.locale
|
||||
const fallbackLocale = args.fallbackLocale || context.req.fallbackLocale
|
||||
const id = value
|
||||
const draft = args.draft ?? context.req.query?.draft
|
||||
|
||||
if (id) {
|
||||
const relatedDocument = await context.req.payloadDataLoader.load(
|
||||
@@ -606,6 +626,7 @@ function buildObjectType({
|
||||
fallbackLocale,
|
||||
false,
|
||||
false,
|
||||
Boolean(draft),
|
||||
]),
|
||||
)
|
||||
|
||||
@@ -614,7 +635,6 @@ function buildObjectType({
|
||||
|
||||
return null
|
||||
},
|
||||
type,
|
||||
}
|
||||
|
||||
const whereFields = payload.collections[relationTo].config.fields
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"alreadyActivated": "تمّ التّفعيل بالفعل",
|
||||
"alreadyLoggedIn": "تمّ تسجيل الدّخول بالفعل",
|
||||
"apiKey": "مفتاح API",
|
||||
"authenticated": "مصادق عليه",
|
||||
"backToLogin": "العودة لتسجيل الدخول",
|
||||
"beginCreateFirstUser": "للبدء, قم بإنشاء المستخدم الأوّل.",
|
||||
"changePassword": "تغيير كلمة المرور",
|
||||
@@ -177,6 +178,7 @@
|
||||
"deletedCountSuccessfully": "تمّ حذف {{count}} {{label}} بنجاح.",
|
||||
"deletedSuccessfully": "تمّ الحذف بنجاح.",
|
||||
"deleting": "يتمّ الحذف...",
|
||||
"depth": "عمق",
|
||||
"descending": "تنازلي",
|
||||
"deselectAllRows": "إلغاء تحديد جميع الصفوف",
|
||||
"document": "وثيقة",
|
||||
@@ -195,6 +197,7 @@
|
||||
"error": "خطأ",
|
||||
"errors": "أخطاء",
|
||||
"fallbackToDefaultLocale": "الرجوع إلى اللغة الافتراضية",
|
||||
"false": "كاذب",
|
||||
"filter": "تصفية",
|
||||
"filterWhere": "تصفية {{label}} حيث",
|
||||
"filters": "عوامل التصفية",
|
||||
@@ -250,6 +253,7 @@
|
||||
"successfullyDuplicated": "{{label}} تم استنساخها بنجاح.",
|
||||
"thisLanguage": "العربية",
|
||||
"titleDeleted": "تم حذف {{label}} \"{{title}}\" بنجاح.",
|
||||
"true": "صحيح",
|
||||
"unauthorized": "غير مصرح به",
|
||||
"unsavedChangesDuplicate": "لديك تغييرات لم يتم حفظها. هل تريد الاستمرار في الاستنساخ؟",
|
||||
"untitled": "بدون عنوان",
|
||||
@@ -373,4 +377,4 @@
|
||||
"viewingVersions": "يتمّ استعراض النُّسَخ ل {{entityLabel}} {{documentTitle}}",
|
||||
"viewingVersionsGlobal": "يتمّ استعراض النُّسَخ للاعداد العامّ {{entityLabel}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
"alreadyActivated": "Artıq Aktivləşdirilib",
|
||||
"alreadyLoggedIn": "Artıq daxil olunub",
|
||||
"apiKey": "API Açarı",
|
||||
"authenticated": "Doğrulandı",
|
||||
"backToLogin": "Girişə qayıt",
|
||||
"beginCreateFirstUser": "Başlamaq üçün ilk istifadəçinizi yaradın.",
|
||||
"changePassword": "Parolu dəyişdir",
|
||||
@@ -177,6 +178,7 @@
|
||||
"deletedCountSuccessfully": "{{count}} {{label}} uğurla silindi.",
|
||||
"deletedSuccessfully": "Uğurla silindi.",
|
||||
"deleting": "Silinir...",
|
||||
"depth": "Dərinlik",
|
||||
"descending": "Azalan",
|
||||
"deselectAllRows": "Bütün sıraları seçimi ləğv edin",
|
||||
"document": "Sənəd",
|
||||
@@ -195,6 +197,7 @@
|
||||
"error": "Xəta",
|
||||
"errors": "Xətalar",
|
||||
"fallbackToDefaultLocale": "Standart lokalə keçid",
|
||||
"false": "Yalan",
|
||||
"filter": "Filter",
|
||||
"filterWhere": "{{label}} filtrlə",
|
||||
"filters": "Filtərlər",
|
||||
@@ -250,6 +253,7 @@
|
||||
"successfullyDuplicated": "{{label}} uğurla dublikatlandı.",
|
||||
"thisLanguage": "Azərbaycan dili",
|
||||
"titleDeleted": "{{label}} \"{{title}}\" uğurla silindi.",
|
||||
"true": "Doğru",
|
||||
"unauthorized": "İcazəsiz",
|
||||
"unsavedChangesDuplicate": "Saxlanılmamış dəyişiklikləriniz var. Dublikatla davam etmək istəyirsiniz?",
|
||||
"untitled": "Başlıqsız",
|
||||
@@ -373,4 +377,4 @@
|
||||
"viewingVersions": "{{entityLabel}} {{documentTitle}} üçün versiyaları göstərir",
|
||||
"viewingVersionsGlobal": "Qlobal {{entityLabel}} üçün versiyaları göstərir"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
"alreadyActivated": "Вече активиран",
|
||||
"alreadyLoggedIn": "Вече влязъл",
|
||||
"apiKey": "API ключ",
|
||||
"authenticated": "Аутентикиран",
|
||||
"backToLogin": "Обратно към влизане",
|
||||
"beginCreateFirstUser": "За да започнеш, създай първия си потребител",
|
||||
"changePassword": "Промяна на паролата",
|
||||
@@ -177,6 +178,7 @@
|
||||
"deletedCountSuccessfully": "Изтрити {{count}} {{label}} успешно.",
|
||||
"deletedSuccessfully": "Изтрито успешно.",
|
||||
"deleting": "Изтриване...",
|
||||
"depth": "Дълбочина",
|
||||
"descending": "Низходящо",
|
||||
"deselectAllRows": "Деселектирай всички редове",
|
||||
"document": "Документ",
|
||||
@@ -195,6 +197,7 @@
|
||||
"error": "Грешка",
|
||||
"errors": "Грешки",
|
||||
"fallbackToDefaultLocale": "Използвай локализация по подразбиране",
|
||||
"false": "Ложно",
|
||||
"filter": "Филтрирай",
|
||||
"filterWhere": "Филтрирай {{label}} където",
|
||||
"filters": "Филтри",
|
||||
@@ -250,6 +253,7 @@
|
||||
"successfullyDuplicated": "{{label}} успешно дупликиран.",
|
||||
"thisLanguage": "Български",
|
||||
"titleDeleted": "{{label}} \"{{title}}\" успешно изтрит.",
|
||||
"true": "Истина",
|
||||
"unauthorized": "Неавторизиран",
|
||||
"unsavedChangesDuplicate": "Имаш незапазени промени. Искаш ли да продължиш да дупликираш?",
|
||||
"untitled": "Неозаглавен",
|
||||
@@ -373,4 +377,4 @@
|
||||
"viewingVersions": "Гледане на версии за {{entityLabel}} {{documentTitle}}",
|
||||
"viewingVersionsGlobal": "Гледане на версии за глобалния документ {{entityLabel}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,8 @@
|
||||
"accountOfCurrentUser": "Účet současného uživatele",
|
||||
"alreadyActivated": "Již aktivováno",
|
||||
"alreadyLoggedIn": "Již přihlášen",
|
||||
"apiKey": "Klíč API",
|
||||
"apiKey": "API klíč",
|
||||
"authenticated": "Ověřený",
|
||||
"backToLogin": "Zpět na přihlášení",
|
||||
"beginCreateFirstUser": "Začněte vytvořením svého prvního uživatele.",
|
||||
"changePassword": "Změnit heslo",
|
||||
@@ -15,15 +16,15 @@
|
||||
"createFirstUser": "Vytvořit prvního uživatele",
|
||||
"emailNotValid": "Zadaný email není platný",
|
||||
"emailSent": "Email odeslán",
|
||||
"enableAPIKey": "Povolit klíč API",
|
||||
"enableAPIKey": "Povolit API klíč",
|
||||
"failedToUnlock": "Nepodařilo se odemknout",
|
||||
"forceUnlock": "Vynutit odemčení",
|
||||
"forgotPassword": "Zapomněli jste heslo?",
|
||||
"forgotPasswordEmailInstructions": "Zadejte svůj email níže. Obdržíte email s instrukcemi, jak resetovat vaše heslo.",
|
||||
"forgotPasswordQuestion": "Zapomněli jste heslo?",
|
||||
"generate": "Generovat",
|
||||
"generateNewAPIKey": "Generovat nový klíč API",
|
||||
"generatingNewAPIKeyWillInvalidate": "Vytvoření nového klíče API <1>zneplatní</1> předchozí klíč. Opravdu chcete pokračovat?",
|
||||
"generateNewAPIKey": "Generovat nový API klíč",
|
||||
"generatingNewAPIKeyWillInvalidate": "Vytvoření nového API klíče <1>zneplatní</1> předchozí klíč. Opravdu chcete pokračovat?",
|
||||
"lockUntil": "Uzamknout do",
|
||||
"logBackIn": "Znovu se přihlásit",
|
||||
"logOut": "Odhlásit se",
|
||||
@@ -37,7 +38,7 @@
|
||||
"loginWithAnotherUser": "Abyste se mohli přihlásit s jiným uživatelem, nejdříve se <0>odhlaste</0>.",
|
||||
"logout": "Odhlásit se",
|
||||
"logoutUser": "Odhlásit uživatele",
|
||||
"newAPIKeyGenerated": "Byl vygenerován nový klíč API.",
|
||||
"newAPIKeyGenerated": "Byl vygenerován nový API klíč.",
|
||||
"newAccountCreated": "Pro přístup k <a href=\"{{serverURL}}\">{{serverURL}}</a> byl pro vás vytvořen nový účet. Klepněte na následující odkaz nebo zkopírujte URL do svého prohlížeče pro ověření vašeho emailu: <a href=\"{{verificationURL}}\">{{verificationURL}}</a><br> Po ověření vašeho emailu se budete moci úspěšně přihlásit.",
|
||||
"newPassword": "Nové heslo",
|
||||
"resetPassword": "Resetovat heslo",
|
||||
@@ -96,7 +97,7 @@
|
||||
},
|
||||
"fields": {
|
||||
"addLabel": "Přidat {{label}}",
|
||||
"addLink": "Přidat Odkaz",
|
||||
"addLink": "Přidat odkaz",
|
||||
"addNew": "Přidat nový",
|
||||
"addNewLabel": "Přidat nový {{label}}",
|
||||
"addRelationship": "Přidat vztah",
|
||||
@@ -147,10 +148,10 @@
|
||||
"addBelow": "Přidat pod",
|
||||
"addFilter": "Přidat filtr",
|
||||
"adminTheme": "Motiv administračního rozhraní",
|
||||
"and": "A",
|
||||
"and": "a",
|
||||
"applyChanges": "Použít změny",
|
||||
"ascending": "Vzestupně",
|
||||
"automatic": "Automatické",
|
||||
"automatic": "Automatický",
|
||||
"backToDashboard": "Zpět na nástěnku",
|
||||
"cancel": "Zrušit",
|
||||
"changesNotSaved": "Vaše změny nebyly uloženy. Pokud teď odejdete, ztratíte své změny.",
|
||||
@@ -171,12 +172,13 @@
|
||||
"createdAt": "Vytvořeno v",
|
||||
"creating": "Vytváření",
|
||||
"creatingNewLabel": "Vytváření nového {{label}}",
|
||||
"dark": "Tmavé",
|
||||
"dark": "Tmavý",
|
||||
"dashboard": "Nástěnka",
|
||||
"delete": "Odstranit",
|
||||
"deletedCountSuccessfully": "Úspěšně smazáno {{count}} {{label}}.",
|
||||
"deletedSuccessfully": "Úspěšně odstraněno.",
|
||||
"deleting": "Odstraňování...",
|
||||
"depth": "Hloubka",
|
||||
"descending": "Sestupně",
|
||||
"deselectAllRows": "Zrušte výběr všech řádků",
|
||||
"document": "Dokument",
|
||||
@@ -185,7 +187,7 @@
|
||||
"duplicateWithoutSaving": "Duplikovat bez uložení změn",
|
||||
"edit": "Upravit",
|
||||
"editLabel": "Upravit {{label}}",
|
||||
"editing": "Úpravy",
|
||||
"editing": "Úprava",
|
||||
"editingLabel_many": "Úprava {{count}} {{label}}",
|
||||
"editingLabel_one": "Úprava {{count}} {{label}}",
|
||||
"editingLabel_other": "Úprava {{count}} {{label}}",
|
||||
@@ -195,6 +197,7 @@
|
||||
"error": "Chyba",
|
||||
"errors": "Chyby",
|
||||
"fallbackToDefaultLocale": "Zpětné přepnutí do výchozího locale",
|
||||
"false": "Nepravda",
|
||||
"filter": "Filtr",
|
||||
"filterWhere": "Filtrovat {{label}} kde",
|
||||
"filters": "Filtry",
|
||||
@@ -203,7 +206,7 @@
|
||||
"lastModified": "Naposledy změněno",
|
||||
"leaveAnyway": "Přesto odejít",
|
||||
"leaveWithoutSaving": "Odejít bez uložení",
|
||||
"light": "Světlé",
|
||||
"light": "Světlý",
|
||||
"livePreview": "Náhled",
|
||||
"loading": "Načítání",
|
||||
"locale": "Místní verze",
|
||||
@@ -226,7 +229,7 @@
|
||||
"order": "Pořadí",
|
||||
"pageNotFound": "Stránka nenalezena",
|
||||
"password": "Heslo",
|
||||
"payloadSettings": "Nastavení datového záběru",
|
||||
"payloadSettings": "Nastavení Payload",
|
||||
"perPage": "Na stránku: {{limit}}",
|
||||
"remove": "Odstranit",
|
||||
"reset": "Resetovat",
|
||||
@@ -250,6 +253,7 @@
|
||||
"successfullyDuplicated": "{{label}} úspěšně duplikováno.",
|
||||
"thisLanguage": "Čeština",
|
||||
"titleDeleted": "{{label}} \"{{title}}\" úspěšně smazáno.",
|
||||
"true": "Pravda",
|
||||
"unauthorized": "Neoprávněný",
|
||||
"unsavedChangesDuplicate": "Máte neuložené změny. Chtěli byste pokračovat v duplikování?",
|
||||
"untitled": "Bez názvu",
|
||||
@@ -274,19 +278,19 @@
|
||||
"isLessThanOrEqualTo": "je menší nebo rovno",
|
||||
"isLike": "je jako",
|
||||
"isNotEqualTo": "není rovno",
|
||||
"isNotIn": "není in",
|
||||
"isNotIn": "není v",
|
||||
"near": "blízko"
|
||||
},
|
||||
"upload": {
|
||||
"crop": "Plodina",
|
||||
"cropToolDescription": "Přetáhněte rohy vybrané oblasti, nakreslete novou oblast nebo upravte hodnoty níže.",
|
||||
"crop": "Ořez",
|
||||
"cropToolDescription": "Přetáhněte rohy vybrané oblasti, nakreslete novou oblast nebo upravte níže uvedené hodnoty.",
|
||||
"dragAndDrop": "Přetáhněte soubor",
|
||||
"dragAndDropHere": "nebo sem přetáhněte soubor",
|
||||
"editImage": "Upravit obrázek",
|
||||
"fileName": "Název souboru",
|
||||
"fileSize": "Velikost souboru",
|
||||
"focalPoint": "Středobod",
|
||||
"focalPointDescription": "Přetáhněte bod zaměření přímo na náhled nebo upravte hodnoty níže.",
|
||||
"focalPointDescription": "Přetáhněte bod zaměření přímo na náhled nebo upravte níže uvedené hodnoty.",
|
||||
"height": "Výška",
|
||||
"lessInfo": "Méně informací",
|
||||
"moreInfo": "Více informací",
|
||||
@@ -294,7 +298,7 @@
|
||||
"selectCollectionToBrowse": "Vyberte kolekci pro procházení",
|
||||
"selectFile": "Vyberte soubor",
|
||||
"setCropArea": "Nastavit oblast ořezu",
|
||||
"setFocalPoint": "Nastavit ohnisko",
|
||||
"setFocalPoint": "Nastavit středobod",
|
||||
"sizes": "Velikosti",
|
||||
"sizesFor": "Velikosti pro {{label}}",
|
||||
"width": "Šířka"
|
||||
@@ -373,4 +377,4 @@
|
||||
"viewingVersions": "Zobrazuji verze pro {{entityLabel}} {{documentTitle}}",
|
||||
"viewingVersionsGlobal": "Zobrazuji verze pro globální {{entityLabel}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user