x
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
<!-- Standalone Tab Underline -->
<nav class="nav nav-underline">
<a class="nav-link active" data-bs-toggle="tab" href="#">
Personal data
</a>
<a class="nav-link" data-bs-toggle="tab" href="#">
Email
</a>
<a class="nav-link" data-bs-toggle="tab" href="#">
ID
</a>
</nav>
<!-- Tab Underline In Card -->
<div class="card" data-controller="tab">
<div class="card-body">
<nav class="nav nav-underline">
<a class="nav-link active" data-bs-toggle="tab" href="#search-name-tab-content"
data-action="click->tab#toggleTabFieldsetAttributes">
Personal data
</a>
<a class="nav-link" data-bs-toggle="tab" href="#search-email-tab-content"
data-action="click->tab#toggleTabFieldsetAttributes">
Email
</a>
<a class="nav-link" data-bs-toggle="tab" href="#search-context-id-tab-content"
data-action="click->tab#toggleTabFieldsetAttributes">
ID
</a>
</nav>
<div class="tab-content">
<fieldset id="search-name-tab-content" data-tab-target="tabFieldset" class="tab-pane mt-3 active">
<div class="row">
<div class="col-6">
<div class="form-group has-float-label string required first_name">
<label class="form-label string required" for="first_name">
<abbr title="erforderlich">*</abbr> First name
</label>
<input class="form-control string required" autofocus="autofocus" required="required"
aria-required="true" type="text" name="#" id="first_name" autocomplete="given-name">
</div>
</div>
<div class="col-6">
<div class="form-group has-float-label string required last_name">
<label class="form-label string required" for="last_name">
<abbr title="erforderlich">*</abbr> Last name
</label>
<input class="form-control string required" required="required" aria-required="true" type="text"
name="#" id="last_name" autocomplete="family-name">
</div>
</div>
</div>
<div class="row mt-3">
<div class="col-6">
<div class="form-group has-float-label date required viseca_search_birthdate">
<label class="form-label date required" for="birthdate">
<abbr title="erforderlich">*</abbr> Birthdate
</label>
<input class="form-control date required" data-tab-target="searchAttribute" max="2099-01-01"
required="required" aria-required="true" type="date" name="#" id="birthdate" autocomplete="bday">
</div>
</div>
<div class="col-6">
<div class="d-flex align-items-end h-100 gap-3">
<input type="submit" name="commit" value="Search" class="btn btn-primary w-100"
data-disable-with="Suchen">
<button class="btn btn-secondary w-100">Clear</button>
</div>
</div>
</div>
</fieldset>
<fieldset id="search-email-tab-content" data-tab-target="tabFieldset" class="tab-pane mt-3">
<div class="row">
<div class="col-6">
<div class="form-group has-float-label string required email">
<label class="form-label string required" for="email">
<abbr title="erforderlich">*</abbr> Email
</label>
<input class="form-control string required" required="required" aria-required="true" type="text"
name="#" id="email" autocomplete="email">
</div>
</div>
<div class="col-6">
<div class="d-flex align-items-end h-100 gap-3">
<input type="submit" name="commit" value="Search" class="btn btn-primary w-100"
data-disable-with="Suchen">
<button class="btn btn-secondary w-100">Clear</button>
</div>
</div>
</div>
</fieldset>
<fieldset id="search-context-id-tab-content" data-tab-target="tabFieldset" class="tab-pane mt-3">
<div class="row">
<div class="col-6">
<div class="form-group has-float-label string required context_id">
<label class="form-label string required" for="context_id">
<abbr title="erforderlich">*</abbr> ID
</label>
<input class="form-control string required" required="required" aria-required="true" type="text"
name="#" id="context_id" autocomplete="off">
</div>
</div>
<div class="col-6">
<div class="d-flex align-items-end h-100 gap-3">
<input type="submit" name="commit" value="Search" class="btn btn-primary w-100"
data-disable-with="Suchen">
<button class="btn btn-secondary w-100">Clear</button>
</div>
</div>
</div>
</fieldset>
</div>
</div>
</div>
1
2
3
4
5
# Standalone Tab Underline
render template: 'components/tabs/_tab_underline_standalone'
# Tab Underline In Card
render template: 'components/tabs/_tab_underline_card'

_tab.scss

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
$nav-underline-nav-link-padding-x: 0;
$nav-underline-nav-link-padding-y: 1rem;
$primary: rgb(40, 215, 157);
.card {
--bs-card-spacer-x: 1.5rem;
--bs-card-spacer-y: 1.5rem;
--bs-card-border-width: 1px;
--bs-card-border-radius: 1rem;
--bs-card-cap-bg: initial;
--bs-card-cap-padding-x: 1.5rem;
--bs-card-cap-padding-y: 1rem;
--separator-thickness: 1px;
--separator-color: #{rgba($gray-900, 0.1)};
.card-separator {
background-color: var(--separator-color);
height: var(--separator-thickness);
margin: 1rem calc(var(--bs-card-spacer-x) * -1);
}
.nav-underline {
border-bottom: var(--separator-thickness) solid var(--separator-color);
// Counter the .card spacer margin to fully align the nav-underline with the card edge
margin: calc(var(--bs-card-spacer-y) * -1) calc(var(--bs-card-spacer-x) * -1)
var(--bs-card-spacer-y) calc(var(--bs-card-spacer-x) * -1);
}
}
.nav-underline {
--bs-nav-underline-gap: 0;
--nav-underline-link-active-color: #{$navbar-light-active-color};
.nav-link {
--bs-nav-link-padding-y: #{$nav-underline-nav-link-padding-y};
--bs-nav-link-color: #{$navbar-light-color};
--bs-nav-link-hover-color: #{$navbar-light-hover-color};
margin: 0 1.5rem;
&.active {
border-bottom-color: $primary;
font-weight: normal;
}
&:hover {
border-bottom-color: $primary;
}
}
}

tab_controller.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { Controller } from '@hotwired/stimulus';
export default class extends Controller {
static targets = ['tabFieldset'];
connect() {
this.toggleTabFieldsetAttributes();
}
// This method disables fields that are currently hidden in a different tab from the selected one,
// therefore preventing them from being submitted by a form.
toggleTabFieldsetAttributes() {
this.tabFieldsetTargets.forEach((tabFieldset) => {
const tabFieldsetVisible = tabFieldset.classList.contains('active');
tabFieldset.disabled = !tabFieldsetVisible;
const submitButton = tabFieldset.querySelector('input[type="submit"], input[type="button"]');
submitButton.type = tabFieldsetVisible ? 'submit' : 'button';
});
}
}