<div class="form-group">
<label i18n for="instanceDescription">Description</label><my-help helpType="markdownText"></my-help>
<my-markdown-textarea
- name="instanceDescription" formControlName="description" textareaWidth="500px" [previewColumn]="true"
+ name="instanceDescription" formControlName="description" textareaMaxWidth="500px"
[classes]="{ 'input-error': formErrors['instance.description'] }"
></my-markdown-textarea>
<div *ngIf="formErrors.instance.description" class="form-error">{{ formErrors.instance.description }}</div>
<div class="form-group">
<label i18n for="instanceTerms">Terms</label><my-help helpType="markdownText"></my-help>
<my-markdown-textarea
- name="instanceTerms" formControlName="terms" textareaWidth="500px" [previewColumn]="true"
+ name="instanceTerms" formControlName="terms" textareaMaxWidth="500px"
[ngClass]="{ 'input-error': formErrors['instance.terms'] }"
></my-markdown-textarea>
<div *ngIf="formErrors.instance.terms" class="form-error">{{ formErrors.instance.terms }}</div>
<div class="form-group">
<label i18n for="instanceCodeOfConduct">Code of conduct</label><my-help helpType="markdownText"></my-help>
<my-markdown-textarea
- name="instanceCodeOfConduct" formControlName="codeOfConduct" textareaWidth="500px" [previewColumn]="true"
+ name="instanceCodeOfConduct" formControlName="codeOfConduct" textareaMaxWidth="500px"
[ngClass]="{ 'input-error': formErrors['instance.codeOfConduct'] }"
></my-markdown-textarea>
<div *ngIf="formErrors.instance.codeOfConduct" class="form-error">{{ formErrors.instance.codeOfConduct }}</div>
<div i18n class="label-small-info">Who moderates the instance? What is the policy regarding NSFW videos? Political videos? etc</div>
<my-markdown-textarea
- name="instanceModerationInformation" formControlName="moderationInformation" textareaWidth="500px" [previewColumn]="true"
+ name="instanceModerationInformation" formControlName="moderationInformation" textareaMaxWidth="500px"
[ngClass]="{ 'input-error': formErrors['instance.moderationInformation'] }"
></my-markdown-textarea>
<div *ngIf="formErrors.instance.moderationInformation" class="form-error">{{ formErrors.instance.moderationInformation }}</div>
<div i18n class="label-small-info">A single person? A non-profit? A company?</div>
<my-markdown-textarea
- name="instanceAdministrator" formControlName="administrator" textareaWidth="500px" textareaHeight="75px" [previewColumn]="true"
+ name="instanceAdministrator" formControlName="administrator" textareaMaxWidth="500px" textareaHeight="75px"
[classes]="{ 'input-error': formErrors['instance.administrator'] }"
></my-markdown-textarea>
<div i18n class="label-small-info">i.e. 2vCore 2GB RAM, a direct the link to the server you rent, etc.</div>
<my-markdown-textarea
- name="instanceHardwareInformation" formControlName="hardwareInformation" textareaWidth="500px" textareaHeight="75px" [previewColumn]="true"
+ name="instanceHardwareInformation" formControlName="hardwareInformation" textareaMaxWidth="500px" textareaHeight="75px"
[classes]="{ 'input-error': formErrors['instance.hardwareInformation'] }"
></my-markdown-textarea>
pointer-events: none;
}
-my-markdown-textarea ::ng-deep {
- .root {
- @media screen and (max-width: 1400px) {
- flex-direction: column !important;
- }
-
- textarea {
- max-width: 100%;
- }
- }
-}
-
.form-group-right {
padding-top: 2px;
}
<my-markdown-textarea
*ngIf="setting.type === 'markdown-text'"
- markdownType="text" [id]="setting.name" [formControlName]="setting.name" textareaWidth="500px" [previewColumn]="false"
+ markdownType="text" [id]="setting.name" [formControlName]="setting.name" textareaWidth="500px"
[classes]="{ 'input-error': formErrors['settings.name'] }"
></my-markdown-textarea>
<my-markdown-textarea
*ngIf="setting.type === 'markdown-enhanced'"
- markdownType="enhanced" [id]="setting.name" [formControlName]="setting.name" textareaWidth="500px" [previewColumn]="false"
+ markdownType="enhanced" [id]="setting.name" [formControlName]="setting.name" textareaWidth="500px"
[classes]="{ 'input-error': formErrors['settings.name'] }"
></my-markdown-textarea>
{{ formErrors['display-name'] }}
</div>
</div>
-
+
<div class="form-group">
<label i18n for="description">Description</label>
<textarea
{{ formErrors.description }}
</div>
</div>
-
+
<div class="form-group">
<label for="support">Support</label>
<my-help
When you will upload a video in this channel, the video support field will be automatically filled by this text."
></my-help>
<my-markdown-textarea
- id="support" formControlName="support" textareaWidth="500px" [previewColumn]="true" markdownType="enhanced"
+ id="support" formControlName="support" textareaMaxWidth="500px" markdownType="enhanced"
[classes]="{ 'input-error': formErrors['support'] }"
></my-markdown-textarea>
<div *ngIf="formErrors.support" class="form-error">
{{ formErrors.support }}
</div>
</div>
-
+
<div class="form-group" *ngIf="isBulkUpdateVideosDisplayed()">
<my-peertube-checkbox
inputName="bulkVideosSupportUpdate" formControlName="bulkVideosSupportUpdate"
display: block;
}
-my-markdown-textarea ::ng-deep {
- .root {
- @media screen and (max-width: 1400px) {
- flex-direction: column !important;
- }
- }
-}
-
.peertube-select-container {
@include peertube-select-container(340px);
}
-<div class="root" [ngStyle]="{ 'flex-direction': flexDirection }">
- <textarea
+<div class="root" [ngClass]="{ 'maximized': isMaximized }" [ngStyle]="{ 'max-width': textareaMaxWidth }">
+ <textarea #textarea
[(ngModel)]="content" (ngModelChange)="onModelChange()"
class="form-control" [ngClass]="classes"
- [ngStyle]="{ width: textareaWidth, height: textareaHeight, 'margin-right': textareaMarginRight }"
+ [ngStyle]="{ height: textareaHeight }"
[id]="name" [name]="name">
</textarea>
- <div ngbNav #nav="ngbNav" class="nav-pills previews">
+ <div ngbNav #nav="ngbNav" class="nav-pills nav-preview">
<ng-container ngbNavItem *ngIf="truncate !== undefined">
<a ngbNavLink i18n>Truncated preview</a>
<div [innerHTML]="previewHTML"></div>
</ng-template>
</ng-container>
+
+ <my-button
+ *ngIf="!isMaximized" icon="fullscreen" (click)="onMaximizeClick()"
+ ></my-button>
+
+ <my-button
+ *ngIf="isMaximized" icon="exit-fullscreen" (click)="onMaximizeClick()"
+ ></my-button>
</div>
<div [ngbNavOutlet]="nav"></div>
@import '_variables';
@import '_mixins';
-.root {
- display: flex;
+$nav-preview-tab-height: 30px;
+$base-padding: 15px;
+$input-border-color: #C6C6C6;
+$input-border-radius: 3px;
+
+@mixin in-small-view {
+ .root {
+ display: flex;
+ flex-direction: column;
+
+ textarea {
+ @include peertube-textarea(100%, 150px);
+
+ background-color: var(--textareaBackgroundColor);
+ font-family: courier, monospace;
+ font-size: 13px;
+ border-bottom: none;
+ border-bottom-left-radius: unset;
+ border-bottom-right-radius: unset;
+ }
- textarea {
- @include peertube-textarea(100%, 150px);
+ .nav-preview {
+ display: block;
+ text-align: right;
+ padding-top: 10px;
+ padding-bottom: 10px;
+ padding-left: 10px;
+ padding-right: 10px;
+ border-top: 1px dashed $input-border-color;
+ border-left: 1px solid $input-border-color;
+ border-right: 1px solid $input-border-color;
+ border-bottom: 1px solid $input-border-color;
+ border-bottom-right-radius: $input-border-radius;
- margin-bottom: 15px;
+ border-bottom-left-radius: $input-border-radius;
+ ::ng-deep {
+ .nav-link {
+ display: none !important;
+ }
+
+ .grey-button {
+ padding: 0 12px 0 12px;
+ }
+ }
+ }
+
+ ::ng-deep {
+ .tab-content {
+ display: none;
+ }
+ }
}
+}
+
+@mixin nav-preview-medium {
+ display: flex;
+ flex-grow: 1;
+ border-bottom-left-radius: unset;
+ border-bottom-right-radius: unset;
+ border-bottom: 2px solid var(--mainColor);
- .previews {
- max-height: 150px;
- overflow-y: auto;
- flex-grow: 1;
+ :first-child {
+ margin-left: auto;
}
::ng-deep {
.nav-link {
display: flex !important;
align-items: center;
- height: 30px !important;
+ height: $nav-preview-tab-height !important;
padding: 0 15px !important;
font-size: 85% !important;
opacity: .7;
}
- .tab-content {
- min-height: 75px;
- padding: 15px;
- font-size: 15px;
- word-wrap: break-word;
+ .grey-button {
+ margin-left: 5px;
+ }
+ }
+}
+
+@mixin content-preview-base {
+ display: block;
+ min-height: 75px;
+ padding: $base-padding;
+ overflow-y: auto;
+ font-size: 15px;
+ word-wrap: break-word;
+}
+
+@mixin maximized-base {
+ flex-direction: row;
+ z-index: #{z(header) - 1};
+ position: fixed;
+ top: $header-height;
+ left: $menu-width;
+ max-height: none !important;
+ max-width: none !important;
+ width: calc(100% - #{$menu-width});
+ height: calc(100vh - #{$header-height}) !important;
+
+ $nav-preview-vertical-padding: 40px;
+
+ .nav-preview {
+ @include nav-preview-medium();
+ padding-top: #{$nav-preview-vertical-padding / 2};
+ padding-bottom: #{$nav-preview-vertical-padding / 2};
+ padding-left: 0px;
+ padding-right: 0px;
+ position: absolute;
+ background-color: var(--mainBackgroundColor);
+ width: 100% !important;
+ border-top: none;
+ border-left: none;
+ border-right: none;
+
+ :last-child {
+ margin-right: $not-expanded-horizontal-margins;
+ }
+ }
+
+ ::ng-deep .tab-content {
+ @include content-preview-base();
+ background-color: var(--mainBackgroundColor);
+ scrollbar-color: var(--actionButtonColor) var(--mainBackgroundColor);
+ }
+
+ textarea,
+ ::ng-deep .tab-content {
+ max-height: none !important;
+ max-width: none !important;
+ margin-top: #{$nav-preview-tab-height + $nav-preview-vertical-padding} !important;
+ height: calc(100vh - #{$header-height + $nav-preview-tab-height + $nav-preview-vertical-padding}) !important;
+ width: 50% !important;
+ border: none !important;
+ border-radius: unset !important;
+ }
+
+ :host-context(.expanded) {
+ .root.maximized {
+ left: 0;
+ width: 100%;
+ }
+ }
+}
+
+@mixin maximized-in-small-view {
+ .root.maximized {
+ @include maximized-base();
+
+ textarea {
+ display: none;
}
+
+ ::ng-deep .tab-content {
+ width: 100% !important;
+ }
+ }
+}
+
+@mixin maximized-tabs-in-mobile-view {
+ // Ellipsis on tabs for mobile view
+ .root.maximized {
+ .nav-preview {
+ ::ng-deep .nav-link {
+ @include ellipsis();
+
+ display: block !important;
+ max-width: 45% !important;
+ padding: 5px 0 !important;
+ margin-right: 10px !important;
+ text-align: center;
+
+ &:not(.active) {
+ max-width: 15% !important;
+ }
+
+ &.active {
+ padding: 5px 15px !important;
+ }
+ }
+ }
+ }
+}
+
+@mixin in-medium-view {
+ .root {
+ .nav-preview {
+ @include nav-preview-medium();
+ }
+
+ ::ng-deep .tab-content {
+ @include content-preview-base();
+ max-height: 210px;
+ border-bottom: 1px solid $input-border-color;
+ border-left: 1px solid $input-border-color;
+ border-right: 1px solid $input-border-color;
+ border-bottom-left-radius: $input-border-radius;
+ border-bottom-right-radius: $input-border-radius;
+ }
+ }
+}
+
+@mixin maximized-in-medium-view {
+ .root.maximized {
+ @include maximized-base();
+
+ textarea {
+ display: block;
+ padding: $base-padding;
+ border-right: 1px dashed $input-border-color !important;
+ resize: none;
+ scrollbar-color: var(--actionButtonColor) var(--textareaBackgroundColor);
+
+ &:focus {
+ box-shadow: none;
+ }
+ }
+ }
+}
+
+@include in-small-view();
+@include maximized-in-small-view();
+
+@media only screen and (max-width: $mobile-view) {
+ @include maximized-tabs-in-mobile-view();
+}
+
+@media only screen and (max-width: #{$mobile-view + $menu-width}) {
+ :host-context(.main-col:not(.expanded)) {
+ @include maximized-tabs-in-mobile-view();
+ }
+}
+
+@media only screen and (min-width: $small-view) {
+ :host-context(.expanded) {
+ @include in-medium-view();
+ }
+
+ @include maximized-in-medium-view();
+}
+
+@media only screen and (min-width: #{$small-view + $menu-width}) {
+ :host-context(.main-col:not(.expanded)) {
+ @include in-medium-view();
}
}
import { debounceTime, distinctUntilChanged } from 'rxjs/operators'
-import { Component, forwardRef, Input, OnInit } from '@angular/core'
+import { Component, forwardRef, Input, OnInit, ViewChild, ElementRef } from '@angular/core'
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'
import { Subject } from 'rxjs'
import truncate from 'lodash-es/truncate'
export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit {
@Input() content = ''
@Input() classes: string[] | { [klass: string]: any[] | any } = []
- @Input() textareaWidth = '100%'
+ @Input() textareaMaxWidth = '100%'
@Input() textareaHeight = '150px'
- @Input() previewColumn = false
@Input() truncate: number
@Input() markdownType: 'text' | 'enhanced' = 'text'
@Input() markdownVideo = false
@Input() name = 'description'
- textareaMarginRight = '0'
- flexDirection = 'column'
+ @ViewChild('textarea') textareaElement: ElementRef
+
truncatedPreviewHTML = ''
previewHTML = ''
+ isMaximized = false
private contentChanged = new Subject<string>()
.subscribe(() => this.updatePreviews())
this.contentChanged.next(this.content)
-
- if (this.previewColumn) {
- this.flexDirection = 'row'
- this.textareaMarginRight = '15px'
- }
}
propagateChange = (_: any) => { /* empty */ }
this.contentChanged.next(this.content)
}
- arePreviewsDisplayed () {
- return this.screenService.isInSmallView() === false
+ onMaximizeClick () {
+ this.isMaximized = !this.isMaximized
+
+ // Make sure textarea have the focus
+ this.textareaElement.nativeElement.focus()
+
+ // Make sure the window has no scrollbars
+ if (!this.isMaximized) {
+ this.unlockBodyScroll()
+ } else {
+ this.lockBodyScroll()
+ }
+ }
+
+ private lockBodyScroll () {
+ document.getElementById('content').classList.add('lock-scroll')
+ }
+
+ private unlockBodyScroll () {
+ document.getElementById('content').classList.remove('lock-scroll')
}
private async updatePreviews () {
'users': require('!!raw-loader?!../../../assets/images/global/users.svg').default,
'search': require('!!raw-loader?!../../../assets/images/global/search.svg').default,
'refresh': require('!!raw-loader?!../../../assets/images/global/refresh.svg').default,
- 'npm': require('!!raw-loader?!../../../assets/images/global/npm.svg').default
+ 'npm': require('!!raw-loader?!../../../assets/images/global/npm.svg').default,
+ 'fullscreen': require('!!raw-loader?!../../../assets/images/global/fullscreen.svg').default,
+ 'exit-fullscreen': require('!!raw-loader?!../../../assets/images/global/exit-fullscreen.svg').default
}
export type GlobalIconName = keyof typeof icons
<label i18n for="support">Support</label>
<my-help helpType="markdownEnhanced" i18n-preHtml preHtml="Short text to tell people how they can support you (membership platform...)."></my-help>
<my-markdown-textarea
- id="support" formControlName="support" textareaWidth="500px" [previewColumn]="true" markdownType="enhanced"
+ id="support" formControlName="support" markdownType="enhanced"
[classes]="{ 'input-error': formErrors['support'] }"
></my-markdown-textarea>
<div *ngIf="formErrors.support" class="form-error">
}
}
-::ng-deep my-markdown-textarea {
- .root {
- @include media-breakpoint-down(xl) {
- flex-direction: column !important;
- }
-
- textarea {
- max-width: 100%;
- }
- }
-}
-
@include ng2-tags;
// columns for the video
.col-video-edit {
@include media-breakpoint-up(md) {
@include make-col(8);
-
+
& + .col-video-edit {
@include make-col(4);
}
--- /dev/null
+<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs/>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linejoin="round">
+ <g id="Artboard-4" transform="translate(-400.000000, -1046.000000)" stroke="#333333" stroke-width="2">
+ <g id="Extras" transform="translate(48.000000, 1046.000000)">
+ <g id="exit-fullscreen" transform="translate(352.000000, 0.000000)">
+ <rect id="Rectangle-433" x="6" y="8" width="12" height="8"/>
+ <polyline id="Path-42" stroke-linecap="round" transform="translate(21.500000, 5.500000) scale(-1, -1) translate(-21.500000, -5.500000) " points="23 7 23 4 20 4"/>
+ <polyline id="Path-42" stroke-linecap="round" transform="translate(2.500000, 18.500000) scale(-1, -1) translate(-2.500000, -18.500000) " points="4 20 1 20 1 17"/>
+ <polyline id="Path-42" stroke-linecap="round" transform="translate(21.500000, 18.500000) scale(-1, 1) translate(-21.500000, -18.500000) " points="23 20 23 17 20 17"/>
+ <polyline id="Path-42" stroke-linecap="round" transform="translate(2.500000, 5.500000) scale(-1, 1) translate(-2.500000, -5.500000) " points="4 7 1 7 1 4"/>
+ </g>
+ </g>
+ </g>
+ </g>
+</svg>
--- /dev/null
+<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch -->
+ <title>fullscreen</title>
+ <desc>Created with Sketch.</desc>
+ <defs/>
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+ <g id="Artboard-4" transform="translate(-576.000000, -159.000000)" stroke="#333333" stroke-width="2">
+ <g id="33" transform="translate(576.000000, 159.000000)">
+ <rect id="Rectangle-433" x="1" y="4" width="22" height="16" rx="1"/>
+ <polyline id="Path-42" stroke-linecap="round" stroke-linejoin="round" points="20 10 20 7 17 7"/>
+ <polyline id="Path-42" stroke-linecap="round" stroke-linejoin="round" points="7 17 4 17 4 14"/>
+ <polyline id="Path-42" stroke-linecap="round" stroke-linejoin="round" transform="translate(18.500000, 15.500000) scale(1, -1) translate(-18.500000, -15.500000) " points="20 17 20 14 17 14"/>
+ <polyline id="Path-42" stroke-linecap="round" stroke-linejoin="round" transform="translate(5.500000, 8.500000) scale(1, -1) translate(-5.500000, -8.500000) " points="7 10 4 10 4 7"/>
+ </g>
+ </g>
+ </g>
+</svg>
--inputBackgroundColor: #{$input-background-color};
--inputPlaceholderColor: #{$input-placeholder-color};
+ --textareaBackgroundColor: #{$textarea-background-color};
+
--actionButtonColor: #{$grey-foreground-color};
--supportButtonBackgroundColor: #{transparent};
--supportButtonColor: #{var(--actionButtonColor)};
padding-right: $expanded-horizontal-margins;
}
}
+
+ &.lock-scroll .main-row > router-outlet + * {
+ // Lock and hide body scrollbars
+ position: fixed;
+
+ // Lock and hide sub-menu scrollbars
+ .sub-menu {
+ overflow-x: hidden;
+ }
+ }
}
.title-page {
margin-bottom: $sub-menu-margin-bottom-small-view;
}
+ my-markdown-textarea {
+ .root {
+ max-width: 100% !important;
+ }
+ }
+
input[type=text],
input[type=password],
input[type=email],
$input-background-color: $bg-color;
$input-placeholder-color: #898989;
+$textarea-background-color: $grey-background-hover-color;
+
$sub-menu-margin-bottom: 30px;
$sub-menu-margin-bottom-small-view: 10px;
--inputBackgroundColor: var(--inputBackgroundColor),
--inputPlaceholderColor: var(--inputPlaceholderColor),
+ --textareaBackgroundColor: var(--textareaBackgroundColor),
+
--actionButtonColor: var(--actionButtonColor),
--supportButtonColor: var(--supportButtonColor),
--supportButtonBackgroundColor: var(--supportButtonBackgroundColor),