Skip to content

Add tocItemComponent property and enhanced props for custom TOC items#11072

Draft
Copilot wants to merge 3 commits intomasterfrom
copilot/t26775-multi-page-form-question-count
Draft

Add tocItemComponent property and enhanced props for custom TOC items#11072
Copilot wants to merge 3 commits intomasterfrom
copilot/t26775-multi-page-form-question-count

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 25, 2026

Custom TOC item components required verbose internal API access and manual survey/page resolution. This adds a first-class tocItemComponent property and passes survey/page directly as props.

Changes

  • SurveyModel.tocItemComponent — new string property (serialized, hidden from designer) that sets the TOC list's itemComponent reactively
  • ListModel.onGetItemExtraComponentData — generic callback on ListModel to supply additional props to item components; TOC sets this to return { survey, page }
  • TOC action items now store { page, survey } in their data property
  • React/Vue 3/Angular list-item renderers spread extra component data from the callback when rendering item components
  • 4 new unit tests covering property binding, dynamic updates, data storage, and extra component data

New API

survey.tocItemComponent = "sv-custom-toc-item";

replaces:

survey.findLayoutElement("toc-navigation").getData().listModel.itemComponent = "sv-custom-toc-item";

Custom components now receive survey and page directly as props:

// Before
const survey = this.props.model.locOwner;
const page = survey.getPageByName(this.props.item.id);

// After
const survey = this.props.survey;
const page = this.props.page;

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • accounts.google.com
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --disable-dev-shm-usage --use-angle=swiftshader-webgl --crashpad-handler-pid=4603 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/karma-40020992 --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,18094749898004438211,11194549379751376931,262144 --disable-features=PaintHolding --variations-seed-version --trace-process-track-uuid=3190708989122997041 (dns block)
    • Triggering command: /opt/google/chrome/chrome /usr/bin/google-chrome --user-data-dir=/tmp/karma-40020992 --enable-automation --no-default-browser-check --no-first-run --disable-default-apps --disable-popup-blocking --disable-translate --disable-REDACTED-timer-throttling --disable-renderer-REDACTEDing --disable-device-discovery-notifications --no-sandbox --disable-gpu --no-default-browser-check --no-first-run --disable-default-apps --disable-popup-blocking --disable-translate --disable-REDACTED-timer-throttling --disable-renderer-REDACTEDing (dns block)
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --disable-dev-shm-usage --use-angle=swiftshader-webgl --crashpad-handler-pid=4786 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/karma-60412438 --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,5386423749754924228,14712679445128286092,262144 --disable-features=PaintHolding --variations-seed-version --trace-process-track-uuid=3190708989122997041 (dns block)
  • android.clients.google.com
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --disable-dev-shm-usage --use-angle=swiftshader-webgl --crashpad-handler-pid=4603 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/karma-40020992 --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,18094749898004438211,11194549379751376931,262144 --disable-features=PaintHolding --variations-seed-version --trace-process-track-uuid=3190708989122997041 (dns block)
    • Triggering command: /opt/google/chrome/chrome /usr/bin/google-chrome --user-data-dir=/tmp/karma-40020992 --enable-automation --no-default-browser-check --no-first-run --disable-default-apps --disable-popup-blocking --disable-translate --disable-REDACTED-timer-throttling --disable-renderer-REDACTEDing --disable-device-discovery-notifications --no-sandbox --disable-gpu --no-default-browser-check --no-first-run --disable-default-apps --disable-popup-blocking --disable-translate --disable-REDACTED-timer-throttling --disable-renderer-REDACTEDing (dns block)
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --disable-dev-shm-usage --use-angle=swiftshader-webgl --crashpad-handler-pid=4786 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/karma-60412438 --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,5386423749754924228,14712679445128286092,262144 --disable-features=PaintHolding --variations-seed-version --trace-process-track-uuid=3190708989122997041 (dns block)
  • clients2.google.com
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --disable-dev-shm-usage --use-angle=swiftshader-webgl --crashpad-handler-pid=4603 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/karma-40020992 --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,18094749898004438211,11194549379751376931,262144 --disable-features=PaintHolding --variations-seed-version --trace-process-track-uuid=3190708989122997041 (dns block)
    • Triggering command: /opt/google/chrome/chrome /usr/bin/google-chrome --user-data-dir=/tmp/karma-40020992 --enable-automation --no-default-browser-check --no-first-run --disable-default-apps --disable-popup-blocking --disable-translate --disable-REDACTED-timer-throttling --disable-renderer-REDACTEDing --disable-device-discovery-notifications --no-sandbox --disable-gpu --no-default-browser-check --no-first-run --disable-default-apps --disable-popup-blocking --disable-translate --disable-REDACTED-timer-throttling --disable-renderer-REDACTEDing (dns block)
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --disable-dev-shm-usage --use-angle=swiftshader-webgl --crashpad-handler-pid=4786 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/karma-60412438 --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,5386423749754924228,14712679445128286092,262144 --disable-features=PaintHolding --variations-seed-version --trace-process-track-uuid=3190708989122997041 (dns block)
  • clientservices.googleapis.com
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --disable-dev-shm-usage --use-angle=swiftshader-webgl --crashpad-handler-pid=4603 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/karma-40020992 --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,18094749898004438211,11194549379751376931,262144 --disable-features=PaintHolding --variations-seed-version --trace-process-track-uuid=3190708989122997041 (dns block)
    • Triggering command: /opt/google/chrome/chrome /usr/bin/google-chrome --user-data-dir=/tmp/karma-40020992 --enable-automation --no-default-browser-check --no-first-run --disable-default-apps --disable-popup-blocking --disable-translate --disable-REDACTED-timer-throttling --disable-renderer-REDACTEDing --disable-device-discovery-notifications --no-sandbox --disable-gpu --no-default-browser-check --no-first-run --disable-default-apps --disable-popup-blocking --disable-translate --disable-REDACTED-timer-throttling --disable-renderer-REDACTEDing (dns block)
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --disable-dev-shm-usage --use-angle=swiftshader-webgl --crashpad-handler-pid=4786 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/karma-60412438 --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,5386423749754924228,14712679445128286092,262144 --disable-features=PaintHolding --variations-seed-version --trace-process-track-uuid=3190708989122997041 (dns block)
  • optimizationguide-pa.googleapis.com
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --disable-dev-shm-usage --use-angle=swiftshader-webgl --crashpad-handler-pid=4603 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/karma-40020992 --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,18094749898004438211,11194549379751376931,262144 --disable-features=PaintHolding --variations-seed-version --trace-process-track-uuid=3190708989122997041 (dns block)
    • Triggering command: /opt/google/chrome/chrome /usr/bin/google-chrome --user-data-dir=/tmp/karma-40020992 --enable-automation --no-default-browser-check --no-first-run --disable-default-apps --disable-popup-blocking --disable-translate --disable-REDACTED-timer-throttling --disable-renderer-REDACTEDing --disable-device-discovery-notifications --no-sandbox --disable-gpu --no-default-browser-check --no-first-run --disable-default-apps --disable-popup-blocking --disable-translate --disable-REDACTED-timer-throttling --disable-renderer-REDACTEDing (dns block)
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --disable-dev-shm-usage --use-angle=swiftshader-webgl --crashpad-handler-pid=4786 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/karma-60412438 --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,5386423749754924228,14712679445128286092,262144 --disable-features=PaintHolding --variations-seed-version --trace-process-track-uuid=3190708989122997041 (dns block)
  • redirector.gvt1.com
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --disable-dev-shm-usage --use-angle=swiftshader-webgl --crashpad-handler-pid=4603 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/karma-40020992 --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,18094749898004438211,11194549379751376931,262144 --disable-features=PaintHolding --variations-seed-version --trace-process-track-uuid=3190708989122997041 (dns block)
    • Triggering command: /opt/google/chrome/chrome /usr/bin/google-chrome --user-data-dir=/tmp/karma-40020992 --enable-automation --no-default-browser-check --no-first-run --disable-default-apps --disable-popup-blocking --disable-translate --disable-REDACTED-timer-throttling --disable-renderer-REDACTEDing --disable-device-discovery-notifications --no-sandbox --disable-gpu --no-default-browser-check --no-first-run --disable-default-apps --disable-popup-blocking --disable-translate --disable-REDACTED-timer-throttling --disable-renderer-REDACTEDing (dns block)
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --disable-dev-shm-usage --use-angle=swiftshader-webgl --crashpad-handler-pid=4786 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/karma-60412438 --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,5386423749754924228,14712679445128286092,262144 --disable-features=PaintHolding --variations-seed-version --trace-process-track-uuid=3190708989122997041 (dns block)
  • safebrowsingohttpgateway.googleapis.com
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --disable-dev-shm-usage --use-angle=swiftshader-webgl --crashpad-handler-pid=4603 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/karma-40020992 --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,18094749898004438211,11194549379751376931,262144 --disable-features=PaintHolding --variations-seed-version --trace-process-track-uuid=3190708989122997041 (dns block)
    • Triggering command: /opt/google/chrome/chrome /usr/bin/google-chrome --user-data-dir=/tmp/karma-40020992 --enable-automation --no-default-browser-check --no-first-run --disable-default-apps --disable-popup-blocking --disable-translate --disable-REDACTED-timer-throttling --disable-renderer-REDACTEDing --disable-device-discovery-notifications --no-sandbox --disable-gpu --no-default-browser-check --no-first-run --disable-default-apps --disable-popup-blocking --disable-translate --disable-REDACTED-timer-throttling --disable-renderer-REDACTEDing (dns block)
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --disable-dev-shm-usage --use-angle=swiftshader-webgl --crashpad-handler-pid=4786 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/karma-60412438 --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,5386423749754924228,14712679445128286092,262144 --disable-features=PaintHolding --variations-seed-version --trace-process-track-uuid=3190708989122997041 (dns block)
  • www.google.com
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --disable-dev-shm-usage --use-angle=swiftshader-webgl --crashpad-handler-pid=4603 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/karma-40020992 --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,18094749898004438211,11194549379751376931,262144 --disable-features=PaintHolding --variations-seed-version --trace-process-track-uuid=3190708989122997041 (dns block)
    • Triggering command: /opt/google/chrome/chrome /usr/bin/google-chrome --user-data-dir=/tmp/karma-40020992 --enable-automation --no-default-browser-check --no-first-run --disable-default-apps --disable-popup-blocking --disable-translate --disable-REDACTED-timer-throttling --disable-renderer-REDACTEDing --disable-device-discovery-notifications --no-sandbox --disable-gpu --no-default-browser-check --no-first-run --disable-default-apps --disable-popup-blocking --disable-translate --disable-REDACTED-timer-throttling --disable-renderer-REDACTEDing (dns block)
    • Triggering command: /proc/self/exe /proc/self/exe --type=utility --utility-sub-type=network.mojom.NetworkService --lang=en-US --service-sandbox-type=none --no-sandbox --disable-dev-shm-usage --use-angle=swiftshader-webgl --crashpad-handler-pid=4786 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/karma-60412438 --change-stack-guard-on-fork=enable --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,5386423749754924228,14712679445128286092,262144 --disable-features=PaintHolding --variations-seed-version --trace-process-track-uuid=3190708989122997041 (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

This section details on the original issue you should resolve

<issue_title>Table Of Contents - Custom TOC Item API Enhancement</issue_title>
<issue_description>T26775 - Multi page form show the count of questions on each page title
https://surveyjs.answerdesk.io/internal/ticket/details/T26775
T23589 - Table of Contents - Display the number of total and answered questions
https://surveyjs.answerdesk.io/internal/ticket/details/T23589
T14475 - Is it possible to show the progress bar based on the pages?
https://surveyjs.answerdesk.io/internal/ticket/details/T14475


The current API: View Demo.

class CustomTOCItem extends ReactSurveyElement {
    constructor(props) {
        super(props);
    }
    render() {
        const survey = this.props.model.locOwner;
        const pageName = this.props.item.id;
        const page = survey.getPageByName(pageName);
        
        const pageQuestions = page.questions;
        const totalNumberOfQuestions = pageQuestions.length;
        const answeredQuestionsPerPage = pageQuestions.filter(question => !question.isEmpty()).length;

        const text = page.title || page.name;
        const content = (
            <span>
                {text} {" (Answered "}
                <strong>{answeredQuestionsPerPage}</strong>
                {"/"}
                <strong>{totalNumberOfQuestions}</strong>
                {")"}
            </span>
        );

        const backgroundClass =
            answeredQuestionsPerPage === 0
                ? "no-answered"
                : answeredQuestionsPerPage === totalNumberOfQuestions
                    ? "all-answered"
                    : "some-answered";

        return (
            <div className={`toc-item-container ${backgroundClass}`}>
                {content}
            </div>
        );
    }
}

ReactElementFactory.Instance.registerElement("sv-custom-toc-item", (props) => {
    return React.createElement(CustomTOCItem, props);
});
//...
survey.findLayoutElement("toc-navigation").getData().listModel.itemComponent = "sv-custom-toc-item";

Please consider the following API enhancements:

  • Introduce a property within SurveyModel which would allow developers to specify a custom TOC component name.
survey.tocItemComponent = "sv-custom-toc-item";
  • Update a custom TOC item component props and add all required objects:
    • Instead of const survey = this.props.model.locOwner, use this.props.survey
    • Instead of this
       const pageName = this.props.item.id;
       const page = survey.getPageByName(pageName);
    
    Add the this.props.page.
    • Perhaps, add this.props.item to access a current IAction.</issue_description>

Comments on the Issue (you are @copilot in this section)

@JaneSjs More demos are available at https://github.com//issues/7320#issuecomment-2345364992 @JaneSjs https://surveyjs.io/form-library/examples/table-of-contents-page-progress [Vue 3 Demo](https://codesandbox.io/p/devbox/surveyjs-library-vue3-forked-mf9yx5?file=%2Fsrc%2Fmain.ts%3A6%2C51&workspaceId=ws_QrfppueirPWWh9z2ztBr8j)

📱 Kick off Copilot coding agent tasks wherever you are with GitHub Mobile, available on iOS and Android.

Copilot AI and others added 2 commits March 25, 2026 09:33
- Add tocItemComponent property to SurveyModel
- Store page/survey references on TOC Action items data
- Apply tocItemComponent to listModel.itemComponent
- Add onGetItemExtraComponentData to ListModel for passing extra props
- Update React, Vue, Angular list-item rendering to spread extra data
- Add unit tests for new functionality

Co-authored-by: tsv2013 <3013415+tsv2013@users.noreply.github.com>
Agent-Logs-Url: https://github.com/surveyjs/survey-library/sessions/b5699f73-c24a-44ed-b3c5-8104056d3b0f
Copilot AI changed the title [WIP] Enhance Custom TOC Item API to display question count Add tocItemComponent property and enhanced props for custom TOC items Mar 25, 2026
Copilot AI requested a review from tsv2013 March 25, 2026 09:39
@JaneSjs
Copy link
Copy Markdown
Contributor

JaneSjs commented Mar 25, 2026

Examples

Vue

// CustomTocItem.vue
<template>
  <div :class="['toc-item-container', backgroundClass]">
    <span>
      {{ pageTitle }} (Answered <strong>{{ answeredQuestions }}</strong
      >/<strong>{{ totalQuestions }}</strong
      >)
    </span>
  </div>
</template>

<script setup lang="ts">
import type { PageModel } from "survey-core";
import { computed } from "vue";

const props = defineProps<{ page: PageModel }>();

const page = props.page;

const totalQuestions = computed(
  () => page.questions.filter((q) => q.visible).length
);
const answeredQuestions = computed(
  () => page.questions.filter((question) => !question.isEmpty()).length
);

const pageTitle = computed(() => page.title || page.name);

const backgroundClass = computed(() => {
  if (answeredQuestions.value === 0) return "not-answered";
  if (answeredQuestions.value === totalQuestions.value) return "all-answered";
  return "some-answered";
});
</script>
<style scoped>
.toc-item-container {
  width: 100%;
  height: 80px;
  padding: 10px;
  border-radius: 8px;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  flex-wrap: wrap;
  color: #333;
}

.not-answered {
  background-color: #F7A8A9;
}

.all-answered {
  background-color: #4BCC8A;
}

.some-answered {
  background-color: #F8E67E;
}
</style>

React

<style> 
        .toc-item-container {
            width: 100%;
            height: 80px;
            padding: 10px;
            border-radius: 8px;
            box-sizing: border-box;
            display: flex;
            align-items: center;
            justify-content: center;
            overflow: hidden;
            flex-wrap: wrap;
            color: #333;
        }

        .no-answered {
            background-color: #F7A8A9; 
        }

        .all-answered {
            background-color: #4BCC8A;
        }

        .some-answered {
            background-color: #F8E67E;
        }
    </style>
//...
class CustomTOCItem extends SurveyReact.ReactSurveyElement {
  constructor(props) {
    super(props);
  }
  render() {
    const page = this.props.page;
    const visiblePageQuestions = page.questions.filter((question) => question.visible);
    const totalNumberOfQuestions = visiblePageQuestions.length;
    const answeredQuestionsPerPage = visiblePageQuestions.filter(
      (question) => !question.isEmpty()
    ).length;

    const text = page.title || page.name;
    const content = (
      <span>
        {text} {" (Answered "}
        <strong>{answeredQuestionsPerPage}</strong>
        {"/"}
        <strong>{totalNumberOfQuestions}</strong>
        {")"}
      </span>
    );

    const backgroundClass =
      answeredQuestionsPerPage === 0
        ? "no-answered"
        : answeredQuestionsPerPage === totalNumberOfQuestions
        ? "all-answered"
        : "some-answered";

    return (
      <div className={`toc-item-container ${backgroundClass}`}>{content}</div>
    );
  }
}

SurveyReact.ReactElementFactory.Instance.registerElement("sv-custom-toc-item", (props) => {
  return React.createElement(CustomTOCItem, props);
});

Angular

// toc-custom-item.component.ts

import { Component, Input, ViewEncapsulation  } from "@angular/core";
import { AngularComponentFactory } from "survey-angular-ui";
import { Action, PageModel } from "survey-core";

@Component({
    // tslint:disable-next-line:component-selector
    selector: "sv-ng-custom-toc-item",
    templateUrl: "./toc-custom-item.component.html",
    styleUrls: ["./toc-custom-item.component.css"],
    encapsulation:  ViewEncapsulation.None,
})
export class CustomTocItem {    
    @Input() model!: Action;

    private _page?: PageModel;

    @Input()
    get page(): PageModel {
        // fallback to model.data.page if _page is not set
        return this._page || this.model?.data?.page;
    }
    set page(value: PageModel) {
        this._page = value;
    }   

    get answeredQuestionsPerPage(): number {        
        return this.page.questions.filter((question: any) => !question.isEmpty()).length;
    }

    get totalNumberOfQuestions(): number {
        return this.page.questions.length;
    }

    get backgroundColor(): string {
        const answered = this.answeredQuestionsPerPage;
        const total = this.totalNumberOfQuestions;
        if (answered === 0) return "not-answered";
        if (answered === total) return "all-answered";
        return "some-answered";
    }
}
AngularComponentFactory.Instance.registerComponent(
    "custom-toc-item",
    CustomTocItem
);
// toc-custom-item.component.html
<div class="toc-item-container" [ngClass]="backgroundColor">
    <span>
        {{ page.title || page.name }} (Answered
        <strong>{{ answeredQuestionsPerPage }}</strong>/<strong>{{ totalNumberOfQuestions }}</strong>)
    </span>
</div>
//toc-custom-item.component.css
.toc-item-container {
    width: 100%;
    height: 80px;
    padding: 10px;
    border-radius: 8px;
    box-sizing: border-box;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    flex-wrap: wrap;
    color: #333;
}

.not-answered {
    background-color: #f7a8a9;
}

.all-answered {
    background-color: #4bcc8a;
}

.some-answered {
    background-color: #f8e67e;
}

@JaneSjs JaneSjs requested a review from RomanTsukanov March 25, 2026 14:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Table Of Contents - Custom TOC Item API Enhancement

3 participants