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/7540 matrix cell component #7611

Merged
merged 21 commits into from
Jan 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
3f86910
Introduce component for react #7540
andrewtelnov Dec 29, 2023
375c545
Add cellComponent property #7540
andrewtelnov Dec 29, 2023
de63c6d
Implement cell component for knockout #7540
andrewtelnov Dec 29, 2023
cc29cc9
knockout: Rename survey-matrixcell => survey-matrixdropdown-cell #7540
andrewtelnov Dec 29, 2023
9e9db80
vue: rename matrixcell => matrixdropdowncell
andrewtelnov Dec 29, 2023
3fbd50b
vue: implement matrix cell component #7540
andrewtelnov Dec 29, 2023
1c159e8
vue3: rename survey-matrixcell => survey-matrixdropdown-cell #7540
andrewtelnov Dec 29, 2023
ea2740f
vue3: implement cell component #7540
andrewtelnov Dec 29, 2023
f456192
Angular: rename sv-ng-matrix-cell => sv-ng-matrixdropdown-cell #7540
andrewtelnov Dec 29, 2023
a3f8818
Angular: support matrix cell component #7540
andrewtelnov Dec 29, 2023
75cebac
FIx compilation in vue3 #7540
andrewtelnov Dec 29, 2023
d5215b9
Fix angular compilation #7540
andrewtelnov Dec 29, 2023
b818a03
Fix compilation #7540
andrewtelnov Dec 30, 2023
b26c5eb
Fix vue compilation #7540
andrewtelnov Dec 31, 2023
4909d00
Fix Angular selector #7540
andrewtelnov Dec 31, 2023
83a943c
Fix angular selector name #7540
andrewtelnov Jan 1, 2024
1ed8be2
Fix react row state issue #7540
andrewtelnov Jan 1, 2024
ff1572f
Angular register survey-matrix-cell component #7540
andrewtelnov Jan 1, 2024
087688b
Fix Angular reactive #7540
andrewtelnov Jan 1, 2024
17b83a5
Angular: Fix compilation errors #7540
andrewtelnov Jan 1, 2024
25076bc
Angular: fix functional tests #7540
andrewtelnov Jan 1, 2024
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
7 changes: 5 additions & 2 deletions packages/survey-angular-ui/src/angular-ui.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ import { EmbeddedViewContentComponent } from "./embedded-view-content.component"
import { ElementComponent } from "./element.component";
import { CustomWidgetComponent } from "./questions/customwidget.component";
import { MatrixCellComponent } from "./questions/matrixcell.component";
import { MatrixDropdownCellComponent } from "./questions/matrixdropdowncell.component";
import { MatrixTableComponent } from "./questions/matrixtable.component";
import { MatrixDropdownComponent } from "./questions/matrixdropdown.component";
import { MatrixDynamicComponent } from "./questions/matrixdynamic.component";
Expand Down Expand Up @@ -133,7 +134,8 @@ import { SvgBundleComponent } from "./svgbundle.component";
SelectBaseItemComponent, SelectBaseComponent, SurveyCommentComponent, SurveyCommentOtherComponent, ElementHeaderComponent, ElementTitleActionsComponent, ElementTitleComponent, DynamicHeadComponent, RowComponent,
RatingQuestionComponent, RatingDropdownItemComponent, RatingDropdownComponent, BooleanQuestionComponent, BooleanCheckboxComponent, BooleanRadioComponent, BooleanRadioItemComponent, ImagePickerItemComponent, ImagePickerQuestionComponent, ImageQuestionComponent,
SurveyHeaderComponent, ProgressDefaultComponent, ProgressButtonsComponent, ProgressTocComponent, SurveyNavigationButton, MatrixQuestionComponent, SvgIconComponent, FileQuestionComponent, SafeUrlPipe, SafeHtmlPipe, CommentQuestionComponent, SignaturePadQuestionComponent, ErrorsComponent,
MultipleTextComponent, MultipleTextItemComponent, DynamicComponentDirective, RankingQuestionComponent, RankingItemComponent, PanelDynamicQuestionComponent, EmbeddedViewContentComponent, CustomWidgetComponent, MatrixCellComponent, MatrixTableComponent, MatrixDropdownComponent,
MultipleTextComponent, MultipleTextItemComponent, DynamicComponentDirective, RankingQuestionComponent, RankingItemComponent, PanelDynamicQuestionComponent, EmbeddedViewContentComponent, CustomWidgetComponent,
MatrixCellComponent, MatrixDropdownCellComponent, MatrixTableComponent, MatrixDropdownComponent,
MatrixDynamicComponent, MatrixDetailButtonComponent, MatrixDynamicRemoveButtonComponent, MatrixDynamicDragDropIconComponent, MatrixRequiredHeader, ExpressionComponent, SafeResourceUrlPipe, BrandInfoComponent,
CustomQuestionComponent, CompositeQuestionComponent, ButtonGroupItemComponent, ButtonGroupQuestionComponent, MatrixRowComponent, ModalComponent, LogoImageComponent, SkeletonComponent, TimerPanelComponent, PaneldynamicRemoveButtonComponent,
NotifierComponent, ComponentsContainerComponent, MultipleTextRowComponent, LoadingIndicatorComponent, HeaderComponent, HeaderCellComponent, HeaderMobileComponent, ChooseFileBtn, FilePreviewComponent, SvgBundleComponent
Expand All @@ -154,7 +156,8 @@ import { SvgBundleComponent } from "./svgbundle.component";
SelectBaseItemComponent, SelectBaseComponent, SurveyCommentComponent, SurveyCommentOtherComponent, ElementHeaderComponent, ElementTitleComponent, DynamicHeadComponent, RowComponent,
RatingQuestionComponent, RatingDropdownItemComponent, RatingDropdownComponent, BooleanQuestionComponent, BooleanCheckboxComponent, BooleanRadioComponent, BooleanRadioItemComponent, ImagePickerItemComponent, ImagePickerQuestionComponent, ImageQuestionComponent,
SurveyHeaderComponent, ProgressDefaultComponent, ProgressButtonsComponent, SurveyNavigationButton, MatrixQuestionComponent, SvgIconComponent, FileQuestionComponent, SafeUrlPipe, SafeHtmlPipe, CommentQuestionComponent, SignaturePadQuestionComponent, ErrorsComponent,
MultipleTextComponent, MultipleTextItemComponent, DynamicComponentDirective, RankingQuestionComponent, RankingItemComponent, PanelDynamicQuestionComponent, EmbeddedViewContentComponent, CustomWidgetComponent, MatrixCellComponent, MatrixTableComponent, MatrixDropdownComponent,
MultipleTextComponent, MultipleTextItemComponent, DynamicComponentDirective, RankingQuestionComponent, RankingItemComponent, PanelDynamicQuestionComponent, EmbeddedViewContentComponent, CustomWidgetComponent,
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
Expand Down
1 change: 1 addition & 0 deletions packages/survey-angular-ui/src/angular-ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ export * from "./element.component";
export * from "./template-renderer.component";
export * from "./questions/customwidget.component";
export * from "./questions/matrixcell.component";
export * from "./questions/matrixdropdowncell.component";
export * from "./questions/matrixtable.component";
export * from "./questions/matrixdropdown.component";
export * from "./questions/matrixdynamic.component";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<ng-template #template>
<tr *ngIf="model.visible" [class]="model.className" (pointerdown)="question.onPointerDown($event, row)"
[attr.data-sv-drop-target-matrix-row]="row && row.id">
<sv-ng-matrix-cell [cell]="cell" [question]="question"
*ngFor="let cell of model.cells; trackBy: trackCellBy"></sv-ng-matrix-cell>
<sv-ng-matrixdropdown-cell [cell]="cell" [question]="question"
*ngFor="let cell of model.cells; trackBy: trackCellBy"></sv-ng-matrixdropdown-cell>
</tr>
</ng-template>
25 changes: 2 additions & 23 deletions packages/survey-angular-ui/src/questions/matrix.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,36 +24,15 @@
<ng-container *ngIf="model.hasCellText">
<td *ngFor="let column of model.visibleColumns"
[class]="model.getItemClass(row, column)"
(click)="onChange(row, column)" [model]="model.getCellDisplayLocText(row.name, column)" sv-ng-string>
(click)="onCellChanged(row, column)" [model]="model.getCellDisplayLocText(row.name, column)" sv-ng-string>
</td>
</ng-container>
<ng-container *ngIf="!model.hasCellText">
<td
*ngFor="let column of model.visibleColumns; index as columnIndex; trackBy: trackColumnByFn"
[attr.data-responsive-title]="column.locText.renderedHtml"
[class]="model.cssClasses.cell">
<label (mousedown)="model.onMouseDown()" [class]="model.getItemClass(row, column)">
<input
type="radio"
[class]="model.cssClasses.itemValue"
[name]="row.fullName"
[value]="column.value"
[checked]="row.value === column.value"
[disabled]="model.isInputReadOnly"
[attr.id]="model.inputId + '_' + row.name + '_' + columnIndex"
(change)="onChange(row, column)"
[attr.aria-required]="model.a11y_input_ariaRequired"
[attr.aria-label]="model.getCellAriaLabel(row.locText.renderedHtml, column.locText.renderedHtml)"
[attr.aria-invalid]="model.a11y_input_ariaInvalid"
[attr.aria-describedby]="model.a11y_input_ariaDescribedBy"
/>
<span [class]="model.cssClasses.materialDecorator">
<svg *ngIf="model.itemSvgIcon" [class]="model.cssClasses.itemDecorator">
<use [attr.xlink:href]="model.itemSvgIcon"></use>
</svg>
</span>
<span *ngIf="model.isMobile" [class]="model.cssClasses.cellResponsiveTitle" [model]="column.locText" sv-ng-string></span>
</label>
<ng-template [component]="{ name: model.cellComponent, data: { cellChangedOwner: this, question: model, row: row, column: column, columnIndex: columnIndex } }"></ng-template>
</td>
</ng-container>
</tr>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export class MatrixQuestionComponent extends QuestionAngular<QuestionMatrixModel
};
super.ngOnInit();
}
public onChange(row: any, column: any): void {
public onCellChanged(row: any, column: any): void {
if (this.model.isInputReadOnly) return;
row.value = column.value;
this.detectChanges();
Expand Down
64 changes: 23 additions & 41 deletions packages/survey-angular-ui/src/questions/matrixcell.component.html
Original file line number Diff line number Diff line change
@@ -1,41 +1,23 @@
<ng-template #template>
<td [class]="cell.className" [attr.data-responsive-title]="getHeaders()" [title]="cell.getTitle()"
[style]="getCellStyle()" [attr.colspan]="cell.colSpans" (focusin)="cell.focusIn()" #cellContainer>
<sv-ng-matrix-drag-drop-icon *ngIf="cell.isDragHandlerCell"
[model]="$any({ data: { row: row, question: question } })"></sv-ng-matrix-drag-drop-icon>
<sv-action-bar *ngIf="cell.isActionsCell" [model]="cell.item.getData()" [handleClick]="false"></sv-action-bar>
<ng-container *ngIf="cell.hasPanel">
<ng-template [component]="{ name: panelComponentName, data: panelComponentData }"></ng-template>
</ng-container>
<div *ngIf="cell.isErrorsCell && cell.question?.hasVisibleErrors" [element]="cell.question" sv-ng-errors></div>
<div *ngIf="cell.hasQuestion" [class]="question.cssClasses.cellQuestionWrapper"
[visible]="cell.question.isVisible">
<ng-container *ngIf="!cell.isChoice && cell.question.isDefaultRendering()">
<ng-template
[component]="{ name: question.getCellWrapperComponentName(cell.cell), data: { componentData: question.getCellWrapperComponentData(cell.cell)} }">
<ng-template
[component]="{ name: getComponentName(cell.question), data: { model: cell.question } }"></ng-template>
</ng-template>
</ng-container>
<ng-template *ngIf="!cell.isChoice && !cell.question.isDefaultRendering()"
[component]="{ name: cell.question.getComponentName(), data: { model: cell.question } }">
</ng-template>
<ng-container *ngIf="cell.isItemChoice">
<ng-template
[component]="{ name: question.getCellWrapperComponentName(cell.cell), data: { componentData: question.getCellWrapperComponentData(cell.cell)} }">
<sv-ng-selebase-item [showLabel]="false" [inputType]="cell.isCheckbox ? 'checkbox': 'radio'"
[question]="cell.question" [model]="cell.item"></sv-ng-selebase-item>
</ng-template>
</ng-container>
<div *ngIf="cell.isOtherChoice" [class]="cell.question.getCommentAreaCss(true)" [question]="cell.question"
sv-ng-comment-other></div>
</div>
<ng-container *ngIf="cell.hasTitle">
<ng-template
[component]="{ name: question.getCellWrapperComponentName($any(cell)), data: { componentData: question.getCellWrapperComponentData($any(cell))} }">
<sv-ng-string [model]="cell.locTitle"></sv-ng-string>
<span *ngIf="!!cell.requiredText" [class]="question.cssClasses.cellRequiredText">{{ cell.requiredText }}</span>
</ng-template>
</ng-container>
</td>
</ng-template>
<label (mousedown)="question.onMouseDown()" [class]="question.getItemClass(row, column)">
<input
type="radio"
[class]="question.cssClasses.itemValue"
[name]="row.fullName"
[value]="column.value"
[checked]="row.value === column.value"
[disabled]="question.isInputReadOnly"
[attr.id]="question.inputId + '_' + row.name + '_' + columnIndex"
(change)="onChange()"
[attr.aria-required]="question.a11y_input_ariaRequired"
[attr.aria-label]="question.getCellAriaLabel(row.locText.renderedHtml, column.locText.renderedHtml)"
[attr.aria-invalid]="question.a11y_input_ariaInvalid"
[attr.aria-describedby]="question.a11y_input_ariaDescribedBy"
/>
<span [class]="question.cssClasses.materialDecorator">
<svg *ngIf="question.itemSvgIcon" [class]="question.cssClasses.itemDecorator">
<use [attr.xlink:href]="question.itemSvgIcon"></use>
</svg>
</span>
<span *ngIf="question.isMobile" [class]="question.cssClasses.cellResponsiveTitle" [model]="column.locText" sv-ng-string></span>
</label>
<ng-content></ng-content>
114 changes: 22 additions & 92 deletions packages/survey-angular-ui/src/questions/matrixcell.component.ts
Original file line number Diff line number Diff line change
@@ -1,95 +1,25 @@
import { Component, ElementRef, Input, ViewChild } from "@angular/core";
import { BaseAngular } from "../base-angular";
import {
Question,
QuestionMatrixDropdownModelBase,
QuestionMatrixDropdownRenderedCell,
MatrixDropdownRowModelBase,
SurveyModel
} from "survey-core";
import { getComponentName } from "../question";
import { Component, Input } from "@angular/core";
import { MatrixRowModel, ItemValue, QuestionMatrixModel } from "survey-core";
import { AngularComponentFactory } from "../component-factory";

export interface INgMatrixCellChanged {
onCellChanged(row: MatrixRowModel, column: ItemValue): void;
}

@Component({
selector: "sv-ng-matrix-cell",
templateUrl: "./matrixcell.component.html",
styles: [":host { display: none; }"]
selector: "sv-ng-matrix-cell, '[sv-ng-matrix-cell]'",
templateUrl: "./matrixcell.component.html"
})
export class MatrixCellComponent extends BaseAngular<Question> {
@Input() question!: QuestionMatrixDropdownModelBase;
@Input() cell!: QuestionMatrixDropdownRenderedCell;

@ViewChild("cellContainer") cellContainer!: ElementRef<HTMLElement>;
getModel() {
if(this.cell.hasQuestion) {
return this.cell.question;
}
return null as any;
}
public get row(): MatrixDropdownRowModelBase {
return this.cell.row;
}
public override ngDoCheck(): void {
super.ngDoCheck();
if(this.cell.isErrorsCell && this.cell?.question) {
this.cell.question.registerFunctionOnPropertyValueChanged("errors", () => {
this.update();
}, "__ngSubscription")
}
}
public get panelComponentName(): string {
const panel = this.cell.panel;
const survey = <SurveyModel>panel.survey;
if(!!survey) {
const name = survey.getElementWrapperComponentName(panel);
if(!!name) {
return name;
}
}
return "panel";
}
public get panelComponentData(): any {
const panel = this.cell.panel;
const survey = <SurveyModel>panel.survey;
let data: any;
if(!!survey) {
data = survey.getElementWrapperComponentData(panel);
}
return {
componentName: "panel",
componentData: {
model: panel,
data: data
}
};
}

getComponentName(element: Question) { return getComponentName(element); }
getHeaders(): string {
return this.cell.headers;
}
getCellStyle() {
if (!!this.cell.width || !!this.cell.minWidth)
return { width: this.cell.width, minWidth: this.cell.minWidth };
return null;
}
ngAfterViewInit() {
if (!this.cell.hasQuestion || !this.question || !this.question.survey) return;
const el = this.cellContainer.nativeElement;
const cellQ = this.cell.question;
var options = {
cell: this.cell.cell,
cellQuestion: cellQ,
htmlElement: el,
row: this.cell.row,
column: this.cell.cell.column,
};
this.question.survey.matrixAfterCellRender(this.question, options);
cellQ.afterRenderCore(el);
}
override ngOnDestroy(): void {
super.ngOnDestroy();
if(this.cell.isErrorsCell && this.cell?.question) {
this.cell.question.unRegisterFunctionOnPropertyValueChanged("errors", "__ngSubscription")
}
}
}
export class MatrixCellComponent {
@Input() question!: QuestionMatrixModel;
@Input() column!: ItemValue;
@Input() row!: MatrixRowModel;
@Input() columnIndex!: number;
@Input() cellChangedOwner!: INgMatrixCellChanged;
constructor() {
}
public onChange(): void {
this.cellChangedOwner.onCellChanged(this.row, this.column);
}
}
AngularComponentFactory.Instance.registerComponent("survey-matrix-cell", MatrixCellComponent);
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<ng-template #template>
<td [class]="cell.className" [attr.data-responsive-title]="getHeaders()" [title]="cell.getTitle()"
[style]="getCellStyle()" [attr.colspan]="cell.colSpans" (focusin)="cell.focusIn()" #cellContainer>
<sv-ng-matrix-drag-drop-icon *ngIf="cell.isDragHandlerCell"
[model]="$any({ data: { row: row, question: question } })"></sv-ng-matrix-drag-drop-icon>
<sv-action-bar *ngIf="cell.isActionsCell" [model]="cell.item.getData()" [handleClick]="false"></sv-action-bar>
<ng-container *ngIf="cell.hasPanel">
<ng-template [component]="{ name: panelComponentName, data: panelComponentData }"></ng-template>
</ng-container>
<div *ngIf="cell.isErrorsCell && cell.question?.hasVisibleErrors" [element]="cell.question" sv-ng-errors></div>
<div *ngIf="cell.hasQuestion" [class]="question.cssClasses.cellQuestionWrapper"
[visible]="cell.question.isVisible">
<ng-container *ngIf="!cell.isChoice && cell.question.isDefaultRendering()">
<ng-template
[component]="{ name: question.getCellWrapperComponentName(cell.cell), data: { componentData: question.getCellWrapperComponentData(cell.cell)} }">
<ng-template
[component]="{ name: getComponentName(cell.question), data: { model: cell.question } }"></ng-template>
</ng-template>
</ng-container>
<ng-template *ngIf="!cell.isChoice && !cell.question.isDefaultRendering()"
[component]="{ name: cell.question.getComponentName(), data: { model: cell.question } }">
</ng-template>
<ng-container *ngIf="cell.isItemChoice">
<ng-template
[component]="{ name: question.getCellWrapperComponentName(cell.cell), data: { componentData: question.getCellWrapperComponentData(cell.cell)} }">
<sv-ng-selebase-item [showLabel]="false" [inputType]="cell.isCheckbox ? 'checkbox': 'radio'"
[question]="cell.question" [model]="cell.item"></sv-ng-selebase-item>
</ng-template>
</ng-container>
<div *ngIf="cell.isOtherChoice" [class]="cell.question.getCommentAreaCss(true)" [question]="cell.question"
sv-ng-comment-other></div>
</div>
<ng-container *ngIf="cell.hasTitle">
<ng-template
[component]="{ name: question.getCellWrapperComponentName($any(cell)), data: { componentData: question.getCellWrapperComponentData($any(cell))} }">
<sv-ng-string [model]="cell.locTitle"></sv-ng-string>
<span *ngIf="!!cell.requiredText" [class]="question.cssClasses.cellRequiredText">{{ cell.requiredText }}</span>
</ng-template>
</ng-container>
</td>
</ng-template>
Loading
Loading