Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<div [class]="listModel.cssClasses.itemBody" [style]="itemStyle" [attr.title]="model.getTooltip()"
(mouseover)="listModel.onItemHover(model)"
(mouseleave)="listModel.onItemLeave(model)">
<ng-template [component]="{ name: itemComponent, data: { model: model, listModel: listModel } }"></ng-template>
<ng-template [component]="{ name: itemComponent, data: itemComponentData }"></ng-template>
</div>
</li>
</ng-template>
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ export class ListItemComponent extends BaseAngular implements AfterViewInit {
get itemComponent(): string {
return this.model.component || this.listModel.itemComponent;
}
get itemComponentData(): any {
const extraData = this.listModel.onGetItemExtraComponentData ? this.listModel.onGetItemExtraComponentData(this.model) : undefined;
return { model: this.model, listModel: this.listModel, ...extraData };
}

getModel() {
return this.model;
Expand Down
1 change: 1 addition & 0 deletions packages/survey-core/src/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export class ListModel<T extends BaseAction = Action> extends ActionContainer<T>
public scrollHandler: (e?: any) => void;
public loadingIndicatorVisibilityObserver: (isVisible: boolean) => void;
public areSameItemsCallback: (item1: IAction, item2: IAction) => boolean;
public onGetItemExtraComponentData: (item: T) => any;

private hasText(item: T, filterStringInLow: string): boolean {
if (!filterStringInLow) return true;
Expand Down
17 changes: 17 additions & 0 deletions packages/survey-core/src/survey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1664,6 +1664,19 @@ export class SurveyModel extends SurveyElementCore
* @see showTOC
*/
@property() tocLocation: "left" | "right";
/**
* Specifies a custom component name for table of contents items. Applies only when [`showTOC`](https://surveyjs.io/form-library/documentation/api-reference/survey-data-model#showTOC) is enabled.
*
* The custom component receives the following props:
*
* - `item` - The action item ([`IAction`](https://surveyjs.io/form-library/documentation/api-reference/iaction)).
* - `model` - The list model ([`ListModel`](https://surveyjs.io/form-library/documentation/api-reference/list-model)).
* - `survey` - The survey model ([`SurveyModel`](https://surveyjs.io/form-library/documentation/api-reference/survey-data-model)).
* - `page` - The page model ([`PageModel`](https://surveyjs.io/form-library/documentation/api-reference/page-model)).
* @see showTOC
* @see tocLocation
*/
@property() tocItemComponent: string;
/**
* Specifies whether to display the [survey title](https://surveyjs.io/form-library/documentation/api-reference/survey-data-model#title).
*
Expand Down Expand Up @@ -8651,6 +8664,10 @@ Serializer.addClass("survey", [
dependsOn: ["showTOC"],
visibleIf: (survey: any) => { return !!survey && survey.showTOC; }
},
{
name: "tocItemComponent",
visible: false
},
{ name: "readOnly:boolean" },
{ name: "mode", visible: false, isSerializable: false },
{ name: "storeOthersAsComment:boolean", default: true },
Expand Down
14 changes: 14 additions & 0 deletions packages/survey-core/src/surveyToc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,18 @@ export function createTOCListModel(survey: SurveyModel, onAction?: () => void):
};
var listModel = new ListModel(listOptions as any);
listModel.allowSelection = false;
if (survey.tocItemComponent) {
listModel.itemComponent = survey.tocItemComponent;
}
listModel.onGetItemExtraComponentData = (item: Action) => {
return { survey: survey, page: item.data?.page };
};
const updateTocItemComponent = () => {
if (survey.tocItemComponent) {
listModel.itemComponent = survey.tocItemComponent;
}
};
survey.registerFunctionOnPropertyValueChanged("tocItemComponent", updateTocItemComponent, "toc");
const updateSelectedItem = (currentPage: PageModel, defaultSelection?: IAction) => {
listModel.selectedItem = !!currentPage && listModel.actions.filter(i => i.id === currentPage.name)[0] || defaultSelection;
};
Expand All @@ -66,6 +78,7 @@ function getTOCItems(survey: SurveyModel, onAction: () => void) {
return new Action({
id: page.name,
locTitle: page.locNavigationTitle,
data: { page: page, survey: survey },
action: () => {
DomDocumentHelper.activeElementBlur();
!!onAction && onAction();
Expand Down Expand Up @@ -155,6 +168,7 @@ export class TOCModel {
};
public dispose(): void {
this.survey.unRegisterFunctionOnPropertyValueChanged("_isMobile", "toc");
this.survey.unRegisterFunctionOnPropertyValueChanged("tocItemComponent", "toc");
const [handler] = this.survey.unRegisterFunctionOnPropertyValueChanged("pages", "toc");
this.survey.onEndLoadingFromJson.remove(handler);
this.survey.onPageAdded.remove(handler);
Expand Down
134 changes: 134 additions & 0 deletions packages/survey-core/tests/surveyTOCTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -969,3 +969,137 @@ QUnit.test("update TOC pages on survey loaded from JSON", function (assert) {
assert.equal(tocListModel.actions[0].title, survey.pages[0].name, "Page 1 is visible in TOC actions");
assert.equal(tocListModel.actions[1].title, survey.pages[1].name, "Page 2 is visible in TOC actions");
});

QUnit.test("tocItemComponent sets listModel.itemComponent", function (assert) {
let json: any = {
"pages": [
{
"name": "page1",
"elements": [
{
"type": "text",
"name": "question1"
}
]
},
{
"name": "page2",
"elements": [
{
"type": "text",
"name": "question2"
}
]
}
],
"showTOC": true,
"tocItemComponent": "sv-custom-toc-item"
};
let survey: SurveyModel = new SurveyModel(json);
let tocListModel = createTOCListModel(survey);

assert.equal(tocListModel.itemComponent, "sv-custom-toc-item", "listModel.itemComponent is set from survey.tocItemComponent");
});

QUnit.test("tocItemComponent updates listModel.itemComponent dynamically", function (assert) {
let json: any = {
"pages": [
{
"name": "page1",
"elements": [
{
"type": "text",
"name": "question1"
}
]
},
{
"name": "page2",
"elements": [
{
"type": "text",
"name": "question2"
}
]
}
],
"showTOC": true
};
let survey: SurveyModel = new SurveyModel(json);
let tocListModel = createTOCListModel(survey);

assert.equal(tocListModel.itemComponent, "sv-list-item-content", "default itemComponent");

survey.tocItemComponent = "sv-custom-toc-item";
assert.equal(tocListModel.itemComponent, "sv-custom-toc-item", "listModel.itemComponent is updated when survey.tocItemComponent changes");
});

QUnit.test("TOC items have page and survey in data", function (assert) {
let json: any = {
"pages": [
{
"name": "page1",
"elements": [
{
"type": "text",
"name": "question1"
}
]
},
{
"name": "page2",
"elements": [
{
"type": "text",
"name": "question2"
}
]
}
],
"showTOC": true
};
let survey: SurveyModel = new SurveyModel(json);
let tocListModel = createTOCListModel(survey);

assert.ok(tocListModel.actions[0].data, "First item has data");
assert.equal(tocListModel.actions[0].data.page, survey.pages[0], "First item data.page is the first page");
assert.equal(tocListModel.actions[0].data.survey, survey, "First item data.survey is the survey");
assert.equal(tocListModel.actions[1].data.page, survey.pages[1], "Second item data.page is the second page");
assert.equal(tocListModel.actions[1].data.survey, survey, "Second item data.survey is the survey");
});

QUnit.test("TOC listModel.onGetItemExtraComponentData returns survey and page", function (assert) {
let json: any = {
"pages": [
{
"name": "page1",
"elements": [
{
"type": "text",
"name": "question1"
}
]
},
{
"name": "page2",
"elements": [
{
"type": "text",
"name": "question2"
}
]
}
],
"showTOC": true
};
let survey: SurveyModel = new SurveyModel(json);
let tocListModel = createTOCListModel(survey);

assert.ok(tocListModel.onGetItemExtraComponentData, "onGetItemExtraComponentData is set");
const extraData = tocListModel.onGetItemExtraComponentData(tocListModel.actions[0]);
assert.equal(extraData.survey, survey, "Extra data contains the survey");
assert.equal(extraData.page, survey.pages[0], "Extra data contains the page for first item");

const extraData2 = tocListModel.onGetItemExtraComponentData(tocListModel.actions[1]);
assert.equal(extraData2.page, survey.pages[1], "Extra data contains the page for second item");
});
3 changes: 2 additions & 1 deletion packages/survey-react-ui/src/components/list/list-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ export class ListItem extends SurveyElementBase<IListItemProps, any> {
if (!this.item) return null;
const className = this.model.getItemClass(this.item);
const itemContent = this.item.component || this.model.itemComponent;
const newElement = ReactElementFactory.Instance.createElement(itemContent, { item: this.item, key: this.item.id, model: this.model });
const extraData = this.model.onGetItemExtraComponentData ? this.model.onGetItemExtraComponentData(this.item) : undefined;
const newElement = ReactElementFactory.Instance.createElement(itemContent, { item: this.item, key: this.item.id, model: this.model, ...extraData });
const contentWrap =
<div
ref={this.elementRef}
Expand Down
5 changes: 5 additions & 0 deletions packages/survey-vue3-ui/src/components/list/ListItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
:is="itemComponent"
:item="item"
:model="model"
v-bind="extraComponentData"
></SvComponent>
</div>
</li>
Expand Down Expand Up @@ -56,6 +57,10 @@ const itemComponent = computed(
() => props.item.component || props.model.itemComponent
);

const extraComponentData = computed(
() => props.model.onGetItemExtraComponentData ? props.model.onGetItemExtraComponentData(props.item as any) : undefined
);

onMounted(() => {
setTimeout(() => {
props.model.onItemRended(props.item as any, elementRef.value);
Expand Down
Loading