@dayflow/plugin-sidebar 1.2.4 → 1.2.6
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/dist/index.d.ts +5 -1
- package/dist/index.esm.js +169 -73
- package/dist/index.js +168 -72
- package/dist/types/DefaultCalendarSidebar.d.ts +1 -1
- package/dist/types/components/DeleteCalendarDialog.d.ts +2 -2
- package/dist/types/components/ImportCalendarDialog.d.ts +1 -1
- package/dist/types/components/MergeCalendarDialog.d.ts +1 -1
- package/dist/types/plugin.d.ts +5 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ICalendarApp, CalendarType, TNode, SidebarHeaderSlotArgs, CreateCalendarDialogProps, CalendarPlugin } from '@dayflow/core';
|
|
1
|
+
import { ICalendarApp, CalendarType, TNode, SidebarHeaderSlotArgs, CreateCalendarDialogProps, Event, CalendarPlugin } from '@dayflow/core';
|
|
2
2
|
export { SidebarHeaderSlotArgs } from '@dayflow/core';
|
|
3
3
|
|
|
4
4
|
interface CalendarSidebarRenderProps {
|
|
@@ -15,6 +15,8 @@ interface CalendarSidebarRenderProps {
|
|
|
15
15
|
editingCalendarId?: string | null;
|
|
16
16
|
setEditingCalendarId?: (id: string | null) => void;
|
|
17
17
|
onCreateCalendar?: () => void;
|
|
18
|
+
onSubscribeCalendar?: (calendar: CalendarType, events: Event[]) => Promise<void>;
|
|
19
|
+
onLoadSubscription?: (calendar: CalendarType) => Promise<void>;
|
|
18
20
|
}
|
|
19
21
|
interface SidebarPluginConfig {
|
|
20
22
|
width?: number | string;
|
|
@@ -25,6 +27,8 @@ interface SidebarPluginConfig {
|
|
|
25
27
|
renderCalendarContextMenu?: (calendar: CalendarType, onClose: () => void) => TNode;
|
|
26
28
|
renderSidebarHeader?: (args: SidebarHeaderSlotArgs) => TNode;
|
|
27
29
|
renderCreateCalendarDialog?: (props: CreateCalendarDialogProps) => TNode;
|
|
30
|
+
onSubscribeCalendar?: (calendar: CalendarType, events: Event[]) => Promise<void>;
|
|
31
|
+
onLoadSubscription?: (calendar: CalendarType) => Promise<void>;
|
|
28
32
|
[key: string]: unknown;
|
|
29
33
|
}
|
|
30
34
|
declare function createSidebarPlugin(config?: SidebarPluginConfig): CalendarPlugin;
|
package/dist/index.esm.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ChevronDown, AudioLines, useLocale, createPortal, cancelButton, ChevronsUpDown, Check, ChevronRight, sidebarHeader, sidebarHeaderToggle, PanelRightClose, PanelRightOpen, sidebarHeaderTitle, getCalendarColorsForHex, generateUniKey, downloadICS, ContentSlot, MiniCalendar, ContextMenu, ContextMenuLabel, ContextMenuItem, ContextMenuSeparator, ContextMenuColorPicker, DefaultColorPicker, BlossomColorPicker, importICSFile,
|
|
1
|
+
import { ChevronDown, AudioLines, AlertCircle, useLocale, createPortal, cancelButton, LoadingButton, ChevronsUpDown, Check, ChevronRight, sidebarHeader, sidebarHeaderToggle, PanelRightClose, PanelRightOpen, sidebarHeaderTitle, getCalendarColorsForHex, generateUniKey, downloadICS, ContentSlot, MiniCalendar, ContextMenu, ContextMenuLabel, ContextMenuItem, ContextMenuSeparator, ContextMenuColorPicker, DefaultColorPicker, BlossomColorPicker, importICSFile, subscribeCalendar, sidebarContainer, normalizeCssWidth, registerSidebarImplementation, CreateCalendarDialog } from '@dayflow/core';
|
|
2
2
|
import { options, Fragment, h } from 'preact';
|
|
3
3
|
import { useState, useRef, useCallback, useEffect, useMemo } from 'preact/hooks';
|
|
4
4
|
import { hexToHsl, lightnessToSliderValue } from '@dayflow/blossom-color-picker';
|
|
@@ -45,7 +45,7 @@ const getCalendarInitials = (calendar) => {
|
|
|
45
45
|
return name.charAt(0).toUpperCase();
|
|
46
46
|
};
|
|
47
47
|
const CalendarItem = ({ calendar, isDraggable, isEditable: _isEditable, editingId, editingName, setEditingName, editInputRef, draggedCalendarId, dropTarget, activeContextMenuCalendarId, onDragStart, onDragEnd, onDragOver, onDragLeave, onDrop, onContextMenu, onToggleVisibility, onRenameStart, onRenameSave, onRenameKeyDown, }) => {
|
|
48
|
-
var _a;
|
|
48
|
+
var _a, _b;
|
|
49
49
|
const isVisible = calendar.isVisible !== false;
|
|
50
50
|
const calendarColor = ((_a = calendar.colors) === null || _a === void 0 ? void 0 : _a.lineColor) || '#3b82f6';
|
|
51
51
|
const showIcon = Boolean(calendar.icon);
|
|
@@ -53,7 +53,9 @@ const CalendarItem = ({ calendar, isDraggable, isEditable: _isEditable, editingI
|
|
|
53
53
|
const isActive = activeContextMenuCalendarId === calendar.id || editingId === calendar.id;
|
|
54
54
|
return (u("li", { className: 'df-calendar-list-item relative', onDragOver: e => onDragOver(e, calendar.id), onDragLeave: onDragLeave, onDrop: () => onDrop(calendar), onContextMenu: e => onContextMenu(e, calendar.id), children: [isDropTarget && dropTarget.position === 'top' && (u("div", { className: 'pointer-events-none absolute top-0 right-0 left-0 z-10 h-0.5 bg-[var(--df-color-primary)]' })), u("div", { draggable: isDraggable && !editingId, onDragStart: e => onDragStart(calendar, e), onDragEnd: onDragEnd, className: `rounded transition ${draggedCalendarId === calendar.id ? 'opacity-50' : ''} ${isDraggable ? 'cursor-grab' : 'cursor-default'}`, children: u("div", { className: `group flex items-center rounded px-2 py-2 transition hover:bg-gray-100 dark:hover:bg-slate-800 ${isActive ? 'bg-gray-100 dark:bg-slate-800' : ''}`, title: calendar.name, children: [u("input", { type: 'checkbox', className: 'df-calendar-checkbox shrink-0 cursor-pointer', style: {
|
|
55
55
|
'--checkbox-color': calendarColor,
|
|
56
|
-
}, checked: isVisible, onChange: event => onToggleVisibility(calendar.id, event.target.checked) }), showIcon && (u("span", { className: 'ml-2 flex h-5 w-5 shrink-0 items-center justify-center text-xs font-semibold text-white', "aria-hidden": 'true', children: getCalendarInitials(calendar) })), editingId === calendar.id ? (u("input", { ref: editInputRef, type: 'text', value: editingName, onChange: e => setEditingName(e.target.value), onBlur: onRenameSave, onKeyDown: onRenameKeyDown, className: 'ml-2 h-5 min-w-0 flex-1 rounded bg-white px-0 py-0 text-sm text-gray-900 focus:outline-none dark:bg-slate-700 dark:text-gray-100', onClick: e => e.stopPropagation() })) : (u(Fragment, { children: [u("span", { className: 'ml-2 flex-1 truncate pl-1 text-sm text-gray-700 group-hover:text-gray-900 dark:text-gray-200 dark:group-hover:text-white', onDblClick: () => onRenameStart(calendar), children: calendar.name || calendar.id }), calendar.
|
|
56
|
+
}, checked: isVisible, onChange: event => onToggleVisibility(calendar.id, event.target.checked) }), showIcon && (u("span", { className: 'ml-2 flex h-5 w-5 shrink-0 items-center justify-center text-xs font-semibold text-white', "aria-hidden": 'true', children: getCalendarInitials(calendar) })), editingId === calendar.id ? (u("input", { ref: editInputRef, type: 'text', value: editingName, onChange: e => setEditingName(e.target.value), onBlur: onRenameSave, onKeyDown: onRenameKeyDown, className: 'ml-2 h-5 min-w-0 flex-1 rounded bg-white px-0 py-0 text-sm text-gray-900 focus:outline-none dark:bg-slate-700 dark:text-gray-100', onClick: e => e.stopPropagation() })) : (u(Fragment, { children: [u("span", { className: 'ml-2 flex-1 truncate pl-1 text-sm text-gray-700 group-hover:text-gray-900 dark:text-gray-200 dark:group-hover:text-white', onDblClick: () => onRenameStart(calendar), children: calendar.name || calendar.id }), ((_b = calendar.subscription) === null || _b === void 0 ? void 0 : _b.status) === 'error' && (u(AlertCircle, { width: 13, height: 13, className: 'ml-1 shrink-0 text-red-500', title: 'Failed to load subscription' })), calendar.subscribed &&
|
|
57
|
+
(!calendar.subscription ||
|
|
58
|
+
calendar.subscription.status === 'ready') && (u(AudioLines, { width: 13, height: 13, className: 'ml-1 shrink-0 text-gray-400 dark:text-gray-500' }))] }))] }) }), isDropTarget && dropTarget.position === 'bottom' && (u("div", { className: 'pointer-events-none absolute right-0 bottom-0 left-0 z-10 h-0.5 bg-[var(--df-color-primary)]' }))] }, calendar.id));
|
|
57
59
|
};
|
|
58
60
|
const CalendarList = ({ calendars, onToggleVisibility, onReorder, onRename, onContextMenu, editingId, setEditingId, activeContextMenuCalendarId, isDraggable = true, isEditable = true, }) => {
|
|
59
61
|
var _a;
|
|
@@ -245,16 +247,39 @@ function renderWithChip(template, name, color) {
|
|
|
245
247
|
const DeleteCalendarDialog = ({ calendarId, calendarName, calendars, step, onStepChange, onConfirmDelete, onCancel, onMergeSelect, }) => {
|
|
246
248
|
var _a, _b;
|
|
247
249
|
const [showMergeDropdown, setShowMergeDropdown] = useState(false);
|
|
250
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
248
251
|
const { t } = useLocale();
|
|
249
252
|
const calendarColor = (_b = (_a = calendars.find(c => c.id === calendarId)) === null || _a === void 0 ? void 0 : _a.colors.lineColor) !== null && _b !== void 0 ? _b : '#6b7280';
|
|
250
|
-
|
|
253
|
+
const handleMergeSelect = (id) => __awaiter(void 0, void 0, void 0, function* () {
|
|
254
|
+
if (isLoading)
|
|
255
|
+
return;
|
|
256
|
+
setIsLoading(true);
|
|
257
|
+
try {
|
|
258
|
+
yield onMergeSelect(id);
|
|
259
|
+
setShowMergeDropdown(false);
|
|
260
|
+
}
|
|
261
|
+
finally {
|
|
262
|
+
setIsLoading(false);
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
const handleConfirmDelete = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
266
|
+
if (isLoading)
|
|
267
|
+
return;
|
|
268
|
+
setIsLoading(true);
|
|
269
|
+
try {
|
|
270
|
+
yield onConfirmDelete();
|
|
271
|
+
}
|
|
272
|
+
finally {
|
|
273
|
+
setIsLoading(false);
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
return createPortal(u("div", { className: 'df-portal fixed inset-0 z-[9999] flex items-center justify-center bg-black/50', children: u("div", { className: 'w-full max-w-md rounded-lg bg-white p-6 shadow-xl dark:bg-gray-800', children: step === 'initial' ? (u(Fragment, { children: [u("h2", { className: 'text-lg font-semibold text-gray-900 dark:text-white', children: t('deleteCalendar', { calendarName }) }), u("p", { className: 'mt-3 flex flex-wrap items-center gap-y-0.5 text-sm text-gray-600 dark:text-gray-300', children: renderWithChip(t('deleteCalendarMessage', { calendarName: CAL_SENTINEL }), calendarName, calendarColor) }), u("div", { className: 'mt-6 flex items-center justify-between', children: [u("div", { className: 'relative', children: [u("button", { type: 'button', disabled: isLoading, onClick: () => setShowMergeDropdown(!showMergeDropdown), className: 'flex items-center gap-1 rounded-md border border-gray-300 px-4 py-2 text-xs font-medium text-gray-700 hover:bg-gray-50 disabled:opacity-50 dark:border-gray-600 dark:text-gray-200 dark:hover:bg-slate-700', children: t('merge') }), showMergeDropdown && (u("div", { className: 'absolute top-full left-0 z-10 mt-1 max-h-60 w-max min-w-full overflow-y-auto rounded-md border border-gray-200 bg-white shadow-lg dark:border-slate-700 dark:bg-gray-800', children: calendars
|
|
251
277
|
.filter(c => c.id !== calendarId)
|
|
252
278
|
.map(calendar => (u("div", { className: 'flex cursor-pointer items-center px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-slate-700', onClick: () => {
|
|
253
|
-
|
|
254
|
-
setShowMergeDropdown(false);
|
|
279
|
+
handleMergeSelect(calendar.id);
|
|
255
280
|
}, children: [u("div", { className: 'mr-2 h-3 w-3 shrink-0 rounded-sm', style: {
|
|
256
281
|
backgroundColor: calendar.colors.lineColor,
|
|
257
|
-
} }), u("span", { className: 'whitespace-nowrap', children: calendar.name || calendar.id })] }, calendar.id))) }))] }), u("div", { className: 'flex gap-3', children: [u("button", { type: 'button', onClick: onCancel, className: cancelButton
|
|
282
|
+
} }), u("span", { className: 'whitespace-nowrap', children: calendar.name || calendar.id })] }, calendar.id))) }))] }), u("div", { className: 'flex gap-3', children: [u("button", { type: 'button', onClick: onCancel, disabled: isLoading, className: `${cancelButton} disabled:opacity-50`, children: t('cancel') }), u("button", { type: 'button', onClick: () => onStepChange('confirm_delete'), disabled: isLoading, className: 'df-fill-destructive df-hover-destructive rounded-md px-4 py-2 text-xs font-medium disabled:opacity-50', children: t('delete') })] })] })] })) : (u(Fragment, { children: [u("h2", { className: 'text-lg font-semibold text-gray-900 dark:text-white', children: t('confirmDeleteTitle', { calendarName }) }), u("p", { className: 'mt-3 text-sm text-gray-600 dark:text-gray-300', children: t('confirmDeleteMessage') }), u("div", { className: 'mt-6 flex justify-end gap-3', children: [u("button", { type: 'button', onClick: onCancel, disabled: isLoading, className: 'rounded-md border border-border bg-background px-3 py-2 text-xs font-medium text-gray-700 hover:bg-gray-100 disabled:opacity-50 dark:text-gray-300 dark:hover:bg-slate-700', children: t('cancel') }), u(LoadingButton, { type: 'button', onClick: handleConfirmDelete, loading: isLoading, className: 'df-fill-destructive df-hover-destructive rounded-md px-3 py-2 text-xs font-medium', children: t('delete') })] })] })) }) }), document.body);
|
|
258
283
|
};
|
|
259
284
|
|
|
260
285
|
const NEW_CALENDAR_ID = 'new-calendar';
|
|
@@ -264,6 +289,7 @@ const ImportCalendarDialog = ({ calendars, filename, onConfirm, onCancel, }) =>
|
|
|
264
289
|
const [selectedCalendarId, setSelectedCalendarId] = useState(((_a = calendars[0]) === null || _a === void 0 ? void 0 : _a.id) || NEW_CALENDAR_ID);
|
|
265
290
|
const [isOpen, setIsOpen] = useState(false);
|
|
266
291
|
const [shouldRender, setShouldRender] = useState(false);
|
|
292
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
267
293
|
const dropdownRef = useRef(null);
|
|
268
294
|
const triggerRef = useRef(null);
|
|
269
295
|
useEffect(() => {
|
|
@@ -293,6 +319,17 @@ const ImportCalendarDialog = ({ calendars, filename, onConfirm, onCancel, }) =>
|
|
|
293
319
|
setSelectedCalendarId(id);
|
|
294
320
|
setIsOpen(false);
|
|
295
321
|
};
|
|
322
|
+
const handleConfirm = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
323
|
+
if (isLoading)
|
|
324
|
+
return;
|
|
325
|
+
setIsLoading(true);
|
|
326
|
+
try {
|
|
327
|
+
yield onConfirm(selectedCalendarId);
|
|
328
|
+
}
|
|
329
|
+
finally {
|
|
330
|
+
setIsLoading(false);
|
|
331
|
+
}
|
|
332
|
+
});
|
|
296
333
|
const renderDropdown = () => {
|
|
297
334
|
var _a;
|
|
298
335
|
if (!shouldRender)
|
|
@@ -300,17 +337,17 @@ const ImportCalendarDialog = ({ calendars, filename, onConfirm, onCancel, }) =>
|
|
|
300
337
|
const rect = (_a = triggerRef.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
|
|
301
338
|
if (!rect)
|
|
302
339
|
return null;
|
|
303
|
-
return createPortal(u("div", { ref: dropdownRef, className: `fixed z-110 mt-1 max-h-60 origin-top overflow-y-auto rounded-md border border-gray-200 bg-white shadow-lg transition-all duration-200 dark:border-slate-700 dark:bg-slate-800 ${isOpen ? 'scale-100 opacity-100' : 'scale-95 opacity-0'}`, style: {
|
|
340
|
+
return createPortal(u("div", { ref: dropdownRef, className: `df-portal fixed z-110 mt-1 max-h-60 origin-top overflow-y-auto rounded-md border border-gray-200 bg-white shadow-lg transition-all duration-200 dark:border-slate-700 dark:bg-slate-800 ${isOpen ? 'scale-100 opacity-100' : 'scale-95 opacity-0'}`, style: {
|
|
304
341
|
top: rect.bottom,
|
|
305
342
|
left: rect.left,
|
|
306
343
|
width: rect.width,
|
|
307
344
|
overscrollBehavior: 'none',
|
|
308
|
-
}, children: u("div", { className: 'py-1', children: [calendars.map(calendar => (u("div", { className: `flex cursor-pointer items-center px-3 py-2 hover:bg-gray-100 dark:hover:bg-slate-700 ${selectedCalendarId === calendar.id ? '
|
|
345
|
+
}, children: u("div", { className: 'py-1', children: [calendars.map(calendar => (u("div", { className: `flex cursor-pointer items-center px-3 py-2 hover:bg-gray-100 dark:hover:bg-slate-700 ${selectedCalendarId === calendar.id ? 'df-tint-primary' : ''}`, onClick: () => handleSelect(calendar.id), children: [u("div", { className: 'mr-3 h-3 w-3 shrink-0 rounded-sm', style: { backgroundColor: calendar.colors.lineColor } }), u("span", { className: `flex-1 truncate text-sm ${selectedCalendarId === calendar.id ? 'df-text-primary font-medium' : 'text-gray-700 dark:text-gray-200'}`, children: calendar.name || calendar.id }), selectedCalendarId === calendar.id && (u(Check, { className: 'df-text-primary ml-2 h-4 w-4 shrink-0' }))] }, calendar.id))), u("div", { className: 'my-1 border-t border-gray-100 dark:border-slate-700' }), u("div", { className: `flex cursor-pointer items-center px-3 py-2 hover:bg-gray-100 dark:hover:bg-slate-700 ${isNewSelected ? 'df-tint-primary' : ''}`, onClick: () => handleSelect(NEW_CALENDAR_ID), children: [u("span", { className: `flex-1 truncate text-sm ${isNewSelected ? 'df-text-primary font-medium' : 'pl-6 text-gray-700 dark:text-gray-200'}`, children: [t('newCalendar') || 'New Calendar', ": ", filename] }), isNewSelected && (u(Check, { className: 'df-text-primary ml-2 h-4 w-4 shrink-0' }))] })] }) }), document.body);
|
|
309
346
|
};
|
|
310
347
|
return (u("div", { className: 'df-portal fixed inset-0 z-100 flex items-center justify-center bg-black/50', children: u("div", { className: 'w-full max-w-md rounded-lg bg-white p-6 shadow-xl dark:bg-gray-900', children: [u("h2", { className: 'mb-4 text-lg font-semibold text-gray-900 dark:text-white', children: t('addSchedule') || 'Add Schedule' }), u("p", { className: 'mb-4 text-sm text-gray-600 dark:text-gray-300', children: t('importCalendarMessage') ||
|
|
311
|
-
'This calendar contains new events. Please select a target calendar.' }), u("div", { className: 'relative', children: [u("button", { ref: triggerRef, type: 'button', className: 'flex w-full items-center rounded-md border border-gray-300 px-3 py-2 shadow-sm transition-colors hover:bg-gray-50 dark:border-gray-600 dark:hover:bg-gray-800', onClick: () => setIsOpen(!isOpen), children: [!isNewSelected && selectedCalendar && (u("div", { className: 'mr-3 h-3 w-3 shrink-0 rounded-sm', style: { backgroundColor: selectedCalendar.colors.lineColor } })), u("span", { className: `flex-1 truncate text-left text-sm font-medium text-gray-700 dark:text-gray-200 ${isNewSelected ? 'pl-0' : ''}`, children: isNewSelected
|
|
348
|
+
'This calendar contains new events. Please select a target calendar.' }), u("div", { className: 'relative', children: [u("button", { ref: triggerRef, type: 'button', disabled: isLoading, className: 'flex w-full items-center rounded-md border border-gray-300 px-3 py-2 shadow-sm transition-colors hover:bg-gray-50 disabled:opacity-50 dark:border-gray-600 dark:hover:bg-gray-800', onClick: () => setIsOpen(!isOpen), children: [!isNewSelected && selectedCalendar && (u("div", { className: 'mr-3 h-3 w-3 shrink-0 rounded-sm', style: { backgroundColor: selectedCalendar.colors.lineColor } })), u("span", { className: `flex-1 truncate text-left text-sm font-medium text-gray-700 dark:text-gray-200 ${isNewSelected ? 'pl-0' : ''}`, children: isNewSelected
|
|
312
349
|
? `${t('newCalendar')}: ${filename}`
|
|
313
|
-
: (selectedCalendar === null || selectedCalendar === void 0 ? void 0 : selectedCalendar.name) || (selectedCalendar === null || selectedCalendar === void 0 ? void 0 : selectedCalendar.id) }), u(ChevronsUpDown, { className: 'ml-2 h-4 w-4 shrink-0 text-gray-400' })] }), renderDropdown()] }), u("div", { className: 'mt-8 flex justify-end gap-3', children: [u("button", { type: 'button', onClick: onCancel, className: cancelButton
|
|
350
|
+
: (selectedCalendar === null || selectedCalendar === void 0 ? void 0 : selectedCalendar.name) || (selectedCalendar === null || selectedCalendar === void 0 ? void 0 : selectedCalendar.id) }), u(ChevronsUpDown, { className: 'ml-2 h-4 w-4 shrink-0 text-gray-400' })] }), renderDropdown()] }), u("div", { className: 'mt-8 flex justify-end gap-3', children: [u("button", { type: 'button', onClick: onCancel, disabled: isLoading, className: `${cancelButton} disabled:opacity-50`, children: t('cancel') || 'Cancel' }), u(LoadingButton, { type: 'button', onClick: handleConfirm, loading: isLoading, className: 'df-fill-primary df-hover-primary-solid rounded-md px-6 py-2 text-sm font-medium shadow-sm transition-colors', children: t('ok') || 'OK' })] })] }) }));
|
|
314
351
|
};
|
|
315
352
|
|
|
316
353
|
const SOURCE_SENTINEL = '\u0001S\u0001';
|
|
@@ -328,6 +365,7 @@ function renderLine(line, source, target) {
|
|
|
328
365
|
}
|
|
329
366
|
const MergeCalendarDialog = ({ sourceName, sourceColor, targetName, targetColor, onConfirm, onCancel, }) => {
|
|
330
367
|
const { t } = useLocale();
|
|
368
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
331
369
|
const source = { name: sourceName, color: sourceColor };
|
|
332
370
|
const target = { name: targetName, color: targetColor };
|
|
333
371
|
const messageTemplate = t('mergeConfirmMessage', {
|
|
@@ -335,7 +373,18 @@ const MergeCalendarDialog = ({ sourceName, sourceColor, targetName, targetColor,
|
|
|
335
373
|
targetName: TARGET_SENTINEL,
|
|
336
374
|
});
|
|
337
375
|
const messageLines = messageTemplate.split('\n');
|
|
338
|
-
|
|
376
|
+
const handleConfirm = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
377
|
+
if (isLoading)
|
|
378
|
+
return;
|
|
379
|
+
setIsLoading(true);
|
|
380
|
+
try {
|
|
381
|
+
yield onConfirm();
|
|
382
|
+
}
|
|
383
|
+
finally {
|
|
384
|
+
setIsLoading(false);
|
|
385
|
+
}
|
|
386
|
+
});
|
|
387
|
+
return (u("div", { className: 'df-portal fixed inset-0 z-[9999] flex items-center justify-center bg-black/50', children: u("div", { className: 'rounded-lg bg-white p-6 shadow-xl dark:bg-gray-800', children: [u("h2", { className: 'text-lg font-semibold text-gray-900 dark:text-white', children: t('mergeConfirmTitle', { sourceName, targetName }) }), u("div", { className: 'mt-3 space-y-1 text-sm text-gray-600 dark:text-gray-300', children: messageLines.map((line, i) => (u("p", { className: 'flex flex-wrap items-center gap-y-0.5', children: renderLine(line, source, target) }, i))) }), u("div", { className: 'mt-6 flex justify-end gap-3', children: [u("button", { type: 'button', onClick: onCancel, disabled: isLoading, className: `${cancelButton} disabled:opacity-50`, children: t('cancel') }), u(LoadingButton, { type: 'button', onClick: handleConfirm, loading: isLoading, className: 'df-fill-destructive df-hover-destructive rounded-md px-3 py-2 text-xs font-medium', children: t('merge') })] })] }) }));
|
|
339
388
|
};
|
|
340
389
|
|
|
341
390
|
const stopPropagation = (e) => e.stopPropagation();
|
|
@@ -398,8 +447,13 @@ const SubscribeCalendarDialog = ({ onSubscribe, onCancel, }) => {
|
|
|
398
447
|
try {
|
|
399
448
|
yield onSubscribe(trimmed);
|
|
400
449
|
}
|
|
401
|
-
catch (
|
|
402
|
-
|
|
450
|
+
catch (err) {
|
|
451
|
+
if (err.message === 'DUPLICATE_URL') {
|
|
452
|
+
setError(t('calendarAlreadySubscribed') || 'This URL is already subscribed');
|
|
453
|
+
}
|
|
454
|
+
else {
|
|
455
|
+
setError(t('subscribeError') || 'Failed to subscribe to calendar');
|
|
456
|
+
}
|
|
403
457
|
}
|
|
404
458
|
finally {
|
|
405
459
|
setLoading(false);
|
|
@@ -411,10 +465,10 @@ const SubscribeCalendarDialog = ({ onSubscribe, onCancel, }) => {
|
|
|
411
465
|
if (e.key === 'Escape')
|
|
412
466
|
onCancel();
|
|
413
467
|
};
|
|
414
|
-
return (u("div", { className: 'df-portal fixed inset-0 z-[9999] flex items-center justify-center bg-black/50', children: u("div", { className: 'w-full max-w-xl rounded-lg bg-white p-6 shadow-xl dark:bg-gray-800', children: [u("h2", { className: 'text-lg font-semibold text-gray-900 dark:text-white', children: t('subscribeCalendarTitle') }), u("div", { className: 'mt-4', children: [u("div", { className: 'flex items-center gap-3', children: [u("label", { className: 'shrink-0 text-sm font-medium text-gray-700 dark:text-gray-300', children: t('calendarUrl') }), u("input", { type: 'url', value: url, onInput: e => setUrl(e.target.value), onKeyDown: handleKeyDown, placeholder: t('calendarUrlPlaceholder'), disabled: loading, autoFocus: true, className: 'flex-1 rounded-md border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 placeholder-gray-400 focus:
|
|
468
|
+
return (u("div", { className: 'df-portal fixed inset-0 z-[9999] flex items-center justify-center bg-black/50', children: u("div", { className: 'w-full max-w-xl rounded-lg bg-white p-6 shadow-xl dark:bg-gray-800', children: [u("h2", { className: 'text-lg font-semibold text-gray-900 dark:text-white', children: t('subscribeCalendarTitle') }), u("div", { className: 'mt-4', children: [u("div", { className: 'flex items-center gap-3', children: [u("label", { className: 'shrink-0 text-sm font-medium text-gray-700 dark:text-gray-300', children: t('calendarUrl') }), u("input", { type: 'url', value: url, onInput: e => setUrl(e.target.value), onKeyDown: handleKeyDown, placeholder: t('calendarUrlPlaceholder'), disabled: loading, autoFocus: true, className: 'df-focus-ring flex-1 rounded-md border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 placeholder-gray-400 focus:ring-1 focus:outline-none disabled:opacity-50 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400' })] }), error && (u("p", { className: 'mt-2 text-xs text-red-500 dark:text-red-400', children: error }))] }), u("div", { className: 'mt-6 flex justify-end gap-3', children: [u("button", { type: 'button', onClick: onCancel, disabled: loading, className: cancelButton, children: t('cancel') }), u("button", { type: 'button', onClick: handleSubmit, disabled: loading || !url.trim(), className: 'df-fill-primary df-hover-primary-solid rounded-md px-4 py-2 text-xs font-medium disabled:cursor-not-allowed disabled:opacity-50', children: loading ? t('fetchingCalendar') : t('subscribe') })] })] }) }));
|
|
415
469
|
};
|
|
416
470
|
|
|
417
|
-
const DefaultCalendarSidebar = ({ app, calendars, toggleCalendarVisibility, isCollapsed, setCollapsed, renderCalendarContextMenu, renderSidebarHeader, editingCalendarId: propEditingCalendarId, setEditingCalendarId: propSetEditingCalendarId, onCreateCalendar, }) => {
|
|
471
|
+
const DefaultCalendarSidebar = ({ app, calendars, toggleCalendarVisibility, isCollapsed, setCollapsed, renderCalendarContextMenu, renderSidebarHeader, editingCalendarId: propEditingCalendarId, setEditingCalendarId: propSetEditingCalendarId, onCreateCalendar, onSubscribeCalendar, onLoadSubscription, }) => {
|
|
418
472
|
var _a, _b;
|
|
419
473
|
const { t } = useLocale();
|
|
420
474
|
// Detect if custom color picker slot is provided
|
|
@@ -427,6 +481,33 @@ const DefaultCalendarSidebar = ({ app, calendars, toggleCalendarVisibility, isCo
|
|
|
427
481
|
// File input ref for import
|
|
428
482
|
const fileInputRef = useRef(null);
|
|
429
483
|
const contextMenuRef = useRef(null);
|
|
484
|
+
// Track loaded subscription URLs to avoid redundant fetching
|
|
485
|
+
const loadedSubscriptionsRef = useRef(new Set());
|
|
486
|
+
// Auto-load subscriptions on mount or when calendars change
|
|
487
|
+
useEffect(() => {
|
|
488
|
+
calendars.forEach((calendar) => __awaiter(void 0, void 0, void 0, function* () {
|
|
489
|
+
var _a;
|
|
490
|
+
if (((_a = calendar.subscription) === null || _a === void 0 ? void 0 : _a.url) &&
|
|
491
|
+
!loadedSubscriptionsRef.current.has(calendar.subscription.url)) {
|
|
492
|
+
loadedSubscriptionsRef.current.add(calendar.subscription.url);
|
|
493
|
+
try {
|
|
494
|
+
if (onLoadSubscription) {
|
|
495
|
+
yield onLoadSubscription(calendar);
|
|
496
|
+
}
|
|
497
|
+
else {
|
|
498
|
+
const { events } = yield subscribeCalendar(calendar.subscription.url);
|
|
499
|
+
app.addExternalEvents(calendar.id, events);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
catch (err) {
|
|
503
|
+
console.error(`Failed to auto-load calendar ${calendar.name}:`, err);
|
|
504
|
+
app.updateCalendar(calendar.id, {
|
|
505
|
+
subscription: Object.assign(Object.assign({}, calendar.subscription), { status: 'error' }),
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}));
|
|
510
|
+
}, [app, calendars, onLoadSubscription]);
|
|
430
511
|
const handleMonthChange = useCallback((offset) => {
|
|
431
512
|
const current = app.getVisibleMonth();
|
|
432
513
|
const next = new Date(current.getFullYear(), current.getMonth() + offset, 1);
|
|
@@ -531,19 +612,19 @@ const DefaultCalendarSidebar = ({ app, calendars, toggleCalendarVisibility, isCo
|
|
|
531
612
|
handleCloseContextMenu();
|
|
532
613
|
}
|
|
533
614
|
}, [contextMenu, handleCloseContextMenu]);
|
|
534
|
-
const handleMergeConfirm = useCallback(() => {
|
|
615
|
+
const handleMergeConfirm = useCallback(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
535
616
|
if (mergeState) {
|
|
536
617
|
const { sourceId, targetId } = mergeState;
|
|
537
|
-
app.mergeCalendars(sourceId, targetId);
|
|
618
|
+
yield app.mergeCalendars(sourceId, targetId);
|
|
538
619
|
setMergeState(null);
|
|
539
620
|
}
|
|
540
|
-
}, [app, mergeState]);
|
|
541
|
-
const handleConfirmDelete = useCallback(() => {
|
|
621
|
+
}), [app, mergeState]);
|
|
622
|
+
const handleConfirmDelete = useCallback(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
542
623
|
if (deleteState) {
|
|
543
|
-
app.deleteCalendar(deleteState.calendarId);
|
|
624
|
+
yield app.deleteCalendar(deleteState.calendarId);
|
|
544
625
|
setDeleteState(null);
|
|
545
626
|
}
|
|
546
|
-
}, [app, deleteState]);
|
|
627
|
+
}), [app, deleteState]);
|
|
547
628
|
const handleDeleteMergeSelect = useCallback((targetId) => {
|
|
548
629
|
if (deleteState) {
|
|
549
630
|
setMergeState({
|
|
@@ -565,45 +646,31 @@ const DefaultCalendarSidebar = ({ app, calendars, toggleCalendarVisibility, isCo
|
|
|
565
646
|
handleCloseSidebarContextMenu();
|
|
566
647
|
}, [handleCloseSidebarContextMenu]);
|
|
567
648
|
const handleSubscribeConfirm = useCallback((url) => __awaiter(void 0, void 0, void 0, function* () {
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
const
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
const { colors: calendarColors, darkColors } = getCalendarColorsForHex(randomColor);
|
|
592
|
-
const calendarId = generateUniKey();
|
|
593
|
-
app.createCalendar({
|
|
594
|
-
id: calendarId,
|
|
595
|
-
name: calendarName,
|
|
596
|
-
isDefault: false,
|
|
597
|
-
colors: calendarColors,
|
|
598
|
-
darkColors,
|
|
599
|
-
isVisible: true,
|
|
600
|
-
subscribed: true,
|
|
601
|
-
});
|
|
602
|
-
result.events.forEach(event => {
|
|
603
|
-
app.addEvent(Object.assign(Object.assign({}, event), { calendarId }));
|
|
604
|
-
});
|
|
649
|
+
var _a;
|
|
650
|
+
// 1. Check for duplicates
|
|
651
|
+
const isDuplicate = calendars.some(c => { var _a; return ((_a = c.subscription) === null || _a === void 0 ? void 0 : _a.url) === url; });
|
|
652
|
+
if (isDuplicate) {
|
|
653
|
+
throw new Error('DUPLICATE_URL');
|
|
654
|
+
}
|
|
655
|
+
// 2. Load the subscription (fetch + parse) using the new utility
|
|
656
|
+
const { calendar, events } = yield subscribeCalendar(url);
|
|
657
|
+
// 3. Mark as loaded to avoid the useEffect triggering another fetch
|
|
658
|
+
if ((_a = calendar.subscription) === null || _a === void 0 ? void 0 : _a.url) {
|
|
659
|
+
loadedSubscriptionsRef.current.add(calendar.subscription.url);
|
|
660
|
+
}
|
|
661
|
+
// 4. Delegate to user if callback exists, otherwise use default behavior
|
|
662
|
+
if (onSubscribeCalendar) {
|
|
663
|
+
yield onSubscribeCalendar(calendar, events);
|
|
664
|
+
}
|
|
665
|
+
else {
|
|
666
|
+
// Default behavior: create calendar in the app
|
|
667
|
+
app.createCalendar(calendar);
|
|
668
|
+
}
|
|
669
|
+
// 4. Always add events to the internal external store for IMMEDIATE display
|
|
670
|
+
app.addExternalEvents(calendar.id, events);
|
|
671
|
+
// 5. Close dialog
|
|
605
672
|
setSubscribeDialogOpen(false);
|
|
606
|
-
}), [app]);
|
|
673
|
+
}), [app, onSubscribeCalendar, calendars]);
|
|
607
674
|
const handleFileChange = useCallback((e) => __awaiter(void 0, void 0, void 0, function* () {
|
|
608
675
|
var _a;
|
|
609
676
|
const file = (_a = e.currentTarget.files) === null || _a === void 0 ? void 0 : _a[0];
|
|
@@ -687,8 +754,20 @@ const DefaultCalendarSidebar = ({ app, calendars, toggleCalendarVisibility, isCo
|
|
|
687
754
|
? ((_a = calendars.find(c => c.id === deleteState.calendarId)) === null || _a === void 0 ? void 0 : _a.name) || 'Unknown'
|
|
688
755
|
: '';
|
|
689
756
|
const readOnlyConfig = app.getReadOnlyConfig();
|
|
690
|
-
const isEditable =
|
|
757
|
+
const isEditable = app.canMutateFromUI();
|
|
691
758
|
const isDraggable = readOnlyConfig.draggable !== false;
|
|
759
|
+
useEffect(() => {
|
|
760
|
+
if (isEditable)
|
|
761
|
+
return;
|
|
762
|
+
setContextMenu(null);
|
|
763
|
+
setSidebarContextMenu(null);
|
|
764
|
+
setCustomColorPicker(null);
|
|
765
|
+
setMergeState(null);
|
|
766
|
+
setDeleteState(null);
|
|
767
|
+
setImportState(null);
|
|
768
|
+
setSubscribeDialogOpen(false);
|
|
769
|
+
setEditingCalendarId(null);
|
|
770
|
+
}, [isEditable, setEditingCalendarId]);
|
|
692
771
|
return (u("div", { className: sidebarContainer, onContextMenu: isEditable ? handleSidebarContextMenu : undefined, children: [u(ContentSlot, { generatorName: 'sidebarHeader', generatorArgs: {
|
|
693
772
|
isCollapsed,
|
|
694
773
|
onCollapseToggle: () => setCollapsed(!isCollapsed),
|
|
@@ -719,21 +798,27 @@ const DefaultCalendarSidebar = ({ app, calendars, toggleCalendarVisibility, isCo
|
|
|
719
798
|
? handleContextMenu
|
|
720
799
|
: () => {
|
|
721
800
|
/* noop */
|
|
722
|
-
}, editingId: editingCalendarId, setEditingId: setEditingCalendarId, activeContextMenuCalendarId: contextMenu === null || contextMenu === void 0 ? void 0 : contextMenu.calendarId, isDraggable: isDraggable, isEditable: isEditable }), u("div", { className: 'border-t border-gray-200 dark:border-slate-800', children: u(MiniCalendar, { visibleMonth: app.getVisibleMonth(), currentDate: app.getCurrentDate(), showHeader: true, onMonthChange: handleMonthChange, onDateSelect: date => app.setCurrentDate(date) }) })] })), contextMenu && (u(ContextMenu, { ref: contextMenuRef, x: contextMenu.x, y: contextMenu.y, onClose: handleCloseContextMenu, className: 'w-64 p-2', children: u(ContentSlot, { generatorName: 'calendarContextMenu', generatorArgs: {
|
|
801
|
+
}, editingId: editingCalendarId, setEditingId: setEditingCalendarId, activeContextMenuCalendarId: contextMenu === null || contextMenu === void 0 ? void 0 : contextMenu.calendarId, isDraggable: isDraggable, isEditable: isEditable }), u("div", { className: 'border-t border-gray-200 dark:border-slate-800', children: u(MiniCalendar, { visibleMonth: app.getVisibleMonth(), currentDate: app.getCurrentDate(), showHeader: true, onMonthChange: handleMonthChange, onDateSelect: date => app.setCurrentDate(date) }) })] })), isEditable && contextMenu && (u(ContextMenu, { ref: contextMenuRef, x: contextMenu.x, y: contextMenu.y, onClose: handleCloseContextMenu, className: 'w-64 p-2', children: u(ContentSlot, { generatorName: 'calendarContextMenu', generatorArgs: {
|
|
723
802
|
calendar: calendars.find(c => c.id === contextMenu.calendarId),
|
|
724
803
|
onClose: handleCloseContextMenu,
|
|
725
|
-
}, defaultContent: renderCalendarContextMenu ? (renderCalendarContextMenu(calendars.find(c => c.id === contextMenu.calendarId), handleCloseContextMenu)) : (u(Fragment, { children: [u(ContextMenuLabel, { children: t('calendarOptions') }), u(MergeMenuItem, { calendars: calendars, currentCalendarId: contextMenu.calendarId, onMergeSelect: handleMergeSelect }), u(ContextMenuItem, { onClick: handleDeleteCalendar, children: t('delete') }), u(ContextMenuItem, { onClick: handleExportCalendar, children: t('exportCalendar') || 'Export Calendar' }), u(ContextMenuSeparator, {}), u(ContextMenuColorPicker, { selectedColor: (_b = calendars.find(c => c.id === contextMenu.calendarId)) === null || _b === void 0 ? void 0 : _b.colors.lineColor, onSelect: handleColorSelect, onCustomColor: handleCustomColor })] })) }) })),
|
|
804
|
+
}, defaultContent: renderCalendarContextMenu ? (renderCalendarContextMenu(calendars.find(c => c.id === contextMenu.calendarId), handleCloseContextMenu)) : (u(Fragment, { children: [u(ContextMenuLabel, { children: t('calendarOptions') }), u(MergeMenuItem, { calendars: calendars, currentCalendarId: contextMenu.calendarId, onMergeSelect: handleMergeSelect }), u(ContextMenuItem, { onClick: handleDeleteCalendar, children: t('delete') }), u(ContextMenuItem, { onClick: handleExportCalendar, children: t('exportCalendar') || 'Export Calendar' }), u(ContextMenuSeparator, {}), u(ContextMenuColorPicker, { selectedColor: (_b = calendars.find(c => c.id === contextMenu.calendarId)) === null || _b === void 0 ? void 0 : _b.colors.lineColor, onSelect: handleColorSelect, onCustomColor: handleCustomColor })] })) }) })), isEditable &&
|
|
805
|
+
sidebarContextMenu &&
|
|
726
806
|
createPortal(u(ContextMenu, { x: sidebarContextMenu.x, y: sidebarContextMenu.y, onClose: handleCloseSidebarContextMenu, className: 'w-max p-2', children: [u(ContextMenuItem, { onClick: () => {
|
|
727
807
|
onCreateCalendar === null || onCreateCalendar === void 0 ? void 0 : onCreateCalendar();
|
|
728
808
|
handleCloseSidebarContextMenu();
|
|
729
809
|
}, children: t('newCalendar') || 'New Calendar' }), u(ContextMenuItem, { onClick: handleImportClick, children: t('importCalendar') || 'Import Calendar' }), u(ContextMenuItem, { onClick: handleSubscribeClick, children: t('subscribeCalendar') || 'Subscribe to Calendar' }), u(ContextMenuItem, { onClick: () => {
|
|
730
810
|
app.triggerRender();
|
|
731
811
|
handleCloseSidebarContextMenu();
|
|
732
|
-
}, children: t('refreshAll') || 'Refresh All' })] }), document.body), u("input", { ref: fileInputRef, type: 'file', accept: '.ics', style: { display: 'none' }, onChange: handleFileChange }),
|
|
733
|
-
|
|
734
|
-
createPortal(u(
|
|
735
|
-
|
|
736
|
-
createPortal(u(
|
|
812
|
+
}, children: t('refreshAll') || 'Refresh All' })] }), document.body), u("input", { ref: fileInputRef, type: 'file', accept: '.ics', style: { display: 'none' }, onChange: handleFileChange }), isEditable &&
|
|
813
|
+
importState &&
|
|
814
|
+
createPortal(u(ImportCalendarDialog, { calendars: calendars, filename: importState.filename, onConfirm: handleImportConfirm, onCancel: () => setImportState(null) }), document.body), isEditable &&
|
|
815
|
+
subscribeDialogOpen &&
|
|
816
|
+
createPortal(u(SubscribeCalendarDialog, { onSubscribe: handleSubscribeConfirm, onCancel: () => setSubscribeDialogOpen(false) }), document.body), isEditable &&
|
|
817
|
+
mergeState &&
|
|
818
|
+
createPortal(u(MergeCalendarDialog, { sourceName: sourceCalendarName, sourceColor: sourceCalendarColor, targetName: targetCalendarName, targetColor: targetCalendarColor, onConfirm: handleMergeConfirm, onCancel: () => setMergeState(null) }), document.body), isEditable &&
|
|
819
|
+
deleteState &&
|
|
820
|
+
createPortal(u(DeleteCalendarDialog, { calendarId: deleteState.calendarId, calendarName: deleteCalendarName, calendars: calendars, step: deleteState.step, onStepChange: step => setDeleteState(prev => (prev ? Object.assign(Object.assign({}, prev), { step }) : null)), onConfirmDelete: handleConfirmDelete, onCancel: () => setDeleteState(null), onMergeSelect: handleDeleteMergeSelect }), document.body), isEditable &&
|
|
821
|
+
customColorPicker &&
|
|
737
822
|
createPortal(u("div", { className: 'fixed inset-0 z-50', onMouseDown: () => {
|
|
738
823
|
app.updateCalendar(customColorPicker.calendarId, {});
|
|
739
824
|
setCustomColorPicker(null);
|
|
@@ -804,6 +889,7 @@ function createSidebarPlugin(config = {}) {
|
|
|
804
889
|
const [sidebarVersion, setSidebarVersion] = useState(0);
|
|
805
890
|
const [editingCalendarId, setEditingCalendarId] = useState(null);
|
|
806
891
|
const [showCreateDialog, setShowCreateDialog] = useState(false);
|
|
892
|
+
const isEditable = app.canMutateFromUI();
|
|
807
893
|
const refreshSidebar = useCallback(() => {
|
|
808
894
|
setSidebarVersion(prev => prev + 1);
|
|
809
895
|
}, []);
|
|
@@ -820,6 +906,8 @@ function createSidebarPlugin(config = {}) {
|
|
|
820
906
|
refreshSidebar();
|
|
821
907
|
}, [app, refreshSidebar]);
|
|
822
908
|
const handleCreateCalendar = useCallback(() => {
|
|
909
|
+
if (!isEditable)
|
|
910
|
+
return;
|
|
823
911
|
const createMode = config.createCalendarMode || 'inline';
|
|
824
912
|
if (createMode === 'modal') {
|
|
825
913
|
setShowCreateDialog(true);
|
|
@@ -839,7 +927,13 @@ function createSidebarPlugin(config = {}) {
|
|
|
839
927
|
app.createCalendar(newCalendar);
|
|
840
928
|
setEditingCalendarId(newId);
|
|
841
929
|
refreshSidebar();
|
|
842
|
-
}, [app, t, refreshSidebar]);
|
|
930
|
+
}, [app, isEditable, t, refreshSidebar]);
|
|
931
|
+
useEffect(() => {
|
|
932
|
+
if (isEditable)
|
|
933
|
+
return;
|
|
934
|
+
setShowCreateDialog(false);
|
|
935
|
+
setEditingCalendarId(null);
|
|
936
|
+
}, [isEditable]);
|
|
843
937
|
const sidebarProps = useMemo(() => ({
|
|
844
938
|
app,
|
|
845
939
|
calendars,
|
|
@@ -854,6 +948,8 @@ function createSidebarPlugin(config = {}) {
|
|
|
854
948
|
editingCalendarId,
|
|
855
949
|
setEditingCalendarId,
|
|
856
950
|
onCreateCalendar: handleCreateCalendar,
|
|
951
|
+
onSubscribeCalendar: config.onSubscribeCalendar,
|
|
952
|
+
onLoadSubscription: config.onLoadSubscription,
|
|
857
953
|
}), [
|
|
858
954
|
app,
|
|
859
955
|
calendars,
|
|
@@ -874,14 +970,14 @@ function createSidebarPlugin(config = {}) {
|
|
|
874
970
|
return h(DefaultCalendarSidebar, Object.assign({}, sidebarProps));
|
|
875
971
|
};
|
|
876
972
|
const renderExtraContent = () => {
|
|
877
|
-
if (!showCreateDialog)
|
|
973
|
+
if (!isEditable || !showCreateDialog)
|
|
878
974
|
return null;
|
|
879
975
|
const onClose = () => setShowCreateDialog(false);
|
|
880
|
-
const onCreate = (newCalendar) => {
|
|
881
|
-
app.createCalendar(newCalendar);
|
|
976
|
+
const onCreate = (newCalendar) => __awaiter(this, void 0, void 0, function* () {
|
|
977
|
+
yield app.createCalendar(newCalendar);
|
|
882
978
|
setShowCreateDialog(false);
|
|
883
979
|
refreshSidebar();
|
|
884
|
-
};
|
|
980
|
+
});
|
|
885
981
|
const generatorArgs = {
|
|
886
982
|
onClose,
|
|
887
983
|
onCreate,
|
package/dist/index.js
CHANGED
|
@@ -47,7 +47,7 @@ const getCalendarInitials = (calendar) => {
|
|
|
47
47
|
return name.charAt(0).toUpperCase();
|
|
48
48
|
};
|
|
49
49
|
const CalendarItem = ({ calendar, isDraggable, isEditable: _isEditable, editingId, editingName, setEditingName, editInputRef, draggedCalendarId, dropTarget, activeContextMenuCalendarId, onDragStart, onDragEnd, onDragOver, onDragLeave, onDrop, onContextMenu, onToggleVisibility, onRenameStart, onRenameSave, onRenameKeyDown, }) => {
|
|
50
|
-
var _a;
|
|
50
|
+
var _a, _b;
|
|
51
51
|
const isVisible = calendar.isVisible !== false;
|
|
52
52
|
const calendarColor = ((_a = calendar.colors) === null || _a === void 0 ? void 0 : _a.lineColor) || '#3b82f6';
|
|
53
53
|
const showIcon = Boolean(calendar.icon);
|
|
@@ -55,7 +55,9 @@ const CalendarItem = ({ calendar, isDraggable, isEditable: _isEditable, editingI
|
|
|
55
55
|
const isActive = activeContextMenuCalendarId === calendar.id || editingId === calendar.id;
|
|
56
56
|
return (u("li", { className: 'df-calendar-list-item relative', onDragOver: e => onDragOver(e, calendar.id), onDragLeave: onDragLeave, onDrop: () => onDrop(calendar), onContextMenu: e => onContextMenu(e, calendar.id), children: [isDropTarget && dropTarget.position === 'top' && (u("div", { className: 'pointer-events-none absolute top-0 right-0 left-0 z-10 h-0.5 bg-[var(--df-color-primary)]' })), u("div", { draggable: isDraggable && !editingId, onDragStart: e => onDragStart(calendar, e), onDragEnd: onDragEnd, className: `rounded transition ${draggedCalendarId === calendar.id ? 'opacity-50' : ''} ${isDraggable ? 'cursor-grab' : 'cursor-default'}`, children: u("div", { className: `group flex items-center rounded px-2 py-2 transition hover:bg-gray-100 dark:hover:bg-slate-800 ${isActive ? 'bg-gray-100 dark:bg-slate-800' : ''}`, title: calendar.name, children: [u("input", { type: 'checkbox', className: 'df-calendar-checkbox shrink-0 cursor-pointer', style: {
|
|
57
57
|
'--checkbox-color': calendarColor,
|
|
58
|
-
}, checked: isVisible, onChange: event => onToggleVisibility(calendar.id, event.target.checked) }), showIcon && (u("span", { className: 'ml-2 flex h-5 w-5 shrink-0 items-center justify-center text-xs font-semibold text-white', "aria-hidden": 'true', children: getCalendarInitials(calendar) })), editingId === calendar.id ? (u("input", { ref: editInputRef, type: 'text', value: editingName, onChange: e => setEditingName(e.target.value), onBlur: onRenameSave, onKeyDown: onRenameKeyDown, className: 'ml-2 h-5 min-w-0 flex-1 rounded bg-white px-0 py-0 text-sm text-gray-900 focus:outline-none dark:bg-slate-700 dark:text-gray-100', onClick: e => e.stopPropagation() })) : (u(preact.Fragment, { children: [u("span", { className: 'ml-2 flex-1 truncate pl-1 text-sm text-gray-700 group-hover:text-gray-900 dark:text-gray-200 dark:group-hover:text-white', onDblClick: () => onRenameStart(calendar), children: calendar.name || calendar.id }), calendar.
|
|
58
|
+
}, checked: isVisible, onChange: event => onToggleVisibility(calendar.id, event.target.checked) }), showIcon && (u("span", { className: 'ml-2 flex h-5 w-5 shrink-0 items-center justify-center text-xs font-semibold text-white', "aria-hidden": 'true', children: getCalendarInitials(calendar) })), editingId === calendar.id ? (u("input", { ref: editInputRef, type: 'text', value: editingName, onChange: e => setEditingName(e.target.value), onBlur: onRenameSave, onKeyDown: onRenameKeyDown, className: 'ml-2 h-5 min-w-0 flex-1 rounded bg-white px-0 py-0 text-sm text-gray-900 focus:outline-none dark:bg-slate-700 dark:text-gray-100', onClick: e => e.stopPropagation() })) : (u(preact.Fragment, { children: [u("span", { className: 'ml-2 flex-1 truncate pl-1 text-sm text-gray-700 group-hover:text-gray-900 dark:text-gray-200 dark:group-hover:text-white', onDblClick: () => onRenameStart(calendar), children: calendar.name || calendar.id }), ((_b = calendar.subscription) === null || _b === void 0 ? void 0 : _b.status) === 'error' && (u(core.AlertCircle, { width: 13, height: 13, className: 'ml-1 shrink-0 text-red-500', title: 'Failed to load subscription' })), calendar.subscribed &&
|
|
59
|
+
(!calendar.subscription ||
|
|
60
|
+
calendar.subscription.status === 'ready') && (u(core.AudioLines, { width: 13, height: 13, className: 'ml-1 shrink-0 text-gray-400 dark:text-gray-500' }))] }))] }) }), isDropTarget && dropTarget.position === 'bottom' && (u("div", { className: 'pointer-events-none absolute right-0 bottom-0 left-0 z-10 h-0.5 bg-[var(--df-color-primary)]' }))] }, calendar.id));
|
|
59
61
|
};
|
|
60
62
|
const CalendarList = ({ calendars, onToggleVisibility, onReorder, onRename, onContextMenu, editingId, setEditingId, activeContextMenuCalendarId, isDraggable = true, isEditable = true, }) => {
|
|
61
63
|
var _a;
|
|
@@ -247,16 +249,39 @@ function renderWithChip(template, name, color) {
|
|
|
247
249
|
const DeleteCalendarDialog = ({ calendarId, calendarName, calendars, step, onStepChange, onConfirmDelete, onCancel, onMergeSelect, }) => {
|
|
248
250
|
var _a, _b;
|
|
249
251
|
const [showMergeDropdown, setShowMergeDropdown] = hooks.useState(false);
|
|
252
|
+
const [isLoading, setIsLoading] = hooks.useState(false);
|
|
250
253
|
const { t } = core.useLocale();
|
|
251
254
|
const calendarColor = (_b = (_a = calendars.find(c => c.id === calendarId)) === null || _a === void 0 ? void 0 : _a.colors.lineColor) !== null && _b !== void 0 ? _b : '#6b7280';
|
|
252
|
-
|
|
255
|
+
const handleMergeSelect = (id) => __awaiter(void 0, void 0, void 0, function* () {
|
|
256
|
+
if (isLoading)
|
|
257
|
+
return;
|
|
258
|
+
setIsLoading(true);
|
|
259
|
+
try {
|
|
260
|
+
yield onMergeSelect(id);
|
|
261
|
+
setShowMergeDropdown(false);
|
|
262
|
+
}
|
|
263
|
+
finally {
|
|
264
|
+
setIsLoading(false);
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
const handleConfirmDelete = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
268
|
+
if (isLoading)
|
|
269
|
+
return;
|
|
270
|
+
setIsLoading(true);
|
|
271
|
+
try {
|
|
272
|
+
yield onConfirmDelete();
|
|
273
|
+
}
|
|
274
|
+
finally {
|
|
275
|
+
setIsLoading(false);
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
return core.createPortal(u("div", { className: 'df-portal fixed inset-0 z-[9999] flex items-center justify-center bg-black/50', children: u("div", { className: 'w-full max-w-md rounded-lg bg-white p-6 shadow-xl dark:bg-gray-800', children: step === 'initial' ? (u(preact.Fragment, { children: [u("h2", { className: 'text-lg font-semibold text-gray-900 dark:text-white', children: t('deleteCalendar', { calendarName }) }), u("p", { className: 'mt-3 flex flex-wrap items-center gap-y-0.5 text-sm text-gray-600 dark:text-gray-300', children: renderWithChip(t('deleteCalendarMessage', { calendarName: CAL_SENTINEL }), calendarName, calendarColor) }), u("div", { className: 'mt-6 flex items-center justify-between', children: [u("div", { className: 'relative', children: [u("button", { type: 'button', disabled: isLoading, onClick: () => setShowMergeDropdown(!showMergeDropdown), className: 'flex items-center gap-1 rounded-md border border-gray-300 px-4 py-2 text-xs font-medium text-gray-700 hover:bg-gray-50 disabled:opacity-50 dark:border-gray-600 dark:text-gray-200 dark:hover:bg-slate-700', children: t('merge') }), showMergeDropdown && (u("div", { className: 'absolute top-full left-0 z-10 mt-1 max-h-60 w-max min-w-full overflow-y-auto rounded-md border border-gray-200 bg-white shadow-lg dark:border-slate-700 dark:bg-gray-800', children: calendars
|
|
253
279
|
.filter(c => c.id !== calendarId)
|
|
254
280
|
.map(calendar => (u("div", { className: 'flex cursor-pointer items-center px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-slate-700', onClick: () => {
|
|
255
|
-
|
|
256
|
-
setShowMergeDropdown(false);
|
|
281
|
+
handleMergeSelect(calendar.id);
|
|
257
282
|
}, children: [u("div", { className: 'mr-2 h-3 w-3 shrink-0 rounded-sm', style: {
|
|
258
283
|
backgroundColor: calendar.colors.lineColor,
|
|
259
|
-
} }), u("span", { className: 'whitespace-nowrap', children: calendar.name || calendar.id })] }, calendar.id))) }))] }), u("div", { className: 'flex gap-3', children: [u("button", { type: 'button', onClick: onCancel, className: core.cancelButton
|
|
284
|
+
} }), u("span", { className: 'whitespace-nowrap', children: calendar.name || calendar.id })] }, calendar.id))) }))] }), u("div", { className: 'flex gap-3', children: [u("button", { type: 'button', onClick: onCancel, disabled: isLoading, className: `${core.cancelButton} disabled:opacity-50`, children: t('cancel') }), u("button", { type: 'button', onClick: () => onStepChange('confirm_delete'), disabled: isLoading, className: 'df-fill-destructive df-hover-destructive rounded-md px-4 py-2 text-xs font-medium disabled:opacity-50', children: t('delete') })] })] })] })) : (u(preact.Fragment, { children: [u("h2", { className: 'text-lg font-semibold text-gray-900 dark:text-white', children: t('confirmDeleteTitle', { calendarName }) }), u("p", { className: 'mt-3 text-sm text-gray-600 dark:text-gray-300', children: t('confirmDeleteMessage') }), u("div", { className: 'mt-6 flex justify-end gap-3', children: [u("button", { type: 'button', onClick: onCancel, disabled: isLoading, className: 'rounded-md border border-border bg-background px-3 py-2 text-xs font-medium text-gray-700 hover:bg-gray-100 disabled:opacity-50 dark:text-gray-300 dark:hover:bg-slate-700', children: t('cancel') }), u(core.LoadingButton, { type: 'button', onClick: handleConfirmDelete, loading: isLoading, className: 'df-fill-destructive df-hover-destructive rounded-md px-3 py-2 text-xs font-medium', children: t('delete') })] })] })) }) }), document.body);
|
|
260
285
|
};
|
|
261
286
|
|
|
262
287
|
const NEW_CALENDAR_ID = 'new-calendar';
|
|
@@ -266,6 +291,7 @@ const ImportCalendarDialog = ({ calendars, filename, onConfirm, onCancel, }) =>
|
|
|
266
291
|
const [selectedCalendarId, setSelectedCalendarId] = hooks.useState(((_a = calendars[0]) === null || _a === void 0 ? void 0 : _a.id) || NEW_CALENDAR_ID);
|
|
267
292
|
const [isOpen, setIsOpen] = hooks.useState(false);
|
|
268
293
|
const [shouldRender, setShouldRender] = hooks.useState(false);
|
|
294
|
+
const [isLoading, setIsLoading] = hooks.useState(false);
|
|
269
295
|
const dropdownRef = hooks.useRef(null);
|
|
270
296
|
const triggerRef = hooks.useRef(null);
|
|
271
297
|
hooks.useEffect(() => {
|
|
@@ -295,6 +321,17 @@ const ImportCalendarDialog = ({ calendars, filename, onConfirm, onCancel, }) =>
|
|
|
295
321
|
setSelectedCalendarId(id);
|
|
296
322
|
setIsOpen(false);
|
|
297
323
|
};
|
|
324
|
+
const handleConfirm = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
325
|
+
if (isLoading)
|
|
326
|
+
return;
|
|
327
|
+
setIsLoading(true);
|
|
328
|
+
try {
|
|
329
|
+
yield onConfirm(selectedCalendarId);
|
|
330
|
+
}
|
|
331
|
+
finally {
|
|
332
|
+
setIsLoading(false);
|
|
333
|
+
}
|
|
334
|
+
});
|
|
298
335
|
const renderDropdown = () => {
|
|
299
336
|
var _a;
|
|
300
337
|
if (!shouldRender)
|
|
@@ -302,17 +339,17 @@ const ImportCalendarDialog = ({ calendars, filename, onConfirm, onCancel, }) =>
|
|
|
302
339
|
const rect = (_a = triggerRef.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
|
|
303
340
|
if (!rect)
|
|
304
341
|
return null;
|
|
305
|
-
return core.createPortal(u("div", { ref: dropdownRef, className: `fixed z-110 mt-1 max-h-60 origin-top overflow-y-auto rounded-md border border-gray-200 bg-white shadow-lg transition-all duration-200 dark:border-slate-700 dark:bg-slate-800 ${isOpen ? 'scale-100 opacity-100' : 'scale-95 opacity-0'}`, style: {
|
|
342
|
+
return core.createPortal(u("div", { ref: dropdownRef, className: `df-portal fixed z-110 mt-1 max-h-60 origin-top overflow-y-auto rounded-md border border-gray-200 bg-white shadow-lg transition-all duration-200 dark:border-slate-700 dark:bg-slate-800 ${isOpen ? 'scale-100 opacity-100' : 'scale-95 opacity-0'}`, style: {
|
|
306
343
|
top: rect.bottom,
|
|
307
344
|
left: rect.left,
|
|
308
345
|
width: rect.width,
|
|
309
346
|
overscrollBehavior: 'none',
|
|
310
|
-
}, children: u("div", { className: 'py-1', children: [calendars.map(calendar => (u("div", { className: `flex cursor-pointer items-center px-3 py-2 hover:bg-gray-100 dark:hover:bg-slate-700 ${selectedCalendarId === calendar.id ? '
|
|
347
|
+
}, children: u("div", { className: 'py-1', children: [calendars.map(calendar => (u("div", { className: `flex cursor-pointer items-center px-3 py-2 hover:bg-gray-100 dark:hover:bg-slate-700 ${selectedCalendarId === calendar.id ? 'df-tint-primary' : ''}`, onClick: () => handleSelect(calendar.id), children: [u("div", { className: 'mr-3 h-3 w-3 shrink-0 rounded-sm', style: { backgroundColor: calendar.colors.lineColor } }), u("span", { className: `flex-1 truncate text-sm ${selectedCalendarId === calendar.id ? 'df-text-primary font-medium' : 'text-gray-700 dark:text-gray-200'}`, children: calendar.name || calendar.id }), selectedCalendarId === calendar.id && (u(core.Check, { className: 'df-text-primary ml-2 h-4 w-4 shrink-0' }))] }, calendar.id))), u("div", { className: 'my-1 border-t border-gray-100 dark:border-slate-700' }), u("div", { className: `flex cursor-pointer items-center px-3 py-2 hover:bg-gray-100 dark:hover:bg-slate-700 ${isNewSelected ? 'df-tint-primary' : ''}`, onClick: () => handleSelect(NEW_CALENDAR_ID), children: [u("span", { className: `flex-1 truncate text-sm ${isNewSelected ? 'df-text-primary font-medium' : 'pl-6 text-gray-700 dark:text-gray-200'}`, children: [t('newCalendar') || 'New Calendar', ": ", filename] }), isNewSelected && (u(core.Check, { className: 'df-text-primary ml-2 h-4 w-4 shrink-0' }))] })] }) }), document.body);
|
|
311
348
|
};
|
|
312
349
|
return (u("div", { className: 'df-portal fixed inset-0 z-100 flex items-center justify-center bg-black/50', children: u("div", { className: 'w-full max-w-md rounded-lg bg-white p-6 shadow-xl dark:bg-gray-900', children: [u("h2", { className: 'mb-4 text-lg font-semibold text-gray-900 dark:text-white', children: t('addSchedule') || 'Add Schedule' }), u("p", { className: 'mb-4 text-sm text-gray-600 dark:text-gray-300', children: t('importCalendarMessage') ||
|
|
313
|
-
'This calendar contains new events. Please select a target calendar.' }), u("div", { className: 'relative', children: [u("button", { ref: triggerRef, type: 'button', className: 'flex w-full items-center rounded-md border border-gray-300 px-3 py-2 shadow-sm transition-colors hover:bg-gray-50 dark:border-gray-600 dark:hover:bg-gray-800', onClick: () => setIsOpen(!isOpen), children: [!isNewSelected && selectedCalendar && (u("div", { className: 'mr-3 h-3 w-3 shrink-0 rounded-sm', style: { backgroundColor: selectedCalendar.colors.lineColor } })), u("span", { className: `flex-1 truncate text-left text-sm font-medium text-gray-700 dark:text-gray-200 ${isNewSelected ? 'pl-0' : ''}`, children: isNewSelected
|
|
350
|
+
'This calendar contains new events. Please select a target calendar.' }), u("div", { className: 'relative', children: [u("button", { ref: triggerRef, type: 'button', disabled: isLoading, className: 'flex w-full items-center rounded-md border border-gray-300 px-3 py-2 shadow-sm transition-colors hover:bg-gray-50 disabled:opacity-50 dark:border-gray-600 dark:hover:bg-gray-800', onClick: () => setIsOpen(!isOpen), children: [!isNewSelected && selectedCalendar && (u("div", { className: 'mr-3 h-3 w-3 shrink-0 rounded-sm', style: { backgroundColor: selectedCalendar.colors.lineColor } })), u("span", { className: `flex-1 truncate text-left text-sm font-medium text-gray-700 dark:text-gray-200 ${isNewSelected ? 'pl-0' : ''}`, children: isNewSelected
|
|
314
351
|
? `${t('newCalendar')}: ${filename}`
|
|
315
|
-
: (selectedCalendar === null || selectedCalendar === void 0 ? void 0 : selectedCalendar.name) || (selectedCalendar === null || selectedCalendar === void 0 ? void 0 : selectedCalendar.id) }), u(core.ChevronsUpDown, { className: 'ml-2 h-4 w-4 shrink-0 text-gray-400' })] }), renderDropdown()] }), u("div", { className: 'mt-8 flex justify-end gap-3', children: [u("button", { type: 'button', onClick: onCancel, className: core.cancelButton
|
|
352
|
+
: (selectedCalendar === null || selectedCalendar === void 0 ? void 0 : selectedCalendar.name) || (selectedCalendar === null || selectedCalendar === void 0 ? void 0 : selectedCalendar.id) }), u(core.ChevronsUpDown, { className: 'ml-2 h-4 w-4 shrink-0 text-gray-400' })] }), renderDropdown()] }), u("div", { className: 'mt-8 flex justify-end gap-3', children: [u("button", { type: 'button', onClick: onCancel, disabled: isLoading, className: `${core.cancelButton} disabled:opacity-50`, children: t('cancel') || 'Cancel' }), u(core.LoadingButton, { type: 'button', onClick: handleConfirm, loading: isLoading, className: 'df-fill-primary df-hover-primary-solid rounded-md px-6 py-2 text-sm font-medium shadow-sm transition-colors', children: t('ok') || 'OK' })] })] }) }));
|
|
316
353
|
};
|
|
317
354
|
|
|
318
355
|
const SOURCE_SENTINEL = '\u0001S\u0001';
|
|
@@ -330,6 +367,7 @@ function renderLine(line, source, target) {
|
|
|
330
367
|
}
|
|
331
368
|
const MergeCalendarDialog = ({ sourceName, sourceColor, targetName, targetColor, onConfirm, onCancel, }) => {
|
|
332
369
|
const { t } = core.useLocale();
|
|
370
|
+
const [isLoading, setIsLoading] = hooks.useState(false);
|
|
333
371
|
const source = { name: sourceName, color: sourceColor };
|
|
334
372
|
const target = { name: targetName, color: targetColor };
|
|
335
373
|
const messageTemplate = t('mergeConfirmMessage', {
|
|
@@ -337,7 +375,18 @@ const MergeCalendarDialog = ({ sourceName, sourceColor, targetName, targetColor,
|
|
|
337
375
|
targetName: TARGET_SENTINEL,
|
|
338
376
|
});
|
|
339
377
|
const messageLines = messageTemplate.split('\n');
|
|
340
|
-
|
|
378
|
+
const handleConfirm = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
379
|
+
if (isLoading)
|
|
380
|
+
return;
|
|
381
|
+
setIsLoading(true);
|
|
382
|
+
try {
|
|
383
|
+
yield onConfirm();
|
|
384
|
+
}
|
|
385
|
+
finally {
|
|
386
|
+
setIsLoading(false);
|
|
387
|
+
}
|
|
388
|
+
});
|
|
389
|
+
return (u("div", { className: 'df-portal fixed inset-0 z-[9999] flex items-center justify-center bg-black/50', children: u("div", { className: 'rounded-lg bg-white p-6 shadow-xl dark:bg-gray-800', children: [u("h2", { className: 'text-lg font-semibold text-gray-900 dark:text-white', children: t('mergeConfirmTitle', { sourceName, targetName }) }), u("div", { className: 'mt-3 space-y-1 text-sm text-gray-600 dark:text-gray-300', children: messageLines.map((line, i) => (u("p", { className: 'flex flex-wrap items-center gap-y-0.5', children: renderLine(line, source, target) }, i))) }), u("div", { className: 'mt-6 flex justify-end gap-3', children: [u("button", { type: 'button', onClick: onCancel, disabled: isLoading, className: `${core.cancelButton} disabled:opacity-50`, children: t('cancel') }), u(core.LoadingButton, { type: 'button', onClick: handleConfirm, loading: isLoading, className: 'df-fill-destructive df-hover-destructive rounded-md px-3 py-2 text-xs font-medium', children: t('merge') })] })] }) }));
|
|
341
390
|
};
|
|
342
391
|
|
|
343
392
|
const stopPropagation = (e) => e.stopPropagation();
|
|
@@ -400,8 +449,13 @@ const SubscribeCalendarDialog = ({ onSubscribe, onCancel, }) => {
|
|
|
400
449
|
try {
|
|
401
450
|
yield onSubscribe(trimmed);
|
|
402
451
|
}
|
|
403
|
-
catch (
|
|
404
|
-
|
|
452
|
+
catch (err) {
|
|
453
|
+
if (err.message === 'DUPLICATE_URL') {
|
|
454
|
+
setError(t('calendarAlreadySubscribed') || 'This URL is already subscribed');
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
setError(t('subscribeError') || 'Failed to subscribe to calendar');
|
|
458
|
+
}
|
|
405
459
|
}
|
|
406
460
|
finally {
|
|
407
461
|
setLoading(false);
|
|
@@ -413,10 +467,10 @@ const SubscribeCalendarDialog = ({ onSubscribe, onCancel, }) => {
|
|
|
413
467
|
if (e.key === 'Escape')
|
|
414
468
|
onCancel();
|
|
415
469
|
};
|
|
416
|
-
return (u("div", { className: 'df-portal fixed inset-0 z-[9999] flex items-center justify-center bg-black/50', children: u("div", { className: 'w-full max-w-xl rounded-lg bg-white p-6 shadow-xl dark:bg-gray-800', children: [u("h2", { className: 'text-lg font-semibold text-gray-900 dark:text-white', children: t('subscribeCalendarTitle') }), u("div", { className: 'mt-4', children: [u("div", { className: 'flex items-center gap-3', children: [u("label", { className: 'shrink-0 text-sm font-medium text-gray-700 dark:text-gray-300', children: t('calendarUrl') }), u("input", { type: 'url', value: url, onInput: e => setUrl(e.target.value), onKeyDown: handleKeyDown, placeholder: t('calendarUrlPlaceholder'), disabled: loading, autoFocus: true, className: 'flex-1 rounded-md border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 placeholder-gray-400 focus:
|
|
470
|
+
return (u("div", { className: 'df-portal fixed inset-0 z-[9999] flex items-center justify-center bg-black/50', children: u("div", { className: 'w-full max-w-xl rounded-lg bg-white p-6 shadow-xl dark:bg-gray-800', children: [u("h2", { className: 'text-lg font-semibold text-gray-900 dark:text-white', children: t('subscribeCalendarTitle') }), u("div", { className: 'mt-4', children: [u("div", { className: 'flex items-center gap-3', children: [u("label", { className: 'shrink-0 text-sm font-medium text-gray-700 dark:text-gray-300', children: t('calendarUrl') }), u("input", { type: 'url', value: url, onInput: e => setUrl(e.target.value), onKeyDown: handleKeyDown, placeholder: t('calendarUrlPlaceholder'), disabled: loading, autoFocus: true, className: 'df-focus-ring flex-1 rounded-md border border-gray-300 bg-white px-3 py-2 text-sm text-gray-900 placeholder-gray-400 focus:ring-1 focus:outline-none disabled:opacity-50 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400' })] }), error && (u("p", { className: 'mt-2 text-xs text-red-500 dark:text-red-400', children: error }))] }), u("div", { className: 'mt-6 flex justify-end gap-3', children: [u("button", { type: 'button', onClick: onCancel, disabled: loading, className: core.cancelButton, children: t('cancel') }), u("button", { type: 'button', onClick: handleSubmit, disabled: loading || !url.trim(), className: 'df-fill-primary df-hover-primary-solid rounded-md px-4 py-2 text-xs font-medium disabled:cursor-not-allowed disabled:opacity-50', children: loading ? t('fetchingCalendar') : t('subscribe') })] })] }) }));
|
|
417
471
|
};
|
|
418
472
|
|
|
419
|
-
const DefaultCalendarSidebar = ({ app, calendars, toggleCalendarVisibility, isCollapsed, setCollapsed, renderCalendarContextMenu, renderSidebarHeader, editingCalendarId: propEditingCalendarId, setEditingCalendarId: propSetEditingCalendarId, onCreateCalendar, }) => {
|
|
473
|
+
const DefaultCalendarSidebar = ({ app, calendars, toggleCalendarVisibility, isCollapsed, setCollapsed, renderCalendarContextMenu, renderSidebarHeader, editingCalendarId: propEditingCalendarId, setEditingCalendarId: propSetEditingCalendarId, onCreateCalendar, onSubscribeCalendar, onLoadSubscription, }) => {
|
|
420
474
|
var _a, _b;
|
|
421
475
|
const { t } = core.useLocale();
|
|
422
476
|
// Detect if custom color picker slot is provided
|
|
@@ -429,6 +483,33 @@ const DefaultCalendarSidebar = ({ app, calendars, toggleCalendarVisibility, isCo
|
|
|
429
483
|
// File input ref for import
|
|
430
484
|
const fileInputRef = hooks.useRef(null);
|
|
431
485
|
const contextMenuRef = hooks.useRef(null);
|
|
486
|
+
// Track loaded subscription URLs to avoid redundant fetching
|
|
487
|
+
const loadedSubscriptionsRef = hooks.useRef(new Set());
|
|
488
|
+
// Auto-load subscriptions on mount or when calendars change
|
|
489
|
+
hooks.useEffect(() => {
|
|
490
|
+
calendars.forEach((calendar) => __awaiter(void 0, void 0, void 0, function* () {
|
|
491
|
+
var _a;
|
|
492
|
+
if (((_a = calendar.subscription) === null || _a === void 0 ? void 0 : _a.url) &&
|
|
493
|
+
!loadedSubscriptionsRef.current.has(calendar.subscription.url)) {
|
|
494
|
+
loadedSubscriptionsRef.current.add(calendar.subscription.url);
|
|
495
|
+
try {
|
|
496
|
+
if (onLoadSubscription) {
|
|
497
|
+
yield onLoadSubscription(calendar);
|
|
498
|
+
}
|
|
499
|
+
else {
|
|
500
|
+
const { events } = yield core.subscribeCalendar(calendar.subscription.url);
|
|
501
|
+
app.addExternalEvents(calendar.id, events);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
catch (err) {
|
|
505
|
+
console.error(`Failed to auto-load calendar ${calendar.name}:`, err);
|
|
506
|
+
app.updateCalendar(calendar.id, {
|
|
507
|
+
subscription: Object.assign(Object.assign({}, calendar.subscription), { status: 'error' }),
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}));
|
|
512
|
+
}, [app, calendars, onLoadSubscription]);
|
|
432
513
|
const handleMonthChange = hooks.useCallback((offset) => {
|
|
433
514
|
const current = app.getVisibleMonth();
|
|
434
515
|
const next = new Date(current.getFullYear(), current.getMonth() + offset, 1);
|
|
@@ -533,19 +614,19 @@ const DefaultCalendarSidebar = ({ app, calendars, toggleCalendarVisibility, isCo
|
|
|
533
614
|
handleCloseContextMenu();
|
|
534
615
|
}
|
|
535
616
|
}, [contextMenu, handleCloseContextMenu]);
|
|
536
|
-
const handleMergeConfirm = hooks.useCallback(() => {
|
|
617
|
+
const handleMergeConfirm = hooks.useCallback(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
537
618
|
if (mergeState) {
|
|
538
619
|
const { sourceId, targetId } = mergeState;
|
|
539
|
-
app.mergeCalendars(sourceId, targetId);
|
|
620
|
+
yield app.mergeCalendars(sourceId, targetId);
|
|
540
621
|
setMergeState(null);
|
|
541
622
|
}
|
|
542
|
-
}, [app, mergeState]);
|
|
543
|
-
const handleConfirmDelete = hooks.useCallback(() => {
|
|
623
|
+
}), [app, mergeState]);
|
|
624
|
+
const handleConfirmDelete = hooks.useCallback(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
544
625
|
if (deleteState) {
|
|
545
|
-
app.deleteCalendar(deleteState.calendarId);
|
|
626
|
+
yield app.deleteCalendar(deleteState.calendarId);
|
|
546
627
|
setDeleteState(null);
|
|
547
628
|
}
|
|
548
|
-
}, [app, deleteState]);
|
|
629
|
+
}), [app, deleteState]);
|
|
549
630
|
const handleDeleteMergeSelect = hooks.useCallback((targetId) => {
|
|
550
631
|
if (deleteState) {
|
|
551
632
|
setMergeState({
|
|
@@ -567,45 +648,31 @@ const DefaultCalendarSidebar = ({ app, calendars, toggleCalendarVisibility, isCo
|
|
|
567
648
|
handleCloseSidebarContextMenu();
|
|
568
649
|
}, [handleCloseSidebarContextMenu]);
|
|
569
650
|
const handleSubscribeConfirm = hooks.useCallback((url) => __awaiter(void 0, void 0, void 0, function* () {
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
const
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
const { colors: calendarColors, darkColors } = core.getCalendarColorsForHex(randomColor);
|
|
594
|
-
const calendarId = core.generateUniKey();
|
|
595
|
-
app.createCalendar({
|
|
596
|
-
id: calendarId,
|
|
597
|
-
name: calendarName,
|
|
598
|
-
isDefault: false,
|
|
599
|
-
colors: calendarColors,
|
|
600
|
-
darkColors,
|
|
601
|
-
isVisible: true,
|
|
602
|
-
subscribed: true,
|
|
603
|
-
});
|
|
604
|
-
result.events.forEach(event => {
|
|
605
|
-
app.addEvent(Object.assign(Object.assign({}, event), { calendarId }));
|
|
606
|
-
});
|
|
651
|
+
var _a;
|
|
652
|
+
// 1. Check for duplicates
|
|
653
|
+
const isDuplicate = calendars.some(c => { var _a; return ((_a = c.subscription) === null || _a === void 0 ? void 0 : _a.url) === url; });
|
|
654
|
+
if (isDuplicate) {
|
|
655
|
+
throw new Error('DUPLICATE_URL');
|
|
656
|
+
}
|
|
657
|
+
// 2. Load the subscription (fetch + parse) using the new utility
|
|
658
|
+
const { calendar, events } = yield core.subscribeCalendar(url);
|
|
659
|
+
// 3. Mark as loaded to avoid the useEffect triggering another fetch
|
|
660
|
+
if ((_a = calendar.subscription) === null || _a === void 0 ? void 0 : _a.url) {
|
|
661
|
+
loadedSubscriptionsRef.current.add(calendar.subscription.url);
|
|
662
|
+
}
|
|
663
|
+
// 4. Delegate to user if callback exists, otherwise use default behavior
|
|
664
|
+
if (onSubscribeCalendar) {
|
|
665
|
+
yield onSubscribeCalendar(calendar, events);
|
|
666
|
+
}
|
|
667
|
+
else {
|
|
668
|
+
// Default behavior: create calendar in the app
|
|
669
|
+
app.createCalendar(calendar);
|
|
670
|
+
}
|
|
671
|
+
// 4. Always add events to the internal external store for IMMEDIATE display
|
|
672
|
+
app.addExternalEvents(calendar.id, events);
|
|
673
|
+
// 5. Close dialog
|
|
607
674
|
setSubscribeDialogOpen(false);
|
|
608
|
-
}), [app]);
|
|
675
|
+
}), [app, onSubscribeCalendar, calendars]);
|
|
609
676
|
const handleFileChange = hooks.useCallback((e) => __awaiter(void 0, void 0, void 0, function* () {
|
|
610
677
|
var _a;
|
|
611
678
|
const file = (_a = e.currentTarget.files) === null || _a === void 0 ? void 0 : _a[0];
|
|
@@ -689,8 +756,20 @@ const DefaultCalendarSidebar = ({ app, calendars, toggleCalendarVisibility, isCo
|
|
|
689
756
|
? ((_a = calendars.find(c => c.id === deleteState.calendarId)) === null || _a === void 0 ? void 0 : _a.name) || 'Unknown'
|
|
690
757
|
: '';
|
|
691
758
|
const readOnlyConfig = app.getReadOnlyConfig();
|
|
692
|
-
const isEditable =
|
|
759
|
+
const isEditable = app.canMutateFromUI();
|
|
693
760
|
const isDraggable = readOnlyConfig.draggable !== false;
|
|
761
|
+
hooks.useEffect(() => {
|
|
762
|
+
if (isEditable)
|
|
763
|
+
return;
|
|
764
|
+
setContextMenu(null);
|
|
765
|
+
setSidebarContextMenu(null);
|
|
766
|
+
setCustomColorPicker(null);
|
|
767
|
+
setMergeState(null);
|
|
768
|
+
setDeleteState(null);
|
|
769
|
+
setImportState(null);
|
|
770
|
+
setSubscribeDialogOpen(false);
|
|
771
|
+
setEditingCalendarId(null);
|
|
772
|
+
}, [isEditable, setEditingCalendarId]);
|
|
694
773
|
return (u("div", { className: core.sidebarContainer, onContextMenu: isEditable ? handleSidebarContextMenu : undefined, children: [u(core.ContentSlot, { generatorName: 'sidebarHeader', generatorArgs: {
|
|
695
774
|
isCollapsed,
|
|
696
775
|
onCollapseToggle: () => setCollapsed(!isCollapsed),
|
|
@@ -721,21 +800,27 @@ const DefaultCalendarSidebar = ({ app, calendars, toggleCalendarVisibility, isCo
|
|
|
721
800
|
? handleContextMenu
|
|
722
801
|
: () => {
|
|
723
802
|
/* noop */
|
|
724
|
-
}, editingId: editingCalendarId, setEditingId: setEditingCalendarId, activeContextMenuCalendarId: contextMenu === null || contextMenu === void 0 ? void 0 : contextMenu.calendarId, isDraggable: isDraggable, isEditable: isEditable }), u("div", { className: 'border-t border-gray-200 dark:border-slate-800', children: u(core.MiniCalendar, { visibleMonth: app.getVisibleMonth(), currentDate: app.getCurrentDate(), showHeader: true, onMonthChange: handleMonthChange, onDateSelect: date => app.setCurrentDate(date) }) })] })), contextMenu && (u(core.ContextMenu, { ref: contextMenuRef, x: contextMenu.x, y: contextMenu.y, onClose: handleCloseContextMenu, className: 'w-64 p-2', children: u(core.ContentSlot, { generatorName: 'calendarContextMenu', generatorArgs: {
|
|
803
|
+
}, editingId: editingCalendarId, setEditingId: setEditingCalendarId, activeContextMenuCalendarId: contextMenu === null || contextMenu === void 0 ? void 0 : contextMenu.calendarId, isDraggable: isDraggable, isEditable: isEditable }), u("div", { className: 'border-t border-gray-200 dark:border-slate-800', children: u(core.MiniCalendar, { visibleMonth: app.getVisibleMonth(), currentDate: app.getCurrentDate(), showHeader: true, onMonthChange: handleMonthChange, onDateSelect: date => app.setCurrentDate(date) }) })] })), isEditable && contextMenu && (u(core.ContextMenu, { ref: contextMenuRef, x: contextMenu.x, y: contextMenu.y, onClose: handleCloseContextMenu, className: 'w-64 p-2', children: u(core.ContentSlot, { generatorName: 'calendarContextMenu', generatorArgs: {
|
|
725
804
|
calendar: calendars.find(c => c.id === contextMenu.calendarId),
|
|
726
805
|
onClose: handleCloseContextMenu,
|
|
727
|
-
}, defaultContent: renderCalendarContextMenu ? (renderCalendarContextMenu(calendars.find(c => c.id === contextMenu.calendarId), handleCloseContextMenu)) : (u(preact.Fragment, { children: [u(core.ContextMenuLabel, { children: t('calendarOptions') }), u(MergeMenuItem, { calendars: calendars, currentCalendarId: contextMenu.calendarId, onMergeSelect: handleMergeSelect }), u(core.ContextMenuItem, { onClick: handleDeleteCalendar, children: t('delete') }), u(core.ContextMenuItem, { onClick: handleExportCalendar, children: t('exportCalendar') || 'Export Calendar' }), u(core.ContextMenuSeparator, {}), u(core.ContextMenuColorPicker, { selectedColor: (_b = calendars.find(c => c.id === contextMenu.calendarId)) === null || _b === void 0 ? void 0 : _b.colors.lineColor, onSelect: handleColorSelect, onCustomColor: handleCustomColor })] })) }) })),
|
|
806
|
+
}, defaultContent: renderCalendarContextMenu ? (renderCalendarContextMenu(calendars.find(c => c.id === contextMenu.calendarId), handleCloseContextMenu)) : (u(preact.Fragment, { children: [u(core.ContextMenuLabel, { children: t('calendarOptions') }), u(MergeMenuItem, { calendars: calendars, currentCalendarId: contextMenu.calendarId, onMergeSelect: handleMergeSelect }), u(core.ContextMenuItem, { onClick: handleDeleteCalendar, children: t('delete') }), u(core.ContextMenuItem, { onClick: handleExportCalendar, children: t('exportCalendar') || 'Export Calendar' }), u(core.ContextMenuSeparator, {}), u(core.ContextMenuColorPicker, { selectedColor: (_b = calendars.find(c => c.id === contextMenu.calendarId)) === null || _b === void 0 ? void 0 : _b.colors.lineColor, onSelect: handleColorSelect, onCustomColor: handleCustomColor })] })) }) })), isEditable &&
|
|
807
|
+
sidebarContextMenu &&
|
|
728
808
|
core.createPortal(u(core.ContextMenu, { x: sidebarContextMenu.x, y: sidebarContextMenu.y, onClose: handleCloseSidebarContextMenu, className: 'w-max p-2', children: [u(core.ContextMenuItem, { onClick: () => {
|
|
729
809
|
onCreateCalendar === null || onCreateCalendar === void 0 ? void 0 : onCreateCalendar();
|
|
730
810
|
handleCloseSidebarContextMenu();
|
|
731
811
|
}, children: t('newCalendar') || 'New Calendar' }), u(core.ContextMenuItem, { onClick: handleImportClick, children: t('importCalendar') || 'Import Calendar' }), u(core.ContextMenuItem, { onClick: handleSubscribeClick, children: t('subscribeCalendar') || 'Subscribe to Calendar' }), u(core.ContextMenuItem, { onClick: () => {
|
|
732
812
|
app.triggerRender();
|
|
733
813
|
handleCloseSidebarContextMenu();
|
|
734
|
-
}, children: t('refreshAll') || 'Refresh All' })] }), document.body), u("input", { ref: fileInputRef, type: 'file', accept: '.ics', style: { display: 'none' }, onChange: handleFileChange }),
|
|
735
|
-
|
|
736
|
-
core.createPortal(u(
|
|
737
|
-
|
|
738
|
-
core.createPortal(u(
|
|
814
|
+
}, children: t('refreshAll') || 'Refresh All' })] }), document.body), u("input", { ref: fileInputRef, type: 'file', accept: '.ics', style: { display: 'none' }, onChange: handleFileChange }), isEditable &&
|
|
815
|
+
importState &&
|
|
816
|
+
core.createPortal(u(ImportCalendarDialog, { calendars: calendars, filename: importState.filename, onConfirm: handleImportConfirm, onCancel: () => setImportState(null) }), document.body), isEditable &&
|
|
817
|
+
subscribeDialogOpen &&
|
|
818
|
+
core.createPortal(u(SubscribeCalendarDialog, { onSubscribe: handleSubscribeConfirm, onCancel: () => setSubscribeDialogOpen(false) }), document.body), isEditable &&
|
|
819
|
+
mergeState &&
|
|
820
|
+
core.createPortal(u(MergeCalendarDialog, { sourceName: sourceCalendarName, sourceColor: sourceCalendarColor, targetName: targetCalendarName, targetColor: targetCalendarColor, onConfirm: handleMergeConfirm, onCancel: () => setMergeState(null) }), document.body), isEditable &&
|
|
821
|
+
deleteState &&
|
|
822
|
+
core.createPortal(u(DeleteCalendarDialog, { calendarId: deleteState.calendarId, calendarName: deleteCalendarName, calendars: calendars, step: deleteState.step, onStepChange: step => setDeleteState(prev => (prev ? Object.assign(Object.assign({}, prev), { step }) : null)), onConfirmDelete: handleConfirmDelete, onCancel: () => setDeleteState(null), onMergeSelect: handleDeleteMergeSelect }), document.body), isEditable &&
|
|
823
|
+
customColorPicker &&
|
|
739
824
|
core.createPortal(u("div", { className: 'fixed inset-0 z-50', onMouseDown: () => {
|
|
740
825
|
app.updateCalendar(customColorPicker.calendarId, {});
|
|
741
826
|
setCustomColorPicker(null);
|
|
@@ -806,6 +891,7 @@ function createSidebarPlugin(config = {}) {
|
|
|
806
891
|
const [sidebarVersion, setSidebarVersion] = hooks.useState(0);
|
|
807
892
|
const [editingCalendarId, setEditingCalendarId] = hooks.useState(null);
|
|
808
893
|
const [showCreateDialog, setShowCreateDialog] = hooks.useState(false);
|
|
894
|
+
const isEditable = app.canMutateFromUI();
|
|
809
895
|
const refreshSidebar = hooks.useCallback(() => {
|
|
810
896
|
setSidebarVersion(prev => prev + 1);
|
|
811
897
|
}, []);
|
|
@@ -822,6 +908,8 @@ function createSidebarPlugin(config = {}) {
|
|
|
822
908
|
refreshSidebar();
|
|
823
909
|
}, [app, refreshSidebar]);
|
|
824
910
|
const handleCreateCalendar = hooks.useCallback(() => {
|
|
911
|
+
if (!isEditable)
|
|
912
|
+
return;
|
|
825
913
|
const createMode = config.createCalendarMode || 'inline';
|
|
826
914
|
if (createMode === 'modal') {
|
|
827
915
|
setShowCreateDialog(true);
|
|
@@ -841,7 +929,13 @@ function createSidebarPlugin(config = {}) {
|
|
|
841
929
|
app.createCalendar(newCalendar);
|
|
842
930
|
setEditingCalendarId(newId);
|
|
843
931
|
refreshSidebar();
|
|
844
|
-
}, [app, t, refreshSidebar]);
|
|
932
|
+
}, [app, isEditable, t, refreshSidebar]);
|
|
933
|
+
hooks.useEffect(() => {
|
|
934
|
+
if (isEditable)
|
|
935
|
+
return;
|
|
936
|
+
setShowCreateDialog(false);
|
|
937
|
+
setEditingCalendarId(null);
|
|
938
|
+
}, [isEditable]);
|
|
845
939
|
const sidebarProps = hooks.useMemo(() => ({
|
|
846
940
|
app,
|
|
847
941
|
calendars,
|
|
@@ -856,6 +950,8 @@ function createSidebarPlugin(config = {}) {
|
|
|
856
950
|
editingCalendarId,
|
|
857
951
|
setEditingCalendarId,
|
|
858
952
|
onCreateCalendar: handleCreateCalendar,
|
|
953
|
+
onSubscribeCalendar: config.onSubscribeCalendar,
|
|
954
|
+
onLoadSubscription: config.onLoadSubscription,
|
|
859
955
|
}), [
|
|
860
956
|
app,
|
|
861
957
|
calendars,
|
|
@@ -876,14 +972,14 @@ function createSidebarPlugin(config = {}) {
|
|
|
876
972
|
return preact.h(DefaultCalendarSidebar, Object.assign({}, sidebarProps));
|
|
877
973
|
};
|
|
878
974
|
const renderExtraContent = () => {
|
|
879
|
-
if (!showCreateDialog)
|
|
975
|
+
if (!isEditable || !showCreateDialog)
|
|
880
976
|
return null;
|
|
881
977
|
const onClose = () => setShowCreateDialog(false);
|
|
882
|
-
const onCreate = (newCalendar) => {
|
|
883
|
-
app.createCalendar(newCalendar);
|
|
978
|
+
const onCreate = (newCalendar) => __awaiter(this, void 0, void 0, function* () {
|
|
979
|
+
yield app.createCalendar(newCalendar);
|
|
884
980
|
setShowCreateDialog(false);
|
|
885
981
|
refreshSidebar();
|
|
886
|
-
};
|
|
982
|
+
});
|
|
887
983
|
const generatorArgs = {
|
|
888
984
|
onClose,
|
|
889
985
|
onCreate,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { JSX } from 'preact';
|
|
2
2
|
import type { CalendarSidebarRenderProps } from './plugin';
|
|
3
|
-
declare const DefaultCalendarSidebar: ({ app, calendars, toggleCalendarVisibility, isCollapsed, setCollapsed, renderCalendarContextMenu, renderSidebarHeader, editingCalendarId: propEditingCalendarId, setEditingCalendarId: propSetEditingCalendarId, onCreateCalendar, }: CalendarSidebarRenderProps) => JSX.Element;
|
|
3
|
+
declare const DefaultCalendarSidebar: ({ app, calendars, toggleCalendarVisibility, isCollapsed, setCollapsed, renderCalendarContextMenu, renderSidebarHeader, editingCalendarId: propEditingCalendarId, setEditingCalendarId: propSetEditingCalendarId, onCreateCalendar, onSubscribeCalendar, onLoadSubscription, }: CalendarSidebarRenderProps) => JSX.Element;
|
|
4
4
|
export default DefaultCalendarSidebar;
|
|
@@ -5,9 +5,9 @@ interface DeleteCalendarDialogProps {
|
|
|
5
5
|
calendars: CalendarType[];
|
|
6
6
|
step: 'initial' | 'confirm_delete';
|
|
7
7
|
onStepChange: (step: 'initial' | 'confirm_delete') => void;
|
|
8
|
-
onConfirmDelete: () => void
|
|
8
|
+
onConfirmDelete: () => void | Promise<void>;
|
|
9
9
|
onCancel: () => void;
|
|
10
|
-
onMergeSelect: (targetId: string) => void
|
|
10
|
+
onMergeSelect: (targetId: string) => void | Promise<void>;
|
|
11
11
|
}
|
|
12
12
|
export declare const DeleteCalendarDialog: ({ calendarId, calendarName, calendars, step, onStepChange, onConfirmDelete, onCancel, onMergeSelect, }: DeleteCalendarDialogProps) => import("preact").VNode<any>;
|
|
13
13
|
export {};
|
|
@@ -2,7 +2,7 @@ import { CalendarType } from '@dayflow/core';
|
|
|
2
2
|
interface ImportCalendarDialogProps {
|
|
3
3
|
calendars: CalendarType[];
|
|
4
4
|
filename: string;
|
|
5
|
-
onConfirm: (targetCalendarId: string) => void
|
|
5
|
+
onConfirm: (targetCalendarId: string) => void | Promise<void>;
|
|
6
6
|
onCancel: () => void;
|
|
7
7
|
}
|
|
8
8
|
export declare const NEW_CALENDAR_ID = "new-calendar";
|
|
@@ -3,7 +3,7 @@ interface MergeCalendarDialogProps {
|
|
|
3
3
|
sourceColor: string;
|
|
4
4
|
targetName: string;
|
|
5
5
|
targetColor: string;
|
|
6
|
-
onConfirm: () => void
|
|
6
|
+
onConfirm: () => void | Promise<void>;
|
|
7
7
|
onCancel: () => void;
|
|
8
8
|
}
|
|
9
9
|
export declare const MergeCalendarDialog: ({ sourceName, sourceColor, targetName, targetColor, onConfirm, onCancel, }: MergeCalendarDialogProps) => import("preact").JSX.Element;
|
package/dist/types/plugin.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CalendarPlugin, ICalendarApp, CalendarType, TNode, CreateCalendarDialogProps, SidebarHeaderSlotArgs } from '@dayflow/core';
|
|
1
|
+
import { CalendarPlugin, ICalendarApp, CalendarType, Event, TNode, CreateCalendarDialogProps, SidebarHeaderSlotArgs } from '@dayflow/core';
|
|
2
2
|
export type { SidebarHeaderSlotArgs };
|
|
3
3
|
export interface CalendarSidebarRenderProps {
|
|
4
4
|
app: ICalendarApp;
|
|
@@ -14,6 +14,8 @@ export interface CalendarSidebarRenderProps {
|
|
|
14
14
|
editingCalendarId?: string | null;
|
|
15
15
|
setEditingCalendarId?: (id: string | null) => void;
|
|
16
16
|
onCreateCalendar?: () => void;
|
|
17
|
+
onSubscribeCalendar?: (calendar: CalendarType, events: Event[]) => Promise<void>;
|
|
18
|
+
onLoadSubscription?: (calendar: CalendarType) => Promise<void>;
|
|
17
19
|
}
|
|
18
20
|
export interface SidebarPluginConfig {
|
|
19
21
|
width?: number | string;
|
|
@@ -24,6 +26,8 @@ export interface SidebarPluginConfig {
|
|
|
24
26
|
renderCalendarContextMenu?: (calendar: CalendarType, onClose: () => void) => TNode;
|
|
25
27
|
renderSidebarHeader?: (args: SidebarHeaderSlotArgs) => TNode;
|
|
26
28
|
renderCreateCalendarDialog?: (props: CreateCalendarDialogProps) => TNode;
|
|
29
|
+
onSubscribeCalendar?: (calendar: CalendarType, events: Event[]) => Promise<void>;
|
|
30
|
+
onLoadSubscription?: (calendar: CalendarType) => Promise<void>;
|
|
27
31
|
[key: string]: unknown;
|
|
28
32
|
}
|
|
29
33
|
export declare function createSidebarPlugin(config?: SidebarPluginConfig): CalendarPlugin;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dayflow/plugin-sidebar",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.6",
|
|
4
4
|
"description": "Sidebar plugin for DayFlow calendar",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"calendar",
|
|
@@ -37,10 +37,10 @@
|
|
|
37
37
|
"rollup-plugin-dts": "^6.3.0",
|
|
38
38
|
"temporal-polyfill": "^0.3.0",
|
|
39
39
|
"typescript": "^5.9.3",
|
|
40
|
-
"@dayflow/core": "3.3.
|
|
40
|
+
"@dayflow/core": "3.3.6"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
|
-
"@dayflow/core": "3.3.
|
|
43
|
+
"@dayflow/core": "3.3.6"
|
|
44
44
|
},
|
|
45
45
|
"scripts": {
|
|
46
46
|
"build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json && rollup -c",
|