-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathproject-selector.component.html
More file actions
152 lines (142 loc) · 6.67 KB
/
project-selector.component.html
File metadata and controls
152 lines (142 loc) · 6.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
<!-- Copyright The Linux Foundation and each contributor to LFX. -->
<!-- SPDX-License-Identifier: MIT -->
<button
type="button"
class="box-border content-stretch flex gap-3 items-center overflow-clip p-3 relative w-full cursor-pointer hover:bg-gray-50 transition-colors rounded-lg"
(click)="togglePanel($event, popover)"
[attr.aria-expanded]="popover.overlayVisible"
data-testid="project-selector">
<div class="bg-gray-100 overflow-clip relative rounded-lg shrink-0 size-11 border border-gray-200">
<div class="flex items-center justify-center size-full p-1.5">
@if (displayLogo()) {
<img [alt]="displayName()" class="w-full h-full object-contain" [src]="displayLogo()" />
} @else {
<img src="images/logo_lfx.svg" alt="LFX Logo" />
}
</div>
</div>
<div class="flex flex-col gap-1.5 grow items-start relative min-w-0 overflow-hidden">
<p class="sidebar-project-name" [attr.title]="displayName()">{{ displayName() }}</p>
<div class="flex items-center gap-1 w-full min-w-0 text-left">
<span class="text-xs font-normal text-gray-400 leading-none truncate">{{ lensTypeLabel() }}</span>
@if (selectedRoleLabel()) {
<span class="text-xs font-normal text-gray-400 leading-none shrink-0" aria-hidden="true">・</span>
<span
role="img"
[attr.aria-label]="'Role: ' + selectedRoleLabel()"
[pTooltip]="selectedRoleLabel()"
tooltipPosition="top"
class="inline-flex items-center justify-center size-5 rounded-full bg-white border border-gray-200 text-gray-700 shrink-0">
<i [ngClass]="selectedRoleIcon()" class="text-[10px]" aria-hidden="true"></i>
</span>
}
</div>
</div>
<i class="fa-light fa-angles-up-down shrink-0 text-gray-400" aria-hidden="true"></i>
</button>
<p-popover
#popover
appendTo="body"
[style]="{ width: '28rem' }"
[styleClass]="panelStyleClass()"
(onShow)="onPopoverShow()"
(onHide)="onPopoverHide()"
data-testid="project-selector-panel">
<div class="p-1">
<!-- Search input — no background or border -->
<div class="relative mb-2">
<i class="fa-light fa-search absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400"></i>
<input
[pAutoFocus]="true"
pInputText
type="text"
[formControl]="searchControl"
[placeholder]="searchPlaceholder()"
class="pl-10 h-9 w-full text-sm !border-0 !bg-transparent !shadow-none focus:!ring-0"
data-testid="project-search-input" />
</div>
<!-- Tab switcher — hybrid mode only. These are filter buttons, not an ARIA tabs widget, so we
use aria-pressed instead of role="tab" to avoid promising tab-panel semantics we don't ship. -->
@if (hybridMode()) {
<div class="flex gap-1 mb-5 bg-gray-100 rounded-full p-1" role="group" aria-label="Filter results">
@for (tab of selectorTabs; track tab) {
<button
type="button"
[attr.aria-pressed]="activeTab() === tab"
(click)="activeTab.set(tab)"
class="flex-1 text-sm font-medium py-1 px-3 rounded-full transition-colors capitalize"
[class.bg-white]="activeTab() === tab"
[class.shadow-sm]="activeTab() === tab"
[class.text-gray-900]="activeTab() === tab"
[class.text-gray-500]="activeTab() !== tab">
{{ tab === 'all' ? 'All' : tab === 'foundations' ? 'Foundations' : 'Projects' }}
</button>
}
</div>
}
<div class="max-h-[400px] overflow-y-auto">
@for (displayItem of displayedItems(); track displayItem.item.uid; let i = $index) {
<!-- Auto-load sentinel 8 items from the end -->
@if (i === autoLoadTriggerIndex() && hasMore()) {
@defer (on viewport) {
<div lfxOnRender (rendered)="loadMore()" class="h-0 w-full" aria-hidden="true"></div>
} @placeholder {
<div class="h-0 w-full" aria-hidden="true"></div>
}
}
<div
class="mb-1"
[class.pl-5]="displayItem.isNested"
[class.border-l-2]="displayItem.isNested"
[class.border-gray-100]="displayItem.isNested"
[class.ml-[18px]]="displayItem.isNested">
<button
type="button"
class="flex items-center gap-3 p-2 w-full text-left rounded-lg transition-colors"
[class.bg-blue-50]="displayItem.isSelected"
[class.hover:bg-gray-100]="!displayItem.isSelected"
(click)="selectItem(displayItem.item, popover)"
[attr.data-testid]="'lens-item-' + displayItem.item.slug"
[attr.data-selected]="displayItem.isSelected">
<div class="bg-gray-100 overflow-clip rounded-lg shrink-0 size-9 border border-gray-200">
<div class="flex items-center justify-center size-full p-1.5">
@if (displayItem.item.logoUrl) {
<img [alt]="displayItem.item.name" class="w-full h-full object-contain" [src]="displayItem.item.logoUrl" />
} @else {
<svg width="14" height="14" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3.96369 15.9789V7.98987H0V19.9504H11.9372V15.9789H3.96369Z" class="fill-blue-500" />
<path d="M19.8645 0H0V6.00333H3.96369V4.01761H15.9009V15.9781H13.919V19.9495H19.8645V0Z" class="fill-blue-800" />
</svg>
}
</div>
</div>
<div class="flex-1 min-w-0">
<p class="font-medium text-sm text-gray-900 mb-0 truncate">{{ displayItem.item.name || displayItem.item.slug }}</p>
@if (displayItem.roleLabel) {
<p class="text-xs text-gray-400 leading-none mt-0.5 truncate">
<i [ngClass]="displayItem.roleIcon" class="mr-1" aria-hidden="true"></i>{{ displayItem.roleLabel }}
</p>
}
</div>
<i
class="fa-light fa-check text-blue-500 shrink-0 transition-opacity"
[class.opacity-100]="displayItem.isSelected"
[class.opacity-0]="!displayItem.isSelected"
[attr.aria-label]="displayItem.isSelected ? 'Selected' : null"
[attr.aria-hidden]="!displayItem.isSelected"></i>
</button>
</div>
} @empty {
@if (!loading()) {
<div class="text-center py-8 text-gray-500 text-sm">{{ emptyMessage() }}</div>
}
}
@if (loading()) {
<div class="flex items-center justify-center py-3 text-gray-500 text-sm gap-2">
<i class="fa-light fa-spinner fa-spin"></i>
<span>Loading...</span>
</div>
}
</div>
</div>
</p-popover>