Files
webref/frontend/src/lib/components/canvas/GroupAnnotation.svelte
Danilo Reyes 948fe591dc
All checks were successful
CI/CD Pipeline / VM Test - security (push) Successful in 7s
CI/CD Pipeline / Backend Linting (push) Successful in 4s
CI/CD Pipeline / VM Test - backend-integration (push) Successful in 11s
CI/CD Pipeline / VM Test - full-stack (push) Successful in 8s
CI/CD Pipeline / VM Test - performance (push) Successful in 8s
CI/CD Pipeline / Nix Flake Check (push) Successful in 38s
CI/CD Pipeline / CI Summary (push) Successful in 0s
CI/CD Pipeline / Frontend Linting (push) Successful in 17s
phase 13
2025-11-02 14:48:03 -06:00

267 lines
5.8 KiB
Svelte
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script lang="ts">
/**
* Group annotation UI component
* Displays and edits group name, color, and annotation
*/
import { createEventDispatcher } from 'svelte';
import type { Group } from '$lib/api/groups';
export let group: Group;
export let editing: boolean = false;
const dispatch = createEventDispatcher();
let editName = group.name;
let editAnnotation = group.annotation || '';
function handleSave() {
dispatch('save', {
name: editName,
annotation: editAnnotation || null,
});
editing = false;
}
function handleCancel() {
editName = group.name;
editAnnotation = group.annotation || '';
editing = false;
}
function handleEdit() {
editing = true;
}
function handleDelete() {
dispatch('delete');
}
function handleColorChange() {
dispatch('color-change');
}
</script>
<div class="group-annotation" style="border-left: 4px solid {group.color}">
<div class="annotation-header">
<button
class="color-indicator"
style="background-color: {group.color}"
on:click={handleColorChange}
title="Change color"
/>
{#if editing}
<input
type="text"
bind:value={editName}
class="name-input"
placeholder="Group name"
maxlength="255"
/>
{:else}
<h4 class="group-name" on:dblclick={handleEdit}>{group.name}</h4>
{/if}
<div class="header-actions">
<span class="member-count" title="{group.member_count} images">
{group.member_count}
</span>
{#if !editing}
<button class="icon-button" on:click={handleEdit} title="Edit group"></button>
<button class="icon-button delete" on:click={handleDelete} title="Delete group"> × </button>
{/if}
</div>
</div>
<div class="annotation-body">
{#if editing}
<textarea
bind:value={editAnnotation}
class="annotation-input"
placeholder="Add annotation..."
maxlength="10000"
rows="3"
/>
<div class="edit-actions">
<button class="button button-secondary" on:click={handleCancel}>Cancel</button>
<button class="button button-primary" on:click={handleSave}>Save</button>
</div>
{:else if group.annotation}
<p class="annotation-text" on:dblclick={handleEdit}>{group.annotation}</p>
{:else}
<p class="annotation-empty" on:dblclick={handleEdit}>No annotation</p>
{/if}
</div>
</div>
<style>
.group-annotation {
background-color: var(--color-bg, #ffffff);
border-radius: 0.5rem;
padding: 1rem;
margin-bottom: 0.75rem;
}
.annotation-header {
display: flex;
align-items: center;
gap: 0.75rem;
margin-bottom: 0.75rem;
}
.color-indicator {
width: 24px;
height: 24px;
border-radius: 0.375rem;
cursor: pointer;
border: 2px solid rgba(0, 0, 0, 0.1);
transition: transform 0.2s;
padding: 0;
}
.color-indicator:hover {
transform: scale(1.1);
}
.group-name {
flex: 1;
margin: 0;
font-size: 1rem;
font-weight: 600;
color: var(--color-text, #111827);
cursor: pointer;
}
.group-name:hover {
color: var(--color-primary, #3b82f6);
}
.name-input {
flex: 1;
padding: 0.375rem 0.5rem;
border: 1px solid var(--color-border, #d1d5db);
border-radius: 0.375rem;
font-size: 1rem;
font-weight: 600;
}
.header-actions {
display: flex;
align-items: center;
gap: 0.5rem;
}
.member-count {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 24px;
height: 24px;
padding: 0 0.5rem;
background-color: var(--color-bg-secondary, #f3f4f6);
border-radius: 12px;
font-size: 0.75rem;
font-weight: 600;
color: var(--color-text-secondary, #6b7280);
}
.icon-button {
width: 28px;
height: 28px;
padding: 0;
background: none;
border: none;
cursor: pointer;
border-radius: 0.25rem;
font-size: 1.125rem;
color: var(--color-text-secondary, #6b7280);
transition: all 0.2s;
display: flex;
align-items: center;
justify-content: center;
}
.icon-button:hover {
background-color: var(--color-bg-hover, #f3f4f6);
color: var(--color-text, #374151);
}
.icon-button.delete:hover {
background-color: var(--color-error-bg, #fee2e2);
color: var(--color-error, #ef4444);
}
.annotation-body {
margin-top: 0.75rem;
}
.annotation-text {
margin: 0;
font-size: 0.875rem;
line-height: 1.5;
color: var(--color-text-secondary, #6b7280);
white-space: pre-wrap;
cursor: pointer;
}
.annotation-text:hover {
color: var(--color-text, #374151);
}
.annotation-empty {
margin: 0;
font-size: 0.875rem;
color: var(--color-text-muted, #9ca3af);
font-style: italic;
cursor: pointer;
}
.annotation-input {
width: 100%;
padding: 0.5rem;
border: 1px solid var(--color-border, #d1d5db);
border-radius: 0.375rem;
font-size: 0.875rem;
font-family: inherit;
resize: vertical;
}
.edit-actions {
display: flex;
justify-content: flex-end;
gap: 0.5rem;
margin-top: 0.75rem;
}
.button {
padding: 0.5rem 1rem;
border-radius: 0.375rem;
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
border: 1px solid transparent;
}
.button-secondary {
background-color: var(--color-bg-secondary, #f3f4f6);
color: var(--color-text, #374151);
border-color: var(--color-border, #d1d5db);
}
.button-secondary:hover {
background-color: var(--color-bg-hover, #e5e7eb);
}
.button-primary {
background-color: var(--color-primary, #3b82f6);
color: white;
border: none;
}
.button-primary:hover {
background-color: var(--color-primary-hover, #2563eb);
}
</style>