@mohamedatia/fly-design-system 1.3.9 → 1.5.0
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.
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, signal, computed, Injectable, inject, ErrorHandler, Pipe } from '@angular/core';
|
|
2
|
+
import { InjectionToken, signal, computed, Injectable, inject, ErrorHandler, Pipe, DOCUMENT, ElementRef, input, output, HostListener, ViewChild, ChangeDetectionStrategy, Component, EventEmitter, DestroyRef, Output, Input } from '@angular/core';
|
|
3
3
|
import { Router } from '@angular/router';
|
|
4
4
|
import { of } from 'rxjs';
|
|
5
|
+
import { CommonModule } from '@angular/common';
|
|
6
|
+
import * as i1 from '@angular/forms';
|
|
7
|
+
import { FormsModule } from '@angular/forms';
|
|
8
|
+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
5
9
|
|
|
6
10
|
const WINDOW_DATA = new InjectionToken('WINDOW_DATA');
|
|
7
11
|
|
|
@@ -415,11 +419,468 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
415
419
|
type: Injectable
|
|
416
420
|
}], ctorParameters: () => [] });
|
|
417
421
|
|
|
422
|
+
class ContextMenuComponent {
|
|
423
|
+
menuEl;
|
|
424
|
+
doc = inject(DOCUMENT);
|
|
425
|
+
hostEl = inject((ElementRef));
|
|
426
|
+
x = input.required(...(ngDevMode ? [{ debugName: "x" }] : /* istanbul ignore next */ []));
|
|
427
|
+
y = input.required(...(ngDevMode ? [{ debugName: "y" }] : /* istanbul ignore next */ []));
|
|
428
|
+
sections = input.required(...(ngDevMode ? [{ debugName: "sections" }] : /* istanbul ignore next */ []));
|
|
429
|
+
action = output();
|
|
430
|
+
closed = output();
|
|
431
|
+
menuWidth = signal(0, ...(ngDevMode ? [{ debugName: "menuWidth" }] : /* istanbul ignore next */ []));
|
|
432
|
+
menuHeight = signal(0, ...(ngDevMode ? [{ debugName: "menuHeight" }] : /* istanbul ignore next */ []));
|
|
433
|
+
previouslyFocused = null;
|
|
434
|
+
// Resolved position: clamp to viewport so the menu never overflows an edge.
|
|
435
|
+
// menuWidth/Height start at 0 so the first render places the menu at (x,y)
|
|
436
|
+
// with no clamping; ngAfterViewInit measures the real size and updates the
|
|
437
|
+
// signals, which re-evaluates this computed and repositions correctly.
|
|
438
|
+
clampedPos = computed(() => {
|
|
439
|
+
const vw = this.doc.defaultView?.innerWidth ?? 0;
|
|
440
|
+
const vh = this.doc.defaultView?.innerHeight ?? 0;
|
|
441
|
+
const w = this.menuWidth();
|
|
442
|
+
const h = this.menuHeight();
|
|
443
|
+
const x = this.x();
|
|
444
|
+
const y = this.y();
|
|
445
|
+
return {
|
|
446
|
+
left: w > 0 ? Math.min(x, vw - w - 8) : x,
|
|
447
|
+
top: h > 0 ? Math.min(y, vh - h - 8) : y,
|
|
448
|
+
};
|
|
449
|
+
}, ...(ngDevMode ? [{ debugName: "clampedPos" }] : /* istanbul ignore next */ []));
|
|
450
|
+
ngAfterViewInit() {
|
|
451
|
+
// Portal: move host to <body> so position:fixed resolves against the true
|
|
452
|
+
// viewport, not against any transformed/backdrop-filtered ancestor window.
|
|
453
|
+
this.doc.body.appendChild(this.hostEl.nativeElement);
|
|
454
|
+
// Measure after portal move so offsetWidth/Height are accurate.
|
|
455
|
+
const el = this.menuEl?.nativeElement;
|
|
456
|
+
if (el) {
|
|
457
|
+
this.menuWidth.set(el.offsetWidth);
|
|
458
|
+
this.menuHeight.set(el.offsetHeight);
|
|
459
|
+
}
|
|
460
|
+
this.previouslyFocused = document.activeElement;
|
|
461
|
+
this.focusItem(0);
|
|
462
|
+
}
|
|
463
|
+
ngOnDestroy() {
|
|
464
|
+
const el = this.hostEl.nativeElement;
|
|
465
|
+
if (el.parentNode === this.doc.body) {
|
|
466
|
+
this.doc.body.removeChild(el);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
onAction(id) {
|
|
470
|
+
this.action.emit(id);
|
|
471
|
+
this.close();
|
|
472
|
+
}
|
|
473
|
+
onClickOutside(event) {
|
|
474
|
+
if (!this.menuEl?.nativeElement.contains(event.target)) {
|
|
475
|
+
this.close();
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
onEscape() {
|
|
479
|
+
this.close();
|
|
480
|
+
}
|
|
481
|
+
onContextMenu(event) {
|
|
482
|
+
if (!this.menuEl?.nativeElement.contains(event.target)) {
|
|
483
|
+
this.close();
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
onKeydown(event) {
|
|
487
|
+
const items = this.getMenuItems();
|
|
488
|
+
if (!items.length)
|
|
489
|
+
return;
|
|
490
|
+
const focused = document.activeElement;
|
|
491
|
+
const idx = items.indexOf(focused);
|
|
492
|
+
if (event.key === 'ArrowDown') {
|
|
493
|
+
event.preventDefault();
|
|
494
|
+
this.focusItem(idx < items.length - 1 ? idx + 1 : 0);
|
|
495
|
+
}
|
|
496
|
+
else if (event.key === 'ArrowUp') {
|
|
497
|
+
event.preventDefault();
|
|
498
|
+
this.focusItem(idx > 0 ? idx - 1 : items.length - 1);
|
|
499
|
+
}
|
|
500
|
+
else if (event.key === 'Home') {
|
|
501
|
+
event.preventDefault();
|
|
502
|
+
this.focusItem(0);
|
|
503
|
+
}
|
|
504
|
+
else if (event.key === 'End') {
|
|
505
|
+
event.preventDefault();
|
|
506
|
+
this.focusItem(items.length - 1);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
close() {
|
|
510
|
+
this.restoreFocus();
|
|
511
|
+
this.closed.emit();
|
|
512
|
+
}
|
|
513
|
+
getMenuItems() {
|
|
514
|
+
return Array.from(this.menuEl?.nativeElement.querySelectorAll('[role="menuitem"]') ?? []);
|
|
515
|
+
}
|
|
516
|
+
focusItem(index) {
|
|
517
|
+
const items = this.getMenuItems();
|
|
518
|
+
items[index]?.focus();
|
|
519
|
+
}
|
|
520
|
+
restoreFocus() {
|
|
521
|
+
if (this.previouslyFocused instanceof HTMLElement) {
|
|
522
|
+
this.previouslyFocused.focus();
|
|
523
|
+
}
|
|
524
|
+
this.previouslyFocused = null;
|
|
525
|
+
}
|
|
526
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: ContextMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
527
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: ContextMenuComponent, isStandalone: true, selector: "fly-context-menu", inputs: { x: { classPropertyName: "x", publicName: "x", isSignal: true, isRequired: true, transformFunction: null }, y: { classPropertyName: "y", publicName: "y", isSignal: true, isRequired: true, transformFunction: null }, sections: { classPropertyName: "sections", publicName: "sections", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { action: "action", closed: "closed" }, host: { listeners: { "document:mousedown": "onClickOutside($event)", "document:keydown.escape": "onEscape()", "document:contextmenu": "onContextMenu($event)", "keydown": "onKeydown($event)" } }, viewQueries: [{ propertyName: "menuEl", first: true, predicate: ["contextMenu"], descendants: true }], ngImport: i0, template: "<div\n #contextMenu\n class=\"context-menu\"\n [style.left.px]=\"clampedPos().left\"\n [style.top.px]=\"clampedPos().top\"\n role=\"menu\"\n [attr.aria-label]=\"'shell.context_menu' | translate\">\n\n @for (section of sections(); track $index) {\n @if ($index > 0) {\n <div class=\"menu-divider\"></div>\n }\n @if (section.label) {\n <div class=\"menu-section-label\">{{ section.label }}</div>\n }\n @for (item of section.items; track item.id) {\n <button\n type=\"button\"\n class=\"vos-btn sm rect menu-item\"\n role=\"menuitem\"\n (click)=\"onAction(item.id)\">\n <i [class]=\"'pi ' + item.icon\" aria-hidden=\"true\"></i>\n <span>{{ item.label }}</span>\n </button>\n }\n }\n</div>\n", styles: [":host{display:contents}.context-menu{position:fixed;min-width:200px;padding:6px;border-radius:14px;background:var(--glass-bg, rgba(30, 30, 34, .72));backdrop-filter:blur(40px) saturate(180%);-webkit-backdrop-filter:blur(40px) saturate(180%);border:1px solid var(--glass-border, rgba(255, 255, 255, .15));box-shadow:0 12px 40px #00000059,0 2px 8px #0003,inset 0 .5px #ffffff1a;animation:menuEnter .18s cubic-bezier(.22,1,.36,1) both;transform-origin:top left}@keyframes menuEnter{0%{opacity:0;transform:scale(.92)}to{opacity:1;transform:scale(1)}}.menu-section-label{padding:6px 12px 4px;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.6px;color:var(--text-color-secondary, rgba(255, 255, 255, .45));pointer-events:none}.menu-item{gap:10px;width:100%;padding:8px 12px;color:var(--text-color, #fff);font-size:var(--link-font-size, 13px);font-weight:var(--btn-symbol-font-weight, 510);justify-content:flex-start}.menu-item i{font-size:14px;width:18px;text-align:center;opacity:.7}.menu-item:hover{background:var(--primary-color, #E8732A);color:#fff}.menu-item:hover i{opacity:1}.menu-item:focus-visible{background:var(--primary-color, #E8732A);outline:2px solid var(--focus-ring);outline-offset:-2px}.menu-divider{height:1px;margin:4px 8px;background:var(--glass-border, rgba(255, 255, 255, .12))}\n"], dependencies: [{ kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
528
|
+
}
|
|
529
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: ContextMenuComponent, decorators: [{
|
|
530
|
+
type: Component,
|
|
531
|
+
args: [{ selector: 'fly-context-menu', standalone: true, imports: [TranslatePipe], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div\n #contextMenu\n class=\"context-menu\"\n [style.left.px]=\"clampedPos().left\"\n [style.top.px]=\"clampedPos().top\"\n role=\"menu\"\n [attr.aria-label]=\"'shell.context_menu' | translate\">\n\n @for (section of sections(); track $index) {\n @if ($index > 0) {\n <div class=\"menu-divider\"></div>\n }\n @if (section.label) {\n <div class=\"menu-section-label\">{{ section.label }}</div>\n }\n @for (item of section.items; track item.id) {\n <button\n type=\"button\"\n class=\"vos-btn sm rect menu-item\"\n role=\"menuitem\"\n (click)=\"onAction(item.id)\">\n <i [class]=\"'pi ' + item.icon\" aria-hidden=\"true\"></i>\n <span>{{ item.label }}</span>\n </button>\n }\n }\n</div>\n", styles: [":host{display:contents}.context-menu{position:fixed;min-width:200px;padding:6px;border-radius:14px;background:var(--glass-bg, rgba(30, 30, 34, .72));backdrop-filter:blur(40px) saturate(180%);-webkit-backdrop-filter:blur(40px) saturate(180%);border:1px solid var(--glass-border, rgba(255, 255, 255, .15));box-shadow:0 12px 40px #00000059,0 2px 8px #0003,inset 0 .5px #ffffff1a;animation:menuEnter .18s cubic-bezier(.22,1,.36,1) both;transform-origin:top left}@keyframes menuEnter{0%{opacity:0;transform:scale(.92)}to{opacity:1;transform:scale(1)}}.menu-section-label{padding:6px 12px 4px;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.6px;color:var(--text-color-secondary, rgba(255, 255, 255, .45));pointer-events:none}.menu-item{gap:10px;width:100%;padding:8px 12px;color:var(--text-color, #fff);font-size:var(--link-font-size, 13px);font-weight:var(--btn-symbol-font-weight, 510);justify-content:flex-start}.menu-item i{font-size:14px;width:18px;text-align:center;opacity:.7}.menu-item:hover{background:var(--primary-color, #E8732A);color:#fff}.menu-item:hover i{opacity:1}.menu-item:focus-visible{background:var(--primary-color, #E8732A);outline:2px solid var(--focus-ring);outline-offset:-2px}.menu-divider{height:1px;margin:4px 8px;background:var(--glass-border, rgba(255, 255, 255, .12))}\n"] }]
|
|
532
|
+
}], propDecorators: { menuEl: [{
|
|
533
|
+
type: ViewChild,
|
|
534
|
+
args: ['contextMenu']
|
|
535
|
+
}], x: [{ type: i0.Input, args: [{ isSignal: true, alias: "x", required: true }] }], y: [{ type: i0.Input, args: [{ isSignal: true, alias: "y", required: true }] }], sections: [{ type: i0.Input, args: [{ isSignal: true, alias: "sections", required: true }] }], action: [{ type: i0.Output, args: ["action"] }], closed: [{ type: i0.Output, args: ["closed"] }], onClickOutside: [{
|
|
536
|
+
type: HostListener,
|
|
537
|
+
args: ['document:mousedown', ['$event']]
|
|
538
|
+
}], onEscape: [{
|
|
539
|
+
type: HostListener,
|
|
540
|
+
args: ['document:keydown.escape']
|
|
541
|
+
}], onContextMenu: [{
|
|
542
|
+
type: HostListener,
|
|
543
|
+
args: ['document:contextmenu', ['$event']]
|
|
544
|
+
}], onKeydown: [{
|
|
545
|
+
type: HostListener,
|
|
546
|
+
args: ['keydown', ['$event']]
|
|
547
|
+
}] } });
|
|
548
|
+
|
|
549
|
+
var MessageBoxButtons;
|
|
550
|
+
(function (MessageBoxButtons) {
|
|
551
|
+
MessageBoxButtons[MessageBoxButtons["OK"] = 0] = "OK";
|
|
552
|
+
MessageBoxButtons[MessageBoxButtons["OKCancel"] = 1] = "OKCancel";
|
|
553
|
+
MessageBoxButtons[MessageBoxButtons["YesNo"] = 2] = "YesNo";
|
|
554
|
+
MessageBoxButtons[MessageBoxButtons["YesNoCancel"] = 3] = "YesNoCancel";
|
|
555
|
+
MessageBoxButtons[MessageBoxButtons["RetryCancel"] = 4] = "RetryCancel";
|
|
556
|
+
MessageBoxButtons[MessageBoxButtons["AbortRetryIgnore"] = 5] = "AbortRetryIgnore";
|
|
557
|
+
})(MessageBoxButtons || (MessageBoxButtons = {}));
|
|
558
|
+
var MessageBoxIcon;
|
|
559
|
+
(function (MessageBoxIcon) {
|
|
560
|
+
MessageBoxIcon[MessageBoxIcon["None"] = 0] = "None";
|
|
561
|
+
MessageBoxIcon[MessageBoxIcon["Information"] = 1] = "Information";
|
|
562
|
+
MessageBoxIcon[MessageBoxIcon["Warning"] = 2] = "Warning";
|
|
563
|
+
MessageBoxIcon[MessageBoxIcon["Error"] = 3] = "Error";
|
|
564
|
+
MessageBoxIcon[MessageBoxIcon["Question"] = 4] = "Question";
|
|
565
|
+
})(MessageBoxIcon || (MessageBoxIcon = {}));
|
|
566
|
+
var DialogResult;
|
|
567
|
+
(function (DialogResult) {
|
|
568
|
+
DialogResult[DialogResult["None"] = 0] = "None";
|
|
569
|
+
DialogResult[DialogResult["OK"] = 1] = "OK";
|
|
570
|
+
DialogResult[DialogResult["Cancel"] = 2] = "Cancel";
|
|
571
|
+
DialogResult[DialogResult["Yes"] = 3] = "Yes";
|
|
572
|
+
DialogResult[DialogResult["No"] = 4] = "No";
|
|
573
|
+
DialogResult[DialogResult["Retry"] = 5] = "Retry";
|
|
574
|
+
DialogResult[DialogResult["Abort"] = 6] = "Abort";
|
|
575
|
+
DialogResult[DialogResult["Ignore"] = 7] = "Ignore";
|
|
576
|
+
})(DialogResult || (DialogResult = {}));
|
|
577
|
+
class MessageBoxService {
|
|
578
|
+
i18n = inject(I18nService);
|
|
579
|
+
visible = signal(false, ...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
|
|
580
|
+
title = signal('', ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
|
|
581
|
+
message = signal('', ...(ngDevMode ? [{ debugName: "message" }] : /* istanbul ignore next */ []));
|
|
582
|
+
icon = signal(MessageBoxIcon.None, ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
|
|
583
|
+
buttons = signal([], ...(ngDevMode ? [{ debugName: "buttons" }] : /* istanbul ignore next */ []));
|
|
584
|
+
resolver = null;
|
|
585
|
+
show(options) {
|
|
586
|
+
// Dismiss any pending dialog so its Promise resolves rather than leaking
|
|
587
|
+
if (this.resolver) {
|
|
588
|
+
this.resolver(DialogResult.None);
|
|
589
|
+
this.resolver = null;
|
|
590
|
+
}
|
|
591
|
+
this.title.set(options.title);
|
|
592
|
+
this.message.set(options.message);
|
|
593
|
+
this.icon.set(options.icon ?? MessageBoxIcon.None);
|
|
594
|
+
this.buttons.set(this.resolveButtons(options.buttons ?? MessageBoxButtons.OK));
|
|
595
|
+
this.visible.set(true);
|
|
596
|
+
return new Promise((resolve) => {
|
|
597
|
+
this.resolver = resolve;
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
resolve(result) {
|
|
601
|
+
this.visible.set(false);
|
|
602
|
+
if (this.resolver) {
|
|
603
|
+
this.resolver(result);
|
|
604
|
+
this.resolver = null;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
resolveButtons(buttons) {
|
|
608
|
+
const t = (key) => this.i18n.t(key);
|
|
609
|
+
switch (buttons) {
|
|
610
|
+
case MessageBoxButtons.OK:
|
|
611
|
+
return [{ label: t('common.ok'), result: DialogResult.OK, variant: 'primary' }];
|
|
612
|
+
case MessageBoxButtons.OKCancel:
|
|
613
|
+
return [
|
|
614
|
+
{ label: t('common.cancel'), result: DialogResult.Cancel, variant: 'default' },
|
|
615
|
+
{ label: t('common.ok'), result: DialogResult.OK, variant: 'primary' },
|
|
616
|
+
];
|
|
617
|
+
case MessageBoxButtons.YesNo:
|
|
618
|
+
return [
|
|
619
|
+
{ label: t('common.no'), result: DialogResult.No, variant: 'default' },
|
|
620
|
+
{ label: t('common.yes'), result: DialogResult.Yes, variant: 'primary' },
|
|
621
|
+
];
|
|
622
|
+
case MessageBoxButtons.YesNoCancel:
|
|
623
|
+
return [
|
|
624
|
+
{ label: t('common.cancel'), result: DialogResult.Cancel, variant: 'default' },
|
|
625
|
+
{ label: t('common.no'), result: DialogResult.No, variant: 'default' },
|
|
626
|
+
{ label: t('common.yes'), result: DialogResult.Yes, variant: 'primary' },
|
|
627
|
+
];
|
|
628
|
+
case MessageBoxButtons.RetryCancel:
|
|
629
|
+
return [
|
|
630
|
+
{ label: t('common.cancel'), result: DialogResult.Cancel, variant: 'default' },
|
|
631
|
+
{ label: t('common.retry'), result: DialogResult.Retry, variant: 'primary' },
|
|
632
|
+
];
|
|
633
|
+
case MessageBoxButtons.AbortRetryIgnore:
|
|
634
|
+
return [
|
|
635
|
+
{ label: t('common.abort'), result: DialogResult.Abort, variant: 'danger' },
|
|
636
|
+
{ label: t('common.retry'), result: DialogResult.Retry, variant: 'default' },
|
|
637
|
+
{ label: t('common.ignore'), result: DialogResult.Ignore, variant: 'default' },
|
|
638
|
+
];
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: MessageBoxService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
642
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: MessageBoxService, providedIn: 'root' });
|
|
643
|
+
}
|
|
644
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: MessageBoxService, decorators: [{
|
|
645
|
+
type: Injectable,
|
|
646
|
+
args: [{ providedIn: 'root' }]
|
|
647
|
+
}] });
|
|
648
|
+
|
|
649
|
+
class MessageBoxComponent {
|
|
650
|
+
service = inject(MessageBoxService);
|
|
651
|
+
elRef = inject(ElementRef);
|
|
652
|
+
MessageBoxIcon = MessageBoxIcon;
|
|
653
|
+
DialogResult = DialogResult;
|
|
654
|
+
previouslyFocused = null;
|
|
655
|
+
ngAfterViewInit() {
|
|
656
|
+
// Store focus origin so we can restore it on close
|
|
657
|
+
this.previouslyFocused = document.activeElement;
|
|
658
|
+
this.focusPrimaryButton();
|
|
659
|
+
}
|
|
660
|
+
onEscape() {
|
|
661
|
+
if (this.service.visible()) {
|
|
662
|
+
this.service.resolve(DialogResult.Cancel);
|
|
663
|
+
this.restoreFocus();
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
onBackdropClick() {
|
|
667
|
+
this.service.resolve(DialogResult.Cancel);
|
|
668
|
+
this.restoreFocus();
|
|
669
|
+
}
|
|
670
|
+
onButtonClick(result) {
|
|
671
|
+
this.service.resolve(result);
|
|
672
|
+
this.restoreFocus();
|
|
673
|
+
}
|
|
674
|
+
iconClass() {
|
|
675
|
+
switch (this.service.icon()) {
|
|
676
|
+
case MessageBoxIcon.Information: return 'pi pi-info-circle';
|
|
677
|
+
case MessageBoxIcon.Warning: return 'pi pi-exclamation-triangle';
|
|
678
|
+
case MessageBoxIcon.Error: return 'pi pi-times-circle';
|
|
679
|
+
case MessageBoxIcon.Question: return 'pi pi-question-circle';
|
|
680
|
+
default: return '';
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
focusPrimaryButton() {
|
|
684
|
+
// Focus the primary (last) button, which is the default action
|
|
685
|
+
const buttons = this.elRef.nativeElement.querySelectorAll('.mb-btn');
|
|
686
|
+
const last = buttons[buttons.length - 1];
|
|
687
|
+
last?.focus();
|
|
688
|
+
}
|
|
689
|
+
restoreFocus() {
|
|
690
|
+
if (this.previouslyFocused instanceof HTMLElement) {
|
|
691
|
+
this.previouslyFocused.focus();
|
|
692
|
+
}
|
|
693
|
+
this.previouslyFocused = null;
|
|
694
|
+
}
|
|
695
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: MessageBoxComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
696
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: MessageBoxComponent, isStandalone: true, selector: "fly-message-box", host: { listeners: { "document:keydown.escape": "onEscape()" } }, ngImport: i0, template: "@if (service.visible()) {\n <div\n class=\"mb-backdrop\"\n (click)=\"onBackdropClick()\"\n role=\"presentation\">\n <div\n class=\"mb-dialog\"\n [class.mb-info]=\"service.icon() === MessageBoxIcon.Information\"\n [class.mb-warning]=\"service.icon() === MessageBoxIcon.Warning\"\n [class.mb-danger]=\"service.icon() === MessageBoxIcon.Error\"\n [class.mb-question]=\"service.icon() === MessageBoxIcon.Question\"\n (click)=\"$event.stopPropagation()\"\n role=\"alertdialog\"\n aria-modal=\"true\"\n aria-labelledby=\"mb-title\"\n aria-describedby=\"mb-message\">\n\n @if (iconClass()) {\n <div class=\"mb-icon-wrap\">\n <i [class]=\"iconClass() + ' mb-icon'\" aria-hidden=\"true\"></i>\n </div>\n }\n\n <div class=\"mb-title\" id=\"mb-title\">{{ service.title() }}</div>\n <div class=\"mb-message\" id=\"mb-message\">{{ service.message() }}</div>\n\n <div class=\"mb-actions\">\n @for (btn of service.buttons(); track btn.result) {\n <button\n type=\"button\"\n class=\"vos-btn sm mb-btn\"\n [class.mb-btn--primary]=\"btn.variant === 'primary'\"\n [class.mb-btn--danger]=\"btn.variant === 'danger'\"\n (click)=\"onButtonClick(btn.result)\">\n {{ btn.label }}\n </button>\n }\n </div>\n\n </div>\n </div>\n}\n", styles: [".mb-backdrop{position:absolute;inset:0;z-index:5100;background:#00000073;display:flex;align-items:center;justify-content:center;-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);animation:mbFadeIn .15s ease both}@keyframes mbFadeIn{0%{opacity:0}to{opacity:1}}.mb-dialog{background:var(--surface-card, rgba(30, 30, 30, .98));border:1px solid var(--surface-border);border-radius:16px;padding:28px 28px 20px;width:400px;max-width:90%;display:flex;flex-direction:column;align-items:center;text-align:center;gap:6px;box-shadow:0 20px 60px #0006;animation:mbScaleIn .2s cubic-bezier(.22,1,.36,1) both}@keyframes mbScaleIn{0%{opacity:0;transform:scale(.92)}to{opacity:1;transform:scale(1)}}.mb-icon-wrap{width:52px;height:52px;border-radius:50%;display:flex;align-items:center;justify-content:center;margin-bottom:6px}.mb-icon{font-size:24px}.mb-info .mb-icon-wrap{background:#3b82f61f}.mb-info .mb-icon{color:#3b82f6}.mb-warning .mb-icon-wrap{background:#f59e0b1f}.mb-warning .mb-icon{color:#f59e0b}.mb-danger .mb-icon-wrap{background:#ef44441f}.mb-danger .mb-icon{color:#ef4444}.mb-question .mb-icon-wrap{background:#8b5cf61f}.mb-question .mb-icon{color:#8b5cf6}.mb-title{font-size:15px;font-weight:700;color:var(--text-color)}.mb-message{font-size:13px;color:var(--text-color-secondary);line-height:1.55;max-width:340px;white-space:pre-line}.mb-actions{display:flex;gap:10px;margin-top:16px;width:100%;justify-content:center;flex-wrap:wrap}.mb-btn{min-width:90px}.mb-btn--primary{background:var(--primary-color, #E8732A);color:#fff}.mb-btn--primary:hover{filter:brightness(1.1)}.mb-btn--danger{background:#ef4444;color:#fff}.mb-btn--danger:hover{background:#dc2626}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
697
|
+
}
|
|
698
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: MessageBoxComponent, decorators: [{
|
|
699
|
+
type: Component,
|
|
700
|
+
args: [{ selector: 'fly-message-box', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (service.visible()) {\n <div\n class=\"mb-backdrop\"\n (click)=\"onBackdropClick()\"\n role=\"presentation\">\n <div\n class=\"mb-dialog\"\n [class.mb-info]=\"service.icon() === MessageBoxIcon.Information\"\n [class.mb-warning]=\"service.icon() === MessageBoxIcon.Warning\"\n [class.mb-danger]=\"service.icon() === MessageBoxIcon.Error\"\n [class.mb-question]=\"service.icon() === MessageBoxIcon.Question\"\n (click)=\"$event.stopPropagation()\"\n role=\"alertdialog\"\n aria-modal=\"true\"\n aria-labelledby=\"mb-title\"\n aria-describedby=\"mb-message\">\n\n @if (iconClass()) {\n <div class=\"mb-icon-wrap\">\n <i [class]=\"iconClass() + ' mb-icon'\" aria-hidden=\"true\"></i>\n </div>\n }\n\n <div class=\"mb-title\" id=\"mb-title\">{{ service.title() }}</div>\n <div class=\"mb-message\" id=\"mb-message\">{{ service.message() }}</div>\n\n <div class=\"mb-actions\">\n @for (btn of service.buttons(); track btn.result) {\n <button\n type=\"button\"\n class=\"vos-btn sm mb-btn\"\n [class.mb-btn--primary]=\"btn.variant === 'primary'\"\n [class.mb-btn--danger]=\"btn.variant === 'danger'\"\n (click)=\"onButtonClick(btn.result)\">\n {{ btn.label }}\n </button>\n }\n </div>\n\n </div>\n </div>\n}\n", styles: [".mb-backdrop{position:absolute;inset:0;z-index:5100;background:#00000073;display:flex;align-items:center;justify-content:center;-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);animation:mbFadeIn .15s ease both}@keyframes mbFadeIn{0%{opacity:0}to{opacity:1}}.mb-dialog{background:var(--surface-card, rgba(30, 30, 30, .98));border:1px solid var(--surface-border);border-radius:16px;padding:28px 28px 20px;width:400px;max-width:90%;display:flex;flex-direction:column;align-items:center;text-align:center;gap:6px;box-shadow:0 20px 60px #0006;animation:mbScaleIn .2s cubic-bezier(.22,1,.36,1) both}@keyframes mbScaleIn{0%{opacity:0;transform:scale(.92)}to{opacity:1;transform:scale(1)}}.mb-icon-wrap{width:52px;height:52px;border-radius:50%;display:flex;align-items:center;justify-content:center;margin-bottom:6px}.mb-icon{font-size:24px}.mb-info .mb-icon-wrap{background:#3b82f61f}.mb-info .mb-icon{color:#3b82f6}.mb-warning .mb-icon-wrap{background:#f59e0b1f}.mb-warning .mb-icon{color:#f59e0b}.mb-danger .mb-icon-wrap{background:#ef44441f}.mb-danger .mb-icon{color:#ef4444}.mb-question .mb-icon-wrap{background:#8b5cf61f}.mb-question .mb-icon{color:#8b5cf6}.mb-title{font-size:15px;font-weight:700;color:var(--text-color)}.mb-message{font-size:13px;color:var(--text-color-secondary);line-height:1.55;max-width:340px;white-space:pre-line}.mb-actions{display:flex;gap:10px;margin-top:16px;width:100%;justify-content:center;flex-wrap:wrap}.mb-btn{min-width:90px}.mb-btn--primary{background:var(--primary-color, #E8732A);color:#fff}.mb-btn--primary:hover{filter:brightness(1.1)}.mb-btn--danger{background:#ef4444;color:#fff}.mb-btn--danger:hover{background:#dc2626}\n"] }]
|
|
701
|
+
}], propDecorators: { onEscape: [{
|
|
702
|
+
type: HostListener,
|
|
703
|
+
args: ['document:keydown.escape']
|
|
704
|
+
}] } });
|
|
705
|
+
|
|
706
|
+
/** Default permission levels (file-style ACL). Hosts can override via `permissionLevels`. */
|
|
707
|
+
const SHARE_PANEL_DEFAULT_FILE_LEVELS = [
|
|
708
|
+
{ value: 'View', labelKey: 'files.share.level_view' },
|
|
709
|
+
{ value: 'Download', labelKey: 'files.share.level_download' },
|
|
710
|
+
{ value: 'Edit', labelKey: 'files.share.level_edit' },
|
|
711
|
+
{ value: 'FullControl', labelKey: 'files.share.level_full' },
|
|
712
|
+
];
|
|
713
|
+
class SharePanelComponent {
|
|
714
|
+
/** Resource title shown in the header */
|
|
715
|
+
targetName = '';
|
|
716
|
+
/** i18n key for panel title */
|
|
717
|
+
titleKey = 'files.share.title';
|
|
718
|
+
/** Load current shares / permissions (host resolves display names when possible). */
|
|
719
|
+
loadPermissions;
|
|
720
|
+
searchUsers;
|
|
721
|
+
loadOuTree;
|
|
722
|
+
grantToUser;
|
|
723
|
+
grantToOu;
|
|
724
|
+
updatePermission;
|
|
725
|
+
revokePermission;
|
|
726
|
+
/** When true, show “apply to children” (e.g. folders). */
|
|
727
|
+
showApplyToChildren = false;
|
|
728
|
+
/** Override level dropdown options (e.g. notes: View / Edit only). */
|
|
729
|
+
permissionLevels;
|
|
730
|
+
/** i18n key for wildcard app grant label */
|
|
731
|
+
everyoneLabelKey = 'files.share.everyone';
|
|
732
|
+
close = new EventEmitter();
|
|
733
|
+
destroyRef = inject(DestroyRef);
|
|
734
|
+
i18n = inject(I18nService);
|
|
735
|
+
permissions = signal([], ...(ngDevMode ? [{ debugName: "permissions" }] : /* istanbul ignore next */ []));
|
|
736
|
+
loading = signal(true, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
|
|
737
|
+
searchQuery = '';
|
|
738
|
+
newLevel = '';
|
|
739
|
+
applyToChildren = false;
|
|
740
|
+
searchResults = signal([], ...(ngDevMode ? [{ debugName: "searchResults" }] : /* istanbul ignore next */ []));
|
|
741
|
+
ouTree = signal([], ...(ngDevMode ? [{ debugName: "ouTree" }] : /* istanbul ignore next */ []));
|
|
742
|
+
showOuPicker = signal(false, ...(ngDevMode ? [{ debugName: "showOuPicker" }] : /* istanbul ignore next */ []));
|
|
743
|
+
searchTimeout = null;
|
|
744
|
+
ngOnInit() {
|
|
745
|
+
const levels = this.levelOptions;
|
|
746
|
+
this.newLevel = levels[0]?.value ?? 'View';
|
|
747
|
+
this.refreshPermissions();
|
|
748
|
+
this.loadOuTree()
|
|
749
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
750
|
+
.subscribe({
|
|
751
|
+
next: (tree) => this.ouTree.set(tree),
|
|
752
|
+
error: () => this.ouTree.set([]),
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
get levelOptions() {
|
|
756
|
+
const p = this.permissionLevels;
|
|
757
|
+
return p && p.length > 0 ? p : SHARE_PANEL_DEFAULT_FILE_LEVELS;
|
|
758
|
+
}
|
|
759
|
+
refreshPermissions() {
|
|
760
|
+
this.loading.set(true);
|
|
761
|
+
this.loadPermissions()
|
|
762
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
763
|
+
.subscribe({
|
|
764
|
+
next: (perms) => {
|
|
765
|
+
this.permissions.set(perms);
|
|
766
|
+
this.loading.set(false);
|
|
767
|
+
},
|
|
768
|
+
error: () => {
|
|
769
|
+
this.permissions.set([]);
|
|
770
|
+
this.loading.set(false);
|
|
771
|
+
},
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
onSearchInput() {
|
|
775
|
+
if (this.searchTimeout)
|
|
776
|
+
clearTimeout(this.searchTimeout);
|
|
777
|
+
const query = this.searchQuery.trim();
|
|
778
|
+
if (query.length < 2) {
|
|
779
|
+
this.searchResults.set([]);
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
this.searchTimeout = setTimeout(() => {
|
|
783
|
+
this.searchUsers(query)
|
|
784
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
785
|
+
.subscribe({
|
|
786
|
+
next: (items) => this.searchResults.set(items),
|
|
787
|
+
error: () => this.searchResults.set([]),
|
|
788
|
+
});
|
|
789
|
+
}, 300);
|
|
790
|
+
}
|
|
791
|
+
onGrantToUser(user) {
|
|
792
|
+
this.searchResults.set([]);
|
|
793
|
+
this.searchQuery = '';
|
|
794
|
+
this.grantToUser(user.id, this.newLevel, this.applyToChildren)
|
|
795
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
796
|
+
.subscribe({ next: () => this.refreshPermissions() });
|
|
797
|
+
}
|
|
798
|
+
onGrantToOu(ou) {
|
|
799
|
+
this.grantToOu(ou.id, this.newLevel, this.applyToChildren)
|
|
800
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
801
|
+
.subscribe({ next: () => this.refreshPermissions() });
|
|
802
|
+
}
|
|
803
|
+
onUpdateLevel(perm, level) {
|
|
804
|
+
this.updatePermission(perm.id, level)
|
|
805
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
806
|
+
.subscribe({ next: () => this.refreshPermissions() });
|
|
807
|
+
}
|
|
808
|
+
onRevokePermission(perm) {
|
|
809
|
+
this.revokePermission(perm.id)
|
|
810
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
811
|
+
.subscribe({ next: () => this.refreshPermissions() });
|
|
812
|
+
}
|
|
813
|
+
getPermIcon(perm) {
|
|
814
|
+
if (perm.grantedToUserId)
|
|
815
|
+
return 'pi pi-user';
|
|
816
|
+
if (perm.grantedToOuId)
|
|
817
|
+
return 'pi pi-sitemap';
|
|
818
|
+
if (perm.grantedToAppId)
|
|
819
|
+
return 'pi pi-globe';
|
|
820
|
+
return 'pi pi-question';
|
|
821
|
+
}
|
|
822
|
+
getPermLabel(perm) {
|
|
823
|
+
if (perm.displayName?.trim())
|
|
824
|
+
return perm.displayName.trim();
|
|
825
|
+
if (perm.grantedToUserId) {
|
|
826
|
+
return `${perm.grantedToUserId.substring(0, 8)}…`;
|
|
827
|
+
}
|
|
828
|
+
if (perm.grantedToOuId) {
|
|
829
|
+
const ou = this.ouTree().find((o) => o.id === perm.grantedToOuId);
|
|
830
|
+
if (ou)
|
|
831
|
+
return ou.displayName;
|
|
832
|
+
return `${perm.grantedToOuId.substring(0, 8)}…`;
|
|
833
|
+
}
|
|
834
|
+
if (perm.grantedToAppId === '*')
|
|
835
|
+
return this.i18n.t(this.everyoneLabelKey);
|
|
836
|
+
return perm.grantedToAppId ?? '';
|
|
837
|
+
}
|
|
838
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SharePanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
839
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.5", type: SharePanelComponent, isStandalone: true, selector: "fly-share-panel", inputs: { targetName: "targetName", titleKey: "titleKey", loadPermissions: "loadPermissions", searchUsers: "searchUsers", loadOuTree: "loadOuTree", grantToUser: "grantToUser", grantToOu: "grantToOu", updatePermission: "updatePermission", revokePermission: "revokePermission", showApplyToChildren: "showApplyToChildren", permissionLevels: "permissionLevels", everyoneLabelKey: "everyoneLabelKey" }, outputs: { close: "close" }, ngImport: i0, template: "<div class=\"fac-overlay\" (click)=\"close.emit()\">\n <div class=\"fac-panel\" (click)=\"$event.stopPropagation()\">\n <div class=\"fac-header\">\n <h3>{{ titleKey | translate }}</h3>\n <span class=\"fac-target-name\">{{ targetName }}</span>\n <button type=\"button\" class=\"fac-close\" (click)=\"close.emit()\">\n <i class=\"pi pi-times\" aria-hidden=\"true\"></i>\n </button>\n </div>\n\n <div class=\"fac-add-section\">\n <div class=\"fac-search-row\">\n <input\n type=\"text\"\n class=\"fac-input\"\n [(ngModel)]=\"searchQuery\"\n (input)=\"onSearchInput()\"\n [placeholder]=\"'files.share.search_placeholder' | translate\"\n />\n <select class=\"fac-select\" [(ngModel)]=\"newLevel\">\n @for (opt of levelOptions; track opt.value) {\n <option [value]=\"opt.value\">{{ opt.labelKey | translate }}</option>\n }\n </select>\n </div>\n\n @if (searchResults().length > 0) {\n <div class=\"fac-search-results\">\n @for (result of searchResults(); track result.id) {\n <div class=\"fac-search-item\" (click)=\"onGrantToUser(result)\">\n <i class=\"pi pi-user\" aria-hidden=\"true\"></i>\n <span>{{ result.firstName }} {{ result.lastName }}</span>\n <span class=\"fac-email\">{{ result.email }}</span>\n </div>\n }\n </div>\n }\n\n @if (showOuPicker()) {\n <div class=\"fac-ou-section\">\n <div class=\"fac-section-label\">{{ 'files.share.share_with_ou' | translate }}</div>\n @for (ou of ouTree(); track ou.id) {\n <div class=\"fac-ou-item\" (click)=\"onGrantToOu(ou)\">\n <i class=\"pi pi-sitemap\" aria-hidden=\"true\"></i>\n <span>{{ ou.displayName }}</span>\n </div>\n }\n </div>\n }\n\n <div class=\"fac-toggle-row\">\n <button type=\"button\" class=\"fac-link\" (click)=\"showOuPicker.set(!showOuPicker())\">\n {{ showOuPicker() ? ('files.share.hide_ous' | translate) : ('files.share.show_ous' | translate) }}\n </button>\n @if (showApplyToChildren) {\n <label class=\"fac-checkbox-label\">\n <input type=\"checkbox\" [(ngModel)]=\"applyToChildren\" />\n {{ 'files.share.apply_children' | translate }}\n </label>\n }\n </div>\n </div>\n\n <div class=\"fac-perms-section\">\n <div class=\"fac-section-label\">{{ 'files.share.current_permissions' | translate }}</div>\n @if (loading()) {\n <div class=\"fac-loading\"><i class=\"pi pi-spin pi-spinner\" aria-hidden=\"true\"></i></div>\n } @else if (permissions().length === 0) {\n <div class=\"fac-empty\">{{ 'files.share.no_permissions' | translate }}</div>\n } @else {\n @for (perm of permissions(); track perm.id) {\n <div class=\"fac-perm-row\">\n <div class=\"fac-perm-info\">\n <i [class]=\"getPermIcon(perm)\" aria-hidden=\"true\"></i>\n <span class=\"fac-perm-name\">{{ getPermLabel(perm) }}</span>\n @if (perm.isInherited) {\n <span class=\"fac-badge inherited\">{{ 'files.share.inherited' | translate }}</span>\n }\n </div>\n <div class=\"fac-perm-actions\">\n <select\n class=\"fac-select sm\"\n [ngModel]=\"perm.level\"\n (ngModelChange)=\"onUpdateLevel(perm, $event)\"\n [disabled]=\"perm.isInherited === true\"\n >\n @for (opt of levelOptions; track opt.value) {\n <option [value]=\"opt.value\">{{ opt.labelKey | translate }}</option>\n }\n </select>\n <button\n type=\"button\"\n class=\"fac-icon-btn danger\"\n (click)=\"onRevokePermission(perm)\"\n [disabled]=\"perm.isInherited === true\"\n [title]=\"'files.share.revoke' | translate\"\n >\n <i class=\"pi pi-trash\" aria-hidden=\"true\"></i>\n </button>\n </div>\n </div>\n }\n }\n </div>\n </div>\n</div>\n", styles: [".fac-overlay{position:fixed;inset:0;background:#00000080;display:flex;align-items:center;justify-content:center;z-index:2000;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}.fac-panel{background:var(--surface-card, #1e1e1e);border:1px solid var(--surface-border);border-radius:12px;width:480px;max-height:80vh;display:flex;flex-direction:column;overflow:hidden;box-shadow:0 16px 48px #0006}.fac-header{display:flex;align-items:center;gap:10px;padding:16px 20px;border-bottom:1px solid var(--surface-border)}.fac-header h3{margin:0;font-size:16px;font-weight:600}.fac-target-name{flex:1;font-size:13px;color:var(--text-color-secondary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fac-close{background:none;border:none;color:var(--text-color-secondary);cursor:pointer;font-size:16px;padding:4px}.fac-close:hover{color:var(--text-color)}.fac-add-section{padding:16px 20px;border-bottom:1px solid var(--surface-border)}.fac-search-row{display:flex;gap:8px}.fac-input{flex:1;background:#ffffff14;border:1px solid rgba(255,255,255,.15);border-radius:8px;padding:8px 12px;color:inherit;font-size:13px;outline:none}.fac-input:focus{border-color:var(--primary-color, #e8732a)}.fac-select{background:#ffffff14;border:1px solid rgba(255,255,255,.15);border-radius:8px;padding:8px 10px;color:inherit;font-size:13px;outline:none}.fac-select.sm{padding:4px 8px;font-size:12px}.fac-search-results{margin-top:8px;max-height:150px;overflow-y:auto;border:1px solid var(--surface-border);border-radius:8px}.fac-search-item{display:flex;align-items:center;gap:8px;padding:8px 12px;cursor:pointer;font-size:13px}.fac-search-item:hover{background:var(--surface-hover)}.fac-search-item i{color:var(--text-color-secondary)}.fac-email{color:var(--text-color-secondary);font-size:12px;margin-inline-start:auto}.fac-ou-section{margin-top:10px;max-height:120px;overflow-y:auto}.fac-ou-item{display:flex;align-items:center;gap:8px;padding:6px 12px;cursor:pointer;font-size:13px;border-radius:6px}.fac-ou-item:hover{background:var(--surface-hover)}.fac-ou-item i{color:var(--text-color-secondary)}.fac-section-label{font-size:11px;font-weight:700;text-transform:uppercase;color:var(--text-color-secondary);letter-spacing:.5px;margin-bottom:8px}.fac-toggle-row{display:flex;align-items:center;justify-content:space-between;margin-top:10px}.fac-link{background:none;border:none;color:var(--primary-color, #e8732a);cursor:pointer;font-size:12px;padding:0}.fac-link:hover{text-decoration:underline}.fac-checkbox-label{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--text-color-secondary);cursor:pointer}.fac-checkbox-label input{accent-color:var(--primary-color, #e8732a)}.fac-perms-section{flex:1;overflow-y:auto;padding:16px 20px}.fac-loading,.fac-empty{text-align:center;padding:20px;color:var(--text-color-secondary);font-size:13px}.fac-perm-row{display:flex;align-items:center;justify-content:space-between;padding:8px 0;border-bottom:1px solid rgba(255,255,255,.05);gap:8px}.fac-perm-info{display:flex;align-items:center;gap:8px;flex:1;min-width:0}.fac-perm-info i{color:var(--text-color-secondary);flex-shrink:0}.fac-perm-name{font-size:13px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fac-badge{font-size:10px;padding:2px 6px;border-radius:8px;flex-shrink:0}.fac-badge.inherited{background:#ffffff1a;color:var(--text-color-secondary)}.fac-perm-actions{display:flex;align-items:center;gap:6px;flex-shrink:0}.fac-icon-btn{background:#ffffff0f;border:none;border-radius:6px;width:28px;height:28px;display:flex;align-items:center;justify-content:center;color:inherit;cursor:pointer;font-size:12px}.fac-icon-btn:hover{background:#ffffff24}.fac-icon-btn.danger:hover{background:#ff3b3033;color:#ff3b30}.fac-icon-btn:disabled{opacity:.4;cursor:default}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
840
|
+
}
|
|
841
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImport: i0, type: SharePanelComponent, decorators: [{
|
|
842
|
+
type: Component,
|
|
843
|
+
args: [{ selector: 'fly-share-panel', standalone: true, imports: [CommonModule, FormsModule, TranslatePipe], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"fac-overlay\" (click)=\"close.emit()\">\n <div class=\"fac-panel\" (click)=\"$event.stopPropagation()\">\n <div class=\"fac-header\">\n <h3>{{ titleKey | translate }}</h3>\n <span class=\"fac-target-name\">{{ targetName }}</span>\n <button type=\"button\" class=\"fac-close\" (click)=\"close.emit()\">\n <i class=\"pi pi-times\" aria-hidden=\"true\"></i>\n </button>\n </div>\n\n <div class=\"fac-add-section\">\n <div class=\"fac-search-row\">\n <input\n type=\"text\"\n class=\"fac-input\"\n [(ngModel)]=\"searchQuery\"\n (input)=\"onSearchInput()\"\n [placeholder]=\"'files.share.search_placeholder' | translate\"\n />\n <select class=\"fac-select\" [(ngModel)]=\"newLevel\">\n @for (opt of levelOptions; track opt.value) {\n <option [value]=\"opt.value\">{{ opt.labelKey | translate }}</option>\n }\n </select>\n </div>\n\n @if (searchResults().length > 0) {\n <div class=\"fac-search-results\">\n @for (result of searchResults(); track result.id) {\n <div class=\"fac-search-item\" (click)=\"onGrantToUser(result)\">\n <i class=\"pi pi-user\" aria-hidden=\"true\"></i>\n <span>{{ result.firstName }} {{ result.lastName }}</span>\n <span class=\"fac-email\">{{ result.email }}</span>\n </div>\n }\n </div>\n }\n\n @if (showOuPicker()) {\n <div class=\"fac-ou-section\">\n <div class=\"fac-section-label\">{{ 'files.share.share_with_ou' | translate }}</div>\n @for (ou of ouTree(); track ou.id) {\n <div class=\"fac-ou-item\" (click)=\"onGrantToOu(ou)\">\n <i class=\"pi pi-sitemap\" aria-hidden=\"true\"></i>\n <span>{{ ou.displayName }}</span>\n </div>\n }\n </div>\n }\n\n <div class=\"fac-toggle-row\">\n <button type=\"button\" class=\"fac-link\" (click)=\"showOuPicker.set(!showOuPicker())\">\n {{ showOuPicker() ? ('files.share.hide_ous' | translate) : ('files.share.show_ous' | translate) }}\n </button>\n @if (showApplyToChildren) {\n <label class=\"fac-checkbox-label\">\n <input type=\"checkbox\" [(ngModel)]=\"applyToChildren\" />\n {{ 'files.share.apply_children' | translate }}\n </label>\n }\n </div>\n </div>\n\n <div class=\"fac-perms-section\">\n <div class=\"fac-section-label\">{{ 'files.share.current_permissions' | translate }}</div>\n @if (loading()) {\n <div class=\"fac-loading\"><i class=\"pi pi-spin pi-spinner\" aria-hidden=\"true\"></i></div>\n } @else if (permissions().length === 0) {\n <div class=\"fac-empty\">{{ 'files.share.no_permissions' | translate }}</div>\n } @else {\n @for (perm of permissions(); track perm.id) {\n <div class=\"fac-perm-row\">\n <div class=\"fac-perm-info\">\n <i [class]=\"getPermIcon(perm)\" aria-hidden=\"true\"></i>\n <span class=\"fac-perm-name\">{{ getPermLabel(perm) }}</span>\n @if (perm.isInherited) {\n <span class=\"fac-badge inherited\">{{ 'files.share.inherited' | translate }}</span>\n }\n </div>\n <div class=\"fac-perm-actions\">\n <select\n class=\"fac-select sm\"\n [ngModel]=\"perm.level\"\n (ngModelChange)=\"onUpdateLevel(perm, $event)\"\n [disabled]=\"perm.isInherited === true\"\n >\n @for (opt of levelOptions; track opt.value) {\n <option [value]=\"opt.value\">{{ opt.labelKey | translate }}</option>\n }\n </select>\n <button\n type=\"button\"\n class=\"fac-icon-btn danger\"\n (click)=\"onRevokePermission(perm)\"\n [disabled]=\"perm.isInherited === true\"\n [title]=\"'files.share.revoke' | translate\"\n >\n <i class=\"pi pi-trash\" aria-hidden=\"true\"></i>\n </button>\n </div>\n </div>\n }\n }\n </div>\n </div>\n</div>\n", styles: [".fac-overlay{position:fixed;inset:0;background:#00000080;display:flex;align-items:center;justify-content:center;z-index:2000;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}.fac-panel{background:var(--surface-card, #1e1e1e);border:1px solid var(--surface-border);border-radius:12px;width:480px;max-height:80vh;display:flex;flex-direction:column;overflow:hidden;box-shadow:0 16px 48px #0006}.fac-header{display:flex;align-items:center;gap:10px;padding:16px 20px;border-bottom:1px solid var(--surface-border)}.fac-header h3{margin:0;font-size:16px;font-weight:600}.fac-target-name{flex:1;font-size:13px;color:var(--text-color-secondary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fac-close{background:none;border:none;color:var(--text-color-secondary);cursor:pointer;font-size:16px;padding:4px}.fac-close:hover{color:var(--text-color)}.fac-add-section{padding:16px 20px;border-bottom:1px solid var(--surface-border)}.fac-search-row{display:flex;gap:8px}.fac-input{flex:1;background:#ffffff14;border:1px solid rgba(255,255,255,.15);border-radius:8px;padding:8px 12px;color:inherit;font-size:13px;outline:none}.fac-input:focus{border-color:var(--primary-color, #e8732a)}.fac-select{background:#ffffff14;border:1px solid rgba(255,255,255,.15);border-radius:8px;padding:8px 10px;color:inherit;font-size:13px;outline:none}.fac-select.sm{padding:4px 8px;font-size:12px}.fac-search-results{margin-top:8px;max-height:150px;overflow-y:auto;border:1px solid var(--surface-border);border-radius:8px}.fac-search-item{display:flex;align-items:center;gap:8px;padding:8px 12px;cursor:pointer;font-size:13px}.fac-search-item:hover{background:var(--surface-hover)}.fac-search-item i{color:var(--text-color-secondary)}.fac-email{color:var(--text-color-secondary);font-size:12px;margin-inline-start:auto}.fac-ou-section{margin-top:10px;max-height:120px;overflow-y:auto}.fac-ou-item{display:flex;align-items:center;gap:8px;padding:6px 12px;cursor:pointer;font-size:13px;border-radius:6px}.fac-ou-item:hover{background:var(--surface-hover)}.fac-ou-item i{color:var(--text-color-secondary)}.fac-section-label{font-size:11px;font-weight:700;text-transform:uppercase;color:var(--text-color-secondary);letter-spacing:.5px;margin-bottom:8px}.fac-toggle-row{display:flex;align-items:center;justify-content:space-between;margin-top:10px}.fac-link{background:none;border:none;color:var(--primary-color, #e8732a);cursor:pointer;font-size:12px;padding:0}.fac-link:hover{text-decoration:underline}.fac-checkbox-label{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--text-color-secondary);cursor:pointer}.fac-checkbox-label input{accent-color:var(--primary-color, #e8732a)}.fac-perms-section{flex:1;overflow-y:auto;padding:16px 20px}.fac-loading,.fac-empty{text-align:center;padding:20px;color:var(--text-color-secondary);font-size:13px}.fac-perm-row{display:flex;align-items:center;justify-content:space-between;padding:8px 0;border-bottom:1px solid rgba(255,255,255,.05);gap:8px}.fac-perm-info{display:flex;align-items:center;gap:8px;flex:1;min-width:0}.fac-perm-info i{color:var(--text-color-secondary);flex-shrink:0}.fac-perm-name{font-size:13px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fac-badge{font-size:10px;padding:2px 6px;border-radius:8px;flex-shrink:0}.fac-badge.inherited{background:#ffffff1a;color:var(--text-color-secondary)}.fac-perm-actions{display:flex;align-items:center;gap:6px;flex-shrink:0}.fac-icon-btn{background:#ffffff0f;border:none;border-radius:6px;width:28px;height:28px;display:flex;align-items:center;justify-content:center;color:inherit;cursor:pointer;font-size:12px}.fac-icon-btn:hover{background:#ffffff24}.fac-icon-btn.danger:hover{background:#ff3b3033;color:#ff3b30}.fac-icon-btn:disabled{opacity:.4;cursor:default}\n"] }]
|
|
844
|
+
}], propDecorators: { targetName: [{
|
|
845
|
+
type: Input
|
|
846
|
+
}], titleKey: [{
|
|
847
|
+
type: Input
|
|
848
|
+
}], loadPermissions: [{
|
|
849
|
+
type: Input,
|
|
850
|
+
args: [{ required: true }]
|
|
851
|
+
}], searchUsers: [{
|
|
852
|
+
type: Input,
|
|
853
|
+
args: [{ required: true }]
|
|
854
|
+
}], loadOuTree: [{
|
|
855
|
+
type: Input,
|
|
856
|
+
args: [{ required: true }]
|
|
857
|
+
}], grantToUser: [{
|
|
858
|
+
type: Input,
|
|
859
|
+
args: [{ required: true }]
|
|
860
|
+
}], grantToOu: [{
|
|
861
|
+
type: Input,
|
|
862
|
+
args: [{ required: true }]
|
|
863
|
+
}], updatePermission: [{
|
|
864
|
+
type: Input,
|
|
865
|
+
args: [{ required: true }]
|
|
866
|
+
}], revokePermission: [{
|
|
867
|
+
type: Input,
|
|
868
|
+
args: [{ required: true }]
|
|
869
|
+
}], showApplyToChildren: [{
|
|
870
|
+
type: Input
|
|
871
|
+
}], permissionLevels: [{
|
|
872
|
+
type: Input
|
|
873
|
+
}], everyoneLabelKey: [{
|
|
874
|
+
type: Input
|
|
875
|
+
}], close: [{
|
|
876
|
+
type: Output
|
|
877
|
+
}] } });
|
|
878
|
+
|
|
418
879
|
/*
|
|
419
880
|
* @mohamedatia/fly-design-system — Public API
|
|
420
881
|
* https://www.npmjs.com/package/@mohamedatia/fly-design-system
|
|
421
882
|
*
|
|
422
|
-
* This is the single entry point for Business App developers.
|
|
883
|
+
* This is the single entry point for Business / Supporting App developers.
|
|
423
884
|
* Import everything from '@mohamedatia/fly-design-system' — never from relative shell paths.
|
|
424
885
|
*
|
|
425
886
|
* NOTE: This package is published under @mohamedatia/fly-design-system until the @fly
|
|
@@ -434,12 +895,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.5", ngImpor
|
|
|
434
895
|
* without branching between standalone and embedded modes.
|
|
435
896
|
* v1.3.9: MockAuthService field initializers moved into constructor to fix ngDevMode ReferenceError
|
|
436
897
|
* when the package is loaded in a production-mode bundle (no ngDevMode global defined).
|
|
437
|
-
*
|
|
898
|
+
* v1.4.0: ContextMenuComponent (positioned menu, keyboard nav, focus management) moved from desktop
|
|
899
|
+
* shell to design system. MessageBoxService + MessageBoxComponent added (Windows Forms-style
|
|
900
|
+
* modal dialogs with localized button labels, focus management, and Escape key support).
|
|
901
|
+
* MessageBoxButtons, MessageBoxIcon, DialogResult, MessageBoxOptions, MessageBoxButton exported.
|
|
902
|
+
* v1.5.0: SharePanelComponent — generic share/ACL overlay; host supplies API callbacks and optional
|
|
903
|
+
* permission level options (file ACL vs note-style View/Edit).
|
|
904
|
+
* See docs/ExternalAppsGuide/03-frontend-app.md.
|
|
438
905
|
*/
|
|
439
906
|
|
|
440
907
|
/**
|
|
441
908
|
* Generated bundle index. Do not edit.
|
|
442
909
|
*/
|
|
443
910
|
|
|
444
|
-
export { AuthService, FlyThemeService, I18nService, MockAuthService, RTL_LOCALE_SET, StandaloneWindowManagerService, TranslatePipe, WINDOW_DATA, WindowManagerService, isRtlLocale };
|
|
911
|
+
export { AuthService, ContextMenuComponent, DialogResult, FlyThemeService, I18nService, MessageBoxButtons, MessageBoxComponent, MessageBoxIcon, MessageBoxService, MockAuthService, RTL_LOCALE_SET, SHARE_PANEL_DEFAULT_FILE_LEVELS, SharePanelComponent, StandaloneWindowManagerService, TranslatePipe, WINDOW_DATA, WindowManagerService, isRtlLocale };
|
|
445
912
|
//# sourceMappingURL=mohamedatia-fly-design-system.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mohamedatia-fly-design-system.mjs","sources":["../../../projects/design-system/src/lib/models/window.model.ts","../../../projects/design-system/src/lib/services/auth.service.ts","../../../projects/design-system/src/lib/services/i18n.service.ts","../../../projects/design-system/src/lib/services/fly-theme.service.ts","../../../projects/design-system/src/lib/services/window-manager.service.ts","../../../projects/design-system/src/lib/pipes/translate.pipe.ts","../../../projects/design-system/src/lib/services/auth.service.mock.ts","../../../projects/design-system/src/public-api.ts","../../../projects/design-system/src/mohamedatia-fly-design-system.ts"],"sourcesContent":["import { InjectionToken, Type } from '@angular/core';\r\n\r\nexport type WindowState = 'normal' | 'minimized' | 'maximized';\r\n\r\nexport interface ChildWindowData {\r\n childComponent: Type<unknown>;\r\n [key: string]: unknown;\r\n}\r\n\r\nexport interface WindowInstance {\r\n id: string;\r\n appId: string;\r\n title: string;\r\n icon: string;\r\n iconBg: string;\r\n state: WindowState;\r\n position: { x: number; y: number };\r\n size: { width: number; height: number };\r\n zIndex: number;\r\n isFocused: boolean;\r\n isClosing?: boolean;\r\n isMinimizing?: boolean;\r\n isRestoring?: boolean;\r\n isMaximizing?: boolean;\r\n isUnmaximizing?: boolean;\r\n parentWindowId?: string;\r\n childData?: ChildWindowData;\r\n /** Populated when remote `loadComponent()` fails (e.g. federation). */\r\n remoteLoadError?: string;\r\n}\r\n\r\nexport const WINDOW_DATA = new InjectionToken<WindowInstance>('WINDOW_DATA');\r\n","import { Injectable, computed, signal } from '@angular/core';\nimport { User } from '../models/user.model';\n\ninterface AuthSession {\n user: User;\n accessToken: string;\n expiresAt: number;\n}\n\n/**\n * Shared AuthService for Business Apps.\n *\n * This is a **read-only signal store** — Business Apps inject it to read the\n * current user and access token. The FlyOS shell populates it after PKCE login\n * and silent refresh. Business Apps must never call setSession() directly.\n *\n * When running as a Module Federation remote, the shell and the Business App\n * share this singleton via the `@mohamedatia/fly-design-system` shared mapping\n * in federation.config.js — so the shell's populated instance is the same\n * object the Business App reads.\n *\n * When running standalone (without the shell), call initFromToken() with a\n * JWT stored in localStorage to hydrate the user.\n */\n@Injectable({ providedIn: 'root' })\nexport class AuthService {\n private _session = signal<AuthSession | null>(null);\n\n readonly currentUser = computed(() => this._session()?.user ?? null);\n readonly accessToken = computed(() => this._session()?.accessToken ?? null);\n readonly isAuthenticated = computed(() => {\n const s = this._session();\n return s !== null && s.expiresAt > Date.now();\n });\n\n /**\n * No-op when running as a Module Federation remote — the shell has already\n * initialised the session before the remote bootstraps. Business Apps call\n * this in their APP_INITIALIZER so the same code works in both standalone\n * and embedded modes without branching.\n */\n async init(): Promise<void> {}\n\n /**\n * Called by the shell after a successful login or silent refresh.\n * Business Apps should not call this directly.\n */\n setSession(user: User, accessToken: string, expiresAt: number): void {\n this._session.set({ user, accessToken, expiresAt });\n }\n\n /** Clears the in-memory session (called by the shell on logout). */\n clearSession(): void {\n this._session.set(null);\n }\n\n /**\n * Standalone mode: parse a JWT from localStorage and hydrate the user.\n * Pass the storage key your app uses; embedded remotes typically set this in `APP_INITIALIZER`.\n */\n initFromToken(storageKey = 'circles_token'): void {\n const token = localStorage.getItem(storageKey);\n if (!token) return;\n try {\n const base64 = token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/');\n const payload = JSON.parse(decodeURIComponent(\n atob(base64).split('').map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)).join('')\n )) as Record<string, unknown>;\n\n const rawRole = payload['role'];\n const roles = Array.isArray(rawRole) ? rawRole as string[] : rawRole ? [rawRole as string] : [];\n const rawApps = payload['apps'];\n const apps = Array.isArray(rawApps) ? rawApps as string[] : rawApps ? [rawApps as string] : [];\n const name = (payload['name'] as string) ?? '';\n const nameParts = name.split(' ');\n\n const user: User = {\n id: (payload['sub'] as string) ?? '',\n tenantId: (payload['tenant_id'] as string) ?? (payload['tid'] as string) ?? null,\n email: (payload['email'] as string) ?? '',\n fullName: name,\n firstName: nameParts[0] ?? '',\n lastName: nameParts.slice(1).join(' '),\n preferredLocale: (payload['locale'] as string) ?? 'en',\n roles,\n apps,\n ou: payload['ou'] as string | undefined,\n };\n\n const expiresAt = ((payload['exp'] as number) ?? 0) * 1000;\n this.setSession(user, token, expiresAt);\n } catch {\n // Invalid token — stay unauthenticated\n }\n }\n}\n","import { Injectable, computed, signal, inject, ErrorHandler } from '@angular/core';\n\n/** Locales that use RTL layout for `dir` and DS `isRtl` / `direction`. */\nexport const RTL_LOCALE_SET: ReadonlySet<string> = new Set(['ar', 'ur']);\n\nexport function isRtlLocale(lang: string): boolean {\n return RTL_LOCALE_SET.has(lang);\n}\n\nfunction escapeRegExp(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/** Accepts flat or nested JSON objects; nested keys become dot paths. Rejects arrays and non-string leaves. */\nfunction normalizeLocaleJson(raw: unknown): Record<string, string> {\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {\n throw new Error('Locale bundle must be a JSON object');\n }\n const out: Record<string, string> = {};\n const walk = (obj: Record<string, unknown>, prefix: string): void => {\n for (const [k, v] of Object.entries(obj)) {\n const path = prefix ? `${prefix}.${k}` : k;\n if (typeof v === 'string') {\n out[path] = v;\n } else if (v !== null && typeof v === 'object' && !Array.isArray(v)) {\n walk(v as Record<string, unknown>, path);\n } else {\n throw new Error(`Locale bundle invalid value at \"${path}\" (expected string or nested object)`);\n }\n }\n };\n walk(raw as Record<string, unknown>, '');\n return out;\n}\n\n/** Options for loading a remote or standalone locale JSON bundle. */\nexport interface LoadBundleOptions {\n /** Stable id (e.g. app id from manifest); later loads replace this bundle only. */\n id: string;\n /** Base URL ending with `/`, e.g. `https://app.example.com/locale/` or `/my-remote/locale/` */\n baseUrl: string;\n lang: string;\n /** When aborted, failures are ignored (e.g. superseded language load). */\n signal?: AbortSignal;\n /**\n * When true, fetch/parse failures are not reported via Angular `ErrorHandler`.\n * Use for optional remote locale bundles (e.g. federated apps that may be offline).\n */\n silent?: boolean;\n}\n\n/**\n * Shared I18nService for the shell and Business Apps.\n *\n * **Merge order** (later keys win): shell layer → remote bundles in registration order.\n *\n * - Shell: `setShellTranslations()` after loading `locale/{lang}.json` and API overrides.\n * - Remotes: `loadBundle()` per manifest `localeBaseUrl`.\n */\n@Injectable({ providedIn: 'root' })\nexport class I18nService {\n private readonly errorHandler = inject(ErrorHandler, { optional: true });\n\n private readonly _shell = signal<Record<string, string>>({});\n private readonly _bundles = signal<Record<string, Record<string, string>>>({});\n private readonly _bundleOrder = signal<string[]>([]);\n private readonly _locale = signal<string>('en');\n private readonly _version = signal(0);\n\n private readonly _merged = computed(() => {\n const shell = this._shell();\n const order = this._bundleOrder();\n const bundles = this._bundles();\n let out: Record<string, string> = { ...shell };\n for (const id of order) {\n const b = bundles[id];\n if (b) out = { ...out, ...b };\n }\n return out;\n });\n\n readonly locale = this._locale.asReadonly();\n readonly version = this._version.asReadonly();\n readonly isRtl = computed(() => isRtlLocale(this._locale()));\n readonly direction = computed(() => (isRtlLocale(this._locale()) ? 'rtl' : 'ltr'));\n\n private bump(): void {\n this._version.update((v) => v + 1);\n }\n\n /** Replace shell strings (desktop `locale/*.json` + `/api/i18n/desktop-shell/{lang}` merged by the shell). */\n setShellTranslations(data: Record<string, string>, lang: string): void {\n this._shell.set(data);\n this._locale.set(lang);\n this.bump();\n }\n\n setLanguage(lang: string): void {\n this._locale.set(lang);\n this.bump();\n }\n\n /**\n * Fetch `{baseUrl}{lang}.json` and store under `id`. New ids are appended to the merge order.\n * @returns whether the bundle was loaded successfully.\n */\n async loadBundle(opts: LoadBundleOptions): Promise<boolean> {\n const { id, baseUrl, lang, signal: abortSignal, silent } = opts;\n try {\n const base = baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`;\n const url = `${base}${lang}.json`;\n const resp = await fetch(url, { signal: abortSignal });\n if (!resp.ok) throw new Error(`HTTP ${resp.status}`);\n const data = normalizeLocaleJson(await resp.json());\n if (abortSignal?.aborted) return false;\n this._bundles.update((b) => ({ ...b, [id]: data }));\n this._bundleOrder.update((order) => (order.includes(id) ? order : [...order, id]));\n this._locale.set(lang);\n this.bump();\n return true;\n } catch (e) {\n if (abortSignal?.aborted) return false;\n if (!silent) this.errorHandler?.handleError(e);\n return false;\n }\n }\n\n removeBundle(id: string): void {\n this._bundles.update((b) => {\n const next = { ...b };\n delete next[id];\n return next;\n });\n this._bundleOrder.update((order) => order.filter((x) => x !== id));\n this.bump();\n }\n\n clearRemoteBundles(): void {\n this._bundles.set({});\n this._bundleOrder.set([]);\n this.bump();\n }\n\n t(key: string, params?: Record<string, string | number>): string {\n const val = this._merged()[key] ?? key;\n if (!params) return val;\n return Object.entries(params).reduce((result, [k, v]) => {\n const safe = escapeRegExp(k);\n return result.replace(new RegExp(`{{\\\\s*${safe}\\\\s*}}`, 'g'), String(v));\n }, val);\n }\n}\n","import { Injectable, signal } from '@angular/core';\n\nexport type FlyThemeMode = 'light' | 'dark' | 'transparent';\n\n/**\n * Applies `html.light-theme` / `html.dark-theme` / `html.transparent-theme` for DS SCSS.\n * Shell and standalone Business Apps use the same service (federation singleton when shared).\n */\n@Injectable({ providedIn: 'root' })\nexport class FlyThemeService {\n readonly theme = signal<FlyThemeMode>('light');\n\n applyTheme(mode: FlyThemeMode): void {\n this.theme.set(mode);\n const html = document.documentElement;\n html.classList.remove('dark-theme', 'light-theme', 'transparent-theme');\n if (mode === 'dark') {\n html.classList.add('dark-theme');\n } else if (mode === 'transparent') {\n html.classList.add('transparent-theme');\n } else {\n html.classList.add('light-theme');\n }\n }\n}\n","import { Injectable } from '@angular/core';\n\n/** Options for opening a child window. */\nexport interface OpenWindowOptions {\n /** Unique identifier for the window instance. */\n windowId: string;\n /** Display title shown in the window chrome. */\n title: string;\n /** Route path or URL to load inside the window. */\n route: string;\n /** Initial width in pixels. Defaults to 900. */\n width?: number;\n /** Initial height in pixels. Defaults to 600. */\n height?: number;\n /** Whether the window can be resized. Defaults to true. */\n resizable?: boolean;\n /** Whether the window can be maximised. Defaults to true. */\n maximizable?: boolean;\n /** Arbitrary data passed to the child window via WINDOW_DATA. */\n data?: unknown;\n}\n\n/**\n * Abstract interface for the WindowManager.\n * The FlyOS shell provides the concrete implementation; Business Apps\n * inject this token to open, close, and focus windows without depending\n * on the shell's internal implementation.\n */\nexport abstract class WindowManagerService {\n abstract openChildWindow(options: OpenWindowOptions): void;\n abstract closeWindow(windowId: string): void;\n abstract focusWindow(windowId: string): void;\n}\n\n/**\n * No-op fallback implementation used when running a Business App in\n * standalone mode (outside the FlyOS shell). Logs a warning instead of\n * throwing so standalone dev/test flows are not broken.\n */\n@Injectable()\nexport class StandaloneWindowManagerService extends WindowManagerService {\n openChildWindow(options: OpenWindowOptions): void {\n console.warn('[WindowManagerService] openChildWindow called in standalone mode — no shell available.', options);\n }\n\n closeWindow(windowId: string): void {\n console.warn('[WindowManagerService] closeWindow called in standalone mode — no shell available.', windowId);\n }\n\n focusWindow(windowId: string): void {\n console.warn('[WindowManagerService] focusWindow called in standalone mode — no shell available.', windowId);\n }\n}\n\n","import { Pipe, PipeTransform, inject } from '@angular/core';\nimport { I18nService } from '../services/i18n.service';\n\n/**\n * Translates a key using the shared I18nService.\n *\n * @example\n * {{ 'myApp.section.title' | translate }}\n * {{ 'myApp.items_count' | translate:{ n: count() } }}\n */\n@Pipe({\n name: 'translate',\n standalone: true,\n pure: false,\n})\nexport class TranslatePipe implements PipeTransform {\n private readonly i18n = inject(I18nService);\n\n transform(key: string, params?: Record<string, string | number>): string {\n // Read version so template CD re-runs when bundles load (merged translations updated).\n void this.i18n.version();\n return this.i18n.t(key, params);\n }\n}\n","import { Injectable, Signal, computed, inject, signal } from '@angular/core';\nimport { Router } from '@angular/router';\nimport { Observable, of } from 'rxjs';\nimport { User } from '../models/user.model';\n\nexport interface MockAuthConfig {\n user: User;\n token?: string;\n loginRedirect?: string[];\n logoutRedirect?: string[];\n}\n\ninterface MockSession {\n accessToken: string;\n user: User;\n expiresAt: number;\n}\n\n/**\n * Shared mock AuthService base class for Business App developers.\n *\n * Extend this class in your app's `auth.service.mock.ts` and override\n * `getConfig()` to supply app-specific mock user data and navigation paths.\n * The subclass must be decorated with `@Injectable({ providedIn: 'root' })`\n * and exported as `AuthService` so Angular's `fileReplacements` swap works.\n *\n * @example\n * ```typescript\n * // src/app/core/services/auth.service.mock.ts\n * import { Injectable } from '@angular/core';\n * import { MockAuthService, type MockAuthConfig } from '@mohamedatia/fly-design-system';\n *\n * @Injectable({ providedIn: 'root' })\n * export class AuthService extends MockAuthService {\n * protected override getConfig(): MockAuthConfig {\n * return {\n * user: { id: 'dev-001', tenantId: 'dev-tenant', email: 'dev@example.com',\n * firstName: 'Dev', lastName: 'User', fullName: 'Dev User',\n * preferredLocale: 'en', roles: ['admin'], apps: ['my-app'] },\n * token: 'my-mock-token',\n * loginRedirect: ['/app'],\n * };\n * }\n * }\n * ```\n */\n@Injectable()\nexport class MockAuthService {\n private readonly router = inject(Router);\n\n protected readonly _session: ReturnType<typeof signal<MockSession | null>>;\n\n private readonly _config: MockAuthConfig;\n\n readonly isAuthenticated: Signal<boolean>;\n readonly currentUser: Signal<User | null>;\n readonly accessToken: Signal<string | null>;\n /** Always false in mock mode — 2FA is handled entirely by the STS. */\n readonly hasPendingTwoFactor: Signal<boolean>;\n /** Always false in mock mode — auth is pre-populated synchronously. */\n readonly initializing: Signal<boolean>;\n\n constructor() {\n // All signal/computed calls are inside the constructor body so Angular's\n // injection context and ngDevMode are fully set up before they execute.\n this._config = this.getConfig();\n\n this._session = signal<MockSession | null>({\n accessToken: this._config.token ?? 'mock-token',\n user: this._config.user,\n expiresAt: Date.now() + 24 * 60 * 60 * 1000,\n });\n\n this.isAuthenticated = computed(() => {\n const s = this._session();\n return s !== null && s.expiresAt > Date.now();\n });\n\n this.currentUser = computed(() => this._session()?.user ?? null);\n this.accessToken = computed(() => this._session()?.accessToken ?? null);\n this.hasPendingTwoFactor = computed(() => false);\n this.initializing = signal(false).asReadonly();\n }\n\n /** Override in subclass to supply app-specific mock data. */\n protected getConfig(): MockAuthConfig {\n return {\n user: {\n id: 'mock-user-001',\n tenantId: 'mock-tenant-001',\n email: 'developer@fly.local',\n firstName: 'UI',\n lastName: 'Developer',\n fullName: 'UI Developer',\n preferredLocale: 'en',\n roles: ['admin'],\n apps: [],\n },\n };\n }\n\n async init(): Promise<void> {\n // Mock mode: already authenticated, nothing to initialize.\n }\n\n startLogin(): void {\n this.router.navigate(this._config.loginRedirect ?? ['/']);\n }\n\n handleCallback(_code: string, _state?: string): Observable<User> {\n return of(this._config.user);\n }\n\n async forceRefresh(): Promise<void> {\n // No-op in mock mode — token never expires.\n }\n\n patchSessionPreferredLocale(locale: string): void {\n const session = this._session();\n if (!session?.user) return;\n this._session.set({ ...session, user: { ...session.user, preferredLocale: locale } });\n }\n\n /** Parameter order matches the real AuthService: (user, accessToken, expiresAt). */\n setSession(_user: User, _accessToken: string, _expiresAt: number): void {\n // No-op in mock mode — session is hardcoded.\n }\n\n logout(): void {\n this._session.set(null);\n this.router.navigate(this._config.logoutRedirect ?? ['/']);\n }\n}\n","/*\r\n * @mohamedatia/fly-design-system — Public API\r\n * https://www.npmjs.com/package/@mohamedatia/fly-design-system\r\n *\r\n * This is the single entry point for Business App developers.\r\n * Import everything from '@mohamedatia/fly-design-system' — never from relative shell paths.\r\n *\r\n * NOTE: This package is published under @mohamedatia/fly-design-system until the @fly\r\n * npm org is created. It will be republished as @fly/design-system once available.\r\n *\r\n * v1.3.0: FlyThemeService (html theme classes), hardened I18nService (AbortSignal, ErrorHandler, RTL helpers).\r\n * v1.3.1: loadBundle normalizes locale JSON (flat or nested objects; rejects invalid leaves).\r\n * v1.3.2: loadBundle returns boolean; LoadBundleOptions.silent skips ErrorHandler on failure.\r\n * v1.3.7: MockAuthService base class + MockAuthConfig for Business App mock auth via fileReplacements.\r\n * User model extended with optional avatar, source, twoFactorEnabled, createdAt fields.\r\n * v1.3.8: AuthService.init() no-op added so Business Apps can call init() in APP_INITIALIZER\r\n * without branching between standalone and embedded modes.\r\n * v1.3.9: MockAuthService field initializers moved into constructor to fix ngDevMode ReferenceError\r\n * when the package is loaded in a production-mode bundle (no ngDevMode global defined).\r\n * See BusinessAppsGuide/03-frontend-app.md.\r\n */\r\n\r\n// ─── Models ──────────────────────────────────────────────────────────────────\r\nexport type { DesktopApp } from './lib/models/app.model';\r\nexport type {\r\n WindowInstance,\r\n WindowState,\r\n ChildWindowData,\r\n} from './lib/models/window.model';\r\nexport { WINDOW_DATA } from './lib/models/window.model';\r\nexport type { User } from './lib/models/user.model';\r\n\r\n// ─── Remote App Registration ─────────────────────────────────────────────────\r\nexport type { RemoteAppDef } from './lib/models/remote-app.model';\r\n\r\n// ─── Services ────────────────────────────────────────────────────────────────\r\nexport { AuthService } from './lib/services/auth.service';\r\nexport type { LoadBundleOptions } from './lib/services/i18n.service';\r\nexport { I18nService, RTL_LOCALE_SET, isRtlLocale } from './lib/services/i18n.service';\r\nexport { FlyThemeService, type FlyThemeMode } from './lib/services/fly-theme.service';\r\nexport {\r\n WindowManagerService,\r\n StandaloneWindowManagerService,\r\n type OpenWindowOptions,\r\n} from './lib/services/window-manager.service';\r\n\r\n// ─── Pipes ───────────────────────────────────────────────────────────────────\r\nexport { TranslatePipe } from './lib/pipes/translate.pipe';\r\n\r\n// ─── Mock Auth (for fileReplacements in dev/mock builds) ─────────────────────\r\nexport { MockAuthService, type MockAuthConfig } from './lib/services/auth.service.mock';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mohamedatia-fly-design-system.mjs","sources":["../../../projects/design-system/src/lib/models/window.model.ts","../../../projects/design-system/src/lib/services/auth.service.ts","../../../projects/design-system/src/lib/services/i18n.service.ts","../../../projects/design-system/src/lib/services/fly-theme.service.ts","../../../projects/design-system/src/lib/services/window-manager.service.ts","../../../projects/design-system/src/lib/pipes/translate.pipe.ts","../../../projects/design-system/src/lib/services/auth.service.mock.ts","../../../projects/design-system/src/lib/components/context-menu/context-menu.component.ts","../../../projects/design-system/src/lib/components/context-menu/context-menu.component.html","../../../projects/design-system/src/lib/components/message-box/message-box.service.ts","../../../projects/design-system/src/lib/components/message-box/message-box.component.ts","../../../projects/design-system/src/lib/components/message-box/message-box.component.html","../../../projects/design-system/src/lib/components/share-panel/share-panel.component.ts","../../../projects/design-system/src/lib/components/share-panel/share-panel.component.html","../../../projects/design-system/src/public-api.ts","../../../projects/design-system/src/mohamedatia-fly-design-system.ts"],"sourcesContent":["import { InjectionToken, Type } from '@angular/core';\r\n\r\nexport type WindowState = 'normal' | 'minimized' | 'maximized';\r\n\r\nexport interface ChildWindowData {\r\n childComponent: Type<unknown>;\r\n [key: string]: unknown;\r\n}\r\n\r\nexport interface WindowInstance {\r\n id: string;\r\n appId: string;\r\n title: string;\r\n icon: string;\r\n iconBg: string;\r\n state: WindowState;\r\n position: { x: number; y: number };\r\n size: { width: number; height: number };\r\n zIndex: number;\r\n isFocused: boolean;\r\n isClosing?: boolean;\r\n isMinimizing?: boolean;\r\n isRestoring?: boolean;\r\n isMaximizing?: boolean;\r\n isUnmaximizing?: boolean;\r\n parentWindowId?: string;\r\n childData?: ChildWindowData;\r\n /** Populated when remote `loadComponent()` fails (e.g. federation). */\r\n remoteLoadError?: string;\r\n}\r\n\r\nexport const WINDOW_DATA = new InjectionToken<WindowInstance>('WINDOW_DATA');\r\n","import { Injectable, computed, signal } from '@angular/core';\nimport { User } from '../models/user.model';\n\ninterface AuthSession {\n user: User;\n accessToken: string;\n expiresAt: number;\n}\n\n/**\n * Shared AuthService for Business Apps.\n *\n * This is a **read-only signal store** — Business Apps inject it to read the\n * current user and access token. The FlyOS shell populates it after PKCE login\n * and silent refresh. Business Apps must never call setSession() directly.\n *\n * When running as a Module Federation remote, the shell and the Business App\n * share this singleton via the `@mohamedatia/fly-design-system` shared mapping\n * in federation.config.js — so the shell's populated instance is the same\n * object the Business App reads.\n *\n * When running standalone (without the shell), call initFromToken() with a\n * JWT stored in localStorage to hydrate the user.\n */\n@Injectable({ providedIn: 'root' })\nexport class AuthService {\n private _session = signal<AuthSession | null>(null);\n\n readonly currentUser = computed(() => this._session()?.user ?? null);\n readonly accessToken = computed(() => this._session()?.accessToken ?? null);\n readonly isAuthenticated = computed(() => {\n const s = this._session();\n return s !== null && s.expiresAt > Date.now();\n });\n\n /**\n * No-op when running as a Module Federation remote — the shell has already\n * initialised the session before the remote bootstraps. Business Apps call\n * this in their APP_INITIALIZER so the same code works in both standalone\n * and embedded modes without branching.\n */\n async init(): Promise<void> {}\n\n /**\n * Called by the shell after a successful login or silent refresh.\n * Business Apps should not call this directly.\n */\n setSession(user: User, accessToken: string, expiresAt: number): void {\n this._session.set({ user, accessToken, expiresAt });\n }\n\n /** Clears the in-memory session (called by the shell on logout). */\n clearSession(): void {\n this._session.set(null);\n }\n\n /**\n * Standalone mode: parse a JWT from localStorage and hydrate the user.\n * Pass the storage key your app uses; embedded remotes typically set this in `APP_INITIALIZER`.\n */\n initFromToken(storageKey = 'circles_token'): void {\n const token = localStorage.getItem(storageKey);\n if (!token) return;\n try {\n const base64 = token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/');\n const payload = JSON.parse(decodeURIComponent(\n atob(base64).split('').map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)).join('')\n )) as Record<string, unknown>;\n\n const rawRole = payload['role'];\n const roles = Array.isArray(rawRole) ? rawRole as string[] : rawRole ? [rawRole as string] : [];\n const rawApps = payload['apps'];\n const apps = Array.isArray(rawApps) ? rawApps as string[] : rawApps ? [rawApps as string] : [];\n const name = (payload['name'] as string) ?? '';\n const nameParts = name.split(' ');\n\n const user: User = {\n id: (payload['sub'] as string) ?? '',\n tenantId: (payload['tenant_id'] as string) ?? (payload['tid'] as string) ?? null,\n email: (payload['email'] as string) ?? '',\n fullName: name,\n firstName: nameParts[0] ?? '',\n lastName: nameParts.slice(1).join(' '),\n preferredLocale: (payload['locale'] as string) ?? 'en',\n roles,\n apps,\n ou: payload['ou'] as string | undefined,\n };\n\n const expiresAt = ((payload['exp'] as number) ?? 0) * 1000;\n this.setSession(user, token, expiresAt);\n } catch {\n // Invalid token — stay unauthenticated\n }\n }\n}\n","import { Injectable, computed, signal, inject, ErrorHandler } from '@angular/core';\n\n/** Locales that use RTL layout for `dir` and DS `isRtl` / `direction`. */\nexport const RTL_LOCALE_SET: ReadonlySet<string> = new Set(['ar', 'ur']);\n\nexport function isRtlLocale(lang: string): boolean {\n return RTL_LOCALE_SET.has(lang);\n}\n\nfunction escapeRegExp(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/** Accepts flat or nested JSON objects; nested keys become dot paths. Rejects arrays and non-string leaves. */\nfunction normalizeLocaleJson(raw: unknown): Record<string, string> {\n if (raw === null || typeof raw !== 'object' || Array.isArray(raw)) {\n throw new Error('Locale bundle must be a JSON object');\n }\n const out: Record<string, string> = {};\n const walk = (obj: Record<string, unknown>, prefix: string): void => {\n for (const [k, v] of Object.entries(obj)) {\n const path = prefix ? `${prefix}.${k}` : k;\n if (typeof v === 'string') {\n out[path] = v;\n } else if (v !== null && typeof v === 'object' && !Array.isArray(v)) {\n walk(v as Record<string, unknown>, path);\n } else {\n throw new Error(`Locale bundle invalid value at \"${path}\" (expected string or nested object)`);\n }\n }\n };\n walk(raw as Record<string, unknown>, '');\n return out;\n}\n\n/** Options for loading a remote or standalone locale JSON bundle. */\nexport interface LoadBundleOptions {\n /** Stable id (e.g. app id from manifest); later loads replace this bundle only. */\n id: string;\n /** Base URL ending with `/`, e.g. `https://app.example.com/locale/` or `/my-remote/locale/` */\n baseUrl: string;\n lang: string;\n /** When aborted, failures are ignored (e.g. superseded language load). */\n signal?: AbortSignal;\n /**\n * When true, fetch/parse failures are not reported via Angular `ErrorHandler`.\n * Use for optional remote locale bundles (e.g. federated apps that may be offline).\n */\n silent?: boolean;\n}\n\n/**\n * Shared I18nService for the shell and Business Apps.\n *\n * **Merge order** (later keys win): shell layer → remote bundles in registration order.\n *\n * - Shell: `setShellTranslations()` after loading `locale/{lang}.json` and API overrides.\n * - Remotes: `loadBundle()` per manifest `localeBaseUrl`.\n */\n@Injectable({ providedIn: 'root' })\nexport class I18nService {\n private readonly errorHandler = inject(ErrorHandler, { optional: true });\n\n private readonly _shell = signal<Record<string, string>>({});\n private readonly _bundles = signal<Record<string, Record<string, string>>>({});\n private readonly _bundleOrder = signal<string[]>([]);\n private readonly _locale = signal<string>('en');\n private readonly _version = signal(0);\n\n private readonly _merged = computed(() => {\n const shell = this._shell();\n const order = this._bundleOrder();\n const bundles = this._bundles();\n let out: Record<string, string> = { ...shell };\n for (const id of order) {\n const b = bundles[id];\n if (b) out = { ...out, ...b };\n }\n return out;\n });\n\n readonly locale = this._locale.asReadonly();\n readonly version = this._version.asReadonly();\n readonly isRtl = computed(() => isRtlLocale(this._locale()));\n readonly direction = computed(() => (isRtlLocale(this._locale()) ? 'rtl' : 'ltr'));\n\n private bump(): void {\n this._version.update((v) => v + 1);\n }\n\n /** Replace shell strings (desktop `locale/*.json` + `/api/i18n/desktop-shell/{lang}` merged by the shell). */\n setShellTranslations(data: Record<string, string>, lang: string): void {\n this._shell.set(data);\n this._locale.set(lang);\n this.bump();\n }\n\n setLanguage(lang: string): void {\n this._locale.set(lang);\n this.bump();\n }\n\n /**\n * Fetch `{baseUrl}{lang}.json` and store under `id`. New ids are appended to the merge order.\n * @returns whether the bundle was loaded successfully.\n */\n async loadBundle(opts: LoadBundleOptions): Promise<boolean> {\n const { id, baseUrl, lang, signal: abortSignal, silent } = opts;\n try {\n const base = baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`;\n const url = `${base}${lang}.json`;\n const resp = await fetch(url, { signal: abortSignal });\n if (!resp.ok) throw new Error(`HTTP ${resp.status}`);\n const data = normalizeLocaleJson(await resp.json());\n if (abortSignal?.aborted) return false;\n this._bundles.update((b) => ({ ...b, [id]: data }));\n this._bundleOrder.update((order) => (order.includes(id) ? order : [...order, id]));\n this._locale.set(lang);\n this.bump();\n return true;\n } catch (e) {\n if (abortSignal?.aborted) return false;\n if (!silent) this.errorHandler?.handleError(e);\n return false;\n }\n }\n\n removeBundle(id: string): void {\n this._bundles.update((b) => {\n const next = { ...b };\n delete next[id];\n return next;\n });\n this._bundleOrder.update((order) => order.filter((x) => x !== id));\n this.bump();\n }\n\n clearRemoteBundles(): void {\n this._bundles.set({});\n this._bundleOrder.set([]);\n this.bump();\n }\n\n t(key: string, params?: Record<string, string | number>): string {\n const val = this._merged()[key] ?? key;\n if (!params) return val;\n return Object.entries(params).reduce((result, [k, v]) => {\n const safe = escapeRegExp(k);\n return result.replace(new RegExp(`{{\\\\s*${safe}\\\\s*}}`, 'g'), String(v));\n }, val);\n }\n}\n","import { Injectable, signal } from '@angular/core';\n\nexport type FlyThemeMode = 'light' | 'dark' | 'transparent';\n\n/**\n * Applies `html.light-theme` / `html.dark-theme` / `html.transparent-theme` for DS SCSS.\n * Shell and standalone Business Apps use the same service (federation singleton when shared).\n */\n@Injectable({ providedIn: 'root' })\nexport class FlyThemeService {\n readonly theme = signal<FlyThemeMode>('light');\n\n applyTheme(mode: FlyThemeMode): void {\n this.theme.set(mode);\n const html = document.documentElement;\n html.classList.remove('dark-theme', 'light-theme', 'transparent-theme');\n if (mode === 'dark') {\n html.classList.add('dark-theme');\n } else if (mode === 'transparent') {\n html.classList.add('transparent-theme');\n } else {\n html.classList.add('light-theme');\n }\n }\n}\n","import { Injectable } from '@angular/core';\n\n/** Options for opening a child window. */\nexport interface OpenWindowOptions {\n /** Unique identifier for the window instance. */\n windowId: string;\n /** Display title shown in the window chrome. */\n title: string;\n /** Route path or URL to load inside the window. */\n route: string;\n /** Initial width in pixels. Defaults to 900. */\n width?: number;\n /** Initial height in pixels. Defaults to 600. */\n height?: number;\n /** Whether the window can be resized. Defaults to true. */\n resizable?: boolean;\n /** Whether the window can be maximised. Defaults to true. */\n maximizable?: boolean;\n /** Arbitrary data passed to the child window via WINDOW_DATA. */\n data?: unknown;\n}\n\n/**\n * Abstract interface for the WindowManager.\n * The FlyOS shell provides the concrete implementation; Business Apps\n * inject this token to open, close, and focus windows without depending\n * on the shell's internal implementation.\n */\nexport abstract class WindowManagerService {\n abstract openChildWindow(options: OpenWindowOptions): void;\n abstract closeWindow(windowId: string): void;\n abstract focusWindow(windowId: string): void;\n}\n\n/**\n * No-op fallback implementation used when running a Business App in\n * standalone mode (outside the FlyOS shell). Logs a warning instead of\n * throwing so standalone dev/test flows are not broken.\n */\n@Injectable()\nexport class StandaloneWindowManagerService extends WindowManagerService {\n openChildWindow(options: OpenWindowOptions): void {\n console.warn('[WindowManagerService] openChildWindow called in standalone mode — no shell available.', options);\n }\n\n closeWindow(windowId: string): void {\n console.warn('[WindowManagerService] closeWindow called in standalone mode — no shell available.', windowId);\n }\n\n focusWindow(windowId: string): void {\n console.warn('[WindowManagerService] focusWindow called in standalone mode — no shell available.', windowId);\n }\n}\n\n","import { Pipe, PipeTransform, inject } from '@angular/core';\nimport { I18nService } from '../services/i18n.service';\n\n/**\n * Translates a key using the shared I18nService.\n *\n * @example\n * {{ 'myApp.section.title' | translate }}\n * {{ 'myApp.items_count' | translate:{ n: count() } }}\n */\n@Pipe({\n name: 'translate',\n standalone: true,\n pure: false,\n})\nexport class TranslatePipe implements PipeTransform {\n private readonly i18n = inject(I18nService);\n\n transform(key: string, params?: Record<string, string | number>): string {\n // Read version so template CD re-runs when bundles load (merged translations updated).\n void this.i18n.version();\n return this.i18n.t(key, params);\n }\n}\n","import { Injectable, Signal, computed, inject, signal } from '@angular/core';\nimport { Router } from '@angular/router';\nimport { Observable, of } from 'rxjs';\nimport { User } from '../models/user.model';\n\nexport interface MockAuthConfig {\n user: User;\n token?: string;\n loginRedirect?: string[];\n logoutRedirect?: string[];\n}\n\ninterface MockSession {\n accessToken: string;\n user: User;\n expiresAt: number;\n}\n\n/**\n * Shared mock AuthService base class for Business App developers.\n *\n * Extend this class in your app's `auth.service.mock.ts` and override\n * `getConfig()` to supply app-specific mock user data and navigation paths.\n * The subclass must be decorated with `@Injectable({ providedIn: 'root' })`\n * and exported as `AuthService` so Angular's `fileReplacements` swap works.\n *\n * @example\n * ```typescript\n * // src/app/core/services/auth.service.mock.ts\n * import { Injectable } from '@angular/core';\n * import { MockAuthService, type MockAuthConfig } from '@mohamedatia/fly-design-system';\n *\n * @Injectable({ providedIn: 'root' })\n * export class AuthService extends MockAuthService {\n * protected override getConfig(): MockAuthConfig {\n * return {\n * user: { id: 'dev-001', tenantId: 'dev-tenant', email: 'dev@example.com',\n * firstName: 'Dev', lastName: 'User', fullName: 'Dev User',\n * preferredLocale: 'en', roles: ['admin'], apps: ['my-app'] },\n * token: 'my-mock-token',\n * loginRedirect: ['/app'],\n * };\n * }\n * }\n * ```\n */\n@Injectable()\nexport class MockAuthService {\n private readonly router = inject(Router);\n\n protected readonly _session: ReturnType<typeof signal<MockSession | null>>;\n\n private readonly _config: MockAuthConfig;\n\n readonly isAuthenticated: Signal<boolean>;\n readonly currentUser: Signal<User | null>;\n readonly accessToken: Signal<string | null>;\n /** Always false in mock mode — 2FA is handled entirely by the STS. */\n readonly hasPendingTwoFactor: Signal<boolean>;\n /** Always false in mock mode — auth is pre-populated synchronously. */\n readonly initializing: Signal<boolean>;\n\n constructor() {\n // All signal/computed calls are inside the constructor body so Angular's\n // injection context and ngDevMode are fully set up before they execute.\n this._config = this.getConfig();\n\n this._session = signal<MockSession | null>({\n accessToken: this._config.token ?? 'mock-token',\n user: this._config.user,\n expiresAt: Date.now() + 24 * 60 * 60 * 1000,\n });\n\n this.isAuthenticated = computed(() => {\n const s = this._session();\n return s !== null && s.expiresAt > Date.now();\n });\n\n this.currentUser = computed(() => this._session()?.user ?? null);\n this.accessToken = computed(() => this._session()?.accessToken ?? null);\n this.hasPendingTwoFactor = computed(() => false);\n this.initializing = signal(false).asReadonly();\n }\n\n /** Override in subclass to supply app-specific mock data. */\n protected getConfig(): MockAuthConfig {\n return {\n user: {\n id: 'mock-user-001',\n tenantId: 'mock-tenant-001',\n email: 'developer@fly.local',\n firstName: 'UI',\n lastName: 'Developer',\n fullName: 'UI Developer',\n preferredLocale: 'en',\n roles: ['admin'],\n apps: [],\n },\n };\n }\n\n async init(): Promise<void> {\n // Mock mode: already authenticated, nothing to initialize.\n }\n\n startLogin(): void {\n this.router.navigate(this._config.loginRedirect ?? ['/']);\n }\n\n handleCallback(_code: string, _state?: string): Observable<User> {\n return of(this._config.user);\n }\n\n async forceRefresh(): Promise<void> {\n // No-op in mock mode — token never expires.\n }\n\n patchSessionPreferredLocale(locale: string): void {\n const session = this._session();\n if (!session?.user) return;\n this._session.set({ ...session, user: { ...session.user, preferredLocale: locale } });\n }\n\n /** Parameter order matches the real AuthService: (user, accessToken, expiresAt). */\n setSession(_user: User, _accessToken: string, _expiresAt: number): void {\n // No-op in mock mode — session is hardcoded.\n }\n\n logout(): void {\n this._session.set(null);\n this.router.navigate(this._config.logoutRedirect ?? ['/']);\n }\n}\n","import {\n Component, input, output, HostListener, ViewChild, ElementRef,\n computed, signal, AfterViewInit, OnDestroy, ChangeDetectionStrategy, inject, DOCUMENT,\n} from '@angular/core';\nimport { TranslatePipe } from '../../pipes/translate.pipe';\n\nexport interface ContextMenuItem {\n id: string;\n label: string;\n icon: string;\n}\n\nexport interface ContextMenuSection {\n label?: string;\n items: ContextMenuItem[];\n}\n\n@Component({\n selector: 'fly-context-menu',\n standalone: true,\n imports: [TranslatePipe],\n templateUrl: './context-menu.component.html',\n styleUrl: './context-menu.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class ContextMenuComponent implements AfterViewInit, OnDestroy {\n @ViewChild('contextMenu') private menuEl?: ElementRef<HTMLElement>;\n\n private readonly doc = inject(DOCUMENT);\n private readonly hostEl = inject(ElementRef<HTMLElement>);\n\n x = input.required<number>();\n y = input.required<number>();\n sections = input.required<ContextMenuSection[]>();\n action = output<string>();\n closed = output<void>();\n\n private menuWidth = signal(0);\n private menuHeight = signal(0);\n\n private previouslyFocused: Element | null = null;\n\n // Resolved position: clamp to viewport so the menu never overflows an edge.\n // menuWidth/Height start at 0 so the first render places the menu at (x,y)\n // with no clamping; ngAfterViewInit measures the real size and updates the\n // signals, which re-evaluates this computed and repositions correctly.\n clampedPos = computed(() => {\n const vw = this.doc.defaultView?.innerWidth ?? 0;\n const vh = this.doc.defaultView?.innerHeight ?? 0;\n const w = this.menuWidth();\n const h = this.menuHeight();\n const x = this.x();\n const y = this.y();\n return {\n left: w > 0 ? Math.min(x, vw - w - 8) : x,\n top: h > 0 ? Math.min(y, vh - h - 8) : y,\n };\n });\n\n ngAfterViewInit(): void {\n // Portal: move host to <body> so position:fixed resolves against the true\n // viewport, not against any transformed/backdrop-filtered ancestor window.\n this.doc.body.appendChild(this.hostEl.nativeElement);\n\n // Measure after portal move so offsetWidth/Height are accurate.\n const el = this.menuEl?.nativeElement;\n if (el) {\n this.menuWidth.set(el.offsetWidth);\n this.menuHeight.set(el.offsetHeight);\n }\n\n this.previouslyFocused = document.activeElement;\n this.focusItem(0);\n }\n\n ngOnDestroy(): void {\n const el = this.hostEl.nativeElement;\n if (el.parentNode === this.doc.body) {\n this.doc.body.removeChild(el);\n }\n }\n\n onAction(id: string): void {\n this.action.emit(id);\n this.close();\n }\n\n @HostListener('document:mousedown', ['$event'])\n onClickOutside(event: MouseEvent): void {\n if (!this.menuEl?.nativeElement.contains(event.target as Node)) {\n this.close();\n }\n }\n\n @HostListener('document:keydown.escape')\n onEscape(): void {\n this.close();\n }\n\n @HostListener('document:contextmenu', ['$event'])\n onContextMenu(event: MouseEvent): void {\n if (!this.menuEl?.nativeElement.contains(event.target as Node)) {\n this.close();\n }\n }\n\n @HostListener('keydown', ['$event'])\n onKeydown(event: KeyboardEvent): void {\n const items = this.getMenuItems();\n if (!items.length) return;\n const focused = document.activeElement as HTMLElement;\n const idx = items.indexOf(focused);\n\n if (event.key === 'ArrowDown') {\n event.preventDefault();\n this.focusItem(idx < items.length - 1 ? idx + 1 : 0);\n } else if (event.key === 'ArrowUp') {\n event.preventDefault();\n this.focusItem(idx > 0 ? idx - 1 : items.length - 1);\n } else if (event.key === 'Home') {\n event.preventDefault();\n this.focusItem(0);\n } else if (event.key === 'End') {\n event.preventDefault();\n this.focusItem(items.length - 1);\n }\n }\n\n private close(): void {\n this.restoreFocus();\n this.closed.emit();\n }\n\n private getMenuItems(): HTMLElement[] {\n return Array.from(\n this.menuEl?.nativeElement.querySelectorAll<HTMLElement>('[role=\"menuitem\"]') ?? [],\n );\n }\n\n private focusItem(index: number): void {\n const items = this.getMenuItems();\n items[index]?.focus();\n }\n\n private restoreFocus(): void {\n if (this.previouslyFocused instanceof HTMLElement) {\n this.previouslyFocused.focus();\n }\n this.previouslyFocused = null;\n }\n}\n","<div\n #contextMenu\n class=\"context-menu\"\n [style.left.px]=\"clampedPos().left\"\n [style.top.px]=\"clampedPos().top\"\n role=\"menu\"\n [attr.aria-label]=\"'shell.context_menu' | translate\">\n\n @for (section of sections(); track $index) {\n @if ($index > 0) {\n <div class=\"menu-divider\"></div>\n }\n @if (section.label) {\n <div class=\"menu-section-label\">{{ section.label }}</div>\n }\n @for (item of section.items; track item.id) {\n <button\n type=\"button\"\n class=\"vos-btn sm rect menu-item\"\n role=\"menuitem\"\n (click)=\"onAction(item.id)\">\n <i [class]=\"'pi ' + item.icon\" aria-hidden=\"true\"></i>\n <span>{{ item.label }}</span>\n </button>\n }\n }\n</div>\n","import { Injectable, inject, signal } from '@angular/core';\nimport { I18nService } from '../../services/i18n.service';\n\nexport enum MessageBoxButtons {\n OK,\n OKCancel,\n YesNo,\n YesNoCancel,\n RetryCancel,\n AbortRetryIgnore,\n}\n\nexport enum MessageBoxIcon {\n None,\n Information,\n Warning,\n Error,\n Question,\n}\n\nexport enum DialogResult {\n None,\n OK,\n Cancel,\n Yes,\n No,\n Retry,\n Abort,\n Ignore,\n}\n\nexport interface MessageBoxOptions {\n title: string;\n message: string;\n buttons?: MessageBoxButtons;\n icon?: MessageBoxIcon;\n}\n\nexport interface MessageBoxButton {\n label: string;\n result: DialogResult;\n variant?: 'primary' | 'danger' | 'default';\n}\n\n@Injectable({ providedIn: 'root' })\nexport class MessageBoxService {\n private readonly i18n = inject(I18nService);\n\n readonly visible = signal(false);\n readonly title = signal('');\n readonly message = signal('');\n readonly icon = signal<MessageBoxIcon>(MessageBoxIcon.None);\n readonly buttons = signal<MessageBoxButton[]>([]);\n\n private resolver: ((value: DialogResult) => void) | null = null;\n\n show(options: MessageBoxOptions): Promise<DialogResult> {\n // Dismiss any pending dialog so its Promise resolves rather than leaking\n if (this.resolver) {\n this.resolver(DialogResult.None);\n this.resolver = null;\n }\n\n this.title.set(options.title);\n this.message.set(options.message);\n this.icon.set(options.icon ?? MessageBoxIcon.None);\n this.buttons.set(this.resolveButtons(options.buttons ?? MessageBoxButtons.OK));\n this.visible.set(true);\n\n return new Promise<DialogResult>((resolve) => {\n this.resolver = resolve;\n });\n }\n\n resolve(result: DialogResult): void {\n this.visible.set(false);\n if (this.resolver) {\n this.resolver(result);\n this.resolver = null;\n }\n }\n\n private resolveButtons(buttons: MessageBoxButtons): MessageBoxButton[] {\n const t = (key: string) => this.i18n.t(key);\n switch (buttons) {\n case MessageBoxButtons.OK:\n return [{ label: t('common.ok'), result: DialogResult.OK, variant: 'primary' }];\n\n case MessageBoxButtons.OKCancel:\n return [\n { label: t('common.cancel'), result: DialogResult.Cancel, variant: 'default' },\n { label: t('common.ok'), result: DialogResult.OK, variant: 'primary' },\n ];\n\n case MessageBoxButtons.YesNo:\n return [\n { label: t('common.no'), result: DialogResult.No, variant: 'default' },\n { label: t('common.yes'), result: DialogResult.Yes, variant: 'primary' },\n ];\n\n case MessageBoxButtons.YesNoCancel:\n return [\n { label: t('common.cancel'), result: DialogResult.Cancel, variant: 'default' },\n { label: t('common.no'), result: DialogResult.No, variant: 'default' },\n { label: t('common.yes'), result: DialogResult.Yes, variant: 'primary' },\n ];\n\n case MessageBoxButtons.RetryCancel:\n return [\n { label: t('common.cancel'), result: DialogResult.Cancel, variant: 'default' },\n { label: t('common.retry'), result: DialogResult.Retry, variant: 'primary' },\n ];\n\n case MessageBoxButtons.AbortRetryIgnore:\n return [\n { label: t('common.abort'), result: DialogResult.Abort, variant: 'danger' },\n { label: t('common.retry'), result: DialogResult.Retry, variant: 'default' },\n { label: t('common.ignore'), result: DialogResult.Ignore, variant: 'default' },\n ];\n }\n }\n}\n","import {\n Component, inject, ChangeDetectionStrategy,\n HostListener, ElementRef, AfterViewInit,\n} from '@angular/core';\nimport { MessageBoxService, MessageBoxIcon, DialogResult } from './message-box.service';\n\n@Component({\n selector: 'fly-message-box',\n standalone: true,\n templateUrl: './message-box.component.html',\n styleUrl: './message-box.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class MessageBoxComponent implements AfterViewInit {\n service = inject(MessageBoxService);\n private elRef = inject(ElementRef);\n\n readonly MessageBoxIcon = MessageBoxIcon;\n readonly DialogResult = DialogResult;\n\n private previouslyFocused: Element | null = null;\n\n ngAfterViewInit(): void {\n // Store focus origin so we can restore it on close\n this.previouslyFocused = document.activeElement;\n this.focusPrimaryButton();\n }\n\n @HostListener('document:keydown.escape')\n onEscape(): void {\n if (this.service.visible()) {\n this.service.resolve(DialogResult.Cancel);\n this.restoreFocus();\n }\n }\n\n onBackdropClick(): void {\n this.service.resolve(DialogResult.Cancel);\n this.restoreFocus();\n }\n\n onButtonClick(result: DialogResult): void {\n this.service.resolve(result);\n this.restoreFocus();\n }\n\n iconClass(): string {\n switch (this.service.icon()) {\n case MessageBoxIcon.Information: return 'pi pi-info-circle';\n case MessageBoxIcon.Warning: return 'pi pi-exclamation-triangle';\n case MessageBoxIcon.Error: return 'pi pi-times-circle';\n case MessageBoxIcon.Question: return 'pi pi-question-circle';\n default: return '';\n }\n }\n\n private focusPrimaryButton(): void {\n // Focus the primary (last) button, which is the default action\n const buttons = this.elRef.nativeElement.querySelectorAll('.mb-btn') as NodeListOf<HTMLButtonElement>;\n const last = buttons[buttons.length - 1];\n last?.focus();\n }\n\n private restoreFocus(): void {\n if (this.previouslyFocused instanceof HTMLElement) {\n this.previouslyFocused.focus();\n }\n this.previouslyFocused = null;\n }\n}\n","@if (service.visible()) {\n <div\n class=\"mb-backdrop\"\n (click)=\"onBackdropClick()\"\n role=\"presentation\">\n <div\n class=\"mb-dialog\"\n [class.mb-info]=\"service.icon() === MessageBoxIcon.Information\"\n [class.mb-warning]=\"service.icon() === MessageBoxIcon.Warning\"\n [class.mb-danger]=\"service.icon() === MessageBoxIcon.Error\"\n [class.mb-question]=\"service.icon() === MessageBoxIcon.Question\"\n (click)=\"$event.stopPropagation()\"\n role=\"alertdialog\"\n aria-modal=\"true\"\n aria-labelledby=\"mb-title\"\n aria-describedby=\"mb-message\">\n\n @if (iconClass()) {\n <div class=\"mb-icon-wrap\">\n <i [class]=\"iconClass() + ' mb-icon'\" aria-hidden=\"true\"></i>\n </div>\n }\n\n <div class=\"mb-title\" id=\"mb-title\">{{ service.title() }}</div>\n <div class=\"mb-message\" id=\"mb-message\">{{ service.message() }}</div>\n\n <div class=\"mb-actions\">\n @for (btn of service.buttons(); track btn.result) {\n <button\n type=\"button\"\n class=\"vos-btn sm mb-btn\"\n [class.mb-btn--primary]=\"btn.variant === 'primary'\"\n [class.mb-btn--danger]=\"btn.variant === 'danger'\"\n (click)=\"onButtonClick(btn.result)\">\n {{ btn.label }}\n </button>\n }\n </div>\n\n </div>\n </div>\n}\n","import {\n Component,\n Input,\n Output,\n EventEmitter,\n signal,\n inject,\n OnInit,\n DestroyRef,\n ChangeDetectionStrategy,\n} from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { Observable } from 'rxjs';\nimport { TranslatePipe } from '../../pipes/translate.pipe';\nimport { I18nService } from '../../services/i18n.service';\nimport type {\n ShareOuNode,\n SharePanelLevelOption,\n SharePermissionEntry,\n ShareUserResult,\n} from '../../models/share-panel.model';\n\n/** Default permission levels (file-style ACL). Hosts can override via `permissionLevels`. */\nexport const SHARE_PANEL_DEFAULT_FILE_LEVELS: SharePanelLevelOption[] = [\n { value: 'View', labelKey: 'files.share.level_view' },\n { value: 'Download', labelKey: 'files.share.level_download' },\n { value: 'Edit', labelKey: 'files.share.level_edit' },\n { value: 'FullControl', labelKey: 'files.share.level_full' },\n];\n\n@Component({\n selector: 'fly-share-panel',\n standalone: true,\n imports: [CommonModule, FormsModule, TranslatePipe],\n templateUrl: './share-panel.component.html',\n styleUrl: './share-panel.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class SharePanelComponent implements OnInit {\n /** Resource title shown in the header */\n @Input() targetName = '';\n\n /** i18n key for panel title */\n @Input() titleKey = 'files.share.title';\n\n /** Load current shares / permissions (host resolves display names when possible). */\n @Input({ required: true }) loadPermissions!: () => Observable<SharePermissionEntry[]>;\n\n @Input({ required: true }) searchUsers!: (query: string) => Observable<ShareUserResult[]>;\n\n @Input({ required: true }) loadOuTree!: () => Observable<ShareOuNode[]>;\n\n @Input({ required: true })\n grantToUser!: (userId: string, level: string, applyToChildren: boolean) => Observable<void>;\n\n @Input({ required: true })\n grantToOu!: (ouId: string, level: string, applyToChildren: boolean) => Observable<void>;\n\n @Input({ required: true }) updatePermission!: (id: string, level: string) => Observable<void>;\n\n @Input({ required: true }) revokePermission!: (id: string) => Observable<void>;\n\n /** When true, show “apply to children” (e.g. folders). */\n @Input() showApplyToChildren = false;\n\n /** Override level dropdown options (e.g. notes: View / Edit only). */\n @Input() permissionLevels?: SharePanelLevelOption[];\n\n /** i18n key for wildcard app grant label */\n @Input() everyoneLabelKey = 'files.share.everyone';\n\n @Output() close = new EventEmitter<void>();\n\n private destroyRef = inject(DestroyRef);\n private i18n = inject(I18nService);\n\n permissions = signal<SharePermissionEntry[]>([]);\n loading = signal(true);\n searchQuery = '';\n newLevel = '';\n applyToChildren = false;\n searchResults = signal<ShareUserResult[]>([]);\n ouTree = signal<ShareOuNode[]>([]);\n showOuPicker = signal(false);\n\n private searchTimeout: ReturnType<typeof setTimeout> | null = null;\n\n ngOnInit(): void {\n const levels = this.levelOptions;\n this.newLevel = levels[0]?.value ?? 'View';\n this.refreshPermissions();\n this.loadOuTree()\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe({\n next: (tree) => this.ouTree.set(tree),\n error: () => this.ouTree.set([]),\n });\n }\n\n get levelOptions(): SharePanelLevelOption[] {\n const p = this.permissionLevels;\n return p && p.length > 0 ? p : SHARE_PANEL_DEFAULT_FILE_LEVELS;\n }\n\n refreshPermissions(): void {\n this.loading.set(true);\n this.loadPermissions()\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe({\n next: (perms) => {\n this.permissions.set(perms);\n this.loading.set(false);\n },\n error: () => {\n this.permissions.set([]);\n this.loading.set(false);\n },\n });\n }\n\n onSearchInput(): void {\n if (this.searchTimeout) clearTimeout(this.searchTimeout);\n const query = this.searchQuery.trim();\n if (query.length < 2) {\n this.searchResults.set([]);\n return;\n }\n\n this.searchTimeout = setTimeout(() => {\n this.searchUsers(query)\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe({\n next: (items) => this.searchResults.set(items),\n error: () => this.searchResults.set([]),\n });\n }, 300);\n }\n\n onGrantToUser(user: ShareUserResult): void {\n this.searchResults.set([]);\n this.searchQuery = '';\n this.grantToUser(user.id, this.newLevel, this.applyToChildren)\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe({ next: () => this.refreshPermissions() });\n }\n\n onGrantToOu(ou: ShareOuNode): void {\n this.grantToOu(ou.id, this.newLevel, this.applyToChildren)\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe({ next: () => this.refreshPermissions() });\n }\n\n onUpdateLevel(perm: SharePermissionEntry, level: string): void {\n this.updatePermission(perm.id, level)\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe({ next: () => this.refreshPermissions() });\n }\n\n onRevokePermission(perm: SharePermissionEntry): void {\n this.revokePermission(perm.id)\n .pipe(takeUntilDestroyed(this.destroyRef))\n .subscribe({ next: () => this.refreshPermissions() });\n }\n\n getPermIcon(perm: SharePermissionEntry): string {\n if (perm.grantedToUserId) return 'pi pi-user';\n if (perm.grantedToOuId) return 'pi pi-sitemap';\n if (perm.grantedToAppId) return 'pi pi-globe';\n return 'pi pi-question';\n }\n\n getPermLabel(perm: SharePermissionEntry): string {\n if (perm.displayName?.trim()) return perm.displayName.trim();\n if (perm.grantedToUserId) {\n return `${perm.grantedToUserId.substring(0, 8)}…`;\n }\n if (perm.grantedToOuId) {\n const ou = this.ouTree().find((o) => o.id === perm.grantedToOuId);\n if (ou) return ou.displayName;\n return `${perm.grantedToOuId.substring(0, 8)}…`;\n }\n if (perm.grantedToAppId === '*') return this.i18n.t(this.everyoneLabelKey);\n return perm.grantedToAppId ?? '';\n }\n\n}\n","<div class=\"fac-overlay\" (click)=\"close.emit()\">\n <div class=\"fac-panel\" (click)=\"$event.stopPropagation()\">\n <div class=\"fac-header\">\n <h3>{{ titleKey | translate }}</h3>\n <span class=\"fac-target-name\">{{ targetName }}</span>\n <button type=\"button\" class=\"fac-close\" (click)=\"close.emit()\">\n <i class=\"pi pi-times\" aria-hidden=\"true\"></i>\n </button>\n </div>\n\n <div class=\"fac-add-section\">\n <div class=\"fac-search-row\">\n <input\n type=\"text\"\n class=\"fac-input\"\n [(ngModel)]=\"searchQuery\"\n (input)=\"onSearchInput()\"\n [placeholder]=\"'files.share.search_placeholder' | translate\"\n />\n <select class=\"fac-select\" [(ngModel)]=\"newLevel\">\n @for (opt of levelOptions; track opt.value) {\n <option [value]=\"opt.value\">{{ opt.labelKey | translate }}</option>\n }\n </select>\n </div>\n\n @if (searchResults().length > 0) {\n <div class=\"fac-search-results\">\n @for (result of searchResults(); track result.id) {\n <div class=\"fac-search-item\" (click)=\"onGrantToUser(result)\">\n <i class=\"pi pi-user\" aria-hidden=\"true\"></i>\n <span>{{ result.firstName }} {{ result.lastName }}</span>\n <span class=\"fac-email\">{{ result.email }}</span>\n </div>\n }\n </div>\n }\n\n @if (showOuPicker()) {\n <div class=\"fac-ou-section\">\n <div class=\"fac-section-label\">{{ 'files.share.share_with_ou' | translate }}</div>\n @for (ou of ouTree(); track ou.id) {\n <div class=\"fac-ou-item\" (click)=\"onGrantToOu(ou)\">\n <i class=\"pi pi-sitemap\" aria-hidden=\"true\"></i>\n <span>{{ ou.displayName }}</span>\n </div>\n }\n </div>\n }\n\n <div class=\"fac-toggle-row\">\n <button type=\"button\" class=\"fac-link\" (click)=\"showOuPicker.set(!showOuPicker())\">\n {{ showOuPicker() ? ('files.share.hide_ous' | translate) : ('files.share.show_ous' | translate) }}\n </button>\n @if (showApplyToChildren) {\n <label class=\"fac-checkbox-label\">\n <input type=\"checkbox\" [(ngModel)]=\"applyToChildren\" />\n {{ 'files.share.apply_children' | translate }}\n </label>\n }\n </div>\n </div>\n\n <div class=\"fac-perms-section\">\n <div class=\"fac-section-label\">{{ 'files.share.current_permissions' | translate }}</div>\n @if (loading()) {\n <div class=\"fac-loading\"><i class=\"pi pi-spin pi-spinner\" aria-hidden=\"true\"></i></div>\n } @else if (permissions().length === 0) {\n <div class=\"fac-empty\">{{ 'files.share.no_permissions' | translate }}</div>\n } @else {\n @for (perm of permissions(); track perm.id) {\n <div class=\"fac-perm-row\">\n <div class=\"fac-perm-info\">\n <i [class]=\"getPermIcon(perm)\" aria-hidden=\"true\"></i>\n <span class=\"fac-perm-name\">{{ getPermLabel(perm) }}</span>\n @if (perm.isInherited) {\n <span class=\"fac-badge inherited\">{{ 'files.share.inherited' | translate }}</span>\n }\n </div>\n <div class=\"fac-perm-actions\">\n <select\n class=\"fac-select sm\"\n [ngModel]=\"perm.level\"\n (ngModelChange)=\"onUpdateLevel(perm, $event)\"\n [disabled]=\"perm.isInherited === true\"\n >\n @for (opt of levelOptions; track opt.value) {\n <option [value]=\"opt.value\">{{ opt.labelKey | translate }}</option>\n }\n </select>\n <button\n type=\"button\"\n class=\"fac-icon-btn danger\"\n (click)=\"onRevokePermission(perm)\"\n [disabled]=\"perm.isInherited === true\"\n [title]=\"'files.share.revoke' | translate\"\n >\n <i class=\"pi pi-trash\" aria-hidden=\"true\"></i>\n </button>\n </div>\n </div>\n }\n }\n </div>\n </div>\n</div>\n","/*\r\n * @mohamedatia/fly-design-system — Public API\r\n * https://www.npmjs.com/package/@mohamedatia/fly-design-system\r\n *\r\n * This is the single entry point for Business / Supporting App developers.\r\n * Import everything from '@mohamedatia/fly-design-system' — never from relative shell paths.\r\n *\r\n * NOTE: This package is published under @mohamedatia/fly-design-system until the @fly\r\n * npm org is created. It will be republished as @fly/design-system once available.\r\n *\r\n * v1.3.0: FlyThemeService (html theme classes), hardened I18nService (AbortSignal, ErrorHandler, RTL helpers).\r\n * v1.3.1: loadBundle normalizes locale JSON (flat or nested objects; rejects invalid leaves).\r\n * v1.3.2: loadBundle returns boolean; LoadBundleOptions.silent skips ErrorHandler on failure.\r\n * v1.3.7: MockAuthService base class + MockAuthConfig for Business App mock auth via fileReplacements.\r\n * User model extended with optional avatar, source, twoFactorEnabled, createdAt fields.\r\n * v1.3.8: AuthService.init() no-op added so Business Apps can call init() in APP_INITIALIZER\r\n * without branching between standalone and embedded modes.\r\n * v1.3.9: MockAuthService field initializers moved into constructor to fix ngDevMode ReferenceError\r\n * when the package is loaded in a production-mode bundle (no ngDevMode global defined).\r\n * v1.4.0: ContextMenuComponent (positioned menu, keyboard nav, focus management) moved from desktop\r\n * shell to design system. MessageBoxService + MessageBoxComponent added (Windows Forms-style\r\n * modal dialogs with localized button labels, focus management, and Escape key support).\r\n * MessageBoxButtons, MessageBoxIcon, DialogResult, MessageBoxOptions, MessageBoxButton exported.\r\n * v1.5.0: SharePanelComponent — generic share/ACL overlay; host supplies API callbacks and optional\r\n * permission level options (file ACL vs note-style View/Edit).\r\n * See docs/ExternalAppsGuide/03-frontend-app.md.\r\n */\r\n\r\n// ─── Models ──────────────────────────────────────────────────────────────────\r\nexport type { DesktopApp, DesktopAppKind } from './lib/models/app.model';\r\nexport type {\r\n WindowInstance,\r\n WindowState,\r\n ChildWindowData,\r\n} from './lib/models/window.model';\r\nexport { WINDOW_DATA } from './lib/models/window.model';\r\nexport type { User } from './lib/models/user.model';\r\n\r\nexport type {\r\n SharePermissionEntry,\r\n ShareUserResult,\r\n ShareOuNode,\r\n SharePanelLevelOption,\r\n} from './lib/models/share-panel.model';\r\n\r\n// ─── Remote App Registration ─────────────────────────────────────────────────\r\nexport type { RemoteAppDef } from './lib/models/remote-app.model';\r\n\r\n// ─── Services ────────────────────────────────────────────────────────────────\r\nexport { AuthService } from './lib/services/auth.service';\r\nexport type { LoadBundleOptions } from './lib/services/i18n.service';\r\nexport { I18nService, RTL_LOCALE_SET, isRtlLocale } from './lib/services/i18n.service';\r\nexport { FlyThemeService, type FlyThemeMode } from './lib/services/fly-theme.service';\r\nexport {\r\n WindowManagerService,\r\n StandaloneWindowManagerService,\r\n type OpenWindowOptions,\r\n} from './lib/services/window-manager.service';\r\n\r\n// ─── Pipes ───────────────────────────────────────────────────────────────────\r\nexport { TranslatePipe } from './lib/pipes/translate.pipe';\r\n\r\n// ─── Mock Auth (for fileReplacements in dev/mock builds) ─────────────────────\r\nexport { MockAuthService, type MockAuthConfig } from './lib/services/auth.service.mock';\r\n\r\n// ─── Components ──────────────────────────────────────────────────────────────\r\nexport {\r\n ContextMenuComponent,\r\n type ContextMenuItem,\r\n type ContextMenuSection,\r\n} from './lib/components/context-menu/context-menu.component';\r\n\r\nexport {\r\n MessageBoxComponent,\r\n} from './lib/components/message-box/message-box.component';\r\n\r\nexport {\r\n MessageBoxService,\r\n MessageBoxButtons,\r\n MessageBoxIcon,\r\n DialogResult,\r\n type MessageBoxOptions,\r\n type MessageBoxButton,\r\n} from './lib/components/message-box/message-box.service';\r\n\r\nexport {\r\n SharePanelComponent,\r\n SHARE_PANEL_DEFAULT_FILE_LEVELS,\r\n} from './lib/components/share-panel/share-panel.component';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import { Type, InjectionToken, PipeTransform, signal, Signal } from '@angular/core';
|
|
1
|
+
import * as _angular_core from '@angular/core';
|
|
2
|
+
import { Type, InjectionToken, PipeTransform, signal, Signal, AfterViewInit, OnDestroy, OnInit, EventEmitter } from '@angular/core';
|
|
3
3
|
import { Observable } from 'rxjs';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* FlyOS architectural app category — see repo `docs/01-ecosystem-overview.md`.
|
|
7
|
+
* Orthogonal to {@link DesktopApp.category} (Finder / dock UI grouping: system, productivity, etc.).
|
|
8
|
+
*/
|
|
9
|
+
type DesktopAppKind = 'os-core' | 'business' | 'supporting' | 'control';
|
|
5
10
|
interface DesktopApp {
|
|
6
11
|
id: string;
|
|
7
12
|
name: string;
|
|
@@ -19,6 +24,8 @@ interface DesktopApp {
|
|
|
19
24
|
};
|
|
20
25
|
isPinned: boolean;
|
|
21
26
|
category: 'system' | 'productivity' | 'analytics' | 'utility';
|
|
27
|
+
/** Defaults: shell-bundled entries → `os-core`; manifest remotes → `business` unless overridden. */
|
|
28
|
+
kind?: DesktopAppKind;
|
|
22
29
|
chromeless?: boolean;
|
|
23
30
|
}
|
|
24
31
|
|
|
@@ -76,9 +83,37 @@ interface User {
|
|
|
76
83
|
}
|
|
77
84
|
|
|
78
85
|
/**
|
|
79
|
-
*
|
|
80
|
-
|
|
81
|
-
|
|
86
|
+
* Generic share / ACL panel models — hosts map domain DTOs to these shapes.
|
|
87
|
+
*/
|
|
88
|
+
interface SharePermissionEntry {
|
|
89
|
+
id: string;
|
|
90
|
+
grantedToUserId?: string | null;
|
|
91
|
+
grantedToOuId?: string | null;
|
|
92
|
+
grantedToAppId?: string | null;
|
|
93
|
+
level: string;
|
|
94
|
+
isInherited?: boolean;
|
|
95
|
+
/** Resolved label (user name, OU name, etc.); if omitted, panel uses fallbacks. */
|
|
96
|
+
displayName?: string;
|
|
97
|
+
}
|
|
98
|
+
interface ShareUserResult {
|
|
99
|
+
id: string;
|
|
100
|
+
firstName: string;
|
|
101
|
+
lastName: string;
|
|
102
|
+
email: string;
|
|
103
|
+
}
|
|
104
|
+
interface ShareOuNode {
|
|
105
|
+
id: string;
|
|
106
|
+
displayName: string;
|
|
107
|
+
}
|
|
108
|
+
interface SharePanelLevelOption {
|
|
109
|
+
value: string;
|
|
110
|
+
labelKey: string;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Describes a Business or Supporting App remote from the Controller App's
|
|
115
|
+
* `GET /api/apps/manifest` endpoint. The shell loads the component at runtime
|
|
116
|
+
* via Angular Native Federation.
|
|
82
117
|
*/
|
|
83
118
|
interface RemoteAppDef {
|
|
84
119
|
id: string;
|
|
@@ -102,6 +137,8 @@ interface RemoteAppDef {
|
|
|
102
137
|
};
|
|
103
138
|
isPinned: boolean;
|
|
104
139
|
category: DesktopApp['category'];
|
|
140
|
+
/** Defaults to `business` when omitted; use `supporting` for horizontal productivity remotes. */
|
|
141
|
+
kind?: Extract<DesktopApp['kind'], 'business' | 'supporting'>;
|
|
105
142
|
chromeless?: boolean;
|
|
106
143
|
}
|
|
107
144
|
|
|
@@ -122,9 +159,9 @@ interface RemoteAppDef {
|
|
|
122
159
|
*/
|
|
123
160
|
declare class AuthService {
|
|
124
161
|
private _session;
|
|
125
|
-
readonly currentUser:
|
|
126
|
-
readonly accessToken:
|
|
127
|
-
readonly isAuthenticated:
|
|
162
|
+
readonly currentUser: _angular_core.Signal<User | null>;
|
|
163
|
+
readonly accessToken: _angular_core.Signal<string | null>;
|
|
164
|
+
readonly isAuthenticated: _angular_core.Signal<boolean>;
|
|
128
165
|
/**
|
|
129
166
|
* No-op when running as a Module Federation remote — the shell has already
|
|
130
167
|
* initialised the session before the remote bootstraps. Business Apps call
|
|
@@ -144,8 +181,8 @@ declare class AuthService {
|
|
|
144
181
|
* Pass the storage key your app uses; embedded remotes typically set this in `APP_INITIALIZER`.
|
|
145
182
|
*/
|
|
146
183
|
initFromToken(storageKey?: string): void;
|
|
147
|
-
static ɵfac:
|
|
148
|
-
static ɵprov:
|
|
184
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<AuthService, never>;
|
|
185
|
+
static ɵprov: _angular_core.ɵɵInjectableDeclaration<AuthService>;
|
|
149
186
|
}
|
|
150
187
|
|
|
151
188
|
/** Locales that use RTL layout for `dir` and DS `isRtl` / `direction`. */
|
|
@@ -182,10 +219,10 @@ declare class I18nService {
|
|
|
182
219
|
private readonly _locale;
|
|
183
220
|
private readonly _version;
|
|
184
221
|
private readonly _merged;
|
|
185
|
-
readonly locale:
|
|
186
|
-
readonly version:
|
|
187
|
-
readonly isRtl:
|
|
188
|
-
readonly direction:
|
|
222
|
+
readonly locale: _angular_core.Signal<string>;
|
|
223
|
+
readonly version: _angular_core.Signal<number>;
|
|
224
|
+
readonly isRtl: _angular_core.Signal<boolean>;
|
|
225
|
+
readonly direction: _angular_core.Signal<"rtl" | "ltr">;
|
|
189
226
|
private bump;
|
|
190
227
|
/** Replace shell strings (desktop `locale/*.json` + `/api/i18n/desktop-shell/{lang}` merged by the shell). */
|
|
191
228
|
setShellTranslations(data: Record<string, string>, lang: string): void;
|
|
@@ -198,8 +235,8 @@ declare class I18nService {
|
|
|
198
235
|
removeBundle(id: string): void;
|
|
199
236
|
clearRemoteBundles(): void;
|
|
200
237
|
t(key: string, params?: Record<string, string | number>): string;
|
|
201
|
-
static ɵfac:
|
|
202
|
-
static ɵprov:
|
|
238
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<I18nService, never>;
|
|
239
|
+
static ɵprov: _angular_core.ɵɵInjectableDeclaration<I18nService>;
|
|
203
240
|
}
|
|
204
241
|
|
|
205
242
|
type FlyThemeMode = 'light' | 'dark' | 'transparent';
|
|
@@ -208,10 +245,10 @@ type FlyThemeMode = 'light' | 'dark' | 'transparent';
|
|
|
208
245
|
* Shell and standalone Business Apps use the same service (federation singleton when shared).
|
|
209
246
|
*/
|
|
210
247
|
declare class FlyThemeService {
|
|
211
|
-
readonly theme:
|
|
248
|
+
readonly theme: _angular_core.WritableSignal<FlyThemeMode>;
|
|
212
249
|
applyTheme(mode: FlyThemeMode): void;
|
|
213
|
-
static ɵfac:
|
|
214
|
-
static ɵprov:
|
|
250
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FlyThemeService, never>;
|
|
251
|
+
static ɵprov: _angular_core.ɵɵInjectableDeclaration<FlyThemeService>;
|
|
215
252
|
}
|
|
216
253
|
|
|
217
254
|
/** Options for opening a child window. */
|
|
@@ -253,8 +290,8 @@ declare class StandaloneWindowManagerService extends WindowManagerService {
|
|
|
253
290
|
openChildWindow(options: OpenWindowOptions): void;
|
|
254
291
|
closeWindow(windowId: string): void;
|
|
255
292
|
focusWindow(windowId: string): void;
|
|
256
|
-
static ɵfac:
|
|
257
|
-
static ɵprov:
|
|
293
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<StandaloneWindowManagerService, never>;
|
|
294
|
+
static ɵprov: _angular_core.ɵɵInjectableDeclaration<StandaloneWindowManagerService>;
|
|
258
295
|
}
|
|
259
296
|
|
|
260
297
|
/**
|
|
@@ -267,8 +304,8 @@ declare class StandaloneWindowManagerService extends WindowManagerService {
|
|
|
267
304
|
declare class TranslatePipe implements PipeTransform {
|
|
268
305
|
private readonly i18n;
|
|
269
306
|
transform(key: string, params?: Record<string, string | number>): string;
|
|
270
|
-
static ɵfac:
|
|
271
|
-
static ɵpipe:
|
|
307
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<TranslatePipe, never>;
|
|
308
|
+
static ɵpipe: _angular_core.ɵɵPipeDeclaration<TranslatePipe, "translate", true>;
|
|
272
309
|
}
|
|
273
310
|
|
|
274
311
|
interface MockAuthConfig {
|
|
@@ -332,10 +369,165 @@ declare class MockAuthService {
|
|
|
332
369
|
/** Parameter order matches the real AuthService: (user, accessToken, expiresAt). */
|
|
333
370
|
setSession(_user: User, _accessToken: string, _expiresAt: number): void;
|
|
334
371
|
logout(): void;
|
|
335
|
-
static ɵfac:
|
|
336
|
-
static ɵprov:
|
|
372
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<MockAuthService, never>;
|
|
373
|
+
static ɵprov: _angular_core.ɵɵInjectableDeclaration<MockAuthService>;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
interface ContextMenuItem {
|
|
377
|
+
id: string;
|
|
378
|
+
label: string;
|
|
379
|
+
icon: string;
|
|
380
|
+
}
|
|
381
|
+
interface ContextMenuSection {
|
|
382
|
+
label?: string;
|
|
383
|
+
items: ContextMenuItem[];
|
|
384
|
+
}
|
|
385
|
+
declare class ContextMenuComponent implements AfterViewInit, OnDestroy {
|
|
386
|
+
private menuEl?;
|
|
387
|
+
private readonly doc;
|
|
388
|
+
private readonly hostEl;
|
|
389
|
+
x: _angular_core.InputSignal<number>;
|
|
390
|
+
y: _angular_core.InputSignal<number>;
|
|
391
|
+
sections: _angular_core.InputSignal<ContextMenuSection[]>;
|
|
392
|
+
action: _angular_core.OutputEmitterRef<string>;
|
|
393
|
+
closed: _angular_core.OutputEmitterRef<void>;
|
|
394
|
+
private menuWidth;
|
|
395
|
+
private menuHeight;
|
|
396
|
+
private previouslyFocused;
|
|
397
|
+
clampedPos: _angular_core.Signal<{
|
|
398
|
+
left: number;
|
|
399
|
+
top: number;
|
|
400
|
+
}>;
|
|
401
|
+
ngAfterViewInit(): void;
|
|
402
|
+
ngOnDestroy(): void;
|
|
403
|
+
onAction(id: string): void;
|
|
404
|
+
onClickOutside(event: MouseEvent): void;
|
|
405
|
+
onEscape(): void;
|
|
406
|
+
onContextMenu(event: MouseEvent): void;
|
|
407
|
+
onKeydown(event: KeyboardEvent): void;
|
|
408
|
+
private close;
|
|
409
|
+
private getMenuItems;
|
|
410
|
+
private focusItem;
|
|
411
|
+
private restoreFocus;
|
|
412
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<ContextMenuComponent, never>;
|
|
413
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<ContextMenuComponent, "fly-context-menu", never, { "x": { "alias": "x"; "required": true; "isSignal": true; }; "y": { "alias": "y"; "required": true; "isSignal": true; }; "sections": { "alias": "sections"; "required": true; "isSignal": true; }; }, { "action": "action"; "closed": "closed"; }, never, never, true, never>;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
declare enum MessageBoxButtons {
|
|
417
|
+
OK = 0,
|
|
418
|
+
OKCancel = 1,
|
|
419
|
+
YesNo = 2,
|
|
420
|
+
YesNoCancel = 3,
|
|
421
|
+
RetryCancel = 4,
|
|
422
|
+
AbortRetryIgnore = 5
|
|
423
|
+
}
|
|
424
|
+
declare enum MessageBoxIcon {
|
|
425
|
+
None = 0,
|
|
426
|
+
Information = 1,
|
|
427
|
+
Warning = 2,
|
|
428
|
+
Error = 3,
|
|
429
|
+
Question = 4
|
|
430
|
+
}
|
|
431
|
+
declare enum DialogResult {
|
|
432
|
+
None = 0,
|
|
433
|
+
OK = 1,
|
|
434
|
+
Cancel = 2,
|
|
435
|
+
Yes = 3,
|
|
436
|
+
No = 4,
|
|
437
|
+
Retry = 5,
|
|
438
|
+
Abort = 6,
|
|
439
|
+
Ignore = 7
|
|
440
|
+
}
|
|
441
|
+
interface MessageBoxOptions {
|
|
442
|
+
title: string;
|
|
443
|
+
message: string;
|
|
444
|
+
buttons?: MessageBoxButtons;
|
|
445
|
+
icon?: MessageBoxIcon;
|
|
446
|
+
}
|
|
447
|
+
interface MessageBoxButton {
|
|
448
|
+
label: string;
|
|
449
|
+
result: DialogResult;
|
|
450
|
+
variant?: 'primary' | 'danger' | 'default';
|
|
451
|
+
}
|
|
452
|
+
declare class MessageBoxService {
|
|
453
|
+
private readonly i18n;
|
|
454
|
+
readonly visible: _angular_core.WritableSignal<boolean>;
|
|
455
|
+
readonly title: _angular_core.WritableSignal<string>;
|
|
456
|
+
readonly message: _angular_core.WritableSignal<string>;
|
|
457
|
+
readonly icon: _angular_core.WritableSignal<MessageBoxIcon>;
|
|
458
|
+
readonly buttons: _angular_core.WritableSignal<MessageBoxButton[]>;
|
|
459
|
+
private resolver;
|
|
460
|
+
show(options: MessageBoxOptions): Promise<DialogResult>;
|
|
461
|
+
resolve(result: DialogResult): void;
|
|
462
|
+
private resolveButtons;
|
|
463
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<MessageBoxService, never>;
|
|
464
|
+
static ɵprov: _angular_core.ɵɵInjectableDeclaration<MessageBoxService>;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
declare class MessageBoxComponent implements AfterViewInit {
|
|
468
|
+
service: MessageBoxService;
|
|
469
|
+
private elRef;
|
|
470
|
+
readonly MessageBoxIcon: typeof MessageBoxIcon;
|
|
471
|
+
readonly DialogResult: typeof DialogResult;
|
|
472
|
+
private previouslyFocused;
|
|
473
|
+
ngAfterViewInit(): void;
|
|
474
|
+
onEscape(): void;
|
|
475
|
+
onBackdropClick(): void;
|
|
476
|
+
onButtonClick(result: DialogResult): void;
|
|
477
|
+
iconClass(): string;
|
|
478
|
+
private focusPrimaryButton;
|
|
479
|
+
private restoreFocus;
|
|
480
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<MessageBoxComponent, never>;
|
|
481
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<MessageBoxComponent, "fly-message-box", never, {}, {}, never, never, true, never>;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/** Default permission levels (file-style ACL). Hosts can override via `permissionLevels`. */
|
|
485
|
+
declare const SHARE_PANEL_DEFAULT_FILE_LEVELS: SharePanelLevelOption[];
|
|
486
|
+
declare class SharePanelComponent implements OnInit {
|
|
487
|
+
/** Resource title shown in the header */
|
|
488
|
+
targetName: string;
|
|
489
|
+
/** i18n key for panel title */
|
|
490
|
+
titleKey: string;
|
|
491
|
+
/** Load current shares / permissions (host resolves display names when possible). */
|
|
492
|
+
loadPermissions: () => Observable<SharePermissionEntry[]>;
|
|
493
|
+
searchUsers: (query: string) => Observable<ShareUserResult[]>;
|
|
494
|
+
loadOuTree: () => Observable<ShareOuNode[]>;
|
|
495
|
+
grantToUser: (userId: string, level: string, applyToChildren: boolean) => Observable<void>;
|
|
496
|
+
grantToOu: (ouId: string, level: string, applyToChildren: boolean) => Observable<void>;
|
|
497
|
+
updatePermission: (id: string, level: string) => Observable<void>;
|
|
498
|
+
revokePermission: (id: string) => Observable<void>;
|
|
499
|
+
/** When true, show “apply to children” (e.g. folders). */
|
|
500
|
+
showApplyToChildren: boolean;
|
|
501
|
+
/** Override level dropdown options (e.g. notes: View / Edit only). */
|
|
502
|
+
permissionLevels?: SharePanelLevelOption[];
|
|
503
|
+
/** i18n key for wildcard app grant label */
|
|
504
|
+
everyoneLabelKey: string;
|
|
505
|
+
close: EventEmitter<void>;
|
|
506
|
+
private destroyRef;
|
|
507
|
+
private i18n;
|
|
508
|
+
permissions: _angular_core.WritableSignal<SharePermissionEntry[]>;
|
|
509
|
+
loading: _angular_core.WritableSignal<boolean>;
|
|
510
|
+
searchQuery: string;
|
|
511
|
+
newLevel: string;
|
|
512
|
+
applyToChildren: boolean;
|
|
513
|
+
searchResults: _angular_core.WritableSignal<ShareUserResult[]>;
|
|
514
|
+
ouTree: _angular_core.WritableSignal<ShareOuNode[]>;
|
|
515
|
+
showOuPicker: _angular_core.WritableSignal<boolean>;
|
|
516
|
+
private searchTimeout;
|
|
517
|
+
ngOnInit(): void;
|
|
518
|
+
get levelOptions(): SharePanelLevelOption[];
|
|
519
|
+
refreshPermissions(): void;
|
|
520
|
+
onSearchInput(): void;
|
|
521
|
+
onGrantToUser(user: ShareUserResult): void;
|
|
522
|
+
onGrantToOu(ou: ShareOuNode): void;
|
|
523
|
+
onUpdateLevel(perm: SharePermissionEntry, level: string): void;
|
|
524
|
+
onRevokePermission(perm: SharePermissionEntry): void;
|
|
525
|
+
getPermIcon(perm: SharePermissionEntry): string;
|
|
526
|
+
getPermLabel(perm: SharePermissionEntry): string;
|
|
527
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<SharePanelComponent, never>;
|
|
528
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<SharePanelComponent, "fly-share-panel", never, { "targetName": { "alias": "targetName"; "required": false; }; "titleKey": { "alias": "titleKey"; "required": false; }; "loadPermissions": { "alias": "loadPermissions"; "required": true; }; "searchUsers": { "alias": "searchUsers"; "required": true; }; "loadOuTree": { "alias": "loadOuTree"; "required": true; }; "grantToUser": { "alias": "grantToUser"; "required": true; }; "grantToOu": { "alias": "grantToOu"; "required": true; }; "updatePermission": { "alias": "updatePermission"; "required": true; }; "revokePermission": { "alias": "revokePermission"; "required": true; }; "showApplyToChildren": { "alias": "showApplyToChildren"; "required": false; }; "permissionLevels": { "alias": "permissionLevels"; "required": false; }; "everyoneLabelKey": { "alias": "everyoneLabelKey"; "required": false; }; }, { "close": "close"; }, never, never, true, never>;
|
|
337
529
|
}
|
|
338
530
|
|
|
339
|
-
export { AuthService, FlyThemeService, I18nService, MockAuthService, RTL_LOCALE_SET, StandaloneWindowManagerService, TranslatePipe, WINDOW_DATA, WindowManagerService, isRtlLocale };
|
|
340
|
-
export type { ChildWindowData, DesktopApp, FlyThemeMode, LoadBundleOptions, MockAuthConfig, OpenWindowOptions, RemoteAppDef, User, WindowInstance, WindowState };
|
|
531
|
+
export { AuthService, ContextMenuComponent, DialogResult, FlyThemeService, I18nService, MessageBoxButtons, MessageBoxComponent, MessageBoxIcon, MessageBoxService, MockAuthService, RTL_LOCALE_SET, SHARE_PANEL_DEFAULT_FILE_LEVELS, SharePanelComponent, StandaloneWindowManagerService, TranslatePipe, WINDOW_DATA, WindowManagerService, isRtlLocale };
|
|
532
|
+
export type { ChildWindowData, ContextMenuItem, ContextMenuSection, DesktopApp, DesktopAppKind, FlyThemeMode, LoadBundleOptions, MessageBoxButton, MessageBoxOptions, MockAuthConfig, OpenWindowOptions, RemoteAppDef, ShareOuNode, SharePanelLevelOption, SharePermissionEntry, ShareUserResult, User, WindowInstance, WindowState };
|
|
341
533
|
//# sourceMappingURL=mohamedatia-fly-design-system.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mohamedatia-fly-design-system.d.ts","sources":["../../../projects/design-system/src/lib/models/app.model.ts","../../../projects/design-system/src/lib/models/window.model.ts","../../../projects/design-system/src/lib/models/user.model.ts","../../../projects/design-system/src/lib/models/remote-app.model.ts","../../../projects/design-system/src/lib/services/auth.service.ts","../../../projects/design-system/src/lib/services/i18n.service.ts","../../../projects/design-system/src/lib/services/fly-theme.service.ts","../../../projects/design-system/src/lib/services/window-manager.service.ts","../../../projects/design-system/src/lib/pipes/translate.pipe.ts","../../../projects/design-system/src/lib/services/auth.service.mock.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"mohamedatia-fly-design-system.d.ts","sources":["../../../projects/design-system/src/lib/models/app.model.ts","../../../projects/design-system/src/lib/models/window.model.ts","../../../projects/design-system/src/lib/models/user.model.ts","../../../projects/design-system/src/lib/models/share-panel.model.ts","../../../projects/design-system/src/lib/models/remote-app.model.ts","../../../projects/design-system/src/lib/services/auth.service.ts","../../../projects/design-system/src/lib/services/i18n.service.ts","../../../projects/design-system/src/lib/services/fly-theme.service.ts","../../../projects/design-system/src/lib/services/window-manager.service.ts","../../../projects/design-system/src/lib/pipes/translate.pipe.ts","../../../projects/design-system/src/lib/services/auth.service.mock.ts","../../../projects/design-system/src/lib/components/context-menu/context-menu.component.ts","../../../projects/design-system/src/lib/components/message-box/message-box.service.ts","../../../projects/design-system/src/lib/components/message-box/message-box.component.ts","../../../projects/design-system/src/lib/components/share-panel/share-panel.component.ts"],"mappings":"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","names":[]}
|