From 77cbc9311ceeae31233ef7cc2244ddc7962fd25e Mon Sep 17 00:00:00 2001 From: Valk Dokk Date: Tue, 15 Oct 2024 13:02:35 +0700 Subject: [PATCH 1/2] feat: Add floating button --- .../app-shell/app-shell.component.html | 5 +- .../floating-button.component.html | 26 +++++++ .../floating-button.component.scss | 32 ++++++++ .../floating-button.component.ts | 76 +++++++++++++++++++ .../admin-ui/src/lib/core/src/core.module.ts | 2 + .../core/src/extension/add-floating-button.ts | 16 ++++ .../floating-button/floating-button-types.ts | 19 +++++ .../floating-button.service.ts | 27 +++++++ .../admin-ui/src/lib/core/src/public_api.ts | 4 + 9 files changed, 204 insertions(+), 3 deletions(-) create mode 100644 packages/admin-ui/src/lib/core/src/components/floating-button/floating-button.component.html create mode 100644 packages/admin-ui/src/lib/core/src/components/floating-button/floating-button.component.scss create mode 100644 packages/admin-ui/src/lib/core/src/components/floating-button/floating-button.component.ts create mode 100644 packages/admin-ui/src/lib/core/src/extension/add-floating-button.ts create mode 100644 packages/admin-ui/src/lib/core/src/providers/floating-button/floating-button-types.ts create mode 100644 packages/admin-ui/src/lib/core/src/providers/floating-button/floating-button.service.ts diff --git a/packages/admin-ui/src/lib/core/src/components/app-shell/app-shell.component.html b/packages/admin-ui/src/lib/core/src/components/app-shell/app-shell.component.html index 0c674049d2..642ac57776 100644 --- a/packages/admin-ui/src/lib/core/src/components/app-shell/app-shell.component.html +++ b/packages/admin-ui/src/lib/core/src/components/app-shell/app-shell.component.html @@ -21,9 +21,7 @@
-
- v{{ version }} -
+
v{{ version }}
+ diff --git a/packages/admin-ui/src/lib/core/src/components/floating-button/floating-button.component.html b/packages/admin-ui/src/lib/core/src/components/floating-button/floating-button.component.html new file mode 100644 index 0000000000..413aba8842 --- /dev/null +++ b/packages/admin-ui/src/lib/core/src/components/floating-button/floating-button.component.html @@ -0,0 +1,26 @@ +
+ +
diff --git a/packages/admin-ui/src/lib/core/src/components/floating-button/floating-button.component.scss b/packages/admin-ui/src/lib/core/src/components/floating-button/floating-button.component.scss new file mode 100644 index 0000000000..a72d70aa38 --- /dev/null +++ b/packages/admin-ui/src/lib/core/src/components/floating-button/floating-button.component.scss @@ -0,0 +1,32 @@ +.floating-button-container { + position: fixed; + bottom: 20px; + right: 20px; + display: flex; + flex-direction: row; + gap: 10px; + z-index: 1000; +} + +.floating-button { + position: relative; + bottom: auto; + right: auto; + background-color: #2e83b2; + color: white; + border: none; + border-radius: 50%; + width: 60px; + height: 60px; + font-size: 24px; + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1); + cursor: pointer; + z-index: 1000; +} + +.floating-button:hover { + background-color: #1e5e7f; +} diff --git a/packages/admin-ui/src/lib/core/src/components/floating-button/floating-button.component.ts b/packages/admin-ui/src/lib/core/src/components/floating-button/floating-button.component.ts new file mode 100644 index 0000000000..d29858ab62 --- /dev/null +++ b/packages/admin-ui/src/lib/core/src/components/floating-button/floating-button.component.ts @@ -0,0 +1,76 @@ +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; +import { Subscription, take } from 'rxjs'; +import { NavigationEnd, Router } from '@angular/router'; +import { FloatingButton, ModalService } from '@vendure/admin-ui/core'; +import { FloatingButtonService } from '../../providers/floating-button/floating-button.service'; + +@Component({ + selector: 'vdr-floating-button', + templateUrl: './floating-button.component.html', + styleUrl: './floating-button.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class FloatingButtonComponent implements OnInit, OnDestroy { + constructor( + private floatingButtonService: FloatingButtonService, + private router: Router, + private cdr: ChangeDetectorRef, + ) {} + floatingButtons: FloatingButton[] = []; + private routerSubscription: Subscription; + + handleClick(button: FloatingButton) { + this.floatingButtonService.handleClick(button); + } + + shouldShowButton(button: FloatingButton): boolean { + if (!button.routes || button.routes.length === 0 || button.routes === '*') { + return true; + } + + const routes = Array.isArray(button.routes) ? button.routes : [button.routes]; + const currentRoute = this.router.url; + const result = routes.some(route => { + if (route === '*') { + return true; + } + const regexPattern = route + .replace(/\./g, '\\.') + .replace(/\-/g, '\\-') + .replace(/\*/g, '.*') + .replace(/\?/g, '.') + .replace(/\+/g, '.+') + .replace(/\(/g, '\\(') + .replace(/\)/g, '\\)'); + + const pattern = new RegExp('.*' + regexPattern + '$'); + return pattern.test(currentRoute); + }); + return result; + } + + ngOnInit() { + this.floatingButtonService.getFloatingButtons().subscribe(buttons => { + this.floatingButtons = buttons; + this.updateButtonVisibility(); + }); + this.routerSubscription = this.router.events.subscribe(event => { + if (event instanceof NavigationEnd) { + this.updateButtonVisibility(); + } + }); + } + + ngOnDestroy() { + if (this.routerSubscription) { + this.routerSubscription.unsubscribe(); + } + } + + private updateButtonVisibility() { + this.floatingButtons.forEach(button => { + button.visible = this.shouldShowButton(button); + }); + this.cdr.detectChanges(); + } +} diff --git a/packages/admin-ui/src/lib/core/src/core.module.ts b/packages/admin-ui/src/lib/core/src/core.module.ts index 0811b1f8a7..90917c0dc9 100644 --- a/packages/admin-ui/src/lib/core/src/core.module.ts +++ b/packages/admin-ui/src/lib/core/src/core.module.ts @@ -30,6 +30,7 @@ import { LocalStorageService } from './providers/local-storage/local-storage.ser import { Permission } from './public_api'; import { registerDefaultFormInputs } from './shared/dynamic-form-inputs/default-form-inputs'; import { SharedModule } from './shared/shared.module'; +import { FloatingButtonComponent } from './components/floating-button/floating-button.component'; @NgModule({ imports: [ @@ -53,6 +54,7 @@ import { SharedModule } from './shared/shared.module'; AppShellComponent, UserMenuComponent, BaseNavComponent, + FloatingButtonComponent, MainNavComponent, SettingsNavComponent, BreadcrumbComponent, diff --git a/packages/admin-ui/src/lib/core/src/extension/add-floating-button.ts b/packages/admin-ui/src/lib/core/src/extension/add-floating-button.ts new file mode 100644 index 0000000000..ee79974ba8 --- /dev/null +++ b/packages/admin-ui/src/lib/core/src/extension/add-floating-button.ts @@ -0,0 +1,16 @@ +import { Provider } from '@angular/core'; +import { APP_INITIALIZER } from '@angular/core'; +import { FloatingButton } from '../providers/floating-button/floating-button-types'; + +import { FloatingButtonService } from '../providers/floating-button/floating-button.service'; + +export function addFloatingButton(config: Omit): Provider { + return { + provide: APP_INITIALIZER, + multi: true, + useFactory: (floatingButtonService: FloatingButtonService) => () => { + floatingButtonService.addFloatingButton(config); + }, + deps: [FloatingButtonService], + }; +} diff --git a/packages/admin-ui/src/lib/core/src/providers/floating-button/floating-button-types.ts b/packages/admin-ui/src/lib/core/src/providers/floating-button/floating-button-types.ts new file mode 100644 index 0000000000..cc64f95c42 --- /dev/null +++ b/packages/admin-ui/src/lib/core/src/providers/floating-button/floating-button-types.ts @@ -0,0 +1,19 @@ +import { Injector } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; + +export interface FloatingButtonFunctionContext { + injector: Injector; +} + +export interface FloatingButton { + onClick: (injector: Injector) => void; + routes?: string[] | string; + requiresPermission?: string | ((userPermissions: string[]) => boolean); + disabled?: boolean; + icon?: string; + iconSize?: number; + iconColor?: string; + buttonSize?: number; + backgroundColor?: string; + visible?: boolean; +} diff --git a/packages/admin-ui/src/lib/core/src/providers/floating-button/floating-button.service.ts b/packages/admin-ui/src/lib/core/src/providers/floating-button/floating-button.service.ts new file mode 100644 index 0000000000..fcc8f8d7b0 --- /dev/null +++ b/packages/admin-ui/src/lib/core/src/providers/floating-button/floating-button.service.ts @@ -0,0 +1,27 @@ +import { Injectable, Injector } from '@angular/core'; +import { Observable, of } from 'rxjs'; +import { ActivatedRoute } from '@angular/router'; +import { DataService } from '../../data/providers/data.service'; +import { FloatingButton } from './floating-button-types'; +@Injectable({ + providedIn: 'root', +}) +export class FloatingButtonService { + constructor( + private injector: Injector, + private dataService: DataService, + ) {} + + handleClick(button: FloatingButton) { + button.onClick(this.injector); + } + + getFloatingButtons(): Observable { + return of(this.addedFloatingButtons); + } + private addedFloatingButtons: FloatingButton[] = []; + + addFloatingButton(config: Omit) { + this.addedFloatingButtons.push(config); + } +} diff --git a/packages/admin-ui/src/lib/core/src/public_api.ts b/packages/admin-ui/src/lib/core/src/public_api.ts index 6ce4730f4e..c8129f0850 100644 --- a/packages/admin-ui/src/lib/core/src/public_api.ts +++ b/packages/admin-ui/src/lib/core/src/public_api.ts @@ -28,6 +28,7 @@ export * from './components/app-shell/app-shell.component'; export * from './components/base-nav/base-nav.component'; export * from './components/breadcrumb/breadcrumb.component'; export * from './components/channel-switcher/channel-switcher.component'; +export * from './components/floating-button/floating-button.component'; export * from './components/main-nav/main-nav.component'; export * from './components/notification/notification.component'; export * from './components/overlay-host/overlay-host.component'; @@ -77,6 +78,7 @@ export * from './data/utils/remove-readonly-custom-fields'; export * from './data/utils/transform-relation-custom-field-inputs'; export * from './extension/add-action-bar-dropdown-menu-item'; export * from './extension/add-action-bar-item'; +export * from './extension/add-floating-button'; export * from './extension/add-nav-menu-item'; export * from './extension/components/angular-route.component'; export * from './extension/components/route.component'; @@ -111,6 +113,8 @@ export * from './providers/data-table/data-table-filter-collection'; export * from './providers/data-table/data-table-filter'; export * from './providers/data-table/data-table-sort-collection'; export * from './providers/data-table/data-table-sort'; +export * from './providers/floating-button/floating-button-types'; +export * from './providers/floating-button/floating-button.service'; export * from './providers/guard/auth.guard'; export * from './providers/health-check/health-check.service'; export * from './providers/i18n/custom-http-loader'; From 27b4c8bad96be65bedc899c6d188be7f3bb04033 Mon Sep 17 00:00:00 2001 From: Valk Dokk Date: Tue, 15 Oct 2024 14:38:00 +0700 Subject: [PATCH 2/2] fix: Fix import causing Circular Dependency error --- .../src/components/floating-button/floating-button.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/admin-ui/src/lib/core/src/components/floating-button/floating-button.component.ts b/packages/admin-ui/src/lib/core/src/components/floating-button/floating-button.component.ts index d29858ab62..2f91bc45ed 100644 --- a/packages/admin-ui/src/lib/core/src/components/floating-button/floating-button.component.ts +++ b/packages/admin-ui/src/lib/core/src/components/floating-button/floating-button.component.ts @@ -1,7 +1,7 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; import { Subscription, take } from 'rxjs'; import { NavigationEnd, Router } from '@angular/router'; -import { FloatingButton, ModalService } from '@vendure/admin-ui/core'; +import { FloatingButton } from '../../providers/floating-button/floating-button-types'; import { FloatingButtonService } from '../../providers/floating-button/floating-button.service'; @Component({