diff options
author | Kim <1877318+kimsible@users.noreply.github.com> | 2020-04-28 14:53:43 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-04-28 14:53:43 +0200 |
commit | b15fe00f7409b27573e162192530bc73e3f918b1 (patch) | |
tree | a71df67cee37a60f4de573ca9347aa3262cd8463 | |
parent | 4682468d4d07e0864155dd2b403d93754786ea13 (diff) | |
download | PeerTube-b15fe00f7409b27573e162192530bc73e3f918b1.tar.gz PeerTube-b15fe00f7409b27573e162192530bc73e3f918b1.tar.zst PeerTube-b15fe00f7409b27573e162192530bc73e3f918b1.zip |
Add maximized mode to markdown-textarea + CSS improvements (#2660)
* Add arrows-angle-contract/expand bootstrap icons
* Add grey textarea-background-color
* Add maximized support to markdown-textarea + improve column display
* Refactor CSS + add ResizeObservable
* Replace bootstrap icons with softies
* Add ResizeObserver typing definition
* Add focus on textarea + Fix Observables
* Propage component changes on markdown plugins
* Ignore ResizeObserver not implemented in typescript yet
* Move observers from constructor to click event
* Add scss and css variables
* Replace textareaWidth with textareaMaxWidth to fix others textareas
* Clean unused css rules
* Fix ResizeObserver unknown by TypeScript compiler
* Set max-width: 100% for small and mobile views
* Fix textarea/preview height on maximized mode
* Add common padding textarea/preview side-by-side
* Hide scrollbar sub-menu on small-views
* Add maximized mode for mobile views
* Fix sass calculate syntax
* Revert custom CSS variable for inputBorderRadius and inputBorderColor
* Remove unsued methods
* Fix missing implement method
Co-authored-by: kimsible <kimsible@users.noreply.github.com>
15 files changed, 338 insertions, 78 deletions
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html index b925d3d02..5703d5a2e 100644 --- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html +++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.html | |||
@@ -37,7 +37,7 @@ | |||
37 | <div class="form-group"> | 37 | <div class="form-group"> |
38 | <label i18n for="instanceDescription">Description</label><my-help helpType="markdownText"></my-help> | 38 | <label i18n for="instanceDescription">Description</label><my-help helpType="markdownText"></my-help> |
39 | <my-markdown-textarea | 39 | <my-markdown-textarea |
40 | name="instanceDescription" formControlName="description" textareaWidth="500px" [previewColumn]="true" | 40 | name="instanceDescription" formControlName="description" textareaMaxWidth="500px" |
41 | [classes]="{ 'input-error': formErrors['instance.description'] }" | 41 | [classes]="{ 'input-error': formErrors['instance.description'] }" |
42 | ></my-markdown-textarea> | 42 | ></my-markdown-textarea> |
43 | <div *ngIf="formErrors.instance.description" class="form-error">{{ formErrors.instance.description }}</div> | 43 | <div *ngIf="formErrors.instance.description" class="form-error">{{ formErrors.instance.description }}</div> |
@@ -120,7 +120,7 @@ | |||
120 | <div class="form-group"> | 120 | <div class="form-group"> |
121 | <label i18n for="instanceTerms">Terms</label><my-help helpType="markdownText"></my-help> | 121 | <label i18n for="instanceTerms">Terms</label><my-help helpType="markdownText"></my-help> |
122 | <my-markdown-textarea | 122 | <my-markdown-textarea |
123 | name="instanceTerms" formControlName="terms" textareaWidth="500px" [previewColumn]="true" | 123 | name="instanceTerms" formControlName="terms" textareaMaxWidth="500px" |
124 | [ngClass]="{ 'input-error': formErrors['instance.terms'] }" | 124 | [ngClass]="{ 'input-error': formErrors['instance.terms'] }" |
125 | ></my-markdown-textarea> | 125 | ></my-markdown-textarea> |
126 | <div *ngIf="formErrors.instance.terms" class="form-error">{{ formErrors.instance.terms }}</div> | 126 | <div *ngIf="formErrors.instance.terms" class="form-error">{{ formErrors.instance.terms }}</div> |
@@ -129,7 +129,7 @@ | |||
129 | <div class="form-group"> | 129 | <div class="form-group"> |
130 | <label i18n for="instanceCodeOfConduct">Code of conduct</label><my-help helpType="markdownText"></my-help> | 130 | <label i18n for="instanceCodeOfConduct">Code of conduct</label><my-help helpType="markdownText"></my-help> |
131 | <my-markdown-textarea | 131 | <my-markdown-textarea |
132 | name="instanceCodeOfConduct" formControlName="codeOfConduct" textareaWidth="500px" [previewColumn]="true" | 132 | name="instanceCodeOfConduct" formControlName="codeOfConduct" textareaMaxWidth="500px" |
133 | [ngClass]="{ 'input-error': formErrors['instance.codeOfConduct'] }" | 133 | [ngClass]="{ 'input-error': formErrors['instance.codeOfConduct'] }" |
134 | ></my-markdown-textarea> | 134 | ></my-markdown-textarea> |
135 | <div *ngIf="formErrors.instance.codeOfConduct" class="form-error">{{ formErrors.instance.codeOfConduct }}</div> | 135 | <div *ngIf="formErrors.instance.codeOfConduct" class="form-error">{{ formErrors.instance.codeOfConduct }}</div> |
@@ -140,7 +140,7 @@ | |||
140 | <div i18n class="label-small-info">Who moderates the instance? What is the policy regarding NSFW videos? Political videos? etc</div> | 140 | <div i18n class="label-small-info">Who moderates the instance? What is the policy regarding NSFW videos? Political videos? etc</div> |
141 | 141 | ||
142 | <my-markdown-textarea | 142 | <my-markdown-textarea |
143 | name="instanceModerationInformation" formControlName="moderationInformation" textareaWidth="500px" [previewColumn]="true" | 143 | name="instanceModerationInformation" formControlName="moderationInformation" textareaMaxWidth="500px" |
144 | [ngClass]="{ 'input-error': formErrors['instance.moderationInformation'] }" | 144 | [ngClass]="{ 'input-error': formErrors['instance.moderationInformation'] }" |
145 | ></my-markdown-textarea> | 145 | ></my-markdown-textarea> |
146 | <div *ngIf="formErrors.instance.moderationInformation" class="form-error">{{ formErrors.instance.moderationInformation }}</div> | 146 | <div *ngIf="formErrors.instance.moderationInformation" class="form-error">{{ formErrors.instance.moderationInformation }}</div> |
@@ -161,7 +161,7 @@ | |||
161 | <div i18n class="label-small-info">A single person? A non-profit? A company?</div> | 161 | <div i18n class="label-small-info">A single person? A non-profit? A company?</div> |
162 | 162 | ||
163 | <my-markdown-textarea | 163 | <my-markdown-textarea |
164 | name="instanceAdministrator" formControlName="administrator" textareaWidth="500px" textareaHeight="75px" [previewColumn]="true" | 164 | name="instanceAdministrator" formControlName="administrator" textareaMaxWidth="500px" textareaHeight="75px" |
165 | [classes]="{ 'input-error': formErrors['instance.administrator'] }" | 165 | [classes]="{ 'input-error': formErrors['instance.administrator'] }" |
166 | ></my-markdown-textarea> | 166 | ></my-markdown-textarea> |
167 | 167 | ||
@@ -216,7 +216,7 @@ | |||
216 | <div i18n class="label-small-info">i.e. 2vCore 2GB RAM, a direct the link to the server you rent, etc.</div> | 216 | <div i18n class="label-small-info">i.e. 2vCore 2GB RAM, a direct the link to the server you rent, etc.</div> |
217 | 217 | ||
218 | <my-markdown-textarea | 218 | <my-markdown-textarea |
219 | name="instanceHardwareInformation" formControlName="hardwareInformation" textareaWidth="500px" textareaHeight="75px" [previewColumn]="true" | 219 | name="instanceHardwareInformation" formControlName="hardwareInformation" textareaMaxWidth="500px" textareaHeight="75px" |
220 | [classes]="{ 'input-error': formErrors['instance.hardwareInformation'] }" | 220 | [classes]="{ 'input-error': formErrors['instance.hardwareInformation'] }" |
221 | ></my-markdown-textarea> | 221 | ></my-markdown-textarea> |
222 | 222 | ||
diff --git a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss index d8bc30d55..9ee960ad6 100644 --- a/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss +++ b/client/src/app/+admin/config/edit-custom-config/edit-custom-config.component.scss | |||
@@ -68,18 +68,6 @@ textarea { | |||
68 | pointer-events: none; | 68 | pointer-events: none; |
69 | } | 69 | } |
70 | 70 | ||
71 | my-markdown-textarea ::ng-deep { | ||
72 | .root { | ||
73 | @media screen and (max-width: 1400px) { | ||
74 | flex-direction: column !important; | ||
75 | } | ||
76 | |||
77 | textarea { | ||
78 | max-width: 100%; | ||
79 | } | ||
80 | } | ||
81 | } | ||
82 | |||
83 | .form-group-right { | 71 | .form-group-right { |
84 | padding-top: 2px; | 72 | padding-top: 2px; |
85 | } | 73 | } |
diff --git a/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.html b/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.html index bf135ecbd..f3fc429ff 100644 --- a/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.html +++ b/client/src/app/+admin/plugins/plugin-show-installed/plugin-show-installed.component.html | |||
@@ -19,13 +19,13 @@ | |||
19 | 19 | ||
20 | <my-markdown-textarea | 20 | <my-markdown-textarea |
21 | *ngIf="setting.type === 'markdown-text'" | 21 | *ngIf="setting.type === 'markdown-text'" |
22 | markdownType="text" [id]="setting.name" [formControlName]="setting.name" textareaWidth="500px" [previewColumn]="false" | 22 | markdownType="text" [id]="setting.name" [formControlName]="setting.name" textareaWidth="500px" |
23 | [classes]="{ 'input-error': formErrors['settings.name'] }" | 23 | [classes]="{ 'input-error': formErrors['settings.name'] }" |
24 | ></my-markdown-textarea> | 24 | ></my-markdown-textarea> |
25 | 25 | ||
26 | <my-markdown-textarea | 26 | <my-markdown-textarea |
27 | *ngIf="setting.type === 'markdown-enhanced'" | 27 | *ngIf="setting.type === 'markdown-enhanced'" |
28 | markdownType="enhanced" [id]="setting.name" [formControlName]="setting.name" textareaWidth="500px" [previewColumn]="false" | 28 | markdownType="enhanced" [id]="setting.name" [formControlName]="setting.name" textareaWidth="500px" |
29 | [classes]="{ 'input-error': formErrors['settings.name'] }" | 29 | [classes]="{ 'input-error': formErrors['settings.name'] }" |
30 | ></my-markdown-textarea> | 30 | ></my-markdown-textarea> |
31 | 31 | ||
diff --git a/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-edit.component.html b/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-edit.component.html index 59ac5097b..048d143cd 100644 --- a/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-edit.component.html +++ b/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-edit.component.html | |||
@@ -59,7 +59,7 @@ | |||
59 | {{ formErrors['display-name'] }} | 59 | {{ formErrors['display-name'] }} |
60 | </div> | 60 | </div> |
61 | </div> | 61 | </div> |
62 | 62 | ||
63 | <div class="form-group"> | 63 | <div class="form-group"> |
64 | <label i18n for="description">Description</label> | 64 | <label i18n for="description">Description</label> |
65 | <textarea | 65 | <textarea |
@@ -70,7 +70,7 @@ | |||
70 | {{ formErrors.description }} | 70 | {{ formErrors.description }} |
71 | </div> | 71 | </div> |
72 | </div> | 72 | </div> |
73 | 73 | ||
74 | <div class="form-group"> | 74 | <div class="form-group"> |
75 | <label for="support">Support</label> | 75 | <label for="support">Support</label> |
76 | <my-help | 76 | <my-help |
@@ -78,14 +78,14 @@ | |||
78 | When you will upload a video in this channel, the video support field will be automatically filled by this text." | 78 | When you will upload a video in this channel, the video support field will be automatically filled by this text." |
79 | ></my-help> | 79 | ></my-help> |
80 | <my-markdown-textarea | 80 | <my-markdown-textarea |
81 | id="support" formControlName="support" textareaWidth="500px" [previewColumn]="true" markdownType="enhanced" | 81 | id="support" formControlName="support" textareaMaxWidth="500px" markdownType="enhanced" |
82 | [classes]="{ 'input-error': formErrors['support'] }" | 82 | [classes]="{ 'input-error': formErrors['support'] }" |
83 | ></my-markdown-textarea> | 83 | ></my-markdown-textarea> |
84 | <div *ngIf="formErrors.support" class="form-error"> | 84 | <div *ngIf="formErrors.support" class="form-error"> |
85 | {{ formErrors.support }} | 85 | {{ formErrors.support }} |
86 | </div> | 86 | </div> |
87 | </div> | 87 | </div> |
88 | 88 | ||
89 | <div class="form-group" *ngIf="isBulkUpdateVideosDisplayed()"> | 89 | <div class="form-group" *ngIf="isBulkUpdateVideosDisplayed()"> |
90 | <my-peertube-checkbox | 90 | <my-peertube-checkbox |
91 | inputName="bulkVideosSupportUpdate" formControlName="bulkVideosSupportUpdate" | 91 | inputName="bulkVideosSupportUpdate" formControlName="bulkVideosSupportUpdate" |
diff --git a/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-edit.component.scss b/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-edit.component.scss index 43a49a01a..8f8af655c 100644 --- a/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-edit.component.scss +++ b/client/src/app/+my-account/my-account-video-channels/my-account-video-channel-edit.component.scss | |||
@@ -48,14 +48,6 @@ textarea { | |||
48 | display: block; | 48 | display: block; |
49 | } | 49 | } |
50 | 50 | ||
51 | my-markdown-textarea ::ng-deep { | ||
52 | .root { | ||
53 | @media screen and (max-width: 1400px) { | ||
54 | flex-direction: column !important; | ||
55 | } | ||
56 | } | ||
57 | } | ||
58 | |||
59 | .peertube-select-container { | 51 | .peertube-select-container { |
60 | @include peertube-select-container(340px); | 52 | @include peertube-select-container(340px); |
61 | } | 53 | } |
diff --git a/client/src/app/shared/forms/markdown-textarea.component.html b/client/src/app/shared/forms/markdown-textarea.component.html index 3cadb3619..a519f3e0a 100644 --- a/client/src/app/shared/forms/markdown-textarea.component.html +++ b/client/src/app/shared/forms/markdown-textarea.component.html | |||
@@ -1,12 +1,12 @@ | |||
1 | <div class="root" [ngStyle]="{ 'flex-direction': flexDirection }"> | 1 | <div class="root" [ngClass]="{ 'maximized': isMaximized }" [ngStyle]="{ 'max-width': textareaMaxWidth }"> |
2 | <textarea | 2 | <textarea #textarea |
3 | [(ngModel)]="content" (ngModelChange)="onModelChange()" | 3 | [(ngModel)]="content" (ngModelChange)="onModelChange()" |
4 | class="form-control" [ngClass]="classes" | 4 | class="form-control" [ngClass]="classes" |
5 | [ngStyle]="{ width: textareaWidth, height: textareaHeight, 'margin-right': textareaMarginRight }" | 5 | [ngStyle]="{ height: textareaHeight }" |
6 | [id]="name" [name]="name"> | 6 | [id]="name" [name]="name"> |
7 | </textarea> | 7 | </textarea> |
8 | 8 | ||
9 | <div ngbNav #nav="ngbNav" class="nav-pills previews"> | 9 | <div ngbNav #nav="ngbNav" class="nav-pills nav-preview"> |
10 | <ng-container ngbNavItem *ngIf="truncate !== undefined"> | 10 | <ng-container ngbNavItem *ngIf="truncate !== undefined"> |
11 | <a ngbNavLink i18n>Truncated preview</a> | 11 | <a ngbNavLink i18n>Truncated preview</a> |
12 | 12 | ||
@@ -22,6 +22,14 @@ | |||
22 | <div [innerHTML]="previewHTML"></div> | 22 | <div [innerHTML]="previewHTML"></div> |
23 | </ng-template> | 23 | </ng-template> |
24 | </ng-container> | 24 | </ng-container> |
25 | |||
26 | <my-button | ||
27 | *ngIf="!isMaximized" icon="fullscreen" (click)="onMaximizeClick()" | ||
28 | ></my-button> | ||
29 | |||
30 | <my-button | ||
31 | *ngIf="isMaximized" icon="exit-fullscreen" (click)="onMaximizeClick()" | ||
32 | ></my-button> | ||
25 | </div> | 33 | </div> |
26 | 34 | ||
27 | <div [ngbNavOutlet]="nav"></div> | 35 | <div [ngbNavOutlet]="nav"></div> |
diff --git a/client/src/app/shared/forms/markdown-textarea.component.scss b/client/src/app/shared/forms/markdown-textarea.component.scss index bd02343de..065cd2dec 100644 --- a/client/src/app/shared/forms/markdown-textarea.component.scss +++ b/client/src/app/shared/forms/markdown-textarea.component.scss | |||
@@ -1,36 +1,250 @@ | |||
1 | @import '_variables'; | 1 | @import '_variables'; |
2 | @import '_mixins'; | 2 | @import '_mixins'; |
3 | 3 | ||
4 | .root { | 4 | $nav-preview-tab-height: 30px; |
5 | display: flex; | 5 | $base-padding: 15px; |
6 | $input-border-color: #C6C6C6; | ||
7 | $input-border-radius: 3px; | ||
8 | |||
9 | @mixin in-small-view { | ||
10 | .root { | ||
11 | display: flex; | ||
12 | flex-direction: column; | ||
13 | |||
14 | textarea { | ||
15 | @include peertube-textarea(100%, 150px); | ||
16 | |||
17 | background-color: var(--textareaBackgroundColor); | ||
18 | font-family: courier, monospace; | ||
19 | font-size: 13px; | ||
20 | border-bottom: none; | ||
21 | border-bottom-left-radius: unset; | ||
22 | border-bottom-right-radius: unset; | ||
23 | } | ||
6 | 24 | ||
7 | textarea { | 25 | .nav-preview { |
8 | @include peertube-textarea(100%, 150px); | 26 | display: block; |
27 | text-align: right; | ||
28 | padding-top: 10px; | ||
29 | padding-bottom: 10px; | ||
30 | padding-left: 10px; | ||
31 | padding-right: 10px; | ||
32 | border-top: 1px dashed $input-border-color; | ||
33 | border-left: 1px solid $input-border-color; | ||
34 | border-right: 1px solid $input-border-color; | ||
35 | border-bottom: 1px solid $input-border-color; | ||
36 | border-bottom-right-radius: $input-border-radius; | ||
9 | 37 | ||
10 | margin-bottom: 15px; | 38 | border-bottom-left-radius: $input-border-radius; |
39 | ::ng-deep { | ||
40 | .nav-link { | ||
41 | display: none !important; | ||
42 | } | ||
43 | |||
44 | .grey-button { | ||
45 | padding: 0 12px 0 12px; | ||
46 | } | ||
47 | } | ||
48 | } | ||
49 | |||
50 | ::ng-deep { | ||
51 | .tab-content { | ||
52 | display: none; | ||
53 | } | ||
54 | } | ||
11 | } | 55 | } |
56 | } | ||
57 | |||
58 | @mixin nav-preview-medium { | ||
59 | display: flex; | ||
60 | flex-grow: 1; | ||
61 | border-bottom-left-radius: unset; | ||
62 | border-bottom-right-radius: unset; | ||
63 | border-bottom: 2px solid var(--mainColor); | ||
12 | 64 | ||
13 | .previews { | 65 | :first-child { |
14 | max-height: 150px; | 66 | margin-left: auto; |
15 | overflow-y: auto; | ||
16 | flex-grow: 1; | ||
17 | } | 67 | } |
18 | 68 | ||
19 | ::ng-deep { | 69 | ::ng-deep { |
20 | .nav-link { | 70 | .nav-link { |
21 | display: flex !important; | 71 | display: flex !important; |
22 | align-items: center; | 72 | align-items: center; |
23 | height: 30px !important; | 73 | height: $nav-preview-tab-height !important; |
24 | padding: 0 15px !important; | 74 | padding: 0 15px !important; |
25 | font-size: 85% !important; | 75 | font-size: 85% !important; |
26 | opacity: .7; | 76 | opacity: .7; |
27 | } | 77 | } |
28 | 78 | ||
29 | .tab-content { | 79 | .grey-button { |
30 | min-height: 75px; | 80 | margin-left: 5px; |
31 | padding: 15px; | 81 | } |
32 | font-size: 15px; | 82 | } |
33 | word-wrap: break-word; | 83 | } |
84 | |||
85 | @mixin content-preview-base { | ||
86 | display: block; | ||
87 | min-height: 75px; | ||
88 | padding: $base-padding; | ||
89 | overflow-y: auto; | ||
90 | font-size: 15px; | ||
91 | word-wrap: break-word; | ||
92 | } | ||
93 | |||
94 | @mixin maximized-base { | ||
95 | flex-direction: row; | ||
96 | z-index: #{z(header) - 1}; | ||
97 | position: fixed; | ||
98 | top: $header-height; | ||
99 | left: $menu-width; | ||
100 | max-height: none !important; | ||
101 | max-width: none !important; | ||
102 | width: calc(100% - #{$menu-width}); | ||
103 | height: calc(100vh - #{$header-height}) !important; | ||
104 | |||
105 | $nav-preview-vertical-padding: 40px; | ||
106 | |||
107 | .nav-preview { | ||
108 | @include nav-preview-medium(); | ||
109 | padding-top: #{$nav-preview-vertical-padding / 2}; | ||
110 | padding-bottom: #{$nav-preview-vertical-padding / 2}; | ||
111 | padding-left: 0px; | ||
112 | padding-right: 0px; | ||
113 | position: absolute; | ||
114 | background-color: var(--mainBackgroundColor); | ||
115 | width: 100% !important; | ||
116 | border-top: none; | ||
117 | border-left: none; | ||
118 | border-right: none; | ||
119 | |||
120 | :last-child { | ||
121 | margin-right: $not-expanded-horizontal-margins; | ||
122 | } | ||
123 | } | ||
124 | |||
125 | ::ng-deep .tab-content { | ||
126 | @include content-preview-base(); | ||
127 | background-color: var(--mainBackgroundColor); | ||
128 | scrollbar-color: var(--actionButtonColor) var(--mainBackgroundColor); | ||
129 | } | ||
130 | |||
131 | textarea, | ||
132 | ::ng-deep .tab-content { | ||
133 | max-height: none !important; | ||
134 | max-width: none !important; | ||
135 | margin-top: #{$nav-preview-tab-height + $nav-preview-vertical-padding} !important; | ||
136 | height: calc(100vh - #{$header-height + $nav-preview-tab-height + $nav-preview-vertical-padding}) !important; | ||
137 | width: 50% !important; | ||
138 | border: none !important; | ||
139 | border-radius: unset !important; | ||
140 | } | ||
141 | |||
142 | :host-context(.expanded) { | ||
143 | .root.maximized { | ||
144 | left: 0; | ||
145 | width: 100%; | ||
146 | } | ||
147 | } | ||
148 | } | ||
149 | |||
150 | @mixin maximized-in-small-view { | ||
151 | .root.maximized { | ||
152 | @include maximized-base(); | ||
153 | |||
154 | textarea { | ||
155 | display: none; | ||
34 | } | 156 | } |
157 | |||
158 | ::ng-deep .tab-content { | ||
159 | width: 100% !important; | ||
160 | } | ||
161 | } | ||
162 | } | ||
163 | |||
164 | @mixin maximized-tabs-in-mobile-view { | ||
165 | // Ellipsis on tabs for mobile view | ||
166 | .root.maximized { | ||
167 | .nav-preview { | ||
168 | ::ng-deep .nav-link { | ||
169 | @include ellipsis(); | ||
170 | |||
171 | display: block !important; | ||
172 | max-width: 45% !important; | ||
173 | padding: 5px 0 !important; | ||
174 | margin-right: 10px !important; | ||
175 | text-align: center; | ||
176 | |||
177 | &:not(.active) { | ||
178 | max-width: 15% !important; | ||
179 | } | ||
180 | |||
181 | &.active { | ||
182 | padding: 5px 15px !important; | ||
183 | } | ||
184 | } | ||
185 | } | ||
186 | } | ||
187 | } | ||
188 | |||
189 | @mixin in-medium-view { | ||
190 | .root { | ||
191 | .nav-preview { | ||
192 | @include nav-preview-medium(); | ||
193 | } | ||
194 | |||
195 | ::ng-deep .tab-content { | ||
196 | @include content-preview-base(); | ||
197 | max-height: 210px; | ||
198 | border-bottom: 1px solid $input-border-color; | ||
199 | border-left: 1px solid $input-border-color; | ||
200 | border-right: 1px solid $input-border-color; | ||
201 | border-bottom-left-radius: $input-border-radius; | ||
202 | border-bottom-right-radius: $input-border-radius; | ||
203 | } | ||
204 | } | ||
205 | } | ||
206 | |||
207 | @mixin maximized-in-medium-view { | ||
208 | .root.maximized { | ||
209 | @include maximized-base(); | ||
210 | |||
211 | textarea { | ||
212 | display: block; | ||
213 | padding: $base-padding; | ||
214 | border-right: 1px dashed $input-border-color !important; | ||
215 | resize: none; | ||
216 | scrollbar-color: var(--actionButtonColor) var(--textareaBackgroundColor); | ||
217 | |||
218 | &:focus { | ||
219 | box-shadow: none; | ||
220 | } | ||
221 | } | ||
222 | } | ||
223 | } | ||
224 | |||
225 | @include in-small-view(); | ||
226 | @include maximized-in-small-view(); | ||
227 | |||
228 | @media only screen and (max-width: $mobile-view) { | ||
229 | @include maximized-tabs-in-mobile-view(); | ||
230 | } | ||
231 | |||
232 | @media only screen and (max-width: #{$mobile-view + $menu-width}) { | ||
233 | :host-context(.main-col:not(.expanded)) { | ||
234 | @include maximized-tabs-in-mobile-view(); | ||
235 | } | ||
236 | } | ||
237 | |||
238 | @media only screen and (min-width: $small-view) { | ||
239 | :host-context(.expanded) { | ||
240 | @include in-medium-view(); | ||
241 | } | ||
242 | |||
243 | @include maximized-in-medium-view(); | ||
244 | } | ||
245 | |||
246 | @media only screen and (min-width: #{$small-view + $menu-width}) { | ||
247 | :host-context(.main-col:not(.expanded)) { | ||
248 | @include in-medium-view(); | ||
35 | } | 249 | } |
36 | } | 250 | } |
diff --git a/client/src/app/shared/forms/markdown-textarea.component.ts b/client/src/app/shared/forms/markdown-textarea.component.ts index cbcfdfe78..dde7b4d98 100644 --- a/client/src/app/shared/forms/markdown-textarea.component.ts +++ b/client/src/app/shared/forms/markdown-textarea.component.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import { debounceTime, distinctUntilChanged } from 'rxjs/operators' | 1 | import { debounceTime, distinctUntilChanged } from 'rxjs/operators' |
2 | import { Component, forwardRef, Input, OnInit } from '@angular/core' | 2 | import { Component, forwardRef, Input, OnInit, ViewChild, ElementRef } from '@angular/core' |
3 | import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' | 3 | import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms' |
4 | import { Subject } from 'rxjs' | 4 | import { Subject } from 'rxjs' |
5 | import truncate from 'lodash-es/truncate' | 5 | import truncate from 'lodash-es/truncate' |
@@ -22,18 +22,18 @@ import { MarkdownService } from '@app/shared/renderer' | |||
22 | export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit { | 22 | export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit { |
23 | @Input() content = '' | 23 | @Input() content = '' |
24 | @Input() classes: string[] | { [klass: string]: any[] | any } = [] | 24 | @Input() classes: string[] | { [klass: string]: any[] | any } = [] |
25 | @Input() textareaWidth = '100%' | 25 | @Input() textareaMaxWidth = '100%' |
26 | @Input() textareaHeight = '150px' | 26 | @Input() textareaHeight = '150px' |
27 | @Input() previewColumn = false | ||
28 | @Input() truncate: number | 27 | @Input() truncate: number |
29 | @Input() markdownType: 'text' | 'enhanced' = 'text' | 28 | @Input() markdownType: 'text' | 'enhanced' = 'text' |
30 | @Input() markdownVideo = false | 29 | @Input() markdownVideo = false |
31 | @Input() name = 'description' | 30 | @Input() name = 'description' |
32 | 31 | ||
33 | textareaMarginRight = '0' | 32 | @ViewChild('textarea') textareaElement: ElementRef |
34 | flexDirection = 'column' | 33 | |
35 | truncatedPreviewHTML = '' | 34 | truncatedPreviewHTML = '' |
36 | previewHTML = '' | 35 | previewHTML = '' |
36 | isMaximized = false | ||
37 | 37 | ||
38 | private contentChanged = new Subject<string>() | 38 | private contentChanged = new Subject<string>() |
39 | 39 | ||
@@ -51,11 +51,6 @@ export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit { | |||
51 | .subscribe(() => this.updatePreviews()) | 51 | .subscribe(() => this.updatePreviews()) |
52 | 52 | ||
53 | this.contentChanged.next(this.content) | 53 | this.contentChanged.next(this.content) |
54 | |||
55 | if (this.previewColumn) { | ||
56 | this.flexDirection = 'row' | ||
57 | this.textareaMarginRight = '15px' | ||
58 | } | ||
59 | } | 54 | } |
60 | 55 | ||
61 | propagateChange = (_: any) => { /* empty */ } | 56 | propagateChange = (_: any) => { /* empty */ } |
@@ -80,8 +75,26 @@ export class MarkdownTextareaComponent implements ControlValueAccessor, OnInit { | |||
80 | this.contentChanged.next(this.content) | 75 | this.contentChanged.next(this.content) |
81 | } | 76 | } |
82 | 77 | ||
83 | arePreviewsDisplayed () { | 78 | onMaximizeClick () { |
84 | return this.screenService.isInSmallView() === false | 79 | this.isMaximized = !this.isMaximized |
80 | |||
81 | // Make sure textarea have the focus | ||
82 | this.textareaElement.nativeElement.focus() | ||
83 | |||
84 | // Make sure the window has no scrollbars | ||
85 | if (!this.isMaximized) { | ||
86 | this.unlockBodyScroll() | ||
87 | } else { | ||
88 | this.lockBodyScroll() | ||
89 | } | ||
90 | } | ||
91 | |||
92 | private lockBodyScroll () { | ||
93 | document.getElementById('content').classList.add('lock-scroll') | ||
94 | } | ||
95 | |||
96 | private unlockBodyScroll () { | ||
97 | document.getElementById('content').classList.remove('lock-scroll') | ||
85 | } | 98 | } |
86 | 99 | ||
87 | private async updatePreviews () { | 100 | private async updatePreviews () { |
diff --git a/client/src/app/shared/images/global-icon.component.ts b/client/src/app/shared/images/global-icon.component.ts index a8e5a7020..d2700f6c3 100644 --- a/client/src/app/shared/images/global-icon.component.ts +++ b/client/src/app/shared/images/global-icon.component.ts | |||
@@ -54,7 +54,9 @@ const icons = { | |||
54 | 'users': require('!!raw-loader?!../../../assets/images/global/users.svg').default, | 54 | 'users': require('!!raw-loader?!../../../assets/images/global/users.svg').default, |
55 | 'search': require('!!raw-loader?!../../../assets/images/global/search.svg').default, | 55 | 'search': require('!!raw-loader?!../../../assets/images/global/search.svg').default, |
56 | 'refresh': require('!!raw-loader?!../../../assets/images/global/refresh.svg').default, | 56 | 'refresh': require('!!raw-loader?!../../../assets/images/global/refresh.svg').default, |
57 | 'npm': require('!!raw-loader?!../../../assets/images/global/npm.svg').default | 57 | 'npm': require('!!raw-loader?!../../../assets/images/global/npm.svg').default, |
58 | 'fullscreen': require('!!raw-loader?!../../../assets/images/global/fullscreen.svg').default, | ||
59 | 'exit-fullscreen': require('!!raw-loader?!../../../assets/images/global/exit-fullscreen.svg').default | ||
58 | } | 60 | } |
59 | 61 | ||
60 | export type GlobalIconName = keyof typeof icons | 62 | export type GlobalIconName = keyof typeof icons |
diff --git a/client/src/app/videos/+video-edit/shared/video-edit.component.html b/client/src/app/videos/+video-edit/shared/video-edit.component.html index d59394538..9a0e4f848 100644 --- a/client/src/app/videos/+video-edit/shared/video-edit.component.html +++ b/client/src/app/videos/+video-edit/shared/video-edit.component.html | |||
@@ -232,7 +232,7 @@ | |||
232 | <label i18n for="support">Support</label> | 232 | <label i18n for="support">Support</label> |
233 | <my-help helpType="markdownEnhanced" i18n-preHtml preHtml="Short text to tell people how they can support you (membership platform...)."></my-help> | 233 | <my-help helpType="markdownEnhanced" i18n-preHtml preHtml="Short text to tell people how they can support you (membership platform...)."></my-help> |
234 | <my-markdown-textarea | 234 | <my-markdown-textarea |
235 | id="support" formControlName="support" textareaWidth="500px" [previewColumn]="true" markdownType="enhanced" | 235 | id="support" formControlName="support" markdownType="enhanced" |
236 | [classes]="{ 'input-error': formErrors['support'] }" | 236 | [classes]="{ 'input-error': formErrors['support'] }" |
237 | ></my-markdown-textarea> | 237 | ></my-markdown-textarea> |
238 | <div *ngIf="formErrors.support" class="form-error"> | 238 | <div *ngIf="formErrors.support" class="form-error"> |
diff --git a/client/src/app/videos/+video-edit/shared/video-edit.component.scss b/client/src/app/videos/+video-edit/shared/video-edit.component.scss index de800b03f..2f9067132 100644 --- a/client/src/app/videos/+video-edit/shared/video-edit.component.scss +++ b/client/src/app/videos/+video-edit/shared/video-edit.component.scss | |||
@@ -161,18 +161,6 @@ p-calendar { | |||
161 | } | 161 | } |
162 | } | 162 | } |
163 | 163 | ||
164 | ::ng-deep my-markdown-textarea { | ||
165 | .root { | ||
166 | @include media-breakpoint-down(xl) { | ||
167 | flex-direction: column !important; | ||
168 | } | ||
169 | |||
170 | textarea { | ||
171 | max-width: 100%; | ||
172 | } | ||
173 | } | ||
174 | } | ||
175 | |||
176 | @include ng2-tags; | 164 | @include ng2-tags; |
177 | 165 | ||
178 | // columns for the video | 166 | // columns for the video |
@@ -200,7 +188,7 @@ p-calendar { | |||
200 | .col-video-edit { | 188 | .col-video-edit { |
201 | @include media-breakpoint-up(md) { | 189 | @include media-breakpoint-up(md) { |
202 | @include make-col(8); | 190 | @include make-col(8); |
203 | 191 | ||
204 | & + .col-video-edit { | 192 | & + .col-video-edit { |
205 | @include make-col(4); | 193 | @include make-col(4); |
206 | } | 194 | } |
diff --git a/client/src/assets/images/global/exit-fullscreen.svg b/client/src/assets/images/global/exit-fullscreen.svg new file mode 100644 index 000000000..ba01f583c --- /dev/null +++ b/client/src/assets/images/global/exit-fullscreen.svg | |||
@@ -0,0 +1,16 @@ | |||
1 | <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"> | ||
2 | <defs/> | ||
3 | <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linejoin="round"> | ||
4 | <g id="Artboard-4" transform="translate(-400.000000, -1046.000000)" stroke="#333333" stroke-width="2"> | ||
5 | <g id="Extras" transform="translate(48.000000, 1046.000000)"> | ||
6 | <g id="exit-fullscreen" transform="translate(352.000000, 0.000000)"> | ||
7 | <rect id="Rectangle-433" x="6" y="8" width="12" height="8"/> | ||
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"/> | ||
9 | <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"/> | ||
10 | <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"/> | ||
11 | <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"/> | ||
12 | </g> | ||
13 | </g> | ||
14 | </g> | ||
15 | </g> | ||
16 | </svg> | ||
diff --git a/client/src/assets/images/global/fullscreen.svg b/client/src/assets/images/global/fullscreen.svg new file mode 100644 index 000000000..4a9d67864 --- /dev/null +++ b/client/src/assets/images/global/fullscreen.svg | |||
@@ -0,0 +1,17 @@ | |||
1 | <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"> | ||
2 | <!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch --> | ||
3 | <title>fullscreen</title> | ||
4 | <desc>Created with Sketch.</desc> | ||
5 | <defs/> | ||
6 | <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> | ||
7 | <g id="Artboard-4" transform="translate(-576.000000, -159.000000)" stroke="#333333" stroke-width="2"> | ||
8 | <g id="33" transform="translate(576.000000, 159.000000)"> | ||
9 | <rect id="Rectangle-433" x="1" y="4" width="22" height="16" rx="1"/> | ||
10 | <polyline id="Path-42" stroke-linecap="round" stroke-linejoin="round" points="20 10 20 7 17 7"/> | ||
11 | <polyline id="Path-42" stroke-linecap="round" stroke-linejoin="round" points="7 17 4 17 4 14"/> | ||
12 | <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"/> | ||
13 | <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"/> | ||
14 | </g> | ||
15 | </g> | ||
16 | </g> | ||
17 | </svg> | ||
diff --git a/client/src/sass/application.scss b/client/src/sass/application.scss index cc6552705..ff6ce394f 100644 --- a/client/src/sass/application.scss +++ b/client/src/sass/application.scss | |||
@@ -38,6 +38,8 @@ body { | |||
38 | --inputBackgroundColor: #{$input-background-color}; | 38 | --inputBackgroundColor: #{$input-background-color}; |
39 | --inputPlaceholderColor: #{$input-placeholder-color}; | 39 | --inputPlaceholderColor: #{$input-placeholder-color}; |
40 | 40 | ||
41 | --textareaBackgroundColor: #{$textarea-background-color}; | ||
42 | |||
41 | --actionButtonColor: #{$grey-foreground-color}; | 43 | --actionButtonColor: #{$grey-foreground-color}; |
42 | --supportButtonBackgroundColor: #{transparent}; | 44 | --supportButtonBackgroundColor: #{transparent}; |
43 | --supportButtonColor: #{var(--actionButtonColor)}; | 45 | --supportButtonColor: #{var(--actionButtonColor)}; |
@@ -144,6 +146,16 @@ label { | |||
144 | padding-right: $expanded-horizontal-margins; | 146 | padding-right: $expanded-horizontal-margins; |
145 | } | 147 | } |
146 | } | 148 | } |
149 | |||
150 | &.lock-scroll .main-row > router-outlet + * { | ||
151 | // Lock and hide body scrollbars | ||
152 | position: fixed; | ||
153 | |||
154 | // Lock and hide sub-menu scrollbars | ||
155 | .sub-menu { | ||
156 | overflow-x: hidden; | ||
157 | } | ||
158 | } | ||
147 | } | 159 | } |
148 | 160 | ||
149 | .title-page { | 161 | .title-page { |
@@ -304,6 +316,12 @@ table { | |||
304 | margin-bottom: $sub-menu-margin-bottom-small-view; | 316 | margin-bottom: $sub-menu-margin-bottom-small-view; |
305 | } | 317 | } |
306 | 318 | ||
319 | my-markdown-textarea { | ||
320 | .root { | ||
321 | max-width: 100% !important; | ||
322 | } | ||
323 | } | ||
324 | |||
307 | input[type=text], | 325 | input[type=text], |
308 | input[type=password], | 326 | input[type=password], |
309 | input[type=email], | 327 | input[type=email], |
diff --git a/client/src/sass/include/_variables.scss b/client/src/sass/include/_variables.scss index cf7657550..72eb7b61e 100644 --- a/client/src/sass/include/_variables.scss +++ b/client/src/sass/include/_variables.scss | |||
@@ -68,6 +68,8 @@ $theater-bottom-space: 115px; | |||
68 | $input-background-color: $bg-color; | 68 | $input-background-color: $bg-color; |
69 | $input-placeholder-color: #898989; | 69 | $input-placeholder-color: #898989; |
70 | 70 | ||
71 | $textarea-background-color: $grey-background-hover-color; | ||
72 | |||
71 | $sub-menu-margin-bottom: 30px; | 73 | $sub-menu-margin-bottom: 30px; |
72 | $sub-menu-margin-bottom-small-view: 10px; | 74 | $sub-menu-margin-bottom-small-view: 10px; |
73 | 75 | ||
@@ -95,6 +97,8 @@ $variables: ( | |||
95 | --inputBackgroundColor: var(--inputBackgroundColor), | 97 | --inputBackgroundColor: var(--inputBackgroundColor), |
96 | --inputPlaceholderColor: var(--inputPlaceholderColor), | 98 | --inputPlaceholderColor: var(--inputPlaceholderColor), |
97 | 99 | ||
100 | --textareaBackgroundColor: var(--textareaBackgroundColor), | ||
101 | |||
98 | --actionButtonColor: var(--actionButtonColor), | 102 | --actionButtonColor: var(--actionButtonColor), |
99 | --supportButtonColor: var(--supportButtonColor), | 103 | --supportButtonColor: var(--supportButtonColor), |
100 | --supportButtonBackgroundColor: var(--supportButtonBackgroundColor), | 104 | --supportButtonBackgroundColor: var(--supportButtonBackgroundColor), |