mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 23:21:08 +02:00
feat(contacts): add context menu to alphabet/grid views with icons, add icons to todo context menu
- Add right-click context menu to ContactAlphabetView (was missing entirely) - Add icons to ContactGridView context menu items - Wire up onDeleteContact through ContactList to both views - Add icons to TaskList (todo) context menu: edit, complete, priority, delete Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ab387b9b3d
commit
e5ca208cd8
4 changed files with 121 additions and 4 deletions
|
|
@ -228,6 +228,11 @@
|
|||
}
|
||||
|
||||
// Tag filtering is now client-side via the filteredContacts $derived
|
||||
|
||||
async function handleDeleteContact(id: string) {
|
||||
if (!confirm('Kontakt wirklich löschen?')) return;
|
||||
await contactsStore.deleteContact(id);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="space-y-6">
|
||||
|
|
@ -308,6 +313,7 @@
|
|||
contacts={sortedContacts}
|
||||
onContactClick={handleContactClick}
|
||||
onToggleFavorite={handleToggleFavorite}
|
||||
onDeleteContact={handleDeleteContact}
|
||||
{selectionMode}
|
||||
{selectedIds}
|
||||
onToggleSelection={toggleSelection}
|
||||
|
|
@ -317,6 +323,7 @@
|
|||
contacts={sortedContacts}
|
||||
onContactClick={handleContactClick}
|
||||
onToggleFavorite={handleToggleFavorite}
|
||||
onDeleteContact={handleDeleteContact}
|
||||
{selectionMode}
|
||||
{selectedIds}
|
||||
onToggleSelection={toggleSelection}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,17 @@
|
|||
<script lang="ts">
|
||||
import { Plus, Check, Heart, Phone, Envelope, TextAa, CaretDown } from '@manacore/shared-icons';
|
||||
import {
|
||||
Plus,
|
||||
Check,
|
||||
Heart,
|
||||
HeartBreak,
|
||||
Phone,
|
||||
Envelope,
|
||||
TextAa,
|
||||
CaretDown,
|
||||
ArrowSquareOut,
|
||||
Trash,
|
||||
} from '@manacore/shared-icons';
|
||||
import { ContextMenu, type ContextMenuItem } from '@manacore/shared-ui';
|
||||
import { _ } from 'svelte-i18n';
|
||||
import type { Contact } from '$lib/api/contacts';
|
||||
import { getDisplayName, getInitials } from '$lib/utils/contact-display';
|
||||
|
|
@ -14,6 +26,7 @@
|
|||
contacts: Contact[];
|
||||
onContactClick: (id: string) => void;
|
||||
onToggleFavorite: (e: MouseEvent, id: string) => void;
|
||||
onDeleteContact?: (id: string) => void;
|
||||
selectionMode?: boolean;
|
||||
selectedIds?: Set<string>;
|
||||
onToggleSelection?: (id: string) => void;
|
||||
|
|
@ -25,6 +38,7 @@
|
|||
contacts,
|
||||
onContactClick,
|
||||
onToggleFavorite,
|
||||
onDeleteContact,
|
||||
selectionMode = false,
|
||||
selectedIds = new Set(),
|
||||
onToggleSelection,
|
||||
|
|
@ -32,6 +46,55 @@
|
|||
showNewContactCard = true,
|
||||
}: Props = $props();
|
||||
|
||||
// Context menu state
|
||||
let contactContextMenu = $state({ visible: false, x: 0, y: 0, target: null as Contact | null });
|
||||
|
||||
function handleContactContextMenu(e: MouseEvent, contact: Contact) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
contactContextMenu = { visible: true, x: e.clientX, y: e.clientY, target: contact };
|
||||
}
|
||||
|
||||
function getContactContextMenuItems(contact: Contact): ContextMenuItem[] {
|
||||
return [
|
||||
{
|
||||
id: 'open',
|
||||
label: 'Öffnen',
|
||||
icon: ArrowSquareOut,
|
||||
action: () => onContactClick(contact.id),
|
||||
},
|
||||
{
|
||||
id: 'favorite',
|
||||
label: contact.isFavorite ? 'Favorit entfernen' : 'Zu Favoriten',
|
||||
icon: contact.isFavorite ? HeartBreak : Heart,
|
||||
action: () => onToggleFavorite(new MouseEvent('click'), contact.id),
|
||||
},
|
||||
{ id: 'divider-1', label: '', type: 'divider' },
|
||||
{
|
||||
id: 'call',
|
||||
label: 'Anrufen',
|
||||
icon: Phone,
|
||||
disabled: !contact.phone && !contact.mobile,
|
||||
action: () => window.open('tel:' + (contact.mobile || contact.phone)),
|
||||
},
|
||||
{
|
||||
id: 'email',
|
||||
label: 'E-Mail schreiben',
|
||||
icon: Envelope,
|
||||
disabled: !contact.email,
|
||||
action: () => window.open('mailto:' + contact.email),
|
||||
},
|
||||
{ id: 'divider-2', label: '', type: 'divider' },
|
||||
{
|
||||
id: 'delete',
|
||||
label: 'Löschen',
|
||||
icon: Trash,
|
||||
variant: 'danger',
|
||||
action: () => onDeleteContact?.(contact.id),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
// Derived state for toolbar positioning
|
||||
let isToolbarExpanded = $derived(!contactsFilterStore.isToolbarCollapsed);
|
||||
let isAlphabetNavCollapsed = $derived(contactsFilterStore.isAlphabetNavCollapsed);
|
||||
|
|
@ -194,6 +257,7 @@
|
|||
? 'selected'
|
||||
: ''}"
|
||||
onclick={() => onContactClick(contact.id)}
|
||||
oncontextmenu={(e) => handleContactContextMenu(e, contact)}
|
||||
>
|
||||
<!-- Selection Checkbox -->
|
||||
{#if selectionMode}
|
||||
|
|
@ -360,6 +424,14 @@
|
|||
<AlphabetNavContextMenu bind:this={alphabetContextMenu} />
|
||||
</div>
|
||||
|
||||
<ContextMenu
|
||||
visible={contactContextMenu.visible}
|
||||
x={contactContextMenu.x}
|
||||
y={contactContextMenu.y}
|
||||
items={contactContextMenu.target ? getContactContextMenuItems(contactContextMenu.target) : []}
|
||||
onClose={() => (contactContextMenu = { visible: false, x: 0, y: 0, target: null })}
|
||||
/>
|
||||
|
||||
<style>
|
||||
.alphabet-view {
|
||||
display: block;
|
||||
|
|
@ -423,6 +495,12 @@
|
|||
border: 1px solid hsl(var(--border));
|
||||
border-radius: var(--radius-md);
|
||||
min-width: 0;
|
||||
cursor: pointer;
|
||||
transition: background-color 150ms ease;
|
||||
}
|
||||
|
||||
.alphabet-contact-card:hover {
|
||||
background-color: hsl(var(--accent));
|
||||
}
|
||||
|
||||
.avatar-sm {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,15 @@
|
|||
<script lang="ts">
|
||||
import { Plus, Check, Heart, Phone, Envelope } from '@manacore/shared-icons';
|
||||
import {
|
||||
Plus,
|
||||
Check,
|
||||
Heart,
|
||||
HeartBreak,
|
||||
Phone,
|
||||
Envelope,
|
||||
ArrowSquareOut,
|
||||
Trash,
|
||||
Archive,
|
||||
} from '@manacore/shared-icons';
|
||||
import { _ } from 'svelte-i18n';
|
||||
import type { Contact } from '$lib/api/contacts';
|
||||
import { getDisplayName, getInitials } from '$lib/utils/contact-display';
|
||||
|
|
@ -41,23 +51,27 @@
|
|||
{
|
||||
id: 'open',
|
||||
label: 'Öffnen',
|
||||
icon: ArrowSquareOut,
|
||||
action: () => onContactClick(contact.id),
|
||||
},
|
||||
{
|
||||
id: 'favorite',
|
||||
label: contact.isFavorite ? 'Favorit entfernen' : 'Favorit',
|
||||
label: contact.isFavorite ? 'Favorit entfernen' : 'Zu Favoriten',
|
||||
icon: contact.isFavorite ? HeartBreak : Heart,
|
||||
action: () => onToggleFavorite(new MouseEvent('click'), contact.id),
|
||||
},
|
||||
{ id: 'divider-1', label: '', type: 'divider' },
|
||||
{
|
||||
id: 'call',
|
||||
label: 'Anrufen',
|
||||
icon: Phone,
|
||||
disabled: !contact.phone && !contact.mobile,
|
||||
action: () => window.open('tel:' + (contact.mobile || contact.phone)),
|
||||
},
|
||||
{
|
||||
id: 'email',
|
||||
label: 'E-Mail',
|
||||
label: 'E-Mail schreiben',
|
||||
icon: Envelope,
|
||||
disabled: !contact.email,
|
||||
action: () => window.open('mailto:' + contact.email),
|
||||
},
|
||||
|
|
@ -65,6 +79,7 @@
|
|||
{
|
||||
id: 'delete',
|
||||
label: 'Löschen',
|
||||
icon: Trash,
|
||||
variant: 'danger',
|
||||
action: () => onDeleteContact?.(contact.id),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -5,6 +5,16 @@
|
|||
import { getContext, untrack } from 'svelte';
|
||||
import { tasksStore } from '$lib/stores/tasks.svelte';
|
||||
import { ContextMenu, type ContextMenuItem } from '@manacore/shared-ui';
|
||||
import {
|
||||
PencilSimple,
|
||||
CheckSquare,
|
||||
ArrowCircleUp,
|
||||
ArrowDown,
|
||||
ArrowRight,
|
||||
ArrowUp,
|
||||
Lightning,
|
||||
Trash,
|
||||
} from '@manacore/shared-icons';
|
||||
|
||||
// Context menu state
|
||||
let contextMenuVisible = $state(false);
|
||||
|
|
@ -29,35 +39,41 @@
|
|||
{
|
||||
id: 'edit',
|
||||
label: 'Bearbeiten',
|
||||
icon: PencilSimple,
|
||||
action: () => handleExpandTask(task.id),
|
||||
},
|
||||
{
|
||||
id: 'toggle-complete',
|
||||
label: task.isCompleted ? 'Als offen markieren' : 'Als erledigt markieren',
|
||||
icon: task.isCompleted ? ArrowCircleUp : CheckSquare,
|
||||
action: () => handleToggleComplete(task),
|
||||
},
|
||||
{ id: 'divider-1', label: '', type: 'divider' },
|
||||
{
|
||||
id: 'priority-low',
|
||||
label: 'Niedrig',
|
||||
icon: ArrowDown,
|
||||
action: () => handleSetPriority(task.id, 'low'),
|
||||
disabled: task.priority === 'low',
|
||||
},
|
||||
{
|
||||
id: 'priority-medium',
|
||||
label: 'Mittel',
|
||||
icon: ArrowRight,
|
||||
action: () => handleSetPriority(task.id, 'medium'),
|
||||
disabled: task.priority === 'medium',
|
||||
},
|
||||
{
|
||||
id: 'priority-high',
|
||||
label: 'Hoch',
|
||||
icon: ArrowUp,
|
||||
action: () => handleSetPriority(task.id, 'high'),
|
||||
disabled: task.priority === 'high',
|
||||
},
|
||||
{
|
||||
id: 'priority-urgent',
|
||||
label: 'Dringend',
|
||||
icon: Lightning,
|
||||
action: () => handleSetPriority(task.id, 'urgent'),
|
||||
disabled: task.priority === 'urgent',
|
||||
},
|
||||
|
|
@ -67,6 +83,7 @@
|
|||
items.push({
|
||||
id: 'delete',
|
||||
label: 'Löschen',
|
||||
icon: Trash,
|
||||
variant: 'danger',
|
||||
action: () => handleDelete(task.id),
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue