137 lines
5.5 KiB
HTML
137 lines
5.5 KiB
HTML
<div class="bookmarks-panel flex flex-col h-full bg-white dark:bg-gray-900">
|
|
<!-- Header -->
|
|
<div class="bookmarks-header flex-shrink-0 p-4 border-b border-gray-200 dark:border-gray-700">
|
|
<div class="flex items-center justify-between mb-3">
|
|
<h2 class="text-lg font-semibold text-gray-900 dark:text-gray-100">Bookmarks</h2>
|
|
<div class="flex items-center gap-2">
|
|
<button
|
|
type="button"
|
|
class="btn btn-sm btn-primary"
|
|
(click)="createGroup()"
|
|
title="Créer un groupe">
|
|
+ Group
|
|
</button>
|
|
@if (saving()) {
|
|
<span class="text-xs text-blue-600 dark:text-blue-400 animate-pulse">Saving...</span>
|
|
}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Search -->
|
|
<div class="relative">
|
|
<input
|
|
type="text"
|
|
[value]="searchTerm()"
|
|
(input)="onSearchChange($any($event.target).value)"
|
|
placeholder="Search bookmarks..."
|
|
class="w-full px-3 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 placeholder-gray-500 dark:placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
/>
|
|
@if (searchTerm()) {
|
|
<button
|
|
(click)="onSearchChange('')"
|
|
class="absolute right-2 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
|
|
title="Clear search">
|
|
✕
|
|
</button>
|
|
}
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<!-- Body -->
|
|
<div class="bookmarks-body flex-1 overflow-y-auto p-4">
|
|
@if (loading()) {
|
|
<div class="flex items-center justify-center py-8 text-gray-500 dark:text-gray-400">
|
|
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
|
|
</div>
|
|
} @else if (error()) {
|
|
<div class="p-4 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-md">
|
|
<div class="flex items-start gap-2">
|
|
<span class="text-red-600 dark:text-red-400 font-semibold">Error:</span>
|
|
<span class="text-sm text-red-700 dark:text-red-300 flex-1">{{ error() }}</span>
|
|
<button
|
|
(click)="clearError()"
|
|
class="text-red-600 dark:text-red-400 hover:text-red-800 dark:hover:text-red-200"
|
|
title="Dismiss">
|
|
✕
|
|
</button>
|
|
</div>
|
|
</div>
|
|
} @else if (isEmpty()) {
|
|
<div class="text-center py-8 text-gray-500 dark:text-gray-400">
|
|
<p class="mb-4">No bookmarks yet</p>
|
|
<p class="text-sm">Use the bookmark icon in the note toolbar to add one.</p>
|
|
</div>
|
|
|
|
<div
|
|
class="bookmarks-tree mt-4 border-2 border-dashed border-blue-500/60 dark:border-blue-400/60 bg-blue-500/10 dark:bg-blue-400/10 text-blue-600 dark:text-blue-300 rounded-md min-h-[80px] flex items-center justify-center"
|
|
cdkDropList
|
|
[cdkDropListData]="displayItems()"
|
|
cdkDropListId="root"
|
|
[cdkDropListConnectedTo]="getDropListConnections('root')"
|
|
cdkDropListOrientation="vertical"
|
|
[cdkDropListDisabled]="dragDisabled"
|
|
(cdkDropListDropped)="handleRootDrop($event)">
|
|
<span class="text-sm font-medium">Drop items here</span>
|
|
</div>
|
|
} @else {
|
|
<div
|
|
class="bookmarks-tree"
|
|
cdkDropList
|
|
[cdkDropListData]="displayItems()"
|
|
cdkDropListId="root"
|
|
[cdkDropListConnectedTo]="getDropListConnections('root')"
|
|
cdkDropListOrientation="vertical"
|
|
[cdkDropListDisabled]="dragDisabled"
|
|
(cdkDropListDropped)="handleRootDrop($event)">
|
|
@if (!dragDisabled) {
|
|
<div class="mb-2 rounded-md border border-dashed border-blue-500/40 dark:border-blue-400/40 bg-blue-500/5 dark:bg-blue-400/5 px-3 py-2 text-xs font-medium text-blue-600 dark:text-blue-300 text-center">
|
|
Drop here to move to root
|
|
</div>
|
|
}
|
|
@for (node of displayItems(); track node.ctime) {
|
|
<app-bookmark-item
|
|
cdkDrag
|
|
[cdkDragDisabled]="dragDisabled"
|
|
[cdkDragData]="{ ctime: node.ctime, parentCtime: null }"
|
|
[node]="node"
|
|
[level]="0"
|
|
[dragDisabled]="dragDisabled"
|
|
[dropListIds]="dropListIds()"
|
|
(bookmarkClick)="onBookmarkClick($event)"
|
|
class="mb-1" />
|
|
}
|
|
@if (displayItems().length === 0) {
|
|
<p class="text-sm text-gray-400 dark:text-gray-500 py-4 text-center">No bookmarks to display.</p>
|
|
}
|
|
</div>
|
|
}
|
|
</div>
|
|
|
|
<!-- Conflict Modal -->
|
|
@if (conflictInfo()) {
|
|
<div class="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
|
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-xl max-w-md w-full mx-4 p-6">
|
|
<h3 class="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-3">Conflict Detected</h3>
|
|
<p class="text-sm text-gray-600 dark:text-gray-400 mb-4">
|
|
The bookmarks file has been modified externally. What would you like to do?
|
|
</p>
|
|
<div class="flex gap-3">
|
|
<button
|
|
(click)="resolveConflictReload()"
|
|
class="flex-1 px-4 py-2 text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 rounded-md transition-colors">
|
|
Reload from file
|
|
</button>
|
|
<button
|
|
(click)="resolveConflictOverwrite()"
|
|
class="flex-1 px-4 py-2 text-sm font-medium text-white bg-red-600 hover:bg-red-700 rounded-md transition-colors">
|
|
Overwrite file
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
<!-- Modals placeholders - will be implemented separately -->
|
|
</div>
|