Skip to content

Commit

Permalink
Implement animation for file's preview pages (#7785)
Browse files Browse the repository at this point in the history
* Implement list animation for file question

* Enable animation when swithing page while deleting item

* Refactor + unit tests

* Fix other themes and markup tests

* Implement in Vue3

* Implement in Angular

* Implement in React

* Implement in Vue2

* Use animationsEnabled flag in file question's animation

* Fix knockout build

* Fix vr tests

* Fix react build

* Implement animation via the new approach

* Fix markup tests

* Add more unit tests

* Try to fix vr tests

* Fix angular markup tests + run id changes after init survey in markup tests

* Fix after merge + implement new behavior for AfterRerender event in Vue3

* Revert index.js

* Fix unit tests
  • Loading branch information
dk981234 authored Oct 2, 2024
1 parent dfcc695 commit 74fe73f
Show file tree
Hide file tree
Showing 43 changed files with 1,284 additions and 560 deletions.
2 changes: 1 addition & 1 deletion examples/knockout/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -1048,7 +1048,7 @@ function init() {
});

//model.render("surveyElement");
ko.applyBindings({model});
ko.applyBindings({ model });
}

if (!window["%hammerhead%"]) {
Expand Down
6 changes: 4 additions & 2 deletions packages/survey-angular-ui/src/angular-ui.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ import { HeaderCellComponent } from "./components/header/header-cell.component";
import { HeaderMobileComponent } from "./components/header/header-mobile.component";
import { ChooseFileBtn } from "./components/file/choose-file.component";
import { FilePreviewComponent } from "./components/file/file-preview.component";
import { FileItemComponent } from "./components/file/file-item.component";
import { FilePageComponent } from "./components/file/file-page.component";
import { SvgBundleComponent } from "./svgbundle.component";

@NgModule({
Expand All @@ -144,7 +146,7 @@ import { SvgBundleComponent } from "./svgbundle.component";
MatrixCellComponent, MatrixDropdownCellComponent, MatrixTableComponent, MatrixDropdownComponent,
MatrixDynamicComponent, MatrixDetailButtonComponent, MatrixDynamicRemoveButtonComponent, MatrixDynamicDragDropIconComponent, MatrixRequiredHeader, ExpressionComponent, SafeResourceUrlPipe, BrandInfoComponent, QuestionErrorComponent,
CustomQuestionComponent, CompositeQuestionComponent, ButtonGroupItemComponent, ButtonGroupQuestionComponent, MatrixRowComponent, ModalComponent, LogoImageComponent, SkeletonComponent, TimerPanelComponent, PaneldynamicRemoveButtonComponent,
NotifierComponent, ComponentsContainerComponent, MultipleTextRowComponent, LoadingIndicatorComponent, HeaderComponent, HeaderCellComponent, HeaderMobileComponent, ChooseFileBtn, FilePreviewComponent, SvgBundleComponent
NotifierComponent, ComponentsContainerComponent, MultipleTextRowComponent, LoadingIndicatorComponent, HeaderComponent, HeaderCellComponent, HeaderMobileComponent, ChooseFileBtn, FilePreviewComponent, SvgBundleComponent, FileItemComponent, FilePageComponent
],
imports: [
CommonModule, FormsModule
Expand All @@ -167,7 +169,7 @@ import { SvgBundleComponent } from "./svgbundle.component";
MatrixCellComponent, MatrixDropdownCellComponent, MatrixTableComponent, MatrixDropdownComponent,
MatrixDynamicComponent, MatrixDetailButtonComponent, MatrixDynamicRemoveButtonComponent, MatrixDynamicDragDropIconComponent, MatrixRequiredHeader, ExpressionComponent, SafeResourceUrlPipe,
CustomQuestionComponent, CompositeQuestionComponent, ButtonGroupQuestionComponent, ModalComponent, LogoImageComponent, SkeletonComponent, TimerPanelComponent, PaneldynamicRemoveButtonComponent,
NotifierComponent, ComponentsContainerComponent, MultipleTextRowComponent, LoadingIndicatorComponent, HeaderComponent, HeaderCellComponent, HeaderMobileComponent, FilePreviewComponent, SvgBundleComponent
NotifierComponent, ComponentsContainerComponent, MultipleTextRowComponent, LoadingIndicatorComponent, HeaderComponent, HeaderCellComponent, HeaderMobileComponent, FilePreviewComponent, SvgBundleComponent, FileItemComponent, FilePageComponent
],
providers: [PopupService],
})
Expand Down
2 changes: 2 additions & 0 deletions packages/survey-angular-ui/src/angular-ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ export * from "./panel.component";
export * from "./components/popup/modal-container.component";
export * from "./components/popup/popup.service";
export * from "./components/survey-actions/survey-nav-btn.component";
export * from "./components/file/file-page.component";
export * from "./components/file/file-item.component";
export * from "./components/file/file-preview.component";
export * from "./questions/matrix.component";
export * from "./components/svg-icon/svg-icon.component";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<ng-template #template>
<span [class]="question.cssClasses.previewItem" (click)="question.doDownloadFileFromContainer($event)">
<div *ngIf="item.name && question.cssClasses.fileSign" [class]="question.cssClasses.fileSign">
<a (click)="question.doDownloadFile($event, item)" [attr.href]="item.content | safeUrl"
[attr.title]="item.name" [attr.download]="item.name" [style.width]="question.imageWidth">{{
item.name
}}</a>
</div>
<div [class]="question.getImageWrapperCss(item)">
<img *ngIf="question.canPreviewImage(item)" [attr.src]="item.content | safeUrl"
[style.height]="question.imageHeight" [style.width]="question.imageWidth" alt="File preview" />
<svg *ngIf="question.defaultImage(item)" [iconName]="question.cssClasses.defaultImageIconId"
[partCss]="question.cssClasses.defaultImage" [size]="'auto'" sv-ng-svg-icon></svg>
<div *ngIf="item.name && !question.isReadOnly" [class]="question.getRemoveButtonCss()"
(click)="question.doRemoveFile(item, $event)">
<span [class]="question.cssClasses.removeFile">{{ question.removeFileCaption }}</span>
<svg *ngIf="question.cssClasses.removeFileSvgIconId" [title]="question.removeFileCaption"
[partCss]="question.cssClasses.removeFileSvg" [iconName]="question.cssClasses.removeFileSvgIconId"
[size]="'auto'" sv-ng-svg-icon></svg>
</div>
</div>
<div *ngIf="item.name && question.cssClasses.fileSignBottom" [class]="question.cssClasses.fileSignBottom">
<a (click)="question.doDownloadFile($event, item)" [attr.href]="item.content | safeUrl"
[attr.title]="item.name" [attr.download]="item.name" [style.width]="question.imageWidth">{{
item.name
}}</a>
</div>
</span>
</ng-template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Component, Input } from "@angular/core";
import { EmbeddedViewContentComponent } from "src/embedded-view-content.component";
import { QuestionFileModel } from "survey-core";

@Component({
templateUrl: "./file-item.component.html",
selector: "sv-ng-file-item",
styleUrls: ["../../hide-host.scss"]
})
export class FileItemComponent extends EmbeddedViewContentComponent {
@Input() item!: any;
@Input() question!: QuestionFileModel;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<ng-template #template>
<div [class]="page.css" [attr.id]="page.id">
<ng-container *ngFor="let item of page.items">
<sv-ng-file-item [item]="item" [question]="question"></sv-ng-file-item>
</ng-container>
</div>
</ng-template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Component, Input } from "@angular/core";
import { BaseAngular } from "src/base-angular";
import { QuestionFileModel, QuestionFilePage } from "survey-core";

@Component({
templateUrl: "./file-page.component.html",
selector: "sv-ng-file-page",
styleUrls: ["../../hide-host.scss"]
})
export class FilePageComponent extends BaseAngular<QuestionFilePage> {
@Input() page!: QuestionFilePage;
@Input() question!: QuestionFileModel;
protected override getModel() {
return this.page;
}
}
Original file line number Diff line number Diff line change
@@ -1,36 +1,12 @@
<ng-template #template>
<ng-container *ngIf="question.showPreviewContainer">
<div [class]="question.cssClasses.fileList || undefined">
<span *ngFor="let val of question.previewValue; index as index; trackBy: trackFilesFn"
[visible]="val && question.isPreviewVisible(index)" [class]="question.cssClasses.previewItem"
(click)="question.doDownloadFileFromContainer($event)">
<div *ngIf="val.name && question.cssClasses.fileSign" [class]="question.cssClasses.fileSign">
<a (click)="question.doDownloadFile($event, val)" [attr.href]="val.content | safeUrl"
[attr.title]="val.name" [attr.download]="val.name" [style.width]="question.imageWidth">{{
val.name
}}</a>
</div>
<div [class]="question.getImageWrapperCss(val)">
<img *ngIf="question.canPreviewImage(val)" [attr.src]="val.content | safeUrl"
[style.height]="question.imageHeight" [style.width]="question.imageWidth" alt="File preview" />
<svg *ngIf="question.defaultImage(val)" [iconName]="question.cssClasses.defaultImageIconId"
[partCss]="question.cssClasses.defaultImage" [size]="'auto'" sv-ng-svg-icon></svg>
<div *ngIf="val.name && !question.isReadOnly" [class]="question.getRemoveButtonCss()"
(click)="question.doRemoveFile(val, $event)">
<span [class]="question.cssClasses.removeFile">{{ question.removeFileCaption }}</span>
<svg *ngIf="question.cssClasses.removeFileSvgIconId" [title]="question.removeFileCaption"
[partCss]="question.cssClasses.removeFileSvg"
[iconName]="question.cssClasses.removeFileSvgIconId" [size]="'auto'" sv-ng-svg-icon></svg>
</div>
</div>
<div *ngIf="val.name && question.cssClasses.fileSignBottom"
[class]="question.cssClasses.fileSignBottom">
<a (click)="question.doDownloadFile($event, val)" [attr.href]="val.content | safeUrl"
[attr.title]="val.name" [attr.download]="val.name" [style.width]="question.imageWidth">{{
val.name
}}</a>
</div>
</span>
<ng-container *ngIf="question.supportFileNavigator">
<sv-ng-file-page *ngFor="let page of question.renderedPages; trackBy: trackPagesFn" [page]="page" [question]="question"></sv-ng-file-page>
</ng-container>
<ng-container *ngIf="!question.supportFileNavigator">
<sv-ng-file-item *ngFor="let item of question.previewValue" [item]="item" [question]="question"></sv-ng-file-item>
</ng-container>
</div>
</ng-container>
</ng-template>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Action, QuestionFileModel } from "survey-core";
import { Action, QuestionFileModel, QuestionFilePage } from "survey-core";
import { AngularComponentFactory } from "../../component-factory";
import { Component, Input } from "@angular/core";
import { EmbeddedViewContentComponent } from "../../embedded-view-content.component";
Expand All @@ -11,5 +11,8 @@ export class FilePreviewComponent extends EmbeddedViewContentComponent {
trackFilesFn: (index: number) => string = (index: number): string => {
return this.question.inputId + "_" + index;
}
trackPagesFn(_: number, page: QuestionFilePage): string {
return page.id;
}
}
AngularComponentFactory.Instance.registerComponent("sv-file-preview", FilePreviewComponent);
2 changes: 1 addition & 1 deletion packages/survey-core/entries/chunks/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ export { QuestionRankingModel } from "../../src/question_ranking";
export { QuestionCommentModel } from "../../src/question_comment";
export { QuestionDropdownModel } from "../../src/question_dropdown";
export { QuestionFactory, ElementFactory } from "../../src/questionfactory";
export { QuestionFileModel } from "../../src/question_file";
export { QuestionFileModel, QuestionFilePage } from "../../src/question_file";
export { QuestionHtmlModel } from "../../src/question_html";
export { QuestionRadiogroupModel } from "../../src/question_radiogroup";
export { QuestionRatingModel, RenderedRatingItem } from "../../src/question_rating";
Expand Down
1 change: 1 addition & 0 deletions packages/survey-core/src/defaultCss/defaultV2Css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,7 @@ export var defaultV2Css = {
changeCameraButton: "sd-file__change-camera-button",
takePictureButton: "sd-file__take-picture-button",
loadingIndicator: "sd-file__loading-indicator",
page: "sd-file__page"
},
signaturepad: {
mainRoot: "sd-element sd-question sd-question--signature sd-row__question",
Expand Down
91 changes: 88 additions & 3 deletions packages/survey-core/src/defaultV2-theme/blocks/sd-file.scss
Original file line number Diff line number Diff line change
Expand Up @@ -175,17 +175,99 @@

.sd-file__list {
display: flex;
gap: calcSize(4);
position: relative;
overflow: hidden;
box-sizing: content-box;
flex-direction: row;
align-items: stretch;
justify-content: center;
padding: calcSize(10.5) 0;
min-height: calcSize(15);
max-height: calcSize(15);
width: 100%;
}

.sd-file__page {
display: flex;
left: 0;
align-items: stretch;
justify-content: center;
gap: calcSize(4);
height: calc(100% - 21 * #{$base-unit});
width: 100%;
position: absolute;
}

@keyframes file-page-to-right {
from {
opacity: 1;
left: 0;
}

to {
opacity: 0;
left: 100%;
}
}

@keyframes file-page-from-right {
from {
opacity: 0;
left: 100%;
}

to {
opacity: 1;
left: 0;
}
}

@keyframes file-page-from-left {
from {
opacity: 0;
left: -100%;
}

to {
opacity: 1;
left: 0;
}
}

@keyframes file-page-to-left {
from {
opacity: 1;
left: 0;
}

to {
opacity: 0;
left: -100%;
}
}

.sd-file__page--leave-to-right,
.sd-file__page--enter-from-right,
.sd-file__page--leave-to-left,
.sd-file__page--enter-from-left {
animation-duration: 0.5s;
animation-fill-mode: forwards;
}

.sd-file__page--leave-to-right {
animation-name: file-page-to-right;
}

.sd-file__page--enter-from-right {
animation-name: file-page-from-right;
}

.sd-file__page--leave-to-left {
animation-name: file-page-to-left;
}

.sd-file__page--enter-from-left {
animation-name: file-page-from-left;
}

.sd-file__preview-item {
position: relative;
display: flex;
Expand Down Expand Up @@ -288,6 +370,9 @@
.sd-file--single-image {
height: calc(36 * #{$base-unit});

.sd-file__page {
height: 100%;
}
.sd-file__preview-item {
width: 100%;
margin: 0;
Expand Down
Loading

0 comments on commit 74fe73f

Please sign in to comment.