Merged PR 1879: Warenbegleitschein Übersicht und Details

Related work items: #5137, #5138
This commit is contained in:
Lorenz Hilpert
2025-07-10 16:00:16 +00:00
parent f6b2b554bb
commit a36d746fb8
115 changed files with 37648 additions and 32935 deletions

View File

@@ -0,0 +1,7 @@
# ui-bullet-list
This library was generated with [Nx](https://nx.dev).
## Running unit tests
Run `nx test ui-bullet-list` 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-bullet-list",
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "libs/ui/bullet-list/src",
"prefix": "ui",
"projectType": "library",
"tags": [],
"targets": {
"test": {
"executor": "@nx/vite:test",
"outputs": ["{options.reportsDirectory}"],
"options": {
"reportsDirectory": "../../../coverage/libs/ui/bullet-list"
}
},
"lint": {
"executor": "@nx/eslint:lint"
}
}
}

View File

@@ -0,0 +1,11 @@
.ui-bullet-list {
@apply flex flex-col gap-2 items-start shrink-0;
}
.ui-bullet-list-item {
@apply text-isa-secondary-900 isa-text-body-2-bold flex items-center gap-1;
ng-icon {
@apply text-isa-neutral-900;
}
}

View File

@@ -0,0 +1,6 @@
import { BulletListComponent } from './lib/bullet-list.component';
import { BulletListItemComponent } from './lib/bullet-list-item.component';
export const UiBulletList = [BulletListComponent, BulletListItemComponent];
export { BulletListComponent, BulletListItemComponent };

View File

@@ -0,0 +1,30 @@
import {
ChangeDetectionStrategy,
Component,
computed,
inject,
input,
} from '@angular/core';
import { NgIcon } from '@ng-icons/core';
import { BulletListComponent } from './bullet-list.component';
@Component({
selector: 'ui-bullet-list-item',
template: `<ng-icon [name]="iconName()" size="1.5rem"></ng-icon>
<ng-content></ng-content>`,
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [NgIcon],
host: {
class: 'ui-bullet-list-item',
},
})
export class BulletListItemComponent {
#bulletList = inject(BulletListComponent, { optional: true, host: true });
icon = input<string | undefined>();
iconName = computed(() => {
return this.icon() ?? this.#bulletList?.icon();
});
}

View File

@@ -0,0 +1,18 @@
import { ChangeDetectionStrategy, Component, input } from '@angular/core';
import { provideIcons } from '@ng-icons/core';
import { isaActionChevronRight } from '@isa/icons';
@Component({
selector: 'ui-bullet-list',
template: `<ng-content></ng-content>`,
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [],
providers: [provideIcons({ isaActionChevronRight })],
host: {
class: 'ui-bullet-list',
},
})
export class BulletListComponent {
icon = input<string>('isaActionChevronRight');
}

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/bullet-list',
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/bullet-list',
provider: 'v8' as const,
},
},
}));

View File

@@ -1,81 +1,81 @@
import {
Directive,
ViewContainerRef,
TemplateRef,
inject,
input,
effect,
ComponentRef,
untracked,
EmbeddedViewRef,
} from '@angular/core';
import { SkeletonLoaderComponent } from './skeleton-loader.component';
/**
* Structural directive that conditionally replaces its content with a skeleton loader.
*
* @example
* <div *uiSkeletonLoader="isLoading">Content to hide while loading</div>
*
* @example
* <div *uiSkeletonLoader="isLoading; width: '100px'; height: '20px'">Content</div>
*/
@Directive({
selector: '[uiSkeletonLoader]',
})
export class SkeletonLoaderDirective {
#viewContainerRef = inject(ViewContainerRef);
#templateRef = inject(TemplateRef);
private componentRef: ComponentRef<SkeletonLoaderComponent> | null = null;
private embeddedViewRef: EmbeddedViewRef<unknown> | null = null;
uiSkeletonLoader = input<boolean>(false);
uiSkeletonLoaderWidth = input<string | undefined>(undefined);
uiSkeletonLoaderHeight = input<string | undefined>(undefined);
render = effect(() => {
const condition = this.uiSkeletonLoader();
if (condition && !this.componentRef) {
// Create the skeleton loader when condition is true and view isn't created
this.#viewContainerRef.clear();
this.componentRef = this.#viewContainerRef.createComponent(
SkeletonLoaderComponent,
);
untracked(() => {
this.applyStyles();
});
this.embeddedViewRef = null; // Clear the embedded view reference
} else if (!condition && !this.embeddedViewRef) {
// Show original content when condition is false
this.#viewContainerRef.clear();
this.embeddedViewRef = this.#viewContainerRef.createEmbeddedView(
this.#templateRef,
);
this.componentRef = null; // Clear the component reference
}
});
updateStyles = effect(() => {
this.applyStyles();
});
applyStyles() {
if (this.componentRef) {
const width = this.uiSkeletonLoaderWidth() || '100%';
const height = this.uiSkeletonLoaderHeight() || '100%';
const element: HTMLElement | undefined =
this.componentRef?.location?.nativeElement;
if (element) {
element.style.width = width;
element.style.height = height;
}
}
}
}
import {
Directive,
ViewContainerRef,
TemplateRef,
inject,
input,
effect,
ComponentRef,
untracked,
EmbeddedViewRef,
} from '@angular/core';
import { SkeletonLoaderComponent } from './skeleton-loader.component';
/**
* Structural directive that conditionally replaces its content with a skeleton loader.
*
* @example
* <div *uiSkeletonLoader="isLoading">Content to hide while loading</div>
*
* @example
* <div *uiSkeletonLoader="isLoading; width: '100px'; height: '20px'">Content</div>
*/
@Directive({
selector: '[uiSkeletonLoader]',
})
export class SkeletonLoaderDirective {
#viewContainerRef = inject(ViewContainerRef);
#templateRef = inject(TemplateRef);
private componentRef: ComponentRef<SkeletonLoaderComponent> | null = null;
private embeddedViewRef: EmbeddedViewRef<unknown> | null = null;
uiSkeletonLoader = input<boolean>(false);
uiSkeletonLoaderWidth = input<string | undefined>(undefined);
uiSkeletonLoaderHeight = input<string | undefined>(undefined);
render = effect(() => {
const condition = this.uiSkeletonLoader();
if (condition && !this.componentRef) {
// Create the skeleton loader when condition is true and view isn't created
this.#viewContainerRef.clear();
this.componentRef = this.#viewContainerRef.createComponent(
SkeletonLoaderComponent,
);
untracked(() => {
this.applyStyles();
});
this.embeddedViewRef = null; // Clear the embedded view reference
} else if (!condition && !this.embeddedViewRef) {
// Show original content when condition is false
this.#viewContainerRef.clear();
this.embeddedViewRef = this.#viewContainerRef.createEmbeddedView(
this.#templateRef,
);
this.componentRef = null; // Clear the component reference
}
});
updateStyles = effect(() => {
this.applyStyles();
});
applyStyles() {
if (this.componentRef) {
const width = this.uiSkeletonLoaderWidth() || 'inherit';
const height = this.uiSkeletonLoaderHeight() || 'inherit';
const element: HTMLElement | undefined =
this.componentRef?.location?.nativeElement;
if (element) {
element.style.width = width;
element.style.height = height;
}
}
}
}