Skip to content

Commit 38f9d38

Browse files
committed
feat: enhance CSS editor with clipboard functionality and incorporated UI changes from PR review
1 parent bac7975 commit 38f9d38

File tree

14 files changed

+210
-30
lines changed

14 files changed

+210
-30
lines changed

packages/studio-web/src/app/app.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { PrivacyDialog } from "./app.component";
2020
import { ErrorPageComponent } from "./error-page/error-page.component";
2121
import { EditorComponent } from "./editor/editor.component";
2222
import { SharedModule } from "./shared/shared.module";
23+
import { WcStylingComponent } from "./shared/wc-styling/wc-styling.component";
2324

2425
defineCustomElements();
2526

@@ -34,6 +35,7 @@ defineCustomElements();
3435
ErrorPageComponent,
3536
EditorComponent,
3637
// ShepherdComponent
38+
WcStylingComponent,
3739
],
3840
imports: [
3941
BrowserModule,

packages/studio-web/src/app/editor/editor.component.html

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ <h2 i18n="Title for upload box" class="title">
5858
<div class="col col-lg-7">
5959
6060
</div>
61-
<div class="col-12 bg-fade col-lg-5 pt-lg-5 d-lg-flex flex-row" *ngIf="hasFile">
61+
<div class="col-12 bg-fade col-lg-5 pt-lg-5 d-lg-flex flex-row" *ngIf="rasFileIsLoaded">
6262
6363
6464
</div>
@@ -67,7 +67,11 @@ <h2 i18n="Title for upload box" class="title">
6767
class="d-flex justify-content-center flex-column flex-xl-row flex-xl-nowrap bg-light mt-3 full-height"
6868
>
6969
<div id="readalongContainer" #readalongContainer></div>
70-
<div [class.d-none]="!hasFile" [class.d-lg-flex]="hasFile" class="flex-row">
70+
<div
71+
[class.d-none]="!rasFileIsLoaded"
72+
[class.d-lg-flex]="rasFileIsLoaded"
73+
class="flex-row"
74+
>
7175
<div
7276
class="m-0 p-0 handle me-xl-2 d-none d-xl-block"
7377
#handle

packages/studio-web/src/app/editor/editor.component.sass

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
margin-right: 1em
3030
margin-top: 1em
3131
cursor: col-resize
32-
z-index: 1000
32+
z-index: 10
3333
display: block
3434
#styleWindow
3535
margin: 0

packages/studio-web/src/app/editor/editor.component.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export class EditorComponent implements OnDestroy, OnInit, AfterViewInit {
5454
htmlUploadAccepts = ".html";
5555

5656
unsubscribe$ = new Subject<void>();
57-
hasFile = false;
57+
rasFileIsLoaded = false;
5858
constructor(
5959
public b64Service: B64Service,
6060
private fileService: FileService,
@@ -143,7 +143,7 @@ export class EditorComponent implements OnDestroy, OnInit, AfterViewInit {
143143
}
144144
if (this.handleElement) {
145145
fromEvent(this.handleElement.nativeElement, "dragend")
146-
.pipe(takeUntil(this.unsubscribe$), debounceTime(100))
146+
.pipe(takeUntil(this.unsubscribe$))
147147
.subscribe((event) => {
148148
const ev = event as DragEvent;
149149
console.log("[DEBUG] dragged");
@@ -195,7 +195,7 @@ export class EditorComponent implements OnDestroy, OnInit, AfterViewInit {
195195
this.wcStylingService,
196196
);
197197
}
198-
this.hasFile = false;
198+
this.rasFileIsLoaded = false;
199199
}
200200

201201
download(download_type: SupportedOutputs) {
@@ -280,7 +280,7 @@ export class EditorComponent implements OnDestroy, OnInit, AfterViewInit {
280280
const readalong = await this.parseReadalong(text);
281281
this.loadAudioIntoWavesurferElement();
282282
this.renderReadalong(readalong);
283-
this.hasFile = true;
283+
this.rasFileIsLoaded = true;
284284
}
285285

286286
async renderReadalong(readalongBody: string | undefined) {
@@ -429,7 +429,8 @@ export class EditorComponent implements OnDestroy, OnInit, AfterViewInit {
429429
// stylesheet linked
430430

431431
const css = element.getAttribute("css-url");
432-
if (css !== null) {
432+
433+
if (css !== null && css.length > 0) {
433434
if (css.startsWith("data:text/css;base64,")) {
434435
this.wcStylingService.$wcStyleInput.next(
435436
this.b64Service.b64_to_utf8(css.substring(css.indexOf(",") + 1)),

packages/studio-web/src/app/shared/shared.module.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import { DownloadComponent } from "./download/download.component";
44
import { BrowserModule } from "@angular/platform-browser";
55
import { MaterialModule } from "../material.module";
66
import { FormsModule } from "@angular/forms";
7-
import { WcStylingComponent } from "./wc-styling/wc-styling.component";
7+
88
@NgModule({
9-
declarations: [DownloadComponent, WcStylingComponent],
9+
declarations: [DownloadComponent],
1010
imports: [BrowserModule, MaterialModule, FormsModule, CommonModule],
11-
exports: [DownloadComponent, WcStylingComponent],
11+
exports: [DownloadComponent],
1212
})
1313
export class SharedModule {}

packages/studio-web/src/app/shared/wc-styling/wc-styling-helper.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ <h2 mat-dialog-title i18n="Advanced styling">Advanced styling</h2>
33
<div class="style-helper">
44
<p>
55
To customize the look and feel of your read-along, you need to upload or
6-
write some Cascading Style Sheets in the styling section.
6+
write some Cascading Style Sheets (CSS) in the styling section.
77
</p>
88
<p>
99
We are providing a table below to help you customize. Please note that

packages/studio-web/src/app/shared/wc-styling/wc-styling.component.html

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,9 @@ <h1 class="title pt-2">
120120
</h3>
121121
</span>
122122

123-
<span class="ms-5 ms-md-0 p-0 pt-3 d-flex justify-content-end">
123+
<span
124+
class="ms-5 ms-md-0 p-0 pt-3 d-flex justify-content-end justify-content-xl-center"
125+
>
124126
<button
125127
i18n="Style download button"
126128
mat-button
@@ -136,6 +138,22 @@ <h1 class="title pt-2">
136138
</button>
137139
</span>
138140
</div>
141+
<div *ngIf="canUseClipBoard" class="row">
142+
<div class="col d-flex justify-content-center">
143+
<button
144+
mat-button
145+
(click)="copyStyle()"
146+
[disabled]="!(styleText$ | async)"
147+
>
148+
<mat-icon class="mat-icon-lg">content_copy</mat-icon
149+
><span i18n="copy">Copy</span>
150+
</button>
151+
<button mat-button (click)="pasteStyle()">
152+
<mat-icon class="mat-icon-lg">content_paste</mat-icon
153+
><span i18n="copy">Paste</span>
154+
</button>
155+
</div>
156+
</div>
139157
<div class="row">
140158
<mat-form-field class="col-12 p-0 b-0 ms-5 ms-md-0">
141159
<textarea

packages/studio-web/src/app/shared/wc-styling/wc-styling.component.sass

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,13 @@ code
4444
#style-section
4545
transform: rotate(0deg) translateX(0%)
4646
height: 70vh
47-
transition: height 1s
4847
width: 100%
4948

5049
#style-section.collapsed
5150
transform: rotate(90deg)
5251
transform-origin: bottom left
5352
width: 85em
5453
max-height: 4.7em
55-
transition: transform 1s
5654
max-width: calc( 55vh + 100px)
5755

5856
div.header > div

packages/studio-web/src/app/shared/wc-styling/wc-styling.component.ts

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export class WcStylingComponent implements OnDestroy, OnInit {
3131
@ViewChild("styleInputElement") styleInputElement: ElementRef;
3232
@ViewChild("fontInputElement") fontInputElement: ElementRef;
3333
@ViewChild("styleSection") styleSection: ElementRef;
34+
canUseClipBoard = false;
3435

3536
constructor(
3637
private toastr: ToastrService,
@@ -51,6 +52,20 @@ export class WcStylingComponent implements OnDestroy, OnInit {
5152
//this.collapsed = false;
5253
}
5354
});
55+
//check if clipboard access is allowed
56+
navigator.permissions
57+
.query({ name: "clipboard-write" as PermissionName })
58+
.then((result) => {
59+
if (result.state === "granted" || result.state === "prompt") {
60+
this.canUseClipBoard = true;
61+
} else {
62+
this.canUseClipBoard = false;
63+
}
64+
})
65+
.catch((err) => {
66+
console.error("Failed to query clipboard permissions", err);
67+
this.canUseClipBoard = false;
68+
});
5469
}
5570
onFontSelected(event: any) {
5671
const file: File = event.target.files[0];
@@ -107,6 +122,7 @@ export class WcStylingComponent implements OnDestroy, OnInit {
107122
.text()
108123
.then((val) => {
109124
this.styleText$.next(val);
125+
110126
this.wcStylingService.$wcStyleInput.next(val);
111127
this.inputType = "edit";
112128
this.toastr.success(
@@ -179,7 +195,6 @@ span.theme--dark.sentence__text {
179195
}
180196
toggleStyleInput(event: any) {
181197
this.inputType = event.value;
182-
this.collapsed$.next(false);
183198
}
184199
async ngOnInit() {
185200
this.wcStylingService.$wcStyleInput
@@ -206,6 +221,60 @@ span.theme--dark.sentence__text {
206221
toggleCollapse() {
207222
this.collapsed$.next(!this.collapsed$.getValue());
208223
}
224+
pasteStyle() {
225+
if (this.canUseClipBoard) {
226+
navigator.clipboard
227+
.readText()
228+
.then((text) => {
229+
this.styleText$.next(text);
230+
this.wcStylingService.$wcStyleInput.next(text);
231+
this.inputType = "edit";
232+
this.toastr.success(
233+
$localize`Style sheet pasted from clipboard.`,
234+
undefined,
235+
{ timeOut: 10000 },
236+
);
237+
})
238+
.catch((err) => {
239+
this.toastr.error($localize`Failed to read clipboard content.`, err, {
240+
timeOut: 2000,
241+
});
242+
});
243+
} else {
244+
this.toastr.error(
245+
$localize`Clipboard access is not allowed.`,
246+
$localize`Error`,
247+
);
248+
}
249+
}
250+
251+
copyStyle() {
252+
if (this.canUseClipBoard) {
253+
navigator.clipboard
254+
.writeText(this.styleText$.getValue())
255+
.then(() => {
256+
this.toastr.success(
257+
$localize`Style sheet copied to clipboard.`,
258+
undefined,
259+
{ timeOut: 10000 },
260+
);
261+
})
262+
.catch((err) => {
263+
this.toastr.error(
264+
$localize`Failed to copy style sheet to clipboard.`,
265+
err,
266+
{
267+
timeOut: 2000,
268+
},
269+
);
270+
});
271+
} else {
272+
this.toastr.error(
273+
$localize`Clipboard access is not allowed.`,
274+
$localize`Error`,
275+
);
276+
}
277+
}
209278
}
210279

211280
@Component({

packages/studio-web/src/i18n/messages.es.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"5192585697967841844": " Para obtener este documento, debería haber creado un ReadAlong utilizando el {$START_LINK}{$START_TAG_MAT_ICON}launch{$CLOSE_TAG_MAT_ICON} Studio{$CLOSE_LINK} y luego haber seleccionado «HTML offline» como formato. O puede utilizar el archivo HTML offline incluido en el formato de descarga Archivos web comprimidos. ",
1616
"2136422915588832930": "Barra de herramientas de audio",
1717
"7149012067459406545": "El fichero \"{$fileName}\" no es un fichero HTML.",
18+
"453238823906696872": "No se pudo cargar el fichero CSS \"{$PH}\".",
1819
"7819314041543176992": "Cerrar",
1920
"5701618810648052610": "Título",
2021
"1137319519199859335": "Subtítulo",
@@ -48,6 +49,8 @@
4849
"3796650812518266523": " Escriba o pegue sus hojas de estilos aquí ",
4950
"4934000757746180538": "{$START_TAG_MAT_ICON}save{$CLOSE_TAG_MAT_ICON} Guarde una copia",
5051
"7071695380382454380": "{$START_TAG_MAT_ICON}sync{$CLOSE_TAG_MAT_ICON} Aplicar",
52+
"4323470180912194028": "Copiar",
53+
"8890504809170904482": "Pegar",
5154
"7604046252874749392": "Opcional: utilice una fuente personalizada (.woff2)",
5255
"7719309746449095739": "Fichero ",
5356
"7466581557533667662": " no se pudo procesarlo.",
@@ -56,6 +59,12 @@
5659
"6899344040225872362": "¡Genial!",
5760
"5700078517045291790": " Contenido cargado en el cuadro de texto.",
5861
"6558433540988178003": "No hay texto para descargar.",
62+
"4533795875760195674": "Hojas de estilos pegadas desde el portapapeles.",
63+
"3715050097325047804": "Error al leer el contenido del portapapeles.",
64+
"5048497709983421225": "Acceso al portapapeles no permitido.",
65+
"1519954996184640001": "Error",
66+
"1205008396748653088": "Hojas de estilos copiadas al portapapeles.",
67+
"7492776625383944837": "Error al copiar las hojas de estilos al portapapeles.",
5968
"6731392928829867425": "Bienvenidos al Studio de ReadAlong",
6069
"1309246714146466494": "¡Crear un ReadAlong es fácil! Esta guía le mostrará todas las funcionalidades del Studio.",
6170
"3885497195825665706": "Próximo",

0 commit comments

Comments
 (0)