Merge branch 'main' into next
This commit is contained in:
commit
7ca5f6d1e6
@ -4,6 +4,7 @@ import flatten from 'lodash/flatten';
|
|||||||
import get from 'lodash/get';
|
import get from 'lodash/get';
|
||||||
import isError from 'lodash/isError';
|
import isError from 'lodash/isError';
|
||||||
import uniq from 'lodash/uniq';
|
import uniq from 'lodash/uniq';
|
||||||
|
import { dirname } from 'path';
|
||||||
|
|
||||||
import { resolveFormat } from './formats/formats';
|
import { resolveFormat } from './formats/formats';
|
||||||
import { commitMessageFormatter, slugFormatter } from './lib/formatters';
|
import { commitMessageFormatter, slugFormatter } from './lib/formatters';
|
||||||
@ -40,12 +41,9 @@ import {
|
|||||||
selectMediaFolders,
|
selectMediaFolders,
|
||||||
} from './lib/util/collection.util';
|
} from './lib/util/collection.util';
|
||||||
import filterEntries from './lib/util/filter.util';
|
import filterEntries from './lib/util/filter.util';
|
||||||
import {
|
import { DRAFT_MEDIA_FILES, selectMediaFilePublicPath } from './lib/util/media.util';
|
||||||
DRAFT_MEDIA_FILES,
|
|
||||||
selectMediaFilePath,
|
|
||||||
selectMediaFilePublicPath,
|
|
||||||
} from './lib/util/media.util';
|
|
||||||
import { selectCustomPath, slugFromCustomPath } from './lib/util/nested.util';
|
import { selectCustomPath, slugFromCustomPath } from './lib/util/nested.util';
|
||||||
|
import { isNullish } from './lib/util/null.util';
|
||||||
import { set } from './lib/util/object.util';
|
import { set } from './lib/util/object.util';
|
||||||
import { dateParsers, expandPath, extractTemplateVars } from './lib/widgets/stringTemplate';
|
import { dateParsers, expandPath, extractTemplateVars } from './lib/widgets/stringTemplate';
|
||||||
import createEntry from './valueObjects/createEntry';
|
import createEntry from './valueObjects/createEntry';
|
||||||
@ -63,46 +61,70 @@ import type {
|
|||||||
DisplayURL,
|
DisplayURL,
|
||||||
Entry,
|
Entry,
|
||||||
EntryData,
|
EntryData,
|
||||||
EntryDraft,
|
|
||||||
EventData,
|
EventData,
|
||||||
FilterRule,
|
FilterRule,
|
||||||
ImplementationEntry,
|
ImplementationEntry,
|
||||||
MediaField,
|
MediaField,
|
||||||
|
ObjectValue,
|
||||||
PersistArgs,
|
PersistArgs,
|
||||||
SearchQueryResponse,
|
SearchQueryResponse,
|
||||||
SearchResponse,
|
SearchResponse,
|
||||||
UnknownField,
|
UnknownField,
|
||||||
User,
|
User,
|
||||||
|
ValueOrNestedValue,
|
||||||
} from './interface';
|
} from './interface';
|
||||||
import type { AsyncLock } from './lib/util';
|
import type { AsyncLock } from './lib/util';
|
||||||
import type { RootState } from './store';
|
import type { RootState } from './store';
|
||||||
import type AssetProxy from './valueObjects/AssetProxy';
|
import type AssetProxy from './valueObjects/AssetProxy';
|
||||||
|
|
||||||
function updateAssetProxies(
|
function updatePath(entryPath: string, assetPath: string): string | null {
|
||||||
assetProxies: AssetProxy[],
|
const pathDir = dirname(entryPath);
|
||||||
config: Config,
|
|
||||||
collection: Collection,
|
const pathParts = assetPath.split(DRAFT_MEDIA_FILES);
|
||||||
entryDraft: EntryDraft,
|
const restOfPath = pathParts.length > 1 ? pathParts[1] : null;
|
||||||
path: string,
|
if (restOfPath === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return joinUrlPath(pathDir, restOfPath).replace(/\/\//g, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateAssetFields(data: ValueOrNestedValue, path: string): ValueOrNestedValue {
|
||||||
|
if (
|
||||||
|
isNullish(data) ||
|
||||||
|
typeof data === 'number' ||
|
||||||
|
typeof data === 'boolean' ||
|
||||||
|
data instanceof Date
|
||||||
) {
|
) {
|
||||||
assetProxies.map(asset => {
|
return data;
|
||||||
// update media files path based on entry path
|
}
|
||||||
const oldPath = asset.path;
|
|
||||||
entryDraft.entry.path = path;
|
|
||||||
|
|
||||||
const folderPath = joinUrlPath(
|
if (Array.isArray(data)) {
|
||||||
collection && 'folder' in collection ? collection.folder : '',
|
return data.map(child => updateAssetFields(child, path));
|
||||||
DRAFT_MEDIA_FILES,
|
}
|
||||||
);
|
|
||||||
|
|
||||||
const newPath = selectMediaFilePath(
|
if (typeof data === 'object') {
|
||||||
config,
|
return Object.keys(data).reduce((acc, key) => {
|
||||||
collection,
|
acc[key] = updateAssetFields(data[key], path);
|
||||||
entryDraft.entry,
|
|
||||||
oldPath.replace(folderPath, ''),
|
return acc;
|
||||||
asset.field,
|
}, {} as ObjectValue);
|
||||||
);
|
}
|
||||||
|
|
||||||
|
const newPath = updatePath(path, data);
|
||||||
|
if (!newPath) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return newPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateAssetProxies(assetProxies: AssetProxy[], path: string) {
|
||||||
|
assetProxies.forEach(asset => {
|
||||||
|
const newPath = updatePath(path, asset.path);
|
||||||
|
if (newPath) {
|
||||||
asset.path = newPath;
|
asset.path = newPath;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -865,13 +887,16 @@ export class Backend<EF extends BaseField = UnknownField, BC extends BackendClas
|
|||||||
customPath,
|
customPath,
|
||||||
);
|
);
|
||||||
const path = customPath || (selectEntryPath(collection, slug) ?? '');
|
const path = customPath || (selectEntryPath(collection, slug) ?? '');
|
||||||
|
|
||||||
|
entryDraft.entry.path = path;
|
||||||
|
entryDraft.entry.data = updateAssetFields(entryDraft.entry.data, path) as ObjectValue;
|
||||||
|
updateAssetProxies(assetProxies, path);
|
||||||
|
|
||||||
dataFile = {
|
dataFile = {
|
||||||
path,
|
path,
|
||||||
slug,
|
slug,
|
||||||
raw: this.entryToRaw(collection, entryDraft.entry),
|
raw: this.entryToRaw(collection, entryDraft.entry),
|
||||||
};
|
};
|
||||||
|
|
||||||
updateAssetProxies(assetProxies, config, collection, entryDraft, path);
|
|
||||||
} else {
|
} else {
|
||||||
const slug = entryDraft.entry.slug;
|
const slug = entryDraft.entry.slug;
|
||||||
dataFile = {
|
dataFile = {
|
||||||
|
@ -96,14 +96,23 @@ const EditorInterface = ({
|
|||||||
slug,
|
slug,
|
||||||
}: TranslatedProps<EditorInterfaceProps>) => {
|
}: TranslatedProps<EditorInterfaceProps>) => {
|
||||||
const { locales, defaultLocale } = useMemo(() => getI18nInfo(collection), [collection]) ?? {};
|
const { locales, defaultLocale } = useMemo(() => getI18nInfo(collection), [collection]) ?? {};
|
||||||
const [selectedLocale, setSelectedLocale] = useState<string>(locales?.[1] ?? 'en');
|
const translatedLocales = useMemo(
|
||||||
|
() => locales?.filter(locale => locale !== defaultLocale) ?? [],
|
||||||
|
[locales, defaultLocale],
|
||||||
|
);
|
||||||
|
|
||||||
const [previewActive, setPreviewActive] = useState(
|
const [previewActive, setPreviewActive] = useState(
|
||||||
localStorage.getItem(PREVIEW_VISIBLE) !== 'false',
|
localStorage.getItem(PREVIEW_VISIBLE) !== 'false',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const i18nEnabled = useMemo(() => locales && locales.length > 0, [locales]);
|
||||||
|
|
||||||
const [i18nActive, setI18nActive] = useState(
|
const [i18nActive, setI18nActive] = useState(
|
||||||
Boolean(localStorage.getItem(I18N_VISIBLE) !== 'false' && locales && locales.length > 0),
|
Boolean(localStorage.getItem(I18N_VISIBLE) !== 'false' && i18nEnabled),
|
||||||
|
);
|
||||||
|
|
||||||
|
const [selectedLocale, setSelectedLocale] = useState<string>(
|
||||||
|
(i18nActive ? translatedLocales?.[0] : defaultLocale) ?? 'en',
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -132,8 +141,11 @@ const EditorInterface = ({
|
|||||||
const handleToggleI18n = useCallback(() => {
|
const handleToggleI18n = useCallback(() => {
|
||||||
const newI18nActive = !i18nActive;
|
const newI18nActive = !i18nActive;
|
||||||
setI18nActive(newI18nActive);
|
setI18nActive(newI18nActive);
|
||||||
|
setSelectedLocale(selectedLocale =>
|
||||||
|
newI18nActive && selectedLocale === defaultLocale ? translatedLocales?.[0] : selectedLocale,
|
||||||
|
);
|
||||||
localStorage.setItem(I18N_VISIBLE, `${newI18nActive}`);
|
localStorage.setItem(I18N_VISIBLE, `${newI18nActive}`);
|
||||||
}, [i18nActive]);
|
}, [i18nActive, setSelectedLocale, translatedLocales, defaultLocale]);
|
||||||
|
|
||||||
const handleLocaleChange = useCallback((locale: string) => {
|
const handleLocaleChange = useCallback((locale: string) => {
|
||||||
setSelectedLocale(locale);
|
setSelectedLocale(locale);
|
||||||
@ -174,7 +186,8 @@ const EditorInterface = ({
|
|||||||
setShowMobilePreview(old => !old);
|
setShowMobilePreview(old => !old);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const editor = (
|
const editor = useMemo(
|
||||||
|
() => (
|
||||||
<div
|
<div
|
||||||
key={defaultLocale}
|
key={defaultLocale}
|
||||||
id="control-pane"
|
id="control-pane"
|
||||||
@ -201,13 +214,32 @@ const EditorInterface = ({
|
|||||||
entry={entry}
|
entry={entry}
|
||||||
fields={fields}
|
fields={fields}
|
||||||
fieldsErrors={fieldsErrors}
|
fieldsErrors={fieldsErrors}
|
||||||
locale={defaultLocale}
|
locale={i18nActive ? defaultLocale : selectedLocale}
|
||||||
submitted={submitted}
|
submitted={submitted}
|
||||||
hideBorder={!finalPreviewActive && !i18nActive}
|
hideBorder={!finalPreviewActive && !i18nActive}
|
||||||
|
canChangeLocale={i18nEnabled && !i18nActive}
|
||||||
|
onLocaleChange={handleLocaleChange}
|
||||||
slug={slug}
|
slug={slug}
|
||||||
t={t}
|
t={t}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
),
|
||||||
|
[
|
||||||
|
defaultLocale,
|
||||||
|
finalPreviewActive,
|
||||||
|
i18nActive,
|
||||||
|
showMobilePreview,
|
||||||
|
collection,
|
||||||
|
entry,
|
||||||
|
fields,
|
||||||
|
fieldsErrors,
|
||||||
|
selectedLocale,
|
||||||
|
submitted,
|
||||||
|
i18nEnabled,
|
||||||
|
handleLocaleChange,
|
||||||
|
slug,
|
||||||
|
t,
|
||||||
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
const editorLocale = useMemo(
|
const editorLocale = useMemo(
|
||||||
@ -232,6 +264,7 @@ const EditorInterface = ({
|
|||||||
onLocaleChange={handleLocaleChange}
|
onLocaleChange={handleLocaleChange}
|
||||||
submitted={submitted}
|
submitted={submitted}
|
||||||
canChangeLocale
|
canChangeLocale
|
||||||
|
context="i18nSplit"
|
||||||
hideBorder
|
hideBorder
|
||||||
t={t}
|
t={t}
|
||||||
/>
|
/>
|
||||||
|
@ -31,6 +31,7 @@ export interface EditorControlPaneProps {
|
|||||||
slug?: string;
|
slug?: string;
|
||||||
onLocaleChange?: (locale: string) => void;
|
onLocaleChange?: (locale: string) => void;
|
||||||
allowDefaultLocale?: boolean;
|
allowDefaultLocale?: boolean;
|
||||||
|
context?: 'default' | 'i18nSplit';
|
||||||
}
|
}
|
||||||
|
|
||||||
const EditorControlPane = ({
|
const EditorControlPane = ({
|
||||||
@ -45,6 +46,7 @@ const EditorControlPane = ({
|
|||||||
slug,
|
slug,
|
||||||
onLocaleChange,
|
onLocaleChange,
|
||||||
allowDefaultLocale = false,
|
allowDefaultLocale = false,
|
||||||
|
context = 'default',
|
||||||
t,
|
t,
|
||||||
}: TranslatedProps<EditorControlPaneProps>) => {
|
}: TranslatedProps<EditorControlPaneProps>) => {
|
||||||
const pathField = useMemo(
|
const pathField = useMemo(
|
||||||
@ -120,7 +122,9 @@ const EditorControlPane = ({
|
|||||||
})}
|
})}
|
||||||
canChangeLocale={canChangeLocale}
|
canChangeLocale={canChangeLocale}
|
||||||
onLocaleChange={onLocaleChange}
|
onLocaleChange={onLocaleChange}
|
||||||
allowDefaultLocale={allowDefaultLocale}
|
excludeLocales={
|
||||||
|
!allowDefaultLocale && context === 'i18nSplit' ? [i18n.defaultLocale] : []
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
@ -9,8 +9,8 @@ interface LocaleDropdownProps {
|
|||||||
defaultLocale: string;
|
defaultLocale: string;
|
||||||
dropdownText: string;
|
dropdownText: string;
|
||||||
canChangeLocale: boolean;
|
canChangeLocale: boolean;
|
||||||
allowDefaultLocale: boolean;
|
|
||||||
onLocaleChange?: (locale: string) => void;
|
onLocaleChange?: (locale: string) => void;
|
||||||
|
excludeLocales?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const LocaleDropdown = ({
|
const LocaleDropdown = ({
|
||||||
@ -18,8 +18,8 @@ const LocaleDropdown = ({
|
|||||||
defaultLocale,
|
defaultLocale,
|
||||||
dropdownText,
|
dropdownText,
|
||||||
canChangeLocale,
|
canChangeLocale,
|
||||||
allowDefaultLocale,
|
|
||||||
onLocaleChange,
|
onLocaleChange,
|
||||||
|
excludeLocales = [defaultLocale],
|
||||||
}: LocaleDropdownProps) => {
|
}: LocaleDropdownProps) => {
|
||||||
if (!canChangeLocale) {
|
if (!canChangeLocale) {
|
||||||
return (
|
return (
|
||||||
@ -41,7 +41,7 @@ const LocaleDropdown = ({
|
|||||||
<Menu label={dropdownText}>
|
<Menu label={dropdownText}>
|
||||||
<MenuGroup>
|
<MenuGroup>
|
||||||
{locales
|
{locales
|
||||||
.filter(locale => allowDefaultLocale || locale !== defaultLocale)
|
.filter(locale => !excludeLocales.includes(locale))
|
||||||
.map(locale => (
|
.map(locale => (
|
||||||
<MenuItemButton key={locale} onClick={() => onLocaleChange?.(locale)}>
|
<MenuItemButton key={locale} onClick={() => onLocaleChange?.(locale)}>
|
||||||
{locale}
|
{locale}
|
||||||
|
@ -34,7 +34,7 @@ const Login = ({
|
|||||||
<div className="flex flex-col h-screen items-center justify-center bg-slate-50 dark:bg-slate-900">
|
<div className="flex flex-col h-screen items-center justify-center bg-slate-50 dark:bg-slate-900">
|
||||||
{config?.logo_url ? (
|
{config?.logo_url ? (
|
||||||
<div
|
<div
|
||||||
className="h-40 w-80 mb-4 bg-cover bg-no-repeat bg-center object-cover"
|
className="h-40 w-80 mb-4 bg-contain bg-no-repeat bg-center object-cover"
|
||||||
style={{ backgroundImage: `url('${config.logo_url}')` }}
|
style={{ backgroundImage: `url('${config.logo_url}')` }}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
@ -87,7 +87,7 @@ const Navbar = ({
|
|||||||
>
|
>
|
||||||
{config?.logo_url ? (
|
{config?.logo_url ? (
|
||||||
<div
|
<div
|
||||||
className="h-10 w-10 bg-cover bg-no-repeat bg-center object-cover"
|
className="h-10 w-10 bg-contain bg-no-repeat bg-center object-cover"
|
||||||
style={{ backgroundImage: `url('${config.logo_url}')` }}
|
style={{ backgroundImage: `url('${config.logo_url}')` }}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
@ -320,7 +320,6 @@ export function selectMediaFilePath(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mediaFolder = selectMediaFolder(config, collection, entryMap, field, currentFolder);
|
let mediaFolder = selectMediaFolder(config, collection, entryMap, field, currentFolder);
|
||||||
|
|
||||||
if (!currentFolder) {
|
if (!currentFolder) {
|
||||||
let publicFolder = trim(config['public_folder'] ?? mediaFolder, '/');
|
let publicFolder = trim(config['public_folder'] ?? mediaFolder, '/');
|
||||||
let mediaPathDir = trim(dirname(mediaPath), '/');
|
let mediaPathDir = trim(dirname(mediaPath), '/');
|
||||||
@ -341,10 +340,14 @@ export function selectMediaFilePath(
|
|||||||
collection,
|
collection,
|
||||||
entryMap,
|
entryMap,
|
||||||
field,
|
field,
|
||||||
mediaPathDir.replace(publicFolder, mediaFolder),
|
publicFolder === '' && mediaPathDir.startsWith(mediaFolder)
|
||||||
|
? mediaPathDir
|
||||||
|
: mediaPathDir.replace(publicFolder, mediaFolder),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return joinUrlPath(mediaFolder, basename(mediaPath));
|
return mediaPath.startsWith(mediaFolder)
|
||||||
|
? mediaPath
|
||||||
|
: joinUrlPath(mediaFolder, basename(mediaPath));
|
||||||
}
|
}
|
||||||
|
@ -109,8 +109,17 @@ function mediaLibrary(
|
|||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
isVisible: false,
|
isVisible: false,
|
||||||
insertOptions: undefined,
|
forImage: false,
|
||||||
|
forFolder: false,
|
||||||
|
controlID: undefined,
|
||||||
|
config: undefined,
|
||||||
|
collection: undefined,
|
||||||
|
collectionFile: undefined,
|
||||||
|
field: undefined,
|
||||||
|
value: undefined,
|
||||||
alt: undefined,
|
alt: undefined,
|
||||||
|
replaceIndex: undefined,
|
||||||
|
insertOptions: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
case MEDIA_INSERT: {
|
case MEDIA_INSERT: {
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
{
|
{
|
||||||
"releases": [
|
"releases": [
|
||||||
|
{
|
||||||
|
"date": "2023-06-13T10:00:00.000Z",
|
||||||
|
"version": "v2.5.0",
|
||||||
|
"type": "minor",
|
||||||
|
"description": "Switch i18n locales while in preview mode"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"date": "2023-05-29T10:00:00.000Z",
|
"date": "2023-05-29T10:00:00.000Z",
|
||||||
"version": "v2.4.4",
|
"version": "v2.4.4",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user