|
19 | 19 | <nav class="portal-topbar__nav" aria-label="@L["Portal"]" data-portal-topbar> |
20 | 20 | <a class="portal-topbar__portal-link" href="@_portalTopBar.PortalLink.Href">@L[_portalTopBar.PortalLink.TextKey]</a> |
21 | 21 |
|
22 | | - @if (_portalTopBar.IsPortalAdmin && _portalTopBar.PortalAdminLinks.Count > 0) |
| 22 | + @if (_portalTopBar.IsPortalAdmin && _portalTopBar.PortalAdminSections.Count > 0) |
23 | 23 | { |
24 | | - <details class="portal-topbar__admin" data-portal-topbar-overlay> |
| 24 | + <details class="portal-topbar__admin" data-portal-topbar-overlay data-portal-topbar-hover-menu> |
25 | 25 | <summary title="@L[_portalTopBar.PortalAdminToggleTextKey]" aria-label="@L[_portalTopBar.PortalAdminToggleTextKey]"> |
26 | 26 | <span aria-hidden="true">⚙</span> |
27 | 27 | <span class="visually-hidden">@L[_portalTopBar.PortalAdminToggleTextKey]</span> |
28 | 28 | </summary> |
29 | | - <div class="portal-topbar__admin-menu"> |
30 | | - @foreach (var link in _portalTopBar.PortalAdminLinks) |
| 29 | + <div class="portal-topbar__admin-menu" data-portal-topbar-admin-menu> |
| 30 | + @foreach (var section in _portalTopBar.PortalAdminSections) |
31 | 31 | { |
32 | | - <a class="portal-topbar__admin-link" href="@link.Href">@L[link.TextKey]</a> |
| 32 | + <details class="portal-topbar__admin-submenu" data-portal-topbar-admin-submenu data-portal-topbar-overlay> |
| 33 | + <summary class="portal-topbar__admin-submenu-trigger"> |
| 34 | + <span>@L[section.TextKey]</span> |
| 35 | + </summary> |
| 36 | + <div class="portal-topbar__admin-submenu-menu"> |
| 37 | + @foreach (var item in section.Items) |
| 38 | + { |
| 39 | + if (item.SeparatorBefore) |
| 40 | + { |
| 41 | + <div class="portal-topbar__admin-separator" role="separator" aria-hidden="true"></div> |
| 42 | + } |
| 43 | + <a class="portal-topbar__admin-link" href="@item.Href">@L[item.TextKey]</a> |
| 44 | + } |
| 45 | + </div> |
| 46 | + </details> |
33 | 47 | } |
34 | 48 | </div> |
35 | 49 | </details> |
|
69 | 83 | <div class="portal-topbar__actions"> |
70 | 84 | @if (_portalTopBar.LanguageOptions.Count > 1) |
71 | 85 | { |
72 | | - <details class="portal-topbar__language" data-portal-topbar-overlay> |
| 86 | + <details class="portal-topbar__language" data-portal-topbar-overlay data-portal-topbar-hover-menu> |
73 | 87 | <summary title="@L[_portalTopBar.LanguageToggleTextKey]" aria-label="@L[_portalTopBar.LanguageToggleTextKey]"> |
74 | 88 | <span class="portal-topbar__summary-icon"> |
75 | 89 | <span class="portal-topbar__icon" aria-hidden="true"> |
|
109 | 123 |
|
110 | 124 | @if (!string.IsNullOrWhiteSpace(_portalTopBar.CurrentUserName)) |
111 | 125 | { |
112 | | - <details class="portal-topbar__profile" data-portal-topbar-overlay> |
| 126 | + <details class="portal-topbar__profile" data-portal-topbar-overlay data-portal-topbar-hover-menu> |
113 | 127 | <summary title="@L["User"]" aria-label="@L["User"]"> |
114 | 128 | <span class="portal-topbar__icon" aria-hidden="true"> |
115 | 129 | <svg viewBox="0 0 24 24" focusable="false" aria-hidden="true"> |
|
123 | 137 | <div class="portal-topbar__profile-identity"> |
124 | 138 | <span class="portal-topbar__profile-roles-heading">@L["User"]</span> |
125 | 139 | <div class="portal-topbar__profile-user-row"> |
126 | | - <div class="portal-topbar__profile-current-user" title="@_portalTopBar.CurrentUserName">@_portalTopBar.CurrentUserName</div> |
| 140 | + <button type="button" |
| 141 | + class="portal-topbar__profile-copy-name" |
| 142 | + title="@L["Copy full user name"]" |
| 143 | + aria-label="@L["Copy full user name"]" |
| 144 | + data-copy-to-clipboard="true" |
| 145 | + data-copy-text="@_portalTopBar.CurrentUserName"> |
| 146 | + <span class="portal-topbar__profile-current-user" title="@_portalTopBar.CurrentUserName">@DisplayUserName(_portalTopBar.CurrentUserName)</span> |
| 147 | + <span class="portal-topbar__profile-copy-inline-icon" aria-hidden="true"> |
| 148 | + <svg viewBox="0 0 24 24" focusable="false" aria-hidden="true"> |
| 149 | + <rect x="9" y="9" width="10" height="10" rx="2"></rect> |
| 150 | + <path d="M7 15H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h7a2 2 0 0 1 2 2v1"></path> |
| 151 | + </svg> |
| 152 | + </span> |
| 153 | + </button> |
| 154 | + <button type="button" |
| 155 | + class="portal-topbar__profile-action-button" |
| 156 | + title="@L["Log out"]" |
| 157 | + aria-label="@L["Log out"]"> |
| 158 | + <span class="portal-topbar__icon" aria-hidden="true"> |
| 159 | + <svg viewBox="0 0 24 24" focusable="false" aria-hidden="true"> |
| 160 | + <path d="M14 8l4 4-4 4"></path> |
| 161 | + <path d="M18 12H9"></path> |
| 162 | + <path d="M11 4H6a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h5"></path> |
| 163 | + </svg> |
| 164 | + </span> |
| 165 | + </button> |
127 | 166 | </div> |
128 | 167 | </div> |
129 | 168 | <div class="portal-topbar__profile-section"> |
|
245 | 284 |
|
246 | 285 | private string CultureText(string? text) |
247 | 286 | => string.IsNullOrWhiteSpace(text) ? string.Empty : L[text]; |
| 287 | + |
| 288 | + private static string DisplayUserName(string? value) |
| 289 | + { |
| 290 | + if (string.IsNullOrWhiteSpace(value)) |
| 291 | + { |
| 292 | + return string.Empty; |
| 293 | + } |
| 294 | + |
| 295 | + var separatorIndex = value.LastIndexOf('\\'); |
| 296 | + return separatorIndex >= 0 && separatorIndex < value.Length - 1 |
| 297 | + ? value[(separatorIndex + 1)..] |
| 298 | + : value; |
| 299 | + } |
248 | 300 | } |
0 commit comments