Skip to content

Commit

Permalink
Features/6908 capture file question value from built-in camera (#6939)
Browse files Browse the repository at this point in the history
* Add mode/currentMode properties #6908

* Add currentMode into file question #6908

* Start stream video #6908

* Add snapPicture functionality #6908

* Fix image preview #6908

* Add unit test on stop vidoe on hiding question content #6908

* Add question onHiddenContent #6908

* implement onHidingContent for complex questions #6908

* Update knockout template #6908

* Rename API webcam into camera #6908

* Add camera svg files #6908

* Add flip camera API, #6908

* Work for #6908: implement camera mode ui actions for file question

* Work for #6908: fix renderedPlaceholder for different modes

* Work for #6908: add markup tests

* Work for #6908: add vrt tests

* Work for #6908: implement in Angular

* Work for #6908: implement in Vue3

* Work for #6908: implement in Vue

* Work for #6908: implement in React

* Work for #6908: fix flip camera action for ios

* Try to fix vr and f tests

* Try to fix vr tests

* Work for #6908: add hover state for take picture button

* Fix markup tests

* Fix vrt tests

* Fix vue build

* Fix markup test

* Fix vue build

* Update locales

---------

Co-authored-by: Dmitry Kuzin <dk981234@gmail.com>
  • Loading branch information
andrewtelnov and dk981234 authored Sep 25, 2023
1 parent 905e796 commit 4e5230a
Show file tree
Hide file tree
Showing 148 changed files with 7,297 additions and 6,307 deletions.
3 changes: 2 additions & 1 deletion packages/survey-angular-ui/src/angular-ui.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ import { NotifierComponent } from "./components/notifier/notifier.component";
import { ComponentsContainerComponent } from "./components-container.component";
import { MultipleTextRowComponent } from "./questions/multipletextrow.component";
import { LoadingIndicatorComponent } from "./angular-ui";
import { ChooseFileBtn } from "./components/file-actions/choose-file.component";
@NgModule({
declarations: [
VisibleDirective, Key2ClickDirective, PanelDynamicAddBtn, PanelDynamicNextBtn, PanelDynamicPrevBtn, PanelDynamicProgressText, ElementComponent, TemplateRendererComponent,
Expand All @@ -128,7 +129,7 @@ import { LoadingIndicatorComponent } from "./angular-ui";
MultipleTextComponent, MultipleTextItemComponent, DynamicComponentDirective, RankingQuestionComponent, RankingItemComponent, PanelDynamicQuestionComponent, EmbeddedViewContentComponent, CustomWidgetComponent, MatrixCellComponent, MatrixTableComponent, MatrixDropdownComponent,
MatrixDynamicComponent, MatrixDetailButtonComponent, MatrixDynamicRemoveButtonComponent, MatrixDynamicDragDropIconComponent, MatrixRequiredHeader, ExpressionComponent, SafeResourceUrlPipe, BrandInfoComponent,
CustomQuestionComponent, CompositeQuestionComponent, ButtonGroupItemComponent, ButtonGroupQuestionComponent, MatrixRowComponent, ModalComponent, LogoImageComponent, SkeletonComponent, TimerPanelComponent, PaneldynamicRemoveButtonComponent,
NotifierComponent, ComponentsContainerComponent, MultipleTextRowComponent, LoadingIndicatorComponent
NotifierComponent, ComponentsContainerComponent, MultipleTextRowComponent, LoadingIndicatorComponent, ChooseFileBtn
],
imports: [
CommonModule, FormsModule
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@

<ng-template #template>
<div [class]="model.getActionRootCss()" [id]="model.id">
<div [class]="model.getActionRootCss()" [id]="id">
<div class="sv-action__content">
<ng-container *ngIf="model.needSeparator">
<div class="sv-action-bar-separator"></div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ export class ActionComponent extends BaseAngular {
protected override getPropertiesToUpdateSync(): string[] {
return ["mode"];
}
public get id() {
return this.model.id || '';
}
}

AngularComponentFactory.Instance.registerComponent("sv-action", ActionComponent);
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<ng-template #template>
<label *ngIf="!question.isReadOnly" role="button" tabindex="0" [class]="question.getChooseFileCss()"
[attr.for]="question.inputId" [attr.aria-label]="question.chooseButtonText" [key2click]>
<svg *ngIf="question.cssClasses.chooseFileIconId" [title]="question.chooseButtonText"
[iconName]="question.cssClasses.chooseFileIconId" [size]="'auto'" sv-ng-svg-icon></svg>
<span>{{ question.chooseButtonText }}</span>
</label>
</ng-template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Action, QuestionFileModel } from "survey-core";
import { AngularComponentFactory } from "../../component-factory";
import { Component, Input } from "@angular/core";
import { EmbeddedViewContentComponent } from "../../embedded-view-content.component";
@Component({
selector: "sv-ng-choose-file-btn",
templateUrl: "./choose-file.component.html"
})
export class ChooseFileBtn extends EmbeddedViewContentComponent {
@Input() data: any;
@Input() model!: Action;
public get question(): QuestionFileModel {
return (this.model && this.model.data.question) || this.data.question;
}
}
AngularComponentFactory.Instance.registerComponent("sv-file-choose-btn", ChooseFileBtn);
125 changes: 69 additions & 56 deletions packages/survey-angular-ui/src/questions/file.component.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div [class]="model.fileRootCss" #contentElement>
<input [class]="model.cssClasses.fileInput" *ngIf="!model.isReadOnly" tabindex="-1" type="file"
<input [class]="model.cssClasses.fileInput" *ngIf="!model.isReadOnly && model.hasFileUI" tabindex="-1" type="file"
[attr.id]="model.inputId" (change)="model.doChange($event)" [attr.aria-required]="model.ariaRequired"
[attr.aria-label]="model.ariaLabel" [attr.aria-invalid]="model.ariaInvalid"
[attr.aria-describedby]="model.ariaDescribedBy" [attr.multiple]="model.multipleRendered"
Expand All @@ -8,61 +8,74 @@
[attr.multiple]="model.multipleRendered" [attr.placeholder]="model.title" style="color: transparent" />
<div [class]="model.cssClasses.dragArea" (dragenter)="model.onDragEnter($event)" (drop)="model.onDrop($event)"
(dragover)="model.onDragOver($event)" (dragleave)="model.onDragLeave($event)">
<div [class]="model.getFileDecoratorCss()">
<ng-container *ngIf="model.showLoadingIndicator">
<sv-ng-loading-indicator></sv-ng-loading-indicator>
</ng-container>
<ng-container *ngIf="model.showChooseButton">
<span [class]="model.cssClasses.dragAreaPlaceholder">{{ model.renderedPlaceholder }}</span>
<div [class]="model.cssClasses.wrapper">
<label *ngIf="!model.isReadOnly" role="button" tabindex="0" [class]="model.getChooseFileCss()"
[attr.for]="model.inputId" [attr.aria-label]="model.chooseButtonText" [key2click]>
<span>{{ model.chooseButtonText }}</span>
<svg *ngIf="model.cssClasses.chooseFileIconId" [title]="model.chooseButtonText"
[iconName]="model.cssClasses.chooseFileIconId" [size]="'auto'" sv-ng-svg-icon></svg>
</label>
<span [class]="model.cssClasses.noFileChosen" *ngIf="model.isEmpty()">{{ model.noFileChosenCaption }}</span>
</div>
</ng-container>
</div>
<button type="button" *ngIf="model.showRemoveButton" [class]="model.cssClasses.removeButton"
(click)="model.doClean($event)">
<span>{{ model.clearButtonCaption }}</span>
<svg *ngIf="model.cssClasses.removeButtonIconId" [iconName]="model.cssClasses.removeButtonIconId" [size]="'auto'"
[title]="model.clearButtonCaption" sv-ng-svg-icon></svg>
</button>
<div [class]="model.cssClasses.fileList || undefined" *ngIf="model.allowShowPreview">
<span *ngFor="let val of model.previewValue; index as index; trackBy: trackFilesFn"
[visible]="val && model.isPreviewVisible(index)" [class]="model.cssClasses.preview">
<div *ngIf="val.name && model.cssClasses.fileSign" [class]="model.cssClasses.fileSign">
<a (click)="model.doDownloadFile($event, val)" [attr.href]="val.content | safeUrl" [attr.title]="val.name"
[attr.download]="val.name" [style.width]="model.imageWidth">{{ val.name }}</a>
</div>
<div [class]="model.cssClasses.imageWrapper">
<img *ngIf="model.canPreviewImage(val)" [attr.src]="val.content | safeUrl" [style.height]="model.imageHeight"
[style.width]="model.imageWidth" alt="File preview" />
<svg *ngIf="model.defaultImage(val)" [iconName]="model.cssClasses.defaultImageIconId"
[partCss]="model.cssClasses.defaultImage" [size]="'auto'" sv-ng-svg-icon></svg>
<div *ngIf="val.name && !model.isReadOnly" [class]="model.cssClasses.removeFileButton"
(click)="model.doRemoveFile(val)">
<span [class]="model.cssClasses.removeFile">{{ model.removeFileCaption }}</span>
<svg *ngIf="model.cssClasses.removeFileSvgIconId" [title]="model.removeFileCaption"
[partCss]="model.cssClasses.removeFileSvg" [iconName]="model.cssClasses.removeFileSvgIconId"
[size]="'auto'" sv-ng-svg-icon></svg>
</div>
</div>
<div *ngIf="val.name && model.cssClasses.fileSignBottom" [class]="model.cssClasses.fileSignBottom">
<a (click)="model.doDownloadFile($event, val)" [attr.href]="val.content | safeUrl" [attr.title]="val.name"
[attr.download]="val.name" [style.width]="model.imageWidth">{{ val.name }}</a>
</div>
</span>
<div [class]="model.getFileDecoratorCss()" *ngIf="model.showFileDecorator">
<span [class]="model.cssClasses.dragAreaPlaceholder">{{ model.renderedPlaceholder }}</span>
<div [class]="model.cssClasses.wrapper">
<sv-ng-choose-file-btn [data]="{ question: model }" *ngIf="model.showChooseButton"></sv-ng-choose-file-btn>
<sv-ng-action-bar [model]="model.actionsContainer" *ngIf="model.actionsContainerVisible"></sv-ng-action-bar>
<span [class]="model.cssClasses.noFileChosen" *ngIf="model.isEmpty()">{{ model.noFileChosenCaption }}</span>
</div>
</div>
<button type="button" *ngIf="model.showRemoveButtonBottom" [class]="model.showRemoveButtonBottom"
(click)="model.doClean($event)">
<span>{{ model.clearButtonCaption }}</span>
<svg *ngIf="model.cssClasses.removeButtonIconId" [iconName]="model.cssClasses.removeButtonIconId" [size]="'auto'"
[title]="model.clearButtonCaption" sv-ng-svg-icon></svg>
</button>
<ng-container *ngIf="model.showLoadingIndicator">
<div [class]="model.cssClasses.loadingIndicator">
<sv-ng-loading-indicator></sv-ng-loading-indicator>
</div>
</ng-container>
<ng-container *ngIf="model.isPlayingVideo">
<ng-container *ngTemplateOutlet="fileVideo"></ng-container>
</ng-container>
<ng-container *ngIf="model.showRemoveButton">
<ng-container *ngTemplateOutlet="fileCleanButton; context: { css: model.showRemoveButton }"></ng-container>
</ng-container>
<ng-container *ngIf="model.allowShowPreview">
<ng-container *ngTemplateOutlet="filePreview"></ng-container>
</ng-container>
<ng-container *ngIf="model.showRemoveButtonBottom">
<ng-container *ngTemplateOutlet="fileCleanButton; context: { css: model.showRemoveButtonBottom }"></ng-container>
</ng-container>
<sv-action-bar *ngIf="model.fileNavigatorVisible" [model]="model.fileNavigator"></sv-action-bar>
</div>
</div>
</div>
<ng-template #filePreview>
<div [class]="model.cssClasses.fileList || undefined">
<span *ngFor="let val of model.previewValue; index as index; trackBy: trackFilesFn"
[visible]="val && model.isPreviewVisible(index)" [class]="model.cssClasses.preview">
<div *ngIf="val.name && model.cssClasses.fileSign" [class]="model.cssClasses.fileSign">
<a (click)="model.doDownloadFile($event, val)" [attr.href]="val.content | safeUrl" [attr.title]="val.name"
[attr.download]="val.name" [style.width]="model.imageWidth">{{ val.name }}</a>
</div>
<div [class]="model.cssClasses.imageWrapper">
<img *ngIf="model.canPreviewImage(val)" [attr.src]="val.content | safeUrl" [style.height]="model.imageHeight"
[style.width]="model.imageWidth" alt="File preview" />
<svg *ngIf="model.defaultImage(val)" [iconName]="model.cssClasses.defaultImageIconId"
[partCss]="model.cssClasses.defaultImage" [size]="'auto'" sv-ng-svg-icon></svg>
<div *ngIf="val.name && !model.isReadOnly" [class]="model.getRemoveButtonCss()"
(click)="model.doRemoveFile(val)">
<span [class]="model.cssClasses.removeFile">{{ model.removeFileCaption }}</span>
<svg *ngIf="model.cssClasses.removeFileSvgIconId" [title]="model.removeFileCaption"
[partCss]="model.cssClasses.removeFileSvg" [iconName]="model.cssClasses.removeFileSvgIconId" [size]="'auto'"
sv-ng-svg-icon></svg>
</div>
</div>
<div *ngIf="val.name && model.cssClasses.fileSignBottom" [class]="model.cssClasses.fileSignBottom">
<a (click)="model.doDownloadFile($event, val)" [attr.href]="val.content | safeUrl" [attr.title]="val.name"
[attr.download]="val.name" [style.width]="model.imageWidth">{{ val.name }}</a>
</div>
</span>
</div>
</ng-template>
<ng-template #fileCleanButton let-css="css">
<button type="button" [class]="css" (click)="model.doClean()">
<span>{{ model.clearButtonCaption }}</span>
<svg *ngIf="model.cssClasses.removeButtonIconId" [iconName]="model.cssClasses.removeButtonIconId" [size]="'auto'"
[title]="model.clearButtonCaption" sv-ng-svg-icon></svg>
</button>
</ng-template>
<ng-template #fileVideo>
<div [class]="model.cssClasses.videoContainer">
<sv-ng-action [model]="model.changeCameraAction"></sv-ng-action>
<sv-ng-action [model]="model.closeCameraAction"></sv-ng-action>
<video autoplay playsinline [attr.id]="model.videoId" [class]="model.cssClasses.video"></video>
<sv-ng-action [model]="model.takePictureAction"></sv-ng-action>
</div>
</ng-template>
Loading

0 comments on commit 4e5230a

Please sign in to comment.