diff --git a/browser/package-lock.json b/browser/package-lock.json
index 685144ce6a..96c36c7ed3 100644
--- a/browser/package-lock.json
+++ b/browser/package-lock.json
@@ -9697,6 +9697,46 @@
"node": ">=22"
}
},
+ "node_modules/igniteui-grid-lite/node_modules/igniteui-webcomponents": {
+ "version": "7.2.4",
+ "resolved": "https://registry.npmjs.org/igniteui-webcomponents/-/igniteui-webcomponents-7.2.4.tgz",
+ "integrity": "sha512-h5hrroBn0kSYCYpj5OH4sKXkFZvdOeqpYnNTKMB5PsqsWexhQICkH5xKjDDj3VPbd135OvzWX5XQN0Nclj2qAQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/dom": "^1.7.0",
+ "@lit-labs/virtualizer": "^2.1.0",
+ "@lit/context": "^1.1.0",
+ "igniteui-i18n-core": "^1.0.5",
+ "lit": "^3.3.0"
+ },
+ "engines": {
+ "node": ">=22"
+ },
+ "peerDependencies": {
+ "dompurify": "^3.3.0",
+ "igniteui-i18n-resources": "^1.0.5",
+ "marked": "^17.0.0",
+ "marked-shiki": "^1.2.0",
+ "shiki": "^3.20.0"
+ },
+ "peerDependenciesMeta": {
+ "dompurify": {
+ "optional": true
+ },
+ "igniteui-i18n-resources": {
+ "optional": true
+ },
+ "marked": {
+ "optional": true
+ },
+ "marked-shiki": {
+ "optional": true
+ },
+ "shiki": {
+ "optional": true
+ }
+ }
+ },
"node_modules/igniteui-i18n-core": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/igniteui-i18n-core/-/igniteui-i18n-core-1.0.5.tgz",
diff --git a/samples/layouts/virtual-scroll/horizontal/.devcontainer/devcontainer.json b/samples/layouts/virtual-scroll/horizontal/.devcontainer/devcontainer.json
new file mode 100644
index 0000000000..e0b8e9c925
--- /dev/null
+++ b/samples/layouts/virtual-scroll/horizontal/.devcontainer/devcontainer.json
@@ -0,0 +1,4 @@
+{
+ "name": "Node.js",
+ "image": "mcr.microsoft.com/devcontainers/javascript-node:22"
+}
\ No newline at end of file
diff --git a/samples/layouts/virtual-scroll/horizontal/.eslintrc.js b/samples/layouts/virtual-scroll/horizontal/.eslintrc.js
new file mode 100644
index 0000000000..b45160a9ee
--- /dev/null
+++ b/samples/layouts/virtual-scroll/horizontal/.eslintrc.js
@@ -0,0 +1,75 @@
+// https://www.robertcooper.me/using-eslint-and-prettier-in-a-typescript-project
+module.exports = {
+ parser: "@typescript-eslint/parser", // Specifies the ESLint parser
+ parserOptions: {
+ ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features
+ sourceType: "module", // Allows for the use of imports
+ ecmaFeatures: {
+ jsx: true // Allows for the parsing of JSX
+ }
+ },
+ settings: {
+ react: {
+ version: "999.999.999" // Tells eslint-plugin-react to automatically detect the version of React to use
+ }
+ },
+ extends: [
+ "eslint:recommended",
+ "plugin:react/recommended", // Uses the recommended rules from @eslint-plugin-react
+ "plugin:@typescript-eslint/recommended" // Uses the recommended rules from @typescript-eslint/eslint-plugin
+ ],
+ rules: {
+ // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
+ "default-case": "off",
+ "no-undef": "off",
+ "no-unused-vars": "off",
+ "no-extend-native": "off",
+ "no-throw-literal": "off",
+ "no-useless-concat": "off",
+ "no-mixed-operators": "off",
+ "no-prototype-builtins": "off",
+ "prefer-const": "off",
+ "prefer-rest-params": "off",
+ "jsx-a11y/alt-text": "off",
+ "jsx-a11y/iframe-has-title": "off",
+ "@typescript-eslint/no-unused-vars": "off",
+ "@typescript-eslint/no-explicit-any": "off",
+ "@typescript-eslint/no-inferrable-types": "off",
+ "@typescript-eslint/no-useless-constructor": "off",
+ "@typescript-eslint/no-use-before-define": "off",
+ "@typescript-eslint/no-non-null-assertion": "off",
+ "@typescript-eslint/interface-name-prefix": "off",
+ "@typescript-eslint/prefer-namespace-keyword": "off",
+ "@typescript-eslint/explicit-function-return-type": "off",
+ "@typescript-eslint/explicit-module-boundary-types": "off"
+ },
+ "overrides": [
+ {
+ "files": ["*.ts", "*.tsx"],
+ "rules": {
+ "default-case": "off",
+ "no-undef": "off",
+ "no-unused-vars": "off",
+ "no-extend-native": "off",
+ "no-throw-literal": "off",
+ "no-useless-concat": "off",
+ "no-mixed-operators": "off",
+ "no-prototype-builtins": "off",
+ "prefer-const": "off",
+ "prefer-rest-params": "off",
+ "jsx-a11y/alt-text": "off",
+ "jsx-a11y/iframe-has-title": "off",
+ "@typescript-eslint/no-unused-vars": "off",
+ "@typescript-eslint/no-explicit-any": "off",
+ "@typescript-eslint/no-inferrable-types": "off",
+ "@typescript-eslint/no-useless-constructor": "off",
+ "@typescript-eslint/no-use-before-define": "off",
+ "@typescript-eslint/no-non-null-assertion": "off",
+ "@typescript-eslint/interface-name-prefix": "off",
+ "@typescript-eslint/prefer-namespace-keyword": "off",
+ "@typescript-eslint/explicit-function-return-type": "off",
+ "@typescript-eslint/explicit-module-boundary-types": "off"
+ }
+ }
+ ]
+ };
\ No newline at end of file
diff --git a/samples/layouts/virtual-scroll/horizontal/README.md b/samples/layouts/virtual-scroll/horizontal/README.md
new file mode 100644
index 0000000000..b5a5754eef
--- /dev/null
+++ b/samples/layouts/virtual-scroll/horizontal/README.md
@@ -0,0 +1,56 @@
+
+
+
+This folder contains implementation of React application with example of Horizontal feature using [Virtual Scroll](https://www.infragistics.com/products/ignite-ui-react/react/components/general-getting-started.html) component.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Branches
+
+> **_NOTE:_** You should use [master](https://github.com/IgniteUI/igniteui-react-examples/tree/master) branch of this repository if you want to run samples on your computer. Use the [vnext](https://github.com/IgniteUI/igniteui-react-examples/tree/vnext) branch only when you want to contribute new samples to this repository.
+
+## Instructions
+
+Follow these instructions to run this example:
+
+
+```
+git clone https://github.com/IgniteUI/igniteui-react-examples.git
+git checkout master
+cd ./igniteui-react-examples
+cd ./samples/layouts/virtual-scroll/horizontal
+```
+
+open above folder in VS Code or type:
+```
+code .
+```
+
+In terminal window, run:
+```
+npm install --legacy-peer-deps
+npm run-script start
+```
+
+Then open http://localhost:4200/ in your browser
+
+
+## Learn More
+
+To learn more about **Ignite UI for React** components, check out the [React documentation](https://www.infragistics.com/products/ignite-ui-react/react/components/general-getting-started.html).
diff --git a/samples/layouts/virtual-scroll/horizontal/index.html b/samples/layouts/virtual-scroll/horizontal/index.html
new file mode 100644
index 0000000000..03972d37d0
--- /dev/null
+++ b/samples/layouts/virtual-scroll/horizontal/index.html
@@ -0,0 +1,11 @@
+
+
+
+ VirtualScrollHorizontal
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/layouts/virtual-scroll/horizontal/package.json b/samples/layouts/virtual-scroll/horizontal/package.json
new file mode 100644
index 0000000000..c4c8ae1cb0
--- /dev/null
+++ b/samples/layouts/virtual-scroll/horizontal/package.json
@@ -0,0 +1,42 @@
+{
+ "name": "react-virtual-scroll-horizontal",
+ "description": "This project provides example of Horizontal Orientation feature using Infragistics React components",
+ "author": "Infragistics",
+ "homepage": ".",
+ "version": "1.4.0",
+ "private": true,
+ "scripts": {
+ "start": "vite --port 4200",
+ "build": "tsc && node --max-old-space-size=4096 node_modules/vite/bin/vite build",
+ "preview": "vite preview",
+ "test": "vitest"
+ },
+ "dependencies": {
+ "igniteui-dockmanager": "^1.17.0",
+ "igniteui-react": "19.9.0",
+ "igniteui-react-core": "19.6.0",
+ "igniteui-webcomponents": "^7.4.1",
+ "react": "^19.2.0",
+ "react-dom": "^19.2.0",
+ "tslib": "^2.4.0"
+ },
+ "devDependencies": {
+ "@types/jest": "^29.2.0",
+ "@types/node": "^24.7.1",
+ "@types/react": "^18.0.24",
+ "@types/react-dom": "^18.0.8",
+ "@vitejs/plugin-react": "^5.0.4",
+ "@vitest/browser": "^3.2.4",
+ "typescript": "^4.8.4",
+ "vite": "^7.1.9",
+ "vitest": "^3.2.4",
+ "vitest-canvas-mock": "^0.3.3",
+ "worker-loader": "^3.0.8"
+ },
+ "browserslist": [
+ ">0.2%",
+ "not dead",
+ "not ie <= 11",
+ "not op_mini all"
+ ]
+}
diff --git a/samples/layouts/virtual-scroll/horizontal/sandbox.config.json b/samples/layouts/virtual-scroll/horizontal/sandbox.config.json
new file mode 100644
index 0000000000..00acba0c10
--- /dev/null
+++ b/samples/layouts/virtual-scroll/horizontal/sandbox.config.json
@@ -0,0 +1,5 @@
+{
+ "infiniteLoopProtection": false,
+ "hardReloadOnChange": false,
+ "view": "browser"
+}
\ No newline at end of file
diff --git a/samples/layouts/virtual-scroll/horizontal/src/EmployeeData.ts b/samples/layouts/virtual-scroll/horizontal/src/EmployeeData.ts
new file mode 100644
index 0000000000..cc0f0ea228
--- /dev/null
+++ b/samples/layouts/virtual-scroll/horizontal/src/EmployeeData.ts
@@ -0,0 +1,46 @@
+export type DepartmentVariant = "primary" | "info" | "success" | "warning" | "danger";
+
+export interface Employee {
+ id: number;
+ name: string;
+ initials: string;
+ email: string;
+ department: string;
+ variant: DepartmentVariant;
+ bio: string | null;
+}
+
+const DEPARTMENTS = ["Engineering", "Design", "Marketing", "Sales", "HR", "Finance", "Legal", "Operations"];
+const VARIANTS: DepartmentVariant[] = ["primary", "info", "success", "warning", "danger"];
+
+const FIRST_NAMES = ["Alice", "Bob", "Carol", "David", "Eve", "Frank", "Grace", "Henry", "Iris", "Jack", "Karen", "Leo", "Mia", "Noah", "Olivia", "Paul"];
+
+const LAST_NAMES = ["Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis", "Wilson", "Moore", "Taylor", "Anderson", "Thomas", "Jackson"];
+
+const BIOS = [
+ "Leads a cross-functional team across three time zones and owns the quarterly delivery plan.",
+ "Specializes in scalable service architecture.",
+ "Passionate about accessible, pixel-perfect user interfaces and design systems that scale across products.",
+ "Drives product strategy and stakeholder alignment.",
+ "Champions data-driven decision making, runs the weekly metrics review and mentors new analysts."
+];
+
+export function createEmployee(index: number): Employee {
+ const first = FIRST_NAMES[index % FIRST_NAMES.length];
+ const last = LAST_NAMES[Math.floor(index / FIRST_NAMES.length) % LAST_NAMES.length];
+ const departmentIndex = index % DEPARTMENTS.length;
+
+ return {
+ id: index + 1,
+ name: `${first} ${last}`,
+ initials: `${first[0]}${last[0]}`,
+ email: `${first.toLowerCase()}.${last.toLowerCase()}${index + 1}@example.com`,
+ department: DEPARTMENTS[departmentIndex],
+ variant: VARIANTS[departmentIndex % VARIANTS.length],
+ bio: index % 3 === 0 ? BIOS[index % BIOS.length] : null
+ };
+}
+
+export function generateEmployees(count: number, startIndex = 0): Employee[] {
+ return Array.from({ length: count }, (_, i) => createEmployee(startIndex + i));
+}
diff --git a/samples/layouts/virtual-scroll/horizontal/src/index.css b/samples/layouts/virtual-scroll/horizontal/src/index.css
new file mode 100644
index 0000000000..748ffa5655
--- /dev/null
+++ b/samples/layouts/virtual-scroll/horizontal/src/index.css
@@ -0,0 +1,23 @@
+/* shared styles are loaded from: */
+/* https://dl.infragistics.com/x/css/samples/shared.v8.css */
+
+.cards {
+ block-size: 200px;
+}
+
+/* Items are measured by their border box, so the spacing between cards is
+ padding on the item wrapper rather than a margin. */
+.cards__item {
+ box-sizing: border-box;
+ inline-size: 220px;
+ block-size: 100%;
+ padding: 8px;
+
+ & igc-card {
+ block-size: 100%;
+ }
+}
+
+.cards__item--wide {
+ inline-size: 320px;
+}
diff --git a/samples/layouts/virtual-scroll/horizontal/src/index.tsx b/samples/layouts/virtual-scroll/horizontal/src/index.tsx
new file mode 100644
index 0000000000..d11aa0d692
--- /dev/null
+++ b/samples/layouts/virtual-scroll/horizontal/src/index.tsx
@@ -0,0 +1,61 @@
+import React from "react";
+import ReactDOM from "react-dom/client";
+import {
+ IgrAvatar,
+ IgrCard,
+ IgrCardContent,
+ IgrCardHeader,
+ IgrChip,
+ IgrVirtualScroll,
+} from "igniteui-react";
+import type { VirtualScrollItemContext } from "igniteui-webcomponents";
+import "igniteui-webcomponents/themes/light/bootstrap.css";
+import { Employee, generateEmployees } from "./EmployeeData";
+import "./index.css";
+
+const employees: Employee[] = generateEmployees(10_000);
+
+export default function VirtualScrollHorizontal() {
+ // With a horizontal orientation the items are measured by their width.
+ const renderItem = (ctx: VirtualScrollItemContext) => (
+
+
+
+
+ {ctx.value.name}
+
+ {ctx.value.bio ? ctx.value.email : `#${ctx.value.id}`}
+
+
+
+ {ctx.value.department}
+
+
+
+ );
+
+ return (
+
+
+
+ );
+}
+
+// rendering above component to the React DOM
+const root = ReactDOM.createRoot(document.getElementById("root"));
+root.render();
diff --git a/samples/layouts/virtual-scroll/horizontal/tsconfig.json b/samples/layouts/virtual-scroll/horizontal/tsconfig.json
new file mode 100644
index 0000000000..e7db8a94f5
--- /dev/null
+++ b/samples/layouts/virtual-scroll/horizontal/tsconfig.json
@@ -0,0 +1,44 @@
+{
+ "compilerOptions": {
+ "resolveJsonModule": true,
+ "esModuleInterop": true,
+ "baseUrl": ".",
+ "outDir": "build/dist",
+ "module": "esnext",
+ "target": "es5",
+ "lib": [
+ "es6",
+ "dom"
+ ],
+ "sourceMap": true,
+ "allowJs": true,
+ "jsx": "react",
+ "moduleResolution": "node",
+ "rootDir": "src",
+ "forceConsistentCasingInFileNames": true,
+ "noImplicitReturns": true,
+ "noImplicitThis": true,
+ "noImplicitAny": true,
+ "noUnusedLocals": false,
+ "importHelpers": true,
+ "allowSyntheticDefaultImports": true,
+ "skipLibCheck": true,
+ "strict": false,
+ "isolatedModules": true,
+ "noEmit": true
+ },
+ "exclude": [
+ "node_modules",
+ "build",
+ "scripts",
+ "acceptance-tests",
+ "webpack",
+ "jest",
+ "src/setupTests.ts",
+ "**/odatajs-4.0.0.js",
+ "config-overrides.js"
+ ],
+ "include": [
+ "src"
+ ]
+}
diff --git a/samples/layouts/virtual-scroll/horizontal/vite.config.js b/samples/layouts/virtual-scroll/horizontal/vite.config.js
new file mode 100644
index 0000000000..1744dbc719
--- /dev/null
+++ b/samples/layouts/virtual-scroll/horizontal/vite.config.js
@@ -0,0 +1,12 @@
+import { defineConfig } from 'vite';
+import react from '@vitejs/plugin-react';
+
+export default defineConfig({
+ plugins: [react()],
+ build: {
+ outDir: 'build'
+ },
+ server: {
+ open: false
+ },
+});
\ No newline at end of file
diff --git a/samples/layouts/virtual-scroll/infinite-scroll/.devcontainer/devcontainer.json b/samples/layouts/virtual-scroll/infinite-scroll/.devcontainer/devcontainer.json
new file mode 100644
index 0000000000..e0b8e9c925
--- /dev/null
+++ b/samples/layouts/virtual-scroll/infinite-scroll/.devcontainer/devcontainer.json
@@ -0,0 +1,4 @@
+{
+ "name": "Node.js",
+ "image": "mcr.microsoft.com/devcontainers/javascript-node:22"
+}
\ No newline at end of file
diff --git a/samples/layouts/virtual-scroll/infinite-scroll/.eslintrc.js b/samples/layouts/virtual-scroll/infinite-scroll/.eslintrc.js
new file mode 100644
index 0000000000..b45160a9ee
--- /dev/null
+++ b/samples/layouts/virtual-scroll/infinite-scroll/.eslintrc.js
@@ -0,0 +1,75 @@
+// https://www.robertcooper.me/using-eslint-and-prettier-in-a-typescript-project
+module.exports = {
+ parser: "@typescript-eslint/parser", // Specifies the ESLint parser
+ parserOptions: {
+ ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features
+ sourceType: "module", // Allows for the use of imports
+ ecmaFeatures: {
+ jsx: true // Allows for the parsing of JSX
+ }
+ },
+ settings: {
+ react: {
+ version: "999.999.999" // Tells eslint-plugin-react to automatically detect the version of React to use
+ }
+ },
+ extends: [
+ "eslint:recommended",
+ "plugin:react/recommended", // Uses the recommended rules from @eslint-plugin-react
+ "plugin:@typescript-eslint/recommended" // Uses the recommended rules from @typescript-eslint/eslint-plugin
+ ],
+ rules: {
+ // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
+ "default-case": "off",
+ "no-undef": "off",
+ "no-unused-vars": "off",
+ "no-extend-native": "off",
+ "no-throw-literal": "off",
+ "no-useless-concat": "off",
+ "no-mixed-operators": "off",
+ "no-prototype-builtins": "off",
+ "prefer-const": "off",
+ "prefer-rest-params": "off",
+ "jsx-a11y/alt-text": "off",
+ "jsx-a11y/iframe-has-title": "off",
+ "@typescript-eslint/no-unused-vars": "off",
+ "@typescript-eslint/no-explicit-any": "off",
+ "@typescript-eslint/no-inferrable-types": "off",
+ "@typescript-eslint/no-useless-constructor": "off",
+ "@typescript-eslint/no-use-before-define": "off",
+ "@typescript-eslint/no-non-null-assertion": "off",
+ "@typescript-eslint/interface-name-prefix": "off",
+ "@typescript-eslint/prefer-namespace-keyword": "off",
+ "@typescript-eslint/explicit-function-return-type": "off",
+ "@typescript-eslint/explicit-module-boundary-types": "off"
+ },
+ "overrides": [
+ {
+ "files": ["*.ts", "*.tsx"],
+ "rules": {
+ "default-case": "off",
+ "no-undef": "off",
+ "no-unused-vars": "off",
+ "no-extend-native": "off",
+ "no-throw-literal": "off",
+ "no-useless-concat": "off",
+ "no-mixed-operators": "off",
+ "no-prototype-builtins": "off",
+ "prefer-const": "off",
+ "prefer-rest-params": "off",
+ "jsx-a11y/alt-text": "off",
+ "jsx-a11y/iframe-has-title": "off",
+ "@typescript-eslint/no-unused-vars": "off",
+ "@typescript-eslint/no-explicit-any": "off",
+ "@typescript-eslint/no-inferrable-types": "off",
+ "@typescript-eslint/no-useless-constructor": "off",
+ "@typescript-eslint/no-use-before-define": "off",
+ "@typescript-eslint/no-non-null-assertion": "off",
+ "@typescript-eslint/interface-name-prefix": "off",
+ "@typescript-eslint/prefer-namespace-keyword": "off",
+ "@typescript-eslint/explicit-function-return-type": "off",
+ "@typescript-eslint/explicit-module-boundary-types": "off"
+ }
+ }
+ ]
+ };
\ No newline at end of file
diff --git a/samples/layouts/virtual-scroll/infinite-scroll/README.md b/samples/layouts/virtual-scroll/infinite-scroll/README.md
new file mode 100644
index 0000000000..c93f36c851
--- /dev/null
+++ b/samples/layouts/virtual-scroll/infinite-scroll/README.md
@@ -0,0 +1,56 @@
+
+
+
+This folder contains implementation of React application with example of Infinite Scroll feature using [Virtual Scroll](https://www.infragistics.com/products/ignite-ui-react/react/components/general-getting-started.html) component.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Branches
+
+> **_NOTE:_** You should use [master](https://github.com/IgniteUI/igniteui-react-examples/tree/master) branch of this repository if you want to run samples on your computer. Use the [vnext](https://github.com/IgniteUI/igniteui-react-examples/tree/vnext) branch only when you want to contribute new samples to this repository.
+
+## Instructions
+
+Follow these instructions to run this example:
+
+
+```
+git clone https://github.com/IgniteUI/igniteui-react-examples.git
+git checkout master
+cd ./igniteui-react-examples
+cd ./samples/layouts/virtual-scroll/infinite-scroll
+```
+
+open above folder in VS Code or type:
+```
+code .
+```
+
+In terminal window, run:
+```
+npm install --legacy-peer-deps
+npm run-script start
+```
+
+Then open http://localhost:4200/ in your browser
+
+
+## Learn More
+
+To learn more about **Ignite UI for React** components, check out the [React documentation](https://www.infragistics.com/products/ignite-ui-react/react/components/general-getting-started.html).
diff --git a/samples/layouts/virtual-scroll/infinite-scroll/index.html b/samples/layouts/virtual-scroll/infinite-scroll/index.html
new file mode 100644
index 0000000000..e3713fddbc
--- /dev/null
+++ b/samples/layouts/virtual-scroll/infinite-scroll/index.html
@@ -0,0 +1,11 @@
+
+
+
+ VirtualScrollInfiniteScroll
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/layouts/virtual-scroll/infinite-scroll/package.json b/samples/layouts/virtual-scroll/infinite-scroll/package.json
new file mode 100644
index 0000000000..ae32619f11
--- /dev/null
+++ b/samples/layouts/virtual-scroll/infinite-scroll/package.json
@@ -0,0 +1,42 @@
+{
+ "name": "react-virtual-scroll-infinite-scroll",
+ "description": "This project provides example of Infinite Scroll feature using Infragistics React components",
+ "author": "Infragistics",
+ "homepage": ".",
+ "version": "1.4.0",
+ "private": true,
+ "scripts": {
+ "start": "vite --port 4200",
+ "build": "tsc && node --max-old-space-size=4096 node_modules/vite/bin/vite build",
+ "preview": "vite preview",
+ "test": "vitest"
+ },
+ "dependencies": {
+ "igniteui-dockmanager": "^1.17.0",
+ "igniteui-react": "19.9.0",
+ "igniteui-react-core": "19.6.0",
+ "igniteui-webcomponents": "^7.4.1",
+ "react": "^19.2.0",
+ "react-dom": "^19.2.0",
+ "tslib": "^2.4.0"
+ },
+ "devDependencies": {
+ "@types/jest": "^29.2.0",
+ "@types/node": "^24.7.1",
+ "@types/react": "^18.0.24",
+ "@types/react-dom": "^18.0.8",
+ "@vitejs/plugin-react": "^5.0.4",
+ "@vitest/browser": "^3.2.4",
+ "typescript": "^4.8.4",
+ "vite": "^7.1.9",
+ "vitest": "^3.2.4",
+ "vitest-canvas-mock": "^0.3.3",
+ "worker-loader": "^3.0.8"
+ },
+ "browserslist": [
+ ">0.2%",
+ "not dead",
+ "not ie <= 11",
+ "not op_mini all"
+ ]
+}
diff --git a/samples/layouts/virtual-scroll/infinite-scroll/sandbox.config.json b/samples/layouts/virtual-scroll/infinite-scroll/sandbox.config.json
new file mode 100644
index 0000000000..00acba0c10
--- /dev/null
+++ b/samples/layouts/virtual-scroll/infinite-scroll/sandbox.config.json
@@ -0,0 +1,5 @@
+{
+ "infiniteLoopProtection": false,
+ "hardReloadOnChange": false,
+ "view": "browser"
+}
\ No newline at end of file
diff --git a/samples/layouts/virtual-scroll/infinite-scroll/src/EmployeeData.ts b/samples/layouts/virtual-scroll/infinite-scroll/src/EmployeeData.ts
new file mode 100644
index 0000000000..cc0f0ea228
--- /dev/null
+++ b/samples/layouts/virtual-scroll/infinite-scroll/src/EmployeeData.ts
@@ -0,0 +1,46 @@
+export type DepartmentVariant = "primary" | "info" | "success" | "warning" | "danger";
+
+export interface Employee {
+ id: number;
+ name: string;
+ initials: string;
+ email: string;
+ department: string;
+ variant: DepartmentVariant;
+ bio: string | null;
+}
+
+const DEPARTMENTS = ["Engineering", "Design", "Marketing", "Sales", "HR", "Finance", "Legal", "Operations"];
+const VARIANTS: DepartmentVariant[] = ["primary", "info", "success", "warning", "danger"];
+
+const FIRST_NAMES = ["Alice", "Bob", "Carol", "David", "Eve", "Frank", "Grace", "Henry", "Iris", "Jack", "Karen", "Leo", "Mia", "Noah", "Olivia", "Paul"];
+
+const LAST_NAMES = ["Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis", "Wilson", "Moore", "Taylor", "Anderson", "Thomas", "Jackson"];
+
+const BIOS = [
+ "Leads a cross-functional team across three time zones and owns the quarterly delivery plan.",
+ "Specializes in scalable service architecture.",
+ "Passionate about accessible, pixel-perfect user interfaces and design systems that scale across products.",
+ "Drives product strategy and stakeholder alignment.",
+ "Champions data-driven decision making, runs the weekly metrics review and mentors new analysts."
+];
+
+export function createEmployee(index: number): Employee {
+ const first = FIRST_NAMES[index % FIRST_NAMES.length];
+ const last = LAST_NAMES[Math.floor(index / FIRST_NAMES.length) % LAST_NAMES.length];
+ const departmentIndex = index % DEPARTMENTS.length;
+
+ return {
+ id: index + 1,
+ name: `${first} ${last}`,
+ initials: `${first[0]}${last[0]}`,
+ email: `${first.toLowerCase()}.${last.toLowerCase()}${index + 1}@example.com`,
+ department: DEPARTMENTS[departmentIndex],
+ variant: VARIANTS[departmentIndex % VARIANTS.length],
+ bio: index % 3 === 0 ? BIOS[index % BIOS.length] : null
+ };
+}
+
+export function generateEmployees(count: number, startIndex = 0): Employee[] {
+ return Array.from({ length: count }, (_, i) => createEmployee(startIndex + i));
+}
diff --git a/samples/layouts/virtual-scroll/infinite-scroll/src/index.css b/samples/layouts/virtual-scroll/infinite-scroll/src/index.css
new file mode 100644
index 0000000000..962eb1b7d3
--- /dev/null
+++ b/samples/layouts/virtual-scroll/infinite-scroll/src/index.css
@@ -0,0 +1,16 @@
+/* shared styles are loaded from: */
+/* https://dl.infragistics.com/x/css/samples/shared.v8.css */
+
+.employees__viewport {
+ block-size: 440px;
+}
+
+/* Reserve the space for the progress bar so the list does not shift or overflow while loading. */
+.employees__status {
+ block-size: 4px;
+ overflow: hidden;
+}
+
+.employees__status igc-linear-progress {
+ --ig-linear-bar-track-height: 4px;
+}
diff --git a/samples/layouts/virtual-scroll/infinite-scroll/src/index.tsx b/samples/layouts/virtual-scroll/infinite-scroll/src/index.tsx
new file mode 100644
index 0000000000..7dc72b4737
--- /dev/null
+++ b/samples/layouts/virtual-scroll/infinite-scroll/src/index.tsx
@@ -0,0 +1,97 @@
+import React, { useRef, useState } from "react";
+import ReactDOM from "react-dom/client";
+import {
+ IgrAvatar,
+ IgrChip,
+ IgrLinearProgress,
+ IgrList,
+ IgrListHeader,
+ IgrListItem,
+ IgrVirtualScroll,
+} from "igniteui-react";
+import type {
+ VirtualScrollDataRequest,
+ VirtualScrollItemContext,
+} from "igniteui-webcomponents";
+import "igniteui-webcomponents/themes/light/bootstrap.css";
+import { Employee, generateEmployees } from "./EmployeeData";
+import "./index.css";
+
+/** The size of the whole remote collection. */
+const TOTAL_COUNT = 1_000;
+const PAGE_SIZE = 50;
+
+export default function VirtualScrollInfiniteScroll() {
+ // Load the first page up front: an empty list has no rendered window to run
+ // out of, so it does not request data.
+ const [employees, setEmployees] = useState(() =>
+ generateEmployees(PAGE_SIZE)
+ );
+ const [loading, setLoading] = useState(false);
+ const pending = useRef(false);
+
+ const renderItem = (ctx: VirtualScrollItemContext) => (
+
+
+
+ #{ctx.value.id} {ctx.value.name}
+
+ {ctx.value.email}
+
+ {ctx.value.department}
+
+
+ );
+
+ /**
+ * `igcDataRequest` is emitted when the rendered window nears the end of `data`.
+ * Only one request is emitted at a time: the next one follows the next `data` change.
+ */
+ const loadMore = (request: VirtualScrollDataRequest) => {
+ if (pending.current || request.startIndex >= TOTAL_COUNT) {
+ return;
+ }
+
+ pending.current = true;
+ setLoading(true);
+ const count = Math.min(
+ Math.max(request.count, PAGE_SIZE),
+ TOTAL_COUNT - request.startIndex
+ );
+
+ // Simulates a request to a remote service.
+ setTimeout(() => {
+ // Assign a new array: `data` is compared by reference.
+ setEmployees((current) => [
+ ...current,
+ ...generateEmployees(count, request.startIndex),
+ ]);
+ pending.current = false;
+ setLoading(false);
+ }, 800);
+ };
+
+ return (
+
+
+
+ Loaded {employees.length} of {TOTAL_COUNT} employees
+
+ loadMore(event.detail)}
+ />
+
+ {loading && }
+
+
+
+ );
+}
+
+// rendering above component to the React DOM
+const root = ReactDOM.createRoot(document.getElementById("root"));
+root.render();
diff --git a/samples/layouts/virtual-scroll/infinite-scroll/tsconfig.json b/samples/layouts/virtual-scroll/infinite-scroll/tsconfig.json
new file mode 100644
index 0000000000..e7db8a94f5
--- /dev/null
+++ b/samples/layouts/virtual-scroll/infinite-scroll/tsconfig.json
@@ -0,0 +1,44 @@
+{
+ "compilerOptions": {
+ "resolveJsonModule": true,
+ "esModuleInterop": true,
+ "baseUrl": ".",
+ "outDir": "build/dist",
+ "module": "esnext",
+ "target": "es5",
+ "lib": [
+ "es6",
+ "dom"
+ ],
+ "sourceMap": true,
+ "allowJs": true,
+ "jsx": "react",
+ "moduleResolution": "node",
+ "rootDir": "src",
+ "forceConsistentCasingInFileNames": true,
+ "noImplicitReturns": true,
+ "noImplicitThis": true,
+ "noImplicitAny": true,
+ "noUnusedLocals": false,
+ "importHelpers": true,
+ "allowSyntheticDefaultImports": true,
+ "skipLibCheck": true,
+ "strict": false,
+ "isolatedModules": true,
+ "noEmit": true
+ },
+ "exclude": [
+ "node_modules",
+ "build",
+ "scripts",
+ "acceptance-tests",
+ "webpack",
+ "jest",
+ "src/setupTests.ts",
+ "**/odatajs-4.0.0.js",
+ "config-overrides.js"
+ ],
+ "include": [
+ "src"
+ ]
+}
diff --git a/samples/layouts/virtual-scroll/infinite-scroll/vite.config.js b/samples/layouts/virtual-scroll/infinite-scroll/vite.config.js
new file mode 100644
index 0000000000..1744dbc719
--- /dev/null
+++ b/samples/layouts/virtual-scroll/infinite-scroll/vite.config.js
@@ -0,0 +1,12 @@
+import { defineConfig } from 'vite';
+import react from '@vitejs/plugin-react';
+
+export default defineConfig({
+ plugins: [react()],
+ build: {
+ outDir: 'build'
+ },
+ server: {
+ open: false
+ },
+});
\ No newline at end of file
diff --git a/samples/layouts/virtual-scroll/overview/.devcontainer/devcontainer.json b/samples/layouts/virtual-scroll/overview/.devcontainer/devcontainer.json
new file mode 100644
index 0000000000..e0b8e9c925
--- /dev/null
+++ b/samples/layouts/virtual-scroll/overview/.devcontainer/devcontainer.json
@@ -0,0 +1,4 @@
+{
+ "name": "Node.js",
+ "image": "mcr.microsoft.com/devcontainers/javascript-node:22"
+}
\ No newline at end of file
diff --git a/samples/layouts/virtual-scroll/overview/.eslintrc.js b/samples/layouts/virtual-scroll/overview/.eslintrc.js
new file mode 100644
index 0000000000..b45160a9ee
--- /dev/null
+++ b/samples/layouts/virtual-scroll/overview/.eslintrc.js
@@ -0,0 +1,75 @@
+// https://www.robertcooper.me/using-eslint-and-prettier-in-a-typescript-project
+module.exports = {
+ parser: "@typescript-eslint/parser", // Specifies the ESLint parser
+ parserOptions: {
+ ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features
+ sourceType: "module", // Allows for the use of imports
+ ecmaFeatures: {
+ jsx: true // Allows for the parsing of JSX
+ }
+ },
+ settings: {
+ react: {
+ version: "999.999.999" // Tells eslint-plugin-react to automatically detect the version of React to use
+ }
+ },
+ extends: [
+ "eslint:recommended",
+ "plugin:react/recommended", // Uses the recommended rules from @eslint-plugin-react
+ "plugin:@typescript-eslint/recommended" // Uses the recommended rules from @typescript-eslint/eslint-plugin
+ ],
+ rules: {
+ // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
+ "default-case": "off",
+ "no-undef": "off",
+ "no-unused-vars": "off",
+ "no-extend-native": "off",
+ "no-throw-literal": "off",
+ "no-useless-concat": "off",
+ "no-mixed-operators": "off",
+ "no-prototype-builtins": "off",
+ "prefer-const": "off",
+ "prefer-rest-params": "off",
+ "jsx-a11y/alt-text": "off",
+ "jsx-a11y/iframe-has-title": "off",
+ "@typescript-eslint/no-unused-vars": "off",
+ "@typescript-eslint/no-explicit-any": "off",
+ "@typescript-eslint/no-inferrable-types": "off",
+ "@typescript-eslint/no-useless-constructor": "off",
+ "@typescript-eslint/no-use-before-define": "off",
+ "@typescript-eslint/no-non-null-assertion": "off",
+ "@typescript-eslint/interface-name-prefix": "off",
+ "@typescript-eslint/prefer-namespace-keyword": "off",
+ "@typescript-eslint/explicit-function-return-type": "off",
+ "@typescript-eslint/explicit-module-boundary-types": "off"
+ },
+ "overrides": [
+ {
+ "files": ["*.ts", "*.tsx"],
+ "rules": {
+ "default-case": "off",
+ "no-undef": "off",
+ "no-unused-vars": "off",
+ "no-extend-native": "off",
+ "no-throw-literal": "off",
+ "no-useless-concat": "off",
+ "no-mixed-operators": "off",
+ "no-prototype-builtins": "off",
+ "prefer-const": "off",
+ "prefer-rest-params": "off",
+ "jsx-a11y/alt-text": "off",
+ "jsx-a11y/iframe-has-title": "off",
+ "@typescript-eslint/no-unused-vars": "off",
+ "@typescript-eslint/no-explicit-any": "off",
+ "@typescript-eslint/no-inferrable-types": "off",
+ "@typescript-eslint/no-useless-constructor": "off",
+ "@typescript-eslint/no-use-before-define": "off",
+ "@typescript-eslint/no-non-null-assertion": "off",
+ "@typescript-eslint/interface-name-prefix": "off",
+ "@typescript-eslint/prefer-namespace-keyword": "off",
+ "@typescript-eslint/explicit-function-return-type": "off",
+ "@typescript-eslint/explicit-module-boundary-types": "off"
+ }
+ }
+ ]
+ };
\ No newline at end of file
diff --git a/samples/layouts/virtual-scroll/overview/README.md b/samples/layouts/virtual-scroll/overview/README.md
new file mode 100644
index 0000000000..79ee1611e5
--- /dev/null
+++ b/samples/layouts/virtual-scroll/overview/README.md
@@ -0,0 +1,56 @@
+
+
+
+This folder contains implementation of React application with example of Overview feature using [Virtual Scroll](https://www.infragistics.com/products/ignite-ui-react/react/components/general-getting-started.html) component.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Branches
+
+> **_NOTE:_** You should use [master](https://github.com/IgniteUI/igniteui-react-examples/tree/master) branch of this repository if you want to run samples on your computer. Use the [vnext](https://github.com/IgniteUI/igniteui-react-examples/tree/vnext) branch only when you want to contribute new samples to this repository.
+
+## Instructions
+
+Follow these instructions to run this example:
+
+
+```
+git clone https://github.com/IgniteUI/igniteui-react-examples.git
+git checkout master
+cd ./igniteui-react-examples
+cd ./samples/layouts/virtual-scroll/overview
+```
+
+open above folder in VS Code or type:
+```
+code .
+```
+
+In terminal window, run:
+```
+npm install --legacy-peer-deps
+npm run-script start
+```
+
+Then open http://localhost:4200/ in your browser
+
+
+## Learn More
+
+To learn more about **Ignite UI for React** components, check out the [React documentation](https://www.infragistics.com/products/ignite-ui-react/react/components/general-getting-started.html).
diff --git a/samples/layouts/virtual-scroll/overview/index.html b/samples/layouts/virtual-scroll/overview/index.html
new file mode 100644
index 0000000000..38018e6cea
--- /dev/null
+++ b/samples/layouts/virtual-scroll/overview/index.html
@@ -0,0 +1,11 @@
+
+
+
+ VirtualScrollOverview
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/layouts/virtual-scroll/overview/package.json b/samples/layouts/virtual-scroll/overview/package.json
new file mode 100644
index 0000000000..0820a218ba
--- /dev/null
+++ b/samples/layouts/virtual-scroll/overview/package.json
@@ -0,0 +1,42 @@
+{
+ "name": "react-virtual-scroll-overview",
+ "description": "This project provides example of Overview feature using Infragistics React components",
+ "author": "Infragistics",
+ "homepage": ".",
+ "version": "1.4.0",
+ "private": true,
+ "scripts": {
+ "start": "vite --port 4200",
+ "build": "tsc && node --max-old-space-size=4096 node_modules/vite/bin/vite build",
+ "preview": "vite preview",
+ "test": "vitest"
+ },
+ "dependencies": {
+ "igniteui-dockmanager": "^1.17.0",
+ "igniteui-react": "19.9.0",
+ "igniteui-react-core": "19.6.0",
+ "igniteui-webcomponents": "^7.4.1",
+ "react": "^19.2.0",
+ "react-dom": "^19.2.0",
+ "tslib": "^2.4.0"
+ },
+ "devDependencies": {
+ "@types/jest": "^29.2.0",
+ "@types/node": "^24.7.1",
+ "@types/react": "^18.0.24",
+ "@types/react-dom": "^18.0.8",
+ "@vitejs/plugin-react": "^5.0.4",
+ "@vitest/browser": "^3.2.4",
+ "typescript": "^4.8.4",
+ "vite": "^7.1.9",
+ "vitest": "^3.2.4",
+ "vitest-canvas-mock": "^0.3.3",
+ "worker-loader": "^3.0.8"
+ },
+ "browserslist": [
+ ">0.2%",
+ "not dead",
+ "not ie <= 11",
+ "not op_mini all"
+ ]
+}
diff --git a/samples/layouts/virtual-scroll/overview/sandbox.config.json b/samples/layouts/virtual-scroll/overview/sandbox.config.json
new file mode 100644
index 0000000000..00acba0c10
--- /dev/null
+++ b/samples/layouts/virtual-scroll/overview/sandbox.config.json
@@ -0,0 +1,5 @@
+{
+ "infiniteLoopProtection": false,
+ "hardReloadOnChange": false,
+ "view": "browser"
+}
\ No newline at end of file
diff --git a/samples/layouts/virtual-scroll/overview/src/EmployeeData.ts b/samples/layouts/virtual-scroll/overview/src/EmployeeData.ts
new file mode 100644
index 0000000000..cc0f0ea228
--- /dev/null
+++ b/samples/layouts/virtual-scroll/overview/src/EmployeeData.ts
@@ -0,0 +1,46 @@
+export type DepartmentVariant = "primary" | "info" | "success" | "warning" | "danger";
+
+export interface Employee {
+ id: number;
+ name: string;
+ initials: string;
+ email: string;
+ department: string;
+ variant: DepartmentVariant;
+ bio: string | null;
+}
+
+const DEPARTMENTS = ["Engineering", "Design", "Marketing", "Sales", "HR", "Finance", "Legal", "Operations"];
+const VARIANTS: DepartmentVariant[] = ["primary", "info", "success", "warning", "danger"];
+
+const FIRST_NAMES = ["Alice", "Bob", "Carol", "David", "Eve", "Frank", "Grace", "Henry", "Iris", "Jack", "Karen", "Leo", "Mia", "Noah", "Olivia", "Paul"];
+
+const LAST_NAMES = ["Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis", "Wilson", "Moore", "Taylor", "Anderson", "Thomas", "Jackson"];
+
+const BIOS = [
+ "Leads a cross-functional team across three time zones and owns the quarterly delivery plan.",
+ "Specializes in scalable service architecture.",
+ "Passionate about accessible, pixel-perfect user interfaces and design systems that scale across products.",
+ "Drives product strategy and stakeholder alignment.",
+ "Champions data-driven decision making, runs the weekly metrics review and mentors new analysts."
+];
+
+export function createEmployee(index: number): Employee {
+ const first = FIRST_NAMES[index % FIRST_NAMES.length];
+ const last = LAST_NAMES[Math.floor(index / FIRST_NAMES.length) % LAST_NAMES.length];
+ const departmentIndex = index % DEPARTMENTS.length;
+
+ return {
+ id: index + 1,
+ name: `${first} ${last}`,
+ initials: `${first[0]}${last[0]}`,
+ email: `${first.toLowerCase()}.${last.toLowerCase()}${index + 1}@example.com`,
+ department: DEPARTMENTS[departmentIndex],
+ variant: VARIANTS[departmentIndex % VARIANTS.length],
+ bio: index % 3 === 0 ? BIOS[index % BIOS.length] : null
+ };
+}
+
+export function generateEmployees(count: number, startIndex = 0): Employee[] {
+ return Array.from({ length: count }, (_, i) => createEmployee(startIndex + i));
+}
diff --git a/samples/layouts/virtual-scroll/overview/src/index.css b/samples/layouts/virtual-scroll/overview/src/index.css
new file mode 100644
index 0000000000..cdf32b3741
--- /dev/null
+++ b/samples/layouts/virtual-scroll/overview/src/index.css
@@ -0,0 +1,6 @@
+/* shared styles are loaded from: */
+/* https://dl.infragistics.com/x/css/samples/shared.v8.css */
+
+.employees__viewport {
+ block-size: 480px;
+}
diff --git a/samples/layouts/virtual-scroll/overview/src/index.tsx b/samples/layouts/virtual-scroll/overview/src/index.tsx
new file mode 100644
index 0000000000..a403fa6822
--- /dev/null
+++ b/samples/layouts/virtual-scroll/overview/src/index.tsx
@@ -0,0 +1,48 @@
+import React from "react";
+import ReactDOM from "react-dom/client";
+import {
+ IgrAvatar,
+ IgrChip,
+ IgrList,
+ IgrListHeader,
+ IgrListItem,
+ IgrVirtualScroll,
+} from "igniteui-react";
+import type { VirtualScrollItemContext } from "igniteui-webcomponents";
+import "igniteui-webcomponents/themes/light/bootstrap.css";
+import { Employee, generateEmployees } from "./EmployeeData";
+import "./index.css";
+
+const employees: Employee[] = generateEmployees(100_000);
+
+export default function VirtualScrollOverview() {
+ // Only the items in the viewport, plus a small buffer, are rendered.
+ const renderItem = (ctx: VirtualScrollItemContext) => (
+
+
+ {ctx.value.name}
+ {ctx.value.email}
+
+ {ctx.value.department}
+
+
+ );
+
+ return (
+
+
+ Employees ({employees.length})
+
+
+
+ );
+}
+
+// rendering above component to the React DOM
+const root = ReactDOM.createRoot(document.getElementById("root"));
+root.render();
diff --git a/samples/layouts/virtual-scroll/overview/tsconfig.json b/samples/layouts/virtual-scroll/overview/tsconfig.json
new file mode 100644
index 0000000000..e7db8a94f5
--- /dev/null
+++ b/samples/layouts/virtual-scroll/overview/tsconfig.json
@@ -0,0 +1,44 @@
+{
+ "compilerOptions": {
+ "resolveJsonModule": true,
+ "esModuleInterop": true,
+ "baseUrl": ".",
+ "outDir": "build/dist",
+ "module": "esnext",
+ "target": "es5",
+ "lib": [
+ "es6",
+ "dom"
+ ],
+ "sourceMap": true,
+ "allowJs": true,
+ "jsx": "react",
+ "moduleResolution": "node",
+ "rootDir": "src",
+ "forceConsistentCasingInFileNames": true,
+ "noImplicitReturns": true,
+ "noImplicitThis": true,
+ "noImplicitAny": true,
+ "noUnusedLocals": false,
+ "importHelpers": true,
+ "allowSyntheticDefaultImports": true,
+ "skipLibCheck": true,
+ "strict": false,
+ "isolatedModules": true,
+ "noEmit": true
+ },
+ "exclude": [
+ "node_modules",
+ "build",
+ "scripts",
+ "acceptance-tests",
+ "webpack",
+ "jest",
+ "src/setupTests.ts",
+ "**/odatajs-4.0.0.js",
+ "config-overrides.js"
+ ],
+ "include": [
+ "src"
+ ]
+}
diff --git a/samples/layouts/virtual-scroll/overview/vite.config.js b/samples/layouts/virtual-scroll/overview/vite.config.js
new file mode 100644
index 0000000000..1744dbc719
--- /dev/null
+++ b/samples/layouts/virtual-scroll/overview/vite.config.js
@@ -0,0 +1,12 @@
+import { defineConfig } from 'vite';
+import react from '@vitejs/plugin-react';
+
+export default defineConfig({
+ plugins: [react()],
+ build: {
+ outDir: 'build'
+ },
+ server: {
+ open: false
+ },
+});
\ No newline at end of file
diff --git a/samples/layouts/virtual-scroll/scroll-to-index/.devcontainer/devcontainer.json b/samples/layouts/virtual-scroll/scroll-to-index/.devcontainer/devcontainer.json
new file mode 100644
index 0000000000..e0b8e9c925
--- /dev/null
+++ b/samples/layouts/virtual-scroll/scroll-to-index/.devcontainer/devcontainer.json
@@ -0,0 +1,4 @@
+{
+ "name": "Node.js",
+ "image": "mcr.microsoft.com/devcontainers/javascript-node:22"
+}
\ No newline at end of file
diff --git a/samples/layouts/virtual-scroll/scroll-to-index/.eslintrc.js b/samples/layouts/virtual-scroll/scroll-to-index/.eslintrc.js
new file mode 100644
index 0000000000..b45160a9ee
--- /dev/null
+++ b/samples/layouts/virtual-scroll/scroll-to-index/.eslintrc.js
@@ -0,0 +1,75 @@
+// https://www.robertcooper.me/using-eslint-and-prettier-in-a-typescript-project
+module.exports = {
+ parser: "@typescript-eslint/parser", // Specifies the ESLint parser
+ parserOptions: {
+ ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features
+ sourceType: "module", // Allows for the use of imports
+ ecmaFeatures: {
+ jsx: true // Allows for the parsing of JSX
+ }
+ },
+ settings: {
+ react: {
+ version: "999.999.999" // Tells eslint-plugin-react to automatically detect the version of React to use
+ }
+ },
+ extends: [
+ "eslint:recommended",
+ "plugin:react/recommended", // Uses the recommended rules from @eslint-plugin-react
+ "plugin:@typescript-eslint/recommended" // Uses the recommended rules from @typescript-eslint/eslint-plugin
+ ],
+ rules: {
+ // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
+ "default-case": "off",
+ "no-undef": "off",
+ "no-unused-vars": "off",
+ "no-extend-native": "off",
+ "no-throw-literal": "off",
+ "no-useless-concat": "off",
+ "no-mixed-operators": "off",
+ "no-prototype-builtins": "off",
+ "prefer-const": "off",
+ "prefer-rest-params": "off",
+ "jsx-a11y/alt-text": "off",
+ "jsx-a11y/iframe-has-title": "off",
+ "@typescript-eslint/no-unused-vars": "off",
+ "@typescript-eslint/no-explicit-any": "off",
+ "@typescript-eslint/no-inferrable-types": "off",
+ "@typescript-eslint/no-useless-constructor": "off",
+ "@typescript-eslint/no-use-before-define": "off",
+ "@typescript-eslint/no-non-null-assertion": "off",
+ "@typescript-eslint/interface-name-prefix": "off",
+ "@typescript-eslint/prefer-namespace-keyword": "off",
+ "@typescript-eslint/explicit-function-return-type": "off",
+ "@typescript-eslint/explicit-module-boundary-types": "off"
+ },
+ "overrides": [
+ {
+ "files": ["*.ts", "*.tsx"],
+ "rules": {
+ "default-case": "off",
+ "no-undef": "off",
+ "no-unused-vars": "off",
+ "no-extend-native": "off",
+ "no-throw-literal": "off",
+ "no-useless-concat": "off",
+ "no-mixed-operators": "off",
+ "no-prototype-builtins": "off",
+ "prefer-const": "off",
+ "prefer-rest-params": "off",
+ "jsx-a11y/alt-text": "off",
+ "jsx-a11y/iframe-has-title": "off",
+ "@typescript-eslint/no-unused-vars": "off",
+ "@typescript-eslint/no-explicit-any": "off",
+ "@typescript-eslint/no-inferrable-types": "off",
+ "@typescript-eslint/no-useless-constructor": "off",
+ "@typescript-eslint/no-use-before-define": "off",
+ "@typescript-eslint/no-non-null-assertion": "off",
+ "@typescript-eslint/interface-name-prefix": "off",
+ "@typescript-eslint/prefer-namespace-keyword": "off",
+ "@typescript-eslint/explicit-function-return-type": "off",
+ "@typescript-eslint/explicit-module-boundary-types": "off"
+ }
+ }
+ ]
+ };
\ No newline at end of file
diff --git a/samples/layouts/virtual-scroll/scroll-to-index/README.md b/samples/layouts/virtual-scroll/scroll-to-index/README.md
new file mode 100644
index 0000000000..9e77cc9d8a
--- /dev/null
+++ b/samples/layouts/virtual-scroll/scroll-to-index/README.md
@@ -0,0 +1,56 @@
+
+
+
+This folder contains implementation of React application with example of Scroll To Index feature using [Virtual Scroll](https://www.infragistics.com/products/ignite-ui-react/react/components/general-getting-started.html) component.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Branches
+
+> **_NOTE:_** You should use [master](https://github.com/IgniteUI/igniteui-react-examples/tree/master) branch of this repository if you want to run samples on your computer. Use the [vnext](https://github.com/IgniteUI/igniteui-react-examples/tree/vnext) branch only when you want to contribute new samples to this repository.
+
+## Instructions
+
+Follow these instructions to run this example:
+
+
+```
+git clone https://github.com/IgniteUI/igniteui-react-examples.git
+git checkout master
+cd ./igniteui-react-examples
+cd ./samples/layouts/virtual-scroll/scroll-to-index
+```
+
+open above folder in VS Code or type:
+```
+code .
+```
+
+In terminal window, run:
+```
+npm install --legacy-peer-deps
+npm run-script start
+```
+
+Then open http://localhost:4200/ in your browser
+
+
+## Learn More
+
+To learn more about **Ignite UI for React** components, check out the [React documentation](https://www.infragistics.com/products/ignite-ui-react/react/components/general-getting-started.html).
diff --git a/samples/layouts/virtual-scroll/scroll-to-index/index.html b/samples/layouts/virtual-scroll/scroll-to-index/index.html
new file mode 100644
index 0000000000..3d857c41b0
--- /dev/null
+++ b/samples/layouts/virtual-scroll/scroll-to-index/index.html
@@ -0,0 +1,11 @@
+
+
+
+ VirtualScrollScrollToIndex
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/layouts/virtual-scroll/scroll-to-index/package.json b/samples/layouts/virtual-scroll/scroll-to-index/package.json
new file mode 100644
index 0000000000..ab3ec7d11a
--- /dev/null
+++ b/samples/layouts/virtual-scroll/scroll-to-index/package.json
@@ -0,0 +1,42 @@
+{
+ "name": "react-virtual-scroll-scroll-to-index",
+ "description": "This project provides example of Scroll To Index feature using Infragistics React components",
+ "author": "Infragistics",
+ "homepage": ".",
+ "version": "1.4.0",
+ "private": true,
+ "scripts": {
+ "start": "vite --port 4200",
+ "build": "tsc && node --max-old-space-size=4096 node_modules/vite/bin/vite build",
+ "preview": "vite preview",
+ "test": "vitest"
+ },
+ "dependencies": {
+ "igniteui-dockmanager": "^1.17.0",
+ "igniteui-react": "19.9.0",
+ "igniteui-react-core": "19.6.0",
+ "igniteui-webcomponents": "^7.4.1",
+ "react": "^19.2.0",
+ "react-dom": "^19.2.0",
+ "tslib": "^2.4.0"
+ },
+ "devDependencies": {
+ "@types/jest": "^29.2.0",
+ "@types/node": "^24.7.1",
+ "@types/react": "^18.0.24",
+ "@types/react-dom": "^18.0.8",
+ "@vitejs/plugin-react": "^5.0.4",
+ "@vitest/browser": "^3.2.4",
+ "typescript": "^4.8.4",
+ "vite": "^7.1.9",
+ "vitest": "^3.2.4",
+ "vitest-canvas-mock": "^0.3.3",
+ "worker-loader": "^3.0.8"
+ },
+ "browserslist": [
+ ">0.2%",
+ "not dead",
+ "not ie <= 11",
+ "not op_mini all"
+ ]
+}
diff --git a/samples/layouts/virtual-scroll/scroll-to-index/sandbox.config.json b/samples/layouts/virtual-scroll/scroll-to-index/sandbox.config.json
new file mode 100644
index 0000000000..00acba0c10
--- /dev/null
+++ b/samples/layouts/virtual-scroll/scroll-to-index/sandbox.config.json
@@ -0,0 +1,5 @@
+{
+ "infiniteLoopProtection": false,
+ "hardReloadOnChange": false,
+ "view": "browser"
+}
\ No newline at end of file
diff --git a/samples/layouts/virtual-scroll/scroll-to-index/src/EmployeeData.ts b/samples/layouts/virtual-scroll/scroll-to-index/src/EmployeeData.ts
new file mode 100644
index 0000000000..cc0f0ea228
--- /dev/null
+++ b/samples/layouts/virtual-scroll/scroll-to-index/src/EmployeeData.ts
@@ -0,0 +1,46 @@
+export type DepartmentVariant = "primary" | "info" | "success" | "warning" | "danger";
+
+export interface Employee {
+ id: number;
+ name: string;
+ initials: string;
+ email: string;
+ department: string;
+ variant: DepartmentVariant;
+ bio: string | null;
+}
+
+const DEPARTMENTS = ["Engineering", "Design", "Marketing", "Sales", "HR", "Finance", "Legal", "Operations"];
+const VARIANTS: DepartmentVariant[] = ["primary", "info", "success", "warning", "danger"];
+
+const FIRST_NAMES = ["Alice", "Bob", "Carol", "David", "Eve", "Frank", "Grace", "Henry", "Iris", "Jack", "Karen", "Leo", "Mia", "Noah", "Olivia", "Paul"];
+
+const LAST_NAMES = ["Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis", "Wilson", "Moore", "Taylor", "Anderson", "Thomas", "Jackson"];
+
+const BIOS = [
+ "Leads a cross-functional team across three time zones and owns the quarterly delivery plan.",
+ "Specializes in scalable service architecture.",
+ "Passionate about accessible, pixel-perfect user interfaces and design systems that scale across products.",
+ "Drives product strategy and stakeholder alignment.",
+ "Champions data-driven decision making, runs the weekly metrics review and mentors new analysts."
+];
+
+export function createEmployee(index: number): Employee {
+ const first = FIRST_NAMES[index % FIRST_NAMES.length];
+ const last = LAST_NAMES[Math.floor(index / FIRST_NAMES.length) % LAST_NAMES.length];
+ const departmentIndex = index % DEPARTMENTS.length;
+
+ return {
+ id: index + 1,
+ name: `${first} ${last}`,
+ initials: `${first[0]}${last[0]}`,
+ email: `${first.toLowerCase()}.${last.toLowerCase()}${index + 1}@example.com`,
+ department: DEPARTMENTS[departmentIndex],
+ variant: VARIANTS[departmentIndex % VARIANTS.length],
+ bio: index % 3 === 0 ? BIOS[index % BIOS.length] : null
+ };
+}
+
+export function generateEmployees(count: number, startIndex = 0): Employee[] {
+ return Array.from({ length: count }, (_, i) => createEmployee(startIndex + i));
+}
diff --git a/samples/layouts/virtual-scroll/scroll-to-index/src/index.css b/samples/layouts/virtual-scroll/scroll-to-index/src/index.css
new file mode 100644
index 0000000000..857d878e48
--- /dev/null
+++ b/samples/layouts/virtual-scroll/scroll-to-index/src/index.css
@@ -0,0 +1,38 @@
+/* shared styles are loaded from: */
+/* https://dl.infragistics.com/x/css/samples/shared.v8.css */
+
+.employees__viewport {
+ block-size: 400px;
+}
+
+.toolbar {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 16px;
+ margin-block-end: 16px;
+}
+
+.toolbar__index {
+ inline-size: 140px;
+}
+
+.toolbar__actions {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+}
+
+.employees__item--highlighted {
+ animation: highlight 1.5s ease-out;
+}
+
+@keyframes highlight {
+ from {
+ box-shadow: inset 0 0 0 3px var(--ig-warn-500);
+ }
+
+ to {
+ box-shadow: inset 0 0 0 3px transparent;
+ }
+}
diff --git a/samples/layouts/virtual-scroll/scroll-to-index/src/index.tsx b/samples/layouts/virtual-scroll/scroll-to-index/src/index.tsx
new file mode 100644
index 0000000000..6a195a6a10
--- /dev/null
+++ b/samples/layouts/virtual-scroll/scroll-to-index/src/index.tsx
@@ -0,0 +1,134 @@
+import React, { useRef, useState } from "react";
+import ReactDOM from "react-dom/client";
+import {
+ IgrAvatar,
+ IgrButton,
+ IgrChip,
+ IgrInput,
+ IgrList,
+ IgrListHeader,
+ IgrListItem,
+ IgrRadio,
+ IgrRadioGroup,
+ IgrVirtualScroll,
+} from "igniteui-react";
+import type { VirtualScrollItemContext } from "igniteui-webcomponents";
+import "igniteui-webcomponents/themes/light/bootstrap.css";
+import { Employee, generateEmployees } from "./EmployeeData";
+import "./index.css";
+
+const employees: Employee[] = generateEmployees(100_000);
+const LAST_INDEX = employees.length - 1;
+
+export default function VirtualScrollScrollToIndex() {
+ const virtualScroll = useRef(null);
+ const [targetIndex, setTargetIndex] = useState("50000");
+ const [alignment, setAlignment] = useState("start");
+
+ const renderItem = (ctx: VirtualScrollItemContext) => (
+
+
+
+ #{ctx.index} {ctx.value.name}
+
+ {ctx.value.email}
+
+ {ctx.value.department}
+
+
+ );
+
+ const goTo = async (index: number) => {
+ const target = Math.min(Math.max(Math.trunc(index) || 0, 0), LAST_INDEX);
+ setTargetIndex(String(target));
+
+ // Items that have not been rendered only have an estimated size, so the
+ // first jump lands near the target. The promise resolves once the
+ // component has measured the landing area and corrected the offset.
+ await virtualScroll.current?.scrollToIndex(target, { block: alignment });
+ highlight(target);
+ };
+
+ const highlight = (index: number) => {
+ const item = virtualScroll.current?.querySelector(
+ `[data-vs-index="${index}"]`
+ );
+ if (!item) {
+ return;
+ }
+
+ // Restart the animation when the same item is highlighted again.
+ item.classList.remove("employees__item--highlighted");
+ void item.offsetWidth;
+ item.classList.add("employees__item--highlighted");
+ };
+
+ return (
+
+
+
setTargetIndex(event.detail)}
+ />
+
+ setAlignment((event.target as IgrRadio).value as ScrollLogicalPosition)
+ }
+ >
+
+ start
+
+
+ center
+
+
+ end
+
+
+ nearest
+
+
+
+ goTo(Number(targetIndex))}>
+ Go
+
+ goTo(0)}>
+ First
+
+ goTo(LAST_INDEX / 2)}>
+ Middle
+
+ goTo(LAST_INDEX)}>
+ Last
+
+ goTo(Math.random() * LAST_INDEX)}
+ >
+ Random
+
+
+
+
+ Employees ({employees.length})
+
+
+
+ );
+}
+
+// rendering above component to the React DOM
+const root = ReactDOM.createRoot(document.getElementById("root"));
+root.render();
diff --git a/samples/layouts/virtual-scroll/scroll-to-index/tsconfig.json b/samples/layouts/virtual-scroll/scroll-to-index/tsconfig.json
new file mode 100644
index 0000000000..e7db8a94f5
--- /dev/null
+++ b/samples/layouts/virtual-scroll/scroll-to-index/tsconfig.json
@@ -0,0 +1,44 @@
+{
+ "compilerOptions": {
+ "resolveJsonModule": true,
+ "esModuleInterop": true,
+ "baseUrl": ".",
+ "outDir": "build/dist",
+ "module": "esnext",
+ "target": "es5",
+ "lib": [
+ "es6",
+ "dom"
+ ],
+ "sourceMap": true,
+ "allowJs": true,
+ "jsx": "react",
+ "moduleResolution": "node",
+ "rootDir": "src",
+ "forceConsistentCasingInFileNames": true,
+ "noImplicitReturns": true,
+ "noImplicitThis": true,
+ "noImplicitAny": true,
+ "noUnusedLocals": false,
+ "importHelpers": true,
+ "allowSyntheticDefaultImports": true,
+ "skipLibCheck": true,
+ "strict": false,
+ "isolatedModules": true,
+ "noEmit": true
+ },
+ "exclude": [
+ "node_modules",
+ "build",
+ "scripts",
+ "acceptance-tests",
+ "webpack",
+ "jest",
+ "src/setupTests.ts",
+ "**/odatajs-4.0.0.js",
+ "config-overrides.js"
+ ],
+ "include": [
+ "src"
+ ]
+}
diff --git a/samples/layouts/virtual-scroll/scroll-to-index/vite.config.js b/samples/layouts/virtual-scroll/scroll-to-index/vite.config.js
new file mode 100644
index 0000000000..1744dbc719
--- /dev/null
+++ b/samples/layouts/virtual-scroll/scroll-to-index/vite.config.js
@@ -0,0 +1,12 @@
+import { defineConfig } from 'vite';
+import react from '@vitejs/plugin-react';
+
+export default defineConfig({
+ plugins: [react()],
+ build: {
+ outDir: 'build'
+ },
+ server: {
+ open: false
+ },
+});
\ No newline at end of file
diff --git a/samples/layouts/virtual-scroll/variable-size/.devcontainer/devcontainer.json b/samples/layouts/virtual-scroll/variable-size/.devcontainer/devcontainer.json
new file mode 100644
index 0000000000..e0b8e9c925
--- /dev/null
+++ b/samples/layouts/virtual-scroll/variable-size/.devcontainer/devcontainer.json
@@ -0,0 +1,4 @@
+{
+ "name": "Node.js",
+ "image": "mcr.microsoft.com/devcontainers/javascript-node:22"
+}
\ No newline at end of file
diff --git a/samples/layouts/virtual-scroll/variable-size/.eslintrc.js b/samples/layouts/virtual-scroll/variable-size/.eslintrc.js
new file mode 100644
index 0000000000..b45160a9ee
--- /dev/null
+++ b/samples/layouts/virtual-scroll/variable-size/.eslintrc.js
@@ -0,0 +1,75 @@
+// https://www.robertcooper.me/using-eslint-and-prettier-in-a-typescript-project
+module.exports = {
+ parser: "@typescript-eslint/parser", // Specifies the ESLint parser
+ parserOptions: {
+ ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features
+ sourceType: "module", // Allows for the use of imports
+ ecmaFeatures: {
+ jsx: true // Allows for the parsing of JSX
+ }
+ },
+ settings: {
+ react: {
+ version: "999.999.999" // Tells eslint-plugin-react to automatically detect the version of React to use
+ }
+ },
+ extends: [
+ "eslint:recommended",
+ "plugin:react/recommended", // Uses the recommended rules from @eslint-plugin-react
+ "plugin:@typescript-eslint/recommended" // Uses the recommended rules from @typescript-eslint/eslint-plugin
+ ],
+ rules: {
+ // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
+ "default-case": "off",
+ "no-undef": "off",
+ "no-unused-vars": "off",
+ "no-extend-native": "off",
+ "no-throw-literal": "off",
+ "no-useless-concat": "off",
+ "no-mixed-operators": "off",
+ "no-prototype-builtins": "off",
+ "prefer-const": "off",
+ "prefer-rest-params": "off",
+ "jsx-a11y/alt-text": "off",
+ "jsx-a11y/iframe-has-title": "off",
+ "@typescript-eslint/no-unused-vars": "off",
+ "@typescript-eslint/no-explicit-any": "off",
+ "@typescript-eslint/no-inferrable-types": "off",
+ "@typescript-eslint/no-useless-constructor": "off",
+ "@typescript-eslint/no-use-before-define": "off",
+ "@typescript-eslint/no-non-null-assertion": "off",
+ "@typescript-eslint/interface-name-prefix": "off",
+ "@typescript-eslint/prefer-namespace-keyword": "off",
+ "@typescript-eslint/explicit-function-return-type": "off",
+ "@typescript-eslint/explicit-module-boundary-types": "off"
+ },
+ "overrides": [
+ {
+ "files": ["*.ts", "*.tsx"],
+ "rules": {
+ "default-case": "off",
+ "no-undef": "off",
+ "no-unused-vars": "off",
+ "no-extend-native": "off",
+ "no-throw-literal": "off",
+ "no-useless-concat": "off",
+ "no-mixed-operators": "off",
+ "no-prototype-builtins": "off",
+ "prefer-const": "off",
+ "prefer-rest-params": "off",
+ "jsx-a11y/alt-text": "off",
+ "jsx-a11y/iframe-has-title": "off",
+ "@typescript-eslint/no-unused-vars": "off",
+ "@typescript-eslint/no-explicit-any": "off",
+ "@typescript-eslint/no-inferrable-types": "off",
+ "@typescript-eslint/no-useless-constructor": "off",
+ "@typescript-eslint/no-use-before-define": "off",
+ "@typescript-eslint/no-non-null-assertion": "off",
+ "@typescript-eslint/interface-name-prefix": "off",
+ "@typescript-eslint/prefer-namespace-keyword": "off",
+ "@typescript-eslint/explicit-function-return-type": "off",
+ "@typescript-eslint/explicit-module-boundary-types": "off"
+ }
+ }
+ ]
+ };
\ No newline at end of file
diff --git a/samples/layouts/virtual-scroll/variable-size/README.md b/samples/layouts/virtual-scroll/variable-size/README.md
new file mode 100644
index 0000000000..7773a91f6f
--- /dev/null
+++ b/samples/layouts/virtual-scroll/variable-size/README.md
@@ -0,0 +1,56 @@
+
+
+
+This folder contains implementation of React application with example of Variable Size feature using [Virtual Scroll](https://www.infragistics.com/products/ignite-ui-react/react/components/general-getting-started.html) component.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Branches
+
+> **_NOTE:_** You should use [master](https://github.com/IgniteUI/igniteui-react-examples/tree/master) branch of this repository if you want to run samples on your computer. Use the [vnext](https://github.com/IgniteUI/igniteui-react-examples/tree/vnext) branch only when you want to contribute new samples to this repository.
+
+## Instructions
+
+Follow these instructions to run this example:
+
+
+```
+git clone https://github.com/IgniteUI/igniteui-react-examples.git
+git checkout master
+cd ./igniteui-react-examples
+cd ./samples/layouts/virtual-scroll/variable-size
+```
+
+open above folder in VS Code or type:
+```
+code .
+```
+
+In terminal window, run:
+```
+npm install --legacy-peer-deps
+npm run-script start
+```
+
+Then open http://localhost:4200/ in your browser
+
+
+## Learn More
+
+To learn more about **Ignite UI for React** components, check out the [React documentation](https://www.infragistics.com/products/ignite-ui-react/react/components/general-getting-started.html).
diff --git a/samples/layouts/virtual-scroll/variable-size/index.html b/samples/layouts/virtual-scroll/variable-size/index.html
new file mode 100644
index 0000000000..5a361d4638
--- /dev/null
+++ b/samples/layouts/virtual-scroll/variable-size/index.html
@@ -0,0 +1,11 @@
+
+
+
+ VirtualScrollVariableSize
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/layouts/virtual-scroll/variable-size/package.json b/samples/layouts/virtual-scroll/variable-size/package.json
new file mode 100644
index 0000000000..e2879dce64
--- /dev/null
+++ b/samples/layouts/virtual-scroll/variable-size/package.json
@@ -0,0 +1,42 @@
+{
+ "name": "react-virtual-scroll-variable-size",
+ "description": "This project provides example of Variable Item Size feature using Infragistics React components",
+ "author": "Infragistics",
+ "homepage": ".",
+ "version": "1.4.0",
+ "private": true,
+ "scripts": {
+ "start": "vite --port 4200",
+ "build": "tsc && node --max-old-space-size=4096 node_modules/vite/bin/vite build",
+ "preview": "vite preview",
+ "test": "vitest"
+ },
+ "dependencies": {
+ "igniteui-dockmanager": "^1.17.0",
+ "igniteui-react": "19.9.0",
+ "igniteui-react-core": "19.6.0",
+ "igniteui-webcomponents": "^7.4.1",
+ "react": "^19.2.0",
+ "react-dom": "^19.2.0",
+ "tslib": "^2.4.0"
+ },
+ "devDependencies": {
+ "@types/jest": "^29.2.0",
+ "@types/node": "^24.7.1",
+ "@types/react": "^18.0.24",
+ "@types/react-dom": "^18.0.8",
+ "@vitejs/plugin-react": "^5.0.4",
+ "@vitest/browser": "^3.2.4",
+ "typescript": "^4.8.4",
+ "vite": "^7.1.9",
+ "vitest": "^3.2.4",
+ "vitest-canvas-mock": "^0.3.3",
+ "worker-loader": "^3.0.8"
+ },
+ "browserslist": [
+ ">0.2%",
+ "not dead",
+ "not ie <= 11",
+ "not op_mini all"
+ ]
+}
diff --git a/samples/layouts/virtual-scroll/variable-size/sandbox.config.json b/samples/layouts/virtual-scroll/variable-size/sandbox.config.json
new file mode 100644
index 0000000000..00acba0c10
--- /dev/null
+++ b/samples/layouts/virtual-scroll/variable-size/sandbox.config.json
@@ -0,0 +1,5 @@
+{
+ "infiniteLoopProtection": false,
+ "hardReloadOnChange": false,
+ "view": "browser"
+}
\ No newline at end of file
diff --git a/samples/layouts/virtual-scroll/variable-size/src/EmployeeData.ts b/samples/layouts/virtual-scroll/variable-size/src/EmployeeData.ts
new file mode 100644
index 0000000000..cc0f0ea228
--- /dev/null
+++ b/samples/layouts/virtual-scroll/variable-size/src/EmployeeData.ts
@@ -0,0 +1,46 @@
+export type DepartmentVariant = "primary" | "info" | "success" | "warning" | "danger";
+
+export interface Employee {
+ id: number;
+ name: string;
+ initials: string;
+ email: string;
+ department: string;
+ variant: DepartmentVariant;
+ bio: string | null;
+}
+
+const DEPARTMENTS = ["Engineering", "Design", "Marketing", "Sales", "HR", "Finance", "Legal", "Operations"];
+const VARIANTS: DepartmentVariant[] = ["primary", "info", "success", "warning", "danger"];
+
+const FIRST_NAMES = ["Alice", "Bob", "Carol", "David", "Eve", "Frank", "Grace", "Henry", "Iris", "Jack", "Karen", "Leo", "Mia", "Noah", "Olivia", "Paul"];
+
+const LAST_NAMES = ["Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis", "Wilson", "Moore", "Taylor", "Anderson", "Thomas", "Jackson"];
+
+const BIOS = [
+ "Leads a cross-functional team across three time zones and owns the quarterly delivery plan.",
+ "Specializes in scalable service architecture.",
+ "Passionate about accessible, pixel-perfect user interfaces and design systems that scale across products.",
+ "Drives product strategy and stakeholder alignment.",
+ "Champions data-driven decision making, runs the weekly metrics review and mentors new analysts."
+];
+
+export function createEmployee(index: number): Employee {
+ const first = FIRST_NAMES[index % FIRST_NAMES.length];
+ const last = LAST_NAMES[Math.floor(index / FIRST_NAMES.length) % LAST_NAMES.length];
+ const departmentIndex = index % DEPARTMENTS.length;
+
+ return {
+ id: index + 1,
+ name: `${first} ${last}`,
+ initials: `${first[0]}${last[0]}`,
+ email: `${first.toLowerCase()}.${last.toLowerCase()}${index + 1}@example.com`,
+ department: DEPARTMENTS[departmentIndex],
+ variant: VARIANTS[departmentIndex % VARIANTS.length],
+ bio: index % 3 === 0 ? BIOS[index % BIOS.length] : null
+ };
+}
+
+export function generateEmployees(count: number, startIndex = 0): Employee[] {
+ return Array.from({ length: count }, (_, i) => createEmployee(startIndex + i));
+}
diff --git a/samples/layouts/virtual-scroll/variable-size/src/index.css b/samples/layouts/virtual-scroll/variable-size/src/index.css
new file mode 100644
index 0000000000..cdd1fc0eb6
--- /dev/null
+++ b/samples/layouts/virtual-scroll/variable-size/src/index.css
@@ -0,0 +1,13 @@
+/* shared styles are loaded from: */
+/* https://dl.infragistics.com/x/css/samples/shared.v8.css */
+
+.employees__viewport {
+ block-size: 480px;
+}
+
+.employees__bio {
+ display: block;
+ padding-block-start: 4px;
+ white-space: normal;
+ color: var(--ig-gray-700);
+}
diff --git a/samples/layouts/virtual-scroll/variable-size/src/index.tsx b/samples/layouts/virtual-scroll/variable-size/src/index.tsx
new file mode 100644
index 0000000000..ec67ab7d7d
--- /dev/null
+++ b/samples/layouts/virtual-scroll/variable-size/src/index.tsx
@@ -0,0 +1,50 @@
+import React from "react";
+import ReactDOM from "react-dom/client";
+import {
+ IgrAvatar,
+ IgrChip,
+ IgrList,
+ IgrListHeader,
+ IgrListItem,
+ IgrVirtualScroll,
+} from "igniteui-react";
+import type { VirtualScrollItemContext } from "igniteui-webcomponents";
+import "igniteui-webcomponents/themes/light/bootstrap.css";
+import { Employee, generateEmployees } from "./EmployeeData";
+import "./index.css";
+
+const employees: Employee[] = generateEmployees(10_000);
+
+export default function VirtualScrollVariableSize() {
+ // Every third employee has a bio, so the items have different heights.
+ // Each rendered item is measured and its size replaces the estimate.
+ const renderItem = (ctx: VirtualScrollItemContext) => (
+
+
+ {ctx.value.name}
+ {ctx.value.email}
+ {ctx.value.bio && {ctx.value.bio}}
+
+ {ctx.value.department}
+
+
+ );
+
+ return (
+
+
+ Team directory ({employees.length})
+
+
+
+ );
+}
+
+// rendering above component to the React DOM
+const root = ReactDOM.createRoot(document.getElementById("root"));
+root.render();
diff --git a/samples/layouts/virtual-scroll/variable-size/tsconfig.json b/samples/layouts/virtual-scroll/variable-size/tsconfig.json
new file mode 100644
index 0000000000..e7db8a94f5
--- /dev/null
+++ b/samples/layouts/virtual-scroll/variable-size/tsconfig.json
@@ -0,0 +1,44 @@
+{
+ "compilerOptions": {
+ "resolveJsonModule": true,
+ "esModuleInterop": true,
+ "baseUrl": ".",
+ "outDir": "build/dist",
+ "module": "esnext",
+ "target": "es5",
+ "lib": [
+ "es6",
+ "dom"
+ ],
+ "sourceMap": true,
+ "allowJs": true,
+ "jsx": "react",
+ "moduleResolution": "node",
+ "rootDir": "src",
+ "forceConsistentCasingInFileNames": true,
+ "noImplicitReturns": true,
+ "noImplicitThis": true,
+ "noImplicitAny": true,
+ "noUnusedLocals": false,
+ "importHelpers": true,
+ "allowSyntheticDefaultImports": true,
+ "skipLibCheck": true,
+ "strict": false,
+ "isolatedModules": true,
+ "noEmit": true
+ },
+ "exclude": [
+ "node_modules",
+ "build",
+ "scripts",
+ "acceptance-tests",
+ "webpack",
+ "jest",
+ "src/setupTests.ts",
+ "**/odatajs-4.0.0.js",
+ "config-overrides.js"
+ ],
+ "include": [
+ "src"
+ ]
+}
diff --git a/samples/layouts/virtual-scroll/variable-size/vite.config.js b/samples/layouts/virtual-scroll/variable-size/vite.config.js
new file mode 100644
index 0000000000..1744dbc719
--- /dev/null
+++ b/samples/layouts/virtual-scroll/variable-size/vite.config.js
@@ -0,0 +1,12 @@
+import { defineConfig } from 'vite';
+import react from '@vitejs/plugin-react';
+
+export default defineConfig({
+ plugins: [react()],
+ build: {
+ outDir: 'build'
+ },
+ server: {
+ open: false
+ },
+});
\ No newline at end of file