Merge branch 'master' into workflow

This commit is contained in:
Adina Țeudan 2021-09-27 22:17:11 +03:00
commit ba4fbdabe6
40 changed files with 351 additions and 177 deletions

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M21 11l2-2c-3.73-3.73-8.87-5.15-13.7-4.31l2.58 2.58c3.3-.02 6.61 1.22 9.12 3.73zm-2 2c-1.08-1.08-2.36-1.85-3.72-2.33l3.02 3.02.7-.69zM9 17l3 3 3-3c-1.65-1.66-4.34-1.66-6 0zM3.41 1.64L2 3.05 5.05 6.1C3.59 6.83 2.22 7.79 1 9l2 2c1.23-1.23 2.65-2.16 4.17-2.78l2.24 2.24C7.79 10.89 6.27 11.74 5 13l2 2c1.35-1.35 3.11-2.04 4.89-2.06l7.08 7.08 1.41-1.41L3.41 1.64z"/></svg>

After

Width:  |  Height:  |  Size: 517 B

View File

@ -0,0 +1,3 @@
@function hexToRgb($color) {
@return #{red($color) + ', ' + green($color) + ', ' + blue($color)};
}

View File

@ -1,5 +1,3 @@
@import 'variables';
@mixin line-clamp($lines) {
display: -webkit-box;
-webkit-line-clamp: $lines;
@ -25,7 +23,7 @@
}
@mixin scroll-bar {
scrollbar-color: $grey-5 $grey-2;
scrollbar-color: var(--iqser-quick-filter-border) var(--iqser-grey-2);
scrollbar-width: thin;
&::-webkit-scrollbar {
@ -34,19 +32,19 @@
/* Track */
&::-webkit-scrollbar-track {
background: $grey-2;
background: var(--iqser-grey-2);
}
/* Handle */
&::-webkit-scrollbar-thumb {
background: $grey-5;
background: var(--iqser-quick-filter-border);
}
}
@mixin inset-shadow {
box-shadow: inset 0 4px 3px -2px $grey-4;
box-shadow: inset 0 4px 3px -2px var(--iqser-btn-bg-hover);
}
@mixin drop-shadow {
box-shadow: 0 4px 3px 2px $grey-4;
box-shadow: 0 4px 3px 2px var(--iqser-btn-bg-hover);
}

View File

@ -0,0 +1,31 @@
@use 'sass:meta';
:root {
--iqser-primary: lightblue;
--iqser-primary-rgb: 220, 230, 234;
--iqser-primary-2: orange;
--iqser-accent: blue;
--iqser-accent-rgb: 123, 234, 111;
--iqser-disabled: #9398a0;
--iqser-not-disabled-table-item: #f9fafb;
--iqser-btn-bg-hover: #e2e4e9;
--iqser-btn-bg: #f0f1f4;
--iqser-warn: #fdbd00;
--iqser-white: white;
--iqser-separator: rgba(226, 228, 233, 0.9);
--iqser-quick-filter-border: #d3d5da;
--iqser-grey-2: #f4f5f7;
--iqser-grey-3: #aaacb3;
--iqser-grey-4: #e2e4e9;
--iqser-grey-5: #d3d5da;
--iqser-grey-6: #f0f1f4;
--iqser-helpmode-primary: green;
}
@mixin configure($args...) {
:root {
@each $name, $value in meta.keywords($args) {
--#{$name}: #{$value};
}
}
}

View File

@ -1,10 +0,0 @@
// This rebel line is crying (in WebStorm) but it actually works
@import '../../../../../apps/red-ui/src/assets/styles/variables';
$btn-bg-hover: #e2e4e9 !default;
$btn-bg: #f0f1f4 !default;
$warn: #fdbd00 !default;
$white: white !default;
$separator: rgba(226, 228, 233, 0.9) !default;
$quick-filter-border: #d3d5da !default;
$filter-bg: #f4f5f7 !default;

View File

@ -1,5 +1,3 @@
@import 'variables';
.mat-button,
.mat-flat-button {
border-radius: 17px !important;
@ -36,19 +34,19 @@
padding: 0 14px;
transition: background-color 0.2s, color 0.2s;
background-color: $primary;
background-color: var(--iqser-primary);
&.mat-button-disabled {
background-color: $primary;
background-color: var(--iqser-primary);
.mat-button-wrapper {
color: $white;
color: var(--iqser-white);
opacity: 0.5;
}
}
&:not(.mat-button-disabled):hover {
background-color: $primary-2;
background-color: var(--iqser-primary-2);
}
}
@ -64,30 +62,30 @@ iqser-circle-button {
transition: background-color 0.2s;
&.overlay {
background: rgba($primary, 0.1);
background: rgba(var(--iqser-primary-rgb), 0.1);
}
&:not(.overlay):hover {
background-color: $btn-bg;
background-color: var(--iqser-btn-bg);
}
&.primary {
font-weight: 500 !important;
background-color: $primary;
color: $white;
background-color: var(--iqser-primary);
color: var(--iqser-white);
&:hover {
background-color: $primary-2;
background-color: var(--iqser-primary-2);
}
}
&.dark-bg:hover {
background-color: $btn-bg-hover;
background-color: var(--iqser-btn-bg-hover);
}
}
.dot {
background: $primary;
background: var(--iqser-primary);
height: 10px;
width: 10px;
border-radius: 50%;
@ -102,10 +100,10 @@ iqser-circle-button,
iqser-icon-button {
&[aria-expanded='true'] {
button {
background: rgba($primary, 0.1);
background: rgba(var(--iqser-primary-rgb), 0.1);
&.primary {
background: $primary-2;
background: var(--iqser-primary-2);
}
}
}

View File

@ -1,7 +1,5 @@
@import 'apps/red-ui/src/assets/styles/variables';
.mat-dialog-container {
color: $accent;
color: var(--iqser-accent);
padding: 0 !important;
border-radius: 8px !important;
}
@ -27,7 +25,7 @@
.dialog-actions {
height: 81px;
box-sizing: border-box;
border-top: 1px solid $separator;
border-top: 1px solid var(--iqser-separator);
padding: 0 32px;
align-items: center;

View File

@ -1,17 +1,12 @@
@import 'variables';
.full-page-section,
.full-page-content {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
inset: 0;
}
.full-page-section {
opacity: 0.7;
background: $white;
background: var(--iqser-white);
z-index: 900;
}

View File

@ -1,5 +1,4 @@
@import 'variables';
@import 'mixins';
@use 'common-mixins' as mixins;
form .iqser-input-group:not(first-of-type) {
margin-top: 14px;
@ -22,10 +21,10 @@ form .iqser-input-group:not(first-of-type) {
position: absolute;
right: 1px;
bottom: 1px;
background: $quick-filter-border;
background: var(--iqser-quick-filter-border);
height: 34px;
width: 34px;
border-left: 1px solid $quick-filter-border;
border-left: 1px solid var(--iqser-quick-filter-border);
border-top-right-radius: 7px;
border-bottom-right-radius: 7px;
cursor: pointer;
@ -35,13 +34,13 @@ form .iqser-input-group:not(first-of-type) {
justify-content: center;
&:hover {
background: $btn-bg;
background: var(--iqser-btn-bg);
}
mat-icon {
width: 14px;
height: 14px;
color: $accent;
color: var(--iqser-accent);
}
&.disabled {
@ -60,7 +59,7 @@ form .iqser-input-group:not(first-of-type) {
.mat-form-field-label {
opacity: 0.7 !important;
color: $accent !important;
color: var(--iqser-accent) !important;
transform: translateY(-1.34em) !important;
}
@ -83,9 +82,9 @@ form .iqser-input-group:not(first-of-type) {
}
.mat-button-toggle-checked {
background: $primary;
background: var(--iqser-primary);
transition: background-color 0.25s ease;
color: $white;
color: var(--iqser-white);
}
input,
@ -94,7 +93,7 @@ form .iqser-input-group:not(first-of-type) {
box-sizing: border-box;
padding-left: 11px;
padding-right: 11px;
border: 1px solid $quick-filter-border;
border: 1px solid var(--iqser-quick-filter-border);
font-family: Inter, sans-serif;
font-size: 13px;
background-color: #ffffff;
@ -108,26 +107,26 @@ form .iqser-input-group:not(first-of-type) {
}
&:focus:not(:disabled):not(.mat-select-disabled) {
border-color: $accent;
border-color: var(--iqser-accent);
}
&::placeholder {
color: $accent;
color: var(--iqser-accent);
opacity: 0.7;
}
&.ng-invalid.ng-touched {
border-color: rgba($primary, 0.3);
border-color: rgba(var(--iqser-primary-rgb), 0.3);
&:focus {
border-color: $primary;
border-color: var(--iqser-primary);
}
}
&:disabled,
&.mat-select-disabled {
background-color: $filter-bg;
color: rgba($accent, 0.3);
background-color: var(--iqser-grey-2);
color: rgba(var(--iqser-accent-rgb), 0.3);
}
}
@ -154,7 +153,7 @@ form .iqser-input-group:not(first-of-type) {
resize: vertical;
padding-top: 7px;
padding-bottom: 7px;
@include scroll-bar;
@include mixins.scroll-bar;
&.has-scrollbar {
border-top-right-radius: 0;
@ -168,7 +167,7 @@ form .iqser-input-group:not(first-of-type) {
letter-spacing: 0;
line-height: 14px;
margin-bottom: 2px;
color: $accent;
color: var(--iqser-accent);
&.mat-checkbox-layout {
opacity: 1;
@ -178,7 +177,7 @@ form .iqser-input-group:not(first-of-type) {
&.required label:after {
content: ' *';
color: $primary;
color: var(--iqser-primary);
}
&.datepicker-wrapper {
@ -196,10 +195,10 @@ form .iqser-input-group:not(first-of-type) {
position: absolute;
right: 0;
top: 1px;
color: $accent;
color: var(--iqser-accent);
&.mat-datepicker-toggle-active {
color: $primary;
color: var(--iqser-primary);
}
.mat-icon-button {

View File

@ -0,0 +1,7 @@
@use 'common-inputs';
@use 'common-buttons';
@use 'common-texts';
@use 'common-tables';
@use 'common-full-pages';
@use 'common-layout';
@use 'common-dialogs';

View File

@ -1,5 +1,4 @@
@import 'variables';
@import 'mixins';
@use 'common-mixins' as mixins;
.all-caps-label {
text-transform: uppercase;
@ -24,17 +23,18 @@
}
&.primary {
color: $primary;
color: var(--iqser-primary);
opacity: 1;
}
}
a {
color: $primary;
color: var(--iqser-primary);
transition: color 0.1s;
&:hover {
color: lighten($primary, 10%);
color: var(--iqser-primary);
filter: brightness(140%);
}
&.with-underline {
@ -48,7 +48,7 @@ a {
pre {
font-family: Inter, sans-serif;
color: $accent;
color: var(--iqser-accent);
}
.heading-xl {
@ -102,17 +102,17 @@ pre {
}
.large-label {
color: $accent;
color: var(--iqser-accent);
font-size: 13px;
line-height: 16px;
}
.clamp-1 {
@include line-clamp(1);
@include mixins.line-clamp(1);
}
.clamp-2 {
@include line-clamp(2);
@include mixins.line-clamp(2);
}
.no-wrap {

View File

@ -1,7 +0,0 @@
@import 'inputs';
@import 'buttons';
@import 'texts';
@import 'tables';
@import 'full-pages';
@import 'layout';
@import 'dialogs';

View File

@ -1,5 +1,3 @@
@import '../../../assets/styles/common';
:host {
height: var(--size);
width: var(--size);
@ -26,15 +24,15 @@
}
&.primary.mat-button-disabled {
background-color: $btn-bg;
color: $white !important;
background-color: var(--iqser-btn-bg);
color: var(--iqser-white) !important;
}
&.warn:not([disabled]) {
background-color: $warn;
background-color: var(--iqser-warn);
&:hover {
background-color: $warn;
background-color: var(--iqser-warn);
}
}
}

View File

@ -1,5 +1,3 @@
@import '../../../assets/styles/common';
button {
padding: 0 14px;
width: 100%;
@ -9,10 +7,10 @@ button {
}
&.show-bg {
background-color: $btn-bg;
background-color: var(--iqser-btn-bg);
&:not(.mat-button-disabled):hover {
background-color: $btn-bg-hover;
background-color: var(--iqser-btn-bg-hover);
}
}

View File

@ -16,6 +16,7 @@ import { IqserIconsModule } from './icons';
import { IqserButtonsModule } from './buttons';
import { IqserScrollbarModule } from './scrollbar';
import { IqserEmptyStatesModule } from './empty-states';
import { LogPipe } from './utils/pipes/log.pipe';
const matModules = [MatIconModule, MatProgressSpinnerModule];
const modules = [
@ -33,8 +34,8 @@ const components = [StatusBarComponent, FullPageLoadingIndicatorComponent, FullP
const pipes = [SortByPipe, HumanizePipe];
@NgModule({
declarations: [...components, ...pipes],
declarations: [...components, ...pipes, LogPipe],
imports: [CommonModule, ...matModules, ...modules],
exports: [...components, ...pipes, ...modules]
exports: [...components, ...pipes, ...modules, LogPipe]
})
export class CommonUiModule {}

View File

@ -1,5 +1,3 @@
@import '../../../assets/styles/common';
.empty-state {
display: flex;
flex-direction: column;
@ -13,7 +11,7 @@
}
.heading-l {
color: $grey-7;
color: var(--iqser-disabled);
}
> .heading-l,

View File

@ -1,15 +1,36 @@
<ng-container *ngIf="errorService.error$ | async as error">
<section class="full-page-section"></section>
<section class="full-page-content">
<mat-icon svgIcon="iqser:failure"></mat-icon>
<div class="heading-l mt-24" translate="error.title"></div>
<div class="mt-16 error">{{ error.message }}</div>
<iqser-icon-button
(action)="reload()"
[label]="'error.reload' | translate"
[type]="iconButtonTypes.primary"
class="mt-20"
icon="iqser:refresh"
></iqser-icon-button>
</section>
<ng-container *ngIf="error.status === 0; else serverError">
<div class="offline-box flex-align-items-center" [@animateOpenClose]="'open'">
<mat-icon svgIcon="iqser:offline"></mat-icon>
<div class="heading-l ml-14" [translate]="'error.offline'"></div>
<iqser-circle-button
(action)="errorService.set(undefined)"
[tooltip]="'error.close' | translate"
icon="iqser:close"
tooltipPosition="below"
></iqser-circle-button>
</div>
</ng-container>
<ng-template #serverError>
<section class="full-page-section"></section>
<section class="full-page-content flex-align-items-center">
<mat-icon svgIcon="iqser:failure"></mat-icon>
<div class="heading-l mt-24" [translate]="'error.title'"></div>
<div class="mt-16 error">{{ error.message }}</div>
<iqser-icon-button
(action)="reload()"
[label]="'error.reload' | translate"
[type]="iconButtonTypes.primary"
class="mt-20"
icon="iqser:refresh"
></iqser-icon-button>
</section>
</ng-template>
</ng-container>

View File

@ -1,13 +1,30 @@
@import '../../../assets/styles/variables';
.offline-box {
position: fixed;
bottom: 20px;
right: 20px;
height: 40px;
width: 300px;
background: var(--iqser-white);
border: 1px solid var(--iqser-separator);
border-radius: 10px;
padding: 14px;
> mat-icon {
opacity: 0.3;
}
> iqser-circle-button {
flex-grow: 1;
justify-content: flex-end;
}
}
.full-page-section {
opacity: 0.95;
}
.full-page-content {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
> mat-icon {
@ -16,12 +33,12 @@
opacity: 0.1;
}
.heading-l {
color: $grey-7;
font-weight: initial;
}
.error {
color: $red-1;
color: var(--iqser-primary);
}
}
:is(.offline-box, .full-page-content) .heading-l {
color: #9398a0;
font-weight: initial;
}

View File

@ -1,11 +1,19 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { IconButtonTypes } from '../../buttons';
import { ErrorService } from '../error.service';
import { animate, state, style, transition, trigger } from '@angular/animations';
@Component({
selector: 'iqser-full-page-error',
templateUrl: './full-page-error.component.html',
styleUrls: ['./full-page-error.component.scss'],
animations: [
trigger('animateOpenClose', [
state('open', style({ bottom: '20px' })),
state('void', style({ bottom: '-70px' })),
transition('* => open, open => void', animate('1s ease-in-out'))
])
],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class FullPageErrorComponent {

View File

@ -1,2 +1,4 @@
export * from './error.service';
export * from './max-retries.token';
export * from './server-error-interceptor';
export * from './full-page-error/full-page-error.component';

View File

@ -0,0 +1,3 @@
import { InjectionToken } from '@angular/core';
export const MAX_RETRIES_ON_SERVER_ERROR = new InjectionToken<number>('Number of retries before giving up');

View File

@ -0,0 +1,52 @@
import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Inject, Injectable, Optional } from '@angular/core';
import { MonoTypeOperatorFunction, Observable, throwError, timer } from 'rxjs';
import { catchError, mergeMap, retryWhen, tap } from 'rxjs/operators';
import { MAX_RETRIES_ON_SERVER_ERROR } from './max-retries.token';
import { ErrorService } from './error.service';
function updateSeconds(seconds: number) {
if (seconds === 0 || seconds === 1) {
return seconds + 1;
} else {
return seconds * seconds;
}
}
function backoffOnServerError(maxRetries = 3): MonoTypeOperatorFunction<HttpEvent<unknown>> {
let seconds = 0;
return retryWhen(attempts =>
attempts.pipe(
tap(() => (seconds = updateSeconds(seconds))),
mergeMap((error: HttpErrorResponse, index) => {
if ((error.status < 500 && error.status !== 0) || index === maxRetries) {
return throwError(error);
} else {
console.error('An error occurred: ', error);
console.error(`Retrying in ${seconds} seconds...`);
return timer(seconds * 1000);
}
})
)
);
}
@Injectable()
export class ServerErrorInterceptor implements HttpInterceptor {
constructor(
private readonly _errorService: ErrorService,
@Optional() @Inject(MAX_RETRIES_ON_SERVER_ERROR) private readonly _maxRetries: number
) {}
intercept(req: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
return next.handle(req).pipe(
catchError((error: HttpErrorResponse) => {
if (error.status >= 500 || error.status === 0) {
this._errorService.set(error);
}
return throwError(error);
}),
backoffOnServerError(this._maxRetries || 3)
);
}
}

View File

@ -1,5 +1,3 @@
@import '../../../assets/styles/variables';
.filter-menu-options,
.filter-menu-header {
display: flex;
@ -27,7 +25,7 @@
}
.filter-options {
background-color: $filter-bg;
background-color: var(--iqser-grey-2);
padding-bottom: 8px;
}

View File

@ -1,5 +1,3 @@
@import '../../../assets/styles/variables';
:host {
display: flex;
flex: 1;
@ -8,9 +6,9 @@
.quick-filter {
box-sizing: border-box;
border: 1px solid $quick-filter-border;
border: 1px solid var(--iqser-quick-filter-border);
border-radius: 17px;
background-color: $btn-bg;
background-color: var(--iqser-btn-bg);
padding: 0 14px;
height: 34px;
display: flex;
@ -19,11 +17,11 @@
transition: background-color 0.2s;
&:hover {
background-color: $white;
background-color: var(--iqser-white);
}
&.active {
background-color: $white;
background-color: var(--iqser-white);
font-weight: 600;
border: none;
}

View File

@ -1,5 +1,3 @@
@import '../../../../../../apps/red-ui/src/assets/styles/variables';
.help-button {
width: 44px;
height: 40px;
@ -7,7 +5,7 @@
bottom: 20px;
right: 0;
z-index: 1;
background: $green-2;
background: var(--iqser-helpmode-primary);
border-top-left-radius: 8px;
border-bottom-left-radius: 8px;
box-shadow: -1px 1px 5px 0 rgba(40, 50, 65, 0.25);
@ -40,10 +38,10 @@
box-sizing: border-box;
height: 100%;
width: 100%;
border-left: 8px solid $green-2;
border-right: 8px solid $green-2;
border-top: 8px solid $green-2;
border-bottom: 60px solid $green-2;
border-left: 8px solid var(--iqser-helpmode-primary);
border-right: 8px solid var(--iqser-helpmode-primary);
border-top: 8px solid var(--iqser-helpmode-primary);
border-bottom: 60px solid var(--iqser-helpmode-primary);
z-index: 10;
position: absolute;
display: flex;

View File

@ -20,6 +20,7 @@ export class IqserIconsModule {
'help-outline',
'lanes',
'list',
'offline',
'refresh',
'search',
'sort-asc',

View File

@ -1,5 +1,3 @@
@import '../../../assets/styles/common';
:host {
cursor: pointer;
}
@ -12,22 +10,22 @@
&.inactive {
border: 1px solid #d3d5da;
background-color: $white;
background-color: var(--iqser-white);
}
.mat-icon {
color: $primary;
color: var(--iqser-primary);
width: var(--size);
height: var(--size);
}
&.with-bg {
.mat-icon {
color: $white;
color: var(--iqser-white);
}
&.inactive {
border: 1px solid $btn-bg;
border: 1px solid var(--iqser-btn-bg);
background-color: transparent;
}
}

View File

@ -1,7 +1,5 @@
@import '../../../assets/styles/common';
.scroll-button {
background-color: $white;
background-color: var(--iqser-white);
position: absolute;
right: 0;
height: 40px;
@ -26,5 +24,5 @@
mat-icon {
width: 22px;
height: 22px;
color: $grey-7;
color: var(--iqser-disabled);
}

View File

@ -1,7 +1,7 @@
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 { delay, distinctUntilChanged, map, startWith } from 'rxjs/operators';
import { combineLatest, fromEvent, Observable } from 'rxjs';
import { Required } from '../../utils';
const ButtonTypes = {
@ -34,11 +34,14 @@ export class ScrollButtonComponent implements OnInit {
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)))
);
/** Force an initial emit, so combineLatest works */
const scrolled$ = this.scrollViewport.elementScrolled().pipe(startWith(null));
const resized$ = fromEvent(window, 'resize').pipe(startWith(null));
const rangeChange$ = this.scrollViewport.renderedRangeStream.pipe(startWith(null));
/** Delay so that we can wait for items to be rendered in viewport and get correct values */
const scroll$ = combineLatest([scrolled$, resized$, rangeChange$]).pipe(delay(0));
this.showScrollUp$ = scroll$.pipe(map(showScrollUp), distinctUntilChanged());
this.showScrollDown$ = scroll$.pipe(map(showScrollDown), distinctUntilChanged());
}

View File

@ -8,7 +8,7 @@ export class SyncWidthDirective implements OnDestroy {
private readonly _interval: number;
constructor(private readonly _elementRef: ElementRef) {
this._interval = setInterval(() => {
this._interval = window.setInterval(() => {
this._matchWidth();
}, 1000);
}

View File

@ -1,5 +1,3 @@
@import '../../../assets/styles/common';
:host {
display: flex;
height: 30px;
@ -38,7 +36,7 @@
.sort-arrows-container {
display: none;
color: $primary;
color: var(--iqser-primary);
margin-left: 8px;
mat-icon {

View File

@ -1,8 +1,6 @@
@import '../../../assets/styles/common';
.table-header {
display: flex;
border-bottom: 1px solid $separator;
border-bottom: 1px solid var(--iqser-separator);
&.no-data.selection-enabled:not([synced='true']) {
padding-left: 30px;
@ -10,12 +8,12 @@
}
.header-item {
background-color: $btn-bg;
background-color: var(--iqser-btn-bg);
height: 50px;
display: flex;
align-items: center;
z-index: 1;
border-bottom: 1px solid $separator;
border-bottom: 1px solid var(--iqser-separator);
box-sizing: border-box;
padding: 0 24px;

View File

@ -1,4 +1,4 @@
@import '../../../assets/styles/common';
@use '../../../assets/styles/common-mixins' as mixins;
:host cdk-virtual-scroll-viewport {
height: calc(100vh - 50px - 31px - 111px);
@ -25,7 +25,7 @@
justify-content: center;
position: relative;
box-sizing: border-box;
border-bottom: 1px solid $separator;
border-bottom: 1px solid var(--iqser-separator);
height: var(--itemSize);
padding: 0 10px;
@ -61,8 +61,8 @@
}
&.disabled > div {
background-color: $grey-2;
color: $grey-7;
background-color: var(--iqser-grey-2);
color: var(--iqser-disabled);
.action-buttons {
color: initial;
@ -71,7 +71,7 @@
.table-item-title {
font-weight: 600;
@include line-clamp(1);
@include mixins.line-clamp(1);
}
.action-buttons {
@ -86,7 +86,7 @@
padding-left: 100px;
padding-right: 21px;
z-index: 1;
background: linear-gradient(to right, rgba(244, 245, 247, 0) 0%, $grey-2 35%);
background: linear-gradient(to right, rgba(244, 245, 247, 0) 0%, var(--iqser-grey-2) 35%);
mat-icon {
width: 14px;
@ -113,14 +113,14 @@
}
&:hover:not(.disabled) > div {
background-color: $grey-8;
background-color: var(--iqser-not-disabled-table-item);
}
}
}
&:hover {
overflow-y: auto !important;
@include scroll-bar;
@include mixins.scroll-bar;
&.has-scrollbar {
.table-item {

View File

@ -1,4 +1,5 @@
@import '../../../assets/styles/common';
@import '../../../assets/styles/common-variables';
@import '../../../assets/styles/common-mixins';
:host {
display: flex;
@ -15,7 +16,7 @@
display: flex;
flex-direction: column;
flex: 1;
background-color: $grey-2;
background-color: var(--iqser-grey-2);
border-radius: 6px;
padding-top: 18px;
position: relative;
@ -53,10 +54,10 @@
&:not(.list-can-receive):not(.list-dragging) {
background: repeating-linear-gradient(
-45deg,
$separator,
$separator 1px,
$white 1px,
$white 8px
var(--iqser-separator),
var(--iqser-separator) 1px,
var(--iqser-white) 1px,
var(--iqser-white) 8px
);
> .heading, ::ng-deep.cdk-drag > * {
@ -76,7 +77,7 @@
}
.cdk-drag {
background-color: $white;
background-color: var(--iqser-white);
transition: background-color 0.2s, box-shadow 0.2s;
border-radius: 8px;
margin: 0 8px 4px 8px;
@ -86,13 +87,13 @@
}
&:hover {
background-color: $grey-6;
background-color: var(--iqser-grey-6);
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.15);
}
}
.cdk-drag-placeholder {
border: 1px dashed $grey-5;
border: 1px dashed var(--iqser-grey-5);
border-radius: 8px;
min-height: var(--height);
margin: 0 8px 4px 8px;

View File

@ -6,7 +6,7 @@ export function Debounce(delay = 300): MethodDecorator {
descriptorCopy.value = function _new(...args: unknown[]) {
clearTimeout(timeout);
timeout = setTimeout(() => (descriptor.value as (...params: unknown[]) => unknown).apply(this, args), delay);
timeout = window.setTimeout(() => (descriptor.value as (...params: unknown[]) => unknown).apply(this, args), delay);
};
return descriptorCopy;

View File

@ -0,0 +1,60 @@
import { FunctionKeys } from '../types/utility-types';
export interface SimpleChange<T> {
readonly previousValue: T;
readonly currentValue: T;
readonly isFirstChange: boolean;
}
export type CallBackFunction<T> = (value: T, change: SimpleChange<T>) => void;
type TypedPropertyDecorator<C> = (target: C, key: PropertyKey) => void;
const CACHED_VALUE_KEY = Symbol();
const IS_FIRST_CHANGE_KEY = Symbol();
interface Instance<T> {
[CACHED_VALUE_KEY]: T;
[IS_FIRST_CHANGE_KEY]: boolean;
[key: string]: unknown;
}
export function OnChange<T>(callback: CallBackFunction<T> | string): PropertyDecorator;
// eslint-disable-next-line @typescript-eslint/ban-types
export function OnChange<T, C extends Object = Object>(callback: CallBackFunction<T> | FunctionKeys<C>): TypedPropertyDecorator<C>;
// eslint-disable-next-line @typescript-eslint/ban-types
export function OnChange<T, C extends Object = Object>(callback: CallBackFunction<T> | FunctionKeys<C>): TypedPropertyDecorator<C> {
return function _onChange(target: C, key: PropertyKey) {
Object.defineProperty(target, key, {
set(value: T) {
const instance = this as Instance<T>;
const callBackFn = <CallBackFunction<T>>(typeof callback === 'string' ? instance[callback] : callback);
if (!callBackFn) {
throw new Error(`Cannot find method ${String(callback)} in class ${target.constructor.name}`);
}
instance[IS_FIRST_CHANGE_KEY] = instance[IS_FIRST_CHANGE_KEY] === undefined;
// No operation if new value is same as old value
if (!instance[IS_FIRST_CHANGE_KEY] && instance[CACHED_VALUE_KEY] === value) {
return;
}
const oldValue = instance[CACHED_VALUE_KEY];
instance[CACHED_VALUE_KEY] = value;
const simpleChange: SimpleChange<T> = {
previousValue: oldValue,
currentValue: instance[CACHED_VALUE_KEY],
isFirstChange: instance[IS_FIRST_CHANGE_KEY]
};
callBackFn.call(instance, instance[CACHED_VALUE_KEY], simpleChange);
},
get(): T {
return (this as Instance<T>)[CACHED_VALUE_KEY];
}
});
};
}

View File

@ -8,3 +8,4 @@ export * from './types/tooltip-positions.type';
export * from './decorators/bind.decorator';
export * from './decorators/required.decorator';
export * from './decorators/debounce.decorator';
export * from './decorators/on-change.decorator';

View File

@ -0,0 +1,11 @@
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'log'
})
export class LogPipe implements PipeTransform {
transform<T>(value: T, message = ''): T {
console.log(message, value);
return value;
}
}