Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions apps/web/src/assets/i18n/ar.json
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,8 @@
},
"SHELL": {
"BRAND_ALT": "IPTVnator",
"EXPAND_NAVIGATION": "إظهار القائمة",
"COLLAPSE_NAVIGATION": "إخفاء القائمة",
"RAIL_DASHBOARD": "لوحة التحكم",
"OPEN_DASHBOARD": "فتح لوحة التحكم",
"RAIL_SOURCES": "المصادر",
Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/assets/i18n/ary.json
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,8 @@
},
"SHELL": {
"BRAND_ALT": "IPTVnator",
"EXPAND_NAVIGATION": "وسّع التنقل",
"COLLAPSE_NAVIGATION": "طوي التنقل",
"RAIL_DASHBOARD": "لوحة التحكم",
"OPEN_DASHBOARD": "حل لوحة التحكم",
"RAIL_SOURCES": "المصادر",
Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/assets/i18n/by.json
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,8 @@
},
"SHELL": {
"BRAND_ALT": "IPTVnator",
"EXPAND_NAVIGATION": "Разгарнуць навігацыю",
"COLLAPSE_NAVIGATION": "Згарнуць навігацыю",
"RAIL_DASHBOARD": "Панэль",
"OPEN_DASHBOARD": "Адкрыць панэль",
"RAIL_SOURCES": "Крыніцы",
Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/assets/i18n/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,8 @@
},
"SHELL": {
"BRAND_ALT": "IPTVnator",
"EXPAND_NAVIGATION": "Navigation ausklappen",
"COLLAPSE_NAVIGATION": "Navigation einklappen",
"RAIL_DASHBOARD": "Dashboard",
"OPEN_DASHBOARD": "Dashboard öffnen",
"RAIL_SOURCES": "Quellen",
Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/assets/i18n/el.json
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,8 @@
},
"SHELL": {
"BRAND_ALT": "IPTVnator",
"EXPAND_NAVIGATION": "Ανάπτυξη πλοήγησης",
"COLLAPSE_NAVIGATION": "Σύμπτυξη πλοήγησης",
"RAIL_DASHBOARD": "Πίνακας ελέγχου",
"OPEN_DASHBOARD": "Άνοιγμα πίνακα ελέγχου",
"RAIL_SOURCES": "Πηγές",
Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,8 @@
},
"SHELL": {
"BRAND_ALT": "IPTVnator",
"EXPAND_NAVIGATION": "Expand navigation",
"COLLAPSE_NAVIGATION": "Collapse navigation",
"RAIL_DASHBOARD": "Dashboard",
"OPEN_DASHBOARD": "Open dashboard",
"RAIL_SOURCES": "Sources",
Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/assets/i18n/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,8 @@
},
"SHELL": {
"BRAND_ALT": "IPTVnator",
"EXPAND_NAVIGATION": "Ampliar navegación",
"COLLAPSE_NAVIGATION": "Contraer navegación",
"RAIL_DASHBOARD": "Panel",
"OPEN_DASHBOARD": "Abrir panel",
"RAIL_SOURCES": "Fuentes",
Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/assets/i18n/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,8 @@
},
"SHELL": {
"BRAND_ALT": "IPTVnator",
"EXPAND_NAVIGATION": "Développer la navigation",
"COLLAPSE_NAVIGATION": "Réduire la navigation",
"RAIL_DASHBOARD": "Tableau de bord",
"OPEN_DASHBOARD": "Ouvrir le tableau de bord",
"RAIL_SOURCES": "Sources",
Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/assets/i18n/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,8 @@
},
"SHELL": {
"BRAND_ALT": "IPTVnator",
"EXPAND_NAVIGATION": "Espandi navigazione",
"COLLAPSE_NAVIGATION": "Comprimi navigazione",
"RAIL_DASHBOARD": "Dashboard",
"OPEN_DASHBOARD": "Apri dashboard",
"RAIL_SOURCES": "Sorgenti",
Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/assets/i18n/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,8 @@
},
"SHELL": {
"BRAND_ALT": "IPTVnator",
"EXPAND_NAVIGATION": "ナビゲーションを展開",
"COLLAPSE_NAVIGATION": "ナビゲーションを折りたたむ",
"RAIL_DASHBOARD": "ダッシュボード",
"OPEN_DASHBOARD": "ダッシュボードを開く",
"RAIL_SOURCES": "ソース",
Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/assets/i18n/ko.json
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,8 @@
},
"SHELL": {
"BRAND_ALT": "IPTVnator",
"EXPAND_NAVIGATION": "탐색 펼치기",
"COLLAPSE_NAVIGATION": "탐색 접기",
"RAIL_DASHBOARD": "대시보드",
"OPEN_DASHBOARD": "대시보드 열기",
"RAIL_SOURCES": "소스",
Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/assets/i18n/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,8 @@
},
"SHELL": {
"BRAND_ALT": "IPTVnator",
"EXPAND_NAVIGATION": "Navigatie uitklappen",
"COLLAPSE_NAVIGATION": "Navigatie inklappen",
"RAIL_DASHBOARD": "Dashboard",
"OPEN_DASHBOARD": "Open dashboard",
"RAIL_SOURCES": "Bronnen",
Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/assets/i18n/pl.json
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,8 @@
},
"SHELL": {
"BRAND_ALT": "IPTVnator",
"EXPAND_NAVIGATION": "Rozwiń nawigację",
"COLLAPSE_NAVIGATION": "Zwiń nawigację",
"RAIL_DASHBOARD": "Pulpit",
"OPEN_DASHBOARD": "Otwórz pulpit",
"RAIL_SOURCES": "Źródła",
Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/assets/i18n/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,8 @@
},
"SHELL": {
"BRAND_ALT": "IPTVnator",
"EXPAND_NAVIGATION": "Expandir navegação",
"COLLAPSE_NAVIGATION": "Recolher navegação",
"RAIL_DASHBOARD": "Dashboard",
"OPEN_DASHBOARD": "Abrir dashboard",
"RAIL_SOURCES": "Fontes",
Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/assets/i18n/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,8 @@
},
"SHELL": {
"BRAND_ALT": "IPTVnator",
"EXPAND_NAVIGATION": "Развернуть навигацию",
"COLLAPSE_NAVIGATION": "Свернуть навигацию",
"RAIL_DASHBOARD": "Панель",
"OPEN_DASHBOARD": "Открыть панель",
"RAIL_SOURCES": "Источники",
Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/assets/i18n/tr.json
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,8 @@
},
"SHELL": {
"BRAND_ALT": "IPTVnator",
"EXPAND_NAVIGATION": "Gezinmeyi genişlet",
"COLLAPSE_NAVIGATION": "Gezinmeyi daralt",
"RAIL_DASHBOARD": "Pano",
"OPEN_DASHBOARD": "Panoyu aç",
"RAIL_SOURCES": "Kaynaklar",
Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/assets/i18n/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,8 @@
},
"SHELL": {
"BRAND_ALT": "IPTVnator",
"EXPAND_NAVIGATION": "展开导航",
"COLLAPSE_NAVIGATION": "收起导航",
"RAIL_DASHBOARD": "仪表盘",
"OPEN_DASHBOARD": "打开仪表盘",
"RAIL_SOURCES": "源",
Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/assets/i18n/zhtw.json
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,8 @@
},
"SHELL": {
"BRAND_ALT": "IPTVnator",
"EXPAND_NAVIGATION": "展開導覽",
"COLLAPSE_NAVIGATION": "收合導覽",
"RAIL_DASHBOARD": "儀表板",
"OPEN_DASHBOARD": "開啟儀表板",
"RAIL_SOURCES": "來源",
Expand Down
11 changes: 11 additions & 0 deletions docs/architecture/workspace-shell.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,17 @@ Rail navigation is also shell-owned:
3. On dashboard, sources, settings, and global favorites, the shell falls back
to the currently selected playlist so provider navigation remains available
even outside a provider route.
4. The IPTVnator brand button toggles the primary rail between its default
60px icon-only layout and an expanded, label-bearing layout. The expanded
width is content-driven by the longest visible label with equal horizontal
spacing around icons and text, capped to preserve the content area. Mobile
navigation remains compact and does not enter the expanded state.
5. Active-link markers must stay inset within the rail so selection styling is
not clipped by the rail's overflow boundary.
6. The navigation links own the rail's scroll area while the settings shortcut
remains visible in the fixed footer.
7. Expanded labels use automatic text direction and logical start alignment so
left-to-right and right-to-left translations align naturally.

Command palette behavior is shell-owned but view-extensible:

Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
<nav class="rail-links">
<nav class="rail-links" [class.is-expanded]="expanded()">
@for (link of links(); track link.path.join('/')) {
<a
class="portal-rail-link nav-item"
[routerLink]="link.path"
[routerLinkActive]="activeClass()"
[routerLinkActiveOptions]="{ exact: link.exact ?? false }"
[matTooltip]="link.tooltip"
[matTooltipDisabled]="expanded()"
[matTooltipPosition]="'right'"
[attr.aria-label]="link.tooltip"
[attr.aria-current]="isActive(link) ? 'page' : null"
[class.active]="activeClass() === 'active' && isActive(link)"
[class.is-active]="
activeClass() === 'is-active' && isActive(link)
"
[class.is-active]="activeClass() === 'is-active' && isActive(link)"
>
<mat-icon>{{ resolveIcon(link) }}</mat-icon>
@if (expanded()) {
<span class="portal-rail-link-label" dir="auto">
{{ link.tooltip }}
</span>
}
</a>
}
</nav>
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,21 @@
width: 100%;
}

.rail-links.is-expanded {
align-items: stretch;

.portal-rail-link {
width: 100%;
justify-content: flex-start;
gap: 12px;
padding: 0 12px;
}
}

.portal-rail-link {
width: 44px;
height: 44px;
box-sizing: border-box;
padding: 0;
gap: 0;
border-radius: 14px;
Expand All @@ -34,10 +46,7 @@

&:hover {
color: var(--rail-link-hover-color, var(--mat-sys-on-surface));
background: var(
--rail-link-hover-bg,
var(--mat-sys-surface-container)
);
background: var(--rail-link-hover-bg, var(--mat-sys-surface-container));
}

mat-icon {
Expand All @@ -47,6 +56,17 @@
}
}

.portal-rail-link-label {
flex: 1 1 auto;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
text-align: start;
white-space: nowrap;
font-size: 14px;
font-weight: 500;
}

@for $i from 1 through 8 {
.portal-rail-link:nth-child(#{$i}) {
animation-delay: #{($i - 1) * 30}ms;
Expand Down Expand Up @@ -88,7 +108,7 @@
.portal-rail-link.active::after {
content: '';
position: absolute;
right: -4px;
right: 3px;
top: 50%;
transform: translateY(-50%);
width: 4px;
Expand All @@ -97,3 +117,25 @@
background: var(--rail-link-active-icon, var(--app-selection-color));
box-shadow: 0 0 10px var(--rail-link-active-glow, var(--app-selection-glow));
}

@media (max-width: 640px) {
:host {
flex: 0 0 auto;
width: auto;
}

.rail-links {
flex-direction: row;
}

.rail-links.is-expanded .portal-rail-link {
width: 44px;
justify-content: center;
gap: 0;
padding: 0;
}

.portal-rail-link-label {
display: none;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { provideRouter } from '@angular/router';
import { WorkspaceShellRailLinksComponent } from './workspace-shell-rail-links.component';

describe('WorkspaceShellRailLinksComponent', () => {
let fixture: ComponentFixture<WorkspaceShellRailLinksComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [WorkspaceShellRailLinksComponent],
providers: [provideRouter([])],
}).compileComponents();

fixture = TestBed.createComponent(WorkspaceShellRailLinksComponent);
fixture.componentRef.setInput('links', [
{
icon: 'dashboard',
tooltip: 'Dashboard',
path: ['/workspace/dashboard'],
exact: true,
},
{
icon: 'movie',
tooltip: 'Movies',
path: ['/workspace/movies'],
},
]);
});

it('keeps labels hidden in the compact rail', () => {
fixture.detectChanges();

expect(
fixture.nativeElement.querySelectorAll('.portal-rail-link-label')
).toHaveLength(0);
});

it('renders every navigation label in the expanded rail', () => {
fixture.componentRef.setInput('expanded', true);
fixture.detectChanges();

const labels = Array.from(
fixture.nativeElement.querySelectorAll('.portal-rail-link-label')
).map((element: Element) => element.textContent?.trim());

expect(labels).toEqual(['Dashboard', 'Movies']);
expect(
fixture.nativeElement
.querySelector('.portal-rail-link-label')
?.getAttribute('dir')
).toBe('auto');
expect(
fixture.nativeElement
.querySelector('.rail-links')
?.classList.contains('is-expanded')
).toBe(true);
});
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { ChangeDetectionStrategy, Component, inject, input } from '@angular/core';
import {
ChangeDetectionStrategy,
Component,
inject,
input,
} from '@angular/core';
import { MatIcon } from '@angular/material/icon';
import { MatTooltip } from '@angular/material/tooltip';
import {
Expand Down Expand Up @@ -27,9 +32,12 @@ export class WorkspaceShellRailLinksComponent {
PortalRailSection | string | null | undefined
>(null);
readonly activeClass = input<'active' | 'is-active'>('is-active');
readonly expanded = input(false);

resolveIcon(link: PortalRailLink): string {
const normalizedPath = link.path.map((segment) => String(segment)).join('/');
const normalizedPath = link.path
.map((segment) => String(segment))
.join('/');
if (normalizedPath.includes('/workspace/sources')) {
return 'playlist_play';
}
Expand Down
Loading
Loading