Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 123 additions & 0 deletions src/app/examples/datatable/datatable-bulk-actions-side-panel.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
<div class="has-navbar-fixed-top si-layout-fixed-height h-100">
<si-application-header>
<si-header-brand>
<h1 class="application-name">Bulk actions with side panel detail</h1>
</si-header-brand>
</si-application-header>

<si-side-panel
mode="scroll"
size="regular"
[collapsed]="panelCollapsed()"
(closed)="closePanel()"
>
<div class="si-layout-fixed-height si-layout-main-padding">
<ngx-datatable
#table
class="table-element si-layout-fixed-height"
siDatatableInteraction
columnMode="force"
[class.bulk-actions-mode]="checked().size > 0"
[selectionType]="checked().size > 0 ? undefined : 'single'"
[summaryRow]="checked().size > 0"
[summaryHeight]="tableConfig.rowHeightSmall"
[rows]="rows()"
[columns]="columns"
[rowHeight]="tableConfig.rowHeightSmall"
[scrollbarV]="true"
[footerHeight]="0"
[(selected)]="selectedRow"
(siResizeObserver)="table.recalculate()"
(selectedChange)="panelCollapsed.set(false)"
/>
</div>

<si-side-panel-content>
<div class="d-flex flex-column h-100">
@if (checked().size > 0) {
<si-empty-state icon="element-user-group" [heading]="checked().size + ' rows selected'" />
} @else if (selectedRow().length === 1) {
@let selectedRow = this.selectedRow()[0];
<div class="p-6">
<div class="mb-4">
<label for="detailName" class="form-label">Name</label>
<input
readonly
type="text"
class="form-control"
id="detailName"
[value]="selectedRow.name"
/>
</div>
<div class="mb-4">
<label for="detailDepartment" class="form-label">Department</label>
<input
readonly
type="text"
class="form-control"
id="detailDepartment"
[value]="selectedRow.department"
/>
</div>
<div class="mb-4">
<label for="detailRole" class="form-label">Role</label>
<input
readonly
type="text"
class="form-control"
id="detailRole"
[value]="selectedRow.role"
/>
</div>
<div class="mb-0">
<label for="detailStatus" class="form-label">Status</label>
<input
readonly
type="text"
class="form-control"
id="detailStatus"
[value]="selectedRow.status"
/>
</div>
</div>
<div class="mt-auto p-5">
<button type="button" class="btn btn-primary w-100" (click)="acknowledge()">
Acknowledge
</button>
</div>
}
</div>
</si-side-panel-content>
</si-side-panel>
</div>

<ng-template #checkboxHeaderTmpl>
<input
type="checkbox"
class="form-check-input"
aria-label="Select all"
[checked]="allChecked()"
[indeterminate]="someChecked()"
(change)="toggleAll()"
/>
</ng-template>

<ng-template #checkboxCellTmpl let-row="row">
<input
type="checkbox"
class="form-check-input"
[attr.aria-label]="'Select ' + row.name"
[checked]="checked().has(row.id)"
(click)="$event.stopPropagation()"
(change)="toggleRow(row)"
/>
</ng-template>

<ng-template #bulkActionsTemplate>
<div class="d-flex align-items-center">
<span class="text-body text-nowrap fw-normal">{{ checked().size }} selected</span>
<div class="ms-auto">
<button type="button" class="btn btn-primary" (click)="acknowledge()"> Acknowledge </button>
</div>
</div>
</ng-template>
150 changes: 150 additions & 0 deletions src/app/examples/datatable/datatable-bulk-actions-side-panel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/**
* Copyright (c) Siemens 2016 - 2026
* SPDX-License-Identifier: MIT
*/
import {
ChangeDetectionStrategy,
Component,
computed,
OnInit,
signal,
TemplateRef,
viewChild
} from '@angular/core';
import {
SiApplicationHeaderComponent,
SiHeaderBrandDirective
} from '@siemens/element-ng/application-header';
import { SI_DATATABLE_CONFIG, SiDatatableModule } from '@siemens/element-ng/datatable';
import { SiEmptyStateComponent } from '@siemens/element-ng/empty-state';
import { SiResizeObserverDirective } from '@siemens/element-ng/resize-observer';
import { SiSidePanelComponent, SiSidePanelContentComponent } from '@siemens/element-ng/side-panel';
import { ActivateEvent, NgxDatatableModule, TableColumn } from '@siemens/ngx-datatable';

interface Employee {
id: number;
name: string;
department: string;
role: string;
status: string;
}

@Component({
selector: 'app-sample',
imports: [
NgxDatatableModule,
SiApplicationHeaderComponent,
SiDatatableModule,
SiEmptyStateComponent,
SiHeaderBrandDirective,
SiResizeObserverDirective,
SiSidePanelComponent,
SiSidePanelContentComponent
],
templateUrl: './datatable-bulk-actions-side-panel.html',
styleUrl: './datatable-bulk-actions.scss',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class SampleComponent implements OnInit {
protected readonly tableConfig = SI_DATATABLE_CONFIG;
protected readonly panelCollapsed = signal(true);
protected readonly selectedRow = signal<Employee[]>([]);

private readonly bulkActionsTemplate = viewChild.required('bulkActionsTemplate', {
read: TemplateRef<unknown>
});
private readonly checkboxCellTemplate = viewChild.required('checkboxCellTmpl', {
read: TemplateRef<unknown>
});
private readonly checkboxHeaderTemplate = viewChild.required('checkboxHeaderTmpl', {
read: TemplateRef<unknown>
});

protected readonly checked = signal(new Set<number>());
protected readonly rows = signal<Employee[]>([]);
columns!: TableColumn[];

private readonly departments = ['Engineering', 'Marketing', 'Sales', 'Support'];
private readonly roles = ['Developer', 'Designer', 'Manager', 'Analyst'];
private readonly statuses = ['Active', 'Inactive', 'On Leave'];

protected readonly allChecked = computed(
() => this.rows().length > 0 && this.checked().size === this.rows().length
);
protected readonly someChecked = computed(
() => this.checked().size > 0 && this.checked().size < this.rows().length
);

constructor() {
const initial: Employee[] = [];
for (let i = 1; i <= 50; i++) {
initial.push({
id: i,
name: 'Employee ' + i,
department: this.departments[i % this.departments.length],
role: this.roles[i % this.roles.length],
status: this.statuses[i % this.statuses.length]
});
}
this.rows.set(initial);
}

ngOnInit(): void {
this.columns = [
{
name: '',
width: 50,
sortable: false,
resizeable: false,
canAutoResize: false,
headerTemplate: this.checkboxHeaderTemplate(),
cellTemplate: this.checkboxCellTemplate(),
summaryTemplate: this.bulkActionsTemplate(),
cellClass: 'bulk-actions'
},
{ name: 'Name', prop: 'name', summaryFunc: null },
{ name: 'Department', prop: 'department', summaryFunc: null },
{ name: 'Role', prop: 'role', summaryFunc: null },
{ name: 'Status', prop: 'status', summaryFunc: null }
];
}

toggleAll(): void {
if (this.allChecked()) {
this.checked.set(new Set());
} else {
this.checked.set(new Set(this.rows().map(r => r.id)));
}
this.updatePanel();
}

toggleRow(row: Employee): void {
const next = new Set(this.checked());
if (next.has(row.id)) {
next.delete(row.id);
} else {
next.add(row.id);
}
this.checked.set(next);
this.updatePanel();
}

acknowledge(): void {
if (this.checked().size > 0) {
alert(`Acknowledged ${this.checked().size} items`);
} else if (this.selectedRow()) {
alert(`Acknowledged ${this.selectedRow()!.length} items`);
}
}

closePanel(): void {
this.panelCollapsed.set(true);
this.selectedRow.set([]);
}

private updatePanel(): void {
if (this.checked().size > 0) {
this.selectedRow.set([]);
}
}
}
78 changes: 78 additions & 0 deletions src/app/examples/datatable/datatable-bulk-actions.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<div class="p-5 clearfix si-layout-fixed-height h-100">
<ngx-datatable
class="table-element si-layout-fixed-height"
siDatatableInteraction
columnMode="force"
[class.bulk-actions-mode]="checked().size > 0"
[selectionType]="checked().size > 0 ? undefined : 'single'"
[summaryRow]="checked().size > 0"
[summaryHeight]="tableConfig.rowHeightSmall"
[rows]="rows()"
[columns]="columns"
[rowHeight]="tableConfig.rowHeightSmall"
[scrollbarV]="true"
[footerHeight]="0"
/>
</div>

<ng-template #checkboxHeaderTmpl>
<input
type="checkbox"
class="form-check-input"
aria-label="Select all"
[checked]="allChecked()"
[indeterminate]="someChecked()"
(change)="toggleAll()"
/>
</ng-template>

<ng-template #checkboxCellTmpl let-row="row">
<input
type="checkbox"
class="form-check-input"
[attr.aria-label]="'Select ' + row.name"
[checked]="checked().has(row.id)"
(click)="$event.stopPropagation()"
(change)="toggleRow(row)"
/>
</ng-template>

<ng-template #bulkActionsTemplate>
<div class="d-flex align-items-center" (siResizeObserver)="onResize($event)">
<span class="text-body text-nowrap fw-normal">{{ checked().size }} selected</span>
<div class="d-flex gap-2 ms-auto">
@if (collapsed()) {
<button
type="button"
class="btn btn-icon btn-tertiary"
aria-label="Actions"
[cdkMenuTriggerFor]="allActionsMenu"
>
<si-icon [icon]="icons.elementOptionsVertical" />
</button>
} @else {
<button
type="button"
class="btn btn-tertiary dropdown-toggle"
[class.show]="statusMenuOpen()"
[cdkMenuTriggerFor]="statusMenu"
(cdkMenuOpened)="statusMenuOpen.set(true)"
(cdkMenuClosed)="statusMenuOpen.set(false)"
>
Change status
<i aria-hidden="true" class="dropdown-caret icon element-down-2"></i>
</button>
<button type="button" class="btn btn-tertiary" (click)="delete()"> Delete </button>
<button type="button" class="btn btn-tertiary" (click)="export()"> Export </button>
}
</div>
</div>
</ng-template>

<ng-template #allActionsMenu>
<si-menu-factory [items]="allActions" />
</ng-template>

<ng-template #statusMenu>
<si-menu-factory [items]="statusMenuItems" />
</ng-template>
21 changes: 21 additions & 0 deletions src/app/examples/datatable/datatable-bulk-actions.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
::ng-deep {
.datatable-summary-row {
position: sticky;
inset-block-start: 0;
z-index: 1;
}

.datatable-summary-row .datatable-body-cell {
inline-size: 100% !important; // stylelint-disable-line declaration-no-important

&:not(.bulk-actions) {
display: none !important; // stylelint-disable-line declaration-no-important
}
}
Comment on lines +2 to +14
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@spike-rabbit I am not in favor of overriding summary row styles like this but there is no other option if we want to reuse the summary row itself as bulk actions row.


.bulk-actions-mode {
.datatable-body-row:not(.datatable-summary-row *):hover {
background-color: revert !important; // stylelint-disable-line declaration-no-important
}
}
}
Loading
Loading