-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #28 from fleetbase/dev-v0.2.4
v0.2.4
- Loading branch information
Showing
23 changed files
with
846 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<div role="dialog" aria-modal="true" class="next-drawer {{if this.isMinimized 'drawer-is-minimized'}} {{if this.isResizing 'drawer-is-resizing'}} {{if this.isOpen 'drawer-is-open'}} {{if this.noBackdrop 'drawer-no-backdrop'}} {{if @fullHeight 'drawer-full-height'}} {{@overlayClass}}" {{did-insert this.setupComponent}}> | ||
{{#if this._rendered}} | ||
<div class="next-drawer-panel-container {{@containerClass}}" {{did-insert (fn this.setupNode "drawerContainer")}}> | ||
{{#if this.isResizable}} | ||
<div class="gutter" {{did-insert (fn this.setupNode "gutter")}} {{on "mousedown" this.startResize}}> | ||
{{#if @notchEnabled}} | ||
<div class="notch" {{on "dblclick" this.toggleMinimize}}> | ||
<span class="bar"></span> | ||
<span class="bar"></span> | ||
<span class="bar"></span> | ||
</div> | ||
{{/if}} | ||
</div> | ||
{{/if}} | ||
<div class="next-drawer-panel" {{did-insert (fn this.setupNode "drawerPanel")}} {{set-height this.height}} ...attributes>{{yield this.context}}</div> | ||
</div> | ||
{{/if}} | ||
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,244 @@ | ||
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. */ | ||
@tracked drawerNode; | ||
|
||
/** Node of the drawer container element. */ | ||
@tracked drawerContainerNode; | ||
|
||
/** Node of the drawer panel element. */ | ||
@tracked drawerPanelNode; | ||
|
||
/** Node of the gutter element. */ | ||
@tracked gutterNode; | ||
|
||
/** Indicates if the backdrop is not present. */ | ||
@tracked noBackdrop = true; | ||
|
||
/** Indicates if the drawer is open. */ | ||
@tracked isOpen = true; | ||
|
||
/** Indicates if the drawer is resizable. */ | ||
@tracked isResizable = true; | ||
|
||
/** Indicates if the drawer is currently being resized. */ | ||
@tracked isResizing = false; | ||
|
||
/** Indicates if the drawer is minimized. */ | ||
@tracked isMinimized = false; | ||
|
||
/** Current X position of the mouse. */ | ||
@tracked mouseX = 0; | ||
|
||
/** Current Y position of the mouse. */ | ||
@tracked mouseY = 0; | ||
|
||
/** Height of the drawer. */ | ||
@tracked height = 300; | ||
|
||
/** Indicates if the drawer has been rendered. */ | ||
@tracked _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, | ||
}; | ||
|
||
/** 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, | ||
}; | ||
|
||
/** | ||
* 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. | ||
*/ | ||
@action 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.context); | ||
} | ||
|
||
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. | ||
*/ | ||
@action setupNode(property, node) { | ||
this[`${property}Node`] = node; | ||
} | ||
|
||
/** Toggles the open state of the drawer. */ | ||
@action toggle() { | ||
this.isOpen = !this.isOpen; | ||
} | ||
|
||
/** Opens the drawer. */ | ||
@action open() { | ||
this.isOpen = true; | ||
} | ||
|
||
/** Closes the drawer. */ | ||
@action close() { | ||
this.isOpen = false; | ||
} | ||
|
||
/** Toggles the minimized state of the drawer. */ | ||
@action toggleMinimize() { | ||
this.isMinimized = !this.isMinimized; | ||
} | ||
|
||
/** Minimizes the drawer. */ | ||
@action minimize() { | ||
this.isMinimized = true; | ||
} | ||
|
||
/** Maximizes the drawer. */ | ||
@action maximize() { | ||
this.isMinimized = false; | ||
} | ||
|
||
/** | ||
* Starts the resize process for the drawer. | ||
* @param {MouseEvent} event - The mouse event that initiates the resize. | ||
*/ | ||
@action startResize(event) { | ||
const disableResize = getWithDefault(this.args, 'disableResize', false); | ||
const onResizeStart = getWithDefault(this.args, 'onResizeStart', null); | ||
const { drawerPanelNode, 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, drawerPanelNode }); | ||
} | ||
} | ||
|
||
/** | ||
* Resizes the drawer during a mousemove event. | ||
* @param {MouseEvent} event - The mouse event that triggers the resize. | ||
*/ | ||
@action resize(event) { | ||
const disableResize = getWithDefault(this.args, 'disableResize', false); | ||
const onResize = getWithDefault(this.args, 'onResize', null); | ||
const { drawerPanelNode, 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, drawerPanelNode }); | ||
} | ||
} | ||
|
||
/** | ||
* Stops the resizing process and cleans up event listeners. | ||
* @param {MouseEvent} event - The mouse event that ends the resize. | ||
*/ | ||
@action stopResize(event) { | ||
const onResizeEnd = getWithDefault(this.args, 'onResizeEnd', null); | ||
const { drawerPanelNode } = 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, drawerPanelNode }); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
<div id="fleetbase-full-calendar" class="fleetbase-full-calendar" {{did-insert this.setupCalendar}}></div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
import Component from '@glimmer/component'; | ||
import { tracked } from '@glimmer/tracking'; | ||
import { action } from '@ember/object'; | ||
import { classify } from '@ember/string'; | ||
import { Calendar } from '@fullcalendar/core'; | ||
import dayGridPlugin from '@fullcalendar/daygrid'; | ||
import interactionPlugin from '@fullcalendar/interaction'; | ||
|
||
export default class FullCalendarComponent extends Component { | ||
/** | ||
* @var {HTMLElement} calendarEl | ||
*/ | ||
@tracked calendarEl; | ||
|
||
/** | ||
* @var {Calendar} calendar | ||
* @package @fullcalendar/core | ||
*/ | ||
@tracked calendar; | ||
|
||
/** | ||
* Default events to trigger for | ||
* @var {Array} | ||
*/ | ||
@tracked events = ['dateClick', 'drop', 'eventReceive', 'eventClick', 'eventDragStop', 'eventDrop', 'eventAdd', 'eventChange', 'eventRemove']; | ||
|
||
/** | ||
* Tracked calendar event listeners | ||
* @var {Array} | ||
*/ | ||
@tracked _listeners = []; | ||
|
||
/** | ||
* Initializes and renders the calendar component | ||
* | ||
* @param {HTMLElement} calendarEl | ||
*/ | ||
@action setupCalendar(calendarEl) { | ||
// track calendar htmlelement | ||
this.calendarEl = calendarEl; | ||
|
||
// get events | ||
let events = this.args.events || []; | ||
|
||
// initialize calendar | ||
this.calendar = new Calendar(calendarEl, { | ||
events, | ||
plugins: [dayGridPlugin, interactionPlugin], | ||
initialView: 'dayGridMonth', | ||
editable: true, | ||
headerToolbar: { | ||
left: 'prev,next today', | ||
center: 'title', | ||
right: '', | ||
}, | ||
}); | ||
|
||
// trigger callback on initialize | ||
if (typeof this.args.onInit === 'function') { | ||
this.args.onInit(this.calendar); | ||
} | ||
|
||
// render calendar | ||
this.calendar.render(); | ||
|
||
// listen for events | ||
this.createCalendarEventListeners(); | ||
} | ||
|
||
triggerCalendarEvent(eventName, ...params) { | ||
if (typeof this[eventName] === 'function') { | ||
this[eventName](...params); | ||
} | ||
|
||
if (typeof this.args[eventName] === 'function') { | ||
this.args[eventName](...params); | ||
} | ||
} | ||
|
||
createCalendarEventListeners() { | ||
for (let i = 0; i < this.events.length; i++) { | ||
const eventName = this.events.objectAt(i); | ||
const callbackName = `on${classify(eventName)}`; | ||
|
||
if (typeof this.args[callbackName] === 'function') { | ||
// track for destroy purposes | ||
this._listeners.pushObject({ | ||
eventName, | ||
callbackName, | ||
}); | ||
|
||
// create listener | ||
this.calendar.on(eventName, this.triggerCalendarEvent.bind(this, callbackName)); | ||
} | ||
} | ||
|
||
// check for custom events | ||
// @todo | ||
} | ||
|
||
destroyCalendarEventListeners() { | ||
for (let i = 0; i < this._listeners.length; i++) { | ||
const listener = this._listeners.objectAt(i); | ||
const { eventName, callbackName } = listener; | ||
|
||
// kill listener | ||
this.calendar.off(eventName, this.triggerCalendarEvent.bind(this, callbackName)); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
<div class="fleetbase-full-calendar-draggable" data-event={{@eventData}} {{did-insert this.makeDraggable}} ...attributes> | ||
{{yield}} | ||
</div> |
Oops, something went wrong.