Skip to content

Commit ab9663d

Browse files
release: 0.0.13 added some fixes for datagrid, updated the sitmap, added code problem in playgroup and plugins
1 parent a4af29d commit ab9663d

8 files changed

Lines changed: 927 additions & 423 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@angular-bootstrap/ngbootstrap",
3-
"version": "0.0.12",
3+
"version": "0.0.13",
44
"description": "Angular UI library providing datagrid, drag-and-drop, pagination, stepper, splitter, typeahead and chips components with Bootstrap-friendly styling.",
55
"author": {
66
"name": "Harmeet Singh"

src/datagrid/src/datagrid/datagrid.component.html

Lines changed: 578 additions & 340 deletions
Large diffs are not rendered by default.

src/datagrid/src/datagrid/datagrid.component.scss

Lines changed: 55 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -10,51 +10,69 @@ th.sortable { cursor: pointer; user-select: none; }
1010

1111
.table-wrapper { position: relative; }
1212
.table-wrapper {
13-
display: flex;
14-
flex-direction: column;
13+
overflow-x: auto;
14+
border: 1px solid #dee2e6;
15+
border-radius: 12px;
16+
background: #fff;
1517
}
16-
.table-wrapper .table {
17-
table-layout: fixed;
18-
width: 100%;
19-
margin-bottom: 0;
18+
// .table-wrapper .table {
19+
// table-layout: fixed;
20+
// width: 100%;
21+
// margin-bottom: 0;
22+
// }
23+
.table-body-scroll {
24+
max-height: 320px;
25+
overflow-y: auto;
26+
overflow-x: hidden;
27+
border-top: 1px solid #e9ecef;
28+
border-bottom: 1px solid #e9ecef;
29+
scrollbar-gutter: stable both-edges;
30+
display: inline-block;
31+
width: fit-content;
32+
vertical-align: top;
2033
}
21-
.table-header {
22-
overflow-x: auto; overflow-y: hidden;
34+
.grid-head {
35+
border-bottom: 0;
2336
}
24-
.table-body {
25-
max-height: 60vh;
26-
overflow: auto;
27-
scrollbar-gutter: stable;
37+
38+
.grid-table {
39+
width: 100%;
40+
min-width: 1200px;
41+
table-layout: fixed;
42+
margin: 0;
43+
border-collapse: separate;
44+
border-spacing: 0;
2845
}
29-
.table-header th,
30-
.table-body td {
31-
min-width: 40px;
32-
max-width: 320px;
33-
word-break: break-word;
46+
.grid-head {
47+
position: relative;
48+
z-index: 5;
3449
}
35-
.table-header th { white-space: nowrap; }
36-
.table-body td { white-space: normal; }
37-
.table-header col,
38-
.table-body col {
39-
min-width: 40px;
40-
max-width: 320px;
50+
51+
.grid-head thead tr:first-child th:first-child {
52+
border-top-left-radius: 12px;
4153
}
42-
.table-wrapper.sticky-header thead th {
43-
position: sticky;
44-
top: 0;
45-
z-index: 5;
46-
background: #ccc;
54+
.grid-head thead th {
55+
background: var(--bs-table-bg, #f8f9fa);
56+
border-bottom: 1px solid #dee2e6;
57+
font-weight: 600;
4758
}
48-
.table-wrapper.sticky-footer .grid-footer {
49-
position: sticky;
50-
bottom: 0;
51-
z-index: 5;
52-
background: #ffffff;
53-
border-top: 1px solid #e2e2e2;
54-
border-left: 1px solid #e2e2e2;
55-
border-right: 1px solid #e2e2e2;
56-
padding: 5px 20px;
59+
.grid-table th, .grid-table td {
60+
white-space: nowrap;
61+
overflow: hidden;
62+
text-overflow: ellipsis;
63+
vertical-align: middle;
64+
border-bottom: 1px solid #e9ecef;
65+
padding: .55rem .65rem;
66+
}
67+
68+
.filter-row td {
69+
background: #fff;
70+
border-bottom: 1px solid #dee2e6;
71+
padding-top: .45rem;
72+
padding-bottom: .45rem;
5773
}
74+
75+
5876
.sticky-row {
5977
position: sticky;
6078
background: #ccc;

src/datagrid/src/datagrid/datagrid.component.spec.ts

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { ComponentFixture, TestBed } from '@angular/core/testing';
2+
import { By } from '@angular/platform-browser';
23
import { SimpleChange } from '@angular/core';
34
import { Datagrid } from './datagrid.component';
45
import { ColumnDef } from '../models/column-def';
@@ -565,7 +566,7 @@ describe('Datagrid', () => {
565566
});
566567
});
567568

568-
it('applies Bootstrap-like table options to header/body and wrapper', () => {
569+
it('applies Bootstrap-like table options to header/body and wrapper', async () => {
569570
component.tableOptions = {
570571
stripedRows: true,
571572
stripedColumns: true,
@@ -579,11 +580,14 @@ describe('Datagrid', () => {
579580
responsive: 'lg'
580581
};
581582
fixture.detectChanges();
583+
await fixture.whenStable();
582584

583585
const wrapper = fixture.nativeElement.querySelector('.table-wrapper') as HTMLElement;
584-
const headerTable = fixture.nativeElement.querySelector('.table-header table') as HTMLElement;
585-
const bodyTable = fixture.nativeElement.querySelector('.table-body table') as HTMLElement;
586-
const caption = headerTable.querySelector('caption') as HTMLElement | null;
586+
const headerTable = fixture.debugElement.query(By.css('.grid-header'))?.nativeElement as HTMLElement | null;
587+
const bodyTable = fixture.debugElement.query(By.css('.grid-body'))?.nativeElement as HTMLElement | null;
588+
expect(headerTable).toBeTruthy();
589+
expect(bodyTable).toBeTruthy();
590+
const caption = headerTable?.querySelector('caption') as HTMLElement | null;
587591

588592
expect(wrapper.classList.contains('table-responsive-lg')).toBe(true);
589593

@@ -617,7 +621,7 @@ describe('Datagrid', () => {
617621
expect(component.isPageAllSelected()).toBe(false);
618622
});
619623

620-
it('highlights rows and cells using provided highlight indices', () => {
624+
it('highlights rows and cells using provided highlight indices', async () => {
621625
component.highlightRowKey = (row: Person) => row.id;
622626
component.highlightColKey = (_col: ColumnDef<Person>) => _col.field;
623627
component.highlightedIndex = [
@@ -626,13 +630,35 @@ describe('Datagrid', () => {
626630
];
627631
component.updateHighlightCache();
628632
fixture.detectChanges();
633+
await fixture.whenStable();
629634

630-
const rows = fixture.nativeElement.querySelectorAll('.table-body tbody tr') as NodeListOf<HTMLElement>;
631-
const highlightedRow = rows[1];
632-
const cells = highlightedRow.querySelectorAll('td') as NodeListOf<HTMLElement>;
635+
const rows = fixture.debugElement.queryAll(By.css('.grid-body tbody tr'));
636+
expect(rows.length).toBeGreaterThan(1);
637+
const highlightedRow = rows[1].nativeElement as HTMLElement;
638+
const cells = highlightedRow?.querySelectorAll('td') as NodeListOf<HTMLElement>;
633639

634640
expect(highlightedRow.classList.contains('row-highlight')).toBe(true);
635641
expect(cells[3].classList.contains('cell-highlight')).toBe(true); // score column
636642
expect(cells[0].classList.contains('cell-highlight')).toBe(false);
637643
});
644+
645+
it('applies title attributes with fallbacks for headers and cells', async () => {
646+
component.columns = [
647+
{ ...baseColumns[0], title: 'Custom ID Title' },
648+
{ ...baseColumns[1], cellTitle: (row: Person) => `Name: ${row.name}` },
649+
...baseColumns.slice(2)
650+
];
651+
triggerColumnsChange();
652+
fixture.detectChanges();
653+
await fixture.whenStable();
654+
655+
const headerTitles = fixture.debugElement.queryAll(By.css('.grid-header thead th'))
656+
.map(d => (d.nativeElement as HTMLElement).getAttribute('title'));
657+
expect(headerTitles[0]).toBe('Custom ID Title');
658+
expect(headerTitles[1]).toBe('Name');
659+
660+
const firstRowCells = fixture.nativeElement.querySelectorAll('.grid-body tbody tr td') as NodeListOf<HTMLElement>;
661+
expect(firstRowCells[0].getAttribute('title')).toBe('1');
662+
expect(firstRowCells[1].getAttribute('title')).toBe('Name: Alice');
663+
});
638664
});

src/datagrid/src/datagrid/datagrid.component.ts

Lines changed: 16 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, Input, ChangeDetectionStrategy, EventEmitter, Output, inject, AfterContentInit, ContentChildren, QueryList, OnChanges, SimpleChanges, TemplateRef, ElementRef, ViewChild, AfterViewInit, ChangeDetectorRef } from '@angular/core';
1+
import { Component, Input, ChangeDetectionStrategy, EventEmitter, Output, inject, AfterContentInit, ContentChildren, QueryList, OnChanges, SimpleChanges, TemplateRef, ElementRef, ViewChild, ChangeDetectorRef } from '@angular/core';
22
import { CommonModule } from '@angular/common';
33
import { ColumnDef } from '../models/column-def';
44
import { FormsModule, ReactiveFormsModule, FormBuilder, FormGroup, Validators, AbstractControl, FormControl } from '@angular/forms';
@@ -88,7 +88,7 @@ const isReasonableEmail = (value: unknown): boolean => {
8888
standalone:true,
8989
changeDetection: ChangeDetectionStrategy.OnPush
9090
})
91-
export class Datagrid<T = any> implements AfterContentInit, AfterViewInit, OnChanges {
91+
export class Datagrid<T = any> implements AfterContentInit, OnChanges {
9292
/** Column definitions to render */
9393
@Input() columns: ColumnDef<T>[] = [];
9494

@@ -113,7 +113,7 @@ export class Datagrid<T = any> implements AfterContentInit, AfterViewInit, OnCha
113113
@Input() stickyHeader = false;
114114
/** Enables sticky footer when scrolling. */
115115
@Input() stickyFooter = false;
116-
/** Enables scroll container (used when pagination is off). */
116+
/** Enables scroll table body container */
117117
@Input() scrollable = true;
118118
/** Row height used to stack multiple sticky rows without overlap (px). */
119119
@Input() stickyRowHeight = 40;
@@ -141,7 +141,6 @@ export class Datagrid<T = any> implements AfterContentInit, AfterViewInit, OnCha
141141
@Input() highlightRowKey: NgbRowKey | null = null;
142142
/** Column key for highlighting. */
143143
@Input() highlightColKey: NgbColKey | null = null;
144-
scrollbarWidth = 0;
145144
/** Accessible label for the global filter input. */
146145
@Input() globalFilterAriaLabel = 'Search all columns';
147146
/** Accessible label announced when expanding a row. */
@@ -418,6 +417,19 @@ export class Datagrid<T = any> implements AfterContentInit, AfterViewInit, OnCha
418417
return (col.header ?? col.field ?? '').toString();
419418
}
420419

420+
headerTitle(col: ColumnDef<T>): string {
421+
const t = (col as any)?.title;
422+
return (t ?? this.headerText(col)) ?? '';
423+
}
424+
425+
cellTitle(row: T, col: ColumnDef<T>): string {
426+
const def = (col as any)?.cellTitle;
427+
if (typeof def === 'function') return def(row) ?? '';
428+
if (typeof def === 'string') return def;
429+
const val = (row as any)?.[col.field];
430+
return val === undefined || val === null ? '' : String(val);
431+
}
432+
421433
columnFilterAriaLabel(col: ColumnDef<T>): string {
422434
return `${this.headerText(col)} filter`;
423435
}
@@ -559,9 +571,6 @@ export class Datagrid<T = any> implements AfterContentInit, AfterViewInit, OnCha
559571
this.filterTplQ?.changes.subscribe(rebuild);
560572
this.globalTplQ?.changes.subscribe(rebuild);
561573
}
562-
ngAfterViewInit(): void {
563-
queueMicrotask(() => this.syncScrollbarWidth());
564-
}
565574

566575
ngOnChanges(ch: SimpleChanges): void {
567576
if (ch['columns']) this.rebuildFilterForm();
@@ -998,22 +1007,4 @@ export class Datagrid<T = any> implements AfterContentInit, AfterViewInit, OnCha
9981007
this.toggleSelection(pagedIndex, ev);
9991008
}
10001009

1001-
onHeaderScroll(): void {
1002-
const body = this.bodyScroller?.nativeElement;
1003-
const head = this.headerScroller?.nativeElement;
1004-
if (body && head && body.scrollLeft !== head.scrollLeft) {
1005-
body.scrollLeft = head.scrollLeft;
1006-
}
1007-
}
1008-
1009-
private syncScrollbarWidth(): void {
1010-
const el = this.bodyScroller?.nativeElement;
1011-
if (!el) return;
1012-
const width = el.offsetWidth - el.clientWidth;
1013-
if (width !== this.scrollbarWidth) {
1014-
this.scrollbarWidth = width;
1015-
this.cdr.markForCheck();
1016-
}
1017-
}
1018-
10191010
}

0 commit comments

Comments
 (0)