feat: add InfoButton component with styling and functionality; update process schemas to include tags

This commit is contained in:
Lorenz Hilpert
2025-03-10 13:50:53 +01:00
parent 8eb5e09490
commit 573d6a740e
8 changed files with 124 additions and 1 deletions

View File

@@ -0,0 +1,49 @@
import { InfoButtonComponent } from '@isa/ui/buttons';
import { NgIconComponent, provideIcons } from '@ng-icons/core';
import * as IsaIcons from '@isa/icons';
import { argsToTemplate, moduleMetadata, type Meta, type StoryObj } from '@storybook/angular';
type InfoButtonComponentInputs = {
icon: string;
pending: boolean;
label: string;
disabled: boolean;
};
const meta: Meta<InfoButtonComponentInputs> = {
component: InfoButtonComponent,
decorators: [
moduleMetadata({
imports: [NgIconComponent],
providers: [provideIcons(IsaIcons)],
}),
],
title: 'ui/buttons/InfoButton',
args: {},
argTypes: {
icon: { control: 'select', options: Object.keys(IsaIcons) },
label: { control: 'text' },
disabled: { control: 'boolean' },
pending: {
control: 'boolean',
},
},
render: (args) => ({
props: args,
template: `<ui-info-button ${argsToTemplate(args, { exclude: ['disabled', 'icon'] })} ${args.disabled ? 'disabled' : ''}>
<span>${args.label}</span>
<ng-icon name="${args.icon}"></ng-icon>
</ui-info-button>`,
}),
};
export default meta;
type Story = StoryObj<InfoButtonComponentInputs>;
export const Default: Story = {
args: {
icon: 'isaNavigationLogout',
label: 'STA',
},
};

View File

@@ -2,8 +2,10 @@ import { z } from 'zod';
export const AddProcessSchema = z.object({
name: z.string().nonempty(),
tags: z.array(z.string()),
});
export const PatchProcessSchema = z.object({
name: z.string().nonempty().optional(),
tags: z.array(z.string()).optional(),
});

View File

@@ -34,10 +34,12 @@ export const ProcessService = signalStore(
})),
withMethods((store) => ({
addProcess(add: z.infer<typeof AddProcessSchema>) {
const parsed = AddProcessSchema.parse(add);
const process: Process = {
name: AddProcessSchema.parse(add).name,
name: parsed.name,
id: store.nextId(),
createdAt: Date.now(),
tags: parsed.tags,
};
patchState(store, addEntity(process));
return process;

View File

@@ -1,6 +1,7 @@
export interface Process {
id: number;
name: string;
tags: string[];
createdAt: number;
activatedAt?: number;
}

View File

@@ -1,4 +1,5 @@
export * from './lib/button.component';
export * from './lib/icon-button.component';
export * from './lib/info-button.component';
export * from './lib/text-button.component';
export * from './lib/types';

View File

@@ -0,0 +1,6 @@
<ng-content></ng-content>
@if (pending()) {
<ng-icon class="animate-spin" name="isaLoading"></ng-icon>
} @else {
<ng-content select="ng-icon"></ng-content>
}

View File

@@ -0,0 +1,35 @@
.ui-info-button {
display: inline-flex;
padding-left: 1rem;
align-items: center;
gap: 1rem;
@apply rounded-full cursor-pointer bg-isa-neutral-100 isa-text-body-1-bold text-isa-neutral-900;
ng-icon {
display: flex;
padding: 0.75rem;
justify-content: center;
align-items: center;
@apply rounded-full bg-isa-neutral-300 size-12;
}
&:hover {
ng-icon {
@apply bg-isa-neutral-400;
}
}
&:active,
&.active {
ng-icon {
@apply text-isa-white bg-isa-neutral-500;
}
}
&:disabled,
&[disabled] {
@apply bg-isa-neutral-200 text-isa-neutral-500 cursor-not-allowed;
}
}

View File

@@ -0,0 +1,27 @@
import {
ChangeDetectionStrategy,
Component,
computed,
input,
ViewEncapsulation,
} from '@angular/core';
import { isaLoading } from '@isa/icons';
import { NgIconComponent, provideIcons } from '@ng-icons/core';
@Component({
selector: 'ui-info-button, [uiInfoButton]',
templateUrl: './info-button.component.html',
styleUrls: ['./info-button.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
imports: [NgIconComponent],
providers: [provideIcons({ isaLoading })],
host: {
'[class]': '["ui-info-button", pendingClass()]',
},
})
export class InfoButtonComponent {
pending = input<boolean>(false);
pendingClass = computed(() => (this.pending() ? 'ui-info-button--pending' : ''));
}