You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Connected.Components/TScripts/mudPopover.js

494 lines
20 KiB

2 years ago
// Copyright (c) MudBlazor 2021
// MudBlazor licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
window.mudpopoverHelper = {
calculatePopoverPosition: function (list, boundingRect, selfRect) {
let top = 0;
let left = 0;
if (list.indexOf('mud-popover-anchor-top-left') >= 0) {
left = boundingRect.left;
top = boundingRect.top;
} else if (list.indexOf('mud-popover-anchor-top-center') >= 0) {
left = boundingRect.left + boundingRect.width / 2;
top = boundingRect.top;
} else if (list.indexOf('mud-popover-anchor-top-right') >= 0) {
left = boundingRect.left + boundingRect.width;
top = boundingRect.top;
} else if (list.indexOf('mud-popover-anchor-center-left') >= 0) {
left = boundingRect.left;
top = boundingRect.top + boundingRect.height / 2;
} else if (list.indexOf('mud-popover-anchor-center-center') >= 0) {
left = boundingRect.left + boundingRect.width / 2;
top = boundingRect.top + boundingRect.height / 2;
} else if (list.indexOf('mud-popover-anchor-center-right') >= 0) {
left = boundingRect.left + boundingRect.width;
top = boundingRect.top + boundingRect.height / 2;
} else if (list.indexOf('mud-popover-anchor-bottom-left') >= 0) {
left = boundingRect.left;
top = boundingRect.top + boundingRect.height;
} else if (list.indexOf('mud-popover-anchor-bottom-center') >= 0) {
left = boundingRect.left + boundingRect.width / 2;
top = boundingRect.top + boundingRect.height;
} else if (list.indexOf('mud-popover-anchor-bottom-right') >= 0) {
left = boundingRect.left + boundingRect.width;
top = boundingRect.top + boundingRect.height;
}
let offsetX = 0;
let offsetY = 0;
if (list.indexOf('mud-popover-top-left') >= 0) {
offsetX = 0;
offsetY = 0;
} else if (list.indexOf('mud-popover-top-center') >= 0) {
offsetX = -selfRect.width / 2;
offsetY = 0;
} else if (list.indexOf('mud-popover-top-right') >= 0) {
offsetX = -selfRect.width;
offsetY = 0;
}
else if (list.indexOf('mud-popover-center-left') >= 0) {
offsetX = 0;
offsetY = -selfRect.height / 2;
} else if (list.indexOf('mud-popover-center-center') >= 0) {
offsetX = -selfRect.width / 2;
offsetY = -selfRect.height / 2;
} else if (list.indexOf('mud-popover-center-right') >= 0) {
offsetX = -selfRect.width;
offsetY = -selfRect.height / 2;
}
else if (list.indexOf('mud-popover-bottom-left') >= 0) {
offsetX = 0;
offsetY = -selfRect.height;
} else if (list.indexOf('mud-popover-bottom-center') >= 0) {
offsetX = -selfRect.width / 2;
offsetY = -selfRect.height;
} else if (list.indexOf('mud-popover-bottom-right') >= 0) {
offsetX = -selfRect.width;
offsetY = -selfRect.height;
}
return {
top: top, left: left, offsetX: offsetX, offsetY: offsetY
};
},
flipClassReplacements: {
'top': {
'mud-popover-top-left': 'mud-popover-bottom-left',
'mud-popover-top-center': 'mud-popover-bottom-center',
'mud-popover-anchor-bottom-center': 'mud-popover-anchor-top-center',
'mud-popover-top-right': 'mud-popover-bottom-right',
},
'left': {
'mud-popover-top-left': 'mud-popover-top-right',
'mud-popover-center-left': 'mud-popover-center-right',
'mud-popover-anchor-center-right': 'mud-popover-anchor-center-left',
'mud-popover-bottom-left': 'mud-popover-bottom-right',
},
'right': {
'mud-popover-top-right': 'mud-popover-top-left',
'mud-popover-center-right': 'mud-popover-center-left',
'mud-popover-anchor-center-left': 'mud-popover-anchor-center-right',
'mud-popover-bottom-right': 'mud-popover-bottom-left',
},
'bottom': {
'mud-popover-bottom-left': 'mud-popover-top-left',
'mud-popover-bottom-center': 'mud-popover-top-center',
'mud-popover-anchor-top-center': 'mud-popover-anchor-bottom-center',
'mud-popover-bottom-right': 'mud-popover-top-right',
},
'top-and-left': {
'mud-popover-top-left': 'mud-popover-bottom-right',
},
'top-and-right': {
'mud-popover-top-right': 'mud-popover-bottom-left',
},
'bottom-and-left': {
'mud-popover-bottom-left': 'mud-popover-top-right',
},
'bottom-and-right': {
'mud-popover-bottom-right': 'mud-popover-top-left',
},
},
flipMargin: 0,
getPositionForFlippedPopver: function (inputArray, selector, boundingRect, selfRect) {
const classList = [];
for (var i = 0; i < inputArray.length; i++) {
const item = inputArray[i];
const replacments = window.mudpopoverHelper.flipClassReplacements[selector][item];
if (replacments) {
classList.push(replacments);
}
else {
classList.push(item);
}
}
return window.mudpopoverHelper.calculatePopoverPosition(classList, boundingRect, selfRect);
},
placePopover: function (popoverNode, classSelector) {
if (popoverNode && popoverNode.parentNode) {
const id = popoverNode.id.substr(8);
const popoverContentNode = document.getElementById('popovercontent-' + id);
if (popoverContentNode.classList.contains('mud-popover-open') == false) {
return;
}
if (!popoverContentNode) {
return;
}
if (classSelector) {
if (popoverContentNode.classList.contains(classSelector) == false) {
return;
}
}
const boundingRect = popoverNode.parentNode.getBoundingClientRect();
if (popoverContentNode.classList.contains('mud-popover-relative-width')) {
popoverContentNode.style['max-width'] = (boundingRect.width) + 'px';
}
const selfRect = popoverContentNode.getBoundingClientRect();
const classList = popoverContentNode.classList;
const classListArray = Array.from(popoverContentNode.classList);
const postion = window.mudpopoverHelper.calculatePopoverPosition(classListArray, boundingRect, selfRect);
let left = postion.left;
let top = postion.top;
let offsetX = postion.offsetX;
let offsetY = postion.offsetY;
if (classList.contains('mud-popover-overflow-flip-onopen') || classList.contains('mud-popover-overflow-flip-always')) {
const appBarElements = document.getElementsByClassName("mud-appbar mud-appbar-fixed-top");
let appBarOffset = 0;
if (appBarElements.length > 0) {
appBarOffset = appBarElements[0].getBoundingClientRect().height;
}
const graceMargin = window.mudpopoverHelper.flipMargin;
const deltaToLeft = left + offsetX;
const deltaToRight = window.innerWidth - left - selfRect.width;
const deltaTop = top - selfRect.height - appBarOffset;
const spaceToTop = top - appBarOffset;
const deltaBottom = window.innerHeight - top - selfRect.height;
//console.log('self-width: ' + selfRect.width + ' | self-height: ' + selfRect.height);
//console.log('left: ' + deltaToLeft + ' | rigth:' + deltaToRight + ' | top: ' + deltaTop + ' | bottom: ' + deltaBottom + ' | spaceToTop: ' + spaceToTop);
let selector = popoverContentNode.mudPopoverFliped;
if (!selector) {
if (classList.contains('mud-popover-top-left')) {
if (deltaBottom < graceMargin && deltaToRight < graceMargin && spaceToTop >= selfRect.height && deltaToLeft >= selfRect.width) {
selector = 'top-and-left';
} else if (deltaBottom < graceMargin && spaceToTop >= selfRect.height) {
selector = 'top';
} else if (deltaToRight < graceMargin && deltaToLeft >= selfRect.width) {
selector = 'left';
}
} else if (classList.contains('mud-popover-top-center')) {
if (deltaBottom < graceMargin && spaceToTop >= selfRect.height) {
selector = 'top';
}
} else if (classList.contains('mud-popover-top-right')) {
if (deltaBottom < graceMargin && deltaToLeft < graceMargin && spaceToTop >= selfRect.height && deltaToRight >= selfRect.width) {
selector = 'top-and-right';
} else if (deltaBottom < graceMargin && spaceToTop >= selfRect.height) {
selector = 'top';
} else if (deltaToLeft < graceMargin && deltaToRight >= selfRect.width) {
selector = 'right';
}
}
else if (classList.contains('mud-popover-center-left')) {
if (deltaToRight < graceMargin && deltaToLeft >= selfRect.width) {
selector = 'left';
}
}
else if (classList.contains('mud-popover-center-right')) {
if (deltaToLeft < graceMargin && deltaToRight >= selfRect.width) {
selector = 'right';
}
}
else if (classList.contains('mud-popover-bottom-left')) {
if (deltaTop < graceMargin && deltaToRight < graceMargin && deltaBottom >= 0 && deltaToLeft >= selfRect.width) {
selector = 'bottom-and-left';
} else if (deltaTop < graceMargin && deltaBottom >= 0) {
selector = 'bottom';
} else if (deltaToRight < graceMargin && deltaToLeft >= selfRect.width) {
selector = 'left';
}
} else if (classList.contains('mud-popover-bottom-center')) {
if (deltaTop < graceMargin && deltaBottom >= 0) {
selector = 'bottom';
}
} else if (classList.contains('mud-popover-bottom-right')) {
if (deltaTop < graceMargin && deltaToLeft < graceMargin && deltaBottom >= 0 && deltaToRight >= selfRect.width) {
selector = 'bottom-and-right';
} else if (deltaTop < graceMargin && deltaBottom >= 0) {
selector = 'bottom';
} else if (deltaToLeft < graceMargin && deltaToRight >= selfRect.width) {
selector = 'right';
}
}
}
if (selector && selector != 'none') {
const newPosition = window.mudpopoverHelper.getPositionForFlippedPopver(classListArray, selector, boundingRect, selfRect);
left = newPosition.left;
top = newPosition.top;
offsetX = newPosition.offsetX;
offsetY = newPosition.offsetY;
popoverContentNode.setAttribute('data-mudpopover-flip', 'flipped');
}
else {
popoverContentNode.removeAttribute('data-mudpopover-flip');
}
if (classList.contains('mud-popover-overflow-flip-onopen')) {
if (!popoverContentNode.mudPopoverFliped) {
popoverContentNode.mudPopoverFliped = selector || 'none';
}
}
}
if (popoverContentNode.classList.contains('mud-popover-fixed')) {
}
else if (window.getComputedStyle(popoverNode).position == 'fixed') {
popoverContentNode.style['position'] = 'fixed';
}
else {
offsetX += window.scrollX;
offsetY += window.scrollY
}
popoverContentNode.style['left'] = (left + offsetX) + 'px';
popoverContentNode.style['top'] = (top + offsetY) + 'px';
if (window.getComputedStyle(popoverNode).getPropertyValue('z-index') != 'auto') {
popoverContentNode.style['z-index'] = window.getComputedStyle(popoverNode).getPropertyValue('z-index');
popoverContentNode.skipZIndex = true;
}
}
},
placePopoverByClassSelector: function (classSelector = null) {
var items = window.mudPopover.getAllObservedContainers();
for (let i = 0; i < items.length; i++) {
const popoverNode = document.getElementById('popover-' + items[i]);
window.mudpopoverHelper.placePopover(popoverNode, classSelector);
}
},
placePopoverByNode: function (target) {
const id = target.id.substr(15);
const popoverNode = document.getElementById('popover-' + id);
window.mudpopoverHelper.placePopover(popoverNode);
},
countProviders: function () {
return document.querySelectorAll(".mud-popover-provider").length;
}
}
class MudPopover {
constructor() {
this.map = {};
this.contentObserver = null;
this.mainContainerClass = null;
}
callback(id, mutationsList, observer) {
for (const mutation of mutationsList) {
if (mutation.type === 'attributes') {
const target = mutation.target
if (mutation.attributeName == 'class') {
if (target.classList.contains('mud-popover-overflow-flip-onopen') &&
target.classList.contains('mud-popover-open') == false) {
target.mudPopoverFliped = null;
target.removeAttribute('data-mudpopover-flip');
}
window.mudpopoverHelper.placePopoverByNode(target);
}
else if (mutation.attributeName == 'data-ticks') {
const tickAttribute = target.getAttribute('data-ticks');
const parent = target.parentElement;
const tickValues = [];
let max = -1;
for (let i = 0; i < parent.children.length; i++) {
const childNode = parent.children[i];
const tickValue = parseInt(childNode.getAttribute('data-ticks'));
if (tickValue == 0) {
continue;
}
if (tickValues.indexOf(tickValue) >= 0) {
continue;
}
tickValues.push(tickValue);
if (tickValue > max) {
max = tickValue;
}
}
if (tickValues.length == 0) {
continue;
}
const sortedTickValues = tickValues.sort((x, y) => x - y);
for (let i = 0; i < parent.children.length; i++) {
const childNode = parent.children[i];
const tickValue = parseInt(childNode.getAttribute('data-ticks'));
if (tickValue == 0) {
continue;
}
if (childNode.skipZIndex == true) {
continue;
}
childNode.style['z-index'] = 'calc(var(--mud-zindex-popover) + ' + (sortedTickValues.indexOf(tickValue) + 3).toString() + ')';
}
}
}
}
}
initialize(containerClass, flipMargin) {
const mainContent = document.getElementsByClassName(containerClass);
if (mainContent.length == 0) {
return;
}
if (flipMargin) {
window.mudpopoverHelper.flipMargin = flipMargin;
}
this.mainContainerClass = containerClass;
if (!mainContent[0].mudPopoverMark) {
mainContent[0].mudPopoverMark = "mudded";
if (this.contentObserver != null) {
this.contentObserver.disconnect();
this.contentObserver = null;
}
this.contentObserver = new ResizeObserver(entries => {
window.mudpopoverHelper.placePopoverByClassSelector();
});
this.contentObserver.observe(mainContent[0]);
}
}
connect(id) {
this.initialize(this.mainContainerClass);
const popoverNode = document.getElementById('popover-' + id);
const popoverContentNode = document.getElementById('popovercontent-' + id);
if (popoverNode && popoverNode.parentNode && popoverContentNode) {
window.mudpopoverHelper.placePopover(popoverNode);
const config = { attributeFilter: ['class', 'data-ticks'] };
const observer = new MutationObserver(this.callback.bind(this, id));
observer.observe(popoverContentNode, config);
const resizeObserver = new ResizeObserver(entries => {
for (let entry of entries) {
const target = entry.target;
for (var i = 0; i < target.childNodes.length; i++) {
const childNode = target.childNodes[i];
if (childNode.id && childNode.id.startsWith('popover-')) {
window.mudpopoverHelper.placePopover(childNode);
}
}
}
});
resizeObserver.observe(popoverNode.parentNode);
const contentNodeObserver = new ResizeObserver(entries => {
for (let entry of entries) {
var target = entry.target;
window.mudpopoverHelper.placePopoverByNode(target);
}
});
contentNodeObserver.observe(popoverContentNode);
this.map[id] = {
mutationObserver: observer,
resizeObserver: resizeObserver,
contentNodeObserver: contentNodeObserver
};
}
}
disconnect(id) {
if (this.map[id]) {
const item = this.map[id]
item.mutationObserver.disconnect();
item.resizeObserver.disconnect();
item.contentNodeObserver.disconnect();
delete this.map[id];
}
}
dispose() {
for (var i in this.map) {
disconnect(i);
}
this.contentObserver.disconnect();
this.contentObserver = null;
}
getAllObservedContainers() {
const result = [];
for (var i in this.map) {
result.push(i);
}
return result;
}
}
window.mudPopover = new MudPopover();
window.addEventListener('scroll', () => {
window.mudpopoverHelper.placePopoverByClassSelector('mud-popover-fixed');
window.mudpopoverHelper.placePopoverByClassSelector('mud-popover-overflow-flip-always');
});
window.addEventListener('resize', () => {
window.mudpopoverHelper.placePopoverByClassSelector();
});