Commit 8b3b73da authored by Benni Mack's avatar Benni Mack Committed by Oliver Hader
Browse files

[BUGFIX] Streamline checkbox rendering in TYPO3 Backend

This change uses Twitter Bootstrap 5 usages across TYPO3 Backend,
and fixes the toggle / icon toggle switch.

Now the bootstrap-5 based toggle (form-switch) is used,
and can be modified easily with a custom SVG later-on, but is
kept as is for now, in order to make sure it is used consistently
everywhere.

Resolves: #93310
Releases: master
Change-Id: If52d06932e1ee087d9c7d76cfb7d0f0c0e92276a
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/67461


Tested-by: Oliver Bartsch's avatarOliver Bartsch <bo@cedev.de>
Tested-by: default avatarTYPO3com <noreply@typo3.com>
Tested-by: Oliver Hader's avatarOliver Hader <oliver.hader@typo3.org>
Reviewed-by: Oliver Bartsch's avatarOliver Bartsch <bo@cedev.de>
Reviewed-by: Oliver Hader's avatarOliver Hader <oliver.hader@typo3.org>
parent 17b2e47c
...@@ -17,43 +17,15 @@ $checkbox-focus-border: 1px solid rgba(0, 0, 0, 0.45); ...@@ -17,43 +17,15 @@ $checkbox-focus-border: 1px solid rgba(0, 0, 0, 0.45);
// //
// Component // Component
// //
.checkbox {
.checkbox-input {
opacity: 0;
}
.checkbox-label {
position: relative;
display: inline-flex;
align-items: baseline;
padding: 0;
min-height: 0;
transition: all 0.3s ease-in-out;
}
.checkbox-label-switch,
.checkbox-label-icon {
flex-shrink: 0;
transition: all 0.3s ease-in-out;
}
.checkbox-label-text {
padding-left: 6px;
position: relative;
display: inline-flex;
}
input + .help-link {
margin-left: 0.5rem;
font-weight: normal;
}
}
.form-check { .form-check {
padding-left: 0;
.form-check-input { .form-check-input {
opacity: 0; opacity: 1;
}
&.form-check-type-icon-toggle {
.form-check-input {
opacity: 0;
}
} }
.form-check-label { .form-check-label {
...@@ -65,18 +37,12 @@ $checkbox-focus-border: 1px solid rgba(0, 0, 0, 0.45); ...@@ -65,18 +37,12 @@ $checkbox-focus-border: 1px solid rgba(0, 0, 0, 0.45);
transition: all 0.3s ease-in-out; transition: all 0.3s ease-in-out;
} }
.checkbox-label-switch, .form-check-label-switch,
.checkbox-label-icon { .form-check-label-icon {
flex-shrink: 0; flex-shrink: 0;
transition: all 0.3s ease-in-out; transition: all 0.3s ease-in-out;
} }
.checkbox-label-text {
padding-left: 1rem;
position: relative;
display: inline-flex;
}
input + .help-link { input + .help-link {
margin-left: 0.5rem; margin-left: 0.5rem;
font-weight: normal; font-weight: normal;
...@@ -85,8 +51,8 @@ $checkbox-focus-border: 1px solid rgba(0, 0, 0, 0.45); ...@@ -85,8 +51,8 @@ $checkbox-focus-border: 1px solid rgba(0, 0, 0, 0.45);
// //
// Disabled // Disabled
// //
.checkbox { .form-check {
.checkbox-input:disabled + .checkbox-label { .form-check-input:disabled + .form-check-label {
opacity: 0.5; opacity: 0.5;
} }
} }
...@@ -95,75 +61,33 @@ $checkbox-focus-border: 1px solid rgba(0, 0, 0, 0.45); ...@@ -95,75 +61,33 @@ $checkbox-focus-border: 1px solid rgba(0, 0, 0, 0.45);
// Toggle // Toggle
// //
// Markup: // Markup:
// <div class="checkbox checkbox-type-toggle"> // <div class="form-check form-switch">
// <input type="checkbox" class="checkbox-input" value="1" id="checkbox-toggle"> // <input type="checkbox" class="form-check-input" value="1" id="checkbox-toggle">
// <label class="checkbox-label" for="checkbox-toggle"> // <label class="form-check-label" for="checkbox-toggle">
// <span class="checkbox-label-text">foo</span> // <span class="form-check-label-text">foo</span>
// </label> // </label>
// </div> // </div>
// //
// Styleguide component.checkbox.1 // Styleguide component.checkbox.1
// //
.checkbox-type-toggle { .form-switch {
.form-check-label, .form-check-input {
.checkbox-label { background-color: lighten($checkbox-bg, 10%);
overflow: visible; height: 16px;
padding-left: 36px;
&:before {
content: '';
position: absolute;
top: -1px;
left: 0;
padding-left: 0;
width: 36px;
height: 20px;
background-color: lighten($checkbox-bg, 10%);
border-radius: 10px;
border: 1px solid rgba(0, 0, 0, 0.3);
transition: all 0.3s ease-in-out;
}
&:after {
content: '';
position: absolute;
background-color: #fff;
height: 12px;
width: 12px;
top: 3px;
left: 5px;
border-radius: 50%;
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.9);
transition: all 0.3s ease-in-out;
}
}
.checkbox-input {
position: absolute;
margin-left: -20px;
}
.form-check-input:checked + .form-check-label, &:checked {
.checkbox-input:checked + .checkbox-label { border-color: $input-border-color;
&:before {
background-color: $checkbox-bg-active; background-color: $checkbox-bg-active;
} }
&:after { &:focus {
left: 19px; border-color: $input-border-focus;
}
}
.form-check-input:focus + .form-check-label,
.checkbox-input:focus + .checkbox-label {
&:before {
box-shadow: $checkbox-focus-shadow; box-shadow: $checkbox-focus-shadow;
border: $checkbox-focus-border;
} }
} }
.form-check-label { .form-check-label {
padding-left: 19px; line-height: 1.8em;
} }
} }
...@@ -171,18 +95,18 @@ $checkbox-focus-border: 1px solid rgba(0, 0, 0, 0.45); ...@@ -171,18 +95,18 @@ $checkbox-focus-border: 1px solid rgba(0, 0, 0, 0.45);
// Labeled Toggle // Labeled Toggle
// //
// Markup: // Markup:
// <div class="checkbox checkbox-type-labeled-toggle"> // <div class="form-check checkbox-type-labeled-toggle">
// <input type="checkbox" class="checkbox-input" value="1" id="checkbox-labeled-toggle"> // <input type="checkbox" class="form-check-input" value="1" id="checkbox-labeled-toggle">
// <label class="checkbox-label" for="checkbox-labeled-toggle"> // <label class="form-check-label" for="checkbox-labeled-toggle">
// <span class="checkbox-label-switch"> // <span class="form-check-label-switch">
// <span class="checkbox-label-switch-checked"> // <span class="form-check-label-switch-checked">
// Enabled // Enabled
// </span> // </span>
// <span class="checkbox-label-switch-unchecked"> // <span class="form-check-label-switch-unchecked">
// Disabled // Disabled
// </span> // </span>
// </span> // </span>
// <span class="checkbox-label-text"> // <span class="form-check-label-text">
// Foo // Foo
// </span> // </span>
// </label> // </label>
...@@ -190,11 +114,18 @@ $checkbox-focus-border: 1px solid rgba(0, 0, 0, 0.45); ...@@ -190,11 +114,18 @@ $checkbox-focus-border: 1px solid rgba(0, 0, 0, 0.45);
// //
// Styleguide component.checkbox.2 // Styleguide component.checkbox.2
// //
.checkbox-type-labeled-toggle { .form-check-type-labeled-toggle {
.checkbox-label-switch { padding-left: 0;
.form-check-input {
display: none;
}
.form-check-label-switch {
height: 24px; height: 24px;
background-color: $checkbox-bg-inactive; background-color: $checkbox-bg-inactive;
border-radius: 2px; border-radius: 2px;
margin-right: 0.5rem;
border: 1px solid rgba(0, 0, 0, 0.3); border: 1px solid rgba(0, 0, 0, 0.3);
color: #fff; color: #fff;
display: inline-flex; display: inline-flex;
...@@ -203,38 +134,38 @@ $checkbox-focus-border: 1px solid rgba(0, 0, 0, 0.45); ...@@ -203,38 +134,38 @@ $checkbox-focus-border: 1px solid rgba(0, 0, 0, 0.45);
text-align: center; text-align: center;
} }
.checkbox-label-switch-checked, .form-check-label-switch-checked,
.checkbox-label-switch-unchecked { .form-check-label-switch-unchecked {
padding-left: 8px; padding-left: 8px;
padding-right: 8px; padding-right: 8px;
flex-grow: 1; flex-grow: 1;
user-select: none; user-select: none;
} }
.checkbox-label-switch-checked { .form-check-label-switch-checked {
order: 2; order: 2;
} }
.checkbox-label-switch-unchecked { .form-check-label-switch-unchecked {
order: 1; order: 1;
} }
.checkbox-input:checked + .checkbox-label { .form-check-input:checked + .form-check-label {
.checkbox-label-switch { .form-check-label-switch {
background-color: $checkbox-bg-active; background-color: $checkbox-bg-active;
.checkbox-label-switch-checked { .form-check-label-switch-checked {
order: 1; order: 1;
} }
.checkbox-label-switch-unchecked { .form-check-label-switch-unchecked {
order: 2; order: 2;
} }
} }
} }
.checkbox-input:focus + .checkbox-label { .form-check-input:focus + .form-check-label {
.checkbox-label-switch { .form-check-label-switch {
box-shadow: $checkbox-focus-shadow; box-shadow: $checkbox-focus-shadow;
border: $checkbox-focus-border; border: $checkbox-focus-border;
} }
...@@ -245,18 +176,18 @@ $checkbox-focus-border: 1px solid rgba(0, 0, 0, 0.45); ...@@ -245,18 +176,18 @@ $checkbox-focus-border: 1px solid rgba(0, 0, 0, 0.45);
// Icon Toggle // Icon Toggle
// //
// Markup: // Markup:
// <div class="checkbox checkbox-type-icon-toggle"> // <div class="form-check form-check-type-icon-toggle">
// <input type="checkbox" class="checkbox-input" value="1" id="checkbox-icon-toggle"> // <input type="checkbox" class="form-check-input" value="1" id="checkbox-icon-toggle">
// <label class="checkbox-label" for="checkbox-icon-toggle"> // <label class="form-check-label" for="checkbox-icon-toggle">
// <span class="checkbox-label-icon"> // <span class="form-check-label-icon">
// <span class="checkbox-label-icon-checked"> // <span class="form-check-label-icon-checked">
// <span class="t3js-icon icon icon-size-small icon-state-default icon-actions-check" data-identifier="actions-check"> // <span class="t3js-icon icon icon-size-small icon-state-default icon-actions-check" data-identifier="actions-check">
// <span class="icon-markup"> // <span class="icon-markup">
// <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="M13.3 4.8l-.7-.7c-.2-.2-.5-.2-.7 0L6.5 9.5 4 6.9c-.2-.2-.5-.2-.7 0l-.6.7c-.2.2-.2.5 0 .7l3.6 3.6c.2.2.5.2.7 0l6.4-6.4c.1-.2.1-.5-.1-.7z" class="icon-color"></path></svg> // <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="M13.3 4.8l-.7-.7c-.2-.2-.5-.2-.7 0L6.5 9.5 4 6.9c-.2-.2-.5-.2-.7 0l-.6.7c-.2.2-.2.5 0 .7l3.6 3.6c.2.2.5.2.7 0l6.4-6.4c.1-.2.1-.5-.1-.7z" class="icon-color"></path></svg>
// </span> // </span>
// </span> // </span>
// </span> // </span>
// <span class="checkbox-label-icon-unchecked"> // <span class="form-check-label-icon-unchecked">
// <span class="t3js-icon icon icon-size-small icon-state-default icon-actions-close" data-identifier="actions-close"> // <span class="t3js-icon icon icon-size-small icon-state-default icon-actions-close" data-identifier="actions-close">
// <span class="icon-markup"> // <span class="icon-markup">
// <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="M11.9 5.5L9.4 8l2.5 2.5c.2.2.2.5 0 .7l-.7.7c-.2.2-.5.2-.7 0L8 9.4l-2.5 2.5c-.2.2-.5.2-.7 0l-.7-.7c-.2-.2-.2-.5 0-.7L6.6 8 4.1 5.5c-.2-.2-.2-.5 0-.7l.7-.7c.2-.2.5-.2.7 0L8 6.6l2.5-2.5c.2-.2.5-.2.7 0l.7.7c.2.2.2.5 0 .7z" class="icon-color"></path></svg> // <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="M11.9 5.5L9.4 8l2.5 2.5c.2.2.2.5 0 .7l-.7.7c-.2.2-.5.2-.7 0L8 9.4l-2.5 2.5c-.2.2-.5.2-.7 0l-.7-.7c-.2-.2-.2-.5 0-.7L6.6 8 4.1 5.5c-.2-.2-.2-.5 0-.7l.7-.7c.2-.2.5-.2.7 0L8 6.6l2.5-2.5c.2-.2.5-.2.7 0l.7.7c.2.2.2.5 0 .7z" class="icon-color"></path></svg>
...@@ -264,17 +195,19 @@ $checkbox-focus-border: 1px solid rgba(0, 0, 0, 0.45); ...@@ -264,17 +195,19 @@ $checkbox-focus-border: 1px solid rgba(0, 0, 0, 0.45);
// </span> // </span>
// </span> // </span>
// </span> // </span>
// <span class="checkbox-label-text">foo</span> // <span class="form-check-label-text">foo</span>
// </label> // </label>
// </div> // </div>
// //
// Styleguide component.checkbox.3 // Styleguide component.checkbox.3
// //
.checkbox-type-icon-toggle { .form-check-type-icon-toggle {
.checkbox-label-icon { padding-left: 0;
.form-check-label-icon {
border-radius: 2px; border-radius: 2px;
width: 24px;
height: 24px; height: 24px;
width: 24px;
padding-top: 2px; padding-top: 2px;
padding-bottom: 2px; padding-bottom: 2px;
text-align: center; text-align: center;
...@@ -283,33 +216,35 @@ $checkbox-focus-border: 1px solid rgba(0, 0, 0, 0.45); ...@@ -283,33 +216,35 @@ $checkbox-focus-border: 1px solid rgba(0, 0, 0, 0.45);
border: 1px solid rgba(0, 0, 0, 0.3); border: 1px solid rgba(0, 0, 0, 0.3);
} }
.checkbox-label-icon-checked { .form-check-label-text {
padding-left: 0.5rem;
}
.form-check-label-icon-checked {
display: none; display: none;
} }
.checkbox-label-icon-unchecked { .form-check-label-icon-unchecked {
display: inline-block; display: inline-block;
} }
.form-check-input:checked + .form-check-label, .form-check-input:checked + .form-check-label {
.checkbox-input:checked + .checkbox-label { .form-check-label-icon {
.checkbox-label-icon {
background-color: $checkbox-bg-active; background-color: $checkbox-bg-active;
color: #fff; color: #fff;
} }
.checkbox-label-icon-checked { .form-check-label-icon-checked {
display: inline-block; display: inline-block;
} }
.checkbox-label-icon-unchecked { .form-check-label-icon-unchecked {
display: none; display: none;
} }
} }
.form-check-input:focus + .form-check-label, .form-check-input:focus + .form-check-label {
.checkbox-input:focus + .checkbox-label { .form-check-label-icon {
.checkbox-label-icon {
box-shadow: $checkbox-focus-shadow; box-shadow: $checkbox-focus-shadow;
border: $checkbox-focus-border; border: $checkbox-focus-border;
} }
......
...@@ -147,6 +147,7 @@ $input-color-disabled: $gray-600; ...@@ -147,6 +147,7 @@ $input-color-disabled: $gray-600;
$input-height-large: (ceil($font-size-large * $line-height-large) + ($padding-large-vertical * 1.6) + 2); $input-height-large: (ceil($font-size-large * $line-height-large) + ($padding-large-vertical * 1.6) + 2);
$input-border-color: $gray-500; $input-border-color: $gray-500;
$form-label-font-weight: 700; $form-label-font-weight: 700;
$form-switch-width: 2.5em;
// Navbar // Navbar
$navbar-height: 45px; $navbar-height: 45px;
......
...@@ -676,9 +676,9 @@ class PageLayoutController ...@@ -676,9 +676,9 @@ class PageLayoutController
if ($this->getBackendUser()->check('tables_select', 'tt_content') && $numberOfHiddenElements > 0) { if ($this->getBackendUser()->check('tables_select', 'tt_content') && $numberOfHiddenElements > 0) {
// Toggle hidden ContentElements // Toggle hidden ContentElements
$tableOutput .= ' $tableOutput .= '
<div class="checkbox"> <div class="form-check">
<label for="checkTt_content_showHidden"> <input type="checkbox" id="checkTt_content_showHidden" class="form-check-input" name="SET[tt_content_showHidden]" value="1" ' . ($this->MOD_SETTINGS['tt_content_showHidden'] ? 'checked="checked"' : '') . ' />
<input type="checkbox" id="checkTt_content_showHidden" class="checkbox" name="SET[tt_content_showHidden]" value="1" ' . ($this->MOD_SETTINGS['tt_content_showHidden'] ? 'checked="checked"' : '') . ' /> <label class="form-check-label" for="checkTt_content_showHidden">
' . htmlspecialchars($this->getLanguageService()->getLL('hiddenCE')) . ' (<span class="t3js-hidden-counter">' . $numberOfHiddenElements . '</span>) ' . htmlspecialchars($this->getLanguageService()->getLL('hiddenCE')) . ' (<span class="t3js-hidden-counter">' . $numberOfHiddenElements . '</span>)
</label> </label>
</div>'; </div>';
......
...@@ -200,7 +200,7 @@ class CheckboxElement extends AbstractFormElement ...@@ -200,7 +200,7 @@ class CheckboxElement extends AbstractFormElement
$iconUnchecked = $this->iconFactory->getIcon($iconIdentifierUnchecked, Icon::SIZE_SMALL)->render('inline'); $iconUnchecked = $this->iconFactory->getIcon($iconIdentifierUnchecked, Icon::SIZE_SMALL)->render('inline');
return ' return '
<div class="form-check checkbox-type-icon-toggle' . ($inline ? ' checkbox-inline' : '') . (!$disabled ? '' : ' disabled') . '"> <div class="form-check form-check-type-icon-toggle' . ($inline ? ' form-check-inline' : '') . (!$disabled ? '' : ' disabled') . '">
<input type="checkbox" <input type="checkbox"
class="form-check-input" class="form-check-input"
value="1" value="1"
...@@ -209,11 +209,11 @@ class CheckboxElement extends AbstractFormElement ...@@ -209,11 +209,11 @@ class CheckboxElement extends AbstractFormElement
' . ($disabled ? ' disabled="disabled"' : '') . ' ' . ($disabled ? ' disabled="disabled"' : '') . '
id="' . $checkboxId . '" /> id="' . $checkboxId . '" />
<label class="form-check-label" for="' . $checkboxId . '"> <label class="form-check-label" for="' . $checkboxId . '">
<span class="checkbox-label-icon"> <span class="form-check-label-icon">
<span class="checkbox-label-icon-checked">' . $iconChecked . '</span> <span class="form-check-label-icon-checked">' . $iconChecked . '</span>
<span class="checkbox-label-icon-unchecked">' . $iconUnchecked . '</span> <span class="form-check-label-icon-unchecked">' . $iconUnchecked . '</span>
</span> </span>
<span class="checkbox-label-text">' . $this->appendValueToLabelInDebugMode(($label ? htmlspecialchars($label) : '&nbsp;'), $formElementValue) . '</span> <span class="form-check-label-text">' . $this->appendValueToLabelInDebugMode(($label ? htmlspecialchars($label) : '&nbsp;'), $formElementValue) . '</span>
</label> </label>
</div>'; </div>';
} }
......
...@@ -187,24 +187,24 @@ class CheckboxLabeledToggleElement extends AbstractFormElement ...@@ -187,24 +187,24 @@ class CheckboxLabeledToggleElement extends AbstractFormElement
$uniqueId = StringUtility::getUniqueId('_'); $uniqueId = StringUtility::getUniqueId('_');
$checkboxId = $additionalInformation['itemFormElID'] . '_' . $itemCounter . $uniqueId; $checkboxId = $additionalInformation['itemFormElID'] . '_' . $itemCounter . $uniqueId;
return ' return '
<div class="checkbox checkbox-type-labeled-toggle' . ($inline ? ' checkbox-inline' : '') . (!$disabled ? '' : ' disabled') . '"> <div class="form-check form-check-type-labeled-toggle' . ($inline ? ' form-check-inline' : '') . (!$disabled ? '' : ' disabled') . '">
<input type="checkbox" <input type="checkbox"
class="checkbox-input" class="form-check-input"
value="1" value="1"
data-formengine-input-name="' . htmlspecialchars($additionalInformation['itemFormElName']) . '" data-formengine-input-name="' . htmlspecialchars($additionalInformation['itemFormElName']) . '"
' . $checkboxParameters . ' ' . $checkboxParameters . '
' . (!$disabled ? '' : ' disabled="disabled"') . ' ' . (!$disabled ? '' : ' disabled="disabled"') . '
id="' . $checkboxId . '" /> id="' . $checkboxId . '" />
<label class="checkbox-label" for="' . $checkboxId . '"> <label class="form-check-label" for="' . $checkboxId . '">
<span class="checkbox-label-switch"> <span class="form-check-label-switch">
<span class="checkbox-label-switch-checked"> <span class="form-check-label-switch-checked">
' . $config['items'][$itemCounter]['labelChecked'] . ' ' . $config['items'][$itemCounter]['labelChecked'] . '
</span> </span>
<span class="checkbox-label-switch-unchecked"> <span class="form-check-label-switch-unchecked">
' . $config['items'][$itemCounter]['labelUnchecked'] . ' ' . $config['items'][$itemCounter]['labelUnchecked'] . '
</span> </span>
</span> </span>
<span class="checkbox-label-text">' . $this->appendValueToLabelInDebugMode(($label ? htmlspecialchars($label) : '&nbsp;'), $formElementValue) . '</span> <span class="form-check-label-text">' . $this->appendValueToLabelInDebugMode(($label ? htmlspecialchars($label) : '&nbsp;'), $formElementValue) . '</span>
</label> </label>
</div>'; </div>';
} }
......
...@@ -187,7 +187,7 @@ class CheckboxToggleElement extends AbstractFormElement ...@@ -187,7 +187,7 @@ class CheckboxToggleElement extends AbstractFormElement
$uniqueId = StringUtility::getUniqueId('_'); $uniqueId = StringUtility::getUniqueId('_');
$checkboxId = $additionalInformation['itemFormElID'] . '_' . $itemCounter . $uniqueId; $checkboxId = $additionalInformation['itemFormElID'] . '_' . $itemCounter . $uniqueId;
return ' return '
<div class="form-check benni checkbox-type-toggle' . ($inline ? ' checkbox-inline' : '') . (!$disabled ? '' : ' disabled') . '"> <div class="form-check form-switch' . ($inline ? ' form-check-inline' : '') . (!$disabled ? '' : ' disabled') . '">
<input type="checkbox" <input type="checkbox"
class="form-check-input" class="form-check-input"
value="1" value="1"
...@@ -196,7 +196,7 @@ class CheckboxToggleElement extends AbstractFormElement ...@@ -196,7 +196,7 @@ class CheckboxToggleElement extends AbstractFormElement
' . (!$disabled ? '' : ' disabled="disabled"') . ' ' . (!$disabled ? '' : ' disabled="disabled"') . '
id="' . $checkboxId . '" /> id="' . $checkboxId . '" />
<label class="form-check-label" for="' . $checkboxId . '"> <label class="form-check-label" for="' . $checkboxId . '">
<span class="checkbox-label-text">' . $this->appendValueToLabelInDebugMode(($label ? htmlspecialchars($label) : '&nbsp;'), $formElementValue) . '</span> <span class="form-check-label-text">' . $this->appendValueToLabelInDebugMode(($label ? htmlspecialchars($label) : '&nbsp;'), $formElementValue) . '</span>
</label>