init
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
<div class="treo-drawer-content">
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
@@ -0,0 +1,146 @@
|
||||
@import 'treo';
|
||||
|
||||
$treo-drawer-width: 320;
|
||||
|
||||
treo-drawer {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1 1 auto;
|
||||
width: #{$treo-drawer-width}px;
|
||||
min-width: #{$treo-drawer-width}px;
|
||||
max-width: #{$treo-drawer-width}px;
|
||||
z-index: 300;
|
||||
box-shadow: 0 2px 8px 0 rgba(0, 0, 0, .35);
|
||||
|
||||
// Animations
|
||||
&.treo-drawer-animations-enabled {
|
||||
transition-duration: 400ms;
|
||||
transition-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1);
|
||||
transition-property: visibility, margin-left, margin-right, transform, width, max-width, min-width;
|
||||
|
||||
.treo-drawer-content {
|
||||
transition-duration: 400ms;
|
||||
transition-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1);
|
||||
transition-property: width, max-width, min-width;
|
||||
}
|
||||
}
|
||||
|
||||
// Over mode
|
||||
&.treo-drawer-mode-over {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
|
||||
// Fixed mode
|
||||
&.treo-drawer-fixed {
|
||||
position: fixed;
|
||||
}
|
||||
}
|
||||
|
||||
// Left position
|
||||
&.treo-drawer-position-left {
|
||||
|
||||
// Side mode
|
||||
&.treo-drawer-mode-side {
|
||||
margin-left: #{$treo-drawer-width}px;
|
||||
|
||||
&.treo-drawer-opened {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Over mode
|
||||
&.treo-drawer-mode-over {
|
||||
left: 0;
|
||||
transform: translate3d(-100%, 0, 0);
|
||||
|
||||
&.treo-drawer-opened {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Content
|
||||
.treo-drawer-content {
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Right position
|
||||
&.treo-drawer-position-right {
|
||||
|
||||
// Side mode
|
||||
&.treo-drawer-mode-side {
|
||||
margin-right: -#{$treo-drawer-width}px;
|
||||
|
||||
&.treo-drawer-opened {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Over mode
|
||||
&.treo-drawer-mode-over {
|
||||
right: 0;
|
||||
transform: translate3d(100%, 0, 0);
|
||||
|
||||
&.treo-drawer-opened {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Content
|
||||
.treo-drawer-content {
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Content
|
||||
.treo-drawer-content {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
// Overlay
|
||||
.treo-drawer-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 299;
|
||||
opacity: 0;
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
|
||||
// Fixed mode
|
||||
&.treo-drawer-overlay-fixed {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
// Transparent overlay
|
||||
&.treo-drawer-overlay-transparent {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
// @ Theming
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
@include treo-theme {
|
||||
|
||||
$background: map-get($theme, background);
|
||||
|
||||
treo-drawer {
|
||||
background: map-get($background, card);
|
||||
|
||||
.treo-drawer-content {
|
||||
background: map-get($background, card);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,523 @@
|
||||
import { Component, ElementRef, EventEmitter, HostBinding, HostListener, Input, OnDestroy, OnInit, Output, Renderer2, ViewEncapsulation } from '@angular/core';
|
||||
import { animate, AnimationBuilder, AnimationPlayer, style } from '@angular/animations';
|
||||
import { TreoDrawerMode, TreoDrawerPosition } from '@treo/components/drawer/drawer.types';
|
||||
import { TreoDrawerService } from '@treo/components/drawer/drawer.service';
|
||||
|
||||
@Component({
|
||||
selector : 'treo-drawer',
|
||||
templateUrl : './drawer.component.html',
|
||||
styleUrls : ['./drawer.component.scss'],
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
exportAs : 'treoDrawer'
|
||||
})
|
||||
export class TreoDrawerComponent implements OnInit, OnDestroy
|
||||
{
|
||||
// Name
|
||||
@Input()
|
||||
name: string;
|
||||
|
||||
// Private
|
||||
private _fixed: boolean;
|
||||
private _mode: TreoDrawerMode;
|
||||
private _opened: boolean | '';
|
||||
private _overlay: HTMLElement | null;
|
||||
private _player: AnimationPlayer;
|
||||
private _position: TreoDrawerPosition;
|
||||
private _transparentOverlay: boolean | '';
|
||||
|
||||
// On fixed changed
|
||||
@Output()
|
||||
readonly fixedChanged: EventEmitter<boolean>;
|
||||
|
||||
// On mode changed
|
||||
@Output()
|
||||
readonly modeChanged: EventEmitter<TreoDrawerMode>;
|
||||
|
||||
// On opened changed
|
||||
@Output()
|
||||
readonly openedChanged: EventEmitter<boolean | ''>;
|
||||
|
||||
// On position changed
|
||||
@Output()
|
||||
readonly positionChanged: EventEmitter<TreoDrawerPosition>;
|
||||
|
||||
@HostBinding('class.treo-drawer-animations-enabled')
|
||||
private _animationsEnabled: boolean;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param {AnimationBuilder} _animationBuilder
|
||||
* @param {TreoDrawerService} _treoDrawerService
|
||||
* @param {ElementRef} _elementRef
|
||||
* @param {Renderer2} _renderer2
|
||||
*/
|
||||
constructor(
|
||||
private _animationBuilder: AnimationBuilder,
|
||||
private _treoDrawerService: TreoDrawerService,
|
||||
private _elementRef: ElementRef,
|
||||
private _renderer2: Renderer2
|
||||
)
|
||||
{
|
||||
// Set the private defaults
|
||||
this._animationsEnabled = false;
|
||||
this._overlay = null;
|
||||
|
||||
// Set the defaults
|
||||
this.fixedChanged = new EventEmitter<boolean>();
|
||||
this.modeChanged = new EventEmitter<TreoDrawerMode>();
|
||||
this.openedChanged = new EventEmitter<boolean | ''>();
|
||||
this.positionChanged = new EventEmitter<TreoDrawerPosition>();
|
||||
|
||||
this.fixed = false;
|
||||
this.mode = 'side';
|
||||
this.opened = false;
|
||||
this.position = 'left';
|
||||
this.transparentOverlay = false;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
// @ Accessors
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Setter & getter for fixed
|
||||
*
|
||||
* @param value
|
||||
*/
|
||||
@Input()
|
||||
set fixed(value: boolean)
|
||||
{
|
||||
// If the value is the same, return...
|
||||
if ( this._fixed === value )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Store the fixed value
|
||||
this._fixed = value;
|
||||
|
||||
// Update the class
|
||||
if ( this.fixed )
|
||||
{
|
||||
this._renderer2.addClass(this._elementRef.nativeElement, 'treo-drawer-fixed');
|
||||
}
|
||||
else
|
||||
{
|
||||
this._renderer2.removeClass(this._elementRef.nativeElement, 'treo-drawer-fixed');
|
||||
}
|
||||
|
||||
// Execute the observable
|
||||
this.fixedChanged.next(this.fixed);
|
||||
}
|
||||
|
||||
get fixed(): boolean
|
||||
{
|
||||
return this._fixed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter & getter for mode
|
||||
*
|
||||
* @param value
|
||||
*/
|
||||
@Input()
|
||||
set mode(value: TreoDrawerMode)
|
||||
{
|
||||
// If the value is the same, return...
|
||||
if ( this._mode === value )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable the animations
|
||||
this._disableAnimations();
|
||||
|
||||
// If the mode changes: 'over -> side'
|
||||
if ( this.mode === 'over' && value === 'side' )
|
||||
{
|
||||
// Hide the overlay
|
||||
this._hideOverlay();
|
||||
}
|
||||
|
||||
// If the mode changes: 'side -> over'
|
||||
if ( this.mode === 'side' && value === 'over' )
|
||||
{
|
||||
// If the drawer is opened
|
||||
if ( this.opened )
|
||||
{
|
||||
// Show the overlay
|
||||
this._showOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
let modeClassName;
|
||||
|
||||
// Remove the previous mode class
|
||||
modeClassName = 'treo-drawer-mode-' + this.mode;
|
||||
this._renderer2.removeClass(this._elementRef.nativeElement, modeClassName);
|
||||
|
||||
// Store the mode
|
||||
this._mode = value;
|
||||
|
||||
// Add the new mode class
|
||||
modeClassName = 'treo-drawer-mode-' + this.mode;
|
||||
this._renderer2.addClass(this._elementRef.nativeElement, modeClassName);
|
||||
|
||||
// Execute the observable
|
||||
this.modeChanged.next(this.mode);
|
||||
|
||||
// Enable the animations after a delay
|
||||
// The delay must be bigger than the current transition-duration
|
||||
// to make sure nothing will be animated while the mode changing
|
||||
setTimeout(() => {
|
||||
this._enableAnimations();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
get mode(): TreoDrawerMode
|
||||
{
|
||||
return this._mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter & getter for opened
|
||||
*
|
||||
* @param value
|
||||
*/
|
||||
@Input()
|
||||
set opened(value: boolean | '')
|
||||
{
|
||||
// If the value is the same, return...
|
||||
if ( this._opened === value )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If the provided value is an empty string,
|
||||
// take that as a 'true'
|
||||
if ( value === '' )
|
||||
{
|
||||
value = true;
|
||||
}
|
||||
|
||||
// Set the opened value
|
||||
this._opened = value;
|
||||
|
||||
// If the drawer opened, and the mode
|
||||
// is 'over', show the overlay
|
||||
if ( this.mode === 'over' )
|
||||
{
|
||||
if ( this._opened )
|
||||
{
|
||||
this._showOverlay();
|
||||
}
|
||||
else
|
||||
{
|
||||
this._hideOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
// Update opened classes
|
||||
if ( this.opened )
|
||||
{
|
||||
this._renderer2.setStyle(this._elementRef.nativeElement, 'visibility', 'visible');
|
||||
this._renderer2.addClass(this._elementRef.nativeElement, 'treo-drawer-opened');
|
||||
}
|
||||
else
|
||||
{
|
||||
this._renderer2.setStyle(this._elementRef.nativeElement, 'visibility', 'hidden');
|
||||
this._renderer2.removeClass(this._elementRef.nativeElement, 'treo-drawer-opened');
|
||||
}
|
||||
|
||||
// Execute the observable
|
||||
this.openedChanged.next(this.opened);
|
||||
}
|
||||
|
||||
get opened(): boolean | ''
|
||||
{
|
||||
return this._opened;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter & getter for position
|
||||
*
|
||||
* @param value
|
||||
*/
|
||||
@Input()
|
||||
set position(value: TreoDrawerPosition)
|
||||
{
|
||||
// If the value is the same, return...
|
||||
if ( this._position === value )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
let positionClassName;
|
||||
|
||||
// Remove the previous position class
|
||||
positionClassName = 'treo-drawer-position-' + this.position;
|
||||
this._renderer2.removeClass(this._elementRef.nativeElement, positionClassName);
|
||||
|
||||
// Store the position
|
||||
this._position = value;
|
||||
|
||||
// Add the new position class
|
||||
positionClassName = 'treo-drawer-position-' + this.position;
|
||||
this._renderer2.addClass(this._elementRef.nativeElement, positionClassName);
|
||||
|
||||
// Execute the observable
|
||||
this.positionChanged.next(this.position);
|
||||
}
|
||||
|
||||
get position(): TreoDrawerPosition
|
||||
{
|
||||
return this._position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter & getter for transparent overlay
|
||||
*
|
||||
* @param value
|
||||
*/
|
||||
@Input()
|
||||
set transparentOverlay(value: boolean | '')
|
||||
{
|
||||
// If the value is the same, return...
|
||||
if ( this._opened === value )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If the provided value is an empty string,
|
||||
// take that as a 'true' and set the opened value
|
||||
if ( value === '' )
|
||||
{
|
||||
// Set the opened value
|
||||
this._transparentOverlay = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set the transparent overlay value
|
||||
this._transparentOverlay = value;
|
||||
}
|
||||
}
|
||||
|
||||
get transparentOverlay(): boolean | ''
|
||||
{
|
||||
return this._transparentOverlay;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
// @ Lifecycle hooks
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* On init
|
||||
*/
|
||||
ngOnInit(): void
|
||||
{
|
||||
// Register the drawer
|
||||
this._treoDrawerService.registerComponent(this.name, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* On destroy
|
||||
*/
|
||||
ngOnDestroy(): void
|
||||
{
|
||||
// Deregister the drawer from the registry
|
||||
this._treoDrawerService.deregisterComponent(this.name);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
// @ Private methods
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Enable the animations
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
private _enableAnimations(): void
|
||||
{
|
||||
// If the animations are already enabled, return...
|
||||
if ( this._animationsEnabled )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Enable the animations
|
||||
this._animationsEnabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the animations
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
private _disableAnimations(): void
|
||||
{
|
||||
// If the animations are already disabled, return...
|
||||
if ( !this._animationsEnabled )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable the animations
|
||||
this._animationsEnabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the backdrop
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
private _showOverlay(): void
|
||||
{
|
||||
// Create the backdrop element
|
||||
this._overlay = this._renderer2.createElement('div');
|
||||
|
||||
// Add a class to the backdrop element
|
||||
this._overlay.classList.add('treo-drawer-overlay');
|
||||
|
||||
// Add a class depending on the fixed option
|
||||
if ( this.fixed )
|
||||
{
|
||||
this._overlay.classList.add('treo-drawer-overlay-fixed');
|
||||
}
|
||||
|
||||
// Add a class depending on the transparentOverlay option
|
||||
if ( this.transparentOverlay )
|
||||
{
|
||||
this._overlay.classList.add('treo-drawer-overlay-transparent');
|
||||
}
|
||||
|
||||
// Append the backdrop to the parent of the drawer
|
||||
this._renderer2.appendChild(this._elementRef.nativeElement.parentElement, this._overlay);
|
||||
|
||||
// Create the enter animation and attach it to the player
|
||||
this._player =
|
||||
this._animationBuilder
|
||||
.build([
|
||||
animate('300ms cubic-bezier(0.25, 0.8, 0.25, 1)', style({opacity: 1}))
|
||||
]).create(this._overlay);
|
||||
|
||||
// Play the animation
|
||||
this._player.play();
|
||||
|
||||
// Add an event listener to the overlay
|
||||
this._overlay.addEventListener('click', () => {
|
||||
this.close();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the backdrop
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
private _hideOverlay(): void
|
||||
{
|
||||
if ( !this._overlay )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the leave animation and attach it to the player
|
||||
this._player =
|
||||
this._animationBuilder
|
||||
.build([
|
||||
animate('300ms cubic-bezier(0.25, 0.8, 0.25, 1)', style({opacity: 0}))
|
||||
]).create(this._overlay);
|
||||
|
||||
// Play the animation
|
||||
this._player.play();
|
||||
|
||||
// Once the animation is done...
|
||||
this._player.onDone(() => {
|
||||
|
||||
// If the backdrop still exists...
|
||||
if ( this._overlay )
|
||||
{
|
||||
// Remove the backdrop
|
||||
this._overlay.parentNode.removeChild(this._overlay);
|
||||
this._overlay = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* On mouseenter
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
@HostListener('mouseenter')
|
||||
private _onMouseenter(): void
|
||||
{
|
||||
// Enable the animations
|
||||
this._enableAnimations();
|
||||
|
||||
// Add a class
|
||||
this._renderer2.addClass(this._elementRef.nativeElement, 'treo-drawer-hover');
|
||||
}
|
||||
|
||||
/**
|
||||
* On mouseleave
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
@HostListener('mouseleave')
|
||||
private _onMouseleave(): void
|
||||
{
|
||||
// Enable the animations
|
||||
this._enableAnimations();
|
||||
|
||||
// Remove the class
|
||||
this._renderer2.removeClass(this._elementRef.nativeElement, 'treo-drawer-hover');
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
// @ Public methods
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Open the drawer
|
||||
*/
|
||||
open(): void
|
||||
{
|
||||
// Enable the animations
|
||||
this._enableAnimations();
|
||||
|
||||
// Open
|
||||
this.opened = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the drawer
|
||||
*/
|
||||
close(): void
|
||||
{
|
||||
// Enable the animations
|
||||
this._enableAnimations();
|
||||
|
||||
// Close
|
||||
this.opened = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle the opened status
|
||||
*/
|
||||
toggle(): void
|
||||
{
|
||||
// Toggle
|
||||
if ( this.opened )
|
||||
{
|
||||
this.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
this.open();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { TreoDrawerComponent } from '@treo/components/drawer/drawer.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
TreoDrawerComponent
|
||||
],
|
||||
imports : [
|
||||
CommonModule
|
||||
],
|
||||
exports : [
|
||||
TreoDrawerComponent
|
||||
]
|
||||
})
|
||||
export class TreoDrawerModule
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { TreoDrawerComponent } from '@treo/components/drawer/drawer.component';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class TreoDrawerService
|
||||
{
|
||||
// Private
|
||||
private _componentRegistry: Map<string, TreoDrawerComponent>;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
constructor()
|
||||
{
|
||||
// Set the defaults
|
||||
this._componentRegistry = new Map<string, TreoDrawerComponent>();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
// @ Public methods
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Register drawer component
|
||||
*
|
||||
* @param name
|
||||
* @param component
|
||||
*/
|
||||
registerComponent(name: string, component: TreoDrawerComponent): void
|
||||
{
|
||||
this._componentRegistry.set(name, component);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deregister drawer component
|
||||
*
|
||||
* @param name
|
||||
*/
|
||||
deregisterComponent(name: string): void
|
||||
{
|
||||
this._componentRegistry.delete(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get drawer component from the registry
|
||||
*
|
||||
* @param name
|
||||
*/
|
||||
getComponent(name: string): TreoDrawerComponent
|
||||
{
|
||||
return this._componentRegistry.get(name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export type TreoDrawerMode = 'over' | 'side';
|
||||
export type TreoDrawerPosition = 'left' | 'right';
|
||||
@@ -0,0 +1 @@
|
||||
export * from '@treo/components/drawer/public-api';
|
||||
@@ -0,0 +1,4 @@
|
||||
export * from '@treo/components/drawer/drawer.component';
|
||||
export * from '@treo/components/drawer/drawer.module';
|
||||
export * from '@treo/components/drawer/drawer.service';
|
||||
export * from '@treo/components/drawer/drawer.types';
|
||||
Reference in New Issue
Block a user