Table component WIP
This commit is contained in:
parent
1afafe0e28
commit
1b7104ba14
18
src/assets/icons/arrow-down-o.svg
Normal file
18
src/assets/icons/arrow-down-o.svg
Normal file
@ -0,0 +1,18 @@
|
||||
<svg
|
||||
fill="none"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M14.8285 12.0259L16.2427 13.4402L12 17.6828L7.7574 13.4402L9.17161 12.0259L11 13.8544V6.31724H13V13.8544L14.8285 12.0259Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
clip-rule="evenodd"
|
||||
d="M19.7782 19.7782C15.4824 24.0739 8.51759 24.0739 4.22183 19.7782C-0.0739417 15.4824 -0.0739417 8.51759 4.22183 4.22183C8.51759 -0.0739419 15.4824 -0.0739419 19.7782 4.22183C24.0739 8.51759 24.0739 15.4824 19.7782 19.7782ZM18.364 18.364C14.8492 21.8787 9.15076 21.8787 5.63604 18.364C2.12132 14.8492 2.12132 9.15076 5.63604 5.63604C9.15076 2.12132 14.8492 2.12132 18.364 5.63604C21.8787 9.15076 21.8787 14.8492 18.364 18.364Z"
|
||||
fill="currentColor"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 841 B |
@ -14,6 +14,7 @@ import { IqserInputsModule } from './inputs';
|
||||
import { IqserHelpModeModule } from './help-mode';
|
||||
import { IqserIconsModule } from './icons';
|
||||
import { IqserButtonsModule } from './buttons';
|
||||
import { IqserScrollbarModule } from './scrollbar';
|
||||
|
||||
const matModules = [MatIconModule, MatProgressSpinnerModule];
|
||||
const modules = [
|
||||
@ -23,7 +24,8 @@ const modules = [
|
||||
IqserListingModule,
|
||||
IqserFiltersModule,
|
||||
IqserInputsModule,
|
||||
IqserHelpModeModule
|
||||
IqserHelpModeModule,
|
||||
IqserScrollbarModule
|
||||
];
|
||||
const components = [StatusBarComponent, FullPageLoadingIndicatorComponent, FullPageErrorComponent];
|
||||
const pipes = [SortByPipe, HumanizePipe];
|
||||
|
||||
@ -12,6 +12,7 @@ export class IqserIconsModule {
|
||||
constructor(private readonly _iconRegistry: MatIconRegistry, private readonly _sanitizer: DomSanitizer) {
|
||||
const icons: Set<string> = new Set([
|
||||
'arrow-down',
|
||||
'arrow-down-o',
|
||||
'check',
|
||||
'close',
|
||||
'edit',
|
||||
|
||||
@ -1,8 +1,12 @@
|
||||
export * from './tables';
|
||||
export * from './workflow-listing';
|
||||
export * from './models';
|
||||
export * from './services';
|
||||
|
||||
export * from './scroll-button/scroll-button.component';
|
||||
export * from './table/table.component';
|
||||
export * from './table-column-name/table-column-name.component';
|
||||
export * from './table-header/table-header.component';
|
||||
|
||||
export * from './sync-width.directive';
|
||||
|
||||
export * from './listing.module';
|
||||
export * from './entities.service';
|
||||
export * from './listing-component.directive';
|
||||
export * from './table-header/table-header.component';
|
||||
export * from './models/listable';
|
||||
|
||||
@ -5,9 +5,8 @@ import { FilterService } from '../filtering';
|
||||
import { SortingOrders, SortingService } from '../sorting';
|
||||
import { AutoUnsubscribe, Bind, KeysOf } from '../utils';
|
||||
import { SearchService } from '../search';
|
||||
import { TableColumnConfig } from './tables';
|
||||
import { EntitiesService } from './entities.service';
|
||||
import { Listable } from './models/listable';
|
||||
import { EntitiesService } from './services';
|
||||
import { Listable, TableColumnConfig } from './models';
|
||||
|
||||
export const DefaultListingServices = [FilterService, SearchService, EntitiesService, SortingService] as const;
|
||||
|
||||
|
||||
@ -1,18 +1,35 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { TablesModule } from './tables';
|
||||
import { TableHeaderComponent } from './table-header/table-header.component';
|
||||
import { WorkflowListingModule } from './workflow-listing';
|
||||
import { IqserFiltersModule } from '../filtering';
|
||||
import { IqserInputsModule } from '../inputs';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { TableColumnNameComponent } from './table-column-name/table-column-name.component';
|
||||
import { ScrollButtonComponent } from './scroll-button/scroll-button.component';
|
||||
import { TableComponent } from './table/table.component';
|
||||
import { SyncWidthDirective } from './sync-width.directive';
|
||||
import { ScrollingModule } from '@angular/cdk/scrolling';
|
||||
import { IqserIconsModule } from '../icons';
|
||||
import { IqserScrollbarModule } from '../scrollbar';
|
||||
import { RouterModule } from '@angular/router';
|
||||
|
||||
const components = [TableHeaderComponent];
|
||||
const modules = [TranslateModule, TablesModule, WorkflowListingModule, IqserFiltersModule, IqserInputsModule];
|
||||
const matModules = [MatTooltipModule];
|
||||
const components = [TableHeaderComponent, TableComponent, TableColumnNameComponent, ScrollButtonComponent];
|
||||
const modules = [
|
||||
TranslateModule,
|
||||
IqserFiltersModule,
|
||||
IqserInputsModule,
|
||||
IqserIconsModule,
|
||||
IqserScrollbarModule,
|
||||
ScrollingModule,
|
||||
RouterModule
|
||||
];
|
||||
const utils = [SyncWidthDirective];
|
||||
|
||||
@NgModule({
|
||||
declarations: [...components],
|
||||
exports: [...components, TablesModule, WorkflowListingModule],
|
||||
imports: [CommonModule, ...modules]
|
||||
declarations: [...components, ...utils],
|
||||
exports: [...components, ...utils],
|
||||
imports: [CommonModule, ...modules, ...matModules]
|
||||
})
|
||||
export class IqserListingModule {}
|
||||
|
||||
2
src/lib/listing/models/index.ts
Normal file
2
src/lib/listing/models/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './listable';
|
||||
export * from './table-column-config.model';
|
||||
@ -1,4 +1,5 @@
|
||||
import { KeysOf } from '../../../utils';
|
||||
import { KeysOf } from '../../utils';
|
||||
import { TemplateRef } from '@angular/core';
|
||||
|
||||
export interface TableColumnConfig<T> {
|
||||
readonly label: string;
|
||||
@ -8,4 +9,6 @@ export interface TableColumnConfig<T> {
|
||||
readonly rightIcon?: string;
|
||||
readonly rightIconTooltip?: string;
|
||||
readonly notTranslatable?: boolean;
|
||||
readonly width?: string; // TODO: make required
|
||||
readonly template?: TemplateRef<unknown>; // TODO: make required
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
<button (click)="scroll(buttonType.top)" [hidden]="(showScrollUp$ | async) === false" class="scroll-button top pointer">
|
||||
<mat-icon svgIcon="iqser:arrow-down-o"></mat-icon>
|
||||
</button>
|
||||
|
||||
<button (click)="scroll(buttonType.bottom)" [hidden]="(showScrollDown$ | async) === false" class="scroll-button bottom pointer">
|
||||
<mat-icon svgIcon="iqser:arrow-down-o"></mat-icon>
|
||||
</button>
|
||||
30
src/lib/listing/scroll-button/scroll-button.component.scss
Normal file
30
src/lib/listing/scroll-button/scroll-button.component.scss
Normal file
@ -0,0 +1,30 @@
|
||||
@import '../../../assets/styles/common';
|
||||
|
||||
.scroll-button {
|
||||
background-color: $white;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
height: 40px;
|
||||
width: 44px;
|
||||
border: none;
|
||||
border-radius: 8px 0 0 8px;
|
||||
box-shadow: -1px 1px 5px 0 rgba(40, 50, 65, 0.25);
|
||||
|
||||
&.bottom {
|
||||
bottom: 30px;
|
||||
}
|
||||
|
||||
&.top {
|
||||
top: 100px;
|
||||
|
||||
mat-icon {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mat-icon {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
color: $grey-7;
|
||||
}
|
||||
61
src/lib/listing/scroll-button/scroll-button.component.ts
Normal file
61
src/lib/listing/scroll-button/scroll-button.component.ts
Normal file
@ -0,0 +1,61 @@
|
||||
import { ChangeDetectionStrategy, Component, HostListener, Input, OnInit } from '@angular/core';
|
||||
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
|
||||
import { concatMap, delay, distinctUntilChanged, map, startWith } from 'rxjs/operators';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { Required } from '../../utils';
|
||||
|
||||
const ButtonTypes = {
|
||||
top: 'top',
|
||||
bottom: 'bottom'
|
||||
} as const;
|
||||
|
||||
type ButtonType = keyof typeof ButtonTypes;
|
||||
|
||||
@Component({
|
||||
selector: 'iqser-scroll-button',
|
||||
templateUrl: './scroll-button.component.html',
|
||||
styleUrls: ['./scroll-button.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ScrollButtonComponent implements OnInit {
|
||||
readonly buttonType = ButtonTypes;
|
||||
|
||||
@Input() @Required() scrollViewport!: CdkVirtualScrollViewport;
|
||||
@Input() @Required() itemSize!: number;
|
||||
|
||||
showScrollUp$?: Observable<boolean>;
|
||||
showScrollDown$?: Observable<boolean>;
|
||||
|
||||
ngOnInit(): void {
|
||||
const scrollSize = () => this.scrollViewport.getDataLength() * this.itemSize;
|
||||
const scrollIsNeeded = () => this.scrollViewport.getViewportSize() < scrollSize();
|
||||
const reachedEnd = (type: ButtonType) => this.scrollViewport.measureScrollOffset(type) === 0;
|
||||
|
||||
const showScrollUp = () => scrollIsNeeded() && !reachedEnd(ButtonTypes.top);
|
||||
const showScrollDown = () => scrollIsNeeded() && !reachedEnd(ButtonTypes.bottom);
|
||||
|
||||
const scroll$ = this.scrollViewport.elementScrolled().pipe(
|
||||
startWith(''),
|
||||
/** Delay first value so that we can wait for items to be rendered in viewport and get correct values */
|
||||
concatMap((value, index) => (index === 0 ? of(value).pipe(delay(0)) : of(value)))
|
||||
);
|
||||
this.showScrollUp$ = scroll$.pipe(map(showScrollUp), distinctUntilChanged());
|
||||
this.showScrollDown$ = scroll$.pipe(map(showScrollDown), distinctUntilChanged());
|
||||
}
|
||||
|
||||
scroll(type: ButtonType): void {
|
||||
const viewportSize = (this.scrollViewport?.getViewportSize() - this.itemSize) * (type === ButtonTypes.top ? -1 : 1);
|
||||
const scrollOffset = this.scrollViewport?.measureScrollOffset('top');
|
||||
this.scrollViewport?.scrollToOffset(scrollOffset + viewportSize, 'smooth');
|
||||
}
|
||||
|
||||
@HostListener('document:keyup', ['$event'])
|
||||
spaceAndPageDownScroll(event: KeyboardEvent): void {
|
||||
const target = event.target as EventTarget & { tagName: string };
|
||||
if (['Space', 'PageDown'].includes(event.code) && target.tagName === 'BODY') {
|
||||
this.scroll(ButtonTypes.bottom);
|
||||
} else if (['PageUp'].includes(event.code) && target.tagName === 'BODY') {
|
||||
this.scroll(ButtonTypes.top);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,9 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { BehaviorSubject, combineLatest, Observable, pipe } from 'rxjs';
|
||||
import { distinctUntilChanged, map, tap } from 'rxjs/operators';
|
||||
import { FilterService, getFilteredEntities } from '../filtering';
|
||||
import { SearchService } from '../search';
|
||||
import { Listable } from './models/listable';
|
||||
import { FilterService, getFilteredEntities } from '../../filtering';
|
||||
import { SearchService } from '../../search';
|
||||
import { Listable } from '../models';
|
||||
|
||||
const toLengthValue = (entities: unknown[]) => entities?.length ?? 0;
|
||||
const getLength = pipe(map(toLengthValue), distinctUntilChanged());
|
||||
1
src/lib/listing/services/index.ts
Normal file
1
src/lib/listing/services/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './entities.service';
|
||||
@ -1,4 +1,4 @@
|
||||
@import '../../../../assets/styles/common';
|
||||
@import '../../../assets/styles/common';
|
||||
|
||||
:host {
|
||||
display: flex;
|
||||
@ -1,6 +1,6 @@
|
||||
import { ChangeDetectionStrategy, Component, Input, Optional } from '@angular/core';
|
||||
import { SortingOrders, SortingService } from '../../../sorting';
|
||||
import { KeysOf, Required } from '../../../utils';
|
||||
import { SortingOrders, SortingService } from '../../sorting';
|
||||
import { KeysOf, Required } from '../../utils';
|
||||
|
||||
const ifHasRightIcon = <T>(thisArg: TableColumnNameComponent<T>) => !!thisArg.rightIcon;
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
import { ChangeDetectionStrategy, Component, Input, TemplateRef } from '@angular/core';
|
||||
import { Required } from '../../utils';
|
||||
import { FilterService } from '../../filtering';
|
||||
import { EntitiesService } from '../entities.service';
|
||||
import { Listable } from '../models/listable';
|
||||
import { TableColumnConfig } from '../tables';
|
||||
import { EntitiesService } from '../services';
|
||||
import { Listable, TableColumnConfig } from '../models';
|
||||
|
||||
export const ListingModes = {
|
||||
list: 'list',
|
||||
|
||||
52
src/lib/listing/table/table.component.html
Normal file
52
src/lib/listing/table/table.component.html
Normal file
@ -0,0 +1,52 @@
|
||||
<iqser-table-header
|
||||
[bulkActions]="bulkActions"
|
||||
[hasEmptyColumn]="!!emptyColumnWidth"
|
||||
[selectionEnabled]="selectionEnabled"
|
||||
[tableColumnConfigs]="tableColumnConfigs"
|
||||
[tableHeaderLabel]="tableHeaderLabel"
|
||||
></iqser-table-header>
|
||||
|
||||
<!--TODO: Empty states-->
|
||||
<!--<redaction-empty-state-->
|
||||
<!-- (action)="openAddEditDictionaryDialog()"-->
|
||||
<!-- *ngIf="entitiesService.noData$ | async"-->
|
||||
<!-- [buttonLabel]="'dictionary-listing.no-data.action' | translate"-->
|
||||
<!-- [showButton]="currentUser.isAdmin"-->
|
||||
<!-- [text]="'dictionary-listing.no-data.title' | translate"-->
|
||||
<!-- icon="red:dictionary"-->
|
||||
<!--></redaction-empty-state>-->
|
||||
|
||||
<!--<redaction-empty-state-->
|
||||
<!-- *ngIf="noMatch$ | async"-->
|
||||
<!-- [text]="'dictionary-listing.no-match.title' | translate"-->
|
||||
<!--></redaction-empty-state>-->
|
||||
|
||||
<cdk-virtual-scroll-viewport #scrollViewport [itemSize]="itemSize" iqserHasScrollbar>
|
||||
<div
|
||||
*cdkVirtualFor="let entity of listingComponent.sortedDisplayedEntities$ | async; trackBy: listingComponent.trackByPrimaryKey"
|
||||
[class.pointer]="!!routerLinkFn"
|
||||
[routerLink]="routerLinkFn(entity)"
|
||||
class="table-item"
|
||||
>
|
||||
<div (click)="listingComponent.toggleEntitySelected($event, entity)" *ngIf="selectionEnabled" class="selection-column">
|
||||
<iqser-round-checkbox [active]="listingComponent.isSelected(entity)"></iqser-round-checkbox>
|
||||
</div>
|
||||
|
||||
<ng-container *ngFor="let column of tableColumnConfigs">
|
||||
<ng-container *ngIf="!!column.template">
|
||||
<ng-container *ngTemplateOutlet="column.template; context: { entity: entity }"></ng-container>
|
||||
</ng-container>
|
||||
|
||||
<!-- TODO: Remove-->
|
||||
<div *ngIf="!column.template">n</div>
|
||||
</ng-container>
|
||||
|
||||
<div class="actions-container">
|
||||
<ng-container *ngTemplateOutlet="actionsTemplate; context: { entity: entity }"></ng-container>
|
||||
</div>
|
||||
|
||||
<div class="scrollbar-placeholder"></div>
|
||||
</div>
|
||||
</cdk-virtual-scroll-viewport>
|
||||
|
||||
<iqser-scroll-button *ngIf="hasScrollButton" [itemSize]="itemSize" [scrollViewport]="scrollViewport"></iqser-scroll-button>
|
||||
121
src/lib/listing/table/table.component.scss
Normal file
121
src/lib/listing/table/table.component.scss
Normal file
@ -0,0 +1,121 @@
|
||||
//iqser-table-header::ng-deep .header-item {
|
||||
// padding-right: 16px;
|
||||
//}
|
||||
@import '../../../assets/styles/common';
|
||||
|
||||
cdk-virtual-scroll-viewport {
|
||||
height: calc(100vh - 50px - 31px - 111px);
|
||||
overflow-y: hidden !important;
|
||||
|
||||
&.has-scrollbar:hover ::ng-deep.cdk-virtual-scroll-content-wrapper {
|
||||
grid-template-columns: var(--gridTemplateColumnsHover);
|
||||
}
|
||||
|
||||
::ng-deep.cdk-virtual-scroll-content-wrapper {
|
||||
grid-template-columns: var(--gridTemplateColumns);
|
||||
display: grid;
|
||||
|
||||
.table-item {
|
||||
display: contents;
|
||||
|
||||
> div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
border-bottom: 1px solid $separator;
|
||||
height: var(--itemSize);
|
||||
padding: 0 24px 0 var(--paddingLeft);
|
||||
|
||||
&:not(.scrollbar-placeholder):not(.selection-column) {
|
||||
min-width: 110px;
|
||||
}
|
||||
|
||||
&.selection-column {
|
||||
padding-right: 0 !important;
|
||||
|
||||
iqser-round-checkbox .wrapper {
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
|
||||
&.active {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.table-item-title {
|
||||
font-weight: 600;
|
||||
@include line-clamp(1);
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
position: absolute;
|
||||
display: none;
|
||||
right: -11px;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
width: fit-content;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding-left: 100px;
|
||||
padding-right: 24px;
|
||||
z-index: 1;
|
||||
background: linear-gradient(to right, rgba(244, 245, 247, 0) 0%, $grey-2 35%);
|
||||
|
||||
mat-icon {
|
||||
width: 14px;
|
||||
}
|
||||
|
||||
iqser-circle-button:not(:last-child) {
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
&.active {
|
||||
display: flex;
|
||||
// compensate for scroll
|
||||
padding-right: 23px;
|
||||
}
|
||||
}
|
||||
|
||||
input,
|
||||
mat-select {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
> div {
|
||||
background-color: $grey-8;
|
||||
|
||||
&.selection-column iqser-round-checkbox .wrapper {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
overflow-y: auto !important;
|
||||
@include scroll-bar;
|
||||
|
||||
&.has-scrollbar {
|
||||
.table-item {
|
||||
.action-buttons {
|
||||
right: 0;
|
||||
padding-right: 13px;
|
||||
}
|
||||
|
||||
.scrollbar-placeholder {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
66
src/lib/listing/table/table.component.ts
Normal file
66
src/lib/listing/table/table.component.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import { ChangeDetectionStrategy, Component, forwardRef, Inject, Input, OnInit, TemplateRef, ViewChild } from '@angular/core';
|
||||
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
|
||||
import { Required } from '../../utils';
|
||||
import { Listable, TableColumnConfig } from '../models';
|
||||
import { ListingComponent } from '../listing-component.directive';
|
||||
|
||||
@Component({
|
||||
selector: 'iqser-table',
|
||||
templateUrl: './table.component.html',
|
||||
styleUrls: ['./table.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class TableComponent<T extends Listable> implements OnInit {
|
||||
@Input() bulkActions?: TemplateRef<unknown>;
|
||||
@Input() actionsTemplate?: TemplateRef<unknown>;
|
||||
@Input() @Required() itemSize!: number;
|
||||
@Input() @Required() tableColumnConfigs!: readonly TableColumnConfig<T>[];
|
||||
@Input() @Required() tableHeaderLabel!: string;
|
||||
@Input() selectionEnabled = false;
|
||||
@Input() hasScrollButton = false;
|
||||
@Input() emptyColumnWidth?: string;
|
||||
@Input() classes?: string;
|
||||
@Input() routerLinkFn?: (entity: T) => string | string[];
|
||||
@ViewChild(CdkVirtualScrollViewport, { static: true }) private readonly _viewport!: CdkVirtualScrollViewport;
|
||||
|
||||
constructor(@Inject(forwardRef(() => ListingComponent)) private _parent: ListingComponent<T>) {}
|
||||
|
||||
get listingComponent(): ListingComponent<T> {
|
||||
return this._parent;
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this._setStyles();
|
||||
}
|
||||
|
||||
private _setStyles(): void {
|
||||
const element = this._viewport.elementRef.nativeElement;
|
||||
this._setColumnsWidth(element);
|
||||
this._setItemSize(element);
|
||||
this._setPadding(element);
|
||||
}
|
||||
|
||||
private _setColumnsWidth(element: HTMLElement) {
|
||||
let gridTemplateColumnsHover = '';
|
||||
if (this.selectionEnabled) {
|
||||
gridTemplateColumnsHover += 'auto ';
|
||||
}
|
||||
for (const config of this.tableColumnConfigs) {
|
||||
gridTemplateColumnsHover += `${config.width as string} `; // TODO remove cast
|
||||
}
|
||||
gridTemplateColumnsHover += this.emptyColumnWidth;
|
||||
const gridTemplateColumns = gridTemplateColumnsHover + ' 11px';
|
||||
|
||||
element.style.setProperty('--gridTemplateColumns', gridTemplateColumns);
|
||||
element.style.setProperty('--gridTemplateColumnsHover', gridTemplateColumnsHover);
|
||||
}
|
||||
|
||||
private _setItemSize(element: HTMLElement) {
|
||||
element.style.setProperty('--itemSize', `${this.itemSize}px`);
|
||||
}
|
||||
|
||||
private _setPadding(element: HTMLElement) {
|
||||
const paddingLeft = this.selectionEnabled ? 10 : 24;
|
||||
element.style.setProperty('--paddingLeft', `${paddingLeft}px`);
|
||||
}
|
||||
}
|
||||
@ -1,4 +0,0 @@
|
||||
export * from './tables-module';
|
||||
export * from './table-column-name/table-column-name.component';
|
||||
export * from './models/table-column-config.model';
|
||||
export * from './sync-width.directive';
|
||||
@ -1,18 +0,0 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { TableColumnNameComponent } from './table-column-name/table-column-name.component';
|
||||
import { SyncWidthDirective } from './sync-width.directive';
|
||||
import { IqserIconsModule } from '../../icons';
|
||||
|
||||
const matModules = [MatTooltipModule];
|
||||
const components = [TableColumnNameComponent];
|
||||
const utils = [SyncWidthDirective];
|
||||
|
||||
@NgModule({
|
||||
declarations: [...components, ...utils],
|
||||
exports: [...components, ...utils],
|
||||
imports: [CommonModule, TranslateModule, IqserIconsModule, ...matModules]
|
||||
})
|
||||
export class TablesModule {}
|
||||
@ -1 +0,0 @@
|
||||
export * from './workflow-listing.module';
|
||||
@ -1,12 +0,0 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { DragDropModule } from '@angular/cdk/drag-drop';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
const matModules = [DragDropModule];
|
||||
|
||||
@NgModule({
|
||||
declarations: [],
|
||||
imports: [CommonModule, TranslateModule, ...matModules]
|
||||
})
|
||||
export class WorkflowListingModule {}
|
||||
27
src/lib/scrollbar/has-scrollbar.directive.ts
Normal file
27
src/lib/scrollbar/has-scrollbar.directive.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { AfterContentChecked, Directive, ElementRef, HostBinding } from '@angular/core';
|
||||
|
||||
@Directive({
|
||||
selector: '[iqserHasScrollbar]',
|
||||
exportAs: 'iqserHasScrollbar'
|
||||
})
|
||||
export class HasScrollbarDirective implements AfterContentChecked {
|
||||
@HostBinding('class') class = '';
|
||||
|
||||
constructor(private readonly _elementRef: ElementRef) {}
|
||||
|
||||
get hasScrollbar(): boolean {
|
||||
const element = this._elementRef?.nativeElement as HTMLElement;
|
||||
return element.clientHeight < element.scrollHeight;
|
||||
}
|
||||
|
||||
ngAfterContentChecked(): void {
|
||||
this._process();
|
||||
}
|
||||
|
||||
_process(): void {
|
||||
const newClass = this.hasScrollbar ? 'has-scrollbar' : '';
|
||||
if (this.class !== newClass) {
|
||||
this.class = newClass;
|
||||
}
|
||||
}
|
||||
}
|
||||
2
src/lib/scrollbar/index.ts
Normal file
2
src/lib/scrollbar/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from '../scrollbar/has-scrollbar.directive';
|
||||
export * from './scrollbar.module';
|
||||
13
src/lib/scrollbar/scrollbar.module.ts
Normal file
13
src/lib/scrollbar/scrollbar.module.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { HasScrollbarDirective } from './has-scrollbar.directive';
|
||||
|
||||
const utils = [HasScrollbarDirective];
|
||||
|
||||
@NgModule({
|
||||
declarations: [...utils],
|
||||
exports: [...utils],
|
||||
imports: [CommonModule],
|
||||
providers: [HasScrollbarDirective]
|
||||
})
|
||||
export class IqserScrollbarModule {}
|
||||
Loading…
x
Reference in New Issue
Block a user