();
+ readonly selected = model(false);
+
+ readonly disabledInput = input(false, {
+ transform: booleanAttribute,
+ // eslint-disable-next-line @angular-eslint/no-input-rename
+ alias: "disabled",
+ });
+
+ selectTab() {
+ if (this.disabledInput()) {
+ return;
+ }
+
+ this.selected.set(true);
+ }
+}
diff --git a/community/components/navigation/tabs/tabs-vertical/tabs-vertical.component.html b/community/components/navigation/tabs/tabs-vertical/tabs-vertical.component.html
new file mode 100644
index 00000000..a85f63ee
--- /dev/null
+++ b/community/components/navigation/tabs/tabs-vertical/tabs-vertical.component.html
@@ -0,0 +1,22 @@
+
+
+
+
+@if (activeTabContent(); as activeContent) {
+
+
+ {{ activeTabTitle() }}
+
+
+
+
+
+
+} @else {
+
+
+
+}
diff --git a/community/components/navigation/tabs/tabs-vertical/tabs-vertical.component.scss b/community/components/navigation/tabs/tabs-vertical/tabs-vertical.component.scss
new file mode 100644
index 00000000..1f9df0eb
--- /dev/null
+++ b/community/components/navigation/tabs/tabs-vertical/tabs-vertical.component.scss
@@ -0,0 +1,23 @@
+.tedi-tabs-vertical {
+ display: flex;
+ flex-direction: column;
+ align-items: start;
+ gap: var(--dimensions-10);
+
+ &__accordion,
+ &__card {
+ width: 100%;
+ }
+
+ &__back-link.tedi-button {
+ display: flex;
+ align-items: center;
+ gap: var(--dimensions-03);
+ cursor: pointer;
+ padding: var(--dimensions-02) var(--dimensions-03);
+ }
+
+ &__back-link > * {
+ color: var(--button-main-neutral-text-active);
+ }
+}
diff --git a/community/components/navigation/tabs/tabs-vertical/tabs-vertical.component.ts b/community/components/navigation/tabs/tabs-vertical/tabs-vertical.component.ts
new file mode 100644
index 00000000..756dab32
--- /dev/null
+++ b/community/components/navigation/tabs/tabs-vertical/tabs-vertical.component.ts
@@ -0,0 +1,52 @@
+import {ChangeDetectionStrategy, Component, computed, contentChildren, ViewEncapsulation} from '@angular/core';
+import {CardComponent, CardContentComponent, TabContentComponent} from "@tedi-design-system/angular/community";
+import {TabCardComponent} from "../tab-card/tab-card.component";
+import {
+ AccordionComponent,
+ ButtonComponent,
+ IconComponent,
+ TediTranslationPipe,
+ TextComponent
+} from "@tedi-design-system/angular/tedi";
+import {NgTemplateOutlet} from "@angular/common";
+
+@Component({
+ selector: 'tedi-tabs-vertical',
+ imports: [
+ AccordionComponent,
+ ButtonComponent,
+ CardComponent,
+ CardContentComponent,
+ IconComponent,
+ NgTemplateOutlet,
+ TediTranslationPipe,
+ TextComponent
+ ],
+ templateUrl: './tabs-vertical.component.html',
+ styleUrl: './tabs-vertical.component.scss',
+ encapsulation: ViewEncapsulation.None,
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ host: {
+ "[class.tedi-tabs-vertical]": "true",
+ },
+})
+export class TabsVerticalComponent {
+ private readonly tabs = contentChildren(TabCardComponent);
+ private readonly tabContents = contentChildren(TabContentComponent);
+
+ activeTabId = computed(() =>
+ this.tabs().find((tab) => tab.selected())?.tabId()
+ );
+
+ activeTabTitle = computed(() =>
+ this.tabs().find((tab) => tab.selected())?.title()
+ );
+
+ activeTabContent = computed(() =>
+ this.tabContents().find((content) => content.tabId() === this.activeTabId())?.content()
+ );
+
+ unselectAllTabs() {
+ this.tabs().forEach(tab => tab.selected.set(false));
+ }
+}
diff --git a/community/components/navigation/tabs/tabs.stories.ts b/community/components/navigation/tabs/tabs.stories.ts
index 9c6663e9..cab53c58 100644
--- a/community/components/navigation/tabs/tabs.stories.ts
+++ b/community/components/navigation/tabs/tabs.stories.ts
@@ -1,9 +1,11 @@
-import { Meta, moduleMetadata, StoryFn, StoryObj } from "@storybook/angular";
+import {Meta, moduleMetadata, StoryFn, StoryObj} from "@storybook/angular";
-import { CommonModule } from "@angular/common";
-import { TabsComponent } from "./tabs.component";
-import { TabComponent } from "./tab/tab.component";
-import { TabContentComponent } from "./tab-content/tab-content.component";
+import {CommonModule} from "@angular/common";
+import {TabsComponent} from "./tabs.component";
+import {TabComponent} from "./tab/tab.component";
+import {TabContentComponent} from "./tab-content/tab-content.component";
+import {TabCardComponent} from "./tab-card/tab-card.component";
+import {TabsVerticalComponent} from "./tabs-vertical/tabs-vertical.component";
/**
* Tabs allow to group content into separate chunks to be displayed one at the time.
@@ -17,7 +19,7 @@ export default {
decorators: [
moduleMetadata({
declarations: [],
- imports: [CommonModule, TabsComponent, TabComponent, TabContentComponent],
+ imports: [CommonModule, TabsComponent, TabComponent, TabContentComponent, TabCardComponent, TabsVerticalComponent],
}),
],
argTypes: {
@@ -25,7 +27,7 @@ export default {
description: "Tab unique id",
table: {
category: "tab",
- type: { summary: "string" },
+ type: {summary: "string"},
},
},
selected: {
@@ -33,16 +35,16 @@ export default {
"Whether tab is initially selected. Should be used only for non routed tabs",
table: {
category: "tab",
- type: { summary: "boolean" },
- defaultValue: { summary: "false" },
+ type: {summary: "boolean"},
+ defaultValue: {summary: "false"},
},
},
disabled: {
description: "Whether tab is disabled",
table: {
category: "tab",
- type: { summary: "boolean" },
- defaultValue: { summary: "false" },
+ type: {summary: "boolean"},
+ defaultValue: {summary: "false"},
},
},
contentTabId: {
@@ -50,14 +52,39 @@ export default {
description: "Id that matches `tabId` given to the `tedi-tab` component",
table: {
category: "tab-content",
- type: { summary: "string" },
+ type: {summary: "string"},
+ },
+ },
+ tabCardId: {
+ name: "tabId",
+ description: "Tab unique id",
+ table: {
+ category: "tab-card",
+ type: {summary: "string"},
+ },
+ },
+ tabCardTitle: {
+ name: "title",
+ description: "Tab title",
+ table: {
+ category: "tab-card",
+ type: {summary: "string"},
+ },
+ },
+ tabCardDisabled: {
+ name: "disabled",
+ description: "Whether tab is disabled",
+ table: {
+ category: "tab-card",
+ type: {summary: "boolean"},
+ defaultValue: {summary: "false"},
},
},
},
} as Meta;
-const TabsTemplate: StoryFn = ({ ...args }) => ({
- props: { ...args },
+const TabsTemplate: StoryFn = ({...args}) => ({
+ props: {...args},
template: `
@@ -76,19 +103,40 @@ const TabsTemplate: StoryFn = ({ ...args }) => ({
`,
});
-const RoutedTabTemplate: StoryFn = ({ ...args }) => ({
- props: { ...args },
+const RoutedTabTemplate: StoryFn = ({...args}) => ({
+ props: {...args},
template: `
Tab 1
Tab 2
Tab 3
-
+
router-outlet goes here
`,
});
+const VerticalTabTemplate: StoryFn = ({...args}) => ({
+ props: {...args},
+ template: `
+
+
+
+
+
+
+ Tab 1 content
+
+
+ Tab 2 content
+
+
+ Tab 3 content
+
+
+ `,
+});
+
type TableStylesStory = StoryObj;
export const Default: TableStylesStory = {
@@ -98,3 +146,7 @@ export const Default: TableStylesStory = {
export const RoutedTabs: TableStylesStory = {
render: RoutedTabTemplate,
};
+
+export const VerticalTabs: TableStylesStory = {
+ render: VerticalTabTemplate,
+};
diff --git a/tedi/services/translation/translations.ts b/tedi/services/translation/translations.ts
index 7665e535..d6180db2 100644
--- a/tedi/services/translation/translations.ts
+++ b/tedi/services/translation/translations.ts
@@ -65,6 +65,12 @@ export const translationsMap = {
en: "Breadcrumbs",
ru: "Навигационная цепочка",
},
+ "back": {
+ components: ["Tabs"],
+ et: "Tagasi",
+ en: "Back",
+ ru: "Назад",
+ },
more: {
components: ["Tabs"],
et: "Veel",