diff --git a/src/assets/icons/error.svg b/src/assets/icons/error.svg new file mode 100644 index 0000000..917384f --- /dev/null +++ b/src/assets/icons/error.svg @@ -0,0 +1,11 @@ + + + + diff --git a/src/assets/icons/refresh.svg b/src/assets/icons/refresh.svg new file mode 100644 index 0000000..a4fd589 --- /dev/null +++ b/src/assets/icons/refresh.svg @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/src/assets/styles/_full-pages.scss b/src/assets/styles/_full-pages.scss new file mode 100644 index 0000000..3e3dddc --- /dev/null +++ b/src/assets/styles/_full-pages.scss @@ -0,0 +1,24 @@ +@import 'variables'; + +.full-page-section, +.full-page-content { + position: fixed; + top: 0; + bottom: 0; + left: 0; + right: 0; +} + +.full-page-section { + opacity: 0.7; + background: $white; + z-index: 900; +} + +.full-page-content { + z-index: 1000; + justify-content: center; + align-items: center; + flex-direction: column; + display: flex; +} diff --git a/src/assets/styles/_mixins.scss b/src/assets/styles/_mixins.scss new file mode 100644 index 0000000..7236bd7 --- /dev/null +++ b/src/assets/styles/_mixins.scss @@ -0,0 +1,50 @@ +@import 'variables'; + +@mixin line-clamp($lines) { + display: -webkit-box; + -webkit-line-clamp: $lines; + -webkit-box-orient: vertical; + overflow: hidden; + display: block; + + @if $lines == 1 { + text-overflow: ellipsis; + white-space: nowrap; + } +} + +@mixin no-scroll-bar { + scrollbar-width: none; /* Firefox */ + -ms-overflow-style: none; /* IE 10+ */ + &::-webkit-scrollbar { + width: 0; + background: transparent; /* Chrome/Safari/Webkit */ + } +} + +@mixin scroll-bar { + scrollbar-color: $grey-5 $grey-2; + scrollbar-width: thin; + + &::-webkit-scrollbar { + width: 11px; + } + + /* Track */ + &::-webkit-scrollbar-track { + background: $grey-2; + } + + /* Handle */ + &::-webkit-scrollbar-thumb { + background: $grey-5; + } +} + +@mixin inset-shadow { + box-shadow: inset 0 4px 3px -2px $grey-4; +} + +@mixin drop-shadow { + box-shadow: 0 4px 3px 2px $grey-4; +} diff --git a/src/assets/styles/_texts.scss b/src/assets/styles/_texts.scss index 4455d95..82cf526 100644 --- a/src/assets/styles/_texts.scss +++ b/src/assets/styles/_texts.scss @@ -1,4 +1,5 @@ @import 'variables'; +@import 'mixins'; .all-caps-label { text-transform: uppercase; @@ -27,3 +28,100 @@ opacity: 1; } } + +a { + color: $primary; + transition: color 0.1s; + + &:hover { + color: lighten($primary, 10%); + } + + &.with-underline { + &:hover { + text-decoration: underline; + } + } + + cursor: pointer; +} + +pre { + font-family: Inter, sans-serif; + color: $accent; +} + +.heading-xl { + font-size: 24px; + font-weight: 600; + line-height: 29px; +} + +.heading-l { + font-size: 20px; + font-weight: 600; + line-height: 24px; +} + +.heading { + font-size: 16px; + line-height: 20px; + font-weight: 600; +} + +.info { + font-size: 13px; + line-height: 18px; + opacity: 0.7; +} + +.page-title { + font-size: 13px; + font-weight: 600; + line-height: 18px; + text-align: center; + padding: 0 20px; +} + +.small-label { + opacity: 0.7; + font-size: 11px; + line-height: 14px; + font-weight: initial; +} + +.link-action { + font-size: 11px; + line-height: 14px; + text-decoration: underline; + cursor: pointer; + + &:hover { + text-decoration: none; + } +} + +.large-label { + color: $accent; + font-size: 13px; + line-height: 16px; +} + +.clamp-1 { + @include line-clamp(1); +} + +.clamp-2 { + @include line-clamp(2); +} + +.text-overflow { + display: block !important; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.no-wrap { + white-space: nowrap; +} diff --git a/src/assets/styles/common.scss b/src/assets/styles/common.scss index 0a34c3b..7f9b45b 100644 --- a/src/assets/styles/common.scss +++ b/src/assets/styles/common.scss @@ -1,3 +1,4 @@ @import 'buttons'; @import 'texts'; @import 'tables'; +@import 'full-pages'; diff --git a/src/index.ts b/src/index.ts index 8af8287..53725ba 100644 --- a/src/index.ts +++ b/src/index.ts @@ -32,3 +32,5 @@ export * from './lib/misc/status-bar/status-bar-config.model'; export * from './lib/inputs/editable-input/editable-input.component'; export * from './lib/loading/loading.service'; export * from './lib/loading/full-page-loading-indicator/full-page-loading-indicator.component'; +export * from './lib/error/error.service'; +export * from './lib/error/full-page-error/full-page-error.component'; diff --git a/src/lib/common-ui.module.ts b/src/lib/common-ui.module.ts index 6968ea6..1df59e2 100644 --- a/src/lib/common-ui.module.ts +++ b/src/lib/common-ui.module.ts @@ -20,6 +20,7 @@ import { SyncWidthDirective } from './tables/sync-width.directive'; import { StatusBarComponent } from './misc/status-bar/status-bar.component'; import { EditableInputComponent } from './inputs/editable-input/editable-input.component'; import { FullPageLoadingIndicatorComponent } from './loading/full-page-loading-indicator/full-page-loading-indicator.component'; +import { FullPageErrorComponent } from './error/full-page-error/full-page-error.component'; const buttons = [IconButtonComponent, ChevronButtonComponent, CircleButtonComponent]; @@ -36,7 +37,8 @@ const components = [ QuickFiltersComponent, TableHeaderComponent, StatusBarComponent, - FullPageLoadingIndicatorComponent + FullPageLoadingIndicatorComponent, + FullPageErrorComponent ]; const utils = [SortByPipe, HumanizePipe, SyncWidthDirective]; @@ -48,7 +50,7 @@ const utils = [SortByPipe, HumanizePipe, SyncWidthDirective]; }) export class CommonUiModule { constructor(private readonly _iconRegistry: MatIconRegistry, private readonly _sanitizer: DomSanitizer) { - const icons = ['arrow-down', 'check', 'close', 'edit', 'sort-asc', 'sort-desc']; + const icons = ['arrow-down', 'check', 'close', 'edit', 'error', 'refresh', 'sort-asc', 'sort-desc']; icons.forEach(icon => { _iconRegistry.addSvgIconInNamespace('iqser', icon, _sanitizer.bypassSecurityTrustResourceUrl(`/assets/icons/${icon}.svg`)); diff --git a/src/lib/error/error.service.ts b/src/lib/error/error.service.ts new file mode 100644 index 0000000..24965da --- /dev/null +++ b/src/lib/error/error.service.ts @@ -0,0 +1,34 @@ +import { Injectable } from '@angular/core'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { LoadingService } from '../loading/loading.service'; + +export type Error = { + name: string; +} | null; + +@Injectable({ providedIn: 'root' }) +export class ErrorService { + readonly hasError$: Observable; + private readonly _errorEvent$ = new BehaviorSubject(null); + + constructor(private readonly _loadingService: LoadingService) { + this.hasError$ = this._errorEvent$.asObservable(); + } + + private _error: Error = null; + + get error(): Error { + return this._error; + } + + set(error: Error): void { + this._loadingService.stop(); + this._error = error; + this._errorEvent$.next(error); + } + + clear(): void { + this._errorEvent$.next(null); + this._error = null; + } +} 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 new file mode 100644 index 0000000..6bc1073 --- /dev/null +++ b/src/lib/error/full-page-error/full-page-error.component.html @@ -0,0 +1,14 @@ + +
+
+ +
+

{{ errorService.error.name }}

+ +
+
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 new file mode 100644 index 0000000..0963641 --- /dev/null +++ b/src/lib/error/full-page-error/full-page-error.component.scss @@ -0,0 +1,26 @@ +@import '../../../assets/styles/variables'; + +.full-page-section { + opacity: 0.9; +} + +.full-page-content { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + + > mat-icon { + height: 100px; + width: 100px; + } + + .heading-l { + color: $primary; + } + + > .heading-l, + iqser-icon-button { + margin-top: 32px; + } +} 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 new file mode 100644 index 0000000..138c0f3 --- /dev/null +++ b/src/lib/error/full-page-error/full-page-error.component.ts @@ -0,0 +1,19 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { ErrorService } from '../error.service'; +import { IconButtonTypes } from '../../buttons/icon-button/icon-button.type'; + +@Component({ + selector: 'iqser-full-page-error', + templateUrl: './full-page-error.component.html', + styleUrls: ['./full-page-error.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class FullPageErrorComponent { + readonly iconButtonTypes = IconButtonTypes; + + constructor(readonly errorService: ErrorService) {} + + reload(): void { + window.location.reload(); + } +} diff --git a/src/lib/loading/full-page-loading-indicator/full-page-loading-indicator.component.html b/src/lib/loading/full-page-loading-indicator/full-page-loading-indicator.component.html index 46e67a5..50722a5 100644 --- a/src/lib/loading/full-page-loading-indicator/full-page-loading-indicator.component.html +++ b/src/lib/loading/full-page-loading-indicator/full-page-loading-indicator.component.html @@ -1,6 +1,6 @@ -
-
+
+
diff --git a/src/lib/loading/full-page-loading-indicator/full-page-loading-indicator.component.scss b/src/lib/loading/full-page-loading-indicator/full-page-loading-indicator.component.scss index 1f73bc9..e69de29 100644 --- a/src/lib/loading/full-page-loading-indicator/full-page-loading-indicator.component.scss +++ b/src/lib/loading/full-page-loading-indicator/full-page-loading-indicator.component.scss @@ -1,24 +0,0 @@ -@import '../../../assets/styles/variables'; - -.full-page-load-section, -.full-page-load-spinner { - position: fixed; - top: 0; - bottom: 0; - left: 0; - right: 0; -} - -.full-page-load-section { - opacity: 0.7; - background: $white; - z-index: 900; -} - -.full-page-load-spinner { - z-index: 1000; - justify-content: center; - align-items: center; - flex-direction: column; - display: flex; -} diff --git a/src/lib/tables/table-header/table-header.component.html b/src/lib/tables/table-header/table-header.component.html index 5c8278b..eedc503 100644 --- a/src/lib/tables/table-header/table-header.component.html +++ b/src/lib/tables/table-header/table-header.component.html @@ -12,7 +12,7 @@ - + @@ -29,11 +29,11 @@