@bimetal/calendar-svelte-components 0.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +129 -0
- package/dist/binding/config.d.ts +6 -0
- package/dist/binding/config.d.ts.map +1 -0
- package/dist/binding/config.js +5 -0
- package/dist/binding/context.d.ts +31 -0
- package/dist/binding/context.d.ts.map +1 -0
- package/dist/binding/context.js +55 -0
- package/dist/binding/controller.d.ts +24 -0
- package/dist/binding/controller.d.ts.map +1 -0
- package/dist/binding/controller.js +9 -0
- package/dist/binding/index.d.ts +23 -0
- package/dist/binding/index.d.ts.map +1 -0
- package/dist/binding/index.js +24 -0
- package/dist/binding/types.d.ts +95 -0
- package/dist/binding/types.d.ts.map +1 -0
- package/dist/binding/types.js +11 -0
- package/dist/binding/view-configs.d.ts +6 -0
- package/dist/binding/view-configs.d.ts.map +1 -0
- package/dist/binding/view-configs.js +5 -0
- package/dist/components/Calendar.svelte +263 -0
- package/dist/components/Calendar.svelte.d.ts +24 -0
- package/dist/components/Calendar.svelte.d.ts.map +1 -0
- package/dist/components/CalendarHeader.svelte +47 -0
- package/dist/components/CalendarHeader.svelte.d.ts +15 -0
- package/dist/components/CalendarHeader.svelte.d.ts.map +1 -0
- package/dist/components/CategoryFilter.svelte +29 -0
- package/dist/components/CategoryFilter.svelte.d.ts +10 -0
- package/dist/components/CategoryFilter.svelte.d.ts.map +1 -0
- package/dist/components/EventBar.svelte +187 -0
- package/dist/components/EventBar.svelte.d.ts +10 -0
- package/dist/components/EventBar.svelte.d.ts.map +1 -0
- package/dist/components/EventDialog.svelte +312 -0
- package/dist/components/EventDialog.svelte.d.ts +15 -0
- package/dist/components/EventDialog.svelte.d.ts.map +1 -0
- package/dist/components/EventHistory.svelte +89 -0
- package/dist/components/EventHistory.svelte.d.ts +10 -0
- package/dist/components/EventHistory.svelte.d.ts.map +1 -0
- package/dist/components/ResourceFilter.svelte +28 -0
- package/dist/components/ResourceFilter.svelte.d.ts +10 -0
- package/dist/components/ResourceFilter.svelte.d.ts.map +1 -0
- package/dist/components/UndoToast.svelte +47 -0
- package/dist/components/UndoToast.svelte.d.ts +4 -0
- package/dist/components/UndoToast.svelte.d.ts.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/styles/index.css +1 -0
- package/dist/views/agenda/AgendaEvent.svelte +55 -0
- package/dist/views/agenda/AgendaEvent.svelte.d.ts +14 -0
- package/dist/views/agenda/AgendaEvent.svelte.d.ts.map +1 -0
- package/dist/views/agenda/AgendaView.svelte +155 -0
- package/dist/views/agenda/AgendaView.svelte.d.ts +8 -0
- package/dist/views/agenda/AgendaView.svelte.d.ts.map +1 -0
- package/dist/views/day/AllDayEvent.svelte +49 -0
- package/dist/views/day/AllDayEvent.svelte.d.ts +12 -0
- package/dist/views/day/AllDayEvent.svelte.d.ts.map +1 -0
- package/dist/views/day/DayView.svelte +252 -0
- package/dist/views/day/DayView.svelte.d.ts +8 -0
- package/dist/views/day/DayView.svelte.d.ts.map +1 -0
- package/dist/views/month/MonthView.svelte +66 -0
- package/dist/views/month/MonthView.svelte.d.ts +8 -0
- package/dist/views/month/MonthView.svelte.d.ts.map +1 -0
- package/dist/views/month/TimedEntry.svelte +51 -0
- package/dist/views/month/TimedEntry.svelte.d.ts +12 -0
- package/dist/views/month/TimedEntry.svelte.d.ts.map +1 -0
- package/dist/views/month/WeekRow.svelte +224 -0
- package/dist/views/month/WeekRow.svelte.d.ts +12 -0
- package/dist/views/month/WeekRow.svelte.d.ts.map +1 -0
- package/dist/views/resource/ResourceView.svelte +227 -0
- package/dist/views/resource/ResourceView.svelte.d.ts +11 -0
- package/dist/views/resource/ResourceView.svelte.d.ts.map +1 -0
- package/dist/views/timeline/TimelineView.svelte +178 -0
- package/dist/views/timeline/TimelineView.svelte.d.ts +12 -0
- package/dist/views/timeline/TimelineView.svelte.d.ts.map +1 -0
- package/dist/views/week/AllDayRow.svelte +74 -0
- package/dist/views/week/AllDayRow.svelte.d.ts +9 -0
- package/dist/views/week/AllDayRow.svelte.d.ts.map +1 -0
- package/dist/views/week/TimeEvent.svelte +192 -0
- package/dist/views/week/TimeEvent.svelte.d.ts +25 -0
- package/dist/views/week/TimeEvent.svelte.d.ts.map +1 -0
- package/dist/views/week/WeekView.svelte +232 -0
- package/dist/views/week/WeekView.svelte.d.ts +8 -0
- package/dist/views/week/WeekView.svelte.d.ts.map +1 -0
- package/dist/views/year/YearView.svelte +92 -0
- package/dist/views/year/YearView.svelte.d.ts +8 -0
- package/dist/views/year/YearView.svelte.d.ts.map +1 -0
- package/package.json +56 -0
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { DialogState, EventDraft } from '@bimetal/calendar-headless';
|
|
3
|
+
import { getCalendarConfig, getCalendarContext } from '../binding/index.js';
|
|
4
|
+
import type { DomainEvent } from '@bimetal/calendar-data';
|
|
5
|
+
import { tick } from 'svelte';
|
|
6
|
+
import { focusTrap } from '@bimetal/svelte';
|
|
7
|
+
import EventHistory from './EventHistory.svelte';
|
|
8
|
+
|
|
9
|
+
let {
|
|
10
|
+
dialog,
|
|
11
|
+
setDraft,
|
|
12
|
+
onSave,
|
|
13
|
+
onDelete,
|
|
14
|
+
onClose,
|
|
15
|
+
}: {
|
|
16
|
+
/** The controller-owned dialog state (draft, flags, validation). */
|
|
17
|
+
dialog: DialogState;
|
|
18
|
+
/** Patch the controlled draft from raw input (controller applies the policy). */
|
|
19
|
+
setDraft: (patch: Partial<EventDraft>) => void;
|
|
20
|
+
/** Assemble + validate + save the held draft (controller owns all of it). */
|
|
21
|
+
onSave: () => void;
|
|
22
|
+
onDelete: (eventId: string) => void;
|
|
23
|
+
onClose: () => void;
|
|
24
|
+
} = $props();
|
|
25
|
+
|
|
26
|
+
const config = getCalendarConfig();
|
|
27
|
+
const ctx = getCalendarContext<{
|
|
28
|
+
getHistory: (entityId: string) => DomainEvent[];
|
|
29
|
+
}>();
|
|
30
|
+
|
|
31
|
+
let dialogEl: HTMLDivElement | undefined = $state();
|
|
32
|
+
let showHistory = $state(false);
|
|
33
|
+
|
|
34
|
+
// The controller OWNS the draft + all flags; this component is fully
|
|
35
|
+
// controlled — no local field state, no assembly, no id minting (B-CAL-2).
|
|
36
|
+
const draft = $derived(dialog.draft);
|
|
37
|
+
const isNew = $derived(dialog.isNew);
|
|
38
|
+
const isInstance = $derived(dialog.isInstance);
|
|
39
|
+
const validationErrors = $derived(dialog.validationErrors);
|
|
40
|
+
const event = $derived(dialog.event);
|
|
41
|
+
|
|
42
|
+
const history = $derived(event ? ctx.getHistory(event.id) : []);
|
|
43
|
+
|
|
44
|
+
function positionDialog() {
|
|
45
|
+
const el = dialogEl;
|
|
46
|
+
if (!el) return;
|
|
47
|
+
const anchorRect = dialog.anchorRect;
|
|
48
|
+
const dialogRect = el.getBoundingClientRect();
|
|
49
|
+
const vw = window.innerWidth;
|
|
50
|
+
const vh = window.innerHeight;
|
|
51
|
+
|
|
52
|
+
let left = anchorRect.right + 8;
|
|
53
|
+
if (left + dialogRect.width > vw - 16) {
|
|
54
|
+
left = anchorRect.left - dialogRect.width - 8;
|
|
55
|
+
}
|
|
56
|
+
if (left < 16) {
|
|
57
|
+
left = Math.max(16, anchorRect.left + anchorRect.width / 2 - dialogRect.width / 2);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let top = anchorRect.top;
|
|
61
|
+
if (top + dialogRect.height > vh - 16) {
|
|
62
|
+
top = vh - dialogRect.height - 16;
|
|
63
|
+
}
|
|
64
|
+
if (top < 16) top = 16;
|
|
65
|
+
|
|
66
|
+
el.style.left = `${left}px`;
|
|
67
|
+
el.style.top = `${top}px`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Reposition whenever the anchor changes (mirrors React's effect on anchorRect).
|
|
71
|
+
$effect(() => {
|
|
72
|
+
void dialog.anchorRect;
|
|
73
|
+
void tick().then(positionDialog);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// v0.29 Tastatur: focus lifecycle via the shared `focusTrap` action (contract §3).
|
|
77
|
+
// Escape is element-scoped (no window keydown); focus restores to the opener else the
|
|
78
|
+
// `.calendar` root via the trap's closest()-anchor (the dialog is a `.calendar`
|
|
79
|
+
// descendant). Click-outside becomes the backdrop element's onclick below.
|
|
80
|
+
|
|
81
|
+
function handleKeyDown(e: KeyboardEvent) {
|
|
82
|
+
if (e.key === 'Enter' && !e.shiftKey) {
|
|
83
|
+
e.preventDefault();
|
|
84
|
+
onSave();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
</script>
|
|
88
|
+
|
|
89
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
90
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
91
|
+
<div class="dialog-backdrop" onclick={onClose}></div>
|
|
92
|
+
<!-- svelte-ignore a11y_interactive_supports_focus -->
|
|
93
|
+
<div
|
|
94
|
+
bind:this={dialogEl}
|
|
95
|
+
class="event-dialog"
|
|
96
|
+
role="dialog"
|
|
97
|
+
aria-modal="true"
|
|
98
|
+
aria-label={isNew ? config.locale.newEvent : config.locale.editEvent}
|
|
99
|
+
onkeydown={handleKeyDown}
|
|
100
|
+
use:focusTrap={{ onEscape: onClose }}
|
|
101
|
+
>
|
|
102
|
+
{#if showHistory && event}
|
|
103
|
+
<EventHistory
|
|
104
|
+
eventId={event.id}
|
|
105
|
+
{history}
|
|
106
|
+
onBack={() => { showHistory = false; }}
|
|
107
|
+
/>
|
|
108
|
+
{:else}
|
|
109
|
+
<!-- Header -->
|
|
110
|
+
<div class="dialog-header">
|
|
111
|
+
<span class="dialog-title">
|
|
112
|
+
{isNew ? config.locale.newEvent : config.locale.editEvent}
|
|
113
|
+
</span>
|
|
114
|
+
<button class="dialog-close" aria-label={config.locale.close} onclick={onClose}>×</button>
|
|
115
|
+
</div>
|
|
116
|
+
|
|
117
|
+
<!-- Body -->
|
|
118
|
+
<div class="dialog-body">
|
|
119
|
+
<input
|
|
120
|
+
class="dialog-input dialog-input--title"
|
|
121
|
+
type="text"
|
|
122
|
+
placeholder={config.locale.titlePlaceholder}
|
|
123
|
+
value={draft.title}
|
|
124
|
+
oninput={(e) => setDraft({ title: (e.target as HTMLInputElement).value })}
|
|
125
|
+
/>
|
|
126
|
+
|
|
127
|
+
<label class="dialog-toggle">
|
|
128
|
+
<span>{config.locale.allDay}</span>
|
|
129
|
+
<input
|
|
130
|
+
type="checkbox"
|
|
131
|
+
checked={draft.allDay}
|
|
132
|
+
onchange={(e) => setDraft({ allDay: (e.target as HTMLInputElement).checked })}
|
|
133
|
+
/>
|
|
134
|
+
</label>
|
|
135
|
+
|
|
136
|
+
{#if !isInstance}
|
|
137
|
+
<div class="dialog-recurrence">
|
|
138
|
+
<label class="dialog-toggle">
|
|
139
|
+
<span>Wiederholen</span>
|
|
140
|
+
<input
|
|
141
|
+
type="checkbox"
|
|
142
|
+
checked={draft.recurring}
|
|
143
|
+
onchange={(e) => setDraft({ recurring: (e.target as HTMLInputElement).checked })}
|
|
144
|
+
/>
|
|
145
|
+
</label>
|
|
146
|
+
{#if draft.recurring}
|
|
147
|
+
<div class="dialog-row" style="gap: 8px; align-items: center;">
|
|
148
|
+
<span style="font-size: 13px;">Alle</span>
|
|
149
|
+
<input class="dialog-input" type="number" min="1" max="99" style="width: 50px;"
|
|
150
|
+
value={draft.recInterval}
|
|
151
|
+
oninput={(e) => setDraft({ recInterval: parseInt((e.target as HTMLInputElement).value) })} />
|
|
152
|
+
<select class="dialog-input"
|
|
153
|
+
value={draft.recFreq}
|
|
154
|
+
onchange={(e) => setDraft({ recFreq: (e.target as HTMLSelectElement).value as EventDraft['recFreq'] })}>
|
|
155
|
+
<option value="daily">Tage</option>
|
|
156
|
+
<option value="weekly">Wochen</option>
|
|
157
|
+
<option value="monthly">Monate</option>
|
|
158
|
+
<option value="yearly">Jahre</option>
|
|
159
|
+
</select>
|
|
160
|
+
<label class="dialog-label" style="flex-direction: row; align-items: center; gap: 6px;">
|
|
161
|
+
<span style="font-size: 13px;">bis</span>
|
|
162
|
+
<input class="dialog-input" type="date"
|
|
163
|
+
value={draft.recUntil}
|
|
164
|
+
onchange={(e) => setDraft({ recUntil: (e.target as HTMLInputElement).value })} />
|
|
165
|
+
</label>
|
|
166
|
+
</div>
|
|
167
|
+
{/if}
|
|
168
|
+
</div>
|
|
169
|
+
{/if}
|
|
170
|
+
|
|
171
|
+
{#if isInstance}
|
|
172
|
+
<div style="font-size: 12px; color: var(--cal-color-text-muted); padding: 4px 0;">
|
|
173
|
+
↻ Instanz einer wiederkehrenden Serie
|
|
174
|
+
</div>
|
|
175
|
+
{/if}
|
|
176
|
+
|
|
177
|
+
<!-- Start date row -->
|
|
178
|
+
<div class="dialog-row">
|
|
179
|
+
<label class="dialog-label">
|
|
180
|
+
<span>{config.locale.from}</span>
|
|
181
|
+
<input
|
|
182
|
+
class="dialog-input"
|
|
183
|
+
type="date"
|
|
184
|
+
value={draft.startDate}
|
|
185
|
+
onchange={(e) => setDraft({ startDate: (e.target as HTMLInputElement).value })}
|
|
186
|
+
/>
|
|
187
|
+
</label>
|
|
188
|
+
{#if !draft.allDay}
|
|
189
|
+
<input
|
|
190
|
+
class="dialog-input dialog-input--time"
|
|
191
|
+
type="time"
|
|
192
|
+
value={draft.startTime}
|
|
193
|
+
onchange={(e) => setDraft({ startTime: (e.target as HTMLInputElement).value })}
|
|
194
|
+
/>
|
|
195
|
+
{/if}
|
|
196
|
+
</div>
|
|
197
|
+
|
|
198
|
+
<!-- End date row -->
|
|
199
|
+
<div class="dialog-row">
|
|
200
|
+
<label class="dialog-label">
|
|
201
|
+
<span>{config.locale.to}</span>
|
|
202
|
+
<input
|
|
203
|
+
class="dialog-input"
|
|
204
|
+
type="date"
|
|
205
|
+
value={draft.endDate}
|
|
206
|
+
min={draft.startDate}
|
|
207
|
+
onchange={(e) => setDraft({ endDate: (e.target as HTMLInputElement).value })}
|
|
208
|
+
/>
|
|
209
|
+
</label>
|
|
210
|
+
{#if !draft.allDay}
|
|
211
|
+
<input
|
|
212
|
+
class="dialog-input dialog-input--time"
|
|
213
|
+
type="time"
|
|
214
|
+
value={draft.endTime}
|
|
215
|
+
onchange={(e) => setDraft({ endTime: (e.target as HTMLInputElement).value })}
|
|
216
|
+
/>
|
|
217
|
+
{/if}
|
|
218
|
+
</div>
|
|
219
|
+
|
|
220
|
+
<!-- Category -->
|
|
221
|
+
<label class="dialog-label">
|
|
222
|
+
<span>{config.locale.category}</span>
|
|
223
|
+
<div class="dialog-category-row">
|
|
224
|
+
<select
|
|
225
|
+
class="dialog-input"
|
|
226
|
+
value={draft.category}
|
|
227
|
+
onchange={(e) => setDraft({ category: (e.target as HTMLSelectElement).value })}
|
|
228
|
+
>
|
|
229
|
+
{#each config.categories as cat (cat.name)}
|
|
230
|
+
<option value={cat.name}>{cat.name}</option>
|
|
231
|
+
{/each}
|
|
232
|
+
</select>
|
|
233
|
+
<span class="category-dot" style:background-color={draft.color}></span>
|
|
234
|
+
</div>
|
|
235
|
+
</label>
|
|
236
|
+
|
|
237
|
+
{#if config.resources.length > 0}
|
|
238
|
+
<label class="dialog-label">
|
|
239
|
+
<span>{config.locale.resources ?? 'Resources'}</span>
|
|
240
|
+
<select
|
|
241
|
+
class="dialog-input"
|
|
242
|
+
value={draft.resourceId}
|
|
243
|
+
onchange={(e) => setDraft({ resourceId: (e.target as HTMLSelectElement).value })}
|
|
244
|
+
>
|
|
245
|
+
<option value="">—</option>
|
|
246
|
+
{#each config.resources as r (r.id)}
|
|
247
|
+
<option value={r.id}>{r.name}</option>
|
|
248
|
+
{/each}
|
|
249
|
+
</select>
|
|
250
|
+
</label>
|
|
251
|
+
{/if}
|
|
252
|
+
|
|
253
|
+
<!-- Location -->
|
|
254
|
+
<label class="dialog-label">
|
|
255
|
+
<span>{config.locale.location}</span>
|
|
256
|
+
<input
|
|
257
|
+
class="dialog-input"
|
|
258
|
+
type="text"
|
|
259
|
+
value={draft.location}
|
|
260
|
+
oninput={(e) => setDraft({ location: (e.target as HTMLInputElement).value })}
|
|
261
|
+
/>
|
|
262
|
+
</label>
|
|
263
|
+
|
|
264
|
+
<!-- Description -->
|
|
265
|
+
<label class="dialog-label">
|
|
266
|
+
<span>{config.locale.description}</span>
|
|
267
|
+
<textarea
|
|
268
|
+
class="dialog-input dialog-input--description"
|
|
269
|
+
value={draft.description}
|
|
270
|
+
oninput={(e) => setDraft({ description: (e.target as HTMLTextAreaElement).value })}
|
|
271
|
+
></textarea>
|
|
272
|
+
</label>
|
|
273
|
+
</div>
|
|
274
|
+
|
|
275
|
+
<!-- Validation errors -->
|
|
276
|
+
{#if validationErrors.length > 0}
|
|
277
|
+
<div class="dialog-errors">
|
|
278
|
+
{#each validationErrors as err, i (i)}
|
|
279
|
+
<div class="dialog-error">{err}</div>
|
|
280
|
+
{/each}
|
|
281
|
+
</div>
|
|
282
|
+
{/if}
|
|
283
|
+
|
|
284
|
+
<!-- Footer -->
|
|
285
|
+
<div class="dialog-footer">
|
|
286
|
+
{#if !isNew}
|
|
287
|
+
{#if config.locale.delete !== false}
|
|
288
|
+
<button class="dialog-btn dialog-btn--delete" onclick={() => onDelete(event!.id)}>
|
|
289
|
+
{config.locale.delete}
|
|
290
|
+
</button>
|
|
291
|
+
{/if}
|
|
292
|
+
{#if history.length > 0}
|
|
293
|
+
<button class="dialog-btn dialog-btn--history" onclick={() => { showHistory = true; }}>
|
|
294
|
+
{config.locale.history}
|
|
295
|
+
</button>
|
|
296
|
+
{/if}
|
|
297
|
+
{/if}
|
|
298
|
+
<div class="dialog-footer-right">
|
|
299
|
+
<button class="dialog-btn dialog-btn--cancel" onclick={onClose}>
|
|
300
|
+
{config.locale.cancel}
|
|
301
|
+
</button>
|
|
302
|
+
<button
|
|
303
|
+
class="dialog-btn dialog-btn--save"
|
|
304
|
+
onclick={onSave}
|
|
305
|
+
disabled={!draft.title.trim()}
|
|
306
|
+
>
|
|
307
|
+
{isNew ? config.locale.create : config.locale.save}
|
|
308
|
+
</button>
|
|
309
|
+
</div>
|
|
310
|
+
</div>
|
|
311
|
+
{/if}
|
|
312
|
+
</div>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { DialogState, EventDraft } from '@bimetal/calendar-headless';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
/** The controller-owned dialog state (draft, flags, validation). */
|
|
4
|
+
dialog: DialogState;
|
|
5
|
+
/** Patch the controlled draft from raw input (controller applies the policy). */
|
|
6
|
+
setDraft: (patch: Partial<EventDraft>) => void;
|
|
7
|
+
/** Assemble + validate + save the held draft (controller owns all of it). */
|
|
8
|
+
onSave: () => void;
|
|
9
|
+
onDelete: (eventId: string) => void;
|
|
10
|
+
onClose: () => void;
|
|
11
|
+
};
|
|
12
|
+
declare const EventDialog: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
13
|
+
type EventDialog = ReturnType<typeof EventDialog>;
|
|
14
|
+
export default EventDialog;
|
|
15
|
+
//# sourceMappingURL=EventDialog.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EventDialog.svelte.d.ts","sourceRoot":"","sources":["../../src/components/EventDialog.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAOzE,KAAK,gBAAgB,GAAI;IACtB,oEAAoE;IACpE,MAAM,EAAE,WAAW,CAAC;IACpB,iFAAiF;IACjF,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC;IAC/C,6EAA6E;IAC7E,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAyOJ,QAAA,MAAM,WAAW,sDAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { DomainEvent } from '@bimetal/calendar-data';
|
|
3
|
+
import { getCalendarConfig } from '../binding/index.js';
|
|
4
|
+
|
|
5
|
+
const eventTypeColors: Record<string, string> = {
|
|
6
|
+
'bimetal.calendar.CalendarEventCreated': '#4CD964',
|
|
7
|
+
'bimetal.calendar.CalendarEventUpdated': '#007AFF',
|
|
8
|
+
'bimetal.calendar.CalendarEventDeleted': '#FF3B30',
|
|
9
|
+
'bimetal.calendar.CalendarEventReverted': '#FF9500',
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
let {
|
|
13
|
+
eventId,
|
|
14
|
+
history,
|
|
15
|
+
onBack,
|
|
16
|
+
}: {
|
|
17
|
+
eventId: string;
|
|
18
|
+
history: DomainEvent[];
|
|
19
|
+
onBack: () => void;
|
|
20
|
+
} = $props();
|
|
21
|
+
|
|
22
|
+
const config = getCalendarConfig();
|
|
23
|
+
const eventTypeLabels = config.locale.historyEventTypes;
|
|
24
|
+
|
|
25
|
+
function formatTime(timestamp: number): string {
|
|
26
|
+
return new Date(timestamp).toLocaleString(config.locale.code, {
|
|
27
|
+
day: '2-digit', month: '2-digit', year: 'numeric',
|
|
28
|
+
hour: '2-digit', minute: '2-digit',
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function getSummary(event: DomainEvent): string {
|
|
33
|
+
const payload = event.payload as Record<string, unknown>;
|
|
34
|
+
switch (event.type) {
|
|
35
|
+
case 'bimetal.calendar.CalendarEventCreated': {
|
|
36
|
+
const ev = payload.event as { title?: string };
|
|
37
|
+
return ev?.title ?? '';
|
|
38
|
+
}
|
|
39
|
+
case 'bimetal.calendar.CalendarEventUpdated': {
|
|
40
|
+
const changes = payload.changes as Record<string, unknown> | undefined;
|
|
41
|
+
if (!changes) return '';
|
|
42
|
+
return Object.keys(changes).join(', ');
|
|
43
|
+
}
|
|
44
|
+
default:
|
|
45
|
+
return '';
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<div class="event-history">
|
|
51
|
+
<div class="event-history__header">
|
|
52
|
+
<button class="event-history__back" onclick={onBack}>←</button>
|
|
53
|
+
<span class="event-history__title">{config.locale.historyTitle}</span>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
{#if history.length === 0}
|
|
57
|
+
<div class="event-history__loading">{config.locale.historyLoading}</div>
|
|
58
|
+
{/if}
|
|
59
|
+
|
|
60
|
+
<div class="event-history__timeline">
|
|
61
|
+
{#each history as entry, idx (entry.envelope.id ?? idx)}
|
|
62
|
+
<div class="event-history__entry">
|
|
63
|
+
<div class="event-history__dot-col">
|
|
64
|
+
<div
|
|
65
|
+
class="event-history__dot"
|
|
66
|
+
style:background-color={eventTypeColors[entry.type] ?? '#8E8E93'}
|
|
67
|
+
></div>
|
|
68
|
+
{#if idx < history.length - 1}
|
|
69
|
+
<div class="event-history__line"></div>
|
|
70
|
+
{/if}
|
|
71
|
+
</div>
|
|
72
|
+
<div class="event-history__content">
|
|
73
|
+
<div class="event-history__entry-header">
|
|
74
|
+
<span
|
|
75
|
+
class="event-history__badge"
|
|
76
|
+
style:background-color={eventTypeColors[entry.type] ?? '#8E8E93'}
|
|
77
|
+
>
|
|
78
|
+
{eventTypeLabels[entry.type] ?? entry.type}
|
|
79
|
+
</span>
|
|
80
|
+
<span class="event-history__time">{formatTime(entry.envelope.timestamp)}</span>
|
|
81
|
+
</div>
|
|
82
|
+
{#if getSummary(entry)}
|
|
83
|
+
<div class="event-history__summary">{getSummary(entry)}</div>
|
|
84
|
+
{/if}
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
{/each}
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { DomainEvent } from '@bimetal/calendar-data';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
eventId: string;
|
|
4
|
+
history: DomainEvent[];
|
|
5
|
+
onBack: () => void;
|
|
6
|
+
};
|
|
7
|
+
declare const EventHistory: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
8
|
+
type EventHistory = ReturnType<typeof EventHistory>;
|
|
9
|
+
export default EventHistory;
|
|
10
|
+
//# sourceMappingURL=EventHistory.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EventHistory.svelte.d.ts","sourceRoot":"","sources":["../../src/components/EventHistory.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAGzD,KAAK,gBAAgB,GAAI;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB,CAAC;AAmFJ,QAAA,MAAM,YAAY,sDAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Resource } from '@bimetal/calendar-core';
|
|
3
|
+
|
|
4
|
+
let {
|
|
5
|
+
resources,
|
|
6
|
+
hiddenResources,
|
|
7
|
+
onToggle,
|
|
8
|
+
}: {
|
|
9
|
+
resources: Resource[];
|
|
10
|
+
hiddenResources: ReadonlySet<string>;
|
|
11
|
+
onToggle: (resourceId: string) => void;
|
|
12
|
+
} = $props();
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
{#if resources.length > 0}
|
|
16
|
+
<div class="resource-filter">
|
|
17
|
+
{#each resources as r (r.id)}
|
|
18
|
+
<button
|
|
19
|
+
type="button"
|
|
20
|
+
class="resource-filter__pill{hiddenResources.has(r.id) ? ' resource-filter__pill--hidden' : ''}"
|
|
21
|
+
style:border-left-color={r.color ?? 'transparent'}
|
|
22
|
+
onclick={() => onToggle(r.id)}
|
|
23
|
+
>
|
|
24
|
+
{r.name}
|
|
25
|
+
</button>
|
|
26
|
+
{/each}
|
|
27
|
+
</div>
|
|
28
|
+
{/if}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Resource } from '@bimetal/calendar-core';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
resources: Resource[];
|
|
4
|
+
hiddenResources: ReadonlySet<string>;
|
|
5
|
+
onToggle: (resourceId: string) => void;
|
|
6
|
+
};
|
|
7
|
+
declare const ResourceFilter: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
8
|
+
type ResourceFilter = ReturnType<typeof ResourceFilter>;
|
|
9
|
+
export default ResourceFilter;
|
|
10
|
+
//# sourceMappingURL=ResourceFilter.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ResourceFilter.svelte.d.ts","sourceRoot":"","sources":["../../src/components/ResourceFilter.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAEtD,KAAK,gBAAgB,GAAI;IACtB,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,eAAe,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACrC,QAAQ,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;CACxC,CAAC;AAuBJ,QAAA,MAAM,cAAc,sDAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getCalendarContext, getCalendarConfig } from '../binding/index.js';
|
|
3
|
+
import { DEFAULT_UNDO_DISMISS_MS } from '@bimetal/calendar-headless';
|
|
4
|
+
import { onDestroy } from 'svelte';
|
|
5
|
+
|
|
6
|
+
const ctx = getCalendarContext<{
|
|
7
|
+
lastAction: { entityId: string; description: string } | null;
|
|
8
|
+
onUndo: (entityId: string) => Promise<void>;
|
|
9
|
+
dismissUndo: () => void;
|
|
10
|
+
}>();
|
|
11
|
+
const config = getCalendarConfig();
|
|
12
|
+
|
|
13
|
+
let timer: ReturnType<typeof setTimeout> | null = null;
|
|
14
|
+
let prevAction: typeof ctx.lastAction = null;
|
|
15
|
+
|
|
16
|
+
$effect(() => {
|
|
17
|
+
const action = ctx.lastAction;
|
|
18
|
+
if (action !== prevAction) {
|
|
19
|
+
prevAction = action;
|
|
20
|
+
if (timer) clearTimeout(timer);
|
|
21
|
+
if (action) {
|
|
22
|
+
timer = setTimeout(() => ctx.dismissUndo(), DEFAULT_UNDO_DISMISS_MS);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
onDestroy(() => {
|
|
28
|
+
if (timer) clearTimeout(timer);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
async function handleUndo() {
|
|
32
|
+
const action = ctx.lastAction;
|
|
33
|
+
if (!action) return;
|
|
34
|
+
await ctx.onUndo(action.entityId);
|
|
35
|
+
ctx.dismissUndo();
|
|
36
|
+
}
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
{#if ctx.lastAction}
|
|
40
|
+
<div class="undo-toast" role="status">
|
|
41
|
+
<span class="undo-toast__text">{ctx.lastAction.description}</span>
|
|
42
|
+
<button class="undo-toast__btn" onclick={handleUndo}>
|
|
43
|
+
{config.locale.undo ?? 'Undo'}
|
|
44
|
+
</button>
|
|
45
|
+
<button class="undo-toast__dismiss" onclick={() => ctx.dismissUndo()}>×</button>
|
|
46
|
+
</div>
|
|
47
|
+
{/if}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UndoToast.svelte.d.ts","sourceRoot":"","sources":["../../src/components/UndoToast.svelte.ts"],"names":[],"mappings":"AAyDA,QAAA,MAAM,SAAS,2DAAwC,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export { default as Calendar } from './components/Calendar.svelte';
|
|
2
|
+
export { default as CalendarHeader } from './components/CalendarHeader.svelte';
|
|
3
|
+
export { default as CategoryFilter } from './components/CategoryFilter.svelte';
|
|
4
|
+
export { default as EventBar } from './components/EventBar.svelte';
|
|
5
|
+
export { default as EventDialog } from './components/EventDialog.svelte';
|
|
6
|
+
export { default as EventHistory } from './components/EventHistory.svelte';
|
|
7
|
+
export { default as UndoToast } from './components/UndoToast.svelte';
|
|
8
|
+
export { default as MonthView } from './views/month/MonthView.svelte';
|
|
9
|
+
export { default as WeekRow } from './views/month/WeekRow.svelte';
|
|
10
|
+
export { default as WeekView } from './views/week/WeekView.svelte';
|
|
11
|
+
export { default as AllDayRow } from './views/week/AllDayRow.svelte';
|
|
12
|
+
export { default as TimeEvent } from './views/week/TimeEvent.svelte';
|
|
13
|
+
export { default as DayView } from './views/day/DayView.svelte';
|
|
14
|
+
export { default as AgendaView } from './views/agenda/AgendaView.svelte';
|
|
15
|
+
export { default as YearView } from './views/year/YearView.svelte';
|
|
16
|
+
export { default as ResourceView } from './views/resource/ResourceView.svelte';
|
|
17
|
+
export { default as TimelineView } from './views/timeline/TimelineView.svelte';
|
|
18
|
+
export * from './binding/index.js';
|
|
19
|
+
export * from '@bimetal/svelte';
|
|
20
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAC/E,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAC/E,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAC3E,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,gCAAgC,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,sCAAsC,CAAC;AAC/E,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,sCAAsC,CAAC;AAG/E,cAAc,oBAAoB,CAAC;AAKnC,cAAc,iBAAiB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export { default as Calendar } from './components/Calendar.svelte';
|
|
2
|
+
export { default as CalendarHeader } from './components/CalendarHeader.svelte';
|
|
3
|
+
export { default as CategoryFilter } from './components/CategoryFilter.svelte';
|
|
4
|
+
export { default as EventBar } from './components/EventBar.svelte';
|
|
5
|
+
export { default as EventDialog } from './components/EventDialog.svelte';
|
|
6
|
+
export { default as EventHistory } from './components/EventHistory.svelte';
|
|
7
|
+
export { default as UndoToast } from './components/UndoToast.svelte';
|
|
8
|
+
export { default as MonthView } from './views/month/MonthView.svelte';
|
|
9
|
+
export { default as WeekRow } from './views/month/WeekRow.svelte';
|
|
10
|
+
export { default as WeekView } from './views/week/WeekView.svelte';
|
|
11
|
+
export { default as AllDayRow } from './views/week/AllDayRow.svelte';
|
|
12
|
+
export { default as TimeEvent } from './views/week/TimeEvent.svelte';
|
|
13
|
+
export { default as DayView } from './views/day/DayView.svelte';
|
|
14
|
+
export { default as AgendaView } from './views/agenda/AgendaView.svelte';
|
|
15
|
+
export { default as YearView } from './views/year/YearView.svelte';
|
|
16
|
+
export { default as ResourceView } from './views/resource/ResourceView.svelte';
|
|
17
|
+
export { default as TimelineView } from './views/timeline/TimelineView.svelte';
|
|
18
|
+
// ── Calendar Svelte binding (context/config/controller binding) ──
|
|
19
|
+
export * from './binding/index.js';
|
|
20
|
+
// Re-export the adapter package so consumers get providers/stores/types from a
|
|
21
|
+
// single dependency. Notably required for the "Calendar-Orchester selbst
|
|
22
|
+
// aufziehen"-Pfad (see doc/03-cookbook/07).
|
|
23
|
+
export * from '@bimetal/svelte';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@import '@bimetal/calendar-themes';
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { CalendarEvent } from '@bimetal/calendar-core';
|
|
3
|
+
import { getRecurrenceInstanceMetadata } from '@bimetal/calendar-core';
|
|
4
|
+
import { getEventSlots } from '../../binding/index.js';
|
|
5
|
+
|
|
6
|
+
let {
|
|
7
|
+
ev,
|
|
8
|
+
timeLabel,
|
|
9
|
+
category,
|
|
10
|
+
isSelected,
|
|
11
|
+
onEntryMouseDown,
|
|
12
|
+
onEntryClick,
|
|
13
|
+
onEntryDoubleClick,
|
|
14
|
+
}: {
|
|
15
|
+
ev: CalendarEvent;
|
|
16
|
+
timeLabel: string;
|
|
17
|
+
category: string;
|
|
18
|
+
isSelected: boolean;
|
|
19
|
+
onEntryMouseDown: () => void;
|
|
20
|
+
onEntryClick: (e: MouseEvent, id: string, instanceDate?: number) => void;
|
|
21
|
+
onEntryDoubleClick: (e: MouseEvent, id: string, instanceDate?: number) => void;
|
|
22
|
+
} = $props();
|
|
23
|
+
|
|
24
|
+
const slots = $derived(getEventSlots(ev));
|
|
25
|
+
const renderedTitle = $derived(slots.title ?? ev.title);
|
|
26
|
+
const extraClass = $derived(slots.className ? ' ' + slots.className : '');
|
|
27
|
+
const slotStyleStr = $derived(slots.style
|
|
28
|
+
? Object.entries(slots.style).map(([k, v]) => `${k.replace(/[A-Z]/g, m => '-' + m.toLowerCase())}: ${v}`).join('; ')
|
|
29
|
+
: '');
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
33
|
+
<div
|
|
34
|
+
class="agenda-view__event{isSelected ? ' agenda-view__event--selected' : ''}{extraClass}"
|
|
35
|
+
style={slotStyleStr}
|
|
36
|
+
role="button"
|
|
37
|
+
tabindex="0"
|
|
38
|
+
onmousedown={onEntryMouseDown}
|
|
39
|
+
onclick={(e) => onEntryClick(e, ev.id, getRecurrenceInstanceMetadata(ev)?.instanceDate)}
|
|
40
|
+
ondblclick={(e) => onEntryDoubleClick(e, ev.id, getRecurrenceInstanceMetadata(ev)?.instanceDate)}
|
|
41
|
+
>
|
|
42
|
+
{#if slots.content}
|
|
43
|
+
{#if typeof slots.content === 'string'}{slots.content}{:else}{@render slots.content()}{/if}
|
|
44
|
+
{:else}
|
|
45
|
+
<div class="agenda-view__event-color" style:background-color={ev.metadata.color ?? '#007AFF'}></div>
|
|
46
|
+
<div class="agenda-view__event-content">
|
|
47
|
+
<div class="agenda-view__event-title">{renderedTitle}</div>
|
|
48
|
+
<div class="agenda-view__event-time">{timeLabel}</div>
|
|
49
|
+
</div>
|
|
50
|
+
<div class="agenda-view__event-calendar">{category}</div>
|
|
51
|
+
{/if}
|
|
52
|
+
{#if slots.badges}
|
|
53
|
+
{#if typeof slots.badges === 'string'}{slots.badges}{:else}{@render slots.badges()}{/if}
|
|
54
|
+
{/if}
|
|
55
|
+
</div>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { CalendarEvent } from '@bimetal/calendar-core';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
ev: CalendarEvent;
|
|
4
|
+
timeLabel: string;
|
|
5
|
+
category: string;
|
|
6
|
+
isSelected: boolean;
|
|
7
|
+
onEntryMouseDown: () => void;
|
|
8
|
+
onEntryClick: (e: MouseEvent, id: string, instanceDate?: number) => void;
|
|
9
|
+
onEntryDoubleClick: (e: MouseEvent, id: string, instanceDate?: number) => void;
|
|
10
|
+
};
|
|
11
|
+
declare const AgendaEvent: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
12
|
+
type AgendaEvent = ReturnType<typeof AgendaEvent>;
|
|
13
|
+
export default AgendaEvent;
|
|
14
|
+
//# sourceMappingURL=AgendaEvent.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AgendaEvent.svelte.d.ts","sourceRoot":"","sources":["../../../src/views/agenda/AgendaEvent.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAI3D,KAAK,gBAAgB,GAAI;IACtB,EAAE,EAAE,aAAa,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,YAAY,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACzE,kBAAkB,EAAE,CAAC,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAChF,CAAC;AA2CJ,QAAA,MAAM,WAAW,sDAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
|