mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-09 01:16:15 -08:00
feat: enhance global favorites list and unified collection components with draggable functionality and improved layout
Entire-Checkpoint: 56d3e3141f61
This commit is contained in:
1 parent
34c2323216
commit
d85ef89893
9 files changed
+91
-62
No files matched your search
+2
-2
@@ -1,7 +1,7 @@
|
||||
<div
|
||||
class="nav-list"
|
||||
cdkDropList
|
||||
[cdkDropListDisabled]="hasSearchTerm()"
|
||||
[cdkDropListDisabled]="!draggable() || hasSearchTerm()"
|
||||
(cdkDropListDropped)="onDrop($event)"
|
||||
>
|
||||
@if (enrichedChannels().length === 0) {
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
@for (ch of enrichedChannels(); track trackByUid($index, ch)) {
|
||||
<app-channel-list-item
|
||||
[isDraggable]="!hasSearchTerm()"
|
||||
[isDraggable]="draggable() && !hasSearchTerm()"
|
||||
[logo]="ch.logo"
|
||||
[name]="ch.name"
|
||||
[selected]="activeUid() === ch.uid"
|
||||
|
||||
+1
@@ -39,6 +39,7 @@ export class GlobalFavoritesListComponent {
|
||||
readonly progressTick = input<number>(0);
|
||||
readonly activeUid = input<string | null>(null);
|
||||
readonly searchTermInput = input('');
|
||||
readonly draggable = input(true);
|
||||
|
||||
readonly channelSelected = output<UnifiedFavoriteChannel>();
|
||||
readonly channelsReordered = output<UnifiedFavoriteChannel[]>();
|
||||
|
||||
+57
-39
@@ -1,6 +1,10 @@
|
||||
@if (isLoading()) {
|
||||
<div class="collection-header">
|
||||
<span class="collection-title">{{ title() | translate }}</span>
|
||||
<div class="collection-header-toggles">
|
||||
<span class="toggle-placeholder"></span>
|
||||
<span class="toggle-placeholder toggle-placeholder--scope"></span>
|
||||
</div>
|
||||
</div>
|
||||
@if (selectedContentType() === 'live') {
|
||||
<div class="skeleton-list">
|
||||
@@ -43,50 +47,64 @@
|
||||
<div class="collection-header">
|
||||
<span class="collection-title">{{ title() | translate }}</span>
|
||||
|
||||
@if (showContentToggle()) {
|
||||
<div class="collection-header-toggles">
|
||||
@if (showContentToggle()) {
|
||||
<mat-button-toggle-group
|
||||
class="content-toggle"
|
||||
[value]="selectedContentType()"
|
||||
(change)="onContentTypeChange($event.value)"
|
||||
hideSingleSelectionIndicator
|
||||
>
|
||||
@if (hasLive()) {
|
||||
<mat-button-toggle value="live">
|
||||
{{ 'PORTALS.LIVE_TV' | translate }}
|
||||
</mat-button-toggle>
|
||||
}
|
||||
@if (hasMovies()) {
|
||||
<mat-button-toggle value="movie">
|
||||
{{ 'PORTALS.MOVIES' | translate }}
|
||||
</mat-button-toggle>
|
||||
}
|
||||
@if (hasSeries()) {
|
||||
<mat-button-toggle value="series">
|
||||
{{ 'PORTALS.SERIES' | translate }}
|
||||
</mat-button-toggle>
|
||||
}
|
||||
</mat-button-toggle-group>
|
||||
}
|
||||
|
||||
<mat-button-toggle-group
|
||||
class="content-toggle"
|
||||
[value]="selectedContentType()"
|
||||
(change)="onContentTypeChange($event.value)"
|
||||
class="scope-toggle"
|
||||
[value]="scope()"
|
||||
(change)="onScopeChange($event.value)"
|
||||
hideSingleSelectionIndicator
|
||||
>
|
||||
@if (hasLive()) {
|
||||
<mat-button-toggle value="live">
|
||||
{{ 'PORTALS.LIVE_TV' | translate }}
|
||||
</mat-button-toggle>
|
||||
}
|
||||
@if (hasMovies()) {
|
||||
<mat-button-toggle value="movie">
|
||||
{{ 'PORTALS.MOVIES' | translate }}
|
||||
</mat-button-toggle>
|
||||
}
|
||||
@if (hasSeries()) {
|
||||
<mat-button-toggle value="series">
|
||||
{{ 'PORTALS.SERIES' | translate }}
|
||||
</mat-button-toggle>
|
||||
}
|
||||
<mat-button-toggle value="playlist">
|
||||
<mat-icon>playlist_play</mat-icon>
|
||||
<span class="toggle-label">{{
|
||||
'PORTALS.THIS_PLAYLIST' | translate
|
||||
}}</span>
|
||||
</mat-button-toggle>
|
||||
<mat-button-toggle value="all">
|
||||
<mat-icon>public</mat-icon>
|
||||
<span class="toggle-label">{{
|
||||
'PORTALS.ALL_PLAYLISTS' | translate
|
||||
}}</span>
|
||||
</mat-button-toggle>
|
||||
</mat-button-toggle-group>
|
||||
}
|
||||
|
||||
<mat-button-toggle-group
|
||||
class="scope-toggle"
|
||||
[value]="scope()"
|
||||
(change)="onScopeChange($event.value)"
|
||||
hideSingleSelectionIndicator
|
||||
>
|
||||
<mat-button-toggle value="playlist">
|
||||
<mat-icon>playlist_play</mat-icon>
|
||||
<span class="toggle-label">{{
|
||||
'PORTALS.THIS_PLAYLIST' | translate
|
||||
}}</span>
|
||||
</mat-button-toggle>
|
||||
<mat-button-toggle value="all">
|
||||
<mat-icon>public</mat-icon>
|
||||
<span class="toggle-label">{{
|
||||
'PORTALS.ALL_PLAYLISTS' | translate
|
||||
}}</span>
|
||||
</mat-button-toggle>
|
||||
</mat-button-toggle-group>
|
||||
@if (mode() === 'recent' && allItems().length > 0) {
|
||||
<button
|
||||
type="button"
|
||||
mat-icon-button
|
||||
(click)="clearAllRecent()"
|
||||
[matTooltip]="'WORKSPACE.SHELL.CLEAR_RECENTLY_VIEWED_SECTION' | translate"
|
||||
[attr.aria-label]="'WORKSPACE.SHELL.CLEAR_RECENTLY_VIEWED_SECTION_ARIA' | translate"
|
||||
>
|
||||
<mat-icon>delete_sweep</mat-icon>
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@switch (selectedContentType()) {
|
||||
|
||||
+17
@@ -39,6 +39,23 @@
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.collection-header-toggles {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.toggle-placeholder {
|
||||
height: 43px;
|
||||
width: 120px;
|
||||
border-radius: 18px;
|
||||
background: color-mix(in srgb, var(--mat-sys-on-surface) 6%, transparent);
|
||||
|
||||
&--scope {
|
||||
width: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
.content-toggle,
|
||||
.scope-toggle {
|
||||
.toggle-label {
|
||||
|
||||
+12
@@ -9,7 +9,9 @@ import {
|
||||
signal,
|
||||
} from '@angular/core';
|
||||
import { MatButtonToggleModule } from '@angular/material/button-toggle';
|
||||
import { MatIconButton } from '@angular/material/button';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatTooltip } from '@angular/material/tooltip';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
import {
|
||||
@@ -32,7 +34,9 @@ import { UnifiedGridTabComponent } from './unified-grid-tab.component';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [
|
||||
MatButtonToggleModule,
|
||||
MatIconButton,
|
||||
MatIconModule,
|
||||
MatTooltip,
|
||||
TranslatePipe,
|
||||
UnifiedGridTabComponent,
|
||||
UnifiedLiveTabComponent,
|
||||
@@ -151,6 +155,14 @@ export class UnifiedCollectionPageComponent implements OnInit {
|
||||
);
|
||||
}
|
||||
|
||||
async clearAllRecent(): Promise<void> {
|
||||
await this.recentData.clearRecentItems(
|
||||
this.scope(),
|
||||
this.playlistId()
|
||||
);
|
||||
this.allItems.set([]);
|
||||
}
|
||||
|
||||
async onReorder(items: UnifiedCollectionItem[]): Promise<void> {
|
||||
const nonLive = this.allItems().filter(
|
||||
(i) => i.contentType !== 'live'
|
||||
|
||||
+2
@@ -18,6 +18,7 @@
|
||||
[progressTick]="progressTick()"
|
||||
[activeUid]="activeUid()"
|
||||
[searchTermInput]="searchTerm()"
|
||||
[draggable]="mode() === 'favorites'"
|
||||
(channelSelected)="onChannelSelected($event)"
|
||||
(channelsReordered)="onReorder($event)"
|
||||
(favoriteToggled)="onFavoriteToggled($event)"
|
||||
@@ -97,6 +98,7 @@
|
||||
[progressTick]="progressTick()"
|
||||
[activeUid]="activeUid()"
|
||||
[searchTermInput]="searchTerm()"
|
||||
[draggable]="mode() === 'favorites'"
|
||||
(channelSelected)="onChannelSelected($event)"
|
||||
(channelsReordered)="onReorder($event)"
|
||||
(favoriteToggled)="onFavoriteToggled($event)"
|
||||
|
||||
-13
@@ -101,18 +101,5 @@
|
||||
<mat-icon>download</mat-icon>
|
||||
</button>
|
||||
}
|
||||
@if (headerBulkAction(); as action) {
|
||||
<span class="header-actions-divider" aria-hidden="true"></span>
|
||||
<button
|
||||
type="button"
|
||||
mat-icon-button
|
||||
(click)="onHeaderBulkActionRequested()"
|
||||
[disabled]="action.disabled"
|
||||
[matTooltip]="action.tooltip"
|
||||
[attr.aria-label]="action.ariaLabel"
|
||||
>
|
||||
<mat-icon>{{ action.icon }}</mat-icon>
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
</header>
|
||||
-6
@@ -16,7 +16,6 @@ import {
|
||||
PlaylistType,
|
||||
} from '@iptvnator/playlist/shared/ui';
|
||||
import { WorkspaceHeaderAction } from '@iptvnator/portal/shared/util';
|
||||
import { WorkspaceHeaderBulkAction } from '../../services/workspace-shell.facade';
|
||||
|
||||
@Component({
|
||||
selector: 'app-workspace-shell-header',
|
||||
@@ -49,7 +48,6 @@ export class WorkspaceShellHeaderComponent {
|
||||
readonly headerShortcut = input<WorkspaceHeaderAction | null>(null);
|
||||
readonly isElectron = input(false);
|
||||
readonly isDownloadsView = input(false);
|
||||
readonly headerBulkAction = input<WorkspaceHeaderBulkAction | null>(null);
|
||||
|
||||
readonly searchChanged = output<string>();
|
||||
readonly searchSubmitted = output<string>();
|
||||
@@ -58,7 +56,6 @@ export class WorkspaceShellHeaderComponent {
|
||||
readonly globalFavoritesRequested = output<void>();
|
||||
readonly headerShortcutRequested = output<void>();
|
||||
readonly downloadsRequested = output<void>();
|
||||
readonly headerBulkActionRequested = output<void>();
|
||||
readonly playlistInfoRequested = output<void>();
|
||||
readonly accountInfoRequested = output<void>();
|
||||
|
||||
@@ -100,7 +97,4 @@ export class WorkspaceShellHeaderComponent {
|
||||
this.downloadsRequested.emit();
|
||||
}
|
||||
|
||||
onHeaderBulkActionRequested(): void {
|
||||
this.headerBulkActionRequested.emit();
|
||||
}
|
||||
}
|
||||
@@ -52,7 +52,6 @@
|
||||
[headerShortcut]="facade.headerShortcut()"
|
||||
[isElectron]="facade.isElectron"
|
||||
[isDownloadsView]="facade.isDownloadsView()"
|
||||
[headerBulkAction]="facade.headerBulkAction()"
|
||||
(searchChanged)="facade.onSearchInput($event)"
|
||||
(searchSubmitted)="facade.onSearchEnter($event)"
|
||||
(commandPaletteRequested)="facade.openCommandPalette()"
|
||||
@@ -60,7 +59,6 @@
|
||||
(globalFavoritesRequested)="facade.navigateToGlobalFavorites()"
|
||||
(headerShortcutRequested)="facade.runHeaderShortcut()"
|
||||
(downloadsRequested)="facade.openDownloadsShortcut()"
|
||||
(headerBulkActionRequested)="facade.runHeaderBulkAction()"
|
||||
(playlistInfoRequested)="facade.openPlaylistInfo()"
|
||||
(accountInfoRequested)="facade.openAccountInfo()"
|
||||
/>
|
||||
|
||||
Reference in new issue
Block a user