From f709dbcd704c24d9592e5a227f2c8a23800d6289 Mon Sep 17 00:00:00 2001 From: Alex ORLUC <alex.orluc@maarch.org> Date: Mon, 29 Oct 2018 12:35:35 +0100 Subject: [PATCH] FEAT #8659 add mat-speed-dia custom instead of ecodev module (bugged) --- package.json | 1 - src/frontend/app/app-common.module.ts | 14 +- .../app/menu/menu-shortcut.component.html | 20 +- .../app/menu/menu-shortcut.component.scss | 16 +- .../fab-speed-dial/fab-speed-dial.scss | 160 +++++++++++ .../plugins/fab-speed-dial/fab-speed-dial.ts | 252 ++++++++++++++++++ src/frontend/plugins/fab-speed-dial/index.ts | 1 + 7 files changed, 446 insertions(+), 18 deletions(-) create mode 100644 src/frontend/plugins/fab-speed-dial/fab-speed-dial.scss create mode 100644 src/frontend/plugins/fab-speed-dial/fab-speed-dial.ts create mode 100644 src/frontend/plugins/fab-speed-dial/index.ts diff --git a/package.json b/package.json index 4893eb16e5e..ecde3b13aa7 100755 --- a/package.json +++ b/package.json @@ -11,7 +11,6 @@ "author": "Maarch", "license": "GPL-3.0", "dependencies": { - "@ecodev/fab-speed-dial": "^2.0.0", "@fortawesome/fontawesome-free": "^5.4.2", "bootstrap": "^3.3.7", "chart.js": "1.1.1", diff --git a/src/frontend/app/app-common.module.ts b/src/frontend/app/app-common.module.ts index 19723d8da35..304e79a5ada 100644 --- a/src/frontend/app/app-common.module.ts +++ b/src/frontend/app/app-common.module.ts @@ -17,7 +17,8 @@ import { FilterListPipe } from '../plugins/filterList.pipe /*FRONT IMPORTS*/ import { AppMaterialModule } from './app-material.module'; -import { EcoFabSpeedDialModule } from '@ecodev/fab-speed-dial'; +import { SmdFabSpeedDialComponent,SmdFabSpeedDialTrigger, SmdFabSpeedDialActions, } from '../plugins/fab-speed-dial'; + /*MENU COMPONENT*/ import { MainHeaderComponent } from './menu/main-header.component'; @@ -40,7 +41,6 @@ import { BasketHomeComponent } from './basket/basket-home HttpClientModule, RouterModule, AppMaterialModule, - EcoFabSpeedDialModule ], declarations: [ MainHeaderComponent, @@ -52,7 +52,10 @@ import { BasketHomeComponent } from './basket/basket-home TimeAgoPipe, TimeLimitPipe, FilterListPipe, - IndexingGroupModalComponent + IndexingGroupModalComponent, + SmdFabSpeedDialComponent, + SmdFabSpeedDialTrigger, + SmdFabSpeedDialActions, ], exports: [ CommonModule, @@ -71,7 +74,10 @@ import { BasketHomeComponent } from './basket/basket-home AppMaterialModule, TimeAgoPipe, TimeLimitPipe, - FilterListPipe + FilterListPipe, + SmdFabSpeedDialComponent, + SmdFabSpeedDialTrigger, + SmdFabSpeedDialActions, ], entryComponents: [ IndexingGroupModalComponent diff --git a/src/frontend/app/menu/menu-shortcut.component.html b/src/frontend/app/menu/menu-shortcut.component.html index 1cf53df1549..7837ef7d8f7 100644 --- a/src/frontend/app/menu/menu-shortcut.component.html +++ b/src/frontend/app/menu/menu-shortcut.component.html @@ -38,7 +38,7 @@ </button> </span> <span style="flex:1;text-align: left;padding: 5px;" *ngIf="shortcut.id=='index_mlb' && shortcut.shortcut == 'true' && speedDialFabButtons.length>1"> - <eco-fab-speed-dial #myFab direction="down" animationMode="fling" fixed="false" (mouseenter)="myFab.open = true" (mouseleave)="myFab.open = false"> + <!--<eco-fab-speed-dial #myFab direction="down" animationMode="fling" fixed="false" (mouseenter)="myFab.open = true" (mouseleave)="myFab.open = false"> <eco-fab-speed-dial-trigger spin="true"> <button color="default" mat-fab matTooltip="{{shortcut.name}}" matTooltipPosition="above"> @@ -52,7 +52,23 @@ style="position: absolute;margin-left: 20px;margin-top: -5px;">{{button.label}}</span><mat-icon class="{{button.icon}}"></mat-icon> </button> </eco-fab-speed-dial-actions> - </eco-fab-speed-dial> + </eco-fab-speed-dial>--> + + <smd-fab-speed-dial #myFab direction="down" animationMode="fling" fixed="false" (mouseenter)="myFab.open = true" + (mouseleave)="myFab.open = false"> + <smd-fab-trigger spin="true"> + <button color="default" mat-fab (click)="_click('trigger')"> + <mat-icon style="font-size:22px;" class="fa {{shortcut.style}} spin360"></mat-icon> + </button> + </smd-fab-trigger> + + <smd-fab-actions> + <button color="primary" mat-mini-fab (click)="onSpeedDialFabClicked(button,shortcut)" *ngFor="let button of speedDialFabButtons"> + <span class="speedDialLabel" color="primary" + style="display:none;position: absolute;margin-left: 20px;margin-top: -5px;">{{button.label}}</span><mat-icon class="{{button.icon}}"></mat-icon> + </button> + </smd-fab-actions> + </smd-fab-speed-dial> </span> </ng-container> diff --git a/src/frontend/app/menu/menu-shortcut.component.scss b/src/frontend/app/menu/menu-shortcut.component.scss index c93f31fa112..4afdbf7e348 100644 --- a/src/frontend/app/menu/menu-shortcut.component.scss +++ b/src/frontend/app/menu/menu-shortcut.component.scss @@ -8,17 +8,11 @@ width: 200px; text-align: right; z-index: 0; - text-shadow: 4px 4px 2px rgba(150, 150, 150, 1); + //text-shadow: 4px 4px 2px rgba(150, 150, 150, 1); + font-weight: bold; } -eco-fab-speed-dial:hover .speedDialLabel { - opacity: 1; - transition: opacity 1s ease-in; -} - - -eco-fab-speed-dial:not(hover) .speedDialLabel { - transition: opacity 1s ease-in; - opacity: 0; - z-index: -1; +.smd-opened .speedDialLabel { + transition: opacity 1s ease-in !important; + display: block !important; } diff --git a/src/frontend/plugins/fab-speed-dial/fab-speed-dial.scss b/src/frontend/plugins/fab-speed-dial/fab-speed-dial.scss new file mode 100644 index 00000000000..4df9b2386dd --- /dev/null +++ b/src/frontend/plugins/fab-speed-dial/fab-speed-dial.scss @@ -0,0 +1,160 @@ +@mixin smd-fab-speed-dial-container($box-orient, $flex-direction) { + -webkit-box-orient: $box-orient; + -webkit-box-direction: normal; + -webkit-flex-direction: $flex-direction; + flex-direction: $flex-direction; + } + + @mixin smd-fab-speed-dial-box-order($ordinal-group, $order) { + -webkit-box-ordinal-group: $ordinal-group; + -webkit-order: $order; + order: $order; + } + + @mixin smd-fab-speed-dial-actions($box-orient, $box-direction, $flex-direction, $ordinal-group, $order, $action-item-margin-direction) { + -webkit-box-orient: $box-orient; + -webkit-box-direction: $box-direction; + -webkit-flex-direction: $flex-direction; + flex-direction: $flex-direction; + + @include smd-fab-speed-dial-box-order($ordinal-group, $order); + + & .smd-fab-action-item { + margin-#{$action-item-margin-direction}: 10px; + } + } + + smd-fab-speed-dial { + position: absolute; + display: inline-block; + + &.smd-opened { + .smd-fab-speed-dial-container { + height:auto; + + smd-fab-trigger { + &.smd-spin { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } + } + smd-fab-actions { + height:auto; + } + } + } + + .smd-fab-speed-dial-container { + height:0px; + position: relative; + display: -webkit-box; + display: -webkit-flex; + display: flex; + -webkit-box-align: center; + -webkit-align-items: center; + align-items: center; + z-index: 20; + + smd-fab-trigger { + pointer-events: auto; + z-index: 24; + + &.smd-spin { + -webkit-transition: all .6s cubic-bezier(.4,0,.2,1); + transition: all .6s cubic-bezier(.4,0,.2,1); + } + } + + smd-fab-actions { + height:0px; + display: -webkit-box; + display: -webkit-flex; + display: flex; + } + } + + &.smd-fling { + .smd-fab-speed-dial-container { + smd-fab-actions { + & .smd-fab-action-item { + display: block; + opacity: 1; + -webkit-transition: all .3s cubic-bezier(.55, 0, .55, .2); + transition: all .3s cubic-bezier(.55, 0, .55, .2); + } + } + } + } + + &.smd-scale { + .smd-fab-speed-dial-container { + smd-fab-actions { + & .smd-fab-action-item { + -webkit-transform: scale(0); + transform: scale(0); + -webkit-transition: all .3s cubic-bezier(.55, 0, .55, .2); + transition: all .3s cubic-bezier(.55, 0, .55, .2); + -webkit-transition-duration: .14286s; + transition-duration: .14286s; + } + } + } + } + + &.smd-down { + .smd-fab-speed-dial-container { + @include smd-fab-speed-dial-container(vertical, column); + + & smd-fab-trigger { + @include smd-fab-speed-dial-box-order(2, 1); + } + + & smd-fab-actions { + @include smd-fab-speed-dial-actions(vertical, normal, column, 3, 2, top); + } + } + } + + &.smd-up { + .smd-fab-speed-dial-container { + @include smd-fab-speed-dial-container(vertical, column); + + & smd-fab-trigger { + @include smd-fab-speed-dial-box-order(3, 2); + } + + & smd-fab-actions { + @include smd-fab-speed-dial-actions(vertical, reverse, column-reverse, 2, 1, bottom); + } + } + } + + &.smd-left { + .smd-fab-speed-dial-container { + @include smd-fab-speed-dial-container(horizontal, row); + + & smd-fab-trigger { + @include smd-fab-speed-dial-box-order(3, 2); + } + + & smd-fab-actions { + @include smd-fab-speed-dial-actions(horizontal, normal, row-reverse, 2, 1, right); + } + } + } + + &.smd-right { + .smd-fab-speed-dial-container { + @include smd-fab-speed-dial-container(horizontal, row); + + & smd-fab-trigger { + @include smd-fab-speed-dial-box-order(2, 1); + } + + & smd-fab-actions { + @include smd-fab-speed-dial-actions(horizontal, normal, row, 3, 2, left); + } + } + } + + } \ No newline at end of file diff --git a/src/frontend/plugins/fab-speed-dial/fab-speed-dial.ts b/src/frontend/plugins/fab-speed-dial/fab-speed-dial.ts new file mode 100644 index 00000000000..cfbc97978ef --- /dev/null +++ b/src/frontend/plugins/fab-speed-dial/fab-speed-dial.ts @@ -0,0 +1,252 @@ + +import { + Component, + Input, + Output, + EventEmitter, + ViewEncapsulation, + AfterContentInit, + ElementRef, + Renderer, + Inject, + forwardRef, + ContentChildren, + QueryList, + ContentChild, + HostBinding, + HostListener +} from "@angular/core"; +import {MatButton} from "@angular/material"; + +const Z_INDEX_ITEM: number = 23; + +@Component({ + selector: 'smd-fab-trigger', + template: ` + <ng-content select="[md-fab], [mat-fab]"></ng-content> + ` +}) +export class SmdFabSpeedDialTrigger { + + /** + * Whether this trigger should spin (360dg) while opening the speed dial + */ + @HostBinding('class.smd-spin') + @Input() spin: boolean = false; + + constructor(@Inject(forwardRef(() => SmdFabSpeedDialComponent)) private _parent: SmdFabSpeedDialComponent) { + } + + @HostListener('click', ['$event']) + _onClick(event: any) { + if (!this._parent.fixed) { + this._parent.toggle(); + event.stopPropagation(); + } + } + +} + +@Component({ + selector: 'smd-fab-actions', + template: ` + <ng-content select="[md-mini-fab], [mat-mini-fab]"></ng-content> + ` +}) +export class SmdFabSpeedDialActions implements AfterContentInit { + + @ContentChildren(MatButton) _buttons: QueryList<MatButton>; + + constructor(@Inject(forwardRef(() => SmdFabSpeedDialComponent)) private _parent: SmdFabSpeedDialComponent, private renderer: Renderer) { + } + + ngAfterContentInit(): void { + this._buttons.changes.subscribe(() => { + this.initButtonStates(); + this._parent.setActionsVisibility(); + }); + + this.initButtonStates(); + } + + private initButtonStates() { + this._buttons.toArray().forEach((button, i) => { + this.renderer.setElementClass(button._getHostElement(), 'smd-fab-action-item', true); + this.changeElementStyle(button._getHostElement(), 'z-index', '' + (Z_INDEX_ITEM - i)); + }) + } + + show() { + if (this._buttons) { + this._buttons.toArray().forEach((button, i) => { + let transitionDelay = 0; + let transform; + if (this._parent.animationMode == 'scale') { + // Incremental transition delay of 65ms for each action button + transitionDelay = 3 + (65 * i); + transform = 'scale(1)'; + } else { + transform = this.getTranslateFunction('0'); + } + this.changeElementStyle(button._getHostElement(), 'transition-delay', transitionDelay + 'ms'); + this.changeElementStyle(button._getHostElement(), 'opacity', '1'); + this.changeElementStyle(button._getHostElement(), 'transform', transform); + }) + } + } + + hide() { + if (this._buttons) { + this._buttons.toArray().forEach((button, i) => { + let opacity = '1'; + let transitionDelay = 0; + let transform; + if (this._parent.animationMode == 'scale') { + transitionDelay = 3 - (65 * i); + transform = 'scale(0)'; + opacity = '0'; + } else { + transform = this.getTranslateFunction((55 * (i + 1) - (i * 5)) + 'px'); + } + this.changeElementStyle(button._getHostElement(), 'transition-delay', transitionDelay + 'ms'); + this.changeElementStyle(button._getHostElement(), 'opacity', opacity); + this.changeElementStyle(button._getHostElement(), 'transform', transform); + }) + } + } + + private getTranslateFunction(value: string) { + let dir = this._parent.direction; + let translateFn = (dir == 'up' || dir == 'down') ? 'translateY' : 'translateX'; + let sign = (dir == 'down' || dir == 'right') ? '-' : ''; + return translateFn + '(' + sign + value + ')'; + } + + private changeElementStyle(elem: any, style: string, value: string) { + // FIXME - Find a way to create a "wrapper" around the action button(s) provided by the user, so we don't change it's style tag + this.renderer.setElementStyle(elem, style, value); + } +} + +@Component({ + selector: 'smd-fab-speed-dial', + template: ` + <div class="smd-fab-speed-dial-container"> + <ng-content select="smd-fab-trigger"></ng-content> + <ng-content select="smd-fab-actions"></ng-content> + </div> + `, + styleUrls: ['fab-speed-dial.scss'], + encapsulation: ViewEncapsulation.None +}) +export class SmdFabSpeedDialComponent implements AfterContentInit { + private isInitialized: boolean = false; + private _direction: string = 'up'; + private _open: boolean = false; + private _animationMode: string = 'fling'; + + /** + * Whether this speed dial is fixed on screen (user cannot change it by clicking) + */ + @Input() fixed: boolean = false; + + /** + * Whether this speed dial is opened + */ + @HostBinding('class.smd-opened') + @Input() get open() { + return this._open; + } + + set open(open: boolean) { + let previousOpen = this._open; + this._open = open; + if (previousOpen != this._open) { + this.openChange.emit(this._open); + if (this.isInitialized) { + this.setActionsVisibility(); + } + } + } + + /** + * The direction of the speed dial. Can be 'up', 'down', 'left' or 'right' + */ + @Input() get direction() { + return this._direction; + } + + set direction(direction: string) { + let previousDir = this._direction; + this._direction = direction; + if (previousDir != this.direction) { + this._setElementClass(previousDir, false); + this._setElementClass(this.direction, true); + + if (this.isInitialized) { + this.setActionsVisibility(); + } + } + } + + /** + * The animation mode to open the speed dial. Can be 'fling' or 'scale' + */ + @Input() get animationMode() { + return this._animationMode; + } + + set animationMode(animationMode: string) { + let previousAnimationMode = this._animationMode; + this._animationMode = animationMode; + if (previousAnimationMode != this._animationMode) { + this._setElementClass(previousAnimationMode, false); + this._setElementClass(this.animationMode, true); + + if (this.isInitialized) { + // To start another detect lifecycle and force the "close" on the action buttons + Promise.resolve(null).then(() => this.open = false); + } + } + } + + @Output() openChange: EventEmitter<boolean> = new EventEmitter<boolean>(); + + @ContentChild(SmdFabSpeedDialActions) _childActions: SmdFabSpeedDialActions; + + constructor(private elementRef: ElementRef, private renderer: Renderer) { + } + + ngAfterContentInit(): void { + this.isInitialized = true; + this.setActionsVisibility(); + this._setElementClass(this.direction, true); + this._setElementClass(this.animationMode, true); + } + + /** + * Toggle the open state of this speed dial + */ + public toggle() { + this.open = !this.open; + } + + @HostListener('click') + _onClick() { + if (!this.fixed && this.open) { + this.open = false; + } + } + + setActionsVisibility() { + if (this.open) { + this._childActions.show(); + } else { + this._childActions.hide(); + } + } + + private _setElementClass(elemClass:string , isAdd:boolean) { + this.renderer.setElementClass(this.elementRef.nativeElement, `smd-${elemClass}`, isAdd); + } +} \ No newline at end of file diff --git a/src/frontend/plugins/fab-speed-dial/index.ts b/src/frontend/plugins/fab-speed-dial/index.ts new file mode 100644 index 00000000000..3be3c97c86e --- /dev/null +++ b/src/frontend/plugins/fab-speed-dial/index.ts @@ -0,0 +1 @@ +export * from './fab-speed-dial'; \ No newline at end of file -- GitLab