import { booleanAttribute, ChangeDetectionStrategy, Component, computed, ElementRef, inject, input, ViewEncapsulation, } from "@angular/core"; import { SdBusyProvider, type SdBusyType } from "./sd-busy.provider"; @Component({ selector: "sd-busy-container", changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, standalone: true, host: { "[attr.data-sd-busy]": "busy() || undefined", "[attr.data-sd-type]": "currType()", }, template: `
@switch (currType()) { @case ("spinner") {
} @case ("bar") {
} @case ("cube") {
} }
@if (message() != undefined) {
{{ message() }}
}
@if (progressPercent() != null) {
}
`, styles: [ /* language=SCSS */ ` @use "../../../scss/commons/variables"; @use "sass:map"; sd-busy-container { display: block; position: relative; top: 0; left: 0; width: 100%; height: 100%; min-width: 70px; min-height: 70px; overflow: auto; > ._screen { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: map.get(variables.$vars, z-index, busy); visibility: hidden; pointer-events: none; opacity: 0; transition: opacity var(--animation-duration); transition-timing-function: linear; > ._progress { display: block; position: absolute; top: 0; left: 0; height: 4px; width: 100%; background-color: var(--background-color); > ._progress-bar { position: absolute; top: 0; left: 0; display: inline-block; content: ""; height: 4px; width: 100%; transition: 0.1s ease-in; transition-property: transform; transform-origin: left; transform: scaleX(0); background-color: var(--theme-primary-default); } } } &[data-sd-busy="true"] { > ._screen { visibility: visible; pointer-events: auto; opacity: 1; } } &[data-sd-type="spinner"] { > ._screen > ._rect { transform: translateY(-100%); transition: 0.1s ease-in; transition-property: transform; > ._indicator { top: 0; width: 30px; height: 30px; margin: 20px auto 0 auto; border: 6px solid var(--background-color); border-radius: 100%; border-bottom-color: var(--theme-primary-default); animation: sd-busy-spin 1s linear infinite; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); > div { display: none; } } > ._message { position: absolute; top: 55px; width: 100%; color: var(--background-color); font-weight: bold; text-align: center; text-shadow: 0 0 2px var(--background-rev-color); } } &[data-sd-busy="true"] { > ._screen > ._rect { transform: none; transition: 0.1s ease-out; } } } &[data-sd-type="bar"] { min-height: 4px; &[data-sd-busy="true"] { > ._screen > ._rect { > ._indicator { position: absolute; top: 0; left: 0; height: 4px; width: 100%; background-color: var(--background-color); &:before, &:after { position: absolute; top: 0; left: 0; display: inline-block; content: ""; height: 4px; width: 100%; transform-origin: left; } &:before { background-color: var(--theme-primary-default); animation: sd-busy-bar-indicator-before 2s infinite ease-in; } &:after { background-color: var(--background-color); animation: sd-busy-bar-indicator-after 2s infinite ease-out; } > div { display: none; } } > ._message { position: absolute; top: 4px; right: 0; display: inline-block; } } } } &[data-sd-type="cube"] { > ._screen > ._rect { > ._indicator { position: absolute; top: calc(50% - 20px); left: calc(50% - 20px); width: 40px; height: 40px; transform: rotateZ(45deg); ._cube1, ._cube2, ._cube3, ._cube4 { float: left; width: 50%; height: 50%; position: relative; &:before { content: ""; position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: var(--trans-light); animation: sd-busy-cube 2.4s infinite linear both; transform-origin: 100% 100%; } } ._cube2 { transform: rotateZ(90deg); &:before { animation-delay: 0.3s; } } ._cube3 { transform: rotateZ(180deg); &:before { animation-delay: 0.6s; } } ._cube4 { transform: rotateZ(270deg); &:before { animation-delay: 0.9s; } } } > ._message { position: absolute; top: 4px; right: 0; display: inline-block; } } } } @keyframes sd-busy-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } @keyframes sd-busy-bar-indicator-before { 0% { transform: scaleX(0); } 60%, 100% { transform: scaleX(1); } } @keyframes sd-busy-bar-indicator-after { 0%, 50% { transform: scaleX(0); } 100% { transform: scaleX(1); } } @keyframes sd-busy-cube { 0%, 10% { transform: perspective(140px) rotateX(-180deg); opacity: 0; } 25%, 75% { transform: perspective(140px) rotateX(0deg); opacity: 1; } 90%, 100% { transform: perspective(140px) rotateY(180deg); opacity: 0; } } `, ], }) export class SdBusyContainer { private readonly _sdBusy = inject(SdBusyProvider); busy = input(false, { transform: booleanAttribute }); message = input(undefined); type = input(undefined); progressPercent = input(undefined); currType = computed(() => this.type() ?? this._sdBusy.type()); constructor() { const el = inject(ElementRef).nativeElement as HTMLElement; el.addEventListener( "keydown", (event) => { if (this.busy()) { event.preventDefault(); event.stopPropagation(); } }, true, ); } }