-
{{size}}
+
{{size}}
Floating Button
Floating Button
@@ -69,7 +117,7 @@ export const statesVertical: Story = {
@for(state of buttonStateArray; track state) {
-
{{state}}
+
{{state}}
Floating Button
Floating Button
@@ -87,6 +135,7 @@ export const sizesHorizontal: Story = {
...sizesVertical,
args: {
axis: "horizontal",
+ textOffset: "0px",
},
};
@@ -94,5 +143,6 @@ export const statesHorizontal: Story = {
...statesVertical,
args: {
axis: "horizontal",
+ textOffset: "0px",
},
};
diff --git a/tedi/components/buttons/button/button.component.scss b/tedi/components/buttons/button/button.component.scss
index aa9ea2f76..9bc60658e 100644
--- a/tedi/components/buttons/button/button.component.scss
+++ b/tedi/components/buttons/button/button.component.scss
@@ -1,5 +1,4 @@
@use "@tedi-design-system/core/mixins";
-@use "@tedi-design-system/core/bootstrap-utility/breakpoints";
@use "sass:list";
$neutral-variants: "neutral", "neutral-inverted", "danger-neutral";
From e7ec55c8c6a08373b2e61d9acf981356ca73d304 Mon Sep 17 00:00:00 2001
From: Artur Lang <209750178+artur-langl@users.noreply.github.com>
Date: Fri, 14 Nov 2025 11:40:58 +0200
Subject: [PATCH 10/23] feat(floating-button): remove debug css #195
---
.../floating-button/floating-button.component.scss | 8 --------
1 file changed, 8 deletions(-)
diff --git a/community/components/buttons/floating-button/floating-button.component.scss b/community/components/buttons/floating-button/floating-button.component.scss
index dc112c31c..e0cf41eef 100644
--- a/community/components/buttons/floating-button/floating-button.component.scss
+++ b/community/components/buttons/floating-button/floating-button.component.scss
@@ -43,14 +43,6 @@ $btn-height-large: 3rem;
--_btn-focus-outline: $border-focus;
}
-:host {
- display: contents;
-}
-
-.test {
- display: contents;
-}
-
button.tedi-floating-button {
box-sizing: border-box;
text-wrap: wrap;
From f962835132f33b4d4630a1c268e92280f770abbe Mon Sep 17 00:00:00 2001
From: Artur Lang <209750178+artur-langl@users.noreply.github.com>
Date: Fri, 14 Nov 2025 12:58:18 +0200
Subject: [PATCH 11/23] feat(floating-button): remove id #195
---
.../buttons/floating-button/floating-button.component.ts | 7 +++----
.../buttons/floating-button/floating-button.stories.ts | 2 --
2 files changed, 3 insertions(+), 6 deletions(-)
diff --git a/community/components/buttons/floating-button/floating-button.component.ts b/community/components/buttons/floating-button/floating-button.component.ts
index ad14b48c5..38a35a238 100644
--- a/community/components/buttons/floating-button/floating-button.component.ts
+++ b/community/components/buttons/floating-button/floating-button.component.ts
@@ -2,7 +2,7 @@ import { Component, computed, input, ViewEncapsulation } from "@angular/core";
import { ButtonComponent, ButtonVariant } from "tedi/components";
export type FloatingButtonAxis = "horizontal" | "vertical";
-export type FloatingButtonSize = "small" | "medium" | "large";
+export type FloatingButtonSize = "default" | "small" | "medium" | "large";
@Component({
selector: "tedi-floating-button",
@@ -14,7 +14,6 @@ export type FloatingButtonSize = "small" | "medium" | "large";
encapsulation: ViewEncapsulation.None,
})
export class FloatingButtonComponent {
- id = input
();
/**
* Specifies the color theme of the button. The color should meet accessibility standards for color contrast.
* @default primary
@@ -26,10 +25,10 @@ export class FloatingButtonComponent {
*/
axis = input("horizontal");
/**
- * Button size
+ * Defines the size of the button
* @default medium
*/
- size = input("medium");
+ size = input("default");
classes = computed(() => {
const classes = ["tedi-floating-button"];
diff --git a/community/components/buttons/floating-button/floating-button.stories.ts b/community/components/buttons/floating-button/floating-button.stories.ts
index 31f0e3709..fa68af198 100644
--- a/community/components/buttons/floating-button/floating-button.stories.ts
+++ b/community/components/buttons/floating-button/floating-button.stories.ts
@@ -26,14 +26,12 @@ const meta: Meta = {
}),
],
args: {
- id: "",
variant: "primary",
axis: "horizontal",
size: "medium",
textOffset: "30px",
},
argTypes: {
- id: { control: "text" },
variant: {
control: "select",
description: "Specifies the color theme of the button.",
From 266e82ab2adaee575a4c36fd30f9003120949131 Mon Sep 17 00:00:00 2001
From: Artur Lang <209750178+artur-langl@users.noreply.github.com>
Date: Fri, 14 Nov 2025 18:21:07 +0200
Subject: [PATCH 12/23] feat(floating-button): refactor into selector component
+ trim styles #195
---
.../floating-button.component.scss | 121 +-----------------
.../floating-button.component.ts | 31 ++---
.../floating-button.stories.ts | 31 +++--
.../buttons/button/button.component.scss | 46 ++++---
.../buttons/button/button.component.ts | 4 +-
5 files changed, 63 insertions(+), 170 deletions(-)
diff --git a/community/components/buttons/floating-button/floating-button.component.scss b/community/components/buttons/floating-button/floating-button.component.scss
index e0cf41eef..adb5195ec 100644
--- a/community/components/buttons/floating-button/floating-button.component.scss
+++ b/community/components/buttons/floating-button/floating-button.component.scss
@@ -1,53 +1,10 @@
@use "@tedi-design-system/core/mixins";
@use "@tedi-design-system/core/bootstrap-utility/breakpoints";
-
-$btn-height: 2.75rem;
-$btn-height-small: 2.5rem;
-$btn-height-large: 3rem;
-
-@mixin button-variant(
- $background,
- $border,
- $color,
- $background-hover,
- $border-hover,
- $color-hover,
- $border-focus,
- $background-focus,
- $color-active,
- $border-active,
- $background-active,
- $background-disabled: var(--button-main-disabled-general-background),
- $border-disabled: var(--button-main-disabled-general-border),
- $color-disabled: var(--button-main-disabled-general-text)
-) {
- color: $color;
- background-color: $background;
- border-color: $border;
-
- --_btn-hover-bg: $background-hover;
- --_btn-hover-border: $border-hover;
- --_btn-hover-text: $color-hover;
-
- --_btn-active-bg: $background-active;
- --_btn-active-border: $border-active;
- --_btn-active-text: $color-active;
-
- --_btn-disabled-bg: $background-disabled;
- --_btn-disabled-border: $border-disabled;
- --_btn-disabled-text: $color-disabled;
-
- --_btn-focus-bg: $background-focus;
- --_btn-focus-border: $border;
- --_btn-focus-text: $color;
- --_btn-focus-outline: $border-focus;
-}
+@forward "../../../../tedi/components/buttons/button/button.component.scss";
+@use "../../../../tedi/components/buttons/button/button.component.scss" as
+ buttonMixins;
button.tedi-floating-button {
- box-sizing: border-box;
- text-wrap: wrap;
- box-shadow: 0 4px 10px 0 var(--alpha-14);
-
&--horizontal {
@include mixins.responsive-styles(border-radius, button-radius-default);
}
@@ -65,76 +22,12 @@ button.tedi-floating-button {
);
@include mixins.responsive-styles(border-top-left-radius, button-radius-sm);
}
- tedi-floating-button--vertical &.tedi-floating-button--primary {
- @include button-variant(
- var(--button-floating-primary-background-default),
- var(--button-floating-primary-border-default),
- var(--button-floating-primary-text-default),
- var(--button-floating-primary-background-hover),
- var(--button-floating-primary-border-hover),
- var(--button-floating-primary-text-default),
- var(--button-floating-primary-border-focus),
- var(--button-floating-primary-background-focus),
- var(--button-floating-primary-text-default),
- var(--button-floating-primary-border-active),
- var(--button-floating-primary-background-active)
- );
- }
&.tedi-floating-button--secondary {
- @include button-variant(
- var(--button-floating-secondary-background-default),
- var(--button-floating-secondary-border-default),
- var(--button-floating-secondary-text-default),
- var(--button-floating-secondary-background-hover),
- var(--button-floating-secondary-border-hover),
- var(--button-floating-secondary-text-hover),
- var(--button-floating-primary-border-focus),
- var(--button-floating-secondary-background-focus),
- var(--button-floating-secondary-text-active),
- var(--button-floating-secondary-border-active),
- var(--button-floating-secondary-background-active)
+ @include buttonMixins.button-variant-color-vars(
+ "secondary",
+ false,
+ "floating"
);
}
-
- &--medium {
- height: auto;
- min-height: $btn-height;
-
- @include breakpoints.media-breakpoint-up(md) {
- min-height: $btn-height-small;
- }
-
- @include mixins.responsive-styles(
- padding,
- button-md-padding-y button-md-padding-x
- );
-
- &.tedi-button--icon-only {
- @include mixins.responsive-styles(width, button-md-icon-size);
- @include mixins.responsive-styles(height, button-md-icon-size);
- @include mixins.responsive-styles(padding, button-md-icon-padding);
- }
- }
-
- &--large {
- height: auto;
- min-height: $btn-height-large;
-
- @include mixins.responsive-styles(
- padding,
- button-xl-padding-y button-xl-padding-x
- );
-
- &.tedi-button--icon-only {
- @include mixins.responsive-styles(width, button-xl-icon-size);
- @include mixins.responsive-styles(height, button-xl-icon-size);
- @include mixins.responsive-styles(padding, button-xl-icon-padding);
- }
- }
-
- &:not(.tedi-button--icon-only) {
- @include mixins.responsive-styles(min-width, button-width-min);
- @include mixins.responsive-styles(max-width, button-width-max);
- }
}
diff --git a/community/components/buttons/floating-button/floating-button.component.ts b/community/components/buttons/floating-button/floating-button.component.ts
index 38a35a238..55846a39c 100644
--- a/community/components/buttons/floating-button/floating-button.component.ts
+++ b/community/components/buttons/floating-button/floating-button.component.ts
@@ -1,40 +1,29 @@
import { Component, computed, input, ViewEncapsulation } from "@angular/core";
-import { ButtonComponent, ButtonVariant } from "tedi/components";
+import { ButtonComponent } from "tedi/components";
export type FloatingButtonAxis = "horizontal" | "vertical";
-export type FloatingButtonSize = "default" | "small" | "medium" | "large";
@Component({
- selector: "tedi-floating-button",
- template: ``,
+ selector: "[tedi-floating-button]",
+ template: ``,
styleUrl: "./floating-button.component.scss",
- imports: [ButtonComponent],
encapsulation: ViewEncapsulation.None,
+ host: {
+ "[class]": "floatClasses()",
+ },
})
-export class FloatingButtonComponent {
- /**
- * Specifies the color theme of the button. The color should meet accessibility standards for color contrast.
- * @default primary
- */
- variant = input("primary");
+export class FloatingButtonComponent extends ButtonComponent {
/**
* Button axis
* @default horizontal
*/
axis = input("horizontal");
- /**
- * Defines the size of the button
- * @default medium
- */
- size = input("default");
- classes = computed(() => {
+ floatClasses = computed(() => {
const classes = ["tedi-floating-button"];
classes.push(`tedi-floating-button--${this.axis() ?? "horizontal"}`);
- classes.push(`tedi-floating-button--${this.size() ?? "medium"}`);
classes.push(`tedi-floating-button--${this.variant() ?? "primary"}`);
- return classes.join(" ");
+
+ return `${classes.join(" ")} ${this.classes()}`;
});
}
diff --git a/community/components/buttons/floating-button/floating-button.stories.ts b/community/components/buttons/floating-button/floating-button.stories.ts
index fa68af198..1514c7d33 100644
--- a/community/components/buttons/floating-button/floating-button.stories.ts
+++ b/community/components/buttons/floating-button/floating-button.stories.ts
@@ -7,7 +7,7 @@ import {
import { FloatingButtonComponent } from "./floating-button.component";
import { IconComponent } from "tedi/components";
-const buttonSizeArray = ["small", "large"];
+const buttonSizeArray = ["default", "large"];
const buttonStateArray = ["Default", "Hover", "Active", "Focus"];
interface StoryArgs {
@@ -28,7 +28,6 @@ const meta: Meta = {
args: {
variant: "primary",
axis: "horizontal",
- size: "medium",
textOffset: "30px",
},
argTypes: {
@@ -45,7 +44,7 @@ const meta: Meta = {
size: {
control: "radio",
description: "Button size.",
- options: ["small", "medium", "large"],
+ options: ["small", "default", "large"],
},
// not meant to be user-editable or seen
textOffset: {
@@ -73,7 +72,7 @@ export const Default: Story = {
props: { ...args, debug: () => console.log("floating button clicked!") },
template: `
- floating button
+
`,
}),
@@ -91,13 +90,13 @@ export const sizesVertical: Story = {
@for(size of buttonSizeArray; track size) {
{{size}}
-
Floating Button
-
Floating Button
-
+
+
+
-
Floating Button
-
Floating Button
-
+
+
+
}
`,
@@ -116,13 +115,13 @@ export const statesVertical: Story = {
@for(state of buttonStateArray; track state) {
{{state}}
-
Floating Button
-
Floating Button
-
+
+
+
-
Floating Button
-
Floating Button
-
+
+
+
}
`,
diff --git a/tedi/components/buttons/button/button.component.scss b/tedi/components/buttons/button/button.component.scss
index 9bc60658e..2d930f5ee 100644
--- a/tedi/components/buttons/button/button.component.scss
+++ b/tedi/components/buttons/button/button.component.scss
@@ -3,46 +3,53 @@
$neutral-variants: "neutral", "neutral-inverted", "danger-neutral";
-@mixin button-variant-color-vars($variant, $icon-only: false) {
+@mixin button-variant-color-vars($variant, $icon-only: false, $affix: main) {
& {
@if $icon-only {
- --_btn-bg: var(--button-main-#{$variant}-icon-only-background-default);
+ --_btn-bg: var(
+ --button-#{$affix}-#{$variant}-icon-only-background-default
+ );
} @else {
- --_btn-bg: var(--button-main-#{$variant}-background-default);
+ --_btn-bg: var(--button-#{$affix}-#{$variant}-background-default);
}
- --_btn-border: var(--button-main-#{$variant}-border-default);
- --_btn-text: var(--button-main-#{$variant}-text-default);
+ --_btn-border: var(--button-#{$affix}-#{$variant}-border-default);
+ --_btn-text: var(--button-#{$affix}-#{$variant}-text-default);
--_btn-outline: var(--primary-500);
- @include button-state-color-vars($variant, "hover", $icon-only);
- @include button-state-color-vars($variant, "focus", $icon-only);
- @include button-state-color-vars($variant, "active", $icon-only);
+ @include button-state-color-vars($variant, "hover", $icon-only, $affix);
+ @include button-state-color-vars($variant, "focus", $icon-only, $affix);
+ @include button-state-color-vars($variant, "active", $icon-only, $affix);
@include button-disabled-color-vars($variant);
}
}
-@mixin button-state-color-vars($variant, $state, $icon-only: false) {
+@mixin button-state-color-vars(
+ $variant,
+ $state,
+ $icon-only: false,
+ $affix: main
+) {
& {
@if $icon-only {
--_btn-#{$state}-bg: var(
- --button-main-#{$variant}-icon-only-background-#{$state},
- var(--button-main-#{$variant}-icon-only-background-default)
+ --button-#{$affix}-#{$variant}-icon-only-background-#{$state},
+ var(--button-#{$affix}-#{$variant}-icon-only-background-default)
);
} @else {
--_btn-#{$state}-bg: var(
- --button-main-#{$variant}-background-#{$state},
- var(--button-main-#{$variant}-background-default)
+ --button-#{$affix}-#{$variant}-background-#{$state},
+ var(--button-#{$affix}-#{$variant}-background-default)
);
}
--_btn-#{$state}-border: var(
- --button-main-#{$variant}-border-#{$state},
- var(--button-main-#{$variant}-border-default)
+ --button-#{$affix}-#{$variant}-border-#{$state},
+ var(--button-#{$affix}-#{$variant}-border-default)
);
--_btn-#{$state}-text: var(
- --button-main-#{$variant}-text-#{$state},
- var(--button-main-#{$variant}-text-default)
+ --button-#{$affix}-#{$variant}-text-#{$state},
+ var(--button-#{$affix}-#{$variant}-text-default)
);
}
}
@@ -136,6 +143,11 @@ $neutral-variants: "neutral", "neutral-inverted", "danger-neutral";
@include mixins.responsive-styles(font-size, button-text-size-default);
}
+ &--large {
+ @include button-size-vars("lg");
+ @include mixins.responsive-styles(font-size, button-text-size-default);
+ }
+
&--primary {
@include button-variant-color-vars("primary");
}
diff --git a/tedi/components/buttons/button/button.component.ts b/tedi/components/buttons/button/button.component.ts
index 3f5b716a4..86dc01f6d 100644
--- a/tedi/components/buttons/button/button.component.ts
+++ b/tedi/components/buttons/button/button.component.ts
@@ -20,7 +20,7 @@ export type ButtonVariant =
| "secondary-inverted"
| "neutral-inverted";
-export type ButtonSize = "default" | "small";
+export type ButtonSize = "default" | "small" | "large";
@Component({
selector: "[tedi-button]",
@@ -57,7 +57,7 @@ export class ButtonComponent implements AfterContentChecked {
.map((node, index) => ({ node, index }))
.filter(x => x.node.nodeType === Node.ELEMENT_NODE && x.node.nodeName === "TEDI-ICON")
.map(x => x.index);
-
+
const iconCount = iconIndexes.length;
this.iconOnly.set(nodeCount === 1 && iconCount === 1);
this.iconFirst.set(iconIndexes.includes(0));
From 0b0a15eb7983daa09b04eca7cb1322f54f06d469 Mon Sep 17 00:00:00 2001
From: Artur Lang <209750178+artur-langl@users.noreply.github.com>
Date: Tue, 18 Nov 2025 10:39:36 +0200
Subject: [PATCH 13/23] feat(floating-button): re-add box-shadow + add links to
storybook #195
---
.../buttons/floating-button/floating-button.component.scss | 4 +---
.../buttons/floating-button/floating-button.stories.ts | 4 ++++
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/community/components/buttons/floating-button/floating-button.component.scss b/community/components/buttons/floating-button/floating-button.component.scss
index adb5195ec..b45daadb4 100644
--- a/community/components/buttons/floating-button/floating-button.component.scss
+++ b/community/components/buttons/floating-button/floating-button.component.scss
@@ -5,9 +5,7 @@
buttonMixins;
button.tedi-floating-button {
- &--horizontal {
- @include mixins.responsive-styles(border-radius, button-radius-default);
- }
+ box-shadow: 0 4px 10px 0 var(--alpha-14);
&--vertical {
min-width: max-content;
diff --git a/community/components/buttons/floating-button/floating-button.stories.ts b/community/components/buttons/floating-button/floating-button.stories.ts
index 1514c7d33..ab14524b3 100644
--- a/community/components/buttons/floating-button/floating-button.stories.ts
+++ b/community/components/buttons/floating-button/floating-button.stories.ts
@@ -16,6 +16,10 @@ interface StoryArgs {
type StoryFloatingButtonArgs = FloatingButtonComponent & StoryArgs;
+/**
+ *
Figma ↗
+ *
Zeroheight ↗
+ **/
const meta: Meta
= {
title: "Community/Buttons/Floating Button",
component: FloatingButtonComponent,
From 4b3715f29cc4d37375de4e23dc4fe48cb55f12b9 Mon Sep 17 00:00:00 2001
From: Artur Lang <209750178+artur-langl@users.noreply.github.com>
Date: Tue, 18 Nov 2025 10:46:41 +0200
Subject: [PATCH 14/23] feat(floating-button): use default size by default in
storybook #195
---
.../buttons/floating-button/floating-button.stories.ts | 1 +
1 file changed, 1 insertion(+)
diff --git a/community/components/buttons/floating-button/floating-button.stories.ts b/community/components/buttons/floating-button/floating-button.stories.ts
index ab14524b3..a2ab2a99d 100644
--- a/community/components/buttons/floating-button/floating-button.stories.ts
+++ b/community/components/buttons/floating-button/floating-button.stories.ts
@@ -33,6 +33,7 @@ const meta: Meta = {
variant: "primary",
axis: "horizontal",
textOffset: "30px",
+ size: "default",
},
argTypes: {
variant: {
From eed60d14d84b0cc47f3dd5b38be120dbf309276f Mon Sep 17 00:00:00 2001
From: Artur Lang <209750178+artur-langl@users.noreply.github.com>
Date: Wed, 19 Nov 2025 13:50:06 +0200
Subject: [PATCH 15/23] feat(floating-button): move button implementation to
directive to use as shared #195
---
.../floating-button.component.ts | 4 +-
.../floating-button.stories.ts | 5 +-
.../modal/footer/modal-footer.component.ts | 6 +-
.../buttons/button/base-button.directive.ts | 90 +++++++++++++++++++
.../buttons/button/button.component.ts | 89 ++----------------
.../buttons/button/button.stories.ts | 12 ++-
6 files changed, 117 insertions(+), 89 deletions(-)
create mode 100644 tedi/components/buttons/button/base-button.directive.ts
diff --git a/community/components/buttons/floating-button/floating-button.component.ts b/community/components/buttons/floating-button/floating-button.component.ts
index 55846a39c..fcc50f738 100644
--- a/community/components/buttons/floating-button/floating-button.component.ts
+++ b/community/components/buttons/floating-button/floating-button.component.ts
@@ -22,8 +22,8 @@ export class FloatingButtonComponent extends ButtonComponent {
floatClasses = computed(() => {
const classes = ["tedi-floating-button"];
classes.push(`tedi-floating-button--${this.axis() ?? "horizontal"}`);
- classes.push(`tedi-floating-button--${this.variant() ?? "primary"}`);
+ // classes.push(`tedi-floating-button--${this.variant() ?? "primary"}`);
- return `${classes.join(" ")} ${this.classes()}`;
+ return classes.join(" ");
});
}
diff --git a/community/components/buttons/floating-button/floating-button.stories.ts b/community/components/buttons/floating-button/floating-button.stories.ts
index a2ab2a99d..ed7f89b9e 100644
--- a/community/components/buttons/floating-button/floating-button.stories.ts
+++ b/community/components/buttons/floating-button/floating-button.stories.ts
@@ -6,6 +6,7 @@ import {
} from "@storybook/angular";
import { FloatingButtonComponent } from "./floating-button.component";
import { IconComponent } from "tedi/components";
+import { BaseButtonDirective } from "tedi/components/buttons/button/base-button.directive";
const buttonSizeArray = ["default", "large"];
const buttonStateArray = ["Default", "Hover", "Active", "Focus"];
@@ -14,7 +15,9 @@ interface StoryArgs {
textOffset: string;
}
-type StoryFloatingButtonArgs = FloatingButtonComponent & StoryArgs;
+type StoryFloatingButtonArgs = FloatingButtonComponent &
+ StoryArgs &
+ BaseButtonDirective;
/**
* Figma ↗
diff --git a/community/components/overlay/modal/footer/modal-footer.component.ts b/community/components/overlay/modal/footer/modal-footer.component.ts
index 1ecfc13ad..ba86d644f 100644
--- a/community/components/overlay/modal/footer/modal-footer.component.ts
+++ b/community/components/overlay/modal/footer/modal-footer.component.ts
@@ -2,12 +2,14 @@ import { NgStyle } from "@angular/common";
import { Component, inject, model, OnInit } from "@angular/core";
import {
ButtonComponent,
- ButtonSize,
- ButtonVariant,
IconComponent,
} from "@tedi-design-system/angular/tedi";
import { DialogData } from "../modal.component";
import { DIALOG_DATA } from "@angular/cdk/dialog";
+import {
+ ButtonSize,
+ ButtonVariant,
+} from "tedi/components/buttons/button/base-button.directive";
export interface ModalFooterButton {
label: string;
diff --git a/tedi/components/buttons/button/base-button.directive.ts b/tedi/components/buttons/button/base-button.directive.ts
new file mode 100644
index 000000000..26a5114dc
--- /dev/null
+++ b/tedi/components/buttons/button/base-button.directive.ts
@@ -0,0 +1,90 @@
+import {
+ Directive,
+ AfterContentChecked,
+ signal,
+ input,
+ inject,
+ ElementRef,
+ computed,
+} from "@angular/core";
+
+export type ButtonVariant =
+ | "primary"
+ | "secondary"
+ | "neutral"
+ | "success"
+ | "danger"
+ | "danger-neutral"
+ | "primary-inverted"
+ | "secondary-inverted"
+ | "neutral-inverted";
+
+export type ButtonSize = "default" | "small" | "large";
+
+@Directive({
+ host: {
+ "[class]": "classes()",
+ },
+})
+export class BaseButtonDirective implements AfterContentChecked {
+ /**
+ * Specifies the color theme of the button. The color should meet accessibility standards for color contrast.
+ * @default primary
+ */
+ variant = input("primary");
+ /**
+ * Defines the size of the button.
+ * @default default
+ */
+ size = input("default");
+
+ private host = inject(ElementRef);
+ iconOnly = signal(false);
+ iconFirst = signal(false);
+ iconLast = signal(false);
+
+ ngAfterContentChecked(): void {
+ const hostElement = this.host.nativeElement as HTMLElement;
+ const nodes = Array.from(hostElement.childNodes).filter(
+ (node) =>
+ node.nodeType === Node.ELEMENT_NODE ||
+ (node.nodeType === Node.TEXT_NODE && node.textContent?.trim())
+ );
+ const nodeCount = nodes.length;
+ const iconIndexes = nodes
+ .map((node, index) => ({ node, index }))
+ .filter(
+ (x) =>
+ x.node.nodeType === Node.ELEMENT_NODE &&
+ x.node.nodeName === "TEDI-ICON"
+ )
+ .map((x) => x.index);
+
+ const iconCount = iconIndexes.length;
+ this.iconOnly.set(nodeCount === 1 && iconCount === 1);
+ this.iconFirst.set(iconIndexes.includes(0));
+ this.iconLast.set(iconIndexes.includes(nodes.length - 1));
+ }
+
+ classes = computed(() => {
+ const classList = [
+ "tedi-button",
+ `tedi-button--${this.variant()}`,
+ `tedi-button--${this.size()}`,
+ ];
+
+ if (this.iconOnly()) {
+ classList.push("tedi-button--icon-only");
+ }
+
+ if (!this.iconFirst()) {
+ classList.push("tedi-button--pl");
+ }
+
+ if (!this.iconLast()) {
+ classList.push("tedi-button--pr");
+ }
+
+ return classList.join(" ");
+ });
+}
diff --git a/tedi/components/buttons/button/button.component.ts b/tedi/components/buttons/button/button.component.ts
index 86dc01f6d..d6016860e 100644
--- a/tedi/components/buttons/button/button.component.ts
+++ b/tedi/components/buttons/button/button.component.ts
@@ -1,26 +1,5 @@
-import {
- Component,
- computed,
- ElementRef,
- inject,
- input,
- signal,
- ViewEncapsulation,
- AfterContentChecked,
-} from "@angular/core";
-
-export type ButtonVariant =
- | "primary"
- | "secondary"
- | "neutral"
- | "success"
- | "danger"
- | "danger-neutral"
- | "primary-inverted"
- | "secondary-inverted"
- | "neutral-inverted";
-
-export type ButtonSize = "default" | "small" | "large";
+import { Component, ViewEncapsulation } from "@angular/core";
+import { BaseButtonDirective } from "./base-button.directive";
@Component({
selector: "[tedi-button]",
@@ -28,61 +7,11 @@ export type ButtonSize = "default" | "small" | "large";
template: "",
styleUrl: "./button.component.scss",
encapsulation: ViewEncapsulation.None,
- host: {
- "[class]": "classes()",
- },
+ hostDirectives: [
+ {
+ directive: BaseButtonDirective,
+ inputs: ["variant", "size"],
+ },
+ ],
})
-export class ButtonComponent implements AfterContentChecked {
- /**
- * Specifies the color theme of the button. The color should meet accessibility standards for color contrast.
- * @default primary
- */
- variant = input("primary");
- /**
- * Defines the size of the button.
- * @default default
- */
- size = input("default");
-
- private host = inject(ElementRef);
- iconOnly = signal(false);
- iconFirst = signal(false);
- iconLast = signal(false);
-
- ngAfterContentChecked(): void {
- const hostElement = this.host.nativeElement as HTMLElement;
- const nodes = Array.from(hostElement.childNodes).filter(node => node.nodeType === Node.ELEMENT_NODE || (node.nodeType === Node.TEXT_NODE && node.textContent?.trim()));
- const nodeCount = nodes.length;
- const iconIndexes = nodes
- .map((node, index) => ({ node, index }))
- .filter(x => x.node.nodeType === Node.ELEMENT_NODE && x.node.nodeName === "TEDI-ICON")
- .map(x => x.index);
-
- const iconCount = iconIndexes.length;
- this.iconOnly.set(nodeCount === 1 && iconCount === 1);
- this.iconFirst.set(iconIndexes.includes(0));
- this.iconLast.set(iconIndexes.includes(nodes.length - 1));
- }
-
- classes = computed(() => {
- const classList = [
- "tedi-button",
- `tedi-button--${this.variant()}`,
- `tedi-button--${this.size()}`,
- ];
-
- if (this.iconOnly()) {
- classList.push("tedi-button--icon-only");
- }
-
- if (!this.iconFirst()) {
- classList.push("tedi-button--pl");
- }
-
- if (!this.iconLast()) {
- classList.push("tedi-button--pr");
- }
-
- return classList.join(" ");
- });
-}
+export class ButtonComponent {}
diff --git a/tedi/components/buttons/button/button.stories.ts b/tedi/components/buttons/button/button.stories.ts
index a1dfcb932..7b183b58f 100644
--- a/tedi/components/buttons/button/button.stories.ts
+++ b/tedi/components/buttons/button/button.stories.ts
@@ -10,6 +10,7 @@ import { TextColor, TextComponent } from "../../base/text/text.component";
import { RowComponent } from "../../helpers/grid/row/row.component";
import { ColComponent } from "../../helpers/grid/col/col.component";
import { IconComponent } from "../../base/icon/icon.component";
+import { BaseButtonDirective } from "./base-button.directive";
const PSEUDO_STATE = ["Default", "Hover", "Active", "Focus", "Disabled"];
@@ -78,9 +79,11 @@ export default {
},
},
},
-} as Meta;
+} as Meta;
-type ButtonType = ButtonComponent & { ngContent: string };
+type StoryButtonType = ButtonComponent & BaseButtonDirective;
+
+type ButtonType = StoryButtonType & { ngContent: string };
export const Default: StoryObj = {
args: {
@@ -92,7 +95,8 @@ export const Default: StoryObj = {
}),
};
-type TemplateType = ButtonComponent & { titleColor?: TextColor };
+type TemplateType = StoryButtonType &
+ BaseButtonDirective & { titleColor?: TextColor };
const ButtonTemplate: StoryFn = ({
titleColor = "primary",
@@ -147,7 +151,7 @@ const ButtonTemplate: StoryFn = ({
`,
});
-type ButtonStory = StoryObj;
+type ButtonStory = StoryObj;
export const Primary: StoryObj = {
parameters: {
From 881ee6da87b1de014c35b9f42a124842c8c48fff Mon Sep 17 00:00:00 2001
From: Artur Lang <209750178+artur-langl@users.noreply.github.com>
Date: Wed, 19 Nov 2025 13:58:05 +0200
Subject: [PATCH 16/23] feat(floating-button): remove unused commented code
#195
---
.../buttons/floating-button/floating-button.component.ts | 1 -
1 file changed, 1 deletion(-)
diff --git a/community/components/buttons/floating-button/floating-button.component.ts b/community/components/buttons/floating-button/floating-button.component.ts
index fcc50f738..a5ac9336b 100644
--- a/community/components/buttons/floating-button/floating-button.component.ts
+++ b/community/components/buttons/floating-button/floating-button.component.ts
@@ -22,7 +22,6 @@ export class FloatingButtonComponent extends ButtonComponent {
floatClasses = computed(() => {
const classes = ["tedi-floating-button"];
classes.push(`tedi-floating-button--${this.axis() ?? "horizontal"}`);
- // classes.push(`tedi-floating-button--${this.variant() ?? "primary"}`);
return classes.join(" ");
});
From e0229be9625fb96b928d3475d6902549d74afba2 Mon Sep 17 00:00:00 2001
From: Artur Lang <209750178+artur-langl@users.noreply.github.com>
Date: Wed, 19 Nov 2025 13:59:16 +0200
Subject: [PATCH 17/23] feat(floating-button): fix tesst #195
---
tedi/components/buttons/button/button.component.spec.ts | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/tedi/components/buttons/button/button.component.spec.ts b/tedi/components/buttons/button/button.component.spec.ts
index 67225b3d6..dd9d2380c 100644
--- a/tedi/components/buttons/button/button.component.spec.ts
+++ b/tedi/components/buttons/button/button.component.spec.ts
@@ -1,6 +1,7 @@
import { ComponentFixture, TestBed } from "@angular/core/testing";
-import { ButtonComponent, ButtonSize, ButtonVariant } from "./button.component";
import { IconComponent } from "../../base/icon/icon.component";
+import { ButtonSize, ButtonVariant } from "./base-button.directive";
+import { ButtonComponent } from "./button.component";
describe("ButtonComponent", () => {
let fixture: ComponentFixture;
From 094fb594f86ca42518a7ede03224a11918445a6f Mon Sep 17 00:00:00 2001
From: Artur Lang <209750178+artur-langl@users.noreply.github.com>
Date: Wed, 19 Nov 2025 14:00:19 +0200
Subject: [PATCH 18/23] feat(floating-button): export base-button directive in
index file #195
---
tedi/components/buttons/index.ts | 1 +
1 file changed, 1 insertion(+)
diff --git a/tedi/components/buttons/index.ts b/tedi/components/buttons/index.ts
index 119c4d2ed..6b03de438 100644
--- a/tedi/components/buttons/index.ts
+++ b/tedi/components/buttons/index.ts
@@ -2,3 +2,4 @@ export * from "./button/button.component";
export * from "./info-button/info-button.component";
export * from "./collapse/collapse.component";
export * from "./closing-button/closing-button.component";
+export * from "./button/base-button.directive";
From 09e98d68a963693ff2d76752a2fbed8c6ebda307 Mon Sep 17 00:00:00 2001
From: Artur Lang <209750178+artur-langl@users.noreply.github.com>
Date: Wed, 19 Nov 2025 15:57:09 +0200
Subject: [PATCH 19/23] feat(floating-button): move inputs into button and
floating button comp + narrower types #195
---
.../floating-button.component.scss | 8 +++++
.../floating-button.component.ts | 31 ++++++++++++++++---
.../floating-button.stories.ts | 5 +--
.../modal/footer/modal-footer.component.ts | 6 ++--
.../buttons/button/base-button.directive.ts | 31 +------------------
.../buttons/button/button.component.spec.ts | 3 +-
.../buttons/button/button.component.ts | 29 +++++++++++++++--
.../buttons/button/button.stories.ts | 8 ++---
8 files changed, 68 insertions(+), 53 deletions(-)
diff --git a/community/components/buttons/floating-button/floating-button.component.scss b/community/components/buttons/floating-button/floating-button.component.scss
index b45daadb4..3f1b6e1ce 100644
--- a/community/components/buttons/floating-button/floating-button.component.scss
+++ b/community/components/buttons/floating-button/floating-button.component.scss
@@ -21,6 +21,14 @@ button.tedi-floating-button {
@include mixins.responsive-styles(border-top-left-radius, button-radius-sm);
}
+ &.tedi-floating-button--primary {
+ @include buttonMixins.button-variant-color-vars(
+ "primary",
+ false,
+ "floating"
+ );
+ }
+
&.tedi-floating-button--secondary {
@include buttonMixins.button-variant-color-vars(
"secondary",
diff --git a/community/components/buttons/floating-button/floating-button.component.ts b/community/components/buttons/floating-button/floating-button.component.ts
index a5ac9336b..80eee9898 100644
--- a/community/components/buttons/floating-button/floating-button.component.ts
+++ b/community/components/buttons/floating-button/floating-button.component.ts
@@ -1,6 +1,9 @@
import { Component, computed, input, ViewEncapsulation } from "@angular/core";
-import { ButtonComponent } from "tedi/components";
+import { BaseButtonDirective } from "tedi/components";
+export type FloatingButtonVariant = "primary" | "secondary";
+
+export type FloatingButtonSize = "default" | "large";
export type FloatingButtonAxis = "horizontal" | "vertical";
@Component({
@@ -8,11 +11,26 @@ export type FloatingButtonAxis = "horizontal" | "vertical";
template: ``,
styleUrl: "./floating-button.component.scss",
encapsulation: ViewEncapsulation.None,
+ hostDirectives: [
+ {
+ directive: BaseButtonDirective,
+ },
+ ],
host: {
"[class]": "floatClasses()",
},
})
-export class FloatingButtonComponent extends ButtonComponent {
+export class FloatingButtonComponent {
+ /**
+ * Specifies the color theme of the button. The color should meet accessibility standards for color contrast.
+ * @default primary
+ */
+ variant = input("primary");
+ /**
+ * Defines the size of the button.
+ * @default default
+ */
+ size = input("default");
/**
* Button axis
* @default horizontal
@@ -20,9 +38,12 @@ export class FloatingButtonComponent extends ButtonComponent {
axis = input("horizontal");
floatClasses = computed(() => {
- const classes = ["tedi-floating-button"];
- classes.push(`tedi-floating-button--${this.axis() ?? "horizontal"}`);
-
+ const classes = [
+ "tedi-floating-button",
+ `tedi-button--${this.variant() ?? "primary"}`,
+ `tedi-button--${this.size() ?? "default"}`,
+ `tedi-floating-button--${this.axis() ?? "horizontal"}`,
+ ];
return classes.join(" ");
});
}
diff --git a/community/components/buttons/floating-button/floating-button.stories.ts b/community/components/buttons/floating-button/floating-button.stories.ts
index ed7f89b9e..a2ab2a99d 100644
--- a/community/components/buttons/floating-button/floating-button.stories.ts
+++ b/community/components/buttons/floating-button/floating-button.stories.ts
@@ -6,7 +6,6 @@ import {
} from "@storybook/angular";
import { FloatingButtonComponent } from "./floating-button.component";
import { IconComponent } from "tedi/components";
-import { BaseButtonDirective } from "tedi/components/buttons/button/base-button.directive";
const buttonSizeArray = ["default", "large"];
const buttonStateArray = ["Default", "Hover", "Active", "Focus"];
@@ -15,9 +14,7 @@ interface StoryArgs {
textOffset: string;
}
-type StoryFloatingButtonArgs = FloatingButtonComponent &
- StoryArgs &
- BaseButtonDirective;
+type StoryFloatingButtonArgs = FloatingButtonComponent & StoryArgs;
/**
* Figma ↗
diff --git a/community/components/overlay/modal/footer/modal-footer.component.ts b/community/components/overlay/modal/footer/modal-footer.component.ts
index ba86d644f..1ecfc13ad 100644
--- a/community/components/overlay/modal/footer/modal-footer.component.ts
+++ b/community/components/overlay/modal/footer/modal-footer.component.ts
@@ -2,14 +2,12 @@ import { NgStyle } from "@angular/common";
import { Component, inject, model, OnInit } from "@angular/core";
import {
ButtonComponent,
+ ButtonSize,
+ ButtonVariant,
IconComponent,
} from "@tedi-design-system/angular/tedi";
import { DialogData } from "../modal.component";
import { DIALOG_DATA } from "@angular/cdk/dialog";
-import {
- ButtonSize,
- ButtonVariant,
-} from "tedi/components/buttons/button/base-button.directive";
export interface ModalFooterButton {
label: string;
diff --git a/tedi/components/buttons/button/base-button.directive.ts b/tedi/components/buttons/button/base-button.directive.ts
index 26a5114dc..786bb28c6 100644
--- a/tedi/components/buttons/button/base-button.directive.ts
+++ b/tedi/components/buttons/button/base-button.directive.ts
@@ -2,42 +2,17 @@ import {
Directive,
AfterContentChecked,
signal,
- input,
inject,
ElementRef,
computed,
} from "@angular/core";
-export type ButtonVariant =
- | "primary"
- | "secondary"
- | "neutral"
- | "success"
- | "danger"
- | "danger-neutral"
- | "primary-inverted"
- | "secondary-inverted"
- | "neutral-inverted";
-
-export type ButtonSize = "default" | "small" | "large";
-
@Directive({
host: {
"[class]": "classes()",
},
})
export class BaseButtonDirective implements AfterContentChecked {
- /**
- * Specifies the color theme of the button. The color should meet accessibility standards for color contrast.
- * @default primary
- */
- variant = input("primary");
- /**
- * Defines the size of the button.
- * @default default
- */
- size = input("default");
-
private host = inject(ElementRef);
iconOnly = signal(false);
iconFirst = signal(false);
@@ -67,11 +42,7 @@ export class BaseButtonDirective implements AfterContentChecked {
}
classes = computed(() => {
- const classList = [
- "tedi-button",
- `tedi-button--${this.variant()}`,
- `tedi-button--${this.size()}`,
- ];
+ const classList = ["tedi-button"];
if (this.iconOnly()) {
classList.push("tedi-button--icon-only");
diff --git a/tedi/components/buttons/button/button.component.spec.ts b/tedi/components/buttons/button/button.component.spec.ts
index dd9d2380c..46dceced1 100644
--- a/tedi/components/buttons/button/button.component.spec.ts
+++ b/tedi/components/buttons/button/button.component.spec.ts
@@ -1,7 +1,6 @@
import { ComponentFixture, TestBed } from "@angular/core/testing";
import { IconComponent } from "../../base/icon/icon.component";
-import { ButtonSize, ButtonVariant } from "./base-button.directive";
-import { ButtonComponent } from "./button.component";
+import { ButtonComponent, ButtonSize, ButtonVariant } from "./button.component";
describe("ButtonComponent", () => {
let fixture: ComponentFixture;
diff --git a/tedi/components/buttons/button/button.component.ts b/tedi/components/buttons/button/button.component.ts
index d6016860e..a99827b76 100644
--- a/tedi/components/buttons/button/button.component.ts
+++ b/tedi/components/buttons/button/button.component.ts
@@ -1,6 +1,19 @@
-import { Component, ViewEncapsulation } from "@angular/core";
+import { Component, input, ViewEncapsulation } from "@angular/core";
import { BaseButtonDirective } from "./base-button.directive";
+export type ButtonVariant =
+ | "primary"
+ | "secondary"
+ | "neutral"
+ | "success"
+ | "danger"
+ | "danger-neutral"
+ | "primary-inverted"
+ | "secondary-inverted"
+ | "neutral-inverted";
+
+export type ButtonSize = "default" | "small";
+
@Component({
selector: "[tedi-button]",
standalone: true,
@@ -10,8 +23,18 @@ import { BaseButtonDirective } from "./base-button.directive";
hostDirectives: [
{
directive: BaseButtonDirective,
- inputs: ["variant", "size"],
},
],
})
-export class ButtonComponent {}
+export class ButtonComponent {
+ /**
+ * Specifies the color theme of the button. The color should meet accessibility standards for color contrast.
+ * @default primary
+ */
+ variant = input("primary");
+ /**
+ * Defines the size of the button.
+ * @default default
+ */
+ size = input("default");
+}
diff --git a/tedi/components/buttons/button/button.stories.ts b/tedi/components/buttons/button/button.stories.ts
index 7b183b58f..3d240dd41 100644
--- a/tedi/components/buttons/button/button.stories.ts
+++ b/tedi/components/buttons/button/button.stories.ts
@@ -81,9 +81,7 @@ export default {
},
} as Meta;
-type StoryButtonType = ButtonComponent & BaseButtonDirective;
-
-type ButtonType = StoryButtonType & { ngContent: string };
+type ButtonType = ButtonComponent & { ngContent: string };
export const Default: StoryObj = {
args: {
@@ -95,7 +93,7 @@ export const Default: StoryObj = {
}),
};
-type TemplateType = StoryButtonType &
+type TemplateType = ButtonType &
BaseButtonDirective & { titleColor?: TextColor };
const ButtonTemplate: StoryFn = ({
@@ -151,7 +149,7 @@ const ButtonTemplate: StoryFn = ({
`,
});
-type ButtonStory = StoryObj;
+type ButtonStory = StoryObj;
export const Primary: StoryObj = {
parameters: {
From 0b65dd878ffcf5df5ac1eff0f74f09e18430c943 Mon Sep 17 00:00:00 2001
From: Artur Lang <209750178+artur-langl@users.noreply.github.com>
Date: Wed, 19 Nov 2025 18:19:59 +0200
Subject: [PATCH 20/23] feat(floating-button): ensure to not use non
encapsulated styles in floating button #195
---
.../floating-button.component.scss | 10 +-
.../floating-button.component.ts | 22 ++-
.../floating-button.stories.ts | 4 +-
.../buttons/button/base-button.directive.ts | 13 +-
.../buttons/button/button.component.scss | 135 +++++++++---------
.../buttons/button/button.component.ts | 14 +-
6 files changed, 121 insertions(+), 77 deletions(-)
diff --git a/community/components/buttons/floating-button/floating-button.component.scss b/community/components/buttons/floating-button/floating-button.component.scss
index 3f1b6e1ce..1ffa4c11f 100644
--- a/community/components/buttons/floating-button/floating-button.component.scss
+++ b/community/components/buttons/floating-button/floating-button.component.scss
@@ -1,10 +1,10 @@
@use "@tedi-design-system/core/mixins";
@use "@tedi-design-system/core/bootstrap-utility/breakpoints";
-@forward "../../../../tedi/components/buttons/button/button.component.scss";
@use "../../../../tedi/components/buttons/button/button.component.scss" as
buttonMixins;
button.tedi-floating-button {
+ @include buttonMixins.button-main-styles();
box-shadow: 0 4px 10px 0 var(--alpha-14);
&--vertical {
@@ -36,4 +36,12 @@ button.tedi-floating-button {
"floating"
);
}
+
+ &--default {
+ @include buttonMixins.button-size("md");
+ }
+
+ &--large {
+ @include buttonMixins.button-size("lg");
+ }
}
diff --git a/community/components/buttons/floating-button/floating-button.component.ts b/community/components/buttons/floating-button/floating-button.component.ts
index 80eee9898..ea0d8b060 100644
--- a/community/components/buttons/floating-button/floating-button.component.ts
+++ b/community/components/buttons/floating-button/floating-button.component.ts
@@ -1,4 +1,11 @@
-import { Component, computed, input, ViewEncapsulation } from "@angular/core";
+import {
+ Component,
+ computed,
+ input,
+ ViewEncapsulation,
+ OnInit,
+ inject,
+} from "@angular/core";
import { BaseButtonDirective } from "tedi/components";
export type FloatingButtonVariant = "primary" | "secondary";
@@ -20,7 +27,7 @@ export type FloatingButtonAxis = "horizontal" | "vertical";
"[class]": "floatClasses()",
},
})
-export class FloatingButtonComponent {
+export class FloatingButtonComponent implements OnInit {
/**
* Specifies the color theme of the button. The color should meet accessibility standards for color contrast.
* @default primary
@@ -37,11 +44,18 @@ export class FloatingButtonComponent {
*/
axis = input("horizontal");
+ buttonDirective = inject(BaseButtonDirective);
+
+ ngOnInit() {
+ this.buttonDirective.classNamePrefix.set("tedi-floating-button");
+ console.log("set", this.buttonDirective);
+ }
+
floatClasses = computed(() => {
const classes = [
"tedi-floating-button",
- `tedi-button--${this.variant() ?? "primary"}`,
- `tedi-button--${this.size() ?? "default"}`,
+ `tedi-floating-button--${this.variant() ?? "primary"}`,
+ `tedi-floating-button--${this.size() ?? "default"}`,
`tedi-floating-button--${this.axis() ?? "horizontal"}`,
];
return classes.join(" ");
diff --git a/community/components/buttons/floating-button/floating-button.stories.ts b/community/components/buttons/floating-button/floating-button.stories.ts
index a2ab2a99d..b93ee7539 100644
--- a/community/components/buttons/floating-button/floating-button.stories.ts
+++ b/community/components/buttons/floating-button/floating-button.stories.ts
@@ -39,7 +39,7 @@ const meta: Meta = {
variant: {
control: "select",
description: "Specifies the color theme of the button.",
- options: ["primary", "secondary"],
+ options: ["primary", "secondary", "neutral", "success"],
},
axis: {
control: "radio",
@@ -49,7 +49,7 @@ const meta: Meta = {
size: {
control: "radio",
description: "Button size.",
- options: ["small", "default", "large"],
+ options: ["default", "large"],
},
// not meant to be user-editable or seen
textOffset: {
diff --git a/tedi/components/buttons/button/base-button.directive.ts b/tedi/components/buttons/button/base-button.directive.ts
index 786bb28c6..5dbfde55f 100644
--- a/tedi/components/buttons/button/base-button.directive.ts
+++ b/tedi/components/buttons/button/base-button.directive.ts
@@ -13,6 +13,11 @@ import {
},
})
export class BaseButtonDirective implements AfterContentChecked {
+ /**
+ * CSS class name affix the directive should provide
+ */
+ classNamePrefix = signal("tedi-button");
+
private host = inject(ElementRef);
iconOnly = signal(false);
iconFirst = signal(false);
@@ -42,18 +47,18 @@ export class BaseButtonDirective implements AfterContentChecked {
}
classes = computed(() => {
- const classList = ["tedi-button"];
+ const classList = [this.classNamePrefix()];
if (this.iconOnly()) {
- classList.push("tedi-button--icon-only");
+ classList.push(`${this.classNamePrefix()}--icon-only`);
}
if (!this.iconFirst()) {
- classList.push("tedi-button--pl");
+ classList.push(`${this.classNamePrefix()}--pl`);
}
if (!this.iconLast()) {
- classList.push("tedi-button--pr");
+ classList.push(`${this.classNamePrefix()}--pr`);
}
return classList.join(" ");
diff --git a/tedi/components/buttons/button/button.component.scss b/tedi/components/buttons/button/button.component.scss
index 2d930f5ee..627a0681d 100644
--- a/tedi/components/buttons/button/button.component.scss
+++ b/tedi/components/buttons/button/button.component.scss
@@ -110,7 +110,7 @@ $neutral-variants: "neutral", "neutral-inverted", "danger-neutral";
}
}
-.tedi-button {
+@mixin button-main-styles {
display: inline-flex;
align-items: center;
justify-content: center;
@@ -133,20 +133,81 @@ $neutral-variants: "neutral", "neutral-inverted", "danger-neutral";
@include mixins.responsive-styles(line-height, body-bold-line-height);
@include mixins.responsive-styles(border-radius, button-radius-default);
+ &--icon-only {
+ @include mixins.responsive-styles(width, button-md-icon-size);
+ @include mixins.responsive-styles(height, button-md-icon-size);
+ @include mixins.responsive-styles(padding, button-md-icon-padding);
+ }
+
+ &--pl {
+ &:not(.tedi-button--neutral):not(.tedi-button--danger-neutral):not(
+ .tedi-button--neutral-inverted
+ ) {
+ padding-left: calc(
+ var(--_btn-padding-x) + var(--_btn-inner-spacing) - 1px
+ );
+ }
+ }
+
+ &--pr {
+ &:not(.tedi-button--neutral):not(.tedi-button--danger-neutral):not(
+ .tedi-button--neutral-inverted
+ ) {
+ padding-right: calc(
+ var(--_btn-padding-x) + var(--_btn-inner-spacing) - 1px
+ );
+ }
+ }
+
+ &:hover {
+ background: var(--_btn-hover-bg);
+ border-color: var(--_btn-hover-border);
+ color: var(--_btn-hover-text);
+ }
+
+ &:active {
+ background: var(--_btn-active-bg);
+ border-color: var(--_btn-active-border);
+ color: var(--_btn-active-text);
+ }
+
+ &:focus-visible {
+ background: var(--_btn-focus-bg);
+ border-color: var(--_btn-focus-border);
+ color: var(--_btn-focus-text);
+ outline: 2px solid var(--_btn-outline);
+ outline-offset: 1px;
+ }
+
+ &:disabled {
+ background: var(--_btn-disabled-bg);
+ border-color: var(--_btn-disabled-border);
+ color: var(--_btn-disabled-text);
+ cursor: not-allowed;
+ }
+
+ tedi-icon {
+ color: inherit;
+ font-size: inherit;
+ }
+}
+
+@mixin button-size($size) {
+ @include button-size-vars(#{$size});
+ @include mixins.responsive-styles(font-size, button-text-size-#{$size});
+}
+
+.tedi-button {
+ @include button-main-styles();
+
&--small {
- @include button-size-vars("sm");
- @include mixins.responsive-styles(font-size, button-text-size-sm);
+ @include button-size("sm");
}
&--default {
- @include button-size-vars("md");
- @include mixins.responsive-styles(font-size, button-text-size-default);
+ @include button-size("md");
}
- &--large {
- @include button-size-vars("lg");
- @include mixins.responsive-styles(font-size, button-text-size-default);
- }
&--primary {
@include button-variant-color-vars("primary");
@@ -188,10 +249,6 @@ $neutral-variants: "neutral", "neutral-inverted", "danger-neutral";
}
&--icon-only {
- @include mixins.responsive-styles(width, button-md-icon-size);
- @include mixins.responsive-styles(height, button-md-icon-size);
- @include mixins.responsive-styles(padding, button-md-icon-padding);
-
&.tedi-button--neutral {
@include button-variant-color-vars("neutral", true);
}
@@ -210,56 +267,4 @@ $neutral-variants: "neutral", "neutral-inverted", "danger-neutral";
@include mixins.responsive-styles(padding, button-sm-icon-padding);
}
}
-
- &--pl {
- &:not(.tedi-button--neutral):not(.tedi-button--danger-neutral):not(
- .tedi-button--neutral-inverted
- ) {
- padding-left: calc(
- var(--_btn-padding-x) + var(--_btn-inner-spacing) - 1px
- );
- }
- }
-
- &--pr {
- &:not(.tedi-button--neutral):not(.tedi-button--danger-neutral):not(
- .tedi-button--neutral-inverted
- ) {
- padding-right: calc(
- var(--_btn-padding-x) + var(--_btn-inner-spacing) - 1px
- );
- }
- }
-
- &:hover {
- background: var(--_btn-hover-bg);
- border-color: var(--_btn-hover-border);
- color: var(--_btn-hover-text);
- }
-
- &:active {
- background: var(--_btn-active-bg);
- border-color: var(--_btn-active-border);
- color: var(--_btn-active-text);
- }
-
- &:focus-visible {
- background: var(--_btn-focus-bg);
- border-color: var(--_btn-focus-border);
- color: var(--_btn-focus-text);
- outline: 2px solid var(--_btn-outline);
- outline-offset: 1px;
- }
-
- &:disabled {
- background: var(--_btn-disabled-bg);
- border-color: var(--_btn-disabled-border);
- color: var(--_btn-disabled-text);
- cursor: not-allowed;
- }
-
- tedi-icon {
- color: inherit;
- font-size: inherit;
- }
}
diff --git a/tedi/components/buttons/button/button.component.ts b/tedi/components/buttons/button/button.component.ts
index a99827b76..91ff42e8c 100644
--- a/tedi/components/buttons/button/button.component.ts
+++ b/tedi/components/buttons/button/button.component.ts
@@ -1,4 +1,4 @@
-import { Component, input, ViewEncapsulation } from "@angular/core";
+import { Component, computed, input, ViewEncapsulation } from "@angular/core";
import { BaseButtonDirective } from "./base-button.directive";
export type ButtonVariant =
@@ -25,6 +25,9 @@ export type ButtonSize = "default" | "small";
directive: BaseButtonDirective,
},
],
+ host: {
+ "[class]": "classes()",
+ },
})
export class ButtonComponent {
/**
@@ -37,4 +40,13 @@ export class ButtonComponent {
* @default default
*/
size = input("default");
+
+ classes = computed(() => {
+ const classList = [
+ "tedi-button",
+ `tedi-button--${this.variant()}`,
+ `tedi-button--${this.size()}`,
+ ];
+ return classList.join(" ");
+ });
}
From 7f3c5b3b6777f00fc4e0f2c68bcaa8f59109c636 Mon Sep 17 00:00:00 2001
From: Artur Lang <209750178+artur-langl@users.noreply.github.com>
Date: Thu, 20 Nov 2025 10:24:51 +0200
Subject: [PATCH 21/23] feat(floating-button): remove debug log #195
---
.../buttons/floating-button/floating-button.component.ts | 1 -
1 file changed, 1 deletion(-)
diff --git a/community/components/buttons/floating-button/floating-button.component.ts b/community/components/buttons/floating-button/floating-button.component.ts
index ea0d8b060..802796dd5 100644
--- a/community/components/buttons/floating-button/floating-button.component.ts
+++ b/community/components/buttons/floating-button/floating-button.component.ts
@@ -48,7 +48,6 @@ export class FloatingButtonComponent implements OnInit {
ngOnInit() {
this.buttonDirective.classNamePrefix.set("tedi-floating-button");
- console.log("set", this.buttonDirective);
}
floatClasses = computed(() => {
From 4b37b131db7325f0168c97ff9edb658a0f174e15 Mon Sep 17 00:00:00 2001
From: Artur Lang <209750178+artur-langl@users.noreply.github.com>
Date: Thu, 20 Nov 2025 13:33:10 +0200
Subject: [PATCH 22/23] feat(floating-button): fix scss build warning #195
---
.../floating-button.component.scss | 3 +-
.../buttons/button/button.component.scss | 145 +++++++++---------
2 files changed, 76 insertions(+), 72 deletions(-)
diff --git a/community/components/buttons/floating-button/floating-button.component.scss b/community/components/buttons/floating-button/floating-button.component.scss
index 1ffa4c11f..09eb8c548 100644
--- a/community/components/buttons/floating-button/floating-button.component.scss
+++ b/community/components/buttons/floating-button/floating-button.component.scss
@@ -4,9 +4,10 @@
buttonMixins;
button.tedi-floating-button {
- @include buttonMixins.button-main-styles();
box-shadow: 0 4px 10px 0 var(--alpha-14);
+ @include buttonMixins.button-main-styles();
+
&--vertical {
min-width: max-content;
white-space: nowrap;
diff --git a/tedi/components/buttons/button/button.component.scss b/tedi/components/buttons/button/button.component.scss
index 627a0681d..17463dbe8 100644
--- a/tedi/components/buttons/button/button.component.scss
+++ b/tedi/components/buttons/button/button.component.scss
@@ -111,90 +111,94 @@ $neutral-variants: "neutral", "neutral-inverted", "danger-neutral";
}
@mixin button-main-styles {
- display: inline-flex;
- align-items: center;
- justify-content: center;
- gap: var(--_btn-inner-spacing);
- text-decoration: none;
- background: var(--_btn-bg);
- color: var(--_btn-text);
- border: var(--borders-01) solid var(--_btn-border);
- padding: var(--_btn-padding);
- transition: 150ms ease;
- transition-property: background-color, border-color;
- cursor: pointer;
-
- @include mixins.responsive-styles(
- font-family,
- family-primary,
- $exclude: tablet
- );
- @include mixins.responsive-styles(font-weight, body-regular-weight);
- @include mixins.responsive-styles(line-height, body-bold-line-height);
- @include mixins.responsive-styles(border-radius, button-radius-default);
+ & {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: var(--_btn-inner-spacing);
+ text-decoration: none;
+ background: var(--_btn-bg);
+ color: var(--_btn-text);
+ border: var(--borders-01) solid var(--_btn-border);
+ padding: var(--_btn-padding);
+ transition: 150ms ease;
+ transition-property: background-color, border-color;
+ cursor: pointer;
- &--icon-only {
- @include mixins.responsive-styles(width, button-md-icon-size);
- @include mixins.responsive-styles(height, button-md-icon-size);
- @include mixins.responsive-styles(padding, button-md-icon-padding);
- }
+ @include mixins.responsive-styles(
+ font-family,
+ family-primary,
+ $exclude: tablet
+ );
+ @include mixins.responsive-styles(font-weight, body-regular-weight);
+ @include mixins.responsive-styles(line-height, body-bold-line-height);
+ @include mixins.responsive-styles(border-radius, button-radius-default);
+
+ &--icon-only {
+ @include mixins.responsive-styles(width, button-md-icon-size);
+ @include mixins.responsive-styles(height, button-md-icon-size);
+ @include mixins.responsive-styles(padding, button-md-icon-padding);
+ }
- &--pl {
- &:not(.tedi-button--neutral):not(.tedi-button--danger-neutral):not(
- .tedi-button--neutral-inverted
- ) {
- padding-left: calc(
- var(--_btn-padding-x) + var(--_btn-inner-spacing) - 1px
- );
+ &--pl {
+ &:not(.tedi-button--neutral):not(.tedi-button--danger-neutral):not(
+ .tedi-button--neutral-inverted
+ ) {
+ padding-left: calc(
+ var(--_btn-padding-x) + var(--_btn-inner-spacing) - 1px
+ );
+ }
}
- }
- &--pr {
- &:not(.tedi-button--neutral):not(.tedi-button--danger-neutral):not(
- .tedi-button--neutral-inverted
- ) {
- padding-right: calc(
- var(--_btn-padding-x) + var(--_btn-inner-spacing) - 1px
- );
+ &--pr {
+ &:not(.tedi-button--neutral):not(.tedi-button--danger-neutral):not(
+ .tedi-button--neutral-inverted
+ ) {
+ padding-right: calc(
+ var(--_btn-padding-x) + var(--_btn-inner-spacing) - 1px
+ );
+ }
}
- }
- &:hover {
- background: var(--_btn-hover-bg);
- border-color: var(--_btn-hover-border);
- color: var(--_btn-hover-text);
- }
+ &:hover {
+ background: var(--_btn-hover-bg);
+ border-color: var(--_btn-hover-border);
+ color: var(--_btn-hover-text);
+ }
- &:active {
- background: var(--_btn-active-bg);
- border-color: var(--_btn-active-border);
- color: var(--_btn-active-text);
- }
+ &:active {
+ background: var(--_btn-active-bg);
+ border-color: var(--_btn-active-border);
+ color: var(--_btn-active-text);
+ }
- &:focus-visible {
- background: var(--_btn-focus-bg);
- border-color: var(--_btn-focus-border);
- color: var(--_btn-focus-text);
- outline: 2px solid var(--_btn-outline);
- outline-offset: 1px;
- }
+ &:focus-visible {
+ background: var(--_btn-focus-bg);
+ border-color: var(--_btn-focus-border);
+ color: var(--_btn-focus-text);
+ outline: 2px solid var(--_btn-outline);
+ outline-offset: 1px;
+ }
- &:disabled {
- background: var(--_btn-disabled-bg);
- border-color: var(--_btn-disabled-border);
- color: var(--_btn-disabled-text);
- cursor: not-allowed;
- }
+ &:disabled {
+ background: var(--_btn-disabled-bg);
+ border-color: var(--_btn-disabled-border);
+ color: var(--_btn-disabled-text);
+ cursor: not-allowed;
+ }
- tedi-icon {
- color: inherit;
- font-size: inherit;
+ tedi-icon {
+ color: inherit;
+ font-size: inherit;
+ }
}
}
@mixin button-size($size) {
- @include button-size-vars(#{$size});
- @include mixins.responsive-styles(font-size, button-text-size-#{$size});
+ & {
+ @include button-size-vars(#{$size});
+ @include mixins.responsive-styles(font-size, button-text-size-#{$size});
+ }
}
.tedi-button {
@@ -208,7 +212,6 @@ $neutral-variants: "neutral", "neutral-inverted", "danger-neutral";
@include button-size("md");
}
-
&--primary {
@include button-variant-color-vars("primary");
}
From 0e80438c77ad53fc79b5e3bc746f665e001b96cb Mon Sep 17 00:00:00 2001
From: Artur Lang <209750178+artur-langl@users.noreply.github.com>
Date: Wed, 3 Dec 2025 13:03:04 +0200
Subject: [PATCH 23/23] feat(floating-button): add barrel index eports #195
---
community/components/buttons/floating-button/index.ts | 1 +
community/components/buttons/index.ts | 1 +
community/index.ts | 1 +
3 files changed, 3 insertions(+)
create mode 100644 community/components/buttons/floating-button/index.ts
create mode 100644 community/components/buttons/index.ts
diff --git a/community/components/buttons/floating-button/index.ts b/community/components/buttons/floating-button/index.ts
new file mode 100644
index 000000000..6114a84bf
--- /dev/null
+++ b/community/components/buttons/floating-button/index.ts
@@ -0,0 +1 @@
+export * from "./floating-button.component";
diff --git a/community/components/buttons/index.ts b/community/components/buttons/index.ts
new file mode 100644
index 000000000..96e4859ad
--- /dev/null
+++ b/community/components/buttons/index.ts
@@ -0,0 +1 @@
+export * from "./floating-button";
diff --git a/community/index.ts b/community/index.ts
index 3c0b3f1ac..fa989c6d1 100644
--- a/community/index.ts
+++ b/community/index.ts
@@ -1,3 +1,4 @@
+export * from "./components/buttons";
export * from "./components/cards";
export * from "./components/form";
export * from "./components/navigation";