Customize select
authorChocobozzz <me@florianbigard.com>
Wed, 20 Dec 2017 13:29:55 +0000 (14:29 +0100)
committerChocobozzz <me@florianbigard.com>
Wed, 20 Dec 2017 13:29:55 +0000 (14:29 +0100)
13 files changed:
client/src/app/+admin/users/user-edit/user-edit.component.html
client/src/app/+admin/users/user-edit/user-edit.component.scss
client/src/app/shared/misc/utils.ts
client/src/app/shared/video/video-edit.model.ts
client/src/app/videos/+video-edit/shared/video-edit.component.html
client/src/app/videos/+video-edit/shared/video-edit.component.scss
client/src/app/videos/+video-edit/shared/video-edit.component.ts
client/src/app/videos/+video-edit/video-add.component.html
client/src/app/videos/+video-edit/video-add.component.scss
client/src/app/videos/+video-edit/video-add.component.ts
client/src/app/videos/+video-edit/video-update.component.html
client/src/app/videos/+video-edit/video-update.component.ts
client/src/sass/include/_mixins.scss

index 963e2f39a6598390dafd8fc7471903cb73d4ac3a..77aa613a170ed7191ab4f0d0ad9439b005d7b36f 100644 (file)
@@ -7,7 +7,7 @@
   <div class="form-group" *ngIf="isCreation()">
     <label for="username">Username</label>
     <input
-      type="text" class="form-control" id="username" placeholder="john"
+      type="text" id="username" placeholder="john"
       formControlName="username" [ngClass]="{ 'input-error': formErrors['username'] }"
     >
     <div *ngIf="formErrors.username" class="form-error">
@@ -18,7 +18,7 @@
   <div class="form-group">
     <label for="email">Email</label>
     <input
-      type="text" class="form-control" id="email" placeholder="mail@example.com"
+      type="text" id="email" placeholder="mail@example.com"
       formControlName="email" [ngClass]="{ 'input-error': formErrors['email'] }"
     >
     <div *ngIf="formErrors.email" class="form-error">
@@ -29,7 +29,7 @@
   <div class="form-group" *ngIf="isCreation()">
     <label for="password">Password</label>
     <input
-      type="password" class="form-control" id="password"
+      type="password" id="password"
       formControlName="password" [ngClass]="{ 'input-error': formErrors['password'] }"
     >
     <div *ngIf="formErrors.password" class="form-error">
 
   <div class="form-group">
     <label for="role">Role</label>
-    <select class="form-control" id="role" formControlName="role">
-      <option *ngFor="let role of roles" [value]="role.value">
-        {{ role.label }}
-      </option>
-    </select>
+    <div class="peertube-select-container">
+      <select id="role" formControlName="role">
+        <option *ngFor="let role of roles" [value]="role.value">
+          {{ role.label }}
+        </option>
+      </select>
+    </div>
 
     <div *ngIf="formErrors.role" class="form-error">
       {{ formErrors.role }}
 
   <div class="form-group">
     <label for="videoQuota">Video quota</label>
-    <select class="form-control" id="videoQuota" formControlName="videoQuota">
-      <option *ngFor="let videoQuotaOption of videoQuotaOptions" [value]="videoQuotaOption.value">
-        {{ videoQuotaOption.label }}
-      </option>
-    </select>
+    <div class="peertube-select-container">
+      <select id="videoQuota" formControlName="videoQuota">
+        <option *ngFor="let videoQuotaOption of videoQuotaOptions" [value]="videoQuotaOption.value">
+          {{ videoQuotaOption.label }}
+        </option>
+      </select>
+    </div>
 
     <div class="transcoding-information" *ngIf="isTranscodingInformationDisplayed()">
       Transcoding is enabled on server. The video quota only take in account <strong>original</strong> video. <br />
index 1bb1c5f0f4c4fd2da13ddba4d3e91a1098665cb1..59bb8e3e49f9b64e9fda4b8893309ab9a122890a 100644 (file)
@@ -10,8 +10,8 @@ input:not([type=submit]) {
   display: block;
 }
 
-select {
-  @include peertube-select(340px);
+.peertube-select-container {
+  @include peertube-select-container(340px);
 }
 
 input[type=submit] {
index df9e0381a32c7ab2a1254104d2421b84ffd925c6..5525e4efbd28b1b1de4c9374671f955d8d066c6f 100644 (file)
@@ -1,5 +1,7 @@
 // Thanks: https://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript
 
+import { AuthService } from '../../core/auth'
+
 function getParameterByName (name: string, url: string) {
   if (!url) url = window.location.href
   name = name.replace(/[\[\]]/g, '\\$&')
@@ -17,7 +19,27 @@ function viewportHeight () {
   return Math.max(document.documentElement.clientHeight, window.innerHeight || 0)
 }
 
+function populateAsyncUserVideoChannels (authService: AuthService, channel: any[]) {
+  return new Promise(res => {
+    authService.userInformationLoaded
+      .subscribe(
+        () => {
+          const user = authService.getUser()
+          if (!user) return
+
+          const videoChannels = user.videoChannels
+          if (Array.isArray(videoChannels) === false) return
+
+          videoChannels.forEach(c => channel.push({ id: c.id, label: c.name }))
+
+          return res()
+        }
+      )
+  })
+}
+
 export {
   viewportHeight,
-  getParameterByName
+  getParameterByName,
+  populateAsyncUserVideoChannels
 }
index 955255bfa0dca2a3f40f3fda815c1ce41640a6eb..47c63d976e295195fc8cd1f85afa26992fa4511b 100644 (file)
@@ -45,7 +45,7 @@ export class VideoEdit {
       name: this.name,
       tags: this.tags,
       nsfw: this.nsfw,
-      channel: this.channel,
+      channelId: this.channel,
       privacy: this.privacy
     }
   }
index 8c071ce125f68bd0846addf326f540f12b09287e..e728d8ea2da4da4bcf1dce17cf35b404f75bf7e2 100644 (file)
   </div>
 
   <div class="col-md-4">
+    <div class="form-group">
+      <label>Channel</label>
+      <div class="peertube-select-disabled-container">
+        <select formControlName="channelId">
+          <option *ngFor="let channel of userVideoChannels" [value]="channel.id">{{ channel.label }}</option>
+        </select>
+      </div>
+    </div>
+
     <div class="form-group">
       <label for="category">Category</label>
-      <select id="category" formControlName="category">
-        <option></option>
-        <option *ngFor="let category of videoCategories" [value]="category.id">{{ category.label }}</option>
-      </select>
+      <div class="peertube-select-container">
+        <select id="category" formControlName="category">
+          <option></option>
+          <option *ngFor="let category of videoCategories" [value]="category.id">{{ category.label }}</option>
+        </select>
+      </div>
 
       <div *ngIf="formErrors.category" class="form-error">
         {{ formErrors.category }}
 
     <div class="form-group">
       <label for="licence">Licence</label>
-      <select id="licence" formControlName="licence">
-        <option></option>
-        <option *ngFor="let licence of videoLicences" [value]="licence.id">{{ licence.label }}</option>
-      </select>
+      <div class="peertube-select-container">
+        <select id="licence" formControlName="licence">
+          <option></option>
+          <option *ngFor="let licence of videoLicences" [value]="licence.id">{{ licence.label }}</option>
+        </select>
+      </div>
 
       <div *ngIf="formErrors.licence" class="form-error">
         {{ formErrors.licence }}
 
     <div class="form-group">
       <label for="language">Language</label>
-      <select id="language" formControlName="language">
-        <option></option>
-        <option *ngFor="let language of videoLanguages" [value]="language.id">{{ language.label }}</option>
-      </select>
+      <div class="peertube-select-container">
+        <select id="language" formControlName="language">
+          <option></option>
+          <option *ngFor="let language of videoLanguages" [value]="language.id">{{ language.label }}</option>
+        </select>
+      </div>
 
       <div *ngIf="formErrors.language" class="form-error">
         {{ formErrors.language }}
 
     <div class="form-group">
       <label for="privacy">Privacy</label>
-      <select id="privacy" formControlName="privacy">
-
-        <option></option>
-        <option *ngFor="let privacy of videoPrivacies" [value]="privacy.id">{{ privacy.label }}</option>
-      </select>
+      <div class="peertube-select-container">
+        <select id="privacy" formControlName="privacy">
+          <option></option>
+          <option *ngFor="let privacy of videoPrivacies" [value]="privacy.id">{{ privacy.label }}</option>
+        </select>
+      </div>
 
       <div *ngIf="formErrors.privacy" class="form-error">
         {{ formErrors.privacy }}
index f4466bdde2fe9a70610ba62ecfb363c8ac8d05e9..ba0ca1e21aae8f1cb27a4e22b8556d05cc672c46 100644 (file)
@@ -1,6 +1,14 @@
 @import '_variables';
 @import '_mixins';
 
+.peertube-select-container {
+  @include peertube-select-container(auto);
+}
+
+.peertube-select-disabled-container {
+  @include peertube-select-disabled-container(auto);
+}
+
 .video-edit {
   height: 100%;
 
     }
   }
 
-  select {
-    @include peertube-select(100%);
-  }
-
   input, select {
     font-size: 15px
   }
index 28c9134a748072253b5f68029abe3abd2155d6b9..7fe2652843bc400e450a2b7966a9bc11c8f61b4d 100644 (file)
@@ -4,6 +4,7 @@ import { ActivatedRoute, Router } from '@angular/router'
 import { NotificationsService } from 'angular2-notifications'
 import 'rxjs/add/observable/forkJoin'
 import { ServerService } from '../../../core/server'
+import { VIDEO_CHANNEL } from '../../../shared/forms/form-validators'
 import { ValidatorMessage } from '../../../shared/forms/form-validators/validator-message'
 import {
   VIDEO_CATEGORY,
@@ -27,6 +28,7 @@ export class VideoEditComponent implements OnInit {
   @Input() formErrors: { [ id: string ]: string } = {}
   @Input() validationMessages: ValidatorMessage = {}
   @Input() videoPrivacies = []
+  @Input() userVideoChannels = []
 
   tags: string[] = []
   videoCategories = []
@@ -50,6 +52,7 @@ export class VideoEditComponent implements OnInit {
   updateForm () {
     this.formErrors['name'] = ''
     this.formErrors['privacy'] = ''
+    this.formErrors['channelId'] = ''
     this.formErrors['category'] = ''
     this.formErrors['licence'] = ''
     this.formErrors['language'] = ''
@@ -57,6 +60,7 @@ export class VideoEditComponent implements OnInit {
 
     this.validationMessages['name'] = VIDEO_NAME.MESSAGES
     this.validationMessages['privacy'] = VIDEO_PRIVACY.MESSAGES
+    this.validationMessages['channelId'] = VIDEO_CHANNEL.MESSAGES
     this.validationMessages['category'] = VIDEO_CATEGORY.MESSAGES
     this.validationMessages['licence'] = VIDEO_LICENCE.MESSAGES
     this.validationMessages['language'] = VIDEO_LANGUAGE.MESSAGES
@@ -64,6 +68,7 @@ export class VideoEditComponent implements OnInit {
 
     this.form.addControl('name', new FormControl('', VIDEO_NAME.VALIDATORS))
     this.form.addControl('privacy', new FormControl('', VIDEO_PRIVACY.VALIDATORS))
+    this.form.addControl('channelId', new FormControl({ value: '', disabled: true }))
     this.form.addControl('nsfw', new FormControl(false))
     this.form.addControl('category', new FormControl('', VIDEO_CATEGORY.VALIDATORS))
     this.form.addControl('licence', new FormControl('', VIDEO_LICENCE.VALIDATORS))
index a6f2bf6f28e50146e2b8858bd7a7d6f528a63dc8..20277423c8bbaa2b048ba023f538c8a15371c50e 100644 (file)
         <input #videofileInput type="file" name="videofile" id="videofile" (change)="fileChange()" />
       </div>
 
-      <div class="form-group">
-        <select [(ngModel)]="firstStepPrivacyId">
-          <option *ngFor="let privacy of videoPrivacies" [value]="privacy.id">{{ privacy.label }}</option>
-        </select>
+      <div class="form-group form-group-channel">
+        <label for="first-step-channel">Channel</label>
+        <div class="peertube-select-container">
+          <select id="first-step-channel" [(ngModel)]="firstStepChannelId">
+            <option *ngFor="let channel of userVideoChannels" [value]="channel.id">{{ channel.label }}</option>
+          </select>
+        </div>
       </div>
 
       <div class="form-group">
-        <select [(ngModel)]="firstStepChannelId">
-          <option *ngFor="let channel of userVideoChannels" [value]="channel.id">{{ channel.label }}</option>
-        </select>
+        <label for="first-step-privacy">Privacy</label>
+        <div class="peertube-select-container">
+          <select id="first-step-privacy" [(ngModel)]="firstStepPrivacyId">
+            <option *ngFor="let privacy of videoPrivacies" [value]="privacy.id">{{ privacy.label }}</option>
+          </select>
+        </div>
       </div>
     </div>
   </div>
@@ -37,7 +43,7 @@
   <form [hidden]="!isUploadingVideo" novalidate [formGroup]="form">
     <my-video-edit
         [form]="form" [formErrors]="formErrors"
-        [validationMessages]="validationMessages" [videoPrivacies]="videoPrivacies"
+        [validationMessages]="validationMessages" [videoPrivacies]="videoPrivacies" [userVideoChannels]="userVideoChannels"
     ></my-video-edit>
 
 
index 78140e0e9cde6674b7a44bf15738af794b5b0737..891f38819d32f74fc42c895c68b296e4cb0d966a 100644 (file)
@@ -7,17 +7,24 @@
   border: 3px solid #EAEAEA;
   width: 100%;
   height: 440px;
-  text-align: center;
   margin-top: 40px;
   display: flex;
   justify-content: center;
   align-items: center;
 
+  .peertube-select-container {
+    @include peertube-select-container(190px);
+  }
+
   .upload-video {
     display: flex;
     flex-direction: column;
     align-items: center;
 
+    .form-group-channel {
+      margin-bottom: 20px;
+    }
+
     .icon.icon-upload {
       @include icon(90px);
       margin-bottom: 25px;
@@ -30,7 +37,8 @@
       position: relative;
       overflow: hidden;
       display: inline-block;
-      margin-bottom: 70px;
+      margin-bottom: 45px;
+      width: 190px;
 
       @include peertube-button;
       @include orange-button;
         display: block;
       }
     }
-
-    select {
-      @include peertube-select(auto);
-
-      display: inline-block;
-      font-size: 15px
-    }
   }
 }
 
index 90510d0dc8e0643016a421df9266fe71836322cd..9bbee58d8f2504f3be641349486f23646be30aea 100644 (file)
@@ -7,6 +7,7 @@ import { VideoPrivacy } from '../../../../../shared/models/videos'
 import { AuthService, ServerService } from '../../core'
 import { FormReactive } from '../../shared'
 import { ValidatorMessage } from '../../shared/forms/form-validators/validator-message'
+import { populateAsyncUserVideoChannels } from '../../shared/misc/utils'
 import { VideoEdit } from '../../shared/video/video-edit.model'
 import { VideoService } from '../../shared/video/video.service'
 
@@ -59,6 +60,9 @@ export class VideoAddComponent extends FormReactive implements OnInit {
   ngOnInit () {
     this.buildForm()
 
+    populateAsyncUserVideoChannels(this.authService, this.userVideoChannels)
+      .then(() => this.firstStepChannelId = this.userVideoChannels[0].id)
+
     this.serverService.videoPrivaciesLoaded
       .subscribe(
         () => {
@@ -67,20 +71,6 @@ export class VideoAddComponent extends FormReactive implements OnInit {
           // Public by default
           this.firstStepPrivacyId = VideoPrivacy.PUBLIC
         })
-
-    this.authService.userInformationLoaded
-      .subscribe(
-        () => {
-          const user = this.authService.getUser()
-          if (!user) return
-
-          const videoChannels = user.videoChannels
-          if (Array.isArray(videoChannels) === false) return
-
-          this.userVideoChannels = videoChannels.map(v => ({ id: v.id, label: v.name }))
-          this.firstStepChannelId = this.userVideoChannels[0].id
-        }
-      )
   }
 
   fileChange () {
index 261b8a130330970553dad91166949c2ffc6f73ba..158138bb3aab2ae23d29f1f446a38fcb04ac5ea1 100644 (file)
@@ -7,7 +7,7 @@
 
     <my-video-edit
       [form]="form" [formErrors]="formErrors"
-      [validationMessages]="validationMessages" [videoPrivacies]="videoPrivacies"
+      [validationMessages]="validationMessages" [videoPrivacies]="videoPrivacies" [userVideoChannels]="userVideoChannels"
     ></my-video-edit>
 
     <div class="submit-container">
index 08b74f4c33a36e3334a24c9818786da7de60e97a..941ef24782edb3cf06f5cdb267f3366600b2a0e1 100644 (file)
@@ -5,8 +5,10 @@ import { NotificationsService } from 'angular2-notifications'
 import 'rxjs/add/observable/forkJoin'
 import { VideoPrivacy } from '../../../../../shared/models/videos'
 import { ServerService } from '../../core'
+import { AuthService } from '../../core/auth'
 import { FormReactive } from '../../shared'
 import { ValidatorMessage } from '../../shared/forms/form-validators/validator-message'
+import { populateAsyncUserVideoChannels } from '../../shared/misc/utils'
 import { VideoEdit } from '../../shared/video/video-edit.model'
 import { VideoService } from '../../shared/video/video.service'
 
@@ -24,6 +26,7 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
   formErrors: { [ id: string ]: string } = {}
   validationMessages: ValidatorMessage = {}
   videoPrivacies = []
+  userVideoChannels = []
 
   constructor (
     private formBuilder: FormBuilder,
@@ -31,7 +34,8 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
     private router: Router,
     private notificationsService: NotificationsService,
     private serverService: ServerService,
-    private videoService: VideoService
+    private videoService: VideoService,
+    private authService: AuthService
   ) {
     super()
   }
@@ -44,7 +48,12 @@ export class VideoUpdateComponent extends FormReactive implements OnInit {
   ngOnInit () {
     this.buildForm()
 
-    this.videoPrivacies = this.serverService.getVideoPrivacies()
+    this.serverService.videoPrivaciesLoaded
+      .subscribe(
+        () => this.videoPrivacies = this.serverService.getVideoPrivacies()
+      )
+
+    populateAsyncUserVideoChannels(this.authService, this.userVideoChannels)
 
     const uuid: string = this.route.snapshot.params['uuid']
     this.videoService.getVideo(uuid)
index fdf5e3f67995bc62c8b39700727415b99dec7cac..4a709404d9d34afae783da4674bb60c891476767 100644 (file)
 }
 
 @mixin orange-button {
-  color: #fff;
-  background-color: $orange-color;
+  &, &:active, &:focus {
+    color: #fff;
+    background-color: $orange-color;
+  }
 
-  &:hover, &:active, &:focus {
+  &:hover {
     color: #fff;
     background-color: $orange-hoover-color;
   }
 }
 
 @mixin grey-button {
-  background-color: $grey-color;
-  color: #585858;
+  &, &:active, &:focus {
+    background-color: $grey-color;
+    color: #585858;
+  }
 
   &:hover, &:active, &:focus, &[disabled], &.disabled {
     color: #585858;
 }
 
 
-@mixin peertube-select ($width) {
-  background-color: #fff;
+@mixin peertube-select-container ($width) {
+  padding: 0;
+  margin: 0;
   border: 1px solid #C6C6C6;
-  height: $button-height;
   width: $width;
   border-radius: 3px;
-  padding-left: 15px;
-  padding-right: 15px;
+  overflow: hidden;
+  background: #fff;
+  position: relative;
+
+  &:after {
+    top: 50%;
+    right: calc(0% + 15px);
+    content: " ";
+    height: 0;
+    width: 0;
+    position: absolute;
+    pointer-events: none;
+    border: 5px solid rgba(0, 0, 0, 0);
+    border-top-color: #000000;
+    margin-top: -2px;
+    z-index: 100;
+  }
+
+  select {
+    padding: 0 12px;
+    width: calc(100% + 2px);
+    position: relative;
+    left: 1px;
+    border: none;
+    box-shadow: none;
+    background: transparent none;
+    appearance: none;
+    cursor: pointer;
+    height: $button-height;
+
+    &:focus {
+      outline: none;
+    }
+
+    &:-moz-focusring {
+      color: transparent;
+      text-shadow: 0 0 0 #000;
+    }
+  }
+}
+
+@mixin peertube-select-disabled-container ($width) {
+  @include peertube-select-container($width);
+
+  background-color: #E5E5E5;
+
+  select {
+    cursor: default;
+  }
 }