Skip to content

Commit

Permalink
feat(theme): add RTL support (#343)
Browse files Browse the repository at this point in the history
  • Loading branch information
yggg authored and nnixaa committed May 8, 2018
1 parent 2f27060 commit 0326c1c
Show file tree
Hide file tree
Showing 58 changed files with 905 additions and 166 deletions.
1 change: 1 addition & 0 deletions DEV_DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
- for the override styles - registered in a list of overrides
- component *.theme registered in a list of component themes
- looks great on all default themes
- supports bidirectionality
- requires approval from several core team contributors

# Objectives
Expand Down
48 changes: 48 additions & 0 deletions docs/articles/rtl.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
All Nebular components support RTL out of the box.

The components that accept a position as a setting now also support logical start and end values, similar to flexbox. Value of start and end depends on current layout direction. For LTR it's left and for RTL - right.
For instance, if we need the sidebar to be positioned logically depending on a language direction, then instead of setting it to left we can set its position to start:

```html
<nb-sidebar start></nb-sidebar>
```

Document direction could be set through the `NbThemeModule.forRoot` call. Supported values are `ltr` and `rtl`.

```typescript
@NgModule({
imports: [
NbThemeModule.forRoot(nbThemeOptions, nbJSThemes, nbMediaBreakpoints, 'rtl')
]
})
```
Default value is `ltr`.

<div class="note note-info">
<div class="note-title">Note</div>
<div class="note-body">
Components must be placed inside of the `<nb-layout></nb-layout>` component to correctly support language direction.
</div>
</div>

To help you add RTL support to your custom components, Nebular provides you with two scss mixins: `nb-lrt` and `nb-rtl`. You can use them to alter values of css properties, which don't support logical values, like paddings, margins, etc. You can pass single property and value as arguments, pass multiple statements as a content of mixin or both. For example:
```scss
:host {
nb-ltr(padding-left, 1em);
nb-rtl(padding-right, 1em);
}
```
```scss
:host {
nb-ltr() {
padding-left: 1em;
};
nb-rtl() {
padding-right: 1em;
};
}
```

Please note, the mixins are only available within component `:host` selector or `nb-install-component()` mixin if used.

If you need to change direction dynamically, get current value or listen to changes of direction, Nebular provides `NbLayoutDirectionService`.
22 changes: 22 additions & 0 deletions docs/structure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -637,4 +637,26 @@ export const STRUCTURE = [
},
],
},
{
type: 'section',
name: 'Usability',
children: [
{
type: 'page',
name: 'Right-to-left (RTL)',
children: [
{
type: 'block',
block: 'markdown',
source: 'rtl.md',
},
{
type: 'block',
block: 'component',
source: 'NbLayoutDirectionService',
},
],
},
],
},
];
18 changes: 12 additions & 6 deletions e2e/layout.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe('nb-layout', () => {
});
});

it('should render left with order: 1', () => {
it('should render left with order: 0', () => {
element
.all(
by.css(`#layout-fluid >
Expand All @@ -54,7 +54,7 @@ describe('nb-layout', () => {
.get(0)
.getCssValue('order')
.then(value => {
expect(value).toMatch('1');
expect(value).toMatch('0');
});
});

Expand All @@ -71,7 +71,7 @@ describe('nb-layout', () => {
});
});

it('should render center with order: 2', () => {
it('should render center with order: 1', () => {
element
.all(
by.css(`#layout-fluid >
Expand All @@ -80,7 +80,7 @@ describe('nb-layout', () => {
.get(1)
.getCssValue('order')
.then(value => {
expect(value).toMatch('2');
expect(value).toMatch('1');
});
});

Expand All @@ -97,7 +97,7 @@ describe('nb-layout', () => {
});
});

it('should render right with order: 2', () => {
it('should render right with order: 1', () => {
element
.all(
by.css(`#layout-fluid >
Expand All @@ -106,7 +106,7 @@ describe('nb-layout', () => {
.get(2)
.getCssValue('order')
.then(value => {
expect(value).toMatch('2');
expect(value).toMatch('1');
});
});

Expand Down Expand Up @@ -155,4 +155,10 @@ describe('nb-layout', () => {
})
});
});

it('should set default document direction', () => {
browser.executeScript('return document.dir').then(function (direction) {
expect(direction).toBe('ltr');
});
});
});
5 changes: 4 additions & 1 deletion src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import 'style-loader!./app.themes.scss';

@Component({
selector: 'nb-app-root',
template: `<router-outlet></router-outlet>`,
template: `
<nb-layout-direction-toggle></nb-layout-direction-toggle>
<router-outlet></router-outlet>
`,
})
export class NbAppComponent {
}
2 changes: 2 additions & 0 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ import { AuthGuard } from './auth-guard.service';
import { RoleProvider } from './role.provider';
import { NbDynamicToAddComponent } from './dynamic.component';
import { NbPlaygroundModule } from '../playground/playground.module';
import { NbLayoutDirectionToggleComponent } from './layout-direction-toggle/layout-direction-toggle.component';

const NB_TEST_COMPONENTS = [
NbAppComponent,
Expand Down Expand Up @@ -226,6 +227,7 @@ const NB_TEST_COMPONENTS = [
],
declarations: [
...NB_TEST_COMPONENTS,
NbLayoutDirectionToggleComponent,
],
entryComponents: [
NbDynamicToAddComponent,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
:host {
display: block;
padding: 0.5em;

label {
margin: 0;
}

span {
vertical-align: middle;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Component } from '@angular/core';
import { NbLayoutDirectionService, NbLayoutDirection } from '@nebular/theme';

@Component({
selector: 'nb-layout-direction-toggle',
styleUrls: [ './layout-direction-toggle.component.scss' ],
template: `
<label dir="ltr">
<input type="checkbox" value="isRtl" (click)="toggleFlow()" />
<span>RTL</span>
</label>
`,
})
export class NbLayoutDirectionToggleComponent {
constructor(private directionService: NbLayoutDirectionService) {}

get isRtl() {
return this.directionService.isRtl();
}

toggleFlow() {
const oppositeDirection = this.isRtl
? NbLayoutDirection.LTR
: NbLayoutDirection.RTL;
this.directionService.setDirection(oppositeDirection);
}
}
11 changes: 11 additions & 0 deletions src/app/user-test/user-test.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,17 @@ import { NbBadgeComponent } from 'framework/theme/components/badge/badge.compone
<nb-user
[picture]="'data:image/png;base64,aaa'"></nb-user>
</div>
<div class="test-row">
<nb-user inverse
size="large"
picture="http://lorempixel.com/400/200/animals/"
name="Dmitry Nehaychik"
title="Worker"
badgeText="29"
[badgeStatus]="badge.STATUS_DANGER"
[badgePosition]="badge.TOP_START">
</nb-user>
</div>
</nb-layout-column>
</nb-layout>
`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@
height: nb-theme(actions-size-small);
padding: 0 nb-theme(actions-padding);

&:first-child {
@include nb-ltr(border-left, none!important);
@include nb-rtl(border-right, none!important);
}

a.icon-container {
&:hover, &:focus {
text-decoration: none;
Expand All @@ -25,7 +30,10 @@
color: nb-theme(actions-fg);
font-size: nb-theme(actions-size-small);
}
border-left: 1px solid nb-theme(actions-separator);

@include nb-ltr(border-left, 1px solid nb-theme(actions-separator));
@include nb-rtl(border-right, 1px solid nb-theme(actions-separator));

background: transparent;
}

Expand All @@ -34,7 +42,9 @@
i.control-icon {
color: nb-theme(actions-bg);
}
border-left: 1px solid nb-theme(actions-separator);

@include nb-ltr(border-left, 1px solid nb-theme(actions-separator));
@include nb-rtl(border-right, 1px solid nb-theme(actions-separator));
}
}

Expand Down
4 changes: 0 additions & 4 deletions src/framework/theme/components/actions/actions.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,6 @@
align-items: center;
position: relative;

&:first-child {
border-left: none!important;
}

i.control-icon {
&:hover {
cursor: pointer;
Expand Down
3 changes: 2 additions & 1 deletion src/framework/theme/components/actions/actions.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ export class NbActionComponent {
/**
* Badge position.
* Can be set to any class or to one of predefined positions:
* 'top left', 'top right', 'bottom left', 'bottom right'
* 'top left', 'top right', 'bottom left', 'bottom right',
* 'top start', 'top end', 'bottom start', 'bottom end'
* @type string
*/
@Input() badgePosition: string;
Expand Down
2 changes: 1 addition & 1 deletion src/framework/theme/components/badge/badge.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*/

.nb-badge {
:host .nb-badge {
position: absolute;
padding: 0.25em 0.4em;
font-size: 75%;
Expand Down
31 changes: 24 additions & 7 deletions src/framework/theme/components/badge/badge.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/

import { Component, Input } from '@angular/core';
import { NbLayoutDirectionService } from '../../services/direction.service';

/**
* Badge is a simple labeling component.
Expand Down Expand Up @@ -51,13 +52,17 @@ export class NbBadgeComponent {
static readonly BOTTOM_LEFT = 'bottom left';
static readonly BOTTOM_RIGHT = 'bottom right';

static readonly TOP_START = 'top start';
static readonly TOP_END = 'top end';
static readonly BOTTOM_START = 'bottom start';
static readonly BOTTOM_END = 'bottom end';

static readonly STATUS_PRIMARY = 'primary';
static readonly STATUS_INFO = 'info';
static readonly STATUS_SUCCESS = 'success';
static readonly STATUS_WARNING = 'warning';
static readonly STATUS_DANGER = 'danger';

positionClass: string = NbBadgeComponent.TOP_RIGHT;
colorClass: string = NbBadgeComponent.STATUS_PRIMARY;

/**
Expand All @@ -70,14 +75,11 @@ export class NbBadgeComponent {
* Badge position
*
* Can be set to any class or to one of predefined positions:
* 'top left', 'top right', 'bottom left', 'bottom right'
* 'top left', 'top right', 'bottom left', 'bottom right',
* 'top start', 'top end', 'bottom start', 'bottom end'
* @type string
*/
@Input() set position(value) {
if (value) {
this.positionClass = value;
}
}
@Input() position: string;

/**
* Badge status (adds specific styles):
Expand All @@ -90,4 +92,19 @@ export class NbBadgeComponent {
this.colorClass = value;
}
}

get positionClass() {
if (!this.position) {
return NbBadgeComponent.TOP_RIGHT;
}

const isLtr = this.layoutDirectionService.isLtr();
const startValue = isLtr ? 'left' : 'right';
const endValue = isLtr ? 'right' : 'left';
return this.position
.replace(/\bstart\b/, startValue)
.replace(/\bend\b/, endValue);
}

constructor(private layoutDirectionService: NbLayoutDirectionService) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,9 @@
margin-bottom: nb-theme(card-margin);
padding: nb-theme(card-padding);
}

.flipcard-body .front-container {
@include nb-ltr(margin-right, -100%);
@include nb-rtl(margin-left, -100%);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,6 @@
}
}

.front-container {
margin-right: -100%;
}

.back-container {
transform: rotateY(180deg);
}
Expand Down
Loading

0 comments on commit 0326c1c

Please sign in to comment.