Skip to content

Commit

Permalink
add AdminActionListMixin to support action list burger menu.
Browse files Browse the repository at this point in the history
  • Loading branch information
FreemanPancake committed Nov 18, 2024
1 parent 9275657 commit 17260f6
Show file tree
Hide file tree
Showing 5 changed files with 301 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Unreleased
* Dropped Support for Python 3.8
* Introduced Django CMS 4.1 support.
* delete `VersionLock` as It's moved to Version.locked_by field.
* added `AdminActionListMixin` in utils.py file to support action list burger menu.


1.3.0 (2024-05-16)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
from django.urls import reverse
from django.utils.translation import gettext_lazy as _

from cms.utils.urlutils import static_with_version

Check failure on line 6 in djangocms_version_locking/monkeypatch/djangocms_alias/admin.py

View workflow job for this annotation

GitHub Actions / flake8

'cms.utils.urlutils.static_with_version' imported but unused

from djangocms_alias.admin import AliasAdmin as OriginalAliasAdmin
from djangocms_alias.models import Alias
from djangocms_versioning.constants import DRAFT, PUBLISHED
Expand All @@ -11,9 +13,10 @@
proxy_model,
version_list_url,
)
from djangocms_version_locking.utils import AdminActionListMixin


class AliasAdmin(OriginalAliasAdmin):
class AliasAdmin(AdminActionListMixin, OriginalAliasAdmin):

change_list_template = "monkeypatch/cms/admin/cms/grouper/change_list.html"

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*-------------------------------------
General versioining action list styles
---------------------------------------*/

/* ensure certain columns aren't too wide */
.field-get_title {
min-width: 250px;
word-break: break-word;
white-space: normal !important;
}

.field-url {
min-width: 100px;
word-break: break-all;
white-space: normal !important;
}
.cms-icon-menu {
cursor: pointer;
}

/*-------------------------------------
All action buttons
---------------------------------------*/
.btn.cms-action-btn {
position: relative;
display: -webkit-inline-box;
display: -ms-inline-flexbox;
display: inline-flex;
padding: 0 4px 0 7px !important;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
height: 34px;
margin-top: -12px !important;
position: relative;
height: 34px;
bottom: -6px;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}

/* disable clicking for inactive buttons */
.btn.cms-action-btn.inactive {
pointer-events: none;
background-color: #e1e1e1 !important;
}

.btn.cms-action-btn.inactive span {
opacity: 0.5;
}

/*-------------------------------------
This governs the drop-down behaviour
extending the pagetree classes provided by CMS
---------------------------------------*/

/* add shadow on burger menu trigger */
a.btn.cms-action-btn:hover, a.btn.cms-action-btn.open {
box-shadow: inset 0 3px 5px rgba(0,0,0,.125);
}

/* style for each option row */
ul.cms-pagetree-dropdown-menu-inner li {
border: 1px solid transparent;
border-radius: 5px;
padding: 2px 6px;
}
ul.cms-pagetree-dropdown-menu-inner li:hover {
list-style-type: none;
border: 1px solid #ccc;
border-radius: 5px;
background-color: #0bf;
}

/* align the option text with it's icon */
ul.cms-pagetree-dropdown-menu-inner li a span {
line-height: 1rem;
}

/* disable any inactive option */
ul.cms-pagetree-dropdown-menu-inner li a.inactive {
cursor: not-allowed;
pointer-events: none;
opacity: 0.3;
filter: alpha(opacity=30);
}

/* set the size of the drop-down */
.cms-pagetree-dropdown-menu.open {
display: block;
width: 200px;
}

/* hide when closed */
.cms-pagetree-dropdown-menu.closed, .cms-icon-menu.closed .cms-pagetree-dropdown-menu {
display: none;
}

.cms-pagetree-dropdown-menu:before {
margin-top: 13px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
"use strict";

(function ($) {
if (!$) {
return;
}

$(function () {
var createBurgerMenu = function createBurgerMenu(row) {
/* create burger menu anchor section */
var anchor = document.createElement('A');
var cssclass = document.createAttribute('class');
cssclass.value = 'btn cms-action-btn closed';
anchor.setAttributeNode(cssclass);
// create burger menu title
var title = document.createAttribute('title');
title.value = 'Actions';
anchor.setAttributeNode(title);
// create burger menu icon
var menu_icon = document.createElement('span');
menu_icon.className = "cms-icon cms-icon-menu";
anchor.appendChild(menu_icon);

/* create options container */
var optionsContainer = document.createElement('DIV');
cssclass = document.createAttribute('class');
cssclass.value = 'cms-pagetree-dropdown-menu ' + // main selector for the menu
'cms-pagetree-dropdown-menu-arrow-right-top'; // keeps the menu arrow in position

optionsContainer.setAttributeNode(cssclass);
var ul = document.createElement('UL');
cssclass = document.createAttribute('class');
cssclass.value = 'cms-pagetree-dropdown-menu-inner';
ul.setAttributeNode(cssclass);
/* get the existing actions and move them into the options container */

var li;
var text;
var actions = $(row).children('.field-list_actions');

if (!actions.length) {
/* skip any rows without actions to avoid errors */
return;
}

var actions_btn = $(actions[0]).children('.cms-action-btn');
if (actions_btn.length <=3) {
return;
} else {
actions_btn.each(function (index, item) {
/* exclude preview/view and edit buttons */
if (item.classList.contains('cms-action-preview') ||
item.classList.contains('cms-action-view') ||
item.classList.contains('cms-action-edit')) {
return;
}

li = document.createElement('LI');
/* create an anchor from the item */

var li_anchor = document.createElement('A');
cssclass = document.createAttribute('class');
cssclass.value = 'cms-action-burger-options-anchor';

if ($(item).hasClass('cms-form-get-method')) {
/* ensure the fake-form selector is propagated to the new anchor */
cssclass.value += ' cms-form-get-method';
}

li_anchor.setAttributeNode(cssclass);
var href = document.createAttribute('href');
href.value = $(item).attr('href');
li_anchor.setAttributeNode(href);
/* move the an image element */

var existing_icon_span = $(item).children('span');
li_anchor.appendChild(existing_icon_span[0]);
/* create the button text */

text = document.createTextNode(item.title);
var span = document.createElement('SPAN');
span.appendChild(text); // construct the button

li.appendChild(li_anchor);
li_anchor.appendChild(span);
ul.appendChild(li);
/* destroy original replaced buttons */

actions[0].removeChild(item);
});
}

/* add the options to the drop-down */
optionsContainer.appendChild(ul);
actions[0].appendChild(anchor);
document.body.appendChild(optionsContainer);
/* listen for burger menu clicks */

anchor.addEventListener('click', function (ev) {
ev.stopPropagation();
toggleBurgerMenu(anchor, optionsContainer);
});
/* close burger menu if clicking outside */

$(window).click(function () {
closeBurgerMenu();
});
};

var toggleBurgerMenu = function toggleBurgerMenu(burgerMenuAnchor, optionsContainer) {
var bm = $(burgerMenuAnchor);
var op = $(optionsContainer);
var closed = bm.hasClass('closed');
closeBurgerMenu();

if (closed) {
bm.removeClass('closed');
bm.addClass('open');
op.removeClass('closed');
op.addClass('open');
} else {
bm.addClass('closed');
bm.removeClass('open');
op.addClass('closed');
op.removeClass('open');
}

var pos = bm.offset();
op.css('left', pos.left - 200);
op.css('top', pos.top);
};

var closeBurgerMenu = function closeBurgerMenu() {
$('.cms-pagetree-dropdown-menu').removeClass('open');
$('.cms-pagetree-dropdown-menu').addClass('closed');
$('.cms-action-btn').removeClass('open');
$('.cms-action-btn').addClass('closed');
};

$('#result_list').find('tr').each(function (index, item) {
createBurgerMenu(item);
});
/* it is not possible to put a form inside a form, so
actions have to create their own form on click */

var fakeForm = function fakeForm(e) {
var action = $(e.currentTarget);
var formMethod = action.attr('class').indexOf('cms-form-get-method') !== -1 ? 'GET' : 'POST';
if (formMethod == 'GET') return
e.preventDefault();

var csrfToken = '<input type="hidden" name="csrfmiddlewaretoken" value="' + document.cookie.match(/csrftoken=([^;]*);?/)[1] + '">';
var fakeForm = $('<form style="display: none" action="' + action.attr('href') + '" method="' + formMethod + '">' + csrfToken + '</form>');
var keepSideFrame = action.attr('class').indexOf('js-keep-sideframe') !== -1; // always break out of the sideframe, cause it was never meant to open cms views inside it

try {
if (!keepSideFrame) {
window.top.CMS.API.Sideframe.close();
}
} catch (err) {}

if (keepSideFrame) {
var body = window.document.body;
} else {
var body = window.top.document.body;
}

fakeForm.appendTo(body).submit();
};

$('.js-action, .cms-js-publish-btn, .cms-js-edit-btn, .cms-action-burger-options-anchor').on('click', fakeForm);
$('.js-close-sideframe').on('click', function () {
try {
window.top.CMS.API.Sideframe.close();
} catch (e) {}
});
});
})(typeof django !== 'undefined' && django.jQuery || typeof CMS !== 'undefined' && CMS.$ || false);
14 changes: 14 additions & 0 deletions djangocms_version_locking/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from django import forms


class AdminActionListMixin(metaclass=forms.MediaDefiningClass):
"""AdminActionListMixin is a mixin for the Versioned Model class. It adds the ability to have
action buttons and a burger menu in the admin's change list view.
"""

class Media:
js = (
"admin/js/jquery.init.js",
"djangocms_version_locking/js/actions.js",
)
css = {"all": ("djangocms_version_locking/css/actions.css",)}

0 comments on commit 17260f6

Please sign in to comment.