From 42b1e690b78448c5bc35b297140f9d97f6ca5bb1 Mon Sep 17 00:00:00 2001 From: Rafael Velazco Date: Thu, 19 Oct 2023 11:22:35 -0400 Subject: [PATCH] dev: filew preview v1 --- .../binary-field/binary-field.component.html | 20 +-- .../binary-field/binary-field.component.scss | 3 +- .../binary-field.component.stories.ts | 29 ++-- .../binary-field/binary-field.component.ts | 22 ++- .../dot-binary-field-preview.component.html | 48 +++++- .../dot-binary-field-preview.component.scss | 101 ++++++++++++ ...-binary-field-preview.component.stories.ts | 145 ++++++++++++++++++ .../dot-binary-field-preview.component.ts | 52 ++++++- .../binary-field/store/binary-field.store.ts | 2 + .../components/buttons/_button.scss | 9 +- core-web/libs/ui/src/index.ts | 2 + .../dot-content-thumbnail.component.html | 12 ++ .../dot-content-thumbnail.component.scss | 26 ++++ .../dot-content-thumbnail.component.spec.ts | 22 +++ .../dot-content-thumbnail.component.ts | 55 +++++++ 15 files changed, 510 insertions(+), 38 deletions(-) create mode 100644 core-web/libs/contenttype-fields/src/lib/fields/binary-field/components/dot-binary-field-preview/dot-binary-field-preview.component.stories.ts create mode 100644 core-web/libs/ui/src/lib/components/dot-content-thumbnail/dot-content-thumbnail.component.html create mode 100644 core-web/libs/ui/src/lib/components/dot-content-thumbnail/dot-content-thumbnail.component.scss create mode 100644 core-web/libs/ui/src/lib/components/dot-content-thumbnail/dot-content-thumbnail.component.spec.ts create mode 100644 core-web/libs/ui/src/lib/components/dot-content-thumbnail/dot-content-thumbnail.component.ts diff --git a/core-web/libs/contenttype-fields/src/lib/fields/binary-field/binary-field.component.html b/core-web/libs/contenttype-fields/src/lib/fields/binary-field/binary-field.component.html index 50d6bc5e8ba6..6f72f21c893b 100644 --- a/core-web/libs/contenttype-fields/src/lib/fields/binary-field/binary-field.component.html +++ b/core-web/libs/contenttype-fields/src/lib/fields/binary-field/binary-field.component.html @@ -60,19 +60,13 @@ *ngIf="vm.status === BINARY_FIELD_STATUS.UPLOADING" data-testId="loading"> - -
- - {{ vm.tempFile.fileName }} - -
- -
+ + + { - return new Promise((resolve, reject) => { + return new Promise((resolve, _reject) => { setTimeout(() => { - reject({ - message: 'error URL' + resolve({ + fileName: 'Image.jpg', + folder: 'folder', + id: 'tempFileId', + image: true, + length: 10000, + mimeType: 'mimeType', + referenceUrl: 'referenceUrl', + thumbnailUrl: 'thumbnailUrl' }); - // resolve({ - // fileName: 'Image.jpg', - // folder: 'folder', - // id: 'tempFileId', - // image: true, - // length: 10000, - // mimeType: 'mimeType', - // referenceUrl: 'referenceUrl', - // thumbnailUrl: 'thumbnailUrl' - // }); }, 4000); }); } diff --git a/core-web/libs/contenttype-fields/src/lib/fields/binary-field/binary-field.component.ts b/core-web/libs/contenttype-fields/src/lib/fields/binary-field/binary-field.component.ts index ac7c7124534e..7b5a3968dec4 100644 --- a/core-web/libs/contenttype-fields/src/lib/fields/binary-field/binary-field.component.ts +++ b/core-web/libs/contenttype-fields/src/lib/fields/binary-field/binary-field.component.ts @@ -29,6 +29,7 @@ import { DropZoneFileValidity } from '@dotcms/ui'; +import { DotBinaryFieldPreviewComponent } from './components/dot-binary-field-preview/dot-binary-field-preview.component'; import { DotBinaryFieldUiMessageComponent } from './components/dot-binary-field-ui-message/dot-binary-field-ui-message.component'; import { DotBinaryFieldUrlModeComponent } from './components/dot-binary-field-url-mode/dot-binary-field-url-mode.component'; import { @@ -41,6 +42,24 @@ import { import { UI_MESSAGE_KEYS, UiMessageI, getUiMessage } from '../../utils/binary-field-utils'; const initialState: BinaryFieldState = { + previewFile: { + type: 'image', + resolution: { + width: '400', + height: '400' + }, + fileSize: 8000, + content: '', + mimeType: 'image/png', + inode: '123456789', + titleImage: 'true', + name: 'image.jpg', + title: 'image.jpg', + hasTitleImage: 'true', + contentType: 'image/png', + __icon__: 'contentIcon', + contentTypeIcon: 'image' + }, file: null, tempFile: null, mode: BINARY_FIELD_MODE.DROPZONE, @@ -63,7 +82,8 @@ const initialState: BinaryFieldState = { DotSpinnerModule, HttpClientModule, InputTextModule, - DotBinaryFieldUrlModeComponent + DotBinaryFieldUrlModeComponent, + DotBinaryFieldPreviewComponent ], providers: [DotBinaryFieldStore], templateUrl: './binary-field.component.html', diff --git a/core-web/libs/contenttype-fields/src/lib/fields/binary-field/components/dot-binary-field-preview/dot-binary-field-preview.component.html b/core-web/libs/contenttype-fields/src/lib/fields/binary-field/components/dot-binary-field-preview/dot-binary-field-preview.component.html index f02134fc00d6..ad2b118ac291 100644 --- a/core-web/libs/contenttype-fields/src/lib/fields/binary-field/components/dot-binary-field-preview/dot-binary-field-preview.component.html +++ b/core-web/libs/contenttype-fields/src/lib/fields/binary-field/components/dot-binary-field-preview/dot-binary-field-preview.component.html @@ -1 +1,47 @@ -

dot-binary-field-preview works!

+
+ +
+ +
+ + +
+ + + {{ previewData.content }} + + +
+ + +
diff --git a/core-web/libs/contenttype-fields/src/lib/fields/binary-field/components/dot-binary-field-preview/dot-binary-field-preview.component.scss b/core-web/libs/contenttype-fields/src/lib/fields/binary-field/components/dot-binary-field-preview/dot-binary-field-preview.component.scss index e69de29bb2d1..fe66deeabe03 100644 --- a/core-web/libs/contenttype-fields/src/lib/fields/binary-field/components/dot-binary-field-preview/dot-binary-field-preview.component.scss +++ b/core-web/libs/contenttype-fields/src/lib/fields/binary-field/components/dot-binary-field-preview/dot-binary-field-preview.component.scss @@ -0,0 +1,101 @@ +@use "variables" as *; + +:host { + display: block; + width: 100%; + height: 100%; +} + +.preview_container { + display: flex; + gap: $spacing-1; + align-items: flex-start; + height: 100%; + max-width: 36rem; + width: 100%; + position: relative; +} + +.preview-image_container ::ng-deep { + height: 100%; + width: 280px; + display: flex; + justify-content: center; + align-items: center; + background: $color-palette-gray-200; + + .thumbnail { + img, + video { + object-fit: contain; + } + } +} + +.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; +} + +.preview-metadata_container { + flex-grow: 1; + padding: $spacing-1; + display: flex; + justify-content: center; + flex-direction: column; + height: 100%; +} + +.preview-metadata_info { + flex-grow: 1; + display: flex; + gap: $spacing-1; + flex-direction: column; + + h4 { + font-size: $font-size-md; + font-weight: $font-weight-bold; + margin: 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } +} + +.preview-metadata { + display: flex; + justify-content: flex-start; + align-items: center; + gap: $spacing-1; + + span { + color: $color-palette-gray-700; + } +} + +.preview-metadata_actions { + position: absolute; + bottom: $spacing-1; + right: $spacing-1; + display: flex; + justify-content: flex-end; + align-items: center; + gap: $spacing-1; + width: 100%; + z-index: 100; +} + +code { + background: $white; + color: $color-palette-primary-500; + height: 100%; + width: 100%; + white-space: pre-wrap; + overflow: hidden; +} diff --git a/core-web/libs/contenttype-fields/src/lib/fields/binary-field/components/dot-binary-field-preview/dot-binary-field-preview.component.stories.ts b/core-web/libs/contenttype-fields/src/lib/fields/binary-field/components/dot-binary-field-preview/dot-binary-field-preview.component.stories.ts new file mode 100644 index 000000000000..07c68438453e --- /dev/null +++ b/core-web/libs/contenttype-fields/src/lib/fields/binary-field/components/dot-binary-field-preview/dot-binary-field-preview.component.stories.ts @@ -0,0 +1,145 @@ +import { action } from '@storybook/addon-actions'; +import { moduleMetadata, Story, Meta } from '@storybook/angular'; + +import { CommonModule } from '@angular/common'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; + +import { ButtonModule } from 'primeng/button'; + +import { DotBinaryFieldPreviewComponent } from './dot-binary-field-preview.component'; + +const previewImage = { + type: 'image', + resolution: { + width: '400', + height: '400' + }, + fileSize: 8000, + content: '', + mimeType: 'image/png', + inode: '123456789', + titleImage: 'true', + name: 'image.jpg', + title: 'image.jpg', + hasTitleImage: 'true', + contentType: 'image/png', + __icon__: 'contentIcon', + contentTypeIcon: 'image' +}; + +const previewVideo = { + type: 'image', + fileSize: 8000, + content: '', + mimeType: 'video/png', + inode: '123456789', + titlevideo: 'true', + name: 'video.jpg', + title: 'video.jpg', + hasTitleImage: 'true', + contentType: 'video/png', + __icon__: 'video' +}; + +const previewFile = { + type: 'file', + fileSize: 8000, + mimeType: 'text/html', + inode: '123456789', + titlevideo: 'true', + name: 'template.html', + title: 'template.html', + hasTitleImage: 'true', + contentType: 'text/html', + content: ` + + + + + Document + + +

I have styles

+ + `, + __icon__: 'page' +}; + +export default { + title: 'Library / Contenttype Fields / Component / DotBinaryFieldPreviewComponent', + component: DotBinaryFieldPreviewComponent, + decorators: [ + moduleMetadata({ + imports: [BrowserAnimationsModule, CommonModule, ButtonModule], + providers: [] + }) + ], + parameters: { + actions: { + handles: ['editFile', 'removeFile'] + } + }, + args: { + previewData: previewImage, + variableName: 'binaryField' + }, + argTypes: { + previewData: { + defaultValue: previewImage, + control: 'object', + description: 'Preview object' + }, + variableName: { + defaultValue: 'binaryField', + control: 'text', + description: 'Field variable name' + } + } +} as Meta; + +const Template: Story = (args: DotBinaryFieldPreviewComponent) => ({ + props: { + ...args, + // https://storybook.js.org/docs/6.5/angular/essentials/actions#action-args + editFile: action('editFile'), + removeFile: action('removeFile') + }, + styles: [ + ` + .container { + width: 100%; + max-width: 36rem; + height: 12.5rem; + border: 1px solid #f2f2f2; + border-radius: 4px; + padding: 0.5rem; + } + ` + ], + template: ` +
+ +
+ ` +}); + +export const Image = Template.bind({}); + +export const Video = Template.bind({}); + +Video.args = { + previewData: previewVideo, + variableName: 'binaryField' +}; + +export const file = Template.bind({}); + +file.args = { + previewData: previewFile, + variableName: 'binaryField' +}; diff --git a/core-web/libs/contenttype-fields/src/lib/fields/binary-field/components/dot-binary-field-preview/dot-binary-field-preview.component.ts b/core-web/libs/contenttype-fields/src/lib/fields/binary-field/components/dot-binary-field-preview/dot-binary-field-preview.component.ts index 8af64ea8faa6..478fed6abdae 100644 --- a/core-web/libs/contenttype-fields/src/lib/fields/binary-field/components/dot-binary-field-preview/dot-binary-field-preview.component.ts +++ b/core-web/libs/contenttype-fields/src/lib/fields/binary-field/components/dot-binary-field-preview/dot-binary-field-preview.component.ts @@ -1,12 +1,56 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; import { CommonModule } from '@angular/common'; +import { + ChangeDetectionStrategy, + Component, + EventEmitter, + Input, + OnInit, + Output +} from '@angular/core'; + +import { ButtonModule } from 'primeng/button'; + +import { DotContentThumbnailComponent } from '@dotcms/ui'; + +export interface BinaryPreview { + type: string; + resolution?: { + width: string; + height: string; + }; + fileSize?: number; + content: string; + mimeType: string; + inode: string; + titleImage: string; + name: string; + title: string; + hasTitleImage: string; + contentType: string; + __icon__: string; + contentTypeIcon: string; +} @Component({ - selector: 'dot-dot-binary-field-preview', + selector: 'dot-binary-field-preview', standalone: true, - imports: [CommonModule], + imports: [CommonModule, ButtonModule, DotContentThumbnailComponent], templateUrl: './dot-binary-field-preview.component.html', styleUrls: ['./dot-binary-field-preview.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush }) -export class DotBinaryFieldPreviewComponent {} +export class DotBinaryFieldPreviewComponent implements OnInit { + @Input() previewData: BinaryPreview; + @Input() variableName: string; + + @Output() editFile: EventEmitter = new EventEmitter(); + @Output() removeFile: EventEmitter = new EventEmitter(); + + contentType: string; + resolution; + + ngOnInit(): void { + this.contentType = this.previewData.contentType; + this.resolution = this.previewData.resolution; + } +} diff --git a/core-web/libs/contenttype-fields/src/lib/fields/binary-field/store/binary-field.store.ts b/core-web/libs/contenttype-fields/src/lib/fields/binary-field/store/binary-field.store.ts index 3218e9990a9a..76b520924ba8 100644 --- a/core-web/libs/contenttype-fields/src/lib/fields/binary-field/store/binary-field.store.ts +++ b/core-web/libs/contenttype-fields/src/lib/fields/binary-field/store/binary-field.store.ts @@ -9,8 +9,10 @@ import { DotUploadService } from '@dotcms/data-access'; import { DotCMSTempFile } from '@dotcms/dotcms-models'; import { UI_MESSAGE_KEYS, UiMessageI, getUiMessage } from '../../../utils/binary-field-utils'; +import { BinaryPreview } from '../components/dot-binary-field-preview/dot-binary-field-preview.component'; export interface BinaryFieldState { + previewFile: BinaryPreview; file: File; tempFile: DotCMSTempFile; mode: BINARY_FIELD_MODE; diff --git a/core-web/libs/dotcms-scss/angular/dotcms-theme/components/buttons/_button.scss b/core-web/libs/dotcms-scss/angular/dotcms-theme/components/buttons/_button.scss index a7e0a4d233ec..672965fb32c8 100644 --- a/core-web/libs/dotcms-scss/angular/dotcms-theme/components/buttons/_button.scss +++ b/core-web/libs/dotcms-scss/angular/dotcms-theme/components/buttons/_button.scss @@ -55,16 +55,17 @@ .p-button.p-fileupload-choose { @extend #main-primary-severity; - &.p-button-secondary { - @extend #main-secondary-severity; - } - // Button Links &.p-button-link { color: $color-palette-primary; background: transparent; border: transparent; } + + &.p-button-link.p-button-secondary { + @extend #outlined-secondary-severity; + border: transparent; + } } // Severity for outlined button diff --git a/core-web/libs/ui/src/index.ts b/core-web/libs/ui/src/index.ts index a61351dda426..81aa5da30d40 100644 --- a/core-web/libs/ui/src/index.ts +++ b/core-web/libs/ui/src/index.ts @@ -9,6 +9,8 @@ export * from './lib/components/dot-empty-container/dot-empty-container.componen export * from './lib/dot-tab-buttons/dot-tab-buttons.component'; export * from './lib/components/dot-field-validation-message/dot-field-validation-message.component'; export * from './lib/components/dot-copy-button/dot-copy-button.component'; +export * from './lib/components/dot-content-thumbnail/dot-content-thumbnail.component'; + // Directives export * from './lib/dot-field-required/dot-field-required.directive'; export * from './lib/dot-remove-confirm-popup/dot-remove-confirm-popup.directive'; diff --git a/core-web/libs/ui/src/lib/components/dot-content-thumbnail/dot-content-thumbnail.component.html b/core-web/libs/ui/src/lib/components/dot-content-thumbnail/dot-content-thumbnail.component.html new file mode 100644 index 000000000000..a2ec45da31cf --- /dev/null +++ b/core-web/libs/ui/src/lib/components/dot-content-thumbnail/dot-content-thumbnail.component.html @@ -0,0 +1,12 @@ +
+ + + + + +
diff --git a/core-web/libs/ui/src/lib/components/dot-content-thumbnail/dot-content-thumbnail.component.scss b/core-web/libs/ui/src/lib/components/dot-content-thumbnail/dot-content-thumbnail.component.scss new file mode 100644 index 000000000000..ac9dd601e041 --- /dev/null +++ b/core-web/libs/ui/src/lib/components/dot-content-thumbnail/dot-content-thumbnail.component.scss @@ -0,0 +1,26 @@ +@use "variables" as *; + +:host { + display: block; + width: 100%; + height: 100%; +} + +.thumbnail { + width: 100%; + height: 100%; + justify-content: center; + align-items: center; + display: flex; + + img, + video { + width: 100%; + height: 100%; + object-fit: contain; + } +} + +i { + color: $color-palette-secondary-400; +} diff --git a/core-web/libs/ui/src/lib/components/dot-content-thumbnail/dot-content-thumbnail.component.spec.ts b/core-web/libs/ui/src/lib/components/dot-content-thumbnail/dot-content-thumbnail.component.spec.ts new file mode 100644 index 000000000000..83a2e7c2c451 --- /dev/null +++ b/core-web/libs/ui/src/lib/components/dot-content-thumbnail/dot-content-thumbnail.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DotContentThumbnailComponent } from './dot-content-thumbnail.component'; + +describe('DotContentThumbnailComponent', () => { + let component: DotContentThumbnailComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [DotContentThumbnailComponent] + }).compileComponents(); + + fixture = TestBed.createComponent(DotContentThumbnailComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/core-web/libs/ui/src/lib/components/dot-content-thumbnail/dot-content-thumbnail.component.ts b/core-web/libs/ui/src/lib/components/dot-content-thumbnail/dot-content-thumbnail.component.ts new file mode 100644 index 000000000000..88c3cb494f86 --- /dev/null +++ b/core-web/libs/ui/src/lib/components/dot-content-thumbnail/dot-content-thumbnail.component.ts @@ -0,0 +1,55 @@ +import { CommonModule } from '@angular/common'; +import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; + +const ICON_MAP = { + html: 'pi-file', + pdf: 'pi-file-pdf', + image: 'pi-image', + video: 'pi-video', + msword: 'pi-file-word', + doc: 'pi-file-word', + docx: 'pi-file-word' +}; + +@Component({ + selector: 'dot-content-thumbnail', + standalone: true, + imports: [CommonModule], + templateUrl: './dot-content-thumbnail.component.html', + styleUrls: ['./dot-content-thumbnail.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class DotContentThumbnailComponent implements OnInit { + url: string; + type: string; + subType: string; + + thumbnailIcon: string; + + @Input() tempUrl: string; + @Input() inode: string; + @Input() name: string; + @Input() icon: string; + @Input() contentType: string; + @Input() iconSize: string; + @Input() titleImage: string; + + private defaultIcon = 'pi-file'; + + ngOnInit(): void { + const [type, subtype] = this.contentType.split('/') || []; + this.url = this.tempUrl || this.getThumbnailUrl(); + this.type = type; + this.thumbnailIcon = this.icon || ICON_MAP[subtype] || this.defaultIcon; + } + + handleError() { + this.type = 'icon'; + } + + private getThumbnailUrl(): string { + return this.contentType === 'application/pdf' + ? `/contentAsset/image/${this.inode}/${this.titleImage}/pdf_page/1/resize_w/250/quality_q/45` + : `/dA/${this.inode}/500w/50q`; + } +}