From a0544e38cee68e9418dca63f181224c2a9bdb1ab Mon Sep 17 00:00:00 2001 From: John Cowen Date: Thu, 24 May 2018 17:15:55 +0100 Subject: [PATCH] Prevent action groups from being cutoff by the footer 1. Calculate where group is going to be, if it will get cut off, then dropup instead of down 2. As the action group can now drop up, the z-index should be higher than the previous rows, so add a top z-index higher than the others and use that when opened --- ui-v2/app/components/tabular-collection.js | 144 +++++++++++++++++- ui-v2/app/styles/components/action-group.scss | 15 +- 2 files changed, 153 insertions(+), 6 deletions(-) diff --git a/ui-v2/app/components/tabular-collection.js b/ui-v2/app/components/tabular-collection.js index 11830f0866..c6716eac5b 100644 --- a/ui-v2/app/components/tabular-collection.js +++ b/ui-v2/app/components/tabular-collection.js @@ -1,21 +1,40 @@ import Component from 'ember-collection/components/ember-collection'; import needsRevalidate from 'ember-collection/utils/needs-revalidate'; +import identity from 'ember-collection/utils/identity'; import Grid from 'ember-collection/layouts/grid'; import SlotsMixin from 'ember-block-slots'; import style from 'ember-computed-style'; import { computed, get, set } from '@ember/object'; -const $$ = document.querySelectorAll.bind(document); +const $$ = function(sel, context = document) { + return context.querySelectorAll(sel); +}; const createSizeEvent = function(detail) { return { detail: { width: window.innerWidth, height: window.innerHeight }, }; }; +// need to copy this in wholesale as there is no way to import it +// TODO: separate both Cell and ZIndexedGrid out +class Cell { + constructor(key, item, index, style) { + this.key = key; + this.hidden = false; + this.item = item; + this.index = index; + this.style = style; + } +} +const maxZIndex = 10000; class ZIndexedGrid extends Grid { - formatItemStyle(index, w, h) { - let style = super.formatItemStyle(...arguments); - style += 'z-index: ' + (10000 - index); + formatItemStyle(index, w, h, checked) { + let style = super.formatItemStyle(index, w, h); + let zIndex = maxZIndex - index; + if (checked == index) { + zIndex = maxZIndex + 1; + } + style += 'z-index: ' + zIndex; return style; } } @@ -37,6 +56,21 @@ const change = function(e) { const value = e.currentTarget.value; if (value != get(this, 'checked')) { set(this, 'checked', value); + if (e.currentTarget.getAttribute('id') !== 'actions_close') { + const $tr = closest('tr', e.currentTarget); + const $group = [...$('~ ul', e.currentTarget)][0]; + const $footer = [...$$('footer[role="contentinfo"]')][0]; + const groupRect = $group.getBoundingClientRect(); + const footerRect = $footer.getBoundingClientRect(); + const groupBottom = groupRect.top + $group.clientHeight; + const footerTop = footerRect.top; + if (groupBottom > footerTop) { + $group.classList.add('above'); + } else { + $group.classList.remove('above'); + } + $tr.style.zIndex = maxZIndex + 1; + } } else { set(this, 'checked', null); } @@ -109,6 +143,108 @@ export default Component.extend(SlotsMixin, { needsRevalidate(this); } }, + // need to overwrite this completely so I can pass through the checked index + // for `formatItemStyle` in 3 places + updateCells: function() { + if (!this._items) { + return; + } + const numItems = get(this._items, 'length'); + if (this._cellLayout.length !== numItems) { + this._cellLayout.length = numItems; + } + + var priorMap = this._cellMap; + var cellMap = Object.create(null); + + var index = this._cellLayout.indexAt( + this._scrollLeft, + this._scrollTop, + this._clientWidth, + this._clientHeight + ); + var count = this._cellLayout.count( + this._scrollLeft, + this._scrollTop, + this._clientWidth, + this._clientHeight + ); + var items = this._items; + var bufferBefore = Math.min(index, this._buffer); + index -= bufferBefore; + count += bufferBefore; + count = Math.min(count + this._buffer, get(items, 'length') - index); + var i, style, itemIndex, itemKey, cell; + + var newItems = []; + + for (i = 0; i < count; i++) { + itemIndex = index + i; + itemKey = identity(items.objectAt(itemIndex)); + if (priorMap) { + cell = priorMap[itemKey]; + } + if (cell) { + // additional `checked` argument + style = this._cellLayout.formatItemStyle( + itemIndex, + this._clientWidth, + this._clientHeight, + this.checked + ); + set(cell, 'style', style); + set(cell, 'hidden', false); + set(cell, 'key', itemKey); + cellMap[itemKey] = cell; + } else { + newItems.push(itemIndex); + } + } + + for (i = 0; i < this._cells.length; i++) { + cell = this._cells[i]; + if (!cellMap[cell.key]) { + if (newItems.length) { + itemIndex = newItems.pop(); + let item = items.objectAt(itemIndex); + itemKey = identity(item); + // additional `checked` argument + style = this._cellLayout.formatItemStyle( + itemIndex, + this._clientWidth, + this._clientHeight, + this.checked + ); + set(cell, 'style', style); + set(cell, 'key', itemKey); + set(cell, 'index', itemIndex); + set(cell, 'item', item); + set(cell, 'hidden', false); + cellMap[itemKey] = cell; + } else { + set(cell, 'hidden', true); + set(cell, 'style', 'height: 0; display: none;'); + } + } + } + + for (i = 0; i < newItems.length; i++) { + itemIndex = newItems[i]; + let item = items.objectAt(itemIndex); + itemKey = identity(item); + // additional `checked` argument + style = this._cellLayout.formatItemStyle( + itemIndex, + this._clientWidth, + this._clientHeight, + this.checked + ); + cell = new Cell(itemKey, item, itemIndex, style); + cellMap[itemKey] = cell; + this._cells.pushObject(cell); + } + this._cellMap = cellMap; + }, actions: { click: function(e) { const name = e.target.nodeName.toLowerCase(); diff --git a/ui-v2/app/styles/components/action-group.scss b/ui-v2/app/styles/components/action-group.scss index 51685c970a..b4f324c114 100644 --- a/ui-v2/app/styles/components/action-group.scss +++ b/ui-v2/app/styles/components/action-group.scss @@ -70,19 +70,30 @@ %action-group ul { position: absolute; right: -10px; - top: 35px; padding: 1px; } %action-group ul::before { position: absolute; right: 18px; - top: -6px; content: ''; display: block; width: 10px; height: 10px; +} +%action-group ul:not(.above) { + top: 35px; +} +%action-group ul:not(.above)::before { + top: -6px; transform: rotate(45deg); } +%action-group ul.above { + bottom: 35px; +} +%action-group ul.above::before { + bottom: -6px; + transform: rotate(225deg); +} %action-group li { position: relative; z-index: 1;