Commit 8fc5244a authored by Annett Jähnichen's avatar Annett Jähnichen
Browse files

[FEATURE] Refresh the look of pagemodule

The patch simplifies and optimizes the readability of the page module
interface. It is based on the UX Team's concept of improving user
experience for editors:
https://typo3.org/article/structured-content-initiative-feedback-wanted

Included changes:

* refactored and optimised underlying CSS for future adaptions
* visually simplified content element boxes and their header buttons
* visually differenciated hidden content elements with better opacity
  and dotted border
* placed the "new content element" buttons centered in preparation
  for further concept implementations in dedicated patches
* added a new button for content element context menu in the content
  element header right button bar

This patch was initiated by the UX Team and implemented by Benjamin
Kott.

Resolves: #97306
Resolves: #95247
Related: #94629
Releases: main
Change-Id: I9b4e21a7fc68ebcb17d18f65bf00096f3a295cfb
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/74254

Tested-by: Benjamin Franzke's avatarBenjamin Franzke <bfr@qbus.de>
Tested-by: core-ci's avatarcore-ci <typo3@b13.com>
Tested-by: Georg Ringer's avatarGeorg Ringer <georg.ringer@gmail.com>
Tested-by: Michael Telgkamp's avatarMichael Telgkamp <michael.telgkamp@mindscreen.de>
Tested-by: default avatarAnnett Jähnichen <mcmietz@web.de>
Reviewed-by: Benjamin Franzke's avatarBenjamin Franzke <bfr@qbus.de>
Reviewed-by: Georg Ringer's avatarGeorg Ringer <georg.ringer@gmail.com>
Reviewed-by: Michael Telgkamp's avatarMichael Telgkamp <michael.telgkamp@mindscreen.de>
Reviewed-by: default avatarAnnett Jähnichen <mcmietz@web.de>
parent da0e0152
//
// Variables
//
$page-grid-spacing: 10px;
$page-grid-cell-bg: #fafafa;
$page-grid-cell-border: #cdcdcd;
$page-grid-cell-restricted: $state-danger-bg;
$page-column-header-color: #999;
$page-column-header-bg: #fff;
$page-column-header-border: $page-grid-cell-border;
$page-ce-border: #ccc;
$page-ce-hover-border: #aaa;
$page-ce-border-radius: 2px;
$page-ce-header-color: #fff;
$page-ce-header-bg: #eaeaea;
$page-ce-header-hover-bg: #d0d0d0;
$page-ce-body-bg: #fff;
$page-ce-footer-bg: #fafafa;
$page-ce-hidden-opacity: 0.4;
$page-ce-header-bg-danger: $danger;
$page-ce-header-hover-bg-danger: darken($danger, 10%);
$page-ce-header-border-danger: $page-ce-header-bg-danger;
$page-ce-header-hover-border-danger: $page-ce-header-hover-bg-danger;
$page-ce-header-bg-warning: $warning;
$page-ce-header-hover-bg-warning: darken($warning, 10%);
$page-ce-header-border-warning: $page-ce-header-bg-warning;
$page-ce-header-hover-border-warning: $page-ce-header-hover-bg-warning;
$page-ce-dropzone-bg: $state-warning-bg;
$page-ce-dropzone-border: $state-warning-border;
$page-ce-dropzone-border-radius: $page-ce-border-radius;
$page-ce-dropzone-possible-bg: $state-success-bg;
$page-ce-dropzone-possible-border: $state-success-border;
:root {
--pagemodule-grid-spacing: 1rem;
--pagemodule-grid-inner-spacing: 1rem;
--pagemodule-grid-cell-header-size: 1em;
--pagemodule-grid-cell-border-radius: 4px;
--pagemodule-grid-cell-bg: #f2f2f2;
--pagemodule-grid-cell-restricted-bg: #{$state-warning-bg};
--pagemodule-grid-column-unused-bg: #{$state-warning-bg};
--pagemodule-element-spacing: 1rem;
--pagemodule-element-bg: #fff;
--pagemodule-element-border-radius: 2px;
--pagemodule-element-box-shadow: 0 1px 2px 1px rgba(0, 0, 0, 0.15);
--pagemodule-element-hidden-bg: #f2f2f2;
--pagemodule-dropzone-bg: #{$state-warning-bg};
--pagemodule-dropzone-possible-bg: #{$state-success-bg};
}
//
// Grid
//
.t3-grid-table {
border-collapse: separate;
border-spacing: $page-grid-spacing 0;
border-spacing: var(--pagemodule-grid-spacing);
min-width: 100%;
table-layout: fixed;
}
.t3-grid-container {
margin: 0 ($page-grid-spacing * -1) $line-height-computed;
margin: 0 calc(var(--pagemodule-grid-spacing) * -1);
}
.t3-grid-cell {
background-color: $page-grid-cell-bg;
border-bottom: 1px solid $page-grid-cell-border;
border-radius: var(--pagemodule-grid-cell-border-radius);
background-color: var(--pagemodule-grid-cell-bg);
padding: calc(var(--pagemodule-grid-inner-spacing) - var(--pagemodule-grid-spacing)) 0;
}
.t3-grid-cell-restricted {
background-color: $page-grid-cell-restricted;
background-color: var(--pagemodule-grid-cell-restricted-bg);
}
.t3-page-column-unused {
background-color: $state-warning-bg;
background-color: var(--pagemodule-grid-column-unused-bg);
}
.t3-grid-cell-hidden {
......@@ -63,7 +64,7 @@ $page-ce-dropzone-possible-border: $state-success-border;
}
.t3-grid-cell-unassigned {
background: url('../Images/Backgrounds/layout-not-assigned.png') repeat;
background-image: repeating-linear-gradient(-45deg, rgba(0, 0, 0, 0.05), rgba(0, 0, 0, 0.05) 5px, transparent 5px, transparent 10px);
}
//
......@@ -76,55 +77,38 @@ $page-ce-dropzone-possible-border: $state-success-border;
.t3-page-column {
min-width: 150px;
max-width: 300px;
> h2 {
margin-top: 0;
}
}
.t3-page-column-lang-name {
padding-bottom: 1em;
h2 {
margin-bottom: 0;
margin: 0;
}
}
.t3-page-column-header-icons {
@include transition(opacity 0.15s ease-in);
opacity: 0.3;
position: absolute;
right: $page-grid-spacing;
bottom: $page-grid-spacing / 2;
}
.t3-page-column-header {
padding: ($page-grid-spacing * 2) $page-grid-spacing ($page-grid-spacing / 2) $page-grid-spacing;
font-weight: $font-weight-bold;
margin: var(--pagemodule-element-spacing) var(--pagemodule-grid-inner-spacing);
// duplicate offset for edit icon, inverted in .t3-page-column-header-icons
margin-right: calc(var(--pagemodule-grid-inner-spacing) * 2);
position: relative;
background: $page-column-header-bg;
border-bottom: 1px solid $page-column-header-border;
color: $page-column-header-color;
text-align: left;
[data-colpos='unused'] & {
padding-top: $grid-gutter-width*2;
}
font-size: var(--pagemodule-grid-cell-header-size);
a {
position: relative;
}
&:hover {
.t3-page-column-header-icons {
opacity: 1;
}
}
}
.t3-page-columns-mode {
img.c-divider {
margin: $page-grid-spacing 0 ($page-grid-spacing / 2) 0;
.t3-page-column-header-icons {
@include transition(opacity 0.15s ease-in);
opacity: 0.3;
position: absolute;
right: calc(var(--pagemodule-grid-inner-spacing) * -1);
bottom: 0;
&:hover {
opacity: 1;
}
}
......@@ -132,201 +116,191 @@ $page-ce-dropzone-possible-border: $state-success-border;
// Language
//
.t3-page-lang-copyce {
margin: $page-grid-spacing;
margin: var(--pagemodule-grid-spacing);
}
//
// Content elements
//
.t3-page-ce-wrapper {
min-height: 2em;
}
.t3-page-ce {
@include transition(opacity 0.15s ease-in);
margin: $page-grid-spacing;
margin: var(--pagemodule-grid-spacing) var(--pagemodule-grid-inner-spacing);
&.active-drag {
z-index: 4500;
}
// reset border-spacing from page column grid
* {
border-spacing: 0;
}
> .t3-page-ce {
margin-left: 0;
margin-right: 0;
}
}
.t3-page-ce-body-inner {
padding: $page-grid-spacing;
word-wrap: break-word;
.t3-page-ce-element {
border: 1px solid var(--pagemodule-element-bg);
background-color: var(--pagemodule-element-bg);
border-radius: var(--pagemodule-element-border-radius);
box-shadow: var(--pagemodule-element-box-shadow);
margin-bottom: var(--pagemodule-grid-spacing);
.t3-page-ce-hidden & {
opacity: 0.5;
border: 1px dashed rgba(0, 0, 0, 0.5);
background-color: var(--pagemodule-element-hidden-bg);
box-shadow: none;
transition: opacity 0.3s ease-in-out;
img {
max-width: 100%;
height: auto;
&:hover {
opacity: 1;
}
}
}
.t3-page-ce-header {
display: flex;
@include transition(background 0.15s ease-in);
.t3-page-ce-header {
display: flex;
align-items: center;
gap: 0.5rem;
padding: var(--pagemodule-element-spacing);
padding: ($page-grid-spacing / 2);
border: 1px solid $page-ce-border;
border-bottom: 0;
border-radius: $page-ce-border-radius $page-ce-border-radius 0 0;
background: $page-ce-header-bg;
&-left,
&-right {
flex-shrink: 0;
}
.t3-page-ce-header-icons-left {
> a {
display: inline-block;
padding: $padding-small-vertical $padding-small-horizontal;
}
&-title {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
flex-grow: 1;
font-weight: $font-weight-bold;
}
}
.t3-page-ce-header-icons-right {
@include transition(opacity 0.15s ease-in);
.t3-page-ce-body {
padding: var(--pagemodule-grid-spacing);
word-wrap: break-word;
margin-top: calc(var(--pagemodule-grid-spacing) * -1);
opacity: 0.3;
}
.t3-page-ce-body {
margin-bottom: $page-grid-spacing;
border: 1px solid $page-ce-border;
border-top: 0;
border-radius: 0 0 $page-ce-border-radius $page-ce-border-radius;
background-color: $page-ce-body-bg;
&:empty {
display: none;
}
.t3-page-ce-footer {
@extend .text-monospace;
font-size: 11px;
padding: ($page-grid-spacing / 2) $page-grid-spacing;
background: $page-ce-footer-bg;
img {
max-width: 100%;
height: auto;
}
}
&:hover {
.t3-page-ce-header {
background: $page-ce-header-hover-bg;
}
.t3-page-ce-header,
.t3-page-ce-body {
border-color: $page-ce-hover-border;
}
.t3-page-ce-footer {
font-family: $font-family-monospace;
font-size: 0.75rem;
padding: calc(var(--pagemodule-grid-spacing) / 2) var(--pagemodule-grid-spacing);
background-color: rgba(0, 0, 0, 0.05);
border-bottom-left-radius: var(--pagemodule-element-border-radius);
border-bottom-right-radius: var(--pagemodule-element-border-radius);
}
.t3-page-ce-body {
@include box-shadow(0 1px 0 rgba(0, 0, 0, 0.15));
}
.t3-page-ce-actions {
text-align: center;
}
.t3-page-ce-header-icons-right {
opacity: 1;
//
// Dropzone
//
.t3-page-ce-dropzone {
position: absolute;
left: 0;
right: 0;
bottom: -8px;
z-index: 297;
border-radius: var(--pagemodule-element-border-radius);
background-color: var(--pagemodule-dropzone-bg);
transition: height 0.3s ease-in-out, background-color 0.3s ease-in-out, left 0.3s ease-in-out, right 0.3s ease-in-out, bottom 0.3s ease-in-out;
&.active {
border: 1px dashed rgba(0, 0, 0, 0.15);
height: 40px;
&.t3-page-ce-dropzone-possible {
background-color: var(--pagemodule-dropzone-possible-bg);
z-index: 298;
}
}
}
.t3-page-ce-danger {
&:hover {
.t3-page-ce-header {
background-color: $page-ce-header-hover-bg-danger;
border-color: $page-ce-header-hover-border-danger;
}
//
// Element Preview
//
.element-preview-header {
&:empty {
display: none;
}
.t3-page-ce-body {
border-color: $page-ce-header-hover-border-danger;
}
&-status {
font-size: $font-size-sm;
opacity: 0.5;
}
.t3-page-ce-header {
background-color: $page-ce-header-bg-danger;
border-color: $page-ce-header-border-danger;
&-date {
font-size: $font-size-sm;
}
&:hover {
background-color: $page-ce-header-hover-bg-danger;
border-color: $page-ce-header-hover-border-danger;
}
&-header {
font-weight: $font-weight-bold;
}
.t3-page-ce-body {
border-color: $page-ce-header-border-danger;
&-subheader {
font-weight: $font-weight-bold;
}
}
.t3-page-ce-warning {
&:hover {
.t3-page-ce-header {
background-color: $page-ce-header-hover-bg-warning;
border-color: $page-ce-header-hover-border-warning;
}
.element-preview-header + .element-preview-content {
margin-top: 0.5rem;
}
.t3-page-ce-body {
border-color: $page-ce-header-hover-border-warning;
}
.element-preview-content {
&:empty {
display: none;
}
.t3-page-ce-header {
background-color: $page-ce-header-bg-warning;
border-color: $page-ce-header-border-warning;
&:hover {
background-color: $page-ce-header-hover-bg-warning;
border-color: $page-ce-header-hover-border-warning;
}
> *:first-child {
margin-top: 0;
}
.t3-page-ce-body {
border-color: $page-ce-header-border-warning;
> *:last-child {
margin-bottom: 0;
}
}
.t3-page-ce-hidden {
opacity: $page-ce-hidden-opacity;
@include transition(opacity 0.15s ease-in);
&:hover {
opacity: 1;
.preview-thumbnails {
margin-top: 0.5rem;
}
}
//
// Dropzone
// Thumbnails
//
.t3-page-ce-dropzone-available.active {
border: 1px dashed $page-ce-dropzone-possible-border;
border-radius: $page-ce-dropzone-border-radius;
background-color: $page-ce-dropzone-bg;
height: 27px;
position: absolute !important;
bottom: 0;
z-index: 300;
width: 100%;
:root {
--preview-thumbnails-size: 64px;
}
.t3-page-ce-dropzone-available.active.t3-page-ce-dropzone-possible {
border: 1px dashed $page-ce-dropzone-border;
border-radius: $page-ce-dropzone-border-radius;
background-color: $page-ce-dropzone-possible-bg;
margin: -38px 0 -37px;
padding: 50px 10px;
z-index: 500;
position: relative;
opacity: 0.65;
}
.preview-thumbnails {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
.t3-page-ce-dragitem.dragitem-shadow {
opacity: 0.65;
box-shadow: 0 1px 24px rgba(0, 0, 0, 0.5);
}
&-element {
display: block;
.t3-page-ce.ui-draggable-handle {
height: auto !important;
&-image {
height: var(--preview-thumbnails-size);
width: var(--preview-thumbnails-size);
display: flex;
background-color: rgba(0, 0, 0, 0.05);
overflow: hidden;
border-radius: 2px;
justify-content: center;
align-items: center;
.icon {
height: calc(var(--preview-thumbnails-size) / 2);
width: calc(var(--preview-thumbnails-size) / 2);
}
}
}
}
//
......@@ -336,28 +310,27 @@ $page-ce-dropzone-possible-border: $state-success-border;
.t3-page-ce-header-draggable:hover {
cursor: move;
}
&.dragitem-shadow {
opacity: 0.5;
box-shadow: 0 1px 24px rgba(0, 0, 0, 0.5);
}
}
.ui-draggable-dragging {
z-index: 600 !important;
z-index: 299 !important;
.t3-page-ce-wrapper-new-ce {
display: none;
.t3-page-ce-actions {
visibility: hidden;
}
}
.t3-is-dragged {
.t3-page-ce-body {
max-height: 225px;
overflow: hidden;
.ui-draggable-copy-message {
font-size: $font-size-sm;
position: absolute;
top: -1.25rem;
}
}
.t3-page-ce-dropzone-available.active,
.t3-page-ce-dropzone-possible {
max-height: 225px;
}
//
// New Content Element Wizard
//
......
......@@ -38,7 +38,7 @@ interface DroppableEventUIParam {
class DragDrop {
private static readonly contentIdentifier: string = '.t3js-page-ce';
private static readonly dragIdentifier: string = '.t3-page-ce-dragitem';
private static readonly dragIdentifier: string = '.t3js-page-ce-dragitem';
private static readonly dragHeaderIdentifier: string = '.t3js-page-ce-draghandle';
private static readonly dropZoneIdentifier: string = '.t3js-page-ce-dropzone-available';
private static readonly columnIdentifier: string = '.t3js-page-column';
......
......@@ -97,7 +97,7 @@ class Paste {
* activates the paste into / paste after icons outside of the context menus
*/
private activatePasteIcons(): void {
$('.t3-page-ce-wrapper-new-ce').each((index: number, el: HTMLElement): void => {
$('.t3js-page-new-ce').each((index: number, el: HTMLElement): void => {
if (this.pasteAfterLinkTemplate && this.pasteIntoLinkTemplate) {
const parent = $(el).parent();
// append the buttons
......
......@@ -75,21 +75,18 @@ class StandardContentPreviewRenderer implements PreviewRendererInterface, Logger
$itemLabels = $item->getContext()->getItemLabels();
$outHeader = '';
if ($record['header']) {
$infoArr = [];
$this->getProcessedValue($item, 'header_position,header_layout,header_link', $infoArr);
$hiddenHeaderNote = '';
// If header layout is set to 'hidden', display an accordant note:
if ($record['header_layout'] == 100) {
$hiddenHeaderNote = ' <em>[' . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:header_layout.I.6')) . ']</em>';
}
$outHeader = $record['date']
? htmlspecialchars($itemLabels['date'] . ' ' . BackendUtility::date($record['date'])) . '<br />'
: '';
$outHeader .= '<strong>' . $this->linkEditContent($this->renderText($record['header']), $record)
. $hiddenHeaderNote . '</strong><br />';
}
$outHeader .= $record['header_layout'] == 100
? '<div class="element-preview-header-status">' . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:header_layout.I.6')) . '</div>'
: '';
$outHeader .= $record['date']
? '<div class="element-preview-header-date">' . htmlspecialchars($itemLabels['date'] . ' ' . BackendUtility::date($record['date'])) . ' </div>'
: '';
$outHeader .= $record['header']
? '<div class="element-preview-header-header">' . $this->linkEditContent($this->renderText($record['header']), $record) . '</div>'