From 9f8bf01aba558b52d64b97993a8e67fa51b65097 Mon Sep 17 00:00:00 2001 From: E44 <129310925+programmer-44@users.noreply.github.com> Date: Tue, 4 Nov 2025 20:24:33 +0100 Subject: [PATCH] edit and delete display added --- .../src/components/ControlView.svelte | 18 ++- .../src/components/DisplayGroupObject.svelte | 7 +- .../src/components/DisplayObject.svelte | 17 +-- .../src/components/DisplayView.svelte | 45 ++++--- control/frontend/src/components/PopUp.svelte | 4 +- control/frontend/src/routes/+page.svelte | 122 ++++++++++++++---- control/frontend/src/ts/stores/displays.ts | 37 +++++- control/frontend/src/ts/types.ts | 6 +- 8 files changed, 185 insertions(+), 71 deletions(-) diff --git a/control/frontend/src/components/ControlView.svelte b/control/frontend/src/components/ControlView.svelte index 41c6c4a..3805bc3 100644 --- a/control/frontend/src/components/ControlView.svelte +++ b/control/frontend/src/components/ControlView.svelte @@ -1,9 +1,17 @@
diff --git a/control/frontend/src/components/DisplayGroupObject.svelte b/control/frontend/src/components/DisplayGroupObject.svelte index 16455e1..51b0cf9 100644 --- a/control/frontend/src/components/DisplayGroupObject.svelte +++ b/control/frontend/src/components/DisplayGroupObject.svelte @@ -13,11 +13,12 @@ } from '../ts/stores/displays'; import DNDGrip from './DNDGrip.svelte'; import { fade } from 'svelte/transition'; - import type { DisplayGroup } from '../ts/types'; + import type { DisplayGroup, MenuOption } from '../ts/types'; import { selected_display_ids } from '../ts/stores/select'; - let { display_group } = $props<{ + let { display_group, get_display_menu_options } = $props<{ display_group: DisplayGroup; + get_display_menu_options: (display_id: string) => MenuOption[] }>(); let hovering_selectable = $state(false); @@ -76,7 +77,7 @@ class="outline-none" role="figure" > - + {/each} diff --git a/control/frontend/src/components/DisplayObject.svelte b/control/frontend/src/components/DisplayObject.svelte index d84fc66..02470a5 100644 --- a/control/frontend/src/components/DisplayObject.svelte +++ b/control/frontend/src/components/DisplayObject.svelte @@ -8,11 +8,12 @@ import DNDGrip from './DNDGrip.svelte'; import { Menu, Pencil, Pin, PinOff, Trash2, VideoOff, X } from 'lucide-svelte'; import OnlineState from './OnlineState.svelte'; - import type { Display } from '../ts/types'; + import type { Display, MenuOption } from '../ts/types'; import { is_selected, select, selected_display_ids } from '../ts/stores/select'; - let { display } = $props<{ + let { display, get_display_menu_options } = $props<{ display: Display; + get_display_menu_options: (display_id: string) => MenuOption[]; }>(); let hovering_unselectable = $state(false); @@ -121,17 +122,7 @@ click_function={(e) => { e.stopPropagation(); }} - menu_options={[ - { - icon: Pencil, - name: 'Bildschirm bearbeiten' - }, - { - icon: Trash2, - name: 'Bildschirm löschen', - class: 'text-red-400 hover:text-stone-200 hover:!bg-red-400 active:!bg-red-500' - } - ]} + menu_options={get_display_menu_options(display.id)} > diff --git a/control/frontend/src/components/DisplayView.svelte b/control/frontend/src/components/DisplayView.svelte index 9cf529f..64a6b02 100644 --- a/control/frontend/src/components/DisplayView.svelte +++ b/control/frontend/src/components/DisplayView.svelte @@ -16,7 +16,7 @@ next_height_step_size, pinned_display_id } from '../ts/stores/ui_behavior'; - import { type Display, type DisplayGroup } from '../ts/types'; + import { type Display, type DisplayGroup, type MenuOption } from '../ts/types'; import Button from './Button.svelte'; import OnlineState from './OnlineState.svelte'; import { cubicOut } from 'svelte/easing'; @@ -26,11 +26,33 @@ import { flip } from 'svelte/animate'; import DisplayGroupObject from './DisplayGroupObject.svelte'; + let { handle_display_deletion, handle_display_editing } = $props<{ + handle_display_deletion: (display_id: string) => void; + handle_display_editing: (display_id: string) => void; + }>(); + let displays_scroll_box: HTMLElement; let pinned_display: Display | null = $derived( get_display_by_id($pinned_display_id || '', $displays) ); + + function get_display_menu_options(display_id: string): MenuOption[] { + return [ + { + icon: Pencil, + name: 'Bildschirm bearbeiten', + on_select: () => {handle_display_editing(display_id)}, + }, + { + icon: Trash2, + name: 'Bildschirm löschen', + class: 'text-red-400 hover:text-stone-200 hover:!bg-red-400 active:!bg-red-500', + on_select: () => {handle_display_deletion(display_id)}, + } + ]; + } + function select_all(current_displays: DisplayGroup[], current_selected_display_ids: string[]) { const new_value = !all_selected(current_displays, current_selected_display_ids); for (const display_group of current_displays) { @@ -89,17 +111,7 @@ click_function={(e) => { e.stopPropagation(); }} - menu_options={[ - { - icon: Pencil, - name: 'Bildschirm bearbeiten' - }, - { - icon: Trash2, - name: 'Bildschirm löschen', - class: 'text-red-400 hover:text-stone-200 hover:!bg-red-400 active:!bg-red-500' - } - ]} + menu_options={get_display_menu_options($pinned_display_id)} > @@ -159,7 +171,7 @@ : 'Alle auswählen'} -
+
diff --git a/control/frontend/src/routes/+page.svelte b/control/frontend/src/routes/+page.svelte index d543f85..9789314 100644 --- a/control/frontend/src/routes/+page.svelte +++ b/control/frontend/src/routes/+page.svelte @@ -1,5 +1,5 @@ -{#snippet add_new_display()} +{#snippet remove_display_popup(display_id: string)} +
+ Soll der Bildschirm "{get_display_by_id(display_id, $displays)?.name || '?'}" wirklich gelöscht + werden? Dadurch wird es von diesem Controller nicht mehr erreichbar. Die Installation auf dem + Gerät bleibt bestehen. Mit dem erneuten Hinzufügen des Bildschirms wird er wieder steuerbar. +
+
+ + +
+{/snippet} + +{#snippet add_new_display_popup(existing_display_id: string | null = null)} { - return input.length === 0 || input.length > 50 - ? [false, 'Ungültige Länge'] - : is_display_name_taken(input) - ? [false, 'Name bereits verwendet'] - : [true, 'Gültiger Name']; + if (!!existing_display_id) { + if (input === get_display_by_id(existing_display_id, $displays)?.name) + return [true, 'Gültiger Name']; + } + if (input.length === 0 || input.length > 50) return [false, 'Ungültige Länge']; + if (is_display_name_taken(input)) return [false, 'Name bereits verwendet']; + return [true, 'Gültiger Name']; }} />
@@ -111,18 +171,29 @@ : [false, 'Ungültige MAC-Adresse']; }} /> -
+
+ {#if !!existing_display_id} + + + {/if} + click_function={() => { + finalize_add_edit_display(existing_display_id); + }} + >{#if !!existing_display_id} + Speichern + {:else} + Bildschirm hinzufügen + {/if} +
{/snippet}
- {@html SplashScreen} +
PLG MuDiCS @@ -146,7 +217,10 @@
- +
diff --git a/control/frontend/src/ts/stores/displays.ts b/control/frontend/src/ts/stores/displays.ts index 39164e1..beffe48 100644 --- a/control/frontend/src/ts/stores/displays.ts +++ b/control/frontend/src/ts/stores/displays.ts @@ -11,19 +11,44 @@ export const displays: Writable = writable([{ export function is_display_name_taken(name: string): boolean { - const display_groups = get(displays); - return display_groups.some(group => - group.data.some(display => display.name.trim().toLowerCase() === name.trim().toLowerCase()) - ); + const display_groups = get(displays); + return display_groups.some(group => + group.data.some(display => display.name.trim().toLowerCase() === name.trim().toLowerCase()) + ); } -export function add_display(ip: string, mac: string|null, name: string, status: string) { +export function add_display(ip: string, mac: string | null, name: string, status: string) { displays.update((displays: DisplayGroup[]) => { displays[0].data.push({ id: get_uuid(), ip, preview_url: null, preview_timeout_id: null, mac, name, status }); return displays; }); } +export function edit_display_data(display_id: string, ip: string, mac: string | null, name: string) { + displays.update((display_groups) => + display_groups.map((group) => ({ + ...group, + data: group.data.map((display) => { + if (display.id !== display_id) return display; + return { ...display, ip: ip, mac: mac, name: name }; + }), + })) + ); +} + +export function remove_display(display_id: string) { + console.log(display_id); + displays.update((displays: DisplayGroup[]) => { + displays = displays.map(display_group => ({ + ...display_group, + data: display_group.data.filter(display => display.id !== display_id) + })); + return displays; + }); + + // TODO remove ID from Files usw. +} + export function all_displays_of_group_selected(display_group: DisplayGroup, current_selected_displays: string[]) { if (display_group.data.length === 0) return false; for (const display of display_group.data) { @@ -139,6 +164,6 @@ function add_testing_displays() { // add_display("127.0.0.1", "00:1A:2B:3C:4D:5E", name, "Offline"); // } - // add_display("127.0.0.1", "00:1A:2B:3C:4D:5E", "PC", "Offline"); + add_display("127.0.0.1", "00:1A:2B:3C:4D:5E", "PC", "Offline"); // add_display("192.168.178.111", "D4:81:D7:C0:DF:3C", "Laptop", "Online"); } \ No newline at end of file diff --git a/control/frontend/src/ts/types.ts b/control/frontend/src/ts/types.ts index 410b398..0cf2c01 100644 --- a/control/frontend/src/ts/types.ts +++ b/control/frontend/src/ts/types.ts @@ -1,4 +1,5 @@ import { FileBox, FileImage, FileVideoCamera, ImagePlay, type X } from "lucide-svelte"; +import type { Snippet } from "svelte"; export type SupportedFileType = { display_name: @@ -60,7 +61,7 @@ export type Display = { ip: string; preview_url: string | null; preview_timeout_id: number | null; - mac: string|null; + mac: string | null; name: string; status: string; } @@ -81,7 +82,8 @@ export type MenuOption = { export type PopupContent = { open: boolean; - snippet: any; + snippet: Snippet<[string]> | null; + snippet_arg?: string; title: string; title_class?: string; title_icon?: typeof X | null;