- @if (metadata) {
-
- @if (metadata.width && metadata.height) {
-
@@ -63,19 +70,19 @@
@@ -109,7 +116,7 @@
{{ sourceLink.key | dm }}:
diff --git a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/components/dot-file-field-preview/dot-file-field-preview.component.scss b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/components/dot-file-field-preview/dot-file-field-preview.component.scss
index 6e049035ea24..ee2078c3edb8 100644
--- a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/components/dot-file-field-preview/dot-file-field-preview.component.scss
+++ b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/components/dot-file-field-preview/dot-file-field-preview.component.scss
@@ -6,6 +6,18 @@
height: 100%;
}
+dot-contentlet-thumbnail::ng-deep {
+ .background-image:not(.svg-thumbnail) {
+ img {
+ object-fit: cover;
+ }
+ }
+
+ img {
+ object-fit: contain;
+ }
+}
+
.preview-container {
display: flex;
gap: $spacing-1;
@@ -63,6 +75,25 @@
}
}
+.preview-code_container {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 100%;
+ width: 100%;
+ user-select: none;
+
+ code {
+ background: #ffffff;
+ color: $color-palette-primary-500;
+ height: 100%;
+ width: 100%;
+ white-space: pre-wrap;
+ overflow: hidden;
+ line-height: normal;
+ }
+}
+
.preview-metadata__action--responsive {
position: absolute;
bottom: $spacing-1;
@@ -70,6 +101,7 @@
display: flex;
flex-direction: column;
gap: $spacing-1;
+ z-index: 100;
}
.preview-resource-links__actions {
@@ -139,6 +171,18 @@
gap: $spacing-0;
}
+.preview-container--fade::after {
+ content: "";
+ background: linear-gradient(0deg, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0) 100%);
+ position: absolute;
+ width: 100%;
+ height: 50%;
+ bottom: 0;
+ left: 0;
+ border-radius: $border-radius-md;
+ pointer-events: none;
+}
+
@container preview (min-width: 500px) {
.preview-metadata__container,
.preview-metadata__actions {
diff --git a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/components/dot-file-field-preview/dot-file-field-preview.component.ts b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/components/dot-file-field-preview/dot-file-field-preview.component.ts
index 8ffcbb0a3e9b..2bebd04964e3 100644
--- a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/components/dot-file-field-preview/dot-file-field-preview.component.ts
+++ b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/components/dot-file-field-preview/dot-file-field-preview.component.ts
@@ -8,7 +8,8 @@ import {
inject,
input,
output,
- signal, OnInit
+ signal,
+ OnInit
} from '@angular/core';
import { ButtonModule } from 'primeng/button';
@@ -18,12 +19,16 @@ import { catchError } from 'rxjs/operators';
import { DotResourceLinksService } from '@dotcms/data-access';
import { DotCMSBaseTypesContentTypes, DotCMSContentlet } from '@dotcms/dotcms-models';
-import { DotTempFileThumbnailComponent, DotFileSizeFormatPipe, DotMessagePipe, DotCopyButtonComponent } from '@dotcms/ui';
+import {
+ DotTempFileThumbnailComponent,
+ DotFileSizeFormatPipe,
+ DotMessagePipe,
+ DotCopyButtonComponent
+} from '@dotcms/ui';
import { DotPreviewResourceLink, PreviewFile } from '../../models';
import { getFileMetadata } from '../../utils';
-
@Component({
selector: 'dot-file-field-preview',
standalone: true,
@@ -57,6 +62,15 @@ export class DotFileFieldPreviewComponent implements OnInit {
return getFileMetadata(previewFile.file);
});
+ $content = computed(() => {
+ const previewFile = this.$previewFile();
+ if (previewFile.source === 'contentlet') {
+ return previewFile.file.content;
+ }
+
+ return null;
+ });
+
$downloadLink = computed(() => {
const previewFile = this.$previewFile();
if (previewFile.source === 'contentlet') {
@@ -110,22 +124,22 @@ export class DotFileFieldPreviewComponent implements OnInit {
const options = [
{
key: 'FileLink',
- value: fileLink,
+ value: fileLink
},
{
key: 'VersionPath',
- value: versionPath,
+ value: versionPath
},
{
key: 'IdPath',
- value: idPath,
+ value: idPath
}
];
if (contentlet.baseType === DotCMSBaseTypesContentTypes.FILEASSET) {
options.push({
key: 'Resource-Link',
- value: text,
+ value: text
});
}
diff --git a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/dot-edit-content-file-field.component.html b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/dot-edit-content-file-field.component.html
index d8de8e40c5bc..42aa3426f436 100644
--- a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/dot-edit-content-file-field.component.html
+++ b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/dot-edit-content-file-field.component.html
@@ -1,6 +1,7 @@
@switch (store.fileStatus()) {
@case ('init') {
diff --git a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/dot-edit-content-file-field.component.scss b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/dot-edit-content-file-field.component.scss
index 5e9fbafe9eeb..4171feaaa386 100644
--- a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/dot-edit-content-file-field.component.scss
+++ b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/dot-edit-content-file-field.component.scss
@@ -83,12 +83,15 @@ input[type="file"] {
}
@container fileField (max-width: 500px) {
+ .file-field__container--preview,
.file-field__container--empty {
- height: auto;
flex-direction: column;
justify-content: center;
align-items: flex-start;
}
+ .file-field__container--empty {
+ height: auto;
+ }
.file-field__drop-zone {
width: 100%;
diff --git a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/dot-edit-content-file-field.component.ts b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/dot-edit-content-file-field.component.ts
index cedab707494e..f24a8665fa95 100644
--- a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/dot-edit-content-file-field.component.ts
+++ b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/dot-edit-content-file-field.component.ts
@@ -25,6 +25,7 @@ import {
import { DotFileFieldPreviewComponent } from './components/dot-file-field-preview/dot-file-field-preview.component';
import { DotFileFieldUiMessageComponent } from './components/dot-file-field-ui-message/dot-file-field-ui-message.component';
import { INPUT_TYPES } from './models';
+import { DotFileFieldUploadService } from './services/upload-file/upload-file.service';
import { FileFieldStore } from './store/file-field.store';
import { getUiMessage } from './utils/messages';
@@ -41,6 +42,7 @@ import { getUiMessage } from './utils/messages';
DotFileFieldPreviewComponent
],
providers: [
+ DotFileFieldUploadService,
FileFieldStore,
{
multi: true,
@@ -80,6 +82,10 @@ export class DotEditContentFileFieldComponent implements ControlValueAccessor, O
}
writeValue(value: string): void {
+ if (!value) {
+ return;
+ }
+
this.store.getAssetData(value);
}
registerOnChange(fn: (value: string) => void) {
diff --git a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/models/index.ts b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/models/index.ts
index 23ca92371b74..7eab20aa55ef 100644
--- a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/models/index.ts
+++ b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/models/index.ts
@@ -30,7 +30,7 @@ export type PreviewFile =
file: DotCMSContentlet;
};
- export interface DotPreviewResourceLink {
- key: string;
- value: string;
- }
\ No newline at end of file
+export interface DotPreviewResourceLink {
+ key: string;
+ value: string;
+}
diff --git a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/services/upload-file/upload-file.service.ts b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/services/upload-file/upload-file.service.ts
new file mode 100644
index 000000000000..d7ecfbddb4d2
--- /dev/null
+++ b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/services/upload-file/upload-file.service.ts
@@ -0,0 +1,48 @@
+import { of } from 'rxjs';
+
+import { HttpClient } from '@angular/common/http';
+import { inject, Injectable } from '@angular/core';
+
+import { map, switchMap } from 'rxjs/operators';
+
+import { DotUploadFileService } from '@dotcms/data-access';
+import { DotCMSContentlet } from '@dotcms/dotcms-models';
+
+import { DotEditContentService } from '../../../../services/dot-edit-content.service';
+import { getFileMetadata, getFileVersion } from '../../utils';
+
+@Injectable()
+export class DotFileFieldUploadService {
+ readonly #fileService = inject(DotUploadFileService);
+ readonly #contentService = inject(DotEditContentService);
+ readonly #httpClient = inject(HttpClient);
+
+ uploadDotAsset(file: File) {
+ return this.#fileService
+ .uploadDotAsset(file)
+ .pipe(switchMap((contentlet) => this.#addContent(contentlet)));
+ }
+
+ getContentById(identifier: string) {
+ return this.#contentService
+ .getContentById(identifier)
+ .pipe(switchMap((contentlet) => this.#addContent(contentlet)));
+ }
+
+ #addContent(contentlet: DotCMSContentlet) {
+ const { editableAsText } = getFileMetadata(contentlet);
+ const contentURL = getFileVersion(contentlet);
+
+ if (editableAsText && contentURL) {
+ return this.#getContentFile(contentURL).pipe(
+ map((content) => ({ ...contentlet, content }))
+ );
+ }
+
+ return of(contentlet);
+ }
+
+ #getContentFile(contentURL: string) {
+ return this.#httpClient.get(contentURL, { responseType: 'text' });
+ }
+}
diff --git a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/store/file-field.store.ts b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/store/file-field.store.ts
index da9afd1535be..18224df98cef 100644
--- a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/store/file-field.store.ts
+++ b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/store/file-field.store.ts
@@ -7,12 +7,11 @@ import { computed, inject } from '@angular/core';
import { filter, switchMap, tap } from 'rxjs/operators';
-import { DotUploadFileService } from '@dotcms/data-access';
import { DotCMSContentlet, DotCMSTempFile } from '@dotcms/dotcms-models';
-import { DotEditContentService } from '../../../services/dot-edit-content.service';
import { INPUT_CONFIG } from '../dot-edit-content-file-field.const';
import { INPUT_TYPES, FILE_STATUS, UIMessage, PreviewFile } from '../models';
+import { DotFileFieldUploadService } from '../services/upload-file/upload-file.service';
import { getUiMessage } from '../utils/messages';
export interface FileFieldState {
@@ -58,10 +57,15 @@ const initialState: FileFieldState = {
export const FileFieldStore = signalStore(
withState(initialState),
withComputed(({ fileStatus }) => ({
- isInitOrPreview: computed(() => {
+ isInit: computed(() => {
const currentStatus = fileStatus();
- return currentStatus === 'init' || currentStatus === 'preview';
+ return currentStatus === 'init';
+ }),
+ isPreview: computed(() => {
+ const currentStatus = fileStatus();
+
+ return currentStatus === 'preview';
}),
isUploading: computed(() => {
const currentStatus = fileStatus();
@@ -69,131 +73,125 @@ export const FileFieldStore = signalStore(
return currentStatus === 'uploading';
})
})),
- withMethods(
- (
- store,
- fileService = inject(DotUploadFileService),
- contentService = inject(DotEditContentService)
- ) => ({
- initLoad: (initState: {
- inputType: FileFieldState['inputType'];
- fieldVariable: FileFieldState['fieldVariable'];
- }) => {
- const { inputType, fieldVariable } = initState;
-
- const actions = INPUT_CONFIG[inputType] || {};
-
- patchState(store, {
- inputType,
- fieldVariable,
- ...actions
- });
- },
- setUIMessage: (uiMessage: UIMessage) => {
- const acceptedFiles = store.acceptedFiles();
- const maxFileSize = store.maxFileSize();
-
- patchState(store, {
- uiMessage: {
- ...uiMessage,
- args: [`${maxFileSize}`, acceptedFiles.join(', ')]
- }
- });
- },
- removeFile: () => {
- patchState(store, {
- contentlet: null,
- tempFile: null,
- value: '',
- fileStatus: 'init',
- uiMessage: getUiMessage('DEFAULT')
- });
- },
- setDropZoneState: (state: boolean) => {
- patchState(store, {
- dropZoneActive: state
- });
- },
- setUploading: () => {
- patchState(store, {
- dropZoneActive: false,
- fileStatus: 'uploading'
- });
- },
- handleUploadFile: rxMethod(
- pipe(
- tap(() => {
+ withMethods((store, uploadService = inject(DotFileFieldUploadService)) => ({
+ initLoad: (initState: {
+ inputType: FileFieldState['inputType'];
+ fieldVariable: FileFieldState['fieldVariable'];
+ }) => {
+ const { inputType, fieldVariable } = initState;
+
+ const actions = INPUT_CONFIG[inputType] || {};
+
+ patchState(store, {
+ inputType,
+ fieldVariable,
+ ...actions
+ });
+ },
+ setUIMessage: (uiMessage: UIMessage) => {
+ const acceptedFiles = store.acceptedFiles();
+ const maxFileSize = store.maxFileSize();
+
+ patchState(store, {
+ uiMessage: {
+ ...uiMessage,
+ args: [`${maxFileSize}`, acceptedFiles.join(', ')]
+ }
+ });
+ },
+ removeFile: () => {
+ patchState(store, {
+ contentlet: null,
+ tempFile: null,
+ value: '',
+ fileStatus: 'init',
+ uiMessage: getUiMessage('DEFAULT')
+ });
+ },
+ setDropZoneState: (state: boolean) => {
+ patchState(store, {
+ dropZoneActive: state
+ });
+ },
+ setUploading: () => {
+ patchState(store, {
+ dropZoneActive: false,
+ fileStatus: 'uploading'
+ });
+ },
+ handleUploadFile: rxMethod(
+ pipe(
+ tap(() => {
+ patchState(store, {
+ dropZoneActive: false,
+ fileStatus: 'uploading'
+ });
+ }),
+ filter((file) => {
+ const maxFileSize = store.maxFileSize();
+
+ if (maxFileSize && file.size > maxFileSize) {
patchState(store, {
- dropZoneActive: false,
- fileStatus: 'uploading'
+ fileStatus: 'init',
+ dropZoneActive: true,
+ uiMessage: {
+ ...getUiMessage('MAX_FILE_SIZE_EXCEEDED'),
+ args: [`${maxFileSize}`]
+ }
});
- }),
- filter((file) => {
- const maxFileSize = store.maxFileSize();
-
- if (maxFileSize && file.size > maxFileSize) {
- patchState(store, {
- fileStatus: 'init',
- dropZoneActive: true,
- uiMessage: {
- ...getUiMessage('MAX_FILE_SIZE_EXCEEDED'),
- args: [`${maxFileSize}`]
- }
- });
-
- return false;
- }
-
- return true;
- }),
- switchMap((file) => {
- return fileService.uploadDotAsset(file).pipe(
- tapResponse({
- next: (file) => {
- patchState(store, {
- tempFile: null,
- contentlet: file,
- fileStatus: 'preview',
- value: file.identifier,
- previewFile: { source: 'contentlet', file }
- });
- },
- error: () => {
- patchState(store, {
- fileStatus: 'init',
- uiMessage: getUiMessage('SERVER_ERROR')
- });
- }
- })
- );
- })
- )
- ),
- getAssetData: rxMethod(
- pipe(
- switchMap((identifier) => {
- return contentService.getContentById(identifier).pipe(
- tapResponse({
- next: (file) => {
- patchState(store, {
- tempFile: null,
- contentlet: file,
- fileStatus: 'preview',
- value: file.identifier,
- previewFile: { source: 'contentlet', file }
- });
- },
- error: () => {
- patchState(store, {
- fileStatus: 'init',
- uiMessage: getUiMessage('SERVER_ERROR')
- });
- }
- })
- );
- })
- )
+
+ return false;
+ }
+
+ return true;
+ }),
+ switchMap((file) => {
+ return uploadService.uploadDotAsset(file).pipe(
+ tapResponse({
+ next: (file) => {
+ patchState(store, {
+ tempFile: null,
+ contentlet: file,
+ fileStatus: 'preview',
+ value: file.identifier,
+ previewFile: { source: 'contentlet', file }
+ });
+ },
+ error: () => {
+ patchState(store, {
+ fileStatus: 'init',
+ uiMessage: getUiMessage('SERVER_ERROR')
+ });
+ }
+ })
+ );
+ })
)
- })
- )
+ ),
+ getAssetData: rxMethod(
+ pipe(
+ switchMap((identifier) => {
+ return uploadService.getContentById(identifier).pipe(
+ tapResponse({
+ next: (file) => {
+ patchState(store, {
+ tempFile: null,
+ contentlet: file,
+ fileStatus: 'preview',
+ value: file.identifier,
+ previewFile: { source: 'contentlet', file }
+ });
+ },
+ error: () => {
+ patchState(store, {
+ fileStatus: 'init',
+ uiMessage: getUiMessage('SERVER_ERROR')
+ });
+ }
+ })
+ );
+ })
+ )
+ )
+ }))
);
diff --git a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/utils/index.ts b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/utils/index.ts
index 8460e5c042c1..18e3633de32c 100644
--- a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/utils/index.ts
+++ b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/utils/index.ts
@@ -7,3 +7,7 @@ export const getFileMetadata = (contentlet: DotCMSContentlet): DotFileMetadata =
return metadata || {};
};
+
+export const getFileVersion = (contentlet: DotCMSContentlet) => {
+ return contentlet['assetVersion'] || null;
+};