Skip to content

Commit

Permalink
add support for RTL
Browse files Browse the repository at this point in the history
  • Loading branch information
dianabarsan committed Dec 19, 2024
1 parent 58a5e7d commit 445cfe3
Show file tree
Hide file tree
Showing 18 changed files with 197 additions and 12 deletions.
1 change: 1 addition & 0 deletions admin/src/js/controllers/edit-language.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ angular.module('controllers').controller('EditLanguageCtrl',

$scope.language = _.clone($scope.model) || {
enabled: true,
rtl: false,
generic: {},
custom: {},
type: 'translations'
Expand Down
2 changes: 1 addition & 1 deletion admin/src/templates/display_languages.html
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ <h4 class="panel-title">
<div class="alert alert-warning" role="alert" ng-show="{{localeModel.missing > 0}}" translate translate-values="localeModel">Missing translations</div>
<div>
<button type="button" class="btn btn-link" ng-click="editLanguage(localeModel.doc)">
<i class="fa fa-pencil"></i><span translate>edit.name</span>
<i class="fa fa-pencil"></i><span translate>edit</span>
</button>
</div>
<div ng-if="localeModel.enabled">
Expand Down
7 changes: 7 additions & 0 deletions admin/src/templates/edit_language.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,12 @@
<span class="help-block" translate>Language name help</span>
</div>

<div class="form-group" ng-class="{'has-error': errors.rtl}">
<label translate>RTL</label>
<input type="checkbox" ng-model="language.rtl" />
<span class="error" ng-show="errors.rtl">{{errors.rtl}}</span>
<span class="help-block" translate>Language RTL help</span>
</div>

</form>
</mm-modal>
5 changes: 3 additions & 2 deletions api/resources/translations/messages-en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ Language\ For\ Outgoing\ Messages = Language for outgoing messages
Language\ code = Language code
Language\ code\ help = The 2 or 3 digit code for the language following the
Language\ name\ help = The display name for the language.
Language\ RTL\ help = Whether the language should enable Right-To-Left writing UI.
Language\ to\ edit = Language to edit
Languages = Languages
Last\ Appointment = Last appointment
Expand Down Expand Up @@ -654,8 +655,8 @@ enketo.geopicker.altitude = altitude (m)
enketo.geopicker.closepolygon = close polygon
enketo.geopicker.kmlcoords = KML coordinates
enketo.geopicker.kmlpaste = paste KML coordinates here
enketo.geopicker.latitude = latitude (x.y °)
enketo.geopicker.longitude = longitude (x.y °)
enketo.geopicker.latitude = latitude (x.y )
enketo.geopicker.longitude = longitude (x.y )
enketo.geopicker.points = points
enketo.geopicker.searchPlaceholder = search for place or address
enketo.geopicker.removePoint = This will completely remove the current geopoint from the list of geopoints and cannot be undone. Are you sure you want to do this?
Expand Down
3 changes: 2 additions & 1 deletion webapp/src/css/inbox.less
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
@import 'error-log';
@import 'tooltip';
@import 'old-nav';
@import 'rtl';

.container-fluid {
overflow: hidden;
Expand Down Expand Up @@ -1918,4 +1919,4 @@ mm-sidebar-menu .mat-sidenav-container {
.desktop-only {
display: none;
}
}
}
103 changes: 103 additions & 0 deletions webapp/src/css/rtl.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
@import "variables";

.app-root:not(.old-nav).rtl {
direction: rtl;
* {
direction: rtl;
}

mm-header {
right: 0;
left: auto;
}

.content .page {
padding-right: 80px;
padding-left: 0;
}

.left-pane {
float: right;
}

.fast-action-trigger:not(.embed-fast-action) {
top: 60px;
left: 5px;
right: auto;
}

.content-row {
border-left: 0;
border-right: 5px solid transparent;

a {
padding: 10px 8px 10px 20px;
.icon {
margin: 5px 0 5px 7px;
}
}

input[type="checkbox"] {
float: right;
margin: 20px 10px 20px 7px;
}
}

&.reports .content-row:hover {
border-right-color: @reports-color;
}

.select-mode-available .select-all .select-all-label {
margin-right: 15px;
}

.item-summary .icon {
float: right;
margin-left: 4px;
margin-right: 0;
}

.sidebar-filter .sidebar-main .sidebar-header .sidebar-reset {
margin-right: 0;
margin-left: 20px;
}

.sidebar-filter .sidebar-main .sidebar-body mat-expansion-panel mat-expansion-panel-header .chip {
margin-right: 10px;
}

mm-search-bar .mm-search-bar-container .btn.open-filter .open-filter-label {
margin-left: 6px;
}

.item-content .deselect {
float: left;
clear: left;
margin-right: -10px;
}

&.contacts .action-header {
h3 {
float: right;
}
.table-filter {
float: left;
}
}

.targets .target .heading .icon {
margin-left: 10px;
margin-right: 0;
}

.enketo .or h2, .enketo .or h3, .enketo .or h4 {
text-align: right;
}
}

[dir="rtl"] {
.fast-action-body .fast-action-item .fast-action-item-icon {
margin-left: 20px;
margin-right: 0;
}
}
5 changes: 5 additions & 0 deletions webapp/src/ts/actions/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export const Actions = {
setSearchBar: createSingleValueAction('SET_SEARCH_BAR', 'searchBar'),
setTrainingCard: createSingleValueAction('SET_TRAINING_CARD', 'trainingCard'),
clearTrainingCards: createAction('CLEAR_TRAINING_CARDS'),
setLanguage: createSingleValueAction('SET_LANGUAGE', 'language'),
};

export class GlobalActions {
Expand Down Expand Up @@ -244,4 +245,8 @@ export class GlobalActions {
return this.store.dispatch(Actions.closeSidebarMenu());
}

setLanguage(language) {
return this.store.dispatch(Actions.setLanguage(language));
}

}
5 changes: 4 additions & 1 deletion webapp/src/ts/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
[class.select-mode]="selectMode"
[class.sidebar-filter-active]="isSidebarFilterOpen"
[class.search-bar-active]="openSearch"
[class.old-nav]="hasOldNav">
[class.old-nav]="hasOldNav"
[class.rtl]="direction === 'rtl'"
[dir]="direction"
>

<div class="bootstrap-layer">
<div>
Expand Down
5 changes: 5 additions & 0 deletions webapp/src/ts/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import { BrowserCompatibilityComponent } from '@mm-modals/browser-compatibility/
import { PerformanceService } from '@mm-services/performance.service';
import { UserSettings, UserSettingsService } from '@mm-services/user-settings.service';
import { OLD_NAV_PERMISSION } from '@mm-components/header/header.component';
import { Directionality } from '@angular/cdk/bidi';

const SYNC_STATUS = {
inProgress: {
Expand Down Expand Up @@ -95,6 +96,7 @@ export class AppComponent implements OnInit, AfterViewInit {
androidAppVersion;
hasOldNav = false;
initialisationComplete = false;
direction;
private readonly SVG_ICONS = new Map([
['icon-close', './img/icon-close.svg'],
['icon-filter', './img/icon-filter.svg'],
Expand Down Expand Up @@ -463,18 +465,21 @@ export class AppComponent implements OnInit, AfterViewInit {
this.store.select(Selectors.getCurrentTab),
this.store.select(Selectors.getSelectMode),
this.store.select(Selectors.getSearchBar),
this.store.select(Selectors.getDirection),
]).subscribe(([
replicationStatus,
androidAppVersion,
currentTab,
selectMode,
searchBar,
direction,
]) => {
this.replicationStatus = replicationStatus;
this.androidAppVersion = androidAppVersion;
this.currentTab = currentTab || '';
this.selectMode = selectMode;
this.openSearch = !!searchBar?.isOpen;
this.direction = direction;
});

combineLatest([
Expand Down
6 changes: 4 additions & 2 deletions webapp/src/ts/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { GlobalEffects } from '@mm-effects/global.effects';
import { ReportsEffects } from '@mm-effects/reports.effects';
import { ContactsEffects } from '@mm-effects/contacts.effects';
import { reducers } from '@mm-reducers/index';
import { LanguageService } from '@mm-services/language.service';

const logger = reducer => {
// default, no options
Expand Down Expand Up @@ -74,8 +75,9 @@ export class MissingTranslationHandlerLog implements MissingTranslationHandler {
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: (db: DbService) => new TranslationLoaderProvider(db),
deps: [DbService],
useFactory:
(db: DbService, languageService: LanguageService) => new TranslationLoaderProvider(db, languageService),
deps: [DbService, LanguageService],
},
missingTranslationHandler: {
provide: MissingTranslationHandler,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,19 @@ export class FastActionButtonComponent implements OnInit, OnDestroy {
iconTypeResource = IconType.RESOURCE;
iconTypeFontAwesome = IconType.FONT_AWESOME;
buttonTypeFlat = ButtonType.FLAT;
direction;

constructor(
private store: Store,
private router: Router,
private responsiveService: ResponsiveService,
private matBottomSheet: MatBottomSheet,
private matDialog: MatDialog,
) { }
) {
this.store.select(Selectors.getDirection).subscribe(direction => {
this.direction = direction;
});
}

ngOnInit() {
this.subscribeToStore();
Expand Down Expand Up @@ -87,6 +92,7 @@ export class FastActionButtonComponent implements OnInit, OnDestroy {
autoFocus: false,
minWidth: 300,
minHeight: 150,
direction: this.direction,
});
}

Expand Down
4 changes: 4 additions & 0 deletions webapp/src/ts/modules/reports/reports.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { XmlFormsService } from '@mm-services/xml-forms.service';
import { PerformanceService } from '@mm-services/performance.service';
import { ExtractLineageService } from '@mm-services/extract-lineage.service';
import { ButtonType } from '@mm-components/fast-action-button/fast-action-button.component';
import { Directionality } from '@angular/cdk/bidi';

const PAGE_SIZE = 50;
const CAN_DEFAULT_FACILITY_FILTER = 'can_default_facility_filter';
Expand Down Expand Up @@ -65,6 +66,7 @@ export class ReportsComponent implements OnInit, AfterViewInit, OnDestroy {
userParentPlace;
fastActionList?: FastAction[];
userLineageLevel;
isRtl;

LIMIT_SELECT_ALL_REPORTS = 500;

Expand All @@ -88,9 +90,11 @@ export class ReportsComponent implements OnInit, AfterViewInit, OnDestroy {
private xmlFormsService:XmlFormsService,
private performanceService: PerformanceService,
private extractLineageService: ExtractLineageService,
dir: Directionality,
) {
this.globalActions = new GlobalActions(store);
this.reportsActions = new ReportsActions(store);
this.isRtl = dir.value === 'rtl';
}

ngOnInit() {
Expand Down
9 changes: 7 additions & 2 deletions webapp/src/ts/providers/translation-loader.provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@ import { from } from 'rxjs';
import { DbService } from '@mm-services/db.service';
import * as translationUtils from '@medic/translation-utils';
import { TranslationDocsMatcherProvider } from '@mm-providers/translation-docs-matcher.provider';
import { LanguageService } from '@mm-services/language.service';

@Injectable()
export class TranslationLoaderProvider implements TranslateLoader {
constructor(private db:DbService) {}
constructor(
private db: DbService,
private languageService: LanguageService
) {}

private loadingPromises = {};

Expand Down Expand Up @@ -38,11 +42,12 @@ export class TranslationLoaderProvider implements TranslateLoader {
const promise = this.db
.get()
.get(translationsDocId)
.then(doc => {
.then((doc:{ generic:{}; custom:{}; rtl: boolean }) => {
const values = Object.assign(doc.generic || {}, doc.custom || {});
if (testing) {
mapTesting(values);
}
doc.rtl && this.languageService.setRtlLanguage(locale);
return translationUtils.loadTranslations(values);
})
.catch(err => {
Expand Down
5 changes: 5 additions & 0 deletions webapp/src/ts/reducers/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const initialState: GlobalState = {
translationsLoaded: false,
userFacilityIds: [],
userContactId: null,
language: null,
};

const setShowContent = (state, showContent) => {
Expand Down Expand Up @@ -177,6 +178,9 @@ const _globalReducer = createReducer(
on(Actions.setTrainingCard, (state, { payload: { trainingCard } }) => {
return { ...state, trainingCard: { ...state.trainingCard, ...trainingCard } };
}),
on(Actions.setLanguage, (state, { payload: { language } }) => {
return { ...state, language };
}),
);

export const globalReducer = (state, action) => {
Expand Down Expand Up @@ -211,6 +215,7 @@ export interface GlobalState {
translationsLoaded: boolean;
userFacilityIds: null | string[];
userContactId: null | string;
language: null | Record<string, any>;
}

interface SidebarMenuState {
Expand Down
2 changes: 2 additions & 0 deletions webapp/src/ts/selectors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ export const Selectors = {
getUserContactId: createSelector(getGlobalState, (globalState) => globalState.userContactId),
getTrainingCardFormId: createSelector(getGlobalState, (globalState) => globalState.trainingCard?.formId),
getTrainingCard: createSelector(getGlobalState, (globalState) => globalState.trainingCard),
getLanguage: createSelector(getGlobalState, (globalState) => globalState.language),
getDirection: createSelector(getGlobalState, (globalState) => globalState.language?.rtl ? 'rtl' : 'ltr'),

// enketo
getEnketoStatus: createSelector(getGlobalState, (globalState) => globalState.enketoStatus),
Expand Down
Loading

0 comments on commit 445cfe3

Please sign in to comment.