@fleetbase/ember-ui
Version:
Fleetbase UI provides all the interface components, helpers, services and utilities for building a Fleetbase extension into the Console.
163 lines (127 loc) • 5.05 kB
JavaScript
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action, computed } from '@ember/object';
import { isArray } from '@ember/array';
import { computePosition, flip, shift, offset, arrow } from '@floating-ui/dom';
const { assign } = Object;
export default class FloatingComponent extends Component {
element;
target;
get defaultOptions() {
const { shiftOptions } = this.args;
return {
placement: 'bottom',
strategy: 'absolute',
middleware: [flip(), shift(shiftOptions)],
};
}
get floatingContainer() {
const { container, target } = this.args;
const trackedTarget = this.target;
if (container === undefined && target === undefined) {
return trackedTarget instanceof Element ? trackedTarget.parentNode : trackedTarget;
}
let floatingContainer = document.body;
if (container === undefined && target instanceof Element) {
floatingContainer = target.parentNode || target.closest('section') || target.closest('main') || target.closest('body');
}
if (container instanceof Element) {
floatingContainer = container;
} else if (typeof container === 'string') {
const selector = container;
const possibleContainers = document.querySelectorAll(selector);
floatingContainer = possibleContainers[0];
}
return floatingContainer;
}
resolveTarget(element) {
const { target } = this.args;
if (this.target instanceof Element) {
return this.target;
}
let possibleTarget = element instanceof Element ? element.parentNode : document.body;
if (target instanceof Element) {
possibleTarget = target;
} else if (typeof target === 'string') {
const selector = target;
const possibleTargets = self.document.querySelectorAll(selector);
possibleTarget = possibleTargets[0];
}
return possibleTarget;
}
findParent(parentFinderNode) {
const { container, target } = this.args;
if (container === undefined && target === undefined) {
// set target from parent finder node and remove
this.target = parentFinderNode.parentNode;
}
parentFinderNode.remove();
}
getMiddleware(element) {
const { defaultOptions } = this;
const mware = this.args.middleware;
const offsetBy = this.args.offset;
const displayArrow = this.args.offset;
const middleware = isArray(mware) ? mware : defaultOptions.middleware;
if (typeof offsetBy === 'number') {
middleware.push(offset(offsetBy));
}
if (displayArrow === true) {
const arrowNode = element.closest('[x-arrow]');
if (arrowNode instanceof Element) {
middleware.push(arrow(arrowNode));
}
}
return middleware;
}
getOptions(element) {
const { defaultOptions } = this;
const { placement, strategy } = this.args;
const middleware = this.getMiddleware(element);
return {
placement: placement || defaultOptions.placement,
strategy: strategy || defaultOptions.strategy,
middleware,
};
}
setupComponent(element) {
const { registerAPI } = this.args;
const target = (this.target = this.resolveTarget(element));
if (typeof registerAPI === 'function') {
registerAPI({
floatingElement: element,
floatingTarget: target,
computePosition: this.computePosition.bind(this),
});
}
return this.computePosition(target, element);
}
computePosition(target, element) {
const { onPositionComputed } = this.args;
const { placement, strategy, middleware } = this.getOptions(element);
assign(element.style, { position: strategy });
computePosition(target, element, {
placement,
strategy,
middleware,
}).then(({ x, y }) => {
assign(element.style, {
position: 'absolute',
pointerEvents: 'none',
willChange: 'transform',
top: '0',
left: '0',
transform: `translate3d(${Math.round(x)}px,${Math.round(y)}px,0)`,
});
if (typeof onPositionComputed === 'function') {
onPositionComputed({
floatingElement: element,
floatingTarget: target,
computePosition: this.computePosition.bind(this),
x,
y,
});
}
});
}
}