@trismegisto/spine 3.0.2 → 3.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,7 +3,7 @@ import { inject, ChangeDetectorRef, ChangeDetectionStrategy, Component, Injectio
3
3
  import * as i3 from '@angular/router';
4
4
  import { RouterLink, Router, NavigationEnd, NavigationSkipped, RouterOutlet } from '@angular/router';
5
5
  import { BehaviorSubject, filter, take, Subject, firstValueFrom } from 'rxjs';
6
- import { IconComponent, IconButtonComponent, LoadingSquareComponent, HermesShortcutService, HERMES_MESSAGES, HeaderComponent as HeaderComponent$1, InputComponent, MenuButtonComponent, AccountAvatarComponent, ModalComponent, createPersistedWorkspaceStore, HERMES_WORKSPACE_PERSISTENCE_CONFIG, findPanelTabs, SidebarComponent as SidebarComponent$1, SidebarNodeComponent, ButtonComponent, CarouselComponent, ShortcutListComponent, HermesThemeService, HERMES_THEME_CONFIG, HermesSidebarService, HermesToastService, AlertComponent, SegmentedControlComponent, TagComponent, CodeInputComponent, RadioComponent, CheckboxComponent, CodeViewerComponent, SelectComponent, HERMES_SIDEBAR_CONFIG, HermesI18nService, ComboboxComponent, SettingsDialogComponent, SettingsRowComponent, SettingsSectionComponent, ThemePickerComponent, ToggletipComponent, BreadcrumbComponent, TooltipDirective, SkeletonComponent, WorkspaceComponent, ToastStackComponent } from '@trismegisto/angular';
6
+ import { MenuButtonComponent, HermesShortcutService, HERMES_MESSAGES, HeaderComponent as HeaderComponent$1, IconButtonComponent, IconComponent, InputComponent, AccountAvatarComponent, ModalComponent, createPersistedWorkspaceStore, HERMES_WORKSPACE_PERSISTENCE_CONFIG, findPanelTabs, SidebarComponent as SidebarComponent$1, SidebarNodeComponent, ButtonComponent, CarouselComponent, ShortcutListComponent, HermesThemeService, HERMES_THEME_CONFIG, HermesSidebarService, HermesToastService, AlertComponent, SegmentedControlComponent, TagComponent, CodeInputComponent, RadioComponent, CheckboxComponent, CodeViewerComponent, SelectComponent, HERMES_SIDEBAR_CONFIG, HermesI18nService, ComboboxComponent, SettingsDialogComponent, SettingsRowComponent, SettingsSectionComponent, ThemePickerComponent, ToggletipComponent, BreadcrumbComponent, TooltipDirective, LoadingSquareComponent, SkeletonComponent, WorkspaceComponent, ToastStackComponent } from '@trismegisto/angular';
7
7
  import { ModalComponent as ModalComponent$1 } from '@trismegisto/spine/modal';
8
8
  import { NotificationService, AlertType, NotificationComponent } from '@trismegisto/spine/notification';
9
9
  import { SidePanelComponent } from '@trismegisto/spine/side-panel';
@@ -25,9 +25,9 @@ import { Marked } from 'marked';
25
25
  import DOMPurify from 'dompurify';
26
26
 
27
27
  /**
28
- * A grade de apps não cabe no `MenuItem` de linha única do `hm-menu` (ícone grande + nome
29
- * embaixo, em coluna) — em vez de forçar isso num componente pensado pra lista de ações, usa
30
- * um popover próprio (mesmo padrão do backdrop-fecha-fora da busca global do header).
28
+ * Seletor de aplicações do realm: um `hm-menu-button` próprio, só com o ícone de grade, que abre a lista das
29
+ * aplicações num `hm-menu` dele (o mesmo componente — e o mesmo visual — do menu do perfil, mas um menu à parte;
30
+ * teclado, foco e `role="menu"` vêm do `hm-menu`). Escolher um item abre a aplicação em nova aba, sem `opener`.
31
31
  */
32
32
  class AppLauncherComponent {
33
33
  constructor(authService, realmAppsService) {
@@ -36,8 +36,8 @@ class AppLauncherComponent {
36
36
  this.text = inject(SPINE_MESSAGES).header;
37
37
  this.cdr = inject(ChangeDetectorRef);
38
38
  this.apps = [];
39
+ this.items = [];
39
40
  this.loading = false;
40
- this.open = false;
41
41
  }
42
42
  async ngOnInit() {
43
43
  this.loading = true;
@@ -47,7 +47,9 @@ class AppLauncherComponent {
47
47
  // (apps/argos/auth/server/.../openid.controller.ts), então não há
48
48
  // necessidade de repetir essa checagem no front.
49
49
  try {
50
- this.apps = (await this.realmAppsService.getApplications(token)) ?? [];
50
+ // Ordem alfabética pelo nome (pt-BR, sem diferenciar maiúsculas nem acentos).
51
+ this.apps = [...((await this.realmAppsService.getApplications(token)) ?? [])]
52
+ .sort((a, b) => a.name.localeCompare(b.name, 'pt-BR', { sensitivity: 'base' }));
51
53
  }
52
54
  catch {
53
55
  // Sem a lista não há o que oferecer: o botão some, em vez de ficar carregando para sempre.
@@ -55,22 +57,28 @@ class AppLauncherComponent {
55
57
  }
56
58
  finally {
57
59
  this.loading = false;
60
+ // O título "Aplicações" é o groupTitle do primeiro item (mesmo cabeçalho de grupo do hm-menu).
61
+ this.items = this.apps.map((app, i) => ({
62
+ id: app.id,
63
+ label: app.name,
64
+ icon: 'applications',
65
+ ...(i === 0 ? { groupTitle: this.text.apps } : {}),
66
+ }));
58
67
  // A lista chega fora de qualquer evento do template: sem isto, app zoneless não redesenha.
59
68
  this.cdr.markForCheck();
60
69
  }
61
70
  }
62
- toggle() {
63
- this.open = !this.open;
64
- }
65
- close() {
66
- this.open = false;
71
+ open(id) {
72
+ const app = this.apps.find((a) => a.id === id);
73
+ if (app)
74
+ window.open(app.app_url, '_blank', 'noopener,noreferrer');
67
75
  }
68
76
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.24", ngImport: i0, type: AppLauncherComponent, deps: [{ token: i1.AuthorizationService }, { token: i1.RealmAppsService }], target: i0.ɵɵFactoryTarget.Component }); }
69
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.24", type: AppLauncherComponent, isStandalone: true, selector: "spine-app-launcher", ngImport: i0, template: "<div class=\"app-launcher-trigger\" data-tour=\"apps\" *ngIf=\"loading || apps.length > 0\">\n <hm-icon-button emphasis=\"subtle\" icon=\"grid\" [label]=\"text.apps\" (clicked)=\"toggle()\"></hm-icon-button>\n\n <button type=\"button\" class=\"app-launcher-backdrop\" *ngIf=\"open\" (click)=\"close()\" aria-hidden=\"true\"></button>\n\n <div class=\"app-launcher-menu\" *ngIf=\"open\">\n <div class=\"app-launcher-header\">{{ text.apps }}</div>\n <div class=\"app-launcher-grid\">\n <div class=\"app-launcher-loading\" *ngIf=\"loading\">\n <hm-loading-square></hm-loading-square>\n </div>\n <a *ngFor=\"let app of apps\" [href]=\"app.app_url\" class=\"app-launcher-card\" target=\"_blank\"\n rel=\"noopener noreferrer\">\n <div class=\"app-launcher-icon\">\n <hm-icon shape=\"applications\" size=\"32\"></hm-icon>\n </div>\n <span class=\"app-launcher-name\">{{ app.name }}</span>\n </a>\n </div>\n </div>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:contents}.app-launcher-trigger{position:relative;display:flex;align-items:center;height:100%}.app-launcher-backdrop{position:fixed;inset:0;z-index:1199;border:none;background:transparent;cursor:default}.app-launcher-menu{position:absolute;top:calc(100% + 8px);left:0;z-index:1200;min-width:300px;background:var(--hm-surface);color:var(--hm-text);border:1px solid var(--hm-border);border-radius:12px;box-shadow:var(--hm-shadow-lg, 0 14px 38px rgba(0, 36, 65, .22));overflow:hidden}.app-launcher-header{padding:10px 16px 8px;font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;border-bottom:1px solid var(--hm-border)}.app-launcher-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:4px;padding:12px}.app-launcher-card{display:flex;flex-direction:column;align-items:center;justify-content:flex-start;padding:12px 8px;border-radius:4px;text-decoration:none;color:inherit;cursor:pointer;transition:background .15s ease}.app-launcher-card:hover{background:var(--hm-hover);text-decoration:none;color:inherit}.app-launcher-icon{margin-bottom:6px;color:var(--hm-action)}.app-launcher-name{font-size:11px;text-align:center;line-height:1.3;word-break:break-word;max-width:80px}.app-launcher-loading{grid-column:1/-1;display:flex;justify-content:center;padding:20px}@media(max-width:767px){.app-launcher-trigger{display:none}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: IconComponent, selector: "hm-icon", inputs: ["shape", "direction", "size", "solid", "badge", "status", "flip", "inverse", "label"] }, { kind: "component", type: IconButtonComponent, selector: "hm-icon-button", inputs: ["label", "icon", "solid", "iconDirection", "emphasis", "intent", "size", "disabled", "toggle", "pressed", "keyShortcuts"], outputs: ["pressedChange", "clicked"] }, { kind: "component", type: LoadingSquareComponent, selector: "hm-loading-square" }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
77
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.24", type: AppLauncherComponent, isStandalone: true, selector: "spine-app-launcher", ngImport: i0, template: "<div class=\"app-launcher-trigger\" data-tour=\"apps\" *ngIf=\"loading || apps.length > 0\">\n <hm-menu-button [label]=\"text.apps\" icon=\"grid\" [showArrow]=\"false\" [disabled]=\"loading\" [items]=\"items\"\n (chosen)=\"open($event)\"></hm-menu-button>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:contents}.app-launcher-trigger{display:flex;align-items:center;height:100%}@media(max-width:767px){.app-launcher-trigger{display:none}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: MenuButtonComponent, selector: "hm-menu-button", inputs: ["label", "icon", "items", "showArrow", "align", "side", "size", "shape", "layer", "closeOnCheck", "disabled"], outputs: ["chosen", "checkedChange"] }], changeDetection: i0.ChangeDetectionStrategy.Eager }); }
70
78
  }
71
79
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImport: i0, type: AppLauncherComponent, decorators: [{
72
80
  type: Component,
73
- args: [{ selector: 'spine-app-launcher', imports: [CommonModule, IconComponent, IconButtonComponent, LoadingSquareComponent], changeDetection: ChangeDetectionStrategy.Eager, template: "<div class=\"app-launcher-trigger\" data-tour=\"apps\" *ngIf=\"loading || apps.length > 0\">\n <hm-icon-button emphasis=\"subtle\" icon=\"grid\" [label]=\"text.apps\" (clicked)=\"toggle()\"></hm-icon-button>\n\n <button type=\"button\" class=\"app-launcher-backdrop\" *ngIf=\"open\" (click)=\"close()\" aria-hidden=\"true\"></button>\n\n <div class=\"app-launcher-menu\" *ngIf=\"open\">\n <div class=\"app-launcher-header\">{{ text.apps }}</div>\n <div class=\"app-launcher-grid\">\n <div class=\"app-launcher-loading\" *ngIf=\"loading\">\n <hm-loading-square></hm-loading-square>\n </div>\n <a *ngFor=\"let app of apps\" [href]=\"app.app_url\" class=\"app-launcher-card\" target=\"_blank\"\n rel=\"noopener noreferrer\">\n <div class=\"app-launcher-icon\">\n <hm-icon shape=\"applications\" size=\"32\"></hm-icon>\n </div>\n <span class=\"app-launcher-name\">{{ app.name }}</span>\n </a>\n </div>\n </div>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:contents}.app-launcher-trigger{position:relative;display:flex;align-items:center;height:100%}.app-launcher-backdrop{position:fixed;inset:0;z-index:1199;border:none;background:transparent;cursor:default}.app-launcher-menu{position:absolute;top:calc(100% + 8px);left:0;z-index:1200;min-width:300px;background:var(--hm-surface);color:var(--hm-text);border:1px solid var(--hm-border);border-radius:12px;box-shadow:var(--hm-shadow-lg, 0 14px 38px rgba(0, 36, 65, .22));overflow:hidden}.app-launcher-header{padding:10px 16px 8px;font-size:12px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;border-bottom:1px solid var(--hm-border)}.app-launcher-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:4px;padding:12px}.app-launcher-card{display:flex;flex-direction:column;align-items:center;justify-content:flex-start;padding:12px 8px;border-radius:4px;text-decoration:none;color:inherit;cursor:pointer;transition:background .15s ease}.app-launcher-card:hover{background:var(--hm-hover);text-decoration:none;color:inherit}.app-launcher-icon{margin-bottom:6px;color:var(--hm-action)}.app-launcher-name{font-size:11px;text-align:center;line-height:1.3;word-break:break-word;max-width:80px}.app-launcher-loading{grid-column:1/-1;display:flex;justify-content:center;padding:20px}@media(max-width:767px){.app-launcher-trigger{display:none}}\n"] }]
81
+ args: [{ selector: 'spine-app-launcher', imports: [CommonModule, MenuButtonComponent], changeDetection: ChangeDetectionStrategy.Eager, template: "<div class=\"app-launcher-trigger\" data-tour=\"apps\" *ngIf=\"loading || apps.length > 0\">\n <hm-menu-button [label]=\"text.apps\" icon=\"grid\" [showArrow]=\"false\" [disabled]=\"loading\" [items]=\"items\"\n (chosen)=\"open($event)\"></hm-menu-button>\n</div>\n", styles: ["@charset \"UTF-8\";:host{display:contents}.app-launcher-trigger{display:flex;align-items:center;height:100%}@media(max-width:767px){.app-launcher-trigger{display:none}}\n"] }]
74
82
  }], ctorParameters: () => [{ type: i1.AuthorizationService }, { type: i1.RealmAppsService }] });
75
83
 
76
84
  /**