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
6 changes: 6 additions & 0 deletions apps/angular-app/src/app/app.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Routes } from '@angular/router';
import { LoginComponent } from './login/login.component';
import { AppShellComponent } from './shell/app-shell.component';
import { BrowseAppsComponent } from './browse-apps/browse-apps.component';
import { CustomDataTableComponent } from './custom-data-table/custom-data-table.component';

export const routes: Routes = [
{
Expand All @@ -23,4 +24,9 @@ export const routes: Routes = [
},
],
},
{
path: 'table',
pathMatch: 'full',
component: CustomDataTableComponent,
},
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<p>
Dit is bijna dezelfde tabel, maar deze gebruikt een wrapper component die tabelconfiguratie
grotendeels standaardiseert, waardoor het gebruik er van een stuk eenvoudiger is. Het wordt
daarmee ook opiniated, maar dat wil je tot op zekere hoogte ook om consistentie af te dwingen.
</p>

<curve-data-table
[data]="data"
[columns]="columns"
searchColumn="app"
(rowActionTriggered)="rowActionTriggered($event)"
></curve-data-table>

<br /><br />
<p>Laatst uitgevoerde actie: {{ lastActionDisplay }}</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { Component } from '@angular/core';
import { provideIcons } from '@ng-icons/core';
import { phosphorPlus } from '@ng-icons/phosphor-icons/regular';
import {
ICurveDataTableColumn,
CurveDataTableImports,
CurveDataTableActionColumn,
CurveDataTableImageColumn,
CurveDataTableSelectionColumn,
CurveDataTableStringColumn,
CurveDataTableRowEvent,
} from '@surfnet/curve-angular';

type App = {
id: string;
imageUrl?: string;
app: string;
vendor: string;
revenue: number;
};

const data: App[] = [
{
id: '1',
imageUrl: 'https://picsum.photos/seed/123/40',
app: '10voordeleraar-loa1',
vendor: '10voordeleraar-loa1',
revenue: 199,
},
{
id: '2',
imageUrl: 'https://picsum.photos/seed/321/40',
app: '10voordeleraar-loa2-without-interaction-with-mail',
vendor: '10voordeleraar-loa2-without-interaction-with-mail',
revenue: 199,
},
{
id: '3',
app: '10voordeleraar-loa2-without-interaction-with-mail',
vendor: '10voordeleraar-loa2-without-interaction-with-mail',
revenue: 199,
},
{
id: '4',
app: '10voordeleraar-loa2-without-interaction-with-mail',
vendor: '10voordeleraar-loa2-without-interaction-with-mail',
revenue: 199,
},
{
id: '5',
imageUrl: 'https://picsum.photos/seed/123/40',
app: '10voordeleraar-loa1',
vendor: '10voordeleraar-loa1',
revenue: 199,
},
{
id: '6',
app: '10voordeleraar-loa2-without-interaction-with-mail',
vendor: '10voordeleraar-loa2-without-interaction-with-mail',
revenue: 199,
},
{
id: '7',
imageUrl: 'https://picsum.photos/seed/123/40',
app: '10voordeleraar-loa1',
vendor: '10voordeleraar-loa1',
revenue: 199,
},
{
id: '8',
imageUrl: 'https://picsum.photos/seed/123/40',
app: '10voordeleraar-loa1',
vendor: '10voordeleraar-loa1',
revenue: 199,
},
];

@Component({
selector: 'app-custom-data-table',
templateUrl: './custom-data-table.component.html',
imports: [CurveDataTableImports],
providers: [provideIcons({ phosphorPlus })],
})
export class CustomDataTableComponent {
public lastActionDisplay: string = '';
public data = data;
public columns: ICurveDataTableColumn[] = [
new CurveDataTableSelectionColumn(),
new CurveDataTableImageColumn('imageUrl'),
new CurveDataTableStringColumn('app', 'App'),
new CurveDataTableStringColumn('vendor', 'Vendor'),
new CurveDataTableStringColumn('revenue', 'Revenue', (value: number) =>
this.currencyFormatter.format(value),
),
new CurveDataTableActionColumn(['Go to details', 'Set inactive']),
];

private currencyFormatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
});

protected rowActionTriggered(event: CurveDataTableRowEvent<App>) {
this.lastActionDisplay = `'${event.action}' op rij ${event.record.id}`;
}
}
11 changes: 11 additions & 0 deletions packages/angular/src/lib/ui/curve-data-table/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { CurveDataTableComponent } from './lib/curve-data-table';

export * from './lib/curve-data-table';
export * from './lib/model/curve-data-table-column';
export * from './lib/model/curve-data-table-action-column';
export * from './lib/model/curve-data-table-image-column';
export * from './lib/model/curve-data-table-selection-column';
export * from './lib/model/curve-data-table-string-column';
export * from './lib/model/curve-data-table-row-event';

export const CurveDataTableImports = [CurveDataTableComponent] as const;
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Component, ChangeDetectionStrategy, input, output } from '@angular/core';
import { NgIcon, provideIcons } from '@ng-icons/core';
import { phosphorDotsThree } from '@ng-icons/phosphor-icons/regular';
import { HlmDropdownMenuImports } from '../../../dropdown-menu/src';
import { CurveDataTableRowEvent } from './model/curve-data-table-row-event';

@Component({
selector: 'data-table-icon-cell',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [HlmDropdownMenuImports, NgIcon],
providers: [provideIcons({ phosphorDotsThree })],
template: `
<button hlmBtn variant="ghost" size="sm" [hlmDropdownMenuTrigger]="menu">
<ng-icon name="phosphorDotsThree" />
</button>
<ng-template #menu>
<hlm-dropdown-menu align="start" class="w-48">
@for (action of actions(); track action) {
<button hlmDropdownMenuItem (click)="onActionClick(action)">
{{ action }}
</button>
}
</hlm-dropdown-menu>
</ng-template>
`,
})
export class DataTableActionCell<TData> {
public readonly record = input.required<TData>();
public readonly actions = input.required<string[]>();
public actionTriggered = output<CurveDataTableRowEvent<TData>>();

onActionClick(action: string) {
const event: CurveDataTableRowEvent<TData> = { action: action, record: this.record() };
this.actionTriggered.emit(event);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { ChangeDetectionStrategy, Component, input } from '@angular/core';
import { NgIcon, provideIcons } from '@ng-icons/core';
import { phosphorArrowsDownUp } from '@ng-icons/phosphor-icons/regular';
import { Column } from '@tanstack/angular-table';
import { HlmButton } from '../../../button/src';

@Component({
selector: 'data-table-header-sortable',
imports: [HlmButton, NgIcon],
providers: [provideIcons({ phosphorArrowsDownUp })],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<button
hlmBtn
variant="ghost"
size="sm"
(click)="column().toggleSorting(column().getIsSorted() === 'asc')"
>
{{ label() }}
<ng-icon name="phosphorArrowsDownUp" data-icon="inline-end" />
</button>
`,
})
export class DataTableHeaderSortable<TData> {
public readonly label = input.required<string>();
public readonly column = input.required<Column<TData>>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Component, ChangeDetectionStrategy, input } from '@angular/core';
import { NgIcon } from '@ng-icons/core';

@Component({
selector: 'data-table-image-cell',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [NgIcon],
template: `
@if (imageUrl()) {
<img [src]="imageUrl()" width="40" height="40" />
} @else {
<ng-icon name="phosphorImage" size="40" />
}
`,
})
export class DataTableImageCell {
public readonly imageUrl = input<string>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Component, ChangeDetectionStrategy, input } from '@angular/core';
import { Table } from '@tanstack/angular-table';
import { HlmCheckbox } from '../../../checkbox/src';

@Component({
selector: 'data-table-select-all',
imports: [HlmCheckbox],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<hlm-checkbox
aria-label="Select all"
[checked]="table().getIsAllPageRowsSelected()"
[indeterminate]="table().getIsSomePageRowsSelected()"
(checkedChange)="table().toggleAllPageRowsSelected($event)"
/>
`,
})
export class DataTableSelectAll<TRecord> {
public readonly table = input.required<Table<TRecord>>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Component, ChangeDetectionStrategy, input } from '@angular/core';
import { Row } from '@tanstack/angular-table';
import { HlmCheckbox } from '../../../checkbox/src';

@Component({
selector: 'data-table-select-row',
imports: [HlmCheckbox],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<hlm-checkbox
aria-label="Select row"
[checked]="row().getIsSelected()"
(checkedChange)="row().toggleSelected($event)"
/>
`,
})
export class DataTableSelectRowCell<TRecord> {
public readonly row = input.required<Row<TRecord>>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Component, ChangeDetectionStrategy, input } from '@angular/core';

@Component({
selector: 'data-table-string-cell',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `<div [attr.class]="alignRight() ? 'text-right' : ''">{{ value() }}</div>`,
})
export class DataTableStringCell {
public readonly value = input.required<string>();
public readonly alignRight = input.required<boolean>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
@if (searchColumn() || hasColumnFilter()) {
<div hlmDataTableToolbar>
@if (searchColumn()) {
<div hlmInputGroup class="max-w-xs">
<div hlmInputGroupAddon>
<ng-icon name="phosphorMagnifyingGlass" />
<input
hlmInputGroupInput
[placeholder]="`Search ${searchColumn()}...`"
[value]="appFilter()"
(input)="setAppFilter($event)"
/>
</div>
</div>
} @if (hasColumnFilter()) {
<button hlmBtn variant="outline" class="ml-auto" [hlmDropdownMenuTrigger]="columnsMenu">
Columns
</button>
<ng-template #columnsMenu>
<hlm-dropdown-menu class="w-40">
@for (column of hideableColumns(); track column.id) {
<button
hlmDropdownMenuCheckbox
class="capitalize"
[checked]="column.getIsVisible()"
(triggered)="column.toggleVisibility(!column.getIsVisible())"
>
{{ column.id }}
<hlm-dropdown-menu-checkbox-indicator />
</button>
}
</hlm-dropdown-menu>
</ng-template>
}
</div>
}
<hlm-data-table-content [table]="table" [columns]="tanstackColumns" />
<hlm-data-table-pagination [table]="table" />
Loading
Loading