diff --git a/packages/components-dev/table/module.ts b/packages/components-dev/table/module.ts
index cf0925fb4f..9abf5593ad 100644
--- a/packages/components-dev/table/module.ts
+++ b/packages/components-dev/table/module.ts
@@ -2,14 +2,15 @@ import { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/
import { KbqButtonModule } from '@koobiq/components/button';
import { KbqComponentColors } from '@koobiq/components/core';
import { KbqTableModule } from '@koobiq/components/table';
-import { TableDisableHoverExample } from 'packages/docs-examples/components/table';
+import { TableDisableHoverExample, TableStickyHeaderExample } from 'packages/docs-examples/components/table';
@Component({
selector: 'dev-examples',
- imports: [TableDisableHoverExample],
+ imports: [TableDisableHoverExample, TableStickyHeaderExample],
template: `
+
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
diff --git a/packages/components/table/__screenshots__/01-dark.png b/packages/components/table/__screenshots__/01-dark.png
index 6c937b0713..ea86ee1db2 100644
Binary files a/packages/components/table/__screenshots__/01-dark.png and b/packages/components/table/__screenshots__/01-dark.png differ
diff --git a/packages/components/table/__screenshots__/01-light.png b/packages/components/table/__screenshots__/01-light.png
index 3fd4b6ca93..b0421e4060 100644
Binary files a/packages/components/table/__screenshots__/01-light.png and b/packages/components/table/__screenshots__/01-light.png differ
diff --git a/packages/components/table/_table-theme.scss b/packages/components/table/_table-theme.scss
index c2654f9bbd..bbcc1e78dc 100644
--- a/packages/components/table/_table-theme.scss
+++ b/packages/components/table/_table-theme.scss
@@ -28,6 +28,10 @@
}
}
+ &.kbq-table_sticky-header > thead > tr > th {
+ background-color: var(--kbq-background-bg);
+ }
+
&.kbq-table_bordered {
& > tbody > tr {
& th,
diff --git a/packages/components/table/e2e.playwright-spec.ts b/packages/components/table/e2e.playwright-spec.ts
index 600668f89e..335a1788a5 100644
--- a/packages/components/table/e2e.playwright-spec.ts
+++ b/packages/components/table/e2e.playwright-spec.ts
@@ -7,6 +7,9 @@ test.describe('KbqTableModule', () => {
test('states', async ({ page }) => {
await page.goto('/E2eTableStates');
+ await getComponent(page)
+ .getByTestId('e2eTableStickyHeader')
+ .evaluate((el) => (el.scrollTop = 60));
await expect(getComponent(page)).toHaveScreenshot('01-light.png');
await e2eEnableDarkTheme(page);
await expect(getComponent(page)).toHaveScreenshot('01-dark.png');
diff --git a/packages/components/table/e2e.ts b/packages/components/table/e2e.ts
index acdc1a6ddb..4354c602d5 100644
--- a/packages/components/table/e2e.ts
+++ b/packages/components/table/e2e.ts
@@ -158,6 +158,28 @@ import { KbqTableModule } from '@koobiq/components/table';
+
+
+
+
+
+
+ @for (th of [0, 1, 2, 3]; track $index) {
+ | Sticky |
+ }
+
+
+
+ @for (tr of [0, 1, 2, 3, 4, 5]; track $index) {
+
+ @for (td of [0, 1, 2, 3]; track $index) {
+ | Cell |
+ }
+
+ }
+
+
+
`,
styles: `
:host {
diff --git a/packages/components/table/table.component.ts b/packages/components/table/table.component.ts
index 8d3f9aaa9c..cfa6a13dbd 100644
--- a/packages/components/table/table.component.ts
+++ b/packages/components/table/table.component.ts
@@ -18,13 +18,15 @@ import { KbqButton } from '@koobiq/components/button';
host: {
class: 'kbq-table',
'[class.kbq-table_bordered]': 'border()',
- '[class.kbq-table_disable-hover]': 'disableHover()'
+ '[class.kbq-table_disable-hover]': 'disableHover()',
+ '[class.kbq-table_sticky-header]': 'stickyHeader()'
},
exportAs: 'kbqTable'
})
export class KbqTable {
readonly border = input(false, { transform: booleanAttribute });
readonly disableHover = input(false, { transform: booleanAttribute });
+ readonly stickyHeader = input(false, { transform: booleanAttribute });
}
@Directive({
diff --git a/packages/components/table/table.en.md b/packages/components/table/table.en.md
index 5322caaaf1..c4ac14ded6 100644
--- a/packages/components/table/table.en.md
+++ b/packages/components/table/table.en.md
@@ -19,3 +19,9 @@ Borders are also useful in tables with complex structures — for example, when
Use `disableHover` to remove the background color change on row hover. This is useful when rows are not interactive and the hover highlight would be misleading.
+
+### Sticky header
+
+If a table has a lot of rows, use `stickyHeader` to keep the column names visible while you scroll down. This way you always see what each column means, even after scrolling.
+
+
diff --git a/packages/components/table/table.ru.md b/packages/components/table/table.ru.md
index 2df8528cae..c76c09272d 100644
--- a/packages/components/table/table.ru.md
+++ b/packages/components/table/table.ru.md
@@ -19,3 +19,9 @@
Если строки не являются интерактивными, подсветка при наведении может вводить в заблуждение. Атрибут `disableHover` позволяет убрать этот эффект.
+
+### Закрепленная шапка
+
+Если в таблице много строк, используйте атрибут `stickyHeader`, чтобы названия столбцов оставались видимыми при прокрутке.
+
+
diff --git a/packages/components/table/table.scss b/packages/components/table/table.scss
index de3fb88feb..06d16547d1 100644
--- a/packages/components/table/table.scss
+++ b/packages/components/table/table.scss
@@ -37,6 +37,14 @@
}
}
+.kbq-table.kbq-table_sticky-header {
+ & > thead > tr > th {
+ position: sticky;
+ top: 0;
+ z-index: 1;
+ }
+}
+
.kbq-table.kbq-table_bordered {
& > tbody > tr {
& th,
diff --git a/packages/docs-examples/components/table/index.ts b/packages/docs-examples/components/table/index.ts
index b6c5a87bda..104e1203a1 100644
--- a/packages/docs-examples/components/table/index.ts
+++ b/packages/docs-examples/components/table/index.ts
@@ -2,15 +2,23 @@ import { NgModule } from '@angular/core';
import { TableDisableHoverExample } from './table-disable-hover/table-disable-hover-example';
import { TableFullWidthExample } from './table-full-width/table-full-width-example';
import { TableOverviewExample } from './table-overview/table-overview-example';
+import { TableStickyHeaderExample } from './table-sticky-header/table-sticky-header-example';
import { TableWithBordersExample } from './table-with-borders/table-with-borders-example';
-export { TableDisableHoverExample, TableFullWidthExample, TableOverviewExample, TableWithBordersExample };
+export {
+ TableDisableHoverExample,
+ TableFullWidthExample,
+ TableOverviewExample,
+ TableStickyHeaderExample,
+ TableWithBordersExample
+};
const EXAMPLES = [
TableOverviewExample,
TableWithBordersExample,
TableFullWidthExample,
- TableDisableHoverExample
+ TableDisableHoverExample,
+ TableStickyHeaderExample
];
@NgModule({
diff --git a/packages/docs-examples/components/table/table-sticky-header/table-sticky-header-example.ts b/packages/docs-examples/components/table/table-sticky-header/table-sticky-header-example.ts
new file mode 100644
index 0000000000..6717a86176
--- /dev/null
+++ b/packages/docs-examples/components/table/table-sticky-header/table-sticky-header-example.ts
@@ -0,0 +1,56 @@
+import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
+import { KbqLuxonDateModule } from '@koobiq/angular-luxon-adapter/adapter';
+import { DateAdapter, DateFormatter, KbqRelativeShortDatePipe } from '@koobiq/components/core';
+import { KbqTableModule } from '@koobiq/components/table';
+import { DateTime } from 'luxon';
+
+/**
+ * @title Table with sticky header
+ */
+@Component({
+ selector: 'table-sticky-header-example',
+ imports: [KbqTableModule, KbqLuxonDateModule, KbqRelativeShortDatePipe],
+ template: `
+
+
+
+
+ | File |
+ Owner |
+ Modified |
+
+
+
+ @for (row of rows; track row.name) {
+
+ | {{ row.name }} |
+ {{ row.owner }} |
+ {{ row.modified | kbqRelativeShortDate }} |
+
+ }
+
+
+
+ `,
+ providers: [
+ { provide: DateFormatter, deps: [DateAdapter] }
+ ],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class TableStickyHeaderExample {
+ readonly adapter = inject>(DateAdapter);
+ readonly value = this.adapter.today();
+
+ protected readonly rows = [
+ { name: 'document.txt', owner: 'User 1', modified: this.value },
+ { name: 'report-2023.pdf', owner: 'User 2', modified: this.value },
+ { name: 'notes.doc', owner: 'User 3', modified: this.value },
+ { name: 'archive.zip', owner: 'User 4', modified: this.value },
+ { name: 'presentation.pptx', owner: 'User 5', modified: this.value },
+ { name: 'budget.xlsx', owner: 'User 6', modified: this.value },
+ { name: 'photo.png', owner: 'User 7', modified: this.value },
+ { name: 'contract.pdf', owner: 'User 8', modified: this.value },
+ { name: 'summary.doc', owner: 'User 9', modified: this.value },
+ { name: 'backup.zip', owner: 'User 10', modified: this.value }
+ ];
+}
diff --git a/packages/docs-examples/example-module.ts b/packages/docs-examples/example-module.ts
index d29ac4b48b..149f1c21fa 100644
--- a/packages/docs-examples/example-module.ts
+++ b/packages/docs-examples/example-module.ts
@@ -2059,6 +2059,19 @@ export const EXAMPLE_COMPONENTS: {[id: string]: LiveExample} = {
"primaryFile": "file-upload-multiple-accept-validation-example.ts",
"importPath": "components/file-upload"
},
+ "file-upload-multiple-add-strategy": {
+ "packagePath": "components/file-upload/file-upload-multiple-add-strategy",
+ "title": "File-upload multiple add strategy",
+ "componentName": "FileUploadMultipleAddStrategyExample",
+ "files": [
+ "file-upload-multiple-add-strategy-example.ts"
+ ],
+ "localImportFiles": [],
+ "selector": "file-upload-multiple-add-strategy-example",
+ "additionalComponents": [],
+ "primaryFile": "file-upload-multiple-add-strategy-example.ts",
+ "importPath": "components/file-upload"
+ },
"file-upload-multiple-compact-overview": {
"packagePath": "components/file-upload/file-upload-multiple-compact-overview",
"title": "File-upload multiple compact",
@@ -5640,6 +5653,19 @@ export const EXAMPLE_COMPONENTS: {[id: string]: LiveExample} = {
"primaryFile": "table-overview-example.ts",
"importPath": "components/table"
},
+ "table-sticky-header": {
+ "packagePath": "components/table/table-sticky-header",
+ "title": "Table with sticky header",
+ "componentName": "TableStickyHeaderExample",
+ "files": [
+ "table-sticky-header-example.ts"
+ ],
+ "localImportFiles": [],
+ "selector": "table-sticky-header-example",
+ "additionalComponents": [],
+ "primaryFile": "table-sticky-header-example.ts",
+ "importPath": "components/table"
+ },
"table-with-borders": {
"packagePath": "components/table/table-with-borders",
"title": "Table with borders",
@@ -7968,6 +7994,8 @@ return import('@koobiq/docs-examples/components/file-upload');
case 'file-upload-local-dropzone':
return import('@koobiq/docs-examples/components/file-upload');
case 'file-upload-multiple-accept-validation':
+return import('@koobiq/docs-examples/components/file-upload');
+ case 'file-upload-multiple-add-strategy':
return import('@koobiq/docs-examples/components/file-upload');
case 'file-upload-multiple-compact-overview':
return import('@koobiq/docs-examples/components/file-upload');
@@ -8506,6 +8534,8 @@ return import('@koobiq/docs-examples/components/table');
case 'table-full-width':
return import('@koobiq/docs-examples/components/table');
case 'table-overview':
+return import('@koobiq/docs-examples/components/table');
+ case 'table-sticky-header':
return import('@koobiq/docs-examples/components/table');
case 'table-with-borders':
return import('@koobiq/docs-examples/components/table');
diff --git a/tools/public_api_guard/components/table.api.md b/tools/public_api_guard/components/table.api.md
index f14fc8a45b..9db83fd6d8 100644
--- a/tools/public_api_guard/components/table.api.md
+++ b/tools/public_api_guard/components/table.api.md
@@ -16,7 +16,9 @@ export class KbqTable {
// (undocumented)
readonly disableHover: i0.InputSignalWithTransform;
// (undocumented)
- static ɵcmp: i0.ɵɵComponentDeclaration;
+ readonly stickyHeader: i0.InputSignalWithTransform;
+ // (undocumented)
+ static ɵcmp: i0.ɵɵComponentDeclaration;
// (undocumented)
static ɵfac: i0.ɵɵFactoryDeclaration;
}