npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

csg-table-2

v4.1.0

Published

This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 17

Downloads

102

Readme

CsgTableLibrary

This library was generated with Angular CLI version 15.0.0. This library is an implementation of the Angular material mat-table with additional functionalities to keep your code dry and clean. The library uses the dynamic component injection, to inject any components instances to the table columns,The table and the component instances are linked together to manipulate all the operations needed for the data.

Advantages

  • Keep your code clean and dry , no need to repeat the mat-table HTML code.
  • The dynamic component injection will divide your code to clean blocks easy to track and manipulate.
  • Csg table provide the loading skeleton animation when loading the data.
  • Csg table provide i18 translations, you can provide enums , translations keys to the columns configurations to render the correct record attribute value.
  • Csg table can be connected to all types of data by using the getAllDataObs or paginationObservable functions that injected to the library as inputs, by using these inputs you can inject arrays of objects or a http observable.
  • Csg table provides the functionalities to pass a filtrationModel or a paginationModel with the retrieve data Observable function (getAllDataObs or paginationObservable).
  • The filtrationModel and paginationModel are passed by reference ,so that allows the library to manage the inputs in a dynamic way.
  • Csg table provides all the functionalities to manipulate the paginationObservable and getAllDataObs.

Requirements

  • Downloading Angular material. https://www.npmjs.com/package/@angular/material
  • Imports Angular materials styles and themes to your application :
    • Be sure that you are adding your general styles.scss to the styles list in Angular.json.
    • In your styles.scss add : @import "~@angular/material/prebuilt-themes/indigo-pink.css"; // Choose a theme.
    • In your application Angular.json styles array add : "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css".
  • Downloading Angular material icons. https://www.npmjs.com/package/material-icons
    • In your styles.scss add : @import '~material-icons/iconfont/material-icons.css'.
    • In your application Angular.json styles array add : "node_modules/material-icons/iconfont/material-icons.css".
  • Download and integrate ngx-translate package :
    • npm i @ngx-translate/[email protected]
    • npm i @ngx-translate/[email protected]
    • Add i18n directory to your assets and create the translation json file (ex:en-gb.json)
    • Import TranslateModule in your app module and use HttpLoaderFactory to load the translations from the files.
       export function HttpLoaderFactory(http: HttpClient) {
         return new TranslateHttpLoader(http);
       }
          
       @NgModule({
         declarations: [
           AppComponent,
         ],
         imports: [
          .
          .
          .
           BrowserAnimationsModule, ... required for Angualr material
           HttpClientModule,
           TranslateModule.forRoot({
             loader: {
               provide: TranslateLoader,
               useFactory: HttpLoaderFactory,
               deps: [HttpClient],
             },
           }),
          ]
    • In your AppComponent constructor :
       constructor(private translateService: TranslateService) {
             translateService.use('en-gb'); // 'en-gb' is your translation json file name in the assets/i18n
       }

csg-table basic inputs and outputs

  • uniqueKeyOperation : The unique attribute of the src object that the table CRUD operations will be based on.
  • columns : The csg-table columns configurations of type CsgTableConfiguration[] .
  • renderedColumns : The array of the columns names wanted to view of type string[].
  • skeletonRows : The number of skeleton rows to show in the table loading skeleton when fetching the data.
  • rowSelection : Set to false will remove the cursor pointer and not calling the row selection event.
  • animatedRows : Set to false will remove the row transition hovering effect , you can update the row hover background using --csg-rows-bgr.
  • rowSelectedEmitter : Subscribe to get the selected row event of type CsgSelectedRowEvent<T>.
  • localPaginating : Set to true if a local pagination is needed next to getAllDataObs.
  • pageSize: Selected(initial) page size (default 25) , can be activated locally if the localPaginating is set to true .
  • paginatedPageSizes:The allowed pagination page sizes options(default options [15, 25, 50, 100, 200]), can be activated locally if the localPaginating is set to true .

csg-table inputs for pass all data approach

  • getAllDataObs : Function returns an Observable to load all the data form the data source , could be a predefined list in the observable result or could be a http request observable.
  • filtrationModel :
    • This model was developed to hold the params keys needed for the filtration process,add the filtration keys to this model receive them in the filtration service function called by the observable function.
    • Create a new interface with the required keys(attributes) that extend this model ,and pass it to the getAllDataObs function.
    • The csg-table will not update this model, this model should be manipulated form the parent component, the new filtered data can be loaded by calling reloadCurrentView method.
  • filterKeys : The attributes to filter the table records (locally).
  • filterString: A subscriber get the filter substring to filter the records locally , the filtration will be based on the filterKeys inputs.
  /**
  * filtrationModel defention
  */
  export interface UsersFiltrationModel extends SgTableDataFiltrationModel{
   //add all proparites needed for filtration , ex:name param
   name:string
  }
  
  /**
  * filtrationModel initialization. in ngOnInit().
  * Note : you can connect this object to your filtration form , when any form value updated , update your FiltrationModel and 
  * call the reloadCurrentView function in the csgTable view child referance
  */
  this.usersFiltrationModel : UsersFiltrationModel ={
      . 
      .
      name:'',
  }

  /**
  * getAllUsersObs defention
  */
  getAllUsersObs: (filtrationModel:UsersFiltrationModel) => Observable<User[]> = (filtrationModel): Observable<User[]> => {
    return this.usersService.getUsers(filtrationModel);
  };
  
  /**
  * service calls http request with your server url . ex:get users API with http params
  */
   getUsers(filtrationModel: UsersFiltrationModel): Observable<User[]> {
    let params= new HttpParams().append('filterKey',filtrationModel.name)
    return this.http.get<User[]>(this.baseUrl,{params:params})
  }
  
  /**
  * csg-table tags input 
  */
   <csg-table
       #csgTable
       .
       .
       .
       [filtrationModel]="usersFiltrationModel"
       [getAllDataObs]="getAllUsersObs">
   </csg-table>
  

csg-table inputs for pass paginated data approach

  • paginationObservable :
    • Function returns an Observable, use this function (with a SgTablePaginationModel as parameter) to call the pagination API.The service function called by the observable function should manipulate the model keys in the required form for the HTTP request.
    • In pagination process the csg-table will only change the pagination properties keys values of the pagination model.
    • When the other params updated form the parent page call the reloadCurrentView method to pull the new pagination results from the server.
  • paginationModel :
    • This model was developed to hold the pagination params needed for the server pagination and filtration, this model will be received in the pagination service function called by the observable function.
    • Initially this model holds the basics pagination keys . if another keys needed , a new interface extends the SgTablePaginationModel should be created with the needed keys(attributes).
    • This component will update only the pagination properties of the pagination model, the other properties should be manipulated form the parent component,the new paginated data can be loaded by calling reloadCurrentView method.
  /**
  * pagiantionModel defention , CsgTablePaginationModel type has alrady all the properties needed for pagaintion. pageSize , pageIndex ,sortFiled , ascending.
  */
  export interface UsersPagiantionModel extends CsgTablePaginationModel{
   //add all proparites needed for the pagiantion or filtration , ex:name param for filtration
   name:string
  }
  
  /**
  * pagiantionModel initialization. in ngOnInit().
  * defiend page size should be included in the paginatedPageSizes array 
  * Note : you can connect this object to your pagaintion-filtration form , when any form value updated , update your PagiantionModel and 
  * call the reloadCurrentView function in the csgTable view child referance
  */
  this.usersPaginationModel : UsersPagiantionModel ={
      pageSize:25,
      pageIndex:0,
      name:''
  }

  /**
  * getPaginatedUsersObs defention
  */
  getPaginatedUsersObs: (paginationModel:UsersPagiantionModel) => Observable<CsgTablePaginationResult<User>> = (paginationModel): Observable<CsgTablePaginationResult<User>> => {
    return this.usersService.getPagiantedUsers(pagiantionModel);
  };
  
  /**
  * service calls http request with your server url . ex:get pagented users API  withhttp params.
  * if the retrved data type is not CsgTablePaginationResult<User> use exjs map operator to reconstrcut your response
  */
   getPagiantedUsers(pagiantionModel: UsersPagiantionModel): Observable<CsgTablePaginationResult<User>> {
    let params= new HttpParams()
                .append('filterKey',filtrationModel.name)
                .append('pageSize',filtrationModel.pageSize)
                .append('pageIndex',filtrationModel.pageIndex)
    return this.http.get<CsgTablePaginationResult<User>>(this.baseUrl,{params:params})
  }
  
  /**
  * csg-table tags input 
  */
   <csg-table
       #csgTable
       .
       .
       .
       [pageSize]="25"
       [paginationModel]="usersPaginationModel"
       [paginationObservable]="getPaginatedUsersObs">
   </csg-table>
  

Injected components properties

Csg table provides case-sensitive inputs and outputs to be added in the injected components classes(if needed) for data manipulation :

  • @Input('dataSrc') dataSrc ; the table record object
  • @Input('recordIndex') recordIndex ; the table record row index
  • @Input('rowClickedEmitter') rowClickedEmitter = new EventEmitter<CsgTableUpdateEvent<T>>(); subscribe this event emitter to get the row clicking event.
  • @Output('deleteRecord') deleteRecord = new EventEmitter<string | number>(); an event emitter that emit an event with the component dataSrc uniqueKeyOperation (you add in the csg-table tag inputs csg-table tag) to the table to delete the record
  • @Output('updateRecord') updateRecord = new EventEmitter<CsgTableUpdateEvent<T>>(); an event emitter to emit update record event to update the table record , the table use the uniqueKeyOperation to find the record to update it . CsgTableUpdateEvent is used to provide the re-inject cell option(rerender the injected component after updating).

Injected components requirements

  • Be sure to set the uniqueKeyOperation in the csg-table tag inputs(default value is id), should be unique for all objects records.
  • All the csg-table events developed to accept generic types <T> , add your type when defining the event, ex:CsgTableUpdateEvent<User> event type is used to update a user record of type User in a users table constructed from a User[].

More documentation

You will find more documentations for all the package inputs,outputs and event in the library files.

Usage

The next example represents the csg-table basics implementation with some styling options. The example csg-table is used to preview some dummy sites list.

  • Create the csg-table columns configurations in your component.ts , the configurations you need to define :
    • Rendered columns.
    • Table columns.
    • Retrieve data observable : getAllSitesObs or paginationObservable.
    • Table filtering observable.
        export interface Site {
          id: string,
          name?: string;
          address?: string;
          numberOfFloors?: number;
          logo?: string;
          area?: number;
        }
          
        @Component({
          selector: 'your-component-selector',
          templateUrl: './your-component-selector.html',
          styleUrls: ['./your-component-selector.scss']
        }) 
        export class YourComponent implements OnInit,OnDestroy {
          @ViewChild('csgTable') csgTable: CsgTableComponent;
          .
          .
          .
          private filterObs$ = new Subject<string>();
          filterString = '';
          filterSubscription:Subscription=null;
          renderedColumns: string[] = [];
          tableColumns: CsgTableConfiguration[] = [];
          
          ngOnInit(): void {
            this.subscribeFiltering();
            this.defineSgTableConfig();
          }
          
          /**
           * This function use to subscribe input search field
           * @private
           */
          private subscribeFiltering() {
            this.filterSubscription = this.filterObs$.pipe(distinctUntilChanged(), debounceTime(200)).subscribe((key: string) => {
              this.filterString = key;
            });
          }
          
          filterSites(event: string) {
            this.filterObs$.next(event)
          }
           
          private defineSgTableConfig() {
            this.tableColumns = [
              {
                name: 'Logo',
                width: '10%',
                injectedComponent: SitesLogoCellComponent,
              },
              {
                name: 'Site',
                width: '20%',
                attributeKey: 'name'
              },
              {
                name: 'Address',
                width: '30%',
                attributeKey: 'address'
              },
              {
                name: 'Floors',
                width: '15%',
                attributeKey: 'numberOfFloors',
              },
              {
                name: 'Area',
                width: '15%',
                attributeKey: 'area',
              },
              {
                name: 'actions',
                hideColumnName: true,
                width: '10%',
                actionsComponent: true,
                injectedComponent: DeleteSiteCellComponent,
                headerIcons: [
                  {
                    icon: 'add',
                    action: this.addNewSite,
                    colorHoverTransition:true,
                    toolTip: 'Create site',
                    leftMargin: '41%'
                  }
                ]
              }
            ];
                
            this.renderedColumns = [
              'Logo',
              'Site',
              'Address',
              'Floors',
              'Area',
              'actions'
            ];
          }
              
           private addNewSite = () => {
            let newSite:Site={
              id: 'd5a67eae-835a-483e-9e66-bf93d3449465',
              name: 'new site',
              address: 'new site Adreess',
              numberOfFloors: 15,
              logo: 'new site logo url',
              area: 2500,
            }
            this.csgTable.addNewRecord(newSite)
          }
              
          /**
          * yourDummyData is a list of Site objects
          */
          getAllSitesObs: CsgTableTypes.GetAllDataObs<Site> = (): Observable<Site[]> => {
            return of(yourDummyData)
          };
          
           ngOnDestroy(): void {
             this.filterSubscription.unsubscribe();
           }
        }     
  • In your-component-selector.html :
      <div class="wrapper">
        <mat-form-field appearance="outline">
          <mat-label>{{'FORMS.SEARCH' | translate | capitalize}}</mat-label>
          <input matInput class="name-input" #searchInput
                 (keyup)="filterSites(searchInput.value)"
                 [placeholder]="'FORMS.SEARCH' | translate">
        </mat-form-field>
        <div class="table-container" *ngIf="renderedColumns.length>0">
      
          <csg-table #csgTable
                    [filterKey]="['name']"
                    [uniqueKeyOperation]="'id'"
                    [columns]="tableColumns"
                    [animatedRows]="false"
                    [renderedColumns]="renderedColumns"
                    [filterString]="filterString"
                    [getAllDataObs]="getAllSitesObs">
          </csg-table>
        </div>
      </div>
  • In your-component-selector.scss file :
       .wrapper {
         width: 100%;
         height: 100%;
         display: flex;
         flex-direction: column;
         padding:20px;
         
         mat-form-field {
           height: 80px;
           width: min(30%, 400px);
         }
    
         .table-container {
           height: calc(100% - 80px);
           width: 100%;
           csg-table {
             width: 100%;
             height: 100%;
           }
         }
       }
    
  • Define columns component instances SitesLogoCellComponent and DeleteSiteCellComponent
    • SitesLogoCellComponent :
      @Component({
        selector: 'gmao-sites-logo-cell',
        template: `<img alt="logo" src="{{dataSrc.logo}}" class="site-logo">
                   <style>
                     .site-logo {
                        width: 100px;
                        height: 100px;
                        padding: 5px 0;
                     }
                   </style>
                   `
      })
      export class SitesLogoCellComponent implements OnInit {
        @Input('dataSrc') dataSrc: Site;
        constructor() {}
            
        ngOnInit(): void {}
      }
    • DeleteSiteCellComponent
      • In DeleteSiteCellComponent component
        @Component({
          selector: 'delete-site-cell',
          styleUrls: ['./delete-site-cell.component.scss'],
          template: `
                    <div class="delete-cell">
                         <button matRipple (click)="deleteSite()" [matTooltip]="'FORMS.DELETE' | translate">
                              <mat-icon>delete</mat-icon>
                         </button>
                    </div>
                   `
        })
        export class DeleteSiteCellComponent implements OnInit {
          @Input('dataSrc') dataSrc: Site;
          @Output('deleteRecord') deleteRecord = new EventEmitter<string | number>();
        
          constructor() {}
        
          ngOnInit(): void {}
              
            
          /**
          * emit the uniqueKeyOperation(should be passed to the csg-table input) of the record to delete it 
          * from the table
          */
          deleteSite(){
            this.deleteRecord.emit(this.dataSrc.id);
          }
        }
      • In delete-site-cell.component.scss file
           .delete-cell {
              width: 100%;
              height: 100%;
              display: flex;
              align-items: center;
              justify-content: center;      
           
              button {
                background: transparent !important;
                height: 100%;      
                mat-icon {
                  color: red;
                  transition: 180ms ease-in-out;
                  user-select: none;      
                  &:hover {
                    color: var(--background-primary);
                  }
                }
              }
           }

Fonts inheritance

To let the csg-table inherits your font family , you need to add the font-family: inherit in your styles.scss :host

:host {
  font-family: inherit;
}

SCSS variables

Csg table using a SCSS variables to give some animated styles to the components , these variables should be defined in your styles.scss :root

 :root {
   /* Header icons hovering effect color , mainly its your primary theme color*/
   --background-primary: orange;
   
   /* Table rows hover background color */
   --csg-rows-bgr: rgb(238, 238, 238);
 }

Component styles

Csg table is based on the mat-table , the table styles can be updated by removing the styles encapsulation in the parent component by setting the encapsulation value to None , and add the style you need to overwrite in the parent scss file :

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
  encapsulation:ViewEncapsulation.None,
 
 })

More resources and examples

A git repository will be provided soon for all the csg-table implementations