Skip to content
Snippets Groups Projects
Commit cc39db40 authored by Alexander Reifschneider's avatar Alexander Reifschneider
Browse files

Merge branch 'main' into OZG-7474-WeiterleitenButton

parents cd25cb9e 3e198080
Branches
Tags
1 merge request!49Ozg 7474 weiterleiten button
Showing
with 186 additions and 37 deletions
......@@ -36,6 +36,26 @@
<nav>NAV</nav>
</div>
<main class="flex-auto bg-background-50 p-6">
<div class="my-12">
<h1 class="mb-6 text-2xl font-semibold text-text">Buttons</h1>
<ods-button text="Organisationseinheit hinzufügen" />
<br />
<ods-button disabled="true" text="Organisationseinheit hinzufügen" />
<br />
<ods-button destructive="true" text="Organisationseinheit hinzufügen" />
<br />
<ods-button variant="outline" text="Organisationseinheit hinzufügen" />
<br />
<ods-button destructive="true" variant="outline" text="Organisationseinheit hinzufügen" /><br />
<ods-button destructive="true" disabled="true" variant="outline" text="Organisationseinheit hinzufügen" />
<br />
<ods-button variant="ghost" text="Organisationseinheit hinzufügen" />
<br />
<ods-button destructive="true" variant="ghost" text="Organisationseinheit hinzufügen" />
</div>
<div class="my-12">
<h1 class="mb-6 text-2xl font-semibold text-text">Organisationseinheiten</h1>
<ods-button text="Organisationseinheit hinzufügen" />
......
......@@ -31,7 +31,7 @@
>
<ng-container *ngIf="bescheidDraftStateResource$ | async as bescheidStateResource">
<ods-button
variant="icon"
variant="ghost"
size="fit"
class="absolute right-0 top-0 text-text"
(clickEmitter)="cancelWizard(bescheidStateResource.resource)"
......
......@@ -36,7 +36,7 @@ type IconVariants = VariantProps<typeof iconVariants>;
imports: [CommonModule, ButtonComponent, SaveIconComponent],
template: `<ods-button
[dataTestId]="dataTestId"
variant="icon"
variant="ghost"
size="fit"
spinnerSize="small"
[isLoading]="isLoading"
......
......@@ -22,34 +22,61 @@
* unter der Lizenz sind dem Lizenztext zu entnehmen.
*/
import { CommonModule } from '@angular/common';
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { VariantProps, cva } from 'class-variance-authority';
import { booleanAttribute, Component, EventEmitter, Input, Output } from '@angular/core';
import { cva, VariantProps } from 'class-variance-authority';
import { IconVariants } from '../icons/iconVariants';
import { SpinnerIconComponent } from '../icons/spinner-icon/spinner-icon.component';
export const buttonVariants = cva(
[
'flex items-center gap-4 rounded-md disabled:cursor-wait text-sm font-medium box-border',
'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus',
'flex items-center gap-4 rounded-lg text-sm font-medium box-border',
'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2',
],
{
variants: {
variant: {
primary: 'hover:enabled:bg-primary-hover bg-primary text-white shadow-sm',
primary: 'bg-primary text-white shadow-md hover:enabled:bg-primary-hover',
outline:
'border border-primary bg-background-50 text-primary hover:enabled:bg-background-100',
icon: 'border border-transparent hover:border-primary',
'border border-primary bg-background-50 text-primary shadow-md hover:enabled:bg-ghost-hover focus-visible:border-background-200',
ghost:
'border-2 border-transparent hover:enabled:bg-ghost-hover text-primary focus-visible:border-background-200 font-semibold',
},
size: {
medium: 'h-9 py-2 px-4 min-w-32',
fit: 'h-fit p-2',
},
disabled: {
false: null,
true: ['opacity-70', 'cursor-not-allowed'],
},
destructive: {
false: 'outline-focus',
true: 'outline-destructive',
},
},
defaultVariants: {
variant: 'primary',
size: 'medium',
disabled: false,
},
compoundVariants: [
{
variant: 'primary',
destructive: true,
class: '[&]:hover:enabled:bg-destructive-primary-hover [&]:bg-destructive [&]:outline-destructive',
},
{
variant: 'outline',
destructive: true,
class: '[&]:border-destructive [&]:text-destructive [&]:hover:enabled:bg-destructive-hover',
},
{
variant: 'ghost',
destructive: true,
class: '[&]:text-destructive [&]:hover:enabled:bg-destructive-hover',
},
],
},
);
type ButtonVariants = VariantProps<typeof buttonVariants>;
......@@ -60,9 +87,9 @@ type ButtonVariants = VariantProps<typeof buttonVariants>;
imports: [CommonModule, SpinnerIconComponent],
template: `<button
type="button"
[ngClass]="buttonVariants({ size, variant })"
[disabled]="isLoading"
[attr.aria-disabled]="isLoading"
[ngClass]="buttonVariants({ size, variant, disabled, destructive })"
[disabled]="isDisabled"
[attr.aria-disabled]="isDisabled"
[attr.aria-label]="text"
[attr.data-test-id]="dataTestId"
(click)="clickEmitter.emit()"
......@@ -75,12 +102,18 @@ type ButtonVariants = VariantProps<typeof buttonVariants>;
export class ButtonComponent {
@Input() text: string = '';
@Input() dataTestId: string = '';
@Input() disabled: boolean = false;
@Input() isLoading: boolean = false;
@Input({ transform: booleanAttribute }) destructive: boolean = false;
@Input() variant: ButtonVariants['variant'];
@Input() size: ButtonVariants['size'];
@Input() spinnerSize: IconVariants['size'] = 'medium';
@Output() public clickEmitter: EventEmitter<MouseEvent> = new EventEmitter<MouseEvent>();
buttonVariants = buttonVariants;
get isDisabled() {
return this.disabled || this.isLoading;
}
readonly buttonVariants = buttonVariants;
}
......@@ -45,29 +45,69 @@ export const Default: Story = {
args: {
text: 'Hello world!',
isLoading: false,
disabled: false,
destructive: false,
variant: 'primary',
size: 'medium',
spinnerSize: 'medium',
},
argTypes: {
variant: {
options: ['primary', 'outline'],
options: ['primary', 'outline', 'ghost'],
control: { type: 'radio' },
table: { defaultValue: { summary: 'primary' } },
},
size: {
options: ['medium', 'fit'],
control: { type: 'select' },
table: { defaultValue: { summary: 'medium' } },
},
spinnerSize: {
description: 'Size of loading spinner',
options: ['small', 'medium', 'large', 'extra-large', 'xxl', 'full'],
control: { type: 'select' },
table: { defaultValue: { summary: 'medium' } },
},
isLoading: { description: 'Show spinner icon if true' },
destructive: { description: 'Red version of a button' },
},
};
export const WithIcon: Story = {
export const PrimaryWithIcon: Story = {
args: {
text: 'I have an icon',
variant: 'primary',
},
render: (args) => ({
props: args,
template: `<ods-button ${argsToTemplate(args)}>
<ods-save-icon icon size="small" class="fill-whitetext" />
</ods-button>`,
}),
};
export const SecondaryWithIcon: Story = {
args: {
text: 'I have an icon too',
variant: 'outline',
size: 'medium',
},
render: (args) => ({
props: args,
template: `<ods-button ${argsToTemplate(args)}>
<ods-save-icon icon size='small'></ods-save-icon>
<ods-save-icon icon size="small" />
</ods-button>`,
}),
};
export const SecondaryIconOnly: Story = {
args: {
variant: 'outline',
size: 'fit',
},
render: (args) => ({
props: args,
template: `<ods-button ${argsToTemplate(args)}>
<ods-save-icon icon size="small" />
</ods-button>`,
}),
};
......@@ -76,19 +116,60 @@ export const IsLoading: Story = {
args: {
text: 'Loading...',
isLoading: true,
size: 'medium',
},
};
export const OnlyIcon: Story = {
export const Disabled: Story = {
args: {
text: "Can't click me",
disabled: true,
},
};
export const GhostIcon: Story = {
args: {
variant: 'ghost',
size: 'fit',
},
render: (args) => ({
props: args,
template: `<ods-button ${argsToTemplate(args)}>
<ods-save-icon icon size='medium' />
</ods-button>`,
}),
};
export const GhostIconDestructive: Story = {
args: {
variant: 'icon',
variant: 'ghost',
size: 'fit',
destructive: true,
},
render: (args) => ({
props: args,
template: `<ods-button ${argsToTemplate(args)}>
<ods-save-icon icon size='medium'></ods-save-icon>
<ods-save-icon icon size='medium' class="fill-destructive" />
</ods-button>`,
}),
};
export const GhostLoadingIcon: Story = {
args: {
variant: 'ghost',
size: 'fit',
isLoading: true,
},
render: (args) => ({
props: args,
template: `<ods-button ${argsToTemplate(args)}>
<ods-save-icon icon size='medium' />
</ods-button>`,
}),
};
export const GhostButton: Story = {
args: {
variant: 'ghost',
text: 'I am ghosted',
},
};
......@@ -7,10 +7,11 @@ import { LinkComponent } from '../../link/link.component';
standalone: true,
imports: [LinkComponent, OpenLinkIconComponent],
styles: [':host {@apply first:mt-2}'],
template: ` <ods-link [url]="url" class="bg-whitetext">
template: ` <ods-link [url]="url" class="bg-whitetext" [openInNewTab]="true">
<div class="flex items-center gap-2 px-4 py-3">
<p class="text-primary">{{ text }}</p>
<ods-open-link-icon size="small" />
<p class="font-medium text-primary">{{ text }}</p>
<ods-open-link-icon class="size-5" />
<span class="sr-only">Öffnet in einem neuen Tab</span>
</div>
</ods-link>`,
})
......
......@@ -5,7 +5,7 @@ import { Component, Input } from '@angular/core';
selector: 'ods-dropdown-menu-text-item',
standalone: true,
imports: [CommonModule],
styles: [':host {@apply flex min-h-12 items-start gap-4 px-4 py-3 text-start first:mt-2}'],
styles: [':host {@apply flex min-h-12 items-start gap-4 px-4 py-3 text-start}'],
template: `
<ng-content select="[icon]" />
<div class="w-80 whitespace-normal">
......
......@@ -48,7 +48,7 @@ import { twMerge } from 'tailwind-merge';
</button>
<div
*ngIf="isPopupOpen"
class="absolute z-50 max-h-120 min-w-44 max-w-96 animate-fadeIn overflow-y-auto rounded bg-dropdownBg shadow-md focus:outline-none"
class="absolute z-50 max-h-120 min-w-44 max-w-96 animate-fadeIn overflow-y-auto rounded bg-dropdownBg shadow-md ring-1 ring-grayborder focus:outline-none"
[ngClass]="alignTo === 'left' ? 'right-0' : 'left-0'"
role="menu"
aria-modal="true"
......
......@@ -34,6 +34,7 @@ import { iconVariants, IconVariants } from '../iconVariants';
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
[ngClass]="twMerge(iconVariants({ size }), 'fill-primary', class)"
aria-hidden="true"
>
<path
d="M5 21c-.55 0-1.02-.196-1.413-.587A1.926 1.926 0 0 1 3 19V5c0-.55.196-1.02.587-1.413A1.926 1.926 0 0 1 5 3h7v2H5v14h14v-7h2v7c0 .55-.196 1.02-.587 1.413A1.926 1.926 0 0 1 19 21H5Zm4.7-5.3-1.4-1.4L17.6 5H14V3h7v7h-2V6.4l-9.3 9.3Z"
......
......@@ -11,7 +11,7 @@ import { twMerge } from 'tailwind-merge';
[href]="url"
[class]="
twMerge(
'block rounded border-2 border-transparent text-text hover:bg-neutral-100 focus:border-focus focus:outline-none dark:hover:bg-neutral-700',
'hover:bg-ghost-hover block rounded border-2 border-transparent text-text focus:border-focus focus:outline-none dark:hover:bg-neutral-700',
class
)
"
......
......@@ -19,6 +19,12 @@
--color-disabled: 206 14% 95%;
--color-disabled-dark: 208 12% 65%;
--color-destructive: 360, 71%, 49%, 1;
--color-destructive-hover: 360, 71%, 49%, 0.07;
--color-destructive-primary-hover: 360, 71%, 49%, 0.9;
--color-ghost-hover: 212, 80%, 42%, 0.07;
--color-background-secondary: 0 0% 98%;
--color-mainbg: 0 0% 100%;
--text: 0 0% 0%;
......@@ -54,6 +60,12 @@
--color-disabled: 206 14% 15%;
--color-disabled-dark: 208 12% 33%;
--color-destructive: 360, 71%, 49%, 1;
--color-destructive-hover: 360, 71%, 49%, 0.2;
--color-destructive-primary-hover: 360, 71%, 49%, 0.9;
--color-ghost-hover: 43, 96%, 58%, 0.18;
--color-background-secondary: 0 0% 16%;
--color-mainbg: 0 0% 14%;
--text: 0 0% 100%;
......
......@@ -148,6 +148,10 @@ module.exports = {
dark: 'hsl(var(--color-disabled-dark) / <alpha-value>)',
DEFAULT: 'hsl(var(--color-disabled) / <alpha-value>)',
},
destructive: 'hsla(var(--color-destructive))',
'destructive-primary-hover': 'hsla(var(--color-destructive-primary-hover))',
'destructive-hover': 'hsla(var(--color-destructive-hover))',
'ghost-hover': 'hsla(var(--color-ghost-hover))',
},
backdropBlur: {
1: '1px',
......
......@@ -29,7 +29,7 @@ import { TooltipPosition } from './tooltip.directive';
selector: 'ods-tooltip',
imports: [NgClass],
template: `<span
class="tooltip fixed z-[100] max-w-md animate-fadeIn cursor-default whitespace-pre rounded bg-ozggray-900 px-3 py-2 text-sm text-whitetext before:absolute before:border-l-[0.5rem] before:border-r-[0.5rem] before:border-l-transparent before:border-r-transparent dark:bg-white md:max-w-[calc(90vw)]"
class="tooltip fixed z-[100] max-w-md animate-fadeIn cursor-default break-words rounded bg-ozggray-900 px-3 py-2 text-sm text-whitetext before:absolute before:border-l-[0.5rem] before:border-r-[0.5rem] before:border-l-transparent before:border-r-transparent dark:bg-white md:max-w-[calc(90vw)]"
[ngClass]="class"
[class.visible]="show"
[class.invisible]="!show"
......
......@@ -92,7 +92,6 @@ export class TooltipDirective implements OnDestroy {
}
@HostListener('mouseleave')
@HostListener('window:scroll')
@HostListener('focusout')
@HostListener('window:resize')
hideTooltip(): void {
......
......@@ -29,11 +29,11 @@
[href]="url"
[target]="targetName"
[attr.aria-label]="text"
[tooltip]="tooltip"
[color]="'primary'"
[title]="tooltip"
class="button"
>
<mat-icon>open_in_new</mat-icon>
<span class="text-sm">{{ text }}</span>
<mat-icon>open_in_new</mat-icon>
<span class="sr-only">{{ tooltip }}</span>
</a>
......@@ -24,12 +24,10 @@
-->
<ods-dropdown-menu-text-item
class="border-b border-b-grayborder border-t-grayborder bg-whitetext first:border-t"
class="border-b border-b-grayborder border-t-grayborder bg-whitetext"
title="Benutzerleitfaden"
description="Alle Funktionen der Allgemeinen Fachanwendung (Alfa) erklärt."
>
<ods-file-icon icon fileType="pdf" size="large"></ods-file-icon>
<alfa-open-documentation-button additionalContent [url]="url" data-test-id="documentations-component" />
</ods-dropdown-menu-text-item>
......@@ -24,7 +24,7 @@
-->
<ods-button
variant="icon"
variant="ghost"
size="fit"
data-test-class="edit-zustaendige-stelle-button"
[dataTestId]="dataTestId"
......
......@@ -26,7 +26,7 @@
<div class="my-32 flex h-screen flex-col gap-2">
<div class="flex gap-48 py-6 lg:gap-96">
<h1 class="text-xl font-bold text-primary">{{ title }}</h1>
<ods-button variant="icon" size="fit" (clickEmitter)="closeDialog()" dataTestId="close-search-dialog">
<ods-button variant="ghost" size="fit" (clickEmitter)="closeDialog()" dataTestId="close-search-dialog">
<ods-close-icon class="fill-primary" icon />
</ods-button>
</div>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment