Compare commits

...

1 Commits

Author SHA1 Message Date
Lorenz Hilpert
20003ab72a chore: update package.json dependencies and add snackbar library to tsconfig 2025-07-09 16:20:19 +02:00
22 changed files with 29411 additions and 28484 deletions

View File

@@ -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 {

View File

@@ -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,
});
}
}

View 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.

View 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: {},
},
];

View 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"
}
}
}

View 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';

View 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;
}
}
}

View 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;
}
}

View 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>
}

View 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();
}
}

View 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,
});
}
}

View 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');

View 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,
};

View File

@@ -0,0 +1 @@
@import './lib/snackbar';

View 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(),
);

View 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"
}
]
}

View 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"]
}

View 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"]
}

View 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
View File

File diff suppressed because it is too large Load Diff

View File

@@ -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"
}
}

View File

@@ -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"],