Skip to content

Commit

Permalink
feat(edit-content: Implement ux design for enhanced file field (#30026)
Browse files Browse the repository at this point in the history
### Parent Issue

#29871 

### Proposed Changes
* Init store
* Init components
* Init tests

### Checklist
- [x] Tests
- [x] Translations
- [x] Security Implications Contemplated (add notes if applicable)

### Screenshots

![Screenshot 2024-09-16 at 5 08
13 PM](https://github.com/user-attachments/assets/ca641d98-bee8-442f-9aef-f07c89e348e3)
  • Loading branch information
nicobytes committed Sep 19, 2024
1 parent 702df61 commit effa642
Show file tree
Hide file tree
Showing 21 changed files with 766 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,18 @@
[contentlet]="contentlet"
[field]="field" />
}
@case (fieldTypes.FILE) {
<dot-edit-content-file-field
[formControlName]="field.variable"
[attr.data-testId]="'field-' + field.variable"
[field]="field" />
}
@case (fieldTypes.IMAGE) {
<dot-edit-content-file-field
[formControlName]="field.variable"
[attr.data-testId]="'field-' + field.variable"
[field]="field" />
}
}
@if (field.hint) {
<small [attr.data-testId]="'hint-' + field.variable">{{ field.hint }}</small>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { DotEditContentCalendarFieldComponent } from '../../fields/dot-edit-cont
import { DotEditContentCategoryFieldComponent } from '../../fields/dot-edit-content-category-field/dot-edit-content-category-field.component';
import { DotEditContentCheckboxFieldComponent } from '../../fields/dot-edit-content-checkbox-field/dot-edit-content-checkbox-field.component';
import { DotEditContentCustomFieldComponent } from '../../fields/dot-edit-content-custom-field/dot-edit-content-custom-field.component';
import { DotEditContentFileFieldComponent } from '../../fields/dot-edit-content-file-field/dot-edit-content-file-field.component';
import { DotEditContentHostFolderFieldComponent } from '../../fields/dot-edit-content-host-folder-field/dot-edit-content-host-folder-field.component';
import { DotEditContentJsonFieldComponent } from '../../fields/dot-edit-content-json-field/dot-edit-content-json-field.component';
import { DotEditContentKeyValueComponent } from '../../fields/dot-edit-content-key-value/dot-edit-content-key-value.component';
Expand Down Expand Up @@ -69,6 +70,8 @@ declare module '@tiptap/core' {
const FIELD_TYPES_COMPONENTS: Record<FIELD_TYPES, Type<unknown> | DotEditFieldTestBed> = {
// We had to use unknown because components have different types.
[FIELD_TYPES.TEXT]: DotEditContentTextFieldComponent,
[FIELD_TYPES.FILE]: DotEditContentFileFieldComponent,
[FIELD_TYPES.IMAGE]: DotEditContentFileFieldComponent,
[FIELD_TYPES.TEXTAREA]: DotEditContentTextAreaComponent,
[FIELD_TYPES.SELECT]: DotEditContentSelectFieldComponent,
[FIELD_TYPES.RADIO]: DotEditContentRadioFieldComponent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { DotFieldRequiredDirective } from '@dotcms/ui';

import { DotEditContentBinaryFieldComponent } from '../../fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component';
import { DotEditContentFieldsModule } from '../../fields/dot-edit-content-fields.module';
import { DotEditContentFileFieldComponent } from '../../fields/dot-edit-content-file-field/dot-edit-content-file-field.component';
import { DotEditContentKeyValueComponent } from '../../fields/dot-edit-content-key-value/dot-edit-content-key-value.component';
import { DotEditContentWYSIWYGFieldComponent } from '../../fields/dot-edit-content-wysiwyg-field/dot-edit-content-wysiwyg-field.component';
import { CALENDAR_FIELD_TYPES } from '../../models/dot-edit-content-field.constant';
Expand All @@ -31,7 +32,8 @@ import { FIELD_TYPES } from '../../models/dot-edit-content-field.enum';
BlockEditorModule,
DotEditContentBinaryFieldComponent,
DotEditContentKeyValueComponent,
DotEditContentWYSIWYGFieldComponent
DotEditContentWYSIWYGFieldComponent,
DotEditContentFileFieldComponent
]
})
export class DotEditContentFieldComponent {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ const defaultResolutionFn: FnResolutionValue = (contentlet, field) =>
*/
export const resolutionValue: Record<FIELD_TYPES, FnResolutionValue> = {
[FIELD_TYPES.BINARY]: defaultResolutionFn,
[FIELD_TYPES.FILE]: defaultResolutionFn,
[FIELD_TYPES.IMAGE]: defaultResolutionFn,
[FIELD_TYPES.BLOCK_EDITOR]: defaultResolutionFn,
[FIELD_TYPES.CHECKBOX]: defaultResolutionFn,
[FIELD_TYPES.CONSTANT]: defaultResolutionFn,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<p>Preview</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, Component } from '@angular/core';

@Component({
selector: 'dot-file-field-preview',
standalone: true,
imports: [],
providers: [],
templateUrl: './dot-file-field-preview.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class DotFileFieldPreviewComponent {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<div
[ngClass]="$uiMessage().severity"
class="icon-container"
data-testId="ui-message-icon-container">
<i [ngClass]="$uiMessage().icon" data-testId="ui-message-icon" class="icon"></i>
</div>
<div class="text">
<span [innerHTML]="$uiMessage().message" data-testId="ui-message-span"></span>
<ng-content></ng-content>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
@use "variables" as *;

:host {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: $spacing-3;
height: 100%;
padding: $spacing-3;
}

.icon-container {
border-radius: 50%;
padding: $spacing-3;

.icon {
font-size: $font-size-xxl;
width: auto;
}

&.info {
color: $color-palette-primary-500;
background: $color-palette-primary-200;
}

&.error {
color: $color-alert-yellow;
background: $color-alert-yellow-light;
}
}

.text {
text-align: center;
line-height: 140%;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { NgClass } from '@angular/common';
import { ChangeDetectionStrategy, Component, input } from '@angular/core';

import { UIMessage } from '../../models';

@Component({
selector: 'dot-file-field-ui-message',
standalone: true,
imports: [NgClass],
templateUrl: './dot-file-field-ui-message.component.html',
styleUrls: ['./dot-file-field-ui-message.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class DotFileFieldUiMessageComponent {
$uiMessage = input.required<UIMessage>({ alias: 'uiMessage' });
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<div
class="file-field__container"
[class.file-field__container--empty]="store.isEmpty()"
[class.file-field__container--uploading]="store.isUploading()">
@switch (store.fileStatus()) {
@case ('init') {
<div
[class.file-field__drop-zone--active]="store.dropZoneActive()"
class="file-field__drop-zone">
<dot-drop-zone data-testId="dropzone">
@if (store.uiMessage()) {
<dot-file-field-ui-message [uiMessage]="store.uiMessage()">
<button
class="file-field__drop-zone-btn"
data-testId="choose-file-btn"
type="button">
{{ 'dot.file.field.action.choose.file' | dm }}
</button>
</dot-file-field-ui-message>
}
</dot-drop-zone>
<input #inputFile data-testId="file-field__file-input" type="file" />
</div>

<div class="file-field__actions">
@if (store.allowURLImport()) {
<p-button
[label]="'dot.file.field.action.import.from.url' | dm"
data-testId="action-import-from-url"
icon="pi pi-link"
styleClass="p-button-link p-button-sm" />
}
@if (store.allowExistingFile()) {
<p-button
[label]="'dot.file.field.action.select.existing.file' | dm"
data-testId="action-existing-file"
icon="pi pi-file"
styleClass="p-button-link p-button-sm" />
}
@if (store.allowCreateFile()) {
<p-button
[label]="'dot.file.field.action.create.new.file' | dm"
data-testId="action-new-file"
icon="pi pi-code"
styleClass="p-button-link p-button-sm" />
}
@if (store.allowGenerateImg()) {
<p-button
[disabled]="!store.isAIPluginInstalled()"
tooltipPosition="bottom"
data-testId="action-generate-with-ai"
styleClass="p-button-link p-button-sm pointer-events-auto">
<svg
fill="none"
height="22"
viewBox="0 0 18 22"
width="18"
xmlns="http://www.w3.org/2000/svg">
<path
d="M9.48043 13.2597L5.40457 14.5046C5.29885 14.5368 5.20602 14.6037 5.13999 14.6952C5.07396 14.7868 5.03828 14.8981 5.03828 15.0124C5.03828 15.1268 5.07396 15.238 5.13999 15.3296C5.20602 15.4211 5.29885 15.488 5.40457 15.5203L9.48043 16.7651L10.6799 20.9949C10.711 21.1046 10.7755 21.2009 10.8637 21.2695C10.9519 21.338 11.0591 21.375 11.1693 21.375C11.2795 21.375 11.3867 21.338 11.4749 21.2695C11.5631 21.2009 11.6276 21.1046 11.6586 20.9949L12.8586 16.7651L16.9345 15.5203C17.0402 15.488 17.133 15.4211 17.1991 15.3296C17.2651 15.238 17.3008 15.1268 17.3008 15.0124C17.3008 14.8981 17.2651 14.7868 17.1991 14.6952C17.133 14.6037 17.0402 14.5368 16.9345 14.5046L12.8586 13.2597L11.6586 9.02989C11.6276 8.92018 11.5631 8.82385 11.4749 8.75533C11.3867 8.6868 11.2795 8.64977 11.1693 8.64977C11.0591 8.64977 10.9519 8.6868 10.8637 8.75533C10.7754 8.82385 10.711 8.92018 10.6799 9.02989L9.48043 13.2597Z"
fill="#426BF0" />
<path
d="M14.4668 5.66732L16.6779 4.99138C16.7836 4.95914 16.8764 4.89224 16.9424 4.8007C17.0084 4.70916 17.0441 4.59789 17.0441 4.48355C17.0441 4.3692 17.0084 4.25794 16.9424 4.16639C16.8764 4.07485 16.7836 4.00795 16.6779 3.97571L14.4668 3.3003L13.8154 1.00513C13.7844 0.895414 13.7199 0.799084 13.6317 0.730559C13.5435 0.662034 13.4362 0.625 13.3261 0.625C13.2159 0.625 13.1086 0.662034 13.0204 0.730559C12.9322 0.799084 12.8677 0.895414 12.8367 1.00513L12.1859 3.3003L9.9743 3.97571C9.86858 4.00794 9.77575 4.07483 9.70972 4.16638C9.6437 4.25792 9.60801 4.3692 9.60801 4.48355C9.60801 4.5979 9.6437 4.70917 9.70972 4.80072C9.77575 4.89226 9.86858 4.95915 9.9743 4.99138L12.1859 5.66732L12.8367 7.96196C12.8677 8.07168 12.9322 8.16801 13.0204 8.23653C13.1086 8.30506 13.2159 8.34209 13.3261 8.34209C13.4362 8.34209 13.5435 8.30506 13.6317 8.23653C13.7199 8.16801 13.7844 8.07168 13.8154 7.96196L14.4668 5.66732Z"
fill="#426BF0" />
<path
d="M3.24925 6.62823L1.01825 7.57256C0.924385 7.61231 0.844063 7.68016 0.787521 7.76748C0.730978 7.8548 0.700781 7.95761 0.700781 8.06282C0.700781 8.16802 0.730978 8.27083 0.787521 8.35815C0.844063 8.44547 0.924385 8.51333 1.01825 8.55307L3.24925 9.4974L4.1592 11.8127C4.19751 11.9101 4.2629 11.9935 4.34704 12.0522C4.43118 12.1109 4.53026 12.1422 4.63163 12.1422C4.733 12.1422 4.83208 12.1109 4.91622 12.0522C5.00036 11.9935 5.06576 11.9101 5.10406 11.8127L6.01402 9.4974L8.24456 8.55307C8.33841 8.51332 8.41873 8.44546 8.47527 8.35815C8.53181 8.27083 8.56201 8.16801 8.56201 8.06282C8.56201 7.95762 8.53181 7.8548 8.47527 7.76749C8.41873 7.68017 8.33841 7.61231 8.24456 7.57256L6.01402 6.62823L5.10406 4.31342C5.06576 4.21602 5.00036 4.13266 4.91622 4.07399C4.83208 4.01531 4.733 3.98397 4.63163 3.98397C4.53026 3.98397 4.43118 4.01531 4.34704 4.07399C4.2629 4.13266 4.19751 4.21602 4.1592 4.31342L3.24925 6.62823Z"
fill="#426BF0" />
</svg>
<span class="label-ai">
{{ 'dot.file.field.action.generate.with.dotai' | dm }}
</span>
</p-button>
}
</div>
}
@case ('uploading') {
<dot-spinner data-testId="loading" />
}
@case ('preview') {
<dot-file-field-preview />
}
}
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
@use "variables" as *;

:host {
display: block;
container-type: inline-size;
container-name: binaryField;
}

.file-field__container {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
border-radius: $border-radius-md;
border: $field-border-size solid $color-palette-gray-400;
padding: $spacing-1;
height: 14.4rem;
min-width: 12.5rem;

&:has(.file-field__actions:empty) {
gap: 0;
}
}

.file-field__container--uploading {
border: $field-border-size dashed $color-palette-gray-400;
}

.file-field__actions {
display: flex;
flex-direction: column;
gap: $spacing-3;
justify-content: center;
align-items: flex-start;

&:empty {
display: none;
}

.label-ai {
text-transform: none;
font-size: $font-size-sm;
}

.p-button {
display: inline-flex;
user-select: none;
align-items: center;
vertical-align: bottom;
text-align: center;
}
}

.file-field__drop-zone {
border: $field-border-size dashed $input-border-color;
border-radius: $border-radius-md;
height: 100%;
flex: 1;
overflow: auto;
margin-right: $spacing-1;
}

.file-field__drop-zone-btn {
border: none;
background: none;
color: $color-palette-primary-500;
text-decoration: underline;
font-size: $font-size-md;
font-family: $font-default;
padding: revert;
cursor: pointer;
}

.file-field__drop-zone--active {
border-radius: $border-radius-md;
border-color: $color-palette-secondary-500;
background: $white;
box-shadow: $shadow-l;
}

input[type="file"] {
display: none;
}

@container fileField (max-width: 306px) {
.file-field__container--empty {
height: auto;
flex-direction: column;
justify-content: center;
align-items: flex-start;
}

.file-field__drop-zone {
width: 100%;
margin: 0;
margin-bottom: $spacing-1;
}
}
Loading

0 comments on commit effa642

Please sign in to comment.