Skip to content

Commit

Permalink
fix(admin-ui): Add missing RTL compatibility to some admin-ui compone…
Browse files Browse the repository at this point in the history
…nts (#2451)
  • Loading branch information
HoseinGhanbari authored Oct 17, 2023
1 parent ed109b9 commit 96eb96e
Show file tree
Hide file tree
Showing 9 changed files with 171 additions and 73 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ import { I18nService } from '../../providers/i18n/i18n.service';
import { LocalStorageService } from '../../providers/local-storage/local-storage.service';
import { ModalService } from '../../providers/modal/modal.service';
import { UiLanguageSwitcherDialogComponent } from '../ui-language-switcher-dialog/ui-language-switcher-dialog.component';
import {
LocalizationDirectionType,
LocalizationLanguageCodeType,
LocalizationService,
} from '../../providers/localization/localization.service';

@Component({
selector: 'vdr-app-shell',
Expand All @@ -22,8 +27,8 @@ import { UiLanguageSwitcherDialogComponent } from '../ui-language-switcher-dialo
export class AppShellComponent implements OnInit {
version = ADMIN_UI_VERSION;
userName$: Observable<string>;
uiLanguageAndLocale$: Observable<[LanguageCode, string | undefined]>;
direction$: Observable<'ltr' | 'rtl'>;
uiLanguageAndLocale$: LocalizationLanguageCodeType;
direction$: LocalizationDirectionType;
availableLanguages: LanguageCode[] = [];
hideVendureBranding = getAppConfig().hideVendureBranding;
pageTitle$: Observable<string>;
Expand All @@ -38,25 +43,27 @@ export class AppShellComponent implements OnInit {
private modalService: ModalService,
private localStorageService: LocalStorageService,
private breadcrumbService: BreadcrumbService,
private localizationService: LocalizationService,
) {}

ngOnInit() {
this.direction$ = this.localizationService.direction$;

this.uiLanguageAndLocale$ = this.localizationService.uiLanguageAndLocale$;

this.userName$ = this.dataService.client
.userStatus()
.single$.pipe(map(data => data.userStatus.username));
this.uiLanguageAndLocale$ = this.dataService.client
.uiState()
.stream$.pipe(map(({ uiState }) => [uiState.language, uiState.locale ?? undefined]));

this.availableLanguages = this.i18nService.availableLanguages;

this.pageTitle$ = this.breadcrumbService.breadcrumbs$.pipe(
map(breadcrumbs => breadcrumbs[breadcrumbs.length - 1].label),
);

this.mainNavExpanded$ = this.dataService.client
.uiState()
.stream$.pipe(map(({ uiState }) => uiState.mainNavExpanded));
this.direction$ = this.uiLanguageAndLocale$.pipe(
map(([languageCode]) => (this.i18nService.isRTL(languageCode) ? 'rtl' : 'ltr')),
);
}

selectUiLanguage() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
<div
class="notification-wrapper"
#wrapper
[style.top.px]="offsetTop"
[ngClass]="{
visible: isVisible,
info: type === 'info',
success: type === 'success',
error: type === 'error',
warning: type === 'warning'
}"
>
<div [dir]="direction$ | async" class="notification-wrapper" #wrapper [style.top.px]="offsetTop" [ngClass]="{
visible: isVisible,
info: type === 'info',
success: type === 'success',
error: type === 'error',
warning: type === 'warning'
}">
<clr-icon [attr.shape]="getIcon()" size="24"></clr-icon>
{{ stringifyMessage(message) | translate: translationVars }}
</div>
</div>
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import { Component, ElementRef, HostListener, ViewChild } from '@angular/core';
import { Component, ElementRef, HostListener, OnInit, ViewChild } from '@angular/core';

import { NotificationType } from '../../providers/notification/notification.service';

import {
LocalizationDirectionType,
LocalizationService,
} from '../../providers/localization/localization.service';

@Component({
selector: 'vdr-notification',
templateUrl: './notification.component.html',
styleUrls: ['./notification.component.scss'],
})
export class NotificationComponent {
export class NotificationComponent implements OnInit {
direction$: LocalizationDirectionType;

@ViewChild('wrapper', { static: true }) wrapper: ElementRef;
offsetTop = 0;
message = '';
Expand All @@ -18,6 +25,15 @@ export class NotificationComponent {
/* */
};

/**
*
*/
constructor(private localizationService: LocalizationService) {}

ngOnInit(): void {
this.direction$ = this.localizationService.direction$;
}

registerOnClickFn(fn: () => void): void {
this.onClickFn = fn;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
:host {
display: flex;
justify-content: start;
justify-content: flex-start;
align-items: center;
}

Expand All @@ -27,10 +27,24 @@ button.theme-toggle {
left: 0px;
opacity: 1;
}

&.default.active {
color: #d6ae3f;
}

&.dark.active {
color: #ffdf3a;
}
}

:host-context([dir='rtl']) {
.theme-icon {
left: auto;
right: 6px;

&.active {
left: auto;
right: 0px;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { TestBed } from '@angular/core/testing';

import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { TestingCommonModule } from '../../../../../testing/testing-common.module';
import { MockI18nService } from '../i18n/i18n.service.mock';
import { DataService } from '../../data/providers/data.service';
import { I18nService } from '../../providers/i18n/i18n.service';
import { LocalizationService } from './localization.service';

describe('LocalizationService', () => {
let service: LocalizationService;

beforeEach(() => {
TestBed.configureTestingModule({
imports: [TestingCommonModule],
providers: [
LocalizationService,
{ provide: I18nService, useClass: MockI18nService },
{ provide: DataService, useClass: class {} },
],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
});
service = TestBed.inject(LocalizationService);
});

it('should be created', () => {
expect(service).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Injectable } from '@angular/core';
import { Observable, map } from 'rxjs';

import { DataService } from '../../data/providers/data.service';
import { I18nService } from '../../providers/i18n/i18n.service';
import { LanguageCode } from '../../common/generated-types';

export type LocalizationDirectionType = Observable<'ltr' | 'rtl'>;
export type LocalizationLanguageCodeType = Observable<[LanguageCode, string | undefined]>;

/**
* @description
* Provides localization helper functionality.
*
*/
@Injectable({
providedIn: 'root',
})
export class LocalizationService {
uiLanguageAndLocale$: LocalizationLanguageCodeType;
direction$: LocalizationDirectionType;

constructor(private i18nService: I18nService, private dataService: DataService) {
this.uiLanguageAndLocale$ = this.dataService.client
?.uiState()
?.stream$?.pipe(map(({ uiState }) => [uiState.language, uiState.locale ?? undefined]));

this.direction$ = this.uiLanguageAndLocale$?.pipe(
map(([languageCode]) => {
return this.i18nService.isRTL(languageCode) ? 'rtl' : 'ltr';
}),
);
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,9 @@
import {
ConnectedPosition,
HorizontalConnectionPos,
Overlay,
OverlayRef,
PositionStrategy,
VerticalConnectionPos,
} from '@angular/cdk/overlay';
import { ConnectedPosition, Overlay, OverlayRef, PositionStrategy } from '@angular/cdk/overlay';
import { TemplatePortal } from '@angular/cdk/portal';
import {
AfterViewInit,
ChangeDetectionStrategy,
Component,
ContentChild,
ElementRef,
HostListener,
Input,
OnDestroy,
Expand All @@ -23,7 +14,10 @@ import {
} from '@angular/core';
import { Subscription } from 'rxjs';

import { DropdownTriggerDirective } from './dropdown-trigger.directive';
import {
LocalizationDirectionType,
LocalizationService,
} from '../../../providers/localization/localization.service';
import { DropdownComponent } from './dropdown.component';

export type DropdownPosition = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
Expand All @@ -41,14 +35,16 @@ export type DropdownPosition = 'top-left' | 'top-right' | 'bottom-left' | 'botto
selector: 'vdr-dropdown-menu',
template: `
<ng-template #menu>
<div class="dropdown open">
<div class="dropdown-menu" [ngClass]="customClasses">
<div
class="dropdown-content-wrapper"
[cdkTrapFocus]="true"
[cdkTrapFocusAutoCapture]="true"
>
<ng-content></ng-content>
<div [dir]="direction$ | async">
<div class="dropdown open">
<div class="dropdown-menu" [ngClass]="customClasses">
<div
class="dropdown-content-wrapper"
[cdkTrapFocus]="true"
[cdkTrapFocusAutoCapture]="true"
>
<ng-content></ng-content>
</div>
</div>
</div>
</div>
Expand All @@ -58,6 +54,8 @@ export type DropdownPosition = 'top-left' | 'top-right' | 'bottom-left' | 'botto
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DropdownMenuComponent implements AfterViewInit, OnInit, OnDestroy {
direction$: LocalizationDirectionType;

@Input('vdrPosition') private position: DropdownPosition = 'bottom-left';
@Input() customClasses: string;
@ViewChild('menu', { static: true }) private menuTemplate: TemplateRef<any>;
Expand Down Expand Up @@ -104,9 +102,12 @@ export class DropdownMenuComponent implements AfterViewInit, OnInit, OnDestroy {
private overlay: Overlay,
private viewContainerRef: ViewContainerRef,
private dropdown: DropdownComponent,
private localizationService: LocalizationService,
) {}

ngOnInit(): void {
this.direction$ = this.localizationService.direction$;

this.dropdown.onOpenChange(isOpen => {
if (isOpen) {
this.overlayRef.attach(this.menuPortal);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
<clr-modal
[clrModalOpen]="true"
(clrModalOpenChange)="modalOpenChange($event)"
[clrModalClosable]="options?.closable"
[clrModalSize]="options?.size"
[ngClass]="'modal-valign-' + (options?.verticalAlign || 'center')"
>
<h3 class="modal-title"><ng-container *ngTemplateOutlet="(titleTemplateRef$ | async)"></ng-container></h3>
<div class="modal-body">
<vdr-dialog-component-outlet
[component]="childComponentType"
(create)="onCreate($event)"
></vdr-dialog-component-outlet>
</div>
<div class="modal-footer">
<ng-container *ngTemplateOutlet="(buttonsTemplateRef$ | async)"></ng-container>
</div>
</clr-modal>
<div [dir]="direction$ | async">
<clr-modal [clrModalOpen]="true" (clrModalOpenChange)="modalOpenChange($event)"
[clrModalClosable]="options?.closable" [clrModalSize]="options?.size"
[ngClass]="'modal-valign-' + (options?.verticalAlign || 'center')">
<h3 class="modal-title"><ng-container *ngTemplateOutlet="(titleTemplateRef$ | async)"></ng-container></h3>
<div class="modal-body">
<vdr-dialog-component-outlet [component]="childComponentType"
(create)="onCreate($event)"></vdr-dialog-component-outlet>
</div>
<div class="modal-footer">
<ng-container *ngTemplateOutlet="(buttonsTemplateRef$ | async)"></ng-container>
</div>
</clr-modal>
</div>
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import {
Component,
ContentChild,
ContentChildren,
QueryList,
TemplateRef,
Type,
ViewChild,
ViewChildren,
} from '@angular/core';
import { Observable, Subject } from 'rxjs';
import { Component, OnInit, TemplateRef, Type } from '@angular/core';
import { Subject } from 'rxjs';

import {
LocalizationDirectionType,
LocalizationService,
} from '../../../providers/localization/localization.service';
import { Dialog, ModalOptions } from '../../../providers/modal/modal.types';

import { DialogButtonsDirective } from './dialog-buttons.directive';
Expand All @@ -23,13 +18,24 @@ import { DialogButtonsDirective } from './dialog-buttons.directive';
templateUrl: './modal-dialog.component.html',
styleUrls: ['./modal-dialog.component.scss'],
})
export class ModalDialogComponent<T extends Dialog<any>> {
export class ModalDialogComponent<T extends Dialog<any>> implements OnInit {
direction$: LocalizationDirectionType;

childComponentType: Type<T>;
closeModal: (result?: any) => void;
titleTemplateRef$ = new Subject<TemplateRef<any>>();
buttonsTemplateRef$ = new Subject<TemplateRef<any>>();
options?: ModalOptions<T>;

/**
*
*/
constructor(private localizationService: LocalizationService) {}

ngOnInit(): void {
this.direction$ = this.localizationService.direction$;
}

/**
* This callback is invoked when the childComponentType is instantiated in the
* template by the {@link DialogComponentOutletComponent}.
Expand Down

0 comments on commit 96eb96e

Please sign in to comment.