Skip to content

Commit

Permalink
PR: Ranking - Support the itemComponent setting and allow developer…
Browse files Browse the repository at this point in the history
…s to create a custom item template (#8306)

* work for the #8284

* work for the #8284

* work for the #8284

* work for the #8284

* work for the #8284

* work for the #8284

* work for the #8284

* work for the #8284

* work for the https://github.com/surveyjs/survey-library/issues/8284work for the #8209

* work for the #8284

* work for the #8284

* work for the #8284
  • Loading branch information
dmitry-kurmanov committed Jun 4, 2024
1 parent ed6b812 commit 0dbc06d
Show file tree
Hide file tree
Showing 28 changed files with 241 additions and 20 deletions.
4 changes: 3 additions & 1 deletion packages/survey-angular-ui/example/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { TestComponent } from "./components/test/test.component";
import { ExampleComponent } from "./components/example/example.component";
import { AppRoutingModule } from "./router.module";
import { AngularItemTemplateComponent } from "./components/itemTemplateComponent";
import { AngularItemContentTemplateComponent } from "./components/itemContentTemplateComponent";
import { TestDefaultComponent } from "./components/test/testdefault.component";
import { TestDefaultV2Component } from "./components/test/testdefaultV2.component";
import { TestModernComponent } from "./components/test/testmodern.component";
Expand All @@ -21,6 +22,7 @@ import { EmptyComponent } from "./components/test/empty.component";
AppComponent, ExampleComponent, TestComponent, TestDefaultComponent, TestDefaultV2Component, TestModernComponent, TestBootstrapComponent,
TestCustomWidgetComponent,
AngularItemTemplateComponent,
AngularItemContentTemplateComponent,
CustomActionComponent, EmptyComponent
],
imports: [
Expand All @@ -30,6 +32,6 @@ import { EmptyComponent } from "./components/test/empty.component";
bootstrap: [AppComponent]
})
export class AppModule {
static declaration = [AngularItemTemplateComponent, CustomActionComponent]
static declaration = [AngularItemTemplateComponent, AngularItemContentTemplateComponent, CustomActionComponent]
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<ng-template #template>
<div class="sv-ranking-item__text" style="display: flex; align-items: center; gap: 8px;">
<svg [iconName]="'icon-next_16x16'" [size]="'16'" sv-ng-svg-icon></svg>
<sv-ng-string [model]="item.locText"></sv-ng-string>
</div>
</ng-template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Component, Input } from "@angular/core";
import { AngularComponentFactory, EmbeddedViewContentComponent } from "survey-angular-ui";
import { ItemValue } from "survey-core";

@Component({
selector: "sv-new-item-content",
templateUrl: "./itemContentTemplateComponent.html",
styles: [":host { display: none; }"]
})
export class AngularItemContentTemplateComponent extends EmbeddedViewContentComponent {
@Input() item!: ItemValue;
@Input() cssClasses: any;
}

AngularComponentFactory.Instance.registerComponent("new-item-content", AngularItemContentTemplateComponent);
5 changes: 3 additions & 2 deletions packages/survey-angular-ui/src/angular-ui.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ import { MultipleTextItemComponent } from "./questions/multipletextitem.componen
import { DynamicComponentDirective } from "./utils/dynamic.directive";
import { RankingQuestionComponent } from "./questions/ranking.component";
import { RankingItemComponent } from "./questions/ranking-item.component";
import { RankingItemContentComponent } from "./questions/ranking-item-content.component";
import { SurveyStringComponent } from "./survey-string.component";
import { StringEditorComponent } from "./string-editor.component";
import { PanelDynamicAddBtn } from "./components/paneldynamic-actions/paneldynamic-add-btn.component";
Expand Down Expand Up @@ -137,7 +138,7 @@ 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,
MultipleTextComponent, MultipleTextItemComponent, DynamicComponentDirective, RankingQuestionComponent, RankingItemComponent, RankingItemContentComponent, PanelDynamicQuestionComponent, EmbeddedViewContentComponent, CustomWidgetComponent,
MatrixCellComponent, MatrixDropdownCellComponent, MatrixTableComponent, MatrixDropdownComponent,
MatrixDynamicComponent, MatrixDetailButtonComponent, MatrixDynamicRemoveButtonComponent, MatrixDynamicDragDropIconComponent, MatrixRequiredHeader, ExpressionComponent, SafeResourceUrlPipe, BrandInfoComponent, QuestionErrorComponent,
CustomQuestionComponent, CompositeQuestionComponent, ButtonGroupItemComponent, ButtonGroupQuestionComponent, MatrixRowComponent, ModalComponent, LogoImageComponent, SkeletonComponent, TimerPanelComponent, PaneldynamicRemoveButtonComponent,
Expand All @@ -159,7 +160,7 @@ 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,
MultipleTextComponent, MultipleTextItemComponent, DynamicComponentDirective, RankingQuestionComponent, RankingItemComponent, RankingItemContentComponent, PanelDynamicQuestionComponent, EmbeddedViewContentComponent, CustomWidgetComponent,
MatrixCellComponent, MatrixDropdownCellComponent, MatrixTableComponent, MatrixDropdownComponent,
MatrixDynamicComponent, MatrixDetailButtonComponent, MatrixDynamicRemoveButtonComponent, MatrixDynamicDragDropIconComponent, MatrixRequiredHeader, ExpressionComponent, SafeResourceUrlPipe,
CustomQuestionComponent, CompositeQuestionComponent, ButtonGroupQuestionComponent, ModalComponent, LogoImageComponent, SkeletonComponent, TimerPanelComponent, PaneldynamicRemoveButtonComponent,
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 @@ -85,6 +85,7 @@ export * from "./errors.component";
export * from "./utils/dynamic.directive";
export * from "./questions/ranking.component";
export * from "./questions/ranking-item.component";
export * from "./questions/ranking-item-content.component";
export * from "./survey-string.component";
export * from "./string-editor.component";
export * from "./components/paneldynamic-actions/paneldynamic-add-btn.component";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<ng-template #template>
<div [class]="cssClasses.controlLabel">
<sv-ng-string [model]="item.locText"></sv-ng-string>
</div>
</ng-template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Component, Input } from "@angular/core";
import { ItemValue } from "survey-core";
import { AngularComponentFactory } from "../component-factory";
import { EmbeddedViewContentComponent } from "../embedded-view-content.component";

@Component({
selector: "sv-ranking-item-content",
templateUrl: "./ranking-item-content.component.html",
styles: [":host { display: none; }"]
})
export class RankingItemContentComponent extends EmbeddedViewContentComponent {
@Input() item!: ItemValue;
@Input() cssClasses: any;
}
AngularComponentFactory.Instance.registerComponent("sv-ranking-item-content", RankingItemContentComponent);
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,8 @@
<use [attr.xlink:href]="question.dashSvgIcon"></use>
</svg>
</div></ng-template>
<div [class]="question.cssClasses.controlLabel">
<sv-ng-string [model]="model.locText"></sv-ng-string>
</div>

<ng-template [component]="{ name: question.itemContentComponent, data: { item: model, cssClasses: question.cssClasses } }"></ng-template>
</div>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<template>
<div class="sv-ranking-item__text" :style="{display: 'flex', alignItems: 'center', gap: '8px'}">
<sv-svg-icon iconName="icon-next_16x16" size = "16"></sv-svg-icon>
<survey-string :locString="item.locText" />
</div>
</template>
<script lang="ts" setup>
import type { ItemValue } from 'survey-core';
defineProps<{ item: ItemValue, cssClasses: any }>()
</script>
2 changes: 2 additions & 0 deletions packages/survey-vue3-ui/example/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { surveyPlugin } from "survey-vue3-ui";
import { createApp } from 'vue'
import Action from "./components/test/test-custom-components/Action.vue"
import Item from "./components/test/test-custom-components/Item.vue"
import ItemContent from "./components/test/test-custom-components/ItemContent.vue"
import App from './App.vue'
import router from './router'

Expand All @@ -12,6 +13,7 @@ app.use(surveyPlugin);

app.component("svc-custom-action", Action);
app.component("new-item", Item);
app.component("new-item-content", ItemContent);

app.use(router)

Expand Down
4 changes: 1 addition & 3 deletions packages/survey-vue3-ui/src/RankingItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,7 @@
<use :xlink:href="question.dashSvgIcon"></use>
</svg>
</div>
<div :class="question.cssClasses.controlLabel">
<survey-string :locString="item.locText" />
</div>
<component :is="question.itemContentComponent" :item="item" :cssClasses="question.cssClasses"></component>
</div>
</div>
</div>
Expand Down
14 changes: 14 additions & 0 deletions packages/survey-vue3-ui/src/RankingItemContent.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<template>
<div :class="cssClasses.controlLabel">
<survey-string :locString="item.locText" />
</div>
</template>

<script lang="ts" setup>
import type { ItemValue } from "survey-core";
defineOptions({ inheritAttrs: false });
const props = defineProps<{
item: ItemValue;
cssClasses: any;
}>();
</script>
2 changes: 2 additions & 0 deletions packages/survey-vue3-ui/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import TagboxFilter from "./components/tagbox/TagboxFilter.vue";
import TagboxItem from "./components/tagbox/TagboxItem.vue";
import Ranking from "./Ranking.vue";
import RankingItem from "./RankingItem.vue";
import RankingItemContent from "./RankingItemContent.vue";
import Rating from "./Rating.vue";
import RatingItem from "./components/rating/RatingItem.vue";
import RatingItemSmiley from "./components/rating/RatingItemSmiley.vue";
Expand Down Expand Up @@ -183,6 +184,7 @@ function registerComponents(app: App) {
app.component("sv-tagbox-filter", TagboxFilter);
app.component("survey-ranking", Ranking);
app.component("survey-ranking-item", RankingItem);
app.component("sv-ranking-item-content", RankingItemContent);
app.component("survey-rating", Rating);
app.component("sv-rating-item", RatingItem);
app.component("sv-rating-item-smiley", RatingItemSmiley);
Expand Down
1 change: 1 addition & 0 deletions src/entries/react-ui-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export {
export {
SurveyQuestionRanking,
SurveyQuestionRankingItem,
SurveyQuestionRankingItemContent
} from "../react/reactquestion_ranking";

export { RatingItem } from "../react/components/rating/rating-item";
Expand Down
1 change: 1 addition & 0 deletions src/entries/vue-ui-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export { RatingDropdown } from "../vue/rating-dropdown.vue";
export { Comment } from "../vue/comment.vue";
export { Ranking } from "../vue/ranking/ranking.vue";
export { RankingItem } from "../vue/ranking/ranking-item.vue";
export { RankingItemContent } from "../vue/ranking/ranking-item-content.vue";
export { Checkbox } from "../vue/checkbox.vue";
export { CheckboxItem } from "../vue/checkboxitem.vue";
export { ButtonGroup } from "../vue/buttongroup.vue";
Expand Down
4 changes: 4 additions & 0 deletions src/knockout/components/ranking/item-content.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<div data-bind="css: cssClasses.controlLabel">
<!-- ko template: { name: 'survey-string', data: item.locText } -->
<!-- /ko -->
</div>
14 changes: 14 additions & 0 deletions src/knockout/components/ranking/item-content.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import * as ko from "knockout";

const template = require("./item-content.html");

export var RankingItemContenViewModel: any;

ko.components.register("sv-ranking-item-content", {
viewModel: {
createViewModel: (params: any, componentInfo: any) => {
return params;
},
},
template: template,
});
2 changes: 2 additions & 0 deletions src/knockout/koquestion_ranking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {
} from "survey-core";
import { QuestionImplementor } from "./koquestion";

export * from "./components/ranking/item-content";

export class QuestionRanking extends QuestionRankingModel {
private _implementor: QuestionImplementor;
protected onBaseCreating() {
Expand Down
11 changes: 5 additions & 6 deletions src/knockout/templates/question-ranking.html
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,12 @@
<svg>
<use data-bind="attr:{'xlink:href':question.dashSvgIcon}" xlink:href=''></use>
</svg>
</div>
<!-- /ko -->
<div data-bind="css: question.cssClasses.controlLabel">
<!-- ko template: { name: 'survey-string', data: $data.locText } -->
<!-- /ko -->
</div>
<!-- /ko -->

<!-- ko component: { name: question.itemContentComponent, params: { cssClasses: question.cssClasses, item: $data } } -->
<!-- /ko -->
</div>
</div>
</div>
</script>
</script>
13 changes: 12 additions & 1 deletion src/question_ranking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,16 @@ export class QuestionRankingModel extends QuestionCheckboxModel {
this.setPropertyValue("longTap", val);
}

/**
* The name of a component used to render items.
*/
public get itemContentComponent(): string {
return this.getPropertyValue("itemContentComponent", "sv-ranking-item-content");
}
public set itemContentComponent(value: string) {
this.setPropertyValue("itemContentComponent", value);
}

/**
* Specifies whether users can select choices they want to rank.
*
Expand Down Expand Up @@ -738,7 +748,8 @@ Serializer.addClass(
},
isSerializable: true
},
{ name: "itemComponent", visible: false, default: "" }
{ name: "itemComponent", visible: false, default: "" },
{ name: "itemContentComponent", visible: false, default: "sv-ranking-item-content" },
],
function () {
return new QuestionRankingModel("");
Expand Down
24 changes: 23 additions & 1 deletion src/react/reactquestion_ranking.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import * as React from "react";
import {
ReactSurveyElement,
SurveyElementBase,
SurveyQuestionElementBase,
} from "./reactquestion_element";
import { QuestionRankingModel, SurveyModel, ItemValue } from "survey-core";
import { ReactQuestionFactory } from "./reactquestion_factory";
import { ReactSurveyElementsWrapper } from "./reactsurveymodel";
import { ReactElementFactory } from "./element-factory";

export class SurveyQuestionRanking extends SurveyQuestionElementBase {
protected get question(): QuestionRankingModel {
Expand Down Expand Up @@ -103,6 +105,7 @@ export class SurveyQuestionRanking extends SurveyQuestionElementBase {
const index = i;
const indexText: string = this.question.getNumberByIndex(index);
const tabIndex: number = this.question.getItemTabIndex(item);

const renderedItem = (
<SurveyQuestionRankingItem
key={item.value}
Expand Down Expand Up @@ -176,6 +179,7 @@ export class SurveyQuestionRankingItem extends ReactSurveyElement {
}

protected renderElement(): JSX.Element {
let itemContentComponent = ReactElementFactory.Instance.createElement(this.question.itemContentComponent, { item: this.item, cssClasses: this.cssClasses });
return (
<div
tabIndex={this.itemTabIndex}
Expand Down Expand Up @@ -204,14 +208,32 @@ export class SurveyQuestionRankingItem extends ReactSurveyElement {
<div className={this.question.getItemIndexClasses(this.item)}>
{(!this.unrankedItem && this.indexText) ? this.indexText : this.renderEmptyIcon()}
</div>
<div className={this.cssClasses.controlLabel}>{this.text}</div>
{itemContentComponent}
</div>
</div>
</div>
);
}
}

export class SurveyQuestionRankingItemContent extends ReactSurveyElement {
protected get item(): ItemValue {
return this.props.item;
}

protected get cssClasses(): any {
return this.props.cssClasses;
}

protected renderElement(): JSX.Element {
return <div className={this.cssClasses.controlLabel}>{SurveyElementBase.renderLocString(this.item.locText)}</div>;
}
}

ReactElementFactory.Instance.registerElement("sv-ranking-item-content", props => {
return React.createElement(SurveyQuestionRankingItemContent, props);
});

ReactQuestionFactory.Instance.registerQuestion("ranking", (props) => {
return React.createElement(SurveyQuestionRanking, props);
});
18 changes: 18 additions & 0 deletions src/vue/ranking/ranking-item-content.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<template>
<div :class="cssClasses.controlLabel">
<survey-string :locString="item.locText" />
</div>
</template>

<script lang="ts">
import Vue from "vue";
import { Component, Prop } from "vue-property-decorator";
import { ItemValue } from "survey-core";
@Component
export class RankingItemContent extends Vue {
@Prop() item: ItemValue;
@Prop() cssClasses: any
}
Vue.component("sv-ranking-item-content", RankingItemContent);
export default RankingItemContent;
</script>
4 changes: 1 addition & 3 deletions src/vue/ranking/ranking-item.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@
<use :xlink:href="question.dashSvgIcon"></use>
</svg>
</div>
<div :class="cssClasses.controlLabel">
<survey-string :locString="text" />
</div>
<component :is="question.itemContentComponent" :item="item" :cssClasses="cssClasses"></component>
</div>
</div>
</div>
Expand Down
Loading

0 comments on commit 0dbc06d

Please sign in to comment.