Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Features/6908 capture file question value from built-in camera #6939

Merged
merged 35 commits into from
Sep 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
46a07f0
Add mode/currentMode properties #6908
andrewtelnov Sep 7, 2023
b2f3290
Add currentMode into file question #6908
andrewtelnov Sep 11, 2023
5985771
Start stream video #6908
andrewtelnov Sep 11, 2023
d610e9c
Add snapPicture functionality #6908
andrewtelnov Sep 12, 2023
d91fc75
Fix image preview #6908
andrewtelnov Sep 12, 2023
3401db3
Add unit test on stop vidoe on hiding question content #6908
andrewtelnov Sep 12, 2023
0aca8c6
Add question onHiddenContent #6908
andrewtelnov Sep 13, 2023
8e0ee9b
implement onHidingContent for complex questions #6908
andrewtelnov Sep 13, 2023
61f0489
Merge branch 'master' into features/6908-file-webcam #6908
andrewtelnov Sep 13, 2023
c38aa94
Update knockout template #6908
andrewtelnov Sep 13, 2023
77476b9
Rename API webcam into camera #6908
andrewtelnov Sep 14, 2023
742a02b
Add camera svg files #6908
andrewtelnov Sep 14, 2023
1065f43
Add flip camera API, #6908
andrewtelnov Sep 14, 2023
3f53c35
Merge branch 'master' into features/6908-file-webcam
dk981234 Sep 15, 2023
6507ce1
Work for #6908: implement camera mode ui actions for file question
dk981234 Sep 19, 2023
54b5dbc
Work for #6908: fix renderedPlaceholder for different modes
dk981234 Sep 19, 2023
60b05bf
Work for #6908: add markup tests
dk981234 Sep 19, 2023
dfd1f93
Work for #6908: add vrt tests
dk981234 Sep 19, 2023
8f00b9c
Work for #6908: implement in Angular
dk981234 Sep 19, 2023
f2c7db4
Work for #6908: implement in Vue3
dk981234 Sep 20, 2023
3d4c3d3
Work for #6908: implement in Vue
dk981234 Sep 20, 2023
ddb34e3
Work for #6908: implement in React
dk981234 Sep 20, 2023
081cd3f
Work for #6908: fix flip camera action for ios
dk981234 Sep 22, 2023
9c9bee7
Merge branch 'master' into features/6908-file-webcam
dk981234 Sep 22, 2023
2dd4c3a
Try to fix vr and f tests
dk981234 Sep 22, 2023
80ca1e7
Try to fix vr tests
dk981234 Sep 22, 2023
c695865
Work for #6908: add hover state for take picture button
dk981234 Sep 22, 2023
e849932
Fix markup tests
dk981234 Sep 22, 2023
8291535
Fix vrt tests
dk981234 Sep 24, 2023
d0ae9c7
Fix vue build
dk981234 Sep 24, 2023
89780db
Fix markup test
dk981234 Sep 24, 2023
058faaf
Merge branch 'master' into features/6908-file-webcam
dk981234 Sep 24, 2023
63739db
Fix vue build
dk981234 Sep 24, 2023
3ef3c11
Update locales
dk981234 Sep 25, 2023
047192f
Merge branch 'master' into features/6908-file-webcam
dk981234 Sep 25, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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