diff options
author | Rigel Kent <sendmemail@rigelk.eu> | 2018-09-08 14:34:32 +0200 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2018-09-11 10:58:48 +0200 |
commit | e78980ebd116be1ea901387c126876af902191e6 (patch) | |
tree | ea037d021e1383501e2b79add1a9581ef80a7c99 | |
parent | ecf06378ff95ec35f75de0169a02a974d2c17c62 (diff) | |
download | PeerTube-e78980ebd116be1ea901387c126876af902191e6.tar.gz PeerTube-e78980ebd116be1ea901387c126876af902191e6.tar.zst PeerTube-e78980ebd116be1ea901387c126876af902191e6.zip |
use focus-visible polyfill to improve keyboard navigation
Only the homepage is concerned, but it should have decent keyboard
navigation support now.
-rw-r--r-- | client/package.json | 3 | ||||
-rw-r--r-- | client/src/app/app.component.html | 2 | ||||
-rw-r--r-- | client/src/app/app.module.ts | 1 | ||||
-rw-r--r-- | client/src/app/menu/menu.component.html | 4 | ||||
-rw-r--r-- | client/src/app/menu/menu.component.scss | 3 | ||||
-rw-r--r-- | client/src/app/search/search.component.html | 4 | ||||
-rw-r--r-- | client/src/app/shared/video/video-miniature.component.html | 5 | ||||
-rw-r--r-- | client/src/app/shared/video/video-thumbnail.component.scss | 5 | ||||
-rw-r--r-- | client/src/app/videos/video-list/video-overview.component.scss | 5 | ||||
-rw-r--r-- | client/src/sass/include/_mixins.scss | 4 | ||||
-rw-r--r-- | client/yarn.lock | 72 | ||||
-rw-r--r-- | server/helpers/custom-validators/videos.ts | 4 |
12 files changed, 32 insertions, 80 deletions
diff --git a/client/package.json b/client/package.json index 3a4605b56..4454e9a39 100644 --- a/client/package.json +++ b/client/package.json | |||
@@ -159,6 +159,7 @@ | |||
159 | "webpack-cli": "^3.0.8", | 159 | "webpack-cli": "^3.0.8", |
160 | "webtorrent": "^0.102.1", | 160 | "webtorrent": "^0.102.1", |
161 | "whatwg-fetch": "^2.0.4", | 161 | "whatwg-fetch": "^2.0.4", |
162 | "zone.js": "~0.8.5" | 162 | "zone.js": "~0.8.5", |
163 | "focus-visible": "^4.1.5" | ||
163 | } | 164 | } |
164 | } | 165 | } |
diff --git a/client/src/app/app.component.html b/client/src/app/app.component.html index 20e573de1..23ed04c2d 100644 --- a/client/src/app/app.component.html +++ b/client/src/app/app.component.html | |||
@@ -22,7 +22,7 @@ | |||
22 | <div class="sub-header-container"> | 22 | <div class="sub-header-container"> |
23 | <my-menu *ngIf="isMenuDisplayed"></my-menu> | 23 | <my-menu *ngIf="isMenuDisplayed"></my-menu> |
24 | 24 | ||
25 | <div id="right-container" class="main-col container-fluid" [ngClass]="{ expanded: isMenuDisplayed === false }"> | 25 | <div id="content" tabindex="-1" class="main-col container-fluid" [ngClass]="{ expanded: isMenuDisplayed === false }"> |
26 | 26 | ||
27 | <div class="main-row"> | 27 | <div class="main-row"> |
28 | <router-outlet></router-outlet> | 28 | <router-outlet></router-outlet> |
diff --git a/client/src/app/app.module.ts b/client/src/app/app.module.ts index b484a89e8..ba16c072e 100644 --- a/client/src/app/app.module.ts +++ b/client/src/app/app.module.ts | |||
@@ -6,6 +6,7 @@ import { ResetPasswordModule } from '@app/reset-password' | |||
6 | import { MetaLoader, MetaModule, MetaStaticLoader, PageTitlePositioning } from '@ngx-meta/core' | 6 | import { MetaLoader, MetaModule, MetaStaticLoader, PageTitlePositioning } from '@ngx-meta/core' |
7 | import { ClipboardModule } from 'ngx-clipboard' | 7 | import { ClipboardModule } from 'ngx-clipboard' |
8 | import { HotkeyModule, IHotkeyOptions } from 'angular2-hotkeys' | 8 | import { HotkeyModule, IHotkeyOptions } from 'angular2-hotkeys' |
9 | import 'focus-visible' | ||
9 | 10 | ||
10 | import { AppRoutingModule } from './app-routing.module' | 11 | import { AppRoutingModule } from './app-routing.module' |
11 | import { AppComponent } from './app.component' | 12 | import { AppComponent } from './app.component' |
diff --git a/client/src/app/menu/menu.component.html b/client/src/app/menu/menu.component.html index 63ff4a86f..139664534 100644 --- a/client/src/app/menu/menu.component.html +++ b/client/src/app/menu/menu.component.html | |||
@@ -85,10 +85,10 @@ | |||
85 | 85 | ||
86 | <div class="footer d-flex justify-content-between"> | 86 | <div class="footer d-flex justify-content-between"> |
87 | <span class="language"> | 87 | <span class="language"> |
88 | <span (click)="openLanguageChooser()" i18n-title title="Change the language" class="icon icon-language"></span> | 88 | <span tabindex="0" (keyup.enter)="openLanguageChooser()" (click)="openLanguageChooser()" i18n-title title="Change the language" class="icon icon-language"></span> |
89 | </span> | 89 | </span> |
90 | <span class="color-palette"> | 90 | <span class="color-palette"> |
91 | <span (click)="toggleDarkTheme()" i18n-title title="Toggle dark interface" class="icon icon-moonsun"></span> | 91 | <span tabindex="0" (keyup.enter)="toggleDarkTheme()" (click)="toggleDarkTheme()" i18n-title title="Toggle dark interface" class="icon icon-moonsun"></span> |
92 | </span> | 92 | </span> |
93 | </div> | 93 | </div> |
94 | </menu> | 94 | </menu> |
diff --git a/client/src/app/menu/menu.component.scss b/client/src/app/menu/menu.component.scss index 592860e12..f1b0a284f 100644 --- a/client/src/app/menu/menu.component.scss +++ b/client/src/app/menu/menu.component.scss | |||
@@ -130,7 +130,7 @@ menu { | |||
130 | transition: background-color .1s ease-in-out; | 130 | transition: background-color .1s ease-in-out; |
131 | @include disable-default-a-behaviour; | 131 | @include disable-default-a-behaviour; |
132 | 132 | ||
133 | &:hover { | 133 | &:hover, &.focus-visible { |
134 | background-color: rgba(255, 255, 255, 0.15); | 134 | background-color: rgba(255, 255, 255, 0.15); |
135 | } | 135 | } |
136 | 136 | ||
@@ -202,6 +202,7 @@ menu { | |||
202 | font-weight: $font-semibold; | 202 | font-weight: $font-semibold; |
203 | 203 | ||
204 | .icon { | 204 | .icon { |
205 | @include disable-outline; | ||
205 | @include icon(28px); | 206 | @include icon(28px); |
206 | opacity: 0.9; | 207 | opacity: 0.9; |
207 | 208 | ||
diff --git a/client/src/app/search/search.component.html b/client/src/app/search/search.component.html index a258d4edd..c4072b291 100644 --- a/client/src/app/search/search.component.html +++ b/client/src/app/search/search.component.html | |||
@@ -48,9 +48,9 @@ | |||
48 | <my-video-thumbnail [video]="result"></my-video-thumbnail> | 48 | <my-video-thumbnail [video]="result"></my-video-thumbnail> |
49 | 49 | ||
50 | <div class="video-info"> | 50 | <div class="video-info"> |
51 | <a class="video-info-name" [routerLink]="['/videos/watch', result.uuid]" [attr.title]="result.name">{{ result.name }}</a> | 51 | <a tabindex="-1" class="video-info-name" [routerLink]="['/videos/watch', result.uuid]" [attr.title]="result.name">{{ result.name }}</a> |
52 | <span i18n class="video-info-date-views">{{ result.publishedAt | myFromNow }} - {{ result.views | myNumberFormatter }} views</span> | 52 | <span i18n class="video-info-date-views">{{ result.publishedAt | myFromNow }} - {{ result.views | myNumberFormatter }} views</span> |
53 | <a class="video-info-account" [routerLink]="[ '/accounts', result.byAccount ]">{{ result.byAccount }}</a> | 53 | <a tabindex="-1" class="video-info-account" [routerLink]="[ '/accounts', result.byAccount ]">{{ result.byAccount }}</a> |
54 | </div> | 54 | </div> |
55 | </div> | 55 | </div> |
56 | </ng-container> | 56 | </ng-container> |
diff --git a/client/src/app/shared/video/video-miniature.component.html b/client/src/app/shared/video/video-miniature.component.html index de84bccf9..9cf3fb321 100644 --- a/client/src/app/shared/video/video-miniature.component.html +++ b/client/src/app/shared/video/video-miniature.component.html | |||
@@ -3,6 +3,7 @@ | |||
3 | 3 | ||
4 | <div class="video-miniature-information"> | 4 | <div class="video-miniature-information"> |
5 | <a | 5 | <a |
6 | tabindex="-1" | ||
6 | class="video-miniature-name" | 7 | class="video-miniature-name" |
7 | [routerLink]="[ '/videos/watch', video.uuid ]" [attr.title]="video.name" [ngClass]="{ 'blur-filter': isVideoBlur() }" | 8 | [routerLink]="[ '/videos/watch', video.uuid ]" [attr.title]="video.name" [ngClass]="{ 'blur-filter': isVideoBlur() }" |
8 | > | 9 | > |
@@ -11,10 +12,10 @@ | |||
11 | 12 | ||
12 | <span i18n class="video-miniature-created-at-views">{{ video.publishedAt | myFromNow }} - {{ video.views | myNumberFormatter }} views</span> | 13 | <span i18n class="video-miniature-created-at-views">{{ video.publishedAt | myFromNow }} - {{ video.views | myNumberFormatter }} views</span> |
13 | 14 | ||
14 | <a *ngIf="displayOwnerAccount()" class="video-miniature-account" [routerLink]="[ '/accounts', video.byAccount ]"> | 15 | <a tabindex="-1" *ngIf="displayOwnerAccount()" class="video-miniature-account" [routerLink]="[ '/accounts', video.byAccount ]"> |
15 | {{ video.byAccount }} | 16 | {{ video.byAccount }} |
16 | </a> | 17 | </a> |
17 | <a *ngIf="displayOwnerVideoChannel()" class="video-miniature-channel" [routerLink]="[ '/video-channels', video.byVideoChannel ]"> | 18 | <a tabindex="-1" *ngIf="displayOwnerVideoChannel()" class="video-miniature-channel" [routerLink]="[ '/video-channels', video.byVideoChannel ]"> |
18 | {{ video.byVideoChannel }} | 19 | {{ video.byVideoChannel }} |
19 | </a> | 20 | </a> |
20 | </div> | 21 | </div> |
diff --git a/client/src/app/shared/video/video-thumbnail.component.scss b/client/src/app/shared/video/video-thumbnail.component.scss index c3cb1ec75..1dd8e5338 100644 --- a/client/src/app/shared/video/video-thumbnail.component.scss +++ b/client/src/app/shared/video/video-thumbnail.component.scss | |||
@@ -14,6 +14,11 @@ | |||
14 | text-decoration: none !important; | 14 | text-decoration: none !important; |
15 | } | 15 | } |
16 | 16 | ||
17 | @include disable-outline; | ||
18 | &.focus-visible { | ||
19 | box-shadow: 0 0 0 2px var(--mainColor); | ||
20 | } | ||
21 | |||
17 | img { | 22 | img { |
18 | width: $video-thumbnail-width; | 23 | width: $video-thumbnail-width; |
19 | height: $video-thumbnail-height; | 24 | height: $video-thumbnail-height; |
diff --git a/client/src/app/videos/video-list/video-overview.component.scss b/client/src/app/videos/video-list/video-overview.component.scss index f5508cf61..eca8b230f 100644 --- a/client/src/app/videos/video-list/video-overview.component.scss +++ b/client/src/app/videos/video-list/video-overview.component.scss | |||
@@ -19,7 +19,10 @@ | |||
19 | margin-bottom: 20px; | 19 | margin-bottom: 20px; |
20 | 20 | ||
21 | a { | 21 | a { |
22 | @include disable-default-a-behaviour; | 22 | &:hover, &:focus:not(.focus-visible), &:active { |
23 | text-decoration: none; | ||
24 | outline: none; | ||
25 | } | ||
23 | 26 | ||
24 | color: var(--mainForegroundColor); | 27 | color: var(--mainForegroundColor); |
25 | } | 28 | } |
diff --git a/client/src/sass/include/_mixins.scss b/client/src/sass/include/_mixins.scss index 03cb337c2..d755e7df3 100644 --- a/client/src/sass/include/_mixins.scss +++ b/client/src/sass/include/_mixins.scss | |||
@@ -8,7 +8,9 @@ | |||
8 | } | 8 | } |
9 | 9 | ||
10 | @mixin disable-outline { | 10 | @mixin disable-outline { |
11 | outline: none; | 11 | &:focus:not(.focus-visible) { |
12 | outline: none; | ||
13 | } | ||
12 | 14 | ||
13 | &::-moz-focus-inner { | 15 | &::-moz-focus-inner { |
14 | border: 0; | 16 | border: 0; |
diff --git a/client/yarn.lock b/client/yarn.lock index dcd37c80d..6ecf26393 100644 --- a/client/yarn.lock +++ b/client/yarn.lock | |||
@@ -189,46 +189,12 @@ | |||
189 | version "2.1.3" | 189 | version "2.1.3" |
190 | resolved "https://registry.yarnpkg.com/@angularclass/hmr/-/hmr-2.1.3.tgz#34e658ed3da37f23b0a200e2da5a89be92bb209f" | 190 | resolved "https://registry.yarnpkg.com/@angularclass/hmr/-/hmr-2.1.3.tgz#34e658ed3da37f23b0a200e2da5a89be92bb209f" |
191 | 191 | ||
192 | "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.0.0-beta.35": | 192 | "@babel/code-frame@^7.0.0-beta.35": |
193 | version "7.0.0" | 193 | version "7.0.0" |
194 | resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8" | 194 | resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8" |
195 | dependencies: | 195 | dependencies: |
196 | "@babel/highlight" "^7.0.0" | 196 | "@babel/highlight" "^7.0.0" |
197 | 197 | ||
198 | "@babel/helper-module-imports@^7.0.0": | ||
199 | version "7.0.0" | ||
200 | resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz#96081b7111e486da4d2cd971ad1a4fe216cc2e3d" | ||
201 | dependencies: | ||
202 | "@babel/types" "^7.0.0" | ||
203 | |||
204 | "@babel/helper-module-transforms@^7.0.0": | ||
205 | version "7.0.0" | ||
206 | resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.0.0.tgz#b01ee7d543e81e8c3fc404b19c9f26acb6e4cf4c" | ||
207 | dependencies: | ||
208 | "@babel/helper-module-imports" "^7.0.0" | ||
209 | "@babel/helper-simple-access" "^7.0.0" | ||
210 | "@babel/helper-split-export-declaration" "^7.0.0" | ||
211 | "@babel/template" "^7.0.0" | ||
212 | "@babel/types" "^7.0.0" | ||
213 | lodash "^4.17.10" | ||
214 | |||
215 | "@babel/helper-plugin-utils@^7.0.0": | ||
216 | version "7.0.0" | ||
217 | resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz#bbb3fbee98661c569034237cc03967ba99b4f250" | ||
218 | |||
219 | "@babel/helper-simple-access@^7.0.0": | ||
220 | version "7.0.0" | ||
221 | resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.0.0.tgz#ff36a27983ae4c27122da2f7f294dced80ecbd08" | ||
222 | dependencies: | ||
223 | "@babel/template" "^7.0.0" | ||
224 | "@babel/types" "^7.0.0" | ||
225 | |||
226 | "@babel/helper-split-export-declaration@^7.0.0": | ||
227 | version "7.0.0" | ||
228 | resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0.tgz#3aae285c0311c2ab095d997b8c9a94cad547d813" | ||
229 | dependencies: | ||
230 | "@babel/types" "^7.0.0" | ||
231 | |||
232 | "@babel/highlight@^7.0.0": | 198 | "@babel/highlight@^7.0.0": |
233 | version "7.0.0" | 199 | version "7.0.0" |
234 | resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0.tgz#f710c38c8d458e6dd9a201afb637fcb781ce99e4" | 200 | resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0.tgz#f710c38c8d458e6dd9a201afb637fcb781ce99e4" |
@@ -237,34 +203,6 @@ | |||
237 | esutils "^2.0.2" | 203 | esutils "^2.0.2" |
238 | js-tokens "^4.0.0" | 204 | js-tokens "^4.0.0" |
239 | 205 | ||
240 | "@babel/parser@^7.0.0": | ||
241 | version "7.0.0" | ||
242 | resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.0.0.tgz#697655183394facffb063437ddf52c0277698775" | ||
243 | |||
244 | "@babel/plugin-transform-modules-commonjs@^7.0.0": | ||
245 | version "7.0.0" | ||
246 | resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.0.0.tgz#20b906e5ab130dd8e456b694a94d9575da0fd41f" | ||
247 | dependencies: | ||
248 | "@babel/helper-module-transforms" "^7.0.0" | ||
249 | "@babel/helper-plugin-utils" "^7.0.0" | ||
250 | "@babel/helper-simple-access" "^7.0.0" | ||
251 | |||
252 | "@babel/template@^7.0.0": | ||
253 | version "7.0.0" | ||
254 | resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.0.0.tgz#c2bc9870405959c89a9c814376a2ecb247838c80" | ||
255 | dependencies: | ||
256 | "@babel/code-frame" "^7.0.0" | ||
257 | "@babel/parser" "^7.0.0" | ||
258 | "@babel/types" "^7.0.0" | ||
259 | |||
260 | "@babel/types@^7.0.0": | ||
261 | version "7.0.0" | ||
262 | resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.0.0.tgz#6e191793d3c854d19c6749989e3bc55f0e962118" | ||
263 | dependencies: | ||
264 | esutils "^2.0.2" | ||
265 | lodash "^4.17.10" | ||
266 | to-fast-properties "^2.0.0" | ||
267 | |||
268 | "@neos21/bootstrap3-glyphicons@^1.0.1": | 206 | "@neos21/bootstrap3-glyphicons@^1.0.1": |
269 | version "1.0.1" | 207 | version "1.0.1" |
270 | resolved "https://registry.yarnpkg.com/@neos21/bootstrap3-glyphicons/-/bootstrap3-glyphicons-1.0.1.tgz#e5eeec43e0153d4b51effd9ecb58cdf7029924d7" | 208 | resolved "https://registry.yarnpkg.com/@neos21/bootstrap3-glyphicons/-/bootstrap3-glyphicons-1.0.1.tgz#e5eeec43e0153d4b51effd9ecb58cdf7029924d7" |
@@ -3299,6 +3237,10 @@ flush-write-stream@^1.0.0: | |||
3299 | inherits "^2.0.1" | 3237 | inherits "^2.0.1" |
3300 | readable-stream "^2.0.4" | 3238 | readable-stream "^2.0.4" |
3301 | 3239 | ||
3240 | focus-visible@^4.1.5: | ||
3241 | version "4.1.5" | ||
3242 | resolved "https://registry.yarnpkg.com/focus-visible/-/focus-visible-4.1.5.tgz#50b44e2e84c24b831ceca3cce84d57c2b311c855" | ||
3243 | |||
3302 | follow-redirects@^1.0.0: | 3244 | follow-redirects@^1.0.0: |
3303 | version "1.5.1" | 3245 | version "1.5.1" |
3304 | resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.1.tgz#67a8f14f5a1f67f962c2c46469c79eaec0a90291" | 3246 | resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.1.tgz#67a8f14f5a1f67f962c2c46469c79eaec0a90291" |
@@ -8217,10 +8159,6 @@ to-fast-properties@^1.0.3: | |||
8217 | version "1.0.3" | 8159 | version "1.0.3" |
8218 | resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" | 8160 | resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" |
8219 | 8161 | ||
8220 | to-fast-properties@^2.0.0: | ||
8221 | version "2.0.0" | ||
8222 | resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" | ||
8223 | |||
8224 | to-object-path@^0.3.0: | 8162 | to-object-path@^0.3.0: |
8225 | version "0.3.0" | 8163 | version "0.3.0" |
8226 | resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" | 8164 | resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" |
diff --git a/server/helpers/custom-validators/videos.ts b/server/helpers/custom-validators/videos.ts index 4b1f6c069..edafba6e2 100644 --- a/server/helpers/custom-validators/videos.ts +++ b/server/helpers/custom-validators/videos.ts | |||
@@ -178,7 +178,7 @@ async function isVideoChannelOfAccountExist (channelId: number, user: UserModel, | |||
178 | const videoChannel = await VideoChannelModel.loadAndPopulateAccount(channelId) | 178 | const videoChannel = await VideoChannelModel.loadAndPopulateAccount(channelId) |
179 | if (videoChannel === null) { | 179 | if (videoChannel === null) { |
180 | res.status(400) | 180 | res.status(400) |
181 | .json({ error: 'Unknown video video channel on this instance.' }) | 181 | .json({ error: 'Unknown video `video channel` on this instance.' }) |
182 | .end() | 182 | .end() |
183 | 183 | ||
184 | return false | 184 | return false |
@@ -191,7 +191,7 @@ async function isVideoChannelOfAccountExist (channelId: number, user: UserModel, | |||
191 | const videoChannel = await VideoChannelModel.loadByIdAndAccount(channelId, user.Account.id) | 191 | const videoChannel = await VideoChannelModel.loadByIdAndAccount(channelId, user.Account.id) |
192 | if (videoChannel === null) { | 192 | if (videoChannel === null) { |
193 | res.status(400) | 193 | res.status(400) |
194 | .json({ error: 'Unknown video video channel for this account.' }) | 194 | .json({ error: 'Unknown video `video channel` for this account.' }) |
195 | .end() | 195 | .end() |
196 | 196 | ||
197 | return false | 197 | return false |