1<script lang="ts">
2 import { desktop } from '$lib/states/desktop.svelte';
3 import { chatState } from '$lib/states/chat.svelte';
4
5 const currentUser = $derived(chatState.getCurrentUser());
6
7 interface DesktopIcon {
8 id: string;
9 label: string;
10 icon: string;
11 action: () => void;
12 hidden?: boolean;
13 }
14
15 const icons = $derived<DesktopIcon[]>([
16 {
17 id: 'aim',
18 label: 'PDR AIM',
19 icon: '/desktop/pdraim-icon.png',
20 // Signed out: the sign-on window (which opens the buddy list and unlocks the
21 // chat room once logged in). Signed in: the buddy list.
22 action: () => (currentUser ? desktop.openBuddyList() : desktop.openLogin('signin'))
23 },
24 {
25 id: 'general',
26 label: 'Salon General',
27 icon: '/aim/chat-room-48.png',
28 action: () => desktop.openChatRoom(null, 'General')
29 },
30 {
31 id: 'away',
32 label: 'Message d’absence',
33 icon: '/aim/xp-note-48.png',
34 action: () => desktop.openAway(),
35 hidden: !currentUser
36 },
37 {
38 id: 'prefs',
39 label: 'Préférences',
40 icon: '/aim/xp-control-panel-48.png',
41 action: () => desktop.openPreferences()
42 },
43 {
44 id: 'about',
45 label: 'Aide',
46 icon: '/aim/xp-help-48.png',
47 action: () => desktop.openAbout()
48 }
49 ]);
50
51 let selected = $state<string | null>(null);
52
53 function handleKeydown(event: KeyboardEvent, action: () => void) {
54 if (event.key === 'Enter' || event.key === ' ') {
55 event.preventDefault();
56 action();
57 }
58 }
59</script>
60
61<div class="desktop-icons">
62 {#each icons.filter((i) => !i.hidden) as icon (icon.id)}
63 <div
64 class="desktop-icon"
65 class:selected={selected === icon.id}
66 onclick={(e) => {
67 e.stopPropagation();
68 selected = icon.id;
69 }}
70 ondblclick={icon.action}
71 onkeydown={(e) => handleKeydown(e, icon.action)}
72 onfocus={() => (selected = icon.id)}
73 role="button"
74 tabindex="0"
75 >
76 <img src={icon.icon} alt="" />
77 <span class="icon-label">{icon.label}</span>
78 </div>
79 {/each}
80</div>
81
82<style>
83 .desktop-icons {
84 position: fixed;
85 top: 16px;
86 left: 12px;
87 display: flex;
88 flex-direction: column;
89 gap: 10px;
90 z-index: 1;
91 }
92
93 .desktop-icon {
94 display: flex;
95 flex-direction: column;
96 align-items: center;
97 cursor: default;
98 width: 80px;
99 text-align: center;
100 padding: 6px 4px;
101 border: 1px dotted transparent;
102 border-radius: 2px;
103 outline: none;
104 }
105
106 .desktop-icon:hover {
107 background: rgba(49, 106, 197, 0.15);
108 }
109
110 .desktop-icon.selected,
111 .desktop-icon:focus-visible {
112 background: rgba(49, 106, 197, 0.45);
113 border-color: rgba(255, 255, 255, 0.8);
114 }
115
116 .desktop-icon img {
117 width: 40px;
118 height: 40px;
119 margin-bottom: 4px;
120 pointer-events: none;
121 image-rendering: pixelated;
122 filter: drop-shadow(1px 1px 1px rgba(0, 0, 0, 0.6));
123 }
124
125 .icon-label {
126 color: white;
127 text-shadow:
128 1px 1px 1px rgba(0, 0, 0, 0.9),
129 0 0 4px rgba(0, 0, 0, 0.6);
130 font-family: Tahoma, 'Pixelated MS Sans Serif', sans-serif;
131 font-size: 11px;
132 user-select: none;
133 pointer-events: none;
134 max-width: 100%;
135 overflow-wrap: break-word;
136 line-height: 1.2;
137 }
138
139 @media (max-width: 768px) {
140 .desktop-icons {
141 display: none;
142 }
143 }
144</style>