Skip to content

Commit ebc5856

Browse files
committed
Revert "Feature: ID-freie Tabs mit optional modernem und vertikalem Layout (9.1.0, #401)"
This reverts commit ded0c4e.
1 parent ded0c4e commit ebc5856

12 files changed

Lines changed: 31 additions & 313 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,6 @@
11
# MForm - REDAXO Addon für Modul-Input-Formulare
2-
3-
## Version 9.1.0
4-
5-
### Neu
6-
7-
- **Tabs als ID-freie Implementierung**`addTabElement()` funktioniert jetzt stabil in verschachtelten und dynamischen Kontexten (z. B. FlexRepeater, Fieldset, Collapse, Modal), ohne Bootstrap-ID-Verkabelung und ohne Kollisionen zwischen geklonten Instanzen.
8-
- **Optionale Tab-Modernisierung** – Tab-Navigation kann optional als modernisierte Variante gerendert werden über `tab-style => 'modern'` bzw. `data-group-tab-style => 'modern'`.
9-
- **Optionale vertikale Tab-Navigation** – Tabs können optional links neben dem Inhalt dargestellt werden über `tab-layout => 'vertical'` bzw. `data-group-tab-layout => 'vertical'` (inkl. Mobile-Fallback auf gestapelte Ansicht).
10-
- **Font-Awesome-Icons je Tab** – Icon-Unterstützung über `tab-icon` bleibt erhalten und funktioniert weiterhin in den neuen Tab-Varianten.
11-
12-
### Kompatibilität
13-
14-
- Standardverhalten bleibt unverändert: Ohne neue Attribute bleiben Tabs visuell und funktional wie bisher.
15-
- Bestehende Module mit `addTabElement()` bleiben kompatibel.
16-
17-
---
18-
19-
## Version 9.0.1
2+
#
3+
# Version 9.0.1
204

215
### Behoben
226

assets/css/mform.css

Lines changed: 0 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -29,116 +29,6 @@
2929
background: #fff;
3030
}
3131

32-
.mform .mform-tabs {
33-
--mform-tab-nav-bg: #f7f9fc;
34-
--mform-tab-nav-border: #dfe5ee;
35-
--mform-tab-content-bg: #ffffff;
36-
--mform-tab-content-border: #dfe5ee;
37-
--mform-tab-link-color: #48586b;
38-
--mform-tab-link-active-bg: #ffffff;
39-
--mform-tab-link-active-color: #1d2e40;
40-
--mform-tab-link-hover-bg: #eef3f8;
41-
}
42-
43-
.mform .mform-tabs.mform-tabs--modern > .nav-tabs {
44-
border-bottom: 1px solid var(--mform-tab-nav-border);
45-
background: var(--mform-tab-nav-bg);
46-
padding: 6px 6px 0;
47-
}
48-
49-
.mform .mform-tabs.mform-tabs--modern > .nav-tabs > li > a {
50-
border-radius: 8px 8px 0 0;
51-
border: 1px solid transparent;
52-
color: var(--mform-tab-link-color);
53-
transition: background-color .2s ease, color .2s ease, border-color .2s ease;
54-
}
55-
56-
.mform .mform-tabs.mform-tabs--modern > .nav-tabs > li > a:hover,
57-
.mform .mform-tabs.mform-tabs--modern > .nav-tabs > li > a:focus {
58-
background: var(--mform-tab-link-hover-bg);
59-
color: var(--mform-tab-link-active-color);
60-
}
61-
62-
.mform .mform-tabs.mform-tabs--modern > .nav-tabs > li.active > a,
63-
.mform .mform-tabs.mform-tabs--modern > .nav-tabs > li.active > a:hover,
64-
.mform .mform-tabs.mform-tabs--modern > .nav-tabs > li.active > a:focus {
65-
background: var(--mform-tab-link-active-bg);
66-
border-color: var(--mform-tab-content-border) var(--mform-tab-content-border) var(--mform-tab-link-active-bg);
67-
color: var(--mform-tab-link-active-color);
68-
}
69-
70-
.mform .mform-tabs.mform-tabs--modern > .tab-content {
71-
border: 1px solid var(--mform-tab-content-border);
72-
border-top: 0;
73-
background: var(--mform-tab-content-bg);
74-
}
75-
76-
.mform .mform-tabs.mform-tabs--vertical {
77-
display: flex;
78-
align-items: stretch;
79-
}
80-
81-
.mform .mform-tabs.mform-tabs--vertical > .nav-tabs {
82-
width: 240px;
83-
min-width: 240px;
84-
border-bottom: 0;
85-
border-right: 1px solid var(--mform-tab-nav-border);
86-
background: var(--mform-tab-nav-bg);
87-
padding: 8px;
88-
}
89-
90-
.mform .mform-tabs.mform-tabs--vertical > .nav-tabs > li {
91-
float: none;
92-
width: 100%;
93-
margin-bottom: 4px;
94-
}
95-
96-
.mform .mform-tabs.mform-tabs--vertical > .nav-tabs > li > a {
97-
margin-right: 0;
98-
border-radius: 6px;
99-
border: 1px solid transparent;
100-
color: var(--mform-tab-link-color);
101-
}
102-
103-
.mform .mform-tabs.mform-tabs--vertical > .nav-tabs > li > a:hover,
104-
.mform .mform-tabs.mform-tabs--vertical > .nav-tabs > li > a:focus {
105-
background: var(--mform-tab-link-hover-bg);
106-
color: var(--mform-tab-link-active-color);
107-
}
108-
109-
.mform .mform-tabs.mform-tabs--vertical > .nav-tabs > li.active > a,
110-
.mform .mform-tabs.mform-tabs--vertical > .nav-tabs > li.active > a:hover,
111-
.mform .mform-tabs.mform-tabs--vertical > .nav-tabs > li.active > a:focus {
112-
background: var(--mform-tab-link-active-bg);
113-
border-color: var(--mform-tab-content-border);
114-
color: var(--mform-tab-link-active-color);
115-
}
116-
117-
.mform .mform-tabs.mform-tabs--vertical > .tab-content {
118-
flex: 1;
119-
border: 1px solid var(--mform-tab-content-border);
120-
border-left: 0;
121-
background: var(--mform-tab-content-bg);
122-
}
123-
124-
@media (max-width: 767px) {
125-
.mform .mform-tabs.mform-tabs--vertical {
126-
display: block;
127-
}
128-
129-
.mform .mform-tabs.mform-tabs--vertical > .nav-tabs {
130-
width: auto;
131-
min-width: 0;
132-
border-right: 0;
133-
border-bottom: 1px solid var(--mform-tab-nav-border);
134-
}
135-
136-
.mform .mform-tabs.mform-tabs--vertical > .tab-content {
137-
border-left: 1px solid var(--mform-tab-content-border);
138-
border-top: 0;
139-
}
140-
}
141-
14232
.mform .mform-tabs.rex-page-nav:last-child {
14333
margin-bottom: 0;
14434
}
@@ -279,17 +169,6 @@ body.rex-theme-dark .mform .mform-tabs .nav-tabs {
279169
/*background-color: rgba(0, 0, 0, 0.15);*/
280170
}
281171

282-
body.rex-theme-dark .mform .mform-tabs {
283-
--mform-tab-nav-bg: #19222d;
284-
--mform-tab-nav-border: #304050;
285-
--mform-tab-content-bg: #202b35;
286-
--mform-tab-content-border: #304050;
287-
--mform-tab-link-color: #b9c7d6;
288-
--mform-tab-link-active-bg: #202b35;
289-
--mform-tab-link-active-color: #eef4fb;
290-
--mform-tab-link-hover-bg: #243342;
291-
}
292-
293172
body.rex-theme-dark .mform .mform-tabs .tab-content {
294173
background-color: #202b35;
295174
}
@@ -430,17 +309,6 @@ body.rex-theme-dark .mform .custom-link.input-group.is-empty input.form-control[
430309
}
431310

432311
@media (prefers-color-scheme: dark) {
433-
body.rex-has-theme:not(.rex-theme-light) .mform .mform-tabs {
434-
--mform-tab-nav-bg: #19222d;
435-
--mform-tab-nav-border: #304050;
436-
--mform-tab-content-bg: #202b35;
437-
--mform-tab-content-border: #304050;
438-
--mform-tab-link-color: #b9c7d6;
439-
--mform-tab-link-active-bg: #202b35;
440-
--mform-tab-link-active-color: #eef4fb;
441-
--mform-tab-link-hover-bg: #243342;
442-
}
443-
444312
body.rex-has-theme:not(.rex-theme-light) .mform .mform-tabs .nav-tabs {
445313
/*background-color: rgba(0, 0, 0, 0.15);*/
446314
}

assets/mform.js

Lines changed: 7 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -190,47 +190,14 @@ function initMFormSelectPicker(mform) {
190190

191191
function initMFormTabs(mform) {
192192
mform.find('.mform-tabs').each(function () {
193-
let wrapper = $(this),
194-
nav = wrapper.children('ul[role=tablist]').first(),
195-
panes = wrapper.children('.tab-content').first().children('.tab-pane');
196-
197-
if (!nav.length || !panes.length) {
198-
return;
199-
}
200-
201-
function activateTab(tabId) {
202-
let links = nav.find('[data-mform-tab-toggle]'),
203-
targetLink = links.filter(function () {
204-
return String($(this).data('tab-item')) === String(tabId);
205-
}).first();
206-
207-
if (!targetLink.length) {
208-
targetLink = links.first();
209-
tabId = String(targetLink.data('tab-item'));
210-
}
211-
212-
nav.find('li[role=presentation]').removeClass('active');
213-
links.attr('aria-selected', 'false');
214-
targetLink.closest('li[role=presentation]').addClass('active');
215-
targetLink.attr('aria-selected', 'true');
216-
217-
panes.removeClass('active');
218-
panes.filter(function () {
219-
return String($(this).attr('data-tab-group-nav-tab-id')) === String(tabId);
220-
}).first().addClass('active');
221-
}
222-
223-
wrapper.off('click.mformTabs').on('click.mformTabs', '[data-mform-tab-toggle]', function (e) {
224-
e.preventDefault();
225-
activateTab($(this).data('tab-item'));
193+
let wrapper = $(this);
194+
$(this).find('ul[role=tablist] a').unbind().bind('click', function () {
195+
let tab = wrapper.find('div[data-tab-group-nav-tab-id=' + $(this).data('tab-item') + ']'),
196+
uid = Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
197+
tab.attr('id', uid);
198+
$(this).attr('href', '#' + uid);
199+
$('#' + uid).tab("show");
226200
});
227-
228-
let activeLink = nav.find('li.active [data-mform-tab-toggle]').first();
229-
if (activeLink.length) {
230-
activateTab(activeLink.data('tab-item'));
231-
} else {
232-
activateTab(nav.find('[data-mform-tab-toggle]').first().data('tab-item'));
233-
}
234201
});
235202
}
236203

assets/repeater.js

Lines changed: 7 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -66,47 +66,14 @@ window.repeater = () => {
6666
let tabs = $(element).find('.mform-tabs');
6767
if (tabs.length > 0) {
6868
tabs.each(function() {
69-
let wrapper = $(this),
70-
nav = wrapper.children('ul[role=tablist]').first(),
71-
panes = wrapper.children('.tab-content').first().children('.tab-pane');
72-
73-
if (!nav.length || !panes.length) {
74-
return;
75-
}
76-
77-
function activateTab(tabId) {
78-
let links = nav.find('[data-mform-tab-toggle]'),
79-
targetLink = links.filter(function () {
80-
return String($(this).data('tab-item')) === String(tabId);
81-
}).first();
82-
83-
if (!targetLink.length) {
84-
targetLink = links.first();
85-
tabId = String(targetLink.data('tab-item'));
86-
}
87-
88-
nav.find('li[role=presentation]').removeClass('active');
89-
links.attr('aria-selected', 'false');
90-
targetLink.closest('li[role=presentation]').addClass('active');
91-
targetLink.attr('aria-selected', 'true');
92-
93-
panes.removeClass('active');
94-
panes.filter(function () {
95-
return String($(this).attr('data-tab-group-nav-tab-id')) === String(tabId);
96-
}).first().addClass('active');
97-
}
98-
99-
wrapper.off('click.mformTabs').on('click.mformTabs', '[data-mform-tab-toggle]', function (e) {
100-
e.preventDefault();
101-
activateTab($(this).data('tab-item'));
69+
let wrapper = $(this);
70+
$(this).find('ul[role=tablist] a').unbind().bind('click', function() {
71+
let tab = wrapper.find('div[data-tab-group-nav-tab-id=' + $(this).data('tab-item') + ']'),
72+
uid = Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
73+
tab.attr('id', uid);
74+
$(this).attr('href', '#' + uid);
75+
$('#' + uid).tab("show");
10276
});
103-
104-
let activeLink = nav.find('li.active [data-mform-tab-toggle]').first();
105-
if (activeLink.length) {
106-
activateTab(activeLink.data('tab-item'));
107-
} else {
108-
activateTab(nav.find('[data-mform-tab-toggle]').first().data('tab-item'));
109-
}
11077
});
11178
}
11279
},

docs/05_wrapper.md

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,6 @@ echo $mform->show();
190190

191191
Stellt Tab-Elemente dar, die bei Klick den dargestellten Inhalt wechseln.
192192

193-
Optional kann die Darstellung modernisiert oder vertikal (Navigation links) ausgegeben werden.
194-
195193
```php
196194
<?php
197195
use FriendsOfRedaxo\MForm;
@@ -212,35 +210,6 @@ $mform = MForm::factory()
212210
echo $mform->show();
213211
```
214212

215-
### Optionale Tab-Varianten
216-
217-
```php
218-
<?php
219-
use FriendsOfRedaxo\MForm;
220-
221-
$mform = MForm::factory()
222-
->addTabElement('Inhalt', MForm::factory()
223-
->addTextField('1.0.1', ['label' => 'Titel'])
224-
, true, false, [
225-
'tab-icon' => 'fa-file-text-o',
226-
'tab-style' => 'modern',
227-
'tab-layout' => 'vertical',
228-
])
229-
->addTabElement('Einstellungen', MForm::factory()
230-
->addCheckboxField('1.0.2', ['label' => 'Aktiv'])
231-
, false, false, [
232-
'tab-icon' => 'fa-cog',
233-
]);
234-
235-
echo $mform->show();
236-
```
237-
238-
Hinweise:
239-
240-
- `tab-style => 'modern'` ist optional und aktiviert eine modernisierte Optik.
241-
- `tab-layout => 'vertical'` ist optional und zeigt die Tab-Navigation links neben dem Content.
242-
- Alternativ koennen die Roh-Attribute `data-group-tab-style` und `data-group-tab-layout` gesetzt werden.
243-
244213
## Modal
245214

246215
Öffnet ein Bootstrap-Modal mit einem Sub-Formular. Ein Button wird direkt im Formular eingebettet; alle Felder im Modal-Inhalt werden beim Speichern des Moduls normal übernommen – kein separater AJAX-Request erforderlich.

docs/07_repeater.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -273,11 +273,11 @@ Verfügbare Optionen im Repeater-Array:
273273

274274
## Wrapper im Repeater (Tabs, Collapse, Fieldset, Inline, Columns)
275275

276-
Seit **9.0.0** rendert der FlexRepeater alle gaengigen MForm-Wrapper auch innerhalb eines Repeater-Items. Damit lassen sich komplexe Item-Layouts wie im klassischen MForm-Pfad aufbauen – inkl. Tabs, Collapse-Gruppen, Fieldsets mit Legend, Form-Inline und Spalten-Grids.
276+
Seit **9.0.0** rendert der FlexRepeater alle gaengigen MForm-Wrapper auch innerhalb eines Repeater-Items. Damit lassen sich komplexe Item-Layouts wie im klassischen MForm-Pfad aufbauen – inkl. Bootstrap-3-Tabs, Collapse-Gruppen, Fieldsets mit Legend, Form-Inline und Spalten-Grids.
277277

278278
| Wrapper | Methode | Markup im Repeater-Item |
279279
|---------|---------|-------------------------|
280-
| Tabs | `addStartGroupTab()` / `addTab()` / `addCloseTab()` / `addCloseGroupTab()` | ID-freie `nav-tabs` + `tab-content` (scoped pro Wrapper) |
280+
| Tabs | `addStartGroupTab()` / `addTab()` / `addCloseTab()` / `addCloseGroupTab()` | Bootstrap-3 `nav-tabs` + `tab-content` |
281281
| Collapse (mit Toggle-Button) | `addCollapseElement()` | `<a data-toggle="collapse">` + `.collapse[data-group-collapse-id=…]` |
282282
| Collapse-Gruppe (Standalone-Toggle) | `addStartGroupCollapse()` / `addCloseGroupCollapse()` | `.collapse-group[data-group-accordion=0\|1]` |
283283
| Fieldset | `addFieldsetArea($legend, …)` | `<fieldset><legend>…</legend>…</fieldset>` |
@@ -287,7 +287,7 @@ Seit **9.0.0** rendert der FlexRepeater alle gaengigen MForm-Wrapper auch innerh
287287

288288
### Tabs im Repeater
289289

290-
Tabs werden ID-frei gerendert. Die Navigation wird innerhalb des naechstgelegenen `.mform-tabs`-Wrappers gescoped und per `data-tab-item`/`data-tab-group-nav-tab-id` verknuepft. Dadurch funktionieren geklonte Repeater-Items und verschachtelte Tab-Strukturen ohne ID-Kollisionen. Aktive Tabs werden weiterhin ueber `data-group-open-tab => true` markiert.
290+
Tab-Panel-IDs muessen pro Item-Klon eindeutig sein. Der Renderer schreibt deshalb Platzhalter `__MFRTAB_<n>__` in die Tab-`href`s, `aria-controls` und Tab-Pane-`id`s. `flex-repeater.js _renderItem()` ersetzt die Platzhalter beim Klonen jedes Items durch eine eindeutige UID (gleiche `n` → gleiche UID, damit Nav-Link und Pane matchen). Aktive Tabs werden ueber `data-group-open-tab => true` markiert (setzt `active` auf `<li>` und `tab-pane`).
291291

292292
```php
293293
$itemForm = MForm::factory()

docs/13_api_reference.md

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -213,12 +213,6 @@ Tab-Panel. Mehrere Tabs werden automatisch zu einer Tab-Leiste zusammengefasst.
213213
| `$openTab` | `bool` | `false` | Tab initial geöffnet |
214214
| `$pullNaviItemRight` | `bool` | `false` | Tab-Item nach rechts schieben |
215215

216-
Optionale `$attributes` fuer Tabs:
217-
218-
- `tab-icon` (z. B. `fa-cog`): Icon im Tab-Label.
219-
- `tab-style => 'modern'` oder `data-group-tab-style => 'modern'`: modernisierte Tab-Optik.
220-
- `tab-layout => 'vertical'` oder `data-group-tab-layout => 'vertical'`: vertikale Navigation links neben dem Tab-Content.
221-
222216
---
223217

224218
```php

fragments/mform/mform_wrapper.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,13 @@
4242

4343
// TAB
4444
case 'tabnavli':
45-
echo '<li role="presentation" class="' . $this->getVar('class') . '" data-tab-nav-item="' . $this->getVar('value') . '"><a href="#" role="tab" aria-selected="false" data-mform-tab-toggle="1" data-tab-item="' . $this->getVar('value') . '">' . $this->getVar('label') . '</a></li>';
45+
echo '<li role="presentation" class="' . $this->getVar('class') . '"><a href="#' . $this->getVar('value') . '" aria-controls="' . $this->getVar('value') . '" role="tab" data-toggle="tab" data-tab-item="' . $this->getVar('value') . '">' . $this->getVar('label') . '</a></li>';
4646
break;
4747
case 'tab':
4848
echo '<div role="tabpanel" class="tab-pane ' . $this->getVar('class') . '" ' . $this->getVar('attributes') . '>';
4949
break;
5050
case 'start-group-tab':
51-
echo '<div class="nav mform-tabs rex-page-nav ' . $this->getVar('class') . '" data-mform-tabs="1" ' . $this->getVar('attributes') . '><ul class="nav nav-tabs" role="tablist">' . $this->getVar('element') . '</ul><div class="tab-content">';
51+
echo '<div class="nav mform-tabs rex-page-nav" ' . $this->getVar('attributes') . '><ul class="nav nav-tabs" role="tablist">' . $this->getVar('element') . '</ul><div class="tab-content">';
5252
break;
5353

5454
// FIELDSET

0 commit comments

Comments
 (0)