diff --git a/core-web/libs/edit-content/src/lib/components/dot-edit-content-field/dot-edit-content-field.component.html b/core-web/libs/edit-content/src/lib/components/dot-edit-content-field/dot-edit-content-field.component.html
index 95983b495ead..620adb602ab9 100644
--- a/core-web/libs/edit-content/src/lib/components/dot-edit-content-field/dot-edit-content-field.component.html
+++ b/core-web/libs/edit-content/src/lib/components/dot-edit-content-field/dot-edit-content-field.component.html
@@ -95,12 +95,16 @@
}
@case (fieldTypes.FILE) {
}
@case (fieldTypes.IMAGE) {
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.html b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/components/dot-file-field-preview/dot-file-field-preview.component.html
index f0dc6ce69bcf..28f8254bf096 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.html
+++ b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/components/dot-file-field-preview/dot-file-field-preview.component.html
@@ -1 +1,85 @@
-
Preview
+@let previewFile = $previewFile();
+@let metadata = $metadata();
+
+
+
+ @if (previewFile.source === 'temp') {
+
+ } @else {
+
+ }
+
+
+
+
+
+
+
+
+
{{ 'Size' | dm }}:
+
+ @if (metadata.width && metadata.height) {
+
+
+ {{ metadata.width }}px, {{ metadata.height }}px
+
+ }
+
+
+ {{ metadata.fileSize | dotFileSizeFormat }}
+
+
+
+
+
+
+
+
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
new file mode 100644
index 000000000000..8910d142fb2d
--- /dev/null
+++ 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
@@ -0,0 +1,140 @@
+@use "variables" as *;
+
+:host {
+ display: block;
+ width: 100%;
+ height: 100%;
+}
+
+.preview-container {
+ display: flex;
+ gap: $spacing-1;
+ align-items: flex-start;
+ justify-content: center;
+ height: 100%;
+ width: 100%;
+ position: relative;
+ container-type: inline-size;
+ container-name: preview;
+
+ &:only-child {
+ gap: 0;
+ }
+}
+
+.preview-image__container {
+ height: 100%;
+ width: 100%;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ background: $color-palette-gray-200;
+}
+
+.preview-metadata__info {
+ display: flex;
+ justify-content: flex-start;
+ align-items: center;
+ gap: $spacing-0;
+}
+
+.preview-metadata__container {
+ flex-grow: 1;
+ padding: $spacing-1;
+ padding-right: $spacing-6;
+ flex-direction: column;
+ overflow: hidden;
+ gap: $spacing-2;
+ min-width: 150px;
+
+ span {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+ .preview-metadata_header {
+ font-size: $font-size-md;
+ font-weight: $font-weight-semi-bold;
+ margin: 0;
+ color: $black;
+ }
+}
+
+.preview-metadata__actions {
+ position: absolute;
+ bottom: $spacing-1;
+ right: 0;
+ justify-content: flex-end;
+ align-items: center;
+ gap: $spacing-1;
+ z-index: 100;
+}
+
+.file-info__item {
+ display: flex;
+ padding: $spacing-0 0;
+ flex-direction: column;
+ justify-content: center;
+ align-items: flex-start;
+ gap: $spacing-0;
+
+ &:not(:last-child)::after {
+ content: "";
+ display: block;
+ width: 100%;
+ height: 1px;
+ background: $color-palette-gray-200;
+ margin: $spacing-1 0;
+ }
+}
+
+.file-info__link {
+ display: flex;
+ align-items: center;
+ gap: $spacing-1;
+ min-height: 32px;
+ font-size: $font-size-sm;
+ width: 100%;
+
+ a {
+ color: $black;
+ text-decoration: none;
+ flex: 1 0 0;
+ }
+}
+
+.file-info__title {
+ font-size: $font-size-sm;
+ font-style: normal;
+ font-weight: 600;
+}
+
+.file-info__size {
+ display: flex;
+ align-items: center;
+ gap: $spacing-0;
+}
+
+@container preview (min-width: 376px) {
+ .preview-metadata__container {
+ display: flex;
+ }
+
+ .preview-metadata__action--responsive {
+ display: none;
+ }
+
+ .preview-image__container {
+ height: 100%;
+ max-width: 17.5rem;
+ }
+
+ .preview-resource-links__actions {
+ display: flex;
+ }
+
+ .preview-overlay__container {
+ display: none;
+ }
+}
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 00e7c8b5ec2a..eeb15c4501d9 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
@@ -1,12 +1,53 @@
-import { CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, Component } from '@angular/core';
+import {
+ CUSTOM_ELEMENTS_SCHEMA,
+ ChangeDetectionStrategy,
+ Component,
+ computed,
+ input,
+ output,
+ signal
+} from '@angular/core';
+
+import { ButtonModule } from 'primeng/button';
+import { DialogModule } from 'primeng/dialog';
+
+import { DotTempFileThumbnailComponent, DotFileSizeFormatPipe, DotMessagePipe } from '@dotcms/ui';
+
+import { PreviewFile } from '../../models';
+import { getFileMetadata } from '../../utils';
@Component({
selector: 'dot-file-field-preview',
standalone: true,
- imports: [],
+ imports: [
+ DotTempFileThumbnailComponent,
+ DotFileSizeFormatPipe,
+ DotMessagePipe,
+ ButtonModule,
+ DialogModule
+ ],
providers: [],
templateUrl: './dot-file-field-preview.component.html',
+ styleUrls: ['./dot-file-field-preview.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
-export class DotFileFieldPreviewComponent {}
+export class DotFileFieldPreviewComponent {
+ $previewFile = input.required({ alias: 'previewFile' });
+ $fieldVariable = input.required({ alias: 'fieldVariable' });
+ removeFile = output();
+ $showDialog = signal(false);
+
+ $metadata = computed(() => {
+ const previewFile = this.$previewFile();
+ if (previewFile.source === 'temp') {
+ return previewFile.file.metadata;
+ }
+
+ return getFileMetadata(previewFile.file, this.$fieldVariable());
+ });
+
+ toggleShowDialog() {
+ this.$showDialog.set(!this.$showDialog());
+ }
+}
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 c8b15727cbb2..7e2ace36b1c8 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
@@ -7,10 +7,23 @@
-
+
+
@if (store.uiMessage()) {
}
-
@@ -76,7 +88,12 @@
}
@case ('preview') {
-
+ @if (store.previewFile()) {
+
+ }
}
}
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 1b652b4f6ca1..4d3c13b9c606 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
@@ -1,10 +1,10 @@
import {
ChangeDetectionStrategy,
Component,
+ effect,
forwardRef,
inject,
input,
- signal,
OnInit
} from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
@@ -12,12 +12,13 @@ import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import { ButtonModule } from 'primeng/button';
import { DotMessageService } from '@dotcms/data-access';
-import { DotCMSContentTypeField } from '@dotcms/dotcms-models';
+import { DotCMSContentlet, DotCMSContentTypeField } from '@dotcms/dotcms-models';
import {
DotDropZoneComponent,
DotMessagePipe,
DotAIImagePromptComponent,
- DotSpinnerModule
+ DotSpinnerModule,
+ DropZoneFileEvent
} from '@dotcms/ui';
import { DotFileFieldPreviewComponent } from './components/dot-file-field-preview/dot-file-field-preview.component';
@@ -54,11 +55,20 @@ export class DotEditContentFileFieldComponent implements ControlValueAccessor, O
readonly #messageService = inject(DotMessageService);
$field = input.required({ alias: 'field' });
+ $contentlet = input.required({ alias: 'contentlet' });
+ $fieldVariable = input.required({ alias: 'fieldVariable' });
private onChange: (value: string) => void;
private onTouched: () => void;
- $value = signal('');
+ constructor() {
+ effect(() => {
+ const value = this.store.value();
+ console.log('current value', value);
+ this.onChange(value);
+ this.onTouched();
+ });
+ }
ngOnInit() {
this.store.initLoad({
@@ -72,7 +82,7 @@ export class DotEditContentFileFieldComponent implements ControlValueAccessor, O
}
writeValue(value: string): void {
- this.$value.set(value);
+ this.store.setValue(value);
}
registerOnChange(fn: (value: string) => void) {
this.onChange = fn;
@@ -81,4 +91,28 @@ export class DotEditContentFileFieldComponent implements ControlValueAccessor, O
registerOnTouched(fn: () => void) {
this.onTouched = fn;
}
+
+ /**
+ * Handle file drop
+ *
+ * @param {DropZoneFileEvent} { validity, file }
+ * @return {*}
+ * @memberof DotEditContentBinaryFieldComponent
+ */
+ handleFileDrop({ validity, file }: DropZoneFileEvent): void {
+ if (!file) {
+ return;
+ }
+
+ if (!validity.valid) {
+ //this.handleFileDropError(validity);
+
+ return;
+ }
+
+ const fileList = new FileList();
+ fileList[0] = file;
+
+ this.store.handleUploadFile(fileList);
+ }
}
diff --git a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/dot-edit-content-file-field.const.ts b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/dot-edit-content-file-field.const.ts
index 1b2230740421..c06de23ab146 100644
--- a/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/dot-edit-content-file-field.const.ts
+++ b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/dot-edit-content-file-field.const.ts
@@ -5,27 +5,35 @@ type Actions = {
allowURLImport: boolean;
allowCreateFile: boolean;
allowGenerateImg: boolean;
+ acceptedFiles: string[];
+ maxFileSize: number;
};
type ConfigActions = Record;
-export const INPUT_CONFIG_ACTIONS: ConfigActions = {
+export const INPUT_CONFIG: ConfigActions = {
File: {
allowExistingFile: true,
allowURLImport: true,
allowCreateFile: true,
- allowGenerateImg: false
+ allowGenerateImg: false,
+ acceptedFiles: [],
+ maxFileSize: 0
},
Image: {
allowExistingFile: true,
allowURLImport: true,
allowCreateFile: false,
- allowGenerateImg: true
+ allowGenerateImg: true,
+ acceptedFiles: ['image/*'],
+ maxFileSize: 0
},
Binary: {
allowExistingFile: false,
allowURLImport: true,
allowCreateFile: true,
- allowGenerateImg: true
+ allowGenerateImg: true,
+ acceptedFiles: [],
+ maxFileSize: 0
}
};
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 adbe5448d197..56fdc597afed 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
@@ -1,3 +1,5 @@
+import { DotCMSContentlet, DotCMSTempFile } from '@dotcms/dotcms-models';
+
export type INPUT_TYPES = 'File' | 'Image' | 'Binary';
export type FILE_STATUS = 'init' | 'uploading' | 'preview';
@@ -7,3 +9,13 @@ export interface UIMessage {
severity: 'info' | 'error' | 'warning' | 'success';
icon: string;
}
+
+export type PreviewFile =
+ | {
+ source: 'temp';
+ file: DotCMSTempFile;
+ }
+ | {
+ source: 'contentlet';
+ file: DotCMSContentlet;
+ };
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 bc82b878d76e..cc9ed834b81a 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
@@ -1,11 +1,12 @@
import { patchState, signalStore, withComputed, withMethods, withState } from '@ngrx/signals';
-import { computed } from '@angular/core';
+import { computed, inject } from '@angular/core';
+import { DotUploadService } from '@dotcms/data-access';
import { DotCMSContentlet, DotCMSTempFile } from '@dotcms/dotcms-models';
-import { INPUT_CONFIG_ACTIONS } from '../dot-edit-content-file-field.const';
-import { INPUT_TYPES, FILE_STATUS, UIMessage } from '../models';
+import { INPUT_CONFIG } from '../dot-edit-content-file-field.const';
+import { INPUT_TYPES, FILE_STATUS, UIMessage, PreviewFile } from '../models';
export interface FileFieldState {
contentlet: DotCMSContentlet | null;
@@ -21,6 +22,10 @@ export interface FileFieldState {
allowExistingFile: boolean;
allowCreateFile: boolean;
uiMessage: UIMessage | null;
+ acceptedFiles: string[];
+ maxFileSize: number;
+ fieldVariable: string;
+ previewFile: PreviewFile | null;
}
const initialState: FileFieldState = {
@@ -36,7 +41,11 @@ const initialState: FileFieldState = {
allowGenerateImg: false,
allowExistingFile: false,
allowCreateFile: false,
- uiMessage: null
+ uiMessage: null,
+ acceptedFiles: [],
+ maxFileSize: 0,
+ fieldVariable: '',
+ previewFile: null
};
export const FileFieldStore = signalStore(
@@ -53,20 +62,64 @@ export const FileFieldStore = signalStore(
return currentStatus === 'uploading';
})
})),
- withMethods((store) => ({
+ withMethods((store, uploadService = inject(DotUploadService)) => ({
initLoad: (initState: {
inputType: FileFieldState['inputType'];
uiMessage: FileFieldState['uiMessage'];
}) => {
const { inputType, uiMessage } = initState;
- const actions = INPUT_CONFIG_ACTIONS[inputType] || {};
+ const actions = INPUT_CONFIG[inputType] || {};
patchState(store, {
inputType,
uiMessage,
...actions
});
+ },
+ setValue: (value: string) => {
+ patchState(store, { value });
+ },
+ removeFile: () => {
+ patchState(store, {
+ contentlet: null,
+ tempFile: null,
+ value: '',
+ fileStatus: 'init'
+ });
+ },
+ setDropZoneState: (state: boolean) => {
+ patchState(store, {
+ dropZoneActive: state
+ });
+ },
+ setUploading: () => {
+ patchState(store, {
+ dropZoneActive: false,
+ fileStatus: 'uploading'
+ });
+ },
+ handleUploadFile: async (files: FileList) => {
+ const file = files[0];
+
+ if (file) {
+ patchState(store, {
+ dropZoneActive: false,
+ fileStatus: 'uploading'
+ });
+
+ const tempFile = await uploadService.uploadFile({
+ file,
+ maxSize: `${store.maxFileSize()}`
+ });
+ patchState(store, {
+ tempFile,
+ contentlet: null,
+ fileStatus: 'preview',
+ value: tempFile?.id,
+ previewFile: { source: 'temp', file: tempFile }
+ });
+ }
}
}))
);
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
new file mode 100644
index 000000000000..49a68220754e
--- /dev/null
+++ b/core-web/libs/edit-content/src/lib/fields/dot-edit-content-file-field/utils/index.ts
@@ -0,0 +1,12 @@
+import { DotCMSContentlet, DotFileMetadata } from '@dotcms/dotcms-models';
+
+export const getFileMetadata = (
+ contentlet: DotCMSContentlet,
+ fieldVariable: string
+): DotFileMetadata => {
+ const { metaData } = contentlet;
+
+ const metadata = metaData || contentlet[`${fieldVariable}MetaData`];
+
+ return metadata || {};
+};