Compare commits
26 Commits
fix/random
...
v2.18.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
157fff0417 | ||
|
|
88e113a545 | ||
|
|
d33afe48fe | ||
|
|
e76df32f09 | ||
|
|
b068f30f51 | ||
|
|
82bd5c656f | ||
|
|
afe8992ca6 | ||
|
|
48a410e294 | ||
|
|
82b88a315f | ||
|
|
cc94078607 | ||
|
|
1c30ad73b6 | ||
|
|
d9442dcce3 | ||
|
|
de92c50847 | ||
|
|
b7f5f932f6 | ||
|
|
bc9e591e37 | ||
|
|
ad38011348 | ||
|
|
d02b1fb084 | ||
|
|
51efe4f39b | ||
|
|
30e535b5b9 | ||
|
|
4bd3bb9400 | ||
|
|
69c93d3c62 | ||
|
|
78aa957043 | ||
|
|
cd06c022c0 | ||
|
|
8299e9fc33 | ||
|
|
9df5ab8a10 | ||
|
|
6979f5a1b1 |
@@ -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"
|
||||
47
CHANGELOG.md
47
CHANGELOG.md
@@ -1,3 +1,50 @@
|
||||
## [2.18.2](https://github.com/payloadcms/payload/compare/v2.18.1...v2.18.2) (2024-05-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* allow focal point when no sizes defined ([#6397](https://github.com/payloadcms/payload/issues/6397)) ([88e113a](https://github.com/payloadcms/payload/commit/88e113a5452300434f690186d10ea02ab159ffc3))
|
||||
|
||||
## [2.18.1](https://github.com/payloadcms/payload/compare/v2.18.0...v2.18.1) (2024-05-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add back explicit crop x and y values ([#6391](https://github.com/payloadcms/payload/issues/6391)) ([e76df32](https://github.com/payloadcms/payload/commit/e76df32f0987cc92dc8d9c693950e650c52576bf))
|
||||
|
||||
## [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)
|
||||
|
||||
|
||||
|
||||
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.
|
||||
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/db-postgres",
|
||||
"version": "0.8.2",
|
||||
"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.16.1",
|
||||
"version": "2.18.2",
|
||||
"description": "Node, React and MongoDB Headless CMS and Application Framework",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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()
|
||||
@@ -45,10 +45,11 @@ export const EditUpload: React.FC<{
|
||||
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 +73,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 +91,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 +111,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 +187,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 +258,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>
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -4,6 +4,7 @@ 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'
|
||||
@@ -12,9 +13,11 @@ const RadioInput: React.FC<Props> = (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}>
|
||||
|
||||
@@ -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={[
|
||||
|
||||
@@ -116,10 +116,12 @@ export const Upload: React.FC<Props> = (props) => {
|
||||
|
||||
const hasImageSizes = collection?.upload?.imageSizes?.length > 0
|
||||
const hasResizeOptions = Boolean(collection?.upload?.resizeOptions)
|
||||
// Explicitly check if set to true, default is undefined
|
||||
const focalPointEnabled = collection?.upload?.focalPoint === true
|
||||
|
||||
const { collection: { upload: { crop: showCrop = true, focalPoint = true } } = {} } = props
|
||||
|
||||
const showFocalPoint = focalPoint && (hasImageSizes || hasResizeOptions)
|
||||
const showFocalPoint = focalPoint && (hasImageSizes || hasResizeOptions || focalPointEnabled)
|
||||
|
||||
const lastSubmittedTime = submitted ? new Date().toISOString() : null
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -200,4 +200,11 @@ dialog {
|
||||
z-index: var(--z-modal);
|
||||
}
|
||||
|
||||
.script-language {
|
||||
& > *,
|
||||
& > * > * {
|
||||
letter-spacing: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
@import '~payload-user-css';
|
||||
|
||||
@@ -130,6 +130,7 @@ async function create<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
collection,
|
||||
config,
|
||||
data,
|
||||
operation: 'create',
|
||||
overwriteExistingFiles,
|
||||
req,
|
||||
throwOnMissingFile:
|
||||
|
||||
@@ -157,6 +157,7 @@ async function update<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
collection,
|
||||
config,
|
||||
data: bulkUpdateData,
|
||||
operation: 'update',
|
||||
overwriteExistingFiles,
|
||||
req,
|
||||
throwOnMissingFile: false,
|
||||
|
||||
@@ -148,6 +148,8 @@ async function updateByID<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
collection,
|
||||
config,
|
||||
data,
|
||||
operation: 'update',
|
||||
originalDoc,
|
||||
overwriteExistingFiles,
|
||||
req,
|
||||
throwOnMissingFile: false,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -558,14 +558,6 @@ export type RichTextField<
|
||||
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?: {
|
||||
@@ -683,10 +675,6 @@ export type Field =
|
||||
| UIField
|
||||
| UploadField
|
||||
|
||||
export type FieldWithRichTextRequiredEditor =
|
||||
| Exclude<Field, RichTextField>
|
||||
| RichTextFieldRequiredEditor
|
||||
|
||||
export type FieldAffectingData =
|
||||
| ArrayField
|
||||
| BlockField
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
"alreadyActivated": "Již aktivováno",
|
||||
"alreadyLoggedIn": "Již přihlášen",
|
||||
"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",
|
||||
@@ -177,6 +178,7 @@
|
||||
"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",
|
||||
@@ -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",
|
||||
@@ -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",
|
||||
@@ -373,4 +377,4 @@
|
||||
"viewingVersions": "Zobrazuji verze pro {{entityLabel}} {{documentTitle}}",
|
||||
"viewingVersionsGlobal": "Zobrazuji verze pro globální {{entityLabel}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
"alreadyActivated": "Bereits aktiviert",
|
||||
"alreadyLoggedIn": "Bereits angemeldet",
|
||||
"apiKey": "API-Key",
|
||||
"authenticated": "Authentifiziert",
|
||||
"backToLogin": "Zurück zur Anmeldung",
|
||||
"beginCreateFirstUser": "Erstelle deinen ersten Benutzer um zu beginnen",
|
||||
"changePassword": "Passwort ändern",
|
||||
@@ -177,6 +178,7 @@
|
||||
"deletedCountSuccessfully": "{{count}} {{label}} erfolgreich gelöscht.",
|
||||
"deletedSuccessfully": "Erfolgreich gelöscht.",
|
||||
"deleting": "Lösche...",
|
||||
"depth": "Tiefe",
|
||||
"descending": "Absteigend",
|
||||
"deselectAllRows": "Alle Zeilen abwählen",
|
||||
"document": "Dokument",
|
||||
@@ -195,6 +197,7 @@
|
||||
"error": "Fehler",
|
||||
"errors": "Fehler",
|
||||
"fallbackToDefaultLocale": "Rückgriff auf das Standardgebietsschema",
|
||||
"false": "Falsch",
|
||||
"filter": "Filter",
|
||||
"filterWhere": "Filter {{label}} wo",
|
||||
"filters": "Filter",
|
||||
@@ -250,6 +253,7 @@
|
||||
"successfullyDuplicated": "{{label}} wurde erfolgreich dupliziert.",
|
||||
"thisLanguage": "Deutsch",
|
||||
"titleDeleted": "{{label}} {{title}} wurde erfolgreich gelöscht.",
|
||||
"true": "Wahr",
|
||||
"unauthorized": "Nicht autorisiert",
|
||||
"unsavedChangesDuplicate": "Du hast ungespeicherte Änderungen, möchtest du mit dem Duplizieren fortfahren?",
|
||||
"untitled": "ohne Titel",
|
||||
@@ -373,4 +377,4 @@
|
||||
"viewingVersions": "Betrachte Versionen für {{entityLabel}} {{documentTitle}}",
|
||||
"viewingVersionsGlobal": "`Betrachte Versionen für das Globale Dokument {{entityLabel}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
"alreadyActivated": "Already Activated",
|
||||
"alreadyLoggedIn": "Already logged in",
|
||||
"apiKey": "API Key",
|
||||
"authenticated": "Authenticated",
|
||||
"backToLogin": "Back to login",
|
||||
"beginCreateFirstUser": "To begin, create your first user.",
|
||||
"changePassword": "Change Password",
|
||||
@@ -177,6 +178,7 @@
|
||||
"deletedCountSuccessfully": "Deleted {{count}} {{label}} successfully.",
|
||||
"deletedSuccessfully": "Deleted successfully.",
|
||||
"deleting": "Deleting...",
|
||||
"depth": "Depth",
|
||||
"descending": "Descending",
|
||||
"deselectAllRows": "Deselect all rows",
|
||||
"document": "Document",
|
||||
@@ -195,6 +197,7 @@
|
||||
"error": "Error",
|
||||
"errors": "Errors",
|
||||
"fallbackToDefaultLocale": "Fallback to default locale",
|
||||
"false": "False",
|
||||
"filter": "Filter",
|
||||
"filterWhere": "Filter {{label}} where",
|
||||
"filters": "Filters",
|
||||
@@ -250,6 +253,7 @@
|
||||
"successfullyDuplicated": "{{label}} successfully duplicated.",
|
||||
"thisLanguage": "English",
|
||||
"titleDeleted": "{{label}} \"{{title}}\" successfully deleted.",
|
||||
"true": "True",
|
||||
"unauthorized": "Unauthorized",
|
||||
"unsavedChangesDuplicate": "You have unsaved changes. Would you like to continue to duplicate?",
|
||||
"untitled": "Untitled",
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"alreadyActivated": "Ya Activado",
|
||||
"alreadyLoggedIn": "Sesión iniciada",
|
||||
"apiKey": "Clave API",
|
||||
"authenticated": "Autenticado",
|
||||
"backToLogin": "Regresar al inicio de sesión",
|
||||
"beginCreateFirstUser": "Para empezar, crea tu primer usuario.",
|
||||
"changePassword": "Cambiar contraseña",
|
||||
@@ -177,6 +178,7 @@
|
||||
"deletedCountSuccessfully": "Se eliminó {{count}} {{label}} con éxito.",
|
||||
"deletedSuccessfully": "Borrado exitosamente.",
|
||||
"deleting": "Eliminando...",
|
||||
"depth": "Profundidad",
|
||||
"descending": "Descendente",
|
||||
"deselectAllRows": "Deselecciona todas las filas",
|
||||
"document": "Documento",
|
||||
@@ -195,6 +197,7 @@
|
||||
"error": "Error",
|
||||
"errors": "Errores",
|
||||
"fallbackToDefaultLocale": "Volver a la configuración regional por defecto",
|
||||
"false": "Falso",
|
||||
"filter": "Filtro",
|
||||
"filterWhere": "Filtrar {{label}} donde",
|
||||
"filters": "Filtros",
|
||||
@@ -250,6 +253,7 @@
|
||||
"successfullyDuplicated": "{{label}} duplicado correctamente.",
|
||||
"thisLanguage": "Español",
|
||||
"titleDeleted": "{{label}} {{title}} eliminado correctamente.",
|
||||
"true": "Verdadero",
|
||||
"unauthorized": "No autorizado",
|
||||
"unsavedChangesDuplicate": "Tienes cambios sin guardar. ¿Deseas continuar para duplicar?",
|
||||
"untitled": "Sin título",
|
||||
@@ -373,4 +377,4 @@
|
||||
"viewingVersions": "Viendo versiones para {{entityLabel}} {{documentTitle}}",
|
||||
"viewingVersionsGlobal": "Viendo versiones para el global {{entityLabel}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
"alreadyActivated": "قبلاً فعال شده است",
|
||||
"alreadyLoggedIn": "قبلاً وارد شدهاید",
|
||||
"apiKey": "کلید اِیپیآی",
|
||||
"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": "Déjà activé",
|
||||
"alreadyLoggedIn": "Déjà connecté",
|
||||
"apiKey": "Clé API",
|
||||
"authenticated": "Authentifié",
|
||||
"backToLogin": "Retour à la connexion",
|
||||
"beginCreateFirstUser": "Pour commencer, créez votre premier utilisateur.",
|
||||
"changePassword": "Changer le mot de passe",
|
||||
@@ -177,6 +178,7 @@
|
||||
"deletedCountSuccessfully": "{{count}} {{label}} supprimé avec succès.",
|
||||
"deletedSuccessfully": "Supprimé(e) avec succès.",
|
||||
"deleting": "Suppression en cours...",
|
||||
"depth": "Profondeur",
|
||||
"descending": "Descendant(e)",
|
||||
"deselectAllRows": "Désélectionner toutes les lignes",
|
||||
"document": "Document",
|
||||
@@ -195,6 +197,7 @@
|
||||
"error": "Erreur",
|
||||
"errors": "Erreurs",
|
||||
"fallbackToDefaultLocale": "Retour à la locale par défaut",
|
||||
"false": "Faux",
|
||||
"filter": "Filtrer",
|
||||
"filterWhere": "Filtrer {{label}} où",
|
||||
"filters": "Filtres",
|
||||
@@ -250,6 +253,7 @@
|
||||
"successfullyDuplicated": "{{label}} dupliqué(e) avec succès.",
|
||||
"thisLanguage": "Français",
|
||||
"titleDeleted": "{{label}} \"{{title}}\" supprimé(e) avec succès.",
|
||||
"true": "Vrai",
|
||||
"unauthorized": "Non autorisé",
|
||||
"unsavedChangesDuplicate": "Vous avez des changements non enregistrés. Souhaitez-vous continuer la duplication ?",
|
||||
"untitled": "Sans titre",
|
||||
@@ -373,4 +377,4 @@
|
||||
"viewingVersions": "Affichage des versions de ou du {{entityLabel}} {{documentTitle}}",
|
||||
"viewingVersionsGlobal": "Affichage des versions globales de ou du {{entityLabel}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
"alreadyActivated": "Već aktivirano",
|
||||
"alreadyLoggedIn": "Već prijavljen",
|
||||
"apiKey": "API ključ",
|
||||
"authenticated": "Autenticiran",
|
||||
"backToLogin": "Nazad na prijavu",
|
||||
"beginCreateFirstUser": "Za početak, kreiraj svog prvog korisnika.",
|
||||
"changePassword": "Promjeni lozinku",
|
||||
@@ -177,6 +178,7 @@
|
||||
"deletedCountSuccessfully": "Uspješno izbrisano {{count}} {{label}}.",
|
||||
"deletedSuccessfully": "Uspješno obrisano.",
|
||||
"deleting": "Brisanje...",
|
||||
"depth": "Dubina",
|
||||
"descending": "Silazno",
|
||||
"deselectAllRows": "Odznači sve redove",
|
||||
"document": "Dokument",
|
||||
@@ -195,6 +197,7 @@
|
||||
"error": "Greška",
|
||||
"errors": "Greške",
|
||||
"fallbackToDefaultLocale": "Vraćanje na zadani jezik",
|
||||
"false": "Netočno",
|
||||
"filter": "Filter",
|
||||
"filterWhere": "Filter {{label}} gdje",
|
||||
"filters": "Filteri",
|
||||
@@ -250,6 +253,7 @@
|
||||
"successfullyDuplicated": "{{label}} uspješno duplicirano.",
|
||||
"thisLanguage": "Hrvatski",
|
||||
"titleDeleted": "{{label}} \"{{title}}\" uspješno obrisano.",
|
||||
"true": "Istinito",
|
||||
"unauthorized": "Neovlašteno",
|
||||
"unsavedChangesDuplicate": "Imate nespremljene promjene. Želite li nastaviti s dupliciranjem?",
|
||||
"untitled": "Bez naslova",
|
||||
@@ -373,4 +377,4 @@
|
||||
"viewingVersions": "Pregled verzija za {{entityLabel}} {{documentTitle}}",
|
||||
"viewingVersionsGlobal": "Pregled verzije za globalni {{entityLabel}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
"alreadyActivated": "Már aktiválva van",
|
||||
"alreadyLoggedIn": "Már bejelentkezett",
|
||||
"apiKey": "API-kulcs",
|
||||
"authenticated": "Hitelesített",
|
||||
"backToLogin": "Vissza a bejelentkezéshez",
|
||||
"beginCreateFirstUser": "Kezdésként hozza létre az első felhasználót.",
|
||||
"changePassword": "Jelszó módosítása",
|
||||
@@ -177,6 +178,7 @@
|
||||
"deletedCountSuccessfully": "{{count}} {{label}} sikeresen törölve.",
|
||||
"deletedSuccessfully": "Sikeresen törölve.",
|
||||
"deleting": "Törlés...",
|
||||
"depth": "Mélység",
|
||||
"descending": "Csökkenő",
|
||||
"deselectAllRows": "Jelölje ki az összes sort",
|
||||
"document": "Dokumentum",
|
||||
@@ -195,6 +197,7 @@
|
||||
"error": "Hiba",
|
||||
"errors": "Hibák",
|
||||
"fallbackToDefaultLocale": "Visszatérés az alapértelmezett nyelvhez",
|
||||
"false": "Hamis",
|
||||
"filter": "Szűrő",
|
||||
"filterWhere": "Szűrő {{label}} ahol",
|
||||
"filters": "Szűrők",
|
||||
@@ -250,6 +253,7 @@
|
||||
"successfullyDuplicated": "{{label}} sikeresen duplikálódott.",
|
||||
"thisLanguage": "Magyar",
|
||||
"titleDeleted": "{{label}} \"{{title}}\" sikeresen törölve.",
|
||||
"true": "Igaz",
|
||||
"unauthorized": "Jogosulatlan",
|
||||
"unsavedChangesDuplicate": "Nem mentett módosításai vannak. Szeretné folytatni a duplikációt?",
|
||||
"untitled": "Névtelen",
|
||||
@@ -373,4 +377,4 @@
|
||||
"viewingVersions": "A {{entityLabel}} {{documentTitle}} verzióinak megtekintése",
|
||||
"viewingVersionsGlobal": "A globális {{entityLabel}} verzióinak megtekintése"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
"alreadyActivated": "Già Attivato",
|
||||
"alreadyLoggedIn": "Sei già loggato",
|
||||
"apiKey": "Chiave API",
|
||||
"authenticated": "Autenticato",
|
||||
"backToLogin": "Torna al login",
|
||||
"beginCreateFirstUser": "Per iniziare, crea il tuo primo utente.",
|
||||
"changePassword": "Cambia Password",
|
||||
@@ -177,6 +178,7 @@
|
||||
"deletedCountSuccessfully": "{{count}} {{label}} eliminato con successo.",
|
||||
"deletedSuccessfully": "Eliminato con successo.",
|
||||
"deleting": "Sto eliminando...",
|
||||
"depth": "Profondità",
|
||||
"descending": "Decrescente",
|
||||
"deselectAllRows": "Deseleziona tutte le righe",
|
||||
"document": "Documento",
|
||||
@@ -195,6 +197,7 @@
|
||||
"error": "Errore",
|
||||
"errors": "Errori",
|
||||
"fallbackToDefaultLocale": "Fallback al locale predefinito",
|
||||
"false": "Falso",
|
||||
"filter": "Filtro",
|
||||
"filterWhere": "Filtra {{label}} se",
|
||||
"filters": "Filtri",
|
||||
@@ -251,6 +254,7 @@
|
||||
"successfullyDuplicated": "{{label}} duplicato con successo.",
|
||||
"thisLanguage": "Italiano",
|
||||
"titleDeleted": "{{label}} {{title}} eliminato con successo.",
|
||||
"true": "Vero",
|
||||
"unauthorized": "Non autorizzato",
|
||||
"unsavedChangesDuplicate": "Sono presenti modifiche non salvate. Vuoi continuare a duplicare?",
|
||||
"untitled": "Senza titolo",
|
||||
@@ -374,4 +378,4 @@
|
||||
"viewingVersions": "Visualizzazione delle versioni per {{entityLabel}} {{documentTitle}}",
|
||||
"viewingVersionsGlobal": "`Visualizzazione delle versioni per {{entityLabel}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
"alreadyActivated": "すでに有効です",
|
||||
"alreadyLoggedIn": "すでにログインしています",
|
||||
"apiKey": "API Key",
|
||||
"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": "Japanese",
|
||||
"titleDeleted": "{{label}} \"{{title}}\" が削除されました。",
|
||||
"true": "真実",
|
||||
"unauthorized": "未認証",
|
||||
"unsavedChangesDuplicate": "未保存の変更があります。複製を続けますか?",
|
||||
"untitled": "Untitled",
|
||||
@@ -373,4 +377,4 @@
|
||||
"viewingVersions": "表示バージョン: {{entityLabel}} {{documentTitle}}",
|
||||
"viewingVersionsGlobal": "表示バージョン: グローバルな {{entityLabel}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
{
|
||||
"$schema": "[SKIPPED]",
|
||||
"authentication": {
|
||||
"account": "계정",
|
||||
"accountOfCurrentUser": "현재 사용자의 계정",
|
||||
"alreadyActivated": "이미 활성화됨",
|
||||
"alreadyLoggedIn": "이미 로그인됨",
|
||||
"apiKey": "API 키",
|
||||
"authenticated": "인증됨",
|
||||
"backToLogin": "로그인 화면으로 돌아가기",
|
||||
"beginCreateFirstUser": "시작하려면 첫 번째 사용자를 생성하세요.",
|
||||
"changePassword": "비밀번호 변경",
|
||||
@@ -166,16 +168,17 @@
|
||||
"create": "생성",
|
||||
"createNew": "새로 생성",
|
||||
"createNewLabel": "새로운 {{label}} 생성",
|
||||
"creatingNewLabel": "{{label}} 생성 중",
|
||||
"created": "생성됨",
|
||||
"createdAt": "생성 일시",
|
||||
"creating": "생성 중",
|
||||
"creatingNewLabel": "{{label}} 생성 중",
|
||||
"dark": "다크",
|
||||
"dashboard": "대시보드",
|
||||
"delete": "삭제",
|
||||
"deletedCountSuccessfully": "{{count}}개의 {{label}}를 삭제했습니다.",
|
||||
"deletedSuccessfully": "삭제되었습니다.",
|
||||
"deleting": "삭제 중...",
|
||||
"depth": "깊이",
|
||||
"descending": "내림차순",
|
||||
"deselectAllRows": "모든 행 선택 해제",
|
||||
"document": "문서",
|
||||
@@ -194,6 +197,7 @@
|
||||
"error": "오류",
|
||||
"errors": "오류",
|
||||
"fallbackToDefaultLocale": "기본 locale로 대체",
|
||||
"false": "거짓",
|
||||
"filter": "필터",
|
||||
"filterWhere": "{{label}} 필터링 조건",
|
||||
"filters": "필터",
|
||||
@@ -220,8 +224,8 @@
|
||||
"notFound": "찾을 수 없음",
|
||||
"nothingFound": "찾을 수 없습니다",
|
||||
"of": "의",
|
||||
"or": "또는",
|
||||
"open": "열기",
|
||||
"or": "또는",
|
||||
"order": "순서",
|
||||
"pageNotFound": "페이지를 찾을 수 없음",
|
||||
"password": "비밀번호",
|
||||
@@ -249,6 +253,7 @@
|
||||
"successfullyDuplicated": "{{label}}이(가) 복제되었습니다.",
|
||||
"thisLanguage": "한국어",
|
||||
"titleDeleted": "{{label}} \"{{title}}\"을(를) 삭제했습니다.",
|
||||
"true": "참",
|
||||
"unauthorized": "권한 없음",
|
||||
"unsavedChangesDuplicate": "저장되지 않은 변경 사항이 있습니다. 복제를 계속하시겠습니까?",
|
||||
"untitled": "제목 없음",
|
||||
@@ -282,10 +287,10 @@
|
||||
"dragAndDrop": "파일을 끌어다 놓으세요",
|
||||
"dragAndDropHere": "또는 여기로 파일을 끌어다 놓으세요",
|
||||
"editImage": "이미지 수정",
|
||||
"focalPoint": "초점",
|
||||
"focalPointDescription": "미리보기에서 초점을 직접 드래그하거나 아래의 값을 조정하세요.",
|
||||
"fileName": "파일 이름",
|
||||
"fileSize": "파일 크기",
|
||||
"focalPoint": "초점",
|
||||
"focalPointDescription": "미리보기에서 초점을 직접 드래그하거나 아래의 값을 조정하세요.",
|
||||
"height": "높이",
|
||||
"lessInfo": "정보 숨기기",
|
||||
"moreInfo": "정보 더보기",
|
||||
@@ -372,4 +377,4 @@
|
||||
"viewingVersions": "{{entityLabel}} {{documentTitle}}에 대한 버전 보기",
|
||||
"viewingVersionsGlobal": "글로벌 {{entityLabel}}에 대한 버전 보기"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
"alreadyActivated": "အတည်ပြုပြီး",
|
||||
"alreadyLoggedIn": "ဝင်ရောက်ပြီးသား",
|
||||
"apiKey": "API Key",
|
||||
"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": "နေရာတွင် စစ်ထုတ်ပါ။",
|
||||
"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": "Allerede aktivert",
|
||||
"alreadyLoggedIn": "Allerede logget inn",
|
||||
"apiKey": "API-nøkkel",
|
||||
"authenticated": "Autentisert",
|
||||
"backToLogin": "Tilbake til innlogging",
|
||||
"beginCreateFirstUser": "Opprett din første bruker for å fortsette.",
|
||||
"changePassword": "Endre passord",
|
||||
@@ -177,6 +178,7 @@
|
||||
"deletedCountSuccessfully": "Slettet {{count}} {{label}}.",
|
||||
"deletedSuccessfully": "Slettet.",
|
||||
"deleting": "Sletter...",
|
||||
"depth": "Dybde",
|
||||
"descending": "Synkende",
|
||||
"deselectAllRows": "Fjern markeringen fra alle rader",
|
||||
"document": "Dokument",
|
||||
@@ -195,6 +197,7 @@
|
||||
"error": "Feil",
|
||||
"errors": "Feil",
|
||||
"fallbackToDefaultLocale": "Tilbakestilling til standard lokalitet",
|
||||
"false": "Falsk",
|
||||
"filter": "Filtrer",
|
||||
"filterWhere": "Filtrer {{label}} der",
|
||||
"filters": "Filter",
|
||||
@@ -250,6 +253,7 @@
|
||||
"successfullyDuplicated": "{{label}} ble duplisert.",
|
||||
"thisLanguage": "Norsk",
|
||||
"titleDeleted": "{{label}} \"{{title}}\" ble slettet.",
|
||||
"true": "Sann",
|
||||
"unauthorized": "Ikke autorisert",
|
||||
"unsavedChangesDuplicate": "Du har ulagrede endringer. Vil du fortsette å duplisere?",
|
||||
"untitled": "Uten tittel",
|
||||
@@ -373,4 +377,4 @@
|
||||
"viewingVersions": "Viser versjoner for {{entityLabel}} {{documentTitle}}",
|
||||
"viewingVersionsGlobal": "Viser versjoner for den globale variabelen {{entityLabel}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
"alreadyActivated": "Al geactiveerd",
|
||||
"alreadyLoggedIn": "Al ingelogd",
|
||||
"apiKey": "API-sleutel",
|
||||
"authenticated": "Geverifieerd",
|
||||
"backToLogin": "Terug naar inloggen",
|
||||
"beginCreateFirstUser": "Om te beginnen maakt u uw eerste gebruiker aan.",
|
||||
"changePassword": "Wachtwoord wijzigen",
|
||||
@@ -177,6 +178,7 @@
|
||||
"deletedCountSuccessfully": "{{count}} {{label}} succesvol verwijderd.",
|
||||
"deletedSuccessfully": "Succesvol verwijderd.",
|
||||
"deleting": "Verwijderen...",
|
||||
"depth": "Diepte",
|
||||
"descending": "Aflopend",
|
||||
"deselectAllRows": "Deselecteer alle rijen",
|
||||
"document": "Document",
|
||||
@@ -195,6 +197,7 @@
|
||||
"error": "Fout",
|
||||
"errors": "Fouten",
|
||||
"fallbackToDefaultLocale": "Terugval naar standaardtaal",
|
||||
"false": "Onwaar",
|
||||
"filter": "Filter",
|
||||
"filterWhere": "Filter {{label}} waar",
|
||||
"filters": "Filters",
|
||||
@@ -250,6 +253,7 @@
|
||||
"successfullyDuplicated": "{{label}} succesvol gedupliceerd.",
|
||||
"thisLanguage": "Nederlands",
|
||||
"titleDeleted": "{{label}} \"{{title}}\" succesvol verwijderd.",
|
||||
"true": "Waar",
|
||||
"unauthorized": "Onbevoegd",
|
||||
"unsavedChangesDuplicate": "U heeft onbewaarde wijzigingen. Wilt u doorgaan met dupliceren?",
|
||||
"untitled": "Zonder titel",
|
||||
@@ -373,4 +377,4 @@
|
||||
"viewingVersions": "Bekijk versies voor {{entityLabel}} {{documentTitle}}",
|
||||
"viewingVersionsGlobal": "`Bekijk versies voor global {{entityLabel}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
"alreadyActivated": "Już aktywowano",
|
||||
"alreadyLoggedIn": "Już zalogowano",
|
||||
"apiKey": "Klucz API",
|
||||
"authenticated": "Uwierzytelniony",
|
||||
"backToLogin": "Powrót do logowania",
|
||||
"beginCreateFirstUser": "Aby rozpocząć, utwórz pierwszego użytkownika",
|
||||
"changePassword": "Zmień hasło",
|
||||
@@ -178,6 +179,7 @@
|
||||
"deletedCountSuccessfully": "Pomyślnie usunięto {{count}} {{label}}.",
|
||||
"deletedSuccessfully": "Pomyślnie usunięto.",
|
||||
"deleting": "Usuwanie...",
|
||||
"depth": "Głębokość",
|
||||
"descending": "Malejąco",
|
||||
"deselectAllRows": "Odznacz wszystkie wiersze",
|
||||
"document": "Dokument",
|
||||
@@ -196,6 +198,7 @@
|
||||
"error": "Błąd",
|
||||
"errors": "Błędy",
|
||||
"fallbackToDefaultLocale": "Powrót do domyślnych ustawień regionalnych",
|
||||
"false": "Fałszywe",
|
||||
"filter": "Filtr",
|
||||
"filterWhere": "Filtruj gdzie",
|
||||
"filters": "Filtry",
|
||||
@@ -251,6 +254,7 @@
|
||||
"successfullyDuplicated": "Pomyślnie zduplikowano {{label}}",
|
||||
"thisLanguage": "Polski",
|
||||
"titleDeleted": "Pomyślnie usunięto {{label}} {{title}}",
|
||||
"true": "Prawda",
|
||||
"unauthorized": "Brak autoryzacji",
|
||||
"unsavedChangesDuplicate": "Masz niezapisane zmiany. Czy chcesz kontynuować duplikowanie?",
|
||||
"untitled": "Bez nazwy",
|
||||
@@ -374,4 +378,4 @@
|
||||
"viewingVersions": "Przeglądanie wersji {{entityLabel}} {{documentTitle}}",
|
||||
"viewingVersionsGlobal": "Przeglądanie wersji dla globalnej kolekcji {{entityLabel}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
"alreadyActivated": "Conta já ativada",
|
||||
"alreadyLoggedIn": "Login já realizado",
|
||||
"apiKey": "Chave da API",
|
||||
"authenticated": "Autenticado",
|
||||
"backToLogin": "Voltar para login",
|
||||
"beginCreateFirstUser": "Para começar, crie seu primeiro usuário.",
|
||||
"changePassword": "Mudar senha",
|
||||
@@ -177,6 +178,7 @@
|
||||
"deletedCountSuccessfully": "Excluído {{count}} {{label}} com sucesso.",
|
||||
"deletedSuccessfully": "Apagado com sucesso.",
|
||||
"deleting": "Excluindo...",
|
||||
"depth": "Profundidade",
|
||||
"descending": "Decrescente",
|
||||
"deselectAllRows": "Desmarcar todas as linhas",
|
||||
"document": "Documento",
|
||||
@@ -195,6 +197,7 @@
|
||||
"error": "Erro",
|
||||
"errors": "Erros",
|
||||
"fallbackToDefaultLocale": "Recuo para o local padrão",
|
||||
"false": "Falso",
|
||||
"filter": "Filtro",
|
||||
"filterWhere": "Filtrar {{label}} em que",
|
||||
"filters": "Filtros",
|
||||
@@ -250,6 +253,7 @@
|
||||
"successfullyDuplicated": "{{label}} duplicado com sucesso.",
|
||||
"thisLanguage": "Português",
|
||||
"titleDeleted": "{{label}} {{title}} excluído com sucesso.",
|
||||
"true": "Verdadeiro",
|
||||
"unauthorized": "Não autorizado",
|
||||
"unsavedChangesDuplicate": "Você tem mudanças não salvas. Você gostaria de continuar a duplicar?",
|
||||
"untitled": "Sem título",
|
||||
@@ -373,4 +377,4 @@
|
||||
"viewingVersions": "Visualizando versões para o/a {{entityLabel}} {{documentTitle}}",
|
||||
"viewingVersionsGlobal": "`Visualizando versões para o global {{entityLabel}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
"alreadyActivated": "Deja activat",
|
||||
"alreadyLoggedIn": "Deja autorizat",
|
||||
"apiKey": "Cheia API",
|
||||
"authenticated": "Autentificat",
|
||||
"backToLogin": "Înapoi la login",
|
||||
"beginCreateFirstUser": "Pentru a începe, creați primul utilizator.",
|
||||
"changePassword": "Schimbați parola",
|
||||
@@ -177,6 +178,7 @@
|
||||
"deletedCountSuccessfully": "Șterse cu succes {{count}} {{label}}.",
|
||||
"deletedSuccessfully": "Șters cu succes.",
|
||||
"deleting": "Deleting...",
|
||||
"depth": "Adâncime",
|
||||
"descending": "Descendentă",
|
||||
"deselectAllRows": "Deselectează toate rândurile",
|
||||
"document": "Document",
|
||||
@@ -195,6 +197,7 @@
|
||||
"error": "Eroare",
|
||||
"errors": "Erori",
|
||||
"fallbackToDefaultLocale": "Revenire la locația implicită",
|
||||
"false": "Fals",
|
||||
"filter": "Filtru",
|
||||
"filterWhere": "Filtrează {{label}} unde",
|
||||
"filters": "Filtre",
|
||||
@@ -250,6 +253,7 @@
|
||||
"successfullyDuplicated": "{{label}} duplicat(ă) cu succes.",
|
||||
"thisLanguage": "Română",
|
||||
"titleDeleted": "{{label}} \"{{title}}\" șters cu succes.",
|
||||
"true": "Adevărat",
|
||||
"unauthorized": "neautorizat(ă)",
|
||||
"unsavedChangesDuplicate": "Aveți modificări nesalvate. Doriți să continuați să duplicați?",
|
||||
"untitled": "Fără titlu",
|
||||
@@ -373,4 +377,4 @@
|
||||
"viewingVersions": "Vizualizarea versiunilor pentru {{entityLabel}} {{documentTitle}}",
|
||||
"viewingVersionsGlobal": "Vizualizarea versiunilor pentru globala {{entityLabel}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
"alreadyActivated": "Već aktivirano",
|
||||
"alreadyLoggedIn": "Već prijavljen",
|
||||
"apiKey": "API ključ",
|
||||
"authenticated": "Autentifikovan",
|
||||
"backToLogin": "Nazad na prijavu",
|
||||
"beginCreateFirstUser": "Na samom početku kreiraj svog prvog korisnika",
|
||||
"changePassword": "Promeni lozinku",
|
||||
@@ -177,6 +178,7 @@
|
||||
"deletedCountSuccessfully": "Uspešno izbrisano {{count}} {{label}}.",
|
||||
"deletedSuccessfully": "Uspešno izbrisano.",
|
||||
"deleting": "Brisanje...",
|
||||
"depth": "Dubina",
|
||||
"descending": "Opadajuće",
|
||||
"deselectAllRows": "Deselektujte sve redove",
|
||||
"document": "Dokument",
|
||||
@@ -195,6 +197,7 @@
|
||||
"error": "Greška",
|
||||
"errors": "Greške",
|
||||
"fallbackToDefaultLocale": "Vraćanje na zadati jezik",
|
||||
"false": "Lažno",
|
||||
"filter": "Filter",
|
||||
"filterWhere": "Filter {{label}} gde",
|
||||
"filters": "Filteri",
|
||||
@@ -250,6 +253,7 @@
|
||||
"successfullyDuplicated": "{{label}} uspešno duplicirano.",
|
||||
"thisLanguage": "Srpski (latinica)",
|
||||
"titleDeleted": "{{label}} \"{{title}}\" uspešno obrisano.",
|
||||
"true": "Istinito",
|
||||
"unauthorized": "Niste autorizovani",
|
||||
"unsavedChangesDuplicate": "Imate nesačuvane promene. Da li želite nastaviti sa dupliciranjem?",
|
||||
"untitled": "Bez naslova",
|
||||
@@ -373,4 +377,4 @@
|
||||
"viewingVersions": "Pregled verzija za {{entityLabel}} {{documentTitle}}",
|
||||
"viewingVersionsGlobal": "Pregled verzije za globalni {{entityLabel}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
"alreadyActivated": "Већ активирано",
|
||||
"alreadyLoggedIn": "Већ пријављен",
|
||||
"apiKey": "АПИ кључ",
|
||||
"authenticated": "Autentifikovan",
|
||||
"backToLogin": "Назад на пријаву",
|
||||
"beginCreateFirstUser": "На самом почетку креирај свог првог корисника",
|
||||
"changePassword": "Промени лозинку",
|
||||
@@ -177,6 +178,7 @@
|
||||
"deletedCountSuccessfully": "Успешно избрисано {{count}} {{label}}.",
|
||||
"deletedSuccessfully": "Успешно избрисано.",
|
||||
"deleting": "Брисање...",
|
||||
"depth": "Dubina",
|
||||
"descending": "Опадајуће",
|
||||
"deselectAllRows": "Деселектујте све редове",
|
||||
"document": "Dokument",
|
||||
@@ -195,6 +197,7 @@
|
||||
"error": "Грешка",
|
||||
"errors": "Грешке",
|
||||
"fallbackToDefaultLocale": "Враћање на задати језик",
|
||||
"false": "Lažno",
|
||||
"filter": "Филтер",
|
||||
"filterWhere": "Филтер {{label}} где",
|
||||
"filters": "Филтери",
|
||||
@@ -250,6 +253,7 @@
|
||||
"successfullyDuplicated": "{{label}} успешно дуплицирано.",
|
||||
"thisLanguage": "Српски (ћирилица)",
|
||||
"titleDeleted": "{{label}} \"{{title}}\" успешно обрисано.",
|
||||
"true": "Istinito",
|
||||
"unauthorized": "Нисте ауторизовани",
|
||||
"unsavedChangesDuplicate": "Имате несачуване промене. Да ли желите наставити са дуплицирањем?",
|
||||
"untitled": "Без наслова",
|
||||
@@ -373,4 +377,4 @@
|
||||
"viewingVersions": "Преглед верзија за {{entityLabel}} {{documentTitle}}",
|
||||
"viewingVersionsGlobal": "Преглед верзије за глобални {{entityLabel}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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": "Где фильтровать",
|
||||
"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": "Redan aktiverad",
|
||||
"alreadyLoggedIn": "Redan inloggad",
|
||||
"apiKey": "API Nyckel",
|
||||
"authenticated": "Autentiserad",
|
||||
"backToLogin": "Tillbaka till inloggningen",
|
||||
"beginCreateFirstUser": "För att börja, skapa din första användare.",
|
||||
"changePassword": "Byt Lösenord",
|
||||
@@ -177,6 +178,7 @@
|
||||
"deletedCountSuccessfully": "Raderade {{count}} {{label}} framgångsrikt.",
|
||||
"deletedSuccessfully": "Togs bort framgångsrikt.",
|
||||
"deleting": "Tar bort...",
|
||||
"depth": "Djup",
|
||||
"descending": "Fallande",
|
||||
"deselectAllRows": "Avmarkera alla rader",
|
||||
"document": "Dokument",
|
||||
@@ -195,6 +197,7 @@
|
||||
"error": "Fel",
|
||||
"errors": "Fel",
|
||||
"fallbackToDefaultLocale": "Återgång till standardlokalspråk",
|
||||
"false": "Falskt",
|
||||
"filter": "Filter",
|
||||
"filterWhere": "Filtrera {{label}} där",
|
||||
"filters": "Filter",
|
||||
@@ -250,6 +253,7 @@
|
||||
"successfullyDuplicated": "{{label}} duplicerades framgångsrikt.",
|
||||
"thisLanguage": "Svenska",
|
||||
"titleDeleted": "{{label}} \"{{title}}\" togs bort framgångsrikt.",
|
||||
"true": "Sann",
|
||||
"unauthorized": "Obehörig",
|
||||
"unsavedChangesDuplicate": "Du har osparade ändringar. Vill du fortsätta att duplicera?",
|
||||
"untitled": "Namnlös",
|
||||
@@ -373,4 +377,4 @@
|
||||
"viewingVersions": "Visar versioner för {{entityLabel}} {{documentTitle}}",
|
||||
"viewingVersionsGlobal": "Visa versioner för den globala {{entityLabel}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
"alreadyActivated": "เปิดใช้งานแล้ว",
|
||||
"alreadyLoggedIn": "ลงชื่อเข้าใช้แล้ว",
|
||||
"apiKey": "API Key",
|
||||
"authenticated": "ได้รับการตรวจสอบแล้ว",
|
||||
"backToLogin": "กลับไปหน้าเข้าสู่ระบบ",
|
||||
"beginCreateFirstUser": "สร้างผู้ใช้แรกเพื่อเริ่มใช้งาน",
|
||||
"changePassword": "เปลี่ยนรหัสผ่าน",
|
||||
@@ -177,6 +178,7 @@
|
||||
"deletedCountSuccessfully": "Deleted {{count}} {{label}} successfully.",
|
||||
"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": "กำลังดูเวอร์ชันของ global {{entityLabel}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
"alreadyActivated": "Hesap zaten etkinleştirildi",
|
||||
"alreadyLoggedIn": "Hesaba zaten giriş yapıldı",
|
||||
"apiKey": "API Anahtarı",
|
||||
"authenticated": "Doğrulandı",
|
||||
"backToLogin": "Giriş ekranına geri dön",
|
||||
"beginCreateFirstUser": "Başlamak için ilk kullanıcı hesabını oluşturun.",
|
||||
"changePassword": "Parolayı Değiştir",
|
||||
@@ -177,6 +178,7 @@
|
||||
"deletedCountSuccessfully": "{{count}} {{label}} başarıyla silindi.",
|
||||
"deletedSuccessfully": "Başarıyla silindi.",
|
||||
"deleting": "Siliniyor...",
|
||||
"depth": "Derinlik",
|
||||
"descending": "Azalan",
|
||||
"deselectAllRows": "Tüm satırların seçimini kaldır",
|
||||
"document": "Belge",
|
||||
@@ -195,6 +197,7 @@
|
||||
"error": "Hata",
|
||||
"errors": "Hatalar",
|
||||
"fallbackToDefaultLocale": "Varsayılan yerel ayara geri dönme",
|
||||
"false": "Yanlış",
|
||||
"filter": "Filtrele",
|
||||
"filterWhere": "{{label}} filtrele:",
|
||||
"filters": "Filtreler",
|
||||
@@ -250,6 +253,7 @@
|
||||
"successfullyDuplicated": "{{label}} başarıyla kopyalandı.",
|
||||
"thisLanguage": "Türkçe",
|
||||
"titleDeleted": "{{label}} {{title}} başarıyla silindi.",
|
||||
"true": "Doğru",
|
||||
"unauthorized": "Yetkisiz",
|
||||
"unsavedChangesDuplicate": "Kaydedilmemiş değişiklikler var. Çoğaltma işlemine devam etmek istiyor musunuz?",
|
||||
"untitled": "Başlıksız",
|
||||
@@ -373,4 +377,4 @@
|
||||
"viewingVersions": "{{entityLabel}} {{documentTitle}} için sürümler gösteriliyor",
|
||||
"viewingVersionsGlobal": "`Global {{entityLabel}} için sürümler gösteriliyor"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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": "Đã được kích hoạt",
|
||||
"alreadyLoggedIn": "Đã đăng nhập",
|
||||
"apiKey": "API Key",
|
||||
"authenticated": "Đã xác thực",
|
||||
"backToLogin": "Quay lại đăng nhập.",
|
||||
"beginCreateFirstUser": "Để bắt đầu, hãy tạo người dùng đầu tiên.",
|
||||
"changePassword": "Đổi mật khẩu",
|
||||
@@ -177,6 +178,7 @@
|
||||
"deletedCountSuccessfully": "Đã xóa thành công {{count}} {{label}}.",
|
||||
"deletedSuccessfully": "Đã xoá thành công.",
|
||||
"deleting": "Đang xóa...",
|
||||
"depth": "Độ sâu",
|
||||
"descending": "Xếp theo thứ tự giảm dần",
|
||||
"deselectAllRows": "Bỏ chọn tất cả các hàng",
|
||||
"document": "Tài liệu",
|
||||
@@ -195,6 +197,7 @@
|
||||
"error": "Lỗi",
|
||||
"errors": "Lỗi",
|
||||
"fallbackToDefaultLocale": "Ngôn ngữ mặc định",
|
||||
"false": "Sai",
|
||||
"filter": "Lọc",
|
||||
"filterWhere": "Lọc {{label}} với điều kiện:",
|
||||
"filters": "Bộ lọc",
|
||||
@@ -250,6 +253,7 @@
|
||||
"successfullyDuplicated": "{{label}} đã được sao chép thành công.",
|
||||
"thisLanguage": "Vietnamese (Tiếng Việt)",
|
||||
"titleDeleted": "{{label}} {{title}} đã được xóa thành công.",
|
||||
"true": "Thật",
|
||||
"unauthorized": "Không có quyền truy cập.",
|
||||
"unsavedChangesDuplicate": "Bạn chưa lưu các thay đổi. Bạn có muốn tiếp tục tạo bản sao?",
|
||||
"untitled": "Chưa có tiêu đề",
|
||||
@@ -373,4 +377,4 @@
|
||||
"viewingVersions": "Xem những phiên bản của {{entityLabel}} {{documentTitle}}",
|
||||
"viewingVersionsGlobal": "`Xem những phiên bản toàn thể (global) của {{entityLabel}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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": "無標題",
|
||||
|
||||
@@ -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}}的版本"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import sharp from 'sharp'
|
||||
|
||||
export const percentToPixel = (value, dimension) => {
|
||||
export const percentToPixel = (value: string, dimension: number): number => {
|
||||
if (!value) return 0
|
||||
return Math.floor((parseFloat(value) / 100) * dimension)
|
||||
}
|
||||
|
||||
@@ -8,7 +9,7 @@ export default async function cropImage({ cropData, dimensions, file }) {
|
||||
try {
|
||||
const { height, width, x, y } = cropData
|
||||
|
||||
const formattedCropData = {
|
||||
const formattedCropData: sharp.Region = {
|
||||
height: percentToPixel(height, dimensions.height),
|
||||
left: percentToPixel(x, dimensions.width),
|
||||
top: percentToPixel(y, dimensions.height),
|
||||
|
||||
@@ -11,7 +11,7 @@ import sharp from 'sharp'
|
||||
import type { Collection } from '../collections/config/types'
|
||||
import type { SanitizedConfig } from '../config/types'
|
||||
import type { PayloadRequest } from '../express/types'
|
||||
import type { FileData, FileToSave, ProbedImageSize } from './types'
|
||||
import type { FileData, FileToSave, ProbedImageSize, UploadEdits } from './types'
|
||||
|
||||
import { FileUploadError, MissingFile } from '../errors'
|
||||
import FileRetrievalError from '../errors/FileRetrievalError'
|
||||
@@ -28,6 +28,8 @@ type Args<T> = {
|
||||
collection: Collection
|
||||
config: SanitizedConfig
|
||||
data: T
|
||||
operation: 'create' | 'update'
|
||||
originalDoc?: T
|
||||
overwriteExistingFiles?: boolean
|
||||
req: PayloadRequest
|
||||
throwOnMissingFile?: boolean
|
||||
@@ -42,6 +44,8 @@ export const generateFileData = async <T>({
|
||||
collection: { config: collectionConfig },
|
||||
config,
|
||||
data,
|
||||
operation,
|
||||
originalDoc,
|
||||
overwriteExistingFiles,
|
||||
req,
|
||||
throwOnMissingFile,
|
||||
@@ -54,10 +58,23 @@ export const generateFileData = async <T>({
|
||||
}
|
||||
|
||||
let file = req.files?.file || undefined
|
||||
const { uploadEdits } = req.query || {}
|
||||
|
||||
const { disableLocalStorage, formatOptions, imageSizes, resizeOptions, staticDir, trimOptions } =
|
||||
collectionConfig.upload
|
||||
const uploadEdits = parseUploadEditsFromReqOrIncomingData({
|
||||
data,
|
||||
operation,
|
||||
originalDoc,
|
||||
req,
|
||||
})
|
||||
|
||||
const {
|
||||
disableLocalStorage,
|
||||
focalPoint: focalPointEnabled,
|
||||
formatOptions,
|
||||
imageSizes,
|
||||
resizeOptions,
|
||||
staticDir,
|
||||
trimOptions,
|
||||
} = collectionConfig.upload
|
||||
|
||||
let staticPath = staticDir
|
||||
if (staticDir.indexOf('/') !== 0) {
|
||||
@@ -234,9 +251,9 @@ export const generateFileData = async <T>({
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(imageSizes) && fileSupportsResize) {
|
||||
if (fileSupportsResize && (Array.isArray(imageSizes) || focalPointEnabled !== false)) {
|
||||
req.payloadUploadSizes = {}
|
||||
const { sizeData, sizesToSave } = await resizeAndTransformImageSizes({
|
||||
const { focalPoint, sizeData, sizesToSave } = await resizeAndTransformImageSizes({
|
||||
config: collectionConfig,
|
||||
dimensions: !cropData
|
||||
? dimensions
|
||||
@@ -250,13 +267,16 @@ export const generateFileData = async <T>({
|
||||
req,
|
||||
savedFilename: fsSafeName || file.name,
|
||||
staticPath,
|
||||
uploadEdits,
|
||||
})
|
||||
|
||||
fileData.sizes = sizeData
|
||||
fileData.focalX = focalPoint?.x
|
||||
fileData.focalY = focalPoint?.y
|
||||
filesToSave.push(...sizesToSave)
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
req.payload.logger.error({ err, msg: 'Error uploading file' })
|
||||
throw new FileUploadError(req.t)
|
||||
}
|
||||
|
||||
@@ -270,3 +290,50 @@ export const generateFileData = async <T>({
|
||||
files: filesToSave,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse upload edits from req or incoming data
|
||||
*/
|
||||
function parseUploadEditsFromReqOrIncomingData(args: {
|
||||
data: unknown
|
||||
operation: 'create' | 'update'
|
||||
originalDoc: unknown
|
||||
req: PayloadRequest
|
||||
}): UploadEdits {
|
||||
const { data, operation, originalDoc, req } = args
|
||||
|
||||
// Get intended focal point change from query string or incoming data
|
||||
const {
|
||||
uploadEdits = {},
|
||||
}: {
|
||||
uploadEdits?: UploadEdits
|
||||
} = req.query || {}
|
||||
|
||||
if (uploadEdits.focalPoint) return uploadEdits
|
||||
|
||||
const incomingData = data as FileData
|
||||
const origDoc = originalDoc as FileData
|
||||
|
||||
// If no change in focal point, return undefined.
|
||||
// This prevents a refocal operation triggered from admin, because it always sends the focal point.
|
||||
if (origDoc && incomingData.focalX === origDoc.focalX && incomingData.focalY === origDoc.focalY) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
if (incomingData.focalX && incomingData.focalY) {
|
||||
uploadEdits.focalPoint = {
|
||||
x: incomingData.focalX,
|
||||
y: incomingData.focalY,
|
||||
}
|
||||
return uploadEdits
|
||||
}
|
||||
|
||||
// If no focal point is set, default to center
|
||||
if (operation === 'create') {
|
||||
uploadEdits.focalPoint = {
|
||||
x: 50,
|
||||
y: 50,
|
||||
}
|
||||
}
|
||||
return uploadEdits
|
||||
}
|
||||
|
||||
@@ -25,56 +25,57 @@ const getBaseUploadFields = ({ collection, config }: Options): Field[] => {
|
||||
|
||||
const mimeType: Field = {
|
||||
name: 'mimeType',
|
||||
type: 'text',
|
||||
admin: {
|
||||
hidden: true,
|
||||
readOnly: true,
|
||||
},
|
||||
label: 'MIME Type',
|
||||
type: 'text',
|
||||
}
|
||||
|
||||
const url: Field = {
|
||||
name: 'url',
|
||||
type: 'text',
|
||||
admin: {
|
||||
hidden: true,
|
||||
readOnly: true,
|
||||
},
|
||||
label: 'URL',
|
||||
type: 'text',
|
||||
}
|
||||
|
||||
const width: Field = {
|
||||
name: 'width',
|
||||
type: 'number',
|
||||
admin: {
|
||||
hidden: true,
|
||||
readOnly: true,
|
||||
},
|
||||
label: labels['upload:width'],
|
||||
type: 'number',
|
||||
}
|
||||
|
||||
const height: Field = {
|
||||
name: 'height',
|
||||
type: 'number',
|
||||
admin: {
|
||||
hidden: true,
|
||||
readOnly: true,
|
||||
},
|
||||
label: labels['upload:height'],
|
||||
type: 'number',
|
||||
}
|
||||
|
||||
const filesize: Field = {
|
||||
name: 'filesize',
|
||||
type: 'number',
|
||||
admin: {
|
||||
hidden: true,
|
||||
readOnly: true,
|
||||
},
|
||||
label: labels['upload:fileSize'],
|
||||
type: 'number',
|
||||
}
|
||||
|
||||
const filename: Field = {
|
||||
name: 'filename',
|
||||
type: 'text',
|
||||
admin: {
|
||||
disableBulkEdit: true,
|
||||
hidden: true,
|
||||
@@ -82,7 +83,6 @@ const getBaseUploadFields = ({ collection, config }: Options): Field[] => {
|
||||
},
|
||||
index: true,
|
||||
label: labels['upload:fileName'],
|
||||
type: 'text',
|
||||
unique: true,
|
||||
}
|
||||
|
||||
@@ -115,15 +115,36 @@ const getBaseUploadFields = ({ collection, config }: Options): Field[] => {
|
||||
mimeType.validate = mimeTypeValidator(uploadOptions.mimeTypes)
|
||||
}
|
||||
|
||||
// Add focal point fields if not disabled
|
||||
if (
|
||||
uploadOptions.focalPoint !== false ||
|
||||
uploadOptions.imageSizes ||
|
||||
uploadOptions.resizeOptions
|
||||
) {
|
||||
uploadFields = uploadFields.concat(
|
||||
['focalX', 'focalY'].map((name) => {
|
||||
return {
|
||||
name,
|
||||
type: 'number',
|
||||
admin: {
|
||||
hidden: true,
|
||||
},
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
if (uploadOptions.imageSizes) {
|
||||
uploadFields = uploadFields.concat([
|
||||
{
|
||||
name: 'sizes',
|
||||
type: 'group',
|
||||
admin: {
|
||||
hidden: true,
|
||||
},
|
||||
fields: uploadOptions.imageSizes.map((size) => ({
|
||||
name: size.name,
|
||||
type: 'group',
|
||||
admin: {
|
||||
hidden: true,
|
||||
},
|
||||
@@ -157,13 +178,12 @@ const getBaseUploadFields = ({ collection, config }: Options): Field[] => {
|
||||
},
|
||||
],
|
||||
label: size.name,
|
||||
type: 'group',
|
||||
})),
|
||||
label: labels['upload:Sizes'],
|
||||
type: 'group',
|
||||
label: labels['upload:sizes'],
|
||||
},
|
||||
])
|
||||
}
|
||||
|
||||
return uploadFields
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ export const getExternalFile = async ({ data, req, uploadConfig }: Args): Promis
|
||||
const headers = uploadConfig.externalFileHeaderFilter
|
||||
? uploadConfig.externalFileHeaderFilter(headersToObject(req.headers))
|
||||
: {
|
||||
cookie: req.headers['cookie'],
|
||||
cookie: req.headers?.['cookie'],
|
||||
}
|
||||
|
||||
const res = await fetch(fileURL, {
|
||||
|
||||
@@ -6,10 +6,16 @@ import fs from 'fs'
|
||||
import sanitize from 'sanitize-filename'
|
||||
import sharp from 'sharp'
|
||||
|
||||
import type { UploadEdits } from '../admin/components/views/collections/Edit/types'
|
||||
import type { SanitizedCollectionConfig } from '../collections/config/types'
|
||||
import type { PayloadRequest } from '../express/types'
|
||||
import type { FileSize, FileSizes, FileToSave, ImageSize, ProbedImageSize } from './types'
|
||||
import type {
|
||||
FileSize,
|
||||
FileSizes,
|
||||
FileToSave,
|
||||
ImageSize,
|
||||
ProbedImageSize,
|
||||
UploadEdits,
|
||||
} from './types'
|
||||
|
||||
import { isNumber } from '../utilities/isNumber'
|
||||
import fileExists from './fileExists'
|
||||
@@ -19,17 +25,15 @@ type ResizeArgs = {
|
||||
dimensions: ProbedImageSize
|
||||
file: UploadedFile
|
||||
mimeType: string
|
||||
req: PayloadRequest & {
|
||||
query?: {
|
||||
uploadEdits?: UploadEdits
|
||||
}
|
||||
}
|
||||
req: PayloadRequest
|
||||
savedFilename: string
|
||||
staticPath: string
|
||||
uploadEdits?: UploadEdits
|
||||
}
|
||||
|
||||
/** Result from resizing and transforming the requested image sizes */
|
||||
type ImageSizesResult = {
|
||||
focalPoint?: UploadEdits['focalPoint']
|
||||
sizeData: FileSizes
|
||||
sizesToSave: FileToSave[]
|
||||
}
|
||||
@@ -70,6 +74,16 @@ const createImageName = (
|
||||
extension: string,
|
||||
) => `${outputImageName}-${width}x${height}.${extension}`
|
||||
|
||||
type CreateResultArgs = {
|
||||
filename?: FileSize['filename']
|
||||
filesize?: FileSize['filesize']
|
||||
height?: FileSize['height']
|
||||
mimeType?: FileSize['mimeType']
|
||||
name: string
|
||||
sizesToSave?: FileToSave[]
|
||||
width?: FileSize['width']
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the result object for the image resize operation based on the
|
||||
* provided parameters. If the name is not provided, an empty result object
|
||||
@@ -84,26 +98,28 @@ const createImageName = (
|
||||
* @param sizesToSave - the sizes to save
|
||||
* @returns the result object
|
||||
*/
|
||||
const createResult = (
|
||||
name: string,
|
||||
filename: FileSize['filename'] = null,
|
||||
width: FileSize['width'] = null,
|
||||
height: FileSize['height'] = null,
|
||||
filesize: FileSize['filesize'] = null,
|
||||
mimeType: FileSize['mimeType'] = null,
|
||||
sizesToSave: FileToSave[] = [],
|
||||
): ImageSizesResult => ({
|
||||
sizeData: {
|
||||
[name]: {
|
||||
filename,
|
||||
filesize,
|
||||
height,
|
||||
mimeType,
|
||||
width,
|
||||
const createResult = ({
|
||||
name,
|
||||
filename = null,
|
||||
filesize = null,
|
||||
height = null,
|
||||
mimeType = null,
|
||||
sizesToSave = [],
|
||||
width = null,
|
||||
}: CreateResultArgs): ImageSizesResult => {
|
||||
return {
|
||||
sizeData: {
|
||||
[name]: {
|
||||
filename,
|
||||
filesize,
|
||||
height,
|
||||
mimeType,
|
||||
width,
|
||||
},
|
||||
},
|
||||
},
|
||||
sizesToSave,
|
||||
})
|
||||
sizesToSave,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the image needs to be resized according to the requested dimensions
|
||||
@@ -215,10 +231,26 @@ export default async function resizeAndTransformImageSizes({
|
||||
req,
|
||||
savedFilename,
|
||||
staticPath,
|
||||
uploadEdits,
|
||||
}: ResizeArgs): Promise<ImageSizesResult> {
|
||||
const { imageSizes } = config.upload
|
||||
// Noting to resize here so return as early as possible
|
||||
if (!imageSizes) return { sizeData: {}, sizesToSave: [] }
|
||||
const { focalPoint: focalPointEnabled = true, imageSizes } = config.upload
|
||||
|
||||
// Focal point adjustments
|
||||
const incomingFocalPoint = uploadEdits.focalPoint
|
||||
? {
|
||||
x: isNumber(uploadEdits.focalPoint.x) ? Math.round(uploadEdits.focalPoint.x) : 50,
|
||||
y: isNumber(uploadEdits.focalPoint.y) ? Math.round(uploadEdits.focalPoint.y) : 50,
|
||||
}
|
||||
: undefined
|
||||
|
||||
const defaultResult: ImageSizesResult = {
|
||||
...(focalPointEnabled && incomingFocalPoint && { focalPoint: incomingFocalPoint }),
|
||||
sizeData: {},
|
||||
sizesToSave: [],
|
||||
}
|
||||
|
||||
// Nothing to resize here so return as early as possible
|
||||
if (!imageSizes) return defaultResult
|
||||
|
||||
const sharpBase = sharp(file.tempFilePath || file.data).rotate() // pass rotate() to auto-rotate based on EXIF data. https://github.com/payloadcms/payload/pull/3081
|
||||
|
||||
@@ -230,16 +262,13 @@ export default async function resizeAndTransformImageSizes({
|
||||
// skipped COMPLETELY and thus will not be included in the resulting images.
|
||||
// All further format/trim options will thus be skipped as well.
|
||||
if (preventResize(imageResizeConfig, dimensions)) {
|
||||
return createResult(imageResizeConfig.name)
|
||||
return createResult({ name: imageResizeConfig.name })
|
||||
}
|
||||
|
||||
const imageToResize = sharpBase.clone()
|
||||
let resized = imageToResize
|
||||
|
||||
if (
|
||||
req.query?.uploadEdits?.focalPoint &&
|
||||
applyPayloadAdjustments(imageResizeConfig, dimensions)
|
||||
) {
|
||||
if (incomingFocalPoint && applyPayloadAdjustments(imageResizeConfig, dimensions)) {
|
||||
const { height: resizeHeight, width: resizeWidth } = imageResizeConfig
|
||||
const resizeAspectRatio = resizeWidth / resizeHeight
|
||||
const originalAspectRatio = dimensions.width / dimensions.height
|
||||
@@ -252,27 +281,17 @@ export default async function resizeAndTransformImageSizes({
|
||||
})
|
||||
const { info: scaledImageInfo } = await scaledImage.toBuffer({ resolveWithObject: true })
|
||||
|
||||
// Focal point adjustments
|
||||
const focalPoint = {
|
||||
x: isNumber(req.query.uploadEdits.focalPoint?.x)
|
||||
? req.query.uploadEdits.focalPoint.x
|
||||
: 50,
|
||||
y: isNumber(req.query.uploadEdits.focalPoint?.y)
|
||||
? req.query.uploadEdits.focalPoint.y
|
||||
: 50,
|
||||
}
|
||||
|
||||
const safeResizeWidth = resizeWidth ?? scaledImageInfo.width
|
||||
const maxOffsetX = scaledImageInfo.width - safeResizeWidth
|
||||
const leftFocalEdge = Math.round(
|
||||
scaledImageInfo.width * (focalPoint.x / 100) - safeResizeWidth / 2,
|
||||
scaledImageInfo.width * (incomingFocalPoint.x / 100) - safeResizeWidth / 2,
|
||||
)
|
||||
const safeOffsetX = Math.min(Math.max(0, leftFocalEdge), maxOffsetX)
|
||||
|
||||
const safeResizeHeight = resizeHeight ?? scaledImageInfo.height
|
||||
const maxOffsetY = scaledImageInfo.height - safeResizeHeight
|
||||
const topFocalEdge = Math.round(
|
||||
scaledImageInfo.height * (focalPoint.y / 100) - safeResizeHeight / 2,
|
||||
scaledImageInfo.height * (incomingFocalPoint.y / 100) - safeResizeHeight / 2,
|
||||
)
|
||||
const safeOffsetY = Math.min(Math.max(0, topFocalEdge), maxOffsetY)
|
||||
|
||||
@@ -327,24 +346,21 @@ export default async function resizeAndTransformImageSizes({
|
||||
}
|
||||
|
||||
const { height, size, width } = bufferInfo
|
||||
return createResult(
|
||||
imageResizeConfig.name,
|
||||
imageNameWithDimensions,
|
||||
width,
|
||||
return createResult({
|
||||
name: imageResizeConfig.name,
|
||||
filename: imageNameWithDimensions,
|
||||
filesize: size,
|
||||
height,
|
||||
size,
|
||||
mimeInfo?.mime || mimeType,
|
||||
[{ buffer: bufferData, path: imagePath }],
|
||||
)
|
||||
mimeType: mimeInfo?.mime || mimeType,
|
||||
sizesToSave: [{ buffer: bufferData, path: imagePath }],
|
||||
width,
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
return results.reduce(
|
||||
(acc, result) => {
|
||||
Object.assign(acc.sizeData, result.sizeData)
|
||||
acc.sizesToSave.push(...result.sizesToSave)
|
||||
return acc
|
||||
},
|
||||
{ sizeData: {}, sizesToSave: [] },
|
||||
)
|
||||
return results.reduce((acc, result) => {
|
||||
Object.assign(acc.sizeData, result.sizeData)
|
||||
acc.sizesToSave.push(...result.sizesToSave)
|
||||
return acc
|
||||
}, defaultResult)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import type express from 'express'
|
||||
import type serveStatic from 'serve-static'
|
||||
import type { ResizeOptions, Sharp } from 'sharp'
|
||||
@@ -18,6 +17,8 @@ export type FileSizes = {
|
||||
export type FileData = {
|
||||
filename: string
|
||||
filesize: number
|
||||
focalX?: number
|
||||
focalY?: number
|
||||
height: number
|
||||
mimeType: string
|
||||
sizes: FileSizes
|
||||
@@ -122,3 +123,16 @@ export type FileToSave = {
|
||||
buffer: Buffer
|
||||
path: string
|
||||
}
|
||||
|
||||
export type UploadEdits = {
|
||||
crop?: {
|
||||
height?: number
|
||||
width?: number
|
||||
x?: number
|
||||
y?: number
|
||||
}
|
||||
focalPoint?: {
|
||||
x?: number
|
||||
y?: number
|
||||
}
|
||||
}
|
||||
|
||||
37
packages/plugin-relationship-object-ids/.eslintrc.js
Normal file
37
packages/plugin-relationship-object-ids/.eslintrc.js
Normal file
@@ -0,0 +1,37 @@
|
||||
/** @type {import('prettier').Config} */
|
||||
module.exports = {
|
||||
extends: ['@payloadcms'],
|
||||
overrides: [
|
||||
{
|
||||
extends: ['plugin:@typescript-eslint/disable-type-checked'],
|
||||
files: ['*.js', '*.cjs', '*.json', '*.md', '*.yml', '*.yaml'],
|
||||
},
|
||||
{
|
||||
files: ['package.json', 'tsconfig.json'],
|
||||
rules: {
|
||||
'perfectionist/sort-array-includes': 'off',
|
||||
'perfectionist/sort-astro-attributes': 'off',
|
||||
'perfectionist/sort-classes': 'off',
|
||||
'perfectionist/sort-enums': 'off',
|
||||
'perfectionist/sort-exports': 'off',
|
||||
'perfectionist/sort-imports': 'off',
|
||||
'perfectionist/sort-interfaces': 'off',
|
||||
'perfectionist/sort-jsx-props': 'off',
|
||||
'perfectionist/sort-keys': 'off',
|
||||
'perfectionist/sort-maps': 'off',
|
||||
'perfectionist/sort-named-exports': 'off',
|
||||
'perfectionist/sort-named-imports': 'off',
|
||||
'perfectionist/sort-object-types': 'off',
|
||||
'perfectionist/sort-objects': 'off',
|
||||
'perfectionist/sort-svelte-attributes': 'off',
|
||||
'perfectionist/sort-union-types': 'off',
|
||||
'perfectionist/sort-vue-attributes': 'off',
|
||||
},
|
||||
},
|
||||
],
|
||||
parserOptions: {
|
||||
project: ['./tsconfig.json'],
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
root: true,
|
||||
}
|
||||
7
packages/plugin-relationship-object-ids/.gitignore
vendored
Normal file
7
packages/plugin-relationship-object-ids/.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
node_modules
|
||||
.env
|
||||
dist
|
||||
demo/uploads
|
||||
build
|
||||
.DS_Store
|
||||
package-lock.json
|
||||
15
packages/plugin-relationship-object-ids/.swcrc
Normal file
15
packages/plugin-relationship-object-ids/.swcrc
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/swcrc",
|
||||
"sourceMaps": "inline",
|
||||
"jsc": {
|
||||
"target": "esnext",
|
||||
"parser": {
|
||||
"syntax": "typescript",
|
||||
"tsx": true,
|
||||
"dts": true
|
||||
}
|
||||
},
|
||||
"module": {
|
||||
"type": "commonjs"
|
||||
}
|
||||
}
|
||||
35
packages/plugin-relationship-object-ids/README.md
Normal file
35
packages/plugin-relationship-object-ids/README.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Payload Relationship ObjectID Plugin
|
||||
|
||||
This plugin automatically enables all Payload `relationship` and `upload` field types to be stored as `ObjectID`s in MongoDB.
|
||||
|
||||
Minimum required version of Payload: `1.9.5`
|
||||
|
||||
## What it does
|
||||
|
||||
It injects a `beforeChange` field hook into each `relationship` and `upload` field, which converts string-based IDs to `ObjectID`s immediately prior to storage.
|
||||
|
||||
#### Usage
|
||||
|
||||
Simply import and install the plugin to make it work:
|
||||
|
||||
```ts
|
||||
import { relationshipsAsObjectID } from '@payloadcms/plugin-relationship-object-ids'
|
||||
import { buildConfig } from 'payload/config'
|
||||
|
||||
export default buildConfig({
|
||||
// your config here
|
||||
plugins: [
|
||||
// Call the plugin within your `plugins` array
|
||||
relationshipsAsObjectID()
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
### Migration
|
||||
|
||||
Note - this plugin will only store newly created or resaved documents' relations as `ObjectID`s. It will not modify any of your existing data. If you'd like to convert existing data into an `ObjectID` format, you should write a migration script to loop over all documents in your database and then simply resave each one.
|
||||
|
||||
### Support
|
||||
|
||||
If you need help with this plugin, [join our Discord](https://t.co/30APlsQUPB) and we'd be happy to give you a hand.
|
||||
|
||||
40
packages/plugin-relationship-object-ids/package.json
Normal file
40
packages/plugin-relationship-object-ids/package.json
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "@payloadcms/plugin-relationship-object-ids",
|
||||
"version": "0.0.4",
|
||||
"homepage:": "https://payloadcms.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/payloadcms/payload.git",
|
||||
"directory": "packages/plugin-relationship-object-ids"
|
||||
},
|
||||
"description": "A Payload plugin to store all relationship IDs as ObjectIDs",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "pnpm copyfiles && pnpm build:swc && pnpm build:types",
|
||||
"build:swc": "swc ./src -d ./dist --config-file .swcrc",
|
||||
"build:types": "tsc --emitDeclarationOnly --outDir dist",
|
||||
"copyfiles": "copyfiles -u 1 \"src/**/*.{html,css,scss,ttf,woff,woff2,eot,svg,jpg,png,json}\" dist/",
|
||||
"lint": "eslint src",
|
||||
"lint:fix": "eslint --fix --ext .ts,.tsx src",
|
||||
"clean": "rimraf {dist,*.tsbuildinfo}",
|
||||
"prepublishOnly": "pnpm clean && pnpm build"
|
||||
},
|
||||
"author": "dev@payloadcms.com",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"payload": "workspace:*",
|
||||
"mongoose": "6.12.3"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"*.js",
|
||||
"*.d.ts",
|
||||
"!.prettierrc.js"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@payloadcms/eslint-config": "workspace:*",
|
||||
"payload": "workspace:*",
|
||||
"webpack": "^5.78.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
import type { CollectionConfig, FieldHook, RelationshipField, UploadField } from 'payload/types'
|
||||
import mongoose from 'mongoose'
|
||||
import type { Config } from 'payload/config'
|
||||
import { fieldAffectsData } from 'payload/dist/fields/config/types'
|
||||
|
||||
const convertValue = ({
|
||||
value,
|
||||
relatedCollection,
|
||||
}: {
|
||||
value: string | number
|
||||
relatedCollection: CollectionConfig
|
||||
}): mongoose.Types.ObjectId | string | number => {
|
||||
const customIDField = relatedCollection.fields.find(
|
||||
(field) => fieldAffectsData(field) && field.name === 'id',
|
||||
)
|
||||
|
||||
if (!customIDField) return new mongoose.Types.ObjectId(value)
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
interface RelationObject {
|
||||
value: string | number
|
||||
relationTo: string
|
||||
}
|
||||
|
||||
function isValidRelationObject(value: unknown): value is RelationObject {
|
||||
return typeof value === 'object' && value !== null && 'relationTo' in value && 'value' in value
|
||||
}
|
||||
|
||||
export const getBeforeChangeHook =
|
||||
({ config, field }: { config: Config; field: RelationshipField | UploadField }): FieldHook =>
|
||||
({ value }) => {
|
||||
let relatedCollection: CollectionConfig | undefined
|
||||
|
||||
const hasManyRelations = typeof field.relationTo !== 'string'
|
||||
|
||||
if (!hasManyRelations) {
|
||||
relatedCollection = config.collections?.find(({ slug }) => slug === field.relationTo)
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
return value.map((val) => {
|
||||
// Handle has many
|
||||
if (relatedCollection && val && (typeof val === 'string' || typeof val === 'number')) {
|
||||
return convertValue({
|
||||
relatedCollection,
|
||||
value: val,
|
||||
})
|
||||
}
|
||||
|
||||
// Handle has many - polymorphic
|
||||
if (isValidRelationObject(val)) {
|
||||
const relatedCollectionForSingleValue = config.collections?.find(
|
||||
({ slug }) => slug === val.relationTo,
|
||||
)
|
||||
|
||||
if (relatedCollectionForSingleValue) {
|
||||
return {
|
||||
relationTo: val.relationTo,
|
||||
value: convertValue({
|
||||
relatedCollection: relatedCollectionForSingleValue,
|
||||
value: val.value,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return val
|
||||
})
|
||||
}
|
||||
|
||||
// Handle has one - polymorphic
|
||||
if (isValidRelationObject(value)) {
|
||||
relatedCollection = config.collections?.find(({ slug }) => slug === value.relationTo)
|
||||
|
||||
if (relatedCollection) {
|
||||
return {
|
||||
relationTo: value.relationTo,
|
||||
value: convertValue({ relatedCollection, value: value.value }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle has one
|
||||
if (relatedCollection && value && (typeof value === 'string' || typeof value === 'number')) {
|
||||
return convertValue({
|
||||
relatedCollection,
|
||||
value,
|
||||
})
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
86
packages/plugin-relationship-object-ids/src/index.ts
Normal file
86
packages/plugin-relationship-object-ids/src/index.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import type { Config } from 'payload/config'
|
||||
import type { Field } from 'payload/types'
|
||||
import { extendWebpackConfig } from './webpack'
|
||||
import { getBeforeChangeHook } from './hooks/beforeChange'
|
||||
|
||||
const traverseFields = ({ config, fields }: { fields: Field[]; config: Config }): Field[] => {
|
||||
return fields.map((field) => {
|
||||
if (field.type === 'relationship' || field.type === 'upload') {
|
||||
return {
|
||||
...field,
|
||||
hooks: {
|
||||
...(field.hooks || {}),
|
||||
beforeChange: [
|
||||
...(field.hooks?.beforeChange || []),
|
||||
getBeforeChangeHook({ config, field }),
|
||||
],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if ('fields' in field) {
|
||||
return {
|
||||
...field,
|
||||
fields: traverseFields({ config, fields: field.fields }),
|
||||
}
|
||||
}
|
||||
|
||||
if (field.type === 'tabs') {
|
||||
return {
|
||||
...field,
|
||||
tabs: field.tabs.map((tab) => {
|
||||
return {
|
||||
...tab,
|
||||
fields: traverseFields({ config, fields: tab.fields }),
|
||||
}
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
if (field.type === 'blocks') {
|
||||
return {
|
||||
...field,
|
||||
blocks: field.blocks.map((block) => {
|
||||
return {
|
||||
...block,
|
||||
fields: traverseFields({ config, fields: block.fields }),
|
||||
}
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
return field
|
||||
})
|
||||
}
|
||||
|
||||
export const relationshipsAsObjectID =
|
||||
(/** Possible args in the future */) =>
|
||||
(config: Config): Config => {
|
||||
const webpack = extendWebpackConfig(config)
|
||||
|
||||
return {
|
||||
...config,
|
||||
admin: {
|
||||
...(config.admin || {}),
|
||||
webpack,
|
||||
},
|
||||
collections: (config.collections || []).map((collection) => {
|
||||
return {
|
||||
...collection,
|
||||
fields: traverseFields({
|
||||
config,
|
||||
fields: collection.fields,
|
||||
}),
|
||||
}
|
||||
}),
|
||||
globals: (config.globals || []).map((global) => {
|
||||
return {
|
||||
...global,
|
||||
fields: traverseFields({
|
||||
config,
|
||||
fields: global.fields,
|
||||
}),
|
||||
}
|
||||
}),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export default 'file-stub'
|
||||
23
packages/plugin-relationship-object-ids/src/webpack.ts
Normal file
23
packages/plugin-relationship-object-ids/src/webpack.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import type { Config } from 'payload/config'
|
||||
import path from 'path'
|
||||
import type { Configuration as WebpackConfig } from 'webpack'
|
||||
|
||||
export const extendWebpackConfig =
|
||||
(config: Config): ((webpackConfig: WebpackConfig) => WebpackConfig) =>
|
||||
(webpackConfig) => {
|
||||
const existingWebpackConfig =
|
||||
typeof config.admin?.webpack === 'function'
|
||||
? config.admin.webpack(webpackConfig)
|
||||
: webpackConfig
|
||||
|
||||
return {
|
||||
...existingWebpackConfig,
|
||||
resolve: {
|
||||
...(existingWebpackConfig.resolve || {}),
|
||||
alias: {
|
||||
...(existingWebpackConfig.resolve?.alias ? existingWebpackConfig.resolve.alias : {}),
|
||||
mongoose: path.resolve(__dirname, './mocks/fileStub.js'),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
24
packages/plugin-relationship-object-ids/tsconfig.json
Normal file
24
packages/plugin-relationship-object-ids/tsconfig.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"composite": true, // Make sure typescript knows that this module depends on their references
|
||||
"noEmit": false /* Do not emit outputs. */,
|
||||
"emitDeclarationOnly": true,
|
||||
"outDir": "./dist" /* Specify an output folder for all emitted files. */,
|
||||
"rootDir": "./src" /* Specify the root folder within your source files. */
|
||||
},
|
||||
"exclude": [
|
||||
"dist",
|
||||
"build",
|
||||
"tests",
|
||||
"test",
|
||||
"node_modules",
|
||||
".eslintrc.js",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.spec.jsx",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.spec.tsx"
|
||||
],
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.d.ts", "src/**/*.json"],
|
||||
"references": [{ "path": "../payload" }]
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/richtext-lexical",
|
||||
"version": "0.10.0",
|
||||
"version": "0.11.1",
|
||||
"description": "The officially supported Lexical richtext adapter for Payload",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Block, BlockField, FieldWithRichTextRequiredEditor } from 'payload/types'
|
||||
import type { Block, BlockField, Field } from 'payload/types'
|
||||
|
||||
import { baseBlockFields, sanitizeFields } from 'payload/config'
|
||||
import { fieldsToJSONSchema, formatLabels, getTranslation } from 'payload/utilities'
|
||||
@@ -12,12 +12,8 @@ import { INSERT_BLOCK_COMMAND } from './plugin/commands'
|
||||
import { blockPopulationPromiseHOC } from './populationPromise'
|
||||
import { blockValidationHOC } from './validate'
|
||||
|
||||
export type LexicalBlock = Omit<Block, 'fields'> & {
|
||||
fields: FieldWithRichTextRequiredEditor[]
|
||||
}
|
||||
|
||||
export type BlocksFeatureProps = {
|
||||
blocks: LexicalBlock[]
|
||||
blocks: Block[]
|
||||
}
|
||||
|
||||
export const BlocksFeature = (props?: BlocksFeatureProps): FeatureProvider => {
|
||||
@@ -26,9 +22,7 @@ export const BlocksFeature = (props?: BlocksFeatureProps): FeatureProvider => {
|
||||
props.blocks = props.blocks.map((block) => {
|
||||
const blockCopy = cloneDeep(block)
|
||||
|
||||
blockCopy.fields = blockCopy.fields.concat(
|
||||
baseBlockFields as FieldWithRichTextRequiredEditor[],
|
||||
)
|
||||
blockCopy.fields = blockCopy.fields.concat(baseBlockFields)
|
||||
blockCopy.labels = !blockCopy.labels ? formatLabels(blockCopy.slug) : blockCopy.labels
|
||||
return blockCopy
|
||||
})
|
||||
|
||||
@@ -31,11 +31,12 @@ export function BlocksPlugin(): JSX.Element | null {
|
||||
INSERT_BLOCK_COMMAND,
|
||||
(payload: InsertBlockPayload) => {
|
||||
editor.update(() => {
|
||||
const blockNode = $createBlockNode(payload)
|
||||
|
||||
const selection = $getSelection() || $getPreviousSelection()
|
||||
|
||||
if ($isRangeSelection(selection)) {
|
||||
const blockNode = $createBlockNode(payload)
|
||||
$insertNodeToNearestRoot(blockNode)
|
||||
|
||||
const { focus } = selection
|
||||
const focusNode = focus.getNode()
|
||||
|
||||
@@ -51,8 +52,6 @@ export function BlocksPlugin(): JSX.Element | null {
|
||||
) {
|
||||
focusNode.remove()
|
||||
}
|
||||
|
||||
$insertNodeToNearestRoot(blockNode)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { i18n } from 'i18next'
|
||||
import type { SanitizedConfig } from 'payload/config'
|
||||
import type { FieldWithRichTextRequiredEditor } from 'payload/types'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
import { $findMatchingParent } from '@lexical/utils'
|
||||
import { $getSelection, $isRangeSelection } from 'lexical'
|
||||
@@ -46,12 +46,8 @@ export type LinkFeatureProps = ExclusiveLinkCollectionsProps & {
|
||||
* displayed in the link editor drawer.
|
||||
*/
|
||||
fields?:
|
||||
| ((args: {
|
||||
config: SanitizedConfig
|
||||
defaultFields: FieldWithRichTextRequiredEditor[]
|
||||
i18n: i18n
|
||||
}) => FieldWithRichTextRequiredEditor[])
|
||||
| FieldWithRichTextRequiredEditor[]
|
||||
| ((args: { config: SanitizedConfig; defaultFields: Field[]; i18n: i18n }) => Field[])
|
||||
| Field[]
|
||||
/**
|
||||
* Sets a maximum population depth for the internal doc default field of link, regardless of the remaining depth when the field is reached.
|
||||
* This behaves exactly like the maxDepth properties of relationship and upload fields.
|
||||
|
||||
@@ -46,11 +46,12 @@ export function RelationshipPlugin(props?: RelationshipFeatureProps): JSX.Elemen
|
||||
return editor.registerCommand<RelationshipData>(
|
||||
INSERT_RELATIONSHIP_COMMAND,
|
||||
(payload) => {
|
||||
const relationshipNode = $createRelationshipNode(payload)
|
||||
|
||||
const selection = $getSelection() || $getPreviousSelection()
|
||||
|
||||
if ($isRangeSelection(selection)) {
|
||||
const relationshipNode = $createRelationshipNode(payload)
|
||||
$insertNodeToNearestRoot(relationshipNode)
|
||||
|
||||
const { focus } = selection
|
||||
const focusNode = focus.getNode()
|
||||
|
||||
@@ -66,8 +67,6 @@ export function RelationshipPlugin(props?: RelationshipFeatureProps): JSX.Elemen
|
||||
) {
|
||||
focusNode.remove()
|
||||
}
|
||||
|
||||
$insertNodeToNearestRoot(relationshipNode)
|
||||
}
|
||||
|
||||
return true
|
||||
|
||||
@@ -57,7 +57,7 @@ export const ExtraFieldsUploadDrawer: React.FC<
|
||||
const fieldSchema = useMemo(() => {
|
||||
const fieldSchemaUnSanitized = (
|
||||
editorConfig?.resolvedFeatureMap.get('upload')?.props as UploadFeatureProps
|
||||
)?.collections?.[relatedCollection.slug].fields
|
||||
)?.collections?.[relatedCollection.slug]?.fields
|
||||
|
||||
const validRelationships = config.collections.map((c) => c.slug) || []
|
||||
// Sanitize custom fields here
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { FieldWithRichTextRequiredEditor } from 'payload/types'
|
||||
import type { Field } from 'payload/types'
|
||||
|
||||
import payload from 'payload'
|
||||
|
||||
@@ -15,7 +15,7 @@ import { uploadValidation } from './validate'
|
||||
export type UploadFeatureProps = {
|
||||
collections: {
|
||||
[collection: string]: {
|
||||
fields: FieldWithRichTextRequiredEditor[]
|
||||
fields: Field[]
|
||||
}
|
||||
}
|
||||
/**
|
||||
|
||||
@@ -37,19 +37,20 @@ export function UploadPlugin(): JSX.Element | null {
|
||||
INSERT_UPLOAD_COMMAND,
|
||||
(payload: InsertUploadPayload) => {
|
||||
editor.update(() => {
|
||||
const uploadNode = $createUploadNode({
|
||||
data: {
|
||||
fields: payload.fields,
|
||||
relationTo: payload.relationTo,
|
||||
value: {
|
||||
id: payload.id,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const selection = $getSelection() || $getPreviousSelection()
|
||||
|
||||
if ($isRangeSelection(selection)) {
|
||||
const uploadNode = $createUploadNode({
|
||||
data: {
|
||||
fields: payload.fields,
|
||||
relationTo: payload.relationTo,
|
||||
value: {
|
||||
id: payload.id,
|
||||
},
|
||||
},
|
||||
})
|
||||
$insertNodeToNearestRoot(uploadNode)
|
||||
|
||||
const { focus } = selection
|
||||
const focusNode = focus.getNode()
|
||||
|
||||
@@ -65,8 +66,6 @@ export function UploadPlugin(): JSX.Element | null {
|
||||
) {
|
||||
focusNode.remove()
|
||||
}
|
||||
|
||||
$insertNodeToNearestRoot(uploadNode)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -220,7 +220,7 @@ export function lexicalEditor(props?: LexicalEditorProps): LexicalRichTextAdapte
|
||||
}
|
||||
|
||||
export { BlockQuoteFeature } from './field/features/BlockQuote'
|
||||
export { BlocksFeature, type BlocksFeatureProps, type LexicalBlock } from './field/features/Blocks'
|
||||
export { BlocksFeature, type BlocksFeatureProps } from './field/features/Blocks'
|
||||
export {
|
||||
$createBlockNode,
|
||||
$isBlockNode,
|
||||
|
||||
7147
pnpm-lock.yaml
generated
7147
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,4 @@
|
||||
import type { ArrayField } from '../../../../packages/payload/src/fields/config/types'
|
||||
import type { LexicalBlock } from '../../../../packages/richtext-lexical/src'
|
||||
import type { ArrayField, Block } from '../../../../packages/payload/src/fields/config/types'
|
||||
|
||||
import { lexicalEditor } from '../../../../packages/richtext-lexical/src'
|
||||
import { textFieldsSlug } from '../Text/shared'
|
||||
@@ -18,7 +17,7 @@ export const BlockColumns = ({ name }: { name: string }): ArrayField => ({
|
||||
},
|
||||
],
|
||||
})
|
||||
export const ConditionalLayoutBlock: LexicalBlock = {
|
||||
export const ConditionalLayoutBlock: Block = {
|
||||
fields: [
|
||||
{
|
||||
label: 'Layout',
|
||||
@@ -62,7 +61,7 @@ export const ConditionalLayoutBlock: LexicalBlock = {
|
||||
slug: 'conditionalLayout',
|
||||
}
|
||||
|
||||
export const TextBlock: LexicalBlock = {
|
||||
export const TextBlock: Block = {
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
@@ -73,7 +72,7 @@ export const TextBlock: LexicalBlock = {
|
||||
slug: 'text',
|
||||
}
|
||||
|
||||
export const RadioButtonsBlock: LexicalBlock = {
|
||||
export const RadioButtonsBlock: Block = {
|
||||
interfaceName: 'LexicalBlocksRadioButtonsBlock',
|
||||
fields: [
|
||||
{
|
||||
@@ -98,7 +97,7 @@ export const RadioButtonsBlock: LexicalBlock = {
|
||||
slug: 'radioButtons',
|
||||
}
|
||||
|
||||
export const RichTextBlock: LexicalBlock = {
|
||||
export const RichTextBlock: Block = {
|
||||
fields: [
|
||||
{
|
||||
name: 'richText',
|
||||
@@ -109,7 +108,7 @@ export const RichTextBlock: LexicalBlock = {
|
||||
slug: 'richText',
|
||||
}
|
||||
|
||||
export const UploadAndRichTextBlock: LexicalBlock = {
|
||||
export const UploadAndRichTextBlock: Block = {
|
||||
fields: [
|
||||
{
|
||||
name: 'upload',
|
||||
@@ -126,7 +125,7 @@ export const UploadAndRichTextBlock: LexicalBlock = {
|
||||
slug: 'uploadAndRichText',
|
||||
}
|
||||
|
||||
export const RelationshipHasManyBlock: LexicalBlock = {
|
||||
export const RelationshipHasManyBlock: Block = {
|
||||
fields: [
|
||||
{
|
||||
name: 'rel',
|
||||
@@ -138,7 +137,7 @@ export const RelationshipHasManyBlock: LexicalBlock = {
|
||||
],
|
||||
slug: 'relationshipHasManyBlock',
|
||||
}
|
||||
export const RelationshipBlock: LexicalBlock = {
|
||||
export const RelationshipBlock: Block = {
|
||||
fields: [
|
||||
{
|
||||
name: 'rel',
|
||||
@@ -150,7 +149,7 @@ export const RelationshipBlock: LexicalBlock = {
|
||||
slug: 'relationshipBlock',
|
||||
}
|
||||
|
||||
export const SelectFieldBlock: LexicalBlock = {
|
||||
export const SelectFieldBlock: Block = {
|
||||
fields: [
|
||||
{
|
||||
name: 'select',
|
||||
@@ -182,7 +181,7 @@ export const SelectFieldBlock: LexicalBlock = {
|
||||
slug: 'select',
|
||||
}
|
||||
|
||||
export const SubBlockBlock: LexicalBlock = {
|
||||
export const SubBlockBlock: Block = {
|
||||
slug: 'subBlock',
|
||||
fields: [
|
||||
{
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { LexicalBlock } from '../../../../packages/richtext-lexical/src'
|
||||
import type { Block } from 'payload/types'
|
||||
|
||||
import { lexicalEditor } from '../../../../packages/richtext-lexical/src'
|
||||
|
||||
export const TextBlock: LexicalBlock = {
|
||||
export const TextBlock: Block = {
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
@@ -13,7 +13,7 @@ export const TextBlock: LexicalBlock = {
|
||||
slug: 'text',
|
||||
}
|
||||
|
||||
export const UploadAndRichTextBlock: LexicalBlock = {
|
||||
export const UploadAndRichTextBlock: Block = {
|
||||
fields: [
|
||||
{
|
||||
name: 'upload',
|
||||
@@ -30,7 +30,7 @@ export const UploadAndRichTextBlock: LexicalBlock = {
|
||||
slug: 'uploadAndRichText',
|
||||
}
|
||||
|
||||
export const RelationshipBlock: LexicalBlock = {
|
||||
export const RelationshipBlock: Block = {
|
||||
fields: [
|
||||
{
|
||||
name: 'rel',
|
||||
@@ -42,7 +42,7 @@ export const RelationshipBlock: LexicalBlock = {
|
||||
slug: 'relationshipBlock',
|
||||
}
|
||||
|
||||
export const SelectFieldBlock: LexicalBlock = {
|
||||
export const SelectFieldBlock: Block = {
|
||||
fields: [
|
||||
{
|
||||
name: 'select',
|
||||
|
||||
@@ -1144,7 +1144,7 @@ describe('fields', () => {
|
||||
|
||||
// Fill values and click Confirm
|
||||
await editLinkModal.locator('#field-text').fill('link text')
|
||||
await editLinkModal.locator('label[for="field-linkType-custom"]').click()
|
||||
await editLinkModal.locator('label[for="field-linkType-custom-2"]').click()
|
||||
await editLinkModal.locator('#field-url').fill('')
|
||||
await wait(200)
|
||||
await editLinkModal.locator('button[type="submit"]').click()
|
||||
@@ -1167,7 +1167,7 @@ describe('fields', () => {
|
||||
|
||||
// Fill values and click Confirm
|
||||
await editLinkModal.locator('#field-text').fill('link text')
|
||||
await editLinkModal.locator('label[for="field-linkType-custom"]').click()
|
||||
await editLinkModal.locator('label[for="field-linkType-custom-2"]').click()
|
||||
await editLinkModal.locator('#field-url').fill('https://payloadcms.com')
|
||||
await wait(200)
|
||||
await editLinkModal.locator('button[type="submit"]').click()
|
||||
@@ -1193,7 +1193,7 @@ describe('fields', () => {
|
||||
|
||||
// Fill values and click Confirm
|
||||
await editLinkModal.locator('#field-text').fill('link text')
|
||||
await editLinkModal.locator('label[for="field-linkType-internal"]').click()
|
||||
await editLinkModal.locator('label[for="field-linkType-internal-2"]').click()
|
||||
await editLinkModal.locator('#field-doc .rs__control').click()
|
||||
await page.keyboard.type('dev@')
|
||||
await editLinkModal
|
||||
@@ -1266,7 +1266,7 @@ describe('fields', () => {
|
||||
const editLinkModal = page.locator('[id^=drawer_1_rich-text-link-]')
|
||||
await expect(editLinkModal).toBeVisible()
|
||||
|
||||
await editLinkModal.locator('label[for="field-linkType-internal"]').click()
|
||||
await editLinkModal.locator('label[for="field-linkType-internal-2"]').click()
|
||||
await editLinkModal.locator('.relationship__wrap .rs__control').click()
|
||||
|
||||
const menu = page.locator('.relationship__wrap .rs__menu')
|
||||
|
||||
1
test/plugin-relationship-object-ids/.gitignore
vendored
Normal file
1
test/plugin-relationship-object-ids/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
uploads
|
||||
129
test/plugin-relationship-object-ids/config.ts
Normal file
129
test/plugin-relationship-object-ids/config.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
import path from 'path'
|
||||
|
||||
import { relationshipsAsObjectID } from '../../packages/plugin-relationship-object-ids/src'
|
||||
import { buildConfigWithDefaults } from '../buildConfigWithDefaults'
|
||||
|
||||
export default buildConfigWithDefaults({
|
||||
collections: [
|
||||
{
|
||||
slug: 'uploads',
|
||||
upload: true,
|
||||
fields: [],
|
||||
},
|
||||
{
|
||||
slug: 'pages',
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'posts',
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'relations',
|
||||
fields: [
|
||||
{
|
||||
name: 'hasOne',
|
||||
type: 'relationship',
|
||||
relationTo: 'posts',
|
||||
filterOptions: ({ id }) => ({ id: { not_equals: id } }),
|
||||
},
|
||||
{
|
||||
name: 'hasOnePoly',
|
||||
type: 'relationship',
|
||||
relationTo: ['pages', 'posts'],
|
||||
},
|
||||
{
|
||||
name: 'hasMany',
|
||||
type: 'relationship',
|
||||
relationTo: 'posts',
|
||||
hasMany: true,
|
||||
},
|
||||
{
|
||||
name: 'hasManyPoly',
|
||||
type: 'relationship',
|
||||
relationTo: ['pages', 'posts'],
|
||||
hasMany: true,
|
||||
},
|
||||
{
|
||||
name: 'upload',
|
||||
type: 'upload',
|
||||
relationTo: 'uploads',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
plugins: [relationshipsAsObjectID()],
|
||||
onInit: async (payload) => {
|
||||
if (payload.db.name === 'mongoose') {
|
||||
await payload.create({
|
||||
collection: 'users',
|
||||
data: {
|
||||
email: 'dev@payloadcms.com',
|
||||
password: 'test',
|
||||
},
|
||||
})
|
||||
|
||||
const page = await payload.create({
|
||||
collection: 'pages',
|
||||
data: {
|
||||
title: 'page',
|
||||
},
|
||||
})
|
||||
|
||||
const post1 = await payload.create({
|
||||
collection: 'posts',
|
||||
data: {
|
||||
title: 'post 1',
|
||||
},
|
||||
})
|
||||
|
||||
const post2 = await payload.create({
|
||||
collection: 'posts',
|
||||
data: {
|
||||
title: 'post 2',
|
||||
},
|
||||
})
|
||||
|
||||
const upload = await payload.create({
|
||||
collection: 'uploads',
|
||||
data: {},
|
||||
filePath: path.resolve(__dirname, './payload-logo.png'),
|
||||
})
|
||||
|
||||
await payload.create({
|
||||
collection: 'relations',
|
||||
depth: 0,
|
||||
data: {
|
||||
hasOne: post1.id,
|
||||
hasOnePoly: { relationTo: 'pages', value: page.id },
|
||||
hasMany: [post1.id, post2.id],
|
||||
hasManyPoly: [
|
||||
{ relationTo: 'posts', value: post1.id },
|
||||
{ relationTo: 'pages', value: page.id },
|
||||
],
|
||||
upload: upload.id,
|
||||
},
|
||||
})
|
||||
|
||||
await payload.create({
|
||||
collection: 'relations',
|
||||
depth: 0,
|
||||
data: {
|
||||
hasOnePoly: { relationTo: 'pages', value: page.id },
|
||||
},
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
103
test/plugin-relationship-object-ids/int.spec.ts
Normal file
103
test/plugin-relationship-object-ids/int.spec.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
/* eslint-disable jest/no-if */
|
||||
import payload from '../../packages/payload/src'
|
||||
import { initPayloadTest } from '../helpers/configHelpers'
|
||||
|
||||
describe('Relationship Object IDs Plugin', () => {
|
||||
let relations: any
|
||||
let posts: any
|
||||
|
||||
beforeAll(async () => {
|
||||
await initPayloadTest({ __dirname, init: { local: true } })
|
||||
})
|
||||
|
||||
it('seeds data accordingly', async () => {
|
||||
if (payload.db.name === 'mongoose') {
|
||||
const relationsQuery = await payload.find({
|
||||
collection: 'relations',
|
||||
sort: 'createdAt',
|
||||
})
|
||||
|
||||
relations = relationsQuery.docs
|
||||
|
||||
const postsQuery = await payload.find({
|
||||
collection: 'posts',
|
||||
sort: 'createdAt',
|
||||
})
|
||||
|
||||
posts = postsQuery.docs
|
||||
|
||||
expect(relationsQuery.totalDocs).toEqual(2)
|
||||
expect(postsQuery.totalDocs).toEqual(2)
|
||||
}
|
||||
})
|
||||
|
||||
it('stores relations as object ids', async () => {
|
||||
if (payload.db.name === 'mongoose') {
|
||||
const docs = await payload.db.collections.relations.find()
|
||||
expect(typeof docs[0].hasOne).toBe('object')
|
||||
expect(typeof docs[0].hasOnePoly.value).toBe('object')
|
||||
expect(typeof docs[0].hasMany[0]).toBe('object')
|
||||
expect(typeof docs[0].hasManyPoly[0].value).toBe('object')
|
||||
expect(typeof docs[0].upload).toBe('object')
|
||||
}
|
||||
})
|
||||
|
||||
it('can query by relationship id', async () => {
|
||||
if (payload.db.name === 'mongoose') {
|
||||
const { totalDocs } = await payload.find({
|
||||
collection: 'relations',
|
||||
where: {
|
||||
hasOne: {
|
||||
equals: posts[0].id,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
expect(totalDocs).toStrictEqual(1)
|
||||
}
|
||||
})
|
||||
|
||||
it('populates relations', () => {
|
||||
if (payload.db.name === 'mongoose') {
|
||||
const populatedPostTitle =
|
||||
// eslint-disable-next-line jest/no-if
|
||||
typeof relations[0].hasOne === 'object' ? relations[0].hasOne.title : undefined
|
||||
expect(populatedPostTitle).toBeDefined()
|
||||
|
||||
const populatedUploadFilename =
|
||||
typeof relations[0].upload === 'object' ? relations[0].upload.filename : undefined
|
||||
|
||||
expect(populatedUploadFilename).toBeDefined()
|
||||
}
|
||||
})
|
||||
|
||||
it('can query by nested property', async () => {
|
||||
if (payload.db.name === 'mongoose') {
|
||||
const { totalDocs } = await payload.find({
|
||||
collection: 'relations',
|
||||
where: {
|
||||
'hasOne.title': {
|
||||
equals: 'post 1',
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
expect(totalDocs).toStrictEqual(1)
|
||||
}
|
||||
})
|
||||
|
||||
it('can query using the "in" operator', async () => {
|
||||
if (payload.db.name === 'mongoose') {
|
||||
const { totalDocs } = await payload.find({
|
||||
collection: 'relations',
|
||||
where: {
|
||||
hasMany: {
|
||||
in: [posts[0].id],
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
expect(totalDocs).toStrictEqual(1)
|
||||
}
|
||||
})
|
||||
})
|
||||
BIN
test/plugin-relationship-object-ids/payload-logo.png
Normal file
BIN
test/plugin-relationship-object-ids/payload-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
@@ -7,7 +7,16 @@ import removeFiles from '../helpers/removeFiles'
|
||||
import { Uploads1 } from './collections/Upload1'
|
||||
import Uploads2 from './collections/Upload2'
|
||||
import AdminThumbnailCol from './collections/admin-thumbnail'
|
||||
import { audioSlug, enlargeSlug, mediaSlug, reduceSlug, relationSlug, versionSlug } from './shared'
|
||||
import {
|
||||
audioSlug,
|
||||
cropOnlySlug,
|
||||
enlargeSlug,
|
||||
focalOnlySlug,
|
||||
mediaSlug,
|
||||
reduceSlug,
|
||||
relationSlug,
|
||||
versionSlug,
|
||||
} from './shared'
|
||||
|
||||
const mockModulePath = path.resolve(__dirname, './mocks/mockFSModule.js')
|
||||
|
||||
@@ -136,7 +145,7 @@ export default buildConfigWithDefaults({
|
||||
},
|
||||
},
|
||||
{
|
||||
slug: 'crop-only',
|
||||
slug: cropOnlySlug,
|
||||
fields: [],
|
||||
upload: {
|
||||
focalPoint: false,
|
||||
@@ -163,7 +172,7 @@ export default buildConfigWithDefaults({
|
||||
},
|
||||
},
|
||||
{
|
||||
slug: 'focal-only',
|
||||
slug: focalOnlySlug,
|
||||
fields: [],
|
||||
upload: {
|
||||
crop: false,
|
||||
@@ -189,6 +198,17 @@ export default buildConfigWithDefaults({
|
||||
staticURL: '/focal-only',
|
||||
},
|
||||
},
|
||||
{
|
||||
slug: 'focal-no-sizes',
|
||||
fields: [],
|
||||
upload: {
|
||||
crop: false,
|
||||
focalPoint: true,
|
||||
mimeTypes: ['image/png', 'image/jpg', 'image/jpeg'],
|
||||
staticDir: './focal-no-sizes',
|
||||
staticURL: '/focal-no-sizes',
|
||||
},
|
||||
},
|
||||
{
|
||||
slug: mediaSlug,
|
||||
fields: [],
|
||||
|
||||
@@ -10,7 +10,7 @@ import getFileByPath from '../../packages/payload/src/uploads/getFileByPath'
|
||||
import { initPayloadTest } from '../helpers/configHelpers'
|
||||
import { RESTClient } from '../helpers/rest'
|
||||
import configPromise from './config'
|
||||
import { enlargeSlug, mediaSlug, reduceSlug, relationSlug } from './shared'
|
||||
import { enlargeSlug, focalOnlySlug, mediaSlug, reduceSlug, relationSlug } from './shared'
|
||||
|
||||
const stat = promisify(fs.stat)
|
||||
|
||||
@@ -53,6 +53,8 @@ describe('Collections - Uploads', () => {
|
||||
|
||||
// Check api response
|
||||
expect(doc.mimeType).toEqual('image/png')
|
||||
expect(doc.focalX).toEqual(50)
|
||||
expect(doc.focalY).toEqual(50)
|
||||
expect(sizes.maintainedAspectRatio.url).toContain('/media/image')
|
||||
expect(sizes.maintainedAspectRatio.url).toContain('.png')
|
||||
expect(sizes.maintainedAspectRatio.width).toEqual(1024)
|
||||
@@ -283,6 +285,75 @@ describe('Collections - Uploads', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('focal point', () => {
|
||||
let file
|
||||
|
||||
beforeAll(async () => {
|
||||
// Create image
|
||||
const filePath = path.resolve(__dirname, './image.png')
|
||||
file = await getFileByPath(filePath)
|
||||
file.name = 'focal.png'
|
||||
})
|
||||
|
||||
it('should be able to set focal point through local API', async () => {
|
||||
const doc = await payload.create({
|
||||
collection: focalOnlySlug,
|
||||
data: {
|
||||
focalX: 5,
|
||||
focalY: 5,
|
||||
},
|
||||
file,
|
||||
})
|
||||
|
||||
expect(doc.focalX).toEqual(5)
|
||||
expect(doc.focalY).toEqual(5)
|
||||
|
||||
const updatedFocal = await payload.update({
|
||||
collection: focalOnlySlug,
|
||||
id: doc.id,
|
||||
data: {
|
||||
focalX: 10,
|
||||
focalY: 10,
|
||||
},
|
||||
})
|
||||
|
||||
expect(updatedFocal.focalX).toEqual(10)
|
||||
expect(updatedFocal.focalY).toEqual(10)
|
||||
|
||||
const updateWithoutFocal = await payload.update({
|
||||
collection: focalOnlySlug,
|
||||
id: doc.id,
|
||||
data: {},
|
||||
})
|
||||
|
||||
// Expect focal point to be the same
|
||||
expect(updateWithoutFocal.focalX).toEqual(10)
|
||||
expect(updateWithoutFocal.focalY).toEqual(10)
|
||||
})
|
||||
|
||||
it('should default focal point to 50, 50', async () => {
|
||||
const doc = await payload.create({
|
||||
collection: focalOnlySlug,
|
||||
data: {
|
||||
// No focal point
|
||||
},
|
||||
file,
|
||||
})
|
||||
|
||||
expect(doc.focalX).toEqual(50)
|
||||
expect(doc.focalY).toEqual(50)
|
||||
|
||||
const updateWithoutFocal = await payload.update({
|
||||
collection: focalOnlySlug,
|
||||
id: doc.id,
|
||||
data: {},
|
||||
})
|
||||
|
||||
expect(updateWithoutFocal.focalX).toEqual(50)
|
||||
expect(updateWithoutFocal.focalY).toEqual(50)
|
||||
})
|
||||
})
|
||||
|
||||
it('update', async () => {
|
||||
// Create image
|
||||
const filePath = path.resolve(__dirname, './image.png')
|
||||
|
||||
@@ -47,6 +47,8 @@ export interface Media {
|
||||
filesize?: number
|
||||
width?: number
|
||||
height?: number
|
||||
focalX?: number
|
||||
focalY?: number
|
||||
sizes?: {
|
||||
maintainedAspectRatio?: {
|
||||
url?: string
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
export const mediaSlug = 'media'
|
||||
|
||||
export const relationSlug = 'relation'
|
||||
|
||||
export const audioSlug = 'audio'
|
||||
|
||||
export const enlargeSlug = 'enlarge'
|
||||
|
||||
export const reduceSlug = 'reduce'
|
||||
|
||||
export const adminThumbnailSlug = 'admin-thumbnail'
|
||||
|
||||
export const audioSlug = 'audio'
|
||||
export const cropOnlySlug = 'crop-only'
|
||||
export const enlargeSlug = 'enlarge'
|
||||
export const focalOnlySlug = 'focal-only'
|
||||
export const mediaSlug = 'media'
|
||||
export const reduceSlug = 'reduce'
|
||||
export const relationSlug = 'relation'
|
||||
export const versionSlug = 'versions'
|
||||
|
||||
@@ -908,6 +908,36 @@ describe('Versions', () => {
|
||||
|
||||
expect(byID.docs).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('should be able to query by id AND any other field with draft=true', async () => {
|
||||
const allDocs = await payload.find({
|
||||
collection: 'draft-posts',
|
||||
draft: true,
|
||||
})
|
||||
|
||||
expect(allDocs.docs.length).toBeGreaterThan(1)
|
||||
|
||||
const results = await payload.find({
|
||||
collection: 'draft-posts',
|
||||
draft: true,
|
||||
where: {
|
||||
and: [
|
||||
{
|
||||
id: {
|
||||
not_in: allDocs.docs[0].id,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: {
|
||||
like: 'Published',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
expect(results.docs).toHaveLength(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Collections - GraphQL', () => {
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
{ "path": "./packages/plugin-form-builder" },
|
||||
{ "path": "./packages/plugin-nested-docs" },
|
||||
{ "path": "./packages/plugin-redirects" },
|
||||
{ "path": "./packages/plugin-relationship-object-ids" },
|
||||
{ "path": "./packages/plugin-search" },
|
||||
{ "path": "./packages/plugin-seo" },
|
||||
{ "path": "./packages/plugin-stripe" },
|
||||
|
||||
Reference in New Issue
Block a user