mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
Compare commits
1 Commits
0a1f25a1ee
...
feature/52
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
20003ab72a |
@@ -16,6 +16,7 @@
|
||||
@import "../../../libs/ui/progress-bar/src/lib/progress-bar.scss";
|
||||
@import "../../../libs/ui/search-bar/src/search-bar.scss";
|
||||
@import "../../../libs/ui/skeleton-loader/src/skeleton-loader.scss";
|
||||
@import "../../../libs/ui/snackbar/src/snackbar.scss";
|
||||
@import "../../../libs/ui/tooltip/src/tooltip.scss";
|
||||
|
||||
.input-control {
|
||||
|
||||
@@ -1,19 +1,45 @@
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { ButtonComponent } from '@isa/ui/buttons';
|
||||
import { injectDialog } from '@isa/ui/dialog';
|
||||
import { SearchItemToRemitDialogComponent } from '@isa/remission/shared/search-item-to-remit-dialog';
|
||||
|
||||
@Component({
|
||||
selector: 'remission-feature-remission-start-card',
|
||||
templateUrl: './remission-start-card.component.html',
|
||||
styleUrl: './remission-start-card.component.scss',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [ButtonComponent],
|
||||
})
|
||||
export class RemissionStartCardComponent {
|
||||
searchItemToRemitDialog = injectDialog(SearchItemToRemitDialogComponent);
|
||||
|
||||
startRemission() {
|
||||
this.searchItemToRemitDialog({ data: { searchTerm: 'Pokemon' } });
|
||||
}
|
||||
}
|
||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
|
||||
import { ButtonComponent } from '@isa/ui/buttons';
|
||||
import { injectDialog } from '@isa/ui/dialog';
|
||||
import { SearchItemToRemitDialogComponent } from '@isa/remission/shared/search-item-to-remit-dialog';
|
||||
import { SnackbarService } from '@isa/ui/snackbar';
|
||||
import { isaActionCheck } from '@isa/icons';
|
||||
@Component({
|
||||
selector: 'remission-feature-remission-start-card',
|
||||
templateUrl: './remission-start-card.component.html',
|
||||
styleUrl: './remission-start-card.component.scss',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [ButtonComponent],
|
||||
})
|
||||
export class RemissionStartCardComponent {
|
||||
#snackbar = inject(SnackbarService);
|
||||
searchItemToRemitDialog = injectDialog(SearchItemToRemitDialogComponent);
|
||||
|
||||
startRemission() {
|
||||
// this.searchItemToRemitDialog({ data: { searchTerm: 'Pokemon' } });
|
||||
this.#snackbar.show({
|
||||
message: 'Remission started successfully!',
|
||||
icon: isaActionCheck,
|
||||
actions: [
|
||||
{
|
||||
label: 'View Remission',
|
||||
handler: () => {
|
||||
// Navigate to the remission list or details page
|
||||
// this.router.navigate(['/remissions']);
|
||||
},
|
||||
color: 'primary',
|
||||
},
|
||||
{
|
||||
label: 'New Remission',
|
||||
handler: () => {
|
||||
// Open the dialog to start a new remission
|
||||
},
|
||||
color: 'secondary',
|
||||
},
|
||||
],
|
||||
type: 'brand',
|
||||
position: 'top-right',
|
||||
duration: 3000,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
7
libs/ui/snackbar/README.md
Normal file
7
libs/ui/snackbar/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# ui-snackbar
|
||||
|
||||
This library was generated with [Nx](https://nx.dev).
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `nx test ui-snackbar` to execute the unit tests.
|
||||
34
libs/ui/snackbar/eslint.config.cjs
Normal file
34
libs/ui/snackbar/eslint.config.cjs
Normal file
@@ -0,0 +1,34 @@
|
||||
const nx = require('@nx/eslint-plugin');
|
||||
const baseConfig = require('../../../eslint.config.js');
|
||||
|
||||
module.exports = [
|
||||
...baseConfig,
|
||||
...nx.configs['flat/angular'],
|
||||
...nx.configs['flat/angular-template'],
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
rules: {
|
||||
'@angular-eslint/directive-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'attribute',
|
||||
prefix: 'ui',
|
||||
style: 'camelCase',
|
||||
},
|
||||
],
|
||||
'@angular-eslint/component-selector': [
|
||||
'error',
|
||||
{
|
||||
type: 'element',
|
||||
prefix: 'ui',
|
||||
style: 'kebab-case',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.html'],
|
||||
// Override or add rules here
|
||||
rules: {},
|
||||
},
|
||||
];
|
||||
20
libs/ui/snackbar/project.json
Normal file
20
libs/ui/snackbar/project.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "ui-snackbar",
|
||||
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "libs/ui/snackbar/src",
|
||||
"prefix": "ui",
|
||||
"projectType": "library",
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"test": {
|
||||
"executor": "@nx/vite:test",
|
||||
"outputs": ["{options.reportsDirectory}"],
|
||||
"options": {
|
||||
"reportsDirectory": "../../../coverage/libs/ui/snackbar"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nx/eslint:lint"
|
||||
}
|
||||
}
|
||||
}
|
||||
12
libs/ui/snackbar/src/index.ts
Normal file
12
libs/ui/snackbar/src/index.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
// Services
|
||||
export * from './lib/snackbar.service';
|
||||
|
||||
// Components
|
||||
export * from './lib/snackbar.component';
|
||||
|
||||
// Types and interfaces
|
||||
export * from './lib/snackbar.types';
|
||||
export * from './lib/snackbar-ref';
|
||||
|
||||
// Tokens (for advanced usage)
|
||||
export * from './lib/snackbar.tokens';
|
||||
223
libs/ui/snackbar/src/lib/_snackbar.scss
Normal file
223
libs/ui/snackbar/src/lib/_snackbar.scss
Normal file
@@ -0,0 +1,223 @@
|
||||
// Main snackbar styles (following button patterns)
|
||||
.ui-snackbar {
|
||||
@apply font-sans;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
border-radius: 6.25rem;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
|
||||
position: relative;
|
||||
min-width: 16rem;
|
||||
}
|
||||
|
||||
// Snackbar content area
|
||||
.ui-snackbar__content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.ui-snackbar__icon {
|
||||
flex-shrink: 0;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
}
|
||||
|
||||
.ui-snackbar__message {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
// Snackbar actions area
|
||||
.ui-snackbar__actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.ui-snackbar__action {
|
||||
flex-shrink: 0;
|
||||
|
||||
// Override button min-width for snackbar actions
|
||||
&.ui-text-button {
|
||||
min-width: auto;
|
||||
padding-left: 0.75rem;
|
||||
padding-right: 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-snackbar__dismiss {
|
||||
flex-shrink: 0;
|
||||
|
||||
// Make dismiss button smaller
|
||||
&.ui-icon-button {
|
||||
width: 1.75rem;
|
||||
height: 1.75rem;
|
||||
min-width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
// Size variants (following button patterns)
|
||||
.ui-snackbar__small {
|
||||
padding: 0.375rem 0.75rem;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
|
||||
.ui-snackbar__icon {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-snackbar__medium {
|
||||
padding: 0.5rem 1.25rem;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
|
||||
.ui-snackbar__icon {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-snackbar__large {
|
||||
padding: 0.75rem 1.5rem;
|
||||
font-size: 1rem;
|
||||
line-height: 1.5rem;
|
||||
|
||||
.ui-snackbar__icon {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-snackbar__message {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
white-space: nowrap;
|
||||
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
// Type/color variants (following button patterns)
|
||||
.ui-snackbar__primary {
|
||||
@apply bg-isa-secondary-600 text-isa-white;
|
||||
|
||||
.ui-snackbar__icon {
|
||||
@apply text-isa-white;
|
||||
}
|
||||
|
||||
.ui-snackbar__action {
|
||||
@apply text-isa-white hover:bg-isa-secondary-700 hover:bg-opacity-20;
|
||||
}
|
||||
|
||||
.ui-snackbar__dismiss {
|
||||
@apply text-isa-white hover:bg-isa-secondary-700 hover:bg-opacity-20;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-snackbar__secondary {
|
||||
@apply border border-solid border-isa-secondary-600 text-isa-secondary-600 bg-white;
|
||||
|
||||
.ui-snackbar__icon {
|
||||
@apply text-isa-secondary-600;
|
||||
}
|
||||
|
||||
.ui-snackbar__action {
|
||||
@apply text-isa-secondary-600 hover:bg-isa-neutral-100;
|
||||
}
|
||||
|
||||
.ui-snackbar__dismiss {
|
||||
@apply text-isa-secondary-600 hover:bg-isa-neutral-100;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-snackbar__brand {
|
||||
@apply bg-isa-accent-red text-isa-white;
|
||||
|
||||
.ui-snackbar__icon {
|
||||
@apply text-isa-white;
|
||||
}
|
||||
|
||||
.ui-snackbar__action {
|
||||
@apply text-isa-white hover:bg-isa-shades-red-600 hover:bg-opacity-20;
|
||||
}
|
||||
|
||||
.ui-snackbar__dismiss {
|
||||
@apply text-isa-white hover:bg-isa-shades-red-600 hover:bg-opacity-20;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-snackbar__tertiary {
|
||||
@apply bg-isa-neutral-300 text-isa-neutral-900;
|
||||
|
||||
.ui-snackbar__icon {
|
||||
@apply text-isa-neutral-700;
|
||||
}
|
||||
|
||||
.ui-snackbar__action {
|
||||
@apply text-isa-neutral-900 hover:bg-isa-neutral-400 hover:bg-opacity-50;
|
||||
}
|
||||
|
||||
.ui-snackbar__dismiss {
|
||||
@apply text-isa-neutral-700 hover:bg-isa-neutral-400 hover:bg-opacity-50;
|
||||
}
|
||||
}
|
||||
|
||||
// Animations
|
||||
@keyframes slideIn {
|
||||
from {
|
||||
transform: translateX(100%);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: translateX(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideOut {
|
||||
from {
|
||||
transform: translateX(0);
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
transform: translateX(100%);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Position-specific animations
|
||||
.ui-snackbar-overlay--top-left,
|
||||
.ui-snackbar-overlay--bottom-left {
|
||||
@keyframes slideIn {
|
||||
from {
|
||||
transform: translateX(-100%);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: translateX(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideOut {
|
||||
from {
|
||||
transform: translateX(0);
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
transform: translateX(-100%);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
38
libs/ui/snackbar/src/lib/snackbar-ref.ts
Normal file
38
libs/ui/snackbar/src/lib/snackbar-ref.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { Subject } from 'rxjs';
|
||||
import { SnackbarComponent } from './snackbar.component';
|
||||
import { InternalSnackbarData } from './snackbar.types';
|
||||
import { ComponentRef } from '@angular/core';
|
||||
|
||||
/**
|
||||
* Reference to a snackbar instance.
|
||||
* Allows programmatic control over the snackbar.
|
||||
*/
|
||||
export class SnackbarRef {
|
||||
readonly id = crypto.randomUUID();
|
||||
|
||||
/** Subject that emits when the snackbar is dismissed */
|
||||
#dismissed = new Subject<void>();
|
||||
|
||||
#componentRef: ComponentRef<SnackbarComponent> | undefined;
|
||||
|
||||
get componentRef() {
|
||||
return this.#componentRef;
|
||||
}
|
||||
|
||||
/** Observable that emits after the snackbar is dismissed */
|
||||
readonly dismissed$ = this.#dismissed.asObservable();
|
||||
|
||||
constructor(public readonly data: Readonly<InternalSnackbarData>) {}
|
||||
|
||||
/**
|
||||
* Dismisses the snackbar
|
||||
*/
|
||||
dismiss(): void {
|
||||
this.#dismissed.next();
|
||||
this.#dismissed.complete();
|
||||
}
|
||||
|
||||
_setComponentRef(componentRef: ComponentRef<SnackbarComponent>): void {
|
||||
this.#componentRef = componentRef;
|
||||
}
|
||||
}
|
||||
24
libs/ui/snackbar/src/lib/snackbar.component.html
Normal file
24
libs/ui/snackbar/src/lib/snackbar.component.html
Normal file
@@ -0,0 +1,24 @@
|
||||
<div class="ui-snackbar__content">
|
||||
@if (icon()) {
|
||||
<ng-icon name="snackbarIcon" class="ui-snackbar__icon" aria-hidden="true">
|
||||
</ng-icon>
|
||||
}
|
||||
|
||||
<span class="ui-snackbar__message">{{ message() }}</span>
|
||||
</div>
|
||||
|
||||
@if (actions().length > 0) {
|
||||
<div class="ui-snackbar__actions">
|
||||
@for (action of actions(); track action.label) {
|
||||
<button
|
||||
uiTextButton
|
||||
[color]="action.color === 'secondary' ? 'strong' : 'normal'"
|
||||
size="medium"
|
||||
(click)="onAction(action)"
|
||||
class="ui-snackbar__action"
|
||||
>
|
||||
{{ action.label }}
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
84
libs/ui/snackbar/src/lib/snackbar.component.ts
Normal file
84
libs/ui/snackbar/src/lib/snackbar.component.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
computed,
|
||||
inject,
|
||||
ViewEncapsulation,
|
||||
HostListener,
|
||||
} from '@angular/core';
|
||||
import { NgIconComponent, provideIcons } from '@ng-icons/core';
|
||||
import { TextButtonComponent, IconButtonComponent } from '@isa/ui/buttons';
|
||||
import { SNACKBAR_DATA, SNACKBAR_REF } from './snackbar.tokens';
|
||||
import { SnackbarAction } from './snackbar.types';
|
||||
import { isaActionClose } from '@isa/icons';
|
||||
|
||||
/**
|
||||
* Individual snackbar component that displays messages with optional actions.
|
||||
* Follows the Button component styling patterns for visual consistency.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'ui-snackbar',
|
||||
templateUrl: './snackbar.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
standalone: true,
|
||||
imports: [NgIconComponent, TextButtonComponent],
|
||||
providers: [provideIcons({ isaActionClose })],
|
||||
host: {
|
||||
'[class]': '["ui-snackbar", typeClass(), sizeClass()]',
|
||||
'[attr.role]': '"alert"',
|
||||
'[attr.aria-live]': '"polite"',
|
||||
'[attr.data-what]': '"snackbar"',
|
||||
'[attr.data-which]': 'data.type',
|
||||
},
|
||||
})
|
||||
export class SnackbarComponent {
|
||||
/** Injected snackbar data */
|
||||
readonly data = inject(SNACKBAR_DATA);
|
||||
|
||||
/** Injected snackbar reference */
|
||||
private readonly snackbarRef = inject(SNACKBAR_REF);
|
||||
|
||||
/** The type/color theme of the snackbar */
|
||||
type = computed(() => this.data.type);
|
||||
|
||||
/** The size of the snackbar */
|
||||
size = computed(() => this.data.size);
|
||||
|
||||
/** Computed CSS class based on the type */
|
||||
typeClass = computed(() => `ui-snackbar__${this.type()}`);
|
||||
|
||||
/** Computed CSS class based on the size */
|
||||
sizeClass = computed(() => `ui-snackbar__${this.size()}`);
|
||||
|
||||
/** The message to display */
|
||||
message = computed(() => this.data.message);
|
||||
|
||||
/** Optional icon to display */
|
||||
icon = computed(() => {
|
||||
return this.data.icon || undefined;
|
||||
});
|
||||
|
||||
/** Action buttons configuration */
|
||||
actions = computed(() => this.data.actions || []);
|
||||
|
||||
/**
|
||||
* Handles action button clicks
|
||||
*/
|
||||
onAction(action: SnackbarAction): void {
|
||||
// this.snackbarRef._triggerAction(action.label);
|
||||
action.handler();
|
||||
|
||||
// Dismiss snackbar after action if configured
|
||||
if (!action.color || action.color === 'primary') {
|
||||
this.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismisses the snackbar
|
||||
*/
|
||||
dismiss(): void {
|
||||
this.snackbarRef.dismiss();
|
||||
}
|
||||
}
|
||||
224
libs/ui/snackbar/src/lib/snackbar.service.ts
Normal file
224
libs/ui/snackbar/src/lib/snackbar.service.ts
Normal file
@@ -0,0 +1,224 @@
|
||||
import { Injectable, inject, Injector, Provider } from '@angular/core';
|
||||
import { Overlay, OverlayRef, PositionStrategy } from '@angular/cdk/overlay';
|
||||
import { ComponentPortal } from '@angular/cdk/portal';
|
||||
import {
|
||||
InternalSnackbarData,
|
||||
SnackbarPosition,
|
||||
SnackbarConfig,
|
||||
DEFAULT_SNACKBAR_CONFIG,
|
||||
SnackbarData,
|
||||
} from './snackbar.types';
|
||||
import { SnackbarRef } from './snackbar-ref';
|
||||
import { SnackbarComponent } from './snackbar.component';
|
||||
import { SNACKBAR_DATA, SNACKBAR_REF } from './snackbar.tokens';
|
||||
import { provideIcons } from '@ng-icons/core';
|
||||
|
||||
const POSITION_DISTANCE = '1rem';
|
||||
|
||||
/**
|
||||
* Service for displaying snackbar notifications.
|
||||
* Manages overlay creation, positioning, and snackbar lifecycle.
|
||||
*/
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class SnackbarService {
|
||||
#overlay = inject(Overlay);
|
||||
#injector = inject(Injector);
|
||||
|
||||
/** Snackbar Ref Queue */
|
||||
#snackbarRefQueue: SnackbarRef[] = [];
|
||||
|
||||
/** Currently displayed snackbar reference */
|
||||
#currentSnackbarRef: SnackbarRef | null = null;
|
||||
|
||||
show(data: SnackbarData): SnackbarRef {
|
||||
// Apply default configuration
|
||||
const d: InternalSnackbarData = {
|
||||
...DEFAULT_SNACKBAR_CONFIG,
|
||||
...data,
|
||||
panelClass: data.panelClass || [],
|
||||
};
|
||||
|
||||
// Create SnackbarRef with enhanced dismiss callback
|
||||
const snackbarRef = new SnackbarRef(d);
|
||||
|
||||
// Check if another snackbar is currently displayed
|
||||
if (this.#currentSnackbarRef) {
|
||||
// Add to queue
|
||||
this.#snackbarRefQueue.push(snackbarRef);
|
||||
return snackbarRef;
|
||||
}
|
||||
|
||||
// Display immediately
|
||||
this.#displaySnackbar(snackbarRef);
|
||||
return snackbarRef;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismisses a specific snackbar by ID
|
||||
*/
|
||||
dismiss(snackbarId: string): void {
|
||||
// Check if it's the currently displayed snackbar
|
||||
if (
|
||||
this.#currentSnackbarRef?.id === snackbarId &&
|
||||
this.#currentSnackbarRef
|
||||
) {
|
||||
this.#currentSnackbarRef.dismiss();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if it's in the queue
|
||||
const queueIndex = this.#snackbarRefQueue.findIndex(
|
||||
(e) => e.id === snackbarId,
|
||||
);
|
||||
if (queueIndex !== -1) {
|
||||
// Remove from queue and clean up
|
||||
this.#snackbarRefQueue.splice(queueIndex, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismisses all snackbars (current and queued)
|
||||
*/
|
||||
dismissAll(): void {
|
||||
// Dismiss current snackbar
|
||||
if (this.#currentSnackbarRef) {
|
||||
this.#currentSnackbarRef.dismiss();
|
||||
}
|
||||
|
||||
// Clear the queue
|
||||
this.#snackbarRefQueue.forEach((ref) => ref.dismiss());
|
||||
}
|
||||
|
||||
#createOverlayRef(position: SnackbarPosition): OverlayRef {
|
||||
// Create a new overlay if it doesn't exist
|
||||
const overlayRef = this.#overlay.create({
|
||||
positionStrategy: this.#createPositionStrategy(position),
|
||||
panelClass: `ui-snackbar__${position}`,
|
||||
hasBackdrop: false,
|
||||
scrollStrategy: this.#overlay.scrollStrategies.reposition(),
|
||||
});
|
||||
|
||||
return overlayRef;
|
||||
}
|
||||
|
||||
#createPositionStrategy(
|
||||
position: SnackbarPosition,
|
||||
): PositionStrategy | undefined {
|
||||
switch (position) {
|
||||
case 'top-right':
|
||||
return this.#overlay
|
||||
.position()
|
||||
.global()
|
||||
.top(POSITION_DISTANCE)
|
||||
.right(POSITION_DISTANCE);
|
||||
case 'top-left':
|
||||
return this.#overlay
|
||||
.position()
|
||||
.global()
|
||||
.top(POSITION_DISTANCE)
|
||||
.left(POSITION_DISTANCE);
|
||||
case 'bottom-right':
|
||||
return this.#overlay
|
||||
.position()
|
||||
.global()
|
||||
.bottom(POSITION_DISTANCE)
|
||||
.right(POSITION_DISTANCE);
|
||||
case 'bottom-left':
|
||||
return this.#overlay
|
||||
.position()
|
||||
.global()
|
||||
.bottom(POSITION_DISTANCE)
|
||||
.left(POSITION_DISTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a snackbar immediately
|
||||
*/
|
||||
#displaySnackbar(snackbarRef: SnackbarRef): void {
|
||||
if (this.#currentSnackbarRef) {
|
||||
console.warn(
|
||||
'Another snackbar is already displayed. Please wait until it is dismissed.',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set current snackbar tracking
|
||||
this.#currentSnackbarRef = snackbarRef;
|
||||
|
||||
// Get overlay reference
|
||||
const overlayRef = this.#createOverlayRef(snackbarRef.data.position);
|
||||
|
||||
// Check if overlay is disposed or invalid
|
||||
if (overlayRef.hasAttached()) {
|
||||
console.warn(
|
||||
'Overlay reference is invalid or already has content attached',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create component portal
|
||||
const componentPortal = new ComponentPortal(
|
||||
SnackbarComponent,
|
||||
null,
|
||||
this.#createInjector(snackbarRef.data, snackbarRef),
|
||||
);
|
||||
|
||||
// Attach to overlay
|
||||
const componentRef = overlayRef.attach(componentPortal);
|
||||
|
||||
setTimeout(() => {
|
||||
snackbarRef.dismiss();
|
||||
}, snackbarRef.data.duration);
|
||||
|
||||
snackbarRef._setComponentRef(componentRef);
|
||||
|
||||
snackbarRef.dismissed$.subscribe(() => {
|
||||
overlayRef.detach();
|
||||
|
||||
this.#currentSnackbarRef = null;
|
||||
this.#processQueue();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the queue after current snackbar is dismissed
|
||||
*/
|
||||
#processQueue(): void {
|
||||
if (this.#snackbarRefQueue.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get next snackbar from queue
|
||||
const nextSnackbarRef = this.#snackbarRefQueue.shift();
|
||||
if (!nextSnackbarRef) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.#displaySnackbar(nextSnackbarRef);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an injector for the snackbar component
|
||||
*/
|
||||
#createInjector(
|
||||
data: InternalSnackbarData,
|
||||
snackbarRef: SnackbarRef,
|
||||
): Injector {
|
||||
const providers: Provider[] = [
|
||||
{ provide: SNACKBAR_DATA, useValue: data },
|
||||
{ provide: SNACKBAR_REF, useValue: snackbarRef },
|
||||
];
|
||||
|
||||
if (data.icon) {
|
||||
providers.push(provideIcons({ snackbarIcon: data.icon }));
|
||||
}
|
||||
|
||||
return Injector.create({
|
||||
parent: this.#injector,
|
||||
providers,
|
||||
});
|
||||
}
|
||||
}
|
||||
15
libs/ui/snackbar/src/lib/snackbar.tokens.ts
Normal file
15
libs/ui/snackbar/src/lib/snackbar.tokens.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { InjectionToken } from '@angular/core';
|
||||
import { InternalSnackbarData } from './snackbar.types';
|
||||
import { SnackbarRef } from './snackbar-ref';
|
||||
|
||||
/**
|
||||
* Injection token for the snackbar data
|
||||
*/
|
||||
export const SNACKBAR_DATA = new InjectionToken<InternalSnackbarData>(
|
||||
'SNACKBAR_DATA',
|
||||
);
|
||||
|
||||
/**
|
||||
* Injection token for the snackbar reference
|
||||
*/
|
||||
export const SNACKBAR_REF = new InjectionToken<SnackbarRef>('SNACKBAR_REF');
|
||||
91
libs/ui/snackbar/src/lib/snackbar.types.ts
Normal file
91
libs/ui/snackbar/src/lib/snackbar.types.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* Available snackbar color types following the Button component patterns
|
||||
*/
|
||||
export const SnackbarType = {
|
||||
Primary: 'primary',
|
||||
Secondary: 'secondary',
|
||||
Brand: 'brand',
|
||||
Tertiary: 'tertiary',
|
||||
} as const;
|
||||
|
||||
export type SnackbarType = (typeof SnackbarType)[keyof typeof SnackbarType];
|
||||
|
||||
/**
|
||||
* Available snackbar sizes following the Button component patterns
|
||||
*/
|
||||
export const SnackbarSize = {
|
||||
Small: 'small',
|
||||
Medium: 'medium',
|
||||
Large: 'large',
|
||||
} as const;
|
||||
|
||||
export type SnackbarSize = (typeof SnackbarSize)[keyof typeof SnackbarSize];
|
||||
|
||||
/**
|
||||
* Available snackbar positions
|
||||
*/
|
||||
export const SnackbarPosition = {
|
||||
TopRight: 'top-right',
|
||||
BottomRight: 'bottom-right',
|
||||
BottomLeft: 'bottom-left',
|
||||
TopLeft: 'top-left',
|
||||
} as const;
|
||||
|
||||
export type SnackbarPosition =
|
||||
(typeof SnackbarPosition)[keyof typeof SnackbarPosition];
|
||||
|
||||
/**
|
||||
* Configuration for a snackbar action button
|
||||
*/
|
||||
export interface SnackbarAction {
|
||||
/** Label text for the action button */
|
||||
label: string;
|
||||
/** Handler function called when the action is clicked */
|
||||
handler: () => void;
|
||||
/** Optional color for the action button */
|
||||
color?: 'primary' | 'secondary';
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration options for creating a snackbar
|
||||
*/
|
||||
export interface SnackbarConfig {
|
||||
/** The type/color theme of the snackbar */
|
||||
type?: SnackbarType;
|
||||
/** The size of the snackbar */
|
||||
size?: SnackbarSize;
|
||||
/** Duration in milliseconds before auto-dismiss (0 = no auto-dismiss) */
|
||||
duration?: number;
|
||||
/** Position of the snackbar on screen */
|
||||
position?: SnackbarPosition;
|
||||
|
||||
/** Additional CSS classes to apply */
|
||||
panelClass?: string[];
|
||||
}
|
||||
|
||||
export interface SnackbarData extends SnackbarConfig {
|
||||
/** The message to display */
|
||||
message: string;
|
||||
/** Optional icon name to display */
|
||||
icon?: string;
|
||||
|
||||
/** Action buttons to display in the snackbar */
|
||||
actions?: SnackbarAction[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Data passed to a snackbar component instance
|
||||
*/
|
||||
export type InternalSnackbarData = SnackbarData & Required<SnackbarConfig>;
|
||||
|
||||
/**
|
||||
* Default configuration values for snackbars
|
||||
*/
|
||||
export const DEFAULT_SNACKBAR_CONFIG: Required<
|
||||
Omit<SnackbarConfig, 'actions' | 'icon' | 'panelClass'>
|
||||
> = {
|
||||
type: SnackbarType.Primary,
|
||||
size: SnackbarSize.Medium,
|
||||
duration: 5000,
|
||||
position: SnackbarPosition.BottomRight,
|
||||
};
|
||||
1
libs/ui/snackbar/src/snackbar.scss
Normal file
1
libs/ui/snackbar/src/snackbar.scss
Normal file
@@ -0,0 +1 @@
|
||||
@import './lib/snackbar';
|
||||
13
libs/ui/snackbar/src/test-setup.ts
Normal file
13
libs/ui/snackbar/src/test-setup.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import '@angular/compiler';
|
||||
import '@analogjs/vitest-angular/setup-zone';
|
||||
|
||||
import {
|
||||
BrowserTestingModule,
|
||||
platformBrowserTesting,
|
||||
} from '@angular/platform-browser/testing';
|
||||
import { getTestBed } from '@angular/core/testing';
|
||||
|
||||
getTestBed().initTestEnvironment(
|
||||
BrowserTestingModule,
|
||||
platformBrowserTesting(),
|
||||
);
|
||||
30
libs/ui/snackbar/tsconfig.json
Normal file
30
libs/ui/snackbar/tsconfig.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"importHelpers": true,
|
||||
"moduleResolution": "bundler",
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"module": "preserve"
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"enableI18nLegacyMessageIdFormat": false,
|
||||
"strictInjectionParameters": true,
|
||||
"strictInputAccessModifiers": true,
|
||||
"typeCheckHostBindings": true,
|
||||
"strictTemplates": true
|
||||
},
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
27
libs/ui/snackbar/tsconfig.lib.json
Normal file
27
libs/ui/snackbar/tsconfig.lib.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../dist/out-tsc",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"inlineSources": true,
|
||||
"types": []
|
||||
},
|
||||
"exclude": [
|
||||
"src/**/*.spec.ts",
|
||||
"src/test-setup.ts",
|
||||
"jest.config.ts",
|
||||
"src/**/*.test.ts",
|
||||
"vite.config.ts",
|
||||
"vite.config.mts",
|
||||
"vitest.config.ts",
|
||||
"vitest.config.mts",
|
||||
"src/**/*.test.tsx",
|
||||
"src/**/*.spec.tsx",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.jsx",
|
||||
"src/**/*.spec.jsx"
|
||||
],
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
29
libs/ui/snackbar/tsconfig.spec.json
Normal file
29
libs/ui/snackbar/tsconfig.spec.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../dist/out-tsc",
|
||||
"types": [
|
||||
"vitest/globals",
|
||||
"vitest/importMeta",
|
||||
"vite/client",
|
||||
"node",
|
||||
"vitest"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"vite.config.ts",
|
||||
"vite.config.mts",
|
||||
"vitest.config.ts",
|
||||
"vitest.config.mts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.tsx",
|
||||
"src/**/*.spec.tsx",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.jsx",
|
||||
"src/**/*.spec.jsx",
|
||||
"src/**/*.d.ts"
|
||||
],
|
||||
"files": ["src/test-setup.ts"]
|
||||
}
|
||||
27
libs/ui/snackbar/vite.config.mts
Normal file
27
libs/ui/snackbar/vite.config.mts
Normal file
@@ -0,0 +1,27 @@
|
||||
/// <reference types='vitest' />
|
||||
import { defineConfig } from 'vite';
|
||||
import angular from '@analogjs/vite-plugin-angular';
|
||||
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
|
||||
import { nxCopyAssetsPlugin } from '@nx/vite/plugins/nx-copy-assets.plugin';
|
||||
|
||||
export default defineConfig(() => ({
|
||||
root: __dirname,
|
||||
cacheDir: '../../../node_modules/.vite/libs/ui/snackbar',
|
||||
plugins: [angular(), nxViteTsPaths(), nxCopyAssetsPlugin(['*.md'])],
|
||||
// Uncomment this if you are using workers.
|
||||
// worker: {
|
||||
// plugins: [ nxViteTsPaths() ],
|
||||
// },
|
||||
test: {
|
||||
watch: false,
|
||||
globals: true,
|
||||
environment: 'jsdom',
|
||||
include: ['{src,tests}/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||
setupFiles: ['src/test-setup.ts'],
|
||||
reporters: ['default'],
|
||||
coverage: {
|
||||
reportsDirectory: '../../../coverage/libs/ui/snackbar',
|
||||
provider: 'v8' as const,
|
||||
},
|
||||
},
|
||||
}));
|
||||
56666
package-lock.json
generated
56666
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
264
package.json
264
package.json
@@ -1,132 +1,132 @@
|
||||
{
|
||||
"name": "hima",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "nx serve isa-app --ssl",
|
||||
"pretest": "npx trash-cli testresults",
|
||||
"test": "npx nx run-many --tuiAutoExit true -t test --exclude isa-app",
|
||||
"ci": "npx nx run-many -t test --exclude isa-app -c ci",
|
||||
"build": "nx build isa-app --configuration=development",
|
||||
"build-prod": "nx build isa-app --configuration=production",
|
||||
"lint": "nx lint",
|
||||
"e2e": "nx e2e",
|
||||
"generate:swagger": "nx run-many -t generate -p tag:generated,swagger",
|
||||
"fix:files:swagger": "node ./tools/fix-files.js generated/swagger",
|
||||
"prettier": "prettier --write .",
|
||||
"pretty-quick": "pretty-quick --staged",
|
||||
"prepare": "husky",
|
||||
"storybook": "npx nx run isa-app:storybook"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "20.0.3",
|
||||
"@angular/cdk": "20.0.3",
|
||||
"@angular/common": "20.0.3",
|
||||
"@angular/compiler": "20.0.3",
|
||||
"@angular/core": "20.0.3",
|
||||
"@angular/forms": "20.0.3",
|
||||
"@angular/localize": "20.0.3",
|
||||
"@angular/platform-browser": "20.0.3",
|
||||
"@angular/platform-browser-dynamic": "20.0.3",
|
||||
"@angular/router": "20.0.3",
|
||||
"@angular/service-worker": "20.0.3",
|
||||
"@microsoft/signalr": "^8.0.7",
|
||||
"@ng-icons/core": "32.0.0",
|
||||
"@ng-icons/material-icons": "32.0.0",
|
||||
"@ngrx/component-store": "19.2.1",
|
||||
"@ngrx/effects": "19.2.1",
|
||||
"@ngrx/entity": "19.2.1",
|
||||
"@ngrx/operators": "19.2.1",
|
||||
"@ngrx/signals": "19.2.1",
|
||||
"@ngrx/store": "19.2.1",
|
||||
"@ngrx/store-devtools": "19.2.1",
|
||||
"angular-oauth2-oidc": "20.0.0",
|
||||
"angular-oauth2-oidc-jwks": "20.0.0",
|
||||
"date-fns": "^4.1.0",
|
||||
"lodash": "^4.17.21",
|
||||
"moment": "^2.30.1",
|
||||
"ng2-pdf-viewer": "^10.4.0",
|
||||
"ngx-matomo-client": "^7.0.1",
|
||||
"parse-duration": "^2.1.3",
|
||||
"rxjs": "~7.8.2",
|
||||
"scandit-web-datacapture-barcode": "^6.28.1",
|
||||
"scandit-web-datacapture-core": "^6.28.1",
|
||||
"tslib": "^2.3.0",
|
||||
"uuid": "^8.3.2",
|
||||
"zod": "^3.24.2",
|
||||
"zone.js": "~0.15.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@analogjs/vite-plugin-angular": "~1.17.1",
|
||||
"@analogjs/vitest-angular": "~1.17.1",
|
||||
"@angular-devkit/build-angular": "20.0.3",
|
||||
"@angular-devkit/core": "20.0.2",
|
||||
"@angular-devkit/schematics": "20.0.2",
|
||||
"@angular/build": "20.0.3",
|
||||
"@angular/cli": "~20.0.0",
|
||||
"@angular/compiler-cli": "20.0.3",
|
||||
"@angular/language-service": "20.0.3",
|
||||
"@angular/pwa": "20.0.2",
|
||||
"@eslint/js": "^9.8.0",
|
||||
"@ngneat/spectator": "19.6.2",
|
||||
"@nx/angular": "21.2.1",
|
||||
"@nx/eslint": "21.2.1",
|
||||
"@nx/eslint-plugin": "21.2.1",
|
||||
"@nx/jest": "21.2.1",
|
||||
"@nx/js": "21.2.1",
|
||||
"@nx/storybook": "21.2.1",
|
||||
"@nx/vite": "21.2.1",
|
||||
"@nx/web": "21.2.1",
|
||||
"@nx/workspace": "21.2.1",
|
||||
"@schematics/angular": "20.0.2",
|
||||
"@storybook/addon-docs": "^9.0.11",
|
||||
"@storybook/angular": "^9.0.5",
|
||||
"@swc-node/register": "1.10.10",
|
||||
"@swc/core": "1.12.1",
|
||||
"@swc/helpers": "0.5.17",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/lodash": "^4.17.16",
|
||||
"@types/node": "18.16.9",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"@typescript-eslint/utils": "^8.33.1",
|
||||
"@vitest/coverage-v8": "^3.1.1",
|
||||
"@vitest/ui": "^3.1.1",
|
||||
"angular-eslint": "20.1.1",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"eslint": "^9.28.0",
|
||||
"eslint-config-prettier": "^10.1.5",
|
||||
"husky": "^9.1.7",
|
||||
"jest": "^29.7.0",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"jest-environment-node": "^29.7.0",
|
||||
"jest-junit": "^16.0.0",
|
||||
"jest-preset-angular": "14.6.0",
|
||||
"jiti": "2.4.2",
|
||||
"jsdom": "~22.1.0",
|
||||
"jsonc-eslint-parser": "^2.1.0",
|
||||
"ng-mocks": "14.13.5",
|
||||
"ng-packagr": "20.0.1",
|
||||
"ng-swagger-gen": "^2.3.1",
|
||||
"nx": "21.2.1",
|
||||
"postcss": "^8.5.3",
|
||||
"postcss-url": "~10.1.3",
|
||||
"prettier": "^3.5.2",
|
||||
"pretty-quick": "~4.0.0",
|
||||
"storybook": "^9.0.5",
|
||||
"tailwindcss": "^3.4.14",
|
||||
"ts-jest": "^29.1.0",
|
||||
"ts-node": "10.9.1",
|
||||
"typescript": "5.8.3",
|
||||
"typescript-eslint": "^8.33.1",
|
||||
"vite": "6.3.5",
|
||||
"vitest": "^3.1.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/linux-x64": "^0.25.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22.0.0",
|
||||
"npm": ">=10.0.0"
|
||||
}
|
||||
}
|
||||
{
|
||||
"name": "hima",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "nx serve isa-app --ssl",
|
||||
"pretest": "npx trash-cli testresults",
|
||||
"test": "npx nx run-many --tuiAutoExit true -t test --exclude isa-app",
|
||||
"ci": "npx nx run-many -t test --exclude isa-app -c ci",
|
||||
"build": "nx build isa-app --configuration=development",
|
||||
"build-prod": "nx build isa-app --configuration=production",
|
||||
"lint": "nx lint",
|
||||
"e2e": "nx e2e",
|
||||
"generate:swagger": "nx run-many -t generate -p tag:generated,swagger",
|
||||
"fix:files:swagger": "node ./tools/fix-files.js generated/swagger",
|
||||
"prettier": "prettier --write .",
|
||||
"pretty-quick": "pretty-quick --staged",
|
||||
"prepare": "husky",
|
||||
"storybook": "npx nx run isa-app:storybook"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "20.0.3",
|
||||
"@angular/cdk": "20.0.3",
|
||||
"@angular/common": "20.0.3",
|
||||
"@angular/compiler": "20.0.3",
|
||||
"@angular/core": "20.0.3",
|
||||
"@angular/forms": "20.0.3",
|
||||
"@angular/localize": "20.0.3",
|
||||
"@angular/platform-browser": "20.0.3",
|
||||
"@angular/platform-browser-dynamic": "20.0.3",
|
||||
"@angular/router": "20.0.3",
|
||||
"@angular/service-worker": "20.0.3",
|
||||
"@microsoft/signalr": "^8.0.7",
|
||||
"@ng-icons/core": "32.0.0",
|
||||
"@ng-icons/material-icons": "32.0.0",
|
||||
"@ngrx/component-store": "19.2.1",
|
||||
"@ngrx/effects": "19.2.1",
|
||||
"@ngrx/entity": "19.2.1",
|
||||
"@ngrx/operators": "19.2.1",
|
||||
"@ngrx/signals": "19.2.1",
|
||||
"@ngrx/store": "19.2.1",
|
||||
"@ngrx/store-devtools": "19.2.1",
|
||||
"angular-oauth2-oidc": "20.0.0",
|
||||
"angular-oauth2-oidc-jwks": "20.0.0",
|
||||
"date-fns": "^4.1.0",
|
||||
"lodash": "^4.17.21",
|
||||
"moment": "^2.30.1",
|
||||
"ng2-pdf-viewer": "^10.4.0",
|
||||
"ngx-matomo-client": "^7.0.1",
|
||||
"parse-duration": "^2.1.3",
|
||||
"rxjs": "~7.8.2",
|
||||
"scandit-web-datacapture-barcode": "^6.28.1",
|
||||
"scandit-web-datacapture-core": "^6.28.1",
|
||||
"tslib": "^2.3.0",
|
||||
"uuid": "^8.3.2",
|
||||
"zod": "^3.24.2",
|
||||
"zone.js": "~0.15.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@analogjs/vite-plugin-angular": "~1.17.1",
|
||||
"@analogjs/vitest-angular": "~1.17.1",
|
||||
"@angular-devkit/build-angular": "20.0.3",
|
||||
"@angular-devkit/core": "20.0.2",
|
||||
"@angular-devkit/schematics": "20.0.2",
|
||||
"@angular/build": "20.0.3",
|
||||
"@angular/cli": "~20.0.0",
|
||||
"@angular/compiler-cli": "20.0.3",
|
||||
"@angular/language-service": "20.0.3",
|
||||
"@angular/pwa": "20.0.2",
|
||||
"@eslint/js": "^9.8.0",
|
||||
"@ngneat/spectator": "19.6.2",
|
||||
"@nx/angular": "21.2.1",
|
||||
"@nx/eslint": "21.2.1",
|
||||
"@nx/eslint-plugin": "21.2.1",
|
||||
"@nx/jest": "21.2.1",
|
||||
"@nx/js": "21.2.1",
|
||||
"@nx/storybook": "21.2.1",
|
||||
"@nx/vite": "21.2.1",
|
||||
"@nx/web": "21.2.1",
|
||||
"@nx/workspace": "21.2.1",
|
||||
"@schematics/angular": "20.0.2",
|
||||
"@storybook/addon-docs": "^9.0.11",
|
||||
"@storybook/angular": "^9.0.5",
|
||||
"@swc-node/register": "1.10.10",
|
||||
"@swc/core": "1.12.1",
|
||||
"@swc/helpers": "0.5.17",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/lodash": "^4.17.16",
|
||||
"@types/node": "18.16.9",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"@typescript-eslint/utils": "^8.33.1",
|
||||
"@vitest/coverage-v8": "^3.1.1",
|
||||
"@vitest/ui": "^3.1.1",
|
||||
"angular-eslint": "20.1.1",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"eslint": "^9.28.0",
|
||||
"eslint-config-prettier": "^10.1.5",
|
||||
"husky": "^9.1.7",
|
||||
"jest": "^29.7.0",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"jest-environment-node": "^29.7.0",
|
||||
"jest-junit": "^16.0.0",
|
||||
"jest-preset-angular": "14.6.0",
|
||||
"jiti": "2.4.2",
|
||||
"jsdom": "~22.1.0",
|
||||
"jsonc-eslint-parser": "^2.1.0",
|
||||
"ng-mocks": "14.13.5",
|
||||
"ng-packagr": "20.0.1",
|
||||
"ng-swagger-gen": "^2.3.1",
|
||||
"nx": "21.2.1",
|
||||
"postcss": "^8.5.3",
|
||||
"postcss-url": "~10.1.3",
|
||||
"prettier": "^3.5.2",
|
||||
"pretty-quick": "~4.0.0",
|
||||
"storybook": "^9.0.5",
|
||||
"tailwindcss": "^3.4.14",
|
||||
"ts-jest": "^29.1.0",
|
||||
"ts-node": "10.9.1",
|
||||
"typescript": "5.8.3",
|
||||
"typescript-eslint": "^8.33.1",
|
||||
"vite": "6.3.5",
|
||||
"vitest": "^3.1.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/linux-x64": "^0.25.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22.0.0",
|
||||
"npm": ">=10.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,6 +99,7 @@
|
||||
"@isa/ui/progress-bar": ["libs/ui/progress-bar/src/index.ts"],
|
||||
"@isa/ui/search-bar": ["libs/ui/search-bar/src/index.ts"],
|
||||
"@isa/ui/skeleton-loader": ["libs/ui/skeleton-loader/src/index.ts"],
|
||||
"@isa/ui/snackbar": ["libs/ui/snackbar/src/index.ts"],
|
||||
"@isa/ui/toolbar": ["libs/ui/toolbar/src/index.ts"],
|
||||
"@isa/ui/tooltip": ["libs/ui/tooltip/src/index.ts"],
|
||||
"@isa/utils/scroll-position": ["libs/utils/scroll-position/src/index.ts"],
|
||||
|
||||
Reference in New Issue
Block a user