diff --git a/src/assets/icons/offline.svg b/src/assets/icons/offline.svg
new file mode 100644
index 0000000..d5c6610
--- /dev/null
+++ b/src/assets/icons/offline.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/styles/_common-functions.scss b/src/assets/styles/_common-functions.scss
new file mode 100644
index 0000000..efdd0c0
--- /dev/null
+++ b/src/assets/styles/_common-functions.scss
@@ -0,0 +1,3 @@
+@function hexToRgb($color) {
+ @return #{red($color) + ', ' + green($color) + ', ' + blue($color)};
+}
diff --git a/src/assets/styles/_mixins.scss b/src/assets/styles/_common-mixins.scss
similarity index 73%
rename from src/assets/styles/_mixins.scss
rename to src/assets/styles/_common-mixins.scss
index a49c0b9..e20c3d3 100644
--- a/src/assets/styles/_mixins.scss
+++ b/src/assets/styles/_common-mixins.scss
@@ -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);
}
diff --git a/src/assets/styles/_common-variables.scss b/src/assets/styles/_common-variables.scss
new file mode 100644
index 0000000..d62662f
--- /dev/null
+++ b/src/assets/styles/_common-variables.scss
@@ -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};
+ }
+ }
+}
diff --git a/src/assets/styles/_variables.scss b/src/assets/styles/_variables.scss
deleted file mode 100644
index cf11624..0000000
--- a/src/assets/styles/_variables.scss
+++ /dev/null
@@ -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;
diff --git a/src/assets/styles/_buttons.scss b/src/assets/styles/common-buttons.scss
similarity index 71%
rename from src/assets/styles/_buttons.scss
rename to src/assets/styles/common-buttons.scss
index 4c81a84..2624669 100644
--- a/src/assets/styles/_buttons.scss
+++ b/src/assets/styles/common-buttons.scss
@@ -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);
}
}
}
diff --git a/src/assets/styles/_dialogs.scss b/src/assets/styles/common-dialogs.scss
similarity index 83%
rename from src/assets/styles/_dialogs.scss
rename to src/assets/styles/common-dialogs.scss
index 5a68806..40751e1 100644
--- a/src/assets/styles/_dialogs.scss
+++ b/src/assets/styles/common-dialogs.scss
@@ -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;
diff --git a/src/assets/styles/_full-pages.scss b/src/assets/styles/common-full-pages.scss
similarity index 75%
rename from src/assets/styles/_full-pages.scss
rename to src/assets/styles/common-full-pages.scss
index 9c9c6a4..3b37d46 100644
--- a/src/assets/styles/_full-pages.scss
+++ b/src/assets/styles/common-full-pages.scss
@@ -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;
}
diff --git a/src/assets/styles/_inputs.scss b/src/assets/styles/common-inputs.scss
similarity index 82%
rename from src/assets/styles/_inputs.scss
rename to src/assets/styles/common-inputs.scss
index 02ed6c8..9725141 100644
--- a/src/assets/styles/_inputs.scss
+++ b/src/assets/styles/common-inputs.scss
@@ -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 {
diff --git a/src/assets/styles/_layout.scss b/src/assets/styles/common-layout.scss
similarity index 100%
rename from src/assets/styles/_layout.scss
rename to src/assets/styles/common-layout.scss
diff --git a/src/assets/styles/common-styles.scss b/src/assets/styles/common-styles.scss
new file mode 100644
index 0000000..43d57f2
--- /dev/null
+++ b/src/assets/styles/common-styles.scss
@@ -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';
diff --git a/src/assets/styles/_tables.scss b/src/assets/styles/common-tables.scss
similarity index 100%
rename from src/assets/styles/_tables.scss
rename to src/assets/styles/common-tables.scss
diff --git a/src/assets/styles/_texts.scss b/src/assets/styles/common-texts.scss
similarity index 82%
rename from src/assets/styles/_texts.scss
rename to src/assets/styles/common-texts.scss
index 61e0cbf..bee024f 100644
--- a/src/assets/styles/_texts.scss
+++ b/src/assets/styles/common-texts.scss
@@ -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 {
diff --git a/src/assets/styles/common.scss b/src/assets/styles/common.scss
deleted file mode 100644
index 4e56f5a..0000000
--- a/src/assets/styles/common.scss
+++ /dev/null
@@ -1,7 +0,0 @@
-@import 'inputs';
-@import 'buttons';
-@import 'texts';
-@import 'tables';
-@import 'full-pages';
-@import 'layout';
-@import 'dialogs';
diff --git a/src/lib/buttons/circle-button/circle-button.component.scss b/src/lib/buttons/circle-button/circle-button.component.scss
index 0e44ca4..94bf11c 100644
--- a/src/lib/buttons/circle-button/circle-button.component.scss
+++ b/src/lib/buttons/circle-button/circle-button.component.scss
@@ -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);
}
}
}
diff --git a/src/lib/buttons/icon-button/icon-button.component.scss b/src/lib/buttons/icon-button/icon-button.component.scss
index 787b413..056d65d 100644
--- a/src/lib/buttons/icon-button/icon-button.component.scss
+++ b/src/lib/buttons/icon-button/icon-button.component.scss
@@ -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);
}
}
diff --git a/src/lib/common-ui.module.ts b/src/lib/common-ui.module.ts
index d255dc3..6222977 100644
--- a/src/lib/common-ui.module.ts
+++ b/src/lib/common-ui.module.ts
@@ -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 {}
diff --git a/src/lib/empty-states/empty-state/empty-state.component.scss b/src/lib/empty-states/empty-state/empty-state.component.scss
index 042e4c1..09d4f96 100644
--- a/src/lib/empty-states/empty-state/empty-state.component.scss
+++ b/src/lib/empty-states/empty-state/empty-state.component.scss
@@ -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,
diff --git a/src/lib/error/full-page-error/full-page-error.component.html b/src/lib/error/full-page-error/full-page-error.component.html
index e55ee4d..1ffdcf7 100644
--- a/src/lib/error/full-page-error/full-page-error.component.html
+++ b/src/lib/error/full-page-error/full-page-error.component.html
@@ -1,15 +1,36 @@
-
-
-
-
- {{ error.message }}
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ error.message }}
+
+
+
+
diff --git a/src/lib/error/full-page-error/full-page-error.component.scss b/src/lib/error/full-page-error/full-page-error.component.scss
index 8791f01..d1f3986 100644
--- a/src/lib/error/full-page-error/full-page-error.component.scss
+++ b/src/lib/error/full-page-error/full-page-error.component.scss
@@ -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;
+}
diff --git a/src/lib/error/full-page-error/full-page-error.component.ts b/src/lib/error/full-page-error/full-page-error.component.ts
index dcc4fce..43862b9 100644
--- a/src/lib/error/full-page-error/full-page-error.component.ts
+++ b/src/lib/error/full-page-error/full-page-error.component.ts
@@ -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 {
diff --git a/src/lib/error/index.ts b/src/lib/error/index.ts
index 9a01d8a..42fbcd1 100644
--- a/src/lib/error/index.ts
+++ b/src/lib/error/index.ts
@@ -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';
diff --git a/src/lib/error/max-retries.token.ts b/src/lib/error/max-retries.token.ts
new file mode 100644
index 0000000..b2ad58a
--- /dev/null
+++ b/src/lib/error/max-retries.token.ts
@@ -0,0 +1,3 @@
+import { InjectionToken } from '@angular/core';
+
+export const MAX_RETRIES_ON_SERVER_ERROR = new InjectionToken('Number of retries before giving up');
diff --git a/src/lib/error/server-error-interceptor.ts b/src/lib/error/server-error-interceptor.ts
new file mode 100644
index 0000000..2124e5f
--- /dev/null
+++ b/src/lib/error/server-error-interceptor.ts
@@ -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> {
+ 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, next: HttpHandler): Observable> {
+ 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)
+ );
+ }
+}
diff --git a/src/lib/filtering/popup-filter/popup-filter.component.scss b/src/lib/filtering/popup-filter/popup-filter.component.scss
index d569c5d..01a8e56 100644
--- a/src/lib/filtering/popup-filter/popup-filter.component.scss
+++ b/src/lib/filtering/popup-filter/popup-filter.component.scss
@@ -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;
}
diff --git a/src/lib/filtering/quick-filters/quick-filters.component.scss b/src/lib/filtering/quick-filters/quick-filters.component.scss
index 13ef48d..0974792 100644
--- a/src/lib/filtering/quick-filters/quick-filters.component.scss
+++ b/src/lib/filtering/quick-filters/quick-filters.component.scss
@@ -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;
}
diff --git a/src/lib/help-mode/help-mode/help-mode.component.scss b/src/lib/help-mode/help-mode/help-mode.component.scss
index fb99479..e848a0c 100644
--- a/src/lib/help-mode/help-mode/help-mode.component.scss
+++ b/src/lib/help-mode/help-mode/help-mode.component.scss
@@ -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;
diff --git a/src/lib/icons/icons.module.ts b/src/lib/icons/icons.module.ts
index b5e40e8..1ad00b7 100644
--- a/src/lib/icons/icons.module.ts
+++ b/src/lib/icons/icons.module.ts
@@ -20,6 +20,7 @@ export class IqserIconsModule {
'help-outline',
'lanes',
'list',
+ 'offline',
'refresh',
'search',
'sort-asc',
diff --git a/src/lib/inputs/round-checkbox/round-checkbox.component.scss b/src/lib/inputs/round-checkbox/round-checkbox.component.scss
index 99c3e9b..97f77f2 100644
--- a/src/lib/inputs/round-checkbox/round-checkbox.component.scss
+++ b/src/lib/inputs/round-checkbox/round-checkbox.component.scss
@@ -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;
}
}
diff --git a/src/lib/listing/scroll-button/scroll-button.component.scss b/src/lib/listing/scroll-button/scroll-button.component.scss
index 093aaa9..ee4a294 100644
--- a/src/lib/listing/scroll-button/scroll-button.component.scss
+++ b/src/lib/listing/scroll-button/scroll-button.component.scss
@@ -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);
}
diff --git a/src/lib/listing/scroll-button/scroll-button.component.ts b/src/lib/listing/scroll-button/scroll-button.component.ts
index 09ad966..c6da626 100644
--- a/src/lib/listing/scroll-button/scroll-button.component.ts
+++ b/src/lib/listing/scroll-button/scroll-button.component.ts
@@ -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());
}
diff --git a/src/lib/listing/sync-width.directive.ts b/src/lib/listing/sync-width.directive.ts
index c7cde8c..d2f4693 100644
--- a/src/lib/listing/sync-width.directive.ts
+++ b/src/lib/listing/sync-width.directive.ts
@@ -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);
}
diff --git a/src/lib/listing/table-column-name/table-column-name.component.scss b/src/lib/listing/table-column-name/table-column-name.component.scss
index 731b910..7fb6e0d 100644
--- a/src/lib/listing/table-column-name/table-column-name.component.scss
+++ b/src/lib/listing/table-column-name/table-column-name.component.scss
@@ -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 {
diff --git a/src/lib/listing/table-header/table-header.component.scss b/src/lib/listing/table-header/table-header.component.scss
index 106f35c..1a0c691 100644
--- a/src/lib/listing/table-header/table-header.component.scss
+++ b/src/lib/listing/table-header/table-header.component.scss
@@ -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;
diff --git a/src/lib/listing/table/table.component.scss b/src/lib/listing/table/table.component.scss
index 810b74d..6486db2 100644
--- a/src/lib/listing/table/table.component.scss
+++ b/src/lib/listing/table/table.component.scss
@@ -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 {
diff --git a/src/lib/listing/workflow/workflow.component.scss b/src/lib/listing/workflow/workflow.component.scss
index dd25803..c363708 100644
--- a/src/lib/listing/workflow/workflow.component.scss
+++ b/src/lib/listing/workflow/workflow.component.scss
@@ -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;
diff --git a/src/lib/utils/decorators/debounce.decorator.ts b/src/lib/utils/decorators/debounce.decorator.ts
index 0da4c4d..8be812b 100644
--- a/src/lib/utils/decorators/debounce.decorator.ts
+++ b/src/lib/utils/decorators/debounce.decorator.ts
@@ -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;
diff --git a/src/lib/utils/decorators/on-change.decorator.ts b/src/lib/utils/decorators/on-change.decorator.ts
new file mode 100644
index 0000000..b083e4d
--- /dev/null
+++ b/src/lib/utils/decorators/on-change.decorator.ts
@@ -0,0 +1,60 @@
+import { FunctionKeys } from '../types/utility-types';
+
+export interface SimpleChange {
+ readonly previousValue: T;
+ readonly currentValue: T;
+ readonly isFirstChange: boolean;
+}
+
+export type CallBackFunction = (value: T, change: SimpleChange) => void;
+
+type TypedPropertyDecorator = (target: C, key: PropertyKey) => void;
+
+const CACHED_VALUE_KEY = Symbol();
+const IS_FIRST_CHANGE_KEY = Symbol();
+
+interface Instance {
+ [CACHED_VALUE_KEY]: T;
+ [IS_FIRST_CHANGE_KEY]: boolean;
+
+ [key: string]: unknown;
+}
+
+export function OnChange(callback: CallBackFunction | string): PropertyDecorator;
+// eslint-disable-next-line @typescript-eslint/ban-types
+export function OnChange(callback: CallBackFunction | FunctionKeys): TypedPropertyDecorator;
+// eslint-disable-next-line @typescript-eslint/ban-types
+export function OnChange(callback: CallBackFunction | FunctionKeys): TypedPropertyDecorator {
+ return function _onChange(target: C, key: PropertyKey) {
+ Object.defineProperty(target, key, {
+ set(value: T) {
+ const instance = this as Instance;
+ const callBackFn = >(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 = {
+ 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)[CACHED_VALUE_KEY];
+ }
+ });
+ };
+}
diff --git a/src/lib/utils/index.ts b/src/lib/utils/index.ts
index e601a91..f721eff 100644
--- a/src/lib/utils/index.ts
+++ b/src/lib/utils/index.ts
@@ -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';
diff --git a/src/lib/utils/pipes/log.pipe.ts b/src/lib/utils/pipes/log.pipe.ts
new file mode 100644
index 0000000..d8dc377
--- /dev/null
+++ b/src/lib/utils/pipes/log.pipe.ts
@@ -0,0 +1,11 @@
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({
+ name: 'log'
+})
+export class LogPipe implements PipeTransform {
+ transform(value: T, message = ''): T {
+ console.log(message, value);
+ return value;
+ }
+}