Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement a global "expand/collapse elements" button #6303

Merged
merged 19 commits into from Nov 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 3 additions & 2 deletions comments-bundle/contao/dca/tl_comments.php
Expand Up @@ -72,7 +72,8 @@
'mode' => DataContainer::MODE_SORTABLE,
'fields' => array('date'),
'panelLayout' => 'filter;sort,search,limit',
'defaultSearchField' => 'comment'
'defaultSearchField' => 'comment',
'limitHeight' => 104
),
'label' => array
(
Expand Down Expand Up @@ -556,7 +557,7 @@ public function listComments($arrRow)

return '
<div class="cte_type ' . $key . '"><a href="mailto:' . Idna::decodeEmail($arrRow['email']) . '" title="' . StringUtil::specialchars(Idna::decodeEmail($arrRow['email'])) . '">' . $arrRow['name'] . '</a>' . ($arrRow['website'] ? ' (<a href="' . $arrRow['website'] . '" title="' . StringUtil::specialchars($arrRow['website']) . '" target="_blank" rel="noreferrer noopener">' . $GLOBALS['TL_LANG']['MSC']['com_website'] . '</a>)' : '') . ' – ' . Date::parse(Config::get('datimFormat'), $arrRow['date']) . ' – IP ' . StringUtil::specialchars($arrRow['ip']) . '<br>' . $title . '</div>
<div class="cte_preview limit_height' . (!Config::get('doNotCollapse') ? ' h60' : '') . '">
<div class="cte_preview">
' . $arrRow['comment'] . '
</div>' . "\n ";
}
Expand Down
151 changes: 151 additions & 0 deletions core-bundle/assets/controllers/limit-height-controller.js
@@ -0,0 +1,151 @@
import { Controller } from '@hotwired/stimulus';

export default class extends Controller {
static values = {
max: Number,
expand: String,
collapse: String,
expandAll: String,
expandAllTitle: String,
collapseAll: String,
collapseAllTitle: String,
}

static targets = ['operation', 'node'];

initialize () {
super.initialize();
this.togglerMap = new WeakMap();
this.nextId = 1;
}

operationTargetConnected () {
this.updateOperation();
}

nodeTargetConnected (node) {
const style = window.getComputedStyle(node, null);
const padding = parseFloat(style.paddingTop) + parseFloat(style.paddingBottom);
const height = node.clientHeight - padding;

// Resize the element if it is higher than the maximum height
if (this.maxValue > height) {
return;
}

if (!node.id) {
node.id = `limit-height-${this.nextId++}`;
}

node.style.overflow = 'hidden';
node.style.maxHeight = `${this.maxValue}px`;

const button = document.createElement('button');
button.setAttribute('type', 'button');
button.title = this.expandValue;
button.innerHTML = '<span>...</span>';
button.classList.add('unselectable');
button.setAttribute('aria-expanded', 'false');
button.setAttribute('aria-controls', node.id);

button.addEventListener('click', (event) => {
event.preventDefault();
this.toggle(node);
this.updateOperation(event);
});

const toggler = document.createElement('div');
toggler.classList.add('limit_toggler');
toggler.append(button);

this.togglerMap.set(node, toggler);

node.append(toggler);
this.updateOperation();
}

nodeTargetDisconnected (node) {
if (!this.togglerMap.has(node)) {
return;
}

this.togglerMap.get(node).remove();
this.togglerMap.delete(node);
node.style.overflow = '';
node.style.maxHeight = '';
}

toggle (node) {
if (node.style.maxHeight === '') {
this.collapse(node);
} else {
this.expand(node);
}
}

expand (node) {
if (!this.togglerMap.has(node)) {
return;
}

node.style.maxHeight = '';
const button = this.togglerMap.get(node).querySelector('button');
button.title = this.collapseValue;
button.setAttribute('aria-expanded', 'true');
}

collapse (node) {
if (!this.togglerMap.has(node)) {
return;
}

node.style.maxHeight = `${this.maxValue}px`;
const button = this.togglerMap.get(node).querySelector('button');
button.title = this.expandValue;
button.setAttribute('aria-expanded', 'false');
}

toggleAll (event) {
event.preventDefault();
const isExpanded = this.hasExpanded() ^ event.altKey;

this.nodeTargets.forEach((node) => {
if (isExpanded) {
this.collapse(node);
} else {
this.expand(node);
}
});

this.updateOperation(event);
}

keypress (event) {
this.updateOperation(event);
}

updateOperation (event) {
if (!this.hasOperationTarget) {
return;
}

const hasTogglers = !!this.nodeTargets.find((el) => this.togglerMap.has(el));
const expanded = this.hasExpanded();

this.operationTarget.style.display = hasTogglers ? '' : 'none';
this.operationTarget.setAttribute('aria-controls', this.nodeTargets.map((el) => el.id).join(' '));
this.operationTarget.setAttribute('aria-expanded', expanded ? 'true' : 'false');

if (expanded ^ (event ? event.altKey : false)) {
this.operationTarget.innerText = this.collapseAllValue;
this.operationTarget.title = this.collapseAllTitleValue;
} else {
this.operationTarget.innerText = this.expandAllValue;
this.operationTarget.title = this.expandAllTitleValue;
}
}

hasExpanded () {
return !!this.nodeTargets.find((el) => this.togglerMap.has(el) && el.style.maxHeight === '');
}
}
4 changes: 4 additions & 0 deletions core-bundle/assets/scripts/limit-height.js
@@ -1,5 +1,9 @@
window.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('div.limit_height').forEach(function(div) {
if (window.console) {
console.warn('Using "limit_height" class on child_record_callback is deprecated. Set a list.sorting.limitHeight in your DCA instead.');
}

const parent = div.parentNode.closest('.tl_content');

// Return if the element is a wrapper
Expand Down
1 change: 0 additions & 1 deletion core-bundle/contao/config/default.php
Expand Up @@ -98,7 +98,6 @@
. 'zip,rar,7z,md';
$GLOBALS['TL_CONFIG']['installPassword'] = '';
$GLOBALS['TL_CONFIG']['backendTheme'] = 'flexible';
$GLOBALS['TL_CONFIG']['doNotCollapse'] = false;
$GLOBALS['TL_CONFIG']['minPasswordLength'] = 8;
$GLOBALS['TL_CONFIG']['defaultFileChmod'] = 0644;
$GLOBALS['TL_CONFIG']['defaultFolderChmod'] = 0755;
Expand Down
5 changes: 3 additions & 2 deletions core-bundle/contao/dca/tl_content.php
Expand Up @@ -66,7 +66,8 @@
'defaultSearchField' => 'text',
'headerFields' => array('title', 'headline', 'author', 'tstamp', 'start', 'stop'),
'child_record_callback' => array('tl_content', 'addCteType'),
'renderAsGrid' => true
'renderAsGrid' => true,
'limitHeight' => 160
),
'operations' => array
(
Expand Down Expand Up @@ -1256,7 +1257,7 @@ public function addCteType($arrRow)

return '
<div class="cte_type ' . $key . '">' . $type . '</div>
<div class="' . $class . ' limit_height' . (!Config::get('doNotCollapse') ? ' h112' : '') . '">' . $preview . '</div>';
<div class="' . $class . '">' . $preview . '</div>';
}

/**
Expand Down
5 changes: 3 additions & 2 deletions core-bundle/contao/dca/tl_form_field.php
Expand Up @@ -60,7 +60,8 @@
'defaultSearchField' => 'label',
'headerFields' => array('title', 'tstamp', 'formID', 'storeValues', 'sendViaEmail', 'recipient', 'subject'),
'child_record_callback' => array('tl_form_field', 'listFormFields'),
'renderAsGrid' => true
'renderAsGrid' => true,
'limitHeight' => 104
),
'operations' => array
(
Expand Down Expand Up @@ -633,7 +634,7 @@ public function listFormFields($arrRow)

$strType = '
<div class="cte_type ' . $key . '">' . $GLOBALS['TL_LANG']['FFL'][$arrRow['type']][0] . ($objWidget->submitInput() && $arrRow['name'] ? ' (' . $arrRow['name'] . ')' : '') . '</div>
<div class="cte_preview limit_height' . (!Config::get('doNotCollapse') ? ' h52' : '') . '">';
<div class="cte_preview">';

$strWidget = $objWidget->parse();
$strWidget = preg_replace('/ name="[^"]+"/i', '', $strWidget);
Expand Down
34 changes: 32 additions & 2 deletions core-bundle/contao/drivers/DC_Table.php
Expand Up @@ -4500,6 +4500,7 @@ protected function parentView()
$labelPasteNew = $GLOBALS['TL_LANG'][$this->strTable]['pastenew'] ?? $GLOBALS['TL_LANG']['DCA']['pastenew'];
$labelPasteAfter = $GLOBALS['TL_LANG'][$this->strTable]['pasteafter'] ?? $GLOBALS['TL_LANG']['DCA']['pasteafter'];
$labelEditHeader = $GLOBALS['TL_LANG'][$this->ptable]['edit'] ?? $GLOBALS['TL_LANG']['DCA']['edit'];
$limitHeight = BackendUser::getInstance()->doNotCollapse ? false : (int) ($GLOBALS['TL_DCA'][$this->strTable]['list']['sorting']['limitHeight'] ?? 0);

$db = Database::getInstance();
$security = System::getContainer()->get('security.helper');
Expand Down Expand Up @@ -4820,7 +4821,7 @@ protected function parentView()

$return .= '
<div class="tl_content' . ($blnWrapperStart ? ' wrapper_start' : '') . ($blnWrapperSeparator ? ' wrapper_separator' : '') . ($blnWrapperStop ? ' wrapper_stop' : '') . ($blnIndent ? ' indent indent_' . $intWrapLevel : '') . ($blnIndentFirst ? ' indent_first' : '') . ($blnIndentLast ? ' indent_last' : '') . ((string) $row[$i]['tstamp'] === '0' ? ' draft' : '') . (!empty($GLOBALS['TL_DCA'][$this->strTable]['list']['sorting']['child_record_class']) ? ' ' . $GLOBALS['TL_DCA'][$this->strTable]['list']['sorting']['child_record_class'] : '') . ' click2edit toggle_select">
<div class="inside hover-div">
<div class="inside hover-div"' . ($limitHeight && !$blnWrapperStart && !$blnWrapperStop && !$blnWrapperSeparator ? ' data-contao--limit-height-target="node"' : '') . '>
<div class="tl_content_right">';

// Opening wrappers
Expand Down Expand Up @@ -5010,6 +5011,20 @@ protected function parentView()
</form>';
}

if ($limitHeight)
{
$return = '<div
data-controller="contao--limit-height"
data-contao--limit-height-max-value="' . $limitHeight . '"
data-contao--limit-height-expand-value="' . $GLOBALS['TL_LANG']['MSC']['expandNode'] . '"
data-contao--limit-height-collapse-value="' . $GLOBALS['TL_LANG']['MSC']['collapseNode'] . '"
data-contao--limit-height-expand-all-value="' . $GLOBALS['TL_LANG']['DCA']['expandNodes'][0] . '"
data-contao--limit-height-expand-all-title-value="' . $GLOBALS['TL_LANG']['DCA']['expandNodes'][1] . '"
data-contao--limit-height-collapse-all-value="' . $GLOBALS['TL_LANG']['DCA']['collapseNodes'][0] . '"
data-contao--limit-height-collapse-all-title-value="' . $GLOBALS['TL_LANG']['DCA']['collapseNodes'][0] . '"
>' . $return . '</div>';
}

return $return;
}

Expand All @@ -5032,6 +5047,7 @@ protected function listView()

// Check the default labels (see #509)
$labelNew = $GLOBALS['TL_LANG'][$this->strTable]['new'] ?? $GLOBALS['TL_LANG']['DCA']['new'];
$limitHeight = BackendUser::getInstance()->doNotCollapse ? false : (int) ($GLOBALS['TL_DCA'][$this->strTable]['list']['sorting']['limitHeight'] ?? 0);

$query = "SELECT * FROM " . $this->strTable;

Expand Down Expand Up @@ -5316,7 +5332,7 @@ protected function listView()
}
else
{
$return .= '<td class="tl_file_list">' . $label . '</td>';
$return .= '<td class="tl_file_list">' . ($limitHeight ? '<div data-contao--limit-height-target="node">' : '') . $label . ($limitHeight ? '</div>' : '') . '</td>';
}

// Buttons ($row, $table, $root, $blnCircularReference, $childs, $previous, $next)
Expand Down Expand Up @@ -5417,6 +5433,20 @@ protected function listView()
}
}

if ($limitHeight)
{
$return = '<div
data-controller="contao--limit-height"
data-contao--limit-height-max-value="' . $limitHeight . '"
data-contao--limit-height-expand-value="' . $GLOBALS['TL_LANG']['MSC']['expandNode'] . '"
data-contao--limit-height-collapse-value="' . $GLOBALS['TL_LANG']['MSC']['collapseNode'] . '"
data-contao--limit-height-expand-all-value="' . $GLOBALS['TL_LANG']['DCA']['expandNodes'][0] . '"
data-contao--limit-height-expand-all-title-value="' . $GLOBALS['TL_LANG']['DCA']['expandNodes'][1] . '"
data-contao--limit-height-collapse-all-value="' . $GLOBALS['TL_LANG']['DCA']['collapseNodes'][0] . '"
data-contao--limit-height-collapse-all-title-value="' . $GLOBALS['TL_LANG']['DCA']['collapseNodes'][0] . '"
>' . $return . '</div>';
}

return $return;
}

Expand Down
1 change: 1 addition & 0 deletions core-bundle/contao/library/Contao/Config.php
Expand Up @@ -80,6 +80,7 @@ class Config
private static $arrToBeRemoved = array
(
'disableCron',
'doNotCollapse',
);

/**
Expand Down
2 changes: 2 additions & 0 deletions core-bundle/contao/themes/flexible/backend.4354f825.css

Large diffs are not rendered by default.

Large diffs are not rendered by default.

2 changes: 0 additions & 2 deletions core-bundle/contao/themes/flexible/backend.c528680e.css

This file was deleted.

This file was deleted.

2 changes: 1 addition & 1 deletion core-bundle/contao/themes/flexible/entrypoints.json
Expand Up @@ -2,7 +2,7 @@
"entrypoints": {
"backend": {
"css": [
"/system/themes/flexible/backend.c528680e.css"
"/system/themes/flexible/backend.4354f825.css"
]
},
"confirm": {
Expand Down
4 changes: 2 additions & 2 deletions core-bundle/contao/themes/flexible/manifest.json
@@ -1,5 +1,5 @@
{
"backend.css": "/system/themes/flexible/backend.c528680e.css",
"backend.css": "/system/themes/flexible/backend.4354f825.css",
"confirm.css": "/system/themes/flexible/confirm.5231eaa5.css",
"conflict.css": "/system/themes/flexible/conflict.41a64ff6.css",
"diff.css": "/system/themes/flexible/diff.028ed04c.css",
Expand All @@ -8,7 +8,7 @@
"popup.css": "/system/themes/flexible/popup.751f0537.css",
"tinymce.css": "/system/themes/flexible/tinymce.e5009f94.css",
"tinymce-dark.css": "/system/themes/flexible/tinymce-dark.596023db.css",
"backend.c528680e.css.map": "/system/themes/flexible/backend.c528680e.css.map",
"backend.4354f825.css.map": "/system/themes/flexible/backend.4354f825.css.map",
"confirm.5231eaa5.css.map": "/system/themes/flexible/confirm.5231eaa5.css.map",
"conflict.41a64ff6.css.map": "/system/themes/flexible/conflict.41a64ff6.css.map",
"diff.028ed04c.css.map": "/system/themes/flexible/diff.028ed04c.css.map",
Expand Down
7 changes: 5 additions & 2 deletions core-bundle/contao/themes/flexible/styles/main.css
Expand Up @@ -474,7 +474,7 @@ body.popup {
/* Buttons */
#tl_buttons {
margin: 0;
padding: 12px 15px;
padding: 9px 15px;
text-align: right;
}

Expand Down Expand Up @@ -1113,6 +1113,7 @@ table.tl_listing {
}

.tl_file, .tl_file_list {
position: relative;
leofeyer marked this conversation as resolved.
Show resolved Hide resolved
padding: 5px 6px;
border-bottom: 1px solid var(--border);
background: var(--content-bg);
Expand Down Expand Up @@ -1615,7 +1616,6 @@ table.tl_listing {
width: 24px;
line-height: 8px;
color: var(--gray);
outline: none;
border-top-left-radius: 2px;
border-top-right-radius: 2px;
}
Expand Down Expand Up @@ -2497,9 +2497,12 @@ time[title] {
/* Default icon classes */
.header_icon, .header_clipboard, .header_back, .header_new, .header_rss, .header_edit_all, .header_delete_all,
.header_new_folder, .header_css_import, .header_theme_import, .header_store, .header_toggle, .header_sync {
display: inline-block;
padding: 3px 0 3px 21px;
background-color: transparent;
background-position: left center;
background-repeat: no-repeat;
border: none;
margin-left: 15px;
}

Expand Down
2 changes: 0 additions & 2 deletions core-bundle/public/backend.3c25ca82.js

This file was deleted.

1 change: 0 additions & 1 deletion core-bundle/public/backend.3c25ca82.js.map

This file was deleted.

2 changes: 2 additions & 0 deletions core-bundle/public/backend.e9f9bd8d.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions core-bundle/public/backend.e9f9bd8d.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion core-bundle/public/entrypoints.json
Expand Up @@ -2,7 +2,7 @@
"entrypoints": {
"backend": {
"js": [
"/bundles/contaocore/backend.3c25ca82.js"
"/bundles/contaocore/backend.e9f9bd8d.js"
]
}
}
Expand Down
4 changes: 2 additions & 2 deletions core-bundle/public/manifest.json
@@ -1,4 +1,4 @@
{
"backend.js": "/bundles/contaocore/backend.3c25ca82.js",
"backend.3c25ca82.js.map": "/bundles/contaocore/backend.3c25ca82.js.map"
"backend.js": "/bundles/contaocore/backend.e9f9bd8d.js",
"backend.e9f9bd8d.js.map": "/bundles/contaocore/backend.e9f9bd8d.js.map"
}