Просмотр исходного кода

[feat] Move actions bar to footer and ad bulk actions to search page

Added following
- Add 'More' dropdown if there are more than 4 bulk actions
- Add bulk actions to 'Pages search' page

Changed following
- Move actions bar from headers to footer
Shohan 3 лет назад
Родитель
Сommit
020f8dcfaf

+ 54 - 0
client/scss/components/_listing.scss

@@ -493,6 +493,60 @@ ul.listing {
     }
 }
 
+footer.bulk-actions-choices {
+    @include transition(bottom 0.5s ease 0.5s);
+
+    &.hidden {
+        bottom: -200px;
+    }
+
+    .bulk-actions-more {
+        &.is-open {
+            ul.u-toggle {
+                display: flex;
+            }
+        }
+
+        ul {
+            bottom: 50px;
+            left: 30px;
+            flex-direction: column;
+        }
+    }
+}
+
+.footer__container {
+    display: flex;
+    justify-content: space-around;
+    width: 100%;
+
+    .bulk-actions-filter-checkbox {
+        display: flex;
+        align-items: center;
+        padding-right: 0.2em;
+        border-right: 1px solid darken($color-white, 50);
+    }
+
+    > span {
+        text-transform: none;
+        margin: 0 5px;
+    }
+
+    .num-pages-in-listing {
+        color: $color-teal-light;
+    }
+
+    a.button-secondary {
+        font-weight: 700;
+        color: $color-teal;
+    }
+
+    a.button-secondary,
+    a.button-secondary.serious {
+        background-color: $color-white;
+    }
+}
+
 .image-choice {
     // Force the link to be displayed as a block, so its focus outline has the right shape.
     display: block;

+ 9 - 0
client/scss/overrides/_utilities.dropdowns.scss

@@ -13,6 +13,11 @@
     left: 1rem;
 }
 
+.dropup .u-arrow--tl:before {
+    top: 100%;
+    transform: rotateZ(180deg);
+}
+
 // =============================================================================
 //  Default dropdown theme
 // =============================================================================
@@ -57,6 +62,10 @@
 // =============================================================================
 // Dark theme
 // =============================================================================
+.t-dark {
+    position: relative;
+}
+
 .t-dark .u-link {
     color: #fff;
 }

+ 24 - 16
client/src/entrypoints/admin/bulk-actions.js

@@ -6,12 +6,12 @@ const BULK_ACTION_FILTERS_CLASS = `${BULK_ACTION_SELECT_ALL_CHECKBOX_TH} .c-drop
 const BULK_ACTION_CHOICES_DIV = 'bulk-actions-choices';
 const BULK_ACTION_NUM_PAGES_SPAN = 'num-pages';
 const BULK_ACTION_NUM_PAGES_IN_LISTING = 'num-pages-in-listing';
-const TABLE_HEADERS_TR = 'table-headers';
 
 const checkedState = {
   checkedPages: new Set(),
   numPages: 0,
-  selectAllInListing: false
+  selectAllInListing: false,
+  shouldShowAllInListingText: true
 };
 
 /* Event listener for the `Select All` checkbox */
@@ -32,30 +32,31 @@ function SelectBulkActionsCheckboxes(e) {
   if (e.target.checked) checkedState.checkedPages.add(+e.target.dataset.pageId);
   else {
     /* unchecks `Select all` checkbox as soon as one page is unchecked */
-    document.querySelector(`.${BULK_ACTION_SELECT_ALL_CHECKBOX_TH} input`).checked = false;
+    document.querySelectorAll(`.${BULK_ACTION_SELECT_ALL_CHECKBOX_TH} input`).forEach(_el => {
+      const el = _el;
+      el.checked = false;
+    });
     checkedState.checkedPages.delete(+e.target.dataset.pageId);
   }
 
   if (checkedState.checkedPages.size === 0) {
     /* when all checkboxes are unchecked */
-    document.querySelectorAll(`.${TABLE_HEADERS_TR} > th`).forEach(el => el.classList.remove('u-hidden'));
-    document.querySelector(`.${BULK_ACTION_CHOICES_DIV}`).classList.add('u-hidden');
+    document.querySelector(`.${BULK_ACTION_CHOICES_DIV}`).classList.add('hidden');
     document.querySelectorAll(`.${BULK_ACTION_PAGE_CHECKBOX_INPUT}`).forEach(el => el.classList.remove('show'));
-    document.querySelector(`.${BULK_ACTION_SELECT_ALL_CHECKBOX_TH}`).setAttribute('colspan', '1');
   } else if (checkedState.checkedPages.size === 1 && prevLength === 0) {
     /* when 1 checkbox is checked for the first time */
     document.querySelectorAll(`.${BULK_ACTION_PAGE_CHECKBOX_INPUT}`).forEach(el => {
       el.classList.add('show');
     });
-    document.querySelectorAll(`.${TABLE_HEADERS_TR} > th`).forEach(el => el.classList.add('u-hidden'));
-    document.querySelector(`.${BULK_ACTION_SELECT_ALL_CHECKBOX_TH}`).classList.remove('u-hidden');
-    document.querySelector(`.${BULK_ACTION_CHOICES_DIV}`).classList.remove('u-hidden');
-    document.querySelector(`.${BULK_ACTION_SELECT_ALL_CHECKBOX_TH}`).setAttribute('colspan', '6');
+    document.querySelector(`.${BULK_ACTION_CHOICES_DIV}`).classList.remove('hidden');
   }
 
   if (checkedState.checkedPages.size === checkedState.numPages) {
     /* when all checkboxes in the page are checked */
-    document.querySelector(`.${BULK_ACTION_SELECT_ALL_CHECKBOX_TH} input`).checked = true;
+    document.querySelectorAll(`.${BULK_ACTION_SELECT_ALL_CHECKBOX_TH} input`).forEach(_el => {
+      const el = _el;
+      el.checked = true;
+    });
   }
 
   if (checkedState.checkedPages.size > 0) {
@@ -79,7 +80,7 @@ function SelectBulkActionsCheckboxes(e) {
 function selectAllPageIdsInListing() {
   checkedState.selectAllInListing = true;
   document.querySelector(`.${BULK_ACTION_NUM_PAGES_SPAN}`).
-    textContent = wagtailConfig.STRINGS.NUM_PAGES_SELECTED_ALL_IN_LISTING;
+    textContent = wagtailConfig.STRINGS.NUM_PAGES_SELECTED_ALL_IN_LISTING + '.';
   document.querySelector(`.${BULK_ACTION_NUM_PAGES_IN_LISTING}`).classList.add('u-hidden');
 }
 
@@ -110,7 +111,10 @@ function FilterEventListener(e) {
     }
   } else {
     /* If filter string is empty, select all checkboxes */
-    document.querySelector(`.${BULK_ACTION_SELECT_ALL_CHECKBOX_TH}`).checked = true;
+    document.querySelectorAll(`.${BULK_ACTION_SELECT_ALL_CHECKBOX_TH}`).forEach(_el => {
+      const el = _el;
+      el.checked = true;
+    });
     document.querySelector(`.${BULK_ACTION_SELECT_ALL_CHECKBOX_TH}`).dispatchEvent(changeEvent);
   }
 }
@@ -139,14 +143,18 @@ function addBulkActionListeners() {
       checkedState.numPages++;
       el.addEventListener('change', SelectBulkActionsCheckboxes);
     });
-  document.querySelector(`.${BULK_ACTION_SELECT_ALL_CHECKBOX_TH}`).addEventListener('change', SelectBulkActionsFilter);
+  document.querySelectorAll(`.${BULK_ACTION_SELECT_ALL_CHECKBOX_TH} input`).forEach(el => {
+    el.addEventListener('change', SelectBulkActionsFilter);
+  });
   document.querySelectorAll(`.${BULK_ACTION_FILTERS_CLASS}`).forEach(
     elem => elem.addEventListener('click', FilterEventListener)
   );
-  document.querySelectorAll(`.${BULK_ACTION_CHOICES_DIV} > ul > li > a`).forEach(
+  document.querySelectorAll(`.${BULK_ACTION_CHOICES_DIV} ul > li > a`).forEach(
     elem => elem.addEventListener('click', BulkActionEventListeners)
   );
-  document.querySelector(`.${BULK_ACTION_NUM_PAGES_IN_LISTING}`).addEventListener('click', selectAllPageIdsInListing);
+  const selectAllInListingText = document.querySelector(`.${BULK_ACTION_NUM_PAGES_IN_LISTING}`);
+  if (selectAllInListingText) selectAllInListingText.addEventListener('click', selectAllPageIdsInListing);
+  else checkedState.shouldShowAllInListingText = false;
 }
 
 addBulkActionListeners();

+ 10 - 0
wagtail/admin/templates/wagtailadmin/pages/index.html

@@ -20,6 +20,16 @@
             {% url 'wagtailadmin_explore' parent_page.id as pagination_base_url %}
             {% paginate pages base_url=pagination_base_url %}
         {% endif %}
+        <footer class="footer bulk-actions-choices hidden">
+            <div class="footer__container">
+                <div class="bulk-actions-filter-checkbox">
+                    <input type="checkbox" aria-label="Bulk action checkbox" />
+                </div>
+                <ul>{% page_bulk_action_choices %}</ul>
+                <span class="num-pages"></span>
+                <a class="num-pages-in-listing u-hidden" href='#'>{% trans "Select all pages in listing" %}</a>
+            </div>
+        </footer>
     </form>
 {% endblock %}
 

+ 2 - 2
wagtail/admin/templates/wagtailadmin/pages/listing/_button_with_dropdown.html

@@ -1,6 +1,6 @@
 {% load wagtailadmin_tags %}
-<div {{ self.attrs }} class="c-dropdown  {% if is_parent %}t-inverted{% else %}t-default{% endif %}" data-dropdown>
-    <a href="javascript:void(0)" aria-label="{{ title }}" class="c-dropdown__button  u-btn-current">
+<div {{ self.attrs }} class="c-dropdown  {% if is_parent %}t-inverted{% else %}t-default{% endif %} {{ classes|join:' ' }}" data-dropdown>
+    <a href="javascript:void(0)" aria-label="{{ title }}" class="c-dropdown__button u-btn-current {{btn_classes|join:' ' }}">
         {{ label }}
         <div data-dropdown-toggle class="o-icon c-dropdown__toggle c-dropdown__togle--icon [ icon icon-arrow-down ]">
             {% icon name="arrow-down" %}{% icon name="arrow-up" %}

+ 1 - 6
wagtail/admin/templates/wagtailadmin/pages/listing/_table_headers_explore.html

@@ -36,15 +36,10 @@ ordering: the current sort parameter
         <div>
             <input type="checkbox" aria-label="Bulk action checkbox" />
             {% bulk_action_filters %}
-            <div class="bulk-actions-choices u-hidden">
-                <span class="num-pages"></span>
-                <a class="num-pages-in-listing u-hidden" href='#'>{% trans "Select all pages in listing" %}</a>
-                <ul>{% page_bulk_action_choices %}</ul>
-            </div>
         </div>
     </th>
     {% endif %}
-    <th>
+    <th class="title">
         {% trans 'Page' %}
     </th>
     {% if show_parent %}

+ 9 - 0
wagtail/admin/templates/wagtailadmin/pages/search.html

@@ -21,4 +21,13 @@
     <div id="page-results">
         {% include "wagtailadmin/pages/search_results.html" %}
     </div>
+    <footer class="footer bulk-actions-choices hidden">
+        <div class="footer__container">
+            <div class="bulk-actions-filter-checkbox">
+                <input type="checkbox" aria-label="Bulk action checkbox" />
+            </div>
+            <ul>{% page_bulk_action_choices %}</ul>
+            <span class="num-pages"></span>
+        </div>
+    </footer>
 {% endblock %}

+ 28 - 3
wagtail/admin/templatetags/wagtailadmin_tags.py

@@ -31,7 +31,7 @@ from wagtail.admin.navigation import get_explorable_root_page
 from wagtail.admin.search import admin_search_areas
 from wagtail.admin.staticfiles import versioned_static as versioned_static_func
 from wagtail.admin.ui import sidebar
-from wagtail.admin.widgets import PageListingButton
+from wagtail.admin.widgets import ButtonWithDropdown, PageListingButton
 from wagtail.core import hooks
 from wagtail.core.models import (
     Collection, CollectionViewRestriction, Locale, Page, PageViewRestriction,
@@ -512,19 +512,44 @@ def page_bulk_action_choices(context):
         bulk_actions_list.append(action)
     button_hooks = hooks.get_hooks('construct_page_bulk_action_choices')
     for hook in button_hooks:
-        bulk_actions_list = hook(context.request, bulk_actions_list)
+        hook(context.request, bulk_actions_list)
 
     bulk_actions_list.sort(key=lambda x: x.action_priority)
 
+    bulk_action_more_list = []
+    if len(bulk_actions_list) > 4:
+        bulk_action_more_list = bulk_actions_list[4:]
+        bulk_actions_list = bulk_actions_list[:4]
+
     bulk_action_buttons = [
         PageListingButton(
             action.display_name,
             reverse('wagtailadmin_bulk_action', args=[action.action_type]) + '?' + urlencode({'next': action.next_url}),
             attrs={'aria-label': action.aria_label},
-            priority=action.action_priority
+            priority=action.action_priority,
+            classes=action.classes
         ) for action in bulk_actions_list
     ]
 
+    if bulk_action_more_list:
+        more_button = ButtonWithDropdown(
+            label=_("MORE"),
+            attrs={
+                'target': '_blank', 'rel': 'noopener noreferrer',
+                'title': _("View more bulk actions")
+            },
+            classes={'bulk-actions-more', 'dropup'},
+            btn_classes={'button', 'button-small'},
+            buttons_data=[{
+                'label': action.display_name,
+                'url': reverse('wagtailadmin_bulk_action', args=[action.action_type]) + '?' + urlencode({'next': action.next_url}),
+                'attrs': {'aria-label': action.aria_label},
+                'priority': action.action_priority,
+            } for action in bulk_action_more_list]
+        )
+        more_button.is_parent = True
+        bulk_action_buttons.append(more_button)
+
     return {'buttons': bulk_action_buttons}
 
 

+ 1 - 0
wagtail/admin/views/bulk_action.py

@@ -31,6 +31,7 @@ class BulkAction(ABC, TemplateView):
     action_priority = 100
     model = None
     object_key = 'object'
+    classes = set()
 
     def __init__(self, request):
         self.include_descendants = request.POST.get("include_descendants", False)

+ 1 - 0
wagtail/admin/views/pages/bulk_actions/delete.py

@@ -11,6 +11,7 @@ class DeleteBulkAction(PageBulkAction):
     aria_label = "Delete pages"
     template_name = "wagtailadmin/pages/bulk_actions/confirm_bulk_delete.html"
     action_priority = 30
+    classes = {'serious'}
 
     def check_perm(self, page):
         return page.permissions_for_user(self.request.user).can_delete()

+ 26 - 3
wagtail/admin/widgets/button.py

@@ -65,12 +65,35 @@ class BaseDropdownMenuButton(Button):
     def dropdown_buttons(self):
         raise NotImplementedError
 
-    def render(self):
-        return render_to_string(self.template_name, {
+    def get_context_data(self):
+        return {
             'buttons': self.dropdown_buttons,
             'label': self.label,
             'title': self.attrs.get('title'),
-            'is_parent': self.is_parent})
+            'is_parent': self.is_parent
+        }
+
+    def render(self):
+        return render_to_string(self.template_name, self.get_context_data())
+
+
+class ButtonWithDropdown(BaseDropdownMenuButton):
+    template_name = 'wagtailadmin/pages/listing/_button_with_dropdown.html'
+
+    def __init__(self, *args, **kwargs):
+        self.btn_classes = kwargs.pop('btn_classes', set())
+        self.buttons_data = kwargs.pop('buttons_data', [])
+        super().__init__(*args, **kwargs)
+
+    def get_context_data(self):
+        context = super().get_context_data()
+        context['btn_classes'] = self.btn_classes
+        context['classes'] = self.classes
+        return context
+
+    @cached_property
+    def dropdown_buttons(self):
+        return [Button(**button) for button in self.buttons_data]
 
 
 class ButtonWithDropdownFromHook(BaseDropdownMenuButton):