@alihbuzaid/ember-ui
Version:
Fleetbase UI provides all the interface components, helpers, services and utilities for building a Fleetbase extension into the Console.
272 lines (219 loc) • 8.43 kB
JavaScript
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { later } from '@ember/runloop';
import getWithDefault from '@fleetbase/ember-core/utils/get-with-default';
/**
* DrawerComponent provides a UI drawer element with several features such as
* open, close, toggle, and resize capabilities.
*/
export default class DrawerComponent extends Component {
/** Node of the drawer element. */
drawerNode;
/** Node of the drawer container element. */
drawerContainerNode;
/** Node of the drawer panel element. */
drawerPanelNode;
/** Node of the gutter element. */
gutterNode;
/** Indicates if the backdrop is not present. */
noBackdrop = true;
/** Indicates if the drawer is open. */
isOpen = true;
/** Indicates if the drawer is resizable. */
isResizable = true;
/** Indicates if the drawer is currently being resized. */
isResizing = false;
/** Indicates if the drawer is minimized. */
isMinimized = false;
/** Current X position of the mouse. */
mouseX = 0;
/** Current Y position of the mouse. */
mouseY = 0;
/** Height of the drawer. */
height = 300;
/** Indicates if the drawer has been rendered. */
_rendered = false;
/** Context object providing drawer control functions and state. */
context = {
toggle: this.toggle,
open: this.open,
close: this.close,
toggleMinimize: this.toggleMinimize,
minimize: this.minimize,
maximize: this.maximize,
isOpen: this.isOpen,
isMinimized: this.isMinimized,
};
getContext() {
return {
toggle: this.toggle,
open: this.open,
close: this.close,
toggleMinimize: this.toggleMinimize,
minimize: this.minimize,
maximize: this.maximize,
isOpen: this.isOpen,
isMinimized: this.isMinimized,
};
}
/**
* Sets up the component, establishes default properties, and calls the onLoad callback if provided.
* @param {HTMLElement} element - The element to be used as the drawer node.
*/
setupComponent(element) {
this.drawerNode = element;
this.height = getWithDefault(this.args, 'height', this.height);
this.isMinimized = getWithDefault(this.args, 'isMinimized', this.isMinimized);
later(
this,
() => {
this.isOpen = getWithDefault(this.args, 'isOpen', this.isOpen);
this.isResizable = getWithDefault(this.args, 'isResizable', this.isResizable);
this.noBackdrop = getWithDefault(this.args, 'noBackdrop', this.noBackdrop);
if (typeof this.args.onLoad === 'function') {
this.args.onLoad(this.getContext());
}
this._rendered = true;
},
300
);
}
/**
* Assigns a DOM node to a tracked property.
* @param {string} property - The property name to which the node will be assigned.
* @param {HTMLElement} node - The DOM node to be tracked.
*/
setupNode(property, node) {
this[`${property}Node`] = node;
}
/** Toggles the open state of the drawer. */
toggle() {
this.isOpen = !this.isOpen;
}
/** Opens the drawer. */
open() {
this.isOpen = true;
if (typeof this.args.onOpen === 'function') {
this.args.onOpen(this.getContext());
}
}
/** Closes the drawer. */
close() {
this.isOpen = false;
if (typeof this.args.onClose === 'function') {
this.args.onClose(this.getContext());
}
}
/** Toggles the minimized state of the drawer. */
toggleMinimize(options = {}) {
if (this.isMinimized) {
this.maximize();
} else {
this.minimize();
}
if (typeof options.onToggle === 'function') {
options.onToggle(this.getContext());
}
}
/** Minimizes the drawer. */
minimize(options = {}) {
this.isMinimized = true;
if (typeof options.onMinimize === 'function') {
options.onMinimize(this.getContext());
}
}
/** Maximizes the drawer. */
maximize(options = {}) {
this.isMinimized = false;
if (typeof options.onMaximize === 'function') {
options.onMaximize(this.getContext());
}
}
/**
* Starts the resize process for the drawer.
* @param {MouseEvent} event - The mouse event that initiates the resize.
*/
startResize(event) {
const disableResize = getWithDefault(this.args, 'disableResize', false);
const onResizeStart = getWithDefault(this.args, 'onResizeStart', null);
const { drawerPanelNode, drawerNode, isResizable } = this;
if (disableResize === true || !isResizable || !drawerPanelNode) {
return;
}
// if minimized undo
if (this.isMinimized) {
return this.maximize();
}
const bounds = drawerPanelNode.getBoundingClientRect();
// Set the overlay width/height
this.overlayWidth = bounds.width;
this.overlayHeight = bounds.height;
// Start resizing
this.isResizing = true;
// Get the current mouse position
this.mouseX = event.clientX;
this.mouseY = event.clientY;
// Attach the listeners
document.addEventListener('mousemove', this.resize);
document.addEventListener('mouseup', this.stopResize);
// Send up event
if (typeof onResizeStart === 'function') {
onResizeStart({ event, drawerNode, drawerPanelNode, context: this.getContext() });
}
}
/**
* Resizes the drawer during a mousemove event.
* @param {MouseEvent} event - The mouse event that triggers the resize.
*/
resize(event) {
const disableResize = getWithDefault(this.args, 'disableResize', false);
const onResize = getWithDefault(this.args, 'onResize', null);
const { drawerPanelNode, drawerNode, isResizable } = this;
if (disableResize === true || !isResizable || !drawerPanelNode) {
return;
}
const dy = event.clientY - this.mouseY;
const multiplier = -1;
const height = dy * multiplier + this.overlayHeight;
const minResizeHeight = getWithDefault(this.args, 'minResizeHeight', 0);
const maxResizeHeight = getWithDefault(this.args, 'maxResizeHeight', 600);
// Min resize height
if (height <= minResizeHeight) {
drawerPanelNode.style.height = `${minResizeHeight}px`;
return;
}
// Max resize height
if (height >= maxResizeHeight) {
drawerPanelNode.style.height = `${maxResizeHeight}px`;
return;
}
// Style changes
drawerPanelNode.style.userSelect = 'none';
drawerPanelNode.style.height = `${height}px`;
document.body.style.cursor = 'row-resize';
// Send callback
if (typeof onResize === 'function') {
onResize({ event, drawerNode, drawerPanelNode, context: this.getContext() });
}
}
/**
* Stops the resizing process and cleans up event listeners.
* @param {MouseEvent} event - The mouse event that ends the resize.
*/
stopResize(event) {
const onResizeEnd = getWithDefault(this.args, 'onResizeEnd', null);
const { drawerPanelNode, drawerNode } = this;
// End resizing
this.isResizing = false;
// Remove style changes
document.body.style.removeProperty('cursor');
drawerPanelNode.style.userSelect = 'auto';
// Remove the handlers of `mousemove` and `mouseup`
document.removeEventListener('mousemove', this.resize);
document.removeEventListener('mouseup', this.stopResize);
if (typeof onResizeEnd === 'function') {
onResizeEnd({ event, drawerNode, drawerPanelNode, context: this.getContext() });
}
}
}