feat(export): Added export deltagare. (TV-872)
Squashed commit of the following: commit 9c06b7f1d44a4b48d7f31a22b6bfbd233e09d2f7 Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Tue Nov 2 15:07:15 2021 +0100 Updated api endpoint commit f9bfbecda6d03b70d87febbada920d0eca798b1f Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Tue Nov 2 15:01:13 2021 +0100 Updated libs commit 9edb413d537288fa0708b8a04eb54f801ebc23a0 Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Tue Nov 2 14:58:13 2021 +0100 Added @types/file-saver commit 624affac55ce771fd58e2770a0d4a98233a8e9ba Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Tue Nov 2 14:42:29 2021 +0100 Updated libs commit 65dae1d906bbcec474581692b2aced9e47d2484c Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Tue Nov 2 14:36:42 2021 +0100 Added utils lib config commit 223bd59724663523bdbaf87b5502396156ddb9eb Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Tue Nov 2 14:06:13 2021 +0100 Added validation commit 166dfcf0448155ac21c0eaa904b4ce1271f73193 Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Tue Nov 2 13:25:35 2021 +0100 Changed styling and removed some fake data commit 3906f2793dd52b626b95c13e115495451332c894 Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Tue Nov 2 13:18:52 2021 +0100 Added digi-ng datepicker commit de0d51434d15cac5476303d4b417c591da16fd8f Author: Erik Tiekstra <erik.tiekstra@arbetsformedlingen.se> Date: Tue Nov 2 12:31:48 2021 +0100 Added checkbox
This commit is contained in:
18
libs/ui/src/checkbox/checkbox.component.html
Normal file
18
libs/ui/src/checkbox/checkbox.component.html
Normal file
@@ -0,0 +1,18 @@
|
||||
<div class="ui-checkbox" [ngClass]="{'ui-checkbox--invalid': uiInvalid && uiValidationMessage}">
|
||||
<digi-form-checkbox
|
||||
[afId]="uiId"
|
||||
[afIndeterminate]="uiIndeterminate"
|
||||
[afName]="uiName"
|
||||
[afRequired]="uiRequired"
|
||||
[afValidation]="uiInvalid ? 'error' : 'neutral'"
|
||||
[afVariation]="uiSecondary ? 'secondary' : 'primary'"
|
||||
[afLabel]="labelText"
|
||||
[afChecked]="currentValue"
|
||||
(afOnChange)="checkForChange($event.detail.target.checked)"
|
||||
></digi-form-checkbox>
|
||||
<div aria-atomic="true" role="alert">
|
||||
<digi-form-validation-message *ngIf="uiInvalid && uiValidationMessage" af-variation="error"
|
||||
>{{uiValidationMessage}}</digi-form-validation-message
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
8
libs/ui/src/checkbox/checkbox.component.scss
Normal file
8
libs/ui/src/checkbox/checkbox.component.scss
Normal file
@@ -0,0 +1,8 @@
|
||||
.ui-checkbox {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&--invalid {
|
||||
gap: var(--digi--layout--gutter--xs);
|
||||
}
|
||||
}
|
||||
23
libs/ui/src/checkbox/checkbox.component.spec.ts
Normal file
23
libs/ui/src/checkbox/checkbox.component.spec.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
/* tslint:disable:no-unused-variable */
|
||||
import { FormCheckboxComponent } from './form-checkbox.component';
|
||||
|
||||
export class MockInjector {
|
||||
get = jest.fn();
|
||||
}
|
||||
|
||||
// tslint:disable-next-line: max-classes-per-file
|
||||
export class MockChangeDetectorRef {
|
||||
markForCheck = jest.fn();
|
||||
detach = jest.fn();
|
||||
detectChanges = jest.fn();
|
||||
reattach = jest.fn();
|
||||
checkNoChanges = jest.fn();
|
||||
}
|
||||
|
||||
describe('FormCheckboxComponent', () => {
|
||||
let component: FormCheckboxComponent;
|
||||
it('should create', () => {
|
||||
component = new FormCheckboxComponent(new MockInjector(), new MockChangeDetectorRef());
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
16
libs/ui/src/checkbox/checkbox.component.stories.ts
Normal file
16
libs/ui/src/checkbox/checkbox.component.stories.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { CheckboxComponent } from './checkbox.component';
|
||||
import { UiCheckboxModule } from './checkbox.module';
|
||||
|
||||
export default { title: 'Checkbox', component: CheckboxComponent };
|
||||
|
||||
const componentModule = {
|
||||
moduleMetadata: {
|
||||
imports: [ReactiveFormsModule, UiCheckboxModule],
|
||||
},
|
||||
};
|
||||
|
||||
export const standard = () => ({
|
||||
...componentModule,
|
||||
template: '<ui-checkbox></ui-checkbox>',
|
||||
});
|
||||
111
libs/ui/src/checkbox/checkbox.component.ts
Normal file
111
libs/ui/src/checkbox/checkbox.component.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import {
|
||||
AfterViewInit,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Component,
|
||||
EventEmitter,
|
||||
Injector,
|
||||
Input,
|
||||
OnChanges,
|
||||
Output,
|
||||
SimpleChanges,
|
||||
} from '@angular/core';
|
||||
import { ControlValueAccessor, NgControl, NG_VALUE_ACCESSOR } from '@angular/forms';
|
||||
import { uuid } from '@utils/uuid.util';
|
||||
|
||||
/**
|
||||
* A checkbox input. Implemented with control value accessor
|
||||
*
|
||||
* ## Usage
|
||||
* ``import {UiCheckboxModule} from '@ui/checkbox/checkbox.module';``
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ui-checkbox',
|
||||
templateUrl: './checkbox.component.html',
|
||||
styleUrls: ['./checkbox.component.scss'],
|
||||
providers: [
|
||||
{
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: CheckboxComponent,
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CheckboxComponent implements AfterViewInit, ControlValueAccessor, OnChanges {
|
||||
@Input() uiInvalid: boolean;
|
||||
@Input() uiValidationMessage: string;
|
||||
@Input() uiSecondary: boolean;
|
||||
@Input() uiIndeterminate: boolean = false;
|
||||
@Input() uiLabel: string = '';
|
||||
@Input() uiRequired: boolean;
|
||||
@Input() uiId: string = uuid();
|
||||
@Input() uiName: string;
|
||||
@Input() uiAnnounceIfOptional: boolean = false;
|
||||
@Output() uiOnChange: EventEmitter<any> = new EventEmitter();
|
||||
|
||||
name: string | number;
|
||||
|
||||
onTouched: () => {};
|
||||
private onChange: (value: any) => {};
|
||||
private _value: boolean;
|
||||
|
||||
constructor(private injector: Injector, private changeDetectorRef: ChangeDetectorRef) {}
|
||||
|
||||
get currentValue(): boolean {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
get labelText(): string {
|
||||
return `${this.uiLabel}${this._requiredText}`;
|
||||
}
|
||||
|
||||
private get _requiredText() {
|
||||
if (this.uiRequired && !this.uiAnnounceIfOptional) {
|
||||
return ' (obligatoriskt)';
|
||||
}
|
||||
|
||||
if (!this.uiRequired && this.uiAnnounceIfOptional) {
|
||||
return ' (frivilligt)';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
const ngControl: NgControl = this.injector.get(NgControl, null);
|
||||
if (ngControl) {
|
||||
this.name = ngControl.name;
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
const ngControl: NgControl = this.injector.get(NgControl, null);
|
||||
if (ngControl) {
|
||||
this.name = ngControl.name;
|
||||
}
|
||||
}
|
||||
|
||||
checkForChange(value: boolean): void {
|
||||
if (this._value !== !!value) {
|
||||
if (this.onChange) {
|
||||
this.onChange(!!value);
|
||||
}
|
||||
this._value = !!value;
|
||||
this.uiOnChange.emit(!!value);
|
||||
}
|
||||
}
|
||||
|
||||
writeValue(value: any): void {
|
||||
this._value = value;
|
||||
this.changeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
registerOnChange(fn: (value: string) => {}) {
|
||||
this.onChange = fn;
|
||||
}
|
||||
|
||||
registerOnTouched(fn: () => {}) {
|
||||
this.onTouched = fn;
|
||||
}
|
||||
}
|
||||
11
libs/ui/src/checkbox/checkbox.module.ts
Normal file
11
libs/ui/src/checkbox/checkbox.module.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
||||
import { CheckboxComponent } from './checkbox.component';
|
||||
|
||||
@NgModule({
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||
imports: [CommonModule],
|
||||
declarations: [CheckboxComponent],
|
||||
exports: [CheckboxComponent],
|
||||
})
|
||||
export class UiCheckboxModule {}
|
||||
5
libs/ui/src/link-button/link-button-type.enum.ts
Normal file
5
libs/ui/src/link-button/link-button-type.enum.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export enum UiLinkButtonType {
|
||||
PRIMARY = 'primary',
|
||||
SECONDARY = 'secondary',
|
||||
TERTIARY = 'tertiary',
|
||||
}
|
||||
3
libs/ui/src/link-button/link-button.component.html
Normal file
3
libs/ui/src/link-button/link-button.component.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<a [ngClass]="linkButtonClass" [routerLink]="uiRouterLink" [queryParams]="uiQueryParams">
|
||||
<ng-content></ng-content>
|
||||
</a>
|
||||
13
libs/ui/src/link-button/link-button.component.scss
Normal file
13
libs/ui/src/link-button/link-button.component.scss
Normal file
@@ -0,0 +1,13 @@
|
||||
@import 'mixins/buttons';
|
||||
|
||||
.ui-link-button {
|
||||
&--primary {
|
||||
@include msfa__button;
|
||||
}
|
||||
&--secondary {
|
||||
@include msfa__button('secondary');
|
||||
}
|
||||
&--tertiary {
|
||||
@include msfa__button('tertiary');
|
||||
}
|
||||
}
|
||||
26
libs/ui/src/link-button/link-button.component.spec.ts
Normal file
26
libs/ui/src/link-button/link-button.component.spec.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
/* tslint:disable:no-unused-variable */
|
||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { LinkButtonComponent } from './link-button.component';
|
||||
|
||||
describe('LinkButtonComponent', () => {
|
||||
let component: LinkButtonComponent;
|
||||
let fixture: ComponentFixture<LinkButtonComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [LinkButtonComponent],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(LinkButtonComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
15
libs/ui/src/link-button/link-button.component.stories.ts
Normal file
15
libs/ui/src/link-button/link-button.component.stories.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { LinkButtonComponent } from './link-button.component';
|
||||
import { UiLinkButtonModule } from './link-button.module';
|
||||
|
||||
export default { title: 'LinkButton', component: LinkButtonComponent };
|
||||
|
||||
const componentModule = {
|
||||
moduleMetadata: {
|
||||
imports: [UiLinkButtonModule],
|
||||
},
|
||||
};
|
||||
|
||||
export const standard = () => ({
|
||||
...componentModule,
|
||||
template: '<ui-link-button></ui-link-button>',
|
||||
});
|
||||
24
libs/ui/src/link-button/link-button.component.ts
Normal file
24
libs/ui/src/link-button/link-button.component.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
import { Params } from '@angular/router';
|
||||
import { UiLinkButtonType } from './link-button-type.enum';
|
||||
|
||||
@Component({
|
||||
selector: 'ui-link-button',
|
||||
templateUrl: './link-button.component.html',
|
||||
styleUrls: ['./link-button.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class LinkButtonComponent {
|
||||
private readonly _defaultClass = 'ui-link-button';
|
||||
@Input() uiType: UiLinkButtonType = UiLinkButtonType.PRIMARY;
|
||||
@Input() uiSize: 's' | 'm' = 'm';
|
||||
@Input() uiRouterLink: string | string[];
|
||||
@Input() uiQueryParams: Params = null;
|
||||
|
||||
get linkButtonClass(): string {
|
||||
if (this.uiType) {
|
||||
return `${this._defaultClass} ${this._defaultClass}--${this.uiType as string}`;
|
||||
}
|
||||
return this._defaultClass;
|
||||
}
|
||||
}
|
||||
12
libs/ui/src/link-button/link-button.module.ts
Normal file
12
libs/ui/src/link-button/link-button.module.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { LinkButtonComponent } from './link-button.component';
|
||||
|
||||
@NgModule({
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||
declarations: [LinkButtonComponent],
|
||||
imports: [CommonModule, RouterModule],
|
||||
exports: [LinkButtonComponent],
|
||||
})
|
||||
export class UiLinkButtonModule {}
|
||||
6
libs/ui/src/loader/loader-type.enum.ts
Normal file
6
libs/ui/src/loader/loader-type.enum.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export enum UiLoaderType {
|
||||
FULL_SCREEN = 'fullscreen',
|
||||
FULL_BLANK_SCREEN = 'full-blank-screen',
|
||||
ABSOLUTE = 'absolute',
|
||||
PADDED = 'padded',
|
||||
}
|
||||
4
libs/ui/src/loader/loader.component.html
Normal file
4
libs/ui/src/loader/loader.component.html
Normal file
@@ -0,0 +1,4 @@
|
||||
<div [ngClass]="loaderClass">
|
||||
<digi-icon-spinner [ngClass]="spinnerClass"></digi-icon-spinner>
|
||||
<span class="ui-loader__text" *ngIf="uiShowLoadingText">{{uiLoadingText}}</span>
|
||||
</div>
|
||||
46
libs/ui/src/loader/loader.component.scss
Normal file
46
libs/ui/src/loader/loader.component.scss
Normal file
@@ -0,0 +1,46 @@
|
||||
@import 'mixins/backdrop';
|
||||
@import 'variables/gutters';
|
||||
@import 'variables/z-index';
|
||||
|
||||
@keyframes spinning {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.ui-loader {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $digi--layout--gutter--s;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&--padded {
|
||||
padding: $digi--layout--gutter--l;
|
||||
}
|
||||
|
||||
&--absolute {
|
||||
@include msfa__backdrop($msfa__z-index-backdrop, false);
|
||||
}
|
||||
|
||||
&--full-screen {
|
||||
@include msfa__backdrop($msfa__z-index-backdrop);
|
||||
}
|
||||
|
||||
&--full-blank-screen {
|
||||
@include msfa__backdrop($msfa__z-index-backdrop, true, true);
|
||||
}
|
||||
|
||||
&__spinner {
|
||||
display: inline-flex;
|
||||
animation: spinning 1s linear infinite;
|
||||
|
||||
&--s {
|
||||
width: 2.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
&__text {
|
||||
font-size: var(--digi--typography--font-size--s);
|
||||
}
|
||||
}
|
||||
26
libs/ui/src/loader/loader.component.spec.ts
Normal file
26
libs/ui/src/loader/loader.component.spec.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
/* tslint:disable:no-unused-variable */
|
||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { LoaderComponent } from './loader.component';
|
||||
|
||||
describe('LoaderComponent', () => {
|
||||
let component: LoaderComponent;
|
||||
let fixture: ComponentFixture<LoaderComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [LoaderComponent],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(LoaderComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
15
libs/ui/src/loader/loader.component.stories.ts
Normal file
15
libs/ui/src/loader/loader.component.stories.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { LoaderComponent } from './loader.component';
|
||||
import { UiLoaderModule } from './loader.module';
|
||||
|
||||
export default { title: 'Loader', component: LoaderComponent };
|
||||
|
||||
const componentModule = {
|
||||
moduleMetadata: {
|
||||
imports: [UiLoaderModule],
|
||||
},
|
||||
};
|
||||
|
||||
export const standard = () => ({
|
||||
...componentModule,
|
||||
template: '<ui-loader></ui-loader>',
|
||||
});
|
||||
27
libs/ui/src/loader/loader.component.ts
Normal file
27
libs/ui/src/loader/loader.component.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
|
||||
import { UiLoaderType } from './loader-type.enum';
|
||||
|
||||
@Component({
|
||||
selector: 'ui-loader',
|
||||
templateUrl: './loader.component.html',
|
||||
styleUrls: ['./loader.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class LoaderComponent {
|
||||
private readonly _defaultClass = 'ui-loader';
|
||||
@Input() uiType: UiLoaderType = UiLoaderType.PADDED;
|
||||
@Input() uiSize: 's' | 'm' = 'm';
|
||||
@Input() uiShowLoadingText = false;
|
||||
@Input() uiLoadingText = 'Laddar innehåll';
|
||||
|
||||
get loaderClass(): string {
|
||||
if (this.uiType) {
|
||||
return `${this._defaultClass} ${this._defaultClass}--${this.uiType as string}`;
|
||||
}
|
||||
return this._defaultClass;
|
||||
}
|
||||
|
||||
get spinnerClass(): string {
|
||||
return `${this._defaultClass}__spinner ${this._defaultClass}__spinner--${this.uiSize}`;
|
||||
}
|
||||
}
|
||||
11
libs/ui/src/loader/loader.module.ts
Normal file
11
libs/ui/src/loader/loader.module.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
|
||||
import { LoaderComponent } from './loader.component';
|
||||
|
||||
@NgModule({
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||
declarations: [LoaderComponent],
|
||||
imports: [CommonModule],
|
||||
exports: [LoaderComponent],
|
||||
})
|
||||
export class UiLoaderModule {}
|
||||
11
libs/ui/src/skeleton/skeleton.component.html
Normal file
11
libs/ui/src/skeleton/skeleton.component.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<div class="ui-skeleton" data-digi-ng-component>
|
||||
<div *ngIf="uiText" class="ui-skeleton__top"></div>
|
||||
<p *ngIf="uiText" class="ui-skeleton__text">{{ uiText }}</p>
|
||||
<div class="ui-skeleton__header"></div>
|
||||
<div *ngFor="let stub of stubs" class="ui-skeleton__lines">
|
||||
<div class="ui-skeleton__line"></div>
|
||||
<div class="ui-skeleton__line"></div>
|
||||
<div class="ui-skeleton__line"></div>
|
||||
<div class="ui-skeleton__line ui-skeleton__line--last"></div>
|
||||
</div>
|
||||
</div>
|
||||
91
libs/ui/src/skeleton/skeleton.component.scss
Normal file
91
libs/ui/src/skeleton/skeleton.component.scss
Normal file
@@ -0,0 +1,91 @@
|
||||
@import 'variables/gutters';
|
||||
@import 'variables/breakpoints';
|
||||
|
||||
$typography__margin--text: u(6);
|
||||
$animation__duration--sweep: 4s;
|
||||
$ui__color--gray-10: #f5f5f5;
|
||||
$ui__color--gray-20: #efefef;
|
||||
$ui__color--gray-30: #d1d1d1;
|
||||
$ui__color--skeleton-text: linear-gradient(
|
||||
to left,
|
||||
$ui__color--gray-20,
|
||||
$ui__color--gray-20 40%,
|
||||
$ui__color--gray-10 50%,
|
||||
$ui__color--gray-20 60%,
|
||||
$ui__color--gray-20
|
||||
);
|
||||
$ui__color--skeleton-head: linear-gradient(
|
||||
to left,
|
||||
$ui__color--gray-30,
|
||||
$ui__color--gray-30 40%,
|
||||
$ui__color--gray-20 50%,
|
||||
$ui__color--gray-30 60%,
|
||||
$ui__color--gray-30
|
||||
);
|
||||
$skeleton__top--height: 2.5rem;
|
||||
$skeleton__top--height-desktop: 4.5rem;
|
||||
$skeleton__header--height: 1rem + 0.46875rem;
|
||||
$skeleton__header--height-desktop: 2rem + 0.3125rem;
|
||||
|
||||
:host {
|
||||
|
||||
.ui-skeleton__top {
|
||||
animation: ui-skeleton__animation $animation__duration--sweep infinite;
|
||||
height: $skeleton__top--height;
|
||||
background: $ui__color--skeleton-text;
|
||||
background-size: 400%;
|
||||
width: 25%;
|
||||
margin-bottom: var(--digi--layout--gutter--m);
|
||||
|
||||
@media (min-width: $digi--layout--breakpoint--l) {
|
||||
height: $skeleton__top--height-desktop;
|
||||
width: 20%;
|
||||
margin-bottom: $digi--layout--gutter--l;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-skeleton__header {
|
||||
animation: ui-skeleton__animation $animation__duration--sweep infinite;
|
||||
background: $ui__color--skeleton-head;
|
||||
background-size: 400%;
|
||||
width: 33%;
|
||||
height: $skeleton__header--height;
|
||||
margin-bottom: var(----digi--layout--gutter--s);
|
||||
|
||||
@media (min-width: $digi--layout--breakpoint--l) {
|
||||
height: $skeleton__header--height-desktop;
|
||||
margin-bottom: $digi--layout--gutter--l;
|
||||
width: 90%;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-skeleton__line {
|
||||
animation: ui-skeleton__animation $animation__duration--sweep 0.5s infinite;
|
||||
background: $ui__color--skeleton-text;
|
||||
background-size: 400%;
|
||||
margin-bottom: var(--digi--layout--gutter--xs);
|
||||
height: var(--digi--typography--font-size);
|
||||
}
|
||||
|
||||
|
||||
.ui-skeleton__line--last {
|
||||
width: 75%;
|
||||
margin-bottom: var(--digi--layout--gutter);
|
||||
}
|
||||
|
||||
.ui-skeleton__text {
|
||||
|
||||
font-weight: var(--digi--typography--font-weight--semibold);
|
||||
margin-bottom: $typography__margin--text / 2;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes ui-skeleton__animation {
|
||||
0% {
|
||||
background-position-x: 100%;
|
||||
}
|
||||
|
||||
100% {
|
||||
background-position-x: 0%;
|
||||
}
|
||||
}
|
||||
40
libs/ui/src/skeleton/skeleton.component.stories.ts
Normal file
40
libs/ui/src/skeleton/skeleton.component.stories.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { SkeletonComponent } from './skeleton.component';
|
||||
import { UiSkeletonModule } from './skeleton.module';
|
||||
|
||||
export default { title: 'Skeleton', component: SkeletonComponent };
|
||||
|
||||
const componentModule = {
|
||||
moduleMetadata: {
|
||||
imports: [UiSkeletonModule],
|
||||
},
|
||||
};
|
||||
|
||||
export const standard = () => ({
|
||||
...componentModule,
|
||||
template: '<ui-skeleton></ui-skeleton>',
|
||||
});
|
||||
|
||||
export const limitedMultiLine = () => ({
|
||||
...componentModule,
|
||||
template: '<ui-skeleton [uiCount]="2"></ui-skeleton>',
|
||||
});
|
||||
|
||||
export const extensiveMultiLine = () => ({
|
||||
...componentModule,
|
||||
template: '<ui-skeleton [uiCount]="5"></ui-skeleton>',
|
||||
});
|
||||
|
||||
export const paragraphless = () => ({
|
||||
...componentModule,
|
||||
template: '<ui-skeleton [uiCount]="0"></ui-skeleton>',
|
||||
});
|
||||
|
||||
export const customText = () => ({
|
||||
...componentModule,
|
||||
template: '<ui-skeleton uiText="I am a custom text..."></ui-skeleton>',
|
||||
});
|
||||
|
||||
export const noText = () => ({
|
||||
...componentModule,
|
||||
template: '<ui-skeleton uiText=""></ui-skeleton>',
|
||||
});
|
||||
25
libs/ui/src/skeleton/skeleton.component.ts
Normal file
25
libs/ui/src/skeleton/skeleton.component.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
|
||||
/**
|
||||
* Placeholder for content being loaded
|
||||
*
|
||||
* ##Usage
|
||||
* ``import {SkeletonModule} from '@ui/skeleton';``
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ui-skeleton',
|
||||
templateUrl: './skeleton.component.html',
|
||||
styleUrls: ['./skeleton.component.scss'],
|
||||
})
|
||||
export class SkeletonComponent {
|
||||
/**
|
||||
* The amount of skeleton sections
|
||||
*/
|
||||
@Input() uiCount: number | undefined;
|
||||
|
||||
@Input() uiText = 'Laddar sidan';
|
||||
|
||||
get stubs(): string[] {
|
||||
return Array(this.uiCount);
|
||||
}
|
||||
}
|
||||
10
libs/ui/src/skeleton/skeleton.module.ts
Normal file
10
libs/ui/src/skeleton/skeleton.module.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { SkeletonComponent } from './skeleton.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule],
|
||||
declarations: [SkeletonComponent],
|
||||
exports: [SkeletonComponent],
|
||||
})
|
||||
export class UiSkeletonModule {}
|
||||
1
libs/ui/src/test-setup.ts
Normal file
1
libs/ui/src/test-setup.ts
Normal file
@@ -0,0 +1 @@
|
||||
import 'jest-preset-angular';
|
||||
Reference in New Issue
Block a user