From 68bb8d5989f59e6ecd6c40a5c716861a1d5e211c Mon Sep 17 00:00:00 2001 From: Koen <98043234+koen1711@users.noreply.github.com> Date: Fri, 26 Apr 2024 10:33:49 +0200 Subject: [PATCH] chore: add Dark mode + some rewrites (#248) * chore: start on scss rewrite * chore: rewrite done, added themes * chore: almost done with themes * chore: almost done with themes.... * chore: finished themes * chore: fix other merge conflicts * fix: make CONNECT_PYTHON_ROBOT button primary * chore: prettier * chore: fix some mistakes * chore: fix select color * chore: fix all issues * chore: fix last issues * chore: prettier * fix: oops * chore: fix last issues... (again) * chore: prettier * fix: more contrast to file explorer names * chore: fix color of second selection screen * chore: last header border color * chore: last few styling issues * chore: prettier * fix: oops * fix: modal -> dialog * chore: remove change of color on file explorer * fix: background overrides all props, also when none * fix: duplicate keys * chore: fix last styling issue * chore: prettier * fix: change text color of upload log --- src/app/effects/app.effects.ts | 24 ++- src/app/effects/blockly-editor.effects.ts | 151 ++++++++++-------- .../blockly-editor/blockly-editor.page.scss | 2 +- .../code-view/code-view.component.scss | 2 + .../code-view/code-view.component.ts | 25 ++- .../code-editor-cpp/code-editor-cpp.page.ts | 15 +- .../code-editor-python.page.scss | 1 + .../code-editor-python.page.ts | 11 +- .../components/header/header.component.html | 55 ++++++- .../components/header/header.component.scss | 19 ++- .../components/header/header.component.ts | 8 + .../robot-selection.component.html | 3 +- .../robot-selection.component.scss | 12 +- .../robot-selection.component.ts | 3 - .../components/start/start.component.scss | 2 + .../connect-python/connect-python.dialog.html | 6 +- .../connect-python/connect-python.dialog.scss | 5 +- .../debug-information.dialog.scss | 11 +- .../examples/examples-dialog.component.scss | 7 +- .../file-explorer/file-explorer.dialog.html | 2 +- .../file-explorer/file-explorer.dialog.scss | 12 +- .../location-select.dialog.scss | 1 + .../button-bar/button-bar.component.scss | 2 +- .../serial-output.component.scss | 2 +- .../services/{toolbox => blockly}/category.ts | 0 src/app/services/blockly/theme.ts | 75 +++++++++ .../services/{toolbox => blockly}/toolbox.ts | 0 src/app/state/app.state.ts | 15 ++ src/app/state/blockly-editor.state.ts | 8 + src/assets/i18n/en.json | 3 + src/assets/i18n/nl.json | 3 + src/styles.scss | 91 +++++++---- src/theme.scss | 126 ++++++++++++++- 33 files changed, 544 insertions(+), 158 deletions(-) rename src/app/services/{toolbox => blockly}/category.ts (100%) create mode 100644 src/app/services/blockly/theme.ts rename src/app/services/{toolbox => blockly}/toolbox.ts (100%) diff --git a/src/app/effects/app.effects.ts b/src/app/effects/app.effects.ts index ef23b66c..3806ce88 100644 --- a/src/app/effects/app.effects.ts +++ b/src/app/effects/app.effects.ts @@ -9,6 +9,7 @@ import { MatDialog } from "@angular/material/dialog"; import { ChangeLogDialog } from "../modules/core/dialogs/change-log/change-log.dialog"; import showdown from "showdown"; import { WorkspaceService } from "../services/workspace.service"; +import { BlocklyEditorEffects } from "./blockly-editor.effects"; @Injectable({ providedIn: "root", @@ -21,7 +22,29 @@ export class AppEffects { private localStorage: LocalStorageService, private dialog: MatDialog, private workspaceService: WorkspaceService, + private blocklyEffects: BlocklyEditorEffects, ) { + this.appState.selectedTheme = localStorage.fetch("theme") || "light"; + + this.appState.selectedTheme$ + .pipe(filter((theme) => !!theme)) + .subscribe((theme) => { + document + .getElementsByTagName("body")[0] + .setAttribute("data-theme", theme); + + document + .getElementsByTagName("body")[0] + .setAttribute("data-bs-theme", theme); + + localStorage.store("theme", theme); + if ( + this.appState.selectedCodeEditor == CodeEditorType.Beginner + ) { + this.blocklyEffects.loadTheme(); + } + }); + // Use the current language to translate the angular strings this.appState.currentLanguage$ .pipe(filter((language) => !!language)) @@ -137,7 +160,6 @@ export class AppEffects { this.localStorage.store("releaseVersion", releaseVersion); const robotId = this.localStorage.fetch("changedLanguage"); - console.log(robotId); if (robotId) { this.localStorage.store("changedLanguage", ""); this.workspaceService diff --git a/src/app/effects/blockly-editor.effects.ts b/src/app/effects/blockly-editor.effects.ts index bc965f5b..d0e0d4fe 100644 --- a/src/app/effects/blockly-editor.effects.ts +++ b/src/app/effects/blockly-editor.effects.ts @@ -13,11 +13,10 @@ import { blocks, CATEGORIES, EXTENSIONS, - THEME, translations, } from "@leaphy-robotics/leaphy-blocks"; -import { LeaphyCategory } from "../services/toolbox/category"; -import { LeaphyToolbox } from "../services/toolbox/toolbox"; +import { LeaphyCategory } from "../services/blockly/category"; +import { LeaphyToolbox } from "../services/blockly/toolbox"; import { CodeEditorState } from "../state/code-editor.state"; import { genericRobotType, @@ -28,6 +27,7 @@ import { RobotType } from "../domain/robot.type"; import { WorkspaceService } from "../services/workspace.service"; import { LocalStorageService } from "../services/localstorage.service"; import PinSelectorField from "../domain/blockly-fields"; +import getTheme from "../services/blockly/theme"; @Injectable({ providedIn: "root", @@ -36,6 +36,72 @@ import PinSelectorField from "../domain/blockly-fields"; // Defines the effects on the Blockly Editor that different state changes have export class BlocklyEditorEffects { private firstRun = true; + private startWorkspaceXml = ``; + private baseToolboxXml = ``; + private leaphyToolboxXml = ``; + + private readonly darkTheme = getTheme("dark"); + private readonly lightTheme = getTheme("light"); + + public async loadTheme() { + const workspace = this.blocklyState.workspace; + const darkMode = + document + .getElementsByTagName("body")[0] + .getAttribute("data-theme") === "dark"; + const blocklyTheme = darkMode ? this.darkTheme : this.lightTheme; + workspace.setTheme(blocklyTheme); + workspace.refreshTheme(); + } + + public async loadBlockly( + element, + robotType: RobotType, + config: Blockly.BlocklyOptions, + ) { + const translation = translations[this.appState.currentLanguageCode]; + if (robotType === leaphyFlitzNanoRobotType) + translation.ARD_SERVO_WRITE = translation.ARD_SERVO_ARM_WRITE; + else translation.ARD_SERVO_WRITE = translation.ARD_SERVO_REGULAR_WRITE; + + Blockly.setLocale(translation); + + PinSelectorField.processPinMappings(robotType); + if (this.firstRun) { + this.firstRun = false; + Blockly.defineBlocksWithJsonArray(blocks); + } + + const toolboxXmlString = this.loadToolBox(robotType); + config.toolbox = toolboxXmlString; + // @ts-ignore + const workspace = Blockly.inject(element, config); + const darkMode = + document + .getElementsByTagName("body")[0] + .getAttribute("data-theme") === "dark"; + const blocklyTheme = darkMode ? this.darkTheme : this.lightTheme; + workspace.setTheme(blocklyTheme); + const toolbox = workspace.getToolbox(); + workspace.registerToolboxCategoryCallback("LISTS", CATEGORIES.LISTS); + toolbox.getFlyout().autoClose = false; + const xml = Blockly.utils.xml.textToDom(this.startWorkspaceXml); + Blockly.Xml.domToWorkspace(xml, workspace); + this.blocklyState.workspace = workspace; + this.blocklyState.toolboxXml = toolboxXmlString; + if (this.appState.currentEditor == CodeEditorType.Beginner) { + this.workspaceService.restoreWorkspaceTemp().then(() => {}); + } + toolbox.selectItemByPosition(0); + toolbox.refreshTheme(); + + setTimeout( + () => + (this.blocklyState.isSideNavOpen = + robotType.features.showCodeOnStart), + 200, + ); + } constructor( private blocklyState: BlocklyEditorState, @@ -45,6 +111,10 @@ export class BlocklyEditorEffects { private workspaceService: WorkspaceService, private localStorage: LocalStorageService, ) { + this.getXmlContent("./assets/blockly/leaphy-start.xml").subscribe( + (xml) => (this.startWorkspaceXml = xml), + ); + Blockly.defineBlocksWithJsonArray(blocks); Blockly.fieldRegistry.register("field_pin_selector", PinSelectorField); Blockly.registry.register( @@ -119,68 +189,17 @@ export class BlocklyEditorEffects { withLatestFrom( this.getXmlContent("./assets/blockly/base-toolbox.xml"), this.getXmlContent("./assets/blockly/leaphy-toolbox.xml"), - this.getXmlContent("./assets/blockly/leaphy-start.xml"), ), ) .subscribe( - ([ + async ([ [[element, config], robotType], baseToolboxXml, leaphyToolboxXml, - startWorkspaceXml, ]) => { - const translation = - translations[this.appState.currentLanguageCode]; - if (robotType === leaphyFlitzNanoRobotType) - translation.ARD_SERVO_WRITE = - translation.ARD_SERVO_ARM_WRITE; - else - translation.ARD_SERVO_WRITE = - translation.ARD_SERVO_REGULAR_WRITE; - - Blockly.setLocale(translation); - - PinSelectorField.processPinMappings(robotType); - config.theme = Blockly.Theme.defineTheme("leaphy", { - blockStyles: THEME.defaultBlockStyles, - categoryStyles: THEME.categoryStyles, - componentStyles: THEME.componentStyles, - name: "leaphy", - }); - const toolboxXmlString = this.loadToolBox( - baseToolboxXml, - leaphyToolboxXml, - robotType, - ); - config.toolbox = toolboxXmlString; - // @ts-ignore - const workspace = Blockly.inject(element, config); - const toolbox = workspace.getToolbox(); - workspace.registerToolboxCategoryCallback( - "LISTS", - CATEGORIES.LISTS, - ); - toolbox.getFlyout().autoClose = false; - const xml = Blockly.utils.xml.textToDom(startWorkspaceXml); - Blockly.Xml.domToWorkspace(xml, workspace); - this.blocklyState.workspace = workspace; - this.blocklyState.toolboxXml = toolboxXmlString; - if ( - this.appState.currentEditor == CodeEditorType.Beginner - ) { - this.workspaceService - .restoreWorkspaceTemp() - .then(() => {}); - } - toolbox.selectItemByPosition(0); - toolbox.refreshTheme(); - - setTimeout( - () => - (this.blocklyState.isSideNavOpen = - robotType.features.showCodeOnStart), - 200, - ); + this.baseToolboxXml = baseToolboxXml; + this.leaphyToolboxXml = leaphyToolboxXml; + await this.loadBlockly(element, robotType, config); }, ); @@ -209,11 +228,7 @@ export class BlocklyEditorEffects { leaphyToolboxXml, startWorkspaceXml, ]) => { - this.blocklyState.toolboxXml = this.loadToolBox( - baseToolboxXml, - leaphyToolboxXml, - robotType, - ); + this.blocklyState.toolboxXml = this.loadToolBox(robotType); workspace.clear(); const xml = Blockly.utils.xml.textToDom(startWorkspaceXml); @@ -338,19 +353,15 @@ export class BlocklyEditorEffects { return category; } - private loadToolBox( - baseToolboxXml: string, - leaphyToolboxXml: string, - robotType: RobotType, - ): string { + private loadToolBox(robotType: RobotType): string { const parser = new DOMParser(); const toolboxXmlDoc = parser.parseFromString( - baseToolboxXml, + this.baseToolboxXml, "text/xml", ); const toolboxElement = toolboxXmlDoc.getElementById("easyBloqsToolbox"); const leaphyCategories = parser.parseFromString( - leaphyToolboxXml, + this.leaphyToolboxXml, "text/xml", ); const leaphyRobotCategory = leaphyCategories.getElementById( diff --git a/src/app/modules/blockly-editor/blockly-editor.page.scss b/src/app/modules/blockly-editor/blockly-editor.page.scss index 9d34ef94..4bbd6d4e 100644 --- a/src/app/modules/blockly-editor/blockly-editor.page.scss +++ b/src/app/modules/blockly-editor/blockly-editor.page.scss @@ -9,7 +9,7 @@ .sidenav-container { flex: 1 1 auto; - background: #eee; + background: transparent !important; } #sidenav { diff --git a/src/app/modules/blockly-editor/components/code-view/code-view.component.scss b/src/app/modules/blockly-editor/components/code-view/code-view.component.scss index 2b52e80d..67aa3f25 100644 --- a/src/app/modules/blockly-editor/components/code-view/code-view.component.scss +++ b/src/app/modules/blockly-editor/components/code-view/code-view.component.scss @@ -3,4 +3,6 @@ height: 100%; margin: 0; overflow-x: scroll; + // set the scrollbar color to #9c9a9a + scrollbar-color: #ccc #9c9a9a; } diff --git a/src/app/modules/blockly-editor/components/code-view/code-view.component.ts b/src/app/modules/blockly-editor/components/code-view/code-view.component.ts index 4fac14fc..489141e9 100644 --- a/src/app/modules/blockly-editor/components/code-view/code-view.component.ts +++ b/src/app/modules/blockly-editor/components/code-view/code-view.component.ts @@ -1,5 +1,13 @@ -import { Component } from "@angular/core"; +import { + AfterViewInit, + ChangeDetectorRef, + Component, + OnInit, +} from "@angular/core"; import { CodeEditorState } from "../../../../state/code-editor.state"; +import { editor } from "monaco-editor"; +import IStandaloneEditorConstructionOptions = editor.IStandaloneEditorConstructionOptions; +import { AppState } from "../../../../state/app.state"; @Component({ selector: "app-code-view", @@ -7,11 +15,22 @@ import { CodeEditorState } from "../../../../state/code-editor.state"; styleUrls: ["./code-view.component.scss"], }) export class CodeViewComponent { - editorOptions = { + editorOptions: IStandaloneEditorConstructionOptions = { language: "cpp", readOnly: true, automaticLayout: true, + theme: "vs", }; - constructor(public codeEditor: CodeEditorState) {} + constructor( + private cdr: ChangeDetectorRef, + public codeEditor: CodeEditorState, + private appState: AppState, + ) { + // check if we are currently in dark mode + const isDarkMode = this.appState.selectedTheme === "dark"; + if (isDarkMode) { + this.editorOptions.theme = "vs-dark"; + } + } } diff --git a/src/app/modules/code-editor-cpp/code-editor-cpp.page.ts b/src/app/modules/code-editor-cpp/code-editor-cpp.page.ts index 8702b8dd..49a5e503 100644 --- a/src/app/modules/code-editor-cpp/code-editor-cpp.page.ts +++ b/src/app/modules/code-editor-cpp/code-editor-cpp.page.ts @@ -5,6 +5,9 @@ import { SharedModule } from "../shared/shared.module"; import { CoreModule } from "../core/core.module"; import { WorkspaceService } from "../../services/workspace.service"; import { MonacoEditorModule } from "ngx-monaco-editor-v2"; +import { AppState } from "../../state/app.state"; +import { editor } from "monaco-editor"; +import IStandaloneEditorConstructionOptions = editor.IStandaloneEditorConstructionOptions; @Component({ standalone: true, @@ -14,15 +17,23 @@ import { MonacoEditorModule } from "ngx-monaco-editor-v2"; imports: [CommonModule, SharedModule, CoreModule, MonacoEditorModule], }) export class CodeEditorCppPage implements AfterViewInit { - editorOptions = { + editorOptions: IStandaloneEditorConstructionOptions = { language: "cpp", automaticLayout: true, + theme: "vs", }; constructor( public codeEditorState: CodeEditorState, private workspaceService: WorkspaceService, - ) {} + private appState: AppState, + ) { + // check if we are currently in dark mode + const isDarkMode = appState.selectedTheme === "dark"; + if (isDarkMode) { + this.editorOptions.theme = "vs-dark"; + } + } ngAfterViewInit(): void { window.addEventListener("beforeunload", async () => { diff --git a/src/app/modules/code-editor-python/code-editor-python.page.scss b/src/app/modules/code-editor-python/code-editor-python.page.scss index ee901bd8..3bcc2dba 100644 --- a/src/app/modules/code-editor-python/code-editor-python.page.scss +++ b/src/app/modules/code-editor-python/code-editor-python.page.scss @@ -10,6 +10,7 @@ height: calc(100% - 64px); width: 100%; overflow: hidden; + background-color: var(--leaphy-background-color); } .sidenav-container { diff --git a/src/app/modules/code-editor-python/code-editor-python.page.ts b/src/app/modules/code-editor-python/code-editor-python.page.ts index 45a2fd92..bd15653e 100644 --- a/src/app/modules/code-editor-python/code-editor-python.page.ts +++ b/src/app/modules/code-editor-python/code-editor-python.page.ts @@ -5,6 +5,7 @@ import { SharedModule } from "../shared/shared.module"; import { CoreModule } from "../core/core.module"; import { WorkspaceService } from "../../services/workspace.service"; import { MonacoEditorModule } from "ngx-monaco-editor-v2"; +import { AppState } from "../../state/app.state"; @Component({ selector: "app-code-editor-python", @@ -17,12 +18,20 @@ export class CodeEditorPythonPage implements AfterViewInit { editorOptions = { language: "python", automaticLayout: true, + theme: "vs", }; constructor( public codeEditorState: CodeEditorState, private workspaceService: WorkspaceService, - ) {} + private appState: AppState, + ) { + // check if we are currently in dark mode + const isDarkMode = appState.selectedTheme === "dark"; + if (isDarkMode) { + this.editorOptions.theme = "vs-dark"; + } + } ngAfterViewInit(): void { window.addEventListener("beforeunload", async () => { diff --git a/src/app/modules/components/header/header.component.html b/src/app/modules/components/header/header.component.html index 57dd1153..05e66be4 100644 --- a/src/app/modules/components/header/header.component.html +++ b/src/app/modules/components/header/header.component.html @@ -31,20 +31,36 @@ } - - - @if ( (this.appState.selectedRobotType$ | async) !== microPythonRobotType ) { - } @@ -95,7 +111,7 @@ " (click)="onCodeEditorClicked()" > - editoreditor{{ "CODE" | translate }} } @@ -107,12 +123,16 @@ " (click)="onCodeEditorClicked()" > - {{ "BLOCKS" | translate }} } @if (this.appState.selectedRobotType$ | async) { @@ -243,6 +263,10 @@ language {{ "LANGUAGE" | translate }} + } + + + + + diff --git a/src/app/modules/components/header/header.component.scss b/src/app/modules/components/header/header.component.scss index 83739336..934c9eef 100644 --- a/src/app/modules/components/header/header.component.scss +++ b/src/app/modules/components/header/header.component.scss @@ -1,11 +1,12 @@ .robot-select-dropdown { padding-top: 5%; padding-right: 5%; + color: white; } .header-container { - background-color: var(--leaphy-color-primary); padding: 10px 0; /* Added padding to prevent content from being too close to the edges */ + background-color: var(--leaphy-header-bg); } .header-logo { @@ -27,21 +28,30 @@ button { font-size: 12px; line-height: 36px; height: 40px; + color: var(--leaphy-text-color); + + .selected { + color: var(--leaphy-header-icon-color) !important; + } mat-icon { - color: var(--leaphy-color-primary); + color: var(--leaphy-header-icon-color); margin-right: 5px; } } [mat-stroked-button], [mat-flat-button] { - color: var(--leaphy-color-light) !important; - border-color: var(--leaphy-color-secundary) !important; + background-color: transparent; + border-color: var(--leaphy-header-button-border) !important; font-size: 12px; font-weight: normal; border-radius: 20px; padding: 5px 15px; /* Added padding to buttons */ + + span { + color: var(--leaphy-color-light); + } } #block-icon { @@ -98,6 +108,7 @@ mat-button-toggle { align-content: center; mat-icon { + color: var(--leaphy-color-light) !important; width: 100%; height: 100%; margin-top: 15px; diff --git a/src/app/modules/components/header/header.component.ts b/src/app/modules/components/header/header.component.ts index e6ea62be..b3194b79 100644 --- a/src/app/modules/components/header/header.component.ts +++ b/src/app/modules/components/header/header.component.ts @@ -23,6 +23,7 @@ import { StatusMessageDialog } from "../../core/dialogs/status-message/status-me import { WorkspaceService } from "../../../services/workspace.service"; import { MatSelectChange } from "@angular/material/select"; import { RobotType } from "../../../domain/robot.type"; +import { BlocklyEditorEffects } from "../../../effects/blockly-editor.effects"; @Component({ selector: "app-header", @@ -36,6 +37,7 @@ export class HeaderComponent { constructor( public appState: AppState, public blocklyState: BlocklyEditorState, + public blocklyEffect: BlocklyEditorEffects, public dialogState: DialogState, public robotWiredState: RobotWiredState, private router: Router, @@ -268,4 +270,10 @@ export class HeaderComponent { } protected readonly genericRobots = genericRobots; + + onThemeChanged(theme: any) { + this.appState.selectedTheme = theme; + } + + protected readonly document = document; } diff --git a/src/app/modules/components/robot-selection/robot-selection.component.html b/src/app/modules/components/robot-selection/robot-selection.component.html index 3271fcce..ea145be0 100644 --- a/src/app/modules/components/robot-selection/robot-selection.component.html +++ b/src/app/modules/components/robot-selection/robot-selection.component.html @@ -1,4 +1,5 @@ -
+ +
@for (row of robots; track row) {
diff --git a/src/app/modules/components/robot-selection/robot-selection.component.scss b/src/app/modules/components/robot-selection/robot-selection.component.scss index 0fa0bb13..222a965e 100644 --- a/src/app/modules/components/robot-selection/robot-selection.component.scss +++ b/src/app/modules/components/robot-selection/robot-selection.component.scss @@ -1,17 +1,17 @@ .robot-name { margin-top: 10px; + color: var(--leaphy-text-color); } .robot-button { - background-color: #ffffff; - border: solid 1px #f1f1f1; + background-color: var(--leaphy-robot-tile) !important; + border: solid 1px var(--leaphy-robot-tile-border) !important; width: 10vw; max-width: 180px !important; aspect-ratio: 1 / 1; &:hover, &:active { - background-color: #ffffff !important; border: 3px solid var(--leaphy-color-primary) !important; } } @@ -19,6 +19,7 @@ .robot-button img { max-width: 90%; max-height: 90%; + filter: var(--leaphy-robot-icon-filter); } .selected { @@ -42,6 +43,11 @@ top: 0; height: calc(100vh - 64px); width: 50vw; + background-color: var(--start-screen-bg); +} + +.right-select { + background-color: var(--leaphy-robot-right-bg); } .robot-row { diff --git a/src/app/modules/components/robot-selection/robot-selection.component.ts b/src/app/modules/components/robot-selection/robot-selection.component.ts index 2697b2e5..6f27ffc5 100644 --- a/src/app/modules/components/robot-selection/robot-selection.component.ts +++ b/src/app/modules/components/robot-selection/robot-selection.component.ts @@ -20,21 +20,18 @@ import { "center", style({ translate: "-50%", - background: "#ffffff00", }), ), state( "left", style({ translate: "-100%", - background: "#ffffff00", }), ), state( "right", style({ translate: "0", - background: "#fff", }), ), transition("center => left", [animate(".3s ease-out")]), diff --git a/src/app/modules/components/start/start.component.scss b/src/app/modules/components/start/start.component.scss index 2d884b44..ee3c8ac6 100644 --- a/src/app/modules/components/start/start.component.scss +++ b/src/app/modules/components/start/start.component.scss @@ -6,4 +6,6 @@ justify-content: center; flex-direction: column; position: relative; + background-color: var(--start-screen-bg); + color: var(--leaphy-text-color); } diff --git a/src/app/modules/core/dialogs/connect-python/connect-python.dialog.html b/src/app/modules/core/dialogs/connect-python/connect-python.dialog.html index 928badeb..10210002 100644 --- a/src/app/modules/core/dialogs/connect-python/connect-python.dialog.html +++ b/src/app/modules/core/dialogs/connect-python/connect-python.dialog.html @@ -26,9 +26,9 @@ - {{ "FLASH_FIRMWARE" | translate }} + } @if (didUpload | async) {
} diff --git a/src/app/modules/core/dialogs/file-explorer/file-explorer.dialog.scss b/src/app/modules/core/dialogs/file-explorer/file-explorer.dialog.scss index 45435f23..f6316ff0 100644 --- a/src/app/modules/core/dialogs/file-explorer/file-explorer.dialog.scss +++ b/src/app/modules/core/dialogs/file-explorer/file-explorer.dialog.scss @@ -4,15 +4,15 @@ align-items: center; padding: 0 1rem; height: 3rem; - background-color: #f5f5f5; border-bottom: 1px solid #e0e0e0; } .current-path { + background-color: transparent; display: flex; align-items: center; font-size: 0.9rem; - color: #757575; + color: var(--leaphy-text-color); } .folder-content { @@ -21,19 +21,21 @@ flex-direction: column; height: 30vw; overflow-y: auto; - background-color: #f5f5f5; } .item { margin-left: 10px; } +.file { + color: var(--leaphy-text-color) !important; +} + .folder-content-header-name { font-size: 0.9rem; - color: #757575; + color: var(--leaphy-text-color); } .folder-content-header-name:hover { cursor: pointer; - color: #000000; } diff --git a/src/app/modules/core/dialogs/location-select/location-select.dialog.scss b/src/app/modules/core/dialogs/location-select/location-select.dialog.scss index 9edd85fa..6e79e468 100644 --- a/src/app/modules/core/dialogs/location-select/location-select.dialog.scss +++ b/src/app/modules/core/dialogs/location-select/location-select.dialog.scss @@ -34,6 +34,7 @@ width: 100%; align-content: center; flex: 1; + color: var(--leaphy-text-color); } } } diff --git a/src/app/modules/shared/components/button-bar/button-bar.component.scss b/src/app/modules/shared/components/button-bar/button-bar.component.scss index e85dfe7b..bf3eab27 100644 --- a/src/app/modules/shared/components/button-bar/button-bar.component.scss +++ b/src/app/modules/shared/components/button-bar/button-bar.component.scss @@ -26,7 +26,7 @@ } &:enabled::before { - background: var(--leaphy-color-dark); + background-color: var(--leaphy-color-dark); border-radius: 5px; content: attr(tooltip); display: inline-block; diff --git a/src/app/modules/shared/components/serial-output/serial-output.component.scss b/src/app/modules/shared/components/serial-output/serial-output.component.scss index 306cfe63..228a4829 100644 --- a/src/app/modules/shared/components/serial-output/serial-output.component.scss +++ b/src/app/modules/shared/components/serial-output/serial-output.component.scss @@ -23,7 +23,7 @@ height: calc(100% + 29px); margin: 0; overflow-y: scroll; - background-color: aliceblue; + background-color: var(--start-screen-bg); } [mat-stroked-button], diff --git a/src/app/services/toolbox/category.ts b/src/app/services/blockly/category.ts similarity index 100% rename from src/app/services/toolbox/category.ts rename to src/app/services/blockly/category.ts diff --git a/src/app/services/blockly/theme.ts b/src/app/services/blockly/theme.ts new file mode 100644 index 00000000..510b1c82 --- /dev/null +++ b/src/app/services/blockly/theme.ts @@ -0,0 +1,75 @@ +// check if user is using dark or light theme +import * as Blockly from "blockly"; +import { ITheme, Theme } from "blockly/core/theme"; + +function getTheme(themeStr): Theme { + let theme: ITheme; + if (themeStr === "dark") { + theme = { + blockStyles: { + leaphy_blocks: { colourPrimary: "#066c80", hat: "cap" }, + loop_blocks: { colourPrimary: "#69530d" }, + math_blocks: { colourPrimary: "#45662a" }, + text_blocks: { colourPrimary: "#45662a" }, + logic_blocks: { colourPrimary: "#45662a" }, + variable_blocks: { colourPrimary: "#87451a" }, + list_blocks: { colourPrimary: "#3f144a" }, + procedure_blocks: { colourPrimary: "#06416c" }, + }, + + categoryStyles: { + leaphy_category: { colour: "#066c80" }, + situation_category: { colour: "#69530d" }, + numbers_category: { colour: "#45662a" }, + variables_category: { colour: "#87451a" }, + lists_category: { colour: "#3f144a" }, + functions_category: { colour: "#06416c" }, + }, + componentStyles: { + toolboxBackgroundColour: "#343444", + toolboxForegroundColour: "#fff", + flyoutBackgroundColour: "#1e1e1e", + flyoutForegroundColour: "#ccc", + scrollbarColour: "#9c9a9a", + flyoutOpacity: 1, + }, + name: "dark", + }; + } else if (themeStr === "light") { + theme = { + blockStyles: { + leaphy_blocks: { colourPrimary: "#06778f", hat: "cap" }, + loop_blocks: { colourPrimary: "#D9B53F" }, + math_blocks: { colourPrimary: "#75B342" }, + text_blocks: { colourPrimary: "#75B342" }, + logic_blocks: { colourPrimary: "#75B342" }, + variable_blocks: { colourPrimary: "#DE7C3B" }, + list_blocks: { colourPrimary: "#a500cf" }, + procedure_blocks: { colourPrimary: "#4095CE" }, + }, + + categoryStyles: { + leaphy_category: { colour: "#06778f" }, + situation_category: { colour: "#D9B53F" }, + numbers_category: { colour: "#75B342" }, + variables_category: { colour: "#DE7C3B" }, + lists_category: { colour: "#a500cf" }, + functions_category: { colour: "#4095CE" }, + }, + componentStyles: { + toolboxBackgroundColour: "#343444", + toolboxForegroundColour: "#fff", + flyoutBackgroundColour: "#FFFFFF", + flyoutForegroundColour: "#ccc", + insertionMarkerColour: "#000", + scrollbarColour: "#ccc", + flyoutOpacity: 1, + }, + name: "light", + }; + } + + return Blockly.Theme.defineTheme(themeStr, theme); +} + +export default getTheme; diff --git a/src/app/services/toolbox/toolbox.ts b/src/app/services/blockly/toolbox.ts similarity index 100% rename from src/app/services/toolbox/toolbox.ts rename to src/app/services/blockly/toolbox.ts diff --git a/src/app/state/app.state.ts b/src/app/state/app.state.ts index 635d9411..4eb5f5ca 100644 --- a/src/app/state/app.state.ts +++ b/src/app/state/app.state.ts @@ -127,6 +127,9 @@ export class AppState { }, ]; + public selectedThemeSubject$ = new BehaviorSubject(""); + public selectedTheme$ = this.selectedThemeSubject$.asObservable(); + public releaseInfoSubject$ = new BehaviorSubject(null); public releaseInfo$: Observable = this.releaseInfoSubject$.asObservable(); @@ -234,6 +237,10 @@ export class AppState { this.isCodeEditorToggleConfirmedSubject$.next(confirmed); } + get selectedCodeEditor(): CodeEditorType { + return this.codeEditorSubject$.getValue(); + } + set selectedCodeEditor(codeEditor: CodeEditorType) { this.codeEditorSubject$.next(codeEditor); } @@ -253,4 +260,12 @@ export class AppState { get releaseVersion(): string { return version; } + + set selectedTheme(theme: string) { + this.selectedThemeSubject$.next(theme); + } + + get selectedTheme(): string { + return this.selectedThemeSubject$.getValue(); + } } diff --git a/src/app/state/blockly-editor.state.ts b/src/app/state/blockly-editor.state.ts index a8fa2c6d..5a4c8dbb 100644 --- a/src/app/state/blockly-editor.state.ts +++ b/src/app/state/blockly-editor.state.ts @@ -80,6 +80,10 @@ export class BlocklyEditorState { return this.isSideNavOpenSubject$.getValue(); } + get blocklyElement(): ElementRef { + return this.blocklyElementSubject$.getValue(); + } + set blocklyElement(element: ElementRef) { this.blocklyElementSubject$.next(element); } @@ -129,4 +133,8 @@ export class BlocklyEditorState { get workspaceJSON(): string { return this.workspaceJSONSubject$.getValue(); } + + get blocklyConfig(): any { + return this.blocklyConfigSubject$.getValue(); + } } diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index aa6e2dc4..a2068165 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -57,6 +57,9 @@ "SHOW_ON_SCREEN": "Show on screen", "EDIT_CODE": "Edit code", "LANGUAGE": "Language", + "THEME": "Theme", + "DARK_THEME": "Dark", + "LIGHT_THEME": "Light", "CHOOSE_A_ROBOT": "CONNECT A ROBOT", "SELECT_ROBOT_START_PROJECT": "Select your programming environment", "VIEW_LOG": "View log", diff --git a/src/assets/i18n/nl.json b/src/assets/i18n/nl.json index 49ae4a26..3b76d83d 100644 --- a/src/assets/i18n/nl.json +++ b/src/assets/i18n/nl.json @@ -57,6 +57,9 @@ "SHOW_ON_SCREEN": "Toon op scherm", "EDIT_CODE": "Code bewerken", "LANGUAGE": "Taal", + "THEME": "Thema", + "DARK_THEME": "Donker", + "LIGHT_THEME": "Licht", "CHOOSE_A_ROBOT": "KIES EEN ROBOT", "SELECT_ROBOT_START_PROJECT": "Selecteer je programmeeromgeving en start je nieuwe project", "VIEW_LOG": "Bekijk log", diff --git a/src/styles.scss b/src/styles.scss index 53a40ead..05304fa1 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -1,3 +1,5 @@ +@use "@angular/material" as mat; + /* You can add global styles to this file, and also import other style files */ @import url("./assets/fonts/inter/inter.css"); @font-face { @@ -16,7 +18,6 @@ html { html, body { height: 100%; - background-color: #f1f1f1; } body { margin: 0; @@ -38,51 +39,73 @@ pre[class*="language-"]:after { display: none; } -.blocklyToolboxCategory { - margin: 3px 0; +// global variables + +// check on the root element if the theme is light or dark + +:root { + --leaphy-color-light: #ffffff; - .blocklyTreeRow { - box-sizing: content-box; - border-left: 6px solid var(--leaphy-block-default); + body[data-theme="light"] { + --leaphy-header-bg: #06778f; + --leaphy-header-icon-color: #06778f; + --leaphy-header-button-border: #32a5a9; - &#l_situation { - border-color: var(--leaphy-block-yellow); - } + --leaphy-color-primary: #06778f; + --leaphy-color-primary-dark-tint: #104d59; + --leaphy-color-secundary: #32a5a9; + --leaphy-color-dark: #343444; - &#l_numbers { - border-color: var(--leaphy-block-green); - } + --mat-menu-item-label-text-size: 12px !important; - &#l_variables { - border-color: var(--leaphy-block-orange); - } + --start-screen-bg: #f1f1f1; + --leaphy-background-color: #c1c1c1; - &#l_functions { - border-color: var(--leaphy-block-blue); - } + --leaphy-robot-tile: #ffffff; + --leaphy-robot-tile-border: #f1f1f1; + --leaphy-robot-icon-filter: invert(0); + --leaphy-robot-right-bg: white; + + --leaphy-text-color: #000000; + + --leaphy-blockly-bg: #e1e1e1; } -} + body[data-theme="dark"] { + --leaphy-header-bg: #104d59; + --leaphy-header-icon-color: #32a5a9; + --leaphy-header-button-border: #343444; -// global variables + --leaphy-color-primary: #104d59; + --leaphy-color-primary-dark-tint: #104d59; + --leaphy-color-secundary: #32a5a9; + --leaphy-color-dark: #343444; -:root { - --leaphy-color-primary: #06778f; - --leaphy-color-primary-dark-tint: #035e72; - --leaphy-color-secundary: #32a5a9; - --leaphy-color-dark: #343444; - --leaphy-color-light: #ffffff; + --mat-menu-item-label-text-size: 12px !important; + + --start-screen-bg: #1e1e1e; + --leaphy-background-color: #1e1e1e; + + --leaphy-robot-tile: #414145; + --leaphy-robot-tile-border: #343444; + --leaphy-robot-icon-filter: invert(20%); + --leaphy-robot-right-bg: #363645; - --leaphy-block-default: #06778f; - --leaphy-block-yellow: #d9b53f; - --leaphy-block-green: #75b342; - --leaphy-block-orange: #de7c3b; - --leaphy-block-blue: #4095ce; - --leaphy-block-yellow: #d9b53f; - --leaphy-block-grey: #7d7d80; + --leaphy-text-color: lightgray; + --mat-option-selected-state-label-text-color: #32a5a9; // OVERRIDE - --mat-menu-item-label-text-size: 12px !important; + --leaphy-blockly-bg: #353444; + } } .blocklySvg { + z-index: 0; background-color: transparent !important; } + +.blocklyHtmlInput { + background-color: white !important; +} + +body { + background-color: var(--leaphy-blockly-bg) !important; +} diff --git a/src/theme.scss b/src/theme.scss index 8abb1375..0ea743d8 100644 --- a/src/theme.scss +++ b/src/theme.scss @@ -1,6 +1,19 @@ @use "@angular/material" as mat; +@use "sass:map"; @include mat.core(); +:root { + --dark-mode: 0; +} + +body[data-theme="light"] { + --dark-mode: 0; +} + +body[data-theme="dark"] { + --dark-mode: 1; +} + $light-mode-palette: ( 50: transparent, 100: #b2dfdb, @@ -16,19 +29,118 @@ $light-mode-palette: ( contrast: (), ); -$leaphy-color-accent: mat.define-palette($light-mode-palette, 50); -$leaphy-color-primary: mat.define-palette($light-mode-palette, A100); -$leaphy-color-warn: mat.define-palette($light-mode-palette, 900); +$dark-mode-palette: ( + 50: transparent, + 100: #b2dfdb, + 200: #80cbc4, + 300: #4db6ac, + 400: #26a69a, + 500: #009688, + 600: #00897b, + 700: #00796b, + 800: #00695c, + 900: #004d40, + A100: #10324a, + contrast: ( + 50: transparent, + 100: #b2dfdb, + 200: #80cbc4, + 300: #4db6ac, + 400: #26a69a, + 500: #009688, + 600: #00897b, + 700: #00796b, + 800: #00695c, + 900: #004d40, + A100: #32a5a9, + ), +); + +$leaphy-color-accent-light: mat.define-palette($light-mode-palette, 50); +$leaphy-color-primary-light: mat.define-palette($light-mode-palette, A100); +$leaphy-color-warn-light: mat.define-palette($light-mode-palette, 900); + +$leaphy-color-accent-dark: mat.define-palette($dark-mode-palette, 50); +$leaphy-color-primary-dark: mat.define-palette($dark-mode-palette, A100); +$leaphy-color-warn-dark: mat.define-palette($dark-mode-palette, 900); +$leaphy-color-selected-dark: mat.define-palette($light-mode-palette, A100); $light-mode: mat.define-light-theme( ( color: ( - primary: $leaphy-color-primary, - accent: $leaphy-color-accent, - warn: $leaphy-color-warn, + primary: $leaphy-color-primary-light, + accent: $leaphy-color-accent-light, + warn: $leaphy-color-warn-light, + ), + typography: mat.define-legacy-typography-config(), + ) +); + +$grey-palette: ( + 50: #fafafa, + 100: #f5f5f5, + 200: #eeeeee, + 300: #e0e0e0, + 400: #bdbdbd, + 500: #9e9e9e, + 600: #757575, + 700: #616161, + 800: #424242, + 900: #212121, + A100: #ffffff, + A200: #eeeeee, + A400: #bdbdbd, + A700: #616161, +); + +$dark-background: ( + status-bar: black, + app-bar: #212121, + background: #303030, + hover: rgba(white, 0.04), + disabled-button: rgba(white, 0.12), + raised-button: #424242, + focused-button: rgba(white, 0.12), + dialog: #424242, + card: #424242, + selected-button: #32a5a9, + selected-disabled-button: #424242, + disabled-button-toggle: black, + unselected-chip: #616161, + disabled-list-option: rgba(white, 0.12), + tooltip: #616161, +); + +$dark-theme: mat.define-dark-theme( + ( + color: ( + primary: $leaphy-color-primary-dark, + accent: $leaphy-color-accent-light, + warn: $leaphy-color-warn-light, ), typography: mat.define-legacy-typography-config(), ) ); -@include mat.all-component-themes($light-mode); +$color-map: map.get($dark-theme, "color"); +$modified-color-map: map.merge( + $color-map, + ( + "background": $dark-background, + ) +); +$dark-theme: map.merge( + $dark-theme, + ( + "color": $modified-color-map, + ) +); +/* Light mode */ +body[data-theme="light"] { + @include mat.all-component-themes($light-mode); +} + +/* Dark mode */ +body[data-theme="dark"] { + @include mat.all-component-themes($dark-theme); +}