css generalized 'tile' (DeviceList)

This commit is contained in:
Patrick Haßel 2024-09-11 15:00:56 +02:00
parent 073c42002a
commit 44820f3201
15 changed files with 135 additions and 163 deletions

View File

@ -5,6 +5,8 @@ spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa spring.datasource.username=sa
spring.datasource.password=password spring.datasource.password=password
#- #-
spring.jackson.serialization.indent_output=true
#-
spring.jpa.hibernate.ddl-auto=create spring.jpa.hibernate.ddl-auto=create
#- #-
de.ph87.homeautomation.insert-demo-data=true de.ph87.homeautomation.insert-demo-data=true

View File

@ -8,10 +8,6 @@
Rollläden Rollläden
</div> </div>
<!-- <div class="item" [routerLink]="['/DeviceList', {'type': 'DeviceStateScene'}]" [class.itemActive]="isRouteActive('/DeviceList', 'DeviceStateScene')">-->
<!-- Gruppen-->
<!-- </div>-->
<div class="item" routerLink="/BulkList" routerLinkActive="itemActive"> <div class="item" routerLink="/BulkList" routerLinkActive="itemActive">
Stapel Stapel
</div> </div>
@ -21,11 +17,11 @@
</div> </div>
<div class="item itemSecondary" routerLink="/PropertyList" routerLinkActive="itemActive"> <div class="item itemSecondary" routerLink="/PropertyList" routerLinkActive="itemActive">
Eigenschaften P
</div> </div>
<div class="item itemSecondary" routerLink="/ChannelList" routerLinkActive="itemActive"> <div class="item itemSecondary" routerLink="/ChannelList" routerLinkActive="itemActive">
Kanäle C
</div> </div>
</div> </div>

View File

@ -1,15 +1,17 @@
@import "../config";
.menubar { .menubar {
border-bottom: 1px solid black; border-bottom: @border solid black;
.item { .item {
float: left; float: left;
padding: 10px; padding: @padding;
border-right: 1px solid black; border-right: @border solid black;
} }
.itemSecondary { .itemSecondary {
float: right; float: right;
border-left: 1px solid black; border-left: @border solid black;
border-right: none; border-right: none;
} }

View File

@ -1,76 +1,84 @@
<ng-container *ngFor="let device of devices; trackBy: Device.trackBy"> <div class="tiles">
<ng-container [ngSwitch]="device.type">
<div class="device" *ngSwitchCase="'DeviceSwitch'" [ngClass]="getSwitchClassList(device)"> <ng-container *ngFor="let device of devices; trackBy: Device.trackBy">
<div class="tileHeadTitle">
<app-text [initial]="device.title" (valueChange)="set(device, 'title', $event)"></app-text> <div class="tile" *ngIf="device.type == 'DeviceSwitch'" [ngClass]="getSwitchClassList(device)">
<div class="tileHead">
<div class="flexGrow">
<app-text [initial]="device.title" (valueChange)="set(device, 'title', $event)"></app-text>
</div>
<div class="tileHeadRight" [routerLink]="['/Device', {id: device.id}]">
<fa-icon [icon]="faEdit"></fa-icon>
</div>
</div> </div>
<div class="edit" [routerLink]="['/Device', {id: device.id}]"> <div class="tileBody">
<fa-icon [icon]="faEdit"></fa-icon> <img alt="An" class="control" src="assets/switch-on.svg" (click)="deviceService.setSwitchState(device, true)"/>
</div> <img alt="Aus" class="control" src="assets/switch-off.svg" (click)="deviceService.setSwitchState(device, false)"/>
<div class="controls">
<img alt="An" class="control" src="../../../../assets/switch-on.svg" (click)="deviceService.setSwitchState(device, true)"/>
<img alt="Aus" class="control" src="../../../../assets/switch-off.svg" (click)="deviceService.setSwitchState(device, false)"/>
</div> </div>
</div> </div>
<div class="device" *ngSwitchCase="'DeviceStateScene'" [ngClass]="getStateSceneClassList(device)"> <div class="tile" *ngIf="device.type === 'DeviceStateScene'" [ngClass]="getStateSceneClassList(device)">
<div class="tileHeadTitle"> <div class="tileHead">
<app-text [initial]="device.title" (valueChange)="set(device, 'title', $event)"></app-text> <div class="flexGrow">
<app-text [initial]="device.title" (valueChange)="set(device, 'title', $event)"></app-text>
</div>
<div class="tileHeadRight" [routerLink]="['/Device', {id: device.id}]">
<fa-icon [icon]="faEdit"></fa-icon>
</div>
</div> </div>
<div class="edit" [routerLink]="['/Device', {id: device.id}]"> <div class="tileBody">
<fa-icon [icon]="faEdit"></fa-icon> <div class="control" *ngFor="let scene of getStateScenes(device)" (click)="deviceService.setStateScene(device, scene.number)">
</div> {{ scene.title }}
<div class="controls">
<div *ngFor="let scene of getStateScenes(device)" class="control button" (click)="deviceService.setStateScene(device, scene.number)">
<span class="center">{{scene.title}}</span>
</div> </div>
</div> </div>
</div> </div>
<div class="device" *ngSwitchCase="'DeviceShutter'" [ngClass]="getShutterClassList(device)"> <div class="tile" *ngIf="device.type === 'DeviceShutter'" [ngClass]="getShutterClassList(device)">
<div class="tileHeadTitle"> <div class="tileHead">
<app-text [initial]="device.title" (valueChange)="set(device, 'title', $event)"></app-text> <div class="flexGrow">
<app-text [initial]="device.title" (valueChange)="set(device, 'title', $event)"></app-text>
</div>
<div class="tileHeadRight" [routerLink]="['/Device', {id: device.id}]">
<fa-icon [icon]="faEdit"></fa-icon>
</div>
</div> </div>
<div class="edit" [routerLink]="['/Device', {id: device.id}]"> <div class="tileBody">
<fa-icon [icon]="faEdit"></fa-icon> <div class="control controlShutter" (click)="deviceService.setShutterPosition(device, 0)">
</div> Auf
<div class="controls">
<div class="control button" (click)="deviceService.setShutterPosition(device, 0)">
<span class="center">Auf</span>
</div> </div>
<div class="control button" (click)="deviceService.setShutterPosition(device, 20)"> <div class="control controlShutter" (click)="deviceService.setShutterPosition(device, 20)">
<span class="center">20%</span> 20%
</div> </div>
<div class="control button" (click)="deviceService.setShutterPosition(device, 30)"> <div class="control controlShutter" (click)="deviceService.setShutterPosition(device, 30)">
<span class="center">30%</span> 30%
</div> </div>
<div class="control button" (click)="deviceService.setShutterPosition(device, 40)"> <div class="control controlShutter" (click)="deviceService.setShutterPosition(device, 40)">
<span class="center">40%</span> 40%
</div> </div>
<div class="control button" (click)="deviceService.setShutterPosition(device, 50)"> <div class="control controlShutter" (click)="deviceService.setShutterPosition(device, 50)">
<span class="center">50%</span> 50%
</div> </div>
<div class="control button" (click)="deviceService.setShutterPosition(device, 60)"> <div class="control controlShutter" (click)="deviceService.setShutterPosition(device, 60)">
<span class="center">60%</span> 60%
</div> </div>
<div class="control button" (click)="deviceService.setShutterPosition(device, 70)"> <div class="control controlShutter" (click)="deviceService.setShutterPosition(device, 70)">
<span class="center">70%</span> 70%
</div> </div>
<div class="control button" (click)="deviceService.setShutterPosition(device, 80)"> <div class="control controlShutter" (click)="deviceService.setShutterPosition(device, 80)">
<span class="center">80%</span> 80%
</div> </div>
<div class="control button" (click)="deviceService.setShutterPosition(device, 90)"> <div class="control controlShutter" (click)="deviceService.setShutterPosition(device, 90)">
<span class="center">90%</span> 90%
</div> </div>
<div class="control button" (click)="deviceService.setShutterPosition(device, 100)"> <div class="control controlShutter" (click)="deviceService.setShutterPosition(device, 100)">
<span class="center">Zu</span> Zu
</div> </div>
</div> </div>
</div> </div>
</ng-container> </ng-container>
</ng-container>
</div>
<div class="config"> <div class="config">
<button (click)="create()">+ Hinzufügen</button> <button (click)="create()">+ Hinzufügen</button>

View File

@ -1,71 +1,17 @@
.device { @import "../../../../config";
padding: 5px;
margin-bottom: 5px;
border-radius: 10px;
@media (min-width: 1000px) { .control {
float: left; float: left;
width: 400px; width: 4em;
margin-right: 5px; aspect-ratio: 1;
} margin: @margin;
border-radius: 25%;
.tileHeadTitle {
float: left;
font-weight: bold;
}
.edit {
float: right;
}
.controls {
clear: both;
.control {
position: relative;
float: left;
width: 60px;
height: 60px;
padding: 5px;
margin: 5px;
border-radius: 25%;
}
.button {
background-color: lightblue;
}
.control:hover {
background-color: lightskyblue;
}
}
} }
.switchOn { .controlShutter {
background-color: palegreen; width: 17.3%;
} padding-top: 1.1em;
text-align: center;
.switchOff { margin: calc(@margin / 2);
background-color: indianred; background-color: lightsteelblue;
}
.switchUnknown {
background-color: gray;
}
.shutterOpen {
background-color: palegreen;
}
.shutterBetween {
background-color: yellow;
}
.shutterClosed {
background-color: indianred;
}
.shutterUnknown {
background-color: gray;
} }

View File

@ -81,9 +81,9 @@ export class DeviceListComponent implements OnInit {
getSwitchClassList(device: Device): object { getSwitchClassList(device: Device): object {
const value: number | null | undefined = (device as DeviceSwitch).stateProperty?.readChannel?.value; const value: number | null | undefined = (device as DeviceSwitch).stateProperty?.readChannel?.value;
return { return {
switchOn: value === 1, deviceSwitchOnBack: value === 1,
switchOff: value === 0, deviceSwitchOffBack: value === 0,
switchUnknown: value === null || value === undefined, disabledBack: value === null || value === undefined,
}; };
} }
@ -94,10 +94,10 @@ export class DeviceListComponent implements OnInit {
getShutterClassList(device: Device): object { getShutterClassList(device: Device): object {
const value: number | null | undefined = (device as DeviceShutter).positionProperty?.readChannel?.value; const value: number | null | undefined = (device as DeviceShutter).positionProperty?.readChannel?.value;
return { return {
shutterOpen: value === 0, deviceShutterOpenBack: value === 0,
shutterBetween: value !== null && value !== undefined && value > 0 && value < 100, deviceShutterIntermediateBack: value !== null && value !== undefined && value > 0 && value < 100,
shutterClosed: value === 100, deviceShutterClosedBack: value === 100,
shutterUnknown: value === null || value === undefined, disabledBack: value === null || value === undefined,
}; };
} }

View File

@ -13,12 +13,15 @@
<fa-icon *ngIf="entry.enabled" [icon]="faCheckCircle"></fa-icon> <fa-icon *ngIf="entry.enabled" [icon]="faCheckCircle"></fa-icon>
<fa-icon *ngIf="!entry.enabled" [icon]="faCircle"></fa-icon> <fa-icon *ngIf="!entry.enabled" [icon]="faCircle"></fa-icon>
</div> </div>
<div class="tileHeadTitle"> <div class="flexGrow">
<select [ngModel]="entry.bulk?.id" (ngModelChange)="entryService.set(entry, 'bulk', $event)"> <select [ngModel]="entry.bulk?.id" (ngModelChange)="entryService.set(entry, 'bulk', $event)">
<option [ngValue]="null">-</option> <option [ngValue]="null">-</option>
<option [ngValue]="bulk.id" *ngFor="let bulk of bulks.sort(Bulk.compareName)">{{ bulk.name }}</option> <option [ngValue]="bulk.id" *ngFor="let bulk of bulks.sort(Bulk.compareName)">{{ bulk.name }}</option>
</select> </select>
</div> </div>
<div class="tileHeadDelete">
<fa-icon [icon]="faTimesCircle" (click)="delete(entry)"></fa-icon>
</div>
</div> </div>
<div class="tileBodyFlex"> <div class="tileBodyFlex">
@ -116,4 +119,8 @@
</div> </div>
<div class="config">
<button (click)="create()">+ Hinzufügen</button>
</div>
</ng-container> </ng-container>

View File

@ -12,7 +12,7 @@ import {Bulk} from "../../../api/bulk/Bulk";
import {BulkService} from "../../../api/bulk/BulkService"; import {BulkService} from "../../../api/bulk/BulkService";
import {Zenith} from "../../../api/schedule/entry/Zenith"; import {Zenith} from "../../../api/schedule/entry/Zenith";
import {TimeService} from "../../../api/time.service"; import {TimeService} from "../../../api/time.service";
import {faCheckCircle, faCircle} from "@fortawesome/free-regular-svg-icons"; import {faCheckCircle, faCircle, faTimesCircle} from "@fortawesome/free-regular-svg-icons";
const DAY_MINUTES: number = 24 * 60; const DAY_MINUTES: number = 24 * 60;
@ -134,4 +134,6 @@ export class ScheduleEditorComponent implements OnInit {
protected readonly faCheckCircle = faCheckCircle; protected readonly faCheckCircle = faCheckCircle;
protected readonly faCircle = faCircle; protected readonly faCircle = faCircle;
protected readonly faTimesCircle = faTimesCircle;
} }

View File

@ -9,7 +9,7 @@
<fa-icon *ngIf="!schedule.enabled" [icon]="faCircle"></fa-icon> <fa-icon *ngIf="!schedule.enabled" [icon]="faCircle"></fa-icon>
</div> </div>
<div class="tileHeadTitle" [routerLink]="['/Schedule', {id: schedule.id}]"> <div class="flexGrow" [routerLink]="['/Schedule', {id: schedule.id}]">
{{ schedule.title }} {{ schedule.title }}
</div> </div>

View File

@ -1,7 +1,9 @@
@import "../../../config";
input { input {
outline: none; outline: none;
border: none; border: none;
padding: 5px; padding: @padding;
margin: 0; margin: 0;
width: 100%; width: 100%;
background-color: transparent; background-color: transparent;

View File

@ -1,22 +1,24 @@
@import "../../../config";
.all { .all {
.initial { .initial {
padding: 5px; padding: @padding;
height: 100%; height: 100%;
} }
.selected { .selected {
font-weight: bold; font-weight: bold;
border-bottom: 1px solid black; border-bottom: @border solid black;
} }
.resultList { .resultList {
position: absolute; position: absolute;
background-color: lightgray; background-color: lightgray;
min-width: 200px; min-width: 10em;
border: 1px solid black; border: @border solid black;
.result { .result {
padding: 5px; padding: @padding;
} }
.result:hover { .result:hover {

View File

@ -3,18 +3,10 @@
@border: 0.05em; @border: 0.05em;
@border-radius: 0.2em; @border-radius: 0.2em;
.disabledFont {
color: gray;
}
.disabledBack { .disabledBack {
background-color: gray; background-color: gray;
} }
.enabledFont {
color: #8fbc8f;
}
.enabledBack { .enabledBack {
background-color: #8fbc8f; background-color: #8fbc8f;
} }
@ -34,3 +26,23 @@
.fuzzyBack { .fuzzyBack {
background-color: #88c0ff; background-color: #88c0ff;
} }
.deviceSwitchOnBack {
background-color: #8fbc8f;
}
.deviceSwitchOffBack {
background-color: #bc8f8f;
}
.deviceShutterOpenBack {
background-color: #8fbc8f;
}
.deviceShutterIntermediateBack {
background-color: #e4db9c;
}
.deviceShutterClosedBack {
background-color: #bc8f8f;
}

View File

@ -1,3 +1,5 @@
@import "config";
* { * {
box-sizing: border-box; box-sizing: border-box;
} }
@ -54,11 +56,11 @@ table {
td, th { td, th {
height: 0; // (=> auto growth) enables use of height percent for children height: 0; // (=> auto growth) enables use of height percent for children
padding: 5px; padding: @padding;
border: 1px solid black; border: @border solid black;
img.fullCell { img.fullCell {
margin: -5px; margin: calc(-@margin);
} }
} }
@ -70,14 +72,6 @@ table.vertical {
} }
} }
.center {
position: absolute;
margin: 0;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.empty { .empty {
text-align: center; text-align: center;
color: gray; color: gray;

View File

@ -1,5 +1,6 @@
package de.ph87.homeautomation.web; package de.ph87.homeautomation.web;
import lombok.NonNull;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
@ -27,7 +28,7 @@ public class WebConfig implements WebMvcConfigurer {
.addResourceLocations("classpath:/resources/") .addResourceLocations("classpath:/resources/")
.resourceChain(true) .resourceChain(true)
.addResolver(new PathResourceResolver() { .addResolver(new PathResourceResolver() {
protected Resource getResource(String resourcePath, Resource roomLocation) throws IOException { protected Resource getResource(@NonNull String resourcePath, @NonNull Resource roomLocation) throws IOException {
Resource requestedResource = roomLocation.createRelative(resourcePath); Resource requestedResource = roomLocation.createRelative(resourcePath);
return requestedResource.exists() && requestedResource.isReadable() ? requestedResource : new ClassPathResource("/resources/index.html"); return requestedResource.exists() && requestedResource.isReadable() ? requestedResource : new ClassPathResource("/resources/index.html");
} }

View File

@ -5,6 +5,4 @@ spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.Im
spring.jpa.hibernate.ddl-auto=update spring.jpa.hibernate.ddl-auto=update
spring.jpa.open-in-view=false spring.jpa.open-in-view=false
#- #-
spring.jackson.serialization.indent_output=true
#-
spring.main.banner-mode=off spring.main.banner-mode=off