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
This commit is contained in:
John Cowen 2018-05-24 17:15:55 +01:00
parent 9c43498354
commit c1742a89dd
2 changed files with 153 additions and 6 deletions

View File

@ -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();

View File

@ -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;